diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index 5cbfc09fe76804..00000000000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,38 +0,0 @@ - - -### What version of Go are you using (`go version`)? - -
-$ go version
-
-
- -### Does this issue reproduce with the latest release? - - - -### What operating system and processor architecture are you using (`go env`)? - -
go env Output
-$ go env
-
-
- -### What did you do? - - - - - -### What did you expect to see? - - - -### What did you see instead? diff --git a/.github/ISSUE_TEMPLATE/00-bug.md b/.github/ISSUE_TEMPLATE/00-bug.md new file mode 100644 index 00000000000000..f056dab7dd084a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00-bug.md @@ -0,0 +1,45 @@ +--- +name: Bugs +about: The go command, standard library, or anything else +title: "affected/package: " +--- + + + +### What version of Go are you using (`go version`)? + +
+$ go version
+
+
+ +### Does this issue reproduce with the latest release? + + + +### What operating system and processor architecture are you using (`go env`)? + +
go env Output
+$ go env
+
+
+ +### What did you do? + + + + + +### What did you expect to see? + + + +### What did you see instead? + + diff --git a/.github/ISSUE_TEMPLATE/01-pkgsite.md b/.github/ISSUE_TEMPLATE/01-pkgsite.md new file mode 100644 index 00000000000000..fee00f5b275203 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-pkgsite.md @@ -0,0 +1,47 @@ +--- +name: Pkg.go.dev bugs or feature requests +about: Issues or feature requests for the documentation site +title: "x/pkgsite: " +labels: pkgsite +--- + + + +### What is the URL of the page with the issue? + + + +### What is your user agent? + + + + + +### Screenshot + + + + + +### What did you do? + + + + + +### What did you expect to see? + + + +### What did you see instead? + + diff --git a/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md b/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md new file mode 100644 index 00000000000000..97fe317f5b3914 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md @@ -0,0 +1,39 @@ +--- +name: Pkg.go.dev package removal request +about: Request a package be removed from the documentation site (pkg.go.dev) +title: "x/pkgsite: package removal request for [type path here]" +labels: pkgsite/package-removal +--- + + + +### What is the path of the package that you would like to have removed? + + + + + +### Are you the owner of this package? + + + + + +### What is the reason that you could not retract this package instead? + + + + diff --git a/.github/ISSUE_TEMPLATE/03-gopls.md b/.github/ISSUE_TEMPLATE/03-gopls.md new file mode 100644 index 00000000000000..c4934c38982b43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-gopls.md @@ -0,0 +1,61 @@ +--- +name: Gopls bugs or feature requests +about: Issues or feature requests for the Go language server (gopls) +title: "x/tools/gopls: " +labels: gopls Tools +--- + + + +### gopls version + + + + + +### go env + + + + +### What did you do? + + + + + +### What did you expect to see? + + + +### What did you see instead? + + + +### Editor and settings + + + + + +### Logs + + + + diff --git a/.github/ISSUE_TEMPLATE/04-vuln.md b/.github/ISSUE_TEMPLATE/04-vuln.md new file mode 100644 index 00000000000000..7e129d78db95b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04-vuln.md @@ -0,0 +1,51 @@ +--- +name: Go vulnerability management - bugs and feature requests +about: Issues or feature requests about Go vulnerability management +title: "x/vuln: " +labels: "vulncheck or vulndb" +--- + + + +### What version of Go are you using (`go version`)? + +
+$ go version
+
+
+ +### Does this issue reproduce at the latest version of golang.org/x/vuln? + + + +### What operating system and processor architecture are you using (`go env`)? + +
go env Output
+$ go env
+
+
+ +### What did you do? + + + + + +### What did you expect to see? + + + +### What did you see instead? + + diff --git a/.github/ISSUE_TEMPLATE/10-proposal.md b/.github/ISSUE_TEMPLATE/10-proposal.md new file mode 100644 index 00000000000000..ab30ddf4175e43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/10-proposal.md @@ -0,0 +1,13 @@ +--- +name: Proposals +about: New external API or other notable changes +title: "proposal: affected/package: " +labels: Proposal +--- + + + + diff --git a/.github/ISSUE_TEMPLATE/11-language-change.md b/.github/ISSUE_TEMPLATE/11-language-change.md new file mode 100644 index 00000000000000..2032301327f479 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/11-language-change.md @@ -0,0 +1,52 @@ +--- +name: Language Change Proposals +about: Changes to the language +title: "proposal: Go 2: " +labels: Proposal Go2 LanguageChange +--- + + + +### Author background + +- **Would you consider yourself a novice, intermediate, or experienced Go programmer?** +- **What other languages do you have experience with?** + +### Related proposals + +- **Has this idea, or one like it, been proposed before?** + - **If so, how does this proposal differ?** +- **Does this affect error handling?** + - **If so, how does this differ from previous error handling proposals?** +- **Is this about generics?** + - **If so, how does this relate to the accepted design and other generics proposals?** + +### Proposal + +- **What is the proposed change?** +- **Who does this proposal help, and why?** +- **Please describe as precisely as possible the change to the language.** +- **What would change in the language spec?** +- **Please also describe the change informally, as in a class teaching Go.** +- **Is this change backward compatible?** + - Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit. + Show example code before and after the change. + - **Before** + - **After** +- **Orthogonality: how does this change interact or overlap with existing features?** +- **Is the goal of this change a performance improvement?** + - **If so, what quantifiable improvement should we expect?** + - **How would we measure it?** + +### Costs + +- **Would this change make Go easier or harder to learn, and why?** +- **What is the cost of this proposal? (Every language change has a cost).** +- **How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?** +- **What is the compile time cost?** +- **What is the run time cost?** +- **Can you describe a possible implementation?** +- **Do you have a prototype? (This is not required.)** diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000000..c07f1e4d1c74af --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Questions + about: Please use one of the forums for questions or general discussions + url: https://go.dev/wiki/Questions diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 23fec84fb65e3b..2ec957a52b4e1f 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -11,4 +11,4 @@ For asking questions, see: * [Stack Overflow](https://stackoverflow.com/questions/tagged/go) with questions tagged "go" -* **IRC** channel #go-nuts on Freenode +* **IRC** channel #go-nuts on Libera diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 95d3158d204ff0..00000000000000 --- a/AUTHORS +++ /dev/null @@ -1,1488 +0,0 @@ -# This is the official list of Go authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Since Go 1.11, this file is not actively maintained. -# To be included, send a change adding the individual or -# company who owns a contribution's copyright. - -# Names should be added to this file as one of -# Organization's name -# Individual's name -# Individual's name -# See CONTRIBUTORS for the meaning of multiple email addresses. - -# Please keep the list sorted. - -10x Genomics, Inc. -A Medium Corporation -Aamir Khan -Aaron France -Aaron Stein -Aaron Torres -Aarti Parikh -Abe Haskins -Abhinav Gupta -Adam Eijdenberg -Adam Harvey -Adam Kisala -Adam Medzinski -Adam Shannon -Adam Thomason -Adam Woodbeck -Aditya Mukerjee -Adrian Hesketh -Adrian Nos -Adrian O'Grady -Adrien Bustany -Adrien Petel -Aécio Júnior -Aeneas Rekkas (arekkas) -Afanasev Stanislav -Agis Anastasopoulos -Agniva De Sarker -Ahmed W. Mones -Ahmet Soormally -Ahmy Yulrizka -Aiden Scandella -Ainar Garipov -Aishraj Dahal -Akhil Indurti -Akihiro Suda -Akshat Kumar -Alan Shreve -Albert Nigmatzianov -Albert Strasheim -Albert Yu -Alberto Bertogli -Alberto Donizetti -Alberto García Hierro -Aleksandar Dezelin -Aleksandr Lukinykh -Alekseev Artem -Alessandro Arzilli -Alessandro Baffa -Alex A Skinner -Alex Brainman -Alex Browne -Alex Carol -Alex Jin -Alex Myasoedov -Alex Plugaru -Alex Schroeder -Alex Sergeyev -Alexander Demakin -Alexander Döring -Alexander F Rødseth -Alexander Guz -Alexander Kauer -Alexander Kucherenko -Alexander Larsson -Alexander Menzhinsky -Alexander Morozov -Alexander Neumann -Alexander Orlov -Alexander Pantyukhin -Alexander Reece -Alexander Surma -Alexander Zhavnerchik -Alexander Zolotov -Alexandre Cesaro -Alexandre Fiori -Alexandre Normand -Alexandre Parentea -Alexandre Viau -Alexei Sholik -Alexey Borzenkov -Alexey Neganov -Alexey Palazhchenko -Alexey Semenyuk -Alexis Hildebrandt -Ali Rizvi-Santiago -Aliaksandr Valialkin -Alif Rachmawadi -Allan Simon -Alok Menghrajani -Aman Gupta -Amazon.com, Inc -Amir Mohammad Saied -Amr Mohammed -Amrut Joshi -Anand K. Mistry -Anders Pearson -André Carvalho -Andre Nathan -Andreas Auernhammer -Andreas Litt -Andrei Korzhevskii -Andrei Tudor Călin -Andrei Vieru -Andrew Austin -Andrew Balholm -Andrew Benton -Andrew Bonventre -Andrew Braunstein -Andrew Bursavich -Andrew Ekstedt -Andrew Etter -Andrew Harding -Andrew Lutomirski -Andrew Pogrebnoy -Andrew Pritchard -Andrew Radev -Andrew Skiba -Andrew Szeto -Andrew Wilkins -Andrew Williams -Andrey Mirtchovski -Andrey Petrov -Andrii Soldatenko -Andrii Soluk -Andriy Lytvynov -Andrzej Żeżel -Andy Balholm -Andy Davis -Andy Finkenstadt -Andy Lindeman -Andy Maloney -Andy Pan -Andy Walker -Anfernee Yongkun Gui -Angelo Bulfone -Anh Hai Trinh -Anit Gandhi -Anmol Sethi -Anschel Schaffer-Cohen -Anthony Alves -Anthony Canino -Anthony Eufemio -Anthony Martin -Anthony Sottile -Anthony Starks -Anthony Voutas -Anthony Woods -Antoine Martin -Antonin Amand -Antonio Antelo -Antonio Bibiano -Antonio Troina -Apisak Darakananda -Apsalar -Aram Hăvărneanu -Areski Belaid -Ariel Mashraki -Arlo Breault -ARM Ltd. -Arnaud Ysmal -Arne Hormann -Arnout Engelen -Aron Nopanen -Arthur Khashaev -Artyom Pervukhin -Arvindh Rajesh Tamilmani -Ashish Gandhi -Atin Malaviya -Ato Araki -Audrey Lim -Audrius Butkevicius -Augusto Roman -Aulus Egnatius Varialus -Aurélien Rainone -awaw fumin -Awn Umar -Axel Wagner -Ayanamist Yang -Aymerick Jéhanne -Azat Kaumov -Baiju Muthukadan -Baokun Lee -Bartosz Grzybowski -Bastian Ike -Ben Burkert -Ben Haines -Ben Lubar -Ben Olive -Ben Shi -Benjamin Black -Benjamin Cable -Benjamin Hsieh -Benny Siegert -Benoit Sigoure -Berengar Lehr -Bill Zissimopoulos -Billie Harold Cleek -Bjorn Tillenius -Bjorn Tipling -Blain Smith -Blake Gentry -Blake Mesdag -Blake Mizerany -Blixt -Bob Briski -Bob Potter -Bobby Powers -Bolt -Borja Clemente -Brad Burch -Brad Morgan -Bradley Falzon -Brady Catherman -Brady Sullivan -Brendan Daniel Tracey -Brett Cannon -Brett Merrill -Brian Dellisanti -Brian Downs -Brian G. Merrell -Brian Gitonga Marete -Brian Kennedy -Brian Kessler -Brian Ketelsen -Brian Smith -Brian Starke -Bryan Alexander -Bryan Chan -Bryan Ford -Bulat Gaifullin -Burak Guven -Caine Tighe -Caleb Martinez -Caleb Spare -Canonical Limited -Carl Chatfield -Carl Henrik Lunde -Carl Johnson -Carlisia Campos -Carlo Alberto Ferraris -Carlos Castillo -Carlos Cirello -Carolyn Van Slyck -Case Nelson -Casey Callendrello -Casey Marshall -Cezar Sá Espinola -ChaiShushan -Charles Fenwick Elliott -Charles L. Dorian -Charles Lee -Chef Software, Inc. -Chew Choon Keat -Cholerae Hu -Chotepud Teo -Chris Ball -Chris Biscardi -Chris Dollin -Chris Farmiloe -Chris Hines -Chris Howey -Chris Jones -Chris Kastorff -Chris Lennert -Chris Liles -Chris McGee -Chris Roche -Chris Smith -Chris Stockton -Christian Alexander -Christian Couder -Christian Himpel -Christian Pellegrin -Christine Hansmann -Christoffer Buchholz -Christoph Blecker -Christoph Hack -Christopher Cahoon -Christopher Guiney -Christopher Henderson -Christopher Nelson -Christopher Nielsen -Christopher Redden -Christopher Wedgwood -Christos Zoulas -CL Sung -Clement Skau -CloudFlare Inc. -Cody Oss -Colin Edwards -Colin Kennedy -Conrad Irwin -Conrad Meyer -Conrado Gouvea -Constantin Konstantinidis -CoreOS, Inc. -Corey Thomasson -Cristian Staretu -Currant -Cyrill Schumacher -Daisuke Fujita -Damian Gryski -Damien Lespiau -Damien Mathieu <42@dmathieu.com> -Dan Ballard -Dan Caddigan -Dan Callahan -Dan Peterson -Dan Sinclair -Daniel Fleischman -Daniel Johansson -Daniel Kerwin -Daniel Krech -Daniel Lidén -Daniel Martí -Daniel Morsing -Daniel Nephin -Daniel Ortiz Pereira da Silva -Daniel Skinner -Daniel Speichert -Daniel Theophanes -Daniel Upton -Daniela Petruzalek -Danny Rosseau -Darren Elwood -Darshan Parajuli -Datong Sun -Dave Cheney -Dave MacFarlane -Dave Russell -David Brophy -David Bürgin <676c7473@gmail.com> -David Calavera -David Carlier -David du Colombier <0intro@gmail.com> -David Forsythe -David G. Andersen -David Howden -David Jakob Fritz -David Leon Gil -David NewHamlet -David R. Jenni -David Sansome -David Stainton -David Thomas -David Titarenco -David Url -David Volquartz Lebech -David Wimmer -Davies Liu -Davor Kapsa -Dean Prichard -Deepak Jois -Denis Bernard -Denis Brandolini -Dennis Kuhnert -Denys Honsiorovskyi -Derek Buitenhuis -Derek McGowan -Derek Parker -Derek Shockey -Dev Ojha -Dev Zhoujun -Develer SRL -Devon H. O'Dell -Dhaivat Pandit -Dhiru Kholia -Dhruvdutt Jadhav -Didier Spezia -Dimitri Sokolyuk -Dimitri Tcaciuc -Diogo Pinela -Dirk Gadsden -Diwaker Gupta -Dmitri Popov -Dmitri Shuralyov -Dmitriy Cherchenko -Dmitriy Dudkin -Dmitriy Shelenin -Dmitry Chestnykh -Dmitry Doroginin -Dmitry Savintsev -Dmitry Yakunin -Dominic Green -Dominik Honnef -Donald Huang -Dong-hee Na -Donovan Hide -Dropbox, Inc. -Duncan Holm -Dustin Herbison -Dustin Sallings -Dustin Shields-Cloues -Dvir Volk -Dylan Waits -Edan Bedrik <3d4nb3@gmail.com> -Eden Li -Eduardo Ramalho -Edward Muller -Egon Elbre -Ehren Kret -Eitan Adler -Eivind Uggedal -Elbert Fliek -Eldar Rakhimberdin -Elena Grahovac -Elias Naur -Elliot Morrison-Reed -Emerson Lin -Emil Hessman -Emil Mursalimov -Emilien Kenler -Emmanuel Odeke -Empirical Interfaces Inc. -Eoghan Sherry -Eric Chiang -Eric Clark -Eric Daniels -Eric Engestrom -Eric Lagergren -Eric Milliken -Eric Pauley -Eric Rescorla -Eric Roshan-Eisner -Eric Rykwalder -Erik Aigner -Erik Dubbelboer -Erik St. Martin -Erik Westrup -Ernest Chiang -Erwin Oegema -Esko Luontola -Euan Kemp -Eugene Kalinin -Evan Hicks -Evan Jones -Evan Phoenix -Evan Shaw -Evgeniy Polyakov -Ewan Chou -Ewan Valentine -Eyal Posener -Fabian Wickborn -Fabian Zaremba -Fabrizio Milo -Facebook, Inc. -Faiyaz Ahmed -Fan Hongjian -Fastly, Inc. -Fatih Arslan -Fazlul Shahriar -Fedor Indutny -Felipe Oliveira -Felix Geisendörfer -Felix Kollmann -Filip Gruszczyński -Filip Haglund -Filippo Valsorda -Firmansyah Adiputra -Florian Uekermann -Florian Weimer -Florin Patan -Ford Hurley -Francesc Campoy -Francisco Claude -Francisco Rojas -Francisco Souza -Frank Schroeder -Frank Somers -Frederic Guillot -Frederick Kelly Mayle III -Frederik Ring -Fredrik Enestad -Fredrik Forsmo -Fredrik Wallgren -Frithjof Schulze -Frits van Bommel -Gabríel Arthúr Pétursson -Gabriel Aszalos -Gabriel Nicolas Avellaneda -Gabriel Russell -Gareth Paul Jones -Gary Burd -Gaurish Sharma -Gautham Thambidorai -Gauthier Jolly -Geert-Johan Riemer -Gengliang Wang -Geoffroy Lorieux -Geon Kim -Georg Reinke -George Gkirtsou -George Shammas -Gerasimos Dimitriadis -Getulio Sánchez -Gideon Jan-Wessel Redelinghuys -Giles Lean -Giulio Iotti -Gleb Stepanov -Google Inc. -Gordon Klaus -Graham King -Graham Miller -Grant Griffiths -Greg Poirier -Greg Ward -Grégoire Delattre -Gregory Man -Guilherme Garnier -Guilherme Goncalves -Guilherme Rezende -Guillaume J. Charmes -Guobiao Mei -Gustav Paul -Gustav Westling -Gustavo Niemeyer -Gwenael Treguier -Gyu-Ho Lee -H. İbrahim Güngör -Hajime Hoshi -HAMANO Tsukasa -Hang Qian -Hanjun Kim -Harald Nordgren -Hari haran -Hariharan Srinath -Harley Laue -Harry Moreno -Harshavardhana -Hauke Löffler -Håvard Haugen -Hector Chu -Hector Martin Cantero -Henning Schmiedehausen -Henrik Edwards -Henrik Hodne -Henry Adi Sumarto -Henry Bubert -Henry Chang -Henry Clifford -Herbert Georg Fischer -Hilko Bengen -Hiroaki Nakamura -Hironao OTSUBO -Hiroshi Ioka -Hitoshi Mitake -Holden Huang -Hong Ruiqi -Hongfei Tan -Hootsuite Inc. -Hsin-Ho Yeh -Hu Keping -Hugues Bruant -Ian Gudger -Ian Kent -IBM -Ibrahim AshShohail -Icarus Sparry -Iccha Sethi -Idora Shinatose -Igneous Systems, Inc. -Igor Dolzhikov -Igor Vashyst -INADA Naoki -Inanc Gumus -Infobaleen AB -Ingo Gottwald -Ingo Krabbe -Ingo Oeser -Intel Corporation -Ioannis Georgoulas -Irfan Sharif -Irieda Noboru -Isaac Ardis -Isaac Wagner -Ivan Babrou -Ivan Bertona -Ivan Markin -Ivan Moscoso -Ivan Ukhov -Jack Britton -Jacob H. Haven -Jacob Hoffman-Andrews -Jae Kwon -Jakob Borg -Jakob Weisblat -Jakub Ryszard Czarnowicz -James Bardin -James Clarke -James Cowgill -James David Chalfant -James Fysh -James Gray -James Hartig -James Lawrence -James Meneghello -James Myers -James Neve -James P. Cooper -James Schofield -James Smith -James Sweet -James Toy -James Treanor -James Whitehead -Jamie Beverly -Jamie Kerr -Jamie Stackhouse -Jamil Djadala -Jan Berktold -Jan H. Hosang -Jan Lehnardt -Jan Mercl <0xjnml@gmail.com> -Jan Newmarch -Jan Pilzer -Jan Ziak <0xe2.0x9a.0x9b@gmail.com> -Jani Monoses -Jared Culp -Jaroslavas Počepko -Jason A. Donenfeld -Jason Barnett -Jason Chu -Jason Del Ponte -Jason Smale -Jason Travis -Jason Wangsadinata -Javier Segura -Jay Weisskopf -Jean-André Santoni -Jean-Francois Cantin -Jean-Nicolas Moal -Jeet Parekh -Jeevanandam M -Jeff Dupont -Jeff Hodges -Jeff R. Allen -Jeff Sickel -Jeff Wendling -Jeffrey H -Jelte Fennema -Jens Frederich -Jeremy Jackins -Jeroen Bobbeldijk -Jerrin Shaji George -Jess Frazelle -Jesse Szwedko -Jesús Espino -Jihyun Yu -Jim McGrath -Jimmy Frasche -Jimmy Zelinskie -Jin-wook Jeong -Jingcheng Zhang -Jingguo Yao -Jiong Du -Jirka Daněk -Jiulong Wang -Joakim Sernbrant -Joe Cortopassi -Joe Farrell -Joe Harrison -Joe Henke -Joe Kyo -Joe Poirier -Joe Shaw -Joe Sylve -Joe Tsai -Joel Sing -Joel Stemmer -Joey Geiger -Johan Brandhorst -Johan Sageryd -John Asmuth -John C Barstow -John Gibb -John Graham-Cumming -John Howard Palevich -John Jeffery -John Jenkins -John Leidegren -John Potocny -John R. Lenton -John Schnake -John Shahid -John Tuley -Johnny Luo -Jonas Bernoulli -Jonathan Boulle -Jonathan Chen -Jonathan Gold -Jonathan Mark -Jonathan Pentecost -Jonathan Rudenberg -Jonathan Stacks -Jonathan Wills -Jongmin Kim -Joonas Kuorilehto -Joop Kiefte -Jordan Krage -Jordan Lewis -Jose Luis Vázquez González -Joseph Holsten -Josh Bleecher Snyder -Josh Chorlton -Josh Deprez -Josh Goebel -Josh Holland -Josh Roppo -Josh Varga -Joshua Chase -Joshua Rubin -Josselin Costanzi -Jostein Stuhaug -Joyent, Inc. -JT Olds -Juan Carlos -Jude Pereira -Jukka-Pekka Kekkonen -Julian Kornberger -Julian Phillips -Julien Salleyron -Julien Schmidt -Junda Liu -Junya Hayashi -Justin Gracenin -Justin Nuß -Justyn Temme -Kai Backman -Kai Trukenmüller -Kale Blankenship -Kaleb Elwert -Kamil Chmielewski -Kamil Kisiel -Kamil Rytarowski -Kang Hu -Karel Pazdera -Karoly Negyesi -Karsten Köhler -Kashav Madan -Kate Manson -Kato Kazuyoshi -Katrina Owen -Kaviraj Kanagaraj -Keegan Carruthers-Smith -Kei Son -Keiji Yoshida -Keith Ball -Keith Rarick -Kelsey Hightower -Kelvin Foo Chuan Lyi -Ken Friedenbach -Ken Rockot -Ken Sedgwick -Kenji Kaneda -Kenji Yano -Kenneth Shaw -Kenny Grant -Kevin Ballard -Kevin Burke -Kevin Kirsche -Kevin Ruffin -Kevin Vu -Kieran Colford -Kim Yongbin -Kir Kolyshkin -Kirk Han -Klaus Post -Kodie Goodwin -Koichi Shiraishi -Koki Ide -Konstantin -Konstantin Shaposhnikov -KPCompass, Inc. -Kris Nova -Kristopher Watts -Kun Li -Kunpei Sakai -Kuntal Majumder -Kyle Consalus -Kyle Isom -Kyle Jones -Kyle Lemons -Kyle Shannon -Kyohei Kadota -Kyrylo Silin -L Campbell -Lai Jiangshan -Lakshay Garg -Lanre Adelowo -Larry Hosken -Lars Jeppesen -Lars Lehtonen -Lars Wiegman -Larz Conwell -Laurent Voisin -Laurie Clark-Michalek -LE Manh Cuong -Lee Hinman -Lee Packham -Lehner Florian -Leigh McCulloch -Leo Antunes -Leon Klingele -Leonel Quinteros -Lev Shamardin -Lewin Bormann -Liberty Fund Inc -Linaro Limited -Lion Yang -Lloyd Dewolf -Lorenzo Masini -Lorenzo Stoakes -Luan Santos -Lubomir I. Ivanov -Luca Greco -Lucas Bremgartner -Lucien Stuker -Lucio De Re -Ludi Rehak -Luigi Riefolo -Luit van Drongelen -Luka Zakrajšek -Luke Curley -Luke Granger-Brown -Lyle Franklin -Ma Peiqi -Maicon Costa -Maksym Trykur -Mal Curtis -Manfred Touron -Manigandan Dharmalingam -Manish Goregaokar -Mansour Rahimi -Manu S Ajith -Manuel Mendez -Marc Weistroff -Marcel Edmund Franke -Marcelo Cantos -Marcelo E. Magallon -Marco Hennings -Marin Bašić -Mario Arranz -Mark Adams -Mark Bucciarelli -Mark Percival -Mark Pulford -Mark Rushakoff -Mark Severson -Mark Theunissen -Mark Wolfe -Marko Juhani Silokunnas -Marko Mudrinic -Marko Tiikkaja -Markover Inc. DBA Poptip -Markus Duft -Markus Sonderegger -Markus Zimmermann -Martin Bertschler -Martin Garton -Martin Hamrle -Martin Hoefling -Martin Kunc -Martin Lindhe -Martin Möhrmann -Martin Neubauer -Martin Olsen -Martin Olsson -Martin Probst -Martin Sucha -Martins Sipenko -Marvin Stenger -Marwan Sulaiman -Maryan Hratson -Masahiro Furudate -Masahiro Wakame -Masaki Yoshida -Mat Byczkowski -Mat Ryer -Máté Gulyás -Matej Baćo -Mateus Amin -Mateusz Czapliński -Mathias Beke -Mathias Hall-Andersen -Mathias Leppich -Mathieu Lonjaret -Mats Lidell -Matt Aimonetti -Matt Blair -Matt Bostock -Matt Dee -Matt Drollette -Matt Harden -Matt Jibson -Matt Joiner -Matt Juran -Matt Layher -Matt Reiferson -Matt Robenolt -Matt Strong -Matt T. Proud -Matt Williams -Matthew Brennan -Matthew Broberg -Matthew Cottingham -Matthew Denton -Matthew Holt -Matthew Horsnell -Matthieu Hauglustaine -Matthieu Olivier -Matthijs Kooijman -Max Riveiro -Max Schmitt -Maxim Khitrov -Maxime de Roucy -Máximo Cuadros Ortiz -Maxwell Krohn -Maya Rashish -Mayank Kumar -MediaMath, Inc -Meir Fischer -Meng Zhuo -Meteor Development Group -Mhd Sulhan -Micah Stetson -Michael Brandenburg -Michael Chaten -Michael Dorner -Michael Edwards -Michael Elkins -Michael Fraenkel -Michael Gehring -Michael Hendricks -Michael Hoisie -Michael Kasch -Michael Käufl -Michael Lewis -Michael MacInnis -Michael Marineau -Michael McConville -Michael McLoughlin -Michael Pearson -Michael Schaller -Michael Schurter -Michael Stapelberg -Michael Steinert -Michael Teichgräber -Michael Vetter -Michal Bohuslávek -Michał Derkacz -Michal Franc -Michal Pristas -Miek Gieben -Miguel Mendez -Miguel Molina -Mihai Borobocea -Mihail Minaev -Mikael Tillenius -Mike Andrews -Mike Appleby -Mike Houston -Mike Kabischev -Mike Rosset -Mike Tsao -Mikhail Gusarov -Mikhail Panchenko -Miki Tebeka -Mikio Hara -Mikkel Krautz -Milan Knezevic -Milutin Jovanović -MinJae Kwon -Miquel Sabaté Solà -Miroslav Genov -Misty De Meo -Mohit Agarwal -Mohit kumar Bajoria -Momchil Velikov -Monty Taylor -Moov Corporation -Moriyoshi Koizumi -Morten Siebuhr -Môshe van der Sterre -Mostyn Bramley-Moore -Muhammad Falak R Wani -Muhammed Uluyol -Mura Li -Nan Deng -Naoki Kanatani -Nate Wilkinson -Nathan Cantelmo -Nathan Caza -Nathan Humphreys -Nathan John Youngman -Nathan Otterness -Nathan P Finch -Nathan VanBenschoten -Nathan Youngman -Nathaniel Cook -Naveen Kumar Sangi -Neelesh Chandola -Neil Lyons -Netflix, Inc. -Neuman Vong -Neven Sajko -Nevins Bartolomeo -Nexedi -ngmoco, LLC -Niall Sheridan -Nic Day -Nicholas Katsaros -Nicholas Maniscalco -Nicholas Presta -Nicholas Sullivan -Nicholas Waples -Nick Craig-Wood -Nick Leli -Nick Miyake -Nick Patavalis -Nick Petroni -Nick Robinson -Nick Smolin -Nicolas BRULEZ -Nicolas Kaiser -Nicolas Owens -Nicolas S. Dade -Niek Sanders -Niels Widger -Nigel Kerr -Nik Nyby -Nikhil Benesch -Nikita Gillmann -Niklas Schnelle -Niko Dziemba -Nikolay Turpitko -Nils Larsgård -Niranjan Godbole -Nishanth Shanmugham -Noah Campbell -Noble Johnson -Noel Georgi -Norberto Lopes -Odin Ugedal -Oleg Bulatov -Oleg Vakheta -Oleku Konko -Oling Cat -Oliver Hookins -Oliver Tonnhofer -Olivier Antoine -Olivier Duperray -Olivier Poitrey -Olivier Saingre -Oracle -Orange -Orijtech, Inc. -Özgür Kesim -Pablo Lalloni -Pablo Rozas Larraondo -Pablo Santiago Blum de Aguiar -Padraig Kitterick -Pallat Anchaleechamaikorn -Palm Stone Games -Paolo Giarrusso -Paolo Martini -Parker Moore -Pascal S. de Kloe -Pat Moroney -Patrick Crosby -Patrick Gavlin -Patrick Higgins -Patrick Lee -Patrick Mézard -Patrick Mylund Nielsen -Patrick Pelletier -Patrick Smith -Paul A Querna -Paul Boyd -Paul Hammond -Paul Jolly -Paul Lalonde -Paul Meyer -Paul PISCUC -Paul Querna -Paul Rosania -Paul Ruest -Paul Sbarra -Paul Smith -Paul Tyng -Paul van Brouwershaven -Paulo Casaretto -Pavel Paulau -Pavel Zinovkin -Pavlo Sumkin -Pawel Knap -Percy Wegmann -Perry Abbott -Petar Maymounkov -Peter Armitage -Peter Bourgon -Peter Conerly -Peter Froehlich -Peter Kleiweg -Peter Moody -Peter Morjan -Peter Mundy -Peter Nguyen -Péter Surányi -Péter Szilágyi -Peter Teichman -Peter Waldschmidt -Peter Waller -Peter Williams -Peter Zhang -Petrica Voicu -Phil Pearl -Philip Børgesen -Philip Brown -Philip Hofer -Philip K. Warren -Philip Nelson -Pierre Durand -Pierre Prinetti -Pierre Roullon -Piers -Pieter Droogendijk -Pietro Gagliardi -Piyush Mishra -Platform.sh -Pontus Leitzler -Prasanga Siripala -Prashant Varanasi -Pravendra Singh -Preetam Jinka -Qais Patankar -Qiuxuan Zhu -Qualcomm Data Center, Inc. -Quan Tran -Quan Yong Zhai -Quentin Perez -Quentin Renard -Quoc-Viet Nguyen -RackTop Systems Inc. -Radek Sohlich -Radu Berinde -Rafal Jeczalik -Raif S. Naffah -RainTank -Rajat Goel -Rajath Agasthya -Rajender Reddy Kompally -Ralph Corderoy -Ramazan AYYILDIZ -Raphael Geronimi -Ravil Bikbulatov -RaviTeja Pothana -Ray Tung -Raymond Kazlauskas -Red Hat, Inc. -Reilly Watson -Reinaldo de Souza Jr -Remi Gillig -Rémy Oudompheng -Rens Rikkerink -Ricardo Padilha -Richard Barnes -Richard Crowley -Richard Dingwall -Richard Eric Gavaletz -Richard Gibson -Richard Miller -Richard Musiol -Rick Arnold -Rick Sayre -Risto Jaakko Saarelma -Rob Norman -Rob Phoenix -Robert Daniel Kortschak -Robert Dinu -Robert Figueiredo -Robert Hencke -Robert Obryk -Robert Stepanek -Robert-André Mauchin -Roberto Clapis -Robin Eklind -Rodolfo Carvalho -Rodrigo Moraes de Oliveira -Rodrigo Rafael Monti Kochenburger -Roger Pau Monné -Roger Peppe -Roland Shoemaker -Roman Budnikov -Ron Hashimoto -Ron Minnich -Ross Chater -Ross Light -Rowan Marshall -Rowan Worth -Rudi Kramer -Russell Haering -Ryan Bagwell -Ryan Boehning -Ryan Canty -Ryan Hitchman -Ryan Lower -Ryan Roden-Corrent -Ryan Seys -Ryan Slade -Ryan Zhang -Ryoichi KATO -Ryuji Iwata -Ryuma Yoshida -Ryuzo Yamamoto -S.Çağlar Onur -Sabin Mihai Rapan -Sakeven Jiang -Salmān Aljammāz -Sam Boyer -Sam Hug -Sam Whited -Sami Pönkänen -Samuele Pedroni -Sanjay Menakuru -Sascha Brawer -Sasha Sobol -Scott Barron -Scott Bell -Scott Crunkleton -Scott Ferguson -Scott Lawrence -Sean Rees -Sebastien Binet -Sébastien Paolacci -Seiji Takahashi -Sergei Skorobogatov -Sergey 'SnakE' Gromov -Sergey Lukjanov -Sergey Mishin -Sergey Mudrik -Sergey Semin -Sergio Luis O. B. Correia -Sergiusz Bazanski -Seth Hoenig -Seth Vargo -Shahar Kohanim -Shamil Garatuev -Shane Hansen -Shaozhen Ding -Shaun Dunning -Shawn Smith -Shenghou Ma -Shengyu Zhang -Shi Han Ng -Shinji Tanaka -Shintaro Kaneko -Shivakumar GN -Silvan Jegen -Simon Jefford -Simon Rawet -Simon Thulbourn -Simon Whitehead -Sina Siadat -Sokolov Yura -Song Gao -Sourcegraph Inc -Spencer Nelson -Spring Mc -Square, Inc. -Sridhar Venkatakrishnan -StalkR -Stan Schwertly -Stanislav Afanasev -Steeve Morin -Stefan Nilsson -Stéphane Travostino -Stephen Lewis -Stephen McQuay -Stephen Searles -Stephen Weinberg -Steve Gilbert -Steve McCoy -Steve Phillips -Steve Streeting -Steven Elliot Harris -Steven Erenst -Steven Hartland -Steven Wilkin -Stripe, Inc. -Sukrit Handa -Sunny -Suriyaa Sundararuban -Suyash -Sven Almgren -Sylvain Zimmer -Syohei YOSHIDA -Szabolcs Nagy -Taavi Kivisik -Tad Fisher -Tad Glines -Tailscale Inc. -Taj Khattra -Takayoshi Nishida -Takeshi YAMANASHI <9.nashi@gmail.com> -Takuya Ueda -Tal Shprecher -Tamir Duberstein -Tao Wang -Tarmigan Casebolt -Taro Aoki -Taru Karttunen -Tatsuhiro Tsujikawa -Teague Cole -Ted Kornish -Tejasvi Nareddy -Teleport Inc. -Terin Stock -Terrel Shumway -Tetsuo Kiso -Thanabodee Charoenpiriyakij -Thanatat Tamtan -Thiago Avelino -Thiago Fransosi Farina -Thomas Alan Copeland -Thomas Bonfort -Thomas Bruyelle -Thomas de Zeeuw -Thomas Desrosiers -Thomas Kappler -Thomas Meson -Thomas Wanielista -Thorben Krueger -Thordur Bjornsson -Tiago Queiroz -Tilman Dilo -Tim Cooijmans -Tim Cooper -Tim Ebringer -Tim Heckman -Tim Henderson -Tim Wright -Timo Savola -Timo Truyts -Timothy Studd -Tobias Assarsson -Tobias Columbus -Tobias Klauser -Todd Neal -Tom Heng -Tom Levy -Tom Limoncelli -Tom Linford -Tom Payne -Tom Thorogood -Tommy Schaefer -Tomoya Ishizaki -Tonis Tiigi -Tony Walker -Tor Andersson -Tormod Erevik Lea -Toshiki Shima -Totoro W -Travis Bischel -Travis Cline -Trey Lawrence -Trey Roessig -Trey Tacon -Tristan Colgate -Tristan Ooohry -Tristan Rice -Troels Thomsen -Trung Nguyen -Tudor Golubenco -Tugdual Saunier -Tuo Shan -Tyler Bunnell -Tyler Treat -Uber Technologies -Ugorji Nwoke -Ulf Holm Nielsen -Ulrich Kunitz -Upthere, Inc. -Uriel Mangado -Vadim Grek -Vadim Vygonets -Vee Zhang -Vendasta -Veselkov Konstantin -Victor Vrantchan -Vignesh Ramachandra -Vincent Ambo -Vincent Batts -Vincent Vanackere -Vinu Rajashekhar -Vishvananda Ishaya -Vitor De Mario -Vladimir Mihailenco -Vladimir Nikishenko -Vladimir Stefanovic -Vladimir Varankin -VMware, Inc. -Volker Dobler -W. Trevor King -Wade Simmons -Wander Lairson Costa -Wayne Ashley Berry -Weaveworks -Wèi Cōngruì -Wei Fu -Wei Guangjing -Weichao Tang -Weixie Cui <523516579@qq.com> -Wembley G. Leach, Jr -Will Faught -Will Storey -Willem van der Schyff -William Chang -William Josephson -William Orr -Wisdom Omuya -Wu Yunzhou -Xi Ruoyao -Xia Bin -Xing Xing -Xu Fei -Xudong Zhang -Xudong Zheng <7pkvm5aw@slicealias.com> -Xuyang Kang -Yahoo Inc. -Yamagishi Kazutoshi -Yann Kerhervé -Yann Salaün -Yao Zhang -Yasha Bubnov -Yasuharu Goto -Yasuhiro Matsumoto -Yasuyuki Oka -Yazen Shunnar -Yestin Sun -Yissakhar Z. Beck -Yo-An Lin -Yongjian Xu -Yorman Arias -Yoshiyuki Kanno -Yoshiyuki Mineo -Yosuke Akatsuka -Yuji Yaginuma -Yukihiro Nishinaka <6elpinal@gmail.com> -Yury Smolsky -Yusuke Kagiwada -Yuusei Kuwana -Yuval Pavel Zholkover -Zac Bergquist -Zach Bintliff -Zach Gershman -Zachary Gershman -Zak -Zakatell Kanda -Zellyn Hunter -Zemanta d.o.o. -Zev Goldstein -Zheng Dayu -Zhongtao Chen -Zhou Peng -Ziad Hatahet -Zorion Arrizabalaga -Максим Федосеев -Роман Хавроненко -Тарас Буник -Фахриддин Балтаев -张嵩 -申习之 diff --git a/CONTRIBUTORS b/CONTRIBUTORS deleted file mode 100644 index 1984d44c537736..00000000000000 --- a/CONTRIBUTORS +++ /dev/null @@ -1,2761 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the Go repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# http://code.google.com/legal/individual-cla-v1.0.html -# http://code.google.com/legal/corporate-cla-v1.0.html -# -# The agreement for individuals can be filled out on the web. - -# Names should be added to this file like so: -# Individual's name -# Individual's name -# -# An entry with multiple email addresses specifies that the -# first address should be used in the submit logs and -# that the other addresses should be recognized as the -# same person when interacting with Gerrit. - -# Please keep the list sorted. - -Aamir Khan -Aaron Beitch -Aaron Bieber -Aaron Cannon -Aaron France -Aaron Jacobs -Aaron Jensen -Aaron Kemp -Aaron Patterson -Aaron Sheah -Aaron Stein -Aaron Torres -Aaron Zinman -Aarti Parikh -Abdullah Al Maruf -Abe Haskins -Abhinav Gupta -Adam Azarchs -Adam Bender -Adam Eijdenberg -Adam Harvey -Adam Kisala -Adam Langley -Adam Medzinski -Adam Mitha -Adam Shannon -Adam Shelton -Adam Sindelar -Adam Thomason -Adam Williams -Adam Woodbeck -Adarsh Ravichandran -Adel Rodríguez -Adin Scannell -Aditya Harindar -Aditya Mukerjee -Adrian Hesketh -Adrian Nos -Adrian O'Grady -Adrien Bustany -Adrien Delorme -Adrien Petel -Aécio Júnior -Aeneas Rekkas (arekkas) -Afanasev Stanislav -Agis Anastasopoulos -Agniva De Sarker -Ahmed W. Mones -Ahmet Aktürk -Ahmet Alp Balkan -Ahmet Soormally -Ahmy Yulrizka -Ahsun Ahmed -Aidan Coyle -Aiden Scandella -Ainar Garipov -Aishraj Dahal -Ajanthan Balachandran -Akhil Indurti -Akihiro Suda -Akshat Kumar -Al Cutter -Alan Braithwaite -Alan Donovan -Alan Shreve -Albert Nigmatzianov -Albert Strasheim -Albert Teoh -Albert Yu -Alberto Bertogli -Alberto Donizetti -Alberto García Hierro -Alec Benzer -Alejandro García Montoro -Aleksa Sarai -Aleksandar Dezelin -Aleksandr Lukinykh -Aleksandr Razumov -Alekseev Artem -Aleksei Tirman -Alessandro Arzilli -Alessandro Baffa -Alex A Skinner -Alex Brainman -Alex Bramley -Alex Browne -Alex Buchanan -Alex Carol -Alex Gaynor -Alex Harford -Alex Hays -Alex Jin -Alex Kohler -Alex Myasoedov -Alex Opie -Alex Plugaru -Alex Schroeder -Alex Sergeyev -Alex Tokarev -Alex Vaghin -Alex Zhirov -Alexander Demakin -Alexander Döring -Alexander F Rødseth -Alexander Greim -Alexander Guz -Alexander Kauer -Alexander Klauer -Alexander Kucherenko -Alexander Larsson -Alexander Lourier -Alexander Menzhinsky -Alexander Morozov -Alexander Neumann -Alexander Nohe -Alexander Orlov -Alexander Pantyukhin -Alexander Polcyn -Alexander Rakoczy -Alexander Reece -Alexander Surma -Alexander Zhavnerchik -Alexander Zillion -Alexander Zolotov -Alexandr Mayorskiy -Alexandre Cesaro -Alexandre Fiori -Alexandre Maari -Alexandre Normand -Alexandre Parentea -Alexandre Viau -Alexandru Moșoi -Alexei Sholik -Alexey Alexandrov -Alexey Borzenkov -Alexey Naidonov -Alexey Neganov -Alexey Palazhchenko -Alexey Semenyuk -Alexey Vilenskiy -Alexis Hildebrandt -Alexis Hunt -Alexis Imperial-Legrand -Ali Farooq -Ali Rizvi-Santiago -Aliaksandr Valialkin -Alice Merrick -Alif Rachmawadi -Allan Guwatudde -Allan Simon -Allen Li -Alok Menghrajani -Alwin Doss -Aman Gupta -Amarjeet Anand -Amir Mohammad Saied -Amit Kumar -Amr Mohammed -Amrut Joshi -An Long -An Xiao -Anand K. Mistry -Ananya Saxena -Anatol Pomozov -Anders Pearson -Anderson Queiroz -André Carvalho -André Martins -Andre Nathan -Andrea Nodari -Andrea Simonini -Andrea Spadaccini -Andreas Auernhammer -Andreas Jellinghaus -Andreas Litt -Andrei Enshin -Andrei Gherzan -Andrei Korzhevskii -Andrei Matei -Andrei Tudor Călin -Andrei Vagin -Andrei Vieru -Andres Erbsen -Andres Lowrie -Andrew Austin -Andrew Balholm -Andrew Benton -Andrew Bonventre -Andrew Braunstein -Andrew Bursavich -Andrew Ekstedt -Andrew Etter -Andrew G. Morgan -Andrew Gerrand -Andrew Harding -Andrew Jackura -Andrew Kemm -Andrew Louis -Andrew Lutomirski -Andrew Medvedev -Andrew Pilloud -Andrew Pogrebnoy -Andrew Poydence -Andrew Pritchard -Andrew Radev -Andrew Skiba -Andrew Stormont -Andrew Stribblehill -Andrew Szeto -Andrew Todd -Andrew Werner -Andrew Wilkins -Andrew Williams -Andrew Z Allen -Andrey Bokhanko -Andrey Mirtchovski -Andrey Petrov -Andrii Soldatenko -Andrii Soluk -Andriy Lytvynov -Andrzej Żeżel -Andy Balholm -Andy Davis -Andy Finkenstadt -Andy Lindeman -Andy Maloney -Andy Pan -Andy Walker -Andy Wang -Andy Williams -Andy Zhao -Andzej Maciusovic -Anfernee Yongkun Gui -Angelo Bulfone -Anh Hai Trinh -Anit Gandhi -Ankit Goyal -Anmol Sethi -Annirudh Prasad -Anschel Schaffer-Cohen -Anthony Alves -Anthony Canino -Anthony Eufemio -Anthony Fok -Anthony Martin -Anthony Sottile -Anthony Starks -Anthony Voutas -Anthony Woods -Antoine GIRARD -Antoine Martin -Anton Gyllenberg -Anton Kuklin -Antonin Amand -Antonio Antelo -Antonio Bibiano -Antonio Garcia -Antonio Huete Jimenez -Antonio Murdaca -Antonio Troina -Anze Kolar -Aofei Sheng -Apisak Darakananda -Aram Hăvărneanu -Araragi Hokuto -Arash Bina -Arda Güçlü -Areski Belaid -Ariel Mashraki -Arkadi Pyuro -Arlo Breault -Arnaud Ysmal -Arne Hormann -Arnout Engelen -Aron Nopanen -Artem Alekseev -Artem Khvastunov -Artem Kolin -Arthur Fabre -Arthur Khashaev -Artur M. Wolff -Artyom Pervukhin -Arvindh Rajesh Tamilmani -Ashish Bhate -Ashish Gandhi -Asim Shankar -Assel Meher -Atin Malaviya -Ato Araki -Atsushi Toyama -Audrey Lim -Audrius Butkevicius -Augusto Roman -Aulus Egnatius Varialus -Aurélien Rainone -Aurélio A. Heckert -Austin Clements -Avi Flax -Aviv Klasquin Komissar -awaw fumin -Awn Umar -Axel Wagner -Ayan George -Ayanamist Yang -Ayke van Laethem -Aymerick Jéhanne -Ayzat Sadykov -Azat Kaumov -Baiju Muthukadan -Balaram Makam -Balazs Lecz -Baokun Lee -Barnaby Keene -Bartosz Grzybowski -Bartosz Oler -Bassam Ojeil -Bastian Ike -Ben Burkert -Ben Cartwright-Cox -Ben Eitzen -Ben Fried -Ben Haines -Ben Hoyt -Ben Hutchings -Ben Kraft -Ben Laurie -Ben Lubar -Ben Lynn -Ben Olive -Ben Schwartz -Ben Shi -Ben Toews -Benjamin Barenblat -Benjamin Black -Benjamin Cable -Benjamin Hsieh -Benjamin Peterson -Benjamin Prosnitz -Benjamin Wester -Benjamin Wuethrich -Benny Siegert -Benoit Sigoure -Berengar Lehr -Berkant Ipek <41230766+0xbkt@users.noreply.github.com> -Bharath Thiruveedula -Bhavin Gandhi -Bill Neubauer -Bill O'Farrell -Bill Prin -Bill Thiede -Bill Zissimopoulos -Billie Harold Cleek -Billy Lynch -Billy Zaelani Malik -Bjørn Erik Pedersen -Bjorn Tillenius -Bjorn Tipling -Blain Smith -Blake Gentry -Blake Mesdag -Blake Mizerany -Blixt -Bob Briski -Bob McNaughton -Bob Potter -Bobby DeSimone -Bobby Powers -Boqin Qin -Boris Nagaev -Borja Clemente -Boshi Lian -Brad Burch -Brad Erickson -Brad Fitzpatrick -Brad Garcia -Brad Jones -Brad Morgan -Brad Whitaker -Braden Bassingthwaite -Bradford Lamson-Scribner -Bradley Falzon -Brady Catherman -Brady Sullivan -Branden J. Brown -Brandon Bennett -Brandon Gilmore -Brandon Philips -Brandon Ryan -Brave Cow -Brayden Cloud -Brendan Daniel Tracey -Brendan O'Dea -Brett Cannon -Brett Merrill -Brian Dellisanti -Brian Downs -Brian Falk -Brian G. Merrell -Brian Gitonga Marete -Brian Kennedy -Brian Kessler -Brian Ketelsen -Brian Slesinsky -Brian Smith -Brian Starke -Bryan Alexander -Bryan Boreham -Bryan C. Mills -Bryan Chan -Bryan Ford -Bryan Heden -Bulat Gaifullin -Burak Guven -Caine Tighe -Caio Marcelo de Oliveira Filho -Caleb Martinez -Caleb Spare -Carl Chatfield -Carl Henrik Lunde -Carl Jackson -Carl Johnson -Carl Mastrangelo -Carl Menezes -Carl Shapiro -Carlisia Campos -Carlo Alberto Ferraris -Carlos Alexandro Becker -Carlos Amedee -Carlos Castillo -Carlos Cirello -Carlos Eduardo -Carlos Eduardo Seo -Carlos Iriarte -Carlos Souza -Carolyn Van Slyck -Carrie Bynon -Carson Hoffman -Cary Hull -Case Nelson -Casey Callendrello -Casey Marshall -Catalin Nicutar -Catalin Patulea -Cathal O'Callaghan -Cedric Staub -Cezar Sá Espinola -Chad Rosier -ChaiShushan -Changkun Ou -Channing Kimble-Brown -Chao Xu -Charles Fenwick Elliott -Charles Kenney -Charles L. Dorian -Charles Lee -Charles Weill -Charlie Moog -Charlotte Brandhorst-Satzkorn -Chauncy Cullitan -Chen Zhidong -Chen Zhihan -Cherry Mui -Chew Choon Keat -Chiawen Chen -Chirag Sukhala -Cholerae Hu -Chotepud Teo -Chris Ball -Chris Biscardi -Chris Broadfoot -Chris Dollin -Chris Farmiloe -Chris Hines -Chris Howey -Chris Hundt -Chris Jones -Chris Kastorff -Chris Le Roy -Chris Lennert -Chris Liles -Chris Manghane -Chris Marchesi -Chris McGee -Chris Raynor -Chris Roche -Chris Smith -Chris Stockton -Chris Taylor -Chris Waldon -Chris Zou -Christian Alexander -Christian Couder -Christian Himpel -Christian Muehlhaeuser -Christian Pellegrin -Christian R. Petrin -Christian Svensson -Christine Hansmann -Christoffer Buchholz -Christoph Blecker -Christoph Hack -Christopher Cahoon -Christopher Guiney -Christopher Henderson -Christopher Hlubek -Christopher Koch -Christopher Loessl -Christopher Nelson -Christopher Nielsen -Christopher Redden -Christopher Swenson -Christopher Thomas <53317512+chrisssthomas@users.noreply.github.com> -Christopher Wedgwood -Christos Zoulas -Christy Perez -CL Sung -Clément Chigot -Clement Skau -Clint J. Edwards -Cody Oss -Colby Ranger -Colin Arnott -Colin Cross -Colin Edwards -Colin Kennedy -Colin Nelson -Colin Rice -Conrad Irwin -Conrad Meyer -Conrado Gouvea -Constantin Konstantinidis -Corey Thomasson -Corne van der Plas -Cosmos Nicolaou -Costin Chirvasuta -Craig Citro -Cristian Staretu -Cristo García -cui fliter -Cuihtlauac ALVARADO -Cuong Manh Le -Curtis La Graff -Cyrill Schumacher -Dai Jie -Daisuke Fujita -Daisuke Suzuki -Daker Fernandes Pinheiro -Damian Gryski -Damien Lespiau -Damien Mathieu <42@dmathieu.com> -Damien Neil -Damien Tournoud -Dan Ballard -Dan Caddigan -Dan Callahan -Dan Harrington -Dan Jacques -Dan Johnson -Dan McArdle -Dan Peterson -Dan Pupius -Dan Scales -Dan Sinclair -Daniel Cohen -Daniel Cormier -Daniël de Kok -Daniel Fleischman -Daniel Ingram -Daniel Johansson -Daniel Kerwin -Daniel Kessler -Daniel Krech -Daniel Kumor -Daniel Langner -Daniel Lidén -Daniel Lublin -Daniel Mangum -Daniel Martí -Daniel McCarney -Daniel Morsing -Daniel Nadasi -Daniel Nephin -Daniel Ortiz Pereira da Silva -Daniel S. Fava -Daniel Skinner -Daniel Speichert -Daniel Theophanes -Daniel Upton -Daniela Petruzalek -Danish Dua -Danish Prakash -Danny Rosseau -Daria Kolistratova -Darien Raymond -Darren Elwood -Darren Grant -Darren McCleary -Darshan Parajuli -Datong Sun -Dave Borowitz -Dave Bort -Dave Cheney -Dave Day -Dave Grijalva -Dave MacFarlane -Dave Pifke -Dave Russell -David Anderson -David Barnett -David Benjamin -David Black -David Bond -David Brophy -David Bürgin <676c7473@gmail.com> -David Calavera -David Carlier -David Carter -David Chase -David Covert -David Crawshaw -David du Colombier <0intro@gmail.com> -David Finkel -David Forsythe -David G. Andersen -David Glasser -David Golden -David Heuschmann -David Howden -David Hubbard -David Jakob Fritz -David Jones -David Lazar -David Leon Gil -David McLeish -David Ndungu -David NewHamlet -David Presotto -David Qu -David R. Jenni -David Sansome -David Stainton -David Symonds -David Thomas -David Timm -David Titarenco -David Tolpin -David Url -David Volquartz Lebech -David Wimmer -Davies Liu -Davor Kapsa -Dean Eigenmann <7621705+decanus@users.noreply.github.com> -Dean Prichard -Deepak Jois -Deepak S -Denis Bernard -Denis Brandolini -Denis Isaev -Denis Nagorny -Dennis Kuhnert -Denys Honsiorovskyi -Denys Smirnov -Derek Buitenhuis -Derek Che -Derek McGowan -Derek Parker -Derek Phan -Derek Shockey -Dev Ojha -Dev Zhoujun -Devon H. O'Dell -Dhaivat Pandit -Dhananjay Nakrani -Dhiru Kholia -Dhruvdutt Jadhav -Di Xiao -Didier Spezia -Diego Medina -Diego Siqueira -Dieter Plaetinck -Dilyn Corner -Dimitri Sokolyuk -Dimitri Tcaciuc -Dina Garmash -Diogo Pinela -Dirk Gadsden -Diwaker Gupta -Dmitri Goutnik -Dmitri Popov -Dmitri Shuralyov -Dmitrii Okunev -Dmitriy Cherchenko -Dmitriy Dudkin -Dmitriy Shelenin -Dmitriy Vyukov -Dmitry Chestnykh -Dmitry Doroginin -Dmitry Mottl -Dmitry Neverov -Dmitry Savintsev -Dmitry Yakunin -Doga Fincan -Domas Tamašauskas -Domen Ipavec -Dominic Della Valle -Dominic Green -Dominik Honnef -Dominik Vogt -Don Byington -Donald Huang -Dong-hee Na -Donovan Hide -Doug Anderson -Doug Fawley -Douglas Danger Manley -Drew Flower -Drew Hintz -Drew Richardson -Duco van Amstel -Duncan Holm -Dustin Carlino -Dustin Herbison -Dustin Long -Dustin Sallings -Dustin Shields-Cloues -Dvir Volk -Dylan Waits -Ed Schouten -Edan Bedrik <3d4nb3@gmail.com> -Eddie Scholtz -Eden Li -Eduard Urbach -Eduardo Ramalho -Eduardo Villaseñor -Edward Muller -Egon Elbre -Ehren Kret -Eitan Adler -Eivind Uggedal -El Mostafa Idrassi -Elbert Fliek -Eldar Rakhimberdin -Elena Grahovac -Eli Bendersky -Elias Naur -Elliot Morrison-Reed -Ellison Leão -Elvina Yakubova -Emerson Lin -Emil Bektimirov -Emil Hessman -Emil Mursalimov -Emilien Kenler -Emmanuel Odeke -Emrecan Bati -Eno Compton -Eoghan Sherry -Eric Biggers -Eric Brown -Eric Chiang -Eric Clark -Eric Daniels -Eric Engestrom -Eric Garrido -Eric Koleda -Eric Lagergren -Eric Milliken -Eric Pauley -Eric Ponce -Eric Rescorla -Eric Roshan-Eisner -Eric Rutherford -Eric Rykwalder -Eric Wang -Erick Tryzelaar -Erik Aigner -Erik Dubbelboer -Erik St. Martin -Erik Staab -Erik Westrup -Erin Masatsugu -Ernest Chiang -Erwin Oegema -Esko Luontola -Ethan Burns -Ethan Hur -Ethan Miller -Euan Kemp -Eugene Formanenko -Eugene Kalinin -Evan Broder -Evan Brown -Evan Digby -Evan Hicks -Evan Jones -Evan Klitzke -Evan Kroske -Evan Martin -Evan Phoenix -Evan Shaw -Evgeniy Kulikov -Evgeniy Polyakov -Ewan Chou -Ewan Valentine -Eyal Posener -Fabian Wickborn -Fabian Zaremba -Fabrizio Milo -Faiyaz Ahmed -Fan Hongjian -Fangming Fang -Fannie Zhang -Fatih Arslan -Fazal Majid -Fazlul Shahriar -Federico Bond -Federico Guerinoni -Federico Simoncelli -Fedor Indutny -Fedor Korotkiy -Felipe Oliveira -Felix Bünemann -Felix Cornelius <9767036+fcornelius@users.noreply.github.com> -Felix Geisendörfer -Felix Kollmann -Ferenc Szabo -Fernandez Ludovic -Filip Gruszczyński -Filip Haglund -Filip Stanis -Filippo Valsorda -Firmansyah Adiputra -Florian Forster -Florian Uekermann -Florian Weimer -Florin Patan -Folke Behrens -Ford Hurley -Francesc Campoy -Francesco Guardiani -Francesco Renzi -Francisco Claude -Francisco Rojas -Francisco Souza -Frank Schroeder -Frank Somers -Frederic Guillot -Frederick Kelly Mayle III -Frederik Ring -Frederik Zipp -Fredrik Enestad -Fredrik Forsmo -Fredrik Wallgren -Frew Schmidt -Frithjof Schulze -Frits van Bommel -Fujimoto Kyosuke -Fumitoshi Ukai -G. Hussain Chinoy -Gaal Yahas -Gabríel Arthúr Pétursson -Gabriel Aszalos -Gabriel Guzman -Gabriel Nelle -Gabriel Nicolas Avellaneda -Gabriel Rosenhouse -Gabriel Russell -Gabriel Vasile -Gareth Paul Jones -Garret Kelly -Garrick Evans -Garry McNulty -Gary Burd -Gary Elliott -Gaurav Singh -Gaurish Sharma -Gautham Thambidorai -Gauthier Jolly -Gawen Arab -Geert-Johan Riemer -Genevieve Luyt -Gengliang Wang -Geoff Berry -Geoffroy Lorieux -Geon Kim -Georg Reinke -George Gkirtsou -George Hartzell -George Shammas -George Tsilias -Gerasimos (Makis) Maropoulos -Gerasimos Dimitriadis -Gergely Brautigam -Gernot Vormayr -Gert Cuykens -Getulio Sánchez -Ghazni Nattarshah -Gianguido Sora` -Gideon Jan-Wessel Redelinghuys -Giles Lean -Giovanni Bajo -GitHub User @180909 (70465953) <734461790@qq.com> -GitHub User @6543 (24977596) <6543@obermui.de> -GitHub User @aca (50316549) -GitHub User @ajnirp (1688456) -GitHub User @ajz01 (4744634) -GitHub User @alkesh26 (1019076) -GitHub User @andig (184815) -GitHub User @andrius4669 (4699695) -GitHub User @as (8127015) -GitHub User @bakape (7851952) -GitHub User @bgadrian (830001) -GitHub User @bontequero (2674999) -GitHub User @cch123 (384546) -GitHub User @chainhelen (7046329) -GitHub User @chanxuehong (3416908) -GitHub User @Cluas (10056928) -GitHub User @cncal (23520240) -GitHub User @DQNEO (188741) -GitHub User @Dreamacro (8615343) -GitHub User @dupoxy (1143957) -GitHub User @EndlessCheng (7086966) -GitHub User @erifan (31343225) -GitHub User @esell (9735165) -GitHub User @fatedier (7346661) -GitHub User @frennkie (6499251) -GitHub User @geedchin (11672310) -GitHub User @GrigoriyMikhalkin (3637857) -GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com> -GitHub User @hitzhangjie (3725760) -GitHub User @hqpko (13887251) -GitHub User @itchyny (375258) -GitHub User @jinmiaoluo (39730824) -GitHub User @jopbrown (6345470) -GitHub User @kazyshr (30496953) -GitHub User @kc1212 (1093806) -GitHub User @komisan19 (18901496) -GitHub User @Kropekk (13366453) -GitHub User @lhl2617 (33488131) -GitHub User @linguohua (3434367) -GitHub User @LotusFenn (13775899) -GitHub User @ly303550688 (11519839) -GitHub User @madiganz (18340029) -GitHub User @maltalex (10195391) -GitHub User @markruler (38225900) -GitHub User @Matts966 (28551465) -GitHub User @micnncim (21333876) -GitHub User @mkishere (224617) <224617+mkishere@users.noreply.github.com> -GitHub User @nu50218 (40682920) -GitHub User @OlgaVlPetrova (44112727) -GitHub User @pityonline (438222) -GitHub User @po3rin (29445112) -GitHub User @pokutuna (57545) -GitHub User @povsister (11040951) -GitHub User @pytimer (17105586) -GitHub User @qcrao (7698088) -GitHub User @ramenjuniti (32011829) -GitHub User @saitarunreddy (21041941) -GitHub User @SataQiu (9354727) -GitHub User @shogo-ma (9860598) -GitHub User @sivchari (55221074) -GitHub User @skanehira (7888591) -GitHub User @soolaugust (10558124) -GitHub User @surechen (7249331) -GitHub User @tatsumack (4510569) -GitHub User @tell-k (26263) -GitHub User @tennashi (10219626) -GitHub User @uhei (2116845) -GitHub User @uji (49834542) -GitHub User @unbyte (5772358) -GitHub User @uropek (39370426) -GitHub User @utkarsh-extc (53217283) -GitHub User @witchard (4994659) -GitHub User @wolf1996 (5901874) -GitHub User @yah01 (12216890) -GitHub User @yuanhh (1298735) -GitHub User @zikaeroh (48577114) -GitHub User @ZZMarquis (7624583) -Giulio Iotti -Giulio Micheloni -Giuseppe Valente -Gleb Stepanov -Glenn Brown -Glenn Lewis -Gordon Klaus -Gordon Tyler -Grace Han -Graham King -Graham Miller -Grant Griffiths -Green Lightning -Greg Poirier -Greg Steuck -Greg Thelen -Greg Ward -Grégoire Delattre -Gregory Man -Gregory Petrosyan -Guilherme Caruso -Guilherme Garnier -Guilherme Goncalves -Guilherme Rezende -Guilherme Souza <32180229+gqgs@users.noreply.github.com> -Guillaume J. Charmes -Guillaume Sottas -Günther Noack -Guobiao Mei -Guodong Li -Guoliang Wang -Gustav Paul -Gustav Westling -Gustavo Franco -Gustavo Niemeyer -Gwenael Treguier -Gyu-Ho Lee -H. İbrahim Güngör -Hajime Hoshi -Hallgrimur Gunnarsson -HAMANO Tsukasa -Han-Wen Nienhuys -Hang Qian -Hanjun Kim -Hanlin He -Hanlin Shi -Haoran Luo -Haosdent Huang -Harald Nordgren -Hari haran -Hariharan Srinath -Harley Laue -Harry Moreno -Harshavardhana -Hasan Ozgan -Hasit Bhatt -Hauke Löffler -Håvard Haugen -He Liu -Hector Chu -Hector Martin Cantero -Hein Khant Zaw -Henning Schmiedehausen -Henrik Edwards -Henrik Hodne -Henrique Vicente -Henry Adi Sumarto -Henry Bubert -Henry Chang -Henry Clifford -Henry Wong -Herbert Georg Fischer -Herbie Ong -Heschi Kreinick -Hidetatsu Yaginuma -Hilko Bengen -Himanshu Kishna Srivastava <28himanshu@gmail.com> -Hiroaki Nakamura -Hiromichi Ema -Hironao OTSUBO -Hiroshi Ioka -Hitoshi Mitake -Holden Huang -Hong Ruiqi -Hongfei Tan -Horacio Duran -Horst Rutter -Hossein Sheikh Attar -Hossein Zolfi -Howard Zhang -Hsin Tsao -Hsin-Ho Yeh -Hu Keping -Huan Du -Hugues Bruant -Huy Le -Hyang-Ah Hana Kim -Hyoyoung Chang -Ian Cottrell -Ian Davis -Ian Gudger -Ian Haken -Ian Kent -Ian Lance Taylor -Ian Leue -Ian Mckay -Ian Tay -Ian Woolf -Ian Zapolsky -Ibrahim AshShohail -Icarus Sparry -Iccha Sethi -Ichinose Shogo -Idora Shinatose -Ignacio Hagopian -Igor Bernstein -Igor Bolotnikov -Igor Dolzhikov -Igor Vashyst -Igor Zhilianin -Ikko Ashimine -Illya Yalovyy -Ilya Chukov <56119080+Elias506@users.noreply.github.com> -Ilya Sinelnikov -Ilya Tocar -INADA Naoki -Inanc Gumus -Ingo Gottwald -Ingo Krabbe -Ingo Oeser -Ioannis Georgoulas -Irbe Krumina -Irfan Sharif -Irieda Noboru -Isaac Ardis -Isaac Wagner -Isfan Azhabil -Iskander Sharipov -Issac Trotts -Ivan Babrou -Ivan Bertona -Ivan Krasin -Ivan Kutuzov -Ivan Markin -Ivan Moscoso -Ivan Osadchiy -Ivan Sharavuev -Ivan Trubach -Ivan Ukhov -Ivy Evans -Jaana Burcu Dogan -Jaap Aarts -Jack Britton -Jack Lindamood -Jacob Baskin -Jacob Blain Christen -Jacob H. Haven -Jacob Hoffman-Andrews -Jacob Walker -Jaden Teng -Jae Kwon -Jake B -Jakob Borg -Jakob Weisblat -Jakub Čajka -Jakub Kaczmarzyk -Jakub Ryszard Czarnowicz -Jakub Warczarek -Jamal Carvalho -James Aguilar -James Bardin -James Chacon -James Clarke -James Cowgill -James Craig Burley -James David Chalfant -James Eady -James Fennell -James Fysh -James Gray -James Hartig -James Kasten -James Lawrence -James Meneghello -James Myers -James Naftel -James Neve -James Nugent -James P. Cooper -James Robinson -James Schofield -James Smith -James Sweet -James Toy -James Treanor -James Tucker -James Whitehead -Jamie Beverly -Jamie Gennis -Jamie Kerr -Jamie Liu -Jamie Stackhouse -Jamie Turner -Jamie Wilkinson -Jamil Djadala -Jan Berktold -Jan H. Hosang -Jan Kratochvil -Jan Lehnardt -Jan Mercl <0xjnml@gmail.com> -Jan Newmarch -Jan Pilzer -Jan Steinke -Jan Ziak <0xe2.0x9a.0x9b@gmail.com> -Jani Monoses -Jannis Andrija Schnitzer -Jared Allard -Jared Culp -Jaroslavas Počepko -Jason A. Donenfeld -Jason Baker -Jason Barnett -Jason Buberel -Jason Chu -Jason Del Ponte -Jason Hall -Jason Keene -Jason LeBrun -Jason Smale -Jason Travis -Jason Wangsadinata -Javier Kohen -Javier Revillas -Javier Segura -Jay Chen -Jay Conrod -Jay Lee -Jay Taylor -Jay Weisskopf -Jean de Klerk -Jean-André Santoni -Jean-François Bustarret -Jean-Francois Cantin -Jean-Marc Eurin -Jean-Nicolas Moal -Jed Denlea -Jędrzej Szczepaniak -Jeet Parekh -Jeevanandam M -Jeff (Zhefu) Jiang -Jeff Craig -Jeff Dupont -Jeff Hodges -Jeff Johnson -Jeff R. Allen -Jeff Sickel -Jeff Wendling -Jeff Widman -Jeffrey H -Jelte Fennema -Jens Frederich -Jeremiah Harmsen -Jeremy Banks <_@jeremy.ca> -Jeremy Canady -Jeremy Faller -Jeremy Jackins -Jeremy Jay -Jeremy Schlatter -Jero Bado -Jeroen Bobbeldijk -Jeroen Simonetti -Jérôme Doucet -Jerrin Shaji George -Jess Frazelle -Jesse Szwedko -Jesús Espino -Jia Zhan -Jiacai Liu -Jiahao Lu -Jianing Yu -Jianqiao Li -Jiayu Yi -Jie Ma -Jihyun Yu -Jim Cote -Jim Kingdon -Jim McGrath -Jim Minter -Jimmy Frasche -Jimmy Zelinskie -Jin-wook Jeong -Jingcheng Zhang -Jingguo Yao -Jingnan Si -Jinkun Zhang -Jiong Du -Jirka Daněk -Jiulong Wang -Joakim Sernbrant -Joe Bowbeer -Joe Cortopassi -Joe Farrell -Joe Harrison -Joe Henke -Joe Kyo -Joe Poirier -Joe Richey -Joe Shaw -Joe Sylve -Joe Tsai -Joel Courtney -Joel Ferrier -Joel Sing -Joël Stemmer -Joel Stemmer -Joey Geiger -Johan Brandhorst -Johan Euphrosine -Johan Jansson -Johan Knutzen -Johan Sageryd -Johannes Huning -John Asmuth -John Bampton -John Beisley -John C Barstow -John DeNero -John Dethridge -John Gibb -John Gilik -John Graham-Cumming -John Howard Palevich -John Jago -John Jeffery -John Jenkins -John Leidegren -John McCabe -John Moore -John Newlin -John Papandriopoulos -John Potocny -John R. Lenton -John Schnake -John Shahid -John Tuley -John Weldon -Johnny Luo -Jon Chen -Jon Johnson -Jonas Bernoulli -Jonathan Albrecht -Jonathan Allie -Jonathan Amsterdam -Jonathan Boulle -Jonathan Chen -Jonathan Feinberg -Jonathan Gold -Jonathan Hseu -Jonathan Mark -Jonathan Nieder -Jonathan Pentecost -Jonathan Pittman -Jonathan Rudenberg -Jonathan Stacks -Jonathan Swinney -Jonathan Wills -Jonathon Lacher -Jongmin Kim -Joonas Kuorilehto -Joop Kiefte -Jordan Christiansen -Jordan Krage -Jordan Lewis -Jordan Liggitt -Jordan Rhee -Jordan Rupprecht -Jordi Martin -Jorge Araya -Jorge L. Fatta -Jos Visser -Josa Gesell -Jose Luis Vázquez González -Joseph Bonneau -Joseph Holsten -Joseph Morag -Josh Baum -Josh Bleecher Snyder -Josh Chorlton -Josh Deprez -Josh Goebel -Josh Hoak -Josh Holland -Josh Rickmar -Josh Roppo -Josh Varga -Joshua Bezaleel Abednego -Joshua Boelter -Joshua Chase -Joshua Crowgey -Joshua Harshman -Joshua M. Clulow -Joshua Rubin -Josselin Costanzi -Jostein Stuhaug -JP Sugarbroad -JT Olds -JT Olio -Juan Carlos -Juan Pablo Civile -Jude Pereira -Jukka-Pekka Kekkonen -Julia Hansbrough -Julian Kornberger -Julian Pastarmov -Julian Phillips -Julian Tibble -Julie Qiu -Julien Kauffmann -Julien Salleyron -Julien Schmidt -Julien Tant -Julio Montes -Jun Zhang -Junchen Li -Junda Liu -Jungho Ahn -Junya Hayashi -Juraj Sukop -Jure Ham -Justin Gracenin -Justin Li -Justin Nuß -Justyn Temme -Kai Backman -Kai Dong -Kai Lüke -Kai Trukenmüller -Kale Blankenship -Kaleb Elwert -Kalman Bekesi -Kamal Aboul-Hosn -Kamil Chmielewski -Kamil Kisiel -Kamil Rytarowski -Kang Hu -Kanta Ebihara -Karan Dhiman -Karel Pazdera -Karoly Negyesi -Karsten Köhler -Karthik Nayak -Kashav Madan -Kate Manson -Katharine Berry -Katie Hockman -Kato Kazuyoshi -Katrina Owen -Kaviraj Kanagaraj -Kay Zhu -Kazuhiro Sera -KB Sriram -Keegan Carruthers-Smith -Kei Son -Keiichi Hirobe -Keiji Yoshida -Keisuke Kishimoto -Keith Ball -Keith Randall -Keith Rarick -Kelly Heller -Kelsey Hightower -Kelvin Foo Chuan Lyi -Kemal Elmizan -Ken Friedenbach -Ken Rockot -Ken Sedgwick -Ken Thompson -Kenichi Tsunokawa -Kenji Kaneda -Kenji Yano -Kenneth Shaw -Kenny Grant -Kensei Nakada -Kenta Mori -Kerollos Magdy -Ketan Parmar -Kevan Swanberg -Kevin Albertson -Kevin Ballard -Kevin Burke -Kévin Dunglas -Kevin Gillette -Kevin Herro -Kevin Kirsche -Kevin Klues -Kevin Malachowski -Kevin Parsons -Kevin Ruffin -Kevin Vu -Kevin Zita -Keyan Pishdadian -Keyuan Li -Kezhu Wang -Khosrow Moossavi -Kieran Colford -Kim Shrier -Kim Yongbin -Kir Kolyshkin -Kirill Korotaev -Kirill Motkov -Kirill Smelkov -Kirill Tatchihin -Kirk Han -Kirklin McDonald -KJ Tsanaktsidis -Klaus Post -Kodie Goodwin -Koichi Shiraishi -Koki Ide -Koki Tomoshige -Komu Wairagu -Konstantin -Konstantin Shaposhnikov -Koya IWAMURA -Kris Kwiatkowski -Kris Nova -Kris Rousey -Krishna Birla -Kristopher Watts -Krzysztof Dąbrowski -Kshitij Saraogi -Kun Li -Kunpei Sakai -Kuntal Majumder -Kush Patel -Kyle Consalus -Kyle Isom -Kyle Jones -Kyle Lemons -Kyle Nusbaum -Kyle Shannon -Kyle Spiers -Kyle Wood -Kyohei Kadota -Kyrylo Silin -L Campbell -Lai Jiangshan -Lajos Papp -Lakshay Garg -Lann Martin -Lanre Adelowo -Lapo Luchini -Larry Clapp -Larry Hosken -Lars Jeppesen -Lars Lehtonen -Lars Wiegman -Larz Conwell -Laurent Voisin -Laurie Clark-Michalek -LE Manh Cuong -Lee Hinman -Lee Packham -Lehner Florian -Leigh McCulloch -Leo Antunes -Leo Rudberg -Leon Klingele -Leonard Wang -Leonardo Comelli -Leonel Quinteros -Lev Shamardin -Lewin Bormann -Lewis Waddicor -Liam Haworth -Lily Chung -Lingchao Xin -Lion Yang -Liz Rice -Lize Cai -Lloyd Dewolf -Lluís Batlle i Rossell -Lorenz Bauer -Lorenz Brun -Lorenz Nickel -Lorenzo Masini -Lorenzo Stoakes -Louis Kruger -Luan Santos -Lubomir I. Ivanov -Luca Bruno -Luca Greco -Luca Spiller -Lucas Bremgartner -Lucas Clemente -Lucien Stuker -Lucio De Re -Ludi Rehak -Luigi Riefolo -Luit van Drongelen -Luka Zakrajšek -Luka Zitnik -Lukasz Milewski -Luke Champine -Luke Curley -Luke Granger-Brown -Luke Shumaker -Luke Young -Luna Duclos -Luuk van Dijk -Lyle Franklin -Lynn Boger -Ma Peiqi -Maarten Bezemer -Maciej Dębski -Madhu Rajanna -Magnus Hiie -Mahdi Hosseini Moghaddam -Maia Lee -Maicon Costa -Mak Kolybabi -Maksym Trykur -Mal Curtis -Manfred Touron -Manigandan Dharmalingam -Manish Goregaokar -Manlio Perillo -Manoj Dayaram -Mansour Rahimi -Manu Garg -Manu S Ajith -Manuel Mendez -Marat Khabibullin -Marc Sanmiquel -Marc Weistroff -Marc-Antoine Ruel -Marcel Edmund Franke -Marcel van Lohuizen -Marcelo Cantos -Marcelo E. Magallon -Marco Gazerro -Marco Hennings -Marcus Weiner -Marcus Willock -Marga Manterola -Mariano Cano -Marin Bašić -Mario Arranz -Marius A. Eriksen -Marius Nuennerich -Mark Adams -Mark Bucciarelli -Mark Dain -Mark Glines -Mark Harrison -Mark Percival -Mark Pulford -Mark Rushakoff -Mark Ryan -Mark Severson -Mark Theunissen -Mark Villacampa -Mark Wolfe -Mark Zavislak -Marko Juhani Silokunnas -Marko Kevac -Marko Kungla -Marko Mikulicic -Marko Mudrinic -Marko Tiikkaja -Markus Duft -Markus Sonderegger -Markus Zimmermann -Marten Seemann -Martin Asquino -Martin Bertschler -Martin Garton -Martin Habbecke -Martin Hamrle -Martin Hoefling -Martin Kreichgauer -Martin Kunc -Martin Lindhe -Martin Möhrmann -Martin Neubauer -Martin Olsen -Martin Olsson -Martin Probst -Martin Sucha -Martin Tournoij -Martins Sipenko -Martynas Budriūnas -Marvin Stenger -Marwan Sulaiman -Maryan Hratson -Masahiro Furudate -Masahiro Wakame -Masaki Yoshida -Masaya Watanabe -Mat Byczkowski -Mat Ryer -Máté Gulyás -Matej Baćo -Mateus Amin -Mateusz Czapliński -Matheus Alcantara -Mathias Beke -Mathias Hall-Andersen -Mathias Leppich -Mathieu Lonjaret -Mats Lidell -Matt Aimonetti -Matt Blair -Matt Bostock -Matt Brown -Matt Dee -Matt Drollette -Matt Harden -Matt Jibson -Matt Joiner -Matt Jones -Matt Juran -Matt Layher -Matt Masurka -Matt Pearring -Matt Reiferson -Matt Robenolt -Matt Strong -Matt T. Proud -Matt Williams -Matthew Brennan -Matthew Broberg -Matthew Cottingham -Matthew Dempsky -Matthew Denton -Matthew Holt -Matthew Horsnell -Matthew Waters -Matthias Frei -Matthieu Hauglustaine -Matthieu Olivier -Matthijs Kooijman -Mattias Appelgren -Mauricio Alvarado -Max Drosdo.www -Max Riveiro -Max Schmitt -Max Semenik -Max Ushakov -Maxim Eryomenko -Maxim Khitrov -Maxim Pimenov -Maxim Pugachev -Maxim Ushakov -Maxime de Roucy -Máximo Cuadros Ortiz -Maxwell Krohn -Maya Rashish -Mayank Kumar -Mehrad Sadeghi <2012.linkinpark@gmail.com> -Meir Fischer -Meng Zhuo -Mhd Sulhan -Mia Zhu -Micah Stetson -Michael Anthony Knyszek -Michael Brandenburg -Michael Chaten -Michael Cook -Michael Darakananda -Michael Dorner -Michael Edwards -Michael Elkins -Michael Ellis -Michael Fraenkel -Michael Fromberger -Michael Gehring -Michael Henderson -Michael Hendricks -Michael Hoisie -Michael Hudson-Doyle -Michael Kasch -Michael Käufl -Michael Kelly -Michaël Lévesque-Dion -Michael Lewis -Michael MacInnis -Michael Marineau -Michael Matloob -Michael McConville -Michael McGreevy -Michael McLoughlin -Michael Munday -Michael Pearson -Michael Piatek -Michael Pratt -Michael Schaller -Michael Schurter -Michael Shields -Michael Stapelberg -Michael Steinert -Michael T. Jones -Michael Teichgräber -Michael Traver -Michael Vetter -Michael Vogt -Michail Kargakis -Michal Bohuslávek -Michal Cierniak -Michał Derkacz -Michal Franc -Michał Łowicki -Michal Pristas -Michal Rostecki -Michal Stokluska -Michalis Kargakis -Michel Lespinasse -Michel Levieux -Michele Di Pede -Mickael Kerjean -Mickey Reiss -Miek Gieben -Miguel Acero -Miguel Mendez -Miguel Molina -Mihai Borobocea -Mihai Moldovan -Mihai Todor -Mihail Minaev -Mikael Tillenius -Mike Andrews -Mike Appleby -Mike Danese -Mike Houston -Mike Kabischev -Mike Rosset -Mike Samuel -Mike Solomon -Mike Strosaker -Mike Tsao -Mike Wiacek -Mikhail Fesenko -Mikhail Gusarov -Mikhail Panchenko -Miki Tebeka -Mikio Hara -Mikkel Krautz -Mikołaj Baranowski -Milan Knezevic -Milan Patel -Milutin Jovanović -MinJae Kwon -Miquel Sabaté Solà -Mirko Hansen -Miroslav Genov -Misty De Meo -Mohamed Attahri -Mohit Agarwal -Mohit kumar Bajoria -Mohit Verma -Momchil Velikov -Monis Khan -Monty Taylor -Moritz Fain -Moriyoshi Koizumi -Morten Siebuhr -Môshe van der Sterre -Mostyn Bramley-Moore -Mrunal Patel -Muhammad Falak R Wani -Muhammad Hamza Farrukh -Muhammed Uluyol -Muir Manders -Mukesh Sharma -Mura Li -Mykhailo Lesyk -Nahum Shalman -Naman Aggarwal -Naman Gera -Nan Deng -Nao Yonashiro -Naoki Kanatani -Natanael Copa -Nate Wilkinson -Nathan Cantelmo -Nathan Caza -Nathan Dias -Nathan Fiscaletti -Nathan Humphreys -Nathan John Youngman -Nathan Otterness -Nathan P Finch -Nathan VanBenschoten -Nathan Youngman -Nathan(yinian) Hu -Nathaniel Cook -Naveen Kumar Sangi -Neeilan Selvalingam -Neelesh Chandola -Nehal J Wani -Neil Lyons -Neuman Vong -Neven Sajko -Nevins Bartolomeo -Niall Sheridan -Nic Day -Nicholas Asimov -Nicholas Katsaros -Nicholas Maniscalco -Nicholas Ng -Nicholas Presta -Nicholas Sullivan -Nicholas Waples -Nick Anthony -Nick Cooper -Nick Craig-Wood -Nick Harper -Nick Kubala -Nick Leli -Nick Miyake -Nick Patavalis -Nick Petroni -Nick Robinson -Nick Smolin -Nicolas BRULEZ -Nicolas Kaiser -Nicolas Owens -Nicolas S. Dade -Niek Sanders -Niels Widger -Nigel Kerr -Nigel Tao -Nik Nyby -Nikhil Benesch -Nikita Gillmann -Nikita Kryuchkov -Nikita Melekhin -Nikita Vanyasin -Niklas Schnelle -Niko Dziemba -Nikolay Turpitko -Nikson Kanti Paul -Nils Larsgård -Nir Soffer -Niranjan Godbole -Nishanth Shanmugham -Noah Campbell -Noah Goldman -Noah Santschi-Cooney -Noble Johnson -Nodir Turakulov -Noel Georgi -Norberto Lopes -Norman B. Lancaster -Nuno Cruces -Obei Sideg -Obeyda Djeffal -Odin Ugedal -Oleg Bulatov -Oleg Vakheta -Oleku Konko -Oling Cat -Oliver Hookins -Oliver Powell -Oliver Stenbom -Oliver Tan -Oliver Tonnhofer -Olivier Antoine -Olivier Duperray -Olivier Poitrey -Olivier Saingre -Olivier Wulveryck -Omar Jarjur -Onkar Jadhav -Ori Bernstein -Ori Rawlings -Oryan Moshe -Osamu TONOMORI -Özgür Kesim -Pablo Caderno -Pablo Lalloni -Pablo Rozas Larraondo -Pablo Santiago Blum de Aguiar -Padraig Kitterick -Pallat Anchaleechamaikorn -Pan Chenglong <1004907659@qq.com> -Panos Georgiadis -Pantelis Sampaziotis -Paolo Giarrusso -Paolo Martini -Parker Moore -Parminder Singh -Pascal Dierich -Pascal S. de Kloe -Paschalis Tsilias -Pasi Tähkäpää -Pat Moroney -Patrick Barker -Patrick Crosby -Patrick Gavlin -Patrick Gundlach -Patrick Higgins -Patrick Jones -Patrick Lee -Patrick Mézard -Patrick Mylund Nielsen -Patrick Pelletier -Patrick Riley -Patrick Smith -Patrik Lundin -Paul A Querna -Paul Borman -Paul Boyd -Paul Chang -Paul D. Weber -Paul Davis <43160081+Pawls@users.noreply.github.com> -Paul E. Murphy -Paul Forgey -Paul Hammond -Paul Hankin -Paul Jolly -Paul Lalonde -Paul M Furley -Paul Marks -Paul Meyer -Paul Nasrat -Paul PISCUC -Paul Querna -Paul Rosania -Paul Ruest -Paul Sbarra -Paul Smith -Paul Tyng -Paul van Brouwershaven -Paul Wankadia -Paulo Casaretto -Paulo Flabiano Smorigo -Paulo Gomes -Pavel Paulau -Pavel Watson -Pavel Zinovkin -Pavlo Sumkin -Pawel Knap -Pawel Szczur -Paweł Szulik -Pei Xian Chee -Pei-Ming Wu -Pen Tree -Peng Gao -Percy Wegmann -Perry Abbott -Petar Dambovaliev -Petar Maymounkov -Peter Armitage -Peter Bourgon -Peter Collingbourne -Peter Conerly -Peter Dotchev -Peter Froehlich -Peter Gonda -Peter Hoyes -Peter Kleiweg -Peter McKenzie -Peter Moody -Peter Morjan -Peter Mundy -Peter Nguyen -Péter Surányi -Péter Szabó -Péter Szilágyi -Peter Teichman -Peter Tseng -Peter Waldschmidt -Peter Waller -Peter Weinberger -Peter Williams -Peter Wu -Peter Zhang -Petr Jediný -Petrica Voicu -Phil Pearl -Phil Pennock -Philip Børgesen -Philip Brown -Philip Hofer -Philip K. Warren -Philip Nelson -Philipp Sauter -Philipp Stephani -Phillip Campbell <15082+phillc@users.noreply.github.com> -Pierre Carru -Pierre Durand -Pierre Prinetti -Pierre Roullon -Piers -Pieter Droogendijk -Pietro Gagliardi -Piyush Mishra -Plekhanov Maxim -Poh Zi How -Polina Osadcha -Pontus Leitzler -Povilas Versockas -Prajwal Koirala <16564273+Prajwal-Koirala@users.noreply.github.com> -Prasanga Siripala -Prasanna Swaminathan -Prashant Agrawal -Prashant Varanasi -Praveen Kumar -Pravendra Singh -Preetam Jinka -Pure White -Qais Patankar -Qiuxuan Zhu -Quan Tran -Quan Yong Zhai -Quentin Perez -Quentin Renard -Quentin Smith -Quey-Liang Kao -Quim Muntal -Quinn Slack -Quinten Yearsley -Quoc-Viet Nguyen -Rabin Gaire -Radek Simko -Radek Sohlich -Radu Berinde -Rafal Jeczalik -Raghavendra Nagaraj -Rahul Bajaj -Rahul Chaudhry -Rahul Wadhwani -Raif S. Naffah -Rajat Goel -Rajath Agasthya -Rajender Reddy Kompally -Ralph Corderoy -Ramazan AYYILDIZ -Ramesh Dharan -Randy Reddig -Raph Levien -Raphael Geronimi -Raul Silvera -Ravil Bikbulatov -RaviTeja Pothana -Ray Tung -Ray Wu -Raymond Kazlauskas -Rebecca Stambler -Reilly Watson -Reinaldo de Souza Jr -Remi Gillig -Rémy Oudompheng -Ren Ogaki -Rens Rikkerink -Rhys Hiltner -Ricardo Padilha -Ricardo Pchevuzinske Katz -Ricardo Seriani -Richard Barnes -Richard Crowley -Richard Dingwall -Richard Eric Gavaletz -Richard Gibson -Richard Miller -Richard Musiol -Richard Pickering -Richard Ulmer -Richard Wilkes -Rick Arnold -Rick Hudson -Rick Sayre -Rijnard van Tonder -Riku Voipio -Risto Jaakko Saarelma -Rob Earhart -Rob Findley -Rob Norman -Rob Phoenix -Rob Pike -Robert Ayrapetyan -Robert Daniel Kortschak -Robert Dinu -Robert Figueiredo -Robert Griesemer -Robert Hencke -Robert Iannucci -Robert Kuska -Robert Obryk -Robert Sesek -Robert Snedegar -Robert Stepanek -Robert van Gent -Robert-André Mauchin -Roberto Clapis -Roberto Selbach -Robin Eklind -Robin Zhong -Rodolfo Carvalho -Rodolfo Rodriguez -Rodrigo Moraes de Oliveira -Rodrigo Rafael Monti Kochenburger -Roger Pau Monné -Roger Peppe -Rohan Challa -Rohan Verma -Rohith Ravi -Roland Illig -Roland Shoemaker -Romain Baugue -Roman Budnikov -Roman Kollár -Roman Shchekin -Ron Hashimoto -Ron Minnich -Ronnie Ebrin -Ross Chater -Ross Kinsey -Ross Light -Ross Smith II -Rowan Marshall -Rowan Worth -Rudi Kramer -Rui Ueyama -Ruixin Bao -Ruslan Andreev -Ruslan Nigmatullin -Russ Cox -Russell Haering -Ryan Bagwell -Ryan Barrett -Ryan Boehning -Ryan Brown -Ryan Canty -Ryan Dahl -Ryan Hitchman -Ryan Kohler -Ryan Lower -Ryan Roden-Corrent -Ryan Seys -Ryan Slade -Ryan Zhang -Ryoichi KATO -Ryoya Sekino -Ryuji Iwata -Ryuma Yoshida -Ryuzo Yamamoto -S.Çağlar Onur -Sabin Mihai Rapan -Sad Pencil -Sai Cheemalapati -Sai Kiran Dasika -Sakeven Jiang -Salaheddin M. Mahmud -Salmān Aljammāz -Sam Arnold -Sam Boyer -Sam Chen -Sam Cross -Sam Ding -Sam Hug -Sam Thorogood -Sam Whited -Sam Xie -Sameer Ajmani -Sami Commerot -Sami Pönkänen -Samuel Kelemen -Samuel Tan -Samuele Pedroni -Sander van Harmelen -Sanjay Menakuru -Santhosh Kumar Tekuri -Santiago De la Cruz <51337247+xhit@users.noreply.github.com> -Sarah Adams -Sardorbek Pulatov -Sascha Brawer -Sasha Lionheart -Sasha Sobol -Satoru Kitaguchi -Scott Barron -Scott Bell -Scott Cotton -Scott Crunkleton -Scott Ferguson -Scott Lawrence -Scott Mansfield -Scott Ragan -Scott Schwartz -Scott Van Woudenberg -Sean Burford -Sean Chen -Sean Chittenden -Sean Christopherson -Sean Dolphin -Sean Harger -Sean Harrington -Sean Hildebrand -Sean Liao -Sean Rees -Sebastiaan van Stijn -Sebastian Chlopecki -Sebastian Kinne -Sebastian Schmidt -Sebastien Binet -Sébastien Paolacci -Sebastien Williams-Wynn -Segev Finer -Seiji Takahashi -Sergei Lemeshkin -Sergei Skorobogatov -Sergei Zagurskii -Sergey 'SnakE' Gromov -Sergey Arseev -Sergey Dobrodey -Sergey Frolov -Sergey Glushchenko -Sergey Ivanov -Sergey Kacheev -Sergey Lukjanov -Sergey Mishin -Sergey Mudrik -Sergey Semin -Sergey Yanykin -Sergio Luis O. B. Correia -Sergiusz Bazanski -Serhat Giydiren -Serhii Aheienko -Seth Hoenig -Seth Vargo -Shaba Abhiram -Shahar Kohanim -Shailesh Suryawanshi -Shamil Garatuev -Shane Hansen -Shang Jian Ding -Shaozhen Ding -Shaquille Que -Shaquille Wyan Que -Shaun Dunning -Shawn Elliott -Shawn Ledbetter -Shawn Smith -Shawn Walker-Salas -Shenghou Ma -Shengjing Zhu -Shengyu Zhang -Shi Han Ng -ShihCheng Tu -Shijie Hao -Shin Fan -Shinji Tanaka -Shinnosuke Sawada <6warashi9@gmail.com> -Shintaro Kaneko -Shivakumar GN -Shivani Singhal -Shivansh Rai -Shivashis Padhi -Shoshin Nikita -Shota Sugiura -Shubham Sharma -Shuhei Takahashi -Shun Fan -Silvan Jegen -Simão Gomes Viana -Simarpreet Singh -Simon Drake -Simon Ferquel -Simon Frei -Simon Jefford -Simon Rawet -Simon Rozman -Simon Ser -Simon Thulbourn -Simon Whitehead -Sina Siadat -Sjoerd Siebinga -Sokolov Yura -Song Gao -Song Lim -Songjiayang -Songlin Jiang -Soojin Nam -Søren L. Hansen -Sparrow Li -Spencer Kocot -Spencer Nelson -Spencer Tung -Spenser Black -Spring Mc -Srdjan Petrovic -Sridhar Venkatakrishnan -Srinidhi Kaushik -StalkR -Stan Hu -Stan Schwertly -Stanislav Afanasev -Steeve Morin -Stefan Baebler -Stefan Nilsson -Stepan Shabalin -Stephan Klatt -Stephan Renatus -Stephan Zuercher -Stéphane Travostino -Stephen Lewis -Stephen Lu -Stephen Ma -Stephen McQuay -Stephen Searles -Stephen Weinberg -Steve Francia -Steve Gilbert -Steve LoFurno -Steve McCoy -Steve Mynott -Steve Newman -Steve Phillips -Steve Streeting -Steve Traut -Steven Buss -Steven Elliot Harris -Steven Erenst -Steven Hartland -Steven Littiebrant -Steven Maude -Steven Wilkin -Stuart Jansen -Subham Sarkar -Sue Spence -Sugu Sougoumarane -Suharsh Sivakumar -Sukrit Handa -Sunny -Suriyaa Sundararuban -Suyash -Suzy Mueller -Sven Almgren -Sven Blumenstein -Sven Lee -Sven Taute -Sylvain Zimmer -Syohei YOSHIDA -Szabolcs Nagy -Taavi Kivisik -Tad Fisher -Tad Glines -Tadas Valiukas -Tadeo Kondrak -Taesu Pyo -Tai Le -Taj Khattra -Takashi Matsuo -Takashi Mima -Takayoshi Nishida -Takeshi YAMANASHI <9.nashi@gmail.com> -Takuto Ikuta -Takuya Ueda -Tal Shprecher -Tamás Gulácsi -Tamir Duberstein -Tao Qingyun -Tao Shen -Tao Wang -Tarmigan Casebolt -Taro Aoki -Taru Karttunen -Tatsuhiro Tsujikawa -Tatsuya Kaneko -Taufiq Rahman -Teague Cole -Ted Kornish -Tejasvi Nareddy -Terin Stock -Terrel Shumway -Tetsuo Kiso -Than McIntosh -Thanabodee Charoenpiriyakij -Thanatat Tamtan -The Hatsune Daishi -Thiago Avelino -Thiago Fransosi Farina -Thom Wiggers -Thomas Alan Copeland -Thomas Bonfort -Thomas Bouldin -Thomas Bruyelle -Thomas Bushnell, BSG -Thomas de Zeeuw -Thomas Desrosiers -Thomas Habets -Thomas Kappler -Thomas Meson -Thomas Symborski -Thomas Wanielista -Thorben Krueger -Thordur Bjornsson -Tiago Queiroz -Tianji Wu -Tianon Gravi -Tilman Dilo -Tim Cooijmans -Tim Cooper -Tim Ebringer -Tim Heckman -Tim Henderson -Tim Hockin -Tim King -Tim Möhlmann -Tim Swast -Tim Wright -Tim Xu -Timmy Douglas -Timo Savola -Timo Truyts -Timothy Gu -Timothy Studd -Tipp Moseley -Tiwei Bie -Tobias Assarsson -Tobias Columbus -Tobias Klauser -Tobias Kohlbau -Toby Burress -Todd Kulesza -Todd Neal -Todd Wang -Tom Anthony -Tom Bergan -Tom Freudenberg -Tom Heng -Tom Lanyon -Tom Levy -Tom Limoncelli -Tom Linford -Tom Panton -Tom Parkin -Tom Payne -Tom Szymanski -Tom Thorogood -Tom Wilkie -Tom Zierbock -Tomas Dabasinskas -Tommy Schaefer -Tomohiro Kusumoto -Tomoya Ishizaki -Tonis Tiigi -Tony Reix -Tony Walker -Tooru Takahashi -Tor Andersson -Torben Schinke -Tormod Erevik Lea -Toshihiro Shiino -Toshiki Shima -Totoro W -Travis Bischel -Travis Cline -Trevor Dixon -Trevor Strohman -Trey Lawrence -Trey Roessig -Trey Tacon -Tristan Amini -Tristan Colgate -Tristan Ooohry -Tristan Rice -Troels Thomsen -Trong Bui -Trung Nguyen -Tsuji Daishiro -Tudor Golubenco -Tugdual Saunier -Tuo Shan -Tyler Bui-Palsulich -Tyler Bunnell -Tyler Treat -Tyson Andre -Tzach Shabtay -Tzu-Chiao Yeh -Tzu-Jung Lee -Udalov Max -Uddeshya Singh -Ugorji Nwoke -Ulf Holm Nielsen -Ulrich Kunitz -Umang Parmar -Uriel Mangado -Urvil Patel -Utkarsh Dixit <53217283+utkarsh-extc@users.noreply.github.com> -Uttam C Pawar -Vadim Grek -Vadim Vygonets -Val Polouchkine -Valentin Vidic -Vaughn Iverson -Vee Zhang -Vega Garcia Luis Alfonso -Venil Noronha -Veselkov Konstantin -Viacheslav Poturaev -Victor Chudnovsky -Victor Michel -Victor Vrantchan -Vignesh Ramachandra -Vikas Kedia -Ville Skyttä -Vincent Ambo -Vincent Batts -Vincent Vanackere -Vinu Rajashekhar -Vish Subramanian -Vishal Dalwadi -Vishvananda Ishaya -Visweswara R -Vitaly Zdanevich -Vitor De Mario -Vivek Sekhar -Vivek V -Vivian Liang -Vlad Krasnov -Vladimir Evgrafov -Vladimir Kovpak -Vladimir Kuzmin -Vladimir Mihailenco -Vladimir Nikishenko -Vladimir Stefanovic -Vladimir Varankin -Vojtech Bocek -Volker Dobler -Volodymyr Paprotski -W. Trevor King -Wade Simmons -Wagner Riffel -Walt Della -Walter Poupore -Wander Lairson Costa -Wang Xuerui -Warren Fernandes -Wayne Ashley Berry -Wedson Almeida Filho -Weerasak Chongnguluam -Wèi Cōngruì -Wei Fu -Wei Guangjing -Wei Xiao -Wei Xikai -Weichao Tang -Weixie Cui <523516579@qq.com> -Wembley G. Leach, Jr -Wenlei (Frank) He -Wenzel Lowe -Wil Selwood -Wilfried Teiken -Will Beason -Will Chan -Will Faught -Will Morrow -Will Norris -Will Storey -Willem van der Schyff -William Chan -William Chang -William Josephson -William Langford -William Orr -William Poussier -Wisdom Omuya -Wu Yunzhou -Xi Ruoyao -Xia Bin -Xiangdong Ji -Xiaodong Liu -Xing Gao <18340825824@163.com> -Xing Xing -Xingqang Bai -Xu Fei -Xudong Zhang -Xudong Zheng <7pkvm5aw@slicealias.com> -Xuyang Kang -Yamagishi Kazutoshi -Yan Zou -Yang Hau -Yang Tian -Yann Hodique -Yann Kerhervé -Yann Salaün -Yannic Bonenberger -Yao Zhang -Yaron de Leeuw -Yaroslav Vorobiov -Yasha Bubnov -Yasser Abdolmaleki -Yasuharu Goto -Yasuhiro Matsumoto -Yasutaka Shinzaki -Yasuyuki Oka -Yazen Shunnar -Yestin Sun -Yesudeep Mangalapilly -Yissakhar Z. Beck -Yo-An Lin -Yohei Takeda -Yongjian Xu -Yorman Arias -Yoshiyuki Kanno -Yoshiyuki Mineo -Yosuke Akatsuka -Youfu Zhang -Yu Heng Zhang -Yu Xuan Zhang -Yu, Li-Yu -Yuichi Kishimoto -Yuichi Nishiwaki -Yuji Yaginuma -Yuki Ito -Yuki OKUSHI -Yuki Yugui Sonoda -Yukihiro Nishinaka <6elpinal@gmail.com> -YunQiang Su -Yury Smolsky -Yusuke Kagiwada -Yuusei Kuwana -Yuval Pavel Zholkover -Yves Junqueira -Zac Bergquist -Zach Bintliff -Zach Gershman -Zach Hoffman -Zach Jones -Zachary Amsden -Zachary Gershman -Zak -Zakatell Kanda -Zellyn Hunter -Zev Goldstein -Zhang Boyang -Zheng Dayu -Zheng Xu -Zhengyu He -Zhongpeng Lin -Zhongtao Chen -Zhongwei Yao -Zhou Peng -Ziad Hatahet -Ziheng Liu -Zorion Arrizabalaga -Zvonimir Pavlinovic -Zyad A. Ali -Максадбек Ахмедов -Максим Федосеев -Роман Хавроненко -Тарас Буник -Фахриддин Балтаев -张嵩 -申习之 diff --git a/README.md b/README.md index 837734b6e574bf..e40f3aa0a16bb9 100644 --- a/README.md +++ b/README.md @@ -16,26 +16,26 @@ BSD-style license found in the LICENSE file. #### Binary Distributions -Official binary distributions are available at https://golang.org/dl/. +Official binary distributions are available at https://go.dev/dl/. -After downloading a binary release, visit https://golang.org/doc/install +After downloading a binary release, visit https://go.dev/doc/install for installation instructions. #### Install From Source If a binary distribution is not available for your combination of operating system and architecture, visit -https://golang.org/doc/install/source +https://go.dev/doc/install/source for source installation instructions. ### Contributing Go is the work of thousands of contributors. We appreciate your help! -To contribute, please read the contribution guidelines at https://golang.org/doc/contribute.html. +To contribute, please read the contribution guidelines at https://go.dev/doc/contribute. Note that the Go project uses the issue tracker for bug reports and -proposals only. See https://golang.org/wiki/Questions for a list of +proposals only. See https://go.dev/wiki/Questions for a list of places to ask questions about the Go language. [rf]: https://reneefrench.blogspot.com/ diff --git a/SECURITY.md b/SECURITY.md index 9e92e8b1eafed3..ab608f3af55cc3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,12 +2,12 @@ ## Supported Versions -We support the past two Go releases (for example, Go 1.12.x and Go 1.13.x). +We support the past two Go releases (for example, Go 1.17.x and Go 1.18.x when Go 1.18.x is the latest stable release). -See https://golang.org/wiki/Go-Release-Cycle and in particular the -[Release Maintenance](https://github.com/golang/go/wiki/Go-Release-Cycle#release-maintenance) +See https://go.dev/wiki/Go-Release-Cycle and in particular the +[Release Maintenance](https://go.dev/wiki/Go-Release-Cycle#release-maintenance) part of that page. ## Reporting a Vulnerability -See https://golang.org/security for how to report a vulnerability. +See https://go.dev/security for how to report a vulnerability. diff --git a/api/README b/api/README index ce24efcd312696..1e52f7a843bbbd 100644 --- a/api/README +++ b/api/README @@ -8,6 +8,16 @@ shipped. Each file adds new lines but does not remove any. except.txt lists features that may disappear without breaking true compatibility. -next.txt is the only file intended to be mutated. It's a list of -features that may be added to the next version. It only affects -warning output from the go api tool. +Starting with go1.19.txt, each API feature line must end in "#nnnnn" +giving the GitHub issue number of the proposal issue that accepted +the new API. This helps with our end-of-cycle audit of new APIs. +The same requirement applies to next/* (described below), which will +become a go1.XX.txt for XX >= 19. + +The next/ directory contains the only files intended to be mutated. +Each file in that directory contains a list of features that may be added +to the next release of Go. The files in this directory only affect the +warning output from the go api tool. Each file should be named +nnnnn.txt, after the issue number for the accepted proposal. +(The #nnnnn suffix must also appear at the end of each line in the file; +that will be preserved when next/*.txt is concatenated into go1.XX.txt.) diff --git a/api/except.txt b/api/except.txt index 14fe7785fa54d3..eaaf86a2ec500e 100644 --- a/api/except.txt +++ b/api/except.txt @@ -1,3 +1,4 @@ +pkg debug/elf, const R_PPC64_SECTOFF_LO_DS = 61 pkg encoding/json, method (*RawMessage) MarshalJSON() ([]uint8, error) pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 17516230804060213386546619791123951641/12500000000000000000000000000000000000000000000000000000000000000000000000000000000 @@ -492,6 +493,7 @@ pkg syscall (windows-amd64), type CertRevocationInfo struct, OidSpecificInfo uin pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8 pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M +pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalExample) *M pkg testing, func RegisterCover(Cover) pkg text/scanner, const GoTokens = 1012 pkg text/template/parse, type DotNode bool @@ -504,3 +506,5 @@ pkg unicode, const Version = "6.3.0" pkg unicode, const Version = "7.0.0" pkg unicode, const Version = "8.0.0" pkg unicode, const Version = "9.0.0" +pkg html/template, method (*Template) Funcs(FuncMap) *Template +pkg html/template, type FuncMap map[string]interface{} diff --git a/api/go1.1.txt b/api/go1.1.txt index 81d095704a4c9c..06291faa4b2505 100644 --- a/api/go1.1.txt +++ b/api/go1.1.txt @@ -371,7 +371,7 @@ pkg debug/elf, const ELFCLASSNONE = 0 pkg debug/elf, const ELFDATA2LSB = 1 pkg debug/elf, const ELFDATA2MSB = 2 pkg debug/elf, const ELFDATANONE = 0 -pkg debug/elf, const ELFMAG = "\u007fELF" +pkg debug/elf, const ELFMAG = "\x7fELF" pkg debug/elf, const ELFOSABI_86OPEN = 5 pkg debug/elf, const ELFOSABI_AIX = 7 pkg debug/elf, const ELFOSABI_ARM = 97 @@ -2603,7 +2603,34 @@ pkg runtime/debug, type GCStats struct, Pause []time.Duration pkg runtime/debug, type GCStats struct, PauseQuantiles []time.Duration pkg runtime/debug, type GCStats struct, PauseTotal time.Duration pkg sort, func Reverse(Interface) Interface -pkg strconv, const IntSize = 64 +pkg strconv (darwin-amd64), const IntSize = 64 +pkg strconv (darwin-amd64-cgo), const IntSize = 64 +pkg strconv (freebsd-386), const IntSize = 32 +pkg strconv (freebsd-386-cgo), const IntSize = 32 +pkg strconv (freebsd-amd64), const IntSize = 64 +pkg strconv (freebsd-amd64-cgo), const IntSize = 64 +pkg strconv (freebsd-arm), const IntSize = 32 +pkg strconv (freebsd-arm-cgo), const IntSize = 32 +pkg strconv (linux-386), const IntSize = 32 +pkg strconv (linux-386-cgo), const IntSize = 32 +pkg strconv (linux-amd64), const IntSize = 64 +pkg strconv (linux-amd64-cgo), const IntSize = 64 +pkg strconv (linux-arm), const IntSize = 32 +pkg strconv (linux-arm-cgo), const IntSize = 32 +pkg strconv (netbsd-386), const IntSize = 32 +pkg strconv (netbsd-386-cgo), const IntSize = 32 +pkg strconv (netbsd-amd64), const IntSize = 64 +pkg strconv (netbsd-amd64-cgo), const IntSize = 64 +pkg strconv (netbsd-arm), const IntSize = 32 +pkg strconv (netbsd-arm-cgo), const IntSize = 32 +pkg strconv (netbsd-arm64), const IntSize = 64 +pkg strconv (netbsd-arm64-cgo), const IntSize = 64 +pkg strconv (openbsd-386), const IntSize = 32 +pkg strconv (openbsd-386-cgo), const IntSize = 32 +pkg strconv (openbsd-amd64), const IntSize = 64 +pkg strconv (openbsd-amd64-cgo), const IntSize = 64 +pkg strconv (windows-386), const IntSize = 32 +pkg strconv (windows-amd64), const IntSize = 64 pkg strings, func TrimPrefix(string, string) string pkg strings, func TrimSuffix(string, string) string pkg strings, method (*Reader) WriteTo(io.Writer) (int64, error) @@ -49366,7 +49393,7 @@ pkg syscall (windows-386), const IP_MULTICAST_TTL = 10 pkg syscall (windows-386), const IP_TOS = 3 pkg syscall (windows-386), const IP_TTL = 4 pkg syscall (windows-386), const ImplementsGetwd = true -pkg syscall (windows-386), const InvalidHandle = 18446744073709551615 +pkg syscall (windows-386), const InvalidHandle = 4294967295 pkg syscall (windows-386), const KEY_ALL_ACCESS = 983103 pkg syscall (windows-386), const KEY_CREATE_LINK = 32 pkg syscall (windows-386), const KEY_CREATE_SUB_KEY = 4 diff --git a/api/go1.17.txt b/api/go1.17.txt index 48505381f1e41f..ca2cd162f8bc8e 100644 --- a/api/go1.17.txt +++ b/api/go1.17.txt @@ -63,12 +63,93 @@ pkg image/draw, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64 pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color) pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64) pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry +pkg math (darwin-amd64), const MaxInt = 9223372036854775807 +pkg math (darwin-amd64), const MaxUint = 18446744073709551615 +pkg math (darwin-amd64), const MinInt = -9223372036854775808 +pkg math (darwin-amd64-cgo), const MaxInt = 9223372036854775807 +pkg math (darwin-amd64-cgo), const MaxUint = 18446744073709551615 +pkg math (darwin-amd64-cgo), const MinInt = -9223372036854775808 +pkg math (freebsd-386), const MaxInt = 2147483647 +pkg math (freebsd-386), const MaxUint = 4294967295 +pkg math (freebsd-386), const MinInt = -2147483648 +pkg math (freebsd-386-cgo), const MaxInt = 2147483647 +pkg math (freebsd-386-cgo), const MaxUint = 4294967295 +pkg math (freebsd-386-cgo), const MinInt = -2147483648 +pkg math (freebsd-amd64), const MaxInt = 9223372036854775807 +pkg math (freebsd-amd64), const MaxUint = 18446744073709551615 +pkg math (freebsd-amd64), const MinInt = -9223372036854775808 +pkg math (freebsd-amd64-cgo), const MaxInt = 9223372036854775807 +pkg math (freebsd-amd64-cgo), const MaxUint = 18446744073709551615 +pkg math (freebsd-amd64-cgo), const MinInt = -9223372036854775808 +pkg math (freebsd-arm), const MaxInt = 2147483647 +pkg math (freebsd-arm), const MaxUint = 4294967295 +pkg math (freebsd-arm), const MinInt = -2147483648 +pkg math (freebsd-arm-cgo), const MaxInt = 2147483647 +pkg math (freebsd-arm-cgo), const MaxUint = 4294967295 +pkg math (freebsd-arm-cgo), const MinInt = -2147483648 +pkg math (linux-386), const MaxInt = 2147483647 +pkg math (linux-386), const MaxUint = 4294967295 +pkg math (linux-386), const MinInt = -2147483648 +pkg math (linux-386-cgo), const MaxInt = 2147483647 +pkg math (linux-386-cgo), const MaxUint = 4294967295 +pkg math (linux-386-cgo), const MinInt = -2147483648 +pkg math (linux-amd64), const MaxInt = 9223372036854775807 +pkg math (linux-amd64), const MaxUint = 18446744073709551615 +pkg math (linux-amd64), const MinInt = -9223372036854775808 +pkg math (linux-amd64-cgo), const MaxInt = 9223372036854775807 +pkg math (linux-amd64-cgo), const MaxUint = 18446744073709551615 +pkg math (linux-amd64-cgo), const MinInt = -9223372036854775808 +pkg math (linux-arm), const MaxInt = 2147483647 +pkg math (linux-arm), const MaxUint = 4294967295 +pkg math (linux-arm), const MinInt = -2147483648 +pkg math (linux-arm-cgo), const MaxInt = 2147483647 +pkg math (linux-arm-cgo), const MaxUint = 4294967295 +pkg math (linux-arm-cgo), const MinInt = -2147483648 +pkg math (netbsd-386), const MaxInt = 2147483647 +pkg math (netbsd-386), const MaxUint = 4294967295 +pkg math (netbsd-386), const MinInt = -2147483648 +pkg math (netbsd-386-cgo), const MaxInt = 2147483647 +pkg math (netbsd-386-cgo), const MaxUint = 4294967295 +pkg math (netbsd-386-cgo), const MinInt = -2147483648 +pkg math (netbsd-amd64), const MaxInt = 9223372036854775807 +pkg math (netbsd-amd64), const MaxUint = 18446744073709551615 +pkg math (netbsd-amd64), const MinInt = -9223372036854775808 +pkg math (netbsd-amd64-cgo), const MaxInt = 9223372036854775807 +pkg math (netbsd-amd64-cgo), const MaxUint = 18446744073709551615 +pkg math (netbsd-amd64-cgo), const MinInt = -9223372036854775808 +pkg math (netbsd-arm), const MaxInt = 2147483647 +pkg math (netbsd-arm), const MaxUint = 4294967295 +pkg math (netbsd-arm), const MinInt = -2147483648 +pkg math (netbsd-arm-cgo), const MaxInt = 2147483647 +pkg math (netbsd-arm-cgo), const MaxUint = 4294967295 +pkg math (netbsd-arm-cgo), const MinInt = -2147483648 +pkg math (netbsd-arm64), const MaxInt = 9223372036854775807 +pkg math (netbsd-arm64), const MaxUint = 18446744073709551615 +pkg math (netbsd-arm64), const MinInt = -9223372036854775808 +pkg math (netbsd-arm64-cgo), const MaxInt = 9223372036854775807 +pkg math (netbsd-arm64-cgo), const MaxUint = 18446744073709551615 +pkg math (netbsd-arm64-cgo), const MinInt = -9223372036854775808 +pkg math (openbsd-386), const MaxInt = 2147483647 +pkg math (openbsd-386), const MaxUint = 4294967295 +pkg math (openbsd-386), const MinInt = -2147483648 +pkg math (openbsd-386-cgo), const MaxInt = 2147483647 +pkg math (openbsd-386-cgo), const MaxUint = 4294967295 +pkg math (openbsd-386-cgo), const MinInt = -2147483648 +pkg math (openbsd-amd64), const MaxInt = 9223372036854775807 +pkg math (openbsd-amd64), const MaxUint = 18446744073709551615 +pkg math (openbsd-amd64), const MinInt = -9223372036854775808 +pkg math (openbsd-amd64-cgo), const MaxInt = 9223372036854775807 +pkg math (openbsd-amd64-cgo), const MaxUint = 18446744073709551615 +pkg math (openbsd-amd64-cgo), const MinInt = -9223372036854775808 +pkg math (windows-386), const MaxInt = 2147483647 +pkg math (windows-386), const MaxUint = 4294967295 +pkg math (windows-386), const MinInt = -2147483648 +pkg math (windows-amd64), const MaxInt = 9223372036854775807 +pkg math (windows-amd64), const MaxUint = 18446744073709551615 +pkg math (windows-amd64), const MinInt = -9223372036854775808 pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368 -pkg math, const MaxInt = 9223372036854775807 pkg math, const MaxInt ideal-int -pkg math, const MaxUint = 18446744073709551615 pkg math, const MaxUint ideal-int -pkg math, const MinInt = -9223372036854775808 pkg math, const MinInt ideal-int pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 1/713623846352979940529142984724747568191373312 pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784 diff --git a/api/go1.18.txt b/api/go1.18.txt new file mode 100644 index 00000000000000..0f3e26df9d3296 --- /dev/null +++ b/api/go1.18.txt @@ -0,0 +1,233 @@ +pkg bufio, method (*Writer) AvailableBuffer() []uint8 +pkg bufio, method (ReadWriter) AvailableBuffer() []uint8 +pkg bytes, func Cut([]uint8, []uint8) ([]uint8, []uint8, bool) +pkg crypto/tls, method (*Conn) NetConn() net.Conn +pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error) +pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error) +pkg debug/buildinfo, type BuildInfo = debug.BuildInfo +pkg debug/elf, const R_PPC64_RELATIVE = 22 +pkg debug/elf, const R_PPC64_RELATIVE R_PPC64 +pkg debug/dwarf, type BasicType struct, DataBitOffset int64 +pkg debug/dwarf, type StructField struct, DataBitOffset int64 +pkg debug/plan9obj, var ErrNoSymbols error +pkg go/ast, method (*IndexListExpr) End() token.Pos +pkg go/ast, method (*IndexListExpr) Pos() token.Pos +pkg go/ast, type FuncType struct, TypeParams *FieldList +pkg go/ast, type IndexListExpr struct +pkg go/ast, type IndexListExpr struct, Indices []Expr +pkg go/ast, type IndexListExpr struct, Lbrack token.Pos +pkg go/ast, type IndexListExpr struct, Rbrack token.Pos +pkg go/ast, type IndexListExpr struct, X Expr +pkg go/ast, type TypeSpec struct, TypeParams *FieldList +pkg go/constant, method (Kind) String() string +pkg go/token, const TILDE = 88 +pkg go/token, const TILDE Token +pkg go/types, func Instantiate(*Context, Type, []Type, bool) (Type, error) +pkg go/types, func NewContext() *Context +pkg go/types, func NewSignatureType(*Var, []*TypeParam, []*TypeParam, *Tuple, *Tuple, bool) *Signature +pkg go/types, func NewTerm(bool, Type) *Term +pkg go/types, func NewTypeParam(*TypeName, Type) *TypeParam +pkg go/types, func NewUnion([]*Term) *Union +pkg go/types, method (*ArgumentError) Error() string +pkg go/types, method (*ArgumentError) Unwrap() error +pkg go/types, method (*Interface) IsComparable() bool +pkg go/types, method (*Interface) IsImplicit() bool +pkg go/types, method (*Interface) IsMethodSet() bool +pkg go/types, method (*Interface) MarkImplicit() +pkg go/types, method (*Named) Origin() *Named +pkg go/types, method (*Named) SetTypeParams([]*TypeParam) +pkg go/types, method (*Named) TypeArgs() *TypeList +pkg go/types, method (*Named) TypeParams() *TypeParamList +pkg go/types, method (*Signature) RecvTypeParams() *TypeParamList +pkg go/types, method (*Signature) TypeParams() *TypeParamList +pkg go/types, method (*Term) String() string +pkg go/types, method (*Term) Tilde() bool +pkg go/types, method (*Term) Type() Type +pkg go/types, method (*TypeList) At(int) Type +pkg go/types, method (*TypeList) Len() int +pkg go/types, method (*TypeParam) Constraint() Type +pkg go/types, method (*TypeParam) Index() int +pkg go/types, method (*TypeParam) Obj() *TypeName +pkg go/types, method (*TypeParam) SetConstraint(Type) +pkg go/types, method (*TypeParam) String() string +pkg go/types, method (*TypeParam) Underlying() Type +pkg go/types, method (*TypeParamList) At(int) *TypeParam +pkg go/types, method (*TypeParamList) Len() int +pkg go/types, method (*Union) Len() int +pkg go/types, method (*Union) String() string +pkg go/types, method (*Union) Term(int) *Term +pkg go/types, method (*Union) Underlying() Type +pkg go/types, type ArgumentError struct +pkg go/types, type ArgumentError struct, Err error +pkg go/types, type ArgumentError struct, Index int +pkg go/types, type Config struct, Context *Context +pkg go/types, type Config struct, GoVersion string +pkg go/types, type Context struct +pkg go/types, type Info struct, Instances map[*ast.Ident]Instance +pkg go/types, type Instance struct +pkg go/types, type Instance struct, Type Type +pkg go/types, type Instance struct, TypeArgs *TypeList +pkg go/types, type Term struct +pkg go/types, type TypeList struct +pkg go/types, type TypeParam struct +pkg go/types, type TypeParamList struct +pkg go/types, type Union struct +pkg net, func TCPAddrFromAddrPort(netip.AddrPort) *TCPAddr +pkg net, func UDPAddrFromAddrPort(netip.AddrPort) *UDPAddr +pkg net, method (*Resolver) LookupNetIP(context.Context, string, string) ([]netip.Addr, error) +pkg net, method (*TCPAddr) AddrPort() netip.AddrPort +pkg net, method (*UDPAddr) AddrPort() netip.AddrPort +pkg net, method (*UDPConn) ReadFromUDPAddrPort([]uint8) (int, netip.AddrPort, error) +pkg net, method (*UDPConn) ReadMsgUDPAddrPort([]uint8, []uint8) (int, int, int, netip.AddrPort, error) +pkg net, method (*UDPConn) WriteMsgUDPAddrPort([]uint8, []uint8, netip.AddrPort) (int, int, error) +pkg net, method (*UDPConn) WriteToUDPAddrPort([]uint8, netip.AddrPort) (int, error) +pkg net/http, func MaxBytesHandler(Handler, int64) Handler +pkg net/http, method (*Cookie) Valid() error +pkg net/netip, func AddrFrom16([16]uint8) Addr +pkg net/netip, func AddrFrom4([4]uint8) Addr +pkg net/netip, func AddrFromSlice([]uint8) (Addr, bool) +pkg net/netip, func AddrPortFrom(Addr, uint16) AddrPort +pkg net/netip, func IPv4Unspecified() Addr +pkg net/netip, func IPv6LinkLocalAllNodes() Addr +pkg net/netip, func IPv6Unspecified() Addr +pkg net/netip, func MustParseAddr(string) Addr +pkg net/netip, func MustParseAddrPort(string) AddrPort +pkg net/netip, func MustParsePrefix(string) Prefix +pkg net/netip, func ParseAddr(string) (Addr, error) +pkg net/netip, func ParseAddrPort(string) (AddrPort, error) +pkg net/netip, func ParsePrefix(string) (Prefix, error) +pkg net/netip, func PrefixFrom(Addr, int) Prefix +pkg net/netip, method (*Addr) UnmarshalBinary([]uint8) error +pkg net/netip, method (*Addr) UnmarshalText([]uint8) error +pkg net/netip, method (*AddrPort) UnmarshalBinary([]uint8) error +pkg net/netip, method (*AddrPort) UnmarshalText([]uint8) error +pkg net/netip, method (*Prefix) UnmarshalBinary([]uint8) error +pkg net/netip, method (*Prefix) UnmarshalText([]uint8) error +pkg net/netip, method (Addr) AppendTo([]uint8) []uint8 +pkg net/netip, method (Addr) As16() [16]uint8 +pkg net/netip, method (Addr) As4() [4]uint8 +pkg net/netip, method (Addr) AsSlice() []uint8 +pkg net/netip, method (Addr) BitLen() int +pkg net/netip, method (Addr) Compare(Addr) int +pkg net/netip, method (Addr) Is4() bool +pkg net/netip, method (Addr) Is4In6() bool +pkg net/netip, method (Addr) Is6() bool +pkg net/netip, method (Addr) IsGlobalUnicast() bool +pkg net/netip, method (Addr) IsInterfaceLocalMulticast() bool +pkg net/netip, method (Addr) IsLinkLocalMulticast() bool +pkg net/netip, method (Addr) IsLinkLocalUnicast() bool +pkg net/netip, method (Addr) IsLoopback() bool +pkg net/netip, method (Addr) IsMulticast() bool +pkg net/netip, method (Addr) IsPrivate() bool +pkg net/netip, method (Addr) IsUnspecified() bool +pkg net/netip, method (Addr) IsValid() bool +pkg net/netip, method (Addr) Less(Addr) bool +pkg net/netip, method (Addr) MarshalBinary() ([]uint8, error) +pkg net/netip, method (Addr) MarshalText() ([]uint8, error) +pkg net/netip, method (Addr) Next() Addr +pkg net/netip, method (Addr) Prefix(int) (Prefix, error) +pkg net/netip, method (Addr) Prev() Addr +pkg net/netip, method (Addr) String() string +pkg net/netip, method (Addr) StringExpanded() string +pkg net/netip, method (Addr) Unmap() Addr +pkg net/netip, method (Addr) WithZone(string) Addr +pkg net/netip, method (Addr) Zone() string +pkg net/netip, method (AddrPort) Addr() Addr +pkg net/netip, method (AddrPort) AppendTo([]uint8) []uint8 +pkg net/netip, method (AddrPort) IsValid() bool +pkg net/netip, method (AddrPort) MarshalBinary() ([]uint8, error) +pkg net/netip, method (AddrPort) MarshalText() ([]uint8, error) +pkg net/netip, method (AddrPort) Port() uint16 +pkg net/netip, method (AddrPort) String() string +pkg net/netip, method (Prefix) Addr() Addr +pkg net/netip, method (Prefix) AppendTo([]uint8) []uint8 +pkg net/netip, method (Prefix) Bits() int +pkg net/netip, method (Prefix) Contains(Addr) bool +pkg net/netip, method (Prefix) IsSingleIP() bool +pkg net/netip, method (Prefix) IsValid() bool +pkg net/netip, method (Prefix) MarshalBinary() ([]uint8, error) +pkg net/netip, method (Prefix) MarshalText() ([]uint8, error) +pkg net/netip, method (Prefix) Masked() Prefix +pkg net/netip, method (Prefix) Overlaps(Prefix) bool +pkg net/netip, method (Prefix) String() string +pkg net/netip, type Addr struct +pkg net/netip, type AddrPort struct +pkg net/netip, type Prefix struct +pkg reflect, const Pointer = 22 +pkg reflect, const Pointer Kind +pkg reflect, func PointerTo(Type) Type +pkg reflect, method (*MapIter) Reset(Value) +pkg reflect, method (Value) CanComplex() bool +pkg reflect, method (Value) CanFloat() bool +pkg reflect, method (Value) CanInt() bool +pkg reflect, method (Value) CanUint() bool +pkg reflect, method (Value) FieldByIndexErr([]int) (Value, error) +pkg reflect, method (Value) SetIterKey(*MapIter) +pkg reflect, method (Value) SetIterValue(*MapIter) +pkg reflect, method (Value) UnsafePointer() unsafe.Pointer +pkg runtime/debug, func ParseBuildInfo(string) (*BuildInfo, error) +pkg runtime/debug, method (*BuildInfo) String() string +pkg runtime/debug, type BuildInfo struct, GoVersion string +pkg runtime/debug, type BuildInfo struct, Settings []BuildSetting +pkg runtime/debug, type BuildSetting struct +pkg runtime/debug, type BuildSetting struct, Key string +pkg runtime/debug, type BuildSetting struct, Value string +pkg strings, func Clone(string) string +pkg strings, func Cut(string, string) (string, string, bool) +pkg sync, method (*Mutex) TryLock() bool +pkg sync, method (*RWMutex) TryLock() bool +pkg sync, method (*RWMutex) TryRLock() bool +pkg syscall (freebsd-386), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (freebsd-386-cgo), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (freebsd-amd64), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (freebsd-amd64-cgo), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (freebsd-arm), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (freebsd-arm-cgo), type SysProcAttr struct, Pdeathsig Signal +pkg syscall (windows-386), func SyscallN(uintptr, ...uintptr) (uintptr, uintptr, Errno) +pkg syscall (windows-amd64), func SyscallN(uintptr, ...uintptr) (uintptr, uintptr, Errno) +pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M +pkg testing, method (*F) Add(...interface{}) +pkg testing, method (*F) Cleanup(func()) +pkg testing, method (*F) Error(...interface{}) +pkg testing, method (*F) Errorf(string, ...interface{}) +pkg testing, method (*F) Fail() +pkg testing, method (*F) FailNow() +pkg testing, method (*F) Failed() bool +pkg testing, method (*F) Fatal(...interface{}) +pkg testing, method (*F) Fatalf(string, ...interface{}) +pkg testing, method (*F) Fuzz(interface{}) +pkg testing, method (*F) Helper() +pkg testing, method (*F) Log(...interface{}) +pkg testing, method (*F) Logf(string, ...interface{}) +pkg testing, method (*F) Name() string +pkg testing, method (*F) Setenv(string, string) +pkg testing, method (*F) Skip(...interface{}) +pkg testing, method (*F) SkipNow() +pkg testing, method (*F) Skipf(string, ...interface{}) +pkg testing, method (*F) Skipped() bool +pkg testing, method (*F) TempDir() string +pkg testing, type F struct +pkg testing, type InternalFuzzTarget struct +pkg testing, type InternalFuzzTarget struct, Fn func(*F) +pkg testing, type InternalFuzzTarget struct, Name string +pkg text/template/parse, const NodeBreak = 21 +pkg text/template/parse, const NodeBreak NodeType +pkg text/template/parse, const NodeContinue = 22 +pkg text/template/parse, const NodeContinue NodeType +pkg text/template/parse, method (*BreakNode) Copy() Node +pkg text/template/parse, method (*BreakNode) String() string +pkg text/template/parse, method (*ContinueNode) Copy() Node +pkg text/template/parse, method (*ContinueNode) String() string +pkg text/template/parse, method (BreakNode) Position() Pos +pkg text/template/parse, method (BreakNode) Type() NodeType +pkg text/template/parse, method (ContinueNode) Position() Pos +pkg text/template/parse, method (ContinueNode) Type() NodeType +pkg text/template/parse, type BreakNode struct +pkg text/template/parse, type BreakNode struct, Line int +pkg text/template/parse, type BreakNode struct, embedded NodeType +pkg text/template/parse, type BreakNode struct, embedded Pos +pkg text/template/parse, type ContinueNode struct +pkg text/template/parse, type ContinueNode struct, Line int +pkg text/template/parse, type ContinueNode struct, embedded NodeType +pkg text/template/parse, type ContinueNode struct, embedded Pos +pkg unicode/utf8, func AppendRune([]uint8, int32) []uint8 diff --git a/api/go1.19.txt b/api/go1.19.txt new file mode 100644 index 00000000000000..100c2af9075526 --- /dev/null +++ b/api/go1.19.txt @@ -0,0 +1,299 @@ +pkg crypto/x509, func ParseRevocationList([]uint8) (*RevocationList, error) #50674 +pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 +pkg crypto/x509, method (*CertPool) Equal(*CertPool) bool #46057 +pkg crypto/x509, method (*RevocationList) CheckSignatureFrom(*Certificate) error #50674 +pkg crypto/x509, type RevocationList struct, AuthorityKeyId []uint8 #50674 +pkg crypto/x509, type RevocationList struct, Extensions []pkix.Extension #50674 +pkg crypto/x509, type RevocationList struct, Issuer pkix.Name #50674 +pkg crypto/x509, type RevocationList struct, Raw []uint8 #50674 +pkg crypto/x509, type RevocationList struct, RawIssuer []uint8 #50674 +pkg crypto/x509, type RevocationList struct, RawTBSRevocationList []uint8 #50674 +pkg crypto/x509, type RevocationList struct, Signature []uint8 #50674 +pkg debug/elf, const EM_LOONGARCH = 258 #46229 +pkg debug/elf, const EM_LOONGARCH Machine #46229 +pkg debug/elf, const R_LARCH_32 = 1 #46229 +pkg debug/elf, const R_LARCH_32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_64 = 2 #46229 +pkg debug/elf, const R_LARCH_64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD16 = 48 #46229 +pkg debug/elf, const R_LARCH_ADD16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD24 = 49 #46229 +pkg debug/elf, const R_LARCH_ADD24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD32 = 50 #46229 +pkg debug/elf, const R_LARCH_ADD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD64 = 51 #46229 +pkg debug/elf, const R_LARCH_ADD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD8 = 47 #46229 +pkg debug/elf, const R_LARCH_ADD8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_COPY = 4 #46229 +pkg debug/elf, const R_LARCH_COPY R_LARCH #46229 +pkg debug/elf, const R_LARCH_IRELATIVE = 12 #46229 +pkg debug/elf, const R_LARCH_IRELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT = 5 #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_LA = 20 #46229 +pkg debug/elf, const R_LARCH_MARK_LA R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL = 21 #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_NONE = 0 #46229 +pkg debug/elf, const R_LARCH_NONE R_LARCH #46229 +pkg debug/elf, const R_LARCH_RELATIVE = 3 #46229 +pkg debug/elf, const R_LARCH_RELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ADD = 35 #46229 +pkg debug/elf, const R_LARCH_SOP_ADD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_AND = 36 #46229 +pkg debug/elf, const R_LARCH_SOP_AND R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT = 30 #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE = 37 #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_NOT = 31 #46229 +pkg debug/elf, const R_LARCH_SOP_NOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 = 45 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 = 44 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 = 40 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 = 41 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 = 42 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 = 38 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 = 43 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U = 46 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 = 39 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE = 23 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP = 24 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL = 25 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL = 22 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL = 29 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD = 28 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT = 27 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL = 26 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SL = 33 #46229 +pkg debug/elf, const R_LARCH_SOP_SL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SR = 34 #46229 +pkg debug/elf, const R_LARCH_SOP_SR R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SUB = 32 #46229 +pkg debug/elf, const R_LARCH_SOP_SUB R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB16 = 53 #46229 +pkg debug/elf, const R_LARCH_SUB16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB24 = 54 #46229 +pkg debug/elf, const R_LARCH_SUB24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB32 = 55 #46229 +pkg debug/elf, const R_LARCH_SUB32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB64 = 56 #46229 +pkg debug/elf, const R_LARCH_SUB64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB8 = 52 #46229 +pkg debug/elf, const R_LARCH_SUB8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 = 6 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 = 7 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 = 8 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 = 9 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 = 10 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 = 11 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 R_LARCH #46229 +pkg debug/elf, method (R_LARCH) GoString() string #46229 +pkg debug/elf, method (R_LARCH) String() string #46229 +pkg debug/elf, type R_LARCH int #46229 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY = 2 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY ideal-int #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE ideal-int #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH ideal-int #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST = 6 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST ideal-int #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES = 1 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES ideal-int #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE = 3 #51868 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE ideal-int #51868 +pkg debug/pe, const IMAGE_FILE_MACHINE_LOONGARCH32 = 25138 #46229 +pkg debug/pe, const IMAGE_FILE_MACHINE_LOONGARCH32 ideal-int #46229 +pkg debug/pe, const IMAGE_FILE_MACHINE_LOONGARCH64 = 25188 #46229 +pkg debug/pe, const IMAGE_FILE_MACHINE_LOONGARCH64 ideal-int #46229 +pkg debug/pe, const IMAGE_SCN_CNT_CODE = 32 #51868 +pkg debug/pe, const IMAGE_SCN_CNT_CODE ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA = 64 #51868 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA = 128 #51868 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT = 4096 #51868 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE = 33554432 #51868 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE = 536870912 #51868 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_MEM_READ = 1073741824 #51868 +pkg debug/pe, const IMAGE_SCN_MEM_READ ideal-int #51868 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE = 2147483648 #51868 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE ideal-int #51868 +pkg debug/pe, method (*File) COFFSymbolReadSectionDefAux(int) (*COFFSymbolAuxFormat5, error) #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Checksum uint32 #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumLineNumbers uint16 #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumRelocs uint16 #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, SecNum uint16 #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Selection uint8 #51868 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Size uint32 #51868 +pkg encoding/binary, func AppendUvarint([]uint8, uint64) []uint8 #51644 +pkg encoding/binary, func AppendVarint([]uint8, int64) []uint8 #51644 +pkg encoding/binary, type AppendByteOrder interface { AppendUint16, AppendUint32, AppendUint64, String } #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint16([]uint8, uint16) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint32([]uint8, uint32) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, AppendUint64([]uint8, uint64) []uint8 #50601 +pkg encoding/binary, type AppendByteOrder interface, String() string #50601 +pkg encoding/csv, method (*Reader) InputOffset() int64 #43401 +pkg encoding/xml, method (*Decoder) InputPos() (int, int) #45628 +pkg flag, func TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 +pkg flag, method (*FlagSet) TextVar(encoding.TextUnmarshaler, string, encoding.TextMarshaler, string) #45754 +pkg fmt, func Append([]uint8, ...interface{}) []uint8 #47579 +pkg fmt, func Appendf([]uint8, string, ...interface{}) []uint8 #47579 +pkg fmt, func Appendln([]uint8, ...interface{}) []uint8 #47579 +pkg go/doc, method (*Package) HTML(string) []uint8 #51082 +pkg go/doc, method (*Package) Markdown(string) []uint8 #51082 +pkg go/doc, method (*Package) Parser() *comment.Parser #51082 +pkg go/doc, method (*Package) Printer() *comment.Printer #51082 +pkg go/doc, method (*Package) Synopsis(string) string #51082 +pkg go/doc, method (*Package) Text(string) []uint8 #51082 +pkg go/doc/comment, func DefaultLookupPackage(string) (string, bool) #51082 +pkg go/doc/comment, method (*DocLink) DefaultURL(string) string #51082 +pkg go/doc/comment, method (*Heading) DefaultID() string #51082 +pkg go/doc/comment, method (*List) BlankBefore() bool #51082 +pkg go/doc/comment, method (*List) BlankBetween() bool #51082 +pkg go/doc/comment, method (*Parser) Parse(string) *Doc #51082 +pkg go/doc/comment, method (*Printer) Comment(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) HTML(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) Markdown(*Doc) []uint8 #51082 +pkg go/doc/comment, method (*Printer) Text(*Doc) []uint8 #51082 +pkg go/doc/comment, type Block interface, unexported methods #51082 +pkg go/doc/comment, type Code struct #51082 +pkg go/doc/comment, type Code struct, Text string #51082 +pkg go/doc/comment, type Doc struct #51082 +pkg go/doc/comment, type Doc struct, Content []Block #51082 +pkg go/doc/comment, type Doc struct, Links []*LinkDef #51082 +pkg go/doc/comment, type DocLink struct #51082 +pkg go/doc/comment, type DocLink struct, ImportPath string #51082 +pkg go/doc/comment, type DocLink struct, Name string #51082 +pkg go/doc/comment, type DocLink struct, Recv string #51082 +pkg go/doc/comment, type DocLink struct, Text []Text #51082 +pkg go/doc/comment, type Heading struct #51082 +pkg go/doc/comment, type Heading struct, Text []Text #51082 +pkg go/doc/comment, type Italic string #51082 +pkg go/doc/comment, type Link struct #51082 +pkg go/doc/comment, type Link struct, Auto bool #51082 +pkg go/doc/comment, type Link struct, Text []Text #51082 +pkg go/doc/comment, type Link struct, URL string #51082 +pkg go/doc/comment, type LinkDef struct #51082 +pkg go/doc/comment, type LinkDef struct, Text string #51082 +pkg go/doc/comment, type LinkDef struct, URL string #51082 +pkg go/doc/comment, type LinkDef struct, Used bool #51082 +pkg go/doc/comment, type List struct #51082 +pkg go/doc/comment, type List struct, ForceBlankBefore bool #51082 +pkg go/doc/comment, type List struct, ForceBlankBetween bool #51082 +pkg go/doc/comment, type List struct, Items []*ListItem #51082 +pkg go/doc/comment, type ListItem struct #51082 +pkg go/doc/comment, type ListItem struct, Content []Block #51082 +pkg go/doc/comment, type ListItem struct, Number string #51082 +pkg go/doc/comment, type Paragraph struct #51082 +pkg go/doc/comment, type Paragraph struct, Text []Text #51082 +pkg go/doc/comment, type Parser struct #51082 +pkg go/doc/comment, type Parser struct, LookupPackage func(string) (string, bool) #51082 +pkg go/doc/comment, type Parser struct, LookupSym func(string, string) bool #51082 +pkg go/doc/comment, type Parser struct, Words map[string]string #51082 +pkg go/doc/comment, type Plain string #51082 +pkg go/doc/comment, type Printer struct #51082 +pkg go/doc/comment, type Printer struct, DocLinkBaseURL string #51082 +pkg go/doc/comment, type Printer struct, DocLinkURL func(*DocLink) string #51082 +pkg go/doc/comment, type Printer struct, HeadingID func(*Heading) string #51082 +pkg go/doc/comment, type Printer struct, HeadingLevel int #51082 +pkg go/doc/comment, type Printer struct, TextCodePrefix string #51082 +pkg go/doc/comment, type Printer struct, TextPrefix string #51082 +pkg go/doc/comment, type Printer struct, TextWidth int #51082 +pkg go/doc/comment, type Text interface, unexported methods #51082 +pkg go/types, method (*Func) Origin() *Func #51682 +pkg go/types, method (*Var) Origin() *Var #51682 +pkg hash/maphash, func Bytes(Seed, []uint8) uint64 #42710 +pkg hash/maphash, func String(Seed, string) uint64 #42710 +pkg html/template, method (*Template) Funcs(template.FuncMap) *Template #46121 +pkg html/template, type FuncMap = template.FuncMap #46121 +pkg net/http, method (*MaxBytesError) Error() string #30715 +pkg net/http, type MaxBytesError struct #30715 +pkg net/http, type MaxBytesError struct, Limit int64 #30715 +pkg net/url, func JoinPath(string, ...string) (string, error) #47005 +pkg net/url, method (*URL) JoinPath(...string) *URL #47005 +pkg net/url, type URL struct, OmitHost bool #46059 +pkg os/exec, method (*Cmd) Environ() []string #50599 +pkg os/exec, type Cmd struct, Err error #43724 +pkg os/exec, var ErrDot error #43724 +pkg pgo/inline, func A() #43724 +pkg pgo/inline, func D(uint) int #43724 +pkg pgo/inline, func N(uint) *BS #43724 +pkg pgo/inline, func T(uint64) uint #43724 +pkg pgo/inline, type BS struct #43724 +pkg pgo/inline, method (*BS) NS(uint) (uint, bool) #43724 +pkg pgo/inline, method (*BS) S(uint) *BS #43724 +pkg regexp/syntax, const ErrNestingDepth = "expression nests too deeply" #51684 +pkg regexp/syntax, const ErrNestingDepth ErrorCode #51684 +pkg runtime/debug, func SetMemoryLimit(int64) int64 #48409 +pkg sort, func Find(int, func(int) int) (int, bool) #50340 +pkg sync/atomic, method (*Bool) CompareAndSwap(bool, bool) bool #50860 +pkg sync/atomic, method (*Bool) Load() bool #50860 +pkg sync/atomic, method (*Bool) Store(bool) #50860 +pkg sync/atomic, method (*Bool) Swap(bool) bool #50860 +pkg sync/atomic, method (*Int32) Add(int32) int32 #50860 +pkg sync/atomic, method (*Int32) CompareAndSwap(int32, int32) bool #50860 +pkg sync/atomic, method (*Int32) Load() int32 #50860 +pkg sync/atomic, method (*Int32) Store(int32) #50860 +pkg sync/atomic, method (*Int32) Swap(int32) int32 #50860 +pkg sync/atomic, method (*Int64) Add(int64) int64 #50860 +pkg sync/atomic, method (*Int64) CompareAndSwap(int64, int64) bool #50860 +pkg sync/atomic, method (*Int64) Load() int64 #50860 +pkg sync/atomic, method (*Int64) Store(int64) #50860 +pkg sync/atomic, method (*Int64) Swap(int64) int64 #50860 +pkg sync/atomic, method (*Pointer[$0]) CompareAndSwap(*$0, *$0) bool #50860 +pkg sync/atomic, method (*Pointer[$0]) Load() *$0 #50860 +pkg sync/atomic, method (*Pointer[$0]) Store(*$0) #50860 +pkg sync/atomic, method (*Pointer[$0]) Swap(*$0) *$0 #50860 +pkg sync/atomic, method (*Uint32) Add(uint32) uint32 #50860 +pkg sync/atomic, method (*Uint32) CompareAndSwap(uint32, uint32) bool #50860 +pkg sync/atomic, method (*Uint32) Load() uint32 #50860 +pkg sync/atomic, method (*Uint32) Store(uint32) #50860 +pkg sync/atomic, method (*Uint32) Swap(uint32) uint32 #50860 +pkg sync/atomic, method (*Uint64) Add(uint64) uint64 #50860 +pkg sync/atomic, method (*Uint64) CompareAndSwap(uint64, uint64) bool #50860 +pkg sync/atomic, method (*Uint64) Load() uint64 #50860 +pkg sync/atomic, method (*Uint64) Store(uint64) #50860 +pkg sync/atomic, method (*Uint64) Swap(uint64) uint64 #50860 +pkg sync/atomic, method (*Uintptr) Add(uintptr) uintptr #50860 +pkg sync/atomic, method (*Uintptr) CompareAndSwap(uintptr, uintptr) bool #50860 +pkg sync/atomic, method (*Uintptr) Load() uintptr #50860 +pkg sync/atomic, method (*Uintptr) Store(uintptr) #50860 +pkg sync/atomic, method (*Uintptr) Swap(uintptr) uintptr #50860 +pkg sync/atomic, type Bool struct #50860 +pkg sync/atomic, type Int32 struct #50860 +pkg sync/atomic, type Int64 struct #50860 +pkg sync/atomic, type Pointer[$0 interface{}] struct #50860 +pkg sync/atomic, type Uint32 struct #50860 +pkg sync/atomic, type Uint64 struct #50860 +pkg sync/atomic, type Uintptr struct #50860 +pkg time, method (Duration) Abs() Duration #51414 +pkg time, method (Time) ZoneBounds() (Time, Time) #50062 diff --git a/api/go1.9.txt b/api/go1.9.txt index c23a17ea1a3949..87fae57920d2f6 100644 --- a/api/go1.9.txt +++ b/api/go1.9.txt @@ -49,7 +49,34 @@ pkg image/png, type EncoderBufferPool interface, Put(*EncoderBuffer) pkg math/big, method (*Int) IsInt64() bool pkg math/big, method (*Int) IsUint64() bool pkg math/big, type Word uint -pkg math/bits, const UintSize = 64 +pkg math/bits (darwin-amd64), const UintSize = 64 +pkg math/bits (darwin-amd64-cgo), const UintSize = 64 +pkg math/bits (freebsd-386), const UintSize = 32 +pkg math/bits (freebsd-386-cgo), const UintSize = 32 +pkg math/bits (freebsd-amd64), const UintSize = 64 +pkg math/bits (freebsd-amd64-cgo), const UintSize = 64 +pkg math/bits (freebsd-arm), const UintSize = 32 +pkg math/bits (freebsd-arm-cgo), const UintSize = 32 +pkg math/bits (linux-386), const UintSize = 32 +pkg math/bits (linux-386-cgo), const UintSize = 32 +pkg math/bits (linux-amd64), const UintSize = 64 +pkg math/bits (linux-amd64-cgo), const UintSize = 64 +pkg math/bits (linux-arm), const UintSize = 32 +pkg math/bits (linux-arm-cgo), const UintSize = 32 +pkg math/bits (netbsd-386), const UintSize = 32 +pkg math/bits (netbsd-386-cgo), const UintSize = 32 +pkg math/bits (netbsd-amd64), const UintSize = 64 +pkg math/bits (netbsd-amd64-cgo), const UintSize = 64 +pkg math/bits (netbsd-arm), const UintSize = 32 +pkg math/bits (netbsd-arm-cgo), const UintSize = 32 +pkg math/bits (netbsd-arm64), const UintSize = 64 +pkg math/bits (netbsd-arm64-cgo), const UintSize = 64 +pkg math/bits (openbsd-386), const UintSize = 32 +pkg math/bits (openbsd-386-cgo), const UintSize = 32 +pkg math/bits (openbsd-amd64), const UintSize = 64 +pkg math/bits (openbsd-amd64-cgo), const UintSize = 64 +pkg math/bits (windows-386), const UintSize = 32 +pkg math/bits (windows-amd64), const UintSize = 64 pkg math/bits, const UintSize ideal-int pkg math/bits, func LeadingZeros(uint) int pkg math/bits, func LeadingZeros16(uint16) int diff --git a/api/next/41773.txt b/api/next/41773.txt new file mode 100644 index 00000000000000..116596e73ea9dd --- /dev/null +++ b/api/next/41773.txt @@ -0,0 +1 @@ +pkg net/http, type Server struct, DisableGeneralOptionsHandler bool #41773 diff --git a/api/next/42537.txt b/api/next/42537.txt new file mode 100644 index 00000000000000..4f2446aaf354a1 --- /dev/null +++ b/api/next/42537.txt @@ -0,0 +1,4 @@ +pkg bytes, func CutPrefix([]uint8, []uint8) ([]uint8, bool) #42537 +pkg bytes, func CutSuffix([]uint8, []uint8) ([]uint8, bool) #42537 +pkg strings, func CutPrefix(string, string) (string, bool) #42537 +pkg strings, func CutSuffix(string, string) (string, bool) #42537 \ No newline at end of file diff --git a/api/next/43620.txt b/api/next/43620.txt new file mode 100644 index 00000000000000..9d272fd0c7153b --- /dev/null +++ b/api/next/43620.txt @@ -0,0 +1 @@ +pkg testing, method (*B) Elapsed() time.Duration #43620 \ No newline at end of file diff --git a/api/next/45038.txt b/api/next/45038.txt new file mode 100644 index 00000000000000..64c3f5f2958174 --- /dev/null +++ b/api/next/45038.txt @@ -0,0 +1 @@ +pkg bytes, func Clone([]uint8) []uint8 #45038 diff --git a/api/next/45899.txt b/api/next/45899.txt new file mode 100644 index 00000000000000..a823142b15d49a --- /dev/null +++ b/api/next/45899.txt @@ -0,0 +1,5 @@ +pkg io, type OffsetWriter struct #45899 +pkg io, func NewOffsetWriter(WriterAt, int64) *OffsetWriter #45899 +pkg io, method (*OffsetWriter) Write([]uint8) (int, error) #45899 +pkg io, method (*OffsetWriter) WriteAt([]uint8, int64) (int, error) #45899 +pkg io, method (*OffsetWriter) Seek(int64, int) (int64, error) #45899 \ No newline at end of file diff --git a/api/next/46731.txt b/api/next/46731.txt new file mode 100644 index 00000000000000..1d491ef7ecdb7f --- /dev/null +++ b/api/next/46731.txt @@ -0,0 +1,14 @@ +pkg runtime/cgo (darwin-amd64-cgo), type Incomplete struct #46731 +pkg runtime/cgo (freebsd-386-cgo), type Incomplete struct #46731 +pkg runtime/cgo (freebsd-amd64-cgo), type Incomplete struct #46731 +pkg runtime/cgo (freebsd-arm-cgo), type Incomplete struct #46731 +pkg runtime/cgo (linux-386-cgo), type Incomplete struct #46731 +pkg runtime/cgo (linux-amd64-cgo), type Incomplete struct #46731 +pkg runtime/cgo (linux-arm-cgo), type Incomplete struct #46731 +pkg runtime/cgo (netbsd-386-cgo), type Incomplete struct #46731 +pkg runtime/cgo (netbsd-amd64-cgo), type Incomplete struct #46731 +pkg runtime/cgo (netbsd-arm-cgo), type Incomplete struct #46731 +pkg runtime/cgo (netbsd-arm64-cgo), type Incomplete struct #46731 +pkg runtime/cgo (openbsd-386-cgo), type Incomplete struct #46731 +pkg runtime/cgo (openbsd-amd64-cgo), type Incomplete struct #46731 +pkg runtime/cgo, type Incomplete struct #46731 diff --git a/api/next/46746.txt b/api/next/46746.txt new file mode 100644 index 00000000000000..ae07682b34b8b5 --- /dev/null +++ b/api/next/46746.txt @@ -0,0 +1,2 @@ +pkg reflect, method (Value) Comparable() bool #46746 +pkg reflect, method (Value) Equal(Value) bool #46746 \ No newline at end of file diff --git a/api/next/47209.txt b/api/next/47209.txt new file mode 100644 index 00000000000000..fd4969c21577a6 --- /dev/null +++ b/api/next/47209.txt @@ -0,0 +1,2 @@ +pkg io/fs, var SkipAll error #47209 +pkg path/filepath, var SkipAll error #47209 diff --git a/api/next/50429.txt b/api/next/50429.txt new file mode 100644 index 00000000000000..558937de1cbed3 --- /dev/null +++ b/api/next/50429.txt @@ -0,0 +1 @@ +pkg go/ast, type RangeStmt struct, Range token.Pos #50429 \ No newline at end of file diff --git a/api/next/51246.txt b/api/next/51246.txt new file mode 100644 index 00000000000000..b00f5404662b2f --- /dev/null +++ b/api/next/51246.txt @@ -0,0 +1,72 @@ +pkg syscall (linux-386), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-386), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-386), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-386), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-386), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-386), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-386), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-386), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-386), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-386), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-386), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-386), type SysProcAttr struct, UseCgroupFD bool #51246 +pkg syscall (linux-386-cgo), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-386-cgo), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-386-cgo), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-386-cgo), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-386-cgo), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-386-cgo), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-386-cgo), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-386-cgo), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-386-cgo), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-386-cgo), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-386-cgo), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-386-cgo), type SysProcAttr struct, UseCgroupFD bool #51246 +pkg syscall (linux-amd64), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-amd64), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-amd64), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-amd64), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-amd64), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-amd64), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-amd64), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-amd64), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-amd64), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-amd64), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-amd64), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-amd64), type SysProcAttr struct, UseCgroupFD bool #51246 +pkg syscall (linux-amd64-cgo), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-amd64-cgo), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-amd64-cgo), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-amd64-cgo), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-amd64-cgo), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-amd64-cgo), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-amd64-cgo), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-amd64-cgo), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-amd64-cgo), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-amd64-cgo), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-amd64-cgo), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-amd64-cgo), type SysProcAttr struct, UseCgroupFD bool #51246 +pkg syscall (linux-arm), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-arm), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-arm), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-arm), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-arm), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-arm), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-arm), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-arm), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-arm), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-arm), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-arm), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-arm), type SysProcAttr struct, UseCgroupFD bool #51246 +pkg syscall (linux-arm-cgo), const CLONE_CLEAR_SIGHAND = 4294967296 #51246 +pkg syscall (linux-arm-cgo), const CLONE_CLEAR_SIGHAND ideal-int #51246 +pkg syscall (linux-arm-cgo), const CLONE_INTO_CGROUP = 8589934592 #51246 +pkg syscall (linux-arm-cgo), const CLONE_INTO_CGROUP ideal-int #51246 +pkg syscall (linux-arm-cgo), const CLONE_NEWCGROUP = 33554432 #51246 +pkg syscall (linux-arm-cgo), const CLONE_NEWCGROUP ideal-int #51246 +pkg syscall (linux-arm-cgo), const CLONE_NEWTIME = 128 #51246 +pkg syscall (linux-arm-cgo), const CLONE_NEWTIME ideal-int #51246 +pkg syscall (linux-arm-cgo), const CLONE_PIDFD = 4096 #51246 +pkg syscall (linux-arm-cgo), const CLONE_PIDFD ideal-int #51246 +pkg syscall (linux-arm-cgo), type SysProcAttr struct, CgroupFD int #51246 +pkg syscall (linux-arm-cgo), type SysProcAttr struct, UseCgroupFD bool #51246 diff --git a/api/next/51668.txt b/api/next/51668.txt new file mode 100644 index 00000000000000..c0c2e07e61df38 --- /dev/null +++ b/api/next/51668.txt @@ -0,0 +1 @@ +pkg fmt, func FormatString(State, int32) string #51668 diff --git a/api/next/51896.txt b/api/next/51896.txt new file mode 100644 index 00000000000000..d4ef14cfa2b8d2 --- /dev/null +++ b/api/next/51896.txt @@ -0,0 +1 @@ +pkg unicode/utf16, func AppendRune([]uint16, int32) []uint16 #51896 \ No newline at end of file diff --git a/api/next/52221.txt b/api/next/52221.txt new file mode 100644 index 00000000000000..c288e4660b3686 --- /dev/null +++ b/api/next/52221.txt @@ -0,0 +1,19 @@ +pkg crypto/ecdh, func P256() Curve #52221 +pkg crypto/ecdh, func P384() Curve #52221 +pkg crypto/ecdh, func P521() Curve #52221 +pkg crypto/ecdh, func X25519() Curve #52221 +pkg crypto/ecdh, method (*PrivateKey) Bytes() []uint8 #52221 +pkg crypto/ecdh, method (*PrivateKey) Curve() Curve #52221 +pkg crypto/ecdh, method (*PrivateKey) Equal(crypto.PrivateKey) bool #52221 +pkg crypto/ecdh, method (*PrivateKey) Public() crypto.PublicKey #52221 +pkg crypto/ecdh, method (*PrivateKey) PublicKey() *PublicKey #52221 +pkg crypto/ecdh, method (*PublicKey) Bytes() []uint8 #52221 +pkg crypto/ecdh, method (*PublicKey) Curve() Curve #52221 +pkg crypto/ecdh, method (*PublicKey) Equal(crypto.PublicKey) bool #52221 +pkg crypto/ecdh, type Curve interface, ECDH(*PrivateKey, *PublicKey) ([]uint8, error) #52221 +pkg crypto/ecdh, type Curve interface, GenerateKey(io.Reader) (*PrivateKey, error) #52221 +pkg crypto/ecdh, type Curve interface, NewPrivateKey([]uint8) (*PrivateKey, error) #52221 +pkg crypto/ecdh, type Curve interface, NewPublicKey([]uint8) (*PublicKey, error) #52221 +pkg crypto/ecdh, type Curve interface, unexported methods #52221 +pkg crypto/ecdh, type PrivateKey struct #52221 +pkg crypto/ecdh, type PublicKey struct #52221 diff --git a/api/next/52376.txt b/api/next/52376.txt new file mode 100644 index 00000000000000..9e6b1623eeb4d5 --- /dev/null +++ b/api/next/52376.txt @@ -0,0 +1 @@ +pkg reflect, method (Value) SetZero() #52376 diff --git a/api/next/52746.txt b/api/next/52746.txt new file mode 100644 index 00000000000000..d6c3bd2749b424 --- /dev/null +++ b/api/next/52746.txt @@ -0,0 +1,6 @@ +pkg time, const DateOnly = "2006-01-02" #52746 +pkg time, const DateOnly ideal-string #52746 +pkg time, const DateTime = "2006-01-02 15:04:05" #52746 +pkg time, const DateTime ideal-string #52746 +pkg time, const TimeOnly = "15:04:05" #52746 +pkg time, const TimeOnly ideal-string #52746 diff --git a/api/next/53002.txt b/api/next/53002.txt new file mode 100644 index 00000000000000..b078fee55c65ef --- /dev/null +++ b/api/next/53002.txt @@ -0,0 +1,6 @@ +pkg net/http/httputil, method (*ProxyRequest) SetURL(*url.URL) #53002 +pkg net/http/httputil, method (*ProxyRequest) SetXForwarded() #53002 +pkg net/http/httputil, type ProxyRequest struct #53002 +pkg net/http/httputil, type ProxyRequest struct, In *http.Request #53002 +pkg net/http/httputil, type ProxyRequest struct, Out *http.Request #53002 +pkg net/http/httputil, type ReverseProxy struct, Rewrite func(*ProxyRequest) #53002 diff --git a/api/next/53021.txt b/api/next/53021.txt new file mode 100644 index 00000000000000..3adb9b1198de55 --- /dev/null +++ b/api/next/53021.txt @@ -0,0 +1 @@ +pkg crypto/subtle, func XORBytes([]uint8, []uint8, []uint8) int #53021 diff --git a/api/next/53200.txt b/api/next/53200.txt new file mode 100644 index 00000000000000..f1ecb17a216014 --- /dev/null +++ b/api/next/53200.txt @@ -0,0 +1 @@ +pkg go/token, method (*FileSet) RemoveFile(*File) #53200 diff --git a/api/next/53346.txt b/api/next/53346.txt new file mode 100644 index 00000000000000..dd39f231d5dc26 --- /dev/null +++ b/api/next/53346.txt @@ -0,0 +1 @@ +pkg encoding/xml, method (*Encoder) Close() error #53346 diff --git a/api/next/53356.txt b/api/next/53356.txt new file mode 100644 index 00000000000000..607c5ecde6e685 --- /dev/null +++ b/api/next/53356.txt @@ -0,0 +1 @@ +pkg debug/elf, const R_PPC64_SECTOFF_LO_DS = 62 #53356 diff --git a/api/next/53482.txt b/api/next/53482.txt new file mode 100644 index 00000000000000..a54894498fc0d7 --- /dev/null +++ b/api/next/53482.txt @@ -0,0 +1,2 @@ +pkg net, const FlagRunning = 32 #53482 +pkg net, const FlagRunning Flags #53482 diff --git a/api/next/54222.txt b/api/next/54222.txt new file mode 100644 index 00000000000000..73ae5a40ede68c --- /dev/null +++ b/api/next/54222.txt @@ -0,0 +1,78 @@ +pkg debug/elf, const R_LARCH_32_PCREL = 99 #54222 +pkg debug/elf, const R_LARCH_32_PCREL R_LARCH #54222 +pkg debug/elf, const R_LARCH_ABS64_HI12 = 70 #54222 +pkg debug/elf, const R_LARCH_ABS64_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_ABS64_LO20 = 69 #54222 +pkg debug/elf, const R_LARCH_ABS64_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_ABS_HI20 = 67 #54222 +pkg debug/elf, const R_LARCH_ABS_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_ABS_LO12 = 68 #54222 +pkg debug/elf, const R_LARCH_ABS_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_B16 = 64 #54222 +pkg debug/elf, const R_LARCH_B16 R_LARCH #54222 +pkg debug/elf, const R_LARCH_B21 = 65 #54222 +pkg debug/elf, const R_LARCH_B21 R_LARCH #54222 +pkg debug/elf, const R_LARCH_B26 = 66 #54222 +pkg debug/elf, const R_LARCH_B26 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GNU_VTENTRY = 58 #54222 +pkg debug/elf, const R_LARCH_GNU_VTENTRY R_LARCH #54222 +pkg debug/elf, const R_LARCH_GNU_VTINHERIT = 57 #54222 +pkg debug/elf, const R_LARCH_GNU_VTINHERIT R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT64_HI12 = 82 #54222 +pkg debug/elf, const R_LARCH_GOT64_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT64_LO20 = 81 #54222 +pkg debug/elf, const R_LARCH_GOT64_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT64_PC_HI12 = 78 #54222 +pkg debug/elf, const R_LARCH_GOT64_PC_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT64_PC_LO20 = 77 #54222 +pkg debug/elf, const R_LARCH_GOT64_PC_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT_HI20 = 79 #54222 +pkg debug/elf, const R_LARCH_GOT_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT_LO12 = 80 #54222 +pkg debug/elf, const R_LARCH_GOT_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT_PC_HI20 = 75 #54222 +pkg debug/elf, const R_LARCH_GOT_PC_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_GOT_PC_LO12 = 76 #54222 +pkg debug/elf, const R_LARCH_GOT_PC_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_PCALA64_HI12 = 74 #54222 +pkg debug/elf, const R_LARCH_PCALA64_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_PCALA64_LO20 = 73 #54222 +pkg debug/elf, const R_LARCH_PCALA64_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_PCALA_HI20 = 71 #54222 +pkg debug/elf, const R_LARCH_PCALA_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_PCALA_LO12 = 72 #54222 +pkg debug/elf, const R_LARCH_PCALA_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_RELAX = 100 #54222 +pkg debug/elf, const R_LARCH_RELAX R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_GD_HI20 = 98 #54222 +pkg debug/elf, const R_LARCH_TLS_GD_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_GD_PC_HI20 = 97 #54222 +pkg debug/elf, const R_LARCH_TLS_GD_PC_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_HI12 = 94 #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_LO20 = 93 #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_PC_HI12 = 90 #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_PC_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_PC_LO20 = 89 #54222 +pkg debug/elf, const R_LARCH_TLS_IE64_PC_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE_HI20 = 91 #54222 +pkg debug/elf, const R_LARCH_TLS_IE_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE_LO12 = 92 #54222 +pkg debug/elf, const R_LARCH_TLS_IE_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE_PC_HI20 = 87 #54222 +pkg debug/elf, const R_LARCH_TLS_IE_PC_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_IE_PC_LO12 = 88 #54222 +pkg debug/elf, const R_LARCH_TLS_IE_PC_LO12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LD_HI20 = 96 #54222 +pkg debug/elf, const R_LARCH_TLS_LD_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LD_PC_HI20 = 95 #54222 +pkg debug/elf, const R_LARCH_TLS_LD_PC_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LE64_HI12 = 86 #54222 +pkg debug/elf, const R_LARCH_TLS_LE64_HI12 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LE64_LO20 = 85 #54222 +pkg debug/elf, const R_LARCH_TLS_LE64_LO20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LE_HI20 = 83 #54222 +pkg debug/elf, const R_LARCH_TLS_LE_HI20 R_LARCH #54222 +pkg debug/elf, const R_LARCH_TLS_LE_LO12 = 84 #54222 +pkg debug/elf, const R_LARCH_TLS_LE_LO12 R_LARCH #54222 diff --git a/api/next/54251.txt b/api/next/54251.txt new file mode 100644 index 00000000000000..bfc3baa6e0b0f6 --- /dev/null +++ b/api/next/54251.txt @@ -0,0 +1,6 @@ +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV128 = 20776 #54251 +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV128 ideal-int #54251 +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV32 = 20530 #54251 +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV32 ideal-int #54251 +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV64 = 20580 #54251 +pkg debug/pe, const IMAGE_FILE_MACHINE_RISCV64 ideal-int #54251 diff --git a/api/next/54345.txt b/api/next/54345.txt new file mode 100644 index 00000000000000..50cc2d1711d6a2 --- /dev/null +++ b/api/next/54345.txt @@ -0,0 +1,108 @@ +pkg debug/elf, const R_PPC64_ADDR16_HIGHER34 = 136 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHER34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHERA34 = 137 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHERA34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHEST34 = 138 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHEST34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHESTA34 = 139 #54345 +pkg debug/elf, const R_PPC64_ADDR16_HIGHESTA34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_COPY = 19 #54345 +pkg debug/elf, const R_PPC64_COPY R_PPC64 #54345 +pkg debug/elf, const R_PPC64_D28 = 144 #54345 +pkg debug/elf, const R_PPC64_D28 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_D34 = 128 #54345 +pkg debug/elf, const R_PPC64_D34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_D34_HA30 = 131 #54345 +pkg debug/elf, const R_PPC64_D34_HA30 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_D34_HI30 = 130 #54345 +pkg debug/elf, const R_PPC64_D34_HI30 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_D34_LO = 129 #54345 +pkg debug/elf, const R_PPC64_D34_LO R_PPC64 #54345 +pkg debug/elf, const R_PPC64_DTPREL28 = 147 #54345 +pkg debug/elf, const R_PPC64_DTPREL28 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GLOB_DAT = 20 #54345 +pkg debug/elf, const R_PPC64_GLOB_DAT R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GNU_VTENTRY = 254 #54345 +pkg debug/elf, const R_PPC64_GNU_VTENTRY R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GNU_VTINHERIT = 253 #54345 +pkg debug/elf, const R_PPC64_GNU_VTINHERIT R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GOT_DTPREL_PCREL34 = 151 #54345 +pkg debug/elf, const R_PPC64_GOT_DTPREL_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GOT_PCREL34 = 133 #54345 +pkg debug/elf, const R_PPC64_GOT_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GOT_TLSGD_PCREL34 = 148 #54345 +pkg debug/elf, const R_PPC64_GOT_TLSGD_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GOT_TLSLD_PCREL34 = 149 #54345 +pkg debug/elf, const R_PPC64_GOT_TLSLD_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_GOT_TPREL_PCREL34 = 150 #54345 +pkg debug/elf, const R_PPC64_GOT_TPREL_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PCREL28 = 145 #54345 +pkg debug/elf, const R_PPC64_PCREL28 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PCREL34 = 132 #54345 +pkg debug/elf, const R_PPC64_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PCREL_OPT = 123 #54345 +pkg debug/elf, const R_PPC64_PCREL_OPT R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT16_HA = 31 #54345 +pkg debug/elf, const R_PPC64_PLT16_HA R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT16_HI = 30 #54345 +pkg debug/elf, const R_PPC64_PLT16_HI R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT16_LO = 29 #54345 +pkg debug/elf, const R_PPC64_PLT16_LO R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT32 = 27 #54345 +pkg debug/elf, const R_PPC64_PLT32 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT64 = 45 #54345 +pkg debug/elf, const R_PPC64_PLT64 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTCALL = 120 #54345 +pkg debug/elf, const R_PPC64_PLTCALL R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTCALL_NOTOC = 122 #54345 +pkg debug/elf, const R_PPC64_PLTCALL_NOTOC R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTREL32 = 28 #54345 +pkg debug/elf, const R_PPC64_PLTREL32 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTREL64 = 46 #54345 +pkg debug/elf, const R_PPC64_PLTREL64 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTSEQ = 119 #54345 +pkg debug/elf, const R_PPC64_PLTSEQ R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLTSEQ_NOTOC = 121 #54345 +pkg debug/elf, const R_PPC64_PLTSEQ_NOTOC R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT_PCREL34 = 134 #54345 +pkg debug/elf, const R_PPC64_PLT_PCREL34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_PLT_PCREL34_NOTOC = 135 #54345 +pkg debug/elf, const R_PPC64_PLT_PCREL34_NOTOC R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGH = 240 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGH R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHA = 241 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHA R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHER = 242 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHER R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHER34 = 140 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHER34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHERA = 243 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHERA R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHERA34 = 141 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHERA34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHEST = 244 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHEST R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHEST34 = 142 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHEST34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHESTA = 245 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHESTA R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHESTA34 = 143 #54345 +pkg debug/elf, const R_PPC64_REL16_HIGHESTA34 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_REL30 = 37 #54345 +pkg debug/elf, const R_PPC64_REL30 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_SECTOFF = 33 #54345 +pkg debug/elf, const R_PPC64_SECTOFF R_PPC64 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_HA = 36 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_HA R_PPC64 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_HI = 35 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_HI R_PPC64 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_LO = 34 #54345 +pkg debug/elf, const R_PPC64_SECTOFF_LO R_PPC64 #54345 +pkg debug/elf, const R_PPC64_TPREL28 = 146 #54345 +pkg debug/elf, const R_PPC64_TPREL28 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_UADDR16 = 25 #54345 +pkg debug/elf, const R_PPC64_UADDR16 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_UADDR32 = 24 #54345 +pkg debug/elf, const R_PPC64_UADDR32 R_PPC64 #54345 +pkg debug/elf, const R_PPC64_UADDR64 = 43 #54345 +pkg debug/elf, const R_PPC64_UADDR64 R_PPC64 #54345 diff --git a/doc/asm.html b/doc/asm.html index 51f85eb94822a4..f7787a4076ce60 100644 --- a/doc/asm.html +++ b/doc/asm.html @@ -125,8 +125,8 @@

Constants

One is in constant evaluation. Constant expressions in the assembler are parsed using Go's operator precedence, not the C-like precedence of the original. -Thus 3&1<<2 is 4, not 0—it parses as (3&1)<<2 -not 3&(1<<2). +Thus 3&1<<2 is 4, not 0—it parses as (3&1)<<2 +not 3&(1<<2). Also, constants are always evaluated as 64-bit unsigned integers. Thus -2 is not the integer value minus two, but the unsigned 64-bit integer with the same bit pattern. @@ -914,8 +914,6 @@

PPC64

Reference: Go PPC64 Assembly Instructions Reference Manual

- -

IBM z/Architecture, a.k.a. s390x

diff --git a/doc/go1.17.html b/doc/go1.17.html deleted file mode 100644 index b65d13a0403885..00000000000000 --- a/doc/go1.17.html +++ /dev/null @@ -1,1240 +0,0 @@ - - - - - - -

Introduction to Go 1.17

- -

- The latest Go release, version 1.17, arrives six months after Go 1.16. - Most of its changes are in the implementation of the toolchain, runtime, and libraries. - 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. -

- -

Changes to the language

- -

- Go 1.17 includes three small enhancements to the language. -

- -
    -
  • - Conversions - from slice to array pointer: An expression s of - type []T may now be converted to array pointer type - *[N]T. If a is the result of such a - conversion, then corresponding indices that are in range refer to - the same underlying elements: &a[i] == &s[i] - for 0 <= i < N. The conversion panics if - len(s) is less than N. -
  • - -
  • - unsafe.Add: - unsafe.Add(ptr, len) adds len - to ptr and returns the updated pointer - unsafe.Pointer(uintptr(ptr) + uintptr(len)). -
  • - -
  • - unsafe.Slice: - For expression ptr of type *T, - unsafe.Slice(ptr, len) returns a slice of - type []T whose underlying array starts - at ptr and whose length and capacity - are len. -
  • -
- -

- The package unsafe enhancements were added to simplify writing code that conforms - to unsafe.Pointer's safety - rules, but the rules remain unchanged. In particular, existing - programs that correctly use unsafe.Pointer remain - valid, and new programs must still follow the rules when - using unsafe.Add or unsafe.Slice. -

- - -

- Note that the new conversion from slice to array pointer is the - first case in which a type conversion can panic at run time. - Analysis tools that assume type conversions can never panic - should be updated to consider this possibility. -

- -

Ports

- -

Darwin

- -

- As announced in the Go 1.16 release - notes, Go 1.17 requires macOS 10.13 High Sierra or later; support - for previous versions has been discontinued. -

- -

Windows

- -

- Go 1.17 adds support of 64-bit ARM architecture on Windows (the - windows/arm64 port). This port supports cgo. -

- -

OpenBSD

- -

- The 64-bit MIPS architecture on OpenBSD (the openbsd/mips64 - port) now supports cgo. -

- -

- In Go 1.16, on the 64-bit x86 and 64-bit ARM architectures on - OpenBSD (the openbsd/amd64 and openbsd/arm64 - ports) system calls are made through libc, instead - of directly using machine instructions. In Go 1.17, this is also - done on the 32-bit x86 and 32-bit ARM architectures on OpenBSD - (the openbsd/386 and openbsd/arm ports). - This ensures compatibility with OpenBSD 6.9 onwards, which require - system calls to be made through libc for non-static - Go binaries. -

- -

ARM64

- -

- Go programs now maintain stack frame pointers on the 64-bit ARM - architecture on all operating systems. Previously it maintained - stack frame pointers only on Linux, macOS, and iOS. -

- -

loong64 GOARCH value reserved

- -

- The main Go compiler does not yet support the LoongArch - architecture, but we've reserved the GOARCH value - "loong64". - This means that Go files named *_loong64.go will now - be ignored by Go - tools except when that GOARCH value is being used. -

- -

Tools

- -

Go command

- - -

Pruned module graphs in go 1.17 modules

- -

- If a module specifies go 1.17 or higher, the module - graph includes only the immediate dependencies of - other go 1.17 modules, not their full transitive - dependencies. (See Module graph pruning - for more detail.) -

- -

- For the go command to correctly resolve transitive imports using - the pruned module graph, the go.mod file for each module needs to - include more detail about the transitive dependencies relevant to that module. - If a module specifies go 1.17 or higher in its - go.mod file, its go.mod file now contains an - explicit require - directive for every module that provides a transitively-imported package. - (In previous versions, the go.mod file typically only included - explicit requirements for directly-imported packages.) -

- -

- Since the expanded go.mod file needed for module graph pruning - includes all of the dependencies needed to load the imports of any package in - the main module, if the main module specifies - go 1.17 or higher the go tool no longer - reads (or even downloads) go.mod files for dependencies if they - are not needed in order to complete the requested command. - (See Lazy loading.) -

- -

- Because the number of explicit requirements may be substantially larger in an - expanded Go 1.17 go.mod file, the newly-added requirements - on indirect dependencies in a go 1.17 - module are maintained in a separate require block from the block - containing direct dependencies. -

- -

- To facilitate the upgrade to Go 1.17 pruned module graphs, the - go mod tidy - subcommand now supports a -go flag to set or change - the go version in the go.mod file. To convert - the go.mod file for an existing module to Go 1.17 without - changing the selected versions of its dependencies, run: -

- -
-  go mod tidy -go=1.17
-
- -

- By default, go mod tidy verifies that - the selected versions of dependencies relevant to the main module are the same - versions that would be used by the prior Go release (Go 1.16 for a module that - specifies go 1.17), and preserves - the go.sum entries needed by that release even for dependencies - that are not normally needed by other commands. -

- -

- The -compat flag allows that version to be overridden to support - older (or only newer) versions, up to the version specified by - the go directive in the go.mod file. To tidy - a go 1.17 module for Go 1.17 only, without saving - checksums for (or checking for consistency with) Go 1.16: -

- -
-  go mod tidy -compat=1.17
-
- -

- Note that even if the main module is tidied with -compat=1.17, - users who require the module from a - go 1.16 or earlier module will still be able to - use it, provided that the packages use only compatible language and library - features. -

- -

- The go mod graph - subcommand also supports the -go flag, which causes it to report - the graph as seen by the indicated Go version, showing dependencies that may - otherwise be pruned out. -

- -

Module deprecation comments

- -

- Module authors may deprecate a module by adding a - // Deprecated: - comment to go.mod, then tagging a new version. - go get now prints a warning if a module needed to - build packages named on the command line is deprecated. go - list -m -u prints deprecations for all - dependencies (use -f or -json to show the full - message). The go command considers different major versions to - be distinct modules, so this mechanism may be used, for example, to provide - users with migration instructions for a new major version. -

- -

go get

- -

- The go get -insecure flag is - deprecated and has been removed. To permit the use of insecure schemes - when fetching dependencies, please use the GOINSECURE - environment variable. The -insecure flag also bypassed module - sum validation, use GOPRIVATE or GONOSUMDB if - you need that functionality. See go help - environment for details. -

- -

- go get prints a deprecation warning when installing - commands outside the main module (without the -d flag). - go install cmd@version should be used - instead to install a command at a specific version, using a suffix like - @latest or @v1.2.3. In Go 1.18, the -d - flag will always be enabled, and go get will only - be used to change dependencies in go.mod. -

- -

go.mod files missing go directives

- -

- If the main module's go.mod file does not contain - a go directive and - the go command cannot update the go.mod file, the - go command now assumes go 1.11 instead of the - current release. (go mod init has added - go directives automatically since - Go 1.12.) -

- -

- If a module dependency lacks an explicit go.mod file, or - its go.mod file does not contain - a go directive, - the go command now assumes go 1.16 for that - dependency instead of the current release. (Dependencies developed in GOPATH - mode may lack a go.mod file, and - the vendor/modules.txt has to date never recorded - the go versions indicated by dependencies' go.mod - files.) -

- -

vendor contents

- -

- If the main module specifies go 1.17 or higher, - go mod vendor - now annotates - vendor/modules.txt with the go version indicated by - each vendored module in its own go.mod file. The annotated - version is used when building the module's packages from vendored source code. -

- -

- If the main module specifies go 1.17 or higher, - go mod vendor now omits go.mod - and go.sum files for vendored dependencies, which can otherwise - interfere with the ability of the go command to identify the correct - module root when invoked within the vendor tree. -

- -

Password prompts

- -

- The go command by default now suppresses SSH password prompts and - Git Credential Manager prompts when fetching Git repositories using SSH, as it - already did previously for other Git password prompts. Users authenticating to - private Git repos with password-protected SSH may configure - an ssh-agent to enable the go command to use - password-protected SSH keys. -

- -

go mod download

- -

- When go mod download is invoked without - arguments, it will no longer save sums for downloaded module content to - go.sum. It may still make changes to go.mod and - go.sum needed to load the build list. This is the same as the - behavior in Go 1.15. To save sums for all modules, use go - mod download all. -

- -

//go:build lines

- -

- The go command now understands //go:build lines - and prefers them over // +build lines. The new syntax uses - boolean expressions, just like Go, and should be less error-prone. - As of this release, the new syntax is fully supported, and all Go files - should be updated to have both forms with the same meaning. To aid in - migration, gofmt now automatically - synchronizes the two forms. For more details on the syntax and migration plan, - see - https://golang.org/design/draft-gobuild. -

- -

go run

- -

- go run now accepts arguments with version suffixes - (for example, go run - example.com/cmd@v1.0.0). This causes go - run to build and run packages in module-aware mode, ignoring the - go.mod file in the current directory or any parent directory, if - there is one. This is useful for running executables without installing them or - without changing dependencies of the current module. -

- -

Gofmt

- -

- gofmt (and go fmt) now synchronizes - //go:build lines with // +build lines. If a file - only has // +build lines, they will be moved to the appropriate - location in the file, and matching //go:build lines will be - added. Otherwise, // +build lines will be overwritten based on - any existing //go:build lines. For more information, see - https://golang.org/design/draft-gobuild. -

- -

Vet

- -

New warning for mismatched //go:build and // +build lines

- -

- The vet tool now verifies that //go:build and - // +build lines are in the correct part of the file and - synchronized with each other. If they aren't, - gofmt can be used to fix them. For more - information, see - https://golang.org/design/draft-gobuild. -

- -

New warning for calling signal.Notify on unbuffered channels

- -

- The vet tool now warns about calls to signal.Notify - with incoming signals being sent to an unbuffered channel. Using an unbuffered channel - risks missing signals sent on them as signal.Notify does not block when - sending to a channel. For example: -

- -
-c := make(chan os.Signal)
-// signals are sent on c before the channel is read from.
-// This signal may be dropped as c is unbuffered.
-signal.Notify(c, os.Interrupt)
-
- -

- Users of signal.Notify should use channels with sufficient buffer space to keep up with the - expected signal rate. -

- -

New warnings for Is, As and Unwrap methods

- -

- The vet tool now warns about methods named As, Is or Unwrap - on types implementing the error interface that have a different signature than the - one expected by the errors package. The errors.{As,Is,Unwrap} functions - expect such methods to implement either Is(error) bool, - As(interface{}) bool, or Unwrap() error - respectively. The functions errors.{As,Is,Unwrap} will ignore methods with the same - names but a different signature. For example: -

- -
-type MyError struct { hint string }
-func (m MyError) Error() string { ... } // MyError implements error.
-func (MyError) Is(target interface{}) bool { ... } // target is interface{} instead of error.
-func Foo() bool {
-	x, y := MyError{"A"}, MyError{"B"}
-	return errors.Is(x, y) // returns false as x != y and MyError does not have an `Is(error) bool` function.
-}
-
- -

Cover

- -

- The cover tool now uses an optimized parser - from golang.org/x/tools/cover, which may be noticeably faster - when parsing large coverage profiles. -

- -

Compiler

- -

- Go 1.17 implements a new way of passing function arguments and results using - registers instead of the stack. - Benchmarks for a representative set of Go packages and programs show - performance improvements of about 5%, and a typical reduction in - binary size of about 2%. - This is currently enabled for Linux, macOS, and Windows on the - 64-bit x86 architecture (the linux/amd64, - darwin/amd64, and windows/amd64 ports). -

- -

- This change does not affect the functionality of any safe Go code - and is designed to have no impact on most assembly code. - It may affect code that violates - the unsafe.Pointer - rules when accessing function arguments, or that depends on - undocumented behavior involving comparing function code pointers. - To maintain compatibility with existing assembly functions, the - compiler generates adapter functions that convert between the new - register-based calling convention and the previous stack-based - calling convention. - These adapters are typically invisible to users, except that taking - the address of a Go function in assembly code or taking the address - of an assembly function in Go code - using reflect.ValueOf(fn).Pointer() - or unsafe.Pointer will now return the address of the - adapter. - Code that depends on the value of these code pointers may no longer - behave as expected. - Adapters also may cause a very small performance overhead in two - cases: calling an assembly function indirectly from Go via - a func value, and calling Go functions from assembly. -

- -

- The format of stack traces from the runtime (printed when an uncaught panic - occurs, or when runtime.Stack is called) is improved. Previously, - the function arguments were printed as hexadecimal words based on the memory - layout. Now each argument in the source code is printed separately, separated - by commas. Aggregate-typed (struct, array, string, slice, interface, and complex) - arguments are delimited by curly braces. A caveat is that the value of an - argument that only lives in a register and is not stored to memory may be - inaccurate. Function return values (which were usually inaccurate) are no longer - printed. -

- -

- Functions containing closures can now be inlined. - One effect of this change is that a function with a closure may - produce a distinct closure code pointer for each place that the - function is inlined. - Go function values are not directly comparable, but this change - could reveal bugs in code that uses reflect - or unsafe.Pointer to bypass this language restriction - and compare functions by code pointer. -

- - - -

- When the linker uses external linking mode, which is the default - when linking a program that uses cgo, and the linker is invoked - with a -I option, the option will now be passed to the - external linker as a -Wl,--dynamic-linker option. -

- -

Core library

- -

Cgo

- -

- The runtime/cgo package now provides a - new facility that allows to turn any Go values to a safe representation - that can be used to pass values between C and Go safely. See - runtime/cgo.Handle for more information. -

- -

URL query parsing

- - -

- The net/url and net/http packages used to accept - ";" (semicolon) as a setting separator in URL queries, in - addition to "&" (ampersand). Now, settings with non-percent-encoded - semicolons are rejected and net/http servers will log a warning to - Server.ErrorLog - when encountering one in a request URL. -

- -

- For example, before Go 1.17 the Query - method of the URL example?a=1;b=2&c=3 would have returned - map[a:[1] b:[2] c:[3]], while now it returns map[c:[3]]. -

- -

- When encountering such a query string, - URL.Query - and - Request.FormValue - ignore any settings that contain a semicolon, - ParseQuery - returns the remaining settings and an error, and - Request.ParseForm - and - Request.ParseMultipartForm - return an error but still set Request fields based on the - remaining settings. -

- -

- net/http users can restore the original behavior by using the new - AllowQuerySemicolons - handler wrapper. This will also suppress the ErrorLog warning. - Note that accepting semicolons as query separators can lead to security issues - if different systems interpret cache keys differently. - See issue 25192 for more information. -

- -

TLS strict ALPN

- - -

- When Config.NextProtos - is set, servers now enforce that there is an overlap between the configured - protocols and the ALPN protocols advertised by the client, if any. If there is - no mutually supported protocol, the connection is closed with the - no_application_protocol alert, as required by RFC 7301. This - helps mitigate the ALPACA cross-protocol attack. -

- -

- As an exception, when the value "h2" is included in the server's - Config.NextProtos, HTTP/1.1 clients will be allowed to connect as - if they didn't support ALPN. - See issue 46310 for more information. -

- -

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. -

- -
archive/zip
-
-

- The new methods File.OpenRaw, Writer.CreateRaw, Writer.Copy provide support for cases where performance is a primary concern. -

-
-
- -
bufio
-
-

- The Writer.WriteRune method - now writes the replacement character U+FFFD for negative rune values, - as it does for other invalid runes. -

-
-
- -
bytes
-
-

- The Buffer.WriteRune method - now writes the replacement character U+FFFD for negative rune values, - as it does for other invalid runes. -

-
-
- -
compress/lzw
-
-

- The NewReader - function is guaranteed to return a value of the new - type Reader, - and similarly NewWriter - is guaranteed to return a value of the new - type Writer. - These new types both implement a Reset method - (Reader.Reset, - Writer.Reset) - that allows reuse of the Reader or Writer. -

-
-
- -
crypto/ed25519
-
-

- The crypto/ed25519 package has been rewritten, and all - operations are now approximately twice as fast on amd64 and arm64. - The observable behavior has not otherwise changed. -

-
-
- -
crypto/elliptic
-
-

- CurveParams - methods now automatically invoke faster and safer dedicated - implementations for known curves (P-224, P-256, and P-521) when - available. Note that this is a best-effort approach and applications - should avoid using the generic, not constant-time CurveParams - methods and instead use dedicated - Curve implementations - such as P256. -

- -

- The P521 curve - implementation has been rewritten using code generated by the - fiat-crypto project, - which is based on a formally-verified model of the arithmetic - operations. It is now constant-time and three times faster on amd64 and - arm64. The observable behavior has not otherwise changed. -

-
-
- -
crypto/rand
-
-

- The crypto/rand package now uses the getentropy - syscall on macOS and the getrandom syscall on Solaris, - Illumos, and DragonFlyBSD. -

-
-
- -
crypto/tls
-
-

- The new Conn.HandshakeContext - method allows the user to control cancellation of an in-progress TLS - handshake. The provided context is accessible from various callbacks through the new - ClientHelloInfo.Context and - CertificateRequestInfo.Context - methods. Canceling the context after the handshake has finished has no effect. -

- -

- Cipher suite ordering is now handled entirely by the - crypto/tls package. Currently, cipher suites are sorted based - on their security, performance, and hardware support taking into account - both the local and peer's hardware. The order of the - Config.CipherSuites - field is now ignored, as well as the - Config.PreferServerCipherSuites - field. Note that Config.CipherSuites still allows - applications to choose what TLS 1.0–1.2 cipher suites to enable. -

- -

- The 3DES cipher suites have been moved to - InsecureCipherSuites - due to fundamental block size-related - weakness. They are still enabled by default but only as a last resort, - thanks to the cipher suite ordering change above. -

- -

- Beginning in the next release, Go 1.18, the - Config.MinVersion - for crypto/tls clients will default to TLS 1.2, disabling TLS 1.0 - and TLS 1.1 by default. Applications will be able to override the change by - explicitly setting Config.MinVersion. - This will not affect crypto/tls servers. -

-
-
- -
crypto/x509
-
-

- CreateCertificate - now returns an error if the provided private key doesn't match the - parent's public key, if any. The resulting certificate would have failed - to verify. -

- -

- The temporary GODEBUG=x509ignoreCN=0 flag has been removed. -

- -

- ParseCertificate - has been rewritten, and now consumes ~70% fewer resources. The observable - behavior has not otherwise changed, except for error messages. -

- -

- On BSD systems, /etc/ssl/certs is now searched for trusted - roots. This adds support for the new system trusted certificate store in - FreeBSD 12.2+. -

- -

- Beginning in the next release, Go 1.18, crypto/x509 will - reject certificates signed with the SHA-1 hash function. This doesn't - apply to self-signed root certificates. Practical attacks against SHA-1 - have been demonstrated in 2017 and publicly - trusted Certificate Authorities have not issued SHA-1 certificates since 2015. -

-
-
- -
database/sql
-
-

- The DB.Close method now closes - the connector field if the type in this field implements the - io.Closer interface. -

- -

- The new - NullInt16 - and - NullByte - structs represent the int16 and byte values that may be null. These can be used as - destinations of the Scan method, - similar to NullString. -

-
-
- -
debug/elf
-
-

- The SHT_MIPS_ABIFLAGS - constant has been added. -

-
-
- -
encoding/binary
-
-

- binary.Uvarint will stop reading after 10 bytes to avoid - wasted computations. If more than 10 bytes are needed, the byte count returned is -11. -
- Previous Go versions could return larger negative counts when reading incorrectly encoded varints. -

-
-
- -
encoding/csv
-
-

- The new - Reader.FieldPos - method returns the line and column corresponding to the start of - a given field in the record most recently returned by - Read. -

-
-
- -
encoding/xml
-
-

- When a comment appears within a - Directive, it is now replaced - with a single space instead of being completely elided. -

- -

- Invalid element or attribute names with leading, trailing, or multiple - colons are now stored unmodified into the - Name.Local field. -

-
-
- -
flag
-
-

- Flag declarations now panic if an invalid name is specified. -

-
-
- -
go/build
-
-

- The new - Context.ToolTags - field holds the build tags appropriate to the current Go - toolchain configuration. -

-
-
- -
go/format
-
-

- The Source and - Node functions now - synchronize //go:build lines with // +build - lines. If a file only has // +build lines, they will be - moved to the appropriate location in the file, and matching - //go:build lines will be added. Otherwise, - // +build lines will be overwritten based on any existing - //go:build lines. For more information, see - https://golang.org/design/draft-gobuild. -

-
-
- -
go/parser
-
-

- The new SkipObjectResolution - Mode value instructs the parser not to resolve identifiers to - their declaration. This may improve parsing speed. -

-
-
- -
image
-
-

- The concrete image types (RGBA, Gray16 and so on) - now implement a new RGBA64Image - interface. The concrete types that previously implemented - draw.Image now also implement - draw.RGBA64Image, a - new interface in the image/draw package. -

-
-
- -
io/fs
-
-

- The new FileInfoToDirEntry function converts a FileInfo to a DirEntry. -

-
-
- -
math
-
-

- The math package now defines three more constants: MaxUint, MaxInt and MinInt. - For 32-bit systems their values are 2^32 - 1, 2^31 - 1 and -2^31, respectively. - For 64-bit systems their values are 2^64 - 1, 2^63 - 1 and -2^63, respectively. -

-
-
- -
mime
-
-

- On Unix systems, the table of MIME types is now read from the local system's - Shared MIME-info Database - when available. -

-
-
- -
mime/multipart
-
-

- Part.FileName - now applies - filepath.Base to the - return value. This mitigates potential path traversal vulnerabilities in - applications that accept multipart messages, such as net/http - servers that call - Request.FormFile. -

-
-
- -
net
-
-

- The new method IP.IsPrivate reports whether an address is - a private IPv4 address according to RFC 1918 - or a local IPv6 address according RFC 4193. -

- -

- The Go DNS resolver now only sends one DNS query when resolving an address for an IPv4-only or IPv6-only network, - rather than querying for both address families. -

- -

- The ErrClosed sentinel error and - ParseError error type now implement - the net.Error interface. -

- -

- The ParseIP and ParseCIDR - functions now reject IPv4 addresses which contain decimal components with leading zeros. - - These components were always interpreted as decimal, but some operating systems treat them as octal. - This mismatch could hypothetically lead to security issues if a Go application was used to validate IP addresses - which were then used in their original form with non-Go applications which interpreted components as octal. Generally, - it is advisable to always re-encode values after validation, which avoids this class of parser misalignment issues. -

-
-
- -
net/http
-
-

- The net/http package now uses the new - (*tls.Conn).HandshakeContext - with the Request context - when performing TLS handshakes in the client or server. -

- -

- Setting the Server - ReadTimeout or WriteTimeout fields to a negative value now indicates no timeout - rather than an immediate timeout. -

- -

- The ReadRequest function - now returns an error when the request has multiple Host headers. -

- -

- When producing a redirect to the cleaned version of a URL, - ServeMux now always - uses relative URLs in the Location header. Previously it - would echo the full URL of the request, which could lead to unintended - redirects if the client could be made to send an absolute request URL. -

- -

- When interpreting certain HTTP headers handled by net/http, - non-ASCII characters are now ignored or rejected. -

- -

- If - Request.ParseForm - returns an error when called by - Request.ParseMultipartForm, - the latter now continues populating - Request.MultipartForm - before returning it. -

-
-
- -
net/http/httptest
-
-

- ResponseRecorder.WriteHeader - now panics when the provided code is not a valid three-digit HTTP status code. - This matches the behavior of ResponseWriter - implementations in the net/http package. -

-
-
- -
net/url
-
-

- The new method Values.Has - reports whether a query parameter is set. -

-
-
- -
os
-
-

- The File.WriteString method - has been optimized to not make a copy of the input string. -

-
-
- -
reflect
-
-

- The new - Value.CanConvert - method reports whether a value can be converted to a type. - This may be used to avoid a panic when converting a slice to an - array pointer type if the slice is too short. - Previously it was sufficient to use - Type.ConvertibleTo - for this, but the newly permitted conversion from slice to array - pointer type can panic even if the types are convertible. -

- -

- The new - StructField.IsExported - and - Method.IsExported - methods report whether a struct field or type method is exported. - They provide a more readable alternative to checking whether PkgPath - is empty. -

- -

- The new VisibleFields function - returns all the visible fields in a struct type, including fields inside anonymous struct members. -

- -

- The ArrayOf function now panics when - called with a negative length. -

- -

- Checking the Type.ConvertibleTo method - is no longer sufficient to guarantee that a call to - Value.Convert will not panic. - It may panic when converting `[]T` to `*[N]T` if the slice's length is less than N. - See the language changes section above. -

-
-
- -
runtime/metrics
-
-

- New metrics were added that track total bytes and objects allocated and freed. - A new metric tracking the distribution of goroutine scheduling latencies was - also added. -

-
-
- -
runtime/pprof
-
-

- Block profiles are no longer biased to favor infrequent long events over - frequent short events. -

-
-
- -
strconv
-
-

- The strconv package now uses Ulf Adams's Ryū algorithm for formatting floating-point numbers. - This algorithm improves performance on most inputs and is more than 99% faster on worst-case inputs. -

- -

- The new QuotedPrefix function - returns the quoted string (as understood by - Unquote) - at the start of input. -

-
-
- -
strings
-
-

- The Builder.WriteRune method - now writes the replacement character U+FFFD for negative rune values, - as it does for other invalid runes. -

-
-
- -
sync/atomic
-
-

- atomic.Value now has Swap and - CompareAndSwap methods that provide - additional atomic operations. -

-
-
- -
syscall
-
-

-

- The GetQueuedCompletionStatus and - PostQueuedCompletionStatus - functions are now deprecated. These functions have incorrect signatures and are superseded by - equivalents in the golang.org/x/sys/windows package. -

- -

- On Unix-like systems, the process group of a child process is now set with signals blocked. - This avoids sending a SIGTTOU to the child when the parent is in a background process group. -

- -

- The Windows version of - SysProcAttr - has two new fields. AdditionalInheritedHandles is - a list of additional handles to be inherited by the new child - process. ParentProcess permits specifying the - parent process of the new process. - -

- The constant MSG_CMSG_CLOEXEC is now defined on - DragonFly and all OpenBSD systems (it was already defined on - some OpenBSD systems and all FreeBSD, NetBSD, and Linux systems). -

- -

- The constants SYS_WAIT6 and WEXITED - are now defined on NetBSD systems (SYS_WAIT6 was - already defined on DragonFly and FreeBSD systems; - WEXITED was already defined on Darwin, DragonFly, - FreeBSD, Linux, and Solaris systems). -

-
-
- -
testing
-
-

- Added a new testing flag -shuffle which controls the execution order of tests and benchmarks. -

-

- The new - T.Setenv - and B.Setenv - methods support setting an environment variable for the duration - of the test or benchmark. -

-
-
- -
text/template/parse
-
-

- The new SkipFuncCheck Mode - value changes the template parser to not verify that functions are defined. -

-
-
- -
time
-
-

- The Time type now has a - GoString method that - will return a more useful value for times when printed with the - %#v format specifier in the fmt package. -

- -

- The new Time.IsDST method can be used to check whether the time - is in Daylight Savings Time in its configured location. -

- -

- The new Time.UnixMilli and - Time.UnixMicro - methods return the number of milliseconds and microseconds elapsed since - January 1, 1970 UTC respectively. -
- The new UnixMilli and - UnixMicro functions - return the local Time corresponding to the given Unix time. -

- -

- The package now accepts comma "," as a separator for fractional seconds when parsing and formatting time. - The following time formats are now accepted: -

    -
  • 2006-01-02 14:06:03,999999999 -0700 MST
  • -
  • Mon Jan _2 14:06:03,120007 2006
  • -
  • Mon Jan 2 14:06:03,120007 2006
  • -
-

- -

- The new constant Layout - defines the reference time. -

-
-
- -
unicode
-
-

- The Is, - IsGraphic, - IsLetter, - IsLower, - IsMark, - IsNumber, - IsPrint, - IsPunct, - IsSpace, - IsSymbol, and - IsUpper functions - now return false on negative rune values, as they do for other invalid runes. -

-
-
diff --git a/doc/go1.17_spec.html b/doc/go1.17_spec.html new file mode 100644 index 00000000000000..0b374e7bfb2a93 --- /dev/null +++ b/doc/go1.17_spec.html @@ -0,0 +1,6858 @@ + + +

Introduction

+ +

+This is a reference manual for the Go programming language. For +more information and other documents, see golang.org. +

+ +

+Go is a general-purpose language designed with systems programming +in mind. It is strongly typed and garbage-collected and has explicit +support for concurrent programming. Programs are constructed from +packages, whose properties allow efficient management of +dependencies. +

+ +

+The grammar is compact and simple to parse, allowing for easy analysis +by automatic tools such as integrated development environments. +

+ +

Notation

+

+The syntax is specified using Extended Backus-Naur Form (EBNF): +

+ +
+Production  = production_name "=" [ Expression ] "." .
+Expression  = Alternative { "|" Alternative } .
+Alternative = Term { Term } .
+Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
+Group       = "(" Expression ")" .
+Option      = "[" Expression "]" .
+Repetition  = "{" Expression "}" .
+
+ +

+Productions are expressions constructed from terms and the following +operators, in increasing precedence: +

+
+|   alternation
+()  grouping
+[]  option (0 or 1 times)
+{}  repetition (0 to n times)
+
+ +

+Lower-case production names are used to identify lexical tokens. +Non-terminals are in CamelCase. Lexical tokens are enclosed in +double quotes "" or back quotes ``. +

+ +

+The form a … b represents the set of characters from +a through b as alternatives. The horizontal +ellipsis is also used elsewhere in the spec to informally denote various +enumerations or code snippets that are not further specified. The character +(as opposed to the three characters ...) is not a token of the Go +language. +

+ +

Source code representation

+ +

+Source code is Unicode text encoded in +UTF-8. The text is not +canonicalized, so a single accented code point is distinct from the +same character constructed from combining an accent and a letter; +those are treated as two code points. For simplicity, this document +will use the unqualified term character to refer to a Unicode code point +in the source text. +

+

+Each code point is distinct; for instance, upper and lower case letters +are different characters. +

+

+Implementation restriction: For compatibility with other tools, a +compiler may disallow the NUL character (U+0000) in the source text. +

+

+Implementation restriction: For compatibility with other tools, a +compiler may ignore a UTF-8-encoded byte order mark +(U+FEFF) if it is the first Unicode code point in the source text. +A byte order mark may be disallowed anywhere else in the source. +

+ +

Characters

+ +

+The following terms are used to denote specific Unicode character classes: +

+
+newline        = /* the Unicode code point U+000A */ .
+unicode_char   = /* an arbitrary Unicode code point except newline */ .
+unicode_letter = /* a Unicode code point classified as "Letter" */ .
+unicode_digit  = /* a Unicode code point classified as "Number, decimal digit" */ .
+
+ +

+In The Unicode Standard 8.0, +Section 4.5 "General Category" defines a set of character categories. +Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo +as Unicode letters, and those in the Number category Nd as Unicode digits. +

+ +

Letters and digits

+ +

+The underscore character _ (U+005F) is considered a letter. +

+
+letter        = unicode_letter | "_" .
+decimal_digit = "0" … "9" .
+binary_digit  = "0" | "1" .
+octal_digit   = "0" … "7" .
+hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .
+
+ +

Lexical elements

+ +

Comments

+ +

+Comments serve as program documentation. There are two forms: +

+ +
    +
  1. +Line comments start with the character sequence // +and stop at the end of the line. +
  2. +
  3. +General comments start with the character sequence /* +and stop with the first subsequent character sequence */. +
  4. +
+ +

+A comment cannot start inside a rune or +string literal, or inside a comment. +A general comment containing no newlines acts like a space. +Any other comment acts like a newline. +

+ +

Tokens

+ +

+Tokens form the vocabulary of the Go language. +There are four classes: identifiers, keywords, operators +and punctuation, and literals. White space, formed from +spaces (U+0020), horizontal tabs (U+0009), +carriage returns (U+000D), and newlines (U+000A), +is ignored except as it separates tokens +that would otherwise combine into a single token. Also, a newline or end of file +may trigger the insertion of a semicolon. +While breaking the input into tokens, +the next token is the longest sequence of characters that form a +valid token. +

+ +

Semicolons

+ +

+The formal grammar uses semicolons ";" as terminators in +a number of productions. Go programs may omit most of these semicolons +using the following two rules: +

+ +
    +
  1. +When the input is broken into tokens, a semicolon is automatically inserted +into the token stream immediately after a line's final token if that token is + +
  2. + +
  3. +To allow complex statements to occupy a single line, a semicolon +may be omitted before a closing ")" or "}". +
  4. +
+ +

+To reflect idiomatic use, code examples in this document elide semicolons +using these rules. +

+ + +

Identifiers

+ +

+Identifiers name program entities such as variables and types. +An identifier is a sequence of one or more letters and digits. +The first character in an identifier must be a letter. +

+
+identifier = letter { letter | unicode_digit } .
+
+
+a
+_x9
+ThisVariableIsExported
+αβ
+
+ +

+Some identifiers are predeclared. +

+ + +

Keywords

+ +

+The following keywords are reserved and may not be used as identifiers. +

+
+break        default      func         interface    select
+case         defer        go           map          struct
+chan         else         goto         package      switch
+const        fallthrough  if           range        type
+continue     for          import       return       var
+
+ +

Operators and punctuation

+ +

+The following character sequences represent operators +(including assignment operators) and punctuation: +

+
++    &     +=    &=     &&    ==    !=    (    )
+-    |     -=    |=     ||    <     <=    [    ]
+*    ^     *=    ^=     <-    >     >=    {    }
+/    <<    /=    <<=    ++    =     :=    ,    ;
+%    >>    %=    >>=    --    !     ...   .    :
+     &^          &^=
+
+ +

Integer literals

+ +

+An integer literal is a sequence of digits representing an +integer constant. +An optional prefix sets a non-decimal base: 0b or 0B +for binary, 0, 0o, or 0O for octal, +and 0x or 0X for hexadecimal. +A single 0 is considered a decimal zero. +In hexadecimal literals, letters a through f +and A through F represent values 10 through 15. +

+ +

+For readability, an underscore character _ may appear after +a base prefix or between successive digits; such underscores do not change +the literal's value. +

+
+int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
+decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
+binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
+octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
+hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .
+
+decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
+binary_digits  = binary_digit { [ "_" ] binary_digit } .
+octal_digits   = octal_digit { [ "_" ] octal_digit } .
+hex_digits     = hex_digit { [ "_" ] hex_digit } .
+
+ +
+42
+4_2
+0600
+0_600
+0o600
+0O600       // second character is capital letter 'O'
+0xBadFace
+0xBad_Face
+0x_67_7a_2f_cc_40_c6
+170141183460469231731687303715884105727
+170_141183_460469_231731_687303_715884_105727
+
+_42         // an identifier, not an integer literal
+42_         // invalid: _ must separate successive digits
+4__2        // invalid: only one _ at a time
+0_xBadFace  // invalid: _ must separate successive digits
+
+ + +

Floating-point literals

+ +

+A floating-point literal is a decimal or hexadecimal representation of a +floating-point constant. +

+ +

+A decimal floating-point literal consists of an integer part (decimal digits), +a decimal point, a fractional part (decimal digits), and an exponent part +(e or E followed by an optional sign and decimal digits). +One of the integer part or the fractional part may be elided; one of the decimal point +or the exponent part may be elided. +An exponent value exp scales the mantissa (integer and fractional part) by 10exp. +

+ +

+A hexadecimal floating-point literal consists of a 0x or 0X +prefix, an integer part (hexadecimal digits), a radix point, a fractional part (hexadecimal digits), +and an exponent part (p or P followed by an optional sign and decimal digits). +One of the integer part or the fractional part may be elided; the radix point may be elided as well, +but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.) +An exponent value exp scales the mantissa (integer and fractional part) by 2exp. +

+ +

+For readability, an underscore character _ may appear after +a base prefix or between successive digits; such underscores do not change +the literal value. +

+ +
+float_lit         = decimal_float_lit | hex_float_lit .
+
+decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
+                    decimal_digits decimal_exponent |
+                    "." decimal_digits [ decimal_exponent ] .
+decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
+
+hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
+hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
+                    [ "_" ] hex_digits |
+                    "." hex_digits .
+hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
+
+ +
+0.
+72.40
+072.40       // == 72.40
+2.71828
+1.e+0
+6.67428e-11
+1E6
+.25
+.12345E+5
+1_5.         // == 15.0
+0.15e+0_2    // == 15.0
+
+0x1p-2       // == 0.25
+0x2.p10      // == 2048.0
+0x1.Fp+0     // == 1.9375
+0X.8p-0      // == 0.5
+0X_1FFFP-16  // == 0.1249847412109375
+0x15e-2      // == 0x15e - 2 (integer subtraction)
+
+0x.p1        // invalid: mantissa has no digits
+1p-2         // invalid: p exponent requires hexadecimal mantissa
+0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
+1_.5         // invalid: _ must separate successive digits
+1._5         // invalid: _ must separate successive digits
+1.5_e1       // invalid: _ must separate successive digits
+1.5e_1       // invalid: _ must separate successive digits
+1.5e1_       // invalid: _ must separate successive digits
+
+ + +

Imaginary literals

+ +

+An imaginary literal represents the imaginary part of a +complex constant. +It consists of an integer or +floating-point literal +followed by the lower-case letter i. +The value of an imaginary literal is the value of the respective +integer or floating-point literal multiplied by the imaginary unit i. +

+ +
+imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
+
+ +

+For backward compatibility, an imaginary literal's integer part consisting +entirely of decimal digits (and possibly underscores) is considered a decimal +integer, even if it starts with a leading 0. +

+ +
+0i
+0123i         // == 123i for backward-compatibility
+0o123i        // == 0o123 * 1i == 83i
+0xabci        // == 0xabc * 1i == 2748i
+0.i
+2.71828i
+1.e+0i
+6.67428e-11i
+1E6i
+.25i
+.12345E+5i
+0x1p-2i       // == 0x1p-2 * 1i == 0.25i
+
+ + +

Rune literals

+ +

+A rune literal represents a rune constant, +an integer value identifying a Unicode code point. +A rune literal is expressed as one or more characters enclosed in single quotes, +as in 'x' or '\n'. +Within the quotes, any character may appear except newline and unescaped single +quote. A single quoted character represents the Unicode value +of the character itself, +while multi-character sequences beginning with a backslash encode +values in various formats. +

+ +

+The simplest form represents the single character within the quotes; +since Go source text is Unicode characters encoded in UTF-8, multiple +UTF-8-encoded bytes may represent a single integer value. For +instance, the literal 'a' holds a single byte representing +a literal a, Unicode U+0061, value 0x61, while +'ä' holds two bytes (0xc3 0xa4) representing +a literal a-dieresis, U+00E4, value 0xe4. +

+ +

+Several backslash escapes allow arbitrary values to be encoded as +ASCII text. There are four ways to represent the integer value +as a numeric constant: \x followed by exactly two hexadecimal +digits; \u followed by exactly four hexadecimal digits; +\U followed by exactly eight hexadecimal digits, and a +plain backslash \ followed by exactly three octal digits. +In each case the value of the literal is the value represented by +the digits in the corresponding base. +

+ +

+Although these representations all result in an integer, they have +different valid ranges. Octal escapes must represent a value between +0 and 255 inclusive. Hexadecimal escapes satisfy this condition +by construction. The escapes \u and \U +represent Unicode code points so within them some values are illegal, +in particular those above 0x10FFFF and surrogate halves. +

+ +

+After a backslash, certain single-character escapes represent special values: +

+ +
+\a   U+0007 alert or bell
+\b   U+0008 backspace
+\f   U+000C form feed
+\n   U+000A line feed or newline
+\r   U+000D carriage return
+\t   U+0009 horizontal tab
+\v   U+000B vertical tab
+\\   U+005C backslash
+\'   U+0027 single quote  (valid escape only within rune literals)
+\"   U+0022 double quote  (valid escape only within string literals)
+
+ +

+All other sequences starting with a backslash are illegal inside rune literals. +

+
+rune_lit         = "'" ( unicode_value | byte_value ) "'" .
+unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
+byte_value       = octal_byte_value | hex_byte_value .
+octal_byte_value = `\` octal_digit octal_digit octal_digit .
+hex_byte_value   = `\` "x" hex_digit hex_digit .
+little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
+big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
+                           hex_digit hex_digit hex_digit hex_digit .
+escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
+
+ +
+'a'
+'ä'
+'本'
+'\t'
+'\000'
+'\007'
+'\377'
+'\x07'
+'\xff'
+'\u12e4'
+'\U00101234'
+'\''         // rune literal containing single quote character
+'aa'         // illegal: too many characters
+'\xa'        // illegal: too few hexadecimal digits
+'\0'         // illegal: too few octal digits
+'\uDFFF'     // illegal: surrogate half
+'\U00110000' // illegal: invalid Unicode code point
+
+ + +

String literals

+ +

+A string literal represents a string constant +obtained from concatenating a sequence of characters. There are two forms: +raw string literals and interpreted string literals. +

+ +

+Raw string literals are character sequences between back quotes, as in +`foo`. Within the quotes, any character may appear except +back quote. The value of a raw string literal is the +string composed of the uninterpreted (implicitly UTF-8-encoded) characters +between the quotes; +in particular, backslashes have no special meaning and the string may +contain newlines. +Carriage return characters ('\r') inside raw string literals +are discarded from the raw string value. +

+ +

+Interpreted string literals are character sequences between double +quotes, as in "bar". +Within the quotes, any character may appear except newline and unescaped double quote. +The text between the quotes forms the +value of the literal, with backslash escapes interpreted as they +are in rune literals (except that \' is illegal and +\" is legal), with the same restrictions. +The three-digit octal (\nnn) +and two-digit hexadecimal (\xnn) escapes represent individual +bytes of the resulting string; all other escapes represent +the (possibly multi-byte) UTF-8 encoding of individual characters. +Thus inside a string literal \377 and \xFF represent +a single byte of value 0xFF=255, while ÿ, +\u00FF, \U000000FF and \xc3\xbf represent +the two bytes 0xc3 0xbf of the UTF-8 encoding of character +U+00FF. +

+ +
+string_lit             = raw_string_lit | interpreted_string_lit .
+raw_string_lit         = "`" { unicode_char | newline } "`" .
+interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
+
+ +
+`abc`                // same as "abc"
+`\n
+\n`                  // same as "\\n\n\\n"
+"\n"
+"\""                 // same as `"`
+"Hello, world!\n"
+"日本語"
+"\u65e5本\U00008a9e"
+"\xff\u00FF"
+"\uD800"             // illegal: surrogate half
+"\U00110000"         // illegal: invalid Unicode code point
+
+ +

+These examples all represent the same string: +

+ +
+"日本語"                                 // UTF-8 input text
+`日本語`                                 // UTF-8 input text as a raw literal
+"\u65e5\u672c\u8a9e"                    // the explicit Unicode code points
+"\U000065e5\U0000672c\U00008a9e"        // the explicit Unicode code points
+"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // the explicit UTF-8 bytes
+
+ +

+If the source code represents a character as two code points, such as +a combining form involving an accent and a letter, the result will be +an error if placed in a rune literal (it is not a single code +point), and will appear as two code points if placed in a string +literal. +

+ + +

Constants

+ +

There are boolean constants, +rune constants, +integer constants, +floating-point constants, complex constants, +and string constants. Rune, integer, floating-point, +and complex constants are +collectively called numeric constants. +

+ +

+A constant value is represented by a +rune, +integer, +floating-point, +imaginary, +or +string literal, +an identifier denoting a constant, +a constant expression, +a conversion with a result that is a constant, or +the result value of some built-in functions such as +unsafe.Sizeof applied to any value, +cap or len applied to +some expressions, +real and imag applied to a complex constant +and complex applied to numeric constants. +The boolean truth values are represented by the predeclared constants +true and false. The predeclared identifier +iota denotes an integer constant. +

+ +

+In general, complex constants are a form of +constant expression +and are discussed in that section. +

+ +

+Numeric constants represent exact values of arbitrary precision and do not overflow. +Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, +and not-a-number values. +

+ +

+Constants may be typed or untyped. +Literal constants, true, false, iota, +and certain constant expressions +containing only untyped constant operands are untyped. +

+ +

+A constant may be given a type explicitly by a constant declaration +or conversion, or implicitly when used in a +variable declaration or an +assignment or as an +operand in an expression. +It is an error if the constant value +cannot be represented as a value of the respective type. +

+ +

+An untyped constant has a default type which is the type to which the +constant is implicitly converted in contexts where a typed value is required, +for instance, in a short variable declaration +such as i := 0 where there is no explicit type. +The default type of an untyped constant is bool, rune, +int, float64, complex128 or string +respectively, depending on whether it is a boolean, rune, integer, floating-point, +complex, or string constant. +

+ +

+Implementation restriction: Although numeric constants have arbitrary +precision in the language, a compiler may implement them using an +internal representation with limited precision. That said, every +implementation must: +

+ +
    +
  • Represent integer constants with at least 256 bits.
  • + +
  • Represent floating-point constants, including the parts of + a complex constant, with a mantissa of at least 256 bits + and a signed binary exponent of at least 16 bits.
  • + +
  • Give an error if unable to represent an integer constant + precisely.
  • + +
  • Give an error if unable to represent a floating-point or + complex constant due to overflow.
  • + +
  • Round to the nearest representable constant if unable to + represent a floating-point or complex constant due to limits + on precision.
  • +
+ +

+These requirements apply both to literal constants and to the result +of evaluating constant +expressions. +

+ + +

Variables

+ +

+A variable is a storage location for holding a value. +The set of permissible values is determined by the +variable's type. +

+ +

+A variable declaration +or, for function parameters and results, the signature +of a function declaration +or function literal reserves +storage for a named variable. + +Calling the built-in function new +or taking the address of a composite literal +allocates storage for a variable at run time. +Such an anonymous variable is referred to via a (possibly implicit) +pointer indirection. +

+ +

+Structured variables of array, slice, +and struct types have elements and fields that may +be addressed individually. Each such element +acts like a variable. +

+ +

+The static type (or just type) of a variable is the +type given in its declaration, the type provided in the +new call or composite literal, or the type of +an element of a structured variable. +Variables of interface type also have a distinct dynamic type, +which is the concrete type of the value assigned to the variable at run time +(unless the value is the predeclared identifier nil, +which has no type). +The dynamic type may vary during execution but values stored in interface +variables are always assignable +to the static type of the variable. +

+ +
+var x interface{}  // x is nil and has static type interface{}
+var v *T           // v has value nil, static type *T
+x = 42             // x has value 42 and dynamic type int
+x = v              // x has value (*T)(nil) and dynamic type *T
+
+ +

+A variable's value is retrieved by referring to the variable in an +expression; it is the most recent value +assigned to the variable. +If a variable has not yet been assigned a value, its value is the +zero value for its type. +

+ + +

Types

+ +

+A type determines a set of values together with operations and methods specific +to those values. A type may be denoted by a type name, if it has one, +or specified using a type literal, which composes a type from existing types. +

+ +
+Type      = TypeName | TypeLit | "(" Type ")" .
+TypeName  = identifier | QualifiedIdent .
+TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
+	    SliceType | MapType | ChannelType .
+
+ +

+The language predeclares certain type names. +Others are introduced with type declarations. +Composite types—array, struct, pointer, function, +interface, slice, map, and channel types—may be constructed using +type literals. +

+ +

+Each type T has an underlying type: If T +is one of the predeclared boolean, numeric, or string types, or a type literal, +the corresponding underlying +type is T itself. Otherwise, T's underlying type +is the underlying type of the type to which T refers in its +type declaration. +

+ +
+type (
+	A1 = string
+	A2 = A1
+)
+
+type (
+	B1 string
+	B2 B1
+	B3 []B1
+	B4 B3
+)
+
+ +

+The underlying type of string, A1, A2, B1, +and B2 is string. +The underlying type of []B1, B3, and B4 is []B1. +

+ +

Method sets

+

+A type has a (possibly empty) method set associated with it. +The method set of an interface type is its interface. +The method set of any other type T consists of all +methods declared with receiver type T. +The method set of the corresponding pointer type *T +is the set of all methods declared with receiver *T or T +(that is, it also contains the method set of T). +Further rules apply to structs containing embedded fields, as described +in the section on struct types. +Any other type has an empty method set. +In a method set, each method must have a +unique +non-blank method name. +

+ +

+The method set of a type determines the interfaces that the +type implements +and the methods that can be called +using a receiver of that type. +

+ +

Boolean types

+ +

+A boolean type represents the set of Boolean truth values +denoted by the predeclared constants true +and false. The predeclared boolean type is bool; +it is a defined type. +

+ +

Numeric types

+ +

+A numeric type represents sets of integer or floating-point values. +The predeclared architecture-independent numeric types are: +

+ +
+uint8       the set of all unsigned  8-bit integers (0 to 255)
+uint16      the set of all unsigned 16-bit integers (0 to 65535)
+uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
+uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)
+
+int8        the set of all signed  8-bit integers (-128 to 127)
+int16       the set of all signed 16-bit integers (-32768 to 32767)
+int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
+int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
+
+float32     the set of all IEEE-754 32-bit floating-point numbers
+float64     the set of all IEEE-754 64-bit floating-point numbers
+
+complex64   the set of all complex numbers with float32 real and imaginary parts
+complex128  the set of all complex numbers with float64 real and imaginary parts
+
+byte        alias for uint8
+rune        alias for int32
+
+ +

+The value of an n-bit integer is n bits wide and represented using +two's complement arithmetic. +

+ +

+There is also a set of predeclared numeric types with implementation-specific sizes: +

+ +
+uint     either 32 or 64 bits
+int      same size as uint
+uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value
+
+ +

+To avoid portability issues all numeric types are defined +types and thus distinct except +byte, which is an alias for uint8, and +rune, which is an alias for int32. +Explicit conversions +are required when different numeric types are mixed in an expression +or assignment. For instance, int32 and int +are not the same type even though they may have the same size on a +particular architecture. + + +

String types

+ +

+A string type represents the set of string values. +A string value is a (possibly empty) sequence of bytes. +The number of bytes is called the length of the string and is never negative. +Strings are immutable: once created, +it is impossible to change the contents of a string. +The predeclared string type is string; +it is a defined type. +

+ +

+The length of a string s can be discovered using +the built-in function len. +The length is a compile-time constant if the string is a constant. +A string's bytes can be accessed by integer indices +0 through len(s)-1. +It is illegal to take the address of such an element; if +s[i] is the i'th byte of a +string, &s[i] is invalid. +

+ + +

Array types

+ +

+An array is a numbered sequence of elements of a single +type, called the element type. +The number of elements is called the length of the array and is never negative. +

+ +
+ArrayType   = "[" ArrayLength "]" ElementType .
+ArrayLength = Expression .
+ElementType = Type .
+
+ +

+The length is part of the array's type; it must evaluate to a +non-negative constant +representable by a value +of type int. +The length of array a can be discovered +using the built-in function len. +The elements can be addressed by integer indices +0 through len(a)-1. +Array types are always one-dimensional but may be composed to form +multi-dimensional types. +

+ +
+[32]byte
+[2*N] struct { x, y int32 }
+[1000]*float64
+[3][5]int
+[2][2][2]float64  // same as [2]([2]([2]float64))
+
+ +

Slice types

+ +

+A slice is a descriptor for a contiguous segment of an underlying array and +provides access to a numbered sequence of elements from that array. +A slice type denotes the set of all slices of arrays of its element type. +The number of elements is called the length of the slice and is never negative. +The value of an uninitialized slice is nil. +

+ +
+SliceType = "[" "]" ElementType .
+
+ +

+The length of a slice s can be discovered by the built-in function +len; unlike with arrays it may change during +execution. The elements can be addressed by integer indices +0 through len(s)-1. The slice index of a +given element may be less than the index of the same element in the +underlying array. +

+

+A slice, once initialized, is always associated with an underlying +array that holds its elements. A slice therefore shares storage +with its array and with other slices of the same array; by contrast, +distinct arrays always represent distinct storage. +

+

+The array underlying a slice may extend past the end of the slice. +The capacity is a measure of that extent: it is the sum of +the length of the slice and the length of the array beyond the slice; +a slice of length up to that capacity can be created by +slicing a new one from the original slice. +The capacity of a slice a can be discovered using the +built-in function cap(a). +

+ +

+A new, initialized slice value for a given element type T is +made using the built-in function +make, +which takes a slice type +and parameters specifying the length and optionally the capacity. +A slice created with make always allocates a new, hidden array +to which the returned slice value refers. That is, executing +

+ +
+make([]T, length, capacity)
+
+ +

+produces the same slice as allocating an array and slicing +it, so these two expressions are equivalent: +

+ +
+make([]int, 50, 100)
+new([100]int)[0:50]
+
+ +

+Like arrays, slices are always one-dimensional but may be composed to construct +higher-dimensional objects. +With arrays of arrays, the inner arrays are, by construction, always the same length; +however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. +Moreover, the inner slices must be initialized individually. +

+ +

Struct types

+ +

+A struct is a sequence of named elements, called fields, each of which has a +name and a type. Field names may be specified explicitly (IdentifierList) or +implicitly (EmbeddedField). +Within a struct, non-blank field names must +be unique. +

+ +
+StructType    = "struct" "{" { FieldDecl ";" } "}" .
+FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
+EmbeddedField = [ "*" ] TypeName .
+Tag           = string_lit .
+
+ +
+// An empty struct.
+struct {}
+
+// A struct with 6 fields.
+struct {
+	x, y int
+	u float32
+	_ float32  // padding
+	A *[]int
+	F func()
+}
+
+ +

+A field declared with a type but no explicit field name is called an embedded field. +An embedded field must be specified as +a type name T or as a pointer to a non-interface type name *T, +and T itself may not be +a pointer type. The unqualified type name acts as the field name. +

+ +
+// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
+struct {
+	T1        // field name is T1
+	*T2       // field name is T2
+	P.T3      // field name is T3
+	*P.T4     // field name is T4
+	x, y int  // field names are x and y
+}
+
+ +

+The following declaration is illegal because field names must be unique +in a struct type: +

+ +
+struct {
+	T     // conflicts with embedded field *T and *P.T
+	*T    // conflicts with embedded field T and *P.T
+	*P.T  // conflicts with embedded field T and *T
+}
+
+ +

+A field or method f of an +embedded field in a struct x is called promoted if +x.f is a legal selector that denotes +that field or method f. +

+ +

+Promoted fields act like ordinary fields +of a struct except that they cannot be used as field names in +composite literals of the struct. +

+ +

+Given a struct type S and a defined type +T, promoted methods are included in the method set of the struct as follows: +

+
    +
  • + If S contains an embedded field T, + the method sets of S + and *S both include promoted methods with receiver + T. The method set of *S also + includes promoted methods with receiver *T. +
  • + +
  • + If S contains an embedded field *T, + the method sets of S and *S both + include promoted methods with receiver T or + *T. +
  • +
+ +

+A field declaration may be followed by an optional string literal tag, +which becomes an attribute for all the fields in the corresponding +field declaration. An empty tag string is equivalent to an absent tag. +The tags are made visible through a reflection interface +and take part in type identity for structs +but are otherwise ignored. +

+ +
+struct {
+	x, y float64 ""  // an empty tag string is like an absent tag
+	name string  "any string is permitted as a tag"
+	_    [4]byte "ceci n'est pas un champ de structure"
+}
+
+// A struct corresponding to a TimeStamp protocol buffer.
+// The tag strings define the protocol buffer field numbers;
+// they follow the convention outlined by the reflect package.
+struct {
+	microsec  uint64 `protobuf:"1"`
+	serverIP6 uint64 `protobuf:"2"`
+}
+
+ +

Pointer types

+ +

+A pointer type denotes the set of all pointers to variables of a given +type, called the base type of the pointer. +The value of an uninitialized pointer is nil. +

+ +
+PointerType = "*" BaseType .
+BaseType    = Type .
+
+ +
+*Point
+*[4]int
+
+ +

Function types

+ +

+A function type denotes the set of all functions with the same parameter +and result types. The value of an uninitialized variable of function type +is nil. +

+ +
+FunctionType   = "func" Signature .
+Signature      = Parameters [ Result ] .
+Result         = Parameters | Type .
+Parameters     = "(" [ ParameterList [ "," ] ] ")" .
+ParameterList  = ParameterDecl { "," ParameterDecl } .
+ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
+
+ +

+Within a list of parameters or results, the names (IdentifierList) +must either all be present or all be absent. If present, each name +stands for one item (parameter or result) of the specified type and +all non-blank names in the signature +must be unique. +If absent, each type stands for one item of that type. +Parameter and result +lists are always parenthesized except that if there is exactly +one unnamed result it may be written as an unparenthesized type. +

+ +

+The final incoming parameter in a function signature may have +a type prefixed with .... +A function with such a parameter is called variadic and +may be invoked with zero or more arguments for that parameter. +

+ +
+func()
+func(x int) int
+func(a, _ int, z float32) bool
+func(a, b int, z float32) (bool)
+func(prefix string, values ...int)
+func(a, b int, z float64, opt ...interface{}) (success bool)
+func(int, int, float64) (float64, *[]int)
+func(n int) func(p *T)
+
+ + +

Interface types

+ +

+An interface type specifies a method set called its interface. +A variable of interface type can store a value of any type with a method set +that is any superset of the interface. Such a type is said to +implement the interface. +The value of an uninitialized variable of interface type is nil. +

+ +
+InterfaceType      = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
+MethodSpec         = MethodName Signature .
+MethodName         = identifier .
+InterfaceTypeName  = TypeName .
+
+ +

+An interface type may specify methods explicitly through method specifications, +or it may embed methods of other interfaces through interface type names. +

+ +
+// A simple File interface.
+interface {
+	Read([]byte) (int, error)
+	Write([]byte) (int, error)
+	Close() error
+}
+
+ +

+The name of each explicitly specified method must be unique +and not blank. +

+ +
+interface {
+	String() string
+	String() string  // illegal: String not unique
+	_(x int)         // illegal: method must have non-blank name
+}
+
+ +

+More than one type may implement an interface. +For instance, if two types S1 and S2 +have the method set +

+ +
+func (p T) Read(p []byte) (n int, err error)
+func (p T) Write(p []byte) (n int, err error)
+func (p T) Close() error
+
+ +

+(where T stands for either S1 or S2) +then the File interface is implemented by both S1 and +S2, regardless of what other methods +S1 and S2 may have or share. +

+ +

+A type implements any interface comprising any subset of its methods +and may therefore implement several distinct interfaces. For +instance, all types implement the empty interface: +

+ +
+interface{}
+
+ +

+Similarly, consider this interface specification, +which appears within a type declaration +to define an interface called Locker: +

+ +
+type Locker interface {
+	Lock()
+	Unlock()
+}
+
+ +

+If S1 and S2 also implement +

+ +
+func (p T) Lock() { … }
+func (p T) Unlock() { … }
+
+ +

+they implement the Locker interface as well +as the File interface. +

+ +

+An interface T may use a (possibly qualified) interface type +name E in place of a method specification. This is called +embedding interface E in T. +The method set of T is the union +of the method sets of T’s explicitly declared methods and of +T’s embedded interfaces. +

+ +
+type Reader interface {
+	Read(p []byte) (n int, err error)
+	Close() error
+}
+
+type Writer interface {
+	Write(p []byte) (n int, err error)
+	Close() error
+}
+
+// ReadWriter's methods are Read, Write, and Close.
+type ReadWriter interface {
+	Reader  // includes methods of Reader in ReadWriter's method set
+	Writer  // includes methods of Writer in ReadWriter's method set
+}
+
+ +

+A union of method sets contains the (exported and non-exported) +methods of each method set exactly once, and methods with the +same names must +have identical signatures. +

+ +
+type ReadCloser interface {
+	Reader   // includes methods of Reader in ReadCloser's method set
+	Close()  // illegal: signatures of Reader.Close and Close are different
+}
+
+ +

+An interface type T may not embed itself +or any interface type that embeds T, recursively. +

+ +
+// illegal: Bad cannot embed itself
+type Bad interface {
+	Bad
+}
+
+// illegal: Bad1 cannot embed itself using Bad2
+type Bad1 interface {
+	Bad2
+}
+type Bad2 interface {
+	Bad1
+}
+
+ +

Map types

+ +

+A map is an unordered group of elements of one type, called the +element type, indexed by a set of unique keys of another type, +called the key type. +The value of an uninitialized map is nil. +

+ +
+MapType     = "map" "[" KeyType "]" ElementType .
+KeyType     = Type .
+
+ +

+The comparison operators +== and != must be fully defined +for operands of the key type; thus the key type must not be a function, map, or +slice. +If the key type is an interface type, these +comparison operators must be defined for the dynamic key values; +failure will cause a run-time panic. + +

+ +
+map[string]int
+map[*T]struct{ x, y float64 }
+map[string]interface{}
+
+ +

+The number of map elements is called its length. +For a map m, it can be discovered using the +built-in function len +and may change during execution. Elements may be added during execution +using assignments and retrieved with +index expressions; they may be removed with the +delete built-in function. +

+

+A new, empty map value is made using the built-in +function make, +which takes the map type and an optional capacity hint as arguments: +

+ +
+make(map[string]int)
+make(map[string]int, 100)
+
+ +

+The initial capacity does not bound its size: +maps grow to accommodate the number of items +stored in them, with the exception of nil maps. +A nil map is equivalent to an empty map except that no elements +may be added. + +

Channel types

+ +

+A channel provides a mechanism for +concurrently executing functions +to communicate by +sending and +receiving +values of a specified element type. +The value of an uninitialized channel is nil. +

+ +
+ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
+
+ +

+The optional <- operator specifies the channel direction, +send or receive. If no direction is given, the channel is +bidirectional. +A channel may be constrained only to send or only to receive by +assignment or +explicit conversion. +

+ +
+chan T          // can be used to send and receive values of type T
+chan<- float64  // can only be used to send float64s
+<-chan int      // can only be used to receive ints
+
+ +

+The <- operator associates with the leftmost chan +possible: +

+ +
+chan<- chan int    // same as chan<- (chan int)
+chan<- <-chan int  // same as chan<- (<-chan int)
+<-chan <-chan int  // same as <-chan (<-chan int)
+chan (<-chan int)
+
+ +

+A new, initialized channel +value can be made using the built-in function +make, +which takes the channel type and an optional capacity as arguments: +

+ +
+make(chan int, 100)
+
+ +

+The capacity, in number of elements, sets the size of the buffer in the channel. +If the capacity is zero or absent, the channel is unbuffered and communication +succeeds only when both a sender and receiver are ready. Otherwise, the channel +is buffered and communication succeeds without blocking if the buffer +is not full (sends) or not empty (receives). +A nil channel is never ready for communication. +

+ +

+A channel may be closed with the built-in function +close. +The multi-valued assignment form of the +receive operator +reports whether a received value was sent before +the channel was closed. +

+ +

+A single channel may be used in +send statements, +receive operations, +and calls to the built-in functions +cap and +len +by any number of goroutines without further synchronization. +Channels act as first-in-first-out queues. +For example, if one goroutine sends values on a channel +and a second goroutine receives them, the values are +received in the order sent. +

+ +

Properties of types and values

+ +

Type identity

+ +

+Two types are either identical or different. +

+ +

+A defined type is always different from any other type. +Otherwise, two types are identical if their underlying type literals are +structurally equivalent; that is, they have the same literal structure and corresponding +components have identical types. In detail: +

+ +
    +
  • Two array types are identical if they have identical element types and + the same array length.
  • + +
  • Two slice types are identical if they have identical element types.
  • + +
  • Two struct types are identical if they have the same sequence of fields, + and if corresponding fields have the same names, and identical types, + and identical tags. + Non-exported field names from different + packages are always different.
  • + +
  • Two pointer types are identical if they have identical base types.
  • + +
  • Two function types are identical if they have the same number of parameters + and result values, corresponding parameter and result types are + identical, and either both functions are variadic or neither is. + Parameter and result names are not required to match.
  • + +
  • Two interface types are identical if they have the same set of methods + with the same names and identical function types. + Non-exported method names from different + packages are always different. The order of the methods is irrelevant.
  • + +
  • Two map types are identical if they have identical key and element types.
  • + +
  • Two channel types are identical if they have identical element types and + the same direction.
  • +
+ +

+Given the declarations +

+ +
+type (
+	A0 = []string
+	A1 = A0
+	A2 = struct{ a, b int }
+	A3 = int
+	A4 = func(A3, float64) *A0
+	A5 = func(x int, _ float64) *[]string
+)
+
+type (
+	B0 A0
+	B1 []string
+	B2 struct{ a, b int }
+	B3 struct{ a, c int }
+	B4 func(int, float64) *B0
+	B5 func(x int, y float64) *A1
+)
+
+type	C0 = B0
+
+ +

+these types are identical: +

+ +
+A0, A1, and []string
+A2 and struct{ a, b int }
+A3 and int
+A4, func(int, float64) *[]string, and A5
+
+B0 and C0
+[]int and []int
+struct{ a, b *T5 } and struct{ a, b *T5 }
+func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
+
+ +

+B0 and B1 are different because they are new types +created by distinct type definitions; +func(int, float64) *B0 and func(x int, y float64) *[]string +are different because B0 is different from []string. +

+ + +

Assignability

+ +

+A value x is assignable to a variable of type T +("x is assignable to T") if one of the following conditions applies: +

+ +
    +
  • +x's type is identical to T. +
  • +
  • +x's type V and T have identical +underlying types and at least one of V +or T is not a defined type. +
  • +
  • +T is an interface type and +x implements T. +
  • +
  • +x is a bidirectional channel value, T is a channel type, +x's type V and T have identical element types, +and at least one of V or T is not a defined type. +
  • +
  • +x is the predeclared identifier nil and T +is a pointer, function, slice, map, channel, or interface type. +
  • +
  • +x is an untyped constant +representable +by a value of type T. +
  • +
+ + +

Representability

+ +

+A constant x is representable +by a value of type T if one of the following conditions applies: +

+ +
    +
  • +x is in the set of values determined by T. +
  • + +
  • +T is a floating-point type and x can be rounded to T's +precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE +negative zero further simplified to an unsigned zero. Note that constant values never result +in an IEEE negative zero, NaN, or infinity. +
  • + +
  • +T is a complex type, and x's +components real(x) and imag(x) +are representable by values of T's component type (float32 or +float64). +
  • +
+ +
+x                   T           x is representable by a value of T because
+
+'a'                 byte        97 is in the set of byte values
+97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers
+"foo"               string      "foo" is in the set of string values
+1024                int16       1024 is in the set of 16-bit integers
+42.0                byte        42 is in the set of unsigned 8-bit integers
+1e10                uint64      10000000000 is in the set of unsigned 64-bit integers
+2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
+-1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
+0i                  int         0 is an integer value
+(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values
+
+ +
+x                   T           x is not representable by a value of T because
+
+0                   bool        0 is not in the set of boolean values
+'a'                 string      'a' is a rune, it is not in the set of string values
+1024                byte        1024 is not in the set of unsigned 8-bit integers
+-1                  uint16      -1 is not in the set of unsigned 16-bit integers
+1.1                 int         1.1 is not an integer value
+42i                 float32     (0 + 42i) is not in the set of float32 values
+1e1000              float64     1e1000 overflows to IEEE +Inf after rounding
+
+ + +

Blocks

+ +

+A block is a possibly empty sequence of declarations and statements +within matching brace brackets. +

+ +
+Block = "{" StatementList "}" .
+StatementList = { Statement ";" } .
+
+ +

+In addition to explicit blocks in the source code, there are implicit blocks: +

+ +
    +
  1. The universe block encompasses all Go source text.
  2. + +
  3. Each package has a package block containing all + Go source text for that package.
  4. + +
  5. Each file has a file block containing all Go source text + in that file.
  6. + +
  7. Each "if", + "for", and + "switch" + statement is considered to be in its own implicit block.
  8. + +
  9. Each clause in a "switch" + or "select" statement + acts as an implicit block.
  10. +
+ +

+Blocks nest and influence scoping. +

+ + +

Declarations and scope

+ +

+A declaration binds a non-blank identifier to a +constant, +type, +variable, +function, +label, or +package. +Every identifier in a program must be declared. +No identifier may be declared twice in the same block, and +no identifier may be declared in both the file and package block. +

+ +

+The blank identifier may be used like any other identifier +in a declaration, but it does not introduce a binding and thus is not declared. +In the package block, the identifier init may only be used for +init function declarations, +and like the blank identifier it does not introduce a new binding. +

+ +
+Declaration   = ConstDecl | TypeDecl | VarDecl .
+TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .
+
+ +

+The scope of a declared identifier is the extent of source text in which +the identifier denotes the specified constant, type, variable, function, label, or package. +

+ +

+Go is lexically scoped using blocks: +

+ +
    +
  1. The scope of a predeclared identifier is the universe block.
  2. + +
  3. The scope of an identifier denoting a constant, type, variable, + or function (but not method) declared at top level (outside any + function) is the package block.
  4. + +
  5. The scope of the package name of an imported package is the file block + of the file containing the import declaration.
  6. + +
  7. The scope of an identifier denoting a method receiver, function parameter, + or result variable is the function body.
  8. + +
  9. The scope of a constant or variable identifier declared + inside a function begins at the end of the ConstSpec or VarSpec + (ShortVarDecl for short variable declarations) + and ends at the end of the innermost containing block.
  10. + +
  11. The scope of a type identifier declared inside a function + begins at the identifier in the TypeSpec + and ends at the end of the innermost containing block.
  12. +
+ +

+An identifier declared in a block may be redeclared in an inner block. +While the identifier of the inner declaration is in scope, it denotes +the entity declared by the inner declaration. +

+ +

+The package clause is not a declaration; the package name +does not appear in any scope. Its purpose is to identify the files belonging +to the same package and to specify the default package name for import +declarations. +

+ + +

Label scopes

+ +

+Labels are declared by labeled statements and are +used in the "break", +"continue", and +"goto" statements. +It is illegal to define a label that is never used. +In contrast to other identifiers, labels are not block scoped and do +not conflict with identifiers that are not labels. The scope of a label +is the body of the function in which it is declared and excludes +the body of any nested function. +

+ + +

Blank identifier

+ +

+The blank identifier is represented by the underscore character _. +It serves as an anonymous placeholder instead of a regular (non-blank) +identifier and has special meaning in declarations, +as an operand, and in assignments. +

+ + +

Predeclared identifiers

+ +

+The following identifiers are implicitly declared in the +universe block: +

+
+Types:
+	bool byte complex64 complex128 error float32 float64
+	int int8 int16 int32 int64 rune string
+	uint uint8 uint16 uint32 uint64 uintptr
+
+Constants:
+	true false iota
+
+Zero value:
+	nil
+
+Functions:
+	append cap close complex copy delete imag len
+	make new panic print println real recover
+
+ + +

Exported identifiers

+ +

+An identifier may be exported to permit access to it from another package. +An identifier is exported if both: +

+
    +
  1. the first character of the identifier's name is a Unicode upper case + letter (Unicode class "Lu"); and
  2. +
  3. the identifier is declared in the package block + or it is a field name or + method name.
  4. +
+

+All other identifiers are not exported. +

+ + +

Uniqueness of identifiers

+ +

+Given a set of identifiers, an identifier is called unique if it is +different from every other in the set. +Two identifiers are different if they are spelled differently, or if they +appear in different packages and are not +exported. Otherwise, they are the same. +

+ +

Constant declarations

+ +

+A constant declaration binds a list of identifiers (the names of +the constants) to the values of a list of constant expressions. +The number of identifiers must be equal +to the number of expressions, and the nth identifier on +the left is bound to the value of the nth expression on the +right. +

+ +
+ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
+ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
+
+IdentifierList = identifier { "," identifier } .
+ExpressionList = Expression { "," Expression } .
+
+ +

+If the type is present, all constants take the type specified, and +the expressions must be assignable to that type. +If the type is omitted, the constants take the +individual types of the corresponding expressions. +If the expression values are untyped constants, +the declared constants remain untyped and the constant identifiers +denote the constant values. For instance, if the expression is a +floating-point literal, the constant identifier denotes a floating-point +constant, even if the literal's fractional part is zero. +

+ +
+const Pi float64 = 3.14159265358979323846
+const zero = 0.0         // untyped floating-point constant
+const (
+	size int64 = 1024
+	eof        = -1  // untyped integer constant
+)
+const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", untyped integer and string constants
+const u, v float32 = 0, 3    // u = 0.0, v = 3.0
+
+ +

+Within a parenthesized const declaration list the +expression list may be omitted from any but the first ConstSpec. +Such an empty list is equivalent to the textual substitution of the +first preceding non-empty expression list and its type if any. +Omitting the list of expressions is therefore equivalent to +repeating the previous list. The number of identifiers must be equal +to the number of expressions in the previous list. +Together with the iota constant generator +this mechanism permits light-weight declaration of sequential values: +

+ +
+const (
+	Sunday = iota
+	Monday
+	Tuesday
+	Wednesday
+	Thursday
+	Friday
+	Partyday
+	numberOfDays  // this constant is not exported
+)
+
+ + +

Iota

+ +

+Within a constant declaration, the predeclared identifier +iota represents successive untyped integer +constants. Its value is the index of the respective ConstSpec +in that constant declaration, starting at zero. +It can be used to construct a set of related constants: +

+ +
+const (
+	c0 = iota  // c0 == 0
+	c1 = iota  // c1 == 1
+	c2 = iota  // c2 == 2
+)
+
+const (
+	a = 1 << iota  // a == 1  (iota == 0)
+	b = 1 << iota  // b == 2  (iota == 1)
+	c = 3          // c == 3  (iota == 2, unused)
+	d = 1 << iota  // d == 8  (iota == 3)
+)
+
+const (
+	u         = iota * 42  // u == 0     (untyped integer constant)
+	v float64 = iota * 42  // v == 42.0  (float64 constant)
+	w         = iota * 42  // w == 84    (untyped integer constant)
+)
+
+const x = iota  // x == 0
+const y = iota  // y == 0
+
+ +

+By definition, multiple uses of iota in the same ConstSpec all have the same value: +

+ +
+const (
+	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
+	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
+	_, _                                  //                        (iota == 2, unused)
+	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
+)
+
+ +

+This last example exploits the implicit repetition +of the last non-empty expression list. +

+ + +

Type declarations

+ +

+A type declaration binds an identifier, the type name, to a type. +Type declarations come in two forms: alias declarations and type definitions. +

+ +
+TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
+TypeSpec = AliasDecl | TypeDef .
+
+ +

Alias declarations

+ +

+An alias declaration binds an identifier to the given type. +

+ +
+AliasDecl = identifier "=" Type .
+
+ +

+Within the scope of +the identifier, it serves as an alias for the type. +

+ +
+type (
+	nodeList = []*Node  // nodeList and []*Node are identical types
+	Polar    = polar    // Polar and polar denote identical types
+)
+
+ + +

Type definitions

+ +

+A type definition creates a new, distinct type with the same +underlying type and operations as the given type, +and binds an identifier to it. +

+ +
+TypeDef = identifier Type .
+
+ +

+The new type is called a defined type. +It is different from any other type, +including the type it is created from. +

+ +
+type (
+	Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types
+	polar Point                   // polar and Point denote different types
+)
+
+type TreeNode struct {
+	left, right *TreeNode
+	value *Comparable
+}
+
+type Block interface {
+	BlockSize() int
+	Encrypt(src, dst []byte)
+	Decrypt(src, dst []byte)
+}
+
+ +

+A defined type may have methods associated with it. +It does not inherit any methods bound to the given type, +but the method set +of an interface type or of elements of a composite type remains unchanged: +

+ +
+// A Mutex is a data type with two methods, Lock and Unlock.
+type Mutex struct         { /* Mutex fields */ }
+func (m *Mutex) Lock()    { /* Lock implementation */ }
+func (m *Mutex) Unlock()  { /* Unlock implementation */ }
+
+// NewMutex has the same composition as Mutex but its method set is empty.
+type NewMutex Mutex
+
+// The method set of PtrMutex's underlying type *Mutex remains unchanged,
+// but the method set of PtrMutex is empty.
+type PtrMutex *Mutex
+
+// The method set of *PrintableMutex contains the methods
+// Lock and Unlock bound to its embedded field Mutex.
+type PrintableMutex struct {
+	Mutex
+}
+
+// MyBlock is an interface type that has the same method set as Block.
+type MyBlock Block
+
+ +

+Type definitions may be used to define different boolean, numeric, +or string types and associate methods with them: +

+ +
+type TimeZone int
+
+const (
+	EST TimeZone = -(5 + iota)
+	CST
+	MST
+	PST
+)
+
+func (tz TimeZone) String() string {
+	return fmt.Sprintf("GMT%+dh", tz)
+}
+
+ + +

Variable declarations

+ +

+A variable declaration creates one or more variables, +binds corresponding identifiers to them, and gives each a type and an initial value. +

+ +
+VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
+VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
+
+ +
+var i int
+var U, V, W float64
+var k = 0
+var x, y float32 = -1, -2
+var (
+	i       int
+	u, v, s = 2.0, 3.0, "bar"
+)
+var re, im = complexSqrt(-1)
+var _, found = entries[name]  // map lookup; only interested in "found"
+
+ +

+If a list of expressions is given, the variables are initialized +with the expressions following the rules for assignments. +Otherwise, each variable is initialized to its zero value. +

+ +

+If a type is present, each variable is given that type. +Otherwise, each variable is given the type of the corresponding +initialization value in the assignment. +If that value is an untyped constant, it is first implicitly +converted to its default type; +if it is an untyped boolean value, it is first implicitly converted to type bool. +The predeclared value nil cannot be used to initialize a variable +with no explicit type. +

+ +
+var d = math.Sin(0.5)  // d is float64
+var i = 42             // i is int
+var t, ok = x.(T)      // t is T, ok is bool
+var n = nil            // illegal
+
+ +

+Implementation restriction: A compiler may make it illegal to declare a variable +inside a function body if the variable is +never used. +

+ +

Short variable declarations

+ +

+A short variable declaration uses the syntax: +

+ +
+ShortVarDecl = IdentifierList ":=" ExpressionList .
+
+ +

+It is shorthand for a regular variable declaration +with initializer expressions but no types: +

+ +
+"var" IdentifierList = ExpressionList .
+
+ +
+i, j := 0, 10
+f := func() int { return 7 }
+ch := make(chan int)
+r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
+_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate
+
+ +

+Unlike regular variable declarations, a short variable declaration may redeclare +variables provided they were originally declared earlier in the same block +(or the parameter lists if the block is the function body) with the same type, +and at least one of the non-blank variables is new. +As a consequence, redeclaration can only appear in a multi-variable short declaration. +Redeclaration does not introduce a new variable; it just assigns a new value to the original. +

+ +
+field1, offset := nextField(str, 0)
+field2, offset := nextField(str, offset)  // redeclares offset
+a, a := 1, 2                              // illegal: double declaration of a or no new variable if a was declared elsewhere
+
+ +

+Short variable declarations may appear only inside functions. +In some contexts such as the initializers for +"if", +"for", or +"switch" statements, +they can be used to declare local temporary variables. +

+ +

Function declarations

+ +

+A function declaration binds an identifier, the function name, +to a function. +

+ +
+FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
+FunctionName = identifier .
+FunctionBody = Block .
+
+ +

+If the function's signature declares +result parameters, the function body's statement list must end in +a terminating statement. +

+ +
+func IndexRune(s string, r rune) int {
+	for i, c := range s {
+		if c == r {
+			return i
+		}
+	}
+	// invalid: missing return statement
+}
+
+ +

+A function declaration may omit the body. Such a declaration provides the +signature for a function implemented outside Go, such as an assembly routine. +

+ +
+func min(x int, y int) int {
+	if x < y {
+		return x
+	}
+	return y
+}
+
+func flushICache(begin, end uintptr)  // implemented externally
+
+ +

Method declarations

+ +

+A method is a function with a receiver. +A method declaration binds an identifier, the method name, to a method, +and associates the method with the receiver's base type. +

+ +
+MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
+Receiver   = Parameters .
+
+ +

+The receiver is specified via an extra parameter section preceding the method +name. That parameter section must declare a single non-variadic parameter, the receiver. +Its type must be a defined type T or a +pointer to a defined type T. T is called the receiver +base type. A receiver base type cannot be a pointer or interface type and +it must be defined in the same package as the method. +The method is said to be bound to its receiver base type and the method name +is visible only within selectors for type T +or *T. +

+ +

+A non-blank receiver identifier must be +unique in the method signature. +If the receiver's value is not referenced inside the body of the method, +its identifier may be omitted in the declaration. The same applies in +general to parameters of functions and methods. +

+ +

+For a base type, the non-blank names of methods bound to it must be unique. +If the base type is a struct type, +the non-blank method and field names must be distinct. +

+ +

+Given defined type Point, the declarations +

+ +
+func (p *Point) Length() float64 {
+	return math.Sqrt(p.x * p.x + p.y * p.y)
+}
+
+func (p *Point) Scale(factor float64) {
+	p.x *= factor
+	p.y *= factor
+}
+
+ +

+bind the methods Length and Scale, +with receiver type *Point, +to the base type Point. +

+ +

+The type of a method is the type of a function with the receiver as first +argument. For instance, the method Scale has type +

+ +
+func(p *Point, factor float64)
+
+ +

+However, a function declared this way is not a method. +

+ + +

Expressions

+ +

+An expression specifies the computation of a value by applying +operators and functions to operands. +

+ +

Operands

+ +

+Operands denote the elementary values in an expression. An operand may be a +literal, a (possibly qualified) +non-blank identifier denoting a +constant, +variable, or +function, +or a parenthesized expression. +

+ +

+The blank identifier may appear as an +operand only on the left-hand side of an assignment. +

+ +
+Operand     = Literal | OperandName | "(" Expression ")" .
+Literal     = BasicLit | CompositeLit | FunctionLit .
+BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
+OperandName = identifier | QualifiedIdent .
+
+ +

Qualified identifiers

+ +

+A qualified identifier is an identifier qualified with a package name prefix. +Both the package name and the identifier must not be +blank. +

+ +
+QualifiedIdent = PackageName "." identifier .
+
+ +

+A qualified identifier accesses an identifier in a different package, which +must be imported. +The identifier must be exported and +declared in the package block of that package. +

+ +
+math.Sin	// denotes the Sin function in package math
+
+ +

Composite literals

+ +

+Composite literals construct values for structs, arrays, slices, and maps +and create a new value each time they are evaluated. +They consist of the type of the literal followed by a brace-bound list of elements. +Each element may optionally be preceded by a corresponding key. +

+ +
+CompositeLit  = LiteralType LiteralValue .
+LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
+                SliceType | MapType | TypeName .
+LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
+ElementList   = KeyedElement { "," KeyedElement } .
+KeyedElement  = [ Key ":" ] Element .
+Key           = FieldName | Expression | LiteralValue .
+FieldName     = identifier .
+Element       = Expression | LiteralValue .
+
+ +

+The LiteralType's underlying type must be a struct, array, slice, or map type +(the grammar enforces this constraint except when the type is given +as a TypeName). +The types of the elements and keys must be assignable +to the respective field, element, and key types of the literal type; +there is no additional conversion. +The key is interpreted as a field name for struct literals, +an index for array and slice literals, and a key for map literals. +For map literals, all elements must have a key. It is an error +to specify multiple elements with the same field name or +constant key value. For non-constant map keys, see the section on +evaluation order. +

+ +

+For struct literals the following rules apply: +

+
    +
  • A key must be a field name declared in the struct type. +
  • +
  • An element list that does not contain any keys must + list an element for each struct field in the + order in which the fields are declared. +
  • +
  • If any element has a key, every element must have a key. +
  • +
  • An element list that contains keys does not need to + have an element for each struct field. Omitted fields + get the zero value for that field. +
  • +
  • A literal may omit the element list; such a literal evaluates + to the zero value for its type. +
  • +
  • It is an error to specify an element for a non-exported + field of a struct belonging to a different package. +
  • +
+ +

+Given the declarations +

+
+type Point3D struct { x, y, z float64 }
+type Line struct { p, q Point3D }
+
+ +

+one may write +

+ +
+origin := Point3D{}                            // zero value for Point3D
+line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x
+
+ +

+For array and slice literals the following rules apply: +

+
    +
  • Each element has an associated integer index marking + its position in the array. +
  • +
  • An element with a key uses the key as its index. The + key must be a non-negative constant + representable by + a value of type int; and if it is typed + it must be of integer type. +
  • +
  • An element without a key uses the previous element's index plus one. + If the first element has no key, its index is zero. +
  • +
+ +

+Taking the address of a composite literal +generates a pointer to a unique variable initialized +with the literal's value. +

+ +
+var pointer *Point3D = &Point3D{y: 1000}
+
+ +

+Note that the zero value for a slice or map +type is not the same as an initialized but empty value of the same type. +Consequently, taking the address of an empty slice or map composite literal +does not have the same effect as allocating a new slice or map value with +new. +

+ +
+p1 := &[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0
+p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0
+
+ +

+The length of an array literal is the length specified in the literal type. +If fewer elements than the length are provided in the literal, the missing +elements are set to the zero value for the array element type. +It is an error to provide elements with index values outside the index range +of the array. The notation ... specifies an array length equal +to the maximum element index plus one. +

+ +
+buffer := [10]string{}             // len(buffer) == 10
+intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
+days := [...]string{"Sat", "Sun"}  // len(days) == 2
+
+ +

+A slice literal describes the entire underlying array literal. +Thus the length and capacity of a slice literal are the maximum +element index plus one. A slice literal has the form +

+ +
+[]T{x1, x2, … xn}
+
+ +

+and is shorthand for a slice operation applied to an array: +

+ +
+tmp := [n]T{x1, x2, … xn}
+tmp[0 : n]
+
+ +

+Within a composite literal of array, slice, or map type T, +elements or map keys that are themselves composite literals may elide the respective +literal type if it is identical to the element or key type of T. +Similarly, elements or keys that are addresses of composite literals may elide +the &T when the element or key type is *T. +

+ +
+[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
+[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
+[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
+map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
+map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}
+
+type PPoint *Point
+[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
+[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
+
+ +

+A parsing ambiguity arises when a composite literal using the +TypeName form of the LiteralType appears as an operand between the +keyword and the opening brace of the block +of an "if", "for", or "switch" statement, and the composite literal +is not enclosed in parentheses, square brackets, or curly braces. +In this rare case, the opening brace of the literal is erroneously parsed +as the one introducing the block of statements. To resolve the ambiguity, +the composite literal must appear within parentheses. +

+ +
+if x == (T{a,b,c}[i]) { … }
+if (x == T{a,b,c}[i]) { … }
+
+ +

+Examples of valid array, slice, and map literals: +

+ +
+// list of prime numbers
+primes := []int{2, 3, 5, 7, 9, 2147483647}
+
+// vowels[ch] is true if ch is a vowel
+vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
+
+// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
+filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
+
+// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
+noteFrequency := map[string]float32{
+	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
+	"G0": 24.50, "A0": 27.50, "B0": 30.87,
+}
+
+ + +

Function literals

+ +

+A function literal represents an anonymous function. +

+ +
+FunctionLit = "func" Signature FunctionBody .
+
+ +
+func(a, b int, z float64) bool { return a*b < int(z) }
+
+ +

+A function literal can be assigned to a variable or invoked directly. +

+ +
+f := func(x, y int) int { return x + y }
+func(ch chan int) { ch <- ACK }(replyChan)
+
+ +

+Function literals are closures: they may refer to variables +defined in a surrounding function. Those variables are then shared between +the surrounding function and the function literal, and they survive as long +as they are accessible. +

+ + +

Primary expressions

+ +

+Primary expressions are the operands for unary and binary expressions. +

+ +
+PrimaryExpr =
+	Operand |
+	Conversion |
+	MethodExpr |
+	PrimaryExpr Selector |
+	PrimaryExpr Index |
+	PrimaryExpr Slice |
+	PrimaryExpr TypeAssertion |
+	PrimaryExpr Arguments .
+
+Selector       = "." identifier .
+Index          = "[" Expression "]" .
+Slice          = "[" [ Expression ] ":" [ Expression ] "]" |
+                 "[" [ Expression ] ":" Expression ":" Expression "]" .
+TypeAssertion  = "." "(" Type ")" .
+Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
+
+ + +
+x
+2
+(s + ".txt")
+f(3.1415, true)
+Point{1, 2}
+m["foo"]
+s[i : j + 1]
+obj.color
+f.p[i].x()
+
+ + +

Selectors

+ +

+For a primary expression x +that is not a package name, the +selector expression +

+ +
+x.f
+
+ +

+denotes the field or method f of the value x +(or sometimes *x; see below). +The identifier f is called the (field or method) selector; +it must not be the blank identifier. +The type of the selector expression is the type of f. +If x is a package name, see the section on +qualified identifiers. +

+ +

+A selector f may denote a field or method f of +a type T, or it may refer +to a field or method f of a nested +embedded field of T. +The number of embedded fields traversed +to reach f is called its depth in T. +The depth of a field or method f +declared in T is zero. +The depth of a field or method f declared in +an embedded field A in T is the +depth of f in A plus one. +

+ +

+The following rules apply to selectors: +

+ +
    +
  1. +For a value x of type T or *T +where T is not a pointer or interface type, +x.f denotes the field or method at the shallowest depth +in T where there +is such an f. +If there is not exactly one f +with shallowest depth, the selector expression is illegal. +
  2. + +
  3. +For a value x of type I where I +is an interface type, x.f denotes the actual method with name +f of the dynamic value of x. +If there is no method with name f in the +method set of I, the selector +expression is illegal. +
  4. + +
  5. +As an exception, if the type of x is a defined +pointer type and (*x).f is a valid selector expression denoting a field +(but not a method), x.f is shorthand for (*x).f. +
  6. + +
  7. +In all other cases, x.f is illegal. +
  8. + +
  9. +If x is of pointer type and has the value +nil and x.f denotes a struct field, +assigning to or evaluating x.f +causes a run-time panic. +
  10. + +
  11. +If x is of interface type and has the value +nil, calling or +evaluating the method x.f +causes a run-time panic. +
  12. +
+ +

+For example, given the declarations: +

+ +
+type T0 struct {
+	x int
+}
+
+func (*T0) M0()
+
+type T1 struct {
+	y int
+}
+
+func (T1) M1()
+
+type T2 struct {
+	z int
+	T1
+	*T0
+}
+
+func (*T2) M2()
+
+type Q *T2
+
+var t T2     // with t.T0 != nil
+var p *T2    // with p != nil and (*p).T0 != nil
+var q Q = p
+
+ +

+one may write: +

+ +
+t.z          // t.z
+t.y          // t.T1.y
+t.x          // (*t.T0).x
+
+p.z          // (*p).z
+p.y          // (*p).T1.y
+p.x          // (*(*p).T0).x
+
+q.x          // (*(*q).T0).x        (*q).x is a valid field selector
+
+p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
+p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
+p.M2()       // p.M2()              M2 expects *T2 receiver
+t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls
+
+ +

+but the following is invalid: +

+ +
+q.M0()       // (*q).M0 is valid but not a field selector
+
+ + +

Method expressions

+ +

+If M is in the method set of type T, +T.M is a function that is callable as a regular function +with the same arguments as M prefixed by an additional +argument that is the receiver of the method. +

+ +
+MethodExpr    = ReceiverType "." MethodName .
+ReceiverType  = Type .
+
+ +

+Consider a struct type T with two methods, +Mv, whose receiver is of type T, and +Mp, whose receiver is of type *T. +

+ +
+type T struct {
+	a int
+}
+func (tv  T) Mv(a int) int         { return 0 }  // value receiver
+func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver
+
+var t T
+
+ +

+The expression +

+ +
+T.Mv
+
+ +

+yields a function equivalent to Mv but +with an explicit receiver as its first argument; it has signature +

+ +
+func(tv T, a int) int
+
+ +

+That function may be called normally with an explicit receiver, so +these five invocations are equivalent: +

+ +
+t.Mv(7)
+T.Mv(t, 7)
+(T).Mv(t, 7)
+f1 := T.Mv; f1(t, 7)
+f2 := (T).Mv; f2(t, 7)
+
+ +

+Similarly, the expression +

+ +
+(*T).Mp
+
+ +

+yields a function value representing Mp with signature +

+ +
+func(tp *T, f float32) float32
+
+ +

+For a method with a value receiver, one can derive a function +with an explicit pointer receiver, so +

+ +
+(*T).Mv
+
+ +

+yields a function value representing Mv with signature +

+ +
+func(tv *T, a int) int
+
+ +

+Such a function indirects through the receiver to create a value +to pass as the receiver to the underlying method; +the method does not overwrite the value whose address is passed in +the function call. +

+ +

+The final case, a value-receiver function for a pointer-receiver method, +is illegal because pointer-receiver methods are not in the method set +of the value type. +

+ +

+Function values derived from methods are called with function call syntax; +the receiver is provided as the first argument to the call. +That is, given f := T.Mv, f is invoked +as f(t, 7) not t.f(7). +To construct a function that binds the receiver, use a +function literal or +method value. +

+ +

+It is legal to derive a function value from a method of an interface type. +The resulting function takes an explicit receiver of that interface type. +

+ +

Method values

+ +

+If the expression x has static type T and +M is in the method set of type T, +x.M is called a method value. +The method value x.M is a function value that is callable +with the same arguments as a method call of x.M. +The expression x is evaluated and saved during the evaluation of the +method value; the saved copy is then used as the receiver in any calls, +which may be executed later. +

+ +
+type S struct { *T }
+type T int
+func (t T) M() { print(t) }
+
+t := new(T)
+s := S{T: t}
+f := t.M                    // receiver *t is evaluated and stored in f
+g := s.M                    // receiver *(s.T) is evaluated and stored in g
+*t = 42                     // does not affect stored receivers in f and g
+
+ +

+The type T may be an interface or non-interface type. +

+ +

+As in the discussion of method expressions above, +consider a struct type T with two methods, +Mv, whose receiver is of type T, and +Mp, whose receiver is of type *T. +

+ +
+type T struct {
+	a int
+}
+func (tv  T) Mv(a int) int         { return 0 }  // value receiver
+func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver
+
+var t T
+var pt *T
+func makeT() T
+
+ +

+The expression +

+ +
+t.Mv
+
+ +

+yields a function value of type +

+ +
+func(int) int
+
+ +

+These two invocations are equivalent: +

+ +
+t.Mv(7)
+f := t.Mv; f(7)
+
+ +

+Similarly, the expression +

+ +
+pt.Mp
+
+ +

+yields a function value of type +

+ +
+func(float32) float32
+
+ +

+As with selectors, a reference to a non-interface method with a value receiver +using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv. +

+ +

+As with method calls, a reference to a non-interface method with a pointer receiver +using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp. +

+ +
+f := t.Mv; f(7)   // like t.Mv(7)
+f := pt.Mp; f(7)  // like pt.Mp(7)
+f := pt.Mv; f(7)  // like (*pt).Mv(7)
+f := t.Mp; f(7)   // like (&t).Mp(7)
+f := makeT().Mp   // invalid: result of makeT() is not addressable
+
+ +

+Although the examples above use non-interface types, it is also legal to create a method value +from a value of interface type. +

+ +
+var i interface { M(int) } = myVal
+f := i.M; f(7)  // like i.M(7)
+
+ + +

Index expressions

+ +

+A primary expression of the form +

+ +
+a[x]
+
+ +

+denotes the element of the array, pointer to array, slice, string or map a indexed by x. +The value x is called the index or map key, respectively. +The following rules apply: +

+ +

+If a is not a map: +

+
    +
  • the index x must be of integer type or an untyped constant
  • +
  • a constant index must be non-negative and + representable by a value of type int
  • +
  • a constant index that is untyped is given type int
  • +
  • the index x is in range if 0 <= x < len(a), + otherwise it is out of range
  • +
+ +

+For a of array type A: +

+
    +
  • a constant index must be in range
  • +
  • if x is out of range at run time, + a run-time panic occurs
  • +
  • a[x] is the array element at index x and the type of + a[x] is the element type of A
  • +
+ +

+For a of pointer to array type: +

+
    +
  • a[x] is shorthand for (*a)[x]
  • +
+ +

+For a of slice type S: +

+
    +
  • if x is out of range at run time, + a run-time panic occurs
  • +
  • a[x] is the slice element at index x and the type of + a[x] is the element type of S
  • +
+ +

+For a of string type: +

+
    +
  • a constant index must be in range + if the string a is also constant
  • +
  • if x is out of range at run time, + a run-time panic occurs
  • +
  • a[x] is the non-constant byte value at index x and the type of + a[x] is byte
  • +
  • a[x] may not be assigned to
  • +
+ +

+For a of map type M: +

+
    +
  • x's type must be + assignable + to the key type of M
  • +
  • if the map contains an entry with key x, + a[x] is the map element with key x + and the type of a[x] is the element type of M
  • +
  • if the map is nil or does not contain such an entry, + a[x] is the zero value + for the element type of M
  • +
+ +

+Otherwise a[x] is illegal. +

+ +

+An index expression on a map a of type map[K]V +used in an assignment or initialization of the special form +

+ +
+v, ok = a[x]
+v, ok := a[x]
+var v, ok = a[x]
+
+ +

+yields an additional untyped boolean value. The value of ok is +true if the key x is present in the map, and +false otherwise. +

+ +

+Assigning to an element of a nil map causes a +run-time panic. +

+ + +

Slice expressions

+ +

+Slice expressions construct a substring or slice from a string, array, pointer +to array, or slice. There are two variants: a simple form that specifies a low +and high bound, and a full form that also specifies a bound on the capacity. +

+ +

Simple slice expressions

+ +

+For a string, array, pointer to array, or slice a, the primary expression +

+ +
+a[low : high]
+
+ +

+constructs a substring or slice. The indices low and +high select which elements of operand a appear +in the result. The result has indices starting at 0 and length equal to +high - low. +After slicing the array a +

+ +
+a := [5]int{1, 2, 3, 4, 5}
+s := a[1:4]
+
+ +

+the slice s has type []int, length 3, capacity 4, and elements +

+ +
+s[0] == 2
+s[1] == 3
+s[2] == 4
+
+ +

+For convenience, any of the indices may be omitted. A missing low +index defaults to zero; a missing high index defaults to the length of the +sliced operand: +

+ +
+a[2:]  // same as a[2 : len(a)]
+a[:3]  // same as a[0 : 3]
+a[:]   // same as a[0 : len(a)]
+
+ +

+If a is a pointer to an array, a[low : high] is shorthand for +(*a)[low : high]. +

+ +

+For arrays or strings, the indices are in range if +0 <= low <= high <= len(a), +otherwise they are out of range. +For slices, the upper index bound is the slice capacity cap(a) rather than the length. +A constant index must be non-negative and +representable by a value of type +int; for arrays or constant strings, constant indices must also be in range. +If both indices are constant, they must satisfy low <= high. +If the indices are out of range at run time, a run-time panic occurs. +

+ +

+Except for untyped strings, if the sliced operand is a string or slice, +the result of the slice operation is a non-constant value of the same type as the operand. +For untyped string operands the result is a non-constant value of type string. +If the sliced operand is an array, it must be addressable +and the result of the slice operation is a slice with the same element type as the array. +

+ +

+If the sliced operand of a valid slice expression is a nil slice, the result +is a nil slice. Otherwise, if the result is a slice, it shares its underlying +array with the operand. +

+ +
+var a [10]int
+s1 := a[3:7]   // underlying array of s1 is array a; &s1[2] == &a[5]
+s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
+s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element
+
+ + +

Full slice expressions

+ +

+For an array, pointer to array, or slice a (but not a string), the primary expression +

+ +
+a[low : high : max]
+
+ +

+constructs a slice of the same type, and with the same length and elements as the simple slice +expression a[low : high]. Additionally, it controls the resulting slice's capacity +by setting it to max - low. Only the first index may be omitted; it defaults to 0. +After slicing the array a +

+ +
+a := [5]int{1, 2, 3, 4, 5}
+t := a[1:3:5]
+
+ +

+the slice t has type []int, length 2, capacity 4, and elements +

+ +
+t[0] == 2
+t[1] == 3
+
+ +

+As for simple slice expressions, if a is a pointer to an array, +a[low : high : max] is shorthand for (*a)[low : high : max]. +If the sliced operand is an array, it must be addressable. +

+ +

+The indices are in range if 0 <= low <= high <= max <= cap(a), +otherwise they are out of range. +A constant index must be non-negative and +representable by a value of type +int; for arrays, constant indices must also be in range. +If multiple indices are constant, the constants that are present must be in range relative to each +other. +If the indices are out of range at run time, a run-time panic occurs. +

+ +

Type assertions

+ +

+For an expression x of interface type +and a type T, the primary expression +

+ +
+x.(T)
+
+ +

+asserts that x is not nil +and that the value stored in x is of type T. +The notation x.(T) is called a type assertion. +

+

+More precisely, if T is not an interface type, x.(T) asserts +that the dynamic type of x is identical +to the type T. +In this case, T must implement the (interface) type of x; +otherwise the type assertion is invalid since it is not possible for x +to store a value of type T. +If T is an interface type, x.(T) asserts that the dynamic type +of x implements the interface T. +

+

+If the type assertion holds, the value of the expression is the value +stored in x and its type is T. If the type assertion is false, +a run-time panic occurs. +In other words, even though the dynamic type of x +is known only at run time, the type of x.(T) is +known to be T in a correct program. +

+ +
+var x interface{} = 7          // x has dynamic type int and value 7
+i := x.(int)                   // i has type int and value 7
+
+type I interface { m() }
+
+func f(y I) {
+	s := y.(string)        // illegal: string does not implement I (missing method m)
+	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
+	…
+}
+
+ +

+A type assertion used in an assignment or initialization of the special form +

+ +
+v, ok = x.(T)
+v, ok := x.(T)
+var v, ok = x.(T)
+var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
+
+ +

+yields an additional untyped boolean value. The value of ok is true +if the assertion holds. Otherwise it is false and the value of v is +the zero value for type T. +No run-time panic occurs in this case. +

+ + +

Calls

+ +

+Given an expression f of function type +F, +

+ +
+f(a1, a2, … an)
+
+ +

+calls f with arguments a1, a2, … an. +Except for one special case, arguments must be single-valued expressions +assignable to the parameter types of +F and are evaluated before the function is called. +The type of the expression is the result type +of F. +A method invocation is similar but the method itself +is specified as a selector upon a value of the receiver type for +the method. +

+ +
+math.Atan2(x, y)  // function call
+var pt *Point
+pt.Scale(3.5)     // method call with receiver pt
+
+ +

+In a function call, the function value and arguments are evaluated in +the usual order. +After they are evaluated, the parameters of the call are passed by value to the function +and the called function begins execution. +The return parameters of the function are passed by value +back to the caller when the function returns. +

+ +

+Calling a nil function value +causes a run-time panic. +

+ +

+As a special case, if the return values of a function or method +g are equal in number and individually +assignable to the parameters of another function or method +f, then the call f(g(parameters_of_g)) +will invoke f after binding the return values of +g to the parameters of f in order. The call +of f must contain no parameters other than the call of g, +and g must have at least one return value. +If f has a final ... parameter, it is +assigned the return values of g that remain after +assignment of regular parameters. +

+ +
+func Split(s string, pos int) (string, string) {
+	return s[0:pos], s[pos:]
+}
+
+func Join(s, t string) string {
+	return s + t
+}
+
+if Join(Split(value, len(value)/2)) != value {
+	log.Panic("test fails")
+}
+
+ +

+A method call x.m() is valid if the method set +of (the type of) x contains m and the +argument list can be assigned to the parameter list of m. +If x is addressable and &x's method +set contains m, x.m() is shorthand +for (&x).m(): +

+ +
+var p Point
+p.Scale(3.5)
+
+ +

+There is no distinct method type and there are no method literals. +

+ +

Passing arguments to ... parameters

+ +

+If f is variadic with a final +parameter p of type ...T, then within f +the type of p is equivalent to type []T. +If f is invoked with no actual arguments for p, +the value passed to p is nil. +Otherwise, the value passed is a new slice +of type []T with a new underlying array whose successive elements +are the actual arguments, which all must be assignable +to T. The length and capacity of the slice is therefore +the number of arguments bound to p and may differ for each +call site. +

+ +

+Given the function and calls +

+
+func Greeting(prefix string, who ...string)
+Greeting("nobody")
+Greeting("hello:", "Joe", "Anna", "Eileen")
+
+ +

+within Greeting, who will have the value +nil in the first call, and +[]string{"Joe", "Anna", "Eileen"} in the second. +

+ +

+If the final argument is assignable to a slice type []T and +is followed by ..., it is passed unchanged as the value +for a ...T parameter. In this case no new slice is created. +

+ +

+Given the slice s and call +

+ +
+s := []string{"James", "Jasmine"}
+Greeting("goodbye:", s...)
+
+ +

+within Greeting, who will have the same value as s +with the same underlying array. +

+ + +

Operators

+ +

+Operators combine operands into expressions. +

+ +
+Expression = UnaryExpr | Expression binary_op Expression .
+UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .
+
+binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
+rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
+add_op     = "+" | "-" | "|" | "^" .
+mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
+
+unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
+
+ +

+Comparisons are discussed elsewhere. +For other binary operators, the operand types must be identical +unless the operation involves shifts or untyped constants. +For operations involving constants only, see the section on +constant expressions. +

+ +

+Except for shift operations, if one operand is an untyped constant +and the other operand is not, the constant is implicitly converted +to the type of the other operand. +

+ +

+The right operand in a shift expression must have integer type +or be an untyped constant representable by a +value of type uint. +If the left operand of a non-constant shift expression is an untyped constant, +it is first implicitly converted to the type it would assume if the shift expression were +replaced by its left operand alone. +

+ +
+var a [1024]byte
+var s uint = 33
+
+// The results of the following examples are given for 64-bit ints.
+var i = 1<<s                   // 1 has type int
+var j int32 = 1<<s             // 1 has type int32; j == 0
+var k = uint64(1<<s)           // 1 has type uint64; k == 1<<33
+var m int = 1.0<<s             // 1.0 has type int; m == 1<<33
+var n = 1.0<<s == j            // 1.0 has type int32; n == true
+var o = 1<<s == 2<<s           // 1 and 2 have type int; o == false
+var p = 1<<s == 1<<33          // 1 has type int; p == true
+var u = 1.0<<s                 // illegal: 1.0 has type float64, cannot shift
+var u1 = 1.0<<s != 0           // illegal: 1.0 has type float64, cannot shift
+var u2 = 1<<s != 1.0           // illegal: 1 has type float64, cannot shift
+var v float32 = 1<<s           // illegal: 1 has type float32, cannot shift
+var w int64 = 1.0<<33          // 1.0<<33 is a constant shift expression; w == 1<<33
+var x = a[1.0<<s]              // panics: 1.0 has type int, but 1<<33 overflows array bounds
+var b = make([]byte, 1.0<<s)   // 1.0 has type int; len(b) == 1<<33
+
+// The results of the following examples are given for 32-bit ints,
+// which means the shifts will overflow.
+var mm int = 1.0<<s            // 1.0 has type int; mm == 0
+var oo = 1<<s == 2<<s          // 1 and 2 have type int; oo == true
+var pp = 1<<s == 1<<33         // illegal: 1 has type int, but 1<<33 overflows int
+var xx = a[1.0<<s]             // 1.0 has type int; xx == a[0]
+var bb = make([]byte, 1.0<<s)  // 1.0 has type int; len(bb) == 0
+
+ +

Operator precedence

+

+Unary operators have the highest precedence. +As the ++ and -- operators form +statements, not expressions, they fall +outside the operator hierarchy. +As a consequence, statement *p++ is the same as (*p)++. +

+There are five precedence levels for binary operators. +Multiplication operators bind strongest, followed by addition +operators, comparison operators, && (logical AND), +and finally || (logical OR): +

+ +
+Precedence    Operator
+    5             *  /  %  <<  >>  &  &^
+    4             +  -  |  ^
+    3             ==  !=  <  <=  >  >=
+    2             &&
+    1             ||
+
+ +

+Binary operators of the same precedence associate from left to right. +For instance, x / y * z is the same as (x / y) * z. +

+ +
++x
+23 + 3*x[i]
+x <= f()
+^a >> b
+f() || g()
+x == y+1 && <-chanInt > 0
+
+ + +

Arithmetic operators

+

+Arithmetic operators apply to numeric values and yield a result of the same +type as the first operand. The four standard arithmetic operators (+, +-, *, /) apply to integer, +floating-point, and complex types; + also applies to strings. +The bitwise logical and shift operators apply to integers only. +

+ +
++    sum                    integers, floats, complex values, strings
+-    difference             integers, floats, complex values
+*    product                integers, floats, complex values
+/    quotient               integers, floats, complex values
+%    remainder              integers
+
+&    bitwise AND            integers
+|    bitwise OR             integers
+^    bitwise XOR            integers
+&^   bit clear (AND NOT)    integers
+
+<<   left shift             integer << integer >= 0
+>>   right shift            integer >> integer >= 0
+
+ + +

Integer operators

+ +

+For two integer values x and y, the integer quotient +q = x / y and remainder r = x % y satisfy the following +relationships: +

+ +
+x = q*y + r  and  |r| < |y|
+
+ +

+with x / y truncated towards zero +("truncated division"). +

+ +
+ x     y     x / y     x % y
+ 5     3       1         2
+-5     3      -1        -2
+ 5    -3      -1         2
+-5    -3       1        -2
+
+ +

+The one exception to this rule is that if the dividend x is +the most negative value for the int type of x, the quotient +q = x / -1 is equal to x (and r = 0) +due to two's-complement integer overflow: +

+ +
+			 x, q
+int8                     -128
+int16                  -32768
+int32             -2147483648
+int64    -9223372036854775808
+
+ +

+If the divisor is a constant, it must not be zero. +If the divisor is zero at run time, a run-time panic occurs. +If the dividend is non-negative and the divisor is a constant power of 2, +the division may be replaced by a right shift, and computing the remainder may +be replaced by a bitwise AND operation: +

+ +
+ x     x / 4     x % 4     x >> 2     x & 3
+ 11      2         3         2          3
+-11     -2        -3        -3          1
+
+ +

+The shift operators shift the left operand by the shift count specified by the +right operand, which must be non-negative. If the shift count is negative at run time, +a run-time panic occurs. +The shift operators implement arithmetic shifts if the left operand is a signed +integer and logical shifts if it is an unsigned integer. +There is no upper limit on the shift count. Shifts behave +as if the left operand is shifted n times by 1 for a shift +count of n. +As a result, x << 1 is the same as x*2 +and x >> 1 is the same as +x/2 but truncated towards negative infinity. +

+ +

+For integer operands, the unary operators ++, -, and ^ are defined as +follows: +

+ +
++x                          is 0 + x
+-x    negation              is 0 - x
+^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
+                                      and  m = -1 for signed x
+
+ + +

Integer overflow

+ +

+For unsigned integer values, the operations +, +-, *, and << are +computed modulo 2n, where n is the bit width of +the unsigned integer's type. +Loosely speaking, these unsigned integer operations +discard high bits upon overflow, and programs may rely on "wrap around". +

+

+For signed integers, the operations +, +-, *, /, and << may legally +overflow and the resulting value exists and is deterministically defined +by the signed integer representation, the operation, and its operands. +Overflow does not cause a run-time panic. +A compiler may not optimize code under the assumption that overflow does +not occur. For instance, it may not assume that x < x + 1 is always true. +

+ + +

Floating-point operators

+ +

+For floating-point and complex numbers, ++x is the same as x, +while -x is the negation of x. +The result of a floating-point or complex division by zero is not specified beyond the +IEEE-754 standard; whether a run-time panic +occurs is implementation-specific. +

+ +

+An implementation may combine multiple floating-point operations into a single +fused operation, possibly across statements, and produce a result that differs +from the value obtained by executing and rounding the instructions individually. +An explicit floating-point type conversion rounds to +the precision of the target type, preventing fusion that would discard that rounding. +

+ +

+For instance, some architectures provide a "fused multiply and add" (FMA) instruction +that computes x*y + z without rounding the intermediate result x*y. +These examples show when a Go implementation can use that instruction: +

+ +
+// FMA allowed for computing r, because x*y is not explicitly rounded:
+r  = x*y + z
+r  = z;   r += x*y
+t  = x*y; r = t + z
+*p = x*y; r = *p + z
+r  = x*y + float64(z)
+
+// FMA disallowed for computing r, because it would omit rounding of x*y:
+r  = float64(x*y) + z
+r  = z; r += float64(x*y)
+t  = float64(x*y); r = t + z
+
+ +

String concatenation

+ +

+Strings can be concatenated using the + operator +or the += assignment operator: +

+ +
+s := "hi" + string(c)
+s += " and good bye"
+
+ +

+String addition creates a new string by concatenating the operands. +

+ + +

Comparison operators

+ +

+Comparison operators compare two operands and yield an untyped boolean value. +

+ +
+==    equal
+!=    not equal
+<     less
+<=    less or equal
+>     greater
+>=    greater or equal
+
+ +

+In any comparison, the first operand +must be assignable +to the type of the second operand, or vice versa. +

+

+The equality operators == and != apply +to operands that are comparable. +The ordering operators <, <=, >, and >= +apply to operands that are ordered. +These terms and the result of the comparisons are defined as follows: +

+ +
    +
  • + Boolean values are comparable. + Two boolean values are equal if they are either both + true or both false. +
  • + +
  • + Integer values are comparable and ordered, in the usual way. +
  • + +
  • + Floating-point values are comparable and ordered, + as defined by the IEEE-754 standard. +
  • + +
  • + Complex values are comparable. + Two complex values u and v are + equal if both real(u) == real(v) and + imag(u) == imag(v). +
  • + +
  • + String values are comparable and ordered, lexically byte-wise. +
  • + +
  • + Pointer values are comparable. + Two pointer values are equal if they point to the same variable or if both have value nil. + Pointers to distinct zero-size variables may or may not be equal. +
  • + +
  • + Channel values are comparable. + Two channel values are equal if they were created by the same call to + make + or if both have value nil. +
  • + +
  • + Interface values are comparable. + Two interface values are equal if they have identical dynamic types + and equal dynamic values or if both have value nil. +
  • + +
  • + A value x of non-interface type X and + a value t of interface type T are comparable when values + of type X are comparable and + X implements T. + They are equal if t's dynamic type is identical to X + and t's dynamic value is equal to x. +
  • + +
  • + Struct values are comparable if all their fields are comparable. + Two struct values are equal if their corresponding + non-blank fields are equal. +
  • + +
  • + Array values are comparable if values of the array element type are comparable. + Two array values are equal if their corresponding elements are equal. +
  • +
+ +

+A comparison of two interface values with identical dynamic types +causes a run-time panic if values +of that type are not comparable. This behavior applies not only to direct interface +value comparisons but also when comparing arrays of interface values +or structs with interface-valued fields. +

+ +

+Slice, map, and function values are not comparable. +However, as a special case, a slice, map, or function value may +be compared to the predeclared identifier nil. +Comparison of pointer, channel, and interface values to nil +is also allowed and follows from the general rules above. +

+ +
+const c = 3 < 4            // c is the untyped boolean constant true
+
+type MyBool bool
+var x, y int
+var (
+	// The result of a comparison is an untyped boolean.
+	// The usual assignment rules apply.
+	b3        = x == y // b3 has type bool
+	b4 bool   = x == y // b4 has type bool
+	b5 MyBool = x == y // b5 has type MyBool
+)
+
+ +

Logical operators

+ +

+Logical operators apply to boolean values +and yield a result of the same type as the operands. +The right operand is evaluated conditionally. +

+ +
+&&    conditional AND    p && q  is  "if p then q else false"
+||    conditional OR     p || q  is  "if p then true else q"
+!     NOT                !p      is  "not p"
+
+ + +

Address operators

+ +

+For an operand x of type T, the address operation +&x generates a pointer of type *T to x. +The operand must be addressable, +that is, either a variable, pointer indirection, or slice indexing +operation; or a field selector of an addressable struct operand; +or an array indexing operation of an addressable array. +As an exception to the addressability requirement, x may also be a +(possibly parenthesized) +composite literal. +If the evaluation of x would cause a run-time panic, +then the evaluation of &x does too. +

+ +

+For an operand x of pointer type *T, the pointer +indirection *x denotes the variable of type T pointed +to by x. +If x is nil, an attempt to evaluate *x +will cause a run-time panic. +

+ +
+&x
+&a[f(2)]
+&Point{2, 3}
+*p
+*pf(x)
+
+var x *int = nil
+*x   // causes a run-time panic
+&*x  // causes a run-time panic
+
+ + +

Receive operator

+ +

+For an operand ch of channel type, +the value of the receive operation <-ch is the value received +from the channel ch. The channel direction must permit receive operations, +and the type of the receive operation is the element type of the channel. +The expression blocks until a value is available. +Receiving from a nil channel blocks forever. +A receive operation on a closed channel can always proceed +immediately, yielding the element type's zero value +after any previously sent values have been received. +

+ +
+v1 := <-ch
+v2 = <-ch
+f(<-ch)
+<-strobe  // wait until clock pulse and discard received value
+
+ +

+A receive expression used in an assignment or initialization of the special form +

+ +
+x, ok = <-ch
+x, ok := <-ch
+var x, ok = <-ch
+var x, ok T = <-ch
+
+ +

+yields an additional untyped boolean result reporting whether the +communication succeeded. The value of ok is true +if the value received was delivered by a successful send operation to the +channel, or false if it is a zero value generated because the +channel is closed and empty. +

+ + +

Conversions

+ +

+A conversion changes the type of an expression +to the type specified by the conversion. +A conversion may appear literally in the source, or it may be implied +by the context in which an expression appears. +

+ +

+An explicit conversion is an expression of the form T(x) +where T is a type and x is an expression +that can be converted to type T. +

+ +
+Conversion = Type "(" Expression [ "," ] ")" .
+
+ +

+If the type starts with the operator * or <-, +or if the type starts with the keyword func +and has no result list, it must be parenthesized when +necessary to avoid ambiguity: +

+ +
+*Point(p)        // same as *(Point(p))
+(*Point)(p)      // p is converted to *Point
+<-chan int(c)    // same as <-(chan int(c))
+(<-chan int)(c)  // c is converted to <-chan int
+func()(x)        // function signature func() x
+(func())(x)      // x is converted to func()
+(func() int)(x)  // x is converted to func() int
+func() int(x)    // x is converted to func() int (unambiguous)
+
+ +

+A constant value x can be converted to +type T if x is representable +by a value of T. +As a special case, an integer constant x can be explicitly converted to a +string type using the +same rule +as for non-constant x. +

+ +

+Converting a constant yields a typed constant as result. +

+ +
+uint(iota)               // iota value of type uint
+float32(2.718281828)     // 2.718281828 of type float32
+complex128(1)            // 1.0 + 0.0i of type complex128
+float32(0.49999999)      // 0.5 of type float32
+float64(-1e-1000)        // 0.0 of type float64
+string('x')              // "x" of type string
+string(0x266c)           // "♬" of type string
+MyString("foo" + "bar")  // "foobar" of type MyString
+string([]byte{'a'})      // not a constant: []byte{'a'} is not a constant
+(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
+int(1.2)                 // illegal: 1.2 cannot be represented as an int
+string(65.0)             // illegal: 65.0 is not an integer constant
+
+ +

+A non-constant value x can be converted to type T +in any of these cases: +

+ +
    +
  • + x is assignable + to T. +
  • +
  • + ignoring struct tags (see below), + x's type and T have identical + underlying types. +
  • +
  • + ignoring struct tags (see below), + x's type and T are pointer types + that are not defined types, + and their pointer base types have identical underlying types. +
  • +
  • + x's type and T are both integer or floating + point types. +
  • +
  • + x's type and T are both complex types. +
  • +
  • + x is an integer or a slice of bytes or runes + and T is a string type. +
  • +
  • + x is a string and T is a slice of bytes or runes. +
  • +
  • + x is a slice, T is a pointer to an array, + and the slice and array types have identical element types. +
  • +
+ +

+Struct tags are ignored when comparing struct types +for identity for the purpose of conversion: +

+ +
+type Person struct {
+	Name    string
+	Address *struct {
+		Street string
+		City   string
+	}
+}
+
+var data *struct {
+	Name    string `json:"name"`
+	Address *struct {
+		Street string `json:"street"`
+		City   string `json:"city"`
+	} `json:"address"`
+}
+
+var person = (*Person)(data)  // ignoring tags, the underlying types are identical
+
+ +

+Specific rules apply to (non-constant) conversions between numeric types or +to and from a string type. +These conversions may change the representation of x +and incur a run-time cost. +All other conversions only change the type but not the representation +of x. +

+ +

+There is no linguistic mechanism to convert between pointers and integers. +The package unsafe +implements this functionality under +restricted circumstances. +

+ +

Conversions between numeric types

+ +

+For the conversion of non-constant numeric values, the following rules apply: +

+ +
    +
  1. +When converting between integer types, if the value is a signed integer, it is +sign extended to implicit infinite precision; otherwise it is zero extended. +It is then truncated to fit in the result type's size. +For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. +The conversion always yields a valid value; there is no indication of overflow. +
  2. +
  3. +When converting a floating-point number to an integer, the fraction is discarded +(truncation towards zero). +
  4. +
  5. +When converting an integer or floating-point number to a floating-point type, +or a complex number to another complex type, the result value is rounded +to the precision specified by the destination type. +For instance, the value of a variable x of type float32 +may be stored using additional precision beyond that of an IEEE-754 32-bit number, +but float32(x) represents the result of rounding x's value to +32-bit precision. Similarly, x + 0.1 may use more than 32 bits +of precision, but float32(x + 0.1) does not. +
  6. +
+ +

+In all non-constant conversions involving floating-point or complex values, +if the result type cannot represent the value the conversion +succeeds but the result value is implementation-dependent. +

+ +

Conversions to and from a string type

+ +
    +
  1. +Converting a signed or unsigned integer value to a string type yields a +string containing the UTF-8 representation of the integer. Values outside +the range of valid Unicode code points are converted to "\uFFFD". + +
    +string('a')       // "a"
    +string(-1)        // "\ufffd" == "\xef\xbf\xbd"
    +string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    +type MyString string
    +MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
    +
    +
  2. + +
  3. +Converting a slice of bytes to a string type yields +a string whose successive bytes are the elements of the slice. + +
    +string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    +string([]byte{})                                     // ""
    +string([]byte(nil))                                  // ""
    +
    +type MyBytes []byte
    +string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
    +
    +
  4. + +
  5. +Converting a slice of runes to a string type yields +a string that is the concatenation of the individual rune values +converted to strings. + +
    +string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    +string([]rune{})                         // ""
    +string([]rune(nil))                      // ""
    +
    +type MyRunes []rune
    +string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    +
    +
  6. + +
  7. +Converting a value of a string type to a slice of bytes type +yields a slice whose successive elements are the bytes of the string. + +
    +[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    +[]byte("")        // []byte{}
    +
    +MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    +
    +
  8. + +
  9. +Converting a value of a string type to a slice of runes type +yields a slice containing the individual Unicode code points of the string. + +
    +[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
    +[]rune("")                 // []rune{}
    +
    +MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}
    +
    +
  10. +
+ +

Conversions from slice to array pointer

+ +

+Converting a slice to an array pointer yields a pointer to the underlying array of the slice. +If the length of the slice is less than the length of the array, +a run-time panic occurs. +

+ +
+s := make([]byte, 2, 4)
+s0 := (*[0]byte)(s)      // s0 != nil
+s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
+s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
+s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)
+
+var t []string
+t0 := (*[0]string)(t)    // t0 == nil
+t1 := (*[1]string)(t)    // panics: len([1]string) > len(t)
+
+u := make([]byte, 0)
+u0 := (*[0]byte)(u)      // u0 != nil
+
+ +

Constant expressions

+ +

+Constant expressions may contain only constant +operands and are evaluated at compile time. +

+ +

+Untyped boolean, numeric, and string constants may be used as operands +wherever it is legal to use an operand of boolean, numeric, or string type, +respectively. +

+ +

+A constant comparison always yields +an untyped boolean constant. If the left operand of a constant +shift expression is an untyped constant, the +result is an integer constant; otherwise it is a constant of the same +type as the left operand, which must be of +integer type. +

+ +

+Any other operation on untyped constants results in an untyped constant of the +same kind; that is, a boolean, integer, floating-point, complex, or string +constant. +If the untyped operands of a binary operation (other than a shift) are of +different kinds, the result is of the operand's kind that appears later in this +list: integer, rune, floating-point, complex. +For example, an untyped integer constant divided by an +untyped complex constant yields an untyped complex constant. +

+ +
+const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
+const b = 15 / 4           // b == 3     (untyped integer constant)
+const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
+const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
+const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
+const d = 1 << 3.0         // d == 8     (untyped integer constant)
+const e = 1.0 << 3         // e == 8     (untyped integer constant)
+const f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)
+const g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)
+const h = "foo" > "bar"    // h == true  (untyped boolean constant)
+const j = true             // j == true  (untyped boolean constant)
+const k = 'w' + 1          // k == 'x'   (untyped rune constant)
+const l = "hi"             // l == "hi"  (untyped string constant)
+const m = string(k)        // m == "x"   (type string)
+const Σ = 1 - 0.707i       //            (untyped complex constant)
+const Δ = Σ + 2.0e-4       //            (untyped complex constant)
+const Φ = iota*1i - 1/1i   //            (untyped complex constant)
+
+ +

+Applying the built-in function complex to untyped +integer, rune, or floating-point constants yields +an untyped complex constant. +

+ +
+const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
+const iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)
+
+ +

+Constant expressions are always evaluated exactly; intermediate values and the +constants themselves may require precision significantly larger than supported +by any predeclared type in the language. The following are legal declarations: +

+ +
+const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
+const Four int8 = Huge >> 98  // Four == 4                                (type int8)
+
+ +

+The divisor of a constant division or remainder operation must not be zero: +

+ +
+3.14 / 0.0   // illegal: division by zero
+
+ +

+The values of typed constants must always be accurately +representable by values +of the constant type. The following constant expressions are illegal: +

+ +
+uint(-1)     // -1 cannot be represented as a uint
+int(3.14)    // 3.14 cannot be represented as an int
+int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
+Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
+Four * 100   // product 400 cannot be represented as an int8 (type of Four)
+
+ +

+The mask used by the unary bitwise complement operator ^ matches +the rule for non-constants: the mask is all 1s for unsigned constants +and -1 for signed and untyped constants. +

+ +
+^1         // untyped integer constant, equal to -2
+uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
+^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
+int8(^1)   // same as int8(-2)
+^int8(1)   // same as -1 ^ int8(1) = -2
+
+ +

+Implementation restriction: A compiler may use rounding while +computing untyped floating-point or complex constant expressions; see +the implementation restriction in the section +on constants. This rounding may cause a +floating-point constant expression to be invalid in an integer +context, even if it would be integral when calculated using infinite +precision, and vice versa. +

+ + +

Order of evaluation

+ +

+At package level, initialization dependencies +determine the evaluation order of individual initialization expressions in +variable declarations. +Otherwise, when evaluating the operands of an +expression, assignment, or +return statement, +all function calls, method calls, and +communication operations are evaluated in lexical left-to-right +order. +

+ +

+For example, in the (function-local) assignment +

+
+y[f()], ok = g(h(), i()+x[j()], <-c), k()
+
+

+the function calls and communication happen in the order +f(), h(), i(), j(), +<-c, g(), and k(). +However, the order of those events compared to the evaluation +and indexing of x and the evaluation +of y is not specified. +

+ +
+a := 1
+f := func() int { a++; return a }
+x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
+m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
+n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified
+
+ +

+At package level, initialization dependencies override the left-to-right rule +for individual initialization expressions, but not for operands within each +expression: +

+ +
+var a, b, c = f() + v(), g(), sqr(u()) + v()
+
+func f() int        { return c }
+func g() int        { return a }
+func sqr(x int) int { return x*x }
+
+// functions u and v are independent of all other variables and functions
+
+ +

+The function calls happen in the order +u(), sqr(), v(), +f(), v(), and g(). +

+ +

+Floating-point operations within a single expression are evaluated according to +the associativity of the operators. Explicit parentheses affect the evaluation +by overriding the default associativity. +In the expression x + (y + z) the addition y + z +is performed before adding x. +

+ +

Statements

+ +

+Statements control execution. +

+ +
+Statement =
+	Declaration | LabeledStmt | SimpleStmt |
+	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
+	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
+	DeferStmt .
+
+SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
+
+ +

Terminating statements

+ +

+A terminating statement interrupts the regular flow of control in +a block. The following statements are terminating: +

+ +
    +
  1. + A "return" or + "goto" statement. + +
    +
  2. + +
  3. + A call to the built-in function + panic. + +
    +
  4. + +
  5. + A block in which the statement list ends in a terminating statement. + +
    +
  6. + +
  7. + An "if" statement in which: +
      +
    • the "else" branch is present, and
    • +
    • both branches are terminating statements.
    • +
    +
  8. + +
  9. + A "for" statement in which: +
      +
    • there are no "break" statements referring to the "for" statement, and
    • +
    • the loop condition is absent, and
    • +
    • the "for" statement does not use a range clause.
    • +
    +
  10. + +
  11. + A "switch" statement in which: +
      +
    • there are no "break" statements referring to the "switch" statement,
    • +
    • there is a default case, and
    • +
    • the statement lists in each case, including the default, end in a terminating + statement, or a possibly labeled "fallthrough" + statement.
    • +
    +
  12. + +
  13. + A "select" statement in which: +
      +
    • there are no "break" statements referring to the "select" statement, and
    • +
    • the statement lists in each case, including the default if present, + end in a terminating statement.
    • +
    +
  14. + +
  15. + A labeled statement labeling + a terminating statement. +
  16. +
+ +

+All other statements are not terminating. +

+ +

+A statement list ends in a terminating statement if the list +is not empty and its final non-empty statement is terminating. +

+ + +

Empty statements

+ +

+The empty statement does nothing. +

+ +
+EmptyStmt = .
+
+ + +

Labeled statements

+ +

+A labeled statement may be the target of a goto, +break or continue statement. +

+ +
+LabeledStmt = Label ":" Statement .
+Label       = identifier .
+
+ +
+Error: log.Panic("error encountered")
+
+ + +

Expression statements

+ +

+With the exception of specific built-in functions, +function and method calls and +receive operations +can appear in statement context. Such statements may be parenthesized. +

+ +
+ExpressionStmt = Expression .
+
+ +

+The following built-in functions are not permitted in statement context: +

+ +
+append cap complex imag len make new real
+unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
+
+ +
+h(x+y)
+f.Close()
+<-ch
+(<-ch)
+len("foo")  // illegal if len is the built-in function
+
+ + +

Send statements

+ +

+A send statement sends a value on a channel. +The channel expression must be of channel type, +the channel direction must permit send operations, +and the type of the value to be sent must be assignable +to the channel's element type. +

+ +
+SendStmt = Channel "<-" Expression .
+Channel  = Expression .
+
+ +

+Both the channel and the value expression are evaluated before communication +begins. Communication blocks until the send can proceed. +A send on an unbuffered channel can proceed if a receiver is ready. +A send on a buffered channel can proceed if there is room in the buffer. +A send on a closed channel proceeds by causing a run-time panic. +A send on a nil channel blocks forever. +

+ +
+ch <- 3  // send value 3 to channel ch
+
+ + +

IncDec statements

+ +

+The "++" and "--" statements increment or decrement their operands +by the untyped constant 1. +As with an assignment, the operand must be addressable +or a map index expression. +

+ +
+IncDecStmt = Expression ( "++" | "--" ) .
+
+ +

+The following assignment statements are semantically +equivalent: +

+ +
+IncDec statement    Assignment
+x++                 x += 1
+x--                 x -= 1
+
+ + +

Assignments

+ +
+Assignment = ExpressionList assign_op ExpressionList .
+
+assign_op = [ add_op | mul_op ] "=" .
+
+ +

+Each left-hand side operand must be addressable, +a map index expression, or (for = assignments only) the +blank identifier. +Operands may be parenthesized. +

+ +
+x = 1
+*p = f()
+a[i] = 23
+(k) = <-ch  // same as: k = <-ch
+
+ +

+An assignment operation x op= +y where op is a binary arithmetic operator +is equivalent to x = x op +(y) but evaluates x +only once. The op= construct is a single token. +In assignment operations, both the left- and right-hand expression lists +must contain exactly one single-valued expression, and the left-hand +expression must not be the blank identifier. +

+ +
+a[i] <<= 2
+i &^= 1<<n
+
+ +

+A tuple assignment assigns the individual elements of a multi-valued +operation to a list of variables. There are two forms. In the +first, the right hand operand is a single multi-valued expression +such as a function call, a channel or +map operation, or a type assertion. +The number of operands on the left +hand side must match the number of values. For instance, if +f is a function returning two values, +

+ +
+x, y = f()
+
+ +

+assigns the first value to x and the second to y. +In the second form, the number of operands on the left must equal the number +of expressions on the right, each of which must be single-valued, and the +nth expression on the right is assigned to the nth +operand on the left: +

+ +
+one, two, three = '一', '二', '三'
+
+ +

+The blank identifier provides a way to +ignore right-hand side values in an assignment: +

+ +
+_ = x       // evaluate x but ignore it
+x, _ = f()  // evaluate f() but ignore second result value
+
+ +

+The assignment proceeds in two phases. +First, the operands of index expressions +and pointer indirections +(including implicit pointer indirections in selectors) +on the left and the expressions on the right are all +evaluated in the usual order. +Second, the assignments are carried out in left-to-right order. +

+ +
+a, b = b, a  // exchange a and b
+
+x := []int{1, 2, 3}
+i := 0
+i, x[i] = 1, 2  // set i = 1, x[0] = 2
+
+i = 0
+x[i], i = 2, 1  // set x[0] = 2, i = 1
+
+x[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)
+
+x[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.
+
+type Point struct { x, y int }
+var p *Point
+x[2], p.x = 6, 7  // set x[2] = 6, then panic setting p.x = 7
+
+i = 2
+x = []int{3, 5, 7}
+for i, x[i] = range x {  // set i, x[2] = 0, x[0]
+	break
+}
+// after this loop, i == 0 and x == []int{3, 5, 3}
+
+ +

+In assignments, each value must be assignable +to the type of the operand to which it is assigned, with the following special cases: +

+ +
    +
  1. + Any typed value may be assigned to the blank identifier. +
  2. + +
  3. + If an untyped constant + is assigned to a variable of interface type or the blank identifier, + the constant is first implicitly converted to its + default type. +
  4. + +
  5. + If an untyped boolean value is assigned to a variable of interface type or + the blank identifier, it is first implicitly converted to type bool. +
  6. +
+ +

If statements

+ +

+"If" statements specify the conditional execution of two branches +according to the value of a boolean expression. If the expression +evaluates to true, the "if" branch is executed, otherwise, if +present, the "else" branch is executed. +

+ +
+IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
+
+ +
+if x > max {
+	x = max
+}
+
+ +

+The expression may be preceded by a simple statement, which +executes before the expression is evaluated. +

+ +
+if x := f(); x < y {
+	return x
+} else if x > z {
+	return z
+} else {
+	return y
+}
+
+ + +

Switch statements

+ +

+"Switch" statements provide multi-way execution. +An expression or type is compared to the "cases" +inside the "switch" to determine which branch +to execute. +

+ +
+SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
+
+ +

+There are two forms: expression switches and type switches. +In an expression switch, the cases contain expressions that are compared +against the value of the switch expression. +In a type switch, the cases contain types that are compared against the +type of a specially annotated switch expression. +The switch expression is evaluated exactly once in a switch statement. +

+ +

Expression switches

+ +

+In an expression switch, +the switch expression is evaluated and +the case expressions, which need not be constants, +are evaluated left-to-right and top-to-bottom; the first one that equals the +switch expression +triggers execution of the statements of the associated case; +the other cases are skipped. +If no case matches and there is a "default" case, +its statements are executed. +There can be at most one default case and it may appear anywhere in the +"switch" statement. +A missing switch expression is equivalent to the boolean value +true. +

+ +
+ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
+ExprCaseClause = ExprSwitchCase ":" StatementList .
+ExprSwitchCase = "case" ExpressionList | "default" .
+
+ +

+If the switch expression evaluates to an untyped constant, it is first implicitly +converted to its default type. +The predeclared untyped value nil cannot be used as a switch expression. +The switch expression type must be comparable. +

+ +

+If a case expression is untyped, it is first implicitly converted +to the type of the switch expression. +For each (possibly converted) case expression x and the value t +of the switch expression, x == t must be a valid comparison. +

+ +

+In other words, the switch expression is treated as if it were used to declare and +initialize a temporary variable t without explicit type; it is that +value of t against which each case expression x is tested +for equality. +

+ +

+In a case or default clause, the last non-empty statement +may be a (possibly labeled) +"fallthrough" statement to +indicate that control should flow from the end of this clause to +the first statement of the next clause. +Otherwise control flows to the end of the "switch" statement. +A "fallthrough" statement may appear as the last statement of all +but the last clause of an expression switch. +

+ +

+The switch expression may be preceded by a simple statement, which +executes before the expression is evaluated. +

+ +
+switch tag {
+default: s3()
+case 0, 1, 2, 3: s1()
+case 4, 5, 6, 7: s2()
+}
+
+switch x := f(); {  // missing switch expression means "true"
+case x < 0: return -x
+default: return x
+}
+
+switch {
+case x < y: f1()
+case x < z: f2()
+case x == 4: f3()
+}
+
+ +

+Implementation restriction: A compiler may disallow multiple case +expressions evaluating to the same constant. +For instance, the current compilers disallow duplicate integer, +floating point, or string constants in case expressions. +

+ +

Type switches

+ +

+A type switch compares types rather than values. It is otherwise similar +to an expression switch. It is marked by a special switch expression that +has the form of a type assertion +using the keyword type rather than an actual type: +

+ +
+switch x.(type) {
+// cases
+}
+
+ +

+Cases then match actual types T against the dynamic type of the +expression x. As with type assertions, x must be of +interface type, and each non-interface type +T listed in a case must implement the type of x. +The types listed in the cases of a type switch must all be +different. +

+ +
+TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
+TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+TypeCaseClause  = TypeSwitchCase ":" StatementList .
+TypeSwitchCase  = "case" TypeList | "default" .
+TypeList        = Type { "," Type } .
+
+ +

+The TypeSwitchGuard may include a +short variable declaration. +When that form is used, the variable is declared at the end of the +TypeSwitchCase in the implicit block of each clause. +In clauses with a case listing exactly one type, the variable +has that type; otherwise, the variable has the type of the expression +in the TypeSwitchGuard. +

+ +

+Instead of a type, a case may use the predeclared identifier +nil; +that case is selected when the expression in the TypeSwitchGuard +is a nil interface value. +There may be at most one nil case. +

+ +

+Given an expression x of type interface{}, +the following type switch: +

+ +
+switch i := x.(type) {
+case nil:
+	printString("x is nil")                // type of i is type of x (interface{})
+case int:
+	printInt(i)                            // type of i is int
+case float64:
+	printFloat64(i)                        // type of i is float64
+case func(int) float64:
+	printFunction(i)                       // type of i is func(int) float64
+case bool, string:
+	printString("type is bool or string")  // type of i is type of x (interface{})
+default:
+	printString("don't know the type")     // type of i is type of x (interface{})
+}
+
+ +

+could be rewritten: +

+ +
+v := x  // x is evaluated exactly once
+if v == nil {
+	i := v                                 // type of i is type of x (interface{})
+	printString("x is nil")
+} else if i, isInt := v.(int); isInt {
+	printInt(i)                            // type of i is int
+} else if i, isFloat64 := v.(float64); isFloat64 {
+	printFloat64(i)                        // type of i is float64
+} else if i, isFunc := v.(func(int) float64); isFunc {
+	printFunction(i)                       // type of i is func(int) float64
+} else {
+	_, isBool := v.(bool)
+	_, isString := v.(string)
+	if isBool || isString {
+		i := v                         // type of i is type of x (interface{})
+		printString("type is bool or string")
+	} else {
+		i := v                         // type of i is type of x (interface{})
+		printString("don't know the type")
+	}
+}
+
+ +

+The type switch guard may be preceded by a simple statement, which +executes before the guard is evaluated. +

+ +

+The "fallthrough" statement is not permitted in a type switch. +

+ +

For statements

+ +

+A "for" statement specifies repeated execution of a block. There are three forms: +The iteration may be controlled by a single condition, a "for" clause, or a "range" clause. +

+ +
+ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
+Condition = Expression .
+
+ +

For statements with single condition

+ +

+In its simplest form, a "for" statement specifies the repeated execution of +a block as long as a boolean condition evaluates to true. +The condition is evaluated before each iteration. +If the condition is absent, it is equivalent to the boolean value +true. +

+ +
+for a < b {
+	a *= 2
+}
+
+ +

For statements with for clause

+ +

+A "for" statement with a ForClause is also controlled by its condition, but +additionally it may specify an init +and a post statement, such as an assignment, +an increment or decrement statement. The init statement may be a +short variable declaration, but the post statement must not. +Variables declared by the init statement are re-used in each iteration. +

+ +
+ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
+InitStmt = SimpleStmt .
+PostStmt = SimpleStmt .
+
+ +
+for i := 0; i < 10; i++ {
+	f(i)
+}
+
+ +

+If non-empty, the init statement is executed once before evaluating the +condition for the first iteration; +the post statement is executed after each execution of the block (and +only if the block was executed). +Any element of the ForClause may be empty but the +semicolons are +required unless there is only a condition. +If the condition is absent, it is equivalent to the boolean value +true. +

+ +
+for cond { S() }    is the same as    for ; cond ; { S() }
+for      { S() }    is the same as    for true     { S() }
+
+ +

For statements with range clause

+ +

+A "for" statement with a "range" clause +iterates through all entries of an array, slice, string or map, +or values received on a channel. For each entry it assigns iteration values +to corresponding iteration variables if present and then executes the block. +

+ +
+RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
+
+ +

+The expression on the right in the "range" clause is called the range expression, +which may be an array, pointer to an array, slice, string, map, or channel permitting +receive operations. +As with an assignment, if present the operands on the left must be +addressable or map index expressions; they +denote the iteration variables. If the range expression is a channel, at most +one iteration variable is permitted, otherwise there may be up to two. +If the last iteration variable is the blank identifier, +the range clause is equivalent to the same clause without that identifier. +

+ +

+The range expression x is evaluated once before beginning the loop, +with one exception: if at most one iteration variable is present and +len(x) is constant, +the range expression is not evaluated. +

+ +

+Function calls on the left are evaluated once per iteration. +For each iteration, iteration values are produced as follows +if the respective iteration variables are present: +

+ +
+Range expression                          1st value          2nd value
+
+array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
+string          s  string type            index    i  int    see below  rune
+map             m  map[K]V                key      k  K      m[k]       V
+channel         c  chan E, <-chan E       element  e  E
+
+ +
    +
  1. +For an array, pointer to array, or slice value a, the index iteration +values are produced in increasing order, starting at element index 0. +If at most one iteration variable is present, the range loop produces +iteration values from 0 up to len(a)-1 and does not index into the array +or slice itself. For a nil slice, the number of iterations is 0. +
  2. + +
  3. +For a string value, the "range" clause iterates over the Unicode code points +in the string starting at byte index 0. On successive iterations, the index value will be the +index of the first byte of successive UTF-8-encoded code points in the string, +and the second value, of type rune, will be the value of +the corresponding code point. If the iteration encounters an invalid +UTF-8 sequence, the second value will be 0xFFFD, +the Unicode replacement character, and the next iteration will advance +a single byte in the string. +
  4. + +
  5. +The iteration order over maps is not specified +and is not guaranteed to be the same from one iteration to the next. +If a map entry that has not yet been reached is removed during iteration, +the corresponding iteration value will not be produced. If a map entry is +created during iteration, that entry may be produced during the iteration or +may be skipped. The choice may vary for each entry created and from one +iteration to the next. +If the map is nil, the number of iterations is 0. +
  6. + +
  7. +For channels, the iteration values produced are the successive values sent on +the channel until the channel is closed. If the channel +is nil, the range expression blocks forever. +
  8. +
+ +

+The iteration values are assigned to the respective +iteration variables as in an assignment statement. +

+ +

+The iteration variables may be declared by the "range" clause using a form of +short variable declaration +(:=). +In this case their types are set to the types of the respective iteration values +and their scope is the block of the "for" +statement; they are re-used in each iteration. +If the iteration variables are declared outside the "for" statement, +after execution their values will be those of the last iteration. +

+ +
+var testdata *struct {
+	a *[7]int
+}
+for i, _ := range testdata.a {
+	// testdata.a is never evaluated; len(testdata.a) is constant
+	// i ranges from 0 to 6
+	f(i)
+}
+
+var a [10]string
+for i, s := range a {
+	// type of i is int
+	// type of s is string
+	// s == a[i]
+	g(i, s)
+}
+
+var key string
+var val interface{}  // element type of m is assignable to val
+m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
+for key, val = range m {
+	h(key, val)
+}
+// key == last map key encountered in iteration
+// val == map[key]
+
+var ch chan Work = producer()
+for w := range ch {
+	doWork(w)
+}
+
+// empty a channel
+for range ch {}
+
+ + +

Go statements

+ +

+A "go" statement starts the execution of a function call +as an independent concurrent thread of control, or goroutine, +within the same address space. +

+ +
+GoStmt = "go" Expression .
+
+ +

+The expression must be a function or method call; it cannot be parenthesized. +Calls of built-in functions are restricted as for +expression statements. +

+ +

+The function value and parameters are +evaluated as usual +in the calling goroutine, but +unlike with a regular call, program execution does not wait +for the invoked function to complete. +Instead, the function begins executing independently +in a new goroutine. +When the function terminates, its goroutine also terminates. +If the function has any return values, they are discarded when the +function completes. +

+ +
+go Server()
+go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
+
+ + +

Select statements

+ +

+A "select" statement chooses which of a set of possible +send or +receive +operations will proceed. +It looks similar to a +"switch" statement but with the +cases all referring to communication operations. +

+ +
+SelectStmt = "select" "{" { CommClause } "}" .
+CommClause = CommCase ":" StatementList .
+CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
+RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
+RecvExpr   = Expression .
+
+ +

+A case with a RecvStmt may assign the result of a RecvExpr to one or +two variables, which may be declared using a +short variable declaration. +The RecvExpr must be a (possibly parenthesized) receive operation. +There can be at most one default case and it may appear anywhere +in the list of cases. +

+ +

+Execution of a "select" statement proceeds in several steps: +

+ +
    +
  1. +For all the cases in the statement, the channel operands of receive operations +and the channel and right-hand-side expressions of send statements are +evaluated exactly once, in source order, upon entering the "select" statement. +The result is a set of channels to receive from or send to, +and the corresponding values to send. +Any side effects in that evaluation will occur irrespective of which (if any) +communication operation is selected to proceed. +Expressions on the left-hand side of a RecvStmt with a short variable declaration +or assignment are not yet evaluated. +
  2. + +
  3. +If one or more of the communications can proceed, +a single one that can proceed is chosen via a uniform pseudo-random selection. +Otherwise, if there is a default case, that case is chosen. +If there is no default case, the "select" statement blocks until +at least one of the communications can proceed. +
  4. + +
  5. +Unless the selected case is the default case, the respective communication +operation is executed. +
  6. + +
  7. +If the selected case is a RecvStmt with a short variable declaration or +an assignment, the left-hand side expressions are evaluated and the +received value (or values) are assigned. +
  8. + +
  9. +The statement list of the selected case is executed. +
  10. +
+ +

+Since communication on nil channels can never proceed, +a select with only nil channels and no default case blocks forever. +

+ +
+var a []int
+var c, c1, c2, c3, c4 chan int
+var i1, i2 int
+select {
+case i1 = <-c1:
+	print("received ", i1, " from c1\n")
+case c2 <- i2:
+	print("sent ", i2, " to c2\n")
+case i3, ok := (<-c3):  // same as: i3, ok := <-c3
+	if ok {
+		print("received ", i3, " from c3\n")
+	} else {
+		print("c3 is closed\n")
+	}
+case a[f()] = <-c4:
+	// same as:
+	// case t := <-c4
+	//	a[f()] = t
+default:
+	print("no communication\n")
+}
+
+for {  // send random sequence of bits to c
+	select {
+	case c <- 0:  // note: no statement, no fallthrough, no folding of cases
+	case c <- 1:
+	}
+}
+
+select {}  // block forever
+
+ + +

Return statements

+ +

+A "return" statement in a function F terminates the execution +of F, and optionally provides one or more result values. +Any functions deferred by F +are executed before F returns to its caller. +

+ +
+ReturnStmt = "return" [ ExpressionList ] .
+
+ +

+In a function without a result type, a "return" statement must not +specify any result values. +

+
+func noResult() {
+	return
+}
+
+ +

+There are three ways to return values from a function with a result +type: +

+ +
    +
  1. The return value or values may be explicitly listed + in the "return" statement. Each expression must be single-valued + and assignable + to the corresponding element of the function's result type. +
    +func simpleF() int {
    +	return 2
    +}
    +
    +func complexF1() (re float64, im float64) {
    +	return -7.0, -4.0
    +}
    +
    +
  2. +
  3. The expression list in the "return" statement may be a single + call to a multi-valued function. The effect is as if each value + returned from that function were assigned to a temporary + variable with the type of the respective value, followed by a + "return" statement listing these variables, at which point the + rules of the previous case apply. +
    +func complexF2() (re float64, im float64) {
    +	return complexF1()
    +}
    +
    +
  4. +
  5. The expression list may be empty if the function's result + type specifies names for its result parameters. + The result parameters act as ordinary local variables + and the function may assign values to them as necessary. + The "return" statement returns the values of these variables. +
    +func complexF3() (re float64, im float64) {
    +	re = 7.0
    +	im = 4.0
    +	return
    +}
    +
    +func (devnull) Write(p []byte) (n int, _ error) {
    +	n = len(p)
    +	return
    +}
    +
    +
  6. +
+ +

+Regardless of how they are declared, all the result values are initialized to +the zero values for their type upon entry to the +function. A "return" statement that specifies results sets the result parameters before +any deferred functions are executed. +

+ +

+Implementation restriction: A compiler may disallow an empty expression list +in a "return" statement if a different entity (constant, type, or variable) +with the same name as a result parameter is in +scope at the place of the return. +

+ +
+func f(n int) (res int, err error) {
+	if _, err := f(n-1); err != nil {
+		return  // invalid return statement: err is shadowed
+	}
+	return
+}
+
+ +

Break statements

+ +

+A "break" statement terminates execution of the innermost +"for", +"switch", or +"select" statement +within the same function. +

+ +
+BreakStmt = "break" [ Label ] .
+
+ +

+If there is a label, it must be that of an enclosing +"for", "switch", or "select" statement, +and that is the one whose execution terminates. +

+ +
+OuterLoop:
+	for i = 0; i < n; i++ {
+		for j = 0; j < m; j++ {
+			switch a[i][j] {
+			case nil:
+				state = Error
+				break OuterLoop
+			case item:
+				state = Found
+				break OuterLoop
+			}
+		}
+	}
+
+ +

Continue statements

+ +

+A "continue" statement begins the next iteration of the +innermost "for" loop at its post statement. +The "for" loop must be within the same function. +

+ +
+ContinueStmt = "continue" [ Label ] .
+
+ +

+If there is a label, it must be that of an enclosing +"for" statement, and that is the one whose execution +advances. +

+ +
+RowLoop:
+	for y, row := range rows {
+		for x, data := range row {
+			if data == endOfRow {
+				continue RowLoop
+			}
+			row[x] = data + bias(x, y)
+		}
+	}
+
+ +

Goto statements

+ +

+A "goto" statement transfers control to the statement with the corresponding label +within the same function. +

+ +
+GotoStmt = "goto" Label .
+
+ +
+goto Error
+
+ +

+Executing the "goto" statement must not cause any variables to come into +scope that were not already in scope at the point of the goto. +For instance, this example: +

+ +
+	goto L  // BAD
+	v := 3
+L:
+
+ +

+is erroneous because the jump to label L skips +the creation of v. +

+ +

+A "goto" statement outside a block cannot jump to a label inside that block. +For instance, this example: +

+ +
+if n%2 == 1 {
+	goto L1
+}
+for n > 0 {
+	f()
+	n--
+L1:
+	f()
+	n--
+}
+
+ +

+is erroneous because the label L1 is inside +the "for" statement's block but the goto is not. +

+ +

Fallthrough statements

+ +

+A "fallthrough" statement transfers control to the first statement of the +next case clause in an expression "switch" statement. +It may be used only as the final non-empty statement in such a clause. +

+ +
+FallthroughStmt = "fallthrough" .
+
+ + +

Defer statements

+ +

+A "defer" statement invokes a function whose execution is deferred +to the moment the surrounding function returns, either because the +surrounding function executed a return statement, +reached the end of its function body, +or because the corresponding goroutine is panicking. +

+ +
+DeferStmt = "defer" Expression .
+
+ +

+The expression must be a function or method call; it cannot be parenthesized. +Calls of built-in functions are restricted as for +expression statements. +

+ +

+Each time a "defer" statement +executes, the function value and parameters to the call are +evaluated as usual +and saved anew but the actual function is not invoked. +Instead, deferred functions are invoked immediately before +the surrounding function returns, in the reverse order +they were deferred. That is, if the surrounding function +returns through an explicit return statement, +deferred functions are executed after any result parameters are set +by that return statement but before the function returns to its caller. +If a deferred function value evaluates +to nil, execution panics +when the function is invoked, not when the "defer" statement is executed. +

+ +

+For instance, if the deferred function is +a function literal and the surrounding +function has named result parameters that +are in scope within the literal, the deferred function may access and modify +the result parameters before they are returned. +If the deferred function has any return values, they are discarded when +the function completes. +(See also the section on handling panics.) +

+ +
+lock(l)
+defer unlock(l)  // unlocking happens before surrounding function returns
+
+// prints 3 2 1 0 before surrounding function returns
+for i := 0; i <= 3; i++ {
+	defer fmt.Print(i)
+}
+
+// f returns 42
+func f() (result int) {
+	defer func() {
+		// result is accessed after it was set to 6 by the return statement
+		result *= 7
+	}()
+	return 6
+}
+
+ +

Built-in functions

+ +

+Built-in functions are +predeclared. +They are called like any other function but some of them +accept a type instead of an expression as the first argument. +

+ +

+The built-in functions do not have standard Go types, +so they can only appear in call expressions; +they cannot be used as function values. +

+ +

Close

+ +

+For a channel c, the built-in function close(c) +records that no more values will be sent on the channel. +It is an error if c is a receive-only channel. +Sending to or closing a closed channel causes a run-time panic. +Closing the nil channel also causes a run-time panic. +After calling close, and after any previously +sent values have been received, receive operations will return +the zero value for the channel's type without blocking. +The multi-valued receive operation +returns a received value along with an indication of whether the channel is closed. +

+ + +

Length and capacity

+ +

+The built-in functions len and cap take arguments +of various types and return a result of type int. +The implementation guarantees that the result always fits into an int. +

+ +
+Call      Argument type    Result
+
+len(s)    string type      string length in bytes
+          [n]T, *[n]T      array length (== n)
+          []T              slice length
+          map[K]T          map length (number of defined keys)
+          chan T           number of elements queued in channel buffer
+
+cap(s)    [n]T, *[n]T      array length (== n)
+          []T              slice capacity
+          chan T           channel buffer capacity
+
+ +

+The capacity of a slice is the number of elements for which there is +space allocated in the underlying array. +At any time the following relationship holds: +

+ +
+0 <= len(s) <= cap(s)
+
+ +

+The length of a nil slice, map or channel is 0. +The capacity of a nil slice or channel is 0. +

+ +

+The expression len(s) is constant if +s is a string constant. The expressions len(s) and +cap(s) are constants if the type of s is an array +or pointer to an array and the expression s does not contain +channel receives or (non-constant) +function calls; in this case s is not evaluated. +Otherwise, invocations of len and cap are not +constant and s is evaluated. +

+ +
+const (
+	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
+	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
+	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
+	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
+	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
+)
+var z complex128
+
+ +

Allocation

+ +

+The built-in function new takes a type T, +allocates storage for a variable of that type +at run time, and returns a value of type *T +pointing to it. +The variable is initialized as described in the section on +initial values. +

+ +
+new(T)
+
+ +

+For instance +

+ +
+type S struct { a int; b float64 }
+new(S)
+
+ +

+allocates storage for a variable of type S, +initializes it (a=0, b=0.0), +and returns a value of type *S containing the address +of the location. +

+ +

Making slices, maps and channels

+ +

+The built-in function make takes a type T, +which must be a slice, map or channel type, +optionally followed by a type-specific list of expressions. +It returns a value of type T (not *T). +The memory is initialized as described in the section on +initial values. +

+ +
+Call             Type T     Result
+
+make(T, n)       slice      slice of type T with length n and capacity n
+make(T, n, m)    slice      slice of type T with length n and capacity m
+
+make(T)          map        map of type T
+make(T, n)       map        map of type T with initial space for approximately n elements
+
+make(T)          channel    unbuffered channel of type T
+make(T, n)       channel    buffered channel of type T, buffer size n
+
+ + +

+Each of the size arguments n and m must be of integer type +or an untyped constant. +A constant size argument must be non-negative and representable +by a value of type int; if it is an untyped constant it is given type int. +If both n and m are provided and are constant, then +n must be no larger than m. +If n is negative or larger than m at run time, +a run-time panic occurs. +

+ +
+s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
+s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
+s := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int
+s := make([]int, 10, 0)         // illegal: len(s) > cap(s)
+c := make(chan int, 10)         // channel with a buffer size of 10
+m := make(map[string]int, 100)  // map with initial space for approximately 100 elements
+
+ +

+Calling make with a map type and size hint n will +create a map with initial space to hold n map elements. +The precise behavior is implementation-dependent. +

+ + +

Appending to and copying slices

+ +

+The built-in functions append and copy assist in +common slice operations. +For both functions, the result is independent of whether the memory referenced +by the arguments overlaps. +

+ +

+The variadic function append +appends zero or more values x +to s of type S, which must be a slice type, and +returns the resulting slice, also of type S. +The values x are passed to a parameter of type ...T +where T is the element type of +S and the respective +parameter passing rules apply. +As a special case, append also accepts a first argument +assignable to type []byte with a second argument of +string type followed by .... This form appends the +bytes of the string. +

+ +
+append(s S, x ...T) S  // T is the element type of S
+
+ +

+If the capacity of s is not large enough to fit the additional +values, append allocates a new, sufficiently large underlying +array that fits both the existing slice elements and the additional values. +Otherwise, append re-uses the underlying array. +

+ +
+s0 := []int{0, 0}
+s1 := append(s0, 2)                // append a single element     s1 == []int{0, 0, 2}
+s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
+s3 := append(s2, s0...)            // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
+s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
+
+var t []interface{}
+t = append(t, 42, 3.1415, "foo")   //                             t == []interface{}{42, 3.1415, "foo"}
+
+var b []byte
+b = append(b, "bar"...)            // append string contents      b == []byte{'b', 'a', 'r' }
+
+ +

+The function copy copies slice elements from +a source src to a destination dst and returns the +number of elements copied. +Both arguments must have identical element type T and must be +assignable to a slice of type []T. +The number of elements copied is the minimum of +len(src) and len(dst). +As a special case, copy also accepts a destination argument assignable +to type []byte with a source argument of a string type. +This form copies the bytes from the string into the byte slice. +

+ +
+copy(dst, src []T) int
+copy(dst []byte, src string) int
+
+ +

+Examples: +

+ +
+var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
+var s = make([]int, 6)
+var b = make([]byte, 5)
+n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
+n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
+n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")
+
+ + +

Deletion of map elements

+ +

+The built-in function delete removes the element with key +k from a map m. The +type of k must be assignable +to the key type of m. +

+ +
+delete(m, k)  // remove element m[k] from map m
+
+ +

+If the map m is nil or the element m[k] +does not exist, delete is a no-op. +

+ + +

Manipulating complex numbers

+ +

+Three functions assemble and disassemble complex numbers. +The built-in function complex constructs a complex +value from a floating-point real and imaginary part, while +real and imag +extract the real and imaginary parts of a complex value. +

+ +
+complex(realPart, imaginaryPart floatT) complexT
+real(complexT) floatT
+imag(complexT) floatT
+
+ +

+The type of the arguments and return value correspond. +For complex, the two arguments must be of the same +floating-point type and the return type is the complex type +with the corresponding floating-point constituents: +complex64 for float32 arguments, and +complex128 for float64 arguments. +If one of the arguments evaluates to an untyped constant, it is first implicitly +converted to the type of the other argument. +If both arguments evaluate to untyped constants, they must be non-complex +numbers or their imaginary parts must be zero, and the return value of +the function is an untyped complex constant. +

+ +

+For real and imag, the argument must be +of complex type, and the return type is the corresponding floating-point +type: float32 for a complex64 argument, and +float64 for a complex128 argument. +If the argument evaluates to an untyped constant, it must be a number, +and the return value of the function is an untyped floating-point constant. +

+ +

+The real and imag functions together form the inverse of +complex, so for a value z of a complex type Z, +z == Z(complex(real(z), imag(z))). +

+ +

+If the operands of these functions are all constants, the return +value is a constant. +

+ +
+var a = complex(2, -2)             // complex128
+const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
+x := float32(math.Cos(math.Pi/2))  // float32
+var c64 = complex(5, -x)           // complex64
+var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
+_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift
+var rl = real(c64)                 // float32
+var im = imag(a)                   // float64
+const c = imag(b)                  // untyped constant -1.4
+_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift
+
+ +

Handling panics

+ +

Two built-in functions, panic and recover, +assist in reporting and handling run-time panics +and program-defined error conditions. +

+ +
+func panic(interface{})
+func recover() interface{}
+
+ +

+While executing a function F, +an explicit call to panic or a run-time panic +terminates the execution of F. +Any functions deferred by F +are then executed as usual. +Next, any deferred functions run by F's caller are run, +and so on up to any deferred by the top-level function in the executing goroutine. +At that point, the program is terminated and the error +condition is reported, including the value of the argument to panic. +This termination sequence is called panicking. +

+ +
+panic(42)
+panic("unreachable")
+panic(Error("cannot parse"))
+
+ +

+The recover function allows a program to manage behavior +of a panicking goroutine. +Suppose a function G defers a function D that calls +recover and a panic occurs in a function on the same goroutine in which G +is executing. +When the running of deferred functions reaches D, +the return value of D's call to recover will be the value passed to the call of panic. +If D returns normally, without starting a new +panic, the panicking sequence stops. In that case, +the state of functions called between G and the call to panic +is discarded, and normal execution resumes. +Any functions deferred by G before D are then run and G's +execution terminates by returning to its caller. +

+ +

+The return value of recover is nil if any of the following conditions holds: +

+
    +
  • +panic's argument was nil; +
  • +
  • +the goroutine is not panicking; +
  • +
  • +recover was not called directly by a deferred function. +
  • +
+ +

+The protect function in the example below invokes +the function argument g and protects callers from +run-time panics raised by g. +

+ +
+func protect(g func()) {
+	defer func() {
+		log.Println("done")  // Println executes normally even if there is a panic
+		if x := recover(); x != nil {
+			log.Printf("run time panic: %v", x)
+		}
+	}()
+	log.Println("start")
+	g()
+}
+
+ + +

Bootstrapping

+ +

+Current implementations provide several built-in functions useful during +bootstrapping. These functions are documented for completeness but are not +guaranteed to stay in the language. They do not return a result. +

+ +
+Function   Behavior
+
+print      prints all arguments; formatting of arguments is implementation-specific
+println    like print but prints spaces between arguments and a newline at the end
+
+ +

+Implementation restriction: print and println need not +accept arbitrary argument types, but printing of boolean, numeric, and string +types must be supported. +

+ +

Packages

+ +

+Go programs are constructed by linking together packages. +A package in turn is constructed from one or more source files +that together declare constants, types, variables and functions +belonging to the package and which are accessible in all files +of the same package. Those elements may be +exported and used in another package. +

+ +

Source file organization

+ +

+Each source file consists of a package clause defining the package +to which it belongs, followed by a possibly empty set of import +declarations that declare packages whose contents it wishes to use, +followed by a possibly empty set of declarations of functions, +types, variables, and constants. +

+ +
+SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
+
+ +

Package clause

+ +

+A package clause begins each source file and defines the package +to which the file belongs. +

+ +
+PackageClause  = "package" PackageName .
+PackageName    = identifier .
+
+ +

+The PackageName must not be the blank identifier. +

+ +
+package math
+
+ +

+A set of files sharing the same PackageName form the implementation of a package. +An implementation may require that all source files for a package inhabit the same directory. +

+ +

Import declarations

+ +

+An import declaration states that the source file containing the declaration +depends on functionality of the imported package +(§Program initialization and execution) +and enables access to exported identifiers +of that package. +The import names an identifier (PackageName) to be used for access and an ImportPath +that specifies the package to be imported. +

+ +
+ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
+ImportSpec       = [ "." | PackageName ] ImportPath .
+ImportPath       = string_lit .
+
+ +

+The PackageName is used in qualified identifiers +to access exported identifiers of the package within the importing source file. +It is declared in the file block. +If the PackageName is omitted, it defaults to the identifier specified in the +package clause of the imported package. +If an explicit period (.) appears instead of a name, all the +package's exported identifiers declared in that package's +package block will be declared in the importing source +file's file block and must be accessed without a qualifier. +

+ +

+The interpretation of the ImportPath is implementation-dependent but +it is typically a substring of the full file name of the compiled +package and may be relative to a repository of installed packages. +

+ +

+Implementation restriction: A compiler may restrict ImportPaths to +non-empty strings using only characters belonging to +Unicode's +L, M, N, P, and S general categories (the Graphic characters without +spaces) and may also exclude the characters +!"#$%&'()*,:;<=>?[\]^`{|} +and the Unicode replacement character U+FFFD. +

+ +

+Assume we have compiled a package containing the package clause +package math, which exports function Sin, and +installed the compiled package in the file identified by +"lib/math". +This table illustrates how Sin is accessed in files +that import the package after the +various types of import declaration. +

+ +
+Import declaration          Local name of Sin
+
+import   "lib/math"         math.Sin
+import m "lib/math"         m.Sin
+import . "lib/math"         Sin
+
+ +

+An import declaration declares a dependency relation between +the importing and imported package. +It is illegal for a package to import itself, directly or indirectly, +or to directly import a package without +referring to any of its exported identifiers. To import a package solely for +its side-effects (initialization), use the blank +identifier as explicit package name: +

+ +
+import _ "lib/math"
+
+ + +

An example package

+ +

+Here is a complete Go package that implements a concurrent prime sieve. +

+ +
+package main
+
+import "fmt"
+
+// Send the sequence 2, 3, 4, … to channel 'ch'.
+func generate(ch chan<- int) {
+	for i := 2; ; i++ {
+		ch <- i  // Send 'i' to channel 'ch'.
+	}
+}
+
+// Copy the values from channel 'src' to channel 'dst',
+// removing those divisible by 'prime'.
+func filter(src <-chan int, dst chan<- int, prime int) {
+	for i := range src {  // Loop over values received from 'src'.
+		if i%prime != 0 {
+			dst <- i  // Send 'i' to channel 'dst'.
+		}
+	}
+}
+
+// The prime sieve: Daisy-chain filter processes together.
+func sieve() {
+	ch := make(chan int)  // Create a new channel.
+	go generate(ch)       // Start generate() as a subprocess.
+	for {
+		prime := <-ch
+		fmt.Print(prime, "\n")
+		ch1 := make(chan int)
+		go filter(ch, ch1, prime)
+		ch = ch1
+	}
+}
+
+func main() {
+	sieve()
+}
+
+ +

Program initialization and execution

+ +

The zero value

+

+When storage is allocated for a variable, +either through a declaration or a call of new, or when +a new value is created, either through a composite literal or a call +of make, +and no explicit initialization is provided, the variable or value is +given a default value. Each element of such a variable or value is +set to the zero value for its type: false for booleans, +0 for numeric types, "" +for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. +This initialization is done recursively, so for instance each element of an +array of structs will have its fields zeroed if no value is specified. +

+

+These two simple declarations are equivalent: +

+ +
+var i int
+var i int = 0
+
+ +

+After +

+ +
+type T struct { i int; f float64; next *T }
+t := new(T)
+
+ +

+the following holds: +

+ +
+t.i == 0
+t.f == 0.0
+t.next == nil
+
+ +

+The same would also be true after +

+ +
+var t T
+
+ +

Package initialization

+ +

+Within a package, package-level variable initialization proceeds stepwise, +with each step selecting the variable earliest in declaration order +which has no dependencies on uninitialized variables. +

+ +

+More precisely, a package-level variable is considered ready for +initialization if it is not yet initialized and either has +no initialization expression or +its initialization expression has no dependencies on uninitialized variables. +Initialization proceeds by repeatedly initializing the next package-level +variable that is earliest in declaration order and ready for initialization, +until there are no variables ready for initialization. +

+ +

+If any variables are still uninitialized when this +process ends, those variables are part of one or more initialization cycles, +and the program is not valid. +

+ +

+Multiple variables on the left-hand side of a variable declaration initialized +by single (multi-valued) expression on the right-hand side are initialized +together: If any of the variables on the left-hand side is initialized, all +those variables are initialized in the same step. +

+ +
+var x = a
+var a, b = f() // a and b are initialized together, before x is initialized
+
+ +

+For the purpose of package initialization, blank +variables are treated like any other variables in declarations. +

+ +

+The declaration order of variables declared in multiple files is determined +by the order in which the files are presented to the compiler: Variables +declared in the first file are declared before any of the variables declared +in the second file, and so on. +

+ +

+Dependency analysis does not rely on the actual values of the +variables, only on lexical references to them in the source, +analyzed transitively. For instance, if a variable x's +initialization expression refers to a function whose body refers to +variable y then x depends on y. +Specifically: +

+ +
    +
  • +A reference to a variable or function is an identifier denoting that +variable or function. +
  • + +
  • +A reference to a method m is a +method value or +method expression of the form +t.m, where the (static) type of t is +not an interface type, and the method m is in the +method set of t. +It is immaterial whether the resulting function value +t.m is invoked. +
  • + +
  • +A variable, function, or method x depends on a variable +y if x's initialization expression or body +(for functions and methods) contains a reference to y +or to a function or method that depends on y. +
  • +
+ +

+For example, given the declarations +

+ +
+var (
+	a = c + b  // == 9
+	b = f()    // == 4
+	c = f()    // == 5
+	d = 3      // == 5 after initialization has finished
+)
+
+func f() int {
+	d++
+	return d
+}
+
+ +

+the initialization order is d, b, c, a. +Note that the order of subexpressions in initialization expressions is irrelevant: +a = c + b and a = b + c result in the same initialization +order in this example. +

+ +

+Dependency analysis is performed per package; only references referring +to variables, functions, and (non-interface) methods declared in the current +package are considered. If other, hidden, data dependencies exists between +variables, the initialization order between those variables is unspecified. +

+ +

+For instance, given the declarations +

+ +
+var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
+var _ = sideEffect()  // unrelated to x, a, or b
+var a = b
+var b = 42
+
+type I interface      { ab() []int }
+type T struct{}
+func (T) ab() []int   { return []int{a, b} }
+
+ +

+the variable a will be initialized after b but +whether x is initialized before b, between +b and a, or after a, and +thus also the moment at which sideEffect() is called (before +or after x is initialized) is not specified. +

+ +

+Variables may also be initialized using functions named init +declared in the package block, with no arguments and no result parameters. +

+ +
+func init() { … }
+
+ +

+Multiple such functions may be defined per package, even within a single +source file. In the package block, the init identifier can +be used only to declare init functions, yet the identifier +itself is not declared. Thus +init functions cannot be referred to from anywhere +in a program. +

+ +

+A package with no imports is initialized by assigning initial values +to all its package-level variables followed by calling all init +functions in the order they appear in the source, possibly in multiple files, +as presented to the compiler. +If a package has imports, the imported packages are initialized +before initializing the package itself. If multiple packages import +a package, the imported package will be initialized only once. +The importing of packages, by construction, guarantees that there +can be no cyclic initialization dependencies. +

+ +

+Package initialization—variable initialization and the invocation of +init functions—happens in a single goroutine, +sequentially, one package at a time. +An init function may launch other goroutines, which can run +concurrently with the initialization code. However, initialization +always sequences +the init functions: it will not invoke the next one +until the previous one has returned. +

+ +

+To ensure reproducible initialization behavior, build systems are encouraged +to present multiple files belonging to the same package in lexical file name +order to a compiler. +

+ + +

Program execution

+

+A complete program is created by linking a single, unimported package +called the main package with all the packages it imports, transitively. +The main package must +have package name main and +declare a function main that takes no +arguments and returns no value. +

+ +
+func main() { … }
+
+ +

+Program execution begins by initializing the main package and then +invoking the function main. +When that function invocation returns, the program exits. +It does not wait for other (non-main) goroutines to complete. +

+ +

Errors

+ +

+The predeclared type error is defined as +

+ +
+type error interface {
+	Error() string
+}
+
+ +

+It is the conventional interface for representing an error condition, +with the nil value representing no error. +For instance, a function to read data from a file might be defined: +

+ +
+func Read(f *File, b []byte) (n int, err error)
+
+ +

Run-time panics

+ +

+Execution errors such as attempting to index an array out +of bounds trigger a run-time panic equivalent to a call of +the built-in function panic +with a value of the implementation-defined interface type runtime.Error. +That type satisfies the predeclared interface type +error. +The exact error values that +represent distinct run-time error conditions are unspecified. +

+ +
+package runtime
+
+type Error interface {
+	error
+	// and perhaps other methods
+}
+
+ +

System considerations

+ +

Package unsafe

+ +

+The built-in package unsafe, known to the compiler +and accessible through the import path "unsafe", +provides facilities for low-level programming including operations +that violate the type system. A package using unsafe +must be vetted manually for type safety and may not be portable. +The package provides the following interface: +

+ +
+package unsafe
+
+type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
+type Pointer *ArbitraryType
+
+func Alignof(variable ArbitraryType) uintptr
+func Offsetof(selector ArbitraryType) uintptr
+func Sizeof(variable ArbitraryType) uintptr
+
+type IntegerType int  // shorthand for an integer type; it is not a real type
+func Add(ptr Pointer, len IntegerType) Pointer
+func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
+
+ +

+A Pointer is a pointer type but a Pointer +value may not be dereferenced. +Any pointer or value of underlying type uintptr can be converted to +a type of underlying type Pointer and vice versa. +The effect of converting between Pointer and uintptr is implementation-defined. +

+ +
+var f float64
+bits = *(*uint64)(unsafe.Pointer(&f))
+
+type ptr unsafe.Pointer
+bits = *(*uint64)(ptr(&f))
+
+var p ptr = nil
+
+ +

+The functions Alignof and Sizeof take an expression x +of any type and return the alignment or size, respectively, of a hypothetical variable v +as if v was declared via var v = x. +

+

+The function Offsetof takes a (possibly parenthesized) selector +s.f, denoting a field f of the struct denoted by s +or *s, and returns the field offset in bytes relative to the struct's address. +If f is an embedded field, it must be reachable +without pointer indirections through fields of the struct. +For a struct s with field f: +

+ +
+uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
+
+ +

+Computer architectures may require memory addresses to be aligned; +that is, for addresses of a variable to be a multiple of a factor, +the variable's type's alignment. The function Alignof +takes an expression denoting a variable of any type and returns the +alignment of the (type of the) variable in bytes. For a variable +x: +

+ +
+uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
+
+ +

+Calls to Alignof, Offsetof, and +Sizeof are compile-time constant expressions of type uintptr. +

+ +

+The function Add adds len to ptr +and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). +The len argument must be of integer type or an untyped constant. +A constant len argument must be representable by a value of type int; +if it is an untyped constant it is given type int. +The rules for valid uses of Pointer still apply. +

+ +

+The function Slice returns a slice whose underlying array starts at ptr +and whose length and capacity are len. +Slice(ptr, len) is equivalent to +

+ +
+(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
+
+ +

+except that, as a special case, if ptr +is nil and len is zero, +Slice returns nil. +

+ +

+The len argument must be of integer type or an untyped constant. +A constant len argument must be non-negative and representable by a value of type int; +if it is an untyped constant it is given type int. +At run time, if len is negative, +or if ptr is nil and len is not zero, +a run-time panic occurs. +

+ +

Size and alignment guarantees

+ +

+For the numeric types, the following sizes are guaranteed: +

+ +
+type                                 size in bytes
+
+byte, uint8, int8                     1
+uint16, int16                         2
+uint32, int32, float32                4
+uint64, int64, float64, complex64     8
+complex128                           16
+
+ +

+The following minimal alignment properties are guaranteed: +

+
    +
  1. For a variable x of any type: unsafe.Alignof(x) is at least 1. +
  2. + +
  3. For a variable x of struct type: unsafe.Alignof(x) is the largest of + all the values unsafe.Alignof(x.f) for each field f of x, but at least 1. +
  4. + +
  5. For a variable x of array type: unsafe.Alignof(x) is the same as + the alignment of a variable of the array's element type. +
  6. +
+ +

+A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory. +

diff --git a/doc/go1.20.html b/doc/go1.20.html new file mode 100644 index 00000000000000..71850129c7a5ba --- /dev/null +++ b/doc/go1.20.html @@ -0,0 +1,295 @@ + + + + + + +

DRAFT RELEASE NOTES — Introduction to Go 1.20

+ +

+ + Go 1.20 is not yet released. These are work-in-progress + release notes. Go 1.20 is expected to be released in February 2023. + +

+ +

Changes to the language

+ +

+ TODO: complete this section +

+ +

Ports

+ +

+ TODO: complete this section, or delete if not needed +

+ +

Tools

+ +

Go command

+ +

+ TODO: https://go.dev/cl/421440: cmd/go: add go generate -skip flag +

+ +

+ TODO: https://go.dev/issue/45454: provide build tags for architecture environment variables +

+ +

Vet

+ +

+ TODO: https://go.dev/issue/48801: check for time formats with 2006-02-01 +

+ +

Runtime

+ +

+ TODO: complete this section, or delete if not needed +

+ +

Compiler

+ +

+ TODO: https://go.dev/issue/49390: clarify whether "-l" and "-N" compiler flags are actually supported +

+ +

Linker

+ +

+ TODO: complete this section, or delete if not needed +

+ +

Bootstrap

+ +

+ TODO: https://go.dev/issue/44505: adopt Go 1.17 as bootstrap toolchain for Go 1.20 +

+ +

Core library

+ +

New crypto/ecdh package

+ +

+ TODO: complete this section +

+ +

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. + There are also various performance improvements, not enumerated here. +

+ +

+ TODO: complete this section +

+ +
bytes
+
+

+ TODO: https://go.dev/cl/407176: strings, bytes: add CutPrefix and CutSuffix +

+ +

+ TODO: https://go.dev/cl/359675: bytes: add Clone function +

+
+
+ +
crypto/elliptic
+
+

+ TODO: https://go.dev/issue/34648: automatically upgrade CurveParams for known curves and deprecate custom ones +

+
+
+ +
crypto/subtle
+
+

+ TODO: https://go.dev/issue/53021: add XORBytes +

+ +

+ TODO: https://go.dev/cl/421435: crypto/subtle: add XORBytes; modified api/next/53021.txt +

+
+
+ +
debug/elf
+
+

+ TODO: https://go.dev/cl/420982: debug/elf: add new-style LoongArch reloc types; modified api/next/54222.txt +

+
+
+ +
debug/pe
+
+

+ TODO: https://go.dev/cl/421357: debug/pe: add IMAGE_FILE_MACHINE_RISCV{32,64,128}; modified api/next/54251.txt +

+
+
+ +
encoding/binary
+
+

+ TODO: https://go.dev/cl/420274: encoding/binary: ReadUvarint return io.ErrUnexpectedEOF when read at least 1 byte +

+
+
+ +
encoding/xml
+
+

+ TODO: https://go.dev/issue/53346: Add (*Encoder).Close() to check for unclosed elements +

+ +

+ TODO: https://go.dev/cl/424777: encoding/xml: add (*Encoder).Close +

+
+
+ +
fmt
+
+

+ TODO: https://go.dev/issue/51668: add FormatString(State) string +

+ +

+ TODO: https://go.dev/cl/400875: fmt: add a function to recover the original format string given a State; modified api/next/51668.txt +

+
+
+ +
go/build
+
+

+ TODO: https://go.dev/cl/421434: go/build: add GO$GOARCH-based ToolTags +

+
+
+ +
go/token
+
+

+ TODO: https://go.dev/cl/410114: go/token: add (*FileSet).RemoveFile(*File) method +

+
+
+ +
io
+
+

+ TODO: https://go.dev/issue/45899: add OffsetWriter, NewOffsetWriter +

+ +

+ TODO: https://go.dev/cl/406776: io: add OffsetWriter, NewOffsetWriter; modified api/next/45899.txt +

+
+
+ +
net/http
+
+

+ TODO: https://go.dev/issue/41773: add Server.OptionsHandler to allow custom handling of OPTIONS * +

+ +

+ TODO: https://go.dev/cl/356410: net/http: add Server.DisableOptionsHandler for custom handling of OPTIONS *; modified api/next/41773.txt +

+
+
+ +
net/http/httputil
+
+

+ TODO: https://go.dev/issue/50465: add X-Forwarded-Proto and X-Forwarded-Host by default +

+ +

+ TODO: https://go.dev/issue/53002: replace Director with Rewrite +

+ +

+ TODO: https://go.dev/cl/407214: net/http/httputil: add ReverseProxy.Rewrite; modified api/next/53002.txt +

+ +

+ TODO: https://go.dev/cl/407414: net/http/httputil: add X-Forwarded-{Host,Proto} headers in ReverseProxy +

+
+
+ +
strconv
+
+

+ TODO: https://go.dev/cl/345488: strconv: optimize Parse for []byte arguments +

+
+
+ +
strings
+
+

+ TODO: https://go.dev/issue/42537: add CutPrefix and CutSuffix +

+ +

+ TODO: https://go.dev/issue/45038: bytes, strings: add Clone +

+
+
+ +
syscall
+
+

+ TODO: https://go.dev/cl/407574: syscall: add new CLONE_ flags for Linux +

+
+
+ +
time
+
+

+ TODO: https://go.dev/issue/52746: add DateTime, DateOnly, TimeOnly format constants +

+ +

+ TODO: https://go.dev/cl/412495: time: add DateTime, DateOnly, and TimeOnly +

+
+
+ +
unicode/utf16
+
+

+ TODO: https://go.dev/issue/51896: add AppendRune +

+ +

+ TODO: https://go.dev/cl/409054: unicode/utf16: add AppendRune; modified api/next/51896.txt +

+
+
+ + + + diff --git a/doc/go_mem.html b/doc/go_mem.html index 5f1eb68af395bf..661e1e781cff60 100644 --- a/doc/go_mem.html +++ b/doc/go_mem.html @@ -1,6 +1,6 @@ @@ -8,12 +8,9 @@ p.rule { font-style: italic; } -span.event { - font-style: italic; -} -

Introduction

+

Introduction

The Go memory model specifies the conditions under which @@ -22,7 +19,7 @@

Introduction

-

Advice

+

Advice

Programs that modify data being simultaneously accessed by multiple goroutines @@ -44,90 +41,237 @@

Advice

Don't be clever.

-

Happens Before

+

Informal Overview

-Within a single goroutine, reads and writes must behave -as if they executed in the order specified by the program. -That is, compilers and processors may reorder the reads and writes -executed within a single goroutine only when the reordering -does not change the behavior within that goroutine -as defined by the language specification. -Because of this reordering, the execution order observed -by one goroutine may differ from the order perceived -by another. For example, if one goroutine -executes a = 1; b = 2;, another might observe -the updated value of b before the updated value of a. +Go approaches its memory model in much the same way as the rest of the language, +aiming to keep the semantics simple, understandable, and useful. +This section gives a general overview of the approach and should suffice for most programmers. +The memory model is specified more formally in the next section.

-To specify the requirements of reads and writes, we define -happens before, a partial order on the execution -of memory operations in a Go program. If event e1 happens -before event e2, then we say that e2 happens after e1. -Also, if e1 does not happen before e2 and does not happen -after e2, then we say that e1 and e2 happen concurrently. +A data race is defined as +a write to a memory location happening concurrently with another read or write to that same location, +unless all the accesses involved are atomic data accesses as provided by the sync/atomic package. +As noted already, programmers are strongly encouraged to use appropriate synchronization +to avoid data races. +In the absence of data races, Go programs behave as if all the goroutines +were multiplexed onto a single processor. +This property is sometimes referred to as DRF-SC: data-race-free programs +execute in a sequentially consistent manner.

-

-Within a single goroutine, the happens-before order is the -order expressed by the program. +

+While programmers should write Go programs without data races, +there are limitations to what a Go implementation can do in response to a data race. +An implementation may always react to a data race by reporting the race and terminating the program. +Otherwise, each read of a single-word-sized or sub-word-sized memory location +must observe a value actually written to that location (perhaps by a concurrent executing goroutine) +and not yet overwritten. +These implementation constraints make Go more like Java or JavaScript, +in that most races have a limited number of outcomes, +and less like C and C++, where the meaning of any program with a race +is entirely undefined, and the compiler may do anything at all. +Go's approach aims to make errant programs more reliable and easier to debug, +while still insisting that races are errors and that tools can diagnose and report them.

+

Memory Model

+

-A read r of a variable v is allowed to observe a write w to v -if both of the following hold: +The following formal definition of Go's memory model closely follows +the approach presented by Hans-J. Boehm and Sarita V. Adve in +“Foundations of the C++ Concurrency Memory Model”, +published in PLDI 2008. +The definition of data-race-free programs and the guarantee of sequential consistency +for race-free programs are equivalent to the ones in that work.

-
    -
  1. r does not happen before w.
  2. -
  3. There is no other write w' to v that happens - after w but before r.
  4. -
+

+The memory model describes the requirements on program executions, +which are made up of goroutine executions, +which in turn are made up of memory operations. +

+ +

+A memory operation is modeled by four details: +

+
    +
  • its kind, indicating whether it is an ordinary data read, an ordinary data write, +or a synchronizing operation such as an atomic data access, +a mutex operation, or a channel operation, +
  • its location in the program, +
  • the memory location or variable being accessed, and +
  • the values read or written by the operation. +
+

+Some memory operations are read-like, including read, atomic read, mutex lock, and channel receive. +Other memory operations are write-like, including write, atomic write, mutex unlock, channel send, and channel close. +Some, such as atomic compare-and-swap, are both read-like and write-like. +

+ +

+A goroutine execution is modeled as a set of memory operations executed by a single goroutine. +

+ +

+Requirement 1: +The memory operations in each goroutine must correspond to a correct sequential execution of that goroutine, +given the values read from and written to memory. +That execution must be consistent with the sequenced before relation, +defined as the partial order requirements set out by the Go language specification +for Go's control flow constructs as well as the order of evaluation for expressions. +

+ +

+A Go program execution is modeled as a set of goroutine executions, +together with a mapping W that specifies the write-like operation that each read-like operation reads from. +(Multiple executions of the same program can have different program executions.) +

+ +

+Requirement 2: +For a given program execution, the mapping W, when limited to synchronizing operations, +must be explainable by some implicit total order of the synchronizing operations +that is consistent with sequencing and the values read and written by those operations. +

-To guarantee that a read r of a variable v observes a -particular write w to v, ensure that w is the only -write r is allowed to observe. -That is, r is guaranteed to observe w if both of the following hold: +The synchronized before relation is a partial order on synchronizing memory operations, +derived from W. +If a synchronizing read-like memory operation r +observes a synchronizing write-like memory operation w +(that is, if W(r) = w), +then w is synchronized before r. +Informally, the synchronized before relation is a subset of the implied total order +mentioned in the previous paragraph, +limited to the information that W directly observes.

+

+The happens before relation is defined as the transitive closure of the +union of the sequenced before and synchronized before relations. +

+ +

+Requirement 3: +For an ordinary (non-synchronizing) data read r on a memory location x, +W(r) must be a write w that is visible to r, +where visible means that both of the following hold: +

    -
  1. w happens before r.
  2. -
  3. Any other write to the shared variable v -either happens before w or after r.
  4. +
  5. w happens before r. +
  6. w does not happen before any other write w' (to x) that happens before r.

-This pair of conditions is stronger than the first pair; -it requires that there are no other writes happening -concurrently with w or r. +A read-write data race on memory location x +consists of a read-like memory operation r on x +and a write-like memory operation w on x, +at least one of which is non-synchronizing, +which are unordered by happens before +(that is, neither r happens before w +nor w happens before r). +

+ +

+A write-write data race on memory location x +consists of two write-like memory operations w and w' on x, +at least one of which is non-synchronizing, +which are unordered by happens before. +

+ +

+Note that if there are no read-write or write-write data races on memory location x, +then any read r on x has only one possible W(r): +the single w that immediately precedes it in the happens before order. +

+ +

+More generally, it can be shown that any Go program that is data-race-free, +meaning it has no program executions with read-write or write-write data races, +can only have outcomes explained by some sequentially consistent interleaving +of the goroutine executions. +(The proof is the same as Section 7 of Boehm and Adve's paper cited above.) +This property is called DRF-SC. +

+ +

+The intent of the formal definition is to match +the DRF-SC guarantee provided to race-free programs +by other languages, including C, C++, Java, JavaScript, Rust, and Swift. +

+ +

+Certain Go language operations such as goroutine creation and memory allocation +act as synchronization operations. +The effect of these operations on the synchronized-before partial order +is documented in the “Synchronization” section below. +Individual packages are responsible for providing similar documentation +for their own operations. +

+ +

Implementation Restrictions for Programs Containing Data Races

+ +

+The preceding section gave a formal definition of data-race-free program execution. +This section informally describes the semantics that implementations must provide +for programs that do contain races. +

+ +

+First, any implementation can, upon detecting a data race, +report the race and halt execution of the program. +Implementations using ThreadSanitizer +(accessed with “go build -race”) +do exactly this. +

+ +

+Otherwise, a read r of a memory location x +that is not larger than a machine word must observe +some write w such that r does not happen before w +and there is no write w' such that w happens before w' +and w' happens before r. +That is, each read must observe a value written by a preceding or concurrent write. +

+ +

+Additionally, observation of acausal and “out of thin air” writes is disallowed.

-Within a single goroutine, -there is no concurrency, so the two definitions are equivalent: -a read r observes the value written by the most recent write w to v. -When multiple goroutines access a shared variable v, -they must use synchronization events to establish -happens-before conditions that ensure reads observe the -desired writes. +Reads of memory locations larger than a single machine word +are encouraged but not required to meet the same semantics +as word-sized memory locations, +observing a single allowed write w. +For performance reasons, +implementations may instead treat larger operations +as a set of individual machine-word-sized operations +in an unspecified order. +This means that races on multiword data structures +can lead to inconsistent values not corresponding to a single write. +When the values depend on the consistency +of internal (pointer, length) or (pointer, type) pairs, +as can be the case for interface values, maps, +slices, and strings in most Go implementations, +such races can in turn lead to arbitrary memory corruption.

-The initialization of variable v with the zero value -for v's type behaves as a write in the memory model. +Examples of incorrect synchronization are given in the +“Incorrect synchronization” section below.

-Reads and writes of values larger than a single machine word -behave as multiple machine-word-sized operations in an -unspecified order. +Examples of the limitations on implementations are given in the +“Incorrect compilation” section below.

-

Synchronization

+

Synchronization

-

Initialization

+

Initialization

Program initialization runs in a single goroutine, @@ -141,15 +285,15 @@

Initialization

-The start of the function main.main happens after -all init functions have finished. +The completion of all init functions is synchronized before +the start of the function main.main.

-

Goroutine creation

+

Goroutine creation

The go statement that starts a new goroutine -happens before the goroutine's execution begins. +is synchronized before the start of the goroutine's execution.

@@ -174,11 +318,12 @@

Goroutine creation

at some point in the future (perhaps after hello has returned).

-

Goroutine destruction

+

Goroutine destruction

-The exit of a goroutine is not guaranteed to happen before -any event in the program. For example, in this program: +The exit of a goroutine is not guaranteed to be synchronized before +any event in the program. +For example, in this program:

@@ -203,7 +348,7 @@ 

Goroutine destruction

communication to establish a relative ordering.

-

Channel communication

+

Channel communication

Channel communication is the main method of synchronization @@ -213,8 +358,8 @@

Channel communication

-A send on a channel happens before the corresponding -receive from that channel completes. +A send on a channel is synchronized before the completion of the +corresponding receive from that channel.

@@ -239,13 +384,13 @@

Channel communication

is guaranteed to print "hello, world". The write to a -happens before the send on c, which happens before -the corresponding receive on c completes, which happens before +is sequenced before the send on c, which is synchronized before +the corresponding receive on c completes, which is sequenced before the print.

-The closing of a channel happens before a receive that returns a zero value +The closing of a channel is synchronized before a receive that returns a zero value because the channel is closed.

@@ -256,8 +401,8 @@

Channel communication

-A receive from an unbuffered channel happens before -the send on that channel completes. +A receive from an unbuffered channel is synchronized before the completion of +the corresponding send on that channel.

@@ -283,8 +428,8 @@

Channel communication

is also guaranteed to print "hello, world". The write to a -happens before the receive on c, which happens before -the corresponding send on c completes, which happens +is sequenced before the receive on c, which is synchronized before +the corresponding send on c completes, which is sequenced before the print.

@@ -296,7 +441,7 @@

Channel communication

-The kth receive on a channel with capacity C happens before the k+Cth send from that channel completes. +The kth receive on a channel with capacity C is synchronized before the completion of the k+Cth send from that channel completes.

@@ -330,7 +475,7 @@

Channel communication

}
-

Locks

+

Locks

The sync package implements two lock data types, @@ -339,7 +484,7 @@

Locks

For any sync.Mutex or sync.RWMutex variable l and n < m, -call n of l.Unlock() happens before call m of l.Lock() returns. +call n of l.Unlock() is synchronized before call m of l.Lock() returns.

@@ -365,19 +510,29 @@

Locks

is guaranteed to print "hello, world". -The first call to l.Unlock() (in f) happens +The first call to l.Unlock() (in f) is synchronized before the second call to l.Lock() (in main) returns, -which happens before the print. +which is sequenced before the print.

For any call to l.RLock on a sync.RWMutex variable l, -there is an n such that the l.RLock happens (returns) after call n to -l.Unlock and the matching l.RUnlock happens -before call n+1 to l.Lock. +there is an n such that the nth call to l.Unlock +is synchronized before the return from l.RLock, +and the matching call to l.RUnlock is synchronized before the return from call n+1 to l.Lock.

-

Once

+

+A successful call to l.TryLock (or l.TryRLock) +is equivalent to a call to l.Lock (or l.RLock). +An unsuccessful call has no synchronizing effect at all. +As far as the memory model is concerned, +l.TryLock (or l.TryRLock) +may be considered to be able to return false +even when the mutex l is unlocked. +

+ +

Once

The sync package provides a safe mechanism for @@ -389,7 +544,8 @@

Once

-A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns. +The completion of a single call of f() from once.Do(f) +is synchronized before the return of any call of once.Do(f).

@@ -424,13 +580,60 @@

Once

twice.

-

Incorrect synchronization

+

Atomic Values

+ +

+The APIs in the sync/atomic +package are collectively “atomic operations” +that can be used to synchronize the execution of different goroutines. +If the effect of an atomic operation A is observed by atomic operation B, +then A is synchronized before B. +All the atomic operations executed in a program behave as though executed +in some sequentially consistent order. +

+ +

+The preceding definition has the same semantics as C++’s sequentially consistent atomics +and Java’s volatile variables. +

+ +

Finalizers

+ +

+The runtime package provides +a SetFinalizer function that adds a finalizer to be called when +a particular object is no longer reachable by the program. +A call to SetFinalizer(x, f) is synchronized before the finalization call f(x). +

+ +

Additional Mechanisms

+ +

+The sync package provides additional synchronization abstractions, +including condition variables, +lock-free maps, +allocation pools, +and +wait groups. +The documentation for each of these specifies the guarantees it +makes concerning synchronization. +

+ +

+Other packages that provide synchronization abstractions +should document the guarantees they make too. +

+ + +

Incorrect synchronization

-Note that a read r may observe the value written by a write w -that happens concurrently with r. -Even if this occurs, it does not imply that reads happening after r -will observe writes that happened before w. +Programs with races are incorrect and +can exhibit non-sequentially consistent executions. +In particular, note that a read r may observe the value written by any write w +that executes concurrently with r. +Even if this occurs, it does not imply that reads happening after r +will observe writes that happened before w.

@@ -566,3 +769,197 @@

Incorrect synchronization

In all these examples, the solution is the same: use explicit synchronization.

+ +

Incorrect compilation

+ +

+The Go memory model restricts compiler optimizations as much as it does Go programs. +Some compiler optimizations that would be valid in single-threaded programs are not valid in all Go programs. +In particular, a compiler must not introduce writes that do not exist in the original program, +it must not allow a single read to observe multiple values, +and it must not allow a single write to write multiple values. +

+ +

+All the following examples assume that `*p` and `*q` refer to +memory locations accessible to multiple goroutines. +

+ +

+Not introducing data races into race-free programs means not moving +writes out of conditional statements in which they appear. +For example, a compiler must not invert the conditional in this program: +

+ +
+*p = 1
+if cond {
+	*p = 2
+}
+
+ +

+That is, the compiler must not rewrite the program into this one: +

+ +
+*p = 2
+if !cond {
+	*p = 1
+}
+
+ +

+If cond is false and another goroutine is reading *p, +then in the original program, the other goroutine can only observe any prior value of *p and 1. +In the rewritten program, the other goroutine can observe 2, which was previously impossible. +

+ +

+Not introducing data races also means not assuming that loops terminate. +For example, a compiler must in general not move the accesses to *p or *q +ahead of the loop in this program: +

+ +
+n := 0
+for e := list; e != nil; e = e.next {
+	n++
+}
+i := *p
+*q = 1
+
+ +

+If list pointed to a cyclic list, +then the original program would never access *p or *q, +but the rewritten program would. +(Moving `*p` ahead would be safe if the compiler can prove `*p` will not panic; +moving `*q` ahead would also require the compiler proving that no other +goroutine can access `*q`.) +

+ +

+Not introducing data races also means not assuming that called functions +always return or are free of synchronization operations. +For example, a compiler must not move the accesses to *p or *q +ahead of the function call in this program +(at least not without direct knowledge of the precise behavior of f): +

+ +
+f()
+i := *p
+*q = 1
+
+ +

+If the call never returned, then once again the original program +would never access *p or *q, but the rewritten program would. +And if the call contained synchronizing operations, then the original program +could establish happens before edges preceding the accesses +to *p and *q, but the rewritten program would not. +

+ +

+Not allowing a single read to observe multiple values means +not reloading local variables from shared memory. +For example, a compiler must not discard i and reload it +a second time from *p in this program: +

+ +
+i := *p
+if i < 0 || i >= len(funcs) {
+	panic("invalid function index")
+}
+... complex code ...
+// compiler must NOT reload i = *p here
+funcs[i]()
+
+ +

+If the complex code needs many registers, a compiler for single-threaded programs +could discard i without saving a copy and then reload +i = *p just before +funcs[i](). +A Go compiler must not, because the value of *p may have changed. +(Instead, the compiler could spill i to the stack.) +

+ +

+Not allowing a single write to write multiple values also means not using +the memory where a local variable will be written as temporary storage before the write. +For example, a compiler must not use *p as temporary storage in this program: +

+ +
+*p = i + *p/2
+
+ +

+That is, it must not rewrite the program into this one: +

+ +
+*p /= 2
+*p += i
+
+ +

+If i and *p start equal to 2, +the original code does *p = 3, +so a racing thread can read only 2 or 3 from *p. +The rewritten code does *p = 1 and then *p = 3, +allowing a racing thread to read 1 as well. +

+ +

+Note that all these optimizations are permitted in C/C++ compilers: +a Go compiler sharing a back end with a C/C++ compiler must take care +to disable optimizations that are invalid for Go. +

+ +

+Note that the prohibition on introducing data races +does not apply if the compiler can prove that the races +do not affect correct execution on the target platform. +For example, on essentially all CPUs, it is valid to rewrite +

+ +
+n := 0
+for i := 0; i < m; i++ {
+	n += *shared
+}
+
+ +into: + +
+n := 0
+local := *shared
+for i := 0; i < m; i++ {
+	n += local
+}
+
+ +

+provided it can be proved that *shared will not fault on access, +because the potential added read will not affect any existing concurrent reads or writes. +On the other hand, the rewrite would not be valid in a source-to-source translator. +

+ +

Conclusion

+ +

+Go programmers writing data-race-free programs can rely on +sequentially consistent execution of those programs, +just as in essentially all other modern programming languages. +

+ +

+When it comes to programs with races, +both programmers and compilers should remember the advice: +don't be clever. +

diff --git a/doc/go_spec.html b/doc/go_spec.html index fd5fee46eb220c..764dcd2f787aaa 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,14 +1,16 @@

Introduction

-This is a reference manual for the Go programming language. For -more information and other documents, see golang.org. +This is the reference manual for the Go programming language. +The pre-Go1.18 version, without generics, can be found +here. +For more information and other documents, see golang.org.

@@ -20,20 +22,23 @@

Introduction

-The grammar is compact and simple to parse, allowing for easy analysis +The syntax is compact and simple to parse, allowing for easy analysis by automatic tools such as integrated development environments.

Notation

-The syntax is specified using Extended Backus-Naur Form (EBNF): +The syntax is specified using a +variant +of Extended Backus-Naur Form (EBNF):

+Syntax      = { Production } .
 Production  = production_name "=" [ Expression ] "." .
-Expression  = Alternative { "|" Alternative } .
-Alternative = Term { Term } .
-Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
+Expression  = Term { "|" Term } .
+Term        = Factor { Factor } .
+Factor      = production_name | token [ "…" token ] | Group | Option | Repetition .
 Group       = "(" Expression ")" .
 Option      = "[" Expression "]" .
 Repetition  = "{" Expression "}" .
@@ -51,7 +56,7 @@ 

Notation

-Lower-case production names are used to identify lexical tokens. +Lowercase production names are used to identify lexical (terminal) tokens. Non-terminals are in CamelCase. Lexical tokens are enclosed in double quotes "" or back quotes ``.

@@ -77,7 +82,7 @@

Source code representation

in the source text.

-Each code point is distinct; for instance, upper and lower case letters +Each code point is distinct; for instance, uppercase and lowercase letters are different characters.

@@ -94,13 +99,13 @@

Source code representation

Characters

-The following terms are used to denote specific Unicode character classes: +The following terms are used to denote specific Unicode character categories:

 newline        = /* the Unicode code point U+000A */ .
 unicode_char   = /* an arbitrary Unicode code point except newline */ .
-unicode_letter = /* a Unicode code point classified as "Letter" */ .
-unicode_digit  = /* a Unicode code point classified as "Number, decimal digit" */ .
+unicode_letter = /* a Unicode code point categorized as "Letter" */ .
+unicode_digit  = /* a Unicode code point categorized as "Number, decimal digit" */ .
 

@@ -113,7 +118,7 @@

Characters

Letters and digits

-The underscore character _ (U+005F) is considered a letter. +The underscore character _ (U+005F) is considered a lowercase letter.

 letter        = unicode_letter | "_" .
@@ -168,7 +173,7 @@ 

Tokens

Semicolons

-The formal grammar uses semicolons ";" as terminators in +The formal syntax uses semicolons ";" as terminators in a number of productions. Go programs may omit most of these semicolons using the following two rules:

@@ -258,7 +263,7 @@

Operators and punctuation

The following character sequences represent operators -(including assignment operators) and punctuation: +(including assignment operators) and punctuation:

 +    &     +=    &=     &&    ==    !=    (    )
@@ -266,7 +271,7 @@ 

Operators and punctuation

* ^ *= ^= <- > >= { } / << /= <<= ++ = := , ; % >> %= >>= -- ! ... . : - &^ &^= + &^ &^= ~

Integer literals

@@ -404,7 +409,7 @@

Imaginary literals

complex constant. It consists of an integer or floating-point literal -followed by the lower-case letter i. +followed by the lowercase letter i. The value of an imaginary literal is the value of the respective integer or floating-point literal multiplied by the imaginary unit i.

@@ -497,8 +502,9 @@

Rune literals

-All other sequences starting with a backslash are illegal inside rune literals. +An unrecognized character following a backslash in a rune literal is illegal.

+
 rune_lit         = "'" ( unicode_value | byte_value ) "'" .
 unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
@@ -525,8 +531,10 @@ 

Rune literals

'\U00101234' '\'' // rune literal containing single quote character 'aa' // illegal: too many characters +'\k' // illegal: k is not recognized after a backslash '\xa' // illegal: too few hexadecimal digits '\0' // illegal: too few octal digits +'\400' // illegal: octal value over 255 '\uDFFF' // illegal: surrogate half '\U00110000' // illegal: invalid Unicode code point
@@ -635,7 +643,7 @@

Constants

a constant expression, a conversion with a result that is a constant, or the result value of some built-in functions such as -unsafe.Sizeof applied to any value, +unsafe.Sizeof applied to certain values, cap or len applied to some expressions, real and imag applied to a complex constant @@ -668,10 +676,12 @@

Constants

A constant may be given a type explicitly by a constant declaration or conversion, or implicitly when used in a variable declaration or an -assignment or as an +assignment statement or as an operand in an expression. It is an error if the constant value cannot be represented as a value of the respective type. +If the type is a type parameter, the constant is converted into a non-constant +value of the type parameter.

@@ -752,7 +762,7 @@

Variables

new call or composite literal, or the type of an element of a structured variable. Variables of interface type also have a distinct dynamic type, -which is the concrete type of the value assigned to the variable at run time +which is the (non-interface) type of the value assigned to the variable at run time (unless the value is the predeclared identifier nil, which has no type). The dynamic type may vary during execution but values stored in interface @@ -770,7 +780,7 @@

Variables

A variable's value is retrieved by referring to the variable in an expression; it is the most recent value -assigned to the variable. +assigned to the variable. If a variable has not yet been assigned a value, its value is the zero value for its type.

@@ -780,76 +790,33 @@

Types

A type determines a set of values together with operations and methods specific -to those values. A type may be denoted by a type name, if it has one, -or specified using a type literal, which composes a type from existing types. +to those values. A type may be denoted by a type name, if it has one, which must be +followed by type arguments if the type is generic. +A type may also be specified using a type literal, which composes a type +from existing types.

-Type      = TypeName | TypeLit | "(" Type ")" .
+Type      = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
 TypeName  = identifier | QualifiedIdent .
+TypeArgs  = "[" TypeList [ "," ] "]" .
+TypeList  = Type { "," Type } .
 TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
-	    SliceType | MapType | ChannelType .
+            SliceType | MapType | ChannelType .
 

The language predeclares certain type names. -Others are introduced with type declarations. +Others are introduced with type declarations +or type parameter lists. Composite types—array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals.

-Each type T has an underlying type: If T -is one of the predeclared boolean, numeric, or string types, or a type literal, -the corresponding underlying -type is T itself. Otherwise, T's underlying type -is the underlying type of the type to which T refers in its -type declaration. -

- -
-type (
-	A1 = string
-	A2 = A1
-)
-
-type (
-	B1 string
-	B2 B1
-	B3 []B1
-	B4 B3
-)
-
- -

-The underlying type of string, A1, A2, B1, -and B2 is string. -The underlying type of []B1, B3, and B4 is []B1. -

- -

Method sets

-

-A type has a (possibly empty) method set associated with it. -The method set of an interface type is its interface. -The method set of any other type T consists of all -methods declared with receiver type T. -The method set of the corresponding pointer type *T -is the set of all methods declared with receiver *T or T -(that is, it also contains the method set of T). -Further rules apply to structs containing embedded fields, as described -in the section on struct types. -Any other type has an empty method set. -In a method set, each method must have a -unique -non-blank method name. -

- -

-The method set of a type determines the interfaces that the -type implements -and the methods that can be called -using a receiver of that type. +Predeclared types, defined types, and type parameters are called named types. +An alias denotes a named type if the type given in the alias declaration is a named type.

Boolean types

@@ -864,7 +831,9 @@

Boolean types

Numeric types

-A numeric type represents sets of integer or floating-point values. +An integer, floating-point, or complex type +represents the set of integer, floating-point, or complex values, respectively. +They are collectively called numeric types. The predeclared architecture-independent numeric types are:

@@ -895,7 +864,7 @@

Numeric types

-There is also a set of predeclared numeric types with implementation-specific sizes: +There is also a set of predeclared integer types with implementation-specific sizes:

@@ -1014,7 +983,7 @@ 

Slice types

-A new, initialized slice value for a given element type T is +A new, initialized slice value for a given element type T may be made using the built-in function make, which takes a slice type @@ -1058,7 +1027,7 @@

Struct types

 StructType    = "struct" "{" { FieldDecl ";" } "}" .
 FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
-EmbeddedField = [ "*" ] TypeName .
+EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
 Tag           = string_lit .
 
@@ -1122,7 +1091,7 @@

Struct types

-Given a struct type S and a defined type +Given a struct type S and a named type T, promoted methods are included in the method set of the struct as follows:

    @@ -1232,27 +1201,42 @@

    Function types

    func(n int) func(p *T)
-

Interface types

-An interface type specifies a method set called its interface. -A variable of interface type can store a value of any type with a method set -that is any superset of the interface. Such a type is said to -implement the interface. +An interface type defines a type set. +A variable of interface type can store a value of any type that is in the type +set of the interface. Such a type is said to +implement the interface. The value of an uninitialized variable of interface type is nil.

-InterfaceType      = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
-MethodSpec         = MethodName Signature .
-MethodName         = identifier .
-InterfaceTypeName  = TypeName .
+InterfaceType  = "interface" "{" { InterfaceElem ";" } "}" .
+InterfaceElem  = MethodElem | TypeElem .
+MethodElem     = MethodName Signature .
+MethodName     = identifier .
+TypeElem       = TypeTerm { "|" TypeTerm } .
+TypeTerm       = Type | UnderlyingType .
+UnderlyingType = "~" Type .
 

-An interface type may specify methods explicitly through method specifications, -or it may embed methods of other interfaces through interface type names. +An interface type is specified by a list of interface elements. +An interface element is either a method or a type element, +where a type element is a union of one or more type terms. +A type term is either a single type or a single underlying type. +

+ +

Basic interfaces

+ +

+In its most basic form an interface specifies a (possibly empty) list of methods. +The type set defined by such an interface is the set of types which implement all of +those methods, and the corresponding method set consists +exactly of the methods specified by the interface. +Interfaces whose type sets can be defined entirely by a list of methods are called +basic interfaces.

@@ -1297,15 +1281,20 @@ 

Interface types

-A type implements any interface comprising any subset of its methods -and may therefore implement several distinct interfaces. For -instance, all types implement the empty interface: +Every type that is a member of the type set of an interface implements that interface. +Any given type may implement several distinct interfaces. +For instance, all types implement the empty interface which stands for the set +of all (non-interface) types:

 interface{}
 
+

+For convenience, the predeclared type any is an alias for the empty interface. +

+

Similarly, consider this interface specification, which appears within a type declaration @@ -1333,13 +1322,19 @@

Interface types

as the File interface.

+

Embedded interfaces

+

-An interface T may use a (possibly qualified) interface type -name E in place of a method specification. This is called +In a slightly more general form +an interface T may use a (possibly qualified) interface type +name E as an interface element. This is called embedding interface E in T. -The method set of T is the union -of the method sets of T’s explicitly declared methods and of -T’s embedded interfaces. +The type set of T is the intersection of the type sets +defined by T's explicitly declared methods and the type sets +of T’s embedded interfaces. +In other words, the type set of T is the set of all types that implement all the +explicitly declared methods of T and also all the methods of +E.

@@ -1361,8 +1356,7 @@ 

Interface types

-A union of method sets contains the (exported and non-exported) -methods of each method set exactly once, and methods with the +When embedding interfaces, methods with the same names must have identical signatures.

@@ -1374,9 +1368,151 @@

Interface types

}
+

General interfaces

+ +

+In their most general form, an interface element may also be an arbitrary type term +T, or a term of the form ~T specifying the underlying type T, +or a union of terms t1|t2|…|tn. +Together with method specifications, these elements enable the precise +definition of an interface's type set as follows: +

+ +
    +
  • The type set of the empty interface is the set of all non-interface types. +
  • + +
  • The type set of a non-empty interface is the intersection of the type sets + of its interface elements. +
  • + +
  • The type set of a method specification is the set of all non-interface types + whose method sets include that method. +
  • + +
  • The type set of a non-interface type term is the set consisting + of just that type. +
  • + +
  • The type set of a term of the form ~T + is the set of all types whose underlying type is T. +
  • + +
  • The type set of a union of terms + t1|t2|…|tn + is the union of the type sets of the terms. +
  • +
+ +

+The quantification "the set of all non-interface types" refers not just to all (non-interface) +types declared in the program at hand, but all possible types in all possible programs, and +hence is infinite. +Similarly, given the set of all non-interface types that implement a particular method, the +intersection of the method sets of those types will contain exactly that method, even if all +types in the program at hand always pair that method with another method. +

+ +

+By construction, an interface's type set never contains an interface type. +

+ +
+// An interface representing only the type int.
+interface {
+	int
+}
+
+// An interface representing all types with underlying type int.
+interface {
+	~int
+}
+
+// An interface representing all types with underlying type int that implement the String method.
+interface {
+	~int
+	String() string
+}
+
+// An interface representing an empty type set: there is no type that is both an int and a string.
+interface {
+	int
+	string
+}
+
+ +

+In a term of the form ~T, the underlying type of T +must be itself, and T cannot be an interface. +

+ +
+type MyInt int
+
+interface {
+	~[]byte  // the underlying type of []byte is itself
+	~MyInt   // illegal: the underlying type of MyInt is not MyInt
+	~error   // illegal: error is an interface
+}
+
+ +

+Union elements denote unions of type sets: +

+ +
+// The Float interface represents all floating-point types
+// (including any named types whose underlying types are
+// either float32 or float64).
+type Float interface {
+	~float32 | ~float64
+}
+
+ +

+The type T in a term of the form T or ~T cannot +be a type parameter, and the type sets of all +non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty). +Given a type parameter P: +

+ +
+interface {
+	P                // illegal: P is a type parameter
+	int | ~P         // illegal: P is a type parameter
+	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
+	float32 | Float  // overlapping type sets but Float is an interface
+}
+
+ +

+Implementation restriction: +A union (with more than one term) cannot contain the +predeclared identifier comparable +or interfaces that specify methods, or embed comparable or interfaces +that specify methods. +

+ +

+Interfaces that are not basic may only be used as type +constraints, or as elements of other interfaces used as constraints. +They cannot be the types of values or variables, or components of other, +non-interface types. +

+ +
+var x Float                     // illegal: Float is not a basic interface
+
+var x interface{} = Float(nil)  // illegal
+
+type Floatish struct {
+	f Float                 // illegal
+}
+
+

-An interface type T may not embed itself -or any interface type that embeds T, recursively. +An interface type T may not embed any type element +that is, contains, or embeds T, recursively.

@@ -1392,8 +1528,34 @@ 

Interface types

type Bad2 interface { Bad1 } + +// illegal: Bad3 cannot embed a union containing Bad3 +type Bad3 interface { + ~int | ~string | Bad3 +}
+

Implementing an interface

+ +

+A type T implements an interface I if +

+ +
    +
  • + T is not an interface and is an element of the type set of I; or +
  • +
  • + T is an interface and the type set of T is a subset of the + type set of I. +
  • +
+ +

+A value of type T implements an interface if T +implements the interface. +

+

Map types

@@ -1416,7 +1578,6 @@

Map types

If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic. -

@@ -1430,7 +1591,7 @@ 

Map types

For a map m, it can be discovered using the built-in function len and may change during execution. Elements may be added during execution -using assignments and retrieved with +using assignments and retrieved with index expressions; they may be removed with the delete built-in function.

@@ -1470,10 +1631,10 @@

Channel types

The optional <- operator specifies the channel direction, -send or receive. If no direction is given, the channel is -bidirectional. +send or receive. If a direction is given, the channel is directional, +otherwise it is bidirectional. A channel may be constrained only to send or only to receive by -assignment or +assignment or explicit conversion.

@@ -1540,6 +1701,141 @@

Channel types

Properties of types and values

+

Underlying types

+ +

+Each type T has an underlying type: If T +is one of the predeclared boolean, numeric, or string types, or a type literal, +the corresponding underlying type is T itself. +Otherwise, T's underlying type is the underlying type of the +type to which T refers in its declaration. +For a type parameter that is the underlying type of its +type constraint, which is always an interface. +

+ +
+type (
+	A1 = string
+	A2 = A1
+)
+
+type (
+	B1 string
+	B2 B1
+	B3 []B1
+	B4 B3
+)
+
+func f[P any](x P) { … }
+
+ +

+The underlying type of string, A1, A2, B1, +and B2 is string. +The underlying type of []B1, B3, and B4 is []B1. +The underlying type of P is interface{}. +

+ +

Core types

+ +

+Each non-interface type T has a core type, which is the same as the +underlying type of T. +

+ +

+An interface T has a core type if one of the following +conditions is satisfied: +

+ +
    +
  1. +There is a single type U which is the underlying type +of all types in the type set of T; or +
  2. +
  3. +the type set of T contains only channel types +with identical element type E, and all directional channels have the same +direction. +
  4. +
+ +

+No other interfaces have a core type. +

+ +

+The core type of an interface is, depending on the condition that is satisfied, either: +

+ +
    +
  1. +the type U; or +
  2. +
  3. +the type chan E if T contains only bidirectional +channels, or the type chan<- E or <-chan E +depending on the direction of the directional channels present. +
  4. +
+ +

+By definition, a core type is never a defined type, +type parameter, or +interface type. +

+ +

+Examples of interfaces with core types: +

+ +
+type Celsius float32
+type Kelvin  float32
+
+interface{ int }                          // int
+interface{ Celsius|Kelvin }               // float32
+interface{ ~chan int }                    // chan int
+interface{ ~chan int|~chan<- int }        // chan<- int
+interface{ ~[]*data; String() string }    // []*data
+
+ +

+Examples of interfaces without core types: +

+ +
+interface{}                               // no single underlying type
+interface{ Celsius|float64 }              // no single underlying type
+interface{ chan int | chan<- string }     // channels have different element types
+interface{ <-chan int | chan<- int }      // directional channels have different directions
+
+ +

+Some operations (slice expressions, +append and copy) +rely on a slightly more loose form of core types which accept byte slices and strings. +Specifically, if there are exactly two types, []byte and string, +which are the underlying types of all types in the type set of interface T, +the core type of T is called bytestring. +

+ +

+Examples of interfaces with bytestring core types: +

+ +
+interface{ int }                          // int (same as ordinary core type)
+interface{ []byte | string }              // bytestring
+interface{ ~[]byte | myString }           // bytestring
+
+ +

+Note that bytestring is not a real type; it cannot be used to declare +variables are compose other types. It exists solely to describe the behavior of some +operations that read from a sequence of bytes, which may be a byte slice or a string. +

+

Type identity

@@ -1547,7 +1843,7 @@

Type identity

-A defined type is always different from any other type. +A named type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail: @@ -1572,15 +1868,17 @@

Type identity

identical, and either both functions are variadic or neither is. Parameter and result names are not required to match. -
  • Two interface types are identical if they have the same set of methods - with the same names and identical function types. - Non-exported method names from different - packages are always different. The order of the methods is irrelevant.
  • +
  • Two interface types are identical if they define the same type set. +
  • Two map types are identical if they have identical key and element types.
  • Two channel types are identical if they have identical element types and the same direction.
  • + +
  • Two instantiated types are identical if + their defined types and all type arguments are identical. +
  • @@ -1595,18 +1893,18 @@

    Type identity

    A3 = int A4 = func(A3, float64) *A0 A5 = func(x int, _ float64) *[]string -) -type ( B0 A0 B1 []string B2 struct{ a, b int } B3 struct{ a, c int } B4 func(int, float64) *B0 B5 func(x int, y float64) *A1 -) -type C0 = B0 + C0 = B0 + D0[P1, P2 any] struct{ x P1; y P2 } + E0 = D0[int, string] +)

    @@ -1620,8 +1918,9 @@

    Type identity

    A4, func(int, float64) *[]string, and A5 B0 and C0 +D0[int, string] and E0 []int and []int -struct{ a, b *T5 } and struct{ a, b *T5 } +struct{ a, b *B5 } and struct{ a, b *B5 } func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5 @@ -1629,38 +1928,45 @@

    Type identity

    B0 and B1 are different because they are new types created by distinct type definitions; func(int, float64) *B0 and func(x int, y float64) *[]string -are different because B0 is different from []string. +are different because B0 is different from []string; +and P1 and P2 are different because they are different +type parameters. +D0[int, string] and struct{ x int; y string } are +different because the former is an instantiated +defined type while the latter is a type literal +(but they are still assignable).

    -

    Assignability

    -A value x is assignable to a variable of type T +A value x of type V is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:

    • -x's type is identical to T. +V and T are identical.
    • -x's type V and T have identical -underlying types and at least one of V -or T is not a defined type. +V and T have identical +underlying types +but are not type parameters and at least one of V +or T is not a named type.
    • -T is an interface type and -x implements T. +V and T are channel types with +identical element types, V is a bidirectional channel, +and at least one of V or T is not a named type.
    • -x is a bidirectional channel value, T is a channel type, -x's type V and T have identical element types, -and at least one of V or T is not a defined type. +T is an interface type, but not a type parameter, and +x implements T.
    • x is the predeclared identifier nil and T -is a pointer, function, slice, map, channel, or interface type. +is a pointer, function, slice, map, channel, or interface type, +but not a type parameter.
    • x is an untyped constant @@ -1669,12 +1975,36 @@

      Assignability

    +

    +Additionally, if x's type V or T are type parameters, x +is assignable to a variable of type T if one of the following conditions applies: +

    + +
      +
    • +x is the predeclared identifier nil, T is +a type parameter, and x is assignable to each type in +T's type set. +
    • +
    • +V is not a named type, T is +a type parameter, and x is assignable to each type in +T's type set. +
    • +
    • +V is a type parameter and T is not a named type, +and values of each type in V's type set are assignable +to T. +
    • +

    Representability

    A constant x is representable -by a value of type T if one of the following conditions applies: +by a value of type T, +where T is not a type parameter, +if one of the following conditions applies:

      @@ -1683,7 +2013,7 @@

      Representability

    • -T is a floating-point type and x can be rounded to T's +T is a floating-point type and x can be rounded to T's precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE negative zero further simplified to an unsigned zero. Note that constant values never result in an IEEE negative zero, NaN, or infinity. @@ -1697,6 +2027,12 @@

      Representability

    +

    +If T is a type parameter, +x is representable by a value of type T if x is representable +by a value of each type in T's type set. +

    +
     x                   T           x is representable by a value of T because
     
    @@ -1724,22 +2060,58 @@ 

    Representability

    1e1000 float64 1e1000 overflows to IEEE +Inf after rounding
    - -

    Blocks

    +

    Method sets

    -A block is a possibly empty sequence of declarations and statements -within matching brace brackets. +The method set of a type determines the methods that can be +called on an operand of that type. +Every type has a (possibly empty) method set associated with it:

    -
    -Block = "{" StatementList "}" .
    -StatementList = { Statement ";" } .
    -
    - -

    -In addition to explicit blocks in the source code, there are implicit blocks: -

    +
      +
    • The method set of a defined type T consists of all +methods declared with receiver type T. +
    • + +
    • +The method set of a pointer to a defined type T +(where T is neither a pointer nor an interface) +is the set of all methods declared with receiver *T or T. +
    • + +
    • The method set of an interface type is the intersection +of the method sets of each type in the interface's type set +(the resulting method set is usually just the set of declared methods in the interface). +
    • +
    + +

    +Further rules apply to structs (and pointer to structs) containing embedded fields, +as described in the section on struct types. +Any other type has an empty method set. +

    + +

    +In a method set, each method must have a +unique +non-blank method name. +

    + +

    Blocks

    + +

    +A block is a possibly empty sequence of declarations and statements +within matching brace brackets. +

    + +
    +Block = "{" StatementList "}" .
    +StatementList = { Statement ";" } .
    +
    + +

    +In addition to explicit blocks in the source code, there are implicit blocks: +

    1. The universe block encompasses all Go source text.
    2. @@ -1771,6 +2143,7 @@

      Declarations and scope

      A declaration binds a non-blank identifier to a constant, type, +type parameter, variable, function, label, or @@ -1815,6 +2188,14 @@

      Declarations and scope

    3. The scope of an identifier denoting a method receiver, function parameter, or result variable is the function body.
    4. +
    5. The scope of an identifier denoting a type parameter of a function + or declared by a method receiver begins after the name of the function + and ends at the end of the function body.
    6. + +
    7. The scope of an identifier denoting a type parameter of a type + begins after the name of the type and ends at the end + of the TypeSpec.
    8. +
    9. The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) @@ -1860,7 +2241,7 @@

      Blank identifier

      The blank identifier is represented by the underscore character _. It serves as an anonymous placeholder instead of a regular (non-blank) identifier and has special meaning in declarations, -as an operand, and in assignments. +as an operand, and in assignment statements.

      @@ -1872,7 +2253,8 @@

      Predeclared identifiers

       Types:
      -	bool byte complex64 complex128 error float32 float64
      +	any bool byte comparable
      +	complex64 complex128 error float32 float64
       	int int8 int16 int32 int64 rune string
       	uint uint8 uint16 uint32 uint64 uintptr
       
      @@ -1887,7 +2269,6 @@ 

      Predeclared identifiers

      make new panic print println real recover
      -

      Exported identifiers

      @@ -1895,8 +2276,8 @@

      Exported identifiers

      An identifier is exported if both:

        -
      1. the first character of the identifier's name is a Unicode upper case - letter (Unicode class "Lu"); and
      2. +
      3. the first character of the identifier's name is a Unicode uppercase + letter (Unicode character category Lu); and
      4. the identifier is declared in the package block or it is a field name or method name.
      5. @@ -1905,7 +2286,6 @@

        Exported identifiers

        All other identifiers are not exported.

        -

        Uniqueness of identifiers

        @@ -1937,7 +2317,8 @@

        Constant declarations

        If the type is present, all constants take the type specified, and -the expressions must be assignable to that type. +the expressions must be assignable to that type, +which must not be a type parameter. If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped constants, @@ -2076,12 +2457,12 @@

        Type definitions

        A type definition creates a new, distinct type with the same -underlying type and operations as the given type, -and binds an identifier to it. +underlying type and operations as the given type +and binds an identifier, the type name, to it.

        -TypeDef = identifier Type .
        +TypeDef = identifier [ TypeParameters ] Type .
         

        @@ -2098,7 +2479,7 @@

        Type definitions

        type TreeNode struct { left, right *TreeNode - value *Comparable + value any } type Block interface { @@ -2158,6 +2539,187 @@

        Type definitions

        } +

        +If the type definition specifies type parameters, +the type name denotes a generic type. +Generic types must be instantiated when they +are used. +

        + +
        +type List[T any] struct {
        +	next  *List[T]
        +	value T
        +}
        +
        + +

        +In a type definition the given type cannot be a type parameter. +

        + +
        +type T[P any] P    // illegal: P is a type parameter
        +
        +func f[T any]() {
        +	type L T   // illegal: T is a type parameter declared by the enclosing function
        +}
        +
        + +

        +A generic type may also have methods associated with it. +In this case, the method receivers must declare the same number of type parameters as +present in the generic type definition. +

        + +
        +// The method Len returns the number of elements in the linked list l.
        +func (l *List[T]) Len() int  { … }
        +
        + +

        Type parameter declarations

        + +

        +A type parameter list declares the type parameters of a generic function or type declaration. +The type parameter list looks like an ordinary function parameter list +except that the type parameter names must all be present and the list is enclosed +in square brackets rather than parentheses. +

        + +
        +TypeParameters  = "[" TypeParamList [ "," ] "]" .
        +TypeParamList   = TypeParamDecl { "," TypeParamDecl } .
        +TypeParamDecl   = IdentifierList TypeConstraint .
        +
        + +

        +All non-blank names in the list must be unique. +Each name declares a type parameter, which is a new and different named type +that acts as a place holder for an (as of yet) unknown type in the declaration. +The type parameter is replaced with a type argument upon +instantiation of the generic function or type. +

        + +
        +[P any]
        +[S interface{ ~[]byte|string }]
        +[S ~[]E, E any]
        +[P Constraint[int]]
        +[_ any]
        +
        + +

        +Just as each ordinary function parameter has a parameter type, each type parameter +has a corresponding (meta-)type which is called its +type constraint. +

        + +

        +A parsing ambiguity arises when the type parameter list for a generic type +declares a single type parameter P with a constraint C +such that the text P C forms a valid expression: +

        + +
        +type T[P *C] …
        +type T[P (C)] …
        +type T[P *C|Q] …
        +…
        +
        + +

        +In these rare cases, the type parameter list is indistinguishable from an +expression and the type declaration is parsed as an array type declaration. +To resolve the ambiguity, embed the constraint in an +interface or use a trailing comma: +

        + +
        +type T[P interface{*C}] …
        +type T[P *C,] …
        +
        + +

        +Type parameters may also be declared by the receiver specification +of a method declaration associated +with a generic type. +

        + + + +

        Type constraints

        + +

        +A type constraint is an interface that defines the +set of permissible type arguments for the respective type parameter and controls the +operations supported by values of that type parameter. +

        + +
        +TypeConstraint = TypeElem .
        +
        + +

        +If the constraint is an interface literal of the form interface{E} where +E is an embedded type element (not a method), in a type parameter list +the enclosing interface{ … } may be omitted for convenience: +

        + +
        +[T []P]                      // = [T interface{[]P}]
        +[T ~int]                     // = [T interface{~int}]
        +[T int|string]               // = [T interface{int|string}]
        +type Constraint ~int         // illegal: ~int is not inside a type parameter list
        +
        + + + +

        +The predeclared +interface type comparable +denotes the set of all non-interface types that are +comparable. Specifically, +a type T implements comparable if: +

        + +
          +
        • + T is not an interface type and T supports the operations + == and !=; or +
        • +
        • + T is an interface type and each type in T's + type set implements comparable. +
        • +
        + +

        +Even though interfaces that are not type parameters can be +compared +(possibly causing a run-time panic) they do not implement +comparable. +

        + +
        +int                          // implements comparable
        +[]byte                       // does not implement comparable (slices cannot be compared)
        +interface{}                  // does not implement comparable (see above)
        +interface{ ~int | ~string }  // type parameter only: implements comparable
        +interface{ comparable }      // type parameter only: implements comparable
        +interface{ ~int | ~[]byte }  // type parameter only: does not implement comparable (not all types in the type set are comparable)
        +
        + +

        +The comparable interface and interfaces that (directly or indirectly) embed +comparable may only be used as type constraints. They cannot be the types of +values or variables, or components of other, non-interface types. +

        Variable declarations

        @@ -2186,7 +2748,7 @@

        Variable declarations

        If a list of expressions is given, the variables are initialized -with the expressions following the rules for assignments. +with the expressions following the rules for assignment statements. Otherwise, each variable is initialized to its zero value.

        @@ -2230,7 +2792,7 @@

        Short variable declarations

        -"var" IdentifierList = ExpressionList .
        +"var" IdentifierList "=" ExpressionList .
         
        @@ -2248,12 +2810,14 @@ 

        Short variable declarations

        and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original. +The non-blank variable names on the left side of := +must be unique.

         field1, offset := nextField(str, 0)
         field2, offset := nextField(str, offset)  // redeclares offset
        -a, a := 1, 2                              // illegal: double declaration of a or no new variable if a was declared elsewhere
        +x, y, x := 1, 2, 3                        // illegal: x repeated on left side of :=
         

        @@ -2267,13 +2831,19 @@

        Short variable declarations

        Function declarations

        + +

        A function declaration binds an identifier, the function name, to a function.

        -FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
        +FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
         FunctionName = identifier .
         FunctionBody = Block .
         
        @@ -2296,18 +2866,28 @@

        Function declarations

        -A function declaration may omit the body. Such a declaration provides the -signature for a function implemented outside Go, such as an assembly routine. +If the function declaration specifies type parameters, +the function name denotes a generic function. +A generic function must be instantiated before it can be +called or used as a value.

        -func min(x int, y int) int {
        +func min[T ~int|~float64](x, y T) T {
         	if x < y {
         		return x
         	}
         	return y
         }
        +
        + +

        +A function declaration without type parameters may omit the body. +Such a declaration provides the signature for a function implemented outside Go, +such as an assembly routine. +

        +
         func flushICache(begin, end uintptr)  // implemented externally
         
        @@ -2328,9 +2908,10 @@

        Method declarations

        The receiver is specified via an extra parameter section preceding the method name. That parameter section must declare a single non-variadic parameter, the receiver. Its type must be a defined type T or a -pointer to a defined type T. T is called the receiver -base type. A receiver base type cannot be a pointer or interface type and -it must be defined in the same package as the method. +pointer to a defined type T, possibly followed by a list of type parameter +names [P1, P2, …] enclosed in square brackets. +T is called the receiver base type. A receiver base type cannot be +a pointer or interface type and it must be defined in the same package as the method. The method is said to be bound to its receiver base type and the method name is visible only within selectors for type T or *T. @@ -2351,7 +2932,7 @@

        Method declarations

        -Given defined type Point, the declarations +Given defined type Point the declarations

        @@ -2372,18 +2953,29 @@ 

        Method declarations

        -The type of a method is the type of a function with the receiver as first -argument. For instance, the method Scale has type +If the receiver base type is a generic type, the +receiver specification must declare corresponding type parameters for the method +to use. This makes the receiver type parameters available to the method. +Syntactically, this type parameter declaration looks like an +instantiation of the receiver base type: the type +arguments must be identifiers denoting the type parameters being declared, one +for each type parameter of the receiver base type. +The type parameter names do not need to match their corresponding parameter names in the +receiver base type definition, and all non-blank parameter names must be unique in the +receiver parameter section and the method signature. +The receiver type parameter constraints are implied by the receiver base type definition: +corresponding type parameters have corresponding constraints.

        -func(p *Point, factor float64)
        -
        - -

        -However, a function declared this way is not a method. -

        +type Pair[A, B any] struct { + a A + b B +} +func (p Pair[A, B]) Swap() Pair[B, A] { … } // receiver declares A, B +func (p Pair[First, _]) First() First { … } // receiver declares First, corresponds to A in Pair +

        Expressions

        @@ -2404,22 +2996,36 @@

        Operands

        or a parenthesized expression.

        -

        -The blank identifier may appear as an -operand only on the left-hand side of an assignment. -

        -
        -Operand     = Literal | OperandName | "(" Expression ")" .
        +Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
         Literal     = BasicLit | CompositeLit | FunctionLit .
         BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
         OperandName = identifier | QualifiedIdent .
         
        +

        +An operand name denoting a generic function +may be followed by a list of type arguments; the +resulting operand is an instantiated function. +

        + +

        +The blank identifier may appear as an +operand only on the left-hand side of an assignment statement. +

        + +

        +Implementation restriction: A compiler need not report an error if an operand's +type is a type parameter with an empty +type set. Functions with such type parameters +cannot be instantiated; any attempt will lead +to an error at the instantiation site. +

        +

        Qualified identifiers

        -A qualified identifier is an identifier qualified with a package name prefix. +A qualified identifier is an identifier qualified with a package name prefix. Both the package name and the identifier must not be blank.

        @@ -2436,14 +3042,13 @@

        Qualified identifiers

        -math.Sin	// denotes the Sin function in package math
        +math.Sin // denotes the Sin function in package math
         

        Composite literals

        -Composite literals construct values for structs, arrays, slices, and maps -and create a new value each time they are evaluated. +Composite literals construct new composite values each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key.

        @@ -2451,7 +3056,7 @@

        Composite literals

         CompositeLit  = LiteralType LiteralValue .
         LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
        -                SliceType | MapType | TypeName .
        +                SliceType | MapType | TypeName [ TypeArgs ] .
         LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
         ElementList   = KeyedElement { "," KeyedElement } .
         KeyedElement  = [ Key ":" ] Element .
        @@ -2461,11 +3066,12 @@ 

        Composite literals

        -The LiteralType's underlying type must be a struct, array, slice, or map type -(the grammar enforces this constraint except when the type is given +The LiteralType's core type T +must be a struct, array, slice, or map type +(the syntax enforces this constraint except when the type is given as a TypeName). The types of the elements and keys must be assignable -to the respective field, element, and key types of the literal type; +to the respective field, element, and key types of type T; there is no additional conversion. The key is interpreted as a field name for struct literals, an index for array and slice literals, and a key for map literals. @@ -2527,7 +3133,7 @@

        Composite literals

        key must be a non-negative constant representable by a value of type int; and if it is typed - it must be of integer type. + it must be of integer type.
      6. An element without a key uses the previous element's index plus one. If the first element has no key, its index is zero. @@ -2653,6 +3259,7 @@

        Function literals

        A function literal represents an anonymous function. +Function literals cannot declare type parameters.

        @@ -2764,8 +3371,7 @@ 

        Selectors

        For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth -in T where there -is such an f. +in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
      7. @@ -3000,6 +3606,18 @@

        Method values

        which may be executed later.

        +
        +type S struct { *T }
        +type T int
        +func (t T) M() { print(t) }
        +
        +t := new(T)
        +s := S{T: t}
        +f := t.M                    // receiver *t is evaluated and stored in f
        +g := s.M                    // receiver *(s.T) is evaluated and stored in g
        +*t = 42                     // does not affect stored receivers in f and g
        +
        +

        The type T may be an interface or non-interface type.

        @@ -3110,10 +3728,11 @@

        Index expressions

        -If a is not a map: +If a is neither a map nor a type parameter:

          -
        • the index x must be of integer type or an untyped constant
        • +
        • the index x must be an untyped constant or its + core type must be an integer
        • a constant index must be non-negative and representable by a value of type int
        • a constant index that is untyped is given type int
        • @@ -3177,13 +3796,32 @@

          Index expressions

          for the element type of M
        +

        +For a of type parameter type P: +

        +
          +
        • The index expression a[x] must be valid for values + of all types in P's type set.
        • +
        • The element types of all types in P's type set must be identical. + In this context, the element type of a string type is byte.
        • +
        • If there is a map type in the type set of P, + all types in that type set must be map types, and the respective key types + must be all identical.
        • +
        • a[x] is the array, slice, or string element at index x, + or the map element with key x of the type argument + that P is instantiated with, and the type of a[x] is + the type of the (identical) element types.
        • +
        • a[x] may not be assigned to if P's type set + includes string types. +
        +

        Otherwise a[x] is illegal.

        An index expression on a map a of type map[K]V -used in an assignment or initialization of the special form +used in an assignment statement or initialization of the special form

        @@ -3215,7 +3853,7 @@ 

        Slice expressions

        Simple slice expressions

        -For a string, array, pointer to array, or slice a, the primary expression +The primary expression

        @@ -3223,7 +3861,10 @@ 

        Simple slice expressions

        -constructs a substring or slice. The indices low and +constructs a substring or slice. The core type of +a must be a string, array, pointer to array, slice, or a +bytestring. +The indices low and high select which elements of operand a appear in the result. The result has indices starting at 0 and length equal to high - low. @@ -3296,261 +3937,744 @@

        Simple slice expressions

        -

        Full slice expressions

        +

        Full slice expressions

        + +

        +The primary expression +

        + +
        +a[low : high : max]
        +
        + +

        +constructs a slice of the same type, and with the same length and elements as the simple slice +expression a[low : high]. Additionally, it controls the resulting slice's capacity +by setting it to max - low. Only the first index may be omitted; it defaults to 0. +The core type of a must be an array, pointer to array, +or slice (but not a string). +After slicing the array a +

        + +
        +a := [5]int{1, 2, 3, 4, 5}
        +t := a[1:3:5]
        +
        + +

        +the slice t has type []int, length 2, capacity 4, and elements +

        + +
        +t[0] == 2
        +t[1] == 3
        +
        + +

        +As for simple slice expressions, if a is a pointer to an array, +a[low : high : max] is shorthand for (*a)[low : high : max]. +If the sliced operand is an array, it must be addressable. +

        + +

        +The indices are in range if 0 <= low <= high <= max <= cap(a), +otherwise they are out of range. +A constant index must be non-negative and +representable by a value of type +int; for arrays, constant indices must also be in range. +If multiple indices are constant, the constants that are present must be in range relative to each +other. +If the indices are out of range at run time, a run-time panic occurs. +

        + +

        Type assertions

        + +

        +For an expression x of interface type, +but not a type parameter, and a type T, +the primary expression +

        + +
        +x.(T)
        +
        + +

        +asserts that x is not nil +and that the value stored in x is of type T. +The notation x.(T) is called a type assertion. +

        +

        +More precisely, if T is not an interface type, x.(T) asserts +that the dynamic type of x is identical +to the type T. +In this case, T must implement the (interface) type of x; +otherwise the type assertion is invalid since it is not possible for x +to store a value of type T. +If T is an interface type, x.(T) asserts that the dynamic type +of x implements the interface T. +

        +

        +If the type assertion holds, the value of the expression is the value +stored in x and its type is T. If the type assertion is false, +a run-time panic occurs. +In other words, even though the dynamic type of x +is known only at run time, the type of x.(T) is +known to be T in a correct program. +

        + +
        +var x interface{} = 7          // x has dynamic type int and value 7
        +i := x.(int)                   // i has type int and value 7
        +
        +type I interface { m() }
        +
        +func f(y I) {
        +	s := y.(string)        // illegal: string does not implement I (missing method m)
        +	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
        +	…
        +}
        +
        + +

        +A type assertion used in an assignment statement or initialization of the special form +

        + +
        +v, ok = x.(T)
        +v, ok := x.(T)
        +var v, ok = x.(T)
        +var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
        +
        + +

        +yields an additional untyped boolean value. The value of ok is true +if the assertion holds. Otherwise it is false and the value of v is +the zero value for type T. +No run-time panic occurs in this case. +

        + + +

        Calls

        + +

        +Given an expression f with a core type +F of function type, +

        + +
        +f(a1, a2, … an)
        +
        + +

        +calls f with arguments a1, a2, … an. +Except for one special case, arguments must be single-valued expressions +assignable to the parameter types of +F and are evaluated before the function is called. +The type of the expression is the result type +of F. +A method invocation is similar but the method itself +is specified as a selector upon a value of the receiver type for +the method. +

        + +
        +math.Atan2(x, y)  // function call
        +var pt *Point
        +pt.Scale(3.5)     // method call with receiver pt
        +
        + +

        +If f denotes a generic function, it must be +instantiated before it can be called +or used as a function value. +

        + +

        +In a function call, the function value and arguments are evaluated in +the usual order. +After they are evaluated, the parameters of the call are passed by value to the function +and the called function begins execution. +The return parameters of the function are passed by value +back to the caller when the function returns. +

        + +

        +Calling a nil function value +causes a run-time panic. +

        + +

        +As a special case, if the return values of a function or method +g are equal in number and individually +assignable to the parameters of another function or method +f, then the call f(g(parameters_of_g)) +will invoke f after binding the return values of +g to the parameters of f in order. The call +of f must contain no parameters other than the call of g, +and g must have at least one return value. +If f has a final ... parameter, it is +assigned the return values of g that remain after +assignment of regular parameters. +

        + +
        +func Split(s string, pos int) (string, string) {
        +	return s[0:pos], s[pos:]
        +}
        +
        +func Join(s, t string) string {
        +	return s + t
        +}
        +
        +if Join(Split(value, len(value)/2)) != value {
        +	log.Panic("test fails")
        +}
        +
        + +

        +A method call x.m() is valid if the method set +of (the type of) x contains m and the +argument list can be assigned to the parameter list of m. +If x is addressable and &x's method +set contains m, x.m() is shorthand +for (&x).m(): +

        + +
        +var p Point
        +p.Scale(3.5)
        +
        + +

        +There is no distinct method type and there are no method literals. +

        + +

        Passing arguments to ... parameters

        + +

        +If f is variadic with a final +parameter p of type ...T, then within f +the type of p is equivalent to type []T. +If f is invoked with no actual arguments for p, +the value passed to p is nil. +Otherwise, the value passed is a new slice +of type []T with a new underlying array whose successive elements +are the actual arguments, which all must be assignable +to T. The length and capacity of the slice is therefore +the number of arguments bound to p and may differ for each +call site. +

        + +

        +Given the function and calls +

        +
        +func Greeting(prefix string, who ...string)
        +Greeting("nobody")
        +Greeting("hello:", "Joe", "Anna", "Eileen")
        +
        + +

        +within Greeting, who will have the value +nil in the first call, and +[]string{"Joe", "Anna", "Eileen"} in the second. +

        + +

        +If the final argument is assignable to a slice type []T and +is followed by ..., it is passed unchanged as the value +for a ...T parameter. In this case no new slice is created. +

        + +

        +Given the slice s and call +

        + +
        +s := []string{"James", "Jasmine"}
        +Greeting("goodbye:", s...)
        +
        + +

        +within Greeting, who will have the same value as s +with the same underlying array. +

        + +

        Instantiations

        + +

        +A generic function or type is instantiated by substituting type arguments +for the type parameters. +Instantiation proceeds in two steps: +

        + +
          +
        1. +Each type argument is substituted for its corresponding type parameter in the generic +declaration. +This substitution happens across the entire function or type declaration, +including the type parameter list itself and any types in that list. +
        2. + +
        3. +After substitution, each type argument must implement +the constraint (instantiated, if necessary) +of the corresponding type parameter. Otherwise instantiation fails. +
        4. +
        + +

        +Instantiating a type results in a new non-generic named type; +instantiating a function produces a new non-generic function. +

        + +
        +type parameter list    type arguments    after substitution
        +
        +[P any]                int               int implements any
        +[S ~[]E, E any]        []int, int        []int implements ~[]int, int implements any
        +[P io.Writer]          string            illegal: string doesn't implement io.Writer
        +
        + +

        +For a generic function, type arguments may be provided explicitly, or they +may be partially or completely inferred. +A generic function that is not called requires a +type argument list for instantiation; if the list is partial, all +remaining type arguments must be inferrable. +A generic function that is called may provide a (possibly partial) type +argument list, or may omit it entirely if the omitted type arguments are +inferrable from the ordinary (non-type) function arguments. +

        + +
        +func min[T ~int|~float64](x, y T) T { … }
        +
        +f := min                   // illegal: min must be instantiated with type arguments when used without being called
        +minInt := min[int]         // minInt has type func(x, y int) int
        +a := minInt(2, 3)          // a has value 2 of type int
        +b := min[float64](2.0, 3)  // b has value 2.0 of type float64
        +c := min(b, -1)            // c has value -1.0 of type float64
        +
        + +

        +A partial type argument list cannot be empty; at least the first argument must be present. +The list is a prefix of the full list of type arguments, leaving the remaining arguments +to be inferred. Loosely speaking, type arguments may be omitted from "right to left". +

        + +
        +func apply[S ~[]E, E any](s S, f(E) E) S { … }
        +
        +f0 := apply[]                  // illegal: type argument list cannot be empty
        +f1 := apply[[]int]             // type argument for S explicitly provided, type argument for E inferred
        +f2 := apply[[]string, string]  // both type arguments explicitly provided
        +
        +var bytes []byte
        +r := apply(bytes, func(byte) byte { … })  // both type arguments inferred from the function arguments
        +
        + +

        +For a generic type, all type arguments must always be provided explicitly. +

        + +

        Type inference

        + +

        +Missing function type arguments may be inferred by a series of steps, described below. +Each step attempts to use known information to infer additional type arguments. +Type inference stops as soon as all type arguments are known. +After type inference is complete, it is still necessary to substitute all type arguments +for type parameters and verify that each type argument +implements the relevant constraint; +it is possible for an inferred type argument to fail to implement a constraint, in which +case instantiation fails. +

        + +

        +Type inference is based on +

        + +
          +
        • + a type parameter list +
        • +
        • + a substitution map M initialized with the known type arguments, if any +
        • +
        • + a (possibly empty) list of ordinary function arguments (in case of a function call only) +
        • +
        + +

        +and then proceeds with the following steps: +

        + +
          +
        1. + apply function argument type inference + to all typed ordinary function arguments +
        2. +
        3. + apply constraint type inference +
        4. +
        5. + apply function argument type inference to all untyped ordinary function arguments + using the default type for each of the untyped function arguments +
        6. +
        7. + apply constraint type inference +
        8. +
        + +

        +If there are no ordinary or untyped function arguments, the respective steps are skipped. +Constraint type inference is skipped if the previous step didn't infer any new type arguments, +but it is run at least once if there are missing type arguments. +

        + +

        +The substitution map M is carried through all steps, and each step may add entries to M. +The process stops as soon as M has a type argument for each type parameter or if an inference step fails. +If an inference step fails, or if M is still missing type arguments after the last step, type inference fails. +

        + +

        Type unification

        + +

        +Type inference is based on type unification. A single unification step +applies to a substitution map and two types, either +or both of which may be or contain type parameters. The substitution map tracks +the known (explicitly provided or already inferred) type arguments: the map +contains an entry PA for each type +parameter P and corresponding known type argument A. +During unification, known type arguments take the place of their corresponding type +parameters when comparing types. Unification is the process of finding substitution +map entries that make the two types equivalent. +

        + +

        +For unification, two types that don't contain any type parameters from the current type +parameter list are equivalent +if they are identical, or if they are channel types that are identical ignoring channel +direction, or if their underlying types are equivalent. +

        + +

        +Unification works by comparing the structure of pairs of types: their structure +disregarding type parameters must be identical, and types other than type parameters +must be equivalent. +A type parameter in one type may match any complete subtype in the other type; +each successful match causes an entry to be added to the substitution map. +If the structure differs, or types other than type parameters are not equivalent, +unification fails. +

        + +

        -For an array, pointer to array, or slice a (but not a string), the primary expression +For example, if T1 and T2 are type parameters, +[]map[int]bool can be unified with any of the following:

        -a[low : high : max]
        +[]map[int]bool   // types are identical
        +T1               // adds T1 → []map[int]bool to substitution map
        +[]T1             // adds T1 → map[int]bool to substitution map
        +[]map[T1]T2      // adds T1 → int and T2 → bool to substitution map
         

        -constructs a slice of the same type, and with the same length and elements as the simple slice -expression a[low : high]. Additionally, it controls the resulting slice's capacity -by setting it to max - low. Only the first index may be omitted; it defaults to 0. -After slicing the array a +On the other hand, []map[int]bool cannot be unified with any of

        -a := [5]int{1, 2, 3, 4, 5}
        -t := a[1:3:5]
        +int              // int is not a slice
        +struct{}         // a struct is not a slice
        +[]struct{}       // a struct is not a map
        +[]map[T1]string  // map element types don't match
         

        -the slice t has type []int, length 2, capacity 4, and elements +As an exception to this general rule, because a defined type +D and a type literal L are never equivalent, +unification compares the underlying type of D with L instead. +For example, given the defined type

        -t[0] == 2
        -t[1] == 3
        +type Vector []float64
         

        -As for simple slice expressions, if a is a pointer to an array, -a[low : high : max] is shorthand for (*a)[low : high : max]. -If the sliced operand is an array, it must be addressable. +and the type literal []E, unification compares []float64 with +[]E and adds an entry Efloat64 to +the substitution map.

        +

        Function argument type inference

        + + +

        -The indices are in range if 0 <= low <= high <= max <= cap(a), -otherwise they are out of range. -A constant index must be non-negative and -representable by a value of type -int; for arrays, constant indices must also be in range. -If multiple indices are constant, the constants that are present must be in range relative to each -other. -If the indices are out of range at run time, a run-time panic occurs. +Function argument type inference infers type arguments from function arguments: +if a function parameter is declared with a type T that uses +type parameters, +unifying the type of the corresponding +function argument with T may infer type arguments for the type +parameters used by T.

        -

        Type assertions

        +

        +For instance, given the generic function +

        + +
        +func scale[Number ~int64|~float64|~complex128](v []Number, s Number) []Number
        +

        -For an expression x of interface type -and a type T, the primary expression +and the call

        -x.(T)
        +var vector []float64
        +scaledVector := scale(vector, 42)
         

        -asserts that x is not nil -and that the value stored in x is of type T. -The notation x.(T) is called a type assertion. +the type argument for Number can be inferred from the function argument +vector by unifying the type of vector with the corresponding +parameter type: []float64 and []Number +match in structure and float64 matches with Number. +This adds the entry Numberfloat64 to the +substitution map. +Untyped arguments, such as the second function argument 42 here, are ignored +in the first round of function argument type inference and only considered if there are +unresolved type parameters left.

        +

        -More precisely, if T is not an interface type, x.(T) asserts -that the dynamic type of x is identical -to the type T. -In this case, T must implement the (interface) type of x; -otherwise the type assertion is invalid since it is not possible for x -to store a value of type T. -If T is an interface type, x.(T) asserts that the dynamic type -of x implements the interface T. +Inference happens in two separate phases; each phase operates on a specific list of +(parameter, argument) pairs:

        + +
          +
        1. + The list Lt contains all (parameter, argument) pairs where the parameter + type uses type parameters and where the function argument is typed. +
        2. +
        3. + The list Lu contains all remaining pairs where the parameter type is a single + type parameter. In this list, the respective function arguments are untyped. +
        4. +
        +

        -If the type assertion holds, the value of the expression is the value -stored in x and its type is T. If the type assertion is false, -a run-time panic occurs. -In other words, even though the dynamic type of x -is known only at run time, the type of x.(T) is -known to be T in a correct program. +Any other (parameter, argument) pair is ignored.

        -
        -var x interface{} = 7          // x has dynamic type int and value 7
        -i := x.(int)                   // i has type int and value 7
        +

        +By construction, the arguments of the pairs in Lu are untyped constants +(or the untyped boolean result of a comparison). And because default types +of untyped values are always predeclared non-composite types, they can never match against +a composite type, so it is sufficient to only consider parameter types that are single type +parameters. +

        -type I interface { m() } +

        +Each list is processed in a separate phase: +

        -func f(y I) { - s := y.(string) // illegal: string does not implement I (missing method m) - r := y.(io.Reader) // r has type io.Reader and the dynamic type of y must implement both I and io.Reader - … -} -
        +
          +
        1. + In the first phase, the parameter and argument types of each pair in Lt + are unified. If unification succeeds for a pair, it may yield new entries that + are added to the substitution map M. If unification fails, type inference + fails. +
        2. +
        3. + The second phase considers the entries of list Lu. Type parameters for + which the type argument has already been determined are ignored in this phase. + For each remaining pair, the parameter type (which is a single type parameter) and + the default type of the corresponding untyped argument is + unified. If unification fails, type inference fails. +
        4. +
        + +

        +While unification is successful, processing of each list continues until all list elements +are considered, even if all type arguments are inferred before the last list element has +been processed. +

        -A type assertion used in an assignment or initialization of the special form +Example:

        -v, ok = x.(T)
        -v, ok := x.(T)
        -var v, ok = x.(T)
        -var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
        +func min[T ~int|~float64](x, y T) T
        +
        +var x int
        +min(x, 2.0)    // T is int, inferred from typed argument x; 2.0 is assignable to int
        +min(1.0, 2.0)  // T is float64, inferred from default type for 1.0 and matches default type for 2.0
        +min(1.0, 2)    // illegal: default type float64 (for 1.0) doesn't match default type int (for 2)
         

        -yields an additional untyped boolean value. The value of ok is true -if the assertion holds. Otherwise it is false and the value of v is -the zero value for type T. -No run-time panic occurs in this case. +In the example min(1.0, 2), processing the function argument 1.0 +yields the substitution map entry Tfloat64. Because +processing continues until all untyped arguments are considered, an error is reported. This +ensures that type inference does not depend on the order of the untyped arguments.

        +

        Constraint type inference

        -

        Calls

        +

        +Constraint type inference infers type arguments by considering type constraints. +If a type parameter P has a constraint with a +core type C, +unifying P with C +may infer additional type arguments, either the type argument for P, +or if that is already known, possibly the type arguments for type parameters +used in C. +

        -Given an expression f of function type -F, +For instance, consider the type parameter list with type parameters List and +Elem:

        -f(a1, a2, … an)
        +[List ~[]Elem, Elem any]
         

        -calls f with arguments a1, a2, … an. -Except for one special case, arguments must be single-valued expressions -assignable to the parameter types of -F and are evaluated before the function is called. -The type of the expression is the result type -of F. -A method invocation is similar but the method itself -is specified as a selector upon a value of the receiver type for -the method. +Constraint type inference can deduce the type of Elem from the type argument +for List because Elem is a type parameter in the core type +[]Elem of List. +If the type argument is Bytes:

        -math.Atan2(x, y)  // function call
        -var pt *Point
        -pt.Scale(3.5)     // method call with receiver pt
        +type Bytes []byte
         

        -In a function call, the function value and arguments are evaluated in -the usual order. -After they are evaluated, the parameters of the call are passed by value to the function -and the called function begins execution. -The return parameters of the function are passed by value -back to the caller when the function returns. +unifying the underlying type of Bytes with the core type means +unifying []byte with []Elem. That unification succeeds and yields +the substitution map entry +Elembyte. +Thus, in this example, constraint type inference can infer the second type argument from the +first one.

        -Calling a nil function value -causes a run-time panic. +Using the core type of a constraint may lose some information: In the (unlikely) case that +the constraint's type set contains a single defined type +N, the corresponding core type is N's underlying type rather than +N itself. In this case, constraint type inference may succeed but instantiation +will fail because the inferred type is not in the type set of the constraint. +Thus, constraint type inference uses the adjusted core type of +a constraint: if the type set contains a single type, use that type; otherwise use the +constraint's core type.

        -As a special case, if the return values of a function or method -g are equal in number and individually -assignable to the parameters of another function or method -f, then the call f(g(parameters_of_g)) -will invoke f after binding the return values of -g to the parameters of f in order. The call -of f must contain no parameters other than the call of g, -and g must have at least one return value. -If f has a final ... parameter, it is -assigned the return values of g that remain after -assignment of regular parameters. +Generally, constraint type inference proceeds in two phases: Starting with a given +substitution map M

        -
        -func Split(s string, pos int) (string, string) {
        -	return s[0:pos], s[pos:]
        -}
        +
          +
        1. +For all type parameters with an adjusted core type, unify the type parameter with that +type. If any unification fails, constraint type inference fails. +
        2. -func Join(s, t string) string { - return s + t -} +
        3. +At this point, some entries in M may map type parameters to other +type parameters or to types containing type parameters. For each entry +PA in M where A is or +contains type parameters Q for which there exist entries +QB in M, substitute those +Q with the respective B in A. +Stop when no further substitution is possible. +
        4. +
        -if Join(Split(value, len(value)/2)) != value { - log.Panic("test fails") -} -
        +

        +The result of constraint type inference is the final substitution map M from type +parameters P to type arguments A where no type parameter P +appears in any of the A. +

        -A method call x.m() is valid if the method set -of (the type of) x contains m and the -argument list can be assigned to the parameter list of m. -If x is addressable and &x's method -set contains m, x.m() is shorthand -for (&x).m(): +For instance, given the type parameter list

        -var p Point
        -p.Scale(3.5)
        +[A any, B []C, C *A]
         

        -There is no distinct method type and there are no method literals. +and the single provided type argument int for type parameter A, +the initial substitution map M contains the entry Aint.

        -

        Passing arguments to ... parameters

        +

        +In the first phase, the type parameters B and C are unified +with the core type of their respective constraints. This adds the entries +B[]C and C*A +to M.

        -If f is variadic with a final -parameter p of type ...T, then within f -the type of p is equivalent to type []T. -If f is invoked with no actual arguments for p, -the value passed to p is nil. -Otherwise, the value passed is a new slice -of type []T with a new underlying array whose successive elements -are the actual arguments, which all must be assignable -to T. The length and capacity of the slice is therefore -the number of arguments bound to p and may differ for each -call site. +At this point there are two entries in M where the right-hand side +is or contains type parameters for which there exists other entries in M: +[]C and *A. +In the second phase, these type parameters are replaced with their respective +types. It doesn't matter in which order this happens. Starting with the state +of M after the first phase:

        -Given the function and calls +Aint, +B[]C, +C*A

        -
        -func Greeting(prefix string, who ...string)
        -Greeting("nobody")
        -Greeting("hello:", "Joe", "Anna", "Eileen")
        -

        -within Greeting, who will have the value -nil in the first call, and -[]string{"Joe", "Anna", "Eileen"} in the second. +Replace A on the right-hand side of → with int:

        -If the final argument is assignable to a slice type []T and -is followed by ..., it is passed unchanged as the value -for a ...T parameter. In this case no new slice is created. +Aint, +B[]C, +C*int

        -Given the slice s and call +Replace C on the right-hand side of → with *int:

        -
        -s := []string{"James", "Jasmine"}
        -Greeting("goodbye:", s...)
        -
        -

        -within Greeting, who will have the same value as s -with the same underlying array. +Aint, +B[]*int, +C*int

        +

        +At this point no further substitution is possible and the map is full. +Therefore, M represents the final map of type parameters +to type arguments for the given type parameter list. +

        Operators

        @@ -3585,7 +4709,7 @@

        Operators

        -The right operand in a shift expression must have integer type +The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, @@ -3602,13 +4726,14 @@

        Operators

        var j int32 = 1<<s // 1 has type int32; j == 0 var k = uint64(1<<s) // 1 has type uint64; k == 1<<33 var m int = 1.0<<s // 1.0 has type int; m == 1<<33 -var n = 1.0<<s == j // 1.0 has type int; n == true +var n = 1.0<<s == j // 1.0 has type int32; n == true var o = 1<<s == 2<<s // 1 and 2 have type int; o == false var p = 1<<s == 1<<33 // 1 has type int; p == true var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift var u1 = 1.0<<s != 0 // illegal: 1.0 has type float64, cannot shift var u2 = 1<<s != 1.0 // illegal: 1 has type float64, cannot shift -var v float32 = 1<<s // illegal: 1 has type float32, cannot shift +var v1 float32 = 1<<s // illegal: 1 has type float32, cannot shift +var v2 = string(1<<s) // illegal: 1 is converted to a string, cannot shift var w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression; w == 1<<33 var x = a[1.0<<s] // panics: 1.0 has type int, but 1<<33 overflows array bounds var b = make([]byte, 1.0<<s) // 1.0 has type int; len(b) == 1<<33 @@ -3664,8 +4789,9 @@

        Arithmetic operators

        Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+, --, *, /) apply to integer, -floating-point, and complex types; + also applies to strings. +-, *, /) apply to +integer, floating-point, and +complex types; + also applies to strings. The bitwise logical and shift operators apply to integers only.

        @@ -3685,6 +4811,30 @@

        Arithmetic operators

        >> right shift integer >> integer >= 0 +

        +If the operand type is a type parameter, +the operator must apply to each type in that type set. +The operands are represented as values of the type argument that the type parameter +is instantiated with, and the operation is computed +with the precision of that type argument. For example, given the function: +

        + +
        +func dotProduct[F ~float32|~float64](v1, v2 []F) F {
        +	var s F
        +	for i, x := range v1 {
        +		y := v2[i]
        +		s += x * y
        +	}
        +	return s
        +}
        +
        + +

        +the product x * y and the addition s += x * y +are computed with float32 or float64 precision, +respectively, depending on the type argument for F. +

        Integer operators

        @@ -3719,7 +4869,7 @@

        Integer operators

        -			 x, q
        +                         x, q
         int8                     -128
         int16                  -32768
         int32             -2147483648
        @@ -3771,13 +4921,14 @@ 

        Integer operators

        Integer overflow

        -For unsigned integer values, the operations +, +For unsigned integer values, the operations +, -, *, and << are computed modulo 2n, where n is the bit width of -the unsigned integer's type. +the unsigned integer's type. Loosely speaking, these unsigned integer operations discard high bits upon overflow, and programs may rely on "wrap around".

        +

        For signed integers, the operations +, -, *, /, and << may legally @@ -3788,7 +4939,6 @@

        Integer overflow

        not occur. For instance, it may not assume that x < x + 1 is always true.

        -

        Floating-point operators

        @@ -3804,7 +4954,7 @@

        Floating-point operators

        An implementation may combine multiple floating-point operations into a single fused operation, possibly across statements, and produce a result that differs from the value obtained by executing and rounding the instructions individually. -An explicit floating-point type conversion rounds to +An explicit floating-point type conversion rounds to the precision of the target type, preventing fusion that would discard that rounding.

        @@ -3844,7 +4994,6 @@

        String concatenation

        String addition creates a new string by concatenating the operands.

        -

        Comparison operators

        @@ -3923,7 +5072,7 @@

        Comparison operators

        A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and - X implements T. + X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x. @@ -4025,7 +5174,8 @@

        Address operators

        Receive operator

        -For an operand ch of channel type, +For an operand ch whose core type is a +channel, the value of the receive operation <-ch is the value received from the channel ch. The channel direction must permit receive operations, and the type of the receive operation is the element type of the channel. @@ -4044,7 +5194,7 @@

        Receive operator

        -A receive expression used in an assignment or initialization of the special form +A receive expression used in an assignment statement or initialization of the special form

        @@ -4111,7 +5261,8 @@ 

        Conversions

        -Converting a constant yields a typed constant as result. +Converting a constant to a type that is not a type parameter +yields a typed constant.

        @@ -4122,13 +5273,36 @@ 

        Conversions

        float64(-1e-1000) // 0.0 of type float64 string('x') // "x" of type string string(0x266c) // "♬" of type string -MyString("foo" + "bar") // "foobar" of type MyString +myString("foo" + "bar") // "foobar" of type myString string([]byte{'a'}) // not a constant: []byte{'a'} is not a constant (*int)(nil) // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type int(1.2) // illegal: 1.2 cannot be represented as an int string(65.0) // illegal: 65.0 is not an integer constant
        +

        +Converting a constant to a type parameter yields a non-constant value of that type, +with the value represented as a value of the type argument that the type parameter +is instantiated with. +For example, given the function: +

        + +
        +func f[P ~float32|~float64]() {
        +	… P(1.1) …
        +}
        +
        + +

        +the conversion P(1.1) results in a non-constant value of type P +and the value 1.1 is represented as a float32 or a float64 +depending on the type argument for f. +Accordingly, if f is instantiated with a float32 type, +the numeric value of the expression P(1.1) + 1.2 will be computed +with the same precision as the corresponding non-constant float32 +addition. +

        +

        A non-constant value x can be converted to type T in any of these cases: @@ -4141,14 +5315,16 @@

        Conversions

      8. ignoring struct tags (see below), - x's type and T have identical - underlying types. + x's type and T are not + type parameters but have + identical underlying types.
      9. ignoring struct tags (see below), x's type and T are pointer types - that are not defined types, - and their pointer base types have identical underlying types. + that are not named types, + and their pointer base types are not type parameters but + have identical underlying types.
      10. x's type and T are both integer or floating @@ -4170,6 +5346,28 @@

        Conversions

      11. +

        +Additionally, if T or x's type V are type +parameters, x +can also be converted to type T if one of the following conditions applies: +

        + +
          +
        • +Both V and T are type parameters and a value of each +type in V's type set can be converted to each type in T's +type set. +
        • +
        • +Only V is a type parameter and a value of each +type in V's type set can be converted to T. +
        • +
        • +Only T is a type parameter and x can be converted to each +type in T's type set. +
        • +
        +

        Struct tags are ignored when comparing struct types for identity for the purpose of conversion: @@ -4207,8 +5405,7 @@

        Conversions

        There is no linguistic mechanism to convert between pointers and integers. The package unsafe -implements this functionality under -restricted circumstances. +implements this functionality under restricted circumstances.

        Conversions between numeric types

        @@ -4219,19 +5416,19 @@

        Conversions between numeric types

        1. -When converting between integer types, if the value is a signed integer, it is +When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type's size. For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. The conversion always yields a valid value; there is no indication of overflow.
        2. -When converting a floating-point number to an integer, the fraction is discarded +When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
        3. When converting an integer or floating-point number to a floating-point type, -or a complex number to another complex type, the result value is rounded +or a complex number to another complex type, the result value is rounded to the precision specified by the destination type. For instance, the value of a variable x of type float32 may be stored using additional precision beyond that of an IEEE-754 32-bit number, @@ -4259,8 +5456,9 @@

          Conversions to and from a string string('a') // "a" string(-1) // "\ufffd" == "\xef\xbf\xbd" string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8" -type MyString string -MyString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5" + +type myString string +myString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5"

        @@ -4273,8 +5471,12 @@

        Conversions to and from a string string([]byte{}) // "" string([]byte(nil)) // "" -type MyBytes []byte -string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" +type bytes []byte +string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + +type myByte byte +string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" +myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'}) // "🌍" @@ -4288,8 +5490,12 @@

        Conversions to and from a string string([]rune{}) // "" string([]rune(nil)) // "" -type MyRunes []rune -string(MyRunes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" +type runes []rune +string(runes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + +type myRune rune +string([]myRune{0x266b, 0x266c}) // "\u266b\u266c" == "♫♬" +myString([]myRune{0x1f30e}) // "\U0001f30e" == "🌎" @@ -4298,10 +5504,13 @@

        Conversions to and from a string yields a slice whose successive elements are the bytes of the string.
        -[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
        -[]byte("")        // []byte{}
        +[]byte("hellø")             // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
        +[]byte("")                  // []byte{}
        +
        +bytes("hellø")              // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
         
        -MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
        +[]myByte("world!")          // []myByte{'w', 'o', 'r', 'l', 'd', '!'}
        +[]myByte(myString("🌏"))    // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'}
         
        @@ -4310,24 +5519,34 @@

        Conversions to and from a string yields a slice containing the individual Unicode code points of the string.
        -[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
        -[]rune("")                 // []rune{}
        +[]rune(myString("白鵬翔"))   // []rune{0x767d, 0x9d6c, 0x7fd4}
        +[]rune("")                  // []rune{}
         
        -MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}
        +runes("白鵬翔")              // []rune{0x767d, 0x9d6c, 0x7fd4}
        +
        +[]myRune("♫♬")              // []myRune{0x266b, 0x266c}
        +[]myRune(myString("🌐"))    // []myRune{0x1f310}
         

      -

      Conversions from slice to array pointer

      +

      Conversions from slice to array or array pointer

      -Converting a slice to an array pointer yields a pointer to the underlying array of the slice. -If the length of the slice is less than the length of the array, +Converting a slice to an array yields an array containing the elements of the underlying array of the slice. +Similarly, converting a slice to an array pointer yields a pointer to the underlying array of the slice. +In both cases, if the length of the slice is less than the length of the array, a run-time panic occurs.

       s := make([]byte, 2, 4)
      +
      +a0 := ([0]byte)(s)
      +a1 := ([1]byte)(s[1:])   // a1[0] == s[1]
      +a2 := ([2]byte)(s)       // a2[0] == s[0]
      +a4 := ([4]byte)(s)       // panics: len([4]byte) > len(s)
      +
       s0 := (*[0]byte)(s)      // s0 != nil
       s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
       s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
      @@ -4338,7 +5557,7 @@ 

      Conversions from slice to array t1 := (*[1]string)(t) // panics: len([1]string) > len(t) u := make([]byte, 0) -u0 = (*[0]byte)(u) // u0 != nil +u0 := (*[0]byte)(u) // u0 != nil

      Constant expressions

      @@ -4549,9 +5768,8 @@

      Statements

      Terminating statements

      -A terminating statement prevents execution of all statements that lexically -appear after it in the same block. The following statements -are terminating: +A terminating statement interrupts the regular flow of control in +a block. The following statements are terminating:

        @@ -4587,7 +5805,8 @@

        Terminating statements

        A "for" statement in which:
        • there are no "break" statements referring to the "for" statement, and
        • -
        • the loop condition is absent.
        • +
        • the loop condition is absent, and
        • +
        • the "for" statement does not use a range clause.
        @@ -4690,7 +5909,8 @@

        Send statements

        A send statement sends a value on a channel. -The channel expression must be of channel type, +The channel expression's core type +must be a channel, the channel direction must permit send operations, and the type of the value to be sent must be assignable to the channel's element type. @@ -4729,7 +5949,7 @@

        IncDec statements

        -The following assignment statements are semantically +The following assignment statements are semantically equivalent:

        @@ -4740,7 +5960,14 @@

        IncDec statements

        -

        Assignments

        +

        Assignment statements

        + +

        +An assignment replaces the current value stored in a variable +with a new value specified by an expression. +An assignment statement may assign a single value to a single variable, or multiple values to a +matching number of variables. +

         Assignment = ExpressionList assign_op ExpressionList .
        @@ -5036,7 +6263,8 @@ 

        Type switches

        Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of -interface type, and each non-interface type +interface type, but not a +type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different. @@ -5047,7 +6275,6 @@

        Type switches

        TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . TypeCaseClause = TypeSwitchCase ":" StatementList . TypeSwitchCase = "case" TypeList | "default" . -TypeList = Type { "," Type } .

        @@ -5118,6 +6345,32 @@

        Type switches

        } +

        +A type parameter or a generic type +may be used as a type in a case. If upon instantiation that type turns +out to duplicate another entry in the switch, the first matching case is chosen. +

        + +
        +func f[P any](x any) int {
        +	switch x.(type) {
        +	case P:
        +		return 0
        +	case string:
        +		return 1
        +	case []P:
        +		return 2
        +	case []byte:
        +		return 3
        +	default:
        +		return 4
        +	}
        +}
        +
        +var v1 = f[string]("foo")   // v1 == 0
        +var v2 = f[byte]([]byte{})  // v2 == 2
        +
        +

        The type switch guard may be preceded by a simple statement, which executes before the guard is evaluated. @@ -5210,7 +6463,8 @@

        For statements with range clause

        The expression on the right in the "range" clause is called the range expression, -which may be an array, pointer to an array, slice, string, map, or channel permitting +its core type must be +an array, pointer to an array, slice, string, map, or channel permitting receive operations. As with an assignment, if present the operands on the left must be addressable or map index expressions; they @@ -5256,7 +6510,7 @@

        For statements with range clause

        in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type rune, will be the value of -the corresponding code point. If the iteration encounters an invalid +the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be 0xFFFD, the Unicode replacement character, and the next iteration will advance a single byte in the string. @@ -5282,7 +6536,7 @@

        For statements with range clause

        The iteration values are assigned to the respective -iteration variables as in an assignment statement. +iteration variables as in an assignment statement.

        @@ -5617,7 +6871,8 @@

        Continue statements

        A "continue" statement begins the next iteration of the -innermost "for" loop at its post statement. +innermost enclosing "for" loop +by advancing control to the end of the loop block. The "for" loop must be within the same function.

        @@ -5795,9 +7050,10 @@

        Built-in functions

        Close

        -For a channel c, the built-in function close(c) +For an argument ch with a core type +that is a channel, the built-in function close records that no more values will be sent on the channel. -It is an error if c is a receive-only channel. +It is an error if ch is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously @@ -5807,7 +7063,6 @@

        Close

        returns a received value along with an indication of whether the channel is closed.

        -

        Length and capacity

        @@ -5824,12 +7079,23 @@

        Length and capacity

        []T slice length map[K]T map length (number of defined keys) chan T number of elements queued in channel buffer + type parameter see below cap(s) [n]T, *[n]T array length (== n) []T slice capacity chan T channel buffer capacity + type parameter see below +

        +If the argument type is a type parameter P, +the call len(e) (or cap(e) respectively) must be valid for +each type in P's type set. +The result is the length (or capacity, respectively) of the argument whose type +corresponds to the type argument with which P was +instantiated. +

        +

        The capacity of a slice is the number of elements for which there is space allocated in the underlying array. @@ -5902,35 +7168,37 @@

        Making slices, maps and channels

        The built-in function make takes a type T, -which must be a slice, map or channel type, optionally followed by a type-specific list of expressions. +The core type of T must +be a slice, map or channel. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values.

        -Call             Type T     Result
        +Call             Core type    Result
         
        -make(T, n)       slice      slice of type T with length n and capacity n
        -make(T, n, m)    slice      slice of type T with length n and capacity m
        +make(T, n)       slice        slice of type T with length n and capacity n
        +make(T, n, m)    slice        slice of type T with length n and capacity m
         
        -make(T)          map        map of type T
        -make(T, n)       map        map of type T with initial space for approximately n elements
        +make(T)          map          map of type T
        +make(T, n)       map          map of type T with initial space for approximately n elements
         
        -make(T)          channel    unbuffered channel of type T
        -make(T, n)       channel    buffered channel of type T, buffer size n
        +make(T)          channel      unbuffered channel of type T
        +make(T, n)       channel      buffered channel of type T, buffer size n
         

        -Each of the size arguments n and m must be of integer type -or an untyped constant. +Each of the size arguments n and m must be of integer type, +have a type set containing only integer types, +or be an untyped constant. A constant size argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. If both n and m are provided and are constant, then n must be no larger than m. -If n is negative or larger than m at run time, +For slices and channels, if n is negative or larger than m at run time, a run-time panic occurs.

        @@ -5961,21 +7229,21 @@

        Appending to and copying slices

        The variadic function append -appends zero or more values x -to s of type S, which must be a slice type, and -returns the resulting slice, also of type S. -The values x are passed to a parameter of type ...T -where T is the element type of -S and the respective -parameter passing rules apply. -As a special case, append also accepts a first argument -assignable to type []byte with a second argument of -string type followed by .... This form appends the -bytes of the string. +appends zero or more values x to a slice s +and returns the resulting slice of the same type as s. +The core type of s must be a slice +of type []E. +The values x are passed to a parameter of type ...E +and the respective parameter +passing rules apply. +As a special case, if the core type of s is []byte, +append also accepts a second argument with core type +bytestring followed by .... +This form appends the bytes of the byte slice or string.

        -append(s S, x ...T) S  // T is the element type of S
        +append(s S, x ...E) S  // core type of S is []E
         

        @@ -6003,13 +7271,14 @@

        Appending to and copying slices

        The function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. -Both arguments must have identical element type T and must be -assignable to a slice of type []T. +The core types of both arguments must be slices +with identical element type. The number of elements copied is the minimum of len(src) and len(dst). -As a special case, copy also accepts a destination argument assignable -to type []byte with a source argument of a string type. -This form copies the bytes from the string into the byte slice. +As a special case, if the destination's core type is []byte, +copy also accepts a source argument with core type + bytestring. +This form copies the bytes from the byte slice or string into the byte slice.

        @@ -6036,7 +7305,7 @@ 

        Deletion of map elements

        The built-in function delete removes the element with key k from a map m. The -type of k must be assignable +value k must be assignable to the key type of m.

        @@ -6044,6 +7313,11 @@

        Deletion of map elements

        delete(m, k) // remove element m[k] from map m
        +

        +If the type of m is a type parameter, +all types in that type set must be maps, and they must all have identical key types. +

        +

        If the map m is nil or the element m[k] does not exist, delete is a no-op. @@ -6069,7 +7343,8 @@

        Manipulating complex numbers

        The type of the arguments and return value correspond. For complex, the two arguments must be of the same -floating-point type and the return type is the complex type +floating-point type and the return type is the +complex type with the corresponding floating-point constituents: complex64 for float32 arguments, and complex128 for float64 arguments. @@ -6113,6 +7388,10 @@

        Manipulating complex numbers

        _ = imag(3 << s) // illegal: 3 assumes complex type, cannot shift +

        +Arguments of type parameter type are not permitted. +

        +

        Handling panics

        Two built-in functions, panic and recover, @@ -6131,7 +7410,7 @@

        Handling panics

        terminates the execution of F. Any functions deferred by F are then executed as usual. -Next, any deferred functions run by F's caller are run, +Next, any deferred functions run by F's caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. @@ -6313,7 +7592,7 @@

        Import declarations

        -Assume we have compiled a package containing the package clause +Consider a compiled a package containing the package clause package math, which exports function Sin, and installed the compiled package in the file identified by "lib/math". @@ -6721,11 +8000,17 @@

        Package unsafe

        func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType + +

        A Pointer is a pointer type but a Pointer value may not be dereferenced. -Any pointer or value of underlying type uintptr can be converted to -a type of underlying type Pointer and vice versa. +Any pointer or value of underlying type uintptr can be +converted to a type of underlying type Pointer and vice versa. The effect of converting between Pointer and uintptr is implementation-defined.

        @@ -6771,14 +8056,21 @@

        Package unsafe

        -Calls to Alignof, Offsetof, and -Sizeof are compile-time constant expressions of type uintptr. +A (variable of) type T has variable size if T +is a type parameter, or if it is an +array or struct type containing elements +or fields of variable size. Otherwise the size is constant. +Calls to Alignof, Offsetof, and Sizeof +are compile-time constant expressions of +type uintptr if their arguments (or the struct s in +the selector expression s.f for Offsetof) are types +of constant size.

        The function Add adds len to ptr and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). -The len argument must be of integer type or an untyped constant. +The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. The rules for valid uses of Pointer still apply. @@ -6801,7 +8093,7 @@

        Package unsafe

        -The len argument must be of integer type or an untyped constant. +The len argument must be of integer type or an untyped constant. A constant len argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. At run time, if len is negative, diff --git a/lib/time/README b/lib/time/README index aab4daa7e2df07..0de06df13b9a45 100644 --- a/lib/time/README +++ b/lib/time/README @@ -4,7 +4,7 @@ The IANA asserts that the database is in the public domain. For more information, see https://www.iana.org/time-zones -ftp://ftp.iana.org/tz/code/tz-link.htm -http://tools.ietf.org/html/rfc6557 +ftp://ftp.iana.org/tz/code/tz-link.html +https://datatracker.ietf.org/doc/html/rfc6557 To rebuild the archive, read and run update.bash. diff --git a/lib/time/update.bash b/lib/time/update.bash index e088ea6b908a5f..356b66139130ab 100755 --- a/lib/time/update.bash +++ b/lib/time/update.bash @@ -8,8 +8,8 @@ # Consult https://www.iana.org/time-zones for the latest versions. # Versions to use. -CODE=2021a -DATA=2021a +CODE=2022b +DATA=2022b set -e rm -rf work diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip index d32fbba5175174..e4d2208ca007f7 100644 Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go index 3af2bee5839b93..308dacaf7c1606 100644 --- a/misc/android/go_android_exec.go +++ b/misc/android/go_android_exec.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // This program can be used as go_android_GOARCH_exec by the Go tool. @@ -9,7 +10,6 @@ package main import ( - "bytes" "errors" "fmt" "go/build" @@ -27,7 +27,7 @@ import ( func run(args ...string) (string, error) { cmd := adbCmd(args...) - buf := new(bytes.Buffer) + buf := new(strings.Builder) cmd.Stdout = io.MultiWriter(os.Stdout, buf) // If the adb subprocess somehow hangs, go test will kill this wrapper // and wait for our os.Stderr (and os.Stdout) to close as a result. diff --git a/misc/cgo/errors/badsym_test.go b/misc/cgo/errors/badsym_test.go index fc687567bf71b2..bc3ba2b489f2d1 100644 --- a/misc/cgo/errors/badsym_test.go +++ b/misc/cgo/errors/badsym_test.go @@ -201,6 +201,10 @@ func cCompilerCmd(t *testing.T) []string { if !lastSpace { cc = append(cc, s[start:]) } + + // Force reallocation (and avoid aliasing bugs) for tests that append to cc. + cc = cc[:len(cc):len(cc)] + return cc } diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go index 68a30a44fe427d..9718b7f9fb4d04 100644 --- a/misc/cgo/errors/errors_test.go +++ b/misc/cgo/errors/errors_test.go @@ -36,14 +36,13 @@ func check(t *testing.T, file string) { continue } - frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 2) - if len(frags) == 1 { + _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")) + if !ok { continue } - frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1]) - re, err := regexp.Compile(frag) + re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) if err != nil { - t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1]) + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) continue } errors = append(errors, re) @@ -113,6 +112,7 @@ func TestReportsTypeErrors(t *testing.T) { "issue18889.go", "issue28721.go", "issue33061.go", + "issue50710.go", } { check(t, file) } diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go index a90598fe35b630..aa941584c3c2cf 100644 --- a/misc/cgo/errors/testdata/err2.go +++ b/misc/cgo/errors/testdata/err2.go @@ -91,10 +91,18 @@ func main() { // issue 26745 _ = func(i int) int { - return C.i + 1 // ERROR HERE: 14 + // typecheck reports at column 14 ('+'), but types2 reports at + // column 10 ('C'). + // TODO(mdempsky): Investigate why, and see if types2 can be + // updated to match typecheck behavior. + return C.i + 1 // ERROR HERE: \b(10|14)\b } _ = func(i int) { - C.fi(i) // ERROR HERE: 7 + // typecheck reports at column 7 ('('), but types2 reports at + // column 8 ('i'). The types2 position is more correct, but + // updating typecheck here is fundamentally challenging because of + // IR limitations. + C.fi(i) // ERROR HERE: \b(7|8)\b } C.fi = C.fi // ERROR HERE diff --git a/misc/cgo/errors/testdata/issue50710.go b/misc/cgo/errors/testdata/issue50710.go new file mode 100644 index 00000000000000..dffea229031d41 --- /dev/null +++ b/misc/cgo/errors/testdata/issue50710.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. 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 + +// size_t StrLen(_GoString_ s) { +// return _GoStringLen(s); +// } +import "C" + +func main() { + C.StrLen1() // ERROR HERE +} diff --git a/misc/cgo/gmp/fib.go b/misc/cgo/gmp/fib.go index f1091b1c54f45e..f453fcf1843b7c 100644 --- a/misc/cgo/gmp/fib.go +++ b/misc/cgo/gmp/fib.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Compute Fibonacci numbers with two goroutines diff --git a/misc/cgo/gmp/gmp.go b/misc/cgo/gmp/gmp.go index 971a10aaac644a..0835fdc8dea661 100644 --- a/misc/cgo/gmp/gmp.go +++ b/misc/cgo/gmp/gmp.go @@ -333,10 +333,9 @@ func (z *Int) Abs(x *Int) *Int { // CmpInt compares x and y. The result is // -// -1 if x < y -// 0 if x == y -// +1 if x > y -// +// -1 if x < y +// 0 if x == y +// +1 if x > y func CmpInt(x, y *Int) int { x.doinit() y.doinit() diff --git a/misc/cgo/gmp/pi.go b/misc/cgo/gmp/pi.go index d5851e8e6bd389..5ea034900a9b17 100644 --- a/misc/cgo/gmp/pi.go +++ b/misc/cgo/gmp/pi.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c index 8921b7306c6b14..8ecf70f2729c6f 100644 --- a/misc/cgo/test/callback_c.c +++ b/misc/cgo/test/callback_c.c @@ -3,8 +3,7 @@ // license that can be found in the LICENSE file. #include -#include -#include + #include "_cgo_export.h" void @@ -31,32 +30,10 @@ IntoC(void) BackIntoGo(); } -#ifdef WIN32 -#include -long long -mysleep(int seconds) { - long long st = GetTickCount(); - Sleep(1000 * seconds); - return st; -} -#else -#include -long long -mysleep(int seconds) { - long long st; - struct timeval tv; - gettimeofday(&tv, NULL); - st = tv.tv_sec * 1000 + tv.tv_usec / 1000; - sleep(seconds); - return st; -} -#endif - -long long -twoSleep(int n) +void +Issue1560InC(void) { - BackgroundSleep(n); - return mysleep(n); + Issue1560FromC(); } void diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go index a9746b552ee3f5..f7c07582a4d432 100644 --- a/misc/cgo/test/cgo_linux_test.go +++ b/misc/cgo/test/cgo_linux_test.go @@ -5,6 +5,7 @@ package cgotest import ( + "os" "runtime" "testing" ) @@ -13,8 +14,22 @@ func TestSetgid(t *testing.T) { if runtime.GOOS == "android" { t.Skip("unsupported on Android") } + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("setgid is broken with musl libc - go.dev/issue/39857") + } testSetgid(t) } + +func TestSetgidStress(t *testing.T) { + if runtime.GOOS == "android" { + t.Skip("unsupported on Android") + } + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("setgid is broken with musl libc - go.dev/issue/39857") + } + testSetgidStress(t) +} + func Test1435(t *testing.T) { test1435(t) } func Test6997(t *testing.T) { test6997(t) } func TestBuildID(t *testing.T) { testBuildID(t) } diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 143f23f0e0cc36..dee61643544fdc 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -11,6 +11,7 @@ import "testing" // These wrappers are here for gotest to find. func Test1328(t *testing.T) { test1328(t) } +func Test1560(t *testing.T) { test1560(t) } func Test1635(t *testing.T) { test1635(t) } func Test3250(t *testing.T) { test3250(t) } func Test3729(t *testing.T) { test3729(t) } @@ -59,7 +60,9 @@ func Test28896(t *testing.T) { test28896(t) } func Test30065(t *testing.T) { test30065(t) } func Test32579(t *testing.T) { test32579(t) } func Test31891(t *testing.T) { test31891(t) } +func Test42018(t *testing.T) { test42018(t) } func Test45451(t *testing.T) { test45451(t) } +func Test49633(t *testing.T) { test49633(t) } func TestAlign(t *testing.T) { testAlign(t) } func TestAtol(t *testing.T) { testAtol(t) } func TestBlocking(t *testing.T) { testBlocking(t) } @@ -87,7 +90,6 @@ func TestLibgcc(t *testing.T) { testLibgcc(t) } func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } func TestNaming(t *testing.T) { testNaming(t) } func TestPanicFromC(t *testing.T) { testPanicFromC(t) } -func TestParallelSleep(t *testing.T) { testParallelSleep(t) } func TestPrintf(t *testing.T) { testPrintf(t) } func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } diff --git a/misc/cgo/test/cgo_thread_lock.go b/misc/cgo/test/cgo_thread_lock.go index b1050685182287..3b9ac845493cd5 100644 --- a/misc/cgo/test/cgo_thread_lock.go +++ b/misc/cgo/test/cgo_thread_lock.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux && freebsd && openbsd // +build linux,freebsd,openbsd package cgotest diff --git a/misc/cgo/test/cgo_unix_test.go b/misc/cgo/test/cgo_unix_test.go index e3d591664983be..a324503a22faf0 100644 --- a/misc/cgo/test/cgo_unix_test.go +++ b/misc/cgo/test/cgo_unix_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/misc/cgo/test/issue1435.go b/misc/cgo/test/issue1435.go index 92c6b998465caf..3fb721ac395c2d 100644 --- a/misc/cgo/test/issue1435.go +++ b/misc/cgo/test/issue1435.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux && cgo // +build linux,cgo package cgotest @@ -9,6 +10,7 @@ package cgotest import ( "fmt" "os" + "runtime" "sort" "strings" "syscall" @@ -144,6 +146,11 @@ func test1435(t *testing.T) { if syscall.Getuid() != 0 { t.Skip("skipping root only test") } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("skipping failing test on alpine - go.dev/issue/19938") + } + } // Launch some threads in C. const cts = 5 diff --git a/misc/cgo/test/issue18146.go b/misc/cgo/test/issue18146.go index f92d6c7f939d22..e50f9ae53016fc 100644 --- a/misc/cgo/test/issue18146.go +++ b/misc/cgo/test/issue18146.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows // Issue 18146: pthread_create failure during syscall.Exec. diff --git a/misc/cgo/test/issue21897.go b/misc/cgo/test/issue21897.go index d13246bd84afde..8f39252e688580 100644 --- a/misc/cgo/test/issue21897.go +++ b/misc/cgo/test/issue21897.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin && cgo && !internal // +build darwin,cgo,!internal package cgotest diff --git a/misc/cgo/test/issue21897b.go b/misc/cgo/test/issue21897b.go index 08b5f4d808e240..50aece3528947b 100644 --- a/misc/cgo/test/issue21897b.go +++ b/misc/cgo/test/issue21897b.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !darwin || !cgo || internal // +build !darwin !cgo internal package cgotest diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go index b2d131833a9377..90ca08cbfb7da8 100644 --- a/misc/cgo/test/issue4029.go +++ b/misc/cgo/test/issue4029.go @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !windows,!static +//go:build !windows && !static && (!darwin || (!internal_pie && !arm64)) +// +build !windows +// +build !static // +build !darwin !internal_pie,!arm64 // Excluded in darwin internal linking PIE mode, as dynamic export is not diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go index b969bdd0fe8702..c2f59485e490d6 100644 --- a/misc/cgo/test/issue4029w.go +++ b/misc/cgo/test/issue4029w.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build windows || static || (darwin && internal_pie) || (darwin && arm64) // +build windows static darwin,internal_pie darwin,arm64 package cgotest diff --git a/misc/cgo/test/issue42018.go b/misc/cgo/test/issue42018.go new file mode 100644 index 00000000000000..fab686a678320c --- /dev/null +++ b/misc/cgo/test/issue42018.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +import "testing" + +func test42018(t *testing.T) { + t.Skip("skipping Windows-only test") +} diff --git a/misc/cgo/test/issue42018_windows.go b/misc/cgo/test/issue42018_windows.go new file mode 100644 index 00000000000000..8f4570ab2a5915 --- /dev/null +++ b/misc/cgo/test/issue42018_windows.go @@ -0,0 +1,46 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* +typedef void *HANDLE; + +struct HWND__{int unused;}; typedef struct HWND__ *HWND; +*/ +import "C" + +import ( + "testing" + "unsafe" +) + +func test42018(t *testing.T) { + // Test that Windows handles are marked go:notinheap, by growing the + // stack and checking for pointer adjustments. Trick from + // test/fixedbugs/issue40954.go. + var i int + handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i))) + hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i))) +} + +func recurseHANDLE(n int, p C.HANDLE, v uintptr) { + if n > 0 { + recurseHANDLE(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} + +func recurseHWND(n int, p C.HWND, v uintptr) { + if n > 0 { + recurseHWND(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} diff --git a/misc/cgo/test/issue6997_linux.go b/misc/cgo/test/issue6997_linux.go index f19afb8b7ad816..4acc8c1a070627 100644 --- a/misc/cgo/test/issue6997_linux.go +++ b/misc/cgo/test/issue6997_linux.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !android // +build !android // Test that pthread_cancel works as expected diff --git a/misc/cgo/test/issue8517.go b/misc/cgo/test/issue8517.go index 4e431df921d1e3..7316ab0335d5cb 100644 --- a/misc/cgo/test/issue8517.go +++ b/misc/cgo/test/issue8517.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/misc/cgo/test/issue8694.go b/misc/cgo/test/issue8694.go index 89be7ea090763a..19071ce1595ec7 100644 --- a/misc/cgo/test/issue8694.go +++ b/misc/cgo/test/issue8694.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !android // +build !android package cgotest diff --git a/misc/cgo/test/pkg_test.go b/misc/cgo/test/pkg_test.go index 14013a4cd962b6..cbc80eee7739f4 100644 --- a/misc/cgo/test/pkg_test.go +++ b/misc/cgo/test/pkg_test.go @@ -34,6 +34,10 @@ func TestCrossPackageTests(t *testing.T) { case "arm64": t.Skip("Can't exec cmd/go subprocess on iOS.") } + case "linux": + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("skipping failing test on alpine - go.dev/issue/39857") + } } GOPATH, err := os.MkdirTemp("", "cgotest") diff --git a/misc/cgo/test/setgid2_linux.go b/misc/cgo/test/setgid2_linux.go new file mode 100644 index 00000000000000..438f5ae512d5fd --- /dev/null +++ b/misc/cgo/test/setgid2_linux.go @@ -0,0 +1,35 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stress test setgid and thread creation. A thread +// can get a SIGSETXID signal early on at thread +// initialization, causing crash. See issue 53374. + +package cgotest + +/* +#include +#include +*/ +import "C" + +import ( + "runtime" + "testing" +) + +func testSetgidStress(t *testing.T) { + const N = 50 + ch := make(chan int, N) + for i := 0; i < N; i++ { + go func() { + C.setgid(0) + ch <- 1 + runtime.LockOSThread() // so every goroutine uses a new thread + }() + } + for i := 0; i < N; i++ { + <-ch + } +} diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go index 034cc4b3719150..6b371897a73471 100644 --- a/misc/cgo/test/sigaltstack.go +++ b/misc/cgo/test/sigaltstack.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !android // +build !windows,!android // Test that the Go runtime still works if C code changes the signal stack. diff --git a/misc/cgo/test/sigprocmask.go b/misc/cgo/test/sigprocmask.go index e2b939f05e2b4b..983734cc7b620c 100644 --- a/misc/cgo/test/sigprocmask.go +++ b/misc/cgo/test/sigprocmask.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/misc/cgo/test/test.go b/misc/cgo/test/test.go index 3b8f548b13dd20..109ef987f93023 100644 --- a/misc/cgo/test/test.go +++ b/misc/cgo/test/test.go @@ -367,6 +367,11 @@ void init() { // Cgo incorrectly computed the alignment of structs // with no Go accessible fields as 0, and then panicked on // modulo-by-zero computations. + +// issue 50987 +// disable arm64 GCC warnings +#cgo CFLAGS: -Wno-psabi -Wno-unknown-warning-option + typedef struct { } foo; @@ -915,6 +920,11 @@ void issue40494(enum Enum40494 e, union Union40494* up) {} // Issue 45451, bad handling of go:notinheap types. typedef struct issue45451Undefined issue45451; + +// Issue 49633, example of cgo.Handle with void*. +extern void GoFunc49633(void*); +void cfunc49633(void *context) { GoFunc49633(context); } + */ import "C" diff --git a/misc/cgo/test/test_unix.go b/misc/cgo/test/test_unix.go index 4a234469dbc6e0..831b9ca625a1fb 100644 --- a/misc/cgo/test/test_unix.go +++ b/misc/cgo/test/test_unix.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotest diff --git a/misc/cgo/test/testdata/issue43639.go b/misc/cgo/test/testdata/issue43639.go new file mode 100644 index 00000000000000..e755fbd4bc005f --- /dev/null +++ b/misc/cgo/test/testdata/issue43639.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// Issue 43639: No runtime test needed, make sure package cgotest/issue43639 compiles well. + +import _ "cgotest/issue43639" diff --git a/misc/cgo/test/testdata/issue43639/a.go b/misc/cgo/test/testdata/issue43639/a.go new file mode 100644 index 00000000000000..fe37d5e4b0f00d --- /dev/null +++ b/misc/cgo/test/testdata/issue43639/a.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue43639 + +// #cgo CFLAGS: -W -Wall -Werror +import "C" diff --git a/misc/cgo/test/testdata/issue52611.go b/misc/cgo/test/testdata/issue52611.go new file mode 100644 index 00000000000000..32d22403abab0f --- /dev/null +++ b/misc/cgo/test/testdata/issue52611.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go 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 52611: inconsistent compiler behaviour when compiling a C.struct. +// No runtime test; just make sure it compiles. + +package cgotest + +import ( + _ "cgotest/issue52611a" + _ "cgotest/issue52611b" +) diff --git a/misc/cgo/test/testdata/issue52611a/a.go b/misc/cgo/test/testdata/issue52611a/a.go new file mode 100644 index 00000000000000..0764688ec47e8f --- /dev/null +++ b/misc/cgo/test/testdata/issue52611a/a.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611a + +/* +typedef struct Foo { + int X; +} Foo; +*/ +import "C" + +func GetX1(foo *C.struct_Foo) int32 { + return int32(foo.X) +} diff --git a/misc/cgo/test/testdata/issue52611a/b.go b/misc/cgo/test/testdata/issue52611a/b.go new file mode 100644 index 00000000000000..74a50c5deac718 --- /dev/null +++ b/misc/cgo/test/testdata/issue52611a/b.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611a + +import "C" + +func GetX2(foo *C.struct_Foo) int32 { + return int32(foo.X) +} diff --git a/misc/cgo/test/testdata/issue52611b/a.go b/misc/cgo/test/testdata/issue52611b/a.go new file mode 100644 index 00000000000000..730b52f5e9fea2 --- /dev/null +++ b/misc/cgo/test/testdata/issue52611b/a.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611b + +import "C" + +func GetX1(bar *C.struct_Bar) int32 { + return int32(bar.X) +} diff --git a/misc/cgo/test/testdata/issue52611b/b.go b/misc/cgo/test/testdata/issue52611b/b.go new file mode 100644 index 00000000000000..d30417539512af --- /dev/null +++ b/misc/cgo/test/testdata/issue52611b/b.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611b + +/* +typedef struct Bar { + int X; +} Bar; +*/ +import "C" + +func GetX2(bar *C.struct_Bar) int32 { + return int32(bar.X) +} diff --git a/misc/cgo/test/testdata/issue9400/asm_loong64.s b/misc/cgo/test/testdata/issue9400/asm_loong64.s new file mode 100644 index 00000000000000..c242fc6c623566 --- /dev/null +++ b/misc/cgo/test/testdata/issue9400/asm_loong64.s @@ -0,0 +1,28 @@ +// Copyright 2022 The Go 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 ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDV $(1024*8), R3 + + // Ask signaller to setgid + MOVW $1, R12 + DBAR + MOVW R12, ·Baton(SB) + DBAR + + // Wait for setgid completion +loop: + DBAR + MOVW ·Baton(SB), R12 + OR R13, R13, R13 // hint that we're in a spin loop + BNE R12, loop + DBAR + + // Restore stack + ADDV $(-1024*8), R3 + RET diff --git a/misc/cgo/test/testdata/issue9400_linux.go b/misc/cgo/test/testdata/issue9400_linux.go index e94a9bb45f5def..051b9ab0bbe526 100644 --- a/misc/cgo/test/testdata/issue9400_linux.go +++ b/misc/cgo/test/testdata/issue9400_linux.go @@ -15,6 +15,7 @@ import "C" import ( "runtime" + "runtime/debug" "sync/atomic" "testing" @@ -46,6 +47,14 @@ func test9400(t *testing.T) { big[i] = pattern } + // Disable GC for the duration of the test. + // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695. + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #49695. + runtime.GC() + // Temporarily rewind the stack and trigger SIGSETXID issue9400.RewindAndSetgid() diff --git a/misc/cgo/test/testx.go b/misc/cgo/test/testx.go index 823c3e13d2927a..6a8e97ddf3f1ac 100644 --- a/misc/cgo/test/testx.go +++ b/misc/cgo/test/testx.go @@ -18,7 +18,6 @@ import ( "sync" "sync/atomic" "testing" - "time" "unsafe" ) @@ -30,8 +29,7 @@ extern void doAdd(int, int); void IntoC(void); // issue 1560 -// mysleep returns the absolute start time in ms. -long long mysleep(int seconds); +extern void Issue1560InC(void); // twoSleep returns the absolute start time of the first sleep // in ms. @@ -113,6 +111,7 @@ typedef struct { int i; } Issue38408, *PIssue38408; +extern void cfunc49633(void*); // definition is in test.go */ import "C" @@ -182,35 +181,40 @@ func test1328(t *testing.T) { } // issue 1560 +// Test that C functions and Go functions run in parallel. -var sleepDone = make(chan int64) +var ( + issue1560 int32 -// parallelSleep returns the absolute difference between the start time -// of the two sleeps. -func parallelSleep(n int) int64 { - t := int64(C.twoSleep(C.int(n))) - <-sleepDone - if t < 0 { - return -t + issue1560Ch = make(chan bool, 2) +) + +//export Issue1560FromC +func Issue1560FromC() { + for atomic.LoadInt32(&issue1560) != 1 { + runtime.Gosched() + } + atomic.AddInt32(&issue1560, 1) + for atomic.LoadInt32(&issue1560) != 3 { + runtime.Gosched() } - return t + issue1560Ch <- true } -//export BackgroundSleep -func BackgroundSleep(n int32) { - go func() { - sleepDone <- int64(C.mysleep(C.int(n))) - }() +func Issue1560FromGo() { + atomic.AddInt32(&issue1560, 1) + for atomic.LoadInt32(&issue1560) != 2 { + runtime.Gosched() + } + atomic.AddInt32(&issue1560, 1) + issue1560Ch <- true } -func testParallelSleep(t *testing.T) { - sleepSec := 1 - dt := time.Duration(parallelSleep(sleepSec)) * time.Millisecond - t.Logf("difference in start time for two sleep(%d) is %v", sleepSec, dt) - // bug used to run sleeps in serial, producing a 2*sleepSec-second delay. - // we detect if the start times of those sleeps are > 0.5*sleepSec-second. - if dt >= time.Duration(sleepSec)*time.Second/2 { - t.Fatalf("parallel %d-second sleeps slept for %f seconds", sleepSec, dt.Seconds()) - } +func test1560(t *testing.T) { + go Issue1560FromGo() + go C.Issue1560InC() + <-issue1560Ch + <-issue1560Ch } // issue 2462 @@ -554,3 +558,26 @@ func GoFunc37033(handle C.uintptr_t) { // A typedef pointer can be used as the element type. // No runtime test; just make sure it compiles. var _ C.PIssue38408 = &C.Issue38408{i: 1} + +// issue 49633, example use of cgo.Handle with void* + +type data49633 struct { + msg string +} + +//export GoFunc49633 +func GoFunc49633(context unsafe.Pointer) { + h := *(*cgo.Handle)(context) + v := h.Value().(*data49633) + v.msg = "hello" +} + +func test49633(t *testing.T) { + v := &data49633{} + h := cgo.NewHandle(v) + defer h.Delete() + C.cfunc49633(unsafe.Pointer(&h)) + if v.msg != "hello" { + t.Errorf("msg = %q, want 'hello'", v.msg) + } +} diff --git a/misc/cgo/test/typeparam.go b/misc/cgo/test/typeparam.go new file mode 100644 index 00000000000000..5f766c2bcb93ab --- /dev/null +++ b/misc/cgo/test/typeparam.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// #include +import "C" + +func generic[T, U any](t T, u U) {} + +func useGeneric() { + const zero C.size_t = 0 + + generic(zero, zero) + generic[C.size_t, C.size_t](0, 0) +} diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go index 55be3c5f70710f..f8be3f9c0c6133 100644 --- a/misc/cgo/testcarchive/carchive_test.go +++ b/misc/cgo/testcarchive/carchive_test.go @@ -10,13 +10,16 @@ import ( "debug/elf" "flag" "fmt" + "io" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" + "strconv" "strings" + "sync" "syscall" "testing" "time" @@ -44,6 +47,13 @@ func TestMain(m *testing.M) { fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") os.Exit(0) } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n") + os.Exit(0) + } + } + log.SetFlags(log.Lshortfile) os.Exit(testMain(m)) } @@ -138,6 +148,9 @@ func testMain(m *testing.M) int { libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive") cc = append(cc, "-I", libgodir) + // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc. + cc = cc[:len(cc):len(cc)] + if GOOS == "windows" { exeSuffix = ".exe" } @@ -200,6 +213,7 @@ func genHeader(t *testing.T, header, dir string) { func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { t.Helper() cmd := exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode t.Log(buildcmd) if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) @@ -233,7 +247,7 @@ func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { binArgs := append(cmdToRun(exe), "arg1", "arg2") cmd = exec.Command(binArgs[0], binArgs[1:]...) if runtime.Compiler == "gccgo" { - cmd.Env = append(os.Environ(), "GCCGO=1") + cmd.Env = append(cmd.Environ(), "GCCGO=1") } if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) @@ -263,6 +277,173 @@ func checkLineComments(t *testing.T, hdrname string) { } } +// checkArchive verifies that the created library looks OK. +// We just check a couple of things now, we can add more checks as needed. +func checkArchive(t *testing.T, arname string) { + t.Helper() + + switch GOOS { + case "aix", "darwin", "ios", "windows": + // We don't have any checks for non-ELF libraries yet. + if _, err := os.Stat(arname); err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + } + default: + checkELFArchive(t, arname) + } +} + +// checkELFArchive checks an ELF archive. +func checkELFArchive(t *testing.T, arname string) { + t.Helper() + + f, err := os.Open(arname) + if err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + return + } + defer f.Close() + + // TODO(iant): put these in a shared package? But where? + const ( + magic = "!\n" + fmag = "`\n" + + namelen = 16 + datelen = 12 + uidlen = 6 + gidlen = 6 + modelen = 8 + sizelen = 10 + fmaglen = 2 + hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen + ) + + type arhdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string + } + + var magbuf [len(magic)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + t.Errorf("%s: archive too short", arname) + return + } + if string(magbuf[:]) != magic { + t.Errorf("%s: incorrect archive magic string %q", arname, magbuf) + } + + off := int64(len(magic)) + for { + if off&1 != 0 { + var b [1]byte + if _, err := f.Read(b[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err) + } + off++ + } + + var hdrbuf [hdrlen]byte + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error reading archive header at %d: %v", arname, off, err) + return + } + + var hdr arhdr + hdrslice := hdrbuf[:] + set := func(len int, ps *string) { + *ps = string(bytes.TrimSpace(hdrslice[:len])) + hdrslice = hdrslice[len:] + } + set(namelen, &hdr.name) + set(datelen, &hdr.date) + set(uidlen, &hdr.uid) + set(gidlen, &hdr.gid) + set(modelen, &hdr.mode) + set(sizelen, &hdr.size) + hdr.fmag = string(hdrslice[:fmaglen]) + hdrslice = hdrslice[fmaglen:] + if len(hdrslice) != 0 { + t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice)) + } + + if hdr.fmag != fmag { + t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off) + return + } + + size, err := strconv.ParseInt(hdr.size, 10, 64) + if err != nil { + t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err) + return + } + + off += hdrlen + + switch hdr.name { + case "__.SYMDEF", "/", "/SYM64/": + // The archive symbol map. + case "//", "ARFILENAMES/": + // The extended name table. + default: + // This should be an ELF object. + checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size)) + } + + off += size + if _, err := f.Seek(off, os.SEEK_SET); err != nil { + t.Errorf("%s: failed to seek to %d: %v", arname, off, err) + } + } +} + +// checkELFArchiveObject checks an object in an ELF archive. +func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) { + t.Helper() + + ef, err := elf.NewFile(obj) + if err != nil { + t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err) + return + } + defer ef.Close() + + // Verify section types. + for _, sec := range ef.Sections { + want := elf.SHT_NULL + switch sec.Name { + case ".text", ".data": + want = elf.SHT_PROGBITS + case ".bss": + want = elf.SHT_NOBITS + case ".symtab": + want = elf.SHT_SYMTAB + case ".strtab": + want = elf.SHT_STRTAB + case ".init_array": + want = elf.SHT_INIT_ARRAY + case ".fini_array": + want = elf.SHT_FINI_ARRAY + case ".preinit_array": + want = elf.SHT_PREINIT_ARRAY + } + if want != elf.SHT_NULL && sec.Type != want { + t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want) + } + } +} + func TestInstall(t *testing.T) { if !testWork { defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) @@ -310,7 +491,7 @@ func TestEarlySignalHandler(t *testing.T) { defer func() { os.Remove("libgo2.a") os.Remove("libgo2.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -321,6 +502,7 @@ func TestEarlySignalHandler(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") if runtime.Compiler == "gccgo" { @@ -345,37 +527,13 @@ func TestEarlySignalHandler(t *testing.T) { func TestSignalForwarding(t *testing.T) { checkSignalForwardingTest(t) + buildSignalForwardingTest(t) - if !testWork { - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() - } - - cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) - t.Fatal(err) - } - checkLineComments(t, "libgo2.h") - - ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") - if runtime.Compiler == "gccgo" { - ccArgs = append(ccArgs, "-lgo") - } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) - t.Fatal(err) - } - - cmd = exec.Command(bin[0], append(bin[1:], "1")...) + cmd := exec.Command(bin[0], append(bin[1:], "1")...) out, err := cmd.CombinedOutput() - t.Logf("%s", out) - expectSignal(t, err, syscall.SIGSEGV) + t.Logf("%v\n%s", cmd.Args, out) + expectSignal(t, err, syscall.SIGSEGV, 0) // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { @@ -383,8 +541,10 @@ func TestSignalForwarding(t *testing.T) { cmd = exec.Command(bin[0], append(bin[1:], "3")...) out, err = cmd.CombinedOutput() - t.Logf("%s", out) - expectSignal(t, err, syscall.SIGPIPE) + if len(out) > 0 { + t.Logf("%s", out) + } + expectSignal(t, err, syscall.SIGPIPE, 0) } } @@ -395,31 +555,7 @@ func TestSignalForwardingExternal(t *testing.T) { t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH) } checkSignalForwardingTest(t) - - if !testWork { - defer func() { - os.Remove("libgo2.a") - os.Remove("libgo2.h") - os.Remove("testp") - os.RemoveAll(filepath.Join(GOPATH, "pkg")) - }() - } - - cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) - t.Fatal(err) - } - checkLineComments(t, "libgo2.h") - - ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") - if runtime.Compiler == "gccgo" { - ccArgs = append(ccArgs, "-lgo") - } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) - t.Fatal(err) - } + buildSignalForwardingTest(t) // We want to send the process a signal and see if it dies. // Normally the signal goes to the C thread, the Go signal @@ -432,42 +568,27 @@ func TestSignalForwardingExternal(t *testing.T) { // fail. const tries = 20 for i := 0; i < tries; i++ { - cmd = exec.Command(bin[0], append(bin[1:], "2")...) - - stderr, err := cmd.StderrPipe() - if err != nil { - t.Fatal(err) - } - defer stderr.Close() - - r := bufio.NewReader(stderr) - - err = cmd.Start() - - if err != nil { - t.Fatal(err) - } - - // Wait for trigger to ensure that the process is started. - ok, err := r.ReadString('\n') - - // Verify trigger. - if err != nil || ok != "OK\n" { - t.Fatalf("Did not receive OK signal") - } - - // Give the program a chance to enter the sleep function. - time.Sleep(time.Millisecond) - - cmd.Process.Signal(syscall.SIGSEGV) - - err = cmd.Wait() - + err := runSignalForwardingTest(t, "2") if err == nil { continue } - if expectSignal(t, err, syscall.SIGSEGV) { + // If the signal is delivered to a C thread, as expected, + // the Go signal handler will disable itself and re-raise + // the signal, causing the program to die with SIGSEGV. + // + // It is also possible that the signal will be + // delivered to a Go thread, such as a GC thread. + // Currently when the Go runtime sees that a SIGSEGV was + // sent from a different program, it first tries to send + // the signal to the os/signal API. If nothing is looking + // for (or explicitly ignoring) SIGSEGV, then it crashes. + // Because the Go runtime is invoked via a c-archive, + // it treats this as GOTRACEBACK=crash, meaning that it + // dumps a stack trace for all goroutines, which it does + // by raising SIGQUIT. The effect is that we will see the + // program die with SIGQUIT in that case, not SIGSEGV. + if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) { return } } @@ -475,6 +596,23 @@ func TestSignalForwardingExternal(t *testing.T) { t.Errorf("program succeeded unexpectedly %d times", tries) } +func TestSignalForwardingGo(t *testing.T) { + // This test fails on darwin-amd64 because of the special + // handling of user-generated SIGSEGV signals in fixsigcode in + // runtime/signal_darwin_amd64.go. + if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" { + t.Skip("not supported on darwin-amd64") + } + + checkSignalForwardingTest(t) + buildSignalForwardingTest(t) + err := runSignalForwardingTest(t, "4") + + // Occasionally the signal will be delivered to a C thread, + // and the program will crash with SIGSEGV. + expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV) +} + // checkSignalForwardingTest calls t.Skip if the SignalForwarding test // doesn't work on this platform. func checkSignalForwardingTest(t *testing.T) { @@ -489,18 +627,121 @@ func checkSignalForwardingTest(t *testing.T) { } } +// buildSignalForwardingTest builds the executable used by the various +// signal forwarding tests. +func buildSignalForwardingTest(t *testing.T) { + if !testWork { + t.Cleanup(func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }) + } + + t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2") + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") + out, err := cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Fatal(err) + } + + checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") + + ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + t.Log(ccArgs) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Fatal(err) + } +} + +func runSignalForwardingTest(t *testing.T, arg string) error { + t.Logf("%v %s", bin, arg) + cmd := exec.Command(bin[0], append(bin[1:], arg)...) + + var out strings.Builder + cmd.Stdout = &out + + stderr, err := cmd.StderrPipe() + if err != nil { + t.Fatal(err) + } + defer stderr.Close() + + r := bufio.NewReader(stderr) + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + // Wait for trigger to ensure that process is started. + ok, err := r.ReadString('\n') + + // Verify trigger. + if err != nil || ok != "OK\n" { + t.Fatal("Did not receive OK signal") + } + + var wg sync.WaitGroup + wg.Add(1) + var errsb strings.Builder + go func() { + defer wg.Done() + io.Copy(&errsb, r) + }() + + // Give the program a chance to enter the function. + // If the program doesn't get there the test will still + // pass, although it doesn't quite test what we intended. + // This is fine as long as the program normally makes it. + time.Sleep(time.Millisecond) + + cmd.Process.Signal(syscall.SIGSEGV) + + err = cmd.Wait() + + s := out.String() + if len(s) > 0 { + t.Log(s) + } + wg.Wait() + s = errsb.String() + if len(s) > 0 { + t.Log(s) + } + + return err +} + // expectSignal checks that err, the exit status of a test program, -// shows a failure due to a specific signal. Returns whether we found -// the expected signal. -func expectSignal(t *testing.T, err error, sig syscall.Signal) bool { +// shows a failure due to a specific signal or two. Returns whether we +// found an expected signal. +func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool { + t.Helper() if err == nil { t.Error("test program succeeded unexpectedly") } else if ee, ok := err.(*exec.ExitError); !ok { t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) - } else if !ws.Signaled() || ws.Signal() != sig { - t.Errorf("got %v; expected signal %v", ee, sig) + } else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) { + if sig2 == 0 { + t.Errorf("got %q; expected signal %q", ee, sig1) + } else { + t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2) + } } else { return true } @@ -517,7 +758,7 @@ func TestOsSignal(t *testing.T) { defer func() { os.Remove("libgo3.a") os.Remove("libgo3.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -528,6 +769,7 @@ func TestOsSignal(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo3.h") + checkArchive(t, "libgo3.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") if runtime.Compiler == "gccgo" { @@ -554,7 +796,7 @@ func TestSigaltstack(t *testing.T) { defer func() { os.Remove("libgo4.a") os.Remove("libgo4.h") - os.Remove("testp") + os.Remove("testp" + exeSuffix) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -565,6 +807,7 @@ func TestSigaltstack(t *testing.T) { t.Fatal(err) } checkLineComments(t, "libgo4.h") + checkArchive(t, "libgo4.a") ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") if runtime.Compiler == "gccgo" { @@ -643,9 +886,15 @@ func TestPIE(t *testing.T) { t.Skipf("skipping PIE test on %s", GOOS) } + libgoa := "libgo.a" + if runtime.Compiler == "gccgo" { + libgoa = "liblibgo.a" + } + if !testWork { defer func() { os.Remove("testp" + exeSuffix) + os.Remove(libgoa) os.RemoveAll(filepath.Join(GOPATH, "pkg")) }() } @@ -658,18 +907,13 @@ func TestPIE(t *testing.T) { // be running this test in a GOROOT owned by root.) genHeader(t, "p.h", "./p") - cmd := exec.Command("go", "install", "-buildmode=c-archive", "./libgo") + cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo") if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) t.Fatal(err) } - libgoa := "libgo.a" - if runtime.Compiler == "gccgo" { - libgoa = "liblibgo.a" - } - - ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join(libgodir, libgoa)) + ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa) if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } @@ -747,25 +991,29 @@ func TestSIGPROF(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { t.Fatal(err) } checkLineComments(t, "libgo6.h") + checkArchive(t, "libgo6.a") ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { t.Fatal(err) } argv := cmdToRun("./testp6") cmd = exec.Command(argv[0], argv[1:]...) - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = cmd.CombinedOutput() + t.Logf("%v\n%s", argv, out) + if err != nil { t.Fatal(err) } } @@ -788,13 +1036,13 @@ func TestCompileWithoutShared(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") - t.Log(cmd.Args) out, err := cmd.CombinedOutput() - t.Logf("%s", out) + t.Logf("%v\n%s", cmd.Args, out) if err != nil { t.Fatal(err) } checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") exe := "./testnoshared" + exeSuffix @@ -804,23 +1052,22 @@ func TestCompileWithoutShared(t *testing.T) { if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - t.Log(ccArgs) out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) // If -no-pie unrecognized, try -nopie if this is possibly clang if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") - t.Log(ccArgs) out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) } // Don't use either -no-pie or -nopie if err != nil && bytes.Contains(out, []byte("unrecognized")) { - ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") - t.Log(ccArgs) + ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a") out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) } - t.Logf("%s", out) if err != nil { t.Fatal(err) } @@ -829,18 +1076,16 @@ func TestCompileWithoutShared(t *testing.T) { } binArgs := append(cmdToRun(exe), "1") - t.Log(binArgs) out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() - t.Logf("%s", out) - expectSignal(t, err, syscall.SIGSEGV) + t.Logf("%v\n%s", binArgs, out) + expectSignal(t, err, syscall.SIGSEGV, 0) // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { binArgs := append(cmdToRun(exe), "3") - t.Log(binArgs) out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() - t.Logf("%s", out) - expectSignal(t, err, syscall.SIGPIPE) + t.Logf("%v\n%s", binArgs, out) + expectSignal(t, err, syscall.SIGPIPE, 0) } } @@ -855,6 +1100,7 @@ func TestCachedInstall(t *testing.T) { buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"} cmd := exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode t.Log(buildcmd) if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) @@ -870,6 +1116,7 @@ func TestCachedInstall(t *testing.T) { } cmd = exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") t.Log(buildcmd) if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) @@ -894,26 +1141,29 @@ func TestManyCalls(t *testing.T) { } cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") - if out, err := cmd.CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { t.Fatal(err) } checkLineComments(t, "libgo7.h") + checkArchive(t, "libgo7.a") ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } - if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { - t.Logf("%s", out) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { t.Fatal(err) } argv := cmdToRun("./testp7") cmd = exec.Command(argv[0], argv[1:]...) - var sb strings.Builder - cmd.Stdout = &sb - cmd.Stderr = &sb + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb if err := cmd.Start(); err != nil { t.Fatal(err) } @@ -926,8 +1176,65 @@ func TestManyCalls(t *testing.T) { ) defer timer.Stop() - if err := cmd.Wait(); err != nil { - t.Log(sb.String()) + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { + t.Error(err) + } +} + +// Issue 49288. +func TestPreemption(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("skipping asynchronous preemption test with gccgo") + } + + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp8" + exeSuffix) + os.Remove("libgo8.a") + os.Remove("libgo8.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo8.h") + checkArchive(t, "libgo8.a") + + ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a") + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { + t.Fatal(err) + } + + argv := cmdToRun("./testp8") + cmd = exec.Command(argv[0], argv[1:]...) + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + timer := time.AfterFunc(time.Minute, + func() { + t.Error("test program timed out") + cmd.Process.Kill() + }, + ) + defer timer.Stop() + + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { t.Error(err) } } diff --git a/misc/cgo/testcarchive/testdata/libgo2/libgo2.go b/misc/cgo/testcarchive/testdata/libgo2/libgo2.go index 19c8e1a6dcb8e8..35c89ae92bbff7 100644 --- a/misc/cgo/testcarchive/testdata/libgo2/libgo2.go +++ b/misc/cgo/testcarchive/testdata/libgo2/libgo2.go @@ -49,6 +49,12 @@ func RunGoroutines() { } } +// Block blocks the current thread while running Go code. +//export Block +func Block() { + select {} +} + var P *byte // TestSEGV makes sure that an invalid address turns into a run-time Go panic. diff --git a/misc/cgo/testcarchive/testdata/libgo8/a.go b/misc/cgo/testcarchive/testdata/libgo8/a.go new file mode 100644 index 00000000000000..718418ecb8765f --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo8/a.go @@ -0,0 +1,36 @@ +// Copyright 2021 The Go Authors. 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 "C" + +import ( + "os" + "runtime" + "sync/atomic" +) + +var started int32 + +// Start a goroutine that loops forever. +func init() { + runtime.GOMAXPROCS(1) + go func() { + for { + atomic.StoreInt32(&started, 1) + } + }() +} + +//export GoFunction8 +func GoFunction8() { + for atomic.LoadInt32(&started) == 0 { + runtime.Gosched() + } + os.Exit(0) +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/main5.c b/misc/cgo/testcarchive/testdata/main5.c index d431ce01ce5251..c64c246fdea8e5 100644 --- a/misc/cgo/testcarchive/testdata/main5.c +++ b/misc/cgo/testcarchive/testdata/main5.c @@ -29,10 +29,6 @@ int main(int argc, char** argv) { verbose = (argc > 2); - if (verbose) { - printf("calling RunGoroutines\n"); - } - Noop(); switch (test) { @@ -90,6 +86,15 @@ int main(int argc, char** argv) { printf("did not receive SIGPIPE\n"); return 0; } + case 4: { + fprintf(stderr, "OK\n"); + fflush(stderr); + + if (verbose) { + printf("calling Block\n"); + } + Block(); + } default: printf("Unknown test: %d\n", test); return 0; diff --git a/misc/cgo/testcarchive/testdata/main8.c b/misc/cgo/testcarchive/testdata/main8.c new file mode 100644 index 00000000000000..95fb7a349e1450 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main8.c @@ -0,0 +1,16 @@ +// Copyright 2021 The Go 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 preemption. + +#include + +#include "libgo8.h" + +int main() { + GoFunction8(); + + // That should have exited the program. + abort(); +} diff --git a/misc/cgo/testcshared/cshared_test.go b/misc/cgo/testcshared/cshared_test.go index 19ad8c76a838b5..d6219dc2926d7e 100644 --- a/misc/cgo/testcshared/cshared_test.go +++ b/misc/cgo/testcshared/cshared_test.go @@ -5,6 +5,7 @@ package cshared_test import ( + "bufio" "bytes" "debug/elf" "debug/pe" @@ -43,6 +44,12 @@ func testMain(m *testing.M) int { fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") os.Exit(0) } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n") + os.Exit(0) + } + } GOOS = goEnv("GOOS") GOARCH = goEnv("GOARCH") @@ -117,6 +124,9 @@ func testMain(m *testing.M) int { } cc = append(cc, "-I", filepath.Join("pkg", libgodir)) + // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc. + cc = cc[:len(cc):len(cc)] + if GOOS == "windows" { exeSuffix = ".exe" } @@ -147,16 +157,22 @@ func testMain(m *testing.M) int { // The installation directory format varies depending on the platform. output, err := exec.Command("go", "list", "-buildmode=c-shared", - "-installsuffix", "testcshared", "-f", "{{.Target}}", - "./libgo").CombinedOutput() + "runtime/cgo").CombinedOutput() if err != nil { log.Panicf("go list failed: %v\n%s", err, output) } - target := string(bytes.TrimSpace(output)) - libgoname = filepath.Base(target) - installdir = filepath.Dir(target) - libSuffix = strings.TrimPrefix(filepath.Ext(target), ".") + runtimeCgoTarget := string(bytes.TrimSpace(output)) + libSuffix = strings.TrimPrefix(filepath.Ext(runtimeCgoTarget), ".") + + defer func() { + if installdir != "" { + err := os.RemoveAll(installdir) + if err != nil { + log.Panic(err) + } + } + }() return m.Run() } @@ -200,7 +216,7 @@ func adbRun(t *testing.T, env []string, adbargs ...string) string { args := append(adbCmd(), "exec-out") // Propagate LD_LIBRARY_PATH to the adb shell invocation. for _, e := range env { - if strings.Index(e, "LD_LIBRARY_PATH=") != -1 { + if strings.Contains(e, "LD_LIBRARY_PATH=") { adbargs = append([]string{e}, adbargs...) break } @@ -280,8 +296,13 @@ func createHeaders() error { } // Generate a C header file for libgo itself. - args = []string{"go", "install", "-buildmode=c-shared", - "-installsuffix", "testcshared", "./libgo"} + installdir, err = os.MkdirTemp("", "testcshared") + if err != nil { + return err + } + libgoname = "libgo." + libSuffix + + args = []string{"go", "build", "-buildmode=c-shared", "-o", filepath.Join(installdir, libgoname), "./libgo"} cmd = exec.Command(args[0], args[1:]...) out, err = cmd.CombinedOutput() if err != nil { @@ -326,7 +347,7 @@ func createHeaders() error { base, name := filepath.Split(args[0]) args[0] = filepath.Join(base, "llvm-dlltool") var machine string - switch strings.SplitN(name, "-", 2)[0] { + switch prefix, _, _ := strings.Cut(name, "-"); prefix { case "i686": machine = "i386" case "x86_64": @@ -369,6 +390,7 @@ func createHeadersOnce(t *testing.T) { headersErr = createHeaders() }) if headersErr != nil { + t.Helper() t.Fatal(headersErr) } } @@ -701,12 +723,15 @@ func TestCachedInstall(t *testing.T) { copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "libgo", "libgo.go"), filepath.Join("libgo", "libgo.go")) copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "p", "p.go"), filepath.Join("p", "p.go")) - env := append(os.Environ(), "GOPATH="+tmpdir, "GOBIN="+filepath.Join(tmpdir, "bin")) - buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"} cmd := exec.Command(buildcmd[0], buildcmd[1:]...) cmd.Dir = filepath.Join(tmpdir, "src", "testcshared") + env := append(cmd.Environ(), + "GOPATH="+tmpdir, + "GOBIN="+filepath.Join(tmpdir, "bin"), + "GO111MODULE=off", // 'go install' only works in GOPATH mode + ) cmd.Env = env t.Log(buildcmd) out, err := cmd.CombinedOutput() @@ -781,10 +806,10 @@ func copyFile(t *testing.T, dst, src string) { func TestGo2C2Go(t *testing.T) { switch GOOS { - case "darwin", "ios": - // Darwin shared libraries don't support the multiple + case "darwin", "ios", "windows": + // Non-ELF shared libraries don't support the multiple // copies of the runtime package implied by this test. - t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061") + t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS) case "android": t.Skip("test fails on android; issue 29087") } @@ -835,3 +860,51 @@ func TestGo2C2Go(t *testing.T) { run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2") runExe(t, runenv, bin) } + +func TestIssue36233(t *testing.T) { + t.Parallel() + + // Test that the export header uses GoComplex64 and GoComplex128 + // for complex types. + + tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + const exportHeader = "issue36233.h" + + run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go") + data, err := os.ReadFile(exportHeader) + if err != nil { + t.Fatal(err) + } + + funcs := []struct{ name, signature string }{ + {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"}, + {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"}, + {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"}, + {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"}, + } + + scanner := bufio.NewScanner(bytes.NewReader(data)) + var found int + for scanner.Scan() { + b := scanner.Bytes() + for _, fn := range funcs { + if bytes.Contains(b, []byte(fn.name)) { + found++ + if !bytes.Contains(b, []byte(fn.signature)) { + t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature) + } + } + } + } + if err = scanner.Err(); err != nil { + t.Errorf("scanner encountered error: %v", err) + } + if found != len(funcs) { + t.Error("missing functions") + } +} diff --git a/misc/cgo/testcshared/testdata/issue36233/issue36233.go b/misc/cgo/testcshared/testdata/issue36233/issue36233.go new file mode 100644 index 00000000000000..d0d1e5d50ae375 --- /dev/null +++ b/misc/cgo/testcshared/testdata/issue36233/issue36233.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. 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 + +// #include +import "C" + +//export exportComplex64 +func exportComplex64(v complex64) complex64 { + return v +} + +//export exportComplex128 +func exportComplex128(v complex128) complex128 { + return v +} + +//export exportComplexfloat +func exportComplexfloat(v C.complexfloat) C.complexfloat { + return v +} + +//export exportComplexdouble +func exportComplexdouble(v C.complexdouble) C.complexdouble { + return v +} + +func main() {} diff --git a/misc/cgo/testcshared/testdata/libgo2/dup2.go b/misc/cgo/testcshared/testdata/libgo2/dup2.go index d18f0b130d3f76..d343aa54d9a877 100644 --- a/misc/cgo/testcshared/testdata/libgo2/dup2.go +++ b/misc/cgo/testcshared/testdata/libgo2/dup2.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux,!arm64 netbsd openbsd +// +build darwin dragonfly freebsd linux,!arm64,!riscv64 netbsd openbsd package main diff --git a/misc/cgo/testcshared/testdata/libgo2/dup3.go b/misc/cgo/testcshared/testdata/libgo2/dup3.go index c9c65a6e3c1f62..459f0dc196874f 100644 --- a/misc/cgo/testcshared/testdata/libgo2/dup3.go +++ b/misc/cgo/testcshared/testdata/libgo2/dup3.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 linux,arm64 +// +build linux,arm64 linux,riscv64 package main diff --git a/misc/cgo/testgodefs/testdata/issue48396.go b/misc/cgo/testgodefs/testdata/issue48396.go new file mode 100644 index 00000000000000..d4c192403fde50 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue48396.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +// from +struct issue48396 { + int fd; + int bpf_fd; +}; +*/ +import "C" + +type Issue48396 C.struct_issue48396 diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go index 4a3f6a701cc0e7..5c670f3d329c1a 100644 --- a/misc/cgo/testgodefs/testdata/main.go +++ b/misc/cgo/testgodefs/testdata/main.go @@ -28,6 +28,9 @@ var v7 = S{} // Test that #define'd type is fully defined var _ = issue38649{X: 0} +// Test that prefixes do not cause duplicate field names. +var _ = Issue48396{Fd: 1, Bpf_fd: 2} + func main() { pass := true diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go index aae34043605d34..d03769ea87cce1 100644 --- a/misc/cgo/testgodefs/testgodefs_test.go +++ b/misc/cgo/testgodefs/testgodefs_test.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "testing" ) @@ -25,6 +26,7 @@ var filePrefixes = []string{ "issue37621", "issue38649", "issue39534", + "issue48396", } func TestGoDefs(t *testing.T) { @@ -57,9 +59,32 @@ func TestGoDefs(t *testing.T) { t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) } - if err := os.WriteFile(filepath.Join(dir, fp+"_defs.go"), out, 0644); err != nil { + fn := fp + "_defs.go" + if err := os.WriteFile(filepath.Join(dir, fn), out, 0644); err != nil { t.Fatal(err) } + + // Verify that command line arguments are not rewritten in the generated comment, + // see go.dev/issue/52063 + hasGeneratedByComment := false + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + cgoExe := "cgo" + if runtime.GOOS == "windows" { + cgoExe = "cgo.exe" + } + if !strings.HasPrefix(line, "// "+cgoExe+" -godefs") { + continue + } + if want := "// " + cgoExe + " " + strings.Join(cmd.Args[3:], " "); line != want { + t.Errorf("%s: got generated comment %q, want %q", fn, line, want) + } + hasGeneratedByComment = true + break + } + + if !hasGeneratedByComment { + t.Errorf("%s: comment with generating cgo -godefs command not found", fn) + } } main, err := os.ReadFile(filepath.Join("testdata", "main.go")) diff --git a/misc/cgo/testplugin/plugin_test.go b/misc/cgo/testplugin/plugin_test.go index 9697dbf7a78e3c..285681018ab0aa 100644 --- a/misc/cgo/testplugin/plugin_test.go +++ b/misc/cgo/testplugin/plugin_test.go @@ -19,6 +19,7 @@ import ( ) var gcflags string = os.Getenv("GO_GCFLAGS") +var goroot string func TestMain(m *testing.M) { flag.Parse() @@ -43,6 +44,12 @@ func prettyPrintf(format string, args ...interface{}) { } func testMain(m *testing.M) int { + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + goroot = filepath.Join(cwd, "../../..") + // Copy testdata into GOPATH/src/testplugin, along with a go.mod file // declaring the same path. @@ -113,7 +120,7 @@ func goCmd(t *testing.T, op string, args ...string) { if t != nil { t.Helper() } - run(t, "go", append([]string{op, "-gcflags", gcflags}, args...)...) + run(t, filepath.Join(goroot, "bin", "go"), append([]string{op, "-gcflags", gcflags}, args...)...) } // escape converts a string to something suitable for a shell command line. @@ -211,7 +218,7 @@ func TestIssue18676(t *testing.T) { func TestIssue19534(t *testing.T) { // Test that we can load a plugin built in a path with non-alpha characters. - goCmd(t, "build", "-buildmode=plugin", "-ldflags='-pluginpath=issue.19534'", "-o", "plugin.so", "./issue19534/plugin.go") + goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go") goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go") run(t, "./issue19534.exe") } @@ -265,10 +272,6 @@ func TestIssue25756(t *testing.T) { // Test with main using -buildmode=pie with plugin for issue #43228 func TestIssue25756pie(t *testing.T) { - if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" { - t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239") - } - goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin") goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go") run(t, "./issue25756pie.exe") @@ -287,9 +290,53 @@ func TestMethod2(t *testing.T) { run(t, "./method2.exe") } +func TestMethod3(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go") + goCmd(t, "build", "-o", "method3.exe", "./method3/main.go") + run(t, "./method3.exe") +} + func TestIssue44956(t *testing.T) { goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go") goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go") goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go") run(t, "./issue44956.exe") } + +func TestIssue52937(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go") +} + +func TestIssue53989(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go") + goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go") + run(t, "./issue53989.exe") +} + +func TestForkExec(t *testing.T) { + // Issue 38824: importing the plugin package causes it hang in forkExec on darwin. + + t.Parallel() + goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") + + var cmd *exec.Cmd + done := make(chan int, 1) + + go func() { + for i := 0; i < 100; i++ { + cmd = exec.Command("./forkexec.exe", "1") + err := cmd.Run() + if err != nil { + t.Errorf("running command failed: %v", err) + break + } + } + done <- 1 + }() + select { + case <-done: + case <-time.After(5 * time.Minute): + cmd.Process.Kill() + t.Fatalf("subprocess hang") + } +} diff --git a/misc/cgo/testplugin/testdata/forkexec/main.go b/misc/cgo/testplugin/testdata/forkexec/main.go new file mode 100644 index 00000000000000..3169ff5f04d100 --- /dev/null +++ b/misc/cgo/testplugin/testdata/forkexec/main.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/exec" + _ "plugin" + "sync" +) + +func main() { + if os.Args[1] != "1" { + return + } + + var wg sync.WaitGroup + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // does not matter what we exec, just exec itself + cmd := exec.Command("./forkexec.exe", "0") + cmd.Run() + }() + } + wg.Wait() +} diff --git a/misc/cgo/testplugin/testdata/issue52937/main.go b/misc/cgo/testplugin/testdata/issue52937/main.go new file mode 100644 index 00000000000000..66f09effea28c3 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue52937/main.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() {} +func F[T any]() {} +func G[T any](T) {} diff --git a/misc/cgo/testplugin/testdata/issue53989/main.go b/misc/cgo/testplugin/testdata/issue53989/main.go new file mode 100644 index 00000000000000..6907dfd858096c --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/main.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go 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 53989: the use of jump table caused a function +// from the plugin jumps in the middle of the function +// to the function with the same name in the main +// executable. As these two functions may be compiled +// differently as plugin needs to be PIC, this causes +// crash. + +package main + +import ( + "plugin" + + "testplugin/issue53989/p" +) + +func main() { + p.Square(7) // call the function in main executable + + p, err := plugin.Open("issue53989.so") + if err != nil { + panic(err) + } + f, err := p.Lookup("Square") + if err != nil { + panic(err) + } + f.(func(int))(7) // call the plugin one +} diff --git a/misc/cgo/testplugin/testdata/issue53989/p/p.go b/misc/cgo/testplugin/testdata/issue53989/p/p.go new file mode 100644 index 00000000000000..02567c1cee07bb --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/p/p.go @@ -0,0 +1,52 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import ( + "fmt" + "runtime" +) + +var y int + +//go:noinline +func Square(x int) { + var pc0, pc1 [1]uintptr + runtime.Callers(1, pc0[:]) // get PC at entry + + // a switch using jump table + switch x { + case 1: + y = 1 + case 2: + y = 4 + case 3: + y = 9 + case 4: + y = 16 + case 5: + y = 25 + case 6: + y = 36 + case 7: + y = 49 + case 8: + y = 64 + default: + panic("too large") + } + + // check PC is in the same function + runtime.Callers(1, pc1[:]) + if pc1[0] < pc0[0] || pc1[0] > pc0[0]+1000000 { + fmt.Printf("jump across DSO boundary. pc0=%x, pc1=%x\n", pc0[0], pc1[0]) + panic("FAIL") + } + + if y != x*x { + fmt.Printf("x=%d y=%d!=%d\n", x, y, x*x) + panic("FAIL") + } +} diff --git a/misc/cgo/testplugin/testdata/issue53989/plugin.go b/misc/cgo/testplugin/testdata/issue53989/plugin.go new file mode 100644 index 00000000000000..a753ee4419d735 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/plugin.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. 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 "testplugin/issue53989/p" + +func Square(x int) { // export Square for plugin + p.Square(x) +} + +func main() {} diff --git a/misc/cgo/testplugin/testdata/method3/main.go b/misc/cgo/testplugin/testdata/method3/main.go new file mode 100644 index 00000000000000..a3a51711cda5ae --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/main.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// An unexported method can be reachable from the plugin via interface +// when a package is shared. So it need to be live. + +package main + +import ( + "plugin" + + "testplugin/method3/p" +) + +var i p.I + +func main() { + pl, err := plugin.Open("method3.so") + if err != nil { + panic(err) + } + + f, err := pl.Lookup("F") + if err != nil { + panic(err) + } + + f.(func())() + + i = p.T(123) +} diff --git a/misc/cgo/testplugin/testdata/method3/p/p.go b/misc/cgo/testplugin/testdata/method3/p/p.go new file mode 100644 index 00000000000000..3846bc07f50a4c --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/p/p.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T int + +func (T) m() { println("m") } + +type I interface { m() } + +func F() { + i.m() +} + +var i I = T(123) diff --git a/misc/cgo/testplugin/testdata/method3/plugin.go b/misc/cgo/testplugin/testdata/method3/plugin.go new file mode 100644 index 00000000000000..bd25b31857e92d --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/plugin.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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 "testplugin/method3/p" + +func main() {} + +func F() { p.F() } diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go new file mode 100644 index 00000000000000..1c423add16c5fa --- /dev/null +++ b/misc/cgo/testsanitizers/asan_test.go @@ -0,0 +1,97 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "strings" + "testing" +) + +func TestASAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The asan tests require support for the -asan option. + if !aSanSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch) + } + // The current implementation is only compatible with the ASan library from version + // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the + // -asan option must use a compatible version of ASan library, which requires that + // the gcc version is not less than 7 and the clang version is not less than 9, + // otherwise a segmentation fault will occur. + if !compilerRequiredAsanVersion(goos, goarch) { + t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch) + } + + t.Parallel() + requireOvercommit(t) + config := configure("address") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + memoryAccessError string + errorLocation string + }{ + {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"}, + {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"}, + {src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"}, + {src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"}, + {src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"}, + {src: "asan_useAfterReturn.go"}, + {src: "asan_unsafe_fail1.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail1.go:25"}, + {src: "asan_unsafe_fail2.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail2.go:25"}, + {src: "asan_unsafe_fail3.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail3.go:18"}, + {src: "asan_global1_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global1_fail.go:12"}, + {src: "asan_global2_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global2_fail.go:19"}, + {src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"}, + {src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"}, + {src: "asan_global5.go"}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src))) + + cmd := hangProneCmd(outPath) + if tc.memoryAccessError != "" { + outb, err := cmd.CombinedOutput() + out := string(outb) + if err != nil && strings.Contains(out, tc.memoryAccessError) { + // This string is output if the + // sanitizer library needs a + // symbolizer program and can't find it. + const noSymbolizer = "external symbolizer" + // Check if -asan option can correctly print where the error occurred. + if tc.errorLocation != "" && + !strings.Contains(out, tc.errorLocation) && + !strings.Contains(out, noSymbolizer) && + compilerSupportsLocation() { + + t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out) + } + return + } + t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out) + } + mustRun(t, cmd) + }) + } +} diff --git a/misc/cgo/testsanitizers/cc_test.go b/misc/cgo/testsanitizers/cc_test.go index 384b6250e1ef16..72af42660d3a6b 100644 --- a/misc/cgo/testsanitizers/cc_test.go +++ b/misc/cgo/testsanitizers/cc_test.go @@ -20,6 +20,7 @@ import ( "sync" "syscall" "testing" + "time" "unicode" ) @@ -90,9 +91,26 @@ func replaceEnv(cmd *exec.Cmd, key, value string) { // mustRun executes t and fails cmd with a well-formatted message if it fails. func mustRun(t *testing.T, cmd *exec.Cmd) { t.Helper() - out, err := cmd.CombinedOutput() + out := new(strings.Builder) + cmd.Stdout = out + cmd.Stderr = out + + err := cmd.Start() if err != nil { - t.Fatalf("%#q exited with %v\n%s", strings.Join(cmd.Args, " "), err, out) + t.Fatalf("%v: %v", cmd, err) + } + + if deadline, ok := t.Deadline(); ok { + timeout := time.Until(deadline) + timeout -= timeout / 10 // Leave 10% headroom for logging and cleanup. + timer := time.AfterFunc(timeout, func() { + cmd.Process.Signal(syscall.SIGQUIT) + }) + defer timer.Stop() + } + + if err := cmd.Wait(); err != nil { + t.Fatalf("%v exited with %v\n%s", cmd, err, out) } } @@ -184,14 +202,13 @@ func compilerVersion() (version, error) { var match [][]byte if bytes.HasPrefix(out, []byte("gcc")) { compiler.name = "gcc" - - cmd, err := cc("-dumpversion") + cmd, err := cc("-dumpfullversion", "-dumpversion") if err != nil { return err } out, err := cmd.Output() if err != nil { - // gcc, but does not support gcc's "-dumpversion" flag?! + // gcc, but does not support gcc's "-v" flag?! return err } gccRE := regexp.MustCompile(`(\d+)\.(\d+)`) @@ -218,6 +235,55 @@ func compilerVersion() (version, error) { return compiler.version, compiler.err } +// compilerSupportsLocation reports whether the compiler should be +// able to provide file/line information in backtraces. +func compilerSupportsLocation() bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + return compiler.major >= 10 + case "clang": + return true + default: + return false + } +} + +// compilerRequiredTsanVersion reports whether the compiler is the version required by Tsan. +// Only restrictions for ppc64le are known; otherwise return true. +func compilerRequiredTsanVersion(goos, goarch string) bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + if compiler.name == "gcc" && goarch == "ppc64le" { + return compiler.major >= 9 + } + return true +} + +// compilerRequiredAsanVersion reports whether the compiler is the version required by Asan. +func compilerRequiredAsanVersion(goos, goarch string) bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + if goarch == "ppc64le" { + return compiler.major >= 9 + } + return compiler.major >= 7 + case "clang": + return compiler.major >= 9 + default: + return false + } +} + type compilerCheck struct { once sync.Once err error @@ -267,6 +333,11 @@ func configure(sanitizer string) *config { c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan") } + case "address": + c.goFlags = append(c.goFlags, "-asan") + // Set the debug mode to print the C stack trace. + c.cFlags = append(c.cFlags, "-g") + default: panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer)) } @@ -344,7 +415,7 @@ func (c *config) checkCSanitizer() (skip bool, err error) { if os.IsNotExist(err) { return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err) } - snippet := bytes.SplitN(out, []byte{'\n'}, 2)[0] + snippet, _, _ := bytes.Cut(out, []byte("\n")) return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet) } @@ -450,3 +521,14 @@ func mSanSupported(goos, goarch string) bool { return false } } + +// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported, +// because the internal pacakage can't be used here. +func aSanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "arm64" || goarch == "riscv64" || goarch == "ppc64le" + default: + return false + } +} diff --git a/misc/cgo/testsanitizers/cshared_test.go b/misc/cgo/testsanitizers/cshared_test.go index 8fd03715a11bd8..21b13ce4ed0dc5 100644 --- a/misc/cgo/testsanitizers/cshared_test.go +++ b/misc/cgo/testsanitizers/cshared_test.go @@ -52,6 +52,11 @@ func TestShared(t *testing.T) { t.Logf("skipping %s test on %s/%s; -msan option is not supported.", name, GOOS, GOARCH) continue } + if tc.sanitizer == "thread" && !compilerRequiredTsanVersion(GOOS, GOARCH) { + t.Logf("skipping %s test on %s/%s; compiler version too old for -tsan.", name, GOOS, GOARCH) + continue + } + t.Run(name, func(t *testing.T) { t.Parallel() config := configure(tc.sanitizer) diff --git a/misc/cgo/testsanitizers/testdata/asan1_fail.go b/misc/cgo/testsanitizers/testdata/asan1_fail.go new file mode 100644 index 00000000000000..80289e5c30452f --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan1_fail.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. 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 + +/* +#include +#include + +int *p; +int* test() { + p = (int *)malloc(2 * sizeof(int)); + free(p); + return p; +} +*/ +import "C" +import "fmt" + +func main() { + // C passes Go an invalid pointer. + a := C.test() + // Use after free + *a = 2 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Println(*a) +} diff --git a/misc/cgo/testsanitizers/testdata/asan2_fail.go b/misc/cgo/testsanitizers/testdata/asan2_fail.go new file mode 100644 index 00000000000000..3ab060857107ae --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan2_fail.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Go Authors. 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 + +/* +#include +#include + +int *p; +int* f() { + int i; + p = (int *)malloc(5*sizeof(int)); + for (i = 0; i < 5; i++) { + p[i] = i+10; + } + return p; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +func main() { + a := C.f() + q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5)) + // Access to C pointer out of bounds. + *q5 = 100 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Printf("q5: %d, %x\n", *q5, q5) +} diff --git a/misc/cgo/testsanitizers/testdata/asan3_fail.go b/misc/cgo/testsanitizers/testdata/asan3_fail.go new file mode 100644 index 00000000000000..9f6d26dd89dbce --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan3_fail.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. 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 + +/* +#include +#include + +void test(int *a) { + // Access Go pointer out of bounds. + int c = a[5]; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[5]=%d\n", c); +} +*/ +import "C" + +func main() { + cIntSlice := []C.int{200, 201, 203, 203, 204} + C.test(&cIntSlice[0]) +} diff --git a/misc/cgo/testsanitizers/testdata/asan4_fail.go b/misc/cgo/testsanitizers/testdata/asan4_fail.go new file mode 100644 index 00000000000000..12098458ae91d2 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan4_fail.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. 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 + +/* +#include +#include + +void test(int* a) { + // Access Go pointer out of bounds. + a[3] = 300; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[3]=%d\n", a[3]); +}*/ +import "C" + +func main() { + var cIntArray [2]C.int + C.test(&cIntArray[0]) // cIntArray is moved to heap. +} diff --git a/misc/cgo/testsanitizers/testdata/asan5_fail.go b/misc/cgo/testsanitizers/testdata/asan5_fail.go new file mode 100644 index 00000000000000..d6853eab7333d9 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan5_fail.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "runtime" + "unsafe" +) + +func main() { + p := new([1024 * 1000]int) + p[0] = 10 + r := bar(&p[1024*1000-1]) + fmt.Printf("r value is %d", r) +} + +func bar(a *int) int { + p := unsafe.Add(unsafe.Pointer(a), 2*unsafe.Sizeof(int(1))) + runtime.ASanWrite(p, 8) // BOOM + *((*int)(p)) = 10 + return *((*int)(p)) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global1_fail.go b/misc/cgo/testsanitizers/testdata/asan_global1_fail.go new file mode 100644 index 00000000000000..6cfc0b713812ff --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global1_fail.go @@ -0,0 +1,25 @@ +// Copyright 2022 The Go Authors. 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 + +/* +#include +#include + +int test(int *a) { + a[2] = 300; // BOOM + return a[2]; +} +*/ +import "C" + +import "fmt" + +var cIntArray [2]C.int + +func main() { + r := C.test(&cIntArray[0]) + fmt.Println("r value = ", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global2_fail.go b/misc/cgo/testsanitizers/testdata/asan_global2_fail.go new file mode 100644 index 00000000000000..19326333682f72 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global2_fail.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Go Authors. 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 + +/* +#include +#include + +struct ss { + int *p; + int len; + int cap; +}; + +int test(struct ss *a) { + struct ss *t = a + 1; + t->len = 100; // BOOM + return t->len; +} +*/ +import "C" +import "fmt" + +var tt C.struct_ss + +func main() { + r := C.test(&tt) + fmt.Println("r value = ", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global3_fail.go b/misc/cgo/testsanitizers/testdata/asan_global3_fail.go new file mode 100644 index 00000000000000..9ab026c7fa147a --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global3_fail.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. 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 + +/* +#include +#include + +int test(int *a) { + int* p = a+1; + *p = 10; // BOOM + return *p; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +var cIntV C.int + +func main() { + r := C.test((*C.int)(unsafe.Pointer(&cIntV))) + fmt.Printf("r value is %d", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global4_fail.go b/misc/cgo/testsanitizers/testdata/asan_global4_fail.go new file mode 100644 index 00000000000000..d593598d5b29ba --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global4_fail.go @@ -0,0 +1,25 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +var intGlo int + +func main() { + r := bar(&intGlo) + fmt.Printf("r value is %d", r) +} + +func bar(a *int) int { + p := (*int)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(int(1)))) + if *p == 10 { // BOOM + fmt.Println("its value is 10") + } + return *p +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global5.go b/misc/cgo/testsanitizers/testdata/asan_global5.go new file mode 100644 index 00000000000000..0ed103da4f2e8d --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global5.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Any struct { + s string + b int64 +} + +var Sg = []interface{}{ + Any{"a", 10}, +} + +func main() { + fmt.Println(Sg[0]) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go new file mode 100644 index 00000000000000..ec54a66880c089 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + c := add(a, b) + d := a + b + fmt.Println(c, d) +} + +//go:noinline +func add(a1, b1 int) int { + // The arguments. + // When -asan is enabled, unsafe.Pointer(&a1) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a1), 1*unsafe.Sizeof(int(1)))) + *p = 10 // BOOM + return a1 + b1 +} diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go new file mode 100644 index 00000000000000..70f21275af58ee --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + c := add(a, b) + d := a + b + fmt.Println(c, d) +} + +//go:noinline +func add(a1, b1 int) (ret int) { + // The return value + // When -asan is enabled, the unsafe.Pointer(&ret) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&ret), 1*unsafe.Sizeof(int(1)))) + *p = 123 // BOOM + ret = a1 + b1 + return +} diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go new file mode 100644 index 00000000000000..47a8a072ef4d36 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go @@ -0,0 +1,21 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + // The local variables. + // When -asan is enabled, the unsafe.Pointer(&a) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a), 1*unsafe.Sizeof(int(1)))) + *p = 20 // BOOM + d := a + b + fmt.Println(d) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go new file mode 100644 index 00000000000000..3d3d5a6ab1ad47 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. 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 -fsanitize=address option of C compier can detect stack-use-after-return bugs. +// In the following program, the local variable 'local' was moved to heap by the Go +// compiler because foo() is returning the reference to 'local', and return stack of +// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local' +// must be available even after foo() has finished. Therefore, Go has no such issue. + +import "fmt" + +var ptr *int + +func main() { + foo() + fmt.Printf("ptr=%x, %v", *ptr, ptr) +} + +func foo() { + var local int + local = 1 + ptr = &local // local is moved to heap. +} diff --git a/misc/cgo/testsanitizers/testdata/tsan11.go b/misc/cgo/testsanitizers/testdata/tsan11.go index 70ac9c8ae2cf5e..189e10f699797c 100644 --- a/misc/cgo/testsanitizers/testdata/tsan11.go +++ b/misc/cgo/testsanitizers/testdata/tsan11.go @@ -45,7 +45,7 @@ static void register_handler(int signo) { import "C" func main() { - ch := make(chan os.Signal) + ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR2) C.register_handler(C.int(syscall.SIGUSR1)) diff --git a/misc/cgo/testsanitizers/testdata/tsan12.go b/misc/cgo/testsanitizers/testdata/tsan12.go index 3e767eee1f83e0..0ef545d09b6f59 100644 --- a/misc/cgo/testsanitizers/testdata/tsan12.go +++ b/misc/cgo/testsanitizers/testdata/tsan12.go @@ -22,7 +22,7 @@ import ( import "C" func main() { - ch := make(chan os.Signal) + ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGUSR1) if err := exec.Command("true").Run(); err != nil { diff --git a/misc/cgo/testsanitizers/tsan_test.go b/misc/cgo/testsanitizers/tsan_test.go index ec4e0033fb43a4..00ad313b9cb6d2 100644 --- a/misc/cgo/testsanitizers/tsan_test.go +++ b/misc/cgo/testsanitizers/tsan_test.go @@ -10,6 +10,19 @@ import ( ) func TestTSAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The msan tests require support for the -msan option. + if !compilerRequiredTsanVersion(goos, goarch) { + t.Skipf("skipping on %s/%s; compiler version for -tsan option is too old.", goos, goarch) + } + t.Parallel() requireOvercommit(t) config := configure("thread") diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index e77f84891543f5..92c2166674f40b 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -20,12 +20,14 @@ import ( "regexp" "runtime" "sort" + "strconv" "strings" "testing" "time" ) var gopathInstallDir, gorootInstallDir string +var oldGOROOT string // This is the smallest set of packages we can link into a shared // library (runtime/cgo is built implicitly). @@ -55,11 +57,11 @@ func runWithEnv(t *testing.T, msg string, env []string, args ...string) { // t.Fatalf if the command fails. func goCmd(t *testing.T, args ...string) string { newargs := []string{args[0]} - if *testX { - newargs = append(newargs, "-x") + if *testX && args[0] != "env" { + newargs = append(newargs, "-x", "-ldflags=-v") } newargs = append(newargs, args[1:]...) - c := exec.Command("go", newargs...) + c := exec.Command(filepath.Join(oldGOROOT, "bin", "go"), newargs...) stderr := new(strings.Builder) c.Stderr = stderr @@ -89,6 +91,12 @@ func goCmd(t *testing.T, args ...string) string { // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). func testMain(m *testing.M) (int, error) { + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + oldGOROOT = filepath.Join(cwd, "../../..") + workDir, err := os.MkdirTemp("", "shared_test") if err != nil { return 0, err @@ -100,6 +108,15 @@ func testMain(m *testing.M) (int, error) { defer os.RemoveAll(workDir) } + // -buildmode=shared fundamentally does not work in module mode. + // (It tries to share package dependencies across builds, but in module mode + // each module has its own distinct set of dependency versions.) + // We would like to eliminate it (see https://go.dev/issue/47788), + // but first need to figure out a replacement that covers the small subset + // of use-cases where -buildmode=shared still works today. + // For now, run the tests in GOPATH mode only. + os.Setenv("GO111MODULE", "off") + // Some tests need to edit the source in GOPATH, so copy this directory to a // temporary directory and chdir to that. gopath := filepath.Join(workDir, "gopath") @@ -186,11 +203,6 @@ func cloneTestdataModule(gopath string) (string, error) { // GOROOT/pkg relevant to this test into the given directory. // It must be run from within the testdata module. func cloneGOROOTDeps(goroot string) error { - oldGOROOT := strings.TrimSpace(goCmd(nil, "env", "GOROOT")) - if oldGOROOT == "" { - return fmt.Errorf("go env GOROOT returned an empty string") - } - // Before we clone GOROOT, figure out which packages we need to copy over. listArgs := []string{ "list", @@ -461,7 +473,9 @@ func TestTrivialExecutable(t *testing.T) { run(t, "trivial executable", "../../bin/trivial") AssertIsLinkedTo(t, "../../bin/trivial", soname) AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) - checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "../../bin/trivial", 256000) } // Build a trivial program in PIE mode that links against the shared runtime and check it runs. @@ -470,7 +484,9 @@ func TestTrivialExecutablePIE(t *testing.T) { run(t, "trivial executable", "./trivial.pie") AssertIsLinkedTo(t, "./trivial.pie", soname) AssertHasRPath(t, "./trivial.pie", gorootInstallDir) - checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "./trivial.pie", 256000) } // Check that the file size does not exceed a limit. @@ -512,6 +528,9 @@ func checkPIE(t *testing.T, name string) { } func TestTrivialPIE(t *testing.T) { + if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-alpine") { + t.Skip("skipping on alpine until issue #54354 resolved") + } name := "trivial_pie" goCmd(t, "build", "-buildmode=pie", "-o="+name, "./trivial") defer os.Remove(name) @@ -573,12 +592,12 @@ func testABIHashNote(t *testing.T, f *elf.File, note *note) { return } for _, sym := range symbols { - if sym.Name == "go.link.abihashbytes" { + if sym.Name == "go:link.abihashbytes" { hashbytes = sym } } if hashbytes.Name == "" { - t.Errorf("no symbol called go.link.abihashbytes") + t.Errorf("no symbol called go:link.abihashbytes") return } if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL { @@ -694,7 +713,15 @@ func requireGccgo(t *testing.T) { if err != nil { t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output) } - if string(output) < "5" { + dot := bytes.Index(output, []byte{'.'}) + if dot > 0 { + output = output[:dot] + } + major, err := strconv.Atoi(string(output)) + if err != nil { + t.Skipf("can't parse gccgo version number %s", output) + } + if major < 5 { t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output))) } @@ -1033,7 +1060,7 @@ func TestGlobal(t *testing.T) { // Run a test using -linkshared of an installed shared package. // Issue 26400. func TestTestInstalledShared(t *testing.T) { - goCmd(nil, "test", "-linkshared", "-test.short", "sync/atomic") + goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic") } // Test generated pointer method with -linkshared. @@ -1045,8 +1072,8 @@ func TestGeneratedMethod(t *testing.T) { // Test use of shared library struct with generated hash function. // Issue 30768. func TestGeneratedHash(t *testing.T) { - goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") - goCmd(nil, "test", "-linkshared", "./issue30768") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") + goCmd(t, "test", "-linkshared", "./issue30768") } // Test that packages can be added not in dependency order (here a depends on b, and a adds @@ -1070,3 +1097,11 @@ func TestIssue44031(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b") goCmd(t, "run", "-linkshared", "./issue44031/main") } + +// Test that we use a variable from shared libraries (which implement an +// interface in shared libraries.). A weak reference is used in the itab +// in main process. It can cause unreacheble panic. See issue 47873. +func TestIssue47873(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a") + goCmd(t, "run", "-linkshared", "./issue47837/main") +} diff --git a/misc/cgo/testshared/testdata/issue47837/a/a.go b/misc/cgo/testshared/testdata/issue47837/a/a.go new file mode 100644 index 00000000000000..68588eda2fa0a9 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue47837/a/a.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A interface { + M() +} + +//go:noinline +func TheFuncWithArgA(a A) { + a.M() +} + +type ImplA struct{} + +//go:noinline +func (A *ImplA) M() {} diff --git a/misc/cgo/testshared/testdata/issue47837/main/main.go b/misc/cgo/testshared/testdata/issue47837/main/main.go new file mode 100644 index 00000000000000..77c6f3437938a4 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue47837/main/main.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. 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 ( + "testshared/issue47837/a" +) + +func main() { + var vara a.ImplA + a.TheFuncWithArgA(&vara) +} diff --git a/misc/cgo/testso/noso_test.go b/misc/cgo/testso/noso_test.go index c88aebfb02a91e..1014534d62cf06 100644 --- a/misc/cgo/testso/noso_test.go +++ b/misc/cgo/testso/noso_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !cgo // +build !cgo package so_test diff --git a/misc/cgo/testso/so_test.go b/misc/cgo/testso/so_test.go index 2023c51f113785..6d14e32dc6caa4 100644 --- a/misc/cgo/testso/so_test.go +++ b/misc/cgo/testso/so_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo // +build cgo package so_test diff --git a/misc/cgo/testsovar/noso_test.go b/misc/cgo/testsovar/noso_test.go index c88aebfb02a91e..1014534d62cf06 100644 --- a/misc/cgo/testsovar/noso_test.go +++ b/misc/cgo/testsovar/noso_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !cgo // +build !cgo package so_test diff --git a/misc/cgo/testsovar/so_test.go b/misc/cgo/testsovar/so_test.go index 2023c51f113785..6d14e32dc6caa4 100644 --- a/misc/cgo/testsovar/so_test.go +++ b/misc/cgo/testsovar/so_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo // +build cgo package so_test diff --git a/misc/cgo/testtls/tls_test.go b/misc/cgo/testtls/tls_test.go index 3076c2d5943b5c..a3b67c004413aa 100644 --- a/misc/cgo/testtls/tls_test.go +++ b/misc/cgo/testtls/tls_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows // +build !windows package cgotlstest diff --git a/misc/go.mod b/misc/go.mod index fc9f1133a4608a..712a051f4573c1 100644 --- a/misc/go.mod +++ b/misc/go.mod @@ -8,4 +8,4 @@ // directory.) module misc -go 1.12 +go 1.18 diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh index dca3fcc90439d6..8f7b439315bff7 100755 --- a/misc/ios/clangwrap.sh +++ b/misc/ios/clangwrap.sh @@ -17,4 +17,4 @@ export IPHONEOS_DEPLOYMENT_TARGET=5.1 # cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang. CLANG=`xcrun --sdk $SDK --find clang` -exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=10.0 "$@" +exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@" diff --git a/misc/ios/detect.go b/misc/ios/detect.go index cde57238923be6..1cb8ae5ff711cc 100644 --- a/misc/ios/detect.go +++ b/misc/ios/detect.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // detect attempts to autodetect the correct diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go index 9e63717d9214d6..c275dd339cc9bd 100644 --- a/misc/ios/go_ios_exec.go +++ b/misc/ios/go_ios_exec.go @@ -13,9 +13,11 @@ // binary. // // This script requires that three environment variables be set: -// GOIOS_DEV_ID: The codesigning developer id or certificate identifier -// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids. -// GOIOS_TEAM_ID: The team id that owns the app id prefix. +// +// GOIOS_DEV_ID: The codesigning developer id or certificate identifier +// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids. +// GOIOS_TEAM_ID: The team id that owns the app id prefix. +// // $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these. package main @@ -148,9 +150,8 @@ func runOnDevice(appdir string) error { // Device IDs as listed with ios-deploy -c. deviceID = os.Getenv("GOIOS_DEVICE_ID") - parts := strings.SplitN(appID, ".", 2) - if len(parts) == 2 { - bundleID = parts[1] + if _, id, ok := strings.Cut(appID, "."); ok { + bundleID = id } if err := signApp(appdir); err != nil { @@ -291,11 +292,10 @@ func findDevImage() (string, error) { var iosVer, buildVer string lines := bytes.Split(out, []byte("\n")) for _, line := range lines { - spl := bytes.SplitN(line, []byte(": "), 2) - if len(spl) != 2 { + key, val, ok := strings.Cut(string(line), ": ") + if !ok { continue } - key, val := string(spl[0]), string(spl[1]) switch key { case "ProductVersion": iosVer = val diff --git a/misc/linkcheck/linkcheck.go b/misc/linkcheck/linkcheck.go index 570b430da4f1cb..efe400965b2e06 100644 --- a/misc/linkcheck/linkcheck.go +++ b/misc/linkcheck/linkcheck.go @@ -81,10 +81,8 @@ func crawl(url string, sourceURL string) { } mu.Lock() defer mu.Unlock() - var frag string - if i := strings.Index(url, "#"); i >= 0 { - frag = url[i+1:] - url = url[:i] + if u, frag, ok := strings.Cut(url, "#"); ok { + url = u if frag != "" { uf := urlFrag{url, frag} neededFrags[uf] = append(neededFrags[uf], sourceURL) diff --git a/misc/reboot/experiment_toolid_test.go b/misc/reboot/experiment_toolid_test.go index 4f40284d80f107..87a828e32f73a4 100644 --- a/misc/reboot/experiment_toolid_test.go +++ b/misc/reboot/experiment_toolid_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build explicit // +build explicit // Package experiment_toolid_test verifies that GOEXPERIMENT settings built diff --git a/misc/reboot/overlaydir_test.go b/misc/reboot/overlaydir_test.go index c446d0891cf0b5..71faf0936ba19a 100644 --- a/misc/reboot/overlaydir_test.go +++ b/misc/reboot/overlaydir_test.go @@ -6,6 +6,7 @@ package reboot_test import ( "io" + "io/fs" "os" "path/filepath" "strings" @@ -26,10 +27,14 @@ func overlayDir(dstRoot, srcRoot string) error { return err } - return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + return filepath.WalkDir(srcRoot, func(srcPath string, entry fs.DirEntry, err error) error { if err != nil || srcPath == srcRoot { return err } + if filepath.Base(srcPath) == "testdata" { + // We're just building, so no need to copy those. + return fs.SkipDir + } suffix := strings.TrimPrefix(srcPath, srcRoot) for len(suffix) > 0 && suffix[0] == filepath.Separator { @@ -37,6 +42,7 @@ func overlayDir(dstRoot, srcRoot string) error { } dstPath := filepath.Join(dstRoot, suffix) + info, err := entry.Info() perm := info.Mode() & os.ModePerm if info.Mode()&os.ModeSymlink != 0 { info, err = os.Stat(srcPath) @@ -46,14 +52,15 @@ func overlayDir(dstRoot, srcRoot string) error { perm = info.Mode() & os.ModePerm } - // Always copy directories (don't symlink them). + // Always make copies of directories. // If we add a file in the overlay, we don't want to add it in the original. if info.IsDir() { return os.MkdirAll(dstPath, perm|0200) } - // If the OS supports symlinks, use them instead of copying bytes. - if err := os.Symlink(srcPath, dstPath); err == nil { + // If we can use a hard link, do that instead of copying bytes. + // Go builds don't like symlinks in some cases, such as go:embed. + if err := os.Link(srcPath, dstPath); err == nil { return nil } diff --git a/misc/reboot/reboot_test.go b/misc/reboot/reboot_test.go index 6bafc608b5e234..a134affbc24455 100644 --- a/misc/reboot/reboot_test.go +++ b/misc/reboot/reboot_test.go @@ -12,19 +12,22 @@ import ( "path/filepath" "runtime" "testing" + "time" ) func TestRepeatBootstrap(t *testing.T) { - goroot, err := os.MkdirTemp("", "reboot-goroot") - if err != nil { - t.Fatal(err) + if testing.Short() { + t.Skipf("skipping test that rebuilds the entire toolchain") } - defer os.RemoveAll(goroot) + + goroot := t.TempDir() gorootSrc := filepath.Join(goroot, "src") + overlayStart := time.Now() if err := overlayDir(gorootSrc, filepath.Join(runtime.GOROOT(), "src")); err != nil { t.Fatal(err) } + t.Logf("GOROOT/src overlay set up in %s", time.Since(overlayStart)) if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil { t.Fatal(err) diff --git a/misc/trace/README.md b/misc/trace/README.md deleted file mode 100644 index 218d7285461df9..00000000000000 --- a/misc/trace/README.md +++ /dev/null @@ -1,105 +0,0 @@ -## Resources for Go's trace viewer - -Go execution trace UI (`go tool trace`) embeds -Chrome's trace viewer (Catapult) following the -[instructions]( -https://chromium.googlesource.com/catapult/+/refs/heads/master/tracing/docs/embedding-trace-viewer.md). This directory contains -the helper files to embed Chrome's trace viewer. - -The current resources were generated/copied from -[`Catapult@9508452e18f130c98499cb4c4f1e1efaedee8962`]( -https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962). - -### Updating `trace_viewer_full.html` - -The file was generated by catapult's `vulcanize_trace_viewer` command. -``` -$ git clone https://chromium.googlesource.com/catapult -$ cd catapult -$ ./tracing/bin/vulcanize_trace_viewer --config=full -$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_full.html -``` - -We are supposed to use --config=lean (produces smaller html), -but it is broken at the moment: -https://github.com/catapult-project/catapult/issues/2247 - -### Updating `webcomponents.min.js` - -`webcomponents.min.js` is necessary to let the trace viewer page -to import the `trace_viewer_full.html`. -This is copied from the catapult repo. - -``` -$ cp third_party/polymer/components/webcomponentsjs/webcomponents.min.js $GOROOT/misc/trace/webcomponents.min.js -``` - -## Licenses - -The license for trace-viewer is as follows: -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The license for webcomponents.min.js is as follows: - -/** - * @license - * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ -// Copyright (c) 2014 The Polymer Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/misc/wasm/go_js_wasm_exec b/misc/wasm/go_js_wasm_exec index b700722dfe97e5..fcbd0e4fc8ce0e 100755 --- a/misc/wasm/go_js_wasm_exec +++ b/misc/wasm/go_js_wasm_exec @@ -11,4 +11,4 @@ while [ -h "$SOURCE" ]; do done DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" -exec node "$DIR/wasm_exec.js" "$@" +exec node "$DIR/wasm_exec_node.js" "$@" diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index 231185a123ed12..9ce6a20c3ffa82 100644 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -2,47 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -(() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - // - Webpack - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error("cannot export Go (neither global, window nor self is defined)"); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - const fs = require("fs"); - if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { - global.fs = fs; - } - } +"use strict"; +(() => { const enosys = () => { const err = new Error("not implemented"); err.code = "ENOSYS"; return err; }; - if (!global.fs) { + if (!globalThis.fs) { let outputBuf = ""; - global.fs = { + globalThis.fs = { constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused writeSync(fd, buf) { outputBuf += decoder.decode(buf); @@ -87,8 +58,8 @@ }; } - if (!global.process) { - global.process = { + if (!globalThis.process) { + globalThis.process = { getuid() { return -1; }, getgid() { return -1; }, geteuid() { return -1; }, @@ -102,47 +73,26 @@ } } - if (!global.crypto && global.require) { - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - } - if (!global.crypto) { - throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); } - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); } - if (!global.TextEncoder && global.require) { - global.TextEncoder = require("util").TextEncoder; - } - if (!global.TextEncoder) { - throw new Error("global.TextEncoder is not available, polyfill required"); + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); } - if (!global.TextDecoder && global.require) { - global.TextDecoder = require("util").TextDecoder; - } - if (!global.TextDecoder) { - throw new Error("global.TextDecoder is not available, polyfill required"); + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); } - // End of polyfills for common API. - const encoder = new TextEncoder("utf-8"); const decoder = new TextDecoder("utf-8"); - global.Go = class { + globalThis.Go = class { constructor() { this.argv = ["js"]; this.env = {}; @@ -517,7 +467,7 @@ null, true, false, - global, + globalThis, this, ]; this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id @@ -526,7 +476,7 @@ [null, 2], [true, 3], [false, 4], - [global, 5], + [globalThis, 5], [this, 6], ]); this._idPool = []; // unused ids that have been garbage collected @@ -567,6 +517,13 @@ offset += 8; }); + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + this._inst.exports.run(argc, argv); if (this.exited) { this._resolveExitPromise(); @@ -594,36 +551,4 @@ }; } } - - if ( - typeof module !== "undefined" && - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length < 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - go.argv = process.argv.slice(2); - go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); - go.exit = process.exit; - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - process.on("exit", (code) => { // Node.js exits if no event handler is pending - if (code === 0 && !go.exited) { - // deadlock, make Go print error and stack traces - go._pendingEvent = { id: 0 }; - go._resume(); - } - }); - return go.run(result.instance); - }).catch((err) => { - console.error(err); - process.exit(1); - }); - } })(); diff --git a/misc/wasm/wasm_exec_node.js b/misc/wasm/wasm_exec_node.js new file mode 100644 index 00000000000000..f9200ca95021e9 --- /dev/null +++ b/misc/wasm/wasm_exec_node.js @@ -0,0 +1,49 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +"use strict"; + +if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); +} + +globalThis.require = require; +globalThis.fs = require("fs"); +globalThis.TextEncoder = require("util").TextEncoder; +globalThis.TextDecoder = require("util").TextDecoder; + +globalThis.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, +}; + +const crypto = require("crypto"); +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + +require("./wasm_exec"); + +const go = new Go(); +go.argv = process.argv.slice(2); +go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); +go.exit = process.exit; +WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); +}).catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/src/all.bat b/src/all.bat index ae835d992f318d..dfc83c8b26c197 100644 --- a/src/all.bat +++ b/src/all.bat @@ -13,9 +13,9 @@ goto end :ok set OLDPATH=%PATH% -call make.bat --no-banner --no-local +call .\make.bat --no-banner --no-local if %GOBUILDFAIL%==1 goto end -call run.bat --no-rebuild --no-local +call .\run.bat --no-rebuild --no-local if %GOBUILDFAIL%==1 goto end :: we must restore %PATH% before running "dist banner" so that the latter :: can get the original %PATH% and give suggestion to add %GOROOT%/bin diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go index c667cfc8720b5b..f6d701d925cba2 100644 --- a/src/archive/tar/common.go +++ b/src/archive/tar/common.go @@ -221,9 +221,11 @@ func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } // that the file has no data in it, which is rather odd. // // As an example, if the underlying raw file contains the 10-byte data: +// // var compactFile = "abcdefgh" // // And the sparse map has the following entries: +// // var spd sparseDatas = []sparseEntry{ // {Offset: 2, Length: 5}, // Data fragment for 2..6 // {Offset: 18, Length: 3}, // Data fragment for 18..20 @@ -235,6 +237,7 @@ func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } // } // // Then the content of the resulting sparse file with a Header.Size of 25 is: +// // var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 type ( sparseDatas []sparseEntry @@ -293,9 +296,9 @@ func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry { // The input must have been already validated. // // This function mutates src and returns a normalized map where: -// * adjacent fragments are coalesced together -// * only the last fragment may be empty -// * the endOffset of the last fragment is the total size +// - adjacent fragments are coalesced together +// - only the last fragment may be empty +// - the endOffset of the last fragment is the total size func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { dst := src[:0] var pre sparseEntry @@ -316,10 +319,10 @@ func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { // fileState tracks the number of logical (includes sparse holes) and physical // (actual in tar archive) bytes remaining for the current file. // -// Invariant: LogicalRemaining >= PhysicalRemaining +// Invariant: logicalRemaining >= physicalRemaining type fileState interface { - LogicalRemaining() int64 - PhysicalRemaining() int64 + logicalRemaining() int64 + physicalRemaining() int64 } // allowedFormats determines which formats can be used. @@ -413,22 +416,22 @@ func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err // Check basic fields. var blk block - v7 := blk.V7() - ustar := blk.USTAR() - gnu := blk.GNU() - verifyString(h.Name, len(v7.Name()), "Name", paxPath) - verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) - verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) - verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) - verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) - verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) - verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) - verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) - verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) - verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) - verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) - verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) - verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) + v7 := blk.toV7() + ustar := blk.toUSTAR() + gnu := blk.toGNU() + verifyString(h.Name, len(v7.name()), "Name", paxPath) + verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath) + verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname) + verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname) + verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone) + verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid) + verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid) + verifyNumeric(h.Size, len(v7.size()), "Size", paxSize) + verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone) + verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone) + verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime) + verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime) + verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime) // Check for header-only types. var whyOnlyPAX, whyOnlyGNU string @@ -538,7 +541,7 @@ type headerFileInfo struct { func (fi headerFileInfo) Size() int64 { return fi.h.Size } func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } -func (fi headerFileInfo) Sys() interface{} { return fi.h } +func (fi headerFileInfo) Sys() any { return fi.h } // Name returns the base name of the file. func (fi headerFileInfo) Name() string { diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go index cfe24a5e1d3395..21b9d9d4dbc628 100644 --- a/src/archive/tar/format.go +++ b/src/archive/tar/format.go @@ -156,28 +156,28 @@ 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[:]) } +func (b *block) toV7() *headerV7 { return (*headerV7)(b) } +func (b *block) toGNU() *headerGNU { return (*headerGNU)(b) } +func (b *block) toSTAR() *headerSTAR { return (*headerSTAR)(b) } +func (b *block) toUSTAR() *headerUSTAR { return (*headerUSTAR)(b) } +func (b *block) toSparse() 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 { +func (b *block) getFormat() Format { // Verify checksum. var p parser - value := p.parseOctal(b.V7().Chksum()) - chksum1, chksum2 := b.ComputeChecksum() + value := p.parseOctal(b.toV7().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()) + magic := string(b.toUSTAR().magic()) + version := string(b.toUSTAR().version()) + trailer := string(b.toSTAR().trailer()) switch { case magic == magicUSTAR && trailer == trailerSTAR: return formatSTAR @@ -190,23 +190,23 @@ func (b *block) GetFormat() Format { } } -// SetFormat writes the magic values necessary for specified format +// setFormat writes the magic values necessary for specified format // and then updates the checksum accordingly. -func (b *block) SetFormat(format Format) { +func (b *block) setFormat(format Format) { // Set the magic values. switch { case format.has(formatV7): // Do nothing. case format.has(FormatGNU): - copy(b.GNU().Magic(), magicGNU) - copy(b.GNU().Version(), versionGNU) + copy(b.toGNU().magic(), magicGNU) + copy(b.toGNU().version(), versionGNU) case format.has(formatSTAR): - copy(b.STAR().Magic(), magicUSTAR) - copy(b.STAR().Version(), versionUSTAR) - copy(b.STAR().Trailer(), trailerSTAR) + copy(b.toSTAR().magic(), magicUSTAR) + copy(b.toSTAR().version(), versionUSTAR) + copy(b.toSTAR().trailer(), trailerSTAR) case format.has(FormatUSTAR | FormatPAX): - copy(b.USTAR().Magic(), magicUSTAR) - copy(b.USTAR().Version(), versionUSTAR) + copy(b.toUSTAR().magic(), magicUSTAR) + copy(b.toUSTAR().version(), versionUSTAR) default: panic("invalid format") } @@ -214,17 +214,17 @@ func (b *block) SetFormat(format 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 + field := b.toV7().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. +// 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) { +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. @@ -236,68 +236,68 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) { } // Reset clears the block with all zeros. -func (b *block) Reset() { +func (b *block) reset() { *b = block{} } 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] } +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] } +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] } +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] } +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) sparseElem { return sparseElem(s[i*24:]) } -func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } -func (s sparseArray) MaxEntries() int { return len(s) / 24 } +func (s sparseArray) entry(i int) sparseElem { return sparseElem(s[i*24:]) } +func (s sparseArray) isExtended() []byte { return s[24*s.maxEntries():][:1] } +func (s sparseArray) maxEntries() int { return len(s) / 24 } type sparseElem []byte -func (s sparseElem) Offset() []byte { return s[00:][:12] } -func (s sparseElem) Length() []byte { return s[12:][:12] } +func (s sparseElem) offset() []byte { return s[00:][:12] } +func (s sparseElem) length() []byte { return s[12:][:12] } diff --git a/src/archive/tar/fuzz_test.go b/src/archive/tar/fuzz_test.go new file mode 100644 index 00000000000000..e73e0d2609e50a --- /dev/null +++ b/src/archive/tar/fuzz_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "io" + "testing" +) + +func FuzzReader(f *testing.F) { + b := bytes.NewBuffer(nil) + w := NewWriter(b) + inp := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") + err := w.WriteHeader(&Header{ + Name: "lorem.txt", + Mode: 0600, + Size: int64(len(inp)), + }) + if err != nil { + f.Fatalf("failed to create writer: %s", err) + } + _, err = w.Write(inp) + if err != nil { + f.Fatalf("failed to write file to archive: %s", err) + } + if err := w.Close(); err != nil { + f.Fatalf("failed to write archive: %s", err) + } + f.Add(b.Bytes()) + + f.Fuzz(func(t *testing.T, b []byte) { + r := NewReader(bytes.NewReader(b)) + type file struct { + header *Header + content []byte + } + files := []file{} + for { + hdr, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return + } + buf := bytes.NewBuffer(nil) + if _, err := io.Copy(buf, r); err != nil { + continue + } + files = append(files, file{header: hdr, content: buf.Bytes()}) + } + + // If we were unable to read anything out of the archive don't + // bother trying to roundtrip it. + if len(files) == 0 { + return + } + + out := bytes.NewBuffer(nil) + w := NewWriter(out) + for _, f := range files { + if err := w.WriteHeader(f.header); err != nil { + t.Fatalf("unable to write previously parsed header: %s", err) + } + if _, err := w.Write(f.content); err != nil { + t.Fatalf("unable to write previously parsed content: %s", err) + } + } + if err := w.Close(); err != nil { + t.Fatalf("Unable to write archive: %s", err) + } + + // TODO: We may want to check if the archive roundtrips. This would require + // taking into account addition of the two zero trailer blocks that Writer.Close + // appends. + }) +} diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index 1b1d5b46891b6b..f1b35c34f6f25a 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -65,7 +65,7 @@ func (tr *Reader) next() (*Header, error) { format := FormatUSTAR | FormatPAX | FormatGNU for { // Discard the remainder of the file and any padding. - if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil { + if err := discard(tr.r, tr.curr.physicalRemaining()); err != nil { return nil, err } if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil { @@ -336,9 +336,9 @@ func parsePAX(r io.Reader) (map[string]string, error) { // header in case further processing is required. // // The err will be set to io.EOF only when one of the following occurs: -// * Exactly 0 bytes are read and EOF is hit. -// * Exactly 1 block of zeros is read and EOF is hit. -// * At least 2 blocks of zeros are read. +// - Exactly 0 bytes are read and EOF is hit. +// - Exactly 1 block of zeros is read and EOF is hit. +// - At least 2 blocks of zeros are read. func (tr *Reader) readHeader() (*Header, *block, error) { // Two blocks of zero bytes marks the end of the archive. if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil { @@ -355,7 +355,7 @@ func (tr *Reader) readHeader() (*Header, *block, error) { } // Verify the header matches a known format. - format := tr.blk.GetFormat() + format := tr.blk.getFormat() if format == FormatUnknown { return nil, nil, ErrHeader } @@ -364,30 +364,30 @@ func (tr *Reader) readHeader() (*Header, *block, error) { hdr := new(Header) // Unpack the V7 header. - v7 := tr.blk.V7() - hdr.Typeflag = v7.TypeFlag()[0] - hdr.Name = p.parseString(v7.Name()) - hdr.Linkname = p.parseString(v7.LinkName()) - hdr.Size = p.parseNumeric(v7.Size()) - hdr.Mode = p.parseNumeric(v7.Mode()) - hdr.Uid = int(p.parseNumeric(v7.UID())) - hdr.Gid = int(p.parseNumeric(v7.GID())) - hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) + v7 := tr.blk.toV7() + hdr.Typeflag = v7.typeFlag()[0] + hdr.Name = p.parseString(v7.name()) + hdr.Linkname = p.parseString(v7.linkName()) + hdr.Size = p.parseNumeric(v7.size()) + hdr.Mode = p.parseNumeric(v7.mode()) + hdr.Uid = int(p.parseNumeric(v7.uid())) + hdr.Gid = int(p.parseNumeric(v7.gid())) + hdr.ModTime = time.Unix(p.parseNumeric(v7.modTime()), 0) // Unpack format specific fields. if format > formatV7 { - ustar := tr.blk.USTAR() - hdr.Uname = p.parseString(ustar.UserName()) - hdr.Gname = p.parseString(ustar.GroupName()) - hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) - hdr.Devminor = p.parseNumeric(ustar.DevMinor()) + ustar := tr.blk.toUSTAR() + hdr.Uname = p.parseString(ustar.userName()) + hdr.Gname = p.parseString(ustar.groupName()) + hdr.Devmajor = p.parseNumeric(ustar.devMajor()) + hdr.Devminor = p.parseNumeric(ustar.devMinor()) var prefix string switch { case format.has(FormatUSTAR | FormatPAX): hdr.Format = format - ustar := tr.blk.USTAR() - prefix = p.parseString(ustar.Prefix()) + ustar := tr.blk.toUSTAR() + prefix = p.parseString(ustar.prefix()) // For Format detection, check if block is properly formatted since // the parser is more liberal than what USTAR actually permits. @@ -396,23 +396,23 @@ func (tr *Reader) readHeader() (*Header, *block, error) { hdr.Format = FormatUnknown // Non-ASCII characters in block. } nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } - if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && - nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { + if !(nul(v7.size()) && nul(v7.mode()) && nul(v7.uid()) && nul(v7.gid()) && + nul(v7.modTime()) && nul(ustar.devMajor()) && nul(ustar.devMinor())) { hdr.Format = FormatUnknown // Numeric fields must end in NUL } case format.has(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) + star := tr.blk.toSTAR() + prefix = p.parseString(star.prefix()) + hdr.AccessTime = time.Unix(p.parseNumeric(star.accessTime()), 0) + hdr.ChangeTime = time.Unix(p.parseNumeric(star.changeTime()), 0) case format.has(FormatGNU): hdr.Format = format var p2 parser - gnu := tr.blk.GNU() - if b := gnu.AccessTime(); b[0] != 0 { + gnu := tr.blk.toGNU() + if b := gnu.accessTime(); b[0] != 0 { hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) } - if b := gnu.ChangeTime(); b[0] != 0 { + if b := gnu.changeTime(); b[0] != 0 { hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) } @@ -439,8 +439,8 @@ func (tr *Reader) readHeader() (*Header, *block, error) { // See https://golang.org/issues/21005 if p2.err != nil { hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{} - ustar := tr.blk.USTAR() - if s := p.parseString(ustar.Prefix()); isASCII(s) { + ustar := tr.blk.toUSTAR() + if s := p.parseString(ustar.prefix()); isASCII(s) { prefix = s } hdr.Format = FormatUnknown // Buggy file is not GNU @@ -465,38 +465,38 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err // Make sure that the input format is GNU. // Unfortunately, the STAR format also has a sparse header format that uses // the same type flag but has a completely different layout. - if blk.GetFormat() != FormatGNU { + if blk.getFormat() != FormatGNU { return nil, ErrHeader } hdr.Format.mayOnlyBe(FormatGNU) var p parser - hdr.Size = p.parseNumeric(blk.GNU().RealSize()) + hdr.Size = p.parseNumeric(blk.toGNU().realSize()) if p.err != nil { return nil, p.err } - s := blk.GNU().Sparse() - spd := make(sparseDatas, 0, s.MaxEntries()) + s := blk.toGNU().sparse() + spd := make(sparseDatas, 0, s.maxEntries()) for { - for i := 0; i < s.MaxEntries(); i++ { + for i := 0; i < s.maxEntries(); i++ { // This termination condition is identical to GNU and BSD tar. - if s.Entry(i).Offset()[0] == 0x00 { + if s.entry(i).offset()[0] == 0x00 { break // Don't return, need to process extended headers (even if empty) } - offset := p.parseNumeric(s.Entry(i).Offset()) - length := p.parseNumeric(s.Entry(i).Length()) + offset := p.parseNumeric(s.entry(i).offset()) + length := p.parseNumeric(s.entry(i).length()) if p.err != nil { return nil, p.err } spd = append(spd, sparseEntry{Offset: offset, Length: length}) } - if s.IsExtended()[0] > 0 { + if s.isExtended()[0] > 0 { // There are more entries. Read an extension header and parse its entries. if _, err := mustReadFull(tr.r, blk[:]); err != nil { return nil, err } - s = blk.Sparse() + s = blk.toSparse() continue } return spd, nil // Done @@ -678,11 +678,13 @@ func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) { return io.Copy(w, struct{ io.Reader }{fr}) } -func (fr regFileReader) LogicalRemaining() int64 { +// logicalRemaining implements fileState.logicalRemaining. +func (fr regFileReader) logicalRemaining() int64 { return fr.nb } -func (fr regFileReader) PhysicalRemaining() int64 { +// logicalRemaining implements fileState.physicalRemaining. +func (fr regFileReader) physicalRemaining() int64 { return fr.nb } @@ -694,9 +696,9 @@ type sparseFileReader struct { } func (sr *sparseFileReader) Read(b []byte) (n int, err error) { - finished := int64(len(b)) >= sr.LogicalRemaining() + finished := int64(len(b)) >= sr.logicalRemaining() if finished { - b = b[:sr.LogicalRemaining()] + b = b[:sr.logicalRemaining()] } b0 := b @@ -724,7 +726,7 @@ func (sr *sparseFileReader) Read(b []byte) (n int, err error) { return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err - case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file case finished: return n, io.EOF @@ -746,7 +748,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { var writeLastByte bool pos0 := sr.pos - for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { + for sr.logicalRemaining() > 0 && !writeLastByte && err == nil { var nf int64 // Size of fragment holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() if sr.pos < holeStart { // In a data fragment @@ -754,7 +756,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { nf, err = io.CopyN(ws, sr.fr, nf) } else { // In a hole fragment nf = holeEnd - sr.pos - if sr.PhysicalRemaining() == 0 { + if sr.physicalRemaining() == 0 { writeLastByte = true nf-- } @@ -779,18 +781,18 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err - case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file default: return n, nil } } -func (sr sparseFileReader) LogicalRemaining() int64 { +func (sr sparseFileReader) logicalRemaining() int64 { return sr.sp[len(sr.sp)-1].endOffset() - sr.pos } -func (sr sparseFileReader) PhysicalRemaining() int64 { - return sr.fr.PhysicalRemaining() +func (sr sparseFileReader) physicalRemaining() int64 { + return sr.fr.physicalRemaining() } type zeroReader struct{} diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index 789ddc1bc0345a..a8e9b929d74750 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -657,7 +657,6 @@ func TestReader(t *testing.T) { for i, hdr := range hdrs { if i >= len(v.headers) { t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr) - continue } if !reflect.DeepEqual(*hdr, *v.headers[i]) { t.Fatalf("entry %d: incorrect header:\ngot %+v\nwant %+v", i, *hdr, *v.headers[i]) @@ -670,7 +669,6 @@ func TestReader(t *testing.T) { for i, sum := range chksums { if i >= len(v.chksums) { t.Fatalf("entry %d: unexpected sum: got %s", i, sum) - continue } if sum != v.chksums[i] { t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i]) @@ -1021,12 +1019,12 @@ func TestParsePAX(t *testing.T) { func TestReadOldGNUSparseMap(t *testing.T) { populateSparseMap := func(sa sparseArray, sps []string) []string { - for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ { - copy(sa.Entry(i), sps[0]) + for i := 0; len(sps) > 0 && i < sa.maxEntries(); i++ { + copy(sa.entry(i), sps[0]) sps = sps[1:] } if len(sps) > 0 { - copy(sa.IsExtended(), "\x80") + copy(sa.isExtended(), "\x80") } return sps } @@ -1034,19 +1032,19 @@ func TestReadOldGNUSparseMap(t *testing.T) { makeInput := func(format Format, size string, sps ...string) (out []byte) { // Write the initial GNU header. var blk block - gnu := blk.GNU() - sparse := gnu.Sparse() - copy(gnu.RealSize(), size) + gnu := blk.toGNU() + sparse := gnu.sparse() + copy(gnu.realSize(), size) sps = populateSparseMap(sparse, sps) if format != FormatUnknown { - blk.SetFormat(format) + blk.setFormat(format) } out = append(out, blk[:]...) // Write extended sparse blocks. for len(sps) > 0 { var blk block - sps = populateSparseMap(blk.Sparse(), sps) + sps = populateSparseMap(blk.toSparse(), sps) out = append(out, blk[:]...) } return out @@ -1359,11 +1357,11 @@ func TestFileReader(t *testing.T) { wantCnt int64 wantErr error } - testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt wantLCnt int64 wantPCnt int64 } - testFnc interface{} // testRead | testWriteTo | testRemaining + testFnc any // testRead | testWriteTo | testRemaining ) type ( @@ -1376,7 +1374,7 @@ func TestFileReader(t *testing.T) { spd sparseDatas size int64 } - fileMaker interface{} // makeReg | makeSparse + fileMaker any // makeReg | makeSparse ) vectors := []struct { @@ -1596,11 +1594,11 @@ func TestFileReader(t *testing.T) { t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) } case testRemaining: - if got := fr.LogicalRemaining(); got != tf.wantLCnt { - t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + if got := fr.logicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) } - if got := fr.PhysicalRemaining(); got != tf.wantPCnt { - t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + if got := fr.physicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) } default: t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) diff --git a/src/archive/tar/stat_actime1.go b/src/archive/tar/stat_actime1.go index 4fdf2a04b3df40..c4c2480feeb0c4 100644 --- a/src/archive/tar/stat_actime1.go +++ b/src/archive/tar/stat_actime1.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || dragonfly || openbsd || solaris -// +build aix linux dragonfly openbsd solaris package tar diff --git a/src/archive/tar/stat_actime2.go b/src/archive/tar/stat_actime2.go index 5a9a35cbb4e22d..f76d6be220f87f 100644 --- a/src/archive/tar/stat_actime2.go +++ b/src/archive/tar/stat_actime2.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || netbsd -// +build darwin freebsd netbsd package tar diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go index 3957349d6ef0aa..0f3428bc24b47d 100644 --- a/src/archive/tar/stat_unix.go +++ b/src/archive/tar/stat_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris -// +build aix linux darwin dragonfly freebsd openbsd netbsd solaris +//go:build unix package tar diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go index f0b61e6dba69a4..ac3196370e6113 100644 --- a/src/archive/tar/strconv.go +++ b/src/archive/tar/strconv.go @@ -14,7 +14,7 @@ import ( // hasNUL reports whether the NUL character exists within s. func hasNUL(s string) bool { - return strings.IndexByte(s, 0) >= 0 + return strings.Contains(s, "\x00") } // isASCII reports whether the input is an ASCII C-style string. @@ -201,10 +201,7 @@ func parsePAXTime(s string) (time.Time, error) { const maxNanoSecondDigits = 9 // Split string into seconds and sub-seconds parts. - ss, sn := s, "" - if pos := strings.IndexByte(s, '.'); pos >= 0 { - ss, sn = s[:pos], s[pos+1:] - } + ss, sn, _ := strings.Cut(s, ".") // Parse the seconds. secs, err := strconv.ParseInt(ss, 10, 64) @@ -254,48 +251,32 @@ func formatPAXTime(ts time.Time) (s string) { // return the remainder as r. func parsePAXRecord(s string) (k, v, r string, err error) { // The size field ends at the first space. - sp := strings.IndexByte(s, ' ') - if sp == -1 { + nStr, rest, ok := strings.Cut(s, " ") + if !ok { return "", "", s, ErrHeader } // Parse the first token as a decimal integer. - n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int - if perr != nil || n < 5 || int64(len(s)) < n { + n, perr := strconv.ParseInt(nStr, 10, 0) // Intentionally parse as native int + if perr != nil || n < 5 || n > int64(len(s)) { return "", "", s, ErrHeader } - - afterSpace := int64(sp + 1) - beforeLastNewLine := n - 1 - // In some cases, "length" was perhaps padded/malformed, and - // trying to index past where the space supposedly is goes past - // the end of the actual record. - // For example: - // "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319" - // ^ ^ - // | | - // | afterSpace=35 - // | - // beforeLastNewLine=29 - // yet indexOf(firstSpace) MUST BE before endOfRecord. - // - // See https://golang.org/issues/40196. - if afterSpace >= beforeLastNewLine { + n -= int64(len(nStr) + 1) // convert from index in s to index in rest + if n <= 0 { return "", "", s, ErrHeader } // Extract everything between the space and the final newline. - rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:] + rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:] if nl != "\n" { return "", "", s, ErrHeader } // The first equals separates the key from the value. - eq := strings.IndexByte(rec, '=') - if eq == -1 { + k, v, ok = strings.Cut(rec, "=") + if !ok { return "", "", s, ErrHeader } - k, v = rec[:eq], rec[eq+1:] if !validPAXRecord(k, v) { return "", "", s, ErrHeader @@ -325,6 +306,7 @@ func formatPAXRecord(k, v string) (string, error) { // validPAXRecord reports whether the key-value pair is valid where each // record is formatted as: +// // "%d %s=%s\n" % (size, key, value) // // Keys and values should be UTF-8, but the number of bad writers out there @@ -333,7 +315,7 @@ func formatPAXRecord(k, v string) (string, error) { // for the PAX version of the USTAR string fields. // The key must not contain an '=' character. func validPAXRecord(k, v string) bool { - if k == "" || strings.IndexByte(k, '=') >= 0 { + if k == "" || strings.Contains(k, "=") { return false } switch k { diff --git a/src/archive/tar/tar_test.go b/src/archive/tar/tar_test.go index e9fafc7cc70df5..a476f5eb010f21 100644 --- a/src/archive/tar/tar_test.go +++ b/src/archive/tar/tar_test.go @@ -23,7 +23,7 @@ import ( type testError struct{ error } -type fileOps []interface{} // []T where T is (string | int64) +type fileOps []any // []T where T is (string | int64) // testFile is an io.ReadWriteSeeker where the IO operations performed // on it must match the list of operations in ops. diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index e80498d03e39c0..3729f7e82c192f 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -50,7 +50,7 @@ func (tw *Writer) Flush() error { if tw.err != nil { return tw.err } - if nb := tw.curr.LogicalRemaining(); nb > 0 { + if nb := tw.curr.logicalRemaining(); nb > 0 { return fmt.Errorf("archive/tar: missed writing %d bytes", nb) } if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { @@ -117,8 +117,8 @@ func (tw *Writer) writeUSTARHeader(hdr *Header) error { // Pack the main header. var f formatter blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) - f.formatString(blk.USTAR().Prefix(), namePrefix) - blk.SetFormat(FormatUSTAR) + f.formatString(blk.toUSTAR().prefix(), namePrefix) + blk.setFormat(FormatUSTAR) if f.err != nil { return f.err // Should never happen since header is validated } @@ -208,7 +208,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { var f formatter // Ignore errors since they are expected fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) - blk.SetFormat(FormatPAX) + blk.setFormat(FormatPAX) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } @@ -250,10 +250,10 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error { var spb []byte blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) if !hdr.AccessTime.IsZero() { - f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) + f.formatNumeric(blk.toGNU().accessTime(), hdr.AccessTime.Unix()) } if !hdr.ChangeTime.IsZero() { - f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) + f.formatNumeric(blk.toGNU().changeTime(), hdr.ChangeTime.Unix()) } // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 @@ -293,7 +293,7 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error { f.formatNumeric(blk.GNU().RealSize(), realSize) } */ - blk.SetFormat(FormatGNU) + blk.setFormat(FormatGNU) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } @@ -321,28 +321,28 @@ type ( // The block returned is only valid until the next call to // templateV7Plus or writeRawFile. func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { - tw.blk.Reset() + tw.blk.reset() modTime := hdr.ModTime if modTime.IsZero() { modTime = time.Unix(0, 0) } - v7 := tw.blk.V7() - v7.TypeFlag()[0] = hdr.Typeflag - fmtStr(v7.Name(), hdr.Name) - fmtStr(v7.LinkName(), hdr.Linkname) - fmtNum(v7.Mode(), hdr.Mode) - fmtNum(v7.UID(), int64(hdr.Uid)) - fmtNum(v7.GID(), int64(hdr.Gid)) - fmtNum(v7.Size(), hdr.Size) - fmtNum(v7.ModTime(), modTime.Unix()) + v7 := tw.blk.toV7() + v7.typeFlag()[0] = hdr.Typeflag + fmtStr(v7.name(), hdr.Name) + fmtStr(v7.linkName(), hdr.Linkname) + fmtNum(v7.mode(), hdr.Mode) + fmtNum(v7.uid(), int64(hdr.Uid)) + fmtNum(v7.gid(), int64(hdr.Gid)) + fmtNum(v7.size(), hdr.Size) + fmtNum(v7.modTime(), modTime.Unix()) - ustar := tw.blk.USTAR() - fmtStr(ustar.UserName(), hdr.Uname) - fmtStr(ustar.GroupName(), hdr.Gname) - fmtNum(ustar.DevMajor(), hdr.Devmajor) - fmtNum(ustar.DevMinor(), hdr.Devminor) + ustar := tw.blk.toUSTAR() + fmtStr(ustar.userName(), hdr.Uname) + fmtStr(ustar.groupName(), hdr.Gname) + fmtNum(ustar.devMajor(), hdr.Devmajor) + fmtNum(ustar.devMinor(), hdr.Devminor) return &tw.blk } @@ -351,7 +351,7 @@ func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum num // It uses format to encode the header format and will write data as the body. // It uses default values for all of the other fields (as BSD and GNU tar does). func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { - tw.blk.Reset() + tw.blk.reset() // Best effort for the filename. name = toASCII(name) @@ -361,15 +361,15 @@ func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) erro name = strings.TrimRight(name, "/") var f formatter - v7 := tw.blk.V7() - v7.TypeFlag()[0] = flag - f.formatString(v7.Name(), name) - f.formatOctal(v7.Mode(), 0) - f.formatOctal(v7.UID(), 0) - f.formatOctal(v7.GID(), 0) - f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB - f.formatOctal(v7.ModTime(), 0) - tw.blk.SetFormat(format) + v7 := tw.blk.toV7() + v7.typeFlag()[0] = flag + f.formatString(v7.name(), name) + f.formatOctal(v7.mode(), 0) + f.formatOctal(v7.uid(), 0) + f.formatOctal(v7.gid(), 0) + f.formatOctal(v7.size(), int64(len(data))) // Must be < 8GiB + f.formatOctal(v7.modTime(), 0) + tw.blk.setFormat(format) if f.err != nil { return f.err // Only occurs if size condition is violated } @@ -511,10 +511,13 @@ func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) { return io.Copy(struct{ io.Writer }{fw}, r) } -func (fw regFileWriter) LogicalRemaining() int64 { +// logicalRemaining implements fileState.logicalRemaining. +func (fw regFileWriter) logicalRemaining() int64 { return fw.nb } -func (fw regFileWriter) PhysicalRemaining() int64 { + +// logicalRemaining implements fileState.physicalRemaining. +func (fw regFileWriter) physicalRemaining() int64 { return fw.nb } @@ -526,9 +529,9 @@ type sparseFileWriter struct { } func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { - overwrite := int64(len(b)) > sw.LogicalRemaining() + overwrite := int64(len(b)) > sw.logicalRemaining() if overwrite { - b = b[:sw.LogicalRemaining()] + b = b[:sw.logicalRemaining()] } b0 := b @@ -556,7 +559,7 @@ func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err - case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic case overwrite: return n, ErrWriteTooLong @@ -578,12 +581,12 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { var readLastByte bool pos0 := sw.pos - for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { + for sw.logicalRemaining() > 0 && !readLastByte && err == nil { var nf int64 // Size of fragment dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() if sw.pos < dataStart { // In a hole fragment nf = dataStart - sw.pos - if sw.PhysicalRemaining() == 0 { + if sw.physicalRemaining() == 0 { readLastByte = true nf-- } @@ -613,18 +616,18 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err - case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic default: return n, ensureEOF(rs) } } -func (sw sparseFileWriter) LogicalRemaining() int64 { +func (sw sparseFileWriter) logicalRemaining() int64 { return sw.sp[len(sw.sp)-1].endOffset() - sw.pos } -func (sw sparseFileWriter) PhysicalRemaining() int64 { - return sw.fw.PhysicalRemaining() +func (sw sparseFileWriter) physicalRemaining() int64 { + return sw.fw.physicalRemaining() } // zeroWriter may only be written with NULs, otherwise it returns errWriteHole. diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index a00f02d8fab698..48f35e5133cf78 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -67,7 +67,7 @@ func TestWriter(t *testing.T) { testClose struct { // Close() == wantErr wantErr error } - testFnc interface{} // testHeader | testWrite | testReadFrom | testClose + testFnc any // testHeader | testWrite | testReadFrom | testClose ) vectors := []struct { @@ -987,11 +987,9 @@ func TestIssue12594(t *testing.T) { // The prefix field should never appear in the GNU format. var blk block copy(blk[:], b.Bytes()) - prefix := string(blk.USTAR().Prefix()) - if i := strings.IndexByte(prefix, 0); i >= 0 { - prefix = prefix[:i] // Truncate at the NUL terminator - } - if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { + prefix := string(blk.toUSTAR().prefix()) + prefix, _, _ = strings.Cut(prefix, "\x00") // Truncate at the NUL terminator + if blk.getFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) } @@ -1029,11 +1027,11 @@ func TestFileWriter(t *testing.T) { wantCnt int64 wantErr error } - testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt wantLCnt int64 wantPCnt int64 } - testFnc interface{} // testWrite | testReadFrom | testRemaining + testFnc any // testWrite | testReadFrom | testRemaining ) type ( @@ -1046,7 +1044,7 @@ func TestFileWriter(t *testing.T) { sph sparseHoles size int64 } - fileMaker interface{} // makeReg | makeSparse + fileMaker any // makeReg | makeSparse ) vectors := []struct { @@ -1254,7 +1252,7 @@ func TestFileWriter(t *testing.T) { for i, v := range vectors { var wantStr string - bb := new(bytes.Buffer) + bb := new(strings.Builder) w := testNonEmptyWriter{bb} var fw fileWriter switch maker := v.maker.(type) { @@ -1292,11 +1290,11 @@ func TestFileWriter(t *testing.T) { t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) } case testRemaining: - if got := fw.LogicalRemaining(); got != tf.wantLCnt { - t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + if got := fw.logicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) } - if got := fw.PhysicalRemaining(); got != tf.wantPCnt { - t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + if got := fw.physicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) } default: t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) diff --git a/src/archive/zip/fuzz_test.go b/src/archive/zip/fuzz_test.go new file mode 100644 index 00000000000000..7dffde69bf5d5f --- /dev/null +++ b/src/archive/zip/fuzz_test.go @@ -0,0 +1,81 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" +) + +func FuzzReader(f *testing.F) { + testdata, err := os.ReadDir("testdata") + if err != nil { + f.Fatalf("failed to read testdata directory: %s", err) + } + for _, de := range testdata { + if de.IsDir() { + continue + } + b, err := os.ReadFile(filepath.Join("testdata", de.Name())) + if err != nil { + f.Fatalf("failed to read testdata: %s", err) + } + f.Add(b) + } + + f.Fuzz(func(t *testing.T, b []byte) { + r, err := NewReader(bytes.NewReader(b), int64(len(b))) + if err != nil { + return + } + + type file struct { + header *FileHeader + content []byte + } + files := []file{} + + for _, f := range r.File { + fr, err := f.Open() + if err != nil { + continue + } + content, err := io.ReadAll(fr) + if err != nil { + continue + } + files = append(files, file{header: &f.FileHeader, content: content}) + if _, err := r.Open(f.Name); err != nil { + continue + } + } + + // If we were unable to read anything out of the archive don't + // bother trying to roundtrip it. + if len(files) == 0 { + return + } + + w := NewWriter(io.Discard) + for _, f := range files { + ww, err := w.CreateHeader(f.header) + if err != nil { + t.Fatalf("unable to write previously parsed header: %s", err) + } + if _, err := ww.Write(f.content); err != nil { + t.Fatalf("unable to write previously parsed content: %s", err) + } + } + + if err := w.Close(); err != nil { + t.Fatalf("Unable to write archive: %s", err) + } + + // TODO: We may want to check if the archive roundtrips. + }) +} diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go index 2d53f4c7231653..d7fcff2afd120e 100644 --- a/src/archive/zip/reader.go +++ b/src/archive/zip/reader.go @@ -33,6 +33,10 @@ type Reader struct { Comment string decompressors map[uint16]Decompressor + // Some JAR files are zip files with a prefix that is a bash script. + // The baseOffset field is the start of the zip file proper. + baseOffset int64 + // fileList is a list of files sorted by ename, // for use by the Open method. fileListOnce sync.Once @@ -52,9 +56,8 @@ type File struct { FileHeader zip *Reader zipr io.ReaderAt - headerOffset int64 + headerOffset int64 // includes overall ZIP archive baseOffset zip64 bool // zip64 extended information extra field presence - descErr error // error reading the data descriptor during init } // OpenReader will open the Zip file specified by name and return a ReadCloser. @@ -91,23 +94,24 @@ func NewReader(r io.ReaderAt, size int64) (*Reader, error) { } func (z *Reader) init(r io.ReaderAt, size int64) error { - end, err := readDirectoryEnd(r, size) + end, baseOffset, err := readDirectoryEnd(r, size) if err != nil { return err } z.r = r + z.baseOffset = baseOffset // Since the number of directory records is not validated, it is not // safe to preallocate z.File without first checking that the specified // number of files is reasonable, since a malformed archive may // indicate it contains up to 1 << 128 - 1 files. Since each file has a // header which will be _at least_ 30 bytes we can safely preallocate // if (data size / 30) >= end.directoryRecords. - if (uint64(size)-end.directorySize)/30 >= end.directoryRecords { + if end.directorySize < uint64(size) && (uint64(size)-end.directorySize)/30 >= end.directoryRecords { z.File = make([]*File, 0, end.directoryRecords) } z.Comment = end.comment rs := io.NewSectionReader(r, 0, size) - if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil { + if _, err = rs.Seek(z.baseOffset+int64(end.directoryOffset), io.SeekStart); err != nil { return err } buf := bufio.NewReader(rs) @@ -119,13 +123,27 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { for { f := &File{zip: z, zipr: r} err = readDirectoryHeader(f, buf) + + // For compatibility with other zip programs, + // if we have a non-zero base offset and can't read + // the first directory header, try again with a zero + // base offset. + if err == ErrFormat && z.baseOffset != 0 && len(z.File) == 0 { + z.baseOffset = 0 + if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil { + return err + } + buf.Reset(rs) + continue + } + if err == ErrFormat || err == io.ErrUnexpectedEOF { break } if err != nil { return err } - f.readDataDescriptor() + f.headerOffset += z.baseOffset z.File = append(z.File, f) } if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here @@ -186,10 +204,15 @@ func (f *File) Open() (io.ReadCloser, error) { return nil, ErrAlgorithm } var rc io.ReadCloser = dcomp(r) + var desr io.Reader + if f.hasDataDescriptor() { + desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) + } rc = &checksumReader{ rc: rc, hash: crc32.NewIEEE(), f: f, + desr: desr, } return rc, nil } @@ -205,49 +228,13 @@ func (f *File) OpenRaw() (io.Reader, error) { return r, nil } -func (f *File) readDataDescriptor() { - if !f.hasDataDescriptor() { - return - } - - bodyOffset, err := f.findBodyOffset() - if err != nil { - f.descErr = err - return - } - - // In section 4.3.9.2 of the spec: "However ZIP64 format MAY be used - // regardless of the size of a file. When extracting, if the zip64 - // extended information extra field is present for the file the - // compressed and uncompressed sizes will be 8 byte values." - // - // Historically, this package has used the compressed and uncompressed - // sizes from the central directory to determine if the package is - // zip64. - // - // For this case we allow either the extra field or sizes to determine - // the data descriptor length. - zip64 := f.zip64 || f.isZip64() - n := int64(dataDescriptorLen) - if zip64 { - n = dataDescriptor64Len - } - size := int64(f.CompressedSize64) - r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, n) - dd, err := readDataDescriptor(r, zip64) - if err != nil { - f.descErr = err - return - } - f.CRC32 = dd.crc32 -} - type checksumReader struct { rc io.ReadCloser hash hash.Hash32 nread uint64 // number of bytes read so far f *File - err error // sticky error + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error } func (r *checksumReader) Stat() (fs.FileInfo, error) { @@ -261,6 +248,9 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { n, err = r.rc.Read(b) r.hash.Write(b[:n]) r.nread += uint64(n) + if r.nread > r.f.UncompressedSize64 { + return 0, ErrFormat + } if err == nil { return } @@ -268,12 +258,12 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { if r.nread != r.f.UncompressedSize64 { return 0, io.ErrUnexpectedEOF } - if r.f.hasDataDescriptor() { - if r.f.descErr != nil { - if r.f.descErr == io.EOF { + if r.desr != nil { + if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { + if err1 == io.EOF { err = io.ErrUnexpectedEOF } else { - err = r.f.descErr + err = err1 } } else if r.hash.Sum32() != r.f.CRC32 { err = ErrChecksum @@ -485,10 +475,8 @@ parseExtras: return nil } -func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { - // Create enough space for the largest possible size - var buf [dataDescriptor64Len]byte - +func readDataDescriptor(r io.Reader, f *File) error { + var buf [dataDescriptorLen]byte // The spec says: "Although not originally assigned a // signature, the value 0x08074b50 has commonly been adopted // as a signature value for the data descriptor record. @@ -497,9 +485,10 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { // descriptors and should account for either case when reading // ZIP files to ensure compatibility." // - // First read just those 4 bytes to see if the signature exists. + // dataDescriptorLen includes the size of the signature but + // first read just those 4 bytes to see if it exists. if _, err := io.ReadFull(r, buf[:4]); err != nil { - return nil, err + return err } off := 0 maybeSig := readBuf(buf[:4]) @@ -508,31 +497,24 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { // bytes. off += 4 } - - end := dataDescriptorLen - 4 - if zip64 { - end = dataDescriptor64Len - 4 + if _, err := io.ReadFull(r, buf[off:12]); err != nil { + return err } - if _, err := io.ReadFull(r, buf[off:end]); err != nil { - return nil, err + b := readBuf(buf[:12]) + if b.uint32() != f.CRC32 { + return ErrChecksum } - b := readBuf(buf[:end]) - out := &dataDescriptor{ - crc32: b.uint32(), - } + // The two sizes that follow here can be either 32 bits or 64 bits + // but the spec is not very clear on this and different + // interpretations has been made causing incompatibilities. We + // already have the sizes from the central directory so we can + // just ignore these. - if zip64 { - out.compressedSize = b.uint64() - out.uncompressedSize = b.uint64() - } else { - out.compressedSize = uint64(b.uint32()) - out.uncompressedSize = uint64(b.uint32()) - } - return out, nil + return nil } -func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { +func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, baseOffset int64, err error) { // look for directoryEndSignature in the last 1k, then in the last 65k var buf []byte var directoryEndOffset int64 @@ -542,7 +524,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) } buf = make([]byte, int(bLen)) if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { - return nil, err + return nil, 0, err } if p := findSignatureInBlock(buf); p >= 0 { buf = buf[p:] @@ -550,7 +532,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) break } if i == 1 || bLen == size { - return nil, ErrFormat + return nil, 0, ErrFormat } } @@ -567,7 +549,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) } l := int(d.commentLen) if l > len(b) { - return nil, errors.New("zip: invalid comment length") + return nil, 0, errors.New("zip: invalid comment length") } d.comment = string(b[:l]) @@ -575,17 +557,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) if d.directoryRecords == 0xffff || d.directorySize == 0xffff || d.directoryOffset == 0xffffffff { p, err := findDirectory64End(r, directoryEndOffset) if err == nil && p >= 0 { + directoryEndOffset = p err = readDirectory64End(r, p, d) } if err != nil { - return nil, err + return nil, 0, err } } + + baseOffset = directoryEndOffset - int64(d.directorySize) - int64(d.directoryOffset) + // Make sure directoryOffset points to somewhere in our file. - if o := int64(d.directoryOffset); o < 0 || o >= size { - return nil, ErrFormat + if o := baseOffset + int64(d.directoryOffset); o < 0 || o >= size { + return nil, 0, ErrFormat } - return d, nil + return d, baseOffset, nil } // findDirectory64End tries to read the zip64 locator just before the @@ -690,6 +676,7 @@ type fileListEntry struct { name string file *File isDir bool + isDup bool } type fileInfoDirEntry interface { @@ -697,11 +684,14 @@ type fileInfoDirEntry interface { fs.DirEntry } -func (e *fileListEntry) stat() fileInfoDirEntry { +func (e *fileListEntry) stat() (fileInfoDirEntry, error) { + if e.isDup { + return nil, errors.New(e.name + ": duplicate entries in zip file") + } if !e.isDir { - return headerFileInfo{&e.file.FileHeader} + return headerFileInfo{&e.file.FileHeader}, nil } - return e + return e, nil } // Only used for directories. @@ -710,7 +700,7 @@ func (f *fileListEntry) Size() int64 { return 0 } func (f *fileListEntry) Mode() fs.FileMode { return fs.ModeDir | 0555 } func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir } func (f *fileListEntry) IsDir() bool { return true } -func (f *fileListEntry) Sys() interface{} { return nil } +func (f *fileListEntry) Sys() any { return nil } func (f *fileListEntry) ModTime() time.Time { if f.file == nil { @@ -736,14 +726,37 @@ func toValidName(name string) string { func (r *Reader) initFileList() { r.fileListOnce.Do(func() { + // files and knownDirs map from a file/directory name + // to an index into the r.fileList entry that we are + // building. They are used to mark duplicate entries. + files := make(map[string]int) + knownDirs := make(map[string]int) + + // dirs[name] is true if name is known to be a directory, + // because it appears as a prefix in a path. dirs := make(map[string]bool) - knownDirs := make(map[string]bool) + for _, file := range r.File { isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/' name := toValidName(file.Name) + if name == "" { + continue + } + + if idx, ok := files[name]; ok { + r.fileList[idx].isDup = true + continue + } + if idx, ok := knownDirs[name]; ok { + r.fileList[idx].isDup = true + continue + } + for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) { dirs[dir] = true } + + idx := len(r.fileList) entry := fileListEntry{ name: name, file: file, @@ -751,17 +764,23 @@ func (r *Reader) initFileList() { } r.fileList = append(r.fileList, entry) if isDir { - knownDirs[name] = true + knownDirs[name] = idx + } else { + files[name] = idx } } for dir := range dirs { - if !knownDirs[dir] { - entry := fileListEntry{ - name: dir, - file: nil, - isDir: true, + if _, ok := knownDirs[dir]; !ok { + if idx, ok := files[dir]; ok { + r.fileList[idx].isDup = true + } else { + entry := fileListEntry{ + name: dir, + file: nil, + isDir: true, + } + r.fileList = append(r.fileList, entry) } - r.fileList = append(r.fileList, entry) } } @@ -782,8 +801,11 @@ func fileEntryLess(x, y string) bool { func (r *Reader) Open(name string) (fs.File, error) { r.initFileList() + if !fs.ValidPath(name) { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid} + } e := r.openLookup(name) - if e == nil || !fs.ValidPath(name) { + if e == nil { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} } if e.isDir { @@ -797,7 +819,7 @@ func (r *Reader) Open(name string) (fs.File, error) { } func split(name string) (dir, elem string, isDir bool) { - if name[len(name)-1] == '/' { + if len(name) > 0 && name[len(name)-1] == '/' { isDir = true name = name[:len(name)-1] } @@ -853,7 +875,7 @@ type openDir struct { } func (d *openDir) Close() error { return nil } -func (d *openDir) Stat() (fs.FileInfo, error) { return d.e.stat(), nil } +func (d *openDir) Stat() (fs.FileInfo, error) { return d.e.stat() } func (d *openDir) Read([]byte) (int, error) { return 0, &fs.PathError{Op: "read", Path: d.e.name, Err: errors.New("is a directory")} @@ -872,7 +894,11 @@ func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) { } list := make([]fs.DirEntry, n) for i := range list { - list[i] = d.files[d.offset+i].stat() + s, err := d.files[d.offset+i].stat() + if err != nil { + return nil, err + } + list[i] = s } d.offset += n return list, nil diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go index 37dafe6c8e7c44..84742c7d2af3c7 100644 --- a/src/archive/zip/reader_test.go +++ b/src/archive/zip/reader_test.go @@ -13,6 +13,7 @@ import ( "io/fs" "os" "path/filepath" + "reflect" "regexp" "strings" "testing" @@ -89,6 +90,42 @@ var tests = []ZipTest{ }, }, }, + { + Name: "test-prefix.zip", + Comment: "This is a zipfile comment.", + File: []ZipTestFile{ + { + Name: "test.txt", + Content: []byte("This is a test text file.\n"), + Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)), + Mode: 0644, + }, + { + Name: "gophercolor16x16.png", + File: "gophercolor16x16.png", + Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)), + Mode: 0644, + }, + }, + }, + { + Name: "test-baddirsz.zip", + Comment: "This is a zipfile comment.", + File: []ZipTestFile{ + { + Name: "test.txt", + Content: []byte("This is a test text file.\n"), + Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)), + Mode: 0644, + }, + { + Name: "gophercolor16x16.png", + File: "gophercolor16x16.png", + Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)), + Mode: 0644, + }, + }, + }, { Name: "r.zip", Source: returnRecursiveZip, @@ -486,6 +523,35 @@ var tests = []ZipTest{ }, }, }, + { + Name: "dupdir.zip", + File: []ZipTestFile{ + { + Name: "a/", + Content: []byte{}, + Mode: fs.ModeDir | 0666, + Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)), + }, + { + Name: "a/b", + Content: []byte{}, + Mode: 0666, + Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)), + }, + { + Name: "a/b/", + Content: []byte{}, + Mode: fs.ModeDir | 0666, + Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)), + }, + { + Name: "a/b/c", + Content: []byte{}, + Mode: 0666, + Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)), + }, + }, + }, } func TestReader(t *testing.T) { @@ -864,7 +930,6 @@ func returnRecursiveZip() (r io.ReaderAt, size int64) { // // It's here in hex for the same reason as rZipBytes above: to avoid // problems with on-disk virus scanners or other zip processors. -// func biggestZipBytes() []byte { s := ` 0000000 50 4b 03 04 14 00 08 00 08 00 00 00 00 00 00 00 @@ -1011,7 +1076,7 @@ func TestIssue10957(t *testing.T) { "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" + "00000000PK\x01\x0200000000" + "0000000000000000\v\x00\x00\x00" + - "\x00\x0000PK\x05\x06000000\x05\x000000" + + "\x00\x0000PK\x05\x06000000\x05\x00\xfd\x00\x00\x00" + "\v\x00\x00\x00\x00\x00") z, err := NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { @@ -1056,7 +1121,7 @@ func TestIssue11146(t *testing.T) { "0000000000000000PK\x01\x02" + "0000\b0\b\x00000000000000" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" + - "\x00\x0000\x01\x0000008\x00\x00\x00\x00\x00") + "\x00\x0000\x01\x00\x26\x00\x00\x008\x00\x00\x00\x00\x00") z, err := NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { t.Fatal(err) @@ -1123,6 +1188,7 @@ func TestFS(t *testing.T) { []string{"a/b/c"}, }, } { + test := test t.Run(test.file, func(t *testing.T) { t.Parallel() z, err := OpenReader(test.file) @@ -1137,6 +1203,60 @@ func TestFS(t *testing.T) { } } +func TestFSWalk(t *testing.T) { + for _, test := range []struct { + file string + want []string + wantErr bool + }{ + { + file: "testdata/unix.zip", + want: []string{".", "dir", "dir/bar", "dir/empty", "hello", "readonly"}, + }, + { + file: "testdata/subdir.zip", + want: []string{".", "a", "a/b", "a/b/c"}, + }, + { + file: "testdata/dupdir.zip", + wantErr: true, + }, + } { + test := test + t.Run(test.file, func(t *testing.T) { + t.Parallel() + z, err := OpenReader(test.file) + if err != nil { + t.Fatal(err) + } + var files []string + sawErr := false + err = fs.WalkDir(z, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + if !test.wantErr { + t.Errorf("%s: %v", path, err) + } + sawErr = true + return nil + } + files = append(files, path) + return nil + }) + if err != nil { + t.Errorf("fs.WalkDir error: %v", err) + } + if test.wantErr && !sawErr { + t.Error("succeeded but want error") + } else if !test.wantErr && sawErr { + t.Error("unexpected error") + } + if test.want != nil && !reflect.DeepEqual(files, test.want) { + t.Errorf("got %v want %v", files, test.want) + } + }) + } +} + func TestFSModTime(t *testing.T) { t.Parallel() z, err := OpenReader("testdata/subdir.zip") @@ -1202,127 +1322,14 @@ func TestCVE202127919(t *testing.T) { if err != nil { t.Errorf("Error reading file: %v", err) } -} - -func TestReadDataDescriptor(t *testing.T) { - tests := []struct { - desc string - in []byte - zip64 bool - want *dataDescriptor - wantErr error - }{{ - desc: "valid 32 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, // compressed size - 0x08, 0x09, 0x0a, 0x0b, // uncompressed size - }, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x07060504, - uncompressedSize: 0x0b0a0908, - }, - }, { - desc: "valid 32 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, // compressed size - 0x08, 0x09, 0x0a, 0x0b, // uncompressed size - }, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x07060504, - uncompressedSize: 0x0b0a0908, - }, - }, { - desc: "valid 64 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size - }, - zip64: true, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x0b0a090807060504, - uncompressedSize: 0x131211100f0e0d0c, - }, - }, { - desc: "valid 64 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size - }, - zip64: true, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x0b0a090807060504, - uncompressedSize: 0x131211100f0e0d0c, - }, - }, { - desc: "invalid 32 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, // unexpected end - }, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 32 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, // unexpected end - }, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 64 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end - }, - zip64: true, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 64 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end - }, - zip64: true, - wantErr: io.ErrUnexpectedEOF, - }} - - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - r := bytes.NewReader(test.in) - - desc, err := readDataDescriptor(r, test.zip64) - if err != test.wantErr { - t.Fatalf("got err %v; want nil", err) - } - if test.want == nil { - return - } - if desc == nil { - t.Fatalf("got nil DataDescriptor; want non-nil") - } - if desc.crc32 != test.want.crc32 { - t.Errorf("got CRC32 %#x; want %#x", desc.crc32, test.want.crc32) - } - if desc.compressedSize != test.want.compressedSize { - t.Errorf("got CompressedSize %#x; want %#x", desc.compressedSize, test.want.compressedSize) - } - if desc.uncompressedSize != test.want.uncompressedSize { - t.Errorf("got UncompressedSize %#x; want %#x", desc.uncompressedSize, test.want.uncompressedSize) - } - }) + if len(r.File) != 1 { + t.Fatalf("No entries in the file list") + } + if r.File[0].Name != "../test.txt" { + t.Errorf("Unexpected entry name: %s", r.File[0].Name) + } + if _, err := r.File[0].Open(); err != nil { + t.Errorf("Error opening file: %v", err) } } @@ -1384,3 +1391,166 @@ func TestCVE202133196(t *testing.T) { t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File)) } } + +func TestCVE202139293(t *testing.T) { + // directory size is so large, that the check in Reader.init + // overflows when subtracting from the archive size, causing + // the pre-allocation check to be bypassed. + data := []byte{ + 0x50, 0x4b, 0x06, 0x06, 0x05, 0x06, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, + 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, + 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x50, 0xfe, 0x00, 0xff, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xff, + } + _, err := NewReader(bytes.NewReader(data), int64(len(data))) + if err != ErrFormat { + t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat) + } +} + +func TestCVE202141772(t *testing.T) { + // Archive contains a file whose name is exclusively made up of '/', '\' + // characters, or "../", "..\" paths, which would previously cause a panic. + // + // Length Method Size Cmpr Date Time CRC-32 Name + // -------- ------ ------- ---- ---------- ----- -------- ---- + // 0 Stored 0 0% 08-05-2021 18:32 00000000 / + // 0 Stored 0 0% 09-14-2021 12:59 00000000 // + // 0 Stored 0 0% 09-14-2021 12:59 00000000 \ + // 11 Stored 11 0% 09-14-2021 13:04 0d4a1185 /test.txt + // -------- ------- --- ------- + // 11 11 0% 4 files + data := []byte{ + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x06, 0x94, 0x05, 0x53, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x50, + 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x50, + 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x50, 0x4b, + 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, 0x4a, 0x0d, + 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x74, 0x78, 0x74, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x0a, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x94, 0x05, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x50, + 0x4b, 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x0a, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x18, 0x00, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x3f, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, + 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x00, 0x5c, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x50, 0x4b, + 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, + 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, + 0x00, 0xa9, 0x80, 0x51, 0x01, 0x26, 0xa9, 0xd7, + 0x01, 0x31, 0xd1, 0x57, 0x01, 0x26, 0xa9, 0xd7, + 0x01, 0xdf, 0x48, 0x85, 0xf9, 0x25, 0xa9, 0xd7, + 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + } + r, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data))) + if err != nil { + t.Fatalf("Error reading the archive: %v", err) + } + entryNames := []string{`/`, `//`, `\`, `/test.txt`} + var names []string + for _, f := range r.File { + names = append(names, f.Name) + if _, err := f.Open(); err != nil { + t.Errorf("Error opening %q: %v", f.Name, err) + } + if _, err := r.Open(f.Name); err == nil { + t.Errorf("Opening %q with fs.FS API succeeded", f.Name) + } + } + if !reflect.DeepEqual(names, entryNames) { + t.Errorf("Unexpected file entries: %q", names) + } + if _, err := r.Open(""); err == nil { + t.Errorf("Opening %q with fs.FS API succeeded", "") + } + if _, err := r.Open("test.txt"); err != nil { + t.Errorf("Error opening %q with fs.FS API: %v", "test.txt", err) + } + dirEntries, err := fs.ReadDir(r, ".") + if err != nil { + t.Fatalf("Error reading the root directory: %v", err) + } + if len(dirEntries) != 1 || dirEntries[0].Name() != "test.txt" { + t.Errorf("Unexpected directory entries") + for _, dirEntry := range dirEntries { + _, err := r.Open(dirEntry.Name()) + t.Logf("%q (Open error: %v)", dirEntry.Name(), err) + } + t.FailNow() + } + info, err := dirEntries[0].Info() + if err != nil { + t.Fatalf("Error reading info entry: %v", err) + } + if name := info.Name(); name != "test.txt" { + t.Errorf("Inconsistent name in info entry: %v", name) + } +} + +func TestUnderSize(t *testing.T) { + z, err := OpenReader("testdata/readme.zip") + if err != nil { + t.Fatal(err) + } + defer z.Close() + + for _, f := range z.File { + f.UncompressedSize64 = 1 + } + + for _, f := range z.File { + t.Run(f.Name, func(t *testing.T) { + rd, err := f.Open() + if err != nil { + t.Fatal(err) + } + defer rd.Close() + + _, err = io.Copy(io.Discard, rd) + if err != ErrFormat { + t.Fatalf("Error mismatch\n\tGot: %v\n\tWant: %v", err, ErrFormat) + } + }) + } +} diff --git a/src/archive/zip/struct.go b/src/archive/zip/struct.go index ff9f605eb697ee..6f73fb8376a859 100644 --- a/src/archive/zip/struct.go +++ b/src/archive/zip/struct.go @@ -163,7 +163,7 @@ func (fi headerFileInfo) ModTime() time.Time { } func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() } func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() } -func (fi headerFileInfo) Sys() interface{} { return fi.fh } +func (fi headerFileInfo) Sys() any { return fi.fh } func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil } @@ -390,11 +390,3 @@ func unixModeToFileMode(m uint32) fs.FileMode { } return mode } - -// dataDescriptor holds the data descriptor that optionally follows the file -// contents in the zip file. -type dataDescriptor struct { - crc32 uint32 - compressedSize uint64 - uncompressedSize uint64 -} diff --git a/src/archive/zip/testdata/dupdir.zip b/src/archive/zip/testdata/dupdir.zip new file mode 100644 index 00000000000000..292720b7f01fc4 Binary files /dev/null and b/src/archive/zip/testdata/dupdir.zip differ diff --git a/src/archive/zip/testdata/readme.notzip b/src/archive/zip/testdata/readme.notzip index 81737275c6ebf5..79b1cb6de33c6a 100644 Binary files a/src/archive/zip/testdata/readme.notzip and b/src/archive/zip/testdata/readme.notzip differ diff --git a/src/archive/zip/testdata/test-baddirsz.zip b/src/archive/zip/testdata/test-baddirsz.zip new file mode 100644 index 00000000000000..45b331407624d0 Binary files /dev/null and b/src/archive/zip/testdata/test-baddirsz.zip differ diff --git a/src/archive/zip/testdata/test-prefix.zip b/src/archive/zip/testdata/test-prefix.zip new file mode 100644 index 00000000000000..1eabb4861ece50 Binary files /dev/null and b/src/archive/zip/testdata/test-prefix.zip differ diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go index 97c6c529799468..2b73eca814f621 100644 --- a/src/archive/zip/writer_test.go +++ b/src/archive/zip/writer_test.go @@ -362,7 +362,7 @@ func TestWriterDirAttributes(t *testing.T) { } binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature)) - if bytes.Index(b, sig[:]) != -1 { + if bytes.Contains(b, sig[:]) { t.Error("there should be no data descriptor") } } diff --git a/src/archive/zip/zip_test.go b/src/archive/zip/zip_test.go index ead9cd3aab991f..a4b952efcc6ce0 100644 --- a/src/archive/zip/zip_test.go +++ b/src/archive/zip/zip_test.go @@ -24,7 +24,7 @@ func TestOver65kFiles(t *testing.T) { if testing.Short() && testenv.Builder() == "" { t.Skip("skipping in short mode") } - buf := new(bytes.Buffer) + buf := new(strings.Builder) w := NewWriter(buf) const nFiles = (1 << 16) + 42 for i := 0; i < nFiles; i++ { diff --git a/src/bootstrap.bash b/src/bootstrap.bash index 88c080a9487e1a..1e4f1c5081348c 100755 --- a/src/bootstrap.bash +++ b/src/bootstrap.bash @@ -15,14 +15,8 @@ # Only changes that have been committed to Git (at least locally, # not necessary reviewed and submitted to master) are included in the tree. # -# As a special case for Go's internal use only, if the -# BOOTSTRAP_FORMAT environment variable is set to "mintgz", the -# resulting archive is intended for use by the Go build system and -# differs in that the mintgz file: -# * is a tar.gz file instead of bz2 -# * has many unnecessary files deleted to reduce its size -# * does not have a shared directory component for each tar entry -# Do not depend on the mintgz format. +# See also golang.org/x/build/cmd/genbootstrap, which is used +# to generate bootstrap tgz files for builders. set -e @@ -37,11 +31,6 @@ if [ -e $targ ]; then exit 2 fi -if [ "$BOOTSTRAP_FORMAT" != "mintgz" -a "$BOOTSTRAP_FORMAT" != "" ]; then - echo "unknown BOOTSTRAP_FORMAT format" - exit 2 -fi - unset GOROOT src=$(cd .. && pwd) echo "#### Copying to $targ" @@ -79,39 +68,8 @@ else rm -rf "pkg/${gohostos}_${gohostarch}" "pkg/tool/${gohostos}_${gohostarch}" fi -if [ "$BOOTSTRAP_FORMAT" = "mintgz" ]; then - # Fetch git revision before rm -rf .git. - GITREV=$(git rev-parse --short HEAD) -fi - rm -rf pkg/bootstrap pkg/obj .git -# Support for building minimal tar.gz for the builders. -# The build system doesn't support bzip2, and by deleting more stuff, -# they start faster, especially on machines without fast filesystems -# and things like tmpfs configures. -# Do not depend on this format. It's for internal use only. -if [ "$BOOTSTRAP_FORMAT" = "mintgz" ]; then - OUTGZ="gobootstrap-${GOOS}-${GOARCH}-${GITREV}.tar.gz" - echo "Preparing to generate build system's ${OUTGZ}; cleaning ..." - rm -rf bin/gofmt - rm -rf src/runtime/race/race_*.syso - rm -rf api test doc misc/cgo/test misc/trace - rm -rf pkg/tool/*_*/{addr2line,api,cgo,cover,doc,fix,nm,objdump,pack,pprof,test2json,trace,vet} - rm -rf pkg/*_*/{image,database,cmd} - rm -rf $(find . -type d -name testdata) - find . -type f -name '*_test.go' -exec rm {} \; - # git clean doesn't clean symlinks apparently, and the buildlet - # rejects them, so: - find . -type l -exec rm {} \; - - echo "Writing ${OUTGZ} ..." - tar cf - . | gzip -9 > ../$OUTGZ - cd .. - ls -l "$(pwd)/$OUTGZ" - exit 0 -fi - echo ---- echo Bootstrap toolchain for "$GOOS/$GOARCH" installed in "$(pwd)". echo Building tbz. diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index ec928e7ad69ed5..1da8ffa951cc0d 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -68,7 +68,12 @@ func (b *Reader) Size() int { return len(b.buf) } // Reset discards any buffered data, resets all state, and switches // the buffered reader to read from r. +// Calling Reset on the zero value of Reader initializes the internal buffer +// to the default size. func (b *Reader) Reset(r io.Reader) { + if b.buf == nil { + b.buf = make([]byte, defaultBufSize) + } b.reset(b.buf, r) } @@ -168,6 +173,10 @@ func (b *Reader) Discard(n int) (discarded int, err error) { if n == 0 { return } + + b.lastByte = -1 + b.lastRuneSize = -1 + remain := n for { skip := b.Buffered() @@ -194,7 +203,8 @@ func (b *Reader) Discard(n int) (discarded int, err error) { // The bytes are taken from at most one Read on the underlying Reader, // hence n may be less than len(p). // To read exactly len(p) bytes, use io.ReadFull(b, p). -// At EOF, the count will be zero and err will be io.EOF. +// If the underlying Reader can return a non-zero count with io.EOF, +// then this Read method can do so as well; see the [io.Reader] docs. func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) if n == 0 { @@ -235,6 +245,8 @@ func (b *Reader) Read(p []byte) (n int, err error) { } // copy as much as we can + // Note: if the slice panics here, it is probably because + // the underlying reader returned a bad count. See issue 49795. n = copy(p, b.buf[b.r:b.w]) b.r += n b.lastByte = int(b.buf[b.r-1]) @@ -261,8 +273,8 @@ func (b *Reader) ReadByte() (byte, error) { // UnreadByte unreads the last byte. Only the most recently read byte can be unread. // // UnreadByte returns an error if the most recent method called on the -// Reader was not a read operation. Notably, Peek is not considered a -// read operation. +// Reader was not a read operation. Notably, Peek, Discard, and WriteTo are not +// considered read operations. func (b *Reader) UnreadByte() error { if b.lastByte < 0 || b.r == 0 && b.w > 0 { return ErrInvalidUnreadByte @@ -497,6 +509,9 @@ func (b *Reader) ReadString(delim byte) (string, error) { // If the underlying reader supports the WriteTo method, // this calls the underlying WriteTo without buffering. func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { + b.lastByte = -1 + b.lastRuneSize = -1 + n, err = b.writeBuf(w) if err != nil { return @@ -581,6 +596,8 @@ func NewWriterSize(w io.Writer, size int) *Writer { } // NewWriter returns a new Writer whose buffer has the default size. +// If the argument io.Writer is already a Writer with large enough buffer size, +// it returns the underlying Writer. func NewWriter(w io.Writer) *Writer { return NewWriterSize(w, defaultBufSize) } @@ -590,7 +607,12 @@ func (b *Writer) Size() int { return len(b.buf) } // Reset discards any unflushed buffered data, clears any error, and // resets b to write its output to w. +// Calling Reset on the zero value of Writer initializes the internal buffer +// to the default size. func (b *Writer) Reset(w io.Writer) { + if b.buf == nil { + b.buf = make([]byte, defaultBufSize) + } b.err = nil b.n = 0 b.wr = w @@ -623,6 +645,14 @@ func (b *Writer) Flush() error { // Available returns how many bytes are unused in the buffer. func (b *Writer) Available() int { return len(b.buf) - b.n } +// AvailableBuffer returns an empty buffer with b.Available() capacity. +// This buffer is intended to be appended to and +// passed to an immediately succeeding Write call. +// The buffer is only valid until the next write operation on b. +func (b *Writer) AvailableBuffer() []byte { + return b.buf[b.n:][:0] +} + // Buffered returns the number of bytes that have been written into the current buffer. func (b *Writer) Buffered() int { return b.n } @@ -702,13 +732,28 @@ func (b *Writer) WriteRune(r rune) (size int, err error) { // If the count is less than len(s), it also returns an error explaining // why the write is short. func (b *Writer) WriteString(s string) (int, error) { + var sw io.StringWriter + tryStringWriter := true + nn := 0 for len(s) > b.Available() && b.err == nil { - n := copy(b.buf[b.n:], s) - b.n += n + var n int + if b.Buffered() == 0 && sw == nil && tryStringWriter { + // Check at most once whether b.wr is a StringWriter. + sw, tryStringWriter = b.wr.(io.StringWriter) + } + if b.Buffered() == 0 && tryStringWriter { + // Large write, empty buffer, and the underlying writer supports + // WriteString: forward the write to the underlying StringWriter. + // This avoids an extra copy. + n, b.err = sw.WriteString(s) + } else { + n = copy(b.buf[b.n:], s) + b.n += n + b.Flush() + } nn += n s = s[n:] - b.Flush() } if b.err != nil { return nn, b.err @@ -720,19 +765,14 @@ func (b *Writer) WriteString(s string) (int, error) { } // ReadFrom implements io.ReaderFrom. If the underlying writer -// supports the ReadFrom method, and b has no buffered data yet, -// this calls the underlying ReadFrom without buffering. +// supports the ReadFrom method, this calls the underlying ReadFrom. +// If there is buffered data and an underlying ReadFrom, this fills +// the buffer and writes it before calling ReadFrom. func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { if b.err != nil { return 0, b.err } - if b.Buffered() == 0 { - if w, ok := b.wr.(io.ReaderFrom); ok { - n, err = w.ReadFrom(r) - b.err = err - return n, err - } - } + readerFrom, readerFromOK := b.wr.(io.ReaderFrom) var m int for { if b.Available() == 0 { @@ -740,6 +780,12 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { return n, err1 } } + if readerFromOK && b.Buffered() == 0 { + nn, err := readerFrom.ReadFrom(r) + b.err = err + n += nn + return n, err + } nr := 0 for nr < maxConsecutiveEmptyReads { m, err = r.Read(b.buf[b.n:]) diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index ebcc711db9d48c..e6a6c23dbf5242 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -10,6 +10,8 @@ import ( "errors" "fmt" "io" + "math/rand" + "strconv" "strings" "testing" "testing/iotest" @@ -302,6 +304,40 @@ func TestNoUnreadByteAfterPeek(t *testing.T) { } } +func TestNoUnreadRuneAfterDiscard(t *testing.T) { + br := NewReader(strings.NewReader("example")) + br.ReadRune() + br.Discard(1) + if err := br.UnreadRune(); err == nil { + t.Error("UnreadRune didn't fail after Discard") + } +} + +func TestNoUnreadByteAfterDiscard(t *testing.T) { + br := NewReader(strings.NewReader("example")) + br.ReadByte() + br.Discard(1) + if err := br.UnreadByte(); err == nil { + t.Error("UnreadByte didn't fail after Discard") + } +} + +func TestNoUnreadRuneAfterWriteTo(t *testing.T) { + br := NewReader(strings.NewReader("example")) + br.WriteTo(io.Discard) + if err := br.UnreadRune(); err == nil { + t.Error("UnreadRune didn't fail after WriteTo") + } +} + +func TestNoUnreadByteAfterWriteTo(t *testing.T) { + br := NewReader(strings.NewReader("example")) + br.WriteTo(io.Discard) + if err := br.UnreadByte(); err == nil { + t.Error("UnreadByte didn't fail after WriteTo") + } +} + func TestUnreadByte(t *testing.T) { segments := []string{"Hello, ", "world"} r := NewReader(&StringReader{data: segments}) @@ -538,7 +574,7 @@ func TestWriteInvalidRune(t *testing.T) { // Invalid runes, including negative ones, should be written as the // replacement character. for _, r := range []rune{-1, utf8.MaxRune + 1} { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(&buf) w.WriteRune(r) w.Flush() @@ -608,6 +644,37 @@ func TestWriter(t *testing.T) { } } +func TestWriterAppend(t *testing.T) { + got := new(bytes.Buffer) + var want []byte + rn := rand.New(rand.NewSource(0)) + w := NewWriterSize(got, 64) + for i := 0; i < 100; i++ { + // Obtain a buffer to append to. + b := w.AvailableBuffer() + if w.Available() != cap(b) { + t.Fatalf("Available() = %v, want %v", w.Available(), cap(b)) + } + + // While not recommended, it is valid to append to a shifted buffer. + // This forces Write to copy the input. + if rn.Intn(8) == 0 && cap(b) > 0 { + b = b[1:1:cap(b)] + } + + // Append a random integer of varying width. + n := int64(rn.Intn(1 << rn.Intn(30))) + want = append(strconv.AppendInt(want, n, 10), ' ') + b = append(strconv.AppendInt(b, n, 10), ' ') + w.Write(b) + } + w.Flush() + + if !bytes.Equal(got.Bytes(), want) { + t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want) + } +} + // Check that write errors are returned properly. type errorWriterTest struct { @@ -695,6 +762,67 @@ func TestWriteString(t *testing.T) { } } +func TestWriteStringStringWriter(t *testing.T) { + const BufSize = 8 + { + tw := &teststringwriter{} + b := NewWriterSize(tw, BufSize) + b.WriteString("1234") + tw.check(t, "", "") + b.WriteString("56789012") // longer than BufSize + tw.check(t, "12345678", "") // but not enough (after filling the partially-filled buffer) + b.Flush() + tw.check(t, "123456789012", "") + } + { + tw := &teststringwriter{} + b := NewWriterSize(tw, BufSize) + b.WriteString("123456789") // long string, empty buffer: + tw.check(t, "", "123456789") // use WriteString + } + { + tw := &teststringwriter{} + b := NewWriterSize(tw, BufSize) + b.WriteString("abc") + tw.check(t, "", "") + b.WriteString("123456789012345") // long string, non-empty buffer + tw.check(t, "abc12345", "6789012345") // use Write and then WriteString since the remaining part is still longer than BufSize + } + { + tw := &teststringwriter{} + b := NewWriterSize(tw, BufSize) + b.Write([]byte("abc")) // same as above, but use Write instead of WriteString + tw.check(t, "", "") + b.WriteString("123456789012345") + tw.check(t, "abc12345", "6789012345") // same as above + } +} + +type teststringwriter struct { + write string + writeString string +} + +func (w *teststringwriter) Write(b []byte) (int, error) { + w.write += string(b) + return len(b), nil +} + +func (w *teststringwriter) WriteString(s string) (int, error) { + w.writeString += s + return len(s), nil +} + +func (w *teststringwriter) check(t *testing.T, write, writeString string) { + t.Helper() + if w.write != write { + t.Errorf("write: expected %q, got %q", write, w.write) + } + if w.writeString != writeString { + t.Errorf("writeString: expected %q, got %q", writeString, w.writeString) + } +} + func TestBufferFull(t *testing.T) { const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party" buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize) @@ -873,7 +1001,7 @@ func TestReadAfterLines(t *testing.T) { line1 := "this is line1" restData := "this is line2\nthis is line 3\n" inbuf := bytes.NewReader([]byte(line1 + "\n" + restData)) - outbuf := new(bytes.Buffer) + outbuf := new(strings.Builder) maxLineLength := len(line1) + len(restData)/2 l := NewReaderSize(inbuf, maxLineLength) line, isPrefix, err := l.ReadLine() @@ -1045,7 +1173,7 @@ func TestWriterReadFrom(t *testing.T) { for ri, rfunc := range rs { for wi, wfunc := range ws { input := createTestInput(8192) - b := new(bytes.Buffer) + b := new(strings.Builder) w := NewWriter(wfunc(b)) r := rfunc(bytes.NewReader(input)) if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) { @@ -1284,6 +1412,54 @@ func TestWriterReadFromErrNoProgress(t *testing.T) { } } +type readFromWriter struct { + buf []byte + writeBytes int + readFromBytes int +} + +func (w *readFromWriter) Write(p []byte) (int, error) { + w.buf = append(w.buf, p...) + w.writeBytes += len(p) + return len(p), nil +} + +func (w *readFromWriter) ReadFrom(r io.Reader) (int64, error) { + b, err := io.ReadAll(r) + w.buf = append(w.buf, b...) + w.readFromBytes += len(b) + return int64(len(b)), err +} + +// Test that calling (*Writer).ReadFrom with a partially-filled buffer +// fills the buffer before switching over to ReadFrom. +func TestWriterReadFromWithBufferedData(t *testing.T) { + const bufsize = 16 + + input := createTestInput(64) + rfw := &readFromWriter{} + w := NewWriterSize(rfw, bufsize) + + const writeSize = 8 + if n, err := w.Write(input[:writeSize]); n != writeSize || err != nil { + t.Errorf("w.Write(%v bytes) = %v, %v; want %v, nil", writeSize, n, err, writeSize) + } + n, err := w.ReadFrom(bytes.NewReader(input[writeSize:])) + if wantn := len(input[writeSize:]); int(n) != wantn || err != nil { + t.Errorf("io.Copy(w, %v bytes) = %v, %v; want %v, nil", wantn, n, err, wantn) + } + if err := w.Flush(); err != nil { + t.Errorf("w.Flush() = %v, want nil", err) + } + + if got, want := rfw.writeBytes, bufsize; got != want { + t.Errorf("wrote %v bytes with Write, want %v", got, want) + } + if got, want := rfw.readFromBytes, len(input)-bufsize; got != want { + t.Errorf("wrote %v bytes with ReadFrom, want %v", got, want) + } +} + func TestReadZero(t *testing.T) { for _, size := range []int{100, 2} { t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) { @@ -1312,6 +1488,7 @@ func TestReaderReset(t *testing.T) { if string(buf) != "foo" { t.Errorf("buf = %q; want foo", buf) } + r.Reset(strings.NewReader("bar bar")) all, err := io.ReadAll(r) if err != nil { @@ -1320,12 +1497,23 @@ func TestReaderReset(t *testing.T) { if string(all) != "bar bar" { t.Errorf("ReadAll = %q; want bar bar", all) } + + *r = Reader{} // zero out the Reader + r.Reset(strings.NewReader("bar bar")) + all, err = io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if string(all) != "bar bar" { + t.Errorf("ReadAll = %q; want bar bar", all) + } } func TestWriterReset(t *testing.T) { - var buf1, buf2 bytes.Buffer + var buf1, buf2, buf3 strings.Builder w := NewWriter(&buf1) w.WriteString("foo") + w.Reset(&buf2) // and not flushed w.WriteString("bar") w.Flush() @@ -1335,6 +1523,17 @@ func TestWriterReset(t *testing.T) { if buf2.String() != "bar" { t.Errorf("buf2 = %q; want bar", buf2.String()) } + + *w = Writer{} // zero out the Writer + w.Reset(&buf3) // and not flushed + w.WriteString("bar") + w.Flush() + if buf1.String() != "" { + t.Errorf("buf1 = %q; want empty", buf1.String()) + } + if buf3.String() != "bar" { + t.Errorf("buf3 = %q; want bar", buf3.String()) + } } func TestReaderDiscard(t *testing.T) { @@ -1382,7 +1581,7 @@ func TestReaderDiscard(t *testing.T) { wantBuffered: 0, }, // Any error from filling shouldn't show up until we - // get past the valid bytes. Here we return we return 5 valid bytes at the same time + // get past the valid bytes. Here we return 5 valid bytes at the same time // as an error, but test that we don't see the error from Discard. { name: "fill error, discard less", diff --git a/src/bufio/example_test.go b/src/bufio/example_test.go index 8885d40549f153..a864d11012e77f 100644 --- a/src/bufio/example_test.go +++ b/src/bufio/example_test.go @@ -20,6 +20,18 @@ func ExampleWriter() { // Output: Hello, world! } +func ExampleWriter_AvailableBuffer() { + w := bufio.NewWriter(os.Stdout) + for _, i := range []int64{1, 2, 3, 4} { + b := w.AvailableBuffer() + b = strconv.AppendInt(b, i, 10) + b = append(b, ' ') + w.Write(b) + } + w.Flush() + // Output: 1 2 3 4 +} + // The simplest use of a Scanner, to read standard input as a set of lines. func ExampleScanner_lines() { scanner := bufio.NewScanner(os.Stdin) diff --git a/src/bufio/scan.go b/src/bufio/scan.go index 4846d4f7336777..e247cbcf32f55c 100644 --- a/src/bufio/scan.go +++ b/src/bufio/scan.go @@ -26,7 +26,6 @@ import ( // advanced arbitrarily far past the last token. Programs that need more // control over error handling or large tokens, or must run sequential scans // on a reader, should use bufio.Reader instead. -// type Scanner struct { r io.Reader // The reader provided by the client. split SplitFunc // The function to split the tokens. diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go index 01190e99002d44..7feb209bb4992b 100644 --- a/src/builtin/builtin.go +++ b/src/builtin/builtin.go @@ -3,10 +3,10 @@ // license that can be found in the LICENSE file. /* - Package builtin provides documentation for Go's predeclared identifiers. - The items documented here are not actually in package builtin - but their descriptions here allow godoc to present documentation - for the language's special identifiers. +Package builtin provides documentation for Go's predeclared identifiers. +The items documented here are not actually in package builtin +but their descriptions here allow godoc to present documentation +for the language's special identifiers. */ package builtin @@ -91,6 +91,16 @@ type byte = uint8 // used, by convention, to distinguish character values from integer values. type rune = int32 +// any is an alias for interface{} and is equivalent to interface{} in all ways. +type any = interface{} + +// comparable is an interface that is implemented by all comparable types +// (booleans, numbers, strings, pointers, channels, arrays of comparable types, +// structs whose fields are all comparable types). +// The comparable interface may only be used as a type parameter constraint, +// not as the type of a variable. +type comparable interface{ comparable } + // iota is a predeclared identifier representing the untyped integer ordinal // number of the current const specification in a (usually parenthesized) // const declaration. It is zero-indexed. @@ -127,9 +137,12 @@ type ComplexType complex64 // new elements. If it does not, a new underlying array will be allocated. // Append returns the updated slice. It is therefore necessary to store the // result of append, often in the variable holding the slice itself: +// // slice = append(slice, elem1, elem2) // slice = append(slice, anotherSlice...) +// // As a special case, it is legal to append a string to a byte slice, like this: +// // slice = append([]byte("hello "), "world"...) func append(slice []Type, elems ...Type) []Type @@ -146,24 +159,28 @@ func copy(dst, src []Type) int func delete(m map[Type]Type1, key Type) // The len built-in function returns the length of v, according to its type: +// // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). // Slice, or map: the number of elements in v; if v is nil, len(v) is zero. // String: the number of bytes in v. // Channel: the number of elements queued (unread) in the channel buffer; // if v is nil, len(v) is zero. +// // For some arguments, such as a string literal or a simple array expression, the // result can be a constant. See the Go language specification's "Length and // capacity" section for details. func len(v Type) int // The cap built-in function returns the capacity of v, according to its type: +// // Array: the number of elements in v (same as len(v)). // Pointer to array: the number of elements in *v (same as len(v)). // Slice: the maximum length the slice can reach when resliced; // if v is nil, cap(v) is zero. // Channel: the channel buffer capacity, in units of elements; // if v is nil, cap(v) is zero. +// // For some arguments, such as a simple array expression, the result can be a // constant. See the Go language specification's "Length and capacity" section for // details. @@ -174,6 +191,7 @@ func cap(v Type) int // value. Unlike new, make's return type is the same as the type of its // argument, not a pointer to it. The specification of the result depends on // the type: +// // Slice: The size specifies the length. The capacity of the slice is // equal to its length. A second integer argument may be provided to // specify a different capacity; it must be no smaller than the @@ -215,8 +233,10 @@ func imag(c ComplexType) FloatType // the last sent value is received. After the last value has been received // from a closed channel c, any receive from c will succeed without // blocking, returning the zero value for the channel element. The form +// // x, ok := <-c -// will also set ok to false for a closed channel. +// +// will also set ok to false for a closed and empty channel. func close(c chan<- Type) // The panic built-in function stops normal execution of the current @@ -229,7 +249,7 @@ func close(c chan<- Type) // that point, the program is terminated with a non-zero exit code. This // termination sequence is called panicking and can be controlled by the // built-in function recover. -func panic(v interface{}) +func panic(v any) // The recover built-in function allows a program to manage behavior of a // panicking goroutine. Executing a call to recover inside a deferred @@ -240,7 +260,7 @@ func panic(v interface{}) // panicking, or if the argument supplied to panic was nil, recover returns // nil. Thus the return value from recover reports whether the goroutine is // panicking. -func recover() interface{} +func recover() any // The print built-in function formats its arguments in an // implementation-specific way and writes the result to standard error. diff --git a/src/bytes/boundary_test.go b/src/bytes/boundary_test.go index 5a47526593b093..f9855fcb0520f3 100644 --- a/src/bytes/boundary_test.go +++ b/src/bytes/boundary_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build linux -// +build linux package bytes_test @@ -66,7 +65,11 @@ func TestIndexByteNearPageBoundary(t *testing.T) { func TestIndexNearPageBoundary(t *testing.T) { t.Parallel() - var q [64]byte + q := dangerousSlice(t) + if len(q) > 64 { + // Only worry about when we're near the end of a page. + q = q[len(q)-64:] + } b := dangerousSlice(t) if len(b) > 256 { // Only worry about when we're near the end of a page. @@ -82,4 +85,16 @@ func TestIndexNearPageBoundary(t *testing.T) { } q[j-1] = 0 } + + // Test differing alignments and sizes of q which always end on a page boundary. + q[len(q)-1] = 1 // difference is only found on the last byte + for j := 0; j < len(q); j++ { + for i := range b { + idx := Index(b[i:], q[j:]) + if idx != -1 { + t.Fatalf("Index(b[%d:], q[%d:])=%d, want -1\n", i, j, idx) + } + } + } + q[len(q)-1] = 0 } diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go index 549b077708f80e..ee83fd8b3620e5 100644 --- a/src/bytes/buffer.go +++ b/src/bytes/buffer.go @@ -138,10 +138,8 @@ func (b *Buffer) grow(n int) int { } else if c > maxInt-c-n { panic(ErrTooLarge) } else { - // Not enough space anywhere, we need to allocate. - buf := makeSlice(2*c + n) - copy(buf, b.buf[b.off:]) - b.buf = buf + // Add b.off to account for b.buf[:b.off] being sliced off the front. + b.buf = growSlice(b.buf[b.off:], b.off+n) } // Restore b.off and len(b.buf). b.off = 0 @@ -217,16 +215,31 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { } } -// makeSlice allocates a slice of size n. If the allocation fails, it panics -// with ErrTooLarge. -func makeSlice(n int) []byte { - // If the make fails, give a known error. +// growSlice grows b by n, preserving the original content of b. +// If the allocation fails, it panics with ErrTooLarge. +func growSlice(b []byte, n int) []byte { defer func() { if recover() != nil { panic(ErrTooLarge) } }() - return make([]byte, n) + // TODO(http://golang.org/issue/51462): We should rely on the append-make + // pattern so that the compiler can call runtime.growslice. For example: + // return append(b, make([]byte, n)...) + // This avoids unnecessary zero-ing of the first len(b) bytes of the + // allocated slice, but this pattern causes b to escape onto the heap. + // + // Instead use the append-make pattern with a nil slice to ensure that + // we allocate buffers rounded up to the closest size class. + c := len(b) + n // ensure enough space for n elements + if c < 2*cap(b) { + // The growth rate has historically always been 2x. In the future, + // we could rely purely on append to determine the growth rate. + c = 2 * cap(b) + } + b2 := append([]byte(nil), make([]byte, c)...) + copy(b2, b) + return b2[:len(b)] } // WriteTo writes data to w until the buffer is drained or an error occurs. @@ -285,9 +298,8 @@ func (b *Buffer) WriteRune(r rune) (n int, err error) { if !ok { m = b.grow(utf8.UTFMax) } - n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r) - b.buf = b.buf[:m+n] - return n, nil + b.buf = utf8.AppendRune(b.buf[:m], r) + return len(b.buf) - m, nil } // Read reads the next len(p) bytes from the buffer or until the buffer diff --git a/src/bytes/buffer_test.go b/src/bytes/buffer_test.go index 9c9b7440ffaa75..c0855007c188ea 100644 --- a/src/bytes/buffer_test.go +++ b/src/bytes/buffer_test.go @@ -672,3 +672,18 @@ func BenchmarkBufferFullSmallReads(b *testing.B) { } } } + +func BenchmarkBufferWriteBlock(b *testing.B) { + block := make([]byte, 1024) + for _, n := range []int{1 << 12, 1 << 16, 1 << 20} { + b.Run(fmt.Sprintf("N%d", n), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var bb Buffer + for bb.Len() < n { + bb.Write(block) + } + } + }) + } +} diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index ce52649f132bb9..7b28cf1efcd242 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -21,7 +21,7 @@ func Equal(a, b []byte) bool { } // Compare returns an integer comparing two byte slices lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. +// The result will be 0 if a == b, -1 if a < b, and +1 if a > b. // A nil argument is equivalent to an empty slice. func Compare(a, b []byte) int { return bytealg.Compare(a, b) @@ -30,7 +30,7 @@ func Compare(a, b []byte) int { // explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes), // up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes. func explode(s []byte, n int) [][]byte { - if n <= 0 { + if n <= 0 || n > len(s) { n = len(s) } a := make([][]byte, n) @@ -348,6 +348,9 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte { if n < 0 { n = Count(s, sep) + 1 } + if n > len(s)+1 { + n = len(s) + 1 + } a := make([][]byte, n) n-- @@ -369,18 +372,22 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte { // the subslices between those separators. // If sep is empty, SplitN splits after each UTF-8 sequence. // The count determines the number of subslices to return: -// n > 0: at most n subslices; the last subslice will be the unsplit remainder. -// n == 0: the result is nil (zero subslices) -// n < 0: all subslices +// +// n > 0: at most n subslices; the last subslice will be the unsplit remainder. +// n == 0: the result is nil (zero subslices) +// n < 0: all subslices +// +// To split around the first instance of a separator, see Cut. func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) } // SplitAfterN slices s into subslices after each instance of sep and // returns a slice of those subslices. // If sep is empty, SplitAfterN splits after each UTF-8 sequence. // The count determines the number of subslices to return: -// n > 0: at most n subslices; the last subslice will be the unsplit remainder. -// n == 0: the result is nil (zero subslices) -// n < 0: all subslices +// +// n > 0: at most n subslices; the last subslice will be the unsplit remainder. +// n == 0: the result is nil (zero subslices) +// n < 0: all subslices func SplitAfterN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, len(sep), n) } @@ -389,6 +396,8 @@ func SplitAfterN(s, sep []byte, n int) [][]byte { // the subslices between those separators. // If sep is empty, Split splits after each UTF-8 sequence. // It is equivalent to SplitN with a count of -1. +// +// To split around the first instance of a separator, see Cut. func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) } // SplitAfter slices s into all subslices after each instance of sep and @@ -551,9 +560,7 @@ func Map(mapping func(r rune) rune, s []byte) []byte { // In the worst case, the slice can grow when mapped, making // things unpleasant. But it's so rare we barge in assuming it's // fine. It could also shrink but that falls out naturally. - maxbytes := len(s) // length of b - nbytes := 0 // number of bytes encoded in b - b := make([]byte, maxbytes) + b := make([]byte, 0, len(s)) for i := 0; i < len(s); { wid := 1 r := rune(s[i]) @@ -562,22 +569,11 @@ func Map(mapping func(r rune) rune, s []byte) []byte { } r = mapping(r) if r >= 0 { - rl := utf8.RuneLen(r) - if rl < 0 { - rl = len(string(utf8.RuneError)) - } - if nbytes+rl > maxbytes { - // Grow the buffer. - maxbytes = maxbytes*2 + utf8.UTFMax - nb := make([]byte, maxbytes) - copy(nb, b[0:nbytes]) - b = nb - } - nbytes += utf8.EncodeRune(b[nbytes:maxbytes], r) + b = utf8.AppendRune(b, r) } i += wid } - return b[0:nbytes] + return b } // Repeat returns a new byte slice consisting of count copies of b. @@ -699,7 +695,7 @@ func ToValidUTF8(s, replacement []byte) []byte { if c < utf8.RuneSelf { i++ invalid = false - b = append(b, byte(c)) + b = append(b, c) continue } _, wid := utf8.DecodeRune(s[i:]) @@ -746,7 +742,8 @@ func isSeparator(r rune) bool { // Title treats s as UTF-8-encoded bytes and returns a copy with all Unicode letters that begin // words mapped to their title case. // -// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly. +// Deprecated: The rule Title uses for word boundaries does not handle Unicode +// punctuation properly. Use golang.org/x/text/cases instead. func Title(s []byte) []byte { // Use a closure here to remember state. // Hackish but effective. Depends on Map scanning in order and calling @@ -867,6 +864,8 @@ func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int { // most-significant bit of the highest word, map to the full range of all // 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed, // ensuring that any non-ASCII character will be reported as not in the set. +// This allocates a total of 32 bytes even though the upper half +// is unused to avoid bounds checks in asciiSet.contains. type asciiSet [8]uint32 // makeASCIISet creates a set of ASCII characters and reports whether all @@ -877,53 +876,153 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) { if c >= utf8.RuneSelf { return as, false } - as[c>>5] |= 1 << uint(c&31) + as[c/32] |= 1 << (c % 32) } return as, true } // contains reports whether c is inside the set. func (as *asciiSet) contains(c byte) bool { - return (as[c>>5] & (1 << uint(c&31))) != 0 + return (as[c/32] & (1 << (c % 32))) != 0 } -func makeCutsetFunc(cutset string) func(r rune) bool { - if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { - return func(r rune) bool { - return r == rune(cutset[0]) - } - } - if as, isASCII := makeASCIISet(cutset); isASCII { - return func(r rune) bool { - return r < utf8.RuneSelf && as.contains(byte(r)) +// containsRune is a simplified version of strings.ContainsRune +// to avoid importing the strings package. +// We avoid bytes.ContainsRune to avoid allocating a temporary copy of s. +func containsRune(s string, r rune) bool { + for _, c := range s { + if c == r { + return true } } - return func(r rune) bool { - for _, c := range cutset { - if c == r { - return true - } - } - return false - } + return false } // Trim returns a subslice of s by slicing off all leading and // trailing UTF-8-encoded code points contained in cutset. func Trim(s []byte, cutset string) []byte { - return TrimFunc(s, makeCutsetFunc(cutset)) + if len(s) == 0 { + // This is what we've historically done. + return nil + } + if cutset == "" { + return s + } + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimLeftASCII(trimRightASCII(s, &as), &as) + } + return trimLeftUnicode(trimRightUnicode(s, cutset), cutset) } // TrimLeft returns a subslice of s by slicing off all leading // UTF-8-encoded code points contained in cutset. func TrimLeft(s []byte, cutset string) []byte { - return TrimLeftFunc(s, makeCutsetFunc(cutset)) + if len(s) == 0 { + // This is what we've historically done. + return nil + } + if cutset == "" { + return s + } + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimLeftByte(s, cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimLeftASCII(s, &as) + } + return trimLeftUnicode(s, cutset) +} + +func trimLeftByte(s []byte, c byte) []byte { + for len(s) > 0 && s[0] == c { + s = s[1:] + } + if len(s) == 0 { + // This is what we've historically done. + return nil + } + return s +} + +func trimLeftASCII(s []byte, as *asciiSet) []byte { + for len(s) > 0 { + if !as.contains(s[0]) { + break + } + s = s[1:] + } + if len(s) == 0 { + // This is what we've historically done. + return nil + } + return s +} + +func trimLeftUnicode(s []byte, cutset string) []byte { + for len(s) > 0 { + r, n := rune(s[0]), 1 + if r >= utf8.RuneSelf { + r, n = utf8.DecodeRune(s) + } + if !containsRune(cutset, r) { + break + } + s = s[n:] + } + if len(s) == 0 { + // This is what we've historically done. + return nil + } + return s } // TrimRight returns a subslice of s by slicing off all trailing // UTF-8-encoded code points that are contained in cutset. func TrimRight(s []byte, cutset string) []byte { - return TrimRightFunc(s, makeCutsetFunc(cutset)) + if len(s) == 0 || cutset == "" { + return s + } + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimRightByte(s, cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimRightASCII(s, &as) + } + return trimRightUnicode(s, cutset) +} + +func trimRightByte(s []byte, c byte) []byte { + for len(s) > 0 && s[len(s)-1] == c { + s = s[:len(s)-1] + } + return s +} + +func trimRightASCII(s []byte, as *asciiSet) []byte { + for len(s) > 0 { + if !as.contains(s[len(s)-1]) { + break + } + s = s[:len(s)-1] + } + return s +} + +func trimRightUnicode(s []byte, cutset string) []byte { + for len(s) > 0 { + r, n := rune(s[len(s)-1]), 1 + if r >= utf8.RuneSelf { + r, n = utf8.DecodeLastRune(s) + } + if !containsRune(cutset, r) { + break + } + s = s[:len(s)-n] + } + return s } // TrimSpace returns a subslice of s by slicing off all leading and @@ -1032,7 +1131,7 @@ func ReplaceAll(s, old, new []byte) []byte { } // EqualFold reports whether s and t, interpreted as UTF-8 strings, -// are equal under Unicode case-folding, which is a more general +// are equal under simple Unicode case-folding, which is a more general // form of case-insensitivity. func EqualFold(s, t []byte) bool { for len(s) != 0 && len(t) != 0 { @@ -1174,3 +1273,52 @@ func Index(s, sep []byte) int { } return -1 } + +// Cut slices s around the first instance of sep, +// returning the text before and after sep. +// The found result reports whether sep appears in s. +// If sep does not appear in s, cut returns s, nil, false. +// +// Cut returns slices of the original slice s, not copies. +func Cut(s, sep []byte) (before, after []byte, found bool) { + if i := Index(s, sep); i >= 0 { + return s[:i], s[i+len(sep):], true + } + return s, nil, false +} + +// Clone returns a copy of b[:len(b)]. +// The result may have additional unused capacity. +// Clone(nil) returns nil. +func Clone(b []byte) []byte { + if b == nil { + return nil + } + return append([]byte{}, b...) +} + +// CutPrefix returns s without the provided leading prefix byte slice +// and reports whether it found the prefix. +// If s doesn't start with prefix, CutPrefix returns s, false. +// If prefix is the empty byte slice, CutPrefix returns s, true. +// +// CutPrefix returns slices of the original slice s, not copies. +func CutPrefix(s, prefix []byte) (after []byte, found bool) { + if !HasPrefix(s, prefix) { + return s, false + } + return s[len(prefix):], true +} + +// CutSuffix returns s without the provided ending suffix byte slice +// and reports whether it found the suffix. +// If s doesn't end with suffix, CutSuffix returns s, false. +// If suffix is the empty byte slice, CutSuffix returns s, true. +// +// CutSuffix returns slices of the original slice s, not copies. +func CutSuffix(s, suffix []byte) (before []byte, found bool) { + if !HasSuffix(s, suffix) { + return s, false + } + return s[:len(s)-len(suffix)], true +} diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go index 544ee46f908860..7263af3ed01605 100644 --- a/src/bytes/bytes_test.go +++ b/src/bytes/bytes_test.go @@ -8,12 +8,14 @@ import ( . "bytes" "fmt" "internal/testenv" + "math" "math/rand" "reflect" "strings" "testing" "unicode" "unicode/utf8" + "unsafe" ) func eq(a, b []string) bool { @@ -139,6 +141,36 @@ var indexTests = []BinOpTest{ {"abc", "c", 2}, {"abc", "x", -1}, {"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33}, + {"fofofofooofoboo", "oo", 7}, + {"fofofofofofoboo", "ob", 11}, + {"fofofofofofoboo", "boo", 12}, + {"fofofofofofoboo", "oboo", 11}, + {"fofofofofoooboo", "fooo", 8}, + {"fofofofofofoboo", "foboo", 10}, + {"fofofofofofoboo", "fofob", 8}, + {"fofofofofofofoffofoobarfoo", "foffof", 12}, + {"fofofofofoofofoffofoobarfoo", "foffof", 13}, + {"fofofofofofofoffofoobarfoo", "foffofo", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofo", 13}, + {"fofofofofoofofoffofoobarfoo", "foffofoo", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoo", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofoob", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoob", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofooba", 13}, + {"fofofofofofofoffofoobarfoo", "foffofooba", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofoobar", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoobar", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofoobarf", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoobarf", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofoobarfo", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoobarfo", 12}, + {"fofofofofoofofoffofoobarfoo", "foffofoobarfoo", 13}, + {"fofofofofofofoffofoobarfoo", "foffofoobarfoo", 12}, + {"fofofofofoofofoffofoobarfoo", "ofoffofoobarfoo", 12}, + {"fofofofofofofoffofoobarfoo", "ofoffofoobarfoo", 11}, + {"fofofofofoofofoffofoobarfoo", "fofoffofoobarfoo", 11}, + {"fofofofofofofoffofoobarfoo", "fofoffofoobarfoo", 10}, + {"fofofofofoofofoffofoobarfoo", "foobars", -1}, {"foofyfoobarfoobar", "y", 4}, {"oooooooooooooooooooooo", "r", -1}, {"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22}, @@ -723,6 +755,9 @@ var splittests = []SplitTest{ {"1 2", " ", 3, []string{"1", "2"}}, {"123", "", 2, []string{"1", "23"}}, {"123", "", 17, []string{"1", "2", "3"}}, + {"bT", "T", math.MaxInt / 4, []string{"b", ""}}, + {"\xff-\xff", "", -1, []string{"\xff", "-", "\xff"}}, + {"\xff-\xff", "-", -1, []string{"\xff", "\xff"}}, } func TestSplit(t *testing.T) { @@ -1251,7 +1286,9 @@ var trimTests = []TrimTest{ {"TrimLeft", "abba", "ab", ""}, {"TrimRight", "abba", "ab", ""}, {"TrimLeft", "abba", "a", "bba"}, + {"TrimLeft", "abba", "b", "abba"}, {"TrimRight", "abba", "a", "abb"}, + {"TrimRight", "abba", "b", "abba"}, {"Trim", "", "<>", "tag"}, {"Trim", "* listitem", " *", "listitem"}, {"Trim", `"quote"`, `"`, "quote"}, @@ -1276,24 +1313,69 @@ var trimTests = []TrimTest{ {"TrimSuffix", "aabb", "b", "aab"}, } +type TrimNilTest struct { + f string + in []byte + arg string + out []byte +} + +var trimNilTests = []TrimNilTest{ + {"Trim", nil, "", nil}, + {"Trim", []byte{}, "", nil}, + {"Trim", []byte{'a'}, "a", nil}, + {"Trim", []byte{'a', 'a'}, "a", nil}, + {"Trim", []byte{'a'}, "ab", nil}, + {"Trim", []byte{'a', 'b'}, "ab", nil}, + {"Trim", []byte("☺"), "☺", nil}, + {"TrimLeft", nil, "", nil}, + {"TrimLeft", []byte{}, "", nil}, + {"TrimLeft", []byte{'a'}, "a", nil}, + {"TrimLeft", []byte{'a', 'a'}, "a", nil}, + {"TrimLeft", []byte{'a'}, "ab", nil}, + {"TrimLeft", []byte{'a', 'b'}, "ab", nil}, + {"TrimLeft", []byte("☺"), "☺", nil}, + {"TrimRight", nil, "", nil}, + {"TrimRight", []byte{}, "", []byte{}}, + {"TrimRight", []byte{'a'}, "a", []byte{}}, + {"TrimRight", []byte{'a', 'a'}, "a", []byte{}}, + {"TrimRight", []byte{'a'}, "ab", []byte{}}, + {"TrimRight", []byte{'a', 'b'}, "ab", []byte{}}, + {"TrimRight", []byte("☺"), "☺", []byte{}}, + {"TrimPrefix", nil, "", nil}, + {"TrimPrefix", []byte{}, "", []byte{}}, + {"TrimPrefix", []byte{'a'}, "a", []byte{}}, + {"TrimPrefix", []byte("☺"), "☺", []byte{}}, + {"TrimSuffix", nil, "", nil}, + {"TrimSuffix", []byte{}, "", []byte{}}, + {"TrimSuffix", []byte{'a'}, "a", []byte{}}, + {"TrimSuffix", []byte("☺"), "☺", []byte{}}, +} + func TestTrim(t *testing.T) { - for _, tc := range trimTests { - name := tc.f - var f func([]byte, string) []byte - var fb func([]byte, []byte) []byte + toFn := func(name string) (func([]byte, string) []byte, func([]byte, []byte) []byte) { switch name { case "Trim": - f = Trim + return Trim, nil case "TrimLeft": - f = TrimLeft + return TrimLeft, nil case "TrimRight": - f = TrimRight + return TrimRight, nil case "TrimPrefix": - fb = TrimPrefix + return nil, TrimPrefix case "TrimSuffix": - fb = TrimSuffix + return nil, TrimSuffix default: t.Errorf("Undefined trim function %s", name) + return nil, nil + } + } + + for _, tc := range trimTests { + name := tc.f + f, fb := toFn(name) + if f == nil && fb == nil { + continue } var actual string if f != nil { @@ -1305,6 +1387,36 @@ func TestTrim(t *testing.T) { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out) } } + + for _, tc := range trimNilTests { + name := tc.f + f, fb := toFn(name) + if f == nil && fb == nil { + continue + } + var actual []byte + if f != nil { + actual = f(tc.in, tc.arg) + } else { + actual = fb(tc.in, []byte(tc.arg)) + } + report := func(s []byte) string { + if s == nil { + return "nil" + } else { + return fmt.Sprintf("%q", s) + } + } + if len(actual) != 0 { + t.Errorf("%s(%s, %q) returned non-empty value", name, report(tc.in), tc.arg) + } else { + actualNil := actual == nil + outNil := tc.out == nil + if actualNil != outNil { + t.Errorf("%s(%s, %q) got nil %t; want nil %t", name, report(tc.in), tc.arg, actualNil, outNil) + } + } + } } type predicate struct { @@ -1565,6 +1677,71 @@ func TestEqualFold(t *testing.T) { } } +var cutTests = []struct { + s, sep string + before, after string + found bool +}{ + {"abc", "b", "a", "c", true}, + {"abc", "a", "", "bc", true}, + {"abc", "c", "ab", "", true}, + {"abc", "abc", "", "", true}, + {"abc", "", "", "abc", true}, + {"abc", "d", "abc", "", false}, + {"", "d", "", "", false}, + {"", "", "", "", true}, +} + +func TestCut(t *testing.T) { + for _, tt := range cutTests { + if before, after, found := Cut([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found { + t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found) + } + } +} + +var cutPrefixTests = []struct { + s, sep string + after string + found bool +}{ + {"abc", "a", "bc", true}, + {"abc", "abc", "", true}, + {"abc", "", "abc", true}, + {"abc", "d", "abc", false}, + {"", "d", "", false}, + {"", "", "", true}, +} + +func TestCutPrefix(t *testing.T) { + for _, tt := range cutPrefixTests { + if after, found := CutPrefix([]byte(tt.s), []byte(tt.sep)); string(after) != tt.after || found != tt.found { + t.Errorf("CutPrefix(%q, %q) = %q, %v, want %q, %v", tt.s, tt.sep, after, found, tt.after, tt.found) + } + } +} + +var cutSuffixTests = []struct { + s, sep string + after string + found bool +}{ + {"abc", "bc", "a", true}, + {"abc", "abc", "", true}, + {"abc", "", "abc", true}, + {"abc", "d", "abc", false}, + {"", "d", "", false}, + {"", "", "", true}, +} + +func TestCutSuffix(t *testing.T) { + for _, tt := range cutSuffixTests { + if after, found := CutSuffix([]byte(tt.s), []byte(tt.sep)); string(after) != tt.after || found != tt.found { + t.Errorf("CutSuffix(%q, %q) = %q, %v, want %q, %v", tt.s, tt.sep, after, found, tt.after, tt.found) + } + } +} + func TestBufferGrowNegative(t *testing.T) { defer func() { if err := recover(); err == nil { @@ -1963,6 +2140,13 @@ func BenchmarkTrimASCII(b *testing.B) { } } +func BenchmarkTrimByte(b *testing.B) { + x := []byte(" the quick brown fox ") + for i := 0; i < b.N; i++ { + Trim(x, " ") + } +} + func BenchmarkIndexPeriodic(b *testing.B) { key := []byte{1, 1} for _, skip := range [...]int{2, 4, 8, 16, 32, 64} { @@ -1977,3 +2161,33 @@ func BenchmarkIndexPeriodic(b *testing.B) { }) } } + +func TestClone(t *testing.T) { + var cloneTests = [][]byte{ + []byte(nil), + []byte{}, + Clone([]byte{}), + []byte(strings.Repeat("a", 42))[:0], + []byte(strings.Repeat("a", 42))[:0:0], + []byte("short"), + []byte(strings.Repeat("a", 42)), + } + for _, input := range cloneTests { + clone := Clone(input) + if !Equal(clone, input) { + t.Errorf("Clone(%q) = %q; want %q", input, clone, input) + } + + if input == nil && clone != nil { + t.Errorf("Clone(%#v) return value should be equal to nil slice.", input) + } + + if input != nil && clone == nil { + t.Errorf("Clone(%#v) return value should not be equal to nil slice.", input) + } + + if cap(input) != 0 && unsafe.SliceData(input) == unsafe.SliceData(clone) { + t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input) + } + } +} diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go index ae93202b5706e1..54a7aa6ae6c049 100644 --- a/src/bytes/example_test.go +++ b/src/bytes/example_test.go @@ -37,6 +37,16 @@ func ExampleBuffer_Bytes() { // Output: hello world } +func ExampleBuffer_Cap() { + buf1 := bytes.NewBuffer(make([]byte, 10)) + buf2 := bytes.NewBuffer(make([]byte, 0, 10)) + fmt.Println(buf1.Cap()) + fmt.Println(buf2.Cap()) + // Output: + // 10 + // 10 +} + func ExampleBuffer_Grow() { var b bytes.Buffer b.Grow(64) @@ -54,6 +64,52 @@ func ExampleBuffer_Len() { // Output: 5 } +func ExampleBuffer_Next() { + var b bytes.Buffer + b.Grow(64) + b.Write([]byte("abcde")) + fmt.Printf("%s\n", string(b.Next(2))) + fmt.Printf("%s\n", string(b.Next(2))) + fmt.Printf("%s", string(b.Next(2))) + // Output: + // ab + // cd + // e +} + +func ExampleBuffer_Read() { + var b bytes.Buffer + b.Grow(64) + b.Write([]byte("abcde")) + rdbuf := make([]byte, 1) + n, err := b.Read(rdbuf) + if err != nil { + panic(err) + } + fmt.Println(n) + fmt.Println(b.String()) + fmt.Println(string(rdbuf)) + // Output + // 1 + // bcde + // a +} + +func ExampleBuffer_ReadByte() { + var b bytes.Buffer + b.Grow(64) + b.Write([]byte("abcde")) + c, err := b.ReadByte() + if err != nil { + panic(err) + } + fmt.Println(c) + fmt.Println(b.String()) + // Output + // 97 + // bcde +} + func ExampleCompare() { // Interpret Compare's result by comparing it to zero. var a, b []byte @@ -92,36 +148,6 @@ func ExampleCompare_search() { } } -func ExampleTrimSuffix() { - var b = []byte("Hello, goodbye, etc!") - b = bytes.TrimSuffix(b, []byte("goodbye, etc!")) - b = bytes.TrimSuffix(b, []byte("gopher")) - b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...) - os.Stdout.Write(b) - // Output: Hello, world! -} - -func ExampleTrimPrefix() { - var b = []byte("Goodbye,, world!") - b = bytes.TrimPrefix(b, []byte("Goodbye,")) - b = bytes.TrimPrefix(b, []byte("See ya,")) - fmt.Printf("Hello%s", b) - // Output: Hello, world! -} - -func ExampleFields() { - fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz "))) - // Output: Fields are: ["foo" "bar" "baz"] -} - -func ExampleFieldsFunc() { - f := func(c rune) bool { - return !unicode.IsLetter(c) && !unicode.IsNumber(c) - } - fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f)) - // Output: Fields are: ["foo1" "bar2" "baz3"] -} - func ExampleContains() { fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo"))) fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar"))) @@ -168,6 +194,22 @@ func ExampleCount() { // 5 } +func ExampleCut() { + show := func(s, sep string) { + before, after, found := bytes.Cut([]byte(s), []byte(sep)) + fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found) + } + show("Gopher", "Go") + show("Gopher", "ph") + show("Gopher", "er") + show("Gopher", "Badger") + // Output: + // Cut("Gopher", "Go") = "", "pher", true + // Cut("Gopher", "ph") = "Go", "er", true + // Cut("Gopher", "er") = "Goph", "", true + // Cut("Gopher", "Badger") = "Gopher", "", false +} + func ExampleEqual() { fmt.Println(bytes.Equal([]byte("Go"), []byte("Go"))) fmt.Println(bytes.Equal([]byte("Go"), []byte("C++"))) @@ -181,6 +223,19 @@ func ExampleEqualFold() { // Output: true } +func ExampleFields() { + fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz "))) + // Output: Fields are: ["foo" "bar" "baz"] +} + +func ExampleFieldsFunc() { + f := func(c rune) bool { + return !unicode.IsLetter(c) && !unicode.IsNumber(c) + } + fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f)) + // Output: Fields are: ["foo1" "bar2" "baz3"] +} + func ExampleHasPrefix() { fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go"))) fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C"))) @@ -246,6 +301,12 @@ func ExampleIndexRune() { // -1 } +func ExampleJoin() { + s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")} + fmt.Printf("%s", bytes.Join(s, []byte(", "))) + // Output: foo, bar, baz +} + func ExampleLastIndex() { fmt.Println(bytes.Index([]byte("go gopher"), []byte("go"))) fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go"))) @@ -286,10 +347,12 @@ func ExampleLastIndexFunc() { // -1 } -func ExampleJoin() { - s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")} - fmt.Printf("%s", bytes.Join(s, []byte(", "))) - // Output: foo, bar, baz +func ExampleReader_Len() { + fmt.Println(bytes.NewReader([]byte("Hi!")).Len()) + fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len()) + // Output: + // 3 + // 16 } func ExampleRepeat() { @@ -399,20 +462,6 @@ func ExampleTrimFunc() { // go-gopher! } -func ExampleMap() { - rot13 := func(r rune) rune { - switch { - case r >= 'A' && r <= 'Z': - return 'A' + (r-'A'+13)%26 - case r >= 'a' && r <= 'z': - return 'a' + (r-'a'+13)%26 - } - return r - } - fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher..."))) - // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure... -} - func ExampleTrimLeft() { fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789"))) // Output: @@ -429,11 +478,28 @@ func ExampleTrimLeftFunc() { // go-gopher!567 } +func ExampleTrimPrefix() { + var b = []byte("Goodbye,, world!") + b = bytes.TrimPrefix(b, []byte("Goodbye,")) + b = bytes.TrimPrefix(b, []byte("See ya,")) + fmt.Printf("Hello%s", b) + // Output: Hello, world! +} + func ExampleTrimSpace() { fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n"))) // Output: a lone gopher } +func ExampleTrimSuffix() { + var b = []byte("Hello, goodbye, etc!") + b = bytes.TrimSuffix(b, []byte("goodbye, etc!")) + b = bytes.TrimSuffix(b, []byte("gopher")) + b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...) + os.Stdout.Write(b) + // Output: Hello, world! +} + func ExampleTrimRight() { fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789"))) // Output: @@ -450,21 +516,6 @@ func ExampleTrimRightFunc() { // 1234go-gopher! } -func ExampleToUpper() { - fmt.Printf("%s", bytes.ToUpper([]byte("Gopher"))) - // Output: GOPHER -} - -func ExampleToUpperSpecial() { - str := []byte("ahoj vývojári golang") - totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str) - fmt.Println("Original : " + string(str)) - fmt.Println("ToUpper : " + string(totitle)) - // Output: - // Original : ahoj vývojári golang - // ToUpper : AHOJ VÝVOJÁRİ GOLANG -} - func ExampleToLower() { fmt.Printf("%s", bytes.ToLower([]byte("Gopher"))) // Output: gopher @@ -480,10 +531,17 @@ func ExampleToLowerSpecial() { // ToLower : ahoj vývojári golang } -func ExampleReader_Len() { - fmt.Println(bytes.NewReader([]byte("Hi!")).Len()) - fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len()) +func ExampleToUpper() { + fmt.Printf("%s", bytes.ToUpper([]byte("Gopher"))) + // Output: GOPHER +} + +func ExampleToUpperSpecial() { + str := []byte("ahoj vývojári golang") + totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str) + fmt.Println("Original : " + string(str)) + fmt.Println("ToUpper : " + string(totitle)) // Output: - // 3 - // 16 + // Original : ahoj vývojári golang + // ToUpper : AHOJ VÝVOJÁRİ GOLANG } diff --git a/src/bytes/reader.go b/src/bytes/reader.go index 5946cf9780b0e1..81c22aa0295d6d 100644 --- a/src/bytes/reader.go +++ b/src/bytes/reader.go @@ -32,8 +32,7 @@ func (r *Reader) Len() int { // Size returns the original length of the underlying byte slice. // Size is the number of bytes available for reading via ReadAt. -// The returned value is always the same and is not affected by calls -// to any other method. +// The result is unaffected by any method calls except Reset. func (r *Reader) Size() int64 { return int64(len(r.s)) } // Read implements the io.Reader interface. diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go index 8baac5046cbeec..9119c944ace478 100644 --- a/src/bytes/reader_test.go +++ b/src/bytes/reader_test.go @@ -76,7 +76,7 @@ func TestReaderAt(t *testing.T) { off int64 n int want string - wanterr interface{} + wanterr any }{ {0, 10, "0123456789", nil}, {1, 10, "123456789", io.EOF}, diff --git a/src/clean.bat b/src/clean.bat index c957353d0f10f5..6688b41e5e2c9d 100644 --- a/src/clean.bat +++ b/src/clean.bat @@ -10,7 +10,7 @@ set GOBUILDFAIL=0 go tool dist env -w -p >env.bat if errorlevel 1 goto fail -call env.bat +call .\env.bat del env.bat echo. diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go index 018802940b24f9..6e005a8fac96d6 100644 --- a/src/cmd/addr2line/main.go +++ b/src/cmd/addr2line/main.go @@ -6,6 +6,7 @@ // just enough to support pprof. // // Usage: +// // go tool addr2line binary // // Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix, diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index b07a238d679a16..e6bf62df1fb5cb 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.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. -// Binary api computes the exported API of a set of Go packages. +// Api computes the exported API of a set of Go packages. package main import ( @@ -16,14 +16,15 @@ import ( "go/parser" "go/token" "go/types" - exec "internal/execabs" "io" "log" "os" + "os/exec" "path/filepath" "regexp" "runtime" "sort" + "strconv" "strings" "sync" ) @@ -33,21 +34,24 @@ func goCmd() string { if runtime.GOOS == "windows" { exeSuffix = ".exe" } - path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) - if _, err := os.Stat(path); err == nil { - return path + if goroot := build.Default.GOROOT; goroot != "" { + path := filepath.Join(goroot, "bin", "go"+exeSuffix) + if _, err := os.Stat(path); err == nil { + return path + } } return "go" } // Flags var ( - checkFile = flag.String("c", "", "optional comma-separated filename(s) to check API against") - allowNew = flag.Bool("allow_new", true, "allow API additions") - exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool") - nextFile = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.") - verbose = flag.Bool("v", false, "verbose debugging") - forceCtx = flag.String("contexts", "", "optional comma-separated list of -[-cgo] to override default contexts.") + checkFiles = flag.String("c", "", "optional comma-separated filename(s) to check API against") + requireApproval = flag.String("approval", "", "require approvals in comma-separated list of `files`") + allowNew = flag.Bool("allow_new", true, "allow API additions") + exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool") + nextFiles = flag.String("next", "", "comma-separated list of `files` for upcoming API features for the next release. These files can be lazily maintained. They only affects the delta warnings from the -c file printed on success.") + verbose = flag.Bool("v", false, "verbose debugging") + forceCtx = flag.String("contexts", "", "optional comma-separated list of -[-cgo] to override default contexts.") ) // contexts are the default contexts which are scanned, unless @@ -125,10 +129,14 @@ var internalPkg = regexp.MustCompile(`(^|/)internal($|/)`) func main() { flag.Parse() + if build.Default.GOROOT == "" { + log.Fatalf("GOROOT not found. (If binary was built with -trimpath, $GOROOT must be set.)") + } + if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") { - if *nextFile != "" { - fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFile) - *nextFile = "" + if *nextFiles != "" { + fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFiles) + *nextFiles = "" } } @@ -201,7 +209,7 @@ func main() { bw := bufio.NewWriter(os.Stdout) defer bw.Flush() - if *checkFile == "" { + if *checkFiles == "" { sort.Strings(features) for _, f := range features { fmt.Fprintln(bw, f) @@ -210,10 +218,15 @@ func main() { } var required []string - for _, file := range strings.Split(*checkFile, ",") { + for _, file := range strings.Split(*checkFiles, ",") { required = append(required, fileFeatures(file)...) } - optional := fileFeatures(*nextFile) + var optional []string + if *nextFiles != "" { + for _, file := range strings.Split(*nextFiles, ",") { + optional = append(optional, fileFeatures(file)...) + } + } exception := fileFeatures(*exceptFile) fail = !compareAPI(bw, features, required, optional, exception, *allowNew) } @@ -340,6 +353,13 @@ func fileFeatures(filename string) []string { if filename == "" { return nil } + needApproval := false + for _, name := range strings.Split(*requireApproval, ",") { + if filename == name { + needApproval = true + break + } + } bs, err := os.ReadFile(filename) if err != nil { log.Fatalf("Error reading file %s: %v", filename, err) @@ -348,11 +368,23 @@ func fileFeatures(filename string) []string { s = aliasReplacer.Replace(s) lines := strings.Split(s, "\n") var nonblank []string - for _, line := range lines { + for i, line := range lines { line = strings.TrimSpace(line) - if line != "" && !strings.HasPrefix(line, "#") { - nonblank = append(nonblank, line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + if needApproval { + feature, approval, ok := strings.Cut(line, "#") + if !ok { + log.Fatalf("%s:%d: missing proposal approval\n", filename, i+1) + } + _, err := strconv.Atoi(approval) + if err != nil { + log.Fatalf("%s:%d: malformed proposal approval #%s\n", filename, i+1, approval) + } + line = strings.TrimSpace(feature) } + nonblank = append(nonblank, line) } return nonblank } @@ -459,8 +491,11 @@ type listImports struct { var listCache sync.Map // map[string]listImports, keyed by contextName -// listSem is a semaphore restricting concurrent invocations of 'go list'. -var listSem = make(chan semToken, runtime.GOMAXPROCS(0)) +// listSem is a semaphore restricting concurrent invocations of 'go list'. 'go +// list' has its own internal concurrency, so we use a hard-coded constant (to +// allow the I/O-intensive phases of 'go list' to overlap) instead of scaling +// all the way up to GOMAXPROCS. +var listSem = make(chan semToken, 2) type semToken struct{} @@ -653,10 +688,15 @@ func (w *Walker) ImportFrom(fromPath, fromDir string, mode types.ImportMode) (*t } // Type-check package files. + var sizes types.Sizes + if w.context != nil { + sizes = types.SizesFor(w.context.Compiler, w.context.GOARCH) + } conf := types.Config{ IgnoreFuncBodies: true, FakeImportC: true, Importer: w, + Sizes: sizes, } pkg, err = conf.Check(name, fset, files, nil) if err != nil { @@ -701,6 +741,36 @@ func sortedMethodNames(typ *types.Interface) []string { return list } +// sortedEmbeddeds returns constraint types embedded in an +// interface. It does not include embedded interface types or methods. +func (w *Walker) sortedEmbeddeds(typ *types.Interface) []string { + n := typ.NumEmbeddeds() + list := make([]string, 0, n) + for i := 0; i < n; i++ { + emb := typ.EmbeddedType(i) + switch emb := emb.(type) { + case *types.Interface: + list = append(list, w.sortedEmbeddeds(emb)...) + case *types.Union: + var buf bytes.Buffer + nu := emb.Len() + for i := 0; i < nu; i++ { + if i > 0 { + buf.WriteString(" | ") + } + term := emb.Term(i) + if term.Tilde() { + buf.WriteByte('~') + } + w.writeType(&buf, term.Type()) + } + list = append(list, buf.String()) + } + } + sort.Strings(list) + return list +} + func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { switch typ := typ.(type) { case *types.Basic: @@ -758,9 +828,16 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { case *types.Interface: buf.WriteString("interface{") - if typ.NumMethods() > 0 { + if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 { buf.WriteByte(' ') + } + if typ.NumMethods() > 0 { buf.WriteString(strings.Join(sortedMethodNames(typ), ", ")) + } + if typ.NumEmbeddeds() > 0 { + buf.WriteString(strings.Join(w.sortedEmbeddeds(typ), ", ")) + } + if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 { buf.WriteByte(' ') } buf.WriteString("}") @@ -795,12 +872,19 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { } buf.WriteString(typ.Obj().Name()) + case *types.TypeParam: + // Type parameter names may change, so use a placeholder instead. + fmt.Fprintf(buf, "$%d", typ.Index()) + default: panic(fmt.Sprintf("unknown type %T", typ)) } } func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) { + if tparams := sig.TypeParams(); tparams != nil { + w.writeTypeParams(buf, tparams, true) + } w.writeParams(buf, sig.Params(), sig.Variadic()) switch res := sig.Results(); res.Len() { case 0: @@ -814,6 +898,23 @@ func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) { } } +func (w *Walker) writeTypeParams(buf *bytes.Buffer, tparams *types.TypeParamList, withConstraints bool) { + buf.WriteByte('[') + c := tparams.Len() + for i := 0; i < c; i++ { + if i > 0 { + buf.WriteString(", ") + } + tp := tparams.At(i) + w.writeType(buf, tp) + if withConstraints { + buf.WriteByte(' ') + w.writeType(buf, tp.Constraint()) + } + } + buf.WriteByte(']') +} + func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) { buf.WriteByte('(') for i, n := 0, t.Len(); i < n; i++ { @@ -867,6 +968,12 @@ func (w *Walker) emitObj(obj types.Object) { func (w *Walker) emitType(obj *types.TypeName) { name := obj.Name() + if tparams := obj.Type().(*types.Named).TypeParams(); tparams != nil { + var buf bytes.Buffer + buf.WriteString(name) + w.writeTypeParams(&buf, tparams, true) + name = buf.String() + } typ := obj.Type() if obj.IsAlias() { w.emitf("type %s = %s", name, w.typeString(typ)) @@ -990,10 +1097,16 @@ func (w *Walker) emitMethod(m *types.Selection) { log.Fatalf("exported method with unexported receiver base type: %s", m) } } - w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig)) + tps := "" + if rtp := sig.RecvTypeParams(); rtp != nil { + var buf bytes.Buffer + w.writeTypeParams(&buf, rtp, false) + tps = buf.String() + } + w.emitf("method (%s%s) %s%s", w.typeString(recv), tps, m.Obj().Name(), w.signatureString(sig)) } -func (w *Walker) emitf(format string, args ...interface{}) { +func (w *Walker) emitf(format string, args ...any) { f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...) if strings.Contains(f, "\n") { panic("feature contains newlines: " + f) diff --git a/src/cmd/api/goapi_boring_test.go b/src/cmd/api/goapi_boring_test.go new file mode 100644 index 00000000000000..f0e3575637c62a --- /dev/null +++ b/src/cmd/api/goapi_boring_test.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build boringcrypto + +package main + +import ( + "fmt" + "os" +) + +func init() { + fmt.Printf("SKIP with boringcrypto enabled\n") + os.Exit(0) +} diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index 16e0058e5eac1c..e905e65b24aa92 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -5,10 +5,10 @@ package main import ( - "bytes" "flag" "fmt" "go/build" + "internal/testenv" "os" "path/filepath" "sort" @@ -22,6 +22,7 @@ func TestMain(m *testing.M) { for _, c := range contexts { c.Compiler = build.Default.Compiler } + build.Default.GOROOT = testenv.GOROOT(nil) // Warm up the import cache in parallel. var wg sync.WaitGroup @@ -150,7 +151,7 @@ func TestCompareAPI(t *testing.T) { }, } for _, tt := range tests { - buf := new(bytes.Buffer) + buf := new(strings.Builder) gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception, true) if gotok != tt.ok { t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok) diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go index 81979de191abc2..1ae629a0322304 100644 --- a/src/cmd/api/run.go +++ b/src/cmd/api/run.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // The run program is invoked via the dist tool. // To invoke manually: go tool dist test -run api --no-rebuild @@ -12,13 +11,14 @@ package main import ( "errors" "fmt" - exec "internal/execabs" "internal/goversion" "io/fs" "log" "os" + "os/exec" "path/filepath" "runtime" + "strconv" "strings" ) @@ -42,51 +42,65 @@ func main() { if goroot == "" { log.Fatal("No $GOROOT set.") } - - apiDir := filepath.Join(goroot, "api") - out, err := exec.Command(goCmd(), "tool", "api", - "-c", findAPIDirFiles(apiDir), - allowNew(apiDir), - "-next", filepath.Join(apiDir, "next.txt"), - "-except", filepath.Join(apiDir, "except.txt")).CombinedOutput() - if err != nil { - log.Fatalf("Error running API checker: %v\n%s", err, out) + if err := os.Chdir(filepath.Join(goroot, "api")); err != nil { + log.Fatal(err) } - fmt.Print(string(out)) -} -// findAPIDirFiles returns a comma-separated list of Go API files -// (go1.txt, go1.1.txt, etc.) located in apiDir. -func findAPIDirFiles(apiDir string) string { - dir, err := os.Open(apiDir) + files, err := filepath.Glob("go1*.txt") if err != nil { log.Fatal(err) } - defer dir.Close() - fs, err := dir.Readdirnames(-1) + next, err := filepath.Glob(filepath.Join("next", "*.txt")) if err != nil { log.Fatal(err) } - var apiFiles []string - for _, fn := range fs { - if strings.HasPrefix(fn, "go1") { - apiFiles = append(apiFiles, filepath.Join(apiDir, fn)) + cmd := exec.Command(goCmd(), "tool", "api", + "-c", strings.Join(files, ","), + "-approval", strings.Join(append(approvalNeeded(files), next...), ","), + allowNew(), + "-next", strings.Join(next, ","), + "-except", "except.txt", + ) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("Error running API checker: %v\n%s", err, out) + } + fmt.Print(string(out)) +} + +func approvalNeeded(files []string) []string { + var out []string + for _, f := range files { + name := filepath.Base(f) + if name == "go1.txt" { + continue + } + minor := strings.TrimSuffix(strings.TrimPrefix(name, "go1."), ".txt") + n, err := strconv.Atoi(minor) + if err != nil { + log.Fatalf("unexpected api file: %v", f) + } + if n >= 19 { // approvals started being tracked in Go 1.19 + out = append(out, f) } } - return strings.Join(apiFiles, ",") + return out } // allowNew returns the -allow_new flag to use for the 'go tool api' invocation. -func allowNew(apiDir string) string { +func allowNew() string { + // Experiment for Go 1.19: always require api file updates. + return "-allow_new=false" + // Verify that the api/go1.n.txt for previous Go version exists. // It definitely should, otherwise it's a signal that the logic below may be outdated. - if _, err := os.Stat(filepath.Join(apiDir, fmt.Sprintf("go1.%d.txt", goversion.Version-1))); err != nil { + if _, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version-1)); err != nil { log.Fatalln("Problem with api file for previous release:", err) } // See whether the api/go1.n.txt for this Go version has been created. // (As of April 2021, it gets created during the release of the first Beta.) - _, err := os.Stat(filepath.Join(apiDir, fmt.Sprintf("go1.%d.txt", goversion.Version))) + _, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version)) if errors.Is(err, fs.ErrNotExist) { // It doesn't exist, so we're in development or before Beta 1. // At this stage, unmentioned API additions are deemed okay. diff --git a/src/cmd/api/testdata/src/issue21181/p/p_generic.go b/src/cmd/api/testdata/src/issue21181/p/p_generic.go index 4d75809676200c..ad6df20187e9c0 100644 --- a/src/cmd/api/testdata/src/issue21181/p/p_generic.go +++ b/src/cmd/api/testdata/src/issue21181/p/p_generic.go @@ -1,3 +1,4 @@ +//go:build !amd64 // +build !amd64 package p diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index 65181b248a3038..81826d768b4f8c 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -197,7 +197,7 @@ var m map[string]int var chanVar chan int -var ifaceVar interface{} = 5 +var ifaceVar any = 5 var assertVar = ifaceVar.(int) diff --git a/src/cmd/api/testdata/src/pkg/p4/golden.txt b/src/cmd/api/testdata/src/pkg/p4/golden.txt new file mode 100644 index 00000000000000..7997ab447123e8 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p4/golden.txt @@ -0,0 +1,5 @@ +pkg p4, func NewPair[$0 interface{ M }, $1 interface{ ~int }]($0, $1) Pair +pkg p4, method (Pair[$0, $1]) Second() $1 +pkg p4, method (Pair[$0, $1]) First() $0 +pkg p4, type Pair[$0 interface{ M }, $1 interface{ ~int }] struct +pkg p4, func Clone[$0 interface{ ~[]$1 }, $1 interface{}]($0) $0 diff --git a/src/cmd/api/testdata/src/pkg/p4/p4.go b/src/cmd/api/testdata/src/pkg/p4/p4.go new file mode 100644 index 00000000000000..1f90e779dd4b52 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p4/p4.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p4 + +type Pair[T1 interface{ M() }, T2 ~int] struct { + f1 T1 + f2 T2 +} + +func NewPair[T1 interface{ M() }, T2 ~int](v1 T1, v2 T2) Pair[T1, T2] { + return Pair[T1, T2]{f1: v1, f2: v2} +} + +func (p Pair[X1, _]) First() X1 { + return p.f1 +} + +func (p Pair[_, X2]) Second() X2 { + return p.f2 +} + +func Clone[S ~[]T, T any](s S) S { + return append(S(nil), s...) +} diff --git a/src/cmd/asm/doc.go b/src/cmd/asm/doc.go index 4a0c785aad4192..bb9166b8da981d 100644 --- a/src/cmd/asm/doc.go +++ b/src/cmd/asm/doc.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. /* -Asm, typically invoked as ``go tool asm'', assembles the source file into an object +Asm, typically invoked as “go tool asm”, assembles the source file into an object file named for the basename of the argument source file with a .o suffix. The object file can then be combined with other objects into a package archive. -Command Line +# Command Line Usage: @@ -37,6 +37,8 @@ Flags: Write symbol ABI information to output file. Don't assemble. -o file Write output to file. The default is foo.o for /a/b/c/foo.s. + -p pkgpath + Set expected package import to pkgpath. -shared Generate code that can be linked into a shared library. -spectre list diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index 026d8abf81305f..e9c15a1218bff9 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -9,6 +9,7 @@ import ( "cmd/internal/obj" "cmd/internal/obj/arm" "cmd/internal/obj/arm64" + "cmd/internal/obj/loong64" "cmd/internal/obj/mips" "cmd/internal/obj/ppc64" "cmd/internal/obj/riscv" @@ -50,7 +51,7 @@ func nilRegisterNumber(name string, n int16) (int16, bool) { // Set configures the architecture specified by GOARCH and returns its representation. // It returns nil if GOARCH is not recognized. -func Set(GOARCH string) *Arch { +func Set(GOARCH string, shared bool) *Arch { switch GOARCH { case "386": return archX86(&x86.Link386) @@ -60,6 +61,8 @@ func Set(GOARCH string) *Arch { return archArm() case "arm64": return archArm64() + case "loong64": + return archLoong64(&loong64.Linkloong64) case "mips": return archMips(&mips.Linkmips) case "mipsle": @@ -73,7 +76,7 @@ func Set(GOARCH string) *Arch { case "ppc64le": return archPPC64(&ppc64.Linkppc64le) case "riscv64": - return archRISCV64() + return archRISCV64(shared) case "s390x": return archS390x() case "wasm": @@ -178,6 +181,10 @@ func archX86(linkArch *obj.LinkArch) *Arch { instructions["PSLLDQ"] = x86.APSLLO instructions["PSRLDQ"] = x86.APSRLO instructions["PADDD"] = x86.APADDL + // Spellings originally used in CL 97235. + instructions["MOVBELL"] = x86.AMOVBEL + instructions["MOVBEQQ"] = x86.AMOVBEQ + instructions["MOVBEWW"] = x86.AMOVBEW return &Arch{ LinkArch: linkArch, @@ -274,46 +281,7 @@ func archArm64() *Arch { } register["LR"] = arm64.REGLINK - register["DAIFSet"] = arm64.REG_DAIFSet - register["DAIFClr"] = arm64.REG_DAIFClr - register["PLDL1KEEP"] = arm64.REG_PLDL1KEEP - register["PLDL1STRM"] = arm64.REG_PLDL1STRM - register["PLDL2KEEP"] = arm64.REG_PLDL2KEEP - register["PLDL2STRM"] = arm64.REG_PLDL2STRM - register["PLDL3KEEP"] = arm64.REG_PLDL3KEEP - register["PLDL3STRM"] = arm64.REG_PLDL3STRM - register["PLIL1KEEP"] = arm64.REG_PLIL1KEEP - register["PLIL1STRM"] = arm64.REG_PLIL1STRM - register["PLIL2KEEP"] = arm64.REG_PLIL2KEEP - register["PLIL2STRM"] = arm64.REG_PLIL2STRM - register["PLIL3KEEP"] = arm64.REG_PLIL3KEEP - register["PLIL3STRM"] = arm64.REG_PLIL3STRM - register["PSTL1KEEP"] = arm64.REG_PSTL1KEEP - register["PSTL1STRM"] = arm64.REG_PSTL1STRM - register["PSTL2KEEP"] = arm64.REG_PSTL2KEEP - register["PSTL2STRM"] = arm64.REG_PSTL2STRM - register["PSTL3KEEP"] = arm64.REG_PSTL3KEEP - register["PSTL3STRM"] = arm64.REG_PSTL3STRM - - // Conditional operators, like EQ, NE, etc. - register["EQ"] = arm64.COND_EQ - register["NE"] = arm64.COND_NE - register["HS"] = arm64.COND_HS - register["CS"] = arm64.COND_HS - register["LO"] = arm64.COND_LO - register["CC"] = arm64.COND_LO - register["MI"] = arm64.COND_MI - register["PL"] = arm64.COND_PL - register["VS"] = arm64.COND_VS - register["VC"] = arm64.COND_VC - register["HI"] = arm64.COND_HI - register["LS"] = arm64.COND_LS - register["GE"] = arm64.COND_GE - register["LT"] = arm64.COND_LT - register["GT"] = arm64.COND_GT - register["LE"] = arm64.COND_LE - register["AL"] = arm64.COND_AL - register["NV"] = arm64.COND_NV + // Pseudo-registers. register["SB"] = RSB register["FP"] = RFP @@ -368,12 +336,18 @@ func archPPC64(linkArch *obj.LinkArch) *Arch { for i := ppc64.REG_VS0; i <= ppc64.REG_VS63; i++ { register[obj.Rconv(i)] = int16(i) } + for i := ppc64.REG_A0; i <= ppc64.REG_A7; i++ { + register[obj.Rconv(i)] = int16(i) + } for i := ppc64.REG_CR0; i <= ppc64.REG_CR7; i++ { register[obj.Rconv(i)] = int16(i) } for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ { register[obj.Rconv(i)] = int16(i) } + for i := ppc64.REG_CR0LT; i <= ppc64.REG_CR7SO; i++ { + register[obj.Rconv(i)] = int16(i) + } register["CR"] = ppc64.REG_CR register["XER"] = ppc64.REG_XER register["LR"] = ppc64.REG_LR @@ -534,12 +508,71 @@ func archMips64(linkArch *obj.LinkArch) *Arch { } } -func archRISCV64() *Arch { +func archLoong64(linkArch *obj.LinkArch) *Arch { + register := make(map[string]int16) + // Create maps for easy lookup of instruction names etc. + // Note that there is no list of names as there is for x86. + for i := loong64.REG_R0; i <= loong64.REG_R31; i++ { + register[obj.Rconv(i)] = int16(i) + } + for i := loong64.REG_F0; i <= loong64.REG_F31; i++ { + register[obj.Rconv(i)] = int16(i) + } + for i := loong64.REG_FCSR0; i <= loong64.REG_FCSR31; i++ { + register[obj.Rconv(i)] = int16(i) + } + for i := loong64.REG_FCC0; i <= loong64.REG_FCC31; i++ { + register[obj.Rconv(i)] = int16(i) + } + // Pseudo-registers. + register["SB"] = RSB + register["FP"] = RFP + register["PC"] = RPC + // Avoid unintentionally clobbering g using R22. + delete(register, "R22") + register["g"] = loong64.REG_R22 + register["RSB"] = loong64.REG_R31 + registerPrefix := map[string]bool{ + "F": true, + "FCSR": true, + "FCC": true, + "R": true, + } + + instructions := make(map[string]obj.As) + for i, s := range obj.Anames { + instructions[s] = obj.As(i) + } + for i, s := range loong64.Anames { + if obj.As(i) >= obj.A_ARCHSPECIFIC { + instructions[s] = obj.As(i) + obj.ABaseLoong64 + } + } + // Annoying alias. + instructions["JAL"] = loong64.AJAL + + return &Arch{ + LinkArch: linkArch, + Instructions: instructions, + Register: register, + RegisterPrefix: registerPrefix, + RegisterNumber: loong64RegisterNumber, + IsJump: jumpLoong64, + } +} + +func archRISCV64(shared bool) *Arch { register := make(map[string]int16) // Standard register names. for i := riscv.REG_X0; i <= riscv.REG_X31; i++ { - if i == riscv.REG_G { + // Disallow X3 in shared mode, as this will likely be used as the + // GP register, which could result in problems in non-Go code, + // including signal handlers. + if shared && i == riscv.REG_GP { + continue + } + if i == riscv.REG_TP || i == riscv.REG_G { continue } name := fmt.Sprintf("X%d", i-riscv.REG_X0) diff --git a/src/cmd/asm/internal/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go index 40d828a1fea748..e426814aab76e7 100644 --- a/src/cmd/asm/internal/arch/arm64.go +++ b/src/cmd/asm/internal/arch/arm64.go @@ -12,6 +12,7 @@ import ( "cmd/internal/obj" "cmd/internal/obj/arm64" "errors" + "fmt" ) var arm64LS = map[string]uint8{ @@ -46,13 +47,56 @@ var arm64Jump = map[string]bool{ "JMP": true, "TBNZ": true, "TBZ": true, + + // ADR isn't really a jump, but it takes a PC or label reference, + // which needs to patched like a jump. + "ADR": true, + "ADRP": true, } func jumpArm64(word string) bool { return arm64Jump[word] } -// IsARM64CMP reports whether the op (as defined by an arm.A* constant) is +var arm64SpecialOperand map[string]arm64.SpecialOperand + +// GetARM64SpecialOperand returns the internal representation of a special operand. +func GetARM64SpecialOperand(name string) arm64.SpecialOperand { + if arm64SpecialOperand == nil { + // Generate the mapping automatically when the first time the function is called. + arm64SpecialOperand = map[string]arm64.SpecialOperand{} + for opd := arm64.SPOP_BEGIN; opd < arm64.SPOP_END; opd++ { + s := fmt.Sprintf("%s", opd) + arm64SpecialOperand[s] = opd + } + + // Handle some special cases. + specialMapping := map[string]arm64.SpecialOperand{ + // The internal representation of CS(CC) and HS(LO) are the same. + "CS": arm64.SPOP_HS, + "CC": arm64.SPOP_LO, + } + for s, opd := range specialMapping { + arm64SpecialOperand[s] = opd + } + } + if opd, ok := arm64SpecialOperand[name]; ok { + return opd + } + return arm64.SPOP_END +} + +// IsARM64ADR reports whether the op (as defined by an arm64.A* constant) is +// one of the comparison instructions that require special handling. +func IsARM64ADR(op obj.As) bool { + switch op { + case arm64.AADR, arm64.AADRP: + return true + } + return false +} + +// IsARM64CMP reports whether the op (as defined by an arm64.A* constant) is // one of the comparison instructions that require special handling. func IsARM64CMP(op obj.As) bool { switch op { @@ -87,7 +131,7 @@ func IsARM64STLXR(op obj.As) bool { // inputs does not fit into prog.Reg, so require special handling. func IsARM64TBL(op obj.As) bool { switch op { - case arm64.AVTBL, arm64.AVMOVQ: + case arm64.AVTBL, arm64.AVTBX, arm64.AVMOVQ: return true } return false @@ -165,27 +209,21 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i } } if reg <= arm64.REG_R31 && reg >= arm64.REG_R0 { + if !isAmount { + return errors.New("invalid register extension") + } switch ext { case "UXTB": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { return errors.New("invalid shift for the register offset addressing mode") } a.Reg = arm64.REG_UXTB + Rnum case "UXTH": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { return errors.New("invalid shift for the register offset addressing mode") } a.Reg = arm64.REG_UXTH + Rnum case "UXTW": - if !isAmount { - return errors.New("invalid register extension") - } // effective address of memory is a base register value and an offset register value. if a.Type == obj.TYPE_MEM { a.Index = arm64.REG_UXTW + Rnum @@ -193,48 +231,33 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i a.Reg = arm64.REG_UXTW + Rnum } case "UXTX": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { return errors.New("invalid shift for the register offset addressing mode") } a.Reg = arm64.REG_UXTX + Rnum case "SXTB": - if !isAmount { - return errors.New("invalid register extension") + if a.Type == obj.TYPE_MEM { + return errors.New("invalid shift for the register offset addressing mode") } a.Reg = arm64.REG_SXTB + Rnum case "SXTH": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { return errors.New("invalid shift for the register offset addressing mode") } a.Reg = arm64.REG_SXTH + Rnum case "SXTW": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { a.Index = arm64.REG_SXTW + Rnum } else { a.Reg = arm64.REG_SXTW + Rnum } case "SXTX": - if !isAmount { - return errors.New("invalid register extension") - } if a.Type == obj.TYPE_MEM { a.Index = arm64.REG_SXTX + Rnum } else { a.Reg = arm64.REG_SXTX + Rnum } case "LSL": - if !isAmount { - return errors.New("invalid register extension") - } a.Index = arm64.REG_LSL + Rnum default: return errors.New("unsupported general register extension type: " + ext) diff --git a/src/cmd/asm/internal/arch/loong64.go b/src/cmd/asm/internal/arch/loong64.go new file mode 100644 index 00000000000000..ebf842c1f27400 --- /dev/null +++ b/src/cmd/asm/internal/arch/loong64.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Go 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 +// Loong64 (LoongArch64) instruction set, to minimize its interaction +// with the core of the assembler. + +package arch + +import ( + "cmd/internal/obj" + "cmd/internal/obj/loong64" +) + +func jumpLoong64(word string) bool { + switch word { + case "BEQ", "BFPF", "BFPT", "BLTZ", "BGEZ", "BLEZ", "BGTZ", "BLT", "BLTU", "JIRL", "BNE", "BGE", "BGEU", "JMP", "JAL", "CALL": + return true + } + return false +} + +// IsLoong64CMP reports whether the op (as defined by an loong64.A* constant) is +// one of the CMP instructions that require special handling. +func IsLoong64CMP(op obj.As) bool { + switch op { + case loong64.ACMPEQF, loong64.ACMPEQD, loong64.ACMPGEF, loong64.ACMPGED, + loong64.ACMPGTF, loong64.ACMPGTD: + return true + } + return false +} + +// IsLoong64MUL reports whether the op (as defined by an loong64.A* constant) is +// one of the MUL/DIV/REM instructions that require special handling. +func IsLoong64MUL(op obj.As) bool { + switch op { + case loong64.AMUL, loong64.AMULU, loong64.AMULV, loong64.AMULVU, + loong64.ADIV, loong64.ADIVU, loong64.ADIVV, loong64.ADIVVU, + loong64.AREM, loong64.AREMU, loong64.AREMV, loong64.AREMVU: + return true + } + return false +} + +func loong64RegisterNumber(name string, n int16) (int16, bool) { + switch name { + case "F": + if 0 <= n && n <= 31 { + return loong64.REG_F0 + n, true + } + case "FCSR": + if 0 <= n && n <= 31 { + return loong64.REG_FCSR0 + n, true + } + case "FCC": + if 0 <= n && n <= 31 { + return loong64.REG_FCC0 + n, true + } + case "R": + if 0 <= n && n <= 31 { + return loong64.REG_R0 + n, true + } + } + return 0, false +} diff --git a/src/cmd/asm/internal/arch/ppc64.go b/src/cmd/asm/internal/arch/ppc64.go index 3139665ba55c58..98a2bfedfdd0c5 100644 --- a/src/cmd/asm/internal/arch/ppc64.go +++ b/src/cmd/asm/internal/arch/ppc64.go @@ -15,34 +15,12 @@ import ( func jumpPPC64(word string) bool { switch word { - case "BC", "BCL", "BEQ", "BGE", "BGT", "BL", "BLE", "BLT", "BNE", "BR", "BVC", "BVS", "CALL", "JMP": + case "BC", "BCL", "BEQ", "BGE", "BGT", "BL", "BLE", "BLT", "BNE", "BR", "BVC", "BVS", "BDNZ", "BDZ", "CALL", "JMP": return true } return false } -// IsPPC64RLD reports whether the op (as defined by an ppc64.A* constant) is -// one of the RLD-like instructions that require special handling. -// The FMADD-like instructions behave similarly. -func IsPPC64RLD(op obj.As) bool { - switch op { - case ppc64.ARLDC, ppc64.ARLDCCC, ppc64.ARLDCL, ppc64.ARLDCLCC, - ppc64.ARLDCR, ppc64.ARLDCRCC, ppc64.ARLDMI, ppc64.ARLDMICC, - ppc64.ARLWMI, ppc64.ARLWMICC, ppc64.ARLWNM, ppc64.ARLWNMCC: - return true - case ppc64.AFMADD, ppc64.AFMADDCC, ppc64.AFMADDS, ppc64.AFMADDSCC, - ppc64.AFMSUB, ppc64.AFMSUBCC, ppc64.AFMSUBS, ppc64.AFMSUBSCC, - ppc64.AFNMADD, ppc64.AFNMADDCC, ppc64.AFNMADDS, ppc64.AFNMADDSCC, - ppc64.AFNMSUB, ppc64.AFNMSUBCC, ppc64.AFNMSUBS, ppc64.AFNMSUBSCC: - return true - } - return false -} - -func IsPPC64ISEL(op obj.As) bool { - return op == ppc64.AISEL -} - // IsPPC64CMP reports whether the op (as defined by an ppc64.A* constant) is // one of the CMP instructions that require special handling. func IsPPC64CMP(op obj.As) bool { @@ -77,6 +55,10 @@ func ppc64RegisterNumber(name string, n int16) (int16, bool) { if 0 <= n && n <= 7 { return ppc64.REG_CR0 + n, true } + case "A": + if 0 <= n && n <= 8 { + return ppc64.REG_A0 + n, true + } case "VS": if 0 <= n && n <= 63 { return ppc64.REG_VS0 + n, true diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index cf0d1550f99f0e..754139c566b565 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -5,15 +5,16 @@ package asm import ( - "bytes" "fmt" "strconv" + "strings" "text/scanner" "cmd/asm/internal/arch" "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" + "cmd/internal/obj/ppc64" "cmd/internal/obj/x86" "cmd/internal/objabi" "cmd/internal/sys" @@ -21,7 +22,7 @@ import ( // TODO: configure the architecture -var testOut *bytes.Buffer // Gathers output when testing. +var testOut *strings.Builder // Gathers output when testing. // append adds the Prog to the end of the program-thus-far. // If doLabel is set, it also defines the labels collect for this Prog. @@ -393,6 +394,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { Pos: p.pos(), As: op, } + targetAddr := &prog.To switch len(a) { case 0: if p.arch.Family == sys.Wasm { @@ -405,24 +407,57 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { target = &a[0] case 2: // Special 2-operand jumps. - target = &a[1] - prog.From = a[0] + if p.arch.Family == sys.ARM64 && arch.IsARM64ADR(op) { + // ADR label, R. Label is in From. + target = &a[0] + prog.To = a[1] + targetAddr = &prog.From + } else { + target = &a[1] + prog.From = a[0] + } case 3: if p.arch.Family == sys.PPC64 { // Special 3-operand jumps. - // First two must be constants; a[1] is a register number. + // a[1] is a register number expressed as a constant or register value target = &a[2] - prog.From = obj.Addr{ - Type: obj.TYPE_CONST, - Offset: p.getConstant(prog, op, &a[0]), + prog.From = a[0] + if a[0].Type != obj.TYPE_CONST { + // Legacy code may use a plain constant, accept it, and coerce + // into a constant. E.g: + // BC 4,... + // into + // BC $4,... + prog.From = obj.Addr{ + Type: obj.TYPE_CONST, + Offset: p.getConstant(prog, op, &a[0]), + } + } - reg := int16(p.getConstant(prog, op, &a[1])) - reg, ok := p.arch.RegisterNumber("R", reg) - if !ok { - p.errorf("bad register number %d", reg) - return + + // Likewise, fixup usage like: + // BC x,LT,... + // BC x,foo+2,... + // BC x,4 + // BC x,$5 + // into + // BC x,CR0LT,... + // BC x,CR0EQ,... + // BC x,CR1LT,... + // BC x,CR1GT,... + // The first and second case demonstrate a symbol name which is + // effectively discarded. In these cases, the offset determines + // the CR bit. + prog.Reg = a[1].Reg + if a[1].Type != obj.TYPE_REG { + // The CR bit is represented as a constant 0-31. Convert it to a Reg. + c := p.getConstant(prog, op, &a[1]) + reg, success := ppc64.ConstantToCRbit(c) + if !success { + p.errorf("invalid CR bit register number %d", c) + } + prog.Reg = reg } - prog.Reg = reg break } if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 { @@ -433,6 +468,14 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { prog.Reg = p.getRegister(prog, op, &a[1]) break } + if p.arch.Family == sys.Loong64 { + // 3-operand jumps. + // First two must be registers + target = &a[2] + prog.From = a[0] + prog.Reg = p.getRegister(prog, op, &a[1]) + break + } if p.arch.Family == sys.S390X { // 3-operand jumps. target = &a[2] @@ -461,7 +504,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { p.errorf("wrong number of arguments to %s instruction", op) return case 4: - if p.arch.Family == sys.S390X { + if p.arch.Family == sys.S390X || p.arch.Family == sys.PPC64 { // 4-operand compare-and-branch. prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) @@ -478,20 +521,20 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { switch { case target.Type == obj.TYPE_BRANCH: // JMP 4(PC) - prog.To = obj.Addr{ + *targetAddr = obj.Addr{ Type: obj.TYPE_BRANCH, Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in append, below. } case target.Type == obj.TYPE_REG: // JMP R1 - prog.To = *target + *targetAddr = *target case target.Type == obj.TYPE_MEM && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC): // JMP main·morestack(SB) - prog.To = *target + *targetAddr = *target case target.Type == obj.TYPE_INDIR && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC): // JMP *main·morestack(SB) - prog.To = *target - prog.To.Type = obj.TYPE_INDIR + *targetAddr = *target + targetAddr.Type = obj.TYPE_INDIR case target.Type == obj.TYPE_MEM && target.Reg == 0 && target.Offset == 0: // JMP exit if target.Sym == nil { @@ -500,20 +543,20 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { } targetProg := p.labels[target.Sym.Name] if targetProg == nil { - p.toPatch = append(p.toPatch, Patch{prog, target.Sym.Name}) + p.toPatch = append(p.toPatch, Patch{targetAddr, target.Sym.Name}) } else { - p.branch(prog, targetProg) + p.branch(targetAddr, targetProg) } case target.Type == obj.TYPE_MEM && target.Name == obj.NAME_NONE: // JMP 4(R0) - prog.To = *target + *targetAddr = *target // On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same. if p.arch.Family == sys.PPC64 && target.Offset == 0 { - prog.To.Type = obj.TYPE_REG + targetAddr.Type = obj.TYPE_REG } case target.Type == obj.TYPE_CONST: // JMP $4 - prog.To = a[0] + *targetAddr = a[0] case target.Type == obj.TYPE_NONE: // JMP default: @@ -531,17 +574,17 @@ func (p *Parser) patch() { p.errorf("undefined label %s", patch.label) return } - p.branch(patch.prog, targetProg) + p.branch(patch.addr, targetProg) } p.toPatch = p.toPatch[:0] } -func (p *Parser) branch(jmp, target *obj.Prog) { - jmp.To = obj.Addr{ +func (p *Parser) branch(addr *obj.Addr, target *obj.Prog) { + *addr = obj.Addr{ Type: obj.TYPE_BRANCH, Index: 0, } - jmp.To.Val = target + addr.Val = target } // asmInstruction assembles an instruction. @@ -593,6 +636,12 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.Reg = p.getRegister(prog, op, &a[1]) break } + } else if p.arch.Family == sys.Loong64 { + if arch.IsLoong64CMP(op) { + prog.From = a[0] + prog.Reg = p.getRegister(prog, op, &a[1]) + break + } } prog.From = a[0] prog.To = a[1] @@ -602,6 +651,10 @@ 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.Loong64: + prog.From = a[0] + prog.Reg = p.getRegister(prog, op, &a[1]) + prog.To = a[2] case sys.ARM: // Special cases. if arch.IsARMSTREX(op) { @@ -674,23 +727,17 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.To = a[1] break } - // Arithmetic. Choices are: - // reg reg reg - // imm reg reg - // reg imm reg - // If the immediate is the middle argument, use From3. + + prog.From = a[0] + prog.To = a[2] + + // If the second argument is not a register argument, it must be + // passed RestArgs/SetFrom3 switch a[1].Type { case obj.TYPE_REG: - prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) - prog.To = a[2] - case obj.TYPE_CONST: - prog.From = a[0] - prog.SetFrom3(a[1]) - prog.To = a[2] default: - p.errorf("invalid addressing modes for %s instruction", op) - return + prog.SetFrom3(a[1]) } case sys.RISCV64: // RISCV64 instructions with one input and two outputs. @@ -757,41 +804,25 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { break } if p.arch.Family == sys.PPC64 { - if arch.IsPPC64RLD(op) { - prog.From = a[0] - prog.Reg = p.getRegister(prog, op, &a[1]) - prog.SetFrom3(a[2]) - prog.To = a[3] - break - } else if arch.IsPPC64ISEL(op) { - // ISEL BC,RB,RA,RT becomes isel rt,ra,rb,bc - prog.SetFrom3(a[2]) // ra - prog.From = a[0] // bc - prog.Reg = p.getRegister(prog, op, &a[1]) // rb - prog.To = a[3] // rt - break - } - // Else, it is a VA-form instruction - // reg reg reg reg - // imm reg reg reg - // Or a VX-form instruction - // imm imm reg reg + prog.From = a[0] + prog.To = a[3] + // If the second argument is not a register argument, it must be + // passed RestArgs/SetFrom3 if a[1].Type == obj.TYPE_REG { - prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) - prog.SetFrom3(a[2]) - prog.To = a[3] - break - } else if a[1].Type == obj.TYPE_CONST { - prog.From = a[0] - prog.Reg = p.getRegister(prog, op, &a[2]) - prog.SetFrom3(a[1]) - prog.To = a[3] - break + prog.SetRestArgs([]obj.Addr{a[2]}) } else { - p.errorf("invalid addressing modes for %s instruction", op) - return + // Don't set prog.Reg if a1 isn't a reg arg. + prog.SetRestArgs([]obj.Addr{a[1], a[2]}) } + break + } + if p.arch.Family == sys.RISCV64 { + prog.From = a[0] + prog.Reg = p.getRegister(prog, op, &a[1]) + prog.SetRestArgs([]obj.Addr{a[2]}) + prog.To = a[3] + break } if p.arch.Family == sys.S390X { if a[1].Type != obj.TYPE_REG { @@ -849,6 +880,14 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.As = MRC // Both instructions are coded as MRC. break } + if p.arch.Family == sys.PPC64 { + prog.From = a[0] + // Second arg is always a register type on ppc64. + prog.Reg = p.getRegister(prog, op, &a[1]) + prog.SetRestArgs([]obj.Addr{a[2], a[3], a[4]}) + prog.To = a[5] + break + } fallthrough default: p.errorf("can't handle %s instruction with %d operands", op, len(a)) diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index ead8b27b015df9..78b72ca9c41d8d 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -34,7 +34,7 @@ func testEndToEnd(t *testing.T, goarch, file string) { parser := NewParser(ctxt, architecture, lexer, false) pList := new(obj.Plist) var ok bool - testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. + testOut = new(strings.Builder) // The assembler writes test output to this buffer. ctxt.Bso = bufio.NewWriter(os.Stdout) ctxt.IsAsm = true defer ctxt.Bso.Flush() @@ -277,7 +277,6 @@ func testErrors(t *testing.T, goarch, file string, flags ...string) { parser := NewParser(ctxt, architecture, lexer, false) pList := new(obj.Plist) var ok bool - testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. ctxt.Bso = bufio.NewWriter(os.Stdout) ctxt.IsAsm = true defer ctxt.Bso.Flush() @@ -447,6 +446,13 @@ func TestMIPSEndToEnd(t *testing.T) { testEndToEnd(t, "mips64", "mips64") } +func TestLOONG64Encoder(t *testing.T) { + testEndToEnd(t, "loong64", "loong64enc1") + testEndToEnd(t, "loong64", "loong64enc2") + testEndToEnd(t, "loong64", "loong64enc3") + testEndToEnd(t, "loong64", "loong64") +} + func TestPPC64EndToEnd(t *testing.T) { testEndToEnd(t, "ppc64", "ppc64") } diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go index 8ef02b1a0e8aca..29371d61994ac5 100644 --- a/src/cmd/asm/internal/asm/operand_test.go +++ b/src/cmd/asm/internal/asm/operand_test.go @@ -19,7 +19,7 @@ import ( func setArch(goarch string) (*arch.Arch, *obj.Link) { buildcfg.GOOS = "linux" // obj can handle this OS for all architectures. buildcfg.GOARCH = goarch - architecture := arch.Set(goarch) + architecture := arch.Set(goarch, false) if architecture == nil { panic("asm: unrecognized architecture " + goarch) } @@ -125,6 +125,11 @@ func TestMIPS64OperandParser(t *testing.T) { testOperandParser(t, parser, mips64OperandTests) } +func TestLOONG64OperandParser(t *testing.T) { + parser := newParser("loong64") + testOperandParser(t, parser, loong64OperandTests) +} + func TestS390XOperandParser(t *testing.T) { parser := newParser("s390x") testOperandParser(t, parser, s390xOperandTests) @@ -143,6 +148,7 @@ func TestFuncAddress(t *testing.T) { {"ppc64", ppc64OperandTests}, {"mips", mipsOperandTests}, {"mips64", mips64OperandTests}, + {"loong64", loong64OperandTests}, {"s390x", s390xOperandTests}, } { t.Run(sub.arch, func(t *testing.T) { @@ -467,7 +473,7 @@ var ppc64OperandTests = []operandTest{ {"(R4)", "(R4)"}, {"(R5)", "(R5)"}, {"(R5)(R6*1)", "(R5)(R6*1)"}, - {"(R5+R6)", "(R5)(R6*1)"}, // Old syntax. + {"(R5+R6)", "(R5)(R6)"}, {"-1(R4)", "-1(R4)"}, {"-1(R5)", "-1(R5)"}, {"6(PC)", "6(PC)"}, @@ -845,6 +851,88 @@ var mipsOperandTests = []operandTest{ {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms. } +var loong64OperandTests = []operandTest{ + {"$((1<<63)-1)", "$9223372036854775807"}, + {"$(-64*1024)", "$-65536"}, + {"$(1024 * 8)", "$8192"}, + {"$-1", "$-1"}, + {"$-24(R4)", "$-24(R4)"}, + {"$0", "$0"}, + {"$0(R1)", "$(R1)"}, + {"$0.5", "$(0.5)"}, + {"$0x7000", "$28672"}, + {"$0x88888eef", "$2290650863"}, + {"$1", "$1"}, + {"$_main<>(SB)", "$_main<>(SB)"}, + {"$argframe(FP)", "$argframe(FP)"}, + {"$~3", "$-4"}, + {"(-288-3*8)(R1)", "-312(R1)"}, + {"(16)(R7)", "16(R7)"}, + {"(8)(g)", "8(g)"}, + {"(R0)", "(R0)"}, + {"(R3)", "(R3)"}, + {"(R4)", "(R4)"}, + {"(R5)", "(R5)"}, + {"-1(R4)", "-1(R4)"}, + {"-1(R5)", "-1(R5)"}, + {"6(PC)", "6(PC)"}, + {"F14", "F14"}, + {"F15", "F15"}, + {"F16", "F16"}, + {"F17", "F17"}, + {"F18", "F18"}, + {"F19", "F19"}, + {"F20", "F20"}, + {"F21", "F21"}, + {"F22", "F22"}, + {"F23", "F23"}, + {"F24", "F24"}, + {"F25", "F25"}, + {"F26", "F26"}, + {"F27", "F27"}, + {"F28", "F28"}, + {"F29", "F29"}, + {"F30", "F30"}, + {"F31", "F31"}, + {"R0", "R0"}, + {"R1", "R1"}, + {"R11", "R11"}, + {"R12", "R12"}, + {"R13", "R13"}, + {"R14", "R14"}, + {"R15", "R15"}, + {"R16", "R16"}, + {"R17", "R17"}, + {"R18", "R18"}, + {"R19", "R19"}, + {"R2", "R2"}, + {"R20", "R20"}, + {"R21", "R21"}, + {"R23", "R23"}, + {"R24", "R24"}, + {"R25", "R25"}, + {"R26", "R26"}, + {"R27", "R27"}, + {"R28", "R28"}, + {"R29", "R29"}, + {"R30", "R30"}, + {"R3", "R3"}, + {"R4", "R4"}, + {"R5", "R5"}, + {"R6", "R6"}, + {"R7", "R7"}, + {"R8", "R8"}, + {"R9", "R9"}, + {"a(FP)", "a(FP)"}, + {"g", "g"}, + {"RSB", "R31"}, + {"ret+8(FP)", "ret+8(FP)"}, + {"runtime·abort(SB)", "runtime.abort(SB)"}, + {"·AddUint32(SB)", "\"\".AddUint32(SB)"}, + {"·trunc(SB)", "\"\".trunc(SB)"}, + {"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms. +} + var s390xOperandTests = []operandTest{ {"$((1<<63)-1)", "$9223372036854775807"}, {"$(-64*1024)", "$-65536"}, diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index 4cddcf48a466ee..037084fb8ce20f 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -19,6 +19,7 @@ import ( "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" + "cmd/internal/obj/arm64" "cmd/internal/obj/x86" "cmd/internal/src" "cmd/internal/sys" @@ -48,7 +49,7 @@ type Parser struct { } type Patch struct { - prog *obj.Prog + addr *obj.Addr label string } @@ -161,7 +162,7 @@ func (p *Parser) nextToken() lex.ScanToken { // line consumes a single assembly line from p.lex of the form // -// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n') +// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n') // // It adds any labels to p.pendingLabels and returns the word, cond, // operand list, and true. If there is an error or EOF, it returns @@ -389,8 +390,19 @@ func (p *Parser) operand(a *obj.Addr) { tok := p.next() name := tok.String() if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) { - // We have a symbol. Parse $sym±offset(symkind) - p.symbolReference(a, name, prefix) + switch p.arch.Family { + case sys.ARM64: + // arm64 special operands. + if opd := arch.GetARM64SpecialOperand(name); opd != arm64.SPOP_END { + a.Type = obj.TYPE_SPECIAL + a.Offset = int64(opd) + break + } + fallthrough + default: + // We have a symbol. Parse $sym±offset(symkind) + p.symbolReference(a, name, prefix) + } // fmt.Printf("SYM %s\n", obj.Dconv(&emptyProg, 0, a)) if p.peek() == scanner.EOF { return @@ -843,7 +855,6 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr // // Anything else beginning with "<" logs an error if issueError is // true, otherwise returns (false, obj.ABI0). -// func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) { abi := obj.ABI0 isStatic := false @@ -880,7 +891,7 @@ func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) { // constrained form of the operand syntax that's always SB-based, // non-static, and has at most a simple integer offset: // -// [$|*]sym[][+Int](SB) +// [$|*]sym[][+Int](SB) func (p *Parser) funcAddress() (string, obj.ABI, bool) { switch p.peek() { case '$', '*': @@ -964,13 +975,13 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { return } if p.arch.Family == sys.PPC64 { - // Special form for PPC64: (R1+R2); alias for (R1)(R2*1). + // Special form for PPC64: (R1+R2); alias for (R1)(R2). if prefix != 0 || scale != 0 { p.errorf("illegal address mode for register+register") return } a.Type = obj.TYPE_MEM - a.Scale = 1 + a.Scale = 0 a.Index = r2 // Nothing may follow. return @@ -1003,9 +1014,10 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { p.errorf("unimplemented two-register form") } a.Index = r1 - if scale != 0 && scale != 1 && p.arch.Family == sys.ARM64 { + if scale != 0 && scale != 1 && (p.arch.Family == sys.ARM64 || + p.arch.Family == sys.PPC64) { // Support (R1)(R2) (no scaling) and (R1)(R2*1). - p.errorf("arm64 doesn't support scaled register format") + p.errorf("%s doesn't support scaled register format", p.arch.Name) } else { a.Scale = int16(scale) } @@ -1030,9 +1042,13 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { // // For 386/AMD64 register list specifies 4VNNIW-style multi-source operand. // For range of 4 elements, Intel manual uses "+3" notation, for example: +// // VP4DPWSSDS zmm1{k1}{z}, zmm2+3, m128 +// // Given asm line: +// // VP4DPWSSDS Z5, [Z10-Z13], (AX) +// // zmm2 is Z10, and Z13 is the only valid value for it (Z10+3). // Only simple ranges are accepted, like [Z0-Z3]. // diff --git a/src/cmd/asm/internal/asm/pseudo_test.go b/src/cmd/asm/internal/asm/pseudo_test.go index fe6ffa60740f8a..5e6fcf8dfe8870 100644 --- a/src/cmd/asm/internal/asm/pseudo_test.go +++ b/src/cmd/asm/internal/asm/pseudo_test.go @@ -5,7 +5,6 @@ package asm import ( - "bytes" "strings" "testing" @@ -81,7 +80,7 @@ func TestErroneous(t *testing.T) { // Note these errors should be independent of the architecture. // Just run the test with amd64. parser := newParser("amd64") - var buf bytes.Buffer + var buf strings.Builder parser.errorWriter = &buf for _, cat := range testcats { diff --git a/src/cmd/asm/internal/asm/testdata/amd64enc.s b/src/cmd/asm/internal/asm/testdata/amd64enc.s index c02f51d125916b..5bba292dee43f6 100644 --- a/src/cmd/asm/internal/asm/testdata/amd64enc.s +++ b/src/cmd/asm/internal/asm/testdata/amd64enc.s @@ -2495,30 +2495,30 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 MOVAPS X11, (BX) // 440f291b MOVAPS X2, (R11) // 410f2913 MOVAPS X11, (R11) // 450f291b - MOVBEWW DX, (BX) // 660f38f113 - MOVBEWW R11, (BX) // 66440f38f11b - MOVBEWW DX, (R11) // 66410f38f113 - MOVBEWW R11, (R11) // 66450f38f11b - MOVBEWW (BX), DX // 660f38f013 - MOVBEWW (R11), DX // 66410f38f013 - MOVBEWW (BX), R11 // 66440f38f01b - MOVBEWW (R11), R11 // 66450f38f01b - MOVBELL DX, (BX) // 0f38f113 - MOVBELL R11, (BX) // 440f38f11b - MOVBELL DX, (R11) // 410f38f113 - MOVBELL R11, (R11) // 450f38f11b - MOVBELL (BX), DX // 0f38f013 - MOVBELL (R11), DX // 410f38f013 - MOVBELL (BX), R11 // 440f38f01b - MOVBELL (R11), R11 // 450f38f01b - MOVBEQQ DX, (BX) // 480f38f113 - MOVBEQQ R11, (BX) // 4c0f38f11b - MOVBEQQ DX, (R11) // 490f38f113 - MOVBEQQ R11, (R11) // 4d0f38f11b - MOVBEQQ (BX), DX // 480f38f013 - MOVBEQQ (R11), DX // 490f38f013 - MOVBEQQ (BX), R11 // 4c0f38f01b - MOVBEQQ (R11), R11 // 4d0f38f01b + MOVBEW DX, (BX) // 660f38f113 + MOVBEW R11, (BX) // 66440f38f11b + MOVBEW DX, (R11) // 66410f38f113 + MOVBEW R11, (R11) // 66450f38f11b + MOVBEW (BX), DX // 660f38f013 + MOVBEW (R11), DX // 66410f38f013 + MOVBEW (BX), R11 // 66440f38f01b + MOVBEW (R11), R11 // 66450f38f01b + MOVBEL DX, (BX) // 0f38f113 + MOVBEL R11, (BX) // 440f38f11b + MOVBEL DX, (R11) // 410f38f113 + MOVBEL R11, (R11) // 450f38f11b + MOVBEL (BX), DX // 0f38f013 + MOVBEL (R11), DX // 410f38f013 + MOVBEL (BX), R11 // 440f38f01b + MOVBEL (R11), R11 // 450f38f01b + MOVBEQ DX, (BX) // 480f38f113 + MOVBEQ R11, (BX) // 4c0f38f11b + MOVBEQ DX, (R11) // 490f38f113 + MOVBEQ R11, (R11) // 4d0f38f11b + MOVBEQ (BX), DX // 480f38f013 + MOVBEQ (R11), DX // 490f38f013 + MOVBEQ (BX), R11 // 4c0f38f01b + MOVBEQ (R11), R11 // 4d0f38f01b MOVQ (BX), M2 // 0f6e13 or 0f6f13 or 480f6e13 MOVQ (R11), M2 // 410f6e13 or 410f6f13 or 490f6e13 MOVQ DX, M2 // 0f6ed2 or 480f6ed2 diff --git a/src/cmd/asm/internal/asm/testdata/arm.s b/src/cmd/asm/internal/asm/testdata/arm.s index cc8e25ef7c7a55..2ba22c71dec17f 100644 --- a/src/cmd/asm/internal/asm/testdata/arm.s +++ b/src/cmd/asm/internal/asm/testdata/arm.s @@ -1042,7 +1042,7 @@ jmp_label_3: BFI $29, $2, R8 // 1881dee7 BFI $16, $8, R1, R2 // BFI $16, R1, $8, R2 // 1124d7e7 -// synthetic arithmatic +// synthetic arithmetic ADD $0xffffffaa, R2, R3 // ADD $4294967210, R2, R3 // 55b0e0e30b3082e0 ADD $0xffffff55, R5 // ADD $4294967125, R5 // aab0e0e30b5085e0 ADD.S $0xffffffab, R2, R3 // ADD.S $4294967211, R2, R3 // 54b0e0e30b3092e0 diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index d8a20edfc13039..a1493a7ad3a0b0 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -10,7 +10,6 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 - // arithmetic operations ADDW $1, R2, R3 ADDW R1, R2, R3 @@ -96,6 +95,14 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 // CLS CLSW R1, R2 CLS R1, R2 + SBC $0, R1 // 21001fda + SBCW $0, R1 // 21001f5a + SBCS $0, R1 // 21001ffa + SBCSW $0, R1 // 21001f7a + ADC $0, R1 // 21001f9a + ADCW $0, R1 // 21001f1a + ADCS $0, R1 // 21001fba + ADCSW $0, R1 // 21001f3a // fp/simd instructions. VADDP V1.B16, V2.B16, V3.B16 // 43bc214e @@ -168,6 +175,22 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 VTBL V14.B16, [V3.B16, V4.B16, V5.B16], V17.B16 // 71400e4e VTBL V13.B16, [V29.B16, V30.B16, V31.B16, V0.B16], V28.B16 // bc630d4e VTBL V3.B8, [V27.B16], V8.B8 // 6803030e + VTBX V22.B16, [V28.B16, V29.B16], V11.B16 // 8b33164e + VTBX V18.B8, [V17.B16, V18.B16, V19.B16], V22.B8 // 3652120e + VTBX V31.B8, [V14.B16, V15.B16, V16.B16, V17.B16], V15.B8 // cf711f0e + VTBX V14.B16, [V16.B16], V11.B16 // 0b120e4e + VTBX V28.B16, [V25.B16, V26.B16], V5.B16 // 25331c4e + VTBX V16.B8, [V4.B16, V5.B16, V6.B16], V12.B8 // 8c50100e + VTBX V4.B8, [V16.B16, V17.B16, V18.B16, V19.B16], V4.B8 // 0472040e + VTBX V15.B8, [V1.B16], V20.B8 // 34100f0e + VTBX V26.B16, [V2.B16, V3.B16], V26.B16 // 5a301a4e + VTBX V15.B8, [V6.B16, V7.B16, V8.B16], V2.B8 // c2500f0e + VTBX V2.B16, [V27.B16, V28.B16, V29.B16, V30.B16], V18.B16 // 7273024e + VTBX V11.B16, [V13.B16], V27.B16 // bb110b4e + VTBX V3.B8, [V7.B16, V8.B16], V25.B8 // f930030e + VTBX V14.B16, [V3.B16, V4.B16, V5.B16], V17.B16 // 71500e4e + VTBX V13.B16, [V29.B16, V30.B16, V31.B16, V0.B16], V28.B16 // bc730d4e + VTBX V3.B8, [V27.B16], V8.B8 // 6813030e VZIP1 V16.H8, V3.H8, V19.H8 // 7338504e VZIP2 V22.D2, V25.D2, V21.D2 // 357bd64e VZIP1 V6.D2, V9.D2, V11.D2 // 2b39c64e @@ -211,6 +234,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 FMOVD $(0.1796875), F2 // 02f0681e FMOVS $(0.96875), F3 // 03f02d1e FMOVD $(28.0), F4 // 0490671e + FMOVD $0, F0 // e003679e + FMOVS $0, F0 // e003271e + FMOVD ZR, F0 // e003679e + FMOVS ZR, F0 // e003271e VUADDW V9.B8, V12.H8, V14.H8 // 8e11292e VUADDW V13.H4, V10.S4, V11.S4 // 4b116d2e VUADDW V21.S2, V24.D2, V29.D2 // 1d13b52e @@ -241,6 +268,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 FADDS F2, F3, F4 // 6428221e FADDD F1, F2 // 4228611e VDUP V19.S[0], V17.S4 // 7106044e + VTRN1 V3.D2, V2.D2, V20.D2 // 5428c34e + VTRN2 V3.D2, V2.D2, V21.D2 // 5568c34e + VTRN1 V5.D2, V4.D2, V22.D2 // 9628c54e + VTRN2 V5.D2, V4.D2, V23.D2 // 9768c54e // special @@ -334,6 +365,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 EONW $0x6006000060060, R5 // EONW $1689262177517664, R5 // 1b0c8052db00a072a5003b4a ORNW $0x6006000060060, R5 // ORNW $1689262177517664, R5 // 1b0c8052db00a072a5003b2a BICSW $0x6006000060060, R5 // BICSW $1689262177517664, R5 // 1b0c8052db00a072a5003b6a + AND $1, ZR // fb0340b2ff031b8a + ANDW $1, ZR // fb030032ff031b0a // TODO: this could have better encoding ANDW $-1, R10 // 1b0080124a011b0a AND $8, R0, RSP // 1f007d92 @@ -365,13 +398,13 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 MOVD $0x11110000, R1 // MOVD $286326784, R1 // 2122a2d2 MOVD $0xaaaa0000aaaa1111, R1 // MOVD $-6149102338357718767, R1 // 212282d24155b5f24155f5f2 MOVD $0x1111ffff1111aaaa, R1 // MOVD $1230045644216969898, R1 // a1aa8a922122a2f22122e2f2 - MOVD $0, R1 // 010080d2 + MOVD $0, R1 // e1031faa MOVD $-1, R1 // 01008092 MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92 - MOVW $1, ZR + MOVW $1, ZR // 3f008052 MOVW $1, R1 - MOVD $1, ZR + MOVD $1, ZR // 3f0080d2 MOVD $1, R1 MOVK $1, R1 MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2 @@ -386,10 +419,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 VMOVQ $0x8040201008040202, $0x7040201008040201, V20 // VMOVQ $-9205322385119247870, $8088500183983456769, V20 // mov(to/from sp) - MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // fb074091610b0091 - MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // fb0740917f231c91 - MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // fb08409161070091 - MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // fbfc7f9161ff3f91 + MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // e107409121080091 + MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // ff074091ff231c91 + MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // e108409121040091 + MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // e1fc7f9121fc3f91 MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1 MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1 MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1 @@ -475,6 +508,15 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8 FMOVQ.P 11(R10), F13 // 4db5c03c FMOVQ.W 11(R20), F15 // 8fbec03c +// storing $0 to memory, $0 will be replaced with ZR. + MOVD $0, (R1) // 3f0000f9 + MOVW $0, (R1) // 3f0000b9 + MOVWU $0, (R1) // 3f0000b9 + MOVH $0, (R1) // 3f000079 + MOVHU $0, (R1) // 3f000079 + MOVB $0, (R1) // 3f000039 + MOVBU $0, (R1) // 3f000039 + // small offset fits into instructions MOVB R1, 1(R2) // 41040039 MOVH R1, 1(R2) // 41100078 @@ -622,7 +664,8 @@ again: CSELW LT, R2, R3, R4 // 44b0831a CSINC GT, R1, ZR, R3 // 23c49f9a CSNEG MI, R1, R2, R3 // 234482da - CSINV CS, R1, R2, R3 // CSINV HS, R1, R2, R3 // 232082da + CSINV CS, R1, R2, R3 // CSINV HS, R1, R2, R3 // 232082da + CSINV HS, R1, R2, R3 // 232082da CSINVW MI, R2, ZR, R2 // 42409f5a CINC EQ, R4, R9 // 8914849a CINCW PL, R2, ZR // 5f44821a @@ -844,6 +887,11 @@ again: JMP foo(SB) CALL foo(SB) +// ADR + ADR next, R11 // ADR R11 // 2b000010 +next: + NOP + // LDP/STP LDP (R0), (R0, R1) // 000440a9 LDP (R0), (R1, R2) // 010840a9 @@ -1065,6 +1113,7 @@ again: MSR $1, SPSel // bf4100d5 MSR $9, DAIFSet // df4903d5 MSR $6, DAIFClr // ff4603d5 + MSR $0, CPACR_EL1 // 5f1018d5 MRS ELR_EL1, R8 // 284038d5 MSR R16, ELR_EL1 // 304018d5 MSR R2, ACTLR_EL1 // 221018d5 @@ -1621,4 +1670,116 @@ again: MSR R13, ZCR_EL1 // 0d1218d5 MRS ZCR_EL1, R23 // 171238d5 MSR R17, ZCR_EL1 // 111218d5 + SYS $32768, R1 // 018008d5 + SYS $32768 // 1f8008d5 + +// TLBI instruction + TLBI VMALLE1IS // 1f8308d5 + TLBI VMALLE1 // 1f8708d5 + TLBI ALLE2IS // 1f830cd5 + TLBI ALLE1IS // 9f830cd5 + TLBI VMALLS12E1IS // df830cd5 + TLBI ALLE2 // 1f870cd5 + TLBI ALLE1 // 9f870cd5 + TLBI VMALLS12E1 // df870cd5 + TLBI ALLE3IS // 1f830ed5 + TLBI ALLE3 // 1f870ed5 + TLBI VMALLE1OS // 1f8108d5 + TLBI ALLE2OS // 1f810cd5 + TLBI ALLE1OS // 9f810cd5 + TLBI VMALLS12E1OS // df810cd5 + TLBI ALLE3OS // 1f810ed5 + TLBI VAE1IS, R0 // 208308d5 + TLBI ASIDE1IS, R1 // 418308d5 + TLBI VAAE1IS, R2 // 628308d5 + TLBI VALE1IS, R3 // a38308d5 + TLBI VAALE1IS, R4 // e48308d5 + TLBI VAE1, R5 // 258708d5 + TLBI ASIDE1, R6 // 468708d5 + TLBI VAAE1, R7 // 678708d5 + TLBI VALE1, R8 // a88708d5 + TLBI VAALE1, R9 // e98708d5 + TLBI IPAS2E1IS, R10 // 2a800cd5 + TLBI IPAS2LE1IS, R11 // ab800cd5 + TLBI VAE2IS, R12 // 2c830cd5 + TLBI VALE2IS, R13 // ad830cd5 + TLBI IPAS2E1, R14 // 2e840cd5 + TLBI IPAS2LE1, R15 // af840cd5 + TLBI VAE2, R16 // 30870cd5 + TLBI VALE2, R17 // b1870cd5 + TLBI VAE3IS, ZR // 3f830ed5 + TLBI VALE3IS, R19 // b3830ed5 + TLBI VAE3, R20 // 34870ed5 + TLBI VALE3, R21 // b5870ed5 + TLBI VAE1OS, R22 // 368108d5 + TLBI ASIDE1OS, R23 // 578108d5 + TLBI VAAE1OS, R24 // 788108d5 + TLBI VALE1OS, R25 // b98108d5 + TLBI VAALE1OS, R26 // fa8108d5 + TLBI RVAE1IS, R27 // 3b8208d5 + TLBI RVAAE1IS, ZR // 7f8208d5 + TLBI RVALE1IS, R29 // bd8208d5 + TLBI RVAALE1IS, R30 // fe8208d5 + TLBI RVAE1OS, ZR // 3f8508d5 + TLBI RVAAE1OS, R0 // 608508d5 + TLBI RVALE1OS, R1 // a18508d5 + TLBI RVAALE1OS, R2 // e28508d5 + TLBI RVAE1, R3 // 238608d5 + TLBI RVAAE1, R4 // 648608d5 + TLBI RVALE1, R5 // a58608d5 + TLBI RVAALE1, R6 // e68608d5 + TLBI RIPAS2E1IS, R7 // 47800cd5 + TLBI RIPAS2LE1IS, R8 // c8800cd5 + TLBI VAE2OS, R9 // 29810cd5 + TLBI VALE2OS, R10 // aa810cd5 + TLBI RVAE2IS, R11 // 2b820cd5 + TLBI RVALE2IS, R12 // ac820cd5 + TLBI IPAS2E1OS, R13 // 0d840cd5 + TLBI RIPAS2E1, R14 // 4e840cd5 + TLBI RIPAS2E1OS, R15 // 6f840cd5 + TLBI IPAS2LE1OS, R16 // 90840cd5 + TLBI RIPAS2LE1, R17 // d1840cd5 + TLBI RIPAS2LE1OS, ZR // ff840cd5 + TLBI RVAE2OS, R19 // 33850cd5 + TLBI RVALE2OS, R20 // b4850cd5 + TLBI RVAE2, R21 // 35860cd5 + TLBI RVALE2, R22 // b6860cd5 + TLBI VAE3OS, R23 // 37810ed5 + TLBI VALE3OS, R24 // b8810ed5 + TLBI RVAE3IS, R25 // 39820ed5 + TLBI RVALE3IS, R26 // ba820ed5 + TLBI RVAE3OS, R27 // 3b850ed5 + TLBI RVALE3OS, ZR // bf850ed5 + TLBI RVAE3, R29 // 3d860ed5 + TLBI RVALE3, R30 // be860ed5 + +// DC instruction + DC IVAC, R0 // 207608d5 + DC ISW, R1 // 417608d5 + DC CSW, R2 // 427a08d5 + DC CISW, R3 // 437e08d5 + DC ZVA, R4 // 24740bd5 + DC CVAC, R5 // 257a0bd5 + DC CVAU, R6 // 267b0bd5 + DC CIVAC, R7 // 277e0bd5 + DC IGVAC, R8 // 687608d5 + DC IGSW, R9 // 897608d5 + DC IGDVAC, R10 // aa7608d5 + DC IGDSW, R11 // cb7608d5 + DC CGSW, R12 // 8c7a08d5 + DC CGDSW, R13 // cd7a08d5 + DC CIGSW, R14 // 8e7e08d5 + DC CIGDSW, R15 // cf7e08d5 + DC GVA, R16 // 70740bd5 + DC GZVA, R17 // 91740bd5 + DC CGVAC, ZR // 7f7a0bd5 + DC CGDVAC, R19 // b37a0bd5 + DC CGVAP, R20 // 747c0bd5 + DC CGDVAP, R21 // b57c0bd5 + DC CGVADP, R22 // 767d0bd5 + DC CGDVADP, R23 // b77d0bd5 + DC CIGVAC, R24 // 787e0bd5 + DC CIGDVAC, R25 // b97e0bd5 + DC CVAP, R26 // 3a7c0bd5 + DC CVADP, R27 // 3b7d0bd5 END diff --git a/src/cmd/asm/internal/asm/testdata/arm64enc.s b/src/cmd/asm/internal/asm/testdata/arm64enc.s index a29862822d30a1..0ae00d2ac9277d 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64enc.s +++ b/src/cmd/asm/internal/asm/testdata/arm64enc.s @@ -134,7 +134,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 CSINV LO, R2, R11, R14 // 4e308bda CSNEGW HS, R16, R29, R10 // 0a269d5a CSNEG NE, R21, R19, R11 // ab1693da - //TODO DC + DC IVAC, R1 // 217608d5 DCPS1 $11378 // 418ea5d4 DCPS2 $10699 // 6239a5d4 DCPS3 $24415 // e3ebabd4 @@ -263,7 +263,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 MOVKW $(3905<<0), R21 // MOVKW $3905, R21 // 35e88172 MOVKW $(3905<<16), R21 // MOVKW $255918080, R21 // 35e8a172 MOVK $(3905<<32), R21 // MOVK $16771847290880, R21 // 35e8c1f2 - MOVD $0, R5 // 050080d2 + MOVD $0, R5 // e5031faa MSR $1, SPSel // bf4100d5 MSR $9, DAIFSet // df4903d5 MSR $6, DAIFClr // ff4603d5 @@ -397,7 +397,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 SXTH R17, R25 // 393e4093 SXTW R0, R27 // 1b7c4093 SYSL $285440, R12 // 0c5b2cd5 - //TODO TLBI + TLBI VAE1IS, R1 // 218308d5 TSTW $0x80000007, R9 // TSTW $2147483655, R9 // 3f0d0172 TST $0xfffffff0, LR // TST $4294967280, R30 // df6f7cf2 TSTW R10@>21, R2 // 5f54ca6a diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s index cf57179e430161..52f01e16a612ae 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64error.s +++ b/src/cmd/asm/internal/asm/testdata/arm64error.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. TEXT errors(SB),$0 - AND $1, RSP // ERROR "illegal combination" + AND $1, RSP // ERROR "illegal source register" ANDS $1, R0, RSP // ERROR "illegal combination" ADDSW R7->32, R14, R13 // ERROR "shift amount out of range 0 to 31" ADD R1.UXTB<<5, R2, R3 // ERROR "shift amount out of range 0 to 4" @@ -406,17 +406,40 @@ TEXT errors(SB),$0 VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement" VUADDW V9.B8, V12.H8, V14.B8 // ERROR "invalid arrangement" VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch" - VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" - VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" + VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" + VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" VUMAX V1.B8, V2.B8, V3.B16 // ERROR "operand mismatch" VUMIN V1.H4, V2.S4, V3.H4 // ERROR "operand mismatch" VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range" - VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range" + VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range" CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register" CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register" CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous" CASPD (R2, R3), (R2), (R8, R10) // ERROR "destination register pair must be contiguous" ADD R1>>2, RSP, R3 // ERROR "illegal combination" - ADDS R2<<3, R3, RSP // ERROR "unexpected SP reference" - CMP R1<<5, RSP // ERROR "the left shift amount out of range 0 to 4" + ADDS R2<<3, R3, RSP // ERROR "illegal destination register" + CMP R1<<5, RSP // ERROR "shift amount out of range 0 to 4" + MOVD.P y+8(FP), R1 // ERROR "illegal combination" + MOVD.W x-8(SP), R1 // ERROR "illegal combination" + LDP.P x+8(FP), (R0, R1) // ERROR "illegal combination" + LDP.W x+8(SP), (R0, R1) // ERROR "illegal combination" + ADD $0x1234567, R27, R3 // ERROR "cannot use REGTMP as source" + ADD $0x3fffffffc000, R27, R5 // ERROR "cannot use REGTMP as source" + AND $0x22220000, R27, R4 // ERROR "cannot use REGTMP as source" + ANDW $0x6006000060060, R27, R5 // ERROR "cannot use REGTMP as source" + STP (R3, R4), 0x1234567(R27) // ERROR "REGTMP used in large offset store" + LDP 0x1234567(R27), (R3, R4) // ERROR "REGTMP used in large offset load" + STP (R26, R27), 700(R2) // ERROR "cannot use REGTMP as source" + MOVK $0, R10 // ERROR "zero shifts cannot be handled correctly" + MOVK $(0<<32), R10 // ERROR "zero shifts cannot be handled correctly" + TLBI PLDL1KEEP // ERROR "illegal argument" + TLBI VMALLE1IS, R0 // ERROR "extraneous register at operand 2" + TLBI ALLE3OS, ZR // ERROR "extraneous register at operand 2" + TLBI VAE1IS // ERROR "missing register at operand 2" + TLBI RVALE3 // ERROR "missing register at operand 2" + DC PLDL1KEEP // ERROR "illegal argument" + DC VMALLE1IS // ERROR "illegal argument" + DC VAE1IS // ERROR "illegal argument" + DC VAE1IS, R0 // ERROR "illegal argument" + DC IVAC // ERROR "missing register at operand 2" RET diff --git a/src/cmd/asm/internal/asm/testdata/loong64.s b/src/cmd/asm/internal/asm/testdata/loong64.s new file mode 100644 index 00000000000000..133cf48db420cc --- /dev/null +++ b/src/cmd/asm/internal/asm/testdata/loong64.s @@ -0,0 +1,11 @@ +// Copyright 2022 The Go 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 "../../../../../runtime/textflag.h" +// TODO: cover more instruction + +TEXT foo(SB),DUPOK|NOSPLIT,$0 + JAL 1(PC) //CALL 1(PC) //000c0054 + JAL (R4) //CALL (R4) //8100004c + JAL foo(SB) //CALL foo(SB) //00100054 diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s new file mode 100644 index 00000000000000..83bb6ec078d22d --- /dev/null +++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s @@ -0,0 +1,220 @@ +// Copyright 2022 The Go 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 "../../../../../runtime/textflag.h" + +TEXT asmtest(SB),DUPOK|NOSPLIT,$0 +lable1: + BFPT 1(PC) // 00050048 + BFPT lable1 // BFPT 2 //1ffdff4b + +lable2: + BFPF 1(PC) // 00040048 + BFPF lable2 // BFPF 4 // 1ffcff4b + + JMP foo(SB) // 00100050 + JMP (R4) // 8000004c + JMP 1(PC) // 00040058 + MOVW $65536, R4 // 04020014 + MOVW $4096, R4 // 24000014 + MOVV $65536, R4 // 04020014 + MOVV $4096, R4 // 24000014 + MOVW R4, R5 // 85001700 + MOVV R4, R5 // 85001500 + MOVBU R4, R5 // 85fc4303 + SUB R4, R5, R6 // a6101100 + SUBV R4, R5, R6 // a6901100 + ADD R4, R5, R6 // a6101000 + ADDV R4, R5, R6 // a6901000 + AND R4, R5, R6 // a6901400 + SUB R4, R5 // a5101100 + SUBV R4, R5 // a5901100 + ADD R4, R5 // a5101000 + ADDV R4, R5 // a5901000 + AND R4, R5 // a5901400 + NEGW R4, R5 // 05101100 + NEGV R4, R5 // 05901100 + SLL R4, R5 // a5101700 + SLL R4, R5, R6 // a6101700 + SRL R4, R5 // a5901700 + SRL R4, R5, R6 // a6901700 + SRA R4, R5 // a5101800 + SRA R4, R5, R6 // a6101800 + ROTR R4, R5 // a5101b00 + ROTR R4, R5, R6 // a6101b00 + SLLV R4, R5 // a5901800 + SLLV R4, R5, R6 // a6901800 + ROTRV R4, R5 // a5901b00 + ROTRV R4, R5, R6 // a6901b00 + CLO R4, R5 // 85100000 + CLZ R4, R5 // 85140000 + ADDF F4, F5 // a5900001 + ADDF F4, R5, F6 // a6900001 + CMPEQF F4, R5 // a010120c + ABSF F4, F5 // 85041401 + MOVVF F4, F5 // 85181d01 + MOVF F4, F5 // 85941401 + MOVD F4, F5 // 85981401 + MOVW R4, result+16(FP) // 64608029 + MOVWU R4, result+16(FP) // 64608029 + MOVV R4, result+16(FP) // 6460c029 + MOVB R4, result+16(FP) // 64600029 + MOVBU R4, result+16(FP) // 64600029 + MOVWL R4, result+16(FP) // 6460002f + MOVVL R4, result+16(FP) // 6460802f + MOVW R4, 1(R5) // a4048029 + MOVWU R4, 1(R5) // a4048029 + MOVV R4, 1(R5) // a404c029 + MOVB R4, 1(R5) // a4040029 + MOVBU R4, 1(R5) // a4040029 + MOVWL R4, 1(R5) // a404002f + MOVVL R4, 1(R5) // a404802f + SC R4, 1(R5) // a4040021 + SCV R4, 1(R5) // a4040023 + MOVW y+8(FP), R4 // 64408028 + MOVWU y+8(FP), R4 // 6440802a + MOVV y+8(FP), R4 // 6440c028 + MOVB y+8(FP), R4 // 64400028 + MOVBU y+8(FP), R4 // 6440002a + MOVWL y+8(FP), R4 // 6440002e + MOVVL y+8(FP), R4 // 6440802e + MOVW 1(R5), R4 // a4048028 + MOVWU 1(R5), R4 // a404802a + MOVV 1(R5), R4 // a404c028 + MOVB 1(R5), R4 // a4040028 + MOVBU 1(R5), R4 // a404002a + MOVWL 1(R5), R4 // a404002e + MOVVL 1(R5), R4 // a404802e + LL 1(R5), R4 // a4040020 + LLV 1(R5), R4 // a4040022 + MOVW $4(R4), R5 // 8510c002 + MOVV $4(R4), R5 // 8510c002 + MOVW $-1, R4 // 04fcff02 + MOVV $-1, R4 // 04fcff02 + MOVW $1, R4 // 0404c002 + MOVV $1, R4 // 0404c002 + ADD $-1, R4, R5 // 85fcbf02 + ADD $-1, R4 // 84fcbf02 + ADDV $-1, R4, R5 // 85fcff02 + ADDV $-1, R4 // 84fcff02 + AND $1, R4, R5 // 85044003 + AND $1, R4 // 84044003 + SLL $4, R4, R5 // 85904000 + SLL $4, R4 // 84904000 + SRL $4, R4, R5 // 85904400 + SRL $4, R4 // 84904400 + SRA $4, R4, R5 // 85904800 + SRA $4, R4 // 84904800 + ROTR $4, R4, R5 // 85904c00 + ROTR $4, R4 // 84904c00 + SLLV $4, R4, R5 // 85104100 + SLLV $4, R4 // 84104100 + ROTRV $4, R4, R5 // 85104d00 + ROTRV $4, R4 // 84104d00 + SYSCALL // 00002b00 + BEQ R4, R5, 1(PC) // 85040058 + BEQ R4, 1(PC) // 80040058 + BLTU R4, 1(PC) // 80040068 + MOVW y+8(FP), F4 // 6440002b + MOVF y+8(FP), F4 // 6440002b + MOVD y+8(FP), F4 // 6440802b + MOVW 1(F5), F4 // a404002b + MOVF 1(F5), F4 // a404002b + MOVD 1(F5), F4 // a404802b + MOVW F4, result+16(FP) // 6460402b + MOVF F4, result+16(FP) // 6460402b + MOVD F4, result+16(FP) // 6460c02b + MOVW F4, 1(F5) // a404402b + MOVF F4, 1(F5) // a404402b + MOVD F4, 1(F5) // a404c02b + MOVW R4, F5 // 85a41401 + MOVW F4, R5 // 85b41401 + MOVV R4, F5 // 85a81401 + MOVV F4, R5 // 85b81401 + WORD $74565 // 45230100 + BREAK R4, result+16(FP) // 64600006 + BREAK R4, 1(R5) // a4040006 + BREAK // 00002a00 + UNDEF // 00002a00 + + // mul + MUL R4, R5 // a5101c00 + MUL R4, R5, R6 // a6101c00 + MULV R4, R5 // a5901d00 + MULV R4, R5, R6 // a6901d00 + MULVU R4, R5 // a5901d00 + MULVU R4, R5, R6 // a6901d00 + MULHV R4, R5 // a5101e00 + MULHV R4, R5, R6 // a6101e00 + MULHVU R4, R5 // a5901e00 + MULHVU R4, R5, R6 // a6901e00 + REMV R4, R5 // a5902200 + REMV R4, R5, R6 // a6902200 + REMVU R4, R5 // a5902300 + REMVU R4, R5, R6 // a6902300 + DIVV R4, R5 // a5102200 + DIVV R4, R5, R6 // a6102200 + DIVVU R4, R5 // a5102300 + DIVVU R4, R5, R6 // a6102300 + + MOVH R4, result+16(FP) // 64604029 + MOVH R4, 1(R5) // a4044029 + MOVH y+8(FP), R4 // 64404028 + MOVH 1(R5), R4 // a4044028 + MOVHU R4, R5 // 8500cf00 + MOVHU R4, result+16(FP) // 64604029 + MOVHU R4, 1(R5) // a4044029 + MOVHU y+8(FP), R4 // 6440402a + MOVHU 1(R5), R4 // a404402a + MULU R4, R5 // a5101c00 + MULU R4, R5, R6 // a6101c00 + MULH R4, R5 // a5901c00 + MULH R4, R5, R6 // a6901c00 + MULHU R4, R5 // a5101d00 + MULHU R4, R5, R6 // a6101d00 + REM R4, R5 // a5902000 + REM R4, R5, R6 // a6902000 + REMU R4, R5 // a5902100 + REMU R4, R5, R6 // a6902100 + DIV R4, R5 // a5102000 + DIV R4, R5, R6 // a6102000 + DIVU R4, R5 // a5102100 + DIVU R4, R5, R6 // a6102100 + SRLV R4, R5 // a5101900 + SRLV R4, R5, R6 // a6101900 + SRLV $4, R4, R5 // 85104500 + SRLV $4, R4 // 84104500 + SRLV $32, R4, R5 // 85804500 + SRLV $32, R4 // 84804500 + + MASKEQZ R4, R5, R6 // a6101300 + MASKNEZ R4, R5, R6 // a6901300 + + MOVFD F4, F5 // 85241901 + MOVDF F4, F5 // 85181901 + MOVWF F4, F5 // 85101d01 + MOVFW F4, F5 // 85041b01 + MOVWD F4, F5 // 85201d01 + MOVDW F4, F5 // 85081b01 + NEGF F4, F5 // 85141401 + NEGD F4, F5 // 85181401 + ABSD F4, F5 // 85081401 + TRUNCDW F4, F5 // 85881a01 + TRUNCFW F4, F5 // 85841a01 + SQRTF F4, F5 // 85441401 + SQRTD F4, F5 // 85481401 + + DBAR // 00007238 + NOOP // 00004003 + + MOVWR R4, result+16(FP) // 6460402f + MOVWR R4, 1(R5) // a404402f + MOVWR y+8(FP), R4 // 6440402e + MOVWR 1(R5), R4 // a404402e + + CMPGTF F4, R5 // a090110c + CMPGTD F4, R5 // a090210c + CMPGEF F4, R5 // a090130c + CMPGED F4, R5 // a090230c + CMPEQD F4, R5 // a010220c diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc2.s b/src/cmd/asm/internal/asm/testdata/loong64enc2.s new file mode 100644 index 00000000000000..3b5e3cb81ae897 --- /dev/null +++ b/src/cmd/asm/internal/asm/testdata/loong64enc2.s @@ -0,0 +1,82 @@ +// Copyright 2022 The Go 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 "../../../../../runtime/textflag.h" + +TEXT asmtest(SB),DUPOK|NOSPLIT,$0 + MOVB R4, R5 // 85e04000a5e04800 + MOVWU R4, R5 // 85804100a5804500 + MOVW $74565, R4 // 4402001484148d03 + MOVW $4097, R4 // 2400001484048003 + MOVV $74565, R4 // 4402001484148d03 + MOVV $4097, R4 // 2400001484048003 + AND $-1, R4, R5 // 1efcbf0285f81400 + AND $-1, R4 // 1efcbf0284f81400 + MOVW $-1, F4 // 1efcbf02c4a71401 + MOVW $1, F4 // 1e048002c4a71401 + TEQ $4, R4, R5 // 8508005c04002a00 + TEQ $4, R4 // 0408005c04002a00 + TNE $4, R4, R5 // 8508005804002a00 + TNE $4, R4 // 0408005804002a00 + ADD $65536, R4, R5 // 1e02001485781000 + ADD $4096, R4, R5 // 3e00001485781000 + ADD $65536, R4 // 1e02001484781000 + ADD $4096, R4 // 3e00001484781000 + ADDV $65536, R4, R5 // 1e02001485f81000 + ADDV $4096, R4, R5 // 3e00001485f81000 + ADDV $65536, R4 // 1e02001484f81000 + ADDV $4096, R4 // 3e00001484f81000 + AND $65536, R4, R5 // 1e02001485f81400 + AND $4096, R4, R5 // 3e00001485f81400 + AND $65536, R4 // 1e02001484f81400 + AND $4096, R4 // 3e00001484f81400 + SGT $65536, R4, R5 // 1e02001485781200 + SGT $4096, R4, R5 // 3e00001485781200 + SGT $65536, R4 // 1e02001484781200 + SGT $4096, R4 // 3e00001484781200 + SGTU $65536, R4, R5 // 1e02001485f81200 + SGTU $4096, R4, R5 // 3e00001485f81200 + SGTU $65536, R4 // 1e02001484f81200 + SGTU $4096, R4 // 3e00001484f81200 + ADDU $65536, R4, R5 // 1e02001485781000 + ADDU $4096, R4, R5 // 3e00001485781000 + ADDU $65536, R4 // 1e02001484781000 + ADDU $4096, R4 // 3e00001484781000 + ADDVU $65536, R4, R5 // 1e02001485f81000 + ADDVU $4096, R4, R5 // 3e00001485f81000 + ADDVU $65536, R4 // 1e02001484f81000 + ADDVU $4096, R4 // 3e00001484f81000 + OR $65536, R4, R5 // 1e02001485781500 + OR $4096, R4, R5 // 3e00001485781500 + OR $65536, R4 // 1e02001484781500 + OR $4096, R4 // 3e00001484781500 + OR $-1, R4, R5 // 1efcbf0285781500 + OR $-1, R4 // 1efcbf0284781500 + XOR $65536, R4, R5 // 1e02001485f81500 + XOR $4096, R4, R5 // 3e00001485f81500 + XOR $65536, R4 // 1e02001484f81500 + XOR $4096, R4 // 3e00001484f81500 + XOR $-1, R4, R5 // 1efcbf0285f81500 + XOR $-1, R4 // 1efcbf0284f81500 + MOVH R4, R5 // 85c04000a5c04800 + + // relocation instructions + MOVW R4, name(SB) // 1e00001cc4038029 + MOVWU R4, name(SB) // 1e00001cc4038029 + MOVV R4, name(SB) // 1e00001cc403c029 + MOVB R4, name(SB) // 1e00001cc4030029 + MOVBU R4, name(SB) // 1e00001cc4030029 + MOVF F4, name(SB) // 1e00001cc403402b + MOVD F4, name(SB) // 1e00001cc403c02b + MOVW name(SB), R4 // 1e00001cc4038028 + MOVWU name(SB), R4 // 1e00001cc403802a + MOVV name(SB), R4 // 1e00001cc403c028 + MOVB name(SB), R4 // 1e00001cc4030028 + MOVBU name(SB), R4 // 1e00001cc403002a + MOVF name(SB), F4 // 1e00001cc403002b + MOVD name(SB), F4 // 1e00001cc403802b + MOVH R4, name(SB) // 1e00001cc4034029 + MOVH name(SB), R4 // 1e00001cc4034028 + MOVHU R4, name(SB) // 1e00001cc4034029 + MOVHU name(SB), R4 // 1e00001cc403402a diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc3.s b/src/cmd/asm/internal/asm/testdata/loong64enc3.s new file mode 100644 index 00000000000000..eceb0d71d001c6 --- /dev/null +++ b/src/cmd/asm/internal/asm/testdata/loong64enc3.s @@ -0,0 +1,131 @@ +// Copyright 2022 The Go 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 "../../../../../runtime/textflag.h" + +TEXT asmtest(SB),DUPOK|NOSPLIT,$0 + MOVW $65536(R4), R5 // 1e020014de03800385f81000 + MOVW $4096(R4), R5 // 3e000014de03800385f81000 + MOVV $65536(R4), R5 // 1e020014de03800385f81000 + MOVV $4096(R4), R5 // 3e000014de03800385f81000 + ADD $74565, R4 // 5e020014de178d0384781000 + ADD $4097, R4 // 3e000014de07800384781000 + ADDV $74565, R4 // 5e020014de178d0384f81000 + ADDV $4097, R4 // 3e000014de07800384f81000 + AND $74565, R4 // 5e020014de178d0384f81400 + AND $4097, R4 // 3e000014de07800384f81400 + ADD $74565, R4, R5 // 5e020014de178d0385781000 + ADD $4097, R4, R5 // 3e000014de07800385781000 + ADDV $74565, R4, R5 // 5e020014de178d0385f81000 + ADDV $4097, R4, R5 // 3e000014de07800385f81000 + AND $74565, R4, R5 // 5e020014de178d0385f81400 + AND $4097, R4, R5 // 3e000014de07800385f81400 + + MOVW R4, result+65540(FP) // 1e020014de8f1000c4338029 + MOVW R4, result+4097(FP) // 3e000014de8f1000c4278029 + MOVWU R4, result+65540(FP) // 1e020014de8f1000c4338029 + MOVWU R4, result+4097(FP) // 3e000014de8f1000c4278029 + MOVV R4, result+65540(FP) // 1e020014de8f1000c433c029 + MOVV R4, result+4097(FP) // 3e000014de8f1000c427c029 + MOVB R4, result+65540(FP) // 1e020014de8f1000c4330029 + MOVB R4, result+4097(FP) // 3e000014de8f1000c4270029 + MOVBU R4, result+65540(FP) // 1e020014de8f1000c4330029 + MOVBU R4, result+4097(FP) // 3e000014de8f1000c4270029 + MOVW R4, 65536(R5) // 1e020014de971000c4038029 + MOVW R4, 4096(R5) // 3e000014de971000c4038029 + MOVWU R4, 65536(R5) // 1e020014de971000c4038029 + MOVWU R4, 4096(R5) // 3e000014de971000c4038029 + MOVV R4, 65536(R5) // 1e020014de971000c403c029 + MOVV R4, 4096(R5) // 3e000014de971000c403c029 + MOVB R4, 65536(R5) // 1e020014de971000c4030029 + MOVB R4, 4096(R5) // 3e000014de971000c4030029 + MOVBU R4, 65536(R5) // 1e020014de971000c4030029 + MOVBU R4, 4096(R5) // 3e000014de971000c4030029 + SC R4, 65536(R5) // 1e020014de971000c4030021 + SC R4, 4096(R5) // 3e000014de971000c4030021 + MOVW y+65540(FP), R4 // 1e020014de8f1000c4338028 + MOVWU y+65540(FP), R4 // 1e020014de8f1000c433802a + MOVV y+65540(FP), R4 // 1e020014de8f1000c433c028 + MOVB y+65540(FP), R4 // 1e020014de8f1000c4330028 + MOVBU y+65540(FP), R4 // 1e020014de8f1000c433002a + MOVW y+4097(FP), R4 // 3e000014de8f1000c4278028 + MOVWU y+4097(FP), R4 // 3e000014de8f1000c427802a + MOVV y+4097(FP), R4 // 3e000014de8f1000c427c028 + MOVB y+4097(FP), R4 // 3e000014de8f1000c4270028 + MOVBU y+4097(FP), R4 // 3e000014de8f1000c427002a + MOVW 65536(R5), R4 // 1e020014de971000c4038028 + MOVWU 65536(R5), R4 // 1e020014de971000c403802a + MOVV 65536(R5), R4 // 1e020014de971000c403c028 + MOVB 65536(R5), R4 // 1e020014de971000c4030028 + MOVBU 65536(R5), R4 // 1e020014de971000c403002a + MOVW 4096(R5), R4 // 3e000014de971000c4038028 + MOVWU 4096(R5), R4 // 3e000014de971000c403802a + MOVV 4096(R5), R4 // 3e000014de971000c403c028 + MOVB 4096(R5), R4 // 3e000014de971000c4030028 + MOVBU 4096(R5), R4 // 3e000014de971000c403002a + MOVW y+65540(FP), F4 // 1e020014de8f1000c433002b + MOVF y+65540(FP), F4 // 1e020014de8f1000c433002b + MOVD y+65540(FP), F4 // 1e020014de8f1000c433802b + MOVW y+4097(FP), F4 // 3e000014de8f1000c427002b + MOVF y+4097(FP), F4 // 3e000014de8f1000c427002b + MOVD y+4097(FP), F4 // 3e000014de8f1000c427802b + MOVW 65536(R5), F4 // 1e020014de971000c403002b + MOVF 65536(R5), F4 // 1e020014de971000c403002b + MOVD 65536(R5), F4 // 1e020014de971000c403802b + MOVW 4096(R5), F4 // 3e000014de971000c403002b + MOVF 4096(R5), F4 // 3e000014de971000c403002b + MOVD 4096(R5), F4 // 3e000014de971000c403802b + MOVW F4, result+65540(FP) // 1e020014de8f1000c433402b + MOVF F4, result+65540(FP) // 1e020014de8f1000c433402b + MOVD F4, result+65540(FP) // 1e020014de8f1000c433c02b + MOVW F4, result+4097(FP) // 3e000014de8f1000c427402b + MOVF F4, result+4097(FP) // 3e000014de8f1000c427402b + MOVD F4, result+4097(FP) // 3e000014de8f1000c427c02b + MOVW F4, 65536(R5) // 1e020014de971000c403402b + MOVF F4, 65536(R5) // 1e020014de971000c403402b + MOVD F4, 65536(R5) // 1e020014de971000c403c02b + MOVW F4, 4096(R5) // 3e000014de971000c403402b + MOVF F4, 4096(R5) // 3e000014de971000c403402b + MOVD F4, 4096(R5) // 3e000014de971000c403c02b + + MOVH R4, result+65540(FP) // 1e020014de8f1000c4334029 + MOVH R4, 65536(R5) // 1e020014de971000c4034029 + MOVH y+65540(FP), R4 // 1e020014de8f1000c4334028 + MOVH 65536(R5), R4 // 1e020014de971000c4034028 + MOVH R4, result+4097(FP) // 3e000014de8f1000c4274029 + MOVH R4, 4096(R5) // 3e000014de971000c4034029 + MOVH y+4097(FP), R4 // 3e000014de8f1000c4274028 + MOVH 4096(R5), R4 // 3e000014de971000c4034028 + MOVHU R4, result+65540(FP) // 1e020014de8f1000c4334029 + MOVHU R4, 65536(R5) // 1e020014de971000c4034029 + MOVHU y+65540(FP), R4 // 1e020014de8f1000c433402a + MOVHU 65536(R5), R4 // 1e020014de971000c403402a + MOVHU R4, result+4097(FP) // 3e000014de8f1000c4274029 + MOVHU R4, 4096(R5) // 3e000014de971000c4034029 + MOVHU y+4097(FP), R4 // 3e000014de8f1000c427402a + MOVHU 4096(R5), R4 // 3e000014de971000c403402a + SGT $74565, R4 // 5e020014de178d0384781200 + SGT $74565, R4, R5 // 5e020014de178d0385781200 + SGT $4097, R4 // 3e000014de07800384781200 + SGT $4097, R4, R5 // 3e000014de07800385781200 + SGTU $74565, R4 // 5e020014de178d0384f81200 + SGTU $74565, R4, R5 // 5e020014de178d0385f81200 + SGTU $4097, R4 // 3e000014de07800384f81200 + SGTU $4097, R4, R5 // 3e000014de07800385f81200 + ADDU $74565, R4 // 5e020014de178d0384781000 + ADDU $74565, R4, R5 // 5e020014de178d0385781000 + ADDU $4097, R4 // 3e000014de07800384781000 + ADDU $4097, R4, R5 // 3e000014de07800385781000 + ADDVU $4097, R4 // 3e000014de07800384f81000 + ADDVU $4097, R4, R5 // 3e000014de07800385f81000 + ADDVU $74565, R4 // 5e020014de178d0384f81000 + ADDVU $74565, R4, R5 // 5e020014de178d0385f81000 + OR $74565, R4 // 5e020014de178d0384781500 + OR $74565, R4, R5 // 5e020014de178d0385781500 + OR $4097, R4 // 3e000014de07800384781500 + OR $4097, R4, R5 // 3e000014de07800385781500 + XOR $74565, R4 // 5e020014de178d0384f81500 + XOR $74565, R4, R5 // 5e020014de178d0385f81500 + XOR $4097, R4 // 3e000014de07800384f81500 + XOR $4097, R4, R5 // 3e000014de07800385f81500 diff --git a/src/cmd/asm/internal/asm/testdata/mips64.s b/src/cmd/asm/internal/asm/testdata/mips64.s index 99044d89f7be33..8f628e26c9a563 100644 --- a/src/cmd/asm/internal/asm/testdata/mips64.s +++ b/src/cmd/asm/internal/asm/testdata/mips64.s @@ -21,9 +21,9 @@ label0: BEQ R1, 2(PC) JMP label0+0 // JMP 3 // 1000fffd BEQ R1, 2(PC) - JAL 1(PC) // CALL 1(PC) // 0c00000e + JAL 1(PC) // CALL 1(PC) // 0c00000f BEQ R1, 2(PC) - JAL label0+0 // CALL 3 // 0c000006 + JAL label0+0 // CALL 3 // 0c000007 // LBRA addr // { @@ -32,11 +32,11 @@ label0: BEQ R1, 2(PC) JMP 0(R1) // JMP (R1) // 00200008 BEQ R1, 2(PC) - JMP foo+0(SB) // JMP foo(SB) // 08000018 + JMP foo+0(SB) // JMP foo(SB) // 08000019 BEQ R1, 2(PC) JAL 0(R1) // CALL (R1) // 0020f809 BEQ R1, 2(PC) - JAL foo+0(SB) // CALL foo(SB) // 0c000020 + JAL foo+0(SB) // CALL foo(SB) // 0c000021 // // BEQ/BNE diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s index b6c0aa5035c013..2e086056d7e8a0 100644 --- a/src/cmd/asm/internal/asm/testdata/ppc64.s +++ b/src/cmd/asm/internal/asm/testdata/ppc64.s @@ -8,6 +8,10 @@ #include "../../../../../runtime/textflag.h" +// In case of index mode instructions, usage of +// (Rx)(R0) is equivalent to (Rx+R0) +// In case of base+displacement mode instructions if +// the offset is 0, usage of (Rx) is equivalent to 0(Rx) TEXT asmtest(SB),DUPOK|NOSPLIT,$0 // move constants MOVD $1, R3 // 38600001 @@ -24,69 +28,124 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 MOVW $-32767, R5 // 38a08001 MOVW $-32768, R6 // 38c08000 MOVW $1234567, R5 // 6405001260a5d687 - MOVD 8(R3), R4 // e8830008 + MOVD 8(R3), R4 // e8830008 MOVD (R3)(R4), R5 // 7ca4182a + MOVD (R3)(R0), R5 // 7ca0182a + MOVD (R3), R5 // e8a30000 MOVW 4(R3), R4 // e8830006 MOVW (R3)(R4), R5 // 7ca41aaa + MOVW (R3)(R0), R5 // 7ca01aaa + MOVW (R3), R5 // e8a30002 MOVWZ 4(R3), R4 // 80830004 MOVWZ (R3)(R4), R5 // 7ca4182e + MOVWZ (R3)(R0), R5 // 7ca0182e + MOVWZ (R3), R5 // 80a30000 MOVH 4(R3), R4 // a8830004 MOVH (R3)(R4), R5 // 7ca41aae + MOVH (R3)(R0), R5 // 7ca01aae + MOVH (R3), R5 // a8a30000 + MOVHZ 2(R3), R4 // a0830002 MOVHZ (R3)(R4), R5 // 7ca41a2e + MOVHZ (R3)(R0), R5 // 7ca01a2e + MOVHZ (R3), R5 // a0a30000 MOVB 1(R3), R4 // 888300017c840774 MOVB (R3)(R4), R5 // 7ca418ae7ca50774 + MOVB (R3)(R0), R5 // 7ca018ae7ca50774 + MOVB (R3), R5 // 88a300007ca50774 MOVBZ 1(R3), R4 // 88830001 MOVBZ (R3)(R4), R5 // 7ca418ae + MOVBZ (R3)(R0), R5 // 7ca018ae + MOVBZ (R3), R5 // 88a30000 MOVDBR (R3)(R4), R5 // 7ca41c28 + MOVDBR (R3)(R0), R5 // 7ca01c28 + MOVDBR (R3), R5 // 7ca01c28 MOVWBR (R3)(R4), R5 // 7ca41c2c + MOVWBR (R3)(R0), R5 // 7ca01c2c + MOVWBR (R3), R5 // 7ca01c2c MOVHBR (R3)(R4), R5 // 7ca41e2c - MOVD $foo+4009806848(FP), R5 // 3ca1ef0138a5cc20 + MOVHBR (R3)(R0), R5 // 7ca01e2c + MOVHBR (R3), R5 // 7ca01e2c + MOVD $foo+4009806848(FP), R5 // 3ca1ef0138a5cc40 MOVD $foo(SB), R5 // 3ca0000038a50000 MOVDU 8(R3), R4 // e8830009 MOVDU (R3)(R4), R5 // 7ca4186a + MOVDU (R3)(R0), R5 // 7ca0186a + MOVDU (R3), R5 // e8a30001 MOVWU (R3)(R4), R5 // 7ca41aea + MOVWU (R3)(R0), R5 // 7ca01aea MOVWZU 4(R3), R4 // 84830004 MOVWZU (R3)(R4), R5 // 7ca4186e + MOVWZU (R3)(R0), R5 // 7ca0186e + MOVWZU (R3), R5 // 84a30000 MOVHU 2(R3), R4 // ac830002 MOVHU (R3)(R4), R5 // 7ca41aee + MOVHU (R3)(R0), R5 // 7ca01aee + MOVHU (R3), R5 // aca30000 MOVHZU 2(R3), R4 // a4830002 MOVHZU (R3)(R4), R5 // 7ca41a6e + MOVHZU (R3)(R0), R5 // 7ca01a6e + MOVHZU (R3), R5 // a4a30000 MOVBU 1(R3), R4 // 8c8300017c840774 MOVBU (R3)(R4), R5 // 7ca418ee7ca50774 + MOVBU (R3)(R0), R5 // 7ca018ee7ca50774 + MOVBU (R3), R5 // 8ca300007ca50774 MOVBZU 1(R3), R4 // 8c830001 MOVBZU (R3)(R4), R5 // 7ca418ee + MOVBZU (R3)(R0), R5 // 7ca018ee + MOVBZU (R3), R5 // 8ca30000 MOVD R4, 8(R3) // f8830008 MOVD R5, (R3)(R4) // 7ca4192a + MOVD R5, (R3)(R0) // 7ca0192a + MOVD R5, (R3) // f8a30000 MOVW R4, 4(R3) // 90830004 MOVW R5, (R3)(R4) // 7ca4192e + MOVW R5, (R3)(R0) // 7ca0192e + MOVW R5, (R3) // 90a30000 MOVH R4, 2(R3) // b0830002 MOVH R5, (R3)(R4) // 7ca41b2e + MOVH R5, (R3)(R0) // 7ca01b2e + MOVH R5, (R3) // b0a30000 MOVB R4, 1(R3) // 98830001 MOVB R5, (R3)(R4) // 7ca419ae + MOVB R5, (R3)(R0) // 7ca019ae + MOVB R5, (R3) // 98a30000 MOVDBR R5, (R3)(R4) // 7ca41d28 + MOVDBR R5, (R3)(R0) // 7ca01d28 + MOVDBR R5, (R3) // 7ca01d28 MOVWBR R5, (R3)(R4) // 7ca41d2c + MOVWBR R5, (R3)(R0) // 7ca01d2c + MOVWBR R5, (R3) // 7ca01d2c MOVHBR R5, (R3)(R4) // 7ca41f2c + MOVHBR R5, (R3)(R0) // 7ca01f2c + MOVHBR R5, (R3) // 7ca01f2c MOVDU R4, 8(R3) // f8830009 MOVDU R5, (R3)(R4) // 7ca4196a + MOVDU R5, (R3)(R0) // 7ca0196a + MOVDU R5, (R3) // f8a30001 MOVWU R4, 4(R3) // 94830004 MOVWU R5, (R3)(R4) // 7ca4196e + MOVWU R5, (R3)(R0) // 7ca0196e MOVHU R4, 2(R3) // b4830002 MOVHU R5, (R3)(R4) // 7ca41b6e + MOVHU R5, (R3)(R0) // 7ca01b6e + MOVHU R5, (R3) // b4a30000 MOVBU R4, 1(R3) // 9c830001 MOVBU R5, (R3)(R4) // 7ca419ee - - MOVB $0, R4 // 38800000 - MOVBZ $0, R4 // 38800000 - MOVH $0, R4 // 38800000 - MOVHZ $0, R4 // 38800000 - MOVW $0, R4 // 38800000 - MOVWZ $0, R4 // 38800000 - MOVD $0, R4 // 38800000 - MOVD $0, R0 // 38000000 + MOVBU R5, (R3)(R0) // 7ca019ee + MOVBU R5, (R3) // 9ca30000 + + MOVB $0, R4 // 38800000 + MOVBZ $0, R4 // 38800000 + MOVH $0, R4 // 38800000 + MOVHZ $0, R4 // 38800000 + MOVW $0, R4 // 38800000 + MOVWZ $0, R4 // 38800000 + MOVD $0, R4 // 38800000 + MOVD $0, R0 // 38000000 ADD $1, R3 // 38630001 ADD $1, R3, R4 // 38830001 @@ -103,6 +162,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 ADD $1234567, R5 // 641f001263ffd6877cbf2a14 ADD $1234567, R5, R6 // 641f001263ffd6877cdf2a14 ADDEX R3, R5, $3, R6 // 7cc32f54 + ADDEX R3, $3, R5, R6 // 7cc32f54 ADDIS $8, R3 // 3c630008 ADDIS $1000, R3, R4 // 3c8303e8 @@ -342,20 +402,27 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 NOP F2 NOP $4 - CRAND CR1, CR2, CR3 // 4c620a02 - CRANDN CR1, CR2, CR3 // 4c620902 - CREQV CR1, CR2, CR3 // 4c620a42 - CRNAND CR1, CR2, CR3 // 4c6209c2 - CRNOR CR1, CR2, CR3 // 4c620842 - CROR CR1, CR2, CR3 // 4c620b82 - CRORN CR1, CR2, CR3 // 4c620b42 - CRXOR CR1, CR2, CR3 // 4c620982 + CRAND CR0GT, CR0EQ, CR0SO // 4c620a02 + CRANDN CR0GT, CR0EQ, CR0SO // 4c620902 + CREQV CR0GT, CR0EQ, CR0SO // 4c620a42 + CRNAND CR0GT, CR0EQ, CR0SO // 4c6209c2 + CRNOR CR0GT, CR0EQ, CR0SO // 4c620842 + CROR CR0GT, CR0EQ, CR0SO // 4c620b82 + CRORN CR0GT, CR0EQ, CR0SO // 4c620b42 + CRXOR CR0GT, CR0EQ, CR0SO // 4c620982 - ISEL $1, R3, R4, R5 // 7ca3205e ISEL $0, R3, R4, R5 // 7ca3201e + ISEL $1, R3, R4, R5 // 7ca3205e ISEL $2, R3, R4, R5 // 7ca3209e ISEL $3, R3, R4, R5 // 7ca320de ISEL $4, R3, R4, R5 // 7ca3211e + ISEL $31, R3, R4, R5 // 7ca327de + ISEL CR0LT, R3, R4, R5 // 7ca3201e + ISEL CR0GT, R3, R4, R5 // 7ca3205e + ISEL CR0EQ, R3, R4, R5 // 7ca3209e + ISEL CR0SO, R3, R4, R5 // 7ca320de + ISEL CR1LT, R3, R4, R5 // 7ca3211e + ISEL CR7SO, R3, R4, R5 // 7ca327de POPCNTB R3, R4 // 7c6400f4 POPCNTW R3, R4 // 7c6402f4 POPCNTD R3, R4 // 7c6403f4 @@ -365,23 +432,41 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 // load-and-reserve LBAR (R4)(R3*1),$1,R5 // 7ca32069 + LBAR (R4)(R0),$1,R5 // 7ca02069 LBAR (R4),$0,R5 // 7ca02068 LBAR (R3),R5 // 7ca01868 LHAR (R4)(R3*1),$1,R5 // 7ca320e9 + LHAR (R4)(R0),$1,R5 // 7ca020e9 LHAR (R4),$0,R5 // 7ca020e8 LHAR (R3),R5 // 7ca018e8 LWAR (R4)(R3*1),$1,R5 // 7ca32029 + LWAR (R4)(R0),$1,R5 // 7ca02029 LWAR (R4),$0,R5 // 7ca02028 LWAR (R3),R5 // 7ca01828 LDAR (R4)(R3*1),$1,R5 // 7ca320a9 + LDAR (R4)(R0),$1,R5 // 7ca020a9 LDAR (R4),$0,R5 // 7ca020a8 LDAR (R3),R5 // 7ca018a8 + LSW (R3)(R4), R5 // 7ca41c2a + LSW (R3)(R0), R5 // 7ca01c2a + LSW (R3), R5 // 7ca01c2a + STBCCC R3, (R4)(R5) // 7c65256d + STBCCC R3, (R4)(R0) // 7c60256d + STBCCC R3, (R4) // 7c60256d STWCCC R3, (R4)(R5) // 7c65212d + STWCCC R3, (R4)(R0) // 7c60212d + STWCCC R3, (R4) // 7c60212d STDCCC R3, (R4)(R5) // 7c6521ad - STHCCC R3, (R4)(R5) - STSW R3, (R4)(R5) + STDCCC R3, (R4)(R0) // 7c6021ad + STDCCC R3, (R4) // 7c6021ad + STHCCC R3, (R4)(R5) // 7c6525ad + STHCCC R3, (R4)(R0) // 7c6025ad + STHCCC R3, (R4) // 7c6025ad + STSW R3, (R4)(R5) // 7c65252a + STSW R3, (R4)(R0) // 7c60252a + STSW R3, (R4) // 7c60252a SYNC // 7c0004ac ISYNC // 4c00012c @@ -390,11 +475,21 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 DARN $1, R5 // 7ca105e6 DCBF (R3)(R4) // 7c0418ac - DCBI (R3)(R4) // 7c041bac + DCBF (R3)(R0) // 7c0018ac + DCBF (R3) // 7c0018ac + DCBST (R3)(R4) // 7c04186c + DCBST (R3)(R0) // 7c00186c + DCBST (R3) // 7c00186c DCBZ (R3)(R4) // 7c041fec + DCBZ (R3)(R0) // 7c001fec + DCBZ (R3) // 7c001fec DCBT (R3)(R4) // 7c041a2c + DCBT (R3)(R0) // 7c001a2c + DCBT (R3) // 7c001a2c ICBI (R3)(R4) // 7c041fac + ICBI (R3)(R0) // 7c001fac + ICBI (R3) // 7c001fac // float constants FMOVD $(0.0), F1 // f0210cd0 @@ -402,21 +497,46 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 FMOVD 8(R3), F1 // c8230008 FMOVD (R3)(R4), F1 // 7c241cae + FMOVD (R3)(R0), F1 // 7c201cae + FMOVD (R3), F1 // c8230000 FMOVDU 8(R3), F1 // cc230008 FMOVDU (R3)(R4), F1 // 7c241cee + FMOVDU (R3)(R0), F1 // 7c201cee + FMOVDU (R3), F1 // cc230000 FMOVS 4(R3), F1 // c0230004 FMOVS (R3)(R4), F1 // 7c241c2e + FMOVS (R3)(R0), F1 // 7c201c2e + FMOVS (R3), F1 // c0230000 FMOVSU 4(R3), F1 // c4230004 FMOVSU (R3)(R4), F1 // 7c241c6e + FMOVSU (R3)(R0), F1 // 7c201c6e + FMOVSU (R3), F1 // c4230000 + FMOVSX (R3)(R4), F1 // 7c241eae + FMOVSX (R3)(R0), F1 // 7c201eae + FMOVSX (R3), F1 // 7c201eae + FMOVSZ (R3)(R4), F1 // 7c241eee + FMOVSZ (R3)(R0), F1 // 7c201eee + FMOVSZ (R3), F1 // 7c201eee FMOVD F1, 8(R3) // d8230008 FMOVD F1, (R3)(R4) // 7c241dae + FMOVD F1, (R3)(R0) // 7c201dae + FMOVD F1, (R3) // d8230000 FMOVDU F1, 8(R3) // dc230008 FMOVDU F1, (R3)(R4) // 7c241dee + FMOVDU F1, (R3)(R0) // 7c201dee + FMOVDU F1, (R3) // dc230000 FMOVS F1, 4(R3) // d0230004 FMOVS F1, (R3)(R4) // 7c241d2e + FMOVS F1, (R3)(R0) // 7c201d2e + FMOVS F1, (R3) // d0230000 FMOVSU F1, 4(R3) // d4230004 FMOVSU F1, (R3)(R4) // 7c241d6e + FMOVSU F1, (R3)(R0) // 7c201d6e + FMOVSU F1, (R3) // d4230000 + FMOVSX F1, (R3)(R4) // 7c241fae + FMOVSX F1, (R3)(R0) // 7c201fae + FMOVSX F1, (R3) // 7c201fae FADD F1, F2 // fc42082a FADD F1, F2, F3 // fc62082a FADDCC F1, F2, F3 // fc62082b @@ -500,17 +620,41 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 FCMPO F1, F2 // fc011040 FCMPU F1, F2 // fc011000 LVX (R3)(R4), V1 // 7c2418ce + LVX (R3)(R0), V1 // 7c2018ce + LVX (R3), V1 // 7c2018ce LVXL (R3)(R4), V1 // 7c241ace + LVXL (R3)(R0), V1 // 7c201ace + LVXL (R3), V1 // 7c201ace LVSL (R3)(R4), V1 // 7c24180c + LVSL (R3)(R0), V1 // 7c20180c + LVSL (R3), V1 // 7c20180c LVSR (R3)(R4), V1 // 7c24184c + LVSR (R3)(R0), V1 // 7c20184c + LVSR (R3), V1 // 7c20184c LVEBX (R3)(R4), V1 // 7c24180e + LVEBX (R3)(R0), V1 // 7c20180e + LVEBX (R3), V1 // 7c20180e LVEHX (R3)(R4), V1 // 7c24184e + LVEHX (R3)(R0), V1 // 7c20184e + LVEHX (R3), V1 // 7c20184e LVEWX (R3)(R4), V1 // 7c24188e + LVEWX (R3)(R0), V1 // 7c20188e + LVEWX (R3), V1 // 7c20188e STVX V1, (R3)(R4) // 7c2419ce + STVX V1, (R3)(R0) // 7c2019ce + STVX V1, (R3) // 7c2019ce STVXL V1, (R3)(R4) // 7c241bce + STVXL V1, (R3)(R0) // 7c201bce + STVXL V1, (R3) // 7c201bce STVEBX V1, (R3)(R4) // 7c24190e + STVEBX V1, (R3)(R0) // 7c20190e + STVEBX V1, (R3) // 7c20190e STVEHX V1, (R3)(R4) // 7c24194e + STVEHX V1, (R3)(R0) // 7c20194e + STVEHX V1, (R3) // 7c20194e STVEWX V1, (R3)(R4) // 7c24198e + STVEWX V1, (R3)(R0) // 7c20198e + STVEWX V1, (R3) // 7c20198e VAND V1, V2, V3 // 10611404 VANDC V1, V2, V3 // 10611444 @@ -641,35 +785,71 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 VNCIPHERLAST V1, V2, V3 // 10611549 VSBOX V1, V2 // 104105c8 VSHASIGMAW $1, V1, $15, V2 // 10418e82 + VSHASIGMAW $1, $15, V1, V2 // 10418e82 VSHASIGMAD $2, V1, $15, V2 // 104196c2 + VSHASIGMAD $2, $15, V1, V2 // 104196c2 LXVD2X (R3)(R4), VS1 // 7c241e98 + LXVD2X (R3)(R0), VS1 // 7c201e98 + LXVD2X (R3), VS1 // 7c201e98 LXVDSX (R3)(R4), VS1 // 7c241a98 + LXVDSX (R3)(R0), VS1 // 7c201a98 + LXVDSX (R3), VS1 // 7c201a98 LXVH8X (R3)(R4), VS1 // 7c241e58 + LXVH8X (R3)(R0), VS1 // 7c201e58 + LXVH8X (R3), VS1 // 7c201e58 LXVB16X (R3)(R4), VS1 // 7c241ed8 + LXVB16X (R3)(R0), VS1 // 7c201ed8 + LXVB16X (R3), VS1 // 7c201ed8 LXVW4X (R3)(R4), VS1 // 7c241e18 + LXVW4X (R3)(R0), VS1 // 7c201e18 + LXVW4X (R3), VS1 // 7c201e18 LXV 16(R3), VS1 // f4230011 + LXV (R3), VS1 // f4230001 + LXV 16(R3), VS33 // f4230019 + LXV (R3), VS33 // f4230009 + LXV 16(R3), V1 // f4230019 + LXV (R3), V1 // f4230009 LXVL R3, R4, VS1 // 7c23221a LXVLL R3, R4, VS1 // 7c23225a LXVX R3, R4, VS1 // 7c232218 LXSDX (R3)(R4), VS1 // 7c241c98 + LXSDX (R3)(R0), VS1 // 7c201c98 + LXSDX (R3), VS1 // 7c201c98 STXVD2X VS1, (R3)(R4) // 7c241f98 + STXVD2X VS1, (R3)(R0) // 7c201f98 + STXVD2X VS1, (R3) // 7c201f98 STXV VS1,16(R3) // f4230015 + STXV VS1,(R3) // f4230005 STXVL VS1, R3, R4 // 7c23231a STXVLL VS1, R3, R4 // 7c23235a STXVX VS1, R3, R4 // 7c232318 STXVB16X VS1, (R4)(R5) // 7c2527d8 + STXVB16X VS1, (R4)(R0) // 7c2027d8 + STXVB16X VS1, (R4) // 7c2027d8 STXVH8X VS1, (R4)(R5) // 7c252758 - + STXVH8X VS1, (R4)(R0) // 7c202758 + STXVH8X VS1, (R4) // 7c202758 STXSDX VS1, (R3)(R4) // 7c241d98 + STXSDX VS1, (R4)(R0) // 7c202598 + STXSDX VS1, (R4) // 7c202598 LXSIWAX (R3)(R4), VS1 // 7c241898 + LXSIWAX (R3)(R0), VS1 // 7c201898 + LXSIWAX (R3), VS1 // 7c201898 STXSIWX VS1, (R3)(R4) // 7c241918 + STXSIWX VS1, (R3)(R0) // 7c201918 + STXSIWX VS1, (R3) // 7c201918 MFVSRD VS1, R3 // 7c230066 MTFPRD R3, F0 // 7c030166 MFVRD V0, R3 // 7c030067 MFVSRLD VS63,R4 // 7fe40267 + MFVSRLD V31,R4 // 7fe40267 MFVSRWZ VS33,R4 // 7c2400e7 + MFVSRWZ V1,R4 // 7c2400e7 MTVSRD R3, VS1 // 7c230166 + MTVSRDD R3, R4, VS1 // 7c232366 + MTVSRDD R3, R4, VS33 // 7c232367 + MTVSRDD R3, R4, V1 // 7c232367 MTVRD R3, V13 // 7da30167 MTVSRWA R4, VS31 // 7fe401a6 MTVSRWS R4, VS32 // 7c040327 @@ -678,6 +858,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 XXBRW VS1, VS2 // f04f0f6c XXBRH VS2, VS3 // f067176c XXLAND VS1, VS2, VS3 // f0611410 + XXLAND V1, V2, V3 // f0611417 + XXLAND VS33, VS34, VS35 // f0611417 XXLANDC VS1, VS2, VS3 // f0611450 XXLEQV VS0, VS1, VS2 // f0400dd0 XXLNAND VS0, VS1, VS2 // f0400d90 @@ -687,11 +869,21 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 XXLORQ VS1, VS2, VS3 // f0611490 XXLXOR VS1, VS2, VS3 // f06114d0 XXSEL VS1, VS2, VS3, VS4 // f08110f0 + XXSEL VS33, VS34, VS35, VS36 // f08110ff + XXSEL V1, V2, V3, V4 // f08110ff XXMRGHW VS1, VS2, VS3 // f0611090 XXMRGLW VS1, VS2, VS3 // f0611190 XXSPLTW VS1, $1, VS2 // f0410a90 + XXSPLTW VS33, $1, VS34 // f0410a93 + XXSPLTW V1, $1, V2 // f0410a93 XXPERM VS1, VS2, VS3 // f06110d0 XXSLDWI VS1, VS2, $1, VS3 // f0611110 + XXSLDWI V1, V2, $1, V3 // f0611117 + XXSLDWI V1, $1, V2, V3 // f0611117 + XXSLDWI VS33, VS34, $1, VS35 // f0611117 + XXSLDWI VS33, $1, VS34, VS35 // f0611117 + XXPERMDI VS33, VS34, $1, VS35 // f0611157 + XXPERMDI VS33, $1, VS34, VS35 // f0611157 XSCVDPSP VS1, VS2 // f0400c24 XVCVDPSP VS1, VS2 // f0400e24 XSCVSXDDP VS1, VS2 // f0400de0 @@ -736,4 +928,65 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 MOVD XER, R3 // 7c6102a6 MOVFL CR3, CR1 // 4c8c0000 + MOVW CR0, R1 // 7c380026 + MOVW CR7, R1 // 7c301026 + MOVW CR, R1 // 7c200026 + + MOVW R1, CR // 7c2ff120 + MOVFL R1, CR // 7c2ff120 + MOVW R1, CR2 // 7c320120 + MOVFL R1, CR2 // 7c320120 + MOVFL R1, $255 // 7c2ff120 + MOVFL R1, $1 // 7c301120 + MOVFL R1, $128 // 7c380120 + MOVFL R1, $3 // 7c203120 + + // Verify supported bdnz/bdz encodings. + BC 16,0,0(PC) // BC $16, CR0LT, 0(PC) // 42000000 + BDNZ 0(PC) // 42000000 + BDZ 0(PC) // 42400000 + BC 18,0,0(PC) // BC $18, CR0LT, 0(PC) // 42400000 + + // Verify the supported forms of bcclr[l] + BC $20,CR0LT,$1,LR // 4e800820 + BC $20,CR0LT,$0,LR // 4e800020 + BC $20,CR0LT,LR // 4e800020 + BC $20,CR0GT,LR // 4e810020 + BC 20,CR0LT,LR // BC $20,CR0LT,LR // 4e800020 + BC 20,undefined_symbol,LR // BC $20,CR0LT,LR // 4e800020 + BC 20,undefined_symbol+1,LR // BC $20,CR0GT,LR // 4e810020 + JMP LR // 4e800020 + BR LR // JMP LR // 4e800020 + BCL $20,CR0LT,$1,LR // 4e800821 + BCL $20,CR0LT,$0,LR // 4e800021 + BCL $20,CR0LT,LR // 4e800021 + BCL $20,CR0GT,LR // 4e810021 + BCL 20,CR0LT,LR // BCL $20,CR0LT,LR // 4e800021 + BCL 20,undefined_symbol,LR // BCL $20,CR0LT,LR // 4e800021 + BCL 20,undefined_symbol+1,LR // BCL $20,CR0GT,LR // 4e810021 + + // Verify the supported forms of bcctr[l] + BC $20,CR0LT,CTR // 4e800420 + BC $20,CR0GT,CTR // 4e810420 + BC 20,CR0LT,CTR // BC $20,CR0LT,CTR // 4e800420 + BC 20,undefined_symbol,CTR // BC $20,CR0LT,CTR // 4e800420 + BC 20,undefined_symbol+1,CTR // BC $20,CR0GT,CTR // 4e810420 + JMP CTR // 4e800420 + BR CTR // JMP CTR // 4e800420 + BCL $20,CR0LT,CTR // 4e800421 + BCL $20,CR0GT,CTR // 4e810421 + BCL 20,CR0LT,CTR // BCL $20,CR0LT,CTR // 4e800421 + BCL 20,undefined_symbol,CTR // BCL $20,CR0LT,CTR // 4e800421 + BCL 20,undefined_symbol+1,CTR // BCL $20,CR0GT,CTR // 4e810421 + + // Verify bc encoding (without pic enabled) + BC $16,CR0LT,0(PC) // 42000000 + BCL $16,CR0LT,0(PC) // 42000001 + BC $18,CR0LT,0(PC) // 42400000 + + MOVD SPR(3), 4(R1) // 7fe302a6fbe10004 + MOVD XER, 4(R1) // 7fe102a6fbe10004 + MOVD 4(R1), SPR(3) // ebe100047fe303a6 + MOVD 4(R1), XER // ebe100047fe103a6 + RET diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index 77c0764c48117a..79d60548695a12 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -10,20 +10,35 @@ start: // 2.4: Integer Computational Instructions - ADDI $2047, X5, X6 // 1383f27f - ADDI $-2048, X5, X6 // 13830280 ADDI $2047, X5 // 9382f27f ADDI $-2048, X5 // 93820280 + ADDI $2048, X5 // 9382024093820240 + ADDI $-2049, X5 // 938202c09382f2bf + ADDI $4094, X5 // 9382f27f9382f27f + ADDI $-4096, X5 // 9382028093820280 + ADDI $4095, X5 // b71f00009b8fffffb382f201 + ADDI $-4097, X5 // b7ffffff9b8fffffb382f201 + ADDI $2047, X5, X6 // 1383f27f + ADDI $-2048, X5, X6 // 13830280 + ADDI $2048, X5, X6 // 1383024013030340 + ADDI $-2049, X5, X6 // 138302c01303f3bf + ADDI $4094, X5, X6 // 1383f27f1303f37f + ADDI $-4096, X5, X6 // 1383028013030380 + ADDI $4095, X5, X6 // b71f00009b8fffff3383f201 + ADDI $-4097, X5, X6 // b7ffffff9b8fffff3383f201 SLTI $55, X5, X7 // 93a37203 SLTIU $55, X5, X7 // 93b37203 ANDI $1, X5, X6 // 13f31200 ANDI $1, X5 // 93f21200 + ANDI $2048, X5 // b71f00009b8f0f80b3f2f201 ORI $1, X5, X6 // 13e31200 ORI $1, X5 // 93e21200 + ORI $2048, X5 // b71f00009b8f0f80b3e2f201 XORI $1, X5, X6 // 13c31200 XORI $1, X5 // 93c21200 + XORI $2048, X5 // b71f00009b8f0f80b3c2f201 SLLI $1, X5, X6 // 13931200 SLLI $1, X5 // 93921200 @@ -86,20 +101,15 @@ start: SRA $1, X5 // 93d21240 // 2.5: Control Transfer Instructions - - // These jumps and branches get printed as a jump or branch - // to 2 because they transfer control to the second instruction - // in the function (the first instruction being an invisible - // stack pointer adjustment). - JAL X5, start // JAL X5, 2 // eff25ff0 + JAL X5, 2(PC) // ef028000 JALR X6, (X5) // 67830200 JALR X6, 4(X5) // 67834200 - BEQ X5, X6, start // BEQ X5, X6, 2 // e38c62ee - BNE X5, X6, start // BNE X5, X6, 2 // e39a62ee - BLT X5, X6, start // BLT X5, X6, 2 // e3c862ee - BLTU X5, X6, start // BLTU X5, X6, 2 // e3e662ee - BGE X5, X6, start // BGE X5, X6, 2 // e3d462ee - BGEU X5, X6, start // BGEU X5, X6, 2 // e3f262ee + BEQ X5, X6, 2(PC) // 63846200 + BNE X5, X6, 2(PC) // 63946200 + BLT X5, X6, 2(PC) // 63c46200 + BLTU X5, X6, 2(PC) // 63e46200 + BGE X5, X6, 2(PC) // 63d46200 + BGEU X5, X6, 2(PC) // 63f46200 // 2.6: Load and Store Instructions LW (X5), X6 // 03a30200 @@ -135,6 +145,19 @@ start: SRLW X5, X6, X7 // bb535300 SUBW X5, X6, X7 // bb035340 SRAW X5, X6, X7 // bb535340 + ADDIW $1, X6 // 1b031300 + SLLIW $1, X6 // 1b131300 + SRLIW $1, X6 // 1b531300 + SRAIW $1, X6 // 1b531340 + ADDW X5, X7 // bb835300 + SLLW X5, X7 // bb935300 + SRLW X5, X7 // bbd35300 + SUBW X5, X7 // bb835340 + SRAW X5, X7 // bbd35340 + ADDW $1, X6 // 1b031300 + SLLW $1, X6 // 1b131300 + SRLW $1, X6 // 1b531300 + SRAW $1, X6 // 1b531340 // 5.3: Load and Store Instructions (RV64I) LD (X5), X6 // 03b30200 @@ -219,6 +242,10 @@ start: FMVSX X5, F0 // 538002f0 FMVXW F0, X5 // d30200e0 FMVWX X5, F0 // 538002f0 + FMADDS F1, F2, F3, F4 // 43822018 + FMSUBS F1, F2, F3, F4 // 47822018 + FNMSUBS F1, F2, F3, F4 // 4b822018 + FNMADDS F1, F2, F3, F4 // 4f822018 // 11.8: Single-Precision Floating-Point Compare Instructions FEQS F0, F1, X7 // d3a300a0 @@ -259,6 +286,10 @@ start: FSGNJXD F1, F0, F2 // 53211022 FMVXD F0, X5 // d30200e2 FMVDX X5, F0 // 538002f2 + FMADDD F1, F2, F3, F4 // 4382201a + FMSUBD F1, F2, F3, F4 // 4782201a + FNMSUBD F1, F2, F3, F4 // 4b82201a + FNMADDD F1, F2, F3, F4 // 4f82201a // 12.6: Double-Precision Floating-Point Classify Instruction FCLASSD F0, X5 // d31200e2 @@ -277,11 +308,17 @@ start: // MOV pseudo-instructions MOV X5, X6 // 13830200 - MOV $2047, X5 // 9b02f07f - MOV $-2048, X5 // 9b020080 - - // Converted to load of symbol. - MOV $4294967296, X5 // 97020000 + MOV $2047, X5 // 9302f07f + MOV $-2048, X5 // 93020080 + MOV $2048, X5 // b71200009b820280 + MOV $-2049, X5 // b7f2ffff9b82f27f + MOV $4096, X5 // b7120000 + MOV $2147479552, X5 // b7f2ff7f + MOV $2147483647, X5 // b70200809b82f2ff + MOV $-2147483647, X5 // b70200809b821200 + + // Converted to load of symbol (AUIPC + LD) + MOV $4294967296, X5 // 9702000083b20200 MOV (X5), X6 // 03b30200 MOV 4(X5), X6 // 03b34200 @@ -325,42 +362,44 @@ start: NEGW X5 // bb025040 NEGW X5, X6 // 3b035040 - // These jumps can get printed as jumps to 2 because they go to the - // second instruction in the function (the first instruction is an - // invisible stack pointer adjustment). - JMP start // JMP 2 // 6ff01fc2 + // This jumps to the second instruction in the function (the + // first instruction is an invisible stack pointer adjustment). + JMP start // JMP 2 + + JMP 2(PC) // 6f008000 JMP (X5) // 67800200 JMP 4(X5) // 67804200 - // JMP and CALL to symbol are encoded as: - // AUIPC $0, TMP - // JALR $0, TMP - // with a R_RISCV_PCREL_ITYPE relocation - the linker resolves the - // real address and updates the immediates for both instructions. - CALL asmtest(SB) // 970f0000 - JMP asmtest(SB) // 970f0000 + // CALL and JMP to symbol are encoded as JAL (using LR or ZERO + // respectively), with a R_RISCV_CALL relocation. The linker resolves + // the real address and updates the immediate, using a trampoline in + // the case where the address is not directly reachable. + CALL asmtest(SB) // ef000000 + JMP asmtest(SB) // 6f000000 // Branch pseudo-instructions - BEQZ X5, start // BEQZ X5, 2 // e38202c0 - BGEZ X5, start // BGEZ X5, 2 // e3d002c0 - BGT X5, X6, start // BGT X5, X6, 2 // e34e53be - BGTU X5, X6, start // BGTU X5, X6, 2 // e36c53be - BGTZ X5, start // BGTZ X5, 2 // e34a50be - BLE X5, X6, start // BLE X5, X6, 2 // e35853be - BLEU X5, X6, start // BLEU X5, X6, 2 // e37653be - BLEZ X5, start // BLEZ X5, 2 // e35450be - BLTZ X5, start // BLTZ X5, 2 // e3c202be - BNEZ X5, start // BNEZ X5, 2 // e39002be + BEQZ X5, 2(PC) // 63840200 + BGEZ X5, 2(PC) // 63d40200 + BGT X5, X6, 2(PC) // 63445300 + BGTU X5, X6, 2(PC) // 63645300 + BGTZ X5, 2(PC) // 63445000 + BLE X5, X6, 2(PC) // 63545300 + BLEU X5, X6, 2(PC) // 63745300 + BLEZ X5, 2(PC) // 63545000 + BLTZ X5, 2(PC) // 63c40200 + BNEZ X5, 2(PC) // 63940200 // Set pseudo-instructions SEQZ X15, X15 // 93b71700 SNEZ X15, X15 // b337f000 // F extension + FABSS F0, F1 // d3200020 FNEGS F0, F1 // d3100020 FNES F0, F1, X7 // d3a300a093c31300 // D extension + FABSD F0, F1 // d3200022 FNEGD F0, F1 // d3100022 FNED F0, F1, X5 // d3a200a293c21200 FLTD F0, F1, X5 // d39200a2 diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s index fb43e68fc1740b..d3e43e721db55e 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64error.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s @@ -3,6 +3,14 @@ // license that can be found in the LICENSE file. TEXT errors(SB),$0 + MOV $errors(SB), (X5) // ERROR "address load must target register" + MOV $8(SP), (X5) // ERROR "address load must target register" + MOVB $8(SP), X5 // ERROR "unsupported address load" + MOVH $8(SP), X5 // ERROR "unsupported address load" + MOVW $8(SP), X5 // ERROR "unsupported address load" + MOVF $8(SP), X5 // ERROR "unsupported address load" + MOV $1234, 0(SP) // ERROR "constant load must target register" + MOV $1234, 8(SP) // ERROR "constant load must target register" MOV $0, 0(SP) // ERROR "constant load must target register" MOV $0, 8(SP) // ERROR "constant load must target register" MOV $1234, 0(SP) // ERROR "constant load must target register" @@ -11,4 +19,12 @@ TEXT errors(SB),$0 MOVH $1, X5 // ERROR "unsupported constant load" MOVW $1, X5 // ERROR "unsupported constant load" MOVF $1, X5 // ERROR "unsupported constant load" + MOVBU X5, (X6) // ERROR "unsupported unsigned store" + MOVHU X5, (X6) // ERROR "unsupported unsigned store" + MOVWU X5, (X6) // ERROR "unsupported unsigned store" + MOVF F0, F1, F2 // ERROR "illegal MOV instruction" + MOVD F0, F1, F2 // ERROR "illegal MOV instruction" + MOV X10, X11, X12 // ERROR "illegal MOV instruction" + MOVW X10, X11, X12 // ERROR "illegal MOV instruction" + RET diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index dd947c7b5ba5b8..d5e818223ba1ae 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -6,6 +6,7 @@ package flags import ( + "cmd/internal/obj" "cmd/internal/objabi" "flag" "fmt" @@ -23,11 +24,16 @@ var ( Linkshared = flag.Bool("linkshared", false, "generate code that will be linked against Go shared libraries") AllErrors = flag.Bool("e", false, "no limit on number of errors reported") SymABIs = flag.Bool("gensymabis", false, "write symbol ABI information to output file, don't assemble") - Importpath = flag.String("p", "", "set expected package import to path") + Importpath = flag.String("p", obj.UnlinkablePkg, "set expected package import to path") Spectre = flag.String("spectre", "", "enable spectre mitigations in `list` (all, ret)") CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime") ) +var DebugFlags struct { + MayMoreStack string `help:"call named function before all stack growth checks"` + PCTab string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"` +} + var ( D MultiFlag I MultiFlag @@ -39,6 +45,7 @@ func init() { flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times") flag.Var(&I, "I", "include directory; can be set multiple times") flag.BoolVar(&DebugV, "v", false, "print debug output") + flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help") objabi.AddVersionFlag() // -V objabi.Flagcount("S", "print assembly and machine code", &PrintOut) } @@ -66,8 +73,7 @@ func Usage() { } func Parse() { - flag.Usage = Usage - flag.Parse() + objabi.Flagparse(Usage) if flag.NArg() == 0 { flag.Usage() } diff --git a/src/cmd/asm/internal/lex/input.go b/src/cmd/asm/internal/lex/input.go index e373ae817e0d83..276b4b0dcd6cd1 100644 --- a/src/cmd/asm/internal/lex/input.go +++ b/src/cmd/asm/internal/lex/input.go @@ -50,7 +50,7 @@ func predefine(defines flags.MultiFlag) map[string]*Macro { // Set macros for GOEXPERIMENTs so we can easily switch // runtime assembly code based on them. if *flags.CompilingRuntime { - for _, exp := range buildcfg.EnabledExperiments() { + for _, exp := range buildcfg.Experiment.Enabled() { // Define macro. name := "GOEXPERIMENT_" + exp macros[name] = &Macro{ diff --git a/src/cmd/asm/internal/lex/lex_test.go b/src/cmd/asm/internal/lex/lex_test.go index 51679d2fbc7175..e8dcf4b22f1218 100644 --- a/src/cmd/asm/internal/lex/lex_test.go +++ b/src/cmd/asm/internal/lex/lex_test.go @@ -5,7 +5,6 @@ package lex import ( - "bytes" "strings" "testing" "text/scanner" @@ -275,7 +274,7 @@ func lines(a ...string) string { // drain returns a single string representing the processed input tokens. func drain(input *Input) string { - var buf bytes.Buffer + var buf strings.Builder for { tok := input.Next() if tok == scanner.EOF { diff --git a/src/cmd/asm/internal/lex/tokenizer.go b/src/cmd/asm/internal/lex/tokenizer.go index 861a2d421d5a81..f60f7a11af3c33 100644 --- a/src/cmd/asm/internal/lex/tokenizer.go +++ b/src/cmd/asm/internal/lex/tokenizer.go @@ -5,6 +5,7 @@ package lex import ( + "go/build/constraint" "io" "os" "strings" @@ -109,8 +110,7 @@ func (t *Tokenizer) Next() ScanToken { } text := s.TokenText() t.line += strings.Count(text, "\n") - // TODO: Use constraint.IsGoBuild once it exists. - if strings.HasPrefix(text, "//go:build") { + if constraint.IsGoBuild(text) { t.tok = BuildComment break } diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 043bc696e58ae2..6a25fd426b7de0 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -29,19 +29,21 @@ func main() { buildcfg.Check() GOARCH := buildcfg.GOARCH - architecture := arch.Set(GOARCH) + flags.Parse() + + architecture := arch.Set(GOARCH, *flags.Shared || *flags.Dynlink) if architecture == nil { log.Fatalf("unrecognized architecture %s", GOARCH) } - flags.Parse() - ctxt := obj.Linknew(architecture.LinkArch) ctxt.Debugasm = flags.PrintOut ctxt.Debugvlog = flags.DebugV ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_linkshared = *flags.Linkshared ctxt.Flag_shared = *flags.Shared || *flags.Dynlink + ctxt.Flag_maymorestack = flags.DebugFlags.MayMoreStack + ctxt.Debugpcln = flags.DebugFlags.PCTab ctxt.IsAsm = true ctxt.Pkgpath = *flags.Importpath switch *flags.Spectre { diff --git a/src/cmd/buildid/buildid.go b/src/cmd/buildid/buildid.go index 8e02a7ae109611..72ad80dbbba02f 100644 --- a/src/cmd/buildid/buildid.go +++ b/src/cmd/buildid/buildid.go @@ -53,6 +53,11 @@ func main() { log.Fatal(err) } + // <= go 1.7 doesn't embed the contentID or actionID, so no slash is present + if !strings.Contains(id, "/") { + log.Fatalf("%s: build ID is a legacy format...binary too old for this tool", file) + } + newID := id[:strings.LastIndex(id, "/")] + "/" + buildid.HashToString(hash) if len(newID) != len(id) { log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID) diff --git a/src/cmd/buildid/doc.go b/src/cmd/buildid/doc.go index d1ec155c97617c..a554d798c062bb 100644 --- a/src/cmd/buildid/doc.go +++ b/src/cmd/buildid/doc.go @@ -6,6 +6,7 @@ Buildid displays or updates the build ID stored in a Go package or binary. Usage: + go tool buildid [-w] file By default, buildid prints the build ID found in the named file. diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index a073407a961e15..c419699cb1a9ae 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -52,8 +52,8 @@ func (f *File) ParseGo(abspath string, src []byte) { // and reprinting. // In cgo mode, we ignore ast2 and just apply edits directly // the text behind ast1. In godefs mode we modify and print ast2. - ast1 := parse(abspath, src, parser.ParseComments) - ast2 := parse(abspath, src, 0) + ast1 := parse(abspath, src, parser.SkipObjectResolution|parser.ParseComments) + ast2 := parse(abspath, src, parser.SkipObjectResolution) f.Package = ast1.Name.Name f.Name = make(map[string]*Name) @@ -338,8 +338,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa // everything else just recurs default: - error_(token.NoPos, "unexpected type %T in walk", x) - panic("unexpected type") + f.walkUnexpected(x, context, visit) case nil: diff --git a/src/cmd/cgo/ast_go1.go b/src/cmd/cgo/ast_go1.go new file mode 100644 index 00000000000000..f52bf00d7cb946 --- /dev/null +++ b/src/cmd/cgo/ast_go1.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build compiler_bootstrap +// +build compiler_bootstrap + +package main + +import ( + "go/token" +) + +func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { + error_(token.NoPos, "unexpected type %T in walk", x) + panic("unexpected type") +} diff --git a/src/cmd/cgo/ast_go118.go b/src/cmd/cgo/ast_go118.go new file mode 100644 index 00000000000000..db0108ed726836 --- /dev/null +++ b/src/cmd/cgo/ast_go118.go @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !compiler_bootstrap +// +build !compiler_bootstrap + +package main + +import ( + "go/ast" + "go/token" +) + +func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) { + switch n := x.(type) { + default: + error_(token.NoPos, "unexpected type %T in walk", x) + panic("unexpected type") + + case *ast.IndexListExpr: + f.walk(&n.X, ctxExpr, visit) + f.walk(n.Indices, ctxExpr, visit) + } +} diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index a6787f64050113..7fb6179e264b56 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -3,10 +3,9 @@ // license that can be found in the LICENSE file. /* - Cgo enables the creation of Go packages that call C code. -Using cgo with the go command +# Using cgo with the go command To use cgo write normal Go code that imports a pseudo-package "C". The Go code can then refer to types such as C.size_t, variables such @@ -91,11 +90,11 @@ file. This allows pre-compiled static libraries to be included in the package directory and linked properly. For example if package foo is in the directory /go/src/foo: - // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo + // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo Will be expanded to: - // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo + // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile @@ -139,7 +138,7 @@ or you can set the CC environment variable any time you run the go tool. The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX environment variables work in a similar way for C++ code. -Go references to C +# Go references to C Within the Go file, C's struct field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C @@ -291,7 +290,7 @@ the helper function crashes the program, like when Go itself runs out of memory. Because C.malloc cannot fail, it has no two-result form that returns errno. -C references to Go +# C references to Go Go functions can be exported for use by C code in the following way: @@ -327,7 +326,7 @@ definitions and declarations, then the two output files will produce duplicate symbols and the linker will fail. To avoid this, definitions must be placed in preambles in other files, or in C source files. -Passing pointers +# Passing pointers Go is a garbage collected language, and the garbage collector needs to know the location of every pointer to Go memory. Because of this, @@ -398,7 +397,7 @@ passing uninitialized C memory to Go code if the Go code is going to store pointer values in it. Zero out the memory in C before passing it to Go. -Special cases +# Special cases A few special C types which would normally be represented by a pointer type in Go are instead represented by a uintptr. Those include: @@ -449,9 +448,10 @@ to auto-update code from Go 1.14 and earlier: go tool fix -r eglconf -Using cgo directly +# Using cgo directly Usage: + go tool cgo [cgo options] [-- compiler options] gofiles... Cgo transforms the specified input Go source files into several output @@ -753,6 +753,16 @@ presented to cmd/link as part of a larger program, contains: _go_.o # gc-compiled object for _cgo_gotypes.go, _cgo_import.go, *.cgo1.go _all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c +If there is an error generating the _cgo_import.go file, then, instead +of adding _cgo_import.go to the package, the go tool adds an empty +file named dynimportfail. The _cgo_import.go file is only needed when +using internal linking mode, which is not the default when linking +programs that use cgo (as described below). If the linker sees a file +named dynimportfail it reports an error if it has been told to use +internal linking mode. This approach is taken because generating +_cgo_import.go requires doing a full C link of the package, which can +fail for reasons that are irrelevant when using external linking mode. + The final program will be a dynamic executable, so that cmd/link can avoid needing to process arbitrary .o files. It only needs to process the .o files generated from C files that cgo writes, and those are much more diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index a73e998877af81..06cf46f63fa663 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -23,10 +23,13 @@ import ( "internal/xcoff" "math" "os" + "os/exec" "strconv" "strings" "unicode" "unicode/utf8" + + "cmd/internal/quoted" ) var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") @@ -111,12 +114,11 @@ func (p *Package) addToFlag(flag string, args []string) { // // For example, the following string: // -// `a b:"c d" 'e''f' "g\""` +// `a b:"c d" 'e''f' "g\""` // // Would be parsed as: // -// []string{"a", "b:c d", "ef", `g"`} -// +// []string{"a", "b:c d", "ef", `g"`} func splitQuoted(s string) (r []string, err error) { var args []string arg := make([]rune, len(s)) @@ -382,7 +384,7 @@ func (p *Package) guessKinds(f *File) []*Name { stderr = p.gccErrors(b.Bytes()) } if stderr == "" { - fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) + fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes()) } completed := false @@ -457,7 +459,7 @@ func (p *Package) guessKinds(f *File) []*Name { } if !completed { - fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr) + fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr) } for i, n := range names { @@ -486,9 +488,9 @@ func (p *Package) guessKinds(f *File) []*Name { // Check if compiling the preamble by itself causes any errors, // because the messages we've printed out so far aren't helpful // to users debugging preamble mistakes. See issue 8442. - preambleErrors := p.gccErrors([]byte(f.Preamble)) + preambleErrors := p.gccErrors([]byte(builtinProlog + f.Preamble)) if len(preambleErrors) > 0 { - error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors) + error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors) } fatalf("unresolved names") @@ -574,8 +576,23 @@ func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) { switch e.Tag { case dwarf.TagVariable: name, _ := e.Val(dwarf.AttrName).(string) + // As of https://reviews.llvm.org/D123534, clang + // now emits DW_TAG_variable DIEs that have + // no name (so as to be able to describe the + // type and source locations of constant strings + // like the second arg in the call below: + // + // myfunction(42, "foo") + // + // If a var has no name we won't see attempts to + // refer to it via "C.", so skip these vars + // + // See issue 53000 for more context. + if name == "" { + break + } typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset) - if name == "" || typOff == 0 { + if typOff == 0 { if e.Val(dwarf.AttrSpecification) != nil { // Since we are reading all the DWARF, // assume we will see the variable elsewhere. @@ -809,6 +826,7 @@ func (p *Package) rewriteCall(f *File, call *Call) (string, bool) { params := name.FuncType.Params args := call.Call.Args + end := call.Call.End() // Avoid a crash if the number of arguments doesn't match // the number of parameters. @@ -956,7 +974,7 @@ func (p *Package) rewriteCall(f *File, call *Call) (string, bool) { if nu { needsUnsafe = true } - sb.WriteString(gofmtLine(m)) + sb.WriteString(gofmtPos(m, end)) sb.WriteString("(") for i := range params { @@ -1135,13 +1153,19 @@ func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bo // checkIndex checks whether arg has the form &a[i], possibly inside // type conversions. If so, then in the general case it writes -// _cgoIndexNN := a -// _cgoNN := &cgoIndexNN[i] // with type conversions, if any +// +// _cgoIndexNN := a +// _cgoNN := &cgoIndexNN[i] // with type conversions, if any +// // to sb, and writes -// _cgoCheckPointer(_cgoNN, _cgoIndexNN) +// +// _cgoCheckPointer(_cgoNN, _cgoIndexNN) +// // to sbCheck, and returns true. If a is a simple variable or field reference, // it writes -// _cgoIndexNN := &a +// +// _cgoIndexNN := &a +// // and dereferences the uses of _cgoIndexNN. Taking the address avoids // making a copy of an array. // @@ -1189,10 +1213,14 @@ func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) boo // checkAddr checks whether arg has the form &x, possibly inside type // conversions. If so, it writes -// _cgoBaseNN := &x -// _cgoNN := _cgoBaseNN // with type conversions, if any +// +// _cgoBaseNN := &x +// _cgoNN := _cgoBaseNN // with type conversions, if any +// // to sb, and writes -// _cgoCheckPointer(_cgoBaseNN, true) +// +// _cgoCheckPointer(_cgoBaseNN, true) +// // to sbCheck, and returns true. This tells _cgoCheckPointer to check // just the contents of the pointer being passed, not any other part // of the memory allocation. This is run after checkIndex, which looks @@ -1503,7 +1531,7 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr { Args: []ast.Expr{getNewIdent(name.Mangle)}, } case "type": - // Okay - might be new(T) + // Okay - might be new(T), T(x), Generic[T], etc. if r.Name.Type == nil { error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } @@ -1545,20 +1573,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string { return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s) } -// gccBaseCmd returns the start of the compiler command line. +// checkGCCBaseCmd returns the start of the compiler command line. // It uses $CC if set, or else $GCC, or else the compiler recorded // during the initial build as defaultCC. // defaultCC is defined in zdefaultcc.go, written by cmd/dist. -func (p *Package) gccBaseCmd() []string { +// +// The compiler command line is split into arguments on whitespace. Quotes +// are understood, so arguments may contain whitespace. +// +// checkGCCBaseCmd confirms that the compiler exists in PATH, returning +// an error if it does not. +func checkGCCBaseCmd() ([]string, error) { // Use $CC if set, since that's what the build uses. - if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 { - return ret + value := os.Getenv("CC") + if value == "" { + // Try $GCC if set, since that's what we used to use. + value = os.Getenv("GCC") } - // Try $GCC if set, since that's what we used to use. - if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { - return ret + if value == "" { + value = defaultCC(goos, goarch) } - return strings.Fields(defaultCC(goos, goarch)) + args, err := quoted.Split(value) + if err != nil { + return nil, err + } + if len(args) == 0 { + return nil, errors.New("CC not set and no default found") + } + if _, err := exec.LookPath(args[0]); err != nil { + return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err) + } + return args[:len(args):len(args)], nil } // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". @@ -1593,6 +1638,8 @@ func (p *Package) gccMachine() []string { } else if gomips == "softfloat" { return []string{"-mabi=32", "-msoft-float"} } + case "loong64": + return []string{"-mabi=lp64d"} } return nil } @@ -1604,7 +1651,7 @@ func gccTmp() string { // gccCmd returns the gcc command line to use for compiling // the input. func (p *Package) gccCmd() []string { - c := append(p.gccBaseCmd(), + c := append(gccBaseCmd, "-w", // no warnings "-Wno-error", // warnings are not errors "-o"+gccTmp(), // write object to tmp @@ -1784,6 +1831,23 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 bo := f.ByteOrder symtab, err := f.Symbols() if err == nil { + // Check for use of -fsanitize=hwaddress (issue 53285). + removeTag := func(v uint64) uint64 { return v } + if goarch == "arm64" { + for i := range symtab { + if symtab[i].Name == "__hwasan_init" { + // -fsanitize=hwaddress on ARM + // uses the upper byte of a + // memory address as a hardware + // tag. Remove it so that + // we can find the associated + // data. + removeTag = func(v uint64) uint64 { return v &^ (0xff << (64 - 8)) } + break + } + } + } + for i := range symtab { s := &symtab[i] switch { @@ -1791,9 +1855,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) @@ -1805,9 +1870,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) @@ -1820,9 +1886,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] strdata[n] = string(data) } } @@ -1833,9 +1900,10 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] - if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + val := removeTag(s.Value) + if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { - data := sdat[s.Value-sect.Addr:] + data := sdat[val-sect.Addr:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") @@ -2005,7 +2073,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6 // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := append(p.gccBaseCmd(), "-E", "-dM", "-xc") + base := append(gccBaseCmd, "-E", "-dM", "-xc") base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout @@ -2086,6 +2154,9 @@ type typeConv struct { // Type names X for which there exists an XGetTypeID function with type func() CFTypeID. getTypeIDs map[string]bool + // incompleteStructs contains C structs that should be marked Incomplete. + incompleteStructs map[string]bool + // Predeclared types. bool ast.Expr byte ast.Expr // denotes padding @@ -2120,6 +2191,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) { c.m = make(map[string]*Type) c.ptrs = make(map[string][]*Type) c.getTypeIDs = make(map[string]bool) + c.incompleteStructs = make(map[string]bool) c.bool = c.Ident("bool") c.byte = c.Ident("byte") c.int8 = c.Ident("int8") @@ -2189,6 +2261,8 @@ var dwarfToName = map[string]string{ "long long unsigned int": "ulonglong", "signed char": "schar", "unsigned char": "uchar", + "unsigned long": "ulong", // Used by Clang 14; issue 53013. + "unsigned long long": "ulonglong", // Used by Clang 14; issue 53013. } const signedDelta = 64 @@ -2475,24 +2549,23 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ t.Go = name // publish before recursive calls goIdent[name.Name] = name if dt.ByteSize < 0 { + // Don't override old type + if _, ok := typedef[name.Name]; ok { + break + } + // Size calculation in c.Struct/c.Opaque will die with size=-1 (unknown), // so execute the basic things that the struct case would do // other than try to determine a Go representation. tt := *t tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}} - tt.Go = c.Ident("struct{}") - if dt.Kind == "struct" { - // We don't know what the representation of this struct is, so don't let - // anyone allocate one on the Go side. As a side effect of this annotation, - // pointers to this type will not be considered pointers in Go. They won't - // get writebarrier-ed or adjusted during a stack copy. This should handle - // all the cases badPointerTypedef used to handle, but hopefully will - // continue to work going forward without any more need for cgo changes. - tt.NotInHeap = true - // TODO: we should probably do the same for unions. Unions can't live - // on the Go heap, right? It currently doesn't work for unions because - // they are defined as a type alias for struct{}, not a defined type. - } + // We don't know what the representation of this struct is, so don't let + // anyone allocate one on the Go side. As a side effect of this annotation, + // pointers to this type will not be considered pointers in Go. They won't + // get writebarrier-ed or adjusted during a stack copy. This should handle + // all the cases badPointerTypedef used to handle, but hopefully will + // continue to work going forward without any more need for cgo changes. + tt.Go = c.Ident("_cgopackage.Incomplete") typedef[name.Name] = &tt break } @@ -2518,6 +2591,9 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ tt.C = &TypeRepr{"struct %s", []interface{}{tag}} } tt.Go = g + if c.incompleteStructs[tag] { + tt.Go = c.Ident("_cgopackage.Incomplete") + } typedef[name.Name] = &tt } @@ -2561,9 +2637,32 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ oldType.BadPointer = true } } + if c.badVoidPointerTypedef(dt) { + // Treat this typedef as a pointer to a _cgopackage.Incomplete. + s := *sub + s.Go = c.Ident("*_cgopackage.Incomplete") + sub = &s + // Make sure we update any previously computed type. + if oldType := typedef[name.Name]; oldType != nil { + oldType.Go = sub.Go + } + } + // Check for non-pointer "struct {...}; typedef struct *" + // typedefs that should be marked Incomplete. + if ptr, ok := dt.Type.(*dwarf.PtrType); ok { + if strct, ok := ptr.Type.(*dwarf.StructType); ok { + if c.badStructPointerTypedef(dt.Name, strct) { + c.incompleteStructs[strct.StructName] = true + // Make sure we update any previously computed type. + name := "_Ctype_struct_" + strct.StructName + if oldType := typedef[name]; oldType != nil { + oldType.Go = c.Ident("_cgopackage.Incomplete") + } + } + } + } t.Go = name t.BadPointer = sub.BadPointer - t.NotInHeap = sub.NotInHeap if unionWithPointer[sub.Go] { unionWithPointer[t.Go] = true } @@ -2574,7 +2673,6 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ tt := *t tt.Go = sub.Go tt.BadPointer = sub.BadPointer - tt.NotInHeap = sub.NotInHeap typedef[name.Name] = &tt } @@ -2817,7 +2915,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct // Minimum alignment for a struct is 1 byte. align = 1 - var buf bytes.Buffer + var buf strings.Builder buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field sizes := make([]int64, 0, 2*len(dt.Field)+1) @@ -3010,6 +3108,31 @@ func upper(s string) string { // so that all fields are exported. func godefsFields(fld []*ast.Field) { prefix := fieldPrefix(fld) + + // Issue 48396: check for duplicate field names. + if prefix != "" { + names := make(map[string]bool) + fldLoop: + for _, f := range fld { + for _, n := range f.Names { + name := n.Name + if name == "_" { + continue + } + if name != prefix { + name = strings.TrimPrefix(n.Name, prefix) + } + name = upper(name) + if names[name] { + // Field name conflict: don't remove prefix. + prefix = "" + break fldLoop + } + names[name] = true + } + } + } + npad := 0 for _, f := range fld { for _, n := range f.Names { @@ -3073,7 +3196,7 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool { // non-pointers in this type. // TODO: Currently our best solution is to find these manually and list them as // they come up. A better solution is desired. -// Note: DEPRECATED. There is now a better solution. Search for NotInHeap in this file. +// Note: DEPRECATED. There is now a better solution. Search for _cgopackage.Incomplete in this file. func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { if c.badCFType(dt) { return true @@ -3087,6 +3210,48 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { return false } +// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be _cgopackage.Incomplete. +func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool { + // Match the Windows HANDLE type (#42018). + if goos != "windows" || dt.Name != "HANDLE" { + return false + } + // Check that the typedef is "typedef void *". + if ptr, ok := dt.Type.(*dwarf.PtrType); ok { + if _, ok := ptr.Type.(*dwarf.VoidType); ok { + return true + } + } + return false +} + +// badStructPointerTypedef is like badVoidPointerTypedefs but for structs. +func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool { + // Windows handle types can all potentially contain non-pointers. + // badVoidPointerTypedef handles the "void *" HANDLE type, but other + // handles are defined as + // + // struct __{int unused;}; typedef struct __ *name; + // + // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in + // the Windows ntdef.h header, + // + // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779 + if goos != "windows" { + return false + } + if len(dt.Field) != 1 { + return false + } + if dt.StructName != name+"__" { + return false + } + if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" { + return false + } + return true +} + // baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef // as badPointerTypedef reports. func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool { diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index c0d59aee01d747..f62867053f5821 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -5,7 +5,6 @@ package main import ( - "bytes" "fmt" "go/ast" "go/printer" @@ -16,11 +15,11 @@ import ( ) // godefs returns the output for -godefs mode. -func (p *Package) godefs(f *File) string { - var buf bytes.Buffer +func (p *Package) godefs(f *File, args []string) string { + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n") - fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " ")) + fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(args[0]), strings.Join(args[1:], " ")) fmt.Fprintf(&buf, "\n") override := make(map[string]string) @@ -115,7 +114,7 @@ func (p *Package) godefs(f *File) string { return buf.String() } -var gofmtBuf bytes.Buffer +var gofmtBuf strings.Builder // gofmt returns the gofmt-formatted string for an AST node. func gofmt(n interface{}) string { diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index c6a0c525e64f21..55515a677f6df3 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -11,7 +11,6 @@ package main import ( - "crypto/md5" "flag" "fmt" "go/ast" @@ -21,7 +20,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "reflect" "runtime" @@ -29,6 +27,7 @@ import ( "strings" "cmd/internal/edit" + "cmd/internal/notsha256" "cmd/internal/objabi" ) @@ -154,7 +153,6 @@ type Type struct { EnumValues map[string]int64 Typedef string BadPointer bool // this pointer type should be represented as a uintptr (deprecated) - NotInHeap bool // this type should have a go:notinheap annotation } // A FuncType collects information about a function type in both the C and Go worlds. @@ -176,6 +174,7 @@ var ptrSizeMap = map[string]int64{ "amd64": 8, "arm": 4, "arm64": 8, + "loong64": 8, "m68k": 4, "mips": 4, "mipsle": 4, @@ -201,6 +200,7 @@ var intSizeMap = map[string]int64{ "amd64": 8, "arm": 4, "arm64": 8, + "loong64": 8, "m68k": 4, "mips": 4, "mipsle": 4, @@ -248,11 +248,11 @@ var importSyscall = flag.Bool("import_syscall", true, "import syscall in generat var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths") var goarch, goos, gomips, gomips64 string +var gccBaseCmd []string func main() { objabi.AddVersionFlag() // -V - flag.Usage = usage - flag.Parse() + objabi.Flagparse(usage) if *dynobj != "" { // cgo -dynimport is essentially a separate helper command @@ -291,6 +291,10 @@ func main() { usage() } + // Save original command line arguments for the godefs generated comment. Relative file + // paths in os.Args will be rewritten to absolute file paths in the loop below. + osArgs := make([]string, len(os.Args)) + copy(osArgs, os.Args[:]) goFiles := args[i:] for _, arg := range args[:i] { @@ -305,10 +309,10 @@ func main() { p := newPackage(args[:i]) // We need a C compiler to be available. Check this. - gccName := p.gccBaseCmd()[0] - _, err := exec.LookPath(gccName) + var err error + gccBaseCmd, err = checkGCCBaseCmd() if err != nil { - fatalf("C compiler %q not found: %v", gccName, err) + fatalf("%v", err) os.Exit(2) } @@ -325,8 +329,8 @@ func main() { // we use to coordinate between gcc and ourselves. // We already put _cgo_ at the beginning, so the main // concern is other cgo wrappers for the same functions. - // Use the beginning of the md5 of the input to disambiguate. - h := md5.New() + // Use the beginning of the notsha256 of the input to disambiguate. + h := notsha256.New() io.WriteString(h, *importPath) fs := make([]*File, len(goFiles)) for i, input := range goFiles { @@ -390,7 +394,7 @@ func main() { p.PackagePath = f.Package p.Record(f) if *godefs { - os.Stdout.WriteString(p.godefs(f)) + os.Stdout.WriteString(p.godefs(f, osArgs)) } else { p.writeOutput(f, input) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 94152f4278cb04..95223588252b28 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,10 +14,10 @@ import ( "go/ast" "go/printer" "go/token" - exec "internal/execabs" "internal/xcoff" "io" "os" + "os/exec" "path/filepath" "regexp" "sort" @@ -43,7 +43,7 @@ func (p *Package) writeDefs() { } fm := creat(*objDir + "_cgo_main.c") - var gccgoInit bytes.Buffer + var gccgoInit strings.Builder fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { @@ -57,21 +57,22 @@ func (p *Package) writeDefs() { fflg.Close() // Write C main file for using gcc to resolve imports. + fmt.Fprintf(fm, "#include \n") // For size_t below. fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt) { }\n") - fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n") - fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), size_t ctxt __attribute__((unused))) { }\n") + fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void) { return 0; }\n") + fmt.Fprintf(fm, "void _cgo_release_context(size_t ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides these functions. We just need a prototype. - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt);\n") - fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") - fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, size_t ctxt);\n") + fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void);\n") + fmt.Fprintf(fm, "void _cgo_release_context(size_t);\n") } - fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") - fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") + fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n") // Write second Go output: definitions of _C_xxx. @@ -80,11 +81,14 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - if !*gccgo && *importRuntimeCgo { - fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") - } if *importSyscall { fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + } + if *importRuntimeCgo { + fmt.Fprintf(fgo2, "import _cgopackage \"runtime/cgo\"\n\n") + fmt.Fprintf(fgo2, "type _ _cgopackage.Incomplete\n") // prevent import-not-used error + } + if *importSyscall { fmt.Fprintf(fgo2, "var _ syscall.Errno\n") } fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n") @@ -108,9 +112,6 @@ func (p *Package) writeDefs() { sort.Strings(typedefNames) for _, name := range typedefNames { def := typedef[name] - if def.NotInHeap { - fmt.Fprintf(fgo2, "//go:notinheap\n") - } fmt.Fprintf(fgo2, "type %s ", name) // We don't have source info for these types, so write them out without source info. // Otherwise types would look like: @@ -172,7 +173,7 @@ func (p *Package) writeDefs() { // the external linker will add DT_NEEDED // entries as needed on ELF systems. // Treat function variables differently - // to avoid type confict errors from LTO + // to avoid type conflict errors from LTO // (Link Time Optimization). if n.Kind == "fpvar" { fmt.Fprintf(fm, "extern void %s();\n", n.C) @@ -437,7 +438,7 @@ func checkImportSymName(s string) { // Also assumes that gc convention is to word-align the // input and output parameters. func (p *Package) structType(n *Name) (string, int64) { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprint(&buf, "struct {\n") off := int64(0) for i, t := range n.FuncType.Params { @@ -885,9 +886,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Wpragmas\"\n") fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Waddress-of-packed-member\"\n") - fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, __SIZE_TYPE__);\n") - fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") - fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n") + fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, size_t);\n") + fmt.Fprintf(fgcc, "extern size_t _cgo_wait_runtime_init_done(void);\n") + fmt.Fprintf(fgcc, "extern void _cgo_release_context(size_t);\n\n") fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);") fmt.Fprintf(fgcc, "%s\n", tsanProlog) fmt.Fprintf(fgcc, "%s\n", msanProlog) @@ -991,7 +992,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { 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") + fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n") // The results part of the argument structure must be // initialized to 0 so the write barriers generated by // the assignments to these fields in Go are safe. @@ -1054,9 +1055,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgo2, "\t") + if gccResult != "void" { // Write results back to frame. - fmt.Fprintf(fgo2, "\t") forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { if i > 0 { @@ -1112,7 +1114,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { fn := exp.Func fntype := fn.Type - cdeclBuf := new(bytes.Buffer) + cdeclBuf := new(strings.Builder) resultCount := 0 forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { resultCount++ }) @@ -1144,7 +1146,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { cRet := cdeclBuf.String() - cdeclBuf = new(bytes.Buffer) + cdeclBuf = new(strings.Builder) fmt.Fprintf(cdeclBuf, "(") if fn.Recv != nil { fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String()) @@ -1396,6 +1398,19 @@ func (p *Package) cgoType(e ast.Expr) *Type { case *ast.ChanType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")} case *ast.Ident: + goTypesFixup := func(r *Type) *Type { + if r.Size == 0 { // int or uint + rr := new(Type) + *rr = *r + rr.Size = p.IntSize + rr.Align = p.IntSize + r = rr + } + if r.Align > p.PtrSize { + r.Align = p.PtrSize + } + return r + } // Look up the type in the top level declarations. // TODO: Handle types defined within a function. for _, d := range p.Decl { @@ -1414,6 +1429,17 @@ func (p *Package) cgoType(e ast.Expr) *Type { } } if def := typedef[t.Name]; def != nil { + if defgo, ok := def.Go.(*ast.Ident); ok { + switch defgo.Name { + case "complex64", "complex128": + // MSVC does not support the _Complex keyword + // nor the complex macro. + // Use GoComplex64 and GoComplex128 instead, + // which are typedef-ed to a compatible type. + // See go.dev/issues/36233. + return goTypesFixup(goTypes[defgo.Name]) + } + } return def } if t.Name == "uintptr" { @@ -1427,17 +1453,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} } if r, ok := goTypes[t.Name]; ok { - if r.Size == 0 { // int or uint - rr := new(Type) - *rr = *r - rr.Size = p.IntSize - rr.Align = p.IntSize - r = rr - } - if r.Align > p.PtrSize { - r.Align = p.PtrSize - } - return r + return goTypesFixup(r) } error_(e.Pos(), "unrecognized Go type %s", t.Name) return &Type{Size: 4, Align: 4, C: c("int")} @@ -1458,10 +1474,10 @@ const gccProlog = ` (have a negative array count) and an inscrutable error will come out of the compiler and hopefully mention "name". */ -#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; +#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL]; /* Check at compile time that the sizes we use match our expectations. */ -#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) +#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n) __cgo_size_assert(char, 1) __cgo_size_assert(short, 2) @@ -1559,7 +1575,7 @@ var msanProlog = noMsanProlog const builtinProlog = ` #line 1 "cgo-builtin-prolog" -#include /* for ptrdiff_t and size_t below */ +#include /* Define intgo when compiling with GCC. */ typedef ptrdiff_t intgo; @@ -1602,6 +1618,7 @@ const goStringDef = ` //go:linkname _cgo_runtime_gostring runtime.gostring func _cgo_runtime_gostring(*_Ctype_char) string +// GoString converts the C string p into a Go string. func _Cfunc_GoString(p *_Ctype_char) string { return _cgo_runtime_gostring(p) } @@ -1611,6 +1628,7 @@ const goStringNDef = ` //go:linkname _cgo_runtime_gostringn runtime.gostringn func _cgo_runtime_gostringn(*_Ctype_char, int) string +// GoStringN converts the C data p with explicit length l to a Go string. func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string { return _cgo_runtime_gostringn(p, int(l)) } @@ -1620,26 +1638,52 @@ const goBytesDef = ` //go:linkname _cgo_runtime_gobytes runtime.gobytes func _cgo_runtime_gobytes(unsafe.Pointer, int) []byte +// GoBytes converts the C data p with explicit length l to a Go []byte. func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte { return _cgo_runtime_gobytes(p, int(l)) } ` const cStringDef = ` +// CString converts the Go string s to a C string. +// +// The C string is allocated in the C heap using malloc. +// It is the caller's responsibility to arrange for it to be +// freed, such as by calling C.free (be sure to include stdlib.h +// if C.free is needed). func _Cfunc_CString(s string) *_Ctype_char { + if len(s)+1 <= 0 { + panic("string too large") + } p := _cgo_cmalloc(uint64(len(s)+1)) - pp := (*[1<<30]byte)(p) - copy(pp[:], s) - pp[len(s)] = 0 + sliceHeader := struct { + p unsafe.Pointer + len int + cap int + }{p, len(s)+1, len(s)+1} + b := *(*[]byte)(unsafe.Pointer(&sliceHeader)) + copy(b, s) + b[len(s)] = 0 return (*_Ctype_char)(p) } ` const cBytesDef = ` +// CBytes converts the Go []byte slice b to a C array. +// +// The C array is allocated in the C heap using malloc. +// It is the caller's responsibility to arrange for it to be +// freed, such as by calling C.free (be sure to include stdlib.h +// if C.free is needed). func _Cfunc_CBytes(b []byte) unsafe.Pointer { p := _cgo_cmalloc(uint64(len(b))) - pp := (*[1<<30]byte)(p) - copy(pp[:], b) + sliceHeader := struct { + p unsafe.Pointer + len int + cap int + }{p, len(b), len(b)} + s := *(*[]byte)(unsafe.Pointer(&sliceHeader)) + copy(s, b) return p } ` @@ -1828,7 +1872,7 @@ void localCgoCheckResult(Eface val) { const builtinExportProlog = ` #line 1 "cgo-builtin-export-prolog" -#include /* for ptrdiff_t below */ +#include #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H @@ -1874,11 +1918,17 @@ typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoIntGOINTBITS GoInt; typedef GoUintGOINTBITS GoUint; -typedef __SIZE_TYPE__ GoUintptr; +typedef size_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; +#ifdef _MSC_VER +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; +#endif /* static assertion to make sure the file is being used on architecture @@ -1924,5 +1974,5 @@ static void GoInit(void) { runtime_iscgo = 1; } -extern __SIZE_TYPE__ _cgo_wait_runtime_init_done(void) __attribute__ ((weak)); +extern size_t _cgo_wait_runtime_init_done(void) __attribute__ ((weak)); ` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 00d931b98a0c1e..779f7be2259b37 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -8,9 +8,9 @@ import ( "bytes" "fmt" "go/token" - exec "internal/execabs" "io/ioutil" "os" + "os/exec" ) // run runs the command argv, feeding in stdin on standard input. diff --git a/src/cmd/compile/README.md b/src/cmd/compile/README.md index babc3f7679525d..9c4eeeb51ead3e 100644 --- a/src/cmd/compile/README.md +++ b/src/cmd/compile/README.md @@ -15,10 +15,12 @@ the compiler. Roughly speaking, these translate to the first two and last two phases we are going to list here. A third term, "middle-end", often refers to much of the work that happens in the second phase. -Note that the `go/*` family of packages, such as `go/parser` and `go/types`, -have no relation to the compiler. Since the compiler was initially written in C, -the `go/*` packages were developed to enable writing tools working with Go code, -such as `gofmt` and `vet`. +Note that the `go/*` family of packages, such as `go/parser` and +`go/types`, are mostly unused by the compiler. Since the compiler was +initially written in C, the `go/*` packages were developed to enable +writing tools working with Go code, such as `gofmt` and `vet`. +However, over time the compiler's internal APIs have slowly evolved to +be more familiar to users of the `go/*` packages. It should be clarified that the name "gc" stands for "Go compiler", and has little to do with uppercase "GC", which stands for garbage collection. @@ -36,33 +38,71 @@ nodes corresponding to the various elements of the source such as expressions, declarations, and statements. The syntax tree also includes position information which is used for error reporting and the creation of debugging information. -### 2. Type-checking and AST transformations +### 2. Type checking -* `cmd/compile/internal/gc` (create compiler AST, type checking, AST transformations) +* `cmd/compile/internal/types2` (type checking) -The gc package includes an AST definition carried over from when it was written -in C. All of its code is written in terms of it, so the first thing that the gc -package must do is convert the syntax package's syntax tree to the compiler's -AST representation. This extra step may be refactored away in the future. +The types2 package is a port of `go/types` to use the syntax package's +AST instead of `go/ast`. -The AST is then type-checked. The first steps are name resolution and type -inference, which determine which object belongs to which identifier, and what -type each expression has. Type-checking includes certain extra checks, such as -"declared and not used" as well as determining whether or not a function -terminates. +### 3. IR construction ("noding") -Certain transformations are also done on the AST. Some nodes are refined based -on type information, such as string additions being split from the arithmetic -addition node type. Some other examples are dead code elimination, function call +* `cmd/compile/internal/types` (compiler types) +* `cmd/compile/internal/ir` (compiler AST) +* `cmd/compile/internal/typecheck` (AST transformations) +* `cmd/compile/internal/noder` (create compiler AST) + +The compiler middle end uses its own AST definition and representation of Go +types carried over from when it was written in C. All of its code is written in +terms of these, so the next step after type checking is to convert the syntax +and types2 representations to ir and types. This process is referred to as +"noding." + +There are currently two noding implementations: + +1. irgen (aka "-G=3" or sometimes "noder2") is the implementation used starting + with Go 1.18, and + +2. Unified IR is another, in-development implementation (enabled with + `GOEXPERIMENT=unified`), which also implements import/export and inlining. + +Up through Go 1.18, there was a third noding implementation (just +"noder" or "-G=0"), which directly converted the pre-type-checked +syntax representation into IR and then invoked package typecheck's +type checker. This implementation was removed after Go 1.18, so now +package typecheck is only used for IR transformations. + +### 4. Middle end + +* `cmd/compile/internal/deadcode` (dead code elimination) +* `cmd/compile/internal/inline` (function call inlining) +* `cmd/compile/internal/devirtualize` (devirtualization of known interface method calls) +* `cmd/compile/internal/escape` (escape analysis) + +Several optimization passes are performed on the IR representation: +dead code elimination, (early) devirtualization, function call inlining, and escape analysis. -### 3. Generic SSA +### 5. Walk -* `cmd/compile/internal/gc` (converting to SSA) -* `cmd/compile/internal/ssa` (SSA passes and rules) +* `cmd/compile/internal/walk` (order of evaluation, desugaring) + +The final pass over the IR representation is "walk," which serves two purposes: + +1. It decomposes complex statements into individual, simpler statements, + introducing temporary variables and respecting order of evaluation. This step + is also referred to as "order." + +2. It desugars higher-level Go constructs into more primitive ones. For example, + `switch` statements are turned into binary search or jump tables, and + operations on maps and channels are replaced with runtime calls. +### 6. Generic SSA -In this phase, the AST is converted into Static Single Assignment (SSA) form, a +* `cmd/compile/internal/ssa` (SSA passes and rules) +* `cmd/compile/internal/ssagen` (converting IR to SSA) + +In this phase, IR is converted into Static Single Assignment (SSA) form, a lower-level intermediate representation with specific properties that make it easier to implement optimizations and to eventually generate machine code from it. @@ -79,13 +119,12 @@ historical reasons, but the long-term plan is to move all of them here. Then, a series of machine-independent passes and rules are applied. These do not concern any single computer architecture, and thus run on all `GOARCH` variants. - -Some examples of these generic passes include dead code elimination, removal of +These passes include dead code elimination, removal of unneeded nil checks, and removal of unused branches. The generic rewrite rules mainly concern expressions, such as replacing some expressions with constant values, and optimizing multiplications and float operations. -### 4. Generating machine code +### 7. Generating machine code * `cmd/compile/internal/ssa` (SSA lowering and arch-specific passes) * `cmd/internal/obj` (machine code generation) diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md index 2bb40550832630..14464ed904c51e 100644 --- a/src/cmd/compile/abi-internal.md +++ b/src/cmd/compile/abi-internal.md @@ -1,5 +1,7 @@ # Go internal ABI specification +Self-link: [go.dev/s/regabi](https://go.dev/s/regabi) + This document describes Go’s internal application binary interface (ABI), known as ABIInternal. Go's ABI defines the layout of data in memory and the conventions for @@ -30,19 +32,19 @@ specification](/doc/go_spec.html#Size_and_alignment_guarantees). Those that aren't guaranteed may change in future versions of Go (for example, we've considered changing the alignment of int64 on 32-bit). -| Type | 64-bit | | 32-bit | | -| --- | --- | --- | --- | --- | -| | Size | Align | Size | Align | -| bool, uint8, int8 | 1 | 1 | 1 | 1 | -| uint16, int16 | 2 | 2 | 2 | 2 | -| uint32, int32 | 4 | 4 | 4 | 4 | -| uint64, int64 | 8 | 8 | 8 | 4 | -| int, uint | 8 | 8 | 4 | 4 | -| float32 | 4 | 4 | 4 | 4 | -| float64 | 8 | 8 | 8 | 4 | -| complex64 | 8 | 4 | 8 | 4 | -| complex128 | 16 | 8 | 16 | 4 | -| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 | +| Type | 64-bit | | 32-bit | | +|-----------------------------|--------|-------|--------|-------| +| | Size | Align | Size | Align | +| bool, uint8, int8 | 1 | 1 | 1 | 1 | +| uint16, int16 | 2 | 2 | 2 | 2 | +| uint32, int32 | 4 | 4 | 4 | 4 | +| uint64, int64 | 8 | 8 | 8 | 4 | +| int, uint | 8 | 8 | 4 | 4 | +| float32 | 4 | 4 | 4 | 4 | +| float64 | 8 | 8 | 8 | 4 | +| complex64 | 8 | 4 | 8 | 4 | +| complex128 | 16 | 8 | 16 | 4 | +| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 | The types `byte` and `rune` are aliases for `uint8` and `int32`, respectively, and hence have the same size and alignment as these @@ -155,7 +157,7 @@ as follows: 1. Remember I and FP. 1. If T has zero size, add T to the stack sequence S and return. 1. Try to register-assign V. -1. If step 2 failed, reset I and FP to the values from step 1, add T +1. If step 3 failed, reset I and FP to the values from step 1, add T to the stack sequence S, and assign V to this field in S. Register-assignment of a value V of underlying type T works as follows: @@ -410,7 +412,11 @@ Special-purpose registers are as follows: | R13 | Scratch | Scratch | Scratch | | R14 | Current goroutine | Same | Same | | R15 | GOT reference temporary if dynlink | Same | Same | -| X15 | Zero value | Same | Scratch | +| X15 | Zero value (*) | Same | Scratch | + +(*) Except on Plan 9, where X15 is a scratch register because SSE +registers cannot be used in note handlers (so the compiler avoids +using them except when absolutely necessary). *Rationale*: These register meanings are compatible with Go’s stack-based calling convention except for R14 and X15, which will have @@ -505,6 +511,278 @@ control bits specified by the ELF AMD64 ABI. The x87 floating-point control word is not used by Go on amd64. +### arm64 architecture + +The arm64 architecture uses R0 – R15 for integer arguments and results. + +It uses F0 – F15 for floating-point arguments and results. + +*Rationale*: 16 integer registers and 16 floating-point registers are +more than enough for passing arguments and results for practically all +functions (see Appendix). While there are more registers available, +using more registers provides little benefit. Additionally, it will add +overhead on code paths where the number of arguments are not statically +known (e.g. reflect call), and will consume more stack space when there +is only limited stack space available to fit in the nosplit limit. + +Registers R16 and R17 are permanent scratch registers. They are also +used as scratch registers by the linker (Go linker and external +linker) in trampolines. + +Register R18 is reserved and never used. It is reserved for the OS +on some platforms (e.g. macOS). + +Registers R19 – R25 are permanent scratch registers. In addition, +R27 is a permanent scratch register used by the assembler when +expanding instructions. + +Floating-point registers F16 – F31 are also permanent scratch +registers. + +Special-purpose registers are as follows: + +| Register | Call meaning | Return meaning | Body meaning | +| --- | --- | --- | --- | +| RSP | Stack pointer | Same | Same | +| R30 | Link register | Same | Scratch (non-leaf functions) | +| R29 | Frame pointer | Same | Same | +| R28 | Current goroutine | Same | Same | +| R27 | Scratch | Scratch | Scratch | +| R26 | Closure context pointer | Scratch | Scratch | +| R18 | Reserved (not used) | Same | Same | +| ZR | Zero value | Same | Same | + +*Rationale*: These register meanings are compatible with Go’s +stack-based calling convention. + +*Rationale*: The link register, R30, holds the function return +address at the function entry. For functions that have frames +(including most non-leaf functions), R30 is saved to stack in the +function prologue and restored in the epilogue. Within the function +body, R30 can be used as a scratch register. + +*Implementation note*: Registers with fixed meaning at calls but not +in function bodies must be initialized by "injected" calls such as +signal-based panics. + +#### Stack layout + +The stack pointer, RSP, grows down and is always aligned to 16 bytes. + +*Rationale*: The arm64 architecture requires the stack pointer to be +16-byte aligned. + +A function's stack frame, after the frame is created, is laid out as +follows: + + +------------------------------+ + | ... locals ... | + | ... outgoing arguments ... | + | return PC | ← RSP points to + | frame pointer on entry | + +------------------------------+ ↓ lower addresses + +The "return PC" is loaded to the link register, R30, as part of the +arm64 `CALL` operation. + +On entry, a function subtracts from RSP to open its stack frame, and +saves the values of R30 and R29 at the bottom of the frame. +Specifically, R30 is saved at 0(RSP) and R29 is saved at -8(RSP), +after RSP is updated. + +A leaf function that does not require any stack space may omit the +saved R30 and R29. + +The Go ABI's use of R29 as a frame pointer register is compatible with +arm64 architecture requirement so that Go can inter-operate with platform +debuggers and profilers. + +This stack layout is used by both register-based (ABIInternal) and +stack-based (ABI0) calling conventions. + +#### Flags + +The arithmetic status flags (NZCV) are treated like scratch registers +and not preserved across calls. +All other bits in PSTATE are system flags and are not modified by Go. + +The floating-point status register (FPSR) is treated like scratch +registers and not preserved across calls. + +At calls, the floating-point control register (FPCR) bits are always +set as follows: + +| Flag | Bit | Value | Meaning | +| --- | --- | --- | --- | +| DN | 25 | 0 | Propagate NaN operands | +| FZ | 24 | 0 | Do not flush to zero | +| RC | 23/22 | 0 (RN) | Round to nearest, choose even if tied | +| IDE | 15 | 0 | Denormal operations trap disabled | +| IXE | 12 | 0 | Inexact trap disabled | +| UFE | 11 | 0 | Underflow trap disabled | +| OFE | 10 | 0 | Overflow trap disabled | +| DZE | 9 | 0 | Divide-by-zero trap disabled | +| IOE | 8 | 0 | Invalid operations trap disabled | +| NEP | 2 | 0 | Scalar operations do not affect higher elements in vector registers | +| AH | 1 | 0 | No alternate handling of de-normal inputs | +| FIZ | 0 | 0 | Do not zero de-normals | + +*Rationale*: Having a fixed FPCR control configuration allows Go +functions to use floating-point and vector (SIMD) operations without +modifying or saving the FPCR. +Functions are allowed to modify it between calls (as long as they +restore it), but as of this writing Go code never does. + +### ppc64 architecture + +The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments +and results. + +It uses F1 – F12 for floating-point arguments and results. + +Register R31 is a permanent scratch register in Go. + +Special-purpose registers used within Go generated code and Go +assembly code are as follows: + +| Register | Call meaning | Return meaning | Body meaning | +| --- | --- | --- | --- | +| R0 | Zero value | Same | Same | +| R1 | Stack pointer | Same | Same | +| R2 | TOC register | Same | Same | +| R11 | Closure context pointer | Scratch | Scratch | +| R12 | Function address on indirect calls | Scratch | Scratch | +| R13 | TLS pointer | Same | Same | +| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero | +| R30 | Current goroutine | Same | Same | +| R31 | Scratch | Scratch | Scratch | +| LR | Link register | Link register | Scratch | +*Rationale*: These register meanings are compatible with Go’s +stack-based calling convention. + +The link register, LR, holds the function return +address at the function entry and is set to the correct return +address before exiting the function. It is also used +in some cases as the function address when doing an indirect call. + +The register R2 contains the address of the TOC (table of contents) which +contains data or code addresses used when generating position independent +code. Non-Go code generated when using cgo contains TOC-relative addresses +which depend on R2 holding a valid TOC. Go code compiled with -shared or +-dynlink initializes and maintains R2 and uses it in some cases for +function calls; Go code compiled without these options does not modify R2. + +When making a function call R12 contains the function address for use by the +code to generate R2 at the beginning of the function. R12 can be used for +other purposes within the body of the function, such as trampoline generation. + +R20 and R21 are used in duffcopy and duffzero which could be generated +before arguments are saved so should not be used for register arguments. + +The Count register CTR can be used as the call target for some branch instructions. +It holds the return address when preemption has occurred. + +On PPC64 when a float32 is loaded it becomes a float64 in the register, which is +different from other platforms and that needs to be recognized by the internal +implementation of reflection so that float32 arguments are passed correctly. + +Registers R18 - R29 and F13 - F31 are considered scratch registers. + +#### Stack layout + +The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed +to 16 bytes when calling cgo. + +A function's stack frame, after the frame is created, is laid out as +follows: + + +------------------------------+ + | ... locals ... | + | ... outgoing arguments ... | + | 24 TOC register R2 save | When compiled with -shared/-dynlink + | 16 Unused in Go | Not used in Go + | 8 CR save | nonvolatile CR fields + | 0 return PC | ← R1 points to + +------------------------------+ ↓ lower addresses + +The "return PC" is loaded to the link register, LR, as part of the +ppc64 `BL` operations. + +On entry to a non-leaf function, the stack frame size is subtracted from R1 to +create its stack frame, and saves the value of LR at the bottom of the frame. + +A leaf function that does not require any stack space does not modify R1 and +does not save LR. + +*NOTE*: We might need to save the frame pointer on the stack as +in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers +and profilers. + +This stack layout is used by both register-based (ABIInternal) and +stack-based (ABI0) calling conventions. + +#### Flags + +The condition register consists of 8 condition code register fields +CR0-CR7. Go generated code only sets and uses CR0, commonly set by +compare functions and use to determine the target of a conditional +branch. The generated code does not set or use CR1-CR7. + +The floating point status and control register (FPSCR) is initialized +to 0 by the kernel at startup of the Go program and not changed by +the Go generated code. + +### riscv64 architecture + +The riscv64 architecture uses X10 – X17, X8, X9, X18 – X23 for integer arguments +and results. + +It uses F10 – F17, F8, F9, F18 – F23 for floating-point arguments and results. + +Special-purpose registers used within Go generated code and Go +assembly code are as follows: + +| Register | Call meaning | Return meaning | Body meaning | +| --- | --- | --- | --- | +| X0 | Zero value | Same | Same | +| X1 | Link register | Link register | Scratch | +| X2 | Stack pointer | Same | Same | +| X3 | Global pointer | Same | Used by dynamic linker | +| X4 | TLS (thread pointer) | TLS | Scratch | +| X24,X25 | Scratch | Scratch | Used by duffcopy, duffzero | +| X26 | Closure context pointer | Scratch | Scratch | +| X27 | Current goroutine | Same | Same | +| X31 | Scratch | Scratch | Scratch | + +*Rationale*: These register meanings are compatible with Go’s +stack-based calling convention. Context register X20 will change to X26, +duffcopy, duffzero register will change to X24, X25 before this register ABI been adopted. +X10 – X17, X8, X9, X18 – X23, is the same order as A0 – A7, S0 – S7 in platform ABI. +F10 – F17, F8, F9, F18 – F23, is the same order as FA0 – FA7, FS0 – FS7 in platform ABI. +X8 – X23, F8 – F15 are used for compressed instruction (RVC) which will benefit code size in the future. + +#### Stack layout + +The stack pointer, X2, grows down and is aligned to 8 bytes. + +A function's stack frame, after the frame is created, is laid out as +follows: + + +------------------------------+ + | ... locals ... | + | ... outgoing arguments ... | + | return PC | ← X2 points to + +------------------------------+ ↓ lower addresses + +The "return PC" is loaded to the link register, X1, as part of the +riscv64 `CALL` operation. + +#### Flags + +The riscv64 has Zicsr extension for control and status register (CSR) and +treated as scratch register. +All bits in CSR are system flags and are not modified by Go. + ## Future directions ### Spill path improvements diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go index b68ef274f379e0..60e12630c5580e 100644 --- a/src/cmd/compile/doc.go +++ b/src/cmd/compile/doc.go @@ -44,6 +44,8 @@ Flags: Print compiler version and exit. -asmhdr file Write assembly header to file. + -asan + Insert calls to C/C++ address sanitizer. -buildid id Record id as the build id in the export metadata. -blockprofile file @@ -66,9 +68,6 @@ Flags: -importcfg file Read import configuration from file. In the file, set importmap, packagefile to specify import resolution. - -importmap old=new - Interpret import "old" as import "new" during compilation. - The option may be repeated to add multiple mappings. -installsuffix suffix Look for packages in $GOROOT/pkg/$GOOS_$GOARCH_suffix instead of $GOROOT/pkg/$GOOS_$GOARCH. @@ -217,11 +216,13 @@ calling the function. //go:uintptrescapes The //go:uintptrescapes directive must be followed by a function declaration. -It specifies that the function's uintptr arguments may be pointer values -that have been converted to uintptr and must be treated as such by the -garbage collector. The conversion from pointer to uintptr must appear in -the argument list of any call to this function. This directive is necessary -for some low-level system call implementations and should be avoided otherwise. +It specifies that the function's uintptr arguments may be pointer values that +have been converted to uintptr and must be on the heap and kept alive for the +duration of the call, even though from the types alone it would appear that the +object is no longer needed during the call. The conversion from pointer to +uintptr must appear in the argument list of any call to this function. This +directive is necessary for some low-level system call implementations and +should be avoided otherwise. //go:noinline diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index d657ddc867bad0..09bc0fbf85fdc3 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -144,7 +144,7 @@ func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64) } func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type { - w := t.Width + w := t.Size() if w == 0 { return rts } @@ -193,12 +193,12 @@ func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type { // to input offsets, and returns the longer slice and the next unused offset. func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) { at = align(at, t) - w := t.Width + w := t.Size() if w == 0 { return offsets, at } if t.IsScalar() || t.IsPtrShaped() { - if t.IsComplex() || int(t.Width) > types.RegSize { // complex and *int64 on 32-bit + if t.IsComplex() || int(t.Size()) > types.RegSize { // complex and *int64 on 32-bit s := w / 2 return append(offsets, at, at+s), at + w } else { @@ -214,7 +214,7 @@ func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int6 case types.TSTRUCT: for i, f := range t.FieldSlice() { offsets, at = appendParamOffsets(offsets, at, f.Type) - if f.Type.Width == 0 && i == t.NumFields()-1 { + if f.Type.Size() == 0 && i == t.NumFields()-1 { at++ // last field has zero width } } @@ -258,7 +258,7 @@ type RegAmounts struct { // by the ABI rules for parameter passing and result returning. type ABIConfig struct { // Do we need anything more than this? - offsetForLocals int64 // e.g., obj.(*Link).FixedFrameSize() -- extra linkage information on some architectures. + offsetForLocals int64 // e.g., obj.(*Link).Arch.FixedFrameSize -- extra linkage information on some architectures. regAmounts RegAmounts regsForTypeCache map[*types.Type]int } @@ -359,7 +359,7 @@ func (config *ABIConfig) ABIAnalyzeTypes(rcvr *types.Type, ins, outs []*types.Ty result.inparams = append(result.inparams, s.assignParamOrReturn(t, nil, false)) } - s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize)) + s.stackOffset = types.RoundUp(s.stackOffset, int64(types.RegSize)) result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs // Outputs @@ -403,7 +403,7 @@ func (config *ABIConfig) ABIAnalyzeFuncType(ft *types.Func) *ABIParamResultInfo result.inparams = append(result.inparams, s.assignParamOrReturn(f.Type, f.Nname, false)) } - s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize)) + s.stackOffset = types.RoundUp(s.stackOffset, int64(types.RegSize)) result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs // Outputs @@ -531,7 +531,7 @@ type assignState struct { // align returns a rounded up to t's alignment func align(a int64, t *types.Type) int64 { - return alignTo(a, int(t.Align)) + return alignTo(a, int(uint8(t.Alignment()))) } // alignTo returns a rounded up to t, where t must be 0 or a power of 2. @@ -539,14 +539,14 @@ func alignTo(a int64, t int) int64 { if t == 0 { return a } - return types.Rnd(a, int64(t)) + return types.RoundUp(a, int64(t)) } // stackSlot returns a stack offset for a param or result of the // specified type. func (state *assignState) stackSlot(t *types.Type) int64 { rv := align(state.stackOffset, t) - state.stackOffset = rv + t.Width + state.stackOffset = rv + t.Size() return rv } @@ -554,7 +554,7 @@ func (state *assignState) stackSlot(t *types.Type) int64 { // that we've just determined to be register-assignable. The number of registers // needed is assumed to be stored in state.pUsed. func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex { - if t.Width == 0 { + if t.Size() == 0 { return regs } ri := state.rUsed.intRegs @@ -647,7 +647,7 @@ func (state *assignState) floatUsed() int { // can register allocate, FALSE otherwise (and updates state // accordingly). func (state *assignState) regassignIntegral(t *types.Type) bool { - regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize)) + regsNeeded := int(types.RoundUp(t.Size(), int64(types.PtrSize)) / int64(types.PtrSize)) if t.IsComplex() { regsNeeded = 2 } @@ -715,21 +715,25 @@ func setup() { synthOnce.Do(func() { fname := types.BuiltinPkg.Lookup nxp := src.NoXPos - unsp := types.Types[types.TUNSAFEPTR] - ui := types.Types[types.TUINTPTR] + bp := types.NewPtr(types.Types[types.TUINT8]) + it := types.Types[types.TINT] synthSlice = types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(nxp, fname("ptr"), unsp), - types.NewField(nxp, fname("len"), ui), - types.NewField(nxp, fname("cap"), ui), + types.NewField(nxp, fname("ptr"), bp), + types.NewField(nxp, fname("len"), it), + types.NewField(nxp, fname("cap"), it), }) + types.CalcStructSize(synthSlice) synthString = types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(nxp, fname("data"), unsp), - types.NewField(nxp, fname("len"), ui), + types.NewField(nxp, fname("data"), bp), + types.NewField(nxp, fname("len"), it), }) + types.CalcStructSize(synthString) + unsp := types.Types[types.TUNSAFEPTR] synthIface = types.NewStruct(types.NoPkg, []*types.Field{ types.NewField(nxp, fname("f1"), unsp), types.NewField(nxp, fname("f2"), unsp), }) + types.CalcStructSize(synthIface) }) } @@ -764,10 +768,10 @@ func (state *assignState) regassign(pt *types.Type) bool { // ABIParamResultInfo held in 'state'. func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment { state.pUsed = RegAmounts{} - if pt.Width == types.BADWIDTH { + if pt.Size() == types.BADWIDTH { base.Fatalf("should never happen") panic("unreachable") - } else if pt.Width == 0 { + } else if pt.Size() == 0 { return state.stackAllocate(pt, n) } else if state.regassign(pt) { return state.regAllocate(pt, n, isReturn) @@ -777,24 +781,24 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is } // ComputePadding returns a list of "post element" padding values in -// the case where we have a structure being passed in registers. Give -// a param assignment corresponding to a struct, it returns a list of -// contaning padding values for each field, e.g. the Kth element in +// the case where we have a structure being passed in registers. Given +// a param assignment corresponding to a struct, it returns a list +// containing padding values for each field, e.g. the Kth element in // the list is the amount of padding between field K and the following -// field. For things that are not struct (or structs without padding) +// field. For things that are not structs (or structs without padding) // it returns a list of zeros. Example: // -// type small struct { -// x uint16 -// y uint8 -// z int32 -// w int32 -// } +// type small struct { +// x uint16 +// y uint8 +// z int32 +// w int32 +// } // // For this struct we would return a list [0, 1, 0, 0], meaning that // we have one byte of padding after the second field, and no bytes of -// padding after any of the other fields. Input parameter "storage" -// is with enough capacity to accommodate padding elements for +// padding after any of the other fields. Input parameter "storage" is +// a slice with enough capacity to accommodate padding elements for // the architected register set in question. func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 { nr := len(pa.Registers) diff --git a/src/cmd/compile/internal/abt/avlint32.go b/src/cmd/compile/internal/abt/avlint32.go new file mode 100644 index 00000000000000..eed5fa5d38b7b4 --- /dev/null +++ b/src/cmd/compile/internal/abt/avlint32.go @@ -0,0 +1,832 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abt + +import ( + "fmt" + "strconv" + "strings" +) + +const ( + LEAF_HEIGHT = 1 + ZERO_HEIGHT = 0 + NOT_KEY32 = int32(-0x80000000) +) + +// T is the exported applicative balanced tree data type. +// A T can be used as a value; updates to one copy of the value +// do not change other copies. +type T struct { + root *node32 + size int +} + +// node32 is the internal tree node data type +type node32 struct { + // Standard conventions hold for left = smaller, right = larger + left, right *node32 + data interface{} + key int32 + height_ int8 +} + +func makeNode(key int32) *node32 { + return &node32{key: key, height_: LEAF_HEIGHT} +} + +// IsSingle returns true iff t is empty. +func (t *T) IsEmpty() bool { + return t.root == nil +} + +// IsSingle returns true iff t is a singleton (leaf). +func (t *T) 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 *T) VisitInOrder(f func(int32, interface{})) { + if t.root == nil { + return + } + t.root.visitInOrder(f) +} + +func (n *node32) nilOrData() interface{} { + if n == nil { + return nil + } + return n.data +} + +func (n *node32) nilOrKeyAndData() (k int32, d interface{}) { + if n == nil { + k = NOT_KEY32 + d = nil + } else { + k = n.key + d = n.data + } + return +} + +func (n *node32) height() int8 { + if n == nil { + return 0 + } + return n.height_ +} + +// Find returns the data associated with x in the tree, or +// nil if x is not in the tree. +func (t *T) Find(x int32) interface{} { + return t.root.find(x).nilOrData() +} + +// Insert either adds x to the tree if x was not previously +// a key in the tree, or updates the data for x in the tree if +// x was already a key in the tree. The previous data associated +// with x is returned, and is nil if x was not previously a +// key in the tree. +func (t *T) Insert(x int32, data interface{}) interface{} { + if x == NOT_KEY32 { + panic("Cannot use sentinel value -0x80000000 as key") + } + n := t.root + var newroot *node32 + var o *node32 + if n == nil { + n = makeNode(x) + newroot = n + } else { + newroot, n, o = n.aInsert(x) + } + var r interface{} + if o != nil { + r = o.data + } else { + t.size++ + } + n.data = data + t.root = newroot + return r +} + +func (t *T) Copy() *T { + u := *t + return &u +} + +func (t *T) Delete(x int32) interface{} { + n := t.root + if n == nil { + return nil + } + d, s := n.aDelete(x) + if d == nil { + return nil + } + t.root = s + t.size-- + return d.data +} + +func (t *T) DeleteMin() (int32, interface{}) { + n := t.root + if n == nil { + return NOT_KEY32, nil + } + d, s := n.aDeleteMin() + if d == nil { + return NOT_KEY32, nil + } + t.root = s + t.size-- + return d.key, d.data +} + +func (t *T) DeleteMax() (int32, interface{}) { + n := t.root + if n == nil { + return NOT_KEY32, nil + } + d, s := n.aDeleteMax() + if d == nil { + return NOT_KEY32, nil + } + t.root = s + t.size-- + return d.key, d.data +} + +func (t *T) Size() int { + return t.size +} + +// Intersection returns the intersection of t and u, where the result +// data for any common keys is given by f(t's data, u's data) -- f need +// not be symmetric. If f returns nil, then the key and data are not +// added to the result. If f itself is nil, then whatever value was +// already present in the smaller set is used. +func (t *T) Intersection(u *T, f func(x, y interface{}) interface{}) *T { + if t.Size() == 0 || u.Size() == 0 { + return &T{} + } + + // For faster execution and less allocation, prefer t smaller, iterate over t. + if t.Size() <= u.Size() { + v := t.Copy() + for it := t.Iterator(); !it.Done(); { + k, d := it.Next() + e := u.Find(k) + if e == nil { + v.Delete(k) + continue + } + if f == nil { + continue + } + if c := f(d, e); c != d { + if c == nil { + v.Delete(k) + } else { + v.Insert(k, c) + } + } + } + return v + } + v := u.Copy() + for it := u.Iterator(); !it.Done(); { + k, e := it.Next() + d := t.Find(k) + if d == nil { + v.Delete(k) + continue + } + if f == nil { + continue + } + if c := f(d, e); c != d { + if c == nil { + v.Delete(k) + } else { + v.Insert(k, c) + } + } + } + + return v +} + +// Union returns the union of t and u, where the result data for any common keys +// is given by f(t's data, u's data) -- f need not be symmetric. If f returns nil, +// then the key and data are not added to the result. If f itself is nil, then +// whatever value was already present in the larger set is used. +func (t *T) Union(u *T, f func(x, y interface{}) interface{}) *T { + if t.Size() == 0 { + return u + } + if u.Size() == 0 { + return t + } + + if t.Size() >= u.Size() { + v := t.Copy() + for it := u.Iterator(); !it.Done(); { + k, e := it.Next() + d := t.Find(k) + if d == nil { + v.Insert(k, e) + continue + } + if f == nil { + continue + } + if c := f(d, e); c != d { + if c == nil { + v.Delete(k) + } else { + v.Insert(k, c) + } + } + } + return v + } + + v := u.Copy() + for it := t.Iterator(); !it.Done(); { + k, d := it.Next() + e := u.Find(k) + if e == nil { + v.Insert(k, d) + continue + } + if f == nil { + continue + } + if c := f(d, e); c != d { + if c == nil { + v.Delete(k) + } else { + v.Insert(k, c) + } + } + } + return v +} + +// Difference returns the difference of t and u, subject to the result +// of f applied to data corresponding to equal keys. If f returns nil +// (or if f is nil) then the key+data are excluded, as usual. If f +// returns not-nil, then that key+data pair is inserted. instead. +func (t *T) Difference(u *T, f func(x, y interface{}) interface{}) *T { + if t.Size() == 0 { + return &T{} + } + if u.Size() == 0 { + return t + } + v := t.Copy() + for it := t.Iterator(); !it.Done(); { + k, d := it.Next() + e := u.Find(k) + if e != nil { + if f == nil { + v.Delete(k) + continue + } + c := f(d, e) + if c == nil { + v.Delete(k) + continue + } + if c != d { + v.Insert(k, c) + } + } + } + return v +} + +func (t *T) Iterator() Iterator { + return Iterator{it: t.root.iterator()} +} + +func (t *T) Equals(u *T) bool { + if t == u { + return true + } + if t.Size() != u.Size() { + return false + } + return t.root.equals(u.root) +} + +func (t *T) String() string { + var b strings.Builder + first := true + for it := t.Iterator(); !it.Done(); { + k, v := it.Next() + if first { + first = false + } else { + b.WriteString("; ") + } + b.WriteString(strconv.FormatInt(int64(k), 10)) + b.WriteString(":") + b.WriteString(fmt.Sprint(v)) + } + return b.String() +} + +func (t *node32) equals(u *node32) bool { + if t == u { + return true + } + it, iu := t.iterator(), u.iterator() + for !it.done() && !iu.done() { + nt := it.next() + nu := iu.next() + if nt == nu { + continue + } + if nt.key != nu.key { + return false + } + if nt.data != nu.data { + return false + } + } + return it.done() == iu.done() +} + +func (t *T) Equiv(u *T, eqv func(x, y interface{}) bool) bool { + if t == u { + return true + } + if t.Size() != u.Size() { + return false + } + return t.root.equiv(u.root, eqv) +} + +func (t *node32) equiv(u *node32, eqv func(x, y interface{}) bool) bool { + if t == u { + return true + } + it, iu := t.iterator(), u.iterator() + for !it.done() && !iu.done() { + nt := it.next() + nu := iu.next() + if nt == nu { + continue + } + if nt.key != nu.key { + return false + } + if !eqv(nt.data, nu.data) { + return false + } + } + return it.done() == iu.done() +} + +type iterator struct { + parents []*node32 +} + +type Iterator struct { + it iterator +} + +func (it *Iterator) Next() (int32, interface{}) { + x := it.it.next() + if x == nil { + return NOT_KEY32, nil + } + return x.key, x.data +} + +func (it *Iterator) Done() bool { + return len(it.it.parents) == 0 +} + +func (t *node32) iterator() iterator { + if t == nil { + return iterator{} + } + it := iterator{parents: make([]*node32, 0, int(t.height()))} + it.leftmost(t) + return it +} + +func (it *iterator) leftmost(t *node32) { + for t != nil { + it.parents = append(it.parents, t) + t = t.left + } +} + +func (it *iterator) done() bool { + return len(it.parents) == 0 +} + +func (it *iterator) next() *node32 { + l := len(it.parents) + if l == 0 { + return nil + } + x := it.parents[l-1] // return value + if x.right != nil { + it.leftmost(x.right) + return x + } + // discard visited top of parents + l-- + it.parents = it.parents[:l] + y := x // y is known visited/returned + for l > 0 && y == it.parents[l-1].right { + y = it.parents[l-1] + l-- + it.parents = it.parents[:l] + } + + return x +} + +// Min returns the minimum element of t. +// If t is empty, then (NOT_KEY32, nil) is returned. +func (t *T) Min() (k int32, d interface{}) { + return t.root.min().nilOrKeyAndData() +} + +// Max returns the maximum element of t. +// If t is empty, then (NOT_KEY32, nil) is returned. +func (t *T) Max() (k int32, d interface{}) { + return t.root.max().nilOrKeyAndData() +} + +// Glb returns the greatest-lower-bound-exclusive of x and the associated +// data. If x has no glb in the tree, then (NOT_KEY32, nil) is returned. +func (t *T) Glb(x int32) (k int32, d interface{}) { + return t.root.glb(x, false).nilOrKeyAndData() +} + +// GlbEq returns the greatest-lower-bound-inclusive of x and the associated +// data. If x has no glbEQ in the tree, then (NOT_KEY32, nil) is returned. +func (t *T) GlbEq(x int32) (k int32, d interface{}) { + return t.root.glb(x, true).nilOrKeyAndData() +} + +// Lub returns the least-upper-bound-exclusive of x and the associated +// data. If x has no lub in the tree, then (NOT_KEY32, nil) is returned. +func (t *T) Lub(x int32) (k int32, d interface{}) { + return t.root.lub(x, false).nilOrKeyAndData() +} + +// LubEq returns the least-upper-bound-inclusive of x and the associated +// data. If x has no lubEq in the tree, then (NOT_KEY32, nil) is returned. +func (t *T) LubEq(x int32) (k int32, d interface{}) { + return t.root.lub(x, true).nilOrKeyAndData() +} + +func (t *node32) isLeaf() bool { + return t.left == nil && t.right == nil && t.height_ == LEAF_HEIGHT +} + +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) 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 allow_eq && key == t.key { + 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 allow_eq && key == t.key { + 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) aInsert(x int32) (newroot, newnode, oldnode *node32) { + // oldnode default of nil is good, others should be assigned. + if x == t.key { + oldnode = t + newt := *t + newnode = &newt + newroot = newnode + return + } + if x < t.key { + if t.left == nil { + t = t.copy() + n := makeNode(x) + t.left = n + newnode = n + newroot = t + t.height_ = 2 // was balanced w/ 0, sibling is height 0 or 1 + return + } + var new_l *node32 + new_l, newnode, oldnode = t.left.aInsert(x) + t = t.copy() + t.left = new_l + if new_l.height() > 1+t.right.height() { + newroot = t.aLeftIsHigh(newnode) + } else { + t.height_ = 1 + max(t.left.height(), t.right.height()) + newroot = t + } + } else { // x > t.key + if t.right == nil { + t = t.copy() + n := makeNode(x) + t.right = n + newnode = n + newroot = t + t.height_ = 2 // was balanced w/ 0, sibling is height 0 or 1 + return + } + var new_r *node32 + new_r, newnode, oldnode = t.right.aInsert(x) + t = t.copy() + t.right = new_r + if new_r.height() > 1+t.left.height() { + newroot = t.aRightIsHigh(newnode) + } else { + t.height_ = 1 + max(t.left.height(), t.right.height()) + newroot = t + } + } + return +} + +func (t *node32) aDelete(key int32) (deleted, newSubTree *node32) { + if t == nil { + return nil, nil + } + + if key < t.key { + oh := t.left.height() + d, tleft := t.left.aDelete(key) + if tleft == t.left { + return d, t + } + return d, t.copy().aRebalanceAfterLeftDeletion(oh, tleft) + } else if key > t.key { + oh := t.right.height() + d, tright := t.right.aDelete(key) + if tright == t.right { + return d, t + } + return d, t.copy().aRebalanceAfterRightDeletion(oh, tright) + } + + if t.height() == LEAF_HEIGHT { + return t, nil + } + + // Interior delete by removing left.Max or right.Min, + // then swapping contents + if t.left.height() > t.right.height() { + oh := t.left.height() + d, tleft := t.left.aDeleteMax() + r := t + t = t.copy() + t.data, t.key = d.data, d.key + return r, t.aRebalanceAfterLeftDeletion(oh, tleft) + } + + oh := t.right.height() + d, tright := t.right.aDeleteMin() + r := t + t = t.copy() + t.data, t.key = d.data, d.key + return r, t.aRebalanceAfterRightDeletion(oh, tright) +} + +func (t *node32) aDeleteMin() (deleted, newSubTree *node32) { + if t == nil { + return nil, nil + } + if t.left == nil { // leaf or left-most + return t, t.right + } + oh := t.left.height() + d, tleft := t.left.aDeleteMin() + if tleft == t.left { + return d, t + } + return d, t.copy().aRebalanceAfterLeftDeletion(oh, tleft) +} + +func (t *node32) aDeleteMax() (deleted, newSubTree *node32) { + if t == nil { + return nil, nil + } + + if t.right == nil { // leaf or right-most + return t, t.left + } + + oh := t.right.height() + d, tright := t.right.aDeleteMax() + if tright == t.right { + return d, t + } + return d, t.copy().aRebalanceAfterRightDeletion(oh, tright) +} + +func (t *node32) aRebalanceAfterLeftDeletion(oldLeftHeight int8, tleft *node32) *node32 { + t.left = tleft + + if oldLeftHeight == tleft.height() || oldLeftHeight == t.right.height() { + // this node is still balanced and its height is unchanged + return t + } + + if oldLeftHeight > t.right.height() { + // left was larger + t.height_-- + return t + } + + // left height fell by 1 and it was already less than right height + t.right = t.right.copy() + return t.aRightIsHigh(nil) +} + +func (t *node32) aRebalanceAfterRightDeletion(oldRightHeight int8, tright *node32) *node32 { + t.right = tright + + if oldRightHeight == tright.height() || oldRightHeight == t.left.height() { + // this node is still balanced and its height is unchanged + return t + } + + if oldRightHeight > t.left.height() { + // left was larger + t.height_-- + return t + } + + // right height fell by 1 and it was already less than left height + t.left = t.left.copy() + return t.aLeftIsHigh(nil) +} + +// aRightIsHigh does rotations necessary to fix a high right child +// assume that t and t.right are already fresh copies. +func (t *node32) aRightIsHigh(newnode *node32) *node32 { + right := t.right + if right.right.height() < right.left.height() { + // double rotation + if newnode != right.left { + right.left = right.left.copy() + } + t.right = right.leftToRoot() + } + t = t.rightToRoot() + return t +} + +// aLeftIsHigh does rotations necessary to fix a high left child +// assume that t and t.left are already fresh copies. +func (t *node32) aLeftIsHigh(newnode *node32) *node32 { + left := t.left + if left.left.height() < left.right.height() { + // double rotation + if newnode != left.right { + left.right = left.right.copy() + } + t.left = left.rightToRoot() + } + t = t.leftToRoot() + return t +} + +// rightToRoot does that rotation, modifying t and t.right in the process. +func (t *node32) rightToRoot() *node32 { + // this + // left right + // rl rr + // + // becomes + // + // right + // this rr + // left rl + // + right := t.right + rl := right.left + right.left = t + // parent's child ptr fixed in caller + t.right = rl + t.height_ = 1 + max(rl.height(), t.left.height()) + right.height_ = 1 + max(t.height(), right.right.height()) + return right +} + +// leftToRoot does that rotation, modifying t and t.left in the process. +func (t *node32) leftToRoot() *node32 { + // this + // left right + // ll lr + // + // becomes + // + // left + // ll this + // lr right + // + left := t.left + lr := left.right + left.right = t + // parent's child ptr fixed in caller + t.left = lr + t.height_ = 1 + max(lr.height(), t.right.height()) + left.height_ = 1 + max(t.height(), left.left.height()) + return left +} + +func max(a, b int8) int8 { + if a > b { + return a + } + return b +} + +func (t *node32) copy() *node32 { + u := *t + return &u +} diff --git a/src/cmd/compile/internal/abt/avlint32_test.go b/src/cmd/compile/internal/abt/avlint32_test.go new file mode 100644 index 00000000000000..7fa9ed4fd68236 --- /dev/null +++ b/src/cmd/compile/internal/abt/avlint32_test.go @@ -0,0 +1,700 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abt + +import ( + "fmt" + "strconv" + "testing" +) + +func makeTree(te *testing.T, x []int32, check bool) (t *T, k int, min, max int32) { + t = &T{} + k = 0 + min = int32(0x7fffffff) + max = int32(-0x80000000) + history := []*T{} + + for _, d := range x { + d = d + d // double everything for Glb/Lub testing. + + if check { + history = append(history, t.Copy()) + } + + t.Insert(d, stringer(fmt.Sprintf("%v", d))) + + k++ + if d < min { + min = d + } + if d > max { + max = d + } + + if !check { + continue + } + + for j, old := range history { + s, i := old.wellFormed() + if s != "" { + te.Errorf("Old tree consistency problem %v at k=%d, j=%d, old=\n%v, t=\n%v", s, k, j, old.DebugString(), t.DebugString()) + return + } + if i != j { + te.Errorf("Wrong tree size %v, expected %v for old %v", i, j, old.DebugString()) + } + } + s, i := t.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem at %v", s) + return + } + if i != k { + te.Errorf("Wrong tree size %v, expected %v for %v", i, k, t.DebugString()) + return + } + if t.Size() != k { + te.Errorf("Wrong t.Size() %v, expected %v for %v", t.Size(), k, t.DebugString()) + return + } + } + return +} + +func applicInsert(te *testing.T, x []int32) { + makeTree(te, x, true) +} + +func applicFind(te *testing.T, x []int32) { + t, _, _, _ := makeTree(te, x, false) + + for _, d := range x { + d = d + d // double everything for Glb/Lub testing. + s := fmt.Sprintf("%v", d) + f := t.Find(d) + + // data + if s != fmt.Sprint(f) { + te.Errorf("s(%v) != f(%v)", s, f) + } + } +} + +func applicBounds(te *testing.T, x []int32) { + t, _, min, max := makeTree(te, x, false) + for _, d := range x { + d = d + d // double everything for Glb/Lub testing. + 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.Sprint(g) { + te.Errorf("s(%v) != g(%v)", s, g) + } + if s != fmt.Sprint(l) { + te.Errorf("s(%v) != l(%v)", s, l) + } + if s != fmt.Sprint(ge) { + te.Errorf("s(%v) != ge(%v)", s, ge) + } + if s != fmt.Sprint(le) { + te.Errorf("s(%v) != le(%v)", s, le) + } + } + + for _, d := range x { + d = d + d // double everything for Glb/Lub testing. + 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.Sprint(ge) { + te.Errorf("s(%v) != ge(%v)", s, ge) + } + if s != fmt.Sprint(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(max + 1) + + if kg != NOT_KEY32 || kge != NOT_KEY32 || kl != NOT_KEY32 || kle != NOT_KEY32 { + te.Errorf("Got non-error-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 applicDeleteMin(te *testing.T, x []int32) { + t, _, _, _ := makeTree(te, x, false) + _, size := t.wellFormed() + history := []*T{} + for !t.IsEmpty() { + k, _ := t.Min() + history = append(history, t.Copy()) + kd, _ := t.DeleteMin() + if kd != k { + te.Errorf("Deleted minimum key %v not equal to minimum %v", kd, k) + } + for j, old := range history { + s, i := old.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem %s at old after DeleteMin, old=\n%stree=\n%v", s, old.DebugString(), t.DebugString()) + return + } + if i != len(x)-j { + te.Errorf("Wrong old tree size %v, expected %v after DeleteMin, old=\n%vtree\n%v", i, len(x)-j, old.DebugString(), t.DebugString()) + return + } + } + size-- + s, i := t.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem at %v after DeleteMin, tree=\n%v", s, t.DebugString()) + return + } + if i != size { + te.Errorf("Wrong tree size %v, expected %v after DeleteMin", i, size) + return + } + if t.Size() != size { + te.Errorf("Wrong t.Size() %v, expected %v for %v", t.Size(), i, t.DebugString()) + return + } + } +} + +func applicDeleteMax(te *testing.T, x []int32) { + t, _, _, _ := makeTree(te, x, false) + _, size := t.wellFormed() + history := []*T{} + + for !t.IsEmpty() { + k, _ := t.Max() + history = append(history, t.Copy()) + kd, _ := t.DeleteMax() + if kd != k { + te.Errorf("Deleted maximum key %v not equal to maximum %v", kd, k) + } + + for j, old := range history { + s, i := old.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem %s at old after DeleteMin, old=\n%stree=\n%v", s, old.DebugString(), t.DebugString()) + return + } + if i != len(x)-j { + te.Errorf("Wrong old tree size %v, expected %v after DeleteMin, old=\n%vtree\n%v", i, len(x)-j, old.DebugString(), t.DebugString()) + return + } + } + + size-- + s, i := t.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem at %v after DeleteMax, tree=\n%v", s, t.DebugString()) + return + } + if i != size { + te.Errorf("Wrong tree size %v, expected %v after DeleteMax", i, size) + return + } + if t.Size() != size { + te.Errorf("Wrong t.Size() %v, expected %v for %v", t.Size(), i, t.DebugString()) + return + } + } +} + +func applicDelete(te *testing.T, x []int32) { + t, _, _, _ := makeTree(te, x, false) + _, size := t.wellFormed() + history := []*T{} + + missing := t.Delete(11) + if missing != nil { + te.Errorf("Returned a value when there should have been none, %v", missing) + return + } + + s, i := t.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem at %v after delete of missing value, tree=\n%v", s, t.DebugString()) + return + } + if size != i { + te.Errorf("Delete of missing data should not change tree size, expected %d, got %d", size, i) + return + } + + for _, d := range x { + d += d // double + vWant := fmt.Sprintf("%v", d) + history = append(history, t.Copy()) + v := t.Delete(d) + + for j, old := range history { + s, i := old.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem %s at old after DeleteMin, old=\n%stree=\n%v", s, old.DebugString(), t.DebugString()) + return + } + if i != len(x)-j { + te.Errorf("Wrong old tree size %v, expected %v after DeleteMin, old=\n%vtree\n%v", i, len(x)-j, old.DebugString(), t.DebugString()) + return + } + } + + if v.(*sstring).s != vWant { + te.Errorf("Deleted %v expected %v but got %v", d, vWant, v) + return + } + size-- + s, i := t.wellFormed() + if s != "" { + te.Errorf("Tree consistency problem at %v after Delete %d, tree=\n%v", s, d, t.DebugString()) + return + } + if i != size { + te.Errorf("Wrong tree size %v, expected %v after Delete", i, size) + return + } + if t.Size() != size { + te.Errorf("Wrong t.Size() %v, expected %v for %v", t.Size(), i, t.DebugString()) + return + } + } + +} + +func applicIterator(te *testing.T, x []int32) { + t, _, _, _ := makeTree(te, x, false) + it := t.Iterator() + for !it.Done() { + k0, d0 := it.Next() + k1, d1 := t.DeleteMin() + if k0 != k1 || d0 != d1 { + te.Errorf("Iterator and deleteMin mismatch, k0, k1, d0, d1 = %v, %v, %v, %v", k0, k1, d0, d1) + return + } + } + if t.Size() != 0 { + te.Errorf("Iterator ended early, remaining tree = \n%s", t.DebugString()) + return + } +} + +func equiv(a, b interface{}) bool { + sa, sb := a.(*sstring), b.(*sstring) + return *sa == *sb +} + +func applicEquals(te *testing.T, x, y []int32) { + t, _, _, _ := makeTree(te, x, false) + u, _, _, _ := makeTree(te, y, false) + if !t.Equiv(t, equiv) { + te.Errorf("Equiv failure, t == t, =\n%v", t.DebugString()) + return + } + if !t.Equiv(t.Copy(), equiv) { + te.Errorf("Equiv failure, t == t.Copy(), =\n%v", t.DebugString()) + return + } + if !t.Equiv(u, equiv) { + te.Errorf("Equiv failure, t == u, =\n%v", t.DebugString()) + return + } + v := t.Copy() + + v.DeleteMax() + if t.Equiv(v, equiv) { + te.Errorf("!Equiv failure, t != v, =\n%v\nand%v\n", t.DebugString(), v.DebugString()) + return + } + + if v.Equiv(u, equiv) { + te.Errorf("!Equiv failure, v != u, =\n%v\nand%v\n", v.DebugString(), u.DebugString()) + return + } + +} + +func tree(x []int32) *T { + t := &T{} + for _, d := range x { + t.Insert(d, stringer(fmt.Sprintf("%v", d))) + } + return t +} + +func treePlus1(x []int32) *T { + t := &T{} + for _, d := range x { + t.Insert(d, stringer(fmt.Sprintf("%v", d+1))) + } + return t +} +func TestApplicInsert(t *testing.T) { + applicInsert(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}) + applicInsert(t, []int32{1, 2, 3, 4}) + applicInsert(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicInsert(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}) + applicInsert(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}) + applicInsert(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}) + applicInsert(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}) + applicInsert(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}) +} + +func TestApplicFind(t *testing.T) { + applicFind(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}) + applicFind(t, []int32{1, 2, 3, 4}) + applicFind(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicFind(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}) + applicFind(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}) + applicFind(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}) + applicFind(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}) + applicFind(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}) +} + +func TestBounds(t *testing.T) { + applicBounds(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}) + applicBounds(t, []int32{1, 2, 3, 4}) + applicBounds(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicBounds(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}) + applicBounds(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}) + applicBounds(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}) + applicBounds(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}) + applicBounds(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}) +} +func TestDeleteMin(t *testing.T) { + applicDeleteMin(t, []int32{1, 2, 3, 4}) + applicDeleteMin(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}) + applicDeleteMin(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicDeleteMin(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}) + applicDeleteMin(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}) + applicDeleteMin(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}) + applicDeleteMin(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}) + applicDeleteMin(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}) +} +func TestDeleteMax(t *testing.T) { + applicDeleteMax(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}) + applicDeleteMax(t, []int32{1, 2, 3, 4}) + applicDeleteMax(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicDeleteMax(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}) + applicDeleteMax(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}) + applicDeleteMax(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}) + applicDeleteMax(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}) + applicDeleteMax(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}) +} +func TestDelete(t *testing.T) { + applicDelete(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}) + applicDelete(t, []int32{1, 2, 3, 4}) + applicDelete(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicDelete(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}) + applicDelete(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}) + applicDelete(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}) + applicDelete(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}) + applicDelete(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}) +} +func TestIterator(t *testing.T) { + applicIterator(t, []int32{1, 2, 3, 4}) + applicIterator(t, []int32{1, 2, 3, 4, 5, 6, 7, 8, 9}) + applicIterator(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}) + applicIterator(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}) + applicIterator(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}) + applicIterator(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}) + applicIterator(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}) + applicIterator(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}) +} +func TestEquals(t *testing.T) { + applicEquals(t, []int32{1, 2, 3, 4}, []int32{4, 3, 2, 1}) + + applicEquals(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}, + []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}) + applicEquals(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}, + []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}) + applicEquals(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}, + []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}) +} + +func first(x, y interface{}) interface{} { + return x +} +func second(x, y interface{}) interface{} { + return y +} +func alwaysNil(x, y interface{}) interface{} { + return nil +} +func smaller(x, y interface{}) interface{} { + xi, _ := strconv.Atoi(fmt.Sprint(x)) + yi, _ := strconv.Atoi(fmt.Sprint(y)) + if xi < yi { + return x + } + return y +} +func assert(t *testing.T, expected, got *T, what string) { + s, _ := got.wellFormed() + if s != "" { + t.Errorf("Tree consistency problem %v for 'got' in assert for %s, tree=\n%v", s, what, got.DebugString()) + return + } + + if !expected.Equiv(got, equiv) { + t.Errorf("%s fail, expected\n%vgot\n%v\n", what, expected.DebugString(), got.DebugString()) + } +} + +func TestSetOps(t *testing.T) { + A := tree([]int32{1, 2, 3, 4}) + B := tree([]int32{3, 4, 5, 6, 7}) + + AIB := tree([]int32{3, 4}) + ADB := tree([]int32{1, 2}) + BDA := tree([]int32{5, 6, 7}) + AUB := tree([]int32{1, 2, 3, 4, 5, 6, 7}) + AXB := tree([]int32{1, 2, 5, 6, 7}) + + aib1 := A.Intersection(B, first) + assert(t, AIB, aib1, "aib1") + if A.Find(3) != aib1.Find(3) { + t.Errorf("Failed aliasing/reuse check, A/aib1") + } + aib2 := A.Intersection(B, second) + assert(t, AIB, aib2, "aib2") + if B.Find(3) != aib2.Find(3) { + t.Errorf("Failed aliasing/reuse check, B/aib2") + } + aib3 := B.Intersection(A, first) + assert(t, AIB, aib3, "aib3") + if A.Find(3) != aib3.Find(3) { + // A is smaller, intersection favors reuse from smaller when function is "first" + t.Errorf("Failed aliasing/reuse check, A/aib3") + } + aib4 := B.Intersection(A, second) + assert(t, AIB, aib4, "aib4") + if A.Find(3) != aib4.Find(3) { + t.Errorf("Failed aliasing/reuse check, A/aib4") + } + + aub1 := A.Union(B, first) + assert(t, AUB, aub1, "aub1") + if B.Find(3) != aub1.Find(3) { + // B is larger, union favors reuse from larger when function is "first" + t.Errorf("Failed aliasing/reuse check, A/aub1") + } + aub2 := A.Union(B, second) + assert(t, AUB, aub2, "aub2") + if B.Find(3) != aub2.Find(3) { + t.Errorf("Failed aliasing/reuse check, B/aub2") + } + aub3 := B.Union(A, first) + assert(t, AUB, aub3, "aub3") + if B.Find(3) != aub3.Find(3) { + t.Errorf("Failed aliasing/reuse check, B/aub3") + } + aub4 := B.Union(A, second) + assert(t, AUB, aub4, "aub4") + if A.Find(3) != aub4.Find(3) { + t.Errorf("Failed aliasing/reuse check, A/aub4") + } + + axb1 := A.Union(B, alwaysNil) + assert(t, AXB, axb1, "axb1") + axb2 := B.Union(A, alwaysNil) + assert(t, AXB, axb2, "axb2") + + adb := A.Difference(B, alwaysNil) + assert(t, ADB, adb, "adb") + bda := B.Difference(A, nil) + assert(t, BDA, bda, "bda") + + Ap1 := treePlus1([]int32{1, 2, 3, 4}) + + ada1_1 := A.Difference(Ap1, smaller) + assert(t, A, ada1_1, "ada1_1") + ada1_2 := Ap1.Difference(A, smaller) + assert(t, A, ada1_2, "ada1_2") + +} + +type sstring struct { + s string +} + +func (s *sstring) String() string { + return s.s +} + +func stringer(s string) interface{} { + return &sstring{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 *T) 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, keyMin, keyMax int32) (s string, i int) { + i = -1 // initialize to a failing value + s = "" // s is the reason for failure; empty means okay. + + if keyMin >= t.key { + s = " min >= t.key" + return + } + + if keyMax <= t.key { + s = " max <= t.key" + return + } + + l := t.left + r := t.right + + lh := l.height() + rh := r.height() + mh := max(lh, rh) + th := t.height() + dh := lh - rh + if dh < 0 { + dh = -dh + } + if dh > 1 { + s = fmt.Sprintf(" dh > 1, t=%d", t.key) + return + } + + if l == nil && r == nil { + if th != LEAF_HEIGHT { + s = " leaf height wrong" + return + } + } + + if th != mh+1 { + s = " th != mh + 1" + return + } + + if l != nil { + if th <= lh { + s = " t.height <= l.height" + } else if th > 2+lh { + s = " t.height > 2+l.height" + } else if t.key <= l.key { + s = " t.key <= l.key" + } + if s != "" { + return + } + + } + + if r != nil { + if th <= rh { + s = " t.height <= r.height" + } else if th > 2+rh { + s = " t.height > 2+r.height" + } else if t.key >= r.key { + s = " t.key >= r.key" + } + if s != "" { + return + } + } + + ii := 1 + if l != nil { + res, il := l.wellFormedSubtree(t, keyMin, t.key) + if res != "" { + s = ".L" + res + return + } + ii += il + } + if r != nil { + res, ir := r.wellFormedSubtree(t, t.key, keyMax) + if res != "" { + s = ".R" + res + return + } + ii += ir + } + i = ii + return +} + +func (t *T) DebugString() string { + if t.root == nil { + return "" + } + return t.root.DebugString(0) +} + +// DebugString prints the tree with nested information +// to allow an eyeball check on the tree balance. +func (t *node32) DebugString(indent int) string { + s := "" + if t.left != nil { + s = s + t.left.DebugString(indent+1) + } + for i := 0; i < indent; i++ { + s = s + " " + } + s = s + fmt.Sprintf("%v=%v:%d\n", t.key, t.data, t.height_) + if t.right != nil { + s = s + t.right.DebugString(indent+1) + } + return s +} diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go index 2785aa03368b78..ca44263afc476c 100644 --- a/src/cmd/compile/internal/amd64/galign.go +++ b/src/cmd/compile/internal/amd64/galign.go @@ -18,11 +18,10 @@ func Init(arch *ssagen.ArchInfo) { arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = ssaMarkMoves arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock - arch.LoadRegResults = loadRegResults + arch.LoadRegResult = loadRegResult arch.SpillArgReg = spillArgReg } diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go index 1484ad5404b479..b8dce81a92d2cc 100644 --- a/src/cmd/compile/internal/amd64/ggen.go +++ b/src/cmd/compile/internal/amd64/ggen.go @@ -57,7 +57,6 @@ func dzDI(b int64) int64 { func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog { const ( r13 = 1 << iota // if R13 is already zeroed. - x15 // if X15 is already zeroed. Note: in new ABI, X15 is always zero. ) if cnt == 0 { @@ -85,11 +84,6 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj. } p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R13, 0, obj.TYPE_MEM, x86.REG_SP, off) } else if !isPlan9 && cnt <= int64(8*types.RegSize) { - if !buildcfg.Experiment.RegabiG && *state&x15 == 0 { - p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0) - *state |= x15 - } - for i := int64(0); i < cnt/16; i++ { p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16) } @@ -98,10 +92,6 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj. p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16)) } } else if !isPlan9 && (cnt <= int64(128*types.RegSize)) { - if !buildcfg.Experiment.RegabiG && *state&x15 == 0 { - p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0) - *state |= x15 - } // Save DI to r12. With the amd64 Go register abi, DI can contain // an incoming parameter, whereas R12 is always scratch. p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_DI, 0, obj.TYPE_REG, x86.REG_R12, 0) diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index ca5f36e77598c6..0a95aaabd77c95 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -78,6 +78,8 @@ func storeByType(t *types.Type) obj.As { return x86.AMOVL case 8: return x86.AMOVQ + case 16: + return x86.AMOVUPS } } panic(fmt.Sprintf("bad store type %v", t)) @@ -111,7 +113,9 @@ func moveByType(t *types.Type) obj.As { } // opregreg emits instructions for -// dest := dest(To) op src(From) +// +// dest := dest(To) op src(From) +// // and also returns the created obj.Prog so it // may be further adjusted (offset, scale, etc). func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog { @@ -263,6 +267,49 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = lo p.SetFrom3Reg(hi) + case ssa.OpAMD64BLSIQ, ssa.OpAMD64BLSIL, + ssa.OpAMD64BLSMSKQ, ssa.OpAMD64BLSMSKL, + ssa.OpAMD64BLSRQ, ssa.OpAMD64BLSRL: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + + case ssa.OpAMD64ANDNQ, ssa.OpAMD64ANDNL: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + p.SetFrom3Reg(v.Args[1].Reg()) + + case ssa.OpAMD64SARXL, ssa.OpAMD64SARXQ, + ssa.OpAMD64SHLXL, ssa.OpAMD64SHLXQ, + ssa.OpAMD64SHRXL, ssa.OpAMD64SHRXQ: + p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg()) + p.SetFrom3Reg(v.Args[0].Reg()) + + case ssa.OpAMD64SHLXLload, ssa.OpAMD64SHLXQload, + ssa.OpAMD64SHRXLload, ssa.OpAMD64SHRXQload, + ssa.OpAMD64SARXLload, ssa.OpAMD64SARXQload: + p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg()) + m := obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[0].Reg()} + ssagen.AddAux(&m, v) + p.SetFrom3(m) + + case ssa.OpAMD64SHLXLloadidx1, ssa.OpAMD64SHLXLloadidx4, ssa.OpAMD64SHLXLloadidx8, + ssa.OpAMD64SHRXLloadidx1, ssa.OpAMD64SHRXLloadidx4, ssa.OpAMD64SHRXLloadidx8, + ssa.OpAMD64SARXLloadidx1, ssa.OpAMD64SARXLloadidx4, ssa.OpAMD64SARXLloadidx8, + ssa.OpAMD64SHLXQloadidx1, ssa.OpAMD64SHLXQloadidx8, + ssa.OpAMD64SHRXQloadidx1, ssa.OpAMD64SHRXQloadidx8, + ssa.OpAMD64SARXQloadidx1, ssa.OpAMD64SARXQloadidx8: + p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[2].Reg()) + m := obj.Addr{Type: obj.TYPE_MEM} + memIdx(&m, v) + ssagen.AddAux(&m, v) + p.SetFrom3(m) + case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU: // Arg[0] (the dividend) is in AX. // Arg[1] (the divisor) can be in any other register. @@ -271,11 +318,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { r := v.Args[1].Reg() // Zero extend dividend. - c := s.Prog(x86.AXORL) - c.From.Type = obj.TYPE_REG - c.From.Reg = x86.REG_DX - c.To.Type = obj.TYPE_REG - c.To.Reg = x86.REG_DX + opregreg(s, x86.AXORL, x86.REG_DX, x86.REG_DX) // Issue divide. p := s.Prog(v.Op.Asm()) @@ -345,11 +388,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { n1.To.Reg = x86.REG_AX // n % -1 == 0 - n2 := s.Prog(x86.AXORL) - n2.From.Type = obj.TYPE_REG - n2.From.Reg = x86.REG_DX - n2.To.Type = obj.TYPE_REG - n2.To.Reg = x86.REG_DX + opregreg(s, x86.AXORL, x86.REG_DX, x86.REG_DX) // TODO(khr): issue only the -1 fixup code we need. // For instance, if only the quotient is used, no point in zeroing the remainder. @@ -600,8 +639,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = r p.SetFrom3Reg(v.Args[0].Reg()) + case ssa.OpAMD64ANDQconst: + asm := v.Op.Asm() + // If the constant is positive and fits into 32 bits, use ANDL. + // This saves a few bytes of encoding. + if 0 <= v.AuxInt && v.AuxInt <= (1<<32-1) { + asm = x86.AANDL + } + p := s.Prog(asm) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst, - ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst, + ssa.OpAMD64ANDLconst, ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst, ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst, ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst, @@ -714,11 +766,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // If flags aren't live (indicated by v.Aux == nil), // then we can rewrite MOV $0, AX into XOR AX, AX. if v.AuxInt == 0 && v.Aux == nil { - p := s.Prog(x86.AXORL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = x + opregreg(s, x86.AXORL, x, x) break } @@ -741,7 +789,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Val = math.Float64frombits(uint64(v.AuxInt)) p.To.Type = obj.TYPE_REG p.To.Reg = x - case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload, ssa.OpAMD64MOVOload: + case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVOload, + ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload, + ssa.OpAMD64MOVBEQload, ssa.OpAMD64MOVBELload: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() @@ -749,7 +799,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1, - ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2: + ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2, + ssa.OpAMD64MOVBELloadidx1, ssa.OpAMD64MOVBELloadidx4, ssa.OpAMD64MOVBELloadidx8, ssa.OpAMD64MOVBEQloadidx1, ssa.OpAMD64MOVBEQloadidx8: p := s.Prog(v.Op.Asm()) memIdx(&p.From, v) ssagen.AddAux(&p.From, v) @@ -757,7 +808,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore, ssa.OpAMD64ADDQmodify, ssa.OpAMD64SUBQmodify, ssa.OpAMD64ANDQmodify, ssa.OpAMD64ORQmodify, ssa.OpAMD64XORQmodify, - ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify: + ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify, + ssa.OpAMD64MOVBEQstore, ssa.OpAMD64MOVBELstore, ssa.OpAMD64MOVBEWstore: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() @@ -770,7 +822,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpAMD64SUBLmodifyidx1, ssa.OpAMD64SUBLmodifyidx4, ssa.OpAMD64SUBLmodifyidx8, ssa.OpAMD64SUBQmodifyidx1, ssa.OpAMD64SUBQmodifyidx8, ssa.OpAMD64ANDLmodifyidx1, ssa.OpAMD64ANDLmodifyidx4, ssa.OpAMD64ANDLmodifyidx8, ssa.OpAMD64ANDQmodifyidx1, ssa.OpAMD64ANDQmodifyidx8, ssa.OpAMD64ORLmodifyidx1, ssa.OpAMD64ORLmodifyidx4, ssa.OpAMD64ORLmodifyidx8, ssa.OpAMD64ORQmodifyidx1, ssa.OpAMD64ORQmodifyidx8, - ssa.OpAMD64XORLmodifyidx1, ssa.OpAMD64XORLmodifyidx4, ssa.OpAMD64XORLmodifyidx8, ssa.OpAMD64XORQmodifyidx1, ssa.OpAMD64XORQmodifyidx8: + ssa.OpAMD64XORLmodifyidx1, ssa.OpAMD64XORLmodifyidx4, ssa.OpAMD64XORLmodifyidx8, ssa.OpAMD64XORQmodifyidx1, ssa.OpAMD64XORQmodifyidx8, + ssa.OpAMD64MOVBEWstoreidx1, ssa.OpAMD64MOVBEWstoreidx2, ssa.OpAMD64MOVBELstoreidx1, ssa.OpAMD64MOVBELstoreidx4, ssa.OpAMD64MOVBELstoreidx8, ssa.OpAMD64MOVBEQstoreidx1, ssa.OpAMD64MOVBEQstoreidx8: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[2].Reg() @@ -822,8 +875,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() ssagen.AddAux2(&p.To, v, sc.Off64()) - case ssa.OpAMD64MOVOstorezero: - if !buildcfg.Experiment.RegabiG || s.ABI != obj.ABIInternal { + case ssa.OpAMD64MOVOstoreconst: + sc := v.AuxValAndOff() + if sc.Val() != 0 { + v.Fatalf("MOVO for non zero constants not implemented: %s", v.LongString()) + } + + if s.ABI != obj.ABIInternal { // zero X15 manually opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) } @@ -832,7 +890,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Reg = x86.REG_X15 p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() - ssagen.AddAux(&p.To, v) + ssagen.AddAux2(&p.To, v, sc.Off64()) + case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1, ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8, ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8, @@ -914,7 +973,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpAMD64DUFFZERO: - if !buildcfg.Experiment.RegabiG || s.ABI != obj.ABIInternal { + if s.ABI != obj.ABIInternal { // zero X15 manually opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) } @@ -997,22 +1056,30 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // Closure pointer is DX. ssagen.CheckLoweredGetClosurePtr(v) case ssa.OpAMD64LoweredGetG: - if buildcfg.Experiment.RegabiG && s.ABI == obj.ABIInternal { + if s.ABI == obj.ABIInternal { v.Fatalf("LoweredGetG should not appear in ABIInternal") } r := v.Reg() getgFromTLS(s, r) - case ssa.OpAMD64CALLstatic: - if buildcfg.Experiment.RegabiG && s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal { + case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail: + if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal { // zeroing X15 when entering ABIInternal from ABI0 - opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) + if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9 + opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) + } // set G register from TLS getgFromTLS(s, x86.REG_R14) } + if v.Op == ssa.OpAMD64CALLtail { + s.TailCall(v) + break + } s.Call(v) - if buildcfg.Experiment.RegabiG && s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 { + if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 { // zeroing X15 when entering ABIInternal from ABI0 - opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) + if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9 + opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) + } // set G register from TLS getgFromTLS(s, x86.REG_R14) } @@ -1035,7 +1102,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } p := s.Prog(mov) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize // 0 on amd64, just to be consistent with other architectures p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -1089,15 +1156,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.SetFrom3Reg(v.Args[0].Reg()) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() - case ssa.OpAMD64POPCNTQ, ssa.OpAMD64POPCNTL: + case ssa.OpAMD64POPCNTQ, ssa.OpAMD64POPCNTL, + ssa.OpAMD64TZCNTQ, ssa.OpAMD64TZCNTL, + ssa.OpAMD64LZCNTQ, ssa.OpAMD64LZCNTL: if v.Args[0].Reg() != v.Reg() { - // POPCNT on Intel has a false dependency on the destination register. + // POPCNT/TZCNT/LZCNT have a false dependency on the destination register on Intel cpus. + // TZCNT/LZCNT problem affects pre-Skylake models. See discussion at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62011#c7. // Xor register with itself to break the dependency. - p := s.Prog(x86.AXORQ) - p.From.Type = obj.TYPE_REG - p.From.Reg = v.Reg() - p.To.Type = obj.TYPE_REG - p.To.Reg = v.Reg() + opregreg(s, x86.AXORL, v.Reg(), v.Reg()) } p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG @@ -1221,6 +1287,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() ssagen.AddAux(&p.To, v) + case ssa.OpAMD64PrefetchT0, ssa.OpAMD64PrefetchNTA: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() case ssa.OpClobber: p := s.Prog(x86.AMOVL) p.From.Type = obj.TYPE_CONST @@ -1300,20 +1370,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - if buildcfg.Experiment.RegabiG && s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal { - // zeroing X15 when entering ABIInternal from ABI0 - opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) - // set G register from TLS - getgFromTLS(s, x86.REG_R14) - } - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockAMD64EQF: s.CombJump(b, next, &eqfJumps) @@ -1343,25 +1402,30 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { } } + case ssa.BlockAMD64JUMPTABLE: + // JMP *(TABLE)(INDEX*8) + p := s.Prog(obj.AJMP) + p.To.Type = obj.TYPE_MEM + p.To.Reg = b.Controls[1].Reg() + p.To.Index = b.Controls[0].Reg() + p.To.Scale = 8 + // Save jump tables for later resolution of the target blocks. + s.JumpTables = append(s.JumpTables, b) + default: b.Fatalf("branch not implemented: %s", b.LongString()) } } -func loadRegResults(s *ssagen.State, f *ssa.Func) { - for _, o := range f.OwnAux.ABIInfo().OutParams() { - n := o.Name.(*ir.Name) - rts, offs := o.RegisterTypesAndOffsets() - for i := range o.Registers { - p := s.Prog(loadByType(rts[i])) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_AUTO - p.From.Sym = n.Linksym() - p.From.Offset = n.FrameOffset() + offs[i] - p.To.Type = obj.TYPE_REG - p.To.Reg = ssa.ObjRegForAbiReg(o.Registers[i], f.Config) - } - } +func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p := s.Prog(loadByType(t)) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_AUTO + p.From.Sym = n.Linksym() + p.From.Offset = n.FrameOffset() + off + p.To.Type = obj.TYPE_REG + p.To.Reg = reg + return p } func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go new file mode 100644 index 00000000000000..647bcdc2f07280 --- /dev/null +++ b/src/cmd/compile/internal/amd64/versions_test.go @@ -0,0 +1,423 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// When using GOEXPERIMENT=boringcrypto, the test program links in the boringcrypto syso, +// which does not respect GOAMD64, so we skip the test if boringcrypto is enabled. +//go:build !boringcrypto + +package amd64_test + +import ( + "bufio" + "debug/elf" + "debug/macho" + "errors" + "fmt" + "go/build" + "internal/testenv" + "io" + "math" + "math/bits" + "os" + "os/exec" + "regexp" + "runtime" + "strconv" + "strings" + "testing" +) + +// Test to make sure that when building for GOAMD64=v1, we don't +// use any >v1 instructions. +func TestGoAMD64v1(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skip("amd64-only test") + } + if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { + t.Skip("test only works on elf or macho platforms") + } + for _, tag := range build.Default.ToolTags { + if tag == "amd64.v2" { + t.Skip("compiling for GOAMD64=v2 or higher") + } + } + if os.Getenv("TESTGOAMD64V1") != "" { + t.Skip("recursive call") + } + + // Make a binary which will be a modified version of the + // currently running binary. + dst, err := os.CreateTemp("", "TestGoAMD64v1") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer os.Remove(dst.Name()) + dst.Chmod(0500) // make executable + + // Clobber all the non-v1 opcodes. + opcodes := map[string]bool{} + var features []string + for feature, opcodeList := range featureToOpcodes { + if runtimeFeatures[feature] { + features = append(features, fmt.Sprintf("cpu.%s=off", feature)) + } + for _, op := range opcodeList { + opcodes[op] = true + } + } + clobber(t, os.Args[0], dst, opcodes) + if err = dst.Close(); err != nil { + t.Fatalf("can't close binary: %v", err) + } + + // Run the resulting binary. + cmd := exec.Command(dst.Name()) + testenv.CleanCmdEnv(cmd) + cmd.Env = append(cmd.Env, "TESTGOAMD64V1=yes") + cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=%s", strings.Join(features, ","))) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("couldn't execute test: %s", err) + } + // Expect to see output of the form "PASS\n", unless the test binary + // was compiled for coverage (in which case there will be an extra line). + success := false + lines := strings.Split(string(out), "\n") + if len(lines) == 2 { + success = lines[0] == "PASS" && lines[1] == "" + } else if len(lines) == 3 { + success = lines[0] == "PASS" && + strings.HasPrefix(lines[1], "coverage") && lines[2] == "" + } + if !success { + t.Fatalf("test reported error: %s lines=%+v", string(out), lines) + } +} + +// Clobber copies the binary src to dst, replacing all the instructions in opcodes with +// faulting instructions. +func clobber(t *testing.T, src string, dst *os.File, opcodes map[string]bool) { + // Run objdump to get disassembly. + var re *regexp.Regexp + var disasm io.Reader + if false { + // TODO: go tool objdump doesn't disassemble the bmi1 instructions + // in question correctly. See issue 48584. + cmd := exec.Command("go", "tool", "objdump", src) + var err error + disasm, err = cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + re = regexp.MustCompile(`^[^:]*:[-0-9]+\s+0x([0-9a-f]+)\s+([0-9a-f]+)\s+([A-Z]+)`) + } else { + // TODO: we're depending on platform-native objdump here. Hence the Skipf + // below if it doesn't run for some reason. + cmd := exec.Command("objdump", "-d", src) + var err error + disasm, err = cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + if err := cmd.Start(); err != nil { + if errors.Is(err, exec.ErrNotFound) { + t.Skipf("can't run test due to missing objdump: %s", err) + } + t.Fatal(err) + } + re = regexp.MustCompile(`^\s*([0-9a-f]+):\s*((?:[0-9a-f][0-9a-f] )+)\s*([a-z0-9]+)`) + } + + // Find all the instruction addresses we need to edit. + virtualEdits := map[uint64]bool{} + scanner := bufio.NewScanner(disasm) + for scanner.Scan() { + line := scanner.Text() + parts := re.FindStringSubmatch(line) + if len(parts) == 0 { + continue + } + addr, err := strconv.ParseUint(parts[1], 16, 64) + if err != nil { + continue // not a hex address + } + opcode := strings.ToLower(parts[3]) + if !opcodes[opcode] { + continue + } + t.Logf("clobbering instruction %s", line) + n := (len(parts[2]) - strings.Count(parts[2], " ")) / 2 // number of bytes in instruction encoding + for i := 0; i < n; i++ { + // Only really need to make the first byte faulting, but might + // as well make all the bytes faulting. + virtualEdits[addr+uint64(i)] = true + } + } + + // Figure out where in the binary the edits must be done. + physicalEdits := map[uint64]bool{} + if e, err := elf.Open(src); err == nil { + for _, sec := range e.Sections { + vaddr := sec.Addr + paddr := sec.Offset + size := sec.Size + for a := range virtualEdits { + if a >= vaddr && a < vaddr+size { + physicalEdits[paddr+(a-vaddr)] = true + } + } + } + } else if m, err2 := macho.Open(src); err2 == nil { + for _, sec := range m.Sections { + vaddr := sec.Addr + paddr := uint64(sec.Offset) + size := sec.Size + for a := range virtualEdits { + if a >= vaddr && a < vaddr+size { + physicalEdits[paddr+(a-vaddr)] = true + } + } + } + } else { + t.Log(err) + t.Log(err2) + t.Fatal("executable format not elf or macho") + } + if len(virtualEdits) != len(physicalEdits) { + t.Fatal("couldn't find an instruction in text sections") + } + + // Copy source to destination, making edits along the way. + f, err := os.Open(src) + if err != nil { + t.Fatal(err) + } + r := bufio.NewReader(f) + w := bufio.NewWriter(dst) + a := uint64(0) + done := 0 + for { + b, err := r.ReadByte() + if err == io.EOF { + break + } + if err != nil { + t.Fatal("can't read") + } + if physicalEdits[a] { + b = 0xcc // INT3 opcode + done++ + } + err = w.WriteByte(b) + if err != nil { + t.Fatal("can't write") + } + a++ + } + if done != len(physicalEdits) { + t.Fatal("physical edits remaining") + } + w.Flush() + f.Close() +} + +func setOf(keys ...string) map[string]bool { + m := make(map[string]bool, len(keys)) + for _, key := range keys { + m[key] = true + } + return m +} + +var runtimeFeatures = setOf( + "adx", "aes", "avx", "avx2", "bmi1", "bmi2", "erms", "fma", + "pclmulqdq", "popcnt", "rdtscp", "sse3", "sse41", "sse42", "ssse3", +) + +var featureToOpcodes = map[string][]string{ + // Note: we include *q, *l, and plain opcodes here. + // go tool objdump doesn't include a [QL] on popcnt instructions, until CL 351889 + // native objdump doesn't include [QL] on linux. + "popcnt": {"popcntq", "popcntl", "popcnt"}, + "bmi1": { + "andnq", "andnl", "andn", + "blsiq", "blsil", "blsi", + "blsmskq", "blsmskl", "blsmsk", + "blsrq", "blsrl", "blsr", + "tzcntq", "tzcntl", "tzcnt", + }, + "bmi2": { + "sarxq", "sarxl", "sarx", + "shlxq", "shlxl", "shlx", + "shrxq", "shrxl", "shrx", + }, + "sse41": { + "roundsd", + "pinsrq", "pinsrl", "pinsrd", "pinsrb", "pinsr", + "pextrq", "pextrl", "pextrd", "pextrb", "pextr", + "pminsb", "pminsd", "pminuw", "pminud", // Note: ub and sw are ok. + "pmaxsb", "pmaxsd", "pmaxuw", "pmaxud", + "pmovzxbw", "pmovzxbd", "pmovzxbq", "pmovzxwd", "pmovzxwq", "pmovzxdq", + "pmovsxbw", "pmovsxbd", "pmovsxbq", "pmovsxwd", "pmovsxwq", "pmovsxdq", + "pblendvb", + }, + "fma": {"vfmadd231sd"}, + "movbe": {"movbeqq", "movbeq", "movbell", "movbel", "movbe"}, + "lzcnt": {"lzcntq", "lzcntl", "lzcnt"}, +} + +// Test to use POPCNT instruction, if available +func TestPopCnt(t *testing.T) { + for _, tt := range []struct { + x uint64 + want int + }{ + {0b00001111, 4}, + {0b00001110, 3}, + {0b00001100, 2}, + {0b00000000, 0}, + } { + if got := bits.OnesCount64(tt.x); got != tt.want { + t.Errorf("OnesCount64(%#x) = %d, want %d", tt.x, got, tt.want) + } + if got := bits.OnesCount32(uint32(tt.x)); got != tt.want { + t.Errorf("OnesCount32(%#x) = %d, want %d", tt.x, got, tt.want) + } + } +} + +// Test to use ANDN, if available +func TestAndNot(t *testing.T) { + for _, tt := range []struct { + x, y, want uint64 + }{ + {0b00001111, 0b00000011, 0b1100}, + {0b00001111, 0b00001100, 0b0011}, + {0b00000000, 0b00000000, 0b0000}, + } { + if got := tt.x &^ tt.y; got != tt.want { + t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want) + } + if got := uint32(tt.x) &^ uint32(tt.y); got != uint32(tt.want) { + t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want) + } + } +} + +// Test to use BLSI, if available +func TestBLSI(t *testing.T) { + for _, tt := range []struct { + x, want uint64 + }{ + {0b00001111, 0b001}, + {0b00001110, 0b010}, + {0b00001100, 0b100}, + {0b11000110, 0b010}, + {0b00000000, 0b000}, + } { + if got := tt.x & -tt.x; got != tt.want { + t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want) + } + if got := uint32(tt.x) & -uint32(tt.x); got != uint32(tt.want) { + t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want) + } + } +} + +// Test to use BLSMSK, if available +func TestBLSMSK(t *testing.T) { + for _, tt := range []struct { + x, want uint64 + }{ + {0b00001111, 0b001}, + {0b00001110, 0b011}, + {0b00001100, 0b111}, + {0b11000110, 0b011}, + {0b00000000, 1<<64 - 1}, + } { + if got := tt.x ^ (tt.x - 1); got != tt.want { + t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want) + } + if got := uint32(tt.x) ^ (uint32(tt.x) - 1); got != uint32(tt.want) { + t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, uint32(tt.want)) + } + } +} + +// Test to use BLSR, if available +func TestBLSR(t *testing.T) { + for _, tt := range []struct { + x, want uint64 + }{ + {0b00001111, 0b00001110}, + {0b00001110, 0b00001100}, + {0b00001100, 0b00001000}, + {0b11000110, 0b11000100}, + {0b00000000, 0b00000000}, + } { + if got := tt.x & (tt.x - 1); got != tt.want { + t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want) + } + if got := uint32(tt.x) & (uint32(tt.x) - 1); got != uint32(tt.want) { + t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want) + } + } +} + +func TestTrailingZeros(t *testing.T) { + for _, tt := range []struct { + x uint64 + want int + }{ + {0b00001111, 0}, + {0b00001110, 1}, + {0b00001100, 2}, + {0b00001000, 3}, + {0b00000000, 64}, + } { + if got := bits.TrailingZeros64(tt.x); got != tt.want { + t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, tt.want) + } + want := tt.want + if want == 64 { + want = 32 + } + if got := bits.TrailingZeros32(uint32(tt.x)); got != want { + t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, want) + } + } +} + +func TestRound(t *testing.T) { + for _, tt := range []struct { + x, want float64 + }{ + {1.4, 1}, + {1.5, 2}, + {1.6, 2}, + {2.4, 2}, + {2.5, 2}, + {2.6, 3}, + } { + if got := math.RoundToEven(tt.x); got != tt.want { + t.Errorf("RoundToEven(%f) = %f, want %f", tt.x, got, tt.want) + } + } +} + +func TestFMA(t *testing.T) { + for _, tt := range []struct { + x, y, z, want float64 + }{ + {2, 3, 4, 10}, + {3, 4, 5, 17}, + } { + if got := math.FMA(tt.x, tt.y, tt.z); got != tt.want { + t.Errorf("FMA(%f,%f,%f) = %f, want %f", tt.x, tt.y, tt.z, got, tt.want) + } + } +} diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go index d68500280d00b7..23e52bacbf2a3d 100644 --- a/src/cmd/compile/internal/arm/galign.go +++ b/src/cmd/compile/internal/arm/galign.go @@ -18,7 +18,6 @@ func Init(arch *ssagen.ArchInfo) { arch.SoftFloat = buildcfg.GOARM == 5 arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} arch.SSAGenValue = ssaGenValue diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go index 4b083cec46b4a5..a53f51bd1373bb 100644 --- a/src/cmd/compile/internal/arm/ssa.go +++ b/src/cmd/compile/internal/arm/ssa.go @@ -88,15 +88,18 @@ func (v shift) String() string { } // makeshift encodes a register shifted by a constant -func makeshift(reg int16, typ int64, s int64) shift { +func makeshift(v *ssa.Value, reg int16, typ int64, s int64) shift { + if s < 0 || s >= 32 { + v.Fatalf("shift out of range: %d", s) + } return shift(int64(reg&0xf) | typ | (s&31)<<7) } // genshift generates a Prog for r = r0 op (r1 shifted by n) -func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { +func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { p := s.Prog(as) p.From.Type = obj.TYPE_SHIFT - p.From.Offset = int64(makeshift(r1, typ, n)) + p.From.Offset = int64(makeshift(v, r1, typ, n)) p.Reg = r0 if r != 0 { p.To.Type = obj.TYPE_REG @@ -335,7 +338,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() case ssa.OpARMSRRconst: - genshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) + genshift(s, v, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) case ssa.OpARMADDshiftLL, ssa.OpARMADCshiftLL, ssa.OpARMSUBshiftLL, @@ -346,11 +349,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARMORshiftLL, ssa.OpARMXORshiftLL, ssa.OpARMBICshiftLL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) case ssa.OpARMADDSshiftLL, ssa.OpARMSUBSshiftLL, ssa.OpARMRSBSshiftLL: - p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt) p.Scond = arm.C_SBIT case ssa.OpARMADDshiftRL, ssa.OpARMADCshiftRL, @@ -362,11 +365,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARMORshiftRL, ssa.OpARMXORshiftRL, ssa.OpARMBICshiftRL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) case ssa.OpARMADDSshiftRL, ssa.OpARMSUBSshiftRL, ssa.OpARMRSBSshiftRL: - p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt) p.Scond = arm.C_SBIT case ssa.OpARMADDshiftRA, ssa.OpARMADCshiftRA, @@ -378,20 +381,20 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARMORshiftRA, ssa.OpARMXORshiftRA, ssa.OpARMBICshiftRA: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) case ssa.OpARMADDSshiftRA, ssa.OpARMSUBSshiftRA, ssa.OpARMRSBSshiftRA: - p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt) p.Scond = arm.C_SBIT case ssa.OpARMXORshiftRR: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) case ssa.OpARMMVNshiftLL: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) case ssa.OpARMMVNshiftRL: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) case ssa.OpARMMVNshiftRA: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) case ssa.OpARMMVNshiftLLreg: genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL) case ssa.OpARMMVNshiftRLreg: @@ -513,11 +516,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt) case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt) case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt) case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg: genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL) case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg: @@ -583,13 +586,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // this is just shift 0 bits fallthrough case ssa.OpARMMOVWloadshiftLL: - p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) p.From.Reg = v.Args[0].Reg() case ssa.OpARMMOVWloadshiftRL: - p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) p.From.Reg = v.Args[0].Reg() case ssa.OpARMMOVWloadshiftRA: - p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) + p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) p.From.Reg = v.Args[0].Reg() case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx: // this is just shift 0 bits @@ -600,21 +603,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Reg = v.Args[2].Reg() p.To.Type = obj.TYPE_SHIFT p.To.Reg = v.Args[0].Reg() - p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt)) + p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt)) case ssa.OpARMMOVWstoreshiftRL: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[2].Reg() p.To.Type = obj.TYPE_SHIFT p.To.Reg = v.Args[0].Reg() - p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt)) + p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt)) case ssa.OpARMMOVWstoreshiftRA: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[2].Reg() p.To.Type = obj.TYPE_SHIFT p.To.Reg = v.Args[0].Reg() - p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt)) + p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt)) case ssa.OpARMMOVBreg, ssa.OpARMMOVBUreg, ssa.OpARMMOVHreg, @@ -645,7 +648,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } if buildcfg.GOARM >= 6 { // generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7 - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0) return } fallthrough @@ -696,6 +699,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter: s.Call(v) + case ssa.OpARMCALLtail: + s.TailCall(v) case ssa.OpARMCALLudiv: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -849,7 +854,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(arm.AMOVW) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -936,17 +941,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - case ssa.BlockARMEQ, ssa.BlockARMNE, ssa.BlockARMLT, ssa.BlockARMGE, ssa.BlockARMLE, ssa.BlockARMGT, diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go index d3db37e16f43bc..3ebd860de8f887 100644 --- a/src/cmd/compile/internal/arm64/galign.go +++ b/src/cmd/compile/internal/arm64/galign.go @@ -18,9 +18,10 @@ func Init(arch *ssagen.ArchInfo) { arch.PadFrame = padframe arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock + arch.LoadRegResult = loadRegResult + arch.SpillArgReg = spillArgReg } diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go index 89be4964619c16..a681adcb7fe28f 100644 --- a/src/cmd/compile/internal/arm64/ggen.go +++ b/src/cmd/compile/internal/arm64/ggen.go @@ -10,11 +10,8 @@ import ( "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/obj/arm64" - "internal/buildcfg" ) -var darwin = buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" - func padframe(frame int64) int64 { // arm64 requires that the frame size (not counting saved FP&LR) // be 16 bytes aligned. If not, pad it. @@ -32,7 +29,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog for i := int64(0); i < cnt; i += int64(types.PtrSize) { p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i) } - } else if cnt <= int64(128*types.PtrSize) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend + } else if cnt <= int64(128*types.PtrSize) { if cnt%(2*int64(types.PtrSize)) != 0 { p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off) off += int64(types.PtrSize) diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index 0c997bc4b3e82a..8299cb1de4c960 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" + "cmd/compile/internal/objw" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" @@ -78,15 +79,18 @@ func storeByType(t *types.Type) obj.As { } // makeshift encodes a register shifted by a constant, used as an Offset in Prog -func makeshift(reg int16, typ int64, s int64) int64 { +func makeshift(v *ssa.Value, reg int16, typ int64, s int64) int64 { + if s < 0 || s >= 64 { + v.Fatalf("shift out of range: %d", s) + } return int64(reg&31)<<16 | typ | (s&63)<<10 } // genshift generates a Prog for r = r0 op (r1 shifted by n) -func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { +func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { p := s.Prog(as) p.From.Type = obj.TYPE_SHIFT - p.From.Offset = makeshift(r1, typ, n) + p.From.Offset = makeshift(v, r1, typ, n) p.Reg = r0 if r != 0 { p.To.Type = obj.TYPE_REG @@ -95,21 +99,22 @@ func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) * return p } -// generate the memory operand for the indexed load/store instructions -func genIndexedOperand(v *ssa.Value) obj.Addr { +// generate the memory operand for the indexed load/store instructions. +// base and idx are registers. +func genIndexedOperand(op ssa.Op, base, idx int16) obj.Addr { // Reg: base register, Index: (shifted) index register - mop := obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[0].Reg()} - switch v.Op { + mop := obj.Addr{Type: obj.TYPE_MEM, Reg: base} + switch op { case ssa.OpARM64MOVDloadidx8, ssa.OpARM64MOVDstoreidx8, ssa.OpARM64MOVDstorezeroidx8, ssa.OpARM64FMOVDloadidx8, ssa.OpARM64FMOVDstoreidx8: - mop.Index = arm64.REG_LSL | 3<<5 | v.Args[1].Reg()&31 + mop.Index = arm64.REG_LSL | 3<<5 | idx&31 case ssa.OpARM64MOVWloadidx4, ssa.OpARM64MOVWUloadidx4, ssa.OpARM64MOVWstoreidx4, ssa.OpARM64MOVWstorezeroidx4, ssa.OpARM64FMOVSloadidx4, ssa.OpARM64FMOVSstoreidx4: - mop.Index = arm64.REG_LSL | 2<<5 | v.Args[1].Reg()&31 + mop.Index = arm64.REG_LSL | 2<<5 | idx&31 case ssa.OpARM64MOVHloadidx2, ssa.OpARM64MOVHUloadidx2, ssa.OpARM64MOVHstoreidx2, ssa.OpARM64MOVHstorezeroidx2: - mop.Index = arm64.REG_LSL | 1<<5 | v.Args[1].Reg()&31 + mop.Index = arm64.REG_LSL | 1<<5 | idx&31 default: // not shifted - mop.Index = v.Args[1].Reg() + mop.Index = idx } return mop } @@ -161,6 +166,18 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() ssagen.AddrAuto(&p.To, v) + case ssa.OpArgIntReg, ssa.OpArgFloatReg: + // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill + // The loop only runs once. + for _, a := range v.Block.Func.RegArgs { + // Pass the spill/unspill information along to the assembler, offset by size of + // the saved LR slot. + addr := ssagen.SpillSlotAddr(a, arm64.REGSP, base.Ctxt.Arch.FixedFrameSize) + s.FuncInfo().AddSpill( + obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)}) + } + v.Block.Func.RegArgs = nil + ssagen.CheckArgReg(v) case ssa.OpARM64ADD, ssa.OpARM64SUB, ssa.OpARM64AND, @@ -297,11 +314,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64MVNshiftLL, ssa.OpARM64NEGshiftLL: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) case ssa.OpARM64MVNshiftRL, ssa.OpARM64NEGshiftRL: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA: - genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64MVNshiftRO: + genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64ADDshiftLL, ssa.OpARM64SUBshiftLL, ssa.OpARM64ANDshiftLL, @@ -310,7 +329,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64EONshiftLL, ssa.OpARM64ORNshiftLL, ssa.OpARM64BICshiftLL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) case ssa.OpARM64ADDshiftRL, ssa.OpARM64SUBshiftRL, ssa.OpARM64ANDshiftRL, @@ -319,7 +338,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64EONshiftRL, ssa.OpARM64ORNshiftRL, ssa.OpARM64BICshiftRL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) case ssa.OpARM64ADDshiftRA, ssa.OpARM64SUBshiftRA, ssa.OpARM64ANDshiftRA, @@ -328,7 +347,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64EONshiftRA, ssa.OpARM64ORNshiftRA, ssa.OpARM64BICshiftRA: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64ANDshiftRO, + ssa.OpARM64ORshiftRO, + ssa.OpARM64XORshiftRO, + ssa.OpARM64EONshiftRO, + ssa.OpARM64ORNshiftRO, + ssa.OpARM64BICshiftRO: + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64MOVDconst: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST @@ -371,11 +397,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Offset = v.AuxInt p.Reg = v.Args[0].Reg() case ssa.OpARM64CMPshiftLL, ssa.OpARM64CMNshiftLL, ssa.OpARM64TSTshiftLL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt) case ssa.OpARM64CMPshiftRL, ssa.OpARM64CMNshiftRL, ssa.OpARM64TSTshiftRL: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt) case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA: - genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt) + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64TSTshiftRO: + genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64MOVDaddr: p := s.Prog(arm64.AMOVD) p.From.Type = obj.TYPE_ADDR @@ -421,6 +449,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssagen.AddAux(&p.From, v) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpARM64LDP: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + ssagen.AddAux(&p.From, v) + p.To.Type = obj.TYPE_REGREG + p.To.Reg = v.Reg0() + p.To.Offset = int64(v.Reg1()) case ssa.OpARM64MOVBloadidx, ssa.OpARM64MOVBUloadidx, ssa.OpARM64MOVHloadidx, @@ -438,7 +474,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64FMOVDloadidx8, ssa.OpARM64FMOVSloadidx4: p := s.Prog(v.Op.Asm()) - p.From = genIndexedOperand(v) + p.From = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg()) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64LDAR, @@ -477,7 +513,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64MOVDstoreidx8, ssa.OpARM64FMOVDstoreidx8: p := s.Prog(v.Op.Asm()) - p.To = genIndexedOperand(v) + p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[2].Reg() case ssa.OpARM64STP: @@ -506,7 +542,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64MOVWstorezeroidx4, ssa.OpARM64MOVDstorezeroidx8: p := s.Prog(v.Op.Asm()) - p.To = genIndexedOperand(v) + p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg()) p.From.Type = obj.TYPE_REG p.From.Reg = arm64.REGZERO case ssa.OpARM64MOVQstorezero: @@ -537,21 +573,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() - case ssa.OpARM64LoweredMuluhilo: - r0 := v.Args[0].Reg() - r1 := v.Args[1].Reg() - p := s.Prog(arm64.AUMULH) - p.From.Type = obj.TYPE_REG - p.From.Reg = r1 - p.Reg = r0 - p.To.Type = obj.TYPE_REG - p.To.Reg = v.Reg0() - p1 := s.Prog(arm64.AMUL) - p1.From.Type = obj.TYPE_REG - p1.From.Reg = r1 - p1.Reg = r0 - p1.To.Type = obj.TYPE_REG - p1.To.Reg = v.Reg1() case ssa.OpARM64LoweredAtomicExchange64, ssa.OpARM64LoweredAtomicExchange32: // LDAXR (Rarg0), Rout @@ -701,8 +722,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p4.To.Type = obj.TYPE_BRANCH p4.To.SetTarget(p) p5 := s.Prog(arm64.ACSET) - p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p5.From.Reg = arm64.COND_EQ + p5.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + p5.From.Offset = int64(arm64.SPOP_EQ) p5.To.Type = obj.TYPE_REG p5.To.Reg = out p2.To.SetTarget(p5) @@ -751,8 +772,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // CSET EQ, Rout p3 := s.Prog(arm64.ACSET) - p3.From.Type = obj.TYPE_REG - p3.From.Reg = arm64.COND_EQ + p3.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + p3.From.Offset = int64(arm64.SPOP_EQ) p3.To.Type = obj.TYPE_REG p3.To.Reg = out @@ -951,24 +972,27 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { r1 = v.Args[1].Reg() } p := s.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.Reg = v.Args[0].Reg() p.SetFrom3Reg(r1) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64CSINC, ssa.OpARM64CSINV, ssa.OpARM64CSNEG: p := s.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.Reg = v.Args[0].Reg() p.SetFrom3Reg(v.Args[1].Reg()) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64CSETM: p := s.Prog(arm64.ACSETM) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64DUFFZERO: @@ -1005,25 +1029,27 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Sym = ir.Syms.Duffcopy p.To.Offset = v.AuxInt case ssa.OpARM64LoweredMove: - // MOVD.P 8(R16), Rtmp - // MOVD.P Rtmp, 8(R17) + // LDP.P 16(R16), (R25, Rtmp) + // STP.P (R25, Rtmp), 16(R17) // CMP Rarg2, R16 // BLE -3(PC) // arg2 is the address of the last element of src - p := s.Prog(arm64.AMOVD) + p := s.Prog(arm64.ALDP) p.Scond = arm64.C_XPOST p.From.Type = obj.TYPE_MEM p.From.Reg = arm64.REG_R16 - p.From.Offset = 8 - p.To.Type = obj.TYPE_REG - p.To.Reg = arm64.REGTMP - p2 := s.Prog(arm64.AMOVD) + p.From.Offset = 16 + p.To.Type = obj.TYPE_REGREG + p.To.Reg = arm64.REG_R25 + p.To.Offset = int64(arm64.REGTMP) + p2 := s.Prog(arm64.ASTP) p2.Scond = arm64.C_XPOST - p2.From.Type = obj.TYPE_REG - p2.From.Reg = arm64.REGTMP + p2.From.Type = obj.TYPE_REGREG + p2.From.Reg = arm64.REG_R25 + p2.From.Offset = int64(arm64.REGTMP) p2.To.Type = obj.TYPE_MEM p2.To.Reg = arm64.REG_R17 - p2.To.Offset = 8 + p2.To.Offset = 16 p3 := s.Prog(arm64.ACMP) p3.From.Type = obj.TYPE_REG p3.From.Reg = v.Args[2].Reg() @@ -1033,6 +1059,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p4.To.SetTarget(p) case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter: s.Call(v) + case ssa.OpARM64CALLtail: + s.TailCall(v) case ssa.OpARM64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -1078,10 +1106,17 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64NotGreaterEqualF: // generate boolean values using CSET p := s.Prog(arm64.ACSET) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[v.Op] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[v.Op] + p.From.Offset = int64(condCode) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpARM64PRFM: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_CONST + p.To.Offset = v.AuxInt case ssa.OpARM64LoweredGetClosurePtr: // Closure pointer is R26 (arm64.REGCTXT). ssagen.CheckLoweredGetClosurePtr(v) @@ -1089,7 +1124,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(arm64.AMOVD) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -1097,38 +1132,68 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p := s.Prog(obj.AGETCALLERPC) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpARM64DMB: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt case ssa.OpARM64FlagConstant: v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString()) case ssa.OpARM64InvertFlags: v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) - case ssa.OpClobber, ssa.OpClobberReg: - // TODO: implement for clobberdead experiment. Nop is ok for now. + case ssa.OpClobber: + // MOVW $0xdeaddead, REGTMP + // MOVW REGTMP, (slot) + // MOVW REGTMP, 4(slot) + p := s.Prog(arm64.AMOVW) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 0xdeaddead + p.To.Type = obj.TYPE_REG + p.To.Reg = arm64.REGTMP + p = s.Prog(arm64.AMOVW) + p.From.Type = obj.TYPE_REG + p.From.Reg = arm64.REGTMP + p.To.Type = obj.TYPE_MEM + p.To.Reg = arm64.REGSP + ssagen.AddAux(&p.To, v) + p = s.Prog(arm64.AMOVW) + p.From.Type = obj.TYPE_REG + p.From.Reg = arm64.REGTMP + p.To.Type = obj.TYPE_MEM + p.To.Reg = arm64.REGSP + ssagen.AddAux2(&p.To, v, v.AuxInt+4) + case ssa.OpClobberReg: + x := uint64(0xdeaddeaddeaddead) + p := s.Prog(arm64.AMOVD) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(x) + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() default: v.Fatalf("genValue not implemented: %s", v.LongString()) } } -var condBits = map[ssa.Op]int16{ - ssa.OpARM64Equal: arm64.COND_EQ, - ssa.OpARM64NotEqual: arm64.COND_NE, - ssa.OpARM64LessThan: arm64.COND_LT, - ssa.OpARM64LessThanU: arm64.COND_LO, - ssa.OpARM64LessEqual: arm64.COND_LE, - ssa.OpARM64LessEqualU: arm64.COND_LS, - ssa.OpARM64GreaterThan: arm64.COND_GT, - ssa.OpARM64GreaterThanU: arm64.COND_HI, - ssa.OpARM64GreaterEqual: arm64.COND_GE, - ssa.OpARM64GreaterEqualU: arm64.COND_HS, - ssa.OpARM64LessThanF: arm64.COND_MI, // Less than - ssa.OpARM64LessEqualF: arm64.COND_LS, // Less than or equal to - ssa.OpARM64GreaterThanF: arm64.COND_GT, // Greater than - ssa.OpARM64GreaterEqualF: arm64.COND_GE, // Greater than or equal to +var condBits = map[ssa.Op]arm64.SpecialOperand{ + ssa.OpARM64Equal: arm64.SPOP_EQ, + ssa.OpARM64NotEqual: arm64.SPOP_NE, + ssa.OpARM64LessThan: arm64.SPOP_LT, + ssa.OpARM64LessThanU: arm64.SPOP_LO, + ssa.OpARM64LessEqual: arm64.SPOP_LE, + ssa.OpARM64LessEqualU: arm64.SPOP_LS, + ssa.OpARM64GreaterThan: arm64.SPOP_GT, + ssa.OpARM64GreaterThanU: arm64.SPOP_HI, + ssa.OpARM64GreaterEqual: arm64.SPOP_GE, + ssa.OpARM64GreaterEqualU: arm64.SPOP_HS, + ssa.OpARM64LessThanF: arm64.SPOP_MI, // Less than + ssa.OpARM64LessEqualF: arm64.SPOP_LS, // Less than or equal to + ssa.OpARM64GreaterThanF: arm64.SPOP_GT, // Greater than + ssa.OpARM64GreaterEqualF: arm64.SPOP_GE, // Greater than or equal to // The following condition codes have unordered to handle comparisons related to NaN. - ssa.OpARM64NotLessThanF: arm64.COND_PL, // Greater than, equal to, or unordered - ssa.OpARM64NotLessEqualF: arm64.COND_HI, // Greater than or unordered - ssa.OpARM64NotGreaterThanF: arm64.COND_LE, // Less than, equal to or unordered - ssa.OpARM64NotGreaterEqualF: arm64.COND_LT, // Less than or unordered + ssa.OpARM64NotLessThanF: arm64.SPOP_PL, // Greater than, equal to, or unordered + ssa.OpARM64NotLessEqualF: arm64.SPOP_HI, // Greater than or unordered + ssa.OpARM64NotGreaterThanF: arm64.SPOP_LE, // Less than, equal to or unordered + ssa.OpARM64NotGreaterEqualF: arm64.SPOP_LT, // Less than or unordered } var blockJump = map[ssa.BlockKind]struct { @@ -1196,17 +1261,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - case ssa.BlockARM64EQ, ssa.BlockARM64NE, ssa.BlockARM64LT, ssa.BlockARM64GE, ssa.BlockARM64LE, ssa.BlockARM64GT, @@ -1262,7 +1321,40 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.CombJump(b, next, &leJumps) case ssa.BlockARM64GTnoov: s.CombJump(b, next, >Jumps) + + case ssa.BlockARM64JUMPTABLE: + // MOVD (TABLE)(IDX<<3), Rtmp + // JMP (Rtmp) + p := s.Prog(arm64.AMOVD) + p.From = genIndexedOperand(ssa.OpARM64MOVDloadidx8, b.Controls[1].Reg(), b.Controls[0].Reg()) + p.To.Type = obj.TYPE_REG + p.To.Reg = arm64.REGTMP + p = s.Prog(obj.AJMP) + p.To.Type = obj.TYPE_MEM + p.To.Reg = arm64.REGTMP + // Save jump tables for later resolution of the target blocks. + s.JumpTables = append(s.JumpTables, b) + default: b.Fatalf("branch not implemented: %s", b.LongString()) } } + +func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p := s.Prog(loadByType(t)) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_AUTO + p.From.Sym = n.Linksym() + p.From.Offset = n.FrameOffset() + off + p.To.Type = obj.TYPE_REG + p.To.Reg = reg + return p +} + +func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) + p.To.Name = obj.NAME_PARAM + p.To.Sym = n.Linksym() + p.Pos = p.Pos.WithNotStmt() + return p +} diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go index 4c2516f60e364b..39ce8e66f7313e 100644 --- a/src/cmd/compile/internal/base/base.go +++ b/src/cmd/compile/internal/base/base.go @@ -62,11 +62,13 @@ func Compiling(pkgs []string) bool { // at best instrumentation would cause infinite recursion. var NoInstrumentPkgs = []string{ "runtime/internal/atomic", - "runtime/internal/sys", "runtime/internal/math", + "runtime/internal/sys", + "runtime/internal/syscall", "runtime", "runtime/race", "runtime/msan", + "runtime/asan", "internal/cpu", } diff --git a/src/cmd/compile/internal/base/bootstrap_false.go b/src/cmd/compile/internal/base/bootstrap_false.go new file mode 100644 index 00000000000000..c77fcd730812d8 --- /dev/null +++ b/src/cmd/compile/internal/base/bootstrap_false.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !compiler_bootstrap +// +build !compiler_bootstrap + +package base + +// CompilerBootstrap reports whether the current compiler binary was +// built with -tags=compiler_bootstrap. +const CompilerBootstrap = false diff --git a/src/cmd/compile/internal/base/bootstrap_true.go b/src/cmd/compile/internal/base/bootstrap_true.go new file mode 100644 index 00000000000000..1eb58b2f9dc1ce --- /dev/null +++ b/src/cmd/compile/internal/base/bootstrap_true.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build compiler_bootstrap +// +build compiler_bootstrap + +package base + +// CompilerBootstrap reports whether the current compiler binary was +// built with -tags=compiler_bootstrap. +const CompilerBootstrap = true diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go index 71712ab1a56fd2..32a45d7a9cd2fc 100644 --- a/src/cmd/compile/internal/base/debug.go +++ b/src/cmd/compile/internal/base/debug.go @@ -6,15 +6,6 @@ package base -import ( - "fmt" - "log" - "os" - "reflect" - "strconv" - "strings" -) - // Debug holds the parsed debugging configuration values. var Debug DebugFlags @@ -26,7 +17,7 @@ var Debug DebugFlags // Each setting is name=value; for ints, name is short for name=1. type DebugFlags struct { Append int `help:"print information about append compilation"` - Checkptr int `help:"instrument unsafe pointer conversions"` + Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation"` Closure int `help:"print information about closure compilation"` DclStack int `help:"run internal dclstack check"` Defer int `help:"print information about defer compilation"` @@ -40,150 +31,25 @@ type DebugFlags struct { LocationLists int `help:"print information about DWARF location list creation"` Nil int `help:"print information about nil checks"` NoOpenDefer int `help:"disable open-coded defers"` - PCTab string `help:"print named pc-value table"` + NoRefName int `help:"do not include referenced symbol names in object file"` + PCTab string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"` Panic int `help:"show all compiler panics"` + Reshape int `help:"print information about expression reshaping"` + Shapify int `help:"print information about shaping recursive types"` Slice int `help:"print information about slice compilation"` SoftFloat int `help:"force compiler to emit soft-float code"` + SyncFrames int `help:"how many writer stack frames to include at sync points in unified export data"` TypeAssert int `help:"print information about type assertion inlining"` TypecheckInl int `help:"eager typechecking of inline function bodies"` + Unified int `help:"enable unified IR construction"` WB int `help:"print information about write barriers"` ABIWrap int `help:"print information about ABI wrapper generation"` + MayMoreStack string `help:"call named function before all stack growth checks"` - any bool // set when any of the values have been set -} - -// Any reports whether any of the debug flags have been set. -func (d *DebugFlags) Any() bool { return d.any } - -type debugField struct { - name string - help string - val interface{} // *int or *string -} - -var debugTab []debugField - -func init() { - v := reflect.ValueOf(&Debug).Elem() - t := v.Type() - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - if f.Name == "any" { - continue - } - name := strings.ToLower(f.Name) - help := f.Tag.Get("help") - if help == "" { - panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name)) - } - ptr := v.Field(i).Addr().Interface() - switch ptr.(type) { - default: - panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type)) - case *int, *string: - // ok - } - debugTab = append(debugTab, debugField{name, help, ptr}) - } + Any bool // set when any of the debug flags have been set } // DebugSSA is called to set a -d ssa/... option. // If nil, those options are reported as invalid options. // If DebugSSA returns a non-empty string, that text is reported as a compiler error. var DebugSSA func(phase, flag string, val int, valString string) string - -// parseDebug parses the -d debug string argument. -func parseDebug(debugstr string) { - // parse -d argument - if debugstr == "" { - return - } - Debug.any = true -Split: - for _, name := range strings.Split(debugstr, ",") { - if name == "" { - continue - } - // display help about the -d option itself and quit - if name == "help" { - fmt.Print(debugHelpHeader) - maxLen := len("ssa/help") - for _, t := range debugTab { - if len(t.name) > maxLen { - maxLen = len(t.name) - } - } - for _, t := range debugTab { - fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help) - } - // ssa options have their own help - fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") - fmt.Print(debugHelpFooter) - os.Exit(0) - } - val, valstring, haveInt := 1, "", true - if i := strings.IndexAny(name, "=:"); i >= 0 { - var err error - name, valstring = name[:i], name[i+1:] - val, err = strconv.Atoi(valstring) - if err != nil { - val, haveInt = 1, false - } - } - for _, t := range debugTab { - if t.name != name { - continue - } - switch vp := t.val.(type) { - case nil: - // Ignore - case *string: - *vp = valstring - case *int: - if !haveInt { - log.Fatalf("invalid debug value %v", name) - } - *vp = val - default: - panic("bad debugtab type") - } - continue Split - } - // special case for ssa for now - if DebugSSA != nil && strings.HasPrefix(name, "ssa/") { - // expect form ssa/phase/flag - // e.g. -d=ssa/generic_cse/time - // _ in phase name also matches space - phase := name[4:] - flag := "debug" // default flag is debug - if i := strings.Index(phase, "/"); i >= 0 { - flag = phase[i+1:] - phase = phase[:i] - } - err := DebugSSA(phase, flag, val, valstring) - if err != "" { - log.Fatalf(err) - } - continue Split - } - log.Fatalf("unknown debug key -d %s\n", name) - } -} - -const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=] - - is one of: - -` - -const debugHelpFooter = ` - is key-specific. - -Key "checkptr" supports values: - "0": instrumentation disabled - "1": conversions involving unsafe.Pointer are instrumented - "2": conversions to unsafe.Pointer force heap allocation - -Key "pctab" supports values: - "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata" -` diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go index 42c0c1b94b559c..7420d6d2f91f5d 100644 --- a/src/cmd/compile/internal/base/flag.go +++ b/src/cmd/compile/internal/base/flag.go @@ -16,6 +16,7 @@ import ( "runtime" "strings" + "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/sys" ) @@ -55,28 +56,27 @@ type CmdFlags struct { C CountFlag "help:\"disable printing of columns in error messages\"" D string "help:\"set relative `path` for local imports\"" E CountFlag "help:\"debug symbol export\"" - G CountFlag "help:\"accept generic code\"" I func(string) "help:\"add `directory` to import search path\"" K CountFlag "help:\"debug missing line numbers\"" - L CountFlag "help:\"show full file names in error messages\"" + L CountFlag "help:\"also show actual source file names in error messages for positions affected by //line directives\"" N CountFlag "help:\"disable optimizations\"" S CountFlag "help:\"print assembly listing\"" // V is added by objabi.AddVersionFlag W CountFlag "help:\"debug parse tree after type checking\"" - LowerC int "help:\"concurrency during compilation (1 means no concurrency)\"" - LowerD func(string) "help:\"enable debugging settings; try -d help\"" - LowerE CountFlag "help:\"no limit on number of errors reported\"" - LowerH CountFlag "help:\"halt on error\"" - LowerJ CountFlag "help:\"debug runtime-initialized variables\"" - LowerL CountFlag "help:\"disable inlining\"" - LowerM CountFlag "help:\"print optimization decisions\"" - LowerO string "help:\"write output to `file`\"" - LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below - LowerR CountFlag "help:\"debug generated wrappers\"" - LowerT bool "help:\"enable tracing for debugging the compiler\"" - LowerW CountFlag "help:\"debug type checking\"" - LowerV *bool "help:\"increase debug verbosity\"" + LowerC int "help:\"concurrency during compilation (1 means no concurrency)\"" + LowerD flag.Value "help:\"enable debugging settings; try -d help\"" + LowerE CountFlag "help:\"no limit on number of errors reported\"" + LowerH CountFlag "help:\"halt on error\"" + LowerJ CountFlag "help:\"debug runtime-initialized variables\"" + LowerL CountFlag "help:\"disable inlining\"" + LowerM CountFlag "help:\"print optimization decisions\"" + LowerO string "help:\"write output to `file`\"" + LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below + LowerR CountFlag "help:\"debug generated wrappers\"" + LowerT bool "help:\"enable tracing for debugging the compiler\"" + LowerW CountFlag "help:\"debug type checking\"" + LowerV *bool "help:\"increase debug verbosity\"" // Special characters Percent int "flag:\"%\" help:\"debug non-static initializers\"" @@ -84,6 +84,7 @@ type CmdFlags struct { // Longer names AsmHdr string "help:\"write assembly header to `file`\"" + ASan bool "help:\"build code compatible with C/C++ address sanitizer\"" Bench string "help:\"append benchmark times to `file`\"" BlockProfile string "help:\"write block profile to `file`\"" BuildID string "help:\"record `id` as the build id in the export metadata\"" @@ -99,7 +100,6 @@ type CmdFlags struct { GenDwarfInl int "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals GoVersion string "help:\"required version of the runtime\"" ImportCfg func(string) "help:\"read import configuration from `file`\"" - ImportMap func(string) "help:\"add `definition` of the form source=actual to import map\"" InstallSuffix string "help:\"set pkg directory `suffix`\"" JSON string "help:\"version,file for JSON compiler/optimizer detail output\"" Lang string "help:\"Go language version source code expects\"" @@ -108,7 +108,7 @@ type CmdFlags struct { Live CountFlag "help:\"debug liveness analysis\"" MSan bool "help:\"build code compatible with C/C++ memory sanitizer\"" MemProfile string "help:\"write memory profile to `file`\"" - MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\"" + MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\"" MutexProfile string "help:\"write mutex profile to `file`\"" NoLocalImports bool "help:\"reject local (relative) imports\"" Pack bool "help:\"write to file.a instead of file.o\"" @@ -121,6 +121,9 @@ type CmdFlags struct { TraceProfile string "help:\"write an execution trace to `file`\"" TrimPath string "help:\"remove `prefix` from recorded source file paths\"" WB bool "help:\"enable write barrier\"" // TODO: remove + ProfileUse string "help:\"read profile from `file`\"" + InlineHotThreshold string "help:\"threshold percentage for determining hot methods and callsites for inlining\"" + InlineHotBudget int "help:\"inline budget for hot methods\"" // Configuration derived from flags; not a flag itself. Cfg struct { @@ -129,7 +132,7 @@ type CmdFlags struct { Files map[string]string } ImportDirs []string // appended to by -I - ImportMap map[string]string // set by -importmap OR -importcfg + ImportMap map[string]string // set by -importcfg PackageFile map[string]string // set by -importcfg; nil means not in use SpectreIndex bool // set by -spectre=index or -spectre=all // Whether we are adding any sort of code instrumentation, such as @@ -143,7 +146,7 @@ func ParseFlags() { Flag.I = addImportDir Flag.LowerC = 1 - Flag.LowerD = parseDebug + Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA) Flag.LowerP = &Ctxt.Pkgpath Flag.LowerV = &Ctxt.Debugvlog @@ -155,11 +158,15 @@ func ParseFlags() { Flag.EmbedCfg = readEmbedCfg Flag.GenDwarfInl = 2 Flag.ImportCfg = readImportCfg - Flag.ImportMap = addImportMap Flag.LinkShared = &Ctxt.Flag_linkshared Flag.Shared = &Ctxt.Flag_shared Flag.WB = true + Debug.InlFuncsWithClosures = 1 + if buildcfg.Experiment.Unified { + Debug.Unified = 1 + } + Debug.SyncFrames = -1 // disable sync markers by default Debug.Checkptr = -1 // so we can tell whether it is set explicitly @@ -172,6 +179,9 @@ func ParseFlags() { if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) { log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH) } + if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) { + log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH) + } if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) { log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH) } @@ -183,6 +193,8 @@ func ParseFlags() { Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared Ctxt.Flag_optimize = Flag.N == 0 Ctxt.Debugasm = int(Flag.S) + Ctxt.Flag_maymorestack = Debug.MayMoreStack + Ctxt.Flag_noRefName = Debug.NoRefName != 0 if flag.NArg() < 1 { usage() @@ -193,6 +205,10 @@ func ParseFlags() { Exit(2) } + if *Flag.LowerP == "" { + *Flag.LowerP = obj.UnlinkablePkg + } + if Flag.LowerO == "" { p := flag.Arg(0) if i := strings.LastIndex(p, "/"); i >= 0 { @@ -212,12 +228,16 @@ func ParseFlags() { } Flag.LowerO = p + suffix } - - if Flag.Race && Flag.MSan { + switch { + case Flag.Race && Flag.MSan: log.Fatal("cannot use both -race and -msan") + case Flag.Race && Flag.ASan: + log.Fatal("cannot use both -race and -asan") + case Flag.MSan && Flag.ASan: + log.Fatal("cannot use both -msan and -asan") } - if Flag.Race || Flag.MSan { - // -race and -msan imply -d=checkptr for now. + if Flag.Race || Flag.MSan || Flag.ASan { + // -race, -msan and -asan imply -d=checkptr for now. if Debug.Checkptr == -1 { // if not set explicitly Debug.Checkptr = 1 } @@ -317,6 +337,12 @@ func registerFlags() { case funcType: f := v.Field(i).Interface().(func(string)) objabi.Flagfn1(name, help, f) + default: + if val, ok := v.Field(i).Interface().(flag.Value); ok { + flag.Var(val, name, help) + } else { + panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type)) + } } } } @@ -344,7 +370,7 @@ func concurrentBackendAllowed() bool { // while writing the object file, and that is non-concurrent. // Adding Debug_vlog, however, causes Debug.S to also print // while flushing the plist, which happens concurrently. - if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 { + if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 { return false } // TODO: Test and delete this condition. @@ -352,7 +378,7 @@ func concurrentBackendAllowed() bool { return false } // TODO: fix races and enable the following flags - if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race { + if Ctxt.Flag_dynlink || Flag.Race { return false } return true @@ -364,21 +390,6 @@ func addImportDir(dir string) { } } -func addImportMap(s string) { - if Flag.Cfg.ImportMap == nil { - Flag.Cfg.ImportMap = make(map[string]string) - } - if strings.Count(s, "=") != 1 { - log.Fatal("-importmap argument must be of the form source=actual") - } - i := strings.Index(s, "=") - source, actual := s[:i], s[i+1:] - if source == "" || actual == "" { - log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty") - } - Flag.Cfg.ImportMap[source] = actual -} - func readImportCfg(file string) { if Flag.Cfg.ImportMap == nil { Flag.Cfg.ImportMap = make(map[string]string) diff --git a/src/cmd/compile/internal/base/hashdebug.go b/src/cmd/compile/internal/base/hashdebug.go new file mode 100644 index 00000000000000..0a8e88f26ca118 --- /dev/null +++ b/src/cmd/compile/internal/base/hashdebug.go @@ -0,0 +1,153 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package base + +import ( + "cmd/internal/notsha256" + "fmt" + "io" + "os" + "strings" + "sync" +) + +const GOSSAHASH = "GOSSAHASH" + +type writeSyncer interface { + io.Writer + Sync() error +} + +type HashDebug struct { + mu sync.Mutex + // what file (if any) receives the yes/no logging? + // default is os.Stdout + logfile writeSyncer +} + +var hd HashDebug + +// DebugHashMatch reports whether environment variable GOSSAHASH +// +// 1. is empty (this is a special more-quickly implemented case of 3) +// +// 2. is "y" or "Y" +// +// 3. is a suffix of the sha1 hash of name +// +// 4. OR +// if evname(i) is a suffix of the sha1 hash of name +// where evname(i)=fmt.Sprintf("GOSSAHASH%d", i), +// for 0<=i +// +// for example: GOMAXPROCS=1 gossahash -- ./all.bash +// +// 6. gossahash should return a single function whose miscompilation +// causes the problem, and you can focus on that. +func DebugHashMatch(pkgAndName string) bool { + return hd.DebugHashMatch(pkgAndName) +} + +func (d *HashDebug) DebugHashMatch(pkgAndName string) bool { + evname := GOSSAHASH + evhash := os.Getenv(evname) + hstr := "" + + switch evhash { + case "": + return true // default behavior with no EV is "on" + case "n", "N": + return false + } + + // Check the hash of the name against a partial input hash. + // We use this feature to do a binary search to + // find a function that is incorrectly compiled. + for _, b := range notsha256.Sum256([]byte(pkgAndName)) { + hstr += fmt.Sprintf("%08b", b) + } + + if evhash == "y" || evhash == "Y" || strings.HasSuffix(hstr, evhash) { + d.logDebugHashMatch(evname, pkgAndName, hstr) + return true + } + + // Iteratively try additional hashes to allow tests for multi-point + // failure. + for i := 0; true; i++ { + ev := fmt.Sprintf("%s%d", evname, i) + evv := os.Getenv(ev) + if evv == "" { + break + } + if strings.HasSuffix(hstr, evv) { + d.logDebugHashMatch(ev, pkgAndName, hstr) + return true + } + } + return false +} + +func (d *HashDebug) logDebugHashMatch(evname, name, hstr string) { + d.mu.Lock() + defer d.mu.Unlock() + file := d.logfile + if file == nil { + if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" { + var err error + file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + Fatalf("could not open hash-testing logfile %s", tmpfile) + return + } + } + if file == nil { + file = os.Stdout + } + d.logfile = file + } + if len(hstr) > 24 { + hstr = hstr[len(hstr)-24:] + } + // External tools depend on this string + fmt.Fprintf(file, "%s triggered %s %s\n", evname, name, hstr) + file.Sync() +} diff --git a/src/cmd/compile/internal/base/link.go b/src/cmd/compile/internal/base/link.go index 49fe4352b2f657..d8aa5a7dccd209 100644 --- a/src/cmd/compile/internal/base/link.go +++ b/src/cmd/compile/internal/base/link.go @@ -8,6 +8,19 @@ import ( "cmd/internal/obj" ) +// ReservedImports are import paths used internally for generated +// symbols by the compiler. +// +// The linker uses the magic symbol prefixes "go:" and "type:". +// Avoid potential confusion between import paths and symbols +// by rejecting these reserved imports for now. Also, people +// "can do weird things in GOPATH and we'd prefer they didn't +// do _that_ weird thing" (per rsc). See also #4257. +var ReservedImports = map[string]bool{ + "go": true, + "type": true, +} + var Ctxt *obj.Link // TODO(mdempsky): These should probably be obj.Link methods. @@ -20,7 +33,11 @@ func PkgLinksym(prefix, name string, abi obj.ABI) *obj.LSym { // TODO(mdempsky): Cleanup callers and Fatalf instead. return linksym(prefix, "_", abi) } - return linksym(prefix, prefix+"."+name, abi) + sep := "." + if ReservedImports[prefix] { + sep = ":" + } + return linksym(prefix, prefix+sep+name, abi) } // Linkname returns the linker symbol for the given name as it might diff --git a/src/cmd/compile/internal/base/mapfile_mmap.go b/src/cmd/compile/internal/base/mapfile_mmap.go new file mode 100644 index 00000000000000..3a5f4cfe21d889 --- /dev/null +++ b/src/cmd/compile/internal/base/mapfile_mmap.go @@ -0,0 +1,46 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd + +package base + +import ( + "internal/unsafeheader" + "os" + "runtime" + "syscall" + "unsafe" +) + +// TODO(mdempsky): Is there a higher-level abstraction that still +// works well for iimport? + +// mapFile returns length bytes from the file starting at the +// specified offset as a string. +func MapFile(f *os.File, offset, length int64) (string, error) { + // POSIX mmap: "The implementation may require that off is a + // multiple of the page size." + x := offset & int64(os.Getpagesize()-1) + offset -= x + length += x + + buf, err := syscall.Mmap(int(f.Fd()), offset, int(length), syscall.PROT_READ, syscall.MAP_SHARED) + runtime.KeepAlive(f) + if err != nil { + return "", err + } + + buf = buf[x:] + pSlice := (*unsafeheader.Slice)(unsafe.Pointer(&buf)) + + var res string + pString := (*unsafeheader.String)(unsafe.Pointer(&res)) + + pString.Data = pSlice.Data + pString.Len = pSlice.Len + + return res, nil +} diff --git a/src/cmd/compile/internal/typecheck/mapfile_read.go b/src/cmd/compile/internal/base/mapfile_read.go similarity index 85% rename from src/cmd/compile/internal/typecheck/mapfile_read.go rename to src/cmd/compile/internal/base/mapfile_read.go index 9637ab97abe458..01796a9bab7c99 100644 --- a/src/cmd/compile/internal/typecheck/mapfile_read.go +++ b/src/cmd/compile/internal/base/mapfile_read.go @@ -5,14 +5,14 @@ //go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd // +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd -package typecheck +package base import ( "io" "os" ) -func mapFile(f *os.File, offset, length int64) (string, error) { +func MapFile(f *os.File, offset, length int64) (string, error) { buf := make([]byte, length) _, err := io.ReadFull(io.NewSectionReader(f, offset, length), buf) if err != nil { diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go index b095fd704daad8..955f9d207760d5 100644 --- a/src/cmd/compile/internal/base/print.go +++ b/src/cmd/compile/internal/base/print.go @@ -217,10 +217,10 @@ func FatalfAt(pos src.XPos, format string, args ...interface{}) { fmt.Printf("\n") // If this is a released compiler version, ask for a bug report. - if strings.HasPrefix(buildcfg.Version, "go") { + if Debug.Panic == 0 && strings.HasPrefix(buildcfg.Version, "go") { fmt.Printf("\n") fmt.Printf("Please file a bug report including a short program that triggers the error.\n") - fmt.Printf("https://golang.org/issue/new\n") + fmt.Printf("https://go.dev/issue/new\n") } else { // Not a release; dump a stack trace, too. fmt.Println() @@ -233,6 +233,27 @@ func FatalfAt(pos src.XPos, format string, args ...interface{}) { ErrorExit() } +// Assert reports "assertion failed" with Fatalf, unless b is true. +func Assert(b bool) { + if !b { + Fatalf("assertion failed") + } +} + +// Assertf reports a fatal error with Fatalf, unless b is true. +func Assertf(b bool, format string, args ...interface{}) { + if !b { + Fatalf(format, args...) + } +} + +// AssertfAt reports a fatal error with FatalfAt, unless b is true. +func AssertfAt(b bool, pos src.XPos, format string, args ...interface{}) { + if !b { + FatalfAt(pos, format, args...) + } +} + // hcrash crashes the compiler when -h is set, to find out where a message is generated. func hcrash() { if Flag.LowerH != 0 { diff --git a/src/cmd/compile/internal/bitvec/bv.go b/src/cmd/compile/internal/bitvec/bv.go index bcac1fe351fac1..ad7ed0a1965e9c 100644 --- a/src/cmd/compile/internal/bitvec/bv.go +++ b/src/cmd/compile/internal/bitvec/bv.go @@ -128,10 +128,21 @@ func (bv BitVec) IsEmpty() bool { return true } +func (bv BitVec) Count() int { + n := 0 + for _, x := range bv.B { + n += bits.OnesCount32(x) + } + return n +} + func (bv BitVec) Not() { for i, x := range bv.B { bv.B[i] = ^x } + if bv.N%wordBits != 0 { + bv.B[len(bv.B)-1] &= 1< 1 { + align := t.Alignment() + if off := t.Field(start).Offset; off&(align-1) != 0 { + // Offset is less aligned than the containing type. + // Use offset to determine alignment. + align = 1 << uint(bits.TrailingZeros64(uint64(off))) + } + size := t.Field(next).End() - t.Field(start).Offset + if size > align { + break + } + } + } + return t.Field(next-1).End() - t.Field(start).Offset, next +} + +// EqCanPanic reports whether == on type t could panic (has an interface somewhere). +// t must be comparable. +func EqCanPanic(t *types.Type) bool { + switch t.Kind() { + default: + return false + case types.TINTER: + return true + case types.TARRAY: + return EqCanPanic(t.Elem()) + case types.TSTRUCT: + for _, f := range t.FieldSlice() { + if !f.Sym.IsBlank() && EqCanPanic(f.Type) { + return true + } + } + return false + } +} + +// EqStructCost returns the cost of an equality comparison of two structs. +// +// The cost is determined using an algorithm which takes into consideration +// the size of the registers in the current architecture and the size of the +// memory-only fields in the struct. +func EqStructCost(t *types.Type) int64 { + cost := int64(0) + + for i, fields := 0, t.FieldSlice(); i < len(fields); { + f := fields[i] + + // Skip blank-named fields. + if f.Sym.IsBlank() { + i++ + continue + } + + n, _, next := eqStructFieldCost(t, i) + + cost += n + i = next + } + + return cost +} + +// eqStructFieldCost returns the cost of an equality comparison of two struct fields. +// t is the parent struct type, and i is the index of the field in the parent struct type. +// eqStructFieldCost may compute the cost of several adjacent fields at once. It returns +// the cost, the size of the set of fields it computed the cost for (in bytes), and the +// index of the first field not part of the set of fields for which the cost +// has already been calculated. +func eqStructFieldCost(t *types.Type, i int) (int64, int64, int) { + var ( + cost = int64(0) + regSize = int64(types.RegSize) + + size int64 + next int + ) + + if base.Ctxt.Arch.CanMergeLoads { + // If we can merge adjacent loads then we can calculate the cost of the + // comparison using the size of the memory run and the size of the registers. + size, next = Memrun(t, i) + cost = size / regSize + if size%regSize != 0 { + cost++ + } + return cost, size, next + } + + // If we cannot merge adjacent loads then we have to use the size of the + // field and take into account the type to determine how many loads and compares + // are needed. + ft := t.Field(i).Type + size = ft.Size() + next = i + 1 + + return calculateCostForType(ft), size, next +} + +func calculateCostForType(t *types.Type) int64 { + var cost int64 + switch t.Kind() { + case types.TSTRUCT: + return EqStructCost(t) + case types.TSLICE: + // Slices are not comparable. + base.Fatalf("eqStructFieldCost: unexpected slice type") + case types.TARRAY: + elemCost := calculateCostForType(t.Elem()) + cost = t.NumElem() * elemCost + case types.TSTRING, types.TINTER, types.TCOMPLEX64, types.TCOMPLEX128: + cost = 2 + case types.TINT64, types.TUINT64: + cost = 8 / int64(types.RegSize) + default: + cost = 1 + } + return cost +} + +// EqStruct compares two structs np and nq for equality. +// It works by building a list of boolean conditions to satisfy. +// Conditions must be evaluated in the returned order and +// properly short-circuited by the caller. +func EqStruct(t *types.Type, np, nq ir.Node) []ir.Node { + // The conditions are a list-of-lists. Conditions are reorderable + // within each inner list. The outer lists must be evaluated in order. + var conds [][]ir.Node + conds = append(conds, []ir.Node{}) + and := func(n ir.Node) { + i := len(conds) - 1 + conds[i] = append(conds[i], n) + } + + // Walk the struct using memequal for runs of AMEM + // and calling specific equality tests for the others. + for i, fields := 0, t.FieldSlice(); i < len(fields); { + f := fields[i] + + // Skip blank-named fields. + if f.Sym.IsBlank() { + i++ + continue + } + + // Compare non-memory fields with field equality. + if !IsRegularMemory(f.Type) { + if EqCanPanic(f.Type) { + // Enforce ordering by starting a new set of reorderable conditions. + conds = append(conds, []ir.Node{}) + } + p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) + q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym) + switch { + case f.Type.IsString(): + eqlen, eqmem := EqString(p, q) + and(eqlen) + and(eqmem) + default: + and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q)) + } + if EqCanPanic(f.Type) { + // Also enforce ordering after something that can panic. + conds = append(conds, []ir.Node{}) + } + i++ + continue + } + + cost, size, next := eqStructFieldCost(t, i) + if cost <= 4 { + // Cost of 4 or less: use plain field equality. + s := fields[i:next] + for _, f := range s { + and(eqfield(np, nq, ir.OEQ, f.Sym)) + } + } else { + // Higher cost: use memequal. + cc := eqmem(np, nq, f.Sym, size) + and(cc) + } + i = next + } + + // Sort conditions to put runtime calls last. + // Preserve the rest of the ordering. + var flatConds []ir.Node + for _, c := range conds { + isCall := func(n ir.Node) bool { + return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC + } + sort.SliceStable(c, func(i, j int) bool { + return !isCall(c[i]) && isCall(c[j]) + }) + flatConds = append(flatConds, c...) + } + return flatConds +} + +// EqString returns the nodes +// +// len(s) == len(t) +// +// and +// +// memequal(s.ptr, t.ptr, len(s)) +// +// which can be used to construct string equality comparison. +// eqlen must be evaluated before eqmem, and shortcircuiting is required. +func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) { + s = typecheck.Conv(s, types.Types[types.TSTRING]) + t = typecheck.Conv(t, types.Types[types.TSTRING]) + sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) + tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t) + slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR]) + tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR]) + + fn := typecheck.LookupRuntime("memequal") + fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8]) + call := typecheck.Call(base.Pos, fn, []ir.Node{sptr, tptr, ir.Copy(slen)}, false).(*ir.CallExpr) + + cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen) + cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) + cmp.SetType(types.Types[types.TBOOL]) + return cmp, call +} + +// EqInterface returns the nodes +// +// s.tab == t.tab (or s.typ == t.typ, as appropriate) +// +// and +// +// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate) +// +// which can be used to construct interface equality comparison. +// eqtab must be evaluated before eqdata, and shortcircuiting is required. +func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) { + if !types.Identical(s.Type(), t.Type()) { + base.Fatalf("EqInterface %v %v", s.Type(), t.Type()) + } + // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool) + // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool) + var fn ir.Node + if s.Type().IsEmptyInterface() { + fn = typecheck.LookupRuntime("efaceeq") + } else { + fn = typecheck.LookupRuntime("ifaceeq") + } + + stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s) + ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t) + sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s) + tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t) + sdata.SetType(types.Types[types.TUNSAFEPTR]) + tdata.SetType(types.Types[types.TUNSAFEPTR]) + sdata.SetTypecheck(1) + tdata.SetTypecheck(1) + + call := typecheck.Call(base.Pos, fn, []ir.Node{stab, sdata, tdata}, false).(*ir.CallExpr) + + cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab) + cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) + cmp.SetType(types.Types[types.TBOOL]) + return cmp, call +} + +// eqfield returns the node +// +// p.field == q.field +func eqfield(p ir.Node, q ir.Node, op ir.Op, field *types.Sym) ir.Node { + nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field) + ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field) + ne := ir.NewBinaryExpr(base.Pos, op, nx, ny) + return ne +} + +// eqmem returns the node +// +// memequal(&p.field, &q.field, size]) +func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node { + nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field))) + ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field))) + + fn, needsize := eqmemfunc(size, nx.Type().Elem()) + call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) + call.Args.Append(nx) + call.Args.Append(ny) + if needsize { + call.Args.Append(ir.NewInt(size)) + } + + return call +} + +func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) { + switch size { + default: + fn = typecheck.LookupRuntime("memequal") + needsize = true + case 1, 2, 4, 8, 16: + buf := fmt.Sprintf("memequal%d", int(size)*8) + fn = typecheck.LookupRuntime(buf) + } + + fn = typecheck.SubstArgTypes(fn, t, t) + return fn, needsize +} diff --git a/src/cmd/compile/internal/compare/compare_test.go b/src/cmd/compile/internal/compare/compare_test.go new file mode 100644 index 00000000000000..85c11bfd40f39f --- /dev/null +++ b/src/cmd/compile/internal/compare/compare_test.go @@ -0,0 +1,178 @@ +package compare + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/src" + "cmd/internal/sys" + "testing" +) + +type typefn func() *types.Type + +func init() { + // These are the few constants that need to be initialized in order to use + // the types package without using the typecheck package by calling + // typecheck.InitUniverse() (the normal way to initialize the types package). + types.PtrSize = 8 + types.RegSize = 8 + types.MaxWidth = 1 << 50 + typecheck.InitUniverse() + base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}} +} + +func TestEqStructCost(t *testing.T) { + newByteField := func(parent *types.Type, offset int64) *types.Field { + f := types.NewField(src.XPos{}, parent.Sym(), types.ByteType) + f.Offset = offset + return f + } + newArrayField := func(parent *types.Type, offset int64, len int64, kind types.Kind) *types.Field { + f := types.NewField(src.XPos{}, parent.Sym(), types.NewArray(types.Types[kind], len)) + // Call Type.Size here to force the size calculation to be done. If not done here the size returned later is incorrect. + f.Type.Size() + f.Offset = offset + return f + } + newField := func(parent *types.Type, offset int64, kind types.Kind) *types.Field { + f := types.NewField(src.XPos{}, parent.Sym(), types.Types[kind]) + f.Offset = offset + return f + } + tt := []struct { + name string + cost int64 + nonMergeLoadCost int64 + tfn typefn + }{ + {"struct without fields", 0, 0, + func() *types.Type { + return types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + }}, + {"struct with 1 byte field", 1, 1, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := []*types.Field{ + newByteField(parent, 0), + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with 8 byte fields", 1, 8, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 8) + for i := range fields { + fields[i] = newByteField(parent, int64(i)) + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with 16 byte fields", 2, 16, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 16) + for i := range fields { + fields[i] = newByteField(parent, int64(i)) + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with 32 byte fields", 4, 32, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 32) + for i := range fields { + fields[i] = newByteField(parent, int64(i)) + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with 2 int32 fields", 1, 2, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 2) + for i := range fields { + fields[i] = newField(parent, int64(i*4), types.TINT32) + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with 2 int32 fields and 1 int64", 2, 3, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 3) + fields[0] = newField(parent, int64(0), types.TINT32) + fields[1] = newField(parent, int64(4), types.TINT32) + fields[2] = newField(parent, int64(8), types.TINT64) + parent.SetFields(fields) + return parent + }, + }, + {"struct with 1 int field and 1 string", 3, 3, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 2) + fields[0] = newField(parent, int64(0), types.TINT64) + fields[1] = newField(parent, int64(8), types.TSTRING) + parent.SetFields(fields) + return parent + }, + }, + {"struct with 2 strings", 4, 4, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := make([]*types.Field, 2) + fields[0] = newField(parent, int64(0), types.TSTRING) + fields[1] = newField(parent, int64(8), types.TSTRING) + parent.SetFields(fields) + return parent + }, + }, + {"struct with 1 large byte array field", 26, 101, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := []*types.Field{ + newArrayField(parent, 0, 101, types.TUINT16), + } + parent.SetFields(fields) + return parent + }, + }, + {"struct with string array field", 4, 4, + func() *types.Type { + parent := types.NewStruct(types.NewPkg("main", ""), []*types.Field{}) + fields := []*types.Field{ + newArrayField(parent, 0, 2, types.TSTRING), + } + parent.SetFields(fields) + return parent + }, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + want := tc.cost + base.Ctxt.Arch.CanMergeLoads = true + actual := EqStructCost(tc.tfn()) + if actual != want { + t.Errorf("CanMergeLoads=true EqStructCost(%v) = %d, want %d", tc.tfn, actual, want) + } + + base.Ctxt.Arch.CanMergeLoads = false + want = tc.nonMergeLoadCost + actual = EqStructCost(tc.tfn()) + if actual != want { + t.Errorf("CanMergeLoads=false EqStructCost(%v) = %d, want %d", tc.tfn, actual, want) + } + }) + } +} diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go index 520203787f02dc..decd2611836fb1 100644 --- a/src/cmd/compile/internal/deadcode/deadcode.go +++ b/src/cmd/compile/internal/deadcode/deadcode.go @@ -6,6 +6,7 @@ package deadcode import ( "go/constant" + "go/token" "cmd/compile/internal/base" "cmd/compile/internal/ir" @@ -38,6 +39,7 @@ func Func(fn *ir.Func) { } } + ir.VisitList(fn.Body, markHiddenClosureDead) fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} } @@ -62,9 +64,11 @@ func stmts(nn *ir.Nodes) { if ir.IsConst(n.Cond, constant.Bool) { var body ir.Nodes if ir.BoolVal(n.Cond) { + ir.VisitList(n.Else, markHiddenClosureDead) n.Else = ir.Nodes{} body = n.Body } else { + ir.VisitList(n.Body, markHiddenClosureDead) n.Body = ir.Nodes{} body = n.Else } @@ -83,6 +87,85 @@ func stmts(nn *ir.Nodes) { } } } + if n.Op() == ir.OSWITCH { + n := n.(*ir.SwitchStmt) + // Use a closure wrapper here so we can use "return" to abort the analysis. + func() { + if n.Tag != nil && n.Tag.Op() == ir.OTYPESW { + return // no special type-switch case yet. + } + var x constant.Value // value we're switching on + if n.Tag != nil { + if ir.ConstType(n.Tag) == constant.Unknown { + return + } + x = n.Tag.Val() + } else { + x = constant.MakeBool(true) // switch { ... } => switch true { ... } + } + var def *ir.CaseClause + for _, cas := range n.Cases { + if len(cas.List) == 0 { // default case + def = cas + continue + } + for _, c := range cas.List { + if ir.ConstType(c) == constant.Unknown { + return // can't statically tell if it matches or not - give up. + } + if constant.Compare(x, token.EQL, c.Val()) { + for _, n := range cas.Body { + if n.Op() == ir.OFALL { + return // fallthrough makes it complicated - abort. + } + } + // This switch entry is the one that always triggers. + for _, cas2 := range n.Cases { + for _, c2 := range cas2.List { + if cas2 != cas || c2 != c { + ir.Visit(c2, markHiddenClosureDead) + } + } + if cas2 != cas { + ir.VisitList(cas2.Body, markHiddenClosureDead) + } + } + + cas.List[0] = c + cas.List = cas.List[:1] + n.Cases[0] = cas + n.Cases = n.Cases[:1] + return + } + } + } + if def != nil { + for _, n := range def.Body { + if n.Op() == ir.OFALL { + return // fallthrough makes it complicated - abort. + } + } + for _, cas := range n.Cases { + if cas != def { + ir.VisitList(cas.List, markHiddenClosureDead) + ir.VisitList(cas.Body, markHiddenClosureDead) + } + } + n.Cases[0] = def + n.Cases = n.Cases[:1] + return + } + + // TODO: handle case bodies ending with panic/return as we do in the IF case above. + + // entire switch is a nop - no case ever triggers + for _, cas := range n.Cases { + ir.VisitList(cas.List, markHiddenClosureDead) + ir.VisitList(cas.Body, markHiddenClosureDead) + } + n.Cases = n.Cases[:0] + }() + } if len(n.Init()) != 0 { stmts(n.(ir.InitNode).PtrInit()) @@ -114,6 +197,7 @@ func stmts(nn *ir.Nodes) { } if cut { + ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead) *nn = (*nn)[:i+1] break } @@ -150,3 +234,14 @@ func expr(n ir.Node) ir.Node { } return n } + +func markHiddenClosureDead(n ir.Node) { + if n.Op() != ir.OCLOSURE { + return + } + clo := n.(*ir.ClosureExpr) + if clo.Func.IsHiddenClosure() { + clo.Func.SetIsDeadcodeClosure(true) + } + ir.VisitList(clo.Func.Body, markHiddenClosureDead) +} diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go index 60ba208d0876b1..7350a6f1714417 100644 --- a/src/cmd/compile/internal/devirtualize/devirtualize.go +++ b/src/cmd/compile/internal/devirtualize/devirtualize.go @@ -17,9 +17,25 @@ import ( // Func devirtualizes calls within fn where possible. func Func(fn *ir.Func) { ir.CurFunc = fn + + // For promoted methods (including value-receiver methods promoted to pointer-receivers), + // the interface method wrapper may contain expressions that can panic (e.g., ODEREF, ODOTPTR, ODOTINTER). + // Devirtualization involves inlining these expressions (and possible panics) to the call site. + // This normally isn't a problem, but for go/defer statements it can move the panic from when/where + // the call executes to the go/defer statement itself, which is a visible change in semantics (e.g., #52072). + // To prevent this, we skip devirtualizing calls within go/defer statements altogether. + goDeferCall := make(map[*ir.CallExpr]bool) ir.VisitList(fn.Body, func(n ir.Node) { - if call, ok := n.(*ir.CallExpr); ok { - Call(call) + switch n := n.(type) { + case *ir.GoDeferStmt: + if call, ok := n.Call.(*ir.CallExpr); ok { + goDeferCall[call] = true + } + return + case *ir.CallExpr: + if !goDeferCall[n] { + Call(n) + } } }) } @@ -41,6 +57,60 @@ func Call(call *ir.CallExpr) { return } + if base.Debug.Unified != 0 { + // N.B., stencil.go converts shape-typed values to interface type + // using OEFACE instead of OCONVIFACE, so devirtualization fails + // above instead. That's why this code is specific to unified IR. + + // If typ is a shape type, then it was a type argument originally + // and we'd need an indirect call through the dictionary anyway. + // We're unable to devirtualize this call. + if typ.IsShape() { + return + } + + // If typ *has* a shape type, then it's an shaped, instantiated + // type like T[go.shape.int], and its methods (may) have an extra + // dictionary parameter. We could devirtualize this call if we + // could derive an appropriate dictionary argument. + // + // TODO(mdempsky): If typ has has a promoted non-generic method, + // then that method won't require a dictionary argument. We could + // still devirtualize those calls. + // + // TODO(mdempsky): We have the *runtime.itab in recv.TypeWord. It + // should be possible to compute the represented type's runtime + // dictionary from this (e.g., by adding a pointer from T[int]'s + // *runtime._type to .dict.T[int]; or by recognizing static + // references to go:itab.T[int],iface and constructing a direct + // reference to .dict.T[int]). + if typ.HasShape() { + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped receiver %v", call, typ) + } + return + } + + // Further, if sel.X's type has a shape type, then it's a shaped + // interface type. In this case, the (non-dynamic) TypeAssertExpr + // we construct below would attempt to create an itab + // corresponding to this shaped interface type; but the actual + // itab pointer in the interface value will correspond to the + // original (non-shaped) interface type instead. These are + // functionally equivalent, but they have distinct pointer + // identities, which leads to the type assertion failing. + // + // TODO(mdempsky): We know the type assertion here is safe, so we + // could instead set a flag so that walk skips the itab check. For + // now, punting is easy and safe. + if sel.X.Type().HasShape() { + if base.Flag.LowerM != 0 { + base.WarnfAt(call.Pos(), "cannot devirtualize %v: shaped interface %v", call, sel.X.Type()) + } + return + } + } + dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil) dt.SetType(typ) x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel)) diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 0e22b61bc3ff43..bdaa04339691c2 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -91,6 +91,11 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, continue } apdecls = append(apdecls, n) + if n.Type().Kind() == types.TSSA { + // Can happen for TypeInt128 types. This only happens for + // spill locations, so not a huge deal. + continue + } fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) } } @@ -150,6 +155,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir dcl := apDecls if fnsym.WasInlined() { dcl = preInliningDcls(fnsym) + } else { + // The backend's stackframe pass prunes away entries from the + // fn's Dcl list, including PARAMOUT nodes that correspond to + // output params passed in registers. Add back in these + // entries here so that we can process them properly during + // DWARF-gen. See issue 48573 for more details. + debugInfo := fn.DebugInfo.(*ssa.FuncDebug) + for _, n := range debugInfo.RegOutputParams { + if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { + panic("invalid ir.Name on debugInfo.RegOutputParams list") + } + dcl = append(dcl, n) + } } // If optimization is enabled, the list above will typically be @@ -214,9 +232,10 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir Type: base.Ctxt.Lookup(typename), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), - DeclCol: declpos.Col(), + DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, }) // Record go type of to insure that it gets emitted by the linker. fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) @@ -325,7 +344,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { localAutoOffset := func() int64 { offs = n.FrameOffset() - if base.Ctxt.FixedFrameSize() == 0 { + if base.Ctxt.Arch.FixedFrameSize == 0 { offs -= int64(types.PtrSize) } if buildcfg.FramePointerEnabled { @@ -343,7 +362,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { if n.IsOutputParamInRegisters() { offs = localAutoOffset() } else { - offs = n.FrameOffset() + base.Ctxt.FixedFrameSize() + offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize } default: @@ -371,9 +390,10 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { Type: base.Ctxt.Lookup(typename), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), - DeclCol: declpos.Col(), + DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, } } @@ -451,7 +471,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var gotype := reflectdata.TypeLinksym(n.Type()) delete(fnsym.Func().Autot, gotype) - typename := dwarf.InfoPrefix + gotype.Name[len("type."):] + typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] inlIndex := 0 if base.Flag.GenDwarfInl > 1 { if n.InlFormal() || n.InlLocal() { @@ -475,9 +495,10 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), - DeclCol: declpos.Col(), + DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, } list := debug.LocationLists[varID] if len(list) != 0 { @@ -531,7 +552,7 @@ func RecordFlags(flags ...string) { fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) } - // Adds flag to producer string singalling whether regabi is turned on or + // Adds flag to producer string signaling whether regabi is turned on or // off. // Once regabi is turned on across the board and the relative GOEXPERIMENT // knobs no longer exist this code should be removed. diff --git a/src/cmd/compile/internal/dwarfgen/dwinl.go b/src/cmd/compile/internal/dwarfgen/dwinl.go index 8adb36fc883da0..c785e064a7f484 100644 --- a/src/cmd/compile/internal/dwarfgen/dwinl.go +++ b/src/cmd/compile/internal/dwarfgen/dwinl.go @@ -244,7 +244,7 @@ func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int { DeclName: unversion(n.Sym().Name), DeclFile: pos.RelFilename(), DeclLine: pos.RelLine(), - DeclCol: pos.Col(), + DeclCol: pos.RelCol(), } if _, found := m[vp]; found { // We can see collisions (variables with the same name/file/line/col) in obfuscated or machine-generated code -- see issue 44378 for an example. Skip duplicates in such cases, since it is unlikely that a human will be debugging such code. diff --git a/src/cmd/compile/internal/escape/assign.go b/src/cmd/compile/internal/escape/assign.go new file mode 100644 index 00000000000000..80697bf37b3ee6 --- /dev/null +++ b/src/cmd/compile/internal/escape/assign.go @@ -0,0 +1,120 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" +) + +// addr evaluates an addressable expression n and returns a hole +// that represents storing into the represented location. +func (e *escape) addr(n ir.Node) hole { + if n == nil || ir.IsBlank(n) { + // Can happen in select case, range, maybe others. + return e.discardHole() + } + + k := e.heapHole() + + switch n.Op() { + default: + base.Fatalf("unexpected addr: %v", n) + case ir.ONAME: + n := n.(*ir.Name) + if n.Class == ir.PEXTERN { + break + } + k = e.oldLoc(n).asHole() + case ir.OLINKSYMOFFSET: + break + case ir.ODOT: + n := n.(*ir.SelectorExpr) + k = e.addr(n.X) + case ir.OINDEX: + n := n.(*ir.IndexExpr) + e.discard(n.Index) + if n.X.Type().IsArray() { + k = e.addr(n.X) + } else { + e.discard(n.X) + } + case ir.ODEREF, ir.ODOTPTR: + e.discard(n) + case ir.OINDEXMAP: + n := n.(*ir.IndexExpr) + e.discard(n.X) + e.assignHeap(n.Index, "key of map put", n) + } + + return k +} + +func (e *escape) addrs(l ir.Nodes) []hole { + var ks []hole + for _, n := range l { + ks = append(ks, e.addr(n)) + } + return ks +} + +func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) { + e.expr(e.heapHole().note(where, why), src) +} + +// assignList evaluates the assignment dsts... = srcs.... +func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) { + ks := e.addrs(dsts) + for i, k := range ks { + var src ir.Node + if i < len(srcs) { + src = srcs[i] + } + + if dst := dsts[i]; dst != nil { + // Detect implicit conversion of uintptr to unsafe.Pointer when + // storing into reflect.{Slice,String}Header. + if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) { + e.unsafeValue(e.heapHole().note(where, why), src) + continue + } + + // Filter out some no-op assignments for escape analysis. + if src != nil && isSelfAssign(dst, src) { + if base.Flag.LowerM != 0 { + base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where) + } + k = e.discardHole() + } + } + + e.expr(k.note(where, why), src) + } + + e.reassigned(ks, where) +} + +// reassigned marks the locations associated with the given holes as +// reassigned, unless the location represents a variable declared and +// assigned exactly once by where. +func (e *escape) reassigned(ks []hole, where ir.Node) { + if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil { + if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil { + // Zero-value assignment for variable declared without an + // explicit initial value. Assume this is its initialization + // statement. + return + } + } + + for _, k := range ks { + loc := k.dst + // Variables declared by range statements are assigned on every iteration. + if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE { + continue + } + loc.reassigned = true + } +} diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go new file mode 100644 index 00000000000000..4f602ca15fe779 --- /dev/null +++ b/src/cmd/compile/internal/escape/call.go @@ -0,0 +1,462 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// call evaluates a call expressions, including builtin calls. ks +// should contain the holes representing where the function callee's +// results flows. +func (e *escape) call(ks []hole, call ir.Node) { + var init ir.Nodes + e.callCommon(ks, call, &init, nil) + if len(init) != 0 { + call.(*ir.CallExpr).PtrInit().Append(init...) + } +} + +func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir.Func) { + + // argumentPragma handles escape analysis of argument *argp to the + // given hole. If the function callee is known, pragma is the + // function's pragma flags; otherwise 0. + argumentFunc := func(fn *ir.Name, k hole, argp *ir.Node) { + e.rewriteArgument(argp, init, call, fn, wrapper) + + e.expr(k.note(call, "call parameter"), *argp) + } + + argument := func(k hole, argp *ir.Node) { + argumentFunc(nil, k, argp) + } + + switch call.Op() { + default: + ir.Dump("esc", call) + base.Fatalf("unexpected call op: %v", call.Op()) + + case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: + call := call.(*ir.CallExpr) + typecheck.FixVariadicCall(call) + typecheck.FixMethodCall(call) + + // Pick out the function callee, if statically known. + // + // TODO(mdempsky): Change fn from *ir.Name to *ir.Func, but some + // functions (e.g., runtime builtins, method wrappers, generated + // eq/hash functions) don't have it set. Investigate whether + // that's a concern. + var fn *ir.Name + switch call.Op() { + case ir.OCALLFUNC: + // If we have a direct call to a closure (not just one we were + // able to statically resolve with ir.StaticValue), mark it as + // such so batch.outlives can optimize the flow results. + if call.X.Op() == ir.OCLOSURE { + call.X.(*ir.ClosureExpr).Func.SetClosureCalled(true) + } + + switch v := ir.StaticValue(call.X); v.Op() { + case ir.ONAME: + if v := v.(*ir.Name); v.Class == ir.PFUNC { + fn = v + } + case ir.OCLOSURE: + fn = v.(*ir.ClosureExpr).Func.Nname + case ir.OMETHEXPR: + fn = ir.MethodExprName(v) + } + case ir.OCALLMETH: + base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") + } + + fntype := call.X.Type() + if fn != nil { + fntype = fn.Type() + } + + if ks != nil && fn != nil && e.inMutualBatch(fn) { + for i, result := range fn.Type().Results().FieldSlice() { + e.expr(ks[i], ir.AsNode(result.Nname)) + } + } + + var recvp *ir.Node + if call.Op() == ir.OCALLFUNC { + // Evaluate callee function expression. + // + // Note: We use argument and not argumentFunc, because while + // call.X here may be an argument to runtime.{new,defer}proc, + // it's not an argument to fn itself. + argument(e.discardHole(), &call.X) + } else { + recvp = &call.X.(*ir.SelectorExpr).X + } + + args := call.Args + if recv := fntype.Recv(); recv != nil { + if recvp == nil { + // Function call using method expression. Recevier argument is + // at the front of the regular arguments list. + recvp = &args[0] + args = args[1:] + } + + argumentFunc(fn, e.tagHole(ks, fn, recv), recvp) + } + + for i, param := range fntype.Params().FieldSlice() { + argumentFunc(fn, e.tagHole(ks, fn, param), &args[i]) + } + + case ir.OINLCALL: + call := call.(*ir.InlinedCallExpr) + e.stmts(call.Body) + for i, result := range call.ReturnVars { + k := e.discardHole() + if ks != nil { + k = ks[i] + } + e.expr(k, result) + } + + case ir.OAPPEND: + call := call.(*ir.CallExpr) + args := call.Args + + // Appendee slice may flow directly to the result, if + // it has enough capacity. Alternatively, a new heap + // slice might be allocated, and all slice elements + // might flow to heap. + appendeeK := ks[0] + if args[0].Type().Elem().HasPointers() { + appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) + } + argument(appendeeK, &args[0]) + + if call.IsDDD { + appendedK := e.discardHole() + if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() { + appendedK = e.heapHole().deref(call, "appended slice...") + } + argument(appendedK, &args[1]) + } else { + for i := 1; i < len(args); i++ { + argument(e.heapHole(), &args[i]) + } + } + + case ir.OCOPY: + call := call.(*ir.BinaryExpr) + argument(e.discardHole(), &call.X) + + copiedK := e.discardHole() + if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() { + copiedK = e.heapHole().deref(call, "copied slice") + } + argument(copiedK, &call.Y) + + case ir.OPANIC: + call := call.(*ir.UnaryExpr) + argument(e.heapHole(), &call.X) + + case ir.OCOMPLEX: + call := call.(*ir.BinaryExpr) + argument(e.discardHole(), &call.X) + argument(e.discardHole(), &call.Y) + + case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + call := call.(*ir.CallExpr) + fixRecoverCall(call) + for i := range call.Args { + argument(e.discardHole(), &call.Args[i]) + } + + case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + call := call.(*ir.UnaryExpr) + argument(e.discardHole(), &call.X) + + case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: + call := call.(*ir.BinaryExpr) + argument(ks[0], &call.X) + argument(e.discardHole(), &call.Y) + } +} + +// goDeferStmt analyzes a "go" or "defer" statement. +// +// In the process, it also normalizes the statement to always use a +// simple function call with no arguments and no results. For example, +// it rewrites: +// +// defer f(x, y) +// +// into: +// +// x1, y1 := x, y +// defer func() { f(x1, y1) }() +func (e *escape) goDeferStmt(n *ir.GoDeferStmt) { + k := e.heapHole() + if n.Op() == ir.ODEFER && e.loopDepth == 1 { + // Top-level defer arguments don't escape to the heap, + // but they do need to last until they're invoked. + k = e.later(e.discardHole()) + + // force stack allocation of defer record, unless + // open-coded defers are used (see ssa.go) + n.SetEsc(ir.EscNever) + } + + call := n.Call + + init := n.PtrInit() + init.Append(ir.TakeInit(call)...) + e.stmts(*init) + + // If the function is already a zero argument/result function call, + // just escape analyze it normally. + // + // Note that the runtime is aware of this optimization for + // "go" statements that start in reflect.makeFuncStub or + // reflect.methodValueCall. + if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { + if sig := call.X.Type(); sig.NumParams()+sig.NumResults() == 0 { + if clo, ok := call.X.(*ir.ClosureExpr); ok && n.Op() == ir.OGO { + clo.IsGoWrap = true + } + e.expr(k, call.X) + return + } + } + + // Create a new no-argument function that we'll hand off to defer. + fn := ir.NewClosureFunc(n.Pos(), true) + fn.SetWrapper(true) + fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil)) + fn.Body = []ir.Node{call} + if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { + // If the callee is a named function, link to the original callee. + x := call.X + if x.Op() == ir.ONAME && x.(*ir.Name).Class == ir.PFUNC { + fn.WrappedFunc = call.X.(*ir.Name).Func + } else if x.Op() == ir.OMETHEXPR && ir.MethodExprFunc(x).Nname != nil { + fn.WrappedFunc = ir.MethodExprName(x).Func + } + } + + clo := fn.OClosure + if n.Op() == ir.OGO { + clo.IsGoWrap = true + } + + e.callCommon(nil, call, init, fn) + e.closures = append(e.closures, closure{e.spill(k, clo), clo}) + + // Create new top level call to closure. + n.Call = ir.NewCallExpr(call.Pos(), ir.OCALL, clo, nil) + ir.WithFunc(e.curfn, func() { + typecheck.Stmt(n.Call) + }) +} + +// rewriteArgument rewrites the argument *argp of the given call expression. +// fn is the static callee function, if known. +// wrapper is the go/defer wrapper function for call, if any. +func (e *escape) rewriteArgument(argp *ir.Node, init *ir.Nodes, call ir.Node, fn *ir.Name, wrapper *ir.Func) { + var pragma ir.PragmaFlag + if fn != nil && fn.Func != nil { + pragma = fn.Func.Pragma + } + + // unsafeUintptr rewrites "uintptr(ptr)" arguments to syscall-like + // functions, so that ptr is kept alive and/or escaped as + // appropriate. unsafeUintptr also reports whether it modified arg0. + unsafeUintptr := func(arg0 ir.Node) bool { + if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 { + return false + } + + // If the argument is really a pointer being converted to uintptr, + // arrange for the pointer to be kept alive until the call returns, + // by copying it into a temp and marking that temp + // still alive when we pop the temp stack. + if arg0.Op() != ir.OCONVNOP || !arg0.Type().IsUintptr() { + return false + } + arg := arg0.(*ir.ConvExpr) + + if !arg.X.Type().IsUnsafePtr() { + return false + } + + // Create and declare a new pointer-typed temp variable. + tmp := e.wrapExpr(arg.Pos(), &arg.X, init, call, wrapper) + + if pragma&ir.UintptrEscapes != 0 { + e.flow(e.heapHole().note(arg, "//go:uintptrescapes"), e.oldLoc(tmp)) + } + + if pragma&ir.UintptrKeepAlive != 0 { + call := call.(*ir.CallExpr) + + // SSA implements CallExpr.KeepAlive using OpVarLive, which + // doesn't support PAUTOHEAP variables. I tried changing it to + // use OpKeepAlive, but that ran into issues of its own. + // For now, the easy solution is to explicitly copy to (yet + // another) new temporary variable. + keep := tmp + if keep.Class == ir.PAUTOHEAP { + keep = e.copyExpr(arg.Pos(), tmp, call.PtrInit(), wrapper, false) + } + + keep.SetAddrtaken(true) // ensure SSA keeps the tmp variable + call.KeepAlive = append(call.KeepAlive, keep) + } + + return true + } + + visit := func(pos src.XPos, argp *ir.Node) { + // Optimize a few common constant expressions. By leaving these + // untouched in the call expression, we let the wrapper handle + // evaluating them, rather than taking up closure context space. + switch arg := *argp; arg.Op() { + case ir.OLITERAL, ir.ONIL, ir.OMETHEXPR: + return + case ir.ONAME: + if arg.(*ir.Name).Class == ir.PFUNC { + return + } + } + + if unsafeUintptr(*argp) { + return + } + + if wrapper != nil { + e.wrapExpr(pos, argp, init, call, wrapper) + } + } + + // Peel away any slice literals for better escape analyze + // them. For example: + // + // go F([]int{a, b}) + // + // If F doesn't escape its arguments, then the slice can + // be allocated on the new goroutine's stack. + // + // For variadic functions, the compiler has already rewritten: + // + // f(a, b, c) + // + // to: + // + // f([]T{a, b, c}...) + // + // So we need to look into slice elements to handle uintptr(ptr) + // arguments to syscall-like functions correctly. + if arg := *argp; arg.Op() == ir.OSLICELIT { + list := arg.(*ir.CompLitExpr).List + for i := range list { + el := &list[i] + if list[i].Op() == ir.OKEY { + el = &list[i].(*ir.KeyExpr).Value + } + visit(arg.Pos(), el) + } + } else { + visit(call.Pos(), argp) + } +} + +// wrapExpr replaces *exprp with a temporary variable copy. If wrapper +// is non-nil, the variable will be captured for use within that +// function. +func (e *escape) wrapExpr(pos src.XPos, exprp *ir.Node, init *ir.Nodes, call ir.Node, wrapper *ir.Func) *ir.Name { + tmp := e.copyExpr(pos, *exprp, init, e.curfn, true) + + if wrapper != nil { + // Currently for "defer i.M()" if i is nil it panics at the point + // of defer statement, not when deferred function is called. We + // need to do the nil check outside of the wrapper. + if call.Op() == ir.OCALLINTER && exprp == &call.(*ir.CallExpr).X.(*ir.SelectorExpr).X { + check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp)) + init.Append(typecheck.Stmt(check)) + } + + e.oldLoc(tmp).captured = true + + tmp = ir.NewClosureVar(pos, wrapper, tmp) + } + + *exprp = tmp + return tmp +} + +// copyExpr creates and returns a new temporary variable within fn; +// appends statements to init to declare and initialize it to expr; +// and escape analyzes the data flow if analyze is true. +func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes, fn *ir.Func, analyze bool) *ir.Name { + if ir.HasUniquePos(expr) { + pos = expr.Pos() + } + + tmp := typecheck.TempAt(pos, fn, expr.Type()) + + stmts := []ir.Node{ + ir.NewDecl(pos, ir.ODCL, tmp), + ir.NewAssignStmt(pos, tmp, expr), + } + typecheck.Stmts(stmts) + init.Append(stmts...) + + if analyze { + e.newLoc(tmp, false) + e.stmts(stmts) + } + + return tmp +} + +// tagHole returns a hole for evaluating an argument passed to param. +// ks should contain the holes representing where the function +// callee's results flows. fn is the statically-known callee function, +// if any. +func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole { + // If this is a dynamic call, we can't rely on param.Note. + if fn == nil { + return e.heapHole() + } + + if e.inMutualBatch(fn) { + return e.addr(ir.AsNode(param.Nname)) + } + + // Call to previously tagged function. + + var tagKs []hole + + esc := parseLeaks(param.Note) + if x := esc.Heap(); x >= 0 { + tagKs = append(tagKs, e.heapHole().shift(x)) + } + + if ks != nil { + for i := 0; i < numEscResults; i++ { + if x := esc.Result(i); x >= 0 { + tagKs = append(tagKs, ks[i].shift(x)) + } + } + } + + return e.teeHole(tagKs...) +} diff --git a/src/cmd/compile/internal/escape/desugar.go b/src/cmd/compile/internal/escape/desugar.go new file mode 100644 index 00000000000000..6c21981acad09a --- /dev/null +++ b/src/cmd/compile/internal/escape/desugar.go @@ -0,0 +1,37 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" +) + +// TODO(mdempsky): Desugaring doesn't belong during escape analysis, +// but for now it's the most convenient place for some rewrites. + +// fixRecoverCall rewrites an ORECOVER call into ORECOVERFP, +// adding an explicit frame pointer argument. +// If call is not an ORECOVER call, it's left unmodified. +func fixRecoverCall(call *ir.CallExpr) { + if call.Op() != ir.ORECOVER { + return + } + + pos := call.Pos() + + // FP is equal to caller's SP plus FixedFrameSize. + var fp ir.Node = ir.NewCallExpr(pos, ir.OGETCALLERSP, nil, nil) + if off := base.Ctxt.Arch.FixedFrameSize; off != 0 { + fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off)) + } + // TODO(mdempsky): Replace *int32 with unsafe.Pointer, without upsetting checkptr. + fp = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TINT32]), fp) + + call.SetOp(ir.ORECOVERFP) + call.Args = []ir.Node{typecheck.Expr(fp)} +} diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go index cd56f07b6147a4..05fbe58bbc9c7c 100644 --- a/src/cmd/compile/internal/escape/escape.go +++ b/src/cmd/compile/internal/escape/escape.go @@ -6,15 +6,12 @@ package escape import ( "fmt" - "math" - "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" - "cmd/internal/src" ) // Escape analysis. @@ -118,90 +115,8 @@ type escape struct { loopDepth int } -// An location represents an abstract location that stores a Go -// variable. -type location struct { - n ir.Node // represented variable or expression, if any - curfn *ir.Func // enclosing function - edges []edge // incoming edges - loopDepth int // loopDepth at declaration - - // resultIndex records the tuple index (starting at 1) for - // PPARAMOUT variables within their function's result type. - // For non-PPARAMOUT variables it's 0. - resultIndex int - - // derefs and walkgen are used during walkOne to track the - // minimal dereferences from the walk root. - derefs int // >= -1 - walkgen uint32 - - // dst and dstEdgeindex track the next immediate assignment - // destination location during walkone, along with the index - // of the edge pointing back to this location. - dst *location - dstEdgeIdx int - - // queued is used by walkAll to track whether this location is - // in the walk queue. - queued bool - - // escapes reports whether the represented variable's address - // escapes; that is, whether the variable must be heap - // allocated. - escapes bool - - // transient reports whether the represented expression's - // address does not outlive the statement; that is, whether - // its storage can be immediately reused. - transient bool - - // paramEsc records the represented parameter's leak set. - paramEsc leaks - - captured bool // has a closure captured this variable? - reassigned bool // has this variable been reassigned? - addrtaken bool // has this variable's address been taken? -} - -// An edge represents an assignment edge between two Go variables. -type edge struct { - src *location - derefs int // >= -1 - notes *note -} - -// Fmt is called from node printing to print information about escape analysis results. -func Fmt(n ir.Node) string { - text := "" - switch n.Esc() { - case ir.EscUnknown: - break - - case ir.EscHeap: - text = "esc(h)" - - case ir.EscNone: - text = "esc(no)" - - case ir.EscNever: - text = "esc(N)" - - default: - text = fmt.Sprintf("esc(%d)", n.Esc()) - } - - if n.Op() == ir.ONAME { - n := n.(*ir.Name) - if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 { - if text != "" { - text += " " - } - text += fmt.Sprintf("ld(%d)", loc.loopDepth) - } - } - - return text +func Funcs(all []ir.Node) { + ir.VisitFuncsBottomUp(all, Batch) } // Batch performs escape analysis on a minimal batch of @@ -269,8 +184,14 @@ func (b *batch) initFunc(fn *ir.Func) { // Allocate locations for local variables. for _, n := range fn.Dcl { - if n.Op() == ir.ONAME { - e.newLoc(n, false) + e.newLoc(n, false) + } + + // Also for hidden parameters (e.g., the ".this" parameter to a + // method value wrapper). + if fn.OClosure == nil { + for _, n := range fn.ClosureVars { + e.newLoc(n.Canonical(), false) } } @@ -289,6 +210,9 @@ func (b *batch) walkFunc(fn *ir.Func) { switch n.Op() { case ir.OLABEL: n := n.(*ir.LabelStmt) + if n.Label.IsBlank() { + break + } if e.labels == nil { e.labels = make(map[*types.Sym]labelState) } @@ -323,6 +247,9 @@ func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) { n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128) if !n.Byval() { n.SetAddrtaken(true) + if n.Sym().Name == typecheck.LocalDictName { + base.FatalfAt(n.Pos(), "dictionary variable not captured by value") + } } if base.Flag.LowerM > 1 { @@ -342,1771 +269,171 @@ func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) { } } -// Below we implement the methods for walking the AST and recording -// data flow edges. Note that because a sub-expression might have -// side-effects, it's important to always visit the entire AST. -// -// For example, write either: -// -// if x { -// e.discard(n.Left) -// } else { -// e.value(k, n.Left) -// } -// -// or -// -// if x { -// k = e.discardHole() -// } -// e.value(k, n.Left) -// -// Do NOT write: -// -// // BAD: possibly loses side-effects within n.Left -// if !x { -// e.value(k, n.Left) -// } - -// stmt evaluates a single Go statement. -func (e *escape) stmt(n ir.Node) { - if n == nil { - return - } - - lno := ir.SetPos(n) - defer func() { - base.Pos = lno - }() - - if base.Flag.LowerM > 2 { - fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n) - } - - e.stmts(n.Init()) - - switch n.Op() { - default: - base.Fatalf("unexpected stmt: %v", n) - - case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK: - // nop - - case ir.OBREAK, ir.OCONTINUE, ir.OGOTO: - // TODO(mdempsky): Handle dead code? - - case ir.OBLOCK: - n := n.(*ir.BlockStmt) - e.stmts(n.List) - - case ir.ODCL: - // Record loop depth at declaration. - n := n.(*ir.Decl) - if !ir.IsBlank(n.X) { - e.dcl(n.X) - } - - case ir.OLABEL: - n := n.(*ir.LabelStmt) - switch e.labels[n.Label] { - case nonlooping: - if base.Flag.LowerM > 2 { - fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n) - } - case looping: - if base.Flag.LowerM > 2 { - fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n) - } - e.loopDepth++ - default: - base.Fatalf("label missing tag") - } - delete(e.labels, n.Label) - - case ir.OIF: - n := n.(*ir.IfStmt) - e.discard(n.Cond) - e.block(n.Body) - e.block(n.Else) - - case ir.OFOR, ir.OFORUNTIL: - n := n.(*ir.ForStmt) - e.loopDepth++ - e.discard(n.Cond) - e.stmt(n.Post) - e.block(n.Body) - e.loopDepth-- - - case ir.ORANGE: - // for Key, Value = range X { Body } - n := n.(*ir.RangeStmt) - - // X is evaluated outside the loop. - tmp := e.newLoc(nil, false) - e.expr(tmp.asHole(), n.X) - - e.loopDepth++ - ks := e.addrs([]ir.Node{n.Key, n.Value}) - if n.X.Type().IsArray() { - e.flow(ks[1].note(n, "range"), tmp) - } else { - e.flow(ks[1].deref(n, "range-deref"), tmp) - } - e.reassigned(ks, n) - - e.block(n.Body) - e.loopDepth-- - - case ir.OSWITCH: - n := n.(*ir.SwitchStmt) +func (b *batch) finish(fns []*ir.Func) { + // Record parameter tags for package export data. + for _, fn := range fns { + fn.SetEsc(escFuncTagged) - if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok { - var ks []hole - if guard.Tag != nil { - for _, cas := range n.Cases { - cv := cas.Var - k := e.dcl(cv) // type switch variables have no ODCL. - if cv.Type().HasPointers() { - ks = append(ks, k.dotType(cv.Type(), cas, "switch case")) - } - } + narg := 0 + for _, fs := range &types.RecvsParams { + for _, f := range fs(fn.Type()).Fields().Slice() { + narg++ + f.Note = b.paramTag(fn, narg, f) } - e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X) - } else { - e.discard(n.Tag) - } - - for _, cas := range n.Cases { - e.discards(cas.List) - e.block(cas.Body) - } - - case ir.OSELECT: - n := n.(*ir.SelectStmt) - for _, cas := range n.Cases { - e.stmt(cas.Comm) - e.block(cas.Body) - } - case ir.ORECV: - // TODO(mdempsky): Consider e.discard(n.Left). - n := n.(*ir.UnaryExpr) - e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit - case ir.OSEND: - n := n.(*ir.SendStmt) - e.discard(n.Chan) - e.assignHeap(n.Value, "send", n) - - case ir.OAS: - n := n.(*ir.AssignStmt) - e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) - case ir.OASOP: - n := n.(*ir.AssignOpStmt) - // TODO(mdempsky): Worry about OLSH/ORSH? - e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) - case ir.OAS2: - n := n.(*ir.AssignListStmt) - e.assignList(n.Lhs, n.Rhs, "assign-pair", n) - - case ir.OAS2DOTTYPE: // v, ok = x.(type) - n := n.(*ir.AssignListStmt) - e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n) - case ir.OAS2MAPR: // v, ok = m[k] - n := n.(*ir.AssignListStmt) - e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n) - case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch - n := n.(*ir.AssignListStmt) - e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n) - - case ir.OAS2FUNC: - n := n.(*ir.AssignListStmt) - e.stmts(n.Rhs[0].Init()) - ks := e.addrs(n.Lhs) - e.call(ks, n.Rhs[0], nil) - e.reassigned(ks, n) - case ir.ORETURN: - n := n.(*ir.ReturnStmt) - results := e.curfn.Type().Results().FieldSlice() - dsts := make([]ir.Node, len(results)) - for i, res := range results { - dsts[i] = res.Nname.(*ir.Name) } - e.assignList(dsts, n.Results, "return", n) - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: - e.call(nil, n, nil) - case ir.OGO, ir.ODEFER: - n := n.(*ir.GoDeferStmt) - e.stmts(n.Call.Init()) - e.call(nil, n.Call, n) - - case ir.OTAILCALL: - // TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it. } -} - -func (e *escape) stmts(l ir.Nodes) { - for _, n := range l { - e.stmt(n) - } -} - -// block is like stmts, but preserves loopDepth. -func (e *escape) block(l ir.Nodes) { - old := e.loopDepth - e.stmts(l) - e.loopDepth = old -} - -// expr models evaluating an expression n and flowing the result into -// hole k. -func (e *escape) expr(k hole, n ir.Node) { - if n == nil { - return - } - e.stmts(n.Init()) - e.exprSkipInit(k, n) -} - -func (e *escape) exprSkipInit(k hole, n ir.Node) { - if n == nil { - return - } - - lno := ir.SetPos(n) - defer func() { - base.Pos = lno - }() - - uintptrEscapesHack := k.uintptrEscapesHack - k.uintptrEscapesHack = false - - if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.(*ir.ConvExpr).X.Type().IsUnsafePtr() { - // nop - } else if k.derefs >= 0 && !n.Type().HasPointers() { - k.dst = &e.blankLoc - } - - switch n.Op() { - default: - base.Fatalf("unexpected expr: %s %v", n.Op().String(), n) - case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET: - // nop - - case ir.ONAME: - n := n.(*ir.Name) - if n.Class == ir.PFUNC || n.Class == ir.PEXTERN { - return - } - if n.IsClosureVar() && n.Defn == nil { - return // ".this" from method value wrapper - } - e.flow(k, e.oldLoc(n)) - - case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT: - n := n.(*ir.UnaryExpr) - e.discard(n.X) - case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: - n := n.(*ir.BinaryExpr) - e.discard(n.X) - e.discard(n.Y) - case ir.OANDAND, ir.OOROR: - n := n.(*ir.LogicalExpr) - e.discard(n.X) - e.discard(n.Y) - case ir.OADDR: - n := n.(*ir.AddrExpr) - e.expr(k.addr(n, "address-of"), n.X) // "address-of" - case ir.ODEREF: - n := n.(*ir.StarExpr) - e.expr(k.deref(n, "indirection"), n.X) // "indirection" - case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER: - n := n.(*ir.SelectorExpr) - e.expr(k.note(n, "dot"), n.X) - case ir.ODOTPTR: - n := n.(*ir.SelectorExpr) - e.expr(k.deref(n, "dot of pointer"), n.X) // "dot of pointer" - case ir.ODOTTYPE, ir.ODOTTYPE2: - n := n.(*ir.TypeAssertExpr) - e.expr(k.dotType(n.Type(), n, "dot"), n.X) - case ir.OINDEX: - n := n.(*ir.IndexExpr) - if n.X.Type().IsArray() { - e.expr(k.note(n, "fixed-array-index-of"), n.X) - } else { - // TODO(mdempsky): Fix why reason text. - e.expr(k.deref(n, "dot of pointer"), n.X) - } - e.discard(n.Index) - case ir.OINDEXMAP: - n := n.(*ir.IndexExpr) - e.discard(n.X) - e.discard(n.Index) - case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR: - n := n.(*ir.SliceExpr) - e.expr(k.note(n, "slice"), n.X) - e.discard(n.Low) - e.discard(n.High) - e.discard(n.Max) - - case ir.OCONV, ir.OCONVNOP: - n := n.(*ir.ConvExpr) - if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() { - // When -d=checkptr=2 is enabled, treat - // conversions to unsafe.Pointer as an - // escaping operation. This allows better - // runtime instrumentation, since we can more - // easily detect object boundaries on the heap - // than the stack. - e.assignHeap(n.X, "conversion to unsafe.Pointer", n) - } else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { - e.unsafeValue(k, n.X) - } else { - e.expr(k, n.X) - } - case ir.OCONVIFACE: - n := n.(*ir.ConvExpr) - if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) { - k = e.spill(k, n) - } - e.expr(k.note(n, "interface-converted"), n.X) - case ir.OSLICE2ARRPTR: - // the slice pointer flows directly to the result - n := n.(*ir.ConvExpr) - e.expr(k, n.X) - case ir.ORECV: - n := n.(*ir.UnaryExpr) - e.discard(n.X) - - case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: - e.call([]hole{k}, n, nil) - - case ir.ONEW: - n := n.(*ir.UnaryExpr) - e.spill(k, n) - - case ir.OMAKESLICE: - n := n.(*ir.MakeExpr) - e.spill(k, n) - e.discard(n.Len) - e.discard(n.Cap) - case ir.OMAKECHAN: - n := n.(*ir.MakeExpr) - e.discard(n.Len) - case ir.OMAKEMAP: - n := n.(*ir.MakeExpr) - e.spill(k, n) - e.discard(n.Len) - - case ir.ORECOVER: - // nop - - case ir.OCALLPART: - // Flow the receiver argument to both the closure and - // to the receiver parameter. - - n := n.(*ir.SelectorExpr) - closureK := e.spill(k, n) - - m := n.Selection - - // We don't know how the method value will be called - // later, so conservatively assume the result - // parameters all flow to the heap. - // - // TODO(mdempsky): Change ks into a callback, so that - // we don't have to create this slice? - var ks []hole - for i := m.Type.NumResults(); i > 0; i-- { - ks = append(ks, e.heapHole()) - } - name, _ := m.Nname.(*ir.Name) - paramK := e.tagHole(ks, name, m.Type.Recv()) - - e.expr(e.teeHole(paramK, closureK), n.X) - - case ir.OPTRLIT: - n := n.(*ir.AddrExpr) - e.expr(e.spill(k, n), n.X) - - case ir.OARRAYLIT: - n := n.(*ir.CompLitExpr) - for _, elt := range n.List { - if elt.Op() == ir.OKEY { - elt = elt.(*ir.KeyExpr).Value - } - e.expr(k.note(n, "array literal element"), elt) - } - - case ir.OSLICELIT: - n := n.(*ir.CompLitExpr) - k = e.spill(k, n) - k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters - - for _, elt := range n.List { - if elt.Op() == ir.OKEY { - elt = elt.(*ir.KeyExpr).Value - } - e.expr(k.note(n, "slice-literal-element"), elt) - } - - case ir.OSTRUCTLIT: - n := n.(*ir.CompLitExpr) - for _, elt := range n.List { - e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value) + for _, loc := range b.allLocs { + n := loc.n + if n == nil { + continue } - - case ir.OMAPLIT: - n := n.(*ir.CompLitExpr) - e.spill(k, n) - - // Map keys and values are always stored in the heap. - for _, elt := range n.List { - elt := elt.(*ir.KeyExpr) - e.assignHeap(elt.Key, "map literal key", n) - e.assignHeap(elt.Value, "map literal value", n) + if n.Op() == ir.ONAME { + n := n.(*ir.Name) + n.Opt = nil } - case ir.OCLOSURE: - n := n.(*ir.ClosureExpr) - k = e.spill(k, n) - e.closures = append(e.closures, closure{k, n}) + // Update n.Esc based on escape analysis results. - if fn := n.Func; fn.IsHiddenClosure() { - for _, cv := range fn.ClosureVars { - if loc := e.oldLoc(cv); !loc.captured { - loc.captured = true + // Omit escape diagnostics for go/defer wrappers, at least for now. + // Historically, we haven't printed them, and test cases don't expect them. + // TODO(mdempsky): Update tests to expect this. + goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper() - // Ignore reassignments to the variable in straightline code - // preceding the first capture by a closure. - if loc.loopDepth == e.loopDepth { - loc.reassigned = false - } + if loc.escapes { + if n.Op() == ir.ONAME { + if base.Flag.CompilingRuntime { + base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n) + } + if base.Flag.LowerM != 0 { + base.WarnfAt(n.Pos(), "moved to heap: %v", n) + } + } else { + if base.Flag.LowerM != 0 && !goDeferWrapper { + base.WarnfAt(n.Pos(), "%v escapes to heap", n) + } + if logopt.Enabled() { + var e_curfn *ir.Func // TODO(mdempsky): Fix. + logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn)) } } - - for _, n := range fn.Dcl { - // Add locations for local variables of the - // closure, if needed, in case we're not including - // the closure func in the batch for escape - // analysis (happens for escape analysis called - // from reflectdata.methodWrapper) - if n.Op() == ir.ONAME && n.Opt == nil { - e.with(fn).newLoc(n, false) + n.SetEsc(ir.EscHeap) + } else { + if base.Flag.LowerM != 0 && n.Op() != ir.ONAME && !goDeferWrapper { + base.WarnfAt(n.Pos(), "%v does not escape", n) + } + n.SetEsc(ir.EscNone) + if loc.transient { + switch n.Op() { + case ir.OCLOSURE: + n := n.(*ir.ClosureExpr) + n.SetTransient(true) + case ir.OMETHVALUE: + n := n.(*ir.SelectorExpr) + n.SetTransient(true) + case ir.OSLICELIT: + n := n.(*ir.CompLitExpr) + n.SetTransient(true) } } - e.walkFunc(fn) } - - case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR: - n := n.(*ir.ConvExpr) - e.spill(k, n) - e.discard(n.X) - - case ir.OADDSTR: - n := n.(*ir.AddStringExpr) - e.spill(k, n) - - // Arguments of OADDSTR never escape; - // runtime.concatstrings makes sure of that. - e.discards(n.List) } } -// unsafeValue evaluates a uintptr-typed arithmetic expression looking -// for conversions from an unsafe.Pointer. -func (e *escape) unsafeValue(k hole, n ir.Node) { - if n.Type().Kind() != types.TUINTPTR { - base.Fatalf("unexpected type %v for %v", n.Type(), n) - } - if k.addrtaken { - base.Fatalf("unexpected addrtaken") - } - - e.stmts(n.Init()) - - switch n.Op() { - case ir.OCONV, ir.OCONVNOP: - n := n.(*ir.ConvExpr) - if n.X.Type().IsUnsafePtr() { - e.expr(k, n.X) - } else { - e.discard(n.X) - } - case ir.ODOTPTR: - n := n.(*ir.SelectorExpr) - if ir.IsReflectHeaderDataField(n) { - e.expr(k.deref(n, "reflect.Header.Data"), n.X) - } else { - e.discard(n.X) +// inMutualBatch reports whether function fn is in the batch of +// mutually recursive functions being analyzed. When this is true, +// fn has not yet been analyzed, so its parameters and results +// should be incorporated directly into the flow graph instead of +// relying on its escape analysis tagging. +func (e *escape) inMutualBatch(fn *ir.Name) bool { + if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged { + if fn.Defn.Esc() == escFuncUnknown { + base.Fatalf("graph inconsistency: %v", fn) } - case ir.OPLUS, ir.ONEG, ir.OBITNOT: - n := n.(*ir.UnaryExpr) - e.unsafeValue(k, n.X) - case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT: - n := n.(*ir.BinaryExpr) - e.unsafeValue(k, n.X) - e.unsafeValue(k, n.Y) - case ir.OLSH, ir.ORSH: - n := n.(*ir.BinaryExpr) - e.unsafeValue(k, n.X) - // RHS need not be uintptr-typed (#32959) and can't meaningfully - // flow pointers anyway. - e.discard(n.Y) - default: - e.exprSkipInit(e.discardHole(), n) + return true } + return false } -// discard evaluates an expression n for side-effects, but discards -// its value. -func (e *escape) discard(n ir.Node) { - e.expr(e.discardHole(), n) -} - -func (e *escape) discards(l ir.Nodes) { - for _, n := range l { - e.discard(n) - } -} +const ( + escFuncUnknown = 0 + iota + escFuncPlanned + escFuncStarted + escFuncTagged +) -// addr evaluates an addressable expression n and returns a hole -// that represents storing into the represented location. -func (e *escape) addr(n ir.Node) hole { - if n == nil || ir.IsBlank(n) { - // Can happen in select case, range, maybe others. - return e.discardHole() - } +// Mark labels that have no backjumps to them as not increasing e.loopdepth. +type labelState int - k := e.heapHole() +const ( + looping labelState = 1 + iota + nonlooping +) - switch n.Op() { - default: - base.Fatalf("unexpected addr: %v", n) - case ir.ONAME: - n := n.(*ir.Name) - if n.Class == ir.PEXTERN { - break - } - k = e.oldLoc(n).asHole() - case ir.OLINKSYMOFFSET: - break - case ir.ODOT: - n := n.(*ir.SelectorExpr) - k = e.addr(n.X) - case ir.OINDEX: - n := n.(*ir.IndexExpr) - e.discard(n.Index) - if n.X.Type().IsArray() { - k = e.addr(n.X) - } else { - e.discard(n.X) +func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string { + name := func() string { + if f.Sym != nil { + return f.Sym.Name } - case ir.ODEREF, ir.ODOTPTR: - e.discard(n) - case ir.OINDEXMAP: - n := n.(*ir.IndexExpr) - e.discard(n.X) - e.assignHeap(n.Index, "key of map put", n) + return fmt.Sprintf("arg#%d", narg) } - return k -} + // Only report diagnostics for user code; + // not for wrappers generated around them. + // TODO(mdempsky): Generalize this. + diagnose := base.Flag.LowerM != 0 && !(fn.Wrapper() || fn.Dupok()) -func (e *escape) addrs(l ir.Nodes) []hole { - var ks []hole - for _, n := range l { - ks = append(ks, e.addr(n)) - } - return ks -} + if len(fn.Body) == 0 { + // Assume that uintptr arguments must be held live across the call. + // This is most important for syscall.Syscall. + // See golang.org/issue/13372. + // This really doesn't have much to do with escape analysis per se, + // but we are reusing the ability to annotate an individual function + // argument and pass those annotations along to importing code. + fn.Pragma |= ir.UintptrKeepAlive -// reassigned marks the locations associated with the given holes as -// reassigned, unless the location represents a variable declared and -// assigned exactly once by where. -func (e *escape) reassigned(ks []hole, where ir.Node) { - if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil { - if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil { - // Zero-value assignment for variable declared without an - // explicit initial value. Assume this is its initialization - // statement. - return + if f.Type.IsUintptr() { + if diagnose { + base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name()) + } + return "" } - } - for _, k := range ks { - loc := k.dst - // Variables declared by range statements are assigned on every iteration. - if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE { - continue + if !f.Type.HasPointers() { // don't bother tagging for scalars + return "" } - loc.reassigned = true - } -} -// assignList evaluates the assignment dsts... = srcs.... -func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) { - ks := e.addrs(dsts) - for i, k := range ks { - var src ir.Node - if i < len(srcs) { - src = srcs[i] - } + var esc leaks - if dst := dsts[i]; dst != nil { - // Detect implicit conversion of uintptr to unsafe.Pointer when - // storing into reflect.{Slice,String}Header. - if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) { - e.unsafeValue(e.heapHole().note(where, why), src) - continue + // External functions are assumed unsafe, unless + // //go:noescape is given before the declaration. + if fn.Pragma&ir.Noescape != 0 { + if diagnose && f.Sym != nil { + base.WarnfAt(f.Pos, "%v does not escape", name()) } - - // Filter out some no-op assignments for escape analysis. - if src != nil && isSelfAssign(dst, src) { - if base.Flag.LowerM != 0 { - base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where) - } - k = e.discardHole() + } else { + if diagnose && f.Sym != nil { + base.WarnfAt(f.Pos, "leaking param: %v", name()) } + esc.AddHeap(0) } - e.expr(k.note(where, why), src) - } - - e.reassigned(ks, where) -} - -func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) { - e.expr(e.heapHole().note(where, why), src) -} - -// call evaluates a call expressions, including builtin calls. ks -// should contain the holes representing where the function callee's -// results flows; where is the OGO/ODEFER context of the call, if any. -func (e *escape) call(ks []hole, call, where ir.Node) { - topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1 - if topLevelDefer { - // force stack allocation of defer record, unless - // open-coded defers are used (see ssa.go) - where.SetEsc(ir.EscNever) + return esc.Encode() } - argument := func(k hole, arg ir.Node) { - if topLevelDefer { - // Top level defers arguments don't escape to - // heap, but they do need to last until end of - // function. - k = e.later(k) - } else if where != nil { - k = e.heapHole() - } - - e.expr(k.note(call, "call parameter"), arg) - } - - switch call.Op() { - default: - ir.Dump("esc", call) - base.Fatalf("unexpected call op: %v", call.Op()) - - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: - call := call.(*ir.CallExpr) - typecheck.FixVariadicCall(call) - - // Pick out the function callee, if statically known. - var fn *ir.Name - switch call.Op() { - case ir.OCALLFUNC: - switch v := ir.StaticValue(call.X); { - case v.Op() == ir.ONAME && v.(*ir.Name).Class == ir.PFUNC: - fn = v.(*ir.Name) - case v.Op() == ir.OCLOSURE: - fn = v.(*ir.ClosureExpr).Func.Nname - } - case ir.OCALLMETH: - fn = ir.MethodExprName(call.X) - } - - fntype := call.X.Type() - if fn != nil { - fntype = fn.Type() - } - - if ks != nil && fn != nil && e.inMutualBatch(fn) { - for i, result := range fn.Type().Results().FieldSlice() { - e.expr(ks[i], ir.AsNode(result.Nname)) - } - } - - if r := fntype.Recv(); r != nil { - argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X) - } else { - // Evaluate callee function expression. - argument(e.discardHole(), call.X) - } - - args := call.Args - for i, param := range fntype.Params().FieldSlice() { - argument(e.tagHole(ks, fn, param), args[i]) - } - - case ir.OAPPEND: - call := call.(*ir.CallExpr) - args := call.Args - - // Appendee slice may flow directly to the result, if - // it has enough capacity. Alternatively, a new heap - // slice might be allocated, and all slice elements - // might flow to heap. - appendeeK := ks[0] - if args[0].Type().Elem().HasPointers() { - appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice")) - } - argument(appendeeK, args[0]) - - if call.IsDDD { - appendedK := e.discardHole() - if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() { - appendedK = e.heapHole().deref(call, "appended slice...") - } - argument(appendedK, args[1]) - } else { - for _, arg := range args[1:] { - argument(e.heapHole(), arg) - } - } - - case ir.OCOPY: - call := call.(*ir.BinaryExpr) - argument(e.discardHole(), call.X) - - copiedK := e.discardHole() - if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() { - copiedK = e.heapHole().deref(call, "copied slice") - } - argument(copiedK, call.Y) - - case ir.OPANIC: - call := call.(*ir.UnaryExpr) - argument(e.heapHole(), call.X) - - case ir.OCOMPLEX: - call := call.(*ir.BinaryExpr) - argument(e.discardHole(), call.X) - argument(e.discardHole(), call.Y) - case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: - call := call.(*ir.CallExpr) - for _, arg := range call.Args { - argument(e.discardHole(), arg) - } - case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: - call := call.(*ir.UnaryExpr) - argument(e.discardHole(), call.X) - - case ir.OUNSAFEADD, ir.OUNSAFESLICE: - call := call.(*ir.BinaryExpr) - argument(ks[0], call.X) - argument(e.discardHole(), call.Y) - } -} - -// tagHole returns a hole for evaluating an argument passed to param. -// ks should contain the holes representing where the function -// callee's results flows. fn is the statically-known callee function, -// if any. -func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole { - // If this is a dynamic call, we can't rely on param.Note. - if fn == nil { - return e.heapHole() - } - - if e.inMutualBatch(fn) { - return e.addr(ir.AsNode(param.Nname)) - } - - // Call to previously tagged function. - - if param.Note == UintptrEscapesNote { - k := e.heapHole() - k.uintptrEscapesHack = true - return k - } - - var tagKs []hole - - esc := parseLeaks(param.Note) - if x := esc.Heap(); x >= 0 { - tagKs = append(tagKs, e.heapHole().shift(x)) - } - - if ks != nil { - for i := 0; i < numEscResults; i++ { - if x := esc.Result(i); x >= 0 { - tagKs = append(tagKs, ks[i].shift(x)) - } - } - } - - return e.teeHole(tagKs...) -} - -// inMutualBatch reports whether function fn is in the batch of -// mutually recursive functions being analyzed. When this is true, -// fn has not yet been analyzed, so its parameters and results -// should be incorporated directly into the flow graph instead of -// relying on its escape analysis tagging. -func (e *escape) inMutualBatch(fn *ir.Name) bool { - if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged { - if fn.Defn.Esc() == escFuncUnknown { - base.Fatalf("graph inconsistency: %v", fn) - } - return true - } - return false -} - -// An hole represents a context for evaluation a Go -// expression. E.g., when evaluating p in "x = **p", we'd have a hole -// with dst==x and derefs==2. -type hole struct { - dst *location - derefs int // >= -1 - notes *note - - // addrtaken indicates whether this context is taking the address of - // the expression, independent of whether the address will actually - // be stored into a variable. - addrtaken bool - - // uintptrEscapesHack indicates this context is evaluating an - // argument for a //go:uintptrescapes function. - uintptrEscapesHack bool -} - -type note struct { - next *note - where ir.Node - why string -} - -func (k hole) note(where ir.Node, why string) hole { - if where == nil || why == "" { - base.Fatalf("note: missing where/why") - } - if base.Flag.LowerM >= 2 || logopt.Enabled() { - k.notes = ¬e{ - next: k.notes, - where: where, - why: why, - } - } - return k -} - -func (k hole) shift(delta int) hole { - k.derefs += delta - if k.derefs < -1 { - base.Fatalf("derefs underflow: %v", k.derefs) - } - k.addrtaken = delta < 0 - return k -} - -func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) } -func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) } - -func (k hole) dotType(t *types.Type, where ir.Node, why string) hole { - if !t.IsInterface() && !types.IsDirectIface(t) { - k = k.shift(1) - } - return k.note(where, why) -} - -// teeHole returns a new hole that flows into each hole of ks, -// similar to the Unix tee(1) command. -func (e *escape) teeHole(ks ...hole) hole { - if len(ks) == 0 { - return e.discardHole() - } - if len(ks) == 1 { - return ks[0] - } - // TODO(mdempsky): Optimize if there's only one non-discard hole? - - // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a - // new temporary location ltmp, wire it into place, and return - // a hole for "ltmp = _". - loc := e.newLoc(nil, true) - for _, k := range ks { - // N.B., "p = &q" and "p = &tmp; tmp = q" are not - // semantically equivalent. To combine holes like "l1 - // = _" and "l2 = &_", we'd need to wire them as "l1 = - // *ltmp" and "l2 = ltmp" and return "ltmp = &_" - // instead. - if k.derefs < 0 { - base.Fatalf("teeHole: negative derefs") - } - - e.flow(k, loc) - } - return loc.asHole() -} - -func (e *escape) dcl(n *ir.Name) hole { - if n.Curfn != e.curfn || n.IsClosureVar() { - base.Fatalf("bad declaration of %v", n) - } - loc := e.oldLoc(n) - loc.loopDepth = e.loopDepth - return loc.asHole() -} - -// spill allocates a new location associated with expression n, flows -// its address to k, and returns a hole that flows values to it. It's -// intended for use with most expressions that allocate storage. -func (e *escape) spill(k hole, n ir.Node) hole { - loc := e.newLoc(n, true) - e.flow(k.addr(n, "spill"), loc) - return loc.asHole() -} - -// later returns a new hole that flows into k, but some time later. -// Its main effect is to prevent immediate reuse of temporary -// variables introduced during Order. -func (e *escape) later(k hole) hole { - loc := e.newLoc(nil, false) - e.flow(k, loc) - return loc.asHole() -} - -func (e *escape) newLoc(n ir.Node, transient bool) *location { - if e.curfn == nil { - base.Fatalf("e.curfn isn't set") - } - if n != nil && n.Type() != nil && n.Type().NotInHeap() { - base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type()) - } - - if n != nil && n.Op() == ir.ONAME { - n = n.(*ir.Name).Canonical() - } - loc := &location{ - n: n, - curfn: e.curfn, - loopDepth: e.loopDepth, - transient: transient, - } - e.allLocs = append(e.allLocs, loc) - if n != nil { - if n.Op() == ir.ONAME { - n := n.(*ir.Name) - if n.Curfn != e.curfn { - base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n) - } - - if n.Opt != nil { - base.Fatalf("%v already has a location", n) - } - n.Opt = loc - } - } - return loc -} - -func (b *batch) oldLoc(n *ir.Name) *location { - if n.Canonical().Opt == nil { - base.Fatalf("%v has no location", n) - } - return n.Canonical().Opt.(*location) -} - -func (l *location) asHole() hole { - return hole{dst: l} -} - -func (b *batch) flow(k hole, src *location) { - if k.addrtaken { - src.addrtaken = true - } - - dst := k.dst - if dst == &b.blankLoc { - return - } - if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ... - return - } - if dst.escapes && k.derefs < 0 { // dst = &src - if base.Flag.LowerM >= 2 || logopt.Enabled() { - pos := base.FmtPos(src.n.Pos()) - if base.Flag.LowerM >= 2 { - fmt.Printf("%s: %v escapes to heap:\n", pos, src.n) - } - explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{}) - if logopt.Enabled() { - var e_curfn *ir.Func // TODO(mdempsky): Fix. - logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation) - } - - } - src.escapes = true - return - } - - // TODO(mdempsky): Deduplicate edges? - dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes}) -} - -func (b *batch) heapHole() hole { return b.heapLoc.asHole() } -func (b *batch) discardHole() hole { return b.blankLoc.asHole() } - -// walkAll computes the minimal dereferences between all pairs of -// locations. -func (b *batch) walkAll() { - // We use a work queue to keep track of locations that we need - // to visit, and repeatedly walk until we reach a fixed point. - // - // We walk once from each location (including the heap), and - // then re-enqueue each location on its transition from - // transient->!transient and !escapes->escapes, which can each - // happen at most once. So we take Θ(len(e.allLocs)) walks. - - // LIFO queue, has enough room for e.allLocs and e.heapLoc. - todo := make([]*location, 0, len(b.allLocs)+1) - enqueue := func(loc *location) { - if !loc.queued { - todo = append(todo, loc) - loc.queued = true - } - } - - for _, loc := range b.allLocs { - enqueue(loc) - } - enqueue(&b.heapLoc) - - var walkgen uint32 - for len(todo) > 0 { - root := todo[len(todo)-1] - todo = todo[:len(todo)-1] - root.queued = false - - walkgen++ - b.walkOne(root, walkgen, enqueue) - } -} - -// walkOne computes the minimal number of dereferences from root to -// all other locations. -func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) { - // The data flow graph has negative edges (from addressing - // operations), so we use the Bellman-Ford algorithm. However, - // we don't have to worry about infinite negative cycles since - // we bound intermediate dereference counts to 0. - - root.walkgen = walkgen - root.derefs = 0 - root.dst = nil - - todo := []*location{root} // LIFO queue - for len(todo) > 0 { - l := todo[len(todo)-1] - todo = todo[:len(todo)-1] - - derefs := l.derefs - - // If l.derefs < 0, then l's address flows to root. - addressOf := derefs < 0 - if addressOf { - // For a flow path like "root = &l; l = x", - // l's address flows to root, but x's does - // not. We recognize this by lower bounding - // derefs at 0. - derefs = 0 - - // If l's address flows to a non-transient - // location, then l can't be transiently - // allocated. - if !root.transient && l.transient { - l.transient = false - enqueue(l) - } - } - - if b.outlives(root, l) { - // l's value flows to root. If l is a function - // parameter and root is the heap or a - // corresponding result parameter, then record - // that value flow for tagging the function - // later. - if l.isName(ir.PPARAM) { - if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes { - if base.Flag.LowerM >= 2 { - fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs) - } - explanation := b.explainPath(root, l) - if logopt.Enabled() { - var e_curfn *ir.Func // TODO(mdempsky): Fix. - logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn), - fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation) - } - } - l.leakTo(root, derefs) - } - - // If l's address flows somewhere that - // outlives it, then l needs to be heap - // allocated. - if addressOf && !l.escapes { - if logopt.Enabled() || base.Flag.LowerM >= 2 { - if base.Flag.LowerM >= 2 { - fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n) - } - explanation := b.explainPath(root, l) - if logopt.Enabled() { - var e_curfn *ir.Func // TODO(mdempsky): Fix. - logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation) - } - } - l.escapes = true - enqueue(l) - continue - } - } - - for i, edge := range l.edges { - if edge.src.escapes { - continue - } - d := derefs + edge.derefs - if edge.src.walkgen != walkgen || edge.src.derefs > d { - edge.src.walkgen = walkgen - edge.src.derefs = d - edge.src.dst = l - edge.src.dstEdgeIdx = i - todo = append(todo, edge.src) - } - } - } -} - -// explainPath prints an explanation of how src flows to the walk root. -func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt { - visited := make(map[*location]bool) - pos := base.FmtPos(src.n.Pos()) - var explanation []*logopt.LoggedOpt - for { - // Prevent infinite loop. - if visited[src] { - if base.Flag.LowerM >= 2 { - fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos) - } - break - } - visited[src] = true - dst := src.dst - edge := &dst.edges[src.dstEdgeIdx] - if edge.src != src { - base.Fatalf("path inconsistency: %v != %v", edge.src, src) - } - - explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation) - - if dst == root { - break - } - src = dst - } - - return explanation -} - -func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt { - ops := "&" - if derefs >= 0 { - ops = strings.Repeat("*", derefs) - } - print := base.Flag.LowerM >= 2 - - flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc)) - if print { - fmt.Printf("%s:%s\n", pos, flow) - } - if logopt.Enabled() { - var epos src.XPos - if notes != nil { - epos = notes.where.Pos() - } else if srcloc != nil && srcloc.n != nil { - epos = srcloc.n.Pos() - } - var e_curfn *ir.Func // TODO(mdempsky): Fix. - explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow)) - } - - for note := notes; note != nil; note = note.next { - if print { - fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos())) - } - if logopt.Enabled() { - var e_curfn *ir.Func // TODO(mdempsky): Fix. - explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn), - fmt.Sprintf(" from %v (%v)", note.where, note.why))) - } - } - return explanation -} - -func (b *batch) explainLoc(l *location) string { - if l == &b.heapLoc { - return "{heap}" - } - if l.n == nil { - // TODO(mdempsky): Omit entirely. - return "{temp}" - } - if l.n.Op() == ir.ONAME { - return fmt.Sprintf("%v", l.n) - } - return fmt.Sprintf("{storage for %v}", l.n) -} - -// outlives reports whether values stored in l may survive beyond -// other's lifetime if stack allocated. -func (b *batch) outlives(l, other *location) bool { - // The heap outlives everything. - if l.escapes { - return true - } - - // We don't know what callers do with returned values, so - // pessimistically we need to assume they flow to the heap and - // outlive everything too. - if l.isName(ir.PPARAMOUT) { - // Exception: Directly called closures can return - // locations allocated outside of them without forcing - // them to the heap. For example: - // - // var u int // okay to stack allocate - // *(func() *int { return &u }()) = 42 - if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() { - return false - } - - return true - } - - // If l and other are within the same function, then l - // outlives other if it was declared outside other's loop - // scope. For example: - // - // var l *int - // for { - // l = new(int) - // } - if l.curfn == other.curfn && l.loopDepth < other.loopDepth { - return true - } - - // If other is declared within a child closure of where l is - // declared, then l outlives it. For example: - // - // var l *int - // func() { - // l = new(int) - // } - if containsClosure(l.curfn, other.curfn) { - return true - } - - return false -} - -// containsClosure reports whether c is a closure contained within f. -func containsClosure(f, c *ir.Func) bool { - // Common case. - if f == c { - return false - } - - // Closures within function Foo are named like "Foo.funcN..." - // TODO(mdempsky): Better way to recognize this. - fn := f.Sym().Name - cn := c.Sym().Name - return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.' -} - -// leak records that parameter l leaks to sink. -func (l *location) leakTo(sink *location, derefs int) { - // If sink is a result parameter that doesn't escape (#44614) - // and we can fit return bits into the escape analysis tag, - // then record as a result leak. - if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn { - ri := sink.resultIndex - 1 - if ri < numEscResults { - // Leak to result parameter. - l.paramEsc.AddResult(ri, derefs) - return - } - } - - // Otherwise, record as heap leak. - l.paramEsc.AddHeap(derefs) -} - -func (b *batch) finish(fns []*ir.Func) { - // Record parameter tags for package export data. - for _, fn := range fns { - fn.SetEsc(escFuncTagged) - - narg := 0 - for _, fs := range &types.RecvsParams { - for _, f := range fs(fn.Type()).Fields().Slice() { - narg++ - f.Note = b.paramTag(fn, narg, f) - } - } - } - - for _, loc := range b.allLocs { - n := loc.n - if n == nil { - continue - } - if n.Op() == ir.ONAME { - n := n.(*ir.Name) - n.Opt = nil - } - - // Update n.Esc based on escape analysis results. - - if loc.escapes { - if n.Op() == ir.ONAME { - if base.Flag.CompilingRuntime { - base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n) - } - if base.Flag.LowerM != 0 { - base.WarnfAt(n.Pos(), "moved to heap: %v", n) - } - } else { - if base.Flag.LowerM != 0 { - base.WarnfAt(n.Pos(), "%v escapes to heap", n) - } - if logopt.Enabled() { - var e_curfn *ir.Func // TODO(mdempsky): Fix. - logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn)) - } - } - n.SetEsc(ir.EscHeap) - } else { - if base.Flag.LowerM != 0 && n.Op() != ir.ONAME { - base.WarnfAt(n.Pos(), "%v does not escape", n) - } - n.SetEsc(ir.EscNone) - if loc.transient { - switch n.Op() { - case ir.OCLOSURE: - n := n.(*ir.ClosureExpr) - n.SetTransient(true) - case ir.OCALLPART: - n := n.(*ir.SelectorExpr) - n.SetTransient(true) - case ir.OSLICELIT: - n := n.(*ir.CompLitExpr) - n.SetTransient(true) - } - } - } - } -} - -func (l *location) isName(c ir.Class) bool { - return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c -} - -const numEscResults = 7 - -// An leaks represents a set of assignment flows from a parameter -// to the heap or to any of its function's (first numEscResults) -// result parameters. -type leaks [1 + numEscResults]uint8 - -// Empty reports whether l is an empty set (i.e., no assignment flows). -func (l leaks) Empty() bool { return l == leaks{} } - -// Heap returns the minimum deref count of any assignment flow from l -// to the heap. If no such flows exist, Heap returns -1. -func (l leaks) Heap() int { return l.get(0) } - -// Result returns the minimum deref count of any assignment flow from -// l to its function's i'th result parameter. If no such flows exist, -// Result returns -1. -func (l leaks) Result(i int) int { return l.get(1 + i) } - -// AddHeap adds an assignment flow from l to the heap. -func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) } - -// AddResult adds an assignment flow from l to its function's i'th -// result parameter. -func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) } - -func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) } - -func (l leaks) get(i int) int { return int(l[i]) - 1 } - -func (l *leaks) add(i, derefs int) { - if old := l.get(i); old < 0 || derefs < old { - l.set(i, derefs) - } -} - -func (l *leaks) set(i, derefs int) { - v := derefs + 1 - if v < 0 { - base.Fatalf("invalid derefs count: %v", derefs) - } - if v > math.MaxUint8 { - v = math.MaxUint8 - } - - l[i] = uint8(v) -} - -// Optimize removes result flow paths that are equal in length or -// longer than the shortest heap flow path. -func (l *leaks) Optimize() { - // If we have a path to the heap, then there's no use in - // keeping equal or longer paths elsewhere. - if x := l.Heap(); x >= 0 { - for i := 0; i < numEscResults; i++ { - if l.Result(i) >= x { - l.setResult(i, -1) - } - } - } -} - -var leakTagCache = map[leaks]string{} - -// Encode converts l into a binary string for export data. -func (l leaks) Encode() string { - if l.Heap() == 0 { - // Space optimization: empty string encodes more - // efficiently in export data. - return "" - } - if s, ok := leakTagCache[l]; ok { - return s - } - - n := len(l) - for n > 0 && l[n-1] == 0 { - n-- - } - s := "esc:" + string(l[:n]) - leakTagCache[l] = s - return s -} - -// parseLeaks parses a binary string representing a leaks -func parseLeaks(s string) leaks { - var l leaks - if !strings.HasPrefix(s, "esc:") { - l.AddHeap(0) - return l - } - copy(l[:], s[4:]) - return l -} - -func Funcs(all []ir.Node) { - ir.VisitFuncsBottomUp(all, Batch) -} - -const ( - escFuncUnknown = 0 + iota - escFuncPlanned - escFuncStarted - escFuncTagged -) - -// Mark labels that have no backjumps to them as not increasing e.loopdepth. -type labelState int - -const ( - looping labelState = 1 + iota - nonlooping -) - -func isSliceSelfAssign(dst, src ir.Node) bool { - // Detect the following special case. - // - // func (b *Buffer) Foo() { - // n, m := ... - // b.buf = b.buf[n:m] - // } - // - // This assignment is a no-op for escape analysis, - // it does not store any new pointers into b that were not already there. - // However, without this special case b will escape, because we assign to OIND/ODOTPTR. - // Here we assume that the statement will not contain calls, - // that is, that order will move any calls to init. - // Otherwise base ONAME value could change between the moments - // when we evaluate it for dst and for src. - - // dst is ONAME dereference. - var dstX ir.Node - switch dst.Op() { - default: - return false - case ir.ODEREF: - dst := dst.(*ir.StarExpr) - dstX = dst.X - case ir.ODOTPTR: - dst := dst.(*ir.SelectorExpr) - dstX = dst.X - } - if dstX.Op() != ir.ONAME { - return false - } - // src is a slice operation. - switch src.Op() { - case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR: - // OK. - case ir.OSLICEARR, ir.OSLICE3ARR: - // Since arrays are embedded into containing object, - // slice of non-pointer array will introduce a new pointer into b that was not already there - // (pointer to b itself). After such assignment, if b contents escape, - // b escapes as well. If we ignore such OSLICEARR, we will conclude - // that b does not escape when b contents do. - // - // Pointer to an array is OK since it's not stored inside b directly. - // For slicing an array (not pointer to array), there is an implicit OADDR. - // We check that to determine non-pointer array slicing. - src := src.(*ir.SliceExpr) - if src.X.Op() == ir.OADDR { - return false - } - default: - return false - } - // slice is applied to ONAME dereference. - var baseX ir.Node - switch base := src.(*ir.SliceExpr).X; base.Op() { - default: - return false - case ir.ODEREF: - base := base.(*ir.StarExpr) - baseX = base.X - case ir.ODOTPTR: - base := base.(*ir.SelectorExpr) - baseX = base.X - } - if baseX.Op() != ir.ONAME { - return false - } - // dst and src reference the same base ONAME. - return dstX.(*ir.Name) == baseX.(*ir.Name) -} - -// isSelfAssign reports whether assignment from src to dst can -// be ignored by the escape analysis as it's effectively a self-assignment. -func isSelfAssign(dst, src ir.Node) bool { - if isSliceSelfAssign(dst, src) { - return true - } - - // Detect trivial assignments that assign back to the same object. - // - // It covers these cases: - // val.x = val.y - // val.x[i] = val.y[j] - // val.x1.x2 = val.x1.y2 - // ... etc - // - // These assignments do not change assigned object lifetime. - - if dst == nil || src == nil || dst.Op() != src.Op() { - return false - } - - // The expression prefix must be both "safe" and identical. - switch dst.Op() { - case ir.ODOT, ir.ODOTPTR: - // Safe trailing accessors that are permitted to differ. - dst := dst.(*ir.SelectorExpr) - src := src.(*ir.SelectorExpr) - return ir.SameSafeExpr(dst.X, src.X) - case ir.OINDEX: - dst := dst.(*ir.IndexExpr) - src := src.(*ir.IndexExpr) - if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) { - return false - } - return ir.SameSafeExpr(dst.X, src.X) - default: - return false - } -} - -// mayAffectMemory reports whether evaluation of n may affect the program's -// memory state. If the expression can't affect memory state, then it can be -// safely ignored by the escape analysis. -func mayAffectMemory(n ir.Node) bool { - // We may want to use a list of "memory safe" ops instead of generally - // "side-effect free", which would include all calls and other ops that can - // allocate or change global state. For now, it's safer to start with the latter. - // - // We're ignoring things like division by zero, index out of range, - // and nil pointer dereference here. - - // TODO(rsc): It seems like it should be possible to replace this with - // an ir.Any looking for any op that's not the ones in the case statement. - // But that produces changes in the compiled output detected by buildall. - switch n.Op() { - case ir.ONAME, ir.OLITERAL, ir.ONIL: - return false - - case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD: - n := n.(*ir.BinaryExpr) - return mayAffectMemory(n.X) || mayAffectMemory(n.Y) - - case ir.OINDEX: - n := n.(*ir.IndexExpr) - return mayAffectMemory(n.X) || mayAffectMemory(n.Index) - - case ir.OCONVNOP, ir.OCONV: - n := n.(*ir.ConvExpr) - return mayAffectMemory(n.X) - - case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: - n := n.(*ir.UnaryExpr) - return mayAffectMemory(n.X) - - case ir.ODOT, ir.ODOTPTR: - n := n.(*ir.SelectorExpr) - return mayAffectMemory(n.X) - - case ir.ODEREF: - n := n.(*ir.StarExpr) - return mayAffectMemory(n.X) - - default: - return true - } -} - -// HeapAllocReason returns the reason the given Node must be heap -// allocated, or the empty string if it doesn't. -func HeapAllocReason(n ir.Node) string { - if n == nil || n.Type() == nil { - return "" - } - - // Parameters are always passed via the stack. - if n.Op() == ir.ONAME { - n := n.(*ir.Name) - if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { - return "" - } - } - - if n.Type().Width > ir.MaxStackVarSize { - return "too large for stack" - } - - if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize { - return "too large for stack" - } - - if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize { - return "too large for stack" - } - if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize { - return "too large for stack" - } - - if n.Op() == ir.OMAKESLICE { - n := n.(*ir.MakeExpr) - r := n.Cap - if r == nil { - r = n.Len - } - if !ir.IsSmallIntConst(r) { - return "non-constant size" - } - if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width { - return "too large for stack" - } - } - - return "" -} - -// This special tag is applied to uintptr variables -// that we believe may hold unsafe.Pointers for -// calls into assembly functions. -const UnsafeUintptrNote = "unsafe-uintptr" - -// This special tag is applied to uintptr parameters of functions -// marked go:uintptrescapes. -const UintptrEscapesNote = "uintptr-escapes" - -func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string { - name := func() string { - if f.Sym != nil { - return f.Sym.Name - } - return fmt.Sprintf("arg#%d", narg) - } - - if len(fn.Body) == 0 { - // Assume that uintptr arguments must be held live across the call. - // This is most important for syscall.Syscall. - // See golang.org/issue/13372. - // This really doesn't have much to do with escape analysis per se, - // but we are reusing the ability to annotate an individual function - // argument and pass those annotations along to importing code. - if f.Type.IsUintptr() { - if base.Flag.LowerM != 0 { - base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name()) - } - return UnsafeUintptrNote - } - - if !f.Type.HasPointers() { // don't bother tagging for scalars - return "" - } - - var esc leaks - - // External functions are assumed unsafe, unless - // //go:noescape is given before the declaration. - if fn.Pragma&ir.Noescape != 0 { - if base.Flag.LowerM != 0 && f.Sym != nil { - base.WarnfAt(f.Pos, "%v does not escape", name()) - } - } else { - if base.Flag.LowerM != 0 && f.Sym != nil { - base.WarnfAt(f.Pos, "leaking param: %v", name()) - } - esc.AddHeap(0) - } - - return esc.Encode() - } - - if fn.Pragma&ir.UintptrEscapes != 0 { - if f.Type.IsUintptr() { - if base.Flag.LowerM != 0 { - base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name()) - } - return UintptrEscapesNote + if fn.Pragma&ir.UintptrEscapes != 0 { + if f.Type.IsUintptr() { + if diagnose { + base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name()) + } + return "" } if f.IsDDD() && f.Type.Elem().IsUintptr() { // final argument is ...uintptr. - if base.Flag.LowerM != 0 { + if diagnose { base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name()) } - return UintptrEscapesNote + return "" } } @@ -2125,7 +452,7 @@ func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string { esc := loc.paramEsc esc.Optimize() - if base.Flag.LowerM != 0 && !loc.escapes { + if diagnose && !loc.escapes { if esc.Empty() { base.WarnfAt(f.Pos, "%v does not escape", name()) } diff --git a/src/cmd/compile/internal/escape/expr.go b/src/cmd/compile/internal/escape/expr.go new file mode 100644 index 00000000000000..fd758bbf208183 --- /dev/null +++ b/src/cmd/compile/internal/escape/expr.go @@ -0,0 +1,337 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" +) + +// expr models evaluating an expression n and flowing the result into +// hole k. +func (e *escape) expr(k hole, n ir.Node) { + if n == nil { + return + } + e.stmts(n.Init()) + e.exprSkipInit(k, n) +} + +func (e *escape) exprSkipInit(k hole, n ir.Node) { + if n == nil { + return + } + + lno := ir.SetPos(n) + defer func() { + base.Pos = lno + }() + + if k.derefs >= 0 && !n.Type().IsUntyped() && !n.Type().HasPointers() { + k.dst = &e.blankLoc + } + + switch n.Op() { + default: + base.Fatalf("unexpected expr: %s %v", n.Op().String(), n) + + case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET: + // nop + + case ir.ONAME: + n := n.(*ir.Name) + if n.Class == ir.PFUNC || n.Class == ir.PEXTERN { + return + } + e.flow(k, e.oldLoc(n)) + + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT: + n := n.(*ir.UnaryExpr) + e.discard(n.X) + case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + n := n.(*ir.BinaryExpr) + e.discard(n.X) + e.discard(n.Y) + case ir.OANDAND, ir.OOROR: + n := n.(*ir.LogicalExpr) + e.discard(n.X) + e.discard(n.Y) + case ir.OADDR: + n := n.(*ir.AddrExpr) + e.expr(k.addr(n, "address-of"), n.X) // "address-of" + case ir.ODEREF: + n := n.(*ir.StarExpr) + e.expr(k.deref(n, "indirection"), n.X) // "indirection" + case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER: + n := n.(*ir.SelectorExpr) + e.expr(k.note(n, "dot"), n.X) + case ir.ODOTPTR: + n := n.(*ir.SelectorExpr) + e.expr(k.deref(n, "dot of pointer"), n.X) // "dot of pointer" + case ir.ODOTTYPE, ir.ODOTTYPE2: + n := n.(*ir.TypeAssertExpr) + e.expr(k.dotType(n.Type(), n, "dot"), n.X) + case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: + n := n.(*ir.DynamicTypeAssertExpr) + e.expr(k.dotType(n.Type(), n, "dot"), n.X) + // n.T doesn't need to be tracked; it always points to read-only storage. + case ir.OINDEX: + n := n.(*ir.IndexExpr) + if n.X.Type().IsArray() { + e.expr(k.note(n, "fixed-array-index-of"), n.X) + } else { + // TODO(mdempsky): Fix why reason text. + e.expr(k.deref(n, "dot of pointer"), n.X) + } + e.discard(n.Index) + case ir.OINDEXMAP: + n := n.(*ir.IndexExpr) + e.discard(n.X) + e.discard(n.Index) + case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR: + n := n.(*ir.SliceExpr) + e.expr(k.note(n, "slice"), n.X) + e.discard(n.Low) + e.discard(n.High) + e.discard(n.Max) + + case ir.OCONV, ir.OCONVNOP: + n := n.(*ir.ConvExpr) + if (ir.ShouldCheckPtr(e.curfn, 2) || ir.ShouldAsanCheckPtr(e.curfn)) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() { + // When -d=checkptr=2 or -asan is enabled, + // treat conversions to unsafe.Pointer as an + // escaping operation. This allows better + // runtime instrumentation, since we can more + // easily detect object boundaries on the heap + // than the stack. + e.assignHeap(n.X, "conversion to unsafe.Pointer", n) + } else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { + e.unsafeValue(k, n.X) + } else { + e.expr(k, n.X) + } + case ir.OCONVIFACE, ir.OCONVIDATA: + n := n.(*ir.ConvExpr) + if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) { + k = e.spill(k, n) + } + e.expr(k.note(n, "interface-converted"), n.X) + case ir.OEFACE: + n := n.(*ir.BinaryExpr) + // Note: n.X is not needed because it can never point to memory that might escape. + e.expr(k, n.Y) + case ir.OITAB, ir.OIDATA, ir.OSPTR: + n := n.(*ir.UnaryExpr) + e.expr(k, n.X) + case ir.OSLICE2ARRPTR: + // the slice pointer flows directly to the result + n := n.(*ir.ConvExpr) + e.expr(k, n.X) + case ir.ORECV: + n := n.(*ir.UnaryExpr) + e.discard(n.X) + + case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL, + ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER, + ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + e.call([]hole{k}, n) + + case ir.ONEW: + n := n.(*ir.UnaryExpr) + e.spill(k, n) + + case ir.OMAKESLICE: + n := n.(*ir.MakeExpr) + e.spill(k, n) + e.discard(n.Len) + e.discard(n.Cap) + case ir.OMAKECHAN: + n := n.(*ir.MakeExpr) + e.discard(n.Len) + case ir.OMAKEMAP: + n := n.(*ir.MakeExpr) + e.spill(k, n) + e.discard(n.Len) + + case ir.OMETHVALUE: + // Flow the receiver argument to both the closure and + // to the receiver parameter. + + n := n.(*ir.SelectorExpr) + closureK := e.spill(k, n) + + m := n.Selection + + // We don't know how the method value will be called + // later, so conservatively assume the result + // parameters all flow to the heap. + // + // TODO(mdempsky): Change ks into a callback, so that + // we don't have to create this slice? + var ks []hole + for i := m.Type.NumResults(); i > 0; i-- { + ks = append(ks, e.heapHole()) + } + name, _ := m.Nname.(*ir.Name) + paramK := e.tagHole(ks, name, m.Type.Recv()) + + e.expr(e.teeHole(paramK, closureK), n.X) + + case ir.OPTRLIT: + n := n.(*ir.AddrExpr) + e.expr(e.spill(k, n), n.X) + + case ir.OARRAYLIT: + n := n.(*ir.CompLitExpr) + for _, elt := range n.List { + if elt.Op() == ir.OKEY { + elt = elt.(*ir.KeyExpr).Value + } + e.expr(k.note(n, "array literal element"), elt) + } + + case ir.OSLICELIT: + n := n.(*ir.CompLitExpr) + k = e.spill(k, n) + + for _, elt := range n.List { + if elt.Op() == ir.OKEY { + elt = elt.(*ir.KeyExpr).Value + } + e.expr(k.note(n, "slice-literal-element"), elt) + } + + case ir.OSTRUCTLIT: + n := n.(*ir.CompLitExpr) + for _, elt := range n.List { + e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value) + } + + case ir.OMAPLIT: + n := n.(*ir.CompLitExpr) + e.spill(k, n) + + // Map keys and values are always stored in the heap. + for _, elt := range n.List { + elt := elt.(*ir.KeyExpr) + e.assignHeap(elt.Key, "map literal key", n) + e.assignHeap(elt.Value, "map literal value", n) + } + + case ir.OCLOSURE: + n := n.(*ir.ClosureExpr) + k = e.spill(k, n) + e.closures = append(e.closures, closure{k, n}) + + if fn := n.Func; fn.IsHiddenClosure() { + for _, cv := range fn.ClosureVars { + if loc := e.oldLoc(cv); !loc.captured { + loc.captured = true + + // Ignore reassignments to the variable in straightline code + // preceding the first capture by a closure. + if loc.loopDepth == e.loopDepth { + loc.reassigned = false + } + } + } + + for _, n := range fn.Dcl { + // Add locations for local variables of the + // closure, if needed, in case we're not including + // the closure func in the batch for escape + // analysis (happens for escape analysis called + // from reflectdata.methodWrapper) + if n.Op() == ir.ONAME && n.Opt == nil { + e.with(fn).newLoc(n, false) + } + } + e.walkFunc(fn) + } + + case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR: + n := n.(*ir.ConvExpr) + e.spill(k, n) + e.discard(n.X) + + case ir.OADDSTR: + n := n.(*ir.AddStringExpr) + e.spill(k, n) + + // Arguments of OADDSTR never escape; + // runtime.concatstrings makes sure of that. + e.discards(n.List) + + case ir.ODYNAMICTYPE: + // Nothing to do - argument is a *runtime._type (+ maybe a *runtime.itab) pointing to static data section + } +} + +// unsafeValue evaluates a uintptr-typed arithmetic expression looking +// for conversions from an unsafe.Pointer. +func (e *escape) unsafeValue(k hole, n ir.Node) { + if n.Type().Kind() != types.TUINTPTR { + base.Fatalf("unexpected type %v for %v", n.Type(), n) + } + if k.addrtaken { + base.Fatalf("unexpected addrtaken") + } + + e.stmts(n.Init()) + + switch n.Op() { + case ir.OCONV, ir.OCONVNOP: + n := n.(*ir.ConvExpr) + if n.X.Type().IsUnsafePtr() { + e.expr(k, n.X) + } else { + e.discard(n.X) + } + case ir.ODOTPTR: + n := n.(*ir.SelectorExpr) + if ir.IsReflectHeaderDataField(n) { + e.expr(k.deref(n, "reflect.Header.Data"), n.X) + } else { + e.discard(n.X) + } + case ir.OPLUS, ir.ONEG, ir.OBITNOT: + n := n.(*ir.UnaryExpr) + e.unsafeValue(k, n.X) + case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT: + n := n.(*ir.BinaryExpr) + e.unsafeValue(k, n.X) + e.unsafeValue(k, n.Y) + case ir.OLSH, ir.ORSH: + n := n.(*ir.BinaryExpr) + e.unsafeValue(k, n.X) + // RHS need not be uintptr-typed (#32959) and can't meaningfully + // flow pointers anyway. + e.discard(n.Y) + default: + e.exprSkipInit(e.discardHole(), n) + } +} + +// discard evaluates an expression n for side-effects, but discards +// its value. +func (e *escape) discard(n ir.Node) { + e.expr(e.discardHole(), n) +} + +func (e *escape) discards(l ir.Nodes) { + for _, n := range l { + e.discard(n) + } +} + +// spill allocates a new location associated with expression n, flows +// its address to k, and returns a hole that flows values to it. It's +// intended for use with most expressions that allocate storage. +func (e *escape) spill(k hole, n ir.Node) hole { + loc := e.newLoc(n, true) + e.flow(k.addr(n, "spill"), loc) + return loc.asHole() +} diff --git a/src/cmd/compile/internal/escape/graph.go b/src/cmd/compile/internal/escape/graph.go new file mode 100644 index 00000000000000..cc3d078adddf87 --- /dev/null +++ b/src/cmd/compile/internal/escape/graph.go @@ -0,0 +1,324 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/logopt" + "cmd/compile/internal/types" + "fmt" +) + +// Below we implement the methods for walking the AST and recording +// data flow edges. Note that because a sub-expression might have +// side-effects, it's important to always visit the entire AST. +// +// For example, write either: +// +// if x { +// e.discard(n.Left) +// } else { +// e.value(k, n.Left) +// } +// +// or +// +// if x { +// k = e.discardHole() +// } +// e.value(k, n.Left) +// +// Do NOT write: +// +// // BAD: possibly loses side-effects within n.Left +// if !x { +// e.value(k, n.Left) +// } + +// An location represents an abstract location that stores a Go +// variable. +type location struct { + n ir.Node // represented variable or expression, if any + curfn *ir.Func // enclosing function + edges []edge // incoming edges + loopDepth int // loopDepth at declaration + + // resultIndex records the tuple index (starting at 1) for + // PPARAMOUT variables within their function's result type. + // For non-PPARAMOUT variables it's 0. + resultIndex int + + // derefs and walkgen are used during walkOne to track the + // minimal dereferences from the walk root. + derefs int // >= -1 + walkgen uint32 + + // dst and dstEdgeindex track the next immediate assignment + // destination location during walkone, along with the index + // of the edge pointing back to this location. + dst *location + dstEdgeIdx int + + // queued is used by walkAll to track whether this location is + // in the walk queue. + queued bool + + // escapes reports whether the represented variable's address + // escapes; that is, whether the variable must be heap + // allocated. + escapes bool + + // transient reports whether the represented expression's + // address does not outlive the statement; that is, whether + // its storage can be immediately reused. + transient bool + + // paramEsc records the represented parameter's leak set. + paramEsc leaks + + captured bool // has a closure captured this variable? + reassigned bool // has this variable been reassigned? + addrtaken bool // has this variable's address been taken? +} + +// An edge represents an assignment edge between two Go variables. +type edge struct { + src *location + derefs int // >= -1 + notes *note +} + +func (l *location) asHole() hole { + return hole{dst: l} +} + +// leak records that parameter l leaks to sink. +func (l *location) leakTo(sink *location, derefs int) { + // If sink is a result parameter that doesn't escape (#44614) + // and we can fit return bits into the escape analysis tag, + // then record as a result leak. + if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn { + ri := sink.resultIndex - 1 + if ri < numEscResults { + // Leak to result parameter. + l.paramEsc.AddResult(ri, derefs) + return + } + } + + // Otherwise, record as heap leak. + l.paramEsc.AddHeap(derefs) +} + +func (l *location) isName(c ir.Class) bool { + return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c +} + +// A hole represents a context for evaluation of a Go +// expression. E.g., when evaluating p in "x = **p", we'd have a hole +// with dst==x and derefs==2. +type hole struct { + dst *location + derefs int // >= -1 + notes *note + + // addrtaken indicates whether this context is taking the address of + // the expression, independent of whether the address will actually + // be stored into a variable. + addrtaken bool +} + +type note struct { + next *note + where ir.Node + why string +} + +func (k hole) note(where ir.Node, why string) hole { + if where == nil || why == "" { + base.Fatalf("note: missing where/why") + } + if base.Flag.LowerM >= 2 || logopt.Enabled() { + k.notes = ¬e{ + next: k.notes, + where: where, + why: why, + } + } + return k +} + +func (k hole) shift(delta int) hole { + k.derefs += delta + if k.derefs < -1 { + base.Fatalf("derefs underflow: %v", k.derefs) + } + k.addrtaken = delta < 0 + return k +} + +func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) } +func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) } + +func (k hole) dotType(t *types.Type, where ir.Node, why string) hole { + if !t.IsInterface() && !types.IsDirectIface(t) { + k = k.shift(1) + } + return k.note(where, why) +} + +func (b *batch) flow(k hole, src *location) { + if k.addrtaken { + src.addrtaken = true + } + + dst := k.dst + if dst == &b.blankLoc { + return + } + if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ... + return + } + if dst.escapes && k.derefs < 0 { // dst = &src + if base.Flag.LowerM >= 2 || logopt.Enabled() { + pos := base.FmtPos(src.n.Pos()) + if base.Flag.LowerM >= 2 { + fmt.Printf("%s: %v escapes to heap:\n", pos, src.n) + } + explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{}) + if logopt.Enabled() { + var e_curfn *ir.Func // TODO(mdempsky): Fix. + logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation) + } + + } + src.escapes = true + return + } + + // TODO(mdempsky): Deduplicate edges? + dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes}) +} + +func (b *batch) heapHole() hole { return b.heapLoc.asHole() } +func (b *batch) discardHole() hole { return b.blankLoc.asHole() } + +func (b *batch) oldLoc(n *ir.Name) *location { + if n.Canonical().Opt == nil { + base.Fatalf("%v has no location", n) + } + return n.Canonical().Opt.(*location) +} + +func (e *escape) newLoc(n ir.Node, transient bool) *location { + if e.curfn == nil { + base.Fatalf("e.curfn isn't set") + } + if n != nil && n.Type() != nil && n.Type().NotInHeap() { + base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type()) + } + + if n != nil && n.Op() == ir.ONAME { + if canon := n.(*ir.Name).Canonical(); n != canon { + base.Fatalf("newLoc on non-canonical %v (canonical is %v)", n, canon) + } + } + loc := &location{ + n: n, + curfn: e.curfn, + loopDepth: e.loopDepth, + transient: transient, + } + e.allLocs = append(e.allLocs, loc) + if n != nil { + if n.Op() == ir.ONAME { + n := n.(*ir.Name) + if n.Class == ir.PPARAM && n.Curfn == nil { + // ok; hidden parameter + } else if n.Curfn != e.curfn { + base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n) + } + + if n.Opt != nil { + base.Fatalf("%v already has a location", n) + } + n.Opt = loc + } + } + return loc +} + +// teeHole returns a new hole that flows into each hole of ks, +// similar to the Unix tee(1) command. +func (e *escape) teeHole(ks ...hole) hole { + if len(ks) == 0 { + return e.discardHole() + } + if len(ks) == 1 { + return ks[0] + } + // TODO(mdempsky): Optimize if there's only one non-discard hole? + + // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a + // new temporary location ltmp, wire it into place, and return + // a hole for "ltmp = _". + loc := e.newLoc(nil, true) + for _, k := range ks { + // N.B., "p = &q" and "p = &tmp; tmp = q" are not + // semantically equivalent. To combine holes like "l1 + // = _" and "l2 = &_", we'd need to wire them as "l1 = + // *ltmp" and "l2 = ltmp" and return "ltmp = &_" + // instead. + if k.derefs < 0 { + base.Fatalf("teeHole: negative derefs") + } + + e.flow(k, loc) + } + return loc.asHole() +} + +// later returns a new hole that flows into k, but some time later. +// Its main effect is to prevent immediate reuse of temporary +// variables introduced during Order. +func (e *escape) later(k hole) hole { + loc := e.newLoc(nil, false) + e.flow(k, loc) + return loc.asHole() +} + +// Fmt is called from node printing to print information about escape analysis results. +func Fmt(n ir.Node) string { + text := "" + switch n.Esc() { + case ir.EscUnknown: + break + + case ir.EscHeap: + text = "esc(h)" + + case ir.EscNone: + text = "esc(no)" + + case ir.EscNever: + text = "esc(N)" + + default: + text = fmt.Sprintf("esc(%d)", n.Esc()) + } + + if n.Op() == ir.ONAME { + n := n.(*ir.Name) + if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 { + if text != "" { + text += " " + } + text += fmt.Sprintf("ld(%d)", loc.loopDepth) + } + } + + return text +} diff --git a/src/cmd/compile/internal/escape/leaks.go b/src/cmd/compile/internal/escape/leaks.go new file mode 100644 index 00000000000000..4c848a5ee7859d --- /dev/null +++ b/src/cmd/compile/internal/escape/leaks.go @@ -0,0 +1,106 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "math" + "strings" +) + +const numEscResults = 7 + +// An leaks represents a set of assignment flows from a parameter +// to the heap or to any of its function's (first numEscResults) +// result parameters. +type leaks [1 + numEscResults]uint8 + +// Empty reports whether l is an empty set (i.e., no assignment flows). +func (l leaks) Empty() bool { return l == leaks{} } + +// Heap returns the minimum deref count of any assignment flow from l +// to the heap. If no such flows exist, Heap returns -1. +func (l leaks) Heap() int { return l.get(0) } + +// Result returns the minimum deref count of any assignment flow from +// l to its function's i'th result parameter. If no such flows exist, +// Result returns -1. +func (l leaks) Result(i int) int { return l.get(1 + i) } + +// AddHeap adds an assignment flow from l to the heap. +func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) } + +// AddResult adds an assignment flow from l to its function's i'th +// result parameter. +func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) } + +func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) } + +func (l leaks) get(i int) int { return int(l[i]) - 1 } + +func (l *leaks) add(i, derefs int) { + if old := l.get(i); old < 0 || derefs < old { + l.set(i, derefs) + } +} + +func (l *leaks) set(i, derefs int) { + v := derefs + 1 + if v < 0 { + base.Fatalf("invalid derefs count: %v", derefs) + } + if v > math.MaxUint8 { + v = math.MaxUint8 + } + + l[i] = uint8(v) +} + +// Optimize removes result flow paths that are equal in length or +// longer than the shortest heap flow path. +func (l *leaks) Optimize() { + // If we have a path to the heap, then there's no use in + // keeping equal or longer paths elsewhere. + if x := l.Heap(); x >= 0 { + for i := 0; i < numEscResults; i++ { + if l.Result(i) >= x { + l.setResult(i, -1) + } + } + } +} + +var leakTagCache = map[leaks]string{} + +// Encode converts l into a binary string for export data. +func (l leaks) Encode() string { + if l.Heap() == 0 { + // Space optimization: empty string encodes more + // efficiently in export data. + return "" + } + if s, ok := leakTagCache[l]; ok { + return s + } + + n := len(l) + for n > 0 && l[n-1] == 0 { + n-- + } + s := "esc:" + string(l[:n]) + leakTagCache[l] = s + return s +} + +// parseLeaks parses a binary string representing a leaks +func parseLeaks(s string) leaks { + var l leaks + if !strings.HasPrefix(s, "esc:") { + l.AddHeap(0) + return l + } + copy(l[:], s[4:]) + return l +} diff --git a/src/cmd/compile/internal/escape/solve.go b/src/cmd/compile/internal/escape/solve.go new file mode 100644 index 00000000000000..77d6b27dd75b01 --- /dev/null +++ b/src/cmd/compile/internal/escape/solve.go @@ -0,0 +1,289 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/logopt" + "cmd/internal/src" + "fmt" + "strings" +) + +// walkAll computes the minimal dereferences between all pairs of +// locations. +func (b *batch) walkAll() { + // We use a work queue to keep track of locations that we need + // to visit, and repeatedly walk until we reach a fixed point. + // + // We walk once from each location (including the heap), and + // then re-enqueue each location on its transition from + // transient->!transient and !escapes->escapes, which can each + // happen at most once. So we take Θ(len(e.allLocs)) walks. + + // LIFO queue, has enough room for e.allLocs and e.heapLoc. + todo := make([]*location, 0, len(b.allLocs)+1) + enqueue := func(loc *location) { + if !loc.queued { + todo = append(todo, loc) + loc.queued = true + } + } + + for _, loc := range b.allLocs { + enqueue(loc) + } + enqueue(&b.heapLoc) + + var walkgen uint32 + for len(todo) > 0 { + root := todo[len(todo)-1] + todo = todo[:len(todo)-1] + root.queued = false + + walkgen++ + b.walkOne(root, walkgen, enqueue) + } +} + +// walkOne computes the minimal number of dereferences from root to +// all other locations. +func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) { + // The data flow graph has negative edges (from addressing + // operations), so we use the Bellman-Ford algorithm. However, + // we don't have to worry about infinite negative cycles since + // we bound intermediate dereference counts to 0. + + root.walkgen = walkgen + root.derefs = 0 + root.dst = nil + + todo := []*location{root} // LIFO queue + for len(todo) > 0 { + l := todo[len(todo)-1] + todo = todo[:len(todo)-1] + + derefs := l.derefs + + // If l.derefs < 0, then l's address flows to root. + addressOf := derefs < 0 + if addressOf { + // For a flow path like "root = &l; l = x", + // l's address flows to root, but x's does + // not. We recognize this by lower bounding + // derefs at 0. + derefs = 0 + + // If l's address flows to a non-transient + // location, then l can't be transiently + // allocated. + if !root.transient && l.transient { + l.transient = false + enqueue(l) + } + } + + if b.outlives(root, l) { + // l's value flows to root. If l is a function + // parameter and root is the heap or a + // corresponding result parameter, then record + // that value flow for tagging the function + // later. + if l.isName(ir.PPARAM) { + if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes { + if base.Flag.LowerM >= 2 { + fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs) + } + explanation := b.explainPath(root, l) + if logopt.Enabled() { + var e_curfn *ir.Func // TODO(mdempsky): Fix. + logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn), + fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation) + } + } + l.leakTo(root, derefs) + } + + // If l's address flows somewhere that + // outlives it, then l needs to be heap + // allocated. + if addressOf && !l.escapes { + if logopt.Enabled() || base.Flag.LowerM >= 2 { + if base.Flag.LowerM >= 2 { + fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n) + } + explanation := b.explainPath(root, l) + if logopt.Enabled() { + var e_curfn *ir.Func // TODO(mdempsky): Fix. + logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation) + } + } + l.escapes = true + enqueue(l) + continue + } + } + + for i, edge := range l.edges { + if edge.src.escapes { + continue + } + d := derefs + edge.derefs + if edge.src.walkgen != walkgen || edge.src.derefs > d { + edge.src.walkgen = walkgen + edge.src.derefs = d + edge.src.dst = l + edge.src.dstEdgeIdx = i + todo = append(todo, edge.src) + } + } + } +} + +// explainPath prints an explanation of how src flows to the walk root. +func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt { + visited := make(map[*location]bool) + pos := base.FmtPos(src.n.Pos()) + var explanation []*logopt.LoggedOpt + for { + // Prevent infinite loop. + if visited[src] { + if base.Flag.LowerM >= 2 { + fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos) + } + break + } + visited[src] = true + dst := src.dst + edge := &dst.edges[src.dstEdgeIdx] + if edge.src != src { + base.Fatalf("path inconsistency: %v != %v", edge.src, src) + } + + explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation) + + if dst == root { + break + } + src = dst + } + + return explanation +} + +func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt { + ops := "&" + if derefs >= 0 { + ops = strings.Repeat("*", derefs) + } + print := base.Flag.LowerM >= 2 + + flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc)) + if print { + fmt.Printf("%s:%s\n", pos, flow) + } + if logopt.Enabled() { + var epos src.XPos + if notes != nil { + epos = notes.where.Pos() + } else if srcloc != nil && srcloc.n != nil { + epos = srcloc.n.Pos() + } + var e_curfn *ir.Func // TODO(mdempsky): Fix. + explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow)) + } + + for note := notes; note != nil; note = note.next { + if print { + fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos())) + } + if logopt.Enabled() { + var e_curfn *ir.Func // TODO(mdempsky): Fix. + explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn), + fmt.Sprintf(" from %v (%v)", note.where, note.why))) + } + } + return explanation +} + +func (b *batch) explainLoc(l *location) string { + if l == &b.heapLoc { + return "{heap}" + } + if l.n == nil { + // TODO(mdempsky): Omit entirely. + return "{temp}" + } + if l.n.Op() == ir.ONAME { + return fmt.Sprintf("%v", l.n) + } + return fmt.Sprintf("{storage for %v}", l.n) +} + +// outlives reports whether values stored in l may survive beyond +// other's lifetime if stack allocated. +func (b *batch) outlives(l, other *location) bool { + // The heap outlives everything. + if l.escapes { + return true + } + + // We don't know what callers do with returned values, so + // pessimistically we need to assume they flow to the heap and + // outlive everything too. + if l.isName(ir.PPARAMOUT) { + // Exception: Directly called closures can return + // locations allocated outside of them without forcing + // them to the heap. For example: + // + // var u int // okay to stack allocate + // *(func() *int { return &u }()) = 42 + if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() { + return false + } + + return true + } + + // If l and other are within the same function, then l + // outlives other if it was declared outside other's loop + // scope. For example: + // + // var l *int + // for { + // l = new(int) + // } + if l.curfn == other.curfn && l.loopDepth < other.loopDepth { + return true + } + + // If other is declared within a child closure of where l is + // declared, then l outlives it. For example: + // + // var l *int + // func() { + // l = new(int) + // } + if containsClosure(l.curfn, other.curfn) { + return true + } + + return false +} + +// containsClosure reports whether c is a closure contained within f. +func containsClosure(f, c *ir.Func) bool { + // Common case. + if f == c { + return false + } + + // Closures within function Foo are named like "Foo.funcN..." + // TODO(mdempsky): Better way to recognize this. + fn := f.Sym().Name + cn := c.Sym().Name + return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.' +} diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go new file mode 100644 index 00000000000000..90d4f2dedcde87 --- /dev/null +++ b/src/cmd/compile/internal/escape/stmt.go @@ -0,0 +1,215 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "fmt" +) + +// stmt evaluates a single Go statement. +func (e *escape) stmt(n ir.Node) { + if n == nil { + return + } + + lno := ir.SetPos(n) + defer func() { + base.Pos = lno + }() + + if base.Flag.LowerM > 2 { + fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n) + } + + e.stmts(n.Init()) + + switch n.Op() { + default: + base.Fatalf("unexpected stmt: %v", n) + + case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK: + // nop + + case ir.OBREAK, ir.OCONTINUE, ir.OGOTO: + // TODO(mdempsky): Handle dead code? + + case ir.OBLOCK: + n := n.(*ir.BlockStmt) + e.stmts(n.List) + + case ir.ODCL: + // Record loop depth at declaration. + n := n.(*ir.Decl) + if !ir.IsBlank(n.X) { + e.dcl(n.X) + } + + case ir.OLABEL: + n := n.(*ir.LabelStmt) + if n.Label.IsBlank() { + break + } + switch e.labels[n.Label] { + case nonlooping: + if base.Flag.LowerM > 2 { + fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n) + } + case looping: + if base.Flag.LowerM > 2 { + fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n) + } + e.loopDepth++ + default: + base.Fatalf("label missing tag") + } + delete(e.labels, n.Label) + + case ir.OIF: + n := n.(*ir.IfStmt) + e.discard(n.Cond) + e.block(n.Body) + e.block(n.Else) + + case ir.OCHECKNIL: + n := n.(*ir.UnaryExpr) + e.discard(n.X) + + case ir.OFOR: + n := n.(*ir.ForStmt) + e.loopDepth++ + e.discard(n.Cond) + e.stmt(n.Post) + e.block(n.Body) + e.loopDepth-- + + case ir.ORANGE: + // for Key, Value = range X { Body } + n := n.(*ir.RangeStmt) + + // X is evaluated outside the loop. + tmp := e.newLoc(nil, false) + e.expr(tmp.asHole(), n.X) + + e.loopDepth++ + ks := e.addrs([]ir.Node{n.Key, n.Value}) + if n.X.Type().IsArray() { + e.flow(ks[1].note(n, "range"), tmp) + } else { + e.flow(ks[1].deref(n, "range-deref"), tmp) + } + e.reassigned(ks, n) + + e.block(n.Body) + e.loopDepth-- + + case ir.OSWITCH: + n := n.(*ir.SwitchStmt) + + if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok { + var ks []hole + if guard.Tag != nil { + for _, cas := range n.Cases { + cv := cas.Var + k := e.dcl(cv) // type switch variables have no ODCL. + if cv.Type().HasPointers() { + ks = append(ks, k.dotType(cv.Type(), cas, "switch case")) + } + } + } + e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X) + } else { + e.discard(n.Tag) + } + + for _, cas := range n.Cases { + e.discards(cas.List) + e.block(cas.Body) + } + + case ir.OSELECT: + n := n.(*ir.SelectStmt) + for _, cas := range n.Cases { + e.stmt(cas.Comm) + e.block(cas.Body) + } + case ir.ORECV: + // TODO(mdempsky): Consider e.discard(n.Left). + n := n.(*ir.UnaryExpr) + e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit + case ir.OSEND: + n := n.(*ir.SendStmt) + e.discard(n.Chan) + e.assignHeap(n.Value, "send", n) + + case ir.OAS: + n := n.(*ir.AssignStmt) + e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) + case ir.OASOP: + n := n.(*ir.AssignOpStmt) + // TODO(mdempsky): Worry about OLSH/ORSH? + e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n) + case ir.OAS2: + n := n.(*ir.AssignListStmt) + e.assignList(n.Lhs, n.Rhs, "assign-pair", n) + + case ir.OAS2DOTTYPE: // v, ok = x.(type) + n := n.(*ir.AssignListStmt) + e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n) + case ir.OAS2MAPR: // v, ok = m[k] + n := n.(*ir.AssignListStmt) + e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n) + case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch + n := n.(*ir.AssignListStmt) + e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n) + + case ir.OAS2FUNC: + n := n.(*ir.AssignListStmt) + e.stmts(n.Rhs[0].Init()) + ks := e.addrs(n.Lhs) + e.call(ks, n.Rhs[0]) + e.reassigned(ks, n) + case ir.ORETURN: + n := n.(*ir.ReturnStmt) + results := e.curfn.Type().Results().FieldSlice() + dsts := make([]ir.Node, len(results)) + for i, res := range results { + dsts[i] = res.Nname.(*ir.Name) + } + e.assignList(dsts, n.Results, "return", n) + case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + e.call(nil, n) + case ir.OGO, ir.ODEFER: + n := n.(*ir.GoDeferStmt) + e.goDeferStmt(n) + + case ir.OTAILCALL: + n := n.(*ir.TailCallStmt) + e.call(nil, n.Call) + } +} + +func (e *escape) stmts(l ir.Nodes) { + for _, n := range l { + e.stmt(n) + } +} + +// block is like stmts, but preserves loopDepth. +func (e *escape) block(l ir.Nodes) { + old := e.loopDepth + e.stmts(l) + e.loopDepth = old +} + +func (e *escape) dcl(n *ir.Name) hole { + if n.Curfn != e.curfn || n.IsClosureVar() { + base.Fatalf("bad declaration of %v", n) + } + loc := e.oldLoc(n) + loc.loopDepth = e.loopDepth + return loc.asHole() +} diff --git a/src/cmd/compile/internal/escape/utils.go b/src/cmd/compile/internal/escape/utils.go new file mode 100644 index 00000000000000..b481d8e4b6479f --- /dev/null +++ b/src/cmd/compile/internal/escape/utils.go @@ -0,0 +1,222 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package escape + +import ( + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" +) + +func isSliceSelfAssign(dst, src ir.Node) bool { + // Detect the following special case. + // + // func (b *Buffer) Foo() { + // n, m := ... + // b.buf = b.buf[n:m] + // } + // + // This assignment is a no-op for escape analysis, + // it does not store any new pointers into b that were not already there. + // However, without this special case b will escape, because we assign to OIND/ODOTPTR. + // Here we assume that the statement will not contain calls, + // that is, that order will move any calls to init. + // Otherwise base ONAME value could change between the moments + // when we evaluate it for dst and for src. + + // dst is ONAME dereference. + var dstX ir.Node + switch dst.Op() { + default: + return false + case ir.ODEREF: + dst := dst.(*ir.StarExpr) + dstX = dst.X + case ir.ODOTPTR: + dst := dst.(*ir.SelectorExpr) + dstX = dst.X + } + if dstX.Op() != ir.ONAME { + return false + } + // src is a slice operation. + switch src.Op() { + case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR: + // OK. + case ir.OSLICEARR, ir.OSLICE3ARR: + // Since arrays are embedded into containing object, + // slice of non-pointer array will introduce a new pointer into b that was not already there + // (pointer to b itself). After such assignment, if b contents escape, + // b escapes as well. If we ignore such OSLICEARR, we will conclude + // that b does not escape when b contents do. + // + // Pointer to an array is OK since it's not stored inside b directly. + // For slicing an array (not pointer to array), there is an implicit OADDR. + // We check that to determine non-pointer array slicing. + src := src.(*ir.SliceExpr) + if src.X.Op() == ir.OADDR { + return false + } + default: + return false + } + // slice is applied to ONAME dereference. + var baseX ir.Node + switch base := src.(*ir.SliceExpr).X; base.Op() { + default: + return false + case ir.ODEREF: + base := base.(*ir.StarExpr) + baseX = base.X + case ir.ODOTPTR: + base := base.(*ir.SelectorExpr) + baseX = base.X + } + if baseX.Op() != ir.ONAME { + return false + } + // dst and src reference the same base ONAME. + return dstX.(*ir.Name) == baseX.(*ir.Name) +} + +// isSelfAssign reports whether assignment from src to dst can +// be ignored by the escape analysis as it's effectively a self-assignment. +func isSelfAssign(dst, src ir.Node) bool { + if isSliceSelfAssign(dst, src) { + return true + } + + // Detect trivial assignments that assign back to the same object. + // + // It covers these cases: + // val.x = val.y + // val.x[i] = val.y[j] + // val.x1.x2 = val.x1.y2 + // ... etc + // + // These assignments do not change assigned object lifetime. + + if dst == nil || src == nil || dst.Op() != src.Op() { + return false + } + + // The expression prefix must be both "safe" and identical. + switch dst.Op() { + case ir.ODOT, ir.ODOTPTR: + // Safe trailing accessors that are permitted to differ. + dst := dst.(*ir.SelectorExpr) + src := src.(*ir.SelectorExpr) + return ir.SameSafeExpr(dst.X, src.X) + case ir.OINDEX: + dst := dst.(*ir.IndexExpr) + src := src.(*ir.IndexExpr) + if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) { + return false + } + return ir.SameSafeExpr(dst.X, src.X) + default: + return false + } +} + +// mayAffectMemory reports whether evaluation of n may affect the program's +// memory state. If the expression can't affect memory state, then it can be +// safely ignored by the escape analysis. +func mayAffectMemory(n ir.Node) bool { + // We may want to use a list of "memory safe" ops instead of generally + // "side-effect free", which would include all calls and other ops that can + // allocate or change global state. For now, it's safer to start with the latter. + // + // We're ignoring things like division by zero, index out of range, + // and nil pointer dereference here. + + // TODO(rsc): It seems like it should be possible to replace this with + // an ir.Any looking for any op that's not the ones in the case statement. + // But that produces changes in the compiled output detected by buildall. + switch n.Op() { + case ir.ONAME, ir.OLITERAL, ir.ONIL: + return false + + case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD: + n := n.(*ir.BinaryExpr) + return mayAffectMemory(n.X) || mayAffectMemory(n.Y) + + case ir.OINDEX: + n := n.(*ir.IndexExpr) + return mayAffectMemory(n.X) || mayAffectMemory(n.Index) + + case ir.OCONVNOP, ir.OCONV: + n := n.(*ir.ConvExpr) + return mayAffectMemory(n.X) + + case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + n := n.(*ir.UnaryExpr) + return mayAffectMemory(n.X) + + case ir.ODOT, ir.ODOTPTR: + n := n.(*ir.SelectorExpr) + return mayAffectMemory(n.X) + + case ir.ODEREF: + n := n.(*ir.StarExpr) + return mayAffectMemory(n.X) + + default: + return true + } +} + +// HeapAllocReason returns the reason the given Node must be heap +// allocated, or the empty string if it doesn't. +func HeapAllocReason(n ir.Node) string { + if n == nil || n.Type() == nil { + return "" + } + + // Parameters are always passed via the stack. + if n.Op() == ir.ONAME { + n := n.(*ir.Name) + if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { + return "" + } + } + + if n.Type().Size() > ir.MaxStackVarSize { + return "too large for stack" + } + if n.Type().Alignment() > int64(types.PtrSize) { + return "too aligned for stack" + } + + if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Size() > ir.MaxImplicitStackVarSize { + return "too large for stack" + } + if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Alignment() > int64(types.PtrSize) { + return "too aligned for stack" + } + + if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize { + return "too large for stack" + } + if n.Op() == ir.OMETHVALUE && typecheck.MethodValueType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize { + return "too large for stack" + } + + if n.Op() == ir.OMAKESLICE { + n := n.(*ir.MakeExpr) + r := n.Cap + if r == nil { + r = n.Len + } + if !ir.IsSmallIntConst(r) { + return "non-constant size" + } + if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() { + return "too large for stack" + } + } + + return "" +} diff --git a/src/cmd/compile/internal/gc/bootstrap.go b/src/cmd/compile/internal/gc/bootstrap.go deleted file mode 100644 index 37b0d59ede7a1d..00000000000000 --- a/src/cmd/compile/internal/gc/bootstrap.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.8 -// +build !go1.8 - -package gc - -import ( - "cmd/compile/internal/base" - "runtime" -) - -func startMutexProfiling() { - base.Fatalf("mutex profiling unavailable in version %v", runtime.Version()) -} diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 2137f1d1961abf..c9acfc1710f4fb 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -5,46 +5,16 @@ package gc import ( + "fmt" + "go/constant" + "cmd/compile/internal/base" - "cmd/compile/internal/inline" "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/bio" - "fmt" - "go/constant" ) -func exportf(bout *bio.Writer, format string, args ...interface{}) { - fmt.Fprintf(bout, format, args...) - if base.Debug.Export != 0 { - fmt.Printf(format, args...) - } -} - -func dumpexport(bout *bio.Writer) { - p := &exporter{marked: make(map[*types.Type]bool)} - for _, n := range typecheck.Target.Exports { - // Must catch it here rather than Export(), because the type can be - // not fully set (still TFORW) when Export() is called. - if n.Type() != nil && n.Type().HasTParam() { - base.Fatalf("Cannot (yet) export a generic type: %v", n) - } - p.markObject(n) - } - - // The linker also looks for the $$ marker - use char after $$ to distinguish format. - exportf(bout, "\n$$B\n") // indicate binary export format - off := bout.Offset() - typecheck.WriteExports(bout.Writer) - size := bout.Offset() - off - exportf(bout, "\n$$\n") - - if base.Debug.Export != 0 { - fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, size) - } -} - func dumpasmhdr() { b, err := bio.Create(base.Flag.AsmHdr) if err != nil { @@ -61,14 +31,14 @@ func dumpasmhdr() { if t == constant.Float || t == constant.Complex { break } - fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym().Name, n.Val()) + fmt.Fprintf(b, "#define const_%s %v\n", n.Sym().Name, n.Val().ExactString()) case ir.OTYPE: t := n.Type() if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() { break } - fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Width)) + fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Size())) for _, f := range t.Fields().Slice() { if !f.Sym.IsBlank() { fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym().Name, f.Sym.Name, int(f.Offset)) @@ -79,83 +49,3 @@ func dumpasmhdr() { b.Close() } - -type exporter struct { - marked map[*types.Type]bool // types already seen by markType -} - -// markObject visits a reachable object. -func (p *exporter) markObject(n ir.Node) { - if n.Op() == ir.ONAME { - n := n.(*ir.Name) - if n.Class == ir.PFUNC { - inline.Inline_Flood(n, typecheck.Export) - } - } - - p.markType(n.Type()) -} - -// markType recursively visits types reachable from t to identify -// functions whose inline bodies may be needed. -func (p *exporter) markType(t *types.Type) { - if p.marked[t] { - return - } - p.marked[t] = true - - // If this is a named type, mark all of its associated - // methods. Skip interface types because t.Methods contains - // only their unexpanded method set (i.e., exclusive of - // interface embeddings), and the switch statement below - // handles their full method set. - if t.Sym() != nil && t.Kind() != types.TINTER { - for _, m := range t.Methods().Slice() { - if types.IsExported(m.Sym.Name) { - p.markObject(ir.AsNode(m.Nname)) - } - } - } - - // Recursively mark any types that can be produced given a - // value of type t: dereferencing a pointer; indexing or - // iterating over an array, slice, or map; receiving from a - // channel; accessing a struct field or interface method; or - // calling a function. - // - // Notably, we don't mark function parameter types, because - // the user already needs some way to construct values of - // those types. - switch t.Kind() { - case types.TPTR, types.TARRAY, types.TSLICE: - p.markType(t.Elem()) - - case types.TCHAN: - if t.ChanDir().CanRecv() { - p.markType(t.Elem()) - } - - case types.TMAP: - p.markType(t.Key()) - p.markType(t.Elem()) - - case types.TSTRUCT: - for _, f := range t.FieldSlice() { - if types.IsExported(f.Sym.Name) || f.Embedded != 0 { - p.markType(f.Type) - } - } - - case types.TFUNC: - for _, f := range t.Results().FieldSlice() { - p.markType(f.Type) - } - - case types.TINTER: - for _, f := range t.AllMethods().Slice() { - if types.IsExported(f.Sym.Name) { - p.markType(f.Type) - } - } - } -} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index ce50cbb4c2e691..1929b2f6cfc2b0 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -16,6 +16,7 @@ import ( "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/noder" + "cmd/compile/internal/pgo" "cmd/compile/internal/pkginit" "cmd/compile/internal/reflectdata" "cmd/compile/internal/ssa" @@ -29,23 +30,24 @@ import ( "flag" "fmt" "internal/buildcfg" + "internal/profile" "log" "os" "runtime" ) -func hidePanic() { - if base.Debug.Panic == 0 && base.Errors() > 0 { - // If we've already complained about things - // in the program, don't bother complaining - // about a panic too; let the user clean up - // the code and try again. - if err := recover(); err != nil { - if err == "-h" { - panic(err) - } - base.ErrorExit() +// handlePanic ensures that we print out an "internal compiler error" for any panic +// or runtime exception during front-end compiler processing (unless there have +// already been some compiler errors). It may also be invoked from the explicit panic in +// hcrash(), in which case, we pass the panic on through. +func handlePanic() { + if err := recover(); err != nil { + if err == "-h" { + // Force real panic now with -h option (hcrash) - the error + // information will have already been printed. + panic(err) } + base.Fatalf("panic: %v", err) } } @@ -55,7 +57,7 @@ func hidePanic() { func Main(archInit func(*ssagen.ArchInfo)) { base.Timer.Start("fe", "init") - defer hidePanic() + defer handlePanic() archInit(&ssagen.Arch) @@ -70,20 +72,17 @@ func Main(archInit func(*ssagen.ArchInfo)) { // See bugs 31188 and 21945 (CLs 170638, 98075, 72371). base.Ctxt.UseBASEntries = base.Ctxt.Headtype != objabi.Hdarwin - types.LocalPkg = types.NewPkg("", "") - types.LocalPkg.Prefix = "\"\"" + base.DebugSSA = ssa.PhaseOption + base.ParseFlags() - // We won't know localpkg's height until after import - // processing. In the mean time, set to MaxPkgHeight to ensure - // height comparisons at least work until then. - types.LocalPkg.Height = types.MaxPkgHeight + types.LocalPkg = types.NewPkg(base.Ctxt.Pkgpath, "") // pseudo-package, for scoping types.BuiltinPkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? - types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin + types.BuiltinPkg.Prefix = "go:builtin" // pseudo-package, accessed by import "unsafe" - ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe") + types.UnsafePkg = types.NewPkg("unsafe", "unsafe") // Pseudo-package that contains the compiler's builtin // declarations for package runtime. These are declared in a @@ -95,18 +94,15 @@ func Main(archInit func(*ssagen.ArchInfo)) { // pseudo-packages used in symbol tables ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab") - ir.Pkgs.Itab.Prefix = "go.itab" // not go%2eitab + ir.Pkgs.Itab.Prefix = "go:itab" // pseudo-package used for methods with anonymous receivers ir.Pkgs.Go = types.NewPkg("go", "") - base.DebugSSA = ssa.PhaseOption - base.ParseFlags() - // Record flags that affect the build result. (And don't // record flags that don't, since that would cause spurious // changes in the binary.) - dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre") + dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre") if !base.EnableTrace && base.Flag.LowerT { log.Fatalf("compiler not built with support for -t") @@ -140,7 +136,7 @@ func Main(archInit func(*ssagen.ArchInfo)) { types.ParseLangFlag() - symABIs := ssagen.NewSymABIs(base.Ctxt.Pkgpath) + symABIs := ssagen.NewSymABIs() if base.Flag.SymABIs != "" { symABIs.ReadSymABIs(base.Flag.SymABIs) } @@ -148,20 +144,18 @@ func Main(archInit func(*ssagen.ArchInfo)) { if base.Compiling(base.NoInstrumentPkgs) { base.Flag.Race = false base.Flag.MSan = false + base.Flag.ASan = false } ssagen.Arch.LinkArch.Init(base.Ctxt) startProfile() - if base.Flag.Race || base.Flag.MSan { + if base.Flag.Race || base.Flag.MSan || base.Flag.ASan { base.Flag.Cfg.Instrumenting = true } if base.Flag.Dwarf { dwarf.EnableLogging(base.Debug.DwarfInl != 0) } if base.Debug.SoftFloat != 0 { - if buildcfg.Experiment.RegabiArgs { - log.Fatalf("softfloat mode with GOEXPERIMENT=regabiargs not implemented ") - } ssagen.Arch.SoftFloat = true } @@ -181,22 +175,39 @@ func Main(archInit func(*ssagen.ArchInfo)) { typecheck.Target = new(ir.Package) - typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) } typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock? base.AutogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0) typecheck.InitUniverse() + typecheck.InitRuntime() // Parse and typecheck input. noder.LoadPackage(flag.Args()) + // As a convenience to users (toolchain maintainers, in particular), + // when compiling a package named "main", we default the package + // path to "main" if the -p flag was not specified. + if base.Ctxt.Pkgpath == obj.UnlinkablePkg && types.LocalPkg.Name == "main" { + base.Ctxt.Pkgpath = "main" + types.LocalPkg.Path = "main" + types.LocalPkg.Prefix = "main" + } + dwarfgen.RecordPackageName() - // Build init task. - if initTask := pkginit.Task(); initTask != nil { - typecheck.Export(initTask) - } + // Prepare for backend processing. This must happen before pkginit, + // because it generates itabs for initializing global variables. + ssagen.InitConfig() + + // Create "init" function for package-scope variable initialization + // statements, if any. + // + // Note: This needs to happen early, before any optimizations. The + // Go spec defines a precise order than initialization should be + // carried out in, and even mundane optimizations like dead code + // removal can skew the results (e.g., #43444). + pkginit.MakeInit() // Eliminate some obviously dead code. // Must happen after typechecking. @@ -223,11 +234,30 @@ func Main(archInit func(*ssagen.ArchInfo)) { typecheck.AllImportedBodies() } + // Read cpu profile file and build cross-package pprof-graph and per-package weighted-call-graph. + base.Timer.Start("fe", "profileuse") + if base.Flag.ProfileUse != "" { + if pgo.PProfGraph == nil { + pgo.PProfGraph = pgo.BuildPProfGraph(base.Flag.ProfileUse, &profile.Options{ + CallTree: false, + SampleValue: func(v []int64) int64 { return v[1] }, + }) + } + pgo.WeightedCG = pgo.BuildWeightedCallGraph() + } + // Inlining base.Timer.Start("fe", "inlining") if base.Flag.LowerL != 0 { + if pgo.WeightedCG != nil { + inline.InlinePrologue() + } inline.InlinePackage() + if pgo.WeightedCG != nil { + inline.InlineEpilogue() + } } + noder.MakeWrappers(typecheck.Target) // must happen after inlining // Devirtualize. for _, n := range typecheck.Target.Decls { @@ -237,6 +267,11 @@ func Main(archInit func(*ssagen.ArchInfo)) { } ir.CurFunc = nil + // Build init task, if needed. + if initTask := pkginit.Task(); initTask != nil { + typecheck.Export(initTask) + } + // Generate ABI wrappers. Must happen before escape analysis // and doesn't benefit from dead-coding or inlining. symABIs.GenABIWrappers() @@ -252,6 +287,11 @@ func Main(archInit func(*ssagen.ArchInfo)) { base.Timer.Start("fe", "escapes") escape.Funcs(typecheck.Target.Decls) + // TODO(mdempsky): This is a hack. We need a proper, global work + // queue for scheduling function compilation so components don't + // need to adjust their behavior depending on when they're called. + reflectdata.AfterGlobalEscapeAnalysis = true + // Collect information for go:nowritebarrierrec // checking. This must happen before transforming closures during Walk // We'll do the final check after write barriers are @@ -260,17 +300,7 @@ func Main(archInit func(*ssagen.ArchInfo)) { ssagen.EnableNoWriteBarrierRecCheck() } - // Prepare for SSA compilation. - // This must be before CompileITabs, because CompileITabs - // can trigger function compilation. - typecheck.InitRuntime() - ssagen.InitConfig() - - // Just before compilation, compile itabs found on - // the right side of OCONVIFACE so that methods - // can be de-virtualized during compilation. ir.CurFunc = nil - reflectdata.CompileITabs() // Compile top level functions. // Don't use range--walk can add functions to Target.Decls. @@ -278,6 +308,10 @@ func Main(archInit func(*ssagen.ArchInfo)) { fcount := int64(0) for i := 0; i < len(typecheck.Target.Decls); i++ { if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok { + // Don't try compiling dead hidden closure. + if fn.IsDeadcodeClosure() { + continue + } enqueueFunc(fn) fcount++ } diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 474d718525f5b8..715b8ee2637e00 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -7,7 +7,9 @@ package gc import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/noder" "cmd/compile/internal/objw" + "cmd/compile/internal/pkginit" "cmd/compile/internal/reflectdata" "cmd/compile/internal/staticdata" "cmd/compile/internal/typecheck" @@ -18,6 +20,7 @@ import ( "cmd/internal/objabi" "encoding/json" "fmt" + "strings" ) // These modes say which kind of object file to generate. @@ -103,20 +106,19 @@ func finishArchiveEntry(bout *bio.Writer, start int64, name string) { func dumpCompilerObj(bout *bio.Writer) { printObjHeader(bout) - dumpexport(bout) + noder.WriteExports(bout) } func dumpdata() { numExterns := len(typecheck.Target.Externs) numDecls := len(typecheck.Target.Decls) - dumpglobls(typecheck.Target.Externs) reflectdata.CollectPTabs() numExports := len(typecheck.Target.Exports) addsignats(typecheck.Target.Externs) reflectdata.WriteRuntimeTypes() reflectdata.WriteTabs() - numPTabs, numITabs := reflectdata.CountTabs() + numPTabs := reflectdata.CountPTabs() reflectdata.WriteImportStrings() reflectdata.WriteBasicTypes() dumpembeds() @@ -146,7 +148,7 @@ func dumpdata() { dumpglobls(typecheck.Target.Externs[numExterns:]) if reflectdata.ZeroSize > 0 { - zero := base.PkgLinksym("go.map", "zero", obj.ABI0) + zero := base.PkgLinksym("go:map", "zero", obj.ABI0) objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA) zero.Set(obj.AttrStatic, true) } @@ -157,13 +159,10 @@ func dumpdata() { if numExports != len(typecheck.Target.Exports) { base.Fatalf("Target.Exports changed after compile functions loop") } - newNumPTabs, newNumITabs := reflectdata.CountTabs() + newNumPTabs := reflectdata.CountPTabs() if newNumPTabs != numPTabs { base.Fatalf("ptabs changed after compile functions loop") } - if newNumITabs != numITabs { - base.Fatalf("itabs changed after compile functions loop") - } } func dumpLinkerObj(bout *bio.Writer) { @@ -219,6 +218,10 @@ func dumpGlobalConst(n ir.Node) { if ir.ConstOverflow(v, t) { return } + } else { + // If the type of the constant is an instantiated generic, we need to emit + // that type so the linker knows about it. See issue 51245. + _ = reflectdata.TypeLinksym(t) } base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v)) } @@ -251,8 +254,7 @@ func addGCLocals() { } } if x := fn.StackObjects; x != nil { - attr := int16(obj.RODATA) - objw.Global(x, int32(len(x.P)), attr) + objw.Global(x, int32(len(x.P)), obj.RODATA) x.Set(obj.AttrStatic, true) } if x := fn.OpenCodedDeferInfo; x != nil { @@ -261,13 +263,34 @@ func addGCLocals() { if x := fn.ArgInfo; x != nil { objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) x.Set(obj.AttrStatic, true) - x.Set(obj.AttrContentAddressable, true) + } + if x := fn.ArgLiveInfo; x != nil { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + x.Set(obj.AttrStatic, true) + } + if x := fn.WrapInfo; x != nil && !x.OnList() { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + x.Set(obj.AttrStatic, true) + } + for _, jt := range fn.JumpTables { + objw.Global(jt.Sym, int32(len(jt.Targets)*base.Ctxt.Arch.PtrSize), obj.RODATA) } } } func ggloblnod(nam *ir.Name) { s := nam.Linksym() + + // main_inittask and runtime_inittask in package runtime (and in + // test/initempty.go) aren't real variable declarations, but + // linknamed variables pointing to the compiler's generated + // .inittask symbol. The real symbol was already written out in + // pkginit.Task, so we need to avoid writing them out a second time + // here, otherwise base.Ctxt.Globl will fail. + if strings.HasSuffix(s.Name, "..inittask") && s.OnList() { + return + } + s.Gotype = reflectdata.TypeLinksym(nam.Type()) flags := 0 if nam.Readonly() { @@ -276,9 +299,22 @@ func ggloblnod(nam *ir.Name) { if nam.Type() != nil && !nam.Type().HasPointers() { flags |= obj.NOPTR } - base.Ctxt.Globl(s, nam.Type().Width, flags) - if nam.LibfuzzerExtraCounter() { - s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER + size := nam.Type().Size() + linkname := nam.Sym().Linkname + name := nam.Sym().Name + + // We've skipped linkname'd globals's instrument, so we can skip them here as well. + if base.Flag.ASan && linkname == "" && pkginit.InstrumentGlobalsMap[name] != nil { + // Write the new size of instrumented global variables that have + // trailing redzones into object file. + rzSize := pkginit.GetRedzoneSizeForGlobal(size) + sizeWithRZ := rzSize + size + base.Ctxt.Globl(s, sizeWithRZ, flags) + } else { + base.Ctxt.Globl(s, size, flags) + } + if nam.Libfuzzer8BitCounter() { + s.Type = objabi.SLIBFUZZER_8BIT_COUNTER } if nam.Sym().Linkname != "" { // Make sure linkname'd symbol is non-package. When a symbol is diff --git a/src/cmd/compile/internal/gc/pprof.go b/src/cmd/compile/internal/gc/pprof.go deleted file mode 100644 index 5f9b030621cb8a..00000000000000 --- a/src/cmd/compile/internal/gc/pprof.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.8 -// +build go1.8 - -package gc - -import "runtime" - -func startMutexProfiling() { - runtime.SetMutexProfileFraction(1) -} diff --git a/src/cmd/compile/internal/gc/trace.go b/src/cmd/compile/internal/gc/trace.go deleted file mode 100644 index 8cdbd4b0f3a5da..00000000000000 --- a/src/cmd/compile/internal/gc/trace.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.7 -// +build go1.7 - -package gc - -import ( - "os" - tracepkg "runtime/trace" - - "cmd/compile/internal/base" -) - -func init() { - traceHandler = traceHandlerGo17 -} - -func traceHandlerGo17(traceprofile string) { - f, err := os.Create(traceprofile) - if err != nil { - base.Fatalf("%v", err) - } - if err := tracepkg.Start(f); err != nil { - base.Fatalf("%v", err) - } - base.AtExit(tracepkg.Stop) -} diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go index 4baddbc029a8d2..dcac0ce79ae35a 100644 --- a/src/cmd/compile/internal/gc/util.go +++ b/src/cmd/compile/internal/gc/util.go @@ -8,15 +8,11 @@ import ( "os" "runtime" "runtime/pprof" + tracepkg "runtime/trace" "cmd/compile/internal/base" ) -var ( - memprofilerate int64 - traceHandler func(string) -) - func startProfile() { if base.Flag.CPUProfile != "" { f, err := os.Create(base.Flag.CPUProfile) @@ -29,8 +25,8 @@ func startProfile() { base.AtExit(pprof.StopCPUProfile) } if base.Flag.MemProfile != "" { - if memprofilerate != 0 { - runtime.MemProfileRate = int(memprofilerate) + if base.Flag.MemProfileRate != 0 { + runtime.MemProfileRate = base.Flag.MemProfileRate } f, err := os.Create(base.Flag.MemProfile) if err != nil { @@ -67,13 +63,20 @@ func startProfile() { if err != nil { base.Fatalf("%v", err) } - startMutexProfiling() + runtime.SetMutexProfileFraction(1) base.AtExit(func() { pprof.Lookup("mutex").WriteTo(f, 0) f.Close() }) } - if base.Flag.TraceProfile != "" && traceHandler != nil { - traceHandler(base.Flag.TraceProfile) + if base.Flag.TraceProfile != "" { + f, err := os.Create(base.Flag.TraceProfile) + if err != nil { + base.Fatalf("%v", err) + } + if err := tracepkg.Start(f); err != nil { + base.Fatalf("%v", err) + } + base.AtExit(tracepkg.Stop) } } diff --git a/src/cmd/compile/internal/importer/exportdata.go b/src/cmd/compile/internal/importer/exportdata.go index 3925a64314ea0d..42fc5c9a573d04 100644 --- a/src/cmd/compile/internal/importer/exportdata.go +++ b/src/cmd/compile/internal/importer/exportdata.go @@ -1,4 +1,3 @@ -// UNREVIEWED // 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. @@ -42,7 +41,9 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { // start of the file before calling this function. The hdr result // is the string before the export data, either "$$" or "$$B". // -func FindExportData(r *bufio.Reader) (hdr string, err error) { +// If size is non-negative, it's the number of bytes of export data +// still available to read from r. +func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { @@ -53,7 +54,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { if string(line) == "!\n" { // Archive file. Scan to __.PKGDEF. var name string - if name, _, err = readGopackHeader(r); err != nil { + if name, size, err = readGopackHeader(r); err != nil { return } @@ -77,6 +78,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("not a Go object file") return } + size -= len(line) // Skip over object header to export data. // Begins after first line starting with $$. @@ -85,6 +87,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("can't find export data (%v)", err) return } + size -= len(line) } hdr = string(line) diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go index feb18cf2c9ad78..bcf0480cfc87ad 100644 --- a/src/cmd/compile/internal/importer/gcimporter.go +++ b/src/cmd/compile/internal/importer/gcimporter.go @@ -1,4 +1,3 @@ -// UNREVIEWED // 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. @@ -11,6 +10,7 @@ import ( "cmd/compile/internal/types2" "fmt" "go/build" + "internal/pkgbits" "io" "io/ioutil" "os" @@ -28,7 +28,6 @@ var pkgExts = [...]string{".a", ".o"} // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. -// func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return @@ -84,7 +83,6 @@ func FindPkg(path, srcDir string) (filename, id string) { // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. -// func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) { var rc io.ReadCloser var id string @@ -135,9 +133,9 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun } defer rc.Close() - var hdr string buf := bufio.NewReader(rc) - if hdr, err = FindExportData(buf); err != nil { + hdr, size, err := FindExportData(buf) + if err != nil { return } @@ -147,17 +145,33 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + var r io.Reader = buf + if size >= 0 { + r = io.LimitReader(r, int64(size)) + } + data, err = ioutil.ReadAll(r) if err != nil { break } + if len(data) == 0 { + err = fmt.Errorf("import %q: missing export data", path) + break + } + exportFormat := data[0] + s := string(data[1:]) + // The indexed export format starts with an 'i'; the older // binary export format starts with a 'c', 'd', or 'v' // (from "version"). Select appropriate importer. - if len(data) > 0 && data[0] == 'i' { - _, pkg, err = iImportData(packages, data[1:], id) - } else { + switch exportFormat { + case 'u': + s = s[:strings.Index(s, "\n$$\n")] + input := pkgbits.NewPkgDecoder(id, s) + pkg = ReadPackage(nil, packages, input) + case 'i': + pkg, err = ImportData(packages, s, id) + default: err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) } diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index 7fb8fed59cf9be..2fbd3f00d24a73 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -1,4 +1,3 @@ -// UNREVIEWED // 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. @@ -9,8 +8,9 @@ import ( "bytes" "cmd/compile/internal/types2" "fmt" + "go/build" + "internal/goexperiment" "internal/testenv" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -20,6 +20,11 @@ import ( "time" ) +func TestMain(m *testing.M) { + build.Default.GOROOT = testenv.GOROOT(nil) + os.Exit(m.Run()) +} + // skipSpecialPlatforms causes the test to be skipped for platforms where // builders (build.golang.org) don't have access to compiled packages for // import. @@ -39,7 +44,7 @@ func compile(t *testing.T, dirname, filename, outdirname string) string { } basename := filepath.Base(filename) outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o") - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p", strings.TrimSuffix(outname, ".o"), "-o", outname, filename) cmd.Dir = dirname out, err := cmd.CombinedOutput() if err != nil { @@ -63,8 +68,8 @@ func testPath(t *testing.T, path, srcDir string) *types2.Package { const maxTime = 30 * time.Second func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { - dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) - list, err := ioutil.ReadDir(dirname) + dirname := filepath.Join(testenv.GOROOT(t), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) + list, err := os.ReadDir(dirname) if err != nil { t.Fatalf("testDir(%s): %s", dirname, err) } @@ -92,7 +97,7 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { } func mktmpdir(t *testing.T) string { - tmpdir, err := ioutil.TempDir("", "gcimporter_test") + tmpdir, err := os.MkdirTemp("", "gcimporter_test") if err != nil { t.Fatal("mktmpdir:", err) } @@ -109,25 +114,33 @@ func TestImportTestdata(t *testing.T) { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) } - tmpdir := mktmpdir(t) - defer os.RemoveAll(tmpdir) + testfiles := map[string][]string{ + "exports.go": {"go/ast", "go/token"}, + "generics.go": nil, + } + if goexperiment.Unified { + // TODO(mdempsky): Fix test below to flatten the transitive + // Package.Imports graph. Unified IR is more precise about + // recreating the package import graph. + testfiles["exports.go"] = []string{"go/ast"} + } - compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata")) - - if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil { - // The package's Imports list must include all packages - // explicitly imported by exports.go, plus all packages - // referenced indirectly via exported objects in exports.go. - // With the textual export format, the list may also include - // additional packages that are not strictly required for - // import processing alone (they are exported to err "on - // the safe side"). - // TODO(gri) update the want list to be precise, now that - // the textual export data is gone. - got := fmt.Sprint(pkg.Imports()) - for _, want := range []string{"go/ast", "go/token"} { - if !strings.Contains(got, want) { - t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) + for testfile, wantImports := range testfiles { + tmpdir := mktmpdir(t) + defer os.RemoveAll(tmpdir) + + compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata")) + path := "./testdata/" + strings.TrimSuffix(testfile, ".go") + + if pkg := testPath(t, path, tmpdir); pkg != nil { + // The package's Imports list must include all packages + // explicitly imported by testfile, plus all packages + // referenced indirectly via exported objects in testfile. + got := fmt.Sprint(pkg.Imports()) + for _, want := range wantImports { + if !strings.Contains(got, want) { + t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want) + } } } } @@ -142,7 +155,7 @@ func TestVersionHandling(t *testing.T) { } const dir = "./testdata/versions" - list, err := ioutil.ReadDir(dir) + list, err := os.ReadDir(dir) if err != nil { t.Fatal(err) } @@ -195,7 +208,7 @@ func TestVersionHandling(t *testing.T) { // create file with corrupted export data // 1) read file - data, err := ioutil.ReadFile(filepath.Join(dir, name)) + data, err := os.ReadFile(filepath.Join(dir, name)) if err != nil { t.Fatal(err) } @@ -212,7 +225,7 @@ func TestVersionHandling(t *testing.T) { // 4) write the file pkgpath += "_corrupted" filename := filepath.Join(corruptdir, pkgpath) + ".a" - ioutil.WriteFile(filename, data, 0666) + os.WriteFile(filename, data, 0666) // test that importing the corrupted file results in an error _, err = Import(make(map[string]*types2.Package), pkgpath, corruptdir, nil) @@ -255,14 +268,13 @@ var importedObjectTests = []struct { {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"}, // interfaces - {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"}, + {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"}, {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"}, {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"}, {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"}, {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"}, - // go/types.Type has grown much larger - excluded for now - // {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, + {"go/types.Type", "type Type interface{String() string; Underlying() Type}"}, } func TestImportedTypes(t *testing.T) { @@ -318,6 +330,14 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) { return // not an interface } + // The unified IR importer always sets interface method receiver + // parameters to point to the Interface type, rather than the Named. + // See #49906. + var want types2.Type = named + if goexperiment.Unified { + want = iface + } + // check explicitly declared methods for i := 0; i < iface.NumExplicitMethods(); i++ { m := iface.ExplicitMethod(i) @@ -326,7 +346,7 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) { t.Errorf("%s: missing receiver type", m) continue } - if recv.Type() != named { + if recv.Type() != want { t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) } } @@ -457,17 +477,17 @@ func TestIssue13898(t *testing.T) { t.Fatal("go/types not found") } - // look for go/types2.Object type + // look for go/types.Object type obj := lookupObj(t, goTypesPkg.Scope(), "Object") typ, ok := obj.Type().(*types2.Named) if !ok { - t.Fatalf("go/types2.Object type is %v; wanted named type", typ) + t.Fatalf("go/types.Object type is %v; wanted named type", typ) } - // lookup go/types2.Object.Pkg method + // lookup go/types.Object.Pkg method m, index, indirect := types2.LookupFieldOrMethod(typ, false, nil, "Pkg") if m == nil { - t.Fatalf("go/types2.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) + t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) } // the method must belong to go/types diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 8ab0b7b98961fd..82aff18335472b 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2018 The Go 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,8 +8,8 @@ package importer import ( - "bytes" "cmd/compile/internal/syntax" + "cmd/compile/internal/typecheck" "cmd/compile/internal/types2" "encoding/binary" "fmt" @@ -19,10 +18,11 @@ import ( "io" "math/big" "sort" + "strings" ) type intReader struct { - *bytes.Reader + *strings.Reader path string } @@ -42,6 +42,21 @@ func (r *intReader) uint64() uint64 { return i } +// Keep this in sync with constants in iexport.go. +const ( + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + iexportVersionGenerics = 2 + iexportVersionGo1_18 = 2 + + iexportVersionCurrent = 2 +) + +type ident struct { + pkg *types2.Package + name string +} + const predeclReserved = 32 type itag uint64 @@ -57,6 +72,9 @@ const ( signatureType structType interfaceType + typeParamType + instanceType + unionType ) const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4) @@ -65,8 +83,8 @@ const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4) // and returns the number of bytes consumed and a reference to the package. // If the export data version is not recognized or the format is otherwise // compromised, an error is returned. -func iImportData(imports map[string]*types2.Package, data []byte, path string) (_ int, pkg *types2.Package, err error) { - const currentVersion = 1 +func ImportData(imports map[string]*types2.Package, data, path string) (pkg *types2.Package, err error) { + const currentVersion = iexportVersionCurrent version := int64(-1) defer func() { if e := recover(); e != nil { @@ -78,11 +96,11 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( } }() - r := &intReader{bytes.NewReader(data), path} + r := &intReader{strings.NewReader(data), path} version = int64(r.uint64()) switch version { - case currentVersion, 0: + case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: default: errorf("unknown iexport format version %d", version) } @@ -96,16 +114,20 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( r.Seek(sLen+dLen, io_SeekCurrent) p := iimporter{ - ipath: path, - version: int(version), + exportVersion: version, + ipath: path, + version: int(version), - stringData: stringData, - stringCache: make(map[uint64]string), - pkgCache: make(map[uint64]*types2.Package), + stringData: stringData, + pkgCache: make(map[uint64]*types2.Package), + posBaseCache: make(map[uint64]*syntax.PosBase), declData: declData, pkgIndex: make(map[*types2.Package]map[string]uint64), typCache: make(map[uint64]types2.Type), + // Separate map for typeparams, keyed by their package and unique + // name (name with subscript). + tparamIndex: make(map[ident]*types2.TypeParam), } for i, pt := range predeclared { @@ -117,7 +139,7 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( pkgPathOff := r.uint64() pkgPath := p.stringAt(pkgPathOff) pkgName := p.stringAt(r.uint64()) - _ = r.uint64() // package height; unused by go/types + _ = int(r.uint64()) // was package height, but not necessary anymore. if pkgPath == "" { pkgPath = path @@ -126,8 +148,10 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( if pkg == nil { pkg = types2.NewPackage(pkgPath, pkgName) imports[pkgPath] = pkg - } else if pkg.Name() != pkgName { - errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) + } else { + if pkg.Name() != pkgName { + errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) + } } p.pkgCache[pkgPathOff] = pkg @@ -153,10 +177,14 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( p.doDecl(localpkg, name) } - for _, typ := range p.interfaceList { - typ.Complete() + // SetConstraint can't be called if the constraint type is not yet complete. + // When type params are created in the 'P' case of (*importReader).obj(), + // the associated constraint type may not be complete due to recursion. + // Therefore, we defer calling SetConstraint there, and call it here instead + // after all types are complete. + for _, d := range p.later { + d.t.SetConstraint(d.constraint) } - // record all referenced packages as imports list := append(([]*types2.Package)(nil), pkgList[1:]...) sort.Sort(byPath(list)) @@ -165,23 +193,32 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) ( // package was imported completely and without errors localpkg.MarkComplete() - consumed, _ := r.Seek(0, io_SeekCurrent) - return int(consumed), localpkg, nil + return localpkg, nil +} + +type setConstraintArgs struct { + t *types2.TypeParam + constraint types2.Type } type iimporter struct { - ipath string - version int + exportVersion int64 + ipath string + version int - stringData []byte - stringCache map[uint64]string - pkgCache map[uint64]*types2.Package + stringData string + pkgCache map[uint64]*types2.Package + posBaseCache map[uint64]*syntax.PosBase - declData []byte - pkgIndex map[*types2.Package]map[string]uint64 - typCache map[uint64]types2.Type + declData string + pkgIndex map[*types2.Package]map[string]uint64 + typCache map[uint64]types2.Type + tparamIndex map[ident]*types2.TypeParam interfaceList []*types2.Interface + + // Arguments for calls to SetConstraint that are deferred due to recursive types + later []setConstraintArgs } func (p *iimporter) doDecl(pkg *types2.Package, name string) { @@ -196,27 +233,21 @@ func (p *iimporter) doDecl(pkg *types2.Package, name string) { } r := &importReader{p: p, currPkg: pkg} - // Reader.Reset is not available in Go 1.4. - // Use bytes.NewReader for now. - // r.declReader.Reset(p.declData[off:]) - r.declReader = *bytes.NewReader(p.declData[off:]) + r.declReader.Reset(p.declData[off:]) r.obj(name) } func (p *iimporter) stringAt(off uint64) string { - if s, ok := p.stringCache[off]; ok { - return s - } + var x [binary.MaxVarintLen64]byte + n := copy(x[:], p.stringData[off:]) - slen, n := binary.Uvarint(p.stringData[off:]) + slen, n := binary.Uvarint(x[:n]) if n <= 0 { errorf("varint failed") } spos := off + uint64(n) - s := string(p.stringData[spos : spos+slen]) - p.stringCache[off] = s - return s + return p.stringData[spos : spos+slen] } func (p *iimporter) pkgAt(off uint64) *types2.Package { @@ -228,8 +259,18 @@ func (p *iimporter) pkgAt(off uint64) *types2.Package { return nil } +func (p *iimporter) posBaseAt(off uint64) *syntax.PosBase { + if posBase, ok := p.posBaseCache[off]; ok { + return posBase + } + filename := p.stringAt(off) + posBase := syntax.NewTrimmedFileBase(filename, true) + p.posBaseCache[off] = posBase + return posBase +} + func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type { - if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) { + if t, ok := p.typCache[off]; ok && canReuse(base, t) { return t } @@ -238,25 +279,40 @@ func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type { } r := &importReader{p: p} - // Reader.Reset is not available in Go 1.4. - // Use bytes.NewReader for now. - // r.declReader.Reset(p.declData[off-predeclReserved:]) - r.declReader = *bytes.NewReader(p.declData[off-predeclReserved:]) + r.declReader.Reset(p.declData[off-predeclReserved:]) t := r.doType(base) - if base == nil || !isInterface(t) { + if canReuse(base, t) { p.typCache[off] = t } return t } +// canReuse reports whether the type rhs on the RHS of the declaration for def +// may be re-used. +// +// Specifically, if def is non-nil and rhs is an interface type with methods, it +// may not be re-used because we have a convention of setting the receiver type +// for interface methods to def. +func canReuse(def *types2.Named, rhs types2.Type) bool { + if def == nil { + return true + } + iface, _ := rhs.(*types2.Interface) + if iface == nil { + return true + } + // Don't use iface.Empty() here as iface may not be complete. + return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0 +} + type importReader struct { - p *iimporter - declReader bytes.Reader - currPkg *types2.Package - prevFile string - prevLine int64 - prevColumn int64 + p *iimporter + declReader strings.Reader + currPkg *types2.Package + prevPosBase *syntax.PosBase + prevLine int64 + prevColumn int64 } func (r *importReader) obj(name string) { @@ -274,17 +330,26 @@ func (r *importReader) obj(name string) { r.declare(types2.NewConst(pos, r.currPkg, name, typ, val)) - case 'F': - sig := r.signature(nil) - + case 'F', 'G': + var tparams []*types2.TypeParam + if tag == 'G' { + tparams = r.tparamList() + } + sig := r.signature(nil, nil, tparams) r.declare(types2.NewFunc(pos, r.currPkg, name, sig)) - case 'T': + case 'T', 'U': // Types can be recursive. We need to setup a stub // declaration before recursing. obj := types2.NewTypeName(pos, r.currPkg, name, nil) named := types2.NewNamed(obj, nil, nil) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + named.SetTypeParams(tparams) + } underlying := r.p.typAt(r.uint64(), named).Underlying() named.SetUnderlying(underlying) @@ -294,12 +359,61 @@ func (r *importReader) obj(name string) { mpos := r.pos() mname := r.ident() recv := r.param() - msig := r.signature(recv) + + // If the receiver has any targs, set those as the + // rparams of the method (since those are the + // typeparams being used in the method sig/body). + targs := baseType(recv.Type()).TypeArgs() + var rparams []*types2.TypeParam + if targs.Len() > 0 { + rparams = make([]*types2.TypeParam, targs.Len()) + for i := range rparams { + rparams[i], _ = targs.At(i).(*types2.TypeParam) + } + } + msig := r.signature(recv, rparams, nil) named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig)) } } + case 'P': + // We need to "declare" a typeparam in order to have a name that + // can be referenced recursively (if needed) in the type param's + // bound. + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected type param type") + } + name0 := typecheck.TparamName(name) + if name0 == "" { + errorf("malformed type parameter export name %s: missing prefix", name) + } + + tn := types2.NewTypeName(pos, r.currPkg, name0, nil) + t := types2.NewTypeParam(tn, nil) + // To handle recursive references to the typeparam within its + // bound, save the partial type in tparamIndex before reading the bounds. + id := ident{r.currPkg, name} + r.p.tparamIndex[id] = t + + var implicit bool + if r.p.exportVersion >= iexportVersionGo1_18 { + implicit = r.bool() + } + constraint := r.typ() + if implicit { + iface, _ := constraint.(*types2.Interface) + if iface == nil { + errorf("non-interface constraint marked implicit") + } + iface.MarkImplicit() + } + // The constraint type may not be complete, if we + // are in the middle of a type recursion involving type + // constraints. So, we defer SetConstraint until we have + // completely set up all types in ImportData. + r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint}) + case 'V': typ := r.typ() @@ -316,6 +430,10 @@ func (r *importReader) declare(obj types2.Object) { func (r *importReader) value() (typ types2.Type, val constant.Value) { typ = r.typ() + if r.p.exportVersion >= iexportVersionGo1_18 { + // TODO: add support for using the kind + _ = constant.Kind(r.int64()) + } switch b := typ.Underlying().(*types2.Basic); b.Info() & types2.IsConstType { case types2.IsBoolean: @@ -439,12 +557,11 @@ func (r *importReader) pos() syntax.Pos { r.posv0() } - if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 { + if (r.prevPosBase == nil || r.prevPosBase.Filename() == "") && r.prevLine == 0 && r.prevColumn == 0 { return syntax.Pos{} } - // TODO(gri) fix this - // return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn)) - return syntax.Pos{} + + return syntax.MakePos(r.prevPosBase, uint(r.prevLine), uint(r.prevColumn)) } func (r *importReader) posv0() { @@ -454,7 +571,7 @@ func (r *importReader) posv0() { } else if l := r.int64(); l == -1 { r.prevLine += deltaNewFile } else { - r.prevFile = r.string() + r.prevPosBase = r.posBase() r.prevLine = l } } @@ -466,7 +583,7 @@ func (r *importReader) posv1() { delta = r.int64() r.prevLine += delta >> 1 if delta&1 != 0 { - r.prevFile = r.string() + r.prevPosBase = r.posBase() } } } @@ -480,8 +597,9 @@ func isInterface(t types2.Type) bool { return ok } -func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) } -func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } +func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) } +func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } +func (r *importReader) posBase() *syntax.PosBase { return r.p.posBaseAt(r.uint64()) } func (r *importReader) doType(base *types2.Named) types2.Type { switch k := r.kind(); k { @@ -507,7 +625,7 @@ func (r *importReader) doType(base *types2.Named) types2.Type { return types2.NewMap(r.typ(), r.typ()) case signatureType: r.currPkg = r.pkg() - return r.signature(nil) + return r.signature(nil, nil, nil) case structType: r.currPkg = r.pkg() @@ -547,13 +665,56 @@ func (r *importReader) doType(base *types2.Named) types2.Type { recv = types2.NewVar(syntax.Pos{}, r.currPkg, "", base) } - msig := r.signature(recv) + msig := r.signature(recv, nil, nil) methods[i] = types2.NewFunc(mpos, r.currPkg, mname, msig) } typ := types2.NewInterfaceType(methods, embeddeds) r.p.interfaceList = append(r.p.interfaceList, typ) return typ + + case typeParamType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected type param type") + } + pkg, name := r.qualifiedIdent() + id := ident{pkg, name} + if t, ok := r.p.tparamIndex[id]; ok { + // We're already in the process of importing this typeparam. + return t + } + // Otherwise, import the definition of the typeparam now. + r.p.doDecl(pkg, name) + return r.p.tparamIndex[id] + + case instanceType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + // pos does not matter for instances: they are positioned on the original + // type. + _ = r.pos() + len := r.uint64() + targs := make([]types2.Type, len) + for i := range targs { + targs[i] = r.typ() + } + baseType := r.typ() + // The imported instantiated type doesn't include any methods, so + // we must always use the methods of the base (orig) type. + // TODO provide a non-nil *Context + t, _ := types2.Instantiate(nil, baseType, targs, false) + return t + + case unionType: + if r.p.exportVersion < iexportVersionGenerics { + errorf("unexpected instantiation type") + } + terms := make([]*types2.Term, r.uint64()) + for i := range terms { + terms[i] = types2.NewTerm(r.bool(), r.typ()) + } + return types2.NewUnion(terms) } } @@ -561,11 +722,23 @@ func (r *importReader) kind() itag { return itag(r.uint64()) } -func (r *importReader) signature(recv *types2.Var) *types2.Signature { +func (r *importReader) signature(recv *types2.Var, rparams, tparams []*types2.TypeParam) *types2.Signature { params := r.paramList() results := r.paramList() variadic := params.Len() > 0 && r.bool() - return types2.NewSignature(recv, params, results, variadic) + return types2.NewSignatureType(recv, rparams, tparams, params, results, variadic) +} + +func (r *importReader) tparamList() []*types2.TypeParam { + n := r.uint64() + if n == 0 { + return nil + } + xs := make([]*types2.TypeParam, n) + for i := range xs { + xs[i] = r.typ().(*types2.TypeParam) + } + return xs } func (r *importReader) paramList() *types2.Tuple { @@ -610,3 +783,13 @@ func (r *importReader) byte() byte { } return x } + +func baseType(typ types2.Type) *types2.Named { + // pointer receivers are never types2.Named types + if p, _ := typ.(*types2.Pointer); p != nil { + typ = p.Elem() + } + // receiver base types are always (possibly generic) types2.Named types + n, _ := typ.(*types2.Named) + return n +} diff --git a/src/cmd/compile/internal/importer/support.go b/src/cmd/compile/internal/importer/support.go index 40b9c7c9583fbf..5810f5e172b813 100644 --- a/src/cmd/compile/internal/importer/support.go +++ b/src/cmd/compile/internal/importer/support.go @@ -1,4 +1,3 @@ -// UNREVIEWED // 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. @@ -8,12 +7,18 @@ package importer import ( + "cmd/compile/internal/base" "cmd/compile/internal/types2" "fmt" "go/token" + "internal/pkgbits" "sync" ) +func assert(p bool) { + base.Assert(p) +} + func errorf(format string, args ...interface{}) { panic(fmt.Sprintf(format, args...)) } @@ -119,10 +124,29 @@ var predeclared = []types2.Type{ types2.Typ[types2.Invalid], // only appears in packages with errors // used internally by gc; never used by this package or in .a files + // not to be confused with the universe any anyType{}, + + // comparable + types2.Universe.Lookup("comparable").Type(), + + // any + types2.Universe.Lookup("any").Type(), } type anyType struct{} func (t anyType) Underlying() types2.Type { return t } func (t anyType) String() string { return "any" } + +// See cmd/compile/internal/noder.derivedInfo. +type derivedInfo struct { + idx pkgbits.Index + needed bool +} + +// See cmd/compile/internal/noder.typeInfo. +type typeInfo struct { + idx pkgbits.Index + derived bool +} diff --git a/src/cmd/compile/internal/importer/testdata/a.go b/src/cmd/compile/internal/importer/testdata/a.go index 06dafee98c30f8..56e4292cda9f3a 100644 --- a/src/cmd/compile/internal/importer/testdata/a.go +++ b/src/cmd/compile/internal/importer/testdata/a.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/b.go b/src/cmd/compile/internal/importer/testdata/b.go index a601dbccc5a0e9..419667820078e4 100644 --- a/src/cmd/compile/internal/importer/testdata/b.go +++ b/src/cmd/compile/internal/importer/testdata/b.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/exports.go b/src/cmd/compile/internal/importer/testdata/exports.go index 2a720fd2c17254..91598c03e35c03 100644 --- a/src/cmd/compile/internal/importer/testdata/exports.go +++ b/src/cmd/compile/internal/importer/testdata/exports.go @@ -1,4 +1,3 @@ -// UNREVIEWED // 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. @@ -16,14 +15,17 @@ const init1 = 0 func init() {} const ( - C0 int = 0 - C1 = 3.14159265 - C2 = 2.718281828i - C3 = -123.456e-789 - C4 = +123.456e+789 - C5 = 1234i - C6 = "foo\n" - C7 = `bar\n` + C0 int = 0 + C1 = 3.14159265 + C2 = 2.718281828i + C3 = -123.456e-789 + C4 = +123.456e+789 + C5 = 1234i + C6 = "foo\n" + C7 = `bar\n` + C8 = 42 + C9 int = 42 + C10 float64 = 42 ) type ( diff --git a/src/cmd/compile/internal/importer/testdata/generics.go b/src/cmd/compile/internal/importer/testdata/generics.go new file mode 100644 index 00000000000000..00bf04000fa06c --- /dev/null +++ b/src/cmd/compile/internal/importer/testdata/generics.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Go 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 is used to generate an object file which +// serves as test file for gcimporter_test.go. + +package generics + +type Any any + +var x any + +type T[A, B any] struct { + Left A + Right B +} + +var X T[int, string] = T[int, string]{1, "hi"} + +func ToInt[P interface{ ~int }](p P) int { return int(p) } + +var IntID = ToInt[int] + +type G[C comparable] int + +func ImplicitFunc[T ~int]() {} + +type ImplicitType[T ~int] int diff --git a/src/cmd/compile/internal/importer/testdata/issue15920.go b/src/cmd/compile/internal/importer/testdata/issue15920.go index b4020261627583..c70f7d8267b2f9 100644 --- a/src/cmd/compile/internal/importer/testdata/issue15920.go +++ b/src/cmd/compile/internal/importer/testdata/issue15920.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/issue20046.go b/src/cmd/compile/internal/importer/testdata/issue20046.go index e412f353ad2e6d..c63ee821c959da 100644 --- a/src/cmd/compile/internal/importer/testdata/issue20046.go +++ b/src/cmd/compile/internal/importer/testdata/issue20046.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/issue25301.go b/src/cmd/compile/internal/importer/testdata/issue25301.go index a9dc1d7f083b2e..e3dc98b4e1f9ac 100644 --- a/src/cmd/compile/internal/importer/testdata/issue25301.go +++ b/src/cmd/compile/internal/importer/testdata/issue25301.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/issue25596.go b/src/cmd/compile/internal/importer/testdata/issue25596.go index 95bef42280e9b4..8923373e5fa44d 100644 --- a/src/cmd/compile/internal/importer/testdata/issue25596.go +++ b/src/cmd/compile/internal/importer/testdata/issue25596.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/p.go b/src/cmd/compile/internal/importer/testdata/p.go index 34a20eaa1451ec..9e2e7057653725 100644 --- a/src/cmd/compile/internal/importer/testdata/p.go +++ b/src/cmd/compile/internal/importer/testdata/p.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/testdata/versions/test.go b/src/cmd/compile/internal/importer/testdata/versions/test.go index 2f8eb5ced047c4..227fc092519212 100644 --- a/src/cmd/compile/internal/importer/testdata/versions/test.go +++ b/src/cmd/compile/internal/importer/testdata/versions/test.go @@ -1,4 +1,3 @@ -// UNREVIEWED // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/compile/internal/importer/ureader.go b/src/cmd/compile/internal/importer/ureader.go new file mode 100644 index 00000000000000..a227ab64db8833 --- /dev/null +++ b/src/cmd/compile/internal/importer/ureader.go @@ -0,0 +1,510 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package importer + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" + "cmd/internal/src" + "internal/pkgbits" +) + +type pkgReader struct { + pkgbits.PkgDecoder + + ctxt *types2.Context + imports map[string]*types2.Package + + posBases []*syntax.PosBase + pkgs []*types2.Package + typs []types2.Type +} + +func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input pkgbits.PkgDecoder) *types2.Package { + pr := pkgReader{ + PkgDecoder: input, + + ctxt: ctxt, + imports: imports, + + posBases: make([]*syntax.PosBase, input.NumElems(pkgbits.RelocPosBase)), + pkgs: make([]*types2.Package, input.NumElems(pkgbits.RelocPkg)), + typs: make([]types2.Type, input.NumElems(pkgbits.RelocType)), + } + + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + pkg := r.pkg() + r.Bool() // TODO(mdempsky): Remove; was "has init" + + for i, n := 0, r.Len(); i < n; i++ { + // As if r.obj(), but avoiding the Scope.Lookup call, + // to avoid eager loading of imports. + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + assert(r.Len() == 0) + } + + r.Sync(pkgbits.SyncEOF) + + pkg.MarkComplete() + return pkg +} + +type reader struct { + pkgbits.Decoder + + p *pkgReader + + dict *readerDict +} + +type readerDict struct { + bounds []typeInfo + + tparams []*types2.TypeParam + + derived []derivedInfo + derivedTypes []types2.Type +} + +type readerTypeBound struct { + derived bool + boundIdx int +} + +func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader { + return &reader{ + Decoder: pr.NewDecoder(k, idx, marker), + p: pr, + } +} + +// @@@ Positions + +func (r *reader) pos() syntax.Pos { + r.Sync(pkgbits.SyncPos) + if !r.Bool() { + return syntax.Pos{} + } + + // TODO(mdempsky): Delta encoding. + posBase := r.posBase() + line := r.Uint() + col := r.Uint() + return syntax.MakePos(posBase, line, col) +} + +func (r *reader) posBase() *syntax.PosBase { + return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)) +} + +func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *syntax.PosBase { + if b := pr.posBases[idx]; b != nil { + return b + } + + r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase) + var b *syntax.PosBase + + filename := r.String() + + if r.Bool() { + b = syntax.NewTrimmedFileBase(filename, true) + } else { + pos := r.pos() + line := r.Uint() + col := r.Uint() + b = syntax.NewLineBase(pos, filename, true, line, col) + } + + pr.posBases[idx] = b + return b +} + +// @@@ Packages + +func (r *reader) pkg() *types2.Package { + r.Sync(pkgbits.SyncPkg) + return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg)) +} + +func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types2.Package { + // TODO(mdempsky): Consider using some non-nil pointer to indicate + // the universe scope, so we don't need to keep re-reading it. + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +func (r *reader) doPkg() *types2.Package { + path := r.String() + switch path { + case "": + path = r.p.PkgPath() + case "builtin": + return nil // universe + case "unsafe": + return types2.Unsafe + } + + if pkg := r.p.imports[path]; pkg != nil { + return pkg + } + + name := r.String() + pkg := types2.NewPackage(path, name) + r.p.imports[path] = pkg + + // TODO(mdempsky): The list of imported packages is important for + // go/types, but we could probably skip populating it for types2. + imports := make([]*types2.Package, r.Len()) + for i := range imports { + imports[i] = r.pkg() + } + pkg.SetImports(imports) + + return pkg +} + +// @@@ Types + +func (r *reader) typ() types2.Type { + return r.p.typIdx(r.typInfo(), r.dict) +} + +func (r *reader) typInfo() typeInfo { + r.Sync(pkgbits.SyncType) + if r.Bool() { + return typeInfo{idx: pkgbits.Index(r.Len()), derived: true} + } + return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false} +} + +func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types2.Type { + idx := info.idx + var where *types2.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // See comment in pkgReader.typIdx explaining how this happens. + if prev := *where; prev != nil { + return prev + } + + *where = typ + return typ +} + +func (r *reader) doTyp() (res types2.Type) { + switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag { + default: + base.FatalfAt(src.NoXPos, "unhandled type tag: %v", tag) + panic("unreachable") + + case pkgbits.TypeBasic: + return types2.Typ[r.Len()] + + case pkgbits.TypeNamed: + obj, targs := r.obj() + name := obj.(*types2.TypeName) + if len(targs) != 0 { + t, _ := types2.Instantiate(r.p.ctxt, name.Type(), targs, false) + return t + } + return name.Type() + + case pkgbits.TypeTypeParam: + return r.dict.tparams[r.Len()] + + case pkgbits.TypeArray: + len := int64(r.Uint64()) + return types2.NewArray(r.typ(), len) + case pkgbits.TypeChan: + dir := types2.ChanDir(r.Len()) + return types2.NewChan(dir, r.typ()) + case pkgbits.TypeMap: + return types2.NewMap(r.typ(), r.typ()) + case pkgbits.TypePointer: + return types2.NewPointer(r.typ()) + case pkgbits.TypeSignature: + return r.signature(nil, nil, nil) + case pkgbits.TypeSlice: + return types2.NewSlice(r.typ()) + case pkgbits.TypeStruct: + return r.structType() + case pkgbits.TypeInterface: + return r.interfaceType() + case pkgbits.TypeUnion: + return r.unionType() + } +} + +func (r *reader) structType() *types2.Struct { + fields := make([]*types2.Var, r.Len()) + var tags []string + for i := range fields { + pos := r.pos() + pkg, name := r.selector() + ftyp := r.typ() + tag := r.String() + embedded := r.Bool() + + fields[i] = types2.NewField(pos, pkg, name, ftyp, embedded) + if tag != "" { + for len(tags) < i { + tags = append(tags, "") + } + tags = append(tags, tag) + } + } + return types2.NewStruct(fields, tags) +} + +func (r *reader) unionType() *types2.Union { + terms := make([]*types2.Term, r.Len()) + for i := range terms { + terms[i] = types2.NewTerm(r.Bool(), r.typ()) + } + return types2.NewUnion(terms) +} + +func (r *reader) interfaceType() *types2.Interface { + methods := make([]*types2.Func, r.Len()) + embeddeds := make([]types2.Type, r.Len()) + implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool() + + for i := range methods { + pos := r.pos() + pkg, name := r.selector() + mtyp := r.signature(nil, nil, nil) + methods[i] = types2.NewFunc(pos, pkg, name, mtyp) + } + + for i := range embeddeds { + embeddeds[i] = r.typ() + } + + iface := types2.NewInterfaceType(methods, embeddeds) + if implicit { + iface.MarkImplicit() + } + return iface +} + +func (r *reader) signature(recv *types2.Var, rtparams, tparams []*types2.TypeParam) *types2.Signature { + r.Sync(pkgbits.SyncSignature) + + params := r.params() + results := r.params() + variadic := r.Bool() + + return types2.NewSignatureType(recv, rtparams, tparams, params, results, variadic) +} + +func (r *reader) params() *types2.Tuple { + r.Sync(pkgbits.SyncParams) + params := make([]*types2.Var, r.Len()) + for i := range params { + params[i] = r.param() + } + return types2.NewTuple(params...) +} + +func (r *reader) param() *types2.Var { + r.Sync(pkgbits.SyncParam) + + pos := r.pos() + pkg, name := r.localIdent() + typ := r.typ() + + return types2.NewParam(pos, pkg, name, typ) +} + +// @@@ Objects + +func (r *reader) obj() (types2.Object, []types2.Type) { + r.Sync(pkgbits.SyncObject) + + assert(!r.Bool()) + + pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + obj := pkg.Scope().Lookup(name) + + targs := make([]types2.Type, r.Len()) + for i := range targs { + targs[i] = r.typ() + } + + return obj, targs +} + +func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) { + rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) + + objPkg, objName := rname.qualifiedIdent() + assert(objName != "") + + tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) + + if tag == pkgbits.ObjStub { + base.Assertf(objPkg == nil || objPkg == types2.Unsafe, "unexpected stub package: %v", objPkg) + return objPkg, objName + } + + objPkg.Scope().InsertLazy(objName, func() types2.Object { + dict := pr.objDictIdx(idx) + + r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1) + r.dict = dict + + switch tag { + default: + panic("weird") + + case pkgbits.ObjAlias: + pos := r.pos() + typ := r.typ() + return types2.NewTypeName(pos, objPkg, objName, typ) + + case pkgbits.ObjConst: + pos := r.pos() + typ := r.typ() + val := r.Value() + return types2.NewConst(pos, objPkg, objName, typ, val) + + case pkgbits.ObjFunc: + pos := r.pos() + tparams := r.typeParamNames() + sig := r.signature(nil, nil, tparams) + return types2.NewFunc(pos, objPkg, objName, sig) + + case pkgbits.ObjType: + pos := r.pos() + + return types2.NewTypeNameLazy(pos, objPkg, objName, func(named *types2.Named) (tparams []*types2.TypeParam, underlying types2.Type, methods []*types2.Func) { + tparams = r.typeParamNames() + + // TODO(mdempsky): Rewrite receiver types to underlying is an + // Interface? The go/types importer does this (I think because + // unit tests expected that), but cmd/compile doesn't care + // about it, so maybe we can avoid worrying about that here. + underlying = r.typ().Underlying() + + methods = make([]*types2.Func, r.Len()) + for i := range methods { + methods[i] = r.method() + } + + return + }) + + case pkgbits.ObjVar: + pos := r.pos() + typ := r.typ() + return types2.NewVar(pos, objPkg, objName, typ) + } + }) + + return objPkg, objName +} + +func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict { + r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1) + + var dict readerDict + + if implicits := r.Len(); implicits != 0 { + base.Fatalf("unexpected object with %v implicit type parameter(s)", implicits) + } + + dict.bounds = make([]typeInfo, r.Len()) + for i := range dict.bounds { + dict.bounds[i] = r.typInfo() + } + + dict.derived = make([]derivedInfo, r.Len()) + dict.derivedTypes = make([]types2.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + } + + // function references follow, but reader doesn't need those + + return &dict +} + +func (r *reader) typeParamNames() []*types2.TypeParam { + r.Sync(pkgbits.SyncTypeParamNames) + + // Note: This code assumes it only processes objects without + // implement type parameters. This is currently fine, because + // reader is only used to read in exported declarations, which are + // always package scoped. + + if len(r.dict.bounds) == 0 { + return nil + } + + // Careful: Type parameter lists may have cycles. To allow for this, + // we construct the type parameter list in two passes: first we + // create all the TypeNames and TypeParams, then we construct and + // set the bound type. + + r.dict.tparams = make([]*types2.TypeParam, len(r.dict.bounds)) + for i := range r.dict.bounds { + pos := r.pos() + pkg, name := r.localIdent() + + tname := types2.NewTypeName(pos, pkg, name, nil) + r.dict.tparams[i] = types2.NewTypeParam(tname, nil) + } + + for i, bound := range r.dict.bounds { + r.dict.tparams[i].SetConstraint(r.p.typIdx(bound, r.dict)) + } + + return r.dict.tparams +} + +func (r *reader) method() *types2.Func { + r.Sync(pkgbits.SyncMethod) + pos := r.pos() + pkg, name := r.selector() + + rtparams := r.typeParamNames() + sig := r.signature(r.param(), rtparams, nil) + + _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go. + return types2.NewFunc(pos, pkg, name, sig) +} + +func (r *reader) qualifiedIdent() (*types2.Package, string) { return r.ident(pkgbits.SyncSym) } +func (r *reader) localIdent() (*types2.Package, string) { return r.ident(pkgbits.SyncLocalIdent) } +func (r *reader) selector() (*types2.Package, string) { return r.ident(pkgbits.SyncSelector) } + +func (r *reader) ident(marker pkgbits.SyncMarker) (*types2.Package, string) { + r.Sync(marker) + return r.pkg(), r.String() +} diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index d6b4ced4e157cd..078eeb733157aa 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -29,11 +29,13 @@ package inline import ( "fmt" "go/constant" + "strconv" "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" + "cmd/compile/internal/pgo" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" @@ -42,7 +44,8 @@ import ( // Inlining budget parameters, gathered in one place const ( - inlineMaxBudget = 80 + inlineMaxBudget = 80 + // Budget increased due to hotness. inlineExtraAppendCost = 0 // default is to inline if there's at most one call. -l=4 overrides this by using 1 instead. inlineExtraCallCost = 57 // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742 @@ -51,9 +54,87 @@ const ( inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big". inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function. + ) -// InlinePackage finds functions that can be inlined and clones them before walk expands them. +var ( + // Per-caller data structure to track the list of hot call sites. This gets rewritten every caller leaving it to GC for cleanup. + listOfHotCallSites = make(map[pgo.CallSiteInfo]struct{}) + + // List of all hot call sites. + candHotEdgeMap = make(map[string]struct{}) + + // List of inlined call sites. + inlinedCallSites = make(map[pgo.CallSiteInfo]struct{}) + + // Threshold for Hot callsite inlining. + inlineHotThresholdPercent = float64(2) + + // Budget increased due to hotness. + inlineHotMaxBudget int32 = 160 +) + +// InlinePrologue records the hot callsites from ir-graph. +func InlinePrologue() { + if s, err := strconv.ParseFloat(base.Flag.InlineHotThreshold, 64); err == nil { + inlineHotThresholdPercent = s + if base.Flag.LowerM != 0 { + fmt.Printf("hot-thres=%v\n", inlineHotThresholdPercent) + } + } + + if base.Flag.InlineHotBudget != 0 { + inlineHotMaxBudget = int32(base.Flag.InlineHotBudget) + } + + ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { + for _, f := range list { + name := ir.PkgFuncName(f) + if n, ok := pgo.WeightedCG.IRNodes[name]; ok { + nodeweight := pgo.WeightInPercentage(n.Flat, pgo.GlobalTotalNodeWeight) + if nodeweight > inlineHotThresholdPercent { + n.HotNode = true + } + for _, e := range pgo.WeightedCG.OutEdges[n] { + if e.Weight != 0 { + weightpercent := pgo.WeightInPercentage(e.Weight, pgo.GlobalTotalEdgeWeight) + if weightpercent > inlineHotThresholdPercent { + splits := strings.Split(e.CallSite, ":") + line2, _ := strconv.ParseInt(splits[len(splits)-2], 0, 64) + lineno := fmt.Sprintf("%v", line2) + canonicalName := ir.PkgFuncName(n.AST) + "-" + lineno + "-" + ir.PkgFuncName(e.Dst.AST) + if _, ok := candHotEdgeMap[canonicalName]; !ok { + candHotEdgeMap[canonicalName] = struct{}{} + } + } + } + } + } + } + }) + if base.Flag.LowerM > 4 { + fmt.Printf("hot-cg before inline in dot format:") + pgo.PrintWeightedCallGraphDOT(inlineHotThresholdPercent) + } +} + +// InlineEpilogue updates IRGraph after inlining. +func InlineEpilogue() { + ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { + for _, f := range list { + name := ir.PkgFuncName(f) + if n, ok := pgo.WeightedCG.IRNodes[name]; ok { + pgo.RedirectEdges(n, inlinedCallSites) + } + } + }) + if base.Flag.LowerM > 4 { + fmt.Printf("hot-cg after inline in dot:") + pgo.PrintWeightedCallGraphDOT(inlineHotThresholdPercent) + } +} + +// InlinePackage finds functions that can be inlined and clones them. func InlinePackage() { ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { numfns := numNonClosures(list) @@ -81,6 +162,9 @@ func CanInline(fn *ir.Func) { base.Fatalf("CanInline no nname %+v", fn) } + // Initialize an empty list of hot callsites for this caller. + listOfHotCallSites = make(map[pgo.CallSiteInfo]struct{}) + var reason string // reason, if any, that the function was not inlined if base.Flag.LowerM > 1 || logopt.Enabled() { defer func() { @@ -120,6 +204,17 @@ func CanInline(fn *ir.Func) { return } + // If marked as "go:uintptrkeepalive", don't inline, since the + // keep alive information is lost during inlining. + // + // TODO(prattmic): This is handled on calls during escape analysis, + // which is after inlining. Move prior to inlining so the keep-alive is + // maintained after inlining. + if fn.Pragma&ir.UintptrKeepAlive != 0 { + reason = "marked as having a keep-alive uintptr argument" + return + } + // If marked as "go:uintptrescapes", don't inline, since the // escape information is lost during inlining. if fn.Pragma&ir.UintptrEscapes != 0 { @@ -170,15 +265,21 @@ func CanInline(fn *ir.Func) { budget: inlineMaxBudget, extraCallCost: cc, } + savefn := ir.CurFunc + ir.CurFunc = fn if visitor.tooHairy(fn) { reason = visitor.reason + ir.CurFunc = savefn return } + ir.CurFunc = savefn n.Func.Inl = &ir.Inline{ Cost: inlineMaxBudget - visitor.budget, Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor), Body: inlcopylist(fn.Body), + + CanDelayResults: canDelayResults(fn), } if base.Flag.LowerM > 1 { @@ -191,60 +292,36 @@ func CanInline(fn *ir.Func) { } } -// Inline_Flood marks n's inline body for export and recursively ensures -// all called functions are marked too. -func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) { - if n == nil { - return - } - if n.Op() != ir.ONAME || n.Class != ir.PFUNC { - base.Fatalf("Inline_Flood: unexpected %v, %v, %v", n, n.Op(), n.Class) - } - fn := n.Func - if fn == nil { - base.Fatalf("Inline_Flood: missing Func on %v", n) - } - if fn.Inl == nil { - return - } - - if fn.ExportInline() { - return - } - fn.SetExportInline(true) - - typecheck.ImportedBody(fn) - - var doFlood func(n ir.Node) - doFlood = func(n ir.Node) { - switch n.Op() { - case ir.OMETHEXPR, ir.ODOTMETH: - Inline_Flood(ir.MethodExprName(n), exportsym) +// canDelayResults reports whether inlined calls to fn can delay +// declaring the result parameter until the "return" statement. +func canDelayResults(fn *ir.Func) bool { + // We can delay declaring+initializing result parameters if: + // (1) there's exactly one "return" statement in the inlined function; + // (2) it's not an empty return statement (#44355); and + // (3) the result parameters aren't named. - case ir.ONAME: - n := n.(*ir.Name) - switch n.Class { - case ir.PFUNC: - Inline_Flood(n, exportsym) - exportsym(n) - case ir.PEXTERN: - exportsym(n) + nreturns := 0 + ir.VisitList(fn.Body, func(n ir.Node) { + if n, ok := n.(*ir.ReturnStmt); ok { + nreturns++ + if len(n.Results) == 0 { + nreturns++ // empty return statement (case 2) } + } + }) - case ir.OCALLPART: - // Okay, because we don't yet inline indirect - // calls to method values. - case ir.OCLOSURE: - // VisitList doesn't visit closure bodies, so force a - // recursive call to VisitList on the body of the closure. - ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood) + if nreturns != 1 { + return false // not exactly one return statement (case 1) + } + + // temporaries for return values. + for _, param := range fn.Type().Results().FieldSlice() { + if sym := types.OrigSym(param.Sym); sym != nil && !sym.IsBlank() { + return false // found a named result parameter (case 3) } } - // Recursively identify all referenced functions for - // reexport. We want to include even non-called functions, - // because after inlining they might be callable. - ir.VisitList(ir.Nodes(fn.Inl.Body), doFlood) + return true } // hairyVisitor visits a function body to determine its inlining @@ -263,6 +340,19 @@ func (v *hairyVisitor) tooHairy(fn *ir.Func) bool { return true } if v.budget < 0 { + if pgo.WeightedCG != nil { + // Find the existing node in WeightedCallGraph. + if n, ok := pgo.WeightedCG.IRNodes[ir.PkgFuncName(fn)]; ok { + // If the cost of hot function is greater than inlineHotMaxBudget, + // the inliner won't inline this function. + if inlineMaxBudget-v.budget < inlineHotMaxBudget && n.HotNode == true { + if base.Flag.LowerM > 1 { + fmt.Printf("hot-node enabled increased budget for func=%v\n", ir.PkgFuncName(fn)) + } + return false + } + } + } v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget) return true } @@ -295,13 +385,60 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { } } } + if n.X.Op() == ir.OMETHEXPR { + if meth := ir.MethodExprName(n.X); meth != nil { + if fn := meth.Func; fn != nil { + s := fn.Sym() + var cheap bool + if types.IsRuntimePkg(s.Pkg) && s.Name == "heapBits.nextArena" { + // Special case: explicitly allow mid-stack inlining of + // runtime.heapBits.next even though it calls slow-path + // runtime.heapBits.nextArena. + cheap = true + } + // Special case: on architectures that can do unaligned loads, + // explicitly mark encoding/binary methods as cheap, + // because in practice they are, even though our inlining + // budgeting system does not see that. See issue 42958. + if base.Ctxt.Arch.CanMergeLoads && s.Pkg.Path == "encoding/binary" { + switch s.Name { + case "littleEndian.Uint64", "littleEndian.Uint32", "littleEndian.Uint16", + "bigEndian.Uint64", "bigEndian.Uint32", "bigEndian.Uint16", + "littleEndian.PutUint64", "littleEndian.PutUint32", "littleEndian.PutUint16", + "bigEndian.PutUint64", "bigEndian.PutUint32", "bigEndian.PutUint16": + cheap = true + } + } + if cheap { + break // treat like any other node, that is, cost of 1 + } + } + } + } + + // Determine if callee edge is a hot callee or not. + if pgo.WeightedCG != nil && ir.CurFunc != nil { + if fn := inlCallee(n.X); fn != nil && typecheck.HaveInlineBody(fn) { + lineno := fmt.Sprintf("%v", ir.Line(n)) + splits := strings.Split(lineno, ":") + l, _ := strconv.ParseInt(splits[len(splits)-2], 0, 64) + linenum := fmt.Sprintf("%v", l) + canonicalName := ir.PkgFuncName(ir.CurFunc) + "-" + linenum + "-" + ir.PkgFuncName(fn) + if _, o := candHotEdgeMap[canonicalName]; o { + if base.Flag.LowerM > 1 { + fmt.Printf("hot-callsite identified at line=%v for func=%v\n", ir.Line(n), ir.PkgFuncName(ir.CurFunc)) + } + listOfHotCallSites[pgo.CallSiteInfo{ir.Line(n), ir.CurFunc}] = struct{}{} + } + } + } if ir.IsIntrinsicCall(n) { // Treat like any other node. break } - if fn := inlCallee(n.X); fn != nil && fn.Inl != nil { + if fn := inlCallee(n.X); fn != nil && typecheck.HaveInlineBody(fn) { v.budget -= fn.Inl.Cost break } @@ -309,28 +446,8 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { // Call cost for non-leaf inlining. v.budget -= v.extraCallCost - // Call is okay if inlinable and we have the budget for the body. case ir.OCALLMETH: - n := n.(*ir.CallExpr) - t := n.X.Type() - if t == nil { - base.Fatalf("no function type for [%p] %+v\n", n.X, n.X) - } - fn := ir.MethodExprName(n.X).Func - if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" { - // Special case: explicitly allow - // mid-stack inlining of - // runtime.heapBits.next even though - // it calls slow-path - // runtime.heapBits.nextArena. - break - } - if fn.Inl != nil { - v.budget -= fn.Inl.Cost - break - } - // Call cost for non-leaf inlining. - v.budget -= v.extraCallCost + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") // Things that are too hairy, irrespective of the budget case ir.OCALL, ir.OCALLINTER: @@ -370,9 +487,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { return true } - case ir.ORANGE, - ir.OSELECT, - ir.OGO, + case ir.OGO, ir.ODEFER, ir.ODCLTYPE, // can't print yet ir.OTAILCALL: @@ -402,35 +517,18 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { // These nodes don't produce code; omit from inlining budget. return false - case ir.OFOR, ir.OFORUNTIL: - n := n.(*ir.ForStmt) - if n.Label != nil { - v.reason = "labeled control" - return true - } - case ir.OSWITCH: - n := n.(*ir.SwitchStmt) - if n.Label != nil { - v.reason = "labeled control" - return true - } - // case ir.ORANGE, ir.OSELECT in "unhandled" above - - case ir.OBREAK, ir.OCONTINUE: - n := n.(*ir.BranchStmt) - if n.Label != nil { - // Should have short-circuited due to labeled control error above. - base.Fatalf("unexpected labeled break/continue: %v", n) - } - case ir.OIF: n := n.(*ir.IfStmt) if ir.IsConst(n.Cond, constant.Bool) { // This if and the condition cost nothing. - // TODO(rsc): It seems strange that we visit the dead branch. - return doList(n.Init(), v.do) || - doList(n.Body, v.do) || - doList(n.Else, v.do) + if doList(n.Init(), v.do) { + return true + } + if ir.BoolVal(n.Cond) { + return doList(n.Body, v.do) + } else { + return doList(n.Else, v.do) + } } case ir.ONAME: @@ -445,19 +543,51 @@ func (v *hairyVisitor) doNode(n ir.Node) bool { // and don't charge for the OBLOCK itself. The ++ undoes the -- below. v.budget++ - case ir.OCALLPART, ir.OSLICELIT: + case ir.OMETHVALUE, ir.OSLICELIT: v.budget-- // Hack for toolstash -cmp. case ir.OMETHEXPR: v.budget++ // Hack for toolstash -cmp. + + case ir.OAS2: + n := n.(*ir.AssignListStmt) + + // Unified IR unconditionally rewrites: + // + // a, b = f() + // + // into: + // + // DCL tmp1 + // DCL tmp2 + // tmp1, tmp2 = f() + // a, b = tmp1, tmp2 + // + // so that it can insert implicit conversions as necessary. To + // minimize impact to the existing inlining heuristics (in + // particular, to avoid breaking the existing inlinability regress + // tests), we need to compensate for this here. + if base.Debug.Unified != 0 { + if init := n.Rhs[0].Init(); len(init) == 1 { + if _, ok := init[0].(*ir.AssignListStmt); ok { + // 4 for each value, because each temporary variable now + // appears 3 times (DCL, LHS, RHS), plus an extra DCL node. + // + // 1 for the extra "tmp1, tmp2 = f()" assignment statement. + v.budget += 4*int32(len(n.Lhs)) + 1 + } + } + } } v.budget-- - // When debugging, don't stop early, to get full cost of inlining this function - if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() { - v.reason = "too expensive" - return true + if pgo.WeightedCG == nil { + // When debugging, don't stop early, to get full cost of inlining this function + if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() { + v.reason = "too expensive" + return true + } } return ir.DoChildren(n, v.do) @@ -499,17 +629,10 @@ func inlcopy(n ir.Node) ir.Node { // x.Func.Body for iexport and local inlining. oldfn := x.Func newfn := ir.NewFunc(oldfn.Pos()) - if oldfn.ClosureCalled() { - newfn.SetClosureCalled(true) - } m.(*ir.ClosureExpr).Func = newfn newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym()) // XXX OK to share fn.Type() ?? newfn.Nname.SetType(oldfn.Nname.Type()) - // Ntype can be nil for -G=3 mode. - if oldfn.Nname.Ntype != nil { - newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype) - } newfn.Body = inlcopylist(oldfn.Body) // Make shallow copy of the Dcl and ClosureVar slices newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...) @@ -529,50 +652,24 @@ func InlineCalls(fn *ir.Func) { if isBigFunc(fn) { maxCost = inlineBigFunctionMaxCost } - // Map to keep track of functions that have been inlined at a particular - // call site, in order to stop inlining when we reach the beginning of a - // recursion cycle again. We don't inline immediately recursive functions, - // but allow inlining if there is a recursion cycle of many functions. - // Most likely, the inlining will stop before we even hit the beginning of - // the cycle again, but the map catches the unusual case. - inlMap := make(map[*ir.Func]bool) + var inlCalls []*ir.InlinedCallExpr var edit func(ir.Node) ir.Node edit = func(n ir.Node) ir.Node { - return inlnode(n, maxCost, inlMap, edit) + return inlnode(n, maxCost, &inlCalls, edit) } ir.EditChildren(fn, edit) - ir.CurFunc = savefn -} -// Turn an OINLCALL into a statement. -func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node { - n := ir.NewBlockStmt(inlcall.Pos(), nil) - n.List = inlcall.Init() - n.List.Append(inlcall.Body.Take()...) - return n -} - -// Turn an OINLCALL into a single valued expression. -// The result of inlconv2expr MUST be assigned back to n, e.g. -// n.Left = inlconv2expr(n.Left) -func inlconv2expr(n *ir.InlinedCallExpr) ir.Node { - r := n.ReturnVars[0] - return ir.InitExpr(append(n.Init(), n.Body...), r) -} - -// Turn the rlist (with the return values) of the OINLCALL in -// n into an expression list lumping the ninit and body -// containing the inlined statements on the first list element so -// order will be preserved. Used in return, oas2func and call -// statements. -func inlconv2list(n *ir.InlinedCallExpr) []ir.Node { - if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 { - base.Fatalf("inlconv2list %+v\n", n) + // If we inlined any calls, we want to recursively visit their + // bodies for further inlining. However, we need to wait until + // *after* the original function body has been expanded, or else + // inlCallee can have false positives (e.g., #54632). + for len(inlCalls) > 0 { + call := inlCalls[0] + inlCalls = inlCalls[1:] + ir.EditChildren(call, edit) } - s := n.ReturnVars - s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0]) - return s + ir.CurFunc = savefn } // inlnode recurses over the tree to find inlineable calls, which will @@ -587,8 +684,9 @@ func inlconv2list(n *ir.InlinedCallExpr) []ir.Node { // but then you may as well do it here. so this is cleaner and // shorter and less complicated. // The result of inlnode MUST be assigned back to n, e.g. -// n.Left = inlnode(n.Left) -func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node { +// +// n.Left = inlnode(n.Left) +func inlnode(n ir.Node, maxCost int32, inlCalls *[]*ir.InlinedCallExpr, edit func(ir.Node) ir.Node) ir.Node { if n == nil { return n } @@ -597,21 +695,33 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No case ir.ODEFER, ir.OGO: n := n.(*ir.GoDeferStmt) switch call := n.Call; call.Op() { - case ir.OCALLFUNC, ir.OCALLMETH: + case ir.OCALLMETH: + base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") + case ir.OCALLFUNC: call := call.(*ir.CallExpr) call.NoInline = true } + case ir.OTAILCALL: + n := n.(*ir.TailCallStmt) + n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)? // TODO do them here (or earlier), // so escape analysis can avoid more heapmoves. case ir.OCLOSURE: return n case ir.OCALLMETH: - // Prevent inlining some reflect.Value methods when using checkptr, - // even when package reflect was compiled without it (#35073). + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") + case ir.OCALLFUNC: n := n.(*ir.CallExpr) - if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") { - return n + if n.X.Op() == ir.OMETHEXPR { + // Prevent inlining some reflect.Value methods when using checkptr, + // even when package reflect was compiled without it (#35073). + if meth := ir.MethodExprName(n.X); meth != nil { + s := meth.Sym() + if base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") { + return n + } + } } } @@ -619,72 +729,31 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No ir.EditChildren(n, edit) - if as := n; as.Op() == ir.OAS2FUNC { - as := as.(*ir.AssignListStmt) - if as.Rhs[0].Op() == ir.OINLCALL { - as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr)) - as.SetOp(ir.OAS2) - as.SetTypecheck(0) - n = typecheck.Stmt(as) - } - } - // with all the branches out of the way, it is now time to // transmogrify this node itself unless inhibited by the // switch at the top of this function. switch n.Op() { - case ir.OCALLFUNC, ir.OCALLMETH: - n := n.(*ir.CallExpr) - if n.NoInline { - return n - } - } + case ir.OCALLMETH: + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") - var call *ir.CallExpr - switch n.Op() { case ir.OCALLFUNC: - call = n.(*ir.CallExpr) + call := n.(*ir.CallExpr) + if call.NoInline { + break + } if base.Flag.LowerM > 3 { fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X) } if ir.IsIntrinsicCall(call) { break } - if fn := inlCallee(call.X); fn != nil && fn.Inl != nil { - n = mkinlcall(call, fn, maxCost, inlMap, edit) + if fn := inlCallee(call.X); fn != nil && typecheck.HaveInlineBody(fn) { + n = mkinlcall(call, fn, maxCost, inlCalls, edit) } - - case ir.OCALLMETH: - call = n.(*ir.CallExpr) - if base.Flag.LowerM > 3 { - fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel) - } - - // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. - if call.X.Type() == nil { - base.Fatalf("no function type for [%p] %+v\n", call.X, call.X) - } - - n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit) } base.Pos = lno - if n.Op() == ir.OINLCALL { - ic := n.(*ir.InlinedCallExpr) - switch call.Use { - default: - ir.Dump("call", call) - base.Fatalf("call missing use") - case ir.CallUseExpr: - n = inlconv2expr(ic) - case ir.CallUseStmt: - n = inlconv2stmt(ic) - case ir.CallUseList: - // leave for caller to convert - } - } - return n } @@ -740,14 +809,19 @@ var inlgen int // when producing output for debugging the compiler itself. var SSADumpInline = func(*ir.Func) {} -// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a +// InlineCall allows the inliner implementation to be overridden. +// If it returns nil, the function will not be inlined. +var InlineCall = oldInlineCall + +// If n is a OCALLFUNC node, and fn is an ONAME node for a // function with an inlinable body, return an OINLCALL node that can replace n. // The returned node's Ninit has the parameter assignments, the Nbody is the // inlined function body, and (List, Rlist) contain the (input, output) // parameters. // The result of mkinlcall MUST be assigned back to n, e.g. -// n.Left = mkinlcall(n.Left, fn, isddd) -func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node { +// +// n.Left = mkinlcall(n.Left, fn, isddd) +func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlCalls *[]*ir.InlinedCallExpr, edit func(ir.Node) ir.Node) ir.Node { if fn.Inl == nil { if logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc), @@ -762,7 +836,18 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc), fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost)) } - return n + + // If the callsite is hot and it is under the inlineHotMaxBudget budget, then inline it, or else bail. + if _, ok := listOfHotCallSites[pgo.CallSiteInfo{ir.Line(n), ir.CurFunc}]; ok { + if fn.Inl.Cost > inlineHotMaxBudget { + return n + } + if base.Flag.LowerM > 1 { + fmt.Printf("hot-budget check allows inlining at %v\n", ir.Line(n)) + } + } else { + return n + } } if fn == ir.CurFunc { @@ -773,6 +858,49 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b return n } + // The non-unified frontend has issues with inlining and shape parameters. + if base.Debug.Unified == 0 { + // Don't inline a function fn that has no shape parameters, but is passed at + // least one shape arg. This means we must be inlining a non-generic function + // fn that was passed into a generic function, and can be called with a shape + // arg because it matches an appropriate type parameters. But fn may include + // an interface conversion (that may be applied to a shape arg) that was not + // apparent when we first created the instantiation of the generic function. + // We can't handle this if we actually do the inlining, since we want to know + // all interface conversions immediately after stenciling. So, we avoid + // inlining in this case, see issue #49309. (1) + // + // See discussion on go.dev/cl/406475 for more background. + if !fn.Type().Params().HasShape() { + for _, arg := range n.Args { + if arg.Type().HasShape() { + if logopt.Enabled() { + logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc), + fmt.Sprintf("inlining function %v has no-shape params with shape args", ir.FuncName(fn))) + } + return n + } + } + } else { + // Don't inline a function fn that has shape parameters, but is passed no shape arg. + // See comments (1) above, and issue #51909. + inlineable := len(n.Args) == 0 // Function has shape in type, with no arguments can always be inlined. + for _, arg := range n.Args { + if arg.Type().HasShape() { + inlineable = true + break + } + } + if !inlineable { + if logopt.Enabled() { + logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc), + fmt.Sprintf("inlining function %v has shape params with no-shape args", ir.FuncName(fn))) + } + return n + } + } + } + if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) { // Runtime package must not be instrumented. // Instrument skips runtime package. However, some runtime code can be @@ -783,48 +911,104 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b return n } - if inlMap[fn] { - if base.Flag.LowerM > 1 { - fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc)) + parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex() + sym := fn.Linksym() + + // Check if we've already inlined this function at this particular + // call site, in order to stop inlining when we reach the beginning + // of a recursion cycle again. We don't inline immediately recursive + // functions, but allow inlining if there is a recursion cycle of + // many functions. Most likely, the inlining will stop before we + // even hit the beginning of the cycle again, but this catches the + // unusual case. + for inlIndex := parent; inlIndex >= 0; inlIndex = base.Ctxt.InlTree.Parent(inlIndex) { + if base.Ctxt.InlTree.InlinedFunction(inlIndex) == sym { + if base.Flag.LowerM > 1 { + fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc)) + } + return n } - return n } - inlMap[fn] = true - defer func() { - inlMap[fn] = false - }() - if base.Debug.TypecheckInl == 0 { - typecheck.ImportedBody(fn) + + typecheck.FixVariadicCall(n) + + inlIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym) + + if base.Flag.GenDwarfInl > 0 { + if !sym.WasInlined() { + base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn) + sym.Set(obj.AttrWasInlined, true) + } } - // We have a function node, and it has an inlineable body. - if base.Flag.LowerM > 1 { - fmt.Printf("%v: inlining call to %v %v { %v }\n", ir.Line(n), fn.Sym(), fn.Type(), ir.Nodes(fn.Inl.Body)) - } else if base.Flag.LowerM != 0 { + if base.Flag.LowerM != 0 { fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn) } if base.Flag.LowerM > 2 { fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n) } - SSADumpInline(fn) + if _, ok := inlinedCallSites[pgo.CallSiteInfo{ir.Line(n), ir.CurFunc}]; !ok { + inlinedCallSites[pgo.CallSiteInfo{ir.Line(n), ir.CurFunc}] = struct{}{} + } - ninit := n.Init() + res := InlineCall(n, fn, inlIndex) - // For normal function calls, the function callee expression - // may contain side effects (e.g., added by addinit during - // inlconv2expr or inlconv2list). Make sure to preserve these, - // if necessary (#42703). - if n.Op() == ir.OCALLFUNC { - callee := n.X - for callee.Op() == ir.OCONVNOP { + if res == nil { + base.FatalfAt(n.Pos(), "inlining call to %v failed", fn) + } + + if base.Flag.LowerM > 2 { + fmt.Printf("%v: After inlining %+v\n\n", ir.Line(res), res) + } + + *inlCalls = append(*inlCalls, res) + + return res +} + +// CalleeEffects appends any side effects from evaluating callee to init. +func CalleeEffects(init *ir.Nodes, callee ir.Node) { + for { + init.Append(ir.TakeInit(callee)...) + + switch callee.Op() { + case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR: + return // done + + case ir.OCONVNOP: conv := callee.(*ir.ConvExpr) - ninit.Append(ir.TakeInit(conv)...) callee = conv.X + + case ir.OINLCALL: + ic := callee.(*ir.InlinedCallExpr) + init.Append(ic.Body.Take()...) + callee = ic.SingleResult() + + default: + base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee) } - if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR { - base.Fatalf("unexpected callee expression: %v", callee) - } + } +} + +// oldInlineCall creates an InlinedCallExpr to replace the given call +// expression. fn is the callee function to be inlined. inlIndex is +// the inlining tree position index, for use with src.NewInliningBase +// when rewriting positions. +func oldInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { + if base.Debug.TypecheckInl == 0 { + typecheck.ImportedBody(fn) + } + + SSADumpInline(fn) + + ninit := call.Init() + + // For normal function calls, the function callee expression + // may contain side effects. Make sure to preserve these, + // if necessary (#42703). + if call.Op() == ir.OCALLFUNC { + CalleeEffects(&ninit, call.X) } // Make temp names to use instead of the originals. @@ -854,25 +1038,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b } // We can delay declaring+initializing result parameters if: - // (1) there's exactly one "return" statement in the inlined function; - // (2) it's not an empty return statement (#44355); and - // (3) the result parameters aren't named. - delayretvars := true - - nreturns := 0 - ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) { - if n, ok := n.(*ir.ReturnStmt); ok { - nreturns++ - if len(n.Results) == 0 { - delayretvars = false // empty return statement (case 2) - } - } - }) - - if nreturns != 1 { - delayretvars = false // not exactly one return statement (case 1) - } - // temporaries for return values. var retvars []ir.Node for i, t := range fn.Type().Results().Fields().Slice() { @@ -882,7 +1047,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b m = inlvar(n) m = typecheck.Expr(m).(*ir.Name) inlvars[n] = m - delayretvars = false // found a named result parameter (case 3) } else { // anonymous return values, synthesize names for use in assignment that replaces return m = retvar(t, i) @@ -905,61 +1069,23 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b // Assign arguments to the parameters' temp names. as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil) as.Def = true - if n.Op() == ir.OCALLMETH { - sel := n.X.(*ir.SelectorExpr) - if sel.X == nil { - base.Fatalf("method call without receiver: %+v", n) - } - as.Rhs.Append(sel.X) + if call.Op() == ir.OCALLMETH { + base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") } - as.Rhs.Append(n.Args...) - - // For non-dotted calls to variadic functions, we assign the - // variadic parameter's temp name separately. - var vas *ir.AssignStmt + as.Rhs.Append(call.Args...) if recv := fn.Type().Recv(); recv != nil { as.Lhs.Append(inlParam(recv, as, inlvars)) } for _, param := range fn.Type().Params().Fields().Slice() { - // For ordinary parameters or variadic parameters in - // dotted calls, just add the variable to the - // assignment list, and we're done. - if !param.IsDDD() || n.IsDDD { - as.Lhs.Append(inlParam(param, as, inlvars)) - continue - } - - // Otherwise, we need to collect the remaining values - // to pass as a slice. - - x := len(as.Lhs) - for len(as.Lhs) < len(as.Rhs) { - as.Lhs.Append(argvar(param.Type, len(as.Lhs))) - } - varargs := as.Lhs[x:] - - vas = ir.NewAssignStmt(base.Pos, nil, nil) - vas.X = inlParam(param, vas, inlvars) - if len(varargs) == 0 { - vas.Y = typecheck.NodNil() - vas.Y.SetType(param.Type) - } else { - lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(param.Type), nil) - lit.List = varargs - vas.Y = lit - } + as.Lhs.Append(inlParam(param, as, inlvars)) } if len(as.Rhs) != 0 { ninit.Append(typecheck.Stmt(as)) } - if vas != nil { - ninit.Append(typecheck.Stmt(vas)) - } - - if !delayretvars { + if !fn.Inl.CanDelayResults { // Zero the return parameters. for _, n := range retvars { ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name))) @@ -972,40 +1098,21 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b inlgen++ - parent := -1 - if b := base.Ctxt.PosTable.Pos(n.Pos()).Base(); b != nil { - parent = b.InliningIndex() - } - - sym := fn.Linksym() - newIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym) - // Add an inline mark just before the inlined body. // This mark is inline in the code so that it's a reasonable spot // to put a breakpoint. Not sure if that's really necessary or not // (in which case it could go at the end of the function instead). // Note issue 28603. - inlMark := ir.NewInlineMarkStmt(base.Pos, types.BADWIDTH) - inlMark.SetPos(n.Pos().WithIsStmt()) - inlMark.Index = int64(newIndex) - ninit.Append(inlMark) - - if base.Flag.GenDwarfInl > 0 { - if !sym.WasInlined() { - base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn) - sym.Set(obj.AttrWasInlined, true) - } - } + ninit.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(inlIndex))) subst := inlsubst{ - retlabel: retlabel, - retvars: retvars, - delayretvars: delayretvars, - inlvars: inlvars, - defnMarker: ir.NilExpr{}, - bases: make(map[*src.PosBase]*src.PosBase), - newInlIndex: newIndex, - fn: fn, + retlabel: retlabel, + retvars: retvars, + inlvars: inlvars, + defnMarker: ir.NilExpr{}, + bases: make(map[*src.PosBase]*src.PosBase), + newInlIndex: inlIndex, + fn: fn, } subst.edit = subst.node @@ -1014,10 +1121,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b lab := ir.NewLabelStmt(base.Pos, retlabel) body = append(body, lab) - if !typecheck.Go117ExportTypes { - typecheck.Stmts(body) - } - if base.Flag.GenDwarfInl > 0 { for _, v := range inlfvars { v.SetPos(subst.updatedPos(v.Pos())) @@ -1026,26 +1129,11 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b //dumplist("ninit post", ninit); - call := ir.NewInlinedCallExpr(base.Pos, nil, nil) - *call.PtrInit() = ninit - call.Body = body - call.ReturnVars = retvars - call.SetType(n.Type()) - call.SetTypecheck(1) - - // transitive inlining - // might be nice to do this before exporting the body, - // but can't emit the body with inlining expanded. - // instead we emit the things that the body needs - // and each use must redo the inlining. - // luckily these are small. - ir.EditChildren(call, edit) - - if base.Flag.LowerM > 2 { - fmt.Printf("%v: After inlining %+v\n\n", ir.Line(call), call) - } - - return call + res := ir.NewInlinedCallExpr(base.Pos, body, retvars) + res.SetInit(ninit) + res.SetType(call.Type()) + res.SetTypecheck(1) + return res } // Every time we expand a function we generate a new set of tmpnames, @@ -1058,8 +1146,10 @@ func inlvar(var_ *ir.Name) *ir.Name { n := typecheck.NewName(var_.Sym()) n.SetType(var_.Type()) + n.SetTypecheck(1) n.Class = ir.PAUTO n.SetUsed(true) + n.SetAutoTemp(var_.AutoTemp()) n.Curfn = ir.CurFunc // the calling function, not the called one n.SetAddrtaken(var_.Addrtaken()) @@ -1071,18 +1161,7 @@ func inlvar(var_ *ir.Name) *ir.Name { func retvar(t *types.Field, i int) *ir.Name { n := typecheck.NewName(typecheck.LookupNum("~R", i)) n.SetType(t.Type) - n.Class = ir.PAUTO - n.SetUsed(true) - n.Curfn = ir.CurFunc // the calling function, not the called one - ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n) - return n -} - -// Synthesize a variable to store the inlined function's arguments -// when they come from a multiple return call. -func argvar(t *types.Type, i int) ir.Node { - n := typecheck.NewName(typecheck.LookupNum("~arg", i)) - n.SetType(t.Elem()) + n.SetTypecheck(1) n.Class = ir.PAUTO n.SetUsed(true) n.Curfn = ir.CurFunc // the calling function, not the called one @@ -1099,10 +1178,6 @@ type inlsubst struct { // Temporary result variables. retvars []ir.Node - // Whether result variables should be initialized at the - // "return" statement. - delayretvars bool - inlvars map[*ir.Name]*ir.Name // defnMarker is used to mark a Node for reassignment. // inlsubst.clovar set this during creating new ONAME. @@ -1157,17 +1232,21 @@ func (subst *inlsubst) fields(oldt *types.Type) []*types.Field { // clovar creates a new ONAME node for a local variable or param of a closure // inside a function being inlined. func (subst *inlsubst) clovar(n *ir.Name) *ir.Name { - // TODO(danscales): want to get rid of this shallow copy, with code like the - // following, but it is hard to copy all the necessary flags in a maintainable way. - // m := ir.NewNameAt(n.Pos(), n.Sym()) - // m.Class = n.Class - // m.SetType(n.Type()) - // m.SetTypecheck(1) - //if n.IsClosureVar() { - // m.SetIsClosureVar(true) - //} - m := &ir.Name{} - *m = *n + m := ir.NewNameAt(n.Pos(), n.Sym()) + m.Class = n.Class + m.SetType(n.Type()) + m.SetTypecheck(1) + if n.IsClosureVar() { + m.SetIsClosureVar(true) + } + if n.Addrtaken() { + m.SetAddrtaken(true) + } + if n.Used() { + m.SetUsed(true) + } + m.Defn = n.Defn + m.Curfn = subst.newclofn switch defn := n.Defn.(type) { @@ -1200,6 +1279,8 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name { m.Defn = &subst.defnMarker case *ir.TypeSwitchGuard: // TODO(mdempsky): Set m.Defn properly. See discussion on #45743. + case *ir.RangeStmt: + // TODO: Set m.Defn properly if we support inlining range statement in the future. default: base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn) } @@ -1222,38 +1303,22 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name { // closure does the necessary substitions for a ClosureExpr n and returns the new // closure node. func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node { - m := ir.Copy(n) - - // Prior to the subst edit, set a flag in the inlsubst to - // indicated that we don't want to update the source positions in - // the new closure. If we do this, it will appear that the closure - // itself has things inlined into it, which is not the case. See - // issue #46234 for more details. + // Prior to the subst edit, set a flag in the inlsubst to indicate + // that we don't want to update the source positions in the new + // closure function. If we do this, it will appear that the + // closure itself has things inlined into it, which is not the + // case. See issue #46234 for more details. At the same time, we + // do want to update the position in the new ClosureExpr (which is + // part of the function we're working on). See #49171 for an + // example of what happens if we miss that update. + newClosurePos := subst.updatedPos(n.Pos()) defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate) subst.noPosUpdate = true - ir.EditChildren(m, subst.edit) //fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc)) - // The following is similar to funcLit oldfn := n.Func - newfn := ir.NewFunc(oldfn.Pos()) - // These three lines are not strictly necessary, but just to be clear - // that new function needs to redo typechecking and inlinability. - newfn.SetTypecheck(0) - newfn.SetInlinabilityChecked(false) - newfn.Inl = nil - newfn.SetIsHiddenClosure(true) - newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym()) - newfn.Nname.Func = newfn - // Ntype can be nil for -G=3 mode. - if oldfn.Nname.Ntype != nil { - newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype) - } - newfn.Nname.Defn = newfn - - m.(*ir.ClosureExpr).Func = newfn - newfn.OClosure = m.(*ir.ClosureExpr) + newfn := ir.NewClosureFunc(oldfn.Pos(), true) if subst.newclofn != nil { //fmt.Printf("Inlining a closure with a nested closure\n") @@ -1303,13 +1368,10 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node { // Actually create the named function for the closure, now that // the closure is inlined in a specific function. - m.SetTypecheck(0) - if oldfn.ClosureCalled() { - typecheck.Callee(m) - } else { - typecheck.Expr(m) - } - return m + newclo := newfn.OClosure + newclo.SetPos(newClosurePos) + newclo.SetInit(subst.list(n.Init())) + return typecheck.Expr(newclo) } // node recursively copies a node from the saved pristine body of the @@ -1376,7 +1438,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { // Don't do special substitutions if inside a closure break } - // Since we don't handle bodies with closures, + // Because of the above test for subst.newclofn, // this return is guaranteed to belong to the current inlined function. n := n.(*ir.ReturnStmt) init := subst.list(n.Init()) @@ -1391,7 +1453,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { } as.Rhs = subst.list(n.Results) - if subst.delayretvars { + if subst.fn.Inl.CanDelayResults { for _, n := range as.Lhs { as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name))) n.Name().Defn = as @@ -1404,7 +1466,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { typecheck.Stmts(init) return ir.NewBlockStmt(base.Pos, init) - case ir.OGOTO: + case ir.OGOTO, ir.OBREAK, ir.OCONTINUE: if subst.newclofn != nil { // Don't do special substitutions if inside a closure break @@ -1412,9 +1474,8 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { n := n.(*ir.BranchStmt) m := ir.Copy(n).(*ir.BranchStmt) m.SetPos(subst.updatedPos(m.Pos())) - *m.PtrInit() = nil - p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen) - m.Label = typecheck.Lookup(p) + m.SetInit(nil) + m.Label = translateLabel(n.Label) return m case ir.OLABEL: @@ -1425,9 +1486,8 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { n := n.(*ir.LabelStmt) m := ir.Copy(n).(*ir.LabelStmt) m.SetPos(subst.updatedPos(m.Pos())) - *m.PtrInit() = nil - p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen) - m.Label = typecheck.Lookup(p) + m.SetInit(nil) + m.Label = translateLabel(n.Label) return m case ir.OCLOSURE: @@ -1439,6 +1499,31 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { m.SetPos(subst.updatedPos(m.Pos())) ir.EditChildren(m, subst.edit) + if subst.newclofn == nil { + // Translate any label on FOR, RANGE loops, SWITCH or SELECT + switch m.Op() { + case ir.OFOR: + m := m.(*ir.ForStmt) + m.Label = translateLabel(m.Label) + return m + + case ir.ORANGE: + m := m.(*ir.RangeStmt) + m.Label = translateLabel(m.Label) + return m + + case ir.OSWITCH: + m := m.(*ir.SwitchStmt) + m.Label = translateLabel(m.Label) + return m + + case ir.OSELECT: + m := m.(*ir.SelectStmt) + m.Label = translateLabel(m.Label) + return m + } + } + switch m := m.(type) { case *ir.AssignStmt: if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker { @@ -1455,6 +1540,16 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { return m } +// translateLabel makes a label from an inlined function (if non-nil) be unique by +// adding "·inlgen". +func translateLabel(l *types.Sym) *types.Sym { + if l == nil { + return nil + } + p := fmt.Sprintf("%s·%d", l.Name, inlgen) + return typecheck.Lookup(p) +} + func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos { if subst.noPosUpdate { return xpos diff --git a/src/cmd/compile/internal/ir/const.go b/src/cmd/compile/internal/ir/const.go index eaa4d5b6b15ca7..f0b66957f1201b 100644 --- a/src/cmd/compile/internal/ir/const.go +++ b/src/cmd/compile/internal/ir/const.go @@ -26,7 +26,7 @@ func NewString(s string) Node { } const ( - // Maximum size in bits for big.Ints before signalling + // Maximum size in bits for big.Ints before signaling // overflow and also mantissa precision for big.Floats. ConstPrec = 512 ) diff --git a/src/cmd/compile/internal/ir/copy.go b/src/cmd/compile/internal/ir/copy.go index 7da9b24940ff9d..be57a8fbc6d0b6 100644 --- a/src/cmd/compile/internal/ir/copy.go +++ b/src/cmd/compile/internal/ir/copy.go @@ -79,7 +79,7 @@ func DeepCopy(pos src.XPos, n Node) Node { var edit func(Node) Node edit = func(x Node) Node { switch x.Op() { - case OPACK, ONAME, ONONAME, OLITERAL, ONIL, OTYPE: + case ONAME, ONONAME, OLITERAL, ONIL, OTYPE: return x } x = Copy(x) diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index f70645f07913f8..ff315bd0275222 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -119,8 +119,9 @@ func (n *BasicLit) SetVal(val constant.Value) { n.val = val } // or Op(X, Y) for builtin functions that do not become calls. type BinaryExpr struct { miniExpr - X Node - Y Node + X Node + Y Node + RType Node `mknode:"-"` // see reflectdata/helpers.go } func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr { @@ -136,34 +137,22 @@ func (n *BinaryExpr) SetOp(op Op) { panic(n.no("SetOp " + op.String())) case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR, - OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, + OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, OUNSAFESTRING, OEFACE: n.op = op } } -// A CallUse records how the result of the call is used: -type CallUse byte - -const ( - _ CallUse = iota - - CallUseExpr // single expression result is used - CallUseList // list of results are used - CallUseStmt // results not used - call is a statement -) - // A CallExpr is a function call X(Args). type CallExpr struct { miniExpr origNode - X Node - Args Nodes - KeepAlive []*Name // vars to be kept alive until call returns - IsDDD bool - Use CallUse - NoInline bool - PreserveClosure bool // disable directClosureCall for this call + X Node + Args Nodes + RType Node `mknode:"-"` // see reflectdata/helpers.go + KeepAlive []*Name // vars to be kept alive until call returns + IsDDD bool + NoInline bool } func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr { @@ -181,8 +170,12 @@ func (n *CallExpr) SetOp(op Op) { switch op { default: panic(n.no("SetOp " + op.String())) - case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, - OAPPEND, ODELETE, OGETG, OMAKE, OPRINT, OPRINTN, ORECOVER: + case OAPPEND, + OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, + ODELETE, + OGETG, OGETCALLERPC, OGETCALLERSP, + OMAKE, OPRINT, OPRINTN, + ORECOVER, ORECOVERFP: n.op = op } } @@ -192,13 +185,7 @@ type ClosureExpr struct { miniExpr Func *Func `mknode:"-"` Prealloc *Name -} - -func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr { - n := &ClosureExpr{Func: fn} - n.op = OCLOSURE - n.pos = pos - return n + IsGoWrap bool // whether this is wrapper closure of a go statement } // A CompLitExpr is a composite literal Type{Vals}. @@ -206,17 +193,22 @@ func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr { type CompLitExpr struct { miniExpr origNode - Ntype Ntype List Nodes // initialized values + RType Node `mknode:"-"` // *runtime._type for OMAPLIT map types Prealloc *Name - Len int64 // backing array length for OSLICELIT + // For OSLICELIT, Len is the backing array length. + // For OMAPLIT, Len is the number of entries that we've removed from List and + // generated explicit mapassign calls for. This is used to inform the map alloc hint. + Len int64 } -func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr { - n := &CompLitExpr{Ntype: typ} +func NewCompLitExpr(pos src.XPos, op Op, typ *types.Type, list []Node) *CompLitExpr { + n := &CompLitExpr{List: list} n.pos = pos n.SetOp(op) - n.List = list + if typ != nil { + n.SetType(typ) + } n.orig = n return n } @@ -246,7 +238,6 @@ func NewConstExpr(val constant.Value, orig Node) Node { n.orig = orig n.SetType(orig.Type()) n.SetTypecheck(orig.Typecheck()) - n.SetDiag(orig.Diag()) return n } @@ -258,6 +249,27 @@ func (n *ConstExpr) Val() constant.Value { return n.val } type ConvExpr struct { miniExpr X Node + + // For implementing OCONVIFACE expressions. + // + // TypeWord is an expression yielding a *runtime._type or + // *runtime.itab value to go in the type word of the iface/eface + // result. See reflectdata.ConvIfaceTypeWord for further details. + // + // SrcRType is an expression yielding a *runtime._type value for X, + // if it's not pointer-shaped and needs to be heap allocated. + TypeWord Node `mknode:"-"` + SrcRType Node `mknode:"-"` + + // For -d=checkptr instrumentation of conversions from + // unsafe.Pointer to *Elem or *[Len]Elem. + // + // TODO(mdempsky): We only ever need one of these, but currently we + // don't decide which one until walk. Longer term, it probably makes + // sense to have a dedicated IR op for `(*[Len]Elem)(ptr)[:n:m]` + // expressions. + ElemRType Node `mknode:"-"` + ElemElemRType Node `mknode:"-"` } func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr { @@ -277,16 +289,17 @@ func (n *ConvExpr) SetOp(op Op) { switch op { default: panic(n.no("SetOp " + op.String())) - case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR: + case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR: n.op = op } } -// An IndexExpr is an index expression X[Y]. +// An IndexExpr is an index expression X[Index]. type IndexExpr struct { miniExpr X Node Index Node + RType Node `mknode:"-"` // see reflectdata/helpers.go Assigned bool } @@ -323,26 +336,24 @@ func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr { // A StructKeyExpr is an Field: Value composite literal key. type StructKeyExpr struct { miniExpr - Field *types.Sym - Value Node - Offset int64 + Field *types.Field + Value Node } -func NewStructKeyExpr(pos src.XPos, field *types.Sym, value Node) *StructKeyExpr { +func NewStructKeyExpr(pos src.XPos, field *types.Field, value Node) *StructKeyExpr { n := &StructKeyExpr{Field: field, Value: value} n.pos = pos n.op = OSTRUCTKEY - n.Offset = types.BADWIDTH return n } -func (n *StructKeyExpr) Sym() *types.Sym { return n.Field } +func (n *StructKeyExpr) Sym() *types.Sym { return n.Field.Sym } // An InlinedCallExpr is an inlined function call. type InlinedCallExpr struct { miniExpr Body Nodes - ReturnVars Nodes + ReturnVars Nodes // must be side-effect free } func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr { @@ -354,6 +365,21 @@ func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr { return n } +func (n *InlinedCallExpr) SingleResult() Node { + if have := len(n.ReturnVars); have != 1 { + base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have) + } + if !n.Type().HasShape() && n.ReturnVars[0].Type().HasShape() { + // If the type of the call is not a shape, but the type of the return value + // is a shape, we need to do an implicit conversion, so the real type + // of n is maintained. + r := NewConvExpr(n.Pos(), OCONVNOP, n.Type(), n.ReturnVars[0]) + r.SetTypecheck(1) + return r + } + return n.ReturnVars[0] +} + // A LogicalExpr is a expression X Op Y where Op is && or ||. // It is separate from BinaryExpr to make room for statements // that must be executed before Y but after X. @@ -384,8 +410,9 @@ func (n *LogicalExpr) SetOp(op Op) { // but *not* OMAKE (that's a pre-typechecking CallExpr). type MakeExpr struct { miniExpr - Len Node - Cap Node + RType Node `mknode:"-"` // see reflectdata/helpers.go + Len Node + Cap Node } func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr { @@ -408,7 +435,6 @@ func (n *MakeExpr) SetOp(op Op) { // (It may be copied and assigned a type, though.) type NilExpr struct { miniExpr - Sym_ *types.Sym // TODO: Remove } func NewNilExpr(pos src.XPos) *NilExpr { @@ -418,9 +444,6 @@ func NewNilExpr(pos src.XPos) *NilExpr { return n } -func (n *NilExpr) Sym() *types.Sym { return n.Sym_ } -func (n *NilExpr) SetSym(x *types.Sym) { n.Sym_ = x } - // A ParenExpr is a parenthesized expression (X). // It may end up being a value or a type. type ParenExpr struct { @@ -438,14 +461,18 @@ func NewParenExpr(pos src.XPos, x Node) *ParenExpr { func (n *ParenExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } func (n *ParenExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } -func (*ParenExpr) CanBeNtype() {} +// A RawOrigExpr represents an arbitrary Go expression as a string value. +// When printed in diagnostics, the string value is written out exactly as-is. +type RawOrigExpr struct { + miniExpr + Raw string +} -// SetOTYPE changes n to be an OTYPE node returning t, -// like all the type nodes in type.go. -func (n *ParenExpr) SetOTYPE(t *types.Type) { - n.op = OTYPE - n.typ = t - t.SetNod(n) +func NewRawOrigExpr(pos src.XPos, op Op, raw string) *RawOrigExpr { + n := &RawOrigExpr{Raw: raw} + n.pos = pos + n.op = op + return n } // A ResultExpr represents a direct access to a result. @@ -494,10 +521,15 @@ func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type) // A SelectorExpr is a selector expression X.Sel. type SelectorExpr struct { miniExpr - X Node - Sel *types.Sym + X Node + // Sel is the name of the field or method being selected, without (in the + // case of methods) any preceding type specifier. If the field/method is + // exported, than the Sym uses the local package regardless of the package + // of the containing type. + Sel *types.Sym + // The actual selected field - may not be filled in until typechecking. Selection *types.Field - Prealloc *Name // preallocated storage for OCALLPART, if any + Prealloc *Name // preallocated storage for OMETHVALUE, if any } func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr { @@ -511,7 +543,7 @@ func (n *SelectorExpr) SetOp(op Op) { switch op { default: panic(n.no("SetOp " + op.String())) - case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OCALLPART, OMETHEXPR: + case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OMETHVALUE, OMETHEXPR: n.op = op } } @@ -538,10 +570,6 @@ func (n *SelectorExpr) FuncName() *Name { return fn } -// Before type-checking, bytes.Buffer is a SelectorExpr. -// After type-checking it becomes a Name. -func (*SelectorExpr) CanBeNtype() {} - // A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max]. type SliceExpr struct { miniExpr @@ -596,6 +624,21 @@ func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *Slic return n } +// A StringHeaderExpr expression constructs a string header from its parts. +type StringHeaderExpr struct { + miniExpr + Ptr Node + Len Node +} + +func NewStringHeaderExpr(pos src.XPos, ptr, len Node) *StringHeaderExpr { + n := &StringHeaderExpr{Ptr: ptr, Len: len} + n.pos = pos + n.op = OSTRINGHEADER + n.typ = types.Types[types.TSTRING] + return n +} + // A StarExpr is a dereference expression *X. // It may end up being a value or a type. type StarExpr struct { @@ -613,33 +656,24 @@ func NewStarExpr(pos src.XPos, x Node) *StarExpr { func (n *StarExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } -func (*StarExpr) CanBeNtype() {} - -// SetOTYPE changes n to be an OTYPE node returning t, -// like all the type nodes in type.go. -func (n *StarExpr) SetOTYPE(t *types.Type) { - n.op = OTYPE - n.X = nil - n.typ = t - t.SetNod(n) -} - // A TypeAssertionExpr is a selector expression X.(Type). // Before type-checking, the type is Ntype. type TypeAssertExpr struct { miniExpr - X Node - Ntype Ntype + X Node // Runtime type information provided by walkDotType for // assertions from non-empty interface to concrete type. - Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type + ITab Node `mknode:"-"` // *runtime.itab for Type implementing X's type } -func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr { - n := &TypeAssertExpr{X: x, Ntype: typ} +func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr { + n := &TypeAssertExpr{X: x} n.pos = pos n.op = ODOTTYPE + if typ != nil { + n.SetType(typ) + } return n } @@ -652,6 +686,48 @@ func (n *TypeAssertExpr) SetOp(op Op) { } } +// A DynamicTypeAssertExpr asserts that X is of dynamic type RType. +type DynamicTypeAssertExpr struct { + miniExpr + X Node + + // SrcRType is an expression that yields a *runtime._type value + // representing X's type. It's used in failed assertion panic + // messages. + SrcRType Node + + // RType is an expression that yields a *runtime._type value + // representing the asserted type. + // + // BUG(mdempsky): If ITab is non-nil, RType may be nil. + RType Node + + // ITab is an expression that yields a *runtime.itab value + // representing the asserted type within the assertee expression's + // original interface type. + // + // ITab is only used for assertions from non-empty interface type to + // a concrete (i.e., non-interface) type. For all other assertions, + // ITab is nil. + ITab Node +} + +func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, rtype Node) *DynamicTypeAssertExpr { + n := &DynamicTypeAssertExpr{X: x, RType: rtype} + n.pos = pos + n.op = op + return n +} + +func (n *DynamicTypeAssertExpr) SetOp(op Op) { + switch op { + default: + panic(n.no("SetOp " + op.String())) + case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2: + n.op = op + } +} + // A UnaryExpr is a unary expression Op X, // or Op(X) for a builtin function that does not end up being a call. type UnaryExpr struct { @@ -673,19 +749,25 @@ func (n *UnaryExpr) SetOp(op Op) { case OBITNOT, ONEG, ONOT, OPLUS, ORECV, OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW, OOFFSETOF, OPANIC, OREAL, OSIZEOF, - OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, OVARDEF, OVARKILL, OVARLIVE: + OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, + OUNSAFESTRINGDATA, OUNSAFESLICEDATA: n.op = op } } +// Probably temporary: using Implicit() flag to mark generic function nodes that +// are called to make getGfInfo analysis easier in one pre-order pass. +func (n *InstExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } +func (n *InstExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } + // An InstExpr is a generic function or type instantiation. type InstExpr struct { miniExpr X Node - Targs []Node + Targs []Ntype } -func NewInstExpr(pos src.XPos, op Op, x Node, targs []Node) *InstExpr { +func NewInstExpr(pos src.XPos, op Op, x Node, targs []Ntype) *InstExpr { n := &InstExpr{X: x, Targs: targs} n.pos = pos n.op = op @@ -773,6 +855,11 @@ func StaticValue(n Node) Node { continue } + if n.Op() == OINLCALL { + n = n.(*InlinedCallExpr).SingleResult() + continue + } + n1 := staticValue1(n) if n1 == nil { return n @@ -890,11 +977,11 @@ var IsIntrinsicCall = func(*CallExpr) bool { return false } // instead of computing both. SameSafeExpr assumes that l and r are // used in the same statement or expression. In order for it to be // safe to reuse l or r, they must: -// * be the same expression -// * not have side-effects (no function calls, no channel ops); -// however, panics are ok -// * not cause inappropriate aliasing; e.g. two string to []byte -// conversions, must result in two distinct slices +// - be the same expression +// - not have side-effects (no function calls, no channel ops); +// however, panics are ok +// - not cause inappropriate aliasing; e.g. two string to []byte +// conversions, must result in two distinct slices // // The handling of OINDEXMAP is subtle. OINDEXMAP can occur both // as an lvalue (map assignment) and an rvalue (map access). This is @@ -902,6 +989,12 @@ var IsIntrinsicCall = func(*CallExpr) bool { return false } // lvalue expression is for OSLICE and OAPPEND optimizations, and it // is correct in those settings. func SameSafeExpr(l Node, r Node) bool { + for l.Op() == OCONVNOP { + l = l.(*ConvExpr).X + } + for r.Op() == OCONVNOP { + r = r.(*ConvExpr).X + } if l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) { return false } @@ -925,11 +1018,6 @@ func SameSafeExpr(l Node, r Node) bool { r := r.(*UnaryExpr) return SameSafeExpr(l.X, r.X) - case OCONVNOP: - l := l.(*ConvExpr) - r := r.(*ConvExpr) - return SameSafeExpr(l.X, r.X) - case OCONV: l := l.(*ConvExpr) r := r.(*ConvExpr) @@ -964,6 +1052,12 @@ func ShouldCheckPtr(fn *Func, level int) bool { return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0 } +// ShouldAsanCheckPtr reports whether pointer checking should be enabled for +// function fn when -asan is enabled. +func ShouldAsanCheckPtr(fn *Func) bool { + return base.Flag.ASan && fn.Pragma&NoCheckPtr == 0 +} + // IsReflectHeaderDataField reports whether l is an expression p.Data // where p has type reflect.SliceHeader or reflect.StringHeader. func IsReflectHeaderDataField(l Node) bool { @@ -1056,7 +1150,6 @@ func MethodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sy b.WriteString(".") b.WriteString(msym.Name) b.WriteString(suffix) - return rpkg.LookupBytes(b.Bytes()) } @@ -1071,7 +1164,7 @@ func MethodExprName(n Node) *Name { // MethodExprFunc is like MethodExprName, but returns the types.Field instead. func MethodExprFunc(n Node) *types.Field { switch n.Op() { - case ODOTMETH, OMETHEXPR, OCALLPART: + case ODOTMETH, OMETHEXPR, OMETHVALUE: return n.(*SelectorExpr).Selection } base.Fatalf("unexpected node: %v (%v)", n, n.Op()) diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index f2ae0f7606ee7b..d051c88a29e958 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -25,71 +25,73 @@ import ( // Op var OpNames = []string{ - OADDR: "&", - OADD: "+", - OADDSTR: "+", - OALIGNOF: "unsafe.Alignof", - OANDAND: "&&", - OANDNOT: "&^", - OAND: "&", - OAPPEND: "append", - OAS: "=", - OAS2: "=", - OBREAK: "break", - OCALL: "function call", // not actual syntax - OCAP: "cap", - OCASE: "case", - OCLOSE: "close", - OCOMPLEX: "complex", - OBITNOT: "^", - OCONTINUE: "continue", - OCOPY: "copy", - ODELETE: "delete", - ODEFER: "defer", - ODIV: "/", - OEQ: "==", - OFALL: "fallthrough", - OFOR: "for", - OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892 - OGE: ">=", - OGOTO: "goto", - OGT: ">", - OIF: "if", - OIMAG: "imag", - OINLMARK: "inlmark", - ODEREF: "*", - OLEN: "len", - OLE: "<=", - OLSH: "<<", - OLT: "<", - OMAKE: "make", - ONEG: "-", - OMOD: "%", - OMUL: "*", - ONEW: "new", - ONE: "!=", - ONOT: "!", - OOFFSETOF: "unsafe.Offsetof", - OOROR: "||", - OOR: "|", - OPANIC: "panic", - OPLUS: "+", - OPRINTN: "println", - OPRINT: "print", - ORANGE: "range", - OREAL: "real", - ORECV: "<-", - ORECOVER: "recover", - ORETURN: "return", - ORSH: ">>", - OSELECT: "select", - OSEND: "<-", - OSIZEOF: "unsafe.Sizeof", - OSUB: "-", - OSWITCH: "switch", - OUNSAFEADD: "unsafe.Add", - OUNSAFESLICE: "unsafe.Slice", - OXOR: "^", + OADDR: "&", + OADD: "+", + OADDSTR: "+", + OALIGNOF: "unsafe.Alignof", + OANDAND: "&&", + OANDNOT: "&^", + OAND: "&", + OAPPEND: "append", + OAS: "=", + OAS2: "=", + OBREAK: "break", + OCALL: "function call", // not actual syntax + OCAP: "cap", + OCASE: "case", + OCLOSE: "close", + OCOMPLEX: "complex", + OBITNOT: "^", + OCONTINUE: "continue", + OCOPY: "copy", + ODELETE: "delete", + ODEFER: "defer", + ODIV: "/", + OEQ: "==", + OFALL: "fallthrough", + OFOR: "for", + OGE: ">=", + OGOTO: "goto", + OGT: ">", + OIF: "if", + OIMAG: "imag", + OINLMARK: "inlmark", + ODEREF: "*", + OLEN: "len", + OLE: "<=", + OLSH: "<<", + OLT: "<", + OMAKE: "make", + ONEG: "-", + OMOD: "%", + OMUL: "*", + ONEW: "new", + ONE: "!=", + ONOT: "!", + OOFFSETOF: "unsafe.Offsetof", + OOROR: "||", + OOR: "|", + OPANIC: "panic", + OPLUS: "+", + OPRINTN: "println", + OPRINT: "print", + ORANGE: "range", + OREAL: "real", + ORECV: "<-", + ORECOVER: "recover", + ORETURN: "return", + ORSH: ">>", + OSELECT: "select", + OSEND: "<-", + OSIZEOF: "unsafe.Sizeof", + OSUB: "-", + OSWITCH: "switch", + OUNSAFEADD: "unsafe.Add", + OUNSAFESLICE: "unsafe.Slice", + OUNSAFESLICEDATA: "unsafe.SliceData", + OUNSAFESTRING: "unsafe.String", + OUNSAFESTRINGDATA: "unsafe.StringData", + OXOR: "^", } // GoString returns the Go syntax for the Op, or else its name. @@ -105,7 +107,6 @@ func (o Op) GoString() string { // // %v Go syntax ("+", "<-", "print") // %+v Debug syntax ("ADD", "RECV", "PRINT") -// func (o Op) Format(s fmt.State, verb rune) { switch verb { default: @@ -129,7 +130,6 @@ func (o Op) Format(s fmt.State, verb rune) { // %v Go syntax // %L Go syntax followed by " (type T)" if type is known. // %+v Debug syntax, as in Dump. -// func fmtNode(n Node, s fmt.State, verb rune) { // %+v prints Dump. // Otherwise we print Go syntax. @@ -171,101 +171,98 @@ func fmtNode(n Node, s fmt.State, verb rune) { } var OpPrec = []int{ - OALIGNOF: 8, - OAPPEND: 8, - OBYTES2STR: 8, - OARRAYLIT: 8, - OSLICELIT: 8, - ORUNES2STR: 8, - OCALLFUNC: 8, - OCALLINTER: 8, - OCALLMETH: 8, - OCALL: 8, - OCAP: 8, - OCLOSE: 8, - OCOMPLIT: 8, - OCONVIFACE: 8, - OCONVNOP: 8, - OCONV: 8, - OCOPY: 8, - ODELETE: 8, - OGETG: 8, - OLEN: 8, - OLITERAL: 8, - OMAKESLICE: 8, - OMAKESLICECOPY: 8, - OMAKE: 8, - OMAPLIT: 8, - ONAME: 8, - ONEW: 8, - ONIL: 8, - ONONAME: 8, - OOFFSETOF: 8, - OPACK: 8, - OPANIC: 8, - OPAREN: 8, - OPRINTN: 8, - OPRINT: 8, - ORUNESTR: 8, - OSIZEOF: 8, - OSLICE2ARRPTR: 8, - OSTR2BYTES: 8, - OSTR2RUNES: 8, - OSTRUCTLIT: 8, - OTARRAY: 8, - OTSLICE: 8, - OTCHAN: 8, - OTFUNC: 8, - OTINTER: 8, - OTMAP: 8, - OTSTRUCT: 8, - OTYPE: 8, - OUNSAFEADD: 8, - OUNSAFESLICE: 8, - OINDEXMAP: 8, - OINDEX: 8, - OSLICE: 8, - OSLICESTR: 8, - OSLICEARR: 8, - OSLICE3: 8, - OSLICE3ARR: 8, - OSLICEHEADER: 8, - ODOTINTER: 8, - ODOTMETH: 8, - ODOTPTR: 8, - ODOTTYPE2: 8, - ODOTTYPE: 8, - ODOT: 8, - OXDOT: 8, - OCALLPART: 8, - OMETHEXPR: 8, - OPLUS: 7, - ONOT: 7, - OBITNOT: 7, - ONEG: 7, - OADDR: 7, - ODEREF: 7, - ORECV: 7, - OMUL: 6, - ODIV: 6, - OMOD: 6, - OLSH: 6, - ORSH: 6, - OAND: 6, - OANDNOT: 6, - OADD: 5, - OSUB: 5, - OOR: 5, - OXOR: 5, - OEQ: 4, - OLT: 4, - OLE: 4, - OGE: 4, - OGT: 4, - ONE: 4, - OSEND: 3, - OANDAND: 2, - OOROR: 1, + OALIGNOF: 8, + OAPPEND: 8, + OBYTES2STR: 8, + OARRAYLIT: 8, + OSLICELIT: 8, + ORUNES2STR: 8, + OCALLFUNC: 8, + OCALLINTER: 8, + OCALLMETH: 8, + OCALL: 8, + OCAP: 8, + OCLOSE: 8, + OCOMPLIT: 8, + OCONVIFACE: 8, + OCONVIDATA: 8, + OCONVNOP: 8, + OCONV: 8, + OCOPY: 8, + ODELETE: 8, + OGETG: 8, + OLEN: 8, + OLITERAL: 8, + OMAKESLICE: 8, + OMAKESLICECOPY: 8, + OMAKE: 8, + OMAPLIT: 8, + ONAME: 8, + ONEW: 8, + ONIL: 8, + ONONAME: 8, + OOFFSETOF: 8, + OPANIC: 8, + OPAREN: 8, + OPRINTN: 8, + OPRINT: 8, + ORUNESTR: 8, + OSIZEOF: 8, + OSLICE2ARRPTR: 8, + OSTR2BYTES: 8, + OSTR2RUNES: 8, + OSTRUCTLIT: 8, + OTYPE: 8, + OUNSAFEADD: 8, + OUNSAFESLICE: 8, + OUNSAFESLICEDATA: 8, + OUNSAFESTRING: 8, + OUNSAFESTRINGDATA: 8, + OINDEXMAP: 8, + OINDEX: 8, + OSLICE: 8, + OSLICESTR: 8, + OSLICEARR: 8, + OSLICE3: 8, + OSLICE3ARR: 8, + OSLICEHEADER: 8, + OSTRINGHEADER: 8, + ODOTINTER: 8, + ODOTMETH: 8, + ODOTPTR: 8, + ODOTTYPE2: 8, + ODOTTYPE: 8, + ODOT: 8, + OXDOT: 8, + OMETHVALUE: 8, + OMETHEXPR: 8, + OPLUS: 7, + ONOT: 7, + OBITNOT: 7, + ONEG: 7, + OADDR: 7, + ODEREF: 7, + ORECV: 7, + OMUL: 6, + ODIV: 6, + OMOD: 6, + OLSH: 6, + ORSH: 6, + OAND: 6, + OANDNOT: 6, + OADD: 5, + OSUB: 5, + OOR: 5, + OXOR: 5, + OEQ: 4, + OLT: 4, + OLE: 4, + OGE: 4, + OGT: 4, + ONE: 4, + OSEND: 3, + OANDAND: 2, + OOROR: 1, // Statements handled by stmtfmt OAS: -1, @@ -283,7 +280,6 @@ var OpPrec = []int{ ODEFER: -1, OFALL: -1, OFOR: -1, - OFORUNTIL: -1, OGOTO: -1, OIF: -1, OLABEL: -1, @@ -299,7 +295,7 @@ var OpPrec = []int{ // StmtWithInit reports whether op is a statement with an explicit init list. func StmtWithInit(op Op) bool { switch op { - case OIF, OFOR, OFORUNTIL, OSWITCH: + case OIF, OFOR, OSWITCH: return true } return false @@ -385,7 +381,7 @@ func stmtFmt(n Node, s fmt.State) { case OTAILCALL: n := n.(*TailCallStmt) - fmt.Fprintf(s, "tailcall %v", n.Target) + fmt.Fprintf(s, "tailcall %v", n.Call) case OINLMARK: n := n.(*InlineMarkStmt) @@ -410,18 +406,14 @@ func stmtFmt(n Node, s fmt.State) { fmt.Fprintf(s, " else { %v }", n.Else) } - case OFOR, OFORUNTIL: + case OFOR: n := n.(*ForStmt) - opname := "for" - if n.Op() == OFORUNTIL { - opname = "foruntil" - } if !exportFormat { // TODO maybe only if FmtShort, same below - fmt.Fprintf(s, "%s loop", opname) + fmt.Fprintf(s, "for loop") break } - fmt.Fprint(s, opname) + fmt.Fprint(s, "for") if simpleinit { fmt.Fprintf(s, " %v;", n.Init()[0]) } else if n.Post != nil { @@ -438,10 +430,6 @@ func stmtFmt(n Node, s fmt.State) { fmt.Fprint(s, ";") } - if n.Op() == OFORUNTIL && len(n.Late) != 0 { - fmt.Fprintf(s, "; %v", n.Late) - } - fmt.Fprintf(s, " { %v }", n.Body) case ORANGE: @@ -546,7 +534,7 @@ func exprFmt(n Node, s fmt.State, prec int) { n = nn.X continue } - case OCONV, OCONVNOP, OCONVIFACE: + case OCONV, OCONVNOP, OCONVIFACE, OCONVIDATA: nn := nn.(*ConvExpr) if nn.Implicit() { n = nn.X @@ -558,7 +546,7 @@ func exprFmt(n Node, s fmt.State, prec int) { } nprec := OpPrec[n.Op()] - if n.Op() == OTYPE && n.Type().IsPtr() { + if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() { nprec = OpPrec[ODEREF] } @@ -567,6 +555,11 @@ func exprFmt(n Node, s fmt.State, prec int) { return } + if n, ok := n.(*RawOrigExpr); ok { + fmt.Fprint(s, n.Raw) + return + } + switch n.Op() { case OPAREN: n := n.(*ParenExpr) @@ -634,7 +627,7 @@ func exprFmt(n Node, s fmt.State, prec int) { return } fallthrough - case OPACK, ONONAME: + case ONONAME: fmt.Fprint(s, n.Sym()) case OLINKSYMOFFSET: @@ -648,52 +641,6 @@ func exprFmt(n Node, s fmt.State, prec int) { } fmt.Fprintf(s, "%v", n.Type()) - case OTSLICE: - n := n.(*SliceType) - if n.DDD { - fmt.Fprintf(s, "...%v", n.Elem) - } else { - fmt.Fprintf(s, "[]%v", n.Elem) // happens before typecheck - } - - case OTARRAY: - n := n.(*ArrayType) - if n.Len == nil { - fmt.Fprintf(s, "[...]%v", n.Elem) - } else { - fmt.Fprintf(s, "[%v]%v", n.Len, n.Elem) - } - - case OTMAP: - n := n.(*MapType) - fmt.Fprintf(s, "map[%v]%v", n.Key, n.Elem) - - case OTCHAN: - n := n.(*ChanType) - switch n.Dir { - case types.Crecv: - fmt.Fprintf(s, "<-chan %v", n.Elem) - - case types.Csend: - fmt.Fprintf(s, "chan<- %v", n.Elem) - - default: - if n.Elem != nil && n.Elem.Op() == OTCHAN && n.Elem.(*ChanType).Dir == types.Crecv { - fmt.Fprintf(s, "chan (%v)", n.Elem) - } else { - fmt.Fprintf(s, "chan %v", n.Elem) - } - } - - case OTSTRUCT: - fmt.Fprint(s, "") - - case OTINTER: - fmt.Fprint(s, "") - - case OTFUNC: - fmt.Fprint(s, "") - case OCLOSURE: n := n.(*ClosureExpr) if !exportFormat { @@ -709,15 +656,14 @@ func exprFmt(n Node, s fmt.State, prec int) { fmt.Fprintf(s, "... argument") return } - if n.Ntype != nil { - fmt.Fprintf(s, "%v{%s}", n.Ntype, ellipsisIf(len(n.List) != 0)) + if typ := n.Type(); typ != nil { + fmt.Fprintf(s, "%v{%s}", typ, ellipsisIf(len(n.List) != 0)) return } - fmt.Fprint(s, "composite literal") return } - fmt.Fprintf(s, "(%v{ %.v })", n.Ntype, n.List) + fmt.Fprintf(s, "(%v{ %.v })", n.Type(), n.List) case OPTRLIT: n := n.(*AddrExpr) @@ -752,7 +698,7 @@ func exprFmt(n Node, s fmt.State, prec int) { n := n.(*StructKeyExpr) fmt.Fprintf(s, "%v:%v", n.Field, n.Value) - case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OCALLPART, OMETHEXPR: + case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OMETHVALUE, OMETHEXPR: n := n.(*SelectorExpr) exprFmt(n.X, s, nprec) if n.Sel == nil { @@ -764,10 +710,6 @@ func exprFmt(n Node, s fmt.State, prec int) { case ODOTTYPE, ODOTTYPE2: n := n.(*TypeAssertExpr) exprFmt(n.X, s, nprec) - if n.Ntype != nil { - fmt.Fprintf(s, ".(%v)", n.Ntype) - return - } fmt.Fprintf(s, ".(%v)", n.Type()) case OINDEX, OINDEXMAP: @@ -804,6 +746,7 @@ func exprFmt(n Node, s fmt.State, prec int) { case OCONV, OCONVIFACE, + OCONVIDATA, OCONVNOP, OBYTES2STR, ORUNES2STR, @@ -854,6 +797,15 @@ func exprFmt(n Node, s fmt.State, prec int) { } fmt.Fprintf(s, "(%.v)", n.Args) + case OINLCALL: + n := n.(*InlinedCallExpr) + // TODO(mdempsky): Print Init and/or Body? + if len(n.ReturnVars) == 1 { + fmt.Fprintf(s, "%v", n.ReturnVars[0]) + return + } + fmt.Fprintf(s, "(.%v)", n.ReturnVars) + case OMAKEMAP, OMAKECHAN, OMAKESLICE: n := n.(*MakeExpr) if n.Cap != nil { @@ -956,7 +908,6 @@ func ellipsisIf(b bool) string { // %v Go syntax, semicolon-separated // %.v Go syntax, comma-separated // %+v Debug syntax, as in DumpList. -// func (l Nodes) Format(s fmt.State, verb rune) { if s.Flag('+') && verb == 'v' { // %+v is DumpList output @@ -986,7 +937,7 @@ func (l Nodes) Format(s fmt.State, verb rune) { // Dump prints the message s followed by a debug dump of n. func Dump(s string, n Node) { - fmt.Printf("%s [%p]%+v\n", s, n, n) + fmt.Printf("%s%+v\n", s, n) } // DumpList prints the message s followed by a debug dump of each node in the list. @@ -1041,8 +992,8 @@ func dumpNodeHeader(w io.Writer, n Node) { } } - if n.Typecheck() != 0 { - fmt.Fprintf(w, " tc(%d)", n.Typecheck()) + if n.Sym() != nil && n.Op() != ONAME && n.Op() != ONONAME && n.Op() != OTYPE { + fmt.Fprintf(w, " %+v", n.Sym()) } // Print Node-specific fields of basic type in header line. @@ -1112,18 +1063,27 @@ func dumpNodeHeader(w io.Writer, n Node) { } fmt.Fprintf(w, " %+v", n.Type()) } + if n.Typecheck() != 0 { + fmt.Fprintf(w, " tc(%d)", n.Typecheck()) + } if n.Pos().IsKnown() { - pfx := "" + fmt.Fprint(w, " # ") switch n.Pos().IsStmt() { case src.PosNotStmt: - pfx = "_" // "-" would be confusing + fmt.Fprint(w, "_") // "-" would be confusing case src.PosIsStmt: - pfx = "+" + fmt.Fprint(w, "+") + } + for i, pos := range base.Ctxt.AllPos(n.Pos(), nil) { + if i > 0 { + fmt.Fprint(w, ",") + } + // TODO(mdempsky): Print line pragma details too. + file := filepath.Base(pos.Filename()) + // Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync. + fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col()) } - pos := base.Ctxt.PosTable.Pos(n.Pos()) - file := filepath.Base(pos.Filename()) - fmt.Fprintf(w, " # %s%s:%d", pfx, file, pos.Line()) } } @@ -1162,13 +1122,17 @@ func dumpNode(w io.Writer, n Node, depth int) { fmt.Fprintf(w, "%+v", n.Op()) } dumpNodeHeader(w, n) - if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { - indent(w, depth) - fmt.Fprintf(w, "%+v-ntype", n.Op()) - dumpNode(w, n.Name().Ntype, depth+1) - } return + case OLINKSYMOFFSET: + n := n.(*LinksymOffsetExpr) + fmt.Fprintf(w, "%+v-%v", n.Op(), n.Linksym) + // Offset is almost always 0, so only print when it's interesting. + if n.Offset_ != 0 { + fmt.Fprintf(w, "%+v", n.Offset_) + } + dumpNodeHeader(w, n) + case OASOP: n := n.(*AssignOpStmt) fmt.Fprintf(w, "%+v-%+v", n.Op(), n.AsOp) @@ -1177,11 +1141,6 @@ func dumpNode(w io.Writer, n Node, depth int) { case OTYPE: fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym()) dumpNodeHeader(w, n) - if n.Type() == nil && n.Name() != nil && n.Name().Ntype != nil { - indent(w, depth) - fmt.Fprintf(w, "%+v-ntype", n.Op()) - dumpNode(w, n.Name().Ntype, depth+1) - } return case OCLOSURE: @@ -1222,13 +1181,6 @@ func dumpNode(w io.Writer, n Node, depth int) { return } - if n.Sym() != nil { - fmt.Fprintf(w, " %+v", n.Sym()) - } - if n.Type() != nil { - fmt.Fprintf(w, " %+v", n.Type()) - } - v := reflect.ValueOf(n).Elem() t := reflect.TypeOf(n).Elem() nf := t.NumField() diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go index 20fe965711df33..2bbacfc2c3cee3 100644 --- a/src/cmd/compile/internal/ir/func.go +++ b/src/cmd/compile/internal/ir/func.go @@ -9,6 +9,7 @@ import ( "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/src" + "fmt" ) // A Func corresponds to a single function in a Go program @@ -30,8 +31,7 @@ import ( // using a special data structure passed in a register. // // A method declaration is represented like functions, except f.Sym -// will be the qualified method name (e.g., "T.m") and -// f.Func.Shortname is the bare method name (e.g., "m"). +// will be the qualified method name (e.g., "T.m"). // // A method expression (T.M) is represented as an OMETHEXPR node, // in which n.Left and n.Right point to the type and method, respectively. @@ -39,24 +39,21 @@ import ( // constructs a fresh node. // // A method value (t.M) is represented by ODOTMETH/ODOTINTER -// when it is called directly and by OCALLPART otherwise. +// when it is called directly and by OMETHVALUE otherwise. // These are like method expressions, except that for ODOTMETH/ODOTINTER, // the method name is stored in Sym instead of Right. -// Each OCALLPART ends up being implemented as a new +// Each OMETHVALUE ends up being implemented as a new // function, a bit like a closure, with its own ODCLFUNC. -// The OCALLPART uses n.Func to record the linkage to +// The OMETHVALUE uses n.Func to record the linkage to // the generated ODCLFUNC, but there is no -// pointer from the Func back to the OCALLPART. +// pointer from the Func back to the OMETHVALUE. type Func struct { miniNode Body Nodes - Iota int64 Nname *Name // ONAME node OClosure *ClosureExpr // OCLOSURE node - Shortname *types.Sym - // Extra entry code for the function. For example, allocate and initialize // memory for escaping parameters. Enter Nodes @@ -132,13 +129,16 @@ type Func struct { // function for go:nowritebarrierrec analysis. Only filled in // if nowritebarrierrecCheck != nil. NWBRCalls *[]SymAndPos + + // For wrapper functions, WrappedFunc point to the original Func. + // Currently only used for go/defer wrappers. + WrappedFunc *Func } func NewFunc(pos src.XPos) *Func { f := new(Func) f.pos = pos f.op = ODCLFUNC - f.Iota = -1 // Most functions are ABIInternal. The importer or symabis // pass may override this. f.ABI = obj.ABIInternal @@ -166,6 +166,11 @@ type Inline struct { // another package is imported. Dcl []*Name Body []Node + + // CanDelayResults reports whether it's safe for the inliner to delay + // initializing the result parameters until immediately before the + // "return" statement. + CanDelayResults bool } // A Mark represents a scope boundary. @@ -190,13 +195,14 @@ const ( // true if closure inside a function; false if a simple function or a // closure in a global variable initialization funcIsHiddenClosure + funcIsDeadcodeClosure // true if closure is deadcode funcHasDefer // contains a defer statement funcNilCheckDisabled // disable nil checks when compiling this function funcInlinabilityChecked // inliner has already determined whether the function is inlinable funcExportInline // include inline body in export data - funcInstrumentBody // add race/msan instrumentation during SSA construction + funcInstrumentBody // add race/msan/asan instrumentation during SSA construction funcOpenCodedDeferDisallowed // can't do open-coded defers - funcClosureCalled // closure is only immediately called + funcClosureCalled // closure is only immediately called; used by escape analysis ) type SymAndPos struct { @@ -210,6 +216,7 @@ func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper ! func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 } func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 } func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 } +func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 } func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 } func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 } func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 } @@ -224,6 +231,7 @@ func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper, func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) } func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) } func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) } +func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) } func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) } func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) } func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) } @@ -260,18 +268,22 @@ func PkgFuncName(f *Func) string { s := f.Sym() pkg := s.Pkg - p := base.Ctxt.Pkgpath - if pkg != nil && pkg.Path != "" { - p = pkg.Path - } - if p == "" { - return s.Name - } - return p + "." + s.Name + return pkg.Path + "." + s.Name } var CurFunc *Func +// WithFunc invokes do with CurFunc and base.Pos set to curfn and +// curfn.Pos(), respectively, and then restores their previous values +// before returning. +func WithFunc(curfn *Func, do func()) { + oldfn, oldpos := CurFunc, base.Pos + defer func() { CurFunc, base.Pos = oldfn, oldpos }() + + CurFunc, base.Pos = curfn, curfn.Pos() + do() +} + func FuncSymName(s *types.Sym) string { return s.Name + "·f" } @@ -279,7 +291,7 @@ func FuncSymName(s *types.Sym) string { // MarkFunc marks a node as a function. func MarkFunc(n *Name) { if n.Op() != ONAME || n.Class != Pxxx { - base.Fatalf("expected ONAME/Pxxx node, got %v", n) + base.FatalfAt(n.Pos(), "expected ONAME/Pxxx node, got %v (%v/%v)", n, n.Op(), n.Class) } n.Class = PFUNC @@ -296,8 +308,8 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) { base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars) } } - if base.Flag.CompilingRuntime && clo.Esc() == EscHeap { - base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime") + if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap { + base.ErrorfAt(clo.Pos(), "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func)) } } @@ -306,3 +318,111 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) { func IsTrivialClosure(clo *ClosureExpr) bool { return len(clo.Func.ClosureVars) == 0 } + +// globClosgen is like Func.Closgen, but for the global scope. +var globClosgen int32 + +// closureName generates a new unique name for a closure within outerfn. +func closureName(outerfn *Func) *types.Sym { + pkg := types.LocalPkg + outer := "glob." + prefix := "func" + gen := &globClosgen + + if outerfn != nil { + if outerfn.OClosure != nil { + prefix = "" + } + + pkg = outerfn.Sym().Pkg + outer = FuncName(outerfn) + + // There may be multiple functions named "_". In those + // cases, we can't use their individual Closgens as it + // would lead to name clashes. + if !IsBlank(outerfn.Nname) { + gen = &outerfn.Closgen + } + } + + *gen++ + return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) +} + +// NewClosureFunc creates a new Func to represent a function literal. +// If hidden is true, then the closure is marked hidden (i.e., as a +// function literal contained within another function, rather than a +// package-scope variable initialization expression). +func NewClosureFunc(pos src.XPos, hidden bool) *Func { + fn := NewFunc(pos) + fn.SetIsHiddenClosure(hidden) + + fn.Nname = NewNameAt(pos, BlankNode.Sym()) + fn.Nname.Func = fn + fn.Nname.Defn = fn + + fn.OClosure = &ClosureExpr{Func: fn} + fn.OClosure.op = OCLOSURE + fn.OClosure.pos = pos + + return fn +} + +// NameClosure generates a unique for the given function literal, +// which must have appeared within outerfn. +func NameClosure(clo *ClosureExpr, outerfn *Func) { + fn := clo.Func + if fn.IsHiddenClosure() != (outerfn != nil) { + base.FatalfAt(clo.Pos(), "closure naming inconsistency: hidden %v, but outer %v", fn.IsHiddenClosure(), outerfn) + } + + name := fn.Nname + if !IsBlank(name) { + base.FatalfAt(clo.Pos(), "closure already named: %v", name) + } + + name.SetSym(closureName(outerfn)) + MarkFunc(name) +} + +// UseClosure checks that the ginen function literal has been setup +// correctly, and then returns it as an expression. +// It must be called after clo.Func.ClosureVars has been set. +func UseClosure(clo *ClosureExpr, pkg *Package) Node { + fn := clo.Func + name := fn.Nname + + if IsBlank(name) { + base.FatalfAt(fn.Pos(), "unnamed closure func: %v", fn) + } + // Caution: clo.Typecheck() is still 0 when UseClosure is called by + // tcClosure. + if fn.Typecheck() != 1 || name.Typecheck() != 1 { + base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn) + } + if clo.Type() == nil || name.Type() == nil { + base.FatalfAt(fn.Pos(), "missing types: %v", fn) + } + if !types.Identical(clo.Type(), name.Type()) { + base.FatalfAt(fn.Pos(), "mismatched types: %v", fn) + } + + if base.Flag.W > 1 { + s := fmt.Sprintf("new closure func: %v", fn) + Dump(s, fn) + } + + if pkg != nil { + pkg.Decls = append(pkg.Decls, fn) + } + + if false && IsTrivialClosure(clo) { + // TODO(mdempsky): Investigate if we can/should optimize this + // case. walkClosure already handles it later, but it could be + // useful to recognize earlier (e.g., it might allow multiple + // inlined calls to a function to share a common trivial closure + // func, rather than cloning it for each inlined call). + } + + return clo +} diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go index a7ff4ac9c77a1a..52c622df2306e6 100644 --- a/src/cmd/compile/internal/ir/mini.go +++ b/src/cmd/compile/internal/ir/mini.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. -//go:generate go run -mod=mod mknode.go +//go:generate go run mknode.go package ir @@ -27,7 +27,6 @@ import ( // The embedding struct should also fill in n.op in its constructor, // for more useful panic messages when invalid methods are called, // instead of implementing Op itself. -// type miniNode struct { pos src.XPos // uint32 op Op // uint8 @@ -54,23 +53,18 @@ func (n *miniNode) Esc() uint16 { return n.esc } func (n *miniNode) SetEsc(x uint16) { n.esc = x } const ( - miniWalkdefShift = 0 // TODO(mdempsky): Move to Name.flags. - miniTypecheckShift = 2 - miniDiag = 1 << 4 - miniWalked = 1 << 5 // to prevent/catch re-walking + miniTypecheckShift = 0 + miniWalked = 1 << 2 // to prevent/catch re-walking ) func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) } func (n *miniNode) SetTypecheck(x uint8) { - if x > 3 { + if x > 2 { panic(fmt.Sprintf("cannot SetTypecheck %d", x)) } n.bits.set2(miniTypecheckShift, x) } -func (n *miniNode) Diag() bool { return n.bits&miniDiag != 0 } -func (n *miniNode) SetDiag(x bool) { n.bits.set(miniDiag, x) } - func (n *miniNode) Walked() bool { return n.bits&miniWalked != 0 } func (n *miniNode) SetWalked(x bool) { n.bits.set(miniWalked, x) } diff --git a/src/cmd/compile/internal/ir/mknode.go b/src/cmd/compile/internal/ir/mknode.go index 5a0aaadf16fa66..d815195721f215 100644 --- a/src/cmd/compile/internal/ir/mknode.go +++ b/src/cmd/compile/internal/ir/mknode.go @@ -1,229 +1,348 @@ -// Copyright 2020 The Go Authors. All rights reserved. +// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build ignore // +build ignore +// Note: this program must be run in this directory. +// go run mknode.go + package main import ( "bytes" "fmt" + "go/ast" "go/format" - "go/types" + "go/parser" + "go/token" + "io/fs" "io/ioutil" "log" - "reflect" "sort" "strings" - - "golang.org/x/tools/go/packages" ) -var irPkg *types.Package +var fset = token.NewFileSet() + var buf bytes.Buffer -func main() { - cfg := &packages.Config{ - Mode: packages.NeedSyntax | packages.NeedTypes, +// concreteNodes contains all concrete types in the package that implement Node +// (except for the mini* types). +var concreteNodes []*ast.TypeSpec + +// interfaceNodes contains all interface types in the package that implement Node. +var interfaceNodes []*ast.TypeSpec + +// mini contains the embeddable mini types (miniNode, miniExpr, and miniStmt). +var mini = map[string]*ast.TypeSpec{} + +// implementsNode reports whether the type t is one which represents a Node +// in the AST. +func implementsNode(t ast.Expr) bool { + id, ok := t.(*ast.Ident) + if !ok { + return false // only named types } - pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir") - if err != nil { - log.Fatal(err) + for _, ts := range interfaceNodes { + if ts.Name.Name == id.Name { + return true + } + } + for _, ts := range concreteNodes { + if ts.Name.Name == id.Name { + return true + } } - irPkg = pkgs[0].Types + return false +} + +func isMini(t ast.Expr) bool { + id, ok := t.(*ast.Ident) + return ok && mini[id.Name] != nil +} + +func isNamedType(t ast.Expr, name string) bool { + if id, ok := t.(*ast.Ident); ok { + if id.Name == name { + return true + } + } + return false +} +func main() { fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.") fmt.Fprintln(&buf) fmt.Fprintln(&buf, "package ir") fmt.Fprintln(&buf) fmt.Fprintln(&buf, `import "fmt"`) - scope := irPkg.Scope() - for _, name := range scope.Names() { - if strings.HasPrefix(name, "mini") { - continue - } - - obj, ok := scope.Lookup(name).(*types.TypeName) - if !ok { - continue - } - typ := obj.Type().(*types.Named) - if !implementsNode(types.NewPointer(typ)) { - continue + filter := func(file fs.FileInfo) bool { + return !strings.HasPrefix(file.Name(), "mknode") + } + pkgs, err := parser.ParseDir(fset, ".", filter, 0) + if err != nil { + panic(err) + } + pkg := pkgs["ir"] + + // Find all the mini types. These let us determine which + // concrete types implement Node, so we need to find them first. + for _, f := range pkg.Files { + for _, d := range f.Decls { + g, ok := d.(*ast.GenDecl) + if !ok { + continue + } + for _, s := range g.Specs { + t, ok := s.(*ast.TypeSpec) + if !ok { + continue + } + if strings.HasPrefix(t.Name.Name, "mini") { + mini[t.Name.Name] = t + // Double-check that it is or embeds miniNode. + if t.Name.Name != "miniNode" { + s := t.Type.(*ast.StructType) + if !isNamedType(s.Fields.List[0].Type, "miniNode") { + panic(fmt.Sprintf("can't find miniNode in %s", t.Name.Name)) + } + } + } + } } + } - fmt.Fprintf(&buf, "\n") - fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name) - - switch name { - case "Name", "Func": - // Too specialized to automate. - continue + // Find all the declarations of concrete types that implement Node. + for _, f := range pkg.Files { + for _, d := range f.Decls { + g, ok := d.(*ast.GenDecl) + if !ok { + continue + } + for _, s := range g.Specs { + t, ok := s.(*ast.TypeSpec) + if !ok { + continue + } + if strings.HasPrefix(t.Name.Name, "mini") { + // We don't treat the mini types as + // concrete implementations of Node + // (even though they are) because + // we only use them by embedding them. + continue + } + if isConcreteNode(t) { + concreteNodes = append(concreteNodes, t) + } + if isInterfaceNode(t) { + interfaceNodes = append(interfaceNodes, t) + } + } } - - forNodeFields(typ, - "func (n *%[1]s) copy() Node { c := *n\n", - "", - "c.%[1]s = copy%[2]s(c.%[1]s)", - "return &c }\n") - - forNodeFields(typ, - "func (n *%[1]s) doChildren(do func(Node) bool) bool {\n", - "if n.%[1]s != nil && do(n.%[1]s) { return true }", - "if do%[2]s(n.%[1]s, do) { return true }", - "return false }\n") - - forNodeFields(typ, - "func (n *%[1]s) editChildren(edit func(Node) Node) {\n", - "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }", - "edit%[2]s(n.%[1]s, edit)", - "}\n") } + // Sort for deterministic output. + sort.Slice(concreteNodes, func(i, j int) bool { + return concreteNodes[i].Name.Name < concreteNodes[j].Name.Name + }) + // Generate code for each concrete type. + for _, t := range concreteNodes { + processType(t) + } + // Add some helpers. + generateHelpers() - makeHelpers() - + // Format and write output. out, err := format.Source(buf.Bytes()) if err != nil { // write out mangled source so we can see the bug. out = buf.Bytes() } - err = ioutil.WriteFile("node_gen.go", out, 0666) if err != nil { log.Fatal(err) } } -// needHelper maps needed slice helpers from their base name to their -// respective slice-element type. -var needHelper = map[string]string{} - -func makeHelpers() { - var names []string - for name := range needHelper { - names = append(names, name) +// isConcreteNode reports whether the type t is a concrete type +// implementing Node. +func isConcreteNode(t *ast.TypeSpec) bool { + s, ok := t.Type.(*ast.StructType) + if !ok { + return false } - sort.Strings(names) - - for _, name := range names { - fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name]) + for _, f := range s.Fields.List { + if isMini(f.Type) { + return true + } } + return false } -const sliceHelperTmpl = ` -func copy%[1]s(list []%[2]s) []%[2]s { - if list == nil { - return nil +// isInterfaceNode reports whether the type t is an interface type +// implementing Node (including Node itself). +func isInterfaceNode(t *ast.TypeSpec) bool { + s, ok := t.Type.(*ast.InterfaceType) + if !ok { + return false } - c := make([]%[2]s, len(list)) - copy(c, list) - return c -} -func do%[1]s(list []%[2]s, do func(Node) bool) bool { - for _, x := range list { - if x != nil && do(x) { + if t.Name.Name == "Node" { + return true + } + if t.Name.Name == "OrigNode" || t.Name.Name == "InitNode" { + // These we exempt from consideration (fields of + // this type don't need to be walked or copied). + return false + } + + // Look for embedded Node type. + // Note that this doesn't handle multi-level embedding, but + // we have none of that at the moment. + for _, f := range s.Methods.List { + if len(f.Names) != 0 { + continue + } + if isNamedType(f.Type, "Node") { return true } } return false } -func edit%[1]s(list []%[2]s, edit func(Node) Node) { - for i, x := range list { - if x != nil { - list[i] = edit(x).(%[2]s) - } + +func processType(t *ast.TypeSpec) { + name := t.Name.Name + fmt.Fprintf(&buf, "\n") + fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name) + + switch name { + case "Name", "Func": + // Too specialized to automate. + return } -} -` -func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) { - fmt.Fprintf(&buf, prologue, named.Obj().Name()) + s := t.Type.(*ast.StructType) + fields := s.Fields.List - anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool { - if f.Embedded() { - return false + // Expand any embedded fields. + for i := 0; i < len(fields); i++ { + f := fields[i] + if len(f.Names) != 0 { + continue // not embedded } - name, typ := f.Name(), f.Type() - - slice, _ := typ.Underlying().(*types.Slice) - if slice != nil { - typ = slice.Elem() + if isMini(f.Type) { + // Insert the fields of the embedded type into the main type. + // (It would be easier just to append, but inserting in place + // matches the old mknode behavior.) + ss := mini[f.Type.(*ast.Ident).Name].Type.(*ast.StructType) + var f2 []*ast.Field + f2 = append(f2, fields[:i]...) + f2 = append(f2, ss.Fields.List...) + f2 = append(f2, fields[i+1:]...) + fields = f2 + i-- + continue + } else if isNamedType(f.Type, "origNode") { + // Ignore this field + copy(fields[i:], fields[i+1:]) + fields = fields[:len(fields)-1] + i-- + continue + } else { + panic("unknown embedded field " + fmt.Sprintf("%v", f.Type)) } - - tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg)) - if implementsNode(typ) { - if slice != nil { - helper := strings.TrimPrefix(what, "*") + "s" - needHelper[helper] = what - tmpl, what = sliceTmpl, helper - } - } else if what == "*Field" { - // Special case for *Field. - tmpl = sliceTmpl - if slice != nil { - what = "Fields" - } else { - what = "Field" + } + // Process fields. + var copyBody strings.Builder + var doChildrenBody strings.Builder + var editChildrenBody strings.Builder + for _, f := range fields { + if f.Tag != nil { + tag := f.Tag.Value[1 : len(f.Tag.Value)-1] + if strings.HasPrefix(tag, "mknode:") { + if tag[7:] == "\"-\"" { + continue + } + panic(fmt.Sprintf("unexpected tag value: %s", tag)) } - } else { - return false } - - if tmpl == "" { - return false + names := f.Names + ft := f.Type + if isNamedType(ft, "Nodes") { + // Nodes == []Node + ft = &ast.ArrayType{Elt: &ast.Ident{Name: "Node"}} } - - // Allow template to not use all arguments without - // upsetting fmt.Printf. - s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what) - fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")]) - return false - }) - - fmt.Fprintf(&buf, epilogue) -} - -func implementsNode(typ types.Type) bool { - if _, ok := typ.Underlying().(*types.Interface); ok { - // TODO(mdempsky): Check the interface implements Node. - // Worst case, node_gen.go will fail to compile if we're wrong. - return true - } - - if ptr, ok := typ.(*types.Pointer); ok { - if str, ok := ptr.Elem().Underlying().(*types.Struct); ok { - return anyField(str, func(f *types.Var) bool { - return f.Embedded() && f.Name() == "miniNode" - }) + isSlice := false + if a, ok := ft.(*ast.ArrayType); ok && a.Len == nil { + isSlice = true + ft = a.Elt } - } - - return false -} - -func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool { - for i, n := 0, typ.NumFields(); i < n; i++ { - if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok { - if value != "-" { - panic(fmt.Sprintf("unexpected tag value: %q", value)) - } - continue + isPtr := false + if p, ok := ft.(*ast.StarExpr); ok { + isPtr = true + ft = p.X } - - f := typ.Field(i) - if pred(f) { - return true + if !implementsNode(ft) { + continue } - if f.Embedded() { - if typ, ok := f.Type().Underlying().(*types.Struct); ok { - if anyField(typ, pred) { - return true + for _, name := range names { + if isSlice { + fmt.Fprintf(©Body, "c.%s = copy%ss(c.%s)\n", name, ft, name) + fmt.Fprintf(&doChildrenBody, + "if do%ss(n.%s, do) {\nreturn true\n}\n", ft, name) + fmt.Fprintf(&editChildrenBody, + "edit%ss(n.%s, edit)\n", ft, name) + } else { + fmt.Fprintf(&doChildrenBody, + "if n.%s != nil && do(n.%s) {\nreturn true\n}\n", name, name) + ptr := "" + if isPtr { + ptr = "*" } + fmt.Fprintf(&editChildrenBody, + "if n.%s != nil {\nn.%s = edit(n.%s).(%s%s)\n}\n", name, name, name, ptr, ft) } } } - return false + fmt.Fprintf(&buf, "func (n *%s) copy() Node {\nc := *n\n", name) + buf.WriteString(copyBody.String()) + fmt.Fprintf(&buf, "return &c\n}\n") + fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) bool) bool {\n", name) + buf.WriteString(doChildrenBody.String()) + fmt.Fprintf(&buf, "return false\n}\n") + fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name) + buf.WriteString(editChildrenBody.String()) + fmt.Fprintf(&buf, "}\n") +} + +func generateHelpers() { + for _, typ := range []string{"CaseClause", "CommClause", "Name", "Node", "Ntype"} { + ptr := "*" + if typ == "Node" || typ == "Ntype" { + ptr = "" // interfaces don't need * + } + fmt.Fprintf(&buf, "\n") + fmt.Fprintf(&buf, "func copy%ss(list []%s%s) []%s%s {\n", typ, ptr, typ, ptr, typ) + fmt.Fprintf(&buf, "if list == nil { return nil }\n") + fmt.Fprintf(&buf, "c := make([]%s%s, len(list))\n", ptr, typ) + fmt.Fprintf(&buf, "copy(c, list)\n") + fmt.Fprintf(&buf, "return c\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "func do%ss(list []%s%s, do func(Node) bool) bool {\n", typ, ptr, typ) + fmt.Fprintf(&buf, "for _, x := range list {\n") + fmt.Fprintf(&buf, "if x != nil && do(x) {\n") + fmt.Fprintf(&buf, "return true\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "return false\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "func edit%ss(list []%s%s, edit func(Node) Node) {\n", typ, ptr, typ) + fmt.Fprintf(&buf, "for i, x := range list {\n") + fmt.Fprintf(&buf, "if x != nil {\n") + fmt.Fprintf(&buf, "list[i] = edit(x).(%s%s)\n", ptr, typ) + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + fmt.Fprintf(&buf, "}\n") + } } diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go index b6c68bc5e01a4b..310481f6f0579e 100644 --- a/src/cmd/compile/internal/ir/name.go +++ b/src/cmd/compile/internal/ir/name.go @@ -31,8 +31,6 @@ func NewIdent(pos src.XPos, sym *types.Sym) *Ident { func (n *Ident) Sym() *types.Sym { return n.sym } -func (*Ident) CanBeNtype() {} - // Name holds Node fields used only by named nodes (ONAME, OTYPE, some OLITERAL). type Name struct { miniExpr @@ -40,6 +38,7 @@ type Name struct { Class Class // uint8 pragma PragmaFlag // int16 flags bitset16 + DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1 sym *types.Sym Func *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem Offset_ int64 @@ -47,17 +46,17 @@ type Name struct { Opt interface{} // for use by escape analysis Embed *[]Embed // list of embedded files, for ONAME var - PkgName *PkgName // real package for import . names // For a local variable (not param) or extern, the initializing assignment (OAS or OAS2). // For a closure var, the ONAME node of the outer captured variable. // For the case-local variables of a type switch, the type switch guard (OTYPESW). + // For a range variable, the range statement (ORANGE) + // For a recv variable in a case of a select statement, the receive assignment (OSELRECV2) // For the name of a function, points to corresponding Func node. Defn Node // The function, method, or closure in which local variable or param is declared. Curfn *Func - Ntype Ntype Heapaddr *Name // temp holding heap address of param // ONAME closure linkage @@ -139,13 +138,6 @@ func (n *Name) copy() Node { panic(n.no("copy")) } func (n *Name) doChildren(do func(Node) bool) bool { return false } func (n *Name) editChildren(edit func(Node) Node) {} -// TypeDefn returns the type definition for a named OTYPE. -// That is, given "type T Defn", it returns Defn. -// It is used by package types. -func (n *Name) TypeDefn() *types.Type { - return n.Ntype.Type() -} - // RecordFrameOffset records the frame offset for the name. // It is used by package types when laying out function arguments. func (n *Name) RecordFrameOffset(offset int64) { @@ -161,14 +153,6 @@ func NewNameAt(pos src.XPos, sym *types.Sym) *Name { return newNameAt(pos, ONAME, sym) } -// NewIota returns a new OIOTA Node. -func NewIota(pos src.XPos, sym *types.Sym) *Name { - if sym == nil { - base.Fatalf("NewIota nil") - } - return newNameAt(pos, OIOTA, sym) -} - // NewDeclNameAt returns a new Name associated with symbol s at position pos. // The caller is responsible for setting Curfn. func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name { @@ -218,15 +202,6 @@ func (n *Name) SetOffset(x int64) { } func (n *Name) FrameOffset() int64 { return n.Offset_ } func (n *Name) SetFrameOffset(x int64) { n.Offset_ = x } -func (n *Name) Iota() int64 { return n.Offset_ } -func (n *Name) SetIota(x int64) { n.Offset_ = x } -func (n *Name) Walkdef() uint8 { return n.bits.get2(miniWalkdefShift) } -func (n *Name) SetWalkdef(x uint8) { - if x > 3 { - panic(fmt.Sprintf("cannot SetWalkdef %d", x)) - } - n.bits.set2(miniWalkdefShift, x) -} func (n *Name) Linksym() *obj.LSym { return n.sym.Linksym() } func (n *Name) LinksymABI(abi obj.ABI) *obj.LSym { return n.sym.LinksymABI(abi) } @@ -260,7 +235,7 @@ const ( nameInlFormal // PAUTO created by inliner, derived from callee formal nameInlLocal // PAUTO created by inliner, derived from callee local nameOpenDeferSlot // if temporary var storing info for open-coded defers - nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section + nameLibfuzzer8BitCounter // if PEXTERN should be assigned to __sancov_cntrs section nameAlias // is type name an alias ) @@ -275,7 +250,7 @@ func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 } func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 } func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 } -func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 } +func (n *Name) Libfuzzer8BitCounter() bool { return n.flags&nameLibfuzzer8BitCounter != 0 } func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) } func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) } @@ -288,7 +263,7 @@ func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) } func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) } func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) } -func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) } +func (n *Name) SetLibfuzzer8BitCounter(b bool) { n.flags.set(nameLibfuzzer8BitCounter, b) } // OnStack reports whether variable n may reside on the stack. func (n *Name) OnStack() bool { @@ -358,39 +333,82 @@ func (n *Name) Byval() bool { return n.Canonical().flags&nameByval != 0 } +// NewClosureVar returns a new closure variable for fn to refer to +// outer variable n. +func NewClosureVar(pos src.XPos, fn *Func, n *Name) *Name { + switch n.Class { + case PAUTO, PPARAM, PPARAMOUT, PAUTOHEAP: + // ok + default: + // Prevent mistaken capture of global variables. + base.Fatalf("NewClosureVar: %+v", n) + } + + c := NewNameAt(pos, n.Sym()) + c.Curfn = fn + c.Class = PAUTOHEAP + c.SetIsClosureVar(true) + c.Defn = n.Canonical() + c.Outer = n + + c.SetType(n.Type()) + c.SetTypecheck(n.Typecheck()) + + fn.ClosureVars = append(fn.ClosureVars, c) + + return c +} + +// NewHiddenParam returns a new hidden parameter for fn with the given +// name and type. +func NewHiddenParam(pos src.XPos, fn *Func, sym *types.Sym, typ *types.Type) *Name { + if fn.OClosure != nil { + base.FatalfAt(fn.Pos(), "cannot add hidden parameters to closures") + } + + fn.SetNeedctxt(true) + + // Create a fake parameter, disassociated from any real function, to + // pretend to capture. + fake := NewNameAt(pos, sym) + fake.Class = PPARAM + fake.SetType(typ) + fake.SetByval(true) + + return NewClosureVar(pos, fn, fake) +} + // CaptureName returns a Name suitable for referring to n from within function // fn or from the package block if fn is nil. If n is a free variable declared -// within a function that encloses fn, then CaptureName returns a closure -// variable that refers to n and adds it to fn.ClosureVars. Otherwise, it simply -// returns n. +// within a function that encloses fn, then CaptureName returns the closure +// variable that refers to n within fn, creating it if necessary. +// Otherwise, it simply returns n. func CaptureName(pos src.XPos, fn *Func, n *Name) *Name { - if n.IsClosureVar() { - base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n) - } - if n.Op() != ONAME || n.Curfn == nil || n.Curfn == fn { + if n.Op() != ONAME || n.Curfn == nil { return n // okay to use directly } - if fn == nil { - base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn) + if n.IsClosureVar() { + base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n) } c := n.Innermost - if c != nil && c.Curfn == fn { + if c == nil { + c = n + } + if c.Curfn == fn { return c } + if fn == nil { + base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn) + } + // Do not have a closure var for the active closure yet; make one. - c = NewNameAt(pos, n.Sym()) - c.Curfn = fn - c.Class = PAUTOHEAP - c.SetIsClosureVar(true) - c.Defn = n + c = NewClosureVar(pos, fn, c) // Link into list of active closure variables. // Popped from list in FinishCaptureNames. - c.Outer = n.Innermost n.Innermost = c - fn.ClosureVars = append(fn.ClosureVars, c) return c } @@ -495,22 +513,3 @@ type Embed struct { Pos src.XPos Patterns []string } - -// A Pack is an identifier referring to an imported package. -type PkgName struct { - miniNode - sym *types.Sym - Pkg *types.Pkg - Used bool -} - -func (p *PkgName) Sym() *types.Sym { return p.sym } - -func (*PkgName) CanBeNtype() {} - -func NewPkgName(pos src.XPos, sym *types.Sym, pkg *types.Pkg) *PkgName { - p := &PkgName{sym: sym, Pkg: pkg} - p.op = OPACK - p.pos = pos - return p -} diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go index af559cc0820cc3..7a4fb02f25d01a 100644 --- a/src/cmd/compile/internal/ir/node.go +++ b/src/cmd/compile/internal/ir/node.go @@ -46,8 +46,6 @@ type Node interface { // Storage for analysis passes. Esc() uint16 SetEsc(x uint16) - Diag() bool - SetDiag(x bool) // Typecheck values: // 0 means the node is not typechecked @@ -118,7 +116,6 @@ const ( // Also used for a qualified package identifier that hasn't been resolved yet. ONONAME OTYPE // type name - OPACK // import OLITERAL // literal ONIL // nil @@ -155,11 +152,10 @@ const ( // Prior to walk, they are: X(Args), where Args is all regular arguments. // After walk, if any argument whose evaluation might requires temporary variable, // that temporary variable will be pushed to Init, Args will contains an updated - // set of arguments. KeepAlive is all OVARLIVE nodes that are attached to OCALLxxx. + // set of arguments. OCALLFUNC // X(Args) (function call f(args)) OCALLMETH // X(Args) (direct method call x.Method(args)) OCALLINTER // X(Args) (interface method call x.Method(args)) - OCALLPART // X.Sel (method expression x.Method, not called) OCAP // cap(X) OCLOSE // close(X) OCLOSURE // func Type { Func.Closure.Body } (func literal) @@ -171,6 +167,7 @@ const ( OPTRLIT // &X (X is composite literal) OCONV // Type(X) (type conversion) OCONVIFACE // Type(X) (type conversion, to interface) + OCONVIDATA // Builds a data word to store X in an interface. Equivalent to IDATA(CONVIFACE(X)). Is an ir.ConvExpr. OCONVNOP // Type(X) (type conversion, no effect) OCOPY // copy(X, Y) ODCL // var X (declares X of type X.Type) @@ -212,51 +209,57 @@ const ( // // This node is created so the walk pass can optimize this pattern which would // otherwise be hard to detect after the order pass. - OMUL // X * Y - ODIV // X / Y - OMOD // X % Y - OLSH // X << Y - ORSH // X >> Y - OAND // X & Y - OANDNOT // X &^ Y - ONEW // new(X); corresponds to calls to new in source code - ONOT // !X - OBITNOT // ^X - OPLUS // +X - ONEG // -X - OOROR // X || Y - OPANIC // panic(X) - OPRINT // print(List) - OPRINTN // println(List) - OPAREN // (X) - OSEND // Chan <- Value - OSLICE // X[Low : High] (X is untypechecked or slice) - OSLICEARR // X[Low : High] (X is pointer to array) - OSLICESTR // X[Low : High] (X is string) - OSLICE3 // X[Low : High : Max] (X is untypedchecked or slice) - OSLICE3ARR // X[Low : High : Max] (X is pointer to array) - OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity) - ORECOVER // recover() - ORECV // <-X - ORUNESTR // Type(X) (Type is string, X is rune) - OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE) - OIOTA // iota - OREAL // real(X) - OIMAG // imag(X) - OCOMPLEX // complex(X, Y) - OALIGNOF // unsafe.Alignof(X) - OOFFSETOF // unsafe.Offsetof(X) - OSIZEOF // unsafe.Sizeof(X) - OUNSAFEADD // unsafe.Add(X, Y) - OUNSAFESLICE // unsafe.Slice(X, Y) - OMETHEXPR // method expression + OMUL // X * Y + ODIV // X / Y + OMOD // X % Y + OLSH // X << Y + ORSH // X >> Y + OAND // X & Y + OANDNOT // X &^ Y + ONEW // new(X); corresponds to calls to new in source code + ONOT // !X + OBITNOT // ^X + OPLUS // +X + ONEG // -X + OOROR // X || Y + OPANIC // panic(X) + OPRINT // print(List) + OPRINTN // println(List) + OPAREN // (X) + OSEND // Chan <- Value + OSLICE // X[Low : High] (X is untypechecked or slice) + OSLICEARR // X[Low : High] (X is pointer to array) + OSLICESTR // X[Low : High] (X is string) + OSLICE3 // X[Low : High : Max] (X is untypedchecked or slice) + OSLICE3ARR // X[Low : High : Max] (X is pointer to array) + OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity) + OSTRINGHEADER // stringheader{Ptr, Len} (Ptr is unsafe.Pointer, Len is length) + ORECOVER // recover() + ORECOVERFP // recover(Args) w/ explicit FP argument + ORECV // <-X + ORUNESTR // Type(X) (Type is string, X is rune) + OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE) + OREAL // real(X) + OIMAG // imag(X) + OCOMPLEX // complex(X, Y) + OALIGNOF // unsafe.Alignof(X) + OOFFSETOF // unsafe.Offsetof(X) + OSIZEOF // unsafe.Sizeof(X) + OUNSAFEADD // unsafe.Add(X, Y) + OUNSAFESLICE // unsafe.Slice(X, Y) + OUNSAFESLICEDATA // unsafe.SliceData(X) + OUNSAFESTRING // unsafe.String(X, Y) + OUNSAFESTRINGDATA // unsafe.StringData(X) + OMETHEXPR // X(Args) (method expression T.Method(args), first argument is the method receiver) + OMETHVALUE // X.Sel (method expression t.Method, not called) // statements OBLOCK // { List } (block of code) OBREAK // break [Label] // OCASE: case List: Body (List==nil means default) // For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL - // for nil), and, if a type-switch variable is specified, Rlist is an + // for nil) or an ODYNAMICTYPE indicating a runtime type for generics. + // If a type-switch variable is specified, Var is an // ONAME for the version of the type-switch variable with the specified // type. OCASE @@ -264,40 +267,19 @@ const ( ODEFER // defer Call OFALL // fallthrough OFOR // for Init; Cond; Post { Body } - // OFORUNTIL is like OFOR, but the test (Cond) is applied after the body: - // Init - // top: { Body } // Execute the body at least once - // cont: Post - // if Cond { // And then test the loop condition - // List // Before looping to top, execute List - // goto top - // } - // OFORUNTIL is created by walk. There's no way to write this in Go code. - OFORUNTIL - OGOTO // goto Label - OIF // if Init; Cond { Then } else { Else } - OLABEL // Label: - OGO // go Call - ORANGE // for Key, Value = range X { Body } - ORETURN // return Results - OSELECT // select { Cases } - OSWITCH // switch Init; Expr { Cases } + OGOTO // goto Label + OIF // if Init; Cond { Then } else { Else } + OLABEL // Label: + OGO // go Call + ORANGE // for Key, Value = range X { Body } + ORETURN // return Results + OSELECT // select { Cases } + OSWITCH // switch Init; Expr { Cases } // OTYPESW: X := Y.(type) (appears as .Tag of OSWITCH) // X is nil if there is no type-switch variable OTYPESW OFUNCINST // instantiation of a generic function - // types - OTCHAN // chan int - OTMAP // map[string]int - OTSTRUCT // struct{} - OTINTER // interface{} - // OTFUNC: func() - Recv is receiver field, Params is list of param fields, Results is - // list of result fields. - OTFUNC - OTARRAY // [8]int or [...]int - OTSLICE // []int - // misc // intermediate representation of an inlined call. Uses Init (assignments // for the captured variables, parameters, retvars, & INLMARK op), @@ -310,20 +292,35 @@ const ( OSPTR // base pointer of a slice or string. OCFUNC // reference to c function pointer (not go func value) OCHECKNIL // emit code to ensure pointer/interface not nil - OVARDEF // variable is about to be fully initialized - OVARKILL // variable is dead - OVARLIVE // variable is alive ORESULT // result of a function call; Xoffset is stack offset OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree. OLINKSYMOFFSET // offset within a name + OJUMPTABLE // A jump table structure for implementing dense expression switches + + // opcodes for generics + ODYNAMICDOTTYPE // x = i.(T) where T is a type parameter (or derived from a type parameter) + ODYNAMICDOTTYPE2 // x, ok = i.(T) where T is a type parameter (or derived from a type parameter) + ODYNAMICTYPE // a type node for type switches (represents a dynamic target type for a type switch) // arch-specific opcodes - OTAILCALL // tail call to another function - OGETG // runtime.getg() (read g pointer) + OTAILCALL // tail call to another function + OGETG // runtime.getg() (read g pointer) + OGETCALLERPC // runtime.getcallerpc() (continuation PC in caller frame) + OGETCALLERSP // runtime.getcallersp() (stack pointer in caller frame) OEND ) +// IsCmp reports whether op is a comparison operation (==, !=, <, <=, +// >, or >=). +func (op Op) IsCmp() bool { + switch op { + case OEQ, ONE, OLT, OLE, OGT, OGE: + return true + } + return false +} + // Nodes is a pointer to a slice of *Node. // For fields that are not used in most nodes, this is used instead of // a slice to save space. @@ -436,29 +433,27 @@ func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name { return res } -type PragmaFlag int16 +type PragmaFlag uint16 const ( // Func pragmas. - Nointerface PragmaFlag = 1 << iota - Noescape // func parameters don't escape - Norace // func must not have race detector annotations - Nosplit // func should not execute on separate stack - Noinline // func should not be inlined - NoCheckPtr // func should not be instrumented by checkptr - CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all - UintptrEscapes // pointers converted to uintptr escape + Nointerface PragmaFlag = 1 << iota + Noescape // func parameters don't escape + Norace // func must not have race detector annotations + Nosplit // func should not execute on separate stack + Noinline // func should not be inlined + NoCheckPtr // func should not be instrumented by checkptr + CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all + UintptrKeepAlive // pointers converted to uintptr must be kept alive + UintptrEscapes // pointers converted to uintptr escape // Runtime-only func pragmas. - // See ../../../../runtime/README.md for detailed descriptions. + // See ../../../../runtime/HACKING.md for detailed descriptions. Systemstack // func must run on system stack Nowritebarrier // emit compiler error instead of write barrier Nowritebarrierrec // error on write barrier in this or recursive callees Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees - // Runtime and cgo type pragmas - NotInHeap // values of this type must not be heap allocated - // Go command pragmas GoBuildPragma @@ -512,7 +507,7 @@ func HasNamedResults(fn *Func) bool { // their usage position. func HasUniquePos(n Node) bool { switch n.Op() { - case ONAME, OPACK: + case ONAME: return false case OLITERAL, ONIL, OTYPE: if n.Sym() != nil { @@ -539,7 +534,8 @@ func SetPos(n Node) src.XPos { } // The result of InitExpr MUST be assigned back to n, e.g. -// n.X = InitExpr(init, n.X) +// +// n.X = InitExpr(init, n.X) func InitExpr(init []Node, expr Node) Node { if len(init) == 0 { return expr @@ -563,7 +559,7 @@ func OuterValue(n Node) Node { for { switch nn := n; nn.Op() { case OXDOT: - base.Fatalf("OXDOT in walk") + base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n) case ODOT: nn := nn.(*SelectorExpr) n = nn.X diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go index 22855d7163f44a..f5d362eef543d1 100644 --- a/src/cmd/compile/internal/ir/node_gen.go +++ b/src/cmd/compile/internal/ir/node_gen.go @@ -59,29 +59,6 @@ func (n *AddrExpr) editChildren(edit func(Node) Node) { } } -func (n *ArrayType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *ArrayType) copy() Node { - c := *n - return &c -} -func (n *ArrayType) doChildren(do func(Node) bool) bool { - if n.Len != nil && do(n.Len) { - return true - } - if n.Elem != nil && do(n.Elem) { - return true - } - return false -} -func (n *ArrayType) editChildren(edit func(Node) Node) { - if n.Len != nil { - n.Len = edit(n.Len).(Node) - } - if n.Elem != nil { - n.Elem = edit(n.Elem).(Ntype) - } -} - func (n *AssignListStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *AssignListStmt) copy() Node { c := *n @@ -282,6 +259,7 @@ func (n *CaseClause) copy() Node { c := *n c.init = copyNodes(c.init) c.List = copyNodes(c.List) + c.RTypes = copyNodes(c.RTypes) c.Body = copyNodes(c.Body) return &c } @@ -295,6 +273,9 @@ func (n *CaseClause) doChildren(do func(Node) bool) bool { if doNodes(n.List, do) { return true } + if doNodes(n.RTypes, do) { + return true + } if doNodes(n.Body, do) { return true } @@ -306,26 +287,10 @@ func (n *CaseClause) editChildren(edit func(Node) Node) { n.Var = edit(n.Var).(*Name) } editNodes(n.List, edit) + editNodes(n.RTypes, edit) editNodes(n.Body, edit) } -func (n *ChanType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *ChanType) copy() Node { - c := *n - return &c -} -func (n *ChanType) doChildren(do func(Node) bool) bool { - if n.Elem != nil && do(n.Elem) { - return true - } - return false -} -func (n *ChanType) editChildren(edit func(Node) Node) { - if n.Elem != nil { - n.Elem = edit(n.Elem).(Ntype) - } -} - func (n *ClosureExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *ClosureExpr) copy() Node { c := *n @@ -386,9 +351,6 @@ func (n *CompLitExpr) doChildren(do func(Node) bool) bool { if doNodes(n.init, do) { return true } - if n.Ntype != nil && do(n.Ntype) { - return true - } if doNodes(n.List, do) { return true } @@ -399,9 +361,6 @@ func (n *CompLitExpr) doChildren(do func(Node) bool) bool { } func (n *CompLitExpr) editChildren(edit func(Node) Node) { editNodes(n.init, edit) - if n.Ntype != nil { - n.Ntype = edit(n.Ntype).(Ntype) - } editNodes(n.List, edit) if n.Prealloc != nil { n.Prealloc = edit(n.Prealloc).(*Name) @@ -463,72 +422,109 @@ func (n *Decl) editChildren(edit func(Node) Node) { } } -func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *ForStmt) copy() Node { +func (n *DynamicType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *DynamicType) copy() Node { c := *n c.init = copyNodes(c.init) - c.Late = copyNodes(c.Late) - c.Body = copyNodes(c.Body) return &c } -func (n *ForStmt) doChildren(do func(Node) bool) bool { +func (n *DynamicType) doChildren(do func(Node) bool) bool { if doNodes(n.init, do) { return true } - if n.Cond != nil && do(n.Cond) { + if n.RType != nil && do(n.RType) { return true } - if doNodes(n.Late, do) { + if n.ITab != nil && do(n.ITab) { return true } - if n.Post != nil && do(n.Post) { + return false +} +func (n *DynamicType) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.RType != nil { + n.RType = edit(n.RType).(Node) + } + if n.ITab != nil { + n.ITab = edit(n.ITab).(Node) + } +} + +func (n *DynamicTypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *DynamicTypeAssertExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *DynamicTypeAssertExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { return true } - if doNodes(n.Body, do) { + if n.X != nil && do(n.X) { + return true + } + if n.SrcRType != nil && do(n.SrcRType) { + return true + } + if n.RType != nil && do(n.RType) { + return true + } + if n.ITab != nil && do(n.ITab) { return true } return false } -func (n *ForStmt) editChildren(edit func(Node) Node) { +func (n *DynamicTypeAssertExpr) editChildren(edit func(Node) Node) { editNodes(n.init, edit) - if n.Cond != nil { - n.Cond = edit(n.Cond).(Node) + if n.X != nil { + n.X = edit(n.X).(Node) } - editNodes(n.Late, edit) - if n.Post != nil { - n.Post = edit(n.Post).(Node) + if n.SrcRType != nil { + n.SrcRType = edit(n.SrcRType).(Node) + } + if n.RType != nil { + n.RType = edit(n.RType).(Node) + } + if n.ITab != nil { + n.ITab = edit(n.ITab).(Node) } - editNodes(n.Body, edit) } -func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } - -func (n *FuncType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *FuncType) copy() Node { +func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *ForStmt) copy() Node { c := *n - c.Recv = copyField(c.Recv) - c.Params = copyFields(c.Params) - c.Results = copyFields(c.Results) + c.init = copyNodes(c.init) + c.Body = copyNodes(c.Body) return &c } -func (n *FuncType) doChildren(do func(Node) bool) bool { - if doField(n.Recv, do) { +func (n *ForStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Cond != nil && do(n.Cond) { return true } - if doFields(n.Params, do) { + if n.Post != nil && do(n.Post) { return true } - if doFields(n.Results, do) { + if doNodes(n.Body, do) { return true } return false } -func (n *FuncType) editChildren(edit func(Node) Node) { - editField(n.Recv, edit) - editFields(n.Params, edit) - editFields(n.Results, edit) +func (n *ForStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Cond != nil { + n.Cond = edit(n.Cond).(Node) + } + if n.Post != nil { + n.Post = edit(n.Post).(Node) + } + editNodes(n.Body, edit) } +func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } + func (n *GoDeferStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *GoDeferStmt) copy() Node { c := *n @@ -673,7 +669,7 @@ func (n *InstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *InstExpr) copy() Node { c := *n c.init = copyNodes(c.init) - c.Targs = copyNodes(c.Targs) + c.Targs = copyNtypes(c.Targs) return &c } func (n *InstExpr) doChildren(do func(Node) bool) bool { @@ -683,7 +679,7 @@ func (n *InstExpr) doChildren(do func(Node) bool) bool { if n.X != nil && do(n.X) { return true } - if doNodes(n.Targs, do) { + if doNtypes(n.Targs, do) { return true } return false @@ -693,23 +689,29 @@ func (n *InstExpr) editChildren(edit func(Node) Node) { if n.X != nil { n.X = edit(n.X).(Node) } - editNodes(n.Targs, edit) + editNtypes(n.Targs, edit) } -func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *InterfaceType) copy() Node { +func (n *JumpTableStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *JumpTableStmt) copy() Node { c := *n - c.Methods = copyFields(c.Methods) + c.init = copyNodes(c.init) return &c } -func (n *InterfaceType) doChildren(do func(Node) bool) bool { - if doFields(n.Methods, do) { +func (n *JumpTableStmt) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.Idx != nil && do(n.Idx) { return true } return false } -func (n *InterfaceType) editChildren(edit func(Node) Node) { - editFields(n.Methods, edit) +func (n *JumpTableStmt) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.Idx != nil { + n.Idx = edit(n.Idx).(Node) + } } func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } @@ -828,29 +830,6 @@ func (n *MakeExpr) editChildren(edit func(Node) Node) { } } -func (n *MapType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *MapType) copy() Node { - c := *n - return &c -} -func (n *MapType) doChildren(do func(Node) bool) bool { - if n.Key != nil && do(n.Key) { - return true - } - if n.Elem != nil && do(n.Elem) { - return true - } - return false -} -func (n *MapType) editChildren(edit func(Node) Node) { - if n.Key != nil { - n.Key = edit(n.Key).(Ntype) - } - if n.Elem != nil { - n.Elem = edit(n.Elem).(Ntype) - } -} - func (n *Name) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *NilExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } @@ -891,17 +870,6 @@ func (n *ParenExpr) editChildren(edit func(Node) Node) { } } -func (n *PkgName) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *PkgName) copy() Node { - c := *n - return &c -} -func (n *PkgName) doChildren(do func(Node) bool) bool { - return false -} -func (n *PkgName) editChildren(edit func(Node) Node) { -} - func (n *RangeStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *RangeStmt) copy() Node { c := *n @@ -947,6 +915,22 @@ func (n *RangeStmt) editChildren(edit func(Node) Node) { } } +func (n *RawOrigExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *RawOrigExpr) copy() Node { + c := *n + c.init = copyNodes(c.init) + return &c +} +func (n *RawOrigExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + return false +} +func (n *RawOrigExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) +} + func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *ResultExpr) copy() Node { c := *n @@ -1140,42 +1124,53 @@ func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) { } } -func (n *SliceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *SliceType) copy() Node { +func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *StarExpr) copy() Node { c := *n + c.init = copyNodes(c.init) return &c } -func (n *SliceType) doChildren(do func(Node) bool) bool { - if n.Elem != nil && do(n.Elem) { +func (n *StarExpr) doChildren(do func(Node) bool) bool { + if doNodes(n.init, do) { + return true + } + if n.X != nil && do(n.X) { return true } return false } -func (n *SliceType) editChildren(edit func(Node) Node) { - if n.Elem != nil { - n.Elem = edit(n.Elem).(Ntype) +func (n *StarExpr) editChildren(edit func(Node) Node) { + editNodes(n.init, edit) + if n.X != nil { + n.X = edit(n.X).(Node) } } -func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *StarExpr) copy() Node { +func (n *StringHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } +func (n *StringHeaderExpr) copy() Node { c := *n c.init = copyNodes(c.init) return &c } -func (n *StarExpr) doChildren(do func(Node) bool) bool { +func (n *StringHeaderExpr) doChildren(do func(Node) bool) bool { if doNodes(n.init, do) { return true } - if n.X != nil && do(n.X) { + if n.Ptr != nil && do(n.Ptr) { + return true + } + if n.Len != nil && do(n.Len) { return true } return false } -func (n *StarExpr) editChildren(edit func(Node) Node) { +func (n *StringHeaderExpr) editChildren(edit func(Node) Node) { editNodes(n.init, edit) - if n.X != nil { - n.X = edit(n.X).(Node) + if n.Ptr != nil { + n.Ptr = edit(n.Ptr).(Node) + } + if n.Len != nil { + n.Len = edit(n.Len).(Node) } } @@ -1201,22 +1196,6 @@ func (n *StructKeyExpr) editChildren(edit func(Node) Node) { } } -func (n *StructType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } -func (n *StructType) copy() Node { - c := *n - c.Fields = copyFields(c.Fields) - return &c -} -func (n *StructType) doChildren(do func(Node) bool) bool { - if doFields(n.Fields, do) { - return true - } - return false -} -func (n *StructType) editChildren(edit func(Node) Node) { - editFields(n.Fields, edit) -} - func (n *SwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *SwitchStmt) copy() Node { c := *n @@ -1259,15 +1238,15 @@ func (n *TailCallStmt) doChildren(do func(Node) bool) bool { if doNodes(n.init, do) { return true } - if n.Target != nil && do(n.Target) { + if n.Call != nil && do(n.Call) { return true } return false } func (n *TailCallStmt) editChildren(edit func(Node) Node) { editNodes(n.init, edit) - if n.Target != nil { - n.Target = edit(n.Target).(*Name) + if n.Call != nil { + n.Call = edit(n.Call).(*CallExpr) } } @@ -1284,9 +1263,6 @@ func (n *TypeAssertExpr) doChildren(do func(Node) bool) bool { if n.X != nil && do(n.X) { return true } - if n.Ntype != nil && do(n.Ntype) { - return true - } return false } func (n *TypeAssertExpr) editChildren(edit func(Node) Node) { @@ -1294,9 +1270,6 @@ func (n *TypeAssertExpr) editChildren(edit func(Node) Node) { if n.X != nil { n.X = edit(n.X).(Node) } - if n.Ntype != nil { - n.Ntype = edit(n.Ntype).(Ntype) - } } func (n *TypeSwitchGuard) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } @@ -1450,3 +1423,27 @@ func editNodes(list []Node, edit func(Node) Node) { } } } + +func copyNtypes(list []Ntype) []Ntype { + if list == nil { + return nil + } + c := make([]Ntype, len(list)) + copy(c, list) + return c +} +func doNtypes(list []Ntype, do func(Node) bool) bool { + for _, x := range list { + if x != nil && do(x) { + return true + } + } + return false +} +func editNtypes(list []Ntype, edit func(Node) Node) { + for i, x := range list { + if x != nil { + list[i] = edit(x).(Ntype) + } + } +} diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go index 405a0c6b3c9858..e44168c7ba3697 100644 --- a/src/cmd/compile/internal/ir/op_string.go +++ b/src/cmd/compile/internal/ir/op_string.go @@ -12,107 +12,107 @@ func _() { _ = x[ONAME-1] _ = x[ONONAME-2] _ = x[OTYPE-3] - _ = x[OPACK-4] - _ = x[OLITERAL-5] - _ = x[ONIL-6] - _ = x[OADD-7] - _ = x[OSUB-8] - _ = x[OOR-9] - _ = x[OXOR-10] - _ = x[OADDSTR-11] - _ = x[OADDR-12] - _ = x[OANDAND-13] - _ = x[OAPPEND-14] - _ = x[OBYTES2STR-15] - _ = x[OBYTES2STRTMP-16] - _ = x[ORUNES2STR-17] - _ = x[OSTR2BYTES-18] - _ = x[OSTR2BYTESTMP-19] - _ = x[OSTR2RUNES-20] - _ = x[OSLICE2ARRPTR-21] - _ = x[OAS-22] - _ = x[OAS2-23] - _ = x[OAS2DOTTYPE-24] - _ = x[OAS2FUNC-25] - _ = x[OAS2MAPR-26] - _ = x[OAS2RECV-27] - _ = x[OASOP-28] - _ = x[OCALL-29] - _ = x[OCALLFUNC-30] - _ = x[OCALLMETH-31] - _ = x[OCALLINTER-32] - _ = x[OCALLPART-33] - _ = x[OCAP-34] - _ = x[OCLOSE-35] - _ = x[OCLOSURE-36] - _ = x[OCOMPLIT-37] - _ = x[OMAPLIT-38] - _ = x[OSTRUCTLIT-39] - _ = x[OARRAYLIT-40] - _ = x[OSLICELIT-41] - _ = x[OPTRLIT-42] - _ = x[OCONV-43] - _ = x[OCONVIFACE-44] - _ = x[OCONVNOP-45] - _ = x[OCOPY-46] - _ = x[ODCL-47] - _ = x[ODCLFUNC-48] - _ = x[ODCLCONST-49] - _ = x[ODCLTYPE-50] - _ = x[ODELETE-51] - _ = x[ODOT-52] - _ = x[ODOTPTR-53] - _ = x[ODOTMETH-54] - _ = x[ODOTINTER-55] - _ = x[OXDOT-56] - _ = x[ODOTTYPE-57] - _ = x[ODOTTYPE2-58] - _ = x[OEQ-59] - _ = x[ONE-60] - _ = x[OLT-61] - _ = x[OLE-62] - _ = x[OGE-63] - _ = x[OGT-64] - _ = x[ODEREF-65] - _ = x[OINDEX-66] - _ = x[OINDEXMAP-67] - _ = x[OKEY-68] - _ = x[OSTRUCTKEY-69] - _ = x[OLEN-70] - _ = x[OMAKE-71] - _ = x[OMAKECHAN-72] - _ = x[OMAKEMAP-73] - _ = x[OMAKESLICE-74] - _ = x[OMAKESLICECOPY-75] - _ = x[OMUL-76] - _ = x[ODIV-77] - _ = x[OMOD-78] - _ = x[OLSH-79] - _ = x[ORSH-80] - _ = x[OAND-81] - _ = x[OANDNOT-82] - _ = x[ONEW-83] - _ = x[ONOT-84] - _ = x[OBITNOT-85] - _ = x[OPLUS-86] - _ = x[ONEG-87] - _ = x[OOROR-88] - _ = x[OPANIC-89] - _ = x[OPRINT-90] - _ = x[OPRINTN-91] - _ = x[OPAREN-92] - _ = x[OSEND-93] - _ = x[OSLICE-94] - _ = x[OSLICEARR-95] - _ = x[OSLICESTR-96] - _ = x[OSLICE3-97] - _ = x[OSLICE3ARR-98] - _ = x[OSLICEHEADER-99] + _ = x[OLITERAL-4] + _ = x[ONIL-5] + _ = x[OADD-6] + _ = x[OSUB-7] + _ = x[OOR-8] + _ = x[OXOR-9] + _ = x[OADDSTR-10] + _ = x[OADDR-11] + _ = x[OANDAND-12] + _ = x[OAPPEND-13] + _ = x[OBYTES2STR-14] + _ = x[OBYTES2STRTMP-15] + _ = x[ORUNES2STR-16] + _ = x[OSTR2BYTES-17] + _ = x[OSTR2BYTESTMP-18] + _ = x[OSTR2RUNES-19] + _ = x[OSLICE2ARRPTR-20] + _ = x[OAS-21] + _ = x[OAS2-22] + _ = x[OAS2DOTTYPE-23] + _ = x[OAS2FUNC-24] + _ = x[OAS2MAPR-25] + _ = x[OAS2RECV-26] + _ = x[OASOP-27] + _ = x[OCALL-28] + _ = x[OCALLFUNC-29] + _ = x[OCALLMETH-30] + _ = x[OCALLINTER-31] + _ = x[OCAP-32] + _ = x[OCLOSE-33] + _ = x[OCLOSURE-34] + _ = x[OCOMPLIT-35] + _ = x[OMAPLIT-36] + _ = x[OSTRUCTLIT-37] + _ = x[OARRAYLIT-38] + _ = x[OSLICELIT-39] + _ = x[OPTRLIT-40] + _ = x[OCONV-41] + _ = x[OCONVIFACE-42] + _ = x[OCONVIDATA-43] + _ = x[OCONVNOP-44] + _ = x[OCOPY-45] + _ = x[ODCL-46] + _ = x[ODCLFUNC-47] + _ = x[ODCLCONST-48] + _ = x[ODCLTYPE-49] + _ = x[ODELETE-50] + _ = x[ODOT-51] + _ = x[ODOTPTR-52] + _ = x[ODOTMETH-53] + _ = x[ODOTINTER-54] + _ = x[OXDOT-55] + _ = x[ODOTTYPE-56] + _ = x[ODOTTYPE2-57] + _ = x[OEQ-58] + _ = x[ONE-59] + _ = x[OLT-60] + _ = x[OLE-61] + _ = x[OGE-62] + _ = x[OGT-63] + _ = x[ODEREF-64] + _ = x[OINDEX-65] + _ = x[OINDEXMAP-66] + _ = x[OKEY-67] + _ = x[OSTRUCTKEY-68] + _ = x[OLEN-69] + _ = x[OMAKE-70] + _ = x[OMAKECHAN-71] + _ = x[OMAKEMAP-72] + _ = x[OMAKESLICE-73] + _ = x[OMAKESLICECOPY-74] + _ = x[OMUL-75] + _ = x[ODIV-76] + _ = x[OMOD-77] + _ = x[OLSH-78] + _ = x[ORSH-79] + _ = x[OAND-80] + _ = x[OANDNOT-81] + _ = x[ONEW-82] + _ = x[ONOT-83] + _ = x[OBITNOT-84] + _ = x[OPLUS-85] + _ = x[ONEG-86] + _ = x[OOROR-87] + _ = x[OPANIC-88] + _ = x[OPRINT-89] + _ = x[OPRINTN-90] + _ = x[OPAREN-91] + _ = x[OSEND-92] + _ = x[OSLICE-93] + _ = x[OSLICEARR-94] + _ = x[OSLICESTR-95] + _ = x[OSLICE3-96] + _ = x[OSLICE3ARR-97] + _ = x[OSLICEHEADER-98] + _ = x[OSTRINGHEADER-99] _ = x[ORECOVER-100] - _ = x[ORECV-101] - _ = x[ORUNESTR-102] - _ = x[OSELRECV2-103] - _ = x[OIOTA-104] + _ = x[ORECOVERFP-101] + _ = x[ORECV-102] + _ = x[ORUNESTR-103] + _ = x[OSELRECV2-104] _ = x[OREAL-105] _ = x[OIMAG-106] _ = x[OCOMPLEX-107] @@ -121,53 +121,52 @@ func _() { _ = x[OSIZEOF-110] _ = x[OUNSAFEADD-111] _ = x[OUNSAFESLICE-112] - _ = x[OMETHEXPR-113] - _ = x[OBLOCK-114] - _ = x[OBREAK-115] - _ = x[OCASE-116] - _ = x[OCONTINUE-117] - _ = x[ODEFER-118] - _ = x[OFALL-119] - _ = x[OFOR-120] - _ = x[OFORUNTIL-121] - _ = x[OGOTO-122] - _ = x[OIF-123] - _ = x[OLABEL-124] - _ = x[OGO-125] - _ = x[ORANGE-126] - _ = x[ORETURN-127] - _ = x[OSELECT-128] - _ = x[OSWITCH-129] - _ = x[OTYPESW-130] - _ = x[OFUNCINST-131] - _ = x[OTCHAN-132] - _ = x[OTMAP-133] - _ = x[OTSTRUCT-134] - _ = x[OTINTER-135] - _ = x[OTFUNC-136] - _ = x[OTARRAY-137] - _ = x[OTSLICE-138] - _ = x[OINLCALL-139] - _ = x[OEFACE-140] - _ = x[OITAB-141] - _ = x[OIDATA-142] - _ = x[OSPTR-143] - _ = x[OCFUNC-144] - _ = x[OCHECKNIL-145] - _ = x[OVARDEF-146] - _ = x[OVARKILL-147] - _ = x[OVARLIVE-148] - _ = x[ORESULT-149] - _ = x[OINLMARK-150] - _ = x[OLINKSYMOFFSET-151] - _ = x[OTAILCALL-152] - _ = x[OGETG-153] - _ = x[OEND-154] + _ = x[OUNSAFESLICEDATA-113] + _ = x[OUNSAFESTRING-114] + _ = x[OUNSAFESTRINGDATA-115] + _ = x[OMETHEXPR-116] + _ = x[OMETHVALUE-117] + _ = x[OBLOCK-118] + _ = x[OBREAK-119] + _ = x[OCASE-120] + _ = x[OCONTINUE-121] + _ = x[ODEFER-122] + _ = x[OFALL-123] + _ = x[OFOR-124] + _ = x[OGOTO-125] + _ = x[OIF-126] + _ = x[OLABEL-127] + _ = x[OGO-128] + _ = x[ORANGE-129] + _ = x[ORETURN-130] + _ = x[OSELECT-131] + _ = x[OSWITCH-132] + _ = x[OTYPESW-133] + _ = x[OFUNCINST-134] + _ = x[OINLCALL-135] + _ = x[OEFACE-136] + _ = x[OITAB-137] + _ = x[OIDATA-138] + _ = x[OSPTR-139] + _ = x[OCFUNC-140] + _ = x[OCHECKNIL-141] + _ = x[ORESULT-142] + _ = x[OINLMARK-143] + _ = x[OLINKSYMOFFSET-144] + _ = x[OJUMPTABLE-145] + _ = x[ODYNAMICDOTTYPE-146] + _ = x[ODYNAMICDOTTYPE2-147] + _ = x[ODYNAMICTYPE-148] + _ = x[OTAILCALL-149] + _ = x[OGETG-150] + _ = x[OGETCALLERPC-151] + _ = x[OGETCALLERSP-152] + _ = x[OEND-153] } -const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND" +const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" -var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 213, 216, 221, 228, 235, 241, 250, 258, 266, 272, 276, 285, 292, 296, 299, 306, 314, 321, 327, 330, 336, 343, 351, 355, 362, 370, 372, 374, 376, 378, 380, 382, 387, 392, 400, 403, 412, 415, 419, 427, 434, 443, 456, 459, 462, 465, 468, 471, 474, 480, 483, 486, 492, 496, 499, 503, 508, 513, 519, 524, 528, 533, 541, 549, 555, 564, 575, 582, 586, 593, 601, 605, 609, 613, 620, 627, 635, 641, 650, 661, 669, 674, 679, 683, 691, 696, 700, 703, 711, 715, 717, 722, 724, 729, 735, 741, 747, 753, 761, 766, 770, 777, 783, 788, 794, 800, 807, 812, 816, 821, 825, 830, 838, 844, 851, 858, 864, 871, 884, 892, 896, 899} +var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 132, 134, 137, 147, 154, 161, 168, 172, 176, 184, 192, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 282, 289, 293, 296, 303, 311, 318, 324, 327, 333, 340, 348, 352, 359, 367, 369, 371, 373, 375, 377, 379, 384, 389, 397, 400, 409, 412, 416, 424, 431, 440, 453, 456, 459, 462, 465, 468, 471, 477, 480, 483, 489, 493, 496, 500, 505, 510, 516, 521, 525, 530, 538, 546, 552, 561, 572, 584, 591, 600, 604, 611, 619, 623, 627, 634, 641, 649, 655, 664, 675, 690, 702, 718, 726, 735, 740, 745, 749, 757, 762, 766, 769, 773, 775, 780, 782, 787, 793, 799, 805, 811, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 883, 892, 906, 921, 932, 940, 944, 955, 966, 969} func (i Op) String() string { if i >= Op(len(_Op_index)-1) { diff --git a/src/cmd/compile/internal/ir/package.go b/src/cmd/compile/internal/ir/package.go index e4b93d113eda9c..3896e2b91b1177 100644 --- a/src/cmd/compile/internal/ir/package.go +++ b/src/cmd/compile/internal/ir/package.go @@ -32,7 +32,4 @@ type Package struct { // Exported (or re-exported) symbols. Exports []*Name - - // Map from function names of stencils to already-created stencils. - Stencils map[*types.Sym]*Func } diff --git a/src/cmd/compile/internal/ir/scc.go b/src/cmd/compile/internal/ir/scc.go index 83c6074170b313..a42951c1dda2de 100644 --- a/src/cmd/compile/internal/ir/scc.go +++ b/src/cmd/compile/internal/ir/scc.go @@ -90,7 +90,7 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 { if n := n.(*Name); n.Class == PFUNC { do(n.Defn) } - case ODOTMETH, OCALLPART, OMETHEXPR: + case ODOTMETH, OMETHVALUE, OMETHEXPR: if fn := MethodExprName(n); fn != nil { do(fn.Defn) } @@ -116,12 +116,11 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 { var i int for i = len(v.stack) - 1; i >= 0; i-- { x := v.stack[i] + v.nodeID[x] = ^uint32(0) if x == n { break } - v.nodeID[x] = ^uint32(0) } - v.nodeID[n] = ^uint32(0) block := v.stack[i:] // Run escape analysis on this set of functions. v.stack = v.stack[:i] diff --git a/src/cmd/compile/internal/ir/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go index a4421fcf531503..754d1a8de070bf 100644 --- a/src/cmd/compile/internal/ir/sizeof_test.go +++ b/src/cmd/compile/internal/ir/sizeof_test.go @@ -20,8 +20,8 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Func{}, 192, 328}, - {Name{}, 112, 200}, + {Func{}, 184, 320}, + {Name{}, 100, 176}, } for _, tt := range tests { diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 8115012f97852c..9f2d04f450554c 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -8,6 +8,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/types" "cmd/internal/src" + "go/constant" ) // A Decl is a declaration of a const, type, or var. (A declared func is a Func.) @@ -169,6 +170,17 @@ type CaseClause struct { miniStmt Var *Name // declared variable for this case in type switch List Nodes // list of expressions for switch, early select + + // RTypes is a list of RType expressions, which are copied to the + // corresponding OEQ nodes that are emitted when switch statements + // are desugared. RTypes[i] must be non-nil if the emitted + // comparison for List[i] will be a mixed interface/concrete + // comparison; see reflectdata.CompareRType for details. + // + // Because mixed interface/concrete switch cases are rare, we allow + // len(RTypes) < len(List). Missing entries are implicitly nil. + RTypes Nodes + Body Nodes } @@ -193,12 +205,10 @@ func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause { } // A ForStmt is a non-range for loop: for Init; Cond; Post { Body } -// Op can be OFOR or OFORUNTIL (!Cond). type ForStmt struct { miniStmt Label *types.Sym Cond Node - Late Nodes Post Node Body Nodes HasBreak bool @@ -215,13 +225,6 @@ func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt return n } -func (n *ForStmt) SetOp(op Op) { - if op != OFOR && op != OFORUNTIL { - panic(n.no("SetOp " + op.String())) - } - n.op = op -} - // A GoDeferStmt is a go or defer statement: go Call / defer Call. // // The two opcodes use a single syntax because the implementations @@ -244,7 +247,7 @@ func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt { return n } -// A IfStmt is a return statement: if Init; Cond { Then } else { Else }. +// An IfStmt is a return statement: if Init; Cond { Body } else { Else }. type IfStmt struct { miniStmt Cond Node @@ -262,6 +265,39 @@ func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt { return n } +// A JumpTableStmt is used to implement switches. Its semantics are: +// +// tmp := jt.Idx +// if tmp == Cases[0] goto Targets[0] +// if tmp == Cases[1] goto Targets[1] +// ... +// if tmp == Cases[n] goto Targets[n] +// +// Note that a JumpTableStmt is more like a multiway-goto than +// a multiway-if. In particular, the case bodies are just +// labels to jump to, not not full Nodes lists. +type JumpTableStmt struct { + miniStmt + + // Value used to index the jump table. + // We support only integer types that + // are at most the size of a uintptr. + Idx Node + + // If Idx is equal to Cases[i], jump to Targets[i]. + // Cases entries must be distinct and in increasing order. + // The length of Cases and Targets must be equal. + Cases []constant.Value + Targets []*types.Sym +} + +func NewJumpTableStmt(pos src.XPos, idx Node) *JumpTableStmt { + n := &JumpTableStmt{Idx: idx} + n.pos = pos + n.op = OJUMPTABLE + return n +} + // An InlineMarkStmt is a marker placed just before an inlined body. type InlineMarkStmt struct { miniStmt @@ -299,11 +335,20 @@ type RangeStmt struct { Label *types.Sym Def bool X Node + RType Node `mknode:"-"` // see reflectdata/helpers.go Key Node Value Node Body Nodes HasBreak bool Prealloc *Name + + // When desugaring the RangeStmt during walk, the assignments to Key + // and Value may require OCONVIFACE operations. If so, these fields + // will be copied to their respective ConvExpr fields. + KeyTypeWord Node `mknode:"-"` + KeySrcRType Node `mknode:"-"` + ValueTypeWord Node `mknode:"-"` + ValueSrcRType Node `mknode:"-"` } func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt { @@ -338,7 +383,7 @@ type SelectStmt struct { HasBreak bool // TODO(rsc): Instead of recording here, replace with a block? - Compiled Nodes // compiled form, after walkSwitch + Compiled Nodes // compiled form, after walkSelect } func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt { @@ -362,7 +407,7 @@ func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt { return n } -// A SwitchStmt is a switch statement: switch Init; Expr { Cases }. +// A SwitchStmt is a switch statement: switch Init; Tag { Cases }. type SwitchStmt struct { miniStmt Tag Node @@ -385,14 +430,11 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt { // code generation to jump directly to another function entirely. type TailCallStmt struct { miniStmt - Target *Name + Call *CallExpr // the underlying call } -func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt { - if target.Op() != ONAME || target.Class != PFUNC { - base.FatalfAt(pos, "tail call to non-func %v", target) - } - n := &TailCallStmt{Target: target} +func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt { + n := &TailCallStmt{Call: call} n.pos = pos n.op = OTAILCALL return n diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go index 61727fb1c4b004..148edb2c885e42 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -11,33 +11,37 @@ import ( // Syms holds known symbols. var Syms struct { - AssertE2I *obj.LSym - AssertE2I2 *obj.LSym - AssertI2I *obj.LSym - AssertI2I2 *obj.LSym - Deferproc *obj.LSym - DeferprocStack *obj.LSym - Deferreturn *obj.LSym - Duffcopy *obj.LSym - Duffzero *obj.LSym - GCWriteBarrier *obj.LSym - Goschedguarded *obj.LSym - Growslice *obj.LSym - Msanread *obj.LSym - Msanwrite *obj.LSym - Msanmove *obj.LSym - Newobject *obj.LSym - Newproc *obj.LSym - Panicdivide *obj.LSym - Panicshift *obj.LSym - PanicdottypeE *obj.LSym - PanicdottypeI *obj.LSym - Panicnildottype *obj.LSym - Panicoverflow *obj.LSym - Raceread *obj.LSym - Racereadrange *obj.LSym - Racewrite *obj.LSym - Racewriterange *obj.LSym + AssertE2I *obj.LSym + AssertE2I2 *obj.LSym + AssertI2I *obj.LSym + AssertI2I2 *obj.LSym + Asanread *obj.LSym + Asanwrite *obj.LSym + CheckPtrAlignment *obj.LSym + Deferproc *obj.LSym + DeferprocStack *obj.LSym + Deferreturn *obj.LSym + Duffcopy *obj.LSym + Duffzero *obj.LSym + GCWriteBarrier *obj.LSym + Goschedguarded *obj.LSym + Growslice *obj.LSym + Memmove *obj.LSym + Msanread *obj.LSym + Msanwrite *obj.LSym + Msanmove *obj.LSym + Newobject *obj.LSym + Newproc *obj.LSym + Panicdivide *obj.LSym + Panicshift *obj.LSym + PanicdottypeE *obj.LSym + PanicdottypeI *obj.LSym + Panicnildottype *obj.LSym + Panicoverflow *obj.LSym + Raceread *obj.LSym + Racereadrange *obj.LSym + Racewrite *obj.LSym + Racewriterange *obj.LSym // Wasm SigPanic *obj.LSym Staticuint64s *obj.LSym @@ -68,5 +72,4 @@ var Pkgs struct { Go *types.Pkg Itab *types.Pkg Runtime *types.Pkg - Unsafe *types.Pkg } diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go index a903ea8cd45543..033d1eed4a252d 100644 --- a/src/cmd/compile/internal/ir/type.go +++ b/src/cmd/compile/internal/ir/type.go @@ -26,259 +26,24 @@ type Ntype interface { CanBeNtype() } -// A miniType is a minimal type syntax Node implementation, -// to be embedded as the first field in a larger node implementation. -type miniType struct { - miniNode - typ *types.Type -} - -func (*miniType) CanBeNtype() {} - -func (n *miniType) Type() *types.Type { return n.typ } - -// setOTYPE changes n to be an OTYPE node returning t. -// Rewriting the node in place this way should not be strictly -// necessary (we should be able to update the uses with -// proper OTYPE nodes), but it's mostly harmless and easy -// to keep doing for now. -// -// setOTYPE also records t.Nod = self if t.Nod is not already set. -// (Some types are shared by multiple OTYPE nodes, so only -// the first such node is used as t.Nod.) -func (n *miniType) setOTYPE(t *types.Type, self Ntype) { - if n.typ != nil { - panic(n.op.String() + " SetType: type already set") - } - n.op = OTYPE - n.typ = t - t.SetNod(self) -} - -func (n *miniType) Sym() *types.Sym { return nil } // for Format OTYPE -func (n *miniType) Implicit() bool { return false } // for Format OTYPE - -// A ChanType represents a chan Elem syntax with the direction Dir. -type ChanType struct { - miniType - Elem Ntype - Dir types.ChanDir -} - -func NewChanType(pos src.XPos, elem Ntype, dir types.ChanDir) *ChanType { - n := &ChanType{Elem: elem, Dir: dir} - n.op = OTCHAN - n.pos = pos - return n -} - -func (n *ChanType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Elem = nil -} - -// A MapType represents a map[Key]Value type syntax. -type MapType struct { - miniType - Key Ntype - Elem Ntype -} - -func NewMapType(pos src.XPos, key, elem Ntype) *MapType { - n := &MapType{Key: key, Elem: elem} - n.op = OTMAP - n.pos = pos - return n -} - -func (n *MapType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Key = nil - n.Elem = nil -} - -// A StructType represents a struct { ... } type syntax. -type StructType struct { - miniType - Fields []*Field -} - -func NewStructType(pos src.XPos, fields []*Field) *StructType { - n := &StructType{Fields: fields} - n.op = OTSTRUCT - n.pos = pos - return n -} - -func (n *StructType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Fields = nil -} - -// An InterfaceType represents a struct { ... } type syntax. -type InterfaceType struct { - miniType - Methods []*Field -} - -func NewInterfaceType(pos src.XPos, methods []*Field) *InterfaceType { - n := &InterfaceType{Methods: methods} - n.op = OTINTER - n.pos = pos - return n -} - -func (n *InterfaceType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Methods = nil -} - -// A FuncType represents a func(Args) Results type syntax. -type FuncType struct { - miniType - Recv *Field - Params []*Field - Results []*Field -} - -func NewFuncType(pos src.XPos, rcvr *Field, args, results []*Field) *FuncType { - n := &FuncType{Recv: rcvr, Params: args, Results: results} - n.op = OTFUNC - n.pos = pos - return n -} - -func (n *FuncType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Recv = nil - n.Params = nil - n.Results = nil -} - -// A Field is a declared struct field, interface method, or function argument. +// A Field is a declared function parameter. // It is not a Node. type Field struct { - Pos src.XPos - Sym *types.Sym - Ntype Ntype - Type *types.Type - Embedded bool - IsDDD bool - Note string - Decl *Name + Pos src.XPos + Sym *types.Sym + Type *types.Type + IsDDD bool } -func NewField(pos src.XPos, sym *types.Sym, ntyp Ntype, typ *types.Type) *Field { - return &Field{Pos: pos, Sym: sym, Ntype: ntyp, Type: typ} +func NewField(pos src.XPos, sym *types.Sym, typ *types.Type) *Field { + return &Field{Pos: pos, Sym: sym, Type: typ} } func (f *Field) String() string { - var typ string - if f.Type != nil { - typ = fmt.Sprint(f.Type) - } else { - typ = fmt.Sprint(f.Ntype) - } if f.Sym != nil { - return fmt.Sprintf("%v %v", f.Sym, typ) - } - return typ -} - -// TODO(mdempsky): Make Field a Node again so these can be generated? -// Fields are Nodes in go/ast and cmd/compile/internal/syntax. - -func copyField(f *Field) *Field { - if f == nil { - return nil - } - c := *f - return &c -} -func doField(f *Field, do func(Node) bool) bool { - if f == nil { - return false - } - if f.Decl != nil && do(f.Decl) { - return true - } - if f.Ntype != nil && do(f.Ntype) { - return true - } - return false -} -func editField(f *Field, edit func(Node) Node) { - if f == nil { - return - } - if f.Decl != nil { - f.Decl = edit(f.Decl).(*Name) - } - if f.Ntype != nil { - f.Ntype = edit(f.Ntype).(Ntype) - } -} - -func copyFields(list []*Field) []*Field { - out := make([]*Field, len(list)) - for i, f := range list { - out[i] = copyField(f) + return fmt.Sprintf("%v %v", f.Sym, f.Type) } - return out -} -func doFields(list []*Field, do func(Node) bool) bool { - for _, x := range list { - if doField(x, do) { - return true - } - } - return false -} -func editFields(list []*Field, edit func(Node) Node) { - for _, f := range list { - editField(f, edit) - } -} - -// A SliceType represents a []Elem type syntax. -// If DDD is true, it's the ...Elem at the end of a function list. -type SliceType struct { - miniType - Elem Ntype - DDD bool -} - -func NewSliceType(pos src.XPos, elem Ntype) *SliceType { - n := &SliceType{Elem: elem} - n.op = OTSLICE - n.pos = pos - return n -} - -func (n *SliceType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Elem = nil -} - -// An ArrayType represents a [Len]Elem type syntax. -// If Len is nil, the type is a [...]Elem in an array literal. -type ArrayType struct { - miniType - Len Node - Elem Ntype -} - -func NewArrayType(pos src.XPos, len Node, elem Ntype) *ArrayType { - n := &ArrayType{Len: len, Elem: elem} - n.op = OTARRAY - n.pos = pos - return n -} - -func (n *ArrayType) SetOTYPE(t *types.Type) { - n.setOTYPE(t, n) - n.Len = nil - n.Elem = nil + return fmt.Sprint(f.Type) } // A typeNode is a Node wrapper for type t. @@ -287,9 +52,9 @@ type typeNode struct { typ *types.Type } -func newTypeNode(pos src.XPos, typ *types.Type) *typeNode { +func newTypeNode(typ *types.Type) *typeNode { n := &typeNode{typ: typ} - n.pos = pos + n.pos = src.NoXPos n.op = OTYPE return n } @@ -306,5 +71,33 @@ func TypeNode(t *types.Type) Ntype { } return n.(Ntype) } - return newTypeNode(src.NoXPos, t) + return newTypeNode(t) +} + +// A DynamicType represents a type expression whose exact type must be +// computed dynamically. +type DynamicType struct { + miniExpr + + // RType is an expression that yields a *runtime._type value + // representing the asserted type. + // + // BUG(mdempsky): If ITab is non-nil, RType may be nil. + RType Node + + // ITab is an expression that yields a *runtime.itab value + // representing the asserted type within the assertee expression's + // original interface type. + // + // ITab is only used for assertions (including type switches) from + // non-empty interface type to a concrete (i.e., non-interface) + // type. For all other assertions, ITab is nil. + ITab Node +} + +func NewDynamicType(pos src.XPos, rtype Node) *DynamicType { + n := &DynamicType{RType: rtype} + n.pos = pos + n.op = ODYNAMICTYPE + return n } diff --git a/src/cmd/compile/internal/ir/val.go b/src/cmd/compile/internal/ir/val.go index 03c320e205dbfd..925222b1137e20 100644 --- a/src/cmd/compile/internal/ir/val.go +++ b/src/cmd/compile/internal/ir/val.go @@ -6,7 +6,6 @@ package ir import ( "go/constant" - "math" "cmd/compile/internal/base" "cmd/compile/internal/types" @@ -19,27 +18,6 @@ func ConstType(n Node) constant.Kind { return n.Val().Kind() } -// ConstValue returns the constant value stored in n as an interface{}. -// It returns int64s for ints and runes, float64s for floats, -// and complex128s for complex values. -func ConstValue(n Node) interface{} { - switch v := n.Val(); v.Kind() { - default: - base.Fatalf("unexpected constant: %v", v) - panic("unreachable") - case constant.Bool: - return constant.BoolVal(v) - case constant.String: - return constant.StringVal(v) - case constant.Int: - return IntVal(n.Type(), v) - case constant.Float: - return Float64Val(v) - case constant.Complex: - return complex(Float64Val(constant.Real(v)), Float64Val(constant.Imag(v))) - } -} - // IntVal returns v converted to int64. // Note: if t is uint64, very large values will be converted to negative int64. func IntVal(t *types.Type, v constant.Value) int64 { @@ -56,17 +34,9 @@ func IntVal(t *types.Type, v constant.Value) int64 { panic("unreachable") } -func Float64Val(v constant.Value) float64 { - if x, _ := constant.Float64Val(v); !math.IsInf(x, 0) { - return x + 0 // avoid -0 (should not be needed, but be conservative) - } - base.Fatalf("bad float64 value: %v", v) - panic("unreachable") -} - func AssertValidTypeForConst(t *types.Type, v constant.Value) { if !ValidTypeForConst(t, v) { - base.Fatalf("%v does not represent %v", t, v) + base.Fatalf("%v (%v) does not represent %v (%v)", t, t.Kind(), v, v.Kind()) } } @@ -114,18 +84,6 @@ func idealType(ct constant.Kind) *types.Type { var OKForConst [types.NTYPE]bool -// CanInt64 reports whether it is safe to call Int64Val() on n. -func CanInt64(n Node) bool { - if !IsConst(n, constant.Int) { - return false - } - - // if the value inside n cannot be represented as an int64, the - // return value of Int64 is undefined - _, ok := constant.Int64Val(n.Val()) - return ok -} - // Int64Val returns n as an int64. // n must be an integer or rune constant. func Int64Val(n Node) int64 { diff --git a/src/cmd/compile/internal/liveness/arg.go b/src/cmd/compile/internal/liveness/arg.go new file mode 100644 index 00000000000000..abbc8c97714908 --- /dev/null +++ b/src/cmd/compile/internal/liveness/arg.go @@ -0,0 +1,339 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package liveness + +import ( + "fmt" + + "cmd/compile/internal/base" + "cmd/compile/internal/bitvec" + "cmd/compile/internal/ir" + "cmd/compile/internal/objw" + "cmd/compile/internal/ssa" + "cmd/internal/obj" + "cmd/internal/objabi" +) + +// Argument liveness tracking. +// +// For arguments passed in registers, this file tracks if their spill slots +// are live for runtime traceback. An argument spill slot is live at a PC +// if we know that an actual value has stored into it at or before this point. +// +// Stack args are always live and not tracked in this code. Stack args are +// laid out before register spill slots, so we emit the smallest offset that +// needs tracking. Slots before that offset are always live. That offset is +// usually the offset of the first spill slot. But if the first spill slot is +// always live (e.g. if it is address-taken), it will be the offset of a later +// one. +// +// The liveness information is emitted as a FUNCDATA and a PCDATA. +// +// FUNCDATA format: +// - start (smallest) offset that needs tracking (1 byte) +// - a list of bitmaps. +// In a bitmap bit i is set if the i-th spill slot is live. +// +// At a PC where the liveness info changes, a PCDATA indicates the +// byte offset of the liveness map in the FUNCDATA. PCDATA -1 is a +// special case indicating all slots are live (for binary size +// saving). + +const allLiveIdx = -1 + +// name and offset +type nameOff struct { + n *ir.Name + off int64 +} + +func (a nameOff) FrameOffset() int64 { return a.n.FrameOffset() + a.off } +func (a nameOff) String() string { return fmt.Sprintf("%v+%d", a.n, a.off) } + +type blockArgEffects struct { + livein bitvec.BitVec // variables live at block entry + liveout bitvec.BitVec // variables live at block exit +} + +type argLiveness struct { + fn *ir.Func + f *ssa.Func + args []nameOff // name and offset of spill slots + idx map[nameOff]int32 // index in args + + be []blockArgEffects // indexed by block ID + + bvset bvecSet // Set of liveness bitmaps, used for uniquifying. + + // Liveness map indices at each Value (where it changes) and Block entry. + // During the computation the indices are temporarily index to bvset. + // At the end they will be index (offset) to the output funcdata (changed + // in (*argLiveness).emit). + blockIdx map[ssa.ID]int + valueIdx map[ssa.ID]int +} + +// ArgLiveness computes the liveness information of register argument spill slots. +// An argument's spill slot is "live" if we know it contains a meaningful value, +// that is, we have stored the register value to it. +// Returns the liveness map indices at each Block entry and at each Value (where +// it changes). +func ArgLiveness(fn *ir.Func, f *ssa.Func, pp *objw.Progs) (blockIdx, valueIdx map[ssa.ID]int) { + if f.OwnAux.ABIInfo().InRegistersUsed() == 0 || base.Flag.N != 0 { + // No register args. Nothing to emit. + // Or if -N is used we spill everything upfront so it is always live. + return nil, nil + } + + lv := &argLiveness{ + fn: fn, + f: f, + idx: make(map[nameOff]int32), + be: make([]blockArgEffects, f.NumBlocks()), + blockIdx: make(map[ssa.ID]int), + valueIdx: make(map[ssa.ID]int), + } + // Gather all register arg spill slots. + for _, a := range f.OwnAux.ABIInfo().InParams() { + n, ok := a.Name.(*ir.Name) + if !ok || len(a.Registers) == 0 { + continue + } + _, offs := a.RegisterTypesAndOffsets() + for _, off := range offs { + if n.FrameOffset()+off > 0xff { + // We only print a limited number of args, with stack + // offsets no larger than 255. + continue + } + lv.args = append(lv.args, nameOff{n, off}) + } + } + if len(lv.args) > 10 { + lv.args = lv.args[:10] // We print no more than 10 args. + } + + // We spill address-taken or non-SSA-able value upfront, so they are always live. + alwaysLive := func(n *ir.Name) bool { return n.Addrtaken() || !f.Frontend().CanSSA(n.Type()) } + + // We'll emit the smallest offset for the slots that need liveness info. + // No need to include a slot with a lower offset if it is always live. + for len(lv.args) > 0 && alwaysLive(lv.args[0].n) { + lv.args = lv.args[1:] + } + if len(lv.args) == 0 { + return // everything is always live + } + + for i, a := range lv.args { + lv.idx[a] = int32(i) + } + + nargs := int32(len(lv.args)) + bulk := bitvec.NewBulk(nargs, int32(len(f.Blocks)*2)) + for _, b := range f.Blocks { + be := &lv.be[b.ID] + be.livein = bulk.Next() + be.liveout = bulk.Next() + + // initialize to all 1s, so we can AND them + be.livein.Not() + be.liveout.Not() + } + + entrybe := &lv.be[f.Entry.ID] + entrybe.livein.Clear() + for i, a := range lv.args { + if alwaysLive(a.n) { + entrybe.livein.Set(int32(i)) + } + } + + // Visit blocks in reverse-postorder, compute block effects. + po := f.Postorder() + for i := len(po) - 1; i >= 0; i-- { + b := po[i] + be := &lv.be[b.ID] + + // A slot is live at block entry if it is live in all predecessors. + for _, pred := range b.Preds { + pb := pred.Block() + be.livein.And(be.livein, lv.be[pb.ID].liveout) + } + + be.liveout.Copy(be.livein) + for _, v := range b.Values { + lv.valueEffect(v, be.liveout) + } + } + + // Coalesce identical live vectors. Compute liveness indices at each PC + // where it changes. + live := bitvec.New(nargs) + addToSet := func(bv bitvec.BitVec) (int, bool) { + if bv.Count() == int(nargs) { // special case for all live + return allLiveIdx, false + } + return lv.bvset.add(bv) + } + for _, b := range lv.f.Blocks { + be := &lv.be[b.ID] + lv.blockIdx[b.ID], _ = addToSet(be.livein) + + live.Copy(be.livein) + var lastv *ssa.Value + for i, v := range b.Values { + if lv.valueEffect(v, live) { + // Record that liveness changes but not emit a map now. + // For a sequence of StoreRegs we only need to emit one + // at last. + lastv = v + } + if lastv != nil && (mayFault(v) || i == len(b.Values)-1) { + // Emit the liveness map if it may fault or at the end of + // the block. We may need a traceback if the instruction + // may cause a panic. + var added bool + lv.valueIdx[lastv.ID], added = addToSet(live) + if added { + // live is added to bvset and we cannot modify it now. + // Make a copy. + t := live + live = bitvec.New(nargs) + live.Copy(t) + } + lastv = nil + } + } + + // Sanity check. + if !live.Eq(be.liveout) { + panic("wrong arg liveness map at block end") + } + } + + // Emit funcdata symbol, update indices to offsets in the symbol data. + lsym := lv.emit() + fn.LSym.Func().ArgLiveInfo = lsym + + //lv.print() + + p := pp.Prog(obj.AFUNCDATA) + p.From.SetConst(objabi.FUNCDATA_ArgLiveInfo) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = lsym + + return lv.blockIdx, lv.valueIdx +} + +// valueEffect applies the effect of v to live, return whether it is changed. +func (lv *argLiveness) valueEffect(v *ssa.Value, live bitvec.BitVec) bool { + if v.Op != ssa.OpStoreReg { // TODO: include other store instructions? + return false + } + n, off := ssa.AutoVar(v) + if n.Class != ir.PPARAM { + return false + } + i, ok := lv.idx[nameOff{n, off}] + if !ok || live.Get(i) { + return false + } + live.Set(i) + return true +} + +func mayFault(v *ssa.Value) bool { + switch v.Op { + case ssa.OpLoadReg, ssa.OpStoreReg, ssa.OpCopy, ssa.OpPhi, + ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, + ssa.OpSelect0, ssa.OpSelect1, ssa.OpSelectN, ssa.OpMakeResult, + ssa.OpConvert, ssa.OpInlMark, ssa.OpGetG: + return false + } + if len(v.Args) == 0 { + return false // assume constant op cannot fault + } + return true // conservatively assume all other ops could fault +} + +func (lv *argLiveness) print() { + fmt.Println("argument liveness:", lv.f.Name) + live := bitvec.New(int32(len(lv.args))) + for _, b := range lv.f.Blocks { + be := &lv.be[b.ID] + + fmt.Printf("%v: live in: ", b) + lv.printLivenessVec(be.livein) + if idx, ok := lv.blockIdx[b.ID]; ok { + fmt.Printf(" #%d", idx) + } + fmt.Println() + + for _, v := range b.Values { + if lv.valueEffect(v, live) { + fmt.Printf(" %v: ", v) + lv.printLivenessVec(live) + if idx, ok := lv.valueIdx[v.ID]; ok { + fmt.Printf(" #%d", idx) + } + fmt.Println() + } + } + + fmt.Printf("%v: live out: ", b) + lv.printLivenessVec(be.liveout) + fmt.Println() + } + fmt.Println("liveness maps data:", lv.fn.LSym.Func().ArgLiveInfo.P) +} + +func (lv *argLiveness) printLivenessVec(bv bitvec.BitVec) { + for i, a := range lv.args { + if bv.Get(int32(i)) { + fmt.Printf("%v ", a) + } + } +} + +func (lv *argLiveness) emit() *obj.LSym { + livenessMaps := lv.bvset.extractUnique() + + // stack offsets of register arg spill slots + argOffsets := make([]uint8, len(lv.args)) + for i, a := range lv.args { + off := a.FrameOffset() + if off > 0xff { + panic("offset too large") + } + argOffsets[i] = uint8(off) + } + + idx2off := make([]int, len(livenessMaps)) + + lsym := base.Ctxt.Lookup(lv.fn.LSym.Name + ".argliveinfo") + lsym.Set(obj.AttrContentAddressable, true) + + off := objw.Uint8(lsym, 0, argOffsets[0]) // smallest offset that needs liveness info. + for idx, live := range livenessMaps { + idx2off[idx] = off + off = objw.BitVec(lsym, off, live) + } + + // Update liveness indices to offsets. + for i, x := range lv.blockIdx { + if x != allLiveIdx { + lv.blockIdx[i] = idx2off[x] + } + } + for i, x := range lv.valueIdx { + if x != allLiveIdx { + lv.valueIdx[i] = idx2off[x] + } + } + + return lsym +} diff --git a/src/cmd/compile/internal/liveness/bvset.go b/src/cmd/compile/internal/liveness/bvset.go index 3431f54ede84e0..60b25938677c85 100644 --- a/src/cmd/compile/internal/liveness/bvset.go +++ b/src/cmd/compile/internal/liveness/bvset.go @@ -47,9 +47,10 @@ func (m *bvecSet) grow() { m.index = newIndex } -// add adds bv to the set and returns its index in m.extractUnique. -// The caller must not modify bv after this. -func (m *bvecSet) add(bv bitvec.BitVec) int { +// add adds bv to the set and returns its index in m.extractUnique, +// and whether it is newly added. +// If it is newly added, the caller must not modify bv after this. +func (m *bvecSet) add(bv bitvec.BitVec) (int, bool) { if len(m.uniq)*4 >= len(m.index) { m.grow() } @@ -62,12 +63,12 @@ func (m *bvecSet) add(bv bitvec.BitVec) int { // New bvec. index[h] = len(m.uniq) m.uniq = append(m.uniq, bv) - return len(m.uniq) - 1 + return len(m.uniq) - 1, true } jlive := m.uniq[j] if bv.Eq(jlive) { // Existing bvec. - return j + return j, false } h++ diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go index f5c2ef7709e515..1a2d3a45c99c8a 100644 --- a/src/cmd/compile/internal/liveness/plive.go +++ b/src/cmd/compile/internal/liveness/plive.go @@ -15,8 +15,6 @@ package liveness import ( - "crypto/md5" - "crypto/sha1" "fmt" "os" "sort" @@ -31,6 +29,7 @@ import ( "cmd/compile/internal/ssa" "cmd/compile/internal/typebits" "cmd/compile/internal/types" + "cmd/internal/notsha256" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" @@ -81,15 +80,6 @@ import ( // the liveness analysis work on single-word values as well, although // there are complications around interface values, slices, and strings, // all of which cannot be treated as individual words. -// -// OpVarKill is the opposite of OpVarDef: it marks a value as no longer needed, -// even if its address has been taken. That is, an OpVarKill annotation asserts -// that its argument is certainly dead, for use when the liveness analysis -// would not otherwise be able to deduce that fact. - -// TODO: get rid of OpVarKill here. It's useful for stack frame allocation -// so the compiler can allocate two temps to the same location. Here it's now -// useless, since the implementation of stack objects. // blockEffects summarizes the liveness effects on an SSA block. type blockEffects struct { @@ -245,8 +235,10 @@ func (lv *liveness) initcache() { // liveness effects on a variable. // // The possible flags are: +// // uevar - used by the instruction // varkill - killed by the instruction (set) +// // A kill happens after the use (for an instruction that updates a value, for example). type liveEffect int @@ -268,13 +260,13 @@ func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) { // OpVarFoo pseudo-ops. Ignore them to prevent "lost track of // variable" ICEs (issue 19632). switch v.Op { - case ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive: + case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive: if !n.Used() { return -1, 0 } } - if n.Class == ir.PPARAM && !n.Addrtaken() && n.Type().Width > int64(types.PtrSize) { + if n.Class == ir.PPARAM && !n.Addrtaken() && n.Type().Size() > int64(types.PtrSize) { // Only aggregate-typed arguments that are not address-taken can be // partially live. lv.partLiveArgs[n] = true @@ -333,7 +325,7 @@ func affectedVar(v *ssa.Value) (*ir.Name, ssa.SymEffect) { case ssa.OpVarLive: return v.Aux.(*ir.Name), ssa.SymRead - case ssa.OpVarDef, ssa.OpVarKill: + case ssa.OpVarDef: return v.Aux.(*ir.Name), ssa.SymWrite case ssa.OpKeepAlive: n, _ := ssa.AutoVar(v.Args[0]) @@ -855,8 +847,9 @@ func (lv *liveness) epilogue() { if lv.fn.OpenCodedDeferDisallowed() { lv.livenessMap.DeferReturn = objw.LivenessDontCare } else { + idx, _ := lv.stackMapSet.add(livedefer) lv.livenessMap.DeferReturn = objw.LivenessIndex{ - StackMapIndex: lv.stackMapSet.add(livedefer), + StackMapIndex: idx, IsUnsafePoint: false, } } @@ -903,7 +896,7 @@ func (lv *liveness) compact(b *ssa.Block) { isUnsafePoint := lv.allUnsafe || v.Op != ssa.OpClobber && lv.unsafePoints.Get(int32(v.ID)) idx := objw.LivenessIndex{StackMapIndex: objw.StackMapDontCare, IsUnsafePoint: isUnsafePoint} if hasStackMap { - idx.StackMapIndex = lv.stackMapSet.add(lv.livevars[pos]) + idx.StackMapIndex, _ = lv.stackMapSet.add(lv.livevars[pos]) pos++ } if hasStackMap || isUnsafePoint { @@ -957,7 +950,7 @@ func (lv *liveness) enableClobber() { // Clobber only functions where the hash of the function name matches a pattern. // Useful for binary searching for a miscompiled function. hstr := "" - for _, b := range sha1.Sum([]byte(lv.f.Name)) { + for _, b := range notsha256.Sum256([]byte(lv.f.Name)) { hstr += fmt.Sprintf("%08b", b) } if !strings.HasSuffix(hstr, h) { @@ -1082,6 +1075,10 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) { if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") { return } + if lv.fn.Wrapper() || lv.fn.Dupok() { + // Skip reporting liveness information for compiler-generated wrappers. + return + } if !(v == nil || v.Op.IsCall()) { // Historically we only printed this information at // calls. Keep doing so. @@ -1322,19 +1319,9 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) { loff = objw.BitVec(&liveSymTmp, loff, locals) } - // Give these LSyms content-addressable names, - // so that they can be de-duplicated. - // This provides significant binary size savings. - // // These symbols will be added to Ctxt.Data by addGCLocals // after parallel compilation is done. - makeSym := func(tmpSym *obj.LSym) *obj.LSym { - return base.Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) { - lsym.P = tmpSym.P - lsym.Set(obj.AttrContentAddressable, true) - }) - } - return makeSym(&argsSymTmp), makeSym(&liveSymTmp) + return base.Ctxt.GCLocalsSym(argsSymTmp.P), base.Ctxt.GCLocalsSym(liveSymTmp.P) } // Entry pointer for Compute analysis. Solves for the Compute of @@ -1424,6 +1411,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym { // Populate the stack object data. // Format must match runtime/stack.go:stackObjectRecord. x := base.Ctxt.Lookup(lv.fn.LSym.Name + ".stkobj") + x.Set(obj.AttrContentAddressable, true) lv.fn.LSym.Func().StackObjects = x off := 0 off = objw.Uintptr(x, off, uint64(len(vars))) @@ -1440,7 +1428,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym { off = objw.Uint32(x, off, uint32(frameOffset)) t := v.Type() - sz := t.Width + sz := t.Size() if sz != int64(int32(sz)) { base.Fatalf("stack object too big: %v of type %v, size %d", v, t, sz) } @@ -1450,7 +1438,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym { } off = objw.Uint32(x, off, uint32(sz)) off = objw.Uint32(x, off, uint32(ptrdata)) - off = objw.SymPtr(x, off, lsym, 0) + off = objw.SymPtrOff(x, off, lsym) } if base.Flag.Live != 0 { @@ -1465,14 +1453,14 @@ func (lv *liveness) emitStackObjects() *obj.LSym { // isfat reports whether a variable of type t needs multiple assignments to initialize. // For example: // -// type T struct { x, y int } -// x := T{x: 0, y: 1} +// type T struct { x, y int } +// x := T{x: 0, y: 1} // // Then we need: // -// var t T -// t.x = 0 -// t.y = 1 +// var t T +// t.x = 0 +// t.y = 1 // // to fully initialize t. func isfat(t *types.Type) bool { diff --git a/src/cmd/compile/internal/logopt/escape.go b/src/cmd/compile/internal/logopt/escape.go deleted file mode 100644 index 9660e938b4ae12..00000000000000 --- a/src/cmd/compile/internal/logopt/escape.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.8 -// +build go1.8 - -package logopt - -import "net/url" - -func pathEscape(s string) string { - return url.PathEscape(s) -} diff --git a/src/cmd/compile/internal/logopt/escape_bootstrap.go b/src/cmd/compile/internal/logopt/escape_bootstrap.go deleted file mode 100644 index cc04eaadfd1172..00000000000000 --- a/src/cmd/compile/internal/logopt/escape_bootstrap.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.8 -// +build !go1.8 - -package logopt - -// For bootstrapping with an early version of Go -func pathEscape(s string) string { - panic("This should never be called; the compiler is not fully bootstrapped if it is.") -} diff --git a/src/cmd/compile/internal/logopt/log_opts.go b/src/cmd/compile/internal/logopt/log_opts.go index 97ebf569448982..09825e8278fd4f 100644 --- a/src/cmd/compile/internal/logopt/log_opts.go +++ b/src/cmd/compile/internal/logopt/log_opts.go @@ -376,7 +376,7 @@ func writerForLSP(subdirpath, file string) io.WriteCloser { if lastdot != -1 { basename = basename[:lastdot] } - basename = pathEscape(basename) + basename = url.PathEscape(basename) // Assume a directory, make a file p := filepath.Join(subdirpath, basename+".json") @@ -405,7 +405,7 @@ func uriIfy(f string) DocumentURI { // Return filename, replacing a first occurrence of $GOROOT with the // actual value of the GOROOT (because LSP does not speak "$GOROOT"). func uprootedPath(filename string) string { - if !strings.HasPrefix(filename, "$GOROOT/") { + if buildcfg.GOROOT == "" || !strings.HasPrefix(filename, "$GOROOT/") { return filename } return buildcfg.GOROOT + filename[len("$GOROOT"):] @@ -428,7 +428,7 @@ func FlushLoggedOpts(ctxt *obj.Link, slashPkgPath string) { if slashPkgPath == "" { slashPkgPath = "\000" } - subdirpath := filepath.Join(dest, pathEscape(slashPkgPath)) + subdirpath := filepath.Join(dest, url.PathEscape(slashPkgPath)) err := os.MkdirAll(subdirpath, 0755) if err != nil { log.Fatalf("Could not create directory %s for logging optimizer actions, %v", subdirpath, err) diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go index 71976174b03517..411319f9e9eff6 100644 --- a/src/cmd/compile/internal/logopt/logopt_test.go +++ b/src/cmd/compile/internal/logopt/logopt_test.go @@ -155,7 +155,7 @@ func s15a8(x *[15]int64) [15]int64 { arches := []string{runtime.GOARCH} goos0 := runtime.GOOS if runtime.GOARCH == "amd64" { // Test many things with "linux" (wasm will get "js") - arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "ppc64le", "riscv64", "s390x", "wasm"} + arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "loong64", "ppc64le", "riscv64", "s390x", "wasm"} goos0 = "linux" } @@ -209,7 +209,7 @@ func s15a8(x *[15]int64) [15]int64 { want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`) want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`) // escape analysis explanation - want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+ + want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r0 with derefs=0",`+ `"relatedInformation":[`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+ @@ -220,13 +220,13 @@ func s15a8(x *[15]int64) [15]int64 { `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~R0:"},`+ - `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`) + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~R0:"},`+ + `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~R0 (return)"}]}`) }) } func testLogOpt(t *testing.T, flag, src, outfile string) (string, error) { - run := []string{testenv.GoToolPath(t), "tool", "compile", flag, "-o", outfile, src} + run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=p", flag, "-o", outfile, src} t.Log(run) cmd := exec.Command(run[0], run[1:]...) out, err := cmd.CombinedOutput() @@ -236,7 +236,7 @@ func testLogOpt(t *testing.T, flag, src, outfile string) (string, error) { func testLogOptDir(t *testing.T, dir, flag, src, outfile string) (string, error) { // Notice the specified import path "x" - run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", flag, "-o", outfile, src} + run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=x", flag, "-o", outfile, src} t.Log(run) cmd := exec.Command(run[0], run[1:]...) cmd.Dir = dir @@ -247,7 +247,7 @@ func testLogOptDir(t *testing.T, dir, flag, src, outfile string) (string, error) func testCopy(t *testing.T, dir, goarch, goos, src, outfile string) (string, error) { // Notice the specified import path "x" - run := []string{testenv.GoToolPath(t), "tool", "compile", "-p", "x", "-json=0,file://log/opt", "-o", outfile, src} + run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=x", "-json=0,file://log/opt", "-o", outfile, src} t.Log(run) cmd := exec.Command(run[0], run[1:]...) cmd.Dir = dir diff --git a/src/cmd/compile/internal/loong64/galign.go b/src/cmd/compile/internal/loong64/galign.go new file mode 100644 index 00000000000000..99ab7bdfb5f346 --- /dev/null +++ b/src/cmd/compile/internal/loong64/galign.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +import ( + "cmd/compile/internal/ssa" + "cmd/compile/internal/ssagen" + "cmd/internal/obj/loong64" +) + +func Init(arch *ssagen.ArchInfo) { + arch.LinkArch = &loong64.Linkloong64 + arch.REGSP = loong64.REGSP + arch.MAXWIDTH = 1 << 50 + arch.ZeroRange = zerorange + arch.Ginsnop = ginsnop + + arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} + arch.SSAGenValue = ssaGenValue + arch.SSAGenBlock = ssaGenBlock +} diff --git a/src/cmd/compile/internal/loong64/ggen.go b/src/cmd/compile/internal/loong64/ggen.go new file mode 100644 index 00000000000000..c6fd1a65a10650 --- /dev/null +++ b/src/cmd/compile/internal/loong64/ggen.go @@ -0,0 +1,59 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +import ( + "cmd/compile/internal/ir" + "cmd/compile/internal/objw" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/obj/loong64" +) + +func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog { + if cnt == 0 { + return p + } + if cnt < int64(4*types.PtrSize) { + for i := int64(0); i < cnt; i += int64(types.PtrSize) { + p = pp.Append(p, loong64.AMOVV, obj.TYPE_REG, loong64.REGZERO, 0, obj.TYPE_MEM, loong64.REGSP, 8+off+i) + } + } else if cnt <= int64(128*types.PtrSize) { + p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, loong64.REGRT1, 0) + p.Reg = loong64.REGSP + p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ir.Syms.Duffzero + p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize)) + } else { + // ADDV $(8+frame+lo-8), SP, r1 + // ADDV $cnt, r1, r2 + // loop: + // MOVV R0, (Widthptr)r1 + // ADDV $Widthptr, r1 + // BNE r1, r2, loop + p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, loong64.REGRT1, 0) + p.Reg = loong64.REGSP + p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, loong64.REGRT2, 0) + p.Reg = loong64.REGRT1 + p = pp.Append(p, loong64.AMOVV, obj.TYPE_REG, loong64.REGZERO, 0, obj.TYPE_MEM, loong64.REGRT1, int64(types.PtrSize)) + p1 := p + p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, loong64.REGRT1, 0) + p = pp.Append(p, loong64.ABNE, obj.TYPE_REG, loong64.REGRT1, 0, obj.TYPE_BRANCH, 0, 0) + p.Reg = loong64.REGRT2 + p.To.SetTarget(p1) + } + + return p +} + +func ginsnop(pp *objw.Progs) *obj.Prog { + p := pp.Prog(loong64.ANOR) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REG_R0 + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REG_R0 + return p +} diff --git a/src/cmd/compile/internal/loong64/ssa.go b/src/cmd/compile/internal/loong64/ssa.go new file mode 100644 index 00000000000000..e004c6f7fbecf1 --- /dev/null +++ b/src/cmd/compile/internal/loong64/ssa.go @@ -0,0 +1,865 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package loong64 + +import ( + "math" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/logopt" + "cmd/compile/internal/ssa" + "cmd/compile/internal/ssagen" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/obj/loong64" +) + +// isFPreg reports whether r is an FP register +func isFPreg(r int16) bool { + return loong64.REG_F0 <= r && r <= loong64.REG_F31 +} + +// loadByType returns the load instruction of the given type. +func loadByType(t *types.Type, r int16) obj.As { + if isFPreg(r) { + if t.Size() == 4 { + return loong64.AMOVF + } else { + return loong64.AMOVD + } + } else { + switch t.Size() { + case 1: + if t.IsSigned() { + return loong64.AMOVB + } else { + return loong64.AMOVBU + } + case 2: + if t.IsSigned() { + return loong64.AMOVH + } else { + return loong64.AMOVHU + } + case 4: + if t.IsSigned() { + return loong64.AMOVW + } else { + return loong64.AMOVWU + } + case 8: + return loong64.AMOVV + } + } + panic("bad load type") +} + +// storeByType returns the store instruction of the given type. +func storeByType(t *types.Type, r int16) obj.As { + if isFPreg(r) { + if t.Size() == 4 { + return loong64.AMOVF + } else { + return loong64.AMOVD + } + } else { + switch t.Size() { + case 1: + return loong64.AMOVB + case 2: + return loong64.AMOVH + case 4: + return loong64.AMOVW + case 8: + return loong64.AMOVV + } + } + panic("bad store type") +} + +func ssaGenValue(s *ssagen.State, v *ssa.Value) { + switch v.Op { + case ssa.OpCopy, ssa.OpLOONG64MOVVreg: + if v.Type.IsMemory() { + return + } + x := v.Args[0].Reg() + y := v.Reg() + if x == y { + return + } + as := loong64.AMOVV + if isFPreg(x) && isFPreg(y) { + as = loong64.AMOVD + } + p := s.Prog(as) + p.From.Type = obj.TYPE_REG + p.From.Reg = x + p.To.Type = obj.TYPE_REG + p.To.Reg = y + case ssa.OpLOONG64MOVVnop: + if v.Reg() != v.Args[0].Reg() { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) + } + // nothing to do + case ssa.OpLoadReg: + if v.Type.IsFlags() { + v.Fatalf("load flags not implemented: %v", v.LongString()) + return + } + r := v.Reg() + p := s.Prog(loadByType(v.Type, r)) + ssagen.AddrAuto(&p.From, v.Args[0]) + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpStoreReg: + if v.Type.IsFlags() { + v.Fatalf("store flags not implemented: %v", v.LongString()) + return + } + r := v.Args[0].Reg() + p := s.Prog(storeByType(v.Type, r)) + p.From.Type = obj.TYPE_REG + p.From.Reg = r + ssagen.AddrAuto(&p.To, v) + case ssa.OpLOONG64ADDV, + ssa.OpLOONG64SUBV, + ssa.OpLOONG64AND, + ssa.OpLOONG64OR, + ssa.OpLOONG64XOR, + ssa.OpLOONG64NOR, + ssa.OpLOONG64SLLV, + ssa.OpLOONG64SRLV, + ssa.OpLOONG64SRAV, + ssa.OpLOONG64ROTR, + ssa.OpLOONG64ROTRV, + ssa.OpLOONG64ADDF, + ssa.OpLOONG64ADDD, + ssa.OpLOONG64SUBF, + ssa.OpLOONG64SUBD, + ssa.OpLOONG64MULF, + ssa.OpLOONG64MULD, + ssa.OpLOONG64DIVF, + ssa.OpLOONG64DIVD: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64SGT, + ssa.OpLOONG64SGTU: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.Reg = v.Args[1].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64ADDVconst, + ssa.OpLOONG64SUBVconst, + ssa.OpLOONG64ANDconst, + ssa.OpLOONG64ORconst, + ssa.OpLOONG64XORconst, + ssa.OpLOONG64NORconst, + ssa.OpLOONG64SLLVconst, + ssa.OpLOONG64SRLVconst, + ssa.OpLOONG64SRAVconst, + ssa.OpLOONG64ROTRconst, + ssa.OpLOONG64ROTRVconst, + ssa.OpLOONG64SGTconst, + ssa.OpLOONG64SGTUconst: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64MULV: + p := s.Prog(loong64.AMULV) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg1() + p1 := s.Prog(loong64.AMULHV) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = v.Args[1].Reg() + p1.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + case ssa.OpLOONG64MULVU: + p := s.Prog(loong64.AMULV) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg1() + p1 := s.Prog(loong64.AMULHVU) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = v.Args[1].Reg() + p1.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + case ssa.OpLOONG64DIVV: + p := s.Prog(loong64.ADIVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg1() + p1 := s.Prog(loong64.AREMV) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = v.Args[1].Reg() + p1.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + case ssa.OpLOONG64DIVVU: + p := s.Prog(loong64.ADIVVU) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg1() + p1 := s.Prog(loong64.AREMVU) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = v.Args[1].Reg() + p1.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + case ssa.OpLOONG64MOVVconst: + r := v.Reg() + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.To.Type = obj.TYPE_REG + p.To.Reg = r + if isFPreg(r) { + // cannot move into FP or special registers, use TMP as intermediate + p.To.Reg = loong64.REGTMP + p = s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGTMP + p.To.Type = obj.TYPE_REG + p.To.Reg = r + } + case ssa.OpLOONG64MOVFconst, + ssa.OpLOONG64MOVDconst: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_FCONST + p.From.Val = math.Float64frombits(uint64(v.AuxInt)) + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64CMPEQF, + ssa.OpLOONG64CMPEQD, + ssa.OpLOONG64CMPGEF, + ssa.OpLOONG64CMPGED, + ssa.OpLOONG64CMPGTF, + ssa.OpLOONG64CMPGTD: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.Reg = v.Args[1].Reg() + case ssa.OpLOONG64MOVVaddr: + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_ADDR + p.From.Reg = v.Args[0].Reg() + var wantreg string + // MOVV $sym+off(base), R + // the assembler expands it as the following: + // - base is SP: add constant offset to SP (R3) + // when constant is large, tmp register (R30) may be used + // - base is SB: load external address with relocation + switch v.Aux.(type) { + default: + v.Fatalf("aux is of unknown type %T", v.Aux) + case *obj.LSym: + wantreg = "SB" + ssagen.AddAux(&p.From, v) + case *ir.Name: + wantreg = "SP" + ssagen.AddAux(&p.From, v) + case nil: + // No sym, just MOVV $off(SP), R + wantreg = "SP" + p.From.Offset = v.AuxInt + } + if reg := v.Args[0].RegName(); reg != wantreg { + v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) + } + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64MOVBload, + ssa.OpLOONG64MOVBUload, + ssa.OpLOONG64MOVHload, + ssa.OpLOONG64MOVHUload, + ssa.OpLOONG64MOVWload, + ssa.OpLOONG64MOVWUload, + ssa.OpLOONG64MOVVload, + ssa.OpLOONG64MOVFload, + ssa.OpLOONG64MOVDload: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + ssagen.AddAux(&p.From, v) + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64MOVBstore, + ssa.OpLOONG64MOVHstore, + ssa.OpLOONG64MOVWstore, + ssa.OpLOONG64MOVVstore, + ssa.OpLOONG64MOVFstore, + ssa.OpLOONG64MOVDstore: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.To.Type = obj.TYPE_MEM + p.To.Reg = v.Args[0].Reg() + ssagen.AddAux(&p.To, v) + case ssa.OpLOONG64MOVBstorezero, + ssa.OpLOONG64MOVHstorezero, + ssa.OpLOONG64MOVWstorezero, + ssa.OpLOONG64MOVVstorezero: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGZERO + p.To.Type = obj.TYPE_MEM + p.To.Reg = v.Args[0].Reg() + ssagen.AddAux(&p.To, v) + case ssa.OpLOONG64MOVBreg, + ssa.OpLOONG64MOVBUreg, + ssa.OpLOONG64MOVHreg, + ssa.OpLOONG64MOVHUreg, + ssa.OpLOONG64MOVWreg, + ssa.OpLOONG64MOVWUreg: + a := v.Args[0] + for a.Op == ssa.OpCopy || a.Op == ssa.OpLOONG64MOVVreg { + a = a.Args[0] + } + if a.Op == ssa.OpLoadReg && loong64.REG_R0 <= a.Reg() && a.Reg() <= loong64.REG_R31 { + // LoadReg from a narrower type does an extension, except loading + // to a floating point register. So only eliminate the extension + // if it is loaded to an integer register. + + t := a.Type + switch { + case v.Op == ssa.OpLOONG64MOVBreg && t.Size() == 1 && t.IsSigned(), + v.Op == ssa.OpLOONG64MOVBUreg && t.Size() == 1 && !t.IsSigned(), + v.Op == ssa.OpLOONG64MOVHreg && t.Size() == 2 && t.IsSigned(), + v.Op == ssa.OpLOONG64MOVHUreg && t.Size() == 2 && !t.IsSigned(), + v.Op == ssa.OpLOONG64MOVWreg && t.Size() == 4 && t.IsSigned(), + v.Op == ssa.OpLOONG64MOVWUreg && t.Size() == 4 && !t.IsSigned(): + // arg is a proper-typed load, already zero/sign-extended, don't extend again + if v.Reg() == v.Args[0].Reg() { + return + } + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + return + default: + } + } + fallthrough + case ssa.OpLOONG64MOVWF, + ssa.OpLOONG64MOVWD, + ssa.OpLOONG64TRUNCFW, + ssa.OpLOONG64TRUNCDW, + ssa.OpLOONG64MOVVF, + ssa.OpLOONG64MOVVD, + ssa.OpLOONG64TRUNCFV, + ssa.OpLOONG64TRUNCDV, + ssa.OpLOONG64MOVFD, + ssa.OpLOONG64MOVDF, + ssa.OpLOONG64NEGF, + ssa.OpLOONG64NEGD, + ssa.OpLOONG64SQRTD, + ssa.OpLOONG64SQRTF: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64NEGV: + // SUB from REGZERO + p := s.Prog(loong64.ASUBVU) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.Reg = loong64.REGZERO + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64DUFFZERO: + // runtime.duffzero expects start address - 8 in R19 + p := s.Prog(loong64.ASUBVU) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + p.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REG_R19 + p = s.Prog(obj.ADUFFZERO) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ir.Syms.Duffzero + p.To.Offset = v.AuxInt + case ssa.OpLOONG64LoweredZero: + // SUBV $8, R19 + // MOVV R0, 8(R19) + // ADDV $8, R19 + // BNE Rarg1, R19, -2(PC) + // arg1 is the address of the last element to zero + var sz int64 + var mov obj.As + switch { + case v.AuxInt%8 == 0: + sz = 8 + mov = loong64.AMOVV + case v.AuxInt%4 == 0: + sz = 4 + mov = loong64.AMOVW + case v.AuxInt%2 == 0: + sz = 2 + mov = loong64.AMOVH + default: + sz = 1 + mov = loong64.AMOVB + } + p := s.Prog(loong64.ASUBVU) + p.From.Type = obj.TYPE_CONST + p.From.Offset = sz + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REG_R19 + p2 := s.Prog(mov) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = loong64.REGZERO + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = loong64.REG_R19 + p2.To.Offset = sz + p3 := s.Prog(loong64.AADDVU) + p3.From.Type = obj.TYPE_CONST + p3.From.Offset = sz + p3.To.Type = obj.TYPE_REG + p3.To.Reg = loong64.REG_R19 + p4 := s.Prog(loong64.ABNE) + p4.From.Type = obj.TYPE_REG + p4.From.Reg = v.Args[1].Reg() + p4.Reg = loong64.REG_R19 + p4.To.Type = obj.TYPE_BRANCH + p4.To.SetTarget(p2) + case ssa.OpLOONG64DUFFCOPY: + p := s.Prog(obj.ADUFFCOPY) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ir.Syms.Duffcopy + p.To.Offset = v.AuxInt + case ssa.OpLOONG64LoweredMove: + // SUBV $8, R19 + // MOVV 8(R19), Rtmp + // MOVV Rtmp, (R4) + // ADDV $8, R19 + // ADDV $8, R4 + // BNE Rarg2, R19, -4(PC) + // arg2 is the address of the last element of src + var sz int64 + var mov obj.As + switch { + case v.AuxInt%8 == 0: + sz = 8 + mov = loong64.AMOVV + case v.AuxInt%4 == 0: + sz = 4 + mov = loong64.AMOVW + case v.AuxInt%2 == 0: + sz = 2 + mov = loong64.AMOVH + default: + sz = 1 + mov = loong64.AMOVB + } + p := s.Prog(loong64.ASUBVU) + p.From.Type = obj.TYPE_CONST + p.From.Offset = sz + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REG_R19 + p2 := s.Prog(mov) + p2.From.Type = obj.TYPE_MEM + p2.From.Reg = loong64.REG_R19 + p2.From.Offset = sz + p2.To.Type = obj.TYPE_REG + p2.To.Reg = loong64.REGTMP + p3 := s.Prog(mov) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = loong64.REGTMP + p3.To.Type = obj.TYPE_MEM + p3.To.Reg = loong64.REG_R4 + p4 := s.Prog(loong64.AADDVU) + p4.From.Type = obj.TYPE_CONST + p4.From.Offset = sz + p4.To.Type = obj.TYPE_REG + p4.To.Reg = loong64.REG_R19 + p5 := s.Prog(loong64.AADDVU) + p5.From.Type = obj.TYPE_CONST + p5.From.Offset = sz + p5.To.Type = obj.TYPE_REG + p5.To.Reg = loong64.REG_R4 + p6 := s.Prog(loong64.ABNE) + p6.From.Type = obj.TYPE_REG + p6.From.Reg = v.Args[2].Reg() + p6.Reg = loong64.REG_R19 + p6.To.Type = obj.TYPE_BRANCH + p6.To.SetTarget(p2) + case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter: + s.Call(v) + case ssa.OpLOONG64CALLtail: + s.TailCall(v) + case ssa.OpLOONG64LoweredWB: + p := s.Prog(obj.ACALL) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = v.Aux.(*obj.LSym) + case ssa.OpLOONG64LoweredPanicBoundsA, ssa.OpLOONG64LoweredPanicBoundsB, ssa.OpLOONG64LoweredPanicBoundsC: + p := s.Prog(obj.ACALL) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] + s.UseArgs(16) // space used in callee args area by assembly stubs + case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64: + as := loong64.AMOVV + switch v.Op { + case ssa.OpLOONG64LoweredAtomicLoad8: + as = loong64.AMOVB + case ssa.OpLOONG64LoweredAtomicLoad32: + as = loong64.AMOVW + } + s.Prog(loong64.ADBAR) + p := s.Prog(as) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + s.Prog(loong64.ADBAR) + case ssa.OpLOONG64LoweredAtomicStore8, ssa.OpLOONG64LoweredAtomicStore32, ssa.OpLOONG64LoweredAtomicStore64: + as := loong64.AMOVV + switch v.Op { + case ssa.OpLOONG64LoweredAtomicStore8: + as = loong64.AMOVB + case ssa.OpLOONG64LoweredAtomicStore32: + as = loong64.AMOVW + } + s.Prog(loong64.ADBAR) + p := s.Prog(as) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.To.Type = obj.TYPE_MEM + p.To.Reg = v.Args[0].Reg() + s.Prog(loong64.ADBAR) + case ssa.OpLOONG64LoweredAtomicStorezero32, ssa.OpLOONG64LoweredAtomicStorezero64: + as := loong64.AMOVV + if v.Op == ssa.OpLOONG64LoweredAtomicStorezero32 { + as = loong64.AMOVW + } + s.Prog(loong64.ADBAR) + p := s.Prog(as) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGZERO + p.To.Type = obj.TYPE_MEM + p.To.Reg = v.Args[0].Reg() + s.Prog(loong64.ADBAR) + case ssa.OpLOONG64LoweredAtomicExchange32, ssa.OpLOONG64LoweredAtomicExchange64: + // DBAR + // MOVV Rarg1, Rtmp + // LL (Rarg0), Rout + // SC Rtmp, (Rarg0) + // BEQ Rtmp, -3(PC) + // DBAR + ll := loong64.ALLV + sc := loong64.ASCV + if v.Op == ssa.OpLOONG64LoweredAtomicExchange32 { + ll = loong64.ALL + sc = loong64.ASC + } + s.Prog(loong64.ADBAR) + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[1].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REGTMP + p1 := s.Prog(ll) + p1.From.Type = obj.TYPE_MEM + p1.From.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + p2 := s.Prog(sc) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = loong64.REGTMP + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = v.Args[0].Reg() + p3 := s.Prog(loong64.ABEQ) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = loong64.REGTMP + p3.To.Type = obj.TYPE_BRANCH + p3.To.SetTarget(p) + s.Prog(loong64.ADBAR) + case ssa.OpLOONG64LoweredAtomicAdd32, ssa.OpLOONG64LoweredAtomicAdd64: + // DBAR + // LL (Rarg0), Rout + // ADDV Rarg1, Rout, Rtmp + // SC Rtmp, (Rarg0) + // BEQ Rtmp, -3(PC) + // DBAR + // ADDV Rarg1, Rout + ll := loong64.ALLV + sc := loong64.ASCV + if v.Op == ssa.OpLOONG64LoweredAtomicAdd32 { + ll = loong64.ALL + sc = loong64.ASC + } + s.Prog(loong64.ADBAR) + p := s.Prog(ll) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + p1 := s.Prog(loong64.AADDVU) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = v.Args[1].Reg() + p1.Reg = v.Reg0() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = loong64.REGTMP + p2 := s.Prog(sc) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = loong64.REGTMP + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = v.Args[0].Reg() + p3 := s.Prog(loong64.ABEQ) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = loong64.REGTMP + p3.To.Type = obj.TYPE_BRANCH + p3.To.SetTarget(p) + s.Prog(loong64.ADBAR) + p4 := s.Prog(loong64.AADDVU) + p4.From.Type = obj.TYPE_REG + p4.From.Reg = v.Args[1].Reg() + p4.Reg = v.Reg0() + p4.To.Type = obj.TYPE_REG + p4.To.Reg = v.Reg0() + case ssa.OpLOONG64LoweredAtomicAddconst32, ssa.OpLOONG64LoweredAtomicAddconst64: + // DBAR + // LL (Rarg0), Rout + // ADDV $auxint, Rout, Rtmp + // SC Rtmp, (Rarg0) + // BEQ Rtmp, -3(PC) + // DBAR + // ADDV $auxint, Rout + ll := loong64.ALLV + sc := loong64.ASCV + if v.Op == ssa.OpLOONG64LoweredAtomicAddconst32 { + ll = loong64.ALL + sc = loong64.ASC + } + s.Prog(loong64.ADBAR) + p := s.Prog(ll) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + p1 := s.Prog(loong64.AADDVU) + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = v.AuxInt + p1.Reg = v.Reg0() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = loong64.REGTMP + p2 := s.Prog(sc) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = loong64.REGTMP + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = v.Args[0].Reg() + p3 := s.Prog(loong64.ABEQ) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = loong64.REGTMP + p3.To.Type = obj.TYPE_BRANCH + p3.To.SetTarget(p) + s.Prog(loong64.ADBAR) + p4 := s.Prog(loong64.AADDVU) + p4.From.Type = obj.TYPE_CONST + p4.From.Offset = v.AuxInt + p4.Reg = v.Reg0() + p4.To.Type = obj.TYPE_REG + p4.To.Reg = v.Reg0() + case ssa.OpLOONG64LoweredAtomicCas32, ssa.OpLOONG64LoweredAtomicCas64: + // MOVV $0, Rout + // DBAR + // LL (Rarg0), Rtmp + // BNE Rtmp, Rarg1, 4(PC) + // MOVV Rarg2, Rout + // SC Rout, (Rarg0) + // BEQ Rout, -4(PC) + // DBAR + ll := loong64.ALLV + sc := loong64.ASCV + if v.Op == ssa.OpLOONG64LoweredAtomicCas32 { + ll = loong64.ALL + sc = loong64.ASC + } + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGZERO + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + s.Prog(loong64.ADBAR) + p1 := s.Prog(ll) + p1.From.Type = obj.TYPE_MEM + p1.From.Reg = v.Args[0].Reg() + p1.To.Type = obj.TYPE_REG + p1.To.Reg = loong64.REGTMP + p2 := s.Prog(loong64.ABNE) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = v.Args[1].Reg() + p2.Reg = loong64.REGTMP + p2.To.Type = obj.TYPE_BRANCH + p3 := s.Prog(loong64.AMOVV) + p3.From.Type = obj.TYPE_REG + p3.From.Reg = v.Args[2].Reg() + p3.To.Type = obj.TYPE_REG + p3.To.Reg = v.Reg0() + p4 := s.Prog(sc) + p4.From.Type = obj.TYPE_REG + p4.From.Reg = v.Reg0() + p4.To.Type = obj.TYPE_MEM + p4.To.Reg = v.Args[0].Reg() + p5 := s.Prog(loong64.ABEQ) + p5.From.Type = obj.TYPE_REG + p5.From.Reg = v.Reg0() + p5.To.Type = obj.TYPE_BRANCH + p5.To.SetTarget(p1) + p6 := s.Prog(loong64.ADBAR) + p2.To.SetTarget(p6) + case ssa.OpLOONG64LoweredNilCheck: + // Issue a load which will fault if arg is nil. + p := s.Prog(loong64.AMOVB) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + ssagen.AddAux(&p.From, v) + p.To.Type = obj.TYPE_REG + p.To.Reg = loong64.REGTMP + if logopt.Enabled() { + logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name) + } + if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers + base.WarnfAt(v.Pos, "generated nil check") + } + case ssa.OpLOONG64FPFlagTrue, + ssa.OpLOONG64FPFlagFalse: + // MOVV $0, r + // BFPF 2(PC) + // MOVV $1, r + branch := loong64.ABFPF + if v.Op == ssa.OpLOONG64FPFlagFalse { + branch = loong64.ABFPT + } + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGZERO + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + p2 := s.Prog(branch) + p2.To.Type = obj.TYPE_BRANCH + p3 := s.Prog(loong64.AMOVV) + p3.From.Type = obj.TYPE_CONST + p3.From.Offset = 1 + p3.To.Type = obj.TYPE_REG + p3.To.Reg = v.Reg() + p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land + p2.To.SetTarget(p4) + case ssa.OpLOONG64LoweredGetClosurePtr: + // Closure pointer is R22 (loong64.REGCTXT). + ssagen.CheckLoweredGetClosurePtr(v) + case ssa.OpLOONG64LoweredGetCallerSP: + // caller's SP is FixedFrameSize below the address of the first arg + p := s.Prog(loong64.AMOVV) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize + p.From.Name = obj.NAME_PARAM + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpLOONG64LoweredGetCallerPC: + p := s.Prog(obj.AGETCALLERPC) + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + case ssa.OpClobber, ssa.OpClobberReg: + // TODO: implement for clobberdead experiment. Nop is ok for now. + default: + v.Fatalf("genValue not implemented: %s", v.LongString()) + } +} + +var blockJump = map[ssa.BlockKind]struct { + asm, invasm obj.As +}{ + ssa.BlockLOONG64EQ: {loong64.ABEQ, loong64.ABNE}, + ssa.BlockLOONG64NE: {loong64.ABNE, loong64.ABEQ}, + ssa.BlockLOONG64LTZ: {loong64.ABLTZ, loong64.ABGEZ}, + ssa.BlockLOONG64GEZ: {loong64.ABGEZ, loong64.ABLTZ}, + ssa.BlockLOONG64LEZ: {loong64.ABLEZ, loong64.ABGTZ}, + ssa.BlockLOONG64GTZ: {loong64.ABGTZ, loong64.ABLEZ}, + ssa.BlockLOONG64FPT: {loong64.ABFPT, loong64.ABFPF}, + ssa.BlockLOONG64FPF: {loong64.ABFPF, loong64.ABFPT}, +} + +func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { + switch b.Kind { + case ssa.BlockPlain: + if b.Succs[0].Block() != next { + p := s.Prog(obj.AJMP) + p.To.Type = obj.TYPE_BRANCH + s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) + } + case ssa.BlockDefer: + // defer returns in R19: + // 0 if we should continue executing + // 1 if we should jump to deferreturn call + p := s.Prog(loong64.ABNE) + p.From.Type = obj.TYPE_REG + p.From.Reg = loong64.REGZERO + p.Reg = loong64.REG_R19 + p.To.Type = obj.TYPE_BRANCH + s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()}) + if b.Succs[0].Block() != next { + p := s.Prog(obj.AJMP) + p.To.Type = obj.TYPE_BRANCH + s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) + } + case ssa.BlockExit, ssa.BlockRetJmp: + case ssa.BlockRet: + s.Prog(obj.ARET) + case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE, + ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ, + ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ, + ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF: + jmp := blockJump[b.Kind] + var p *obj.Prog + switch next { + case b.Succs[0].Block(): + p = s.Br(jmp.invasm, b.Succs[1].Block()) + case b.Succs[1].Block(): + p = s.Br(jmp.asm, b.Succs[0].Block()) + default: + if b.Likely != ssa.BranchUnlikely { + p = s.Br(jmp.asm, b.Succs[0].Block()) + s.Br(obj.AJMP, b.Succs[1].Block()) + } else { + p = s.Br(jmp.invasm, b.Succs[1].Block()) + s.Br(obj.AJMP, b.Succs[0].Block()) + } + } + if !b.Controls[0].Type.IsFlags() { + p.From.Type = obj.TYPE_REG + p.From.Reg = b.Controls[0].Reg() + } + default: + b.Fatalf("branch not implemented: %s", b.LongString()) + } +} diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go index f892923ba038f3..4e6897042ec04c 100644 --- a/src/cmd/compile/internal/mips/galign.go +++ b/src/cmd/compile/internal/mips/galign.go @@ -21,7 +21,6 @@ func Init(arch *ssagen.ArchInfo) { arch.SoftFloat = (buildcfg.GOMIPS == "softfloat") arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go index 1a5125207dd0ea..a18440e7b3c405 100644 --- a/src/cmd/compile/internal/mips/ggen.go +++ b/src/cmd/compile/internal/mips/ggen.go @@ -20,7 +20,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } if cnt < int64(4*types.PtrSize) { for i := int64(0); i < cnt; i += int64(types.PtrSize) { - p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.FixedFrameSize()+off+i) + p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.Arch.FixedFrameSize+off+i) } } else { //fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi) @@ -30,7 +30,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog // MOVW R0, (Widthptr)r1 // ADD $Widthptr, r1 // BNE r1, r2, loop - p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-4, obj.TYPE_REG, mips.REGRT1, 0) + p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-4, obj.TYPE_REG, mips.REGRT1, 0) p.Reg = mips.REGSP p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0) p.Reg = mips.REGRT1 diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go index e0447f38cbf233..0411756c8d71c6 100644 --- a/src/cmd/compile/internal/mips/ssa.go +++ b/src/cmd/compile/internal/mips/ssa.go @@ -475,6 +475,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p6.To.SetTarget(p2) case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter: s.Call(v) + case ssa.OpMIPSCALLtail: + s.TailCall(v) case ssa.OpMIPSLoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -790,7 +792,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(mips.AMOVW) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -841,14 +843,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockMIPSEQ, ssa.BlockMIPSNE, ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ, ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ, diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go index af81366e51bdf5..412bc71aab270d 100644 --- a/src/cmd/compile/internal/mips64/galign.go +++ b/src/cmd/compile/internal/mips64/galign.go @@ -21,7 +21,6 @@ func Init(arch *ssagen.ArchInfo) { arch.SoftFloat = buildcfg.GOMIPS64 == "softfloat" arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} arch.SSAGenValue = ssaGenValue diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go index e821a00876fa79..f3e372c3bcb8f4 100644 --- a/src/cmd/compile/internal/mips64/ssa.go +++ b/src/cmd/compile/internal/mips64/ssa.go @@ -320,7 +320,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg { a = a.Args[0] } - if a.Op == ssa.OpLoadReg { + if a.Op == ssa.OpLoadReg && mips.REG_R0 <= a.Reg() && a.Reg() <= mips.REG_R31 { + // LoadReg from a narrower type does an extension, except loading + // to a floating point register. So only eliminate the extension + // if it is loaded to an integer register. t := a.Type switch { case v.Op == ssa.OpMIPS64MOVBreg && t.Size() == 1 && t.IsSigned(), @@ -491,6 +494,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p6.To.SetTarget(p2) case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter: s.Call(v) + case ssa.OpMIPS64CALLtail: + s.TailCall(v) case ssa.OpMIPS64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -757,7 +762,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -808,14 +813,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE, ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ, ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ, diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go new file mode 100644 index 00000000000000..c1ee8d15c5d15b --- /dev/null +++ b/src/cmd/compile/internal/noder/codes.go @@ -0,0 +1,87 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import "internal/pkgbits" + +// A codeStmt distinguishes among statement encodings. +type codeStmt int + +func (c codeStmt) Marker() pkgbits.SyncMarker { return pkgbits.SyncStmt1 } +func (c codeStmt) Value() int { return int(c) } + +const ( + stmtEnd codeStmt = iota + stmtLabel + stmtBlock + stmtExpr + stmtSend + stmtAssign + stmtAssignOp + stmtIncDec + stmtBranch + stmtCall + stmtReturn + stmtIf + stmtFor + stmtSwitch + stmtSelect +) + +// A codeExpr distinguishes among expression encodings. +type codeExpr int + +func (c codeExpr) Marker() pkgbits.SyncMarker { return pkgbits.SyncExpr } +func (c codeExpr) Value() int { return int(c) } + +// TODO(mdempsky): Split expr into addr, for lvalues. +const ( + exprConst codeExpr = iota + exprLocal // local variable + exprGlobal // global variable or function + exprCompLit + exprFuncLit + exprFieldVal + exprMethodVal + exprMethodExpr + exprIndex + exprSlice + exprAssert + exprUnaryOp + exprBinaryOp + exprCall + exprConvert + exprNew + exprMake + exprNil + exprFuncInst + exprRecv + exprReshape +) + +type codeAssign int + +func (c codeAssign) Marker() pkgbits.SyncMarker { return pkgbits.SyncAssign } +func (c codeAssign) Value() int { return int(c) } + +const ( + assignBlank codeAssign = iota + assignDef + assignExpr +) + +// A codeDecl distinguishes among declaration encodings. +type codeDecl int + +func (c codeDecl) Marker() pkgbits.SyncMarker { return pkgbits.SyncDecl } +func (c codeDecl) Value() int { return int(c) } + +const ( + declEnd codeDecl = iota + declFunc + declMethod + declVar + declOther +) diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go index 4ca2eb4740a491..07353cc17eaaf2 100644 --- a/src/cmd/compile/internal/noder/decl.go +++ b/src/cmd/compile/internal/noder/decl.go @@ -18,43 +18,48 @@ import ( // TODO(mdempsky): Skip blank declarations? Probably only safe // for declarations without pragmas. -func (g *irgen) decls(decls []syntax.Decl) []ir.Node { - var res ir.Nodes +func (g *irgen) decls(res *ir.Nodes, decls []syntax.Decl) { for _, decl := range decls { switch decl := decl.(type) { case *syntax.ConstDecl: - g.constDecl(&res, decl) + g.constDecl(res, decl) case *syntax.FuncDecl: - g.funcDecl(&res, decl) + g.funcDecl(res, decl) case *syntax.TypeDecl: if ir.CurFunc == nil { continue // already handled in irgen.generate } - g.typeDecl(&res, decl) + g.typeDecl(res, decl) case *syntax.VarDecl: - g.varDecl(&res, decl) + g.varDecl(res, decl) default: g.unhandled("declaration", decl) } } - return res } func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) { - // TODO(mdempsky): Merge with gcimports so we don't have to import - // packages twice. - g.pragmaFlags(decl.Pragma, 0) - ipkg := importfile(decl) - if ipkg == ir.Pkgs.Unsafe { + // Get the imported package's path, as resolved already by types2 + // and gcimporter. This is the same path as would be computed by + // parseImportPath. + switch pkgNameOf(g.info, decl).Imported().Path() { + case "unsafe": p.importedUnsafe = true - } - if ipkg.Path == "embed" { + case "embed": p.importedEmbed = true } } +// pkgNameOf returns the PkgName associated with the given ImportDecl. +func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName { + if name := decl.LocalPkgName; name != nil { + return info.Defs[name].(*types2.PkgName) + } + return info.Implicits[decl].(*types2.PkgName) +} + func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) { g.pragmaFlags(decl.Pragma, 0) @@ -81,6 +86,18 @@ func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) { } func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { + assert(g.curDecl == "") + // Set g.curDecl to the function name, as context for the type params declared + // during types2-to-types1 translation if this is a generic function. + g.curDecl = decl.Name.Value + obj2 := g.info.Defs[decl.Name] + recv := types2.AsSignature(obj2.Type()).Recv() + if recv != nil { + t2 := deref2(recv.Type()) + // This is a method, so set g.curDecl to recvTypeName.methName instead. + g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl + } + fn := ir.NewFunc(g.pos(decl)) fn.Nname, _ = g.def(decl.Name) fn.Nname.Func = fn @@ -90,29 +107,99 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) { if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 { base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined") } + if fn.Pragma&ir.Nointerface != 0 { + // Propagate //go:nointerface from Func.Pragma to Field.Nointerface. + // This is a bit roundabout, but this is the earliest point where we've + // processed the function's pragma flags, and we've also already created + // the Fields to represent the receiver's method set. + if recv := fn.Type().Recv(); recv != nil { + typ := types.ReceiverBaseType(recv.Type) + if orig := typ.OrigType(); orig != nil { + // For a generic method, we mark the methods on the + // base generic type, since those are the methods + // that will be stenciled. + typ = orig + } + meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0) + meth.SetNointerface(true) + } + } + + if decl.Body != nil { + if fn.Pragma&ir.Noescape != 0 { + base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations") + } + if (fn.Pragma&ir.UintptrKeepAlive != 0 && fn.Pragma&ir.UintptrEscapes == 0) && fn.Pragma&ir.Nosplit == 0 { + // Stack growth can't handle uintptr arguments that may + // be pointers (as we don't know which are pointers + // when creating the stack map). Thus uintptrkeepalive + // functions (and all transitive callees) must be + // nosplit. + // + // N.B. uintptrescapes implies uintptrkeepalive but it + // is OK since the arguments must escape to the heap. + // + // TODO(prattmic): Add recursive nosplit check of callees. + // TODO(prattmic): Functions with no body (i.e., + // assembly) must also be nosplit, but we can't check + // that here. + base.ErrorfAt(fn.Pos(), "go:uintptrkeepalive requires go:nosplit") + } + } if decl.Name.Value == "init" && decl.Recv == nil { g.target.Inits = append(g.target.Inits, fn) } - g.funcBody(fn, decl.Recv, decl.Type, decl.Body) + saveHaveEmbed := g.haveEmbed + saveCurDecl := g.curDecl + g.curDecl = "" + g.later(func() { + defer func(b bool, s string) { + // Revert haveEmbed and curDecl back to what they were before + // the "later" function. + g.haveEmbed = b + g.curDecl = s + }(g.haveEmbed, g.curDecl) + + // Set haveEmbed and curDecl to what they were for this funcDecl. + g.haveEmbed = saveHaveEmbed + g.curDecl = saveCurDecl + if fn.Type().HasTParam() { + g.topFuncIsGeneric = true + } + g.funcBody(fn, decl.Recv, decl.Type, decl.Body) + g.topFuncIsGeneric = false + if fn.Type().HasTParam() && fn.Body != nil { + // Set pointers to the dcls/body of a generic function/method in + // the Inl struct, so it is marked for export, is available for + // stenciling, and works with Inline_Flood(). + fn.Inl = &ir.Inline{ + Cost: 1, + Dcl: fn.Dcl, + Body: fn.Body, + } + } - out.Append(fn) + out.Append(fn) + }) } func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { + // Set the position for any error messages we might print (e.g. too large types). + base.Pos = g.pos(decl) + assert(ir.CurFunc != nil || g.curDecl == "") + // Set g.curDecl to the type name, as context for the type params declared + // during types2-to-types1 translation if this is a generic type. + saveCurDecl := g.curDecl + g.curDecl = decl.Name.Value if decl.Alias { name, _ := g.def(decl.Name) g.pragmaFlags(decl.Pragma, 0) - - // TODO(mdempsky): This matches how typecheckdef marks aliases for - // export, but this won't generalize to exporting function-scoped - // type aliases. We should maybe just use n.Alias() instead. - if ir.CurFunc == nil { - name.Sym().Def = ir.TypeNode(name.Type()) - } + assert(name.Alias()) // should be set by irgen.obj out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name)) + g.curDecl = "" return } @@ -122,53 +209,37 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { name, obj := g.def(decl.Name) ntyp, otyp := name.Type(), obj.Type() if ir.CurFunc != nil { - typecheck.TypeGen++ - ntyp.Vargen = typecheck.TypeGen + ntyp.SetVargen() } - pragmas := g.pragmaFlags(decl.Pragma, typePragmas) + pragmas := g.pragmaFlags(decl.Pragma, 0) name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed? - if pragmas&ir.NotInHeap != 0 { - ntyp.SetNotInHeap(true) - } - - // We need to use g.typeExpr(decl.Type) here to ensure that for - // chained, defined-type declarations like: - // - // type T U - // - // //go:notinheap - // type U struct { … } - // - // we mark both T and U as NotInHeap. If we instead used just - // g.typ(otyp.Underlying()), then we'd instead set T's underlying - // type directly to the struct type (which is not marked NotInHeap) - // and fail to mark T as NotInHeap. - // - // Also, we rely here on Type.SetUnderlying allowing passing a - // defined type and handling forward references like from T to U - // above. Contrast with go/types's Named.SetUnderlying, which - // disallows this. - // - // [mdempsky: Subtleties like these are why I always vehemently - // object to new type pragmas.] ntyp.SetUnderlying(g.typeExpr(decl.Type)) - if len(decl.TParamList) > 0 { - // Set HasTParam if there are any tparams, even if no tparams are - // used in the type itself (e.g., if it is an empty struct, or no - // fields in the struct use the tparam). - ntyp.SetHasTParam(true) + + tparams := otyp.(*types2.Named).TypeParams() + if n := tparams.Len(); n > 0 { + rparams := make([]*types.Type, n) + for i := range rparams { + rparams[i] = g.typ(tparams.At(i)) + } + // This will set hasTParam flag if any rparams are not concrete types. + ntyp.SetRParams(rparams) } types.ResumeCheckSize() + g.curDecl = saveCurDecl if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 { methods := make([]*types.Field, otyp.NumMethods()) for i := range methods { m := otyp.Method(i) + // Set g.curDecl to recvTypeName.methName, as context for the + // method-specific type params in the receiver. + g.curDecl = decl.Name.Value + "." + m.Name() meth := g.obj(m) methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) methods[i].Nname = meth + g.curDecl = "" } ntyp.Methods().Set(methods) } @@ -178,57 +249,78 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) { pos := g.pos(decl) + // Set the position for any error messages we might print (e.g. too large types). + base.Pos = pos names := make([]*ir.Name, len(decl.NameList)) for i, name := range decl.NameList { names[i], _ = g.def(name) } - values := g.exprList(decl.Values) if decl.Pragma != nil { pragma := decl.Pragma.(*pragmas) - // TODO(mdempsky): Plumb noder.importedEmbed through to here. - varEmbed(g.makeXPos, names[0], decl, pragma, true) + varEmbed(g.makeXPos, names[0], decl, pragma, g.haveEmbed) g.reportUnused(pragma) } - var as2 *ir.AssignListStmt - if len(values) != 0 && len(names) != len(values) { - as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values) - } + haveEmbed := g.haveEmbed + do := func() { + defer func(b bool) { g.haveEmbed = b }(g.haveEmbed) - for i, name := range names { - if ir.CurFunc != nil { - out.Append(ir.NewDecl(pos, ir.ODCL, name)) + g.haveEmbed = haveEmbed + values := g.exprList(decl.Values) + + var as2 *ir.AssignListStmt + if len(values) != 0 && len(names) != len(values) { + as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values) } - if as2 != nil { - as2.Lhs[i] = name - name.Defn = as2 - } else { - as := ir.NewAssignStmt(pos, name, nil) - if len(values) != 0 { - as.Y = values[i] - name.Defn = as - } else if ir.CurFunc == nil { - name.Defn = as + + for i, name := range names { + if ir.CurFunc != nil { + out.Append(ir.NewDecl(pos, ir.ODCL, name)) } - lhs := []ir.Node{as.X} - rhs := []ir.Node{} - if as.Y != nil { - rhs = []ir.Node{as.Y} + if as2 != nil { + as2.Lhs[i] = name + name.Defn = as2 + } else { + as := ir.NewAssignStmt(pos, name, nil) + if len(values) != 0 { + as.Y = values[i] + name.Defn = as + } else if ir.CurFunc == nil { + name.Defn = as + } + if !g.delayTransform() { + lhs := []ir.Node{as.X} + rhs := []ir.Node{} + if as.Y != nil { + rhs = []ir.Node{as.Y} + } + transformAssign(as, lhs, rhs) + as.X = lhs[0] + if as.Y != nil { + as.Y = rhs[0] + } + } + as.SetTypecheck(1) + out.Append(as) } - transformAssign(as, lhs, rhs) - as.X = lhs[0] - if as.Y != nil { - as.Y = rhs[0] + } + if as2 != nil { + if !g.delayTransform() { + transformAssign(as2, as2.Lhs, as2.Rhs) } - as.SetTypecheck(1) - out.Append(as) + as2.SetTypecheck(1) + out.Append(as2) } } - if as2 != nil { - transformAssign(as2, as2.Lhs, as2.Rhs) - as2.SetTypecheck(1) - out.Append(as2) + + // If we're within a function, we need to process the assignment + // part of the variable declaration right away. Otherwise, we leave + // it to be handled after all top-level declarations are processed. + if ir.CurFunc != nil { + do() + } else { + g.later(do) } } diff --git a/src/cmd/compile/internal/noder/export.go b/src/cmd/compile/internal/noder/export.go new file mode 100644 index 00000000000000..263cdc262bb88d --- /dev/null +++ b/src/cmd/compile/internal/noder/export.go @@ -0,0 +1,35 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "bytes" + "fmt" + "io" + + "cmd/compile/internal/base" + "cmd/compile/internal/typecheck" + "cmd/internal/bio" +) + +func WriteExports(out *bio.Writer) { + var data bytes.Buffer + + if base.Debug.Unified != 0 { + data.WriteByte('u') + writeUnifiedExport(&data) + } else { + typecheck.WriteExports(&data, true) + } + + // The linker also looks for the $$ marker - use char after $$ to distinguish format. + out.WriteString("\n$$B\n") // indicate binary export format + io.Copy(out, &data) + out.WriteString("\n$$\n") + + if base.Debug.Export != 0 { + fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, data.Len()) + } +} diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index c7695ed9204352..54b07c39f49405 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -5,6 +5,8 @@ package noder import ( + "fmt" + "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" @@ -15,6 +17,8 @@ import ( ) func (g *irgen) expr(expr syntax.Expr) ir.Node { + expr = unparen(expr) // skip parens; unneeded after parse+typecheck + if expr == nil { return nil } @@ -46,35 +50,25 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node { base.FatalfAt(g.pos(expr), "unrecognized type-checker result") } - // The gc backend expects all expressions to have a concrete type, and - // types2 mostly satisfies this expectation already. But there are a few - // cases where the Go spec doesn't require converting to concrete type, - // and so types2 leaves them untyped. So we need to fix those up here. - typ := tv.Type - if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { - switch basic.Kind() { - case types2.UntypedNil: - // ok; can appear in type switch case clauses - // TODO(mdempsky): Handle as part of type switches instead? - case types2.UntypedBool: - typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition - case types2.UntypedString: - typ = types2.Typ[types2.String] // argument to "append" or "copy" calls - default: - base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic) - } + base.Assert(g.exprStmtOK) + + typ := idealType(tv) + if typ == nil { + base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", tv.Type) } // Constant expression. if tv.Value != nil { - return Const(g.pos(expr), g.typ(typ), tv.Value) + typ := g.typ(typ) + value := FixValue(typ, tv.Value) + return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr)) } n := g.expr0(typ, expr) if n.Typecheck() != 1 && n.Typecheck() != 3 { base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n) } - if !g.match(n.Type(), typ, tv.HasOk()) { + if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) { base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ) } return n @@ -82,6 +76,11 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node { func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { pos := g.pos(expr) + assert(pos.IsKnown()) + + // Set base.Pos for transformation code that still uses base.Pos, rather than + // the pos of the node being converted. + base.Pos = pos switch expr := expr.(type) { case *syntax.Name: @@ -101,68 +100,27 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { case *syntax.CallExpr: fun := g.expr(expr.Fun) - - // The key for the Inferred map is the CallExpr (if inferring - // types required the function arguments) or the IndexExpr below - // (if types could be inferred without the function arguments). - if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 { - // This is the case where inferring types required the - // types of the function arguments. - targs := make([]ir.Node, len(inferred.Targs)) - for i, targ := range inferred.Targs { - targs[i] = ir.TypeNode(g.typ(targ)) - } - if fun.Op() == ir.OFUNCINST { - // Replace explicit type args with the full list that - // includes the additional inferred type args - fun.(*ir.InstExpr).Targs = targs - } else { - // Create a function instantiation here, given - // there are only inferred type args (e.g. - // min(5,6), where min is a generic function) - inst := ir.NewInstExpr(pos, ir.OFUNCINST, fun, targs) - typed(fun.Type(), inst) - fun = inst - } - - } - return Call(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots) + return g.callExpr(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots) case *syntax.IndexExpr: - var targs []ir.Node - - if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 { - // This is the partial type inference case where the types - // can be inferred from other type arguments without using - // the types of the function arguments. - targs = make([]ir.Node, len(inferred.Targs)) - for i, targ := range inferred.Targs { - targs[i] = ir.TypeNode(g.typ(targ)) - } - } else if _, ok := expr.Index.(*syntax.ListExpr); ok { - targs = g.exprList(expr.Index) - } else { - index := g.expr(expr.Index) - if index.Op() != ir.OTYPE { + args := unpackListExpr(expr.Index) + if len(args) == 1 { + tv, ok := g.info.Types[args[0]] + assert(ok) + if tv.IsValue() { // This is just a normal index expression - return Index(pos, g.typ(typ), g.expr(expr.X), index) + n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0])) + if !g.delayTransform() { + // transformIndex will modify n.Type() for OINDEXMAP. + transformIndex(n) + } + return n } - // This is generic function instantiation with a single type - targs = []ir.Node{index} - } - // This is a generic function instantiation (e.g. min[int]). - // Generic type instantiation is handled in the type - // section of expr() above (using g.typ). - x := g.expr(expr.X) - if x.Op() != ir.ONAME || x.Type().Kind() != types.TFUNC { - panic("Incorrect argument for generic func instantiation") } - n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs) - typed(g.typ(typ), n) - return n - case *syntax.ParenExpr: - return g.expr(expr.X) // skip parens; unneeded after parse+typecheck + // expr.Index is a list of type args, so we ignore it, since types2 has + // already provided this info with the Info.Instances map. + return g.expr(expr.X) case *syntax.SelectorExpr: // Qualified identifier. @@ -174,17 +132,37 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { return g.selectorExpr(pos, typ, expr) case *syntax.SliceExpr: - return Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2])) + n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2])) + if !g.delayTransform() { + transformSlice(n) + } + return n case *syntax.Operation: if expr.Y == nil { - return Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X)) + n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X)) + if n.Op() == ir.OADDR && !g.delayTransform() { + transformAddr(n.(*ir.AddrExpr)) + } + return n } switch op := g.op(expr.Op, binOps[:]); op { case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: - return Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y)) + n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y)) + if !g.delayTransform() { + transformCompare(n) + } + return n + case ir.OANDAND, ir.OOROR: + x := g.expr(expr.X) + y := g.expr(expr.Y) + return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y)) default: - return Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y)) + n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y)) + if op == ir.OADD && !g.delayTransform() { + return transformAdd(n) + } + return n } default: @@ -193,7 +171,75 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { } } -// selectorExpr resolves the choice of ODOT, ODOTPTR, OCALLPART (eventually +// substType does a normal type substition, but tparams is in the form of a field +// list, and targs is in terms of a slice of type nodes. substType records any newly +// instantiated types into g.instTypeList. +func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Ntype) *types.Type { + fields := tparams.FieldSlice() + tparams1 := make([]*types.Type, len(fields)) + for i, f := range fields { + tparams1[i] = f.Type + } + targs1 := make([]*types.Type, len(targs)) + for i, n := range targs { + targs1[i] = n.Type() + } + ts := typecheck.Tsubster{ + Tparams: tparams1, + Targs: targs1, + } + newt := ts.Typ(typ) + return newt +} + +// callExpr creates a call expression (which might be a type conversion, built-in +// call, or a regular call) and does standard transforms, unless we are in a generic +// function. +func (g *irgen) callExpr(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node { + n := ir.NewCallExpr(pos, ir.OCALL, fun, args) + n.IsDDD = dots + typed(typ, n) + + if fun.Op() == ir.OTYPE { + // Actually a type conversion, not a function call. + if !g.delayTransform() { + return transformConvCall(n) + } + return n + } + + if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 { + if !g.delayTransform() { + return transformBuiltin(n) + } + return n + } + + // Add information, now that we know that fun is actually being called. + switch fun := fun.(type) { + case *ir.SelectorExpr: + if fun.Op() == ir.OMETHVALUE { + op := ir.ODOTMETH + if fun.X.Type().IsInterface() { + op = ir.ODOTINTER + } + fun.SetOp(op) + // Set the type to include the receiver, since that's what + // later parts of the compiler expect + fun.SetType(fun.Selection.Type) + } + } + + // A function instantiation (even if fully concrete) shouldn't be + // transformed yet, because we need to add the dictionary during the + // transformation. + if fun.Op() != ir.OFUNCINST && !g.delayTransform() { + transformCall(n) + } + return n +} + +// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually // ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather // than in typecheck.go. func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node { @@ -222,12 +268,6 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto return DotField(pos, x, last) } - // TODO(danscales,mdempsky): Interface method sets are not sorted the - // same between types and types2. In particular, using "last" here - // without conversion will likely fail if an interface contains - // unexported methods from two different packages (due to cross-package - // interface embedding). - var n ir.Node method2 := selinfo.Obj().(*types2.Func) @@ -259,24 +299,26 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto if wantPtr { recvType2Base = types2.AsPointer(recvType2).Elem() } - if len(types2.AsNamed(recvType2Base).TParams()) > 0 { + if recvType2Base.(*types2.Named).TypeParams().Len() > 0 { // recvType2 is the original generic type that is // instantiated for this method call. // selinfo.Recv() is the instantiated type recvType2 = recvType2Base - // method is the generic method associated with the gen type - method := g.obj(types2.AsNamed(recvType2).Method(last)) - n = ir.NewSelectorExpr(pos, ir.OCALLPART, x, method.Sym()) + recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name()) + recvType := recvTypeSym.Def.(*ir.Name).Type() + // method is the generic method associated with + // the base generic type. The instantiated type may not + // have method bodies filled in, if it was imported. + method := recvType.Methods().Index(last).Nname.(*ir.Name) + n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value)) n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type()) n.(*ir.SelectorExpr).Selection.Nname = method typed(method.Type(), n) - // selinfo.Targs() are the types used to - // instantiate the type of receiver - targs2 := getTargs(selinfo) - targs := make([]ir.Node, len(targs2)) - for i, targ2 := range targs2 { - targs[i] = ir.TypeNode(g.typ(targ2)) + xt := deref(x.Type()) + targs := make([]ir.Ntype, len(xt.RParams())) + for i := range targs { + targs[i] = ir.TypeNode(xt.RParams()[i]) } // Create function instantiation with the type @@ -299,27 +341,18 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto return n } -// getTargs gets the targs associated with the receiver of a selected method -func getTargs(selinfo *types2.Selection) []types2.Type { - r := selinfo.Recv() - if p := types2.AsPointer(r); p != nil { - r = p.Elem() - } - n := types2.AsNamed(r) - if n == nil { - base.Fatalf("Incorrect type for selinfo %v", selinfo) - } - return n.TArgs() +func (g *irgen) exprList(expr syntax.Expr) []ir.Node { + return g.exprs(unpackListExpr(expr)) } -func (g *irgen) exprList(expr syntax.Expr) []ir.Node { +func unpackListExpr(expr syntax.Expr) []syntax.Expr { switch expr := expr.(type) { case nil: return nil case *syntax.ListExpr: - return g.exprs(expr.ElemList) + return expr.ElemList default: - return []ir.Node{g.expr(expr)} + return []syntax.Expr{expr} } } @@ -332,47 +365,56 @@ func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node { } func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node { - if ptr, ok := typ.Underlying().(*types2.Pointer); ok { + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit)) n.SetOp(ir.OPTRLIT) return typed(g.typ(typ), n) } - _, isStruct := typ.Underlying().(*types2.Struct) + _, isStruct := types2.CoreType(typ).(*types2.Struct) exprs := make([]ir.Node, len(lit.ElemList)) for i, elem := range lit.ElemList { switch elem := elem.(type) { case *syntax.KeyValueExpr: + var key ir.Node if isStruct { - exprs[i] = ir.NewStructKeyExpr(g.pos(elem), g.name(elem.Key.(*syntax.Name)), g.expr(elem.Value)) + key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name))) } else { - exprs[i] = ir.NewKeyExpr(g.pos(elem), g.expr(elem.Key), g.expr(elem.Value)) + key = g.expr(elem.Key) + } + value := wrapname(g.pos(elem.Value), g.expr(elem.Value)) + if value.Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(value.(*ir.ParenExpr).X.Type(), value) } + exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value) default: - exprs[i] = g.expr(elem) + exprs[i] = wrapname(g.pos(elem), g.expr(elem)) + if exprs[i].Op() == ir.OPAREN { + // Make sure any PAREN node added by wrapper has a type + typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i]) + } } } n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs) typed(g.typ(typ), n) - return transformCompLit(n) + var r ir.Node = n + if !g.delayTransform() { + r = transformCompLit(n) + } + return r } func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node { - fn := ir.NewFunc(g.pos(expr)) - fn.SetIsHiddenClosure(ir.CurFunc != nil) + fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil) + ir.NameClosure(fn.OClosure, ir.CurFunc) - fn.Nname = ir.NewNameAt(g.pos(expr), typecheck.ClosureName(ir.CurFunc)) - ir.MarkFunc(fn.Nname) typ := g.typ(typ2) - fn.Nname.Func = fn - fn.Nname.Defn = fn typed(typ, fn.Nname) - fn.SetTypecheck(1) - - fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn) typed(typ, fn.OClosure) + fn.SetTypecheck(1) g.funcBody(fn, nil, expr.Type, expr.Body) @@ -383,12 +425,16 @@ func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node { for _, cv := range fn.ClosureVars { cv.SetType(cv.Canonical().Type()) cv.SetTypecheck(1) - cv.SetWalkdef(1) } - g.target.Decls = append(g.target.Decls, fn) - - return fn.OClosure + if g.topFuncIsGeneric { + // Don't add any closure inside a generic function/method to the + // g.target.Decls list, even though it may not be generic itself. + // See issue #47514. + return ir.UseClosure(fn.OClosure, nil) + } else { + return ir.UseClosure(fn.OClosure, g.target) + } } func (g *irgen) typeExpr(typ syntax.Expr) *types.Type { @@ -398,3 +444,35 @@ func (g *irgen) typeExpr(typ syntax.Expr) *types.Type { } return n.Type() } + +// constExprOp returns an ir.Op that represents the outermost +// operation of the given constant expression. It's intended for use +// with ir.RawOrigExpr. +func constExprOp(expr syntax.Expr) ir.Op { + switch expr := expr.(type) { + default: + panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr)) + + case *syntax.BasicLit: + return ir.OLITERAL + case *syntax.Name, *syntax.SelectorExpr: + return ir.ONAME + case *syntax.CallExpr: + return ir.OCALL + case *syntax.Operation: + if expr.Y == nil { + return unOps[expr.Op] + } + return binOps[expr.Op] + } +} + +func unparen(expr syntax.Expr) syntax.Expr { + for { + paren, ok := expr.(*syntax.ParenExpr) + if !ok { + return expr + } + expr = paren.X + } +} diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go index 702138157c53b8..6077b348a5c73e 100644 --- a/src/cmd/compile/internal/noder/func.go +++ b/src/cmd/compile/internal/noder/func.go @@ -37,8 +37,7 @@ func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, // calculated its size, including parameter offsets. Now that we've // created the parameter Names, force a recalculation to ensure // their offsets are correct. - typ.Align = 0 - types.CalcSize(typ) + types.RecalcSize(typ) if block != nil { typecheck.DeclContext = ir.PAUTO diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index 9da0e493007a06..764dcb3f85bb76 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -11,6 +11,7 @@ import ( "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/compile/internal/types2" "cmd/internal/src" ) @@ -39,8 +40,30 @@ func typed(typ *types.Type, n ir.Node) ir.Node { // Values -func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node { - return typed(typ, ir.NewBasicLit(pos, val)) +func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node { + orig := ir.NewRawOrigExpr(pos, op, raw) + return ir.NewConstExpr(val, typed(typ, orig)) +} + +// FixValue returns val after converting and truncating it as +// appropriate for typ. +func FixValue(typ *types.Type, val constant.Value) constant.Value { + assert(typ.Kind() != types.TFORW) + switch { + case typ.IsInteger(): + val = constant.ToInt(val) + case typ.IsFloat(): + val = constant.ToFloat(val) + case typ.IsComplex(): + val = constant.ToComplex(val) + } + if !typ.IsUntyped() { + val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val() + } + if !typ.IsTypeParam() { + ir.AssertValidTypeForConst(typ, val) + } + return val } func Nil(pos src.XPos, typ *types.Type) ir.Node { @@ -51,10 +74,6 @@ func Nil(pos src.XPos, typ *types.Type) ir.Node { func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr { n := typecheck.NodAddrAt(pos, x) - switch x.Op() { - case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: - n.SetOp(ir.OPTRLIT) - } typed(types.NewPtr(x.Type()), n) return n } @@ -63,138 +82,22 @@ func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node { return typed(typ, ir.NewTypeAssertExpr(pos, x, nil)) } -func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node { +func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExpr { switch op { - case ir.OANDAND, ir.OOROR: - return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y)) case ir.OADD: n := ir.NewBinaryExpr(pos, op, x, y) - if x.Type().HasTParam() || y.Type().HasTParam() { - // Delay transformAdd() if either arg has a type param, - // since it needs to know the exact types to decide whether - // to transform OADD to OADDSTR. - n.SetType(typ) - n.SetTypecheck(3) - return n - } - typed(typ, n) - return transformAdd(n) - default: - return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y)) - } -} - -func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node { - n := ir.NewCallExpr(pos, ir.OCALL, fun, args) - n.IsDDD = dots - // n.Use will be changed to ir.CallUseStmt in g.stmt() if this call is - // just a statement (any return values are ignored). - n.Use = ir.CallUseExpr - - if fun.Op() == ir.OTYPE { - // Actually a type conversion, not a function call. - if fun.Type().HasTParam() || args[0].Type().HasTParam() { - // For type params, don't typecheck until we actually know - // the type. - return typed(typ, n) - } - typed(typ, n) - return transformConvCall(n) - } - - if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 { - // For Builtin ops, we currently stay with using the old - // typechecker to transform the call to a more specific expression - // and possibly use more specific ops. However, for a bunch of the - // ops, we delay doing the old typechecker if any of the args have - // type params, for a variety of reasons: - // - // OMAKE: hard to choose specific ops OMAKESLICE, etc. until arg type is known - // OREAL/OIMAG: can't determine type float32/float64 until arg type know - // OLEN/OCAP: old typechecker will complain if arg is not obviously a slice/array. - // OAPPEND: old typechecker will complain if arg is not obviously slice, etc. - // - // We will eventually break out the transforming functionality - // needed for builtin's, and call it here or during stenciling, as - // appropriate. - switch fun.BuiltinOp { - case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND: - hasTParam := false - for _, arg := range args { - if arg.Type().HasTParam() { - hasTParam = true - break - } - } - if hasTParam { - return typed(typ, n) - } - } - - typed(typ, n) - return transformBuiltin(n) - } - - // Add information, now that we know that fun is actually being called. - switch fun := fun.(type) { - case *ir.ClosureExpr: - fun.Func.SetClosureCalled(true) - case *ir.SelectorExpr: - if fun.Op() == ir.OCALLPART { - op := ir.ODOTMETH - if fun.X.Type().IsInterface() { - op = ir.ODOTINTER - } - fun.SetOp(op) - // Set the type to include the receiver, since that's what - // later parts of the compiler expect - fun.SetType(fun.Selection.Type) - } - } - - if fun.Type().HasTParam() { - // If the fun arg is or has a type param, don't do any extra - // transformations, since we may not have needed properties yet - // (e.g. number of return values, etc). The type param is probably - // described by a structural constraint that requires it to be a - // certain function type, etc., but we don't want to analyze that. - return typed(typ, n) - } - - if fun.Op() == ir.OXDOT { - if !fun.(*ir.SelectorExpr).X.Type().HasTParam() { - base.FatalfAt(pos, "Expecting type param receiver in %v", fun) - } - // For methods called in a generic function, don't do any extra - // transformations. We will do those later when we create the - // instantiated function and have the correct receiver type. typed(typ, n) return n - } - if fun.Op() != ir.OFUNCINST { - // If no type params, do the normal call transformations. This - // will convert OCALL to OCALLFUNC. - typed(typ, n) - transformCall(n) + default: + n := ir.NewBinaryExpr(pos, op, x, y) + typed(x.Type(), n) return n } - - // Leave the op as OCALL, which indicates the call still needs typechecking. - typed(typ, n) - return n } -func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node { +func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr { n := ir.NewBinaryExpr(pos, op, x, y) - if x.Type().HasTParam() || y.Type().HasTParam() { - // Delay transformCompare() if either arg has a type param, since - // it needs to know the exact types to decide on any needed conversions. - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - transformCompare(n) return n } @@ -225,7 +128,7 @@ func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr { // Method value. typ := typecheck.NewMethodType(method.Type, nil) - return dot(pos, typ, ir.OCALLPART, x, method) + return dot(pos, typ, ir.OMETHVALUE, x, method) } // MethodExpr returns a OMETHEXPR node with the indicated index into the methods @@ -264,34 +167,19 @@ func method(typ *types.Type, index int) *types.Field { return types.ReceiverBaseType(typ).Methods().Index(index) } -func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node { +func Index(pos src.XPos, typ *types.Type, x, index ir.Node) *ir.IndexExpr { n := ir.NewIndexExpr(pos, x, index) - if x.Type().HasTParam() { - // transformIndex needs to know exact type - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - // transformIndex will modify n.Type() for OINDEXMAP. - transformIndex(n) return n } -func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node { +func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) *ir.SliceExpr { op := ir.OSLICE if max != nil { op = ir.OSLICE3 } n := ir.NewSliceExpr(pos, op, x, low, high, max) - if x.Type().HasTParam() { - // transformSlice needs to know if x.Type() is a string or an array or a slice. - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - transformSlice(n) return n } @@ -321,5 +209,75 @@ var one = constant.MakeInt64(1) func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt { assert(x.Type() != nil) - return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type())) + bl := ir.NewBasicLit(pos, one) + if x.Type().HasTParam() { + // If the operand is generic, then types2 will have proved it must be + // a type that fits with increment/decrement, so just set the type of + // "one" to n.Type(). This works even for types that are eventually + // float or complex. + typed(x.Type(), bl) + } else { + bl = typecheck.DefaultLit(bl, x.Type()) + } + return ir.NewAssignOpStmt(pos, op, x, bl) +} + +func idealType(tv types2.TypeAndValue) types2.Type { + // The gc backend expects all expressions to have a concrete type, and + // types2 mostly satisfies this expectation already. But there are a few + // cases where the Go spec doesn't require converting to concrete type, + // and so types2 leaves them untyped. So we need to fix those up here. + typ := tv.Type + if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 { + switch basic.Kind() { + case types2.UntypedNil: + // ok; can appear in type switch case clauses + // TODO(mdempsky): Handle as part of type switches instead? + case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex: + // Untyped rhs of non-constant shift, e.g. x << 1.0. + // If we have a constant value, it must be an int >= 0. + if tv.Value != nil { + s := constant.ToInt(tv.Value) + assert(s.Kind() == constant.Int && constant.Sign(s) >= 0) + } + typ = types2.Typ[types2.Uint] + case types2.UntypedBool: + typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition + case types2.UntypedString: + typ = types2.Typ[types2.String] // argument to "append" or "copy" calls + default: + return nil + } + } + return typ +} + +func isTypeParam(t types2.Type) bool { + _, ok := t.(*types2.TypeParam) + return ok +} + +// isNotInHeap reports whether typ is or contains an element of type +// runtime/internal/sys.NotInHeap. +func isNotInHeap(typ types2.Type) bool { + if named, ok := typ.(*types2.Named); ok { + if obj := named.Obj(); obj.Name() == "nih" && obj.Pkg().Path() == "runtime/internal/sys" { + return true + } + typ = named.Underlying() + } + + switch typ := typ.(type) { + case *types2.Array: + return isNotInHeap(typ.Elem()) + case *types2.Struct: + for i := 0; i < typ.NumFields(); i++ { + if isNotInHeap(typ.Field(i).Type()) { + return true + } + } + return false + default: + return false + } } diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go index 701e9001c859ea..8b017ecfd59b29 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -8,12 +8,10 @@ import ( "errors" "fmt" "internal/buildcfg" - "io" + "internal/pkgbits" "os" pathpkg "path" "runtime" - "sort" - "strconv" "strings" "unicode" "unicode/utf8" @@ -21,7 +19,6 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/importer" "cmd/compile/internal/ir" - "cmd/compile/internal/syntax" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/compile/internal/types2" @@ -29,11 +26,10 @@ import ( "cmd/internal/bio" "cmd/internal/goobj" "cmd/internal/objabi" - "cmd/internal/src" ) -// Temporary import helper to get type2-based type-checking going. type gcimports struct { + ctxt *types2.Context packages map[string]*types2.Package } @@ -46,13 +42,8 @@ func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*ty panic("mode must be 0") } - path, err := resolveImportPath(path) - if err != nil { - return nil, err - } - - lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) } - return importer.Import(m.packages, path, srcDir, lookup) + _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages) + return pkg, err } func isDriveLetter(b byte) bool { @@ -117,6 +108,8 @@ func openPackage(path string) (*os.File, error) { suffix = "_race" } else if base.Flag.MSan { suffix = "_msan" + } else if base.Flag.ASan { + suffix = "_asan" } if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { @@ -129,10 +122,6 @@ func openPackage(path string) (*os.File, error) { return nil, errors.New("file not found") } -// myheight tracks the local package's height based on packages -// imported so far. -var myheight int - // resolveImportPath resolves an import path as it appears in a Go // source file to the package's full path. func resolveImportPath(path string) (string, error) { @@ -175,170 +164,195 @@ func resolveImportPath(path string) (string, error) { return path, nil } -// TODO(mdempsky): Return an error instead. -func importfile(decl *syntax.ImportDecl) *types.Pkg { - if decl.Path.Kind != syntax.StringLit { - base.Errorf("import path must be a string") - return nil +// readImportFile reads the import file for the given package path and +// returns its types.Pkg representation. If packages is non-nil, the +// types2.Package representation is also returned. +func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { + path, err = resolveImportPath(path) + if err != nil { + return } - path, err := strconv.Unquote(decl.Path.Value) - if err != nil { - base.Errorf("import path must be a string") - return nil + if path == "unsafe" { + pkg1, pkg2 = types.UnsafePkg, types2.Unsafe + + // TODO(mdempsky): Investigate if this actually matters. Why would + // the linker or runtime care whether a package imported unsafe? + if !pkg1.Direct { + pkg1.Direct = true + target.Imports = append(target.Imports, pkg1) + } + + return } - if err := checkImportPath(path, false); err != nil { - base.Errorf("%s", err.Error()) - return nil + pkg1 = types.NewPkg(path, "") + if packages != nil { + pkg2 = packages[path] + assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete())) } - path, err = resolveImportPath(path) + if pkg1.Direct { + return + } + pkg1.Direct = true + target.Imports = append(target.Imports, pkg1) + + f, err := openPackage(path) if err != nil { - base.Errorf("%s", err) - return nil + return } + defer f.Close() - importpkg := types.NewPkg(path, "") - if importpkg.Direct { - return importpkg // already fully loaded + r, end, err := findExportData(f) + if err != nil { + return } - importpkg.Direct = true - typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg) - if path == "unsafe" { - return importpkg // initialized with universe + if base.Debug.Export != 0 { + fmt.Printf("importing %s (%s)\n", path, f.Name()) } - f, err := openPackage(path) + c, err := r.ReadByte() + if err != nil { + return + } + + pos := r.Offset() + + // Map export data section into memory as a single large + // string. This reduces heap fragmentation and allows returning + // individual substrings very efficiently. + var data string + data, err = base.MapFile(r.File(), pos, end-pos) if err != nil { - base.Errorf("could not import %q: %v", path, err) - base.ErrorExit() + return + } + + switch c { + case 'u': + if !buildcfg.Experiment.Unified { + base.Fatalf("unexpected export data format") + } + + // TODO(mdempsky): This seems a bit clunky. + data = strings.TrimSuffix(data, "\n$$\n") + + pr := pkgbits.NewPkgDecoder(pkg1.Path, data) + + // Read package descriptors for both types2 and compiler backend. + readPackage(newPkgReader(pr), pkg1, false) + pkg2 = importer.ReadPackage(env, packages, pr) + + case 'i': + if buildcfg.Experiment.Unified { + base.Fatalf("unexpected export data format") + } + + typecheck.ReadImports(pkg1, data) + + if packages != nil { + pkg2, err = importer.ImportData(packages, data, path) + if err != nil { + return + } + } + + default: + // Indexed format is distinguished by an 'i' byte, + // whereas previous export formats started with 'c', 'd', or 'v'. + err = fmt.Errorf("unexpected package format byte: %v", c) + return } - imp := bio.NewReader(f) - defer imp.Close() - file := f.Name() + + err = addFingerprint(path, f, end) + return +} + +// findExportData returns a *bio.Reader positioned at the start of the +// binary export data section, and a file offset for where to stop +// reading. +func findExportData(f *os.File) (r *bio.Reader, end int64, err error) { + r = bio.NewReader(f) // check object header - p, err := imp.ReadString('\n') + line, err := r.ReadString('\n') if err != nil { - base.Errorf("import %s: reading input: %v", file, err) - base.ErrorExit() + return } - if p == "!\n" { // package archive + if line == "!\n" { // package archive // package export block should be first - sz := archive.ReadHeader(imp.Reader, "__.PKGDEF") + sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF")) if sz <= 0 { - base.Errorf("import %s: not a package file", file) - base.ErrorExit() + err = errors.New("not a package file") + return } - p, err = imp.ReadString('\n') + end = r.Offset() + sz + line, err = r.ReadString('\n') if err != nil { - base.Errorf("import %s: reading input: %v", file, err) - base.ErrorExit() + return } + } else { + // Not an archive; provide end of file instead. + // TODO(mdempsky): I don't think this happens anymore. + var fi os.FileInfo + fi, err = f.Stat() + if err != nil { + return + } + end = fi.Size() } - if !strings.HasPrefix(p, "go object ") { - base.Errorf("import %s: not a go object file: %s", file, p) - base.ErrorExit() + if !strings.HasPrefix(line, "go object ") { + err = fmt.Errorf("not a go object file: %s", line) + return } - q := objabi.HeaderString() - if p != q { - base.Errorf("import %s: object is [%s] expected [%s]", file, p, q) - base.ErrorExit() + if expect := objabi.HeaderString(); line != expect { + err = fmt.Errorf("object is [%s] expected [%s]", line, expect) + return } // process header lines - for { - p, err = imp.ReadString('\n') + for !strings.HasPrefix(line, "$$") { + line, err = r.ReadString('\n') if err != nil { - base.Errorf("import %s: reading input: %v", file, err) - base.ErrorExit() - } - if p == "\n" { - break // header ends with blank line + return } } // Expect $$B\n to signal binary import format. - - // look for $$ - var c byte - for { - c, err = imp.ReadByte() - if err != nil { - break - } - if c == '$' { - c, err = imp.ReadByte() - if c == '$' || err != nil { - break - } - } + if line != "$$B\n" { + err = errors.New("old export format no longer supported (recompile library)") + return } - // get character after $$ - if err == nil { - c, _ = imp.ReadByte() - } + return +} +// addFingerprint reads the linker fingerprint included at the end of +// the exportdata. +func addFingerprint(path string, f *os.File, end int64) error { + const eom = "\n$$\n" var fingerprint goobj.FingerprintType - switch c { - case '\n': - base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path) - return nil - - case 'B': - if base.Debug.Export != 0 { - fmt.Printf("importing %s (%s)\n", path, file) - } - imp.ReadByte() // skip \n after $$B - c, err = imp.ReadByte() - if err != nil { - base.Errorf("import %s: reading input: %v", file, err) - base.ErrorExit() - } - - // Indexed format is distinguished by an 'i' byte, - // whereas previous export formats started with 'c', 'd', or 'v'. - if c != 'i' { - base.Errorf("import %s: unexpected package format byte: %v", file, c) - base.ErrorExit() - } - fingerprint = typecheck.ReadImports(importpkg, imp) - - default: - base.Errorf("no import in %q", path) - base.ErrorExit() + var buf [len(fingerprint) + len(eom)]byte + if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil { + return err } - // assume files move (get installed) so don't record the full path - if base.Flag.Cfg.PackageFile != nil { - // If using a packageFile map, assume path_ can be recorded directly. - base.Ctxt.AddImport(path, fingerprint) - } else { - // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a". - base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint) + // Caller should have given us the end position of the export data, + // which should end with the "\n$$\n" marker. As a consistency check + // to make sure we're reading at the right offset, make sure we + // found the marker. + if s := string(buf[len(fingerprint):]); s != eom { + return fmt.Errorf("expected $$ marker, but found %q", s) } - if importpkg.Height >= myheight { - myheight = importpkg.Height + 1 - } + copy(fingerprint[:], buf[:]) + base.Ctxt.AddImport(path, fingerprint) - return importpkg -} - -// The linker uses the magic symbol prefixes "go." and "type." -// Avoid potential confusion between import paths and symbols -// by rejecting these reserved imports for now. Also, people -// "can do weird things in GOPATH and we'd prefer they didn't -// do _that_ weird thing" (per rsc). See also #4257. -var reservedimports = []string{ - "go", - "type", + return nil } func checkImportPath(path string, allowSpace bool) error { @@ -350,7 +364,7 @@ func checkImportPath(path string, allowSpace bool) error { return errors.New("import path contains NUL") } - for _, ri := range reservedimports { + for ri := range base.ReservedImports { if path == ri { return fmt.Errorf("import path %q is reserved and cannot be used", path) } @@ -373,135 +387,3 @@ func checkImportPath(path string, allowSpace bool) error { return nil } - -func pkgnotused(lineno src.XPos, path string, name string) { - // If the package was imported with a name other than the final - // import path element, show it explicitly in the error message. - // Note that this handles both renamed imports and imports of - // packages containing unconventional package declarations. - // Note that this uses / always, even on Windows, because Go import - // paths always use forward slashes. - elem := path - if i := strings.LastIndex(elem, "/"); i >= 0 { - elem = elem[i+1:] - } - if name == "" || elem == name { - base.ErrorfAt(lineno, "imported and not used: %q", path) - } else { - base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name) - } -} - -func mkpackage(pkgname string) { - if types.LocalPkg.Name == "" { - if pkgname == "_" { - base.Errorf("invalid package name _") - } - types.LocalPkg.Name = pkgname - } else { - if pkgname != types.LocalPkg.Name { - base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name) - } - } -} - -func clearImports() { - type importedPkg struct { - pos src.XPos - path string - name string - } - var unused []importedPkg - - for _, s := range types.LocalPkg.Syms { - n := ir.AsNode(s.Def) - if n == nil { - continue - } - if n.Op() == ir.OPACK { - // throw away top-level package name left over - // from previous file. - // leave s->block set to cause redeclaration - // errors if a conflicting top-level name is - // introduced by a different file. - p := n.(*ir.PkgName) - if !p.Used && base.SyntaxErrors() == 0 { - unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name}) - } - s.Def = nil - continue - } - if types.IsDotAlias(s) { - // throw away top-level name left over - // from previous import . "x" - // We'll report errors after type checking in CheckDotImports. - s.Def = nil - continue - } - } - - sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) }) - for _, pkg := range unused { - pkgnotused(pkg.pos, pkg.path, pkg.name) - } -} - -// CheckDotImports reports errors for any unused dot imports. -func CheckDotImports() { - for _, pack := range dotImports { - if !pack.Used { - base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path) - } - } - - // No longer needed; release memory. - dotImports = nil - typecheck.DotImportRefs = nil -} - -// dotImports tracks all PkgNames that have been dot-imported. -var dotImports []*ir.PkgName - -// find all the exported symbols in package referenced by PkgName, -// and make them available in the current package -func importDot(pack *ir.PkgName) { - if typecheck.DotImportRefs == nil { - typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName) - } - - opkg := pack.Pkg - for _, s := range opkg.Syms { - if s.Def == nil { - if _, ok := typecheck.DeclImporter[s]; !ok { - continue - } - } - if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot - continue - } - s1 := typecheck.Lookup(s.Name) - if s1.Def != nil { - pkgerror := fmt.Sprintf("during import %q", opkg.Path) - typecheck.Redeclared(base.Pos, s1, pkgerror) - continue - } - - id := ir.NewIdent(src.NoXPos, s) - typecheck.DotImportRefs[id] = pack - s1.Def = id - s1.Block = 1 - } - - dotImports = append(dotImports, pack) -} - -// importName is like oldname, -// but it reports an error if sym is from another package and not exported. -func importName(sym *types.Sym) ir.Node { - n := oldname(sym) - if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg { - n.SetDiag(true) - base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name) - } - return n -} diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 3e0d3285ab916e..dc69e949249ee2 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -6,7 +6,7 @@ package noder import ( "fmt" - "os" + "sort" "cmd/compile/internal/base" "cmd/compile/internal/dwarfgen" @@ -18,9 +18,9 @@ import ( "cmd/internal/src" ) -// check2 type checks a Go package using types2, and then generates IR -// using the results. -func check2(noders []*noder) { +// checkFiles configures and runs the types2 checker on the given +// parsed source files and then returns the result. +func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) { if base.SyntaxErrors() != 0 { base.ErrorExit() } @@ -34,52 +34,143 @@ func check2(noders []*noder) { } // typechecking + ctxt := types2.NewContext() + importer := gcimports{ + ctxt: ctxt, + packages: make(map[string]*types2.Package), + } conf := types2.Config{ + Context: ctxt, GoVersion: base.Flag.Lang, - IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode + IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode CompilerErrorMessages: true, // use error strings matching existing compiler errors Error: func(err error) { terr := err.(types2.Error) base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg) }, - Importer: &gcimports{ - packages: make(map[string]*types2.Package), - }, - Sizes: &gcSizes{}, + Importer: &importer, + Sizes: &gcSizes{}, } - info := types2.Info{ + info := &types2.Info{ Types: make(map[syntax.Expr]types2.TypeAndValue), Defs: make(map[*syntax.Name]types2.Object), Uses: make(map[*syntax.Name]types2.Object), Selections: make(map[*syntax.SelectorExpr]*types2.Selection), Implicits: make(map[syntax.Node]types2.Object), Scopes: make(map[syntax.Node]*types2.Scope), - Inferred: make(map[syntax.Expr]types2.Inferred), + Instances: make(map[*syntax.Name]types2.Instance), // expand as needed } - pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info) - files = nil + + pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info) + + // Implementation restriction: we don't allow not-in-heap types to + // be used as type arguments (#54765). + { + type nihTarg struct { + pos src.XPos + typ types2.Type + } + var nihTargs []nihTarg + + for name, inst := range info.Instances { + for i := 0; i < inst.TypeArgs.Len(); i++ { + if targ := inst.TypeArgs.At(i); isNotInHeap(targ) { + nihTargs = append(nihTargs, nihTarg{m.makeXPos(name.Pos()), targ}) + } + } + } + sort.Slice(nihTargs, func(i, j int) bool { + ti, tj := nihTargs[i], nihTargs[j] + return ti.pos.Before(tj.pos) + }) + for _, targ := range nihTargs { + base.ErrorfAt(targ.pos, "cannot use incomplete (or unallocatable) type as a type argument: %v", targ.typ) + } + } + base.ExitIfErrors() if err != nil { base.FatalfAt(src.NoXPos, "conf.Check error: %v", err) } - if base.Flag.G < 2 { - os.Exit(0) - } + + return m, pkg, info +} + +// check2 type checks a Go package using types2, and then generates IR +// using the results. +func check2(noders []*noder) { + m, pkg, info := checkFiles(noders) g := irgen{ target: typecheck.Target, self: pkg, - info: &info, + info: info, posMap: m, objs: make(map[types2.Object]*ir.Name), typs: make(map[types2.Type]*types.Type), } g.generate(noders) +} - if base.Flag.G < 3 { - os.Exit(0) - } +// Information about sub-dictionary entries in a dictionary +type subDictInfo struct { + // Call or XDOT node that requires a dictionary. + callNode ir.Node + // Saved CallExpr.X node (*ir.SelectorExpr or *InstExpr node) for a generic + // method or function call, since this node will get dropped when the generic + // method/function call is transformed to a call on the instantiated shape + // function. Nil for other kinds of calls or XDOTs. + savedXNode ir.Node +} + +// dictInfo is the dictionary format for an instantiation of a generic function with +// particular shapes. shapeParams, derivedTypes, subDictCalls, itabConvs, and methodExprClosures +// describe the actual dictionary entries in order, and the remaining fields are other info +// needed in doing dictionary processing during compilation. +type dictInfo struct { + // Types substituted for the type parameters, which are shape types. + shapeParams []*types.Type + // All types derived from those typeparams used in the instantiation. + derivedTypes []*types.Type + // Nodes in the instantiation that requires a subdictionary. Includes + // method and function calls (OCALL), function values (OFUNCINST), method + // values/expressions (OXDOT). + subDictCalls []subDictInfo + // Nodes in the instantiation that are a conversion from a typeparam/derived + // type to a specific interface. + itabConvs []ir.Node + // Method expression closures. For a generic type T with method M(arg1, arg2) res, + // these closures are func(rcvr T, arg1, arg2) res. + // These closures capture no variables, they are just the generic version of ·f symbols + // that live in the dictionary instead of in the readonly globals section. + methodExprClosures []methodExprClosure + + // Mapping from each shape type that substitutes a type param, to its + // type bound (which is also substituted with shapes if it is parameterized) + shapeToBound map[*types.Type]*types.Type + + // For type switches on nonempty interfaces, a map from OTYPE entries of + // HasShape type, to the interface type we're switching from. + type2switchType map[ir.Node]*types.Type + + startSubDict int // Start of dict entries for subdictionaries + startItabConv int // Start of dict entries for itab conversions + startMethodExprClosures int // Start of dict entries for closures for method expressions + dictLen int // Total number of entries in dictionary +} + +type methodExprClosure struct { + idx int // index in list of shape parameters + name string // method name +} + +// instInfo is information gathered on an shape instantiation of a function. +type instInfo struct { + fun *ir.Func // The instantiated function (with body) + dictParam *ir.Name // The node inside fun that refers to the dictionary param + + dictInfo *dictInfo } type irgen struct { @@ -92,8 +183,64 @@ type irgen struct { typs map[types2.Type]*types.Type marker dwarfgen.ScopeMarker - // Fully-instantiated generic types whose methods should be instantiated - instTypeList []*types.Type + // laterFuncs records tasks that need to run after all declarations + // are processed. + laterFuncs []func() + // haveEmbed indicates whether the current node belongs to file that + // imports "embed" package. + haveEmbed bool + + // exprStmtOK indicates whether it's safe to generate expressions or + // statements yet. + exprStmtOK bool + + // types which we need to finish, by doing g.fillinMethods. + typesToFinalize []*typeDelayInfo + + // True when we are compiling a top-level generic function or method. Use to + // avoid adding closures of generic functions/methods to the target.Decls + // list. + topFuncIsGeneric bool + + // The context during type/function/method declarations that is used to + // uniquely name type parameters. We need unique names for type params so we + // can be sure they match up correctly between types2-to-types1 translation + // and types1 importing. + curDecl string +} + +// genInst has the information for creating needed instantiations and modifying +// functions to use instantiations. +type genInst struct { + dnum int // for generating unique dictionary variables + + // Map from the names of all instantiations to information about the + // instantiations. + instInfoMap map[*types.Sym]*instInfo + + // Dictionary syms which we need to finish, by writing out any itabconv + // or method expression closure entries. + dictSymsToFinalize []*delayInfo + + // New instantiations created during this round of buildInstantiations(). + newInsts []ir.Node +} + +func (g *irgen) later(fn func()) { + g.laterFuncs = append(g.laterFuncs, fn) +} + +type delayInfo struct { + gf *ir.Name + targs []*types.Type + sym *types.Sym + off int + isMeth bool +} + +type typeDelayInfo struct { + typ *types2.Named + ntyp *types.Type } func (g *irgen) generate(noders []*noder) { @@ -107,7 +254,7 @@ func (g *irgen) generate(noders []*noder) { // At this point, types2 has already handled name resolution and // type checking. We just need to map from its object and type // representations to those currently used by the rest of the - // compiler. This happens mostly in 3 passes. + // compiler. This happens in a few passes. // 1. Process all import declarations. We use the compiler's own // importer for this, rather than types2's gcimporter-derived one, @@ -132,7 +279,6 @@ Outer: } } } - types.LocalPkg.Height = myheight // 2. Process all package-block type declarations. As with imports, // we need to make sure all types are properly instantiated before @@ -156,8 +302,20 @@ Outer: types.ResumeCheckSize() // 3. Process all remaining declarations. - for _, declList := range declLists { - g.target.Decls = append(g.target.Decls, g.decls(declList)...) + for i, declList := range declLists { + old := g.haveEmbed + g.haveEmbed = noders[i].importedEmbed + g.decls((*ir.Nodes)(&g.target.Decls), declList) + g.haveEmbed = old + } + g.exprStmtOK = true + + // 4. Run any "later" tasks. Avoid using 'range' so that tasks can + // recursively queue further tasks. (Not currently utilized though.) + for len(g.laterFuncs) > 0 { + fn := g.laterFuncs[0] + g.laterFuncs = g.laterFuncs[1:] + fn() } if base.Flag.W > 1 { @@ -167,26 +325,41 @@ Outer: } } - typecheck.DeclareUniverse() - for _, p := range noders { // Process linkname and cgo pragmas. p.processPragmas() // Double check for any type-checking inconsistencies. This can be // removed once we're confident in IR generation results. - syntax.Walk(p.file, func(n syntax.Node) bool { + syntax.Crawl(p.file, func(n syntax.Node) bool { g.validate(n) return false }) } - // Create any needed stencils of generic functions - g.stencil() + if base.Flag.Complete { + for _, n := range g.target.Decls { + if fn, ok := n.(*ir.Func); ok { + if fn.Body == nil && fn.Nname.Sym().Linkname == "" { + base.ErrorfAt(fn.Pos(), "missing function body") + } + } + } + } + + // Check for unusual case where noder2 encounters a type error that types2 + // doesn't check for (e.g. notinheap incompatibility). + base.ExitIfErrors() + + typecheck.DeclareUniverse() + + // Create any needed instantiations of generic functions and transform + // existing and new functions to use those instantiations. + BuildInstantiations() - // For now, remove all generic functions from g.target.Decl, since they - // have been used for stenciling, but don't compile. TODO: We will - // eventually export any exportable generic functions. + // Remove all generic functions from g.target.Decl, since they have been + // used for stenciling, but don't compile. Generic functions will already + // have been marked for export as appropriate. j := 0 for i, decl := range g.target.Decls { if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() { @@ -195,9 +368,17 @@ Outer: } } g.target.Decls = g.target.Decls[:j] + + base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs)) } func (g *irgen) unhandled(what string, p poser) { base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p) panic("unreachable") } + +// delayTransform returns true if we should delay all transforms, because we are +// creating the nodes for a generic function/method. +func (g *irgen) delayTransform() bool { + return g.topFuncIsGeneric +} diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go index 66a56a50ec8ebc..c964eca678416c 100644 --- a/src/cmd/compile/internal/noder/lex.go +++ b/src/cmd/compile/internal/noder/lex.go @@ -30,13 +30,12 @@ const ( ir.NoCheckPtr | ir.RegisterParams | // TODO(register args) remove after register abi is working ir.CgoUnsafeArgs | + ir.UintptrKeepAlive | ir.UintptrEscapes | ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec - - typePragmas = ir.NotInHeap ) func pragmaFlag(verb string) ir.PragmaFlag { @@ -67,23 +66,15 @@ func pragmaFlag(verb string) ir.PragmaFlag { return ir.Yeswritebarrierrec case "go:cgo_unsafe_args": return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968) + case "go:uintptrkeepalive": + return ir.UintptrKeepAlive case "go:uintptrescapes": - // For the next function declared in the file - // any uintptr arguments may be pointer values - // converted to uintptr. This directive - // ensures that the referenced allocated - // object, if any, is retained and not moved - // until the call completes, even though from - // the types alone it would appear that the - // object is no longer needed during the - // call. The conversion to uintptr must appear - // in the argument list. - // Used in syscall/dll_windows.go. - return ir.UintptrEscapes + // This directive extends //go:uintptrkeepalive by forcing + // uintptr arguments to escape to the heap, which makes stack + // growth safe. + return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive case "go:registerparams": // TODO(register args) remove after register abi is working return ir.RegisterParams - case "go:notinheap": - return ir.NotInHeap } return 0 } diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go new file mode 100644 index 00000000000000..0f39fdec051f5f --- /dev/null +++ b/src/cmd/compile/internal/noder/linker.go @@ -0,0 +1,337 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "internal/pkgbits" + "io" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/types" + "cmd/internal/goobj" + "cmd/internal/obj" +) + +// This file implements the unified IR linker, which combines the +// local package's stub data with imported package data to produce a +// complete export data file. It also rewrites the compiler's +// extension data sections based on the results of compilation (e.g., +// the function inlining cost and linker symbol index assignments). +// +// TODO(mdempsky): Using the name "linker" here is confusing, because +// readers are likely to mistake references to it for cmd/link. But +// there's a shortage of good names for "something that combines +// multiple parts into a cohesive whole"... e.g., "assembler" and +// "compiler" are also already taken. + +// TODO(mdempsky): Should linker go into pkgbits? Probably the +// low-level linking details can be moved there, but the logic for +// handling extension data needs to stay in the compiler. + +// A linker combines a package's stub export data with any referenced +// elements from imported packages into a single, self-contained +// export data file. +type linker struct { + pw pkgbits.PkgEncoder + + pkgs map[string]pkgbits.Index + decls map[*types.Sym]pkgbits.Index + bodies map[*types.Sym]pkgbits.Index +} + +// relocAll ensures that all elements specified by pr and relocs are +// copied into the output export data file, and returns the +// corresponding indices in the output. +func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt { + res := make([]pkgbits.RelocEnt, len(relocs)) + for i, rent := range relocs { + rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx) + res[i] = rent + } + return res +} + +// relocIdx ensures a single element is copied into the output export +// data file, and returns the corresponding index in the output. +func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx pkgbits.Index) pkgbits.Index { + assert(pr != nil) + + absIdx := pr.AbsIdx(k, idx) + + if newidx := pr.newindex[absIdx]; newidx != 0 { + return ^newidx + } + + var newidx pkgbits.Index + switch k { + case pkgbits.RelocString: + newidx = l.relocString(pr, idx) + case pkgbits.RelocPkg: + newidx = l.relocPkg(pr, idx) + case pkgbits.RelocObj: + newidx = l.relocObj(pr, idx) + + default: + // Generic relocations. + // + // TODO(mdempsky): Deduplicate more sections? In fact, I think + // every section could be deduplicated. This would also be easier + // if we do external relocations. + + w := l.pw.NewEncoderRaw(k) + l.relocCommon(pr, &w, k, idx) + newidx = w.Idx + } + + pr.newindex[absIdx] = ^newidx + + return newidx +} + +// relocString copies the specified string from pr into the output +// export data file, deduplicating it against other strings. +func (l *linker) relocString(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { + return l.pw.StringIdx(pr.StringIdx(idx)) +} + +// relocPkg copies the specified package from pr into the output +// export data file, rewriting its import path to match how it was +// imported. +// +// TODO(mdempsky): Since CL 391014, we already have the compilation +// unit's import path, so there should be no need to rewrite packages +// anymore. +func (l *linker) relocPkg(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { + path := pr.PeekPkgPath(idx) + + if newidx, ok := l.pkgs[path]; ok { + return newidx + } + + r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef) + w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef) + l.pkgs[path] = w.Idx + + // TODO(mdempsky): We end up leaving an empty string reference here + // from when the package was originally written as "". Probably not + // a big deal, but a little annoying. Maybe relocating + // cross-references in place is the way to go after all. + w.Relocs = l.relocAll(pr, r.Relocs) + + _ = r.String() // original path + w.String(path) + + io.Copy(&w.Data, &r.Data) + + return w.Flush() +} + +// relocObj copies the specified object from pr into the output export +// data file, rewriting its compiler-private extension data (e.g., +// adding inlining cost and escape analysis results for functions). +func (l *linker) relocObj(pr *pkgReader, idx pkgbits.Index) pkgbits.Index { + path, name, tag := pr.PeekObj(idx) + sym := types.NewPkg(path, "").Lookup(name) + + if newidx, ok := l.decls[sym]; ok { + return newidx + } + + if tag == pkgbits.ObjStub && path != "builtin" && path != "unsafe" { + pri, ok := objReader[sym] + if !ok { + base.Fatalf("missing reader for %q.%v", path, name) + } + assert(ok) + + pr = pri.pr + idx = pri.idx + + path2, name2, tag2 := pr.PeekObj(idx) + sym2 := types.NewPkg(path2, "").Lookup(name2) + assert(sym == sym2) + assert(tag2 != pkgbits.ObjStub) + } + + w := l.pw.NewEncoderRaw(pkgbits.RelocObj) + wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt) + wname := l.pw.NewEncoderRaw(pkgbits.RelocName) + wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict) + + l.decls[sym] = w.Idx + assert(wext.Idx == w.Idx) + assert(wname.Idx == w.Idx) + assert(wdict.Idx == w.Idx) + + l.relocCommon(pr, &w, pkgbits.RelocObj, idx) + l.relocCommon(pr, &wname, pkgbits.RelocName, idx) + l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx) + + // Generic types and functions won't have definitions, and imported + // objects may not either. + obj, _ := sym.Def.(*ir.Name) + local := sym.Pkg == types.LocalPkg + + if local && obj != nil { + wext.Sync(pkgbits.SyncObject1) + switch tag { + case pkgbits.ObjFunc: + l.relocFuncExt(&wext, obj) + case pkgbits.ObjType: + l.relocTypeExt(&wext, obj) + case pkgbits.ObjVar: + l.relocVarExt(&wext, obj) + } + wext.Flush() + } else { + l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx) + } + + // Check if we need to export the inline bodies for functions and + // methods. + if obj != nil { + if obj.Op() == ir.ONAME && obj.Class == ir.PFUNC { + l.exportBody(obj, local) + } + + if obj.Op() == ir.OTYPE { + if typ := obj.Type(); !typ.IsInterface() { + for _, method := range typ.Methods().Slice() { + l.exportBody(method.Nname.(*ir.Name), local) + } + } + } + } + + return w.Idx +} + +// exportBody exports the given function or method's body, if +// appropriate. local indicates whether it's a local function or +// method available on a locally declared type. (Due to cross-package +// type aliases, a method may be imported, but still available on a +// locally declared type.) +func (l *linker) exportBody(obj *ir.Name, local bool) { + assert(obj.Op() == ir.ONAME && obj.Class == ir.PFUNC) + + fn := obj.Func + if fn.Inl == nil { + return // not inlinable anyway + } + + // As a simple heuristic, if the function was declared in this + // package or we inlined it somewhere in this package, then we'll + // (re)export the function body. This isn't perfect, but seems + // reasonable in practice. In particular, it has the nice property + // that in the worst case, adding a blank import ensures the + // function body is available for inlining. + // + // TODO(mdempsky): Reimplement the reachable method crawling logic + // from typecheck/crawler.go. + exportBody := local || fn.Inl.Body != nil + if !exportBody { + return + } + + sym := obj.Sym() + if _, ok := l.bodies[sym]; ok { + // Due to type aliases, we might visit methods multiple times. + base.AssertfAt(obj.Type().Recv() != nil, obj.Pos(), "expected method: %v", obj) + return + } + + pri, ok := bodyReaderFor(fn) + assert(ok) + l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx) +} + +// relocCommon copies the specified element from pr into w, +// recursively relocating any referenced elements as well. +func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx pkgbits.Index) { + r := pr.NewDecoderRaw(k, idx) + w.Relocs = l.relocAll(pr, r.Relocs) + io.Copy(&w.Data, &r.Data) + w.Flush() +} + +func (l *linker) pragmaFlag(w *pkgbits.Encoder, pragma ir.PragmaFlag) { + w.Sync(pkgbits.SyncPragma) + w.Int(int(pragma)) +} + +func (l *linker) relocFuncExt(w *pkgbits.Encoder, name *ir.Name) { + w.Sync(pkgbits.SyncFuncExt) + + l.pragmaFlag(w, name.Func.Pragma) + l.linkname(w, name) + + // Relocated extension data. + w.Bool(true) + + // Record definition ABI so cross-ABI calls can be direct. + // This is important for the performance of calling some + // common functions implemented in assembly (e.g., bytealg). + w.Uint64(uint64(name.Func.ABI)) + + // Escape analysis. + for _, fs := range &types.RecvsParams { + for _, f := range fs(name.Type()).FieldSlice() { + w.String(f.Note) + } + } + + if inl := name.Func.Inl; w.Bool(inl != nil) { + w.Len(int(inl.Cost)) + w.Bool(inl.CanDelayResults) + } + + w.Sync(pkgbits.SyncEOF) +} + +func (l *linker) relocTypeExt(w *pkgbits.Encoder, name *ir.Name) { + w.Sync(pkgbits.SyncTypeExt) + + typ := name.Type() + + l.pragmaFlag(w, name.Pragma()) + + // For type T, export the index of type descriptor symbols of T and *T. + l.lsymIdx(w, "", reflectdata.TypeLinksym(typ)) + l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo())) + + if typ.Kind() != types.TINTER { + for _, method := range typ.Methods().Slice() { + l.relocFuncExt(w, method.Nname.(*ir.Name)) + } + } +} + +func (l *linker) relocVarExt(w *pkgbits.Encoder, name *ir.Name) { + w.Sync(pkgbits.SyncVarExt) + l.linkname(w, name) +} + +func (l *linker) linkname(w *pkgbits.Encoder, name *ir.Name) { + w.Sync(pkgbits.SyncLinkname) + + linkname := name.Sym().Linkname + if !l.lsymIdx(w, linkname, name.Linksym()) { + w.String(linkname) + } +} + +func (l *linker) lsymIdx(w *pkgbits.Encoder, linkname string, lsym *obj.LSym) bool { + if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" { + w.Int64(-1) + return false + } + + // For a defined symbol, export its index. + // For re-exporting an imported symbol, pass its index through. + w.Int64(int64(lsym.SymIdx)) + return true +} diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 5fcad096c28ae5..15b1bf7b9fe51a 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -5,9 +5,8 @@ package noder import ( + "errors" "fmt" - "go/constant" - "go/token" "os" "path/filepath" "runtime" @@ -17,7 +16,6 @@ import ( "unicode/utf8" "cmd/compile/internal/base" - "cmd/compile/internal/dwarfgen" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" "cmd/compile/internal/typecheck" @@ -29,39 +27,39 @@ import ( func LoadPackage(filenames []string) { base.Timer.Start("fe", "parse") - mode := syntax.CheckBranches - if base.Flag.G != 0 { - mode |= syntax.AllowGenerics - } - // Limit the number of simultaneously open files. sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) noders := make([]*noder, len(filenames)) - for i, filename := range filenames { + for i := range noders { p := noder{ - err: make(chan syntax.Error), - trackScopes: base.Flag.Dwarf, + err: make(chan syntax.Error), } noders[i] = &p + } - filename := filename - go func() { + // Move the entire syntax processing logic into a separate goroutine to avoid blocking on the "sem". + go func() { + for i, filename := range filenames { + filename := filename + p := noders[i] sem <- struct{}{} - defer func() { <-sem }() - defer close(p.err) - fbase := syntax.NewFileBase(filename) - - f, err := os.Open(filename) - if err != nil { - p.error(syntax.Error{Msg: err.Error()}) - return - } - defer f.Close() + go func() { + defer func() { <-sem }() + defer close(p.err) + fbase := syntax.NewFileBase(filename) + + f, err := os.Open(filename) + if err != nil { + p.error(syntax.Error{Msg: err.Error()}) + return + } + defer f.Close() - p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error - }() - } + p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error + }() + } + }() var lines uint for _, p := range noders { @@ -75,110 +73,36 @@ func LoadPackage(filenames []string) { } base.Timer.AddEvent(int64(lines), "lines") - if base.Flag.G != 0 { - // Use types2 to type-check and possibly generate IR. - check2(noders) + if base.Debug.Unified != 0 { + unified(noders) return } - for _, p := range noders { - p.node() - p.file = nil // release memory - } - - if base.SyntaxErrors() != 0 { - base.ErrorExit() - } - types.CheckDclstack() - - for _, p := range noders { - p.processPragmas() - } - - // Typecheck. - types.LocalPkg.Height = myheight - typecheck.DeclareUniverse() - typecheck.TypecheckAllowed = true - - // Process top-level declarations in phases. - - // Phase 1: const, type, and names and types of funcs. - // This will gather all the information about types - // and methods but doesn't depend on any of it. - // - // We also defer type alias declarations until phase 2 - // to avoid cycles like #18640. - // TODO(gri) Remove this again once we have a fix for #25838. - - // Don't use range--typecheck can add closures to Target.Decls. - base.Timer.Start("fe", "typecheck", "top1") - for i := 0; i < len(typecheck.Target.Decls); i++ { - n := typecheck.Target.Decls[i] - if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).X.Alias()) { - typecheck.Target.Decls[i] = typecheck.Stmt(n) - } - } - - // Phase 2: Variable assignments. - // To check interface assignments, depends on phase 1. - - // Don't use range--typecheck can add closures to Target.Decls. - base.Timer.Start("fe", "typecheck", "top2") - for i := 0; i < len(typecheck.Target.Decls); i++ { - n := typecheck.Target.Decls[i] - if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() { - typecheck.Target.Decls[i] = typecheck.Stmt(n) - } - } - - // Phase 3: Type check function bodies. - // Don't use range--typecheck can add closures to Target.Decls. - base.Timer.Start("fe", "typecheck", "func") - var fcount int64 - for i := 0; i < len(typecheck.Target.Decls); i++ { - n := typecheck.Target.Decls[i] - if n.Op() == ir.ODCLFUNC { - if base.Flag.W > 1 { - s := fmt.Sprintf("\nbefore typecheck %v", n) - ir.Dump(s, n) - } - typecheck.FuncBody(n.(*ir.Func)) - if base.Flag.W > 1 { - s := fmt.Sprintf("\nafter typecheck %v", n) - ir.Dump(s, n) - } - fcount++ - } - } - - // Phase 4: Check external declarations. - // TODO(mdempsky): This should be handled when type checking their - // corresponding ODCL nodes. - base.Timer.Start("fe", "typecheck", "externdcls") - for i, n := range typecheck.Target.Externs { - if n.Op() == ir.ONAME { - typecheck.Target.Externs[i] = typecheck.Expr(typecheck.Target.Externs[i]) - } - } - - // Phase 5: With all user code type-checked, it's now safe to verify map keys. - // With all user code typechecked, it's now safe to verify unused dot imports. - typecheck.CheckMapKeys() - CheckDotImports() - base.ExitIfErrors() + // Use types2 to type-check and generate IR. + check2(noders) } func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { base.ErrorfAt(p.makeXPos(pos), format, args...) } -// TODO(gri) Can we eliminate fileh in favor of absFilename? -func fileh(name string) string { - return objabi.AbsFile("", name, base.Flag.TrimPath) -} - -func absFilename(name string) string { - return objabi.AbsFile(base.Ctxt.Pathname, name, base.Flag.TrimPath) +// trimFilename returns the "trimmed" filename of b, which is the +// absolute filename after applying -trimpath processing. This +// filename form is suitable for use in object files and export data. +// +// If b's filename has already been trimmed (i.e., because it was read +// in from an imported package's export data), then the filename is +// returned unchanged. +func trimFilename(b *syntax.PosBase) string { + filename := b.Filename() + if !b.Trimmed() { + dir := "" + if b.IsFileBase() { + dir = base.Ctxt.Pathname + } + filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath) + } + return filename } // noder transforms package syntax's AST into a Node tree. @@ -191,76 +115,6 @@ type noder struct { err chan syntax.Error importedUnsafe bool importedEmbed bool - trackScopes bool - - funcState *funcState -} - -// funcState tracks all per-function state to make handling nested -// functions easier. -type funcState struct { - // scopeVars is a stack tracking the number of variables declared in - // the current function at the moment each open scope was opened. - scopeVars []int - marker dwarfgen.ScopeMarker - - lastCloseScopePos syntax.Pos -} - -func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) { - outerFuncState := p.funcState - p.funcState = new(funcState) - typecheck.StartFuncBody(fn) - - if block != nil { - body := p.stmts(block.List) - if body == nil { - body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} - } - fn.Body = body - - base.Pos = p.makeXPos(block.Rbrace) - fn.Endlineno = base.Pos - } - - typecheck.FinishFuncBody() - p.funcState.marker.WriteTo(fn) - p.funcState = outerFuncState -} - -func (p *noder) openScope(pos syntax.Pos) { - fs := p.funcState - types.Markdcl() - - if p.trackScopes { - fs.scopeVars = append(fs.scopeVars, len(ir.CurFunc.Dcl)) - fs.marker.Push(p.makeXPos(pos)) - } -} - -func (p *noder) closeScope(pos syntax.Pos) { - fs := p.funcState - fs.lastCloseScopePos = pos - types.Popdcl() - - if p.trackScopes { - scopeVars := fs.scopeVars[len(fs.scopeVars)-1] - fs.scopeVars = fs.scopeVars[:len(fs.scopeVars)-1] - if scopeVars == len(ir.CurFunc.Dcl) { - // no variables were declared in this scope, so we can retract it. - fs.marker.Unpush() - } else { - fs.marker.Pop(p.makeXPos(pos)) - } - } -} - -// closeAnotherScope is like closeScope, but it reuses the same mark -// position as the last closeScope call. This is useful for "for" and -// "if" statements, as their implicit blocks always end at the same -// position as an explicit block. -func (p *noder) closeAnotherScope() { - p.closeScope(p.funcState.lastCloseScopePos) } // linkname records a //go:linkname directive. @@ -270,24 +124,6 @@ type linkname struct { remote string } -func (p *noder) node() { - p.importedUnsafe = false - p.importedEmbed = false - - p.setlineno(p.file.PkgName) - mkpackage(p.file.PkgName.Value) - - if pragma, ok := p.file.Pragma.(*pragmas); ok { - pragma.Flag &^= ir.GoBuildPragma - p.checkUnused(pragma) - } - - typecheck.Target.Decls = append(typecheck.Target.Decls, p.decls(p.file.DeclList)...) - - base.Pos = src.NoXPos - clearImports() -} - func (p *noder) processPragmas() { for _, l := range p.linknames { if !p.importedUnsafe { @@ -296,8 +132,7 @@ func (p *noder) processPragmas() { } n := ir.AsNode(typecheck.Lookup(l.local).Def) if n == nil || n.Op() != ir.ONAME { - // TODO(mdempsky): Change to p.errorAt before Go 1.17 release. - // base.WarnfAt(p.makeXPos(l.pos), "//go:linkname must refer to declared function or variable (will be an error in Go 1.17)") + p.errorAt(l.pos, "//go:linkname must refer to declared function or variable") continue } if n.Sym().Linkname != "" { @@ -309,1061 +144,6 @@ func (p *noder) processPragmas() { typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...) } -func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) { - var cs constState - - for _, decl := range decls { - p.setlineno(decl) - switch decl := decl.(type) { - case *syntax.ImportDecl: - p.importDecl(decl) - - case *syntax.VarDecl: - l = append(l, p.varDecl(decl)...) - - case *syntax.ConstDecl: - l = append(l, p.constDecl(decl, &cs)...) - - case *syntax.TypeDecl: - l = append(l, p.typeDecl(decl)) - - case *syntax.FuncDecl: - l = append(l, p.funcDecl(decl)) - - default: - panic("unhandled Decl") - } - } - - return -} - -func (p *noder) importDecl(imp *syntax.ImportDecl) { - if imp.Path == nil || imp.Path.Bad { - return // avoid follow-on errors if there was a syntax error - } - - if pragma, ok := imp.Pragma.(*pragmas); ok { - p.checkUnused(pragma) - } - - ipkg := importfile(imp) - if ipkg == nil { - if base.Errors() == 0 { - base.Fatalf("phase error in import") - } - return - } - - if ipkg == ir.Pkgs.Unsafe { - p.importedUnsafe = true - } - if ipkg.Path == "embed" { - p.importedEmbed = true - } - - var my *types.Sym - if imp.LocalPkgName != nil { - my = p.name(imp.LocalPkgName) - } else { - my = typecheck.Lookup(ipkg.Name) - } - - pack := ir.NewPkgName(p.pos(imp), my, ipkg) - - switch my.Name { - case ".": - importDot(pack) - return - case "init": - base.ErrorfAt(pack.Pos(), "cannot import package as init - init must be a func") - return - case "_": - return - } - if my.Def != nil { - typecheck.Redeclared(pack.Pos(), my, "as imported package name") - } - my.Def = pack - my.Lastlineno = pack.Pos() - my.Block = 1 // at top level -} - -func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node { - names := p.declNames(ir.ONAME, decl.NameList) - typ := p.typeExprOrNil(decl.Type) - exprs := p.exprList(decl.Values) - - if pragma, ok := decl.Pragma.(*pragmas); ok { - varEmbed(p.makeXPos, names[0], decl, pragma, p.importedEmbed) - p.checkUnused(pragma) - } - - var init []ir.Node - p.setlineno(decl) - - if len(names) > 1 && len(exprs) == 1 { - as2 := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, exprs) - for _, v := range names { - as2.Lhs.Append(v) - typecheck.Declare(v, typecheck.DeclContext) - v.Ntype = typ - v.Defn = as2 - if ir.CurFunc != nil { - init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) - } - } - - return append(init, as2) - } - - for i, v := range names { - var e ir.Node - if i < len(exprs) { - e = exprs[i] - } - - typecheck.Declare(v, typecheck.DeclContext) - v.Ntype = typ - - if ir.CurFunc != nil { - init = append(init, ir.NewDecl(base.Pos, ir.ODCL, v)) - } - as := ir.NewAssignStmt(base.Pos, v, e) - init = append(init, as) - if e != nil || ir.CurFunc == nil { - v.Defn = as - } - } - - if len(exprs) != 0 && len(names) != len(exprs) { - base.Errorf("assignment mismatch: %d variables but %d values", len(names), len(exprs)) - } - - return init -} - -// constState tracks state between constant specifiers within a -// declaration group. This state is kept separate from noder so nested -// constant declarations are handled correctly (e.g., issue 15550). -type constState struct { - group *syntax.Group - typ ir.Ntype - values []ir.Node - iota int64 -} - -func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node { - if decl.Group == nil || decl.Group != cs.group { - *cs = constState{ - group: decl.Group, - } - } - - if pragma, ok := decl.Pragma.(*pragmas); ok { - p.checkUnused(pragma) - } - - names := p.declNames(ir.OLITERAL, decl.NameList) - typ := p.typeExprOrNil(decl.Type) - - var values []ir.Node - if decl.Values != nil { - values = p.exprList(decl.Values) - cs.typ, cs.values = typ, values - } else { - if typ != nil { - base.Errorf("const declaration cannot have type without expression") - } - typ, values = cs.typ, cs.values - } - - nn := make([]ir.Node, 0, len(names)) - for i, n := range names { - if i >= len(values) { - base.Errorf("missing value in const declaration") - break - } - v := values[i] - if decl.Values == nil { - v = ir.DeepCopy(n.Pos(), v) - } - typecheck.Declare(n, typecheck.DeclContext) - - n.Ntype = typ - n.Defn = v - n.SetIota(cs.iota) - - nn = append(nn, ir.NewDecl(p.pos(decl), ir.ODCLCONST, n)) - } - - if len(values) > len(names) { - base.Errorf("extra expression in const declaration") - } - - cs.iota++ - - return nn -} - -func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node { - n := p.declName(ir.OTYPE, decl.Name) - typecheck.Declare(n, typecheck.DeclContext) - - // decl.Type may be nil but in that case we got a syntax error during parsing - typ := p.typeExprOrNil(decl.Type) - - n.Ntype = typ - n.SetAlias(decl.Alias) - if pragma, ok := decl.Pragma.(*pragmas); ok { - if !decl.Alias { - n.SetPragma(pragma.Flag & typePragmas) - pragma.Flag &^= typePragmas - } - p.checkUnused(pragma) - } - - nod := ir.NewDecl(p.pos(decl), ir.ODCLTYPE, n) - if n.Alias() && !types.AllowsGoVersion(types.LocalPkg, 1, 9) { - base.ErrorfAt(nod.Pos(), "type aliases only supported as of -lang=go1.9") - } - return nod -} - -func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name { - nodes := make([]*ir.Name, 0, len(names)) - for _, name := range names { - nodes = append(nodes, p.declName(op, name)) - } - return nodes -} - -func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name { - return ir.NewDeclNameAt(p.pos(name), op, p.name(name)) -} - -func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { - name := p.name(fun.Name) - t := p.signature(fun.Recv, fun.Type) - f := ir.NewFunc(p.pos(fun)) - - if fun.Recv == nil { - if name.Name == "init" { - name = renameinit() - if len(t.Params) > 0 || len(t.Results) > 0 { - base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values") - } - typecheck.Target.Inits = append(typecheck.Target.Inits, f) - } - - if types.LocalPkg.Name == "main" && name.Name == "main" { - if len(t.Params) > 0 || len(t.Results) > 0 { - base.ErrorfAt(f.Pos(), "func main must have no arguments and no return values") - } - } - } else { - f.Shortname = name - name = ir.BlankNode.Sym() // filled in by tcFunc - } - - f.Nname = ir.NewNameAt(p.pos(fun.Name), name) - f.Nname.Func = f - f.Nname.Defn = f - f.Nname.Ntype = t - - if pragma, ok := fun.Pragma.(*pragmas); ok { - f.Pragma = pragma.Flag & funcPragmas - if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 { - base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined") - } - pragma.Flag &^= funcPragmas - p.checkUnused(pragma) - } - - if fun.Recv == nil { - typecheck.Declare(f.Nname, ir.PFUNC) - } - - p.funcBody(f, fun.Body) - - if fun.Body != nil { - if f.Pragma&ir.Noescape != 0 { - base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations") - } - } else { - if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") { - // Linknamed functions are allowed to have no body. Hopefully - // the linkname target has a body. See issue 23311. - isLinknamed := false - for _, n := range p.linknames { - if ir.FuncName(f) == n.local { - isLinknamed = true - break - } - } - if !isLinknamed { - base.ErrorfAt(f.Pos(), "missing function body") - } - } - } - - return f -} - -func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *ir.FuncType { - var rcvr *ir.Field - if recv != nil { - rcvr = p.param(recv, false, false) - } - return ir.NewFuncType(p.pos(typ), rcvr, - p.params(typ.ParamList, true), - p.params(typ.ResultList, false)) -} - -func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field { - nodes := make([]*ir.Field, 0, len(params)) - for i, param := range params { - p.setlineno(param) - nodes = append(nodes, p.param(param, dddOk, i+1 == len(params))) - } - return nodes -} - -func (p *noder) param(param *syntax.Field, dddOk, final bool) *ir.Field { - var name *types.Sym - if param.Name != nil { - name = p.name(param.Name) - } - - typ := p.typeExpr(param.Type) - n := ir.NewField(p.pos(param), name, typ, nil) - - // rewrite ...T parameter - if typ, ok := typ.(*ir.SliceType); ok && typ.DDD { - if !dddOk { - // We mark these as syntax errors to get automatic elimination - // of multiple such errors per line (see ErrorfAt in subr.go). - base.Errorf("syntax error: cannot use ... in receiver or result parameter list") - } else if !final { - if param.Name == nil { - base.Errorf("syntax error: cannot use ... with non-final parameter") - } else { - p.errorAt(param.Name.Pos(), "syntax error: cannot use ... with non-final parameter %s", param.Name.Value) - } - } - typ.DDD = false - n.IsDDD = true - } - - return n -} - -func (p *noder) exprList(expr syntax.Expr) []ir.Node { - switch expr := expr.(type) { - case nil: - return nil - case *syntax.ListExpr: - return p.exprs(expr.ElemList) - default: - return []ir.Node{p.expr(expr)} - } -} - -func (p *noder) exprs(exprs []syntax.Expr) []ir.Node { - nodes := make([]ir.Node, 0, len(exprs)) - for _, expr := range exprs { - nodes = append(nodes, p.expr(expr)) - } - return nodes -} - -func (p *noder) expr(expr syntax.Expr) ir.Node { - p.setlineno(expr) - switch expr := expr.(type) { - case nil, *syntax.BadExpr: - return nil - case *syntax.Name: - return p.mkname(expr) - case *syntax.BasicLit: - n := ir.NewBasicLit(p.pos(expr), p.basicLit(expr)) - if expr.Kind == syntax.RuneLit { - n.SetType(types.UntypedRune) - } - n.SetDiag(expr.Bad || n.Val().Kind() == constant.Unknown) // avoid follow-on errors if there was a syntax error - return n - case *syntax.CompositeLit: - n := ir.NewCompLitExpr(p.pos(expr), ir.OCOMPLIT, p.typeExpr(expr.Type), nil) - l := p.exprs(expr.ElemList) - for i, e := range l { - l[i] = p.wrapname(expr.ElemList[i], e) - } - n.List = l - base.Pos = p.makeXPos(expr.Rbrace) - return n - case *syntax.KeyValueExpr: - // use position of expr.Key rather than of expr (which has position of ':') - return ir.NewKeyExpr(p.pos(expr.Key), p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value))) - case *syntax.FuncLit: - return p.funcLit(expr) - case *syntax.ParenExpr: - return ir.NewParenExpr(p.pos(expr), p.expr(expr.X)) - case *syntax.SelectorExpr: - // parser.new_dotname - obj := p.expr(expr.X) - if obj.Op() == ir.OPACK { - pack := obj.(*ir.PkgName) - pack.Used = true - return importName(pack.Pkg.Lookup(expr.Sel.Value)) - } - n := ir.NewSelectorExpr(base.Pos, ir.OXDOT, obj, p.name(expr.Sel)) - n.SetPos(p.pos(expr)) // lineno may have been changed by p.expr(expr.X) - return n - case *syntax.IndexExpr: - return ir.NewIndexExpr(p.pos(expr), p.expr(expr.X), p.expr(expr.Index)) - case *syntax.SliceExpr: - op := ir.OSLICE - if expr.Full { - op = ir.OSLICE3 - } - x := p.expr(expr.X) - var index [3]ir.Node - for i, n := range &expr.Index { - if n != nil { - index[i] = p.expr(n) - } - } - return ir.NewSliceExpr(p.pos(expr), op, x, index[0], index[1], index[2]) - case *syntax.AssertExpr: - return ir.NewTypeAssertExpr(p.pos(expr), p.expr(expr.X), p.typeExpr(expr.Type)) - case *syntax.Operation: - if expr.Op == syntax.Add && expr.Y != nil { - return p.sum(expr) - } - x := p.expr(expr.X) - if expr.Y == nil { - pos, op := p.pos(expr), p.unOp(expr.Op) - switch op { - case ir.OADDR: - return typecheck.NodAddrAt(pos, x) - case ir.ODEREF: - return ir.NewStarExpr(pos, x) - } - return ir.NewUnaryExpr(pos, op, x) - } - - pos, op, y := p.pos(expr), p.binOp(expr.Op), p.expr(expr.Y) - switch op { - case ir.OANDAND, ir.OOROR: - return ir.NewLogicalExpr(pos, op, x, y) - } - return ir.NewBinaryExpr(pos, op, x, y) - case *syntax.CallExpr: - n := ir.NewCallExpr(p.pos(expr), ir.OCALL, p.expr(expr.Fun), p.exprs(expr.ArgList)) - n.IsDDD = expr.HasDots - return n - - case *syntax.ArrayType: - var len ir.Node - if expr.Len != nil { - len = p.expr(expr.Len) - } - return ir.NewArrayType(p.pos(expr), len, p.typeExpr(expr.Elem)) - case *syntax.SliceType: - return ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) - case *syntax.DotsType: - t := ir.NewSliceType(p.pos(expr), p.typeExpr(expr.Elem)) - t.DDD = true - return t - case *syntax.StructType: - return p.structType(expr) - case *syntax.InterfaceType: - return p.interfaceType(expr) - case *syntax.FuncType: - return p.signature(nil, expr) - case *syntax.MapType: - return ir.NewMapType(p.pos(expr), - p.typeExpr(expr.Key), p.typeExpr(expr.Value)) - case *syntax.ChanType: - return ir.NewChanType(p.pos(expr), - p.typeExpr(expr.Elem), p.chanDir(expr.Dir)) - - case *syntax.TypeSwitchGuard: - var tag *ir.Ident - if expr.Lhs != nil { - tag = ir.NewIdent(p.pos(expr.Lhs), p.name(expr.Lhs)) - if ir.IsBlank(tag) { - base.Errorf("invalid variable name %v in type switch", tag) - } - } - return ir.NewTypeSwitchGuard(p.pos(expr), tag, p.expr(expr.X)) - } - panic("unhandled Expr") -} - -// sum efficiently handles very large summation expressions (such as -// in issue #16394). In particular, it avoids left recursion and -// collapses string literals. -func (p *noder) sum(x syntax.Expr) ir.Node { - // While we need to handle long sums with asymptotic - // efficiency, the vast majority of sums are very small: ~95% - // have only 2 or 3 operands, and ~99% of string literals are - // never concatenated. - - adds := make([]*syntax.Operation, 0, 2) - for { - add, ok := x.(*syntax.Operation) - if !ok || add.Op != syntax.Add || add.Y == nil { - break - } - adds = append(adds, add) - x = add.X - } - - // nstr is the current rightmost string literal in the - // summation (if any), and chunks holds its accumulated - // substrings. - // - // Consider the expression x + "a" + "b" + "c" + y. When we - // reach the string literal "a", we assign nstr to point to - // its corresponding Node and initialize chunks to {"a"}. - // Visiting the subsequent string literals "b" and "c", we - // simply append their values to chunks. Finally, when we - // reach the non-constant operand y, we'll join chunks to form - // "abc" and reassign the "a" string literal's value. - // - // N.B., we need to be careful about named string constants - // (indicated by Sym != nil) because 1) we can't modify their - // value, as doing so would affect other uses of the string - // constant, and 2) they may have types, which we need to - // handle correctly. For now, we avoid these problems by - // treating named string constants the same as non-constant - // operands. - var nstr ir.Node - chunks := make([]string, 0, 1) - - n := p.expr(x) - if ir.IsConst(n, constant.String) && n.Sym() == nil { - nstr = n - chunks = append(chunks, ir.StringVal(nstr)) - } - - for i := len(adds) - 1; i >= 0; i-- { - add := adds[i] - - r := p.expr(add.Y) - if ir.IsConst(r, constant.String) && r.Sym() == nil { - if nstr != nil { - // Collapse r into nstr instead of adding to n. - chunks = append(chunks, ir.StringVal(r)) - continue - } - - nstr = r - chunks = append(chunks, ir.StringVal(nstr)) - } else { - if len(chunks) > 1 { - nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) - } - nstr = nil - chunks = chunks[:0] - } - n = ir.NewBinaryExpr(p.pos(add), ir.OADD, n, r) - } - if len(chunks) > 1 { - nstr.SetVal(constant.MakeString(strings.Join(chunks, ""))) - } - - return n -} - -func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype { - // TODO(mdempsky): Be stricter? typecheck should handle errors anyway. - n := p.expr(typ) - if n == nil { - return nil - } - return n.(ir.Ntype) -} - -func (p *noder) typeExprOrNil(typ syntax.Expr) ir.Ntype { - if typ != nil { - return p.typeExpr(typ) - } - return nil -} - -func (p *noder) chanDir(dir syntax.ChanDir) types.ChanDir { - switch dir { - case 0: - return types.Cboth - case syntax.SendOnly: - return types.Csend - case syntax.RecvOnly: - return types.Crecv - } - panic("unhandled ChanDir") -} - -func (p *noder) structType(expr *syntax.StructType) ir.Node { - l := make([]*ir.Field, 0, len(expr.FieldList)) - for i, field := range expr.FieldList { - p.setlineno(field) - var n *ir.Field - if field.Name == nil { - n = p.embedded(field.Type) - } else { - n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil) - } - if i < len(expr.TagList) && expr.TagList[i] != nil { - n.Note = constant.StringVal(p.basicLit(expr.TagList[i])) - } - l = append(l, n) - } - - p.setlineno(expr) - return ir.NewStructType(p.pos(expr), l) -} - -func (p *noder) interfaceType(expr *syntax.InterfaceType) ir.Node { - l := make([]*ir.Field, 0, len(expr.MethodList)) - for _, method := range expr.MethodList { - p.setlineno(method) - var n *ir.Field - if method.Name == nil { - n = ir.NewField(p.pos(method), nil, importName(p.packname(method.Type)).(ir.Ntype), nil) - } else { - mname := p.name(method.Name) - if mname.IsBlank() { - base.Errorf("methods must have a unique non-blank name") - continue - } - sig := p.typeExpr(method.Type).(*ir.FuncType) - sig.Recv = fakeRecv() - n = ir.NewField(p.pos(method), mname, sig, nil) - } - l = append(l, n) - } - - return ir.NewInterfaceType(p.pos(expr), l) -} - -func (p *noder) packname(expr syntax.Expr) *types.Sym { - switch expr := expr.(type) { - case *syntax.Name: - name := p.name(expr) - if n := oldname(name); n.Name() != nil && n.Name().PkgName != nil { - n.Name().PkgName.Used = true - } - return name - case *syntax.SelectorExpr: - name := p.name(expr.X.(*syntax.Name)) - def := ir.AsNode(name.Def) - if def == nil { - base.Errorf("undefined: %v", name) - return name - } - var pkg *types.Pkg - if def.Op() != ir.OPACK { - base.Errorf("%v is not a package", name) - pkg = types.LocalPkg - } else { - def := def.(*ir.PkgName) - def.Used = true - pkg = def.Pkg - } - return pkg.Lookup(expr.Sel.Value) - } - panic(fmt.Sprintf("unexpected packname: %#v", expr)) -} - -func (p *noder) embedded(typ syntax.Expr) *ir.Field { - op, isStar := typ.(*syntax.Operation) - if isStar { - if op.Op != syntax.Mul || op.Y != nil { - panic("unexpected Operation") - } - typ = op.X - } - - sym := p.packname(typ) - n := ir.NewField(p.pos(typ), typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil) - n.Embedded = true - - if isStar { - n.Ntype = ir.NewStarExpr(p.pos(op), n.Ntype) - } - return n -} - -func (p *noder) stmts(stmts []syntax.Stmt) []ir.Node { - return p.stmtsFall(stmts, false) -} - -func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []ir.Node { - var nodes []ir.Node - for i, stmt := range stmts { - s := p.stmtFall(stmt, fallOK && i+1 == len(stmts)) - if s == nil { - } else if s.Op() == ir.OBLOCK && len(s.(*ir.BlockStmt).List) > 0 { - // Inline non-empty block. - // Empty blocks must be preserved for CheckReturn. - nodes = append(nodes, s.(*ir.BlockStmt).List...) - } else { - nodes = append(nodes, s) - } - } - return nodes -} - -func (p *noder) stmt(stmt syntax.Stmt) ir.Node { - return p.stmtFall(stmt, false) -} - -func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node { - p.setlineno(stmt) - switch stmt := stmt.(type) { - case nil, *syntax.EmptyStmt: - return nil - case *syntax.LabeledStmt: - return p.labeledStmt(stmt, fallOK) - case *syntax.BlockStmt: - l := p.blockStmt(stmt) - if len(l) == 0 { - // TODO(mdempsky): Line number? - return ir.NewBlockStmt(base.Pos, nil) - } - return ir.NewBlockStmt(src.NoXPos, l) - case *syntax.ExprStmt: - return p.wrapname(stmt, p.expr(stmt.X)) - case *syntax.SendStmt: - return ir.NewSendStmt(p.pos(stmt), p.expr(stmt.Chan), p.expr(stmt.Value)) - case *syntax.DeclStmt: - return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList)) - case *syntax.AssignStmt: - if stmt.Rhs == nil { - pos := p.pos(stmt) - n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one)) - n.IncDec = true - return n - } - - if stmt.Op != 0 && stmt.Op != syntax.Def { - n := ir.NewAssignOpStmt(p.pos(stmt), p.binOp(stmt.Op), p.expr(stmt.Lhs), p.expr(stmt.Rhs)) - return n - } - - rhs := p.exprList(stmt.Rhs) - if list, ok := stmt.Lhs.(*syntax.ListExpr); ok && len(list.ElemList) != 1 || len(rhs) != 1 { - n := ir.NewAssignListStmt(p.pos(stmt), ir.OAS2, nil, nil) - n.Def = stmt.Op == syntax.Def - n.Lhs = p.assignList(stmt.Lhs, n, n.Def) - n.Rhs = rhs - return n - } - - n := ir.NewAssignStmt(p.pos(stmt), nil, nil) - n.Def = stmt.Op == syntax.Def - n.X = p.assignList(stmt.Lhs, n, n.Def)[0] - n.Y = rhs[0] - return n - - case *syntax.BranchStmt: - var op ir.Op - switch stmt.Tok { - case syntax.Break: - op = ir.OBREAK - case syntax.Continue: - op = ir.OCONTINUE - case syntax.Fallthrough: - if !fallOK { - base.Errorf("fallthrough statement out of place") - } - op = ir.OFALL - case syntax.Goto: - op = ir.OGOTO - default: - panic("unhandled BranchStmt") - } - var sym *types.Sym - if stmt.Label != nil { - sym = p.name(stmt.Label) - } - return ir.NewBranchStmt(p.pos(stmt), op, sym) - case *syntax.CallStmt: - var op ir.Op - switch stmt.Tok { - case syntax.Defer: - op = ir.ODEFER - case syntax.Go: - op = ir.OGO - default: - panic("unhandled CallStmt") - } - return ir.NewGoDeferStmt(p.pos(stmt), op, p.expr(stmt.Call)) - case *syntax.ReturnStmt: - n := ir.NewReturnStmt(p.pos(stmt), p.exprList(stmt.Results)) - if len(n.Results) == 0 && ir.CurFunc != nil { - for _, ln := range ir.CurFunc.Dcl { - if ln.Class == ir.PPARAM { - continue - } - if ln.Class != ir.PPARAMOUT { - break - } - if ln.Sym().Def != ln { - base.Errorf("%s is shadowed during return", ln.Sym().Name) - } - } - } - return n - case *syntax.IfStmt: - return p.ifStmt(stmt) - case *syntax.ForStmt: - return p.forStmt(stmt) - case *syntax.SwitchStmt: - return p.switchStmt(stmt) - case *syntax.SelectStmt: - return p.selectStmt(stmt) - } - panic("unhandled Stmt") -} - -func (p *noder) assignList(expr syntax.Expr, defn ir.InitNode, colas bool) []ir.Node { - if !colas { - return p.exprList(expr) - } - - var exprs []syntax.Expr - if list, ok := expr.(*syntax.ListExpr); ok { - exprs = list.ElemList - } else { - exprs = []syntax.Expr{expr} - } - - res := make([]ir.Node, len(exprs)) - seen := make(map[*types.Sym]bool, len(exprs)) - - newOrErr := false - for i, expr := range exprs { - p.setlineno(expr) - res[i] = ir.BlankNode - - name, ok := expr.(*syntax.Name) - if !ok { - p.errorAt(expr.Pos(), "non-name %v on left side of :=", p.expr(expr)) - newOrErr = true - continue - } - - sym := p.name(name) - if sym.IsBlank() { - continue - } - - if seen[sym] { - p.errorAt(expr.Pos(), "%v repeated on left side of :=", sym) - newOrErr = true - continue - } - seen[sym] = true - - if sym.Block == types.Block { - res[i] = oldname(sym) - continue - } - - newOrErr = true - n := typecheck.NewName(sym) - typecheck.Declare(n, typecheck.DeclContext) - n.Defn = defn - defn.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n)) - res[i] = n - } - - if !newOrErr { - base.ErrorfAt(defn.Pos(), "no new variables on left side of :=") - } - return res -} - -func (p *noder) blockStmt(stmt *syntax.BlockStmt) []ir.Node { - p.openScope(stmt.Pos()) - nodes := p.stmts(stmt.List) - p.closeScope(stmt.Rbrace) - return nodes -} - -func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node { - p.openScope(stmt.Pos()) - init := p.stmt(stmt.Init) - n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil) - if init != nil { - *n.PtrInit() = []ir.Node{init} - } - if stmt.Else != nil { - e := p.stmt(stmt.Else) - if e.Op() == ir.OBLOCK { - e := e.(*ir.BlockStmt) - n.Else = e.List - } else { - n.Else = []ir.Node{e} - } - } - p.closeAnotherScope() - return n -} - -func (p *noder) forStmt(stmt *syntax.ForStmt) ir.Node { - p.openScope(stmt.Pos()) - if r, ok := stmt.Init.(*syntax.RangeClause); ok { - if stmt.Cond != nil || stmt.Post != nil { - panic("unexpected RangeClause") - } - - n := ir.NewRangeStmt(p.pos(r), nil, nil, p.expr(r.X), nil) - if r.Lhs != nil { - n.Def = r.Def - lhs := p.assignList(r.Lhs, n, n.Def) - n.Key = lhs[0] - if len(lhs) > 1 { - n.Value = lhs[1] - } - } - n.Body = p.blockStmt(stmt.Body) - p.closeAnotherScope() - return n - } - - n := ir.NewForStmt(p.pos(stmt), p.stmt(stmt.Init), p.expr(stmt.Cond), p.stmt(stmt.Post), p.blockStmt(stmt.Body)) - p.closeAnotherScope() - return n -} - -func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node { - p.openScope(stmt.Pos()) - - init := p.stmt(stmt.Init) - n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil) - if init != nil { - *n.PtrInit() = []ir.Node{init} - } - - var tswitch *ir.TypeSwitchGuard - if l := n.Tag; l != nil && l.Op() == ir.OTYPESW { - tswitch = l.(*ir.TypeSwitchGuard) - } - n.Cases = p.caseClauses(stmt.Body, tswitch, stmt.Rbrace) - - p.closeScope(stmt.Rbrace) - return n -} - -func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *ir.TypeSwitchGuard, rbrace syntax.Pos) []*ir.CaseClause { - nodes := make([]*ir.CaseClause, 0, len(clauses)) - for i, clause := range clauses { - p.setlineno(clause) - if i > 0 { - p.closeScope(clause.Pos()) - } - p.openScope(clause.Pos()) - - n := ir.NewCaseStmt(p.pos(clause), p.exprList(clause.Cases), nil) - if tswitch != nil && tswitch.Tag != nil { - nn := typecheck.NewName(tswitch.Tag.Sym()) - typecheck.Declare(nn, typecheck.DeclContext) - n.Var = nn - // keep track of the instances for reporting unused - nn.Defn = tswitch - } - - // Trim trailing empty statements. We omit them from - // the Node AST anyway, and it's easier to identify - // out-of-place fallthrough statements without them. - body := clause.Body - for len(body) > 0 { - if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok { - break - } - body = body[:len(body)-1] - } - - n.Body = p.stmtsFall(body, true) - if l := len(n.Body); l > 0 && n.Body[l-1].Op() == ir.OFALL { - if tswitch != nil { - base.Errorf("cannot fallthrough in type switch") - } - if i+1 == len(clauses) { - base.Errorf("cannot fallthrough final case in switch") - } - } - - nodes = append(nodes, n) - } - if len(clauses) > 0 { - p.closeScope(rbrace) - } - return nodes -} - -func (p *noder) selectStmt(stmt *syntax.SelectStmt) ir.Node { - return ir.NewSelectStmt(p.pos(stmt), p.commClauses(stmt.Body, stmt.Rbrace)) -} - -func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace syntax.Pos) []*ir.CommClause { - nodes := make([]*ir.CommClause, len(clauses)) - for i, clause := range clauses { - p.setlineno(clause) - if i > 0 { - p.closeScope(clause.Pos()) - } - p.openScope(clause.Pos()) - - nodes[i] = ir.NewCommStmt(p.pos(clause), p.stmt(clause.Comm), p.stmts(clause.Body)) - } - if len(clauses) > 0 { - p.closeScope(rbrace) - } - return nodes -} - -func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) ir.Node { - sym := p.name(label.Label) - lhs := ir.NewLabelStmt(p.pos(label), sym) - - var ls ir.Node - if label.Stmt != nil { // TODO(mdempsky): Should always be present. - ls = p.stmtFall(label.Stmt, fallOK) - // Attach label directly to control statement too. - if ls != nil { - switch ls.Op() { - case ir.OFOR: - ls := ls.(*ir.ForStmt) - ls.Label = sym - case ir.ORANGE: - ls := ls.(*ir.RangeStmt) - ls.Label = sym - case ir.OSWITCH: - ls := ls.(*ir.SwitchStmt) - ls.Label = sym - case ir.OSELECT: - ls := ls.(*ir.SelectStmt) - ls.Label = sym - } - } - } - - l := []ir.Node{lhs} - if ls != nil { - if ls.Op() == ir.OBLOCK { - ls := ls.(*ir.BlockStmt) - l = append(l, ls.List...) - } else { - l = append(l, ls) - } - } - return ir.NewBlockStmt(src.NoXPos, l) -} - var unOps = [...]ir.Op{ syntax.Recv: ir.ORECV, syntax.Mul: ir.ODEREF, @@ -1375,13 +155,6 @@ var unOps = [...]ir.Op{ syntax.Sub: ir.ONEG, } -func (p *noder) unOp(op syntax.Operator) ir.Op { - if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 { - panic("invalid Operator") - } - return unOps[op] -} - var binOps = [...]ir.Op{ syntax.OrOr: ir.OOROR, syntax.AndAnd: ir.OANDAND, @@ -1407,97 +180,7 @@ var binOps = [...]ir.Op{ syntax.Shr: ir.ORSH, } -func (p *noder) binOp(op syntax.Operator) ir.Op { - if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 { - panic("invalid Operator") - } - return binOps[op] -} - -// checkLangCompat reports an error if the representation of a numeric -// literal is not compatible with the current language version. -func checkLangCompat(lit *syntax.BasicLit) { - s := lit.Value - if len(s) <= 2 || types.AllowsGoVersion(types.LocalPkg, 1, 13) { - return - } - // len(s) > 2 - if strings.Contains(s, "_") { - base.ErrorfVers("go1.13", "underscores in numeric literals") - return - } - if s[0] != '0' { - return - } - radix := s[1] - if radix == 'b' || radix == 'B' { - base.ErrorfVers("go1.13", "binary literals") - return - } - if radix == 'o' || radix == 'O' { - base.ErrorfVers("go1.13", "0o/0O-style octal literals") - return - } - if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') { - base.ErrorfVers("go1.13", "hexadecimal floating-point literals") - } -} - -func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value { - // We don't use the errors of the conversion routines to determine - // if a literal string is valid because the conversion routines may - // accept a wider syntax than the language permits. Rely on lit.Bad - // instead. - if lit.Bad { - return constant.MakeUnknown() - } - - switch lit.Kind { - case syntax.IntLit, syntax.FloatLit, syntax.ImagLit: - checkLangCompat(lit) - // The max. mantissa precision for untyped numeric values - // is 512 bits, or 4048 bits for each of the two integer - // parts of a fraction for floating-point numbers that are - // represented accurately in the go/constant package. - // Constant literals that are longer than this many bits - // are not meaningful; and excessively long constants may - // consume a lot of space and time for a useless conversion. - // Cap constant length with a generous upper limit that also - // allows for separators between all digits. - const limit = 10000 - if len(lit.Value) > limit { - p.errorAt(lit.Pos(), "excessively long constant: %s... (%d chars)", lit.Value[:10], len(lit.Value)) - return constant.MakeUnknown() - } - } - - v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0) - if v.Kind() == constant.Unknown { - // TODO(mdempsky): Better error message? - p.errorAt(lit.Pos(), "malformed constant: %s", lit.Value) - } - - return v -} - -var tokenForLitKind = [...]token.Token{ - syntax.IntLit: token.INT, - syntax.RuneLit: token.CHAR, - syntax.FloatLit: token.FLOAT, - syntax.ImagLit: token.IMAG, - syntax.StringLit: token.STRING, -} - -func (p *noder) name(name *syntax.Name) *types.Sym { - return typecheck.Lookup(name.Value) -} - -func (p *noder) mkname(name *syntax.Name) ir.Node { - // TODO(mdempsky): Set line number? - return mkname(p.name(name)) -} - -func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { +func wrapname(pos src.XPos, x ir.Node) ir.Node { // These nodes do not carry line numbers. // Introduce a wrapper node to give them the correct line. switch x.Op() { @@ -1506,20 +189,14 @@ func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { break } fallthrough - case ir.ONAME, ir.ONONAME, ir.OPACK: - p := ir.NewParenExpr(p.pos(n), x) + case ir.ONAME, ir.ONONAME: + p := ir.NewParenExpr(pos, x) p.SetImplicit(true) return p } return x } -func (p *noder) setlineno(n syntax.Node) { - if n != nil { - base.Pos = p.pos(n) - } -} - // error is called concurrently if files are parsed concurrently. func (p *noder) error(err error) { p.err <- err.(syntax.Error) @@ -1555,19 +232,6 @@ type pragmaEmbed struct { Patterns []string } -func (p *noder) checkUnused(pragma *pragmas) { - for _, pos := range pragma.Pos { - if pos.Flag&pragma.Flag != 0 { - p.errorAt(pos.Pos, "misplaced compiler directive") - } - } - if len(pragma.Embeds) > 0 { - for _, e := range pragma.Embeds { - p.errorAt(e.Pos, "misplaced go:embed directive") - } - } -} - func (p *noder) checkUnusedDuringParse(pragma *pragmas) { for _, pos := range pragma.Pos { if pos.Flag&pragma.Flag != 0 { @@ -1674,6 +338,9 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) } + if flag == ir.UintptrKeepAlive && !base.Flag.Std { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)}) + } if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) } @@ -1691,7 +358,7 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P // (primarily misuse of linker flags), other files are not. // See golang.org/issue/23672. func isCgoGeneratedFile(pos syntax.Pos) bool { - return strings.HasPrefix(filepath.Base(filepath.Clean(fileh(pos.Base().Filename()))), "_cgo_") + return strings.HasPrefix(filepath.Base(trimFilename(pos.Base())), "_cgo_") } // safeArg reports whether arg is a "safe" command-line argument, @@ -1706,14 +373,6 @@ func safeArg(name string) bool { return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf } -func mkname(sym *types.Sym) ir.Node { - n := oldname(sym) - if n.Name() != nil && n.Name().PkgName != nil { - n.Name().PkgName.Used = true - } - return n -} - // parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. // It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. // go/build/read.go also processes these strings and contains similar logic. @@ -1775,99 +434,27 @@ func parseGoEmbed(args string) ([]string, error) { return list, nil } -func fakeRecv() *ir.Field { - return ir.NewField(base.Pos, nil, nil, types.FakeRecvType()) -} - -func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node { - xtype := p.typeExpr(expr.Type) - - fn := ir.NewFunc(p.pos(expr)) - fn.SetIsHiddenClosure(ir.CurFunc != nil) - - fn.Nname = ir.NewNameAt(p.pos(expr), ir.BlankNode.Sym()) // filled in by tcClosure - fn.Nname.Func = fn - fn.Nname.Ntype = xtype - fn.Nname.Defn = fn - - clo := ir.NewClosureExpr(p.pos(expr), fn) - fn.OClosure = clo - - p.funcBody(fn, expr.Body) - - ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn) - - return clo -} - // A function named init is a special case. // It is called by the initialization before main is run. // To make it unique within a package and also uncallable, // the name, normally "pkg.init", is altered to "pkg.init.0". var renameinitgen int -func renameinit() *types.Sym { +func Renameinit() *types.Sym { s := typecheck.LookupNum("init.", renameinitgen) renameinitgen++ return s } -// oldname returns the Node that declares symbol s in the current scope. -// If no such Node currently exists, an ONONAME Node is returned instead. -// Automatically creates a new closure variable if the referenced symbol was -// declared in a different (containing) function. -func oldname(s *types.Sym) ir.Node { - if s.Pkg != types.LocalPkg { - return ir.NewIdent(base.Pos, s) - } - - n := ir.AsNode(s.Def) - if n == nil { - // Maybe a top-level declaration will come along later to - // define s. resolve will check s.Def again once all input - // source has been processed. - return ir.NewIdent(base.Pos, s) - } - - if n, ok := n.(*ir.Name); ok { - // 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. - return ir.CaptureName(base.Pos, ir.CurFunc, n) - } - - return n -} - func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) { - if pragma.Embeds == nil { - return - } - pragmaEmbeds := pragma.Embeds pragma.Embeds = nil - pos := makeXPos(pragmaEmbeds[0].Pos) - - if !haveEmbed { - base.ErrorfAt(pos, "go:embed only allowed in Go files that import \"embed\"") - return - } - if len(decl.NameList) > 1 { - base.ErrorfAt(pos, "go:embed cannot apply to multiple vars") - return - } - if decl.Values != nil { - base.ErrorfAt(pos, "go:embed cannot apply to var with initializer") - return - } - if decl.Type == nil { - // Should not happen, since Values == nil now. - base.ErrorfAt(pos, "go:embed cannot apply to var without type") + if len(pragmaEmbeds) == 0 { return } - if typecheck.DeclContext != ir.PEXTERN { - base.ErrorfAt(pos, "go:embed cannot apply to var inside func") + + if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil { + base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err) return } @@ -1878,3 +465,24 @@ func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.Va typecheck.Target.Embeds = append(typecheck.Target.Embeds, name) name.Embed = &embeds } + +func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error { + switch { + case !haveEmbed: + return errors.New("go:embed only allowed in Go files that import \"embed\"") + case len(decl.NameList) > 1: + return errors.New("go:embed cannot apply to multiple vars") + case decl.Values != nil: + return errors.New("go:embed cannot apply to var with initializer") + case decl.Type == nil: + // Should not happen, since Values == nil now. + return errors.New("go:embed cannot apply to var without type") + case withinFunc: + return errors.New("go:embed cannot apply to var inside func") + case !types.AllowsGoVersion(1, 16): + return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang) + + default: + return nil + } +} diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go index 82cce1ace0fcff..3b60760a34db45 100644 --- a/src/cmd/compile/internal/noder/object.go +++ b/src/cmd/compile/internal/noder/object.go @@ -22,20 +22,35 @@ func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) { return g.obj(obj), obj } -// use returns the Name node associated with the use of name. The returned node -// will have the correct type and be marked as typechecked. -func (g *irgen) use(name *syntax.Name) *ir.Name { +// use returns the Name or InstExpr node associated with the use of name, +// possibly instantiated by type arguments. The returned node will have +// the correct type and be marked as typechecked. +func (g *irgen) use(name *syntax.Name) ir.Node { obj2, ok := g.info.Uses[name] if !ok { base.FatalfAt(g.pos(name), "unknown name %v", name) } - obj := ir.CaptureName(g.pos(obj2), ir.CurFunc, g.obj(obj2)) + obj := ir.CaptureName(g.pos(name), ir.CurFunc, g.obj(obj2)) if obj.Defn != nil && obj.Defn.Op() == ir.ONAME { // If CaptureName created a closure variable, then transfer the // type of the captured name to the new closure variable. obj.SetTypecheck(1) obj.SetType(obj.Defn.Type()) } + + if obj.Class == ir.PFUNC { + if inst, ok := g.info.Instances[name]; ok { + // This is the case where inferring types required the + // types of the function arguments. + targs := make([]ir.Ntype, inst.TypeArgs.Len()) + for i := range targs { + targs[i] = ir.TypeNode(g.typ(inst.TypeArgs.At(i))) + } + typ := g.substType(obj.Type(), obj.Type().TParams(), targs) + return typed(typ, ir.NewInstExpr(g.pos(name), ir.OFUNCINST, obj, targs)) + } + } + return obj } @@ -49,6 +64,11 @@ func (g *irgen) obj(obj types2.Object) *ir.Name { // For imported objects, we use iimport directly instead of mapping // the types2 representation. if obj.Pkg() != g.self { + if sig, ok := obj.Type().(*types2.Signature); ok && sig.Recv() != nil { + // We can't import a method by name - must import the type + // and access the method from it. + base.FatalfAt(g.pos(obj), "tried to import a method directly") + } sym := g.sym(obj) if sym.Def != nil { return sym.Def.(*ir.Name) @@ -84,7 +104,7 @@ func (g *irgen) obj(obj types2.Object) *ir.Name { var typ *types.Type if recv := sig.Recv(); recv == nil { if obj.Name() == "init" { - sym = renameinit() + sym = Renameinit() } else { sym = g.sym(obj) } @@ -101,25 +121,28 @@ func (g *irgen) obj(obj types2.Object) *ir.Name { case *types2.TypeName: if obj.IsAlias() { name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type())) + name.SetAlias(true) } else { name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj)) g.objFinish(name, class, types.NewNamed(name)) } case *types2.Var: - var sym *types.Sym - if class == ir.PPARAMOUT { + sym := g.sym(obj) + if class == ir.PPARAMOUT && (sym == nil || sym.IsBlank()) { // Backend needs names for result parameters, // even if they're anonymous or blank. - switch obj.Name() { - case "": - sym = typecheck.LookupNum("~r", len(ir.CurFunc.Dcl)) // 'r' for "result" - case "_": - sym = typecheck.LookupNum("~b", len(ir.CurFunc.Dcl)) // 'b' for "blank" + nresults := 0 + for _, n := range ir.CurFunc.Dcl { + if n.Class == ir.PPARAMOUT { + nresults++ + } + } + if sym == nil { + sym = typecheck.LookupNum("~r", nresults) // 'r' for "result" + } else { + sym = typecheck.LookupNum("~b", nresults) // 'b' for "blank" } - } - if sym == nil { - sym = g.sym(obj) } name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type())) @@ -148,7 +171,6 @@ func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) { } name.SetTypecheck(1) - name.SetWalkdef(1) if ir.IsBlank(name) { return @@ -164,9 +186,8 @@ func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) { break // methods are exported with their receiver type } if types.IsExported(sym.Name) { - if name.Class == ir.PFUNC && name.Type().NumTParams() > 0 { - base.FatalfAt(name.Pos(), "Cannot export a generic function (yet): %v", name) - } + // Generic functions can be marked for export here, even + // though they will not be compiled until instantiated. typecheck.Export(name) } if base.Flag.AsmHdr != "" && !name.Sym().Asm() { diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go index a6d3e2d7ef4d9a..6c7e57c9963940 100644 --- a/src/cmd/compile/internal/noder/posmap.go +++ b/src/cmd/compile/internal/noder/posmap.go @@ -26,8 +26,9 @@ func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) } func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) } func (m *posMap) makeXPos(pos syntax.Pos) src.XPos { + // Predeclared objects (e.g., the result parameter for error.Error) + // do not have a position. if !pos.IsKnown() { - // TODO(mdempsky): Investigate restoring base.Fatalf. return src.NoXPos } @@ -45,8 +46,10 @@ func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { b1, ok := m.bases[b0] if !ok { fn := b0.Filename() + absfn := trimFilename(b0) + if b0.IsFileBase() { - b1 = src.NewFileBase(fn, absFilename(fn)) + b1 = src.NewFileBase(fn, absfn) } else { // line directive base p0 := b0.Pos() @@ -55,7 +58,7 @@ func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase { panic("infinite recursion in makeSrcPosBase") } p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col()) - b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col()) + b1 = src.NewLinePragmaBase(p1, fn, absfn, b0.Line(), b0.Col()) } if m.bases == nil { m.bases = make(map[*syntax.PosBase]*src.PosBase) diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go new file mode 100644 index 00000000000000..a22577f9656f96 --- /dev/null +++ b/src/cmd/compile/internal/noder/quirks.go @@ -0,0 +1,79 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "fmt" + + "cmd/compile/internal/syntax" +) + +// typeExprEndPos returns the position that noder would leave base.Pos +// after parsing the given type expression. +// +// Deprecated: This function exists to emulate position semantics from +// Go 1.17, necessary for compatibility with the backend DWARF +// generation logic that assigns variables to their appropriate scope. +func typeExprEndPos(expr0 syntax.Expr) syntax.Pos { + for { + switch expr := expr0.(type) { + case *syntax.Name: + return expr.Pos() + case *syntax.SelectorExpr: + return expr.X.Pos() + + case *syntax.ParenExpr: + expr0 = expr.X + + case *syntax.Operation: + assert(expr.Op == syntax.Mul) + assert(expr.Y == nil) + expr0 = expr.X + + case *syntax.ArrayType: + expr0 = expr.Elem + case *syntax.ChanType: + expr0 = expr.Elem + case *syntax.DotsType: + expr0 = expr.Elem + case *syntax.MapType: + expr0 = expr.Value + case *syntax.SliceType: + expr0 = expr.Elem + + case *syntax.StructType: + return expr.Pos() + + case *syntax.InterfaceType: + expr0 = lastFieldType(expr.MethodList) + if expr0 == nil { + return expr.Pos() + } + + case *syntax.FuncType: + expr0 = lastFieldType(expr.ResultList) + if expr0 == nil { + expr0 = lastFieldType(expr.ParamList) + if expr0 == nil { + return expr.Pos() + } + } + + case *syntax.IndexExpr: // explicit type instantiation + targs := unpackListExpr(expr.Index) + expr0 = targs[len(targs)-1] + + default: + panic(fmt.Sprintf("%s: unexpected type expression %v", expr.Pos(), syntax.String(expr))) + } + } +} + +func lastFieldType(fields []*syntax.Field) syntax.Expr { + if len(fields) == 0 { + return nil + } + return fields[len(fields)-1].Type +} diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go new file mode 100644 index 00000000000000..b8df7c9773424d --- /dev/null +++ b/src/cmd/compile/internal/noder/reader.go @@ -0,0 +1,3980 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "fmt" + "go/constant" + "internal/buildcfg" + "internal/pkgbits" + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/deadcode" + "cmd/compile/internal/dwarfgen" + "cmd/compile/internal/inline" + "cmd/compile/internal/ir" + "cmd/compile/internal/objw" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/staticinit" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" +) + +// This file implements cmd/compile backend's reader for the Unified +// IR export data. + +// A pkgReader reads Unified IR export data. +type pkgReader struct { + pkgbits.PkgDecoder + + // Indices for encoded things; lazily populated as needed. + // + // Note: Objects (i.e., ir.Names) are lazily instantiated by + // populating their types.Sym.Def; see objReader below. + + posBases []*src.PosBase + pkgs []*types.Pkg + typs []*types.Type + + // offset for rewriting the given (absolute!) index into the output, + // but bitwise inverted so we can detect if we're missing the entry + // or not. + newindex []pkgbits.Index +} + +func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader { + return &pkgReader{ + PkgDecoder: pr, + + posBases: make([]*src.PosBase, pr.NumElems(pkgbits.RelocPosBase)), + pkgs: make([]*types.Pkg, pr.NumElems(pkgbits.RelocPkg)), + typs: make([]*types.Type, pr.NumElems(pkgbits.RelocType)), + + newindex: make([]pkgbits.Index, pr.TotalElems()), + } +} + +// A pkgReaderIndex compactly identifies an index (and its +// corresponding dictionary) within a package's export data. +type pkgReaderIndex struct { + pr *pkgReader + idx pkgbits.Index + dict *readerDict + methodSym *types.Sym + + synthetic func(pos src.XPos, r *reader) +} + +func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader { + if pri.synthetic != nil { + return &reader{synthetic: pri.synthetic} + } + + r := pri.pr.newReader(k, pri.idx, marker) + r.dict = pri.dict + r.methodSym = pri.methodSym + return r +} + +func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader { + return &reader{ + Decoder: pr.NewDecoder(k, idx, marker), + p: pr, + } +} + +// A reader provides APIs for reading an individual element. +type reader struct { + pkgbits.Decoder + + p *pkgReader + + dict *readerDict + + // TODO(mdempsky): The state below is all specific to reading + // function bodies. It probably makes sense to split it out + // separately so that it doesn't take up space in every reader + // instance. + + curfn *ir.Func + locals []*ir.Name + closureVars []*ir.Name + + funarghack bool + + // methodSym is the name of method's name, if reading a method. + // It's nil if reading a normal function or closure body. + methodSym *types.Sym + + // dictParam is the .dict param, if any. + dictParam *ir.Name + + // synthetic is a callback function to construct a synthetic + // function body. It's used for creating the bodies of function + // literals used to curry arguments to shaped functions. + synthetic func(pos src.XPos, r *reader) + + // scopeVars is a stack tracking the number of variables declared in + // the current function at the moment each open scope was opened. + scopeVars []int + marker dwarfgen.ScopeMarker + lastCloseScopePos src.XPos + + // === details for handling inline body expansion === + + // If we're reading in a function body because of inlining, this is + // the call that we're inlining for. + inlCaller *ir.Func + inlCall *ir.CallExpr + inlFunc *ir.Func + inlTreeIndex int + inlPosBases map[*src.PosBase]*src.PosBase + + // suppressInlPos tracks whether position base rewriting for + // inlining should be suppressed. See funcLit. + suppressInlPos int + + delayResults bool + + // Label to return to. + retlabel *types.Sym + + // inlvars is the list of variables that the inlinee's arguments are + // assigned to, one for each receiver and normal parameter, in order. + inlvars ir.Nodes + + // retvars is the list of variables that the inlinee's results are + // assigned to, one for each result parameter, in order. + retvars ir.Nodes +} + +// A readerDict represents an instantiated "compile-time dictionary," +// used for resolving any derived types needed for instantiating a +// generic object. +// +// A compile-time dictionary can either be "shaped" or "non-shaped." +// Shaped compile-time dictionaries are only used for instantiating +// shaped type definitions and function bodies, while non-shaped +// compile-time dictionaries are used for instantiating runtime +// dictionaries. +type readerDict struct { + shaped bool // whether this is a shaped dictionary + + // baseSym is the symbol for the object this dictionary belongs to. + // If the object is an instantiated function or defined type, then + // baseSym is the mangled symbol, including any type arguments. + baseSym *types.Sym + + // For non-shaped dictionaries, shapedObj is a reference to the + // corresponding shaped object (always a function or defined type). + shapedObj *ir.Name + + // targs holds the implicit and explicit type arguments in use for + // reading the current object. For example: + // + // func F[T any]() { + // type X[U any] struct { t T; u U } + // var _ X[string] + // } + // + // var _ = F[int] + // + // While instantiating F[int], we need to in turn instantiate + // X[string]. [int] and [string] are explicit type arguments for F + // and X, respectively; but [int] is also the implicit type + // arguments for X. + // + // (As an analogy to function literals, explicits are the function + // literal's formal parameters, while implicits are variables + // captured by the function literal.) + targs []*types.Type + + // implicits counts how many of types within targs are implicit type + // arguments; the rest are explicit. + implicits int + + derived []derivedInfo // reloc index of the derived type's descriptor + derivedTypes []*types.Type // slice of previously computed derived types + + // These slices correspond to entries in the runtime dictionary. + typeParamMethodExprs []readerMethodExprInfo + subdicts []objInfo + rtypes []typeInfo + itabs []itabInfo +} + +type readerMethodExprInfo struct { + typeParamIdx int + method *types.Sym +} + +func setType(n ir.Node, typ *types.Type) { + n.SetType(typ) + n.SetTypecheck(1) +} + +func setValue(name *ir.Name, val constant.Value) { + name.SetVal(val) + name.Defn = nil +} + +// @@@ Positions + +// pos reads a position from the bitstream. +func (r *reader) pos() src.XPos { + return base.Ctxt.PosTable.XPos(r.pos0()) +} + +func (r *reader) pos0() src.Pos { + r.Sync(pkgbits.SyncPos) + if !r.Bool() { + return src.NoPos + } + + posBase := r.posBase() + line := r.Uint() + col := r.Uint() + return src.MakePos(posBase, line, col) +} + +// posBase reads a position base from the bitstream. +func (r *reader) posBase() *src.PosBase { + return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))) +} + +// posBaseIdx returns the specified position base, reading it first if +// needed. +func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *src.PosBase { + if b := pr.posBases[idx]; b != nil { + return b + } + + r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase) + var b *src.PosBase + + absFilename := r.String() + filename := absFilename + + // For build artifact stability, the export data format only + // contains the "absolute" filename as returned by objabi.AbsFile. + // However, some tests (e.g., test/run.go's asmcheck tests) expect + // to see the full, original filename printed out. Re-expanding + // "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to + // satisfy this. + // + // TODO(mdempsky): De-duplicate this logic with similar logic in + // cmd/link/internal/ld's expandGoroot. However, this will probably + // require being more consistent about when we use native vs UNIX + // file paths. + const dollarGOROOT = "$GOROOT" + if buildcfg.GOROOT != "" && strings.HasPrefix(filename, dollarGOROOT) { + filename = buildcfg.GOROOT + filename[len(dollarGOROOT):] + } + + if r.Bool() { + b = src.NewFileBase(filename, absFilename) + } else { + pos := r.pos0() + line := r.Uint() + col := r.Uint() + b = src.NewLinePragmaBase(pos, filename, absFilename, line, col) + } + + pr.posBases[idx] = b + return b +} + +// inlPosBase returns the inlining-adjusted src.PosBase corresponding +// to oldBase, which must be a non-inlined position. When not +// inlining, this is just oldBase. +func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase { + if index := oldBase.InliningIndex(); index >= 0 { + base.Fatalf("oldBase %v already has inlining index %v", oldBase, index) + } + + if r.inlCall == nil || r.suppressInlPos != 0 { + return oldBase + } + + if newBase, ok := r.inlPosBases[oldBase]; ok { + return newBase + } + + newBase := src.NewInliningBase(oldBase, r.inlTreeIndex) + r.inlPosBases[oldBase] = newBase + return newBase +} + +// inlPos returns the inlining-adjusted src.XPos corresponding to +// xpos, which must be a non-inlined position. When not inlining, this +// is just xpos. +func (r *reader) inlPos(xpos src.XPos) src.XPos { + pos := base.Ctxt.PosTable.Pos(xpos) + pos.SetBase(r.inlPosBase(pos.Base())) + return base.Ctxt.PosTable.XPos(pos) +} + +// @@@ Packages + +// pkg reads a package reference from the bitstream. +func (r *reader) pkg() *types.Pkg { + r.Sync(pkgbits.SyncPkg) + return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg)) +} + +// pkgIdx returns the specified package from the export data, reading +// it first if needed. +func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Pkg { + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +// doPkg reads a package definition from the bitstream. +func (r *reader) doPkg() *types.Pkg { + path := r.String() + switch path { + case "": + path = r.p.PkgPath() + case "builtin": + return types.BuiltinPkg + case "unsafe": + return types.UnsafePkg + } + + name := r.String() + + pkg := types.NewPkg(path, "") + + if pkg.Name == "" { + pkg.Name = name + } else { + base.Assertf(pkg.Name == name, "package %q has name %q, but want %q", pkg.Path, pkg.Name, name) + } + + return pkg +} + +// @@@ Types + +func (r *reader) typ() *types.Type { + return r.typWrapped(true) +} + +// typWrapped is like typ, but allows suppressing generation of +// unnecessary wrappers as a compile-time optimization. +func (r *reader) typWrapped(wrapped bool) *types.Type { + return r.p.typIdx(r.typInfo(), r.dict, wrapped) +} + +func (r *reader) typInfo() typeInfo { + r.Sync(pkgbits.SyncType) + if r.Bool() { + return typeInfo{idx: pkgbits.Index(r.Len()), derived: true} + } + return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false} +} + +// typListIdx returns a list of the specified types, resolving derived +// types within the given dictionary. +func (pr *pkgReader) typListIdx(infos []typeInfo, dict *readerDict) []*types.Type { + typs := make([]*types.Type, len(infos)) + for i, info := range infos { + typs[i] = pr.typIdx(info, dict, true) + } + return typs +} + +// typIdx returns the specified type. If info specifies a derived +// type, it's resolved within the given dictionary. If wrapped is +// true, then method wrappers will be generated, if appropriate. +func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *types.Type { + idx := info.idx + var where **types.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // For recursive type declarations involving interfaces and aliases, + // above r.doTyp() call may have already set pr.typs[idx], so just + // double check and return the type. + // + // Example: + // + // type F = func(I) + // + // type I interface { + // m(F) + // } + // + // The writer writes data types in following index order: + // + // 0: func(I) + // 1: I + // 2: interface{m(func(I))} + // + // The reader resolves it in following index order: + // + // 0 -> 1 -> 2 -> 0 -> 1 + // + // and can divide in logically 2 steps: + // + // - 0 -> 1 : first time the reader reach type I, + // it creates new named type with symbol I. + // + // - 2 -> 0 -> 1: the reader ends up reaching symbol I again, + // now the symbol I was setup in above step, so + // the reader just return the named type. + // + // Now, the functions called return, the pr.typs looks like below: + // + // - 0 -> 1 -> 2 -> 0 : [ I ] + // - 0 -> 1 -> 2 : [func(I) I ] + // - 0 -> 1 : [func(I) I interface { "".m(func("".I)) }] + // + // The idx 1, corresponding with type I was resolved successfully + // after r.doTyp() call. + + if prev := *where; prev != nil { + return prev + } + + if wrapped { + // Only cache if we're adding wrappers, so that other callers that + // find a cached type know it was wrapped. + *where = typ + + r.needWrapper(typ) + } + + if !typ.IsUntyped() { + types.CheckSize(typ) + } + + return typ +} + +func (r *reader) doTyp() *types.Type { + switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag { + default: + panic(fmt.Sprintf("unexpected type: %v", tag)) + + case pkgbits.TypeBasic: + return *basics[r.Len()] + + case pkgbits.TypeNamed: + obj := r.obj() + assert(obj.Op() == ir.OTYPE) + return obj.Type() + + case pkgbits.TypeTypeParam: + return r.dict.targs[r.Len()] + + case pkgbits.TypeArray: + len := int64(r.Uint64()) + return types.NewArray(r.typ(), len) + case pkgbits.TypeChan: + dir := dirs[r.Len()] + return types.NewChan(r.typ(), dir) + case pkgbits.TypeMap: + return types.NewMap(r.typ(), r.typ()) + case pkgbits.TypePointer: + return types.NewPtr(r.typ()) + case pkgbits.TypeSignature: + return r.signature(types.LocalPkg, nil) + case pkgbits.TypeSlice: + return types.NewSlice(r.typ()) + case pkgbits.TypeStruct: + return r.structType() + case pkgbits.TypeInterface: + return r.interfaceType() + case pkgbits.TypeUnion: + return r.unionType() + } +} + +func (r *reader) unionType() *types.Type { + terms := make([]*types.Type, r.Len()) + tildes := make([]bool, len(terms)) + for i := range terms { + tildes[i] = r.Bool() + terms[i] = r.typ() + } + return types.NewUnion(terms, tildes) +} + +func (r *reader) interfaceType() *types.Type { + tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. + + nmethods, nembeddeds := r.Len(), r.Len() + implicit := nmethods == 0 && nembeddeds == 1 && r.Bool() + assert(!implicit) // implicit interfaces only appear in constraints + + fields := make([]*types.Field, nmethods+nembeddeds) + methods, embeddeds := fields[:nmethods], fields[nmethods:] + + for i := range methods { + pos := r.pos() + pkg, sym := r.selector() + tpkg = pkg + mtyp := r.signature(pkg, types.FakeRecv()) + methods[i] = types.NewField(pos, sym, mtyp) + } + for i := range embeddeds { + embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ()) + } + + if len(fields) == 0 { + return types.Types[types.TINTER] // empty interface + } + return types.NewInterface(tpkg, fields, false) +} + +func (r *reader) structType() *types.Type { + tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. + fields := make([]*types.Field, r.Len()) + for i := range fields { + pos := r.pos() + pkg, sym := r.selector() + tpkg = pkg + ftyp := r.typ() + tag := r.String() + embedded := r.Bool() + + f := types.NewField(pos, sym, ftyp) + f.Note = tag + if embedded { + f.Embedded = 1 + } + fields[i] = f + } + return types.NewStruct(tpkg, fields) +} + +func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type { + r.Sync(pkgbits.SyncSignature) + + params := r.params(&tpkg) + results := r.params(&tpkg) + if r.Bool() { // variadic + params[len(params)-1].SetIsDDD(true) + } + + return types.NewSignature(tpkg, recv, nil, params, results) +} + +func (r *reader) params(tpkg **types.Pkg) []*types.Field { + r.Sync(pkgbits.SyncParams) + fields := make([]*types.Field, r.Len()) + for i := range fields { + *tpkg, fields[i] = r.param() + } + return fields +} + +func (r *reader) param() (*types.Pkg, *types.Field) { + r.Sync(pkgbits.SyncParam) + + pos := r.pos() + pkg, sym := r.localIdent() + typ := r.typ() + + return pkg, types.NewField(pos, sym, typ) +} + +// @@@ Objects + +// objReader maps qualified identifiers (represented as *types.Sym) to +// a pkgReader and corresponding index that can be used for reading +// that object's definition. +var objReader = map[*types.Sym]pkgReaderIndex{} + +// obj reads an instantiated object reference from the bitstream. +func (r *reader) obj() ir.Node { + return r.p.objInstIdx(r.objInfo(), r.dict, false) +} + +// objInfo reads an instantiated object reference from the bitstream +// and returns the encoded reference to it, without instantiating it. +func (r *reader) objInfo() objInfo { + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) // TODO(mdempsky): Remove; was derived func inst. + idx := r.Reloc(pkgbits.RelocObj) + + explicits := make([]typeInfo, r.Len()) + for i := range explicits { + explicits[i] = r.typInfo() + } + + return objInfo{idx, explicits} +} + +// objInstIdx returns the encoded, instantiated object. If shaped is +// true, then the shaped variant of the object is returned instead. +func (pr *pkgReader) objInstIdx(info objInfo, dict *readerDict, shaped bool) ir.Node { + explicits := pr.typListIdx(info.explicits, dict) + + var implicits []*types.Type + if dict != nil { + implicits = dict.targs + } + + return pr.objIdx(info.idx, implicits, explicits, shaped) +} + +// objIdx returns the specified object, instantiated with the given +// type arguments, if any. If shaped is true, then the shaped variant +// of the object is returned instead. +func (pr *pkgReader) objIdx(idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) ir.Node { + rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) + _, sym := rname.qualifiedIdent() + tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) + + if tag == pkgbits.ObjStub { + assert(!sym.IsBlank()) + switch sym.Pkg { + case types.BuiltinPkg, types.UnsafePkg: + return sym.Def.(ir.Node) + } + if pri, ok := objReader[sym]; ok { + return pri.pr.objIdx(pri.idx, nil, explicits, shaped) + } + base.Fatalf("unresolved stub: %v", sym) + } + + dict := pr.objDictIdx(sym, idx, implicits, explicits, shaped) + + sym = dict.baseSym + if !sym.IsBlank() && sym.Def != nil { + return sym.Def.(*ir.Name) + } + + r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1) + rext := pr.newReader(pkgbits.RelocObjExt, idx, pkgbits.SyncObject1) + + r.dict = dict + rext.dict = dict + + do := func(op ir.Op, hasTParams bool) *ir.Name { + pos := r.pos() + setBasePos(pos) + if hasTParams { + r.typeParamNames() + } + + name := ir.NewDeclNameAt(pos, op, sym) + name.Class = ir.PEXTERN // may be overridden later + if !sym.IsBlank() { + if sym.Def != nil { + base.FatalfAt(name.Pos(), "already have a definition for %v", name) + } + assert(sym.Def == nil) + sym.Def = name + } + return name + } + + switch tag { + default: + panic("unexpected object") + + case pkgbits.ObjAlias: + name := do(ir.OTYPE, false) + setType(name, r.typ()) + name.SetAlias(true) + return name + + case pkgbits.ObjConst: + name := do(ir.OLITERAL, false) + typ := r.typ() + val := FixValue(typ, r.Value()) + setType(name, typ) + setValue(name, val) + return name + + case pkgbits.ObjFunc: + if sym.Name == "init" { + sym = Renameinit() + } + name := do(ir.ONAME, true) + setType(name, r.signature(sym.Pkg, nil)) + + name.Func = ir.NewFunc(r.pos()) + name.Func.Nname = name + + if r.hasTypeParams() { + name.Func.SetDupok(true) + if r.dict.shaped { + setType(name, shapeSig(name.Func, r.dict)) + } else { + todoDicts = append(todoDicts, func() { + r.dict.shapedObj = pr.objIdx(idx, implicits, explicits, true).(*ir.Name) + }) + } + } + + rext.funcExt(name, nil) + return name + + case pkgbits.ObjType: + name := do(ir.OTYPE, true) + typ := types.NewNamed(name) + setType(name, typ) + if r.hasTypeParams() && r.dict.shaped { + typ.SetHasShape(true) + } + + // Important: We need to do this before SetUnderlying. + rext.typeExt(name) + + // We need to defer CheckSize until we've called SetUnderlying to + // handle recursive types. + types.DeferCheckSize() + typ.SetUnderlying(r.typWrapped(false)) + types.ResumeCheckSize() + + if r.hasTypeParams() && !r.dict.shaped { + todoDicts = append(todoDicts, func() { + r.dict.shapedObj = pr.objIdx(idx, implicits, explicits, true).(*ir.Name) + }) + } + + methods := make([]*types.Field, r.Len()) + for i := range methods { + methods[i] = r.method(rext) + } + if len(methods) != 0 { + typ.Methods().Set(methods) + } + + if !r.dict.shaped { + r.needWrapper(typ) + } + + return name + + case pkgbits.ObjVar: + name := do(ir.ONAME, false) + setType(name, r.typ()) + rext.varExt(name) + return name + } +} + +func (dict *readerDict) mangle(sym *types.Sym) *types.Sym { + if !dict.hasTypeParams() { + return sym + } + + // If sym is a locally defined generic type, we need the suffix to + // stay at the end after mangling so that types/fmt.go can strip it + // out again when writing the type's runtime descriptor (#54456). + base, suffix := types.SplitVargenSuffix(sym.Name) + + var buf strings.Builder + buf.WriteString(base) + buf.WriteByte('[') + for i, targ := range dict.targs { + if i > 0 { + if i == dict.implicits { + buf.WriteByte(';') + } else { + buf.WriteByte(',') + } + } + buf.WriteString(targ.LinkString()) + } + buf.WriteByte(']') + buf.WriteString(suffix) + return sym.Pkg.Lookup(buf.String()) +} + +// shapify returns the shape type for targ. +// +// If basic is true, then the type argument is used to instantiate a +// type parameter whose constraint is a basic interface. +func shapify(targ *types.Type, basic bool) *types.Type { + if targ.Kind() == types.TFORW { + if targ.IsFullyInstantiated() { + // For recursive instantiated type argument, it may still be a TFORW + // when shapifying happens. If we don't have targ's underlying type, + // shapify won't work. The worst case is we end up not reusing code + // optimally in some tricky cases. + if base.Debug.Shapify != 0 { + base.Warn("skipping shaping of recursive type %v", targ) + } + if targ.HasShape() { + return targ + } + } else { + base.Fatalf("%v is missing its underlying type", targ) + } + } + + // When a pointer type is used to instantiate a type parameter + // constrained by a basic interface, we know the pointer's element + // type can't matter to the generated code. In this case, we can use + // an arbitrary pointer type as the shape type. (To match the + // non-unified frontend, we use `*byte`.) + // + // Otherwise, we simply use the type's underlying type as its shape. + // + // TODO(mdempsky): It should be possible to do much more aggressive + // shaping still; e.g., collapsing all pointer-shaped types into a + // common type, collapsing scalars of the same size/alignment into a + // common type, recursively shaping the element types of composite + // types, and discarding struct field names and tags. However, we'll + // need to start tracking how type parameters are actually used to + // implement some of these optimizations. + under := targ.Underlying() + if basic && targ.IsPtr() && !targ.Elem().NotInHeap() { + under = types.NewPtr(types.Types[types.TUINT8]) + } + + sym := types.ShapePkg.Lookup(under.LinkString()) + if sym.Def == nil { + name := ir.NewDeclNameAt(under.Pos(), ir.OTYPE, sym) + typ := types.NewNamed(name) + typ.SetUnderlying(under) + sym.Def = typed(typ, name) + } + res := sym.Def.Type() + assert(res.IsShape()) + assert(res.HasShape()) + return res +} + +// objDictIdx reads and returns the specified object dictionary. +func (pr *pkgReader) objDictIdx(sym *types.Sym, idx pkgbits.Index, implicits, explicits []*types.Type, shaped bool) *readerDict { + r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1) + + dict := readerDict{ + shaped: shaped, + } + + nimplicits := r.Len() + nexplicits := r.Len() + + if nimplicits > len(implicits) || nexplicits != len(explicits) { + base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits)) + } + + dict.targs = append(implicits[:nimplicits:nimplicits], explicits...) + dict.implicits = nimplicits + + // Within the compiler, we can just skip over the type parameters. + for range dict.targs[dict.implicits:] { + // Skip past bounds without actually evaluating them. + r.typInfo() + } + + dict.derived = make([]derivedInfo, r.Len()) + dict.derivedTypes = make([]*types.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + } + + // Runtime dictionary information; private to the compiler. + + // If any type argument is already shaped, then we're constructing a + // shaped object, even if not explicitly requested (i.e., calling + // objIdx with shaped==true). This can happen with instantiating + // types that are referenced within a function body. + for _, targ := range dict.targs { + if targ.HasShape() { + dict.shaped = true + break + } + } + + // And if we're constructing a shaped object, then shapify all type + // arguments. + for i, targ := range dict.targs { + basic := r.Bool() + if dict.shaped { + dict.targs[i] = shapify(targ, basic) + } + } + + dict.baseSym = dict.mangle(sym) + + dict.typeParamMethodExprs = make([]readerMethodExprInfo, r.Len()) + for i := range dict.typeParamMethodExprs { + typeParamIdx := r.Len() + _, method := r.selector() + + dict.typeParamMethodExprs[i] = readerMethodExprInfo{typeParamIdx, method} + } + + dict.subdicts = make([]objInfo, r.Len()) + for i := range dict.subdicts { + dict.subdicts[i] = r.objInfo() + } + + dict.rtypes = make([]typeInfo, r.Len()) + for i := range dict.rtypes { + dict.rtypes[i] = r.typInfo() + } + + dict.itabs = make([]itabInfo, r.Len()) + for i := range dict.itabs { + dict.itabs[i] = itabInfo{typ: r.typInfo(), iface: r.typInfo()} + } + + return &dict +} + +func (r *reader) typeParamNames() { + r.Sync(pkgbits.SyncTypeParamNames) + + for range r.dict.targs[r.dict.implicits:] { + r.pos() + r.localIdent() + } +} + +func (r *reader) method(rext *reader) *types.Field { + r.Sync(pkgbits.SyncMethod) + pos := r.pos() + pkg, sym := r.selector() + r.typeParamNames() + _, recv := r.param() + typ := r.signature(pkg, recv) + + name := ir.NewNameAt(pos, ir.MethodSym(recv.Type, sym)) + setType(name, typ) + + name.Func = ir.NewFunc(r.pos()) + name.Func.Nname = name + + if r.hasTypeParams() { + name.Func.SetDupok(true) + if r.dict.shaped { + typ = shapeSig(name.Func, r.dict) + setType(name, typ) + } + } + + rext.funcExt(name, sym) + + meth := types.NewField(name.Func.Pos(), sym, typ) + meth.Nname = name + meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0) + + return meth +} + +func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) { + r.Sync(pkgbits.SyncSym) + pkg = r.pkg() + if name := r.String(); name != "" { + sym = pkg.Lookup(name) + } + return +} + +func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) { + r.Sync(pkgbits.SyncLocalIdent) + pkg = r.pkg() + if name := r.String(); name != "" { + sym = pkg.Lookup(name) + } + return +} + +func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) { + r.Sync(pkgbits.SyncSelector) + origPkg = r.pkg() + name := r.String() + pkg := origPkg + if types.IsExported(name) { + pkg = types.LocalPkg + } + sym = pkg.Lookup(name) + return +} + +func (r *reader) hasTypeParams() bool { + return r.dict.hasTypeParams() +} + +func (dict *readerDict) hasTypeParams() bool { + return dict != nil && len(dict.targs) != 0 +} + +// @@@ Compiler extensions + +func (r *reader) funcExt(name *ir.Name, method *types.Sym) { + r.Sync(pkgbits.SyncFuncExt) + + name.Class = 0 // so MarkFunc doesn't complain + ir.MarkFunc(name) + + fn := name.Func + + // XXX: Workaround because linker doesn't know how to copy Pos. + if !fn.Pos().IsKnown() { + fn.SetPos(name.Pos()) + } + + // Normally, we only compile local functions, which saves redundant compilation work. + // n.Defn is not nil for local functions, and is nil for imported function. But for + // generic functions, we might have an instantiation that no other package has seen before. + // So we need to be conservative and compile it again. + // + // That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function. + // TODO(mdempsky,cuonglm): find a cleaner way to handle this. + if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() { + name.Defn = fn + } + + fn.Pragma = r.pragmaFlag() + r.linkname(name) + + typecheck.Func(fn) + + if r.Bool() { + assert(name.Defn == nil) + + fn.ABI = obj.ABI(r.Uint64()) + + // Escape analysis. + for _, fs := range &types.RecvsParams { + for _, f := range fs(name.Type()).FieldSlice() { + f.Note = r.String() + } + } + + if r.Bool() { + fn.Inl = &ir.Inline{ + Cost: int32(r.Len()), + CanDelayResults: r.Bool(), + } + } + } else { + r.addBody(name.Func, method) + } + r.Sync(pkgbits.SyncEOF) +} + +func (r *reader) typeExt(name *ir.Name) { + r.Sync(pkgbits.SyncTypeExt) + + typ := name.Type() + + if r.hasTypeParams() { + // Set "RParams" (really type arguments here, not parameters) so + // this type is treated as "fully instantiated". This ensures the + // type descriptor is written out as DUPOK and method wrappers are + // generated even for imported types. + var targs []*types.Type + targs = append(targs, r.dict.targs...) + typ.SetRParams(targs) + } + + name.SetPragma(r.pragmaFlag()) + + typecheck.SetBaseTypeIndex(typ, r.Int64(), r.Int64()) +} + +func (r *reader) varExt(name *ir.Name) { + r.Sync(pkgbits.SyncVarExt) + r.linkname(name) +} + +func (r *reader) linkname(name *ir.Name) { + assert(name.Op() == ir.ONAME) + r.Sync(pkgbits.SyncLinkname) + + if idx := r.Int64(); idx >= 0 { + lsym := name.Linksym() + lsym.SymIdx = int32(idx) + lsym.Set(obj.AttrIndexed, true) + } else { + name.Sym().Linkname = r.String() + } +} + +func (r *reader) pragmaFlag() ir.PragmaFlag { + r.Sync(pkgbits.SyncPragma) + return ir.PragmaFlag(r.Int()) +} + +// @@@ Function bodies + +// bodyReader tracks where the serialized IR for a local or imported, +// generic function's body can be found. +var bodyReader = map[*ir.Func]pkgReaderIndex{} + +// importBodyReader tracks where the serialized IR for an imported, +// static (i.e., non-generic) function body can be read. +var importBodyReader = map[*types.Sym]pkgReaderIndex{} + +// bodyReaderFor returns the pkgReaderIndex for reading fn's +// serialized IR, and whether one was found. +func bodyReaderFor(fn *ir.Func) (pri pkgReaderIndex, ok bool) { + if fn.Nname.Defn != nil { + pri, ok = bodyReader[fn] + base.AssertfAt(ok, base.Pos, "must have bodyReader for %v", fn) // must always be available + } else { + pri, ok = importBodyReader[fn.Sym()] + } + return +} + +// todoDicts holds the list of dictionaries that still need their +// runtime dictionary objects constructed. +var todoDicts []func() + +// todoBodies holds the list of function bodies that still need to be +// constructed. +var todoBodies []*ir.Func + +// addBody reads a function body reference from the element bitstream, +// and associates it with fn. +func (r *reader) addBody(fn *ir.Func, method *types.Sym) { + // addBody should only be called for local functions or imported + // generic functions; see comment in funcExt. + assert(fn.Nname.Defn != nil) + + idx := r.Reloc(pkgbits.RelocBody) + + pri := pkgReaderIndex{r.p, idx, r.dict, method, nil} + bodyReader[fn] = pri + + if r.curfn == nil { + todoBodies = append(todoBodies, fn) + return + } + + pri.funcBody(fn) +} + +func (pri pkgReaderIndex) funcBody(fn *ir.Func) { + r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody) + r.funcBody(fn) +} + +// funcBody reads a function body definition from the element +// bitstream, and populates fn with it. +func (r *reader) funcBody(fn *ir.Func) { + r.curfn = fn + r.closureVars = fn.ClosureVars + if len(r.closureVars) != 0 && r.hasTypeParams() { + r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit + } + + ir.WithFunc(fn, func() { + r.funcargs(fn) + + if r.syntheticBody(fn.Pos()) { + return + } + + if !r.Bool() { + return + } + + body := r.stmts() + if body == nil { + body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(src.NoXPos, nil))} + } + fn.Body = body + fn.Endlineno = r.pos() + }) + + r.marker.WriteTo(fn) +} + +// syntheticBody adds a synthetic body to r.curfn if appropriate, and +// reports whether it did. +func (r *reader) syntheticBody(pos src.XPos) bool { + if r.synthetic != nil { + r.synthetic(pos, r) + return true + } + + // If this function has type parameters and isn't shaped, then we + // just tail call its corresponding shaped variant. + if r.hasTypeParams() && !r.dict.shaped { + r.callShaped(pos) + return true + } + + return false +} + +// callShaped emits a tail call to r.shapedFn, passing along the +// arguments to the current function. +func (r *reader) callShaped(pos src.XPos) { + shapedObj := r.dict.shapedObj + assert(shapedObj != nil) + + var shapedFn ir.Node + if r.methodSym == nil { + // Instantiating a generic function; shapedObj is the shaped + // function itself. + assert(shapedObj.Op() == ir.ONAME && shapedObj.Class == ir.PFUNC) + shapedFn = shapedObj + } else { + // Instantiating a generic type's method; shapedObj is the shaped + // type, so we need to select it's corresponding method. + shapedFn = shapedMethodExpr(pos, shapedObj, r.methodSym) + } + + recvs, params := r.syntheticArgs(pos) + + // Construct the arguments list: receiver (if any), then runtime + // dictionary, and finally normal parameters. + // + // Note: For simplicity, shaped methods are added as normal methods + // on their shaped types. So existing code (e.g., packages ir and + // typecheck) expects the shaped type to appear as the receiver + // parameter (or first parameter, as a method expression). Hence + // putting the dictionary parameter after that is the least invasive + // solution at the moment. + var args ir.Nodes + args.Append(recvs...) + args.Append(typecheck.Expr(ir.NewAddrExpr(pos, r.p.dictNameOf(r.dict)))) + args.Append(params...) + + r.syntheticTailCall(pos, shapedFn, args) +} + +// syntheticArgs returns the recvs and params arguments passed to the +// current function. +func (r *reader) syntheticArgs(pos src.XPos) (recvs, params ir.Nodes) { + sig := r.curfn.Nname.Type() + + inlVarIdx := 0 + addParams := func(out *ir.Nodes, params []*types.Field) { + for _, param := range params { + var arg ir.Node + if param.Nname != nil { + name := param.Nname.(*ir.Name) + if !ir.IsBlank(name) { + if r.inlCall != nil { + // During inlining, we want the respective inlvar where we + // assigned the callee's arguments. + arg = r.inlvars[inlVarIdx] + } else { + // Otherwise, we can use the parameter itself directly. + base.AssertfAt(name.Curfn == r.curfn, name.Pos(), "%v has curfn %v, but want %v", name, name.Curfn, r.curfn) + arg = name + } + } + } + + // For anonymous and blank parameters, we don't have an *ir.Name + // to use as the argument. However, since we know the shaped + // function won't use the value either, we can just pass the + // zero value. (Also unfortunately, we don't have an easy + // zero-value IR node; so we use a default-initialized temporary + // variable.) + if arg == nil { + tmp := typecheck.TempAt(pos, r.curfn, param.Type) + r.curfn.Body.Append( + typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)), + typecheck.Stmt(ir.NewAssignStmt(pos, tmp, nil)), + ) + arg = tmp + } + + out.Append(arg) + inlVarIdx++ + } + } + + addParams(&recvs, sig.Recvs().FieldSlice()) + addParams(¶ms, sig.Params().FieldSlice()) + return +} + +// syntheticTailCall emits a tail call to fn, passing the given +// arguments list. +func (r *reader) syntheticTailCall(pos src.XPos, fn ir.Node, args ir.Nodes) { + // Mark the function as a wrapper so it doesn't show up in stack + // traces. + r.curfn.SetWrapper(true) + + call := typecheck.Call(pos, fn, args, fn.Type().IsVariadic()).(*ir.CallExpr) + + var stmt ir.Node + if fn.Type().NumResults() != 0 { + stmt = typecheck.Stmt(ir.NewReturnStmt(pos, []ir.Node{call})) + } else { + stmt = call + } + r.curfn.Body.Append(stmt) +} + +// dictNameOf returns the runtime dictionary corresponding to dict. +func (pr *pkgReader) dictNameOf(dict *readerDict) *ir.Name { + pos := base.AutogeneratedPos + + // Check that we only instantiate runtime dictionaries with real types. + base.AssertfAt(!dict.shaped, pos, "runtime dictionary of shaped object %v", dict.baseSym) + + sym := dict.baseSym.Pkg.Lookup(objabi.GlobalDictPrefix + "." + dict.baseSym.Name) + if sym.Def != nil { + return sym.Def.(*ir.Name) + } + + name := ir.NewNameAt(pos, sym) + name.Class = ir.PEXTERN + sym.Def = name // break cycles with mutual subdictionaries + + lsym := name.Linksym() + ot := 0 + + assertOffset := func(section string, offset int) { + base.AssertfAt(ot == offset*types.PtrSize, pos, "writing section %v at offset %v, but it should be at %v*%v", section, ot, offset, types.PtrSize) + } + + assertOffset("type param method exprs", dict.typeParamMethodExprsOffset()) + for _, info := range dict.typeParamMethodExprs { + typeParam := dict.targs[info.typeParamIdx] + method := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(typeParam), info.method)).(*ir.SelectorExpr) + assert(method.Op() == ir.OMETHEXPR) + + rsym := method.FuncName().Linksym() + assert(rsym.ABI() == obj.ABIInternal) // must be ABIInternal; see ir.OCFUNC in ssagen/ssa.go + + ot = objw.SymPtr(lsym, ot, rsym, 0) + } + + assertOffset("subdictionaries", dict.subdictsOffset()) + for _, info := range dict.subdicts { + explicits := pr.typListIdx(info.explicits, dict) + + // Careful: Due to subdictionary cycles, name may not be fully + // initialized yet. + name := pr.objDictName(info.idx, dict.targs, explicits) + + ot = objw.SymPtr(lsym, ot, name.Linksym(), 0) + } + + assertOffset("rtypes", dict.rtypesOffset()) + for _, info := range dict.rtypes { + typ := pr.typIdx(info, dict, true) + ot = objw.SymPtr(lsym, ot, reflectdata.TypeLinksym(typ), 0) + + // TODO(mdempsky): Double check this. + reflectdata.MarkTypeUsedInInterface(typ, lsym) + } + + // For each (typ, iface) pair, we write the *runtime.itab pointer + // for the pair. For pairs that don't actually require an itab + // (i.e., typ is an interface, or iface is an empty interface), we + // write a nil pointer instead. This is wasteful, but rare in + // practice (e.g., instantiating a type parameter with an interface + // type). + assertOffset("itabs", dict.itabsOffset()) + for _, info := range dict.itabs { + typ := pr.typIdx(info.typ, dict, true) + iface := pr.typIdx(info.iface, dict, true) + + if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() { + ot = objw.SymPtr(lsym, ot, reflectdata.ITabLsym(typ, iface), 0) + } else { + ot += types.PtrSize + } + + // TODO(mdempsky): Double check this. + reflectdata.MarkTypeUsedInInterface(typ, lsym) + reflectdata.MarkTypeUsedInInterface(iface, lsym) + } + + objw.Global(lsym, int32(ot), obj.DUPOK|obj.RODATA) + + name.SetType(dict.varType()) + name.SetTypecheck(1) + + return name +} + +// typeParamMethodExprsOffset returns the offset of the runtime +// dictionary's type parameter method expressions section, in words. +func (dict *readerDict) typeParamMethodExprsOffset() int { + return 0 +} + +// subdictsOffset returns the offset of the runtime dictionary's +// subdictionary section, in words. +func (dict *readerDict) subdictsOffset() int { + return dict.typeParamMethodExprsOffset() + len(dict.typeParamMethodExprs) +} + +// rtypesOffset returns the offset of the runtime dictionary's rtypes +// section, in words. +func (dict *readerDict) rtypesOffset() int { + return dict.subdictsOffset() + len(dict.subdicts) +} + +// itabsOffset returns the offset of the runtime dictionary's itabs +// section, in words. +func (dict *readerDict) itabsOffset() int { + return dict.rtypesOffset() + len(dict.rtypes) +} + +// numWords returns the total number of words that comprise dict's +// runtime dictionary variable. +func (dict *readerDict) numWords() int64 { + return int64(dict.itabsOffset() + len(dict.itabs)) +} + +// varType returns the type of dict's runtime dictionary variable. +func (dict *readerDict) varType() *types.Type { + return types.NewArray(types.Types[types.TUINTPTR], dict.numWords()) +} + +func (r *reader) funcargs(fn *ir.Func) { + sig := fn.Nname.Type() + + if recv := sig.Recv(); recv != nil { + r.funcarg(recv, recv.Sym, ir.PPARAM) + } + for _, param := range sig.Params().FieldSlice() { + r.funcarg(param, param.Sym, ir.PPARAM) + } + + for i, param := range sig.Results().FieldSlice() { + sym := types.OrigSym(param.Sym) + + if sym == nil || sym.IsBlank() { + prefix := "~r" + if r.inlCall != nil { + prefix = "~R" + } else if sym != nil { + prefix = "~b" + } + sym = typecheck.LookupNum(prefix, i) + } + + r.funcarg(param, sym, ir.PPARAMOUT) + } +} + +func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) { + if sym == nil { + assert(ctxt == ir.PPARAM) + if r.inlCall != nil { + r.inlvars.Append(ir.BlankNode) + } + return + } + + name := ir.NewNameAt(r.inlPos(param.Pos), sym) + setType(name, param.Type) + r.addLocal(name, ctxt) + + if r.inlCall == nil { + if !r.funarghack { + param.Sym = sym + param.Nname = name + } + } else { + if ctxt == ir.PPARAMOUT { + r.retvars.Append(name) + } else { + r.inlvars.Append(name) + } + } +} + +func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) { + assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT) + + if name.Sym().Name == dictParamName { + r.dictParam = name + } else { + if r.synthetic == nil { + r.Sync(pkgbits.SyncAddLocal) + if r.p.SyncMarkers() { + want := r.Int() + if have := len(r.locals); have != want { + base.FatalfAt(name.Pos(), "locals table has desynced") + } + } + r.varDictIndex(name) + } + + r.locals = append(r.locals, name) + } + + name.SetUsed(true) + + // TODO(mdempsky): Move earlier. + if ir.IsBlank(name) { + return + } + + if r.inlCall != nil { + if ctxt == ir.PAUTO { + name.SetInlLocal(true) + } else { + name.SetInlFormal(true) + ctxt = ir.PAUTO + } + + // TODO(mdempsky): Rethink this hack. + if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 { + name.SetPos(r.inlCall.Pos()) + name.SetInlFormal(false) + name.SetInlLocal(false) + } + } + + name.Class = ctxt + name.Curfn = r.curfn + + r.curfn.Dcl = append(r.curfn.Dcl, name) + + if ctxt == ir.PAUTO { + name.SetFrameOffset(0) + } +} + +func (r *reader) useLocal() *ir.Name { + r.Sync(pkgbits.SyncUseObjLocal) + if r.Bool() { + return r.locals[r.Len()] + } + return r.closureVars[r.Len()] +} + +func (r *reader) openScope() { + r.Sync(pkgbits.SyncOpenScope) + pos := r.pos() + + if base.Flag.Dwarf { + r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl)) + r.marker.Push(pos) + } +} + +func (r *reader) closeScope() { + r.Sync(pkgbits.SyncCloseScope) + r.lastCloseScopePos = r.pos() + + r.closeAnotherScope() +} + +// closeAnotherScope is like closeScope, but it reuses the same mark +// position as the last closeScope call. This is useful for "for" and +// "if" statements, as their implicit blocks always end at the same +// position as an explicit block. +func (r *reader) closeAnotherScope() { + r.Sync(pkgbits.SyncCloseAnotherScope) + + if base.Flag.Dwarf { + scopeVars := r.scopeVars[len(r.scopeVars)-1] + r.scopeVars = r.scopeVars[:len(r.scopeVars)-1] + + // Quirkish: noder decides which scopes to keep before + // typechecking, whereas incremental typechecking during IR + // construction can result in new autotemps being allocated. To + // produce identical output, we ignore autotemps here for the + // purpose of deciding whether to retract the scope. + // + // This is important for net/http/fcgi, because it contains: + // + // var body io.ReadCloser + // if len(content) > 0 { + // body, req.pw = io.Pipe() + // } else { … } + // + // Notably, io.Pipe is inlinable, and inlining it introduces a ~R0 + // variable at the call site. + // + // Noder does not preserve the scope where the io.Pipe() call + // resides, because it doesn't contain any declared variables in + // source. So the ~R0 variable ends up being assigned to the + // enclosing scope instead. + // + // However, typechecking this assignment also introduces + // autotemps, because io.Pipe's results need conversion before + // they can be assigned to their respective destination variables. + // + // TODO(mdempsky): We should probably just keep all scopes, and + // let dwarfgen take care of pruning them instead. + retract := true + for _, n := range r.curfn.Dcl[scopeVars:] { + if !n.AutoTemp() { + retract = false + break + } + } + + if retract { + // no variables were declared in this scope, so we can retract it. + r.marker.Unpush() + } else { + r.marker.Pop(r.lastCloseScopePos) + } + } +} + +// @@@ Statements + +func (r *reader) stmt() ir.Node { + switch stmts := r.stmts(); len(stmts) { + case 0: + return nil + case 1: + return stmts[0] + default: + return ir.NewBlockStmt(stmts[0].Pos(), stmts) + } +} + +func (r *reader) stmts() []ir.Node { + assert(ir.CurFunc == r.curfn) + var res ir.Nodes + + r.Sync(pkgbits.SyncStmts) + for { + tag := codeStmt(r.Code(pkgbits.SyncStmt1)) + if tag == stmtEnd { + r.Sync(pkgbits.SyncStmtsEnd) + return res + } + + if n := r.stmt1(tag, &res); n != nil { + res.Append(typecheck.Stmt(n)) + } + } +} + +func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { + var label *types.Sym + if n := len(*out); n > 0 { + if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok { + label = ls.Label + } + } + + switch tag { + default: + panic("unexpected statement") + + case stmtAssign: + pos := r.pos() + names, lhs := r.assignList() + rhs := r.multiExpr() + + if len(rhs) == 0 { + for _, name := range names { + as := ir.NewAssignStmt(pos, name, nil) + as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name)) + out.Append(typecheck.Stmt(as)) + } + return nil + } + + if len(lhs) == 1 && len(rhs) == 1 { + n := ir.NewAssignStmt(pos, lhs[0], rhs[0]) + n.Def = r.initDefn(n, names) + return n + } + + n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs) + n.Def = r.initDefn(n, names) + return n + + case stmtAssignOp: + op := r.op() + lhs := r.expr() + pos := r.pos() + rhs := r.expr() + return ir.NewAssignOpStmt(pos, op, lhs, rhs) + + case stmtIncDec: + op := r.op() + lhs := r.expr() + pos := r.pos() + n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one)) + n.IncDec = true + return n + + case stmtBlock: + out.Append(r.blockStmt()...) + return nil + + case stmtBranch: + pos := r.pos() + op := r.op() + sym := r.optLabel() + return ir.NewBranchStmt(pos, op, sym) + + case stmtCall: + pos := r.pos() + op := r.op() + call := r.expr() + return ir.NewGoDeferStmt(pos, op, call) + + case stmtExpr: + return r.expr() + + case stmtFor: + return r.forStmt(label) + + case stmtIf: + return r.ifStmt() + + case stmtLabel: + pos := r.pos() + sym := r.label() + return ir.NewLabelStmt(pos, sym) + + case stmtReturn: + pos := r.pos() + results := r.multiExpr() + return ir.NewReturnStmt(pos, results) + + case stmtSelect: + return r.selectStmt(label) + + case stmtSend: + pos := r.pos() + ch := r.expr() + value := r.expr() + return ir.NewSendStmt(pos, ch, value) + + case stmtSwitch: + return r.switchStmt(label) + } +} + +func (r *reader) assignList() ([]*ir.Name, []ir.Node) { + lhs := make([]ir.Node, r.Len()) + var names []*ir.Name + + for i := range lhs { + expr, def := r.assign() + lhs[i] = expr + if def { + names = append(names, expr.(*ir.Name)) + } + } + + return names, lhs +} + +// assign returns an assignee expression. It also reports whether the +// returned expression is a newly declared variable. +func (r *reader) assign() (ir.Node, bool) { + switch tag := codeAssign(r.Code(pkgbits.SyncAssign)); tag { + default: + panic("unhandled assignee expression") + + case assignBlank: + return typecheck.AssignExpr(ir.BlankNode), false + + case assignDef: + pos := r.pos() + setBasePos(pos) + _, sym := r.localIdent() + typ := r.typ() + + name := ir.NewNameAt(pos, sym) + setType(name, typ) + r.addLocal(name, ir.PAUTO) + return name, true + + case assignExpr: + return r.expr(), false + } +} + +func (r *reader) blockStmt() []ir.Node { + r.Sync(pkgbits.SyncBlockStmt) + r.openScope() + stmts := r.stmts() + r.closeScope() + return stmts +} + +func (r *reader) forStmt(label *types.Sym) ir.Node { + r.Sync(pkgbits.SyncForStmt) + + r.openScope() + + if r.Bool() { + pos := r.pos() + rang := ir.NewRangeStmt(pos, nil, nil, nil, nil) + rang.Label = label + + names, lhs := r.assignList() + if len(lhs) >= 1 { + rang.Key = lhs[0] + if len(lhs) >= 2 { + rang.Value = lhs[1] + } + } + rang.Def = r.initDefn(rang, names) + + rang.X = r.expr() + if rang.X.Type().IsMap() { + rang.RType = r.rtype(pos) + } + if rang.Key != nil && !ir.IsBlank(rang.Key) { + rang.KeyTypeWord, rang.KeySrcRType = r.convRTTI(pos) + } + if rang.Value != nil && !ir.IsBlank(rang.Value) { + rang.ValueTypeWord, rang.ValueSrcRType = r.convRTTI(pos) + } + + rang.Body = r.blockStmt() + r.closeAnotherScope() + + return rang + } + + pos := r.pos() + init := r.stmt() + cond := r.optExpr() + post := r.stmt() + body := r.blockStmt() + r.closeAnotherScope() + + stmt := ir.NewForStmt(pos, init, cond, post, body) + stmt.Label = label + return stmt +} + +func (r *reader) ifStmt() ir.Node { + r.Sync(pkgbits.SyncIfStmt) + r.openScope() + pos := r.pos() + init := r.stmts() + cond := r.expr() + then := r.blockStmt() + els := r.stmts() + n := ir.NewIfStmt(pos, cond, then, els) + n.SetInit(init) + r.closeAnotherScope() + return n +} + +func (r *reader) selectStmt(label *types.Sym) ir.Node { + r.Sync(pkgbits.SyncSelectStmt) + + pos := r.pos() + clauses := make([]*ir.CommClause, r.Len()) + for i := range clauses { + if i > 0 { + r.closeScope() + } + r.openScope() + + pos := r.pos() + comm := r.stmt() + body := r.stmts() + + // "case i = <-c: ..." may require an implicit conversion (e.g., + // see fixedbugs/bug312.go). Currently, typecheck throws away the + // implicit conversion and relies on it being reinserted later, + // but that would lose any explicit RTTI operands too. To preserve + // RTTI, we rewrite this as "case tmp := <-c: i = tmp; ...". + if as, ok := comm.(*ir.AssignStmt); ok && as.Op() == ir.OAS && !as.Def { + if conv, ok := as.Y.(*ir.ConvExpr); ok && conv.Op() == ir.OCONVIFACE { + base.AssertfAt(conv.Implicit(), conv.Pos(), "expected implicit conversion: %v", conv) + + recv := conv.X + base.AssertfAt(recv.Op() == ir.ORECV, recv.Pos(), "expected receive expression: %v", recv) + + tmp := r.temp(pos, recv.Type()) + + // Replace comm with `tmp := <-c`. + tmpAs := ir.NewAssignStmt(pos, tmp, recv) + tmpAs.Def = true + tmpAs.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp)) + comm = tmpAs + + // Change original assignment to `i = tmp`, and prepend to body. + conv.X = tmp + body = append([]ir.Node{as}, body...) + } + } + + // multiExpr will have desugared a comma-ok receive expression + // into a separate statement. However, the rest of the compiler + // expects comm to be the OAS2RECV statement itself, so we need to + // shuffle things around to fit that pattern. + if as2, ok := comm.(*ir.AssignListStmt); ok && as2.Op() == ir.OAS2 { + init := ir.TakeInit(as2.Rhs[0]) + base.AssertfAt(len(init) == 1 && init[0].Op() == ir.OAS2RECV, as2.Pos(), "unexpected assignment: %+v", as2) + + comm = init[0] + body = append([]ir.Node{as2}, body...) + } + + clauses[i] = ir.NewCommStmt(pos, comm, body) + } + if len(clauses) > 0 { + r.closeScope() + } + n := ir.NewSelectStmt(pos, clauses) + n.Label = label + return n +} + +func (r *reader) switchStmt(label *types.Sym) ir.Node { + r.Sync(pkgbits.SyncSwitchStmt) + + r.openScope() + pos := r.pos() + init := r.stmt() + + var tag ir.Node + var ident *ir.Ident + var iface *types.Type + if r.Bool() { + pos := r.pos() + if r.Bool() { + pos := r.pos() + _, sym := r.localIdent() + ident = ir.NewIdent(pos, sym) + } + x := r.expr() + iface = x.Type() + tag = ir.NewTypeSwitchGuard(pos, ident, x) + } else { + tag = r.optExpr() + } + + clauses := make([]*ir.CaseClause, r.Len()) + for i := range clauses { + if i > 0 { + r.closeScope() + } + r.openScope() + + pos := r.pos() + var cases, rtypes []ir.Node + if iface != nil { + cases = make([]ir.Node, r.Len()) + if len(cases) == 0 { + cases = nil // TODO(mdempsky): Unclear if this matters. + } + for i := range cases { + if r.Bool() { // case nil + cases[i] = typecheck.Expr(types.BuiltinPkg.Lookup("nil").Def.(*ir.NilExpr)) + } else { + cases[i] = r.exprType() + } + } + } else { + cases = r.exprList() + + // For `switch { case any(true): }` (e.g., issue 3980 in + // test/switch.go), the backend still creates a mixed bool/any + // comparison, and we need to explicitly supply the RTTI for the + // comparison. + // + // TODO(mdempsky): Change writer.go to desugar "switch {" into + // "switch true {", which we already handle correctly. + if tag == nil { + for i, cas := range cases { + if cas.Type().IsEmptyInterface() { + for len(rtypes) < i { + rtypes = append(rtypes, nil) + } + rtypes = append(rtypes, reflectdata.TypePtrAt(cas.Pos(), types.Types[types.TBOOL])) + } + } + } + } + + clause := ir.NewCaseStmt(pos, cases, nil) + clause.RTypes = rtypes + + if ident != nil { + pos := r.pos() + typ := r.typ() + + name := ir.NewNameAt(pos, ident.Sym()) + setType(name, typ) + r.addLocal(name, ir.PAUTO) + clause.Var = name + name.Defn = tag + } + + clause.Body = r.stmts() + clauses[i] = clause + } + if len(clauses) > 0 { + r.closeScope() + } + r.closeScope() + + n := ir.NewSwitchStmt(pos, tag, clauses) + n.Label = label + if init != nil { + n.SetInit([]ir.Node{init}) + } + return n +} + +func (r *reader) label() *types.Sym { + r.Sync(pkgbits.SyncLabel) + name := r.String() + if r.inlCall != nil { + name = fmt.Sprintf("~%s·%d", name, inlgen) + } + return typecheck.Lookup(name) +} + +func (r *reader) optLabel() *types.Sym { + r.Sync(pkgbits.SyncOptLabel) + if r.Bool() { + return r.label() + } + return nil +} + +// initDefn marks the given names as declared by defn and populates +// its Init field with ODCL nodes. It then reports whether any names +// were so declared, which can be used to initialize defn.Def. +func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool { + if len(names) == 0 { + return false + } + + init := make([]ir.Node, len(names)) + for i, name := range names { + name.Defn = defn + init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name) + } + defn.SetInit(init) + return true +} + +// @@@ Expressions + +// expr reads and returns a typechecked expression. +func (r *reader) expr() (res ir.Node) { + defer func() { + if res != nil && res.Typecheck() == 0 { + base.FatalfAt(res.Pos(), "%v missed typecheck", res) + } + }() + + switch tag := codeExpr(r.Code(pkgbits.SyncExpr)); tag { + default: + panic("unhandled expression") + + case exprLocal: + return typecheck.Expr(r.useLocal()) + + case exprGlobal: + // Callee instead of Expr allows builtins + // TODO(mdempsky): Handle builtins directly in exprCall, like method calls? + return typecheck.Callee(r.obj()) + + case exprFuncInst: + pos := r.pos() + wrapperFn, baseFn, dictPtr := r.funcInst(pos) + if wrapperFn != nil { + return wrapperFn + } + return r.curry(pos, false, baseFn, dictPtr, nil) + + case exprConst: + pos := r.pos() + typ := r.typ() + val := FixValue(typ, r.Value()) + op := r.op() + orig := r.String() + return typecheck.Expr(OrigConst(pos, typ, val, op, orig)) + + case exprNil: + pos := r.pos() + typ := r.typ() + return Nil(pos, typ) + + case exprCompLit: + return r.compLit() + + case exprFuncLit: + return r.funcLit() + + case exprFieldVal: + x := r.expr() + pos := r.pos() + _, sym := r.selector() + + return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr) + + case exprMethodVal: + recv := r.expr() + pos := r.pos() + wrapperFn, baseFn, dictPtr := r.methodExpr() + + // For simple wrapperFn values, the existing machinery for creating + // and deduplicating wrapperFn value wrappers still works fine. + if wrapperFn, ok := wrapperFn.(*ir.SelectorExpr); ok && wrapperFn.Op() == ir.OMETHEXPR { + // The receiver expression we constructed may have a shape type. + // For example, in fixedbugs/issue54343.go, `New[int]()` is + // constructed as `New[go.shape.int](&.dict.New[int])`, which + // has type `*T[go.shape.int]`, not `*T[int]`. + // + // However, the method we want to select here is `(*T[int]).M`, + // not `(*T[go.shape.int]).M`, so we need to manually convert + // the type back so that the OXDOT resolves correctly. + // + // TODO(mdempsky): Logically it might make more sense for + // exprCall to take responsibility for setting a non-shaped + // result type, but this is the only place where we care + // currently. And only because existing ir.OMETHVALUE backend + // code relies on n.X.Type() instead of n.Selection.Recv().Type + // (because the latter is types.FakeRecvType() in the case of + // interface method values). + // + if recv.Type().HasShape() { + typ := wrapperFn.Type().Params().Field(0).Type + if !types.Identical(typ, recv.Type()) { + base.FatalfAt(wrapperFn.Pos(), "receiver %L does not match %L", recv, wrapperFn) + } + recv = typecheck.Expr(ir.NewConvExpr(recv.Pos(), ir.OCONVNOP, typ, recv)) + } + + n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, recv, wrapperFn.Sel)).(*ir.SelectorExpr) + assert(n.Selection == wrapperFn.Selection) + + wrapper := methodValueWrapper{ + rcvr: n.X.Type(), + method: n.Selection, + } + + if r.importedDef() { + haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper) + } else { + needMethodValueWrappers = append(needMethodValueWrappers, wrapper) + } + return n + } + + // For more complicated method expressions, we construct a + // function literal wrapper. + return r.curry(pos, true, baseFn, recv, dictPtr) + + case exprMethodExpr: + recv := r.typ() + + implicits := make([]int, r.Len()) + for i := range implicits { + implicits[i] = r.Len() + } + var deref, addr bool + if r.Bool() { + deref = true + } else if r.Bool() { + addr = true + } + + pos := r.pos() + wrapperFn, baseFn, dictPtr := r.methodExpr() + + // If we already have a wrapper and don't need to do anything with + // it, we can just return the wrapper directly. + // + // N.B., we use implicits/deref/addr here as the source of truth + // rather than types.Identical, because the latter can be confused + // by tricky promoted methods (e.g., typeparam/mdempsky/21.go). + if wrapperFn != nil && len(implicits) == 0 && !deref && !addr { + if !types.Identical(recv, wrapperFn.Type().Params().Field(0).Type) { + base.FatalfAt(pos, "want receiver type %v, but have method %L", recv, wrapperFn) + } + return wrapperFn + } + + // Otherwise, if the wrapper function is a static method + // expression (OMETHEXPR) and the receiver type is unshaped, then + // we can rely on a statically generated wrapper being available. + if method, ok := wrapperFn.(*ir.SelectorExpr); ok && method.Op() == ir.OMETHEXPR && !recv.HasShape() { + return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), method.Sel)).(*ir.SelectorExpr) + } + + return r.methodExprWrap(pos, recv, implicits, deref, addr, baseFn, dictPtr) + + case exprIndex: + x := r.expr() + pos := r.pos() + index := r.expr() + n := typecheck.Expr(ir.NewIndexExpr(pos, x, index)) + switch n.Op() { + case ir.OINDEXMAP: + n := n.(*ir.IndexExpr) + n.RType = r.rtype(pos) + } + return n + + case exprSlice: + x := r.expr() + pos := r.pos() + var index [3]ir.Node + for i := range index { + index[i] = r.optExpr() + } + op := ir.OSLICE + if index[2] != nil { + op = ir.OSLICE3 + } + return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2])) + + case exprAssert: + x := r.expr() + pos := r.pos() + typ := r.exprType() + srcRType := r.rtype(pos) + + // TODO(mdempsky): Always emit ODYNAMICDOTTYPE for uniformity? + if typ, ok := typ.(*ir.DynamicType); ok && typ.Op() == ir.ODYNAMICTYPE { + assert := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, x, typ.RType) + assert.SrcRType = srcRType + assert.ITab = typ.ITab + return typed(typ.Type(), assert) + } + return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ.Type())) + + case exprUnaryOp: + op := r.op() + pos := r.pos() + x := r.expr() + + switch op { + case ir.OADDR: + return typecheck.Expr(typecheck.NodAddrAt(pos, x)) + case ir.ODEREF: + return typecheck.Expr(ir.NewStarExpr(pos, x)) + } + return typecheck.Expr(ir.NewUnaryExpr(pos, op, x)) + + case exprBinaryOp: + op := r.op() + x := r.expr() + pos := r.pos() + y := r.expr() + + switch op { + case ir.OANDAND, ir.OOROR: + return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y)) + } + return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) + + case exprRecv: + x := r.expr() + pos := r.pos() + for i, n := 0, r.Len(); i < n; i++ { + x = Implicit(DotField(pos, x, r.Len())) + } + if r.Bool() { // needs deref + x = Implicit(Deref(pos, x.Type().Elem(), x)) + } else if r.Bool() { // needs addr + x = Implicit(Addr(pos, x)) + } + return x + + case exprCall: + var fun ir.Node + var args ir.Nodes + if r.Bool() { // method call + recv := r.expr() + _, method, dictPtr := r.methodExpr() + + if recv.Type().IsInterface() && method.Op() == ir.OMETHEXPR { + method := method.(*ir.SelectorExpr) + + // The compiler backend (e.g., devirtualization) handle + // OCALLINTER/ODOTINTER better than OCALLFUNC/OMETHEXPR for + // interface calls, so we prefer to continue constructing + // calls that way where possible. + // + // There are also corner cases where semantically it's perhaps + // significant; e.g., fixedbugs/issue15975.go, #38634, #52025. + + fun = typecheck.Callee(ir.NewSelectorExpr(method.Pos(), ir.OXDOT, recv, method.Sel)) + } else { + if recv.Type().IsInterface() { + // N.B., this happens currently for typeparam/issue51521.go + // and typeparam/typeswitch3.go. + if base.Flag.LowerM > 0 { + base.WarnfAt(method.Pos(), "imprecise interface call") + } + } + + fun = method + args.Append(recv) + } + if dictPtr != nil { + args.Append(dictPtr) + } + } else if r.Bool() { // call to instanced function + pos := r.pos() + _, shapedFn, dictPtr := r.funcInst(pos) + fun = shapedFn + args.Append(dictPtr) + } else { + fun = r.expr() + } + pos := r.pos() + args.Append(r.multiExpr()...) + dots := r.Bool() + n := typecheck.Call(pos, fun, args, dots) + switch n.Op() { + case ir.OAPPEND: + n := n.(*ir.CallExpr) + n.RType = r.rtype(pos) + // For append(a, b...), we don't need the implicit conversion. The typechecker already + // ensured that a and b are both slices with the same base type, or []byte and string. + if n.IsDDD { + if conv, ok := n.Args[1].(*ir.ConvExpr); ok && conv.Op() == ir.OCONVNOP && conv.Implicit() { + n.Args[1] = conv.X + } + } + case ir.OCOPY: + n := n.(*ir.BinaryExpr) + n.RType = r.rtype(pos) + case ir.ODELETE: + n := n.(*ir.CallExpr) + n.RType = r.rtype(pos) + case ir.OUNSAFESLICE: + n := n.(*ir.BinaryExpr) + n.RType = r.rtype(pos) + } + return n + + case exprMake: + pos := r.pos() + typ := r.exprType() + extra := r.exprs() + n := typecheck.Expr(ir.NewCallExpr(pos, ir.OMAKE, nil, append([]ir.Node{typ}, extra...))).(*ir.MakeExpr) + n.RType = r.rtype(pos) + return n + + case exprNew: + pos := r.pos() + typ := r.exprType() + return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ)) + + case exprReshape: + typ := r.typ() + x := r.expr() + + if types.IdenticalStrict(x.Type(), typ) { + return x + } + + // Comparison expressions are constructed as "untyped bool" still. + // + // TODO(mdempsky): It should be safe to reshape them here too, but + // maybe it's better to construct them with the proper type + // instead. + if x.Type() == types.UntypedBool && typ.IsBoolean() { + return x + } + + base.AssertfAt(x.Type().HasShape() || typ.HasShape(), x.Pos(), "%L and %v are not shape types", x, typ) + base.AssertfAt(types.Identical(x.Type(), typ), x.Pos(), "%L is not shape-identical to %v", x, typ) + + // We use ir.HasUniquePos here as a check that x only appears once + // in the AST, so it's okay for us to call SetType without + // breaking any other uses of it. + // + // Notably, any ONAMEs should already have the exactly right shape + // type and been caught by types.IdenticalStrict above. + base.AssertfAt(ir.HasUniquePos(x), x.Pos(), "cannot call SetType(%v) on %L", typ, x) + + if base.Debug.Reshape != 0 { + base.WarnfAt(x.Pos(), "reshaping %L to %v", x, typ) + } + + x.SetType(typ) + return x + + case exprConvert: + implicit := r.Bool() + typ := r.typ() + pos := r.pos() + typeWord, srcRType := r.convRTTI(pos) + dstTypeParam := r.Bool() + identical := r.Bool() + x := r.expr() + + // TODO(mdempsky): Stop constructing expressions of untyped type. + x = typecheck.DefaultLit(x, typ) + + ce := ir.NewConvExpr(pos, ir.OCONV, typ, x) + ce.TypeWord, ce.SrcRType = typeWord, srcRType + if implicit { + ce.SetImplicit(true) + } + n := typecheck.Expr(ce) + + // Conversions between non-identical, non-empty interfaces always + // requires a runtime call, even if they have identical underlying + // interfaces. This is because we create separate itab instances + // for each unique interface type, not merely each unique + // interface shape. + // + // However, due to shape types, typecheck.Expr might mistakenly + // think a conversion between two non-empty interfaces are + // identical and set ir.OCONVNOP, instead of ir.OCONVIFACE. To + // ensure we update the itab field appropriately, we force it to + // ir.OCONVIFACE instead when shape types are involved. + // + // TODO(mdempsky): Are there other places we might get this wrong? + // Should this be moved down into typecheck.{Assign,Convert}op? + // This would be a non-issue if itabs were unique for each + // *underlying* interface type instead. + if !identical { + if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OCONVNOP && n.Type().IsInterface() && !n.Type().IsEmptyInterface() && (n.Type().HasShape() || n.X.Type().HasShape()) { + n.SetOp(ir.OCONVIFACE) + } + } + + // spec: "If the type is a type parameter, the constant is converted + // into a non-constant value of the type parameter." + if dstTypeParam && ir.IsConstNode(n) { + // Wrap in an OCONVNOP node to ensure result is non-constant. + n = Implicit(ir.NewConvExpr(pos, ir.OCONVNOP, n.Type(), n)) + n.SetTypecheck(1) + } + return n + } +} + +// funcInst reads an instantiated function reference, and returns +// three (possibly nil) expressions related to it: +// +// baseFn is always non-nil: it's either a function of the appropriate +// type already, or it has an extra dictionary parameter as the first +// parameter. +// +// If dictPtr is non-nil, then it's a dictionary argument that must be +// passed as the first argument to baseFn. +// +// If wrapperFn is non-nil, then it's either the same as baseFn (if +// dictPtr is nil), or it's semantically equivalent to currying baseFn +// to pass dictPtr. (wrapperFn is nil when dictPtr is an expression +// that needs to be computed dynamically.) +// +// For callers that are creating a call to the returned function, it's +// best to emit a call to baseFn, and include dictPtr in the arguments +// list as appropriate. +// +// For callers that want to return the function without invoking it, +// they may return wrapperFn if it's non-nil; but otherwise, they need +// to create their own wrapper. +func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) { + // Like in methodExpr, I'm pretty sure this isn't needed. + var implicits []*types.Type + if r.dict != nil { + implicits = r.dict.targs + } + + if r.Bool() { // dynamic subdictionary + idx := r.Len() + info := r.dict.subdicts[idx] + explicits := r.p.typListIdx(info.explicits, r.dict) + + baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name) + + // TODO(mdempsky): Is there a more robust way to get the + // dictionary pointer type here? + dictPtrType := baseFn.Type().Params().Field(0).Type + dictPtr = typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx))) + + return + } + + info := r.objInfo() + explicits := r.p.typListIdx(info.explicits, r.dict) + + wrapperFn = r.p.objIdx(info.idx, implicits, explicits, false).(*ir.Name) + baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name) + + dictName := r.p.objDictName(info.idx, implicits, explicits) + dictPtr = typecheck.Expr(ir.NewAddrExpr(pos, dictName)) + + return +} + +func (pr *pkgReader) objDictName(idx pkgbits.Index, implicits, explicits []*types.Type) *ir.Name { + rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) + _, sym := rname.qualifiedIdent() + tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) + + if tag == pkgbits.ObjStub { + assert(!sym.IsBlank()) + if pri, ok := objReader[sym]; ok { + return pri.pr.objDictName(pri.idx, nil, explicits) + } + base.Fatalf("unresolved stub: %v", sym) + } + + dict := pr.objDictIdx(sym, idx, implicits, explicits, false) + + return pr.dictNameOf(dict) +} + +// curry returns a function literal that calls fun with arg0 and +// (optionally) arg1, accepting additional arguments to the function +// literal as necessary to satisfy fun's signature. +// +// If nilCheck is true and arg0 is an interface value, then it's +// checked to be non-nil as an initial step at the point of evaluating +// the function literal itself. +func (r *reader) curry(pos src.XPos, ifaceHack bool, fun ir.Node, arg0, arg1 ir.Node) ir.Node { + var captured ir.Nodes + captured.Append(fun, arg0) + if arg1 != nil { + captured.Append(arg1) + } + + params, results := syntheticSig(fun.Type()) + params = params[len(captured)-1:] // skip curried parameters + typ := types.NewSignature(types.NoPkg, nil, nil, params, results) + + addBody := func(pos src.XPos, r *reader, captured []ir.Node) { + recvs, params := r.syntheticArgs(pos) + assert(len(recvs) == 0) + + fun := captured[0] + + var args ir.Nodes + args.Append(captured[1:]...) + args.Append(params...) + + r.syntheticTailCall(pos, fun, args) + } + + return r.syntheticClosure(pos, typ, ifaceHack, captured, addBody) +} + +// methodExprWrap returns a function literal that changes method's +// first parameter's type to recv, and uses implicits/deref/addr to +// select the appropriate receiver parameter to pass to method. +func (r *reader) methodExprWrap(pos src.XPos, recv *types.Type, implicits []int, deref, addr bool, method, dictPtr ir.Node) ir.Node { + var captured ir.Nodes + captured.Append(method) + + params, results := syntheticSig(method.Type()) + + // Change first parameter to recv. + params[0].Type = recv + + // If we have a dictionary pointer argument to pass, then omit the + // underlying method expression's dictionary parameter from the + // returned signature too. + if dictPtr != nil { + captured.Append(dictPtr) + params = append(params[:1], params[2:]...) + } + + typ := types.NewSignature(types.NoPkg, nil, nil, params, results) + + addBody := func(pos src.XPos, r *reader, captured []ir.Node) { + recvs, args := r.syntheticArgs(pos) + assert(len(recvs) == 0) + + fn := captured[0] + + // Rewrite first argument based on implicits/deref/addr. + { + arg := args[0] + for _, ix := range implicits { + arg = Implicit(DotField(pos, arg, ix)) + } + if deref { + arg = Implicit(Deref(pos, arg.Type().Elem(), arg)) + } else if addr { + arg = Implicit(Addr(pos, arg)) + } + args[0] = arg + } + + // Insert dictionary argument, if provided. + if dictPtr != nil { + newArgs := make([]ir.Node, len(args)+1) + newArgs[0] = args[0] + newArgs[1] = captured[1] + copy(newArgs[2:], args[1:]) + args = newArgs + } + + r.syntheticTailCall(pos, fn, args) + } + + return r.syntheticClosure(pos, typ, false, captured, addBody) +} + +// syntheticClosure constructs a synthetic function literal for +// currying dictionary arguments. pos is the position used for the +// closure. typ is the function literal's signature type. +// +// captures is a list of expressions that need to be evaluated at the +// point of function literal evaluation and captured by the function +// literal. If ifaceHack is true and captures[1] is an interface type, +// it's checked to be non-nil after evaluation. +// +// addBody is a callback function to populate the function body. The +// list of captured values passed back has the captured variables for +// use within the function literal, corresponding to the expressions +// in captures. +func (r *reader) syntheticClosure(pos src.XPos, typ *types.Type, ifaceHack bool, captures ir.Nodes, addBody func(pos src.XPos, r *reader, captured []ir.Node)) ir.Node { + // isSafe reports whether n is an expression that we can safely + // defer to evaluating inside the closure instead, to avoid storing + // them into the closure. + // + // In practice this is always (and only) the wrappee function. + isSafe := func(n ir.Node) bool { + if n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PFUNC { + return true + } + if n.Op() == ir.OMETHEXPR { + return true + } + + return false + } + + fn := ir.NewClosureFunc(pos, r.curfn != nil) + fn.SetWrapper(true) + clo := fn.OClosure + ir.NameClosure(clo, r.curfn) + + setType(fn.Nname, typ) + typecheck.Func(fn) + setType(clo, fn.Type()) + + var init ir.Nodes + for i, n := range captures { + if isSafe(n) { + continue // skip capture; can reference directly + } + + tmp := r.tempCopy(pos, n, &init) + ir.NewClosureVar(pos, fn, tmp) + + // We need to nil check interface receivers at the point of method + // value evaluation, ugh. + if ifaceHack && i == 1 && n.Type().IsInterface() { + check := ir.NewUnaryExpr(pos, ir.OCHECKNIL, ir.NewUnaryExpr(pos, ir.OITAB, tmp)) + init.Append(typecheck.Stmt(check)) + } + } + + pri := pkgReaderIndex{synthetic: func(pos src.XPos, r *reader) { + captured := make([]ir.Node, len(captures)) + next := 0 + for i, n := range captures { + if isSafe(n) { + captured[i] = n + } else { + captured[i] = r.closureVars[next] + next++ + } + } + assert(next == len(r.closureVars)) + + addBody(pos, r, captured) + }} + bodyReader[fn] = pri + pri.funcBody(fn) + + // TODO(mdempsky): Remove hard-coding of typecheck.Target. + return ir.InitExpr(init, ir.UseClosure(clo, typecheck.Target)) +} + +// syntheticSig duplicates and returns the params and results lists +// for sig, but renaming anonymous parameters so they can be assigned +// ir.Names. +func syntheticSig(sig *types.Type) (params, results []*types.Field) { + clone := func(params []*types.Field) []*types.Field { + res := make([]*types.Field, len(params)) + for i, param := range params { + sym := param.Sym + if sym == nil || sym.Name == "_" { + sym = typecheck.LookupNum(".anon", i) + } + // TODO(mdempsky): It would be nice to preserve the original + // parameter positions here instead, but at least + // typecheck.NewMethodType replaces them with base.Pos, making + // them useless. Worse, the positions copied from base.Pos may + // have inlining contexts, which we definitely don't want here + // (e.g., #54625). + res[i] = types.NewField(base.AutogeneratedPos, sym, param.Type) + res[i].SetIsDDD(param.IsDDD()) + } + return res + } + + return clone(sig.Params().FieldSlice()), clone(sig.Results().FieldSlice()) +} + +func (r *reader) optExpr() ir.Node { + if r.Bool() { + return r.expr() + } + return nil +} + +// methodExpr reads a method expression reference, and returns three +// (possibly nil) expressions related to it: +// +// baseFn is always non-nil: it's either a function of the appropriate +// type already, or it has an extra dictionary parameter as the second +// parameter (i.e., immediately after the promoted receiver +// parameter). +// +// If dictPtr is non-nil, then it's a dictionary argument that must be +// passed as the second argument to baseFn. +// +// If wrapperFn is non-nil, then it's either the same as baseFn (if +// dictPtr is nil), or it's semantically equivalent to currying baseFn +// to pass dictPtr. (wrapperFn is nil when dictPtr is an expression +// that needs to be computed dynamically.) +// +// For callers that are creating a call to the returned method, it's +// best to emit a call to baseFn, and include dictPtr in the arguments +// list as appropriate. +// +// For callers that want to return a method expression without +// invoking it, they may return wrapperFn if it's non-nil; but +// otherwise, they need to create their own wrapper. +func (r *reader) methodExpr() (wrapperFn, baseFn, dictPtr ir.Node) { + recv := r.typ() + sig0 := r.typ() + pos := r.pos() + _, sym := r.selector() + + // Signature type to return (i.e., recv prepended to the method's + // normal parameters list). + sig := typecheck.NewMethodType(sig0, recv) + + if r.Bool() { // type parameter method expression + idx := r.Len() + word := r.dictWord(pos, r.dict.typeParamMethodExprsOffset()+idx) + + // TODO(mdempsky): If the type parameter was instantiated with an + // interface type (i.e., embed.IsInterface()), then we could + // return the OMETHEXPR instead and save an indirection. + + // We wrote the method expression's entry point PC into the + // dictionary, but for Go `func` values we need to return a + // closure (i.e., pointer to a structure with the PC as the first + // field). Because method expressions don't have any closure + // variables, we pun the dictionary entry as the closure struct. + fn := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, sig, ir.NewAddrExpr(pos, word))) + return fn, fn, nil + } + + // TODO(mdempsky): I'm pretty sure this isn't needed: implicits is + // only relevant to locally defined types, but they can't have + // (non-promoted) methods. + var implicits []*types.Type + if r.dict != nil { + implicits = r.dict.targs + } + + if r.Bool() { // dynamic subdictionary + idx := r.Len() + info := r.dict.subdicts[idx] + explicits := r.p.typListIdx(info.explicits, r.dict) + + shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name) + shapedFn := shapedMethodExpr(pos, shapedObj, sym) + + // TODO(mdempsky): Is there a more robust way to get the + // dictionary pointer type here? + dictPtrType := shapedFn.Type().Params().Field(1).Type + dictPtr := typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, dictPtrType, r.dictWord(pos, r.dict.subdictsOffset()+idx))) + + return nil, shapedFn, dictPtr + } + + if r.Bool() { // static dictionary + info := r.objInfo() + explicits := r.p.typListIdx(info.explicits, r.dict) + + shapedObj := r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name) + shapedFn := shapedMethodExpr(pos, shapedObj, sym) + + dict := r.p.objDictName(info.idx, implicits, explicits) + dictPtr := typecheck.Expr(ir.NewAddrExpr(pos, dict)) + + // Check that dictPtr matches shapedFn's dictionary parameter. + if !types.Identical(dictPtr.Type(), shapedFn.Type().Params().Field(1).Type) { + base.FatalfAt(pos, "dict %L, but shaped method %L", dict, shapedFn) + } + + // For statically known instantiations, we can take advantage of + // the stenciled wrapper. + base.AssertfAt(!recv.HasShape(), pos, "shaped receiver %v", recv) + wrapperFn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr) + base.AssertfAt(types.Identical(sig, wrapperFn.Type()), pos, "wrapper %L does not have type %v", wrapperFn, sig) + + return wrapperFn, shapedFn, dictPtr + } + + // Simple method expression; no dictionary needed. + base.AssertfAt(!recv.HasShape() || recv.IsInterface(), pos, "shaped receiver %v", recv) + fn := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr) + return fn, fn, nil +} + +// shapedMethodExpr returns the specified method on the given shaped +// type. +func shapedMethodExpr(pos src.XPos, obj *ir.Name, sym *types.Sym) *ir.SelectorExpr { + assert(obj.Op() == ir.OTYPE) + + typ := obj.Type() + assert(typ.HasShape()) + + method := func() *types.Field { + for _, method := range typ.Methods().Slice() { + if method.Sym == sym { + return method + } + } + + base.FatalfAt(pos, "failed to find method %v in shaped type %v", sym, typ) + panic("unreachable") + }() + + // Construct an OMETHEXPR node. + recv := method.Type.Recv().Type + return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, ir.TypeNode(recv), sym)).(*ir.SelectorExpr) +} + +func (r *reader) multiExpr() []ir.Node { + r.Sync(pkgbits.SyncMultiExpr) + + if r.Bool() { // N:1 + pos := r.pos() + expr := r.expr() + + results := make([]ir.Node, r.Len()) + as := ir.NewAssignListStmt(pos, ir.OAS2, nil, []ir.Node{expr}) + as.Def = true + for i := range results { + tmp := r.temp(pos, r.typ()) + as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp)) + as.Lhs.Append(tmp) + + res := ir.Node(tmp) + if r.Bool() { + n := ir.NewConvExpr(pos, ir.OCONV, r.typ(), res) + n.TypeWord, n.SrcRType = r.convRTTI(pos) + n.SetImplicit(true) + res = typecheck.Expr(n) + } + results[i] = res + } + + // TODO(mdempsky): Could use ir.InlinedCallExpr instead? + results[0] = ir.InitExpr([]ir.Node{typecheck.Stmt(as)}, results[0]) + return results + } + + // N:N + exprs := make([]ir.Node, r.Len()) + if len(exprs) == 0 { + return nil + } + for i := range exprs { + exprs[i] = r.expr() + } + return exprs +} + +// temp returns a new autotemp of the specified type. +func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name { + // See typecheck.typecheckargs. + curfn := r.curfn + if curfn == nil { + curfn = typecheck.InitTodoFunc + } + + return typecheck.TempAt(pos, curfn, typ) +} + +// tempCopy declares and returns a new autotemp initialized to the +// value of expr. +func (r *reader) tempCopy(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name { + if r.curfn == nil { + // Escape analysis doesn't know how to handle package-scope + // function literals with free variables (i.e., that capture + // temporary variables added to typecheck.InitTodoFunc). + // + // stencil.go works around this limitation by spilling values to + // global variables instead, but that causes the value to stay + // alive indefinitely; see go.dev/issue/54343. + // + // This code path (which implements the same workaround) isn't + // actually needed by unified IR, because it creates uses normal + // OMETHEXPR/OMETHVALUE nodes when statically-known instantiated + // types are used. But it's kept around for now because it's handy + // for testing that the generic fallback paths work correctly. + base.Fatalf("tempCopy called at package scope") + + tmp := staticinit.StaticName(expr.Type()) + + assign := ir.NewAssignStmt(pos, tmp, expr) + assign.Def = true + tmp.Defn = assign + + typecheck.Target.Decls = append(typecheck.Target.Decls, typecheck.Stmt(assign)) + + return tmp + } + + tmp := r.temp(pos, expr.Type()) + + init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp))) + + assign := ir.NewAssignStmt(pos, tmp, expr) + assign.Def = true + init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, expr))) + + tmp.Defn = assign + + return tmp +} + +func (r *reader) compLit() ir.Node { + r.Sync(pkgbits.SyncCompLit) + pos := r.pos() + typ0 := r.typ() + + typ := typ0 + if typ.IsPtr() { + typ = typ.Elem() + } + if typ.Kind() == types.TFORW { + base.FatalfAt(pos, "unresolved composite literal type: %v", typ) + } + var rtype ir.Node + if typ.IsMap() { + rtype = r.rtype(pos) + } + isStruct := typ.Kind() == types.TSTRUCT + + elems := make([]ir.Node, r.Len()) + for i := range elems { + elemp := &elems[i] + + if isStruct { + sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.Len()), nil) + *elemp, elemp = sk, &sk.Value + } else if r.Bool() { + kv := ir.NewKeyExpr(r.pos(), r.expr(), nil) + *elemp, elemp = kv, &kv.Value + } + + *elemp = wrapName(r.pos(), r.expr()) + } + + lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, elems)) + if rtype != nil { + lit := lit.(*ir.CompLitExpr) + lit.RType = rtype + } + if typ0.IsPtr() { + lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit)) + lit.SetType(typ0) + } + return lit +} + +func wrapName(pos src.XPos, x ir.Node) ir.Node { + // These nodes do not carry line numbers. + // Introduce a wrapper node to give them the correct line. + switch ir.Orig(x).Op() { + case ir.OTYPE, ir.OLITERAL: + if x.Sym() == nil { + break + } + fallthrough + case ir.ONAME, ir.ONONAME, ir.ONIL: + p := ir.NewParenExpr(pos, x) + p.SetImplicit(true) + return p + } + return x +} + +func (r *reader) funcLit() ir.Node { + r.Sync(pkgbits.SyncFuncLit) + + // The underlying function declaration (including its parameters' + // positions, if any) need to remain the original, uninlined + // positions. This is because we track inlining-context on nodes so + // we can synthesize the extra implied stack frames dynamically when + // generating tracebacks, whereas those stack frames don't make + // sense *within* the function literal. (Any necessary inlining + // adjustments will have been applied to the call expression + // instead.) + // + // This is subtle, and getting it wrong leads to cycles in the + // inlining tree, which lead to infinite loops during stack + // unwinding (#46234, #54625). + // + // Note that we *do* want the inline-adjusted position for the + // OCLOSURE node, because that position represents where any heap + // allocation of the closure is credited (#49171). + r.suppressInlPos++ + pos := r.pos() + xtype2 := r.signature(types.LocalPkg, nil) + r.suppressInlPos-- + + fn := ir.NewClosureFunc(pos, r.curfn != nil) + clo := fn.OClosure + clo.SetPos(r.inlPos(pos)) // see comment above + ir.NameClosure(clo, r.curfn) + + setType(fn.Nname, xtype2) + typecheck.Func(fn) + setType(clo, fn.Type()) + + fn.ClosureVars = make([]*ir.Name, 0, r.Len()) + for len(fn.ClosureVars) < cap(fn.ClosureVars) { + ir.NewClosureVar(r.pos(), fn, r.useLocal()) + } + if param := r.dictParam; param != nil { + // If we have a dictionary parameter, capture it too. For + // simplicity, we capture it last and unconditionally. + ir.NewClosureVar(param.Pos(), fn, param) + } + + r.addBody(fn, nil) + + // TODO(mdempsky): Remove hard-coding of typecheck.Target. + return ir.UseClosure(clo, typecheck.Target) +} + +func (r *reader) exprList() []ir.Node { + r.Sync(pkgbits.SyncExprList) + return r.exprs() +} + +func (r *reader) exprs() []ir.Node { + r.Sync(pkgbits.SyncExprs) + nodes := make([]ir.Node, r.Len()) + if len(nodes) == 0 { + return nil // TODO(mdempsky): Unclear if this matters. + } + for i := range nodes { + nodes[i] = r.expr() + } + return nodes +} + +// dictWord returns an expression to return the specified +// uintptr-typed word from the dictionary parameter. +func (r *reader) dictWord(pos src.XPos, idx int) ir.Node { + base.AssertfAt(r.dictParam != nil, pos, "expected dictParam in %v", r.curfn) + return typecheck.Expr(ir.NewIndexExpr(pos, r.dictParam, ir.NewBasicLit(pos, constant.MakeInt64(int64(idx))))) +} + +// rttiWord is like dictWord, but converts it to *byte (the type used +// internally to represent *runtime._type and *runtime.itab). +func (r *reader) rttiWord(pos src.XPos, idx int) ir.Node { + return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TUINT8]), r.dictWord(pos, idx))) +} + +// rtype reads a type reference from the element bitstream, and +// returns an expression of type *runtime._type representing that +// type. +func (r *reader) rtype(pos src.XPos) ir.Node { + _, rtype := r.rtype0(pos) + return rtype +} + +func (r *reader) rtype0(pos src.XPos) (typ *types.Type, rtype ir.Node) { + r.Sync(pkgbits.SyncRType) + if r.Bool() { // derived type + idx := r.Len() + info := r.dict.rtypes[idx] + typ = r.p.typIdx(info, r.dict, true) + rtype = r.rttiWord(pos, r.dict.rtypesOffset()+idx) + return + } + + typ = r.typ() + rtype = reflectdata.TypePtrAt(pos, typ) + return +} + +// varDictIndex populates name.DictIndex if name is a derived type. +func (r *reader) varDictIndex(name *ir.Name) { + if r.Bool() { + idx := 1 + r.dict.rtypesOffset() + r.Len() + if int(uint16(idx)) != idx { + base.FatalfAt(name.Pos(), "DictIndex overflow for %v: %v", name, idx) + } + name.DictIndex = uint16(idx) + } +} + +// itab returns a (typ, iface) pair of types. +// +// typRType and ifaceRType are expressions that evaluate to the +// *runtime._type for typ and iface, respectively. +// +// If typ is a concrete type and iface is a non-empty interface type, +// then itab is an expression that evaluates to the *runtime.itab for +// the pair. Otherwise, itab is nil. +func (r *reader) itab(pos src.XPos) (typ *types.Type, typRType ir.Node, iface *types.Type, ifaceRType ir.Node, itab ir.Node) { + typ, typRType = r.rtype0(pos) + iface, ifaceRType = r.rtype0(pos) + + idx := -1 + if r.Bool() { + idx = r.Len() + } + + if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() { + if idx >= 0 { + itab = r.rttiWord(pos, r.dict.itabsOffset()+idx) + } else { + base.AssertfAt(!typ.HasShape(), pos, "%v is a shape type", typ) + base.AssertfAt(!iface.HasShape(), pos, "%v is a shape type", iface) + + lsym := reflectdata.ITabLsym(typ, iface) + itab = typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8]) + } + } + + return +} + +// convRTTI returns expressions appropriate for populating an +// ir.ConvExpr's TypeWord and SrcRType fields, respectively. +func (r *reader) convRTTI(pos src.XPos) (typeWord, srcRType ir.Node) { + r.Sync(pkgbits.SyncConvRTTI) + src, srcRType0, dst, dstRType, itab := r.itab(pos) + if !dst.IsInterface() { + return + } + + // See reflectdata.ConvIfaceTypeWord. + switch { + case dst.IsEmptyInterface(): + if !src.IsInterface() { + typeWord = srcRType0 // direct eface construction + } + case !src.IsInterface(): + typeWord = itab // direct iface construction + default: + typeWord = dstRType // convI2I + } + + // See reflectdata.ConvIfaceSrcRType. + if !src.IsInterface() { + srcRType = srcRType0 + } + + return +} + +func (r *reader) exprType() ir.Node { + r.Sync(pkgbits.SyncExprType) + pos := r.pos() + + var typ *types.Type + var rtype, itab ir.Node + + if r.Bool() { + typ, rtype, _, _, itab = r.itab(pos) + if !typ.IsInterface() { + rtype = nil // TODO(mdempsky): Leave set? + } + } else { + typ, rtype = r.rtype0(pos) + + if !r.Bool() { // not derived + // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node. + n := ir.TypeNode(typ) + n.SetTypecheck(1) + return n + } + } + + dt := ir.NewDynamicType(pos, rtype) + dt.ITab = itab + return typed(typ, dt) +} + +func (r *reader) op() ir.Op { + r.Sync(pkgbits.SyncOp) + return ir.Op(r.Len()) +} + +// @@@ Package initialization + +func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) { + cgoPragmas := make([][]string, r.Len()) + for i := range cgoPragmas { + cgoPragmas[i] = r.Strings() + } + target.CgoPragmas = cgoPragmas + + r.pkgDecls(target) + + r.Sync(pkgbits.SyncEOF) +} + +func (r *reader) pkgDecls(target *ir.Package) { + r.Sync(pkgbits.SyncDecls) + for { + switch code := codeDecl(r.Code(pkgbits.SyncDecl)); code { + default: + panic(fmt.Sprintf("unhandled decl: %v", code)) + + case declEnd: + return + + case declFunc: + names := r.pkgObjs(target) + assert(len(names) == 1) + target.Decls = append(target.Decls, names[0].Func) + + case declMethod: + typ := r.typ() + _, sym := r.selector() + + method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0) + target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func) + + case declVar: + pos := r.pos() + names := r.pkgObjs(target) + values := r.exprList() + + if len(names) > 1 && len(values) == 1 { + as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values) + for _, name := range names { + as.Lhs.Append(name) + name.Defn = as + } + target.Decls = append(target.Decls, as) + } else { + for i, name := range names { + as := ir.NewAssignStmt(pos, name, nil) + if i < len(values) { + as.Y = values[i] + } + name.Defn = as + target.Decls = append(target.Decls, as) + } + } + + if n := r.Len(); n > 0 { + assert(len(names) == 1) + embeds := make([]ir.Embed, n) + for i := range embeds { + embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.Strings()} + } + names[0].Embed = &embeds + target.Embeds = append(target.Embeds, names[0]) + } + + case declOther: + r.pkgObjs(target) + } + } +} + +func (r *reader) pkgObjs(target *ir.Package) []*ir.Name { + r.Sync(pkgbits.SyncDeclNames) + nodes := make([]*ir.Name, r.Len()) + for i := range nodes { + r.Sync(pkgbits.SyncDeclName) + + name := r.obj().(*ir.Name) + nodes[i] = name + + sym := name.Sym() + if sym.IsBlank() { + continue + } + + switch name.Class { + default: + base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class) + + case ir.PEXTERN: + target.Externs = append(target.Externs, name) + + case ir.PFUNC: + assert(name.Type().Recv() == nil) + + // TODO(mdempsky): Cleaner way to recognize init? + if strings.HasPrefix(sym.Name, "init.") { + target.Inits = append(target.Inits, name.Func) + } + } + + if types.IsExported(sym.Name) { + assert(!sym.OnExportList()) + target.Exports = append(target.Exports, name) + sym.SetOnExportList(true) + } + + if base.Flag.AsmHdr != "" { + assert(!sym.Asm()) + target.Asms = append(target.Asms, name) + sym.SetAsm(true) + } + } + + return nodes +} + +// @@@ Inlining + +// unifiedHaveInlineBody reports whether we have the function body for +// fn, so we can inline it. +func unifiedHaveInlineBody(fn *ir.Func) bool { + if fn.Inl == nil { + return false + } + + _, ok := bodyReaderFor(fn) + return ok +} + +var inlgen = 0 + +// unifiedInlineCall implements inline.NewInline by re-reading the function +// body from its Unified IR export data. +func unifiedInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { + // TODO(mdempsky): Turn callerfn into an explicit parameter. + callerfn := ir.CurFunc + + pri, ok := bodyReaderFor(fn) + if !ok { + base.FatalfAt(call.Pos(), "cannot inline call to %v: missing inline body", fn) + } + + if fn.Inl.Body == nil { + expandInline(fn, pri) + } + + r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody) + + // TODO(mdempsky): This still feels clumsy. Can we do better? + tmpfn := ir.NewFunc(fn.Pos()) + tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym()) + tmpfn.Closgen = callerfn.Closgen + defer func() { callerfn.Closgen = tmpfn.Closgen }() + + setType(tmpfn.Nname, fn.Type()) + r.curfn = tmpfn + + r.inlCaller = callerfn + r.inlCall = call + r.inlFunc = fn + r.inlTreeIndex = inlIndex + r.inlPosBases = make(map[*src.PosBase]*src.PosBase) + + r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars)) + for i, cv := range r.inlFunc.ClosureVars { + r.closureVars[i] = cv.Outer + } + if len(r.closureVars) != 0 && r.hasTypeParams() { + r.dictParam = r.closureVars[len(r.closureVars)-1] // dictParam is last; see reader.funcLit + } + + r.funcargs(fn) + + r.delayResults = fn.Inl.CanDelayResults + + r.retlabel = typecheck.AutoLabel(".i") + inlgen++ + + init := ir.TakeInit(call) + + // For normal function calls, the function callee expression + // may contain side effects. Make sure to preserve these, + // if necessary (#42703). + if call.Op() == ir.OCALLFUNC { + inline.CalleeEffects(&init, call.X) + } + + var args ir.Nodes + if call.Op() == ir.OCALLMETH { + base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") + } + args.Append(call.Args...) + + // Create assignment to declare and initialize inlvars. + as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args) + as2.Def = true + var as2init ir.Nodes + for _, name := range r.inlvars { + if ir.IsBlank(name) { + continue + } + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) + name.Defn = as2 + } + as2.SetInit(as2init) + init.Append(typecheck.Stmt(as2)) + + if !r.delayResults { + // If not delaying retvars, declare and zero initialize the + // result variables now. + for _, name := range r.retvars { + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) + ras := ir.NewAssignStmt(call.Pos(), name, nil) + init.Append(typecheck.Stmt(ras)) + } + } + + // Add an inline mark just before the inlined body. + // This mark is inline in the code so that it's a reasonable spot + // to put a breakpoint. Not sure if that's really necessary or not + // (in which case it could go at the end of the function instead). + // Note issue 28603. + init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex))) + + nparams := len(r.curfn.Dcl) + + ir.WithFunc(r.curfn, func() { + if !r.syntheticBody(call.Pos()) { + assert(r.Bool()) // have body + + r.curfn.Body = r.stmts() + r.curfn.Endlineno = r.pos() + } + + // TODO(mdempsky): This shouldn't be necessary. Inlining might + // read in new function/method declarations, which could + // potentially be recursively inlined themselves; but we shouldn't + // need to read in the non-inlined bodies for the declarations + // themselves. But currently it's an easy fix to #50552. + readBodies(typecheck.Target) + + deadcode.Func(r.curfn) + + // Replace any "return" statements within the function body. + var edit func(ir.Node) ir.Node + edit = func(n ir.Node) ir.Node { + if ret, ok := n.(*ir.ReturnStmt); ok { + n = typecheck.Stmt(r.inlReturn(ret)) + } + ir.EditChildren(n, edit) + return n + } + edit(r.curfn) + }) + + body := ir.Nodes(r.curfn.Body) + + // Quirkish: We need to eagerly prune variables added during + // inlining, but removed by deadcode.FuncBody above. Unused + // variables will get removed during stack frame layout anyway, but + // len(fn.Dcl) ends up influencing things like autotmp naming. + + used := usedLocals(body) + + for i, name := range r.curfn.Dcl { + if i < nparams || used.Has(name) { + name.Curfn = callerfn + callerfn.Dcl = append(callerfn.Dcl, name) + + // Quirkish. TODO(mdempsky): Document why. + if name.AutoTemp() { + name.SetEsc(ir.EscUnknown) + + if base.Flag.GenDwarfInl != 0 { + name.SetInlLocal(true) + } else { + name.SetPos(r.inlCall.Pos()) + } + } + } + } + + body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel)) + + res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...)) + res.SetInit(init) + res.SetType(call.Type()) + res.SetTypecheck(1) + + // Inlining shouldn't add any functions to todoBodies. + assert(len(todoBodies) == 0) + + return res +} + +// inlReturn returns a statement that can substitute for the given +// return statement when inlining. +func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt { + pos := r.inlCall.Pos() + + block := ir.TakeInit(ret) + + if results := ret.Results; len(results) != 0 { + assert(len(r.retvars) == len(results)) + + as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results) + + if r.delayResults { + for _, name := range r.retvars { + // TODO(mdempsky): Use inlined position of name.Pos() instead? + name := name.(*ir.Name) + block.Append(ir.NewDecl(pos, ir.ODCL, name)) + name.Defn = as2 + } + } + + block.Append(as2) + } + + block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel)) + return ir.NewBlockStmt(pos, block) +} + +// expandInline reads in an extra copy of IR to populate +// fn.Inl.{Dcl,Body}. +func expandInline(fn *ir.Func, pri pkgReaderIndex) { + // TODO(mdempsky): Remove this function. It's currently needed by + // dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to + // create abstract function DIEs. But we should be able to provide it + // with the same information some other way. + + fndcls := len(fn.Dcl) + topdcls := len(typecheck.Target.Decls) + + tmpfn := ir.NewFunc(fn.Pos()) + tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym()) + tmpfn.ClosureVars = fn.ClosureVars + + { + r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody) + setType(tmpfn.Nname, fn.Type()) + + // Don't change parameter's Sym/Nname fields. + r.funarghack = true + + r.funcBody(tmpfn) + + ir.WithFunc(tmpfn, func() { + deadcode.Func(tmpfn) + }) + } + + used := usedLocals(tmpfn.Body) + + for _, name := range tmpfn.Dcl { + if name.Class != ir.PAUTO || used.Has(name) { + name.Curfn = fn + fn.Inl.Dcl = append(fn.Inl.Dcl, name) + } + } + fn.Inl.Body = tmpfn.Body + + // Double check that we didn't change fn.Dcl by accident. + assert(fndcls == len(fn.Dcl)) + + // typecheck.Stmts may have added function literals to + // typecheck.Target.Decls. Remove them again so we don't risk trying + // to compile them multiple times. + typecheck.Target.Decls = typecheck.Target.Decls[:topdcls] +} + +// usedLocals returns a set of local variables that are used within body. +func usedLocals(body []ir.Node) ir.NameSet { + var used ir.NameSet + ir.VisitList(body, func(n ir.Node) { + if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO { + used.Add(n) + } + }) + return used +} + +// @@@ Method wrappers + +// needWrapperTypes lists types for which we may need to generate +// method wrappers. +var needWrapperTypes []*types.Type + +// haveWrapperTypes lists types for which we know we already have +// method wrappers, because we found the type in an imported package. +var haveWrapperTypes []*types.Type + +// needMethodValueWrappers lists methods for which we may need to +// generate method value wrappers. +var needMethodValueWrappers []methodValueWrapper + +// haveMethodValueWrappers lists methods for which we know we already +// have method value wrappers, because we found it in an imported +// package. +var haveMethodValueWrappers []methodValueWrapper + +type methodValueWrapper struct { + rcvr *types.Type + method *types.Field +} + +func (r *reader) needWrapper(typ *types.Type) { + if typ.IsPtr() { + return + } + + // If a type was found in an imported package, then we can assume + // that package (or one of its transitive dependencies) already + // generated method wrappers for it. + if r.importedDef() { + haveWrapperTypes = append(haveWrapperTypes, typ) + } else { + needWrapperTypes = append(needWrapperTypes, typ) + } +} + +// importedDef reports whether r is reading from an imported and +// non-generic element. +// +// If a type was found in an imported package, then we can assume that +// package (or one of its transitive dependencies) already generated +// method wrappers for it. +// +// Exception: If we're instantiating an imported generic type or +// function, we might be instantiating it with type arguments not +// previously seen before. +// +// TODO(mdempsky): Distinguish when a generic function or type was +// instantiated in an imported package so that we can add types to +// haveWrapperTypes instead. +func (r *reader) importedDef() bool { + return r.p != localPkgReader && !r.hasTypeParams() +} + +func MakeWrappers(target *ir.Package) { + // Only unified IR emits its own wrappers. + if base.Debug.Unified == 0 { + return + } + + // always generate a wrapper for error.Error (#29304) + needWrapperTypes = append(needWrapperTypes, types.ErrorType) + + seen := make(map[string]*types.Type) + + for _, typ := range haveWrapperTypes { + wrapType(typ, target, seen, false) + } + haveWrapperTypes = nil + + for _, typ := range needWrapperTypes { + wrapType(typ, target, seen, true) + } + needWrapperTypes = nil + + for _, wrapper := range haveMethodValueWrappers { + wrapMethodValue(wrapper.rcvr, wrapper.method, target, false) + } + haveMethodValueWrappers = nil + + for _, wrapper := range needMethodValueWrappers { + wrapMethodValue(wrapper.rcvr, wrapper.method, target, true) + } + needMethodValueWrappers = nil +} + +func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) { + key := typ.LinkString() + if prev := seen[key]; prev != nil { + if !types.Identical(typ, prev) { + base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key) + } + return + } + seen[key] = typ + + if !needed { + // Only called to add to 'seen'. + return + } + + if !typ.IsInterface() { + typecheck.CalcMethods(typ) + } + for _, meth := range typ.AllMethods().Slice() { + if meth.Sym.IsBlank() || !meth.IsMethod() { + base.FatalfAt(meth.Pos, "invalid method: %v", meth) + } + + methodWrapper(0, typ, meth, target) + + // For non-interface types, we also want *T wrappers. + if !typ.IsInterface() { + methodWrapper(1, typ, meth, target) + + // For not-in-heap types, *T is a scalar, not pointer shaped, + // so the interface wrappers use **T. + if typ.NotInHeap() { + methodWrapper(2, typ, meth, target) + } + } + } +} + +func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) { + wrapper := tbase + for i := 0; i < derefs; i++ { + wrapper = types.NewPtr(wrapper) + } + + sym := ir.MethodSym(wrapper, method.Sym) + base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym) + sym.SetSiggen(true) + + wrappee := method.Type.Recv().Type + if types.Identical(wrapper, wrappee) || + !types.IsMethodApplicable(wrapper, method) || + !reflectdata.NeedEmit(tbase) { + return + } + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := newWrapperFunc(pos, sym, wrapper, method) + + var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name) + + // For simple *T wrappers around T methods, panicwrap produces a + // nicer panic message. + if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) { + cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node)) + then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)} + fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil)) + } + + // typecheck will add one implicit deref, if necessary, + // but not-in-heap types require more for their **T wrappers. + for i := 1; i < derefs; i++ { + recv = Implicit(ir.NewStarExpr(pos, recv)) + } + + addTailCall(pos, fn, recv, method) + + finishWrapperFunc(fn, target) +} + +func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) { + sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm") + if sym.Uniq() { + return + } + sym.SetUniq(true) + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := newWrapperFunc(pos, sym, nil, method) + sym.Def = fn.Nname + + // Declare and initialize variable holding receiver. + recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType) + + if !needed { + typecheck.Func(fn) + return + } + + addTailCall(pos, fn, recv, method) + + finishWrapperFunc(fn, target) +} + +func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func { + fn := ir.NewFunc(pos) + fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? + + name := ir.NewNameAt(pos, sym) + ir.MarkFunc(name) + name.Func = fn + name.Defn = fn + fn.Nname = name + + sig := newWrapperType(wrapper, method) + setType(name, sig) + + // TODO(mdempsky): De-duplicate with similar logic in funcargs. + defParams := func(class ir.Class, params *types.Type) { + for _, param := range params.FieldSlice() { + name := ir.NewNameAt(param.Pos, param.Sym) + name.Class = class + setType(name, param.Type) + + name.Curfn = fn + fn.Dcl = append(fn.Dcl, name) + + param.Nname = name + } + } + + defParams(ir.PPARAM, sig.Recvs()) + defParams(ir.PPARAM, sig.Params()) + defParams(ir.PPARAMOUT, sig.Results()) + + return fn +} + +func finishWrapperFunc(fn *ir.Func, target *ir.Package) { + typecheck.Func(fn) + + ir.WithFunc(fn, func() { + typecheck.Stmts(fn.Body) + }) + + // We generate wrappers after the global inlining pass, + // so we're responsible for applying inlining ourselves here. + inline.InlineCalls(fn) + + // The body of wrapper function after inlining may reveal new ir.OMETHVALUE node, + // we don't know whether wrapper function has been generated for it or not, so + // generate one immediately here. + ir.VisitList(fn.Body, func(n ir.Node) { + if n, ok := n.(*ir.SelectorExpr); ok && n.Op() == ir.OMETHVALUE { + wrapMethodValue(n.X.Type(), n.Selection, target, true) + } + }) + + target.Decls = append(target.Decls, fn) +} + +// newWrapperType returns a copy of the given signature type, but with +// the receiver parameter type substituted with recvType. +// If recvType is nil, newWrapperType returns a signature +// without a receiver parameter. +func newWrapperType(recvType *types.Type, method *types.Field) *types.Type { + clone := func(params []*types.Field) []*types.Field { + res := make([]*types.Field, len(params)) + for i, param := range params { + sym := param.Sym + if sym == nil || sym.Name == "_" { + sym = typecheck.LookupNum(".anon", i) + } + res[i] = types.NewField(param.Pos, sym, param.Type) + res[i].SetIsDDD(param.IsDDD()) + } + return res + } + + sig := method.Type + + var recv *types.Field + if recvType != nil { + recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType) + } + params := clone(sig.Params().FieldSlice()) + results := clone(sig.Results().FieldSlice()) + + return types.NewSignature(types.NoPkg, recv, nil, params, results) +} + +func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) { + sig := fn.Nname.Type() + args := make([]ir.Node, sig.NumParams()) + for i, param := range sig.Params().FieldSlice() { + args[i] = param.Nname.(*ir.Name) + } + + // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper. + // Not urgent though, because tail calls are currently incompatible with regabi anyway. + + fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls? + + dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym) + call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr) + + if method.Type.NumResults() == 0 { + fn.Body.Append(call) + return + } + + ret := ir.NewReturnStmt(pos, nil) + ret.Results = []ir.Node{call} + fn.Body.Append(ret) +} + +func setBasePos(pos src.XPos) { + // Set the position for any error messages we might print (e.g. too large types). + base.Pos = pos +} + +// dictParamName is the name of the synthetic dictionary parameter +// added to shaped functions. +// +// N.B., this variable name is known to Delve: +// https://github.com/go-delve/delve/blob/cb91509630529e6055be845688fd21eb89ae8714/pkg/proc/eval.go#L28 +const dictParamName = ".dict" + +// shapeSig returns a copy of fn's signature, except adding a +// dictionary parameter and promoting the receiver parameter (if any) +// to a normal parameter. +// +// The parameter types.Fields are all copied too, so their Nname +// fields can be initialized for use by the shape function. +func shapeSig(fn *ir.Func, dict *readerDict) *types.Type { + sig := fn.Nname.Type() + oldRecv := sig.Recv() + + var recv *types.Field + if oldRecv != nil { + recv = types.NewField(oldRecv.Pos, oldRecv.Sym, oldRecv.Type) + } + + params := make([]*types.Field, 1+sig.Params().Fields().Len()) + params[0] = types.NewField(fn.Pos(), fn.Sym().Pkg.Lookup(dictParamName), types.NewPtr(dict.varType())) + for i, param := range sig.Params().Fields().Slice() { + d := types.NewField(param.Pos, param.Sym, param.Type) + d.SetIsDDD(param.IsDDD()) + params[1+i] = d + } + + results := make([]*types.Field, sig.Results().Fields().Len()) + for i, result := range sig.Results().Fields().Slice() { + results[i] = types.NewField(result.Pos, result.Sym, result.Type) + } + + return types.NewSignature(types.LocalPkg, recv, nil, params, results) +} diff --git a/src/cmd/compile/internal/noder/sizes.go b/src/cmd/compile/internal/noder/sizes.go index 23f206267547f4..107f4d0adfdf93 100644 --- a/src/cmd/compile/internal/noder/sizes.go +++ b/src/cmd/compile/internal/noder/sizes.go @@ -25,6 +25,17 @@ func (s *gcSizes) Alignof(T types2.Type) int64 { // is the same as unsafe.Alignof(x[0]), but at least 1." return s.Alignof(t.Elem()) case *types2.Struct: + if t.NumFields() == 0 && types2.IsSyncAtomicAlign64(T) { + // Special case: sync/atomic.align64 is an + // empty struct we recognize as a signal that + // the struct it contains must be + // 64-bit-aligned. + // + // This logic is equivalent to the logic in + // cmd/compile/internal/types/size.go:calcStructOffset + return 8 + } + // spec: "For a variable x of struct type: unsafe.Alignof(x) // is the largest of the values unsafe.Alignof(x.f) for each // field f of x, but at least 1." @@ -71,7 +82,7 @@ func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 { for i, f := range fields { typ := f.Type() a := s.Alignof(typ) - o = types.Rnd(o, a) + o = types.RoundUp(o, a) offsets[i] = o o += s.Sizeof(typ) } @@ -123,7 +134,7 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 { } // gc: Size includes alignment padding. - return types.Rnd(offsets[n-1]+last, s.Alignof(t)) + return types.RoundUp(offsets[n-1]+last, s.Alignof(t)) case *types2.Interface: return int64(types.PtrSize) * 2 case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature: @@ -134,6 +145,7 @@ func (s *gcSizes) Sizeof(T types2.Type) int64 { } var basicSizes = [...]byte{ + types2.Invalid: 1, types2.Bool: 1, types2.Int8: 1, types2.Int16: 2, diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 3ebc8dff6d8432..5a41d2f1f0f732 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -8,272 +8,745 @@ package noder import ( - "bytes" "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/objw" + "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/obj" "cmd/internal/src" "fmt" - "strings" + "go/constant" ) -// For catching problems as we add more features -// TODO(danscales): remove assertions or replace with base.FatalfAt() +// Enable extra consistency checks. +const doubleCheck = false + func assert(p bool) { - if !p { - panic("assertion failed") + base.Assert(p) +} + +// For outputting debug information on dictionary format and instantiated dictionaries +// (type arg, derived types, sub-dictionary, and itab entries). +var infoPrintMode = false + +func infoPrint(format string, a ...interface{}) { + if infoPrintMode { + fmt.Printf(format, a...) } } -// stencil scans functions for instantiated generic function calls and creates the -// required instantiations for simple generic functions. It also creates -// instantiated methods for all fully-instantiated generic types that have been -// encountered already or new ones that are encountered during the stenciling -// process. -func (g *irgen) stencil() { - g.target.Stencils = make(map[*types.Sym]*ir.Func) +var geninst genInst +func BuildInstantiations() { + geninst.instInfoMap = make(map[*types.Sym]*instInfo) + geninst.buildInstantiations() + geninst.instInfoMap = nil +} + +// buildInstantiations scans functions for generic function calls and methods, and +// creates the required instantiations. It also creates instantiated methods for all +// fully-instantiated generic types that have been encountered already or new ones +// that are encountered during the instantiation process. It scans all declarations +// in typecheck.Target.Decls first, before scanning any new instantiations created. +func (g *genInst) buildInstantiations() { // Instantiate the methods of instantiated generic types that we have seen so far. g.instantiateMethods() - // Don't use range(g.target.Decls) - we also want to process any new instantiated - // functions that are created during this loop, in order to handle generic - // functions calling other generic functions. - for i := 0; i < len(g.target.Decls); i++ { - decl := g.target.Decls[i] - - // Look for function instantiations in bodies of non-generic - // functions or in global assignments (ignore global type and - // constant declarations). - switch decl.Op() { - case ir.ODCLFUNC: - if decl.Type().HasTParam() { - // Skip any generic functions - continue - } - // transformCall() below depends on CurFunc being set. - ir.CurFunc = decl.(*ir.Func) - - case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP: - // These are all the various kinds of global assignments, - // whose right-hand-sides might contain a function - // instantiation. + // Scan all currentdecls for call to generic functions/methods. + n := len(typecheck.Target.Decls) + for i := 0; i < n; i++ { + g.scanForGenCalls(typecheck.Target.Decls[i]) + } - default: - // The other possible ops at the top level are ODCLCONST - // and ODCLTYPE, which don't have any function - // instantiations. - continue - } - - // For all non-generic code, search for any function calls using - // generic function instantiations. Then create the needed - // instantiated function if it hasn't been created yet, and change - // to calling that function directly. - modified := false - foundFuncInst := false - ir.Visit(decl, func(n ir.Node) { - if n.Op() == ir.OFUNCINST { - // We found a function instantiation that is not - // immediately called. - foundFuncInst = true - } - if n.Op() != ir.OCALL || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST { - return - } + // Scan all new instantiations created due to g.instantiateMethods() and the + // scan of current decls. This loop purposely runs until no new + // instantiations are created. + for i := 0; i < len(g.newInsts); i++ { + g.scanForGenCalls(g.newInsts[i]) + } + + g.finalizeSyms() + + // All the instantiations and dictionaries have been created. Now go through + // each new instantiation and transform the various operations that need to make + // use of their dictionary. + l := len(g.newInsts) + for _, fun := range g.newInsts { + info := g.instInfoMap[fun.Sym()] + g.dictPass(info) + if doubleCheck { + ir.Visit(info.fun, func(n ir.Node) { + if n.Op() != ir.OCONVIFACE { + return + } + c := n.(*ir.ConvExpr) + if c.X.Type().HasShape() && !c.X.Type().IsInterface() { + ir.Dump("BAD FUNCTION", info.fun) + ir.Dump("BAD CONVERSION", c) + base.Fatalf("converting shape type to interface") + } + }) + } + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun) + } + } + assert(l == len(g.newInsts)) + g.newInsts = nil +} + +// scanForGenCalls scans a single function (or global assignment), looking for +// references to generic functions/methods. At each such reference, it creates any +// required instantiation and transforms the reference. +func (g *genInst) scanForGenCalls(decl ir.Node) { + switch decl.Op() { + case ir.ODCLFUNC: + if decl.Type().HasTParam() { + // Skip any generic functions + return + } + // transformCall() below depends on CurFunc being set. + ir.CurFunc = decl.(*ir.Func) + + case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP: + // These are all the various kinds of global assignments, + // whose right-hand-sides might contain a function + // instantiation. + + default: + // The other possible ops at the top level are ODCLCONST + // and ODCLTYPE, which don't have any function + // instantiations. + return + } + + // Search for any function references using generic function/methods. Then + // create the needed instantiated function if it hasn't been created yet, and + // change to calling that function directly. + modified := false + closureRequired := false + // declInfo will be non-nil exactly if we are scanning an instantiated function + declInfo := g.instInfoMap[decl.Sym()] + + ir.Visit(decl, func(n ir.Node) { + if n.Op() == ir.OFUNCINST { + // generic F, not immediately called + closureRequired = true + } + if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) { + // T.M or x.M, where T or x is generic, but not immediately + // called. Not necessary if the method selected is + // actually for an embedded interface field. + closureRequired = true + } + if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { // We have found a function call using a generic function // instantiation. call := n.(*ir.CallExpr) inst := call.X.(*ir.InstExpr) - st := g.getInstantiationForNode(inst) + nameNode, isMeth := g.getInstNameNode(inst) + targs := typecheck.TypesOf(inst.Targs) + st := g.getInstantiation(nameNode, targs, isMeth).fun + dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + if inst.X.Op() == ir.OMETHVALUE { + fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call) + } else { + fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call) + } + } + + // Transform the Call now, which changes OCALL to + // OCALLFUNC and does typecheckaste/assignconvfn. Do + // it before installing the instantiation, so we are + // checking against non-shape param types in + // typecheckaste. + transformCall(call) + // Replace the OFUNCINST with a direct reference to the // new stenciled function call.X = st.Nname - if inst.X.Op() == ir.OCALLPART { + if inst.X.Op() == ir.OMETHVALUE { // When we create an instantiation of a method // call, we make it a function. So, move the // receiver to be the first arg of the function // call. - withRecv := make([]ir.Node, len(call.Args)+1) - dot := inst.X.(*ir.SelectorExpr) - withRecv[0] = dot.X - copy(withRecv[1:], call.Args) - call.Args = withRecv + call.Args.Prepend(inst.X.(*ir.SelectorExpr).X) } + + // Add dictionary to argument list. + call.Args.Prepend(dictValue) + modified = true + } + if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 { + // Method call on a generic type, which was instantiated by stenciling. + // Method calls on explicitly instantiated types will have an OFUNCINST + // and are handled above. + call := n.(*ir.CallExpr) + meth := call.X.(*ir.SelectorExpr) + targs := deref(meth.Type().Recv().Type).RParams() + + t := meth.X.Type() + baseType := deref(t).OrigType() + var gf *ir.Name + for _, m := range baseType.Methods().Slice() { + if meth.Sel == m.Sym { + gf = m.Nname.(*ir.Name) + break + } + } + // Transform the Call now, which changes OCALL // to OCALLFUNC and does typecheckaste/assignconvfn. transformCall(call) + + st := g.getInstantiation(gf, targs, true).fun + dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true) + if hasShapeTypes(targs) { + // We have to be using a subdictionary, since this is + // a generic method call. + assert(usingSubdict) + } else { + // We should use main dictionary, because the receiver is + // an instantiation already, see issue #53406. + assert(!usingSubdict) + } + + // Transform to a function call, by appending the + // dictionary and the receiver to the args. + call.SetOp(ir.OCALLFUNC) + call.X = st.Nname + call.Args.Prepend(dictValue, meth.X) modified = true - }) - - // If we found an OFUNCINST without a corresponding call in the - // above decl, then traverse the nodes of decl again (with - // EditChildren rather than Visit), where we actually change the - // OFUNCINST node to an ONAME for the instantiated function. - // EditChildren is more expensive than Visit, so we only do this - // in the infrequent case of an OFUNCINSt without a corresponding - // call. - if foundFuncInst { - var edit func(ir.Node) ir.Node - edit = func(x ir.Node) ir.Node { - if x.Op() == ir.OFUNCINST { - st := g.getInstantiationForNode(x.(*ir.InstExpr)) - return st.Nname + } + }) + + // If we found a reference to a generic instantiation that wasn't an + // immediate call, then traverse the nodes of decl again (with + // EditChildren rather than Visit), where we actually change the + // reference to the instantiation to a closure that captures the + // dictionary, then does a direct call. + // EditChildren is more expensive than Visit, so we only do this + // in the infrequent case of an OFUNCINST without a corresponding + // call. + if closureRequired { + modified = true + var edit func(ir.Node) ir.Node + var outer *ir.Func + if f, ok := decl.(*ir.Func); ok { + outer = f + } + edit = func(x ir.Node) ir.Node { + if x.Op() == ir.OFUNCINST { + child := x.(*ir.InstExpr).X + if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE { + // Call EditChildren on child (x.X), + // not x, so that we don't do + // buildClosure() on the + // METHEXPR/METHVALUE nodes as well. + ir.EditChildren(child, edit) + return g.buildClosure(outer, x) } - ir.EditChildren(x, edit) - return x } - edit(decl) + ir.EditChildren(x, edit) + switch { + case x.Op() == ir.OFUNCINST: + return g.buildClosure(outer, x) + case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) && + len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && + !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type): + return g.buildClosure(outer, x) + } + return x } - if base.Flag.W > 1 && modified { - ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl) + edit(decl) + } + if base.Flag.W > 1 && modified { + ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl) + } + ir.CurFunc = nil + // We may have seen new fully-instantiated generic types while + // instantiating any needed functions/methods in the above + // function. If so, instantiate all the methods of those types + // (which will then lead to more function/methods to scan in the loop). + g.instantiateMethods() +} + +// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR/OMETHVALUE +// of generic type. outer is the containing function (or nil if closure is +// in a global assignment instead of a function). +func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node { + pos := x.Pos() + var target *ir.Func // target instantiated function/method + var dictValue ir.Node // dictionary to use + var rcvrValue ir.Node // receiver, if a method value + typ := x.Type() // type of the closure + var outerInfo *instInfo + if outer != nil { + outerInfo = g.instInfoMap[outer.Sym()] + } + usingSubdict := false + valueMethod := false + if x.Op() == ir.OFUNCINST { + inst := x.(*ir.InstExpr) + + // Type arguments we're instantiating with. + targs := typecheck.TypesOf(inst.Targs) + + // Find the generic function/method. + var gf *ir.Name + if inst.X.Op() == ir.ONAME { + // Instantiating a generic function call. + gf = inst.X.(*ir.Name) + } else if inst.X.Op() == ir.OMETHVALUE { + // Instantiating a method value x.M. + se := inst.X.(*ir.SelectorExpr) + rcvrValue = se.X + gf = se.Selection.Nname.(*ir.Name) + } else { + panic("unhandled") + } + + // target is the instantiated function we're trying to call. + // For functions, the target expects a dictionary as its first argument. + // For method values, the target expects a dictionary and the receiver + // as its first two arguments. + // dictValue is the value to use for the dictionary argument. + target = g.getInstantiation(gf, targs, rcvrValue != nil).fun + dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + if rcvrValue == nil { + fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X) + } else { + fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X) + } + } + } else { // ir.OMETHEXPR or ir.METHVALUE + // Method expression T.M where T is a generic type. + se := x.(*ir.SelectorExpr) + if x.Op() == ir.OMETHVALUE { + rcvrValue = se.X + } + + // se.X.Type() is the top-level type of the method expression. To + // correctly handle method expressions involving embedded fields, + // look up the generic method below using the type of the receiver + // of se.Selection, since that will be the type that actually has + // the method. + recv := deref(se.Selection.Type.Recv().Type) + targs := recv.RParams() + if len(targs) == 0 { + // The embedded type that actually has the method is not + // actually generic, so no need to build a closure. + return x + } + baseType := recv.OrigType() + var gf *ir.Name + for _, m := range baseType.Methods().Slice() { + if se.Sel == m.Sym { + gf = m.Nname.(*ir.Name) + break + } + } + if !gf.Type().Recv().Type.IsPtr() { + // Remember if value method, so we can detect (*T).M case. + valueMethod = true + } + target = g.getInstantiation(gf, targs, true).fun + dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true) + if infoPrintMode { + dictkind := "Main dictionary" + if usingSubdict { + dictkind = "Sub-dictionary" + } + fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x) } - ir.CurFunc = nil - // We may have seen new fully-instantiated generic types while - // instantiating any needed functions/methods in the above - // function. If so, instantiate all the methods of those types - // (which will then lead to more function/methods to scan in the loop). - g.instantiateMethods() } -} + // Build a closure to implement a function instantiation. + // + // func f[T any] (int, int) (int, int) { ...whatever... } + // + // Then any reference to f[int] not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // func(a0, a1 int) (r0, r1 int) { + // return .inst.f[int](.dictN, a0, a1) + // } + // + // Similarly for method expressions, + // + // type g[T any] .... + // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... } + // + // Any reference to g[int].f not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // func(rcvr g[int], a0, a1 int) (r0, r1 int) { + // return .inst.g[int].f(.dictN, rcvr, a0, a1) + // } + // + // Also method values + // + // var x g[int] + // + // Any reference to x.f not directly called gets rewritten to + // + // .dictN := ... dictionary to use ... + // x2 := x + // func(a0, a1 int) (r0, r1 int) { + // return .inst.g[int].f(.dictN, x2, a0, a1) + // } + + // Make a new internal function. + fn, formalParams, formalResults := startClosure(pos, outer, typ) + fn.SetWrapper(true) // See issue 52237 + + // This is the dictionary we want to use. + // It may be a constant, it may be the outer functions's dictionary, or it may be + // a subdictionary acquired from the outer function's dictionary. + // For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary + // read from the outer function's dictionary. + var dictVar *ir.Name + var dictAssign *ir.AssignStmt + if outer != nil { + dictVar = ir.NewNameAt(pos, closureSym(outer, typecheck.LocalDictName, g.dnum)) + g.dnum++ + dictVar.Class = ir.PAUTO + typed(types.Types[types.TUINTPTR], dictVar) + dictVar.Curfn = outer + dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue) + dictAssign.SetTypecheck(1) + dictVar.Defn = dictAssign + outer.Dcl = append(outer.Dcl, dictVar) + } + // assign the receiver to a temporary. + var rcvrVar *ir.Name + var rcvrAssign ir.Node + if rcvrValue != nil { + rcvrVar = ir.NewNameAt(pos, closureSym(outer, ".rcvr", g.dnum)) + g.dnum++ + typed(rcvrValue.Type(), rcvrVar) + rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue) + rcvrAssign.SetTypecheck(1) + rcvrVar.Defn = rcvrAssign + if outer == nil { + rcvrVar.Class = ir.PEXTERN + typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign) + typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar) + } else { + rcvrVar.Class = ir.PAUTO + rcvrVar.Curfn = outer + outer.Dcl = append(outer.Dcl, rcvrVar) + } + } + + // Build body of closure. This involves just calling the wrapped function directly + // with the additional dictionary argument. -// instantiateMethods instantiates all the methods of all fully-instantiated -// generic types that have been added to g.instTypeList. -func (g *irgen) instantiateMethods() { - for i := 0; i < len(g.instTypeList); i++ { - typ := g.instTypeList[i] - // Get the base generic type by looking up the symbol of the - // generic (uninstantiated) name. - baseSym := typ.Sym().Pkg.Lookup(genericTypeName(typ.Sym())) - baseType := baseSym.Def.(*ir.Name).Type() - for j, m := range typ.Methods().Slice() { - name := m.Nname.(*ir.Name) - targs := make([]ir.Node, len(typ.RParams())) - for k, targ := range typ.RParams() { - targs[k] = ir.TypeNode(targ) + // First, figure out the dictionary argument. + var dict2Var ir.Node + if usingSubdict { + // Capture sub-dictionary calculated in the outer function + dict2Var = ir.CaptureName(pos, fn, dictVar) + typed(types.Types[types.TUINTPTR], dict2Var) + } else { + // Static dictionary, so can be used directly in the closure + dict2Var = dictValue + } + // Also capture the receiver variable. + var rcvr2Var *ir.Name + if rcvrValue != nil { + rcvr2Var = ir.CaptureName(pos, fn, rcvrVar) + } + + // Build arguments to call inside the closure. + var args []ir.Node + + // First the dictionary argument. + args = append(args, dict2Var) + // Then the receiver. + if rcvrValue != nil { + args = append(args, rcvr2Var) + } + // Then all the other arguments (including receiver for method expressions). + for i := 0; i < typ.NumParams(); i++ { + if x.Op() == ir.OMETHEXPR && i == 0 { + // If we are doing a method expression, we need to + // explicitly traverse any embedded fields in the receiver + // argument in order to call the method instantiation. + arg0 := formalParams[0].Nname.(ir.Node) + arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X + if valueMethod && arg0.Type().IsPtr() { + // For handling the (*T).M case: if we have a pointer + // receiver after following all the embedded fields, + // but it's a value method, add a star operator. + arg0 = ir.NewStarExpr(arg0.Pos(), arg0) } - baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) - name.Func = g.getInstantiation(baseNname, targs, true) + args = append(args, arg0) + } else { + args = append(args, formalParams[i].Nname.(*ir.Name)) } } - g.instTypeList = nil + // Build call itself. + var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args) + innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic() + if len(formalResults) > 0 { + innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall}) + } + // Finish building body of closure. + ir.CurFunc = fn + // TODO: set types directly here instead of using typecheck.Stmt + typecheck.Stmt(innerCall) + ir.CurFunc = nil + fn.Body = []ir.Node{innerCall} + + // We're all done with the captured dictionary (and receiver, for method values). + ir.FinishCaptureNames(pos, outer, fn) + + // Make a closure referencing our new internal function. + c := ir.UseClosure(fn.OClosure, typecheck.Target) + var init []ir.Node + if outer != nil { + init = append(init, dictAssign) + } + if rcvrValue != nil { + init = append(init, rcvrAssign) + } + return ir.InitExpr(init, c) } -// genericSym returns the name of the base generic type for the type named by -// sym. It simply returns the name obtained by removing everything after the -// first bracket ("["). -func genericTypeName(sym *types.Sym) string { - return sym.Name[0:strings.Index(sym.Name, "[")] +// instantiateMethods instantiates all the methods (and associated dictionaries) of +// all fully-instantiated generic types that have been added to typecheck.instTypeList. +// It continues until no more types are added to typecheck.instTypeList. +func (g *genInst) instantiateMethods() { + for { + instTypeList := typecheck.GetInstTypeList() + if len(instTypeList) == 0 { + break + } + typecheck.ClearInstTypeList() + for _, typ := range instTypeList { + assert(!typ.HasShape()) + // Mark runtime type as needed, since this ensures that the + // compiler puts out the needed DWARF symbols, when this + // instantiated type has a different package from the local + // package. + typecheck.NeedRuntimeType(typ) + // Lookup the method on the base generic type, since methods may + // not be set on imported instantiated types. + baseType := typ.OrigType() + for j, _ := range typ.Methods().Slice() { + if baseType.Methods().Slice()[j].Nointerface() { + typ.Methods().Slice()[j].SetNointerface(true) + } + baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) + // Eagerly generate the instantiations and dictionaries that implement these methods. + // We don't use the instantiations here, just generate them (and any + // further instantiations those generate, etc.). + // Note that we don't set the Func for any methods on instantiated + // types. Their signatures don't match so that would be confusing. + // Direct method calls go directly to the instantiations, implemented above. + // Indirect method calls use wrappers generated in reflectcall. Those wrappers + // will use these instantiations if they are needed (for interface tables or reflection). + _ = g.getInstantiation(baseNname, typ.RParams(), true) + _ = g.getDictionarySym(baseNname, typ.RParams(), true) + } + } + } } -// getInstantiationForNode returns the function/method instantiation for a -// InstExpr node inst. -func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) *ir.Func { +// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated. +func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) { if meth, ok := inst.X.(*ir.SelectorExpr); ok { - return g.getInstantiation(meth.Selection.Nname.(*ir.Name), inst.Targs, true) + return meth.Selection.Nname.(*ir.Name), true } else { - return g.getInstantiation(inst.X.(*ir.Name), inst.Targs, false) + return inst.X.(*ir.Name), false } } -// getInstantiation gets the instantiantion of the function or method nameNode -// with the type arguments targs. If the instantiated function is not already +// getDictOrSubdict returns, for a method/function call or reference (node n) in an +// instantiation (described by instInfo), a node which is accessing a sub-dictionary +// or main/static dictionary, as needed, and also returns a boolean indicating if a +// sub-dictionary was accessed. nameNode is the particular function or method being +// called/referenced, and targs are the type arguments. +func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) { + var dict ir.Node + usingSubdict := false + if declInfo != nil { + entry := -1 + for i, de := range declInfo.dictInfo.subDictCalls { + if n == de.callNode { + entry = declInfo.dictInfo.startSubDict + i + break + } + } + // If the entry is not found, it may be that this node did not have + // any type args that depend on type params, so we need a main + // dictionary, not a sub-dictionary. + if entry >= 0 { + dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen) + usingSubdict = true + } + } + if !usingSubdict { + dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth) + } + return dict, usingSubdict +} + +// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded +// yet. If so, it imports the body. +func checkFetchBody(nameNode *ir.Name) { + if nameNode.Func.Body == nil && nameNode.Func.Inl != nil { + // If there is no body yet but Func.Inl exists, then we can + // import the whole generic body. + assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg) + typecheck.ImportBody(nameNode.Func) + assert(nameNode.Func.Inl.Body != nil) + nameNode.Func.Body = nameNode.Func.Inl.Body + nameNode.Func.Dcl = nameNode.Func.Inl.Dcl + } +} + +// getInstantiation gets the instantiation and dictionary of the function or method nameNode +// with the type arguments shapes. If the instantiated function is not already // cached, then it calls genericSubst to create the new instantiation. -func (g *irgen) getInstantiation(nameNode *ir.Name, targs []ir.Node, isMeth bool) *ir.Func { - sym := makeInstName(nameNode.Sym(), targs, isMeth) - st := g.target.Stencils[sym] - if st == nil { - // If instantiation doesn't exist yet, create it and add - // to the list of decls. - st = g.genericSubst(sym, nameNode, targs, isMeth) - g.target.Stencils[sym] = st - g.target.Decls = append(g.target.Decls, st) - if base.Flag.W > 1 { - ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) +func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo { + if nameNode.Func == nil { + // If nameNode.Func is nil, this must be a reference to a method of + // an imported instantiated type. We will have already called + // g.instantiateMethods() on the fully-instantiated type, so + // g.instInfoMap[sym] will be non-nil below. + rcvr := nameNode.Type().Recv() + if rcvr == nil || !deref(rcvr.Type).IsFullyInstantiated() { + base.FatalfAt(nameNode.Pos(), "Unexpected function instantiation %v with no body", nameNode) } + } else { + checkFetchBody(nameNode) } - return st -} - -// makeInstName makes the unique name for a stenciled generic function or method, -// based on the name of the function fy=nsym and the targs. It replaces any -// existing bracket type list in the name. makeInstName asserts that fnsym has -// brackets in its name if and only if hasBrackets is true. -// TODO(danscales): remove the assertions and the hasBrackets argument later. -// -// Names of declared generic functions have no brackets originally, so hasBrackets -// should be false. Names of generic methods already have brackets, since the new -// type parameter is specified in the generic type of the receiver (e.g. func -// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set. -// -// The standard naming is something like: 'genFn[int,bool]' for functions and -// '(*genType[int,bool]).methodName' for methods -func makeInstName(fnsym *types.Sym, targs []ir.Node, hasBrackets bool) *types.Sym { - b := bytes.NewBufferString("") - name := fnsym.Name - i := strings.Index(name, "[") - assert(hasBrackets == (i >= 0)) - if i >= 0 { - b.WriteString(name[0:i]) + + var tparams []*types.Type + if isMeth { + // Get the type params from the method receiver (after skipping + // over any pointer) + recvType := nameNode.Type().Recv().Type + recvType = deref(recvType) + if recvType.IsFullyInstantiated() { + // Get the type of the base generic type, so we get + // its original typeparams. + recvType = recvType.OrigType() + } + tparams = recvType.RParams() } else { - b.WriteString(name) + fields := nameNode.Type().TParams().Fields().Slice() + tparams = make([]*types.Type, len(fields)) + for i, f := range fields { + tparams[i] = f.Type + } } - b.WriteString("[") - for i, targ := range targs { - if i > 0 { - b.WriteString(",") + + // Convert any non-shape type arguments to their shape, so we can reduce the + // number of instantiations we have to generate. You can actually have a mix + // of shape and non-shape arguments, because of inferred or explicitly + // specified concrete type args. + s1 := make([]*types.Type, len(shapes)) + for i, t := range shapes { + var tparam *types.Type + // Shapes are grouped differently for structural types, so we + // pass the type param to Shapify(), so we can distinguish. + tparam = tparams[i] + if !t.IsShape() { + s1[i] = typecheck.Shapify(t, i, tparam) + } else { + // Already a shape, but make sure it has the correct index. + s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam) } - b.WriteString(targ.Type().String()) } - b.WriteString("]") - if i >= 0 { - i2 := strings.Index(name[i:], "]") - assert(i2 >= 0) - b.WriteString(name[i+i2+1:]) + shapes = s1 + + sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, false, isMeth) + info := g.instInfoMap[sym] + if info == nil { + // If instantiation doesn't exist yet, create it and add + // to the list of decls. + info = &instInfo{ + dictInfo: &dictInfo{}, + } + info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type) + + if sym.Def != nil { + // This instantiation must have been imported from another + // package (because it was needed for inlining), so we should + // not re-generate it and have conflicting definitions for the + // symbol (issue #50121). It will have already gone through the + // dictionary transformations of dictPass, so we don't actually + // need the info.dictParam and info.shapeToBound info filled in + // below. We just set the imported instantiation as info.fun. + assert(sym.Pkg != types.LocalPkg) + info.fun = sym.Def.(*ir.Name).Func + assert(info.fun != nil) + g.instInfoMap[sym] = info + return info + } + + // genericSubst fills in info.dictParam and info.shapeToBound. + st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info) + info.fun = st + g.instInfoMap[sym] = info + + // getInstInfo fills in info.dictInfo. + g.getInstInfo(st, shapes, info) + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) + } + + // This ensures that the linker drops duplicates of this instantiation. + // All just works! + st.SetDupok(true) + typecheck.Target.Decls = append(typecheck.Target.Decls, st) + g.newInsts = append(g.newInsts, st) } - return typecheck.Lookup(b.String()) + return info } // Struct containing info needed for doing the substitution as we create the // instantiation of a generic function with specified type arguments. type subster struct { - g *irgen - isMethod bool // If a method is being instantiated - newf *ir.Func // Func node for the new stenciled function - tparams []*types.Field - targs []ir.Node - // The substitution map from name nodes in the generic function to the - // name nodes in the new stenciled function. - vars map[*ir.Name]*ir.Name + g *genInst + isMethod bool // If a method is being instantiated + newf *ir.Func // Func node for the new stenciled function + ts typecheck.Tsubster + info *instInfo // Place to put extra info in the instantiation + skipClosure bool // Skip substituting closures + + // Map from non-nil, non-ONAME node n to slice of all m, where m.Defn = n + defnMap map[ir.Node][]**ir.Name } // genericSubst returns a new function with name newsym. The function is an // instantiation of a generic function or method specified by namedNode with type -// args targs. For a method with a generic receiver, it returns an instantiated -// function type where the receiver becomes the first parameter. Otherwise the -// instantiated method would still need to be transformed by later compiler -// phases. -func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []ir.Node, isMethod bool) *ir.Func { - var tparams []*types.Field - if isMethod { - // Get the type params from the method receiver (after skipping - // over any pointer) - recvType := nameNode.Type().Recv().Type - recvType = deref(recvType) - tparams = make([]*types.Field, len(recvType.RParams())) - for i, rparam := range recvType.RParams() { - tparams[i] = types.NewField(src.NoXPos, nil, rparam) - } - } else { - tparams = nameNode.Type().TParams().Fields().Slice() - } +// args shapes. For a method with a generic receiver, it returns an instantiated +// function type where the receiver becomes the first parameter. For either a generic +// method or function, a dictionary parameter is the added as the very first +// parameter. genericSubst fills in info.dictParam and info.shapeToBound. +func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func { gf := nameNode.Func // Pos of the instantiated function is same as the generic function newf := ir.NewFunc(gf.Pos()) newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation. + newf.Endlineno = gf.Endlineno newf.Nname = ir.NewNameAt(gf.Pos(), newsym) newf.Nname.Func = newf newf.Nname.Defn = newf @@ -283,145 +756,297 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []ir.No // depend on ir.CurFunc being set. ir.CurFunc = newf - assert(len(tparams) == len(targs)) + assert(len(tparams) == len(shapes)) subst := &subster{ g: g, isMethod: isMethod, newf: newf, - tparams: tparams, - targs: targs, - vars: make(map[*ir.Name]*ir.Name), + info: info, + ts: typecheck.Tsubster{ + Tparams: tparams, + Targs: shapes, + Vars: make(map[*ir.Name]*ir.Name), + }, + defnMap: make(map[ir.Node][]**ir.Name), } - newf.Dcl = make([]*ir.Name, len(gf.Dcl)) - for i, n := range gf.Dcl { - newf.Dcl[i] = subst.node(n).(*ir.Name) + newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1) + + // Create the needed dictionary param + dictionarySym := newsym.Pkg.Lookup(typecheck.LocalDictName) + dictionaryType := types.Types[types.TUINTPTR] + dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym) + typed(dictionaryType, dictionaryName) + dictionaryName.Class = ir.PPARAM + dictionaryName.Curfn = newf + newf.Dcl = append(newf.Dcl, dictionaryName) + for _, n := range gf.Dcl { + if n.Sym().Name == typecheck.LocalDictName { + panic("already has dictionary") + } + newf.Dcl = append(newf.Dcl, subst.localvar(n)) } + dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType) + dictionaryArg.Nname = dictionaryName + info.dictParam = dictionaryName - // Ugly: we have to insert the Name nodes of the parameters/results into + // We add the dictionary as the first parameter in the function signature. + // We also transform a method type to the corresponding function type + // (make the receiver be the next parameter after the dictionary). + oldt := nameNode.Type() + var args []*types.Field + args = append(args, dictionaryArg) + args = append(args, oldt.Recvs().FieldSlice()...) + args = append(args, oldt.Params().FieldSlice()...) + + // Replace the types in the function signature via subst.fields. + // Ugly: also, we have to insert the Name nodes of the parameters/results into // the function type. The current function type has no Nname fields set, // because it came via conversion from the types2 type. - oldt := nameNode.Type() - // We also transform a generic method type to the corresponding - // instantiated function type where the receiver is the first parameter. newt := types.NewSignature(oldt.Pkg(), nil, nil, - subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl), + subst.fields(ir.PPARAM, args, newf.Dcl), subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl)) - newf.Nname.SetType(newt) + typed(newt, newf.Nname) ir.MarkFunc(newf.Nname) newf.SetTypecheck(1) - newf.Nname.SetTypecheck(1) // Make sure name/type of newf is set before substituting the body. newf.Body = subst.list(gf.Body) + if len(newf.Body) == 0 { + // Ensure the body is nonempty, for issue 49524. + // TODO: have some other way to detect the difference between + // a function declared with no body, vs. one with an empty body? + newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil)) + } + + if len(subst.defnMap) > 0 { + base.Fatalf("defnMap is not empty") + } + + for i, tp := range tparams { + info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound()) + } + ir.CurFunc = savef - return newf + return subst.newf +} + +// localvar creates a new name node for the specified local variable and enters it +// in subst.vars. It substitutes type arguments for type parameters in the type of +// name as needed. +func (subst *subster) localvar(name *ir.Name) *ir.Name { + m := ir.NewNameAt(name.Pos(), name.Sym()) + if name.IsClosureVar() { + m.SetIsClosureVar(true) + } + m.SetType(subst.ts.Typ(name.Type())) + m.BuiltinOp = name.BuiltinOp + m.Curfn = subst.newf + m.Class = name.Class + assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC) + m.Func = name.Func + subst.ts.Vars[name] = m + m.SetTypecheck(1) + m.DictIndex = name.DictIndex + if name.Defn != nil { + if name.Defn.Op() == ir.ONAME { + // This is a closure variable, so its Defn is the outer + // captured variable, which has already been substituted. + m.Defn = subst.node(name.Defn) + } else { + // The other values of Defn are nodes in the body of the + // function, so just remember the mapping so we can set Defn + // properly in node() when we create the new body node. We + // always call localvar() on all the local variables before + // we substitute the body. + slice := subst.defnMap[name.Defn] + subst.defnMap[name.Defn] = append(slice, &m) + } + } + if name.Outer != nil { + m.Outer = subst.node(name.Outer).(*ir.Name) + } + + return m +} + +// getDictionaryEntry gets the i'th entry in the dictionary dict. +func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node { + // Convert dictionary to *[N]uintptr + // All entries in the dictionary are pointers. They all point to static data, though, so we + // treat them as uintptrs so the GC doesn't need to keep track of them. + d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict) + d.SetTypecheck(1) + d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d) + d.SetTypecheck(1) + types.CheckSize(d.Type().Elem()) + + // Load entry i out of the dictionary. + deref := ir.NewStarExpr(pos, d) + typed(d.Type().Elem(), deref) + idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to? + typed(types.Types[types.TUINTPTR], idx) + r := ir.NewIndexExpr(pos, deref, idx) + typed(types.Types[types.TUINTPTR], r) + return r +} + +// getDictionaryEntryAddr gets the address of the i'th entry in dictionary dict. +func getDictionaryEntryAddr(pos src.XPos, dict *ir.Name, i int, size int) ir.Node { + a := ir.NewAddrExpr(pos, getDictionaryEntry(pos, dict, i, size)) + typed(types.Types[types.TUINTPTR].PtrTo(), a) + return a } -// node is like DeepCopy(), but creates distinct ONAME nodes, and also descends -// into closures. It substitutes type arguments for type parameters in all the new -// nodes. +// getDictionaryType returns a *runtime._type from the dictionary entry i (which +// refers to a type param or a derived type that uses type params). It uses the +// specified dictionary dictParam, rather than the one in info.dictParam. +func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node { + if i < 0 || i >= info.dictInfo.startSubDict { + base.Fatalf(fmt.Sprintf("bad dict index %d", i)) + } + + r := getDictionaryEntry(pos, dictParam, i, info.dictInfo.startSubDict) + // change type of retrieved dictionary entry to *byte, which is the + // standard typing of a *runtime._type in the compiler + typed(types.Types[types.TUINT8].PtrTo(), r) + return r +} + +// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and +// also descends into closures. It substitutes type arguments for type parameters in +// all the new nodes and does the transformations that were delayed on the generic +// function. func (subst *subster) node(n ir.Node) ir.Node { // Use closure to capture all state needed by the ir.EditChildren argument. var edit func(ir.Node) ir.Node edit = func(x ir.Node) ir.Node { + // Analogous to ir.SetPos() at beginning of typecheck.typecheck() - + // allows using base.Pos during the transform functions, just like + // the tc*() functions. + ir.SetPos(x) switch x.Op() { case ir.OTYPE: - return ir.TypeNode(subst.typ(x.Type())) + return ir.TypeNode(subst.ts.Typ(x.Type())) case ir.ONAME: - name := x.(*ir.Name) - if v := subst.vars[name]; v != nil { + if v := subst.ts.Vars[x.(*ir.Name)]; v != nil { return v } - m := ir.NewNameAt(name.Pos(), name.Sym()) - if name.IsClosureVar() { - m.SetIsClosureVar(true) + if ir.IsBlank(x) { + // Special case, because a blank local variable is + // not in the fn.Dcl list. + m := ir.NewNameAt(x.Pos(), ir.BlankNode.Sym()) + return typed(subst.ts.Typ(x.Type()), m) } - t := x.Type() - if t == nil { - assert(name.BuiltinOp != 0) - } else { - newt := subst.typ(t) - m.SetType(newt) - } - m.BuiltinOp = name.BuiltinOp - m.Curfn = subst.newf - m.Class = name.Class - m.Func = name.Func - subst.vars[name] = m - m.SetTypecheck(1) - return m + return x + case ir.ONONAME: + // This handles the identifier in a type switch guard + fallthrough case ir.OLITERAL, ir.ONIL: if x.Sym() != nil { return x } } m := ir.Copy(x) + + slice, ok := subst.defnMap[x] + if ok { + // We just copied a non-ONAME node which was the Defn value + // of a local variable. Set the Defn value of the copied + // local variable to this new Defn node. + for _, ptr := range slice { + (*ptr).Defn = m + } + delete(subst.defnMap, x) + } + if _, isExpr := m.(ir.Expr); isExpr { t := x.Type() if t == nil { - // t can be nil only if this is a call that has no - // return values, so allow that and otherwise give - // an error. + // Check for known cases where t can be nil (call + // that has no return values, and key expressions) + // and otherwise cause a fatal error. _, isCallExpr := m.(*ir.CallExpr) _, isStructKeyExpr := m.(*ir.StructKeyExpr) - if !isCallExpr && !isStructKeyExpr && x.Op() != ir.OPANIC && + _, isKeyExpr := m.(*ir.KeyExpr) + if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC && x.Op() != ir.OCLOSE { - base.Fatalf(fmt.Sprintf("Nil type for %v", x)) + base.FatalfAt(m.Pos(), "Nil type for %v", x) } } else if x.Op() != ir.OCLOSURE { - m.SetType(subst.typ(x.Type())) + m.SetType(subst.ts.Typ(x.Type())) } } - ir.EditChildren(m, edit) - if x.Typecheck() == 3 { - // These are nodes whose transforms were delayed until - // their instantiated type was known. - m.SetTypecheck(1) - if typecheck.IsCmp(x.Op()) { - transformCompare(m.(*ir.BinaryExpr)) - } else { - switch x.Op() { - case ir.OSLICE, ir.OSLICE3: - transformSlice(m.(*ir.SliceExpr)) + old := subst.skipClosure + // For unsafe.{Alignof,Offsetof,Sizeof}, subster will transform them to OLITERAL nodes, + // and discard their arguments. However, their children nodes were already process before, + // thus if they contain any closure, the closure was still be added to package declarations + // queue for processing later. Thus, genInst will fail to generate instantiation for the + // closure because of lacking dictionary information, see issue #53390. + if call, ok := m.(*ir.CallExpr); ok && call.X.Op() == ir.ONAME { + switch call.X.Name().BuiltinOp { + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + subst.skipClosure = true + } + } + ir.EditChildren(m, edit) + subst.skipClosure = old - case ir.OADD: - m = transformAdd(m.(*ir.BinaryExpr)) + m.SetTypecheck(1) - case ir.OINDEX: - transformIndex(m.(*ir.IndexExpr)) + // Do the transformations that we delayed on the generic function + // node, now that we have substituted in the type args. + switch x.Op() { + case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE: + transformCompare(m.(*ir.BinaryExpr)) + + case ir.OSLICE, ir.OSLICE3: + transformSlice(m.(*ir.SliceExpr)) + + case ir.OADD: + m = transformAdd(m.(*ir.BinaryExpr)) + + case ir.OINDEX: + transformIndex(m.(*ir.IndexExpr)) + + case ir.OAS2: + as2 := m.(*ir.AssignListStmt) + transformAssign(as2, as2.Lhs, as2.Rhs) + + case ir.OAS: + as := m.(*ir.AssignStmt) + if as.Y != nil { + // transformAssign doesn't handle the case + // of zeroing assignment of a dcl (rhs[0] is nil). + lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y} + transformAssign(as, lhs, rhs) + as.X, as.Y = lhs[0], rhs[0] + } - case ir.OAS2: - as2 := m.(*ir.AssignListStmt) - transformAssign(as2, as2.Lhs, as2.Rhs) + case ir.OASOP: + as := m.(*ir.AssignOpStmt) + transformCheckAssign(as, as.X) - case ir.OAS: - as := m.(*ir.AssignStmt) - lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y} - transformAssign(as, lhs, rhs) + case ir.ORETURN: + transformReturn(m.(*ir.ReturnStmt)) - case ir.OASOP: - as := m.(*ir.AssignOpStmt) - transformCheckAssign(as, as.X) + case ir.OSEND: + transformSend(m.(*ir.SendStmt)) - case ir.ORETURN: - transformReturn(m.(*ir.ReturnStmt)) + case ir.OSELECT: + transformSelect(m.(*ir.SelectStmt)) - case ir.OSEND: - transformSend(m.(*ir.SendStmt)) + case ir.OCOMPLIT: + transformCompLit(m.(*ir.CompLitExpr)) - default: - base.Fatalf("Unexpected node with Typecheck() == 3") - } - } - } + case ir.OADDR: + transformAddr(m.(*ir.AddrExpr)) - switch x.Op() { case ir.OLITERAL: t := m.Type() if t != x.Type() { @@ -439,18 +1064,17 @@ func (subst *subster) node(n ir.Node) ir.Node { } case ir.OXDOT: - // A method value/call via a type param will have been - // left as an OXDOT. When we see this during stenciling, - // finish the transformation, now that we have the - // instantiated receiver type. We need to do this now, - // since the access/selection to the method for the real - // type is very different from the selection for the type - // param. m will be transformed to an OCALLPART node. It - // will be transformed to an ODOTMETH or ODOTINTER node if - // we find in the OCALL case below that the method value - // is actually called. - transformDot(m.(*ir.SelectorExpr), false) - m.SetTypecheck(1) + // Finish the transformation of an OXDOT, unless this is + // bound call or field access on a type param. A bound call + // or field access on a type param will be transformed during + // the dictPass. Otherwise, m will be transformed to an + // OMETHVALUE node. It will be transformed to an ODOTMETH or + // ODOTINTER node if we find in the OCALL case below that the + // method value is actually called. + mse := m.(*ir.SelectorExpr) + if src := mse.X.Type(); !src.IsShape() { + transformDot(mse, false) + } case ir.OCALL: call := m.(*ir.CallExpr) @@ -458,9 +1082,9 @@ func (subst *subster) node(n ir.Node) ir.Node { case ir.OTYPE: // Transform the conversion, now that we know the // type argument. - m = transformConvCall(m.(*ir.CallExpr)) + m = transformConvCall(call) - case ir.OCALLPART: + case ir.OMETHVALUE, ir.OMETHEXPR: // Redo the transformation of OXDOT, now that we // know the method value is being called. Then // transform the call. @@ -478,14 +1102,7 @@ func (subst *subster) node(n ir.Node) ir.Node { case ir.ONAME: name := call.X.Name() if name.BuiltinOp != ir.OXXX { - switch name.BuiltinOp { - case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND: - // Transform these builtins now that we - // know the type of the args. - m = transformBuiltin(call) - default: - base.FatalfAt(call.Pos(), "Unexpected builtin op") - } + m = transformBuiltin(call) } else { // This is the case of a function value that was a // type parameter (implied to be a function via a @@ -493,54 +1110,125 @@ func (subst *subster) node(n ir.Node) ir.Node { transformCall(call) } - case ir.OCLOSURE: - transformCall(call) - case ir.OFUNCINST: // A call with an OFUNCINST will get transformed // in stencil() once we have created & attached the // instantiation to be called. + // We must transform the arguments of the call now, though, + // so that any needed CONVIFACE nodes are exposed, + // so the dictionary format is correct. + transformEarlyCall(call) + + case ir.OXDOT: + // This is the case of a bound call or a field access + // on a typeparam, which will be handled in the + // dictPass. As with OFUNCINST, we must transform the + // arguments of the call now, so any needed CONVIFACE + // nodes are exposed. + transformEarlyCall(call) + + case ir.ODOTTYPE, ir.ODOTTYPE2: + // These are DOTTYPEs that could get transformed into + // ODYNAMIC DOTTYPEs by the dict pass. default: - base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op())) + // Transform a call for all other values of + // call.X.Op() that don't require any special + // handling. + transformCall(call) + } case ir.OCLOSURE: + if subst.skipClosure { + break + } + // We're going to create a new closure from scratch, so clear m + // to avoid using the ir.Copy by accident until we reassign it. + m = nil + x := x.(*ir.ClosureExpr) // Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and // x.Func.Body. oldfn := x.Func - newfn := ir.NewFunc(oldfn.Pos()) - if oldfn.ClosureCalled() { - newfn.SetClosureCalled(true) - } - newfn.SetIsHiddenClosure(true) - m.(*ir.ClosureExpr).Func = newfn - // Closure name can already have brackets, if it derives - // from a generic method - newsym := makeInstName(oldfn.Nname.Sym(), subst.targs, subst.isMethod) - newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), newsym) - newfn.Nname.Func = newfn - newfn.Nname.Defn = newfn - ir.MarkFunc(newfn.Nname) - newfn.OClosure = m.(*ir.ClosureExpr) + newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil) + ir.NameClosure(newfn.OClosure, subst.newf) saveNewf := subst.newf ir.CurFunc = newfn subst.newf = newfn newfn.Dcl = subst.namelist(oldfn.Dcl) - newfn.ClosureVars = subst.namelist(oldfn.ClosureVars) - typed(subst.typ(oldfn.Nname.Type()), newfn.Nname) - typed(newfn.Nname.Type(), m) + // Make a closure variable for the dictionary of the + // containing function. + cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam) + typed(types.Types[types.TUINTPTR], cdict) + ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn) + newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...) + + // Copy that closure variable to a local one. + // Note: this allows the dictionary to be captured by child closures. + // See issue 47723. + ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(typecheck.LocalDictName)) + typed(types.Types[types.TUINTPTR], ldict) + ldict.Class = ir.PAUTO + ldict.Curfn = newfn + newfn.Dcl = append(newfn.Dcl, ldict) + as := ir.NewAssignStmt(x.Pos(), ldict, cdict) + as.SetTypecheck(1) + ldict.Defn = as + newfn.Body.Append(as) + + // Create inst info for the instantiated closure. The dict + // param is the closure variable for the dictionary of the + // outer function. Since the dictionary is shared, use the + // same dictInfo. + cinfo := &instInfo{ + fun: newfn, + dictParam: ldict, + dictInfo: subst.info.dictInfo, + } + subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo + + typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname) + typed(newfn.Nname.Type(), newfn.OClosure) newfn.SetTypecheck(1) + outerinfo := subst.info + subst.info = cinfo // Make sure type of closure function is set before doing body. - newfn.Body = subst.list(oldfn.Body) + newfn.Body.Append(subst.list(oldfn.Body)...) + subst.info = outerinfo subst.newf = saveNewf ir.CurFunc = saveNewf - subst.g.target.Decls = append(subst.g.target.Decls, newfn) + m = ir.UseClosure(newfn.OClosure, typecheck.Target) + subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func) + m.(*ir.ClosureExpr).SetInit(subst.list(x.Init())) + + case ir.OSWITCH: + m := m.(*ir.SwitchStmt) + if m.Tag != nil && m.Tag.Op() == ir.OTYPESW { + break // Nothing to do here for type switches. + } + if m.Tag != nil && !types.IsComparable(m.Tag.Type()) { + break // Nothing to do here for un-comparable types. + } + if m.Tag != nil && !m.Tag.Type().IsEmptyInterface() && m.Tag.Type().HasShape() { + // To implement a switch on a value that is or has a type parameter, we first convert + // that thing we're switching on to an interface{}. + m.Tag = assignconvfn(m.Tag, types.Types[types.TINTER]) + } + for _, c := range m.Cases { + for i, x := range c.List { + // If we have a case that is or has a type parameter, convert that case + // to an interface{}. + if !x.Type().IsEmptyInterface() && x.Type().HasShape() { + c.List[i] = assignconvfn(x, types.Types[types.TINTER]) + } + } + } + } return m } @@ -548,16 +1236,299 @@ func (subst *subster) node(n ir.Node) ir.Node { return edit(n) } +// dictPass takes a function instantiation and does the transformations on the +// operations that need to make use of the dictionary param. +func (g *genInst) dictPass(info *instInfo) { + savef := ir.CurFunc + ir.CurFunc = info.fun + + callMap := make(map[ir.Node]bool) + + var edit func(ir.Node) ir.Node + edit = func(m ir.Node) ir.Node { + if m.Op() == ir.OCALL && m.(*ir.CallExpr).X.Op() == ir.OXDOT { + callMap[m.(*ir.CallExpr).X] = true + } + + ir.EditChildren(m, edit) + + switch m.Op() { + case ir.OCLOSURE: + newf := m.(*ir.ClosureExpr).Func + ir.CurFunc = newf + outerinfo := info + info = g.instInfoMap[newf.Nname.Sym()] + + body := newf.Body + for i, n := range body { + body[i] = edit(n) + } + + info = outerinfo + ir.CurFunc = info.fun + + case ir.OXDOT: + // This is the case of a dot access on a type param. This is + // typically a bound call on the type param, but could be a + // field access, if the constraint has a single structural type. + mse := m.(*ir.SelectorExpr) + src := mse.X.Type() + assert(src.IsShape()) + + if mse.X.Op() == ir.OTYPE { + // Method expression T.M + idx := findMethodExprClosure(info.dictInfo, mse) + c := getDictionaryEntryAddr(m.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen) + m = ir.NewConvExpr(m.Pos(), ir.OCONVNOP, mse.Type(), c) + m.SetTypecheck(1) + } else { + // If we can't find the selected method in the + // AllMethods of the bound, then this must be an access + // to a field of a structural type. If so, we skip the + // dictionary lookups - transformDot() will convert to + // the desired direct field access. + if isBoundMethod(info.dictInfo, mse) { + if callMap[m] { + // The OCALL surrounding this XDOT will rewrite the call + // to use the method expression closure directly. + break + } + // Convert this method value to a closure. + // TODO: use method expression closure. + dst := info.dictInfo.shapeToBound[mse.X.Type()] + // Implement x.M as a conversion-to-bound-interface + // 1) convert x to the bound interface + // 2) select method value M on that interface + if src.IsInterface() { + // If type arg is an interface (unusual case), + // we do a type assert to the type bound. + mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst) + } else { + mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst) + } + } + transformDot(mse, false) + } + case ir.OCALL: + call := m.(*ir.CallExpr) + op := call.X.Op() + if op == ir.OXDOT { + // This is a call of a method value where the value has a type parameter type. + // We transform to a call of the appropriate method expression closure + // in the dictionary. + // So if x has a type parameter type: + // _ = x.m(a) + // Rewrite to: + // _ = methexpr(x, a) + se := call.X.(*ir.SelectorExpr) + call.SetOp(ir.OCALLFUNC) + idx := findMethodExprClosure(info.dictInfo, se) + c := getDictionaryEntryAddr(se.Pos(), info.dictParam, info.dictInfo.startMethodExprClosures+idx, info.dictInfo.dictLen) + t := typecheck.NewMethodType(se.Type(), se.X.Type()) + call.X = ir.NewConvExpr(se.Pos(), ir.OCONVNOP, t, c) + typed(t, call.X) + call.Args.Prepend(se.X) + break + // TODO: deref case? + } + if op == ir.OMETHVALUE { + // Redo the transformation of OXDOT, now that we + // know the method value is being called. + call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT) + transformDot(call.X.(*ir.SelectorExpr), true) + } + transformCall(call) + + case ir.OCONVIFACE: + if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() { + // Was T->interface{}, after stenciling it is now interface{}->interface{}. + // No longer need the conversion. See issue 48276. + m.(*ir.ConvExpr).SetOp(ir.OCONVNOP) + break + } + mce := m.(*ir.ConvExpr) + // Note: x's argument is still typed as a type parameter. + // m's argument now has an instantiated type. + if mce.X.Type().HasShape() || (m.Type().HasShape() && !m.Type().IsEmptyInterface()) { + m = convertUsingDictionary(info, info.dictParam, m.Pos(), mce.X, m, m.Type()) + } + case ir.ODOTTYPE, ir.ODOTTYPE2: + dt := m.(*ir.TypeAssertExpr) + if dt.Type().IsEmptyInterface() || (dt.Type().IsInterface() && !dt.Type().HasShape()) { + break + } + if !dt.Type().HasShape() && !(dt.X.Type().HasShape() && !dt.X.Type().IsEmptyInterface()) { + break + } + var rtype, itab ir.Node + if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() { + // TODO(mdempsky): Investigate executing this block unconditionally. + ix := findDictType(info, m.Type()) + assert(ix >= 0) + rtype = getDictionaryType(info, info.dictParam, dt.Pos(), ix) + } else { + // nonempty interface to noninterface. Need an itab. + ix := -1 + for i, ic := range info.dictInfo.itabConvs { + if ic == m { + ix = info.dictInfo.startItabConv + i + break + } + } + assert(ix >= 0) + itab = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen) + } + op := ir.ODYNAMICDOTTYPE + if m.Op() == ir.ODOTTYPE2 { + op = ir.ODYNAMICDOTTYPE2 + } + m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rtype) + m.(*ir.DynamicTypeAssertExpr).ITab = itab + m.SetType(dt.Type()) + m.SetTypecheck(1) + case ir.OCASE: + if _, ok := m.(*ir.CommClause); ok { + // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE? + break + } + m := m.(*ir.CaseClause) + for i, c := range m.List { + if c.Op() == ir.OTYPE && c.Type().HasShape() { + // Use a *runtime._type for the dynamic type. + ix := findDictType(info, m.List[i].Type()) + assert(ix >= 0) + dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)) + + // For type switch from nonempty interfaces to non-interfaces, we need an itab as well. + if !m.List[i].Type().IsInterface() { + if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok { + // Type switch from nonempty interface. We need a *runtime.itab + // for the dynamic type. + ix := -1 + for j, ic := range info.dictInfo.itabConvs { + if ic == m.List[i] { + ix = info.dictInfo.startItabConv + j + break + } + } + assert(ix >= 0) + dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen) + } + } + typed(m.List[i].Type(), dt) + m.List[i] = dt + } + } + + } + return m + } + edit(info.fun) + ir.CurFunc = savef +} + +// findDictType looks for type t in the typeparams or derived types in the generic +// function info.gfInfo. This will indicate the dictionary entry with the +// correct concrete type for the associated instantiated function. +func findDictType(info *instInfo, t *types.Type) int { + for i, dt := range info.dictInfo.shapeParams { + if dt == t { + return i + } + } + for i, dt := range info.dictInfo.derivedTypes { + if types.IdenticalStrict(dt, t) { + return i + len(info.dictInfo.shapeParams) + } + } + return -1 +} + +// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface +// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the +// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the +// conversion. +func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node { + assert(v.Type().HasShape() || (in.Type().HasShape() && !in.Type().IsEmptyInterface())) + assert(dst.IsInterface()) + + if v.Type().IsInterface() { + // Converting from an interface. The shape-ness of the source doesn't really matter, as + // we'll be using the concrete type from the first interface word. + if dst.IsEmptyInterface() { + // Converting I2E. OCONVIFACE does that for us, and doesn't depend + // on what the empty interface was instantiated with. No dictionary entry needed. + v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v) + v.SetTypecheck(1) + return v + } + if !in.Type().HasShape() { + // Regular OCONVIFACE works if the destination isn't parameterized. + v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v) + v.SetTypecheck(1) + return v + } + + // We get the destination interface type from the dictionary and the concrete + // type from the argument's itab. Call runtime.convI2I to get the new itab. + tmp := typecheck.Temp(v.Type()) + as := ir.NewAssignStmt(pos, tmp, v) + as.SetTypecheck(1) + itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp) + typed(types.Types[types.TUINTPTR].PtrTo(), itab) + idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp) + typed(types.Types[types.TUNSAFEPTR], idata) + + fn := typecheck.LookupRuntime("convI2I") + fn.SetTypecheck(1) + types.CalcSize(fn.Type()) + call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil) + typed(types.Types[types.TUINT8].PtrTo(), call) + ix := findDictType(info, in.Type()) + assert(ix >= 0) + inter := getDictionaryType(info, dictParam, pos, ix) + call.Args = []ir.Node{inter, itab} + i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata) + typed(dst, i) + i.PtrInit().Append(as) + return i + } + + var rt ir.Node + if !dst.IsEmptyInterface() { + // We should have an itab entry in the dictionary. Using this itab + // will be more efficient than converting to an empty interface first + // and then type asserting to dst. + ix := -1 + for i, ic := range info.dictInfo.itabConvs { + if ic == in { + ix = info.dictInfo.startItabConv + i + break + } + } + assert(ix >= 0) + rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen) + } else { + ix := findDictType(info, v.Type()) + assert(ix >= 0) + // Load the actual runtime._type of the type parameter from the dictionary. + rt = getDictionaryType(info, dictParam, pos, ix) + } + + // Figure out what the data field of the interface will be. + data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v) + typed(types.Types[types.TUNSAFEPTR], data) + + // Build an interface from the type and data parts. + var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data) + typed(dst, i) + return i +} + func (subst *subster) namelist(l []*ir.Name) []*ir.Name { s := make([]*ir.Name, len(l)) for i, n := range l { - s[i] = subst.node(n).(*ir.Name) - if n.Defn != nil { - s[i].Defn = subst.node(n.Defn) - } - if n.Outer != nil { - s[i].Outer = subst.node(n.Outer).(*ir.Name) - } + s[i] = subst.localvar(n) } return s } @@ -570,348 +1541,770 @@ func (subst *subster) list(l []ir.Node) []ir.Node { return s } -// tstruct substitutes type params in types of the fields of a structure type. For -// each field, if Nname is set, tstruct also translates the Nname using -// subst.vars, if Nname is in subst.vars. To always force the creation of a new -// (top-level) struct, regardless of whether anything changed with the types or -// names of the struct's fields, set force to true. -func (subst *subster) tstruct(t *types.Type, force bool) *types.Type { - if t.NumFields() == 0 { - if t.HasTParam() { - // For an empty struct, we need to return a new type, - // since it may now be fully instantiated (HasTParam - // becomes false). - return types.NewStruct(t.Pkg(), nil) - } - return t - } - var newfields []*types.Field - if force { - newfields = make([]*types.Field, t.NumFields()) - } - for i, f := range t.Fields().Slice() { - t2 := subst.typ(f.Type) - if (t2 != f.Type || f.Nname != nil) && newfields == nil { - newfields = make([]*types.Field, t.NumFields()) - for j := 0; j < i; j++ { - newfields[j] = t.Field(j) - } - } - if newfields != nil { - // TODO(danscales): make sure this works for the field - // names of embedded types (which should keep the name of - // the type param, not the instantiated type). - newfields[i] = types.NewField(f.Pos, f.Sym, t2) - if f.Nname != nil { - // f.Nname may not be in subst.vars[] if this is - // a function name or a function instantiation type - // that we are translating - v := subst.vars[f.Nname.(*ir.Name)] - // Be careful not to put a nil var into Nname, - // since Nname is an interface, so it would be a - // non-nil interface. - if v != nil { - newfields[i].Nname = v - } - } +// fields sets the Nname field for the Field nodes inside a type signature, based +// on the corresponding in/out parameters in dcl. It depends on the in and out +// parameters being in order in dcl. +func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field { + // Find the starting index in dcl of declarations of the class (either + // PPARAM or PPARAMOUT). + var i int + for i = range dcl { + if dcl[i].Class == class { + break } } - if newfields != nil { - return types.NewStruct(t.Pkg(), newfields) + + // Create newfields nodes that are copies of the oldfields nodes, but + // with substitution for any type params, and with Nname set to be the node in + // Dcl for the corresponding PPARAM or PPARAMOUT. + newfields := make([]*types.Field, len(oldfields)) + for j := range oldfields { + newfields[j] = oldfields[j].Copy() + newfields[j].Type = subst.ts.Typ(oldfields[j].Type) + // A PPARAM field will be missing from dcl if its name is + // unspecified or specified as "_". So, we compare the dcl sym + // with the field sym (or sym of the field's Nname node). (Unnamed + // results still have a name like ~r2 in their Nname node.) If + // they don't match, this dcl (if there is one left) must apply to + // a later field. + if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym || + (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) { + newfields[j].Nname = dcl[i] + i++ + } + } + return newfields +} + +// deref does a single deref of type t, if it is a pointer type. +func deref(t *types.Type) *types.Type { + if t.IsPtr() { + return t.Elem() } return t +} +// markTypeUsed marks type t as used in order to help avoid dead-code elimination of +// needed methods. +func markTypeUsed(t *types.Type, lsym *obj.LSym) { + if t.IsInterface() { + return + } + // TODO: This is somewhat overkill, we really only need it + // for types that are put into interfaces. + // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go + reflectdata.MarkTypeUsedInInterface(t, lsym) } -// tinter substitutes type params in types of the methods of an interface type. -func (subst *subster) tinter(t *types.Type) *types.Type { - if t.Methods().Len() == 0 { - return t +// getDictionarySym returns the dictionary for the named generic function gf, which +// is instantiated with the type arguments targs. +func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym { + if len(targs) == 0 { + base.Fatalf("%s should have type arguments", gf.Sym().Name) + } + + // Enforce that only concrete types can make it to here. + for _, t := range targs { + if t.HasShape() { + panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name)) + } } - var newfields []*types.Field - for i, f := range t.Methods().Slice() { - t2 := subst.typ(f.Type) - if (t2 != f.Type || f.Nname != nil) && newfields == nil { - newfields = make([]*types.Field, t.Methods().Len()) - for j := 0; j < i; j++ { - newfields[j] = t.Methods().Index(j) + + // Get a symbol representing the dictionary. + sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth) + + // Initialize the dictionary, if we haven't yet already. + lsym := sym.Linksym() + if len(lsym.P) > 0 { + // We already started creating this dictionary and its lsym. + return sym + } + + infoPrint("=== Creating dictionary %v\n", sym.Name) + off := 0 + // Emit an entry for each targ (concrete type or gcshape). + for _, t := range targs { + infoPrint(" * %v\n", t) + s := reflectdata.TypeLinksym(t) + off = objw.SymPtr(lsym, off, s, 0) + markTypeUsed(t, lsym) + } + + instInfo := g.getInstantiation(gf, targs, isMeth) + info := instInfo.dictInfo + + subst := typecheck.Tsubster{ + Tparams: info.shapeParams, + Targs: targs, + } + // Emit an entry for each derived type (after substituting targs) + for _, t := range info.derivedTypes { + ts := subst.Typ(t) + infoPrint(" - %v\n", ts) + s := reflectdata.TypeLinksym(ts) + off = objw.SymPtr(lsym, off, s, 0) + markTypeUsed(ts, lsym) + } + // Emit an entry for each subdictionary (after substituting targs) + for _, subDictInfo := range info.subDictCalls { + var sym *types.Sym + n := subDictInfo.callNode + switch n.Op() { + case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH: + call := n.(*ir.CallExpr) + if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH { + var nameNode *ir.Name + se := call.X.(*ir.SelectorExpr) + if se.X.Type().IsShape() { + tparam := se.X.Type() + // Ensure methods on all instantiating types are computed. + typecheck.CalcMethods(tparam) + if typecheck.Lookdot1(nil, se.Sel, tparam, tparam.AllMethods(), 0) != nil { + // This is a method call enabled by a type bound. + // We need this extra check for method expressions, + // which don't add in the implicit XDOTs. + tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel) + tmpse = typecheck.AddImplicitDots(tmpse) + tparam = tmpse.X.Type() + } + if !tparam.IsShape() { + // The method expression is not + // really on a typeparam. + break + } + ix := -1 + for i, shape := range info.shapeParams { + if shape == tparam { + ix = i + break + } + } + assert(ix >= 0) + recvType := targs[ix] + if recvType.IsInterface() || len(recvType.RParams()) == 0 { + // No sub-dictionary entry is + // actually needed, since the + // type arg is not an + // instantiated type that + // will have generic methods. + break + } + // This is a method call for an + // instantiated type, so we need a + // sub-dictionary. + targs := recvType.RParams() + genRecvType := recvType.OrigType() + nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) + sym = g.getDictionarySym(nameNode, targs, true) + } else { + // This is the case of a normal + // method call on a generic type. + assert(subDictInfo.savedXNode == se) + sym = g.getSymForMethodCall(se, &subst) + } + } else { + inst, ok := call.X.(*ir.InstExpr) + if ok { + // Code hasn't been transformed yet + assert(subDictInfo.savedXNode == inst) + } + // If !ok, then the generic method/function call has + // already been transformed to a shape instantiation + // call. Either way, use the SelectorExpr/InstExpr + // node saved in info. + cex := subDictInfo.savedXNode + if se, ok := cex.(*ir.SelectorExpr); ok { + sym = g.getSymForMethodCall(se, &subst) + } else { + inst := cex.(*ir.InstExpr) + nameNode := inst.X.(*ir.Name) + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym = g.getDictionarySym(nameNode, subtargs, false) + } } + + case ir.OFUNCINST: + inst := n.(*ir.InstExpr) + nameNode := inst.X.(*ir.Name) + subtargs := typecheck.TypesOf(inst.Targs) + for i, t := range subtargs { + subtargs[i] = subst.Typ(t) + } + sym = g.getDictionarySym(nameNode, subtargs, false) + + case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE: + sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst) + + default: + assert(false) } - if newfields != nil { - newfields[i] = types.NewField(f.Pos, f.Sym, t2) + + if sym == nil { + // Unused sub-dictionary entry, just emit 0. + off = objw.Uintptr(lsym, off, 0) + infoPrint(" - Unused subdict entry\n") + } else { + off = objw.SymPtr(lsym, off, sym.Linksym(), 0) + infoPrint(" - Subdict %v\n", sym.Name) } } - if newfields != nil { - return types.NewInterface(t.Pkg(), newfields) + + g.instantiateMethods() + delay := &delayInfo{ + gf: gf, + targs: targs, + sym: sym, + off: off, + isMeth: isMeth, + } + g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay) + return sym +} + +// getSymForMethodCall gets the dictionary sym for a method call, method value, or method +// expression that has selector se. subst gives the substitution from shape types to +// concrete types. +func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym { + // For everything except method expressions, 'recvType = deref(se.X.Type)' would + // also give the receiver type. For method expressions with embedded types, we + // need to look at the type of the selection to get the final receiver type. + recvType := deref(se.Selection.Type.Recv().Type) + genRecvType := recvType.OrigType() + nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) + subtargs := recvType.RParams() + s2targs := make([]*types.Type, len(subtargs)) + for i, t := range subtargs { + s2targs[i] = subst.Typ(t) } - return t + return g.getDictionarySym(nameNode, s2targs, true) } -// instTypeName creates a name for an instantiated type, based on the name of the -// generic type and the type args -func instTypeName(name string, targs []*types.Type) string { - b := bytes.NewBufferString(name) - b.WriteByte('[') - for i, targ := range targs { - if i > 0 { - b.WriteByte(',') - } - b.WriteString(targ.String()) - } - b.WriteByte(']') - return b.String() -} - -// typ computes the type obtained by substituting any type parameter in t with the -// corresponding type argument in subst. If t contains no type parameters, the -// result is t; otherwise the result is a new type. It deals with recursive types -// by using TFORW types and finding partially or fully created types via sym.Def. -func (subst *subster) typ(t *types.Type) *types.Type { - if !t.HasTParam() && t.Kind() != types.TFUNC { - // Note: function types need to be copied regardless, as the - // types of closures may contain declarations that need - // to be copied. See #45738. - return t - } - - if t.Kind() == types.TTYPEPARAM { - for i, tp := range subst.tparams { - if tp.Type == t { - return subst.targs[i].Type() - } - } - // If t is a simple typeparam T, then t has the name/symbol 'T' - // and t.Underlying() == t. - // - // However, consider the type definition: 'type P[T any] T'. We - // might use this definition so we can have a variant of type T - // that we can add new methods to. Suppose t is a reference to - // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM, - // because P[T] is defined as T. If we look at t.Underlying(), it - // is different, because the name of t.Underlying() is 'T' rather - // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM. - // In this case, we do the needed recursive substitution in the - // case statement below. - if t.Underlying() == t { - // t is a simple typeparam that didn't match anything in tparam - return t - } - // t is a more complex typeparam (e.g. P[T], as above, whose - // definition is just T). - assert(t.Sym() != nil) - } - - var newsym *types.Sym - var neededTargs []*types.Type - var forw *types.Type - - if t.Sym() != nil { - // Translate the type params for this type according to - // the tparam/targs mapping from subst. - neededTargs = make([]*types.Type, len(t.RParams())) - for i, rparam := range t.RParams() { - neededTargs[i] = subst.typ(rparam) - } - // For a named (defined) type, we have to change the name of the - // type as well. We do this first, so we can look up if we've - // already seen this type during this substitution or other - // definitions/substitutions. - genName := genericTypeName(t.Sym()) - newsym = t.Sym().Pkg.Lookup(instTypeName(genName, neededTargs)) - if newsym.Def != nil { - // We've already created this instantiated defined type. - return newsym.Def.Type() - } - - // In order to deal with recursive generic types, create a TFORW - // type initially and set the Def field of its sym, so it can be - // found if this type appears recursively within the type. - forw = newIncompleteNamedType(t.Pos(), newsym) - //println("Creating new type by sub", newsym.Name, forw.HasTParam()) - forw.SetRParams(neededTargs) - } - - var newt *types.Type +// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out +// any needed LSyms for itabs. The itab lsyms create wrappers which need various +// dictionaries and method instantiations to be complete, so, to avoid recursive +// dependencies, we finalize the itab lsyms only after all dictionaries syms and +// instantiations have been created. +// Also handles writing method expression closures into the dictionaries. +func (g *genInst) finalizeSyms() { + for _, d := range g.dictSymsToFinalize { + infoPrint("=== Finalizing dictionary %s\n", d.sym.Name) + + lsym := d.sym.Linksym() + instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth) + info := instInfo.dictInfo + + subst := typecheck.Tsubster{ + Tparams: info.shapeParams, + Targs: d.targs, + } - switch t.Kind() { - case types.TTYPEPARAM: - if t.Sym() == newsym { - // The substitution did not change the type. - return t + // Emit an entry for each itab + for _, n := range info.itabConvs { + var srctype, dsttype *types.Type + switch n.Op() { + case ir.OXDOT, ir.OMETHVALUE: + se := n.(*ir.SelectorExpr) + srctype = subst.Typ(se.X.Type()) + dsttype = subst.Typ(info.shapeToBound[se.X.Type()]) + case ir.ODOTTYPE, ir.ODOTTYPE2: + srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type()) + dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type()) + case ir.OCONVIFACE: + srctype = subst.Typ(n.(*ir.ConvExpr).X.Type()) + dsttype = subst.Typ(n.Type()) + case ir.OTYPE: + srctype = subst.Typ(n.Type()) + dsttype = subst.Typ(info.type2switchType[n]) + default: + base.Fatalf("itab entry with unknown op %s", n.Op()) + } + if srctype.IsInterface() || dsttype.IsEmptyInterface() { + // No itab is wanted if src type is an interface. We + // will use a type assert instead. + d.off = objw.Uintptr(lsym, d.off, 0) + infoPrint(" + Unused itab entry for %v\n", srctype) + } else { + // Make sure all new fully-instantiated types have + // their methods created before generating any itabs. + g.instantiateMethods() + itabLsym := reflectdata.ITabLsym(srctype, dsttype) + d.off = objw.SymPtr(lsym, d.off, itabLsym, 0) + markTypeUsed(srctype, lsym) + infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype) + } + } + + // Emit an entry for each method expression closure. + // Each entry is a (captureless) closure pointing to the method on the instantiating type. + // In other words, the entry is a runtime.funcval whose fn field is set to the method + // in question, and has no other fields. The address of this dictionary entry can be + // cast to a func of the appropriate type. + // TODO: do these need to be done when finalizing, or can we do them earlier? + for _, bf := range info.methodExprClosures { + rcvr := d.targs[bf.idx] + rcvr2 := deref(rcvr) + found := false + typecheck.CalcMethods(rcvr2) // Ensure methods on all instantiating types are computed. + for _, f := range rcvr2.AllMethods().Slice() { + if f.Sym.Name == bf.name { + codePtr := ir.MethodSym(rcvr, f.Sym).Linksym() + d.off = objw.SymPtr(lsym, d.off, codePtr, 0) + infoPrint(" + MethodExprClosure for %v.%s\n", rcvr, bf.name) + found = true + break + } + } + if !found { + base.Fatalf("method %s on %v not found", bf.name, rcvr) + } } - // Substitute the underlying typeparam (e.g. T in P[T], see - // the example describing type P[T] above). - newt = subst.typ(t.Underlying()) - assert(newt != t) - case types.TARRAY: - elem := t.Elem() - newelem := subst.typ(elem) - if newelem != elem { - newt = types.NewArray(newelem, t.NumElem()) + objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA) + infoPrint("=== Finalized dictionary %s\n", d.sym.Name) + } + g.dictSymsToFinalize = nil +} + +func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node { + sym := g.getDictionarySym(gf, targs, isMeth) + + // Make (or reuse) a node referencing the dictionary symbol. + var n *ir.Name + if sym.Def != nil { + n = sym.Def.(*ir.Name) + } else { + // We set the position of a static dictionary to be the position of + // one of its uses. + n = ir.NewNameAt(pos, sym) + n.Curfn = ir.CurFunc + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + } + + // Return the address of the dictionary. Addr node gets position that was passed in. + np := typecheck.NodAddrAt(pos, n) + // Note: treat dictionary pointers as uintptrs, so they aren't pointers + // with respect to GC. That saves on stack scanning work, write barriers, etc. + // We can get away with it because dictionaries are global variables. + // TODO: use a cast, or is typing directly ok? + np.SetType(types.Types[types.TUINTPTR]) + np.SetTypecheck(1) + return np +} + +// hasShapeNodes returns true if the type of any node in targs has a shape. +func hasShapeNodes(targs []ir.Ntype) bool { + for _, n := range targs { + if n.Type().HasShape() { + return true } + } + return false +} - case types.TPTR: - elem := t.Elem() - newelem := subst.typ(elem) - if newelem != elem { - newt = types.NewPtr(newelem) +// hasShapeTypes returns true if any type in targs has a shape. +func hasShapeTypes(targs []*types.Type) bool { + for _, t := range targs { + if t.HasShape() { + return true } + } + return false +} - case types.TSLICE: - elem := t.Elem() - newelem := subst.typ(elem) - if newelem != elem { - newt = types.NewSlice(newelem) +// getInstInfo get the dictionary format for a function instantiation- type params, derived +// types, and needed subdictionaries, itabs, and method expression closures. +func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) { + info := instInfo.dictInfo + info.shapeParams = shapes + + for _, t := range info.shapeParams { + b := info.shapeToBound[t] + if b.HasShape() { + // If a type bound is parameterized (unusual case), then we + // may need its derived type to do a type assert when doing a + // bound call for a type arg that is an interface. + addType(info, nil, b) } + } - case types.TSTRUCT: - newt = subst.tstruct(t, false) - if newt == t { - newt = nil + for _, n := range st.Dcl { + addType(info, n, n.Type()) + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) + } + + if infoPrintMode { + fmt.Printf(">>> InstInfo for %v\n", st) + for _, t := range info.shapeParams { + fmt.Printf(" Typeparam %v\n", t) } + } - case types.TFUNC: - newrecvs := subst.tstruct(t.Recvs(), false) - newparams := subst.tstruct(t.Params(), false) - newresults := subst.tstruct(t.Results(), false) - if newrecvs != t.Recvs() || newparams != t.Params() || newresults != t.Results() { - // If any types have changed, then the all the fields of - // of recv, params, and results must be copied, because they have - // offset fields that are dependent, and so must have an - // independent copy for each new signature. - var newrecv *types.Field - if newrecvs.NumFields() > 0 { - if newrecvs == t.Recvs() { - newrecvs = subst.tstruct(t.Recvs(), true) + // Map to remember when we have seen an instantiated function value or method + // expression/value as part of a call, so we can determine when we encounter + // an uncalled function value or method expression/value. + callMap := make(map[ir.Node]bool) + + var visitFunc func(ir.Node) + visitFunc = func(n ir.Node) { + switch n.Op() { + case ir.OFUNCINST: + if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) { + infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X) + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + case ir.OMETHEXPR, ir.OMETHVALUE: + if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) && + len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && + hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) { + if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE { + infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n) + } else { + infoPrint(" Closure&subdictionary required at generic meth value %v\n", n) + } + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + case ir.OCALL: + ce := n.(*ir.CallExpr) + if ce.X.Op() == ir.OFUNCINST { + callMap[ce.X] = true + if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) { + infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n) + // Save the instExpr node for the function call, + // since we will lose this information when the + // generic function call is transformed to a call + // on the shape instantiation. + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X}) + } + } + // Note: this XDOT code is not actually needed as long as we + // continue to disable type parameters on RHS of type + // declarations (#45639). + if ce.X.Op() == ir.OXDOT { + callMap[ce.X] = true + if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) { + infoPrint(" Optional subdictionary at generic bound call: %v\n", n) + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil}) + } + } + case ir.OCALLMETH: + ce := n.(*ir.CallExpr) + if ce.X.Op() == ir.ODOTMETH && + len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { + callMap[ce.X] = true + if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) { + infoPrint(" Subdictionary at generic method call: %v\n", n) + // Save the selector for the method call, since we + // will eventually lose this information when the + // generic method call is transformed into a + // function call on the method shape instantiation. + info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X}) + } + } + case ir.OCONVIFACE: + if n.Type().IsInterface() && !n.Type().IsEmptyInterface() && + (n.Type().HasShape() || n.(*ir.ConvExpr).X.Type().HasShape()) { + infoPrint(" Itab for interface conv: %v\n", n) + info.itabConvs = append(info.itabConvs, n) + } + case ir.OXDOT: + se := n.(*ir.SelectorExpr) + if se.X.Op() == ir.OTYPE && se.X.Type().IsShape() { + // Method expression. + addMethodExprClosure(info, se) + break + } + if isBoundMethod(info, se) { + if callMap[n] { + // Method value called directly. Use method expression closure. + addMethodExprClosure(info, se) + break } - newrecv = newrecvs.Field(0) + // Method value not called directly. Still doing the old way. + infoPrint(" Itab for bound call: %v\n", n) + info.itabConvs = append(info.itabConvs, n) } - if newparams == t.Params() { - newparams = subst.tstruct(t.Params(), true) + + case ir.ODOTTYPE, ir.ODOTTYPE2: + if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() { + infoPrint(" Itab for dot type: %v\n", n) + info.itabConvs = append(info.itabConvs, n) + } + case ir.OCLOSURE: + // Visit the closure body and add all relevant entries to the + // dictionary of the outer function (closure will just use + // the dictionary of the outer function). + cfunc := n.(*ir.ClosureExpr).Func + for _, n1 := range cfunc.Body { + ir.Visit(n1, visitFunc) + } + for _, n := range cfunc.Dcl { + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) } - if newresults == t.Results() { - newresults = subst.tstruct(t.Results(), true) + case ir.OSWITCH: + ss := n.(*ir.SwitchStmt) + if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW && + !ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() { + for _, cc := range ss.Cases { + for _, c := range cc.List { + if c.Op() == ir.OTYPE && c.Type().HasShape() { + // Type switch from a non-empty interface - might need an itab. + infoPrint(" Itab for type switch: %v\n", c) + info.itabConvs = append(info.itabConvs, c) + if info.type2switchType == nil { + info.type2switchType = map[ir.Node]*types.Type{} + } + info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type() + } + } + } } - newt = types.NewSignature(t.Pkg(), newrecv, t.TParams().FieldSlice(), newparams.FieldSlice(), newresults.FieldSlice()) } + addType(info, n, n.Type()) + } - case types.TINTER: - newt = subst.tinter(t) - if newt == t { - newt = nil + for _, stmt := range st.Body { + ir.Visit(stmt, visitFunc) + } + if infoPrintMode { + for _, t := range info.derivedTypes { + fmt.Printf(" Derived type %v\n", t) } + fmt.Printf(">>> Done Instinfo\n") + } + info.startSubDict = len(info.shapeParams) + len(info.derivedTypes) + info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + info.startMethodExprClosures = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) + info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) + len(info.methodExprClosures) +} - case types.TMAP: - newkey := subst.typ(t.Key()) - newval := subst.typ(t.Elem()) - if newkey != t.Key() || newval != t.Elem() { - newt = types.NewMap(newkey, newval) - } - - case types.TCHAN: - elem := t.Elem() - newelem := subst.typ(elem) - if newelem != elem { - newt = types.NewChan(newelem, t.ChanDir()) - if !newt.HasTParam() { - // TODO(danscales): not sure why I have to do this - // only for channels..... - types.CheckSize(newt) - } - } - } - if newt == nil { - // Even though there were typeparams in the type, there may be no - // change if this is a function type for a function call (which will - // have its own tparams/targs in the function instantiation). - return t - } - - if t.Sym() == nil { - // Not a named type, so there was no forwarding type and there are - // no methods to substitute. - assert(t.Methods().Len() == 0) - return newt - } - - forw.SetUnderlying(newt) - newt = forw - - if t.Kind() != types.TINTER && t.Methods().Len() > 0 { - // Fill in the method info for the new type. - var newfields []*types.Field - newfields = make([]*types.Field, t.Methods().Len()) - for i, f := range t.Methods().Slice() { - t2 := subst.typ(f.Type) - oldsym := f.Nname.Sym() - newsym := makeInstName(oldsym, subst.targs, true) - var nname *ir.Name - if newsym.Def != nil { - nname = newsym.Def.(*ir.Name) - } else { - nname = ir.NewNameAt(f.Pos, newsym) - nname.SetType(t2) - newsym.Def = nname - } - newfields[i] = types.NewField(f.Pos, f.Sym, t2) - newfields[i].Nname = nname +// isBoundMethod returns true if the selection indicated by se is a bound method of +// se.X. se.X must be a shape type (i.e. substituted directly from a type param). If +// isBoundMethod returns false, then the selection must be a field access of a +// structural type. +func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool { + bound := info.shapeToBound[se.X.Type()] + return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil +} + +func shapeIndex(info *dictInfo, t *types.Type) int { + for i, s := range info.shapeParams { + if s == t { + return i } - newt.Methods().Set(newfields) - if !newt.HasTParam() { - // Generate all the methods for a new fully-instantiated type. - subst.g.instTypeList = append(subst.g.instTypeList, newt) + } + base.Fatalf("can't find type %v in shape params", t) + return -1 +} + +// addMethodExprClosure adds the T.M method expression to the list of bound method expressions +// used in the generic body. +// isBoundMethod must have returned true on the same arguments. +func addMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) { + idx := shapeIndex(info, se.X.Type()) + name := se.Sel.Name + for _, b := range info.methodExprClosures { + if idx == b.idx && name == b.name { + return } } - return newt + infoPrint(" Method expression closure for %v.%s\n", info.shapeParams[idx], name) + info.methodExprClosures = append(info.methodExprClosures, methodExprClosure{idx: idx, name: name}) } -// fields sets the Nname field for the Field nodes inside a type signature, based -// on the corresponding in/out parameters in dcl. It depends on the in and out -// parameters being in order in dcl. -func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field { - // Find the starting index in dcl of declarations of the class (either - // PPARAM or PPARAMOUT). - var i int - for i = range dcl { - if dcl[i].Class == class { - break +// findMethodExprClosure finds the entry in the dictionary to use for the T.M +// method expression encoded in se. +// isBoundMethod must have returned true on the same arguments. +func findMethodExprClosure(info *dictInfo, se *ir.SelectorExpr) int { + idx := shapeIndex(info, se.X.Type()) + name := se.Sel.Name + for i, b := range info.methodExprClosures { + if idx == b.idx && name == b.name { + return i } } + base.Fatalf("can't find method expression closure for %s %s", se.X.Type(), name) + return -1 +} - // Create newfields nodes that are copies of the oldfields nodes, but - // with substitution for any type params, and with Nname set to be the node in - // Dcl for the corresponding PPARAM or PPARAMOUT. - newfields := make([]*types.Field, len(oldfields)) - for j := range oldfields { - newfields[j] = oldfields[j].Copy() - newfields[j].Type = subst.typ(oldfields[j].Type) - // A param field will be missing from dcl if its name is - // unspecified or specified as "_". So, we compare the dcl sym - // with the field sym. If they don't match, this dcl (if there is - // one left) must apply to a later field. - if i < len(dcl) && dcl[i].Sym() == oldfields[j].Sym { - newfields[j].Nname = dcl[i] - i++ +// addType adds t to info.derivedTypes if it is parameterized type (which is not +// just a simple shape) that is different from any existing type on +// info.derivedTypes. +func addType(info *dictInfo, n ir.Node, t *types.Type) { + if t == nil || !t.HasShape() { + return + } + if t.IsShape() { + return + } + if t.Kind() == types.TFUNC && n != nil && + (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) { + // Don't use the type of a named generic function or method, + // since that is parameterized by other typeparams. + // (They all come from arguments of a FUNCINST node.) + return + } + if doubleCheck && !parameterizedBy(t, info.shapeParams) { + base.Fatalf("adding type with invalid parameters %+v", t) + } + if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() { + // Multiple return values are not a relevant new type (?). + return + } + // Ignore a derived type we've already added. + for _, et := range info.derivedTypes { + if types.IdenticalStrict(t, et) { + return } } - return newfields + info.derivedTypes = append(info.derivedTypes, t) } -// defer does a single defer of type t, if it is a pointer type. -func deref(t *types.Type) *types.Type { - if t.IsPtr() { - return t.Elem() +// parameterizedBy returns true if t is parameterized by (at most) params. +func parameterizedBy(t *types.Type, params []*types.Type) bool { + return parameterizedBy1(t, params, map[*types.Type]bool{}) +} +func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool { + if visited[t] { + return true } - return t + visited[t] = true + + if t.Sym() != nil && len(t.RParams()) > 0 { + // This defined type is instantiated. Check the instantiating types. + for _, r := range t.RParams() { + if !parameterizedBy1(r, params, visited) { + return false + } + } + return true + } + if t.IsShape() { + // Check if t is one of the allowed parameters in scope. + for _, p := range params { + if p == t { + return true + } + } + // Couldn't find t in the list of allowed parameters. + return false + + } + switch t.Kind() { + case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN: + return parameterizedBy1(t.Elem(), params, visited) + + case types.TMAP: + return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited) + + case types.TFUNC: + return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited) + + case types.TSTRUCT: + for _, f := range t.Fields().Slice() { + if !parameterizedBy1(f.Type, params, visited) { + return false + } + } + return true + + case types.TINTER: + for _, f := range t.Methods().Slice() { + if !parameterizedBy1(f.Type, params, visited) { + return false + } + } + return true + + case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64, + types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, + types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR: + return true + + case types.TUNION: + for i := 0; i < t.NumTerms(); i++ { + tt, _ := t.Term(i) + if !parameterizedBy1(tt, params, visited) { + return false + } + } + return true + + default: + base.Fatalf("bad type kind %+v", t) + return true + } +} + +// startClosures starts creation of a closure that has the function type typ. It +// creates all the formal params and results according to the type typ. On return, +// the body and closure variables of the closure must still be filled in, and +// ir.UseClosure() called. +func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) { + // Make a new internal function. + fn := ir.NewClosureFunc(pos, outer != nil) + ir.NameClosure(fn.OClosure, outer) + + // Build formal argument and return lists. + var formalParams []*types.Field // arguments of closure + var formalResults []*types.Field // returns of closure + for i := 0; i < typ.NumParams(); i++ { + t := typ.Params().Field(i).Type + arg := ir.NewNameAt(pos, closureSym(outer, "a", i)) + arg.Class = ir.PPARAM + typed(t, arg) + arg.Curfn = fn + fn.Dcl = append(fn.Dcl, arg) + f := types.NewField(pos, arg.Sym(), t) + f.Nname = arg + f.SetIsDDD(typ.Params().Field(i).IsDDD()) + formalParams = append(formalParams, f) + } + for i := 0; i < typ.NumResults(); i++ { + t := typ.Results().Field(i).Type + result := ir.NewNameAt(pos, closureSym(outer, "r", i)) // TODO: names not needed? + result.Class = ir.PPARAMOUT + typed(t, result) + result.Curfn = fn + fn.Dcl = append(fn.Dcl, result) + f := types.NewField(pos, result.Sym(), t) + f.Nname = result + formalResults = append(formalResults, f) + } + + // Build an internal function with the right signature. + closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults) + typed(closureType, fn.Nname) + typed(typ, fn.OClosure) + fn.SetTypecheck(1) + return fn, formalParams, formalResults + } -// newIncompleteNamedType returns a TFORW type t with name specified by sym, such -// that t.nod and sym.Def are set correctly. -func newIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type { - name := ir.NewDeclNameAt(pos, ir.OTYPE, sym) - forw := types.NewNamed(name) - name.SetType(forw) - sym.Def = name - return forw +// closureSym returns outer.Sym().Pkg.LookupNum(prefix, n). +// If outer is nil, then types.LocalPkg is used instead. +func closureSym(outer *ir.Func, prefix string, n int) *types.Sym { + pkg := types.LocalPkg + if outer != nil { + pkg = outer.Sym().Pkg + } + return pkg.LookupNum(prefix, n) +} + +// assertToBound returns a new node that converts a node rcvr with interface type to +// the 'dst' interface type. +func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node { + if !dst.HasShape() { + return typed(dst, ir.NewTypeAssertExpr(pos, rcvr, nil)) + } + + ix := findDictType(info, dst) + assert(ix >= 0) + rt := getDictionaryType(info, dictVar, pos, ix) + return typed(dst, ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt)) } diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index 32a1483b4aabeb..a349a7ef10e03f 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -5,6 +5,7 @@ package noder import ( + "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" "cmd/compile/internal/typecheck" @@ -12,8 +13,10 @@ import ( "cmd/internal/src" ) +// stmts creates nodes for a slice of statements that form a scope. func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node { var nodes []ir.Node + types.Markdcl() for _, stmt := range stmts { switch s := g.stmt(stmt).(type) { case nil: // EmptyStmt @@ -23,10 +26,12 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node { nodes = append(nodes, s) } } + types.Popdcl() return nodes } func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { + base.Assert(g.exprStmtOK) switch stmt := stmt.(type) { case nil, *syntax.EmptyStmt: return nil @@ -35,24 +40,24 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { case *syntax.BlockStmt: return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt)) case *syntax.ExprStmt: - x := g.expr(stmt.X) - if call, ok := x.(*ir.CallExpr); ok { - call.Use = ir.CallUseStmt - } - return x + return wrapname(g.pos(stmt.X), g.expr(stmt.X)) case *syntax.SendStmt: n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value)) - if n.Chan.Type().HasTParam() || n.Value.Type().HasTParam() { - // Delay transforming the send if the channel or value - // have a type param. - n.SetTypecheck(3) - return n + if !g.delayTransform() { + transformSend(n) } - transformSend(n) n.SetTypecheck(1) return n case *syntax.DeclStmt: - return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList)) + if g.topFuncIsGeneric && len(stmt.DeclList) > 0 { + if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok { + // TODO: remove this restriction. See issue 47631. + base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported") + } + } + n := ir.NewBlockStmt(g.pos(stmt), nil) + g.decls(&n.List, stmt.DeclList) + return n case *syntax.AssignStmt: if stmt.Op != 0 && stmt.Op != syntax.Def { @@ -61,60 +66,40 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { if stmt.Rhs == nil { n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs)) } else { - n = ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs)) + // Eval rhs before lhs, for compatibility with noder1 + rhs := g.expr(stmt.Rhs) + lhs := g.expr(stmt.Lhs) + n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs) } - if n.X.Typecheck() == 3 { - n.SetTypecheck(3) - return n + if !g.delayTransform() { + transformAsOp(n) } - transformAsOp(n) n.SetTypecheck(1) return n } - names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def) + // Eval rhs before lhs, for compatibility with noder1 rhs := g.exprList(stmt.Rhs) - - // We must delay transforming the assign statement if any of the - // lhs or rhs nodes are also delayed, since transformAssign needs - // to know the types of the left and right sides in various cases. - delay := false - for _, e := range lhs { - if e.Typecheck() == 3 { - delay = true - break - } - } - for _, e := range rhs { - if e.Typecheck() == 3 { - delay = true - break - } - } + names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def) if len(lhs) == 1 && len(rhs) == 1 { n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0]) n.Def = initDefn(n, names) - if delay { - n.SetTypecheck(3) - return n + if !g.delayTransform() { + lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} + transformAssign(n, lhs, rhs) + n.X, n.Y = lhs[0], rhs[0] } - - lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} - transformAssign(n, lhs, rhs) - n.X, n.Y = lhs[0], rhs[0] n.SetTypecheck(1) return n } n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs) n.Def = initDefn(n, names) - if delay { - n.SetTypecheck(3) - return n + if !g.delayTransform() { + transformAssign(n, n.Lhs, n.Rhs) } - transformAssign(n, n.Lhs, n.Rhs) n.SetTypecheck(1) return n @@ -124,15 +109,9 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call)) case *syntax.ReturnStmt: n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results)) - for _, e := range n.Results { - if e.Type().HasTParam() { - // Delay transforming the return statement if any of the - // return values have a type param. - n.SetTypecheck(3) - return n - } + if !g.delayTransform() { + transformReturn(n) } - transformReturn(n) n.SetTypecheck(1) return n case *syntax.IfStmt: @@ -141,7 +120,10 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { return g.forStmt(stmt) case *syntax.SelectStmt: n := g.selectStmt(stmt) - transformSelect(n.(*ir.SelectStmt)) + + if !g.delayTransform() { + transformSelect(n.(*ir.SelectStmt)) + } n.SetTypecheck(1) return n case *syntax.SwitchStmt: @@ -266,6 +248,12 @@ func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node { key, value := unpackTwo(lhs) n := ir.NewRangeStmt(g.pos(r), key, value, g.expr(r.X), g.blockStmt(stmt.Body)) n.Def = initDefn(n, names) + if key != nil { + transformCheckAssign(n, key) + } + if value != nil { + transformCheckAssign(n, value) + } return n } @@ -311,6 +299,8 @@ func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node { if obj, ok := g.info.Implicits[clause]; ok { cv = g.obj(obj) cv.SetPos(g.makeXPos(clause.Colon)) + assert(expr.Op() == ir.OTYPESW) + cv.Defn = expr } body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body)) body[i].Var = cv diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index 2859089e69b913..15adb89e5f6c4b 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -74,8 +74,7 @@ func stringtoruneslit(n *ir.ConvExpr) ir.Node { i++ } - nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil) - nn.List = list + nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, n.Type(), list) typed(n.Type(), nn) // Need to transform the OCOMPLIT. return transformCompLit(nn) @@ -85,7 +84,15 @@ func stringtoruneslit(n *ir.ConvExpr) ir.Node { // etc. Corresponds to typecheck.tcConv. func transformConv(n *ir.ConvExpr) ir.Node { t := n.X.Type() - op, _ := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) + op, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) + if op == ir.OXXX { + // types2 currently ignores pragmas, so a 'notinheap' mismatch is the + // one type-related error that it does not catch. This error will be + // caught here by Convertop (see two checks near beginning of + // Convertop) and reported at the end of noding. + base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why) + return n + } n.SetOp(op) switch n.Op() { case ir.OCONVNOP: @@ -107,6 +114,31 @@ func transformConv(n *ir.ConvExpr) ir.Node { if n.X.Op() == ir.OLITERAL { return stringtoruneslit(n) } + + case ir.OBYTES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TUINT8) + if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] { + // If t is a slice of a user-defined byte type B (not uint8 + // or byte), then add an extra CONVNOP from []B to []byte, so + // that the call to slicebytetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X) + n.X.SetTypecheck(1) + } + + case ir.ORUNES2STR: + assert(t.IsSlice()) + assert(t.Elem().Kind() == types.TINT32) + if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] { + // If t is a slice of a user-defined rune type B (not uint32 + // or rune), then add an extra CONVNOP from []B to []rune, so + // that the call to slicerunetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X) + n.X.SetTypecheck(1) + } + } return n } @@ -122,10 +154,15 @@ func transformConvCall(n *ir.CallExpr) ir.Node { } // transformCall transforms a normal function/method call. Corresponds to last half -// (non-conversion, non-builtin part) of typecheck.tcCall. +// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even +// in the case of OCALL/OFUNCINST. func transformCall(n *ir.CallExpr) { + // Set base.Pos, since transformArgs below may need it, but transformCall + // is called in some passes that don't set base.Pos. + ir.SetPos(n) // n.Type() can be nil for calls with no return value assert(n.Typecheck() == 1) + typecheck.RewriteNonNameCall(n) transformArgs(n) l := n.X t := l.Type() @@ -149,9 +186,10 @@ func transformCall(n *ir.CallExpr) { } typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args) + if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 { + typecheck.FixMethodCall(n) + } if t.NumResults() == 1 { - n.SetType(l.Type().Results().Field(0).Type) - if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME { if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" { // Emit code for runtime.getg() directly instead of calling function. @@ -167,6 +205,12 @@ func transformCall(n *ir.CallExpr) { } } +// transformEarlyCall transforms the arguments of a call with an OFUNCINST node. +func transformEarlyCall(n *ir.CallExpr) { + transformArgs(n) + typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args) +} + // transformCompare transforms a compare operation (currently just equals/not // equals). Corresponds to the "comparison operators" case in // typecheck.typecheck1, including tcArith. @@ -185,7 +229,7 @@ func transformCompare(n *ir.BinaryExpr) { aop, _ := typecheck.Assignop(lt, rt) if aop != ir.OXXX { types.CalcSize(lt) - if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 { + if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 { l = ir.NewConvExpr(base.Pos, aop, rt, l) l.SetTypecheck(1) } @@ -198,7 +242,7 @@ func transformCompare(n *ir.BinaryExpr) { aop, _ := typecheck.Assignop(rt, lt) if aop != ir.OXXX { types.CalcSize(rt) - if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 { + if rt.HasShape() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 { r = ir.NewConvExpr(base.Pos, aop, lt, r) r.SetTypecheck(1) } @@ -303,11 +347,46 @@ assignOK: r := r.(*ir.TypeAssertExpr) stmt.SetOp(ir.OAS2DOTTYPE) r.SetOp(ir.ODOTTYPE2) + case ir.ODYNAMICDOTTYPE: + r := r.(*ir.DynamicTypeAssertExpr) + stmt.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODYNAMICDOTTYPE2) default: break assignOK } checkLHS(0, r.Type()) checkLHS(1, types.UntypedBool) + t := lhs[0].Type() + if t != nil && rhs[0].Type().HasShape() && t.IsInterface() && !types.IdenticalStrict(t, rhs[0].Type()) { + // This is a multi-value assignment (map, channel, or dot-type) + // where the main result is converted to an interface during the + // assignment. Normally, the needed CONVIFACE is not created + // until (*orderState).as2ok(), because the AS2* ops and their + // sub-ops are so tightly intertwined. But we need to create the + // CONVIFACE now to enable dictionary lookups. So, assign the + // results first to temps, so that we can manifest the CONVIFACE + // in assigning the first temp to lhs[0]. If we added the + // CONVIFACE into rhs[0] directly, we would break a lot of later + // code that depends on the tight coupling between the AS2* ops + // and their sub-ops. (Issue #50642). + v := typecheck.Temp(rhs[0].Type()) + ok := typecheck.Temp(types.Types[types.TBOOL]) + as := ir.NewAssignListStmt(base.Pos, stmt.Op(), []ir.Node{v, ok}, []ir.Node{r}) + as.Def = true + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, v)) + as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, ok)) + as.SetTypecheck(1) + // Change stmt to be a normal assignment of the temps to the final + // left-hand-sides. We re-create the original multi-value assignment + // so that it assigns to the temps and add it as an init of stmt. + // + // TODO: fix the order of evaluation, so that the lval of lhs[0] + // is evaluated before rhs[0] (similar to problem in #50672). + stmt.SetOp(ir.OAS2) + stmt.PtrInit().Append(as) + // assignconvfn inserts the CONVIFACE. + stmt.Rhs = []ir.Node{assignconvfn(v, t), ok} + } return } @@ -323,11 +402,22 @@ assignOK: stmt := stmt.(*ir.AssignListStmt) stmt.SetOp(ir.OAS2FUNC) r := rhs[0].(*ir.CallExpr) - r.Use = ir.CallUseList rtyp := r.Type() + mismatched := false + failed := false for i := range lhs { - checkLHS(i, rtyp.Field(i).Type) + result := rtyp.Field(i).Type + checkLHS(i, result) + + if lhs[i].Type() == nil || result == nil { + failed = true + } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { + mismatched = true + } + } + if mismatched && !failed { + typecheck.RewriteMultiValueCall(stmt, r) } return } @@ -340,12 +430,12 @@ assignOK: } } -// Corresponds to typecheck.typecheckargs. +// Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls. func transformArgs(n ir.InitNode) { var list []ir.Node switch n := n.(type) { default: - base.Fatalf("typecheckargs %+v", n.Op()) + base.Fatalf("transformArgs %+v", n.Op()) case *ir.CallExpr: list = n.Args if n.IsDDD { @@ -363,46 +453,13 @@ func transformArgs(n ir.InitNode) { return } - // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). - // Save n as n.Orig for fmt.go. if ir.Orig(n) == n { n.(ir.OrigNode).SetOrig(ir.SepCopy(n)) } - as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil) - as.Rhs.Append(list...) - - // If we're outside of function context, then this call will - // be executed during the generated init function. However, - // init.go hasn't yet created it. Instead, associate the - // temporary variables with InitTodoFunc for now, and init.go - // will reassociate them later when it's appropriate. - static := ir.CurFunc == nil - if static { - ir.CurFunc = typecheck.InitTodoFunc - } - list = nil - for _, f := range t.FieldSlice() { - t := typecheck.Temp(f.Type) - as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t)) - as.Lhs.Append(t) - list = append(list, t) - } - if static { - ir.CurFunc = nil - } - - switch n := n.(type) { - case *ir.CallExpr: - n.Args = list - case *ir.ReturnStmt: - n.Results = list - } - - transformAssign(as, as.Lhs, as.Rhs) - as.SetTypecheck(1) - n.PtrInit().Append(as) + // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). + typecheck.RewriteMultiValueCall(n, list[0]) } // assignconvfn converts node n for assignment to type t. Corresponds to @@ -412,11 +469,18 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node { return n } - if types.Identical(n.Type(), t) { + if n.Op() == ir.OPAREN { + n = n.(*ir.ParenExpr).X + } + + if types.IdenticalStrict(n.Type(), t) { return n } - op, _ := typecheck.Assignop(n.Type(), t) + op, why := Assignop(n.Type(), t) + if op == ir.OXXX { + base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why) + } r := ir.NewConvExpr(base.Pos, op, t, n) r.SetTypecheck(1) @@ -424,7 +488,34 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node { return r } -// Corresponds to typecheck.typecheckaste. +func Assignop(src, dst *types.Type) (ir.Op, string) { + if src == dst { + return ir.OCONVNOP, "" + } + if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil { + return ir.OXXX, "" + } + + // 1. src type is identical to dst (taking shapes into account) + if types.Identical(src, dst) { + // We already know from assignconvfn above that IdenticalStrict(src, + // dst) is false, so the types are not exactly the same and one of + // src or dst is a shape. If dst is an interface (which means src is + // an interface too), we need a real OCONVIFACE op; otherwise we need a + // OCONVNOP. See issue #48453. + if dst.IsInterface() { + return ir.OCONVIFACE, "" + } else { + return ir.OCONVNOP, "" + } + } + return typecheck.Assignop1(src, dst) +} + +// Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly +// only. If convifaceOnly is true, we only do interface conversion. We use this to do +// early insertion of CONVIFACE nodes during noder2, when the function or args may +// have typeparams. func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) { var t *types.Type var i int @@ -495,10 +586,16 @@ func transformSelect(sel *ir.SelectStmt) { if ncase.Comm != nil { n := ncase.Comm oselrecv2 := func(dst, recv ir.Node, def bool) { - n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) - n.Def = def - n.SetTypecheck(1) - ncase.Comm = n + selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) + if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n { + // Must fix Defn for dst, since we are + // completely changing the node. + dst.(*ir.Name).Defn = selrecv + } + selrecv.Def = def + selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) + ncase.Comm = selrecv } switch n.Op() { case ir.OAS: @@ -537,13 +634,31 @@ func transformAsOp(n *ir.AssignOpStmt) { } // transformDot transforms an OXDOT (or ODOT) or ODOT, ODOTPTR, ODOTMETH, -// ODOTINTER, or OCALLPART, as appropriate. It adds in extra nodes as needed to +// ODOTINTER, or OMETHVALUE, as appropriate. It adds in extra nodes as needed to // access embedded fields. Corresponds to typecheck.tcDot. func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node { assert(n.Type() != nil && n.Typecheck() == 1) if n.Op() == ir.OXDOT { n = typecheck.AddImplicitDots(n) n.SetOp(ir.ODOT) + + // Set the Selection field and typecheck flag for any new ODOT nodes + // added by AddImplicitDots(), and also transform to ODOTPTR if + // needed. Equivalent to 'n.X = typecheck(n.X, ctxExpr|ctxType)' in + // tcDot. + for n1 := n; n1.X.Op() == ir.ODOT; { + n1 = n1.X.(*ir.SelectorExpr) + if !n1.Implicit() { + break + } + t1 := n1.X.Type() + if t1.IsPtr() && !t1.Elem().IsInterface() { + t1 = t1.Elem() + n1.SetOp(ir.ODOTPTR) + } + typecheck.Lookdot(n1, t1, 0) + n1.SetTypecheck(1) + } } t := n.X.Type() @@ -561,8 +676,9 @@ func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node { assert(f != nil) if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall { - n.SetOp(ir.OCALLPART) - n.SetType(typecheck.MethodValueWrapper(n).Type()) + n.SetOp(ir.OMETHVALUE) + // This converts a method type to a function type. See issue 47775. + n.SetType(typecheck.NewMethodType(n.Type(), nil)) } return n } @@ -594,7 +710,11 @@ func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) { s := n.Sel m := typecheck.Lookdot1(n, s, t, ms, 0) - assert(m != nil) + if !t.HasShape() { + // It's OK to not find the method if t is instantiated by shape types, + // because we will use the methods on the generic type anyway. + assert(m != nil) + } n.SetOp(ir.OMETHEXPR) n.Selection = m @@ -610,11 +730,11 @@ func transformAppend(n *ir.CallExpr) ir.Node { assert(t.IsSlice()) if n.IsDDD { - if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { - return n - } - - args[1] = assignconvfn(args[1], t.Underlying()) + // assignconvfn is of args[1] not required here, as the + // types of args[0] and args[1] don't need to match + // (They will both have an underlying type which are + // slices of identical base types, or be []byte and string.) + // See issue 53888. return n } @@ -780,7 +900,7 @@ func transformBuiltin(n *ir.CallExpr) ir.Node { transformArgs(n) fallthrough - case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRINGDATA: u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0]) u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init switch op { @@ -790,12 +910,15 @@ func transformBuiltin(n *ir.CallExpr) ir.Node { return transformRealImag(u1.(*ir.UnaryExpr)) case ir.OPANIC: return transformPanic(u1.(*ir.UnaryExpr)) - case ir.OCLOSE, ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: + // This corresponds to the EvalConst() call near end of typecheck(). + return typecheck.EvalConst(u1) + case ir.OCLOSE, ir.ONEW, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: // nothing more to do return u1 } - case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: + case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: transformArgs(n) b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1]) n1 := typed(n.Type(), ir.InitExpr(n.Init(), b)) @@ -856,7 +979,7 @@ func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64 // transformCompLit transforms n to an OARRAYLIT, OSLICELIT, OMAPLIT, or // OSTRUCTLIT node, with any needed conversions. Corresponds to -// typecheck.tcCompLit. +// typecheck.tcCompLit (and includes parts corresponding to tcStructLitKey). func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { assert(n.Type() != nil && n.Typecheck() == 1) lno := base.Pos @@ -911,9 +1034,7 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { f := t.Field(i) n1 = assignconvfn(n1, f.Type) - sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1) - sk.Offset = f.Offset - ls[i] = sk + ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1) } assert(len(ls) >= t.NumFields()) } else { @@ -922,33 +1043,28 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { for i, l := range ls { ir.SetPos(l) - if l.Op() == ir.OKEY { - kv := l.(*ir.KeyExpr) - key := kv.Key - - // Sym might have resolved to name in other top-level - // package, because of import dot. Redirect to correct sym - // before we do the lookup. - s := key.Sym() - if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil { - s = typecheck.Lookup(s.Name) - } - - // An OXDOT uses the Sym field to hold - // the field to the right of the dot, - // so s will be non-nil, but an OXDOT - // is never a valid struct literal key. - assert(!(s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank())) - - l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value) - ls[i] = l + kv := l.(*ir.KeyExpr) + key := kv.Key + + s := key.Sym() + if types.IsExported(s.Name) && s.Pkg != types.LocalPkg { + // Exported field names should always have + // local pkg. We only need to do this + // adjustment for generic functions that are + // being transformed after being imported + // from another package. + s = typecheck.Lookup(s.Name) } - assert(l.Op() == ir.OSTRUCTKEY) - l := l.(*ir.StructKeyExpr) + // An OXDOT uses the Sym field to hold + // the field to the right of the dot, + // so s will be non-nil, but an OXDOT + // is never a valid struct literal key. + assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank())) - f := typecheck.Lookdot1(nil, l.Field, t, t.Fields(), 0) - l.Offset = f.Offset + f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0) + l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value) + ls[i] = l l.Value = assignconvfn(l.Value, f.Type) } @@ -959,3 +1075,11 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) { return n } + +// transformAddr corresponds to typecheck.tcAddr. +func transformAddr(n *ir.AddrExpr) { + switch n.X.Op() { + case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT: + n.SetOp(ir.OPTRLIT) + } +} diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index 8680559a412970..57b35e602b821b 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -5,7 +5,6 @@ package noder import ( - "bytes" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" @@ -22,23 +21,32 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg { case g.self: return types.LocalPkg case types2.Unsafe: - return ir.Pkgs.Unsafe + return types.UnsafePkg } return types.NewPkg(pkg.Path(), pkg.Name()) } +var universeAny = types2.Universe.Lookup("any").Type() + // typ converts a types2.Type to a types.Type, including caching of previously // translated types. func (g *irgen) typ(typ types2.Type) *types.Type { + // Defer the CheckSize calls until we have fully-defined a + // (possibly-recursive) top-level type. + types.DeferCheckSize() res := g.typ1(typ) - - // Calculate the size for all concrete types seen by the frontend. The old - // typechecker calls CheckSize() a lot, and we want to eliminate calling - // it eventually, so we should do it here instead. We only call it for - // top-level types (i.e. we do it here rather in typ1), to make sure that - // recursive types have been fully constructed before we call CheckSize. - if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() { - types.CheckSize(res) + types.ResumeCheckSize() + + // Finish up any types on typesToFinalize, now that we are at the top of a + // fully-defined (possibly recursive) type. fillinMethods could create more + // types to finalize. + for len(g.typesToFinalize) > 0 { + l := len(g.typesToFinalize) + info := g.typesToFinalize[l-1] + g.typesToFinalize = g.typesToFinalize[:l-1] + types.DeferCheckSize() + g.fillinMethods(info.typ, info.ntyp) + types.ResumeCheckSize() } return res } @@ -47,6 +55,12 @@ func (g *irgen) typ(typ types2.Type) *types.Type { // constructed part of a recursive type. Should not be called from outside this // file (g.typ is the "external" entry point). func (g *irgen) typ1(typ types2.Type) *types.Type { + // See issue 49583: the type checker has trouble keeping track of aliases, + // but for such a common alias as any we can improve things by preserving a + // pointer identity that can be checked when formatting type strings. + if typ == universeAny { + return types.AnyType + } // Cache type2-to-type mappings. Important so that each defined generic // type (instantiated or not) has a single types.Type representation. // Also saves a lot of computation and memory by avoiding re-translating @@ -54,6 +68,12 @@ func (g *irgen) typ1(typ types2.Type) *types.Type { res, ok := g.typs[typ] if !ok { res = g.typ0(typ) + // Calculate the size for all concrete types seen by the frontend. + // This is the replacement for the CheckSize() calls in the types1 + // typechecker. These will be deferred until the top-level g.typ(). + if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() { + types.CheckSize(res) + } g.typs[typ] = res } return res @@ -61,25 +81,12 @@ func (g *irgen) typ1(typ types2.Type) *types.Type { // instTypeName2 creates a name for an instantiated type, base on the type args // (given as types2 types). -func instTypeName2(name string, targs []types2.Type) string { - b := bytes.NewBufferString(name) - b.WriteByte('[') - for i, targ := range targs { - if i > 0 { - b.WriteByte(',') - } - tname := types2.TypeString(targ, - func(*types2.Package) string { return "" }) - if strings.Index(tname, ", ") >= 0 { - // types2.TypeString puts spaces after a comma in a type - // list, but we don't want spaces in our actual type names - // and method/function names derived from them. - tname = strings.Replace(tname, ", ", ",", -1) - } - b.WriteString(tname) +func (g *irgen) instTypeName2(name string, targs *types2.TypeList) string { + rparams := make([]*types.Type, targs.Len()) + for i := range rparams { + rparams[i] = g.typ(targs.At(i)) } - b.WriteByte(']') - return b.String() + return typecheck.InstTypeName(name, rparams) } // typ0 converts a types2.Type to a types.Type, but doesn't do the caching check @@ -89,60 +96,88 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { case *types2.Basic: return g.basic(typ) case *types2.Named: - if typ.TParams() != nil { + // If tparams is set, but targs is not, typ is a base generic + // type. typ is appearing as part of the source type of an alias, + // since that is the only use of a generic type that doesn't + // involve instantiation. We just translate the named type in the + // normal way below using g.obj(). + if typ.TypeParams() != nil && typ.TypeArgs() != nil { // typ is an instantiation of a defined (named) generic type. // This instantiation should also be a defined (named) type. // types2 gives us the substituted type in t.Underlying() // The substituted type may or may not still have type // params. We might, for example, be substituting one type // param for another type param. + // + // When converted to types.Type, typ has a unique name, + // based on the names of the type arguments. + instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs()) + s := g.pkg(typ.Obj().Pkg()).Lookup(instName) - if typ.TArgs() == nil { - base.Fatalf("In typ0, Targs should be set if TParams is set") - } + // Make sure the base generic type exists in type1 (it may + // not yet if we are referecing an imported generic type, as + // opposed to a generic type declared in this package). Make + // sure to do this lookup before checking s.Def, in case + // s.Def gets defined while importing base (if an imported + // type). (Issue #50486). + base := g.obj(typ.Origin().Obj()) - // When converted to types.Type, typ must have a name, - // based on the names of the type arguments. We need a - // name to deal with recursive generic types (and it also - // looks better when printing types). - instName := instTypeName2(typ.Obj().Name(), typ.TArgs()) - s := g.pkg(typ.Obj().Pkg()).Lookup(instName) if s.Def != nil { - // We have already encountered this instantiation, - // so use the type we previously created, since there + // We have already encountered this instantiation. + // Use the type we previously created, since there // must be exactly one instance of a defined type. return s.Def.Type() } + if base.Class == ir.PAUTO { + // If the base type is a local type, we want to pop + // this instantiated type symbol/definition when we + // leave the containing block, so we don't use it + // incorrectly later. + types.Pushdcl(s) + } + // Create a forwarding type first and put it in the g.typs - // map, in order to deal with recursive generic types. - // Fully set up the extra ntyp information (Def, RParams, - // which may set HasTParam) before translating the - // underlying type itself, so we handle recursion - // correctly, including via method signatures. - ntyp := newIncompleteNamedType(g.pos(typ.Obj().Pos()), s) + // map, in order to deal with recursive generic types + // (including via method signatures). Set up the extra + // ntyp information (Def, RParams, which may set + // HasTParam) before translating the underlying type + // itself, so we handle recursion correctly. + ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s) g.typs[typ] = ntyp // If ntyp still has type params, then we must be // referencing something like 'value[T2]', as when - // specifying the generic receiver of a method, - // where value was defined as "type value[T any] - // ...". Save the type args, which will now be the - // new type of the current type. + // specifying the generic receiver of a method, where + // value was defined as "type value[T any] ...". Save the + // type args, which will now be the new typeparams of the + // current type. // // If ntyp does not have type params, we are saving the - // concrete types used to instantiate this type. We'll use - // these when instantiating the methods of the + // non-generic types used to instantiate this type. We'll + // use these when instantiating the methods of the // instantiated type. - rparams := make([]*types.Type, len(typ.TArgs())) - for i, targ := range typ.TArgs() { - rparams[i] = g.typ1(targ) + targs := typ.TypeArgs() + rparams := make([]*types.Type, targs.Len()) + for i := range rparams { + rparams[i] = g.typ1(targs.At(i)) } ntyp.SetRParams(rparams) //fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam()) + // Save the symbol for the base generic type. + ntyp.SetOrigType(base.Type()) ntyp.SetUnderlying(g.typ1(typ.Underlying())) - g.fillinMethods(typ, ntyp) + if typ.NumMethods() != 0 { + // Save a delayed call to g.fillinMethods() (once + // potentially recursive types have been fully + // resolved). + g.typesToFinalize = append(g.typesToFinalize, + &typeDelayInfo{ + typ: typ, + ntyp: ntyp, + }) + } return ntyp } obj := g.obj(typ.Obj()) @@ -183,12 +218,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { for i := range embeddeds { // TODO(mdempsky): Get embedding position. e := typ.EmbeddedType(i) - if t := types2.AsInterface(e); t != nil && t.IsComparable() { - // Ignore predefined type 'comparable', since it - // doesn't resolve and it doesn't have any - // relevant methods. - continue - } + + // With Go 1.18, an embedded element can be any type, not + // just an interface. embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e)) j++ } @@ -197,27 +229,52 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { methods := make([]*types.Field, typ.NumExplicitMethods()) for i := range methods { m := typ.ExplicitMethod(i) - mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature)) + mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature)) methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp) } - return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...)) + return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...), typ.IsImplicit()) case *types2.TypeParam: - tp := types.NewTypeParam(g.tpkg(typ)) // Save the name of the type parameter in the sym of the type. // Include the types2 subscript in the sym name - sym := g.pkg(typ.Obj().Pkg()).Lookup(types2.TypeString(typ, func(*types2.Package) string { return "" })) - tp.SetSym(sym) + pkg := g.tpkg(typ) + // Create the unique types1 name for a type param, using its context + // with a function, type, or method declaration. Also, map blank type + // param names to a unique name based on their type param index. The + // unique blank names will be exported, but will be reverted during + // types2 and gcimporter import. + assert(g.curDecl != "") + nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index()) + sym := pkg.Lookup(nm) + if sym.Def != nil { + // Make sure we use the same type param type for the same + // name, whether it is created during types1-import or + // this types2-to-types1 translation. + return sym.Def.Type() + } + obj := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym) + sym.Def = obj + tp := types.NewTypeParam(obj, typ.Index()) + obj.SetType(tp) // Set g.typs[typ] in case the bound methods reference typ. g.typs[typ] = tp - // TODO(danscales): we don't currently need to use the bounds - // anywhere, so eventually we can probably remove. - bound := g.typ1(typ.Bound()) - *tp.Methods() = *bound.Methods() + bound := g.typ1(typ.Constraint()) + tp.SetBound(bound) return tp + case *types2.Union: + nt := typ.Len() + tlist := make([]*types.Type, nt) + tildes := make([]bool, nt) + for i := range tlist { + t := typ.Term(i) + tlist[i] = g.typ1(t.Type()) + tildes[i] = t.Tilde() + } + return types.NewUnion(tlist, tildes) + case *types2.Tuple: // Tuples are used for the type of a function call (i.e. the // return value of the function). @@ -238,76 +295,92 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { } } -// fillinMethods fills in the method name nodes and types for a defined type. This -// is needed for later typechecking when looking up methods of instantiated types, -// and for actually generating the methods for instantiated types. +// fillinMethods fills in the method name nodes and types for a defined type with at +// least one method. This is needed for later typechecking when looking up methods of +// instantiated types, and for actually generating the methods for instantiated +// types. func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { - if typ.NumMethods() != 0 { - targs := make([]ir.Node, len(typ.TArgs())) - for i, targ := range typ.TArgs() { - targs[i] = ir.TypeNode(g.typ1(targ)) - } + targs2 := typ.TypeArgs() + targs := make([]*types.Type, targs2.Len()) + for i := range targs { + targs[i] = g.typ1(targs2.At(i)) + } - methods := make([]*types.Field, typ.NumMethods()) - for i := range methods { - m := typ.Method(i) - meth := g.obj(m) - recvType := types2.AsSignature(m.Type()).Recv().Type() - ptr := types2.AsPointer(recvType) - if ptr != nil { - recvType = ptr.Elem() - } - if recvType != types2.Type(typ) { - // Unfortunately, meth is the type of the method of the - // generic type, so we have to do a substitution to get - // the name/type of the method of the instantiated type, - // using m.Type().RParams() and typ.TArgs() - inst2 := instTypeName2("", typ.TArgs()) - name := meth.Sym().Name - i1 := strings.Index(name, "[") - i2 := strings.Index(name[i1:], "]") - assert(i1 >= 0 && i2 >= 0) - // Generate the name of the instantiated method. - name = name[0:i1] + inst2 + name[i1+i2+1:] - newsym := meth.Sym().Pkg.Lookup(name) - var meth2 *ir.Name - if newsym.Def != nil { - meth2 = newsym.Def.(*ir.Name) - } else { - meth2 = ir.NewNameAt(meth.Pos(), newsym) - rparams := types2.AsSignature(m.Type()).RParams() - tparams := make([]*types.Field, len(rparams)) - for i, rparam := range rparams { - tparams[i] = types.NewField(src.NoXPos, nil, g.typ1(rparam.Type())) - } - assert(len(tparams) == len(targs)) - subst := &subster{ - g: g, - tparams: tparams, - targs: targs, - } - // Do the substitution of the type - meth2.SetType(subst.typ(meth.Type())) - newsym.Def = meth2 + methods := make([]*types.Field, typ.NumMethods()) + for i := range methods { + m := typ.Method(i) + recvType := deref2(types2.AsSignature(m.Type()).Recv().Type()) + var meth *ir.Name + imported := false + if m.Pkg() != g.self { + // Imported methods cannot be loaded by name (what + // g.obj() does) - they must be loaded via their + // type. + meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name) + // XXX Because Obj() returns the object of the base generic + // type, we have to still do the method translation below. + imported = true + } else { + meth = g.obj(m) + } + assert(recvType == types2.Type(typ)) + if imported { + // Unfortunately, meth is the type of the method of the + // generic type, so we have to do a substitution to get + // the name/type of the method of the instantiated type, + // using m.Type().RParams() and typ.TArgs() + inst2 := g.instTypeName2("", typ.TypeArgs()) + name := meth.Sym().Name + i1 := strings.Index(name, "[") + i2 := strings.Index(name[i1:], "]") + assert(i1 >= 0 && i2 >= 0) + // Generate the name of the instantiated method. + name = name[0:i1] + inst2 + name[i1+i2+1:] + newsym := meth.Sym().Pkg.Lookup(name) + var meth2 *ir.Name + if newsym.Def != nil { + meth2 = newsym.Def.(*ir.Name) + } else { + meth2 = ir.NewNameAt(meth.Pos(), newsym) + rparams := types2.AsSignature(m.Type()).RecvTypeParams() + tparams := make([]*types.Type, rparams.Len()) + // Set g.curDecl to be the method context, so type + // params in the receiver of the method that we are + // translating gets the right unique name. We could + // be in a top-level typeDecl, so save and restore + // the current contents of g.curDecl. + savedCurDecl := g.curDecl + g.curDecl = typ.Obj().Name() + "." + m.Name() + for i := range tparams { + tparams[i] = g.typ1(rparams.At(i)) } - meth = meth2 + g.curDecl = savedCurDecl + assert(len(tparams) == len(targs)) + ts := typecheck.Tsubster{ + Tparams: tparams, + Targs: targs, + } + // Do the substitution of the type + meth2.SetType(ts.Typ(meth.Type())) + newsym.Def = meth2 } - methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) - methods[i].Nname = meth - } - ntyp.Methods().Set(methods) - if !ntyp.HasTParam() { - // Generate all the methods for a new fully-instantiated type. - g.instTypeList = append(g.instTypeList, ntyp) + meth = meth2 } + methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) + methods[i].Nname = meth + } + ntyp.Methods().Set(methods) + if !ntyp.HasTParam() && !ntyp.HasShape() { + // Generate all the methods for a new fully-instantiated type. + typecheck.NeedInstType(ntyp) } } func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type { - tparams2 := sig.TParams() - tparams := make([]*types.Field, len(tparams2)) + tparams2 := sig.TypeParams() + tparams := make([]*types.Field, tparams2.Len()) for i := range tparams { - tp := tparams2[i] + tp := tparams2.At(i).Obj() tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type())) } @@ -346,7 +419,7 @@ func (g *irgen) selector(obj types2.Object) *types.Sym { return pkg.Lookup(name) } -// tpkg returns the package that a function, interface, or struct type +// tpkg returns the package that a function, interface, struct, or typeparam type // expression appeared in. // // Caveat: For the degenerate types "func()", "interface{}", and @@ -356,36 +429,39 @@ func (g *irgen) selector(obj types2.Object) *types.Sym { // particular types is because go/types does *not* report it for // them. So in practice this limitation is probably moot. func (g *irgen) tpkg(typ types2.Type) *types.Pkg { - anyObj := func() types2.Object { - switch typ := typ.(type) { - case *types2.Signature: - if recv := typ.Recv(); recv != nil { - return recv - } - if params := typ.Params(); params.Len() > 0 { - return params.At(0) - } - if results := typ.Results(); results.Len() > 0 { - return results.At(0) - } - case *types2.Struct: - if typ.NumFields() > 0 { - return typ.Field(0) - } - case *types2.Interface: - if typ.NumExplicitMethods() > 0 { - return typ.ExplicitMethod(0) - } - } - return nil - } - - if obj := anyObj(); obj != nil { + if obj := anyObj(typ); obj != nil { return g.pkg(obj.Pkg()) } return types.LocalPkg } +// anyObj returns some object accessible from typ, if any. +func anyObj(typ types2.Type) types2.Object { + switch typ := typ.(type) { + case *types2.Signature: + if recv := typ.Recv(); recv != nil { + return recv + } + if params := typ.Params(); params.Len() > 0 { + return params.At(0) + } + if results := typ.Results(); results.Len() > 0 { + return results.At(0) + } + case *types2.Struct: + if typ.NumFields() > 0 { + return typ.Field(0) + } + case *types2.Interface: + if typ.NumExplicitMethods() > 0 { + return typ.ExplicitMethod(0) + } + case *types2.TypeParam: + return typ.Obj() + } + return nil +} + func (g *irgen) basic(typ *types2.Basic) *types.Type { switch typ.Name() { case "byte": @@ -430,3 +506,11 @@ var dirs = [...]types.ChanDir{ types2.SendOnly: types.Csend, types2.RecvOnly: types.Crecv, } + +// deref2 does a single deref of types2 type t, if it is a pointer type. +func deref2(t types2.Type) types2.Type { + if ptr := types2.AsPointer(t); ptr != nil { + t = ptr.Elem() + } + return t +} diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go new file mode 100644 index 00000000000000..b8e4fe78d7e037 --- /dev/null +++ b/src/cmd/compile/internal/noder/unified.go @@ -0,0 +1,402 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "fmt" + "internal/goversion" + "internal/pkgbits" + "io" + "runtime" + "sort" + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/inline" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/compile/internal/types2" + "cmd/internal/src" +) + +// localPkgReader holds the package reader used for reading the local +// package. It exists so the unified IR linker can refer back to it +// later. +var localPkgReader *pkgReader + +// unified constructs the local package's Internal Representation (IR) +// from its syntax tree (AST). +// +// The pipeline contains 2 steps: +// +// 1. Generate the export data "stub". +// +// 2. Generate the IR from the export data above. +// +// The package data "stub" at step (1) contains everything from the local package, +// but nothing that has been imported. When we're actually writing out export data +// to the output files (see writeNewExport), we run the "linker", which: +// +// - Updates compiler extensions data (e.g. inlining cost, escape analysis results). +// +// - Handles re-exporting any transitive dependencies. +// +// - Prunes out any unnecessary details (e.g. non-inlineable functions, because any +// downstream importers only care about inlinable functions). +// +// The source files are typechecked twice: once before writing the export data +// using types2, and again after reading the export data using gc/typecheck. +// The duplication of work will go away once we only use the types2 type checker, +// removing the gc/typecheck step. For now, it is kept because: +// +// - It reduces the engineering costs in maintaining a fork of typecheck +// (e.g. no need to backport fixes like CL 327651). +// +// - It makes it easier to pass toolstash -cmp. +// +// - Historically, we would always re-run the typechecker after importing a package, +// even though we know the imported data is valid. It's not ideal, but it's +// not causing any problems either. +// +// - gc/typecheck is still in charge of some transformations, such as rewriting +// multi-valued function calls or transforming ir.OINDEX to ir.OINDEXMAP. +// +// Using the syntax tree with types2, which has a complete representation of generics, +// the unified IR has the full typed AST needed for introspection during step (1). +// In other words, we have all the necessary information to build the generic IR form +// (see writer.captureVars for an example). +func unified(noders []*noder) { + inline.InlineCall = unifiedInlineCall + typecheck.HaveInlineBody = unifiedHaveInlineBody + + data := writePkgStub(noders) + + // We already passed base.Flag.Lang to types2 to handle validating + // the user's source code. Bump it up now to the current version and + // re-parse, so typecheck doesn't complain if we construct IR that + // utilizes newer Go features. + base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version) + types.ParseLangFlag() + + target := typecheck.Target + + typecheck.TypecheckAllowed = true + + localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data)) + readPackage(localPkgReader, types.LocalPkg, true) + + r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) + r.pkgInit(types.LocalPkg, target) + + // Type-check any top-level assignments. We ignore non-assignments + // here because other declarations are typechecked as they're + // constructed. + for i, ndecls := 0, len(target.Decls); i < ndecls; i++ { + switch n := target.Decls[i]; n.Op() { + case ir.OAS, ir.OAS2: + target.Decls[i] = typecheck.Stmt(n) + } + } + + readBodies(target) + + // Check that nothing snuck past typechecking. + for _, n := range target.Decls { + if n.Typecheck() == 0 { + base.FatalfAt(n.Pos(), "missed typecheck: %v", n) + } + + // For functions, check that at least their first statement (if + // any) was typechecked too. + if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 { + if stmt := fn.Body[0]; stmt.Typecheck() == 0 { + base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt) + } + } + } + + base.ExitIfErrors() // just in case +} + +// readBodies iteratively expands all pending dictionaries and +// function bodies. +func readBodies(target *ir.Package) { + // Don't use range--bodyIdx can add closures to todoBodies. + for { + // The order we expand dictionaries and bodies doesn't matter, so + // pop from the end to reduce todoBodies reallocations if it grows + // further. + // + // However, we do at least need to flush any pending dictionaries + // before reading bodies, because bodies might reference the + // dictionaries. + + if len(todoDicts) > 0 { + fn := todoDicts[len(todoDicts)-1] + todoDicts = todoDicts[:len(todoDicts)-1] + fn() + continue + } + + if len(todoBodies) > 0 { + fn := todoBodies[len(todoBodies)-1] + todoBodies = todoBodies[:len(todoBodies)-1] + + pri, ok := bodyReader[fn] + assert(ok) + pri.funcBody(fn) + + // Instantiated generic function: add to Decls for typechecking + // and compilation. + if fn.OClosure == nil && len(pri.dict.targs) != 0 { + target.Decls = append(target.Decls, fn) + } + + continue + } + + break + } + + todoDicts = nil + todoBodies = nil +} + +// writePkgStub type checks the given parsed source files, +// writes an export data package stub representing them, +// and returns the result. +func writePkgStub(noders []*noder) string { + m, pkg, info := checkFiles(noders) + + pw := newPkgWriter(m, pkg, info) + + pw.collectDecls(noders) + + publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic) + privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate) + + assert(publicRootWriter.Idx == pkgbits.PublicRootIdx) + assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx) + + { + w := publicRootWriter + w.pkg(pkg) + w.Bool(false) // TODO(mdempsky): Remove; was "has init" + + scope := pkg.Scope() + names := scope.Names() + w.Len(len(names)) + for _, name := range names { + w.obj(scope.Lookup(name), nil) + } + + w.Sync(pkgbits.SyncEOF) + w.Flush() + } + + { + w := privateRootWriter + w.pkgInit(noders) + w.Flush() + } + + var sb strings.Builder + pw.DumpTo(&sb) + + // At this point, we're done with types2. Make sure the package is + // garbage collected. + freePackage(pkg) + + return sb.String() +} + +// freePackage ensures the given package is garbage collected. +func freePackage(pkg *types2.Package) { + // The GC test below relies on a precise GC that runs finalizers as + // soon as objects are unreachable. Our implementation provides + // this, but other/older implementations may not (e.g., Go 1.4 does + // not because of #22350). To avoid imposing unnecessary + // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test + // during bootstrapping. + if base.CompilerBootstrap { + return + } + + // Set a finalizer on pkg so we can detect if/when it's collected. + done := make(chan struct{}) + runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) }) + + // Important: objects involved in cycles are not finalized, so zero + // out pkg to break its cycles and allow the finalizer to run. + *pkg = types2.Package{} + + // It typically takes just 1 or 2 cycles to release pkg, but it + // doesn't hurt to try a few more times. + for i := 0; i < 10; i++ { + select { + case <-done: + return + default: + runtime.GC() + } + } + + base.Fatalf("package never finalized") +} + +// readPackage reads package export data from pr to populate +// importpkg. +// +// localStub indicates whether pr is reading the stub export data for +// the local package, as opposed to relocated export data for an +// import. +func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { + { + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + + pkg := r.pkg() + base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) + + r.Bool() // TODO(mdempsky): Remove; was "has init" + + for i, n := 0, r.Len(); i < n; i++ { + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + idx := r.Reloc(pkgbits.RelocObj) + assert(r.Len() == 0) + + path, name, code := r.p.PeekObj(idx) + if code != pkgbits.ObjStub { + objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil, nil, nil} + } + } + + r.Sync(pkgbits.SyncEOF) + } + + if !localStub { + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate) + + if r.Bool() { + sym := importpkg.Lookup(".inittask") + task := ir.NewNameAt(src.NoXPos, sym) + task.Class = ir.PEXTERN + sym.Def = task + } + + for i, n := 0, r.Len(); i < n; i++ { + path := r.String() + name := r.String() + idx := r.Reloc(pkgbits.RelocBody) + + sym := types.NewPkg(path, "").Lookup(name) + if _, ok := importBodyReader[sym]; !ok { + importBodyReader[sym] = pkgReaderIndex{pr, idx, nil, nil, nil} + } + } + + r.Sync(pkgbits.SyncEOF) + } +} + +// writeUnifiedExport writes to `out` the finalized, self-contained +// Unified IR export data file for the current compilation unit. +func writeUnifiedExport(out io.Writer) { + l := linker{ + pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), + + pkgs: make(map[string]pkgbits.Index), + decls: make(map[*types.Sym]pkgbits.Index), + bodies: make(map[*types.Sym]pkgbits.Index), + } + + publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic) + privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate) + assert(publicRootWriter.Idx == pkgbits.PublicRootIdx) + assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx) + + var selfPkgIdx pkgbits.Index + + { + pr := localPkgReader + r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + + r.Sync(pkgbits.SyncPkg) + selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg)) + + r.Bool() // TODO(mdempsky): Remove; was "has init" + + for i, n := 0, r.Len(); i < n; i++ { + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + idx := r.Reloc(pkgbits.RelocObj) + assert(r.Len() == 0) + + xpath, xname, xtag := pr.PeekObj(idx) + assert(xpath == pr.PkgPath()) + assert(xtag != pkgbits.ObjStub) + + if types.IsExported(xname) { + l.relocIdx(pr, pkgbits.RelocObj, idx) + } + } + + r.Sync(pkgbits.SyncEOF) + } + + { + var idxs []pkgbits.Index + for _, idx := range l.decls { + idxs = append(idxs, idx) + } + sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] }) + + w := publicRootWriter + + w.Sync(pkgbits.SyncPkg) + w.Reloc(pkgbits.RelocPkg, selfPkgIdx) + w.Bool(false) // TODO(mdempsky): Remove; was "has init" + + w.Len(len(idxs)) + for _, idx := range idxs { + w.Sync(pkgbits.SyncObject) + w.Bool(false) + w.Reloc(pkgbits.RelocObj, idx) + w.Len(0) + } + + w.Sync(pkgbits.SyncEOF) + w.Flush() + } + + { + type symIdx struct { + sym *types.Sym + idx pkgbits.Index + } + var bodies []symIdx + for sym, idx := range l.bodies { + bodies = append(bodies, symIdx{sym, idx}) + } + sort.Slice(bodies, func(i, j int) bool { return bodies[i].idx < bodies[j].idx }) + + w := privateRootWriter + + w.Bool(typecheck.Lookup(".inittask").Def != nil) + + w.Len(len(bodies)) + for _, body := range bodies { + w.String(body.sym.Pkg.Path) + w.String(body.sym.Name) + w.Reloc(pkgbits.RelocBody, body.idx) + } + + w.Sync(pkgbits.SyncEOF) + w.Flush() + } + + base.Ctxt.Fingerprint = l.pw.DumpTo(out) +} diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go index b926222c89c417..dcacae7480c297 100644 --- a/src/cmd/compile/internal/noder/validate.go +++ b/src/cmd/compile/internal/noder/validate.go @@ -55,7 +55,15 @@ func (g *irgen) validate(n syntax.Node) { case *syntax.CallExpr: tv := g.info.Types[n.Fun] if tv.IsBuiltin() { - switch builtin := n.Fun.(type) { + fun := n.Fun + for { + builtin, ok := fun.(*syntax.ParenExpr) + if !ok { + break + } + fun = builtin.X + } + switch builtin := fun.(type) { case *syntax.Name: g.validateBuiltin(builtin.Value, n) case *syntax.SelectorExpr: @@ -73,7 +81,16 @@ func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) { // Check that types2+gcSizes calculates sizes the same // as cmd/compile does. - got, ok := constant.Int64Val(g.info.Types[call].Value) + tv := g.info.Types[call] + if !tv.IsValue() { + base.FatalfAt(g.pos(call), "expected a value") + } + + if tv.Value == nil { + break // unsafe op is not a constant, so no further validation + } + + got, ok := constant.Int64Val(tv.Value) if !ok { base.FatalfAt(g.pos(call), "expected int64 constant value") } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go new file mode 100644 index 00000000000000..198bae71903da0 --- /dev/null +++ b/src/cmd/compile/internal/noder/writer.go @@ -0,0 +1,2735 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package noder + +import ( + "fmt" + "internal/pkgbits" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" +) + +// This file implements the Unified IR package writer and defines the +// Unified IR export data format. +// +// Low-level coding details (e.g., byte-encoding of individual +// primitive values, or handling element bitstreams and +// cross-references) are handled by internal/pkgbits, so here we only +// concern ourselves with higher-level worries like mapping Go +// language constructs into elements. + +// There are two central types in the writing process: the "writer" +// type handles writing out individual elements, while the "pkgWriter" +// type keeps track of which elements have already been created. +// +// For each sort of "thing" (e.g., position, package, object, type) +// that can be written into the export data, there are generally +// several methods that work together: +// +// - writer.thing handles writing out a *use* of a thing, which often +// means writing a relocation to that thing's encoded index. +// +// - pkgWriter.thingIdx handles reserving an index for a thing, and +// writing out any elements needed for the thing. +// +// - writer.doThing handles writing out the *definition* of a thing, +// which in general is a mix of low-level coding primitives (e.g., +// ints and strings) or uses of other things. +// +// A design goal of Unified IR is to have a single, canonical writer +// implementation, but multiple reader implementations each tailored +// to their respective needs. For example, within cmd/compile's own +// backend, inlining is implemented largely by just re-running the +// function body reading code. + +// TODO(mdempsky): Add an importer for Unified IR to the x/tools repo, +// and better document the file format boundary between public and +// private data. + +// A pkgWriter constructs Unified IR export data from the results of +// running the types2 type checker on a Go compilation unit. +type pkgWriter struct { + pkgbits.PkgEncoder + + m posMap + curpkg *types2.Package + info *types2.Info + + // Indices for previously written syntax and types2 things. + + posBasesIdx map[*syntax.PosBase]pkgbits.Index + pkgsIdx map[*types2.Package]pkgbits.Index + typsIdx map[types2.Type]pkgbits.Index + objsIdx map[types2.Object]pkgbits.Index + + // Maps from types2.Objects back to their syntax.Decl. + + funDecls map[*types2.Func]*syntax.FuncDecl + typDecls map[*types2.TypeName]typeDeclGen + + // linknames maps package-scope objects to their linker symbol name, + // if specified by a //go:linkname directive. + linknames map[types2.Object]string + + // cgoPragmas accumulates any //go:cgo_* pragmas that need to be + // passed through to cmd/link. + cgoPragmas [][]string +} + +// newPkgWriter returns an initialized pkgWriter for the specified +// package. +func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter { + return &pkgWriter{ + PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), + + m: m, + curpkg: pkg, + info: info, + + pkgsIdx: make(map[*types2.Package]pkgbits.Index), + objsIdx: make(map[types2.Object]pkgbits.Index), + typsIdx: make(map[types2.Type]pkgbits.Index), + + posBasesIdx: make(map[*syntax.PosBase]pkgbits.Index), + + funDecls: make(map[*types2.Func]*syntax.FuncDecl), + typDecls: make(map[*types2.TypeName]typeDeclGen), + + linknames: make(map[types2.Object]string), + } +} + +// errorf reports a user error about thing p. +func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) { + base.ErrorfAt(pw.m.pos(p), msg, args...) +} + +// fatalf reports an internal compiler error about thing p. +func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) { + base.FatalfAt(pw.m.pos(p), msg, args...) +} + +// unexpected reports a fatal error about a thing of unexpected +// dynamic type. +func (pw *pkgWriter) unexpected(what string, p poser) { + pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p) +} + +// typeOf returns the Type of the given value expression. +func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type { + tv, ok := pw.info.Types[expr] + if !ok { + pw.fatalf(expr, "missing Types entry: %v", syntax.String(expr)) + } + if !tv.IsValue() { + pw.fatalf(expr, "expected value: %v", syntax.String(expr)) + } + return tv.Type +} + +// A writer provides APIs for writing out an individual element. +type writer struct { + p *pkgWriter + + pkgbits.Encoder + + // sig holds the signature for the current function body, if any. + sig *types2.Signature + + // TODO(mdempsky): We should be able to prune localsIdx whenever a + // scope closes, and then maybe we can just use the same map for + // storing the TypeParams too (as their TypeName instead). + + // localsIdx tracks any local variables declared within this + // function body. It's unused for writing out non-body things. + localsIdx map[*types2.Var]int + + // closureVars tracks any free variables that are referenced by this + // function body. It's unused for writing out non-body things. + closureVars []posVar + closureVarsIdx map[*types2.Var]int // index of previously seen free variables + + dict *writerDict + + // derived tracks whether the type being written out references any + // type parameters. It's unused for writing non-type things. + derived bool +} + +// A writerDict tracks types and objects that are used by a declaration. +type writerDict struct { + implicits []*types2.TypeName + + // derived is a slice of type indices for computing derived types + // (i.e., types that depend on the declaration's type parameters). + derived []derivedInfo + + // derivedIdx maps a Type to its corresponding index within the + // derived slice, if present. + derivedIdx map[types2.Type]pkgbits.Index + + // These slices correspond to entries in the runtime dictionary. + typeParamMethodExprs []writerMethodExprInfo + subdicts []objInfo + rtypes []typeInfo + itabs []itabInfo +} + +type itabInfo struct { + typ typeInfo + iface typeInfo +} + +// typeParamIndex returns the index of the given type parameter within +// the dictionary. This may differ from typ.Index() when there are +// implicit type parameters due to defined types declared within a +// generic function or method. +func (dict *writerDict) typeParamIndex(typ *types2.TypeParam) int { + for idx, implicit := range dict.implicits { + if implicit.Type().(*types2.TypeParam) == typ { + return idx + } + } + + return len(dict.implicits) + typ.Index() +} + +// A derivedInfo represents a reference to an encoded generic Go type. +type derivedInfo struct { + idx pkgbits.Index + needed bool // TODO(mdempsky): Remove. +} + +// A typeInfo represents a reference to an encoded Go type. +// +// If derived is true, then the typeInfo represents a generic Go type +// that contains type parameters. In this case, idx is an index into +// the readerDict.derived{,Types} arrays. +// +// Otherwise, the typeInfo represents a non-generic Go type, and idx +// is an index into the reader.typs array instead. +type typeInfo struct { + idx pkgbits.Index + derived bool +} + +// An objInfo represents a reference to an encoded, instantiated (if +// applicable) Go object. +type objInfo struct { + idx pkgbits.Index // index for the generic function declaration + explicits []typeInfo // info for the type arguments +} + +// A selectorInfo represents a reference to an encoded field or method +// name (i.e., objects that can only be accessed using selector +// expressions). +type selectorInfo struct { + pkgIdx pkgbits.Index + nameIdx pkgbits.Index +} + +// anyDerived reports whether any of info's explicit type arguments +// are derived types. +func (info objInfo) anyDerived() bool { + for _, explicit := range info.explicits { + if explicit.derived { + return true + } + } + return false +} + +// equals reports whether info and other represent the same Go object +// (i.e., same base object and identical type arguments, if any). +func (info objInfo) equals(other objInfo) bool { + if info.idx != other.idx { + return false + } + assert(len(info.explicits) == len(other.explicits)) + for i, targ := range info.explicits { + if targ != other.explicits[i] { + return false + } + } + return true +} + +type writerMethodExprInfo struct { + typeParamIdx int + methodInfo selectorInfo +} + +// typeParamMethodExprIdx returns the index where the given encoded +// method expression function pointer appears within this dictionary's +// type parameters method expressions section, adding it if necessary. +func (dict *writerDict) typeParamMethodExprIdx(typeParamIdx int, methodInfo selectorInfo) int { + newInfo := writerMethodExprInfo{typeParamIdx, methodInfo} + + for idx, oldInfo := range dict.typeParamMethodExprs { + if oldInfo == newInfo { + return idx + } + } + + idx := len(dict.typeParamMethodExprs) + dict.typeParamMethodExprs = append(dict.typeParamMethodExprs, newInfo) + return idx +} + +// subdictIdx returns the index where the given encoded object's +// runtime dictionary appears within this dictionary's subdictionary +// section, adding it if necessary. +func (dict *writerDict) subdictIdx(newInfo objInfo) int { + for idx, oldInfo := range dict.subdicts { + if oldInfo.equals(newInfo) { + return idx + } + } + + idx := len(dict.subdicts) + dict.subdicts = append(dict.subdicts, newInfo) + return idx +} + +// rtypeIdx returns the index where the given encoded type's +// *runtime._type value appears within this dictionary's rtypes +// section, adding it if necessary. +func (dict *writerDict) rtypeIdx(newInfo typeInfo) int { + for idx, oldInfo := range dict.rtypes { + if oldInfo == newInfo { + return idx + } + } + + idx := len(dict.rtypes) + dict.rtypes = append(dict.rtypes, newInfo) + return idx +} + +// itabIdx returns the index where the given encoded type pair's +// *runtime.itab value appears within this dictionary's itabs section, +// adding it if necessary. +func (dict *writerDict) itabIdx(typInfo, ifaceInfo typeInfo) int { + newInfo := itabInfo{typInfo, ifaceInfo} + + for idx, oldInfo := range dict.itabs { + if oldInfo == newInfo { + return idx + } + } + + idx := len(dict.itabs) + dict.itabs = append(dict.itabs, newInfo) + return idx +} + +func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer { + return &writer{ + Encoder: pw.NewEncoder(k, marker), + p: pw, + } +} + +// @@@ Positions + +// pos writes the position of p into the element bitstream. +func (w *writer) pos(p poser) { + w.Sync(pkgbits.SyncPos) + pos := p.Pos() + + // TODO(mdempsky): Track down the remaining cases here and fix them. + if !w.Bool(pos.IsKnown()) { + return + } + + // TODO(mdempsky): Delta encoding. + w.posBase(pos.Base()) + w.Uint(pos.Line()) + w.Uint(pos.Col()) +} + +// posBase writes a reference to the given PosBase into the element +// bitstream. +func (w *writer) posBase(b *syntax.PosBase) { + w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b)) +} + +// posBaseIdx returns the index for the given PosBase. +func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) pkgbits.Index { + if idx, ok := pw.posBasesIdx[b]; ok { + return idx + } + + w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase) + w.p.posBasesIdx[b] = w.Idx + + w.String(trimFilename(b)) + + if !w.Bool(b.IsFileBase()) { + w.pos(b) + w.Uint(b.Line()) + w.Uint(b.Col()) + } + + return w.Flush() +} + +// @@@ Packages + +// pkg writes a use of the given Package into the element bitstream. +func (w *writer) pkg(pkg *types2.Package) { + w.pkgRef(w.p.pkgIdx(pkg)) +} + +func (w *writer) pkgRef(idx pkgbits.Index) { + w.Sync(pkgbits.SyncPkg) + w.Reloc(pkgbits.RelocPkg, idx) +} + +// pkgIdx returns the index for the given package, adding it to the +// package export data if needed. +func (pw *pkgWriter) pkgIdx(pkg *types2.Package) pkgbits.Index { + if idx, ok := pw.pkgsIdx[pkg]; ok { + return idx + } + + w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef) + pw.pkgsIdx[pkg] = w.Idx + + // The universe and package unsafe need to be handled specially by + // importers anyway, so we serialize them using just their package + // path. This ensures that readers don't confuse them for + // user-defined packages. + switch pkg { + case nil: // universe + w.String("builtin") // same package path used by godoc + case types2.Unsafe: + w.String("unsafe") + default: + // TODO(mdempsky): Write out pkg.Path() for curpkg too. + var path string + if pkg != w.p.curpkg { + path = pkg.Path() + } + base.Assertf(path != "builtin" && path != "unsafe", "unexpected path for user-defined package: %q", path) + w.String(path) + w.String(pkg.Name()) + + w.Len(len(pkg.Imports())) + for _, imp := range pkg.Imports() { + w.pkg(imp) + } + } + + return w.Flush() +} + +// @@@ Types + +var ( + anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName) + runeTypeName = types2.Universe.Lookup("rune").(*types2.TypeName) +) + +// typ writes a use of the given type into the bitstream. +func (w *writer) typ(typ types2.Type) { + w.typInfo(w.p.typIdx(typ, w.dict)) +} + +// typInfo writes a use of the given type (specified as a typeInfo +// instead) into the bitstream. +func (w *writer) typInfo(info typeInfo) { + w.Sync(pkgbits.SyncType) + if w.Bool(info.derived) { + w.Len(int(info.idx)) + w.derived = true + } else { + w.Reloc(pkgbits.RelocType, info.idx) + } +} + +// typIdx returns the index where the export data description of type +// can be read back in. If no such index exists yet, it's created. +// +// typIdx also reports whether typ is a derived type; that is, whether +// its identity depends on type parameters. +func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo { + if idx, ok := pw.typsIdx[typ]; ok { + return typeInfo{idx: idx, derived: false} + } + if dict != nil { + if idx, ok := dict.derivedIdx[typ]; ok { + return typeInfo{idx: idx, derived: true} + } + } + + w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx) + w.dict = dict + + switch typ := typ.(type) { + default: + base.Fatalf("unexpected type: %v (%T)", typ, typ) + + case *types2.Basic: + switch kind := typ.Kind(); { + case kind == types2.Invalid: + base.Fatalf("unexpected types2.Invalid") + + case types2.Typ[kind] == typ: + w.Code(pkgbits.TypeBasic) + w.Len(int(kind)) + + default: + // Handle "byte" and "rune" as references to their TypeName. + obj := types2.Universe.Lookup(typ.Name()) + assert(obj.Type() == typ) + + w.Code(pkgbits.TypeNamed) + w.obj(obj, nil) + } + + case *types2.Named: + obj, targs := splitNamed(typ) + + // Defined types that are declared within a generic function (and + // thus have implicit type parameters) are always derived types. + if w.p.hasImplicitTypeParams(obj) { + w.derived = true + } + + w.Code(pkgbits.TypeNamed) + w.obj(obj, targs) + + case *types2.TypeParam: + w.derived = true + w.Code(pkgbits.TypeTypeParam) + w.Len(w.dict.typeParamIndex(typ)) + + case *types2.Array: + w.Code(pkgbits.TypeArray) + w.Uint64(uint64(typ.Len())) + w.typ(typ.Elem()) + + case *types2.Chan: + w.Code(pkgbits.TypeChan) + w.Len(int(typ.Dir())) + w.typ(typ.Elem()) + + case *types2.Map: + w.Code(pkgbits.TypeMap) + w.typ(typ.Key()) + w.typ(typ.Elem()) + + case *types2.Pointer: + w.Code(pkgbits.TypePointer) + w.typ(typ.Elem()) + + case *types2.Signature: + base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ) + w.Code(pkgbits.TypeSignature) + w.signature(typ) + + case *types2.Slice: + w.Code(pkgbits.TypeSlice) + w.typ(typ.Elem()) + + case *types2.Struct: + w.Code(pkgbits.TypeStruct) + w.structType(typ) + + case *types2.Interface: + if typ == anyTypeName.Type() { + w.Code(pkgbits.TypeNamed) + w.obj(anyTypeName, nil) + break + } + + w.Code(pkgbits.TypeInterface) + w.interfaceType(typ) + + case *types2.Union: + w.Code(pkgbits.TypeUnion) + w.unionType(typ) + } + + if w.derived { + idx := pkgbits.Index(len(dict.derived)) + dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()}) + dict.derivedIdx[typ] = idx + return typeInfo{idx: idx, derived: true} + } + + pw.typsIdx[typ] = w.Idx + return typeInfo{idx: w.Flush(), derived: false} +} + +func (w *writer) structType(typ *types2.Struct) { + w.Len(typ.NumFields()) + for i := 0; i < typ.NumFields(); i++ { + f := typ.Field(i) + w.pos(f) + w.selector(f) + w.typ(f.Type()) + w.String(typ.Tag(i)) + w.Bool(f.Embedded()) + } +} + +func (w *writer) unionType(typ *types2.Union) { + w.Len(typ.Len()) + for i := 0; i < typ.Len(); i++ { + t := typ.Term(i) + w.Bool(t.Tilde()) + w.typ(t.Type()) + } +} + +func (w *writer) interfaceType(typ *types2.Interface) { + w.Len(typ.NumExplicitMethods()) + w.Len(typ.NumEmbeddeds()) + + if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 { + w.Bool(typ.IsImplicit()) + } else { + // Implicit interfaces always have 0 explicit methods and 1 + // embedded type, so we skip writing out the implicit flag + // otherwise as a space optimization. + assert(!typ.IsImplicit()) + } + + for i := 0; i < typ.NumExplicitMethods(); i++ { + m := typ.ExplicitMethod(i) + sig := m.Type().(*types2.Signature) + assert(sig.TypeParams() == nil) + + w.pos(m) + w.selector(m) + w.signature(sig) + } + + for i := 0; i < typ.NumEmbeddeds(); i++ { + w.typ(typ.EmbeddedType(i)) + } +} + +func (w *writer) signature(sig *types2.Signature) { + w.Sync(pkgbits.SyncSignature) + w.params(sig.Params()) + w.params(sig.Results()) + w.Bool(sig.Variadic()) +} + +func (w *writer) params(typ *types2.Tuple) { + w.Sync(pkgbits.SyncParams) + w.Len(typ.Len()) + for i := 0; i < typ.Len(); i++ { + w.param(typ.At(i)) + } +} + +func (w *writer) param(param *types2.Var) { + w.Sync(pkgbits.SyncParam) + w.pos(param) + w.localIdent(param) + w.typ(param.Type()) +} + +// @@@ Objects + +// obj writes a use of the given object into the bitstream. +// +// If obj is a generic object, then explicits are the explicit type +// arguments used to instantiate it (i.e., used to substitute the +// object's own declared type parameters). +func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) { + w.objInfo(w.p.objInstIdx(obj, explicits, w.dict)) +} + +// objInfo writes a use of the given encoded object into the +// bitstream. +func (w *writer) objInfo(info objInfo) { + w.Sync(pkgbits.SyncObject) + w.Bool(false) // TODO(mdempsky): Remove; was derived func inst. + w.Reloc(pkgbits.RelocObj, info.idx) + + w.Len(len(info.explicits)) + for _, info := range info.explicits { + w.typInfo(info) + } +} + +// objInstIdx returns the indices for an object and a corresponding +// list of type arguments used to instantiate it, adding them to the +// export data as needed. +func (pw *pkgWriter) objInstIdx(obj types2.Object, explicits *types2.TypeList, dict *writerDict) objInfo { + explicitInfos := make([]typeInfo, explicits.Len()) + for i := range explicitInfos { + explicitInfos[i] = pw.typIdx(explicits.At(i), dict) + } + return objInfo{idx: pw.objIdx(obj), explicits: explicitInfos} +} + +// objIdx returns the index for the given Object, adding it to the +// export data as needed. +func (pw *pkgWriter) objIdx(obj types2.Object) pkgbits.Index { + // TODO(mdempsky): Validate that obj is a global object (or a local + // defined type, which we hoist to global scope anyway). + + if idx, ok := pw.objsIdx[obj]; ok { + return idx + } + + dict := &writerDict{ + derivedIdx: make(map[types2.Type]pkgbits.Index), + } + + if isDefinedType(obj) && obj.Pkg() == pw.curpkg { + decl, ok := pw.typDecls[obj.(*types2.TypeName)] + assert(ok) + dict.implicits = decl.implicits + } + + // We encode objects into 4 elements across different sections, all + // sharing the same index: + // + // - RelocName has just the object's qualified name (i.e., + // Object.Pkg and Object.Name) and the CodeObj indicating what + // specific type of Object it is (Var, Func, etc). + // + // - RelocObj has the remaining public details about the object, + // relevant to go/types importers. + // + // - RelocObjExt has additional private details about the object, + // which are only relevant to cmd/compile itself. This is + // separated from RelocObj so that go/types importers are + // unaffected by internal compiler changes. + // + // - RelocObjDict has public details about the object's type + // parameters and derived type's used by the object. This is + // separated to facilitate the eventual introduction of + // shape-based stenciling. + // + // TODO(mdempsky): Re-evaluate whether RelocName still makes sense + // to keep separate from RelocObj. + + w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1) + wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1) + wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1) + wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1) + + pw.objsIdx[obj] = w.Idx // break cycles + assert(wext.Idx == w.Idx) + assert(wname.Idx == w.Idx) + assert(wdict.Idx == w.Idx) + + w.dict = dict + wext.dict = dict + + code := w.doObj(wext, obj) + w.Flush() + wext.Flush() + + wname.qualifiedIdent(obj) + wname.Code(code) + wname.Flush() + + wdict.objDict(obj, w.dict) + wdict.Flush() + + return w.Idx +} + +// doObj writes the RelocObj definition for obj to w, and the +// RelocObjExt definition to wext. +func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj { + if obj.Pkg() != w.p.curpkg { + return pkgbits.ObjStub + } + + switch obj := obj.(type) { + default: + w.p.unexpected("object", obj) + panic("unreachable") + + case *types2.Const: + w.pos(obj) + w.typ(obj.Type()) + w.Value(obj.Val()) + return pkgbits.ObjConst + + case *types2.Func: + decl, ok := w.p.funDecls[obj] + assert(ok) + sig := obj.Type().(*types2.Signature) + + w.pos(obj) + w.typeParamNames(sig.TypeParams()) + w.signature(sig) + w.pos(decl) + wext.funcExt(obj) + return pkgbits.ObjFunc + + case *types2.TypeName: + decl, ok := w.p.typDecls[obj] + assert(ok) + + if obj.IsAlias() { + w.pos(obj) + w.typ(obj.Type()) + return pkgbits.ObjAlias + } + + named := obj.Type().(*types2.Named) + assert(named.TypeArgs() == nil) + + w.pos(obj) + w.typeParamNames(named.TypeParams()) + wext.typeExt(obj) + w.typExpr(decl.Type) + + w.Len(named.NumMethods()) + for i := 0; i < named.NumMethods(); i++ { + w.method(wext, named.Method(i)) + } + + return pkgbits.ObjType + + case *types2.Var: + w.pos(obj) + w.typ(obj.Type()) + wext.varExt(obj) + return pkgbits.ObjVar + } +} + +// typExpr writes the type represented by the given expression. +// +// TODO(mdempsky): Document how this differs from exprType. +func (w *writer) typExpr(expr syntax.Expr) { + tv, ok := w.p.info.Types[expr] + assert(ok) + assert(tv.IsType()) + w.typ(tv.Type) +} + +// objDict writes the dictionary needed for reading the given object. +func (w *writer) objDict(obj types2.Object, dict *writerDict) { + // TODO(mdempsky): Split objDict into multiple entries? reader.go + // doesn't care about the type parameter bounds, and reader2.go + // doesn't care about referenced functions. + + w.dict = dict // TODO(mdempsky): This is a bit sketchy. + + w.Len(len(dict.implicits)) + + tparams := objTypeParams(obj) + ntparams := tparams.Len() + w.Len(ntparams) + for i := 0; i < ntparams; i++ { + w.typ(tparams.At(i).Constraint()) + } + + nderived := len(dict.derived) + w.Len(nderived) + for _, typ := range dict.derived { + w.Reloc(pkgbits.RelocType, typ.idx) + w.Bool(typ.needed) + } + + // Write runtime dictionary information. + // + // N.B., the go/types importer reads up to the section, but doesn't + // read any further, so it's safe to change. (See TODO above.) + + // For each type parameter, write out whether the constraint is a + // basic interface. This is used to determine how aggressively we + // can shape corresponding type arguments. + // + // This is somewhat redundant with writing out the full type + // parameter constraints above, but the compiler currently skips + // over those. Also, we don't care about the *declared* constraints, + // but how the type parameters are actually *used*. E.g., if a type + // parameter is constrained to `int | uint` but then never used in + // arithmetic/conversions/etc, we could shape those together. + for _, implicit := range dict.implicits { + tparam := implicit.Type().(*types2.TypeParam) + w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet()) + } + for i := 0; i < ntparams; i++ { + tparam := tparams.At(i) + w.Bool(tparam.Underlying().(*types2.Interface).IsMethodSet()) + } + + w.Len(len(dict.typeParamMethodExprs)) + for _, info := range dict.typeParamMethodExprs { + w.Len(info.typeParamIdx) + w.selectorInfo(info.methodInfo) + } + + w.Len(len(dict.subdicts)) + for _, info := range dict.subdicts { + w.objInfo(info) + } + + w.Len(len(dict.rtypes)) + for _, info := range dict.rtypes { + w.typInfo(info) + } + + w.Len(len(dict.itabs)) + for _, info := range dict.itabs { + w.typInfo(info.typ) + w.typInfo(info.iface) + } + + assert(len(dict.derived) == nderived) +} + +func (w *writer) typeParamNames(tparams *types2.TypeParamList) { + w.Sync(pkgbits.SyncTypeParamNames) + + ntparams := tparams.Len() + for i := 0; i < ntparams; i++ { + tparam := tparams.At(i).Obj() + w.pos(tparam) + w.localIdent(tparam) + } +} + +func (w *writer) method(wext *writer, meth *types2.Func) { + decl, ok := w.p.funDecls[meth] + assert(ok) + sig := meth.Type().(*types2.Signature) + + w.Sync(pkgbits.SyncMethod) + w.pos(meth) + w.selector(meth) + w.typeParamNames(sig.RecvTypeParams()) + w.param(sig.Recv()) + w.signature(sig) + + w.pos(decl) // XXX: Hack to workaround linker limitations. + wext.funcExt(meth) +} + +// qualifiedIdent writes out the name of an object declared at package +// scope. (For now, it's also used to refer to local defined types.) +func (w *writer) qualifiedIdent(obj types2.Object) { + w.Sync(pkgbits.SyncSym) + + name := obj.Name() + if isDefinedType(obj) && obj.Pkg() == w.p.curpkg { + decl, ok := w.p.typDecls[obj.(*types2.TypeName)] + assert(ok) + if decl.gen != 0 { + // For local defined types, we embed a scope-disambiguation + // number directly into their name. types.SplitVargenSuffix then + // knows to look for this. + // + // TODO(mdempsky): Find a better solution; this is terrible. + name = fmt.Sprintf("%s·%v", name, decl.gen) + } + } + + w.pkg(obj.Pkg()) + w.String(name) +} + +// TODO(mdempsky): We should be able to omit pkg from both localIdent +// and selector, because they should always be known from context. +// However, past frustrations with this optimization in iexport make +// me a little nervous to try it again. + +// localIdent writes the name of a locally declared object (i.e., +// objects that can only be accessed by non-qualified name, within the +// context of a particular function). +func (w *writer) localIdent(obj types2.Object) { + assert(!isGlobal(obj)) + w.Sync(pkgbits.SyncLocalIdent) + w.pkg(obj.Pkg()) + w.String(obj.Name()) +} + +// selector writes the name of a field or method (i.e., objects that +// can only be accessed using selector expressions). +func (w *writer) selector(obj types2.Object) { + w.selectorInfo(w.p.selectorIdx(obj)) +} + +func (w *writer) selectorInfo(info selectorInfo) { + w.Sync(pkgbits.SyncSelector) + w.pkgRef(info.pkgIdx) + w.StringRef(info.nameIdx) +} + +func (pw *pkgWriter) selectorIdx(obj types2.Object) selectorInfo { + pkgIdx := pw.pkgIdx(obj.Pkg()) + nameIdx := pw.StringIdx(obj.Name()) + return selectorInfo{pkgIdx: pkgIdx, nameIdx: nameIdx} +} + +// @@@ Compiler extensions + +func (w *writer) funcExt(obj *types2.Func) { + decl, ok := w.p.funDecls[obj] + assert(ok) + + // TODO(mdempsky): Extend these pragma validation flags to account + // for generics. E.g., linkname probably doesn't make sense at + // least. + + pragma := asPragmaFlag(decl.Pragma) + if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 { + w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined") + } + + if decl.Body != nil { + if pragma&ir.Noescape != 0 { + w.p.errorf(decl, "can only use //go:noescape with external func implementations") + } + if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 { + // Stack growth can't handle uintptr arguments that may + // be pointers (as we don't know which are pointers + // when creating the stack map). Thus uintptrkeepalive + // functions (and all transitive callees) must be + // nosplit. + // + // N.B. uintptrescapes implies uintptrkeepalive but it + // is OK since the arguments must escape to the heap. + // + // TODO(prattmic): Add recursive nosplit check of callees. + // TODO(prattmic): Functions with no body (i.e., + // assembly) must also be nosplit, but we can't check + // that here. + w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit") + } + } else { + if base.Flag.Complete || decl.Name.Value == "init" { + // Linknamed functions are allowed to have no body. Hopefully + // the linkname target has a body. See issue 23311. + if _, ok := w.p.linknames[obj]; !ok { + w.p.errorf(decl, "missing function body") + } + } + } + + sig, block := obj.Type().(*types2.Signature), decl.Body + body, closureVars := w.p.bodyIdx(sig, block, w.dict) + assert(len(closureVars) == 0) + + w.Sync(pkgbits.SyncFuncExt) + w.pragmaFlag(pragma) + w.linkname(obj) + w.Bool(false) // stub extension + w.Reloc(pkgbits.RelocBody, body) + w.Sync(pkgbits.SyncEOF) +} + +func (w *writer) typeExt(obj *types2.TypeName) { + decl, ok := w.p.typDecls[obj] + assert(ok) + + w.Sync(pkgbits.SyncTypeExt) + + w.pragmaFlag(asPragmaFlag(decl.Pragma)) + + // No LSym.SymIdx info yet. + w.Int64(-1) + w.Int64(-1) +} + +func (w *writer) varExt(obj *types2.Var) { + w.Sync(pkgbits.SyncVarExt) + w.linkname(obj) +} + +func (w *writer) linkname(obj types2.Object) { + w.Sync(pkgbits.SyncLinkname) + w.Int64(-1) + w.String(w.p.linknames[obj]) +} + +func (w *writer) pragmaFlag(p ir.PragmaFlag) { + w.Sync(pkgbits.SyncPragma) + w.Int(int(p)) +} + +// @@@ Function bodies + +// bodyIdx returns the index for the given function body (specified by +// block), adding it to the export data +func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx pkgbits.Index, closureVars []posVar) { + w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody) + w.sig = sig + w.dict = dict + + w.funcargs(sig) + if w.Bool(block != nil) { + w.stmts(block.List) + w.pos(block.Rbrace) + } + + return w.Flush(), w.closureVars +} + +func (w *writer) funcargs(sig *types2.Signature) { + do := func(params *types2.Tuple, result bool) { + for i := 0; i < params.Len(); i++ { + w.funcarg(params.At(i), result) + } + } + + if recv := sig.Recv(); recv != nil { + w.funcarg(recv, false) + } + do(sig.Params(), false) + do(sig.Results(), true) +} + +func (w *writer) funcarg(param *types2.Var, result bool) { + if param.Name() != "" || result { + w.addLocal(param) + } +} + +// addLocal records the declaration of a new local variable. +func (w *writer) addLocal(obj *types2.Var) { + idx := len(w.localsIdx) + + w.Sync(pkgbits.SyncAddLocal) + if w.p.SyncMarkers() { + w.Int(idx) + } + w.varDictIndex(obj) + + if w.localsIdx == nil { + w.localsIdx = make(map[*types2.Var]int) + } + w.localsIdx[obj] = idx +} + +// useLocal writes a reference to the given local or free variable +// into the bitstream. +func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) { + w.Sync(pkgbits.SyncUseObjLocal) + + if idx, ok := w.localsIdx[obj]; w.Bool(ok) { + w.Len(idx) + return + } + + idx, ok := w.closureVarsIdx[obj] + if !ok { + if w.closureVarsIdx == nil { + w.closureVarsIdx = make(map[*types2.Var]int) + } + idx = len(w.closureVars) + w.closureVars = append(w.closureVars, posVar{pos, obj}) + w.closureVarsIdx[obj] = idx + } + w.Len(idx) +} + +func (w *writer) openScope(pos syntax.Pos) { + w.Sync(pkgbits.SyncOpenScope) + w.pos(pos) +} + +func (w *writer) closeScope(pos syntax.Pos) { + w.Sync(pkgbits.SyncCloseScope) + w.pos(pos) + w.closeAnotherScope() +} + +func (w *writer) closeAnotherScope() { + w.Sync(pkgbits.SyncCloseAnotherScope) +} + +// @@@ Statements + +// stmt writes the given statement into the function body bitstream. +func (w *writer) stmt(stmt syntax.Stmt) { + var stmts []syntax.Stmt + if stmt != nil { + stmts = []syntax.Stmt{stmt} + } + w.stmts(stmts) +} + +func (w *writer) stmts(stmts []syntax.Stmt) { + w.Sync(pkgbits.SyncStmts) + for _, stmt := range stmts { + w.stmt1(stmt) + } + w.Code(stmtEnd) + w.Sync(pkgbits.SyncStmtsEnd) +} + +func (w *writer) stmt1(stmt syntax.Stmt) { + switch stmt := stmt.(type) { + default: + w.p.unexpected("statement", stmt) + + case nil, *syntax.EmptyStmt: + return + + case *syntax.AssignStmt: + switch { + case stmt.Rhs == nil: + w.Code(stmtIncDec) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + + case stmt.Op != 0 && stmt.Op != syntax.Def: + w.Code(stmtAssignOp) + w.op(binOps[stmt.Op]) + w.expr(stmt.Lhs) + w.pos(stmt) + + var typ types2.Type + if stmt.Op != syntax.Shl && stmt.Op != syntax.Shr { + typ = w.p.typeOf(stmt.Lhs) + } + w.implicitConvExpr(typ, stmt.Rhs) + + default: + w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) + } + + case *syntax.BlockStmt: + w.Code(stmtBlock) + w.blockStmt(stmt) + + case *syntax.BranchStmt: + w.Code(stmtBranch) + w.pos(stmt) + w.op(branchOps[stmt.Tok]) + w.optLabel(stmt.Label) + + case *syntax.CallStmt: + w.Code(stmtCall) + w.pos(stmt) + w.op(callOps[stmt.Tok]) + w.expr(stmt.Call) + + case *syntax.DeclStmt: + for _, decl := range stmt.DeclList { + w.declStmt(decl) + } + + case *syntax.ExprStmt: + w.Code(stmtExpr) + w.expr(stmt.X) + + case *syntax.ForStmt: + w.Code(stmtFor) + w.forStmt(stmt) + + case *syntax.IfStmt: + w.Code(stmtIf) + w.ifStmt(stmt) + + case *syntax.LabeledStmt: + w.Code(stmtLabel) + w.pos(stmt) + w.label(stmt.Label) + w.stmt1(stmt.Stmt) + + case *syntax.ReturnStmt: + w.Code(stmtReturn) + w.pos(stmt) + + resultTypes := w.sig.Results() + dstType := func(i int) types2.Type { + return resultTypes.At(i).Type() + } + w.multiExpr(stmt, dstType, unpackListExpr(stmt.Results)) + + case *syntax.SelectStmt: + w.Code(stmtSelect) + w.selectStmt(stmt) + + case *syntax.SendStmt: + chanType := types2.CoreType(w.p.typeOf(stmt.Chan)).(*types2.Chan) + + w.Code(stmtSend) + w.pos(stmt) + w.expr(stmt.Chan) + w.implicitConvExpr(chanType.Elem(), stmt.Value) + + case *syntax.SwitchStmt: + w.Code(stmtSwitch) + w.switchStmt(stmt) + } +} + +func (w *writer) assignList(expr syntax.Expr) { + exprs := unpackListExpr(expr) + w.Len(len(exprs)) + + for _, expr := range exprs { + w.assign(expr) + } +} + +func (w *writer) assign(expr syntax.Expr) { + expr = unparen(expr) + + if name, ok := expr.(*syntax.Name); ok { + if name.Value == "_" { + w.Code(assignBlank) + return + } + + if obj, ok := w.p.info.Defs[name]; ok { + obj := obj.(*types2.Var) + + w.Code(assignDef) + w.pos(obj) + w.localIdent(obj) + w.typ(obj.Type()) + + // TODO(mdempsky): Minimize locals index size by deferring + // this until the variables actually come into scope. + w.addLocal(obj) + return + } + } + + w.Code(assignExpr) + w.expr(expr) +} + +func (w *writer) declStmt(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ConstDecl, *syntax.TypeDecl: + + case *syntax.VarDecl: + w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values) + } +} + +// assignStmt writes out an assignment for "lhs = rhs". +func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { + lhs := unpackListExpr(lhs0) + rhs := unpackListExpr(rhs0) + + w.Code(stmtAssign) + w.pos(pos) + + // As if w.assignList(lhs0). + w.Len(len(lhs)) + for _, expr := range lhs { + w.assign(expr) + } + + dstType := func(i int) types2.Type { + dst := lhs[i] + + // Finding dstType is somewhat involved, because for VarDecl + // statements, the Names are only added to the info.{Defs,Uses} + // maps, not to info.Types. + if name, ok := unparen(dst).(*syntax.Name); ok { + if name.Value == "_" { + return nil // ok: no implicit conversion + } else if def, ok := w.p.info.Defs[name].(*types2.Var); ok { + return def.Type() + } else if use, ok := w.p.info.Uses[name].(*types2.Var); ok { + return use.Type() + } else { + w.p.fatalf(dst, "cannot find type of destination object: %v", dst) + } + } + + return w.p.typeOf(dst) + } + + w.multiExpr(pos, dstType, rhs) +} + +func (w *writer) blockStmt(stmt *syntax.BlockStmt) { + w.Sync(pkgbits.SyncBlockStmt) + w.openScope(stmt.Pos()) + w.stmts(stmt.List) + w.closeScope(stmt.Rbrace) +} + +func (w *writer) forStmt(stmt *syntax.ForStmt) { + w.Sync(pkgbits.SyncForStmt) + w.openScope(stmt.Pos()) + + if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) { + w.pos(rang) + w.assignList(rang.Lhs) + w.expr(rang.X) + + xtyp := w.p.typeOf(rang.X) + if _, isMap := types2.CoreType(xtyp).(*types2.Map); isMap { + w.rtype(xtyp) + } + { + lhs := unpackListExpr(rang.Lhs) + assign := func(i int, src types2.Type) { + if i >= len(lhs) { + return + } + dst := unparen(lhs[i]) + if name, ok := dst.(*syntax.Name); ok && name.Value == "_" { + return + } + + var dstType types2.Type + if rang.Def { + // For `:=` assignments, the LHS names only appear in Defs, + // not Types (as used by typeOf). + dstType = w.p.info.Defs[dst.(*syntax.Name)].(*types2.Var).Type() + } else { + dstType = w.p.typeOf(dst) + } + + w.convRTTI(src, dstType) + } + + keyType, valueType := w.p.rangeTypes(rang.X) + assign(0, keyType) + assign(1, valueType) + } + + } else { + w.pos(stmt) + w.stmt(stmt.Init) + w.optExpr(stmt.Cond) + w.stmt(stmt.Post) + } + + w.blockStmt(stmt.Body) + w.closeAnotherScope() +} + +// rangeTypes returns the types of values produced by ranging over +// expr. +func (pw *pkgWriter) rangeTypes(expr syntax.Expr) (key, value types2.Type) { + typ := pw.typeOf(expr) + switch typ := types2.CoreType(typ).(type) { + case *types2.Pointer: // must be pointer to array + return types2.Typ[types2.Int], types2.CoreType(typ.Elem()).(*types2.Array).Elem() + case *types2.Array: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Slice: + return types2.Typ[types2.Int], typ.Elem() + case *types2.Basic: + if typ.Info()&types2.IsString != 0 { + return types2.Typ[types2.Int], runeTypeName.Type() + } + case *types2.Map: + return typ.Key(), typ.Elem() + case *types2.Chan: + return typ.Elem(), nil + } + pw.fatalf(expr, "unexpected range type: %v", typ) + panic("unreachable") +} + +func (w *writer) ifStmt(stmt *syntax.IfStmt) { + w.Sync(pkgbits.SyncIfStmt) + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + w.expr(stmt.Cond) + w.blockStmt(stmt.Then) + w.stmt(stmt.Else) + w.closeAnotherScope() +} + +func (w *writer) selectStmt(stmt *syntax.SelectStmt) { + w.Sync(pkgbits.SyncSelectStmt) + + w.pos(stmt) + w.Len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + w.stmt(clause.Comm) + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } +} + +func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { + w.Sync(pkgbits.SyncSwitchStmt) + + w.openScope(stmt.Pos()) + w.pos(stmt) + w.stmt(stmt.Init) + + var iface, tagType types2.Type + if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { + iface = w.p.typeOf(guard.X) + + w.pos(guard) + if tag := guard.Lhs; w.Bool(tag != nil) { + w.pos(tag) + + // Like w.localIdent, but we don't have a types2.Object. + w.Sync(pkgbits.SyncLocalIdent) + w.pkg(w.p.curpkg) + w.String(tag.Value) + } + w.expr(guard.X) + } else { + tag := stmt.Tag + + if tag != nil { + tagType = w.p.typeOf(tag) + } else { + tagType = types2.Typ[types2.Bool] + } + + // Walk is going to emit comparisons between the tag value and + // each case expression, and we want these comparisons to always + // have the same type. If there are any case values that can't be + // converted to the tag value's type, then convert everything to + // `any` instead. + Outer: + for _, clause := range stmt.Body { + for _, cas := range unpackListExpr(clause.Cases) { + if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { + tagType = types2.NewInterfaceType(nil, nil) + break Outer + } + } + } + + if w.Bool(tag != nil) { + w.implicitConvExpr(tagType, tag) + } + } + + w.Len(len(stmt.Body)) + for i, clause := range stmt.Body { + if i > 0 { + w.closeScope(clause.Pos()) + } + w.openScope(clause.Pos()) + + w.pos(clause) + + cases := unpackListExpr(clause.Cases) + if iface != nil { + w.Len(len(cases)) + for _, cas := range cases { + if w.Bool(isNil(w.p.info, cas)) { + continue + } + w.exprType(iface, cas) + } + } else { + // As if w.exprList(clause.Cases), + // but with implicit conversions to tagType. + + w.Sync(pkgbits.SyncExprList) + w.Sync(pkgbits.SyncExprs) + w.Len(len(cases)) + for _, cas := range cases { + w.implicitConvExpr(tagType, cas) + } + } + + if obj, ok := w.p.info.Implicits[clause]; ok { + // TODO(mdempsky): These pos details are quirkish, but also + // necessary so the variable's position is correct for DWARF + // scope assignment later. It would probably be better for us to + // instead just set the variable's DWARF scoping info earlier so + // we can give it the correct position information. + pos := clause.Pos() + if typs := unpackListExpr(clause.Cases); len(typs) != 0 { + pos = typeExprEndPos(typs[len(typs)-1]) + } + w.pos(pos) + + obj := obj.(*types2.Var) + w.typ(obj.Type()) + w.addLocal(obj) + } + + w.stmts(clause.Body) + } + if len(stmt.Body) > 0 { + w.closeScope(stmt.Rbrace) + } + + w.closeScope(stmt.Rbrace) +} + +func (w *writer) label(label *syntax.Name) { + w.Sync(pkgbits.SyncLabel) + + // TODO(mdempsky): Replace label strings with dense indices. + w.String(label.Value) +} + +func (w *writer) optLabel(label *syntax.Name) { + w.Sync(pkgbits.SyncOptLabel) + if w.Bool(label != nil) { + w.label(label) + } +} + +// @@@ Expressions + +// expr writes the given expression into the function body bitstream. +func (w *writer) expr(expr syntax.Expr) { + base.Assertf(expr != nil, "missing expression") + + expr = unparen(expr) // skip parens; unneeded after typecheck + + obj, inst := lookupObj(w.p.info, expr) + targs := inst.TypeArgs + + if tv, ok := w.p.info.Types[expr]; ok { + if tv.IsType() { + w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr)) + } + + if tv.Value != nil { + w.Code(exprConst) + w.pos(expr) + typ := idealType(tv) + assert(typ != nil) + w.typ(typ) + w.Value(tv.Value) + + // TODO(mdempsky): These details are only important for backend + // diagnostics. Explore writing them out separately. + w.op(constExprOp(expr)) + w.String(syntax.String(expr)) + return + } + + if _, isNil := obj.(*types2.Nil); isNil { + w.Code(exprNil) + w.pos(expr) + w.typ(tv.Type) + return + } + + // With shape types (and particular pointer shaping), we may have + // an expression of type "go.shape.*uint8", but need to reshape it + // to another shape-identical type to allow use in field + // selection, indexing, etc. + if typ := tv.Type; !tv.IsBuiltin() && !isTuple(typ) && !isUntyped(typ) { + w.Code(exprReshape) + w.typ(typ) + // fallthrough + } + } + + if obj != nil { + if targs.Len() != 0 { + obj := obj.(*types2.Func) + + w.Code(exprFuncInst) + w.pos(expr) + w.funcInst(obj, targs) + return + } + + if isGlobal(obj) { + w.Code(exprGlobal) + w.obj(obj, nil) + return + } + + obj := obj.(*types2.Var) + assert(!obj.IsField()) + + w.Code(exprLocal) + w.useLocal(expr.Pos(), obj) + return + } + + switch expr := expr.(type) { + default: + w.p.unexpected("expression", expr) + + case *syntax.CompositeLit: + w.Code(exprCompLit) + w.compLit(expr) + + case *syntax.FuncLit: + w.Code(exprFuncLit) + w.funcLit(expr) + + case *syntax.SelectorExpr: + sel, ok := w.p.info.Selections[expr] + assert(ok) + + switch sel.Kind() { + default: + w.p.fatalf(expr, "unexpected selection kind: %v", sel.Kind()) + + case types2.FieldVal: + w.Code(exprFieldVal) + w.expr(expr.X) + w.pos(expr) + w.selector(sel.Obj()) + + case types2.MethodVal: + w.Code(exprMethodVal) + typ := w.recvExpr(expr, sel) + w.pos(expr) + w.methodExpr(expr, typ, sel) + + case types2.MethodExpr: + w.Code(exprMethodExpr) + + tv, ok := w.p.info.Types[expr.X] + assert(ok) + assert(tv.IsType()) + + index := sel.Index() + implicits := index[:len(index)-1] + + typ := tv.Type + w.typ(typ) + + w.Len(len(implicits)) + for _, ix := range implicits { + w.Len(ix) + typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type() + } + + recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type() + if w.Bool(isPtrTo(typ, recv)) { // need deref + typ = recv + } else if w.Bool(isPtrTo(recv, typ)) { // need addr + typ = recv + } + + w.pos(expr) + w.methodExpr(expr, typ, sel) + } + + case *syntax.IndexExpr: + _ = w.p.typeOf(expr.Index) // ensure this is an index expression, not an instantiation + + xtyp := w.p.typeOf(expr.X) + + var keyType types2.Type + if mapType, ok := types2.CoreType(xtyp).(*types2.Map); ok { + keyType = mapType.Key() + } + + w.Code(exprIndex) + w.expr(expr.X) + w.pos(expr) + w.implicitConvExpr(keyType, expr.Index) + if keyType != nil { + w.rtype(xtyp) + } + + case *syntax.SliceExpr: + w.Code(exprSlice) + w.expr(expr.X) + w.pos(expr) + for _, n := range &expr.Index { + w.optExpr(n) + } + + case *syntax.AssertExpr: + iface := w.p.typeOf(expr.X) + + w.Code(exprAssert) + w.expr(expr.X) + w.pos(expr) + w.exprType(iface, expr.Type) + w.rtype(iface) + + case *syntax.Operation: + if expr.Y == nil { + w.Code(exprUnaryOp) + w.op(unOps[expr.Op]) + w.pos(expr) + w.expr(expr.X) + break + } + + var commonType types2.Type + switch expr.Op { + case syntax.Shl, syntax.Shr: + // ok: operands are allowed to have different types + default: + xtyp := w.p.typeOf(expr.X) + ytyp := w.p.typeOf(expr.Y) + switch { + case types2.AssignableTo(xtyp, ytyp): + commonType = ytyp + case types2.AssignableTo(ytyp, xtyp): + commonType = xtyp + default: + w.p.fatalf(expr, "failed to find common type between %v and %v", xtyp, ytyp) + } + } + + w.Code(exprBinaryOp) + w.op(binOps[expr.Op]) + w.implicitConvExpr(commonType, expr.X) + w.pos(expr) + w.implicitConvExpr(commonType, expr.Y) + + case *syntax.CallExpr: + tv, ok := w.p.info.Types[expr.Fun] + assert(ok) + if tv.IsType() { + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + w.convertExpr(tv.Type, expr.ArgList[0], false) + break + } + + var rtype types2.Type + if tv.IsBuiltin() { + switch obj, _ := lookupObj(w.p.info, expr.Fun); obj.Name() { + case "make": + assert(len(expr.ArgList) >= 1) + assert(!expr.HasDots) + + w.Code(exprMake) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + w.exprs(expr.ArgList[1:]) + + typ := w.p.typeOf(expr) + switch coreType := types2.CoreType(typ).(type) { + default: + w.p.fatalf(expr, "unexpected core type: %v", coreType) + case *types2.Chan: + w.rtype(typ) + case *types2.Map: + w.rtype(typ) + case *types2.Slice: + w.rtype(sliceElem(typ)) + } + + return + + case "new": + assert(len(expr.ArgList) == 1) + assert(!expr.HasDots) + + w.Code(exprNew) + w.pos(expr) + w.exprType(nil, expr.ArgList[0]) + return + + case "append": + rtype = sliceElem(w.p.typeOf(expr)) + case "copy": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "copy(g())" + typ = tuple.At(0).Type() + } + rtype = sliceElem(typ) + case "delete": + typ := w.p.typeOf(expr.ArgList[0]) + if tuple, ok := typ.(*types2.Tuple); ok { // "delete(g())" + typ = tuple.At(0).Type() + } + rtype = typ + case "Slice": + rtype = sliceElem(w.p.typeOf(expr)) + } + } + + writeFunExpr := func() { + fun := unparen(expr.Fun) + + if selector, ok := fun.(*syntax.SelectorExpr); ok { + if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal { + w.Bool(true) // method call + typ := w.recvExpr(selector, sel) + w.methodExpr(selector, typ, sel) + return + } + } + + w.Bool(false) // not a method call (i.e., normal function call) + + if obj, inst := lookupObj(w.p.info, fun); w.Bool(obj != nil && inst.TypeArgs.Len() != 0) { + obj := obj.(*types2.Func) + + w.pos(fun) + w.funcInst(obj, inst.TypeArgs) + return + } + + w.expr(fun) + } + + sigType := types2.CoreType(tv.Type).(*types2.Signature) + paramTypes := sigType.Params() + + w.Code(exprCall) + writeFunExpr() + w.pos(expr) + + paramType := func(i int) types2.Type { + if sigType.Variadic() && !expr.HasDots && i >= paramTypes.Len()-1 { + return paramTypes.At(paramTypes.Len() - 1).Type().(*types2.Slice).Elem() + } + return paramTypes.At(i).Type() + } + + w.multiExpr(expr, paramType, expr.ArgList) + w.Bool(expr.HasDots) + if rtype != nil { + w.rtype(rtype) + } + } +} + +func sliceElem(typ types2.Type) types2.Type { + return types2.CoreType(typ).(*types2.Slice).Elem() +} + +func (w *writer) optExpr(expr syntax.Expr) { + if w.Bool(expr != nil) { + w.expr(expr) + } +} + +// recvExpr writes out expr.X, but handles any implicit addressing, +// dereferencing, and field selections appropriate for the method +// selection. +func (w *writer) recvExpr(expr *syntax.SelectorExpr, sel *types2.Selection) types2.Type { + index := sel.Index() + implicits := index[:len(index)-1] + + w.Code(exprRecv) + w.expr(expr.X) + w.pos(expr) + w.Len(len(implicits)) + + typ := w.p.typeOf(expr.X) + for _, ix := range implicits { + typ = deref2(typ).Underlying().(*types2.Struct).Field(ix).Type() + w.Len(ix) + } + + recv := sel.Obj().(*types2.Func).Type().(*types2.Signature).Recv().Type() + if w.Bool(isPtrTo(typ, recv)) { // needs deref + typ = recv + } else if w.Bool(isPtrTo(recv, typ)) { // needs addr + typ = recv + } + + return typ +} + +// funcInst writes a reference to an instantiated function. +func (w *writer) funcInst(obj *types2.Func, targs *types2.TypeList) { + info := w.p.objInstIdx(obj, targs, w.dict) + + // Type arguments list contains derived types; we can emit a static + // call to the shaped function, but need to dynamically compute the + // runtime dictionary pointer. + if w.Bool(info.anyDerived()) { + w.Len(w.dict.subdictIdx(info)) + return + } + + // Type arguments list is statically known; we can emit a static + // call with a statically reference to the respective runtime + // dictionary. + w.objInfo(info) +} + +// methodExpr writes out a reference to the method selected by +// expr. sel should be the corresponding types2.Selection, and recv +// the type produced after any implicit addressing, dereferencing, and +// field selection. (Note: recv might differ from sel.Obj()'s receiver +// parameter in the case of interface types, and is needed for +// handling type parameter methods.) +func (w *writer) methodExpr(expr *syntax.SelectorExpr, recv types2.Type, sel *types2.Selection) { + fun := sel.Obj().(*types2.Func) + sig := fun.Type().(*types2.Signature) + + w.typ(recv) + w.typ(sig) + w.pos(expr) + w.selector(fun) + + // Method on a type parameter. These require an indirect call + // through the current function's runtime dictionary. + if typeParam, ok := recv.(*types2.TypeParam); w.Bool(ok) { + typeParamIdx := w.dict.typeParamIndex(typeParam) + methodInfo := w.p.selectorIdx(fun) + + w.Len(w.dict.typeParamMethodExprIdx(typeParamIdx, methodInfo)) + return + } + + if isInterface(recv) != isInterface(sig.Recv().Type()) { + w.p.fatalf(expr, "isInterface inconsistency: %v and %v", recv, sig.Recv().Type()) + } + + if !isInterface(recv) { + if named, ok := deref2(recv).(*types2.Named); ok { + obj, targs := splitNamed(named) + info := w.p.objInstIdx(obj, targs, w.dict) + + // Method on a derived receiver type. These can be handled by a + // static call to the shaped method, but require dynamically + // looking up the appropriate dictionary argument in the current + // function's runtime dictionary. + if w.p.hasImplicitTypeParams(obj) || info.anyDerived() { + w.Bool(true) // dynamic subdictionary + w.Len(w.dict.subdictIdx(info)) + return + } + + // Method on a fully known receiver type. These can be handled + // by a static call to the shaped method, and with a static + // reference to the receiver type's dictionary. + if targs.Len() != 0 { + w.Bool(false) // no dynamic subdictionary + w.Bool(true) // static dictionary + w.objInfo(info) + return + } + } + } + + w.Bool(false) // no dynamic subdictionary + w.Bool(false) // no static dictionary +} + +// multiExpr writes a sequence of expressions, where the i'th value is +// implicitly converted to dstType(i). It also handles when exprs is a +// single, multi-valued expression (e.g., the multi-valued argument in +// an f(g()) call, or the RHS operand in a comma-ok assignment). +func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) { + w.Sync(pkgbits.SyncMultiExpr) + + if len(exprs) == 1 { + expr := exprs[0] + if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok { + assert(tuple.Len() > 1) + w.Bool(true) // N:1 assignment + w.pos(pos) + w.expr(expr) + + w.Len(tuple.Len()) + for i := 0; i < tuple.Len(); i++ { + src := tuple.At(i).Type() + // TODO(mdempsky): Investigate not writing src here. I think + // the reader should be able to infer it from expr anyway. + w.typ(src) + if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) { + if src == nil || dst == nil { + w.p.fatalf(pos, "src is %v, dst is %v", src, dst) + } + if !types2.AssignableTo(src, dst) { + w.p.fatalf(pos, "%v is not assignable to %v", src, dst) + } + w.typ(dst) + w.convRTTI(src, dst) + } + } + return + } + } + + w.Bool(false) // N:N assignment + w.Len(len(exprs)) + for i, expr := range exprs { + w.implicitConvExpr(dstType(i), expr) + } +} + +// implicitConvExpr is like expr, but if dst is non-nil and different +// from expr's type, then an implicit conversion operation is inserted +// at expr's position. +func (w *writer) implicitConvExpr(dst types2.Type, expr syntax.Expr) { + w.convertExpr(dst, expr, true) +} + +func (w *writer) convertExpr(dst types2.Type, expr syntax.Expr, implicit bool) { + src := w.p.typeOf(expr) + + // Omit implicit no-op conversions. + identical := dst == nil || types2.Identical(src, dst) + if implicit && identical { + w.expr(expr) + return + } + + if implicit && !types2.AssignableTo(src, dst) { + w.p.fatalf(expr, "%v is not assignable to %v", src, dst) + } + + w.Code(exprConvert) + w.Bool(implicit) + w.typ(dst) + w.pos(expr) + w.convRTTI(src, dst) + w.Bool(isTypeParam(dst)) + w.Bool(identical) + w.expr(expr) +} + +func (w *writer) compLit(lit *syntax.CompositeLit) { + typ := w.p.typeOf(lit) + + w.Sync(pkgbits.SyncCompLit) + w.pos(lit) + w.typ(typ) + + if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok { + typ = ptr.Elem() + } + var keyType, elemType types2.Type + var structType *types2.Struct + switch typ0 := typ; typ := types2.CoreType(typ).(type) { + default: + w.p.fatalf(lit, "unexpected composite literal type: %v", typ) + case *types2.Array: + elemType = typ.Elem() + case *types2.Map: + w.rtype(typ0) + keyType, elemType = typ.Key(), typ.Elem() + case *types2.Slice: + elemType = typ.Elem() + case *types2.Struct: + structType = typ + } + + w.Len(len(lit.ElemList)) + for i, elem := range lit.ElemList { + elemType := elemType + if structType != nil { + if kv, ok := elem.(*syntax.KeyValueExpr); ok { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name)) + elem = kv.Value + } else { + w.pos(elem) + } + elemType = structType.Field(i).Type() + w.Len(i) + } else { + if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) { + // use position of expr.Key rather than of elem (which has position of ':') + w.pos(kv.Key) + w.implicitConvExpr(keyType, kv.Key) + elem = kv.Value + } + } + w.pos(elem) + w.implicitConvExpr(elemType, elem) + } +} + +func (w *writer) funcLit(expr *syntax.FuncLit) { + sig := w.p.typeOf(expr).(*types2.Signature) + + body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict) + + w.Sync(pkgbits.SyncFuncLit) + w.pos(expr) + w.signature(sig) + + w.Len(len(closureVars)) + for _, cv := range closureVars { + w.pos(cv.pos) + w.useLocal(cv.pos, cv.var_) + } + + w.Reloc(pkgbits.RelocBody, body) +} + +type posVar struct { + pos syntax.Pos + var_ *types2.Var +} + +func (w *writer) exprList(expr syntax.Expr) { + w.Sync(pkgbits.SyncExprList) + w.exprs(unpackListExpr(expr)) +} + +func (w *writer) exprs(exprs []syntax.Expr) { + w.Sync(pkgbits.SyncExprs) + w.Len(len(exprs)) + for _, expr := range exprs { + w.expr(expr) + } +} + +// rtype writes information so that the reader can construct an +// expression of type *runtime._type representing typ. +func (w *writer) rtype(typ types2.Type) { + typ = types2.Default(typ) + + info := w.p.typIdx(typ, w.dict) + w.rtypeInfo(info) +} + +func (w *writer) rtypeInfo(info typeInfo) { + w.Sync(pkgbits.SyncRType) + + if w.Bool(info.derived) { + w.Len(w.dict.rtypeIdx(info)) + } else { + w.typInfo(info) + } +} + +// varDictIndex writes out information for populating DictIndex for +// the ir.Name that will represent obj. +func (w *writer) varDictIndex(obj *types2.Var) { + info := w.p.typIdx(obj.Type(), w.dict) + if w.Bool(info.derived) { + w.Len(w.dict.rtypeIdx(info)) + } +} + +func isUntyped(typ types2.Type) bool { + basic, ok := typ.(*types2.Basic) + return ok && basic.Info()&types2.IsUntyped != 0 +} + +func isTuple(typ types2.Type) bool { + _, ok := typ.(*types2.Tuple) + return ok +} + +func (w *writer) itab(typ, iface types2.Type) { + typ = types2.Default(typ) + iface = types2.Default(iface) + + typInfo := w.p.typIdx(typ, w.dict) + ifaceInfo := w.p.typIdx(iface, w.dict) + + w.rtypeInfo(typInfo) + w.rtypeInfo(ifaceInfo) + if w.Bool(typInfo.derived || ifaceInfo.derived) { + w.Len(w.dict.itabIdx(typInfo, ifaceInfo)) + } +} + +// convRTTI writes information so that the reader can construct +// expressions for converting from src to dst. +func (w *writer) convRTTI(src, dst types2.Type) { + w.Sync(pkgbits.SyncConvRTTI) + w.itab(src, dst) +} + +func (w *writer) exprType(iface types2.Type, typ syntax.Expr) { + base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface) + + tv, ok := w.p.info.Types[typ] + assert(ok) + assert(tv.IsType()) + + w.Sync(pkgbits.SyncExprType) + w.pos(typ) + + if w.Bool(iface != nil && !iface.Underlying().(*types2.Interface).Empty()) { + w.itab(tv.Type, iface) + } else { + w.rtype(tv.Type) + + info := w.p.typIdx(tv.Type, w.dict) + w.Bool(info.derived) + } +} + +// isInterface reports whether typ is known to be an interface type. +// If typ is a type parameter, then isInterface reports an internal +// compiler error instead. +func isInterface(typ types2.Type) bool { + if _, ok := typ.(*types2.TypeParam); ok { + // typ is a type parameter and may be instantiated as either a + // concrete or interface type, so the writer can't depend on + // knowing this. + base.Fatalf("%v is a type parameter", typ) + } + + _, ok := typ.Underlying().(*types2.Interface) + return ok +} + +// op writes an Op into the bitstream. +func (w *writer) op(op ir.Op) { + // TODO(mdempsky): Remove in favor of explicit codes? Would make + // export data more stable against internal refactorings, but low + // priority at the moment. + assert(op != 0) + w.Sync(pkgbits.SyncOp) + w.Len(int(op)) +} + +// @@@ Package initialization + +// Caution: This code is still clumsy, because toolstash -cmp is +// particularly sensitive to it. + +type typeDeclGen struct { + *syntax.TypeDecl + gen int + + // Implicit type parameters in scope at this type declaration. + implicits []*types2.TypeName +} + +type fileImports struct { + importedEmbed, importedUnsafe bool +} + +// declCollector is a visitor type that collects compiler-needed +// information about declarations that types2 doesn't track. +// +// Notably, it maps declared types and functions back to their +// declaration statement, keeps track of implicit type parameters, and +// assigns unique type "generation" numbers to local defined types. +type declCollector struct { + pw *pkgWriter + typegen *int + file *fileImports + withinFunc bool + implicits []*types2.TypeName +} + +func (c *declCollector) withTParams(obj types2.Object) *declCollector { + tparams := objTypeParams(obj) + n := tparams.Len() + if n == 0 { + return c + } + + copy := *c + copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)] + for i := 0; i < n; i++ { + copy.implicits = append(copy.implicits, tparams.At(i).Obj()) + } + return © +} + +func (c *declCollector) Visit(n syntax.Node) syntax.Visitor { + pw := c.pw + + switch n := n.(type) { + case *syntax.File: + pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false) + + case *syntax.ImportDecl: + pw.checkPragmas(n.Pragma, 0, false) + + switch pkgNameOf(pw.info, n).Imported().Path() { + case "embed": + c.file.importedEmbed = true + case "unsafe": + c.file.importedUnsafe = true + } + + case *syntax.ConstDecl: + pw.checkPragmas(n.Pragma, 0, false) + + case *syntax.FuncDecl: + pw.checkPragmas(n.Pragma, funcPragmas, false) + + obj := pw.info.Defs[n.Name].(*types2.Func) + pw.funDecls[obj] = n + + return c.withTParams(obj) + + case *syntax.TypeDecl: + obj := pw.info.Defs[n.Name].(*types2.TypeName) + d := typeDeclGen{TypeDecl: n, implicits: c.implicits} + + if n.Alias { + pw.checkPragmas(n.Pragma, 0, false) + } else { + pw.checkPragmas(n.Pragma, 0, false) + + // Assign a unique ID to function-scoped defined types. + if c.withinFunc { + *c.typegen++ + d.gen = *c.typegen + } + } + + pw.typDecls[obj] = d + + // TODO(mdempsky): Omit? Not strictly necessary; only matters for + // type declarations within function literals within parameterized + // type declarations, but types2 the function literals will be + // constant folded away. + return c.withTParams(obj) + + case *syntax.VarDecl: + pw.checkPragmas(n.Pragma, 0, true) + + if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 { + if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil { + pw.errorf(p.Embeds[0].Pos, "%s", err) + } + } + + case *syntax.BlockStmt: + if !c.withinFunc { + copy := *c + copy.withinFunc = true + return © + } + } + + return c +} + +func (pw *pkgWriter) collectDecls(noders []*noder) { + var typegen int + for _, p := range noders { + var file fileImports + + syntax.Walk(p.file, &declCollector{ + pw: pw, + typegen: &typegen, + file: &file, + }) + + pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...) + + for _, l := range p.linknames { + if !file.importedUnsafe { + pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") + continue + } + + switch obj := pw.curpkg.Scope().Lookup(l.local).(type) { + case *types2.Func, *types2.Var: + if _, ok := pw.linknames[obj]; !ok { + pw.linknames[obj] = l.remote + } else { + pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local) + } + + default: + pw.errorf(l.pos, "//go:linkname must refer to declared function or variable") + } + } + } +} + +func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) { + if p == nil { + return + } + pragma := p.(*pragmas) + + for _, pos := range pragma.Pos { + if pos.Flag&^allowed != 0 { + pw.errorf(pos.Pos, "misplaced compiler directive") + } + } + + if !embedOK { + for _, e := range pragma.Embeds { + pw.errorf(e.Pos, "misplaced go:embed directive") + } + } +} + +func (w *writer) pkgInit(noders []*noder) { + w.Len(len(w.p.cgoPragmas)) + for _, cgoPragma := range w.p.cgoPragmas { + w.Strings(cgoPragma) + } + + w.Sync(pkgbits.SyncDecls) + for _, p := range noders { + for _, decl := range p.file.DeclList { + w.pkgDecl(decl) + } + } + w.Code(declEnd) + + w.Sync(pkgbits.SyncEOF) +} + +func (w *writer) pkgDecl(decl syntax.Decl) { + switch decl := decl.(type) { + default: + w.p.unexpected("declaration", decl) + + case *syntax.ImportDecl: + + case *syntax.ConstDecl: + w.Code(declOther) + w.pkgObjs(decl.NameList...) + + case *syntax.FuncDecl: + if decl.Name.Value == "_" { + break // skip blank functions + } + + obj := w.p.info.Defs[decl.Name].(*types2.Func) + sig := obj.Type().(*types2.Signature) + + if sig.RecvTypeParams() != nil || sig.TypeParams() != nil { + break // skip generic functions + } + + if recv := sig.Recv(); recv != nil { + w.Code(declMethod) + w.typ(recvBase(recv)) + w.selector(obj) + break + } + + w.Code(declFunc) + w.pkgObjs(decl.Name) + + case *syntax.TypeDecl: + if len(decl.TParamList) != 0 { + break // skip generic type decls + } + + if decl.Name.Value == "_" { + break // skip blank type decls + } + + name := w.p.info.Defs[decl.Name].(*types2.TypeName) + // Skip type declarations for interfaces that are only usable as + // type parameter bounds. + if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() { + break + } + + w.Code(declOther) + w.pkgObjs(decl.Name) + + case *syntax.VarDecl: + w.Code(declVar) + w.pos(decl) + w.pkgObjs(decl.NameList...) + + // TODO(mdempsky): It would make sense to use multiExpr here, but + // that results in IR that confuses pkginit/initorder.go. So we + // continue using exprList, and let typecheck handle inserting any + // implicit conversions. That's okay though, because package-scope + // assignments never require dictionaries. + w.exprList(decl.Values) + + var embeds []pragmaEmbed + if p, ok := decl.Pragma.(*pragmas); ok { + embeds = p.Embeds + } + w.Len(len(embeds)) + for _, embed := range embeds { + w.pos(embed.Pos) + w.Strings(embed.Patterns) + } + } +} + +func (w *writer) pkgObjs(names ...*syntax.Name) { + w.Sync(pkgbits.SyncDeclNames) + w.Len(len(names)) + + for _, name := range names { + obj, ok := w.p.info.Defs[name] + assert(ok) + + w.Sync(pkgbits.SyncDeclName) + w.obj(obj, nil) + } +} + +// @@@ Helpers + +// hasImplicitTypeParams reports whether obj is a defined type with +// implicit type parameters (e.g., declared within a generic function +// or method). +func (p *pkgWriter) hasImplicitTypeParams(obj *types2.TypeName) bool { + if obj.Pkg() == p.curpkg { + decl, ok := p.typDecls[obj] + assert(ok) + if len(decl.implicits) != 0 { + return true + } + } + return false +} + +// isDefinedType reports whether obj is a defined type. +func isDefinedType(obj types2.Object) bool { + if obj, ok := obj.(*types2.TypeName); ok { + return !obj.IsAlias() + } + return false +} + +// isGlobal reports whether obj was declared at package scope. +// +// Caveat: blank objects are not declared. +func isGlobal(obj types2.Object) bool { + return obj.Parent() == obj.Pkg().Scope() +} + +// lookupObj returns the object that expr refers to, if any. If expr +// is an explicit instantiation of a generic object, then the instance +// object is returned as well. +func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, inst types2.Instance) { + if index, ok := expr.(*syntax.IndexExpr); ok { + args := unpackListExpr(index.Index) + if len(args) == 1 { + tv, ok := info.Types[args[0]] + assert(ok) + if tv.IsValue() { + return // normal index expression + } + } + + expr = index.X + } + + // Strip package qualifier, if present. + if sel, ok := expr.(*syntax.SelectorExpr); ok { + if !isPkgQual(info, sel) { + return // normal selector expression + } + expr = sel.Sel + } + + if name, ok := expr.(*syntax.Name); ok { + obj = info.Uses[name] + inst = info.Instances[name] + } + return +} + +// isPkgQual reports whether the given selector expression is a +// package-qualified identifier. +func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool { + if name, ok := sel.X.(*syntax.Name); ok { + _, isPkgName := info.Uses[name].(*types2.PkgName) + return isPkgName + } + return false +} + +// isMultiValueExpr reports whether expr is a function call expression +// that yields multiple values. +func isMultiValueExpr(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + assert(tv.IsValue()) + if tuple, ok := tv.Type.(*types2.Tuple); ok { + assert(tuple.Len() > 1) + return true + } + return false +} + +// isNil reports whether expr is a (possibly parenthesized) reference +// to the predeclared nil value. +func isNil(info *types2.Info, expr syntax.Expr) bool { + tv, ok := info.Types[expr] + assert(ok) + return tv.IsNil() +} + +// recvBase returns the base type for the given receiver parameter. +func recvBase(recv *types2.Var) *types2.Named { + typ := recv.Type() + if ptr, ok := typ.(*types2.Pointer); ok { + typ = ptr.Elem() + } + return typ.(*types2.Named) +} + +// namesAsExpr returns a list of names as a syntax.Expr. +func namesAsExpr(names []*syntax.Name) syntax.Expr { + if len(names) == 1 { + return names[0] + } + + exprs := make([]syntax.Expr, len(names)) + for i, name := range names { + exprs[i] = name + } + return &syntax.ListExpr{ElemList: exprs} +} + +// fieldIndex returns the index of the struct field named by key. +func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int { + field := info.Uses[key].(*types2.Var) + + for i := 0; i < str.NumFields(); i++ { + if str.Field(i) == field { + return i + } + } + + panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str)) +} + +// objTypeParams returns the type parameters on the given object. +func objTypeParams(obj types2.Object) *types2.TypeParamList { + switch obj := obj.(type) { + case *types2.Func: + sig := obj.Type().(*types2.Signature) + if sig.Recv() != nil { + return sig.RecvTypeParams() + } + return sig.TypeParams() + case *types2.TypeName: + if !obj.IsAlias() { + return obj.Type().(*types2.Named).TypeParams() + } + } + return nil +} + +// splitNamed decomposes a use of a defined type into its original +// type definition and the type arguments used to instantiate it. +func splitNamed(typ *types2.Named) (*types2.TypeName, *types2.TypeList) { + base.Assertf(typ.TypeParams().Len() == typ.TypeArgs().Len(), "use of uninstantiated type: %v", typ) + + orig := typ.Origin() + base.Assertf(orig.TypeArgs() == nil, "origin %v of %v has type arguments", orig, typ) + base.Assertf(typ.Obj() == orig.Obj(), "%v has object %v, but %v has object %v", typ, typ.Obj(), orig, orig.Obj()) + + return typ.Obj(), typ.TypeArgs() +} + +func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag { + if p == nil { + return 0 + } + return p.(*pragmas).Flag +} + +// isPtrTo reports whether from is the type *to. +func isPtrTo(from, to types2.Type) bool { + ptr, ok := from.(*types2.Pointer) + return ok && types2.Identical(ptr.Elem(), to) +} diff --git a/src/cmd/compile/internal/objw/objw.go b/src/cmd/compile/internal/objw/objw.go index ed5ad754d9b29f..a73ed286ae014c 100644 --- a/src/cmd/compile/internal/objw/objw.go +++ b/src/cmd/compile/internal/objw/objw.go @@ -40,14 +40,14 @@ func UintN(s *obj.LSym, off int, v uint64, wid int) int { } func SymPtr(s *obj.LSym, off int, x *obj.LSym, xoff int) int { - off = int(types.Rnd(int64(off), int64(types.PtrSize))) + off = int(types.RoundUp(int64(off), int64(types.PtrSize))) s.WriteAddr(base.Ctxt, int64(off), types.PtrSize, x, int64(xoff)) off += types.PtrSize return off } func SymPtrWeak(s *obj.LSym, off int, x *obj.LSym, xoff int) int { - off = int(types.Rnd(int64(off), int64(types.PtrSize))) + off = int(types.RoundUp(int64(off), int64(types.PtrSize))) s.WriteWeakAddr(base.Ctxt, int64(off), types.PtrSize, x, int64(xoff)) off += types.PtrSize return off diff --git a/src/cmd/compile/internal/pgo/irgraph.go b/src/cmd/compile/internal/pgo/irgraph.go new file mode 100644 index 00000000000000..d27a5e1fe659cb --- /dev/null +++ b/src/cmd/compile/internal/pgo/irgraph.go @@ -0,0 +1,493 @@ +package pgo + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "fmt" + "internal/profile" + "log" + "os" + "strconv" + "strings" +) + +// IRGraph is the key datastrcture that is built from pprof profile. It is essentially a call graph with nodes pointing to IRs of functions and edges carrying weights and callsite information. The graph is bidirectional that helps in removing nodes efficiently. The IrGraph is updated as we pass through optimization phases, for example, after a function is inlined, we update the graph with new nodes and edges. +type IRGraph struct { + // Nodes of the graph + IRNodes map[string]*IRNode + OutEdges IREdgeMap + InEdges IREdgeMap +} + +// IRNode represents a node in the IRGraph. +type IRNode struct { + // Pointer to the IR of the Function represented by this node. + AST *ir.Func + // Flat weight of the IRNode, obtained from pprof. + Flat int64 + // Cumulative weight of the IRNode. + Cum int64 + // Is this function a recursive function. + Recursive bool + // Is this function a hot node? + HotNode bool +} + +// IREdgeMap maps an IRNode to its successors. +type IREdgeMap map[*IRNode][]*IREdge + +// IREdge represents a call edge in the IRGraph with source, destination, weight, callsite, and line number information. +type IREdge struct { + Src, Dst *IRNode + DstNode *ir.Node + Weight int64 + CallSite string +} + +// NodeMapInfo represents a hash key to identify unique call-edges in pprof and in IR. Used for deduplication of call edges found in pprof. +type NodeMapInfo struct { + FuncName string + DstName string + CallSite int +} + +// Weights capture both node weight and edge weight. +type Weights struct { + NWeight int64 + NTotalWeight int64 + EWeight int64 +} + +// CallSiteInfo captures call site information and its static callee. +type CallSiteInfo struct { + Line string + Caller *ir.Func +} + +var ( + // Aggregated NodeWeights and EdgeWeights across profiles. This helps us determine the percentage threshold for hot/cold partitioning. + GlobalTotalNodeWeight = int64(0) + GlobalTotalEdgeWeight = int64(0) + + // Global node and their aggregated weight information. + GlobalNodeMap = make(map[NodeMapInfo]*Weights) + + // WeightedCG represents the IRGraph built from pprof profile, which we will update as part of inlining. + WeightedCG *IRGraph = nil + + // Original cross-package PProf Graph. + PProfGraph *profile.Graph = nil +) + +// BuildPProfGraph generates a pprof-graph from cpu-profile. +func BuildPProfGraph(profileFile string, opt *profile.Options) *profile.Graph { + + if PProfGraph != nil { + return PProfGraph + } + // open the pprof profile file. + f, err := os.Open(profileFile) + if err != nil { + log.Fatal("failed to open file " + profileFile) + return nil + } + defer f.Close() + p, err := profile.Parse(f) + if err != nil { + log.Fatal("failed to Parse profile file.") + return nil + } + // Build the graph using google's pprof package. + pProfGraph := profile.New(p, opt) + + // Build various global maps from pprof profile. + preprocessPProfGraph(pProfGraph) + + return pProfGraph +} + +// BuildWeightedCallGraph generates a weighted callgraph from the pprof profile for the current package. +func BuildWeightedCallGraph() *IRGraph { + + // Bail if there is no pprof-graph available. + if PProfGraph == nil { + return nil + } + + // Create package-level call graph with weights from pprof profile and IR. + weightedCG := createIRGraph() + + if weightedCG != nil && base.Flag.LowerM > 1 { + log.Println("weighted call graph created successfully!") + } + + return weightedCG +} + +// preprocessPProfGraph builds various maps from profiles. It builds GlobalNodeMap and other information based on the name and callsite to compute node and edge weights which will be used later on to create edges of WeightedCG. +func preprocessPProfGraph(pProfGraph *profile.Graph) { + nweight := make(map[string]int64) + nTotalWeight := make(map[string]int64) + + // Accummulate weights for the same nodes. + for _, n := range pProfGraph.Nodes { + canonicalName := n.Info.Name + if _, ok := nweight[canonicalName]; ok { + nweight[canonicalName] += n.FlatValue() + nTotalWeight[canonicalName] += n.CumValue() + } else { + nweight[canonicalName] = n.FlatValue() + nTotalWeight[canonicalName] = n.CumValue() + } + } + + // Process PProfGraph and build various node and edge maps which will be consumed by AST walk. + for _, n := range pProfGraph.Nodes { + GlobalTotalNodeWeight += n.FlatValue() + canonicalName := n.Info.Name + // Create the key to the NodeMap. + nodeinfo := NodeMapInfo{ + FuncName: canonicalName, + CallSite: n.Info.Lineno, + } + // If there are no outgoing edges, we still need to create the node [for cold sites] with no callee information. + if len(n.Out) == 0 { + nodeinfo.DstName = "" + nodeinfo.CallSite = -1 + + weights := new(Weights) + weights.NWeight = nweight[canonicalName] + weights.NTotalWeight = nTotalWeight[canonicalName] + weights.EWeight = 0 + + GlobalNodeMap[nodeinfo] = weights + } + + for _, e := range n.Out { + GlobalTotalEdgeWeight += e.WeightValue() + nodeinfo.DstName = e.Dest.Info.Name + if w, ok := GlobalNodeMap[nodeinfo]; ok { + w.EWeight += e.WeightValue() + } else { + weights := new(Weights) + weights.NWeight = nweight[canonicalName] + weights.NTotalWeight = nTotalWeight[canonicalName] + weights.EWeight = e.WeightValue() + GlobalNodeMap[nodeinfo] = weights + } + } + } +} + +// createIRGraph builds the IRGraph by visting all the ir.Func in decl list of a package. +func createIRGraph() *IRGraph { + var g IRGraph + // Bottomup walk over the function to create IRGraph. + ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { + for _, n := range list { + g.Visit(n, recursive) + } + }) + return &g +} + +// Visit traverses the body of each ir.Func and use GlobalNodeMap to determine if we need to add an edge from ir.Func and any node in the ir.Func body. +func (g *IRGraph) Visit(fn *ir.Func, recursive bool) { + if g.IRNodes == nil { + g.IRNodes = make(map[string]*IRNode) + } + if g.OutEdges == nil { + g.OutEdges = make(map[*IRNode][]*IREdge) + } + if g.InEdges == nil { + g.InEdges = make(map[*IRNode][]*IREdge) + } + name := ir.PkgFuncName(fn) + node := new(IRNode) + node.AST = fn + node.Recursive = recursive + if g.IRNodes[name] == nil { + g.IRNodes[name] = node + } + // Create the hash key of the GlobalNodeMap. + nodeinfo := NodeMapInfo{ + FuncName: name, + DstName: "", + CallSite: -1, + } + // If the node exists, then update its node weight. + if weights, ok := GlobalNodeMap[nodeinfo]; ok { + g.IRNodes[name].Flat = weights.NWeight + g.IRNodes[name].Cum = weights.NTotalWeight + } + + // Recursively walk over the body of the function to create IRGraph edges. + g.createIRGraphEdge(fn, g.IRNodes[name], name) +} + +// addEdge adds an edge between node1 and new node that points to f based on pprof graph using GlobalNodeMap. node1 represents the caller. Callee as f. CallSite n. +func (g *IRGraph) addEdge(node1 *IRNode, f *ir.Func, n *ir.Node, name string, line string) { + + splits := strings.Split(line, ":") + line2, _ := strconv.ParseInt(splits[len(splits)-2], 0, 64) + + // Create an IRNode for Callee. + node2 := new(IRNode) + node2.AST = f + name2 := ir.PkgFuncName(f) + + // Create a hash key using NodeMapInfo with Caller FuncName and Callee fname. + nodeinfo := NodeMapInfo{ + FuncName: name, + DstName: name2, + CallSite: int(line2), + } + + // A new callee node? If so create the node with node weight. + // Remove this TODO. + if g.IRNodes[name2] == nil { + g.IRNodes[name2] = node2 + nodeinfo2 := NodeMapInfo{ + FuncName: name2, + DstName: "", + CallSite: -1, + } + if weights, ok := GlobalNodeMap[nodeinfo2]; ok { + g.IRNodes[name2].Flat = weights.NWeight + g.IRNodes[name2].Cum = weights.NTotalWeight + } + } + + if weights, ok := GlobalNodeMap[nodeinfo]; ok { + node1.Flat = weights.NWeight + node1.Cum = weights.NTotalWeight + + // Add edge in the IRGraph from caller to callee [callee is an interface type here which can have multiple targets]. + info := &IREdge{Src: node1, Dst: g.IRNodes[name2], DstNode: n, Weight: weights.EWeight, CallSite: line} + g.OutEdges[node1] = append(g.OutEdges[node1], info) + g.InEdges[g.IRNodes[name2]] = append(g.InEdges[g.IRNodes[name2]], info) + } else { + nodeinfo.DstName = "" + nodeinfo.CallSite = -1 + if weights, ok := GlobalNodeMap[nodeinfo]; ok { + node1.Flat = weights.NWeight + node1.Cum = weights.NTotalWeight + info := &IREdge{Src: node1, Dst: g.IRNodes[name2], DstNode: n, Weight: 0, CallSite: line} + g.OutEdges[node1] = append(g.OutEdges[node1], info) + g.InEdges[g.IRNodes[name2]] = append(g.InEdges[g.IRNodes[name2]], info) + } else { + info := &IREdge{Src: node1, Dst: g.IRNodes[name2], DstNode: n, Weight: 0, CallSite: line} + g.OutEdges[node1] = append(g.OutEdges[node1], info) + g.InEdges[g.IRNodes[name2]] = append(g.InEdges[g.IRNodes[name2]], info) + } + } +} + +// createIRGraphEdge traverses the nodes in the body of ir.Func and add edges between node1 which points to the ir.Func and the nodes in the body. +func (g *IRGraph) createIRGraphEdge(fn *ir.Func, node1 *IRNode, name string) { + var doNode func(ir.Node) bool + doNode = func(n ir.Node) bool { + switch n.Op() { + default: + ir.DoChildren(n, doNode) + case ir.OCALLFUNC: + call := n.(*ir.CallExpr) + line := ir.Line(n) + // Find the callee function from the call site and add the edge. + f := inlCallee(call.X) + if f != nil { + g.addEdge(node1, f, &n, name, line) + } + case ir.OCALLMETH: + call := n.(*ir.CallExpr) + // Find the callee method from the call site and add the edge. + fn2 := ir.MethodExprName(call.X).Func + line := ir.Line(n) + g.addEdge(node1, fn2, &n, name, line) + } + return false + } + doNode(fn) +} + +// WeightInPercentage converts profile weights to a percentage. +func WeightInPercentage(value int64, total int64) float64 { + var ratio float64 + // percentage is computed at the (weight/totalweights) * 100 + // e.g. if edge weight is 30 and the sum of all the edges weight is 126 + // the percentage will be 23.8% + if total != 0 { + ratio = (float64(value) / float64(total)) * 100 + } + return ratio +} + +// PrintWeightedCallGraphDOT prints IRGraph in DOT format.. +func PrintWeightedCallGraphDOT(threshold float64) { + fmt.Printf("digraph G {\n") + fmt.Printf("forcelabels=true;\n") + + // List of functions in this package. + funcs := make(map[string]struct{}) + ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { + for _, f := range list { + name := ir.PkgFuncName(f) + funcs[name] = struct{}{} + } + }) + + // Determine nodes of DOT. + nodes := make(map[string]*ir.Func) + for name, _ := range funcs { + if n, ok := WeightedCG.IRNodes[name]; ok { + nodeweight := WeightInPercentage(n.Flat, GlobalTotalNodeWeight) + for _, e := range WeightedCG.OutEdges[n] { + if e.Weight != 0 { + p := WeightInPercentage(e.Weight, GlobalTotalEdgeWeight) + if p > threshold { + nodes[ir.PkgFuncName(e.Src.AST)] = e.Src.AST + nodes[ir.PkgFuncName(e.Dst.AST)] = e.Dst.AST + } + } + } + if nodeweight > threshold { + nodes[ir.PkgFuncName(n.AST)] = n.AST + } + } + } + + // Print nodes. + for name, ast := range nodes { + if n, ok := WeightedCG.IRNodes[name]; ok { + nodeweight := WeightInPercentage(n.Flat, GlobalTotalNodeWeight) + if ast.Inl != nil { + fmt.Printf("\"%v\" [label=\"%v,freq=%.2f,inl_cost=%d\"];\n", ir.PkgFuncName(ast), ir.PkgFuncName(ast), nodeweight, ast.Inl.Cost) + } else { + fmt.Printf("\"%v\" [label=\"%v,freq=%.2f\"];\n", ir.PkgFuncName(ast), ir.PkgFuncName(ast), nodeweight) + } + } + } + // Print edges. + ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) { + for _, f := range list { + name := ir.PkgFuncName(f) + if n, ok := WeightedCG.IRNodes[name]; ok { + for _, e := range WeightedCG.OutEdges[n] { + if e.Weight != 0 { + p := WeightInPercentage(e.Weight, GlobalTotalEdgeWeight) + if p > threshold { + fmt.Printf("edge [color=red, style=solid];\n") + } else { + fmt.Printf("edge [color=black, style=solid];\n") + } + + fmt.Printf("\"%v\" -> \"%v\" [label=\"%.2f\"];\n", ir.PkgFuncName(n.AST), ir.PkgFuncName(e.Dst.AST), p) + } + } + } + } + }) + fmt.Printf("}\n") +} + +// redirectEdges deletes the cur node out-edges and redirect them so now these edges are the parent node out-edges. +func redirectEdges(g *IRGraph, parent *IRNode, cur *IRNode) { + for _, outEdge := range g.OutEdges[cur] { + outEdge.Src = parent + g.OutEdges[parent] = append(g.OutEdges[parent], outEdge) + } + delete(g.OutEdges, cur) +} + +// RedirectEdges deletes and redirects out-edges from node cur based on inlining information via inlinedCallSites. +func RedirectEdges(cur *IRNode, inlinedCallSites map[CallSiteInfo]struct{}) { + g := WeightedCG + for i, outEdge := range g.OutEdges[cur] { + if _, found := inlinedCallSites[CallSiteInfo{outEdge.CallSite, cur.AST}]; !found { + for _, InEdge := range g.InEdges[cur] { + if _, ok := inlinedCallSites[CallSiteInfo{InEdge.CallSite, InEdge.Src.AST}]; ok { + weight := calculateweight(g, InEdge.Src, cur) + redirectEdge(g, InEdge.Src, cur, outEdge, weight, i) + } + } + } else { + remove(g, cur, i, outEdge.Dst.AST.Nname) + } + } + removeall(g, cur) +} + +// calculateweight calculates the weight of the new redirected edge. +func calculateweight(g *IRGraph, parent *IRNode, cur *IRNode) int64 { + sum := int64(0) + pw := int64(0) + for _, InEdge := range g.InEdges[cur] { + sum = sum + InEdge.Weight + if InEdge.Src == parent { + pw = InEdge.Weight + } + } + weight := int64(0) + if sum != 0 { + weight = pw / sum + } else { + weight = pw + } + return weight +} + +// redirectEdge deletes the cur-node's out-edges and redirect them so now these edges are the parent node out-edges. +func redirectEdge(g *IRGraph, parent *IRNode, cur *IRNode, outEdge *IREdge, weight int64, idx int) { + outEdge.Src = parent + outEdge.Weight = weight * outEdge.Weight + g.OutEdges[parent] = append(g.OutEdges[parent], outEdge) + remove(g, cur, idx, outEdge.Dst.AST.Nname) +} + +// remove deletes the cur-node's out-edges at index idx. +func remove(g *IRGraph, cur *IRNode, idx int, name *ir.Name) { + if len(g.OutEdges[cur]) >= 2 { + g.OutEdges[cur][idx] = &IREdge{CallSite: "removed"} + } else { + delete(g.OutEdges, cur) + } +} + +// removeall deletes all cur-node's out-edges that marked to be removed . +func removeall(g *IRGraph, cur *IRNode) { + for i := len(g.OutEdges[cur]) - 1; i >= 0; i-- { + if g.OutEdges[cur][i].CallSite == "removed" { + g.OutEdges[cur][i] = g.OutEdges[cur][len(g.OutEdges[cur])-1] + g.OutEdges[cur] = g.OutEdges[cur][:len(g.OutEdges[cur])-1] + } + } +} + +// inlCallee is same as the implementation for inl.go with one change. The change is that we do not invoke CanInline on a closure. +func inlCallee(fn ir.Node) *ir.Func { + fn = ir.StaticValue(fn) + switch fn.Op() { + case ir.OMETHEXPR: + fn := fn.(*ir.SelectorExpr) + n := ir.MethodExprName(fn) + // Check that receiver type matches fn.X. + // TODO(mdempsky): Handle implicit dereference + // of pointer receiver argument? + if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) { + return nil + } + return n.Func + case ir.ONAME: + fn := fn.(*ir.Name) + if fn.Class == ir.PFUNC { + return fn.Func + } + case ir.OCLOSURE: + fn := fn.(*ir.ClosureExpr) + c := fn.Func + return c + } + return nil +} diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go index 7cad2622146d0e..8c60e3bfd616eb 100644 --- a/src/cmd/compile/internal/pkginit/init.go +++ b/src/cmd/compile/internal/pkginit/init.go @@ -6,23 +6,70 @@ package pkginit import ( "cmd/compile/internal/base" - "cmd/compile/internal/deadcode" "cmd/compile/internal/ir" + "cmd/compile/internal/noder" "cmd/compile/internal/objw" + "cmd/compile/internal/staticinit" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" + "cmd/internal/src" ) +// MakeInit creates a synthetic init function to handle any +// package-scope initialization statements. +// +// TODO(mdempsky): Move into noder, so that the types2-based frontends +// can use Info.InitOrder instead. +func MakeInit() { + nf := initOrder(typecheck.Target.Decls) + if len(nf) == 0 { + return + } + + // Make a function that contains all the initialization statements. + base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt + initializers := typecheck.Lookup("init") + fn := typecheck.DeclFunc(initializers, nil, nil, nil) + for _, dcl := range typecheck.InitTodoFunc.Dcl { + dcl.Curfn = fn + } + fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...) + typecheck.InitTodoFunc.Dcl = nil + + // Suppress useless "can inline" diagnostics. + // Init functions are only called dynamically. + fn.SetInlinabilityChecked(true) + + fn.Body = nf + typecheck.FinishFuncBody() + + typecheck.Func(fn) + ir.WithFunc(fn, func() { + typecheck.Stmts(nf) + }) + typecheck.Target.Decls = append(typecheck.Target.Decls, fn) + + // Prepend to Inits, so it runs first, before any user-declared init + // functions. + typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...) + + if typecheck.InitTodoFunc.Dcl != nil { + // We only generate temps using InitTodoFunc if there + // are package-scope initialization statements, so + // something's weird if we get here. + base.Fatalf("InitTodoFunc still has declarations") + } + typecheck.InitTodoFunc = nil +} + // Task makes and returns an initialization record for the package. // See runtime/proc.go:initTask for its layout. // The 3 tasks for initialization are: -// 1) Initialize all of the packages the current package depends on. -// 2) Initialize all the variables that have initializers. -// 3) Run any init functions. +// 1. Initialize all of the packages the current package depends on. +// 2. Initialize all the variables that have initializers. +// 3. Run any init functions. func Task() *ir.Name { - nf := initOrder(typecheck.Target.Decls) - var deps []*obj.LSym // initTask records for packages the current package depends on var fns []*obj.LSym // functions to call for package initialization @@ -37,40 +84,81 @@ func Task() *ir.Name { } deps = append(deps, n.(*ir.Name).Linksym()) } - - // Make a function that contains all the initialization statements. - if len(nf) > 0 { - base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt - initializers := typecheck.Lookup("init") - fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil)) - for _, dcl := range typecheck.InitTodoFunc.Dcl { - dcl.Curfn = fn + if base.Flag.ASan { + // Make an initialization function to call runtime.asanregisterglobals to register an + // array of instrumented global variables when -asan is enabled. An instrumented global + // variable is described by a structure. + // See the _asan_global structure declared in src/runtime/asan/asan.go. + // + // func init { + // var globals []_asan_global {...} + // asanregisterglobals(&globals[0], len(globals)) + // } + for _, n := range typecheck.Target.Externs { + if canInstrumentGlobal(n) { + name := n.Sym().Name + InstrumentGlobalsMap[name] = n + InstrumentGlobalsSlice = append(InstrumentGlobalsSlice, n) + } } - fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...) - typecheck.InitTodoFunc.Dcl = nil + ni := len(InstrumentGlobalsMap) + if ni != 0 { + // Make an init._ function. + base.Pos = base.AutogeneratedPos + typecheck.DeclContext = ir.PEXTERN + name := noder.Renameinit() + fnInit := typecheck.DeclFunc(name, nil, nil, nil) - fn.Body = nf - typecheck.FinishFuncBody() + // Get an array of intrumented global variables. + globals := instrumentGlobals(fnInit) - typecheck.Func(fn) - ir.CurFunc = fn - typecheck.Stmts(nf) - ir.CurFunc = nil - typecheck.Target.Decls = append(typecheck.Target.Decls, fn) - fns = append(fns, fn.Linksym()) - } - if typecheck.InitTodoFunc.Dcl != nil { - // We only generate temps using InitTodoFunc if there - // are package-scope initialization statements, so - // something's weird if we get here. - base.Fatalf("InitTodoFunc still has declarations") + // Call runtime.asanregisterglobals function to poison redzones. + // runtime.asanregisterglobals(unsafe.Pointer(&globals[0]), ni) + asanf := typecheck.NewName(ir.Pkgs.Runtime.Lookup("asanregisterglobals")) + ir.MarkFunc(asanf) + asanf.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{ + types.NewField(base.Pos, nil, types.Types[types.TUNSAFEPTR]), + types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), + }, nil)) + asancall := ir.NewCallExpr(base.Pos, ir.OCALL, asanf, nil) + asancall.Args.Append(typecheck.ConvNop(typecheck.NodAddr( + ir.NewIndexExpr(base.Pos, globals, ir.NewInt(0))), types.Types[types.TUNSAFEPTR])) + asancall.Args.Append(typecheck.ConvNop(ir.NewInt(int64(ni)), types.Types[types.TUINTPTR])) + + fnInit.Body.Append(asancall) + typecheck.FinishFuncBody() + typecheck.Func(fnInit) + ir.CurFunc = fnInit + typecheck.Stmts(fnInit.Body) + ir.CurFunc = nil + + typecheck.Target.Decls = append(typecheck.Target.Decls, fnInit) + typecheck.Target.Inits = append(typecheck.Target.Inits, fnInit) + } } - typecheck.InitTodoFunc = nil // Record user init functions. for _, fn := range typecheck.Target.Inits { - // Must happen after initOrder; see #43444. - deadcode.Func(fn) + if fn.Sym().Name == "init" { + // Synthetic init function for initialization of package-scope + // variables. We can use staticinit to optimize away static + // assignments. + s := staticinit.Schedule{ + Plans: make(map[ir.Node]*staticinit.Plan), + Temps: make(map[ir.Node]*ir.Name), + } + for _, n := range fn.Body { + s.StaticInit(n) + } + fn.Body = s.Out + ir.WithFunc(fn, func() { + typecheck.Stmts(fn.Body) + }) + + if len(fn.Body) == 0 { + fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)} + } + } // Skip init functions with empty bodies. if len(fn.Body) == 1 { @@ -81,7 +169,7 @@ func Task() *ir.Name { fns = append(fns, fn.Nname.Linksym()) } - if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" { + if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Path != "main" && types.LocalPkg.Path != "runtime" { return nil // nothing to initialize } diff --git a/src/cmd/compile/internal/pkginit/initAsanGlobals.go b/src/cmd/compile/internal/pkginit/initAsanGlobals.go new file mode 100644 index 00000000000000..63aa361694c5ef --- /dev/null +++ b/src/cmd/compile/internal/pkginit/initAsanGlobals.go @@ -0,0 +1,241 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkginit + +import ( + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// instrumentGlobals declares a global array of _asan_global structures and initializes it. +func instrumentGlobals(fn *ir.Func) *ir.Name { + asanGlobalStruct, asanLocationStruct, defStringstruct := createtypes() + lname := typecheck.Lookup + tconv := typecheck.ConvNop + // Make a global array of asanGlobalStruct type. + // var asanglobals []asanGlobalStruct + arraytype := types.NewArray(asanGlobalStruct, int64(len(InstrumentGlobalsMap))) + symG := lname(".asanglobals") + globals := typecheck.NewName(symG) + globals.SetType(arraytype) + globals.Class = ir.PEXTERN + symG.Def = globals + typecheck.Target.Externs = append(typecheck.Target.Externs, globals) + // Make a global array of asanLocationStruct type. + // var asanL []asanLocationStruct + arraytype = types.NewArray(asanLocationStruct, int64(len(InstrumentGlobalsMap))) + symL := lname(".asanL") + asanlocation := typecheck.NewName(symL) + asanlocation.SetType(arraytype) + asanlocation.Class = ir.PEXTERN + symL.Def = asanlocation + typecheck.Target.Externs = append(typecheck.Target.Externs, asanlocation) + // Make three global string variables to pass the global name and module name + // and the name of the source file that defines it. + // var asanName string + // var asanModulename string + // var asanFilename string + symL = lname(".asanName") + asanName := typecheck.NewName(symL) + asanName.SetType(types.Types[types.TSTRING]) + asanName.Class = ir.PEXTERN + symL.Def = asanName + typecheck.Target.Externs = append(typecheck.Target.Externs, asanName) + + symL = lname(".asanModulename") + asanModulename := typecheck.NewName(symL) + asanModulename.SetType(types.Types[types.TSTRING]) + asanModulename.Class = ir.PEXTERN + symL.Def = asanModulename + typecheck.Target.Externs = append(typecheck.Target.Externs, asanModulename) + + symL = lname(".asanFilename") + asanFilename := typecheck.NewName(symL) + asanFilename.SetType(types.Types[types.TSTRING]) + asanFilename.Class = ir.PEXTERN + symL.Def = asanFilename + typecheck.Target.Externs = append(typecheck.Target.Externs, asanFilename) + + var init ir.Nodes + var c ir.Node + // globals[i].odrIndicator = 0 is the default, no need to set it explicitly here. + for i, n := range InstrumentGlobalsSlice { + setField := func(f string, val ir.Node, i int) { + r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, + ir.NewIndexExpr(base.Pos, globals, ir.NewInt(int64(i))), lname(f)), val) + init.Append(typecheck.Stmt(r)) + } + // globals[i].beg = uintptr(unsafe.Pointer(&n)) + c = tconv(typecheck.NodAddr(n), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.Types[types.TUINTPTR]) + setField("beg", c, i) + // Assign globals[i].size. + g := n.(*ir.Name) + size := g.Type().Size() + c = tconv(ir.NewInt(size), types.Types[types.TUINTPTR]) + setField("size", c, i) + // Assign globals[i].sizeWithRedzone. + rzSize := GetRedzoneSizeForGlobal(size) + sizeWithRz := rzSize + size + c = tconv(ir.NewInt(sizeWithRz), types.Types[types.TUINTPTR]) + setField("sizeWithRedzone", c, i) + // The C string type is terminated by a null character "\0", Go should use three-digit + // octal "\000" or two-digit hexadecimal "\x00" to create null terminated string. + // asanName = symbol's linkname + "\000" + // globals[i].name = (*defString)(unsafe.Pointer(&asanName)).data + name := g.Linksym().Name + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanName, ir.NewString(name+"\000")))) + c = tconv(typecheck.NodAddr(asanName), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + setField("name", c, i) + + // Set the name of package being compiled as a unique identifier of a module. + // asanModulename = pkgName + "\000" + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanModulename, ir.NewString(types.LocalPkg.Name+"\000")))) + c = tconv(typecheck.NodAddr(asanModulename), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + setField("moduleName", c, i) + // Assign asanL[i].filename, asanL[i].line, asanL[i].column + // and assign globals[i].location = uintptr(unsafe.Pointer(&asanL[i])) + asanLi := ir.NewIndexExpr(base.Pos, asanlocation, ir.NewInt(int64(i))) + filename := ir.NewString(base.Ctxt.PosTable.Pos(n.Pos()).Filename() + "\000") + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanFilename, filename))) + c = tconv(typecheck.NodAddr(asanFilename), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("filename")), c))) + line := ir.NewInt(int64(n.Pos().Line())) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("line")), line))) + col := ir.NewInt(int64(n.Pos().Col())) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("column")), col))) + c = tconv(typecheck.NodAddr(asanLi), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.Types[types.TUINTPTR]) + setField("sourceLocation", c, i) + } + fn.Body.Append(init...) + return globals +} + +// createtypes creates the asanGlobal, asanLocation and defString struct type. +// Go compiler does not refer to the C types, we represent the struct field +// by a uintptr, then use type conversion to make copies of the data. +// E.g., (*defString)(asanGlobal.name).data to C string. +// +// Keep in sync with src/runtime/asan/asan.go. +// type asanGlobal struct { +// beg uintptr +// size uintptr +// size_with_redzone uintptr +// name uintptr +// moduleName uintptr +// hasDynamicInit uintptr +// sourceLocation uintptr +// odrIndicator uintptr +// } +// +// type asanLocation struct { +// filename uintptr +// line int32 +// column int32 +// } +// +// defString is synthesized struct type meant to capture the underlying +// implementations of string. +// type defString struct { +// data uintptr +// len uintptr +// } + +func createtypes() (*types.Type, *types.Type, *types.Type) { + up := types.Types[types.TUINTPTR] + i32 := types.Types[types.TINT32] + fname := typecheck.Lookup + nxp := src.NoXPos + nfield := types.NewField + asanGlobal := types.NewStruct(types.NoPkg, []*types.Field{ + nfield(nxp, fname("beg"), up), + nfield(nxp, fname("size"), up), + nfield(nxp, fname("sizeWithRedzone"), up), + nfield(nxp, fname("name"), up), + nfield(nxp, fname("moduleName"), up), + nfield(nxp, fname("hasDynamicInit"), up), + nfield(nxp, fname("sourceLocation"), up), + nfield(nxp, fname("odrIndicator"), up), + }) + types.CalcSize(asanGlobal) + + asanLocation := types.NewStruct(types.NoPkg, []*types.Field{ + nfield(nxp, fname("filename"), up), + nfield(nxp, fname("line"), i32), + nfield(nxp, fname("column"), i32), + }) + types.CalcSize(asanLocation) + + defString := types.NewStruct(types.NoPkg, []*types.Field{ + types.NewField(nxp, fname("data"), up), + types.NewField(nxp, fname("len"), up), + }) + types.CalcSize(defString) + + return asanGlobal, asanLocation, defString +} + +// Calculate redzone for globals. +func GetRedzoneSizeForGlobal(size int64) int64 { + maxRZ := int64(1 << 18) + minRZ := int64(32) + redZone := (size / minRZ / 4) * minRZ + switch { + case redZone > maxRZ: + redZone = maxRZ + case redZone < minRZ: + redZone = minRZ + } + // Round up to multiple of minRZ. + if size%minRZ != 0 { + redZone += minRZ - (size % minRZ) + } + return redZone +} + +// InstrumentGlobalsMap contains only package-local (and unlinknamed from somewhere else) +// globals. +// And the key is the object name. For example, in package p, a global foo would be in this +// map as "foo". +// Consider range over maps is nondeterministic, make a slice to hold all the values in the +// InstrumentGlobalsMap and iterate over the InstrumentGlobalsSlice. +var InstrumentGlobalsMap = make(map[string]ir.Node) +var InstrumentGlobalsSlice = make([]ir.Node, 0, 0) + +func canInstrumentGlobal(g ir.Node) bool { + if g.Op() != ir.ONAME { + return false + } + n := g.(*ir.Name) + if n.Class == ir.PFUNC { + return false + } + if n.Sym().Pkg != types.LocalPkg { + return false + } + // Do not instrument any _cgo_ related global variables, because they are declared in C code. + if strings.Contains(n.Sym().Name, "cgo") { + return false + } + + // Do not instrument globals that are linknamed, because their home package will do the work. + if n.Sym().Linkname != "" { + return false + } + + return true +} diff --git a/src/cmd/compile/internal/pkginit/initorder.go b/src/cmd/compile/internal/pkginit/initorder.go index 97d69629fbae9e..6290a8f314b7a3 100644 --- a/src/cmd/compile/internal/pkginit/initorder.go +++ b/src/cmd/compile/internal/pkginit/initorder.go @@ -5,13 +5,12 @@ package pkginit import ( - "bytes" "container/heap" "fmt" + "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" - "cmd/compile/internal/staticinit" ) // Package initialization @@ -78,10 +77,7 @@ type InitOrder struct { // corresponding list of statements to include in the init() function // body. func initOrder(l []ir.Node) []ir.Node { - s := staticinit.Schedule{ - Plans: make(map[ir.Node]*staticinit.Plan), - Temps: make(map[ir.Node]*ir.Name), - } + var res ir.Nodes o := InitOrder{ blocking: make(map[ir.Node][]ir.Node), order: make(map[ir.Node]int), @@ -92,7 +88,7 @@ func initOrder(l []ir.Node) []ir.Node { switch n.Op() { case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: o.processAssign(n) - o.flushReady(s.StaticInit) + o.flushReady(func(n ir.Node) { res.Append(n) }) case ir.ODCLCONST, ir.ODCLFUNC, ir.ODCLTYPE: // nop default: @@ -125,7 +121,7 @@ func initOrder(l []ir.Node) []ir.Node { base.Fatalf("expected empty map: %v", o.blocking) } - return s.Out + return res } func (o *InitOrder) processAssign(n ir.Node) { @@ -240,7 +236,7 @@ func reportInitLoopAndExit(l []*ir.Name) { // TODO(mdempsky): Method values are printed as "T.m-fm" // rather than "T.m". Figure out how to avoid that. - var msg bytes.Buffer + var msg strings.Builder fmt.Fprintf(&msg, "initialization loop:\n") for _, n := range l { fmt.Fprintf(&msg, "\t%v: %v refers to\n", ir.Line(n), n) @@ -304,7 +300,7 @@ func (d *initDeps) visit(n ir.Node) { n := n.(*ir.ClosureExpr) d.inspectList(n.Func.Body) - case ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR: + case ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR: d.foundDep(ir.MethodExprName(n)) } } diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go index 590290fa371008..20fd8cec54f397 100644 --- a/src/cmd/compile/internal/ppc64/galign.go +++ b/src/cmd/compile/internal/ppc64/galign.go @@ -16,13 +16,14 @@ func Init(arch *ssagen.ArchInfo) { arch.LinkArch = &ppc64.Linkppc64le } arch.REGSP = ppc64.REGSP - arch.MAXWIDTH = 1 << 60 + arch.MAXWIDTH = 1 << 50 arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnopdefer arch.SSAMarkMoves = ssaMarkMoves arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock + arch.LoadRegResult = loadRegResult + arch.SpillArgReg = spillArgReg } diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go index c76962cfb81133..4c935cfc71f8d3 100644 --- a/src/cmd/compile/internal/ppc64/ggen.go +++ b/src/cmd/compile/internal/ppc64/ggen.go @@ -19,17 +19,17 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } if cnt < int64(4*types.PtrSize) { for i := int64(0); i < cnt; i += int64(types.PtrSize) { - p = pp.Append(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, base.Ctxt.FixedFrameSize()+off+i) + p = pp.Append(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, base.Ctxt.Arch.FixedFrameSize+off+i) } } else if cnt <= int64(128*types.PtrSize) { - p = pp.Append(p, ppc64.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGRT1, 0) + p = pp.Append(p, ppc64.AADD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-8, obj.TYPE_REG, ppc64.REGRT1, 0) p.Reg = ppc64.REGSP p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) p.To.Name = obj.NAME_EXTERN p.To.Sym = ir.Syms.Duffzero p.To.Offset = 4 * (128 - cnt/int64(types.PtrSize)) } else { - p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGTMP, 0) + p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-8, obj.TYPE_REG, ppc64.REGTMP, 0) p = pp.Append(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0) p.Reg = ppc64.REGSP p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0) @@ -46,37 +46,9 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } func ginsnop(pp *objw.Progs) *obj.Prog { + // Generate the preferred hardware nop: ori 0,0,0 p := pp.Prog(ppc64.AOR) - p.From.Type = obj.TYPE_REG - p.From.Reg = ppc64.REG_R0 - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REG_R0 + p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} + p.To = obj.Addr{Type: obj.TYPE_REG, Reg: ppc64.REG_R0} return p } - -func ginsnopdefer(pp *objw.Progs) *obj.Prog { - // On PPC64 two nops are required in the defer case. - // - // (see gc/cgen.go, gc/plive.go -- copy of comment below) - // - // On ppc64, when compiling Go into position - // independent code on ppc64le we insert an - // instruction to reload the TOC pointer from the - // stack as well. See the long comment near - // jmpdefer in runtime/asm_ppc64.s for why. - // If the MOVD is not needed, insert a hardware NOP - // so that the same number of instructions are used - // on ppc64 in both shared and non-shared modes. - - ginsnop(pp) - if base.Ctxt.Flag_shared { - p := pp.Prog(ppc64.AMOVD) - p.From.Type = obj.TYPE_MEM - p.From.Offset = 24 - p.From.Reg = ppc64.REGSP - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REG_R2 - return p - } - return ginsnop(pp) -} diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index 11226f65a0f016..41f863e7ce6376 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -8,6 +8,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" + "cmd/compile/internal/objw" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" @@ -142,31 +143,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p1.To.Type = obj.TYPE_REG p1.To.Reg = v.Reg1() - case ssa.OpPPC64LoweredAdd64Carry: - // ADDC Rarg2, -1, Rtmp - // ADDE Rarg1, Rarg0, Reg0 - // ADDZE Rzero, Reg1 - r0 := v.Args[0].Reg() - r1 := v.Args[1].Reg() - r2 := v.Args[2].Reg() - p := s.Prog(ppc64.AADDC) - p.From.Type = obj.TYPE_CONST - p.From.Offset = -1 - p.Reg = r2 - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REGTMP - p1 := s.Prog(ppc64.AADDE) - p1.From.Type = obj.TYPE_REG - p1.From.Reg = r1 - p1.Reg = r0 - p1.To.Type = obj.TYPE_REG - p1.To.Reg = v.Reg0() - p2 := s.Prog(ppc64.AADDZE) - p2.From.Type = obj.TYPE_REG - p2.From.Reg = ppc64.REGZERO - p2.To.Type = obj.TYPE_REG - p2.To.Reg = v.Reg1() - case ssa.OpPPC64LoweredAtomicAnd8, ssa.OpPPC64LoweredAtomicAnd32, ssa.OpPPC64LoweredAtomicOr8, @@ -381,18 +357,16 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpPPC64LoweredAtomicCas64, ssa.OpPPC64LoweredAtomicCas32: + // MOVD $0, Rout // LWSYNC // loop: // LDAR (Rarg0), MutexHint, Rtmp // CMP Rarg1, Rtmp - // BNE fail + // BNE end // STDCCC Rarg2, (Rarg0) // BNE loop // LWSYNC // Only for sequential consistency; not required in CasRel. // MOVD $1, Rout - // BR end - // fail: - // MOVD $0, Rout // end: ld := ppc64.ALDAR st := ppc64.ASTDCCC @@ -406,20 +380,26 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { r1 := v.Args[1].Reg() r2 := v.Args[2].Reg() out := v.Reg0() + // Initialize return value to false + p := s.Prog(ppc64.AMOVD) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 0 + p.To.Type = obj.TYPE_REG + p.To.Reg = out // LWSYNC - Assuming shared data not write-through-required nor // caching-inhibited. See Appendix B.2.2.2 in the ISA 2.07b. plwsync1 := s.Prog(ppc64.ALWSYNC) plwsync1.To.Type = obj.TYPE_NONE // LDAR or LWAR - p := s.Prog(ld) - p.From.Type = obj.TYPE_MEM - p.From.Reg = r0 - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REGTMP + p0 := s.Prog(ld) + p0.From.Type = obj.TYPE_MEM + p0.From.Reg = r0 + p0.To.Type = obj.TYPE_REG + p0.To.Reg = ppc64.REGTMP // If it is a Compare-and-Swap-Release operation, set the EH field with // the release hint. if v.AuxInt == 0 { - p.SetFrom3Const(0) + p0.SetFrom3Const(0) } // CMP reg1,reg2 p1 := s.Prog(cmp) @@ -427,7 +407,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p1.From.Reg = r1 p1.To.Reg = ppc64.REGTMP p1.To.Type = obj.TYPE_REG - // BNE cas_fail + // BNE done with return value = false p2 := s.Prog(ppc64.ABNE) p2.To.Type = obj.TYPE_BRANCH // STDCCC or STWCCC @@ -439,7 +419,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // BNE retry p4 := s.Prog(ppc64.ABNE) p4.To.Type = obj.TYPE_BRANCH - p4.To.SetTarget(p) + p4.To.SetTarget(p0) // LWSYNC - Assuming shared data not write-through-required nor // caching-inhibited. See Appendix B.2.1.1 in the ISA 2.07b. // If the operation is a CAS-Release, then synchronization is not necessary. @@ -447,25 +427,19 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { plwsync2 := s.Prog(ppc64.ALWSYNC) plwsync2.To.Type = obj.TYPE_NONE } - // return true + // return value true p5 := s.Prog(ppc64.AMOVD) p5.From.Type = obj.TYPE_CONST p5.From.Offset = 1 p5.To.Type = obj.TYPE_REG p5.To.Reg = out - // BR done - p6 := s.Prog(obj.AJMP) - p6.To.Type = obj.TYPE_BRANCH - // return false - p7 := s.Prog(ppc64.AMOVD) - p7.From.Type = obj.TYPE_CONST - p7.From.Offset = 0 - p7.To.Type = obj.TYPE_REG - p7.To.Reg = out - p2.To.SetTarget(p7) // done (label) - p8 := s.Prog(obj.ANOP) - p6.To.SetTarget(p8) + p6 := s.Prog(obj.ANOP) + p2.To.SetTarget(p6) + + case ssa.OpPPC64LoweredPubBarrier: + // LWSYNC + s.Prog(v.Op.Asm()) case ssa.OpPPC64LoweredGetClosurePtr: // Closure pointer is R11 (already) @@ -475,7 +449,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(ppc64.AMOVD) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -502,6 +476,20 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Reg = v.Args[0].Reg() ssagen.AddrAuto(&p.To, v) + case ssa.OpArgIntReg, ssa.OpArgFloatReg: + // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill + // The loop only runs once. + for _, a := range v.Block.Func.RegArgs { + // Pass the spill/unspill information along to the assembler, offset by size of + // the saved LR slot. + addr := ssagen.SpillSlotAddr(a, ppc64.REGSP, base.Ctxt.Arch.FixedFrameSize) + s.FuncInfo().AddSpill( + obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)}) + } + v.Block.Func.RegArgs = nil + + ssagen.CheckArgReg(v) + case ssa.OpPPC64DIVD: // For now, // @@ -708,7 +696,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() - case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst, + case ssa.OpPPC64ADDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst, ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst, ssa.OpPPC64EXTSWSLconst, ssa.OpPPC64MULLWconst, ssa.OpPPC64MULLDconst: p := s.Prog(v.Op.Asm()) @@ -718,6 +706,41 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpPPC64ADDC, ssa.OpPPC64ADDE, ssa.OpPPC64SUBC, ssa.OpPPC64SUBE: + r := v.Reg0() // CA is the first, implied argument. + r1 := v.Args[0].Reg() + r2 := v.Args[1].Reg() + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = r2 + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + + case ssa.OpPPC64ADDZEzero, ssa.OpPPC64SUBZEzero: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = ppc64.REG_R0 + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + + case ssa.OpPPC64ADDCconst: + p := s.Prog(v.Op.Asm()) + p.Reg = v.Args[0].Reg() + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.To.Type = obj.TYPE_REG + // Output is a pair, the second is the CA, which is implied. + p.To.Reg = v.Reg0() + + case ssa.OpPPC64SUBCconst: + p := s.Prog(v.Op.Asm()) + p.SetFrom3Const(v.AuxInt) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + case ssa.OpPPC64SUBFCconst: p := s.Prog(v.Op.Asm()) p.SetFrom3Const(v.AuxInt) @@ -732,7 +755,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_CONST p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REGTMP // discard result + // p.To.Reg = ppc64.REGTMP // discard result + p.To.Reg = v.Reg0() case ssa.OpPPC64MOVDaddr: switch v.Aux.(type) { @@ -801,7 +825,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpPPC64MOVDload, ssa.OpPPC64MOVWload: // MOVDload and MOVWload are DS form instructions that are restricted to - // offsets that are a multiple of 4. If the offset is not a multple of 4, + // offsets that are a multiple of 4. If the offset is not a multiple of 4, // then the address of the symbol to be loaded is computed (base + offset) // and used as the new base register and the offset field in the instruction // can be set to zero. @@ -823,7 +847,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // Special case for a rule combines the bytes of gostring. // The v alignment might seem OK, but we don't want to load it // using an offset because relocation comes later. - genAddr = strings.HasPrefix(fromAddr.Sym.Name, "go.string") || v.Type.Alignment()%4 != 0 || fromAddr.Offset%4 != 0 + genAddr = strings.HasPrefix(fromAddr.Sym.Name, "go:string") || v.Type.Alignment()%4 != 0 || fromAddr.Offset%4 != 0 default: genAddr = fromAddr.Offset%4 != 0 } @@ -886,6 +910,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpPPC64DCBT: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_MEM + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_CONST + p.To.Offset = v.AuxInt + case ssa.OpPPC64MOVWstorezero, ssa.OpPPC64MOVHstorezero, ssa.OpPPC64MOVBstorezero: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG @@ -897,7 +928,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpPPC64MOVDstore, ssa.OpPPC64MOVDstorezero: // MOVDstore and MOVDstorezero become DS form instructions that are restricted - // to offset values that are a multple of 4. If the offset field is not a + // to offset values that are a multiple of 4. If the offset field is not a // multiple of 4, then the full address of the store target is computed (base + // offset) and used as the new base register and the offset in the instruction // is set to 0. @@ -1086,7 +1117,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p = s.Prog(ppc64.ABC) p.From.Type = obj.TYPE_CONST p.From.Offset = ppc64.BO_BCTR - p.Reg = ppc64.REG_R0 + p.Reg = ppc64.REG_CR0LT p.To.Type = obj.TYPE_BRANCH p.To.SetTarget(top) } @@ -1286,7 +1317,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p = s.Prog(ppc64.ABC) p.From.Type = obj.TYPE_CONST p.From.Offset = ppc64.BO_BCTR - p.Reg = ppc64.REG_R0 + p.Reg = ppc64.REG_CR0LT p.To.Type = obj.TYPE_BRANCH p.To.SetTarget(top) } @@ -1465,7 +1496,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p = s.Prog(ppc64.ABC) p.From.Type = obj.TYPE_CONST p.From.Offset = ppc64.BO_BCTR - p.Reg = ppc64.REG_R0 + p.Reg = ppc64.REG_CR0LT p.To.Type = obj.TYPE_BRANCH p.To.SetTarget(top) @@ -1712,7 +1743,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p = s.Prog(ppc64.ABC) p.From.Type = obj.TYPE_CONST p.From.Offset = ppc64.BO_BCTR - p.Reg = ppc64.REG_R0 + p.Reg = ppc64.REG_CR0LT p.To.Type = obj.TYPE_BRANCH p.To.SetTarget(top) @@ -1829,6 +1860,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpPPC64CALLstatic: s.Call(v) + case ssa.OpPPC64CALLtail: + s.TailCall(v) + case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter: p := s.Prog(ppc64.AMOVD) p.From.Type = obj.TYPE_REG @@ -1841,9 +1875,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } pp := s.Call(v) - pp.To.Reg = ppc64.REG_LR - // Insert a hint this is not a subroutine return. + // Convert the call into a blrl with hint this is not a subroutine return. + // The full bclrl opcode must be specified when passing a hint. + pp.As = ppc64.ABCL + pp.From.Type = obj.TYPE_CONST + pp.From.Offset = ppc64.BO_ALWAYS + pp.Reg = ppc64.REG_CR0LT // The preferred value if BI is ignored. + pp.To.Reg = ppc64.REG_LR pp.SetFrom3Const(1) if base.Ctxt.Flag_shared { @@ -1980,14 +2019,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.AJMP) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockPPC64EQ, ssa.BlockPPC64NE, ssa.BlockPPC64LT, ssa.BlockPPC64GE, @@ -2027,3 +2061,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { b.Fatalf("branch not implemented: %s", b.LongString()) } } + +func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p := s.Prog(loadByType(t)) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_AUTO + p.From.Sym = n.Linksym() + p.From.Offset = n.FrameOffset() + off + p.To.Type = obj.TYPE_REG + p.To.Reg = reg + return p +} + +func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) + p.To.Name = obj.NAME_PARAM + p.To.Sym = n.Linksym() + p.Pos = p.Pos.WithNotStmt() + return p +} diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go index 0707e0b61caf48..8f0c4e8bc3b328 100644 --- a/src/cmd/compile/internal/reflectdata/alg.go +++ b/src/cmd/compile/internal/reflectdata/alg.go @@ -6,10 +6,9 @@ package reflectdata import ( "fmt" - "math/bits" - "sort" "cmd/compile/internal/base" + "cmd/compile/internal/compare" "cmd/compile/internal/ir" "cmd/compile/internal/objw" "cmd/compile/internal/typecheck" @@ -17,43 +16,17 @@ import ( "cmd/internal/obj" ) -// isRegularMemory reports whether t can be compared/hashed as regular memory. -func isRegularMemory(t *types.Type) bool { - a, _ := types.AlgType(t) - return a == types.AMEM -} - -// eqCanPanic reports whether == on type t could panic (has an interface somewhere). -// t must be comparable. -func eqCanPanic(t *types.Type) bool { - switch t.Kind() { - default: - return false - case types.TINTER: - return true - case types.TARRAY: - return eqCanPanic(t.Elem()) - case types.TSTRUCT: - for _, f := range t.FieldSlice() { - if !f.Sym.IsBlank() && eqCanPanic(f.Type) { - return true - } - } - return false - } -} - // AlgType returns the fixed-width AMEMxx variants instead of the general // AMEM kind when possible. func AlgType(t *types.Type) types.AlgKind { a, _ := types.AlgType(t) if a == types.AMEM { - if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Width { + if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() { // For example, we can't treat [2]int16 as an int32 if int32s require // 4-byte alignment. See issue 46283. return a } - switch t.Width { + switch t.Size() { case 0: return types.AMEM0 case 1: @@ -110,7 +83,7 @@ func genhash(t *types.Type) *obj.LSym { // For other sizes of plain memory, we build a closure // that calls memhash_varlen. The size of the memory is // encoded in the first slot of the closure. - closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Width)) + closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Size())) if len(closure.P) > 0 { // already generated return closure } @@ -119,7 +92,7 @@ func genhash(t *types.Type) *obj.LSym { } ot := 0 ot = objw.SymPtr(closure, ot, memhashvarlen, 0) - ot = objw.Uintptr(closure, ot, uint64(t.Width)) // size encoded in closure + ot = objw.Uintptr(closure, ot, uint64(t.Size())) // size encoded in closure objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA) return closure case types.ASPECIAL: @@ -156,15 +129,14 @@ func genhash(t *types.Type) *obj.LSym { // func sym(p *T, h uintptr) uintptr args := []*ir.Field{ - ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)), - ir.NewField(base.Pos, typecheck.Lookup("h"), nil, types.Types[types.TUINTPTR]), + ir.NewField(base.Pos, typecheck.Lookup("p"), types.NewPtr(t)), + ir.NewField(base.Pos, typecheck.Lookup("h"), types.Types[types.TUINTPTR]), } - results := []*ir.Field{ir.NewField(base.Pos, nil, nil, types.Types[types.TUINTPTR])} - tfn := ir.NewFuncType(base.Pos, nil, args, results) + results := []*ir.Field{ir.NewField(base.Pos, nil, types.Types[types.TUINTPTR])} - fn := typecheck.DeclFunc(sym, tfn) - np := ir.AsNode(tfn.Type().Params().Field(0).Nname) - nh := ir.AsNode(tfn.Type().Params().Field(1).Nname) + fn := typecheck.DeclFunc(sym, nil, args, results) + np := ir.AsNode(fn.Type().Params().Field(0).Nname) + nh := ir.AsNode(fn.Type().Params().Field(1).Nname) switch t.Kind() { case types.TARRAY: @@ -206,7 +178,7 @@ func genhash(t *types.Type) *obj.LSym { } // Hash non-memory fields with appropriate hash function. - if !isRegularMemory(f.Type) { + if !compare.IsRegularMemory(f.Type) { hashel := hashfor(f.Type) call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil) nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages? @@ -219,7 +191,7 @@ func genhash(t *types.Type) *obj.LSym { } // Otherwise, hash a maximal length run of raw memory. - size, next := memrun(t, i) + size, next := compare.Memrun(t, i) // h = hashel(&p.first, size, h) hashel := hashmem(f.Type) @@ -354,7 +326,7 @@ func geneq(t *types.Type) *obj.LSym { case types.AMEM: // make equality closure. The size of the type // is encoded in the closure. - closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Width)) + closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Size())) if len(closure.P) != 0 { return closure } @@ -363,7 +335,7 @@ func geneq(t *types.Type) *obj.LSym { } ot := 0 ot = objw.SymPtr(closure, ot, memequalvarlen, 0) - ot = objw.Uintptr(closure, ot, uint64(t.Width)) + ot = objw.Uintptr(closure, ot, uint64(t.Size())) objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA) return closure case types.ASPECIAL: @@ -385,14 +357,13 @@ func geneq(t *types.Type) *obj.LSym { typecheck.DeclContext = ir.PEXTERN // func sym(p, q *T) bool - tfn := ir.NewFuncType(base.Pos, nil, - []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("p"), nil, types.NewPtr(t)), ir.NewField(base.Pos, typecheck.Lookup("q"), nil, types.NewPtr(t))}, - []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("r"), nil, types.Types[types.TBOOL])}) - - fn := typecheck.DeclFunc(sym, tfn) - np := ir.AsNode(tfn.Type().Params().Field(0).Nname) - nq := ir.AsNode(tfn.Type().Params().Field(1).Nname) - nr := ir.AsNode(tfn.Type().Results().Field(0).Nname) + fn := typecheck.DeclFunc(sym, nil, + []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("p"), types.NewPtr(t)), ir.NewField(base.Pos, typecheck.Lookup("q"), types.NewPtr(t))}, + []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("r"), types.Types[types.TBOOL])}, + ) + np := ir.AsNode(fn.Type().Params().Field(0).Nname) + nq := ir.AsNode(fn.Type().Params().Field(1).Nname) + nr := ir.AsNode(fn.Type().Results().Field(0).Nname) // Label to jump to if an equality test fails. neq := typecheck.AutoLabel(".neq") @@ -412,22 +383,25 @@ func geneq(t *types.Type) *obj.LSym { // // if eq(p[0], q[0]) && eq(p[1], q[1]) && ... { // } else { - // return + // goto neq // } // // And so on. // // Otherwise it generates: // - // for i := 0; i < nelem; i++ { - // if eq(p[i], q[i]) { + // iterateTo := nelem/unroll*unroll + // for i := 0; i < iterateTo; i += unroll { + // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) { // } else { // goto neq // } // } + // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... { + // } else { + // goto neq + // } // - // TODO(josharian): consider doing some loop unrolling - // for larger nelem as well, processing a few elements at a time in a loop. checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) { // checkIdx generates a node to check for equality at index i. checkIdx := func(i ir.Node) ir.Node { @@ -442,54 +416,77 @@ func geneq(t *types.Type) *obj.LSym { return eq(pi, qi) } - if nelem <= unroll { - if last { - // Do last comparison in a different manner. - nelem-- - } - // Generate a series of checks. - for i := int64(0); i < nelem; i++ { - // if check {} else { goto neq } - nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(i)), nil, nil) - nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) - fn.Body.Append(nif) - } - if last { - fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem)))) - } - } else { - // Generate a for loop. - // for i := 0; i < nelem; i++ + iterations := nelem / unroll + iterateTo := iterations * unroll + // If a loop is iterated only once, there shouldn't be any loop at all. + if iterations == 1 { + iterateTo = 0 + } + + if iterateTo > 0 { + // Generate an unrolled for loop. + // for i := 0; i < nelem/unroll*unroll; i += unroll i := typecheck.Temp(types.Types[types.TINT]) init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0)) - cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(nelem)) - post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1))) - loop := ir.NewForStmt(base.Pos, nil, cond, post, nil) + cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(iterateTo)) + loop := ir.NewForStmt(base.Pos, nil, cond, nil, nil) loop.PtrInit().Append(init) - // if eq(pi, qi) {} else { goto neq } - nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil) - nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) - loop.Body.Append(nif) + + // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) { + // } else { + // goto neq + // } + for j := int64(0); j < unroll; j++ { + // if check {} else { goto neq } + nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil) + nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) + loop.Body.Append(nif) + post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1))) + loop.Body.Append(post) + } + fn.Body.Append(loop) - if last { - fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true))) + + if nelem == iterateTo { + if last { + fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true))) + } + return } } + + // Generate remaining checks, if nelem is not a multiple of unroll. + if last { + // Do last comparison in a different manner. + nelem-- + } + // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... { + // } else { + // goto neq + // } + for j := iterateTo; j < nelem; j++ { + // if check {} else { goto neq } + nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(j)), nil, nil) + nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) + fn.Body.Append(nif) + } + if last { + fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem)))) + } } switch t.Elem().Kind() { case types.TSTRING: // Do two loops. First, check that all the lengths match (cheap). // Second, check that all the contents match (expensive). - // TODO: when the array size is small, unroll the length match checks. checkAll(3, false, func(pi, qi ir.Node) ir.Node { // Compare lengths. - eqlen, _ := EqString(pi, qi) + eqlen, _ := compare.EqString(pi, qi) return eqlen }) checkAll(1, true, func(pi, qi ir.Node) ir.Node { // Compare contents. - _, eqmem := EqString(pi, qi) + _, eqmem := compare.EqString(pi, qi) return eqmem }) case types.TFLOAT32, types.TFLOAT64: @@ -506,81 +503,7 @@ func geneq(t *types.Type) *obj.LSym { } case types.TSTRUCT: - // Build a list of conditions to satisfy. - // The conditions are a list-of-lists. Conditions are reorderable - // within each inner list. The outer lists must be evaluated in order. - var conds [][]ir.Node - conds = append(conds, []ir.Node{}) - and := func(n ir.Node) { - i := len(conds) - 1 - conds[i] = append(conds[i], n) - } - - // Walk the struct using memequal for runs of AMEM - // and calling specific equality tests for the others. - for i, fields := 0, t.FieldSlice(); i < len(fields); { - f := fields[i] - - // Skip blank-named fields. - if f.Sym.IsBlank() { - i++ - continue - } - - // Compare non-memory fields with field equality. - if !isRegularMemory(f.Type) { - if eqCanPanic(f.Type) { - // Enforce ordering by starting a new set of reorderable conditions. - conds = append(conds, []ir.Node{}) - } - p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) - q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym) - switch { - case f.Type.IsString(): - eqlen, eqmem := EqString(p, q) - and(eqlen) - and(eqmem) - default: - and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q)) - } - if eqCanPanic(f.Type) { - // Also enforce ordering after something that can panic. - conds = append(conds, []ir.Node{}) - } - i++ - continue - } - - // Find maximal length run of memory-only fields. - size, next := memrun(t, i) - - // TODO(rsc): All the calls to newname are wrong for - // cross-package unexported fields. - if s := fields[i:next]; len(s) <= 2 { - // Two or fewer fields: use plain field equality. - for _, f := range s { - and(eqfield(np, nq, f.Sym)) - } - } else { - // More than two fields: use memequal. - and(eqmem(np, nq, f.Sym, size)) - } - i = next - } - - // Sort conditions to put runtime calls last. - // Preserve the rest of the ordering. - var flatConds []ir.Node - for _, c := range conds { - isCall := func(n ir.Node) bool { - return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC - } - sort.SliceStable(c, func(i, j int) bool { - return !isCall(c[i]) && isCall(c[j]) - }) - flatConds = append(flatConds, c...) - } - + flatConds := compare.EqStruct(t, np, nq) if len(flatConds) == 0 { fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true))) } else { @@ -605,7 +528,7 @@ func geneq(t *types.Type) *obj.LSym { // return (or goto ret) fn.Body.Append(ir.NewLabelStmt(base.Pos, neq)) fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(false))) - if eqCanPanic(t) || anyCall(fn) { + if compare.EqCanPanic(t) || anyCall(fn) { // Epilogue is large, so share it with the equal case. fn.Body.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, ret)) } else { @@ -654,145 +577,6 @@ func anyCall(fn *ir.Func) bool { }) } -// eqfield returns the node -// p.field == q.field -func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node { - nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field) - ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field) - ne := ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny) - return ne -} - -// EqString returns the nodes -// len(s) == len(t) -// and -// memequal(s.ptr, t.ptr, len(s)) -// which can be used to construct string equality comparison. -// eqlen must be evaluated before eqmem, and shortcircuiting is required. -func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) { - s = typecheck.Conv(s, types.Types[types.TSTRING]) - t = typecheck.Conv(t, types.Types[types.TSTRING]) - sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) - tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t) - slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR]) - tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR]) - - fn := typecheck.LookupRuntime("memequal") - fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8]) - call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{sptr, tptr, ir.Copy(slen)}) - typecheck.Call(call) - - cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen) - cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) - cmp.SetType(types.Types[types.TBOOL]) - return cmp, call -} - -// EqInterface returns the nodes -// s.tab == t.tab (or s.typ == t.typ, as appropriate) -// and -// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate) -// which can be used to construct interface equality comparison. -// eqtab must be evaluated before eqdata, and shortcircuiting is required. -func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) { - if !types.Identical(s.Type(), t.Type()) { - base.Fatalf("EqInterface %v %v", s.Type(), t.Type()) - } - // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool) - // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool) - var fn ir.Node - if s.Type().IsEmptyInterface() { - fn = typecheck.LookupRuntime("efaceeq") - } else { - fn = typecheck.LookupRuntime("ifaceeq") - } - - stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s) - ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t) - sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s) - tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t) - sdata.SetType(types.Types[types.TUNSAFEPTR]) - tdata.SetType(types.Types[types.TUNSAFEPTR]) - sdata.SetTypecheck(1) - tdata.SetTypecheck(1) - - call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{stab, sdata, tdata}) - typecheck.Call(call) - - cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab) - cmp = typecheck.Expr(cmp).(*ir.BinaryExpr) - cmp.SetType(types.Types[types.TBOOL]) - return cmp, call -} - -// eqmem returns the node -// memequal(&p.field, &q.field [, size]) -func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node { - nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field))) - ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field))) - - fn, needsize := eqmemfunc(size, nx.Type().Elem()) - call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) - call.Args.Append(nx) - call.Args.Append(ny) - if needsize { - call.Args.Append(ir.NewInt(size)) - } - - return call -} - -func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) { - switch size { - default: - fn = typecheck.LookupRuntime("memequal") - needsize = true - case 1, 2, 4, 8, 16: - buf := fmt.Sprintf("memequal%d", int(size)*8) - fn = typecheck.LookupRuntime(buf) - } - - fn = typecheck.SubstArgTypes(fn, t, t) - return fn, needsize -} - -// memrun finds runs of struct fields for which memory-only algs are appropriate. -// t is the parent struct type, and start is the field index at which to start the run. -// size is the length in bytes of the memory included in the run. -// next is the index just after the end of the memory run. -func memrun(t *types.Type, start int) (size int64, next int) { - next = start - for { - next++ - if next == t.NumFields() { - break - } - // Stop run after a padded field. - if types.IsPaddedField(t, next-1) { - break - } - // Also, stop before a blank or non-memory field. - if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) { - break - } - // For issue 46283, don't combine fields if the resulting load would - // require a larger alignment than the component fields. - if base.Ctxt.Arch.Alignment > 1 { - align := t.Alignment() - if off := t.Field(start).Offset; off&(align-1) != 0 { - // Offset is less aligned than the containing type. - // Use offset to determine alignment. - align = 1 << uint(bits.TrailingZeros64(uint64(off))) - } - size := t.Field(next).End() - t.Field(start).Offset - if size > align { - break - } - } - } - return t.Field(next-1).End() - t.Field(start).Offset, next -} - func hashmem(t *types.Type) ir.Node { sym := ir.Pkgs.Runtime.Lookup("memhash") diff --git a/src/cmd/compile/internal/reflectdata/alg_test.go b/src/cmd/compile/internal/reflectdata/alg_test.go new file mode 100644 index 00000000000000..a1fc8c590cb309 --- /dev/null +++ b/src/cmd/compile/internal/reflectdata/alg_test.go @@ -0,0 +1,95 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflectdata_test + +import "testing" + +func BenchmarkEqArrayOfStrings5(b *testing.B) { + var a [5]string + var c [5]string + + for i := 0; i < 5; i++ { + a[i] = "aaaa" + c[i] = "cccc" + } + + for j := 0; j < b.N; j++ { + _ = a == c + } +} + +func BenchmarkEqArrayOfStrings64(b *testing.B) { + var a [64]string + var c [64]string + + for i := 0; i < 64; i++ { + a[i] = "aaaa" + c[i] = "cccc" + } + + for j := 0; j < b.N; j++ { + _ = a == c + } +} + +func BenchmarkEqArrayOfStrings1024(b *testing.B) { + var a [1024]string + var c [1024]string + + for i := 0; i < 1024; i++ { + a[i] = "aaaa" + c[i] = "cccc" + } + + for j := 0; j < b.N; j++ { + _ = a == c + } +} + +func BenchmarkEqArrayOfFloats5(b *testing.B) { + var a [5]float32 + var c [5]float32 + + for i := 0; i < b.N; i++ { + _ = a == c + } +} + +func BenchmarkEqArrayOfFloats64(b *testing.B) { + var a [64]float32 + var c [64]float32 + + for i := 0; i < b.N; i++ { + _ = a == c + } +} + +func BenchmarkEqArrayOfFloats1024(b *testing.B) { + var a [1024]float32 + var c [1024]float32 + + for i := 0; i < b.N; i++ { + _ = a == c + } +} + +const size = 16 + +type T1 struct { + a [size]byte +} + +func BenchmarkEqStruct(b *testing.B) { + x, y := T1{}, T1{} + x.a = [size]byte{1, 2, 3, 4, 5, 6, 7, 8} + y.a = [size]byte{2, 3, 4, 5, 6, 7, 8, 9} + + for i := 0; i < b.N; i++ { + f := x == y + if f { + println("hello") + } + } +} diff --git a/src/cmd/compile/internal/reflectdata/helpers.go b/src/cmd/compile/internal/reflectdata/helpers.go new file mode 100644 index 00000000000000..99461cff52bacd --- /dev/null +++ b/src/cmd/compile/internal/reflectdata/helpers.go @@ -0,0 +1,226 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflectdata + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +func hasRType(n, rtype ir.Node, fieldName string) bool { + if rtype != nil { + return true + } + + // We make an exception for `init`, because we still depend on + // pkginit for sorting package initialization statements, and it + // gets confused by implicit conversions. Also, because + // package-scope statements can never be generic, so they'll never + // require dictionary lookups. + if base.Debug.Unified != 0 && ir.CurFunc.Nname.Sym().Name != "init" { + ir.Dump("CurFunc", ir.CurFunc) + base.FatalfAt(n.Pos(), "missing %s in %v: %+v", fieldName, ir.CurFunc, n) + } + + return false +} + +// assertOp asserts that n is an op. +func assertOp(n ir.Node, op ir.Op) { + base.AssertfAt(n.Op() == op, n.Pos(), "want %v, have %v", op, n) +} + +// assertOp2 asserts that n is an op1 or op2. +func assertOp2(n ir.Node, op1, op2 ir.Op) { + base.AssertfAt(n.Op() == op1 || n.Op() == op2, n.Pos(), "want %v or %v, have %v", op1, op2, n) +} + +// kindRType asserts that typ has the given kind, and returns an +// expression that yields the *runtime._type value representing typ. +func kindRType(pos src.XPos, typ *types.Type, k types.Kind) ir.Node { + base.AssertfAt(typ.Kind() == k, pos, "want %v type, have %v", k, typ) + return TypePtrAt(pos, typ) +} + +// mapRType asserts that typ is a map type, and returns an expression +// that yields the *runtime._type value representing typ. +func mapRType(pos src.XPos, typ *types.Type) ir.Node { + return kindRType(pos, typ, types.TMAP) +} + +// chanRType asserts that typ is a map type, and returns an expression +// that yields the *runtime._type value representing typ. +func chanRType(pos src.XPos, typ *types.Type) ir.Node { + return kindRType(pos, typ, types.TCHAN) +} + +// sliceElemRType asserts that typ is a slice type, and returns an +// expression that yields the *runtime._type value representing typ's +// element type. +func sliceElemRType(pos src.XPos, typ *types.Type) ir.Node { + base.AssertfAt(typ.IsSlice(), pos, "want slice type, have %v", typ) + return TypePtrAt(pos, typ.Elem()) +} + +// concreteRType asserts that typ is not an interface type, and +// returns an expression that yields the *runtime._type value +// representing typ. +func concreteRType(pos src.XPos, typ *types.Type) ir.Node { + base.AssertfAt(!typ.IsInterface(), pos, "want non-interface type, have %v", typ) + return TypePtrAt(pos, typ) +} + +// AppendElemRType asserts that n is an "append" operation, and +// returns an expression that yields the *runtime._type value +// representing the result slice type's element type. +func AppendElemRType(pos src.XPos, n *ir.CallExpr) ir.Node { + assertOp(n, ir.OAPPEND) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return sliceElemRType(pos, n.Type()) +} + +// CompareRType asserts that n is a comparison (== or !=) operation +// between expressions of interface and non-interface type, and +// returns an expression that yields the *runtime._type value +// representing the non-interface type. +func CompareRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp2(n, ir.OEQ, ir.ONE) + base.AssertfAt(n.X.Type().IsInterface() != n.Y.Type().IsInterface(), n.Pos(), "expect mixed interface and non-interface, have %L and %L", n.X, n.Y) + if hasRType(n, n.RType, "RType") { + return n.RType + } + typ := n.X.Type() + if typ.IsInterface() { + typ = n.Y.Type() + } + return concreteRType(pos, typ) +} + +// ConvIfaceTypeWord asserts that n is conversion to interface type, +// and returns an expression that yields the *runtime._type or +// *runtime.itab value necessary for implementing the conversion. +// +// - *runtime._type for the destination type, for I2I conversions +// - *runtime.itab, for T2I conversions +// - *runtime._type for the source type, for T2E conversions +func ConvIfaceTypeWord(pos src.XPos, n *ir.ConvExpr) ir.Node { + assertOp(n, ir.OCONVIFACE) + src, dst := n.X.Type(), n.Type() + base.AssertfAt(dst.IsInterface(), n.Pos(), "want interface type, have %L", n) + if hasRType(n, n.TypeWord, "TypeWord") { + return n.TypeWord + } + if dst.IsEmptyInterface() { + return concreteRType(pos, src) // direct eface construction + } + if !src.IsInterface() { + return ITabAddrAt(pos, src, dst) // direct iface construction + } + return TypePtrAt(pos, dst) // convI2I +} + +// ConvIfaceSrcRType asserts that n is a conversion from +// non-interface type to interface type (or OCONVIDATA operation), and +// returns an expression that yields the *runtime._type for copying +// the convertee value to the heap. +func ConvIfaceSrcRType(pos src.XPos, n *ir.ConvExpr) ir.Node { + assertOp2(n, ir.OCONVIFACE, ir.OCONVIDATA) + if hasRType(n, n.SrcRType, "SrcRType") { + return n.SrcRType + } + return concreteRType(pos, n.X.Type()) +} + +// CopyElemRType asserts that n is a "copy" operation, and returns an +// expression that yields the *runtime._type value representing the +// destination slice type's element type. +func CopyElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp(n, ir.OCOPY) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return sliceElemRType(pos, n.X.Type()) +} + +// DeleteMapRType asserts that n is a "delete" operation, and returns +// an expression that yields the *runtime._type value representing the +// map type. +func DeleteMapRType(pos src.XPos, n *ir.CallExpr) ir.Node { + assertOp(n, ir.ODELETE) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return mapRType(pos, n.Args[0].Type()) +} + +// IndexMapRType asserts that n is a map index operation, and returns +// an expression that yields the *runtime._type value representing the +// map type. +func IndexMapRType(pos src.XPos, n *ir.IndexExpr) ir.Node { + assertOp(n, ir.OINDEXMAP) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return mapRType(pos, n.X.Type()) +} + +// MakeChanRType asserts that n is a "make" operation for a channel +// type, and returns an expression that yields the *runtime._type +// value representing that channel type. +func MakeChanRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp(n, ir.OMAKECHAN) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return chanRType(pos, n.Type()) +} + +// MakeMapRType asserts that n is a "make" operation for a map type, +// and returns an expression that yields the *runtime._type value +// representing that map type. +func MakeMapRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp(n, ir.OMAKEMAP) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return mapRType(pos, n.Type()) +} + +// MakeSliceElemRType asserts that n is a "make" operation for a slice +// type, and returns an expression that yields the *runtime._type +// value representing that slice type's element type. +func MakeSliceElemRType(pos src.XPos, n *ir.MakeExpr) ir.Node { + assertOp2(n, ir.OMAKESLICE, ir.OMAKESLICECOPY) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return sliceElemRType(pos, n.Type()) +} + +// RangeMapRType asserts that n is a "range" loop over a map value, +// and returns an expression that yields the *runtime._type value +// representing that map type. +func RangeMapRType(pos src.XPos, n *ir.RangeStmt) ir.Node { + assertOp(n, ir.ORANGE) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return mapRType(pos, n.X.Type()) +} + +// UnsafeSliceElemRType asserts that n is an "unsafe.Slice" operation, +// and returns an expression that yields the *runtime._type value +// representing the result slice type's element type. +func UnsafeSliceElemRType(pos src.XPos, n *ir.BinaryExpr) ir.Node { + assertOp(n, ir.OUNSAFESLICE) + if hasRType(n, n.RType, "RType") { + return n.RType + } + return sliceElemRType(pos, n.Type()) +} diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index eb9a8a6c9bc73a..0f0c4051672a04 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -7,7 +7,6 @@ package reflectdata import ( "encoding/binary" "fmt" - "internal/buildcfg" "os" "sort" "strings" @@ -15,6 +14,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/bitvec" + "cmd/compile/internal/compare" "cmd/compile/internal/escape" "cmd/compile/internal/inline" "cmd/compile/internal/ir" @@ -28,35 +28,27 @@ import ( "cmd/internal/src" ) -type itabEntry struct { - t, itype *types.Type - lsym *obj.LSym // symbol of the itab itself - - // symbols of each method in - // the itab, sorted by byte offset; - // filled in by CompileITabs - entries []*obj.LSym -} - type ptabEntry struct { s *types.Sym t *types.Type } -func CountTabs() (numPTabs, numITabs int) { - return len(ptabs), len(itabs) +func CountPTabs() int { + return len(ptabs) } // runtime interface and reflection data structures var ( - signatmu sync.Mutex // protects signatset and signatslice - signatset = make(map[*types.Type]struct{}) - signatslice []*types.Type + // protects signatset and signatslice + signatmu sync.Mutex + // Tracking which types need runtime type descriptor + signatset = make(map[*types.Type]struct{}) + // Queue of types wait to be generated runtime type descriptor + signatslice []typeAndStr gcsymmu sync.Mutex // protects gcsymset and gcsymslice gcsymset = make(map[*types.Type]struct{}) - itabs []itabEntry ptabs []*ir.Name ) @@ -105,10 +97,10 @@ func MapBucketType(t *types.Type) *types.Type { elemtype := t.Elem() types.CalcSize(keytype) types.CalcSize(elemtype) - if keytype.Width > MAXKEYSIZE { + if keytype.Size() > MAXKEYSIZE { keytype = types.NewPtr(keytype) } - if elemtype.Width > MAXELEMSIZE { + if elemtype.Size() > MAXELEMSIZE { elemtype = types.NewPtr(elemtype) } @@ -153,46 +145,46 @@ func MapBucketType(t *types.Type) *types.Type { if BUCKETSIZE < 8 { base.Fatalf("bucket size too small for proper alignment") } - if keytype.Align > BUCKETSIZE { + if uint8(keytype.Alignment()) > BUCKETSIZE { base.Fatalf("key align too big for %v", t) } - if elemtype.Align > BUCKETSIZE { + if uint8(elemtype.Alignment()) > BUCKETSIZE { base.Fatalf("elem align too big for %v", t) } - if keytype.Width > MAXKEYSIZE { + if keytype.Size() > MAXKEYSIZE { base.Fatalf("key size to large for %v", t) } - if elemtype.Width > MAXELEMSIZE { + if elemtype.Size() > MAXELEMSIZE { base.Fatalf("elem size to large for %v", t) } - if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() { + if t.Key().Size() > MAXKEYSIZE && !keytype.IsPtr() { base.Fatalf("key indirect incorrect for %v", t) } - if t.Elem().Width > MAXELEMSIZE && !elemtype.IsPtr() { + if t.Elem().Size() > MAXELEMSIZE && !elemtype.IsPtr() { base.Fatalf("elem indirect incorrect for %v", t) } - if keytype.Width%int64(keytype.Align) != 0 { + if keytype.Size()%keytype.Alignment() != 0 { base.Fatalf("key size not a multiple of key align for %v", t) } - if elemtype.Width%int64(elemtype.Align) != 0 { + if elemtype.Size()%elemtype.Alignment() != 0 { base.Fatalf("elem size not a multiple of elem align for %v", t) } - if bucket.Align%keytype.Align != 0 { + if uint8(bucket.Alignment())%uint8(keytype.Alignment()) != 0 { base.Fatalf("bucket align not multiple of key align %v", t) } - if bucket.Align%elemtype.Align != 0 { + if uint8(bucket.Alignment())%uint8(elemtype.Alignment()) != 0 { base.Fatalf("bucket align not multiple of elem align %v", t) } - if keys.Offset%int64(keytype.Align) != 0 { + if keys.Offset%keytype.Alignment() != 0 { base.Fatalf("bad alignment of keys in bmap for %v", t) } - if elems.Offset%int64(elemtype.Align) != 0 { + if elems.Offset%elemtype.Alignment() != 0 { base.Fatalf("bad alignment of elems in bmap for %v", t) } // Double-check that overflow field is final memory in struct, // with no padding at end. - if overflow.Offset != bucket.Width-int64(types.PtrSize) { + if overflow.Offset != bucket.Size()-int64(types.PtrSize) { base.Fatalf("bad offset of overflow in bmap for %v", t) } @@ -242,8 +234,8 @@ func MapType(t *types.Type) *types.Type { // The size of hmap should be 48 bytes on 64 bit // and 28 bytes on 32 bit platforms. - if size := int64(8 + 5*types.PtrSize); hmap.Width != size { - base.Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size) + if size := int64(8 + 5*types.PtrSize); hmap.Size() != size { + base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size) } t.MapType().Hmap = hmap @@ -302,8 +294,8 @@ func MapIterType(t *types.Type) *types.Type { hiter := types.NewStruct(types.NoPkg, fields) hiter.SetNoalg(true) types.CalcSize(hiter) - if hiter.Width != int64(12*types.PtrSize) { - base.Fatalf("hash_iter size not correct %d %d", hiter.Width, 12*types.PtrSize) + if hiter.Size() != int64(12*types.PtrSize) { + base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 12*types.PtrSize) } t.MapType().Hiter = hiter hiter.StructType().Map = t @@ -313,6 +305,10 @@ func MapIterType(t *types.Type) *types.Type { // methods returns the methods of the non-interface type t, sorted by name. // Generates stub functions as needed. func methods(t *types.Type) []*typeSig { + if t.HasShape() { + // Shape types have no methods. + return nil + } // method type mt := types.ReceiverBaseType(t) @@ -321,13 +317,6 @@ func methods(t *types.Type) []*typeSig { } typecheck.CalcMethods(mt) - // type stored in interface word - it := t - - if !types.IsDirectIface(it) { - it = types.NewPtr(t) - } - // make list of methods for t, // generating code if necessary. var ms []*typeSig @@ -341,7 +330,11 @@ func methods(t *types.Type) []*typeSig { if f.Type.Recv() == nil { base.Fatalf("receiver with no type on %v method %v %v", mt, f.Sym, f) } - if f.Nointerface() { + if f.Nointerface() && !t.IsFullyInstantiated() { + // Skip creating method wrappers if f is nointerface. But, if + // t is an instantiated type, we still have to call + // methodWrapper, because methodWrapper generates the actual + // generic method on the type as well. continue } @@ -355,11 +348,16 @@ func methods(t *types.Type) []*typeSig { sig := &typeSig{ name: f.Sym, - isym: methodWrapper(it, f), - tsym: methodWrapper(t, f), + isym: methodWrapper(t, f, true), + tsym: methodWrapper(t, f, false), type_: typecheck.NewMethodType(f.Type, t), mtype: typecheck.NewMethodType(f.Type, nil), } + if f.Nointerface() { + // In the case of a nointerface method on an instantiated + // type, don't actually append the typeSig. + continue + } ms = append(ms, sig) } @@ -394,7 +392,7 @@ func imethods(t *types.Type) []*typeSig { // IfaceType.Method is not in the reflect data. // Generate the method body, so that compiled // code can refer to it. - methodWrapper(t, f) + methodWrapper(t, f, false) } return methods @@ -412,14 +410,8 @@ func dimportpath(p *types.Pkg) { return } - str := p.Path - if p == types.LocalPkg { - // Note: myimportpath != "", or else dgopkgpath won't call dimportpath. - str = base.Ctxt.Pkgpath - } - - s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".") - ot := dnameData(s, 0, str, "", nil, false) + s := base.Ctxt.Lookup("type:.importpath." + p.Prefix + ".") + ot := dnameData(s, 0, p.Path, "", nil, false, false) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) p.Pathsym = s @@ -433,10 +425,10 @@ func dgopkgpath(s *obj.LSym, ot int, pkg *types.Pkg) int { if pkg == types.LocalPkg && base.Ctxt.Pkgpath == "" { // If we don't know the full import path of the package being compiled // (i.e. -p was not passed on the compiler command line), emit a reference to - // type..importpath.""., which the linker will rewrite using the correct import path. + // type:.importpath.""., which the linker will rewrite using the correct import path. // Every package that imports this one directly defines the symbol. // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ. - ns := base.Ctxt.Lookup(`type..importpath."".`) + ns := base.Ctxt.Lookup(`type:.importpath."".`) return objw.SymPtr(s, ot, ns, 0) } @@ -452,10 +444,10 @@ func dgopkgpathOff(s *obj.LSym, ot int, pkg *types.Pkg) int { if pkg == types.LocalPkg && base.Ctxt.Pkgpath == "" { // If we don't know the full import path of the package being compiled // (i.e. -p was not passed on the compiler command line), emit a reference to - // type..importpath.""., which the linker will rewrite using the correct import path. + // type:.importpath.""., which the linker will rewrite using the correct import path. // Every package that imports this one directly defines the symbol. // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ. - ns := base.Ctxt.Lookup(`type..importpath."".`) + ns := base.Ctxt.Lookup(`type:.importpath."".`) return objw.SymPtrOff(s, ot, ns) } @@ -468,12 +460,12 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int { if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg { base.Fatalf("package mismatch for %v", ft.Sym) } - nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name)) + nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name), ft.Embedded != 0) return objw.SymPtr(lsym, ot, nsym, 0) } // dnameData writes the contents of a reflect.name into s at offset ot. -func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int { +func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported, embedded bool) int { if len(name) >= 1<<29 { base.Fatalf("name too long: %d %s...", len(name), name[:1024]) } @@ -498,6 +490,9 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b if pkg != nil { bits |= 1 << 2 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits copy(b[1:], nameLen[:nameLenLen]) @@ -520,12 +515,12 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b var dnameCount int // dname creates a reflect.name for a struct field or method. -func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { - // Write out data as "type.." to signal two things to the +func dname(name, tag string, pkg *types.Pkg, exported, embedded bool) *obj.LSym { + // Write out data as "type:." to signal two things to the // linker, first that when dynamically linking, the symbol // should be moved to a relro section, and second that the // contents should not be decoded as a type. - sname := "type..namedata." + sname := "type:.namedata." if pkg == nil { // In the common case, share data with other packages. if name == "" { @@ -545,11 +540,14 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount) dnameCount++ } + if embedded { + sname += ".embedded" + } s := base.Ctxt.Lookup(sname) if len(s.P) > 0 { return s } - ot := dnameData(s, 0, name, tag, pkg, exported) + ot := dnameData(s, 0, name, tag, pkg, exported, embedded) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) return s @@ -563,7 +561,7 @@ func dextratype(lsym *obj.LSym, ot int, t *types.Type, dataAdd int) int { if t.Sym() == nil && len(m) == 0 { return ot } - noff := int(types.Rnd(int64(ot), int64(types.PtrSize))) + noff := int(types.RoundUp(int64(ot), int64(types.PtrSize))) if noff != ot { base.Fatalf("unexpected alignment in dextratype for %v", t) } @@ -617,7 +615,7 @@ func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int { if !exported && a.name.Pkg != typePkg(t) { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = dmethodptrOff(lsym, ot, writeType(a.mtype)) @@ -669,10 +667,10 @@ var kinds = []int{ // tflag is documented in reflect/type.go. // // tflag values must be kept in sync with copies in: -// cmd/compile/internal/reflectdata/reflect.go -// cmd/link/internal/ld/decodesym.go -// reflect/type.go -// runtime/type.go +// - cmd/compile/internal/reflectdata/reflect.go +// - cmd/link/internal/ld/decodesym.go +// - reflect/type.go +// - runtime/type.go const ( tflagUncommon = 1 << 0 tflagExtraStar = 1 << 1 @@ -719,7 +717,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { // ptrToThis typeOff // } ot := 0 - ot = objw.Uintptr(lsym, ot, uint64(t.Width)) + ot = objw.Uintptr(lsym, ot, uint64(t.Size())) ot = objw.Uintptr(lsym, ot, uint64(ptrdata)) ot = objw.Uint32(lsym, ot, types.TypeHash(t)) @@ -730,12 +728,12 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { if t.Sym() != nil && t.Sym().Name != "" { tflag |= tflagNamed } - if isRegularMemory(t) { + if compare.IsRegularMemory(t) { tflag |= tflagRegularMemory } exported := false - p := t.LongString() + p := t.NameString() // If we're writing out type T, // we are very likely to write out type *T as well. // Use the string "*T"[1:] for "T", so that the two @@ -756,16 +754,16 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { ot = objw.Uint8(lsym, ot, tflag) // runtime (and common sense) expects alignment to be a power of two. - i := int(t.Align) + i := int(uint8(t.Alignment())) if i == 0 { i = 1 } if i&(i-1) != 0 { - base.Fatalf("invalid alignment %d for %v", t.Align, t) + base.Fatalf("invalid alignment %d for %v", uint8(t.Alignment()), t) } - ot = objw.Uint8(lsym, ot, t.Align) // align - ot = objw.Uint8(lsym, ot, t.Align) // fieldAlign + ot = objw.Uint8(lsym, ot, uint8(t.Alignment())) // align + ot = objw.Uint8(lsym, ot, uint8(t.Alignment())) // fieldAlign i = kinds[t.Kind()] if types.IsDirectIface(t) { @@ -782,7 +780,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { } ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata - nsym := dname(p, "", nil, exported) + nsym := dname(p, "", nil, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) // str // ptrToThis if sptr == nil { @@ -799,11 +797,11 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { // TrackSym returns the symbol for tracking use of field/method f, assumed // to be a member of struct/interface type t. func TrackSym(t *types.Type, f *types.Field) *obj.LSym { - return base.PkgLinksym("go.track", t.ShortString()+"."+f.Sym.Name, obj.ABI0) + return base.PkgLinksym("go:track", t.LinkString()+"."+f.Sym.Name, obj.ABI0) } func TypeSymPrefix(prefix string, t *types.Type) *types.Sym { - p := prefix + "." + t.ShortString() + p := prefix + "." + t.LinkString() s := types.TypeSymLookup(p) // This function is for looking up type-related generated functions @@ -843,23 +841,51 @@ func TypeLinksym(t *types.Type) *obj.LSym { return TypeSym(t).Linksym() } +// Deprecated: Use TypePtrAt instead. func TypePtr(t *types.Type) *ir.AddrExpr { - n := ir.NewLinksymExpr(base.Pos, TypeLinksym(t), types.Types[types.TUINT8]) - return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr) + return TypePtrAt(base.Pos, t) } -func ITabAddr(t, itype *types.Type) *ir.AddrExpr { - if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() { - base.Fatalf("ITabAddr(%v, %v)", t, itype) - } - s, existed := ir.Pkgs.Itab.LookupOK(t.ShortString() + "," + itype.ShortString()) +// TypePtrAt returns an expression that evaluates to the +// *runtime._type value for t. +func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr { + return typecheck.LinksymAddr(pos, TypeLinksym(t), types.Types[types.TUINT8]) +} + +// ITabLsym returns the LSym representing the itab for concrete type typ implementing +// interface iface. A dummy tab will be created in the unusual case where typ doesn't +// implement iface. Normally, this wouldn't happen, because the typechecker would +// have reported a compile-time error. This situation can only happen when the +// destination type of a type assert or a type in a type switch is parameterized, so +// it may sometimes, but not always, be a type that can't implement the specified +// interface. +func ITabLsym(typ, iface *types.Type) *obj.LSym { + s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString()) + lsym := s.Linksym() + if !existed { - itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()}) + writeITab(lsym, typ, iface, true) } + return lsym +} +// Deprecated: Use ITabAddrAt instead. +func ITabAddr(typ, iface *types.Type) *ir.AddrExpr { + return ITabAddrAt(base.Pos, typ, iface) +} + +// ITabAddrAt returns an expression that evaluates to the +// *runtime.itab value for concrete type typ implementing interface +// iface. +func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr { + s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString()) lsym := s.Linksym() - n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8]) - return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr) + + if !existed { + writeITab(lsym, typ, iface, false) + } + + return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8]) } // needkeyupdate reports whether map updates with t as a key @@ -914,11 +940,12 @@ func hashMightPanic(t *types.Type) bool { } } -// formalType replaces byte and rune aliases with real types. +// formalType replaces predeclared aliases with real types. // They've been separate internally to make error messages // better, but we have to merge them in the reflect tables. func formalType(t *types.Type) *types.Type { - if t == types.ByteType || t == types.RuneType { + switch t { + case types.AnyType, types.ByteType, types.RuneType: return types.Types[t.Kind()] } return t @@ -926,7 +953,7 @@ func formalType(t *types.Type) *types.Type { func writeType(t *types.Type) *obj.LSym { t = formalType(t) - if t.IsUntyped() { + if t.IsUntyped() || t.HasTParam() { base.Fatalf("writeType %v", t) } @@ -945,25 +972,22 @@ func writeType(t *types.Type) *obj.LSym { if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil { tbase = t.Elem() } - dupok := 0 - if tbase.Sym() == nil { - dupok = obj.DUPOK + if tbase.Kind() == types.TFORW { + base.Fatalf("unresolved defined type: %v", tbase) } - if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc - // named types from other files are defined only by those files - if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg { - if i := typecheck.BaseTypeIndex(t); i >= 0 { - lsym.Pkg = tbase.Sym().Pkg.Prefix - lsym.SymIdx = int32(i) - lsym.Set(obj.AttrIndexed, true) - } - return lsym - } - // TODO(mdempsky): Investigate whether this can happen. - if tbase.Kind() == types.TFORW { - return lsym + if !NeedEmit(tbase) { + if i := typecheck.BaseTypeIndex(t); i >= 0 { + lsym.Pkg = tbase.Sym().Pkg.Prefix + lsym.SymIdx = int32(i) + lsym.Set(obj.AttrIndexed, true) } + + // TODO(mdempsky): Investigate whether this still happens. + // If we know we don't need to emit code for a type, + // we should have a link-symbol index for it. + // See also TODO in NeedEmit. + return lsym } ot := 0 @@ -1066,7 +1090,7 @@ func writeType(t *types.Type) *obj.LSym { if !exported && a.name.Pkg != tpkg { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = objw.SymPtrOff(lsym, ot, writeType(a.type_)) @@ -1087,20 +1111,20 @@ func writeType(t *types.Type) *obj.LSym { var flags uint32 // Note: flags must match maptype accessors in ../../../../runtime/type.go // and maptype builder in ../../../../reflect/type.go:MapOf. - if t.Key().Width > MAXKEYSIZE { + if t.Key().Size() > MAXKEYSIZE { ot = objw.Uint8(lsym, ot, uint8(types.PtrSize)) flags |= 1 // indirect key } else { - ot = objw.Uint8(lsym, ot, uint8(t.Key().Width)) + ot = objw.Uint8(lsym, ot, uint8(t.Key().Size())) } - if t.Elem().Width > MAXELEMSIZE { + if t.Elem().Size() > MAXELEMSIZE { ot = objw.Uint8(lsym, ot, uint8(types.PtrSize)) flags |= 2 // indirect value } else { - ot = objw.Uint8(lsym, ot, uint8(t.Elem().Width)) + ot = objw.Uint8(lsym, ot, uint8(t.Elem().Size())) } - ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Width)) + ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Size())) if types.IsReflexive(t.Key()) { flags |= 4 // reflexive key } @@ -1172,17 +1196,19 @@ func writeType(t *types.Type) *obj.LSym { // ../../../../runtime/type.go:/structField ot = dnameField(lsym, ot, spkg, f) ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0) - offsetAnon := uint64(f.Offset) << 1 - if offsetAnon>>1 != uint64(f.Offset) { - base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name) - } - if f.Embedded != 0 { - offsetAnon |= 1 - } - ot = objw.Uintptr(lsym, ot, offsetAnon) + ot = objw.Uintptr(lsym, ot, uint64(f.Offset)) } } + // Note: DUPOK is required to ensure that we don't end up with more + // than one type descriptor for a given type, if the type descriptor + // can be defined in multiple packages, that is, unnamed types, + // instantiated types and shape types. + dupok := 0 + if tbase.Sym() == nil || tbase.IsFullyInstantiated() || tbase.HasShape() { + dupok = obj.DUPOK + } + ot = dextratypeData(lsym, ot, t) objw.Global(lsym, int32(ot), int16(dupok|obj.RODATA)) @@ -1226,108 +1252,25 @@ func InterfaceMethodOffset(ityp *types.Type, i int64) int64 { return int64(commonSize()+4*types.PtrSize+uncommonSize(ityp)) + i*8 } -// for each itabEntry, gather the methods on -// the concrete type that implement the interface -func CompileITabs() { - for i := range itabs { - tab := &itabs[i] - methods := genfun(tab.t, tab.itype) - if len(methods) == 0 { - continue - } - tab.entries = methods - } -} - -// for the given concrete type and interface -// type, return the (sorted) set of methods -// on the concrete type that implement the interface -func genfun(t, it *types.Type) []*obj.LSym { - if t == nil || it == nil { - return nil - } - sigs := imethods(it) - methods := methods(t) - out := make([]*obj.LSym, 0, len(sigs)) - // TODO(mdempsky): Short circuit before calling methods(t)? - // See discussion on CL 105039. - if len(sigs) == 0 { - return nil - } - - // both sigs and methods are sorted by name, - // so we can find the intersect in a single pass - for _, m := range methods { - if m.name == sigs[0].name { - out = append(out, m.isym) - sigs = sigs[1:] - if len(sigs) == 0 { - break - } - } - } - - if len(sigs) != 0 { - base.Fatalf("incomplete itab") - } - - return out -} - -// ITabSym uses the information gathered in -// CompileITabs to de-virtualize interface methods. -// Since this is called by the SSA backend, it shouldn't -// generate additional Nodes, Syms, etc. -func ITabSym(it *obj.LSym, offset int64) *obj.LSym { - var syms []*obj.LSym - if it == nil { - return nil - } - - for i := range itabs { - e := &itabs[i] - if e.lsym == it { - syms = e.entries - break - } - } - if syms == nil { - return nil - } - - // keep this arithmetic in sync with *itab layout - methodnum := int((offset - 2*int64(types.PtrSize) - 8) / int64(types.PtrSize)) - if methodnum >= len(syms) { - return nil - } - return syms[methodnum] -} - // NeedRuntimeType ensures that a runtime type descriptor is emitted for t. func NeedRuntimeType(t *types.Type) { if t.HasTParam() { - // Generic types don't have a runtime type descriptor (but will - // have a dictionary) + // Generic types don't really exist at run-time and have no runtime + // type descriptor. But we do write out shape types. return } if _, ok := signatset[t]; !ok { signatset[t] = struct{}{} - signatslice = append(signatslice, t) + signatslice = append(signatslice, typeAndStr{t: t, short: types.TypeSymName(t), regular: t.String()}) } } func WriteRuntimeTypes() { - // Process signatset. Use a loop, as writeType adds - // entries to signatset while it is being processed. - signats := make([]typeAndStr, len(signatslice)) + // Process signatslice. Use a loop, as writeType adds + // entries to signatslice while it is being processed. for len(signatslice) > 0 { - signats = signats[:0] - // Transfer entries to a slice and sort, for reproducible builds. - for _, t := range signatslice { - signats = append(signats, typeAndStr{t: t, short: types.TypeSymName(t), regular: t.String()}) - delete(signatset, t) - } - signatslice = signatslice[:0] + signats := signatslice + // Sort for reproducible builds. sort.Sort(typesByString(signats)) for _, ts := range signats { t := ts.t @@ -1336,6 +1279,7 @@ func WriteRuntimeTypes() { writeType(types.NewPtr(t)) } } + signatslice = signatslice[len(signats):] } // Emit GC data symbols. @@ -1349,33 +1293,71 @@ func WriteRuntimeTypes() { } } -func WriteTabs() { - // process itabs - for _, i := range itabs { - // dump empty itab symbol into i.sym - // type itab struct { - // inter *interfacetype - // _type *_type - // hash uint32 - // _ [4]byte - // fun [1]uintptr // variable sized - // } - o := objw.SymPtr(i.lsym, 0, writeType(i.itype), 0) - o = objw.SymPtr(i.lsym, o, writeType(i.t), 0) - o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash - o += 4 // skip unused field - for _, fn := range genfun(i.t, i.itype) { - o = objw.SymPtrWeak(i.lsym, o, fn, 0) // method pointer for each method +// writeITab writes the itab for concrete type typ implementing interface iface. If +// allowNonImplement is true, allow the case where typ does not implement iface, and just +// create a dummy itab with zeroed-out method entries. +func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) { + // TODO(mdempsky): Fix methodWrapper, geneq, and genhash (and maybe + // others) to stop clobbering these. + oldpos, oldfn := base.Pos, ir.CurFunc + defer func() { base.Pos, ir.CurFunc = oldpos, oldfn }() + + if typ == nil || (typ.IsPtr() && typ.Elem() == nil) || typ.IsUntyped() || iface == nil || !iface.IsInterface() || iface.IsEmptyInterface() { + base.Fatalf("writeITab(%v, %v)", typ, iface) + } + + sigs := iface.AllMethods().Slice() + entries := make([]*obj.LSym, 0, len(sigs)) + + // both sigs and methods are sorted by name, + // so we can find the intersection in a single pass + for _, m := range methods(typ) { + if m.name == sigs[0].Sym { + entries = append(entries, m.isym) + if m.isym == nil { + panic("NO ISYM") + } + sigs = sigs[1:] + if len(sigs) == 0 { + break + } } - // Nothing writes static itabs, so they are read only. - objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA)) - i.lsym.Set(obj.AttrContentAddressable, true) } + completeItab := len(sigs) == 0 + if !allowNonImplement && !completeItab { + base.Fatalf("incomplete itab") + } + + // dump empty itab symbol into i.sym + // type itab struct { + // inter *interfacetype + // _type *_type + // hash uint32 // copy of _type.hash. Used for type switches. + // _ [4]byte + // fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. + // } + o := objw.SymPtr(lsym, 0, writeType(iface), 0) + o = objw.SymPtr(lsym, o, writeType(typ), 0) + o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash + o += 4 // skip unused field + if !completeItab { + // If typ doesn't implement iface, make method entries be zero. + o = objw.Uintptr(lsym, o, 0) + entries = entries[:0] + } + for _, fn := range entries { + o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method + } + // Nothing writes static itabs, so they are read only. + objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA)) + lsym.Set(obj.AttrContentAddressable, true) +} +func WriteTabs() { // process ptabs if types.LocalPkg.Name == "main" && len(ptabs) > 0 { ot := 0 - s := base.Ctxt.Lookup("go.plugin.tabs") + s := base.Ctxt.Lookup("go:plugin.tabs") for _, p := range ptabs { // Dump ptab symbol into go.pluginsym package. // @@ -1383,7 +1365,7 @@ func WriteTabs() { // name nameOff // typ typeOff // pointer to symbol // } - nsym := dname(p.Sym().Name, "", nil, true) + nsym := dname(p.Sym().Name, "", nil, true, false) t := p.Type() if p.Class != ir.PFUNC { t = types.NewPtr(t) @@ -1398,7 +1380,7 @@ func WriteTabs() { objw.Global(s, int32(ot), int16(obj.RODATA)) ot = 0 - s = base.Ctxt.Lookup("go.plugin.exports") + s = base.Ctxt.Lookup("go:plugin.exports") for _, p := range ptabs { ot = objw.SymPtr(s, ot, p.Linksym(), 0) } @@ -1426,6 +1408,7 @@ func WriteBasicTypes() { } writeType(types.NewPtr(types.Types[types.TSTRING])) writeType(types.NewPtr(types.Types[types.TUNSAFEPTR])) + writeType(types.AnyType) // emit type structs for error and func(error) string. // The latter is the type of an auto-generated wrapper. @@ -1446,6 +1429,9 @@ func WriteBasicTypes() { if base.Flag.MSan { dimportpath(types.NewPkg("runtime/msan", "")) } + if base.Flag.ASan { + dimportpath(types.NewPkg("runtime/asan", "")) + } dimportpath(types.NewPkg("main", "")) } @@ -1453,7 +1439,7 @@ func WriteBasicTypes() { type typeAndStr struct { t *types.Type - short string + short string // "short" here means TypeSymName regular string } @@ -1461,13 +1447,26 @@ type typesByString []typeAndStr func (a typesByString) Len() int { return len(a) } func (a typesByString) Less(i, j int) bool { + // put named types before unnamed types + if a[i].t.Sym() != nil && a[j].t.Sym() == nil { + return true + } + if a[i].t.Sym() == nil && a[j].t.Sym() != nil { + return false + } + if a[i].short != a[j].short { return a[i].short < a[j].short } // When the only difference between the types is whether // they refer to byte or uint8, such as **byte vs **uint8, - // the types' ShortStrings can be identical. + // the types' NameStrings can be identical. // To preserve deterministic sort ordering, sort these by String(). + // + // TODO(mdempsky): This all seems suspect. Using LinkString would + // avoid naming collisions, and there shouldn't be a reason to care + // about "byte" vs "uint8": they share the same runtime type + // descriptor anyway. if a[i].regular != a[j].regular { return a[i].regular < a[j].regular } @@ -1513,7 +1512,6 @@ func (a typesByString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // use bitmaps for objects up to 64 kB in size. // // Also known to reflect/type.go. -// const maxPtrmaskBytes = 2048 // GCSym returns a data symbol containing GC information for type t, along @@ -1550,7 +1548,11 @@ func dgcsym(t *types.Type, write bool) (lsym *obj.LSym, useGCProg bool, ptrdata // dgcptrmask emits and returns the symbol containing a pointer mask for type t. func dgcptrmask(t *types.Type, write bool) *obj.LSym { - ptrmask := make([]byte, (types.PtrDataSize(t)/int64(types.PtrSize)+7)/8) + // Bytes we need for the ptrmask. + n := (types.PtrDataSize(t)/int64(types.PtrSize) + 7) / 8 + // Runtime wants ptrmasks padded to a multiple of uintptr in size. + n = (n + int64(types.PtrSize) - 1) &^ (int64(types.PtrSize) - 1) + ptrmask := make([]byte, n) fillptrmask(t, ptrmask) p := fmt.Sprintf("runtime.gcbits.%x", ptrmask) @@ -1594,7 +1596,7 @@ func fillptrmask(t *types.Type, ptrmask []byte) { // For non-trivial arrays, the program describes the full t.Width size. func dgcprog(t *types.Type, write bool) (*obj.LSym, int64) { types.CalcSize(t) - if t.Width == types.BADWIDTH { + if t.Size() == types.BADWIDTH { base.Fatalf("dgcprog: %v badwidth", t) } lsym := TypeLinksymPrefix(".gcprog", t) @@ -1603,8 +1605,8 @@ func dgcprog(t *types.Type, write bool) (*obj.LSym, int64) { p.emit(t, 0) offset := p.w.BitIndex() * int64(types.PtrSize) p.end() - if ptrdata := types.PtrDataSize(t); offset < ptrdata || offset > t.Width { - base.Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width) + if ptrdata := types.PtrDataSize(t); offset < ptrdata || offset > t.Size() { + base.Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Size()) } return lsym, offset } @@ -1653,7 +1655,7 @@ func (p *gcProg) emit(t *types.Type, offset int64) { if !t.HasPointers() { return } - if t.Width == int64(types.PtrSize) { + if t.Size() == int64(types.PtrSize) { p.w.Ptr(offset / int64(types.PtrSize)) return } @@ -1685,16 +1687,16 @@ func (p *gcProg) emit(t *types.Type, offset int64) { elem = elem.Elem() } - if !p.w.ShouldRepeat(elem.Width/int64(types.PtrSize), count) { + if !p.w.ShouldRepeat(elem.Size()/int64(types.PtrSize), count) { // Cheaper to just emit the bits. for i := int64(0); i < count; i++ { - p.emit(elem, offset+i*elem.Width) + p.emit(elem, offset+i*elem.Size()) } return } p.emit(elem, offset) - p.w.ZeroUntil((offset + elem.Width) / int64(types.PtrSize)) - p.w.Repeat(elem.Width/int64(types.PtrSize), count-1) + p.w.ZeroUntil((offset + elem.Size()) / int64(types.PtrSize)) + p.w.Repeat(elem.Size()/int64(types.PtrSize), count-1) case types.TSTRUCT: for _, t1 := range t.Fields().Slice() { @@ -1712,7 +1714,7 @@ func ZeroAddr(size int64) ir.Node { if ZeroSize < size { ZeroSize = size } - lsym := base.PkgLinksym("go.map", "zero", obj.ABI0) + lsym := base.PkgLinksym("go:map", "zero", obj.ABI0) x := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8]) return typecheck.Expr(typecheck.NodAddr(x)) } @@ -1737,10 +1739,56 @@ func CollectPTabs() { if s.Pkg.Name != "main" { continue } + if n.Type().HasTParam() { + continue // skip generic functions (#52937) + } ptabs = append(ptabs, n) } } +// NeedEmit reports whether typ is a type that we need to emit code +// for (e.g., runtime type descriptors, method wrappers). +func NeedEmit(typ *types.Type) bool { + // TODO(mdempsky): Export data should keep track of which anonymous + // and instantiated types were emitted, so at least downstream + // packages can skip re-emitting them. + // + // Perhaps we can just generalize the linker-symbol indexing to + // track the index of arbitrary types, not just defined types, and + // use its presence to detect this. The same idea would work for + // instantiated generic functions too. + + switch sym := typ.Sym(); { + case sym == nil: + // Anonymous type; possibly never seen before or ever again. + // Need to emit to be safe (however, see TODO above). + return true + + case sym.Pkg == types.LocalPkg: + // Local defined type; our responsibility. + return true + + case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == types.UnsafePkg): + // Package runtime is responsible for including code for builtin + // types (predeclared and package unsafe). + return true + + case typ.IsFullyInstantiated(): + // Instantiated type; possibly instantiated with unique type arguments. + // Need to emit to be safe (however, see TODO above). + return true + + case typ.HasShape(): + // Shape type; need to emit even though it lives in the .shape package. + // TODO: make sure the linker deduplicates them (see dupok in writeType above). + return true + + default: + // Should have been emitted by an imported package. + return false + } +} + // Generate a wrapper function to convert from // a receiver of type T to a receiver of type U. // That is, @@ -1761,50 +1809,85 @@ func CollectPTabs() { // // rcvr - U // method - M func (t T)(), a TFIELD type struct -func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym { +// +// Also wraps methods on instantiated generic types for use in itab entries. +// For an instantiated generic type G[int], we generate wrappers like: +// G[int] pointer shaped: +// +// func (x G[int]) f(arg) { +// .inst.G[int].f(dictionary, x, arg) +// } +// +// G[int] not pointer shaped: +// +// func (x *G[int]) f(arg) { +// .inst.G[int].f(dictionary, *x, arg) +// } +// +// These wrappers are always fully stenciled. +func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym { + orig := rcvr + if forItab && !types.IsDirectIface(rcvr) { + rcvr = rcvr.PtrTo() + } + + generic := false + // We don't need a dictionary if we are reaching a method (possibly via an + // embedded field) which is an interface method. + if !types.IsInterfaceMethod(method.Type) { + rcvr1 := deref(rcvr) + if len(rcvr1.RParams()) > 0 { + // If rcvr has rparams, remember method as generic, which + // means we need to add a dictionary to the wrapper. + generic = true + if rcvr.HasShape() { + base.Fatalf("method on type instantiated with shapes, rcvr:%+v", rcvr) + } + } + } + newnam := ir.MethodSym(rcvr, method.Sym) lsym := newnam.Linksym() - if newnam.Siggen() { + + // Unified IR creates its own wrappers. + if base.Debug.Unified != 0 { return lsym } - newnam.SetSiggen(true) - if types.Identical(rcvr, method.Type.Recv().Type) { + if newnam.Siggen() { return lsym } + newnam.SetSiggen(true) - // Only generate (*T).M wrappers for T.M in T's own package. - if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && - rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg { + methodrcvr := method.Type.Recv().Type + // For generic methods, we need to generate the wrapper even if the receiver + // types are identical, because we want to add the dictionary. + if !generic && types.Identical(rcvr, methodrcvr) { return lsym } - // Only generate I.M wrappers for I in I's own package - // but keep doing it for error.Error (was issue #29304). - if rcvr.IsInterface() && rcvr.Sym() != nil && rcvr.Sym().Pkg != types.LocalPkg && rcvr != types.ErrorType { + if !NeedEmit(rcvr) || rcvr.IsPtr() && !NeedEmit(rcvr.Elem()) { return lsym } base.Pos = base.AutogeneratedPos typecheck.DeclContext = ir.PEXTERN - tfn := ir.NewFuncType(base.Pos, - ir.NewField(base.Pos, typecheck.Lookup(".this"), nil, rcvr), - typecheck.NewFuncParams(method.Type.Params(), true), - typecheck.NewFuncParams(method.Type.Results(), false)) - // TODO(austin): SelectorExpr may have created one or more // ir.Names for these already with a nil Func field. We should // consolidate these and always attach a Func to the Name. - fn := typecheck.DeclFunc(newnam, tfn) + fn := typecheck.DeclFunc(newnam, ir.NewField(base.Pos, typecheck.Lookup(".this"), rcvr), + typecheck.NewFuncParams(method.Type.Params(), true), + typecheck.NewFuncParams(method.Type.Results(), false)) + fn.SetDupok(true) - nthis := ir.AsNode(tfn.Type().Recv().Nname) + nthis := ir.AsNode(fn.Type().Recv().Nname) - methodrcvr := method.Type.Recv().Type + indirect := rcvr.IsPtr() && rcvr.Elem() == methodrcvr // generate nil pointer check for better error - if rcvr.IsPtr() && rcvr.Elem() == methodrcvr { + if indirect { // generating wrapper from *T to T. n := ir.NewIfStmt(base.Pos, nil, nil, nil) n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil()) @@ -1814,7 +1897,6 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym { } dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym)) - // generate call // It's not possible to use a tail call when dynamic linking on ppc64le. The // bad scenario is when a local call is made to the wrapper: the wrapper will @@ -1822,24 +1904,79 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym { // the TOC to the appropriate value for that module. But if it returns // directly to the wrapper's caller, nothing will reset it to the correct // value for that function. - // - // Disable tailcall for RegabiArgs for now. The IR does not connect the - // arguments with the OTAILCALL node, and the arguments are not marshaled - // correctly. - if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs { - // generate tail call: adjust pointer receiver and jump to embedded method. - left := dot.X // skip final .M - if !left.Type().IsPtr() { - left = typecheck.NodAddr(left) - } - as := ir.NewAssignStmt(base.Pos, nthis, typecheck.ConvNop(left, rcvr)) - fn.Body.Append(as) - fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name))) + var call *ir.CallExpr + if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic { + call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil) + call.Args = ir.ParamNames(fn.Type()) + call.IsDDD = fn.Type().IsVariadic() + fn.Body.Append(ir.NewTailCallStmt(base.Pos, call)) } else { fn.SetWrapper(true) // ignore frame for panic+recover matching - call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil) - call.Args = ir.ParamNames(tfn.Type()) - call.IsDDD = tfn.Type().IsVariadic() + + if generic && dot.X != nthis { + // If there is embedding involved, then we should do the + // normal non-generic embedding wrapper below, which calls + // the wrapper for the real receiver type using dot as an + // argument. There is no need for generic processing (adding + // a dictionary) for this wrapper. + generic = false + } + + if generic { + targs := deref(rcvr).RParams() + // The wrapper for an auto-generated pointer/non-pointer + // receiver method should share the same dictionary as the + // corresponding original (user-written) method. + baseOrig := orig + if baseOrig.IsPtr() && !methodrcvr.IsPtr() { + baseOrig = baseOrig.Elem() + } else if !baseOrig.IsPtr() && methodrcvr.IsPtr() { + baseOrig = types.NewPtr(baseOrig) + } + args := []ir.Node{getDictionary(ir.MethodSym(baseOrig, method.Sym), targs)} + if indirect { + args = append(args, ir.NewStarExpr(base.Pos, dot.X)) + } else if methodrcvr.IsPtr() && methodrcvr.Elem() == dot.X.Type() { + // Case where method call is via a non-pointer + // embedded field with a pointer method. + args = append(args, typecheck.NodAddrAt(base.Pos, dot.X)) + } else { + args = append(args, dot.X) + } + args = append(args, ir.ParamNames(fn.Type())...) + + // Target method uses shaped names. + targs2 := make([]*types.Type, len(targs)) + origRParams := deref(orig).OrigType().RParams() + for i, t := range targs { + targs2[i] = typecheck.Shapify(t, i, origRParams[i]) + } + targs = targs2 + + sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true) + if sym.Def == nil { + // Currently we make sure that we have all the + // instantiations we need by generating them all in + // ../noder/stencil.go:instantiateMethods + // Extra instantiations because of an inlined function + // should have been exported, and so available via + // Resolve. + in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) + if in.Op() == ir.ONONAME { + base.Fatalf("instantiation %s not found", sym.Name) + } + sym = in.Sym() + } + target := ir.AsNode(sym.Def) + call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args) + // Fill-in the generic method node that was not filled in + // in instantiateMethod. + method.Nname = fn.Nname + } else { + call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil) + call.Args = ir.ParamNames(fn.Type()) + } + call.IsDDD = fn.Type().IsVariadic() if method.Type.NumResults() > 0 { ret := ir.NewReturnStmt(base.Pos, nil) ret.Results = []ir.Node{call} @@ -1858,13 +1995,25 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym { ir.CurFunc = fn typecheck.Stmts(fn.Body) - // Inline calls within (*T).M wrappers. This is safe because we only - // generate those wrappers within the same compilation unit as (T).M. - // TODO(mdempsky): Investigate why we can't enable this more generally. - if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil { - inline.InlineCalls(fn) + if AfterGlobalEscapeAnalysis { + // Inlining the method may reveal closures, which require walking all function bodies + // to decide whether to capture free variables by value or by ref. So we only do inline + // if the method do not contain any closures, otherwise, the escape analysis may make + // dead variables resurrected, and causing liveness analysis confused, see issue #53702. + var canInline bool + switch x := call.X.(type) { + case *ir.Name: + canInline = len(x.Func.Closures) == 0 + case *ir.SelectorExpr: + if x.Op() == ir.OMETHEXPR { + canInline = x.FuncName().Func != nil && len(x.FuncName().Func.Closures) == 0 + } + } + if canInline { + inline.InlineCalls(fn) + } + escape.Batch([]*ir.Func{fn}, false) } - escape.Batch([]*ir.Func{fn}, false) ir.CurFunc = nil typecheck.Target.Decls = append(typecheck.Target.Decls, fn) @@ -1872,11 +2021,21 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym { return lsym } +// AfterGlobalEscapeAnalysis tracks whether package gc has already +// performed the main, global escape analysis pass. If so, +// methodWrapper takes responsibility for escape analyzing any +// generated wrappers. +var AfterGlobalEscapeAnalysis bool + var ZeroSize int64 // MarkTypeUsedInInterface marks that type t is converted to an interface. // This information is used in the linker in dead method elimination. func MarkTypeUsedInInterface(t *types.Type, from *obj.LSym) { + if t.HasShape() { + // Shape types shouldn't be put in interfaces, so we shouldn't ever get here. + base.Fatalf("shape types have no methods %+v", t) + } tsym := TypeLinksym(t) // Emit a marker relocation. The linker will know the type is converted // to an interface if "from" is reachable. @@ -1894,12 +2053,101 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) { } dot := n.X.(*ir.SelectorExpr) ityp := dot.X.Type() + if ityp.HasShape() { + // Here we're calling a method on a generic interface. Something like: + // + // type I[T any] interface { foo() T } + // func f[T any](x I[T]) { + // ... = x.foo() + // } + // f[int](...) + // f[string](...) + // + // In this case, in f we're calling foo on a generic interface. + // Which method could that be? Normally we could match the method + // both by name and by type. But in this case we don't really know + // the type of the method we're calling. It could be func()int + // or func()string. So we match on just the function name, instead + // of both the name and the type used for the non-generic case below. + // TODO: instantiations at least know the shape of the instantiated + // type, and the linker could do more complicated matching using + // some sort of fuzzy shape matching. For now, only use the name + // of the method for matching. + r := obj.Addrel(ir.CurFunc.LSym) + // We use a separate symbol just to tell the linker the method name. + // (The symbol itself is not needed in the final binary. Do not use + // staticdata.StringSym, which creates a content addessable symbol, + // which may have trailing zero bytes. This symbol doesn't need to + // be deduplicated anyway.) + name := dot.Sel.Name + var nameSym obj.LSym + nameSym.WriteString(base.Ctxt, 0, len(name), name) + objw.Global(&nameSym, int32(len(name)), obj.RODATA) + r.Sym = &nameSym + r.Type = objabi.R_USEGENERICIFACEMETHOD + return + } + tsym := TypeLinksym(ityp) r := obj.Addrel(ir.CurFunc.LSym) r.Sym = tsym - // dot.Xoffset is the method index * PtrSize (the offset of code pointer + // dot.Offset() is the method index * PtrSize (the offset of code pointer // in itab). midx := dot.Offset() / int64(types.PtrSize) r.Add = InterfaceMethodOffset(ityp, midx) r.Type = objabi.R_USEIFACEMETHOD } + +// getDictionary returns the dictionary for the given named generic function +// or method, with the given type arguments. +func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node { + if len(targs) == 0 { + base.Fatalf("%s should have type arguments", gf.Name) + } + for _, t := range targs { + if t.HasShape() { + base.Fatalf("dictionary for %s should only use concrete types: %+v", gf.Name, t) + } + } + + sym := typecheck.MakeDictSym(gf, targs, true) + + // Dictionary should already have been generated by instantiateMethods(). + // Extra dictionaries needed because of an inlined function should have been + // exported, and so available via Resolve. + if lsym := sym.Linksym(); len(lsym.P) == 0 { + in := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) + if in.Op() == ir.ONONAME { + base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name) + } + sym = in.Sym() + } + + // Make (or reuse) a node referencing the dictionary symbol. + var n *ir.Name + if sym.Def != nil { + n = sym.Def.(*ir.Name) + } else { + n = typecheck.NewName(sym) + n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter + n.SetTypecheck(1) + n.Class = ir.PEXTERN + sym.Def = n + } + + // Return the address of the dictionary. + np := typecheck.NodAddr(n) + // Note: treat dictionary pointers as uintptrs, so they aren't pointers + // with respect to GC. That saves on stack scanning work, write barriers, etc. + // We can get away with it because dictionaries are global variables. + np.SetType(types.Types[types.TUINTPTR]) + np.SetTypecheck(1) + return np +} + +func deref(t *types.Type) *types.Type { + if t.IsPtr() { + return t.Elem() + } + return t +} diff --git a/src/cmd/compile/internal/riscv64/galign.go b/src/cmd/compile/internal/riscv64/galign.go index 338248a7cf27ab..4244afba3e1968 100644 --- a/src/cmd/compile/internal/riscv64/galign.go +++ b/src/cmd/compile/internal/riscv64/galign.go @@ -16,10 +16,11 @@ func Init(arch *ssagen.ArchInfo) { arch.MAXWIDTH = 1 << 50 arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.ZeroRange = zeroRange arch.SSAMarkMoves = ssaMarkMoves arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock + arch.LoadRegResult = loadRegResult + arch.SpillArgReg = spillArgReg } diff --git a/src/cmd/compile/internal/riscv64/ggen.go b/src/cmd/compile/internal/riscv64/ggen.go index 9df739456b9e5a..44488e43276bc2 100644 --- a/src/cmd/compile/internal/riscv64/ggen.go +++ b/src/cmd/compile/internal/riscv64/ggen.go @@ -19,7 +19,7 @@ func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } // Adjust the frame to account for LR. - off += base.Ctxt.FixedFrameSize() + off += base.Ctxt.Arch.FixedFrameSize if cnt < int64(4*types.PtrSize) { for i := int64(0); i < cnt; i += int64(types.PtrSize) { @@ -29,7 +29,7 @@ func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } if cnt <= int64(128*types.PtrSize) { - p = pp.Append(p, riscv.AADDI, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_A0, 0) + p = pp.Append(p, riscv.AADDI, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_X25, 0) p.Reg = riscv.REG_SP p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) p.To.Name = obj.NAME_EXTERN diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go index 64a9b3b33b9aca..5f74fd876cf6cc 100644 --- a/src/cmd/compile/internal/riscv64/ssa.go +++ b/src/cmd/compile/internal/riscv64/ssa.go @@ -7,6 +7,7 @@ package riscv64 import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/objw" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" @@ -230,6 +231,19 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() ssagen.AddrAuto(&p.To, v) + case ssa.OpArgIntReg, ssa.OpArgFloatReg: + // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill + // The loop only runs once. + for _, a := range v.Block.Func.RegArgs { + // Pass the spill/unspill information along to the assembler, offset by size of + // the saved LR slot. + addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize) + s.FuncInfo().AddSpill( + obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)}) + } + v.Block.Func.RegArgs = nil + + ssagen.CheckArgReg(v) case ssa.OpSP, ssa.OpSB, ssa.OpGetG: // nothing to do case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg, @@ -272,17 +286,65 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS, ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES, ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD, - ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED: + ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED, + ssa.OpRISCV64FSGNJD: + r := v.Reg() + r1 := v.Args[0].Reg() + r2 := v.Args[1].Reg() + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = r2 + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + case ssa.OpRISCV64LoweredMuluhilo: + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + p := s.Prog(riscv.AMULHU) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.Reg = r0 + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + p1 := s.Prog(riscv.AMUL) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = r1 + p1.Reg = r0 + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg1() + case ssa.OpRISCV64LoweredMuluover: + r0 := v.Args[0].Reg() + r1 := v.Args[1].Reg() + p := s.Prog(riscv.AMULHU) + p.From.Type = obj.TYPE_REG + p.From.Reg = r1 + p.Reg = r0 + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg1() + p1 := s.Prog(riscv.AMUL) + p1.From.Type = obj.TYPE_REG + p1.From.Reg = r1 + p1.Reg = r0 + p1.To.Type = obj.TYPE_REG + p1.To.Reg = v.Reg0() + p2 := s.Prog(riscv.ASNEZ) + p2.From.Type = obj.TYPE_REG + p2.From.Reg = v.Reg1() + p2.To.Type = obj.TYPE_REG + p2.To.Reg = v.Reg1() + case ssa.OpRISCV64FMADDD, ssa.OpRISCV64FMSUBD, ssa.OpRISCV64FNMADDD, ssa.OpRISCV64FNMSUBD: r := v.Reg() r1 := v.Args[0].Reg() r2 := v.Args[1].Reg() + r3 := v.Args[2].Reg() p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = r2 p.Reg = r1 + p.SetRestArgs([]obj.Addr{{Type: obj.TYPE_REG, Reg: r3}}) p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, + case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX, ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS, ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD, @@ -365,6 +427,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter: s.Call(v) + case ssa.OpRISCV64CALLtail: + s.TailCall(v) case ssa.OpRISCV64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -598,14 +662,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } case ssa.OpRISCV64LoweredGetClosurePtr: - // Closure pointer is S4 (riscv.REG_CTXT). + // Closure pointer is S10 (riscv.REG_CTXT). ssagen.CheckLoweredGetClosurePtr(v) case ssa.OpRISCV64LoweredGetCallerSP: // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(riscv.AMOV) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -677,14 +741,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: @@ -728,3 +787,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { b.Fatalf("Unhandled block: %s", b.LongString()) } } + +func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p := s.Prog(loadByType(t)) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_AUTO + p.From.Sym = n.Linksym() + p.From.Offset = n.FrameOffset() + off + p.To.Type = obj.TYPE_REG + p.To.Reg = reg + return p +} + +func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) + p.To.Name = obj.NAME_PARAM + p.To.Sym = n.Linksym() + p.Pos = p.Pos.WithNotStmt() + return p +} diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go index b004a2db0a39b3..d880834c220d91 100644 --- a/src/cmd/compile/internal/s390x/galign.go +++ b/src/cmd/compile/internal/s390x/galign.go @@ -16,7 +16,6 @@ func Init(arch *ssagen.ArchInfo) { arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = ssaMarkMoves arch.SSAGenValue = ssaGenValue diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go index 488a080c468886..70e403122481e2 100644 --- a/src/cmd/compile/internal/s390x/ggen.go +++ b/src/cmd/compile/internal/s390x/ggen.go @@ -24,7 +24,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog } // Adjust the frame to account for LR. - off += base.Ctxt.FixedFrameSize() + off += base.Ctxt.Arch.FixedFrameSize reg := int16(s390x.REGSP) // If the off cannot fit in a 12-bit unsigned displacement then we diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go index ddc05b36add0fa..7d9b31de4c2896 100644 --- a/src/cmd/compile/internal/s390x/ssa.go +++ b/src/cmd/compile/internal/s390x/ssa.go @@ -132,7 +132,9 @@ func moveByType(t *types.Type) obj.As { } // opregreg emits instructions for -// dest := dest(To) op src(From) +// +// dest := dest(To) op src(From) +// // and also returns the created obj.Prog so it // may be further adjusted (offset, scale, etc). func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog { @@ -145,7 +147,9 @@ func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog { } // opregregimm emits instructions for +// // dest := src(From) op off +// // and also returns the created obj.Prog so it // may be further adjusted (offset, scale, etc). func opregregimm(s *ssagen.State, op obj.As, dest, src int16, off int64) *obj.Prog { @@ -546,7 +550,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(s390x.AMOVD) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -556,6 +560,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter: s.Call(v) + case ssa.OpS390XCALLtail: + s.TailCall(v) case ssa.OpS390XLoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -899,17 +905,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Br(s390x.ABR, b.Succs[0].Block()) } return - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: return case ssa.BlockRet: s.Prog(obj.ARET) return - case ssa.BlockRetJmp: - p := s.Prog(s390x.ABR) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - return } // Handle s390x-specific blocks. These blocks all have a diff --git a/src/cmd/compile/internal/ssa/README.md b/src/cmd/compile/internal/ssa/README.md index 833bf1ddc9fa77..d695fda0451ae3 100644 --- a/src/cmd/compile/internal/ssa/README.md +++ b/src/cmd/compile/internal/ssa/README.md @@ -185,10 +185,10 @@ program. You can also click on values and blocks to highlight them, to help follow the control flow and values. The value specified in GOSSAFUNC can also be a package-qualified function -name, e.g. +name, e.g. GOSSAFUNC=blah.Foo go build - + This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo). diff --git a/src/cmd/compile/internal/ssa/addressingmodes.go b/src/cmd/compile/internal/ssa/addressingmodes.go index 1baf143869a998..469ba0d4947857 100644 --- a/src/cmd/compile/internal/ssa/addressingmodes.go +++ b/src/cmd/compile/internal/ssa/addressingmodes.go @@ -131,10 +131,14 @@ var needSplit = map[Op]bool{ } // For each entry k, v in this map, if we have a value x with: -// x.Op == k[0] -// x.Args[0].Op == k[1] +// +// x.Op == k[0] +// x.Args[0].Op == k[1] +// // then we can set x.Op to v and set x.Args like this: -// x.Args[0].Args + x.Args[1:] +// +// x.Args[0].Args + x.Args[1:] +// // Additionally, the Aux/AuxInt from x.Args[0] is merged into x. var combine = map[[2]Op]Op{ // amd64 @@ -340,6 +344,49 @@ var combine = map[[2]Op]Op{ [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1, [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8, + [2]Op{OpAMD64SARXLload, OpAMD64ADDQ}: OpAMD64SARXLloadidx1, + [2]Op{OpAMD64SARXQload, OpAMD64ADDQ}: OpAMD64SARXQloadidx1, + [2]Op{OpAMD64SHLXLload, OpAMD64ADDQ}: OpAMD64SHLXLloadidx1, + [2]Op{OpAMD64SHLXQload, OpAMD64ADDQ}: OpAMD64SHLXQloadidx1, + [2]Op{OpAMD64SHRXLload, OpAMD64ADDQ}: OpAMD64SHRXLloadidx1, + [2]Op{OpAMD64SHRXQload, OpAMD64ADDQ}: OpAMD64SHRXQloadidx1, + + [2]Op{OpAMD64SARXLload, OpAMD64LEAQ1}: OpAMD64SARXLloadidx1, + [2]Op{OpAMD64SARXLload, OpAMD64LEAQ4}: OpAMD64SARXLloadidx4, + [2]Op{OpAMD64SARXLload, OpAMD64LEAQ8}: OpAMD64SARXLloadidx8, + [2]Op{OpAMD64SARXQload, OpAMD64LEAQ1}: OpAMD64SARXQloadidx1, + [2]Op{OpAMD64SARXQload, OpAMD64LEAQ8}: OpAMD64SARXQloadidx8, + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ1}: OpAMD64SHLXLloadidx1, + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ4}: OpAMD64SHLXLloadidx4, + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ8}: OpAMD64SHLXLloadidx8, + [2]Op{OpAMD64SHLXQload, OpAMD64LEAQ1}: OpAMD64SHLXQloadidx1, + [2]Op{OpAMD64SHLXQload, OpAMD64LEAQ8}: OpAMD64SHLXQloadidx8, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ1}: OpAMD64SHRXLloadidx1, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ4}: OpAMD64SHRXLloadidx4, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ8}: OpAMD64SHRXLloadidx8, + [2]Op{OpAMD64SHRXQload, OpAMD64LEAQ1}: OpAMD64SHRXQloadidx1, + [2]Op{OpAMD64SHRXQload, OpAMD64LEAQ8}: OpAMD64SHRXQloadidx8, + + // amd64/v3 + [2]Op{OpAMD64MOVBELload, OpAMD64ADDQ}: OpAMD64MOVBELloadidx1, + [2]Op{OpAMD64MOVBEQload, OpAMD64ADDQ}: OpAMD64MOVBEQloadidx1, + [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ1}: OpAMD64MOVBELloadidx1, + [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ4}: OpAMD64MOVBELloadidx4, + [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ8}: OpAMD64MOVBELloadidx8, + [2]Op{OpAMD64MOVBEQload, OpAMD64LEAQ1}: OpAMD64MOVBEQloadidx1, + [2]Op{OpAMD64MOVBEQload, OpAMD64LEAQ8}: OpAMD64MOVBEQloadidx8, + + [2]Op{OpAMD64MOVBEWstore, OpAMD64ADDQ}: OpAMD64MOVBEWstoreidx1, + [2]Op{OpAMD64MOVBELstore, OpAMD64ADDQ}: OpAMD64MOVBELstoreidx1, + [2]Op{OpAMD64MOVBEQstore, OpAMD64ADDQ}: OpAMD64MOVBEQstoreidx1, + [2]Op{OpAMD64MOVBEWstore, OpAMD64LEAQ1}: OpAMD64MOVBEWstoreidx1, + [2]Op{OpAMD64MOVBEWstore, OpAMD64LEAQ2}: OpAMD64MOVBEWstoreidx2, + [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ1}: OpAMD64MOVBELstoreidx1, + [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ4}: OpAMD64MOVBELstoreidx4, + [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ8}: OpAMD64MOVBELstoreidx8, + [2]Op{OpAMD64MOVBEQstore, OpAMD64LEAQ1}: OpAMD64MOVBEQstoreidx1, + [2]Op{OpAMD64MOVBEQstore, OpAMD64LEAQ8}: OpAMD64MOVBEQstoreidx8, + // 386 [2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1, [2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1, diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index 71ca774431e33c..9a1dc8e7d12160 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -71,19 +71,25 @@ type Block struct { // Edge represents a CFG edge. // Example edges for b branching to either c or d. // (c and d have other predecessors.) -// b.Succs = [{c,3}, {d,1}] -// c.Preds = [?, ?, ?, {b,0}] -// d.Preds = [?, {b,1}, ?] +// +// b.Succs = [{c,3}, {d,1}] +// c.Preds = [?, ?, ?, {b,0}] +// d.Preds = [?, {b,1}, ?] +// // These indexes allow us to edit the CFG in constant time. // In addition, it informs phi ops in degenerate cases like: -// b: -// if k then c else c -// c: -// v = Phi(x, y) +// +// b: +// if k then c else c +// c: +// v = Phi(x, y) +// // Then the indexes tell you whether x is chosen from // the if or else branch from b. -// b.Succs = [{c,0},{c,1}] -// c.Preds = [{b,0},{b,1}] +// +// b.Succs = [{c,0},{c,1}] +// c.Preds = [{b,0},{b,1}] +// // means x is chosen if k is true. type Edge struct { // block edge goes to (in a Succs list) or from (in a Preds list) @@ -105,13 +111,15 @@ func (e Edge) String() string { return fmt.Sprintf("{%v,%d}", e.b, e.i) } -// kind controls successors -// ------------------------------------------ -// Exit [return mem] [] -// Plain [] [next] -// If [boolean Value] [then, else] -// Defer [mem] [nopanic, panic] (control opcode should be OpStaticCall to runtime.deferproc) -type BlockKind int8 +// BlockKind is the kind of SSA block. +// +// kind controls successors +// ------------------------------------------ +// Exit [return mem] [] +// Plain [] [next] +// If [boolean Value] [then, else] +// Defer [mem] [nopanic, panic] (control opcode should be OpStaticCall to runtime.deferproc) +type BlockKind int16 // short form print func (b *Block) String() string { @@ -279,7 +287,8 @@ func (b *Block) AddEdgeTo(c *Block) { // removePred removes the ith input edge from b. // It is the responsibility of the caller to remove -// the corresponding successor edge. +// the corresponding successor edge, and adjust any +// phi values by calling b.removePhiArg(v, i). func (b *Block) removePred(i int) { n := len(b.Preds) - 1 if i != n { @@ -322,6 +331,30 @@ func (b *Block) swapSuccessors() { b.Likely *= -1 } +// removePhiArg removes the ith arg from phi. +// It must be called after calling b.removePred(i) to +// adjust the corresponding phi value of the block: +// +// b.removePred(i) +// for _, v := range b.Values { +// +// if v.Op != OpPhi { +// continue +// } +// b.removeArg(v, i) +// +// } +func (b *Block) removePhiArg(phi *Value, i int) { + n := len(b.Preds) + if numPhiArgs := len(phi.Args); numPhiArgs-1 != n { + b.Fatalf("inconsistent state, num predecessors: %d, num phi args: %d", n, numPhiArgs) + } + phi.Args[i].Uses-- + phi.Args[i] = phi.Args[n] + phi.Args[n] = nil + phi.Args = phi.Args[:n] +} + // LackingPos indicates whether b is a block whose position should be inherited // from its successors. This is true if all the values within it have unreliable positions // and if it is "plain", meaning that there is no control flow that is also very likely diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go index 1d34f8160b1ea5..7a08654f4e1ef4 100644 --- a/src/cmd/compile/internal/ssa/branchelim.go +++ b/src/cmd/compile/internal/ssa/branchelim.go @@ -11,18 +11,18 @@ import "cmd/internal/src" // // Search for basic blocks that look like // -// bb0 bb0 -// | \ / \ -// | bb1 or bb1 bb2 <- trivial if/else blocks -// | / \ / -// bb2 bb3 +// bb0 bb0 +// | \ / \ +// | bb1 or bb1 bb2 <- trivial if/else blocks +// | / \ / +// bb2 bb3 // // where the intermediate blocks are mostly empty (with no side-effects); // rewrite Phis in the postdominator as CondSelects. func branchelim(f *Func) { // FIXME: add support for lowering CondSelects on more architectures switch f.Config.arch { - case "arm64", "amd64", "wasm": + case "arm64", "ppc64le", "ppc64", "amd64", "wasm": // implemented default: return diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 969fd96dbf573c..f34b9074197bfc 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -5,6 +5,7 @@ package ssa import ( + "cmd/compile/internal/ir" "cmd/internal/obj/s390x" "math" "math/bits" @@ -66,9 +67,6 @@ func checkFunc(f *Func) { if !b.Controls[0].Type.IsMemory() { f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString()) } - if b.Aux == nil { - f.Fatalf("retjmp block %s has nil Aux field", b) - } case BlockPlain: if len(b.Succs) != 1 { f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) @@ -103,6 +101,10 @@ func checkFunc(f *Func) { if b.NumControls() != 0 { f.Fatalf("plain/dead block %s has a control value", b) } + case BlockJumpTable: + if b.NumControls() != 1 { + f.Fatalf("jumpTable block %s has no control value", b) + } } if len(b.Succs) != 2 && b.Likely != BranchUnknown { f.Fatalf("likeliness prediction %d for block %s with %d successors", b.Likely, b, len(b.Succs)) @@ -311,6 +313,10 @@ func checkFunc(f *Func) { if !v.Args[1].Type.IsInteger() { f.Fatalf("bad arg 1 type to %s: want integer, have %s", v.Op, v.Args[1].LongString()) } + case OpVarDef: + if !v.Aux.(*ir.Name).Type().HasPointers() { + f.Fatalf("vardef must have pointer type %s", v.Aux.(*ir.Name).Type().String()) + } } diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index cd8eba405d5c7b..3be2cc7c3738e5 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -5,14 +5,15 @@ package ssa import ( - "bytes" "cmd/internal/src" "fmt" "hash/crc32" "internal/buildcfg" + "io" "log" "math/rand" "os" + "path/filepath" "regexp" "runtime" "sort" @@ -22,10 +23,10 @@ import ( // Compile is the main entry point for this package. // Compile modifies f so that on return: -// · all Values in f map to 0 or 1 assembly instructions of the target architecture -// · the order of f.Blocks is the order to emit the Blocks -// · the order of b.Values is the order to emit the Values in each Block -// · f has a non-nil regAlloc field +// - all Values in f map to 0 or 1 assembly instructions of the target architecture +// - the order of f.Blocks is the order to emit the Blocks +// - the order of b.Values is the order to emit the Values in each Block +// - f has a non-nil regAlloc field func Compile(f *Func) { // TODO: debugging - set flags to control verbosity of compiler, // which phases to dump IR before/after, etc. @@ -59,7 +60,7 @@ func Compile(f *Func) { printFunc(f) } f.HTMLWriter.WritePhase("start", "start") - if BuildDump != "" && BuildDump == f.Name { + if BuildDump[f.Name] { f.dumpFile("build") } if checkEnabled { @@ -150,7 +151,7 @@ func Compile(f *Func) { keys = append(keys, key) } sort.Strings(keys) - buf := new(bytes.Buffer) + buf := new(strings.Builder) fmt.Fprintf(buf, "%s: ", f.Name) for _, key := range keys { fmt.Fprintf(buf, "%s=%d ", key, f.ruleMatches[key]) @@ -163,25 +164,37 @@ func Compile(f *Func) { phaseName = "" } -// dumpFile creates a file from the phase name and function name -// Dumping is done to files to avoid buffering huge strings before -// output. -func (f *Func) dumpFile(phaseName string) { +// DumpFileForPhase creates a file from the function name and phase name, +// warning and returning nil if this is not possible. +func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser { f.dumpFileSeq++ fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName) fname = strings.Replace(fname, " ", "_", -1) fname = strings.Replace(fname, "/", "_", -1) fname = strings.Replace(fname, ":", "_", -1) + if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" { + fname = filepath.Join(ssaDir, fname) + } + fi, err := os.Create(fname) if err != nil { f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname) - return + return nil } + return fi +} - p := stringFuncPrinter{w: fi} - fprintFunc(p, f) - fi.Close() +// dumpFile creates a file from the phase name and function name +// Dumping is done to files to avoid buffering huge strings before +// output. +func (f *Func) dumpFile(phaseName string) { + fi := f.DumpFileForPhase(phaseName) + if fi != nil { + p := stringFuncPrinter{w: fi} + fprintFunc(p, f) + fi.Close() + } } type pass struct { @@ -224,7 +237,9 @@ var IntrinsicsDisable bool var BuildDebug int var BuildTest int var BuildStats int -var BuildDump string // name of function to dump after initial build of ssa +var BuildDump map[string]bool = make(map[string]bool) // names of functions to dump after initial build of ssa + +var GenssaDump map[string]bool = make(map[string]bool) // names of functions to dump after ssa has been converted to asm // PhaseOption sets the specified flag in the specified ssa phase, // returning empty string if this was successful or a string explaining @@ -234,8 +249,8 @@ var BuildDump string // name of function to dump after initial build of ssa // version is used as a regular expression to match the phase name(s). // // Special cases that have turned out to be useful: -// ssa/check/on enables checking after each phase -// ssa/all/time enables time reporting for all phases +// - ssa/check/on enables checking after each phase +// - ssa/all/time enables time reporting for all phases // // See gc/lex.go for dissection of the option string. // Example uses: @@ -243,12 +258,11 @@ var BuildDump string // name of function to dump after initial build of ssa // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash // // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash -// func PhaseOption(phase, flag string, val int, valString string) string { switch phase { case "", "help": lastcr := 0 - phasenames := " check, all, build, intrinsics" + phasenames := " check, all, build, intrinsics, genssa" for _, p := range passes { pn := strings.Replace(p.name, " ", "_", -1) if len(pn)+len(phasenames)-lastcr > 70 { @@ -278,6 +292,7 @@ where: Phase "all" supports flags "time", "mem", and "dump". Phase "intrinsics" supports flags "on", "off", and "debug". +Phase "genssa" (assembly generation) supports the flag "dump". If the "dump" flag is specified, the output is written on a file named ___.dump; otherwise it is directed to stdout. @@ -339,10 +354,11 @@ commas. For example: case "dump": alldump = val != 0 if alldump { - BuildDump = valString + BuildDump[valString] = true + GenssaDump[valString] = true } default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/all/{time,mem,dump=function_name})", flag, phase) } } @@ -355,7 +371,7 @@ commas. For example: case "debug": IntrinsicsDebug = val default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase) } return "" } @@ -368,9 +384,18 @@ commas. For example: case "stats": BuildStats = val case "dump": - BuildDump = valString + BuildDump[valString] = true + default: + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/build/{debug,test,stats,dump=function_name})", flag, phase) + } + return "" + } + if phase == "genssa" { + switch flag { + case "dump": + GenssaDump[valString] = true default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase) } return "" } diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index a8393a19995a5f..d7a413268b6eb0 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -149,12 +149,6 @@ type Frontend interface { // for the parts of that compound type. SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot - // DerefItab dereferences an itab function - // entry, given the symbol of the itab and - // the byte offset of the function pointer. - // It may return nil. - DerefItab(sym *obj.LSym, offset int64) *obj.LSym - // Line returns a string describing the given position. Line(src.XPos) string @@ -174,10 +168,13 @@ type Frontend interface { // MyImportPath provides the import name (roughly, the package) for the function being compiled. MyImportPath() string + + // LSym returns the linker symbol of the function being compiled. + LSym() string } // NewConfig returns a new configuration object for the given architecture. -func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config { +func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat bool) *Config { c := &Config{arch: arch, Types: types} c.useAvg = true c.useHmul = true @@ -196,7 +193,7 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.floatParamRegs = paramFloatRegAMD64 c.FPReg = framepointerRegAMD64 c.LinkReg = linkRegAMD64 - c.hasGReg = buildcfg.Experiment.RegabiG + c.hasGReg = true case "386": c.PtrSize = 4 c.RegSize = 4 @@ -228,10 +225,11 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.registers = registersARM64[:] c.gpRegMask = gpRegMaskARM64 c.fpRegMask = fpRegMaskARM64 + c.intParamRegs = paramIntRegARM64 + c.floatParamRegs = paramFloatRegARM64 c.FPReg = framepointerRegARM64 c.LinkReg = linkRegARM64 c.hasGReg = true - c.noDuffDevice = buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" // darwin linker cannot handle BR26 reloc with non-zero addend case "ppc64": c.BigEndian = true fallthrough @@ -243,9 +241,11 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.registers = registersPPC64[:] c.gpRegMask = gpRegMaskPPC64 c.fpRegMask = fpRegMaskPPC64 + c.specialRegMask = specialRegMaskPPC64 + c.intParamRegs = paramIntRegPPC64 + c.floatParamRegs = paramFloatRegPPC64 c.FPReg = framepointerRegPPC64 c.LinkReg = linkRegPPC64 - c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy) c.hasGReg = true case "mips64": c.BigEndian = true @@ -262,6 +262,17 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.FPReg = framepointerRegMIPS64 c.LinkReg = linkRegMIPS64 c.hasGReg = true + case "loong64": + c.PtrSize = 8 + c.RegSize = 8 + c.lowerBlock = rewriteBlockLOONG64 + c.lowerValue = rewriteValueLOONG64 + c.registers = registersLOONG64[:] + c.gpRegMask = gpRegMaskLOONG64 + c.fpRegMask = fpRegMaskLOONG64 + c.FPReg = framepointerRegLOONG64 + c.LinkReg = linkRegLOONG64 + c.hasGReg = true case "s390x": c.PtrSize = 8 c.RegSize = 8 @@ -299,6 +310,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.registers = registersRISCV64[:] c.gpRegMask = gpRegMaskRISCV64 c.fpRegMask = fpRegMaskRISCV64 + c.intParamRegs = paramIntRegRISCV64 + c.floatParamRegs = paramFloatRegRISCV64 c.FPReg = framepointerRegRISCV64 c.hasGReg = true case "wasm": @@ -324,9 +337,13 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config c.optimize = optimize c.useSSE = true c.UseFMA = true + c.SoftFloat = softfloat + if softfloat { + c.floatParamRegs = nil // no FP registers in softfloat mode + } - c.ABI0 = abi.NewABIConfig(0, 0, ctxt.FixedFrameSize()) - c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.FixedFrameSize()) + c.ABI0 = abi.NewABIConfig(0, 0, ctxt.Arch.FixedFrameSize) + c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.Arch.FixedFrameSize) // On Plan 9, floating point operations are not allowed in note handler. if buildcfg.GOOS == "plan9" { diff --git a/src/cmd/compile/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go index b85721eba48832..500ce3ae61ce09 100644 --- a/src/cmd/compile/internal/ssa/critical.go +++ b/src/cmd/compile/internal/ssa/critical.go @@ -91,14 +91,13 @@ func critical(f *Func) { b.removePred(i) // Update corresponding phi args - n := len(b.Preds) - phi.Args[i].Uses-- - phi.Args[i] = phi.Args[n] - phi.Args[n] = nil - phi.Args = phi.Args[:n] + b.removePhiArg(phi, i) + // splitting occasionally leads to a phi having // a single argument (occurs with -N) - if n == 1 { + // TODO(cuonglm,khr): replace this with phielimValue, and + // make removePhiArg incorporates that. + if len(b.Preds) == 1 { phi.Op = OpCopy } // Don't increment i in this case because we moved diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go index ade5e0648e78d8..a71b78ce6527ae 100644 --- a/src/cmd/compile/internal/ssa/cse.go +++ b/src/cmd/compile/internal/ssa/cse.go @@ -83,7 +83,7 @@ func cse(f *Func) { // non-equivalent arguments. Repeat until we can't find any // more splits. var splitPoints []int - byArgClass := new(partitionByArgClass) // reuseable partitionByArgClass to reduce allocations + byArgClass := new(partitionByArgClass) // reusable partitionByArgClass to reduce allocations for { changed := false @@ -235,14 +235,15 @@ type eqclass []*Value // partitionValues partitions the values into equivalence classes // based on having all the following features match: -// - opcode -// - type -// - auxint -// - aux -// - nargs -// - block # if a phi op -// - first two arg's opcodes and auxint -// - NOT first two arg's aux; that can break CSE. +// - opcode +// - type +// - auxint +// - aux +// - nargs +// - block # if a phi op +// - first two arg's opcodes and auxint +// - NOT first two arg's aux; that can break CSE. +// // partitionValues returns a list of equivalence classes, each // being a sorted by ID list of *Values. The eqclass slices are // backed by the same storage as the input slice. diff --git a/src/cmd/compile/internal/ssa/cse_test.go b/src/cmd/compile/internal/ssa/cse_test.go index 8052016f3af692..813ebe43a1d93f 100644 --- a/src/cmd/compile/internal/ssa/cse_test.go +++ b/src/cmd/compile/internal/ssa/cse_test.go @@ -22,7 +22,7 @@ func TestCSEAuxPartitionBug(t *testing.T) { arg1Aux := &tstAux{"arg1-aux"} arg2Aux := &tstAux{"arg2-aux"} arg3Aux := &tstAux{"arg3-aux"} - a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8) + a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8.PtrTo()) // construct lots of values with args that have aux values and place // them in an order that triggers the bug @@ -93,7 +93,7 @@ func TestCSEAuxPartitionBug(t *testing.T) { // TestZCSE tests the zero arg cse. func TestZCSE(t *testing.T) { c := testConfig(t) - a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8) + a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8.PtrTo()) fun := c.Fun("entry", Bloc("entry", diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index 5d10dfe025e97c..b47b106975ac13 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -348,15 +348,11 @@ func (b *Block) removeEdge(i int) { c.removePred(j) // Remove phi args from c's phis. - n := len(c.Preds) for _, v := range c.Values { if v.Op != OpPhi { continue } - v.Args[j].Uses-- - v.Args[j] = v.Args[n] - v.Args[n] = nil - v.Args = v.Args[:n] + c.removePhiArg(v, j) phielimValue(v) // Note: this is trickier than it looks. Replacing // a Phi with a Copy can in general cause problems because diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index d694133ec3bb8d..b0e4e2bb0926f0 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -39,7 +39,7 @@ func dse(f *Func) { for _, a := range v.Args { if a.Block == b && a.Type.IsMemory() { storeUse.add(a.ID) - if v.Op != OpStore && v.Op != OpZero && v.Op != OpVarDef && v.Op != OpVarKill { + if v.Op != OpStore && v.Op != OpZero && v.Op != OpVarDef { // CALL, DUFFCOPY, etc. are both // reads and writes. loadUse.add(a.ID) @@ -156,7 +156,7 @@ func elimDeadAutosGeneric(f *Func) { changed = true } return - case OpVarDef, OpVarKill: + case OpVarDef: // v should be eliminated if we eliminate the auto. n, ok := v.Aux.(*ir.Name) if !ok || n.Class != ir.PAUTO { diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index 8e2872363b6edb..3eaba3a2385abd 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -6,6 +6,7 @@ package ssa import ( "cmd/compile/internal/abi" + "cmd/compile/internal/abt" "cmd/compile/internal/ir" "cmd/compile/internal/types" "cmd/internal/dwarf" @@ -23,8 +24,8 @@ type SlotID int32 type VarID int32 // A FuncDebug contains all the debug information for the variables in a -// function. Variables are identified by their LocalSlot, which may be the -// result of decomposing a larger variable. +// function. Variables are identified by their LocalSlot, which may be +// the result of decomposing a larger variable. type FuncDebug struct { // Slots is all the slots used in the debug info, indexed by their SlotID. Slots []LocalSlot @@ -34,33 +35,46 @@ type FuncDebug struct { VarSlots [][]SlotID // The location list data, indexed by VarID. Must be processed by PutLocationList. LocationLists [][]byte + // Register-resident output parameters for the function. This is filled in at + // SSA generation time. + RegOutputParams []*ir.Name // Filled in by the user. Translates Block and Value ID to PC. GetPC func(ID, ID) int64 } type BlockDebug struct { + // State at the start and end of the block. These are initialized, + // and updated from new information that flows on back edges. + startState, endState abt.T + // Use these to avoid excess work in the merge. If none of the + // predecessors has changed since the last check, the old answer is + // still good. + lastCheckedTime, lastChangedTime int32 // Whether the block had any changes to user variables at all. relevant bool - // State at the end of the block if it's fully processed. Immutable once initialized. - endState []liveSlot + // false until the block has been processed at least once. This + // affects how the merge is done; the goal is to maximize sharing + // and avoid allocation. + everProcessed bool } // A liveSlot is a slot that's live in loc at entry/exit of a block. type liveSlot struct { - // An inlined VarLoc, so it packs into 16 bytes instead of 20. - Registers RegisterSet - StackOffset + VarLoc +} - slot SlotID +func (ls *liveSlot) String() string { + return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1) } func (loc liveSlot) absent() bool { return loc.Registers == 0 && !loc.onStack() } -// StackOffset encodes whether a value is on the stack and if so, where. It is -// a 31-bit integer followed by a presence flag at the low-order bit. +// StackOffset encodes whether a value is on the stack and if so, where. +// It is a 31-bit integer followed by a presence flag at the low-order +// bit. type StackOffset int32 func (s StackOffset) onStack() bool { @@ -80,7 +94,7 @@ type stateAtPC struct { } // reset fills state with the live variables from live. -func (state *stateAtPC) reset(live []liveSlot) { +func (state *stateAtPC) reset(live abt.T) { slots, registers := state.slots, state.registers for i := range slots { slots[i] = VarLoc{} @@ -88,13 +102,15 @@ func (state *stateAtPC) reset(live []liveSlot) { for i := range registers { registers[i] = registers[i][:0] } - for _, live := range live { - slots[live.slot] = VarLoc{live.Registers, live.StackOffset} - if live.Registers == 0 { + for it := live.Iterator(); !it.Done(); { + k, d := it.Next() + live := d.(*liveSlot) + slots[k] = live.VarLoc + if live.VarLoc.Registers == 0 { continue } - mask := uint64(live.Registers) + mask := uint64(live.VarLoc.Registers) for { if mask == 0 { break @@ -102,7 +118,7 @@ func (state *stateAtPC) reset(live []liveSlot) { reg := uint8(bits.TrailingZeros64(mask)) mask &^= 1 << reg - registers[reg] = append(registers[reg], live.slot) + registers[reg] = append(registers[reg], SlotID(k)) } } state.slots, state.registers = slots, registers @@ -115,7 +131,7 @@ func (s *debugState) LocString(loc VarLoc) string { var storage []string if loc.onStack() { - storage = append(storage, "stack") + storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue())) } mask := uint64(loc.Registers) @@ -144,6 +160,14 @@ func (loc VarLoc) absent() bool { return loc.Registers == 0 && !loc.onStack() } +func (loc VarLoc) intersect(other VarLoc) VarLoc { + if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset { + loc.StackOffset = 0 + } + loc.Registers &= other.Registers + return loc +} + var BlockStart = &Value{ ID: -10000, Op: OpInvalid, @@ -165,8 +189,9 @@ var FuncEnd = &Value{ // RegisterSet is a bitmap of registers, indexed by Register.num. type RegisterSet uint64 -// logf prints debug-specific logging to stdout (always stdout) if the current -// function is tagged by GOSSAFUNC (for ssa output directed either to stdout or html). +// logf prints debug-specific logging to stdout (always stdout) if the +// current function is tagged by GOSSAFUNC (for ssa output directed +// either to stdout or html). func (s *debugState) logf(msg string, args ...interface{}) { if s.f.PrintOrHtmlSSA { fmt.Printf(msg, args...) @@ -183,29 +208,28 @@ type debugState struct { // The user variable that each slot rolls up to, indexed by SlotID. slotVars []VarID - f *Func - loggingEnabled bool - registers []Register - stackOffset func(LocalSlot) int32 - ctxt *obj.Link + f *Func + loggingLevel int + convergeCount int // testing; iterate over block debug state this many times + registers []Register + stackOffset func(LocalSlot) int32 + ctxt *obj.Link // The names (slots) associated with each value, indexed by Value ID. valueNames [][]SlotID // The current state of whatever analysis is running. currentState stateAtPC - liveCount []int changedVars *sparseSet + changedSlots *sparseSet // The pending location list entry for each user variable, indexed by VarID. pendingEntries []pendingEntry - varParts map[*ir.Name][]SlotID - blockDebug []BlockDebug - pendingSlotLocs []VarLoc - liveSlots []liveSlot - liveSlotSliceBegin int - partsByVarOffset sort.Interface + varParts map[*ir.Name][]SlotID + blockDebug []BlockDebug + pendingSlotLocs []VarLoc + partsByVarOffset sort.Interface } func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { @@ -244,15 +268,9 @@ func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { state.currentState.registers = state.currentState.registers[:len(state.registers)] } - // Used many times by mergePredecessors. - if cap(state.liveCount) < numSlots { - state.liveCount = make([]int, numSlots) - } else { - state.liveCount = state.liveCount[:numSlots] - } - // A relatively small slice, but used many times as the return from processValue. state.changedVars = newSparseSet(numVars) + state.changedSlots = newSparseSet(numSlots) // A pending entry per user variable, with space to track each of its pieces. numPieces := 0 @@ -288,25 +306,12 @@ func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { state.lists[i] = nil } } - - state.liveSlots = state.liveSlots[:0] - state.liveSlotSliceBegin = 0 } func (state *debugState) allocBlock(b *Block) *BlockDebug { return &state.blockDebug[b.ID] } -func (state *debugState) appendLiveSlot(ls liveSlot) { - state.liveSlots = append(state.liveSlots, ls) -} - -func (state *debugState) getLiveSlotSlice() []liveSlot { - s := state.liveSlots[state.liveSlotSliceBegin:] - state.liveSlotSliceBegin = len(state.liveSlots) - return s -} - func (s *debugState) blockEndStateString(b *BlockDebug) string { endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))} endState.reset(b.endState) @@ -378,7 +383,7 @@ func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) { split, _ = sc.lookup(*ls.SplitOf) } k := slotKey{ - name: ls.N, offset: ls.Off, width: ls.Type.Width, + name: ls.N, offset: ls.Off, width: ls.Type.Size(), splitOf: split, splitOffset: ls.SplitOffset, } if idx, ok := sc.slmap[k]; ok { @@ -399,33 +404,32 @@ func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot { // OpArg{Int,Float}Reg values, inserting additional values in // cases where they are missing. Example: // -// func foo(s string, used int, notused int) int { -// return len(s) + used -// } +// func foo(s string, used int, notused int) int { +// return len(s) + used +// } // // In the function above, the incoming parameter "used" is fully live, // "notused" is not live, and "s" is partially live (only the length // field of the string is used). At the point where debug value // analysis runs, we might expect to see an entry block with: // -// b1: -// v4 = ArgIntReg {s+8} [0] : BX -// v5 = ArgIntReg {used} [0] : CX +// b1: +// v4 = ArgIntReg {s+8} [0] : BX +// v5 = ArgIntReg {used} [0] : CX // // While this is an accurate picture of the live incoming params, // we also want to have debug locations for non-live params (or // their non-live pieces), e.g. something like // -// b1: -// v9 = ArgIntReg <*uint8> {s+0} [0] : AX -// v4 = ArgIntReg {s+8} [0] : BX -// v5 = ArgIntReg {used} [0] : CX -// v10 = ArgIntReg {unused} [0] : DI +// b1: +// v9 = ArgIntReg <*uint8> {s+0} [0] : AX +// v4 = ArgIntReg {s+8} [0] : BX +// v5 = ArgIntReg {used} [0] : CX +// v10 = ArgIntReg {unused} [0] : DI // // This function examines the live OpArg{Int,Float}Reg values and // synthesizes new (dead) values for the non-live params or the // non-live pieces of partially live params. -// func PopulateABIInRegArgOps(f *Func) { pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType()) @@ -548,15 +552,21 @@ func PopulateABIInRegArgOps(f *Func) { f.Entry.Values = append(newValues, f.Entry.Values...) } -// BuildFuncDebug returns debug information for f. -// f must be fully processed, so that each Value is where it will be when -// machine code is emitted. -func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug { +// BuildFuncDebug debug information for f, placing the results +// in "rval". f must be fully processed, so that each Value is where it +// will be when machine code is emitted. +func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func(LocalSlot) int32, rval *FuncDebug) { if f.RegAlloc == nil { f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) } state := &f.Cache.debugState - state.loggingEnabled = loggingEnabled + state.loggingLevel = loggingLevel % 1000 + + // A specific number demands exactly that many iterations. Under + // particular circumstances it make require more than the total of + // 2 passes implied by a single run through liveness and a single + // run through location list generation. + state.convergeCount = loggingLevel / 1000 state.f = f state.registers = f.Config.registers state.stackOffset = stackOffset @@ -566,7 +576,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu PopulateABIInRegArgOps(f) } - if state.loggingEnabled { + if state.loggingLevel > 0 { state.logf("Generating location lists for function %q\n", f.Name) } @@ -603,7 +613,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu // This would probably be better as an output from stackframe. for _, b := range f.Blocks { for _, v := range b.Values { - if v.Op == OpVarDef || v.Op == OpVarKill { + if v.Op == OpVarDef { n := v.Aux.(*ir.Name) if ir.IsSynthetic(n) { continue @@ -661,254 +671,383 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu blockLocs := state.liveness() state.buildLocationLists(blockLocs) - return &FuncDebug{ - Slots: state.slots, - VarSlots: state.varSlots, - Vars: state.vars, - LocationLists: state.lists, - } + // Populate "rval" with what we've computed. + rval.Slots = state.slots + rval.VarSlots = state.varSlots + rval.Vars = state.vars + rval.LocationLists = state.lists } // liveness walks the function in control flow order, calculating the start // and end state of each block. func (state *debugState) liveness() []*BlockDebug { blockLocs := make([]*BlockDebug, state.f.NumBlocks()) + counterTime := int32(1) // Reverse postorder: visit a block after as many as possible of its // predecessors have been visited. po := state.f.Postorder() - for i := len(po) - 1; i >= 0; i-- { - b := po[i] - - // Build the starting state for the block from the final - // state of its predecessors. - startState, startValid := state.mergePredecessors(b, blockLocs, nil) - changed := false - if state.loggingEnabled { - state.logf("Processing %v, initial state:\n%v", b, state.stateString(state.currentState)) - } + converged := false + + // The iteration rule is that by default, run until converged, but + // if a particular iteration count is specified, run that many + // iterations, no more, no less. A count is specified as the + // thousands digit of the location lists debug flag, + // e.g. -d=locationlists=4000 + keepGoing := func(k int) bool { + if state.convergeCount == 0 { + return !converged + } + return k < state.convergeCount + } + for k := 0; keepGoing(k); k++ { + if state.loggingLevel > 0 { + state.logf("Liveness pass %d\n", k) + } + converged = true + for i := len(po) - 1; i >= 0; i-- { + b := po[i] + locs := blockLocs[b.ID] + if locs == nil { + locs = state.allocBlock(b) + blockLocs[b.ID] = locs + } - // Update locs/registers with the effects of each Value. - for _, v := range b.Values { - slots := state.valueNames[v.ID] + // Build the starting state for the block from the final + // state of its predecessors. + startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false) + locs.lastCheckedTime = counterTime + counterTime++ + if state.loggingLevel > 1 { + state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState)) + } - // Loads and stores inherit the names of their sources. - var source *Value - switch v.Op { - case OpStoreReg: - source = v.Args[0] - case OpLoadReg: - switch a := v.Args[0]; a.Op { - case OpArg, OpPhi: - source = a - case OpStoreReg: - source = a.Args[0] - default: - if state.loggingEnabled { - state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a) + if blockChanged { + // If the start did not change, then the old endState is good + converged = false + changed := false + state.changedSlots.clear() + + // Update locs/registers with the effects of each Value. + for _, v := range b.Values { + slots := state.valueNames[v.ID] + + // Loads and stores inherit the names of their sources. + var source *Value + switch v.Op { + case OpStoreReg: + source = v.Args[0] + case OpLoadReg: + switch a := v.Args[0]; a.Op { + case OpArg, OpPhi: + source = a + case OpStoreReg: + source = a.Args[0] + default: + if state.loggingLevel > 1 { + state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a) + } + } + } + // Update valueNames with the source so that later steps + // don't need special handling. + if source != nil && k == 0 { + // limit to k == 0 otherwise there are duplicates. + slots = append(slots, state.valueNames[source.ID]...) + state.valueNames[v.ID] = slots } - } - } - // Update valueNames with the source so that later steps - // don't need special handling. - if source != nil { - slots = append(slots, state.valueNames[source.ID]...) - state.valueNames[v.ID] = slots - } - reg, _ := state.f.getHome(v.ID).(*Register) - c := state.processValue(v, slots, reg) - changed = changed || c - } + reg, _ := state.f.getHome(v.ID).(*Register) + c := state.processValue(v, slots, reg) + changed = changed || c + } - if state.loggingEnabled { - state.f.Logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState)) - } + if state.loggingLevel > 1 { + state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState)) + } - locs := state.allocBlock(b) - locs.relevant = changed - if !changed && startValid { - locs.endState = startState - } else { - for slotID, slotLoc := range state.currentState.slots { - if slotLoc.absent() { - continue + locs.relevant = locs.relevant || changed + if !changed { + locs.endState = startState + } else { + for _, id := range state.changedSlots.contents() { + slotID := SlotID(id) + slotLoc := state.currentState.slots[slotID] + if slotLoc.absent() { + startState.Delete(int32(slotID)) + continue + } + old := startState.Find(int32(slotID)) // do NOT replace existing values + if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc { + startState.Insert(int32(slotID), + &liveSlot{VarLoc: slotLoc}) + } + } + locs.endState = startState } - state.appendLiveSlot(liveSlot{slot: SlotID(slotID), Registers: slotLoc.Registers, StackOffset: slotLoc.StackOffset}) + locs.lastChangedTime = counterTime } - locs.endState = state.getLiveSlotSlice() + counterTime++ } - blockLocs[b.ID] = locs } return blockLocs } // mergePredecessors takes the end state of each of b's predecessors and -// intersects them to form the starting state for b. It puts that state in -// blockLocs, and fills state.currentState with it. If convenient, it returns -// a reused []liveSlot, true that represents the starting state. -// If previousBlock is non-nil, it registers changes vs. that block's end -// state in state.changedVars. Note that previousBlock will often not be a -// predecessor. -func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block) ([]liveSlot, bool) { +// intersects them to form the starting state for b. It puts that state +// in blockLocs[b.ID].startState, and fills state.currentState with it. +// It returns the start state and whether this is changed from the +// previously approximated value of startState for this block. After +// the first call, subsequent calls can only shrink startState. +// +// Passing forLocationLists=true enables additional side-effects that +// are necessary for building location lists but superflous while still +// iterating to an answer. +// +// If previousBlock is non-nil, it registers changes vs. that block's +// end state in state.changedVars. Note that previousBlock will often +// not be a predecessor. +// +// Note that mergePredecessors behaves slightly differently between +// first and subsequent calls for a block. For the first call, the +// starting state is approximated by taking the state from the +// predecessor whose state is smallest, and removing any elements not +// in all the other predecessors; this makes the smallest number of +// changes and shares the most state. On subsequent calls the old +// value of startState is adjusted with new information; this is judged +// to do the least amount of extra work. +// +// To improve performance, each block's state information is marked with +// lastChanged and lastChecked "times" so unchanged predecessors can be +// skipped on after-the-first iterations. Doing this allows extra +// iterations by the caller to be almost free. +// +// It is important to know that the set representation used for +// startState, endState, and merges can share data for two sets where +// one is a small delta from the other. Doing this does require a +// little care in how sets are updated, both in mergePredecessors, and +// using its result. +func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) { // Filter out back branches. var predsBuf [10]*Block + preds := predsBuf[:0] + locs := blockLocs[b.ID] + + blockChanged := !locs.everProcessed // the first time it always changes. + updating := locs.everProcessed + + // For the first merge, exclude predecessors that have not been seen yet. + // I.e., backedges. for _, pred := range b.Preds { - if blockLocs[pred.b.ID] != nil { + if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed { + // crucially, a self-edge has bl != nil, but bl.everProcessed is false the first time. preds = append(preds, pred.b) } } - if state.loggingEnabled { + locs.everProcessed = true + + if state.loggingLevel > 1 { // The logf below would cause preds to be heap-allocated if // it were passed directly. preds2 := make([]*Block, len(preds)) copy(preds2, preds) - state.logf("Merging %v into %v\n", preds2, b) + state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime) } - // TODO all the calls to this are overkill; only need to do this for slots that are not present in the merge. - markChangedVars := func(slots []liveSlot) { - for _, live := range slots { - state.changedVars.add(ID(state.slotVars[live.slot])) + state.changedVars.clear() + + markChangedVars := func(slots, merged abt.T) { + if !forLocationLists { + return } + // Fill changedVars with those that differ between the previous + // block (in the emit order, not necessarily a flow predecessor) + // and the start state for this block. + for it := slots.Iterator(); !it.Done(); { + k, v := it.Next() + m := merged.Find(k) + if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc { + state.changedVars.add(ID(state.slotVars[k])) + } + } + } + + reset := func(ourStartState abt.T) { + if !(forLocationLists || blockChanged) { + // there is no change and this is not for location lists, do + // not bother to reset currentState because it will not be + // examined. + return + } + state.currentState.reset(ourStartState) } + // Zero predecessors if len(preds) == 0 { if previousBlock != nil { - // Mark everything in previous block as changed because it is not a predecessor. - markChangedVars(blockLocs[previousBlock.ID].endState) + state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String()) } - state.currentState.reset(nil) - return nil, true + // startState is empty + reset(abt.T{}) + return abt.T{}, blockChanged } - p0 := blockLocs[preds[0].ID].endState + // One predecessor + l0 := blockLocs[preds[0].ID] + p0 := l0.endState if len(preds) == 1 { if previousBlock != nil && preds[0].ID != previousBlock.ID { - // Mark everything in previous block as changed because it is not a predecessor. - markChangedVars(blockLocs[previousBlock.ID].endState) + // Change from previous block is its endState minus the predecessor's endState + markChangedVars(blockLocs[previousBlock.ID].endState, p0) + } + locs.startState = p0 + blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime + reset(p0) + return p0, blockChanged + } + + // More than one predecessor + + if updating { + // After the first approximation, i.e., when updating, results + // can only get smaller, because initially backedge + // predecessors do not participate in the intersection. This + // means that for the update, given the prior approximation of + // startState, there is no need to re-intersect with unchanged + // blocks. Therefore remove unchanged blocks from the + // predecessor list. + for i := len(preds) - 1; i >= 0; i-- { + pred := preds[i] + if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime { + continue // keep this predecessor + } + preds[i] = preds[len(preds)-1] + preds = preds[:len(preds)-1] + if state.loggingLevel > 2 { + state.logf("Pruned b%d, lastChanged was %d but b%d lastChecked is %d\n", pred.ID, blockLocs[pred.ID].lastChangedTime, b.ID, locs.lastCheckedTime) + } + } + // Check for an early out; this should always hit for the update + // if there are no cycles. + if len(preds) == 0 { + blockChanged = false + + reset(locs.startState) + if state.loggingLevel > 2 { + state.logf("Early out, no predecessors changed since last check\n") + } + if previousBlock != nil { + markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState) + } + return locs.startState, blockChanged } - state.currentState.reset(p0) - return p0, true } baseID := preds[0].ID baseState := p0 - // If previous block is not a predecessor, its location information changes at boundary with this block. - previousBlockIsNotPredecessor := previousBlock != nil // If it's nil, no info to change. - - if previousBlock != nil { - // Try to use previousBlock as the base state - // if possible. - for _, pred := range preds[1:] { - if pred.ID == previousBlock.ID { - baseID = pred.ID - baseState = blockLocs[pred.ID].endState - previousBlockIsNotPredecessor = false - break - } + // Choose the predecessor with the smallest endState for intersection work + for _, pred := range preds[1:] { + if blockLocs[pred.ID].endState.Size() < baseState.Size() { + baseState = blockLocs[pred.ID].endState + baseID = pred.ID } } - if state.loggingEnabled { + if state.loggingLevel > 2 { state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID])) - } - - slotLocs := state.currentState.slots - for _, predSlot := range baseState { - slotLocs[predSlot.slot] = VarLoc{predSlot.Registers, predSlot.StackOffset} - state.liveCount[predSlot.slot] = 1 - } - for _, pred := range preds { - if pred.ID == baseID { - continue - } - if state.loggingEnabled { - state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID])) - } - for _, predSlot := range blockLocs[pred.ID].endState { - state.liveCount[predSlot.slot]++ - liveLoc := slotLocs[predSlot.slot] - if !liveLoc.onStack() || !predSlot.onStack() || liveLoc.StackOffset != predSlot.StackOffset { - liveLoc.StackOffset = 0 + for _, pred := range preds { + if pred.ID == baseID { + continue } - liveLoc.Registers &= predSlot.Registers - slotLocs[predSlot.slot] = liveLoc + state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID])) } } - // Check if the final state is the same as the first predecessor's - // final state, and reuse it if so. In principle it could match any, - // but it's probably not worth checking more than the first. - unchanged := true - for _, predSlot := range baseState { - if state.liveCount[predSlot.slot] != len(preds) || - slotLocs[predSlot.slot].Registers != predSlot.Registers || - slotLocs[predSlot.slot].StackOffset != predSlot.StackOffset { - unchanged = false - break - } - } - if unchanged { - if state.loggingEnabled { - state.logf("After merge, %v matches b%v exactly.\n", b, baseID) - } - if previousBlockIsNotPredecessor { - // Mark everything in previous block as changed because it is not a predecessor. - markChangedVars(blockLocs[previousBlock.ID].endState) - } - state.currentState.reset(baseState) - return baseState, true - } + state.currentState.reset(abt.T{}) + // The normal logic of "reset" is incuded in the intersection loop below. - for reg := range state.currentState.registers { - state.currentState.registers[reg] = state.currentState.registers[reg][:0] + slotLocs := state.currentState.slots + + // If this is the first call, do updates on the "baseState"; if this + // is a subsequent call, tweak the startState instead. Note that + // these "set" values are values; there are no side effects to + // other values as these are modified. + newState := baseState + if updating { + newState = blockLocs[b.ID].startState } - // A slot is live if it was seen in all predecessors, and they all had - // some storage in common. - for _, predSlot := range baseState { - slotLoc := slotLocs[predSlot.slot] + for it := newState.Iterator(); !it.Done(); { + k, d := it.Next() + thisSlot := d.(*liveSlot) + x := thisSlot.VarLoc + x0 := x // initial value in newState + + // Intersect this slot with the slot in all the predecessors + for _, other := range preds { + if !updating && other.ID == baseID { + continue + } + otherSlot := blockLocs[other.ID].endState.Find(k) + if otherSlot == nil { + x = VarLoc{} + break + } + y := otherSlot.(*liveSlot).VarLoc + x = x.intersect(y) + if x.absent() { + x = VarLoc{} + break + } + } - if state.liveCount[predSlot.slot] != len(preds) { - // Seen in only some predecessors. Clear it out. - slotLocs[predSlot.slot] = VarLoc{} + // Delete if necessary, but not otherwise (in order to maximize sharing). + if x.absent() { + if !x0.absent() { + blockChanged = true + newState.Delete(k) + } + slotLocs[k] = VarLoc{} continue } + if x != x0 { + blockChanged = true + newState.Insert(k, &liveSlot{VarLoc: x}) + } - // Present in all predecessors. - mask := uint64(slotLoc.Registers) + slotLocs[k] = x + mask := uint64(x.Registers) for { if mask == 0 { break } reg := uint8(bits.TrailingZeros64(mask)) mask &^= 1 << reg - state.currentState.registers[reg] = append(state.currentState.registers[reg], predSlot.slot) + state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k)) } } - if previousBlockIsNotPredecessor { - // Mark everything in previous block as changed because it is not a predecessor. - markChangedVars(blockLocs[previousBlock.ID].endState) - + if previousBlock != nil { + markChangedVars(blockLocs[previousBlock.ID].endState, newState) } - return nil, false + locs.startState = newState + return newState, blockChanged } -// processValue updates locs and state.registerContents to reflect v, a value with -// the names in vSlots and homed in vReg. "v" becomes visible after execution of -// the instructions evaluating it. It returns which VarIDs were modified by the -// Value's execution. +// processValue updates locs and state.registerContents to reflect v, a +// value with the names in vSlots and homed in vReg. "v" becomes +// visible after execution of the instructions evaluating it. It +// returns which VarIDs were modified by the Value's execution. func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool { locs := state.currentState changed := false setSlot := func(slot SlotID, loc VarLoc) { changed = true state.changedVars.add(ID(state.slotVars[slot])) + state.changedSlots.add(ID(slot)) state.currentState.slots[slot] = loc } @@ -924,7 +1063,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) clobbers &^= 1 << reg for _, slot := range locs.registers[reg] { - if state.loggingEnabled { + if state.loggingLevel > 1 { state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg]) } @@ -941,7 +1080,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) } switch { - case v.Op == OpVarDef, v.Op == OpVarKill: + case v.Op == OpVarDef: n := v.Aux.(*ir.Name) if ir.IsSynthetic(n) { break @@ -953,7 +1092,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1) } setSlot(slotID, VarLoc{0, stackOffset}) - if state.loggingEnabled { + if state.loggingLevel > 1 { if v.Op == OpVarDef { state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID]) } else { @@ -965,7 +1104,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) home := state.f.getHome(v.ID).(LocalSlot) stackOffset := state.stackOffset(home)<<1 | 1 for _, slot := range vSlots { - if state.loggingEnabled { + if state.loggingLevel > 1 { state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home) if last := locs.slots[slot]; !last.absent() { state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot]) @@ -981,20 +1120,20 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) for _, slot := range vSlots { last := locs.slots[slot] if last.absent() { - if state.loggingEnabled { + if state.loggingLevel > 1 { state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg) } break } setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)}) - if state.loggingEnabled { - state.logf("at %v: %v spilled to stack location %v\n", v, state.slots[slot], home) + if state.loggingLevel > 1 { + state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home)) } } case vReg != nil: - if state.loggingEnabled { + if state.loggingLevel > 1 { newSlots := make([]bool, len(state.slots)) for _, slot := range vSlots { newSlots[slot] = true @@ -1014,7 +1153,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) locs.registers[vReg.num] = locs.registers[vReg.num][:0] locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...) for _, slot := range vSlots { - if state.loggingEnabled { + if state.loggingLevel > 1 { state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg) } @@ -1066,8 +1205,10 @@ func (e *pendingEntry) clear() { } } -// canMerge reports whether the location description for new is the same as -// pending. +// canMerge reports whether a new location description is a superset +// of the (non-empty) pending location description, if so, the two +// can be merged (i.e., pending is still a valid and useful location +// description). func canMerge(pending, new VarLoc) bool { if pending.absent() && new.absent() { return true @@ -1075,13 +1216,18 @@ func canMerge(pending, new VarLoc) bool { if pending.absent() || new.absent() { return false } - if pending.onStack() { - return pending.StackOffset == new.StackOffset + // pending is not absent, therefore it has either a stack mapping, + // or registers, or both. + if pending.onStack() && pending.StackOffset != new.StackOffset { + // if pending has a stack offset, then new must also, and it + // must be the same (StackOffset encodes onStack). + return false } - if pending.Registers != 0 && new.Registers != 0 { - return firstReg(pending.Registers) == firstReg(new.Registers) + if pending.Registers&new.Registers != pending.Registers { + // There is at least one register in pending not mentioned in new. + return false } - return false + return true } // firstReg returns the first register in set that is present. @@ -1094,24 +1240,26 @@ func firstReg(set RegisterSet) uint8 { return uint8(bits.TrailingZeros64(uint64(set))) } -// buildLocationLists builds location lists for all the user variables in -// state.f, using the information about block state in blockLocs. -// The returned location lists are not fully complete. They are in terms of -// SSA values rather than PCs, and have no base address/end entries. They will -// be finished by PutLocationList. +// buildLocationLists builds location lists for all the user variables +// in state.f, using the information about block state in blockLocs. +// The returned location lists are not fully complete. They are in +// terms of SSA values rather than PCs, and have no base address/end +// entries. They will be finished by PutLocationList. func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { // Run through the function in program text order, building up location // lists as we go. The heavy lifting has mostly already been done. var prevBlock *Block for _, b := range state.f.Blocks { - state.mergePredecessors(b, blockLocs, prevBlock) + state.mergePredecessors(b, blockLocs, prevBlock, true) + + // Handle any differences among predecessor blocks and previous block (perhaps not a predecessor) + for _, varID := range state.changedVars.contents() { + state.updateVar(VarID(varID), b, BlockStart) + } + state.changedVars.clear() if !blockLocs[b.ID].relevant { - // Handle any differences among predecessor blocks and previous block (perhaps not a predecessor) - for _, varID := range state.changedVars.contents() { - state.updateVar(VarID(varID), b, BlockStart) - } continue } @@ -1120,60 +1268,99 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { v.Op == OpArgIntReg || v.Op == OpArgFloatReg } + blockPrologComplete := func(v *Value) bool { + if b.ID != state.f.Entry.ID { + return !opcodeTable[v.Op].zeroWidth + } else { + return v.Op == OpInitMem + } + } + + // Examine the prolog portion of the block to process special + // zero-width ops such as Arg, Phi, LoweredGetClosurePtr (etc) + // whose lifetimes begin at the block starting point. In an + // entry block, allow for the possibility that we may see Arg + // ops that appear _after_ other non-zero-width operations. + // Example: + // + // v33 = ArgIntReg {foo+0} [0] : AX (foo) + // v34 = ArgIntReg {bar+0} [0] : BX (bar) + // ... + // v77 = StoreReg v67 : ctx+8[unsafe.Pointer] + // v78 = StoreReg v68 : ctx[unsafe.Pointer] + // v79 = Arg <*uint8> {args} : args[*uint8] (args[*uint8]) + // v80 = Arg {args} [8] : args+8[int] (args+8[int]) + // ... + // v1 = InitMem + // + // We can stop scanning the initial portion of the block when + // we either see the InitMem op (for entry blocks) or the + // first non-zero-width op (for other blocks). + for idx := 0; idx < len(b.Values); idx++ { + v := b.Values[idx] + if blockPrologComplete(v) { + break + } + // Consider only "lifetime begins at block start" ops. + if !mustBeFirst(v) && v.Op != OpArg { + continue + } + slots := state.valueNames[v.ID] + reg, _ := state.f.getHome(v.ID).(*Register) + changed := state.processValue(v, slots, reg) // changed == added to state.changedVars + if changed { + for _, varID := range state.changedVars.contents() { + state.updateVar(VarID(varID), v.Block, BlockStart) + } + state.changedVars.clear() + } + } + + // Now examine the block again, handling things other than the + // "begins at block start" lifetimes. zeroWidthPending := false - blockPrologComplete := false // set to true at first non-zero-width op - apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr + prologComplete := false // expect to see values in pattern (apc)* (zerowidth|real)* for _, v := range b.Values { + if blockPrologComplete(v) { + prologComplete = true + } slots := state.valueNames[v.ID] reg, _ := state.f.getHome(v.ID).(*Register) changed := state.processValue(v, slots, reg) // changed == added to state.changedVars if opcodeTable[v.Op].zeroWidth { + if prologComplete && mustBeFirst(v) { + panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func)) + } if changed { if mustBeFirst(v) || v.Op == OpArg { - // These ranges begin at true beginning of block, not after first instruction - if blockPrologComplete && mustBeFirst(v) { - panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func)) - } - apcChangedSize = len(state.changedVars.contents()) - // Other zero-width ops must wait on a "real" op. - zeroWidthPending = true + // already taken care of above continue } + zeroWidthPending = true } continue } - if !changed && !zeroWidthPending { continue } - // Not zero-width; i.e., a "real" instruction. + // Not zero-width; i.e., a "real" instruction. zeroWidthPending = false - blockPrologComplete = true - for i, varID := range state.changedVars.contents() { - if i < apcChangedSize { // buffered true start-of-block changes - state.updateVar(VarID(varID), v.Block, BlockStart) - } else { - state.updateVar(VarID(varID), v.Block, v) - } + for _, varID := range state.changedVars.contents() { + state.updateVar(VarID(varID), v.Block, v) } state.changedVars.clear() - apcChangedSize = 0 } - for i, varID := range state.changedVars.contents() { - if i < apcChangedSize { // buffered true start-of-block changes - state.updateVar(VarID(varID), b, BlockStart) - } else { - state.updateVar(VarID(varID), b, BlockEnd) - } + for _, varID := range state.changedVars.contents() { + state.updateVar(VarID(varID), b, BlockEnd) } prevBlock = b } - if state.loggingEnabled { + if state.loggingLevel > 0 { state.logf("location lists:\n") } @@ -1181,7 +1368,7 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { for varID := range state.lists { state.writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, FuncEnd.ID) list := state.lists[varID] - if state.loggingEnabled { + if state.loggingLevel > 0 { if len(list) == 0 { state.logf("\t%v : empty list\n", state.vars[varID]) } else { @@ -1252,9 +1439,10 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) { return } if start == end { - if state.loggingEnabled { + if state.loggingLevel > 1 { // Printf not logf so not gated by GOSSAFUNC; this should fire very rarely. - fmt.Printf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name) + // TODO this fires a lot, need to figure out why. + state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name) } return } @@ -1267,7 +1455,7 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) { sizeIdx := len(list) list = list[:len(list)+2] - if state.loggingEnabled { + if state.loggingLevel > 1 { var partStrs []string for i, slot := range state.varSlots[varID] { partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i]))) @@ -1349,11 +1537,11 @@ func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) } -// Pack a value and block ID into an address-sized uint, returning encoded -// value and boolean indicating whether the encoding succeeded. For -// 32-bit architectures the process may fail for very large procedures -// (the theory being that it's ok to have degraded debug quality in -// this case). +// Pack a value and block ID into an address-sized uint, returning +// encoded value and boolean indicating whether the encoding succeeded. +// For 32-bit architectures the process may fail for very large +// procedures(the theory being that it's ok to have degraded debug +// quality in this case). func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) { if ctxt.Arch.PtrSize == 8 { result := uint64(b)<<32 | uint64(uint32(v)) @@ -1449,14 +1637,14 @@ func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) // that spills a register arg. It returns the ID of that instruction // Example: // -// b1: -// v3 = ArgIntReg {p1+0} [0] : AX -// ... more arg regs .. -// v4 = ArgFloatReg {f1+0} [0] : X0 -// v52 = MOVQstore {p1} v2 v3 v1 -// ... more stores ... -// v68 = MOVSSstore {f4} v2 v67 v66 -// v38 = MOVQstoreconst {blob} [val=0,off=0] v2 v32 +// b1: +// v3 = ArgIntReg {p1+0} [0] : AX +// ... more arg regs .. +// v4 = ArgFloatReg {f1+0} [0] : X0 +// v52 = MOVQstore {p1} v2 v3 v1 +// ... more stores ... +// v68 = MOVSSstore {f4} v2 v67 v66 +// v38 = MOVQstoreconst {blob} [val=0,off=0] v2 v32 // // Important: locatePrologEnd is expected to work properly only with // optimization turned off (e.g. "-N"). If optimization is enabled @@ -1554,7 +1742,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool { return true } -// BuildFuncDebugNoOptimized constructs a FuncDebug object with +// BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with // entries corresponding to the register-resident input parameters for // the function "f"; it is used when we are compiling without // optimization but the register ABI is enabled. For each reg param, @@ -1562,8 +1750,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool { // the input register, and the second element holds the stack location // of the param (the assumption being that when optimization is off, // each input param reg will be spilled in the prolog. -func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug { - fd := FuncDebug{} +func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) { pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType()) @@ -1577,7 +1764,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta } } if numRegParams == 0 { - return &fd + return } state := debugState{f: f} @@ -1587,7 +1774,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta } // Allocate location lists. - fd.LocationLists = make([][]byte, numRegParams) + rval.LocationLists = make([][]byte, numRegParams) // Locate the value corresponding to the last spill of // an input register. @@ -1603,10 +1790,10 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta n := inp.Name.(*ir.Name) sl := LocalSlot{N: n, Type: inp.Type, Off: 0} - fd.Vars = append(fd.Vars, n) - fd.Slots = append(fd.Slots, sl) - slid := len(fd.VarSlots) - fd.VarSlots = append(fd.VarSlots, []SlotID{SlotID(slid)}) + rval.Vars = append(rval.Vars, n) + rval.Slots = append(rval.Slots, sl) + slid := len(rval.VarSlots) + rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)}) if afterPrologVal == ID(-1) { // This can happen for degenerate functions with infinite @@ -1623,7 +1810,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta // Param is arriving in one or more registers. We need a 2-element // location expression for it. First entry in location list // will correspond to lifetime in input registers. - list, sizeIdx := setupLocList(ctxt, f, fd.LocationLists[pidx], + list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx], BlockStart.ID, afterPrologVal) if list == nil { pidx++ @@ -1649,7 +1836,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta } if len(inp.Registers) > 1 { list = append(list, dwarf.DW_OP_piece) - ts := rtypes[k].Width + ts := rtypes[k].Size() list = dwarf.AppendUleb128(list, uint64(ts)) if padding[k] > 0 { if loggingEnabled { @@ -1688,8 +1875,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta // fill in size ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) - fd.LocationLists[pidx] = list + rval.LocationLists[pidx] = list pidx++ } - return &fd } diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go new file mode 100644 index 00000000000000..23b511ddb2eb4e --- /dev/null +++ b/src/cmd/compile/internal/ssa/debug_lines_test.go @@ -0,0 +1,273 @@ +// Copyright 2021 The Go Authors. 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_test + +import ( + "bufio" + "bytes" + "flag" + "internal/buildcfg" + "runtime" + "sort" + "strings" + + "fmt" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "strconv" + "testing" +) + +// Matches lines in genssa output that are marked "isstmt", and the parenthesized plus-prefixed line number is a submatch +var asmLine *regexp.Regexp = regexp.MustCompile(`^\s[vb][0-9]+\s+[0-9]+\s\(\+([0-9]+)\)`) + +// this matches e.g. ` v123456789 000007 (+9876654310) MOVUPS X15, ""..autotmp_2-32(SP)` + +// Matches lines in genssa output that describe an inlined file. +// Note it expects an unadventurous choice of basename. +var sepRE = regexp.QuoteMeta(string(filepath.Separator)) +var inlineLine *regexp.Regexp = regexp.MustCompile(`^#\s.*` + sepRE + `[-a-zA-Z0-9_]+\.go:([0-9]+)`) + +// this matches e.g. # /pa/inline-dumpxxxx.go:6 + +var testGoArchFlag = flag.String("arch", "", "run test for specified architecture") + +func testGoArch() string { + if *testGoArchFlag == "" { + return runtime.GOARCH + } + return *testGoArchFlag +} + +func hasRegisterABI() bool { + switch testGoArch() { + case "amd64", "arm64", "ppc64", "ppc64le", "riscv": + return true + } + return false +} + +func unixOnly(t *testing.T) { + if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows. + t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin") + } +} + +// testDebugLinesDefault removes the first wanted statement on architectures that are not (yet) register ABI. +func testDebugLinesDefault(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) { + unixOnly(t) + if !hasRegisterABI() { + wantStmts = wantStmts[1:] + } + testDebugLines(t, gcflags, file, function, wantStmts, ignoreRepeats) +} + +func TestDebugLinesSayHi(t *testing.T) { + // This test is potentially fragile, the goal is that debugging should step properly through "sayhi" + // If the blocks are reordered in a way that changes the statement order but execution flows correctly, + // then rearrange the expected numbers. Register abi and not-register-abi also have different sequences, + // at least for now. + + testDebugLinesDefault(t, "-N -l", "sayhi.go", "sayhi", []int{8, 9, 10, 11}, false) +} + +func TestDebugLinesPushback(t *testing.T) { + unixOnly(t) + + switch testGoArch() { + default: + t.Skip("skipped for many architectures") + + case "arm64", "amd64": // register ABI + fn := "(*List[go.shape.int_0]).PushBack" + if buildcfg.Experiment.Unified { + // Unified mangles differently + fn = "(*List[go.shape.int]).PushBack" + } + testDebugLines(t, "-N -l", "pushback.go", fn, []int{17, 18, 19, 20, 21, 22, 24}, true) + } +} + +func TestDebugLinesConvert(t *testing.T) { + unixOnly(t) + + switch testGoArch() { + default: + t.Skip("skipped for many architectures") + + case "arm64", "amd64": // register ABI + fn := "G[go.shape.int_0]" + if buildcfg.Experiment.Unified { + // Unified mangles differently + fn = "G[go.shape.int]" + } + testDebugLines(t, "-N -l", "convertline.go", fn, []int{9, 10, 11}, true) + } +} + +func TestInlineLines(t *testing.T) { + if runtime.GOARCH != "amd64" && *testGoArchFlag == "" { + // As of september 2021, works for everything except mips64, but still potentially fragile + t.Skip("only runs for amd64 unless -arch explicitly supplied") + } + + want := [][]int{{3}, {4, 10}, {4, 10, 16}, {4, 10}, {4, 11, 16}, {4, 11}, {4}, {5, 10}, {5, 10, 16}, {5, 10}, {5, 11, 16}, {5, 11}, {5}} + testInlineStack(t, "inline-dump.go", "f", want) +} + +func TestDebugLines_53456(t *testing.T) { + testDebugLinesDefault(t, "-N -l", "b53456.go", "(*T).Inc", []int{15, 16, 17, 18}, true) +} + +func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte { + testenv.MustHaveGoBuild(t) + + tmpdir, err := ioutil.TempDir("", "debug_lines_test") + if err != nil { + panic(fmt.Sprintf("Problem creating TempDir, error %v", err)) + } + if testing.Verbose() { + fmt.Printf("Preserving temporary directory %s\n", tmpdir) + } else { + defer os.RemoveAll(tmpdir) + } + + source, err := filepath.Abs(filepath.Join("testdata", file)) + if err != nil { + panic(fmt.Sprintf("Could not get abspath of testdata directory and file, %v", err)) + } + + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "foo.o", "-gcflags=-d=ssa/genssa/dump="+function+" "+moreGCFlags, source) + cmd.Dir = tmpdir + cmd.Env = replaceEnv(cmd.Env, "GOSSADIR", tmpdir) + testGoos := "linux" // default to linux + if testGoArch() == "wasm" { + testGoos = "js" + } + cmd.Env = replaceEnv(cmd.Env, "GOOS", testGoos) + cmd.Env = replaceEnv(cmd.Env, "GOARCH", testGoArch()) + + if testing.Verbose() { + fmt.Printf("About to run %s\n", asCommandLine("", cmd)) + } + + var stdout, stderr strings.Builder + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + t.Fatalf("error running cmd %s: %v\nstdout:\n%sstderr:\n%s\n", asCommandLine("", cmd), err, stdout.String(), stderr.String()) + } + + if s := stderr.String(); s != "" { + t.Fatalf("Wanted empty stderr, instead got:\n%s\n", s) + } + + dumpFile := filepath.Join(tmpdir, function+"_01__genssa.dump") + dumpBytes, err := os.ReadFile(dumpFile) + if err != nil { + t.Fatalf("Could not read dump file %s, err=%v", dumpFile, err) + } + return dumpBytes +} + +func sortInlineStacks(x [][]int) { + sort.Slice(x, func(i, j int) bool { + if len(x[i]) != len(x[j]) { + return len(x[i]) < len(x[j]) + } + for k := range x[i] { + if x[i][k] != x[j][k] { + return x[i][k] < x[j][k] + } + } + return false + }) +} + +// testInlineStack ensures that inlining is described properly in the comments in the dump file +func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) { + // this is an inlining reporting test, not an optimization test. -N makes it less fragile + dumpBytes := compileAndDump(t, file, function, "-N") + dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) + dumpLineNum := 0 + var gotStmts []int + var gotStacks [][]int + for dump.Scan() { + line := dump.Text() + dumpLineNum++ + matches := inlineLine.FindStringSubmatch(line) + if len(matches) == 2 { + stmt, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) + } + if testing.Verbose() { + fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) + } + gotStmts = append(gotStmts, int(stmt)) + } else if len(gotStmts) > 0 { + gotStacks = append(gotStacks, gotStmts) + gotStmts = nil + } + } + if len(gotStmts) > 0 { + gotStacks = append(gotStacks, gotStmts) + gotStmts = nil + } + sortInlineStacks(gotStacks) + sortInlineStacks(wantStacks) + if !reflect.DeepEqual(wantStacks, gotStacks) { + t.Errorf("wanted inlines %+v but got %+v", wantStacks, gotStacks) + } + +} + +// testDebugLines compiles testdata/ with flags -N -l and -d=ssa/genssa/dump= +// then verifies that the statement-marked lines in that file are the same as those in wantStmts +// These files must all be short because this is super-fragile. +// "go build" is run in a temporary directory that is normally deleted, unless -test.v +func testDebugLines(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) { + dumpBytes := compileAndDump(t, file, function, gcflags) + dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) + var gotStmts []int + dumpLineNum := 0 + for dump.Scan() { + line := dump.Text() + dumpLineNum++ + matches := asmLine.FindStringSubmatch(line) + if len(matches) == 2 { + stmt, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) + } + if testing.Verbose() { + fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) + } + gotStmts = append(gotStmts, int(stmt)) + } + } + if ignoreRepeats { // remove repeats from gotStmts + newGotStmts := []int{gotStmts[0]} + for _, x := range gotStmts { + if x != newGotStmts[len(newGotStmts)-1] { + newGotStmts = append(newGotStmts, x) + } + } + if !reflect.DeepEqual(wantStmts, newGotStmts) { + t.Errorf("wanted stmts %v but got %v (with repeats still in: %v)", wantStmts, newGotStmts, gotStmts) + } + + } else { + if !reflect.DeepEqual(wantStmts, gotStmts) { + t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts) + } + } +} diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go index 33463125424f27..f3f8eeeb417249 100644 --- a/src/cmd/compile/internal/ssa/debug_test.go +++ b/src/cmd/compile/internal/ssa/debug_test.go @@ -5,7 +5,6 @@ package ssa_test import ( - "bytes" "flag" "fmt" "internal/testenv" @@ -84,7 +83,7 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog // "O" is an explicit indication that we expect it to be optimized out. // For example: // -// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A) +// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A) // // TODO: not implemented for Delve yet, but this is the plan // @@ -93,7 +92,6 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog // go test debug_test.go -args -u // (for Delve) // go test debug_test.go -args -u -d -// func TestNexting(t *testing.T) { testenv.SkipFlaky(t, 37404) @@ -299,7 +297,7 @@ func runDbgr(dbg dbgr, maxNext int) *nextHist { } func runGo(t *testing.T, dir string, args ...string) string { - var stdout, stderr bytes.Buffer + var stdout, stderr strings.Builder cmd := exec.Command(testenv.GoToolPath(t), args...) cmd.Dir = dir if *dryrun { @@ -952,6 +950,9 @@ func (s *ioState) readSimpleExpecting(expectedRE string) tstring { // replaceEnv returns a new environment derived from env // by removing any existing definition of ev and adding ev=evv. func replaceEnv(env []string, ev string, evv string) []string { + if env == nil { + env = os.Environ() + } evplus := ev + "=" var found bool for i, v := range env { diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 753d69cebcd1a3..2293fc01ce6c61 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -135,7 +135,7 @@ func decomposeBuiltInPhi(v *Value) { case v.Type.IsFloat(): // floats are never decomposed, even ones bigger than RegSize case v.Type.Size() > v.Block.Func.Config.RegSize: - v.Fatalf("undecomposed type %s", v.Type) + v.Fatalf("%v undecomposed type %v", v, v.Type) } } diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 7e973ab20591fb..a85d763a30767c 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -24,7 +24,7 @@ type selKey struct { type Abi1RO uint8 // An offset within a parameter's slice of register indices, for abi1. func isBlockMultiValueExit(b *Block) bool { - return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult + return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && b.Controls[0] != nil && b.Controls[0].Op == OpMakeResult } func badVal(s string, v *Value) error { @@ -176,7 +176,7 @@ func (c *registerCursor) hasRegs() bool { type expandState struct { f *Func abi1 *abi.ABIConfig - debug bool + debug int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both) canSSAType func(*types.Type) bool regSize int64 sp *Value @@ -215,7 +215,7 @@ func (x *expandState) isAlreadyExpandedAggregateType(t *types.Type) bool { return false } return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() || - t.Size() > x.regSize && t.IsInteger() + (t.Size() > x.regSize && (t.IsInteger() || (x.f.Config.SoftFloat && t.IsFloat()))) } // offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP @@ -302,7 +302,7 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error) // // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset) @@ -325,7 +325,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString()) } - if x.debug { + if x.debug > 1 { x.Printf("---%s, break\n", selector.Op.String()) } case OpArg: @@ -335,7 +335,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString()) } - if x.debug { + if x.debug > 1 { x.Printf("---OpArg, break\n") } break @@ -380,6 +380,12 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, // The OpLoad was created to load the single field of the IData // This case removes that StructSelect. if leafType != selector.Type { + if x.f.Config.SoftFloat && selector.Type.IsFloat() { + if x.debug > 1 { + x.Printf("---OpLoad, break\n") + } + break // softfloat pass will take care of that + } x.f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString()) } leaf.copyOf(selector) @@ -462,7 +468,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call) leaf.copyOf(w) - if x.debug { + if x.debug > 1 { x.Printf("---new %s\n", w.LongString()) } } @@ -525,11 +531,11 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, case OpComplexReal: ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset) - locs = x.splitSlots(ls, ".real", 0, leafType) + locs = x.splitSlots(ls, ".real", 0, selector.Type) case OpComplexImag: - ls := x.rewriteSelect(leaf, selector.Args[0], offset+leafType.Width, regOffset+RO_complex_imag) // result is FloatNN, width of result is offset of imaginary part. - locs = x.splitSlots(ls, ".imag", leafType.Width, leafType) + ls := x.rewriteSelect(leaf, selector.Args[0], offset+selector.Type.Size(), regOffset+RO_complex_imag) // result is FloatNN, width of result is offset of imaginary part. + locs = x.splitSlots(ls, ".imag", selector.Type.Size(), selector.Type) case OpStringLen, OpSliceLen: ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize, regOffset+RO_slice_len) @@ -610,7 +616,7 @@ outer: } return path case types.TINT64, types.TUINT64: - if container.Width == x.regSize { + if container.Size() == x.regSize { return path } if offset == x.hiOffset { @@ -650,15 +656,16 @@ outer: // It decomposes a Load or an Arg into smaller parts and returns the new mem. // If the type does not match one of the expected aggregate types, it returns nil instead. // Parameters: -// pos -- the location of any generated code. -// b -- the block into which any generated code should normally be placed -// source -- the value, possibly an aggregate, to be stored. -// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) -// t -- the type of the value to be stored -// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + storeOffset -// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. -// storeRc -- storeRC; if the value is stored in registers, this specifies the registers. -// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. +// +// pos -- the location of any generated code. +// b -- the block into which any generated code should normally be placed +// source -- the value, possibly an aggregate, to be stored. +// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) +// t -- the type of the value to be stored +// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + storeOffset +// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. +// storeRc -- storeRC; if the value is stored in registers, this specifies the registers. +// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { pa := x.prAssignForArg(source) @@ -676,19 +683,19 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t for i := 0; i < len(rts); i++ { rt := rts[i] off := offs[i] - fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Width, rt.Align) + fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Size(), uint8(rt.Alignment())) } panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString())) } - if x.debug { + if x.debug > 1 { x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs)) } for i := loadRegOffset; i < last; i++ { rt := rts[i] off := offs[i] - w := x.commonArgs[selKey{source, off, rt.Width, rt}] + w := x.commonArgs[selKey{source, off, rt.Size(), rt}] if w == nil { w = x.newArgToMemOrRegs(source, w, off, i, rt, pos) suffix := x.pathTo(source.Type, rt, off) @@ -699,7 +706,7 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t if t.IsPtrShaped() { // Preserve the original store type. This ensures pointer type // properties aren't discarded (e.g, notinheap). - if rt.Width != t.Width || len(pa.Registers) != 1 || i != loadRegOffset { + if rt.Size() != t.Size() || len(pa.Registers) != 1 || i != loadRegOffset { b.Func.Fatalf("incompatible store type %v and %v, i=%d", t, rt, i) } rt = t @@ -730,7 +737,7 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t } return mem case types.TINT64, types.TUINT64: - if t.Width == x.regSize { + if t.Size() == x.regSize { break } tHi, tLo := x.intPairTypes(t.Kind()) @@ -771,15 +778,16 @@ func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off // It decomposes a Load into smaller parts and returns the new mem. // If the type does not match one of the expected aggregate types, it returns nil instead. // Parameters: -// pos -- the location of any generated code. -// b -- the block into which any generated code should normally be placed -// source -- the value, possibly an aggregate, to be stored. -// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) -// t -- the type of the value to be stored -// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + offset -// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. -// storeRc -- storeRC; if the value is stored in registers, this specifies the registers. -// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. +// +// pos -- the location of any generated code. +// b -- the block into which any generated code should normally be placed +// source -- the value, possibly an aggregate, to be stored. +// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it) +// t -- the type of the value to be stored +// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + offset +// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg. +// storeRc -- storeRC; if the value is stored in registers, this specifies the registers. +// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation. // // TODO -- this needs cleanup; it just works for SSA-able aggregates, and won't fully generalize to register-args aggregates. func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { @@ -804,7 +812,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, } return mem case types.TINT64, types.TUINT64: - if t.Width == x.regSize { + if t.Size() == x.regSize { break } tHi, tLo := x.intPairTypes(t.Kind()) @@ -830,13 +838,13 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, // pos and b locate the store instruction, source is the "base" of the value input, // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases. func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String()) } - w := x.commonArgs[selKey{source, argOffset, t.Width, t}] + w := x.commonArgs[selKey{source, argOffset, t.Size(), t}] if w == nil { w = x.newArgToMemOrRegs(source, w, argOffset, loadRegOffset, t, pos) x.splitSlotsIntoNames(locs, suffix, argOffset, t, w) @@ -846,7 +854,7 @@ func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suff // storeOneLoad creates a decomposed (one step) load that is then stored. func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { - from := x.offsetFrom(b, source.Args[0], offArg, types.NewPtr(t)) + from := x.offsetFrom(source.Block, source.Args[0], offArg, types.NewPtr(t)) w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem) return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc) } @@ -871,7 +879,7 @@ func storeTwoLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t1 // stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg. // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering. func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("storeArgOrLoad(%s; %s; %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String()) @@ -917,7 +925,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, case OpComplexMake: tPart := x.typs.Float32 - wPart := t.Width / 2 + wPart := t.Size() / 2 if wPart == 8 { tPart = x.typs.Float64 } @@ -946,22 +954,23 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, switch t.Kind() { case types.TARRAY: elt := t.Elem() - if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == x.regSize { + if source.Type != t && t.NumElem() == 1 && elt.Size() == t.Size() && t.Size() == x.regSize { t = removeTrivialWrapperTypes(t) // it could be a leaf type, but the "leaf" could be complex64 (for example) return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) } eltRO := x.regWidth(elt) + source.Type = t for i := int64(0); i < t.NumElem(); i++ { sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source) - mem = x.storeArgOrLoad(pos, b, sel, mem, elt, storeOffset+i*elt.Width, loadRegOffset, storeRc.at(t, 0)) + mem = x.storeArgOrLoad(pos, b, sel, mem, elt, storeOffset+i*elt.Size(), loadRegOffset, storeRc.at(t, 0)) loadRegOffset += eltRO pos = pos.WithNotStmt() } return mem case types.TSTRUCT: - if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == x.regSize { + if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Size() == t.Size() && t.Size() == x.regSize { // This peculiar test deals with accesses to immediate interface data. // It works okay because everything is the same size. // Example code that triggers this can be found in go/constant/value.go, function ToComplex @@ -985,6 +994,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc) } + source.Type = t for i := 0; i < t.NumFields(); i++ { fld := t.Field(i) sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source) @@ -995,7 +1005,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, return mem case types.TINT64, types.TUINT64: - if t.Width == x.regSize { + if t.Size() == x.regSize { break } tHi, tLo := x.intPairTypes(t.Kind()) @@ -1054,7 +1064,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t)) s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem) } - if x.debug { + if x.debug > 1 { x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String()) } return s @@ -1065,18 +1075,23 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, // to account for any parameter stores required. // Any of the old Args that have their use count fall to zero are marked OpInvalid. func (x *expandState) rewriteArgs(v *Value, firstArg int) { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg) } // Thread the stores on the memory arg aux := v.Aux.(*AuxCall) - pos := v.Pos.WithNotStmt() m0 := v.MemoryArg() mem := m0 newArgs := []*Value{} oldArgs := []*Value{} + sp := x.sp + if v.Op == OpTailLECall { + // For tail call, we unwind the frame before the call so we'll use the caller's + // SP. + sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr) + } for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg. oldArgs = append(oldArgs, a) auxI := int64(i) @@ -1087,9 +1102,20 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { if a.MemoryArg() != m0 { x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) } + if v.Op == OpTailLECall { + // It's common for a tail call passing the same arguments (e.g. method wrapper), + // so this would be a self copy. Detect this and optimize it out. + a0 := a.Args[0] + if a0.Op == OpLocalAddr { + n := a0.Aux.(*ir.Name) + if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset { + continue + } + } + } // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move // TODO(register args) this will be more complicated with registers in the picture. - mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, pos) + mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) } else { var rc registerCursor var result *[]*Value @@ -1099,11 +1125,19 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { } else { aOffset = aux.OffsetOfArg(auxI) } - if x.debug { + if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 { + // It's common for a tail call passing the same arguments (e.g. method wrapper), + // so this would be a self copy. Detect this and optimize it out. + n := a.Aux.(*ir.Name) + if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset { + continue + } + } + if x.debug > 1 { x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) } - rc.init(aRegs, aux.abiInfo, result, x.sp) - mem = x.storeArgOrLoad(pos, v.Block, a, mem, aType, aOffset, 0, rc) + rc.init(aRegs, aux.abiInfo, result, sp) + mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc) } } var preArgStore [2]*Value @@ -1114,16 +1148,31 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { v.AddArg(mem) for _, a := range oldArgs { if a.Uses == 0 { - if x.debug { - x.Printf("...marking %v unused\n", a.LongString()) - } - a.invalidateRecursively() + x.invalidateRecursively(a) } } return } +func (x *expandState) invalidateRecursively(a *Value) { + var s string + if x.debug > 0 { + plus := " " + if a.Pos.IsStmt() == src.PosIsStmt { + plus = " +" + } + s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString() + if x.debug > 1 { + x.Printf("...marking %v unused\n", s) + } + } + lost := a.invalidateRecursively() + if x.debug&1 != 0 && lost { // For odd values of x.debug, do this. + x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s) + } +} + // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form // that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are @@ -1142,7 +1191,7 @@ func expandCalls(f *Func) { x := &expandState{ f: f, abi1: f.ABI1, - debug: f.pass.debug > 0, + debug: f.pass.debug, canSSAType: f.fe.CanSSA, regSize: f.Config.RegSize, sp: sp, @@ -1164,7 +1213,7 @@ func expandCalls(f *Func) { x.loRo, x.hiRo = 0, 1 } - if x.debug { + if x.debug > 1 { x.Printf("\nexpandsCalls(%s)\n", f.Name) } @@ -1187,7 +1236,7 @@ func expandCalls(f *Func) { for _, v := range b.Values { firstArg := 0 switch v.Op { - case OpStaticLECall: + case OpStaticLECall, OpTailLECall: case OpInterLECall: firstArg = 1 case OpClosureLECall: @@ -1204,9 +1253,8 @@ func expandCalls(f *Func) { m0 := v.MemoryArg() mem := m0 aux := f.OwnAux - pos := v.Pos.WithNotStmt() allResults := []*Value{} - if x.debug { + if x.debug > 1 { x.Printf("multiValueExit rewriting %s\n", v.LongString()) } var oldArgs []*Value @@ -1227,7 +1275,7 @@ func expandCalls(f *Func) { } continue } - mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos) + mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos) } else { if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr { addr := a.Args[0] // This is a self-move. // TODO(register args) do what here for registers? @@ -1251,13 +1299,13 @@ func expandCalls(f *Func) { b.SetControl(v) for _, a := range oldArgs { if a.Uses == 0 { - if x.debug { + if x.debug > 1 { x.Printf("...marking %v unused\n", a.LongString()) } - a.invalidateRecursively() + x.invalidateRecursively(a) } } - if x.debug { + if x.debug > 1 { x.Printf("...multiValueExit new result %s\n", v.LongString()) } x.indent(-3) @@ -1311,7 +1359,7 @@ func expandCalls(f *Func) { switch w.Op { case OpStructSelect, OpArraySelect, OpSelectN, OpArg: val2Preds[w] += 1 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) } } @@ -1320,7 +1368,7 @@ func expandCalls(f *Func) { case OpSelectN: if _, ok := val2Preds[v]; !ok { val2Preds[v] = 0 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) } } @@ -1331,7 +1379,7 @@ func expandCalls(f *Func) { } if _, ok := val2Preds[v]; !ok { val2Preds[v] = 0 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) } } @@ -1416,7 +1464,7 @@ func expandCalls(f *Func) { if typ.IsMemory() { continue // handled elsewhere, not an indexable result } - size := typ.Width + size := typ.Size() offset := int64(0) switch v.Op { case OpStructSelect: @@ -1445,7 +1493,7 @@ func expandCalls(f *Func) { if dupe == nil { x.commonSelectors[sk] = v } else if x.sdom.IsAncestorEq(dupe.Block, v.Block) { - if x.debug { + if x.debug > 1 { x.Printf("Duplicate, make %s copy of %s\n", v, dupe) } v.copyOf(dupe) @@ -1461,12 +1509,12 @@ func expandCalls(f *Func) { // Rewrite selectors. for i, v := range allOrdered { - if x.debug { + if x.debug > 1 { b := v.Block x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses) } if v.Uses == 0 { - v.invalidateRecursively() + x.invalidateRecursively(v) continue } if v.Op == OpCopy { @@ -1506,6 +1554,10 @@ func expandCalls(f *Func) { v.Op = OpStaticCall rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) v.Type = types.NewResults(append(rts, types.TypeMem)) + case OpTailLECall: + v.Op = OpTailCall + rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) + v.Type = types.NewResults(append(rts, types.TypeMem)) case OpClosureLECall: v.Op = OpClosureCall rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) @@ -1528,7 +1580,7 @@ func expandCalls(f *Func) { case OpArgIntReg: i := v.AuxInt if w := IArg[i]; w != nil { - if w.Type.Width != v.Type.Width { + if w.Type.Size() != v.Type.Size() { f.Fatalf("incompatible OpArgIntReg [%d]: %s and %s", i, v.LongString(), w.LongString()) } if w.Type.IsUnsafePtr() && !v.Type.IsUnsafePtr() { @@ -1543,7 +1595,7 @@ func expandCalls(f *Func) { case OpArgFloatReg: i := v.AuxInt if w := FArg[i]; w != nil { - if w.Type.Width != v.Type.Width { + if w.Type.Size() != v.Type.Size() { f.Fatalf("incompatible OpArgFloatReg [%d]: %v and %v", i, v, w) } v.copyOf(w) @@ -1577,7 +1629,7 @@ func expandCalls(f *Func) { v.SetArg(i, aa) for a.Uses == 0 { b := a.Args[0] - a.invalidateRecursively() + x.invalidateRecursively(a) a = b } } @@ -1613,7 +1665,7 @@ func expandCalls(f *Func) { // rewriteArgToMemOrRegs converts OpArg v in-place into the register version of v, // if that is appropriate. func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString()) @@ -1628,9 +1680,9 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { } case 1: t := v.Type - key := selKey{v, 0, t.Width, t} + key := selKey{v, 0, t.Size(), t} w := x.commonArgs[key] - if w != nil { + if w != nil && w.Uses != 0 { // do not reuse dead value v.copyOf(w) break } @@ -1644,7 +1696,7 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { default: panic(badVal("Saw unexpanded OpArg", v)) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", v.LongString()) } return v @@ -1654,16 +1706,22 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { // or rewrites it into a copy of the appropriate OpArgXXX. The actual OpArgXXX is determined by combining baseArg (an OpArg) // with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers). func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset) } - key := selKey{baseArg, offset, t.Width, t} + key := selKey{baseArg, offset, t.Size(), t} w := x.commonArgs[key] - if w != nil { + if w != nil && w.Uses != 0 { // do not reuse dead value if toReplace != nil { toReplace.copyOf(w) + if x.debug > 1 { + x.Printf("...replace %s\n", toReplace.LongString()) + } + } + if x.debug > 1 { + x.Printf("-->%s\n", w.LongString()) } return w } @@ -1684,13 +1742,13 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, toReplace.Type = t w = toReplace } else { - w = baseArg.Block.NewValue0IA(pos, OpArg, t, auxInt, aux) + w = baseArg.Block.NewValue0IA(baseArg.Pos, OpArg, t, auxInt, aux) } x.commonArgs[key] = w if toReplace != nil { toReplace.copyOf(w) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", w.LongString()) } return w @@ -1715,13 +1773,13 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, toReplace.Type = t w = toReplace } else { - w = baseArg.Block.NewValue0IA(pos, op, t, auxInt, aux) + w = baseArg.Block.NewValue0IA(baseArg.Pos, op, t, auxInt, aux) } x.commonArgs[key] = w if toReplace != nil { toReplace.copyOf(w) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", w.LongString()) } return w diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index 8ed8a0c4a6e3b7..f98437b6297901 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -5,14 +5,16 @@ package ssa import ( + "testing" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/obj/arm64" "cmd/internal/obj/s390x" "cmd/internal/obj/x86" "cmd/internal/src" - "testing" ) var CheckFunc = checkFunc @@ -39,7 +41,7 @@ func testConfigArch(tb testing.TB, arch string) *Conf { tb.Fatal("testTypes is 64-bit only") } c := &Conf{ - config: NewConfig(arch, testTypes, ctxt, true), + config: NewConfig(arch, testTypes, ctxt, true, false), tb: tb, } return c @@ -70,6 +72,7 @@ func (TestFrontend) StringData(s string) *obj.LSym { } func (TestFrontend) Auto(pos src.XPos, t *types.Type) *ir.Name { n := ir.NewNameAt(pos, &types.Sym{Name: "aFakeAuto"}) + n.SetType(t) n.Class = ir.PAUTO return n } @@ -100,37 +103,19 @@ func (d TestFrontend) Debug_checknil() bool { retu func (d TestFrontend) MyImportPath() string { return "my/import/path" } +func (d TestFrontend) LSym() string { + return "my/import/path.function" +} var testTypes Types func init() { - // Initialize just enough of the universe and the types package to make our tests function. - // TODO(josharian): move universe initialization to the types package, - // so this test setup can share it. - - for _, typ := range [...]struct { - width int64 - et types.Kind - }{ - {1, types.TINT8}, - {1, types.TUINT8}, - {1, types.TBOOL}, - {2, types.TINT16}, - {2, types.TUINT16}, - {4, types.TINT32}, - {4, types.TUINT32}, - {4, types.TFLOAT32}, - {4, types.TFLOAT64}, - {8, types.TUINT64}, - {8, types.TINT64}, - {8, types.TINT}, - {8, types.TUINTPTR}, - } { - t := types.New(typ.et) - t.Width = typ.width - t.Align = uint8(typ.width) - types.Types[typ.et] = t - } + // TODO(mdempsky): Push into types.InitUniverse or typecheck.InitUniverse. + types.PtrSize = 8 + types.RegSize = 8 + types.MaxWidth = 1 << 50 + + typecheck.InitUniverse() testTypes.SetTypPtrs() } diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index fac876c23ebc2e..a8eb74efdbef13 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -9,19 +9,12 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/types" "cmd/internal/src" - "crypto/sha1" "fmt" - "io" "math" "os" "strings" ) -type writeSyncer interface { - io.Writer - Sync() error -} - // A Func represents a Go func declaration (or function literal) and its body. // This package compiles each Func independently. // Funcs are single-use; a new Func must be created for every compiled function. @@ -38,12 +31,8 @@ type Func struct { bid idAlloc // block ID allocator vid idAlloc // value ID allocator - // Given an environment variable used for debug hash match, - // what file (if any) receives the yes/no logging? - logfiles map[string]writeSyncer HTMLWriter *HTMLWriter // html writer, for debugging - DebugTest bool // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases - PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. + PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. There's an odd dependence on this in debug.go for method logf. ruleMatches map[string]int // number of times countRule was called during compilation for any given string ABI0 *abi.ABIConfig // A copy, for no-sync access ABI1 *abi.ABIConfig // A copy, for no-sync access @@ -819,83 +808,20 @@ func (f *Func) invalidateCFG() { f.cachedLoopnest = nil } -// DebugHashMatch reports whether environment variable evname -// 1) is empty (this is a special more-quickly implemented case of 3) -// 2) is "y" or "Y" -// 3) is a suffix of the sha1 hash of name -// 4) is a suffix of the environment variable -// fmt.Sprintf("%s%d", evname, n) -// provided that all such variables are nonempty for 0 <= i <= n -// Otherwise it returns false. -// When true is returned the message -// "%s triggered %s\n", evname, name -// is printed on the file named in environment variable -// GSHS_LOGFILE -// or standard out if that is empty or there is an error -// opening the file. -func (f *Func) DebugHashMatch(evname string) bool { - name := f.fe.MyImportPath() + "." + f.Name - evhash := os.Getenv(evname) - switch evhash { - case "": - return true // default behavior with no EV is "on" - case "y", "Y": - f.logDebugHashMatch(evname, name) +// DebugHashMatch returns +// +// base.DebugHashMatch(this function's package.name) +// +// for use in bug isolation. The return value is true unless +// environment variable GOSSAHASH is set, in which case "it depends". +// See [base.DebugHashMatch] for more information. +func (f *Func) DebugHashMatch() bool { + evhash := os.Getenv(base.GOSSAHASH) + if evhash == "" { return true - case "n", "N": - return false - } - // Check the hash of the name against a partial input hash. - // We use this feature to do a binary search to - // find a function that is incorrectly compiled. - hstr := "" - for _, b := range sha1.Sum([]byte(name)) { - hstr += fmt.Sprintf("%08b", b) - } - - if strings.HasSuffix(hstr, evhash) { - f.logDebugHashMatch(evname, name) - return true - } - - // Iteratively try additional hashes to allow tests for multi-point - // failure. - for i := 0; true; i++ { - ev := fmt.Sprintf("%s%d", evname, i) - evv := os.Getenv(ev) - if evv == "" { - break - } - if strings.HasSuffix(hstr, evv) { - f.logDebugHashMatch(ev, name) - return true - } - } - return false -} - -func (f *Func) logDebugHashMatch(evname, name string) { - if f.logfiles == nil { - f.logfiles = make(map[string]writeSyncer) } - file := f.logfiles[evname] - if file == nil { - file = os.Stdout - if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" { - var err error - file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - f.Fatalf("could not open hash-testing logfile %s", tmpfile) - } - } - f.logfiles[evname] = file - } - fmt.Fprintf(file, "%s triggered %s\n", evname, name) - file.Sync() -} - -func DebugNameMatch(evname, name string) bool { - return os.Getenv(evname) == name + name := f.fe.MyImportPath() + "." + f.Name + return base.DebugHashMatch(name) } func (f *Func) spSb() (sp, sb *Value) { diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 276c444b9a88f0..bbb228d8a5fed6 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -34,7 +34,7 @@ package ssa // TODO(matloob): Choose better names for Fun, Bloc, Goto, etc. // TODO(matloob): Write a parser for the Func disassembly. Maybe -// the parser can be used instead of Fun. +// the parser can be used instead of Fun. import ( "cmd/compile/internal/types" diff --git a/src/cmd/compile/internal/ssa/fuse.go b/src/cmd/compile/internal/ssa/fuse.go index fec2ba877372ee..2b176dfa7b2a73 100644 --- a/src/cmd/compile/internal/ssa/fuse.go +++ b/src/cmd/compile/internal/ssa/fuse.go @@ -55,19 +55,21 @@ func fuse(f *Func, typ fuseType) { // fuseBlockIf handles the following cases where s0 and s1 are empty blocks. // -// b b b b -// \ / \ / | \ / \ / | | | -// s0 s1 | s1 s0 | | | -// \ / | / \ | | | -// ss ss ss ss +// b b b b +// \ / \ / | \ / \ / | | | +// s0 s1 | s1 s0 | | | +// \ / | / \ | | | +// ss ss ss ss // // If all Phi ops in ss have identical variables for slots corresponding to // s0, s1 and b then the branch can be dropped. // This optimization often comes up in switch statements with multiple // expressions in a case clause: -// switch n { -// case 1,2,3: return 4 -// } +// +// switch n { +// case 1,2,3: return 4 +// } +// // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway. func fuseBlockIf(b *Block) bool { if b.Kind != BlockIf { diff --git a/src/cmd/compile/internal/ssa/fuse_branchredirect.go b/src/cmd/compile/internal/ssa/fuse_branchredirect.go index 1b8b307bcac7a2..59570968a2751e 100644 --- a/src/cmd/compile/internal/ssa/fuse_branchredirect.go +++ b/src/cmd/compile/internal/ssa/fuse_branchredirect.go @@ -8,21 +8,24 @@ package ssa // of an If block can be derived from its predecessor If block, in // some such cases, we can redirect the predecessor If block to the // corresponding successor block directly. For example: -// p: -// v11 = Less64 v10 v8 -// If v11 goto b else u -// b: <- p ... -// v17 = Leq64 v10 v8 -// If v17 goto s else o +// +// p: +// v11 = Less64 v10 v8 +// If v11 goto b else u +// b: <- p ... +// v17 = Leq64 v10 v8 +// If v17 goto s else o +// // We can redirect p to s directly. // // The implementation here borrows the framework of the prove pass. -// 1, Traverse all blocks of function f to find If blocks. -// 2, For any If block b, traverse all its predecessors to find If blocks. -// 3, For any If block predecessor p, update relationship p->b. -// 4, Traverse all successors of b. -// 5, For any successor s of b, try to update relationship b->s, if a -// contradiction is found then redirect p to another successor of b. +// +// 1, Traverse all blocks of function f to find If blocks. +// 2, For any If block b, traverse all its predecessors to find If blocks. +// 3, For any If block predecessor p, update relationship p->b. +// 4, Traverse all successors of b. +// 5, For any successor s of b, try to update relationship b->s, if a +// contradiction is found then redirect p to another successor of b. func fuseBranchRedirect(f *Func) bool { ft := newFactsTable(f) ft.checkpoint() @@ -78,7 +81,7 @@ func fuseBranchRedirect(f *Func) bool { if v.Op != OpPhi { continue } - v.RemoveArg(k) + b.removePhiArg(v, k) phielimValue(v) } // Fix up child to have one more predecessor. diff --git a/src/cmd/compile/internal/ssa/fuse_comparisons.go b/src/cmd/compile/internal/ssa/fuse_comparisons.go index d843fc3fda1ead..f5fb84b0d73532 100644 --- a/src/cmd/compile/internal/ssa/fuse_comparisons.go +++ b/src/cmd/compile/internal/ssa/fuse_comparisons.go @@ -9,22 +9,22 @@ package ssa // // Look for branch structure like: // -// p -// |\ -// | b -// |/ \ -// s0 s1 +// p +// |\ +// | b +// |/ \ +// s0 s1 // // In our example, p has control '1 <= x', b has control 'x < 5', // and s0 and s1 are the if and else results of the comparison. // // This will be optimized into: // -// p -// \ -// b -// / \ -// s0 s1 +// p +// \ +// b +// / \ +// s0 s1 // // where b has the combined control value 'unsigned(x-1) < 4'. // Later passes will then fuse p and b. diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules index 199b73c42f436f..5e30ca9fd74a12 100644 --- a/src/cmd/compile/internal/ssa/gen/386.rules +++ b/src/cmd/compile/internal/ssa/gen/386.rules @@ -148,10 +148,14 @@ (Rsh16x64 x (Const64 [c])) && uint64(c) >= 16 => (SARWconst x [15]) (Rsh8x64 x (Const64 [c])) && uint64(c) >= 8 => (SARBconst x [7]) +// rotates +(RotateLeft32 ...) => (ROLL ...) +(RotateLeft16 ...) => (ROLW ...) +(RotateLeft8 ...) => (ROLB ...) // constant rotates -(RotateLeft32 x (MOVLconst [c])) => (ROLLconst [c&31] x) -(RotateLeft16 x (MOVLconst [c])) => (ROLWconst [int16(c&15)] x) -(RotateLeft8 x (MOVLconst [c])) => (ROLBconst [int8(c&7)] x) +(ROLL x (MOVLconst [c])) => (ROLLconst [c&31] x) +(ROLW x (MOVLconst [c])) => (ROLWconst [int16(c&15)] x) +(ROLB x (MOVLconst [c])) => (ROLBconst [int8(c&7)] x) // Lowering comparisons (Less32 x y) => (SETL (CMPL x y)) @@ -317,6 +321,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (IsNonNil p) => (SETNE (TESTL p p)) @@ -422,31 +427,6 @@ (SHLL x (ANDLconst [31] y)) => (SHLL x y) (SHRL x (ANDLconst [31] y)) => (SHRL x y) -// Rotate instructions - -(ADDL (SHLLconst [c] x) (SHRLconst [d] x)) && d == 32-c => (ROLLconst [c] x) -( ORL (SHLLconst [c] x) (SHRLconst [d] x)) && d == 32-c => (ROLLconst [c] x) -(XORL (SHLLconst [c] x) (SHRLconst [d] x)) && d == 32-c => (ROLLconst [c] x) - -(ADDL (SHLLconst x [c]) (SHRWconst x [d])) && c < 16 && d == int16(16-c) && t.Size() == 2 - => (ROLWconst x [int16(c)]) -( ORL (SHLLconst x [c]) (SHRWconst x [d])) && c < 16 && d == int16(16-c) && t.Size() == 2 - => (ROLWconst x [int16(c)]) -(XORL (SHLLconst x [c]) (SHRWconst x [d])) && c < 16 && d == int16(16-c) && t.Size() == 2 - => (ROLWconst x [int16(c)]) - -(ADDL (SHLLconst x [c]) (SHRBconst x [d])) && c < 8 && d == int8(8-c) && t.Size() == 1 - => (ROLBconst x [int8(c)]) -( ORL (SHLLconst x [c]) (SHRBconst x [d])) && c < 8 && d == int8(8-c) && t.Size() == 1 - => (ROLBconst x [int8(c)]) -(XORL (SHLLconst x [c]) (SHRBconst x [d])) && c < 8 && d == int8(8-c) && t.Size() == 1 - => (ROLBconst x [int8(c)]) - -(ROLLconst [c] (ROLLconst [d] x)) => (ROLLconst [(c+d)&31] x) -(ROLWconst [c] (ROLWconst [d] x)) => (ROLWconst [(c+d)&15] x) -(ROLBconst [c] (ROLBconst [d] x)) => (ROLBconst [(c+d)& 7] x) - - // Constant shift simplifications (SHLLconst x [0]) => x diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index c4b49fbb23067d..88e061151e10ea 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -274,6 +275,9 @@ func init() { {name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-15 {name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-7 + {name: "ROLL", argLength: 2, reg: gp21shift, asm: "ROLL", resultInArg0: true, clobberFlags: true}, // 32 bits of arg0 rotate left by arg1 + {name: "ROLW", argLength: 2, reg: gp21shift, asm: "ROLW", resultInArg0: true, clobberFlags: true}, // low 16 bits of arg0 rotate left by arg1 + {name: "ROLB", argLength: 2, reg: gp21shift, asm: "ROLB", resultInArg0: true, clobberFlags: true}, // low 8 bits of arg0 rotate left by arg1 {name: "ROLLconst", argLength: 1, reg: gp11, asm: "ROLL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-31 {name: "ROLWconst", argLength: 1, reg: gp11, asm: "ROLW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-15 {name: "ROLBconst", argLength: 1, reg: gp11, asm: "ROLB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-7 @@ -297,7 +301,7 @@ func init() { // unary ops {name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0 - {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0 + {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0 {name: "BSFL", argLength: 1, reg: gp11, asm: "BSFL", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero {name: "BSFW", argLength: 1, reg: gp11, asm: "BSFW", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero @@ -305,7 +309,7 @@ func init() { {name: "BSRL", argLength: 1, reg: gp11, asm: "BSRL", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero {name: "BSRW", argLength: 1, reg: gp11, asm: "BSRW", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero - {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes + {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true}, // arg0 swap bytes {name: "SQRTSD", argLength: 1, reg: fp11, asm: "SQRTSD"}, // sqrt(arg0) {name: "SQRTSS", argLength: 1, reg: fp11, asm: "SQRTSS"}, // sqrt(arg0), float32 @@ -454,6 +458,7 @@ func init() { }, {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 4cd00732fc3f5e..2b6001016d3f70 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -78,24 +78,36 @@ (OffPtr [off] ptr) => (ADDQ (MOVQconst [off]) ptr) // Lowering other arithmetic -(Ctz64 x) => (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x))) -(Ctz32 x) => (Select0 (BSFQ (BTSQconst [32] x))) +(Ctz64 x) && buildcfg.GOAMD64 >= 3 => (TZCNTQ x) +(Ctz32 x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x) +(Ctz64 x) && buildcfg.GOAMD64 < 3 => (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x))) +(Ctz32 x) && buildcfg.GOAMD64 < 3 => (Select0 (BSFQ (BTSQconst [32] x))) (Ctz16 x) => (BSFL (BTSLconst [16] x)) (Ctz8 x) => (BSFL (BTSLconst [ 8] x)) -(Ctz64NonZero x) => (Select0 (BSFQ x)) -(Ctz32NonZero ...) => (BSFL ...) -(Ctz16NonZero ...) => (BSFL ...) -(Ctz8NonZero ...) => (BSFL ...) +(Ctz64NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTQ x) +(Ctz32NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x) +(Ctz16NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x) +(Ctz8NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x) +(Ctz64NonZero x) && buildcfg.GOAMD64 < 3 => (Select0 (BSFQ x)) +(Ctz32NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x) +(Ctz16NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x) +(Ctz8NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x) // BitLen64 of a 64 bit value x requires checking whether x == 0, since BSRQ is undefined when x == 0. // However, for zero-extended values, we can cheat a bit, and calculate // BSR(x<<1 + 1), which is guaranteed to be non-zero, and which conveniently // places the index of the highest set bit where we want it. -(BitLen64 x) => (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) -(BitLen32 x) => (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) -(BitLen16 x) => (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) -(BitLen8 x) => (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) +// For GOAMD64>=3, BitLen can be calculated by OperandSize - LZCNT(x). +(BitLen64 x) && buildcfg.GOAMD64 < 3 => (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) +(BitLen32 x) && buildcfg.GOAMD64 < 3 => (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) +(BitLen16 x) && buildcfg.GOAMD64 < 3 => (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) +(BitLen8 x) && buildcfg.GOAMD64 < 3 => (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) +(BitLen64 x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-64] (LZCNTQ x))) +// Use 64-bit version to allow const-fold remove unnecessary arithmetic. +(BitLen32 x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-32] (LZCNTL x))) +(BitLen16 x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-32] (LZCNTL (MOVWQZX x)))) +(BitLen8 x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-32] (LZCNTL (MOVBQZX x)))) (Bswap(64|32) ...) => (BSWAP(Q|L) ...) @@ -196,6 +208,11 @@ (Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SARW x y) (Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SARB x y) +// Prefer SARX/SHLX/SHRX instruction because it has less register restriction on the shift input. +(SAR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SARX(Q|L) x y) +(SHL(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHLX(Q|L) x y) +(SHR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHRX(Q|L) x y) + // Lowering integer comparisons (Less(64|32|16|8) x y) => (SETL (CMP(Q|L|W|B) x y)) (Less(64|32|16|8)U x y) => (SETB (CMP(Q|L|W|B) x y)) @@ -362,26 +379,26 @@ // Adjust zeros to be a multiple of 16 bytes. (Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE => (Zero [s-s%16] (OffPtr destptr [s%16]) - (MOVOstorezero destptr mem)) + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) (Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE => (Zero [s-s%16] (OffPtr destptr [s%16]) - (MOVQstoreconst [makeValAndOff(0,0)] destptr mem)) + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) (Zero [16] destptr mem) && config.useSSE => - (MOVOstorezero destptr mem) + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem) (Zero [32] destptr mem) && config.useSSE => - (MOVOstorezero (OffPtr destptr [16]) - (MOVOstorezero destptr mem)) + (MOVOstoreconst [makeValAndOff(0,16)] destptr + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) (Zero [48] destptr mem) && config.useSSE => - (MOVOstorezero (OffPtr destptr [32]) - (MOVOstorezero (OffPtr destptr [16]) - (MOVOstorezero destptr mem))) + (MOVOstoreconst [makeValAndOff(0,32)] destptr + (MOVOstoreconst [makeValAndOff(0,16)] destptr + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))) (Zero [64] destptr mem) && config.useSSE => - (MOVOstorezero (OffPtr destptr [48]) - (MOVOstorezero (OffPtr destptr [32]) - (MOVOstorezero (OffPtr destptr [16]) - (MOVOstorezero destptr mem)))) + (MOVOstoreconst [makeValAndOff(0,48)] destptr + (MOVOstoreconst [makeValAndOff(0,32)] destptr + (MOVOstoreconst [makeValAndOff(0,16)] destptr + (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)))) // Medium zeroing uses a duff device. (Zero [s] destptr mem) @@ -408,6 +425,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Lowering conditional moves // If the condition is a SETxx, we can just run a CMOV from the comparison that was @@ -460,12 +478,12 @@ (IsInBounds idx len) => (SETB (CMPQ idx len)) (IsSliceInBounds idx len) => (SETBE (CMPQ idx len)) (NilCheck ...) => (LoweredNilCheck ...) -(GetG mem) && !(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal) => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register. +(GetG mem) && v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register. (GetClosurePtr ...) => (LoweredGetClosurePtr ...) (GetCallerPC ...) => (LoweredGetCallerPC ...) (GetCallerSP ...) => (LoweredGetCallerSP ...) -(HasCPUFeature {s}) => (SETNE (CMPQconst [0] (LoweredHasCPUFeature {s}))) +(HasCPUFeature {s}) => (SETNE (CMPLconst [0] (LoweredHasCPUFeature {s}))) (Addr {sym} base) => (LEAQ {sym} base) (LocalAddr {sym} base _) => (LEAQ {sym} base) @@ -501,6 +519,8 @@ (If cond yes no) => (NE (TESTB cond cond) yes no) +(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (LEAQ {makeJumpTableSym(b)} (SB))) + // Atomic loads. Other than preserving their ordering with respect to other loads, nothing special here. (AtomicLoad8 ptr mem) => (MOVBatomicload ptr mem) (AtomicLoad32 ptr mem) => (MOVLatomicload ptr mem) @@ -543,6 +563,12 @@ (PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem) (PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem) +// lowering rotates +(RotateLeft8 ...) => (ROLB ...) +(RotateLeft16 ...) => (ROLW ...) +(RotateLeft32 ...) => (ROLL ...) +(RotateLeft64 ...) => (ROLQ ...) + // *************************** // Above: lowering rules // Below: optimizations @@ -579,6 +605,8 @@ // mutandis, for UGE and SETAE, and CC and SETCC. ((NE|EQ) (TESTL (SHLL (MOVLconst [1]) x) y)) => ((ULT|UGE) (BTL x y)) ((NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => ((ULT|UGE) (BTQ x y)) +((NE|EQ) (TESTL (SHLXL (MOVLconst [1]) x) y)) => ((ULT|UGE) (BTL x y)) +((NE|EQ) (TESTQ (SHLXQ (MOVQconst [1]) x) y)) => ((ULT|UGE) (BTQ x y)) ((NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c)) => ((ULT|UGE) (BTLconst [int8(log32(c))] x)) ((NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c)) @@ -587,6 +615,8 @@ => ((ULT|UGE) (BTQconst [int8(log64(c))] x)) (SET(NE|EQ) (TESTL (SHLL (MOVLconst [1]) x) y)) => (SET(B|AE) (BTL x y)) (SET(NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => (SET(B|AE) (BTQ x y)) +(SET(NE|EQ) (TESTL (SHLXL (MOVLconst [1]) x) y)) => (SET(B|AE) (BTL x y)) +(SET(NE|EQ) (TESTQ (SHLXQ (MOVQconst [1]) x) y)) => (SET(B|AE) (BTQ x y)) (SET(NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c)) => (SET(B|AE) (BTLconst [int8(log32(c))] x)) (SET(NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c)) @@ -598,6 +628,10 @@ => (SET(B|AE)store [off] {sym} ptr (BTL x y) mem) (SET(NE|EQ)store [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem) => (SET(B|AE)store [off] {sym} ptr (BTQ x y) mem) +(SET(NE|EQ)store [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem) + => (SET(B|AE)store [off] {sym} ptr (BTL x y) mem) +(SET(NE|EQ)store [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem) + => (SET(B|AE)store [off] {sym} ptr (BTQ x y) mem) (SET(NE|EQ)store [off] {sym} ptr (TESTLconst [c] x) mem) && isUint32PowerOfTwo(int64(c)) => (SET(B|AE)store [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem) (SET(NE|EQ)store [off] {sym} ptr (TESTQconst [c] x) mem) && isUint64PowerOfTwo(int64(c)) @@ -610,9 +644,10 @@ (BT(Q|L)const [c] (SHRQconst [d] x)) && (c+d)<64 => (BTQconst [c+d] x) (BT(Q|L)const [c] (SHLQconst [d] x)) && c>d => (BT(Q|L)const [c-d] x) (BT(Q|L)const [0] s:(SHRQ x y)) => (BTQ y x) +(BT(Q|L)const [0] s:(SHRXQ x y)) => (BTQ y x) (BTLconst [c] (SHRLconst [d] x)) && (c+d)<32 => (BTLconst [c+d] x) (BTLconst [c] (SHLLconst [d] x)) && c>d => (BTLconst [c-d] x) -(BTLconst [0] s:(SHRL x y)) => (BTL y x) +(BTLconst [0] s:(SHR(L|XL) x y)) => (BTL y x) // Rewrite a & 1 != 1 into a & 1 == 0. // Among other things, this lets us turn (a>>b)&1 != 1 into a bit test. @@ -624,6 +659,8 @@ // Recognize bit setting (a |= 1< (BTS(Q|L) x y) (XOR(Q|L) (SHL(Q|L) (MOV(Q|L)const [1]) y) x) => (BTC(Q|L) x y) +(OR(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y) x) => (BTS(Q|L) x y) +(XOR(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y) x) => (BTC(Q|L) x y) // Convert ORconst into BTS, if the code gets smaller, with boundary being // (ORL $40,AX is 3 bytes, ORL $80,AX is 6 bytes). @@ -638,6 +675,9 @@ // Recognize bit clearing: a &^= 1< (BTR(Q|L) x y) +(ANDN(Q|L) x (SHL(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y) +(AND(Q|L) (NOT(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y)) x) => (BTR(Q|L) x y) +(ANDN(Q|L) x (SHLX(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y) (ANDQconst [c] x) && isUint64PowerOfTwo(int64(^c)) && uint64(^c) >= 128 => (BTRQconst [int8(log32(^c))] x) (ANDLconst [c] x) && isUint32PowerOfTwo(int64(^c)) && uint64(^c) >= 128 @@ -779,6 +819,8 @@ (SHLQ x (MOV(Q|L)const [c])) => (SHLQconst [int8(c&63)] x) (SHLL x (MOV(Q|L)const [c])) => (SHLLconst [int8(c&31)] x) +(SHLXQ x (MOV(Q|L)const [c])) => (SHLQconst [int8(c&63)] x) +(SHLXL x (MOV(Q|L)const [c])) => (SHLLconst [int8(c&31)] x) (SHRQ x (MOV(Q|L)const [c])) => (SHRQconst [int8(c&63)] x) (SHRL x (MOV(Q|L)const [c])) => (SHRLconst [int8(c&31)] x) @@ -786,91 +828,36 @@ (SHRW _ (MOV(Q|L)const [c])) && c&31 >= 16 => (MOVLconst [0]) (SHRB x (MOV(Q|L)const [c])) && c&31 < 8 => (SHRBconst [int8(c&31)] x) (SHRB _ (MOV(Q|L)const [c])) && c&31 >= 8 => (MOVLconst [0]) +(SHRXQ x (MOV(Q|L)const [c])) => (SHRQconst [int8(c&63)] x) +(SHRXL x (MOV(Q|L)const [c])) => (SHRLconst [int8(c&31)] x) (SARQ x (MOV(Q|L)const [c])) => (SARQconst [int8(c&63)] x) (SARL x (MOV(Q|L)const [c])) => (SARLconst [int8(c&31)] x) (SARW x (MOV(Q|L)const [c])) => (SARWconst [int8(min(int64(c)&31,15))] x) (SARB x (MOV(Q|L)const [c])) => (SARBconst [int8(min(int64(c)&31,7))] x) - +(SARXQ x (MOV(Q|L)const [c])) => (SARQconst [int8(c&63)] x) +(SARXL x (MOV(Q|L)const [c])) => (SARLconst [int8(c&31)] x) // Operations which don't affect the low 6/5 bits of the shift amount are NOPs. -((SHLQ|SHRQ|SARQ) x (ADDQconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x y) -((SHLQ|SHRQ|SARQ) x (NEGQ (ADDQconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x (NEGQ y)) -((SHLQ|SHRQ|SARQ) x (ANDQconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x y) -((SHLQ|SHRQ|SARQ) x (NEGQ (ANDQconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x (NEGQ y)) - -((SHLL|SHRL|SARL) x (ADDQconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL) x y) -((SHLL|SHRL|SARL) x (NEGQ (ADDQconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL) x (NEGQ y)) -((SHLL|SHRL|SARL) x (ANDQconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL) x y) -((SHLL|SHRL|SARL) x (NEGQ (ANDQconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL) x (NEGQ y)) - -((SHLQ|SHRQ|SARQ) x (ADDLconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x y) -((SHLQ|SHRQ|SARQ) x (NEGL (ADDLconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x (NEGL y)) -((SHLQ|SHRQ|SARQ) x (ANDLconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x y) -((SHLQ|SHRQ|SARQ) x (NEGL (ANDLconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x (NEGL y)) - -((SHLL|SHRL|SARL) x (ADDLconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL) x y) -((SHLL|SHRL|SARL) x (NEGL (ADDLconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL) x (NEGL y)) -((SHLL|SHRL|SARL) x (ANDLconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL) x y) -((SHLL|SHRL|SARL) x (NEGL (ANDLconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL) x (NEGL y)) - -// Constant rotate instructions -((ADDQ|ORQ|XORQ) (SHLQconst x [c]) (SHRQconst x [d])) && d==64-c => (ROLQconst x [c]) -((ADDL|ORL|XORL) (SHLLconst x [c]) (SHRLconst x [d])) && d==32-c => (ROLLconst x [c]) - -((ADDL|ORL|XORL) (SHLLconst x [c]) (SHRWconst x [d])) && d==16-c && c < 16 && t.Size() == 2 => (ROLWconst x [c]) -((ADDL|ORL|XORL) (SHLLconst x [c]) (SHRBconst x [d])) && d==8-c && c < 8 && t.Size() == 1 => (ROLBconst x [c]) - -(ROLQconst [c] (ROLQconst [d] x)) => (ROLQconst [(c+d)&63] x) -(ROLLconst [c] (ROLLconst [d] x)) => (ROLLconst [(c+d)&31] x) -(ROLWconst [c] (ROLWconst [d] x)) => (ROLWconst [(c+d)&15] x) -(ROLBconst [c] (ROLBconst [d] x)) => (ROLBconst [(c+d)& 7] x) - -(RotateLeft8 ...) => (ROLB ...) -(RotateLeft16 ...) => (ROLW ...) -(RotateLeft32 ...) => (ROLL ...) -(RotateLeft64 ...) => (ROLQ ...) - -// Non-constant rotates. -// We want to issue a rotate when the Go source contains code like -// y &= 63 -// x << y | x >> (64-y) -// The shift rules above convert << to SHLx and >> to SHRx. -// SHRx converts its shift argument from 64-y to -y. -// A tricky situation occurs when y==0. Then the original code would be: -// x << 0 | x >> 64 -// But x >> 64 is 0, not x. So there's an additional mask that is ANDed in -// to force the second term to 0. We don't need that mask, but we must match -// it in order to strip it out. -(ORQ (SHLQ x y) (ANDQ (SHRQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (ROLQ x y) -(ORQ (SHRQ x y) (ANDQ (SHLQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (RORQ x y) - -(ORL (SHLL x y) (ANDL (SHRL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (ROLL x y) -(ORL (SHRL x y) (ANDL (SHLL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (RORL x y) - -// Help with rotate detection -(CMPQconst (NEGQ (ADDQconst [-16] (ANDQconst [15] _))) [32]) => (FlagLT_ULT) -(CMPQconst (NEGQ (ADDQconst [ -8] (ANDQconst [7] _))) [32]) => (FlagLT_ULT) - -(ORL (SHLL x (AND(Q|L)const y [15])) - (ANDL (SHRW x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16]))) - (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16])) [16])))) - && v.Type.Size() == 2 - => (ROLW x y) -(ORL (SHRW x (AND(Q|L)const y [15])) - (SHLL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16])))) - && v.Type.Size() == 2 - => (RORW x y) - -(ORL (SHLL x (AND(Q|L)const y [ 7])) - (ANDL (SHRB x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8]))) - (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8])) [ 8])))) - && v.Type.Size() == 1 - => (ROLB x y) -(ORL (SHRB x (AND(Q|L)const y [ 7])) - (SHLL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8])))) - && v.Type.Size() == 1 - => (RORB x y) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ADDQconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ (ADDQconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ y)) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ANDQconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ (ANDQconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ y)) + +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ADDQconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ (ADDQconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ y)) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ANDQconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ (ANDQconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ y)) + +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ADDLconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL (ADDLconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL y)) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ANDLconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y) +((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL (ANDLconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL y)) + +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ADDLconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL (ADDLconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL y)) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ANDLconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y) +((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL (ANDLconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL y)) // rotate left negative = rotate right (ROLQ x (NEG(Q|L) y)) => (RORQ x y) @@ -904,6 +891,7 @@ // Multi-register shifts (ORQ (SH(R|L)Q lo bits) (SH(L|R)Q hi (NEGQ bits))) => (SH(R|L)DQ lo hi bits) +(ORQ (SH(R|L)XQ lo bits) (SH(L|R)XQ hi (NEGQ bits))) => (SH(R|L)DQ lo hi bits) // Note: the word and byte shifts keep the low 5 bits (not the low 4 or 3 bits) // because the x86 instructions are defined to use all 5 bits of the shift even @@ -1134,8 +1122,8 @@ (MOVBstoreconst [makeValAndOff(int32(int8(c)),off)] {sym} ptr mem) // Fold address offsets into constant stores. -(MOV(Q|L|W|B)storeconst [sc] {s} (ADDQconst [off] ptr) mem) && ValAndOff(sc).canAdd32(off) => - (MOV(Q|L|W|B)storeconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem) +(MOV(Q|L|W|B|O)storeconst [sc] {s} (ADDQconst [off] ptr) mem) && ValAndOff(sc).canAdd32(off) => + (MOV(Q|L|W|B|O)storeconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem) // We need to fold LEAQ into the MOVx ops so that the live variable analysis knows // what variables are being read/written by the ops. @@ -1145,8 +1133,8 @@ (MOV(Q|L|W|B|SS|SD|O)store [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => (MOV(Q|L|W|B|SS|SD|O)store [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOV(Q|L|W|B)storeconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off) => - (MOV(Q|L|W|B)storeconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) +(MOV(Q|L|W|B|O)storeconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off) => + (MOV(Q|L|W|B|O)storeconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) (SET(L|G|B|A|LE|GE|BE|AE|EQ|NE)store [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => (SET(L|G|B|A|LE|GE|BE|AE|EQ|NE)store [off1+off2] {mergeSym(sym1,sym2)} base val mem) @@ -1867,44 +1855,52 @@ => (MOVQstore [i] {s} p0 (BSWAPQ w) mem) // Combine constant stores into larger (unaligned) stores. -(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem)) +(MOVBstoreconst [c] {s} p1 x:(MOVBstoreconst [a] {s} p0 mem)) + && x.Uses == 1 + && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) + && clobber(x) + => (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p0 mem) +(MOVBstoreconst [a] {s} p0 x:(MOVBstoreconst [c] {s} p1 mem)) && x.Uses == 1 - && a.Off() + 1 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) && clobber(x) - => (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p mem) -(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem)) + => (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p0 mem) +(MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem)) && x.Uses == 1 - && a.Off() + 1 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x) - => (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p mem) -(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem)) + => (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p0 mem) +(MOVWstoreconst [a] {s} p0 x:(MOVWstoreconst [c] {s} p1 mem)) && x.Uses == 1 - && a.Off() + 2 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x) - => (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p mem) -(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem)) + => (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p0 mem) +(MOVLstoreconst [c] {s} p1 x:(MOVLstoreconst [a] {s} p0 mem)) && x.Uses == 1 - && a.Off() + 2 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x) - => (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p mem) -(MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem)) + => (MOVQstore [a.Off()] {s} p0 (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) +(MOVLstoreconst [a] {s} p0 x:(MOVLstoreconst [c] {s} p1 mem)) && x.Uses == 1 - && a.Off() + 4 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x) - => (MOVQstore [a.Off()] {s} p (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) -(MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem)) + => (MOVQstore [a.Off()] {s} p0 (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) +(MOVQstoreconst [c] {s} p1 x:(MOVQstoreconst [a] {s} p0 mem)) + && config.useSSE && x.Uses == 1 - && a.Off() + 4 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) + && a.Val() == 0 + && c.Val() == 0 && clobber(x) - => (MOVQstore [a.Off()] {s} p (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) -(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem)) + => (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p0 mem) +(MOVQstoreconst [a] {s} p0 x:(MOVQstoreconst [c] {s} p1 mem)) && config.useSSE && x.Uses == 1 - && c2.Off() + 8 == c.Off() + && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) + && a.Val() == 0 && c.Val() == 0 - && c2.Val() == 0 && clobber(x) - => (MOVOstorezero [c2.Off()] {s} p mem) + => (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p0 mem) // Combine stores into larger (unaligned) stores. Little endian. (MOVBstore [i] {s} p (SHR(W|L|Q)const [8] w) x:(MOVBstore [i-1] {s} p w mem)) @@ -1973,15 +1969,18 @@ && clobber(x) => (MOVQstore [i] {s} p0 w0 mem) -(MOVBstore [7] {s} p1 (SHRQconst [56] w) - x1:(MOVWstore [5] {s} p1 (SHRQconst [40] w) - x2:(MOVLstore [1] {s} p1 (SHRQconst [8] w) - x3:(MOVBstore [0] {s} p1 w mem)))) +(MOVBstore [c3] {s} p3 (SHRQconst [56] w) + x1:(MOVWstore [c2] {s} p2 (SHRQconst [40] w) + x2:(MOVLstore [c1] {s} p1 (SHRQconst [8] w) + x3:(MOVBstore [c0] {s} p0 w mem)))) && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 + && sequentialAddresses(p0, p1, int64(1 + c0 - c1)) + && sequentialAddresses(p0, p2, int64(5 + c0 - c2)) + && sequentialAddresses(p0, p3, int64(7 + c0 - c3)) && clobber(x1, x2, x3) - => (MOVQstore {s} p1 w mem) + => (MOVQstore [c0] {s} p0 w mem) (MOVBstore [i] {s} p x1:(MOVBload [j] {s2} p2 mem) @@ -2013,50 +2012,6 @@ && clobber(x1, x2, mem2) => (MOVQstore [i-4] {s} p (MOVQload [j-4] {s2} p2 mem) mem) -(MOVQload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVQload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem) - -(MOVQstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - -(MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) => - (MOVQstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) -(MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) => - (MOVLstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) -(MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) => - (MOVWstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) -(MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) => - (MOVBstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) - -(MOVQload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVQload [off1+off2] {sym} ptr mem) -(MOVLload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVLload [off1+off2] {sym} ptr mem) -(MOVWload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVWload [off1+off2] {sym} ptr mem) -(MOVBload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVBload [off1+off2] {sym} ptr mem) -(MOVQstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVQstore [off1+off2] {sym} ptr val mem) -(MOVLstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVLstore [off1+off2] {sym} ptr val mem) -(MOVWstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVWstore [off1+off2] {sym} ptr val mem) -(MOVBstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVBstore [off1+off2] {sym} ptr val mem) -(MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) => - (MOVQstoreconst [sc.addOffset32(off)] {s} ptr mem) -(MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) => - (MOVLstoreconst [sc.addOffset32(off)] {s} ptr mem) -(MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) => - (MOVWstoreconst [sc.addOffset32(off)] {s} ptr mem) -(MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) => - (MOVBstoreconst [sc.addOffset32(off)] {s} ptr mem) - // Merge load and op // TODO: add indexed variants? ((ADD|SUB|AND|OR|XOR)Q x l:(MOVQload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|AND|OR|XOR)Qload x [off] {sym} ptr mem) @@ -2101,7 +2056,7 @@ (CMPXCHGLlock [off1+off2] {sym} ptr old new_ mem) // We don't need the conditional move if we know the arg of BSF is not zero. -(CMOVQEQ x _ (Select1 (BSFQ (ORQconst [c] _)))) && c != 0 => x +(CMOVQEQ x _ (Select1 (BS(F|R)Q (ORQconst [c] _)))) && c != 0 => x // Extension is unnecessary for trailing zeros. (BSFQ (ORQconst [1<<8] (MOVBQZX x))) => (BSFQ (ORQconst [1<<8] x)) (BSFQ (ORQconst [1<<16] (MOVWQZX x))) => (BSFQ (ORQconst [1<<16] x)) @@ -2235,3 +2190,51 @@ && isInlinableMemmove(dst, src, sz, config) && clobber(call) => (Move [sz] dst src mem) + +// Prefetch instructions +(PrefetchCache ...) => (PrefetchT0 ...) +(PrefetchCacheStreamed ...) => (PrefetchNTA ...) + +// CPUID feature: BMI1. +(AND(Q|L) x (NOT(Q|L) y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y) +(AND(Q|L) x (NEG(Q|L) x)) && buildcfg.GOAMD64 >= 3 => (BLSI(Q|L) x) +(XOR(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSMSK(Q|L) x) +(AND(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSR(Q|L) x) + +(BSWAP(Q|L) (BSWAP(Q|L) p)) => p + +// CPUID feature: MOVBE. +(MOV(Q|L)store [i] {s} p x:(BSWAP(Q|L) w) mem) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBE(Q|L)store [i] {s} p w mem) +(BSWAP(Q|L) x:(MOV(Q|L)load [i] {s} p mem)) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBE(Q|L)load [i] {s} p mem) +(BSWAP(Q|L) (MOVBE(Q|L)load [i] {s} p m)) => (MOV(Q|L)load [i] {s} p m) +(MOVBE(Q|L)store [i] {s} p (BSWAP(Q|L) x) m) => (MOV(Q|L)store [i] {s} p x m) +(MOVWstore [i] {s} p x:(ROLWconst [8] w) mem) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBEWstore [i] {s} p w mem) +(MOVBEWstore [i] {s} p x:(ROLWconst [8] w) mem) && x.Uses == 1 => (MOVWstore [i] {s} p w mem) + +(ORQ x0:(MOVBELload [i0] {s} p mem) + sh:(SHLQconst [32] x1:(MOVBELload [i1] {s} p mem))) + && i0 == i1+4 + && x0.Uses == 1 + && x1.Uses == 1 + && sh.Uses == 1 + && mergePoint(b,x0,x1) != nil + && clobber(x0, x1, sh) + => @mergePoint(b,x0,x1) (MOVBEQload [i1] {s} p mem) + +(ORQ x0:(MOVBELload [i] {s} p0 mem) + sh:(SHLQconst [32] x1:(MOVBELload [i] {s} p1 mem))) + && x0.Uses == 1 + && x1.Uses == 1 + && sh.Uses == 1 + && sequentialAddresses(p1, p0, 4) + && mergePoint(b,x0,x1) != nil + && clobber(x0, x1, sh) + => @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem) + +(SARX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SARX(Q|L)load [off] {sym} ptr x mem) +(SHLX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SHLX(Q|L)load [off] {sym} ptr x mem) +(SHRX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SHRX(Q|L)load [off] {sym} ptr x mem) + +((SHL|SHR|SAR)XQload [off] {sym} ptr (MOVQconst [c]) mem) => ((SHL|SHR|SAR)Qconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) +((SHL|SHR|SAR)XQload [off] {sym} ptr (MOVLconst [c]) mem) => ((SHL|SHR|SAR)Qconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) +((SHL|SHR|SAR)XLload [off] {sym} ptr (MOVLconst [c]) mem) => ((SHL|SHR|SAR)Lconst [int8(c&31)] (MOVLload [off] {sym} ptr mem)) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 67b3293903cd91..fc42fa5e28b821 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -141,11 +141,13 @@ func init() { readflags = regInfo{inputs: nil, outputs: gponly} flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}} - gpload = regInfo{inputs: []regMask{gpspsbg, 0}, outputs: gponly} - gp21load = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: gponly} - gploadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}, outputs: gponly} - gp21loadidx = regInfo{inputs: []regMask{gp, gpspsbg, gpsp, 0}, outputs: gponly} - gp21pax = regInfo{inputs: []regMask{gp &^ ax, gp}, outputs: []regMask{gp &^ ax}, clobbers: ax} + gpload = regInfo{inputs: []regMask{gpspsbg, 0}, outputs: gponly} + gp21load = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: gponly} + gploadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}, outputs: gponly} + gp21loadidx = regInfo{inputs: []regMask{gp, gpspsbg, gpsp, 0}, outputs: gponly} + gp21pax = regInfo{inputs: []regMask{gp &^ ax, gp}, outputs: []regMask{gp &^ ax}, clobbers: ax} + gp21shxload = regInfo{inputs: []regMask{gpspsbg, gp, 0}, outputs: gponly} + gp21shxloadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, gp, 0}, outputs: gponly} gpstore = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}} gpstoreconst = regInfo{inputs: []regMask{gpspsbg, 0}} @@ -169,6 +171,8 @@ func init() { fpstore = regInfo{inputs: []regMask{gpspsb, fp, 0}} fpstoreidx = regInfo{inputs: []regMask{gpspsb, gpsp, fp, 0}} + + prefreg = regInfo{inputs: []regMask{gpspsbg}} ) var AMD64ops = []opData{ @@ -511,8 +515,8 @@ func init() { {name: "NEGQ", argLength: 1, reg: gp11, asm: "NEGQ", resultInArg0: true, clobberFlags: true}, // -arg0 {name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0 - {name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true, clobberFlags: true}, // ^arg0 - {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0 + {name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true}, // ^arg0 + {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0 // BS{F,R}Q returns a tuple [result, flags] // result is undefined if the input is zero. @@ -575,8 +579,8 @@ func init() { {name: "CMOVWGTF", argLength: 3, reg: gp21, asm: "CMOVWHI", resultInArg0: true}, {name: "CMOVWGEF", argLength: 3, reg: gp21, asm: "CMOVWCC", resultInArg0: true}, - {name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes - {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes + {name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true}, // arg0 swap bytes + {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true}, // arg0 swap bytes // POPCNT instructions aren't guaranteed to be on the target platform (they are SSE4). // Any use must be preceded by a successful check of runtime.x86HasPOPCNT. @@ -679,20 +683,19 @@ func init() { // Note: LEAx{1,2,4,8} must not have OpSB as either argument. // auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address - {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend. - {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 - {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend. - {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 - {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend. - {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 - {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem - {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem - {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem - {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem - {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem - {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem - {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem - {name: "MOVOstorezero", argLength: 2, reg: regInfo{inputs: []regMask{gpspsb, 0}}, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of zero to arg0+auxint+aux. arg1=mem + {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend. + {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 + {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend. + {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 + {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend. + {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64 + {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem + {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem + {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem // indexed loads/stores {name: "MOVBloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBLZX", scale: 1, aux: "SymOff", typ: "UInt8", symEffect: "Read"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem @@ -717,10 +720,11 @@ func init() { // For storeconst ops, the AuxInt field encodes both // the value to store and an address offset of the store. // Cast AuxInt to a ValAndOff to extract Val and Off fields. - {name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem - {name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ... - {name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ... - {name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ... + {name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem + {name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ... + {name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ... + {name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ... + {name: "MOVOstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVUPS", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of ... {name: "MOVBstoreconstidx1", argLength: 3, reg: gpstoreconstidx, commutative: true, asm: "MOVB", scale: 1, aux: "SymValAndOff", typ: "Mem", symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+1*arg1+ValAndOff(AuxInt).Off()+aux. arg2=mem {name: "MOVWstoreconstidx1", argLength: 3, reg: gpstoreconstidx, commutative: true, asm: "MOVW", scale: 1, aux: "SymValAndOff", typ: "Mem", symEffect: "Write"}, // store low 2 bytes of ... arg1 ... @@ -763,6 +767,7 @@ func init() { // With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific in and out registers. {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem @@ -900,6 +905,83 @@ func init() { {name: "ANDLlock", argLength: 3, reg: gpstore, asm: "ANDL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) &= arg1 {name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1 {name: "ORLlock", argLength: 3, reg: gpstore, asm: "ORL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1 + + // Prefetch instructions + // Do prefetch arg0 address. arg0=addr, arg1=memory. Instruction variant selects locality hint + {name: "PrefetchT0", argLength: 2, reg: prefreg, asm: "PREFETCHT0", hasSideEffects: true}, + {name: "PrefetchNTA", argLength: 2, reg: prefreg, asm: "PREFETCHNTA", hasSideEffects: true}, + + // CPUID feature: BMI1. + {name: "ANDNQ", argLength: 2, reg: gp21, asm: "ANDNQ", clobberFlags: true}, // arg0 &^ arg1 + {name: "ANDNL", argLength: 2, reg: gp21, asm: "ANDNL", clobberFlags: true}, // arg0 &^ arg1 + {name: "BLSIQ", argLength: 1, reg: gp11, asm: "BLSIQ", clobberFlags: true}, // arg0 & -arg0 + {name: "BLSIL", argLength: 1, reg: gp11, asm: "BLSIL", clobberFlags: true}, // arg0 & -arg0 + {name: "BLSMSKQ", argLength: 1, reg: gp11, asm: "BLSMSKQ", clobberFlags: true}, // arg0 ^ (arg0 - 1) + {name: "BLSMSKL", argLength: 1, reg: gp11, asm: "BLSMSKL", clobberFlags: true}, // arg0 ^ (arg0 - 1) + {name: "BLSRQ", argLength: 1, reg: gp11, asm: "BLSRQ", clobberFlags: true}, // arg0 & (arg0 - 1) + {name: "BLSRL", argLength: 1, reg: gp11, asm: "BLSRL", clobberFlags: true}, // arg0 & (arg0 - 1) + // count the number of trailing zero bits, prefer TZCNTQ over BSFQ, as TZCNTQ(0)==64 + // and BSFQ(0) is undefined. Same for TZCNTL(0)==32 + {name: "TZCNTQ", argLength: 1, reg: gp11, asm: "TZCNTQ", clobberFlags: true}, + {name: "TZCNTL", argLength: 1, reg: gp11, asm: "TZCNTL", clobberFlags: true}, + + // CPUID feature: LZCNT. + // count the number of leading zero bits. + {name: "LZCNTQ", argLength: 1, reg: gp11, asm: "LZCNTQ", typ: "UInt64", clobberFlags: true}, + {name: "LZCNTL", argLength: 1, reg: gp11, asm: "LZCNTL", typ: "UInt32", clobberFlags: true}, + + // CPUID feature: MOVBE + // MOVBEWload does not satisfy zero extended, so only use MOVBEWstore + {name: "MOVBEWstore", argLength: 3, reg: gpstore, asm: "MOVBEW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVBELload", argLength: 2, reg: gpload, asm: "MOVBEL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend. + {name: "MOVBELstore", argLength: 3, reg: gpstore, asm: "MOVBEL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem + {name: "MOVBEQload", argLength: 2, reg: gpload, asm: "MOVBEQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 8 bytes from arg0+auxint+aux. arg1=mem + {name: "MOVBEQstore", argLength: 3, reg: gpstore, asm: "MOVBEQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem + // indexed MOVBE loads + {name: "MOVBELloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBEL", scale: 1, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend. + {name: "MOVBELloadidx4", argLength: 3, reg: gploadidx, asm: "MOVBEL", scale: 4, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+4*arg1+auxint+aux. arg2=mem. Zero extend. + {name: "MOVBELloadidx8", argLength: 3, reg: gploadidx, asm: "MOVBEL", scale: 8, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+8*arg1+auxint+aux. arg2=mem. Zero extend. + {name: "MOVBEQloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBEQ", scale: 1, aux: "SymOff", typ: "UInt64", symEffect: "Read"}, // load and swap 8 bytes from arg0+arg1+auxint+aux. arg2=mem + {name: "MOVBEQloadidx8", argLength: 3, reg: gploadidx, asm: "MOVBEQ", scale: 8, aux: "SymOff", typ: "UInt64", symEffect: "Read"}, // load and swap 8 bytes from arg0+8*arg1+auxint+aux. arg2=mem + // indexed MOVBE stores + {name: "MOVBEWstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEW", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem + {name: "MOVBEWstoreidx2", argLength: 4, reg: gpstoreidx, asm: "MOVBEW", scale: 2, aux: "SymOff", symEffect: "Write"}, // swap and store 2 bytes in arg2 to arg0+2*arg1+auxint+aux. arg3=mem + {name: "MOVBELstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEL", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem + {name: "MOVBELstoreidx4", argLength: 4, reg: gpstoreidx, asm: "MOVBEL", scale: 4, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+4*arg1+auxint+aux. arg3=mem + {name: "MOVBELstoreidx8", argLength: 4, reg: gpstoreidx, asm: "MOVBEL", scale: 8, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem + {name: "MOVBEQstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEQ", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem + {name: "MOVBEQstoreidx8", argLength: 4, reg: gpstoreidx, asm: "MOVBEQ", scale: 8, aux: "SymOff", symEffect: "Write"}, // swap and store 8 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem + + // CPUID feature: BMI2. + {name: "SARXQ", argLength: 2, reg: gp21, asm: "SARXQ"}, // signed arg0 >> arg1, shift amount is mod 64 + {name: "SARXL", argLength: 2, reg: gp21, asm: "SARXL"}, // signed int32(arg0) >> arg1, shift amount is mod 32 + {name: "SHLXQ", argLength: 2, reg: gp21, asm: "SHLXQ"}, // arg0 << arg1, shift amount is mod 64 + {name: "SHLXL", argLength: 2, reg: gp21, asm: "SHLXL"}, // arg0 << arg1, shift amount is mod 32 + {name: "SHRXQ", argLength: 2, reg: gp21, asm: "SHRXQ"}, // unsigned arg0 >> arg1, shift amount is mod 64 + {name: "SHRXL", argLength: 2, reg: gp21, asm: "SHRXL"}, // unsigned uint32(arg0) >> arg1, shift amount is mod 32 + + {name: "SARXLload", argLength: 3, reg: gp21shxload, asm: "SARXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 32 + {name: "SARXQload", argLength: 3, reg: gp21shxload, asm: "SARXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 64 + {name: "SHLXLload", argLength: 3, reg: gp21shxload, asm: "SHLXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 32 + {name: "SHLXQload", argLength: 3, reg: gp21shxload, asm: "SHLXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 64 + {name: "SHRXLload", argLength: 3, reg: gp21shxload, asm: "SHRXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 32 + {name: "SHRXQload", argLength: 3, reg: gp21shxload, asm: "SHRXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 64 + + {name: "SARXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SARXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+4*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SARXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SARXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SARXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 + {name: "SARXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SARXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 + {name: "SHLXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+1*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+4*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+8*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHLXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+1*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 64 + {name: "SHLXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHLXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+8*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 64 + {name: "SHRXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+4*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHRXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 + {name: "SHRXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHRXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 } var AMD64blocks = []blockData{ @@ -919,6 +1001,12 @@ func init() { {name: "NEF", controls: 1}, {name: "ORD", controls: 1}, // FP, ordered comparison (parity zero) {name: "NAN", controls: 1}, // FP, unordered comparison (parity one) + + // JUMPTABLE implements jump tables. + // Aux is the symbol (an *obj.LSym) for the jump table. + // control[0] is the index into the jump table. + // control[1] is the address of the jump table (the address of the symbol stored in Aux). + {name: "JUMPTABLE", controls: 2, aux: "Sym"}, } archs = append(archs, arch{ diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules index bcacbafe3a5f02..e5898b036913fb 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -351,6 +351,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // checks (NilCheck ...) => (LoweredNilCheck ...) @@ -478,7 +479,7 @@ (MOVHUloadidx ptr idx (MOVHstoreidx ptr2 idx x _)) && isSamePtr(ptr, ptr2) => (MOVHUreg x) (MOVHloadidx ptr idx (MOVHstoreidx ptr2 idx x _)) && isSamePtr(ptr, ptr2) => (MOVHreg x) -// fold constant into arithmatic ops +// fold constant into arithmetic ops (ADD x (MOVWconst [c])) => (ADDconst [c] x) (SUB (MOVWconst [c]) x) => (RSBconst [c] x) (SUB x (MOVWconst [c])) => (SUBconst [c] x) @@ -497,9 +498,9 @@ (XOR x (MOVWconst [c])) => (XORconst [c] x) (BIC x (MOVWconst [c])) => (BICconst [c] x) -(SLL x (MOVWconst [c])) => (SLLconst x [c&31]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=32) -(SRL x (MOVWconst [c])) => (SRLconst x [c&31]) -(SRA x (MOVWconst [c])) => (SRAconst x [c&31]) +(SLL x (MOVWconst [c])) && 0 <= c && c < 32 => (SLLconst x [c]) +(SRL x (MOVWconst [c])) && 0 <= c && c < 32 => (SRLconst x [c]) +(SRA x (MOVWconst [c])) && 0 <= c && c < 32 => (SRAconst x [c]) (CMP x (MOVWconst [c])) => (CMPconst [c] x) (CMP (MOVWconst [c]) x) => (InvertFlags (CMPconst [c] x)) @@ -507,6 +508,8 @@ (TST x (MOVWconst [c])) => (TSTconst [c] x) (TEQ x (MOVWconst [c])) => (TEQconst [c] x) +(SRR x (MOVWconst [c])) => (SRRconst x [c&31]) + // Canonicalize the order of arguments to comparisons - helps with CSE. (CMP x y) && canonLessThan(x,y) => (InvertFlags (CMP y x)) @@ -1072,70 +1075,61 @@ (CMNshiftRL x (MOVWconst [c]) [d]) => (CMNconst x [int32(uint32(c)>>uint64(d))]) (CMNshiftRA x (MOVWconst [c]) [d]) => (CMNconst x [c>>uint64(d)]) -(ADDshiftLLreg x y (MOVWconst [c])) => (ADDshiftLL x y [c]) -(ADDshiftRLreg x y (MOVWconst [c])) => (ADDshiftRL x y [c]) -(ADDshiftRAreg x y (MOVWconst [c])) => (ADDshiftRA x y [c]) -(ADCshiftLLreg x y (MOVWconst [c]) flags) => (ADCshiftLL x y [c] flags) -(ADCshiftRLreg x y (MOVWconst [c]) flags) => (ADCshiftRL x y [c] flags) -(ADCshiftRAreg x y (MOVWconst [c]) flags) => (ADCshiftRA x y [c] flags) -(ADDSshiftLLreg x y (MOVWconst [c])) => (ADDSshiftLL x y [c]) -(ADDSshiftRLreg x y (MOVWconst [c])) => (ADDSshiftRL x y [c]) -(ADDSshiftRAreg x y (MOVWconst [c])) => (ADDSshiftRA x y [c]) -(SUBshiftLLreg x y (MOVWconst [c])) => (SUBshiftLL x y [c]) -(SUBshiftRLreg x y (MOVWconst [c])) => (SUBshiftRL x y [c]) -(SUBshiftRAreg x y (MOVWconst [c])) => (SUBshiftRA x y [c]) -(SBCshiftLLreg x y (MOVWconst [c]) flags) => (SBCshiftLL x y [c] flags) -(SBCshiftRLreg x y (MOVWconst [c]) flags) => (SBCshiftRL x y [c] flags) -(SBCshiftRAreg x y (MOVWconst [c]) flags) => (SBCshiftRA x y [c] flags) -(SUBSshiftLLreg x y (MOVWconst [c])) => (SUBSshiftLL x y [c]) -(SUBSshiftRLreg x y (MOVWconst [c])) => (SUBSshiftRL x y [c]) -(SUBSshiftRAreg x y (MOVWconst [c])) => (SUBSshiftRA x y [c]) -(RSBshiftLLreg x y (MOVWconst [c])) => (RSBshiftLL x y [c]) -(RSBshiftRLreg x y (MOVWconst [c])) => (RSBshiftRL x y [c]) -(RSBshiftRAreg x y (MOVWconst [c])) => (RSBshiftRA x y [c]) -(RSCshiftLLreg x y (MOVWconst [c]) flags) => (RSCshiftLL x y [c] flags) -(RSCshiftRLreg x y (MOVWconst [c]) flags) => (RSCshiftRL x y [c] flags) -(RSCshiftRAreg x y (MOVWconst [c]) flags) => (RSCshiftRA x y [c] flags) -(RSBSshiftLLreg x y (MOVWconst [c])) => (RSBSshiftLL x y [c]) -(RSBSshiftRLreg x y (MOVWconst [c])) => (RSBSshiftRL x y [c]) -(RSBSshiftRAreg x y (MOVWconst [c])) => (RSBSshiftRA x y [c]) -(ANDshiftLLreg x y (MOVWconst [c])) => (ANDshiftLL x y [c]) -(ANDshiftRLreg x y (MOVWconst [c])) => (ANDshiftRL x y [c]) -(ANDshiftRAreg x y (MOVWconst [c])) => (ANDshiftRA x y [c]) -(ORshiftLLreg x y (MOVWconst [c])) => (ORshiftLL x y [c]) -(ORshiftRLreg x y (MOVWconst [c])) => (ORshiftRL x y [c]) -(ORshiftRAreg x y (MOVWconst [c])) => (ORshiftRA x y [c]) -(XORshiftLLreg x y (MOVWconst [c])) => (XORshiftLL x y [c]) -(XORshiftRLreg x y (MOVWconst [c])) => (XORshiftRL x y [c]) -(XORshiftRAreg x y (MOVWconst [c])) => (XORshiftRA x y [c]) -(BICshiftLLreg x y (MOVWconst [c])) => (BICshiftLL x y [c]) -(BICshiftRLreg x y (MOVWconst [c])) => (BICshiftRL x y [c]) -(BICshiftRAreg x y (MOVWconst [c])) => (BICshiftRA x y [c]) -(MVNshiftLLreg x (MOVWconst [c])) => (MVNshiftLL x [c]) -(MVNshiftRLreg x (MOVWconst [c])) => (MVNshiftRL x [c]) -(MVNshiftRAreg x (MOVWconst [c])) => (MVNshiftRA x [c]) -(CMPshiftLLreg x y (MOVWconst [c])) => (CMPshiftLL x y [c]) -(CMPshiftRLreg x y (MOVWconst [c])) => (CMPshiftRL x y [c]) -(CMPshiftRAreg x y (MOVWconst [c])) => (CMPshiftRA x y [c]) -(TSTshiftLLreg x y (MOVWconst [c])) => (TSTshiftLL x y [c]) -(TSTshiftRLreg x y (MOVWconst [c])) => (TSTshiftRL x y [c]) -(TSTshiftRAreg x y (MOVWconst [c])) => (TSTshiftRA x y [c]) -(TEQshiftLLreg x y (MOVWconst [c])) => (TEQshiftLL x y [c]) -(TEQshiftRLreg x y (MOVWconst [c])) => (TEQshiftRL x y [c]) -(TEQshiftRAreg x y (MOVWconst [c])) => (TEQshiftRA x y [c]) -(CMNshiftLLreg x y (MOVWconst [c])) => (CMNshiftLL x y [c]) -(CMNshiftRLreg x y (MOVWconst [c])) => (CMNshiftRL x y [c]) -(CMNshiftRAreg x y (MOVWconst [c])) => (CMNshiftRA x y [c]) - -// Generate rotates -(ADDshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x) -( ORshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x) -(XORshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x) -(ADDshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x) -( ORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x) -(XORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x) - -(RotateLeft32 x (MOVWconst [c])) => (SRRconst [-c&31] x) +(ADDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftLL x y [c]) +(ADDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRL x y [c]) +(ADDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRA x y [c]) +(ADCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftLL x y [c] flags) +(ADCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRL x y [c] flags) +(ADCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRA x y [c] flags) +(ADDSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftLL x y [c]) +(ADDSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRL x y [c]) +(ADDSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRA x y [c]) +(SUBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftLL x y [c]) +(SUBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRL x y [c]) +(SUBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRA x y [c]) +(SBCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftLL x y [c] flags) +(SBCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRL x y [c] flags) +(SBCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRA x y [c] flags) +(SUBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftLL x y [c]) +(SUBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRL x y [c]) +(SUBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRA x y [c]) +(RSBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftLL x y [c]) +(RSBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRL x y [c]) +(RSBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRA x y [c]) +(RSCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftLL x y [c] flags) +(RSCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRL x y [c] flags) +(RSCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRA x y [c] flags) +(RSBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftLL x y [c]) +(RSBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRL x y [c]) +(RSBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRA x y [c]) +(ANDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftLL x y [c]) +(ANDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRL x y [c]) +(ANDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRA x y [c]) +(ORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftLL x y [c]) +(ORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRL x y [c]) +(ORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRA x y [c]) +(XORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftLL x y [c]) +(XORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRL x y [c]) +(XORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRA x y [c]) +(BICshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftLL x y [c]) +(BICshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRL x y [c]) +(BICshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRA x y [c]) +(MVNshiftLLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftLL x [c]) +(MVNshiftRLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRL x [c]) +(MVNshiftRAreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRA x [c]) +(CMPshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftLL x y [c]) +(CMPshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRL x y [c]) +(CMPshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRA x y [c]) +(TSTshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftLL x y [c]) +(TSTshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRL x y [c]) +(TSTshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRA x y [c]) +(TEQshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftLL x y [c]) +(TEQshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRL x y [c]) +(TEQshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRA x y [c]) +(CMNshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftLL x y [c]) +(CMNshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRL x y [c]) +(CMNshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRA x y [c]) + (RotateLeft16 x (MOVWconst [c])) => (Or16 (Lsh16x32 x (MOVWconst [c&15])) (Rsh16Ux32 x (MOVWconst [-c&15]))) (RotateLeft8 x (MOVWconst [c])) => (Or8 (Lsh8x32 x (MOVWconst [c&7])) (Rsh8Ux32 x (MOVWconst [-c&7]))) (RotateLeft32 x y) => (SRR x (RSBconst [0] y)) @@ -1237,24 +1231,24 @@ (AND x (MVN y)) => (BIC x y) // simplification with *shift ops -(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) +(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(ANDshiftLL y:(SLLconst x [c]) x [c]) => y +(ANDshiftRL y:(SRLconst x [c]) x [c]) => y +(ANDshiftRA y:(SRAconst x [c]) x [c]) => y +(ORshiftLL y:(SLLconst x [c]) x [c]) => y +(ORshiftRL y:(SRLconst x [c]) x [c]) => y +(ORshiftRA y:(SRAconst x [c]) x [c]) => y +(XORshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(XORshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(XORshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) (AND x (MVNshiftLL y [c])) => (BICshiftLL x y [c]) (AND x (MVNshiftRL y [c])) => (BICshiftRL x y [c]) (AND x (MVNshiftRA y [c])) => (BICshiftRA x y [c]) @@ -1268,8 +1262,8 @@ (SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x) // comparison simplification -((LT|LE|EQ|NE|GE|GT) (CMP x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMN x y)) // sense of carry bit not preserved -((LT|LE|EQ|NE|GE|GT) (CMN x (RSBconst [0] y))) => ((LT|LE|EQ|NE|GE|GT) (CMP x y)) // sense of carry bit not preserved +((EQ|NE) (CMP x (RSBconst [0] y))) => ((EQ|NE) (CMN x y)) // sense of carry bit not preserved; see also #50854 +((EQ|NE) (CMN x (RSBconst [0] y))) => ((EQ|NE) (CMP x y)) // sense of carry bit not preserved; see also #50864 (EQ (CMPconst [0] l:(SUB x y)) yes no) && l.Uses==1 => (EQ (CMP x y) yes no) (EQ (CMPconst [0] l:(MULS x y a)) yes no) && l.Uses==1 => (EQ (CMP a (MUL x y)) yes no) (EQ (CMPconst [0] l:(SUBconst [c] x)) yes no) && l.Uses==1 => (EQ (CMPconst [c] x) yes no) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index 62699f290c2149..a70600918b323c 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -16,7 +16,8 @@ (Hmul64u ...) => (UMULH ...) (Hmul32 x y) => (SRAconst (MULL x y) [32]) (Hmul32u x y) => (SRAconst (UMULL x y) [32]) -(Mul64uhilo ...) => (LoweredMuluhilo ...) +(Select0 (Mul64uhilo x y)) => (UMULH x y) +(Select1 (Mul64uhilo x y)) => (MUL x y) (Div64 [false] x y) => (DIV x y) (Div64u ...) => (UDIV ...) @@ -63,8 +64,11 @@ (Sqrt32 ...) => (FSQRTS ...) // lowering rotates +// we do rotate detection in generic rules, if the following rules need to be changed, chcek generic rules first. (RotateLeft8 x (MOVDconst [c])) => (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7]))) +(RotateLeft8 x y) => (OR (SLL x (ANDconst [7] y)) (SRL (ZeroExt8to64 x) (ANDconst [7] (NEG y)))) (RotateLeft16 x (MOVDconst [c])) => (Or16 (Lsh16x64 x (MOVDconst [c&15])) (Rsh16Ux64 x (MOVDconst [-c&15]))) +(RotateLeft16 x y) => (RORW (ORshiftLL (ZeroExt16to32 x) (ZeroExt16to32 x) [16]) (NEG y)) (RotateLeft32 x y) => (RORW x (NEG y)) (RotateLeft64 x y) => (ROR x (NEG y)) @@ -134,65 +138,87 @@ // we compare to 64 to ensure Go semantics for large shifts // Rules about rotates with non-const shift are based on the following rules, // if the following rules change, please also modify the rules based on them. -(Lsh64x64 x y) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) -(Lsh64x32 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Lsh64x16 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Lsh64x8 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Lsh32x64 x y) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) -(Lsh32x32 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Lsh32x16 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Lsh32x8 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Lsh16x64 x y) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) -(Lsh16x32 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Lsh16x16 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Lsh16x8 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Lsh8x64 x y) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) -(Lsh8x32 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Lsh8x16 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Lsh8x8 x y) => (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Rsh64Ux64 x y) => (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] y)) -(Rsh64Ux32 x y) => (CSEL [OpARM64LessThanU] (SRL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Rsh64Ux16 x y) => (CSEL [OpARM64LessThanU] (SRL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Rsh64Ux8 x y) => (CSEL [OpARM64LessThanU] (SRL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Rsh32Ux64 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] y)) -(Rsh32Ux32 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Rsh32Ux16 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Rsh32Ux8 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Rsh16Ux64 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] y)) -(Rsh16Ux32 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Rsh16Ux16 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Rsh16Ux8 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Rsh8Ux64 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] y)) -(Rsh8Ux32 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) -(Rsh8Ux16 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) -(Rsh8Ux8 x y) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) - -(Rsh64x64 x y) => (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) -(Rsh64x32 x y) => (SRA x (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) -(Rsh64x16 x y) => (SRA x (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) -(Rsh64x8 x y) => (SRA x (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) - -(Rsh32x64 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) -(Rsh32x32 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) -(Rsh32x16 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) -(Rsh32x8 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) - -(Rsh16x64 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) -(Rsh16x32 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) -(Rsh16x16 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) -(Rsh16x8 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) - -(Rsh8x64 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) -(Rsh8x32 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) -(Rsh8x16 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) -(Rsh8x8 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + +// check shiftIsBounded first, if shift value is proved to be valid then we +// can do the shift directly. +// left shift +(Lsh(64|32|16|8)x64 x y) && shiftIsBounded(v) => (SLL x y) +(Lsh(64|32|16|8)x32 x y) && shiftIsBounded(v) => (SLL x y) +(Lsh(64|32|16|8)x16 x y) && shiftIsBounded(v) => (SLL x y) +(Lsh(64|32|16|8)x8 x y) && shiftIsBounded(v) => (SLL x y) + +// signed right shift +(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y) +(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt32to64 x) y) +(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt16to64 x) y) +(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt8to64 x) y) + +// unsigned right shift +(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL x y) +(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt32to64 x) y) +(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt16to64 x) y) +(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt8to64 x) y) + +// shift value may be out of range, use CMP + CSEL instead +(Lsh64x64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) +(Lsh64x32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Lsh64x16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Lsh64x8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Lsh32x64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) +(Lsh32x32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Lsh32x16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Lsh32x8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Lsh16x64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) +(Lsh16x32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Lsh16x16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Lsh16x8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Lsh8x64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) +(Lsh8x32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Lsh8x16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Lsh8x8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Rsh64Ux64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] y)) +(Rsh64Ux32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Rsh64Ux16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Rsh64Ux8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Rsh32Ux64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] y)) +(Rsh32Ux32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Rsh32Ux16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Rsh32Ux8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Rsh16Ux64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] y)) +(Rsh16Ux32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Rsh16Ux16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Rsh16Ux8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Rsh8Ux64 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] y)) +(Rsh8Ux32 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) +(Rsh8Ux16 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) +(Rsh8Ux8 x y) && !shiftIsBounded(v) => (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + +(Rsh64x64 x y) && !shiftIsBounded(v) => (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) +(Rsh64x32 x y) && !shiftIsBounded(v) => (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) +(Rsh64x16 x y) && !shiftIsBounded(v) => (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) +(Rsh64x8 x y) && !shiftIsBounded(v) => (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + +(Rsh32x64 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) +(Rsh32x32 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) +(Rsh32x16 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) +(Rsh32x8 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + +(Rsh16x64 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) +(Rsh16x32 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) +(Rsh16x16 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) +(Rsh16x8 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + +(Rsh8x64 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) +(Rsh8x32 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) +(Rsh8x16 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) +(Rsh8x8 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) // constants (Const(64|32|16|8) [val]) => (MOVDconst [int64(val)]) @@ -351,8 +377,6 @@ (Zero [1] ptr mem) => (MOVBstore ptr (MOVDconst [0]) mem) (Zero [2] ptr mem) => (MOVHstore ptr (MOVDconst [0]) mem) (Zero [4] ptr mem) => (MOVWstore ptr (MOVDconst [0]) mem) -(Zero [8] ptr mem) => (MOVDstore ptr (MOVDconst [0]) mem) - (Zero [3] ptr mem) => (MOVBstore [2] ptr (MOVDconst [0]) (MOVHstore ptr (MOVDconst [0]) mem)) @@ -363,9 +387,9 @@ (MOVHstore [4] ptr (MOVDconst [0]) (MOVWstore ptr (MOVDconst [0]) mem)) (Zero [7] ptr mem) => - (MOVBstore [6] ptr (MOVDconst [0]) - (MOVHstore [4] ptr (MOVDconst [0]) - (MOVWstore ptr (MOVDconst [0]) mem))) + (MOVWstore [3] ptr (MOVDconst [0]) + (MOVWstore ptr (MOVDconst [0]) mem)) +(Zero [8] ptr mem) => (MOVDstore ptr (MOVDconst [0]) mem) (Zero [9] ptr mem) => (MOVBstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) @@ -373,25 +397,20 @@ (MOVHstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [11] ptr mem) => - (MOVBstore [10] ptr (MOVDconst [0]) - (MOVHstore [8] ptr (MOVDconst [0]) - (MOVDstore ptr (MOVDconst [0]) mem))) + (MOVDstore [3] ptr (MOVDconst [0]) + (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [12] ptr mem) => (MOVWstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [13] ptr mem) => - (MOVBstore [12] ptr (MOVDconst [0]) - (MOVWstore [8] ptr (MOVDconst [0]) - (MOVDstore ptr (MOVDconst [0]) mem))) + (MOVDstore [5] ptr (MOVDconst [0]) + (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [14] ptr mem) => - (MOVHstore [12] ptr (MOVDconst [0]) - (MOVWstore [8] ptr (MOVDconst [0]) - (MOVDstore ptr (MOVDconst [0]) mem))) + (MOVDstore [6] ptr (MOVDconst [0]) + (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [15] ptr mem) => - (MOVBstore [14] ptr (MOVDconst [0]) - (MOVHstore [12] ptr (MOVDconst [0]) - (MOVWstore [8] ptr (MOVDconst [0]) - (MOVDstore ptr (MOVDconst [0]) mem)))) + (MOVDstore [7] ptr (MOVDconst [0]) + (MOVDstore ptr (MOVDconst [0]) mem)) (Zero [16] ptr mem) => (STP [0] ptr (MOVDconst [0]) (MOVDconst [0]) mem) @@ -439,12 +458,10 @@ (Move [0] _ _ mem) => mem (Move [1] dst src mem) => (MOVBstore dst (MOVBUload src mem) mem) (Move [2] dst src mem) => (MOVHstore dst (MOVHUload src mem) mem) -(Move [4] dst src mem) => (MOVWstore dst (MOVWUload src mem) mem) -(Move [8] dst src mem) => (MOVDstore dst (MOVDload src mem) mem) - (Move [3] dst src mem) => (MOVBstore [2] dst (MOVBUload [2] src mem) (MOVHstore dst (MOVHUload src mem) mem)) +(Move [4] dst src mem) => (MOVWstore dst (MOVWUload src mem) mem) (Move [5] dst src mem) => (MOVBstore [4] dst (MOVBUload [4] src mem) (MOVWstore dst (MOVWUload src mem) mem)) @@ -452,35 +469,60 @@ (MOVHstore [4] dst (MOVHUload [4] src mem) (MOVWstore dst (MOVWUload src mem) mem)) (Move [7] dst src mem) => - (MOVBstore [6] dst (MOVBUload [6] src mem) - (MOVHstore [4] dst (MOVHUload [4] src mem) - (MOVWstore dst (MOVWUload src mem) mem))) + (MOVWstore [3] dst (MOVWUload [3] src mem) + (MOVWstore dst (MOVWUload src mem) mem)) +(Move [8] dst src mem) => (MOVDstore dst (MOVDload src mem) mem) +(Move [9] dst src mem) => + (MOVBstore [8] dst (MOVBUload [8] src mem) + (MOVDstore dst (MOVDload src mem) mem)) +(Move [10] dst src mem) => + (MOVHstore [8] dst (MOVHUload [8] src mem) + (MOVDstore dst (MOVDload src mem) mem)) +(Move [11] dst src mem) => + (MOVDstore [3] dst (MOVDload [3] src mem) + (MOVDstore dst (MOVDload src mem) mem)) (Move [12] dst src mem) => (MOVWstore [8] dst (MOVWUload [8] src mem) (MOVDstore dst (MOVDload src mem) mem)) -(Move [16] dst src mem) => - (MOVDstore [8] dst (MOVDload [8] src mem) +(Move [13] dst src mem) => + (MOVDstore [5] dst (MOVDload [5] src mem) (MOVDstore dst (MOVDload src mem) mem)) -(Move [24] dst src mem) => - (MOVDstore [16] dst (MOVDload [16] src mem) - (MOVDstore [8] dst (MOVDload [8] src mem) - (MOVDstore dst (MOVDload src mem) mem))) +(Move [14] dst src mem) => + (MOVDstore [6] dst (MOVDload [6] src mem) + (MOVDstore dst (MOVDload src mem) mem)) +(Move [15] dst src mem) => + (MOVDstore [7] dst (MOVDload [7] src mem) + (MOVDstore dst (MOVDload src mem) mem)) +(Move [16] dst src mem) => + (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem) +(Move [32] dst src mem) => + (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) + (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)) +(Move [48] dst src mem) => + (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem)) + (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) + (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))) +(Move [64] dst src mem) => + (STP [48] dst (Select0 (LDP [48] src mem)) (Select1 (LDP [48] src mem)) + (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem)) + (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) + (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)))) // strip off fractional word move -(Move [s] dst src mem) && s%8 != 0 && s > 8 => - (Move [s%8] - (OffPtr dst [s-s%8]) - (OffPtr src [s-s%8]) - (Move [s-s%8] dst src mem)) +(Move [s] dst src mem) && s%16 != 0 && s%16 <= 8 && s > 16 => + (Move [8] + (OffPtr dst [s-8]) + (OffPtr src [s-8]) + (Move [s-s%16] dst src mem)) +(Move [s] dst src mem) && s%16 != 0 && s%16 > 8 && s > 16 => + (Move [16] + (OffPtr dst [s-16]) + (OffPtr src [s-16]) + (Move [s-s%16] dst src mem)) // medium move uses a duff device (Move [s] dst src mem) - && s > 32 && s <= 16*64 && s%16 == 8 - && !config.noDuffDevice && logLargeCopy(v, s) => - (MOVDstore [int32(s-8)] dst (MOVDload [int32(s-8)] src mem) - (DUFFCOPY [8*(64-(s-8)/16)] dst src mem)) -(Move [s] dst src mem) - && s > 32 && s <= 16*64 && s%16 == 0 + && s > 64 && s <= 16*64 && s%16 == 0 && !config.noDuffDevice && logLargeCopy(v, s) => (DUFFCOPY [8 * (64 - s/16)] dst src mem) // 8 is the number of bytes to encode: @@ -492,17 +534,19 @@ // large move uses a loop (Move [s] dst src mem) - && s > 24 && s%8 == 0 && logLargeCopy(v, s) => + && s%16 == 0 && (s > 16*64 || config.noDuffDevice) + && logLargeCopy(v, s) => (LoweredMove dst src - (ADDconst src [s-8]) + (ADDconst src [s-16]) mem) // calls (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // checks (NilCheck ...) => (LoweredNilCheck ...) @@ -531,7 +575,9 @@ (If (GreaterThanF cc) yes no) => (FGT cc yes no) (If (GreaterEqualF cc) yes no) => (FGE cc yes no) -(If cond yes no) => (NZ cond yes no) +(If cond yes no) => (TBNZ [0] cond yes no) + +(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (MOVDaddr {makeJumpTableSym(b)} (SB))) // atomic intrinsics // Note: these ops do not accept offset. @@ -567,6 +613,9 @@ // Write barrier. (WB ...) => (LoweredWB ...) +// Publication barrier (0xe is ST option) +(PubBarrier mem) => (DMB [0xe] mem) + (PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem) (PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem) (PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem) @@ -589,12 +638,20 @@ (NZ (GreaterThanF cc) yes no) => (FGT cc yes no) (NZ (GreaterEqualF cc) yes no) => (FGE cc yes no) -(EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (EQ (TSTWconst [int32(c)] y) yes no) -(NE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (NE (TSTWconst [int32(c)] y) yes no) -(LT (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LT (TSTWconst [int32(c)] y) yes no) -(LE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LE (TSTWconst [int32(c)] y) yes no) -(GT (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GT (TSTWconst [int32(c)] y) yes no) -(GE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GE (TSTWconst [int32(c)] y) yes no) +(TBNZ [0] (Equal cc) yes no) => (EQ cc yes no) +(TBNZ [0] (NotEqual cc) yes no) => (NE cc yes no) +(TBNZ [0] (LessThan cc) yes no) => (LT cc yes no) +(TBNZ [0] (LessThanU cc) yes no) => (ULT cc yes no) +(TBNZ [0] (LessEqual cc) yes no) => (LE cc yes no) +(TBNZ [0] (LessEqualU cc) yes no) => (ULE cc yes no) +(TBNZ [0] (GreaterThan cc) yes no) => (GT cc yes no) +(TBNZ [0] (GreaterThanU cc) yes no) => (UGT cc yes no) +(TBNZ [0] (GreaterEqual cc) yes no) => (GE cc yes no) +(TBNZ [0] (GreaterEqualU cc) yes no) => (UGE cc yes no) +(TBNZ [0] (LessThanF cc) yes no) => (FLT cc yes no) +(TBNZ [0] (LessEqualF cc) yes no) => (FLE cc yes no) +(TBNZ [0] (GreaterThanF cc) yes no) => (FGT cc yes no) +(TBNZ [0] (GreaterEqualF cc) yes no) => (FGE cc yes no) (EQ (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (EQ (TST x y) yes no) (NE (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (NE (TST x y) yes no) @@ -603,6 +660,13 @@ (GT (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (GT (TST x y) yes no) (GE (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (GE (TST x y) yes no) +(EQ (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (EQ (TSTconst [c] y) yes no) +(NE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (NE (TSTconst [c] y) yes no) +(LT (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LT (TSTconst [c] y) yes no) +(LE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LE (TSTconst [c] y) yes no) +(GT (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GT (TSTconst [c] y) yes no) +(GE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GE (TSTconst [c] y) yes no) + (EQ (CMPWconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (EQ (TSTW x y) yes no) (NE (CMPWconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (NE (TSTW x y) yes no) (LT (CMPWconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (LT (TSTW x y) yes no) @@ -610,12 +674,41 @@ (GT (CMPWconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (GT (TSTW x y) yes no) (GE (CMPWconst [0] z:(AND x y)) yes no) && z.Uses == 1 => (GE (TSTW x y) yes no) -(EQ (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (EQ (TSTconst [c] y) yes no) -(NE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (NE (TSTconst [c] y) yes no) -(LT (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LT (TSTconst [c] y) yes no) -(LE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LE (TSTconst [c] y) yes no) -(GT (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GT (TSTconst [c] y) yes no) -(GE (CMPconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GE (TSTconst [c] y) yes no) +(EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (EQ (TSTWconst [int32(c)] y) yes no) +(NE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (NE (TSTWconst [int32(c)] y) yes no) +(LT (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LT (TSTWconst [int32(c)] y) yes no) +(LE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (LE (TSTWconst [int32(c)] y) yes no) +(GT (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GT (TSTWconst [int32(c)] y) yes no) +(GE (CMPWconst [0] x:(ANDconst [c] y)) yes no) && x.Uses == 1 => (GE (TSTWconst [int32(c)] y) yes no) + +// For conditional instructions such as CSET, CSEL. +(Equal (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (Equal (TST x y)) +(NotEqual (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (NotEqual (TST x y)) +(LessThan (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (LessThan (TST x y)) +(LessEqual (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (LessEqual (TST x y)) +(GreaterThan (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (GreaterThan (TST x y)) +(GreaterEqual (CMPconst [0] z:(AND x y))) && z.Uses == 1 => (GreaterEqual (TST x y)) + +(Equal (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (Equal (TSTWconst [int32(c)] y)) +(NotEqual (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (NotEqual (TSTWconst [int32(c)] y)) +(LessThan (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (LessThan (TSTWconst [int32(c)] y)) +(LessEqual (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (LessEqual (TSTWconst [int32(c)] y)) +(GreaterThan (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (GreaterThan (TSTWconst [int32(c)] y)) +(GreaterEqual (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (GreaterEqual (TSTWconst [int32(c)] y)) + +(Equal (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (Equal (TSTW x y)) +(NotEqual (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (NotEqual (TSTW x y)) +(LessThan (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (LessThan (TSTW x y)) +(LessEqual (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (LessEqual (TSTW x y)) +(GreaterThan (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (GreaterThan (TSTW x y)) +(GreaterEqual (CMPWconst [0] z:(AND x y))) && z.Uses == 1 => (GreaterEqual (TSTW x y)) + +(Equal (CMPconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (Equal (TSTconst [c] y)) +(NotEqual (CMPconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (NotEqual (TSTconst [c] y)) +(LessThan (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (LessThan (TSTconst [c] y)) +(LessEqual (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (LessEqual (TSTconst [c] y)) +(GreaterThan (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (GreaterThan (TSTconst [c] y)) +(GreaterEqual (CMPWconst [0] x:(ANDconst [c] y))) && x.Uses == 1 => (GreaterEqual (TSTconst [c] y)) (EQ (CMPconst [0] x:(ADDconst [c] y)) yes no) && x.Uses == 1 => (EQ (CMNconst [c] y) yes no) (NE (CMPconst [0] x:(ADDconst [c] y)) yes no) && x.Uses == 1 => (NE (CMNconst [c] y) yes no) @@ -645,19 +738,13 @@ (GT (CMPWconst [0] z:(ADD x y)) yes no) && z.Uses == 1 => (GTnoov (CMNW x y) yes no) (GE (CMPWconst [0] z:(ADD x y)) yes no) && z.Uses == 1 => (GEnoov (CMNW x y) yes no) +// CMP(x,-y) -> CMN(x,y) is only valid for unordered comparison, if y can be -1<<63 (EQ (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMN x y) yes no) (NE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMN x y) yes no) -(LT (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (LT (CMN x y) yes no) -(LE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (LE (CMN x y) yes no) -(GT (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (GT (CMN x y) yes no) -(GE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (GE (CMN x y) yes no) +// CMPW(x,-y) -> CMNW(x,y) is only valid for unordered comparison, if y can be -1<<31 (EQ (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMNW x y) yes no) (NE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMNW x y) yes no) -(LT (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (LT (CMNW x y) yes no) -(LE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (LE (CMNW x y) yes no) -(GT (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (GT (CMNW x y) yes no) -(GE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (GE (CMNW x y) yes no) (EQ (CMPconst [0] x) yes no) => (Z x yes no) (NE (CMPconst [0] x) yes no) => (NZ x yes no) @@ -734,6 +821,9 @@ (MOVDload [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => (MOVDload [off1+int32(off2)] {sym} ptr mem) +(LDP [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) + && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => + (LDP [off1+int32(off2)] {sym} ptr mem) (FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => (FMOVSload [off1+int32(off2)] {sym} ptr mem) @@ -913,6 +1003,10 @@ && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) +(LDP [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) + && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => + (LDP [off1+off2] {mergeSym(sym1,sym2)} ptr mem) (FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) => @@ -1024,6 +1118,7 @@ //(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWreg x) //(MOVWUload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWUreg x) //(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x +//(LDP [off] {sym} ptr (STP [off2] {sym2} ptr2 x y _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x y //(FMOVSload [off] {sym} ptr (FMOVSstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x //(FMOVDload [off] {sym} ptr (FMOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x @@ -1151,7 +1246,7 @@ // But for now, this is enough to get rid of lots of them. (MOVDnop (MOVDconst [c])) => (MOVDconst [c]) -// fold constant into arithmatic ops +// fold constant into arithmetic ops (ADD x (MOVDconst [c])) => (ADDconst [c] x) (SUB x (MOVDconst [c])) => (SUBconst [c] x) (AND x (MOVDconst [c])) => (ANDconst [c] x) @@ -1165,15 +1260,21 @@ (EON x (MOVDconst [c])) => (XORconst [^c] x) (ORN x (MOVDconst [c])) => (ORconst [^c] x) -(SLL x (MOVDconst [c])) => (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64) +(SLL x (MOVDconst [c])) => (SLLconst x [c&63]) (SRL x (MOVDconst [c])) => (SRLconst x [c&63]) (SRA x (MOVDconst [c])) => (SRAconst x [c&63]) +(SLL x (ANDconst [63] y)) => (SLL x y) +(SRL x (ANDconst [63] y)) => (SRL x y) +(SRA x (ANDconst [63] y)) => (SRA x y) (CMP x (MOVDconst [c])) => (CMPconst [c] x) (CMP (MOVDconst [c]) x) => (InvertFlags (CMPconst [c] x)) (CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x) (CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x)) +(ROR x (MOVDconst [c])) => (RORconst x [c&63]) +(RORW x (MOVDconst [c])) => (RORWconst x [c&31]) + // Canonicalize the order of arguments to comparisons - helps with CSE. ((CMP|CMPW) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW) y x)) @@ -1359,6 +1460,7 @@ (XOR x (MVN y)) => (EON x y) (OR x (MVN y)) => (ORN x y) (MVN (XOR x y)) => (EON x y) +(NEG (NEG x)) => x (CSEL [cc] (MOVDconst [-1]) (MOVDconst [0]) flag) => (CSETM [cc] flag) (CSEL [cc] (MOVDconst [0]) (MOVDconst [-1]) flag) => (CSETM [arm64Negate(cc)] flag) @@ -1561,9 +1663,9 @@ (GreaterThanF (InvertFlags x)) => (LessThanF x) (GreaterEqualF (InvertFlags x)) => (LessEqualF x) -// Boolean-generating instructions always +// Boolean-generating instructions (NOTE: NOT all boolean Values) always // zero upper bit of the register; no need to zero-extend -(MOVBUreg x) && x.Type.IsBoolean() => (MOVDreg x) +(MOVBUreg x:((Equal|NotEqual|LessThan|LessThanU|LessThanF|LessEqual|LessEqualU|LessEqualF|GreaterThan|GreaterThanU|GreaterThanF|GreaterEqual|GreaterEqualU|GreaterEqualF) _)) => (MOVDreg x) // absorb flag constants into conditional instructions (CSEL [cc] x _ flag) && ccARM64Eval(cc, flag) > 0 => x @@ -1596,6 +1698,7 @@ (MVN x:(SLLconst [c] y)) && clobberIfDead(x) => (MVNshiftLL [c] y) (MVN x:(SRLconst [c] y)) && clobberIfDead(x) => (MVNshiftRL [c] y) (MVN x:(SRAconst [c] y)) && clobberIfDead(x) => (MVNshiftRA [c] y) +(MVN x:(RORconst [c] y)) && clobberIfDead(x) => (MVNshiftRO [c] y) (ADD x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ADDshiftLL x0 y [c]) (ADD x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ADDshiftRL x0 y [c]) (ADD x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ADDshiftRA x0 y [c]) @@ -1605,21 +1708,27 @@ (AND x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ANDshiftLL x0 y [c]) (AND x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ANDshiftRL x0 y [c]) (AND x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ANDshiftRA x0 y [c]) +(AND x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ANDshiftRO x0 y [c]) (OR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORshiftLL x0 y [c]) // useful for combined load (OR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORshiftRL x0 y [c]) (OR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORshiftRA x0 y [c]) +(OR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORshiftRO x0 y [c]) (XOR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (XORshiftLL x0 y [c]) (XOR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (XORshiftRL x0 y [c]) (XOR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (XORshiftRA x0 y [c]) +(XOR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (XORshiftRO x0 y [c]) (BIC x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (BICshiftLL x0 y [c]) (BIC x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (BICshiftRL x0 y [c]) (BIC x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (BICshiftRA x0 y [c]) +(BIC x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (BICshiftRO x0 y [c]) (ORN x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORNshiftLL x0 y [c]) (ORN x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORNshiftRL x0 y [c]) (ORN x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORNshiftRA x0 y [c]) +(ORN x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORNshiftRO x0 y [c]) (EON x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (EONshiftLL x0 y [c]) (EON x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (EONshiftRL x0 y [c]) (EON x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (EONshiftRA x0 y [c]) +(EON x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (EONshiftRO x0 y [c]) (CMP x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (CMPshiftLL x0 y [c]) (CMP x0:(SLLconst [c] y) x1) && clobberIfDead(x0) => (InvertFlags (CMPshiftLL x1 y [c])) (CMP x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (CMPshiftRL x0 y [c]) @@ -1632,6 +1741,7 @@ (TST x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (TSTshiftLL x0 y [c]) (TST x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (TSTshiftRL x0 y [c]) (TST x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (TSTshiftRA x0 y [c]) +(TST x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (TSTshiftRO x0 y [c]) // prefer *const ops to *shift ops (ADDshiftLL (MOVDconst [c]) x [d]) => (ADDconst [c] (SLLconst x [d])) @@ -1640,12 +1750,15 @@ (ANDshiftLL (MOVDconst [c]) x [d]) => (ANDconst [c] (SLLconst x [d])) (ANDshiftRL (MOVDconst [c]) x [d]) => (ANDconst [c] (SRLconst x [d])) (ANDshiftRA (MOVDconst [c]) x [d]) => (ANDconst [c] (SRAconst x [d])) +(ANDshiftRO (MOVDconst [c]) x [d]) => (ANDconst [c] (RORconst x [d])) (ORshiftLL (MOVDconst [c]) x [d]) => (ORconst [c] (SLLconst x [d])) (ORshiftRL (MOVDconst [c]) x [d]) => (ORconst [c] (SRLconst x [d])) (ORshiftRA (MOVDconst [c]) x [d]) => (ORconst [c] (SRAconst x [d])) +(ORshiftRO (MOVDconst [c]) x [d]) => (ORconst [c] (RORconst x [d])) (XORshiftLL (MOVDconst [c]) x [d]) => (XORconst [c] (SLLconst x [d])) (XORshiftRL (MOVDconst [c]) x [d]) => (XORconst [c] (SRLconst x [d])) (XORshiftRA (MOVDconst [c]) x [d]) => (XORconst [c] (SRAconst x [d])) +(XORshiftRO (MOVDconst [c]) x [d]) => (XORconst [c] (RORconst x [d])) (CMPshiftLL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SLLconst x [d]))) (CMPshiftRL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRLconst x [d]))) (CMPshiftRA (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRAconst x [d]))) @@ -1655,11 +1768,13 @@ (TSTshiftLL (MOVDconst [c]) x [d]) => (TSTconst [c] (SLLconst x [d])) (TSTshiftRL (MOVDconst [c]) x [d]) => (TSTconst [c] (SRLconst x [d])) (TSTshiftRA (MOVDconst [c]) x [d]) => (TSTconst [c] (SRAconst x [d])) +(TSTshiftRO (MOVDconst [c]) x [d]) => (TSTconst [c] (RORconst x [d])) // constant folding in *shift ops (MVNshiftLL (MOVDconst [c]) [d]) => (MOVDconst [^int64(uint64(c)< (MOVDconst [^int64(uint64(c)>>uint64(d))]) (MVNshiftRA (MOVDconst [c]) [d]) => (MOVDconst [^(c>>uint64(d))]) +(MVNshiftRO (MOVDconst [c]) [d]) => (MOVDconst [^rotateRight64(c, d)]) (NEGshiftLL (MOVDconst [c]) [d]) => (MOVDconst [-int64(uint64(c)< (MOVDconst [-int64(uint64(c)>>uint64(d))]) (NEGshiftRA (MOVDconst [c]) [d]) => (MOVDconst [-(c>>uint64(d))]) @@ -1672,21 +1787,27 @@ (ANDshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [int64(uint64(c)< (ANDconst x [int64(uint64(c)>>uint64(d))]) (ANDshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [c>>uint64(d)]) +(ANDshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [rotateRight64(c, d)]) (ORshiftLL x (MOVDconst [c]) [d]) => (ORconst x [int64(uint64(c)< (ORconst x [int64(uint64(c)>>uint64(d))]) (ORshiftRA x (MOVDconst [c]) [d]) => (ORconst x [c>>uint64(d)]) +(ORshiftRO x (MOVDconst [c]) [d]) => (ORconst x [rotateRight64(c, d)]) (XORshiftLL x (MOVDconst [c]) [d]) => (XORconst x [int64(uint64(c)< (XORconst x [int64(uint64(c)>>uint64(d))]) (XORshiftRA x (MOVDconst [c]) [d]) => (XORconst x [c>>uint64(d)]) +(XORshiftRO x (MOVDconst [c]) [d]) => (XORconst x [rotateRight64(c, d)]) (BICshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [^int64(uint64(c)< (ANDconst x [^int64(uint64(c)>>uint64(d))]) (BICshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [^(c>>uint64(d))]) +(BICshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [^rotateRight64(c, d)]) (ORNshiftLL x (MOVDconst [c]) [d]) => (ORconst x [^int64(uint64(c)< (ORconst x [^int64(uint64(c)>>uint64(d))]) (ORNshiftRA x (MOVDconst [c]) [d]) => (ORconst x [^(c>>uint64(d))]) +(ORNshiftRO x (MOVDconst [c]) [d]) => (ORconst x [^rotateRight64(c, d)]) (EONshiftLL x (MOVDconst [c]) [d]) => (XORconst x [^int64(uint64(c)< (XORconst x [^int64(uint64(c)>>uint64(d))]) (EONshiftRA x (MOVDconst [c]) [d]) => (XORconst x [^(c>>uint64(d))]) +(EONshiftRO x (MOVDconst [c]) [d]) => (XORconst x [^rotateRight64(c, d)]) (CMPshiftLL x (MOVDconst [c]) [d]) => (CMPconst x [int64(uint64(c)< (CMPconst x [int64(uint64(c)>>uint64(d))]) (CMPshiftRA x (MOVDconst [c]) [d]) => (CMPconst x [c>>uint64(d)]) @@ -1696,79 +1817,36 @@ (TSTshiftLL x (MOVDconst [c]) [d]) => (TSTconst x [int64(uint64(c)< (TSTconst x [int64(uint64(c)>>uint64(d))]) (TSTshiftRA x (MOVDconst [c]) [d]) => (TSTconst x [c>>uint64(d)]) +(TSTshiftRO x (MOVDconst [c]) [d]) => (TSTconst x [rotateRight64(c, d)]) // simplification with *shift ops -(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(EONshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(EONshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(EONshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) - -// Generate rotates with const shift -(ADDshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x) -( ORshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x) -(XORshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x) -(ADDshiftRL [c] (SLLconst x [64-c]) x) => (RORconst [ c] x) -( ORshiftRL [c] (SLLconst x [64-c]) x) => (RORconst [ c] x) -(XORshiftRL [c] (SLLconst x [64-c]) x) => (RORconst [ c] x) - -(ADDshiftLL [c] (UBFX [bfc] x) x) && c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - => (RORWconst [32-c] x) -( ORshiftLL [c] (UBFX [bfc] x) x) && c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - => (RORWconst [32-c] x) -(XORshiftLL [c] (UBFX [bfc] x) x) && c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - => (RORWconst [32-c] x) -(ADDshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) && c < 32 && t.Size() == 4 => (RORWconst [c] x) -( ORshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) && c < 32 && t.Size() == 4 => (RORWconst [c] x) -(XORshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) && c < 32 && t.Size() == 4 => (RORWconst [c] x) - -(RORconst [c] (RORconst [d] x)) => (RORconst [(c+d)&63] x) -(RORWconst [c] (RORWconst [d] x)) => (RORWconst [(c+d)&31] x) - -// Generate rotates with non-const shift. -// These rules match the Go source code like -// y &= 63 -// x << y | x >> (64-y) -// "|" can also be "^" or "+". -// As arm64 does not have a ROL instruction, so ROL(x, y) is replaced by ROR(x, -y). -((ADD|OR|XOR) (SLL x (ANDconst [63] y)) - (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) - (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) && cc == OpARM64LessThanU - => (ROR x (NEG y)) -((ADD|OR|XOR) (SRL x (ANDconst [63] y)) - (CSEL0 [cc] (SLL x (SUB (MOVDconst [64]) (ANDconst [63] y))) - (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) && cc == OpARM64LessThanU - => (ROR x y) - -// These rules match the Go source code like -// y &= 31 -// x << y | x >> (32-y) -// "|" can also be "^" or "+". -// As arm64 does not have a ROLW instruction, so ROLW(x, y) is replaced by RORW(x, -y). -((ADD|OR|XOR) (SLL x (ANDconst [31] y)) - (CSEL0 [cc] (SRL (MOVWUreg x) (SUB (MOVDconst [32]) (ANDconst [31] y))) - (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) && cc == OpARM64LessThanU - => (RORW x (NEG y)) -((ADD|OR|XOR) (SRL (MOVWUreg x) (ANDconst [31] y)) - (CSEL0 [cc] (SLL x (SUB (MOVDconst [32]) (ANDconst [31] y))) - (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) && cc == OpARM64LessThanU - => (RORW x y) +(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(ANDshiftLL y:(SLLconst x [c]) x [c]) => y +(ANDshiftRL y:(SRLconst x [c]) x [c]) => y +(ANDshiftRA y:(SRAconst x [c]) x [c]) => y +(ANDshiftRO y:(RORconst x [c]) x [c]) => y +(ORshiftLL y:(SLLconst x [c]) x [c]) => y +(ORshiftRL y:(SRLconst x [c]) x [c]) => y +(ORshiftRA y:(SRAconst x [c]) x [c]) => y +(ORshiftRO y:(RORconst x [c]) x [c]) => y +(XORshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0]) +(EONshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1]) // rev16w | rev16 // ((x>>8) | (x<<8)) => (REV16W x), the type of x is uint16, "|" can also be "^" or "+". @@ -1824,16 +1902,26 @@ // sbfiz // (x << lc) >> rc (SRAconst [rc] (SLLconst [lc] x)) && lc > rc => (SBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) +// int64(x << lc) (MOVWreg (SLLconst [lc] x)) && lc < 32 => (SBFIZ [armBFAuxInt(lc, 32-lc)] x) (MOVHreg (SLLconst [lc] x)) && lc < 16 => (SBFIZ [armBFAuxInt(lc, 16-lc)] x) (MOVBreg (SLLconst [lc] x)) && lc < 8 => (SBFIZ [armBFAuxInt(lc, 8-lc)] x) +// int64(x) << lc +(SLLconst [lc] (MOVWreg x)) => (SBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) +(SLLconst [lc] (MOVHreg x)) => (SBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) +(SLLconst [lc] (MOVBreg x)) => (SBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) // sbfx // (x << lc) >> rc (SRAconst [rc] (SLLconst [lc] x)) && lc <= rc => (SBFX [armBFAuxInt(rc-lc, 64-rc)] x) +// int64(x) >> rc (SRAconst [rc] (MOVWreg x)) && rc < 32 => (SBFX [armBFAuxInt(rc, 32-rc)] x) (SRAconst [rc] (MOVHreg x)) && rc < 16 => (SBFX [armBFAuxInt(rc, 16-rc)] x) (SRAconst [rc] (MOVBreg x)) && rc < 8 => (SBFX [armBFAuxInt(rc, 8-rc)] x) +// merge sbfx and sign-extension into sbfx +(MOVWreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (SBFX [bfc] x) +(MOVHreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 16 => (SBFX [bfc] x) +(MOVBreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 8 => (SBFX [bfc] x) // sbfiz/sbfx combinations: merge shifts into bitfield ops (SRAconst [sc] (SBFIZ [bfc] x)) && sc < bfc.getARM64BFlsb() @@ -1843,42 +1931,52 @@ => (SBFX [armBFAuxInt(sc-bfc.getARM64BFlsb(), bfc.getARM64BFlsb()+bfc.getARM64BFwidth()-sc)] x) // ubfiz +// (x << lc) >> rc +(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) +// uint64(x) << lc +(SLLconst [lc] (MOVWUreg x)) => (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) +(SLLconst [lc] (MOVHUreg x)) => (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) +(SLLconst [lc] (MOVBUreg x)) => (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) +// uint64(x << lc) +(MOVWUreg (SLLconst [lc] x)) && lc < 32 => (UBFIZ [armBFAuxInt(lc, 32-lc)] x) +(MOVHUreg (SLLconst [lc] x)) && lc < 16 => (UBFIZ [armBFAuxInt(lc, 16-lc)] x) +(MOVBUreg (SLLconst [lc] x)) && lc < 8 => (UBFIZ [armBFAuxInt(lc, 8-lc)] x) + +// merge ANDconst into ubfiz // (x & ac) << sc (SLLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, 0) => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) -(SLLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFIZ [armBFAuxInt(sc, 32)] x) -(SLLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFIZ [armBFAuxInt(sc, 16)] x) -(SLLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFIZ [armBFAuxInt(sc, 8)] x) // (x << sc) & ac (ANDconst [ac] (SLLconst [sc] x)) && isARM64BFMask(sc, ac, sc) => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) -(MOVWUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) -(MOVHUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) -(MOVBUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) -// (x << lc) >> rc -(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) // ubfx +// (x << lc) >> rc +(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) +// uint64(x) >> rc +(SRLconst [rc] (MOVWUreg x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32-rc)] x) +(SRLconst [rc] (MOVHUreg x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16-rc)] x) +(SRLconst [rc] (MOVBUreg x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8-rc)] x) +// uint64(x >> rc) +(MOVWUreg (SRLconst [rc] x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32)] x) +(MOVHUreg (SRLconst [rc] x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16)] x) +(MOVBUreg (SRLconst [rc] x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8)] x) +// merge ANDconst into ubfx // (x >> sc) & ac (ANDconst [ac] (SRLconst [sc] x)) && isARM64BFMask(sc, ac, 0) => (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) -(MOVWUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFX [armBFAuxInt(sc, 32)] x) -(MOVHUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFX [armBFAuxInt(sc, 16)] x) -(MOVBUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFX [armBFAuxInt(sc, 8)] x) // (x & ac) >> sc (SRLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, sc) => (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) -(SRLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) -(SRLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) -(SRLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) -// (x << lc) >> rc -(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) +// merge ANDconst and ubfx into ubfx +(ANDconst [c] (UBFX [bfc] x)) && isARM64BFMask(0, c, 0) => + (UBFX [armBFAuxInt(bfc.getARM64BFlsb(), min(bfc.getARM64BFwidth(), arm64BFWidth(c, 0)))] x) +(UBFX [bfc] (ANDconst [c] x)) && isARM64BFMask(0, c, 0) && bfc.getARM64BFlsb() + bfc.getARM64BFwidth() <= arm64BFWidth(c, 0) => + (UBFX [bfc] x) +// merge ubfx and zerso-extension into ubfx +(MOVWUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (UBFX [bfc] x) +(MOVHUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 16 => (UBFX [bfc] x) +(MOVBUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 8 => (UBFX [bfc] x) // ubfiz/ubfx combinations: merge shifts into bitfield ops (SRLconst [sc] (UBFX [bfc] x)) && sc < bfc.getARM64BFwidth() @@ -2860,6 +2958,10 @@ (MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVDload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +// Prefetch instructions (aux is option: 0 - PLDL1KEEP; 1 - PLDL1STRM) +(PrefetchCache addr mem) => (PRFM [0] addr mem) +(PrefetchCacheStreamed addr mem) => (PRFM [1] addr mem) + // Arch-specific inlining for small or disjoint runtime.memmove (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem))))) && sz >= 0 @@ -2868,3 +2970,19 @@ && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call) => (Move [sz] dst src mem) + +// Match post-lowering calls, register version. +(SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem)) + && sz >= 0 + && isSameCall(sym, "runtime.memmove") + && call.Uses == 1 + && isInlinableMemmove(dst, src, sz, config) + && clobber(call) + => (Move [sz] dst src mem) + +((REV|REVW) ((REV|REVW) p)) => p + +// runtime/internal/math.MulUintptr intrinsics + +(Select0 (Mul64uover x y)) => (MUL x y) +(Select1 (Mul64uover x y)) => (NotEqual (CMPconst (UMULH x y) [0])) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index 18a5666b40f0df..5aeaf3ad96f433 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -154,9 +154,9 @@ func init() { gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}} gp2flags1flags = regInfo{inputs: []regMask{gp, gp, 0}, outputs: []regMask{gp, 0}} gp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} - gp22 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp, gp}} gp31 = regInfo{inputs: []regMask{gpg, gpg, gpg}, outputs: []regMask{gp}} gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} + gpload2 = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gpg, gpg}} gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} gpstore0 = regInfo{inputs: []regMask{gpspsbg}} gpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}} @@ -175,6 +175,7 @@ func init() { fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} fpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, fp}} readflags = regInfo{inputs: nil, outputs: []regMask{gp}} + prefreg = regInfo{inputs: []regMask{gpspsbg}} ) ops := []opData{ // binary ops @@ -226,8 +227,6 @@ func init() { {name: "EON", argLength: 2, reg: gp21, asm: "EON"}, // arg0 ^ ^arg1 {name: "ORN", argLength: 2, reg: gp21, asm: "ORN"}, // arg0 | ^arg1 - {name: "LoweredMuluhilo", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, returns (hi, lo) - // unary ops {name: "MVN", argLength: 1, reg: gp11, asm: "MVN"}, // ^arg0 {name: "NEG", argLength: 1, reg: gp11, asm: "NEG"}, // -arg0 @@ -284,9 +283,9 @@ func init() { {name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to auxInt {name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1, 32 bit {name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit - {name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1 + {name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1, provided arg1 is not 1<<63 {name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"}, // arg0 compare to -auxInt - {name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags", commutative: true}, // arg0 compare to -arg1, 32 bit + {name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags", commutative: true}, // arg0 compare to -arg1, 32 bit, provided arg1 is not 1<<31 {name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit {name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0 {name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int64", typ: "Flags"}, // arg0 & auxInt compare to 0 @@ -301,6 +300,7 @@ func init() { {name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "MVNshiftRO", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "NEGshiftLL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "NEGshiftRA", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63. @@ -313,21 +313,27 @@ func init() { {name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "ANDshiftRO", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. + {name: "ORshiftRO", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63. {name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. + {name: "XORshiftRO", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63. {name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "BICshiftRO", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "EONshiftLL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "EONshiftRA", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "EONshiftRO", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "ORNshiftLL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "ORNshiftRA", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "ORNshiftRO", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. @@ -337,6 +343,7 @@ func init() { {name: "TSTshiftLL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1<>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63. {name: "TSTshiftRA", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63. + {name: "TSTshiftRO", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1 ROR auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63. // bitfield ops // for all bitfield ops lsb is auxInt>>8, width is auxInt&0xff @@ -360,15 +367,16 @@ func init() { {name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB - {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read"}, // load from ptr = arg0 + auxInt + aux, returns the tuple <*(*uint64)ptr, *(*uint64)(ptr+8)>. arg1=mem. + {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. // register indexed load {name: "MOVDloadidx", argLength: 3, reg: gp2load, asm: "MOVD", typ: "UInt64"}, // load 64-bit dword from arg0 + arg1, arg2 = mem. @@ -482,9 +490,10 @@ func init() { {name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop"}, // auxint(flags) ? -1 : 0 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem // pseudo-ops {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. @@ -574,18 +583,18 @@ func init() { // arg2 = address of the last element of src // arg3 = mem // returns mem - // MOVD.P 8(R16), Rtmp - // MOVD.P Rtmp, 8(R17) + // LDP.P 16(R16), (R25, Rtmp) + // STP.P (R25, Rtmp), 16(R17) // CMP Rarg2, R16 // BLE -3(PC) // Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled. - // the-end-of-src - 8 is within the area to copy, ok to spill. + // the-end-of-src - 16 is within the area to copy, ok to spill. { name: "LoweredMove", argLength: 4, reg: regInfo{ inputs: []regMask{buildReg("R17"), buildReg("R16"), gp}, - clobbers: buildReg("R16 R17"), + clobbers: buildReg("R16 R17 R25"), }, clobberFlags: true, faultOnNilArg0: true, @@ -729,6 +738,13 @@ func init() { {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). + + // Prefetch instruction + // Do prefetch arg0 address with option aux. arg0=addr, arg1=memory, aux=option. + {name: "PRFM", argLength: 2, aux: "Int64", reg: prefreg, asm: "PRFM", hasSideEffects: true}, + + // Publication barrier + {name: "DMB", argLength: 1, aux: "Int64", asm: "DMB", hasSideEffects: true}, // Do data barrier. arg0=memory, aux=option. } blocks := []blockData{ @@ -756,18 +772,26 @@ func init() { {name: "LEnoov", controls: 1}, // 'LE' but without honoring overflow {name: "GTnoov", controls: 1}, // 'GT' but without honoring overflow {name: "GEnoov", controls: 1}, // 'GE' but without honoring overflow + + // JUMPTABLE implements jump tables. + // Aux is the symbol (an *obj.LSym) for the jump table. + // control[0] is the index into the jump table. + // control[1] is the address of the jump table (the address of the symbol stored in Aux). + {name: "JUMPTABLE", controls: 2, aux: "Sym"}, } archs = append(archs, arch{ - name: "ARM64", - pkg: "cmd/internal/obj/arm64", - genfile: "../../arm64/ssa.go", - ops: ops, - blocks: blocks, - regnames: regNamesARM64, - gpregmask: gp, - fpregmask: fp, - framepointerreg: -1, // not used - linkreg: int8(num["R30"]), + name: "ARM64", + pkg: "cmd/internal/obj/arm64", + genfile: "../../arm64/ssa.go", + ops: ops, + blocks: blocks, + regnames: regNamesARM64, + ParamIntRegNames: "R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15", + ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15", + gpregmask: gp, + fpregmask: fp, + framepointerreg: -1, // not used + linkreg: int8(num["R30"]), }) } diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index d1f86039a36e5c..3803f273c1dafd 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -228,14 +228,15 @@ func init() { // shifts {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 256 - {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt + {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, 0 <= auxInt < 32 {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 256 - {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned + {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned, 0 <= auxInt < 32 {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 256 - {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed + {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, 0 <= auxInt < 32 {name: "SRR", argLength: 2, reg: gp21}, // arg0 right rotate by arg1 bits - {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits + {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits, 0 <= auxInt < 32 + // auxInt for all of these satisfy 0 <= auxInt < 32 {name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1<>auxInt, unsigned shift {name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift @@ -330,7 +331,7 @@ func init() { // comparisons {name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1 {name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt - {name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1 + {name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags", commutative: true}, // arg0 compare to -arg1, provided arg1 is not 1<<63 {name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt {name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0 {name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int32", typ: "Flags"}, // arg0 & auxInt compare to 0 @@ -431,6 +432,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/LOONG64.rules b/src/cmd/compile/internal/ssa/gen/LOONG64.rules new file mode 100644 index 00000000000000..431d95f0969d29 --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/LOONG64.rules @@ -0,0 +1,677 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(Add(Ptr|64|32|16|8) ...) => (ADDV ...) +(Add(32|64)F ...) => (ADD(F|D) ...) + +(Sub(Ptr|64|32|16|8) ...) => (SUBV ...) +(Sub(32|64)F ...) => (SUB(F|D) ...) + +(Mul(64|32|16|8) x y) => (Select1 (MULVU x y)) +(Mul(32|64)F ...) => (MUL(F|D) ...) +(Mul64uhilo ...) => (MULVU ...) +(Select0 (Mul64uover x y)) => (Select1 (MULVU x y)) +(Select1 (Mul64uover x y)) => (SGTU (Select0 (MULVU x y)) (MOVVconst [0])) + +(Hmul64 x y) => (Select0 (MULV x y)) +(Hmul64u x y) => (Select0 (MULVU x y)) +(Hmul32 x y) => (SRAVconst (Select1 (MULV (SignExt32to64 x) (SignExt32to64 y))) [32]) +(Hmul32u x y) => (SRLVconst (Select1 (MULVU (ZeroExt32to64 x) (ZeroExt32to64 y))) [32]) + +(Div64 x y) => (Select1 (DIVV x y)) +(Div64u x y) => (Select1 (DIVVU x y)) +(Div32 x y) => (Select1 (DIVV (SignExt32to64 x) (SignExt32to64 y))) +(Div32u x y) => (Select1 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Div16 x y) => (Select1 (DIVV (SignExt16to64 x) (SignExt16to64 y))) +(Div16u x y) => (Select1 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y))) +(Div8 x y) => (Select1 (DIVV (SignExt8to64 x) (SignExt8to64 y))) +(Div8u x y) => (Select1 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y))) +(Div(32|64)F ...) => (DIV(F|D) ...) + +(Mod64 x y) => (Select0 (DIVV x y)) +(Mod64u x y) => (Select0 (DIVVU x y)) +(Mod32 x y) => (Select0 (DIVV (SignExt32to64 x) (SignExt32to64 y))) +(Mod32u x y) => (Select0 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Mod16 x y) => (Select0 (DIVV (SignExt16to64 x) (SignExt16to64 y))) +(Mod16u x y) => (Select0 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y))) +(Mod8 x y) => (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y))) +(Mod8u x y) => (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y))) + +// (x + y) / 2 with x>=y => (x - y) / 2 + y +(Avg64u x y) => (ADDV (SRLVconst (SUBV x y) [1]) y) + +(And(64|32|16|8) ...) => (AND ...) +(Or(64|32|16|8) ...) => (OR ...) +(Xor(64|32|16|8) ...) => (XOR ...) + +// shifts +// hardware instruction uses only the low 6 bits of the shift +// we compare to 64 to ensure Go semantics for large shifts +(Lsh64x64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) +(Lsh64x32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) +(Lsh64x16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) +(Lsh64x8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + +(Lsh32x64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) +(Lsh32x32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) +(Lsh32x16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) +(Lsh32x8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + +(Lsh16x64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) +(Lsh16x32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) +(Lsh16x16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) +(Lsh16x8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + +(Lsh8x64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) +(Lsh8x32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) +(Lsh8x16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) +(Lsh8x8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + +(Rsh64Ux64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV x y)) +(Rsh64Ux32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV x (ZeroExt32to64 y))) +(Rsh64Ux16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV x (ZeroExt16to64 y))) +(Rsh64Ux8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV x (ZeroExt8to64 y))) + +(Rsh32Ux64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt32to64 x) y)) +(Rsh32Ux32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Rsh32Ux16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt16to64 y))) +(Rsh32Ux8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt8to64 y))) + +(Rsh16Ux64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt16to64 x) y)) +(Rsh16Ux32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y))) +(Rsh16Ux16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt16to64 y))) +(Rsh16Ux8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt8to64 y))) + +(Rsh8Ux64 x y) => (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt8to64 x) y)) +(Rsh8Ux32 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt32to64 y))) +(Rsh8Ux16 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt16to64 y))) +(Rsh8Ux8 x y) => (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt8to64 y))) + +(Rsh64x64 x y) => (SRAV x (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +(Rsh64x32 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +(Rsh64x16 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +(Rsh64x8 x y) => (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + +(Rsh32x64 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +(Rsh32x32 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +(Rsh32x16 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +(Rsh32x8 x y) => (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + +(Rsh16x64 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +(Rsh16x32 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +(Rsh16x16 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +(Rsh16x8 x y) => (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + +(Rsh8x64 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) +(Rsh8x32 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) +(Rsh8x16 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) +(Rsh8x8 x y) => (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + +// rotates +(RotateLeft8 x (MOVVconst [c])) => (Or8 (Lsh8x64 x (MOVVconst [c&7])) (Rsh8Ux64 x (MOVVconst [-c&7]))) +(RotateLeft16 x (MOVVconst [c])) => (Or16 (Lsh16x64 x (MOVVconst [c&15])) (Rsh16Ux64 x (MOVVconst [-c&15]))) +(RotateLeft32 x y) => (ROTR x (NEGV y)) +(RotateLeft64 x y) => (ROTRV x (NEGV y)) + +// unary ops +(Neg(64|32|16|8) ...) => (NEGV ...) +(Neg(32|64)F ...) => (NEG(F|D) ...) + +(Com(64|32|16|8) x) => (NOR (MOVVconst [0]) x) + +(Sqrt ...) => (SQRTD ...) +(Sqrt32 ...) => (SQRTF ...) + +// boolean ops -- booleans are represented with 0=false, 1=true +(AndB ...) => (AND ...) +(OrB ...) => (OR ...) +(EqB x y) => (XOR (MOVVconst [1]) (XOR x y)) +(NeqB ...) => (XOR ...) +(Not x) => (XORconst [1] x) + +// constants +(Const(64|32|16|8) [val]) => (MOVVconst [int64(val)]) +(Const(32|64)F [val]) => (MOV(F|D)const [float64(val)]) +(ConstNil) => (MOVVconst [0]) +(ConstBool [t]) => (MOVVconst [int64(b2i(t))]) + +(Slicemask x) => (SRAVconst (NEGV x) [63]) + +// truncations +// Because we ignore high parts of registers, truncates are just copies. +(Trunc16to8 ...) => (Copy ...) +(Trunc32to8 ...) => (Copy ...) +(Trunc32to16 ...) => (Copy ...) +(Trunc64to8 ...) => (Copy ...) +(Trunc64to16 ...) => (Copy ...) +(Trunc64to32 ...) => (Copy ...) + +// Zero-/Sign-extensions +(ZeroExt8to16 ...) => (MOVBUreg ...) +(ZeroExt8to32 ...) => (MOVBUreg ...) +(ZeroExt16to32 ...) => (MOVHUreg ...) +(ZeroExt8to64 ...) => (MOVBUreg ...) +(ZeroExt16to64 ...) => (MOVHUreg ...) +(ZeroExt32to64 ...) => (MOVWUreg ...) + +(SignExt8to16 ...) => (MOVBreg ...) +(SignExt8to32 ...) => (MOVBreg ...) +(SignExt16to32 ...) => (MOVHreg ...) +(SignExt8to64 ...) => (MOVBreg ...) +(SignExt16to64 ...) => (MOVHreg ...) +(SignExt32to64 ...) => (MOVWreg ...) + +// float <=> int conversion +(Cvt32to32F ...) => (MOVWF ...) +(Cvt32to64F ...) => (MOVWD ...) +(Cvt64to32F ...) => (MOVVF ...) +(Cvt64to64F ...) => (MOVVD ...) +(Cvt32Fto32 ...) => (TRUNCFW ...) +(Cvt64Fto32 ...) => (TRUNCDW ...) +(Cvt32Fto64 ...) => (TRUNCFV ...) +(Cvt64Fto64 ...) => (TRUNCDV ...) +(Cvt32Fto64F ...) => (MOVFD ...) +(Cvt64Fto32F ...) => (MOVDF ...) + +(CvtBoolToUint8 ...) => (Copy ...) + +(Round(32|64)F ...) => (Copy ...) + +// comparisons +(Eq8 x y) => (SGTU (MOVVconst [1]) (XOR (ZeroExt8to64 x) (ZeroExt8to64 y))) +(Eq16 x y) => (SGTU (MOVVconst [1]) (XOR (ZeroExt16to64 x) (ZeroExt16to64 y))) +(Eq32 x y) => (SGTU (MOVVconst [1]) (XOR (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Eq64 x y) => (SGTU (MOVVconst [1]) (XOR x y)) +(EqPtr x y) => (SGTU (MOVVconst [1]) (XOR x y)) +(Eq(32|64)F x y) => (FPFlagTrue (CMPEQ(F|D) x y)) + +(Neq8 x y) => (SGTU (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)) (MOVVconst [0])) +(Neq16 x y) => (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to64 y)) (MOVVconst [0])) +(Neq32 x y) => (SGTU (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)) (MOVVconst [0])) +(Neq64 x y) => (SGTU (XOR x y) (MOVVconst [0])) +(NeqPtr x y) => (SGTU (XOR x y) (MOVVconst [0])) +(Neq(32|64)F x y) => (FPFlagFalse (CMPEQ(F|D) x y)) + +(Less8 x y) => (SGT (SignExt8to64 y) (SignExt8to64 x)) +(Less16 x y) => (SGT (SignExt16to64 y) (SignExt16to64 x)) +(Less32 x y) => (SGT (SignExt32to64 y) (SignExt32to64 x)) +(Less64 x y) => (SGT y x) +(Less(32|64)F x y) => (FPFlagTrue (CMPGT(F|D) y x)) // reverse operands to work around NaN + +(Less8U x y) => (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x)) +(Less16U x y) => (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x)) +(Less32U x y) => (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x)) +(Less64U x y) => (SGTU y x) + +(Leq8 x y) => (XOR (MOVVconst [1]) (SGT (SignExt8to64 x) (SignExt8to64 y))) +(Leq16 x y) => (XOR (MOVVconst [1]) (SGT (SignExt16to64 x) (SignExt16to64 y))) +(Leq32 x y) => (XOR (MOVVconst [1]) (SGT (SignExt32to64 x) (SignExt32to64 y))) +(Leq64 x y) => (XOR (MOVVconst [1]) (SGT x y)) +(Leq(32|64)F x y) => (FPFlagTrue (CMPGE(F|D) y x)) // reverse operands to work around NaN + +(Leq8U x y) => (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y))) +(Leq16U x y) => (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y))) +(Leq32U x y) => (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Leq64U x y) => (XOR (MOVVconst [1]) (SGTU x y)) + +(OffPtr [off] ptr:(SP)) => (MOVVaddr [int32(off)] ptr) +(OffPtr [off] ptr) => (ADDVconst [off] ptr) + +(Addr {sym} base) => (MOVVaddr {sym} base) +(LocalAddr {sym} base _) => (MOVVaddr {sym} base) + +// loads +(Load ptr mem) && t.IsBoolean() => (MOVBUload ptr mem) +(Load ptr mem) && (is8BitInt(t) && isSigned(t)) => (MOVBload ptr mem) +(Load ptr mem) && (is8BitInt(t) && !isSigned(t)) => (MOVBUload ptr mem) +(Load ptr mem) && (is16BitInt(t) && isSigned(t)) => (MOVHload ptr mem) +(Load ptr mem) && (is16BitInt(t) && !isSigned(t)) => (MOVHUload ptr mem) +(Load ptr mem) && (is32BitInt(t) && isSigned(t)) => (MOVWload ptr mem) +(Load ptr mem) && (is32BitInt(t) && !isSigned(t)) => (MOVWUload ptr mem) +(Load ptr mem) && (is64BitInt(t) || isPtr(t)) => (MOVVload ptr mem) +(Load ptr mem) && is32BitFloat(t) => (MOVFload ptr mem) +(Load ptr mem) && is64BitFloat(t) => (MOVDload ptr mem) + +// stores +(Store {t} ptr val mem) && t.Size() == 1 => (MOVBstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 2 => (MOVHstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 4 && !is32BitFloat(val.Type) => (MOVWstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 8 && !is64BitFloat(val.Type) => (MOVVstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 4 && is32BitFloat(val.Type) => (MOVFstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 8 && is64BitFloat(val.Type) => (MOVDstore ptr val mem) + +// zeroing +(Zero [0] _ mem) => mem +(Zero [1] ptr mem) => (MOVBstore ptr (MOVVconst [0]) mem) +(Zero [2] {t} ptr mem) && t.Alignment()%2 == 0 => + (MOVHstore ptr (MOVVconst [0]) mem) +(Zero [2] ptr mem) => + (MOVBstore [1] ptr (MOVVconst [0]) + (MOVBstore [0] ptr (MOVVconst [0]) mem)) +(Zero [4] {t} ptr mem) && t.Alignment()%4 == 0 => + (MOVWstore ptr (MOVVconst [0]) mem) +(Zero [4] {t} ptr mem) && t.Alignment()%2 == 0 => + (MOVHstore [2] ptr (MOVVconst [0]) + (MOVHstore [0] ptr (MOVVconst [0]) mem)) +(Zero [4] ptr mem) => + (MOVBstore [3] ptr (MOVVconst [0]) + (MOVBstore [2] ptr (MOVVconst [0]) + (MOVBstore [1] ptr (MOVVconst [0]) + (MOVBstore [0] ptr (MOVVconst [0]) mem)))) +(Zero [8] {t} ptr mem) && t.Alignment()%8 == 0 => + (MOVVstore ptr (MOVVconst [0]) mem) +(Zero [8] {t} ptr mem) && t.Alignment()%4 == 0 => + (MOVWstore [4] ptr (MOVVconst [0]) + (MOVWstore [0] ptr (MOVVconst [0]) mem)) +(Zero [8] {t} ptr mem) && t.Alignment()%2 == 0 => + (MOVHstore [6] ptr (MOVVconst [0]) + (MOVHstore [4] ptr (MOVVconst [0]) + (MOVHstore [2] ptr (MOVVconst [0]) + (MOVHstore [0] ptr (MOVVconst [0]) mem)))) + +(Zero [3] ptr mem) => + (MOVBstore [2] ptr (MOVVconst [0]) + (MOVBstore [1] ptr (MOVVconst [0]) + (MOVBstore [0] ptr (MOVVconst [0]) mem))) +(Zero [6] {t} ptr mem) && t.Alignment()%2 == 0 => + (MOVHstore [4] ptr (MOVVconst [0]) + (MOVHstore [2] ptr (MOVVconst [0]) + (MOVHstore [0] ptr (MOVVconst [0]) mem))) +(Zero [12] {t} ptr mem) && t.Alignment()%4 == 0 => + (MOVWstore [8] ptr (MOVVconst [0]) + (MOVWstore [4] ptr (MOVVconst [0]) + (MOVWstore [0] ptr (MOVVconst [0]) mem))) +(Zero [16] {t} ptr mem) && t.Alignment()%8 == 0 => + (MOVVstore [8] ptr (MOVVconst [0]) + (MOVVstore [0] ptr (MOVVconst [0]) mem)) +(Zero [24] {t} ptr mem) && t.Alignment()%8 == 0 => + (MOVVstore [16] ptr (MOVVconst [0]) + (MOVVstore [8] ptr (MOVVconst [0]) + (MOVVstore [0] ptr (MOVVconst [0]) mem))) + +// medium zeroing uses a duff device +// 8, and 128 are magic constants, see runtime/mkduff.go +(Zero [s] {t} ptr mem) + && s%8 == 0 && s > 24 && s <= 8*128 + && t.Alignment()%8 == 0 && !config.noDuffDevice => + (DUFFZERO [8 * (128 - s/8)] ptr mem) + +// large or unaligned zeroing uses a loop +(Zero [s] {t} ptr mem) + && (s > 8*128 || config.noDuffDevice) || t.Alignment()%8 != 0 => + (LoweredZero [t.Alignment()] + ptr + (ADDVconst ptr [s-moveSize(t.Alignment(), config)]) + mem) + +// moves +(Move [0] _ _ mem) => mem +(Move [1] dst src mem) => (MOVBstore dst (MOVBload src mem) mem) +(Move [2] {t} dst src mem) && t.Alignment()%2 == 0 => + (MOVHstore dst (MOVHload src mem) mem) +(Move [2] dst src mem) => + (MOVBstore [1] dst (MOVBload [1] src mem) + (MOVBstore dst (MOVBload src mem) mem)) +(Move [4] {t} dst src mem) && t.Alignment()%4 == 0 => + (MOVWstore dst (MOVWload src mem) mem) +(Move [4] {t} dst src mem) && t.Alignment()%2 == 0 => + (MOVHstore [2] dst (MOVHload [2] src mem) + (MOVHstore dst (MOVHload src mem) mem)) +(Move [4] dst src mem) => + (MOVBstore [3] dst (MOVBload [3] src mem) + (MOVBstore [2] dst (MOVBload [2] src mem) + (MOVBstore [1] dst (MOVBload [1] src mem) + (MOVBstore dst (MOVBload src mem) mem)))) +(Move [8] {t} dst src mem) && t.Alignment()%8 == 0 => + (MOVVstore dst (MOVVload src mem) mem) +(Move [8] {t} dst src mem) && t.Alignment()%4 == 0 => + (MOVWstore [4] dst (MOVWload [4] src mem) + (MOVWstore dst (MOVWload src mem) mem)) +(Move [8] {t} dst src mem) && t.Alignment()%2 == 0 => + (MOVHstore [6] dst (MOVHload [6] src mem) + (MOVHstore [4] dst (MOVHload [4] src mem) + (MOVHstore [2] dst (MOVHload [2] src mem) + (MOVHstore dst (MOVHload src mem) mem)))) + +(Move [3] dst src mem) => + (MOVBstore [2] dst (MOVBload [2] src mem) + (MOVBstore [1] dst (MOVBload [1] src mem) + (MOVBstore dst (MOVBload src mem) mem))) +(Move [6] {t} dst src mem) && t.Alignment()%2 == 0 => + (MOVHstore [4] dst (MOVHload [4] src mem) + (MOVHstore [2] dst (MOVHload [2] src mem) + (MOVHstore dst (MOVHload src mem) mem))) +(Move [12] {t} dst src mem) && t.Alignment()%4 == 0 => + (MOVWstore [8] dst (MOVWload [8] src mem) + (MOVWstore [4] dst (MOVWload [4] src mem) + (MOVWstore dst (MOVWload src mem) mem))) +(Move [16] {t} dst src mem) && t.Alignment()%8 == 0 => + (MOVVstore [8] dst (MOVVload [8] src mem) + (MOVVstore dst (MOVVload src mem) mem)) +(Move [24] {t} dst src mem) && t.Alignment()%8 == 0 => + (MOVVstore [16] dst (MOVVload [16] src mem) + (MOVVstore [8] dst (MOVVload [8] src mem) + (MOVVstore dst (MOVVload src mem) mem))) + +// medium move uses a duff device +(Move [s] {t} dst src mem) + && s%8 == 0 && s >= 24 && s <= 8*128 && t.Alignment()%8 == 0 + && !config.noDuffDevice && logLargeCopy(v, s) => + (DUFFCOPY [16 * (128 - s/8)] dst src mem) +// 16 and 128 are magic constants. 16 is the number of bytes to encode: +// MOVV (R1), R23 +// ADDV $8, R1 +// MOVV R23, (R2) +// ADDV $8, R2 +// and 128 is the number of such blocks. See runtime/duff_mips64.s:duffcopy. + +// large or unaligned move uses a loop +(Move [s] {t} dst src mem) + && s > 24 && logLargeCopy(v, s) || t.Alignment()%8 != 0 => + (LoweredMove [t.Alignment()] + dst + src + (ADDVconst src [s-moveSize(t.Alignment(), config)]) + mem) + +// calls +(StaticCall ...) => (CALLstatic ...) +(ClosureCall ...) => (CALLclosure ...) +(InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) + +// atomic intrinsics +(AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...) +(AtomicLoadPtr ...) => (LoweredAtomicLoad64 ...) + +(AtomicStore(8|32|64) ...) => (LoweredAtomicStore(8|32|64) ...) +(AtomicStorePtrNoWB ...) => (LoweredAtomicStore64 ...) + +(AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) + +(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...) + +(AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...) + +// checks +(NilCheck ...) => (LoweredNilCheck ...) +(IsNonNil ptr) => (SGTU ptr (MOVVconst [0])) +(IsInBounds idx len) => (SGTU len idx) +(IsSliceInBounds idx len) => (XOR (MOVVconst [1]) (SGTU idx len)) + +// pseudo-ops +(GetClosurePtr ...) => (LoweredGetClosurePtr ...) +(GetCallerSP ...) => (LoweredGetCallerSP ...) +(GetCallerPC ...) => (LoweredGetCallerPC ...) + +(If cond yes no) => (NE cond yes no) + +// Write barrier. +(WB ...) => (LoweredWB ...) + +(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem) +(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem) +(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem) + +// Optimizations + +// Absorb boolean tests into block +(NE (FPFlagTrue cmp) yes no) => (FPT cmp yes no) +(NE (FPFlagFalse cmp) yes no) => (FPF cmp yes no) +(EQ (FPFlagTrue cmp) yes no) => (FPF cmp yes no) +(EQ (FPFlagFalse cmp) yes no) => (FPT cmp yes no) +(NE (XORconst [1] cmp:(SGT _ _)) yes no) => (EQ cmp yes no) +(NE (XORconst [1] cmp:(SGTU _ _)) yes no) => (EQ cmp yes no) +(NE (XORconst [1] cmp:(SGTconst _)) yes no) => (EQ cmp yes no) +(NE (XORconst [1] cmp:(SGTUconst _)) yes no) => (EQ cmp yes no) +(EQ (XORconst [1] cmp:(SGT _ _)) yes no) => (NE cmp yes no) +(EQ (XORconst [1] cmp:(SGTU _ _)) yes no) => (NE cmp yes no) +(EQ (XORconst [1] cmp:(SGTconst _)) yes no) => (NE cmp yes no) +(EQ (XORconst [1] cmp:(SGTUconst _)) yes no) => (NE cmp yes no) +(NE (SGTUconst [1] x) yes no) => (EQ x yes no) +(EQ (SGTUconst [1] x) yes no) => (NE x yes no) +(NE (SGTU x (MOVVconst [0])) yes no) => (NE x yes no) +(EQ (SGTU x (MOVVconst [0])) yes no) => (EQ x yes no) +(NE (SGTconst [0] x) yes no) => (LTZ x yes no) +(EQ (SGTconst [0] x) yes no) => (GEZ x yes no) +(NE (SGT x (MOVVconst [0])) yes no) => (GTZ x yes no) +(EQ (SGT x (MOVVconst [0])) yes no) => (LEZ x yes no) + +// fold offset into address +(ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) && is32Bit(off1+int64(off2)) => (MOVVaddr [int32(off1)+int32(off2)] {sym} ptr) + +// fold address into load/store +(MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBload [off1+int32(off2)] {sym} ptr mem) +(MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBUload [off1+int32(off2)] {sym} ptr mem) +(MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHload [off1+int32(off2)] {sym} ptr mem) +(MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHUload [off1+int32(off2)] {sym} ptr mem) +(MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWload [off1+int32(off2)] {sym} ptr mem) +(MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWUload [off1+int32(off2)] {sym} ptr mem) +(MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVload [off1+int32(off2)] {sym} ptr mem) +(MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVFload [off1+int32(off2)] {sym} ptr mem) +(MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVDload [off1+int32(off2)] {sym} ptr mem) + +(MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVBstore [off1+int32(off2)] {sym} ptr val mem) +(MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVHstore [off1+int32(off2)] {sym} ptr val mem) +(MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVWstore [off1+int32(off2)] {sym} ptr val mem) +(MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVVstore [off1+int32(off2)] {sym} ptr val mem) +(MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVFstore [off1+int32(off2)] {sym} ptr val mem) +(MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVDstore [off1+int32(off2)] {sym} ptr val mem) +(MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) +(MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) +(MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) +(MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVstorezero [off1+int32(off2)] {sym} ptr mem) + +(MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + +(MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) +(MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => + (MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + +(LoweredAtomicStore(32|64) ptr (MOVVconst [0]) mem) => (LoweredAtomicStorezero(32|64) ptr mem) +(LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst32 [int32(c)] ptr mem) +(LoweredAtomicAdd64 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst64 [c] ptr mem) + +// don't extend after proper load +(MOVBreg x:(MOVBload _ _)) => (MOVVreg x) +(MOVBUreg x:(MOVBUload _ _)) => (MOVVreg x) +(MOVHreg x:(MOVBload _ _)) => (MOVVreg x) +(MOVHreg x:(MOVBUload _ _)) => (MOVVreg x) +(MOVHreg x:(MOVHload _ _)) => (MOVVreg x) +(MOVHUreg x:(MOVBUload _ _)) => (MOVVreg x) +(MOVHUreg x:(MOVHUload _ _)) => (MOVVreg x) +(MOVWreg x:(MOVBload _ _)) => (MOVVreg x) +(MOVWreg x:(MOVBUload _ _)) => (MOVVreg x) +(MOVWreg x:(MOVHload _ _)) => (MOVVreg x) +(MOVWreg x:(MOVHUload _ _)) => (MOVVreg x) +(MOVWreg x:(MOVWload _ _)) => (MOVVreg x) +(MOVWUreg x:(MOVBUload _ _)) => (MOVVreg x) +(MOVWUreg x:(MOVHUload _ _)) => (MOVVreg x) +(MOVWUreg x:(MOVWUload _ _)) => (MOVVreg x) + +// fold double extensions +(MOVBreg x:(MOVBreg _)) => (MOVVreg x) +(MOVBUreg x:(MOVBUreg _)) => (MOVVreg x) +(MOVHreg x:(MOVBreg _)) => (MOVVreg x) +(MOVHreg x:(MOVBUreg _)) => (MOVVreg x) +(MOVHreg x:(MOVHreg _)) => (MOVVreg x) +(MOVHUreg x:(MOVBUreg _)) => (MOVVreg x) +(MOVHUreg x:(MOVHUreg _)) => (MOVVreg x) +(MOVWreg x:(MOVBreg _)) => (MOVVreg x) +(MOVWreg x:(MOVBUreg _)) => (MOVVreg x) +(MOVWreg x:(MOVHreg _)) => (MOVVreg x) +(MOVWreg x:(MOVWreg _)) => (MOVVreg x) +(MOVWUreg x:(MOVBUreg _)) => (MOVVreg x) +(MOVWUreg x:(MOVHUreg _)) => (MOVVreg x) +(MOVWUreg x:(MOVWUreg _)) => (MOVVreg x) + +// don't extend before store +(MOVBstore [off] {sym} ptr (MOVBreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVBstore [off] {sym} ptr (MOVHreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVBstore [off] {sym} ptr (MOVHUreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVBstore [off] {sym} ptr (MOVWreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVBstore [off] {sym} ptr (MOVWUreg x) mem) => (MOVBstore [off] {sym} ptr x mem) +(MOVHstore [off] {sym} ptr (MOVHreg x) mem) => (MOVHstore [off] {sym} ptr x mem) +(MOVHstore [off] {sym} ptr (MOVHUreg x) mem) => (MOVHstore [off] {sym} ptr x mem) +(MOVHstore [off] {sym} ptr (MOVWreg x) mem) => (MOVHstore [off] {sym} ptr x mem) +(MOVHstore [off] {sym} ptr (MOVWUreg x) mem) => (MOVHstore [off] {sym} ptr x mem) +(MOVWstore [off] {sym} ptr (MOVWreg x) mem) => (MOVWstore [off] {sym} ptr x mem) +(MOVWstore [off] {sym} ptr (MOVWUreg x) mem) => (MOVWstore [off] {sym} ptr x mem) + +// if a register move has only 1 use, just use the same register without emitting instruction +// MOVVnop doesn't emit instruction, only for ensuring the type. +(MOVVreg x) && x.Uses == 1 => (MOVVnop x) + +// fold constant into arithmetic ops +(ADDV x (MOVVconst [c])) && is32Bit(c) => (ADDVconst [c] x) +(SUBV x (MOVVconst [c])) && is32Bit(c) => (SUBVconst [c] x) +(AND x (MOVVconst [c])) && is32Bit(c) => (ANDconst [c] x) +(OR x (MOVVconst [c])) && is32Bit(c) => (ORconst [c] x) +(XOR x (MOVVconst [c])) && is32Bit(c) => (XORconst [c] x) +(NOR x (MOVVconst [c])) && is32Bit(c) => (NORconst [c] x) + +(SLLV _ (MOVVconst [c])) && uint64(c)>=64 => (MOVVconst [0]) +(SRLV _ (MOVVconst [c])) && uint64(c)>=64 => (MOVVconst [0]) +(SRAV x (MOVVconst [c])) && uint64(c)>=64 => (SRAVconst x [63]) +(SLLV x (MOVVconst [c])) => (SLLVconst x [c]) +(SRLV x (MOVVconst [c])) => (SRLVconst x [c]) +(SRAV x (MOVVconst [c])) => (SRAVconst x [c]) +(ROTR x (MOVVconst [c])) => (ROTRconst x [c&31]) +(ROTRV x (MOVVconst [c])) => (ROTRVconst x [c&63]) + +(SGT (MOVVconst [c]) x) && is32Bit(c) => (SGTconst [c] x) +(SGTU (MOVVconst [c]) x) && is32Bit(c) => (SGTUconst [c] x) + +// mul by constant +(Select1 (MULVU x (MOVVconst [-1]))) => (NEGV x) +(Select1 (MULVU _ (MOVVconst [0]))) => (MOVVconst [0]) +(Select1 (MULVU x (MOVVconst [1]))) => x +(Select1 (MULVU x (MOVVconst [c]))) && isPowerOfTwo64(c) => (SLLVconst [log64(c)] x) + +// div by constant +(Select1 (DIVVU x (MOVVconst [1]))) => x +(Select1 (DIVVU x (MOVVconst [c]))) && isPowerOfTwo64(c) => (SRLVconst [log64(c)] x) +(Select0 (DIVVU _ (MOVVconst [1]))) => (MOVVconst [0]) // mod +(Select0 (DIVVU x (MOVVconst [c]))) && isPowerOfTwo64(c) => (ANDconst [c-1] x) // mod + +// generic simplifications +(ADDV x (NEGV y)) => (SUBV x y) +(SUBV x x) => (MOVVconst [0]) +(SUBV (MOVVconst [0]) x) => (NEGV x) +(AND x x) => x +(OR x x) => x +(XOR x x) => (MOVVconst [0]) + +// remove redundant *const ops +(ADDVconst [0] x) => x +(SUBVconst [0] x) => x +(ANDconst [0] _) => (MOVVconst [0]) +(ANDconst [-1] x) => x +(ORconst [0] x) => x +(ORconst [-1] _) => (MOVVconst [-1]) +(XORconst [0] x) => x +(XORconst [-1] x) => (NORconst [0] x) + +// generic constant folding +(ADDVconst [c] (MOVVconst [d])) => (MOVVconst [c+d]) +(ADDVconst [c] (ADDVconst [d] x)) && is32Bit(c+d) => (ADDVconst [c+d] x) +(ADDVconst [c] (SUBVconst [d] x)) && is32Bit(c-d) => (ADDVconst [c-d] x) +(SUBVconst [c] (MOVVconst [d])) => (MOVVconst [d-c]) +(SUBVconst [c] (SUBVconst [d] x)) && is32Bit(-c-d) => (ADDVconst [-c-d] x) +(SUBVconst [c] (ADDVconst [d] x)) && is32Bit(-c+d) => (ADDVconst [-c+d] x) +(SLLVconst [c] (MOVVconst [d])) => (MOVVconst [d< (MOVVconst [int64(uint64(d)>>uint64(c))]) +(SRAVconst [c] (MOVVconst [d])) => (MOVVconst [d>>uint64(c)]) +(Select1 (MULVU (MOVVconst [c]) (MOVVconst [d]))) => (MOVVconst [c*d]) +(Select1 (DIVV (MOVVconst [c]) (MOVVconst [d]))) && d != 0 => (MOVVconst [c/d]) +(Select1 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) && d != 0 => (MOVVconst [int64(uint64(c)/uint64(d))]) +(Select0 (DIVV (MOVVconst [c]) (MOVVconst [d]))) && d != 0 => (MOVVconst [c%d]) // mod +(Select0 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) && d != 0 => (MOVVconst [int64(uint64(c)%uint64(d))]) // mod +(ANDconst [c] (MOVVconst [d])) => (MOVVconst [c&d]) +(ANDconst [c] (ANDconst [d] x)) => (ANDconst [c&d] x) +(ORconst [c] (MOVVconst [d])) => (MOVVconst [c|d]) +(ORconst [c] (ORconst [d] x)) && is32Bit(c|d) => (ORconst [c|d] x) +(XORconst [c] (MOVVconst [d])) => (MOVVconst [c^d]) +(XORconst [c] (XORconst [d] x)) && is32Bit(c^d) => (XORconst [c^d] x) +(NORconst [c] (MOVVconst [d])) => (MOVVconst [^(c|d)]) +(NEGV (MOVVconst [c])) => (MOVVconst [-c]) +(MOVBreg (MOVVconst [c])) => (MOVVconst [int64(int8(c))]) +(MOVBUreg (MOVVconst [c])) => (MOVVconst [int64(uint8(c))]) +(MOVHreg (MOVVconst [c])) => (MOVVconst [int64(int16(c))]) +(MOVHUreg (MOVVconst [c])) => (MOVVconst [int64(uint16(c))]) +(MOVWreg (MOVVconst [c])) => (MOVVconst [int64(int32(c))]) +(MOVWUreg (MOVVconst [c])) => (MOVVconst [int64(uint32(c))]) +(MOVVreg (MOVVconst [c])) => (MOVVconst [c]) + +// constant comparisons +(SGTconst [c] (MOVVconst [d])) && c>d => (MOVVconst [1]) +(SGTconst [c] (MOVVconst [d])) && c<=d => (MOVVconst [0]) +(SGTUconst [c] (MOVVconst [d])) && uint64(c)>uint64(d) => (MOVVconst [1]) +(SGTUconst [c] (MOVVconst [d])) && uint64(c)<=uint64(d) => (MOVVconst [0]) + +// other known comparisons +(SGTconst [c] (MOVBreg _)) && 0x7f < c => (MOVVconst [1]) +(SGTconst [c] (MOVBreg _)) && c <= -0x80 => (MOVVconst [0]) +(SGTconst [c] (MOVBUreg _)) && 0xff < c => (MOVVconst [1]) +(SGTconst [c] (MOVBUreg _)) && c < 0 => (MOVVconst [0]) +(SGTUconst [c] (MOVBUreg _)) && 0xff < uint64(c) => (MOVVconst [1]) +(SGTconst [c] (MOVHreg _)) && 0x7fff < c => (MOVVconst [1]) +(SGTconst [c] (MOVHreg _)) && c <= -0x8000 => (MOVVconst [0]) +(SGTconst [c] (MOVHUreg _)) && 0xffff < c => (MOVVconst [1]) +(SGTconst [c] (MOVHUreg _)) && c < 0 => (MOVVconst [0]) +(SGTUconst [c] (MOVHUreg _)) && 0xffff < uint64(c) => (MOVVconst [1]) +(SGTconst [c] (MOVWUreg _)) && c < 0 => (MOVVconst [0]) +(SGTconst [c] (ANDconst [m] _)) && 0 <= m && m < c => (MOVVconst [1]) +(SGTUconst [c] (ANDconst [m] _)) && uint64(m) < uint64(c) => (MOVVconst [1]) +(SGTconst [c] (SRLVconst _ [d])) && 0 <= c && 0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c) => (MOVVconst [1]) +(SGTUconst [c] (SRLVconst _ [d])) && 0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c) => (MOVVconst [1]) + +// absorb constants into branches +(EQ (MOVVconst [0]) yes no) => (First yes no) +(EQ (MOVVconst [c]) yes no) && c != 0 => (First no yes) +(NE (MOVVconst [0]) yes no) => (First no yes) +(NE (MOVVconst [c]) yes no) && c != 0 => (First yes no) +(LTZ (MOVVconst [c]) yes no) && c < 0 => (First yes no) +(LTZ (MOVVconst [c]) yes no) && c >= 0 => (First no yes) +(LEZ (MOVVconst [c]) yes no) && c <= 0 => (First yes no) +(LEZ (MOVVconst [c]) yes no) && c > 0 => (First no yes) +(GTZ (MOVVconst [c]) yes no) && c > 0 => (First yes no) +(GTZ (MOVVconst [c]) yes no) && c <= 0 => (First no yes) +(GEZ (MOVVconst [c]) yes no) && c >= 0 => (First yes no) +(GEZ (MOVVconst [c]) yes no) && c < 0 => (First no yes) diff --git a/src/cmd/compile/internal/ssa/gen/LOONG64Ops.go b/src/cmd/compile/internal/ssa/gen/LOONG64Ops.go new file mode 100644 index 00000000000000..a59b592445ac0c --- /dev/null +++ b/src/cmd/compile/internal/ssa/gen/LOONG64Ops.go @@ -0,0 +1,484 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +package main + +import "strings" + +// Notes: +// - Integer types live in the low portion of registers. Upper portions are junk. +// - Boolean types use the low-order byte of a register. 0=false, 1=true. +// Upper bytes are junk. +// - *const instructions may use a constant larger than the instruction can encode. +// In this case the assembler expands to multiple instructions and uses tmp +// register (R23). + +// Suffixes encode the bit width of various instructions. +// V (vlong) = 64 bit +// WU (word) = 32 bit unsigned +// W (word) = 32 bit +// H (half word) = 16 bit +// HU = 16 bit unsigned +// B (byte) = 8 bit +// BU = 8 bit unsigned +// F (float) = 32 bit float +// D (double) = 64 bit float + +// Note: registers not used in regalloc are not included in this list, +// so that regmask stays within int64 +// Be careful when hand coding regmasks. +var regNamesLOONG64 = []string{ + "R0", // constant 0 + "R1", + "SP", // aka R3 + "R4", + "R5", + "R6", + "R7", + "R8", + "R9", + "R10", + "R11", + "R12", + "R13", + "R14", + "R15", + "R16", + "R17", + "R18", + "R19", + "R20", + "R21", + "g", // aka R22 + "R23", + "R24", + "R25", + "R26", + "R27", + "R28", + "R29", + // R30 is REGTMP not used in regalloc + "R31", + + "F0", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "F24", + "F25", + "F26", + "F27", + "F28", + "F29", + "F30", + "F31", + + // If you add registers, update asyncPreempt in runtime. + + // pseudo-registers + "SB", +} + +func init() { + // Make map from reg names to reg integers. + if len(regNamesLOONG64) > 64 { + panic("too many registers") + } + num := map[string]int{} + for i, name := range regNamesLOONG64 { + num[name] = i + } + buildReg := func(s string) regMask { + m := regMask(0) + for _, r := range strings.Split(s, " ") { + if n, ok := num[r]; ok { + m |= regMask(1) << uint(n) + continue + } + panic("register " + r + " not found") + } + return m + } + + // Common individual register masks + var ( + gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R21-unused, R22 is g, R30 is REGTMP + gps = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") | buildReg("g") + gpg = gp | buildReg("g") + gpsp = gp | buildReg("SP") + gpspg = gpg | buildReg("SP") + gpspsbg = gpspg | buildReg("SB") + fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31") + callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g + r1 = buildReg("R19") + r2 = buildReg("R18") + r3 = buildReg("R17") + r4 = buildReg("R4") + ) + // Common regInfo + var ( + gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} + gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} + gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} + gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} + gpmuldiv = regInfo{inputs: []regMask{gps, gps}, outputs: []regMask{buildReg("R17"), buildReg("R18")}} + gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}} + gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}} + gpstore0 = regInfo{inputs: []regMask{gpspsbg}} + gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} + gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}} + fp01 = regInfo{inputs: nil, outputs: []regMask{fp}} + fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}} + fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}} + fp2flags = regInfo{inputs: []regMask{fp, fp}} + fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} + fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} + readflags = regInfo{inputs: nil, outputs: []regMask{gp}} + ) + ops := []opData{ + // binary ops + {name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true}, // arg0 + arg1 + {name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops. + {name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"}, // arg0 - arg1 + {name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"}, // arg0 - auxInt + + {name: "MULV", argLength: 2, reg: gpmuldiv, commutative: true, typ: "(Int64,Int64)"}, // arg0 * arg1, signed + {name: "MULVU", argLength: 2, reg: gpmuldiv, commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned + {name: "DIVV", argLength: 2, reg: gpmuldiv, typ: "(Int64,Int64)"}, // arg0 / arg1, signed + {name: "DIVVU", argLength: 2, reg: gpmuldiv, typ: "(UInt64,UInt64)"}, // arg0 / arg1, unsigned + + {name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1 + {name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1 + {name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"}, // arg0 - arg1 + {name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"}, // arg0 - arg1 + {name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1 + {name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1 + {name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"}, // arg0 / arg1 + {name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"}, // arg0 / arg1 + + {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1 + {name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt + {name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true}, // arg0 | arg1 + {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0 | auxInt + {name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1 + {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt + {name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true}, // ^(arg0 | arg1) + {name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"}, // ^(arg0 | auxInt) + + {name: "NEGV", argLength: 1, reg: gp11}, // -arg0 + {name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32 + {name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64 + {name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64 + {name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32 + + // shifts + {name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"}, // arg0 << arg1, shift amount is mod 64 + {name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt + {name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"}, // arg0 >> arg1, unsigned, shift amount is mod 64 + {name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned + {name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"}, // arg0 >> arg1, signed, shift amount is mod 64 + {name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed + {name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"}, // arg0 right rotate by (arg1 mod 32) bits + {name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"}, // arg0 right rotate by (arg1 mod 64) bits + {name: "ROTRconst", argLength: 1, reg: gp11, asm: "ROTR", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31. + {name: "ROTRVconst", argLength: 1, reg: gp11, asm: "ROTRV", aux: "Int64"}, // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63. + + // comparisons + {name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"}, // 1 if arg0 > arg1 (signed), 0 otherwise + {name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (signed), 0 otherwise + {name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"}, // 1 if arg0 > arg1 (unsigned), 0 otherwise + {name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise + + {name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32 + {name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64 + {name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32 + {name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64 + {name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32 + {name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64 + + // moves + {name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true}, // auxint + {name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float + {name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float + + {name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB + + {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + + {name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of arg1 to arg0 + auxInt + aux. arg2=mem. + {name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + {name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + {name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + {name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + {name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + + {name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux. arg1=mem. + {name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux. arg1=mem. + {name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux. arg1=mem. + {name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux. ar12=mem. + + // conversions + {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"}, // move from arg0, sign-extended from byte + {name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte + {name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"}, // move from arg0, sign-extended from half + {name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half + {name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"}, // move from arg0, sign-extended from word + {name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word + {name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"}, // move from arg0 + + {name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register + + {name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"}, // int32 -> float32 + {name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"}, // int32 -> float64 + {name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"}, // int64 -> float32 + {name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"}, // int64 -> float64 + {name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32 + {name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32 + {name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64 + {name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64 + {name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"}, // float32 -> float64 + {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 + + // function calls + {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + + // duffzero + // arg0 = address of memory to zero + // arg1 = mem + // auxint = offset into duffzero code to start executing + // returns mem + // R19 aka loong64.REGRT1 changed as side effect + { + name: "DUFFZERO", + aux: "Int64", + argLength: 2, + reg: regInfo{ + inputs: []regMask{gp}, + clobbers: buildReg("R19 R1"), + }, + faultOnNilArg0: true, + }, + + // duffcopy + // arg0 = address of dst memory (in R20, changed as side effect) REGRT2 + // arg1 = address of src memory (in R19, changed as side effect) REGRT1 + // arg2 = mem + // auxint = offset into duffcopy code to start executing + // returns mem + { + name: "DUFFCOPY", + aux: "Int64", + argLength: 3, + reg: regInfo{ + inputs: []regMask{buildReg("R20"), buildReg("R19")}, + clobbers: buildReg("R19 R20 R1"), + }, + faultOnNilArg0: true, + faultOnNilArg1: true, + }, + + // large or unaligned zeroing + // arg0 = address of memory to zero (in R19, changed as side effect) + // arg1 = address of the last element to zero + // arg2 = mem + // auxint = alignment + // returns mem + // SUBV $8, R19 + // MOVV R0, 8(R19) + // ADDV $8, R19 + // BNE Rarg1, R19, -2(PC) + { + name: "LoweredZero", + aux: "Int64", + argLength: 3, + reg: regInfo{ + inputs: []regMask{buildReg("R19"), gp}, + clobbers: buildReg("R19"), + }, + clobberFlags: true, + faultOnNilArg0: true, + }, + + // large or unaligned move + // arg0 = address of dst memory (in R4, changed as side effect) + // arg1 = address of src memory (in R19, changed as side effect) + // arg2 = address of the last element of src + // arg3 = mem + // auxint = alignment + // returns mem + // SUBV $8, R19 + // MOVV 8(R19), Rtmp + // MOVV Rtmp, (R4) + // ADDV $8, R19 + // ADDV $8, R4 + // BNE Rarg2, R19, -4(PC) + { + name: "LoweredMove", + aux: "Int64", + argLength: 4, + reg: regInfo{ + inputs: []regMask{buildReg("R4"), buildReg("R19"), gp}, + clobbers: buildReg("R19 R4"), + }, + clobberFlags: true, + faultOnNilArg0: true, + faultOnNilArg1: true, + }, + + // atomic loads. + // load from arg0. arg1=mem. + // returns so they can be properly ordered with other loads. + {name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true}, + {name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true}, + {name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true}, + + // atomic stores. + // store arg1 to arg0. arg2=mem. returns memory. + {name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true}, + // store zero to arg0. arg1=mem. returns memory. + {name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, + {name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true}, + + // atomic exchange. + // store arg1 to arg0. arg2=mem. returns . + // DBAR + // LL (Rarg0), Rout + // MOVV Rarg1, Rtmp + // SC Rtmp, (Rarg0) + // BEQ Rtmp, -3(PC) + // DBAR + {name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + + // atomic add. + // *arg0 += arg1. arg2=mem. returns . + // DBAR + // LL (Rarg0), Rout + // ADDV Rarg1, Rout, Rtmp + // SC Rtmp, (Rarg0) + // BEQ Rtmp, -3(PC) + // DBAR + // ADDV Rarg1, Rout + {name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + // *arg0 += auxint. arg1=mem. returns . auxint is 32-bit. + {name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + + // atomic compare and swap. + // arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. + // if *arg0 == arg1 { + // *arg0 = arg2 + // return (true, memory) + // } else { + // return (false, memory) + // } + // DBAR + // MOVV $0, Rout + // LL (Rarg0), Rtmp + // BNE Rtmp, Rarg1, 4(PC) + // MOVV Rarg2, Rout + // SC Rout, (Rarg0) + // BEQ Rout, -4(PC) + // DBAR + {name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + {name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true}, + + // pseudo-ops + {name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem. + + {name: "FPFlagTrue", argLength: 1, reg: readflags}, // bool, true if FP flag is true + {name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false + + // Scheduler ensures LoweredGetClosurePtr occurs only in entry block, + // and sorts it to the very beginning of the block to prevent other + // use of R22 (loong64.REGCTXT, the closure pointer) + {name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true}, + + // LoweredGetCallerSP returns the SP of the caller of the current function. + {name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true}, + + // LoweredGetCallerPC evaluates to the PC to which its "caller" will return. + // I.e., if f calls g "calls" getcallerpc, + // the result should be the PC within f that g will return to. + // See runtime/stubs.go for a more detailed discussion. + {name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true}, + + // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier + // It saves all GP registers if necessary, + // but clobbers R1 (LR) because it's a call + // and R30 (REGTMP). + {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R27"), buildReg("R28")}, clobbers: (callerSave &^ gpg) | buildReg("R1")}, clobberFlags: true, aux: "Sym", symEffect: "None"}, + + // There are three of these functions so that they can have three different register inputs. + // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the + // default registers to match so we don't need to copy registers around unnecessarily. + {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). + {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). + {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go). + } + + blocks := []blockData{ + {name: "EQ", controls: 1}, + {name: "NE", controls: 1}, + {name: "LTZ", controls: 1}, // < 0 + {name: "LEZ", controls: 1}, // <= 0 + {name: "GTZ", controls: 1}, // > 0 + {name: "GEZ", controls: 1}, // >= 0 + {name: "FPT", controls: 1}, // FP flag is true + {name: "FPF", controls: 1}, // FP flag is false + } + + archs = append(archs, arch{ + name: "LOONG64", + pkg: "cmd/internal/obj/loong64", + genfile: "../../loong64/ssa.go", + ops: ops, + blocks: blocks, + regnames: regNamesLOONG64, + // TODO: support register ABI on loong64 + ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11", + ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7", + gpregmask: gp, + fpregmask: fp, + framepointerreg: -1, // not used + linkreg: int8(num["R1"]), + }) +} diff --git a/src/cmd/compile/internal/ssa/gen/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules index 4ac9668ea9cb56..6f696da3cc8472 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS.rules +++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules @@ -334,6 +334,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // atomic intrinsics (AtomicLoad(8|32) ...) => (LoweredAtomicLoad(8|32) ...) @@ -564,7 +565,7 @@ // But for now, this is enough to get rid of lots of them. (MOVWnop (MOVWconst [c])) => (MOVWconst [c]) -// fold constant into arithmatic ops +// fold constant into arithmetic ops (ADD x (MOVWconst [c])) => (ADDconst [c] x) (SUB x (MOVWconst [c])) => (SUBconst [c] x) (AND x (MOVWconst [c])) => (ANDconst [c] x) diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64.rules b/src/cmd/compile/internal/ssa/gen/MIPS64.rules index fd04a6c3a85c7a..17634afd729d2c 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64.rules +++ b/src/cmd/compile/internal/ssa/gen/MIPS64.rules @@ -379,6 +379,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // atomic intrinsics (AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...) @@ -563,7 +564,7 @@ // But for now, this is enough to get rid of lots of them. (MOVVnop (MOVVconst [c])) => (MOVVconst [c]) -// fold constant into arithmatic ops +// fold constant into arithmetic ops (ADDV x (MOVVconst [c])) && is32Bit(c) => (ADDVconst [c] x) (SUBV x (MOVVconst [c])) && is32Bit(c) => (SUBVconst [c] x) (AND x (MOVVconst [c])) && is32Bit(c) => (ANDconst [c] x) diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index 77f251c0d3f920..7b18c42ffb91e3 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -275,6 +276,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index b92e8cb9f1ee64..523847badc9ce2 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -257,6 +258,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index ce4b324b5e1022..4eae2fc1af13e6 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -30,7 +30,6 @@ // (x + y) / 2 with x>=y => (x - y) / 2 + y (Avg64u x y) => (ADD (SRDconst (SUB x y) [1]) y) -(Add64carry ...) => (LoweredAdd64Carry ...) (Mul64 ...) => (MULLD ...) (Mul(32|16|8) ...) => (MULLW ...) (Mul64uhilo ...) => (LoweredMuluhilo ...) @@ -46,22 +45,16 @@ (Hmul(64|64u|32|32u) ...) => (MULH(D|DU|W|WU) ...) -(Mul32F ...) => (FMULS ...) -(Mul64F ...) => (FMUL ...) +(Mul(32|64)F ...) => ((FMULS|FMUL) ...) -(Div32F ...) => (FDIVS ...) -(Div64F ...) => (FDIV ...) +(Div(32|64)F ...) => ((FDIVS|FDIV) ...) // Lowering float <=> int -(Cvt32to32F x) => (FCFIDS (MTVSRD (SignExt32to64 x))) -(Cvt32to64F x) => (FCFID (MTVSRD (SignExt32to64 x))) -(Cvt64to32F x) => (FCFIDS (MTVSRD x)) -(Cvt64to64F x) => (FCFID (MTVSRD x)) +(Cvt32to(32|64)F x) => ((FCFIDS|FCFID) (MTVSRD (SignExt32to64 x))) +(Cvt64to(32|64)F x) => ((FCFIDS|FCFID) (MTVSRD x)) -(Cvt32Fto32 x) => (MFVSRD (FCTIWZ x)) -(Cvt32Fto64 x) => (MFVSRD (FCTIDZ x)) -(Cvt64Fto32 x) => (MFVSRD (FCTIWZ x)) -(Cvt64Fto64 x) => (MFVSRD (FCTIDZ x)) +(Cvt32Fto(32|64) x) => (MFVSRD (FCTI(W|D)Z x)) +(Cvt64Fto(32|64) x) => (MFVSRD (FCTI(W|D)Z x)) (Cvt32Fto64F ...) => (Copy ...) // Note v will have the wrong type for patterns dependent on Float32/Float64 (Cvt64Fto32F ...) => (FRSP ...) @@ -103,6 +96,22 @@ (ConstNil) => (MOVDconst [0]) (ConstBool [t]) => (MOVDconst [b2i(t)]) +// Carrying addition. +(Select0 (Add64carry x y c)) => (Select0 (ADDE x y (Select1 (ADDCconst c [-1])))) +(Select1 (Add64carry x y c)) => (ADDZEzero (Select1 (ADDE x y (Select1 (ADDCconst c [-1]))))) +// Fold initial carry bit if 0. +(ADDE x y (Select1 (ADDCconst (MOVDconst [0]) [-1]))) => (ADDC x y) +// Fold transfer of CA -> GPR -> CA. Note 2 uses when feeding into a chained Add64carry. +(Select1 (ADDCconst n:(ADDZEzero x) [-1])) && n.Uses <= 2 => x + +// Borrowing subtraction. +(Select0 (Sub64borrow x y c)) => (Select0 (SUBE x y (Select1 (SUBCconst c [0])))) +(Select1 (Sub64borrow x y c)) => (NEG (SUBZEzero (Select1 (SUBE x y (Select1 (SUBCconst c [0])))))) +// Fold initial borrow bit if 0. +(SUBE x y (Select1 (SUBCconst (MOVDconst [0]) [0]))) => (SUBC x y) +// Fold transfer of CA -> GPR -> CA. Note 2 uses when feeding into a chained Sub64borrow. +(Select1 (SUBCconst n:(NEG (SUBZEzero x)) [0])) && n.Uses <= 2 => x + // Constant folding (FABS (FMOVDconst [x])) => (FMOVDconst [math.Abs(x)]) (FSQRT (FMOVDconst [x])) && x >= 0 => (FMOVDconst [math.Sqrt(x)]) @@ -113,61 +122,29 @@ // Rotates (RotateLeft8 x (MOVDconst [c])) => (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7]))) (RotateLeft16 x (MOVDconst [c])) => (Or16 (Lsh16x64 x (MOVDconst [c&15])) (Rsh16Ux64 x (MOVDconst [-c&15]))) -(RotateLeft32 x (MOVDconst [c])) => (ROTLWconst [c&31] x) -(RotateLeft64 x (MOVDconst [c])) => (ROTLconst [c&63] x) - -// Rotate generation with const shift -(ADD (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (ROTLconst [c] x) -( OR (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (ROTLconst [c] x) -(XOR (SLDconst x [c]) (SRDconst x [d])) && d == 64-c => (ROTLconst [c] x) - -(ADD (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (ROTLWconst [c] x) -( OR (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (ROTLWconst [c] x) -(XOR (SLWconst x [c]) (SRWconst x [d])) && d == 32-c => (ROTLWconst [c] x) - -// Rotate generation with non-const shift -// these match patterns from math/bits/RotateLeft[32|64], but there could be others -(ADD (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) => (ROTL x y) -(ADD (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) => (ROTL x y) -( OR (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) => (ROTL x y) -( OR (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) => (ROTL x y) -(XOR (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) => (ROTL x y) -(XOR (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) => (ROTL x y) - - -(ADD (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) => (ROTLW x y) -(ADD (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) => (ROTLW x y) -( OR (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) => (ROTLW x y) -( OR (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) => (ROTLW x y) -(XOR (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) => (ROTLW x y) -(XOR (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) => (ROTLW x y) - - -// Lowering rotates -(RotateLeft32 x y) => (ROTLW x y) -(RotateLeft64 x y) => (ROTL x y) +(RotateLeft(32|64) ...) => ((ROTLW|ROTL) ...) // Constant rotate generation (ROTLW x (MOVDconst [c])) => (ROTLWconst x [c&31]) (ROTL x (MOVDconst [c])) => (ROTLconst x [c&63]) // Combine rotate and mask operations -(ANDconst [m] (ROTLWconst [r] x)) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,m,32)] x) +(Select0 (ANDCCconst [m] (ROTLWconst [r] x))) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,m,32)] x) (AND (MOVDconst [m]) (ROTLWconst [r] x)) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,m,32)] x) -(ANDconst [m] (ROTLW x r)) && isPPC64WordRotateMask(m) => (RLWNM [encodePPC64RotateMask(0,m,32)] x r) +(Select0 (ANDCCconst [m] (ROTLW x r))) && isPPC64WordRotateMask(m) => (RLWNM [encodePPC64RotateMask(0,m,32)] x r) (AND (MOVDconst [m]) (ROTLW x r)) && isPPC64WordRotateMask(m) => (RLWNM [encodePPC64RotateMask(0,m,32)] x r) // Note, any rotated word bitmask is still a valid word bitmask. (ROTLWconst [r] (AND (MOVDconst [m]) x)) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,rotateLeft32(m,r),32)] x) -(ROTLWconst [r] (ANDconst [m] x)) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,rotateLeft32(m,r),32)] x) +(ROTLWconst [r] (Select0 (ANDCCconst [m] x))) && isPPC64WordRotateMask(m) => (RLWINM [encodePPC64RotateMask(r,rotateLeft32(m,r),32)] x) -(ANDconst [m] (SRWconst x [s])) && mergePPC64RShiftMask(m,s,32) == 0 => (MOVDconst [0]) -(ANDconst [m] (SRWconst x [s])) && mergePPC64AndSrwi(m,s) != 0 => (RLWINM [mergePPC64AndSrwi(m,s)] x) +(Select0 (ANDCCconst [m] (SRWconst x [s]))) && mergePPC64RShiftMask(m,s,32) == 0 => (MOVDconst [0]) +(Select0 (ANDCCconst [m] (SRWconst x [s]))) && mergePPC64AndSrwi(m,s) != 0 => (RLWINM [mergePPC64AndSrwi(m,s)] x) (AND (MOVDconst [m]) (SRWconst x [s])) && mergePPC64RShiftMask(m,s,32) == 0 => (MOVDconst [0]) (AND (MOVDconst [m]) (SRWconst x [s])) && mergePPC64AndSrwi(m,s) != 0 => (RLWINM [mergePPC64AndSrwi(m,s)] x) -(SRWconst (ANDconst [m] x) [s]) && mergePPC64RShiftMask(m>>uint(s),s,32) == 0 => (MOVDconst [0]) -(SRWconst (ANDconst [m] x) [s]) && mergePPC64AndSrwi(m>>uint(s),s) != 0 => (RLWINM [mergePPC64AndSrwi(m>>uint(s),s)] x) +(SRWconst (Select0 (ANDCCconst [m] x)) [s]) && mergePPC64RShiftMask(m>>uint(s),s,32) == 0 => (MOVDconst [0]) +(SRWconst (Select0 (ANDCCconst [m] x)) [s]) && mergePPC64AndSrwi(m>>uint(s),s) != 0 => (RLWINM [mergePPC64AndSrwi(m>>uint(s),s)] x) (SRWconst (AND (MOVDconst [m]) x) [s]) && mergePPC64RShiftMask(m>>uint(s),s,32) == 0 => (MOVDconst [0]) (SRWconst (AND (MOVDconst [m]) x) [s]) && mergePPC64AndSrwi(m>>uint(s),s) != 0 => (RLWINM [mergePPC64AndSrwi(m>>uint(s),s)] x) @@ -178,14 +155,10 @@ (CLRLSLDI [c] i:(RLWINM [s] x)) && mergePPC64ClrlsldiRlwinm(c,s) != 0 => (RLWINM [mergePPC64ClrlsldiRlwinm(c,s)] x) // large constant shifts -(Lsh64x64 _ (MOVDconst [c])) && uint64(c) >= 64 => (MOVDconst [0]) -(Rsh64Ux64 _ (MOVDconst [c])) && uint64(c) >= 64 => (MOVDconst [0]) -(Lsh32x64 _ (MOVDconst [c])) && uint64(c) >= 32 => (MOVDconst [0]) -(Rsh32Ux64 _ (MOVDconst [c])) && uint64(c) >= 32 => (MOVDconst [0]) -(Lsh16x64 _ (MOVDconst [c])) && uint64(c) >= 16 => (MOVDconst [0]) -(Rsh16Ux64 _ (MOVDconst [c])) && uint64(c) >= 16 => (MOVDconst [0]) -(Lsh8x64 _ (MOVDconst [c])) && uint64(c) >= 8 => (MOVDconst [0]) -(Rsh8Ux64 _ (MOVDconst [c])) && uint64(c) >= 8 => (MOVDconst [0]) +((Lsh64|Rsh64U)x64 _ (MOVDconst [c])) && uint64(c) >= 64 => (MOVDconst [0]) +((Lsh32|Rsh32U)x64 _ (MOVDconst [c])) && uint64(c) >= 32 => (MOVDconst [0]) +((Lsh16|Rsh16U)x64 _ (MOVDconst [c])) && uint64(c) >= 16 => (MOVDconst [0]) +((Lsh8|Rsh8U)x64 _ (MOVDconst [c])) && uint64(c) >= 8 => (MOVDconst [0]) // large constant signed right shift, we leave the sign bit (Rsh64x64 x (MOVDconst [c])) && uint64(c) >= 64 => (SRADconst x [63]) @@ -194,31 +167,19 @@ (Rsh8x64 x (MOVDconst [c])) && uint64(c) >= 8 => (SRAWconst (SignExt8to32 x) [63]) // constant shifts -(Lsh64x64 x (MOVDconst [c])) && uint64(c) < 64 => (SLDconst x [c]) -(Rsh64x64 x (MOVDconst [c])) && uint64(c) < 64 => (SRADconst x [c]) -(Rsh64Ux64 x (MOVDconst [c])) && uint64(c) < 64 => (SRDconst x [c]) -(Lsh32x64 x (MOVDconst [c])) && uint64(c) < 32 => (SLWconst x [c]) -(Rsh32x64 x (MOVDconst [c])) && uint64(c) < 32 => (SRAWconst x [c]) -(Rsh32Ux64 x (MOVDconst [c])) && uint64(c) < 32 => (SRWconst x [c]) +((Lsh64|Rsh64|Rsh64U)x64 x (MOVDconst [c])) && uint64(c) < 64 => (S(L|RA|R)Dconst x [c]) +((Lsh32|Rsh32|Rsh32U)x64 x (MOVDconst [c])) && uint64(c) < 32 => (S(L|RA|R)Wconst x [c]) +((Rsh16|Rsh16U)x64 x (MOVDconst [c])) && uint64(c) < 16 => (SR(AW|W)const ((Sign|Zero)Ext16to32 x) [c]) (Lsh16x64 x (MOVDconst [c])) && uint64(c) < 16 => (SLWconst x [c]) -(Rsh16x64 x (MOVDconst [c])) && uint64(c) < 16 => (SRAWconst (SignExt16to32 x) [c]) -(Rsh16Ux64 x (MOVDconst [c])) && uint64(c) < 16 => (SRWconst (ZeroExt16to32 x) [c]) -(Lsh8x64 x (MOVDconst [c])) && uint64(c) < 8 => (SLWconst x [c]) -(Rsh8x64 x (MOVDconst [c])) && uint64(c) < 8 => (SRAWconst (SignExt8to32 x) [c]) -(Rsh8Ux64 x (MOVDconst [c])) && uint64(c) < 8 => (SRWconst (ZeroExt8to32 x) [c]) - -(Lsh64x32 x (MOVDconst [c])) && uint32(c) < 64 => (SLDconst x [c&63]) -(Rsh64x32 x (MOVDconst [c])) && uint32(c) < 64 => (SRADconst x [c&63]) -(Rsh64Ux32 x (MOVDconst [c])) && uint32(c) < 64 => (SRDconst x [c&63]) -(Lsh32x32 x (MOVDconst [c])) && uint32(c) < 32 => (SLWconst x [c&31]) -(Rsh32x32 x (MOVDconst [c])) && uint32(c) < 32 => (SRAWconst x [c&31]) -(Rsh32Ux32 x (MOVDconst [c])) && uint32(c) < 32 => (SRWconst x [c&31]) -(Lsh16x32 x (MOVDconst [c])) && uint32(c) < 16 => (SLWconst x [c&31]) -(Rsh16x32 x (MOVDconst [c])) && uint32(c) < 16 => (SRAWconst (SignExt16to32 x) [c&15]) -(Rsh16Ux32 x (MOVDconst [c])) && uint32(c) < 16 => (SRWconst (ZeroExt16to32 x) [c&15]) +((Rsh8|Rsh8U)x64 x (MOVDconst [c])) && uint64(c) < 8 => (SR(AW|W)const ((Sign|Zero)Ext8to32 x) [c]) +(Lsh8x64 x (MOVDconst [c])) && uint64(c) < 8 => (SLWconst x [c]) + +((Lsh64|Rsh64|Rsh64U)x32 x (MOVDconst [c])) && uint32(c) < 64 => (S(L|RA|R)Dconst x [c&63]) +((Lsh32|Rsh32|Rsh32U)x32 x (MOVDconst [c])) && uint32(c) < 32 => (S(L|RA|R)Wconst x [c&31]) +(Lsh16x32 x (MOVDconst [c])) && uint32(c) < 16 => (SLWconst x [c&15]) +(Rsh(16|16U)x32 x (MOVDconst [c])) && uint32(c) < 16 => (S(RA|R)Wconst ((Sign|Zero)Ext16to32 x) [c&15]) (Lsh8x32 x (MOVDconst [c])) && uint32(c) < 8 => (SLWconst x [c&7]) -(Rsh8x32 x (MOVDconst [c])) && uint32(c) < 8 => (SRAWconst (SignExt8to32 x) [c&7]) -(Rsh8Ux32 x (MOVDconst [c])) && uint32(c) < 8 => (SRWconst (ZeroExt8to32 x) [c&7]) +(Rsh(8|8U)x32 x (MOVDconst [c])) && uint32(c) < 8 => (S(RA|R)Wconst ((Sign|Zero)Ext8to32 x) [c&7]) // Lower bounded shifts first. No need to check shift value. (Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SLD x y) @@ -238,106 +199,65 @@ // These are subexpressions found in statements that can become rotates // In these cases the shift count is known to be < 64 so the more complicated expressions // with Mask & Carry is not needed -(Lsh64x64 x (AND y (MOVDconst [63]))) => (SLD x (ANDconst [63] y)) -(Lsh64x64 x (ANDconst [63] y)) => (SLD x (ANDconst [63] y)) -(Rsh64Ux64 x (AND y (MOVDconst [63]))) => (SRD x (ANDconst [63] y)) -(Rsh64Ux64 x (ANDconst [63] y)) => (SRD x (ANDconst [63] y)) -(Rsh64Ux64 x (SUB (MOVDconst [64]) (ANDconst [63] y))) => (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y))) -(Rsh64Ux64 x (SUBFCconst [64] (ANDconst [63] y))) => (SRD x (SUBFCconst [64] (ANDconst [63] y))) -(Rsh64Ux64 x (SUB (MOVDconst [64]) (AND y (MOVDconst [63])))) => (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y))) -(Rsh64Ux64 x (SUBFCconst [64] (AND y (MOVDconst [63])))) => (SRD x (SUBFCconst [64] (ANDconst [63] y))) -(Rsh64x64 x (AND y (MOVDconst [63]))) => (SRAD x (ANDconst [63] y)) -(Rsh64x64 x (ANDconst [63] y)) => (SRAD x (ANDconst [63] y)) -(Rsh64x64 x (SUB (MOVDconst [64]) (ANDconst [63] y))) => (SRAD x (SUB (MOVDconst [64]) (ANDconst [63] y))) -(Rsh64x64 x (SUBFCconst [64] (ANDconst [63] y))) => (SRAD x (SUBFCconst [64] (ANDconst [63] y))) -(Rsh64x64 x (SUB (MOVDconst [64]) (AND y (MOVDconst [63])))) => (SRAD x (SUB (MOVDconst [64]) (ANDconst [63] y))) -(Rsh64x64 x (SUBFCconst [64] (AND y (MOVDconst [63])))) => (SRAD x (SUBFCconst [64] (ANDconst [63] y))) - -(Lsh64x64 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) -(Rsh64x64 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) -(Rsh64Ux64 x y) => (SRD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) - -(Lsh32x64 x (AND y (MOVDconst [31]))) => (SLW x (ANDconst [31] y)) -(Lsh32x64 x (ANDconst [31] y)) => (SLW x (ANDconst [31] y)) - -(Rsh32Ux64 x (AND y (MOVDconst [31]))) => (SRW x (ANDconst [31] y)) -(Rsh32Ux64 x (ANDconst [31] y)) => (SRW x (ANDconst [31] y)) -(Rsh32Ux64 x (SUB (MOVDconst [32]) (ANDconst [31] y))) => (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y))) -(Rsh32Ux64 x (SUBFCconst [32] (ANDconst [31] y))) => (SRW x (SUBFCconst [32] (ANDconst [31] y))) -(Rsh32Ux64 x (SUB (MOVDconst [32]) (AND y (MOVDconst [31])))) => (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y))) -(Rsh32Ux64 x (SUBFCconst [32] (AND y (MOVDconst [31])))) => (SRW x (SUBFCconst [32] (ANDconst [31] y))) - -(Rsh32x64 x (AND y (MOVDconst [31]))) => (SRAW x (ANDconst [31] y)) -(Rsh32x64 x (ANDconst [31] y)) => (SRAW x (ANDconst [31] y)) -(Rsh32x64 x (SUB (MOVDconst [32]) (ANDconst [31] y))) => (SRAW x (SUB (MOVDconst [32]) (ANDconst [31] y))) -(Rsh32x64 x (SUBFCconst [32] (ANDconst [31] y))) => (SRAW x (SUBFCconst [32] (ANDconst [31] y))) -(Rsh32x64 x (SUB (MOVDconst [32]) (AND y (MOVDconst [31])))) => (SRAW x (SUB (MOVDconst [32]) (ANDconst [31] y))) -(Rsh32x64 x (SUBFCconst [32] (AND y (MOVDconst [31])))) => (SRAW x (SUBFCconst [32] (ANDconst [31] y))) - -(Rsh32x64 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) -(Rsh32Ux64 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) -(Lsh32x64 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) - -(Rsh16x64 x y) => (SRAW (SignExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) -(Rsh16Ux64 x y) => (SRW (ZeroExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) +((Lsh64|Rsh64U|Rsh64)x64 x (AND y (MOVDconst [63]))) => (S(L|R|RA)D x (Select0 (ANDCCconst [63] y))) +(Lsh64x64 x (Select0 (ANDCCconst [63] y))) => (SLD x (Select0 (ANDCCconst [63] y))) +((Rsh64U|Rsh64)x64 x (Select0 (ANDCCconst [63] y))) => (S(R|RA)D x (Select0 (ANDCCconst [63] y))) +((Rsh64U|Rsh64)x64 x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) => (SR(D|AD) x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) +((Rsh64U|Rsh64)x64 x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) => (SR(D|AD) x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) +((Rsh64U|Rsh64)x64 x (SUB (MOVDconst [64]) (AND y (MOVDconst [63])))) => (SR(D|AD) x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) +((Rsh64U|Rsh64)x64 x (SUBFCconst [64] (AND y (MOVDconst [63])))) => (SR(D|AD) x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) + +((Lsh64|Rsh64|Rsh64U)x64 x y) => (S(L|RA|R)D x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) + +((Lsh32|Rsh32|Rsh32U)x64 x (AND y (MOVDconst [31]))) => (S(L|RA|R)W x (Select0 (ANDCCconst [31] y))) +(Lsh32x64 x (Select0 (ANDCCconst [31] y))) => (SLW x (Select0 (ANDCCconst [31] y))) +((Rsh32|Rsh32U)x64 x (Select0 (ANDCCconst [31] y))) => (S(RA|R)W x (Select0 (ANDCCconst [31] y))) +(Rsh(32|32U)x64 x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) => (SR(AW|W) x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) +(Rsh(32|32U)x64 x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) => (SR(AW|W) x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) +(Rsh(32|32U)x64 x (SUB (MOVDconst [32]) (AND y (MOVDconst [31])))) => (SR(AW|W) x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) +(Rsh(32|32U)x64 x (SUBFCconst [32] (AND y (MOVDconst [31])))) => (SR(AW|W) x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) + +((Rsh32|Rsh32U|Lsh32)x64 x y) => (S(RA|R|L)W x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) + +(Rsh(16|16U)x64 x y) => (SR(AW|W) ((Sign|Zero)Ext16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) (Lsh16x64 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) -(Rsh8x64 x y) => (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) -(Rsh8Ux64 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) +(Rsh(8|8U)x64 x y) => (SR(AW|W) ((Sign|Zero)Ext8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) (Lsh8x64 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) -(Rsh64x32 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) -(Rsh64Ux32 x y) => (SRD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) -(Lsh64x32 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) -(Rsh32x32 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) -(Rsh32Ux32 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) -(Lsh32x32 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) +((Rsh64|Rsh64U|Lsh64)x32 x y) => (S(RA|R|L)D x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64])))) +((Rsh32|Rsh32U|Lsh32)x32 x y) => (S(RA|R|L)W x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32])))) -(Rsh16x32 x y) => (SRAW (SignExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) -(Rsh16Ux32 x y) => (SRW (ZeroExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) +(Rsh(16|16U)x32 x y) => (SR(AW|W) ((Sign|Zero)Ext16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) (Lsh16x32 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [16])))) -(Rsh8x32 x y) => (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) -(Rsh8Ux32 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) +(Rsh(8|8U)x32 x y) => (SR(AW|W) ((Sign|Zero)Ext8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) (Lsh8x32 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) +((Rsh64|Rsh64U|Lsh64)x16 x y) => (S(RA|R|L)D x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [64])))) -(Rsh64x16 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [64])))) -(Rsh64Ux16 x y) => (SRD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [64])))) -(Lsh64x16 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [64])))) +((Rsh32|Rsh32U|Lsh32)x16 x y) => (S(RA|R|L)W x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [32])))) -(Rsh32x16 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [32])))) -(Rsh32Ux16 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [32])))) -(Lsh32x16 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [32])))) - -(Rsh16x16 x y) => (SRAW (SignExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [16])))) -(Rsh16Ux16 x y) => (SRW (ZeroExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [16])))) +(Rsh(16|16U)x16 x y) => (S(RA|R)W ((Sign|Zero)Ext16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [16])))) (Lsh16x16 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [16])))) -(Rsh8x16 x y) => (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [8])))) -(Rsh8Ux16 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [8])))) +(Rsh(8|8U)x16 x y) => (SR(AW|W) ((Sign|Zero)Ext8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [8])))) (Lsh8x16 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt16to64 y) (MOVDconst [8])))) -(Rsh64x8 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [64])))) -(Rsh64Ux8 x y) => (SRD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [64])))) -(Lsh64x8 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [64])))) +((Rsh64|Rsh64U|Lsh64)x8 x y) => (S(RA|R|L)D x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [64])))) -(Rsh32x8 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [32])))) -(Rsh32Ux8 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [32])))) -(Lsh32x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [32])))) +((Rsh32|Rsh32U|Lsh32)x8 x y) => (S(RA|R|L)W x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [32])))) -(Rsh16x8 x y) => (SRAW (SignExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [16])))) -(Rsh16Ux8 x y) => (SRW (ZeroExt16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [16])))) +(Rsh(16|16U)x8 x y) => (S(RA|R)W ((Sign|Zero)Ext16to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [16])))) (Lsh16x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [16])))) -(Rsh8x8 x y) => (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) -(Rsh8Ux8 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) +(Rsh(8|8U)x8 x y) => (S(RA|R)W ((Sign|Zero)Ext8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) (Lsh8x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) // Cleaning up shift ops -(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPU (ANDconst [d] y) (MOVDconst [c]))) && c >= d => (ANDconst [d] y) -(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPUconst [c] (ANDconst [d] y))) && c >= d => (ANDconst [d] y) +(ISEL [0] (Select0 (ANDCCconst [d] y)) (MOVDconst [-1]) (CMPU (Select0 (ANDCCconst [d] y)) (MOVDconst [c]))) && c >= d => (Select0 (ANDCCconst [d] y)) +(ISEL [0] (Select0 (ANDCCconst [d] y)) (MOVDconst [-1]) (CMPUconst [c] (Select0 (ANDCCconst [d] y)))) && c >= d => (Select0 (ANDCCconst [d] y)) (ORN x (MOVDconst [-1])) => x (S(RAD|RD|LD) x (MOVDconst [c])) => (S(RAD|RD|LD)const [c&63 | (c>>6&1*63)] x) @@ -362,17 +282,14 @@ (BitLen32 x) => (SUBFCconst [32] (CNTLZW x)) (PopCount64 ...) => (POPCNTD ...) -(PopCount32 x) => (POPCNTW (MOVWZreg x)) -(PopCount16 x) => (POPCNTW (MOVHZreg x)) -(PopCount8 x) => (POPCNTB (MOVBZreg x)) +(PopCount(32|16|8) x) => (POPCNT(W|W|B) (MOV(W|H|B)Zreg x)) (And(64|32|16|8) ...) => (AND ...) (Or(64|32|16|8) ...) => (OR ...) (Xor(64|32|16|8) ...) => (XOR ...) (Neg(64|32|16|8) ...) => (NEG ...) -(Neg64F ...) => (FNEG ...) -(Neg32F ...) => (FNEG ...) +(Neg(64|32)F ...) => (FNEG ...) (Com(64|32|16|8) x) => (NOR x x) @@ -381,57 +298,38 @@ (OrB ...) => (OR ...) (Not x) => (XORconst [1] x) -// Use ANDN for AND x NOT y +// Merge logical operations (AND x (NOR y y)) => (ANDN x y) +(OR x (NOR y y)) => (ORN x y) // Lowering comparisons -(EqB x y) => (ANDconst [1] (EQV x y)) +(EqB x y) => (Select0 (ANDCCconst [1] (EQV x y))) // Sign extension dependence on operand sign sets up for sign/zero-extension elision later -(Eq8 x y) && isSigned(x.Type) && isSigned(y.Type) => (Equal (CMPW (SignExt8to32 x) (SignExt8to32 y))) -(Eq16 x y) && isSigned(x.Type) && isSigned(y.Type) => (Equal (CMPW (SignExt16to32 x) (SignExt16to32 y))) -(Eq8 x y) => (Equal (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y))) -(Eq16 x y) => (Equal (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y))) -(Eq32 x y) => (Equal (CMPW x y)) -(Eq64 x y) => (Equal (CMP x y)) -(Eq32F x y) => (Equal (FCMPU x y)) -(Eq64F x y) => (Equal (FCMPU x y)) -(EqPtr x y) => (Equal (CMP x y)) +(Eq(8|16) x y) && isSigned(x.Type) && isSigned(y.Type) => (Equal (CMPW (SignExt(8|16)to32 x) (SignExt(8|16)to32 y))) +(Eq(8|16) x y) => (Equal (CMPW (ZeroExt(8|16)to32 x) (ZeroExt(8|16)to32 y))) +(Eq(32|64|Ptr) x y) => (Equal ((CMPW|CMP|CMP) x y)) +(Eq(32|64)F x y) => (Equal (FCMPU x y)) (NeqB ...) => (XOR ...) // Like Eq8 and Eq16, prefer sign extension likely to enable later elision. -(Neq8 x y) && isSigned(x.Type) && isSigned(y.Type) => (NotEqual (CMPW (SignExt8to32 x) (SignExt8to32 y))) -(Neq16 x y) && isSigned(x.Type) && isSigned(y.Type) => (NotEqual (CMPW (SignExt16to32 x) (SignExt16to32 y))) -(Neq8 x y) => (NotEqual (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y))) -(Neq16 x y) => (NotEqual (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y))) -(Neq32 x y) => (NotEqual (CMPW x y)) -(Neq64 x y) => (NotEqual (CMP x y)) -(Neq32F x y) => (NotEqual (FCMPU x y)) -(Neq64F x y) => (NotEqual (FCMPU x y)) -(NeqPtr x y) => (NotEqual (CMP x y)) - -(Less8 x y) => (LessThan (CMPW (SignExt8to32 x) (SignExt8to32 y))) -(Less16 x y) => (LessThan (CMPW (SignExt16to32 x) (SignExt16to32 y))) -(Less32 x y) => (LessThan (CMPW x y)) -(Less64 x y) => (LessThan (CMP x y)) -(Less32F x y) => (FLessThan (FCMPU x y)) -(Less64F x y) => (FLessThan (FCMPU x y)) - -(Less8U x y) => (LessThan (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y))) -(Less16U x y) => (LessThan (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y))) -(Less32U x y) => (LessThan (CMPWU x y)) -(Less64U x y) => (LessThan (CMPU x y)) - -(Leq8 x y) => (LessEqual (CMPW (SignExt8to32 x) (SignExt8to32 y))) -(Leq16 x y) => (LessEqual (CMPW (SignExt16to32 x) (SignExt16to32 y))) -(Leq32 x y) => (LessEqual (CMPW x y)) -(Leq64 x y) => (LessEqual (CMP x y)) -(Leq32F x y) => (FLessEqual (FCMPU x y)) -(Leq64F x y) => (FLessEqual (FCMPU x y)) - -(Leq8U x y) => (LessEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y))) -(Leq16U x y) => (LessEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y))) -(Leq32U x y) => (LessEqual (CMPWU x y)) -(Leq64U x y) => (LessEqual (CMPU x y)) +(Neq(8|16) x y) && isSigned(x.Type) && isSigned(y.Type) => (NotEqual (CMPW (SignExt(8|16)to32 x) (SignExt(8|16)to32 y))) +(Neq(8|16) x y) => (NotEqual (CMPW (ZeroExt(8|16)to32 x) (ZeroExt(8|16)to32 y))) +(Neq(32|64|Ptr) x y) => (NotEqual ((CMPW|CMP|CMP) x y)) +(Neq(32|64)F x y) => (NotEqual (FCMPU x y)) + +(Less(8|16) x y) => (LessThan (CMPW (SignExt(8|16)to32 x) (SignExt(8|16)to32 y))) +(Less(32|64) x y) => (LessThan ((CMPW|CMP) x y)) +(Less(32|64)F x y) => (FLessThan (FCMPU x y)) + +(Less(8|16)U x y) => (LessThan (CMPWU (ZeroExt(8|16)to32 x) (ZeroExt(8|16)to32 y))) +(Less(32|64)U x y) => (LessThan ((CMPWU|CMPU) x y)) + +(Leq(8|16) x y) => (LessEqual (CMPW (SignExt(8|16)to32 x) (SignExt(8|16)to32 y))) +(Leq(32|64) x y) => (LessEqual ((CMPW|CMP) x y)) +(Leq(32|64)F x y) => (FLessEqual (FCMPU x y)) + +(Leq(8|16)U x y) => (LessEqual (CMPWU (ZeroExt(8|16)to32 x) (ZeroExt(8|16)to32 y))) +(Leq(32|64)U x y) => (LessEqual (CMP(WU|U) x y)) // Absorb pseudo-ops into blocks. (If (Equal cc) yes no) => (EQ cc yes no) @@ -445,25 +343,15 @@ (If (FGreaterThan cc) yes no) => (FGT cc yes no) (If (FGreaterEqual cc) yes no) => (FGE cc yes no) -(If cond yes no) => (NE (CMPWconst [0] cond) yes no) +(If cond yes no) => (NE (CMPWconst [0] (Select0 (ANDCCconst [1] cond))) yes no) // Absorb boolean tests into block -(NE (CMPWconst [0] (Equal cc)) yes no) => (EQ cc yes no) -(NE (CMPWconst [0] (NotEqual cc)) yes no) => (NE cc yes no) -(NE (CMPWconst [0] (LessThan cc)) yes no) => (LT cc yes no) -(NE (CMPWconst [0] (LessEqual cc)) yes no) => (LE cc yes no) -(NE (CMPWconst [0] (GreaterThan cc)) yes no) => (GT cc yes no) -(NE (CMPWconst [0] (GreaterEqual cc)) yes no) => (GE cc yes no) -(NE (CMPWconst [0] (FLessThan cc)) yes no) => (FLT cc yes no) -(NE (CMPWconst [0] (FLessEqual cc)) yes no) => (FLE cc yes no) -(NE (CMPWconst [0] (FGreaterThan cc)) yes no) => (FGT cc yes no) -(NE (CMPWconst [0] (FGreaterEqual cc)) yes no) => (FGE cc yes no) - -// Elide compares of bit tests // TODO need to make both CC and result of ANDCC available. -(EQ (CMPconst [0] (ANDconst [c] x)) yes no) => (EQ (ANDCCconst [c] x) yes no) -(NE (CMPconst [0] (ANDconst [c] x)) yes no) => (NE (ANDCCconst [c] x) yes no) -(EQ (CMPWconst [0] (ANDconst [c] x)) yes no) => (EQ (ANDCCconst [c] x) yes no) -(NE (CMPWconst [0] (ANDconst [c] x)) yes no) => (NE (ANDCCconst [c] x) yes no) +(NE (CMPWconst [0] (Select0 (ANDCCconst [1] ((Equal|NotEqual|LessThan|LessEqual|GreaterThan|GreaterEqual) cc)))) yes no) => ((EQ|NE|LT|LE|GT|GE) cc yes no) +(NE (CMPWconst [0] (Select0 (ANDCCconst [1] ((FLessThan|FLessEqual|FGreaterThan|FGreaterEqual) cc)))) yes no) => ((FLT|FLE|FGT|FGE) cc yes no) + +// Elide compares of bit tests +((EQ|NE) (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) => ((EQ|NE) (Select1 (ANDCCconst [c] x)) yes no) +((EQ|NE) (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) => ((EQ|NE) (Select1 (ANDCCconst [c] x)) yes no) // absorb flag constants into branches (EQ (FlagEQ) yes no) => (First yes no) @@ -515,12 +403,6 @@ (CMPUconst (MOVDconst [x]) [y]) && uint64(x) (FlagLT) (CMPUconst (MOVDconst [x]) [y]) && uint64(x)>uint64(y) => (FlagGT) -// other known comparisons -//(CMPconst (MOVBUreg _) [c]) && 0xff < c => (FlagLT) -//(CMPconst (MOVHUreg _) [c]) && 0xffff < c => (FlagLT) -//(CMPconst (ANDconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) => (FlagLT) -//(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1< (FlagLT) - // absorb flag constants into boolean values (Equal (FlagEQ)) => (MOVDconst [1]) (Equal (FlagLT)) => (MOVDconst [0]) @@ -547,22 +429,20 @@ (GreaterEqual (FlagGT)) => (MOVDconst [1]) // absorb InvertFlags into boolean values -(Equal (InvertFlags x)) => (Equal x) -(NotEqual (InvertFlags x)) => (NotEqual x) -(LessThan (InvertFlags x)) => (GreaterThan x) -(GreaterThan (InvertFlags x)) => (LessThan x) -(LessEqual (InvertFlags x)) => (GreaterEqual x) -(GreaterEqual (InvertFlags x)) => (LessEqual x) - -// Elide compares of bit tests // TODO need to make both CC and result of ANDCC available. -((EQ|NE|LT|LE|GT|GE) (CMPconst [0] (ANDconst [c] x)) yes no) => ((EQ|NE|LT|LE|GT|GE) (ANDCCconst [c] x) yes no) -((EQ|NE|LT|LE|GT|GE) (CMPWconst [0] (ANDconst [c] x)) yes no) => ((EQ|NE|LT|LE|GT|GE) (ANDCCconst [c] x) yes no) +((Equal|NotEqual|LessThan|GreaterThan|LessEqual|GreaterEqual) (InvertFlags x)) => ((Equal|NotEqual|GreaterThan|LessThan|GreaterEqual|LessEqual) x) + + +// Elide compares of bit tests +((EQ|NE|LT|LE|GT|GE) (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) => ((EQ|NE|LT|LE|GT|GE) (Select1 (ANDCCconst [c] x)) yes no) +((EQ|NE|LT|LE|GT|GE) (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) => ((EQ|NE|LT|LE|GT|GE) (Select1 (ANDCCconst [c] x)) yes no) ((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(AND x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (ANDCC x y) yes no) ((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(OR x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (ORCC x y) yes no) ((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(XOR x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (XORCC x y) yes no) -(CondSelect x y bool) && flagArg(bool) != nil => (ISEL [2] x y bool) -(CondSelect x y bool) && flagArg(bool) == nil => (ISEL [2] x y (CMPWconst [0] bool)) +// Only lower after bool is lowered. It should always lower. This helps ensure the folding below happens reliably. +(CondSelect x y bool) && flagArg(bool) == nil => (ISEL [6] x y (CMPWconst [0] bool)) +// Fold any CR -> GPR -> CR transfers when applying the above rule. +(ISEL [6] x y (CMPWconst [0] (ISELB [c] one cmp))) => (ISEL [c] x y cmp) // Lowering loads (Load ptr mem) && (is64BitInt(t) || isPtr(t)) => (MOVDload ptr mem) @@ -580,7 +460,7 @@ (Store {t} ptr val mem) && t.Size() == 8 && is64BitFloat(val.Type) => (FMOVDstore ptr val mem) (Store {t} ptr val mem) && t.Size() == 8 && is32BitFloat(val.Type) => (FMOVDstore ptr val mem) // glitch from (Cvt32Fto64F x) => x -- type is wrong (Store {t} ptr val mem) && t.Size() == 4 && is32BitFloat(val.Type) => (FMOVSstore ptr val mem) -(Store {t} ptr val mem) && t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type)) => (MOVDstore ptr val mem) +(Store {t} ptr val mem) && t.Size() == 8 && !is64BitFloat(val.Type) => (MOVDstore ptr val mem) (Store {t} ptr val mem) && t.Size() == 4 && is32BitInt(val.Type) => (MOVWstore ptr val mem) (Store {t} ptr val mem) && t.Size() == 2 => (MOVHstore ptr val mem) (Store {t} ptr val mem) && t.Size() == 1 => (MOVBstore ptr val mem) @@ -670,6 +550,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (GetClosurePtr ...) => (LoweredGetClosurePtr ...) @@ -683,6 +564,9 @@ // Write barrier. (WB ...) => (LoweredWB ...) +// Publication barrier as intrinsic +(PubBarrier ...) => (LoweredPubBarrier ...) + (PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem) (PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem) (PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem) @@ -700,44 +584,44 @@ (NOR (MOVDconst [c]) (MOVDconst [d])) => (MOVDconst [^(c|d)]) // Discover consts -(AND x (MOVDconst [c])) && isU16Bit(c) => (ANDconst [c] x) +(AND x (MOVDconst [c])) && isU16Bit(c) => (Select0 (ANDCCconst [c] x)) (XOR x (MOVDconst [c])) && isU32Bit(c) => (XORconst [c] x) (OR x (MOVDconst [c])) && isU32Bit(c) => (ORconst [c] x) // Simplify consts -(ANDconst [c] (ANDconst [d] x)) => (ANDconst [c&d] x) +(Select0 (ANDCCconst [c] (Select0 (ANDCCconst [d] x)))) => (Select0 (ANDCCconst [c&d] x)) (ORconst [c] (ORconst [d] x)) => (ORconst [c|d] x) (XORconst [c] (XORconst [d] x)) => (XORconst [c^d] x) -(ANDconst [-1] x) => x -(ANDconst [0] _) => (MOVDconst [0]) +(Select0 (ANDCCconst [-1] x)) => x +(Select0 (ANDCCconst [0] _)) => (MOVDconst [0]) (XORconst [0] x) => x (ORconst [-1] _) => (MOVDconst [-1]) (ORconst [0] x) => x // zero-extend of small and => small and -(MOVBZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFF => y -(MOVHZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFF => y -(MOVWZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFFFFFF => y +(MOVBZreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0xFF => y +(MOVHZreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0xFFFF => y +(MOVWZreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0xFFFFFFFF => y (MOVWZreg y:(AND (MOVDconst [c]) _)) && uint64(c) <= 0xFFFFFFFF => y // sign extend of small-positive and => small-positive-and -(MOVBreg y:(ANDconst [c] _)) && uint64(c) <= 0x7F => y -(MOVHreg y:(ANDconst [c] _)) && uint64(c) <= 0x7FFF => y -(MOVWreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFF => y // 0xFFFF is largest immediate constant, when regarded as 32-bit is > 0 +(MOVBreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0x7F => y +(MOVHreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0x7FFF => y +(MOVWreg y:(Select0 (ANDCCconst [c] _))) && uint64(c) <= 0xFFFF => y // 0xFFFF is largest immediate constant, when regarded as 32-bit is > 0 (MOVWreg y:(AND (MOVDconst [c]) _)) && uint64(c) <= 0x7FFFFFFF => y // small and of zero-extend => either zero-extend or small and -(ANDconst [c] y:(MOVBZreg _)) && c&0xFF == 0xFF => y -(ANDconst [0xFF] y:(MOVBreg _)) => y -(ANDconst [c] y:(MOVHZreg _)) && c&0xFFFF == 0xFFFF => y -(ANDconst [0xFFFF] y:(MOVHreg _)) => y +(Select0 (ANDCCconst [c] y:(MOVBZreg _))) && c&0xFF == 0xFF => y +(Select0 (ANDCCconst [0xFF] y:(MOVBreg _))) => y +(Select0 (ANDCCconst [c] y:(MOVHZreg _))) && c&0xFFFF == 0xFFFF => y +(Select0 (ANDCCconst [0xFFFF] y:(MOVHreg _))) => y (AND (MOVDconst [c]) y:(MOVWZreg _)) && c&0xFFFFFFFF == 0xFFFFFFFF => y (AND (MOVDconst [0xFFFFFFFF]) y:(MOVWreg x)) => (MOVWZreg x) // normal case -(ANDconst [c] (MOV(B|BZ)reg x)) => (ANDconst [c&0xFF] x) -(ANDconst [c] (MOV(H|HZ)reg x)) => (ANDconst [c&0xFFFF] x) -(ANDconst [c] (MOV(W|WZ)reg x)) => (ANDconst [c&0xFFFFFFFF] x) +(Select0 (ANDCCconst [c] (MOV(B|BZ)reg x))) => (Select0 (ANDCCconst [c&0xFF] x)) +(Select0 (ANDCCconst [c] (MOV(H|HZ)reg x))) => (Select0 (ANDCCconst [c&0xFFFF] x)) +(Select0 (ANDCCconst [c] (MOV(W|WZ)reg x))) => (Select0 (ANDCCconst [c&0xFFFFFFFF] x)) // Eliminate unnecessary sign/zero extend following right shift (MOV(B|H|W)Zreg (SRWconst [c] (MOVBZreg x))) => (SRWconst [c] (MOVBZreg x)) @@ -747,12 +631,9 @@ (MOV(H|W)reg (SRAWconst [c] (MOVHreg x))) => (SRAWconst [c] (MOVHreg x)) (MOVWreg (SRAWconst [c] (MOVWreg x))) => (SRAWconst [c] (MOVWreg x)) -(MOVWZreg (SRWconst [c] x)) && sizeof(x.Type) <= 32 => (SRWconst [c] x) -(MOVHZreg (SRWconst [c] x)) && sizeof(x.Type) <= 16 => (SRWconst [c] x) -(MOVBZreg (SRWconst [c] x)) && sizeof(x.Type) == 8 => (SRWconst [c] x) -(MOVWreg (SRAWconst [c] x)) && sizeof(x.Type) <= 32 => (SRAWconst [c] x) -(MOVHreg (SRAWconst [c] x)) && sizeof(x.Type) <= 16 => (SRAWconst [c] x) -(MOVBreg (SRAWconst [c] x)) && sizeof(x.Type) == 8 => (SRAWconst [c] x) +(MOV(WZ|W)reg (S(R|RA)Wconst [c] x)) && sizeof(x.Type) <= 32 => (S(R|RA)Wconst [c] x) +(MOV(HZ|H)reg (S(R|RA)Wconst [c] x)) && sizeof(x.Type) <= 16 => (S(R|RA)Wconst [c] x) +(MOV(BZ|B)reg (S(R|RA)Wconst [c] x)) && sizeof(x.Type) == 8 => (S(R|RA)Wconst [c] x) // initial right shift will handle sign/zero extend (MOVBZreg (SRDconst [c] x)) && c>=56 => (SRDconst [c] x) @@ -786,30 +667,19 @@ // H - there are more combinations than these -(MOVHZreg y:(MOVHZreg _)) => y // repeat -(MOVHZreg y:(MOVBZreg _)) => y // wide of narrow +(MOVHZreg y:(MOV(H|B)Zreg _)) => y // repeat (MOVHZreg y:(MOVHBRload _ _)) => y -(MOVHreg y:(MOVHreg _)) => y // repeat -(MOVHreg y:(MOVBreg _)) => y // wide of narrow +(MOVHreg y:(MOV(H|B)reg _)) => y // repeat -(MOVHreg y:(MOVHZreg x)) => (MOVHreg x) -(MOVHZreg y:(MOVHreg x)) => (MOVHZreg x) +(MOV(H|HZ)reg y:(MOV(HZ|H)reg x)) => (MOV(H|HZ)reg x) // W - there are more combinations than these -(MOVWZreg y:(MOVWZreg _)) => y // repeat -(MOVWZreg y:(MOVHZreg _)) => y // wide of narrow -(MOVWZreg y:(MOVBZreg _)) => y // wide of narrow -(MOVWZreg y:(MOVHBRload _ _)) => y -(MOVWZreg y:(MOVWBRload _ _)) => y - -(MOVWreg y:(MOVWreg _)) => y // repeat -(MOVWreg y:(MOVHreg _)) => y // wide of narrow -(MOVWreg y:(MOVBreg _)) => y // wide of narrow +(MOV(WZ|WZ|WZ|W|W|W)reg y:(MOV(WZ|HZ|BZ|W|H|B)reg _)) => y // repeat +(MOVWZreg y:(MOV(H|W)BRload _ _)) => y -(MOVWreg y:(MOVWZreg x)) => (MOVWreg x) -(MOVWZreg y:(MOVWreg x)) => (MOVWZreg x) +(MOV(W|WZ)reg y:(MOV(WZ|W)reg x)) => (MOV(W|WZ)reg x) // Truncate then logical then truncate: omit first, lesser or equal truncate (MOVWZreg ((OR|XOR|AND) x (MOVWZreg y))) => (MOVWZreg ((OR|XOR|AND) x y)) @@ -819,12 +689,10 @@ (MOVBZreg ((OR|XOR|AND) x (MOVHZreg y))) => (MOVBZreg ((OR|XOR|AND) x y)) (MOVBZreg ((OR|XOR|AND) x (MOVBZreg y))) => (MOVBZreg ((OR|XOR|AND) x y)) -(MOV(B|H|W)Zreg z:(ANDconst [c] (MOVBZload ptr x))) => z -(MOVBZreg z:(AND y (MOVBZload ptr x))) => z -(MOV(H|W)Zreg z:(ANDconst [c] (MOVHZload ptr x))) => z -(MOVHZreg z:(AND y (MOVHZload ptr x))) => z -(MOVWZreg z:(ANDconst [c] (MOVWZload ptr x))) => z -(MOVWZreg z:(AND y (MOVWZload ptr x))) => z +(MOV(B|H|W)Zreg z:(Select0 (ANDCCconst [c] (MOVBZload ptr x)))) => z +(MOV(B|H|W)Zreg z:(AND y (MOV(B|H|W)Zload ptr x))) => z +(MOV(H|W)Zreg z:(Select0 (ANDCCconst [c] (MOVHZload ptr x)))) => z +(MOVWZreg z:(Select0 (ANDCCconst [c] (MOVWZload ptr x)))) => z // Arithmetic constant ops @@ -847,6 +715,8 @@ (ADDconst [c] (SUBFCconst [d] x)) && is32Bit(c+d) => (SUBFCconst [c+d] x) (NEG (ADDconst [c] x)) && is32Bit(-c) => (SUBFCconst [-c] x) (NEG (SUBFCconst [c] x)) && is32Bit(-c) => (ADDconst [-c] x) +(NEG (SUB x y)) => (SUB y x) +(NEG (NEG x)) => x // Use register moves instead of stores and loads to move int<=>float values // Common with math Float64bits, Float64frombits @@ -863,74 +733,37 @@ (MFVSRD x:(FMOVDload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) => @x.Block (MOVDload [off] {sym} ptr mem) // Fold offsets for stores. -(MOVDstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(int64(off1)+off2) => (MOVDstore [off1+int32(off2)] {sym} x val mem) -(MOVWstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(int64(off1)+off2) => (MOVWstore [off1+int32(off2)] {sym} x val mem) -(MOVHstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(int64(off1)+off2) => (MOVHstore [off1+int32(off2)] {sym} x val mem) -(MOVBstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(int64(off1)+off2) => (MOVBstore [off1+int32(off2)] {sym} x val mem) +(MOV(D|W|H|B)store [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(int64(off1)+off2) => (MOV(D|W|H|B)store [off1+int32(off2)] {sym} x val mem) -(FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is16Bit(int64(off1)+off2) => (FMOVSstore [off1+int32(off2)] {sym} ptr val mem) -(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is16Bit(int64(off1)+off2) => (FMOVDstore [off1+int32(off2)] {sym} ptr val mem) +(FMOV(S|D)store [off1] {sym} (ADDconst [off2] ptr) val mem) && is16Bit(int64(off1)+off2) => (FMOV(S|D)store [off1+int32(off2)] {sym} ptr val mem) // Fold address into load/store. // The assembler needs to generate several instructions and use // temp register for accessing global, and each time it will reload // the temp register. So don't fold address of global, unless there // is only one use. -(MOVBstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(MOVHstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(MOVWstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(MOVDstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) +(MOV(B|H|W|D)store [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) + (MOV(B|H|W|D)store [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(FMOVSstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) +(FMOV(S|D)store [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(FMOVDstore [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) + (FMOV(S|D)store [off1+off2] {mergeSym(sym1,sym2)} ptr val mem) -(MOVBZload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVBZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVHload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVHZload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVHZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVWload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) +(MOV(B|H|W)Zload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVWZload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) + (MOV(B|H|W)Zload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) +(MOV(H|W|D)load [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVWZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVDload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) + (MOV(H|W|D)load [off1+off2] {mergeSym(sym1,sym2)} ptr mem) +(FMOV(S|D)load [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(FMOVSload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(FMOVDload [off1] {sym1} p:(MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) - && is16Bit(int64(off1+off2)) && (ptr.Op != OpSB || p.Uses == 1) => - (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + (FMOV(S|D)load [off1+off2] {mergeSym(sym1,sym2)} ptr mem) // Fold offsets for loads. -(FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem) && is16Bit(int64(off1)+off2) => (FMOVSload [off1+int32(off2)] {sym} ptr mem) -(FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem) && is16Bit(int64(off1)+off2) => (FMOVDload [off1+int32(off2)] {sym} ptr mem) +(FMOV(S|D)load [off1] {sym} (ADDconst [off2] ptr) mem) && is16Bit(int64(off1)+off2) => (FMOV(S|D)load [off1+int32(off2)] {sym} ptr mem) -(MOVDload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVDload [off1+int32(off2)] {sym} x mem) -(MOVWload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVWload [off1+int32(off2)] {sym} x mem) -(MOVWZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVWZload [off1+int32(off2)] {sym} x mem) -(MOVHload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVHload [off1+int32(off2)] {sym} x mem) -(MOVHZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVHZload [off1+int32(off2)] {sym} x mem) -(MOVBZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOVBZload [off1+int32(off2)] {sym} x mem) +(MOV(D|W|WZ|H|HZ|BZ)load [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => (MOV(D|W|WZ|H|HZ|BZ)load [off1+int32(off2)] {sym} x mem) // Determine load + addressing that can be done as a register indexed load (MOV(D|W|WZ|H|HZ|BZ)load [0] {sym} p:(ADD ptr idx) mem) && sym == nil && p.Uses == 1 => (MOV(D|W|WZ|H|HZ|BZ)loadidx ptr idx mem) @@ -945,20 +778,11 @@ (MOV(WZ|H|HZ|BZ)loadidx (MOVDconst [c]) ptr mem) && is16Bit(c) => (MOV(WZ|H|HZ|BZ)load [int32(c)] ptr mem) // Store of zero => storezero -(MOVDstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVDstorezero [off] {sym} ptr mem) -(MOVWstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVWstorezero [off] {sym} ptr mem) -(MOVHstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVHstorezero [off] {sym} ptr mem) -(MOVBstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem) +(MOV(D|W|H|B)store [off] {sym} ptr (MOVDconst [0]) mem) => (MOV(D|W|H|B)storezero [off] {sym} ptr mem) // Fold offsets for storezero -(MOVDstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => - (MOVDstorezero [off1+int32(off2)] {sym} x mem) -(MOVWstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => - (MOVWstorezero [off1+int32(off2)] {sym} x mem) -(MOVHstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => - (MOVHstorezero [off1+int32(off2)] {sym} x mem) -(MOVBstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => - (MOVBstorezero [off1+int32(off2)] {sym} x mem) +(MOV(D|W|H|B)storezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(int64(off1)+off2) => + (MOV(D|W|H|B)storezero [off1+int32(off2)] {sym} x mem) // Stores with addressing that can be done as indexed stores (MOV(D|W|H|B)store [0] {sym} p:(ADD ptr idx) val mem) && sym == nil && p.Uses == 1 => (MOV(D|W|H|B)storeidx ptr idx val mem) @@ -971,18 +795,9 @@ (MOV(W|H|B)storeidx (MOVDconst [c]) ptr val mem) && is16Bit(c) => (MOV(W|H|B)store [int32(c)] ptr val mem) // Fold symbols into storezero -(MOVDstorezero [off1] {sym1} p:(MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) - && (x.Op != OpSB || p.Uses == 1) => - (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem) -(MOVWstorezero [off1] {sym1} p:(MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) - && (x.Op != OpSB || p.Uses == 1) => - (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem) -(MOVHstorezero [off1] {sym1} p:(MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) - && (x.Op != OpSB || p.Uses == 1) => - (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem) -(MOVBstorezero [off1] {sym1} p:(MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) +(MOV(D|W|H|B)storezero [off1] {sym1} p:(MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) && (x.Op != OpSB || p.Uses == 1) => - (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem) + (MOV(D|W|H|B)storezero [off1+off2] {mergeSym(sym1,sym2)} x mem) // atomic intrinsics (AtomicLoad(8|32|64|Ptr) ptr mem) => (LoweredAtomicLoad(8|32|64|Ptr) [1] ptr mem) @@ -990,7 +805,6 @@ (AtomicStore(8|32|64) ptr val mem) => (LoweredAtomicStore(8|32|64) [1] ptr val mem) (AtomicStoreRel(32|64) ptr val mem) => (LoweredAtomicStore(32|64) [0] ptr val mem) -//(AtomicStorePtrNoWB ptr val mem) => (STLR ptr val mem) (AtomicExchange(32|64) ...) => (LoweredAtomicExchange(32|64) ...) @@ -999,10 +813,8 @@ (AtomicCompareAndSwap(32|64) ptr old new_ mem) => (LoweredAtomicCas(32|64) [1] ptr old new_ mem) (AtomicCompareAndSwapRel32 ptr old new_ mem) => (LoweredAtomicCas32 [0] ptr old new_ mem) -(AtomicAnd8 ...) => (LoweredAtomicAnd8 ...) -(AtomicAnd32 ...) => (LoweredAtomicAnd32 ...) -(AtomicOr8 ...) => (LoweredAtomicOr8 ...) -(AtomicOr32 ...) => (LoweredAtomicOr32 ...) +(AtomicAnd(8|32) ...) => (LoweredAtomicAnd(8|32) ...) +(AtomicOr(8|32) ...) => (LoweredAtomicOr(8|32) ...) (Slicemask x) => (SRADconst (NEG x) [63]) @@ -1014,10 +826,9 @@ (MOV(H|W)Zreg x:(MOVHZloadidx _ _ _)) => x (MOV(H|W)reg x:(MOVHload _ _)) => x (MOV(H|W)reg x:(MOVHloadidx _ _ _)) => x -(MOVWZreg x:(MOVWZload _ _)) => x -(MOVWZreg x:(MOVWZloadidx _ _ _)) => x -(MOVWreg x:(MOVWload _ _)) => x -(MOVWreg x:(MOVWloadidx _ _ _)) => x +(MOV(WZ|W)reg x:(MOV(WZ|W)load _ _)) => x +(MOV(WZ|W)reg x:(MOV(WZ|W)loadidx _ _ _)) => x +(MOV(B|W)Zreg x:(Select0 (LoweredAtomicLoad(8|32) _ _))) => x // don't extend if argument is already extended (MOVBreg x:(Arg )) && is8BitInt(t) && isSigned(t) => x @@ -1041,11 +852,11 @@ (SLDconst [c] z:(MOVHZreg x)) && c < 16 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,48,63,64)] x) (SLDconst [c] z:(MOVWZreg x)) && c < 32 && z.Uses == 1 => (CLRLSLDI [newPPC64ShiftAuxInt(c,32,63,64)] x) -(SLDconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c <= (64-getPPC64ShiftMaskLength(d)) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) +(SLDconst [c] z:(Select0 (ANDCCconst [d] x))) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c <= (64-getPPC64ShiftMaskLength(d)) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) (SLDconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c<=(64-getPPC64ShiftMaskLength(d)) => (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) (SLWconst [c] z:(MOVBZreg x)) && z.Uses == 1 && c < 8 => (CLRLSLWI [newPPC64ShiftAuxInt(c,24,31,32)] x) (SLWconst [c] z:(MOVHZreg x)) && z.Uses == 1 && c < 16 => (CLRLSLWI [newPPC64ShiftAuxInt(c,16,31,32)] x) -(SLWconst [c] z:(ANDconst [d] x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c<=(32-getPPC64ShiftMaskLength(d)) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) +(SLWconst [c] z:(Select0 (ANDCCconst [d] x))) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c<=(32-getPPC64ShiftMaskLength(d)) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) (SLWconst [c] z:(AND (MOVDconst [d]) x)) && z.Uses == 1 && isPPC64ValidShiftMask(d) && c<=(32-getPPC64ShiftMaskLength(d)) => (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) // special case for power9 (SL(W|D)const [c] z:(MOVWreg x)) && c < 32 && buildcfg.GOPPC64 >= 9 => (EXTSWSLconst [c] x) @@ -1065,10 +876,8 @@ (MOVWBRstore {sym} ptr (MOV(W|WZ)reg x) mem) => (MOVWBRstore {sym} ptr x mem) // Lose W-widening ops fed to compare-W -(CMPW x (MOVWreg y)) => (CMPW x y) -(CMPW (MOVWreg x) y) => (CMPW x y) -(CMPWU x (MOVWZreg y)) => (CMPWU x y) -(CMPWU (MOVWZreg x) y) => (CMPWU x y) +(CMP(W|WU) x (MOV(W|WZ)reg y)) => (CMP(W|WU) x y) +(CMP(W|WU) (MOV(W|WZ)reg x) y) => (CMP(W|WU) x y) (CMP x (MOVDconst [c])) && is16Bit(c) => (CMPconst x [c]) (CMP (MOVDconst [c]) y) && is16Bit(c) => (InvertFlags (CMPconst y [c])) @@ -1084,7 +893,7 @@ ((CMP|CMPW|CMPU|CMPWU) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x)) // ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1 -// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0 +// ISEL auxInt values 4=GE 5=LE 6=NE !arg2 ? arg1 : arg0 // ISELB special case where arg0, arg1 values are 0, 1 (Equal cmp) => (ISELB [2] (MOVDconst [1]) cmp) @@ -1129,26 +938,30 @@ (ISEL [4] x _ (Flag(EQ|GT))) => x (ISEL [4] _ y (FlagLT)) => y +(ISEL [2] x y ((CMP|CMPW)const [0] (Select0 (ANDCCconst [1] z)))) => (ISEL [2] x y (Select1 (ANDCCconst [1] z ))) +(ISEL [6] x y ((CMP|CMPW)const [0] (Select0 (ANDCCconst [1] z)))) => (ISEL [6] x y (Select1 (ANDCCconst [1] z ))) +(ISELB [2] x ((CMP|CMPW)const [0] (Select0 (ANDCCconst [1] z)))) => (XORconst [1] (Select0 (ANDCCconst [1] z ))) +(ISELB [6] x ((CMP|CMPW)const [0] (Select0 (ANDCCconst [1] z)))) => (Select0 (ANDCCconst [1] z )) + (ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 0 => (ISELB [n+1] (MOVDconst [1]) bool) (ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 1 => (ISELB [n-1] (MOVDconst [1]) bool) (ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 2 => (ISELB [n] (MOVDconst [1]) bool) (ISEL [n] x y (InvertFlags bool)) && n%4 == 0 => (ISEL [n+1] x y bool) (ISEL [n] x y (InvertFlags bool)) && n%4 == 1 => (ISEL [n-1] x y bool) (ISEL [n] x y (InvertFlags bool)) && n%4 == 2 => (ISEL [n] x y bool) +(XORconst [1] (ISELB [6] (MOVDconst [1]) cmp)) => (ISELB [2] (MOVDconst [1]) cmp) +(XORconst [1] (ISELB [5] (MOVDconst [1]) cmp)) => (ISELB [1] (MOVDconst [1]) cmp) +(XORconst [1] (ISELB [4] (MOVDconst [1]) cmp)) => (ISELB [0] (MOVDconst [1]) cmp) // A particular pattern seen in cgo code: -(AND (MOVDconst [c]) x:(MOVBZload _ _)) => (ANDconst [c&0xFF] x) +(AND (MOVDconst [c]) x:(MOVBZload _ _)) => (Select0 (ANDCCconst [c&0xFF] x)) // floating point negative abs -(FNEG (FABS x)) => (FNABS x) -(FNEG (FNABS x)) => (FABS x) +(FNEG (F(ABS|NABS) x)) => (F(NABS|ABS) x) // floating-point fused multiply-add/sub -(FADD (FMUL x y) z) => (FMADD x y z) -(FSUB (FMUL x y) z) => (FMSUB x y z) -(FADDS (FMULS x y) z) => (FMADDS x y z) -(FSUBS (FMULS x y) z) => (FMSUBS x y z) - +(F(ADD|SUB) (FMUL x y) z) => (FM(ADD|SUB) x y z) +(F(ADDS|SUBS) (FMULS x y) z) => (FM(ADDS|SUBS) x y z) // The following statements are found in encoding/binary functions UintXX (load) and PutUintXX (store) // and convert the statements in these functions from multiple single byte loads or stores to @@ -1450,3 +1263,30 @@ && i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && i4 == i0+4 && i5 == i0+5 && i6 == i0+6 && i7 == i0+7 && clobber(x0, x1, x2, x3, x4, x5, x6) => (MOVDBRstore (MOVDaddr [i0] {s} p) w mem) + +// Arch-specific inlining for small or disjoint runtime.memmove +(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem))))) + && sz >= 0 + && isSameCall(sym, "runtime.memmove") + && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 + && isInlinableMemmove(dst, src, sz, config) + && clobber(s1, s2, s3, call) + => (Move [sz] dst src mem) + +// Match post-lowering calls, register version. +(SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem)) + && sz >= 0 + && isSameCall(sym, "runtime.memmove") + && call.Uses == 1 + && isInlinableMemmove(dst, src, sz, config) + && clobber(call) + => (Move [sz] dst src mem) + +// Prefetch instructions (TH specified using aux field) +// For DCBT Ra,Rb,TH, A value of TH indicates: +// 0, hint this cache line will be used soon. (PrefetchCache) +// 16, hint this cache line will not be used for long. (PrefetchCacheStreamed) +// See ISA 3.0 Book II 4.3.2 for more detail. https://openpower.foundation/specifications/isa/ +(PrefetchCache ptr mem) => (DCBT ptr mem [0]) +(PrefetchCacheStreamed ptr mem) => (DCBT ptr mem [16]) + diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index f7198b90c32e48..5e0ed6f3b5ae48 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -81,7 +82,9 @@ var regNamesPPC64 = []string{ "F28", "F29", "F30", - "F31", + // "F31", the allocator is limited to 64 entries. We sacrifice this FPR to support XER. + + "XER", // If you add registers, update asyncPreempt in runtime. @@ -95,7 +98,6 @@ var regNamesPPC64 = []string{ // "CR7", // "CR", - // "XER", // "LR", // "CTR", } @@ -122,11 +124,12 @@ func init() { } var ( - gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29") - fp = buildReg("F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26") - sp = buildReg("SP") - sb = buildReg("SB") - gr = buildReg("g") + gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29") + fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30") + sp = buildReg("SP") + sb = buildReg("SB") + gr = buildReg("g") + xer = buildReg("XER") // cr = buildReg("CR") // ctr = buildReg("CTR") // lr = buildReg("LR") @@ -136,11 +139,16 @@ func init() { // tls = buildReg("R13") gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} gp11 = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}} + xergp = regInfo{inputs: []regMask{xer}, outputs: []regMask{gp}, clobbers: xer} + gp11cxer = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}, clobbers: xer} + gp11xer = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp, xer}} gp21 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}} gp21a0 = regInfo{inputs: []regMask{gp, gp | sp | sb}, outputs: []regMask{gp}} + gp21cxer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}, clobbers: xer} + gp21xer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, xer}, clobbers: xer} + gp2xer1xer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, xer}, outputs: []regMask{gp, xer}, clobbers: xer} gp31 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}} gp22 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, gp}} - gp32 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, gp}} gp1cr = regInfo{inputs: []regMask{gp | sp | sb}} gp2cr = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}} crgp = regInfo{inputs: nil, outputs: []regMask{gp}} @@ -148,6 +156,7 @@ func init() { crgp21 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}} gpload = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}} gploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp}, outputs: []regMask{gp}} + prefreg = regInfo{inputs: []regMask{gp | sp | sb}} gpstore = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}} gpstoreidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}} gpstorezero = regInfo{inputs: []regMask{gp | sp | sb}} // ppc64.REGZERO is reserved zero value @@ -164,21 +173,21 @@ func init() { fploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{fp}} fpstore = regInfo{inputs: []regMask{gp | sp | sb, fp}} fpstoreidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, fp}} - callerSave = regMask(gp | fp | gr) + callerSave = regMask(gp | fp | gr | xer) r3 = buildReg("R3") r4 = buildReg("R4") r5 = buildReg("R5") r6 = buildReg("R6") ) ops := []opData{ - {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 - {name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "Int64"}, // arg0 + auxInt - {name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true}, // arg0+arg1 - {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1 - {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0-arg1 - {name: "SUBFCconst", argLength: 1, reg: gp11, asm: "SUBC", aux: "Int64"}, // auxInt - arg0 (with carry) - {name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"}, // arg0-arg1 - {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0-arg1 + {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 + {name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "Int64"}, // arg0 + auxInt + {name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true}, // arg0+arg1 + {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1 + {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0-arg1 + {name: "SUBFCconst", argLength: 1, reg: gp11cxer, asm: "SUBC", aux: "Int64"}, // auxInt - arg0 (carry is ignored) + {name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"}, // arg0-arg1 + {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0-arg1 {name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit) {name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit) @@ -200,12 +209,12 @@ func init() { {name: "FMSUB", argLength: 3, reg: fp31, asm: "FMSUB"}, // arg0*arg1 - arg2 {name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS"}, // arg0*arg1 - arg2 - {name: "SRAD", argLength: 2, reg: gp21, asm: "SRAD"}, // signed arg0 >> (arg1&127), 64 bit width (note: 127, not 63!) - {name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // signed arg0 >> (arg1&63), 32 bit width - {name: "SRD", argLength: 2, reg: gp21, asm: "SRD"}, // unsigned arg0 >> (arg1&127), 64 bit width - {name: "SRW", argLength: 2, reg: gp21, asm: "SRW"}, // unsigned arg0 >> (arg1&63), 32 bit width - {name: "SLD", argLength: 2, reg: gp21, asm: "SLD"}, // arg0 << (arg1&127), 64 bit width - {name: "SLW", argLength: 2, reg: gp21, asm: "SLW"}, // arg0 << (arg1&63), 32 bit width + {name: "SRAD", argLength: 2, reg: gp21cxer, asm: "SRAD"}, // signed arg0 >> (arg1&127), 64 bit width (note: 127, not 63!) + {name: "SRAW", argLength: 2, reg: gp21cxer, asm: "SRAW"}, // signed arg0 >> (arg1&63), 32 bit width + {name: "SRD", argLength: 2, reg: gp21, asm: "SRD"}, // unsigned arg0 >> (arg1&127), 64 bit width + {name: "SRW", argLength: 2, reg: gp21, asm: "SRW"}, // unsigned arg0 >> (arg1&63), 32 bit width + {name: "SLD", argLength: 2, reg: gp21, asm: "SLD"}, // arg0 << (arg1&127), 64 bit width + {name: "SLW", argLength: 2, reg: gp21, asm: "SLW"}, // arg0 << (arg1&63), 32 bit width {name: "ROTL", argLength: 2, reg: gp21, asm: "ROTL"}, // arg0 rotate left by arg1 mod 64 {name: "ROTLW", argLength: 2, reg: gp21, asm: "ROTLW"}, // uint32(arg0) rotate left by arg1 mod 32 @@ -215,14 +224,22 @@ func init() { {name: "CLRLSLWI", argLength: 1, reg: gp11, asm: "CLRLSLWI", aux: "Int32"}, // {name: "CLRLSLDI", argLength: 1, reg: gp11, asm: "CLRLSLDI", aux: "Int32"}, // - {name: "LoweredAdd64Carry", argLength: 3, reg: gp32, resultNotInArgs: true}, // arg0 + arg1 + carry, returns (sum, carry) - - {name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width - {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width - {name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 32, 32 bit width + // Operations which consume or generate the CA (xer) + {name: "ADDC", argLength: 2, reg: gp21xer, asm: "ADDC", commutative: true, typ: "(UInt64, UInt64)"}, // arg0 + arg1 -> out, CA + {name: "SUBC", argLength: 2, reg: gp21xer, asm: "SUBC", typ: "(UInt64, UInt64)"}, // arg0 - arg1 -> out, CA + {name: "ADDCconst", argLength: 1, reg: gp11xer, asm: "ADDC", typ: "(UInt64, UInt64)", aux: "Int64"}, // arg0 + imm16 -> out, CA + {name: "SUBCconst", argLength: 1, reg: gp11xer, asm: "SUBC", typ: "(UInt64, UInt64)", aux: "Int64"}, // imm16 - arg0 -> out, CA + {name: "ADDE", argLength: 3, reg: gp2xer1xer, asm: "ADDE", typ: "(UInt64, UInt64)", commutative: true}, // arg0 + arg1 + CA (arg2) -> out, CA + {name: "SUBE", argLength: 3, reg: gp2xer1xer, asm: "SUBE", typ: "(UInt64, UInt64)"}, // arg0 - arg1 - CA (arg2) -> out, CA + {name: "ADDZEzero", argLength: 1, reg: xergp, asm: "ADDZE", typ: "UInt64"}, // CA (arg0) + $0 -> out + {name: "SUBZEzero", argLength: 1, reg: xergp, asm: "SUBZE", typ: "UInt64"}, // $0 - CA (arg0) -> out + + {name: "SRADconst", argLength: 1, reg: gp11cxer, asm: "SRAD", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SRAWconst", argLength: 1, reg: gp11cxer, asm: "SRAW", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width + {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width + {name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 32, 32 bit width {name: "ROTLconst", argLength: 1, reg: gp11, asm: "ROTL", aux: "Int64"}, // arg0 rotate left by auxInt bits {name: "ROTLWconst", argLength: 1, reg: gp11, asm: "ROTLW", aux: "Int64"}, // uint32(arg0) rotate left by auxInt bits @@ -294,10 +311,9 @@ func init() { {name: "FNABS", argLength: 1, reg: fp11, asm: "FNABS"}, // -abs(arg0), float64 {name: "FCPSGN", argLength: 2, reg: fp21, asm: "FCPSGN"}, // copysign arg0 -> arg1, float64 - {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0|aux - {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64"}, // arg0^aux - {name: "ANDconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}, asm: "ANDCC", aux: "Int64", clobberFlags: true}, // arg0&aux // and-immediate sets CC on PPC, always. - {name: "ANDCCconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}}, asm: "ANDCC", aux: "Int64", typ: "Flags"}, // arg0&aux == 0 // and-immediate sets CC on PPC, always. + {name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"}, // arg0|aux + {name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64"}, // arg0^aux + {name: "ANDCCconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}, asm: "ANDCC", aux: "Int64", clobberFlags: true, typ: "(Int,Flags)"}, // arg0&aux == 0 // and-immediate sets CC on PPC, always. {name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", typ: "Int64"}, // sign extend int8 to int64 {name: "MOVBZreg", argLength: 1, reg: gp11, asm: "MOVBZ", typ: "Int64"}, // zero extend uint8 to uint64 @@ -335,6 +351,10 @@ func init() { {name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", typ: "Float64"}, {name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", typ: "Float32"}, + // Prefetch instruction + // Do prefetch of address generated with arg0 and arg1 with option aux. arg0=addr,arg1=memory, aux=option. + {name: "DCBT", argLength: 2, aux: "Int64", reg: prefreg, asm: "DCBT", hasSideEffects: true}, + // Store bytes in the reverse endian order of the arch into arg0. // These are indexed stores with no offset field in the instruction so the auxint fields are not used. {name: "MOVDBRstore", argLength: 3, reg: gpstore, asm: "MOVDBR", aux: "Sym", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes reverse order @@ -390,7 +410,7 @@ func init() { {name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"}, // ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1 - // ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0 + // ISEL auxInt values 4=GE 5=LE 6=NE !arg2 ? arg1 : arg0 // ISELB special case where arg0, arg1 values are 0, 1 for boolean result {name: "ISEL", argLength: 3, reg: crgp21, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above {name: "ISELB", argLength: 2, reg: crgp11, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above @@ -427,9 +447,10 @@ func init() { {name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem // large or unaligned zeroing // arg0 = address of memory to zero (in R3, changed as side effect) @@ -661,6 +682,7 @@ func init() { // but may clobber anything else, including R31 (REGTMP). {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ buildReg("R0 R3 R4 R5 R6 R7 R8 R9 R10 R14 R15 R16 R17 R20 R21 g")) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"}, + {name: "LoweredPubBarrier", argLength: 1, asm: "LWSYNC", hasSideEffects: true}, // Do data barrier. arg0=memory // There are three of these functions so that they can have three different register inputs. // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the // default registers to match so we don't need to copy registers around unnecessarily. @@ -703,15 +725,18 @@ func init() { } archs = append(archs, arch{ - name: "PPC64", - pkg: "cmd/internal/obj/ppc64", - genfile: "../../ppc64/ssa.go", - ops: ops, - blocks: blocks, - regnames: regNamesPPC64, - gpregmask: gp, - fpregmask: fp, - framepointerreg: int8(num["SP"]), - linkreg: -1, // not used + name: "PPC64", + pkg: "cmd/internal/obj/ppc64", + genfile: "../../ppc64/ssa.go", + ops: ops, + blocks: blocks, + regnames: regNamesPPC64, + ParamIntRegNames: "R3 R4 R5 R6 R7 R8 R9 R10 R14 R15 R16 R17", + ParamFloatRegNames: "F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12", + gpregmask: gp, + fpregmask: fp, + specialregmask: xer, + framepointerreg: -1, + linkreg: -1, // not used }) } diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules index 9cdd62edbe01c0..fd5bfd36c6d8bb 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules @@ -6,9 +6,7 @@ // * Use SLTI and SLTIU for comparisons to constants, instead of SLT/SLTU with constants in registers // * Use the zero register instead of moving 0 into a register. // * Add rules to avoid generating a temp bool value for (If (SLT[U] ...) ...). -// * Optimize left and right shift by simplifying SLTIU, Neg, and ADD for constants. // * Arrange for non-trivial Zero and Move lowerings to use aligned loads and stores. -// * Eliminate zero immediate shifts, adds, etc. // * Avoid using Neq32 for writeBarrier.enabled checks. // Lowering arithmetic @@ -29,6 +27,8 @@ (Sub64F ...) => (FSUBD ...) (Mul64 ...) => (MUL ...) +(Mul64uhilo ...) => (LoweredMuluhilo ...) +(Mul64uover ...) => (LoweredMuluover ...) (Mul32 ...) => (MULW ...) (Mul16 x y) => (MULW (SignExt16to32 x) (SignExt16to32 y)) (Mul8 x y) => (MULW (SignExt8to32 x) (SignExt8to32 y)) @@ -52,6 +52,14 @@ (Hmul32 x y) => (SRAI [32] (MUL (SignExt32to64 x) (SignExt32to64 y))) (Hmul32u x y) => (SRLI [32] (MUL (ZeroExt32to64 x) (ZeroExt32to64 y))) +(Select0 (Add64carry x y c)) => (ADD (ADD x y) c) +(Select1 (Add64carry x y c)) => + (OR (SLTU s:(ADD x y) x) (SLTU (ADD s c) s)) + +(Select0 (Sub64borrow x y c)) => (SUB (SUB x y) c) +(Select1 (Sub64borrow x y c)) => + (OR (SLTU x s:(SUB x y)) (SLTU s (SUB s c))) + // (x + y) / 2 => (x / 2) + (y / 2) + (x & y & 1) (Avg64u x y) => (ADD (ADD (SRLI [1] x) (SRLI [1] y)) (ANDI [1] (AND x y))) @@ -94,6 +102,12 @@ (Sqrt ...) => (FSQRTD ...) (Sqrt32 ...) => (FSQRTS ...) +(Copysign ...) => (FSGNJD ...) + +(Abs ...) => (FABSD ...) + +(FMA ...) => (FMADDD ...) + // Sign and zero extension. (SignExt8to16 ...) => (MOVBreg ...) @@ -128,14 +142,7 @@ (Round32F ...) => (Copy ...) (Round64F ...) => (Copy ...) -// From genericOps.go: -// "0 if arg0 == 0, -1 if arg0 > 0, undef if arg0<0" -// -// Like other arches, we compute ~((x-1) >> 63), with arithmetic right shift. -// For positive x, bit 63 of x-1 is always 0, so the result is -1. -// For zero x, bit 63 of x-1 is 1, so the result is 0. -// -(Slicemask x) => (NOT (SRAI [63] (ADDI [-1] x))) +(Slicemask x) => (SRAI [63] (NEG x)) // Truncations // We ignore the unused high parts of registers, so truncates are just copies. @@ -158,41 +165,51 @@ // If y < 64, this is the value we want. Otherwise, we want zero. // // So, we AND with -1 * uint64(y < 64), which is 0xfffff... if y < 64 and 0 otherwise. -(Lsh8x8 x y) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) -(Lsh8x16 x y) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) -(Lsh8x32 x y) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) -(Lsh8x64 x y) => (AND (SLL x y) (Neg8 (SLTIU [64] y))) -(Lsh16x8 x y) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) -(Lsh16x16 x y) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) -(Lsh16x32 x y) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) -(Lsh16x64 x y) => (AND (SLL x y) (Neg16 (SLTIU [64] y))) -(Lsh32x8 x y) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) -(Lsh32x16 x y) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) -(Lsh32x32 x y) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) -(Lsh32x64 x y) => (AND (SLL x y) (Neg32 (SLTIU [64] y))) -(Lsh64x8 x y) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) -(Lsh64x16 x y) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) -(Lsh64x32 x y) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) -(Lsh64x64 x y) => (AND (SLL x y) (Neg64 (SLTIU [64] y))) +(Lsh8x8 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) +(Lsh8x16 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) +(Lsh8x32 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) +(Lsh8x64 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg8 (SLTIU [64] y))) +(Lsh16x8 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) +(Lsh16x16 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) +(Lsh16x32 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) +(Lsh16x64 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg16 (SLTIU [64] y))) +(Lsh32x8 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) +(Lsh32x16 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) +(Lsh32x32 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) +(Lsh32x64 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg32 (SLTIU [64] y))) +(Lsh64x8 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) +(Lsh64x16 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) +(Lsh64x32 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) +(Lsh64x64 x y) && !shiftIsBounded(v) => (AND (SLL x y) (Neg64 (SLTIU [64] y))) + +(Lsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y) +(Lsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y) +(Lsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y) +(Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y) // SRL only considers the bottom 6 bits of y. If y > 64, the result should // always be 0. See Lsh above for a detailed description. -(Rsh8Ux8 x y) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh8Ux16 x y) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh8Ux32 x y) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh8Ux64 x y) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] y))) -(Rsh16Ux8 x y) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh16Ux16 x y) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh16Ux32 x y) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh16Ux64 x y) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] y))) -(Rsh32Ux8 x y) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh32Ux16 x y) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh32Ux32 x y) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh32Ux64 x y) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] y))) -(Rsh64Ux8 x y) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh64Ux16 x y) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh64Ux32 x y) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh64Ux64 x y) => (AND (SRL x y) (Neg64 (SLTIU [64] y))) +(Rsh8Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh8Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh8Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh8Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] y))) +(Rsh16Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh16Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh16Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh16Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] y))) +(Rsh32Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh32Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh32Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh32Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] y))) +(Rsh64Ux8 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh64Ux16 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh64Ux32 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh64Ux64 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] y))) + +(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt8to64 x) y) +(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt16to64 x) y) +(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt32to64 x) y) +(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL x y) // SRA only considers the bottom 6 bits of y. If y > 64, the result should // be either 0 or -1 based on the sign bit. @@ -204,24 +221,29 @@ // // We don't need to sign-extend the OR result, as it will be at minimum 8 bits, // more than the 6 bits SRA cares about. -(Rsh8x8 x y) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh8x16 x y) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh8x32 x y) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh8x64 x y) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh16x8 x y) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh16x16 x y) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh16x32 x y) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh16x64 x y) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh32x8 x y) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh32x16 x y) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh32x32 x y) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh32x64 x y) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh64x8 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh64x16 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh64x32 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh64x64 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] y)))) - -// rotates +(Rsh8x8 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh8x16 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh8x32 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh8x64 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh16x8 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh16x16 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh16x32 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh16x64 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh32x8 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh32x16 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh32x32 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh32x64 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh64x8 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh64x16 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh64x32 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh64x64 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] y)))) + +(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt8to64 x) y) +(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt16to64 x) y) +(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt32to64 x) y) +(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y) + +// Rotates. (RotateLeft8 x (MOVDconst [c])) => (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7]))) (RotateLeft16 x (MOVDconst [c])) => (Or16 (Lsh16x64 x (MOVDconst [c&15])) (Rsh16Ux64 x (MOVDconst [-c&15]))) (RotateLeft32 x (MOVDconst [c])) => (Or32 (Lsh32x64 x (MOVDconst [c&31])) (Rsh32Ux64 x (MOVDconst [-c&31]))) @@ -250,7 +272,7 @@ (Leq64F ...) => (FLED ...) (Leq32F ...) => (FLES ...) -(EqPtr x y) => (SEQZ (SUB x y)) +(EqPtr x y) => (SEQZ (SUB x y)) (Eq64 x y) => (SEQZ (SUB x y)) (Eq32 x y) => (SEQZ (SUB (ZeroExt32to64 x) (ZeroExt32to64 y))) (Eq16 x y) => (SEQZ (SUB (ZeroExt16to64 x) (ZeroExt16to64 y))) @@ -258,7 +280,7 @@ (Eq64F ...) => (FEQD ...) (Eq32F ...) => (FEQS ...) -(NeqPtr x y) => (SNEZ (SUB x y)) +(NeqPtr x y) => (SNEZ (SUB x y)) (Neq64 x y) => (SNEZ (SUB x y)) (Neq32 x y) => (SNEZ (SUB (ZeroExt32to64 x) (ZeroExt32to64 y))) (Neq16 x y) => (SNEZ (SUB (ZeroExt16to64 x) (ZeroExt16to64 y))) @@ -538,6 +560,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Atomic Intrinsics (AtomicLoad8 ...) => (LoweredAtomicLoad8 ...) @@ -576,7 +599,7 @@ (AtomicOr32 ...) => (LoweredAtomicOr32 ...) // Conditional branches -(If cond yes no) => (BNEZ cond yes no) +(If cond yes no) => (BNEZ (MOVBUreg cond) yes no) // Optimizations @@ -586,6 +609,16 @@ (BNEZ (SEQZ x) yes no) => (BEQZ x yes no) (BNEZ (SNEZ x) yes no) => (BNEZ x yes no) +// Remove redundant NEG from BEQZ/BNEZ. +(BEQZ (NEG x) yes no) => (BEQZ x yes no) +(BNEZ (NEG x) yes no) => (BNEZ x yes no) + +// Negate comparision with FNES/FNED. +(BEQZ (FNES x y) yes no) => (BNEZ (FEQS x y) yes no) +(BNEZ (FNES x y) yes no) => (BEQZ (FEQS x y) yes no) +(BEQZ (FNED x y) yes no) => (BNEZ (FEQD x y) yes no) +(BNEZ (FNED x y) yes no) => (BEQZ (FEQD x y) yes no) + // Convert BEQZ/BNEZ into more optimal branch conditions. (BEQZ (SUB x y) yes no) => (BEQ x y yes no) (BNEZ (SUB x y) yes no) => (BNE x y yes no) @@ -594,18 +627,46 @@ (BEQZ (SLTU x y) yes no) => (BGEU x y yes no) (BNEZ (SLTU x y) yes no) => (BLTU x y yes no) -// Convert branch with zero to BEQZ/BNEZ. +// Convert branch with zero to more optimal branch zero. (BEQ (MOVDconst [0]) cond yes no) => (BEQZ cond yes no) (BEQ cond (MOVDconst [0]) yes no) => (BEQZ cond yes no) (BNE (MOVDconst [0]) cond yes no) => (BNEZ cond yes no) (BNE cond (MOVDconst [0]) yes no) => (BNEZ cond yes no) - -// Store zero +(BLT (MOVDconst [0]) cond yes no) => (BGTZ cond yes no) +(BLT cond (MOVDconst [0]) yes no) => (BLTZ cond yes no) +(BGE (MOVDconst [0]) cond yes no) => (BLEZ cond yes no) +(BGE cond (MOVDconst [0]) yes no) => (BGEZ cond yes no) + +// Remove redundant NEG from SEQZ/SNEZ. +(SEQZ (NEG x)) => (SEQZ x) +(SNEZ (NEG x)) => (SNEZ x) + +// Remove redundant SEQZ/SNEZ. +(SEQZ (SEQZ x)) => (SNEZ x) +(SEQZ (SNEZ x)) => (SEQZ x) +(SNEZ (SEQZ x)) => (SEQZ x) +(SNEZ (SNEZ x)) => (SNEZ x) + +// Store zero. (MOVBstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem) (MOVHstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVHstorezero [off] {sym} ptr mem) (MOVWstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVWstorezero [off] {sym} ptr mem) (MOVDstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVDstorezero [off] {sym} ptr mem) +// Boolean ops are already extended. +(MOVBUreg x:((FLES|FLTS|FEQS|FNES) _ _)) => x +(MOVBUreg x:((FLED|FLTD|FEQD|FNED) _ _)) => x +(MOVBUreg x:((SEQZ|SNEZ) _)) => x +(MOVBUreg x:((SLT|SLTU) _ _)) => x + +// Avoid extending when already sufficiently masked. +(MOVBreg x:(ANDI [c] y)) && c >= 0 && int64(int8(c)) == c => x +(MOVHreg x:(ANDI [c] y)) && c >= 0 && int64(int16(c)) == c => x +(MOVWreg x:(ANDI [c] y)) && c >= 0 && int64(int32(c)) == c => x +(MOVBUreg x:(ANDI [c] y)) && c >= 0 && int64(uint8(c)) == c => x +(MOVHUreg x:(ANDI [c] y)) && c >= 0 && int64(uint16(c)) == c => x +(MOVWUreg x:(ANDI [c] y)) && c >= 0 && int64(uint32(c)) == c => x + // Avoid sign/zero extension for consts. (MOVBreg (MOVDconst [c])) => (MOVDconst [int64(int8(c))]) (MOVHreg (MOVDconst [c])) => (MOVDconst [int64(int16(c))]) @@ -631,6 +692,11 @@ (MOVWUreg x:(MOVHUload _ _)) => (MOVDreg x) (MOVWUreg x:(MOVWUload _ _)) => (MOVDreg x) +// Avoid zero extension after properly typed atomic operation. +(MOVBUreg x:(Select0 (LoweredAtomicLoad8 _ _))) => (MOVDreg x) +(MOVBUreg x:(Select0 (LoweredAtomicCas32 _ _ _ _))) => (MOVDreg x) +(MOVBUreg x:(Select0 (LoweredAtomicCas64 _ _ _ _))) => (MOVDreg x) + // Fold double extensions. (MOVBreg x:(MOVBreg _)) => (MOVDreg x) (MOVHreg x:(MOVBreg _)) => (MOVDreg x) @@ -686,20 +752,61 @@ (SRL x (MOVDconst [val])) => (SRLI [int64(val&63)] x) (SRA x (MOVDconst [val])) => (SRAI [int64(val&63)] x) -// Convert subtraction of a const into ADDI with negative immediate, where possible. +// Convert const subtraction into ADDI with negative immediate, where possible. (SUB x (MOVDconst [val])) && is32Bit(-val) => (ADDI [-val] x) +(SUB (MOVDconst [val]) y) && is32Bit(-val) => (NEG (ADDI [-val] y)) // Subtraction of zero. -(SUB x (MOVDconst [0])) => x - -// Subtraction of zero with sign extension. +(SUB x (MOVDconst [0])) => x (SUBW x (MOVDconst [0])) => (ADDIW [0] x) // Subtraction from zero. -(SUB (MOVDconst [0]) x) => (NEG x) - -// Subtraction from zero with sign extension. +(SUB (MOVDconst [0]) x) => (NEG x) (SUBW (MOVDconst [0]) x) => (NEGW x) -// Addition of zero. +// Addition of zero or two constants. (ADDI [0] x) => x +(ADDI [x] (MOVDconst [y])) && is32Bit(x + y) => (MOVDconst [x + y]) + +// ANDI with all zeros, all ones or two constants. +(ANDI [0] x) => (MOVDconst [0]) +(ANDI [-1] x) => x +(ANDI [x] (MOVDconst [y])) => (MOVDconst [x & y]) + +// ORI with all zeroes, all ones or two constants. +(ORI [0] x) => x +(ORI [-1] x) => (MOVDconst [-1]) +(ORI [x] (MOVDconst [y])) => (MOVDconst [x | y]) + +// Negation of a constant. +(NEG (MOVDconst [x])) => (MOVDconst [-x]) +(NEGW (MOVDconst [x])) => (MOVDconst [int64(int32(-x))]) + +// Shift of a constant. +(SLLI [x] (MOVDconst [y])) && is32Bit(y << uint32(x)) => (MOVDconst [y << uint32(x)]) +(SRLI [x] (MOVDconst [y])) => (MOVDconst [int64(uint64(y) >> uint32(x))]) +(SRAI [x] (MOVDconst [y])) => (MOVDconst [int64(y) >> uint32(x)]) + +// SLTI/SLTIU with constants. +(SLTI [x] (MOVDconst [y])) => (MOVDconst [b2i(int64(y) < int64(x))]) +(SLTIU [x] (MOVDconst [y])) => (MOVDconst [b2i(uint64(y) < uint64(x))]) + +(SLT x x) => (MOVDconst [0]) +(SLTU x x) => (MOVDconst [0]) + +// deadcode for LoweredMuluhilo +(Select0 m:(LoweredMuluhilo x y)) && m.Uses == 1 => (MULHU x y) +(Select1 m:(LoweredMuluhilo x y)) && m.Uses == 1 => (MUL x y) + +// Merge negation into fused multiply-add and multiply-subtract. +// +// Key: +// +// [+ -](x * y) [+ -] z. +// _ N A S +// D U +// D B +// +// Note: multiplication commutativity handled by rule generator. +(F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMADD|MADD|NMSUB|MSUB)D x y z) +(F(MADD|NMADD|MSUB|NMSUB)D x y neg:(FNEGD z)) && neg.Uses == 1 => (F(MSUB|NMSUB|MADD|NMADD)D x y z) diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index 0ac9c5f62ad176..f09910782d5e24 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -25,9 +26,10 @@ import ( const ( riscv64REG_G = 27 - riscv64REG_CTXT = 20 + riscv64REG_CTXT = 26 riscv64REG_LR = 1 riscv64REG_SP = 2 + riscv64REG_GP = 3 riscv64REG_TP = 4 riscv64REG_TMP = 31 riscv64REG_ZERO = 0 @@ -79,8 +81,8 @@ func init() { // Add general purpose registers to gpMask. switch r { - // ZERO, TP and TMP are not in any gp mask. - case riscv64REG_ZERO, riscv64REG_TP, riscv64REG_TMP: + // ZERO, GP, TP and TMP are not in any gp mask. + case riscv64REG_ZERO, riscv64REG_GP, riscv64REG_TP, riscv64REG_TMP: case riscv64REG_G: gpgMask |= mask gpspsbgMask |= mask @@ -113,7 +115,7 @@ func init() { panic("Too many RISCV64 registers") } - regCtxt := regNamed["X20"] + regCtxt := regNamed["X26"] callerSave := gpMask | fpMask | regNamed["g"] var ( @@ -122,6 +124,7 @@ func init() { gp01 = regInfo{outputs: []regMask{gpMask}} gp11 = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}} gp21 = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask}} + gp22 = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask, gpMask}} gpload = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{gpMask}} gp11sb = regInfo{inputs: []regMask{gpspsbMask}, outputs: []regMask{gpMask}} gpxchg = regInfo{inputs: []regMask{gpspsbgMask, gpgMask}, outputs: []regMask{gpMask}} @@ -130,6 +133,7 @@ func init() { fp11 = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{fpMask}} fp21 = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{fpMask}} + fp31 = regInfo{inputs: []regMask{fpMask, fpMask, fpMask}, outputs: []regMask{fpMask}} gpfp = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{fpMask}} fpgp = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{gpMask}} fpstore = regInfo{inputs: []regMask{gpspsbMask, fpMask, 0}} @@ -156,6 +160,9 @@ func init() { {name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, typ: "Int32"}, {name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int64"}, {name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt64"}, + {name: "LoweredMuluhilo", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (hi, lo) + {name: "LoweredMuluover", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (64 bits of arg0*arg1, overflow) + {name: "DIV", argLength: 2, reg: gp21, asm: "DIV", typ: "Int64"}, // arg0 / arg1 {name: "DIVU", argLength: 2, reg: gp21, asm: "DIVU", typ: "UInt64"}, {name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"}, @@ -234,12 +241,13 @@ func init() { {name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem // Calls - {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: -1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: call, aux: "CallOff", call: true, tailCall: true}, // tail call static function aux.(*gc.Sym). last arg=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: -1, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: -1, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem // duffzero - // arg0 = address of memory to zero (in X10, changed as side effect) + // arg0 = address of memory to zero (in X25, changed as side effect) // arg1 = mem // auxint = offset into duffzero code to start executing // X1 (link register) changed because of function call @@ -249,16 +257,16 @@ func init() { aux: "Int64", argLength: 2, reg: regInfo{ - inputs: []regMask{regNamed["X10"]}, - clobbers: regNamed["X1"] | regNamed["X10"], + inputs: []regMask{regNamed["X25"]}, + clobbers: regNamed["X1"] | regNamed["X25"], }, typ: "Mem", faultOnNilArg0: true, }, // duffcopy - // arg0 = address of dst memory (in X11, changed as side effect) - // arg1 = address of src memory (in X10, changed as side effect) + // arg0 = address of dst memory (in X25, changed as side effect) + // arg1 = address of src memory (in X24, changed as side effect) // arg2 = mem // auxint = offset into duffcopy code to start executing // X1 (link register) changed because of function call @@ -268,8 +276,8 @@ func init() { aux: "Int64", argLength: 3, reg: regInfo{ - inputs: []regMask{regNamed["X11"], regNamed["X10"]}, - clobbers: regNamed["X1"] | regNamed["X10"] | regNamed["X11"], + inputs: []regMask{regNamed["X25"], regNamed["X24"]}, + clobbers: regNamed["X1"] | regNamed["X24"] | regNamed["X25"], }, typ: "Mem", faultOnNilArg0: true, @@ -420,8 +428,14 @@ func init() { {name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", commutative: false, typ: "Float64"}, // arg0 - arg1 {name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, typ: "Float64"}, // arg0 * arg1 {name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", commutative: false, typ: "Float64"}, // arg0 / arg1 + {name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", commutative: true, typ: "Float64"}, // (arg0 * arg1) + arg2 + {name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD", commutative: true, typ: "Float64"}, // (arg0 * arg1) - arg2 + {name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD", commutative: true, typ: "Float64"}, // -(arg0 * arg1) + arg2 + {name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64"}, // -(arg0 * arg1) - arg2 {name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"}, // sqrt(arg0) {name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"}, // -arg0 + {name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", typ: "Float64"}, // abs(arg0) + {name: "FSGNJD", argLength: 2, reg: fp21, asm: "FSGNJD", typ: "Float64"}, // copy sign of arg1 to arg0 {name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"}, // reinterpret arg0 as float {name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"}, // float64(low 32 bits of arg0) {name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"}, // float64(arg0) @@ -463,5 +477,9 @@ func init() { gpregmask: gpMask, fpregmask: fpMask, framepointerreg: -1, // not used + // Integer parameters passed in register X10-X17, X8-X9, X18-X23 + ParamIntRegNames: "X10 X11 X12 X13 X14 X15 X16 X17 X8 X9 X18 X19 X20 X21 X22 X23", + // Float parameters passed in register F10-F17, F8-F9, F18-F23 + ParamFloatRegNames: "F10 F11 F12 F13 F14 F15 F16 F17 F8 F9 F18 F19 F20 F21 F22 F23", }) } diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules index 88762f70458906..8c48d6f6012b46 100644 --- a/src/cmd/compile/internal/ssa/gen/S390X.rules +++ b/src/cmd/compile/internal/ssa/gen/S390X.rules @@ -434,6 +434,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (IsNonNil p) => (LOCGR {s390x.NotEqual} (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0])) @@ -690,10 +691,6 @@ (RLLG x (MOVDconst [c])) => (RISBGZ x {s390x.NewRotateParams(0, 63, uint8(c&63))}) (RLL x (MOVDconst [c])) => (RLLconst x [uint8(c&31)]) -// Match rotate by constant pattern. -((ADD|OR|XOR) (SLDconst x [c]) (SRDconst x [64-c])) => (RISBGZ x {s390x.NewRotateParams(0, 63, c)}) -((ADD|OR|XOR)W (SLWconst x [c]) (SRWconst x [32-c])) => (RLLconst x [c]) - // Signed 64-bit comparison with immediate. (CMP x (MOVDconst [c])) && is32Bit(c) => (CMPconst x [int32(c)]) (CMP (MOVDconst [c]) x) && is32Bit(c) => (InvertFlags (CMPconst x [int32(c)])) diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index 5b33ba710e9818..eef8a2557ca39d 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -479,6 +480,7 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem @@ -507,8 +509,9 @@ func init() { // LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier // It saves all GP registers if necessary, - // but clobbers R14 (LR) because it's a call. - {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"}, + // but clobbers R14 (LR) because it's a call, + // and also clobbers R1 as the PLT stub does. + {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14") | r1}, clobberFlags: true, aux: "Sym", symEffect: "None"}, // There are three of these functions so that they can have three different register inputs. // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the diff --git a/src/cmd/compile/internal/ssa/gen/Wasm.rules b/src/cmd/compile/internal/ssa/gen/Wasm.rules index 7ad3d1c72e1fb0..9e683b116c1613 100644 --- a/src/cmd/compile/internal/ssa/gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/gen/Wasm.rules @@ -307,6 +307,7 @@ (StaticCall ...) => (LoweredStaticCall ...) (ClosureCall ...) => (LoweredClosureCall ...) (InterCall ...) => (LoweredInterCall ...) +(TailCall ...) => (LoweredTailCall ...) // Miscellaneous (Convert ...) => (LoweredConvert ...) diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index c92878ca73b844..edfba4ee99b774 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main @@ -123,6 +124,7 @@ func init() { var WasmOps = []opData{ {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredTailCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/dec64.rules b/src/cmd/compile/internal/ssa/gen/dec64.rules index b0f10d0a0f4e9f..ba776af1a705c6 100644 --- a/src/cmd/compile/internal/ssa/gen/dec64.rules +++ b/src/cmd/compile/internal/ssa/gen/dec64.rules @@ -217,6 +217,11 @@ (Rsh8x64 x y) => (Rsh8x32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) (Rsh8Ux64 x y) => (Rsh8Ux32 x (Or32 (Zeromask (Int64Hi y)) (Int64Lo y))) +(RotateLeft64 x (Int64Make hi lo)) => (RotateLeft64 x lo) +(RotateLeft32 x (Int64Make hi lo)) => (RotateLeft32 x lo) +(RotateLeft16 x (Int64Make hi lo)) => (RotateLeft16 x lo) +(RotateLeft8 x (Int64Make hi lo)) => (RotateLeft8 x lo) + // Clean up constants a little (Or32 (Zeromask (Const32 [c])) y) && c == 0 => y (Or32 (Zeromask (Const32 [c])) y) && c != 0 => (Const32 [-1]) diff --git a/src/cmd/compile/internal/ssa/gen/dec64Ops.go b/src/cmd/compile/internal/ssa/gen/dec64Ops.go index 8c5883bc569134..78fcea885aa8b2 100644 --- a/src/cmd/compile/internal/ssa/gen/dec64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/dec64Ops.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main diff --git a/src/cmd/compile/internal/ssa/gen/decOps.go b/src/cmd/compile/internal/ssa/gen/decOps.go index b826481c9fbb2f..d5cd79378c76a3 100644 --- a/src/cmd/compile/internal/ssa/gen/decOps.go +++ b/src/cmd/compile/internal/ssa/gen/decOps.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore package main diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index 5cbc70cf41d1c6..668164949f8732 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -163,8 +163,15 @@ (Not (ConstBool [c])) => (ConstBool [!c]) +(Floor (Const64F [c])) => (Const64F [math.Floor(c)]) +(Ceil (Const64F [c])) => (Const64F [math.Ceil(c)]) +(Trunc (Const64F [c])) => (Const64F [math.Trunc(c)]) +(RoundToEven (Const64F [c])) => (Const64F [math.RoundToEven(c)]) + // Convert x * 1 to x. (Mul(8|16|32|64) (Const(8|16|32|64) [1]) x) => x +(Select0 (Mul(32|64)uover (Const(32|64) [1]) x)) => x +(Select1 (Mul(32|64)uover (Const(32|64) [1]) x)) => (ConstBool [false]) // Convert x * -1 to -x. (Mul(8|16|32|64) (Const(8|16|32|64) [-1]) x) => (Neg(8|16|32|64) x) @@ -436,6 +443,17 @@ (Rsh32Ux64 (Rsh32x64 x _) (Const64 [31])) => (Rsh32Ux64 x (Const64 [31])) (Rsh64Ux64 (Rsh64x64 x _) (Const64 [63])) => (Rsh64Ux64 x (Const64 [63])) +// Convert x>>c<= 0 && c < 64 && i.Uses == 1 => (And64 x (Const64 [int64(-1) << c])) +(Lsh32x64 i:(Rsh(32|32U)x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 32 && i.Uses == 1 => (And32 x (Const32 [int32(-1) << c])) +(Lsh16x64 i:(Rsh(16|16U)x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 16 && i.Uses == 1 => (And16 x (Const16 [int16(-1) << c])) +(Lsh8x64 i:(Rsh(8|8U)x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 8 && i.Uses == 1 => (And8 x (Const8 [int8(-1) << c])) +// similarly for x<>c +(Rsh64Ux64 i:(Lsh64x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 64 && i.Uses == 1 => (And64 x (Const64 [int64(^uint64(0)>>c)])) +(Rsh32Ux64 i:(Lsh32x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 32 && i.Uses == 1 => (And32 x (Const32 [int32(^uint32(0)>>c)])) +(Rsh16Ux64 i:(Lsh16x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 16 && i.Uses == 1 => (And16 x (Const16 [int16(^uint16(0)>>c)])) +(Rsh8Ux64 i:(Lsh8x64 x (Const64 [c])) (Const64 [c])) && c >= 0 && c < 8 && i.Uses == 1 => (And8 x (Const8 [int8 (^uint8 (0)>>c)])) + // ((x >> c1) << c2) >> c3 (Rsh(64|32|16|8)Ux64 (Lsh(64|32|16|8)x64 (Rsh(64|32|16|8)Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) && uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) @@ -500,6 +518,9 @@ (Leq32 (Const32 [0]) (Rsh32Ux64 _ (Const64 [c]))) && c > 0 => (ConstBool [true]) (Leq64 (Const64 [0]) (Rsh64Ux64 _ (Const64 [c]))) && c > 0 => (ConstBool [true]) +(Less(64|32|16|8) (Const(64|32|16|8) [0]) x) && isNonNegative(x) => (Neq(64|32|16|8) (Const(64|32|16|8) [0]) x) +(Less(64|32|16|8) x (Const(64|32|16|8) [1])) && isNonNegative(x) => (Eq(64|32|16|8) (Const(64|32|16|8) [0]) x) + // constant floating point comparisons (Eq32F (Const32F [c]) (Const32F [d])) => (ConstBool [c == d]) (Eq64F (Const64F [c]) (Const64F [d])) => (ConstBool [c == d]) @@ -512,24 +533,36 @@ // simplifications (Or(64|32|16|8) x x) => x -(Or(64|32|16|8) (Const(64|32|16|8) [0]) x) => x +(Or(64|32|16|8) (Const(64|32|16|8) [0]) x) => x (Or(64|32|16|8) (Const(64|32|16|8) [-1]) _) => (Const(64|32|16|8) [-1]) +(Or(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1]) (And(64|32|16|8) x x) => x (And(64|32|16|8) (Const(64|32|16|8) [-1]) x) => x -(And(64|32|16|8) (Const(64|32|16|8) [0]) _) => (Const(64|32|16|8) [0]) +(And(64|32|16|8) (Const(64|32|16|8) [0]) _) => (Const(64|32|16|8) [0]) +(And(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [0]) (Xor(64|32|16|8) x x) => (Const(64|32|16|8) [0]) (Xor(64|32|16|8) (Const(64|32|16|8) [0]) x) => x +(Xor(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1]) (Add(64|32|16|8) (Const(64|32|16|8) [0]) x) => x (Sub(64|32|16|8) x x) => (Const(64|32|16|8) [0]) (Mul(64|32|16|8) (Const(64|32|16|8) [0]) _) => (Const(64|32|16|8) [0]) +(Select0 (Mul(64|32)uover (Const(64|32) [0]) x)) => (Const(64|32) [0]) +(Select1 (Mul(64|32)uover (Const(64|32) [0]) x)) => (ConstBool [false]) (Com(64|32|16|8) (Com(64|32|16|8) x)) => x (Com(64|32|16|8) (Const(64|32|16|8) [c])) => (Const(64|32|16|8) [^c]) (Neg(64|32|16|8) (Sub(64|32|16|8) x y)) => (Sub(64|32|16|8) y x) +(Add(64|32|16|8) x (Neg(64|32|16|8) y)) => (Sub(64|32|16|8) x y) + +(Xor(64|32|16|8) (Const(64|32|16|8) [-1]) x) => (Com(64|32|16|8) x) + +(Sub(64|32|16|8) (Neg(64|32|16|8) x) (Com(64|32|16|8) x)) => (Const(64|32|16|8) [1]) +(Sub(64|32|16|8) (Com(64|32|16|8) x) (Neg(64|32|16|8) x)) => (Const(64|32|16|8) [-1]) +(Add(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1]) // ^(x-1) == ^x+1 == -x (Add(64|32|16|8) (Const(64|32|16|8) [1]) (Com(64|32|16|8) x)) => (Neg(64|32|16|8) x) @@ -590,6 +623,10 @@ // simplifications often used for lengths. e.g. len(s[i:i+5])==5 (Sub(64|32|16|8) (Add(64|32|16|8) x y) x) => y (Sub(64|32|16|8) (Add(64|32|16|8) x y) y) => x +(Sub(64|32|16|8) (Sub(64|32|16|8) x y) x) => (Neg(64|32|16|8) y) +(Sub(64|32|16|8) x (Add(64|32|16|8) x y)) => (Neg(64|32|16|8) y) +(Add(64|32|16|8) x (Sub(64|32|16|8) y x)) => y +(Add(64|32|16|8) x (Add(64|32|16|8) y (Sub(64|32|16|8) z x))) => (Add(64|32|16|8) y z) // basic phi simplifications (Phi (Const8 [c]) (Const8 [c])) => (Const8 [c]) @@ -2004,12 +2041,30 @@ => (Invalid) // for late-expanded calls, recognize memequal applied to a single constant byte -// TODO figure out breakeven number of bytes for this optimization. +// Support is limited by 1, 2, 4, 8 byte sizes (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [1]) mem) && isSameCall(callAux, "runtime.memequal") && symIsRO(scon) => (MakeResult (Eq8 (Load sptr mem) (Const8 [int8(read8(scon,0))])) mem) +(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [2]) mem) + && isSameCall(callAux, "runtime.memequal") + && symIsRO(scon) + && canLoadUnaligned(config) + => (MakeResult (Eq16 (Load sptr mem) (Const16 [int16(read16(scon,0,config.ctxt.Arch.ByteOrder))])) mem) + +(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [4]) mem) + && isSameCall(callAux, "runtime.memequal") + && symIsRO(scon) + && canLoadUnaligned(config) + => (MakeResult (Eq32 (Load sptr mem) (Const32 [int32(read32(scon,0,config.ctxt.Arch.ByteOrder))])) mem) + +(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [8]) mem) + && isSameCall(callAux, "runtime.memequal") + && symIsRO(scon) + && canLoadUnaligned(config) && config.PtrSize == 8 + => (MakeResult (Eq64 (Load sptr mem) (Const64 [int64(read64(scon,0,config.ctxt.Arch.ByteOrder))])) mem) + // Evaluate constant address comparisons. (EqPtr x x) => (ConstBool [true]) (NeqPtr x x) => (ConstBool [false]) @@ -2512,3 +2567,91 @@ // Elide self-moves. This only happens rarely (e.g test/fixedbugs/bug277.go). // However, this rule is needed to prevent the previous rule from looping forever in such cases. (Move dst src mem) && isSamePtr(dst, src) => mem + +// Constant rotate detection. +((Add64|Or64|Xor64) (Lsh64x64 x z:(Const64 [c])) (Rsh64Ux64 x (Const64 [d]))) && c < 64 && d == 64-c && canRotate(config, 64) => (RotateLeft64 x z) +((Add32|Or32|Xor32) (Lsh32x64 x z:(Const64 [c])) (Rsh32Ux64 x (Const64 [d]))) && c < 32 && d == 32-c && canRotate(config, 32) => (RotateLeft32 x z) +((Add16|Or16|Xor16) (Lsh16x64 x z:(Const64 [c])) (Rsh16Ux64 x (Const64 [d]))) && c < 16 && d == 16-c && canRotate(config, 16) => (RotateLeft16 x z) +((Add8|Or8|Xor8) (Lsh8x64 x z:(Const64 [c])) (Rsh8Ux64 x (Const64 [d]))) && c < 8 && d == 8-c && canRotate(config, 8) => (RotateLeft8 x z) + +// Non-constant rotate detection. +// We use shiftIsBounded to make sure that neither of the shifts are >64. +// Note: these rules are subtle when the shift amounts are 0/64, as Go shifts +// are different from most native shifts. But it works out. +((Add64|Or64|Xor64) left:(Lsh64x64 x y) right:(Rsh64Ux64 x (Sub64 (Const64 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x y) +((Add64|Or64|Xor64) left:(Lsh64x32 x y) right:(Rsh64Ux32 x (Sub32 (Const32 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x y) +((Add64|Or64|Xor64) left:(Lsh64x16 x y) right:(Rsh64Ux16 x (Sub16 (Const16 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x y) +((Add64|Or64|Xor64) left:(Lsh64x8 x y) right:(Rsh64Ux8 x (Sub8 (Const8 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x y) + +((Add64|Or64|Xor64) right:(Rsh64Ux64 x y) left:(Lsh64x64 x z:(Sub64 (Const64 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x z) +((Add64|Or64|Xor64) right:(Rsh64Ux32 x y) left:(Lsh64x32 x z:(Sub32 (Const32 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x z) +((Add64|Or64|Xor64) right:(Rsh64Ux16 x y) left:(Lsh64x16 x z:(Sub16 (Const16 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x z) +((Add64|Or64|Xor64) right:(Rsh64Ux8 x y) left:(Lsh64x8 x z:(Sub8 (Const8 [64]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) => (RotateLeft64 x z) + +((Add32|Or32|Xor32) left:(Lsh32x64 x y) right:(Rsh32Ux64 x (Sub64 (Const64 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x y) +((Add32|Or32|Xor32) left:(Lsh32x32 x y) right:(Rsh32Ux32 x (Sub32 (Const32 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x y) +((Add32|Or32|Xor32) left:(Lsh32x16 x y) right:(Rsh32Ux16 x (Sub16 (Const16 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x y) +((Add32|Or32|Xor32) left:(Lsh32x8 x y) right:(Rsh32Ux8 x (Sub8 (Const8 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x y) + +((Add32|Or32|Xor32) right:(Rsh32Ux64 x y) left:(Lsh32x64 x z:(Sub64 (Const64 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x z) +((Add32|Or32|Xor32) right:(Rsh32Ux32 x y) left:(Lsh32x32 x z:(Sub32 (Const32 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x z) +((Add32|Or32|Xor32) right:(Rsh32Ux16 x y) left:(Lsh32x16 x z:(Sub16 (Const16 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x z) +((Add32|Or32|Xor32) right:(Rsh32Ux8 x y) left:(Lsh32x8 x z:(Sub8 (Const8 [32]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) => (RotateLeft32 x z) + +((Add16|Or16|Xor16) left:(Lsh16x64 x y) right:(Rsh16Ux64 x (Sub64 (Const64 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x y) +((Add16|Or16|Xor16) left:(Lsh16x32 x y) right:(Rsh16Ux32 x (Sub32 (Const32 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x y) +((Add16|Or16|Xor16) left:(Lsh16x16 x y) right:(Rsh16Ux16 x (Sub16 (Const16 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x y) +((Add16|Or16|Xor16) left:(Lsh16x8 x y) right:(Rsh16Ux8 x (Sub8 (Const8 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x y) + +((Add16|Or16|Xor16) right:(Rsh16Ux64 x y) left:(Lsh16x64 x z:(Sub64 (Const64 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x z) +((Add16|Or16|Xor16) right:(Rsh16Ux32 x y) left:(Lsh16x32 x z:(Sub32 (Const32 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x z) +((Add16|Or16|Xor16) right:(Rsh16Ux16 x y) left:(Lsh16x16 x z:(Sub16 (Const16 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x z) +((Add16|Or16|Xor16) right:(Rsh16Ux8 x y) left:(Lsh16x8 x z:(Sub8 (Const8 [16]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) => (RotateLeft16 x z) + +((Add8|Or8|Xor8) left:(Lsh8x64 x y) right:(Rsh8Ux64 x (Sub64 (Const64 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x y) +((Add8|Or8|Xor8) left:(Lsh8x32 x y) right:(Rsh8Ux32 x (Sub32 (Const32 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x y) +((Add8|Or8|Xor8) left:(Lsh8x16 x y) right:(Rsh8Ux16 x (Sub16 (Const16 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x y) +((Add8|Or8|Xor8) left:(Lsh8x8 x y) right:(Rsh8Ux8 x (Sub8 (Const8 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x y) + +((Add8|Or8|Xor8) right:(Rsh8Ux64 x y) left:(Lsh8x64 x z:(Sub64 (Const64 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x z) +((Add8|Or8|Xor8) right:(Rsh8Ux32 x y) left:(Lsh8x32 x z:(Sub32 (Const32 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x z) +((Add8|Or8|Xor8) right:(Rsh8Ux16 x y) left:(Lsh8x16 x z:(Sub16 (Const16 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x z) +((Add8|Or8|Xor8) right:(Rsh8Ux8 x y) left:(Lsh8x8 x z:(Sub8 (Const8 [8]) y))) && (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) => (RotateLeft8 x z) + +// Rotating by y&c, with c a mask that doesn't change the bottom bits, is the same as rotating by y. +(RotateLeft64 x (And(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&63 == 63 => (RotateLeft64 x y) +(RotateLeft32 x (And(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&31 == 31 => (RotateLeft32 x y) +(RotateLeft16 x (And(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&15 == 15 => (RotateLeft16 x y) +(RotateLeft8 x (And(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&7 == 7 => (RotateLeft8 x y) + +// Rotating by -(y&c), with c a mask that doesn't change the bottom bits, is the same as rotating by -y. +(RotateLeft64 x (Neg(64|32|16|8) (And(64|32|16|8) y (Const(64|32|16|8) [c])))) && c&63 == 63 => (RotateLeft64 x (Neg(64|32|16|8) y)) +(RotateLeft32 x (Neg(64|32|16|8) (And(64|32|16|8) y (Const(64|32|16|8) [c])))) && c&31 == 31 => (RotateLeft32 x (Neg(64|32|16|8) y)) +(RotateLeft16 x (Neg(64|32|16|8) (And(64|32|16|8) y (Const(64|32|16|8) [c])))) && c&15 == 15 => (RotateLeft16 x (Neg(64|32|16|8) y)) +(RotateLeft8 x (Neg(64|32|16|8) (And(64|32|16|8) y (Const(64|32|16|8) [c])))) && c&7 == 7 => (RotateLeft8 x (Neg(64|32|16|8) y)) + +// Rotating by y+c, with c a multiple of the value width, is the same as rotating by y. +(RotateLeft64 x (Add(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&63 == 0 => (RotateLeft64 x y) +(RotateLeft32 x (Add(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&31 == 0 => (RotateLeft32 x y) +(RotateLeft16 x (Add(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&15 == 0 => (RotateLeft16 x y) +(RotateLeft8 x (Add(64|32|16|8) y (Const(64|32|16|8) [c]))) && c&7 == 0 => (RotateLeft8 x y) + +// Rotating by c-y, with c a multiple of the value width, is the same as rotating by -y. +(RotateLeft64 x (Sub(64|32|16|8) (Const(64|32|16|8) [c]) y)) && c&63 == 0 => (RotateLeft64 x (Neg(64|32|16|8) y)) +(RotateLeft32 x (Sub(64|32|16|8) (Const(64|32|16|8) [c]) y)) && c&31 == 0 => (RotateLeft32 x (Neg(64|32|16|8) y)) +(RotateLeft16 x (Sub(64|32|16|8) (Const(64|32|16|8) [c]) y)) && c&15 == 0 => (RotateLeft16 x (Neg(64|32|16|8) y)) +(RotateLeft8 x (Sub(64|32|16|8) (Const(64|32|16|8) [c]) y)) && c&7 == 0 => (RotateLeft8 x (Neg(64|32|16|8) y)) + +// Ensure we don't do Const64 rotates in a 32-bit system. +(RotateLeft64 x (Const64 [c])) && config.PtrSize == 4 => (RotateLeft64 x (Const32 [int32(c)])) +(RotateLeft32 x (Const64 [c])) && config.PtrSize == 4 => (RotateLeft32 x (Const32 [int32(c)])) +(RotateLeft16 x (Const64 [c])) && config.PtrSize == 4 => (RotateLeft16 x (Const32 [int32(c)])) +(RotateLeft8 x (Const64 [c])) && config.PtrSize == 4 => (RotateLeft8 x (Const32 [int32(c)])) + +// Rotating by c, then by d, is the same as rotating by c+d. +// We're trading a rotate for an add, which seems generally a good choice. It is especially good when c and d are constants. +// This rule is a bit tricky as c and d might be different widths. We handle only cases where they are the same width. +(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 8 && d.Type.Size() == 8 => (RotateLeft(64|32|16|8) x (Add64 c d)) +(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 4 && d.Type.Size() == 4 => (RotateLeft(64|32|16|8) x (Add32 c d)) +(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 2 && d.Type.Size() == 2 => (RotateLeft(64|32|16|8) x (Add16 c d)) +(RotateLeft(64|32|16|8) (RotateLeft(64|32|16|8) x c) d) && c.Type.Size() == 1 && d.Type.Size() == 1 => (RotateLeft(64|32|16|8) x (Add8 c d)) diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 9f6664386c9d17..d09b9aab7573ac 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -106,7 +106,7 @@ var genericOps = []opData{ // For shifts, AxB means the shifted value has A bits and the shift amount has B bits. // Shift amounts are considered unsigned. - // If arg1 is known to be less than the number of bits in arg0, + // If arg1 is known to be nonnegative and less than the number of bits in arg0, // then auxInt may be set to 1. // This enables better code generation on some platforms. {name: "Lsh8x8", argLength: 2, aux: "Bool"}, // arg0 << arg1 @@ -249,14 +249,19 @@ var genericOps = []opData{ {name: "BitRev32", argLength: 1}, // Reverse the bits in arg[0] {name: "BitRev64", argLength: 1}, // Reverse the bits in arg[0] - {name: "PopCount8", argLength: 1}, // Count bits in arg[0] - {name: "PopCount16", argLength: 1}, // Count bits in arg[0] - {name: "PopCount32", argLength: 1}, // Count bits in arg[0] - {name: "PopCount64", argLength: 1}, // Count bits in arg[0] - {name: "RotateLeft8", argLength: 2}, // Rotate bits in arg[0] left by arg[1] - {name: "RotateLeft16", argLength: 2}, // Rotate bits in arg[0] left by arg[1] - {name: "RotateLeft32", argLength: 2}, // Rotate bits in arg[0] left by arg[1] - {name: "RotateLeft64", argLength: 2}, // Rotate bits in arg[0] left by arg[1] + {name: "PopCount8", argLength: 1}, // Count bits in arg[0] + {name: "PopCount16", argLength: 1}, // Count bits in arg[0] + {name: "PopCount32", argLength: 1}, // Count bits in arg[0] + {name: "PopCount64", argLength: 1}, // Count bits in arg[0] + + // RotateLeftX instructions rotate the X bits of arg[0] to the left + // by the low lg_2(X) bits of arg[1], interpreted as an unsigned value. + // Note that this works out regardless of the bit width or signedness of + // arg[1]. In particular, RotateLeft by x is the same as RotateRight by -x. + {name: "RotateLeft64", argLength: 2}, + {name: "RotateLeft32", argLength: 2}, + {name: "RotateLeft16", argLength: 2}, + {name: "RotateLeft8", argLength: 2}, // Square root. // Special cases: @@ -355,7 +360,9 @@ var genericOps = []opData{ {name: "Load", argLength: 2}, // Load from arg0. arg1=memory {name: "Dereference", argLength: 2}, // Load from arg0. arg1=memory. Helper op for arg/result passing, result is an otherwise not-SSA-able "value". {name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // Store arg1 to arg0. arg2=memory, aux=type. Returns memory. - // The source and destination of Move may overlap in some cases. See e.g. + // Normally we require that the source and destination of Move do not overlap. + // There is an exception when we know all the loads will happen before all + // the stores. In that case, overlap is ok. See // memmove inlining in generic.rules. When inlineablememmovesize (in ../rewrite.go) // returns true, we must do all loads before all stores, when lowering Move. // The type of Move is used for the write barrier pass to insert write barriers @@ -417,10 +424,12 @@ var genericOps = []opData{ {name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "StaticCall", argLength: -1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "InterCall", argLength: -1, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1..argN-1 are register inputs, argN=memory, auxint=arg size. Returns Result of register results, plus memory. + {name: "TailCall", argLength: -1, aux: "CallOff", call: true}, // tail call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. {name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. + {name: "TailLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static tail call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, @@ -526,7 +535,6 @@ var genericOps = []opData{ {name: "Unknown"}, {name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem", symEffect: "None", zeroWidth: true}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem - {name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem // TODO: what's the difference between VarLive and KeepAlive? {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem {name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem @@ -615,9 +623,16 @@ var genericOps = []opData{ {name: "AtomicOr8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. {name: "AtomicOr32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory. + // Publication barrier + {name: "PubBarrier", argLength: 1, hasSideEffects: true}, // Do data barrier. arg0=memory. + // Clobber experiment op {name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable {name: "ClobberReg", argLength: 0, typ: "Void"}, // clobber a register + + // Prefetch instruction + {name: "PrefetchCache", argLength: 2, hasSideEffects: true}, // Do prefetch arg0 to cache. arg0=addr, arg1=memory. + {name: "PrefetchCacheStreamed", argLength: 2, hasSideEffects: true}, // Do non-temporal or streamed prefetch arg0 to cache. arg0=addr, arg1=memory. } // kind controls successors implicit exit @@ -630,12 +645,13 @@ var genericOps = []opData{ // First [] [always, never] var genericBlocks = []blockData{ - {name: "Plain"}, // a single successor - {name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1] - {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type) - {name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result - {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is memory result, jumps to b.Aux.(*gc.Sym) - {name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic + {name: "Plain"}, // a single successor + {name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1] + {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type) + {name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result + {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call + {name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic + {name: "JumpTable", controls: 1}, // multiple successors, the integer Controls[0] selects which one // transient block state used for dead code removal {name: "First"}, // 2 successors, always takes the first one (second is dead) diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go index 8e5997b25a4473..b1a7cf9f6f2f98 100644 --- a/src/cmd/compile/internal/ssa/gen/main.go +++ b/src/cmd/compile/internal/ssa/gen/main.go @@ -63,6 +63,7 @@ type opData struct { resultNotInArgs bool // outputs must not be allocated to the same registers as inputs clobberFlags bool // this op clobbers flags register call bool // is a function call + tailCall bool // is a tail call nilCheck bool // this op is a nil check on arg0 faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset) faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset) @@ -93,7 +94,7 @@ type regInfo struct { type regMask uint64 func (a arch) regMaskComment(r regMask) string { - var buf bytes.Buffer + var buf strings.Builder for i := uint64(0); r != 0; i++ { if r&1 != 0 { if buf.Len() == 0 { @@ -307,6 +308,9 @@ func genOp() { if v.call { fmt.Fprintln(w, "call: true,") } + if v.tailCall { + fmt.Fprintln(w, "tailCall: true,") + } if v.nilCheck { fmt.Fprintln(w, "nilCheck: true,") } @@ -405,6 +409,7 @@ func genOp() { fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }") fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }") + fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }") fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }") fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }") fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }") diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go index fe8db4ed1f2518..0f7e970372636e 100644 --- a/src/cmd/compile/internal/ssa/gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/gen/rulegen.go @@ -1838,6 +1838,8 @@ func (op opData) auxIntType() string { // auxType returns the Go type that this block should store in its aux field. func (b blockData) auxType() string { switch b.aux { + case "Sym": + return "Sym" case "S390XCCMask", "S390XCCMaskInt8", "S390XCCMaskUint8": return "s390x.CCMask" case "S390XRotateParams": diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 4d191199fbafe1..c3950697d30b4b 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -9,9 +9,9 @@ import ( "cmd/internal/src" "fmt" "html" - exec "internal/execabs" "io" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -848,7 +848,7 @@ func (w *HTMLWriter) WriteSources(phase string, all []*FuncLines) { if w == nil { return // avoid generating HTML just to discard it } - var buf bytes.Buffer + var buf strings.Builder fmt.Fprint(&buf, "

        ") filename := "" for _, fl := range all { @@ -890,7 +890,7 @@ func (w *HTMLWriter) WriteAST(phase string, buf *bytes.Buffer) { return // avoid generating HTML just to discard it } lines := strings.Split(buf.String(), "\n") - var out bytes.Buffer + var out strings.Builder fmt.Fprint(&out, "
        ") for _, l := range lines { @@ -903,15 +903,12 @@ func (w *HTMLWriter) WriteAST(phase string, buf *bytes.Buffer) { if strings.HasPrefix(l, "buildssa") { escaped = fmt.Sprintf("%v", l) } else { - // Parse the line number from the format l(123). - idx := strings.Index(l, " l(") - if idx != -1 { - subl := l[idx+3:] - idxEnd := strings.Index(subl, ")") - if idxEnd != -1 { - if _, err := strconv.Atoi(subl[:idxEnd]); err == nil { - lineNo = subl[:idxEnd] - } + // Parse the line number from the format file:line:col. + // See the implementation in ir/fmt.go:dumpNodeHeader. + sl := strings.Split(l, ":") + if len(sl) >= 3 { + if _, err := strconv.Atoi(sl[len(sl)-2]); err == nil { + lineNo = sl[len(sl)-2] } } escaped = html.EscapeString(l) @@ -1056,7 +1053,7 @@ func (b *Block) LongHTML() string { } func (f *Func) HTML(phase string, dot *dotWriter) string { - buf := new(bytes.Buffer) + buf := new(strings.Builder) if dot != nil { dot.writeFuncSVG(buf, phase, f) } @@ -1085,7 +1082,7 @@ func (d *dotWriter) writeFuncSVG(w io.Writer, phase string, f *Func) { } buf := new(bytes.Buffer) cmd.Stdout = buf - bufErr := new(bytes.Buffer) + bufErr := new(strings.Builder) cmd.Stderr = bufErr err = cmd.Start() if err != nil { @@ -1221,7 +1218,7 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { } } -func (p htmlFuncPrinter) endBlock(b *Block) { +func (p htmlFuncPrinter) endBlock(b *Block, reachable bool) { if len(b.Values) > 0 { // end list of values io.WriteString(p.w, "") io.WriteString(p.w, "") diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index 252c47cdebc8b6..00aea879363d6b 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -46,19 +46,19 @@ func (r *Register) GCNum() int16 { // variable that has been decomposed into multiple stack slots. // As an example, a string could have the following configurations: // -// stack layout LocalSlots +// stack layout LocalSlots // -// Optimizations are disabled. s is on the stack and represented in its entirety. -// [ ------- s string ---- ] { N: s, Type: string, Off: 0 } +// Optimizations are disabled. s is on the stack and represented in its entirety. +// [ ------- s string ---- ] { N: s, Type: string, Off: 0 } // -// s was not decomposed, but the SSA operates on its parts individually, so -// there is a LocalSlot for each of its fields that points into the single stack slot. -// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8} +// s was not decomposed, but the SSA operates on its parts individually, so +// there is a LocalSlot for each of its fields that points into the single stack slot. +// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8} // -// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot. -// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0}, -// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8} -// parent = &{N: s, Type: string} +// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot. +// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0}, +// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8} +// parent = &{N: s, Type: string} type LocalSlot struct { N *ir.Name // an ONAME *ir.Name representing a stack location. Type *types.Type // type of slot @@ -91,8 +91,8 @@ func (t LocPair) String() string { type LocResults []Location func (t LocResults) String() string { - s := "<" - a := "" + s := "" + a := "<" for _, r := range t { a += s s = "," diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go index 5a4bc1d60aa5e5..d92566f2d3eb45 100644 --- a/src/cmd/compile/internal/ssa/loopbce.go +++ b/src/cmd/compile/internal/ssa/loopbce.go @@ -5,6 +5,7 @@ package ssa import ( + "cmd/compile/internal/base" "fmt" "math" ) @@ -31,9 +32,10 @@ type indVar struct { // parseIndVar checks whether the SSA value passed as argument is a valid induction // variable, and, if so, extracts: -// * the minimum bound -// * the increment value -// * the "next" value (SSA value that is Phi'd into the induction variable every loop) +// - the minimum bound +// - the increment value +// - the "next" value (SSA value that is Phi'd into the induction variable every loop) +// // Currently, we detect induction variables that match (Phi min nxt), // with nxt being (Add inc ind). // If it can't parse the induction variable correctly, it returns (nil, nil, nil). @@ -66,19 +68,18 @@ func parseIndVar(ind *Value) (min, inc, nxt *Value) { // // Look for variables and blocks that satisfy the following // -// loop: -// ind = (Phi min nxt), -// if ind < max -// then goto enter_loop -// else goto exit_loop -// -// enter_loop: -// do something -// nxt = inc + ind -// goto loop +// loop: +// ind = (Phi min nxt), +// if ind < max +// then goto enter_loop +// else goto exit_loop // -// exit_loop: +// enter_loop: +// do something +// nxt = inc + ind +// goto loop // +// exit_loop: // // TODO: handle 32 bit operations func findIndVar(f *Func) []indVar { @@ -90,41 +91,42 @@ func findIndVar(f *Func) []indVar { continue } - var flags indVarFlags - var ind, max *Value // induction, and maximum + var ind *Value // induction variable + var init *Value // starting value + var limit *Value // ending value - // Check thet the control if it either ind />= ind. + // Check thet the control if it either ind i; i++ - min, inc, nxt = parseIndVar(max) - if min == nil { + // whether the control op was written with the induction variable on the RHS + // instead of the LHS. This happens for the downwards case, like: + // for i := len(n)-1; i >= 0; i-- + init, inc, nxt = parseIndVar(limit) + if init == nil { // No recognied induction variable on either operand continue } // Ok, the arguments were reversed. Swap them, and remember that we're // looking at a ind >/>= loop (so the induction must be decrementing). - ind, max = max, ind + ind, limit = limit, ind less = false } @@ -138,8 +140,8 @@ func findIndVar(f *Func) []indVar { } // Increment sign must match comparison direction. - // When incrementing, the termination comparison must be ind />= max. + // When incrementing, the termination comparison must be ind />= limit. // See issue 26116. if step > 0 && !less { continue @@ -148,170 +150,243 @@ func findIndVar(f *Func) []indVar { continue } - // If the increment is negative, swap min/max and their flags - if step < 0 { - min, max = max, min - oldf := flags - flags = indVarMaxInc - if oldf&indVarMaxInc == 0 { - flags |= indVarMinExc - } - step = -step - } - // Up to now we extracted the induction variable (ind), // the increment delta (inc), the temporary sum (nxt), - // the mininum value (min) and the maximum value (max). + // the initial value (init) and the limiting value (limit). // - // We also know that ind has the form (Phi min nxt) where + // We also know that ind has the form (Phi init nxt) where // nxt is (Add inc nxt) which means: 1) inc dominates nxt // and 2) there is a loop starting at inc and containing nxt. // // We need to prove that the induction variable is incremented - // only when it's smaller than the maximum value. + // only when it's smaller than the limiting value. // Two conditions must happen listed below to accept ind // as an induction variable. // First condition: loop entry has a single predecessor, which // is the header block. This implies that b.Succs[0] is - // reached iff ind < max. + // reached iff ind < limit. if len(b.Succs[0].b.Preds) != 1 { // b.Succs[1] must exit the loop. continue } // Second condition: b.Succs[0] dominates nxt so that - // nxt is computed when inc < max, meaning nxt <= max. + // nxt is computed when inc < limit. if !sdom.IsAncestorEq(b.Succs[0].b, nxt.Block) { // inc+ind can only be reached through the branch that enters the loop. continue } - // We can only guarantee that the loop runs within limits of induction variable - // if (one of) - // (1) the increment is ±1 - // (2) the limits are constants - // (3) loop is of the form k0 upto Known_not_negative-k inclusive, step <= k - // (4) loop is of the form k0 upto Known_not_negative-k exclusive, step <= k+1 - // (5) loop is of the form Known_not_negative downto k0, minint+step < k0 - if step > 1 { - ok := false - if min.Op == OpConst64 && max.Op == OpConst64 { - if max.AuxInt > min.AuxInt && max.AuxInt%step == min.AuxInt%step { // handle overflow - ok = true - } - } - // Handle induction variables of these forms. - // KNN is known-not-negative. - // SIGNED ARITHMETIC ONLY. (see switch on c above) - // Possibilities for KNN are len and cap; perhaps we can infer others. - // for i := 0; i <= KNN-k ; i += k - // for i := 0; i < KNN-(k-1); i += k - // Also handle decreasing. - - // "Proof" copied from https://go-review.googlesource.com/c/go/+/104041/10/src/cmd/compile/internal/ssa/loopbce.go#164 - // - // In the case of - // // PC is Positive Constant - // L := len(A)-PC - // for i := 0; i < L; i = i+PC - // - // we know: - // - // 0 + PC does not over/underflow. - // len(A)-PC does not over/underflow - // maximum value for L is MaxInt-PC - // i < L <= MaxInt-PC means i + PC < MaxInt hence no overflow. - - // To match in SSA: - // if (a) min.Op == OpConst64(k0) - // and (b) k0 >= MININT + step - // and (c) max.Op == OpSubtract(Op{StringLen,SliceLen,SliceCap}, k) - // or (c) max.Op == OpAdd(Op{StringLen,SliceLen,SliceCap}, -k) - // or (c) max.Op == Op{StringLen,SliceLen,SliceCap} - // and (d) if upto loop, require indVarMaxInc && step <= k or !indVarMaxInc && step-1 <= k - - if min.Op == OpConst64 && min.AuxInt >= step+math.MinInt64 { - knn := max - k := int64(0) - var kArg *Value - - switch max.Op { - case OpSub64: - knn = max.Args[0] - kArg = max.Args[1] - - case OpAdd64: - knn = max.Args[0] - kArg = max.Args[1] - if knn.Op == OpConst64 { - knn, kArg = kArg, knn + // Check for overflow/underflow. We need to make sure that inc never causes + // the induction variable to wrap around. + // We use a function wrapper here for easy return true / return false / keep going logic. + // This function returns true if the increment will never overflow/underflow. + ok := func() bool { + if step > 0 { + if limit.Op == OpConst64 { + // Figure out the actual largest value. + v := limit.AuxInt + if !inclusive { + if v == math.MinInt64 { + return false // < minint is never satisfiable. + } + v-- + } + if init.Op == OpConst64 { + // Use stride to compute a better lower limit. + if init.AuxInt > v { + return false + } + v = addU(init.AuxInt, diff(v, init.AuxInt)/uint64(step)*uint64(step)) + } + if addWillOverflow(v, step) { + return false + } + if inclusive && v != limit.AuxInt || !inclusive && v+1 != limit.AuxInt { + // We know a better limit than the programmer did. Use our limit instead. + limit = f.ConstInt64(f.Config.Types.Int64, v) + inclusive = true } + return true } - switch knn.Op { - case OpSliceLen, OpStringLen, OpSliceCap: - default: - knn = nil + if step == 1 && !inclusive { + // Can't overflow because maxint is never a possible value. + return true } - - if kArg != nil && kArg.Op == OpConst64 { - k = kArg.AuxInt - if max.Op == OpAdd64 { - k = -k - } + // If the limit is not a constant, check to see if it is a + // negative offset from a known non-negative value. + knn, k := findKNN(limit) + if knn == nil || k < 0 { + return false + } + // limit == (something nonnegative) - k. That subtraction can't underflow, so + // we can trust it. + if inclusive { + // ind <= knn - k cannot overflow if step is at most k + return step <= k } - if k >= 0 && knn != nil { - if inc.AuxInt > 0 { // increasing iteration - // The concern for the relation between step and k is to ensure that iv never exceeds knn - // i.e., iv < knn-(K-1) ==> iv + K <= knn; iv <= knn-K ==> iv +K < knn - if step <= k || flags&indVarMaxInc == 0 && step-1 == k { - ok = true + // ind < knn - k cannot overflow if step is at most k+1 + return step <= k+1 && k != math.MaxInt64 + } else { // step < 0 + if limit.Op == OpConst64 { + // Figure out the actual smallest value. + v := limit.AuxInt + if !inclusive { + if v == math.MaxInt64 { + return false // > maxint is never satisfiable. } - } else { // decreasing iteration - // Will be decrementing from max towards min; max is knn-k; will only attempt decrement if - // knn-k >[=] min; underflow is only a concern if min-step is not smaller than min. - // This all assumes signed integer arithmetic - // This is already assured by the test above: min.AuxInt >= step+math.MinInt64 - ok = true + v++ + } + if init.Op == OpConst64 { + // Use stride to compute a better lower limit. + if init.AuxInt < v { + return false + } + v = subU(init.AuxInt, diff(init.AuxInt, v)/uint64(-step)*uint64(-step)) + } + if subWillUnderflow(v, -step) { + return false } + if inclusive && v != limit.AuxInt || !inclusive && v-1 != limit.AuxInt { + // We know a better limit than the programmer did. Use our limit instead. + limit = f.ConstInt64(f.Config.Types.Int64, v) + inclusive = true + } + return true + } + if step == -1 && !inclusive { + // Can't underflow because minint is never a possible value. + return true } } + return false - // TODO: other unrolling idioms - // for i := 0; i < KNN - KNN % k ; i += k - // for i := 0; i < KNN&^(k-1) ; i += k // k a power of 2 - // for i := 0; i < KNN&(-k) ; i += k // k a power of 2 + } - if !ok { - continue + if ok() { + flags := indVarFlags(0) + var min, max *Value + if step > 0 { + min = init + max = limit + if inclusive { + flags |= indVarMaxInc + } + } else { + min = limit + max = init + flags |= indVarMaxInc + if !inclusive { + flags |= indVarMinExc + } + step = -step + } + if f.pass.debug >= 1 { + printIndVar(b, ind, min, max, step, flags) } - } - if f.pass.debug >= 1 { - printIndVar(b, ind, min, max, step, flags) + iv = append(iv, indVar{ + ind: ind, + min: min, + max: max, + entry: b.Succs[0].b, + flags: flags, + }) + b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max) } - iv = append(iv, indVar{ - ind: ind, - min: min, - max: max, - entry: b.Succs[0].b, - flags: flags, - }) - b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max) + // TODO: other unrolling idioms + // for i := 0; i < KNN - KNN % k ; i += k + // for i := 0; i < KNN&^(k-1) ; i += k // k a power of 2 + // for i := 0; i < KNN&(-k) ; i += k // k a power of 2 } return iv } -func dropAdd64(v *Value) (*Value, int64) { - if v.Op == OpAdd64 && v.Args[0].Op == OpConst64 { - return v.Args[1], v.Args[0].AuxInt +// addWillOverflow reports whether x+y would result in a value more than maxint. +func addWillOverflow(x, y int64) bool { + return x+y < x +} + +// subWillUnderflow reports whether x-y would result in a value less than minint. +func subWillUnderflow(x, y int64) bool { + return x-y > x +} + +// diff returns x-y as a uint64. Requires x>=y. +func diff(x, y int64) uint64 { + if x < y { + base.Fatalf("diff %d - %d underflowed", x, y) + } + return uint64(x - y) +} + +// addU returns x+y. Requires that x+y does not overflow an int64. +func addU(x int64, y uint64) int64 { + if y >= 1<<63 { + if x >= 0 { + base.Fatalf("addU overflowed %d + %d", x, y) + } + x += 1<<63 - 1 + x += 1 + y -= 1 << 63 + } + if addWillOverflow(x, int64(y)) { + base.Fatalf("addU overflowed %d + %d", x, y) + } + return x + int64(y) +} + +// subU returns x-y. Requires that x-y does not underflow an int64. +func subU(x int64, y uint64) int64 { + if y >= 1<<63 { + if x < 0 { + base.Fatalf("subU underflowed %d - %d", x, y) + } + x -= 1<<63 - 1 + x -= 1 + y -= 1 << 63 + } + if subWillUnderflow(x, int64(y)) { + base.Fatalf("subU underflowed %d - %d", x, y) + } + return x - int64(y) +} + +// if v is known to be x - c, where x is known to be nonnegative and c is a +// constant, return x, c. Otherwise return nil, 0. +func findKNN(v *Value) (*Value, int64) { + var x, y *Value + x = v + switch v.Op { + case OpSub64: + x = v.Args[0] + y = v.Args[1] + + case OpAdd64: + x = v.Args[0] + y = v.Args[1] + if x.Op == OpConst64 { + x, y = y, x + } + } + switch x.Op { + case OpSliceLen, OpStringLen, OpSliceCap: + default: + return nil, 0 + } + if y == nil { + return x, 0 + } + if y.Op != OpConst64 { + return nil, 0 } - if v.Op == OpAdd64 && v.Args[1].Op == OpConst64 { - return v.Args[0], v.Args[1].AuxInt + if v.Op == OpAdd64 { + return x, -y.AuxInt } - return v, 0 + return x, y.AuxInt } func printIndVar(b *Block, i, min, max *Value, inc int64, flags indVarFlags) { diff --git a/src/cmd/compile/internal/ssa/loopreschedchecks.go b/src/cmd/compile/internal/ssa/loopreschedchecks.go index 738c62607adb49..1326fa5ee8e25b 100644 --- a/src/cmd/compile/internal/ssa/loopreschedchecks.go +++ b/src/cmd/compile/internal/ssa/loopreschedchecks.go @@ -246,8 +246,8 @@ func insertLoopReschedChecks(f *Func) { // mem1 := call resched (mem0) // goto header resched := f.fe.Syslook("goschedguarded") - // TODO(register args) -- will need more details - mem1 := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeMem, StaticAuxCall(resched, nil), mem0) + call := sched.NewValue1A(bb.Pos, OpStaticCall, types.TypeResultMem, StaticAuxCall(resched, bb.Func.ABIDefault.ABIAnalyzeTypes(nil, nil, nil)), mem0) + mem1 := sched.NewValue1I(bb.Pos, OpSelectN, types.TypeMem, 0, call) sched.AddEdgeTo(h) headerMemPhi.AddArg(mem1) @@ -448,6 +448,16 @@ func findLastMems(f *Func) []*Value { if last == nil { b.Fatalf("no last store found - cycle?") } + + // If this is a tuple containing a mem, select just + // the mem. This will generate ops we don't need, but + // it's the easiest thing to do. + if last.Type.IsTuple() { + last = b.NewValue1(last.Pos, OpSelect1, types.TypeMem, last) + } else if last.Type.IsResults() { + last = b.NewValue1I(last.Pos, OpSelectN, types.TypeMem, int64(last.Type.NumFields()-1), last) + } + lastMems[b.ID] = last } return lastMems diff --git a/src/cmd/compile/internal/ssa/looprotate.go b/src/cmd/compile/internal/ssa/looprotate.go index 35010a78d8e049..2eefda1c8b3667 100644 --- a/src/cmd/compile/internal/ssa/looprotate.go +++ b/src/cmd/compile/internal/ssa/looprotate.go @@ -8,19 +8,19 @@ package ssa // to loops with a check-loop-condition-at-end. // This helps loops avoid extra unnecessary jumps. // -// loop: -// CMPQ ... -// JGE exit -// ... -// JMP loop -// exit: +// loop: +// CMPQ ... +// JGE exit +// ... +// JMP loop +// exit: // -// JMP entry -// loop: -// ... -// entry: -// CMPQ ... -// JLT loop +// JMP entry +// loop: +// ... +// entry: +// CMPQ ... +// JLT loop func loopRotate(f *Func) { loopnest := f.loopnest() if loopnest.hasIrreducible { diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index 5760c356015f16..fb4b7484136a87 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, OpArgIntReg, OpArgFloatReg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpSelectN, OpConvert, OpInlMark: + case OpSP, OpSB, OpInitMem, OpArg, OpArgIntReg, OpArgFloatReg, OpPhi, OpVarDef, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpSelectN, OpConvert, OpInlMark: continue // ok not to lower case OpMakeResult: if b.Controls[0] == v { diff --git a/src/cmd/compile/internal/ssa/magic.go b/src/cmd/compile/internal/ssa/magic.go index 93f8801bce49e8..e903d92bb61903 100644 --- a/src/cmd/compile/internal/ssa/magic.go +++ b/src/cmd/compile/internal/ssa/magic.go @@ -110,7 +110,8 @@ type umagicData struct { // umagic computes the constants needed to strength reduce unsigned n-bit divides by the constant uint64(c). // The return values satisfy for all 0 <= x < 2^n -// floor(x / uint64(c)) = x * (m + 2^n) >> (n+s) +// +// floor(x / uint64(c)) = x * (m + 2^n) >> (n+s) func umagic(n uint, c int64) umagicData { // Convert from ConstX auxint values to the real uint64 constant they represent. d := uint64(c) << (64 - n) >> (64 - n) @@ -183,7 +184,8 @@ type smagicData struct { // magic computes the constants needed to strength reduce signed n-bit divides by the constant c. // Must have c>0. // The return values satisfy for all -2^(n-1) <= x < 2^(n-1) -// trunc(x / c) = x * m >> (n+s) + (x < 0 ? 1 : 0) +// +// trunc(x / c) = x * m >> (n+s) + (x < 0 ? 1 : 0) func smagic(n uint, c int64) smagicData { C := new(big.Int).SetInt64(c) s := C.BitLen() - 1 diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go index 14f511a5f1cfc6..521cdfd7ae74f7 100644 --- a/src/cmd/compile/internal/ssa/nilcheck.go +++ b/src/cmd/compile/internal/ssa/nilcheck.go @@ -236,7 +236,7 @@ func nilcheckelim2(f *Func) { continue } if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() { - if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(*ir.Name).Type().HasPointers()) { + if v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(*ir.Name).Type().HasPointers()) { // These ops don't really change memory. continue // Note: OpVarDef requires that the defined variable not have pointers. diff --git a/src/cmd/compile/internal/ssa/numberlines.go b/src/cmd/compile/internal/ssa/numberlines.go index 9d6aeca9c0dc01..4cbc4919f497b1 100644 --- a/src/cmd/compile/internal/ssa/numberlines.go +++ b/src/cmd/compile/internal/ssa/numberlines.go @@ -62,7 +62,7 @@ func nextGoodStatementIndex(v *Value, i int, b *Block) int { // statement boundary. func notStmtBoundary(op Op) bool { switch op { - case OpCopy, OpPhi, OpVarKill, OpVarDef, OpVarLive, OpUnknown, OpFwdRef, OpArg, OpArgIntReg, OpArgFloatReg: + case OpCopy, OpPhi, OpVarDef, OpVarLive, OpUnknown, OpFwdRef, OpArg, OpArgIntReg, OpArgFloatReg: return true } return false diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index f09a08abcf7521..a3e8dcd2f624f4 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -34,6 +34,7 @@ type opInfo struct { resultNotInArgs bool // outputs must not be allocated to the same registers as inputs clobberFlags bool // this op clobbers flags register call bool // is a function call + tailCall bool // is a tail call nilCheck bool // this op is a nil check on arg0 faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset) faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset) @@ -102,6 +103,10 @@ func (a *AuxNameOffset) String() string { return fmt.Sprintf("%s+%d", a.Name.Sym().Name, a.Offset) } +func (a *AuxNameOffset) FrameOffset() int64 { + return a.Name.FrameOffset() + a.Offset +} + type AuxCall struct { Fn *obj.LSym reg *regInfo // regInfo for this call @@ -254,13 +259,13 @@ func (a *AuxCall) TypeOfArg(which int64) *types.Type { // SizeOfResult returns the size of result which (indexed 0, 1, etc). func (a *AuxCall) SizeOfResult(which int64) int64 { - return a.TypeOfResult(which).Width + return a.TypeOfResult(which).Size() } // SizeOfArg returns the size of argument which (indexed 0, 1, etc). // If the call is to a method, the receiver is the first argument (i.e., index 0) func (a *AuxCall) SizeOfArg(which int64) int64 { - return a.TypeOfArg(which).Width + return a.TypeOfArg(which).Size() } // NResults returns the number of results @@ -386,9 +391,9 @@ const ( // A Sym represents a symbolic offset from a base register. // Currently a Sym can be one of 3 things: -// - a *gc.Node, for an offset from SP (the stack pointer) -// - a *obj.LSym, for an offset from SB (the global pointer) -// - nil, for no offset +// - a *gc.Node, for an offset from SP (the stack pointer) +// - a *obj.LSym, for an offset from SB (the global pointer) +// - nil, for no offset type Sym interface { CanBeAnSSASym() CanBeAnSSAAux() @@ -474,12 +479,13 @@ const ( ) // boundsAPI determines which register arguments a bounds check call should use. For an [a:b:c] slice, we do: -// CMPQ c, cap -// JA fail1 -// CMPQ b, c -// JA fail2 -// CMPQ a, b -// JA fail3 +// +// CMPQ c, cap +// JA fail1 +// CMPQ b, c +// JA fail2 +// CMPQ a, b +// JA fail3 // // fail1: CALL panicSlice3Acap (c, cap) // fail2: CALL panicSlice3B (b, c) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 1c37fbe0db4a49..7755ee3c15439e 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -6,6 +6,7 @@ import ( "cmd/internal/obj" "cmd/internal/obj/arm" "cmd/internal/obj/arm64" + "cmd/internal/obj/loong64" "cmd/internal/obj/mips" "cmd/internal/obj/ppc64" "cmd/internal/obj/riscv" @@ -50,6 +51,7 @@ const ( BlockAMD64NEF BlockAMD64ORD BlockAMD64NAN + BlockAMD64JUMPTABLE BlockARMEQ BlockARMNE @@ -90,6 +92,16 @@ const ( BlockARM64LEnoov BlockARM64GTnoov BlockARM64GEnoov + BlockARM64JUMPTABLE + + BlockLOONG64EQ + BlockLOONG64NE + BlockLOONG64LTZ + BlockLOONG64LEZ + BlockLOONG64GTZ + BlockLOONG64GEZ + BlockLOONG64FPT + BlockLOONG64FPF BlockMIPSEQ BlockMIPSNE @@ -149,6 +161,7 @@ const ( BlockRet BlockRetJmp BlockExit + BlockJumpTable BlockFirst ) @@ -172,22 +185,23 @@ var blockString = [...]string{ Block386ORD: "ORD", Block386NAN: "NAN", - BlockAMD64EQ: "EQ", - BlockAMD64NE: "NE", - BlockAMD64LT: "LT", - BlockAMD64LE: "LE", - BlockAMD64GT: "GT", - BlockAMD64GE: "GE", - BlockAMD64OS: "OS", - BlockAMD64OC: "OC", - BlockAMD64ULT: "ULT", - BlockAMD64ULE: "ULE", - BlockAMD64UGT: "UGT", - BlockAMD64UGE: "UGE", - BlockAMD64EQF: "EQF", - BlockAMD64NEF: "NEF", - BlockAMD64ORD: "ORD", - BlockAMD64NAN: "NAN", + BlockAMD64EQ: "EQ", + BlockAMD64NE: "NE", + BlockAMD64LT: "LT", + BlockAMD64LE: "LE", + BlockAMD64GT: "GT", + BlockAMD64GE: "GE", + BlockAMD64OS: "OS", + BlockAMD64OC: "OC", + BlockAMD64ULT: "ULT", + BlockAMD64ULE: "ULE", + BlockAMD64UGT: "UGT", + BlockAMD64UGE: "UGE", + BlockAMD64EQF: "EQF", + BlockAMD64NEF: "NEF", + BlockAMD64ORD: "ORD", + BlockAMD64NAN: "NAN", + BlockAMD64JUMPTABLE: "JUMPTABLE", BlockARMEQ: "EQ", BlockARMNE: "NE", @@ -204,30 +218,40 @@ var blockString = [...]string{ BlockARMGTnoov: "GTnoov", BlockARMGEnoov: "GEnoov", - BlockARM64EQ: "EQ", - BlockARM64NE: "NE", - BlockARM64LT: "LT", - BlockARM64LE: "LE", - BlockARM64GT: "GT", - BlockARM64GE: "GE", - BlockARM64ULT: "ULT", - BlockARM64ULE: "ULE", - BlockARM64UGT: "UGT", - BlockARM64UGE: "UGE", - BlockARM64Z: "Z", - BlockARM64NZ: "NZ", - BlockARM64ZW: "ZW", - BlockARM64NZW: "NZW", - BlockARM64TBZ: "TBZ", - BlockARM64TBNZ: "TBNZ", - BlockARM64FLT: "FLT", - BlockARM64FLE: "FLE", - BlockARM64FGT: "FGT", - BlockARM64FGE: "FGE", - BlockARM64LTnoov: "LTnoov", - BlockARM64LEnoov: "LEnoov", - BlockARM64GTnoov: "GTnoov", - BlockARM64GEnoov: "GEnoov", + BlockARM64EQ: "EQ", + BlockARM64NE: "NE", + BlockARM64LT: "LT", + BlockARM64LE: "LE", + BlockARM64GT: "GT", + BlockARM64GE: "GE", + BlockARM64ULT: "ULT", + BlockARM64ULE: "ULE", + BlockARM64UGT: "UGT", + BlockARM64UGE: "UGE", + BlockARM64Z: "Z", + BlockARM64NZ: "NZ", + BlockARM64ZW: "ZW", + BlockARM64NZW: "NZW", + BlockARM64TBZ: "TBZ", + BlockARM64TBNZ: "TBNZ", + BlockARM64FLT: "FLT", + BlockARM64FLE: "FLE", + BlockARM64FGT: "FGT", + BlockARM64FGE: "FGE", + BlockARM64LTnoov: "LTnoov", + BlockARM64LEnoov: "LEnoov", + BlockARM64GTnoov: "GTnoov", + BlockARM64GEnoov: "GEnoov", + BlockARM64JUMPTABLE: "JUMPTABLE", + + BlockLOONG64EQ: "EQ", + BlockLOONG64NE: "NE", + BlockLOONG64LTZ: "LTZ", + BlockLOONG64LEZ: "LEZ", + BlockLOONG64GTZ: "GTZ", + BlockLOONG64GEZ: "GEZ", + BlockLOONG64FPT: "FPT", + BlockLOONG64FPF: "FPF", BlockMIPSEQ: "EQ", BlockMIPSNE: "NE", @@ -281,13 +305,14 @@ var blockString = [...]string{ BlockS390XCLIJ: "CLIJ", BlockS390XCLGIJ: "CLGIJ", - BlockPlain: "Plain", - BlockIf: "If", - BlockDefer: "Defer", - BlockRet: "Ret", - BlockRetJmp: "RetJmp", - BlockExit: "Exit", - BlockFirst: "First", + BlockPlain: "Plain", + BlockIf: "If", + BlockDefer: "Defer", + BlockRet: "Ret", + BlockRetJmp: "RetJmp", + BlockExit: "Exit", + BlockJumpTable: "JumpTable", + BlockFirst: "First", } func (k BlockKind) String() string { return blockString[k] } @@ -409,6 +434,9 @@ const ( Op386SARLconst Op386SARWconst Op386SARBconst + Op386ROLL + Op386ROLW + Op386ROLB Op386ROLLconst Op386ROLWconst Op386ROLBconst @@ -515,6 +543,7 @@ const ( Op386DUFFZERO Op386REPSTOSL Op386CALLstatic + Op386CALLtail Op386CALLclosure Op386CALLinter Op386DUFFCOPY @@ -962,7 +991,6 @@ const ( OpAMD64MOVQstore OpAMD64MOVOload OpAMD64MOVOstore - OpAMD64MOVOstorezero OpAMD64MOVBloadidx1 OpAMD64MOVWloadidx1 OpAMD64MOVWloadidx2 @@ -983,6 +1011,7 @@ const ( OpAMD64MOVWstoreconst OpAMD64MOVLstoreconst OpAMD64MOVQstoreconst + OpAMD64MOVOstoreconst OpAMD64MOVBstoreconstidx1 OpAMD64MOVWstoreconstidx1 OpAMD64MOVWstoreconstidx2 @@ -993,6 +1022,7 @@ const ( OpAMD64DUFFZERO OpAMD64REPSTOSQ OpAMD64CALLstatic + OpAMD64CALLtail OpAMD64CALLclosure OpAMD64CALLinter OpAMD64DUFFCOPY @@ -1029,6 +1059,64 @@ const ( OpAMD64ANDLlock OpAMD64ORBlock OpAMD64ORLlock + OpAMD64PrefetchT0 + OpAMD64PrefetchNTA + OpAMD64ANDNQ + OpAMD64ANDNL + OpAMD64BLSIQ + OpAMD64BLSIL + OpAMD64BLSMSKQ + OpAMD64BLSMSKL + OpAMD64BLSRQ + OpAMD64BLSRL + OpAMD64TZCNTQ + OpAMD64TZCNTL + OpAMD64LZCNTQ + OpAMD64LZCNTL + OpAMD64MOVBEWstore + OpAMD64MOVBELload + OpAMD64MOVBELstore + OpAMD64MOVBEQload + OpAMD64MOVBEQstore + OpAMD64MOVBELloadidx1 + OpAMD64MOVBELloadidx4 + OpAMD64MOVBELloadidx8 + OpAMD64MOVBEQloadidx1 + OpAMD64MOVBEQloadidx8 + OpAMD64MOVBEWstoreidx1 + OpAMD64MOVBEWstoreidx2 + OpAMD64MOVBELstoreidx1 + OpAMD64MOVBELstoreidx4 + OpAMD64MOVBELstoreidx8 + OpAMD64MOVBEQstoreidx1 + OpAMD64MOVBEQstoreidx8 + OpAMD64SARXQ + OpAMD64SARXL + OpAMD64SHLXQ + OpAMD64SHLXL + OpAMD64SHRXQ + OpAMD64SHRXL + OpAMD64SARXLload + OpAMD64SARXQload + OpAMD64SHLXLload + OpAMD64SHLXQload + OpAMD64SHRXLload + OpAMD64SHRXQload + OpAMD64SARXLloadidx1 + OpAMD64SARXLloadidx4 + OpAMD64SARXLloadidx8 + OpAMD64SARXQloadidx1 + OpAMD64SARXQloadidx8 + OpAMD64SHLXLloadidx1 + OpAMD64SHLXLloadidx4 + OpAMD64SHLXLloadidx8 + OpAMD64SHLXQloadidx1 + OpAMD64SHLXQloadidx8 + OpAMD64SHRXLloadidx1 + OpAMD64SHRXLloadidx4 + OpAMD64SHRXLloadidx8 + OpAMD64SHRXQloadidx1 + OpAMD64SHRXQloadidx8 OpARMADD OpARMADDconst @@ -1267,6 +1355,7 @@ const ( OpARMCMOVWLSconst OpARMSRAcond OpARMCALLstatic + OpARMCALLtail OpARMCALLclosure OpARMCALLinter OpARMLoweredNilCheck @@ -1342,7 +1431,6 @@ const ( OpARM64BIC OpARM64EON OpARM64ORN - OpARM64LoweredMuluhilo OpARM64MVN OpARM64NEG OpARM64NEGSflags @@ -1407,6 +1495,7 @@ const ( OpARM64MVNshiftLL OpARM64MVNshiftRL OpARM64MVNshiftRA + OpARM64MVNshiftRO OpARM64NEGshiftLL OpARM64NEGshiftRL OpARM64NEGshiftRA @@ -1419,21 +1508,27 @@ const ( OpARM64ANDshiftLL OpARM64ANDshiftRL OpARM64ANDshiftRA + OpARM64ANDshiftRO OpARM64ORshiftLL OpARM64ORshiftRL OpARM64ORshiftRA + OpARM64ORshiftRO OpARM64XORshiftLL OpARM64XORshiftRL OpARM64XORshiftRA + OpARM64XORshiftRO OpARM64BICshiftLL OpARM64BICshiftRL OpARM64BICshiftRA + OpARM64BICshiftRO OpARM64EONshiftLL OpARM64EONshiftRL OpARM64EONshiftRA + OpARM64EONshiftRO OpARM64ORNshiftLL OpARM64ORNshiftRL OpARM64ORNshiftRA + OpARM64ORNshiftRO OpARM64CMPshiftLL OpARM64CMPshiftRL OpARM64CMPshiftRA @@ -1443,6 +1538,7 @@ const ( OpARM64TSTshiftLL OpARM64TSTshiftRL OpARM64TSTshiftRA + OpARM64TSTshiftRO OpARM64BFI OpARM64BFXIL OpARM64SBFIZ @@ -1460,6 +1556,7 @@ const ( OpARM64MOVWload OpARM64MOVWUload OpARM64MOVDload + OpARM64LDP OpARM64FMOVSload OpARM64FMOVDload OpARM64MOVDloadidx @@ -1550,6 +1647,7 @@ const ( OpARM64CSNEG OpARM64CSETM OpARM64CALLstatic + OpARM64CALLtail OpARM64CALLclosure OpARM64CALLinter OpARM64LoweredNilCheck @@ -1610,6 +1708,133 @@ const ( OpARM64LoweredPanicBoundsA OpARM64LoweredPanicBoundsB OpARM64LoweredPanicBoundsC + OpARM64PRFM + OpARM64DMB + + OpLOONG64ADDV + OpLOONG64ADDVconst + OpLOONG64SUBV + OpLOONG64SUBVconst + OpLOONG64MULV + OpLOONG64MULVU + OpLOONG64DIVV + OpLOONG64DIVVU + OpLOONG64ADDF + OpLOONG64ADDD + OpLOONG64SUBF + OpLOONG64SUBD + OpLOONG64MULF + OpLOONG64MULD + OpLOONG64DIVF + OpLOONG64DIVD + OpLOONG64AND + OpLOONG64ANDconst + OpLOONG64OR + OpLOONG64ORconst + OpLOONG64XOR + OpLOONG64XORconst + OpLOONG64NOR + OpLOONG64NORconst + OpLOONG64NEGV + OpLOONG64NEGF + OpLOONG64NEGD + OpLOONG64SQRTD + OpLOONG64SQRTF + OpLOONG64SLLV + OpLOONG64SLLVconst + OpLOONG64SRLV + OpLOONG64SRLVconst + OpLOONG64SRAV + OpLOONG64SRAVconst + OpLOONG64ROTR + OpLOONG64ROTRV + OpLOONG64ROTRconst + OpLOONG64ROTRVconst + OpLOONG64SGT + OpLOONG64SGTconst + OpLOONG64SGTU + OpLOONG64SGTUconst + OpLOONG64CMPEQF + OpLOONG64CMPEQD + OpLOONG64CMPGEF + OpLOONG64CMPGED + OpLOONG64CMPGTF + OpLOONG64CMPGTD + OpLOONG64MOVVconst + OpLOONG64MOVFconst + OpLOONG64MOVDconst + OpLOONG64MOVVaddr + OpLOONG64MOVBload + OpLOONG64MOVBUload + OpLOONG64MOVHload + OpLOONG64MOVHUload + OpLOONG64MOVWload + OpLOONG64MOVWUload + OpLOONG64MOVVload + OpLOONG64MOVFload + OpLOONG64MOVDload + OpLOONG64MOVBstore + OpLOONG64MOVHstore + OpLOONG64MOVWstore + OpLOONG64MOVVstore + OpLOONG64MOVFstore + OpLOONG64MOVDstore + OpLOONG64MOVBstorezero + OpLOONG64MOVHstorezero + OpLOONG64MOVWstorezero + OpLOONG64MOVVstorezero + OpLOONG64MOVBreg + OpLOONG64MOVBUreg + OpLOONG64MOVHreg + OpLOONG64MOVHUreg + OpLOONG64MOVWreg + OpLOONG64MOVWUreg + OpLOONG64MOVVreg + OpLOONG64MOVVnop + OpLOONG64MOVWF + OpLOONG64MOVWD + OpLOONG64MOVVF + OpLOONG64MOVVD + OpLOONG64TRUNCFW + OpLOONG64TRUNCDW + OpLOONG64TRUNCFV + OpLOONG64TRUNCDV + OpLOONG64MOVFD + OpLOONG64MOVDF + OpLOONG64CALLstatic + OpLOONG64CALLtail + OpLOONG64CALLclosure + OpLOONG64CALLinter + OpLOONG64DUFFZERO + OpLOONG64DUFFCOPY + OpLOONG64LoweredZero + OpLOONG64LoweredMove + OpLOONG64LoweredAtomicLoad8 + OpLOONG64LoweredAtomicLoad32 + OpLOONG64LoweredAtomicLoad64 + OpLOONG64LoweredAtomicStore8 + OpLOONG64LoweredAtomicStore32 + OpLOONG64LoweredAtomicStore64 + OpLOONG64LoweredAtomicStorezero32 + OpLOONG64LoweredAtomicStorezero64 + OpLOONG64LoweredAtomicExchange32 + OpLOONG64LoweredAtomicExchange64 + OpLOONG64LoweredAtomicAdd32 + OpLOONG64LoweredAtomicAdd64 + OpLOONG64LoweredAtomicAddconst32 + OpLOONG64LoweredAtomicAddconst64 + OpLOONG64LoweredAtomicCas32 + OpLOONG64LoweredAtomicCas64 + OpLOONG64LoweredNilCheck + OpLOONG64FPFlagTrue + OpLOONG64FPFlagFalse + OpLOONG64LoweredGetClosurePtr + OpLOONG64LoweredGetCallerSP + OpLOONG64LoweredGetCallerPC + OpLOONG64LoweredWB + OpLOONG64LoweredPanicBoundsA + OpLOONG64LoweredPanicBoundsB + OpLOONG64LoweredPanicBoundsC OpMIPSADD OpMIPSADDconst @@ -1694,6 +1919,7 @@ const ( OpMIPSMOVFD OpMIPSMOVDF OpMIPSCALLstatic + OpMIPSCALLtail OpMIPSCALLclosure OpMIPSCALLinter OpMIPSLoweredAtomicLoad8 @@ -1810,6 +2036,7 @@ const ( OpMIPS64MOVFD OpMIPS64MOVDF OpMIPS64CALLstatic + OpMIPS64CALLtail OpMIPS64CALLclosure OpMIPS64CALLinter OpMIPS64DUFFZERO @@ -1878,7 +2105,14 @@ const ( OpPPC64RLDICL OpPPC64CLRLSLWI OpPPC64CLRLSLDI - OpPPC64LoweredAdd64Carry + OpPPC64ADDC + OpPPC64SUBC + OpPPC64ADDCconst + OpPPC64SUBCconst + OpPPC64ADDE + OpPPC64SUBE + OpPPC64ADDZEzero + OpPPC64SUBZEzero OpPPC64SRADconst OpPPC64SRAWconst OpPPC64SRDconst @@ -1938,7 +2172,6 @@ const ( OpPPC64FCPSGN OpPPC64ORconst OpPPC64XORconst - OpPPC64ANDconst OpPPC64ANDCCconst OpPPC64MOVBreg OpPPC64MOVBZreg @@ -1966,6 +2199,7 @@ const ( OpPPC64MOVDBRloadidx OpPPC64FMOVDloadidx OpPPC64FMOVSloadidx + OpPPC64DCBT OpPPC64MOVDBRstore OpPPC64MOVWBRstore OpPPC64MOVHBRstore @@ -2022,6 +2256,7 @@ const ( OpPPC64LoweredRound32F OpPPC64LoweredRound64F OpPPC64CALLstatic + OpPPC64CALLtail OpPPC64CALLclosure OpPPC64CALLinter OpPPC64LoweredZero @@ -2050,6 +2285,7 @@ const ( OpPPC64LoweredAtomicOr8 OpPPC64LoweredAtomicOr32 OpPPC64LoweredWB + OpPPC64LoweredPubBarrier OpPPC64LoweredPanicBoundsA OpPPC64LoweredPanicBoundsB OpPPC64LoweredPanicBoundsC @@ -2069,6 +2305,8 @@ const ( OpRISCV64MULW OpRISCV64MULH OpRISCV64MULHU + OpRISCV64LoweredMuluhilo + OpRISCV64LoweredMuluover OpRISCV64DIV OpRISCV64DIVU OpRISCV64DIVW @@ -2123,6 +2361,7 @@ const ( OpRISCV64SLTIU OpRISCV64MOVconvert OpRISCV64CALLstatic + OpRISCV64CALLtail OpRISCV64CALLclosure OpRISCV64CALLinter OpRISCV64DUFFZERO @@ -2172,8 +2411,14 @@ const ( OpRISCV64FSUBD OpRISCV64FMULD OpRISCV64FDIVD + OpRISCV64FMADDD + OpRISCV64FMSUBD + OpRISCV64FNMADDD + OpRISCV64FNMSUBD OpRISCV64FSQRTD OpRISCV64FNEGD + OpRISCV64FABSD + OpRISCV64FSGNJD OpRISCV64FMVDX OpRISCV64FCVTDW OpRISCV64FCVTDL @@ -2375,6 +2620,7 @@ const ( OpS390XMOVDstoreconst OpS390XCLEAR OpS390XCALLstatic + OpS390XCALLtail OpS390XCALLclosure OpS390XCALLinter OpS390XInvertFlags @@ -2428,6 +2674,7 @@ const ( OpS390XLoweredZero OpWasmLoweredStaticCall + OpWasmLoweredTailCall OpWasmLoweredClosureCall OpWasmLoweredInterCall OpWasmLoweredAddr @@ -2723,10 +2970,10 @@ const ( OpPopCount16 OpPopCount32 OpPopCount64 - OpRotateLeft8 - OpRotateLeft16 - OpRotateLeft32 OpRotateLeft64 + OpRotateLeft32 + OpRotateLeft16 + OpRotateLeft8 OpSqrt OpSqrt32 OpFloor @@ -2774,9 +3021,11 @@ const ( OpClosureCall OpStaticCall OpInterCall + OpTailCall OpClosureLECall OpStaticLECall OpInterLECall + OpTailLECall OpSignExt8to16 OpSignExt8to32 OpSignExt8to64 @@ -2846,7 +3095,6 @@ const ( OpFwdRef OpUnknown OpVarDef - OpVarKill OpVarLive OpKeepAlive OpInlMark @@ -2910,8 +3158,11 @@ const ( OpAtomicAnd32Variant OpAtomicOr8Variant OpAtomicOr32Variant + OpPubBarrier OpClobber OpClobberReg + OpPrefetchCache + OpPrefetchCacheStreamed ) var opcodeTable = [...]opInfo{ @@ -4392,6 +4643,54 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ROLL", + argLen: 2, + resultInArg0: true, + clobberFlags: true, + asm: x86.AROLL, + reg: regInfo{ + inputs: []inputInfo{ + {1, 2}, // CX + {0, 239}, // AX CX DX BX BP SI DI + }, + outputs: []outputInfo{ + {0, 239}, // AX CX DX BX BP SI DI + }, + }, + }, + { + name: "ROLW", + argLen: 2, + resultInArg0: true, + clobberFlags: true, + asm: x86.AROLW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 2}, // CX + {0, 239}, // AX CX DX BX BP SI DI + }, + outputs: []outputInfo{ + {0, 239}, // AX CX DX BX BP SI DI + }, + }, + }, + { + name: "ROLB", + argLen: 2, + resultInArg0: true, + clobberFlags: true, + asm: x86.AROLB, + reg: regInfo{ + inputs: []inputInfo{ + {1, 2}, // CX + {0, 239}, // AX CX DX BX BP SI DI + }, + outputs: []outputInfo{ + {0, 239}, // AX CX DX BX BP SI DI + }, + }, + }, { name: "ROLLconst", auxType: auxInt32, @@ -4687,7 +4986,6 @@ var opcodeTable = [...]opInfo{ name: "NOTL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ANOTL, reg: regInfo{ inputs: []inputInfo{ @@ -4758,7 +5056,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPL, reg: regInfo{ inputs: []inputInfo{ @@ -5893,6 +6190,17 @@ var opcodeTable = [...]opInfo{ clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -10721,7 +11029,6 @@ var opcodeTable = [...]opInfo{ name: "NOTQ", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ANOTQ, reg: regInfo{ inputs: []inputInfo{ @@ -10736,7 +11043,6 @@ var opcodeTable = [...]opInfo{ name: "NOTL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ANOTL, reg: regInfo{ inputs: []inputInfo{ @@ -11440,7 +11746,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPQ", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPQ, reg: regInfo{ inputs: []inputInfo{ @@ -11455,7 +11760,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPL, reg: regInfo{ inputs: []inputInfo{ @@ -12623,19 +12927,6 @@ var opcodeTable = [...]opInfo{ }, }, }, - { - name: "MOVOstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: x86.AMOVUPS, - reg: regInfo{ - inputs: []inputInfo{ - {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB - }, - }, - }, { name: "MOVBloadidx1", auxType: auxSymOff, @@ -12952,6 +13243,19 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "MOVOstoreconst", + auxType: auxSymValAndOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: x86.AMOVUPS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + }, + }, { name: "MOVBstoreconstidx1", auxType: auxSymValAndOff, @@ -13090,6 +13394,17 @@ var opcodeTable = [...]opInfo{ clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -13553,849 +13868,944 @@ var opcodeTable = [...]opInfo{ }, }, }, - { - name: "ADD", - argLen: 2, - commutative: true, - asm: arm.AADD, + name: "PrefetchT0", + argLen: 2, + hasSideEffects: true, + asm: x86.APREFETCHT0, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - }, - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "ADDconst", - auxType: auxInt32, - argLen: 1, - asm: arm.AADD, + name: "PrefetchNTA", + argLen: 2, + hasSideEffects: true, + asm: x86.APREFETCHNTA, reg: regInfo{ inputs: []inputInfo{ - {0, 30719}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 - }, - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "SUB", - argLen: 2, - asm: arm.ASUB, + name: "ANDNQ", + argLen: 2, + clobberFlags: true, + asm: x86.AANDNQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "SUBconst", - auxType: auxInt32, - argLen: 1, - asm: arm.ASUB, + name: "ANDNL", + argLen: 2, + clobberFlags: true, + asm: x86.AANDNL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "RSB", - argLen: 2, - asm: arm.ARSB, + name: "BLSIQ", + argLen: 1, + clobberFlags: true, + asm: x86.ABLSIQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "RSBconst", - auxType: auxInt32, - argLen: 1, - asm: arm.ARSB, + name: "BLSIL", + argLen: 1, + clobberFlags: true, + asm: x86.ABLSIL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MUL", - argLen: 2, - commutative: true, - asm: arm.AMUL, + name: "BLSMSKQ", + argLen: 1, + clobberFlags: true, + asm: x86.ABLSMSKQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "HMUL", - argLen: 2, - commutative: true, - asm: arm.AMULL, + name: "BLSMSKL", + argLen: 1, + clobberFlags: true, + asm: x86.ABLSMSKL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "HMULU", - argLen: 2, - commutative: true, - asm: arm.AMULLU, + name: "BLSRQ", + argLen: 1, + clobberFlags: true, + asm: x86.ABLSRQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "CALLudiv", - argLen: 2, + name: "BLSRL", + argLen: 1, clobberFlags: true, + asm: x86.ABLSRL, reg: regInfo{ inputs: []inputInfo{ - {0, 2}, // R1 - {1, 1}, // R0 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, - clobbers: 20492, // R2 R3 R12 R14 outputs: []outputInfo{ - {0, 1}, // R0 - {1, 2}, // R1 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADDS", - argLen: 2, - commutative: true, - asm: arm.AADD, + name: "TZCNTQ", + argLen: 1, + clobberFlags: true, + asm: x86.ATZCNTQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADDSconst", - auxType: auxInt32, - argLen: 1, - asm: arm.AADD, + name: "TZCNTL", + argLen: 1, + clobberFlags: true, + asm: x86.ATZCNTL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADC", - argLen: 3, - commutative: true, - asm: arm.AADC, + name: "LZCNTQ", + argLen: 1, + clobberFlags: true, + asm: x86.ALZCNTQ, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADCconst", - auxType: auxInt32, - argLen: 2, - asm: arm.AADC, + name: "LZCNTL", + argLen: 1, + clobberFlags: true, + asm: x86.ALZCNTL, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "SUBS", - argLen: 2, - asm: arm.ASUB, + name: "MOVBEWstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: x86.AMOVBEW, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - }, - outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "SUBSconst", - auxType: auxInt32, - argLen: 1, - asm: arm.ASUB, + name: "MOVBELload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.AMOVBEL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "RSBSconst", - auxType: auxInt32, - argLen: 1, - asm: arm.ARSB, + name: "MOVBELstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: x86.AMOVBEL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - }, - outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "SBC", - argLen: 3, - asm: arm.ASBC, + name: "MOVBEQload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.AMOVBEQ, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "SBCconst", - auxType: auxInt32, - argLen: 2, - asm: arm.ASBC, + name: "MOVBEQstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: x86.AMOVBEQ, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - }, - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "RSCconst", - auxType: auxInt32, - argLen: 2, - asm: arm.ARSC, + name: "MOVBELloadidx1", + auxType: auxSymOff, + argLen: 3, + commutative: true, + symEffect: SymRead, + asm: x86.AMOVBEL, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULLU", - argLen: 2, - commutative: true, - asm: arm.AMULLU, + name: "MOVBELloadidx4", + auxType: auxSymOff, + argLen: 3, + symEffect: SymRead, + asm: x86.AMOVBEL, + scale: 4, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULA", - argLen: 3, - asm: arm.AMULA, + name: "MOVBELloadidx8", + auxType: auxSymOff, + argLen: 3, + symEffect: SymRead, + asm: x86.AMOVBEL, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULS", - argLen: 3, - asm: arm.AMULS, + name: "MOVBEQloadidx1", + auxType: auxSymOff, + argLen: 3, + commutative: true, + symEffect: SymRead, + asm: x86.AMOVBEQ, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADDF", - argLen: 2, - commutative: true, - asm: arm.AADDF, + name: "MOVBEQloadidx8", + auxType: auxSymOff, + argLen: 3, + symEffect: SymRead, + asm: x86.AMOVBEQ, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ADDD", - argLen: 2, + name: "MOVBEWstoreidx1", + auxType: auxSymOff, + argLen: 4, commutative: true, - asm: arm.AADDD, + symEffect: SymWrite, + asm: x86.AMOVBEW, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "SUBF", - argLen: 2, - asm: arm.ASUBF, + name: "MOVBEWstoreidx2", + auxType: auxSymOff, + argLen: 4, + symEffect: SymWrite, + asm: x86.AMOVBEW, + scale: 2, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "SUBD", - argLen: 2, - asm: arm.ASUBD, + name: "MOVBELstoreidx1", + auxType: auxSymOff, + argLen: 4, + commutative: true, + symEffect: SymWrite, + asm: x86.AMOVBEL, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "MULF", - argLen: 2, - commutative: true, - asm: arm.AMULF, + name: "MOVBELstoreidx4", + auxType: auxSymOff, + argLen: 4, + symEffect: SymWrite, + asm: x86.AMOVBEL, + scale: 4, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "MULD", - argLen: 2, - commutative: true, - asm: arm.AMULD, + name: "MOVBELstoreidx8", + auxType: auxSymOff, + argLen: 4, + symEffect: SymWrite, + asm: x86.AMOVBEL, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "NMULF", - argLen: 2, + name: "MOVBEQstoreidx1", + auxType: auxSymOff, + argLen: 4, commutative: true, - asm: arm.ANMULF, + symEffect: SymWrite, + asm: x86.AMOVBEQ, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "NMULD", - argLen: 2, - commutative: true, - asm: arm.ANMULD, + name: "MOVBEQstoreidx8", + auxType: auxSymOff, + argLen: 4, + symEffect: SymWrite, + asm: x86.AMOVBEQ, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - }, - outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, }, }, { - name: "DIVF", + name: "SARXQ", argLen: 2, - asm: arm.ADIVF, + asm: x86.ASARXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "DIVD", + name: "SARXL", argLen: 2, - asm: arm.ADIVD, + asm: x86.ASARXL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULAF", - argLen: 3, - resultInArg0: true, - asm: arm.AMULAF, + name: "SHLXQ", + argLen: 2, + asm: x86.ASHLXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULAD", - argLen: 3, - resultInArg0: true, - asm: arm.AMULAD, + name: "SHLXL", + argLen: 2, + asm: x86.ASHLXL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULSF", - argLen: 3, - resultInArg0: true, - asm: arm.AMULSF, + name: "SHRXQ", + argLen: 2, + asm: x86.ASHRXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MULSD", - argLen: 3, - resultInArg0: true, - asm: arm.AMULSD, + name: "SHRXL", + argLen: 2, + asm: x86.ASHRXL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "FMULAD", - argLen: 3, - resultInArg0: true, - asm: arm.AFMULAD, + name: "SARXLload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "AND", - argLen: 2, - commutative: true, - asm: arm.AAND, + name: "SARXQload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ANDconst", - auxType: auxInt32, - argLen: 1, - asm: arm.AAND, + name: "SHLXLload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "OR", - argLen: 2, - commutative: true, - asm: arm.AORR, + name: "SHLXQload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ORconst", - auxType: auxInt32, - argLen: 1, - asm: arm.AORR, + name: "SHRXLload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "XOR", - argLen: 2, - commutative: true, - asm: arm.AEOR, + name: "SHRXQload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "XORconst", - auxType: auxInt32, - argLen: 1, - asm: arm.AEOR, + name: "SARXLloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXL, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "BIC", - argLen: 2, - asm: arm.ABIC, + name: "SARXLloadidx4", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXL, + scale: 4, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "BICconst", - auxType: auxInt32, - argLen: 1, - asm: arm.ABIC, + name: "SARXLloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXL, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "BFX", - auxType: auxInt32, - argLen: 1, - asm: arm.ABFX, + name: "SARXQloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXQ, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "BFXU", - auxType: auxInt32, - argLen: 1, - asm: arm.ABFXU, + name: "SARXQloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASARXQ, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "MVN", - argLen: 1, - asm: arm.AMVN, + name: "SHLXLloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "NEGF", - argLen: 1, - asm: arm.ANEGF, + name: "SHLXLloadidx4", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 4, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "NEGD", - argLen: 1, - asm: arm.ANEGD, + name: "SHLXLloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "SQRTD", - argLen: 1, - asm: arm.ASQRTD, + name: "SHLXQloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "SQRTF", - argLen: 1, - asm: arm.ASQRTF, + name: "SHLXQloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "ABSD", - argLen: 1, - asm: arm.AABSD, + name: "SHRXLloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "CLZ", - argLen: 1, - asm: arm.ACLZ, + name: "SHRXLloadidx4", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 4, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "REV", - argLen: 1, - asm: arm.AREV, + name: "SHRXLloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "REV16", - argLen: 1, - asm: arm.AREV16, + name: "SHRXQloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, + scale: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, { - name: "RBIT", - argLen: 1, - asm: arm.ARBIT, + name: "SHRXQloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, + scale: 8, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 }, }, }, + { - name: "SLL", - argLen: 2, - asm: arm.ASLL, + name: "ADD", + argLen: 2, + commutative: true, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14407,13 +14817,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SLLconst", + name: "ADDconst", auxType: auxInt32, argLen: 1, - asm: arm.ASLL, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 30719}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14421,9 +14831,9 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRL", + name: "SUB", argLen: 2, - asm: arm.ASRL, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14435,10 +14845,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRLconst", + name: "SUBconst", auxType: auxInt32, argLen: 1, - asm: arm.ASRL, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14449,9 +14859,9 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRA", + name: "RSB", argLen: 2, - asm: arm.ASRA, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14463,10 +14873,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRAconst", + name: "RSBconst", auxType: auxInt32, argLen: 1, - asm: arm.ASRA, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14477,8 +14887,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRR", - argLen: 2, + name: "MUL", + argLen: 2, + commutative: true, + asm: arm.AMUL, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14490,12 +14902,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SRRconst", - auxType: auxInt32, - argLen: 1, + name: "HMUL", + argLen: 2, + commutative: true, + asm: arm.AMULL, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14503,10 +14917,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + name: "HMULU", + argLen: 2, + commutative: true, + asm: arm.AMULLU, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14518,59 +14932,61 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + name: "CALLudiv", + argLen: 2, + clobberFlags: true, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 2}, // R1 + {1, 1}, // R0 }, + clobbers: 20492, // R2 R3 R12 R14 outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 1}, // R0 + {1, 2}, // R1 }, }, }, { - name: "ADDshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + name: "ADDS", + argLen: 2, + commutative: true, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBshiftLL", + name: "ADDSconst", auxType: auxInt32, - argLen: 2, - asm: arm.ASUB, + argLen: 1, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.ASUB, + name: "ADC", + argLen: 3, + commutative: true, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14578,14 +14994,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftRA", + name: "ADCconst", auxType: auxInt32, argLen: 2, - asm: arm.ASUB, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14593,59 +15008,58 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSBshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.ARSB, + name: "SUBS", + argLen: 2, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBshiftRL", + name: "SUBSconst", auxType: auxInt32, - argLen: 2, - asm: arm.ARSB, + argLen: 1, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBshiftRA", + name: "RSBSconst", auxType: auxInt32, - argLen: 2, + argLen: 1, asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ANDshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.AAND, + name: "SBC", + argLen: 3, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14653,14 +15067,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRL", + name: "SBCconst", auxType: auxInt32, argLen: 2, - asm: arm.AAND, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14668,14 +15081,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRA", + name: "RSCconst", auxType: auxInt32, argLen: 2, - asm: arm.AAND, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14683,10 +15095,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.AORR, + name: "MULLU", + argLen: 2, + commutative: true, + asm: arm.AMULLU, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -14694,18 +15106,19 @@ var opcodeTable = [...]opInfo{ }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ORshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.AORR, + name: "MULA", + argLen: 3, + asm: arm.AMULA, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14713,14 +15126,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.AORR, + name: "MULS", + argLen: 3, + asm: arm.AMULS, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14728,236 +15141,240 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "XORshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.AEOR, - reg: regInfo{ - inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + name: "ADDF", + argLen: 2, + commutative: true, + asm: arm.AADDF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "XORshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.AEOR, + name: "ADDD", + argLen: 2, + commutative: true, + asm: arm.AADDD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "XORshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.AEOR, + name: "SUBF", + argLen: 2, + asm: arm.ASUBF, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "XORshiftRR", - auxType: auxInt32, - argLen: 2, - asm: arm.AEOR, + name: "SUBD", + argLen: 2, + asm: arm.ASUBD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "BICshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.ABIC, + name: "MULF", + argLen: 2, + commutative: true, + asm: arm.AMULF, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "BICshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.ABIC, + name: "MULD", + argLen: 2, + commutative: true, + asm: arm.AMULD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "BICshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.ABIC, + name: "NMULF", + argLen: 2, + commutative: true, + asm: arm.ANMULF, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "MVNshiftLL", - auxType: auxInt32, - argLen: 1, - asm: arm.AMVN, + name: "NMULD", + argLen: 2, + commutative: true, + asm: arm.ANMULD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "MVNshiftRL", - auxType: auxInt32, - argLen: 1, - asm: arm.AMVN, + name: "DIVF", + argLen: 2, + asm: arm.ADIVF, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "MVNshiftRA", - auxType: auxInt32, - argLen: 1, - asm: arm.AMVN, + name: "DIVD", + argLen: 2, + asm: arm.ADIVD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADCshiftLL", - auxType: auxInt32, - argLen: 3, - asm: arm.AADC, + name: "MULAF", + argLen: 3, + resultInArg0: true, + asm: arm.AMULAF, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADCshiftRL", - auxType: auxInt32, - argLen: 3, - asm: arm.AADC, + name: "MULAD", + argLen: 3, + resultInArg0: true, + asm: arm.AMULAD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADCshiftRA", - auxType: auxInt32, - argLen: 3, - asm: arm.AADC, + name: "MULSF", + argLen: 3, + resultInArg0: true, + asm: arm.AMULSF, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "SBCshiftLL", - auxType: auxInt32, - argLen: 3, - asm: arm.ASBC, + name: "MULSD", + argLen: 3, + resultInArg0: true, + asm: arm.AMULSD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "SBCshiftRL", - auxType: auxInt32, - argLen: 3, - asm: arm.ASBC, + name: "FMULAD", + argLen: 3, + resultInArg0: true, + asm: arm.AFMULAD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "SBCshiftRA", - auxType: auxInt32, - argLen: 3, - asm: arm.ASBC, + name: "AND", + argLen: 2, + commutative: true, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14965,14 +15382,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftLL", + name: "ANDconst", auxType: auxInt32, - argLen: 3, - asm: arm.ARSC, + argLen: 1, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14980,14 +15396,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftRL", - auxType: auxInt32, - argLen: 3, - asm: arm.ARSC, + name: "OR", + argLen: 2, + commutative: true, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -14995,14 +15411,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftRA", + name: "ORconst", auxType: auxInt32, - argLen: 3, - asm: arm.ARSC, + argLen: 1, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15010,203 +15425,175 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDSshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + name: "XOR", + argLen: 2, + commutative: true, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ADDSshiftRL", + name: "XORconst", auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + argLen: 1, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ADDSshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.AADD, + name: "BIC", + argLen: 2, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftLL", + name: "BICconst", auxType: auxInt32, - argLen: 2, - asm: arm.ASUB, + argLen: 1, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftRL", + name: "BFX", auxType: auxInt32, - argLen: 2, - asm: arm.ASUB, + argLen: 1, + asm: arm.ABFX, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftRA", + name: "BFXU", auxType: auxInt32, - argLen: 2, - asm: arm.ASUB, + argLen: 1, + asm: arm.ABFXU, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBSshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.ARSB, + name: "MVN", + argLen: 1, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBSshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.ARSB, + name: "NEGF", + argLen: 1, + asm: arm.ANEGF, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "RSBSshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.ARSB, + name: "NEGD", + argLen: 1, + asm: arm.ANEGD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {1, 0}, - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADDshiftLLreg", - argLen: 3, - asm: arm.AADD, + name: "SQRTD", + argLen: 1, + asm: arm.ASQRTD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADDshiftRLreg", - argLen: 3, - asm: arm.AADD, + name: "SQRTF", + argLen: 1, + asm: arm.ASQRTF, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "ADDshiftRAreg", - argLen: 3, - asm: arm.AADD, + name: "ABSD", + argLen: 1, + asm: arm.AABSD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, }, { - name: "SUBshiftLLreg", - argLen: 3, - asm: arm.ASUB, + name: "CLZ", + argLen: 1, + asm: arm.ACLZ, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15214,14 +15601,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftRLreg", - argLen: 3, - asm: arm.ASUB, + name: "REV", + argLen: 1, + asm: arm.AREV, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15229,14 +15614,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftRAreg", - argLen: 3, - asm: arm.ASUB, + name: "REV16", + argLen: 1, + asm: arm.AREV16, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15244,14 +15627,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSBshiftLLreg", - argLen: 3, - asm: arm.ARSB, + name: "RBIT", + argLen: 1, + asm: arm.ARBIT, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15259,14 +15640,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSBshiftRLreg", - argLen: 3, - asm: arm.ARSB, + name: "SLL", + argLen: 2, + asm: arm.ASLL, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15274,14 +15654,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSBshiftRAreg", - argLen: 3, - asm: arm.ARSB, + name: "SLLconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ASLL, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15289,14 +15668,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftLLreg", - argLen: 3, - asm: arm.AAND, + name: "SRL", + argLen: 2, + asm: arm.ASRL, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15304,14 +15682,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRLreg", - argLen: 3, - asm: arm.AAND, + name: "SRLconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ASRL, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15319,14 +15696,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRAreg", - argLen: 3, - asm: arm.AAND, + name: "SRA", + argLen: 2, + asm: arm.ASRA, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15334,14 +15710,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftLLreg", - argLen: 3, - asm: arm.AORR, + name: "SRAconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ASRA, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15349,14 +15724,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftRLreg", - argLen: 3, - asm: arm.AORR, + name: "SRR", + argLen: 2, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15364,14 +15737,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftRAreg", - argLen: 3, - asm: arm.AORR, + name: "SRRconst", + auxType: auxInt32, + argLen: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15379,14 +15750,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "XORshiftLLreg", - argLen: 3, - asm: arm.AEOR, + name: "ADDshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15394,14 +15765,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "XORshiftRLreg", - argLen: 3, - asm: arm.AEOR, + name: "ADDshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15409,14 +15780,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "XORshiftRAreg", - argLen: 3, - asm: arm.AEOR, + name: "ADDshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15424,14 +15795,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "BICshiftLLreg", - argLen: 3, - asm: arm.ABIC, + name: "SUBshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15439,14 +15810,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "BICshiftRLreg", - argLen: 3, - asm: arm.ABIC, + name: "SUBshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15454,14 +15825,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "BICshiftRAreg", - argLen: 3, - asm: arm.ABIC, + name: "SUBshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15469,9 +15840,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MVNshiftLLreg", - argLen: 2, - asm: arm.AMVN, + name: "RSBshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -15483,9 +15855,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MVNshiftRLreg", - argLen: 2, - asm: arm.AMVN, + name: "RSBshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -15497,9 +15870,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MVNshiftRAreg", - argLen: 2, - asm: arm.AMVN, + name: "RSBshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 @@ -15511,14 +15885,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADCshiftLLreg", - argLen: 4, - asm: arm.AADC, + name: "ANDshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15526,14 +15900,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADCshiftRLreg", - argLen: 4, - asm: arm.AADC, + name: "ANDshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15541,14 +15915,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADCshiftRAreg", - argLen: 4, - asm: arm.AADC, + name: "ANDshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15556,14 +15930,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SBCshiftLLreg", - argLen: 4, - asm: arm.ASBC, + name: "ORshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15571,14 +15945,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SBCshiftRLreg", - argLen: 4, - asm: arm.ASBC, + name: "ORshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15586,14 +15960,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SBCshiftRAreg", - argLen: 4, - asm: arm.ASBC, + name: "ORshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15601,14 +15975,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftLLreg", - argLen: 4, - asm: arm.ARSC, + name: "XORshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15616,14 +15990,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftRLreg", - argLen: 4, - asm: arm.ARSC, + name: "XORshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15631,14 +16005,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RSCshiftRAreg", - argLen: 4, - asm: arm.ARSC, + name: "XORshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -15646,616 +16020,695 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDSshiftLLreg", - argLen: 3, - asm: arm.AADD, + name: "XORshiftRR", + auxType: auxInt32, + argLen: 2, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ADDSshiftRLreg", - argLen: 3, - asm: arm.AADD, + name: "BICshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "ADDSshiftRAreg", - argLen: 3, - asm: arm.AADD, + name: "BICshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftLLreg", - argLen: 3, - asm: arm.ASUB, + name: "BICshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftRLreg", - argLen: 3, - asm: arm.ASUB, + name: "MVNshiftLL", + auxType: auxInt32, + argLen: 1, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "SUBSshiftRAreg", - argLen: 3, - asm: arm.ASUB, + name: "MVNshiftRL", + auxType: auxInt32, + argLen: 1, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBSshiftLLreg", - argLen: 3, - asm: arm.ARSB, + name: "MVNshiftRA", + auxType: auxInt32, + argLen: 1, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBSshiftRLreg", - argLen: 3, - asm: arm.ARSB, + name: "ADCshiftLL", + auxType: auxInt32, + argLen: 3, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "RSBSshiftRAreg", - argLen: 3, - asm: arm.ARSB, + name: "ADCshiftRL", + auxType: auxInt32, + argLen: 3, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ - {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMP", - argLen: 2, - asm: arm.ACMP, + name: "ADCshiftRA", + auxType: auxInt32, + argLen: 3, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPconst", + name: "SBCshiftLL", auxType: auxInt32, - argLen: 1, - asm: arm.ACMP, + argLen: 3, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, - }, - }, - { - name: "CMN", - argLen: 2, - commutative: true, - asm: arm.ACMN, - reg: regInfo{ - inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMNconst", + name: "SBCshiftRL", auxType: auxInt32, - argLen: 1, - asm: arm.ACMN, + argLen: 3, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, - }, - }, - { - name: "TST", - argLen: 2, - commutative: true, - asm: arm.ATST, - reg: regInfo{ - inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "TSTconst", + name: "SBCshiftRA", auxType: auxInt32, - argLen: 1, - asm: arm.ATST, + argLen: 3, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, - }, - }, - { - name: "TEQ", - argLen: 2, - commutative: true, - asm: arm.ATEQ, - reg: regInfo{ - inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "TEQconst", + name: "RSCshiftLL", auxType: auxInt32, - argLen: 1, - asm: arm.ATEQ, + argLen: 3, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPF", - argLen: 2, - asm: arm.ACMPF, + name: "RSCshiftRL", + auxType: auxInt32, + argLen: 3, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPD", - argLen: 2, - asm: arm.ACMPD, + name: "RSCshiftRA", + auxType: auxInt32, + argLen: 3, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPshiftLL", + name: "ADDSshiftLL", auxType: auxInt32, argLen: 2, - asm: arm.ACMP, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMPshiftRL", + name: "ADDSshiftRL", auxType: auxInt32, argLen: 2, - asm: arm.ACMP, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMPshiftRA", + name: "ADDSshiftRA", auxType: auxInt32, argLen: 2, - asm: arm.ACMP, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftLL", + name: "SUBSshiftLL", auxType: auxInt32, argLen: 2, - asm: arm.ACMN, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftRL", + name: "SUBSshiftRL", auxType: auxInt32, argLen: 2, - asm: arm.ACMN, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftRA", + name: "SUBSshiftRA", auxType: auxInt32, argLen: 2, - asm: arm.ACMN, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftLL", + name: "RSBSshiftLL", auxType: auxInt32, argLen: 2, - asm: arm.ATST, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftRL", + name: "RSBSshiftRL", auxType: auxInt32, argLen: 2, - asm: arm.ATST, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftRA", + name: "RSBSshiftRA", auxType: auxInt32, argLen: 2, - asm: arm.ATST, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TEQshiftLL", - auxType: auxInt32, - argLen: 2, - asm: arm.ATEQ, + name: "ADDshiftLLreg", + argLen: 3, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "TEQshiftRL", - auxType: auxInt32, - argLen: 2, - asm: arm.ATEQ, + name: "ADDshiftRLreg", + argLen: 3, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "TEQshiftRA", - auxType: auxInt32, - argLen: 2, - asm: arm.ATEQ, + name: "ADDshiftRAreg", + argLen: 3, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPshiftLLreg", + name: "SUBshiftLLreg", argLen: 3, - asm: arm.ACMP, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMPshiftRLreg", + name: "SUBshiftRLreg", argLen: 3, - asm: arm.ACMP, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMPshiftRAreg", + name: "SUBshiftRAreg", argLen: 3, - asm: arm.ACMP, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftLLreg", + name: "RSBshiftLLreg", argLen: 3, - asm: arm.ACMN, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftRLreg", + name: "RSBshiftRLreg", argLen: 3, - asm: arm.ACMN, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMNshiftRAreg", + name: "RSBshiftRAreg", argLen: 3, - asm: arm.ACMN, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftLLreg", + name: "ANDshiftLLreg", argLen: 3, - asm: arm.ATST, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftRLreg", + name: "ANDshiftRLreg", argLen: 3, - asm: arm.ATST, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TSTshiftRAreg", + name: "ANDshiftRAreg", argLen: 3, - asm: arm.ATST, + asm: arm.AAND, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TEQshiftLLreg", + name: "ORshiftLLreg", argLen: 3, - asm: arm.ATEQ, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TEQshiftRLreg", + name: "ORshiftRLreg", argLen: 3, - asm: arm.ATEQ, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "TEQshiftRAreg", + name: "ORshiftRAreg", argLen: 3, - asm: arm.ATEQ, + asm: arm.AORR, reg: regInfo{ inputs: []inputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, }, }, { - name: "CMPF0", - argLen: 1, - asm: arm.ACMPF, + name: "XORshiftLLreg", + argLen: 3, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "CMPD0", - argLen: 1, - asm: arm.ACMPD, + name: "XORshiftRLreg", + argLen: 3, + asm: arm.AEOR, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWconst", - auxType: auxInt32, - argLen: 0, - rematerializeable: true, - asm: arm.AMOVW, + name: "XORshiftRAreg", + argLen: 3, + asm: arm.AEOR, reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVFconst", - auxType: auxFloat64, - argLen: 0, - rematerializeable: true, - asm: arm.AMOVF, + name: "BICshiftLLreg", + argLen: 3, + asm: arm.ABIC, reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVDconst", - auxType: auxFloat64, - argLen: 0, - rematerializeable: true, - asm: arm.AMOVD, + name: "BICshiftRLreg", + argLen: 3, + asm: arm.ABIC, reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWaddr", - auxType: auxSymOff, - argLen: 1, - rematerializeable: true, - symEffect: SymAddr, - asm: arm.AMOVW, + name: "BICshiftRAreg", + argLen: 3, + asm: arm.ABIC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294975488}, // SP SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16263,15 +16716,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVBload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVB, + name: "MVNshiftLLreg", + argLen: 2, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16279,15 +16730,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVBUload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVBU, + name: "MVNshiftRLreg", + argLen: 2, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16295,15 +16744,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVHload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVH, + name: "MVNshiftRAreg", + argLen: 2, + asm: arm.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16311,15 +16758,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVHUload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVHU, + name: "ADCshiftLLreg", + argLen: 4, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16327,15 +16773,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVW, + name: "ADCshiftRLreg", + argLen: 4, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 @@ -16343,915 +16788,3327 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVFload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVF, + name: "ADCshiftRAreg", + argLen: 4, + asm: arm.AADC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVDload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm.AMOVD, + name: "SBCshiftLLreg", + argLen: 4, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVBstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm.AMOVB, + name: "SBCshiftRLreg", + argLen: 4, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVHstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm.AMOVH, + name: "SBCshiftRAreg", + argLen: 4, + asm: arm.ASBC, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm.AMOVW, + name: "RSCshiftLLreg", + argLen: 4, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVFstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm.AMOVF, + name: "RSCshiftRLreg", + argLen: 4, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVDstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm.AMOVD, + name: "RSCshiftRAreg", + argLen: 4, + asm: arm.ARSC, reg: regInfo{ inputs: []inputInfo{ - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB - {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWloadidx", + name: "ADDSshiftLLreg", argLen: 3, - asm: arm.AMOVW, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWloadshiftLL", - auxType: auxInt32, - argLen: 3, - asm: arm.AMOVW, + name: "ADDSshiftRLreg", + argLen: 3, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWloadshiftRL", - auxType: auxInt32, - argLen: 3, - asm: arm.AMOVW, + name: "ADDSshiftRAreg", + argLen: 3, + asm: arm.AADD, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWloadshiftRA", - auxType: auxInt32, - argLen: 3, - asm: arm.AMOVW, + name: "SUBSshiftLLreg", + argLen: 3, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVBUloadidx", + name: "SUBSshiftRLreg", argLen: 3, - asm: arm.AMOVBU, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVBloadidx", + name: "SUBSshiftRAreg", argLen: 3, - asm: arm.AMOVB, + asm: arm.ASUB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVHUloadidx", + name: "RSBSshiftLLreg", argLen: 3, - asm: arm.AMOVHU, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVHloadidx", + name: "RSBSshiftRLreg", argLen: 3, - asm: arm.AMOVH, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, outputs: []outputInfo{ + {1, 0}, {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWstoreidx", - argLen: 4, - asm: arm.AMOVW, + name: "RSBSshiftRAreg", + argLen: 3, + asm: arm.ARSB, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 }, }, }, { - name: "MOVWstoreshiftLL", - auxType: auxInt32, - argLen: 4, - asm: arm.AMOVW, + name: "CMP", + argLen: 2, + asm: arm.ACMP, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, }, }, { - name: "MOVWstoreshiftRL", + name: "CMPconst", auxType: auxInt32, - argLen: 4, - asm: arm.AMOVW, + argLen: 1, + asm: arm.ACMP, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, }, }, { - name: "MOVWstoreshiftRA", - auxType: auxInt32, - argLen: 4, - asm: arm.AMOVW, + name: "CMN", + argLen: 2, + commutative: true, + asm: arm.ACMN, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, }, }, { - name: "MOVBstoreidx", - argLen: 4, - asm: arm.AMOVB, + name: "CMNconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ACMN, reg: regInfo{ inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 }, }, }, { - name: "MOVHstoreidx", - argLen: 4, - asm: arm.AMOVH, - reg: regInfo{ - inputs: []inputInfo{ - {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 - {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + name: "TST", + argLen: 2, + commutative: true, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TSTconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TEQ", + argLen: 2, + commutative: true, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TEQconst", + auxType: auxInt32, + argLen: 1, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMPF", + argLen: 2, + asm: arm.ACMPF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "CMPD", + argLen: 2, + asm: arm.ACMPD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "CMPshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMPshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMPshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMNshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMNshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMNshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TSTshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TSTshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TSTshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TEQshiftLL", + auxType: auxInt32, + argLen: 2, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TEQshiftRL", + auxType: auxInt32, + argLen: 2, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "TEQshiftRA", + auxType: auxInt32, + argLen: 2, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "CMPshiftLLreg", + argLen: 3, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMPshiftRLreg", + argLen: 3, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMPshiftRAreg", + argLen: 3, + asm: arm.ACMP, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMNshiftLLreg", + argLen: 3, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMNshiftRLreg", + argLen: 3, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMNshiftRAreg", + argLen: 3, + asm: arm.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TSTshiftLLreg", + argLen: 3, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TSTshiftRLreg", + argLen: 3, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TSTshiftRAreg", + argLen: 3, + asm: arm.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TEQshiftLLreg", + argLen: 3, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TEQshiftRLreg", + argLen: 3, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "TEQshiftRAreg", + argLen: 3, + asm: arm.ATEQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMPF0", + argLen: 1, + asm: arm.ACMPF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "CMPD0", + argLen: 1, + asm: arm.ACMPD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWconst", + auxType: auxInt32, + argLen: 0, + rematerializeable: true, + asm: arm.AMOVW, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVFconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: arm.AMOVF, + reg: regInfo{ + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVDconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: arm.AMOVD, + reg: regInfo{ + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWaddr", + auxType: auxSymOff, + argLen: 1, + rematerializeable: true, + symEffect: SymAddr, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294975488}, // SP SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVBload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVB, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVBUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVBU, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVH, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVHU, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVFload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVDload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm.AMOVD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVBstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm.AMOVB, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVHstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm.AMOVH, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVWstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVFstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm.AMOVF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVDstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm.AMOVD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWloadidx", + argLen: 3, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWloadshiftLL", + auxType: auxInt32, + argLen: 3, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWloadshiftRL", + auxType: auxInt32, + argLen: 3, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWloadshiftRA", + auxType: auxInt32, + argLen: 3, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVBUloadidx", + argLen: 3, + asm: arm.AMOVBU, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVBloadidx", + argLen: 3, + asm: arm.AMOVB, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHUloadidx", + argLen: 3, + asm: arm.AMOVHU, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHloadidx", + argLen: 3, + asm: arm.AMOVH, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWstoreidx", + argLen: 4, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVWstoreshiftLL", + auxType: auxInt32, + argLen: 4, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVWstoreshiftRL", + auxType: auxInt32, + argLen: 4, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVWstoreshiftRA", + auxType: auxInt32, + argLen: 4, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVBstoreidx", + argLen: 4, + asm: arm.AMOVB, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVHstoreidx", + argLen: 4, + asm: arm.AMOVH, + reg: regInfo{ + inputs: []inputInfo{ + {1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {2, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB + }, + }, + }, + { + name: "MOVBreg", + argLen: 1, + asm: arm.AMOVBS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVBUreg", + argLen: 1, + asm: arm.AMOVBU, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHreg", + argLen: 1, + asm: arm.AMOVHS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVHUreg", + argLen: 1, + asm: arm.AMOVHU, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWreg", + argLen: 1, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWnop", + argLen: 1, + resultInArg0: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVWF", + argLen: 1, + asm: arm.AMOVWF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWD", + argLen: 1, + asm: arm.AMOVWD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWUF", + argLen: 1, + asm: arm.AMOVWF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVWUD", + argLen: 1, + asm: arm.AMOVWD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVFW", + argLen: 1, + asm: arm.AMOVFW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVDW", + argLen: 1, + asm: arm.AMOVDW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVFWU", + argLen: 1, + asm: arm.AMOVFW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVDWU", + argLen: 1, + asm: arm.AMOVDW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + clobbers: 2147483648, // F15 + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "MOVFD", + argLen: 1, + asm: arm.AMOVFD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "MOVDF", + argLen: 1, + asm: arm.AMOVDF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + outputs: []outputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + }, + { + name: "CMOVWHSconst", + auxType: auxInt32, + argLen: 2, + resultInArg0: true, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CMOVWLSconst", + auxType: auxInt32, + argLen: 2, + resultInArg0: true, + asm: arm.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "SRAcond", + argLen: 3, + asm: arm.ASRA, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "CALLstatic", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + { + name: "CALLclosure", + auxType: auxCallOff, + argLen: 3, + clobberFlags: true, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {1, 128}, // R7 + {0, 29695}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP R14 + }, + clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + { + name: "CALLinter", + auxType: auxCallOff, + argLen: 2, + clobberFlags: true, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + { + name: "LoweredNilCheck", + argLen: 2, + nilCheck: true, + faultOnNilArg0: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + }, + }, + }, + { + name: "Equal", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "NotEqual", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LessThan", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LessEqual", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "GreaterThan", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "GreaterEqual", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LessThanU", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LessEqualU", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "GreaterThanU", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "GreaterEqualU", + argLen: 1, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "DUFFZERO", + auxType: auxInt64, + argLen: 3, + faultOnNilArg0: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2}, // R1 + {1, 1}, // R0 + }, + clobbers: 20482, // R1 R12 R14 + }, + }, + { + name: "DUFFCOPY", + auxType: auxInt64, + argLen: 3, + faultOnNilArg0: true, + faultOnNilArg1: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4}, // R2 + {1, 2}, // R1 + }, + clobbers: 20487, // R0 R1 R2 R12 R14 + }, + }, + { + name: "LoweredZero", + auxType: auxInt64, + argLen: 4, + clobberFlags: true, + faultOnNilArg0: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2}, // R1 + {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 2, // R1 + }, + }, + { + name: "LoweredMove", + auxType: auxInt64, + argLen: 4, + clobberFlags: true, + faultOnNilArg0: true, + faultOnNilArg1: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4}, // R2 + {1, 2}, // R1 + {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + clobbers: 6, // R1 R2 + }, + }, + { + name: "LoweredGetClosurePtr", + argLen: 0, + zeroWidth: true, + reg: regInfo{ + outputs: []outputInfo{ + {0, 128}, // R7 + }, + }, + }, + { + name: "LoweredGetCallerSP", + argLen: 0, + rematerializeable: true, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LoweredGetCallerPC", + argLen: 0, + rematerializeable: true, + reg: regInfo{ + outputs: []outputInfo{ + {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + }, + }, + }, + { + name: "LoweredPanicBoundsA", + auxType: auxInt64, + argLen: 3, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4}, // R2 + {1, 8}, // R3 + }, + }, + }, + { + name: "LoweredPanicBoundsB", + auxType: auxInt64, + argLen: 3, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2}, // R1 + {1, 4}, // R2 + }, + }, + }, + { + name: "LoweredPanicBoundsC", + auxType: auxInt64, + argLen: 3, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1}, // R0 + {1, 2}, // R1 + }, + }, + }, + { + name: "LoweredPanicExtendA", + auxType: auxInt64, + argLen: 4, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 16}, // R4 + {1, 4}, // R2 + {2, 8}, // R3 + }, + }, + }, + { + name: "LoweredPanicExtendB", + auxType: auxInt64, + argLen: 4, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 16}, // R4 + {1, 2}, // R1 + {2, 4}, // R2 + }, + }, + }, + { + name: "LoweredPanicExtendC", + auxType: auxInt64, + argLen: 4, + call: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 16}, // R4 + {1, 1}, // R0 + {2, 2}, // R1 + }, + }, + }, + { + name: "FlagConstant", + auxType: auxFlagConstant, + argLen: 0, + reg: regInfo{}, + }, + { + name: "InvertFlags", + argLen: 1, + reg: regInfo{}, + }, + { + name: "LoweredWB", + auxType: auxSym, + argLen: 3, + clobberFlags: true, + symEffect: SymNone, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4}, // R2 + {1, 8}, // R3 + }, + clobbers: 4294922240, // R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, + + { + name: "ADCSflags", + argLen: 3, + commutative: true, + asm: arm64.AADCS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ADCzerocarry", + argLen: 1, + asm: arm64.AADC, + reg: regInfo{ + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ADD", + argLen: 2, + commutative: true, + asm: arm64.AADD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ADDconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AADD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1878786047}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ADDSconstflags", + auxType: auxInt64, + argLen: 1, + asm: arm64.AADDS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ADDSflags", + argLen: 2, + commutative: true, + asm: arm64.AADDS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "SUB", + argLen: 2, + asm: arm64.ASUB, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "SUBconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ASUB, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "SBCSflags", + argLen: 3, + asm: arm64.ASBCS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "SUBSflags", + argLen: 2, + asm: arm64.ASUBS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MUL", + argLen: 2, + commutative: true, + asm: arm64.AMUL, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MULW", + argLen: 2, + commutative: true, + asm: arm64.AMULW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MNEG", + argLen: 2, + commutative: true, + asm: arm64.AMNEG, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MNEGW", + argLen: 2, + commutative: true, + asm: arm64.AMNEGW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MULH", + argLen: 2, + commutative: true, + asm: arm64.ASMULH, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UMULH", + argLen: 2, + commutative: true, + asm: arm64.AUMULH, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MULL", + argLen: 2, + commutative: true, + asm: arm64.ASMULL, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UMULL", + argLen: 2, + commutative: true, + asm: arm64.AUMULL, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "DIV", + argLen: 2, + asm: arm64.ASDIV, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UDIV", + argLen: 2, + asm: arm64.AUDIV, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "DIVW", + argLen: 2, + asm: arm64.ASDIVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UDIVW", + argLen: 2, + asm: arm64.AUDIVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MOD", + argLen: 2, + asm: arm64.AREM, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UMOD", + argLen: 2, + asm: arm64.AUREM, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MODW", + argLen: 2, + asm: arm64.AREMW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "UMODW", + argLen: 2, + asm: arm64.AUREMW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "FADDS", + argLen: 2, + commutative: true, + asm: arm64.AFADDS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FADDD", + argLen: 2, + commutative: true, + asm: arm64.AFADDD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSUBS", + argLen: 2, + asm: arm64.AFSUBS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSUBD", + argLen: 2, + asm: arm64.AFSUBD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FMULS", + argLen: 2, + commutative: true, + asm: arm64.AFMULS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FMULD", + argLen: 2, + commutative: true, + asm: arm64.AFMULD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNMULS", + argLen: 2, + commutative: true, + asm: arm64.AFNMULS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNMULD", + argLen: 2, + commutative: true, + asm: arm64.AFNMULD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FDIVS", + argLen: 2, + asm: arm64.AFDIVS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FDIVD", + argLen: 2, + asm: arm64.AFDIVD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "AND", + argLen: 2, + commutative: true, + asm: arm64.AAND, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ANDconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AAND, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "OR", + argLen: 2, + commutative: true, + asm: arm64.AORR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ORconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AORR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "XOR", + argLen: 2, + commutative: true, + asm: arm64.AEOR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "XORconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AEOR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "BIC", + argLen: 2, + asm: arm64.ABIC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "EON", + argLen: 2, + asm: arm64.AEON, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "ORN", + argLen: 2, + asm: arm64.AORN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "MVN", + argLen: 1, + asm: arm64.AMVN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "NEG", + argLen: 1, + asm: arm64.ANEG, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "NEGSflags", + argLen: 1, + asm: arm64.ANEGS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {1, 0}, + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "NGCzerocarry", + argLen: 1, + asm: arm64.ANGC, + reg: regInfo{ + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "FABSD", + argLen: 1, + asm: arm64.AFABSD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNEGS", + argLen: 1, + asm: arm64.AFNEGS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNEGD", + argLen: 1, + asm: arm64.AFNEGD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSQRTD", + argLen: 1, + asm: arm64.AFSQRTD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSQRTS", + argLen: 1, + asm: arm64.AFSQRTS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "REV", + argLen: 1, + asm: arm64.AREV, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "REVW", + argLen: 1, + asm: arm64.AREVW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "REV16", + argLen: 1, + asm: arm64.AREV16, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "REV16W", + argLen: 1, + asm: arm64.AREV16W, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "RBIT", + argLen: 1, + asm: arm64.ARBIT, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "RBITW", + argLen: 1, + asm: arm64.ARBITW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "CLZ", + argLen: 1, + asm: arm64.ACLZ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "CLZW", + argLen: 1, + asm: arm64.ACLZW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, + { + name: "VCNT", + argLen: 1, + asm: arm64.AVCNT, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "VUADDLV", + argLen: 1, + asm: arm64.AVUADDLV, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, }, }, { - name: "MOVBreg", - argLen: 1, - asm: arm.AMOVBS, + name: "LoweredRound32F", + argLen: 1, + resultInArg0: true, + zeroWidth: true, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVBUreg", - argLen: 1, - asm: arm.AMOVBU, + name: "LoweredRound64F", + argLen: 1, + resultInArg0: true, + zeroWidth: true, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHreg", - argLen: 1, - asm: arm.AMOVHS, + name: "FMADDS", + argLen: 3, + asm: arm64.AFMADDS, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHUreg", - argLen: 1, - asm: arm.AMOVHU, + name: "FMADDD", + argLen: 3, + asm: arm64.AFMADDD, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWreg", - argLen: 1, - asm: arm.AMOVW, + name: "FNMADDS", + argLen: 3, + asm: arm64.AFNMADDS, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWnop", - argLen: 1, - resultInArg0: true, + name: "FNMADDD", + argLen: 3, + asm: arm64.AFNMADDD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWF", - argLen: 1, - asm: arm.AMOVWF, + name: "FMSUBS", + argLen: 3, + asm: arm64.AFMSUBS, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWD", - argLen: 1, - asm: arm.AMOVWD, + name: "FMSUBD", + argLen: 3, + asm: arm64.AFMSUBD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWUF", - argLen: 1, - asm: arm.AMOVWF, + name: "FNMSUBS", + argLen: 3, + asm: arm64.AFNMSUBS, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWUD", - argLen: 1, - asm: arm.AMOVWD, + name: "FNMSUBD", + argLen: 3, + asm: arm64.AFNMSUBD, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVFW", - argLen: 1, - asm: arm.AMOVFW, + name: "MADD", + argLen: 3, + asm: arm64.AMADD, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVDW", - argLen: 1, - asm: arm.AMOVDW, + name: "MADDW", + argLen: 3, + asm: arm64.AMADDW, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVFWU", - argLen: 1, - asm: arm.AMOVFW, + name: "MSUB", + argLen: 3, + asm: arm64.AMSUB, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVDWU", - argLen: 1, - asm: arm.AMOVDW, + name: "MSUBW", + argLen: 3, + asm: arm64.AMSUBW, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 2147483648, // F15 outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVFD", - argLen: 1, - asm: arm.AMOVFD, + name: "SLL", + argLen: 2, + asm: arm64.ALSL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVDF", - argLen: 1, - asm: arm.AMOVDF, + name: "SLLconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ALSL, reg: regInfo{ inputs: []inputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "CMOVWHSconst", - auxType: auxInt32, - argLen: 2, - resultInArg0: true, - asm: arm.AMOVW, + name: "SRL", + argLen: 2, + asm: arm64.ALSR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "CMOVWLSconst", - auxType: auxInt32, - argLen: 2, - resultInArg0: true, - asm: arm.AMOVW, + name: "SRLconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ALSR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "SRAcond", - argLen: 3, - asm: arm.ASRA, + name: "SRA", + argLen: 2, + asm: arm64.AASR, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "CALLstatic", - auxType: auxCallOff, - argLen: 1, - clobberFlags: true, - call: true, + name: "SRAconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AASR, reg: regInfo{ - clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "CALLclosure", - auxType: auxCallOff, - argLen: 3, - clobberFlags: true, - call: true, + name: "ROR", + argLen: 2, + asm: arm64.AROR, reg: regInfo{ inputs: []inputInfo{ - {1, 128}, // R7 - {0, 29695}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, - clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, { - name: "CALLinter", - auxType: auxCallOff, - argLen: 2, - clobberFlags: true, - call: true, + name: "RORW", + argLen: 2, + asm: arm64.ARORW, reg: regInfo{ inputs: []inputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, - clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, { - name: "LoweredNilCheck", - argLen: 2, - nilCheck: true, - faultOnNilArg0: true, + name: "RORconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.AROR, reg: regInfo{ inputs: []inputInfo{ - {0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "Equal", - argLen: 1, + name: "RORWconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ARORW, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "NotEqual", - argLen: 1, + name: "EXTRconst", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEXTR, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LessThan", - argLen: 1, + name: "EXTRWconst", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEXTRW, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LessEqual", - argLen: 1, + name: "CMP", + argLen: 2, + asm: arm64.ACMP, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "GreaterThan", - argLen: 1, + name: "CMPconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ACMP, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "GreaterEqual", - argLen: 1, + name: "CMPW", + argLen: 2, + asm: arm64.ACMPW, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "LessThanU", - argLen: 1, + name: "CMPWconst", + auxType: auxInt32, + argLen: 1, + asm: arm64.ACMPW, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "LessEqualU", - argLen: 1, + name: "CMN", + argLen: 2, + commutative: true, + asm: arm64.ACMN, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "GreaterThanU", - argLen: 1, + name: "CMNconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ACMN, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "GreaterEqualU", - argLen: 1, + name: "CMNW", + argLen: 2, + commutative: true, + asm: arm64.ACMNW, reg: regInfo{ - outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "DUFFZERO", - auxType: auxInt64, - argLen: 3, - faultOnNilArg0: true, + name: "CMNWconst", + auxType: auxInt32, + argLen: 1, + asm: arm64.ACMNW, reg: regInfo{ inputs: []inputInfo{ - {0, 2}, // R1 - {1, 1}, // R0 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 20482, // R1 R12 R14 }, }, { - name: "DUFFCOPY", - auxType: auxInt64, - argLen: 3, - faultOnNilArg0: true, - faultOnNilArg1: true, + name: "TST", + argLen: 2, + commutative: true, + asm: arm64.ATST, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 2}, // R1 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 20487, // R0 R1 R2 R12 R14 }, }, { - name: "LoweredZero", - auxType: auxInt64, - argLen: 4, - clobberFlags: true, - faultOnNilArg0: true, + name: "TSTconst", + auxType: auxInt64, + argLen: 1, + asm: arm64.ATST, reg: regInfo{ inputs: []inputInfo{ - {0, 2}, // R1 - {1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - clobbers: 2, // R1 }, }, { - name: "LoweredMove", - auxType: auxInt64, - argLen: 4, - clobberFlags: true, - faultOnNilArg0: true, - faultOnNilArg1: true, + name: "TSTW", + argLen: 2, + commutative: true, + asm: arm64.ATSTW, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 2}, // R1 - {2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + }, + }, + { + name: "TSTWconst", + auxType: auxInt32, + argLen: 1, + asm: arm64.ATSTW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + }, + }, + { + name: "FCMPS", + argLen: 2, + asm: arm64.AFCMPS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FCMPD", + argLen: 2, + asm: arm64.AFCMPD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FCMPS0", + argLen: 1, + asm: arm64.AFCMPS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FCMPD0", + argLen: 1, + asm: arm64.AFCMPD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - clobbers: 6, // R1 R2 }, }, { - name: "LoweredGetClosurePtr", - argLen: 0, - zeroWidth: true, + name: "MVNshiftLL", + auxType: auxInt64, + argLen: 1, + asm: arm64.AMVN, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 128}, // R7 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredGetCallerSP", - argLen: 0, - rematerializeable: true, + name: "MVNshiftRL", + auxType: auxInt64, + argLen: 1, + asm: arm64.AMVN, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredGetCallerPC", - argLen: 0, - rematerializeable: true, + name: "MVNshiftRA", + auxType: auxInt64, + argLen: 1, + asm: arm64.AMVN, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ - {0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicBoundsA", + name: "MVNshiftRO", auxType: auxInt64, - argLen: 3, - call: true, + argLen: 1, + asm: arm64.AMVN, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 8}, // R3 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicBoundsB", + name: "NEGshiftLL", auxType: auxInt64, - argLen: 3, - call: true, + argLen: 1, + asm: arm64.ANEG, reg: regInfo{ inputs: []inputInfo{ - {0, 2}, // R1 - {1, 4}, // R2 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicBoundsC", + name: "NEGshiftRL", auxType: auxInt64, - argLen: 3, - call: true, + argLen: 1, + asm: arm64.ANEG, reg: regInfo{ inputs: []inputInfo{ - {0, 1}, // R0 - {1, 2}, // R1 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicExtendA", + name: "NEGshiftRA", auxType: auxInt64, - argLen: 4, - call: true, + argLen: 1, + asm: arm64.ANEG, reg: regInfo{ inputs: []inputInfo{ - {0, 16}, // R4 - {1, 4}, // R2 - {2, 8}, // R3 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicExtendB", + name: "ADDshiftLL", auxType: auxInt64, - argLen: 4, - call: true, + argLen: 2, + asm: arm64.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 16}, // R4 - {1, 2}, // R1 - {2, 4}, // R2 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredPanicExtendC", + name: "ADDshiftRL", auxType: auxInt64, - argLen: 4, - call: true, + argLen: 2, + asm: arm64.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 16}, // R4 - {1, 1}, // R0 - {2, 2}, // R1 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FlagConstant", - auxType: auxFlagConstant, - argLen: 0, - reg: regInfo{}, - }, - { - name: "InvertFlags", - argLen: 1, - reg: regInfo{}, - }, - { - name: "LoweredWB", - auxType: auxSym, - argLen: 3, - clobberFlags: true, - symEffect: SymNone, + name: "ADDshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 8}, // R3 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, - clobbers: 4294922240, // R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, - { - name: "ADCSflags", - argLen: 3, - commutative: true, - asm: arm64.AADCS, + name: "SUBshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ASUB, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "ADCzerocarry", - argLen: 1, - asm: arm64.AADC, + name: "SUBshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ASUB, reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "ADD", - argLen: 2, - commutative: true, - asm: arm64.AADD, + name: "SUBshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.ASUB, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17263,13 +20120,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDconst", + name: "ANDshiftLL", auxType: auxInt64, - argLen: 1, - asm: arm64.AADD, + argLen: 2, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 1878786047}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17277,40 +20135,40 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDSconstflags", + name: "ANDshiftRL", auxType: auxInt64, - argLen: 1, - asm: arm64.AADDS, + argLen: 2, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "ADDSflags", - argLen: 2, - commutative: true, - asm: arm64.AADDS, + name: "ANDshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "SUB", - argLen: 2, - asm: arm64.ASUB, + name: "ANDshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17322,13 +20180,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBconst", + name: "ORshiftLL", auxType: auxInt64, - argLen: 1, - asm: arm64.ASUB, + argLen: 2, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17336,40 +20195,40 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SBCSflags", - argLen: 3, - asm: arm64.ASBCS, + name: "ORshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "SUBSflags", - argLen: 2, - asm: arm64.ASUBS, + name: "ORshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MUL", - argLen: 2, - commutative: true, - asm: arm64.AMUL, + name: "ORshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17381,10 +20240,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MULW", - argLen: 2, - commutative: true, - asm: arm64.AMULW, + name: "XORshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17396,10 +20255,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MNEG", - argLen: 2, - commutative: true, - asm: arm64.AMNEG, + name: "XORshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17411,10 +20270,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MNEGW", - argLen: 2, - commutative: true, - asm: arm64.AMNEGW, + name: "XORshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17426,10 +20285,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MULH", - argLen: 2, - commutative: true, - asm: arm64.ASMULH, + name: "XORshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEOR, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17441,10 +20300,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UMULH", - argLen: 2, - commutative: true, - asm: arm64.AUMULH, + name: "BICshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17456,10 +20315,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MULL", - argLen: 2, - commutative: true, - asm: arm64.ASMULL, + name: "BICshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17471,10 +20330,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UMULL", - argLen: 2, - commutative: true, - asm: arm64.AUMULL, + name: "BICshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17486,9 +20345,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "DIV", - argLen: 2, - asm: arm64.ASDIV, + name: "BICshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.ABIC, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17500,9 +20360,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UDIV", - argLen: 2, - asm: arm64.AUDIV, + name: "EONshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEON, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17514,9 +20375,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "DIVW", - argLen: 2, - asm: arm64.ASDIVW, + name: "EONshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEON, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17528,9 +20390,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UDIVW", - argLen: 2, - asm: arm64.AUDIVW, + name: "EONshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEON, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17542,9 +20405,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOD", - argLen: 2, - asm: arm64.AREM, + name: "EONshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEON, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17556,9 +20420,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UMOD", - argLen: 2, - asm: arm64.AUREM, + name: "ORNshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORN, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17570,9 +20435,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MODW", - argLen: 2, - asm: arm64.AREMW, + name: "ORNshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORN, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17584,9 +20450,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "UMODW", - argLen: 2, - asm: arm64.AUREMW, + name: "ORNshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORN, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17598,160 +20465,180 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FADDS", - argLen: 2, - commutative: true, - asm: arm64.AFADDS, + name: "ORNshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORN, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FADDD", - argLen: 2, - commutative: true, - asm: arm64.AFADDD, + name: "CMPshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMP, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FSUBS", - argLen: 2, - asm: arm64.AFSUBS, + name: "CMPshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMP, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FSUBD", - argLen: 2, - asm: arm64.AFSUBD, + name: "CMPshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMP, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FMULS", - argLen: 2, - commutative: true, - asm: arm64.AFMULS, + name: "CMNshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMN, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FMULD", - argLen: 2, - commutative: true, - asm: arm64.AFMULD, + name: "CMNshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMN, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "CMNshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.ACMN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FNMULS", - argLen: 2, - commutative: true, - asm: arm64.AFNMULS, + name: "TSTshiftLL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ATST, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "TSTshiftRL", + auxType: auxInt64, + argLen: 2, + asm: arm64.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FNMULD", - argLen: 2, - commutative: true, - asm: arm64.AFNMULD, + name: "TSTshiftRA", + auxType: auxInt64, + argLen: 2, + asm: arm64.ATST, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "TSTshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FDIVS", - argLen: 2, - asm: arm64.AFDIVS, + name: "BFI", + auxType: auxARM64BitField, + argLen: 2, + resultInArg0: true, + asm: arm64.ABFI, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FDIVD", - argLen: 2, - asm: arm64.AFDIVD, + name: "BFXIL", + auxType: auxARM64BitField, + argLen: 2, + resultInArg0: true, + asm: arm64.ABFXIL, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "AND", - argLen: 2, - commutative: true, - asm: arm64.AAND, + name: "SBFIZ", + auxType: auxARM64BitField, + argLen: 1, + asm: arm64.ASBFIZ, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17759,10 +20646,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDconst", - auxType: auxInt64, + name: "SBFX", + auxType: auxARM64BitField, argLen: 1, - asm: arm64.AAND, + asm: arm64.ASBFX, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17773,14 +20660,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "OR", - argLen: 2, - commutative: true, - asm: arm64.AORR, + name: "UBFIZ", + auxType: auxARM64BitField, + argLen: 1, + asm: arm64.AUBFIZ, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17788,10 +20674,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORconst", - auxType: auxInt64, + name: "UBFX", + auxType: auxARM64BitField, argLen: 1, - asm: arm64.AORR, + asm: arm64.AUBFX, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -17802,42 +20688,51 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "XOR", - argLen: 2, - commutative: true, - asm: arm64.AEOR, + name: "MOVDconst", + auxType: auxInt64, + argLen: 0, + rematerializeable: true, + asm: arm64.AMOVD, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "XORconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.AEOR, + name: "FMOVSconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: arm64.AFMOVS, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, + }, + }, + { + name: "FMOVDconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: arm64.AFMOVD, + reg: regInfo{ outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "BIC", - argLen: 2, - asm: arm64.ABIC, + name: "MOVDaddr", + auxType: auxSymOff, + argLen: 1, + rematerializeable: true, + symEffect: SymAddr, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372037928517632}, // SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17845,13 +20740,15 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "EON", - argLen: 2, - asm: arm64.AEON, + name: "MOVBload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17859,13 +20756,15 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORN", - argLen: 2, - asm: arm64.AORN, + name: "MOVBUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17873,27 +20772,31 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LoweredMuluhilo", - argLen: 2, - resultNotInArgs: true, + name: "MOVHload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MVN", - argLen: 1, - asm: arm64.AMVN, + name: "MOVHUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17901,12 +20804,15 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "NEG", - argLen: 1, - asm: arm64.ANEG, + name: "MOVWload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -17914,49 +20820,64 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "NEGSflags", - argLen: 1, - asm: arm64.ANEGS, + name: "MOVWUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {1, 0}, {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "NGCzerocarry", - argLen: 1, - asm: arm64.ANGC, + name: "MOVDload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AMOVD, reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FABSD", - argLen: 1, - asm: arm64.AFABSD, + name: "LDP", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.ALDP, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "FNEGS", - argLen: 1, - asm: arm64.AFNEGS, + name: "FMOVSload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -17964,12 +20885,15 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FNEGD", - argLen: 1, - asm: arm64.AFNEGD, + name: "FMOVDload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -17977,38 +20901,41 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FSQRTD", - argLen: 1, - asm: arm64.AFSQRTD, + name: "MOVDloadidx", + argLen: 3, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FSQRTS", - argLen: 1, - asm: arm64.AFSQRTS, + name: "MOVWloadidx", + argLen: 3, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "REV", - argLen: 1, - asm: arm64.AREV, + name: "MOVWUloadidx", + argLen: 3, + asm: arm64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18016,12 +20943,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "REVW", - argLen: 1, - asm: arm64.AREVW, + name: "MOVHloadidx", + argLen: 3, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18029,12 +20957,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "REV16", - argLen: 1, - asm: arm64.AREV16, + name: "MOVHUloadidx", + argLen: 3, + asm: arm64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18042,12 +20971,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "REV16W", - argLen: 1, - asm: arm64.AREV16W, + name: "MOVBloadidx", + argLen: 3, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18055,12 +20985,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RBIT", - argLen: 1, - asm: arm64.ARBIT, + name: "MOVBUloadidx", + argLen: 3, + asm: arm64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18068,38 +20999,41 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "RBITW", - argLen: 1, - asm: arm64.ARBITW, + name: "FMOVSloadidx", + argLen: 3, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CLZ", - argLen: 1, - asm: arm64.ACLZ, + name: "FMOVDloadidx", + argLen: 3, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CLZW", - argLen: 1, - asm: arm64.ACLZW, + name: "MOVHloadidx2", + argLen: 3, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18107,68 +21041,69 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "VCNT", - argLen: 1, - asm: arm64.AVCNT, + name: "MOVHUloadidx2", + argLen: 3, + asm: arm64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "VUADDLV", - argLen: 1, - asm: arm64.AVUADDLV, + name: "MOVWloadidx4", + argLen: 3, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredRound32F", - argLen: 1, - resultInArg0: true, - zeroWidth: true, + name: "MOVWUloadidx4", + argLen: 3, + asm: arm64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "LoweredRound64F", - argLen: 1, - resultInArg0: true, - zeroWidth: true, + name: "MOVDloadidx8", + argLen: 3, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FMADDS", + name: "FMOVSloadidx4", argLen: 3, - asm: arm64.AFMADDS, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -18176,14 +21111,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMADDD", + name: "FMOVDloadidx8", argLen: 3, - asm: arm64.AFMADDD, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -18191,626 +21125,645 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FNMADDS", - argLen: 3, - asm: arm64.AFNMADDS, + name: "MOVBstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "FNMADDD", - argLen: 3, - asm: arm64.AFNMADDD, + name: "MOVHstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "MOVWstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "FMSUBS", - argLen: 3, - asm: arm64.AFMSUBS, + name: "MOVDstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "STP", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.ASTP, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "FMSUBD", - argLen: 3, - asm: arm64.AFMSUBD, + name: "FMOVSstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FNMSUBS", - argLen: 3, - asm: arm64.AFNMSUBS, + name: "FMOVDstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "MOVBstoreidx", + argLen: 4, + asm: arm64.AMOVB, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "FNMSUBD", - argLen: 3, - asm: arm64.AFNMSUBD, + name: "MOVHstoreidx", + argLen: 4, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "MOVWstoreidx", + argLen: 4, + asm: arm64.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "MADD", - argLen: 3, - asm: arm64.AMADD, + name: "MOVDstoreidx", + argLen: 4, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "MADDW", - argLen: 3, - asm: arm64.AMADDW, + name: "FMOVSstoreidx", + argLen: 4, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MSUB", - argLen: 3, - asm: arm64.AMSUB, + name: "FMOVDstoreidx", + argLen: 4, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MSUBW", - argLen: 3, - asm: arm64.AMSUBW, + name: "MOVHstoreidx2", + argLen: 4, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "SLL", - argLen: 2, - asm: arm64.ALSL, + name: "MOVWstoreidx4", + argLen: 4, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "SLLconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ALSL, + name: "MOVDstoreidx8", + argLen: 4, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "SRL", - argLen: 2, - asm: arm64.ALSR, + name: "FMOVSstoreidx4", + argLen: 4, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "SRLconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ALSR, + name: "FMOVDstoreidx8", + argLen: 4, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "SRA", - argLen: 2, - asm: arm64.AASR, + name: "MOVBstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "SRAconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.AASR, + name: "MOVHstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "ROR", - argLen: 2, - asm: arm64.AROR, + name: "MOVWstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "RORW", - argLen: 2, - asm: arm64.ARORW, + name: "MOVDstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "RORconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.AROR, + name: "MOVQstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.ASTP, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "RORWconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ARORW, + name: "MOVBstorezeroidx", + argLen: 3, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "EXTRconst", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEXTR, + name: "MOVHstorezeroidx", + argLen: 3, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "EXTRWconst", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEXTRW, + name: "MOVWstorezeroidx", + argLen: 3, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "CMP", - argLen: 2, - asm: arm64.ACMP, + name: "MOVDstorezeroidx", + argLen: 3, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "CMPconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ACMP, + name: "MOVHstorezeroidx2", + argLen: 3, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "CMPW", - argLen: 2, - asm: arm64.ACMPW, + name: "MOVWstorezeroidx4", + argLen: 3, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "CMPWconst", - auxType: auxInt32, - argLen: 1, - asm: arm64.ACMPW, + name: "MOVDstorezeroidx8", + argLen: 3, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "CMN", - argLen: 2, - commutative: true, - asm: arm64.ACMN, + name: "FMOVDgpfp", + argLen: 1, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CMNconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ACMN, + name: "FMOVDfpgp", + argLen: 1, + asm: arm64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "CMNW", - argLen: 2, - commutative: true, - asm: arm64.ACMNW, + name: "FMOVSgpfp", + argLen: 1, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CMNWconst", - auxType: auxInt32, - argLen: 1, - asm: arm64.ACMNW, + name: "FMOVSfpgp", + argLen: 1, + asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TST", - argLen: 2, - commutative: true, - asm: arm64.ATST, + name: "MOVBreg", + argLen: 1, + asm: arm64.AMOVB, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TSTconst", - auxType: auxInt64, - argLen: 1, - asm: arm64.ATST, + name: "MOVBUreg", + argLen: 1, + asm: arm64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "TSTW", - argLen: 2, - commutative: true, - asm: arm64.ATSTW, + name: "MOVHreg", + argLen: 1, + asm: arm64.AMOVH, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TSTWconst", - auxType: auxInt32, - argLen: 1, - asm: arm64.ATSTW, + name: "MOVHUreg", + argLen: 1, + asm: arm64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "FCMPS", - argLen: 2, - asm: arm64.AFCMPS, + name: "MOVWreg", + argLen: 1, + asm: arm64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FCMPD", - argLen: 2, - asm: arm64.AFCMPD, + name: "MOVWUreg", + argLen: 1, + asm: arm64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FCMPS0", + name: "MOVDreg", argLen: 1, - asm: arm64.AFCMPS, + asm: arm64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FCMPD0", - argLen: 1, - asm: arm64.AFCMPD, + name: "MOVDnop", + argLen: 1, + resultInArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MVNshiftLL", - auxType: auxInt64, - argLen: 1, - asm: arm64.AMVN, + name: "SCVTFWS", + argLen: 1, + asm: arm64.ASCVTFWS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MVNshiftRL", - auxType: auxInt64, - argLen: 1, - asm: arm64.AMVN, + name: "SCVTFWD", + argLen: 1, + asm: arm64.ASCVTFWD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MVNshiftRA", - auxType: auxInt64, - argLen: 1, - asm: arm64.AMVN, + name: "UCVTFWS", + argLen: 1, + asm: arm64.AUCVTFWS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NEGshiftLL", - auxType: auxInt64, - argLen: 1, - asm: arm64.ANEG, + name: "UCVTFWD", + argLen: 1, + asm: arm64.AUCVTFWD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NEGshiftRL", - auxType: auxInt64, - argLen: 1, - asm: arm64.ANEG, + name: "SCVTFS", + argLen: 1, + asm: arm64.ASCVTFS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NEGshiftRA", - auxType: auxInt64, - argLen: 1, - asm: arm64.ANEG, + name: "SCVTFD", + argLen: 1, + asm: arm64.ASCVTFD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "ADDshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AADD, + name: "UCVTFS", + argLen: 1, + asm: arm64.AUCVTFS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "ADDshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AADD, + name: "UCVTFD", + argLen: 1, + asm: arm64.AUCVTFD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "ADDshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AADD, + name: "FCVTZSSW", + argLen: 1, + asm: arm64.AFCVTZSSW, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18818,14 +21771,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ASUB, + name: "FCVTZSDW", + argLen: 1, + asm: arm64.AFCVTZSDW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18833,14 +21784,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ASUB, + name: "FCVTZUSW", + argLen: 1, + asm: arm64.AFCVTZUSW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18848,14 +21797,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "SUBshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.ASUB, + name: "FCVTZUDW", + argLen: 1, + asm: arm64.AFCVTZUDW, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18863,14 +21810,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AAND, + name: "FCVTZSS", + argLen: 1, + asm: arm64.AFCVTZSS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18878,14 +21823,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AAND, + name: "FCVTZSD", + argLen: 1, + asm: arm64.AFCVTZSD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18893,14 +21836,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AAND, + name: "FCVTZUS", + argLen: 1, + asm: arm64.AFCVTZUS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18908,14 +21849,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORR, + name: "FCVTZUD", + argLen: 1, + asm: arm64.AFCVTZUD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -18923,119 +21862,105 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORR, + name: "FCVTSD", + argLen: 1, + asm: arm64.AFCVTSD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "ORshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORR, + name: "FCVTDS", + argLen: 1, + asm: arm64.AFCVTDS, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "XORshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEOR, + name: "FRINTAD", + argLen: 1, + asm: arm64.AFRINTAD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "XORshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEOR, + name: "FRINTMD", + argLen: 1, + asm: arm64.AFRINTMD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "XORshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEOR, + name: "FRINTND", + argLen: 1, + asm: arm64.AFRINTND, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "BICshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ABIC, + name: "FRINTPD", + argLen: 1, + asm: arm64.AFRINTPD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "BICshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ABIC, + name: "FRINTZD", + argLen: 1, + asm: arm64.AFRINTZD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "BICshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.ABIC, + name: "CSEL", + auxType: auxCCop, + argLen: 3, + asm: arm64.ACSEL, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19043,14 +21968,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "EONshiftLL", - auxType: auxInt64, + name: "CSEL0", + auxType: auxCCop, argLen: 2, - asm: arm64.AEON, + asm: arm64.ACSEL, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19058,14 +21982,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "EONshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEON, + name: "CSINC", + auxType: auxCCop, + argLen: 3, + asm: arm64.ACSINC, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19073,14 +21997,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "EONshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AEON, + name: "CSINV", + auxType: auxCCop, + argLen: 3, + asm: arm64.ACSINV, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19088,14 +22012,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORNshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORN, + name: "CSNEG", + auxType: auxCCop, + argLen: 3, + asm: arm64.ACSNEG, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19103,237 +22027,195 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ORNshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORN, + name: "CSETM", + auxType: auxCCop, + argLen: 1, + asm: arm64.ACSETM, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "ORNshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.AORN, + name: "CALLstatic", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, + clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "CMPshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMP, + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, + tailCall: true, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, + clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "CMPshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMP, + name: "CALLclosure", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 67108864}, // R26 + {0, 1744568319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 SP }, + clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "CMPshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMP, + name: "CALLinter", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, + clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "CMNshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMN, + name: "LoweredNilCheck", + argLen: 2, + nilCheck: true, + faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 }, }, }, { - name: "CMNshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMN, + name: "Equal", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "CMNshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.ACMN, + name: "NotEqual", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TSTshiftLL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ATST, + name: "LessThan", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TSTshiftRL", - auxType: auxInt64, - argLen: 2, - asm: arm64.ATST, + name: "LessEqual", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "TSTshiftRA", - auxType: auxInt64, - argLen: 2, - asm: arm64.ATST, + name: "GreaterThan", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "BFI", - auxType: auxARM64BitField, - argLen: 2, - resultInArg0: true, - asm: arm64.ABFI, + name: "GreaterEqual", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ + outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, + }, + }, + { + name: "LessThanU", + argLen: 1, + reg: regInfo{ outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "BFXIL", - auxType: auxARM64BitField, - argLen: 2, - resultInArg0: true, - asm: arm64.ABFXIL, + name: "LessEqualU", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ + outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, + }, + }, + { + name: "GreaterThanU", + argLen: 1, + reg: regInfo{ outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "SBFIZ", - auxType: auxARM64BitField, - argLen: 1, - asm: arm64.ASBFIZ, + name: "GreaterEqualU", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "SBFX", - auxType: auxARM64BitField, - argLen: 1, - asm: arm64.ASBFX, + name: "LessThanF", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "UBFIZ", - auxType: auxARM64BitField, - argLen: 1, - asm: arm64.AUBFIZ, + name: "LessEqualF", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "UBFX", - auxType: auxARM64BitField, - argLen: 1, - asm: arm64.AUBFX, + name: "GreaterThanF", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVDconst", - auxType: auxInt64, - argLen: 0, - rematerializeable: true, - asm: arm64.AMOVD, + name: "GreaterEqualF", + argLen: 1, reg: regInfo{ outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 @@ -19341,100 +22223,143 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMOVSconst", - auxType: auxFloat64, - argLen: 0, - rematerializeable: true, - asm: arm64.AFMOVS, + name: "NotLessThanF", + argLen: 1, reg: regInfo{ outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FMOVDconst", - auxType: auxFloat64, - argLen: 0, - rematerializeable: true, - asm: arm64.AFMOVD, + name: "NotLessEqualF", + argLen: 1, reg: regInfo{ outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVDaddr", - auxType: auxSymOff, - argLen: 1, - rematerializeable: true, - symEffect: SymAddr, - asm: arm64.AMOVD, + name: "NotGreaterThanF", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {0, 9223372037928517632}, // SP SB + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, + }, + }, + { + name: "NotGreaterEqualF", + argLen: 1, + reg: regInfo{ outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVBload", - auxType: auxSymOff, + name: "DUFFZERO", + auxType: auxInt64, argLen: 2, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVB, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1048576}, // R20 + }, + clobbers: 538116096, // R16 R17 R20 R30 + }, + }, + { + name: "LoweredZero", + argLen: 3, + clobberFlags: true, + faultOnNilArg0: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 65536}, // R16 + {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + clobbers: 65536, // R16 + }, + }, + { + name: "DUFFCOPY", + auxType: auxInt64, + argLen: 3, + faultOnNilArg0: true, + faultOnNilArg1: true, + unsafePoint: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2097152}, // R21 + {1, 1048576}, // R20 + }, + clobbers: 607322112, // R16 R17 R20 R21 R26 R30 + }, + }, + { + name: "LoweredMove", + argLen: 4, + clobberFlags: true, + faultOnNilArg0: true, + faultOnNilArg1: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 131072}, // R17 + {1, 65536}, // R16 + {2, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, + clobbers: 33751040, // R16 R17 R25 + }, + }, + { + name: "LoweredGetClosurePtr", + argLen: 0, + zeroWidth: true, + reg: regInfo{ outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 67108864}, // R26 }, }, }, { - name: "MOVBUload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVBU, + name: "LoweredGetCallerSP", + argLen: 0, + rematerializeable: true, reg: regInfo{ - inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVHload", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVH, + name: "LoweredGetCallerPC", + argLen: 0, + rematerializeable: true, reg: regInfo{ - inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVHUload", - auxType: auxSymOff, + name: "FlagConstant", + auxType: auxFlagConstant, + argLen: 0, + reg: regInfo{}, + }, + { + name: "InvertFlags", + argLen: 1, + reg: regInfo{}, + }, + { + name: "LDAR", argLen: 2, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVHU, + asm: arm64.ALDAR, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB @@ -19445,12 +22370,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWload", - auxType: auxSymOff, + name: "LDARB", argLen: 2, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVW, + asm: arm64.ALDARB, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB @@ -19461,12 +22384,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWUload", - auxType: auxSymOff, + name: "LDARW", argLen: 2, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVWU, + asm: arm64.ALDARW, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB @@ -19477,57 +22398,51 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVDload", - auxType: auxSymOff, - argLen: 2, + name: "STLRB", + argLen: 3, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AMOVD, + hasSideEffects: true, + asm: arm64.ASTLRB, reg: regInfo{ inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, }, }, { - name: "FMOVSload", - auxType: auxSymOff, - argLen: 2, + name: "STLR", + argLen: 3, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AFMOVS, + hasSideEffects: true, + asm: arm64.ASTLR, reg: regInfo{ inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, }, }, { - name: "FMOVDload", - auxType: auxSymOff, - argLen: 2, + name: "STLRW", + argLen: 3, faultOnNilArg0: true, - symEffect: SymRead, - asm: arm64.AFMOVD, + hasSideEffects: true, + asm: arm64.ASTLRW, reg: regInfo{ inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, - outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, }, }, { - name: "MOVDloadidx", - argLen: 3, - asm: arm64.AMOVD, + name: "LoweredAtomicExchange64", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19539,9 +22454,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWloadidx", - argLen: 3, - asm: arm64.AMOVW, + name: "LoweredAtomicExchange32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19553,9 +22471,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWUloadidx", - argLen: 3, - asm: arm64.AMOVWU, + name: "LoweredAtomicExchange64Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19567,9 +22487,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVHloadidx", - argLen: 3, - asm: arm64.AMOVH, + name: "LoweredAtomicExchange32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19581,9 +22503,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVHUloadidx", - argLen: 3, - asm: arm64.AMOVHU, + name: "LoweredAtomicAdd64", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19595,9 +22520,12 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVBloadidx", - argLen: 3, - asm: arm64.AMOVB, + name: "LoweredAtomicAdd32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19609,9 +22537,11 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVBUloadidx", - argLen: 3, - asm: arm64.AMOVBU, + name: "LoweredAtomicAdd64Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19623,40 +22553,52 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMOVSloadidx", - argLen: 3, - asm: arm64.AFMOVS, + name: "LoweredAtomicAdd32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FMOVDloadidx", - argLen: 3, - asm: arm64.AFMOVD, + name: "LoweredAtomicCas64", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVHloadidx2", - argLen: 3, - asm: arm64.AMOVH, + name: "LoweredAtomicCas32", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ @@ -19665,12 +22607,17 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVHUloadidx2", - argLen: 3, - asm: arm64.AMOVHU, + name: "LoweredAtomicCas64Variant", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ @@ -19679,12 +22626,17 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWloadidx4", - argLen: 3, - asm: arm64.AMOVW, + name: "LoweredAtomicCas32Variant", + argLen: 4, + resultNotInArgs: true, + clobberFlags: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ @@ -19693,9 +22645,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVWUloadidx4", - argLen: 3, - asm: arm64.AMOVWU, + name: "LoweredAtomicAnd8", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19707,9 +22663,13 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "MOVDloadidx8", - argLen: 3, - asm: arm64.AMOVD, + name: "LoweredAtomicAnd32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 @@ -19721,1402 +22681,1636 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMOVSloadidx4", - argLen: 3, - asm: arm64.AFMOVS, + name: "LoweredAtomicOr8", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "FMOVDloadidx8", - argLen: 3, - asm: arm64.AFMOVD, + name: "LoweredAtomicOr32", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, + asm: arm64.AORR, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 }, }, }, { - name: "MOVBstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVB, + name: "LoweredAtomicAnd8Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "MOVHstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVH, + name: "LoweredAtomicAnd32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "MOVWstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVW, + name: "LoweredAtomicOr8Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "MOVDstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVD, + name: "LoweredAtomicOr32Variant", + argLen: 3, + resultNotInArgs: true, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, }, }, { - name: "STP", - auxType: auxSymOff, - argLen: 4, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.ASTP, + name: "LoweredWB", + auxType: auxSym, + argLen: 3, + clobberFlags: true, + symEffect: SymNone, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4}, // R2 + {1, 8}, // R3 }, + clobbers: 9223372035244359680, // R16 R17 R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "FMOVSstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AFMOVS, + name: "LoweredPanicBoundsA", + auxType: auxInt64, + argLen: 3, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4}, // R2 + {1, 8}, // R3 }, }, }, { - name: "FMOVDstore", - auxType: auxSymOff, - argLen: 3, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AFMOVD, + name: "LoweredPanicBoundsB", + auxType: auxInt64, + argLen: 3, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 2}, // R1 + {1, 4}, // R2 }, }, }, { - name: "MOVBstoreidx", - argLen: 4, - asm: arm64.AMOVB, + name: "LoweredPanicBoundsC", + auxType: auxInt64, + argLen: 3, + call: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1}, // R0 + {1, 2}, // R1 }, }, }, { - name: "MOVHstoreidx", - argLen: 4, - asm: arm64.AMOVH, + name: "PRFM", + auxType: auxInt64, + argLen: 2, + hasSideEffects: true, + asm: arm64.APRFM, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, }, }, { - name: "MOVWstoreidx", - argLen: 4, - asm: arm64.AMOVW, + name: "DMB", + auxType: auxInt64, + argLen: 1, + hasSideEffects: true, + asm: arm64.ADMB, + reg: regInfo{}, + }, + + { + name: "ADDV", + argLen: 2, + commutative: true, + asm: loong64.AADDVU, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVDstoreidx", - argLen: 4, - asm: arm64.AMOVD, + name: "ADDVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AADDVU, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693244}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVSstoreidx", - argLen: 4, - asm: arm64.AFMOVS, + name: "SUBV", + argLen: 2, + asm: loong64.ASUBVU, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVDstoreidx", - argLen: 4, - asm: arm64.AFMOVD, + name: "SUBVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASUBVU, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVHstoreidx2", - argLen: 4, - asm: arm64.AMOVH, + name: "MULV", + argLen: 2, + commutative: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 65536}, // R17 + {1, 131072}, // R18 }, }, }, { - name: "MOVWstoreidx4", - argLen: 4, - asm: arm64.AMOVW, + name: "MULVU", + argLen: 2, + commutative: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 65536}, // R17 + {1, 131072}, // R18 }, }, }, { - name: "MOVDstoreidx8", - argLen: 4, - asm: arm64.AMOVD, + name: "DIVV", + argLen: 2, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 65536}, // R17 + {1, 131072}, // R18 }, }, }, { - name: "FMOVSstoreidx4", - argLen: 4, - asm: arm64.AFMOVS, + name: "DIVVU", + argLen: 2, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072496632}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 65536}, // R17 + {1, 131072}, // R18 }, }, }, { - name: "FMOVDstoreidx8", - argLen: 4, - asm: arm64.AFMOVD, + name: "ADDF", + argLen: 2, + commutative: true, + asm: loong64.AADDF, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVBstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVB, + name: "ADDD", + argLen: 2, + commutative: true, + asm: loong64.AADDD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVH, + name: "SUBF", + argLen: 2, + asm: loong64.ASUBF, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVW, + name: "SUBD", + argLen: 2, + asm: loong64.ASUBD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVDstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.AMOVD, + name: "MULF", + argLen: 2, + commutative: true, + asm: loong64.AMULF, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVQstorezero", - auxType: auxSymOff, - argLen: 2, - faultOnNilArg0: true, - symEffect: SymWrite, - asm: arm64.ASTP, + name: "MULD", + argLen: 2, + commutative: true, + asm: loong64.AMULD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVBstorezeroidx", - argLen: 3, - asm: arm64.AMOVB, + name: "DIVF", + argLen: 2, + asm: loong64.ADIVF, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHstorezeroidx", - argLen: 3, - asm: arm64.AMOVH, + name: "DIVD", + argLen: 2, + asm: loong64.ADIVD, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWstorezeroidx", - argLen: 3, - asm: arm64.AMOVW, + name: "AND", + argLen: 2, + commutative: true, + asm: loong64.AAND, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVDstorezeroidx", - argLen: 3, - asm: arm64.AMOVD, + name: "ANDconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AAND, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVHstorezeroidx2", - argLen: 3, - asm: arm64.AMOVH, + name: "OR", + argLen: 2, + commutative: true, + asm: loong64.AOR, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVWstorezeroidx4", - argLen: 3, - asm: arm64.AMOVW, + name: "ORconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AOR, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVDstorezeroidx8", - argLen: 3, - asm: arm64.AMOVD, + name: "XOR", + argLen: 2, + commutative: true, + asm: loong64.AXOR, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, + outputs: []outputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVDgpfp", - argLen: 1, - asm: arm64.AFMOVD, + name: "XORconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AXOR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVDfpgp", - argLen: 1, - asm: arm64.AFMOVD, + name: "NOR", + argLen: 2, + commutative: true, + asm: loong64.ANOR, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVSgpfp", - argLen: 1, - asm: arm64.AFMOVS, + name: "NORconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ANOR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FMOVSfpgp", + name: "NEGV", argLen: 1, - asm: arm64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVBreg", + name: "NEGF", argLen: 1, - asm: arm64.AMOVB, + asm: loong64.ANEGF, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVBUreg", + name: "NEGD", argLen: 1, - asm: arm64.AMOVBU, + asm: loong64.ANEGD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHreg", + name: "SQRTD", argLen: 1, - asm: arm64.AMOVH, + asm: loong64.ASQRTD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVHUreg", + name: "SQRTF", argLen: 1, - asm: arm64.AMOVHU, + asm: loong64.ASQRTF, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "MOVWreg", - argLen: 1, - asm: arm64.AMOVW, + name: "SLLV", + argLen: 2, + asm: loong64.ASLLV, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVWUreg", - argLen: 1, - asm: arm64.AMOVWU, + name: "SLLVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASLLV, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVDreg", - argLen: 1, - asm: arm64.AMOVD, + name: "SRLV", + argLen: 2, + asm: loong64.ASRLV, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "MOVDnop", - argLen: 1, - resultInArg0: true, + name: "SRLVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASRLV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "SCVTFWS", - argLen: 1, - asm: arm64.ASCVTFWS, + name: "SRAV", + argLen: 2, + asm: loong64.ASRAV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "SCVTFWD", - argLen: 1, - asm: arm64.ASCVTFWD, + name: "SRAVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASRAV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "UCVTFWS", - argLen: 1, - asm: arm64.AUCVTFWS, + name: "ROTR", + argLen: 2, + asm: loong64.AROTR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "UCVTFWD", - argLen: 1, - asm: arm64.AUCVTFWD, + name: "ROTRV", + argLen: 2, + asm: loong64.AROTRV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "SCVTFS", - argLen: 1, - asm: arm64.ASCVTFS, + name: "ROTRconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AROTR, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "SCVTFD", - argLen: 1, - asm: arm64.ASCVTFD, + name: "ROTRVconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.AROTRV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "UCVTFS", - argLen: 1, - asm: arm64.AUCVTFS, + name: "SGT", + argLen: 2, + asm: loong64.ASGT, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "UCVTFD", - argLen: 1, - asm: arm64.AUCVTFD, + name: "SGTconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASGT, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTZSSW", - argLen: 1, - asm: arm64.AFCVTZSSW, + name: "SGTU", + argLen: 2, + asm: loong64.ASGTU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTZSDW", - argLen: 1, - asm: arm64.AFCVTZSDW, + name: "SGTUconst", + auxType: auxInt64, + argLen: 1, + asm: loong64.ASGTU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTZUSW", - argLen: 1, - asm: arm64.AFCVTZUSW, + name: "CMPEQF", + argLen: 2, + asm: loong64.ACMPEQF, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FCVTZUDW", - argLen: 1, - asm: arm64.AFCVTZUDW, + name: "CMPEQD", + argLen: 2, + asm: loong64.ACMPEQD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + { + name: "CMPGEF", + argLen: 2, + asm: loong64.ACMPGEF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FCVTZSS", - argLen: 1, - asm: arm64.AFCVTZSS, + name: "CMPGED", + argLen: 2, + asm: loong64.ACMPGED, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + { + name: "CMPGTF", + argLen: 2, + asm: loong64.ACMPGTF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FCVTZSD", - argLen: 1, - asm: arm64.AFCVTZSD, + name: "CMPGTD", + argLen: 2, + asm: loong64.ACMPGTD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, + }, + }, + { + name: "MOVVconst", + auxType: auxInt64, + argLen: 0, + rematerializeable: true, + asm: loong64.AMOVV, + reg: regInfo{ outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTZUS", - argLen: 1, - asm: arm64.AFCVTZUS, + name: "MOVFconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: loong64.AMOVF, reg: regInfo{ - inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + outputs: []outputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, + }, + }, + { + name: "MOVDconst", + auxType: auxFloat64, + argLen: 0, + rematerializeable: true, + asm: loong64.AMOVD, + reg: regInfo{ outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FCVTZUD", - argLen: 1, - asm: arm64.AFCVTZUD, + name: "MOVVaddr", + auxType: auxSymOff, + argLen: 1, + rematerializeable: true, + symEffect: SymAddr, + asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686018427387908}, // SP SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTSD", - argLen: 1, - asm: arm64.AFCVTSD, + name: "MOVBload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FCVTDS", - argLen: 1, - asm: arm64.AFCVTDS, + name: "MOVBUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FRINTAD", - argLen: 1, - asm: arm64.AFRINTAD, + name: "MOVHload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FRINTMD", - argLen: 1, - asm: arm64.AFRINTMD, + name: "MOVHUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FRINTND", - argLen: 1, - asm: arm64.AFRINTND, + name: "MOVWload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FRINTPD", - argLen: 1, - asm: arm64.AFRINTPD, + name: "MOVWUload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "FRINTZD", - argLen: 1, - asm: arm64.AFRINTZD, + name: "MOVVload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "CSEL", - auxType: auxCCop, - argLen: 3, - asm: arm64.ACSEL, + name: "MOVFload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVF, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CSEL0", - auxType: auxCCop, - argLen: 2, - asm: arm64.ACSEL, + name: "MOVDload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: loong64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CSINC", - auxType: auxCCop, - argLen: 3, - asm: arm64.ACSINC, + name: "MOVBstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "CSINV", - auxType: auxCCop, - argLen: 3, - asm: arm64.ACSINV, + name: "MOVHstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + { + name: "MOVWstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "CSNEG", - auxType: auxCCop, - argLen: 3, - asm: arm64.ACSNEG, + name: "MOVVstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + { + name: "MOVFstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVF, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CSETM", - auxType: auxCCop, - argLen: 1, - asm: arm64.ACSETM, + name: "MOVDstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVD, reg: regInfo{ - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + inputs: []inputInfo{ + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "CALLstatic", - auxType: auxCallOff, - argLen: 1, - clobberFlags: true, - call: true, + name: "MOVBstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVB, reg: regInfo{ - clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + inputs: []inputInfo{ + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + }, }, }, { - name: "CALLclosure", - auxType: auxCallOff, - argLen: 3, - clobberFlags: true, - call: true, + name: "MOVHstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {1, 67108864}, // R26 - {0, 1744568319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 SP + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, - clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "CALLinter", - auxType: auxCallOff, - argLen: 2, - clobberFlags: true, - call: true, + name: "MOVWstorezero", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, - clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "LoweredNilCheck", + name: "MOVVstorezero", + auxType: auxSymOff, argLen: 2, - nilCheck: true, faultOnNilArg0: true, + symEffect: SymWrite, + asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "Equal", + name: "MOVBreg", argLen: 1, + asm: loong64.AMOVB, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "NotEqual", + name: "MOVBUreg", argLen: 1, + asm: loong64.AMOVBU, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LessThan", + name: "MOVHreg", argLen: 1, + asm: loong64.AMOVH, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LessEqual", + name: "MOVHUreg", argLen: 1, + asm: loong64.AMOVHU, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "GreaterThan", + name: "MOVWreg", argLen: 1, + asm: loong64.AMOVW, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "GreaterEqual", + name: "MOVWUreg", argLen: 1, + asm: loong64.AMOVWU, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LessThanU", + name: "MOVVreg", argLen: 1, + asm: loong64.AMOVV, reg: regInfo{ + inputs: []inputInfo{ + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LessEqualU", - argLen: 1, + name: "MOVVnop", + argLen: 1, + resultInArg0: true, reg: regInfo{ + inputs: []inputInfo{ + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "GreaterThanU", + name: "MOVWF", argLen: 1, + asm: loong64.AMOVWF, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "GreaterEqualU", + name: "MOVWD", argLen: 1, + asm: loong64.AMOVWD, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "LessThanF", + name: "MOVVF", argLen: 1, + asm: loong64.AMOVVF, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "LessEqualF", + name: "MOVVD", argLen: 1, + asm: loong64.AMOVVD, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "GreaterThanF", + name: "TRUNCFW", argLen: 1, + asm: loong64.ATRUNCFW, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "GreaterEqualF", + name: "TRUNCDW", argLen: 1, + asm: loong64.ATRUNCDW, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NotLessThanF", + name: "TRUNCFV", argLen: 1, + asm: loong64.ATRUNCFV, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NotLessEqualF", + name: "TRUNCDV", argLen: 1, + asm: loong64.ATRUNCDV, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NotGreaterThanF", + name: "MOVFD", argLen: 1, + asm: loong64.AMOVFD, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "NotGreaterEqualF", + name: "MOVDF", argLen: 1, + asm: loong64.AMOVDF, reg: regInfo{ + inputs: []inputInfo{ + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686017353646080}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "DUFFZERO", - auxType: auxInt64, - argLen: 2, - faultOnNilArg0: true, - unsafePoint: true, + name: "CALLstatic", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, reg: regInfo{ - inputs: []inputInfo{ - {0, 1048576}, // R20 - }, - clobbers: 538116096, // R16 R17 R20 R30 + clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "LoweredZero", - argLen: 3, - clobberFlags: true, - faultOnNilArg0: true, + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "CALLclosure", + auxType: auxCallOff, + argLen: 3, + clobberFlags: true, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 65536}, // R16 - {1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 268435456}, // R29 + {0, 1070596092}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 65536, // R16 + clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "DUFFCOPY", - auxType: auxInt64, - argLen: 3, - faultOnNilArg0: true, - faultOnNilArg1: true, - unsafePoint: true, + name: "CALLinter", + auxType: auxCallOff, + argLen: 2, + clobberFlags: true, + call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 2097152}, // R21 - {1, 1048576}, // R20 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 607322112, // R16 R17 R20 R21 R26 R30 + clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { - name: "LoweredMove", - argLen: 4, - clobberFlags: true, + name: "DUFFZERO", + auxType: auxInt64, + argLen: 2, faultOnNilArg0: true, - faultOnNilArg1: true, reg: regInfo{ inputs: []inputInfo{ - {0, 131072}, // R17 - {1, 65536}, // R16 - {2, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 196608, // R16 R17 + clobbers: 262146, // R1 R19 }, }, { - name: "LoweredGetClosurePtr", - argLen: 0, - zeroWidth: true, + name: "DUFFCOPY", + auxType: auxInt64, + argLen: 3, + faultOnNilArg0: true, + faultOnNilArg1: true, reg: regInfo{ - outputs: []outputInfo{ - {0, 67108864}, // R26 + inputs: []inputInfo{ + {0, 524288}, // R20 + {1, 262144}, // R19 }, + clobbers: 786434, // R1 R19 R20 }, }, { - name: "LoweredGetCallerSP", - argLen: 0, - rematerializeable: true, + name: "LoweredZero", + auxType: auxInt64, + argLen: 3, + clobberFlags: true, + faultOnNilArg0: true, reg: regInfo{ - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + inputs: []inputInfo{ + {0, 262144}, // R19 + {1, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, + clobbers: 262144, // R19 }, }, { - name: "LoweredGetCallerPC", - argLen: 0, - rematerializeable: true, + name: "LoweredMove", + auxType: auxInt64, + argLen: 4, + clobberFlags: true, + faultOnNilArg0: true, + faultOnNilArg1: true, reg: regInfo{ - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + inputs: []inputInfo{ + {0, 8}, // R4 + {1, 262144}, // R19 + {2, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, + clobbers: 262152, // R4 R19 }, }, { - name: "FlagConstant", - auxType: auxFlagConstant, - argLen: 0, - reg: regInfo{}, - }, - { - name: "InvertFlags", - argLen: 1, - reg: regInfo{}, - }, - { - name: "LDAR", + name: "LoweredAtomicLoad8", argLen: 2, faultOnNilArg0: true, - asm: arm64.ALDAR, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LDARB", + name: "LoweredAtomicLoad32", argLen: 2, faultOnNilArg0: true, - asm: arm64.ALDARB, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LDARW", + name: "LoweredAtomicLoad64", argLen: 2, faultOnNilArg0: true, - asm: arm64.ALDARW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "STLRB", + name: "LoweredAtomicStore8", argLen: 3, faultOnNilArg0: true, hasSideEffects: true, - asm: arm64.ASTLRB, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "STLR", + name: "LoweredAtomicStore32", argLen: 3, faultOnNilArg0: true, hasSideEffects: true, - asm: arm64.ASTLR, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "STLRW", + name: "LoweredAtomicStore64", argLen: 3, faultOnNilArg0: true, hasSideEffects: true, - asm: arm64.ASTLRW, - reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - }, - }, - { - name: "LoweredAtomicExchange64", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "LoweredAtomicExchange32", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, + name: "LoweredAtomicStorezero32", + argLen: 2, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "LoweredAtomicExchange64Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, + name: "LoweredAtomicStorezero64", + argLen: 2, + faultOnNilArg0: true, + hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, { - name: "LoweredAtomicExchange32Variant", + name: "LoweredAtomicExchange32", argLen: 3, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, + unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicAdd64", + name: "LoweredAtomicExchange64", argLen: 3, resultNotInArgs: true, faultOnNilArg0: true, @@ -21124,11 +24318,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -21141,257 +24335,157 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, - }, - }, - { - name: "LoweredAtomicAdd64Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicAdd32Variant", + name: "LoweredAtomicAdd64", argLen: 3, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, - reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, - }, - }, - { - name: "LoweredAtomicCas64", - argLen: 4, - resultNotInArgs: true, - clobberFlags: true, - faultOnNilArg0: true, - hasSideEffects: true, unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicCas32", - argLen: 4, + name: "LoweredAtomicAddconst32", + auxType: auxInt32, + argLen: 2, resultNotInArgs: true, - clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicCas64Variant", - argLen: 4, + name: "LoweredAtomicAddconst64", + auxType: auxInt64, + argLen: 2, resultNotInArgs: true, - clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicCas32Variant", + name: "LoweredAtomicCas32", argLen: 4, resultNotInArgs: true, - clobberFlags: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, - reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 - }, - }, - }, - { - name: "LoweredAtomicAnd8", - argLen: 3, - resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, - asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {2, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicAnd32", - argLen: 3, + name: "LoweredAtomicCas64", + argLen: 4, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true, - asm: arm64.AAND, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {2, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicOr8", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, - asm: arm64.AORR, + name: "LoweredNilCheck", + argLen: 2, + nilCheck: true, + faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, - outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicOr32", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, - asm: arm64.AORR, + name: "FPFlagTrue", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicAnd8Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, + name: "FPFlagFalse", + argLen: 1, reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicAnd32Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, - unsafePoint: true, + name: "LoweredGetClosurePtr", + argLen: 0, + zeroWidth: true, reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 268435456}, // R29 }, }, }, { - name: "LoweredAtomicOr8Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, + name: "LoweredGetCallerSP", + argLen: 0, + rematerializeable: true, reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, { - name: "LoweredAtomicOr32Variant", - argLen: 3, - resultNotInArgs: true, - faultOnNilArg0: true, - hasSideEffects: true, + name: "LoweredGetCallerPC", + argLen: 0, + rematerializeable: true, reg: regInfo{ - inputs: []inputInfo{ - {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 - {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB - }, outputs: []outputInfo{ - {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -21403,10 +24497,10 @@ var opcodeTable = [...]opInfo{ symEffect: SymNone, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 8}, // R3 + {0, 67108864}, // R27 + {1, 134217728}, // R28 }, - clobbers: 9223372035244359680, // R16 R17 R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + clobbers: 4611686017353646082, // R1 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { @@ -21416,8 +24510,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4}, // R2 - {1, 8}, // R3 + {0, 65536}, // R17 + {1, 8}, // R4 }, }, }, @@ -21428,8 +24522,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 2}, // R1 - {1, 4}, // R2 + {0, 131072}, // R18 + {1, 65536}, // R17 }, }, }, @@ -21440,8 +24534,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1}, // R0 - {1, 2}, // R1 + {0, 262144}, // R19 + {1, 131072}, // R18 }, }, }, @@ -22592,6 +25686,17 @@ var opcodeTable = [...]opInfo{ clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -24151,6 +27256,17 @@ var opcodeTable = [...]opInfo{ clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -24615,11 +27731,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFADD, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24630,11 +27746,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFADDS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24661,6 +27777,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -24672,11 +27789,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFSUB, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24686,11 +27803,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFSUBS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24849,11 +27966,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMUL, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24864,11 +27981,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMULS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24878,12 +27995,12 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMADD, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24893,12 +28010,12 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMADDS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24908,12 +28025,12 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMSUB, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24923,12 +28040,12 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMSUBS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -24941,6 +28058,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -24955,6 +28073,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25087,18 +28206,128 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "LoweredAdd64Carry", - argLen: 3, - resultNotInArgs: true, + name: "ADDC", + argLen: 2, + commutative: true, + asm: ppc64.AADDC, reg: regInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 - {2, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBC", + argLen: 2, + asm: ppc64.ASUBC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDCconst", + auxType: auxInt64, + argLen: 1, + asm: ppc64.AADDC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBCconst", + auxType: auxInt64, + argLen: 1, + asm: ppc64.ASUBC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDE", + argLen: 3, + commutative: true, + asm: ppc64.AADDE, + reg: regInfo{ + inputs: []inputInfo{ + {2, 9223372036854775808}, // XER + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBE", + argLen: 3, + asm: ppc64.ASUBE, + reg: regInfo{ + inputs: []inputInfo{ + {2, 9223372036854775808}, // XER + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDZEzero", + argLen: 1, + asm: ppc64.AADDZE, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372036854775808}, // XER + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBZEzero", + argLen: 1, + asm: ppc64.ASUBZE, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372036854775808}, // XER + }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 - {1, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, }, }, @@ -25111,6 +28340,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25125,6 +28355,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25372,11 +28603,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFDIV, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25386,11 +28617,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFDIVS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25512,10 +28743,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCTIDZ, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25525,10 +28756,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCTIWZ, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25538,10 +28769,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCFID, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25551,10 +28782,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCFIDS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25564,10 +28795,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFRSP, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25577,7 +28808,7 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AMFVSRD, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 @@ -25593,7 +28824,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25755,10 +28986,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFNEG, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25768,10 +28999,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFSQRT, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25781,10 +29012,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFSQRTS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25794,10 +29025,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFRIM, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25807,10 +29038,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFRIP, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25820,10 +29051,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFRIZ, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25833,10 +29064,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFRIN, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25846,10 +29077,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFABS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25859,10 +29090,10 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFNABS, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25872,11 +29103,11 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCPSGN, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -25909,7 +29140,7 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ANDconst", + name: "ANDCCconst", auxType: auxInt64, argLen: 1, clobberFlags: true, @@ -25923,17 +29154,6 @@ var opcodeTable = [...]opInfo{ }, }, }, - { - name: "ANDCCconst", - auxType: auxInt64, - argLen: 1, - asm: ppc64.AANDCC, - reg: regInfo{ - inputs: []inputInfo{ - {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 - }, - }, - }, { name: "MOVBreg", argLen: 1, @@ -26292,7 +29512,7 @@ var opcodeTable = [...]opInfo{ {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26306,7 +29526,19 @@ var opcodeTable = [...]opInfo{ {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + }, + }, + }, + { + name: "DCBT", + auxType: auxInt64, + argLen: 2, + hasSideEffects: true, + asm: ppc64.ADCBT, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, }, }, @@ -26364,7 +29596,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26380,7 +29612,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26449,8 +29681,8 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26463,8 +29695,8 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26522,9 +29754,9 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVD, reg: regInfo{ inputs: []inputInfo{ - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 - {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26534,9 +29766,9 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVS, reg: regInfo{ inputs: []inputInfo{ - {2, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 - {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {2, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26664,7 +29896,7 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVD, reg: regInfo{ outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26676,7 +29908,7 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFMOVS, reg: regInfo{ outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26686,8 +29918,8 @@ var opcodeTable = [...]opInfo{ asm: ppc64.AFCMPU, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 - {1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + {1, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26948,10 +30180,10 @@ var opcodeTable = [...]opInfo{ zeroWidth: true, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, @@ -26962,27 +30194,38 @@ var opcodeTable = [...]opInfo{ zeroWidth: true, reg: regInfo{ inputs: []inputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, outputs: []outputInfo{ - {0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + {0, 9223372032559808512}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 }, }, }, { name: "CALLstatic", auxType: auxCallOff, - argLen: 1, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ - clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER + }, + }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { name: "CALLclosure", auxType: auxCallOff, - argLen: 3, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ @@ -26990,20 +30233,20 @@ var opcodeTable = [...]opInfo{ {0, 4096}, // R12 {1, 2048}, // R11 }, - clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { name: "CALLinter", auxType: auxCallOff, - argLen: 2, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ inputs: []inputInfo{ {0, 4096}, // R12 }, - clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -27386,9 +30629,16 @@ var opcodeTable = [...]opInfo{ {0, 1048576}, // R20 {1, 2097152}, // R21 }, - clobbers: 576460746931312640, // R11 R12 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + clobbers: 18446744072632408064, // R11 R12 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, + { + name: "LoweredPubBarrier", + argLen: 1, + hasSideEffects: true, + asm: ppc64.ALWSYNC, + reg: regInfo{}, + }, { name: "LoweredPanicBoundsA", auxType: auxInt64, @@ -27453,11 +30703,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AADD, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27468,10 +30718,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AADDI, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27482,10 +30732,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AADDIW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27495,10 +30745,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ANEG, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27508,10 +30758,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ANEGW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27521,11 +30771,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASUB, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27535,11 +30785,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASUBW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27550,11 +30800,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMUL, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27565,11 +30815,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMULW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27580,11 +30830,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMULH, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27595,11 +30845,41 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMULHU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + }, + }, + }, + { + name: "LoweredMuluhilo", + argLen: 2, + resultNotInArgs: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + }, + outputs: []outputInfo{ + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + }, + }, + }, + { + name: "LoweredMuluover", + argLen: 2, + resultNotInArgs: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + }, + outputs: []outputInfo{ + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27609,11 +30889,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ADIV, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27623,11 +30903,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ADIVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27637,11 +30917,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ADIVW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27651,11 +30931,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ADIVUW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27665,11 +30945,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AREM, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27679,11 +30959,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AREMU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27693,11 +30973,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AREMW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27707,11 +30987,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AREMUW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27724,10 +31004,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27739,7 +31019,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27752,10 +31032,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27768,10 +31048,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27784,10 +31064,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27800,10 +31080,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27816,10 +31096,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27832,10 +31112,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27848,10 +31128,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27864,8 +31144,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27878,8 +31158,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27892,8 +31172,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27906,8 +31186,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27920,7 +31200,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27933,7 +31213,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27946,7 +31226,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27959,7 +31239,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -27969,10 +31249,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27982,10 +31262,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -27995,10 +31275,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28008,10 +31288,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28021,10 +31301,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28034,10 +31314,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28047,10 +31327,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28060,10 +31340,10 @@ var opcodeTable = [...]opInfo{ resultInArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28073,11 +31353,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLL, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28087,11 +31367,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASRA, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28101,11 +31381,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASRL, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28116,10 +31396,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLLI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28130,10 +31410,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASRAI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28144,10 +31424,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASRLI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28158,11 +31438,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AXOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28173,10 +31453,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AXORI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28187,11 +31467,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28202,10 +31482,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AORI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28216,11 +31496,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28231,10 +31511,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.AANDI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28244,10 +31524,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ANOT, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28257,10 +31537,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASEQZ, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28270,10 +31550,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASNEZ, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28283,11 +31563,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLT, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28298,10 +31578,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLTI, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28311,11 +31591,11 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLTU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28326,10 +31606,10 @@ var opcodeTable = [...]opInfo{ asm: riscv.ASLTIU, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28339,45 +31619,55 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOV, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, { name: "CALLstatic", auxType: auxCallOff, - argLen: 1, + argLen: -1, call: true, reg: regInfo{ - clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { name: "CALLclosure", auxType: auxCallOff, - argLen: 3, + argLen: -1, call: true, reg: regInfo{ inputs: []inputInfo{ - {1, 524288}, // X20 - {0, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 33554432}, // X26 + {0, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, - clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { name: "CALLinter", auxType: auxCallOff, - argLen: 2, + argLen: -1, call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, - clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, { @@ -28387,9 +31677,9 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 512}, // X10 + {0, 16777216}, // X25 }, - clobbers: 512, // X10 + clobbers: 16777216, // X25 }, }, { @@ -28400,10 +31690,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg1: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1024}, // X11 - {1, 512}, // X10 + {0, 16777216}, // X25 + {1, 8388608}, // X24 }, - clobbers: 1536, // X10 X11 + clobbers: 25165824, // X24 X25 }, }, { @@ -28414,7 +31704,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {0, 16}, // X5 - {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, clobbers: 16, // X5 }, @@ -28429,7 +31719,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 16}, // X5 {1, 32}, // X6 - {2, 1006632884}, // X3 X5 X6 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {2, 1006632880}, // X5 X6 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, clobbers: 112, // X5 X6 X7 }, @@ -28440,10 +31730,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28453,10 +31743,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28466,10 +31756,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28480,8 +31770,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -28492,8 +31782,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -28504,8 +31794,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, }, }, @@ -28517,11 +31807,11 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28533,11 +31823,11 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28550,11 +31840,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28567,11 +31857,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28584,12 +31874,12 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {2, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {2, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28602,12 +31892,12 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {2, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {2, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28619,8 +31909,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AAMOANDW, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, }, }, @@ -28632,8 +31922,8 @@ var opcodeTable = [...]opInfo{ asm: riscv.AAMOORW, reg: regInfo{ inputs: []inputInfo{ - {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 - {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB + {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 + {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB }, }, }, @@ -28644,7 +31934,7 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28653,7 +31943,7 @@ var opcodeTable = [...]opInfo{ argLen: 0, reg: regInfo{ outputs: []outputInfo{ - {0, 524288}, // X20 + {0, 33554432}, // X26 }, }, }, @@ -28663,7 +31953,7 @@ var opcodeTable = [...]opInfo{ rematerializeable: true, reg: regInfo{ outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28673,7 +31963,7 @@ var opcodeTable = [...]opInfo{ rematerializeable: true, reg: regInfo{ outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28817,7 +32107,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AFMVSX, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -28830,7 +32120,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AFCVTSW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -28843,7 +32133,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AFCVTSL, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -28859,7 +32149,7 @@ var opcodeTable = [...]opInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28872,7 +32162,7 @@ var opcodeTable = [...]opInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28885,7 +32175,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVF, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -28901,7 +32191,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVF, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, @@ -28917,7 +32207,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28932,7 +32222,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28946,7 +32236,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -28960,7 +32250,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29022,6 +32312,70 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "FMADDD", + argLen: 3, + commutative: true, + asm: riscv.AFMADDD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FMSUBD", + argLen: 3, + commutative: true, + asm: riscv.AFMSUBD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNMADDD", + argLen: 3, + commutative: true, + asm: riscv.AFNMADDD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FNMSUBD", + argLen: 3, + commutative: true, + asm: riscv.AFNMSUBD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, { name: "FSQRTD", argLen: 1, @@ -29048,13 +32402,40 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "FABSD", + argLen: 1, + asm: riscv.AFABSD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSGNJD", + argLen: 2, + asm: riscv.AFSGNJD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, { name: "FMVDX", argLen: 1, asm: riscv.AFMVDX, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -29067,7 +32448,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AFCVTDW, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -29080,7 +32461,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AFCVTDL, reg: regInfo{ inputs: []inputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -29096,7 +32477,7 @@ var opcodeTable = [...]opInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29109,7 +32490,7 @@ var opcodeTable = [...]opInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29148,7 +32529,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 @@ -29164,7 +32545,7 @@ var opcodeTable = [...]opInfo{ asm: riscv.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB + {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, @@ -29180,7 +32561,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29195,7 +32576,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29209,7 +32590,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -29223,7 +32604,7 @@ var opcodeTable = [...]opInfo{ {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, outputs: []outputInfo{ - {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 + {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 }, }, }, @@ -32019,6 +35400,17 @@ var opcodeTable = [...]opInfo{ clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -32141,7 +35533,7 @@ var opcodeTable = [...]opInfo{ {0, 4}, // R2 {1, 8}, // R3 }, - clobbers: 4294918144, // R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + clobbers: 4294918146, // R1 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, { @@ -32689,6 +36081,16 @@ var opcodeTable = [...]opInfo{ clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g }, }, + { + name: "LoweredTailCall", + auxType: auxCallOff, + argLen: 1, + call: true, + tailCall: true, + reg: regInfo{ + clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g + }, + }, { name: "LoweredClosureCall", auxType: auxCallOff, @@ -35166,22 +38568,22 @@ var opcodeTable = [...]opInfo{ generic: true, }, { - name: "RotateLeft8", + name: "RotateLeft64", argLen: 2, generic: true, }, { - name: "RotateLeft16", + name: "RotateLeft32", argLen: 2, generic: true, }, { - name: "RotateLeft32", + name: "RotateLeft16", argLen: 2, generic: true, }, { - name: "RotateLeft64", + name: "RotateLeft8", argLen: 2, generic: true, }, @@ -35465,6 +38867,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "TailCall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "ClosureLECall", auxType: auxCallOff, @@ -35486,6 +38895,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "TailLECall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "SignExt8to16", argLen: 1, @@ -35840,13 +39256,6 @@ var opcodeTable = [...]opInfo{ symEffect: SymNone, generic: true, }, - { - name: "VarKill", - auxType: auxSym, - argLen: 1, - symEffect: SymNone, - generic: true, - }, { name: "VarLive", auxType: auxSym, @@ -36201,6 +39610,12 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, generic: true, }, + { + name: "PubBarrier", + argLen: 1, + hasSideEffects: true, + generic: true, + }, { name: "Clobber", auxType: auxSymOff, @@ -36213,6 +39628,18 @@ var opcodeTable = [...]opInfo{ argLen: 0, generic: true, }, + { + name: "PrefetchCache", + argLen: 2, + hasSideEffects: true, + generic: true, + }, + { + name: "PrefetchCacheStreamed", + argLen: 2, + hasSideEffects: true, + generic: true, + }, } func (o Op) Asm() obj.As { return opcodeTable[o].asm } @@ -36220,6 +39647,7 @@ func (o Op) Scale() int16 { return int16(opcodeTable[o].scale) } func (o Op) String() string { return opcodeTable[o].name } func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect } func (o Op) IsCall() bool { return opcodeTable[o].call } +func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall } func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects } func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint } func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 } @@ -36400,13 +39828,85 @@ var registersARM64 = [...]Register{ {62, arm64.REG_F31, -1, "F31"}, {63, 0, -1, "SB"}, } -var paramIntRegARM64 = []int8(nil) -var paramFloatRegARM64 = []int8(nil) +var paramIntRegARM64 = []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} +var paramFloatRegARM64 = []int8{31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46} var gpRegMaskARM64 = regMask(670826495) var fpRegMaskARM64 = regMask(9223372034707292160) var specialRegMaskARM64 = regMask(0) var framepointerRegARM64 = int8(-1) var linkRegARM64 = int8(29) +var registersLOONG64 = [...]Register{ + {0, loong64.REG_R0, -1, "R0"}, + {1, loong64.REG_R1, -1, "R1"}, + {2, loong64.REGSP, -1, "SP"}, + {3, loong64.REG_R4, 0, "R4"}, + {4, loong64.REG_R5, 1, "R5"}, + {5, loong64.REG_R6, 2, "R6"}, + {6, loong64.REG_R7, 3, "R7"}, + {7, loong64.REG_R8, 4, "R8"}, + {8, loong64.REG_R9, 5, "R9"}, + {9, loong64.REG_R10, 6, "R10"}, + {10, loong64.REG_R11, 7, "R11"}, + {11, loong64.REG_R12, 8, "R12"}, + {12, loong64.REG_R13, 9, "R13"}, + {13, loong64.REG_R14, 10, "R14"}, + {14, loong64.REG_R15, 11, "R15"}, + {15, loong64.REG_R16, 12, "R16"}, + {16, loong64.REG_R17, 13, "R17"}, + {17, loong64.REG_R18, 14, "R18"}, + {18, loong64.REG_R19, 15, "R19"}, + {19, loong64.REG_R20, 16, "R20"}, + {20, loong64.REG_R21, -1, "R21"}, + {21, loong64.REGG, -1, "g"}, + {22, loong64.REG_R23, 17, "R23"}, + {23, loong64.REG_R24, 18, "R24"}, + {24, loong64.REG_R25, 19, "R25"}, + {25, loong64.REG_R26, 20, "R26"}, + {26, loong64.REG_R27, 21, "R27"}, + {27, loong64.REG_R28, 22, "R28"}, + {28, loong64.REG_R29, 23, "R29"}, + {29, loong64.REG_R31, 24, "R31"}, + {30, loong64.REG_F0, -1, "F0"}, + {31, loong64.REG_F1, -1, "F1"}, + {32, loong64.REG_F2, -1, "F2"}, + {33, loong64.REG_F3, -1, "F3"}, + {34, loong64.REG_F4, -1, "F4"}, + {35, loong64.REG_F5, -1, "F5"}, + {36, loong64.REG_F6, -1, "F6"}, + {37, loong64.REG_F7, -1, "F7"}, + {38, loong64.REG_F8, -1, "F8"}, + {39, loong64.REG_F9, -1, "F9"}, + {40, loong64.REG_F10, -1, "F10"}, + {41, loong64.REG_F11, -1, "F11"}, + {42, loong64.REG_F12, -1, "F12"}, + {43, loong64.REG_F13, -1, "F13"}, + {44, loong64.REG_F14, -1, "F14"}, + {45, loong64.REG_F15, -1, "F15"}, + {46, loong64.REG_F16, -1, "F16"}, + {47, loong64.REG_F17, -1, "F17"}, + {48, loong64.REG_F18, -1, "F18"}, + {49, loong64.REG_F19, -1, "F19"}, + {50, loong64.REG_F20, -1, "F20"}, + {51, loong64.REG_F21, -1, "F21"}, + {52, loong64.REG_F22, -1, "F22"}, + {53, loong64.REG_F23, -1, "F23"}, + {54, loong64.REG_F24, -1, "F24"}, + {55, loong64.REG_F25, -1, "F25"}, + {56, loong64.REG_F26, -1, "F26"}, + {57, loong64.REG_F27, -1, "F27"}, + {58, loong64.REG_F28, -1, "F28"}, + {59, loong64.REG_F29, -1, "F29"}, + {60, loong64.REG_F30, -1, "F30"}, + {61, loong64.REG_F31, -1, "F31"}, + {62, 0, -1, "SB"}, +} +var paramIntRegLOONG64 = []int8{3, 4, 5, 6, 7, 8, 9, 10} +var paramFloatRegLOONG64 = []int8{30, 31, 32, 33, 34, 35, 36, 37} +var gpRegMaskLOONG64 = regMask(1070596088) +var fpRegMaskLOONG64 = regMask(4611686017353646080) +var specialRegMaskLOONG64 = regMask(0) +var framepointerRegLOONG64 = int8(-1) +var linkRegLOONG64 = int8(1) var registersMIPS = [...]Register{ {0, mips.REG_R0, -1, "R0"}, {1, mips.REG_R1, 0, "R1"}, @@ -36600,46 +40100,46 @@ var registersPPC64 = [...]Register{ {60, ppc64.REG_F28, -1, "F28"}, {61, ppc64.REG_F29, -1, "F29"}, {62, ppc64.REG_F30, -1, "F30"}, - {63, ppc64.REG_F31, -1, "F31"}, + {63, ppc64.REG_XER, -1, "XER"}, } -var paramIntRegPPC64 = []int8(nil) -var paramFloatRegPPC64 = []int8(nil) +var paramIntRegPPC64 = []int8{3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17} +var paramFloatRegPPC64 = []int8{33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44} var gpRegMaskPPC64 = regMask(1073733624) -var fpRegMaskPPC64 = regMask(576460743713488896) -var specialRegMaskPPC64 = regMask(0) -var framepointerRegPPC64 = int8(1) +var fpRegMaskPPC64 = regMask(9223372032559808512) +var specialRegMaskPPC64 = regMask(9223372036854775808) +var framepointerRegPPC64 = int8(-1) var linkRegPPC64 = int8(-1) var registersRISCV64 = [...]Register{ {0, riscv.REG_X0, -1, "X0"}, {1, riscv.REGSP, -1, "SP"}, - {2, riscv.REG_X3, 0, "X3"}, + {2, riscv.REG_X3, -1, "X3"}, {3, riscv.REG_X4, -1, "X4"}, - {4, riscv.REG_X5, 1, "X5"}, - {5, riscv.REG_X6, 2, "X6"}, - {6, riscv.REG_X7, 3, "X7"}, - {7, riscv.REG_X8, 4, "X8"}, - {8, riscv.REG_X9, 5, "X9"}, - {9, riscv.REG_X10, 6, "X10"}, - {10, riscv.REG_X11, 7, "X11"}, - {11, riscv.REG_X12, 8, "X12"}, - {12, riscv.REG_X13, 9, "X13"}, - {13, riscv.REG_X14, 10, "X14"}, - {14, riscv.REG_X15, 11, "X15"}, - {15, riscv.REG_X16, 12, "X16"}, - {16, riscv.REG_X17, 13, "X17"}, - {17, riscv.REG_X18, 14, "X18"}, - {18, riscv.REG_X19, 15, "X19"}, - {19, riscv.REG_X20, 16, "X20"}, - {20, riscv.REG_X21, 17, "X21"}, - {21, riscv.REG_X22, 18, "X22"}, - {22, riscv.REG_X23, 19, "X23"}, - {23, riscv.REG_X24, 20, "X24"}, - {24, riscv.REG_X25, 21, "X25"}, - {25, riscv.REG_X26, 22, "X26"}, + {4, riscv.REG_X5, 0, "X5"}, + {5, riscv.REG_X6, 1, "X6"}, + {6, riscv.REG_X7, 2, "X7"}, + {7, riscv.REG_X8, 3, "X8"}, + {8, riscv.REG_X9, 4, "X9"}, + {9, riscv.REG_X10, 5, "X10"}, + {10, riscv.REG_X11, 6, "X11"}, + {11, riscv.REG_X12, 7, "X12"}, + {12, riscv.REG_X13, 8, "X13"}, + {13, riscv.REG_X14, 9, "X14"}, + {14, riscv.REG_X15, 10, "X15"}, + {15, riscv.REG_X16, 11, "X16"}, + {16, riscv.REG_X17, 12, "X17"}, + {17, riscv.REG_X18, 13, "X18"}, + {18, riscv.REG_X19, 14, "X19"}, + {19, riscv.REG_X20, 15, "X20"}, + {20, riscv.REG_X21, 16, "X21"}, + {21, riscv.REG_X22, 17, "X22"}, + {22, riscv.REG_X23, 18, "X23"}, + {23, riscv.REG_X24, 19, "X24"}, + {24, riscv.REG_X25, 20, "X25"}, + {25, riscv.REG_X26, 21, "X26"}, {26, riscv.REGG, -1, "g"}, - {27, riscv.REG_X28, 23, "X28"}, - {28, riscv.REG_X29, 24, "X29"}, - {29, riscv.REG_X30, 25, "X30"}, + {27, riscv.REG_X28, 22, "X28"}, + {28, riscv.REG_X29, 23, "X29"}, + {29, riscv.REG_X30, 24, "X30"}, {30, riscv.REG_X31, -1, "X31"}, {31, riscv.REG_F0, -1, "F0"}, {32, riscv.REG_F1, -1, "F1"}, @@ -36675,9 +40175,9 @@ var registersRISCV64 = [...]Register{ {62, riscv.REG_F31, -1, "F31"}, {63, 0, -1, "SB"}, } -var paramIntRegRISCV64 = []int8(nil) -var paramFloatRegRISCV64 = []int8(nil) -var gpRegMaskRISCV64 = regMask(1006632948) +var paramIntRegRISCV64 = []int8{9, 10, 11, 12, 13, 14, 15, 16, 7, 8, 17, 18, 19, 20, 21, 22} +var paramFloatRegRISCV64 = []int8{41, 42, 43, 44, 45, 46, 47, 48, 39, 40, 49, 50, 51, 52, 53, 54} +var gpRegMaskRISCV64 = regMask(1006632944) var fpRegMaskRISCV64 = regMask(9223372034707292160) var specialRegMaskRISCV64 = regMask(0) var framepointerRegRISCV64 = int8(-1) diff --git a/src/cmd/compile/internal/ssa/phielim.go b/src/cmd/compile/internal/ssa/phielim.go index 761cb7a392fb77..4fc942375fdef3 100644 --- a/src/cmd/compile/internal/ssa/phielim.go +++ b/src/cmd/compile/internal/ssa/phielim.go @@ -8,13 +8,19 @@ package ssa // A phi is redundant if its arguments are all equal. For // purposes of counting, ignore the phi itself. Both of // these phis are redundant: -// v = phi(x,x,x) -// v = phi(x,v,x,v) +// +// v = phi(x,x,x) +// v = phi(x,v,x,v) +// // We repeat this process to also catch situations like: -// v = phi(x, phi(x, x), phi(x, v)) +// +// v = phi(x, phi(x, x), phi(x, v)) +// // TODO: Can we also simplify cases like: -// v = phi(v, w, x) -// w = phi(v, w, x) +// +// v = phi(v, w, x) +// w = phi(v, w, x) +// // and would that be useful? func phielim(f *Func) { for { diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go index 745c61cb860a95..037845eacf2db6 100644 --- a/src/cmd/compile/internal/ssa/phiopt.go +++ b/src/cmd/compile/internal/ssa/phiopt.go @@ -7,20 +7,22 @@ package ssa // phiopt eliminates boolean Phis based on the previous if. // // Main use case is to transform: -// x := false -// if b { -// x = true -// } +// +// x := false +// if b { +// x = true +// } +// // into x = b. // // In SSA code this appears as // -// b0 -// If b -> b1 b2 -// b1 -// Plain -> b2 -// b2 -// x = (OpPhi (ConstBool [true]) (ConstBool [false])) +// b0 +// If b -> b1 b2 +// b1 +// Plain -> b2 +// b2 +// x = (OpPhi (ConstBool [true]) (ConstBool [false])) // // In this case we can replace x with a copy of b. func phiopt(f *Func) { diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go index d2719eb8a1d9d4..ad89de3f1357ec 100644 --- a/src/cmd/compile/internal/ssa/poset.go +++ b/src/cmd/compile/internal/ssa/poset.go @@ -140,12 +140,11 @@ type posetNode struct { // to record that A 0 { io.WriteString(p.w, " <-") @@ -64,14 +78,33 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) { io.WriteString(p.w, "\n") } -func (p stringFuncPrinter) endBlock(b *Block) { +func (p stringFuncPrinter) endBlock(b *Block, reachable bool) { + if !p.printDead && !reachable { + return + } fmt.Fprintln(p.w, " "+b.LongString()) } +func StmtString(p src.XPos) string { + linenumber := "(?) " + if p.IsKnown() { + pfx := "" + if p.IsStmt() == src.PosIsStmt { + pfx = "+" + } + if p.IsStmt() == src.PosNotStmt { + pfx = "-" + } + linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line()) + } + return linenumber +} + func (p stringFuncPrinter) value(v *Value, live bool) { - fmt.Fprint(p.w, " ") - //fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos)) - //fmt.Fprint(p.w, ": ") + if !p.printDead && !live { + return + } + fmt.Fprintf(p.w, " %s", StmtString(v.Pos)) fmt.Fprint(p.w, v.LongString()) if !live { fmt.Fprint(p.w, " DEAD") @@ -103,7 +136,7 @@ func fprintFunc(p funcPrinter, f *Func) { p.value(v, live[v.ID]) printed[v.ID] = true } - p.endBlock(b) + p.endBlock(b, reachable[b.ID]) continue } @@ -151,7 +184,7 @@ func fprintFunc(p funcPrinter, f *Func) { } } - p.endBlock(b) + p.endBlock(b, reachable[b.ID]) } for _, name := range f.Names { p.named(*name, f.NamedValues[*name]) diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index b203584c6b42cf..20c967d0987f41 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -16,6 +16,10 @@ const ( unknown branch = iota positive negative + // The outedges from a jump table are jumpTable0, + // jumpTable0+1, jumpTable0+2, etc. There could be an + // arbitrary number so we can't list them all here. + jumpTable0 ) // relation represents the set of possible relations between @@ -27,17 +31,17 @@ const ( // // E.g. // -// r := relation(...) +// r := relation(...) // -// if v < w { -// newR := r & lt -// } -// if v >= w { -// newR := r & (eq|gt) -// } -// if v != w { -// newR := r & (lt|gt) -// } +// if v < w { +// newR := r & lt +// } +// if v >= w { +// newR := r & (eq|gt) +// } +// if v != w { +// newR := r & (lt|gt) +// } type relation uint const ( @@ -459,15 +463,23 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) { if parent.Func.pass.debug > 1 { parent.Func.Warnl(parent.Pos, "x+d %s w; x:%v %v delta:%v w:%v d:%v", r, x, parent.String(), delta, w.AuxInt, d) } + underflow := true + if l, has := ft.limits[x.ID]; has && delta < 0 { + if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) || + (x.Type.Size() == 4 && l.min >= math.MinInt32-delta) { + underflow = false + } + } + if delta < 0 && !underflow { + // If delta < 0 and x+delta cannot underflow then x > x+delta (that is, x > v) + ft.update(parent, x, v, signed, gt) + } if !w.isGenericIntConst() { // If we know that x+delta > w but w is not constant, we can derive: - // if delta < 0 and x > MinInt - delta, then x > w (because x+delta cannot underflow) + // if delta < 0 and x+delta cannot underflow, then x > w // This is useful for loops with bounds "len(slice)-K" (delta = -K) - if l, has := ft.limits[x.ID]; has && delta < 0 { - if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) || - (x.Type.Size() == 4 && l.min >= math.MinInt32-delta) { - ft.update(parent, x, w, signed, r) - } + if delta < 0 && !underflow { + ft.update(parent, x, w, signed, r) } } else { // With w,delta constants, we want to derive: x+delta > w ⇒ x > w-delta @@ -746,19 +758,19 @@ func (ft *factsTable) cleanup(f *Func) { // By far, the most common redundant pair are generated by bounds checking. // For example for the code: // -// a[i] = 4 -// foo(a[i]) +// a[i] = 4 +// foo(a[i]) // // The compiler will generate the following code: // -// if i >= len(a) { -// panic("not in bounds") -// } -// a[i] = 4 -// if i >= len(a) { -// panic("not in bounds") -// } -// foo(a[i]) +// if i >= len(a) { +// panic("not in bounds") +// } +// a[i] = 4 +// if i >= len(a) { +// panic("not in bounds") +// } +// foo(a[i]) // // The second comparison i >= len(a) is clearly redundant because if the // else branch of the first comparison is executed, we already know that i < len(a). @@ -824,6 +836,15 @@ func prove(f *Func) { } lensVars[b] = append(lensVars[b], v) } + case OpCtz64, OpCtz32, OpCtz16, OpCtz8, OpBitLen64, OpBitLen32, OpBitLen16, OpBitLen8: + ft.update(b, v, ft.zero, signed, gt|eq) + // TODO: we could also do <= 64/32/16/8, if that helped. + case OpAnd64, OpAnd32, OpAnd16, OpAnd8: + ft.update(b, v, v.Args[1], unsigned, lt|eq) + ft.update(b, v, v.Args[0], unsigned, lt|eq) + case OpOr64, OpOr32, OpOr16, OpOr8: + ft.update(b, v, v.Args[1], unsigned, gt|eq) + ft.update(b, v, v.Args[0], unsigned, gt|eq) } } } @@ -937,20 +958,31 @@ 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 { - if p == nil || p.Kind != BlockIf { + if p == nil { return unknown } - // If p and p.Succs[0] are dominators it means that every path - // from entry to b passes through p and p.Succs[0]. We care that - // no path from entry to b passes through p.Succs[1]. If p.Succs[0] - // has one predecessor then (apart from the degenerate case), - // there is no path from entry that can reach b through p.Succs[1]. - // TODO: how about p->yes->b->yes, i.e. a loop in yes. - if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 { - return positive - } - if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 { - return negative + switch p.Kind { + case BlockIf: + // If p and p.Succs[0] are dominators it means that every path + // from entry to b passes through p and p.Succs[0]. We care that + // no path from entry to b passes through p.Succs[1]. If p.Succs[0] + // has one predecessor then (apart from the degenerate case), + // there is no path from entry that can reach b through p.Succs[1]. + // TODO: how about p->yes->b->yes, i.e. a loop in yes. + if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 { + return positive + } + if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 { + return negative + } + case BlockJumpTable: + // TODO: this loop can lead to quadratic behavior, as + // getBranch can be called len(p.Succs) times. + for i, e := range p.Succs { + if sdom.IsAncestorEq(e.b, b) && len(e.b.Preds) == 1 { + return jumpTable0 + branch(i) + } + } } return unknown } @@ -981,11 +1013,36 @@ func addIndVarRestrictions(ft *factsTable, b *Block, iv indVar) { // branching from Block b in direction br. func addBranchRestrictions(ft *factsTable, b *Block, br branch) { c := b.Controls[0] - switch br { - case negative: + switch { + case br == negative: addRestrictions(b, ft, boolean, nil, c, eq) - case positive: + case br == positive: addRestrictions(b, ft, boolean, nil, c, lt|gt) + case br >= jumpTable0: + idx := br - jumpTable0 + val := int64(idx) + if v, off := isConstDelta(c); v != nil { + // Establish the bound on the underlying value we're switching on, + // not on the offset-ed value used as the jump table index. + c = v + val -= off + } + old, ok := ft.limits[c.ID] + if !ok { + old = noLimit + } + ft.limitStack = append(ft.limitStack, limitFact{c.ID, old}) + if val < old.min || val > old.max || uint64(val) < old.umin || uint64(val) > old.umax { + ft.unsat = true + if b.Func.pass.debug > 2 { + b.Func.Warnl(b.Pos, "block=%s outedge=%d %s=%d unsat", b, idx, c, val) + } + } else { + ft.limits[c.ID] = limit{val, val, uint64(val), uint64(val)} + if b.Func.pass.debug > 2 { + b.Func.Warnl(b.Pos, "block=%s outedge=%d %s=%d", b, idx, c, val) + } + } default: panic("unknown branch") } @@ -1053,8 +1110,7 @@ func addRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r rel // addLocalInductiveFacts adds inductive facts when visiting b, where // b is a join point in a loop. In contrast with findIndVar, this // depends on facts established for b, which is why it happens when -// visiting b. addLocalInductiveFacts specifically targets the pattern -// created by OFORUNTIL, which isn't detected by findIndVar. +// visiting b. // // TODO: It would be nice to combine this with findIndVar. func addLocalInductiveFacts(ft *factsTable, b *Block) { @@ -1179,13 +1235,13 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { // Replace OpSlicemask operations in b with constants where possible. x, delta := isConstDelta(v.Args[0]) if x == nil { - continue + break } // slicemask(x + y) // if x is larger than -y (y is negative), then slicemask is -1. lim, ok := ft.limits[x.ID] if !ok { - continue + break } if lim.umin > uint64(-delta) { if v.Args[0].Op == OpAdd64 { @@ -1205,7 +1261,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { x := v.Args[0] lim, ok := ft.limits[x.ID] if !ok { - continue + break } if lim.umin > 0 || lim.min > 0 || lim.max < 0 { if b.Func.pass.debug > 0 { @@ -1237,7 +1293,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { panic("unexpected integer size") } v.AuxInt = 0 - continue // Be sure not to fallthrough - this is no longer OpRsh. + break // Be sure not to fallthrough - this is no longer OpRsh. } // If the Rsh hasn't been replaced with 0, still check if it is bounded. fallthrough @@ -1254,7 +1310,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { by := v.Args[1] lim, ok := ft.limits[by.ID] if !ok { - continue + break } bits := 8 * v.Args[0].Type.Size() if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) { @@ -1288,6 +1344,60 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { } } } + // Fold provable constant results. + // Helps in cases where we reuse a value after branching on its equality. + for i, arg := range v.Args { + switch arg.Op { + case OpConst64, OpConst32, OpConst16, OpConst8: + continue + } + lim, ok := ft.limits[arg.ID] + if !ok { + continue + } + + var constValue int64 + typ := arg.Type + bits := 8 * typ.Size() + switch { + case lim.min == lim.max: + constValue = lim.min + case lim.umin == lim.umax: + // truncate then sign extand + switch bits { + case 64: + constValue = int64(lim.umin) + case 32: + constValue = int64(int32(lim.umin)) + case 16: + constValue = int64(int16(lim.umin)) + case 8: + constValue = int64(int8(lim.umin)) + default: + panic("unexpected integer size") + } + default: + continue + } + var c *Value + f := b.Func + switch bits { + case 64: + c = f.ConstInt64(typ, constValue) + case 32: + c = f.ConstInt32(typ, int32(constValue)) + case 16: + c = f.ConstInt16(typ, int16(constValue)) + case 8: + c = f.ConstInt8(typ, int8(constValue)) + default: + panic("unexpected integer size") + } + v.SetArg(i, c) + if b.Func.pass.debug > 1 { + b.Func.Warnl(v.Pos, "Proved %v's arg %d (%v) is constant %d", v, i, arg, constValue) + } + } } if b.Kind != BlockIf { @@ -1340,10 +1450,14 @@ func removeBranch(b *Block, branch branch) { // attempt to preserve statement marker. b.Pos = b.Pos.WithIsStmt() } - b.Kind = BlockFirst - b.ResetControls() - if branch == positive { - b.swapSuccessors() + if branch == positive || branch == negative { + b.Kind = BlockFirst + b.ResetControls() + if branch == positive { + b.swapSuccessors() + } + } else { + // TODO: figure out how to remove an entry from a jump table } } @@ -1372,7 +1486,9 @@ func isNonNegative(v *Value) bool { case OpStringLen, OpSliceLen, OpSliceCap, OpZeroExt8to64, OpZeroExt16to64, OpZeroExt32to64, OpZeroExt8to32, OpZeroExt16to32, OpZeroExt8to16, - OpCtz64, OpCtz32, OpCtz16, OpCtz8: + OpCtz64, OpCtz32, OpCtz16, OpCtz8, + OpCtz64NonZero, OpCtz32NonZero, OpCtz16NonZero, OpCtz8NonZero, + OpBitLen64, OpBitLen32, OpBitLen16, OpBitLen8: return true case OpRsh64Ux64, OpRsh32Ux64: diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 3b90b8769c2f1d..02faf8a7bdad1f 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -559,7 +559,8 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, pos func isLeaf(f *Func) bool { for _, b := range f.Blocks { for _, v := range b.Values { - if opcodeTable[v.Op].call { + if v.Op.IsCall() && !v.Op.IsTailCall() { + // tail call is not counted as it does not save the return PC or need a frame return false } } @@ -620,20 +621,22 @@ func (s *regAllocState) init(f *Func) { } if s.f.Config.ctxt.Flag_dynlink { switch s.f.Config.arch { - case "amd64": - s.allocatable &^= 1 << 15 // R15 - case "arm": - s.allocatable &^= 1 << 9 // R9 - case "ppc64le": // R2 already reserved. - // nothing to do - case "arm64": - // nothing to do? case "386": // nothing to do. // Note that for Flag_shared (position independent code) // we do need to be careful, but that carefulness is hidden // in the rewrite rules so we always have a free register // available for global load/stores. See gen/386.rules (search for Flag_shared). + case "amd64": + s.allocatable &^= 1 << 15 // R15 + case "arm": + s.allocatable &^= 1 << 9 // R9 + case "arm64": + // nothing to do + case "ppc64le": // R2 already reserved. + // nothing to do + case "riscv64": // X3 (aka GP) and X4 (aka TP) already reserved. + // nothing to do case "s390x": s.allocatable &^= 1 << 11 // R11 default: @@ -1234,7 +1237,7 @@ func (s *regAllocState) regalloc(f *Func) { desired.clobber(j.regs) desired.add(v.Args[j.idx].ID, pickReg(j.regs)) } - if opcodeTable[v.Op].resultInArg0 { + if opcodeTable[v.Op].resultInArg0 || v.Op == OpAMD64ADDQconst || v.Op == OpAMD64ADDLconst || v.Op == OpSelect0 { if opcodeTable[v.Op].commutative { desired.addList(v.Args[1].ID, prefs) } @@ -1595,11 +1598,13 @@ func (s *regAllocState) regalloc(f *Func) { } } } - for _, r := range dinfo[idx].out { - if r != noRegister && (mask&^s.used)>>r&1 != 0 { - // Desired register is allowed and unused. - mask = regMask(1) << r - break + if out.idx == 0 { // desired registers only apply to the first element of a tuple result + for _, r := range dinfo[idx].out { + if r != noRegister && (mask&^s.used)>>r&1 != 0 { + // Desired register is allowed and unused. + mask = regMask(1) << r + break + } } } // Avoid registers we're saving for other values. @@ -1840,7 +1845,7 @@ func (s *regAllocState) regalloc(f *Func) { if s.f.pass.debug > regDebug { fmt.Printf("delete copied value %s\n", c.LongString()) } - c.RemoveArg(0) + c.resetArgs() f.freeValue(c) delete(s.copies, c) progress = true @@ -1865,23 +1870,6 @@ func (s *regAllocState) regalloc(f *Func) { } func (s *regAllocState) placeSpills() { - f := s.f - - // Precompute some useful info. - phiRegs := make([]regMask, f.NumBlocks()) - for _, b := range s.visitOrder { - var m regMask - for _, v := range b.Values { - if v.Op != OpPhi { - break - } - if r, ok := f.getHome(v.ID).(*Register); ok { - m |= regMask(1) << uint(r.num) - } - } - phiRegs[b.ID] = m - } - mustBeFirst := func(op Op) bool { return op.isLoweredGetClosurePtr() || op == OpPhi || op == OpArgIntReg || op == OpArgFloatReg } @@ -2595,7 +2583,12 @@ func (s *regAllocState) computeLive() { desired.add(v.Args[j.idx].ID, pickReg(j.regs)) } // Set desired register of input 0 if this is a 2-operand instruction. - if opcodeTable[v.Op].resultInArg0 { + if opcodeTable[v.Op].resultInArg0 || v.Op == OpAMD64ADDQconst || v.Op == OpAMD64ADDLconst || v.Op == OpSelect0 { + // ADDQconst is added here because we want to treat it as resultInArg0 for + // the purposes of desired registers, even though it is not an absolute requirement. + // This is because we'd rather implement it as ADDQ instead of LEAQ. + // Same for ADDLconst + // Select0 is added here to propagate the desired register to the tuple-generating instruction. if opcodeTable[v.Op].commutative { desired.addList(v.Args[1].ID, prefs) } @@ -2720,6 +2713,8 @@ type desiredStateEntry struct { ID ID // Registers it would like to be in, in priority order. // Unused slots are filled with noRegister. + // For opcodes that return tuples, we track desired registers only + // for the first element of the tuple. regs [4]register } diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 375c4d5a5605f5..c95d8734564c49 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -5,6 +5,7 @@ package ssa import ( + "cmd/compile/internal/base" "cmd/compile/internal/logopt" "cmd/compile/internal/types" "cmd/internal/obj" @@ -36,8 +37,11 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu if debug > 1 { fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name) } + var iters int + var states map[string]bool for { change := false + deadChange := false for _, b := range f.Blocks { var b0 *Block if debug > 1 { @@ -71,7 +75,7 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu // Not quite a deadcode pass, because it does not handle cycles. // But it should help Uses==1 rules to fire. v.reset(OpInvalid) - change = true + deadChange = true } // No point rewriting values which aren't used. continue @@ -143,9 +147,34 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu } } } - if !change { + if !change && !deadChange { break } + iters++ + if (iters > 1000 || debug >= 2) && change { + // We've done a suspiciously large number of rewrites (or we're in debug mode). + // As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer + // and the maximum value encountered during make.bash is 12. + // Start checking for cycles. (This is too expensive to do routinely.) + // Note: we avoid this path for deadChange-only iterations, to fix #51639. + if states == nil { + states = make(map[string]bool) + } + h := f.rewriteHash() + if _, ok := states[h]; ok { + // We've found a cycle. + // To diagnose it, set debug to 2 and start again, + // so that we'll print all rules applied until we complete another cycle. + // If debug is already >= 2, we've already done that, so it's time to crash. + if debug < 2 { + debug = 2 + states = make(map[string]bool) + } else { + f.Fatalf("rewrite cycle detected") + } + } + states[h] = true + } } // remove clobbered values for _, b := range f.Blocks { @@ -389,6 +418,11 @@ func isSameCall(sym interface{}, name string) bool { return fn != nil && fn.String() == name } +// canLoadUnaligned reports if the architecture supports unaligned load operations +func canLoadUnaligned(c *Config) bool { + return c.ctxt.Arch.Alignment == 1 +} + // nlz returns the number of leading zeros. func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) } func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) } @@ -745,27 +779,21 @@ func uaddOvf(a, b int64) bool { return uint64(a)+uint64(b) < uint64(a) } -// de-virtualize an InterCall -// 'sym' is the symbol for the itab -func devirt(v *Value, aux Aux, sym Sym, offset int64) *AuxCall { - f := v.Block.Func - n, ok := sym.(*obj.LSym) - if !ok { +// loadLSymOffset simulates reading a word at an offset into a +// read-only symbol's runtime memory. If it would read a pointer to +// another symbol, that symbol is returned. Otherwise, it returns nil. +func loadLSymOffset(lsym *obj.LSym, offset int64) *obj.LSym { + if lsym.Type != objabi.SRODATA { return nil } - lsym := f.fe.DerefItab(n, offset) - if f.pass.debug > 0 { - if lsym != nil { - f.Warnl(v.Pos, "de-virtualizing call") - } else { - f.Warnl(v.Pos, "couldn't de-virtualize call") + + for _, r := range lsym.R { + if int64(r.Off) == offset && r.Type&^objabi.R_WEAK == objabi.R_ADDR && r.Add == 0 { + return r.Sym } } - if lsym == nil { - return nil - } - va := aux.(*AuxCall) - return StaticAuxCall(lsym, va.abiInfo) + + return nil } // de-virtualize an InterLECall @@ -776,18 +804,14 @@ func devirtLESym(v *Value, aux Aux, sym Sym, offset int64) *obj.LSym { return nil } - f := v.Block.Func - lsym := f.fe.DerefItab(n, offset) - if f.pass.debug > 0 { + lsym := loadLSymOffset(n, offset) + if f := v.Block.Func; f.pass.debug > 0 { if lsym != nil { f.Warnl(v.Pos, "de-virtualizing call") } else { f.Warnl(v.Pos, "couldn't de-virtualize call") } } - if lsym == nil { - return nil - } return lsym } @@ -795,7 +819,11 @@ func devirtLECall(v *Value, sym *obj.LSym) *Value { v.Op = OpStaticLECall auxcall := v.Aux.(*AuxCall) auxcall.Fn = sym - v.RemoveArg(0) + // Remove first arg + v.Args[0].Uses-- + copy(v.Args[0:], v.Args[1:]) + v.Args[len(v.Args)-1] = nil // aid GC + v.Args = v.Args[:len(v.Args)-1] return v } @@ -935,8 +963,9 @@ found: // clobber invalidates values. Returns true. // clobber is used by rewrite rules to: -// A) make sure the values are really dead and never used again. -// B) decrement use counts of the values' args. +// +// A) make sure the values are really dead and never used again. +// B) decrement use counts of the values' args. func clobber(vv ...*Value) bool { for _, v := range vv { v.reset(OpInvalid) @@ -958,7 +987,9 @@ func clobberIfDead(v *Value) bool { // noteRule is an easy way to track if a rule is matched when writing // new ones. Make the rule of interest also conditional on -// noteRule("note to self: rule of interest matched") +// +// noteRule("note to self: rule of interest matched") +// // and that message will print when the rule matches. func noteRule(s string) bool { fmt.Println(s) @@ -1263,7 +1294,7 @@ func zeroUpper32Bits(x *Value, depth int) bool { OpAMD64SHLL, OpAMD64SHLLconst: return true case OpArg: - return x.Type.Width == 4 + return x.Type.Size() == 4 case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. @@ -1287,7 +1318,7 @@ func zeroUpper48Bits(x *Value, depth int) bool { case OpAMD64MOVWQZX, OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVWloadidx2: return true case OpArg: - return x.Type.Width == 2 + return x.Type.Size() == 2 case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. @@ -1311,7 +1342,7 @@ func zeroUpper56Bits(x *Value, depth int) bool { case OpAMD64MOVBQZX, OpAMD64MOVBload, OpAMD64MOVBloadidx1: return true case OpArg: - return x.Type.Width == 1 + return x.Type.Size() == 1 case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. @@ -1331,7 +1362,8 @@ func zeroUpper56Bits(x *Value, depth int) bool { // isInlinableMemmove reports whether the given arch performs a Move of the given size // faster than memmove. It will only return true if replacing the memmove with a Move is -// safe, either because Move is small or because the arguments are disjoint. +// safe, either because Move will do all of its loads before any of its stores, or +// because the arguments are known to be disjoint. // This is used as a check for replacing memmove with Move ops. func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool { // It is always safe to convert memmove into Move when its arguments are disjoint. @@ -1345,11 +1377,14 @@ func isInlinableMemmove(dst, src *Value, sz int64, c *Config) bool { return sz <= 8 case "s390x", "ppc64", "ppc64le": return sz <= 8 || disjoint(dst, sz, src, sz) - case "arm", "mips", "mips64", "mipsle", "mips64le": + case "arm", "loong64", "mips", "mips64", "mipsle", "mips64le": return sz <= 4 } return false } +func IsInlinableMemmove(dst, src *Value, sz int64, c *Config) bool { + return isInlinableMemmove(dst, src, sz, c) +} // logLargeCopy logs the occurrence of a large copy. // The best place to do this is in the rewrite rules where the size of the move is easy to find. @@ -1363,6 +1398,14 @@ func logLargeCopy(v *Value, s int64) bool { } return true } +func LogLargeCopy(funcName string, pos src.XPos, s int64) { + if s < 128 { + return + } + if logopt.Enabled() { + logopt.LogOpt(pos, "copy", "lower", funcName, fmt.Sprintf("%d bytes", s)) + } +} // hasSmallRotate reports whether the architecture has rotate instructions // for sizes < 32-bit. This is used to decide whether to promote some rotations. @@ -1551,12 +1594,16 @@ func rotateLeft32(v, rotate int64) int64 { return int64(bits.RotateLeft32(uint32(v), int(rotate))) } +func rotateRight64(v, rotate int64) int64 { + return int64(bits.RotateLeft64(uint64(v), int(-rotate))) +} + // encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format. func armBFAuxInt(lsb, width int64) arm64BitField { if lsb < 0 || lsb > 63 { panic("ARM(64) bit field lsb constant out of range") } - if width < 1 || width > 64 { + if width < 1 || lsb+width > 64 { panic("ARM(64) bit field width constant out of range") } return arm64BitField(width | lsb<<8) @@ -1597,7 +1644,7 @@ func sizeof(t interface{}) int64 { // a register. It assumes float64 values will always fit into registers // even if that isn't strictly true. func registerizable(b *Block, typ *types.Type) bool { - if typ.IsPtrShaped() || typ.IsFloat() { + if typ.IsPtrShaped() || typ.IsFloat() || typ.IsBoolean() { return true } if typ.IsInteger() { @@ -1729,6 +1776,9 @@ func read64(sym interface{}, off int64, byteorder binary.ByteOrder) uint64 { // sequentialAddresses reports true if it can prove that x + n == y func sequentialAddresses(x, y *Value, n int64) bool { + if x == y && n == 0 { + return true + } if x.Op == Op386ADDL && y.Op == Op386LEAL1 && y.AuxInt == n && y.Aux == nil && (x.Args[0] == y.Args[0] && x.Args[1] == y.Args[1] || x.Args[0] == y.Args[1] && x.Args[1] == y.Args[0]) { @@ -1758,9 +1808,11 @@ func sequentialAddresses(x, y *Value, n int64) bool { // We happen to match the semantics to those of arm/arm64. // Note that these semantics differ from x86: the carry flag has the opposite // sense on a subtraction! -// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C. -// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C. -// (because it does x + ^y + C). +// +// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C. +// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C. +// (because it does x + ^y + C). +// // See https://en.wikipedia.org/wiki/Carry_flag#Vs._borrow_flag type flagConstant uint8 @@ -1918,3 +1970,27 @@ func logicFlags32(x int32) flagConstant { fcb.N = x < 0 return fcb.encode() } + +func makeJumpTableSym(b *Block) *obj.LSym { + s := base.Ctxt.Lookup(fmt.Sprintf("%s.jump%d", b.Func.fe.LSym(), b.ID)) + s.Set(obj.AttrDuplicateOK, true) + s.Set(obj.AttrLocal, true) + return s +} + +// canRotate reports whether the architecture supports +// rotates of integer registers with the given number of bits. +func canRotate(c *Config, bits int64) bool { + if bits > c.PtrSize*8 { + // Don't rewrite to rotates bigger than the machine word. + return false + } + switch c.arch { + case "386", "amd64", "arm64": + return true + case "arm", "s390x", "ppc64", "ppc64le", "wasm", "loong64": + return bits >= 32 + default: + return false + } +} diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go index 1ec2d26f750f6b..08d81451f5fed0 100644 --- a/src/cmd/compile/internal/ssa/rewrite386.go +++ b/src/cmd/compile/internal/ssa/rewrite386.go @@ -146,10 +146,16 @@ func rewriteValue386(v *Value) bool { return rewriteValue386_Op386ORLload(v) case Op386ORLmodify: return rewriteValue386_Op386ORLmodify(v) + case Op386ROLB: + return rewriteValue386_Op386ROLB(v) case Op386ROLBconst: return rewriteValue386_Op386ROLBconst(v) + case Op386ROLL: + return rewriteValue386_Op386ROLL(v) case Op386ROLLconst: return rewriteValue386_Op386ROLLconst(v) + case Op386ROLW: + return rewriteValue386_Op386ROLW(v) case Op386ROLWconst: return rewriteValue386_Op386ROLWconst(v) case Op386SARB: @@ -541,11 +547,14 @@ func rewriteValue386(v *Value) bool { case OpPanicExtend: return rewriteValue386_OpPanicExtend(v) case OpRotateLeft16: - return rewriteValue386_OpRotateLeft16(v) + v.Op = Op386ROLW + return true case OpRotateLeft32: - return rewriteValue386_OpRotateLeft32(v) + v.Op = Op386ROLL + return true case OpRotateLeft8: - return rewriteValue386_OpRotateLeft8(v) + v.Op = Op386ROLB + return true case OpRound32F: v.Op = OpCopy return true @@ -652,6 +661,9 @@ func rewriteValue386(v *Value) bool { case OpSubPtr: v.Op = Op386SUBL return true + case OpTailCall: + v.Op = Op386CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true @@ -731,80 +743,6 @@ func rewriteValue386_Op386ADDL(v *Value) bool { } break } - // match: (ADDL (SHLLconst [c] x) (SHRLconst [d] x)) - // cond: d == 32-c - // result: (ROLLconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRLconst { - continue - } - d := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(Op386ROLLconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (ADDL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: c < 16 && d == int16(16-c) && t.Size() == 2 - // result: (ROLWconst x [int16(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRWconst { - continue - } - d := auxIntToInt16(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 16 && d == int16(16-c) && t.Size() == 2) { - continue - } - v.reset(Op386ROLWconst) - v.AuxInt = int16ToAuxInt(int16(c)) - v.AddArg(x) - return true - } - break - } - // match: (ADDL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: c < 8 && d == int8(8-c) && t.Size() == 1 - // result: (ROLBconst x [int8(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRBconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 8 && d == int8(8-c) && t.Size() == 1) { - continue - } - v.reset(Op386ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c)) - v.AddArg(x) - return true - } - break - } // match: (ADDL x (SHLLconst [3] y)) // result: (LEAL8 x y) for { @@ -6302,80 +6240,6 @@ func rewriteValue386_Op386ORL(v *Value) bool { } break } - // match: ( ORL (SHLLconst [c] x) (SHRLconst [d] x)) - // cond: d == 32-c - // result: (ROLLconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRLconst { - continue - } - d := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(Op386ROLLconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: ( ORL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: c < 16 && d == int16(16-c) && t.Size() == 2 - // result: (ROLWconst x [int16(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRWconst { - continue - } - d := auxIntToInt16(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 16 && d == int16(16-c) && t.Size() == 2) { - continue - } - v.reset(Op386ROLWconst) - v.AuxInt = int16ToAuxInt(int16(c)) - v.AddArg(x) - return true - } - break - } - // match: ( ORL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: c < 8 && d == int8(8-c) && t.Size() == 1 - // result: (ROLBconst x [int8(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRBconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 8 && d == int8(8-c) && t.Size() == 1) { - continue - } - v.reset(Op386ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c)) - v.AddArg(x) - return true - } - break - } // match: (ORL x l:(MOVLload [off] {sym} ptr mem)) // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (ORLload x [off] {sym} ptr mem) @@ -6806,22 +6670,26 @@ func rewriteValue386_Op386ORLmodify(v *Value) bool { } return false } -func rewriteValue386_Op386ROLBconst(v *Value) bool { +func rewriteValue386_Op386ROLB(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ROLBconst [c] (ROLBconst [d] x)) - // result: (ROLBconst [(c+d)& 7] x) + // match: (ROLB x (MOVLconst [c])) + // result: (ROLBconst [int8(c&7)] x) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != Op386ROLBconst { + x := v_0 + if v_1.Op != Op386MOVLconst { break } - d := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] + c := auxIntToInt32(v_1.AuxInt) v.reset(Op386ROLBconst) - v.AuxInt = int8ToAuxInt((c + d) & 7) + v.AuxInt = int8ToAuxInt(int8(c & 7)) v.AddArg(x) return true } + return false +} +func rewriteValue386_Op386ROLBconst(v *Value) bool { + v_0 := v.Args[0] // match: (ROLBconst [0] x) // result: x for { @@ -6834,22 +6702,26 @@ func rewriteValue386_Op386ROLBconst(v *Value) bool { } return false } -func rewriteValue386_Op386ROLLconst(v *Value) bool { +func rewriteValue386_Op386ROLL(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ROLLconst [c] (ROLLconst [d] x)) - // result: (ROLLconst [(c+d)&31] x) + // match: (ROLL x (MOVLconst [c])) + // result: (ROLLconst [c&31] x) for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != Op386ROLLconst { + x := v_0 + if v_1.Op != Op386MOVLconst { break } - d := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] + c := auxIntToInt32(v_1.AuxInt) v.reset(Op386ROLLconst) - v.AuxInt = int32ToAuxInt((c + d) & 31) + v.AuxInt = int32ToAuxInt(c & 31) v.AddArg(x) return true } + return false +} +func rewriteValue386_Op386ROLLconst(v *Value) bool { + v_0 := v.Args[0] // match: (ROLLconst [0] x) // result: x for { @@ -6862,22 +6734,26 @@ func rewriteValue386_Op386ROLLconst(v *Value) bool { } return false } -func rewriteValue386_Op386ROLWconst(v *Value) bool { +func rewriteValue386_Op386ROLW(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ROLWconst [c] (ROLWconst [d] x)) - // result: (ROLWconst [(c+d)&15] x) + // match: (ROLW x (MOVLconst [c])) + // result: (ROLWconst [int16(c&15)] x) for { - c := auxIntToInt16(v.AuxInt) - if v_0.Op != Op386ROLWconst { + x := v_0 + if v_1.Op != Op386MOVLconst { break } - d := auxIntToInt16(v_0.AuxInt) - x := v_0.Args[0] + c := auxIntToInt32(v_1.AuxInt) v.reset(Op386ROLWconst) - v.AuxInt = int16ToAuxInt((c + d) & 15) + v.AuxInt = int16ToAuxInt(int16(c & 15)) v.AddArg(x) return true } + return false +} +func rewriteValue386_Op386ROLWconst(v *Value) bool { + v_0 := v.Args[0] // match: (ROLWconst [0] x) // result: x for { @@ -8343,80 +8219,6 @@ func rewriteValue386_Op386XORL(v *Value) bool { } break } - // match: (XORL (SHLLconst [c] x) (SHRLconst [d] x)) - // cond: d == 32-c - // result: (ROLLconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRLconst { - continue - } - d := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(Op386ROLLconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (XORL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: c < 16 && d == int16(16-c) && t.Size() == 2 - // result: (ROLWconst x [int16(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRWconst { - continue - } - d := auxIntToInt16(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 16 && d == int16(16-c) && t.Size() == 2) { - continue - } - v.reset(Op386ROLWconst) - v.AuxInt = int16ToAuxInt(int16(c)) - v.AddArg(x) - return true - } - break - } - // match: (XORL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: c < 8 && d == int8(8-c) && t.Size() == 1 - // result: (ROLBconst x [int8(c)]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != Op386SHLLconst { - continue - } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != Op386SHRBconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(c < 8 && d == int8(8-c) && t.Size() == 1) { - continue - } - v.reset(Op386ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c)) - v.AddArg(x) - return true - } - break - } // match: (XORL x l:(MOVLload [off] {sym} ptr mem)) // cond: canMergeLoadClobber(v, l, x) && clobber(l) // result: (XORLload x [off] {sym} ptr mem) @@ -10295,60 +10097,6 @@ func rewriteValue386_OpPanicExtend(v *Value) bool { } return false } -func rewriteValue386_OpRotateLeft16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft16 x (MOVLconst [c])) - // result: (ROLWconst [int16(c&15)] x) - for { - x := v_0 - if v_1.Op != Op386MOVLconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - v.reset(Op386ROLWconst) - v.AuxInt = int16ToAuxInt(int16(c & 15)) - v.AddArg(x) - return true - } - return false -} -func rewriteValue386_OpRotateLeft32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft32 x (MOVLconst [c])) - // result: (ROLLconst [c&31] x) - for { - x := v_0 - if v_1.Op != Op386MOVLconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - v.reset(Op386ROLLconst) - v.AuxInt = int32ToAuxInt(c & 31) - v.AddArg(x) - return true - } - return false -} -func rewriteValue386_OpRotateLeft8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft8 x (MOVLconst [c])) - // result: (ROLBconst [int8(c&7)] x) - for { - x := v_0 - if v_1.Op != Op386MOVLconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - v.reset(Op386ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c & 7)) - v.AddArg(x) - return true - } - return false -} func rewriteValue386_OpRsh16Ux16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 5045ba7351f447..d4af5f03e126c3 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -54,6 +54,10 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64ANDLload(v) case OpAMD64ANDLmodify: return rewriteValueAMD64_OpAMD64ANDLmodify(v) + case OpAMD64ANDNL: + return rewriteValueAMD64_OpAMD64ANDNL(v) + case OpAMD64ANDNQ: + return rewriteValueAMD64_OpAMD64ANDNQ(v) case OpAMD64ANDQ: return rewriteValueAMD64_OpAMD64ANDQ(v) case OpAMD64ANDQconst: @@ -66,6 +70,10 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64ANDQmodify(v) case OpAMD64BSFQ: return rewriteValueAMD64_OpAMD64BSFQ(v) + case OpAMD64BSWAPL: + return rewriteValueAMD64_OpAMD64BSWAPL(v) + case OpAMD64BSWAPQ: + return rewriteValueAMD64_OpAMD64BSWAPQ(v) case OpAMD64BTCLconst: return rewriteValueAMD64_OpAMD64BTCLconst(v) case OpAMD64BTCQconst: @@ -214,6 +222,12 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64LEAQ4(v) case OpAMD64LEAQ8: return rewriteValueAMD64_OpAMD64LEAQ8(v) + case OpAMD64MOVBELstore: + return rewriteValueAMD64_OpAMD64MOVBELstore(v) + case OpAMD64MOVBEQstore: + return rewriteValueAMD64_OpAMD64MOVBEQstore(v) + case OpAMD64MOVBEWstore: + return rewriteValueAMD64_OpAMD64MOVBEWstore(v) case OpAMD64MOVBQSX: return rewriteValueAMD64_OpAMD64MOVBQSX(v) case OpAMD64MOVBQSXload: @@ -250,6 +264,8 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64MOVOload(v) case OpAMD64MOVOstore: return rewriteValueAMD64_OpAMD64MOVOstore(v) + case OpAMD64MOVOstoreconst: + return rewriteValueAMD64_OpAMD64MOVOstoreconst(v) case OpAMD64MOVQatomicload: return rewriteValueAMD64_OpAMD64MOVQatomicload(v) case OpAMD64MOVQf2i: @@ -366,6 +382,14 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64SARW(v) case OpAMD64SARWconst: return rewriteValueAMD64_OpAMD64SARWconst(v) + case OpAMD64SARXL: + return rewriteValueAMD64_OpAMD64SARXL(v) + case OpAMD64SARXLload: + return rewriteValueAMD64_OpAMD64SARXLload(v) + case OpAMD64SARXQ: + return rewriteValueAMD64_OpAMD64SARXQ(v) + case OpAMD64SARXQload: + return rewriteValueAMD64_OpAMD64SARXQload(v) case OpAMD64SBBLcarrymask: return rewriteValueAMD64_OpAMD64SBBLcarrymask(v) case OpAMD64SBBQ: @@ -422,6 +446,14 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64SHLQ(v) case OpAMD64SHLQconst: return rewriteValueAMD64_OpAMD64SHLQconst(v) + case OpAMD64SHLXL: + return rewriteValueAMD64_OpAMD64SHLXL(v) + case OpAMD64SHLXLload: + return rewriteValueAMD64_OpAMD64SHLXLload(v) + case OpAMD64SHLXQ: + return rewriteValueAMD64_OpAMD64SHLXQ(v) + case OpAMD64SHLXQload: + return rewriteValueAMD64_OpAMD64SHLXQload(v) case OpAMD64SHRB: return rewriteValueAMD64_OpAMD64SHRB(v) case OpAMD64SHRBconst: @@ -438,6 +470,14 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64SHRW(v) case OpAMD64SHRWconst: return rewriteValueAMD64_OpAMD64SHRWconst(v) + case OpAMD64SHRXL: + return rewriteValueAMD64_OpAMD64SHRXL(v) + case OpAMD64SHRXLload: + return rewriteValueAMD64_OpAMD64SHRXLload(v) + case OpAMD64SHRXQ: + return rewriteValueAMD64_OpAMD64SHRXQ(v) + case OpAMD64SHRXQload: + return rewriteValueAMD64_OpAMD64SHRXQload(v) case OpAMD64SUBL: return rewriteValueAMD64_OpAMD64SUBL(v) case OpAMD64SUBLconst: @@ -641,13 +681,11 @@ func rewriteValueAMD64(v *Value) bool { case OpCtz16: return rewriteValueAMD64_OpCtz16(v) case OpCtz16NonZero: - v.Op = OpAMD64BSFL - return true + return rewriteValueAMD64_OpCtz16NonZero(v) case OpCtz32: return rewriteValueAMD64_OpCtz32(v) case OpCtz32NonZero: - v.Op = OpAMD64BSFL - return true + return rewriteValueAMD64_OpCtz32NonZero(v) case OpCtz64: return rewriteValueAMD64_OpCtz64(v) case OpCtz64NonZero: @@ -655,8 +693,7 @@ func rewriteValueAMD64(v *Value) bool { case OpCtz8: return rewriteValueAMD64_OpCtz8(v) case OpCtz8NonZero: - v.Op = OpAMD64BSFL - return true + return rewriteValueAMD64_OpCtz8NonZero(v) case OpCvt32Fto32: v.Op = OpAMD64CVTTSS2SL return true @@ -950,6 +987,12 @@ func rewriteValueAMD64(v *Value) bool { return true case OpPopCount8: return rewriteValueAMD64_OpPopCount8(v) + case OpPrefetchCache: + v.Op = OpAMD64PrefetchT0 + return true + case OpPrefetchCacheStreamed: + v.Op = OpAMD64PrefetchNTA + return true case OpRotateLeft16: v.Op = OpAMD64ROLW return true @@ -1096,6 +1139,9 @@ func rewriteValueAMD64(v *Value) bool { case OpSubPtr: v.Op = OpAMD64SUBQ return true + case OpTailCall: + v.Op = OpAMD64CALLtail + return true case OpTrunc: return rewriteValueAMD64_OpTrunc(v) case OpTrunc16to8: @@ -1230,80 +1276,6 @@ func rewriteValueAMD64_OpAMD64ADDL(v *Value) bool { } break } - // match: (ADDL (SHLLconst x [c]) (SHRLconst x [d])) - // cond: d==32-c - // result: (ROLLconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRLconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (ADDL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: d==16-c && c < 16 && t.Size() == 2 - // result: (ROLWconst x [c]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRWconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 16-c && c < 16 && t.Size() == 2) { - continue - } - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (ADDL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: d==8-c && c < 8 && t.Size() == 1 - // result: (ROLBconst x [c]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRBconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 8-c && c < 8 && t.Size() == 1) { - continue - } - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (ADDL x (SHLLconst [3] y)) // result: (LEAL8 x y) for { @@ -1869,30 +1841,6 @@ func rewriteValueAMD64_OpAMD64ADDQ(v *Value) bool { } break } - // match: (ADDQ (SHLQconst x [c]) (SHRQconst x [d])) - // cond: d==64-c - // result: (ROLQconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRQconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { - continue - } - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (ADDQ x (SHLQconst [3] y)) // result: (LEAQ8 x y) for { @@ -2678,6 +2626,29 @@ func rewriteValueAMD64_OpAMD64ANDL(v *Value) bool { } break } + // match: (ANDL (NOTL (SHLXL (MOVLconst [1]) y)) x) + // result: (BTRL x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64NOTL { + continue + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64SHLXL { + continue + } + y := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { + continue + } + x := v_1 + v.reset(OpAMD64BTRL) + v.AddArg2(x, y) + return true + } + break + } // match: (ANDL (MOVLconst [c]) x) // cond: isUint32PowerOfTwo(int64(^c)) && uint64(^c) >= 128 // result: (BTRLconst [int8(log32(^c))] x) @@ -2749,6 +2720,55 @@ func rewriteValueAMD64_OpAMD64ANDL(v *Value) bool { } break } + // match: (ANDL x (NOTL y)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (ANDNL x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64NOTL { + continue + } + y := v_1.Args[0] + if !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64ANDNL) + v.AddArg2(x, y) + return true + } + break + } + // match: (ANDL x (NEGL x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSIL x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64NEGL || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSIL) + v.AddArg(x) + return true + } + break + } + // match: (ANDL x (ADDLconst [-1] x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSRL x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64ADDLconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSRL) + v.AddArg(x) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64ANDLconst(v *Value) bool { @@ -3027,6 +3047,80 @@ func rewriteValueAMD64_OpAMD64ANDLmodify(v *Value) bool { } return false } +func rewriteValueAMD64_OpAMD64ANDNL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ANDNL x (SHLL (MOVLconst [1]) y)) + // result: (BTRL x y) + for { + x := v_0 + if v_1.Op != OpAMD64SHLL { + break + } + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0.AuxInt) != 1 { + break + } + v.reset(OpAMD64BTRL) + v.AddArg2(x, y) + return true + } + // match: (ANDNL x (SHLXL (MOVLconst [1]) y)) + // result: (BTRL x y) + for { + x := v_0 + if v_1.Op != OpAMD64SHLXL { + break + } + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0.AuxInt) != 1 { + break + } + v.reset(OpAMD64BTRL) + v.AddArg2(x, y) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ANDNQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ANDNQ x (SHLQ (MOVQconst [1]) y)) + // result: (BTRQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64SHLQ { + break + } + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0.AuxInt) != 1 { + break + } + v.reset(OpAMD64BTRQ) + v.AddArg2(x, y) + return true + } + // match: (ANDNQ x (SHLXQ (MOVQconst [1]) y)) + // result: (BTRQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64SHLXQ { + break + } + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0.AuxInt) != 1 { + break + } + v.reset(OpAMD64BTRQ) + v.AddArg2(x, y) + return true + } + return false +} func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -3053,6 +3147,29 @@ func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool { } break } + // match: (ANDQ (NOTQ (SHLXQ (MOVQconst [1]) y)) x) + // result: (BTRQ x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64NOTQ { + continue + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64SHLXQ { + continue + } + y := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + x := v_1 + v.reset(OpAMD64BTRQ) + v.AddArg2(x, y) + return true + } + break + } // match: (ANDQ (MOVQconst [c]) x) // cond: isUint64PowerOfTwo(^c) && uint64(^c) >= 128 // result: (BTRQconst [int8(log64(^c))] x) @@ -3128,6 +3245,55 @@ func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool { } break } + // match: (ANDQ x (NOTQ y)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (ANDNQ x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64NOTQ { + continue + } + y := v_1.Args[0] + if !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64ANDNQ) + v.AddArg2(x, y) + return true + } + break + } + // match: (ANDQ x (NEGQ x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSIQ x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64NEGQ || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSIQ) + v.AddArg(x) + return true + } + break + } + // match: (ANDQ x (ADDQconst [-1] x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSRQ x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64ADDQconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSRQ) + v.AddArg(x) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64ANDQconst(v *Value) bool { @@ -3455,6 +3621,108 @@ func rewriteValueAMD64_OpAMD64BSFQ(v *Value) bool { } return false } +func rewriteValueAMD64_OpAMD64BSWAPL(v *Value) bool { + v_0 := v.Args[0] + // match: (BSWAPL (BSWAPL p)) + // result: p + for { + if v_0.Op != OpAMD64BSWAPL { + break + } + p := v_0.Args[0] + v.copyOf(p) + return true + } + // match: (BSWAPL x:(MOVLload [i] {s} p mem)) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBELload [i] {s} p mem) + for { + x := v_0 + if x.Op != OpAMD64MOVLload { + break + } + i := auxIntToInt32(x.AuxInt) + s := auxToSym(x.Aux) + mem := x.Args[1] + p := x.Args[0] + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64MOVBELload) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg2(p, mem) + return true + } + // match: (BSWAPL (MOVBELload [i] {s} p m)) + // result: (MOVLload [i] {s} p m) + for { + if v_0.Op != OpAMD64MOVBELload { + break + } + i := auxIntToInt32(v_0.AuxInt) + s := auxToSym(v_0.Aux) + m := v_0.Args[1] + p := v_0.Args[0] + v.reset(OpAMD64MOVLload) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg2(p, m) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64BSWAPQ(v *Value) bool { + v_0 := v.Args[0] + // match: (BSWAPQ (BSWAPQ p)) + // result: p + for { + if v_0.Op != OpAMD64BSWAPQ { + break + } + p := v_0.Args[0] + v.copyOf(p) + return true + } + // match: (BSWAPQ x:(MOVQload [i] {s} p mem)) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBEQload [i] {s} p mem) + for { + x := v_0 + if x.Op != OpAMD64MOVQload { + break + } + i := auxIntToInt32(x.AuxInt) + s := auxToSym(x.Aux) + mem := x.Args[1] + p := x.Args[0] + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64MOVBEQload) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg2(p, mem) + return true + } + // match: (BSWAPQ (MOVBEQload [i] {s} p m)) + // result: (MOVQload [i] {s} p m) + for { + if v_0.Op != OpAMD64MOVBEQload { + break + } + i := auxIntToInt32(v_0.AuxInt) + s := auxToSym(v_0.Aux) + m := v_0.Args[1] + p := v_0.Args[0] + v.reset(OpAMD64MOVQload) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg2(p, m) + return true + } + return false +} func rewriteValueAMD64_OpAMD64BTCLconst(v *Value) bool { v_0 := v.Args[0] // match: (BTCLconst [c] (XORLconst [d] x)) @@ -3605,6 +3873,22 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool { v.AddArg2(y, x) return true } + // match: (BTLconst [0] s:(SHRXQ x y)) + // result: (BTQ y x) + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + s := v_0 + if s.Op != OpAMD64SHRXQ { + break + } + y := s.Args[1] + x := s.Args[0] + v.reset(OpAMD64BTQ) + v.AddArg2(y, x) + return true + } // match: (BTLconst [c] (SHRLconst [d] x)) // cond: (c+d)<32 // result: (BTLconst [c+d] x) @@ -3657,6 +3941,22 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool { v.AddArg2(y, x) return true } + // match: (BTLconst [0] s:(SHRXL x y)) + // result: (BTL y x) + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + s := v_0 + if s.Op != OpAMD64SHRXL { + break + } + y := s.Args[1] + x := s.Args[0] + v.reset(OpAMD64BTL) + v.AddArg2(y, x) + return true + } return false } func rewriteValueAMD64_OpAMD64BTQconst(v *Value) bool { @@ -3713,6 +4013,22 @@ func rewriteValueAMD64_OpAMD64BTQconst(v *Value) bool { v.AddArg2(y, x) return true } + // match: (BTQconst [0] s:(SHRXQ x y)) + // result: (BTQ y x) + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + s := v_0 + if s.Op != OpAMD64SHRXQ { + break + } + y := s.Args[1] + x := s.Args[0] + v.reset(OpAMD64BTQ) + v.AddArg2(y, x) + return true + } return false } func rewriteValueAMD64_OpAMD64BTRLconst(v *Value) bool { @@ -4929,6 +5245,29 @@ func rewriteValueAMD64_OpAMD64CMOVQEQ(v *Value) bool { v.copyOf(x) return true } + // match: (CMOVQEQ x _ (Select1 (BSRQ (ORQconst [c] _)))) + // cond: c != 0 + // result: x + for { + x := v_0 + if v_2.Op != OpSelect1 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpAMD64BSRQ { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpAMD64ORQconst { + break + } + c := auxIntToInt32(v_2_0_0.AuxInt) + if !(c != 0) { + break + } + v.copyOf(x) + return true + } return false } func rewriteValueAMD64_OpAMD64CMOVQGE(v *Value) bool { @@ -7079,40 +7418,6 @@ func rewriteValueAMD64_OpAMD64CMPQ(v *Value) bool { func rewriteValueAMD64_OpAMD64CMPQconst(v *Value) bool { v_0 := v.Args[0] b := v.Block - // match: (CMPQconst (NEGQ (ADDQconst [-16] (ANDQconst [15] _))) [32]) - // result: (FlagLT_ULT) - for { - if auxIntToInt32(v.AuxInt) != 32 || v_0.Op != OpAMD64NEGQ { - break - } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_0_0.AuxInt) != -16 { - break - } - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_0_0.AuxInt) != 15 { - break - } - v.reset(OpAMD64FlagLT_ULT) - return true - } - // match: (CMPQconst (NEGQ (ADDQconst [ -8] (ANDQconst [7] _))) [32]) - // result: (FlagLT_ULT) - for { - if auxIntToInt32(v.AuxInt) != 32 || v_0.Op != OpAMD64NEGQ { - break - } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_0_0.AuxInt) != -8 { - break - } - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_0_0.AuxInt) != 7 { - break - } - v.reset(OpAMD64FlagLT_ULT) - return true - } // match: (CMPQconst (MOVQconst [x]) [y]) // cond: x==int64(y) // result: (FlagEQ) @@ -9211,6 +9516,80 @@ func rewriteValueAMD64_OpAMD64LEAQ8(v *Value) bool { } return false } +func rewriteValueAMD64_OpAMD64MOVBELstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBELstore [i] {s} p (BSWAPL x) m) + // result: (MOVLstore [i] {s} p x m) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + if v_1.Op != OpAMD64BSWAPL { + break + } + x := v_1.Args[0] + m := v_2 + v.reset(OpAMD64MOVLstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, x, m) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64MOVBEQstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBEQstore [i] {s} p (BSWAPQ x) m) + // result: (MOVQstore [i] {s} p x m) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + if v_1.Op != OpAMD64BSWAPQ { + break + } + x := v_1.Args[0] + m := v_2 + v.reset(OpAMD64MOVQstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, x, m) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64MOVBEWstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBEWstore [i] {s} p x:(ROLWconst [8] w) mem) + // cond: x.Uses == 1 + // result: (MOVWstore [i] {s} p w mem) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64ROLWconst || auxIntToInt8(x.AuxInt) != 8 { + break + } + w := x.Args[0] + mem := v_2 + if !(x.Uses == 1) { + break + } + v.reset(OpAMD64MOVWstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) + return true + } + return false +} func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value) bool { v_0 := v.Args[0] b := v.Block @@ -9631,49 +10010,6 @@ func rewriteValueAMD64_OpAMD64MOVBload(v *Value) bool { v.AddArg2(base, mem) return true } - // match: (MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVBload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(base, mem) - return true - } - // match: (MOVBload [off1] {sym} (ADDLconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVBload [off1+off2] {sym} ptr mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVBload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) - return true - } // match: (MOVBload [off] {sym} (SB) _) // cond: symIsRO(sym) // result: (MOVLconst [int32(read8(sym, int64(off)))]) @@ -10787,54 +11123,62 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value) bool { v.AddArg3(p0, w0, mem) return true } - // match: (MOVBstore [7] {s} p1 (SHRQconst [56] w) x1:(MOVWstore [5] {s} p1 (SHRQconst [40] w) x2:(MOVLstore [1] {s} p1 (SHRQconst [8] w) x3:(MOVBstore [0] {s} p1 w mem)))) - // cond: x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && clobber(x1, x2, x3) - // result: (MOVQstore {s} p1 w mem) + // match: (MOVBstore [c3] {s} p3 (SHRQconst [56] w) x1:(MOVWstore [c2] {s} p2 (SHRQconst [40] w) x2:(MOVLstore [c1] {s} p1 (SHRQconst [8] w) x3:(MOVBstore [c0] {s} p0 w mem)))) + // cond: x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && sequentialAddresses(p0, p1, int64(1 + c0 - c1)) && sequentialAddresses(p0, p2, int64(5 + c0 - c2)) && sequentialAddresses(p0, p3, int64(7 + c0 - c3)) && clobber(x1, x2, x3) + // result: (MOVQstore [c0] {s} p0 w mem) for { - if auxIntToInt32(v.AuxInt) != 7 { - break - } + c3 := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) - p1 := v_0 + p3 := v_0 if v_1.Op != OpAMD64SHRQconst || auxIntToInt8(v_1.AuxInt) != 56 { break } w := v_1.Args[0] x1 := v_2 - if x1.Op != OpAMD64MOVWstore || auxIntToInt32(x1.AuxInt) != 5 || auxToSym(x1.Aux) != s { + if x1.Op != OpAMD64MOVWstore { break } - _ = x1.Args[2] - if p1 != x1.Args[0] { + c2 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { break } + _ = x1.Args[2] + p2 := x1.Args[0] x1_1 := x1.Args[1] if x1_1.Op != OpAMD64SHRQconst || auxIntToInt8(x1_1.AuxInt) != 40 || w != x1_1.Args[0] { break } x2 := x1.Args[2] - if x2.Op != OpAMD64MOVLstore || auxIntToInt32(x2.AuxInt) != 1 || auxToSym(x2.Aux) != s { + if x2.Op != OpAMD64MOVLstore { break } - _ = x2.Args[2] - if p1 != x2.Args[0] { + c1 := auxIntToInt32(x2.AuxInt) + if auxToSym(x2.Aux) != s { break } + _ = x2.Args[2] + p1 := x2.Args[0] x2_1 := x2.Args[1] if x2_1.Op != OpAMD64SHRQconst || auxIntToInt8(x2_1.AuxInt) != 8 || w != x2_1.Args[0] { break } x3 := x2.Args[2] - if x3.Op != OpAMD64MOVBstore || auxIntToInt32(x3.AuxInt) != 0 || auxToSym(x3.Aux) != s { + if x3.Op != OpAMD64MOVBstore { + break + } + c0 := auxIntToInt32(x3.AuxInt) + if auxToSym(x3.Aux) != s { break } mem := x3.Args[2] - if p1 != x3.Args[0] || w != x3.Args[1] || !(x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && clobber(x1, x2, x3)) { + p0 := x3.Args[0] + if w != x3.Args[1] || !(x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && sequentialAddresses(p0, p1, int64(1+c0-c1)) && sequentialAddresses(p0, p2, int64(5+c0-c2)) && sequentialAddresses(p0, p3, int64(7+c0-c3)) && clobber(x1, x2, x3)) { break } v.reset(OpAMD64MOVQstore) + v.AuxInt = int32ToAuxInt(c0) v.Aux = symToAux(s) - v.AddArg3(p1, w, mem) + v.AddArg3(p0, w, mem) return true } // match: (MOVBstore [i] {s} p x1:(MOVBload [j] {s2} p2 mem) mem2:(MOVBstore [i-1] {s} p x2:(MOVBload [j-1] {s2} p2 mem) mem)) @@ -10878,51 +11222,6 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value) bool { v.AddArg3(p, v0, mem) return true } - // match: (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (MOVBstore [off1] {sym} (ADDLconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVBstore [off1+off2] {sym} ptr val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(ptr, val, mem) - return true - } return false } func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool { @@ -10971,13 +11270,13 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool { v.AddArg2(ptr, mem) return true } - // match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 1 == c.Off() && clobber(x) - // result: (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p mem) + // match: (MOVBstoreconst [c] {s} p1 x:(MOVBstoreconst [a] {s} p0 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) && clobber(x) + // result: (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p0 mem) for { c := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p1 := v_0 x := v_1 if x.Op != OpAMD64MOVBstoreconst { break @@ -10987,22 +11286,23 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+1 == c.Off() && clobber(x)) { + p0 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVWstoreconst) v.AuxInt = valAndOffToAuxInt(makeValAndOff(a.Val()&0xff|c.Val()<<8, a.Off())) v.Aux = symToAux(s) - v.AddArg2(p, mem) + v.AddArg2(p0, mem) return true } - // match: (MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 1 == c.Off() && clobber(x) - // result: (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p mem) + // match: (MOVBstoreconst [a] {s} p0 x:(MOVBstoreconst [c] {s} p1 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) && clobber(x) + // result: (MOVWstoreconst [makeValAndOff(a.Val()&0xff | c.Val()<<8, a.Off())] {s} p0 mem) for { a := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p0 := v_0 x := v_1 if x.Op != OpAMD64MOVBstoreconst { break @@ -11012,56 +11312,14 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+1 == c.Off() && clobber(x)) { + p1 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+1-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVWstoreconst) v.AuxInt = valAndOffToAuxInt(makeValAndOff(a.Val()&0xff|c.Val()<<8, a.Off())) v.Aux = symToAux(s) - v.AddArg2(p, mem) - return true - } - // match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) - // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off) - // result: (MOVBstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVBstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) - return true - } - // match: (MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem) - // cond: sc.canAdd32(off) - // result: (MOVBstoreconst [sc.addOffset32(off)] {s} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - s := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVBstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(s) - v.AddArg2(ptr, mem) + v.AddArg2(p0, mem) return true } return false @@ -11492,49 +11750,6 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value) bool { v.AddArg2(base, mem) return true } - // match: (MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVLload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(base, mem) - return true - } - // match: (MOVLload [off1] {sym} (ADDLconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVLload [off1+off2] {sym} ptr mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVLload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) - return true - } // match: (MOVLload [off] {sym} ptr (MOVSSstore [off] {sym} ptr val _)) // result: (MOVLf2i val) for { @@ -11836,51 +12051,6 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value) bool { v.AddArg3(p, v0, mem) return true } - // match: (MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVLstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (MOVLstore [off1] {sym} (ADDLconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVLstore [off1+off2] {sym} ptr val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVLstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(ptr, val, mem) - return true - } // match: (MOVLstore {sym} [off] ptr y:(ADDLload x [off] {sym} ptr mem) mem) // cond: y.Uses==1 && clobber(y) // result: (ADDLmodify [off] {sym} ptr x mem) @@ -12260,6 +12430,28 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value) bool { v.AddArg3(ptr, val, mem) return true } + // match: (MOVLstore [i] {s} p x:(BSWAPL w) mem) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBELstore [i] {s} p w mem) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64BSWAPL { + break + } + w := x.Args[0] + mem := v_2 + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64MOVBELstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { @@ -12310,13 +12502,13 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { v.AddArg2(ptr, mem) return true } - // match: (MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 4 == c.Off() && clobber(x) - // result: (MOVQstore [a.Off()] {s} p (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) + // match: (MOVLstoreconst [c] {s} p1 x:(MOVLstoreconst [a] {s} p0 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x) + // result: (MOVQstore [a.Off()] {s} p0 (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) for { c := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p1 := v_0 x := v_1 if x.Op != OpAMD64MOVLstoreconst { break @@ -12326,7 +12518,8 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+4 == c.Off() && clobber(x)) { + p0 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVQstore) @@ -12334,16 +12527,16 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { v.Aux = symToAux(s) v0 := b.NewValue0(x.Pos, OpAMD64MOVQconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(a.Val64()&0xffffffff | c.Val64()<<32) - v.AddArg3(p, v0, mem) + v.AddArg3(p0, v0, mem) return true } - // match: (MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 4 == c.Off() && clobber(x) - // result: (MOVQstore [a.Off()] {s} p (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) + // match: (MOVLstoreconst [a] {s} p0 x:(MOVLstoreconst [c] {s} p1 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x) + // result: (MOVQstore [a.Off()] {s} p0 (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem) for { a := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p0 := v_0 x := v_1 if x.Op != OpAMD64MOVLstoreconst { break @@ -12353,7 +12546,8 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+4 == c.Off() && clobber(x)) { + p1 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+4-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVQstore) @@ -12361,50 +12555,7 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool { v.Aux = symToAux(s) v0 := b.NewValue0(x.Pos, OpAMD64MOVQconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(a.Val64()&0xffffffff | c.Val64()<<32) - v.AddArg3(p, v0, mem) - return true - } - // match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) - // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off) - // result: (MOVLstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVLstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) - return true - } - // match: (MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem) - // cond: sc.canAdd32(off) - // result: (MOVLstoreconst [sc.addOffset32(off)] {s} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - s := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVLstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(s) - v.AddArg2(ptr, mem) + v.AddArg3(p0, v0, mem) return true } return false @@ -12545,6 +12696,54 @@ func rewriteValueAMD64_OpAMD64MOVOstore(v *Value) bool { } return false } +func rewriteValueAMD64_OpAMD64MOVOstoreconst(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVOstoreconst [sc] {s} (ADDQconst [off] ptr) mem) + // cond: ValAndOff(sc).canAdd32(off) + // result: (MOVOstoreconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem) + for { + sc := auxIntToValAndOff(v.AuxInt) + s := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { + break + } + off := auxIntToInt32(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(ValAndOff(sc).canAdd32(off)) { + break + } + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(ValAndOff(sc).addOffset32(off)) + v.Aux = symToAux(s) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVOstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem) + // cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off) + // result: (MOVOstoreconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) + for { + sc := auxIntToValAndOff(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { + break + } + off := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off)) { + break + } + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(ValAndOff(sc).addOffset32(off)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} func rewriteValueAMD64_OpAMD64MOVQatomicload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -12713,49 +12912,6 @@ func rewriteValueAMD64_OpAMD64MOVQload(v *Value) bool { v.AddArg2(base, mem) return true } - // match: (MOVQload [off1] {sym1} (LEAL [off2] {sym2} base) mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVQload [off1+off2] {mergeSym(sym1,sym2)} base mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVQload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(base, mem) - return true - } - // match: (MOVQload [off1] {sym} (ADDLconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVQload [off1+off2] {sym} ptr mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVQload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) - return true - } // match: (MOVQload [off] {sym} ptr (MOVSDstore [off] {sym} ptr val _)) // result: (MOVQf2i val) for { @@ -12858,51 +13014,6 @@ func rewriteValueAMD64_OpAMD64MOVQstore(v *Value) bool { v.AddArg3(base, val, mem) return true } - // match: (MOVQstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVQstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (MOVQstore [off1] {sym} (ADDLconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVQstore [off1+off2] {sym} ptr val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVQstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(ptr, val, mem) - return true - } // match: (MOVQstore {sym} [off] ptr y:(ADDQload x [off] {sym} ptr mem) mem) // cond: y.Uses==1 && clobber(y) // result: (ADDQmodify [off] {sym} ptr x mem) @@ -13282,6 +13393,28 @@ func rewriteValueAMD64_OpAMD64MOVQstore(v *Value) bool { v.AddArg3(ptr, val, mem) return true } + // match: (MOVQstore [i] {s} p x:(BSWAPQ w) mem) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBEQstore [i] {s} p w mem) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64BSWAPQ { + break + } + w := x.Args[0] + mem := v_2 + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64MOVBEQstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool { @@ -13332,72 +13465,56 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool { v.AddArg2(ptr, mem) return true } - // match: (MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem)) - // cond: config.useSSE && x.Uses == 1 && c2.Off() + 8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x) - // result: (MOVOstorezero [c2.Off()] {s} p mem) + // match: (MOVQstoreconst [c] {s} p1 x:(MOVQstoreconst [a] {s} p0 mem)) + // cond: config.useSSE && x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) && a.Val() == 0 && c.Val() == 0 && clobber(x) + // result: (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p0 mem) for { c := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p1 := v_0 x := v_1 if x.Op != OpAMD64MOVQstoreconst { break } - c2 := auxIntToValAndOff(x.AuxInt) + a := auxIntToValAndOff(x.AuxInt) if auxToSym(x.Aux) != s { break } mem := x.Args[1] - if p != x.Args[0] || !(config.useSSE && x.Uses == 1 && c2.Off()+8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x)) { + p0 := x.Args[0] + if !(config.useSSE && x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) && a.Val() == 0 && c.Val() == 0 && clobber(x)) { break } - v.reset(OpAMD64MOVOstorezero) - v.AuxInt = int32ToAuxInt(c2.Off()) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, a.Off())) v.Aux = symToAux(s) - v.AddArg2(p, mem) + v.AddArg2(p0, mem) return true } - // match: (MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) - // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off) - // result: (MOVQstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) + // match: (MOVQstoreconst [a] {s} p0 x:(MOVQstoreconst [c] {s} p1 mem)) + // cond: config.useSSE && x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) && a.Val() == 0 && c.Val() == 0 && clobber(x) + // result: (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p0 mem) for { - sc := auxIntToValAndOff(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) { + a := auxIntToValAndOff(v.AuxInt) + s := auxToSym(v.Aux) + p0 := v_0 + x := v_1 + if x.Op != OpAMD64MOVQstoreconst { break } - v.reset(OpAMD64MOVQstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) - return true - } - // match: (MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem) - // cond: sc.canAdd32(off) - // result: (MOVQstoreconst [sc.addOffset32(off)] {s} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - s := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { + c := auxIntToValAndOff(x.AuxInt) + if auxToSym(x.Aux) != s { break } - off := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(sc.canAdd32(off)) { + mem := x.Args[1] + p1 := x.Args[0] + if !(config.useSSE && x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off())) && a.Val() == 0 && c.Val() == 0 && clobber(x)) { break } - v.reset(OpAMD64MOVQstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, a.Off())) v.Aux = symToAux(s) - v.AddArg2(ptr, mem) + v.AddArg2(p0, mem) return true } return false @@ -14018,49 +14135,6 @@ func rewriteValueAMD64_OpAMD64MOVWload(v *Value) bool { v.AddArg2(base, mem) return true } - // match: (MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVWload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(base, mem) - return true - } - // match: (MOVWload [off1] {sym} (ADDLconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVWload [off1+off2] {sym} ptr mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64MOVWload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) - return true - } // match: (MOVWload [off] {sym} (SB) _) // cond: symIsRO(sym) // result: (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) @@ -14454,49 +14528,26 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value) bool { v.AddArg3(p, v0, mem) return true } - // match: (MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) - // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { - break - } - v.reset(OpAMD64MOVWstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (MOVWstore [off1] {sym} (ADDLconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (MOVWstore [off1+off2] {sym} ptr val mem) + // match: (MOVWstore [i] {s} p x:(ROLWconst [8] w) mem) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBEWstore [i] {s} p w mem) for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64ROLWconst || auxIntToInt8(x.AuxInt) != 8 { break } - off2 := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - val := v_1 + w := x.Args[0] mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { break } - v.reset(OpAMD64MOVWstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(ptr, val, mem) + v.reset(OpAMD64MOVBEWstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) return true } return false @@ -14547,13 +14598,13 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool { v.AddArg2(ptr, mem) return true } - // match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 2 == c.Off() && clobber(x) - // result: (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p mem) + // match: (MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x) + // result: (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p0 mem) for { c := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p1 := v_0 x := v_1 if x.Op != OpAMD64MOVWstoreconst { break @@ -14563,22 +14614,23 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+2 == c.Off() && clobber(x)) { + p0 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVLstoreconst) v.AuxInt = valAndOffToAuxInt(makeValAndOff(a.Val()&0xffff|c.Val()<<16, a.Off())) v.Aux = symToAux(s) - v.AddArg2(p, mem) + v.AddArg2(p0, mem) return true } - // match: (MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem)) - // cond: x.Uses == 1 && a.Off() + 2 == c.Off() && clobber(x) - // result: (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p mem) + // match: (MOVWstoreconst [a] {s} p0 x:(MOVWstoreconst [c] {s} p1 mem)) + // cond: x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x) + // result: (MOVLstoreconst [makeValAndOff(a.Val()&0xffff | c.Val()<<16, a.Off())] {s} p0 mem) for { a := auxIntToValAndOff(v.AuxInt) s := auxToSym(v.Aux) - p := v_0 + p0 := v_0 x := v_1 if x.Op != OpAMD64MOVWstoreconst { break @@ -14588,56 +14640,14 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool { break } mem := x.Args[1] - if p != x.Args[0] || !(x.Uses == 1 && a.Off()+2 == c.Off() && clobber(x)) { + p1 := x.Args[0] + if !(x.Uses == 1 && sequentialAddresses(p0, p1, int64(a.Off()+2-c.Off())) && clobber(x)) { break } v.reset(OpAMD64MOVLstoreconst) v.AuxInt = valAndOffToAuxInt(makeValAndOff(a.Val()&0xffff|c.Val()<<16, a.Off())) v.Aux = symToAux(s) - v.AddArg2(p, mem) - return true - } - // match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) - // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off) - // result: (MOVWstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAL { - break - } - off := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] - mem := v_1 - if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVWstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) - return true - } - // match: (MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem) - // cond: sc.canAdd32(off) - // result: (MOVWstoreconst [sc.addOffset32(off)] {s} ptr mem) - for { - sc := auxIntToValAndOff(v.AuxInt) - s := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDLconst { - break - } - off := auxIntToInt32(v_0.AuxInt) - ptr := v_0.Args[0] - mem := v_1 - if !(sc.canAdd32(off)) { - break - } - v.reset(OpAMD64MOVWstoreconst) - v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off)) - v.Aux = symToAux(s) - v.AddArg2(ptr, mem) + v.AddArg2(p0, mem) return true } return false @@ -15910,6 +15920,25 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool { } break } + // match: (ORL (SHLXL (MOVLconst [1]) y) x) + // result: (BTSL x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64SHLXL { + continue + } + y := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0.AuxInt) != 1 { + continue + } + x := v_1 + v.reset(OpAMD64BTSL) + v.AddArg2(x, y) + return true + } + break + } // match: (ORL (MOVLconst [c]) x) // cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128 // result: (BTSLconst [int8(log32(c))] x) @@ -15946,1214 +15975,1379 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool { } break } - // match: (ORL (SHLLconst x [c]) (SHRLconst x [d])) - // cond: d==32-c - // result: (ROLLconst x [c]) + // match: (ORL x x) + // result: x + for { + x := v_0 + if x != v_1 { + break + } + v.copyOf(x) + return true + } + // match: (ORL x0:(MOVBload [i0] {s} p mem) sh:(SHLLconst [8] x1:(MOVBload [i1] {s} p mem))) + // cond: i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { + x0 := v_0 + if x0.Op != OpAMD64MOVBload { continue } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRLconst { + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i0) + v0.Aux = symToAux(s) + v0.AddArg2(p, mem) return true } break } - // match: (ORL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: d==16-c && c < 16 && t.Size() == 2 - // result: (ROLWconst x [c]) + // match: (ORL x0:(MOVBload [i] {s} p0 mem) sh:(SHLLconst [8] x1:(MOVBload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p0 mem) for { - t := v.Type for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { + x0 := v_0 + if x0.Op != OpAMD64MOVBload { continue } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRWconst { + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 16-c && c < 16 && t.Size() == 2) { + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { continue } - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i) + v0.Aux = symToAux(s) + v0.AddArg2(p0, mem) return true } break } - // match: (ORL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: d==8-c && c < 8 && t.Size() == 1 - // result: (ROLBconst x [c]) + // match: (ORL x0:(MOVWload [i0] {s} p mem) sh:(SHLLconst [16] x1:(MOVWload [i1] {s} p mem))) + // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVLload [i0] {s} p mem) for { - t := v.Type for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { + x0 := v_0 + if x0.Op != OpAMD64MOVWload { continue } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRBconst { + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { continue } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 8-c && c < 8 && t.Size() == 1) { + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVWload { continue } - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i0) + v0.Aux = symToAux(s) + v0.AddArg2(p, mem) return true } break } - // match: (ORL (SHLL x y) (ANDL (SHRL x (NEGQ y)) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [31]) [-32])) [32])))) - // result: (ROLL x y) + // match: (ORL x0:(MOVWload [i] {s} p0 mem) sh:(SHLLconst [16] x1:(MOVWload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVLload [i] {s} p0 mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { + x0 := v_0 + if x0.Op != OpAMD64MOVWload { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDL { + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] { - continue - } - v.reset(OpAMD64ROLL) - v.AddArg2(x, y) - return true + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVWload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i) + v0.Aux = symToAux(s) + v0.AddArg2(p0, mem) + return true } break } - // match: (ORL (SHLL x y) (ANDL (SHRL x (NEGL y)) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [31]) [-32])) [32])))) - // result: (ROLL x y) + // match: (ORL s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem)) or:(ORL s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem)) y)) + // cond: i1 == i0+1 && j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j0] (MOVWload [i0] {s} p mem)) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { + s1 := v_0 + if s1.Op != OpAMD64SHLLconst { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDL { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + or := v_1 + if or.Op != OpAMD64ORL { + continue + } + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s0 := or_0 + if s0.Op != OpAMD64SHLLconst { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { continue } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 { + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] { continue } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] { + y := or_1 + if !(i1 == i0+1 && j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } - v.reset(OpAMD64ROLL) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x0.Pos, OpAMD64ORL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLLconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v2.AuxInt = int32ToAuxInt(i0) + v2.Aux = symToAux(s) + v2.AddArg2(p, mem) + v1.AddArg(v2) + v0.AddArg2(v1, y) return true } } break } - // match: (ORL (SHRL x y) (ANDL (SHLL x (NEGQ y)) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [31]) [-32])) [32])))) - // result: (RORL x y) + // match: (ORL s1:(SHLLconst [j1] x1:(MOVBload [i] {s} p1 mem)) or:(ORL s0:(SHLLconst [j0] x0:(MOVBload [i] {s} p0 mem)) y)) + // cond: j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j0] (MOVWload [i] {s} p0 mem)) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRL { + s1 := v_0 + if s1.Op != OpAMD64SHLLconst { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDL { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 { + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + or := v_1 + if or.Op != OpAMD64ORL { + continue + } + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s0 := or_0 + if s0.Op != OpAMD64SHLLconst { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { continue } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 { + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] { continue } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] { + y := or_1 + if !(j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } - v.reset(OpAMD64RORL) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x0.Pos, OpAMD64ORL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLLconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v2.AuxInt = int32ToAuxInt(i) + v2.Aux = symToAux(s) + v2.AddArg2(p0, mem) + v1.AddArg(v2) + v0.AddArg2(v1, y) return true } } break } - // match: (ORL (SHRL x y) (ANDL (SHLL x (NEGL y)) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [31]) [-32])) [32])))) - // result: (RORL x y) + // match: (ORL x1:(MOVBload [i1] {s} p mem) sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p mem))) + // cond: i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i0] {s} p mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRL { + x1 := v_0 + if x1.Op != OpAMD64MOVBload { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDL { + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] { - continue - } - v.reset(OpAMD64RORL) - v.AddArg2(x, y) - return true + x0 := sh.Args[0] + if x0.Op != OpAMD64MOVBload { + continue + } + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) + v.copyOf(v0) + v0.AuxInt = int8ToAuxInt(8) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v1.AuxInt = int32ToAuxInt(i0) + v1.Aux = symToAux(s) + v1.AddArg2(p, mem) + v0.AddArg(v1) + return true } break } - // match: (ORL (SHLL x (ANDQconst y [15])) (ANDL (SHRW x (NEGQ (ADDQconst (ANDQconst y [15]) [-16]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [15]) [-16])) [16])))) - // cond: v.Type.Size() == 2 - // result: (ROLW x y) + // match: (ORL x1:(MOVBload [i] {s} p1 mem) sh:(SHLLconst [8] x0:(MOVBload [i] {s} p0 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i] {s} p0 mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { + x1 := v_0 + if x1.Op != OpAMD64MOVBload { continue } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 15 { + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64ANDL { - continue - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRW { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ { - continue - } - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -16 { - continue - } - v_1_0_1_0_0 := v_1_0_1_0.Args[0] - if v_1_0_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 15 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 16 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -16 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 15 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 2) { - continue - } - v.reset(OpAMD64ROLW) - v.AddArg2(x, y) - return true - } - } - break - } - // match: (ORL (SHLL x (ANDLconst y [15])) (ANDL (SHRW x (NEGL (ADDLconst (ANDLconst y [15]) [-16]))) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [15]) [-16])) [16])))) - // cond: v.Type.Size() == 2 - // result: (ROLW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 15 { + x0 := sh.Args[0] + if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64ANDL { + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRW { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL { - continue - } - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -16 { - continue - } - v_1_0_1_0_0 := v_1_0_1_0.Args[0] - if v_1_0_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 15 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 16 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -16 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 15 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 2) { - continue - } - v.reset(OpAMD64ROLW) - v.AddArg2(x, y) - return true - } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) + v.copyOf(v0) + v0.AuxInt = int8ToAuxInt(8) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v1.AuxInt = int32ToAuxInt(i) + v1.Aux = symToAux(s) + v1.AddArg2(p0, mem) + v0.AddArg(v1) + return true } break } - // match: (ORL (SHRW x (ANDQconst y [15])) (SHLL x (NEGQ (ADDQconst (ANDQconst y [15]) [-16])))) - // cond: v.Type.Size() == 2 - // result: (RORW x y) + // match: (ORL r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) + // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i0] {s} p mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRW { + r1 := v_0 + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { continue } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 15 { + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64SHLL { + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { continue } - _ = v_1.Args[1] - if x != v_1.Args[0] { + r0 := sh.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGQ { + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVWload { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0.AuxInt) != -16 { + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 15 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 2) { + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { continue } - v.reset(OpAMD64RORW) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v1.AuxInt = int32ToAuxInt(i0) + v1.Aux = symToAux(s) + v1.AddArg2(p, mem) + v0.AddArg(v1) return true } break } - // match: (ORL (SHRW x (ANDLconst y [15])) (SHLL x (NEGL (ADDLconst (ANDLconst y [15]) [-16])))) - // cond: v.Type.Size() == 2 - // result: (RORW x y) + // match: (ORL r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem)) sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem)))) + // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i] {s} p0 mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 15 { + r1 := v_0 + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64SHLL { + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload { continue } - _ = v_1.Args[1] - if x != v_1.Args[0] { + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGL { + r0 := sh.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0.AuxInt) != -16 { + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 15 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 2) { + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { continue } - v.reset(OpAMD64RORW) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v1.AuxInt = int32ToAuxInt(i) + v1.Aux = symToAux(s) + v1.AddArg2(p0, mem) + v0.AddArg(v1) return true } break } - // match: (ORL (SHLL x (ANDQconst y [ 7])) (ANDL (SHRB x (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8])) [ 8])))) - // cond: v.Type.Size() == 1 - // result: (ROLB x y) + // match: (ORL s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem)) or:(ORL s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem)) y)) + // cond: i1 == i0+1 && j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j1] (ROLWconst [8] (MOVWload [i0] {s} p mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { + s0 := v_0 + if s0.Op != OpAMD64SHLLconst { continue } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 7 { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64ANDL { + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + or := v_1 + if or.Op != OpAMD64ORL { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRB { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ { - continue - } - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -8 { - continue - } - v_1_0_1_0_0 := v_1_0_1_0.Args[0] - if v_1_0_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 7 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLLconst { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 8 { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { continue } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -8 { + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] { continue } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 7 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 1) { + y := or_1 + if !(i1 == i0+1 && j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } - v.reset(OpAMD64ROLB) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLLconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) + v2.AuxInt = int8ToAuxInt(8) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v3.AuxInt = int32ToAuxInt(i0) + v3.Aux = symToAux(s) + v3.AddArg2(p, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) return true } } break } - // match: (ORL (SHLL x (ANDLconst y [ 7])) (ANDL (SHRB x (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8]))) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8])) [ 8])))) - // cond: v.Type.Size() == 1 - // result: (ROLB x y) + // match: (ORL s0:(SHLLconst [j0] x0:(MOVBload [i] {s} p0 mem)) or:(ORL s1:(SHLLconst [j1] x1:(MOVBload [i] {s} p1 mem)) y)) + // cond: j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j1] (ROLWconst [8] (MOVWload [i] {s} p0 mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLL { + s0 := v_0 + if s0.Op != OpAMD64SHLLconst { continue } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 7 { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64ANDL { + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + or := v_1 + if or.Op != OpAMD64ORL { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRB { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL { - continue - } - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -8 { - continue - } - v_1_0_1_0_0 := v_1_0_1_0.Args[0] - if v_1_0_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 7 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 8 { + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLLconst { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { continue } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -8 { + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] { continue } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 7 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 1) { + y := or_1 + if !(j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } - v.reset(OpAMD64ROLB) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLLconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) + v2.AuxInt = int8ToAuxInt(8) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v3.AuxInt = int32ToAuxInt(i) + v3.Aux = symToAux(s) + v3.AddArg2(p0, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) return true } } break } - // match: (ORL (SHRB x (ANDQconst y [ 7])) (SHLL x (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8])))) - // cond: v.Type.Size() == 1 - // result: (RORB x y) + // match: (ORL x l:(MOVLload [off] {sym} ptr mem)) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) + // result: (ORLload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRB { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 7 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64SHLL { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGQ { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0.AuxInt) != -8 { + x := v_0 + l := v_1 + if l.Op != OpAMD64MOVLload { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 7 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 1) { + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } - v.reset(OpAMD64RORB) - v.AddArg2(x, y) + v.reset(OpAMD64ORLload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(x, ptr, mem) return true } break } - // match: (ORL (SHRB x (ANDLconst y [ 7])) (SHLL x (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8])))) - // cond: v.Type.Size() == 1 - // result: (RORB x y) + return false +} +func rewriteValueAMD64_OpAMD64ORLconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ORLconst [c] x) + // cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128 + // result: (BTSLconst [int8(log32(c))] x) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRB { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 7 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpAMD64SHLL { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGL { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0.AuxInt) != -8 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 7 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 1) { - continue - } - v.reset(OpAMD64RORB) - v.AddArg2(x, y) - return true + c := auxIntToInt32(v.AuxInt) + x := v_0 + if !(isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128) { + break } - break + v.reset(OpAMD64BTSLconst) + v.AuxInt = int8ToAuxInt(int8(log32(c))) + v.AddArg(x) + return true } - // match: (ORL x x) + // match: (ORLconst [c] (ORLconst [d] x)) + // result: (ORLconst [c | d] x) + for { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpAMD64ORLconst { + break + } + d := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] + v.reset(OpAMD64ORLconst) + v.AuxInt = int32ToAuxInt(c | d) + v.AddArg(x) + return true + } + // match: (ORLconst [c] (BTSLconst [d] x)) + // result: (ORLconst [c | 1< (SHLLconst [j0] (MOVWload [i0] {s} p mem)) y) + // match: (ORQ (SHLXQ (MOVQconst [1]) y) x) + // result: (BTSQ x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s1 := v_0 - if s1.Op != OpAMD64SHLLconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { + if v_0.Op != OpAMD64SHLXQ { continue } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - or := v_1 - if or.Op != OpAMD64ORL { + y := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0.AuxInt) != 1 { continue } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s0 := or_0 - if s0.Op != OpAMD64SHLLconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] { - continue - } - y := or_1 - if !(i1 == i0+1 && j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x0.Pos, OpAMD64ORL, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64SHLLconst, v.Type) - v1.AuxInt = int8ToAuxInt(j0) - v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v2.AuxInt = int32ToAuxInt(i0) - v2.Aux = symToAux(s) - v2.AddArg2(p, mem) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + x := v_1 + v.reset(OpAMD64BTSQ) + v.AddArg2(x, y) + return true } break } - // match: (ORL s1:(SHLLconst [j1] x1:(MOVBload [i] {s} p1 mem)) or:(ORL s0:(SHLLconst [j0] x0:(MOVBload [i] {s} p0 mem)) y)) - // cond: j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j0] (MOVWload [i] {s} p0 mem)) y) + // match: (ORQ (MOVQconst [c]) x) + // cond: isUint64PowerOfTwo(c) && uint64(c) >= 128 + // result: (BTSQconst [int8(log64(c))] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s1 := v_0 - if s1.Op != OpAMD64SHLLconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { + if v_0.Op != OpAMD64MOVQconst { continue } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - or := v_1 - if or.Op != OpAMD64ORL { + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + if !(isUint64PowerOfTwo(c) && uint64(c) >= 128) { continue } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s0 := or_0 - if s0.Op != OpAMD64SHLLconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] { - continue - } - y := or_1 - if !(j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x0.Pos, OpAMD64ORL, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64SHLLconst, v.Type) - v1.AuxInt = int8ToAuxInt(j0) - v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v2.AuxInt = int32ToAuxInt(i) - v2.Aux = symToAux(s) - v2.AddArg2(p0, mem) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + v.reset(OpAMD64BTSQconst) + v.AuxInt = int8ToAuxInt(int8(log64(c))) + v.AddArg(x) + return true } break } - // match: (ORL x1:(MOVBload [i1] {s} p mem) sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p mem))) - // cond: i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i0] {s} p mem)) + // match: (ORQ x (MOVQconst [c])) + // cond: is32Bit(c) + // result: (ORQconst [int32(c)] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x1 := v_0 - if x1.Op != OpAMD64MOVBload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { - continue - } - x0 := sh.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { continue } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { continue } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) - v.copyOf(v0) - v0.AuxInt = int8ToAuxInt(8) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v1.AuxInt = int32ToAuxInt(i0) - v1.Aux = symToAux(s) - v1.AddArg2(p, mem) - v0.AddArg(v1) + v.reset(OpAMD64ORQconst) + v.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg(x) return true } break } - // match: (ORL x1:(MOVBload [i] {s} p1 mem) sh:(SHLLconst [8] x0:(MOVBload [i] {s} p0 mem))) - // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i] {s} p0 mem)) + // match: (ORQ x (MOVLconst [c])) + // result: (ORQconst [c] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x1 := v_0 - if x1.Op != OpAMD64MOVBload { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { continue } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 8 { + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ORQconst) + v.AuxInt = int32ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + // match: (ORQ (SHRQ lo bits) (SHLQ hi (NEGQ bits))) + // result: (SHRDQ lo hi bits) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64SHRQ { continue } - x0 := sh.Args[0] - if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + bits := v_0.Args[1] + lo := v_0.Args[0] + if v_1.Op != OpAMD64SHLQ { continue } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + _ = v_1.Args[1] + hi := v_1.Args[0] + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { continue } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) - v.copyOf(v0) - v0.AuxInt = int8ToAuxInt(8) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v1.AuxInt = int32ToAuxInt(i) - v1.Aux = symToAux(s) - v1.AddArg2(p0, mem) - v0.AddArg(v1) + v.reset(OpAMD64SHRDQ) + v.AddArg3(lo, hi, bits) return true } break } - // match: (ORL r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) - // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i0] {s} p mem)) + // match: (ORQ (SHLQ lo bits) (SHRQ hi (NEGQ bits))) + // result: (SHLDQ lo hi bits) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload { + if v_0.Op != OpAMD64SHLQ { continue } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { + bits := v_0.Args[1] + lo := v_0.Args[0] + if v_1.Op != OpAMD64SHRQ { continue } - r0 := sh.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { + _ = v_1.Args[1] + hi := v_1.Args[0] + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { continue } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload { + v.reset(OpAMD64SHLDQ) + v.AddArg3(lo, hi, bits) + return true + } + break + } + // match: (ORQ (SHRXQ lo bits) (SHLXQ hi (NEGQ bits))) + // result: (SHRDQ lo hi bits) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64SHRXQ { continue } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { + bits := v_0.Args[1] + lo := v_0.Args[0] + if v_1.Op != OpAMD64SHLXQ { continue } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + _ = v_1.Args[1] + hi := v_1.Args[0] + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { continue } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) - v1.AuxInt = int32ToAuxInt(i0) - v1.Aux = symToAux(s) - v1.AddArg2(p, mem) - v0.AddArg(v1) + v.reset(OpAMD64SHRDQ) + v.AddArg3(lo, hi, bits) return true } break } - // match: (ORL r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem)) sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem)))) - // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i] {s} p0 mem)) + // match: (ORQ (SHLXQ lo bits) (SHRXQ hi (NEGQ bits))) + // result: (SHLDQ lo hi bits) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { + if v_0.Op != OpAMD64SHLXQ { continue } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload { + bits := v_0.Args[1] + lo := v_0.Args[0] + if v_1.Op != OpAMD64SHRXQ { continue } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLLconst || auxIntToInt8(sh.AuxInt) != 16 { + _ = v_1.Args[1] + hi := v_1.Args[0] + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { continue } - r0 := sh.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { + v.reset(OpAMD64SHLDQ) + v.AddArg3(lo, hi, bits) + return true + } + break + } + // match: (ORQ (MOVQconst [c]) (MOVQconst [d])) + // result: (MOVQconst [c|d]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64MOVQconst { continue } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpAMD64MOVQconst { continue } - _ = x0.Args[1] + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(c | d) + return true + } + break + } + // match: (ORQ x x) + // result: x + for { + x := v_0 + if x != v_1 { + break + } + v.copyOf(x) + return true + } + // match: (ORQ x0:(MOVBload [i0] {s} p mem) sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p mem))) + // cond: i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + if x0.Op != OpAMD64MOVBload { + continue + } + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVBload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i0) + v0.Aux = symToAux(s) + v0.AddArg2(p, mem) + return true + } + break + } + // match: (ORQ x0:(MOVBload [i] {s} p0 mem) sh:(SHLQconst [8] x1:(MOVBload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p0 mem) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + if x0.Op != OpAMD64MOVBload { + continue + } + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] p0 := x0.Args[0] - if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { continue } b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) - v1.AuxInt = int32ToAuxInt(i) - v1.Aux = symToAux(s) - v1.AddArg2(p0, mem) - v0.AddArg(v1) + v0.AuxInt = int32ToAuxInt(i) + v0.Aux = symToAux(s) + v0.AddArg2(p0, mem) return true } break } - // match: (ORL s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem)) or:(ORL s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem)) y)) - // cond: i1 == i0+1 && j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j1] (ROLWconst [8] (MOVWload [i0] {s} p mem))) y) + // match: (ORQ x0:(MOVWload [i0] {s} p mem) sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p mem))) + // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVLload [i0] {s} p mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLLconst { + x0 := v_0 + if x0.Op != OpAMD64MOVWload { continue } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVWload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i0) + v0.Aux = symToAux(s) + v0.AddArg2(p, mem) + return true + } + break + } + // match: (ORQ x0:(MOVWload [i] {s} p0 mem) sh:(SHLQconst [16] x1:(MOVWload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVLload [i] {s} p0 mem) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + if x0.Op != OpAMD64MOVWload { + continue + } + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVWload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i) + v0.Aux = symToAux(s) + v0.AddArg2(p0, mem) + return true + } + break + } + // match: (ORQ x0:(MOVLload [i0] {s} p mem) sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p mem))) + // cond: i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVQload [i0] {s} p mem) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + if x0.Op != OpAMD64MOVLload { continue } i0 := auxIntToInt32(x0.AuxInt) s := auxToSym(x0.Aux) mem := x0.Args[1] p := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVLload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVQload, typ.UInt64) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i0) + v0.Aux = symToAux(s) + v0.AddArg2(p, mem) + return true + } + break + } + // match: (ORQ x0:(MOVLload [i] {s} p0 mem) sh:(SHLQconst [32] x1:(MOVLload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVQload [i] {s} p0 mem) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + if x0.Op != OpAMD64MOVLload { + continue + } + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { + continue + } + x1 := sh.Args[0] + if x1.Op != OpAMD64MOVLload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVQload, typ.UInt64) + v.copyOf(v0) + v0.AuxInt = int32ToAuxInt(i) + v0.Aux = symToAux(s) + v0.AddArg2(p0, mem) + return true + } + break + } + // match: (ORQ s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) y)) + // cond: i1 == i0+1 && j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVWload [i0] {s} p mem)) y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + s1 := v_0 + if s1.Op != OpAMD64SHLQconst { + continue + } + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] or := v_1 - if or.Op != OpAMD64ORL { + if or.Op != OpAMD64ORQ { continue } _ = or.Args[1] or_0 := or.Args[0] or_1 := or.Args[1] for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLLconst { + s0 := or_0 + if s0.Op != OpAMD64SHLQconst { continue } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - i1 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { continue } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] { + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] { continue } y := or_1 - if !(i1 == i0+1 && j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { + if !(i1 == i0+1 && j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORL, v.Type) + v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLLconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) - v2.AuxInt = int8ToAuxInt(8) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v3.AuxInt = int32ToAuxInt(i0) - v3.Aux = symToAux(s) - v3.AddArg2(p, mem) - v2.AddArg(v3) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v2.AuxInt = int32ToAuxInt(i0) + v2.Aux = symToAux(s) + v2.AddArg2(p, mem) v1.AddArg(v2) v0.AddArg2(v1, y) return true @@ -17161,62 +17355,59 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool { } break } - // match: (ORL s0:(SHLLconst [j0] x0:(MOVBload [i] {s} p0 mem)) or:(ORL s1:(SHLLconst [j1] x1:(MOVBload [i] {s} p1 mem)) y)) - // cond: j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORL (SHLLconst [j1] (ROLWconst [8] (MOVWload [i] {s} p0 mem))) y) + // match: (ORQ s1:(SHLQconst [j1] x1:(MOVBload [i] {s} p1 mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVBload [i] {s} p0 mem)) y)) + // cond: j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVWload [i] {s} p0 mem)) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLLconst { + s1 := v_0 + if s1.Op != OpAMD64SHLQconst { continue } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - i := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p0 := x0.Args[0] + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] or := v_1 - if or.Op != OpAMD64ORL { + if or.Op != OpAMD64ORQ { continue } _ = or.Args[1] or_0 := or.Args[0] or_1 := or.Args[1] for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLLconst { + s0 := or_0 + if s0.Op != OpAMD64SHLQconst { continue } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { continue } - _ = x1.Args[1] - p1 := x1.Args[0] - if mem != x1.Args[1] { + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] { continue } y := or_1 - if !(j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { + if !(j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORL, v.Type) + v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLLconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) - v2.AuxInt = int8ToAuxInt(8) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v3.AuxInt = int32ToAuxInt(i) - v3.Aux = symToAux(s) - v3.AddArg2(p0, mem) - v2.AddArg(v3) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v2.AuxInt = int32ToAuxInt(i) + v2.Aux = symToAux(s) + v2.AddArg2(p0, mem) v1.AddArg(v2) v0.AddArg2(v1, y) return true @@ -17224,824 +17415,704 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool { } break } - // match: (ORL x l:(MOVLload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && clobber(l) - // result: (ORLload x [off] {sym} ptr mem) + // match: (ORQ s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p mem)) y)) + // cond: i1 == i0+2 && j1 == j0+16 && j0 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVLload [i0] {s} p mem)) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - l := v_1 - if l.Op != OpAMD64MOVLload { + s1 := v_0 + if s1.Op != OpAMD64SHLQconst { continue } - off := auxIntToInt32(l.AuxInt) - sym := auxToSym(l.Aux) - mem := l.Args[1] - ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && clobber(l)) { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVWload { continue } - v.reset(OpAMD64ORLload) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(x, ptr, mem) - return true + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + or := v_1 + if or.Op != OpAMD64ORQ { + continue + } + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s0 := or_0 + if s0.Op != OpAMD64SHLQconst { + continue + } + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVWload { + continue + } + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] { + continue + } + y := or_1 + if !(i1 == i0+2 && j1 == j0+16 && j0%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { + continue + } + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v2.AuxInt = int32ToAuxInt(i0) + v2.Aux = symToAux(s) + v2.AddArg2(p, mem) + v1.AddArg(v2) + v0.AddArg2(v1, y) + return true + } } break } - return false -} -func rewriteValueAMD64_OpAMD64ORLconst(v *Value) bool { - v_0 := v.Args[0] - // match: (ORLconst [c] x) - // cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128 - // result: (BTSLconst [int8(log32(c))] x) + // match: (ORQ s1:(SHLQconst [j1] x1:(MOVWload [i] {s} p1 mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVWload [i] {s} p0 mem)) y)) + // cond: j1 == j0+16 && j0 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVLload [i] {s} p0 mem)) y) for { - c := auxIntToInt32(v.AuxInt) - x := v_0 - if !(isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128) { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + s1 := v_0 + if s1.Op != OpAMD64SHLQconst { + continue + } + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVWload { + continue + } + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + or := v_1 + if or.Op != OpAMD64ORQ { + continue + } + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s0 := or_0 + if s0.Op != OpAMD64SHLQconst { + continue + } + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] { + continue + } + y := or_1 + if !(j1 == j0+16 && j0%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { + continue + } + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j0) + v2 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v2.AuxInt = int32ToAuxInt(i) + v2.Aux = symToAux(s) + v2.AddArg2(p0, mem) + v1.AddArg(v2) + v0.AddArg2(v1, y) + return true + } } - v.reset(OpAMD64BTSLconst) - v.AuxInt = int8ToAuxInt(int8(log32(c))) - v.AddArg(x) - return true + break } - // match: (ORLconst [c] (ORLconst [d] x)) - // result: (ORLconst [c | d] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpAMD64ORLconst { - break - } - d := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpAMD64ORLconst) - v.AuxInt = int32ToAuxInt(c | d) - v.AddArg(x) - return true - } - // match: (ORLconst [c] (BTSLconst [d] x)) - // result: (ORLconst [c | 1< [8] (MOVWload [i0] {s} p mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQ { + x1 := v_0 + if x1.Op != OpAMD64MOVBload { continue } - y := v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0.AuxInt) != 1 { + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - x := v_1 - v.reset(OpAMD64BTSQ) - v.AddArg2(x, y) - return true - } - break - } - // match: (ORQ (MOVQconst [c]) x) - // cond: isUint64PowerOfTwo(c) && uint64(c) >= 128 - // result: (BTSQconst [int8(log64(c))] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64MOVQconst { + x0 := sh.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - c := auxIntToInt64(v_0.AuxInt) - x := v_1 - if !(isUint64PowerOfTwo(c) && uint64(c) >= 128) { + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { continue } - v.reset(OpAMD64BTSQconst) - v.AuxInt = int8ToAuxInt(int8(log64(c))) - v.AddArg(x) + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) + v.copyOf(v0) + v0.AuxInt = int8ToAuxInt(8) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v1.AuxInt = int32ToAuxInt(i0) + v1.Aux = symToAux(s) + v1.AddArg2(p, mem) + v0.AddArg(v1) return true } break } - // match: (ORQ x (MOVQconst [c])) - // cond: is32Bit(c) - // result: (ORQconst [int32(c)] x) + // match: (ORQ x1:(MOVBload [i] {s} p1 mem) sh:(SHLQconst [8] x0:(MOVBload [i] {s} p0 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i] {s} p0 mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + x1 := v_0 + if x1.Op != OpAMD64MOVBload { continue } - c := auxIntToInt64(v_1.AuxInt) - if !(is32Bit(c)) { + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { continue } - v.reset(OpAMD64ORQconst) - v.AuxInt = int32ToAuxInt(int32(c)) - v.AddArg(x) + x0 := sh.Args[0] + if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) + v.copyOf(v0) + v0.AuxInt = int8ToAuxInt(8) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) + v1.AuxInt = int32ToAuxInt(i) + v1.Aux = symToAux(s) + v1.AddArg2(p0, mem) + v0.AddArg(v1) return true } break } - // match: (ORQ x (MOVLconst [c])) - // result: (ORQconst [c] x) + // match: (ORQ r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) + // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i0] {s} p mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + r1 := v_0 + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { continue } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ORQconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { + continue + } + r0 := sh.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { + continue + } + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVWload { + continue + } + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v1.AuxInt = int32ToAuxInt(i0) + v1.Aux = symToAux(s) + v1.AddArg2(p, mem) + v0.AddArg(v1) return true } break } - // match: (ORQ (SHLQconst x [c]) (SHRQconst x [d])) - // cond: d==64-c - // result: (ROLQconst x [c]) + // match: (ORQ r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem)) sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem)))) + // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i] {s} p0 mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQconst { + r1 := v_0 + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { continue } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRQconst { + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload { + continue + } + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { continue } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { + r0 := sh.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { continue } - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + continue + } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) + v1.AuxInt = int32ToAuxInt(i) + v1.Aux = symToAux(s) + v1.AddArg2(p0, mem) + v0.AddArg(v1) return true } break } - // match: (ORQ (SHLQ x y) (ANDQ (SHRQ x (NEGQ y)) (SBBQcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [63]) [-64])) [64])))) - // result: (ROLQ x y) + // match: (ORQ r1:(BSWAPL x1:(MOVLload [i1] {s} p mem)) sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p mem)))) + // cond: i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPQ (MOVQload [i0] {s} p mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQ { + r1 := v_0 + if r1.Op != OpAMD64BSWAPL { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDQ { + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVLload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRQ { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] { - continue - } - v.reset(OpAMD64ROLQ) - v.AddArg2(x, y) - return true + i1 := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { + continue } - } - break - } - // match: (ORQ (SHLQ x y) (ANDQ (SHRQ x (NEGL y)) (SBBQcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [63]) [-64])) [64])))) - // result: (ROLQ x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQ { + r0 := sh.Args[0] + if r0.Op != OpAMD64BSWAPL { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDQ { + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVLload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHRQ { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] { - continue - } - v.reset(OpAMD64ROLQ) - v.AddArg2(x, y) - return true + i0 := auxIntToInt32(x0.AuxInt) + if auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + continue } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVQload, typ.UInt64) + v1.AuxInt = int32ToAuxInt(i0) + v1.Aux = symToAux(s) + v1.AddArg2(p, mem) + v0.AddArg(v1) + return true } break } - // match: (ORQ (SHRQ x y) (ANDQ (SHLQ x (NEGQ y)) (SBBQcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [63]) [-64])) [64])))) - // result: (RORQ x y) + // match: (ORQ r1:(BSWAPL x1:(MOVLload [i] {s} p1 mem)) sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i] {s} p0 mem)))) + // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) + // result: @mergePoint(b,x0,x1) (BSWAPQ (MOVQload [i] {s} p0 mem)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRQ { + r1 := v_0 + if r1.Op != OpAMD64BSWAPL { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDQ { + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVLload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLQ { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGQ { - continue - } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 { - continue - } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] { - continue - } - v.reset(OpAMD64RORQ) - v.AddArg2(x, y) - return true + i := auxIntToInt32(x1.AuxInt) + s := auxToSym(x1.Aux) + mem := x1.Args[1] + p1 := x1.Args[0] + sh := v_1 + if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { + continue + } + r0 := sh.Args[0] + if r0.Op != OpAMD64BSWAPL { + continue + } + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVLload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { + continue + } + _ = x0.Args[1] + p0 := x0.Args[0] + if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { + continue } + b = mergePoint(b, x0, x1) + v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x0.Pos, OpAMD64MOVQload, typ.UInt64) + v1.AuxInt = int32ToAuxInt(i) + v1.Aux = symToAux(s) + v1.AddArg2(p0, mem) + v0.AddArg(v1) + return true } break } - // match: (ORQ (SHRQ x y) (ANDQ (SHLQ x (NEGL y)) (SBBQcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [63]) [-64])) [64])))) - // result: (RORQ x y) + // match: (ORQ s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) or:(ORQ s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) y)) + // cond: i1 == i0+1 && j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (ROLWconst [8] (MOVWload [i0] {s} p mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRQ { + s0 := v_0 + if s0.Op != OpAMD64SHLQconst { continue } - y := v_0.Args[1] - x := v_0.Args[0] - if v_1.Op != OpAMD64ANDQ { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLQ { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask { + i0 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + or := v_1 + if or.Op != OpAMD64ORQ { + continue + } + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLQconst { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 { + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload { continue } - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpAMD64NEGL { + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { continue } - v_1_1_0_0_0 := v_1_1_0_0.Args[0] - if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 { + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] { continue } - v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0] - if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] { + y := or_1 + if !(i1 == i0+1 && j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { continue } - v.reset(OpAMD64RORQ) - v.AddArg2(x, y) + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) + v2.AuxInt = int8ToAuxInt(8) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v3.AuxInt = int32ToAuxInt(i0) + v3.Aux = symToAux(s) + v3.AddArg2(p, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) return true } } break } - // match: (ORQ (SHRQ lo bits) (SHLQ hi (NEGQ bits))) - // result: (SHRDQ lo hi bits) + // match: (ORQ s0:(SHLQconst [j0] x0:(MOVBload [i] {s} p0 mem)) or:(ORQ s1:(SHLQconst [j1] x1:(MOVBload [i] {s} p1 mem)) y)) + // cond: j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (ROLWconst [8] (MOVWload [i] {s} p0 mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHRQ { - continue - } - bits := v_0.Args[1] - lo := v_0.Args[0] - if v_1.Op != OpAMD64SHLQ { - continue - } - _ = v_1.Args[1] - hi := v_1.Args[0] - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { + s0 := v_0 + if s0.Op != OpAMD64SHLQconst { continue } - v.reset(OpAMD64SHRDQ) - v.AddArg3(lo, hi, bits) - return true - } - break - } - // match: (ORQ (SHLQ lo bits) (SHRQ hi (NEGQ bits))) - // result: (SHLDQ lo hi bits) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQ { + j0 := auxIntToInt8(s0.AuxInt) + x0 := s0.Args[0] + if x0.Op != OpAMD64MOVBload { continue } - bits := v_0.Args[1] - lo := v_0.Args[0] - if v_1.Op != OpAMD64SHRQ { + i := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p0 := x0.Args[0] + or := v_1 + if or.Op != OpAMD64ORQ { continue } - _ = v_1.Args[1] - hi := v_1.Args[0] - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] { - continue + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLQconst { + continue + } + j1 := auxIntToInt8(s1.AuxInt) + x1 := s1.Args[0] + if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] { + continue + } + y := or_1 + if !(j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { + continue + } + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) + v2.AuxInt = int8ToAuxInt(8) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) + v3.AuxInt = int32ToAuxInt(i) + v3.Aux = symToAux(s) + v3.AddArg2(p0, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) + return true } - v.reset(OpAMD64SHLDQ) - v.AddArg3(lo, hi, bits) - return true } break } - // match: (ORQ (MOVQconst [c]) (MOVQconst [d])) - // result: (MOVQconst [c|d]) + // match: (ORQ s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem))) or:(ORQ s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem))) y)) + // cond: i1 == i0+2 && j1 == j0-16 && j1 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, r0, r1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (BSWAPL (MOVLload [i0] {s} p mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64MOVQconst { + s0 := v_0 + if s0.Op != OpAMD64SHLQconst { continue } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpAMD64MOVQconst { + j0 := auxIntToInt8(s0.AuxInt) + r0 := s0.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { continue } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(c | d) - return true - } - break - } - // match: (ORQ x x) - // result: x - for { - x := v_0 - if x != v_1 { - break - } - v.copyOf(x) - return true - } - // match: (ORQ x0:(MOVBload [i0] {s} p mem) sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p mem))) - // cond: i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x0 := v_0 - if x0.Op != OpAMD64MOVBload { + x0 := r0.Args[0] + if x0.Op != OpAMD64MOVWload { continue } i0 := auxIntToInt32(x0.AuxInt) s := auxToSym(x0.Aux) mem := x0.Args[1] p := x0.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { - continue - } - x1 := sh.Args[0] - if x1.Op != OpAMD64MOVBload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { + or := v_1 + if or.Op != OpAMD64ORQ { continue } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { - continue + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLQconst { + continue + } + j1 := auxIntToInt8(s1.AuxInt) + r1 := s1.Args[0] + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { + continue + } + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload { + continue + } + i1 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] { + continue + } + y := or_1 + if !(i1 == i0+2 && j1 == j0-16 && j1%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, r0, r1, s0, s1, or)) { + continue + } + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64BSWAPL, typ.UInt32) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v3.AuxInt = int32ToAuxInt(i0) + v3.Aux = symToAux(s) + v3.AddArg2(p, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) + return true } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v.copyOf(v0) - v0.AuxInt = int32ToAuxInt(i0) - v0.Aux = symToAux(s) - v0.AddArg2(p, mem) - return true } break } - // match: (ORQ x0:(MOVBload [i] {s} p0 mem) sh:(SHLQconst [8] x1:(MOVBload [i] {s} p1 mem))) - // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p0 mem) + // match: (ORQ s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem))) or:(ORQ s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem))) y)) + // cond: j1 == j0-16 && j1 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, r0, r1, s0, s1, or) + // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (BSWAPL (MOVLload [i] {s} p0 mem))) y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x0 := v_0 - if x0.Op != OpAMD64MOVBload { - continue - } - i := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p0 := x0.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { - continue - } - x1 := sh.Args[0] - if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + s0 := v_0 + if s0.Op != OpAMD64SHLQconst { continue } - _ = x1.Args[1] - p1 := x1.Args[0] - if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + j0 := auxIntToInt8(s0.AuxInt) + r0 := s0.Args[0] + if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { continue } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v.copyOf(v0) - v0.AuxInt = int32ToAuxInt(i) - v0.Aux = symToAux(s) - v0.AddArg2(p0, mem) - return true - } - break - } - // match: (ORQ x0:(MOVWload [i0] {s} p mem) sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p mem))) - // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVLload [i0] {s} p mem) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x0 := v_0 + x0 := r0.Args[0] if x0.Op != OpAMD64MOVWload { continue } - i0 := auxIntToInt32(x0.AuxInt) + i := auxIntToInt32(x0.AuxInt) s := auxToSym(x0.Aux) mem := x0.Args[1] - p := x0.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { - continue - } - x1 := sh.Args[0] - if x1.Op != OpAMD64MOVWload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { + p0 := x0.Args[0] + or := v_1 + if or.Op != OpAMD64ORQ { continue } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { - continue + _ = or.Args[1] + or_0 := or.Args[0] + or_1 := or.Args[1] + for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { + s1 := or_0 + if s1.Op != OpAMD64SHLQconst { + continue + } + j1 := auxIntToInt8(s1.AuxInt) + r1 := s1.Args[0] + if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { + continue + } + x1 := r1.Args[0] + if x1.Op != OpAMD64MOVWload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + continue + } + _ = x1.Args[1] + p1 := x1.Args[0] + if mem != x1.Args[1] { + continue + } + y := or_1 + if !(j1 == j0-16 && j1%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, r0, r1, s0, s1, or)) { + continue + } + b = mergePoint(b, x0, x1, y) + v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) + v1.AuxInt = int8ToAuxInt(j1) + v2 := b.NewValue0(x1.Pos, OpAMD64BSWAPL, typ.UInt32) + v3 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) + v3.AuxInt = int32ToAuxInt(i) + v3.Aux = symToAux(s) + v3.AddArg2(p0, mem) + v2.AddArg(v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) + return true } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) - v.copyOf(v0) - v0.AuxInt = int32ToAuxInt(i0) - v0.Aux = symToAux(s) - v0.AddArg2(p, mem) - return true } break } - // match: (ORQ x0:(MOVWload [i] {s} p0 mem) sh:(SHLQconst [16] x1:(MOVWload [i] {s} p1 mem))) - // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVLload [i] {s} p0 mem) + // match: (ORQ x l:(MOVQload [off] {sym} ptr mem)) + // cond: canMergeLoadClobber(v, l, x) && clobber(l) + // result: (ORQload x [off] {sym} ptr mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x0 := v_0 - if x0.Op != OpAMD64MOVWload { - continue - } - i := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p0 := x0.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { - continue - } - x1 := sh.Args[0] - if x1.Op != OpAMD64MOVWload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + x := v_0 + l := v_1 + if l.Op != OpAMD64MOVQload { continue } - _ = x1.Args[1] - p1 := x1.Args[0] - if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + if !(canMergeLoadClobber(v, l, x) && clobber(l)) { continue } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) - v.copyOf(v0) - v0.AuxInt = int32ToAuxInt(i) - v0.Aux = symToAux(s) - v0.AddArg2(p0, mem) + v.reset(OpAMD64ORQload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(x, ptr, mem) return true } break } - // match: (ORQ x0:(MOVLload [i0] {s} p mem) sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p mem))) - // cond: i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVQload [i0] {s} p mem) + // match: (ORQ x0:(MOVBELload [i0] {s} p mem) sh:(SHLQconst [32] x1:(MOVBELload [i1] {s} p mem))) + // cond: i0 == i1+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVBEQload [i1] {s} p mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x0 := v_0 - if x0.Op != OpAMD64MOVLload { + if x0.Op != OpAMD64MOVBELload { continue } i0 := auxIntToInt32(x0.AuxInt) @@ -18053,7 +18124,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool { continue } x1 := sh.Args[0] - if x1.Op != OpAMD64MOVLload { + if x1.Op != OpAMD64MOVBELload { continue } i1 := auxIntToInt32(x1.AuxInt) @@ -18061,26 +18132,26 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool { continue } _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] || !(i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + if p != x1.Args[0] || mem != x1.Args[1] || !(i0 == i1+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { continue } b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVQload, typ.UInt64) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVBEQload, typ.UInt64) v.copyOf(v0) - v0.AuxInt = int32ToAuxInt(i0) + v0.AuxInt = int32ToAuxInt(i1) v0.Aux = symToAux(s) v0.AddArg2(p, mem) return true } break } - // match: (ORQ x0:(MOVLload [i] {s} p0 mem) sh:(SHLQconst [32] x1:(MOVLload [i] {s} p1 mem))) - // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (MOVQload [i] {s} p0 mem) + // match: (ORQ x0:(MOVBELload [i] {s} p0 mem) sh:(SHLQconst [32] x1:(MOVBELload [i] {s} p1 mem))) + // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p1, p0, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) + // result: @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x0 := v_0 - if x0.Op != OpAMD64MOVLload { + if x0.Op != OpAMD64MOVBELload { continue } i := auxIntToInt32(x0.AuxInt) @@ -18092,951 +18163,2270 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool { continue } x1 := sh.Args[0] - if x1.Op != OpAMD64MOVLload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { + if x1.Op != OpAMD64MOVBELload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { continue } _ = x1.Args[1] p1 := x1.Args[0] - if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { + if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p1, p0, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { continue } b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x1.Pos, OpAMD64MOVQload, typ.UInt64) + v0 := b.NewValue0(x1.Pos, OpAMD64MOVBEQload, typ.UInt64) v.copyOf(v0) v0.AuxInt = int32ToAuxInt(i) v0.Aux = symToAux(s) - v0.AddArg2(p0, mem) + v0.AddArg2(p1, mem) return true } break } - // match: (ORQ s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) y)) - // cond: i1 == i0+1 && j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVWload [i0] {s} p mem)) y) + return false +} +func rewriteValueAMD64_OpAMD64ORQconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ORQconst [c] x) + // cond: isUint64PowerOfTwo(int64(c)) && uint64(c) >= 128 + // result: (BTSQconst [int8(log32(c))] x) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s1 := v_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s0 := or_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] { - continue - } - y := or_1 - if !(i1 == i0+1 && j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j0) - v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v2.AuxInt = int32ToAuxInt(i0) - v2.Aux = symToAux(s) - v2.AddArg2(p, mem) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + c := auxIntToInt32(v.AuxInt) + x := v_0 + if !(isUint64PowerOfTwo(int64(c)) && uint64(c) >= 128) { + break } - break + v.reset(OpAMD64BTSQconst) + v.AuxInt = int8ToAuxInt(int8(log32(c))) + v.AddArg(x) + return true } - // match: (ORQ s1:(SHLQconst [j1] x1:(MOVBload [i] {s} p1 mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVBload [i] {s} p0 mem)) y)) - // cond: j1 == j0+8 && j0 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVWload [i] {s} p0 mem)) y) + // match: (ORQconst [c] (ORQconst [d] x)) + // result: (ORQconst [c | d] x) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s1 := v_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { - continue - } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s0 := or_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] { - continue - } - y := or_1 - if !(j1 == j0+8 && j0%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j0) - v2 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v2.AuxInt = int32ToAuxInt(i) - v2.Aux = symToAux(s) - v2.AddArg2(p0, mem) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpAMD64ORQconst { + break } - break + d := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] + v.reset(OpAMD64ORQconst) + v.AuxInt = int32ToAuxInt(c | d) + v.AddArg(x) + return true } - // match: (ORQ s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p mem)) or:(ORQ s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p mem)) y)) - // cond: i1 == i0+2 && j1 == j0+16 && j0 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j0] (MOVLload [i0] {s} p mem)) y) + // match: (ORQconst [c] (BTSQconst [d] x)) + // cond: is32Bit(int64(c) | 1< (SHLQconst [j0] (MOVLload [i] {s} p0 mem)) y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s1 := v_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVWload { - continue - } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s0 := or_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] { - continue - } - y := or_1 - if !(j1 == j0+16 && j0%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x0.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j0) - v2 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) - v2.AuxInt = int32ToAuxInt(i) - v2.Aux = symToAux(s) - v2.AddArg2(p0, mem) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + d := auxIntToInt8(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(int64(c) | 1< [8] (MOVWload [i0] {s} p mem)) + // match: (ORQconst [0] x) + // result: x for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x1 := v_0 - if x1.Op != OpAMD64MOVBload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { - continue - } - x0 := sh.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+1 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) - v.copyOf(v0) - v0.AuxInt = int8ToAuxInt(8) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v1.AuxInt = int32ToAuxInt(i0) - v1.Aux = symToAux(s) - v1.AddArg2(p, mem) - v0.AddArg(v1) - return true + if auxIntToInt32(v.AuxInt) != 0 { + break } - break + x := v_0 + v.copyOf(x) + return true } - // match: (ORQ x1:(MOVBload [i] {s} p1 mem) sh:(SHLQconst [8] x0:(MOVBload [i] {s} p0 mem))) - // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) - // result: @mergePoint(b,x0,x1) (ROLWconst [8] (MOVWload [i] {s} p0 mem)) + // match: (ORQconst [-1] _) + // result: (MOVQconst [-1]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x1 := v_0 - if x1.Op != OpAMD64MOVBload { - continue - } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 8 { - continue - } - x0 := sh.Args[0] - if x0.Op != OpAMD64MOVBload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64ROLWconst, v.Type) - v.copyOf(v0) - v0.AuxInt = int8ToAuxInt(8) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVWload, typ.UInt16) - v1.AuxInt = int32ToAuxInt(i) - v1.Aux = symToAux(s) - v1.AddArg2(p0, mem) - v0.AddArg(v1) - return true + if auxIntToInt32(v.AuxInt) != -1 { + break } - break + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(-1) + return true } - // match: (ORQ r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) - // cond: i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i0] {s} p mem)) + // match: (ORQconst [c] (MOVQconst [d])) + // result: (MOVQconst [int64(c)|d]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { - continue - } - r0 := sh.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+2 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) - v1.AuxInt = int32ToAuxInt(i0) - v1.Aux = symToAux(s) - v1.AddArg2(p, mem) - v0.AddArg(v1) - return true + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { + break } - break + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(c) | d) + return true } - // match: (ORQ r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem)) sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem)))) - // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPL (MOVLload [i] {s} p0 mem)) + return false +} +func rewriteValueAMD64_OpAMD64ORQconstmodify(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ORQconstmodify [valoff1] {sym} (ADDQconst [off2] base) mem) + // cond: ValAndOff(valoff1).canAdd32(off2) + // result: (ORQconstmodify [ValAndOff(valoff1).addOffset32(off2)] {sym} base mem) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload { - continue - } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 16 { - continue - } - r0 := sh.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPL, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVLload, typ.UInt32) - v1.AuxInt = int32ToAuxInt(i) - v1.Aux = symToAux(s) - v1.AddArg2(p0, mem) - v0.AddArg(v1) - return true + valoff1 := auxIntToValAndOff(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { + break } - break + off2 := auxIntToInt32(v_0.AuxInt) + base := v_0.Args[0] + mem := v_1 + if !(ValAndOff(valoff1).canAdd32(off2)) { + break + } + v.reset(OpAMD64ORQconstmodify) + v.AuxInt = valAndOffToAuxInt(ValAndOff(valoff1).addOffset32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(base, mem) + return true } - // match: (ORQ r1:(BSWAPL x1:(MOVLload [i1] {s} p mem)) sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p mem)))) - // cond: i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPQ (MOVQload [i0] {s} p mem)) + // match: (ORQconstmodify [valoff1] {sym1} (LEAQ [off2] {sym2} base) mem) + // cond: ValAndOff(valoff1).canAdd32(off2) && canMergeSym(sym1, sym2) + // result: (ORQconstmodify [ValAndOff(valoff1).addOffset32(off2)] {mergeSym(sym1,sym2)} base mem) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64BSWAPL { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVLload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { - continue - } - r0 := sh.Args[0] - if r0.Op != OpAMD64BSWAPL { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVLload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - if auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - if p != x0.Args[0] || mem != x0.Args[1] || !(i1 == i0+4 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVQload, typ.UInt64) - v1.AuxInt = int32ToAuxInt(i0) - v1.Aux = symToAux(s) - v1.AddArg2(p, mem) - v0.AddArg(v1) - return true + valoff1 := auxIntToValAndOff(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { + break } - break + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + mem := v_1 + if !(ValAndOff(valoff1).canAdd32(off2) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpAMD64ORQconstmodify) + v.AuxInt = valAndOffToAuxInt(ValAndOff(valoff1).addOffset32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(base, mem) + return true } - // match: (ORQ r1:(BSWAPL x1:(MOVLload [i] {s} p1 mem)) sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i] {s} p0 mem)))) - // cond: x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, r0, r1, sh) - // result: @mergePoint(b,x0,x1) (BSWAPQ (MOVQload [i] {s} p0 mem)) + return false +} +func rewriteValueAMD64_OpAMD64ORQload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (ORQload [off1] {sym} val (ADDQconst [off2] base) mem) + // cond: is32Bit(int64(off1)+int64(off2)) + // result: (ORQload [off1+off2] {sym} val base mem) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - r1 := v_0 - if r1.Op != OpAMD64BSWAPL { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVLload { - continue - } - i := auxIntToInt32(x1.AuxInt) - s := auxToSym(x1.Aux) - mem := x1.Args[1] - p1 := x1.Args[0] - sh := v_1 - if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 { - continue - } - r0 := sh.Args[0] - if r0.Op != OpAMD64BSWAPL { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVLload || auxIntToInt32(x0.AuxInt) != i || auxToSym(x0.Aux) != s { - continue - } - _ = x0.Args[1] - p0 := x0.Args[0] - if mem != x0.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p0, p1, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, r0, r1, sh)) { - continue - } - b = mergePoint(b, x0, x1) - v0 := b.NewValue0(x0.Pos, OpAMD64BSWAPQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x0.Pos, OpAMD64MOVQload, typ.UInt64) - v1.AuxInt = int32ToAuxInt(i) - v1.Aux = symToAux(s) - v1.AddArg2(p0, mem) - v0.AddArg(v1) - return true + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + val := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + off2 := auxIntToInt32(v_1.AuxInt) + base := v_1.Args[0] + mem := v_2 + if !(is32Bit(int64(off1) + int64(off2))) { + break + } + v.reset(OpAMD64ORQload) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(sym) + v.AddArg3(val, base, mem) + return true + } + // match: (ORQload [off1] {sym1} val (LEAQ [off2] {sym2} base) mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (ORQload [off1+off2] {mergeSym(sym1,sym2)} val base mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + val := v_0 + if v_1.Op != OpAMD64LEAQ { + break + } + off2 := auxIntToInt32(v_1.AuxInt) + sym2 := auxToSym(v_1.Aux) + base := v_1.Args[0] + mem := v_2 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpAMD64ORQload) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(val, base, mem) + return true + } + // match: ( ORQload x [off] {sym} ptr (MOVSDstore [off] {sym} ptr y _)) + // result: ( ORQ x (MOVQf2i y)) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + x := v_0 + ptr := v_1 + if v_2.Op != OpAMD64MOVSDstore || auxIntToInt32(v_2.AuxInt) != off || auxToSym(v_2.Aux) != sym { + break + } + y := v_2.Args[1] + if ptr != v_2.Args[0] { + break + } + v.reset(OpAMD64ORQ) + v0 := b.NewValue0(v_2.Pos, OpAMD64MOVQf2i, typ.UInt64) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ORQmodify(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ORQmodify [off1] {sym} (ADDQconst [off2] base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) + // result: (ORQmodify [off1+off2] {sym} base val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + int64(off2))) { + break + } + v.reset(OpAMD64ORQmodify) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(sym) + v.AddArg3(base, val, mem) + return true + } + // match: (ORQmodify [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (ORQmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpAMD64ORQmodify) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(base, val, mem) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROLB x (NEGQ y)) + // result: (RORB x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORB) + v.AddArg2(x, y) + return true + } + // match: (ROLB x (NEGL y)) + // result: (RORB x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORB) + v.AddArg2(x, y) + return true + } + // match: (ROLB x (MOVQconst [c])) + // result: (ROLBconst [int8(c&7) ] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLBconst) + v.AuxInt = int8ToAuxInt(int8(c & 7)) + v.AddArg(x) + return true + } + // match: (ROLB x (MOVLconst [c])) + // result: (ROLBconst [int8(c&7) ] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLBconst) + v.AuxInt = int8ToAuxInt(int8(c & 7)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLBconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ROLBconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROLL x (NEGQ y)) + // result: (RORL x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORL) + v.AddArg2(x, y) + return true + } + // match: (ROLL x (NEGL y)) + // result: (RORL x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORL) + v.AddArg2(x, y) + return true + } + // match: (ROLL x (MOVQconst [c])) + // result: (ROLLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (ROLL x (MOVLconst [c])) + // result: (ROLLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLLconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ROLLconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROLQ x (NEGQ y)) + // result: (RORQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORQ) + v.AddArg2(x, y) + return true + } + // match: (ROLQ x (NEGL y)) + // result: (RORQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORQ) + v.AddArg2(x, y) + return true + } + // match: (ROLQ x (MOVQconst [c])) + // result: (ROLQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (ROLQ x (MOVLconst [c])) + // result: (ROLQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLQconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ROLQconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROLW x (NEGQ y)) + // result: (RORW x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORW) + v.AddArg2(x, y) + return true + } + // match: (ROLW x (NEGL y)) + // result: (RORW x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64RORW) + v.AddArg2(x, y) + return true + } + // match: (ROLW x (MOVQconst [c])) + // result: (ROLWconst [int8(c&15)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLWconst) + v.AuxInt = int8ToAuxInt(int8(c & 15)) + v.AddArg(x) + return true + } + // match: (ROLW x (MOVLconst [c])) + // result: (ROLWconst [int8(c&15)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLWconst) + v.AuxInt = int8ToAuxInt(int8(c & 15)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64ROLWconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ROLWconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64RORB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORB x (NEGQ y)) + // result: (ROLB x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLB) + v.AddArg2(x, y) + return true + } + // match: (RORB x (NEGL y)) + // result: (ROLB x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLB) + v.AddArg2(x, y) + return true + } + // match: (RORB x (MOVQconst [c])) + // result: (ROLBconst [int8((-c)&7) ] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLBconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 7)) + v.AddArg(x) + return true + } + // match: (RORB x (MOVLconst [c])) + // result: (ROLBconst [int8((-c)&7) ] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLBconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 7)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64RORL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORL x (NEGQ y)) + // result: (ROLL x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLL) + v.AddArg2(x, y) + return true + } + // match: (RORL x (NEGL y)) + // result: (ROLL x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLL) + v.AddArg2(x, y) + return true + } + // match: (RORL x (MOVQconst [c])) + // result: (ROLLconst [int8((-c)&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLLconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 31)) + v.AddArg(x) + return true + } + // match: (RORL x (MOVLconst [c])) + // result: (ROLLconst [int8((-c)&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLLconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 31)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64RORQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORQ x (NEGQ y)) + // result: (ROLQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLQ) + v.AddArg2(x, y) + return true + } + // match: (RORQ x (NEGL y)) + // result: (ROLQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLQ) + v.AddArg2(x, y) + return true + } + // match: (RORQ x (MOVQconst [c])) + // result: (ROLQconst [int8((-c)&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLQconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 63)) + v.AddArg(x) + return true + } + // match: (RORQ x (MOVLconst [c])) + // result: (ROLQconst [int8((-c)&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLQconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 63)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64RORW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORW x (NEGQ y)) + // result: (ROLW x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLW) + v.AddArg2(x, y) + return true + } + // match: (RORW x (NEGL y)) + // result: (ROLW x y) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + y := v_1.Args[0] + v.reset(OpAMD64ROLW) + v.AddArg2(x, y) + return true + } + // match: (RORW x (MOVQconst [c])) + // result: (ROLWconst [int8((-c)&15)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64ROLWconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 15)) + v.AddArg(x) + return true + } + // match: (RORW x (MOVLconst [c])) + // result: (ROLWconst [int8((-c)&15)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64ROLWconst) + v.AuxInt = int8ToAuxInt(int8((-c) & 15)) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SARB x (MOVQconst [c])) + // result: (SARBconst [int8(min(int64(c)&31,7))] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARBconst) + v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 7))) + v.AddArg(x) + return true + } + // match: (SARB x (MOVLconst [c])) + // result: (SARBconst [int8(min(int64(c)&31,7))] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARBconst) + v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 7))) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARBconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SARBconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (SARBconst [c] (MOVQconst [d])) + // result: (MOVQconst [int64(int8(d))>>uint64(c)]) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int8(d)) >> uint64(c)) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SARL x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SARXL x y) + for { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64SARXL) + v.AddArg2(x, y) + return true + } + // match: (SARL x (MOVQconst [c])) + // result: (SARLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (SARL x (MOVLconst [c])) + // result: (SARLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (SARL x (ADDQconst [c] y)) + // cond: c & 31 == 0 + // result: (SARL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARL) + v.AddArg2(x, y) + return true + } + // match: (SARL x (NEGQ (ADDQconst [c] y))) + // cond: c & 31 == 0 + // result: (SARL x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARL x (ANDQconst [c] y)) + // cond: c & 31 == 31 + // result: (SARL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARL) + v.AddArg2(x, y) + return true + } + // match: (SARL x (NEGQ (ANDQconst [c] y))) + // cond: c & 31 == 31 + // result: (SARL x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARL x (ADDLconst [c] y)) + // cond: c & 31 == 0 + // result: (SARL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARL) + v.AddArg2(x, y) + return true + } + // match: (SARL x (NEGL (ADDLconst [c] y))) + // cond: c & 31 == 0 + // result: (SARL x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARL x (ANDLconst [c] y)) + // cond: c & 31 == 31 + // result: (SARL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARL) + v.AddArg2(x, y) + return true + } + // match: (SARL x (NEGL (ANDLconst [c] y))) + // cond: c & 31 == 31 + // result: (SARL x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARLconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SARLconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (SARLconst [c] (MOVQconst [d])) + // result: (MOVQconst [int64(int32(d))>>uint64(c)]) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int32(d)) >> uint64(c)) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SARQ x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SARXQ x y) + for { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64SARXQ) + v.AddArg2(x, y) + return true + } + // match: (SARQ x (MOVQconst [c])) + // result: (SARQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (SARQ x (MOVLconst [c])) + // result: (SARQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (SARQ x (ADDQconst [c] y)) + // cond: c & 63 == 0 + // result: (SARQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARQ) + v.AddArg2(x, y) + return true + } + // match: (SARQ x (NEGQ (ADDQconst [c] y))) + // cond: c & 63 == 0 + // result: (SARQ x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARQ x (ANDQconst [c] y)) + // cond: c & 63 == 63 + // result: (SARQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARQ) + v.AddArg2(x, y) + return true + } + // match: (SARQ x (NEGQ (ANDQconst [c] y))) + // cond: c & 63 == 63 + // result: (SARQ x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARQ x (ADDLconst [c] y)) + // cond: c & 63 == 0 + // result: (SARQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARQ) + v.AddArg2(x, y) + return true + } + // match: (SARQ x (NEGL (ADDLconst [c] y))) + // cond: c & 63 == 0 + // result: (SARQ x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARQ x (ANDLconst [c] y)) + // cond: c & 63 == 63 + // result: (SARQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARQ) + v.AddArg2(x, y) + return true + } + // match: (SARQ x (NEGL (ANDLconst [c] y))) + // cond: c & 63 == 63 + // result: (SARQ x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARQconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SARQconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (SARQconst [c] (MOVQconst [d])) + // result: (MOVQconst [d>>uint64(c)]) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(d >> uint64(c)) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SARW x (MOVQconst [c])) + // result: (SARWconst [int8(min(int64(c)&31,15))] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARWconst) + v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 15))) + v.AddArg(x) + return true + } + // match: (SARW x (MOVLconst [c])) + // result: (SARWconst [int8(min(int64(c)&31,15))] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARWconst) + v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 15))) + v.AddArg(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARWconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SARWconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (SARWconst [c] (MOVQconst [d])) + // result: (MOVQconst [int64(int16(d))>>uint64(c)]) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int16(d)) >> uint64(c)) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARXL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SARXL x (MOVQconst [c])) + // result: (SARLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (SARXL x (MOVLconst [c])) + // result: (SARLconst [int8(c&31)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (SARXL x (ADDQconst [c] y)) + // cond: c & 31 == 0 + // result: (SARXL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARXL) + v.AddArg2(x, y) + return true + } + // match: (SARXL x (NEGQ (ADDQconst [c] y))) + // cond: c & 31 == 0 + // result: (SARXL x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXL x (ANDQconst [c] y)) + // cond: c & 31 == 31 + // result: (SARXL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { + break } - break + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARXL) + v.AddArg2(x, y) + return true } - // match: (ORQ s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) or:(ORQ s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) y)) - // cond: i1 == i0+1 && j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (ROLWconst [8] (MOVWload [i0] {s} p mem))) y) + // match: (SARXL x (NEGQ (ANDQconst [c] y))) + // cond: c & 31 == 31 + // result: (SARXL x (NEGQ y)) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p := x0.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { - continue - } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] { - continue - } - y := or_1 - if !(i1 == i0+1 && j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) - v2.AuxInt = int8ToAuxInt(8) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v3.AuxInt = int32ToAuxInt(i0) - v3.Aux = symToAux(s) - v3.AddArg2(p, mem) - v2.AddArg(v3) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXL x (ADDLconst [c] y)) + // cond: c & 31 == 0 + // result: (SARXL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARXL) + v.AddArg2(x, y) + return true + } + // match: (SARXL x (NEGL (ADDLconst [c] y))) + // cond: c & 31 == 0 + // result: (SARXL x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SARXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXL x (ANDLconst [c] y)) + // cond: c & 31 == 31 + // result: (SARXL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARXL) + v.AddArg2(x, y) + return true + } + // match: (SARXL x (NEGL (ANDLconst [c] y))) + // cond: c & 31 == 31 + // result: (SARXL x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SARXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXL l:(MOVLload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SARXLload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVLload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SARXLload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARXLload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SARXLload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SARLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem)) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + mem := v_2 + v.reset(OpAMD64SARLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARXQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SARXQ x (MOVQconst [c])) + // result: (SARQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (SARXQ x (MOVLconst [c])) + // result: (SARQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (SARXQ x (ADDQconst [c] y)) + // cond: c & 63 == 0 + // result: (SARXQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARXQ) + v.AddArg2(x, y) + return true + } + // match: (SARXQ x (NEGQ (ADDQconst [c] y))) + // cond: c & 63 == 0 + // result: (SARXQ x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXQ x (ANDQconst [c] y)) + // cond: c & 63 == 63 + // result: (SARXQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARXQ) + v.AddArg2(x, y) + return true + } + // match: (SARXQ x (NEGQ (ANDQconst [c] y))) + // cond: c & 63 == 63 + // result: (SARXQ x (NEGQ y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXQ x (ADDLconst [c] y)) + // cond: c & 63 == 0 + // result: (SARXQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARXQ) + v.AddArg2(x, y) + return true + } + // match: (SARXQ x (NEGL (ADDLconst [c] y))) + // cond: c & 63 == 0 + // result: (SARXQ x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SARXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXQ x (ANDLconst [c] y)) + // cond: c & 63 == 63 + // result: (SARXQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARXQ) + v.AddArg2(x, y) + return true + } + // match: (SARXQ x (NEGL (ANDLconst [c] y))) + // cond: c & 63 == 63 + // result: (SARXQ x (NEGL y)) + for { + x := v_0 + if v_1.Op != OpAMD64NEGL { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SARXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SARXQ l:(MOVQload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SARXQload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVQload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SARXQload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SARXQload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SARXQload [off] {sym} ptr (MOVQconst [c]) mem) + // result: (SARQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + mem := v_2 + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) + return true + } + // match: (SARXQload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SARQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + mem := v_2 + v.reset(OpAMD64SARQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SBBLcarrymask(v *Value) bool { + v_0 := v.Args[0] + // match: (SBBLcarrymask (FlagEQ)) + // result: (MOVLconst [0]) + for { + if v_0.Op != OpAMD64FlagEQ { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (SBBLcarrymask (FlagLT_ULT)) + // result: (MOVLconst [-1]) + for { + if v_0.Op != OpAMD64FlagLT_ULT { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(-1) + return true + } + // match: (SBBLcarrymask (FlagLT_UGT)) + // result: (MOVLconst [0]) + for { + if v_0.Op != OpAMD64FlagLT_UGT { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (SBBLcarrymask (FlagGT_ULT)) + // result: (MOVLconst [-1]) + for { + if v_0.Op != OpAMD64FlagGT_ULT { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(-1) + return true + } + // match: (SBBLcarrymask (FlagGT_UGT)) + // result: (MOVLconst [0]) + for { + if v_0.Op != OpAMD64FlagGT_UGT { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SBBQ(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SBBQ x (MOVQconst [c]) borrow) + // cond: is32Bit(c) + // result: (SBBQconst x [int32(c)] borrow) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + borrow := v_2 + if !(is32Bit(c)) { + break + } + v.reset(OpAMD64SBBQconst) + v.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg2(x, borrow) + return true + } + // match: (SBBQ x y (FlagEQ)) + // result: (SUBQborrow x y) + for { + x := v_0 + y := v_1 + if v_2.Op != OpAMD64FlagEQ { + break } - break + v.reset(OpAMD64SUBQborrow) + v.AddArg2(x, y) + return true } - // match: (ORQ s0:(SHLQconst [j0] x0:(MOVBload [i] {s} p0 mem)) or:(ORQ s1:(SHLQconst [j1] x1:(MOVBload [i] {s} p1 mem)) y)) - // cond: j1 == j0-8 && j1 % 16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (ROLWconst [8] (MOVWload [i] {s} p0 mem))) y) + return false +} +func rewriteValueAMD64_OpAMD64SBBQcarrymask(v *Value) bool { + v_0 := v.Args[0] + // match: (SBBQcarrymask (FlagEQ)) + // result: (MOVQconst [0]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - x0 := s0.Args[0] - if x0.Op != OpAMD64MOVBload { - continue - } - i := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p0 := x0.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - x1 := s1.Args[0] - if x1.Op != OpAMD64MOVBload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { - continue - } - _ = x1.Args[1] - p1 := x1.Args[0] - if mem != x1.Args[1] { - continue - } - y := or_1 - if !(j1 == j0-8 && j1%16 == 0 && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 1) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64ROLWconst, typ.UInt16) - v2.AuxInt = int8ToAuxInt(8) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVWload, typ.UInt16) - v3.AuxInt = int32ToAuxInt(i) - v3.Aux = symToAux(s) - v3.AddArg2(p0, mem) - v2.AddArg(v3) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + if v_0.Op != OpAMD64FlagEQ { + break } - break + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(0) + return true } - // match: (ORQ s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem))) or:(ORQ s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem))) y)) - // cond: i1 == i0+2 && j1 == j0-16 && j1 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, r0, r1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (BSWAPL (MOVLload [i0] {s} p mem))) y) + // match: (SBBQcarrymask (FlagLT_ULT)) + // result: (MOVQconst [-1]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - r0 := s0.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload { - continue - } - i0 := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p := x0.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - r1 := s1.Args[0] - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload { - continue - } - i1 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { - continue - } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] { - continue - } - y := or_1 - if !(i1 == i0+2 && j1 == j0-16 && j1%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, r0, r1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64BSWAPL, typ.UInt32) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) - v3.AuxInt = int32ToAuxInt(i0) - v3.Aux = symToAux(s) - v3.AddArg2(p, mem) - v2.AddArg(v3) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + if v_0.Op != OpAMD64FlagLT_ULT { + break } - break + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(-1) + return true } - // match: (ORQ s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i] {s} p0 mem))) or:(ORQ s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i] {s} p1 mem))) y)) - // cond: j1 == j0-16 && j1 % 32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b,x0,x1,y) != nil && clobber(x0, x1, r0, r1, s0, s1, or) - // result: @mergePoint(b,x0,x1,y) (ORQ (SHLQconst [j1] (BSWAPL (MOVLload [i] {s} p0 mem))) y) + // match: (SBBQcarrymask (FlagLT_UGT)) + // result: (MOVQconst [0]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - s0 := v_0 - if s0.Op != OpAMD64SHLQconst { - continue - } - j0 := auxIntToInt8(s0.AuxInt) - r0 := s0.Args[0] - if r0.Op != OpAMD64ROLWconst || auxIntToInt8(r0.AuxInt) != 8 { - continue - } - x0 := r0.Args[0] - if x0.Op != OpAMD64MOVWload { - continue - } - i := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p0 := x0.Args[0] - or := v_1 - if or.Op != OpAMD64ORQ { - continue - } - _ = or.Args[1] - or_0 := or.Args[0] - or_1 := or.Args[1] - for _i1 := 0; _i1 <= 1; _i1, or_0, or_1 = _i1+1, or_1, or_0 { - s1 := or_0 - if s1.Op != OpAMD64SHLQconst { - continue - } - j1 := auxIntToInt8(s1.AuxInt) - r1 := s1.Args[0] - if r1.Op != OpAMD64ROLWconst || auxIntToInt8(r1.AuxInt) != 8 { - continue - } - x1 := r1.Args[0] - if x1.Op != OpAMD64MOVWload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s { - continue - } - _ = x1.Args[1] - p1 := x1.Args[0] - if mem != x1.Args[1] { - continue - } - y := or_1 - if !(j1 == j0-16 && j1%32 == 0 && x0.Uses == 1 && x1.Uses == 1 && r0.Uses == 1 && r1.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && or.Uses == 1 && sequentialAddresses(p0, p1, 2) && mergePoint(b, x0, x1, y) != nil && clobber(x0, x1, r0, r1, s0, s1, or)) { - continue - } - b = mergePoint(b, x0, x1, y) - v0 := b.NewValue0(x1.Pos, OpAMD64ORQ, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(x1.Pos, OpAMD64SHLQconst, v.Type) - v1.AuxInt = int8ToAuxInt(j1) - v2 := b.NewValue0(x1.Pos, OpAMD64BSWAPL, typ.UInt32) - v3 := b.NewValue0(x1.Pos, OpAMD64MOVLload, typ.UInt32) - v3.AuxInt = int32ToAuxInt(i) - v3.Aux = symToAux(s) - v3.AddArg2(p0, mem) - v2.AddArg(v3) - v1.AddArg(v2) - v0.AddArg2(v1, y) - return true - } + if v_0.Op != OpAMD64FlagLT_UGT { + break } - break + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(0) + return true } - // match: (ORQ x l:(MOVQload [off] {sym} ptr mem)) - // cond: canMergeLoadClobber(v, l, x) && clobber(l) - // result: (ORQload x [off] {sym} ptr mem) + // match: (SBBQcarrymask (FlagGT_ULT)) + // result: (MOVQconst [-1]) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - l := v_1 - if l.Op != OpAMD64MOVQload { - continue - } - off := auxIntToInt32(l.AuxInt) - sym := auxToSym(l.Aux) - mem := l.Args[1] - ptr := l.Args[0] - if !(canMergeLoadClobber(v, l, x) && clobber(l)) { - continue - } - v.reset(OpAMD64ORQload) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(x, ptr, mem) - return true + if v_0.Op != OpAMD64FlagGT_ULT { + break } - break + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + // match: (SBBQcarrymask (FlagGT_UGT)) + // result: (MOVQconst [0]) + for { + if v_0.Op != OpAMD64FlagGT_UGT { + break + } + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(0) + return true } return false } -func rewriteValueAMD64_OpAMD64ORQconst(v *Value) bool { +func rewriteValueAMD64_OpAMD64SBBQconst(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ORQconst [c] x) - // cond: isUint64PowerOfTwo(int64(c)) && uint64(c) >= 128 - // result: (BTSQconst [int8(log32(c))] x) + // match: (SBBQconst x [c] (FlagEQ)) + // result: (SUBQconstborrow x [c]) for { c := auxIntToInt32(v.AuxInt) x := v_0 - if !(isUint64PowerOfTwo(int64(c)) && uint64(c) >= 128) { + if v_1.Op != OpAMD64FlagEQ { break } - v.reset(OpAMD64BTSQconst) - v.AuxInt = int8ToAuxInt(int8(log32(c))) + v.reset(OpAMD64SUBQconstborrow) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } - // match: (ORQconst [c] (ORQconst [d] x)) - // result: (ORQconst [c | d] x) + return false +} +func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { + v_0 := v.Args[0] + // match: (SETA (InvertFlags x)) + // result: (SETB x) for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpAMD64ORQconst { + if v_0.Op != OpAMD64InvertFlags { break } - d := auxIntToInt32(v_0.AuxInt) x := v_0.Args[0] - v.reset(OpAMD64ORQconst) - v.AuxInt = int32ToAuxInt(c | d) + v.reset(OpAMD64SETB) v.AddArg(x) return true } - // match: (ORQconst [c] (BTSQconst [d] x)) - // cond: is32Bit(int64(c) | 1< [1]) mem) for { - off1 := auxIntToInt32(v.AuxInt) + off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) - val := v_0 - if v_1.Op != OpAMD64ADDQconst { + ptr := v_0 + if v_1.Op != OpAMD64FlagEQ { break } - off2 := auxIntToInt32(v_1.AuxInt) - base := v_1.Args[0] mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64ORQload) - v.AuxInt = int32ToAuxInt(off1 + off2) + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v.AddArg3(val, base, mem) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (ORQload [off1] {sym1} val (LEAQ [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (ORQload [off1+off2] {mergeSym(sym1,sym2)} val base mem) + // match: (SETAEstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - val := v_0 - if v_1.Op != OpAMD64LEAQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_ULT { break } - off2 := auxIntToInt32(v_1.AuxInt) - sym2 := auxToSym(v_1.Aux) - base := v_1.Args[0] mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (SETAEstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_UGT { break } - v.reset(OpAMD64ORQload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(val, base, mem) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: ( ORQload x [off] {sym} ptr (MOVSDstore [off] {sym} ptr y _)) - // result: ( ORQ x (MOVQf2i y)) + // match: (SETAEstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) - x := v_0 - ptr := v_1 - if v_2.Op != OpAMD64MOVSDstore || auxIntToInt32(v_2.AuxInt) != off || auxToSym(v_2.Aux) != sym { + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_ULT { break } - y := v_2.Args[1] - if ptr != v_2.Args[0] { + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (SETAEstore [off] {sym} ptr (FlagGT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_UGT { break } - v.reset(OpAMD64ORQ) - v0 := b.NewValue0(v_2.Pos, OpAMD64MOVQf2i, typ.UInt64) - v0.AddArg(y) - v.AddArg2(x, v0) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } return false } -func rewriteValueAMD64_OpAMD64ORQmodify(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ORQmodify [off1] {sym} (ADDQconst [off2] base) val mem) + b := v.Block + typ := &b.Func.Config.Types + // match: (SETAstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64InvertFlags { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (SETAstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (ORQmodify [off1+off2] {sym} base val mem) + // result: (SETAstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -19149,15 +20576,15 @@ func rewriteValueAMD64_OpAMD64ORQmodify(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64ORQmodify) + v.reset(OpAMD64SETAstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (ORQmodify [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETAstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (ORQmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETAstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -19172,1300 +20599,1760 @@ func rewriteValueAMD64_OpAMD64ORQmodify(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64ORQmodify) + v.reset(OpAMD64SETAstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLB(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (ROLB x (NEGQ y)) - // result: (RORB x y) - for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { - break - } - y := v_1.Args[0] - v.reset(OpAMD64RORB) - v.AddArg2(x, y) - return true - } - // match: (ROLB x (NEGL y)) - // result: (RORB x y) + // match: (SETAstore [off] {sym} ptr (FlagEQ) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagEQ { break } - y := v_1.Args[0] - v.reset(OpAMD64RORB) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (ROLB x (MOVQconst [c])) - // result: (ROLBconst [int8(c&7) ] x) + // match: (SETAstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_ULT { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c & 7)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (ROLB x (MOVLconst [c])) - // result: (ROLBconst [int8(c&7) ] x) + // match: (SETAstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_UGT { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(int8(c & 7)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLBconst(v *Value) bool { - v_0 := v.Args[0] - // match: (ROLBconst [c] (ROLBconst [d] x)) - // result: (ROLBconst [(c+d)& 7] x) + // match: (SETAstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64ROLBconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_ULT { break } - d := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt((c + d) & 7) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (ROLBconst x [0]) - // result: x + // match: (SETAstore [off] {sym} ptr (FlagGT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - if auxIntToInt8(v.AuxInt) != 0 { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_UGT { break } - x := v_0 - v.copyOf(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } return false } -func rewriteValueAMD64_OpAMD64ROLL(v *Value) bool { - v_1 := v.Args[1] +func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { v_0 := v.Args[0] - // match: (ROLL x (NEGQ y)) - // result: (RORL x y) + // match: (SETB (TESTQ x x)) + // result: (ConstBool [false]) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + if v_0.Op != OpAMD64TESTQ { break } - y := v_1.Args[0] - v.reset(OpAMD64RORL) - v.AddArg2(x, y) + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } - // match: (ROLL x (NEGL y)) - // result: (RORL x y) + // match: (SETB (TESTL x x)) + // result: (ConstBool [false]) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + if v_0.Op != OpAMD64TESTL { break } - y := v_1.Args[0] - v.reset(OpAMD64RORL) - v.AddArg2(x, y) + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } - // match: (ROLL x (MOVQconst [c])) - // result: (ROLLconst [int8(c&31)] x) + // match: (SETB (TESTW x x)) + // result: (ConstBool [false]) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64TESTW { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } - // match: (ROLL x (MOVLconst [c])) - // result: (ROLLconst [int8(c&31)] x) + // match: (SETB (TESTB x x)) + // result: (ConstBool [false]) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + if v_0.Op != OpAMD64TESTB { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLLconst(v *Value) bool { - v_0 := v.Args[0] - // match: (ROLLconst [c] (ROLLconst [d] x)) - // result: (ROLLconst [(c+d)&31] x) + // match: (SETB (BTLconst [0] x)) + // result: (ANDLconst [1] x) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64ROLLconst { + if v_0.Op != OpAMD64BTLconst || auxIntToInt8(v_0.AuxInt) != 0 { break } - d := auxIntToInt8(v_0.AuxInt) x := v_0.Args[0] - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt((c + d) & 31) + v.reset(OpAMD64ANDLconst) + v.AuxInt = int32ToAuxInt(1) v.AddArg(x) return true } - // match: (ROLLconst x [0]) - // result: x + // match: (SETB (BTQconst [0] x)) + // result: (ANDQconst [1] x) for { - if auxIntToInt8(v.AuxInt) != 0 { + if v_0.Op != OpAMD64BTQconst || auxIntToInt8(v_0.AuxInt) != 0 { break } - x := v_0 - v.copyOf(x) + x := v_0.Args[0] + v.reset(OpAMD64ANDQconst) + v.AuxInt = int32ToAuxInt(1) + v.AddArg(x) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLQ(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (ROLQ x (NEGQ y)) - // result: (RORQ x y) + // match: (SETB (InvertFlags x)) + // result: (SETA x) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + if v_0.Op != OpAMD64InvertFlags { break } - y := v_1.Args[0] - v.reset(OpAMD64RORQ) - v.AddArg2(x, y) + x := v_0.Args[0] + v.reset(OpAMD64SETA) + v.AddArg(x) return true } - // match: (ROLQ x (NEGL y)) - // result: (RORQ x y) + // match: (SETB (FlagEQ)) + // result: (MOVLconst [0]) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + if v_0.Op != OpAMD64FlagEQ { break } - y := v_1.Args[0] - v.reset(OpAMD64RORQ) - v.AddArg2(x, y) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (ROLQ x (MOVQconst [c])) - // result: (ROLQconst [int8(c&63)] x) + // match: (SETB (FlagLT_ULT)) + // result: (MOVLconst [1]) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64FlagLT_ULT { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(int8(c & 63)) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (ROLQ x (MOVLconst [c])) - // result: (ROLQconst [int8(c&63)] x) + // match: (SETB (FlagLT_UGT)) + // result: (MOVLconst [0]) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + if v_0.Op != OpAMD64FlagLT_UGT { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(int8(c & 63)) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLQconst(v *Value) bool { - v_0 := v.Args[0] - // match: (ROLQconst [c] (ROLQconst [d] x)) - // result: (ROLQconst [(c+d)&63] x) + // match: (SETB (FlagGT_ULT)) + // result: (MOVLconst [1]) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64ROLQconst { + if v_0.Op != OpAMD64FlagGT_ULT { break } - d := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt((c + d) & 63) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (ROLQconst x [0]) - // result: x + // match: (SETB (FlagGT_UGT)) + // result: (MOVLconst [0]) for { - if auxIntToInt8(v.AuxInt) != 0 { + if v_0.Op != OpAMD64FlagGT_UGT { break } - x := v_0 - v.copyOf(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValueAMD64_OpAMD64ROLW(v *Value) bool { - v_1 := v.Args[1] +func rewriteValueAMD64_OpAMD64SETBE(v *Value) bool { v_0 := v.Args[0] - // match: (ROLW x (NEGQ y)) - // result: (RORW x y) + // match: (SETBE (InvertFlags x)) + // result: (SETAE x) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + if v_0.Op != OpAMD64InvertFlags { break } - y := v_1.Args[0] - v.reset(OpAMD64RORW) - v.AddArg2(x, y) + x := v_0.Args[0] + v.reset(OpAMD64SETAE) + v.AddArg(x) return true } - // match: (ROLW x (NEGL y)) - // result: (RORW x y) + // match: (SETBE (FlagEQ)) + // result: (MOVLconst [1]) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + if v_0.Op != OpAMD64FlagEQ { break } - y := v_1.Args[0] - v.reset(OpAMD64RORW) - v.AddArg2(x, y) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (ROLW x (MOVQconst [c])) - // result: (ROLWconst [int8(c&15)] x) + // match: (SETBE (FlagLT_ULT)) + // result: (MOVLconst [1]) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64FlagLT_ULT { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(int8(c & 15)) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (ROLW x (MOVLconst [c])) - // result: (ROLWconst [int8(c&15)] x) + // match: (SETBE (FlagLT_UGT)) + // result: (MOVLconst [0]) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + if v_0.Op != OpAMD64FlagLT_UGT { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(int8(c & 15)) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } - return false -} -func rewriteValueAMD64_OpAMD64ROLWconst(v *Value) bool { - v_0 := v.Args[0] - // match: (ROLWconst [c] (ROLWconst [d] x)) - // result: (ROLWconst [(c+d)&15] x) + // match: (SETBE (FlagGT_ULT)) + // result: (MOVLconst [1]) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64ROLWconst { + if v_0.Op != OpAMD64FlagGT_ULT { break } - d := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt((c + d) & 15) - v.AddArg(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (ROLWconst x [0]) - // result: x + // match: (SETBE (FlagGT_UGT)) + // result: (MOVLconst [0]) for { - if auxIntToInt8(v.AuxInt) != 0 { + if v_0.Op != OpAMD64FlagGT_UGT { break } - x := v_0 - v.copyOf(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValueAMD64_OpAMD64RORB(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { + v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] - // match: (RORB x (NEGQ y)) - // result: (ROLB x y) + b := v.Block + typ := &b.Func.Config.Types + // match: (SETBEstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETAEstore [off] {sym} ptr x mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64InvertFlags { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLB) - v.AddArg2(x, y) + x := v_1.Args[0] + mem := v_2 + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) return true } - // match: (RORB x (NEGL y)) - // result: (ROLB x y) + // match: (SETBEstore [off1] {sym} (ADDQconst [off2] base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) + // result: (SETBEstore [off1+off2] {sym} base val mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLB) - v.AddArg2(x, y) + off2 := auxIntToInt32(v_0.AuxInt) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + int64(off2))) { + break + } + v.reset(OpAMD64SETBEstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(sym) + v.AddArg3(base, val, mem) return true } - // match: (RORB x (MOVQconst [c])) - // result: (ROLBconst [int8((-c)&7) ] x) + // match: (SETBEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (SETBEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 7)) - v.AddArg(x) + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpAMD64SETBEstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(base, val, mem) return true } - // match: (RORB x (MOVLconst [c])) - // result: (ROLBconst [int8((-c)&7) ] x) + // match: (SETBEstore [off] {sym} ptr (FlagEQ) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagEQ { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 7)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64RORL(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RORL x (NEGQ y)) - // result: (ROLL x y) + // match: (SETBEstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_ULT { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLL) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORL x (NEGL y)) - // result: (ROLL x y) + // match: (SETBEstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_UGT { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLL) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORL x (MOVQconst [c])) - // result: (ROLLconst [int8((-c)&31)] x) + // match: (SETBEstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_ULT { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 31)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORL x (MOVLconst [c])) - // result: (ROLLconst [int8((-c)&31)] x) + // match: (SETBEstore [off] {sym} ptr (FlagGT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_UGT { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 31)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } return false } -func rewriteValueAMD64_OpAMD64RORQ(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { + v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] - // match: (RORQ x (NEGQ y)) - // result: (ROLQ x y) + b := v.Block + typ := &b.Func.Config.Types + // match: (SETBstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETAstore [off] {sym} ptr x mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64InvertFlags { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLQ) - v.AddArg2(x, y) + x := v_1.Args[0] + mem := v_2 + v.reset(OpAMD64SETAstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) return true } - // match: (RORQ x (NEGL y)) - // result: (ROLQ x y) + // match: (SETBstore [off1] {sym} (ADDQconst [off2] base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) + // result: (SETBstore [off1+off2] {sym} base val mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLQ) - v.AddArg2(x, y) + off2 := auxIntToInt32(v_0.AuxInt) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + int64(off2))) { + break + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(sym) + v.AddArg3(base, val, mem) return true } - // match: (RORQ x (MOVQconst [c])) - // result: (ROLQconst [int8((-c)&63)] x) + // match: (SETBstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (SETBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 63)) - v.AddArg(x) + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(base, val, mem) return true } - // match: (RORQ x (MOVLconst [c])) - // result: (ROLQconst [int8((-c)&63)] x) + // match: (SETBstore [off] {sym} ptr (FlagEQ) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagEQ { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 63)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64RORW(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RORW x (NEGQ y)) - // result: (ROLW x y) + // match: (SETBstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_ULT { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLW) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORW x (NEGL y)) - // result: (ROLW x y) + // match: (SETBstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_UGT { break } - y := v_1.Args[0] - v.reset(OpAMD64ROLW) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORW x (MOVQconst [c])) - // result: (ROLWconst [int8((-c)&15)] x) + // match: (SETBstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_ULT { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 15)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (RORW x (MOVLconst [c])) - // result: (ROLWconst [int8((-c)&15)] x) + // match: (SETBstore [off] {sym} ptr (FlagGT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_UGT { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(int8((-c) & 15)) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } return false } -func rewriteValueAMD64_OpAMD64SARB(v *Value) bool { - v_1 := v.Args[1] +func rewriteValueAMD64_OpAMD64SETEQ(v *Value) bool { v_0 := v.Args[0] - // match: (SARB x (MOVQconst [c])) - // result: (SARBconst [int8(min(int64(c)&31,7))] x) + b := v.Block + // match: (SETEQ (TESTL (SHLL (MOVLconst [1]) x) y)) + // result: (SETAE (BTL x y)) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64TESTL { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SARBconst) - v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 7))) - v.AddArg(x) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLL { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break } - // match: (SARB x (MOVLconst [c])) - // result: (SARBconst [int8(min(int64(c)&31,7))] x) + // match: (SETEQ (TESTQ (SHLQ (MOVQconst [1]) x) y)) + // result: (SETAE (BTQ x y)) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + if v_0.Op != OpAMD64TESTQ { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SARBconst) - v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 7))) - v.AddArg(x) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLQ { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (SETEQ (TESTL (SHLXL (MOVLconst [1]) x) y)) + // result: (SETAE (BTL x y)) + for { + if v_0.Op != OpAMD64TESTL { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXL { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break + } + // match: (SETEQ (TESTQ (SHLXQ (MOVQconst [1]) x) y)) + // result: (SETAE (BTQ x y)) + for { + if v_0.Op != OpAMD64TESTQ { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXQ { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break } - return false -} -func rewriteValueAMD64_OpAMD64SARBconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SARBconst x [0]) - // result: x + // match: (SETEQ (TESTLconst [c] x)) + // cond: isUint32PowerOfTwo(int64(c)) + // result: (SETAE (BTLconst [int8(log32(c))] x)) for { - if auxIntToInt8(v.AuxInt) != 0 { + if v_0.Op != OpAMD64TESTLconst { break } - x := v_0 - v.copyOf(x) - return true - } - // match: (SARBconst [c] (MOVQconst [d])) - // result: (MOVQconst [int64(int8(d))>>uint64(c)]) - for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVQconst { + c := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] + if !(isUint32PowerOfTwo(int64(c))) { break } - d := auxIntToInt64(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(int64(int8(d)) >> uint64(c)) + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg(v0) return true } - return false -} -func rewriteValueAMD64_OpAMD64SARL(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (SARL x (MOVQconst [c])) - // result: (SARLconst [int8(c&31)] x) + // match: (SETEQ (TESTQconst [c] x)) + // cond: isUint64PowerOfTwo(int64(c)) + // result: (SETAE (BTQconst [int8(log32(c))] x)) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64TESTQconst { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SARLconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) - return true - } - // match: (SARL x (MOVLconst [c])) - // result: (SARLconst [int8(c&31)] x) - for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + c := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] + if !(isUint64PowerOfTwo(int64(c))) { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SARLconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg(v0) return true } - // match: (SARL x (ADDQconst [c] y)) - // cond: c & 31 == 0 - // result: (SARL x y) + // match: (SETEQ (TESTQ (MOVQconst [c]) x)) + // cond: isUint64PowerOfTwo(c) + // result: (SETAE (BTQconst [int8(log64(c))] x)) for { - x := v_0 - if v_1.Op != OpAMD64ADDQconst { + if v_0.Op != OpAMD64TESTQ { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&31 == 0) { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64MOVQconst { + continue + } + c := auxIntToInt64(v_0_0.AuxInt) + x := v_0_1 + if !(isUint64PowerOfTwo(c)) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log64(c))) + v0.AddArg(x) + v.AddArg(v0) + return true } - v.reset(OpAMD64SARL) - v.AddArg2(x, y) - return true + break } - // match: (SARL x (NEGQ (ADDQconst [c] y))) - // cond: c & 31 == 0 - // result: (SARL x (NEGQ y)) + // match: (SETEQ (CMPLconst [1] s:(ANDLconst [1] _))) + // result: (SETNE (CMPLconst [0] s)) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { - break - } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ADDQconst { + if v_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_0.AuxInt) != 1 { break } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&31 == 0) { + s := v_0.Args[0] + if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { break } - v.reset(OpAMD64SARL) - v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) - v0.AddArg(y) - v.AddArg2(x, v0) + v.reset(OpAMD64SETNE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) + v.AddArg(v0) return true } - // match: (SARL x (ANDQconst [c] y)) - // cond: c & 31 == 31 - // result: (SARL x y) + // match: (SETEQ (CMPQconst [1] s:(ANDQconst [1] _))) + // result: (SETNE (CMPQconst [0] s)) for { - x := v_0 - if v_1.Op != OpAMD64ANDQconst { + if v_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_0.AuxInt) != 1 { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&31 == 31) { + s := v_0.Args[0] + if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { break } - v.reset(OpAMD64SARL) - v.AddArg2(x, y) + v.reset(OpAMD64SETNE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) + v.AddArg(v0) return true } - // match: (SARL x (NEGQ (ANDQconst [c] y))) - // cond: c & 31 == 31 - // result: (SARL x (NEGQ y)) + // match: (SETEQ (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2)) + // cond: z1==z2 + // result: (SETAE (BTQconst [63] x)) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + if v_0.Op != OpAMD64TESTQ { break } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ANDQconst { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg(v0) + return true } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&31 == 31) { + break + } + // match: (SETEQ (TESTL z1:(SHLLconst [31] (SHRQconst [31] x)) z2)) + // cond: z1==z2 + // result: (SETAE (BTQconst [31] x)) + for { + if v_0.Op != OpAMD64TESTL { break } - v.reset(OpAMD64SARL) - v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg(v0) + return true + } + break } - // match: (SARL x (ADDLconst [c] y)) - // cond: c & 31 == 0 - // result: (SARL x y) + // match: (SETEQ (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2)) + // cond: z1==z2 + // result: (SETAE (BTQconst [0] x)) for { - x := v_0 - if v_1.Op != OpAMD64ADDLconst { + if v_0.Op != OpAMD64TESTQ { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&31 == 0) { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg(v0) + return true } - v.reset(OpAMD64SARL) - v.AddArg2(x, y) - return true + break } - // match: (SARL x (NEGL (ADDLconst [c] y))) - // cond: c & 31 == 0 - // result: (SARL x (NEGL y)) + // match: (SETEQ (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2)) + // cond: z1==z2 + // result: (SETAE (BTLconst [0] x)) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + if v_0.Op != OpAMD64TESTL { break } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ADDLconst { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg(v0) + return true } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&31 == 0) { + break + } + // match: (SETEQ (TESTQ z1:(SHRQconst [63] x) z2)) + // cond: z1==z2 + // result: (SETAE (BTQconst [63] x)) + for { + if v_0.Op != OpAMD64TESTQ { break } - v.reset(OpAMD64SARL) - v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + x := z1.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg(v0) + return true + } + break } - // match: (SARL x (ANDLconst [c] y)) - // cond: c & 31 == 31 - // result: (SARL x y) + // match: (SETEQ (TESTL z1:(SHRLconst [31] x) z2)) + // cond: z1==z2 + // result: (SETAE (BTLconst [31] x)) for { - x := v_0 - if v_1.Op != OpAMD64ANDLconst { + if v_0.Op != OpAMD64TESTL { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&31 == 31) { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + x := z1.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAE) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg(v0) + return true + } + break + } + // match: (SETEQ (InvertFlags x)) + // result: (SETEQ x) + for { + if v_0.Op != OpAMD64InvertFlags { break } - v.reset(OpAMD64SARL) - v.AddArg2(x, y) + x := v_0.Args[0] + v.reset(OpAMD64SETEQ) + v.AddArg(x) return true } - // match: (SARL x (NEGL (ANDLconst [c] y))) - // cond: c & 31 == 31 - // result: (SARL x (NEGL y)) + // match: (SETEQ (FlagEQ)) + // result: (MOVLconst [1]) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + if v_0.Op != OpAMD64FlagEQ { break } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ANDLconst { + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(1) + return true + } + // match: (SETEQ (FlagLT_ULT)) + // result: (MOVLconst [0]) + for { + if v_0.Op != OpAMD64FlagLT_ULT { break } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&31 == 31) { + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (SETEQ (FlagLT_UGT)) + // result: (MOVLconst [0]) + for { + if v_0.Op != OpAMD64FlagLT_UGT { break } - v.reset(OpAMD64SARL) - v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) - v0.AddArg(y) - v.AddArg2(x, v0) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } - return false -} -func rewriteValueAMD64_OpAMD64SARLconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SARLconst x [0]) - // result: x + // match: (SETEQ (FlagGT_ULT)) + // result: (MOVLconst [0]) for { - if auxIntToInt8(v.AuxInt) != 0 { + if v_0.Op != OpAMD64FlagGT_ULT { break } - x := v_0 - v.copyOf(x) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SARLconst [c] (MOVQconst [d])) - // result: (MOVQconst [int64(int32(d))>>uint64(c)]) + // match: (SETEQ (FlagGT_UGT)) + // result: (MOVLconst [0]) for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVQconst { + if v_0.Op != OpAMD64FlagGT_UGT { break } - d := auxIntToInt64(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(int64(int32(d)) >> uint64(c)) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValueAMD64_OpAMD64SARQ(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool { + v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SARQ x (MOVQconst [c])) - // result: (SARQconst [int8(c&63)] x) + typ := &b.Func.Config.Types + // match: (SETEQstore [off] {sym} ptr (TESTL (SHLL (MOVLconst [1]) x) y) mem) + // result: (SETAEstore [off] {sym} ptr (BTL x y) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SARQconst) - v.AuxInt = int8ToAuxInt(int8(c & 63)) - v.AddArg(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLL { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true + } + break } - // match: (SARQ x (MOVLconst [c])) - // result: (SARQconst [int8(c&63)] x) + // match: (SETEQstore [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem) + // result: (SETAEstore [off] {sym} ptr (BTQ x y) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SARQconst) - v.AuxInt = int8ToAuxInt(int8(c & 63)) - v.AddArg(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLQ { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true + } + break } - // match: (SARQ x (ADDQconst [c] y)) - // cond: c & 63 == 0 - // result: (SARQ x y) + // match: (SETEQstore [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem) + // result: (SETAEstore [off] {sym} ptr (BTL x y) mem) for { - x := v_0 - if v_1.Op != OpAMD64ADDQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&63 == 0) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLXL { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64SARQ) - v.AddArg2(x, y) - return true + break } - // match: (SARQ x (NEGQ (ADDQconst [c] y))) - // cond: c & 63 == 0 - // result: (SARQ x (NEGQ y)) + // match: (SETEQstore [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem) + // result: (SETAEstore [off] {sym} ptr (BTQ x y) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - t := v_1.Type + _ = v_1.Args[1] v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ADDQconst { - break - } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&63 == 0) { - break + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLXQ { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64SARQ) - v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true + break } - // match: (SARQ x (ANDQconst [c] y)) - // cond: c & 63 == 63 - // result: (SARQ x y) + // match: (SETEQstore [off] {sym} ptr (TESTLconst [c] x) mem) + // cond: isUint32PowerOfTwo(int64(c)) + // result: (SETAEstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem) for { - x := v_0 - if v_1.Op != OpAMD64ANDQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTLconst { break } c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&63 == 63) { + x := v_1.Args[0] + mem := v_2 + if !(isUint32PowerOfTwo(int64(c))) { break } - v.reset(OpAMD64SARQ) - v.AddArg2(x, y) + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) return true } - // match: (SARQ x (NEGQ (ANDQconst [c] y))) - // cond: c & 63 == 63 - // result: (SARQ x (NEGQ y)) + // match: (SETEQstore [off] {sym} ptr (TESTQconst [c] x) mem) + // cond: isUint64PowerOfTwo(int64(c)) + // result: (SETAEstore [off] {sym} ptr (BTQconst [int8(log32(c))] x) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGQ { - break - } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ANDQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQconst { break } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&63 == 63) { + c := auxIntToInt32(v_1.AuxInt) + x := v_1.Args[0] + mem := v_2 + if !(isUint64PowerOfTwo(int64(c))) { break } - v.reset(OpAMD64SARQ) - v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) - v0.AddArg(y) - v.AddArg2(x, v0) + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) return true } - // match: (SARQ x (ADDLconst [c] y)) - // cond: c & 63 == 0 - // result: (SARQ x y) + // match: (SETEQstore [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) + // cond: isUint64PowerOfTwo(c) + // result: (SETAEstore [off] {sym} ptr (BTQconst [int8(log64(c))] x) mem) for { - x := v_0 - if v_1.Op != OpAMD64ADDLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&63 == 0) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64MOVQconst { + continue + } + c := auxIntToInt64(v_1_0.AuxInt) + x := v_1_1 + mem := v_2 + if !(isUint64PowerOfTwo(c)) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log64(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64SARQ) - v.AddArg2(x, y) - return true + break } - // match: (SARQ x (NEGL (ADDLconst [c] y))) - // cond: c & 63 == 0 - // result: (SARQ x (NEGL y)) + // match: (SETEQstore [off] {sym} ptr (CMPLconst [1] s:(ANDLconst [1] _)) mem) + // result: (SETNEstore [off] {sym} ptr (CMPLconst [0] s) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { - break - } - t := v_1.Type - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ADDLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64CMPLconst || auxIntToInt32(v_1.AuxInt) != 1 { break } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&63 == 0) { + s := v_1.Args[0] + if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { break } - v.reset(OpAMD64SARQ) - v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) - v0.AddArg(y) - v.AddArg2(x, v0) + mem := v_2 + v.reset(OpAMD64SETNEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) + v.AddArg3(ptr, v0, mem) return true } - // match: (SARQ x (ANDLconst [c] y)) - // cond: c & 63 == 63 - // result: (SARQ x y) + // match: (SETEQstore [off] {sym} ptr (CMPQconst [1] s:(ANDQconst [1] _)) mem) + // result: (SETNEstore [off] {sym} ptr (CMPQconst [0] s) mem) for { - x := v_0 - if v_1.Op != OpAMD64ANDLconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64CMPQconst || auxIntToInt32(v_1.AuxInt) != 1 { break } - c := auxIntToInt32(v_1.AuxInt) - y := v_1.Args[0] - if !(c&63 == 63) { + s := v_1.Args[0] + if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { break } - v.reset(OpAMD64SARQ) - v.AddArg2(x, y) + mem := v_2 + v.reset(OpAMD64SETNEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) + v.AddArg3(ptr, v0, mem) return true } - // match: (SARQ x (NEGL (ANDLconst [c] y))) - // cond: c & 63 == 63 - // result: (SARQ x (NEGL y)) + // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTQconst [63] x) mem) for { - x := v_0 - if v_1.Op != OpAMD64NEGL { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - t := v_1.Type + _ = v_1.Args[1] v_1_0 := v_1.Args[0] - if v_1_0.Op != OpAMD64ANDLconst { - break - } - c := auxIntToInt32(v_1_0.AuxInt) - y := v_1_0.Args[0] - if !(c&63 == 63) { - break + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64SARQ) - v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true + break } - return false -} -func rewriteValueAMD64_OpAMD64SARQconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SARQconst x [0]) - // result: x + // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHLLconst [31] (SHRLconst [31] x)) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTLconst [31] x) mem) for { - if auxIntToInt8(v.AuxInt) != 0 { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - x := v_0 - v.copyOf(x) - return true - } - // match: (SARQconst [c] (MOVQconst [d])) - // result: (MOVQconst [d>>uint64(c)]) - for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVQconst { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - d := auxIntToInt64(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(d >> uint64(c)) - return true + break } - return false -} -func rewriteValueAMD64_OpAMD64SARW(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (SARW x (MOVQconst [c])) - // result: (SARWconst [int8(min(int64(c)&31,15))] x) + // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTQconst [0] x) mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SARWconst) - v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 15))) - v.AddArg(x) - return true - } - // match: (SARW x (MOVLconst [c])) - // result: (SARWconst [int8(min(int64(c)&31,15))] x) - for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SARWconst) - v.AuxInt = int8ToAuxInt(int8(min(int64(c)&31, 15))) - v.AddArg(x) - return true + break } - return false -} -func rewriteValueAMD64_OpAMD64SARWconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SARWconst x [0]) - // result: x + // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTLconst [0] x) mem) for { - if auxIntToInt8(v.AuxInt) != 0 { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - x := v_0 - v.copyOf(x) - return true - } - // match: (SARWconst [c] (MOVQconst [d])) - // result: (MOVQconst [int64(int16(d))>>uint64(c)]) - for { - c := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVQconst { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - d := auxIntToInt64(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(int64(int16(d)) >> uint64(c)) - return true + break } - return false -} -func rewriteValueAMD64_OpAMD64SBBLcarrymask(v *Value) bool { - v_0 := v.Args[0] - // match: (SBBLcarrymask (FlagEQ)) - // result: (MOVLconst [0]) + // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] x) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTQconst [63] x) mem) for { - if v_0.Op != OpAMD64FlagEQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SBBLcarrymask (FlagLT_ULT)) - // result: (MOVLconst [-1]) - for { - if v_0.Op != OpAMD64FlagLT_ULT { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + x := z1.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(-1) - return true + break } - // match: (SBBLcarrymask (FlagLT_UGT)) - // result: (MOVLconst [0]) + // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] x) z2) mem) + // cond: z1==z2 + // result: (SETAEstore [off] {sym} ptr (BTLconst [31] x) mem) for { - if v_0.Op != OpAMD64FlagLT_UGT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SBBLcarrymask (FlagGT_ULT)) - // result: (MOVLconst [-1]) - for { - if v_0.Op != OpAMD64FlagGT_ULT { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + x := z1.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETAEstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(-1) - return true + break } - // match: (SBBLcarrymask (FlagGT_UGT)) - // result: (MOVLconst [0]) + // match: (SETEQstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETEQstore [off] {sym} ptr x mem) for { - if v_0.Op != OpAMD64FlagGT_UGT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64InvertFlags { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + x := v_1.Args[0] + mem := v_2 + v.reset(OpAMD64SETEQstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64SBBQ(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (SBBQ x (MOVQconst [c]) borrow) - // cond: is32Bit(c) - // result: (SBBQconst x [int32(c)] borrow) + // match: (SETEQstore [off1] {sym} (ADDQconst [off2] base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) + // result: (SETEQstore [off1+off2] {sym} base val mem) for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQconst { break } - c := auxIntToInt64(v_1.AuxInt) - borrow := v_2 - if !(is32Bit(c)) { + off2 := auxIntToInt32(v_0.AuxInt) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SBBQconst) - v.AuxInt = int32ToAuxInt(int32(c)) - v.AddArg2(x, borrow) + v.reset(OpAMD64SETEQstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(sym) + v.AddArg3(base, val, mem) return true } - // match: (SBBQ x y (FlagEQ)) - // result: (SUBQborrow x y) + // match: (SETEQstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (SETEQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { - x := v_0 - y := v_1 - if v_2.Op != OpAMD64FlagEQ { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpAMD64LEAQ { break } - v.reset(OpAMD64SUBQborrow) - v.AddArg2(x, y) - return true - } - return false -} -func rewriteValueAMD64_OpAMD64SBBQcarrymask(v *Value) bool { - v_0 := v.Args[0] - // match: (SBBQcarrymask (FlagEQ)) - // result: (MOVQconst [0]) - for { - if v_0.Op != OpAMD64FlagEQ { + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(0) + v.reset(OpAMD64SETEQstore) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(base, val, mem) return true } - // match: (SBBQcarrymask (FlagLT_ULT)) - // result: (MOVQconst [-1]) + // match: (SETEQstore [off] {sym} ptr (FlagEQ) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { - if v_0.Op != OpAMD64FlagLT_ULT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagEQ { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(-1) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(1) + v.AddArg3(ptr, v0, mem) return true } - // match: (SBBQcarrymask (FlagLT_UGT)) - // result: (MOVQconst [0]) + // match: (SETEQstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - if v_0.Op != OpAMD64FlagLT_UGT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_ULT { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(0) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (SBBQcarrymask (FlagGT_ULT)) - // result: (MOVQconst [-1]) + // match: (SETEQstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - if v_0.Op != OpAMD64FlagGT_ULT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagLT_UGT { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(-1) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - // match: (SBBQcarrymask (FlagGT_UGT)) - // result: (MOVQconst [0]) + // match: (SETEQstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - if v_0.Op != OpAMD64FlagGT_UGT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_ULT { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(0) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } - return false -} -func rewriteValueAMD64_OpAMD64SBBQconst(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (SBBQconst x [c] (FlagEQ)) - // result: (SUBQconstborrow x [c]) + // match: (SETEQstore [off] {sym} ptr (FlagGT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { - c := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpAMD64FlagEQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64FlagGT_UGT { break } - v.reset(OpAMD64SUBQconstborrow) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) + mem := v_2 + v.reset(OpAMD64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg3(ptr, v0, mem) return true } return false } -func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { v_0 := v.Args[0] - // match: (SETA (InvertFlags x)) - // result: (SETB x) + // match: (SETG (InvertFlags x)) + // result: (SETL x) for { if v_0.Op != OpAMD64InvertFlags { break } x := v_0.Args[0] - v.reset(OpAMD64SETB) + v.reset(OpAMD64SETL) v.AddArg(x) return true } - // match: (SETA (FlagEQ)) + // match: (SETG (FlagEQ)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagEQ { @@ -20475,7 +22362,7 @@ func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETA (FlagLT_ULT)) + // match: (SETG (FlagLT_ULT)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagLT_ULT { @@ -20485,27 +22372,27 @@ func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETA (FlagLT_UGT)) - // result: (MOVLconst [1]) + // match: (SETG (FlagLT_UGT)) + // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagLT_UGT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETA (FlagGT_ULT)) - // result: (MOVLconst [0]) + // match: (SETG (FlagGT_ULT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_ULT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETA (FlagGT_UGT)) + // match: (SETG (FlagGT_UGT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_UGT { @@ -20517,76 +22404,20 @@ func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETGE(v *Value) bool { v_0 := v.Args[0] - // match: (SETAE (TESTQ x x)) - // result: (ConstBool [true]) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(true) - return true - } - // match: (SETAE (TESTL x x)) - // result: (ConstBool [true]) - for { - if v_0.Op != OpAMD64TESTL { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(true) - return true - } - // match: (SETAE (TESTW x x)) - // result: (ConstBool [true]) - for { - if v_0.Op != OpAMD64TESTW { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(true) - return true - } - // match: (SETAE (TESTB x x)) - // result: (ConstBool [true]) - for { - if v_0.Op != OpAMD64TESTB { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(true) - return true - } - // match: (SETAE (InvertFlags x)) - // result: (SETBE x) + // match: (SETGE (InvertFlags x)) + // result: (SETLE x) for { if v_0.Op != OpAMD64InvertFlags { break } x := v_0.Args[0] - v.reset(OpAMD64SETBE) + v.reset(OpAMD64SETLE) v.AddArg(x) return true } - // match: (SETAE (FlagEQ)) + // match: (SETGE (FlagEQ)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagEQ { @@ -20596,7 +22427,7 @@ func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETAE (FlagLT_ULT)) + // match: (SETGE (FlagLT_ULT)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagLT_ULT { @@ -20606,27 +22437,27 @@ func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETAE (FlagLT_UGT)) - // result: (MOVLconst [1]) + // match: (SETGE (FlagLT_UGT)) + // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagLT_UGT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETAE (FlagGT_ULT)) - // result: (MOVLconst [0]) + // match: (SETGE (FlagGT_ULT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_ULT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETAE (FlagGT_UGT)) + // match: (SETGE (FlagGT_UGT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_UGT { @@ -20638,14 +22469,14 @@ func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETGEstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (SETAEstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETBEstore [off] {sym} ptr x mem) + // match: (SETGEstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETLEstore [off] {sym} ptr x mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20655,15 +22486,15 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { } x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETBEstore) + v.reset(OpAMD64SETLEstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v.AddArg3(ptr, x, mem) return true } - // match: (SETAEstore [off1] {sym} (ADDQconst [off2] base) val mem) + // match: (SETGEstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETAEstore [off1+off2] {sym} base val mem) + // result: (SETGEstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20677,15 +22508,15 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SETAEstore) + v.reset(OpAMD64SETGEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (SETAEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETGEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETAEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETGEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -20700,13 +22531,13 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64SETAEstore) + v.reset(OpAMD64SETGEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - // match: (SETAEstore [off] {sym} ptr (FlagEQ) mem) + // match: (SETGEstore [off] {sym} ptr (FlagEQ) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20724,7 +22555,7 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETAEstore [off] {sym} ptr (FlagLT_ULT) mem) + // match: (SETGEstore [off] {sym} ptr (FlagLT_ULT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20742,8 +22573,8 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETAEstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETGEstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20756,12 +22587,12 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) + v0.AuxInt = int32ToAuxInt(0) v.AddArg3(ptr, v0, mem) return true } - // match: (SETAEstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETGEstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20774,11 +22605,11 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETAEstore [off] {sym} ptr (FlagGT_UGT) mem) + // match: (SETGEstore [off] {sym} ptr (FlagGT_UGT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20798,14 +22629,14 @@ func rewriteValueAMD64_OpAMD64SETAEstore(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (SETAstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETBstore [off] {sym} ptr x mem) + // match: (SETGstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETLstore [off] {sym} ptr x mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20815,15 +22646,15 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { } x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETBstore) + v.reset(OpAMD64SETLstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v.AddArg3(ptr, x, mem) return true } - // match: (SETAstore [off1] {sym} (ADDQconst [off2] base) val mem) + // match: (SETGstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETAstore [off1+off2] {sym} base val mem) + // result: (SETGstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20837,15 +22668,15 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SETAstore) + v.reset(OpAMD64SETGstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (SETAstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETGstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETAstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETGstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -20860,13 +22691,13 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64SETAstore) + v.reset(OpAMD64SETGstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - // match: (SETAstore [off] {sym} ptr (FlagEQ) mem) + // match: (SETGstore [off] {sym} ptr (FlagEQ) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20884,7 +22715,7 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETAstore [off] {sym} ptr (FlagLT_ULT) mem) + // match: (SETGstore [off] {sym} ptr (FlagLT_ULT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20902,8 +22733,8 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETAstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETGstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20916,12 +22747,12 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) + v0.AuxInt = int32ToAuxInt(0) v.AddArg3(ptr, v0, mem) return true } - // match: (SETAstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETGstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -20934,11 +22765,11 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETAstore [off] {sym} ptr (FlagGT_UGT) mem) + // match: (SETGstore [off] {sym} ptr (FlagGT_UGT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -20958,100 +22789,20 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETL(v *Value) bool { v_0 := v.Args[0] - // match: (SETB (TESTQ x x)) - // result: (ConstBool [false]) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(false) - return true - } - // match: (SETB (TESTL x x)) - // result: (ConstBool [false]) - for { - if v_0.Op != OpAMD64TESTL { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(false) - return true - } - // match: (SETB (TESTW x x)) - // result: (ConstBool [false]) - for { - if v_0.Op != OpAMD64TESTW { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(false) - return true - } - // match: (SETB (TESTB x x)) - // result: (ConstBool [false]) - for { - if v_0.Op != OpAMD64TESTB { - break - } - x := v_0.Args[1] - if x != v_0.Args[0] { - break - } - v.reset(OpConstBool) - v.AuxInt = boolToAuxInt(false) - return true - } - // match: (SETB (BTLconst [0] x)) - // result: (ANDLconst [1] x) - for { - if v_0.Op != OpAMD64BTLconst || auxIntToInt8(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - v.reset(OpAMD64ANDLconst) - v.AuxInt = int32ToAuxInt(1) - v.AddArg(x) - return true - } - // match: (SETB (BTQconst [0] x)) - // result: (ANDQconst [1] x) - for { - if v_0.Op != OpAMD64BTQconst || auxIntToInt8(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - v.reset(OpAMD64ANDQconst) - v.AuxInt = int32ToAuxInt(1) - v.AddArg(x) - return true - } - // match: (SETB (InvertFlags x)) - // result: (SETA x) + // match: (SETL (InvertFlags x)) + // result: (SETG x) for { if v_0.Op != OpAMD64InvertFlags { break } x := v_0.Args[0] - v.reset(OpAMD64SETA) + v.reset(OpAMD64SETG) v.AddArg(x) return true } - // match: (SETB (FlagEQ)) + // match: (SETL (FlagEQ)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagEQ { @@ -21061,7 +22812,7 @@ func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETB (FlagLT_ULT)) + // match: (SETL (FlagLT_ULT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_ULT { @@ -21071,27 +22822,27 @@ func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETB (FlagLT_UGT)) - // result: (MOVLconst [0]) + // match: (SETL (FlagLT_UGT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_UGT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETB (FlagGT_ULT)) - // result: (MOVLconst [1]) + // match: (SETL (FlagGT_ULT)) + // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagGT_ULT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETB (FlagGT_UGT)) + // match: (SETL (FlagGT_UGT)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagGT_UGT { @@ -21103,20 +22854,20 @@ func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETBE(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETLE(v *Value) bool { v_0 := v.Args[0] - // match: (SETBE (InvertFlags x)) - // result: (SETAE x) + // match: (SETLE (InvertFlags x)) + // result: (SETGE x) for { if v_0.Op != OpAMD64InvertFlags { break } x := v_0.Args[0] - v.reset(OpAMD64SETAE) + v.reset(OpAMD64SETGE) v.AddArg(x) return true } - // match: (SETBE (FlagEQ)) + // match: (SETLE (FlagEQ)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagEQ { @@ -21126,7 +22877,7 @@ func rewriteValueAMD64_OpAMD64SETBE(v *Value) bool { v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETBE (FlagLT_ULT)) + // match: (SETLE (FlagLT_ULT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_ULT { @@ -21136,27 +22887,27 @@ func rewriteValueAMD64_OpAMD64SETBE(v *Value) bool { v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETBE (FlagLT_UGT)) - // result: (MOVLconst [0]) + // match: (SETLE (FlagLT_UGT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_UGT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETBE (FlagGT_ULT)) - // result: (MOVLconst [1]) + // match: (SETLE (FlagGT_ULT)) + // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagGT_ULT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETBE (FlagGT_UGT)) + // match: (SETLE (FlagGT_UGT)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagGT_UGT { @@ -21168,14 +22919,14 @@ func rewriteValueAMD64_OpAMD64SETBE(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETLEstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (SETBEstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETAEstore [off] {sym} ptr x mem) + // match: (SETLEstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETGEstore [off] {sym} ptr x mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21185,15 +22936,15 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { } x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETAEstore) + v.reset(OpAMD64SETGEstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v.AddArg3(ptr, x, mem) return true } - // match: (SETBEstore [off1] {sym} (ADDQconst [off2] base) val mem) + // match: (SETLEstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETBEstore [off1+off2] {sym} base val mem) + // result: (SETLEstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21207,15 +22958,15 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SETBEstore) + v.reset(OpAMD64SETLEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (SETBEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETLEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETBEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETLEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -21230,13 +22981,13 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64SETBEstore) + v.reset(OpAMD64SETLEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - // match: (SETBEstore [off] {sym} ptr (FlagEQ) mem) + // match: (SETLEstore [off] {sym} ptr (FlagEQ) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21254,7 +23005,7 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETBEstore [off] {sym} ptr (FlagLT_ULT) mem) + // match: (SETLEstore [off] {sym} ptr (FlagLT_ULT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21272,8 +23023,8 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETBEstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETLEstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21286,12 +23037,12 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETBEstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETLEstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21304,11 +23055,11 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) + v0.AuxInt = int32ToAuxInt(0) v.AddArg3(ptr, v0, mem) return true } - // match: (SETBEstore [off] {sym} ptr (FlagGT_UGT) mem) + // match: (SETLEstore [off] {sym} ptr (FlagGT_UGT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21328,14 +23079,14 @@ func rewriteValueAMD64_OpAMD64SETBEstore(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETLstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (SETBstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETAstore [off] {sym} ptr x mem) + // match: (SETLstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETGstore [off] {sym} ptr x mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21345,15 +23096,15 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { } x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETAstore) + v.reset(OpAMD64SETGstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v.AddArg3(ptr, x, mem) return true } - // match: (SETBstore [off1] {sym} (ADDQconst [off2] base) val mem) + // match: (SETLstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETBstore [off1+off2] {sym} base val mem) + // result: (SETLstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21367,15 +23118,15 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SETBstore) + v.reset(OpAMD64SETLstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (SETBstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETLstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -21390,13 +23141,13 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64SETBstore) + v.reset(OpAMD64SETLstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - // match: (SETBstore [off] {sym} ptr (FlagEQ) mem) + // match: (SETLstore [off] {sym} ptr (FlagEQ) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21414,7 +23165,7 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETBstore [off] {sym} ptr (FlagLT_ULT) mem) + // match: (SETLstore [off] {sym} ptr (FlagLT_ULT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21432,8 +23183,8 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETBstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETLstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21446,12 +23197,12 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETBstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETLstore [off] {sym} ptr (FlagGT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -21464,11 +23215,11 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) + v0.AuxInt = int32ToAuxInt(0) v.AddArg3(ptr, v0, mem) return true } - // match: (SETBstore [off] {sym} ptr (FlagGT_UGT) mem) + // match: (SETLstore [off] {sym} ptr (FlagGT_UGT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -21488,327 +23239,35 @@ func rewriteValueAMD64_OpAMD64SETBstore(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETEQ(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETNE(v *Value) bool { v_0 := v.Args[0] b := v.Block - // match: (SETEQ (TESTL (SHLL (MOVLconst [1]) x) y)) - // result: (SETAE (BTL x y)) - for { - if v_0.Op != OpAMD64TESTL { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64SHLL { - continue - } - x := v_0_0.Args[1] - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { - continue - } - y := v_0_1 - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTQ (SHLQ (MOVQconst [1]) x) y)) - // result: (SETAE (BTQ x y)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64SHLQ { - continue - } - x := v_0_0.Args[1] - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { - continue - } - y := v_0_1 - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTLconst [c] x)) - // cond: isUint32PowerOfTwo(int64(c)) - // result: (SETAE (BTLconst [int8(log32(c))] x)) + // match: (SETNE (TESTBconst [1] x)) + // result: (ANDLconst [1] x) for { - if v_0.Op != OpAMD64TESTLconst { + if v_0.Op != OpAMD64TESTBconst || auxIntToInt8(v_0.AuxInt) != 1 { break } - c := auxIntToInt32(v_0.AuxInt) x := v_0.Args[0] - if !(isUint32PowerOfTwo(int64(c))) { - break - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpAMD64ANDLconst) + v.AuxInt = int32ToAuxInt(1) + v.AddArg(x) return true } - // match: (SETEQ (TESTQconst [c] x)) - // cond: isUint64PowerOfTwo(int64(c)) - // result: (SETAE (BTQconst [int8(log32(c))] x)) + // match: (SETNE (TESTWconst [1] x)) + // result: (ANDLconst [1] x) for { - if v_0.Op != OpAMD64TESTQconst { + if v_0.Op != OpAMD64TESTWconst || auxIntToInt16(v_0.AuxInt) != 1 { break } - c := auxIntToInt32(v_0.AuxInt) x := v_0.Args[0] - if !(isUint64PowerOfTwo(int64(c))) { - break - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg(v0) - return true - } - // match: (SETEQ (TESTQ (MOVQconst [c]) x)) - // cond: isUint64PowerOfTwo(c) - // result: (SETAE (BTQconst [int8(log64(c))] x)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64MOVQconst { - continue - } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_1 - if !(isUint64PowerOfTwo(c)) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log64(c))) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (CMPLconst [1] s:(ANDLconst [1] _))) - // result: (SETNE (CMPLconst [0] s)) - for { - if v_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_0.AuxInt) != 1 { - break - } - s := v_0.Args[0] - if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { - break - } - v.reset(OpAMD64SETNE) - v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg(v0) - return true - } - // match: (SETEQ (CMPQconst [1] s:(ANDQconst [1] _))) - // result: (SETNE (CMPQconst [0] s)) - for { - if v_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_0.AuxInt) != 1 { - break - } - s := v_0.Args[0] - if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { - break - } - v.reset(OpAMD64SETNE) - v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg(v0) + v.reset(OpAMD64ANDLconst) + v.AuxInt = int32ToAuxInt(1) + v.AddArg(x) return true } - // match: (SETEQ (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2)) - // cond: z1==z2 - // result: (SETAE (BTQconst [63] x)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTL z1:(SHLLconst [31] (SHRQconst [31] x)) z2)) - // cond: z1==z2 - // result: (SETAE (BTQconst [31] x)) - for { - if v_0.Op != OpAMD64TESTL { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2)) - // cond: z1==z2 - // result: (SETAE (BTQconst [0] x)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2)) - // cond: z1==z2 - // result: (SETAE (BTLconst [0] x)) - for { - if v_0.Op != OpAMD64TESTL { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTQ z1:(SHRQconst [63] x) z2)) - // cond: z1==z2 - // result: (SETAE (BTQconst [63] x)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - x := z1.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETEQ (TESTL z1:(SHRLconst [31] x) z2)) - // cond: z1==z2 - // result: (SETAE (BTLconst [31] x)) + // match: (SETNE (TESTL (SHLL (MOVLconst [1]) x) y)) + // result: (SETB (BTL x y)) for { if v_0.Op != OpAMD64TESTL { break @@ -21817,304 +23276,216 @@ func rewriteValueAMD64_OpAMD64SETEQ(v *Value) bool { v_0_0 := v_0.Args[0] v_0_1 := v_0.Args[1] for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + if v_0_0.Op != OpAMD64SHLL { continue } - x := z1.Args[0] - z2 := v_0_1 - if !(z1 == z2) { + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { continue } - v.reset(OpAMD64SETAE) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) + y := v_0_1 + v.reset(OpAMD64SETB) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) v.AddArg(v0) return true } break } - // match: (SETEQ (InvertFlags x)) - // result: (SETEQ x) - for { - if v_0.Op != OpAMD64InvertFlags { - break - } - x := v_0.Args[0] - v.reset(OpAMD64SETEQ) - v.AddArg(x) - return true - } - // match: (SETEQ (FlagEQ)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagEQ { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETEQ (FlagLT_ULT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagLT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETEQ (FlagLT_UGT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagLT_UGT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETEQ (FlagGT_ULT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagGT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETEQ (FlagGT_UGT)) - // result: (MOVLconst [0]) + // match: (SETNE (TESTQ (SHLQ (MOVQconst [1]) x) y)) + // result: (SETB (BTQ x y)) for { - if v_0.Op != OpAMD64FlagGT_UGT { + if v_0.Op != OpAMD64TESTQ { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLQ { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v.reset(OpAMD64SETB) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + break } - return false -} -func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETEQstore [off] {sym} ptr (TESTL (SHLL (MOVLconst [1]) x) y) mem) - // result: (SETAEstore [off] {sym} ptr (BTL x y) mem) + // match: (SETNE (TESTL (SHLXL (MOVLconst [1]) x) y)) + // result: (SETB (BTL x y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + if v_0.Op != OpAMD64TESTL { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLL { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXL { continue } - x := v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { continue } - y := v_1_1 - mem := v_2 - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + y := v_0_1 + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) v0.AddArg2(x, y) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem) - // result: (SETAEstore [off] {sym} ptr (BTQ x y) mem) + // match: (SETNE (TESTQ (SHLXQ (MOVQconst [1]) x) y)) + // result: (SETB (BTQ x y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + if v_0.Op != OpAMD64TESTQ { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLQ { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXQ { continue } - x := v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { continue } - y := v_1_1 - mem := v_2 - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + y := v_0_1 + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) v0.AddArg2(x, y) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTLconst [c] x) mem) + // match: (SETNE (TESTLconst [c] x)) // cond: isUint32PowerOfTwo(int64(c)) - // result: (SETAEstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem) + // result: (SETB (BTLconst [int8(log32(c))] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTLconst { + if v_0.Op != OpAMD64TESTLconst { break } - c := auxIntToInt32(v_1.AuxInt) - x := v_1.Args[0] - mem := v_2 + c := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] if !(isUint32PowerOfTwo(int64(c))) { break } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(int8(log32(c))) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } - // match: (SETEQstore [off] {sym} ptr (TESTQconst [c] x) mem) + // match: (SETNE (TESTQconst [c] x)) // cond: isUint64PowerOfTwo(int64(c)) - // result: (SETAEstore [off] {sym} ptr (BTQconst [int8(log32(c))] x) mem) + // result: (SETB (BTQconst [int8(log32(c))] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQconst { + if v_0.Op != OpAMD64TESTQconst { break } - c := auxIntToInt32(v_1.AuxInt) - x := v_1.Args[0] - mem := v_2 + c := auxIntToInt32(v_0.AuxInt) + x := v_0.Args[0] if !(isUint64PowerOfTwo(int64(c))) { break } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(int8(log32(c))) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } - // match: (SETEQstore [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) + // match: (SETNE (TESTQ (MOVQconst [c]) x)) // cond: isUint64PowerOfTwo(c) - // result: (SETAEstore [off] {sym} ptr (BTQconst [int8(log64(c))] x) mem) + // result: (SETB (BTQconst [int8(log64(c))] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + if v_0.Op != OpAMD64TESTQ { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64MOVQconst { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64MOVQconst { continue } - c := auxIntToInt64(v_1_0.AuxInt) - x := v_1_1 - mem := v_2 + c := auxIntToInt64(v_0_0.AuxInt) + x := v_0_1 if !(isUint64PowerOfTwo(c)) { continue } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(int8(log64(c))) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (CMPLconst [1] s:(ANDLconst [1] _)) mem) - // result: (SETNEstore [off] {sym} ptr (CMPLconst [0] s) mem) + // match: (SETNE (CMPLconst [1] s:(ANDLconst [1] _))) + // result: (SETEQ (CMPLconst [0] s)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64CMPLconst || auxIntToInt32(v_1.AuxInt) != 1 { + if v_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_0.AuxInt) != 1 { break } - s := v_1.Args[0] + s := v_0.Args[0] if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { break } - mem := v_2 - v.reset(OpAMD64SETNEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETEQ) v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) v0.AddArg(s) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } - // match: (SETEQstore [off] {sym} ptr (CMPQconst [1] s:(ANDQconst [1] _)) mem) - // result: (SETNEstore [off] {sym} ptr (CMPQconst [0] s) mem) + // match: (SETNE (CMPQconst [1] s:(ANDQconst [1] _))) + // result: (SETEQ (CMPQconst [0] s)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64CMPQconst || auxIntToInt32(v_1.AuxInt) != 1 { + if v_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_0.AuxInt) != 1 { break } - s := v_1.Args[0] + s := v_0.Args[0] if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { break } - mem := v_2 - v.reset(OpAMD64SETNEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETEQ) v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) v0.AddArg(s) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } - // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2) mem) + // match: (SETNE (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2)) // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTQconst [63] x) mem) + // result: (SETB (BTQconst [63] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + if v_0.Op != OpAMD64TESTQ { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { continue } @@ -22123,76 +23494,64 @@ func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool { continue } x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 + z2 := v_0_1 if !(z1 == z2) { continue } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(63) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHLLconst [31] (SHRLconst [31] x)) z2) mem) + // match: (SETNE (TESTL z1:(SHLLconst [31] (SHRQconst [31] x)) z2)) // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTLconst [31] x) mem) + // result: (SETB (BTQconst [31] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + if v_0.Op != OpAMD64TESTL { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { continue } z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 31 { continue } x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 + z2 := v_0_1 if !(z1 == z2) { continue } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v.reset(OpAMD64SETB) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(31) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2) mem) + // match: (SETNE (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2)) // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTQconst [0] x) mem) + // result: (SETB (BTQconst [0] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + if v_0.Op != OpAMD64TESTQ { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { continue } @@ -22201,37 +23560,31 @@ func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool { continue } x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 + z2 := v_0_1 if !(z1 == z2) { continue } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(0) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2) mem) + // match: (SETNE (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2)) // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTLconst [0] x) mem) + // result: (SETB (BTLconst [0] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + if v_0.Op != OpAMD64TESTL { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { continue } @@ -22240,260 +23593,89 @@ func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool { continue } x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 + z2 := v_0_1 if !(z1 == z2) { continue } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) + v.reset(OpAMD64SETB) v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) v0.AuxInt = int8ToAuxInt(0) v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true - } - break - } - // match: (SETEQstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] x) z2) mem) - // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTQconst [63] x) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { - break - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - x := z1.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.AddArg(v0) return true } break } - // match: (SETEQstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] x) z2) mem) + // match: (SETNE (TESTQ z1:(SHRQconst [63] x) z2)) // cond: z1==z2 - // result: (SETAEstore [off] {sym} ptr (BTLconst [31] x) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { - break - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - x := z1.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETAEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true - } - break - } - // match: (SETEQstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETEQstore [off] {sym} ptr x mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64InvertFlags { - break - } - x := v_1.Args[0] - mem := v_2 - v.reset(OpAMD64SETEQstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(ptr, x, mem) - return true - } - // match: (SETEQstore [off1] {sym} (ADDQconst [off2] base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETEQstore [off1+off2] {sym} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDQconst { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { - break - } - v.reset(OpAMD64SETEQstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(base, val, mem) - return true - } - // match: (SETEQstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETEQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) - for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAQ { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { - break - } - v.reset(OpAMD64SETEQstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (SETEQstore [off] {sym} ptr (FlagEQ) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagEQ { - break - } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) - return true - } - // match: (SETEQstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_ULT { - break - } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true - } - // match: (SETEQstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_UGT { - break - } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true - } - // match: (SETEQstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // result: (SETB (BTQconst [63] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_ULT { + if v_0.Op != OpAMD64TESTQ { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + x := z1.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETB) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg(v0) + return true + } + break } - // match: (SETEQstore [off] {sym} ptr (FlagGT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETNE (TESTL z1:(SHRLconst [31] x) z2)) + // cond: z1==z2 + // result: (SETB (BTLconst [31] x)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_UGT { + if v_0.Op != OpAMD64TESTL { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z1 := v_0_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + x := z1.Args[0] + z2 := v_0_1 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETB) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg(v0) + return true + } + break } - return false -} -func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { - v_0 := v.Args[0] - // match: (SETG (InvertFlags x)) - // result: (SETL x) + // match: (SETNE (InvertFlags x)) + // result: (SETNE x) for { if v_0.Op != OpAMD64InvertFlags { break } x := v_0.Args[0] - v.reset(OpAMD64SETL) + v.reset(OpAMD64SETNE) v.AddArg(x) return true } - // match: (SETG (FlagEQ)) + // match: (SETNE (FlagEQ)) // result: (MOVLconst [0]) for { if v_0.Op != OpAMD64FlagEQ { @@ -22503,27 +23685,27 @@ func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (SETG (FlagLT_ULT)) - // result: (MOVLconst [0]) + // match: (SETNE (FlagLT_ULT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_ULT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETG (FlagLT_UGT)) - // result: (MOVLconst [0]) + // match: (SETNE (FlagLT_UGT)) + // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagLT_UGT { break } v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETG (FlagGT_ULT)) + // match: (SETNE (FlagGT_ULT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_ULT { @@ -22533,7 +23715,7 @@ func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { v.AuxInt = int32ToAuxInt(1) return true } - // match: (SETG (FlagGT_UGT)) + // match: (SETNE (FlagGT_UGT)) // result: (MOVLconst [1]) for { if v_0.Op != OpAMD64FlagGT_UGT { @@ -22545,239 +23727,502 @@ func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETGE(v *Value) bool { +func rewriteValueAMD64_OpAMD64SETNEstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SETGE (InvertFlags x)) - // result: (SETLE x) + b := v.Block + typ := &b.Func.Config.Types + // match: (SETNEstore [off] {sym} ptr (TESTL (SHLL (MOVLconst [1]) x) y) mem) + // result: (SETBstore [off] {sym} ptr (BTL x y) mem) for { - if v_0.Op != OpAMD64InvertFlags { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - x := v_0.Args[0] - v.reset(OpAMD64SETLE) - v.AddArg(x) - return true - } - // match: (SETGE (FlagEQ)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagEQ { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLL { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true + break } - // match: (SETGE (FlagLT_ULT)) - // result: (MOVLconst [0]) + // match: (SETNEstore [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem) + // result: (SETBstore [off] {sym} ptr (BTQ x y) mem) for { - if v_0.Op != OpAMD64FlagLT_ULT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETGE (FlagLT_UGT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagLT_UGT { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLQ { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true + break } - // match: (SETGE (FlagGT_ULT)) - // result: (MOVLconst [1]) + // match: (SETNEstore [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem) + // result: (SETBstore [off] {sym} ptr (BTL x y) mem) for { - if v_0.Op != OpAMD64FlagGT_ULT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLXL { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true + } + break } - // match: (SETGE (FlagGT_UGT)) - // result: (MOVLconst [1]) + // match: (SETNEstore [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem) + // result: (SETBstore [off] {sym} ptr (BTQ x y) mem) for { - if v_0.Op != OpAMD64FlagGT_UGT { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64SHLXQ { + continue + } + x := v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + continue + } + y := v_1_1 + mem := v_2 + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg3(ptr, v0, mem) + return true + } + break } - return false -} -func rewriteValueAMD64_OpAMD64SETGEstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETGEstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETLEstore [off] {sym} ptr x mem) + // match: (SETNEstore [off] {sym} ptr (TESTLconst [c] x) mem) + // cond: isUint32PowerOfTwo(int64(c)) + // result: (SETBstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64InvertFlags { + if v_1.Op != OpAMD64TESTLconst { break } + c := auxIntToInt32(v_1.AuxInt) x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETLEstore) + if !(isUint32PowerOfTwo(int64(c))) { + break + } + v.reset(OpAMD64SETBstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v.AddArg3(ptr, x, mem) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) return true } - // match: (SETGEstore [off1] {sym} (ADDQconst [off2] base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETGEstore [off1+off2] {sym} base val mem) + // match: (SETNEstore [off] {sym} ptr (TESTQconst [c] x) mem) + // cond: isUint64PowerOfTwo(int64(c)) + // result: (SETBstore [off] {sym} ptr (BTQconst [int8(log32(c))] x) mem) for { - off1 := auxIntToInt32(v.AuxInt) + off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDQconst { + ptr := v_0 + if v_1.Op != OpAMD64TESTQconst { break } - off2 := auxIntToInt32(v_0.AuxInt) - base := v_0.Args[0] - val := v_1 + c := auxIntToInt32(v_1.AuxInt) + x := v_1.Args[0] mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { + if !(isUint64PowerOfTwo(int64(c))) { break } - v.reset(OpAMD64SETGEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v.AddArg3(base, val, mem) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log32(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) return true } - // match: (SETGEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETGEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // match: (SETNEstore [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) + // cond: isUint64PowerOfTwo(c) + // result: (SETBstore [off] {sym} ptr (BTQconst [int8(log64(c))] x) mem) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAQ { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { break } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpAMD64MOVQconst { + continue + } + c := auxIntToInt64(v_1_0.AuxInt) + x := v_1_1 + mem := v_2 + if !(isUint64PowerOfTwo(c)) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(int8(log64(c))) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true } - v.reset(OpAMD64SETGEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true + break } - // match: (SETGEstore [off] {sym} ptr (FlagEQ) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETNEstore [off] {sym} ptr (CMPLconst [1] s:(ANDLconst [1] _)) mem) + // result: (SETEQstore [off] {sym} ptr (CMPLconst [0] s) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagEQ { + if v_1.Op != OpAMD64CMPLconst || auxIntToInt32(v_1.AuxInt) != 1 { + break + } + s := v_1.Args[0] + if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { break } mem := v_2 - v.reset(OpAMD64MOVBstore) + v.reset(OpAMD64SETEQstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) v.AddArg3(ptr, v0, mem) return true } - // match: (SETGEstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETNEstore [off] {sym} ptr (CMPQconst [1] s:(ANDQconst [1] _)) mem) + // result: (SETEQstore [off] {sym} ptr (CMPQconst [0] s) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagLT_ULT { + if v_1.Op != OpAMD64CMPQconst || auxIntToInt32(v_1.AuxInt) != 1 { + break + } + s := v_1.Args[0] + if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { break } mem := v_2 - v.reset(OpAMD64MOVBstore) + v.reset(OpAMD64SETEQstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) + v0.AddArg(s) v.AddArg3(ptr, v0, mem) return true } - // match: (SETGEstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTQconst [63] x) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagLT_UGT { + if v_1.Op != OpAMD64TESTQ { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break + } + // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHLLconst [31] (SHRLconst [31] x)) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTLconst [31] x) mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHRLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break + } + // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTQconst [0] x) mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTQ { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break + } + // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTLconst [0] x) mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64TESTL { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + z1_0 := z1.Args[0] + if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { + continue + } + x := z1_0.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(0) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break } - // match: (SETGEstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] x) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTQconst [63] x) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagGT_ULT { + if v_1.Op != OpAMD64TESTQ { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { + continue + } + x := z1.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(63) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break } - // match: (SETGEstore [off] {sym} ptr (FlagGT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] x) z2) mem) + // cond: z1==z2 + // result: (SETBstore [off] {sym} ptr (BTLconst [31] x) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagGT_UGT { + if v_1.Op != OpAMD64TESTL { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z1 := v_1_0 + if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { + continue + } + x := z1.Args[0] + z2 := v_1_1 + mem := v_2 + if !(z1 == z2) { + continue + } + v.reset(OpAMD64SETBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) + v0.AuxInt = int8ToAuxInt(31) + v0.AddArg(x) + v.AddArg3(ptr, v0, mem) + return true + } + break } - return false -} -func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETGstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETLstore [off] {sym} ptr x mem) + // match: (SETNEstore [off] {sym} ptr (InvertFlags x) mem) + // result: (SETNEstore [off] {sym} ptr x mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -22787,15 +24232,15 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { } x := v_1.Args[0] mem := v_2 - v.reset(OpAMD64SETLstore) + v.reset(OpAMD64SETNEstore) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v.AddArg3(ptr, x, mem) return true } - // match: (SETGstore [off1] {sym} (ADDQconst [off2] base) val mem) + // match: (SETNEstore [off1] {sym} (ADDQconst [off2] base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETGstore [off1+off2] {sym} base val mem) + // result: (SETNEstore [off1+off2] {sym} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -22809,15 +24254,15 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { if !(is32Bit(int64(off1) + int64(off2))) { break } - v.reset(OpAMD64SETGstore) + v.reset(OpAMD64SETNEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(sym) v.AddArg3(base, val, mem) return true } - // match: (SETGstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) + // match: (SETNEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETGstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // result: (SETNEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -22832,13 +24277,13 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { break } - v.reset(OpAMD64SETGstore) + v.reset(OpAMD64SETNEstore) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) v.AddArg3(base, val, mem) return true } - // match: (SETGstore [off] {sym} ptr (FlagEQ) mem) + // match: (SETNEstore [off] {sym} ptr (FlagEQ) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -22856,8 +24301,8 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETGstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETNEstore [off] {sym} ptr (FlagLT_ULT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -22870,12 +24315,12 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETGstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SETNEstore [off] {sym} ptr (FlagLT_UGT) mem) + // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -22888,11 +24333,11 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) + v0.AuxInt = int32ToAuxInt(1) v.AddArg3(ptr, v0, mem) return true } - // match: (SETGstore [off] {sym} ptr (FlagGT_ULT) mem) + // match: (SETNEstore [off] {sym} ptr (FlagGT_ULT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -22910,7 +24355,7 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (SETGstore [off] {sym} ptr (FlagGT_UGT) mem) + // match: (SETNEstore [off] {sym} ptr (FlagGT_UGT) mem) // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) for { off := auxIntToInt32(v.AuxInt) @@ -22930,1505 +24375,1153 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SETL(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SETL (InvertFlags x)) - // result: (SETG x) - for { - if v_0.Op != OpAMD64InvertFlags { - break - } - x := v_0.Args[0] - v.reset(OpAMD64SETG) - v.AddArg(x) - return true - } - // match: (SETL (FlagEQ)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagEQ { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETL (FlagLT_ULT)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagLT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETL (FlagLT_UGT)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagLT_UGT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETL (FlagGT_ULT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagGT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETL (FlagGT_UGT)) - // result: (MOVLconst [0]) + b := v.Block + // match: (SHLL x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SHLXL x y) for { - if v_0.Op != OpAMD64FlagGT_UGT { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + v.reset(OpAMD64SHLXL) + v.AddArg2(x, y) return true } - return false -} -func rewriteValueAMD64_OpAMD64SETLE(v *Value) bool { - v_0 := v.Args[0] - // match: (SETLE (InvertFlags x)) - // result: (SETGE x) + // match: (SHLL x (MOVQconst [c])) + // result: (SHLLconst [int8(c&31)] x) for { - if v_0.Op != OpAMD64InvertFlags { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { break } - x := v_0.Args[0] - v.reset(OpAMD64SETGE) + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) v.AddArg(x) return true } - // match: (SETLE (FlagEQ)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagEQ { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETLE (FlagLT_ULT)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagLT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETLE (FlagLT_UGT)) - // result: (MOVLconst [1]) - for { - if v_0.Op != OpAMD64FlagLT_UGT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) - return true - } - // match: (SETLE (FlagGT_ULT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagGT_ULT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (SETLE (FlagGT_UGT)) - // result: (MOVLconst [0]) - for { - if v_0.Op != OpAMD64FlagGT_UGT { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - return false -} -func rewriteValueAMD64_OpAMD64SETLEstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETLEstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETGEstore [off] {sym} ptr x mem) + // match: (SHLL x (MOVLconst [c])) + // result: (SHLLconst [int8(c&31)] x) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64InvertFlags { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { break } - x := v_1.Args[0] - mem := v_2 - v.reset(OpAMD64SETGEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(ptr, x, mem) + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) return true } - // match: (SETLEstore [off1] {sym} (ADDQconst [off2] base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETLEstore [off1+off2] {sym} base val mem) + // match: (SHLL x (ADDQconst [c] y)) + // cond: c & 31 == 0 + // result: (SHLL x y) for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDQconst { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { break } - off2 := auxIntToInt32(v_0.AuxInt) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { break } - v.reset(OpAMD64SETLEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(base, val, mem) + v.reset(OpAMD64SHLL) + v.AddArg2(x, y) return true } - // match: (SETLEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETLEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // match: (SHLL x (NEGQ (ADDQconst [c] y))) + // cond: c & 31 == 0 + // result: (SHLL x (NEGQ y)) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAQ { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { break } - v.reset(OpAMD64SETLEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) - return true - } - // match: (SETLEstore [off] {sym} ptr (FlagEQ) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagEQ { + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETLEstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SHLL x (ANDQconst [c] y)) + // cond: c & 31 == 31 + // result: (SHLL x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_ULT { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) - return true - } - // match: (SETLEstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_UGT { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLL) + v.AddArg2(x, y) return true } - // match: (SETLEstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SHLL x (NEGQ (ANDQconst [c] y))) + // cond: c & 31 == 31 + // result: (SHLL x (NEGQ y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_ULT { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true - } - // match: (SETLEstore [off] {sym} ptr (FlagGT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_UGT { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true - } - return false -} -func rewriteValueAMD64_OpAMD64SETLstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETLstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETGstore [off] {sym} ptr x mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64InvertFlags { + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { break } - x := v_1.Args[0] - mem := v_2 - v.reset(OpAMD64SETGstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(ptr, x, mem) + v.reset(OpAMD64SHLL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETLstore [off1] {sym} (ADDQconst [off2] base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETLstore [off1+off2] {sym} base val mem) + // match: (SHLL x (ADDLconst [c] y)) + // cond: c & 31 == 0 + // result: (SHLL x y) for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDQconst { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { break } - off2 := auxIntToInt32(v_0.AuxInt) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { break } - v.reset(OpAMD64SETLstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(base, val, mem) + v.reset(OpAMD64SHLL) + v.AddArg2(x, y) return true } - // match: (SETLstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // match: (SHLL x (NEGL (ADDLconst [c] y))) + // cond: c & 31 == 0 + // result: (SHLL x (NEGL y)) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAQ { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { break } - v.reset(OpAMD64SETLstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SHLL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETLstore [off] {sym} ptr (FlagEQ) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SHLL x (ANDLconst [c] y)) + // cond: c & 31 == 31 + // result: (SHLL x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagEQ { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SHLL) + v.AddArg2(x, y) return true } - // match: (SETLstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SHLL x (NEGL (ANDLconst [c] y))) + // cond: c & 31 == 31 + // result: (SHLL x (NEGL y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_ULT { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SHLL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETLstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + return false +} +func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SHLLconst [1] (SHRLconst [1] x)) + // result: (BTRLconst [0] x) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagLT_UGT { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRLconst || auxIntToInt8(v_0.AuxInt) != 1 { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + x := v_0.Args[0] + v.reset(OpAMD64BTRLconst) + v.AuxInt = int8ToAuxInt(0) + v.AddArg(x) return true } - // match: (SETLstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SHLLconst x [0]) + // result: x for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_ULT { + if auxIntToInt8(v.AuxInt) != 0 { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) + x := v_0 + v.copyOf(x) return true } - // match: (SETLstore [off] {sym} ptr (FlagGT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SHLLconst [d] (MOVLconst [c])) + // result: (MOVLconst [c << uint64(d)]) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_UGT { + d := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVLconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) + c := auxIntToInt32(v_0.AuxInt) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(c << uint64(d)) return true } return false } -func rewriteValueAMD64_OpAMD64SETNE(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SETNE (TESTBconst [1] x)) - // result: (ANDLconst [1] x) + // match: (SHLQ x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SHLXQ x y) for { - if v_0.Op != OpAMD64TESTBconst || auxIntToInt8(v_0.AuxInt) != 1 { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { break } - x := v_0.Args[0] - v.reset(OpAMD64ANDLconst) - v.AuxInt = int32ToAuxInt(1) - v.AddArg(x) + v.reset(OpAMD64SHLXQ) + v.AddArg2(x, y) return true } - // match: (SETNE (TESTWconst [1] x)) - // result: (ANDLconst [1] x) + // match: (SHLQ x (MOVQconst [c])) + // result: (SHLQconst [int8(c&63)] x) for { - if v_0.Op != OpAMD64TESTWconst || auxIntToInt16(v_0.AuxInt) != 1 { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { break } - x := v_0.Args[0] - v.reset(OpAMD64ANDLconst) - v.AuxInt = int32ToAuxInt(1) + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) v.AddArg(x) return true } - // match: (SETNE (TESTL (SHLL (MOVLconst [1]) x) y)) - // result: (SETB (BTL x y)) + // match: (SHLQ x (MOVLconst [c])) + // result: (SHLQconst [int8(c&63)] x) for { - if v_0.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64SHLL { - continue - } - x := v_0_0.Args[1] - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { - continue - } - y := v_0_1 - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg(v0) - return true - } - break + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true } - // match: (SETNE (TESTQ (SHLQ (MOVQconst [1]) x) y)) - // result: (SETB (BTQ x y)) + // match: (SHLQ x (ADDQconst [c] y)) + // cond: c & 63 == 0 + // result: (SHLQ x y) for { - if v_0.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64SHLQ { - continue - } - x := v_0_0.Args[1] - v_0_0_0 := v_0_0.Args[0] - if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { - continue - } - y := v_0_1 - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg(v0) - return true + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break } - break + v.reset(OpAMD64SHLQ) + v.AddArg2(x, y) + return true } - // match: (SETNE (TESTLconst [c] x)) - // cond: isUint32PowerOfTwo(int64(c)) - // result: (SETB (BTLconst [int8(log32(c))] x)) + // match: (SHLQ x (NEGQ (ADDQconst [c] y))) + // cond: c & 63 == 0 + // result: (SHLQ x (NEGQ y)) for { - if v_0.Op != OpAMD64TESTLconst { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if !(isUint32PowerOfTwo(int64(c))) { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDQconst { break } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg(v0) + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SHLQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETNE (TESTQconst [c] x)) - // cond: isUint64PowerOfTwo(int64(c)) - // result: (SETB (BTQconst [int8(log32(c))] x)) + // match: (SHLQ x (ANDQconst [c] y)) + // cond: c & 63 == 63 + // result: (SHLQ x y) for { - if v_0.Op != OpAMD64TESTQconst { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { break } - c := auxIntToInt32(v_0.AuxInt) - x := v_0.Args[0] - if !(isUint64PowerOfTwo(int64(c))) { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { break } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpAMD64SHLQ) + v.AddArg2(x, y) return true } - // match: (SETNE (TESTQ (MOVQconst [c]) x)) - // cond: isUint64PowerOfTwo(c) - // result: (SETB (BTQconst [int8(log64(c))] x)) + // match: (SHLQ x (NEGQ (ANDQconst [c] y))) + // cond: c & 63 == 63 + // result: (SHLQ x (NEGQ y)) for { - if v_0.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpAMD64MOVQconst { - continue - } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_1 - if !(isUint64PowerOfTwo(c)) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log64(c))) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETNE (CMPLconst [1] s:(ANDLconst [1] _))) - // result: (SETEQ (CMPLconst [0] s)) - for { - if v_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_0.AuxInt) != 1 { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { break } - s := v_0.Args[0] - if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { break } - v.reset(OpAMD64SETEQ) - v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg(v0) + v.reset(OpAMD64SHLQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETNE (CMPQconst [1] s:(ANDQconst [1] _))) - // result: (SETEQ (CMPQconst [0] s)) + // match: (SHLQ x (ADDLconst [c] y)) + // cond: c & 63 == 0 + // result: (SHLQ x y) for { - if v_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_0.AuxInt) != 1 { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { break } - s := v_0.Args[0] - if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { break } - v.reset(OpAMD64SETEQ) - v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg(v0) + v.reset(OpAMD64SHLQ) + v.AddArg2(x, y) return true } - // match: (SETNE (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2)) - // cond: z1==z2 - // result: (SETB (BTQconst [63] x)) - for { - if v_0.Op != OpAMD64TESTQ { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETNE (TESTL z1:(SHLLconst [31] (SHRQconst [31] x)) z2)) - // cond: z1==z2 - // result: (SETB (BTQconst [31] x)) + // match: (SHLQ x (NEGL (ADDLconst [c] y))) + // cond: c & 63 == 0 + // result: (SHLQ x (NEGL y)) for { - if v_0.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETNE (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2)) - // cond: z1==z2 - // result: (SETB (BTQconst [0] x)) - for { - if v_0.Op != OpAMD64TESTQ { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ADDLconst { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break - } - // match: (SETNE (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2)) - // cond: z1==z2 - // result: (SETB (BTLconst [0] x)) - for { - if v_0.Op != OpAMD64TESTL { + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg(v0) - return true - } - break + v.reset(OpAMD64SHLQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNE (TESTQ z1:(SHRQconst [63] x) z2)) - // cond: z1==z2 - // result: (SETB (BTQconst [63] x)) + // match: (SHLQ x (ANDLconst [c] y)) + // cond: c & 63 == 63 + // result: (SHLQ x y) for { - if v_0.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - x := z1.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg(v0) - return true + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break } - break + v.reset(OpAMD64SHLQ) + v.AddArg2(x, y) + return true } - // match: (SETNE (TESTL z1:(SHRLconst [31] x) z2)) - // cond: z1==z2 - // result: (SETB (BTLconst [31] x)) + // match: (SHLQ x (NEGL (ANDLconst [c] y))) + // cond: c & 63 == 63 + // result: (SHLQ x (NEGL y)) for { - if v_0.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z1 := v_0_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - x := z1.Args[0] - z2 := v_0_1 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETB) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg(v0) - return true + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { + break } - break + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SHLQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNE (InvertFlags x)) - // result: (SETNE x) + return false +} +func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SHLQconst [1] (SHRQconst [1] x)) + // result: (BTRQconst [0] x) for { - if v_0.Op != OpAMD64InvertFlags { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRQconst || auxIntToInt8(v_0.AuxInt) != 1 { break } x := v_0.Args[0] - v.reset(OpAMD64SETNE) + v.reset(OpAMD64BTRQconst) + v.AuxInt = int8ToAuxInt(0) v.AddArg(x) return true } - // match: (SETNE (FlagEQ)) - // result: (MOVLconst [0]) + // match: (SHLQconst x [0]) + // result: x for { - if v_0.Op != OpAMD64FlagEQ { + if auxIntToInt8(v.AuxInt) != 0 { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + x := v_0 + v.copyOf(x) return true } - // match: (SETNE (FlagLT_ULT)) - // result: (MOVLconst [1]) + // match: (SHLQconst [d] (MOVQconst [c])) + // result: (MOVQconst [c << uint64(d)]) for { - if v_0.Op != OpAMD64FlagLT_ULT { + d := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVQconst { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(c << uint64(d)) return true } - // match: (SETNE (FlagLT_UGT)) - // result: (MOVLconst [1]) + // match: (SHLQconst [d] (MOVLconst [c])) + // result: (MOVQconst [int64(c) << uint64(d)]) for { - if v_0.Op != OpAMD64FlagLT_UGT { + d := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64MOVLconst { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + c := auxIntToInt32(v_0.AuxInt) + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(c) << uint64(d)) return true } - // match: (SETNE (FlagGT_ULT)) - // result: (MOVLconst [1]) + return false +} +func rewriteValueAMD64_OpAMD64SHLXL(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SHLXL x (MOVQconst [c])) + // result: (SHLLconst [int8(c&31)] x) for { - if v_0.Op != OpAMD64FlagGT_ULT { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) return true } - // match: (SETNE (FlagGT_UGT)) - // result: (MOVLconst [1]) + // match: (SHLXL x (MOVLconst [c])) + // result: (SHLLconst [int8(c&31)] x) for { - if v_0.Op != OpAMD64FlagGT_UGT { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { break } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(1) + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) return true } - return false -} -func rewriteValueAMD64_OpAMD64SETNEstore(v *Value) bool { - v_2 := v.Args[2] - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (SETNEstore [off] {sym} ptr (TESTL (SHLL (MOVLconst [1]) x) y) mem) - // result: (SETBstore [off] {sym} ptr (BTL x y) mem) + // match: (SHLXL x (ADDQconst [c] y)) + // cond: c & 31 == 0 + // result: (SHLXL x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLL { - continue - } - x := v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 { - continue - } - y := v_1_1 - mem := v_2 - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg3(ptr, v0, mem) - return true + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 0) { + break } - break + v.reset(OpAMD64SHLXL) + v.AddArg2(x, y) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem) - // result: (SETBstore [off] {sym} ptr (BTQ x y) mem) + // match: (SHLXL x (NEGQ (ADDQconst [c] y))) + // cond: c & 31 == 0 + // result: (SHLXL x (NEGQ y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - _ = v_1.Args[1] + t := v_1.Type v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64SHLQ { - continue - } - x := v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { - continue - } - y := v_1_1 - mem := v_2 - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags) - v0.AddArg2(x, y) - v.AddArg3(ptr, v0, mem) - return true + if v_1_0.Op != OpAMD64ADDQconst { + break } - break + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SHLXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTLconst [c] x) mem) - // cond: isUint32PowerOfTwo(int64(c)) - // result: (SETBstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem) + // match: (SHLXL x (ANDQconst [c] y)) + // cond: c & 31 == 31 + // result: (SHLXL x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTLconst { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { break } c := auxIntToInt32(v_1.AuxInt) - x := v_1.Args[0] - mem := v_2 - if !(isUint32PowerOfTwo(int64(c))) { + y := v_1.Args[0] + if !(c&31 == 31) { break } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLXL) + v.AddArg2(x, y) return true } - // match: (SETNEstore [off] {sym} ptr (TESTQconst [c] x) mem) - // cond: isUint64PowerOfTwo(int64(c)) - // result: (SETBstore [off] {sym} ptr (BTQconst [int8(log32(c))] x) mem) + // match: (SHLXL x (NEGQ (ANDQconst [c] y))) + // cond: c & 31 == 31 + // result: (SHLXL x (NEGQ y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQconst { + x := v_0 + if v_1.Op != OpAMD64NEGQ { + break + } + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDQconst { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SHLXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SHLXL x (ADDLconst [c] y)) + // cond: c & 31 == 0 + // result: (SHLXL x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { break } c := auxIntToInt32(v_1.AuxInt) - x := v_1.Args[0] - mem := v_2 - if !(isUint64PowerOfTwo(int64(c))) { + y := v_1.Args[0] + if !(c&31 == 0) { break } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log32(c))) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLXL) + v.AddArg2(x, y) return true } - // match: (SETNEstore [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) - // cond: isUint64PowerOfTwo(c) - // result: (SETBstore [off] {sym} ptr (BTQconst [int8(log64(c))] x) mem) + // match: (SHLXL x (NEGL (ADDLconst [c] y))) + // cond: c & 31 == 0 + // result: (SHLXL x (NEGL y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - _ = v_1.Args[1] + t := v_1.Type v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpAMD64MOVQconst { - continue - } - c := auxIntToInt64(v_1_0.AuxInt) - x := v_1_1 - mem := v_2 - if !(isUint64PowerOfTwo(c)) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(int8(log64(c))) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + if v_1_0.Op != OpAMD64ADDLconst { + break } - break + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 0) { + break + } + v.reset(OpAMD64SHLXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNEstore [off] {sym} ptr (CMPLconst [1] s:(ANDLconst [1] _)) mem) - // result: (SETEQstore [off] {sym} ptr (CMPLconst [0] s) mem) + // match: (SHLXL x (ANDLconst [c] y)) + // cond: c & 31 == 31 + // result: (SHLXL x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64CMPLconst || auxIntToInt32(v_1.AuxInt) != 1 { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { break } - s := v_1.Args[0] - if s.Op != OpAMD64ANDLconst || auxIntToInt32(s.AuxInt) != 1 { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&31 == 31) { break } - mem := v_2 - v.reset(OpAMD64SETEQstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLXL) + v.AddArg2(x, y) return true } - // match: (SETNEstore [off] {sym} ptr (CMPQconst [1] s:(ANDQconst [1] _)) mem) - // result: (SETEQstore [off] {sym} ptr (CMPQconst [0] s) mem) + // match: (SHLXL x (NEGL (ANDLconst [c] y))) + // cond: c & 31 == 31 + // result: (SHLXL x (NEGL y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64CMPQconst || auxIntToInt32(v_1.AuxInt) != 1 { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - s := v_1.Args[0] - if s.Op != OpAMD64ANDQconst || auxIntToInt32(s.AuxInt) != 1 { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { break } - mem := v_2 - v.reset(OpAMD64SETEQstore) + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&31 == 31) { + break + } + v.reset(OpAMD64SHLXL) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (SHLXL l:(MOVLload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SHLXLload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVLload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHLXLload) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(s) - v.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, x, mem) return true } - // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHLQconst [63] (SHRQconst [63] x)) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTQconst [63] x) mem) + return false +} +func rewriteValueAMD64_OpAMD64SHLXLload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SHLXLload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SHLLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem)) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + if v_1.Op != OpAMD64MOVLconst { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHLQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + c := auxIntToInt32(v_1.AuxInt) + mem := v_2 + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SHLXQ(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (SHLXQ x (MOVQconst [c])) + // result: (SHLQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { + break } - break + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHLLconst [31] (SHRLconst [31] x)) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTLconst [31] x) mem) + // match: (SHLXQ x (MOVLconst [c])) + // result: (SHLQconst [int8(c&63)] x) + for { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v.AddArg(x) + return true + } + // match: (SHLXQ x (ADDQconst [c] y)) + // cond: c & 63 == 0 + // result: (SHLXQ x y) + for { + x := v_0 + if v_1.Op != OpAMD64ADDQconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SHLXQ) + v.AddArg2(x, y) + return true + } + // match: (SHLXQ x (NEGQ (ADDQconst [c] y))) + // cond: c & 63 == 0 + // result: (SHLXQ x (NEGQ y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - _ = v_1.Args[1] + t := v_1.Type v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHLLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHRLconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + if v_1_0.Op != OpAMD64ADDQconst { + break } - break + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { + break + } + v.reset(OpAMD64SHLXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] (SHLQconst [63] x)) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTQconst [0] x) mem) + // match: (SHLXQ x (ANDQconst [c] y)) + // cond: c & 63 == 63 + // result: (SHLXQ x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64ANDQconst { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLQconst || auxIntToInt8(z1_0.AuxInt) != 63 { - continue - } - x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { + break } - break + v.reset(OpAMD64SHLXQ) + v.AddArg2(x, y) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] (SHLLconst [31] x)) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTLconst [0] x) mem) + // match: (SHLXQ x (NEGQ (ANDQconst [c] y))) + // cond: c & 63 == 63 + // result: (SHLXQ x (NEGQ y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64NEGQ { break } - _ = v_1.Args[1] + t := v_1.Type v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - z1_0 := z1.Args[0] - if z1_0.Op != OpAMD64SHLLconst || auxIntToInt8(z1_0.AuxInt) != 31 { - continue - } - x := z1_0.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(0) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + if v_1_0.Op != OpAMD64ANDQconst { + break } - break + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SHLXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) + v0.AddArg(y) + v.AddArg2(x, v0) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTQ z1:(SHRQconst [63] x) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTQconst [63] x) mem) + // match: (SHLXQ x (ADDLconst [c] y)) + // cond: c & 63 == 0 + // result: (SHLXQ x y) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTQ { + x := v_0 + if v_1.Op != OpAMD64ADDLconst { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRQconst || auxIntToInt8(z1.AuxInt) != 63 { - continue - } - x := z1.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTQconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(63) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 0) { + break } - break + v.reset(OpAMD64SHLXQ) + v.AddArg2(x, y) + return true } - // match: (SETNEstore [off] {sym} ptr (TESTL z1:(SHRLconst [31] x) z2) mem) - // cond: z1==z2 - // result: (SETBstore [off] {sym} ptr (BTLconst [31] x) mem) + // match: (SHLXQ x (NEGL (ADDLconst [c] y))) + // cond: c & 63 == 0 + // result: (SHLXQ x (NEGL y)) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64TESTL { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - _ = v_1.Args[1] + t := v_1.Type v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z1 := v_1_0 - if z1.Op != OpAMD64SHRLconst || auxIntToInt8(z1.AuxInt) != 31 { - continue - } - x := z1.Args[0] - z2 := v_1_1 - mem := v_2 - if !(z1 == z2) { - continue - } - v.reset(OpAMD64SETBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64BTLconst, types.TypeFlags) - v0.AuxInt = int8ToAuxInt(31) - v0.AddArg(x) - v.AddArg3(ptr, v0, mem) - return true + if v_1_0.Op != OpAMD64ADDLconst { + break } - break - } - // match: (SETNEstore [off] {sym} ptr (InvertFlags x) mem) - // result: (SETNEstore [off] {sym} ptr x mem) - for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64InvertFlags { + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 0) { break } - x := v_1.Args[0] - mem := v_2 - v.reset(OpAMD64SETNEstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v.AddArg3(ptr, x, mem) + v.reset(OpAMD64SHLXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETNEstore [off1] {sym} (ADDQconst [off2] base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) - // result: (SETNEstore [off1+off2] {sym} base val mem) + // match: (SHLXQ x (ANDLconst [c] y)) + // cond: c & 63 == 63 + // result: (SHLXQ x y) for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpAMD64ADDQconst { + x := v_0 + if v_1.Op != OpAMD64ANDLconst { break } - off2 := auxIntToInt32(v_0.AuxInt) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1) + int64(off2))) { + c := auxIntToInt32(v_1.AuxInt) + y := v_1.Args[0] + if !(c&63 == 63) { break } - v.reset(OpAMD64SETNEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(sym) - v.AddArg3(base, val, mem) + v.reset(OpAMD64SHLXQ) + v.AddArg2(x, y) return true } - // match: (SETNEstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (SETNEstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) + // match: (SHLXQ x (NEGL (ANDLconst [c] y))) + // cond: c & 63 == 63 + // result: (SHLXQ x (NEGL y)) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpAMD64LEAQ { + x := v_0 + if v_1.Op != OpAMD64NEGL { break } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - val := v_1 - mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + t := v_1.Type + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAMD64ANDLconst { break } - v.reset(OpAMD64SETNEstore) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg3(base, val, mem) + c := auxIntToInt32(v_1_0.AuxInt) + y := v_1_0.Args[0] + if !(c&63 == 63) { + break + } + v.reset(OpAMD64SHLXQ) + v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (SETNEstore [off] {sym} ptr (FlagEQ) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [0]) mem) + // match: (SHLXQ l:(MOVQload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SHLXQload [off] {sym} ptr x mem) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagEQ { + l := v_0 + if l.Op != OpAMD64MOVQload { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHLXQload) v.AuxInt = int32ToAuxInt(off) v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(0) - v.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, x, mem) return true } - // match: (SETNEstore [off] {sym} ptr (FlagLT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + return false +} +func rewriteValueAMD64_OpAMD64SHLXQload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SHLXQload [off] {sym} ptr (MOVQconst [c]) mem) + // result: (SHLQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagLT_ULT { + if v_1.Op != OpAMD64MOVQconst { break } + c := auxIntToInt64(v_1.AuxInt) mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) return true } - // match: (SETNEstore [off] {sym} ptr (FlagLT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SHLXQload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SHLQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) ptr := v_0 - if v_1.Op != OpAMD64FlagLT_UGT { + if v_1.Op != OpAMD64MOVLconst { break } + c := auxIntToInt32(v_1.AuxInt) mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) return true } - // match: (SETNEstore [off] {sym} ptr (FlagGT_ULT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + return false +} +func rewriteValueAMD64_OpAMD64SHRB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SHRB x (MOVQconst [c])) + // cond: c&31 < 8 + // result: (SHRBconst [int8(c&31)] x) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_ULT { + x := v_0 + if v_1.Op != OpAMD64MOVQconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + c := auxIntToInt64(v_1.AuxInt) + if !(c&31 < 8) { + break + } + v.reset(OpAMD64SHRBconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) return true } - // match: (SETNEstore [off] {sym} ptr (FlagGT_UGT) mem) - // result: (MOVBstore [off] {sym} ptr (MOVLconst [1]) mem) + // match: (SHRB x (MOVLconst [c])) + // cond: c&31 < 8 + // result: (SHRBconst [int8(c&31)] x) for { - off := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - ptr := v_0 - if v_1.Op != OpAMD64FlagGT_UGT { + x := v_0 + if v_1.Op != OpAMD64MOVLconst { break } - mem := v_2 - v.reset(OpAMD64MOVBstore) - v.AuxInt = int32ToAuxInt(off) - v.Aux = symToAux(sym) - v0 := b.NewValue0(v.Pos, OpAMD64MOVLconst, typ.UInt8) - v0.AuxInt = int32ToAuxInt(1) - v.AddArg3(ptr, v0, mem) + c := auxIntToInt32(v_1.AuxInt) + if !(c&31 < 8) { + break + } + v.reset(OpAMD64SHRBconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v.AddArg(x) + return true + } + // match: (SHRB _ (MOVQconst [c])) + // cond: c&31 >= 8 + // result: (MOVLconst [0]) + for { + if v_1.Op != OpAMD64MOVQconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(c&31 >= 8) { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (SHRB _ (MOVLconst [c])) + // cond: c&31 >= 8 + // result: (MOVLconst [0]) + for { + if v_1.Op != OpAMD64MOVLconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + if !(c&31 >= 8) { + break + } + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRBconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SHRBconst x [0]) + // result: x + for { + if auxIntToInt8(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + return false +} +func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SHLL x (MOVQconst [c])) - // result: (SHLLconst [int8(c&31)] x) + // match: (SHRL x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SHRXL x y) + for { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64SHRXL) + v.AddArg2(x, y) + return true + } + // match: (SHRL x (MOVQconst [c])) + // result: (SHRLconst [int8(c&31)] x) for { x := v_0 if v_1.Op != OpAMD64MOVQconst { break } c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SHLLconst) + v.reset(OpAMD64SHRLconst) v.AuxInt = int8ToAuxInt(int8(c & 31)) v.AddArg(x) return true } - // match: (SHLL x (MOVLconst [c])) - // result: (SHLLconst [int8(c&31)] x) + // match: (SHRL x (MOVLconst [c])) + // result: (SHRLconst [int8(c&31)] x) for { x := v_0 if v_1.Op != OpAMD64MOVLconst { break } c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SHLLconst) + v.reset(OpAMD64SHRLconst) v.AuxInt = int8ToAuxInt(int8(c & 31)) v.AddArg(x) return true } - // match: (SHLL x (ADDQconst [c] y)) + // match: (SHRL x (ADDQconst [c] y)) // cond: c & 31 == 0 - // result: (SHLL x y) + // result: (SHRL x y) for { x := v_0 if v_1.Op != OpAMD64ADDQconst { @@ -24439,13 +25532,13 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v.AddArg2(x, y) return true } - // match: (SHLL x (NEGQ (ADDQconst [c] y))) + // match: (SHRL x (NEGQ (ADDQconst [c] y))) // cond: c & 31 == 0 - // result: (SHLL x (NEGQ y)) + // result: (SHRL x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -24461,15 +25554,15 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLL x (ANDQconst [c] y)) + // match: (SHRL x (ANDQconst [c] y)) // cond: c & 31 == 31 - // result: (SHLL x y) + // result: (SHRL x y) for { x := v_0 if v_1.Op != OpAMD64ANDQconst { @@ -24480,13 +25573,13 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v.AddArg2(x, y) return true } - // match: (SHLL x (NEGQ (ANDQconst [c] y))) + // match: (SHRL x (NEGQ (ANDQconst [c] y))) // cond: c & 31 == 31 - // result: (SHLL x (NEGQ y)) + // result: (SHRL x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -24502,15 +25595,15 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLL x (ADDLconst [c] y)) + // match: (SHRL x (ADDLconst [c] y)) // cond: c & 31 == 0 - // result: (SHLL x y) + // result: (SHRL x y) for { x := v_0 if v_1.Op != OpAMD64ADDLconst { @@ -24521,13 +25614,13 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v.AddArg2(x, y) return true } - // match: (SHLL x (NEGL (ADDLconst [c] y))) + // match: (SHRL x (NEGL (ADDLconst [c] y))) // cond: c & 31 == 0 - // result: (SHLL x (NEGL y)) + // result: (SHRL x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -24543,15 +25636,15 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLL x (ANDLconst [c] y)) + // match: (SHRL x (ANDLconst [c] y)) // cond: c & 31 == 31 - // result: (SHLL x y) + // result: (SHRL x y) for { x := v_0 if v_1.Op != OpAMD64ANDLconst { @@ -24562,13 +25655,13 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v.AddArg2(x, y) return true } - // match: (SHLL x (NEGL (ANDLconst [c] y))) + // match: (SHRL x (NEGL (ANDLconst [c] y))) // cond: c & 31 == 31 - // result: (SHLL x (NEGL y)) + // result: (SHRL x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -24584,7 +25677,7 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHLL) + v.reset(OpAMD64SHRL) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) @@ -24592,21 +25685,21 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRLconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHLLconst [1] (SHRLconst [1] x)) - // result: (BTRLconst [0] x) + // match: (SHRLconst [1] (SHLLconst [1] x)) + // result: (BTRLconst [31] x) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRLconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLLconst || auxIntToInt8(v_0.AuxInt) != 1 { break } x := v_0.Args[0] v.reset(OpAMD64BTRLconst) - v.AuxInt = int8ToAuxInt(0) + v.AuxInt = int8ToAuxInt(31) v.AddArg(x) return true } - // match: (SHLLconst x [0]) + // match: (SHRLconst x [0]) // result: x for { if auxIntToInt8(v.AuxInt) != 0 { @@ -24616,53 +25709,54 @@ func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { v.copyOf(x) return true } - // match: (SHLLconst [d] (MOVLconst [c])) - // result: (MOVLconst [c << uint64(d)]) - for { - d := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVLconst { - break - } - c := auxIntToInt32(v_0.AuxInt) - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(c << uint64(d)) - return true - } return false } -func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SHLQ x (MOVQconst [c])) - // result: (SHLQconst [int8(c&63)] x) + // match: (SHRQ x y) + // cond: buildcfg.GOAMD64 >= 3 + // result: (SHRXQ x y) + for { + x := v_0 + y := v_1 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64SHRXQ) + v.AddArg2(x, y) + return true + } + // match: (SHRQ x (MOVQconst [c])) + // result: (SHRQconst [int8(c&63)] x) for { x := v_0 if v_1.Op != OpAMD64MOVQconst { break } c := auxIntToInt64(v_1.AuxInt) - v.reset(OpAMD64SHLQconst) + v.reset(OpAMD64SHRQconst) v.AuxInt = int8ToAuxInt(int8(c & 63)) v.AddArg(x) return true } - // match: (SHLQ x (MOVLconst [c])) - // result: (SHLQconst [int8(c&63)] x) + // match: (SHRQ x (MOVLconst [c])) + // result: (SHRQconst [int8(c&63)] x) for { x := v_0 if v_1.Op != OpAMD64MOVLconst { break } c := auxIntToInt32(v_1.AuxInt) - v.reset(OpAMD64SHLQconst) + v.reset(OpAMD64SHRQconst) v.AuxInt = int8ToAuxInt(int8(c & 63)) v.AddArg(x) return true } - // match: (SHLQ x (ADDQconst [c] y)) + // match: (SHRQ x (ADDQconst [c] y)) // cond: c & 63 == 0 - // result: (SHLQ x y) + // result: (SHRQ x y) for { x := v_0 if v_1.Op != OpAMD64ADDQconst { @@ -24673,13 +25767,13 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v.AddArg2(x, y) return true } - // match: (SHLQ x (NEGQ (ADDQconst [c] y))) + // match: (SHRQ x (NEGQ (ADDQconst [c] y))) // cond: c & 63 == 0 - // result: (SHLQ x (NEGQ y)) + // result: (SHRQ x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -24695,15 +25789,15 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLQ x (ANDQconst [c] y)) + // match: (SHRQ x (ANDQconst [c] y)) // cond: c & 63 == 63 - // result: (SHLQ x y) + // result: (SHRQ x y) for { x := v_0 if v_1.Op != OpAMD64ANDQconst { @@ -24714,13 +25808,13 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v.AddArg2(x, y) return true } - // match: (SHLQ x (NEGQ (ANDQconst [c] y))) + // match: (SHRQ x (NEGQ (ANDQconst [c] y))) // cond: c & 63 == 63 - // result: (SHLQ x (NEGQ y)) + // result: (SHRQ x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -24736,15 +25830,15 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLQ x (ADDLconst [c] y)) + // match: (SHRQ x (ADDLconst [c] y)) // cond: c & 63 == 0 - // result: (SHLQ x y) + // result: (SHRQ x y) for { x := v_0 if v_1.Op != OpAMD64ADDLconst { @@ -24755,13 +25849,13 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v.AddArg2(x, y) return true } - // match: (SHLQ x (NEGL (ADDLconst [c] y))) + // match: (SHRQ x (NEGL (ADDLconst [c] y))) // cond: c & 63 == 0 - // result: (SHLQ x (NEGL y)) + // result: (SHRQ x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -24777,15 +25871,15 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHLQ x (ANDLconst [c] y)) + // match: (SHRQ x (ANDLconst [c] y)) // cond: c & 63 == 63 - // result: (SHLQ x y) + // result: (SHRQ x y) for { x := v_0 if v_1.Op != OpAMD64ANDLconst { @@ -24796,13 +25890,13 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v.AddArg2(x, y) return true } - // match: (SHLQ x (NEGL (ANDLconst [c] y))) + // match: (SHRQ x (NEGL (ANDLconst [c] y))) // cond: c & 63 == 63 - // result: (SHLQ x (NEGL y)) + // result: (SHRQ x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -24818,7 +25912,7 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHLQ) + v.reset(OpAMD64SHRQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) @@ -24826,21 +25920,21 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRQconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHLQconst [1] (SHRQconst [1] x)) - // result: (BTRQconst [0] x) + // match: (SHRQconst [1] (SHLQconst [1] x)) + // result: (BTRQconst [63] x) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRQconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLQconst || auxIntToInt8(v_0.AuxInt) != 1 { break } x := v_0.Args[0] v.reset(OpAMD64BTRQconst) - v.AuxInt = int8ToAuxInt(0) + v.AuxInt = int8ToAuxInt(63) v.AddArg(x) return true } - // match: (SHLQconst x [0]) + // match: (SHRQconst x [0]) // result: x for { if auxIntToInt8(v.AuxInt) != 0 { @@ -24850,93 +25944,69 @@ func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { v.copyOf(x) return true } - // match: (SHLQconst [d] (MOVQconst [c])) - // result: (MOVQconst [c << uint64(d)]) - for { - d := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVQconst { - break - } - c := auxIntToInt64(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(c << uint64(d)) - return true - } - // match: (SHLQconst [d] (MOVLconst [c])) - // result: (MOVQconst [int64(c) << uint64(d)]) - for { - d := auxIntToInt8(v.AuxInt) - if v_0.Op != OpAMD64MOVLconst { - break - } - c := auxIntToInt32(v_0.AuxInt) - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(int64(c) << uint64(d)) - return true - } return false } -func rewriteValueAMD64_OpAMD64SHRB(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRW(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SHRB x (MOVQconst [c])) - // cond: c&31 < 8 - // result: (SHRBconst [int8(c&31)] x) + // match: (SHRW x (MOVQconst [c])) + // cond: c&31 < 16 + // result: (SHRWconst [int8(c&31)] x) for { x := v_0 if v_1.Op != OpAMD64MOVQconst { break } c := auxIntToInt64(v_1.AuxInt) - if !(c&31 < 8) { + if !(c&31 < 16) { break } - v.reset(OpAMD64SHRBconst) + v.reset(OpAMD64SHRWconst) v.AuxInt = int8ToAuxInt(int8(c & 31)) v.AddArg(x) return true } - // match: (SHRB x (MOVLconst [c])) - // cond: c&31 < 8 - // result: (SHRBconst [int8(c&31)] x) + // match: (SHRW x (MOVLconst [c])) + // cond: c&31 < 16 + // result: (SHRWconst [int8(c&31)] x) for { x := v_0 if v_1.Op != OpAMD64MOVLconst { break } c := auxIntToInt32(v_1.AuxInt) - if !(c&31 < 8) { + if !(c&31 < 16) { break } - v.reset(OpAMD64SHRBconst) + v.reset(OpAMD64SHRWconst) v.AuxInt = int8ToAuxInt(int8(c & 31)) v.AddArg(x) return true } - // match: (SHRB _ (MOVQconst [c])) - // cond: c&31 >= 8 + // match: (SHRW _ (MOVQconst [c])) + // cond: c&31 >= 16 // result: (MOVLconst [0]) for { if v_1.Op != OpAMD64MOVQconst { break } c := auxIntToInt64(v_1.AuxInt) - if !(c&31 >= 8) { + if !(c&31 >= 16) { break } v.reset(OpAMD64MOVLconst) v.AuxInt = int32ToAuxInt(0) return true } - // match: (SHRB _ (MOVLconst [c])) - // cond: c&31 >= 8 + // match: (SHRW _ (MOVLconst [c])) + // cond: c&31 >= 16 // result: (MOVLconst [0]) for { if v_1.Op != OpAMD64MOVLconst { break } c := auxIntToInt32(v_1.AuxInt) - if !(c&31 >= 8) { + if !(c&31 >= 16) { break } v.reset(OpAMD64MOVLconst) @@ -24945,9 +26015,9 @@ func rewriteValueAMD64_OpAMD64SHRB(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SHRBconst(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRWconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHRBconst x [0]) + // match: (SHRWconst x [0]) // result: x for { if auxIntToInt8(v.AuxInt) != 0 { @@ -24959,11 +26029,11 @@ func rewriteValueAMD64_OpAMD64SHRBconst(v *Value) bool { } return false } -func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRXL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SHRL x (MOVQconst [c])) + // match: (SHRXL x (MOVQconst [c])) // result: (SHRLconst [int8(c&31)] x) for { x := v_0 @@ -24976,7 +26046,7 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { v.AddArg(x) return true } - // match: (SHRL x (MOVLconst [c])) + // match: (SHRXL x (MOVLconst [c])) // result: (SHRLconst [int8(c&31)] x) for { x := v_0 @@ -24989,9 +26059,9 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { v.AddArg(x) return true } - // match: (SHRL x (ADDQconst [c] y)) + // match: (SHRXL x (ADDQconst [c] y)) // cond: c & 31 == 0 - // result: (SHRL x y) + // result: (SHRXL x y) for { x := v_0 if v_1.Op != OpAMD64ADDQconst { @@ -25002,13 +26072,13 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v.AddArg2(x, y) return true } - // match: (SHRL x (NEGQ (ADDQconst [c] y))) + // match: (SHRXL x (NEGQ (ADDQconst [c] y))) // cond: c & 31 == 0 - // result: (SHRL x (NEGQ y)) + // result: (SHRXL x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -25024,15 +26094,15 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRL x (ANDQconst [c] y)) + // match: (SHRXL x (ANDQconst [c] y)) // cond: c & 31 == 31 - // result: (SHRL x y) + // result: (SHRXL x y) for { x := v_0 if v_1.Op != OpAMD64ANDQconst { @@ -25043,13 +26113,13 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v.AddArg2(x, y) return true } - // match: (SHRL x (NEGQ (ANDQconst [c] y))) + // match: (SHRXL x (NEGQ (ANDQconst [c] y))) // cond: c & 31 == 31 - // result: (SHRL x (NEGQ y)) + // result: (SHRXL x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -25065,15 +26135,15 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRL x (ADDLconst [c] y)) + // match: (SHRXL x (ADDLconst [c] y)) // cond: c & 31 == 0 - // result: (SHRL x y) + // result: (SHRXL x y) for { x := v_0 if v_1.Op != OpAMD64ADDLconst { @@ -25084,13 +26154,13 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v.AddArg2(x, y) return true } - // match: (SHRL x (NEGL (ADDLconst [c] y))) + // match: (SHRXL x (NEGL (ADDLconst [c] y))) // cond: c & 31 == 0 - // result: (SHRL x (NEGL y)) + // result: (SHRXL x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -25106,15 +26176,15 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 0) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRL x (ANDLconst [c] y)) + // match: (SHRXL x (ANDLconst [c] y)) // cond: c & 31 == 31 - // result: (SHRL x y) + // result: (SHRXL x y) for { x := v_0 if v_1.Op != OpAMD64ANDLconst { @@ -25125,13 +26195,13 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v.AddArg2(x, y) return true } - // match: (SHRL x (NEGL (ANDLconst [c] y))) + // match: (SHRXL x (NEGL (ANDLconst [c] y))) // cond: c & 31 == 31 - // result: (SHRL x (NEGL y)) + // result: (SHRXL x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -25147,45 +26217,69 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { if !(c&31 == 31) { break } - v.reset(OpAMD64SHRL) + v.reset(OpAMD64SHRXL) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - return false -} -func rewriteValueAMD64_OpAMD64SHRLconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SHRLconst [1] (SHLLconst [1] x)) - // result: (BTRLconst [31] x) + // match: (SHRXL l:(MOVLload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SHRXLload [off] {sym} ptr x mem) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLLconst || auxIntToInt8(v_0.AuxInt) != 1 { + l := v_0 + if l.Op != OpAMD64MOVLload { break } - x := v_0.Args[0] - v.reset(OpAMD64BTRLconst) - v.AuxInt = int8ToAuxInt(31) - v.AddArg(x) + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHRXLload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) return true } - // match: (SHRLconst x [0]) - // result: x + return false +} +func rewriteValueAMD64_OpAMD64SHRXLload(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SHRXLload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SHRLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem)) for { - if auxIntToInt8(v.AuxInt) != 0 { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpAMD64MOVLconst { break } - x := v_0 - v.copyOf(x) + c := auxIntToInt32(v_1.AuxInt) + mem := v_2 + v.reset(OpAMD64SHRLconst) + v.AuxInt = int8ToAuxInt(int8(c & 31)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) return true } return false } -func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRXQ(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (SHRQ x (MOVQconst [c])) + // match: (SHRXQ x (MOVQconst [c])) // result: (SHRQconst [int8(c&63)] x) for { x := v_0 @@ -25198,7 +26292,7 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { v.AddArg(x) return true } - // match: (SHRQ x (MOVLconst [c])) + // match: (SHRXQ x (MOVLconst [c])) // result: (SHRQconst [int8(c&63)] x) for { x := v_0 @@ -25211,9 +26305,9 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { v.AddArg(x) return true } - // match: (SHRQ x (ADDQconst [c] y)) + // match: (SHRXQ x (ADDQconst [c] y)) // cond: c & 63 == 0 - // result: (SHRQ x y) + // result: (SHRXQ x y) for { x := v_0 if v_1.Op != OpAMD64ADDQconst { @@ -25224,13 +26318,13 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v.AddArg2(x, y) return true } - // match: (SHRQ x (NEGQ (ADDQconst [c] y))) + // match: (SHRXQ x (NEGQ (ADDQconst [c] y))) // cond: c & 63 == 0 - // result: (SHRQ x (NEGQ y)) + // result: (SHRXQ x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -25246,15 +26340,15 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRQ x (ANDQconst [c] y)) + // match: (SHRXQ x (ANDQconst [c] y)) // cond: c & 63 == 63 - // result: (SHRQ x y) + // result: (SHRXQ x y) for { x := v_0 if v_1.Op != OpAMD64ANDQconst { @@ -25265,13 +26359,13 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v.AddArg2(x, y) return true } - // match: (SHRQ x (NEGQ (ANDQconst [c] y))) + // match: (SHRXQ x (NEGQ (ANDQconst [c] y))) // cond: c & 63 == 63 - // result: (SHRQ x (NEGQ y)) + // result: (SHRXQ x (NEGQ y)) for { x := v_0 if v_1.Op != OpAMD64NEGQ { @@ -25287,15 +26381,15 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRQ x (ADDLconst [c] y)) + // match: (SHRXQ x (ADDLconst [c] y)) // cond: c & 63 == 0 - // result: (SHRQ x y) + // result: (SHRXQ x y) for { x := v_0 if v_1.Op != OpAMD64ADDLconst { @@ -25306,13 +26400,13 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v.AddArg2(x, y) return true } - // match: (SHRQ x (NEGL (ADDLconst [c] y))) + // match: (SHRXQ x (NEGL (ADDLconst [c] y))) // cond: c & 63 == 0 - // result: (SHRQ x (NEGL y)) + // result: (SHRXQ x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -25328,15 +26422,15 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 0) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (SHRQ x (ANDLconst [c] y)) + // match: (SHRXQ x (ANDLconst [c] y)) // cond: c & 63 == 63 - // result: (SHRQ x y) + // result: (SHRXQ x y) for { x := v_0 if v_1.Op != OpAMD64ANDLconst { @@ -25347,13 +26441,13 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v.AddArg2(x, y) return true } - // match: (SHRQ x (NEGL (ANDLconst [c] y))) + // match: (SHRXQ x (NEGL (ANDLconst [c] y))) // cond: c & 63 == 63 - // result: (SHRQ x (NEGL y)) + // result: (SHRXQ x (NEGL y)) for { x := v_0 if v_1.Op != OpAMD64NEGL { @@ -25369,119 +26463,80 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { if !(c&63 == 63) { break } - v.reset(OpAMD64SHRQ) + v.reset(OpAMD64SHRXQ) v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t) v0.AddArg(y) v.AddArg2(x, v0) return true } - return false -} -func rewriteValueAMD64_OpAMD64SHRQconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SHRQconst [1] (SHLQconst [1] x)) - // result: (BTRQconst [63] x) + // match: (SHRXQ l:(MOVQload [off] {sym} ptr mem) x) + // cond: canMergeLoad(v, l) && clobber(l) + // result: (SHRXQload [off] {sym} ptr x mem) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLQconst || auxIntToInt8(v_0.AuxInt) != 1 { + l := v_0 + if l.Op != OpAMD64MOVQload { break } - x := v_0.Args[0] - v.reset(OpAMD64BTRQconst) - v.AuxInt = int8ToAuxInt(63) - v.AddArg(x) - return true - } - // match: (SHRQconst x [0]) - // result: x - for { - if auxIntToInt8(v.AuxInt) != 0 { + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(canMergeLoad(v, l) && clobber(l)) { break } - x := v_0 - v.copyOf(x) + v.reset(OpAMD64SHRXQload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) return true } return false } -func rewriteValueAMD64_OpAMD64SHRW(v *Value) bool { +func rewriteValueAMD64_OpAMD64SHRXQload(v *Value) bool { + v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SHRW x (MOVQconst [c])) - // cond: c&31 < 16 - // result: (SHRWconst [int8(c&31)] x) - for { - x := v_0 - if v_1.Op != OpAMD64MOVQconst { - break - } - c := auxIntToInt64(v_1.AuxInt) - if !(c&31 < 16) { - break - } - v.reset(OpAMD64SHRWconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) - return true - } - // match: (SHRW x (MOVLconst [c])) - // cond: c&31 < 16 - // result: (SHRWconst [int8(c&31)] x) - for { - x := v_0 - if v_1.Op != OpAMD64MOVLconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - if !(c&31 < 16) { - break - } - v.reset(OpAMD64SHRWconst) - v.AuxInt = int8ToAuxInt(int8(c & 31)) - v.AddArg(x) - return true - } - // match: (SHRW _ (MOVQconst [c])) - // cond: c&31 >= 16 - // result: (MOVLconst [0]) + b := v.Block + typ := &b.Func.Config.Types + // match: (SHRXQload [off] {sym} ptr (MOVQconst [c]) mem) + // result: (SHRQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 if v_1.Op != OpAMD64MOVQconst { break } c := auxIntToInt64(v_1.AuxInt) - if !(c&31 >= 16) { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) + mem := v_2 + v.reset(OpAMD64SHRQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) return true } - // match: (SHRW _ (MOVLconst [c])) - // cond: c&31 >= 16 - // result: (MOVLconst [0]) + // match: (SHRXQload [off] {sym} ptr (MOVLconst [c]) mem) + // result: (SHRQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem)) for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 if v_1.Op != OpAMD64MOVLconst { break } c := auxIntToInt32(v_1.AuxInt) - if !(c&31 >= 16) { - break - } - v.reset(OpAMD64MOVLconst) - v.AuxInt = int32ToAuxInt(0) - return true - } - return false -} -func rewriteValueAMD64_OpAMD64SHRWconst(v *Value) bool { - v_0 := v.Args[0] - // match: (SHRWconst x [0]) - // result: x - for { - if auxIntToInt8(v.AuxInt) != 0 { - break - } - x := v_0 - v.copyOf(x) + mem := v_2 + v.reset(OpAMD64SHRQconst) + v.AuxInt = int8ToAuxInt(int8(c & 63)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(off) + v0.Aux = symToAux(sym) + v0.AddArg2(ptr, mem) + v.AddArg(v0) return true } return false @@ -26769,6 +27824,25 @@ func rewriteValueAMD64_OpAMD64XORL(v *Value) bool { } break } + // match: (XORL (SHLXL (MOVLconst [1]) y) x) + // result: (BTCL x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64SHLXL { + continue + } + y := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0.AuxInt) != 1 { + continue + } + x := v_1 + v.reset(OpAMD64BTCL) + v.AddArg2(x, y) + return true + } + break + } // match: (XORL (MOVLconst [c]) x) // cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128 // result: (BTCLconst [int8(log32(c))] x) @@ -26805,80 +27879,6 @@ func rewriteValueAMD64_OpAMD64XORL(v *Value) bool { } break } - // match: (XORL (SHLLconst x [c]) (SHRLconst x [d])) - // cond: d==32-c - // result: (ROLLconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRLconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(OpAMD64ROLLconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (XORL (SHLLconst x [c]) (SHRWconst x [d])) - // cond: d==16-c && c < 16 && t.Size() == 2 - // result: (ROLWconst x [c]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRWconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 16-c && c < 16 && t.Size() == 2) { - continue - } - v.reset(OpAMD64ROLWconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (XORL (SHLLconst x [c]) (SHRBconst x [d])) - // cond: d==8-c && c < 8 && t.Size() == 1 - // result: (ROLBconst x [c]) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLLconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRBconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 8-c && c < 8 && t.Size() == 1) { - continue - } - v.reset(OpAMD64ROLBconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (XORL x x) // result: (MOVLconst [0]) for { @@ -26915,6 +27915,21 @@ func rewriteValueAMD64_OpAMD64XORL(v *Value) bool { } break } + // match: (XORL x (ADDLconst [-1] x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSMSKL x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64ADDLconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSMSKL) + v.AddArg(x) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64XORLconst(v *Value) bool { @@ -27291,6 +28306,25 @@ func rewriteValueAMD64_OpAMD64XORQ(v *Value) bool { } break } + // match: (XORQ (SHLXQ (MOVQconst [1]) y) x) + // result: (BTCQ x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64SHLXQ { + continue + } + y := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0.AuxInt) != 1 { + continue + } + x := v_1 + v.reset(OpAMD64BTCQ) + v.AddArg2(x, y) + return true + } + break + } // match: (XORQ (MOVQconst [c]) x) // cond: isUint64PowerOfTwo(c) && uint64(c) >= 128 // result: (BTCQconst [int8(log64(c))] x) @@ -27331,30 +28365,6 @@ func rewriteValueAMD64_OpAMD64XORQ(v *Value) bool { } break } - // match: (XORQ (SHLQconst x [c]) (SHRQconst x [d])) - // cond: d==64-c - // result: (ROLQconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAMD64SHLQconst { - continue - } - c := auxIntToInt8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpAMD64SHRQconst { - continue - } - d := auxIntToInt8(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { - continue - } - v.reset(OpAMD64ROLQconst) - v.AuxInt = int8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (XORQ x x) // result: (MOVQconst [0]) for { @@ -27391,6 +28401,21 @@ func rewriteValueAMD64_OpAMD64XORQ(v *Value) bool { } break } + // match: (XORQ x (ADDQconst [-1] x)) + // cond: buildcfg.GOAMD64 >= 3 + // result: (BLSMSKQ x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAMD64ADDQconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) { + continue + } + v.reset(OpAMD64BLSMSKQ) + v.AddArg(x) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64XORQconst(v *Value) bool { @@ -27945,9 +28970,13 @@ func rewriteValueAMD64_OpBitLen16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (BitLen16 x) + // cond: buildcfg.GOAMD64 < 3 // result: (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64BSRL) v0 := b.NewValue0(v.Pos, OpAMD64LEAL1, typ.UInt32) v0.AuxInt = int32ToAuxInt(1) @@ -27957,15 +28986,40 @@ func rewriteValueAMD64_OpBitLen16(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen16 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL (MOVWQZX x)))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpAMD64MOVWQZX, x.Type) + v2.AddArg(x) + v1.AddArg(v2) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen32(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen32 x) + // cond: buildcfg.GOAMD64 < 3 // result: (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpSelect0) v0 := b.NewValue0(v.Pos, OpAMD64BSRQ, types.NewTuple(typ.UInt64, types.TypeFlags)) v1 := b.NewValue0(v.Pos, OpAMD64LEAQ1, typ.UInt64) @@ -27977,16 +29031,39 @@ func rewriteValueAMD64_OpBitLen32(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen32 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen64(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen64 x) + // cond: buildcfg.GOAMD64 < 3 // result: (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) for { t := v.Type x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64ADDQconst) v.AuxInt = int32ToAuxInt(1) v0 := b.NewValue0(v.Pos, OpAMD64CMOVQEQ, t) @@ -28002,15 +29079,38 @@ func rewriteValueAMD64_OpBitLen64(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen64 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-64] (LZCNTQ x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-64) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTQ, typ.UInt64) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen8(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen8 x) + // cond: buildcfg.GOAMD64 < 3 // result: (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64BSRL) v0 := b.NewValue0(v.Pos, OpAMD64LEAL1, typ.UInt32) v0.AuxInt = int32ToAuxInt(1) @@ -28020,6 +29120,27 @@ func rewriteValueAMD64_OpBitLen8(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen8 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL (MOVBQZX x)))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v2 := b.NewValue0(v.Pos, OpAMD64MOVBQZX, x.Type) + v2.AddArg(x) + v1.AddArg(v2) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpCeil(v *Value) bool { v_0 := v.Args[0] @@ -28960,14 +30081,58 @@ func rewriteValueAMD64_OpCtz16(v *Value) bool { return true } } +func rewriteValueAMD64_OpCtz16NonZero(v *Value) bool { + v_0 := v.Args[0] + // match: (Ctz16NonZero x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTL) + v.AddArg(x) + return true + } + // match: (Ctz16NonZero x) + // cond: buildcfg.GOAMD64 < 3 + // result: (BSFL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } + v.reset(OpAMD64BSFL) + v.AddArg(x) + return true + } + return false +} func rewriteValueAMD64_OpCtz32(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (Ctz32 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTL) + v.AddArg(x) + return true + } + // match: (Ctz32 x) + // cond: buildcfg.GOAMD64 < 3 // result: (Select0 (BSFQ (BTSQconst [32] x))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpSelect0) v0 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags)) v1 := b.NewValue0(v.Pos, OpAMD64BTSQconst, typ.UInt64) @@ -28977,16 +30142,61 @@ func rewriteValueAMD64_OpCtz32(v *Value) bool { v.AddArg(v0) return true } + return false +} +func rewriteValueAMD64_OpCtz32NonZero(v *Value) bool { + v_0 := v.Args[0] + // match: (Ctz32NonZero x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTL) + v.AddArg(x) + return true + } + // match: (Ctz32NonZero x) + // cond: buildcfg.GOAMD64 < 3 + // result: (BSFL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } + v.reset(OpAMD64BSFL) + v.AddArg(x) + return true + } + return false } func rewriteValueAMD64_OpCtz64(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Ctz64 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTQ x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTQ) + v.AddArg(x) + return true + } // match: (Ctz64 x) + // cond: buildcfg.GOAMD64 < 3 // result: (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x))) for { t := v.Type x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64CMOVQEQ) v0 := b.NewValue0(v.Pos, OpSelect0, t) v1 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags)) @@ -28999,21 +30209,39 @@ func rewriteValueAMD64_OpCtz64(v *Value) bool { v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueAMD64_OpCtz64NonZero(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (Ctz64NonZero x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTQ x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTQ) + v.AddArg(x) + return true + } + // match: (Ctz64NonZero x) + // cond: buildcfg.GOAMD64 < 3 // result: (Select0 (BSFQ x)) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpSelect0) v0 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags)) v0.AddArg(x) v.AddArg(v0) return true } + return false } func rewriteValueAMD64_OpCtz8(v *Value) bool { v_0 := v.Args[0] @@ -29031,6 +30259,34 @@ func rewriteValueAMD64_OpCtz8(v *Value) bool { return true } } +func rewriteValueAMD64_OpCtz8NonZero(v *Value) bool { + v_0 := v.Args[0] + // match: (Ctz8NonZero x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (TZCNTL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64TZCNTL) + v.AddArg(x) + return true + } + // match: (Ctz8NonZero x) + // cond: buildcfg.GOAMD64 < 3 + // result: (BSFL x) + for { + x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } + v.reset(OpAMD64BSFL) + v.AddArg(x) + return true + } + return false +} func rewriteValueAMD64_OpDiv16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -29339,11 +30595,11 @@ func rewriteValueAMD64_OpFloor(v *Value) bool { func rewriteValueAMD64_OpGetG(v *Value) bool { v_0 := v.Args[0] // match: (GetG mem) - // cond: !(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal) + // cond: v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal // result: (LoweredGetG mem) for { mem := v_0 - if !(!(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal)) { + if !(v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal) { break } v.reset(OpAMD64LoweredGetG) @@ -29356,11 +30612,11 @@ func rewriteValueAMD64_OpHasCPUFeature(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (HasCPUFeature {s}) - // result: (SETNE (CMPQconst [0] (LoweredHasCPUFeature {s}))) + // result: (SETNE (CMPLconst [0] (LoweredHasCPUFeature {s}))) for { s := auxToSym(v.Aux) v.reset(OpAMD64SETNE) - v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) v1 := b.NewValue0(v.Pos, OpAMD64LoweredHasCPUFeature, typ.UInt64) v1.Aux = symToAux(s) @@ -33459,7 +34715,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool { } // match: (Zero [s] destptr mem) // cond: s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE - // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstorezero destptr mem)) + // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) for { s := auxIntToInt64(v.AuxInt) destptr := v_0 @@ -33472,14 +34728,15 @@ func rewriteValueAMD64_OpZero(v *Value) bool { v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) v0.AuxInt = int64ToAuxInt(s % 16) v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) + v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) v1.AddArg2(destptr, mem) v.AddArg2(v0, v1) return true } // match: (Zero [s] destptr mem) // cond: s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE - // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVQstoreconst [makeValAndOff(0,0)] destptr mem)) + // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) for { s := auxIntToInt64(v.AuxInt) destptr := v_0 @@ -33492,7 +34749,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool { v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) v0.AuxInt = int64ToAuxInt(s % 16) v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVQstoreconst, types.TypeMem) + v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) v1.AddArg2(destptr, mem) v.AddArg2(v0, v1) @@ -33500,7 +34757,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool { } // match: (Zero [16] destptr mem) // cond: config.useSSE - // result: (MOVOstorezero destptr mem) + // result: (MOVOstoreconst [makeValAndOff(0,0)] destptr mem) for { if auxIntToInt64(v.AuxInt) != 16 { break @@ -33510,13 +34767,14 @@ func rewriteValueAMD64_OpZero(v *Value) bool { if !(config.useSSE) { break } - v.reset(OpAMD64MOVOstorezero) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) v.AddArg2(destptr, mem) return true } // match: (Zero [32] destptr mem) // cond: config.useSSE - // result: (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem)) + // result: (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) for { if auxIntToInt64(v.AuxInt) != 32 { break @@ -33526,18 +34784,17 @@ func rewriteValueAMD64_OpZero(v *Value) bool { if !(config.useSSE) { break } - v.reset(OpAMD64MOVOstorezero) - v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v0.AuxInt = int64ToAuxInt(16) - v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v1.AddArg2(destptr, mem) - v.AddArg2(v0, v1) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) + v0.AddArg2(destptr, mem) + v.AddArg2(destptr, v0) return true } // match: (Zero [48] destptr mem) // cond: config.useSSE - // result: (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem))) + // result: (MOVOstoreconst [makeValAndOff(0,32)] destptr (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))) for { if auxIntToInt64(v.AuxInt) != 48 { break @@ -33547,23 +34804,20 @@ func rewriteValueAMD64_OpZero(v *Value) bool { if !(config.useSSE) { break } - v.reset(OpAMD64MOVOstorezero) - v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v0.AuxInt = int64ToAuxInt(32) - v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v2.AuxInt = int64ToAuxInt(16) - v2.AddArg(destptr) - v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v3.AddArg2(destptr, mem) - v1.AddArg2(v2, v3) - v.AddArg2(v0, v1) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 32)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16)) + v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) + v1.AddArg2(destptr, mem) + v0.AddArg2(destptr, v1) + v.AddArg2(destptr, v0) return true } // match: (Zero [64] destptr mem) // cond: config.useSSE - // result: (MOVOstorezero (OffPtr destptr [48]) (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem)))) + // result: (MOVOstoreconst [makeValAndOff(0,48)] destptr (MOVOstoreconst [makeValAndOff(0,32)] destptr (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)))) for { if auxIntToInt64(v.AuxInt) != 64 { break @@ -33573,23 +34827,18 @@ func rewriteValueAMD64_OpZero(v *Value) bool { if !(config.useSSE) { break } - v.reset(OpAMD64MOVOstorezero) - v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v0.AuxInt = int64ToAuxInt(48) - v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v2.AuxInt = int64ToAuxInt(32) - v2.AddArg(destptr) - v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v4 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v4.AuxInt = int64ToAuxInt(16) - v4.AddArg(destptr) - v5 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem) - v5.AddArg2(destptr, mem) - v3.AddArg2(v4, v5) - v1.AddArg2(v2, v3) - v.AddArg2(v0, v1) + v.reset(OpAMD64MOVOstoreconst) + v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 48)) + v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 32)) + v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16)) + v2 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) + v2.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) + v2.AddArg2(destptr, mem) + v1.AddArg2(destptr, v2) + v0.AddArg2(destptr, v1) + v.AddArg2(destptr, v0) return true } // match: (Zero [s] destptr mem) @@ -33628,6 +34877,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool { return false } func rewriteBlockAMD64(b *Block) bool { + typ := &b.Func.Config.Types switch b.Kind { case BlockAMD64EQ: // match: (EQ (TESTL (SHLL (MOVLconst [1]) x) y)) @@ -33678,6 +34928,54 @@ func rewriteBlockAMD64(b *Block) bool { } break } + // match: (EQ (TESTL (SHLXL (MOVLconst [1]) x) y)) + // result: (UGE (BTL x y)) + for b.Controls[0].Op == OpAMD64TESTL { + v_0 := b.Controls[0] + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXL { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + b.resetWithControl(BlockAMD64UGE, v0) + return true + } + break + } + // match: (EQ (TESTQ (SHLXQ (MOVQconst [1]) x) y)) + // result: (UGE (BTQ x y)) + for b.Controls[0].Op == OpAMD64TESTQ { + v_0 := b.Controls[0] + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXQ { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + b.resetWithControl(BlockAMD64UGE, v0) + return true + } + break + } // match: (EQ (TESTLconst [c] x)) // cond: isUint32PowerOfTwo(int64(c)) // result: (UGE (BTLconst [int8(log32(c))] x)) @@ -34162,6 +35460,19 @@ func rewriteBlockAMD64(b *Block) bool { b.resetWithControl(BlockAMD64NE, v0) return true } + case BlockJumpTable: + // match: (JumpTable idx) + // result: (JUMPTABLE {makeJumpTableSym(b)} idx (LEAQ {makeJumpTableSym(b)} (SB))) + for { + idx := b.Controls[0] + v0 := b.NewValue0(b.Pos, OpAMD64LEAQ, typ.Uintptr) + v0.Aux = symToAux(makeJumpTableSym(b)) + v1 := b.NewValue0(b.Pos, OpSB, typ.Uintptr) + v0.AddArg(v1) + b.resetWithControl2(BlockAMD64JUMPTABLE, idx, v0) + b.Aux = symToAux(makeJumpTableSym(b)) + return true + } case BlockAMD64LE: // match: (LE (InvertFlags cmp) yes no) // result: (GE cmp yes no) @@ -34481,6 +35792,54 @@ func rewriteBlockAMD64(b *Block) bool { } break } + // match: (NE (TESTL (SHLXL (MOVLconst [1]) x) y)) + // result: (ULT (BTL x y)) + for b.Controls[0].Op == OpAMD64TESTL { + v_0 := b.Controls[0] + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXL { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpAMD64BTL, types.TypeFlags) + v0.AddArg2(x, y) + b.resetWithControl(BlockAMD64ULT, v0) + return true + } + break + } + // match: (NE (TESTQ (SHLXQ (MOVQconst [1]) x) y)) + // result: (ULT (BTQ x y)) + for b.Controls[0].Op == OpAMD64TESTQ { + v_0 := b.Controls[0] + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpAMD64SHLXQ { + continue + } + x := v_0_0.Args[1] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + continue + } + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpAMD64BTQ, types.TypeFlags) + v0.AddArg2(x, y) + b.resetWithControl(BlockAMD64ULT, v0) + return true + } + break + } // match: (NE (TESTLconst [c] x)) // cond: isUint32PowerOfTwo(int64(c)) // result: (ULT (BTLconst [int8(log32(c))] x)) diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index febb5566e33849..0aebdced4009c2 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -338,6 +338,8 @@ func rewriteValueARM(v *Value) bool { return rewriteValueARM_OpARMSRL(v) case OpARMSRLconst: return rewriteValueARM_OpARMSRLconst(v) + case OpARMSRR: + return rewriteValueARM_OpARMSRR(v) case OpARMSUB: return rewriteValueARM_OpARMSUB(v) case OpARMSUBD: @@ -855,6 +857,9 @@ func rewriteValueARM(v *Value) bool { case OpSubPtr: v.Op = OpARMSUB return true + case OpTailCall: + v.Op = OpARMCALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true @@ -1119,6 +1124,7 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool { return true } // match: (ADCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftLL x y [c] flags) for { x := v_0 @@ -1128,6 +1134,9 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1199,6 +1208,7 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool { return true } // match: (ADCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftRA x y [c] flags) for { x := v_0 @@ -1208,6 +1218,9 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1279,6 +1292,7 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool { return true } // match: (ADCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftRL x y [c] flags) for { x := v_0 @@ -1288,6 +1302,9 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1740,6 +1757,7 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool { return true } // match: (ADDSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftLL x y [c]) for { x := v_0 @@ -1748,6 +1766,9 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -1814,6 +1835,7 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool { return true } // match: (ADDSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftRA x y [c]) for { x := v_0 @@ -1822,6 +1844,9 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -1888,6 +1913,7 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool { return true } // match: (ADDSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftRL x y [c]) for { x := v_0 @@ -1896,6 +1922,9 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2051,22 +2080,6 @@ func rewriteValueARM_OpARMADDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ADDshiftLL [c] (SRLconst x [32-c]) x) - // result: (SRRconst [32-c] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(32 - c) - v.AddArg(x) - return true - } // match: (ADDshiftLL [8] (BFXU [int32(armBFAuxInt(8, 8))] x) x) // result: (REV16 x) for { @@ -2124,6 +2137,7 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool { return true } // match: (ADDshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftLL x y [c]) for { x := v_0 @@ -2132,6 +2146,9 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2198,6 +2215,7 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool { return true } // match: (ADDshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftRA x y [c]) for { x := v_0 @@ -2206,6 +2224,9 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2248,22 +2269,6 @@ func rewriteValueARM_OpARMADDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ADDshiftRL [c] (SLLconst x [32-c]) x) - // result: (SRRconst [ c] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } return false } func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool { @@ -2288,6 +2293,7 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool { return true } // match: (ADDshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftRL x y [c]) for { x := v_0 @@ -2296,6 +2302,9 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2614,18 +2623,16 @@ func rewriteValueARM_OpARMANDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2655,6 +2662,7 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool { return true } // match: (ANDshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftLL x y [c]) for { x := v_0 @@ -2663,6 +2671,9 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2705,18 +2716,16 @@ func rewriteValueARM_OpARMANDshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2746,6 +2755,7 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool { return true } // match: (ANDshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftRA x y [c]) for { x := v_0 @@ -2754,6 +2764,9 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2796,18 +2809,16 @@ func rewriteValueARM_OpARMANDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2837,6 +2848,7 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool { return true } // match: (ANDshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftRL x y [c]) for { x := v_0 @@ -2845,6 +2857,9 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3091,17 +3106,15 @@ func rewriteValueARM_OpARMBICshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -3115,6 +3128,7 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftLL x y [c]) for { x := v_0 @@ -3123,6 +3137,9 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3147,17 +3164,15 @@ func rewriteValueARM_OpARMBICshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -3171,6 +3186,7 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftRA x y [c]) for { x := v_0 @@ -3179,6 +3195,9 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3203,17 +3222,15 @@ func rewriteValueARM_OpARMBICshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -3227,6 +3244,7 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftRL x y [c]) for { x := v_0 @@ -3235,6 +3253,9 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3437,6 +3458,7 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool { return true } // match: (CMNshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftLL x y [c]) for { x := v_0 @@ -3445,6 +3467,9 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3511,6 +3536,7 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool { return true } // match: (CMNshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftRA x y [c]) for { x := v_0 @@ -3519,6 +3545,9 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3585,6 +3614,7 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool { return true } // match: (CMNshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftRL x y [c]) for { x := v_0 @@ -3593,6 +3623,9 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4090,6 +4123,7 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool { return true } // match: (CMPshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftLL x y [c]) for { x := v_0 @@ -4098,6 +4132,9 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4168,6 +4205,7 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool { return true } // match: (CMPshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftRA x y [c]) for { x := v_0 @@ -4176,6 +4214,9 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4246,6 +4287,7 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool { return true } // match: (CMPshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftRL x y [c]) for { x := v_0 @@ -4254,6 +4296,9 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8101,6 +8146,7 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftLLreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftLL x [c]) for { x := v_0 @@ -8108,6 +8154,9 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8135,6 +8184,7 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftRAreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftRA x [c]) for { x := v_0 @@ -8142,6 +8192,9 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8169,6 +8222,7 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftRLreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftRL x [c]) for { x := v_0 @@ -8176,6 +8230,9 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8507,22 +8564,6 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: ( ORshiftLL [c] (SRLconst x [32-c]) x) - // result: (SRRconst [32-c] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(32 - c) - v.AddArg(x) - return true - } // match: (ORshiftLL [8] (BFXU [int32(armBFAuxInt(8, 8))] x) x) // result: (REV16 x) for { @@ -8556,18 +8597,16 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -8597,6 +8636,7 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool { return true } // match: (ORshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftLL x y [c]) for { x := v_0 @@ -8605,6 +8645,9 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8647,18 +8690,16 @@ func rewriteValueARM_OpARMORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -8688,6 +8729,7 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool { return true } // match: (ORshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftRA x y [c]) for { x := v_0 @@ -8696,6 +8738,9 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8738,36 +8783,18 @@ func rewriteValueARM_OpARMORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: ( ORshiftRL [c] (SLLconst x [32-c]) x) - // result: (SRRconst [ c] x) + // match: (ORshiftRL y:(SRLconst x [c]) x [c]) + // result: y for { c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != 32-c { + y := v_0 + if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c { break } - x := v_0.Args[0] + x := y.Args[0] if x != v_1 { break } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } - // match: (ORshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d - // result: y - for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRLconst { - break - } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { - break - } v.copyOf(y) return true } @@ -8795,6 +8822,7 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool { return true } // match: (ORshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftRL x y [c]) for { x := v_0 @@ -8803,6 +8831,9 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9090,6 +9121,7 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool { return true } // match: (RSBSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftLL x y [c]) for { x := v_0 @@ -9098,6 +9130,9 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9164,6 +9199,7 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool { return true } // match: (RSBSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftRA x y [c]) for { x := v_0 @@ -9172,6 +9208,9 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9238,6 +9277,7 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool { return true } // match: (RSBSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftRL x y [c]) for { x := v_0 @@ -9246,6 +9286,9 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9346,17 +9389,15 @@ func rewriteValueARM_OpARMRSBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -9387,6 +9428,7 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool { return true } // match: (RSBshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftLL x y [c]) for { x := v_0 @@ -9395,6 +9437,9 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9437,17 +9482,15 @@ func rewriteValueARM_OpARMRSBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -9478,6 +9521,7 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool { return true } // match: (RSBshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftRA x y [c]) for { x := v_0 @@ -9486,6 +9530,9 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9528,17 +9575,15 @@ func rewriteValueARM_OpARMRSBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -9569,6 +9614,7 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool { return true } // match: (RSBshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftRL x y [c]) for { x := v_0 @@ -9577,6 +9623,9 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9683,6 +9732,7 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool { return true } // match: (RSCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftLL x y [c] flags) for { x := v_0 @@ -9692,6 +9742,9 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -9763,6 +9816,7 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool { return true } // match: (RSCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftRA x y [c] flags) for { x := v_0 @@ -9772,6 +9826,9 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -9843,6 +9900,7 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool { return true } // match: (RSCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftRL x y [c] flags) for { x := v_0 @@ -9852,6 +9910,9 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10166,6 +10227,7 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool { return true } // match: (SBCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftLL x y [c] flags) for { x := v_0 @@ -10175,6 +10237,9 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10246,6 +10311,7 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool { return true } // match: (SBCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftRA x y [c] flags) for { x := v_0 @@ -10255,6 +10321,9 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10326,6 +10395,7 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool { return true } // match: (SBCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftRL x y [c] flags) for { x := v_0 @@ -10335,6 +10405,9 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10346,15 +10419,19 @@ func rewriteValueARM_OpARMSLL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SLL x (MOVWconst [c])) - // result: (SLLconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SLLconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSLLconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -10380,15 +10457,19 @@ func rewriteValueARM_OpARMSRA(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SRA x (MOVWconst [c])) - // result: (SRAconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SRAconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSRAconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -10472,15 +10553,19 @@ func rewriteValueARM_OpARMSRL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SRL x (MOVWconst [c])) - // result: (SRLconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SRLconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSRLconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -10520,6 +10605,24 @@ func rewriteValueARM_OpARMSRLconst(v *Value) bool { } return false } +func rewriteValueARM_OpARMSRR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SRR x (MOVWconst [c])) + // result: (SRRconst x [c&31]) + for { + x := v_0 + if v_1.Op != OpARMMOVWconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpARMSRRconst) + v.AuxInt = int32ToAuxInt(c & 31) + v.AddArg(x) + return true + } + return false +} func rewriteValueARM_OpARMSUB(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -11058,6 +11161,7 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool { return true } // match: (SUBSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftLL x y [c]) for { x := v_0 @@ -11066,6 +11170,9 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11132,6 +11239,7 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool { return true } // match: (SUBSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftRA x y [c]) for { x := v_0 @@ -11140,6 +11248,9 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11206,6 +11317,7 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool { return true } // match: (SUBSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftRL x y [c]) for { x := v_0 @@ -11214,6 +11326,9 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11368,17 +11483,15 @@ func rewriteValueARM_OpARMSUBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11409,6 +11522,7 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool { return true } // match: (SUBshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftLL x y [c]) for { x := v_0 @@ -11417,6 +11531,9 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11459,17 +11576,15 @@ func rewriteValueARM_OpARMSUBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11500,6 +11615,7 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool { return true } // match: (SUBshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftRA x y [c]) for { x := v_0 @@ -11508,6 +11624,9 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11550,17 +11669,15 @@ func rewriteValueARM_OpARMSUBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11591,6 +11708,7 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool { return true } // match: (SUBshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftRL x y [c]) for { x := v_0 @@ -11599,6 +11717,9 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11801,6 +11922,7 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool { return true } // match: (TEQshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftLL x y [c]) for { x := v_0 @@ -11809,6 +11931,9 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11875,6 +12000,7 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool { return true } // match: (TEQshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftRA x y [c]) for { x := v_0 @@ -11883,6 +12009,9 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11949,6 +12078,7 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool { return true } // match: (TEQshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftRL x y [c]) for { x := v_0 @@ -11957,6 +12087,9 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12159,6 +12292,7 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool { return true } // match: (TSTshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftLL x y [c]) for { x := v_0 @@ -12167,6 +12301,9 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12233,6 +12370,7 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool { return true } // match: (TSTshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftRA x y [c]) for { x := v_0 @@ -12241,6 +12379,9 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12307,6 +12448,7 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool { return true } // match: (TSTshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftRL x y [c]) for { x := v_0 @@ -12315,6 +12457,9 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12546,22 +12691,6 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftLL [c] (SRLconst x [32-c]) x) - // result: (SRRconst [32-c] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(32 - c) - v.AddArg(x) - return true - } // match: (XORshiftLL [8] (BFXU [int32(armBFAuxInt(8, 8))] x) x) // result: (REV16 x) for { @@ -12595,17 +12724,15 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -12636,6 +12763,7 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool { return true } // match: (XORshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftLL x y [c]) for { x := v_0 @@ -12644,6 +12772,9 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12686,17 +12817,15 @@ func rewriteValueARM_OpARMXORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -12727,6 +12856,7 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool { return true } // match: (XORshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftRA x y [c]) for { x := v_0 @@ -12735,6 +12865,9 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12777,35 +12910,17 @@ func rewriteValueARM_OpARMXORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRL [c] (SLLconst x [32-c]) x) - // result: (SRRconst [ c] x) + // match: (XORshiftRL (SRLconst x [c]) x [c]) + // result: (MOVWconst [0]) for { c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != 32-c { + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } x := v_0.Args[0] if x != v_1 { break } - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(c) - v.AddArg(x) - return true - } - // match: (XORshiftRL x (SRLconst x [c]) [d]) - // cond: c==d - // result: (MOVWconst [0]) - for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { - break - } v.reset(OpARMMOVWconst) v.AuxInt = int32ToAuxInt(0) return true @@ -12834,6 +12949,7 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool { return true } // match: (XORshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftRL x y [c]) for { x := v_0 @@ -12842,6 +12958,9 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -14901,19 +15020,6 @@ func rewriteValueARM_OpRotateLeft32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (RotateLeft32 x (MOVWconst [c])) - // result: (SRRconst [-c&31] x) - for { - x := v_0 - if v_1.Op != OpARMMOVWconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(-c & 31) - v.AddArg(x) - return true - } // match: (RotateLeft32 x y) // result: (SRR x (RSBconst [0] y)) for { @@ -16951,42 +17057,6 @@ func rewriteBlockARM(b *Block) bool { b.resetWithControl(BlockARMLE, cmp) return true } - // match: (GE (CMP x (RSBconst [0] y))) - // result: (GE (CMN x y)) - for b.Controls[0].Op == OpARMCMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - break - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMGE, v0) - return true - } - // match: (GE (CMN x (RSBconst [0] y))) - // result: (GE (CMP x y)) - for b.Controls[0].Op == OpARMCMN { - v_0 := b.Controls[0] - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - continue - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMGE, v0) - return true - } - break - } // match: (GE (CMPconst [0] l:(SUB x y)) yes no) // cond: l.Uses==1 // result: (GEnoov (CMP x y) yes no) @@ -17867,42 +17937,6 @@ func rewriteBlockARM(b *Block) bool { b.resetWithControl(BlockARMLT, cmp) return true } - // match: (GT (CMP x (RSBconst [0] y))) - // result: (GT (CMN x y)) - for b.Controls[0].Op == OpARMCMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - break - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMGT, v0) - return true - } - // match: (GT (CMN x (RSBconst [0] y))) - // result: (GT (CMP x y)) - for b.Controls[0].Op == OpARMCMN { - v_0 := b.Controls[0] - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - continue - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMGT, v0) - return true - } - break - } // match: (GT (CMPconst [0] l:(SUB x y)) yes no) // cond: l.Uses==1 // result: (GTnoov (CMP x y) yes no) @@ -18874,42 +18908,6 @@ func rewriteBlockARM(b *Block) bool { b.resetWithControl(BlockARMGE, cmp) return true } - // match: (LE (CMP x (RSBconst [0] y))) - // result: (LE (CMN x y)) - for b.Controls[0].Op == OpARMCMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - break - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMLE, v0) - return true - } - // match: (LE (CMN x (RSBconst [0] y))) - // result: (LE (CMP x y)) - for b.Controls[0].Op == OpARMCMN { - v_0 := b.Controls[0] - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - continue - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMLE, v0) - return true - } - break - } // match: (LE (CMPconst [0] l:(SUB x y)) yes no) // cond: l.Uses==1 // result: (LEnoov (CMP x y) yes no) @@ -19790,42 +19788,6 @@ func rewriteBlockARM(b *Block) bool { b.resetWithControl(BlockARMGT, cmp) return true } - // match: (LT (CMP x (RSBconst [0] y))) - // result: (LT (CMN x y)) - for b.Controls[0].Op == OpARMCMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - break - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMLT, v0) - return true - } - // match: (LT (CMN x (RSBconst [0] y))) - // result: (LT (CMP x y)) - for b.Controls[0].Op == OpARMCMN { - v_0 := b.Controls[0] - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - if v_0_1.Op != OpARMRSBconst || auxIntToInt32(v_0_1.AuxInt) != 0 { - continue - } - y := v_0_1.Args[0] - v0 := b.NewValue0(v_0.Pos, OpARMCMP, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARMLT, v0) - return true - } - break - } // match: (LT (CMPconst [0] l:(SUB x y)) yes no) // cond: l.Uses==1 // result: (LTnoov (CMP x y) yes no) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 3cdc4d36cb5a40..d39f69c22fcd92 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -29,6 +29,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ANDshiftRA(v) case OpARM64ANDshiftRL: return rewriteValueARM64_OpARM64ANDshiftRL(v) + case OpARM64ANDshiftRO: + return rewriteValueARM64_OpARM64ANDshiftRO(v) case OpARM64BIC: return rewriteValueARM64_OpARM64BIC(v) case OpARM64BICshiftLL: @@ -37,6 +39,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64BICshiftRA(v) case OpARM64BICshiftRL: return rewriteValueARM64_OpARM64BICshiftRL(v) + case OpARM64BICshiftRO: + return rewriteValueARM64_OpARM64BICshiftRO(v) case OpARM64CMN: return rewriteValueARM64_OpARM64CMN(v) case OpARM64CMNW: @@ -89,6 +93,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64EONshiftRA(v) case OpARM64EONshiftRL: return rewriteValueARM64_OpARM64EONshiftRL(v) + case OpARM64EONshiftRO: + return rewriteValueARM64_OpARM64EONshiftRO(v) case OpARM64Equal: return rewriteValueARM64_OpARM64Equal(v) case OpARM64FADDD: @@ -155,6 +161,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64GreaterThanF(v) case OpARM64GreaterThanU: return rewriteValueARM64_OpARM64GreaterThanU(v) + case OpARM64LDP: + return rewriteValueARM64_OpARM64LDP(v) case OpARM64LessEqual: return rewriteValueARM64_OpARM64LessEqual(v) case OpARM64LessEqualF: @@ -295,6 +303,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64MVNshiftRA(v) case OpARM64MVNshiftRL: return rewriteValueARM64_OpARM64MVNshiftRL(v) + case OpARM64MVNshiftRO: + return rewriteValueARM64_OpARM64MVNshiftRO(v) case OpARM64NEG: return rewriteValueARM64_OpARM64NEG(v) case OpARM64NEGshiftLL: @@ -315,6 +325,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ORNshiftRA(v) case OpARM64ORNshiftRL: return rewriteValueARM64_OpARM64ORNshiftRL(v) + case OpARM64ORNshiftRO: + return rewriteValueARM64_OpARM64ORNshiftRO(v) case OpARM64ORconst: return rewriteValueARM64_OpARM64ORconst(v) case OpARM64ORshiftLL: @@ -323,10 +335,16 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ORshiftRA(v) case OpARM64ORshiftRL: return rewriteValueARM64_OpARM64ORshiftRL(v) - case OpARM64RORWconst: - return rewriteValueARM64_OpARM64RORWconst(v) - case OpARM64RORconst: - return rewriteValueARM64_OpARM64RORconst(v) + case OpARM64ORshiftRO: + return rewriteValueARM64_OpARM64ORshiftRO(v) + case OpARM64REV: + return rewriteValueARM64_OpARM64REV(v) + case OpARM64REVW: + return rewriteValueARM64_OpARM64REVW(v) + case OpARM64ROR: + return rewriteValueARM64_OpARM64ROR(v) + case OpARM64RORW: + return rewriteValueARM64_OpARM64RORW(v) case OpARM64SBCSflags: return rewriteValueARM64_OpARM64SBCSflags(v) case OpARM64SLL: @@ -367,6 +385,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64TSTshiftRA(v) case OpARM64TSTshiftRL: return rewriteValueARM64_OpARM64TSTshiftRL(v) + case OpARM64TSTshiftRO: + return rewriteValueARM64_OpARM64TSTshiftRO(v) case OpARM64UBFIZ: return rewriteValueARM64_OpARM64UBFIZ(v) case OpARM64UBFX: @@ -389,6 +409,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64XORshiftRA(v) case OpARM64XORshiftRL: return rewriteValueARM64_OpARM64XORshiftRL(v) + case OpARM64XORshiftRO: + return rewriteValueARM64_OpARM64XORshiftRO(v) case OpAbs: v.Op = OpARM64FABSD return true @@ -825,9 +847,6 @@ func rewriteValueARM64(v *Value) bool { case OpMul64F: v.Op = OpARM64FMULD return true - case OpMul64uhilo: - v.Op = OpARM64LoweredMuluhilo - return true case OpMul8: v.Op = OpARM64MULW return true @@ -896,6 +915,12 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpPopCount32(v) case OpPopCount64: return rewriteValueARM64_OpPopCount64(v) + case OpPrefetchCache: + return rewriteValueARM64_OpPrefetchCache(v) + case OpPrefetchCacheStreamed: + return rewriteValueARM64_OpPrefetchCacheStreamed(v) + case OpPubBarrier: + return rewriteValueARM64_OpPubBarrier(v) case OpRotateLeft16: return rewriteValueARM64_OpRotateLeft16(v) case OpRotateLeft32: @@ -1038,6 +1063,9 @@ func rewriteValueARM64(v *Value) bool { case OpSubPtr: v.Op = OpARM64SUB return true + case OpTailCall: + v.Op = OpARM64CALLtail + return true case OpTrunc: v.Op = OpARM64FRINTZD return true @@ -1149,8 +1177,6 @@ func rewriteValueARM64_OpARM64ADCSflags(v *Value) bool { func rewriteValueARM64_OpARM64ADD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types // match: (ADD x (MOVDconst [c])) // result: (ADDconst [c] x) for { @@ -1332,287 +1358,6 @@ func rewriteValueARM64_OpARM64ADD(v *Value) bool { } break } - // match: (ADD (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x (NEG y)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt64 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt64 { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 64 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 63 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64ROR) - v0 := b.NewValue0(v.Pos, OpARM64NEG, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true - } - break - } - // match: (ADD (SRL x (ANDconst [63] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SRL || v_0.Type != typ.UInt64 { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt64 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 64 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 63 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64ROR) - v.AddArg2(x, y) - return true - } - break - } - // match: (ADD (SLL x (ANDconst [31] y)) (CSEL0 [cc] (SRL (MOVWUreg x) (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x (NEG y)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt32 { - continue - } - _ = v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpARM64MOVWUreg || x != v_1_0_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64RORW) - v0 := b.NewValue0(v.Pos, OpARM64NEG, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true - } - break - } - // match: (ADD (SRL (MOVWUreg x) (ANDconst [31] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SRL || v_0.Type != typ.UInt32 { - continue - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpARM64MOVWUreg { - continue - } - x := v_0_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64RORW) - v.AddArg2(x, y) - return true - } - break - } return false } func rewriteValueARM64_OpARM64ADDconst(v *Value) bool { @@ -1725,41 +1470,6 @@ func rewriteValueARM64_OpARM64ADDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ADDshiftLL [c] (SRLconst x [64-c]) x) - // result: (RORconst [64-c] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != 64-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(64 - c) - v.AddArg(x) - return true - } - // match: (ADDshiftLL [c] (UBFX [bfc] x) x) - // cond: c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - // result: (RORWconst [32-c] x) - for { - t := v.Type - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64UBFX { - break - } - bfc := auxIntToArm64BitField(v_0.AuxInt) - x := v_0.Args[0] - if x != v_1 || !(c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c)) { - break - } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(32 - c) - v.AddArg(x) - return true - } // match: (ADDshiftLL [8] (UBFX [armBFAuxInt(8, 8)] x) x) // result: (REV16W x) for { @@ -1957,40 +1667,6 @@ func rewriteValueARM64_OpARM64ADDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ADDshiftRL [c] (SLLconst x [64-c]) x) - // result: (RORconst [ c] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 64-c { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - // match: (ADDshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) - // cond: c < 32 && t.Size() == 4 - // result: (RORWconst [c] x) - for { - t := v.Type - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if v_1.Op != OpARM64MOVWUreg || x != v_1.Args[0] || !(c < 32 && t.Size() == 4) { - break - } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } return false } func rewriteValueARM64_OpARM64AND(v *Value) bool { @@ -2103,6 +1779,28 @@ func rewriteValueARM64_OpARM64AND(v *Value) bool { } break } + // match: (AND x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ANDshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64ANDshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } return false } func rewriteValueARM64_OpARM64ANDconst(v *Value) bool { @@ -2228,6 +1926,24 @@ func rewriteValueARM64_OpARM64ANDconst(v *Value) bool { v.AddArg(x) return true } + // match: (ANDconst [c] (UBFX [bfc] x)) + // cond: isARM64BFMask(0, c, 0) + // result: (UBFX [armBFAuxInt(bfc.getARM64BFlsb(), min(bfc.getARM64BFwidth(), arm64BFWidth(c, 0)))] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64UBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) + x := v_0.Args[0] + if !(isARM64BFMask(0, c, 0)) { + break + } + v.reset(OpARM64UBFX) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(bfc.getARM64BFlsb(), min(bfc.getARM64BFwidth(), arm64BFWidth(c, 0)))) + v.AddArg(x) + return true + } return false } func rewriteValueARM64_OpARM64ANDshiftLL(v *Value) bool { @@ -2265,18 +1981,16 @@ func rewriteValueARM64_OpARM64ANDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2319,18 +2033,16 @@ func rewriteValueARM64_OpARM64ANDshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2373,18 +2085,16 @@ func rewriteValueARM64_OpARM64ANDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2392,13 +2102,65 @@ func rewriteValueARM64_OpARM64ANDshiftRL(v *Value) bool { } return false } -func rewriteValueARM64_OpARM64BIC(v *Value) bool { +func rewriteValueARM64_OpARM64ANDshiftRO(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (BIC x (MOVDconst [c])) - // result: (ANDconst [^c] x) + b := v.Block + // match: (ANDshiftRO (MOVDconst [c]) x [d]) + // result: (ANDconst [c] (RORconst x [d])) for { - x := v_0 + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (ANDshiftRO x (MOVDconst [c]) [d]) + // result: (ANDconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (ANDshiftRO y:(RORconst x [c]) x [c]) + // result: y + for { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c { + break + } + x := y.Args[0] + if x != v_1 { + break + } + v.copyOf(y) + return true + } + return false +} +func rewriteValueARM64_OpARM64BIC(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (BIC x (MOVDconst [c])) + // result: (ANDconst [^c] x) + for { + x := v_0 if v_1.Op != OpARM64MOVDconst { break } @@ -2476,6 +2238,25 @@ func rewriteValueARM64_OpARM64BIC(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (BIC x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (BICshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64BICshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool { @@ -2495,17 +2276,15 @@ func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -2531,17 +2310,15 @@ func rewriteValueARM64_OpARM64BICshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -2567,17 +2344,49 @@ func rewriteValueARM64_OpARM64BICshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} +func rewriteValueARM64_OpARM64BICshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (BICshiftRO x (MOVDconst [c]) [d]) + // result: (ANDconst x [^rotateRight64(c, d)]) for { d := auxIntToInt64(v.AuxInt) x := v_0 - if v_1.Op != OpARM64SRLconst { + if v_1.Op != OpARM64MOVDconst { break } c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (BICshiftRO (RORconst x [c]) x [c]) + // result: (MOVDconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -3926,6 +3735,25 @@ func rewriteValueARM64_OpARM64EON(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (EON x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (EONshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64EONshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool { @@ -3945,17 +3773,15 @@ func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (EONshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -3981,17 +3807,15 @@ func rewriteValueARM64_OpARM64EONshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (EONshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4017,17 +3841,49 @@ func rewriteValueARM64_OpARM64EONshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (EONshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [-1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + return false +} +func rewriteValueARM64_OpARM64EONshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (EONshiftRO x (MOVDconst [c]) [d]) + // result: (XORconst x [^rotateRight64(c, d)]) for { d := auxIntToInt64(v.AuxInt) x := v_0 - if v_1.Op != OpARM64SRLconst { + if v_1.Op != OpARM64MOVDconst { break } c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (EONshiftRO (RORconst x [c]) x [c]) + // result: (MOVDconst [-1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4038,6 +3894,97 @@ func rewriteValueARM64_OpARM64EONshiftRL(v *Value) bool { } func rewriteValueARM64_OpARM64Equal(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (Equal (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (Equal (TST x y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64Equal) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (Equal (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (Equal (TSTWconst [int32(c)] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64Equal) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (Equal (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (Equal (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64Equal) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (Equal (CMPconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (Equal (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64Equal) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } // match: (Equal (FlagConstant [fc])) // result: (MOVDconst [b2i(fc.eq())]) for { @@ -5302,43 +5249,134 @@ func rewriteValueARM64_OpARM64FSUBS(v *Value) bool { } func rewriteValueARM64_OpARM64GreaterEqual(v *Value) bool { v_0 := v.Args[0] - // match: (GreaterEqual (FlagConstant [fc])) - // result: (MOVDconst [b2i(fc.ge())]) + b := v.Block + // match: (GreaterEqual (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (GreaterEqual (TST x y)) for { - if v_0.Op != OpARM64FlagConstant { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { break } - fc := auxIntToFlagConstant(v_0.AuxInt) - v.reset(OpARM64MOVDconst) - v.AuxInt = int64ToAuxInt(b2i(fc.ge())) - return true - } - // match: (GreaterEqual (InvertFlags x)) - // result: (LessEqual x) - for { - if v_0.Op != OpARM64InvertFlags { + z := v_0.Args[0] + if z.Op != OpARM64AND { break } - x := v_0.Args[0] - v.reset(OpARM64LessEqual) - v.AddArg(x) + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64GreaterEqual) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) return true } - return false -} -func rewriteValueARM64_OpARM64GreaterEqualF(v *Value) bool { - v_0 := v.Args[0] - // match: (GreaterEqualF (InvertFlags x)) - // result: (LessEqualF x) + // match: (GreaterEqual (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (GreaterEqual (TSTWconst [int32(c)] y)) for { - if v_0.Op != OpARM64InvertFlags { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] - v.reset(OpARM64LessEqualF) - v.AddArg(x) - return true - } + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64GreaterEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (GreaterEqual (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (GreaterEqual (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64GreaterEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (GreaterEqual (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (GreaterEqual (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64GreaterEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (GreaterEqual (FlagConstant [fc])) + // result: (MOVDconst [b2i(fc.ge())]) + for { + if v_0.Op != OpARM64FlagConstant { + break + } + fc := auxIntToFlagConstant(v_0.AuxInt) + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(b2i(fc.ge())) + return true + } + // match: (GreaterEqual (InvertFlags x)) + // result: (LessEqual x) + for { + if v_0.Op != OpARM64InvertFlags { + break + } + x := v_0.Args[0] + v.reset(OpARM64LessEqual) + v.AddArg(x) + return true + } + return false +} +func rewriteValueARM64_OpARM64GreaterEqualF(v *Value) bool { + v_0 := v.Args[0] + // match: (GreaterEqualF (InvertFlags x)) + // result: (LessEqualF x) + for { + if v_0.Op != OpARM64InvertFlags { + break + } + x := v_0.Args[0] + v.reset(OpARM64LessEqualF) + v.AddArg(x) + return true + } return false } func rewriteValueARM64_OpARM64GreaterEqualU(v *Value) bool { @@ -5369,6 +5407,97 @@ func rewriteValueARM64_OpARM64GreaterEqualU(v *Value) bool { } func rewriteValueARM64_OpARM64GreaterThan(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (GreaterThan (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (GreaterThan (TST x y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64GreaterThan) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (GreaterThan (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (GreaterThan (TSTWconst [int32(c)] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64GreaterThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (GreaterThan (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (GreaterThan (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64GreaterThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (GreaterThan (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (GreaterThan (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64GreaterThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } // match: (GreaterThan (FlagConstant [fc])) // result: (MOVDconst [b2i(fc.gt())]) for { @@ -5434,8 +5563,149 @@ func rewriteValueARM64_OpARM64GreaterThanU(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64LDP(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + // match: (LDP [off1] {sym} (ADDconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) + // result: (LDP [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpARM64ADDconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)) { + break + } + v.reset(OpARM64LDP) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (LDP [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_shared) + // result: (LDP [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpARM64MOVDaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)) { + break + } + v.reset(OpARM64LDP) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} func rewriteValueARM64_OpARM64LessEqual(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (LessEqual (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (LessEqual (TST x y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64LessEqual) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (LessEqual (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (LessEqual (TSTWconst [int32(c)] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64LessEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (LessEqual (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (LessEqual (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64LessEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (LessEqual (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (LessEqual (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64LessEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } // match: (LessEqual (FlagConstant [fc])) // result: (MOVDconst [b2i(fc.le())]) for { @@ -5503,19 +5773,110 @@ func rewriteValueARM64_OpARM64LessEqualU(v *Value) bool { } func rewriteValueARM64_OpARM64LessThan(v *Value) bool { v_0 := v.Args[0] - // match: (LessThan (FlagConstant [fc])) - // result: (MOVDconst [b2i(fc.lt())]) + b := v.Block + // match: (LessThan (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (LessThan (TST x y)) for { - if v_0.Op != OpARM64FlagConstant { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { break } - fc := auxIntToFlagConstant(v_0.AuxInt) - v.reset(OpARM64MOVDconst) - v.AuxInt = int64ToAuxInt(b2i(fc.lt())) - return true - } - // match: (LessThan (InvertFlags x)) - // result: (GreaterThan x) + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64LessThan) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (LessThan (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (LessThan (TSTWconst [int32(c)] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64LessThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (LessThan (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (LessThan (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64LessThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (LessThan (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (LessThan (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64LessThan) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (LessThan (FlagConstant [fc])) + // result: (MOVDconst [b2i(fc.lt())]) + for { + if v_0.Op != OpARM64FlagConstant { + break + } + fc := auxIntToFlagConstant(v_0.AuxInt) + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(b2i(fc.lt())) + return true + } + // match: (LessThan (InvertFlags x)) + // result: (GreaterThan x) for { if v_0.Op != OpARM64InvertFlags { break @@ -7126,12 +7487,154 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(int64(uint8(c))) return true } - // match: (MOVBUreg x) - // cond: x.Type.IsBoolean() + // match: (MOVBUreg x:(Equal _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64Equal { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(NotEqual _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64NotEqual { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessThan _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessThan { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessThanU _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessThanU { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessThanF _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessThanF { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessEqual _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessEqual { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessEqualU _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessEqualU { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(LessEqualF _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64LessEqualF { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterThan _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64GreaterThan { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterThanU _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64GreaterThanU { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterThanF _)) // result: (MOVDreg x) for { x := v_0 - if !(x.Type.IsBoolean()) { + if x.Op != OpARM64GreaterThanF { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterEqual _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64GreaterEqual { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterEqualU _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64GreaterEqualU { + break + } + v.reset(OpARM64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(GreaterEqualF _)) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpARM64GreaterEqualF { break } v.reset(OpARM64MOVDreg) @@ -7153,37 +7656,54 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVBUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<8-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) + // match: (MOVBUreg (SLLconst [lc] x)) + // cond: lc < 8 + // result: (UBFIZ [armBFAuxInt(lc, 8-lc)] x) for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, sc)) { + if !(lc < 8) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 8-lc)) v.AddArg(x) return true } - // match: (MOVBUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<8-1, 0) - // result: (UBFX [armBFAuxInt(sc, 8)] x) + // match: (MOVBUreg (SRLconst [rc] x)) + // cond: rc < 8 + // result: (UBFX [armBFAuxInt(rc, 8)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(rc < 8) { + break + } + v.reset(OpARM64UBFX) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8)) + v.AddArg(x) + return true + } + // match: (MOVBUreg (UBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 8 + // result: (UBFX [bfc] x) + for { + if v_0.Op != OpARM64UBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, 0)) { + if !(bfc.getARM64BFwidth() <= 8) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8)) + v.AuxInt = arm64BitFieldToAuxInt(bfc) v.AddArg(x) return true } @@ -7401,6 +7921,23 @@ func rewriteValueARM64_OpARM64MOVBreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVBreg (SBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 8 + // result: (SBFX [bfc] x) + for { + if v_0.Op != OpARM64SBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) + x := v_0.Args[0] + if !(bfc.getARM64BFwidth() <= 8) { + break + } + v.reset(OpARM64SBFX) + v.AuxInt = arm64BitFieldToAuxInt(bfc) + v.AddArg(x) + return true + } return false } func rewriteValueARM64_OpARM64MOVBstore(v *Value) bool { @@ -10665,37 +11202,54 @@ func rewriteValueARM64_OpARM64MOVHUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVHUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<16-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) + // match: (MOVHUreg (SLLconst [lc] x)) + // cond: lc < 16 + // result: (UBFIZ [armBFAuxInt(lc, 16-lc)] x) for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, sc)) { + if !(lc < 16) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 16-lc)) v.AddArg(x) return true } - // match: (MOVHUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<16-1, 0) - // result: (UBFX [armBFAuxInt(sc, 16)] x) + // match: (MOVHUreg (SRLconst [rc] x)) + // cond: rc < 16 + // result: (UBFX [armBFAuxInt(rc, 16)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(rc < 16) { + break + } + v.reset(OpARM64UBFX) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16)) + v.AddArg(x) + return true + } + // match: (MOVHUreg (UBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 16 + // result: (UBFX [bfc] x) + for { + if v_0.Op != OpARM64UBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, 0)) { + if !(bfc.getARM64BFwidth() <= 16) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16)) + v.AuxInt = arm64BitFieldToAuxInt(bfc) v.AddArg(x) return true } @@ -11096,6 +11650,23 @@ func rewriteValueARM64_OpARM64MOVHreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVHreg (SBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 16 + // result: (SBFX [bfc] x) + for { + if v_0.Op != OpARM64SBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) + x := v_0.Args[0] + if !(bfc.getARM64BFwidth() <= 16) { + break + } + v.reset(OpARM64SBFX) + v.AuxInt = arm64BitFieldToAuxInt(bfc) + v.AddArg(x) + return true + } return false } func rewriteValueARM64_OpARM64MOVHstore(v *Value) bool { @@ -12777,37 +13348,54 @@ func rewriteValueARM64_OpARM64MOVWUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVWUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<32-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) - for { + // match: (MOVWUreg (SLLconst [lc] x)) + // cond: lc < 32 + // result: (UBFIZ [armBFAuxInt(lc, 32-lc)] x) + for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, sc)) { + if !(lc < 32) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 32-lc)) v.AddArg(x) return true } - // match: (MOVWUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<32-1, 0) - // result: (UBFX [armBFAuxInt(sc, 32)] x) + // match: (MOVWUreg (SRLconst [rc] x)) + // cond: rc < 32 + // result: (UBFX [armBFAuxInt(rc, 32)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(rc < 32) { + break + } + v.reset(OpARM64UBFX) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32)) + v.AddArg(x) + return true + } + // match: (MOVWUreg (UBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 32 + // result: (UBFX [bfc] x) + for { + if v_0.Op != OpARM64UBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, 0)) { + if !(bfc.getARM64BFwidth() <= 32) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32)) + v.AuxInt = arm64BitFieldToAuxInt(bfc) v.AddArg(x) return true } @@ -13266,6 +13854,23 @@ func rewriteValueARM64_OpARM64MOVWreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVWreg (SBFX [bfc] x)) + // cond: bfc.getARM64BFwidth() <= 32 + // result: (SBFX [bfc] x) + for { + if v_0.Op != OpARM64SBFX { + break + } + bfc := auxIntToArm64BitField(v_0.AuxInt) + x := v_0.Args[0] + if !(bfc.getARM64BFwidth() <= 32) { + break + } + v.reset(OpARM64SBFX) + v.AuxInt = arm64BitFieldToAuxInt(bfc) + v.AddArg(x) + return true + } return false } func rewriteValueARM64_OpARM64MOVWstore(v *Value) bool { @@ -15502,6 +16107,24 @@ func rewriteValueARM64_OpARM64MVN(v *Value) bool { v.AddArg(y) return true } + // match: (MVN x:(RORconst [c] y)) + // cond: clobberIfDead(x) + // result: (MVNshiftRO [c] y) + for { + x := v_0 + if x.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(clobberIfDead(x)) { + break + } + v.reset(OpARM64MVNshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(y) + return true + } return false } func rewriteValueARM64_OpARM64MVNshiftLL(v *Value) bool { @@ -15552,6 +16175,22 @@ func rewriteValueARM64_OpARM64MVNshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64MVNshiftRO(v *Value) bool { + v_0 := v.Args[0] + // match: (MVNshiftRO (MOVDconst [c]) [d]) + // result: (MOVDconst [^rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + return true + } + return false +} func rewriteValueARM64_OpARM64NEG(v *Value) bool { v_0 := v.Args[0] // match: (NEG (MUL x y)) @@ -15578,6 +16217,16 @@ func rewriteValueARM64_OpARM64NEG(v *Value) bool { v.AddArg2(x, y) return true } + // match: (NEG (NEG x)) + // result: x + for { + if v_0.Op != OpARM64NEG { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } // match: (NEG (MOVDconst [c])) // result: (MOVDconst [-c]) for { @@ -15695,6 +16344,97 @@ func rewriteValueARM64_OpARM64NEGshiftRL(v *Value) bool { } func rewriteValueARM64_OpARM64NotEqual(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (NotEqual (CMPconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (NotEqual (TST x y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64NotEqual) + v0 := b.NewValue0(v.Pos, OpARM64TST, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (NotEqual (CMPWconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (NotEqual (TSTWconst [int32(c)] y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64NotEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (NotEqual (CMPWconst [0] z:(AND x y))) + // cond: z.Uses == 1 + // result: (NotEqual (TSTW x y)) + for { + if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 { + break + } + z := v_0.Args[0] + if z.Op != OpARM64AND { + break + } + y := z.Args[1] + x := z.Args[0] + if !(z.Uses == 1) { + break + } + v.reset(OpARM64NotEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTW, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (NotEqual (CMPconst [0] x:(ANDconst [c] y))) + // cond: x.Uses == 1 + // result: (NotEqual (TSTconst [c] y)) + for { + if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v.reset(OpARM64NotEqual) + v0 := b.NewValue0(v.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + v.AddArg(v0) + return true + } // match: (NotEqual (FlagConstant [fc])) // result: (MOVDconst [b2i(fc.ne())]) for { @@ -15723,7 +16463,6 @@ func rewriteValueARM64_OpARM64OR(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types // match: (OR x (MOVDconst [c])) // result: (ORconst [c] x) for { @@ -15831,415 +16570,156 @@ func rewriteValueARM64_OpARM64OR(v *Value) bool { } break } - // match: (OR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x (NEG y)) + // match: (OR x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ORshiftRO x0 y [c]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt64 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt64 { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { + v.reset(OpARM64ORshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } + // match: (OR (UBFIZ [bfc] x) (ANDconst [ac] y)) + // cond: ac == ^((1< x (ANDconst [63] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x y) + // match: (OR (UBFX [bfc] x) (ANDconst [ac] y)) + // cond: ac == ^(1< o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24] y0:(MOVDnop x0:(MOVBUload [i3] {s} p mem))) y1:(MOVDnop x1:(MOVBUload [i2] {s} p mem))) y2:(MOVDnop x2:(MOVBUload [i1] {s} p mem))) y3:(MOVDnop x3:(MOVBUload [i0] {s} p mem))) + // cond: i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil && clobber(x0, x1, x2, x3, y0, y1, y2, y3, o0, o1, s0) + // result: @mergePoint(b,x0,x1,x2,x3) (MOVWUload {s} (OffPtr [int64(i0)] p) mem) + for { + t := v.Type + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + o0 := v_0 + if o0.Op != OpARM64ORshiftLL || auxIntToInt64(o0.AuxInt) != 8 { continue } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { + _ = o0.Args[1] + o1 := o0.Args[0] + if o1.Op != OpARM64ORshiftLL || auxIntToInt64(o1.AuxInt) != 16 { continue } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { + _ = o1.Args[1] + s0 := o1.Args[0] + if s0.Op != OpARM64SLLconst || auxIntToInt64(s0.AuxInt) != 24 { continue } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { + y0 := s0.Args[0] + if y0.Op != OpARM64MOVDnop { continue } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { + x0 := y0.Args[0] + if x0.Op != OpARM64MOVBUload { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { + i3 := auxIntToInt32(x0.AuxInt) + s := auxToSym(x0.Aux) + mem := x0.Args[1] + p := x0.Args[0] + y1 := o1.Args[1] + if y1.Op != OpARM64MOVDnop { continue } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { + x1 := y1.Args[0] + if x1.Op != OpARM64MOVBUload { continue } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 64 { + i2 := auxIntToInt32(x1.AuxInt) + if auxToSym(x1.Aux) != s { continue } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 63 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { + _ = x1.Args[1] + if p != x1.Args[0] || mem != x1.Args[1] { continue } - v.reset(OpARM64ROR) - v.AddArg2(x, y) - return true - } - break - } - // match: (OR (SLL x (ANDconst [31] y)) (CSEL0 [cc] (SRL (MOVWUreg x) (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x (NEG y)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { + y2 := o0.Args[1] + if y2.Op != OpARM64MOVDnop { continue } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { + x2 := y2.Args[0] + if x2.Op != OpARM64MOVBUload { continue } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { + i1 := auxIntToInt32(x2.AuxInt) + if auxToSym(x2.Aux) != s { continue } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { + _ = x2.Args[1] + if p != x2.Args[0] || mem != x2.Args[1] { continue } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt32 { + y3 := v_1 + if y3.Op != OpARM64MOVDnop { continue } - _ = v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpARM64MOVWUreg || x != v_1_0_0.Args[0] { + x3 := y3.Args[0] + if x3.Op != OpARM64MOVBUload { continue } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { + i0 := auxIntToInt32(x3.AuxInt) + if auxToSym(x3.Aux) != s { continue } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64RORW) - v0 := b.NewValue0(v.Pos, OpARM64NEG, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true - } - break - } - // match: (OR (SRL (MOVWUreg x) (ANDconst [31] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SRL || v_0.Type != typ.UInt32 { - continue - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpARM64MOVWUreg { - continue - } - x := v_0_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64RORW) - v.AddArg2(x, y) - return true - } - break - } - // match: (OR (UBFIZ [bfc] x) (ANDconst [ac] y)) - // cond: ac == ^((1< o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24] y0:(MOVDnop x0:(MOVBUload [i3] {s} p mem))) y1:(MOVDnop x1:(MOVBUload [i2] {s} p mem))) y2:(MOVDnop x2:(MOVBUload [i1] {s} p mem))) y3:(MOVDnop x3:(MOVBUload [i0] {s} p mem))) - // cond: i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil && clobber(x0, x1, x2, x3, y0, y1, y2, y3, o0, o1, s0) - // result: @mergePoint(b,x0,x1,x2,x3) (MOVWUload {s} (OffPtr [int64(i0)] p) mem) - for { - t := v.Type - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - o0 := v_0 - if o0.Op != OpARM64ORshiftLL || auxIntToInt64(o0.AuxInt) != 8 { - continue - } - _ = o0.Args[1] - o1 := o0.Args[0] - if o1.Op != OpARM64ORshiftLL || auxIntToInt64(o1.AuxInt) != 16 { - continue - } - _ = o1.Args[1] - s0 := o1.Args[0] - if s0.Op != OpARM64SLLconst || auxIntToInt64(s0.AuxInt) != 24 { - continue - } - y0 := s0.Args[0] - if y0.Op != OpARM64MOVDnop { - continue - } - x0 := y0.Args[0] - if x0.Op != OpARM64MOVBUload { - continue - } - i3 := auxIntToInt32(x0.AuxInt) - s := auxToSym(x0.Aux) - mem := x0.Args[1] - p := x0.Args[0] - y1 := o1.Args[1] - if y1.Op != OpARM64MOVDnop { - continue - } - x1 := y1.Args[0] - if x1.Op != OpARM64MOVBUload { - continue - } - i2 := auxIntToInt32(x1.AuxInt) - if auxToSym(x1.Aux) != s { - continue - } - _ = x1.Args[1] - if p != x1.Args[0] || mem != x1.Args[1] { - continue - } - y2 := o0.Args[1] - if y2.Op != OpARM64MOVDnop { - continue - } - x2 := y2.Args[0] - if x2.Op != OpARM64MOVBUload { - continue - } - i1 := auxIntToInt32(x2.AuxInt) - if auxToSym(x2.Aux) != s { - continue - } - _ = x2.Args[1] - if p != x2.Args[0] || mem != x2.Args[1] { - continue - } - y3 := v_1 - if y3.Op != OpARM64MOVDnop { - continue - } - x3 := y3.Args[0] - if x3.Op != OpARM64MOVBUload { - continue - } - i0 := auxIntToInt32(x3.AuxInt) - if auxToSym(x3.Aux) != s { - continue - } - _ = x3.Args[1] - if p != x3.Args[0] || mem != x3.Args[1] || !(i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0, x1, x2, x3, y0, y1, y2, y3, o0, o1, s0)) { + _ = x3.Args[1] + if p != x3.Args[0] || mem != x3.Args[1] || !(i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0, x1, x2, x3, y0, y1, y2, y3, o0, o1, s0)) { continue } b = mergePoint(b, x0, x1, x2, x3) @@ -17800,6 +18280,25 @@ func rewriteValueARM64_OpARM64ORN(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (ORN x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ORNshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64ORNshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool { @@ -17819,17 +18318,15 @@ func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -17855,17 +18352,15 @@ func rewriteValueARM64_OpARM64ORNshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -17891,17 +18386,49 @@ func rewriteValueARM64_OpARM64ORNshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [-1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + return false +} +func rewriteValueARM64_OpARM64ORNshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ORNshiftRO x (MOVDconst [c]) [d]) + // result: (ORconst x [^rotateRight64(c, d)]) for { d := auxIntToInt64(v.AuxInt) x := v_0 - if v_1.Op != OpARM64SRLconst { + if v_1.Op != OpARM64MOVDconst { break } c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (ORNshiftRO (RORconst x [c]) x [c]) + // result: (MOVDconst [-1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { + break + } + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18014,69 +18541,32 @@ func rewriteValueARM64_OpARM64ORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) return true } - // match: ( ORshiftLL [c] (SRLconst x [64-c]) x) - // result: (RORconst [64-c] x) + // match: (ORshiftLL [8] (UBFX [armBFAuxInt(8, 8)] x) x) + // result: (REV16W x) for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != 64-c { + if v.Type != typ.UInt16 || auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || v_0.Type != typ.UInt16 || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 8) { break } x := v_0.Args[0] if x != v_1 { break } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(64 - c) - v.AddArg(x) - return true - } - // match: ( ORshiftLL [c] (UBFX [bfc] x) x) - // cond: c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - // result: (RORWconst [32-c] x) - for { - t := v.Type - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64UBFX { - break - } - bfc := auxIntToArm64BitField(v_0.AuxInt) - x := v_0.Args[0] - if x != v_1 || !(c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c)) { - break - } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(32 - c) - v.AddArg(x) - return true - } - // match: (ORshiftLL [8] (UBFX [armBFAuxInt(8, 8)] x) x) - // result: (REV16W x) - for { - if v.Type != typ.UInt16 || auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || v_0.Type != typ.UInt16 || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 8) { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARM64REV16W) + v.reset(OpARM64REV16W) v.AddArg(x) return true } @@ -19694,18 +20184,16 @@ func rewriteValueARM64_OpARM64ORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -19748,55 +20236,19 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRL y:(SRLconst x [c]) x [c]) // result: y - for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRLconst { - break - } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { - break - } - v.copyOf(y) - return true - } - // match: ( ORshiftRL [c] (SLLconst x [64-c]) x) - // result: (RORconst [ c] x) for { c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 64-c { + y := v_0 + if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c { break } - x := v_0.Args[0] + x := y.Args[0] if x != v_1 { break } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - // match: ( ORshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) - // cond: c < 32 && t.Size() == 4 - // result: (RORWconst [c] x) - for { - t := v.Type - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 32-c { - break - } - x := v_0.Args[0] - if v_1.Op != OpARM64MOVWUreg || x != v_1.Args[0] || !(c < 32 && t.Size() == 4) { - break - } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) + v.copyOf(y) return true } // match: (ORshiftRL [rc] (ANDconst [ac] x) (SLLconst [lc] y)) @@ -19847,37 +20299,117 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool { } return false } -func rewriteValueARM64_OpARM64RORWconst(v *Value) bool { +func rewriteValueARM64_OpARM64ORshiftRO(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (RORWconst [c] (RORWconst [d] x)) - // result: (RORWconst [(c+d)&31] x) + b := v.Block + // match: (ORshiftRO (MOVDconst [c]) x [d]) + // result: (ORconst [c] (RORconst x [d])) for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64RORWconst { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { break } - d := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt((c + d) & 31) + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (ORshiftRO x (MOVDconst [c]) [d]) + // result: (ORconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) v.AddArg(x) return true } + // match: (ORshiftRO y:(RORconst x [c]) x [c]) + // result: y + for { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c { + break + } + x := y.Args[0] + if x != v_1 { + break + } + v.copyOf(y) + return true + } return false } -func rewriteValueARM64_OpARM64RORconst(v *Value) bool { +func rewriteValueARM64_OpARM64REV(v *Value) bool { v_0 := v.Args[0] - // match: (RORconst [c] (RORconst [d] x)) - // result: (RORconst [(c+d)&63] x) + // match: (REV (REV p)) + // result: p for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64RORconst { + if v_0.Op != OpARM64REV { break } - d := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] + p := v_0.Args[0] + v.copyOf(p) + return true + } + return false +} +func rewriteValueARM64_OpARM64REVW(v *Value) bool { + v_0 := v.Args[0] + // match: (REVW (REVW p)) + // result: p + for { + if v_0.Op != OpARM64REVW { + break + } + p := v_0.Args[0] + v.copyOf(p) + return true + } + return false +} +func rewriteValueARM64_OpARM64ROR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROR x (MOVDconst [c])) + // result: (RORconst x [c&63]) + for { + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt((c + d) & 63) + v.AuxInt = int64ToAuxInt(c & 63) + v.AddArg(x) + return true + } + return false +} +func rewriteValueARM64_OpARM64RORW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORW x (MOVDconst [c])) + // result: (RORWconst x [c&31]) + for { + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64RORWconst) + v.AuxInt = int64ToAuxInt(c & 31) v.AddArg(x) return true } @@ -19952,6 +20484,18 @@ func rewriteValueARM64_OpARM64SLL(v *Value) bool { v.AddArg(x) return true } + // match: (SLL x (ANDconst [63] y)) + // result: (SLL x y) + for { + x := v_0 + if v_1.Op != OpARM64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { + break + } + y := v_1.Args[0] + v.reset(OpARM64SLL) + v.AddArg2(x, y) + return true + } return false } func rewriteValueARM64_OpARM64SLLconst(v *Value) bool { @@ -19985,72 +20529,99 @@ func rewriteValueARM64_OpARM64SLLconst(v *Value) bool { v.AddArg(x) return true } - // match: (SLLconst [sc] (ANDconst [ac] x)) - // cond: isARM64BFMask(sc, ac, 0) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) + // match: (SLLconst [lc] (MOVWreg x)) + // result: (SBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64ANDconst { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVWreg { break } - ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, ac, 0)) { + v.reset(OpARM64SBFIZ) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc))) + v.AddArg(x) + return true + } + // match: (SLLconst [lc] (MOVHreg x)) + // result: (SBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) + for { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVHreg { break } - v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0))) + x := v_0.Args[0] + v.reset(OpARM64SBFIZ) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVWUreg x)) - // cond: isARM64BFMask(sc, 1<<32-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 32)] x) + // match: (SLLconst [lc] (MOVBreg x)) + // result: (SBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64MOVWUreg { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVBreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, 0)) { + v.reset(OpARM64SBFIZ) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc))) + v.AddArg(x) + return true + } + // match: (SLLconst [lc] (MOVWUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) + for { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVWUreg { break } + x := v_0.Args[0] v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVHUreg x)) - // cond: isARM64BFMask(sc, 1<<16-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 16)] x) + // match: (SLLconst [lc] (MOVHUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) + lc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVHUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, 0)) { + v.reset(OpARM64UBFIZ) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc))) + v.AddArg(x) + return true + } + // match: (SLLconst [lc] (MOVBUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) + for { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVBUreg { break } + x := v_0.Args[0] v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVBUreg x)) - // cond: isARM64BFMask(sc, 1<<8-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 8)] x) + // match: (SLLconst [sc] (ANDconst [ac] x)) + // cond: isARM64BFMask(sc, ac, 0) + // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) for { sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64MOVBUreg { + if v_0.Op != OpARM64ANDconst { break } + ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, 0)) { + if !(isARM64BFMask(sc, ac, 0)) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0))) v.AddArg(x) return true } @@ -20090,6 +20661,18 @@ func rewriteValueARM64_OpARM64SRA(v *Value) bool { v.AddArg(x) return true } + // match: (SRA x (ANDconst [63] y)) + // result: (SRA x y) + for { + x := v_0 + if v_1.Op != OpARM64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { + break + } + y := v_1.Args[0] + v.reset(OpARM64SRA) + v.AddArg2(x, y) + return true + } return false } func rewriteValueARM64_OpARM64SRAconst(v *Value) bool { @@ -20247,6 +20830,18 @@ func rewriteValueARM64_OpARM64SRL(v *Value) bool { v.AddArg(x) return true } + // match: (SRL x (ANDconst [63] y)) + // result: (SRL x y) + for { + x := v_0 + if v_1.Op != OpARM64ANDconst || auxIntToInt64(v_1.AuxInt) != 63 { + break + } + y := v_1.Args[0] + v.reset(OpARM64SRL) + v.AddArg2(x, y) + return true + } return false } func rewriteValueARM64_OpARM64SRLconst(v *Value) bool { @@ -20343,90 +20938,90 @@ func rewriteValueARM64_OpARM64SRLconst(v *Value) bool { v.AddArg(x) return true } - // match: (SRLconst [sc] (ANDconst [ac] x)) - // cond: isARM64BFMask(sc, ac, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) + // match: (SRLconst [rc] (SLLconst [lc] x)) + // cond: lc < rc + // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64ANDconst { + rc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst { break } - ac := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, ac, sc)) { + if !(lc < rc) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVWUreg x)) - // cond: isARM64BFMask(sc, 1<<32-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) + // match: (SRLconst [rc] (MOVWUreg x)) + // cond: rc < 32 + // result: (UBFX [armBFAuxInt(rc, 32-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVWUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, sc)) { + if !(rc < 32) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVHUreg x)) - // cond: isARM64BFMask(sc, 1<<16-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) + // match: (SRLconst [rc] (MOVHUreg x)) + // cond: rc < 16 + // result: (UBFX [armBFAuxInt(rc, 16-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVHUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, sc)) { + if !(rc < 16) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVBUreg x)) - // cond: isARM64BFMask(sc, 1<<8-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) + // match: (SRLconst [rc] (MOVBUreg x)) + // cond: rc < 8 + // result: (UBFX [armBFAuxInt(rc, 8-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVBUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, sc)) { + if !(rc < 8) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8-rc)) v.AddArg(x) return true } - // match: (SRLconst [rc] (SLLconst [lc] x)) - // cond: lc < rc - // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) + // match: (SRLconst [sc] (ANDconst [ac] x)) + // cond: isARM64BFMask(sc, ac, sc) + // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) for { - rc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst { + sc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64ANDconst { break } - lc := auxIntToInt64(v_0.AuxInt) + ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(lc < rc) { + if !(isARM64BFMask(sc, ac, sc)) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc))) v.AddArg(x) return true } @@ -20836,17 +21431,15 @@ func rewriteValueARM64_OpARM64SUBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -20872,17 +21465,15 @@ func rewriteValueARM64_OpARM64SUBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -20908,17 +21499,15 @@ func rewriteValueARM64_OpARM64SUBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -21012,6 +21601,28 @@ func rewriteValueARM64_OpARM64TST(v *Value) bool { } break } + // match: (TST x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (TSTshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64TSTshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } return false } func rewriteValueARM64_OpARM64TSTW(v *Value) bool { @@ -21178,6 +21789,43 @@ func rewriteValueARM64_OpARM64TSTshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64TSTshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (TSTshiftRO (MOVDconst [c]) x [d]) + // result: (TSTconst [c] (RORconst x [d])) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64TSTconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (TSTshiftRO x (MOVDconst [c]) [d]) + // result: (TSTconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64TSTconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + return false +} func rewriteValueARM64_OpARM64UBFIZ(v *Value) bool { v_0 := v.Args[0] // match: (UBFIZ [bfc] (SLLconst [sc] x)) @@ -21202,6 +21850,24 @@ func rewriteValueARM64_OpARM64UBFIZ(v *Value) bool { } func rewriteValueARM64_OpARM64UBFX(v *Value) bool { v_0 := v.Args[0] + // match: (UBFX [bfc] (ANDconst [c] x)) + // cond: isARM64BFMask(0, c, 0) && bfc.getARM64BFlsb() + bfc.getARM64BFwidth() <= arm64BFWidth(c, 0) + // result: (UBFX [bfc] x) + for { + bfc := auxIntToArm64BitField(v.AuxInt) + if v_0.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(isARM64BFMask(0, c, 0) && bfc.getARM64BFlsb()+bfc.getARM64BFwidth() <= arm64BFWidth(c, 0)) { + break + } + v.reset(OpARM64UBFX) + v.AuxInt = arm64BitFieldToAuxInt(bfc) + v.AddArg(x) + return true + } // match: (UBFX [bfc] (SRLconst [sc] x)) // cond: sc+bfc.getARM64BFwidth()+bfc.getARM64BFlsb() < 64 // result: (UBFX [armBFAuxInt(bfc.getARM64BFlsb()+sc, bfc.getARM64BFwidth())] x) @@ -21527,8 +22193,6 @@ func rewriteValueARM64_OpARM64UMODW(v *Value) bool { func rewriteValueARM64_OpARM64XOR(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types // match: (XOR x (MOVDconst [c])) // result: (XORconst [c] x) for { @@ -21637,283 +22301,24 @@ func rewriteValueARM64_OpARM64XOR(v *Value) bool { } break } - // match: (XOR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x (NEG y)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt64 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt64 { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 64 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 63 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64ROR) - v0 := b.NewValue0(v.Pos, OpARM64NEG, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true - } - break - } - // match: (XOR (SRL x (ANDconst [63] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) - // cond: cc == OpARM64LessThanU - // result: (ROR x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SRL || v_0.Type != typ.UInt64 { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt64 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 64 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 63 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 64 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 63 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64ROR) - v.AddArg2(x, y) - return true - } - break - } - // match: (XOR (SLL x (ANDconst [31] y)) (CSEL0 [cc] (SRL (MOVWUreg x) (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x (NEG y)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SLL { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SRL || v_1_0.Type != typ.UInt32 { - continue - } - _ = v_1_0.Args[1] - v_1_0_0 := v_1_0.Args[0] - if v_1_0_0.Op != OpARM64MOVWUreg || x != v_1_0_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { - continue - } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { - continue - } - v.reset(OpARM64RORW) - v0 := b.NewValue0(v.Pos, OpARM64NEG, t) - v0.AddArg(y) - v.AddArg2(x, v0) - return true - } - break - } - // match: (XOR (SRL (MOVWUreg x) (ANDconst [31] y)) (CSEL0 [cc] (SLL x (SUB (MOVDconst [32]) (ANDconst [31] y))) (CMPconst [64] (SUB (MOVDconst [32]) (ANDconst [31] y))))) - // cond: cc == OpARM64LessThanU - // result: (RORW x y) + // match: (XOR x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (XORshiftRO x0 y [c]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpARM64SRL || v_0.Type != typ.UInt32 { - continue - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpARM64MOVWUreg { - continue - } - x := v_0_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpARM64ANDconst { - continue - } - t := v_0_1.Type - if auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpARM64CSEL0 || v_1.Type != typ.UInt32 { - continue - } - cc := auxIntToOp(v_1.AuxInt) - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpARM64SLL { - continue - } - _ = v_1_0.Args[1] - if x != v_1_0.Args[0] { - continue - } - v_1_0_1 := v_1_0.Args[1] - if v_1_0_1.Op != OpARM64SUB || v_1_0_1.Type != t { - continue - } - _ = v_1_0_1.Args[1] - v_1_0_1_0 := v_1_0_1.Args[0] - if v_1_0_1_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_0_1_0.AuxInt) != 32 { - continue - } - v_1_0_1_1 := v_1_0_1.Args[1] - if v_1_0_1_1.Op != OpARM64ANDconst || v_1_0_1_1.Type != t || auxIntToInt64(v_1_0_1_1.AuxInt) != 31 || y != v_1_0_1_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpARM64CMPconst || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpARM64SUB || v_1_1_0.Type != t { - continue - } - _ = v_1_1_0.Args[1] - v_1_1_0_0 := v_1_1_0.Args[0] - if v_1_1_0_0.Op != OpARM64MOVDconst || auxIntToInt64(v_1_1_0_0.AuxInt) != 32 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { continue } - v_1_1_0_1 := v_1_1_0.Args[1] - if v_1_1_0_1.Op != OpARM64ANDconst || v_1_1_0_1.Type != t || auxIntToInt64(v_1_1_0_1.AuxInt) != 31 || y != v_1_1_0_1.Args[0] || !(cc == OpARM64LessThanU) { + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { continue } - v.reset(OpARM64RORW) - v.AddArg2(x, y) + v.reset(OpARM64XORshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) return true } break @@ -22007,81 +22412,44 @@ func rewriteValueARM64_OpARM64XORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) v.AuxInt = int64ToAuxInt(0) return true } - // match: (XORshiftLL [c] (SRLconst x [64-c]) x) - // result: (RORconst [64-c] x) + // match: (XORshiftLL [8] (UBFX [armBFAuxInt(8, 8)] x) x) + // result: (REV16W x) for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != 64-c { + if v.Type != typ.UInt16 || auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || v_0.Type != typ.UInt16 || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 8) { break } x := v_0.Args[0] if x != v_1 { break } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(64 - c) + v.reset(OpARM64REV16W) v.AddArg(x) return true } - // match: (XORshiftLL [c] (UBFX [bfc] x) x) - // cond: c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c) - // result: (RORWconst [32-c] x) + // match: (XORshiftLL [8] (UBFX [armBFAuxInt(8, 24)] (ANDconst [c1] x)) (ANDconst [c2] x)) + // cond: uint32(c1) == 0xff00ff00 && uint32(c2) == 0x00ff00ff + // result: (REV16W x) for { - t := v.Type - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64UBFX { + if auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 24) { break } - bfc := auxIntToArm64BitField(v_0.AuxInt) - x := v_0.Args[0] - if x != v_1 || !(c < 32 && t.Size() == 4 && bfc == armBFAuxInt(32-c, c)) { - break - } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(32 - c) - v.AddArg(x) - return true - } - // match: (XORshiftLL [8] (UBFX [armBFAuxInt(8, 8)] x) x) - // result: (REV16W x) - for { - if v.Type != typ.UInt16 || auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || v_0.Type != typ.UInt16 || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 8) { - break - } - x := v_0.Args[0] - if x != v_1 { - break - } - v.reset(OpARM64REV16W) - v.AddArg(x) - return true - } - // match: (XORshiftLL [8] (UBFX [armBFAuxInt(8, 24)] (ANDconst [c1] x)) (ANDconst [c2] x)) - // cond: uint32(c1) == 0xff00ff00 && uint32(c2) == 0x00ff00ff - // result: (REV16W x) - for { - if auxIntToInt64(v.AuxInt) != 8 || v_0.Op != OpARM64UBFX || auxIntToArm64BitField(v_0.AuxInt) != armBFAuxInt(8, 24) { - break - } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpARM64ANDconst { + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpARM64ANDconst { break } c1 := auxIntToInt64(v_0_0.AuxInt) @@ -22219,17 +22587,15 @@ func rewriteValueARM64_OpARM64XORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -22273,55 +22639,71 @@ func rewriteValueARM64_OpARM64XORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) v.AuxInt = int64ToAuxInt(0) return true } - // match: (XORshiftRL [c] (SLLconst x [64-c]) x) - // result: (RORconst [ c] x) + return false +} +func rewriteValueARM64_OpARM64XORshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (XORshiftRO (MOVDconst [c]) x [d]) + // result: (XORconst [c] (RORconst x [d])) for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 64-c { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { break } - x := v_0.Args[0] - if x != v_1 { + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (XORshiftRO x (MOVDconst [c]) [d]) + // result: (XORconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { break } - v.reset(OpARM64RORconst) - v.AuxInt = int64ToAuxInt(c) + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) v.AddArg(x) return true } - // match: (XORshiftRL [c] (SLLconst x [32-c]) (MOVWUreg x)) - // cond: c < 32 && t.Size() == 4 - // result: (RORWconst [c] x) + // match: (XORshiftRO (RORconst x [c]) x [c]) + // result: (MOVDconst [0]) for { - t := v.Type c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != 32-c { + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { break } x := v_0.Args[0] - if v_1.Op != OpARM64MOVWUreg || x != v_1.Args[0] || !(c < 32 && t.Size() == 4) { + if x != v_1 { break } - v.reset(OpARM64RORWconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(0) return true } return false @@ -23847,25 +24229,45 @@ func rewriteValueARM64_OpLsh16x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x16 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh16x16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh16x32(v *Value) bool { v_1 := v.Args[1] @@ -23873,36 +24275,75 @@ func rewriteValueARM64_OpLsh16x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x32 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh16x32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh16x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh16x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh16x64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) @@ -23915,6 +24356,7 @@ func rewriteValueARM64_OpLsh16x64(v *Value) bool { v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh16x8(v *Value) bool { v_1 := v.Args[1] @@ -23922,25 +24364,45 @@ func rewriteValueARM64_OpLsh16x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x8 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh16x8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh32x16(v *Value) bool { v_1 := v.Args[1] @@ -23948,25 +24410,45 @@ func rewriteValueARM64_OpLsh32x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x16 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh32x16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh32x32(v *Value) bool { v_1 := v.Args[1] @@ -23974,36 +24456,75 @@ func rewriteValueARM64_OpLsh32x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x32 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh32x32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh32x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh32x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh32x64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) @@ -24016,6 +24537,7 @@ func rewriteValueARM64_OpLsh32x64(v *Value) bool { v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh32x8(v *Value) bool { v_1 := v.Args[1] @@ -24023,25 +24545,45 @@ func rewriteValueARM64_OpLsh32x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x8 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh32x8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh64x16(v *Value) bool { v_1 := v.Args[1] @@ -24049,25 +24591,45 @@ func rewriteValueARM64_OpLsh64x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x16 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh64x16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh64x32(v *Value) bool { v_1 := v.Args[1] @@ -24075,36 +24637,75 @@ func rewriteValueARM64_OpLsh64x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x32 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh64x32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh64x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh64x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh64x64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) @@ -24117,6 +24718,7 @@ func rewriteValueARM64_OpLsh64x64(v *Value) bool { v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh64x8(v *Value) bool { v_1 := v.Args[1] @@ -24124,25 +24726,45 @@ func rewriteValueARM64_OpLsh64x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x8 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh64x8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh8x16(v *Value) bool { v_1 := v.Args[1] @@ -24150,62 +24772,121 @@ func rewriteValueARM64_OpLsh8x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh8x16 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh8x16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh8x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types - // match: (Lsh8x32 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + typ := &b.Func.Config.Types + // match: (Lsh8x32 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh8x32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + if !(!shiftIsBounded(v)) { + break + } + v.reset(OpARM64CSEL) + v.AuxInt = opToAuxInt(OpARM64LessThanU) + v0 := b.NewValue0(v.Pos, OpARM64SLL, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) + return true + } + return false +} +func rewriteValueARM64_OpLsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Lsh8x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) for { t := v.Type x := v_0 y := v_1 - v.reset(OpARM64CSEL) - v.AuxInt = opToAuxInt(OpARM64LessThanU) - v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) return true } -} -func rewriteValueARM64_OpLsh8x64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block // match: (Lsh8x64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) @@ -24218,6 +24899,7 @@ func rewriteValueARM64_OpLsh8x64(v *Value) bool { v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpLsh8x8(v *Value) bool { v_1 := v.Args[1] @@ -24225,25 +24907,45 @@ func rewriteValueARM64_OpLsh8x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh8x8 x y) - // result: (CSEL [OpARM64LessThanU] (SLL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SLL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Lsh8x8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SLL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SLL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpMod16(v *Value) bool { v_1 := v.Args[1] @@ -24394,36 +25096,6 @@ func rewriteValueARM64_OpMove(v *Value) bool { v.AddArg3(dst, v0, mem) return true } - // match: (Move [4] dst src mem) - // result: (MOVWstore dst (MOVWUload src mem) mem) - for { - if auxIntToInt64(v.AuxInt) != 4 { - break - } - dst := v_0 - src := v_1 - mem := v_2 - v.reset(OpARM64MOVWstore) - v0 := b.NewValue0(v.Pos, OpARM64MOVWUload, typ.UInt32) - v0.AddArg2(src, mem) - v.AddArg3(dst, v0, mem) - return true - } - // match: (Move [8] dst src mem) - // result: (MOVDstore dst (MOVDload src mem) mem) - for { - if auxIntToInt64(v.AuxInt) != 8 { - break - } - dst := v_0 - src := v_1 - mem := v_2 - v.reset(OpARM64MOVDstore) - v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v0.AddArg2(src, mem) - v.AddArg3(dst, v0, mem) - return true - } // match: (Move [3] dst src mem) // result: (MOVBstore [2] dst (MOVBUload [2] src mem) (MOVHstore dst (MOVHUload src mem) mem)) for { @@ -24445,6 +25117,21 @@ func rewriteValueARM64_OpMove(v *Value) bool { v.AddArg3(dst, v0, v1) return true } + // match: (Move [4] dst src mem) + // result: (MOVWstore dst (MOVWUload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64MOVWstore) + v0 := b.NewValue0(v.Pos, OpARM64MOVWUload, typ.UInt32) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } // match: (Move [5] dst src mem) // result: (MOVBstore [4] dst (MOVBUload [4] src mem) (MOVWstore dst (MOVWUload src mem) mem)) for { @@ -24488,7 +25175,7 @@ func rewriteValueARM64_OpMove(v *Value) bool { return true } // match: (Move [7] dst src mem) - // result: (MOVBstore [6] dst (MOVBUload [6] src mem) (MOVHstore [4] dst (MOVHUload [4] src mem) (MOVWstore dst (MOVWUload src mem) mem))) + // result: (MOVWstore [3] dst (MOVWUload [3] src mem) (MOVWstore dst (MOVWUload src mem) mem)) for { if auxIntToInt64(v.AuxInt) != 7 { break @@ -24496,21 +25183,93 @@ func rewriteValueARM64_OpMove(v *Value) bool { dst := v_0 src := v_1 mem := v_2 + v.reset(OpARM64MOVWstore) + v.AuxInt = int32ToAuxInt(3) + v0 := b.NewValue0(v.Pos, OpARM64MOVWUload, typ.UInt32) + v0.AuxInt = int32ToAuxInt(3) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpARM64MOVWUload, typ.UInt32) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [8] dst src mem) + // result: (MOVDstore dst (MOVDload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64MOVDstore) + v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } + // match: (Move [9] dst src mem) + // result: (MOVBstore [8] dst (MOVBUload [8] src mem) (MOVDstore dst (MOVDload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 9 { + break + } + dst := v_0 + src := v_1 + mem := v_2 v.reset(OpARM64MOVBstore) - v.AuxInt = int32ToAuxInt(6) + v.AuxInt = int32ToAuxInt(8) v0 := b.NewValue0(v.Pos, OpARM64MOVBUload, typ.UInt8) - v0.AuxInt = int32ToAuxInt(6) + v0.AuxInt = int32ToAuxInt(8) v0.AddArg2(src, mem) - v1 := b.NewValue0(v.Pos, OpARM64MOVHstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(4) - v2 := b.NewValue0(v.Pos, OpARM64MOVHUload, typ.UInt16) - v2.AuxInt = int32ToAuxInt(4) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [10] dst src mem) + // result: (MOVHstore [8] dst (MOVHUload [8] src mem) (MOVDstore dst (MOVDload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 10 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64MOVHstore) + v.AuxInt = int32ToAuxInt(8) + v0 := b.NewValue0(v.Pos, OpARM64MOVHUload, typ.UInt16) + v0.AuxInt = int32ToAuxInt(8) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) v2.AddArg2(src, mem) - v3 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) - v4 := b.NewValue0(v.Pos, OpARM64MOVWUload, typ.UInt32) - v4.AddArg2(src, mem) - v3.AddArg3(dst, v4, mem) - v1.AddArg3(dst, v2, v3) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [11] dst src mem) + // result: (MOVDstore [3] dst (MOVDload [3] src mem) (MOVDstore dst (MOVDload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 11 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(3) + v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(3) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) v.AddArg3(dst, v0, v1) return true } @@ -24535,19 +25294,19 @@ func rewriteValueARM64_OpMove(v *Value) bool { v.AddArg3(dst, v0, v1) return true } - // match: (Move [16] dst src mem) - // result: (MOVDstore [8] dst (MOVDload [8] src mem) (MOVDstore dst (MOVDload src mem) mem)) + // match: (Move [13] dst src mem) + // result: (MOVDstore [5] dst (MOVDload [5] src mem) (MOVDstore dst (MOVDload src mem) mem)) for { - if auxIntToInt64(v.AuxInt) != 16 { + if auxIntToInt64(v.AuxInt) != 13 { break } dst := v_0 src := v_1 mem := v_2 v.reset(OpARM64MOVDstore) - v.AuxInt = int32ToAuxInt(8) + v.AuxInt = int32ToAuxInt(5) v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v0.AuxInt = int32ToAuxInt(8) + v0.AuxInt = int32ToAuxInt(5) v0.AddArg2(src, mem) v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) @@ -24556,89 +25315,243 @@ func rewriteValueARM64_OpMove(v *Value) bool { v.AddArg3(dst, v0, v1) return true } - // match: (Move [24] dst src mem) - // result: (MOVDstore [16] dst (MOVDload [16] src mem) (MOVDstore [8] dst (MOVDload [8] src mem) (MOVDstore dst (MOVDload src mem) mem))) + // match: (Move [14] dst src mem) + // result: (MOVDstore [6] dst (MOVDload [6] src mem) (MOVDstore dst (MOVDload src mem) mem)) for { - if auxIntToInt64(v.AuxInt) != 24 { + if auxIntToInt64(v.AuxInt) != 14 { break } dst := v_0 src := v_1 mem := v_2 v.reset(OpARM64MOVDstore) - v.AuxInt = int32ToAuxInt(16) + v.AuxInt = int32ToAuxInt(6) + v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(6) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [15] dst src mem) + // result: (MOVDstore [7] dst (MOVDload [7] src mem) (MOVDstore dst (MOVDload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 15 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(7) v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v0.AuxInt = int32ToAuxInt(16) + v0.AuxInt = int32ToAuxInt(7) v0.AddArg2(src, mem) v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(8) v2 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v2.AuxInt = int32ToAuxInt(8) v2.AddArg2(src, mem) - v3 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v4 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v4.AddArg2(src, mem) - v3.AddArg3(dst, v4, mem) - v1.AddArg3(dst, v2, v3) + v1.AddArg3(dst, v2, mem) v.AddArg3(dst, v0, v1) return true } + // match: (Move [16] dst src mem) + // result: (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem) + for { + if auxIntToInt64(v.AuxInt) != 16 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64STP) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v1.AddArg2(src, mem) + v0.AddArg(v1) + v2 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2.AddArg(v1) + v.AddArg4(dst, v0, v2, mem) + return true + } + // match: (Move [32] dst src mem) + // result: (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)) + for { + if auxIntToInt64(v.AuxInt) != 32 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64STP) + v.AuxInt = int32ToAuxInt(16) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v1.AuxInt = int32ToAuxInt(16) + v1.AddArg2(src, mem) + v0.AddArg(v1) + v2 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v4 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v5 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v5.AddArg2(src, mem) + v4.AddArg(v5) + v6 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v6.AddArg(v5) + v3.AddArg4(dst, v4, v6, mem) + v.AddArg4(dst, v0, v2, v3) + return true + } + // match: (Move [48] dst src mem) + // result: (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem)) (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))) + for { + if auxIntToInt64(v.AuxInt) != 48 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64STP) + v.AuxInt = int32ToAuxInt(32) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v1.AuxInt = int32ToAuxInt(32) + v1.AddArg2(src, mem) + v0.AddArg(v1) + v2 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v3.AuxInt = int32ToAuxInt(16) + v4 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v5 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v5.AuxInt = int32ToAuxInt(16) + v5.AddArg2(src, mem) + v4.AddArg(v5) + v6 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v6.AddArg(v5) + v7 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v8 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v9 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v9.AddArg2(src, mem) + v8.AddArg(v9) + v10 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v10.AddArg(v9) + v7.AddArg4(dst, v8, v10, mem) + v3.AddArg4(dst, v4, v6, v7) + v.AddArg4(dst, v0, v2, v3) + return true + } + // match: (Move [64] dst src mem) + // result: (STP [48] dst (Select0 (LDP [48] src mem)) (Select1 (LDP [48] src mem)) (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem)) (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem)) (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)))) + for { + if auxIntToInt64(v.AuxInt) != 64 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpARM64STP) + v.AuxInt = int32ToAuxInt(48) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v1.AuxInt = int32ToAuxInt(48) + v1.AddArg2(src, mem) + v0.AddArg(v1) + v2 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v3.AuxInt = int32ToAuxInt(32) + v4 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v5 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v5.AuxInt = int32ToAuxInt(32) + v5.AddArg2(src, mem) + v4.AddArg(v5) + v6 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v6.AddArg(v5) + v7 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v7.AuxInt = int32ToAuxInt(16) + v8 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v9 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v9.AuxInt = int32ToAuxInt(16) + v9.AddArg2(src, mem) + v8.AddArg(v9) + v10 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v10.AddArg(v9) + v11 := b.NewValue0(v.Pos, OpARM64STP, types.TypeMem) + v12 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v13 := b.NewValue0(v.Pos, OpARM64LDP, types.NewTuple(typ.UInt64, typ.UInt64)) + v13.AddArg2(src, mem) + v12.AddArg(v13) + v14 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v14.AddArg(v13) + v11.AddArg4(dst, v12, v14, mem) + v7.AddArg4(dst, v8, v10, v11) + v3.AddArg4(dst, v4, v6, v7) + v.AddArg4(dst, v0, v2, v3) + return true + } // match: (Move [s] dst src mem) - // cond: s%8 != 0 && s > 8 - // result: (Move [s%8] (OffPtr dst [s-s%8]) (OffPtr src [s-s%8]) (Move [s-s%8] dst src mem)) + // cond: s%16 != 0 && s%16 <= 8 && s > 16 + // result: (Move [8] (OffPtr dst [s-8]) (OffPtr src [s-8]) (Move [s-s%16] dst src mem)) for { s := auxIntToInt64(v.AuxInt) dst := v_0 src := v_1 mem := v_2 - if !(s%8 != 0 && s > 8) { + if !(s%16 != 0 && s%16 <= 8 && s > 16) { break } v.reset(OpMove) - v.AuxInt = int64ToAuxInt(s % 8) + v.AuxInt = int64ToAuxInt(8) v0 := b.NewValue0(v.Pos, OpOffPtr, dst.Type) - v0.AuxInt = int64ToAuxInt(s - s%8) + v0.AuxInt = int64ToAuxInt(s - 8) v0.AddArg(dst) v1 := b.NewValue0(v.Pos, OpOffPtr, src.Type) - v1.AuxInt = int64ToAuxInt(s - s%8) + v1.AuxInt = int64ToAuxInt(s - 8) v1.AddArg(src) v2 := b.NewValue0(v.Pos, OpMove, types.TypeMem) - v2.AuxInt = int64ToAuxInt(s - s%8) + v2.AuxInt = int64ToAuxInt(s - s%16) v2.AddArg3(dst, src, mem) v.AddArg3(v0, v1, v2) return true } // match: (Move [s] dst src mem) - // cond: s > 32 && s <= 16*64 && s%16 == 8 && !config.noDuffDevice && logLargeCopy(v, s) - // result: (MOVDstore [int32(s-8)] dst (MOVDload [int32(s-8)] src mem) (DUFFCOPY [8*(64-(s-8)/16)] dst src mem)) + // cond: s%16 != 0 && s%16 > 8 && s > 16 + // result: (Move [16] (OffPtr dst [s-16]) (OffPtr src [s-16]) (Move [s-s%16] dst src mem)) for { s := auxIntToInt64(v.AuxInt) dst := v_0 src := v_1 mem := v_2 - if !(s > 32 && s <= 16*64 && s%16 == 8 && !config.noDuffDevice && logLargeCopy(v, s)) { + if !(s%16 != 0 && s%16 > 8 && s > 16) { break } - v.reset(OpARM64MOVDstore) - v.AuxInt = int32ToAuxInt(int32(s - 8)) - v0 := b.NewValue0(v.Pos, OpARM64MOVDload, typ.UInt64) - v0.AuxInt = int32ToAuxInt(int32(s - 8)) - v0.AddArg2(src, mem) - v1 := b.NewValue0(v.Pos, OpARM64DUFFCOPY, types.TypeMem) - v1.AuxInt = int64ToAuxInt(8 * (64 - (s-8)/16)) - v1.AddArg3(dst, src, mem) - v.AddArg3(dst, v0, v1) + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(16) + v0 := b.NewValue0(v.Pos, OpOffPtr, dst.Type) + v0.AuxInt = int64ToAuxInt(s - 16) + v0.AddArg(dst) + v1 := b.NewValue0(v.Pos, OpOffPtr, src.Type) + v1.AuxInt = int64ToAuxInt(s - 16) + v1.AddArg(src) + v2 := b.NewValue0(v.Pos, OpMove, types.TypeMem) + v2.AuxInt = int64ToAuxInt(s - s%16) + v2.AddArg3(dst, src, mem) + v.AddArg3(v0, v1, v2) return true } // match: (Move [s] dst src mem) - // cond: s > 32 && s <= 16*64 && s%16 == 0 && !config.noDuffDevice && logLargeCopy(v, s) + // cond: s > 64 && s <= 16*64 && s%16 == 0 && !config.noDuffDevice && logLargeCopy(v, s) // result: (DUFFCOPY [8 * (64 - s/16)] dst src mem) for { s := auxIntToInt64(v.AuxInt) dst := v_0 src := v_1 mem := v_2 - if !(s > 32 && s <= 16*64 && s%16 == 0 && !config.noDuffDevice && logLargeCopy(v, s)) { + if !(s > 64 && s <= 16*64 && s%16 == 0 && !config.noDuffDevice && logLargeCopy(v, s)) { break } v.reset(OpARM64DUFFCOPY) @@ -24647,19 +25560,19 @@ func rewriteValueARM64_OpMove(v *Value) bool { return true } // match: (Move [s] dst src mem) - // cond: s > 24 && s%8 == 0 && logLargeCopy(v, s) - // result: (LoweredMove dst src (ADDconst src [s-8]) mem) + // cond: s%16 == 0 && (s > 16*64 || config.noDuffDevice) && logLargeCopy(v, s) + // result: (LoweredMove dst src (ADDconst src [s-16]) mem) for { s := auxIntToInt64(v.AuxInt) dst := v_0 src := v_1 mem := v_2 - if !(s > 24 && s%8 == 0 && logLargeCopy(v, s)) { + if !(s%16 == 0 && (s > 16*64 || config.noDuffDevice) && logLargeCopy(v, s)) { break } v.reset(OpARM64LoweredMove) v0 := b.NewValue0(v.Pos, OpARM64ADDconst, src.Type) - v0.AuxInt = int64ToAuxInt(s - 8) + v0.AuxInt = int64ToAuxInt(s - 16) v0.AddArg(src) v.AddArg4(dst, src, v0, mem) return true @@ -24951,6 +25864,46 @@ func rewriteValueARM64_OpPopCount64(v *Value) bool { return true } } +func rewriteValueARM64_OpPrefetchCache(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (PrefetchCache addr mem) + // result: (PRFM [0] addr mem) + for { + addr := v_0 + mem := v_1 + v.reset(OpARM64PRFM) + v.AuxInt = int64ToAuxInt(0) + v.AddArg2(addr, mem) + return true + } +} +func rewriteValueARM64_OpPrefetchCacheStreamed(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (PrefetchCacheStreamed addr mem) + // result: (PRFM [1] addr mem) + for { + addr := v_0 + mem := v_1 + v.reset(OpARM64PRFM) + v.AuxInt = int64ToAuxInt(1) + v.AddArg2(addr, mem) + return true + } +} +func rewriteValueARM64_OpPubBarrier(v *Value) bool { + v_0 := v.Args[0] + // match: (PubBarrier mem) + // result: (DMB [0xe] mem) + for { + mem := v_0 + v.reset(OpARM64DMB) + v.AuxInt = int64ToAuxInt(0xe) + v.AddArg(mem) + return true + } +} func rewriteValueARM64_OpRotateLeft16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -24977,7 +25930,24 @@ func rewriteValueARM64_OpRotateLeft16(v *Value) bool { v.AddArg2(v0, v2) return true } - return false + // match: (RotateLeft16 x y) + // result: (RORW (ORshiftLL (ZeroExt16to32 x) (ZeroExt16to32 x) [16]) (NEG y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpARM64RORW) + v.Type = t + v0 := b.NewValue0(v.Pos, OpARM64ORshiftLL, typ.UInt32) + v0.AuxInt = int64ToAuxInt(16) + v1 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) + v1.AddArg(x) + v0.AddArg2(v1, v1) + v2 := b.NewValue0(v.Pos, OpARM64NEG, typ.Int64) + v2.AddArg(y) + v.AddArg2(v0, v2) + return true + } } func rewriteValueARM64_OpRotateLeft32(v *Value) bool { v_1 := v.Args[1] @@ -25037,7 +26007,31 @@ func rewriteValueARM64_OpRotateLeft8(v *Value) bool { v.AddArg2(v0, v2) return true } - return false + // match: (RotateLeft8 x y) + // result: (OR (SLL x (ANDconst [7] y)) (SRL (ZeroExt8to64 x) (ANDconst [7] (NEG y)))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpARM64OR) + v.Type = t + v0 := b.NewValue0(v.Pos, OpARM64SLL, t) + v1 := b.NewValue0(v.Pos, OpARM64ANDconst, typ.Int64) + v1.AuxInt = int64ToAuxInt(7) + v1.AddArg(y) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpARM64SRL, t) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(x) + v4 := b.NewValue0(v.Pos, OpARM64ANDconst, typ.Int64) + v4.AuxInt = int64ToAuxInt(7) + v5 := b.NewValue0(v.Pos, OpARM64NEG, typ.Int64) + v5.AddArg(y) + v4.AddArg(v5) + v2.AddArg2(v3, v4) + v.AddArg2(v0, v2) + return true + } } func rewriteValueARM64_OpRsh16Ux16(v *Value) bool { v_1 := v.Args[1] @@ -25045,27 +26039,49 @@ func rewriteValueARM64_OpRsh16Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux16 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh16Ux16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh16Ux32(v *Value) bool { v_1 := v.Args[1] @@ -25073,27 +26089,49 @@ func rewriteValueARM64_OpRsh16Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux32 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh16Ux32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh16Ux64(v *Value) bool { v_1 := v.Args[1] @@ -25101,11 +26139,32 @@ func rewriteValueARM64_OpRsh16Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh16Ux64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) @@ -25120,6 +26179,7 @@ func rewriteValueARM64_OpRsh16Ux64(v *Value) bool { v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh16Ux8(v *Value) bool { v_1 := v.Args[1] @@ -25127,92 +26187,179 @@ func rewriteValueARM64_OpRsh16Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux8 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh16Ux8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt16to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh16x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh16x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh16x16 x y) - // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh16x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh16x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh16x32 x y) - // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh16x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh16x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh16x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) v0.AddArg(x) @@ -25227,33 +26374,56 @@ func rewriteValueARM64_OpRsh16x64(v *Value) bool { v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh16x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh16x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh16x8 x y) - // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh32Ux16(v *Value) bool { v_1 := v.Args[1] @@ -25261,27 +26431,49 @@ func rewriteValueARM64_OpRsh32Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux16 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh32Ux16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh32Ux32(v *Value) bool { v_1 := v.Args[1] @@ -25289,27 +26481,49 @@ func rewriteValueARM64_OpRsh32Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux32 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh32Ux32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh32Ux64(v *Value) bool { v_1 := v.Args[1] @@ -25317,11 +26531,32 @@ func rewriteValueARM64_OpRsh32Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh32Ux64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) @@ -25336,6 +26571,7 @@ func rewriteValueARM64_OpRsh32Ux64(v *Value) bool { v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh32Ux8(v *Value) bool { v_1 := v.Args[1] @@ -25343,92 +26579,179 @@ func rewriteValueARM64_OpRsh32Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux8 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh32Ux8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt32to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh32x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh32x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh32x16 x y) - // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh32x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh32x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh32x32 x y) - // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh32x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh32x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh32x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) v0.AddArg(x) @@ -25443,33 +26766,56 @@ func rewriteValueARM64_OpRsh32x64(v *Value) bool { v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh32x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh32x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh32x8 x y) - // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh64Ux16(v *Value) bool { v_1 := v.Args[1] @@ -25477,25 +26823,45 @@ func rewriteValueARM64_OpRsh64Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux16 x y) - // result: (CSEL [OpARM64LessThanU] (SRL x (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Rsh64Ux16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpRsh64Ux32(v *Value) bool { v_1 := v.Args[1] @@ -25503,36 +26869,75 @@ func rewriteValueARM64_OpRsh64Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux32 x y) - // result: (CSEL [OpARM64LessThanU] (SRL x (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Rsh64Ux32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpRsh64Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Rsh64Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Rsh64Ux64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) @@ -25545,6 +26950,7 @@ func rewriteValueARM64_OpRsh64Ux64(v *Value) bool { v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpRsh64Ux8(v *Value) bool { v_1 := v.Args[1] @@ -25552,85 +26958,164 @@ func rewriteValueARM64_OpRsh64Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux8 x y) - // result: (CSEL [OpARM64LessThanU] (SRL x (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v.AddArg2(x, y) + return true + } + // match: (Rsh64Ux8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL x y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) - v0 := b.NewValue0(v.Pos, OpARM64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v0.AddArg2(x, v1) - v2 := b.NewValue0(v.Pos, OpConst64, t) - v2.AuxInt = int64ToAuxInt(0) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v.AddArg3(v0, v2, v3) + v0 := b.NewValue0(v.Pos, OpARM64SRL, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpConst64, t) + v1.AuxInt = int64ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v.AddArg3(v0, v1, v2) return true } + return false } func rewriteValueARM64_OpRsh64x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh64x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v.AddArg2(x, y) + return true + } // match: (Rsh64x16 x y) - // result: (SRA x (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v0.AuxInt = opToAuxInt(OpARM64LessThanU) - v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v1.AddArg(y) - v2 := b.NewValue0(v.Pos, OpConst64, y.Type) - v2.AuxInt = int64ToAuxInt(63) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v0.AddArg3(v1, v2, v3) + v1 := b.NewValue0(v.Pos, OpConst64, y.Type) + v1.AuxInt = int64ToAuxInt(63) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v0.AddArg3(y, v1, v2) v.AddArg2(x, v0) return true } + return false } func rewriteValueARM64_OpRsh64x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh64x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v.AddArg2(x, y) + return true + } // match: (Rsh64x32 x y) - // result: (SRA x (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v0.AuxInt = opToAuxInt(OpARM64LessThanU) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(y) - v2 := b.NewValue0(v.Pos, OpConst64, y.Type) - v2.AuxInt = int64ToAuxInt(63) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v0.AddArg3(v1, v2, v3) + v1 := b.NewValue0(v.Pos, OpConst64, y.Type) + v1.AuxInt = int64ToAuxInt(63) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v0.AddArg3(y, v1, v2) v.AddArg2(x, v0) return true } + return false } func rewriteValueARM64_OpRsh64x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + // match: (Rsh64x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v.AddArg2(x, y) + return true + } // match: (Rsh64x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v0.AuxInt = opToAuxInt(OpARM64LessThanU) @@ -25643,31 +27128,52 @@ func rewriteValueARM64_OpRsh64x64(v *Value) bool { v.AddArg2(x, v0) return true } + return false } func rewriteValueARM64_OpRsh64x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh64x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v.AddArg2(x, y) + return true + } // match: (Rsh64x8 x y) - // result: (SRA x (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA x (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v0.AuxInt = opToAuxInt(OpARM64LessThanU) - v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v1.AddArg(y) - v2 := b.NewValue0(v.Pos, OpConst64, y.Type) - v2.AuxInt = int64ToAuxInt(63) - v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v3.AuxInt = int64ToAuxInt(64) - v3.AddArg(v1) - v0.AddArg3(v1, v2, v3) + v1 := b.NewValue0(v.Pos, OpConst64, y.Type) + v1.AuxInt = int64ToAuxInt(63) + v2 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v2.AddArg(v3) + v0.AddArg3(y, v1, v2) v.AddArg2(x, v0) return true } + return false } func rewriteValueARM64_OpRsh8Ux16(v *Value) bool { v_1 := v.Args[1] @@ -25675,27 +27181,49 @@ func rewriteValueARM64_OpRsh8Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux16 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8Ux16 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt16to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh8Ux32(v *Value) bool { v_1 := v.Args[1] @@ -25703,27 +27231,49 @@ func rewriteValueARM64_OpRsh8Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux32 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8Ux32 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt32to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh8Ux64(v *Value) bool { v_1 := v.Args[1] @@ -25731,11 +27281,32 @@ func rewriteValueARM64_OpRsh8Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8Ux64 x y) + // cond: !shiftIsBounded(v) // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] y)) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) @@ -25750,6 +27321,7 @@ func rewriteValueARM64_OpRsh8Ux64(v *Value) bool { v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh8Ux8(v *Value) bool { v_1 := v.Args[1] @@ -25757,92 +27329,179 @@ func rewriteValueARM64_OpRsh8Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux8 x y) - // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) (ZeroExt8to64 y)) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRL) + v.Type = t + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8Ux8 x y) + // cond: !shiftIsBounded(v) + // result: (CSEL [OpARM64LessThanU] (SRL (ZeroExt8to64 x) y) (Const64 [0]) (CMPconst [64] (ZeroExt8to64 y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64CSEL) v.AuxInt = opToAuxInt(OpARM64LessThanU) v0 := b.NewValue0(v.Pos, OpARM64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) v1.AddArg(x) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v0.AddArg2(v1, v2) - v3 := b.NewValue0(v.Pos, OpConst64, t) - v3.AuxInt = int64ToAuxInt(0) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v.AddArg3(v0, v3, v4) + v0.AddArg2(v1, y) + v2 := b.NewValue0(v.Pos, OpConst64, t) + v2.AuxInt = int64ToAuxInt(0) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v.AddArg3(v0, v2, v3) return true } + return false } func rewriteValueARM64_OpRsh8x16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh8x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh8x16 x y) - // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt16to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt16to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh8x32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh8x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh8x32 x y) - // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt32to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt32to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } -} -func rewriteValueARM64_OpRsh8x64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types + return false +} +func rewriteValueARM64_OpRsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh8x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] y))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) v0.AddArg(x) @@ -25857,38 +27516,73 @@ func rewriteValueARM64_OpRsh8x64(v *Value) bool { v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpRsh8x8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Rsh8x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + t := v.Type + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpARM64SRA) + v.Type = t + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } // match: (Rsh8x8 x y) - // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] (ZeroExt8to64 y) (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) + // cond: !shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] y (Const64 [63]) (CMPconst [64] (ZeroExt8to64 y)))) for { x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpARM64SRA) v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) v0.AddArg(x) v1 := b.NewValue0(v.Pos, OpARM64CSEL, y.Type) v1.AuxInt = opToAuxInt(OpARM64LessThanU) - v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v2.AddArg(y) - v3 := b.NewValue0(v.Pos, OpConst64, y.Type) - v3.AuxInt = int64ToAuxInt(63) - v4 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) - v4.AuxInt = int64ToAuxInt(64) - v4.AddArg(v2) - v1.AddArg3(v2, v3, v4) + v2 := b.NewValue0(v.Pos, OpConst64, y.Type) + v2.AuxInt = int64ToAuxInt(63) + v3 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v3.AuxInt = int64ToAuxInt(64) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v3.AddArg(v4) + v1.AddArg3(y, v2, v3) v.AddArg2(v0, v1) return true } + return false } func rewriteValueARM64_OpSelect0(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Select0 (Mul64uhilo x y)) + // result: (UMULH x y) + for { + if v_0.Op != OpMul64uhilo { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpARM64UMULH) + v.AddArg2(x, y) + return true + } // match: (Select0 (Add64carry x y c)) // result: (Select0 (ADCSflags x y (Select1 (ADDSconstflags [-1] c)))) for { @@ -25930,12 +27624,36 @@ func rewriteValueARM64_OpSelect0(v *Value) bool { v.AddArg(v0) return true } + // match: (Select0 (Mul64uover x y)) + // result: (MUL x y) + for { + if v_0.Op != OpMul64uover { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpARM64MUL) + v.AddArg2(x, y) + return true + } return false } func rewriteValueARM64_OpSelect1(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types + // match: (Select1 (Mul64uhilo x y)) + // result: (MUL x y) + for { + if v_0.Op != OpMul64uhilo { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpARM64MUL) + v.AddArg2(x, y) + return true + } // match: (Select1 (Add64carry x y c)) // result: (ADCzerocarry (Select1 (ADCSflags x y (Select1 (ADDSconstflags [-1] c))))) for { @@ -25983,6 +27701,23 @@ func rewriteValueARM64_OpSelect1(v *Value) bool { v.AddArg(v0) return true } + // match: (Select1 (Mul64uover x y)) + // result: (NotEqual (CMPconst (UMULH x y) [0])) + for { + if v_0.Op != OpMul64uover { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpARM64NotEqual) + v0 := b.NewValue0(v.Pos, OpARM64CMPconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpARM64UMULH, typ.UInt64) + v1.AddArg2(x, y) + v0.AddArg(v1) + v.AddArg(v0) + return true + } return false } func rewriteValueARM64_OpSelectN(v *Value) bool { @@ -25997,7 +27732,7 @@ func rewriteValueARM64_OpSelectN(v *Value) bool { break } call := v_0 - if call.Op != OpARM64CALLstatic { + if call.Op != OpARM64CALLstatic || len(call.Args) != 1 { break } sym := auxToCall(call.Aux) @@ -26031,6 +27766,34 @@ func rewriteValueARM64_OpSelectN(v *Value) bool { v.AddArg3(dst, src, mem) return true } + // match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem)) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call) + // result: (Move [sz] dst src mem) + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + call := v_0 + if call.Op != OpARM64CALLstatic || len(call.Args) != 4 { + break + } + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpARM64MOVDconst { + break + } + sz := auxIntToInt64(call_2.AuxInt) + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(sz) + v.AddArg3(dst, src, mem) + return true + } return false } func rewriteValueARM64_OpSlicemask(v *Value) bool { @@ -26203,20 +27966,6 @@ func rewriteValueARM64_OpZero(v *Value) bool { v.AddArg3(ptr, v0, mem) return true } - // match: (Zero [8] ptr mem) - // result: (MOVDstore ptr (MOVDconst [0]) mem) - for { - if auxIntToInt64(v.AuxInt) != 8 { - break - } - ptr := v_0 - mem := v_1 - v.reset(OpARM64MOVDstore) - v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) - v0.AuxInt = int64ToAuxInt(0) - v.AddArg3(ptr, v0, mem) - return true - } // match: (Zero [3] ptr mem) // result: (MOVBstore [2] ptr (MOVDconst [0]) (MOVHstore ptr (MOVDconst [0]) mem)) for { @@ -26269,25 +28018,36 @@ func rewriteValueARM64_OpZero(v *Value) bool { return true } // match: (Zero [7] ptr mem) - // result: (MOVBstore [6] ptr (MOVDconst [0]) (MOVHstore [4] ptr (MOVDconst [0]) (MOVWstore ptr (MOVDconst [0]) mem))) + // result: (MOVWstore [3] ptr (MOVDconst [0]) (MOVWstore ptr (MOVDconst [0]) mem)) for { if auxIntToInt64(v.AuxInt) != 7 { break } ptr := v_0 mem := v_1 - v.reset(OpARM64MOVBstore) - v.AuxInt = int32ToAuxInt(6) + v.reset(OpARM64MOVWstore) + v.AuxInt = int32ToAuxInt(3) v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(0) - v1 := b.NewValue0(v.Pos, OpARM64MOVHstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(4) - v2 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) - v2.AddArg3(ptr, v0, mem) - v1.AddArg3(ptr, v0, v2) + v1 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) + v1.AddArg3(ptr, v0, mem) v.AddArg3(ptr, v0, v1) return true } + // match: (Zero [8] ptr mem) + // result: (MOVDstore ptr (MOVDconst [0]) mem) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + ptr := v_0 + mem := v_1 + v.reset(OpARM64MOVDstore) + v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } // match: (Zero [9] ptr mem) // result: (MOVBstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) for { @@ -26323,22 +28083,19 @@ func rewriteValueARM64_OpZero(v *Value) bool { return true } // match: (Zero [11] ptr mem) - // result: (MOVBstore [10] ptr (MOVDconst [0]) (MOVHstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem))) + // result: (MOVDstore [3] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) for { if auxIntToInt64(v.AuxInt) != 11 { break } ptr := v_0 mem := v_1 - v.reset(OpARM64MOVBstore) - v.AuxInt = int32ToAuxInt(10) + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(3) v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(0) - v1 := b.NewValue0(v.Pos, OpARM64MOVHstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(8) - v2 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v2.AddArg3(ptr, v0, mem) - v1.AddArg3(ptr, v0, v2) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v1.AddArg3(ptr, v0, mem) v.AddArg3(ptr, v0, v1) return true } @@ -26360,65 +28117,53 @@ func rewriteValueARM64_OpZero(v *Value) bool { return true } // match: (Zero [13] ptr mem) - // result: (MOVBstore [12] ptr (MOVDconst [0]) (MOVWstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem))) + // result: (MOVDstore [5] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) for { if auxIntToInt64(v.AuxInt) != 13 { break } ptr := v_0 mem := v_1 - v.reset(OpARM64MOVBstore) - v.AuxInt = int32ToAuxInt(12) + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(5) v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(0) - v1 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(8) - v2 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v2.AddArg3(ptr, v0, mem) - v1.AddArg3(ptr, v0, v2) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v1.AddArg3(ptr, v0, mem) v.AddArg3(ptr, v0, v1) return true } // match: (Zero [14] ptr mem) - // result: (MOVHstore [12] ptr (MOVDconst [0]) (MOVWstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem))) + // result: (MOVDstore [6] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) for { if auxIntToInt64(v.AuxInt) != 14 { break } ptr := v_0 mem := v_1 - v.reset(OpARM64MOVHstore) - v.AuxInt = int32ToAuxInt(12) + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(6) v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(0) - v1 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(8) - v2 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v2.AddArg3(ptr, v0, mem) - v1.AddArg3(ptr, v0, v2) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v1.AddArg3(ptr, v0, mem) v.AddArg3(ptr, v0, v1) return true } // match: (Zero [15] ptr mem) - // result: (MOVBstore [14] ptr (MOVDconst [0]) (MOVHstore [12] ptr (MOVDconst [0]) (MOVWstore [8] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)))) + // result: (MOVDstore [7] ptr (MOVDconst [0]) (MOVDstore ptr (MOVDconst [0]) mem)) for { if auxIntToInt64(v.AuxInt) != 15 { break } ptr := v_0 mem := v_1 - v.reset(OpARM64MOVBstore) - v.AuxInt = int32ToAuxInt(14) + v.reset(OpARM64MOVDstore) + v.AuxInt = int32ToAuxInt(7) v0 := b.NewValue0(v.Pos, OpARM64MOVDconst, typ.UInt64) v0.AuxInt = int64ToAuxInt(0) - v1 := b.NewValue0(v.Pos, OpARM64MOVHstore, types.TypeMem) - v1.AuxInt = int32ToAuxInt(12) - v2 := b.NewValue0(v.Pos, OpARM64MOVWstore, types.TypeMem) - v2.AuxInt = int32ToAuxInt(8) - v3 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) - v3.AddArg3(ptr, v0, mem) - v2.AddArg3(ptr, v0, v3) - v1.AddArg3(ptr, v0, v2) + v1 := b.NewValue0(v.Pos, OpARM64MOVDstore, types.TypeMem) + v1.AddArg3(ptr, v0, mem) v.AddArg3(ptr, v0, v1) return true } @@ -26577,31 +28322,9 @@ func rewriteValueARM64_OpZero(v *Value) bool { return false } func rewriteBlockARM64(b *Block) bool { + typ := &b.Func.Config.Types switch b.Kind { case BlockARM64EQ: - // match: (EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (EQ (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64EQ, v0) - return true - } // match: (EQ (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (EQ (TST x y) yes no) @@ -26630,6 +28353,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (EQ (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (EQ (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64EQ, v0) + return true + } // match: (EQ (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (EQ (TSTW x y) yes no) @@ -26658,12 +28404,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (EQ (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (EQ (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (EQ (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (EQ (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -26675,8 +28421,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64EQ, v0) return true @@ -27043,29 +28789,6 @@ func rewriteBlockARM64(b *Block) bool { return true } case BlockARM64GE: - // match: (GE (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (GE (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64GE, v0) - return true - } // match: (GE (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (GE (TST x y) yes no) @@ -27094,6 +28817,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (GE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (GE (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64GE, v0) + return true + } // match: (GE (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (GE (TSTW x y) yes no) @@ -27122,12 +28868,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (GE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (GE (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (GE (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (GE (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -27139,8 +28885,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64GE, v0) return true @@ -27247,46 +28993,6 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (GE (CMP x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (GE (CMN x y) yes no) - for b.Controls[0].Op == OpARM64CMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64GE, v0) - return true - } - // match: (GE (CMPW x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (GE (CMNW x y) yes no) - for b.Controls[0].Op == OpARM64CMPW { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64GE, v0) - return true - } // match: (GE (CMPconst [0] z:(MADD a x y)) yes no) // cond: z.Uses==1 // result: (GEnoov (CMN a (MUL x y)) yes no) @@ -27479,29 +29185,6 @@ func rewriteBlockARM64(b *Block) bool { return true } case BlockARM64GT: - // match: (GT (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (GT (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64GT, v0) - return true - } // match: (GT (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (GT (TST x y) yes no) @@ -27530,6 +29213,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (GT (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (GT (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64GT, v0) + return true + } // match: (GT (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (GT (TSTW x y) yes no) @@ -27558,12 +29264,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (GT (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (GT (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (GT (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (GT (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -27575,8 +29281,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64GT, v0) return true @@ -27683,46 +29389,6 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (GT (CMP x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (GT (CMN x y) yes no) - for b.Controls[0].Op == OpARM64CMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64GT, v0) - return true - } - // match: (GT (CMPW x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (GT (CMNW x y) yes no) - for b.Controls[0].Op == OpARM64CMPW { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64GT, v0) - return true - } // match: (GT (CMPconst [0] z:(MADD a x y)) yes no) // cond: z.Uses==1 // result: (GTnoov (CMN a (MUL x y)) yes no) @@ -28004,36 +29670,27 @@ func rewriteBlockARM64(b *Block) bool { return true } // match: (If cond yes no) - // result: (NZ cond yes no) + // result: (TBNZ [0] cond yes no) for { cond := b.Controls[0] - b.resetWithControl(BlockARM64NZ, cond) + b.resetWithControl(BlockARM64TBNZ, cond) + b.AuxInt = int64ToAuxInt(0) return true } - case BlockARM64LE: - // match: (LE (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (LE (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64LE, v0) + case BlockJumpTable: + // match: (JumpTable idx) + // result: (JUMPTABLE {makeJumpTableSym(b)} idx (MOVDaddr {makeJumpTableSym(b)} (SB))) + for { + idx := b.Controls[0] + v0 := b.NewValue0(b.Pos, OpARM64MOVDaddr, typ.Uintptr) + v0.Aux = symToAux(makeJumpTableSym(b)) + v1 := b.NewValue0(b.Pos, OpSB, typ.Uintptr) + v0.AddArg(v1) + b.resetWithControl2(BlockARM64JUMPTABLE, idx, v0) + b.Aux = symToAux(makeJumpTableSym(b)) return true } + case BlockARM64LE: // match: (LE (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (LE (TST x y) yes no) @@ -28062,6 +29719,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (LE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (LE (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64LE, v0) + return true + } // match: (LE (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (LE (TSTW x y) yes no) @@ -28090,12 +29770,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (LE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (LE (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (LE (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (LE (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -28107,8 +29787,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64LE, v0) return true @@ -28215,46 +29895,6 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (LE (CMP x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (LE (CMN x y) yes no) - for b.Controls[0].Op == OpARM64CMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64LE, v0) - return true - } - // match: (LE (CMPW x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (LE (CMNW x y) yes no) - for b.Controls[0].Op == OpARM64CMPW { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64LE, v0) - return true - } // match: (LE (CMPconst [0] z:(MADD a x y)) yes no) // cond: z.Uses==1 // result: (LEnoov (CMN a (MUL x y)) yes no) @@ -28422,30 +30062,7 @@ func rewriteBlockARM64(b *Block) bool { b.resetWithControl(BlockARM64GEnoov, cmp) return true } - case BlockARM64LT: - // match: (LT (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (LT (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64LT, v0) - return true - } + case BlockARM64LT: // match: (LT (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (LT (TST x y) yes no) @@ -28474,6 +30091,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (LT (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (LT (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64LT, v0) + return true + } // match: (LT (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (LT (TSTW x y) yes no) @@ -28502,12 +30142,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (LT (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (LT (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (LT (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (LT (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -28519,8 +30159,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64LT, v0) return true @@ -28627,46 +30267,6 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (LT (CMP x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (LT (CMN x y) yes no) - for b.Controls[0].Op == OpARM64CMP { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMN, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64LT, v0) - return true - } - // match: (LT (CMPW x z:(NEG y)) yes no) - // cond: z.Uses == 1 - // result: (LT (CMNW x y) yes no) - for b.Controls[0].Op == OpARM64CMPW { - v_0 := b.Controls[0] - _ = v_0.Args[1] - x := v_0.Args[0] - z := v_0.Args[1] - if z.Op != OpARM64NEG { - break - } - y := z.Args[0] - if !(z.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64CMNW, types.TypeFlags) - v0.AddArg2(x, y) - b.resetWithControl(BlockARM64LT, v0) - return true - } // match: (LT (CMPconst [0] z:(MADD a x y)) yes no) // cond: z.Uses==1 // result: (LTnoov (CMN a (MUL x y)) yes no) @@ -28859,29 +30459,6 @@ func rewriteBlockARM64(b *Block) bool { return true } case BlockARM64NE: - // match: (NE (CMPWconst [0] x:(ANDconst [c] y)) yes no) - // cond: x.Uses == 1 - // result: (NE (TSTWconst [int32(c)] y) yes no) - for b.Controls[0].Op == OpARM64CMPWconst { - v_0 := b.Controls[0] - if auxIntToInt32(v_0.AuxInt) != 0 { - break - } - x := v_0.Args[0] - if x.Op != OpARM64ANDconst { - break - } - c := auxIntToInt64(x.AuxInt) - y := x.Args[0] - if !(x.Uses == 1) { - break - } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) - v0.AuxInt = int32ToAuxInt(int32(c)) - v0.AddArg(y) - b.resetWithControl(BlockARM64NE, v0) - return true - } // match: (NE (CMPconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (NE (TST x y) yes no) @@ -28910,6 +30487,29 @@ func rewriteBlockARM64(b *Block) bool { } break } + // match: (NE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // cond: x.Uses == 1 + // result: (NE (TSTconst [c] y) yes no) + for b.Controls[0].Op == OpARM64CMPconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + if x.Op != OpARM64ANDconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(x.Uses == 1) { + break + } + v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(y) + b.resetWithControl(BlockARM64NE, v0) + return true + } // match: (NE (CMPWconst [0] z:(AND x y)) yes no) // cond: z.Uses == 1 // result: (NE (TSTW x y) yes no) @@ -28938,12 +30538,12 @@ func rewriteBlockARM64(b *Block) bool { } break } - // match: (NE (CMPconst [0] x:(ANDconst [c] y)) yes no) + // match: (NE (CMPWconst [0] x:(ANDconst [c] y)) yes no) // cond: x.Uses == 1 - // result: (NE (TSTconst [c] y) yes no) - for b.Controls[0].Op == OpARM64CMPconst { + // result: (NE (TSTWconst [int32(c)] y) yes no) + for b.Controls[0].Op == OpARM64CMPWconst { v_0 := b.Controls[0] - if auxIntToInt64(v_0.AuxInt) != 0 { + if auxIntToInt32(v_0.AuxInt) != 0 { break } x := v_0.Args[0] @@ -28955,8 +30555,8 @@ func rewriteBlockARM64(b *Block) bool { if !(x.Uses == 1) { break } - v0 := b.NewValue0(v_0.Pos, OpARM64TSTconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v_0.Pos, OpARM64TSTWconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(int32(c)) v0.AddArg(y) b.resetWithControl(BlockARM64NE, v0) return true @@ -29476,6 +31076,161 @@ func rewriteBlockARM64(b *Block) bool { b.Reset(BlockFirst) return true } + case BlockARM64TBNZ: + // match: (TBNZ [0] (Equal cc) yes no) + // result: (EQ cc yes no) + for b.Controls[0].Op == OpARM64Equal { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64EQ, cc) + return true + } + // match: (TBNZ [0] (NotEqual cc) yes no) + // result: (NE cc yes no) + for b.Controls[0].Op == OpARM64NotEqual { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64NE, cc) + return true + } + // match: (TBNZ [0] (LessThan cc) yes no) + // result: (LT cc yes no) + for b.Controls[0].Op == OpARM64LessThan { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64LT, cc) + return true + } + // match: (TBNZ [0] (LessThanU cc) yes no) + // result: (ULT cc yes no) + for b.Controls[0].Op == OpARM64LessThanU { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64ULT, cc) + return true + } + // match: (TBNZ [0] (LessEqual cc) yes no) + // result: (LE cc yes no) + for b.Controls[0].Op == OpARM64LessEqual { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64LE, cc) + return true + } + // match: (TBNZ [0] (LessEqualU cc) yes no) + // result: (ULE cc yes no) + for b.Controls[0].Op == OpARM64LessEqualU { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64ULE, cc) + return true + } + // match: (TBNZ [0] (GreaterThan cc) yes no) + // result: (GT cc yes no) + for b.Controls[0].Op == OpARM64GreaterThan { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64GT, cc) + return true + } + // match: (TBNZ [0] (GreaterThanU cc) yes no) + // result: (UGT cc yes no) + for b.Controls[0].Op == OpARM64GreaterThanU { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64UGT, cc) + return true + } + // match: (TBNZ [0] (GreaterEqual cc) yes no) + // result: (GE cc yes no) + for b.Controls[0].Op == OpARM64GreaterEqual { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64GE, cc) + return true + } + // match: (TBNZ [0] (GreaterEqualU cc) yes no) + // result: (UGE cc yes no) + for b.Controls[0].Op == OpARM64GreaterEqualU { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64UGE, cc) + return true + } + // match: (TBNZ [0] (LessThanF cc) yes no) + // result: (FLT cc yes no) + for b.Controls[0].Op == OpARM64LessThanF { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64FLT, cc) + return true + } + // match: (TBNZ [0] (LessEqualF cc) yes no) + // result: (FLE cc yes no) + for b.Controls[0].Op == OpARM64LessEqualF { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64FLE, cc) + return true + } + // match: (TBNZ [0] (GreaterThanF cc) yes no) + // result: (FGT cc yes no) + for b.Controls[0].Op == OpARM64GreaterThanF { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64FGT, cc) + return true + } + // match: (TBNZ [0] (GreaterEqualF cc) yes no) + // result: (FGE cc yes no) + for b.Controls[0].Op == OpARM64GreaterEqualF { + v_0 := b.Controls[0] + cc := v_0.Args[0] + if auxIntToInt64(b.AuxInt) != 0 { + break + } + b.resetWithControl(BlockARM64FGE, cc) + return true + } case BlockARM64UGE: // match: (UGE (FlagConstant [fc]) yes no) // cond: fc.uge() diff --git a/src/cmd/compile/internal/ssa/rewriteCond_test.go b/src/cmd/compile/internal/ssa/rewriteCond_test.go index 2c26fdf1427e20..ca74ed59471de3 100644 --- a/src/cmd/compile/internal/ssa/rewriteCond_test.go +++ b/src/cmd/compile/internal/ssa/rewriteCond_test.go @@ -68,8 +68,10 @@ func TestCondRewrite(t *testing.T) { } // Profile the aforementioned optimization from two angles: -// SoloJump: generated branching code has one 'jump', for '<' and '>=' -// CombJump: generated branching code has two consecutive 'jump', for '<=' and '>' +// +// SoloJump: generated branching code has one 'jump', for '<' and '>=' +// CombJump: generated branching code has two consecutive 'jump', for '<=' and '>' +// // We expect that 'CombJump' is generally on par with the non-optimized code, and // 'SoloJump' demonstrates some improvement. // It's for arm64 initially, please see https://github.com/golang/go/issues/38740 diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go new file mode 100644 index 00000000000000..d2e7e8e061ffc6 --- /dev/null +++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go @@ -0,0 +1,7997 @@ +// Code generated from gen/LOONG64.rules; DO NOT EDIT. +// generated with: cd gen; go run *.go + +package ssa + +import "cmd/compile/internal/types" + +func rewriteValueLOONG64(v *Value) bool { + switch v.Op { + case OpAdd16: + v.Op = OpLOONG64ADDV + return true + case OpAdd32: + v.Op = OpLOONG64ADDV + return true + case OpAdd32F: + v.Op = OpLOONG64ADDF + return true + case OpAdd64: + v.Op = OpLOONG64ADDV + return true + case OpAdd64F: + v.Op = OpLOONG64ADDD + return true + case OpAdd8: + v.Op = OpLOONG64ADDV + return true + case OpAddPtr: + v.Op = OpLOONG64ADDV + return true + case OpAddr: + return rewriteValueLOONG64_OpAddr(v) + case OpAnd16: + v.Op = OpLOONG64AND + return true + case OpAnd32: + v.Op = OpLOONG64AND + return true + case OpAnd64: + v.Op = OpLOONG64AND + return true + case OpAnd8: + v.Op = OpLOONG64AND + return true + case OpAndB: + v.Op = OpLOONG64AND + return true + case OpAtomicAdd32: + v.Op = OpLOONG64LoweredAtomicAdd32 + return true + case OpAtomicAdd64: + v.Op = OpLOONG64LoweredAtomicAdd64 + return true + case OpAtomicCompareAndSwap32: + v.Op = OpLOONG64LoweredAtomicCas32 + return true + case OpAtomicCompareAndSwap64: + v.Op = OpLOONG64LoweredAtomicCas64 + return true + case OpAtomicExchange32: + v.Op = OpLOONG64LoweredAtomicExchange32 + return true + case OpAtomicExchange64: + v.Op = OpLOONG64LoweredAtomicExchange64 + return true + case OpAtomicLoad32: + v.Op = OpLOONG64LoweredAtomicLoad32 + return true + case OpAtomicLoad64: + v.Op = OpLOONG64LoweredAtomicLoad64 + return true + case OpAtomicLoad8: + v.Op = OpLOONG64LoweredAtomicLoad8 + return true + case OpAtomicLoadPtr: + v.Op = OpLOONG64LoweredAtomicLoad64 + return true + case OpAtomicStore32: + v.Op = OpLOONG64LoweredAtomicStore32 + return true + case OpAtomicStore64: + v.Op = OpLOONG64LoweredAtomicStore64 + return true + case OpAtomicStore8: + v.Op = OpLOONG64LoweredAtomicStore8 + return true + case OpAtomicStorePtrNoWB: + v.Op = OpLOONG64LoweredAtomicStore64 + return true + case OpAvg64u: + return rewriteValueLOONG64_OpAvg64u(v) + case OpClosureCall: + v.Op = OpLOONG64CALLclosure + return true + case OpCom16: + return rewriteValueLOONG64_OpCom16(v) + case OpCom32: + return rewriteValueLOONG64_OpCom32(v) + case OpCom64: + return rewriteValueLOONG64_OpCom64(v) + case OpCom8: + return rewriteValueLOONG64_OpCom8(v) + case OpConst16: + return rewriteValueLOONG64_OpConst16(v) + case OpConst32: + return rewriteValueLOONG64_OpConst32(v) + case OpConst32F: + return rewriteValueLOONG64_OpConst32F(v) + case OpConst64: + return rewriteValueLOONG64_OpConst64(v) + case OpConst64F: + return rewriteValueLOONG64_OpConst64F(v) + case OpConst8: + return rewriteValueLOONG64_OpConst8(v) + case OpConstBool: + return rewriteValueLOONG64_OpConstBool(v) + case OpConstNil: + return rewriteValueLOONG64_OpConstNil(v) + case OpCvt32Fto32: + v.Op = OpLOONG64TRUNCFW + return true + case OpCvt32Fto64: + v.Op = OpLOONG64TRUNCFV + return true + case OpCvt32Fto64F: + v.Op = OpLOONG64MOVFD + return true + case OpCvt32to32F: + v.Op = OpLOONG64MOVWF + return true + case OpCvt32to64F: + v.Op = OpLOONG64MOVWD + return true + case OpCvt64Fto32: + v.Op = OpLOONG64TRUNCDW + return true + case OpCvt64Fto32F: + v.Op = OpLOONG64MOVDF + return true + case OpCvt64Fto64: + v.Op = OpLOONG64TRUNCDV + return true + case OpCvt64to32F: + v.Op = OpLOONG64MOVVF + return true + case OpCvt64to64F: + v.Op = OpLOONG64MOVVD + return true + case OpCvtBoolToUint8: + v.Op = OpCopy + return true + case OpDiv16: + return rewriteValueLOONG64_OpDiv16(v) + case OpDiv16u: + return rewriteValueLOONG64_OpDiv16u(v) + case OpDiv32: + return rewriteValueLOONG64_OpDiv32(v) + case OpDiv32F: + v.Op = OpLOONG64DIVF + return true + case OpDiv32u: + return rewriteValueLOONG64_OpDiv32u(v) + case OpDiv64: + return rewriteValueLOONG64_OpDiv64(v) + case OpDiv64F: + v.Op = OpLOONG64DIVD + return true + case OpDiv64u: + return rewriteValueLOONG64_OpDiv64u(v) + case OpDiv8: + return rewriteValueLOONG64_OpDiv8(v) + case OpDiv8u: + return rewriteValueLOONG64_OpDiv8u(v) + case OpEq16: + return rewriteValueLOONG64_OpEq16(v) + case OpEq32: + return rewriteValueLOONG64_OpEq32(v) + case OpEq32F: + return rewriteValueLOONG64_OpEq32F(v) + case OpEq64: + return rewriteValueLOONG64_OpEq64(v) + case OpEq64F: + return rewriteValueLOONG64_OpEq64F(v) + case OpEq8: + return rewriteValueLOONG64_OpEq8(v) + case OpEqB: + return rewriteValueLOONG64_OpEqB(v) + case OpEqPtr: + return rewriteValueLOONG64_OpEqPtr(v) + case OpGetCallerPC: + v.Op = OpLOONG64LoweredGetCallerPC + return true + case OpGetCallerSP: + v.Op = OpLOONG64LoweredGetCallerSP + return true + case OpGetClosurePtr: + v.Op = OpLOONG64LoweredGetClosurePtr + return true + case OpHmul32: + return rewriteValueLOONG64_OpHmul32(v) + case OpHmul32u: + return rewriteValueLOONG64_OpHmul32u(v) + case OpHmul64: + return rewriteValueLOONG64_OpHmul64(v) + case OpHmul64u: + return rewriteValueLOONG64_OpHmul64u(v) + case OpInterCall: + v.Op = OpLOONG64CALLinter + return true + case OpIsInBounds: + return rewriteValueLOONG64_OpIsInBounds(v) + case OpIsNonNil: + return rewriteValueLOONG64_OpIsNonNil(v) + case OpIsSliceInBounds: + return rewriteValueLOONG64_OpIsSliceInBounds(v) + case OpLOONG64ADDV: + return rewriteValueLOONG64_OpLOONG64ADDV(v) + case OpLOONG64ADDVconst: + return rewriteValueLOONG64_OpLOONG64ADDVconst(v) + case OpLOONG64AND: + return rewriteValueLOONG64_OpLOONG64AND(v) + case OpLOONG64ANDconst: + return rewriteValueLOONG64_OpLOONG64ANDconst(v) + case OpLOONG64LoweredAtomicAdd32: + return rewriteValueLOONG64_OpLOONG64LoweredAtomicAdd32(v) + case OpLOONG64LoweredAtomicAdd64: + return rewriteValueLOONG64_OpLOONG64LoweredAtomicAdd64(v) + case OpLOONG64LoweredAtomicStore32: + return rewriteValueLOONG64_OpLOONG64LoweredAtomicStore32(v) + case OpLOONG64LoweredAtomicStore64: + return rewriteValueLOONG64_OpLOONG64LoweredAtomicStore64(v) + case OpLOONG64MOVBUload: + return rewriteValueLOONG64_OpLOONG64MOVBUload(v) + case OpLOONG64MOVBUreg: + return rewriteValueLOONG64_OpLOONG64MOVBUreg(v) + case OpLOONG64MOVBload: + return rewriteValueLOONG64_OpLOONG64MOVBload(v) + case OpLOONG64MOVBreg: + return rewriteValueLOONG64_OpLOONG64MOVBreg(v) + case OpLOONG64MOVBstore: + return rewriteValueLOONG64_OpLOONG64MOVBstore(v) + case OpLOONG64MOVBstorezero: + return rewriteValueLOONG64_OpLOONG64MOVBstorezero(v) + case OpLOONG64MOVDload: + return rewriteValueLOONG64_OpLOONG64MOVDload(v) + case OpLOONG64MOVDstore: + return rewriteValueLOONG64_OpLOONG64MOVDstore(v) + case OpLOONG64MOVFload: + return rewriteValueLOONG64_OpLOONG64MOVFload(v) + case OpLOONG64MOVFstore: + return rewriteValueLOONG64_OpLOONG64MOVFstore(v) + case OpLOONG64MOVHUload: + return rewriteValueLOONG64_OpLOONG64MOVHUload(v) + case OpLOONG64MOVHUreg: + return rewriteValueLOONG64_OpLOONG64MOVHUreg(v) + case OpLOONG64MOVHload: + return rewriteValueLOONG64_OpLOONG64MOVHload(v) + case OpLOONG64MOVHreg: + return rewriteValueLOONG64_OpLOONG64MOVHreg(v) + case OpLOONG64MOVHstore: + return rewriteValueLOONG64_OpLOONG64MOVHstore(v) + case OpLOONG64MOVHstorezero: + return rewriteValueLOONG64_OpLOONG64MOVHstorezero(v) + case OpLOONG64MOVVload: + return rewriteValueLOONG64_OpLOONG64MOVVload(v) + case OpLOONG64MOVVreg: + return rewriteValueLOONG64_OpLOONG64MOVVreg(v) + case OpLOONG64MOVVstore: + return rewriteValueLOONG64_OpLOONG64MOVVstore(v) + case OpLOONG64MOVVstorezero: + return rewriteValueLOONG64_OpLOONG64MOVVstorezero(v) + case OpLOONG64MOVWUload: + return rewriteValueLOONG64_OpLOONG64MOVWUload(v) + case OpLOONG64MOVWUreg: + return rewriteValueLOONG64_OpLOONG64MOVWUreg(v) + case OpLOONG64MOVWload: + return rewriteValueLOONG64_OpLOONG64MOVWload(v) + case OpLOONG64MOVWreg: + return rewriteValueLOONG64_OpLOONG64MOVWreg(v) + case OpLOONG64MOVWstore: + return rewriteValueLOONG64_OpLOONG64MOVWstore(v) + case OpLOONG64MOVWstorezero: + return rewriteValueLOONG64_OpLOONG64MOVWstorezero(v) + case OpLOONG64NEGV: + return rewriteValueLOONG64_OpLOONG64NEGV(v) + case OpLOONG64NOR: + return rewriteValueLOONG64_OpLOONG64NOR(v) + case OpLOONG64NORconst: + return rewriteValueLOONG64_OpLOONG64NORconst(v) + case OpLOONG64OR: + return rewriteValueLOONG64_OpLOONG64OR(v) + case OpLOONG64ORconst: + return rewriteValueLOONG64_OpLOONG64ORconst(v) + case OpLOONG64ROTR: + return rewriteValueLOONG64_OpLOONG64ROTR(v) + case OpLOONG64ROTRV: + return rewriteValueLOONG64_OpLOONG64ROTRV(v) + case OpLOONG64SGT: + return rewriteValueLOONG64_OpLOONG64SGT(v) + case OpLOONG64SGTU: + return rewriteValueLOONG64_OpLOONG64SGTU(v) + case OpLOONG64SGTUconst: + return rewriteValueLOONG64_OpLOONG64SGTUconst(v) + case OpLOONG64SGTconst: + return rewriteValueLOONG64_OpLOONG64SGTconst(v) + case OpLOONG64SLLV: + return rewriteValueLOONG64_OpLOONG64SLLV(v) + case OpLOONG64SLLVconst: + return rewriteValueLOONG64_OpLOONG64SLLVconst(v) + case OpLOONG64SRAV: + return rewriteValueLOONG64_OpLOONG64SRAV(v) + case OpLOONG64SRAVconst: + return rewriteValueLOONG64_OpLOONG64SRAVconst(v) + case OpLOONG64SRLV: + return rewriteValueLOONG64_OpLOONG64SRLV(v) + case OpLOONG64SRLVconst: + return rewriteValueLOONG64_OpLOONG64SRLVconst(v) + case OpLOONG64SUBV: + return rewriteValueLOONG64_OpLOONG64SUBV(v) + case OpLOONG64SUBVconst: + return rewriteValueLOONG64_OpLOONG64SUBVconst(v) + case OpLOONG64XOR: + return rewriteValueLOONG64_OpLOONG64XOR(v) + case OpLOONG64XORconst: + return rewriteValueLOONG64_OpLOONG64XORconst(v) + case OpLeq16: + return rewriteValueLOONG64_OpLeq16(v) + case OpLeq16U: + return rewriteValueLOONG64_OpLeq16U(v) + case OpLeq32: + return rewriteValueLOONG64_OpLeq32(v) + case OpLeq32F: + return rewriteValueLOONG64_OpLeq32F(v) + case OpLeq32U: + return rewriteValueLOONG64_OpLeq32U(v) + case OpLeq64: + return rewriteValueLOONG64_OpLeq64(v) + case OpLeq64F: + return rewriteValueLOONG64_OpLeq64F(v) + case OpLeq64U: + return rewriteValueLOONG64_OpLeq64U(v) + case OpLeq8: + return rewriteValueLOONG64_OpLeq8(v) + case OpLeq8U: + return rewriteValueLOONG64_OpLeq8U(v) + case OpLess16: + return rewriteValueLOONG64_OpLess16(v) + case OpLess16U: + return rewriteValueLOONG64_OpLess16U(v) + case OpLess32: + return rewriteValueLOONG64_OpLess32(v) + case OpLess32F: + return rewriteValueLOONG64_OpLess32F(v) + case OpLess32U: + return rewriteValueLOONG64_OpLess32U(v) + case OpLess64: + return rewriteValueLOONG64_OpLess64(v) + case OpLess64F: + return rewriteValueLOONG64_OpLess64F(v) + case OpLess64U: + return rewriteValueLOONG64_OpLess64U(v) + case OpLess8: + return rewriteValueLOONG64_OpLess8(v) + case OpLess8U: + return rewriteValueLOONG64_OpLess8U(v) + case OpLoad: + return rewriteValueLOONG64_OpLoad(v) + case OpLocalAddr: + return rewriteValueLOONG64_OpLocalAddr(v) + case OpLsh16x16: + return rewriteValueLOONG64_OpLsh16x16(v) + case OpLsh16x32: + return rewriteValueLOONG64_OpLsh16x32(v) + case OpLsh16x64: + return rewriteValueLOONG64_OpLsh16x64(v) + case OpLsh16x8: + return rewriteValueLOONG64_OpLsh16x8(v) + case OpLsh32x16: + return rewriteValueLOONG64_OpLsh32x16(v) + case OpLsh32x32: + return rewriteValueLOONG64_OpLsh32x32(v) + case OpLsh32x64: + return rewriteValueLOONG64_OpLsh32x64(v) + case OpLsh32x8: + return rewriteValueLOONG64_OpLsh32x8(v) + case OpLsh64x16: + return rewriteValueLOONG64_OpLsh64x16(v) + case OpLsh64x32: + return rewriteValueLOONG64_OpLsh64x32(v) + case OpLsh64x64: + return rewriteValueLOONG64_OpLsh64x64(v) + case OpLsh64x8: + return rewriteValueLOONG64_OpLsh64x8(v) + case OpLsh8x16: + return rewriteValueLOONG64_OpLsh8x16(v) + case OpLsh8x32: + return rewriteValueLOONG64_OpLsh8x32(v) + case OpLsh8x64: + return rewriteValueLOONG64_OpLsh8x64(v) + case OpLsh8x8: + return rewriteValueLOONG64_OpLsh8x8(v) + case OpMod16: + return rewriteValueLOONG64_OpMod16(v) + case OpMod16u: + return rewriteValueLOONG64_OpMod16u(v) + case OpMod32: + return rewriteValueLOONG64_OpMod32(v) + case OpMod32u: + return rewriteValueLOONG64_OpMod32u(v) + case OpMod64: + return rewriteValueLOONG64_OpMod64(v) + case OpMod64u: + return rewriteValueLOONG64_OpMod64u(v) + case OpMod8: + return rewriteValueLOONG64_OpMod8(v) + case OpMod8u: + return rewriteValueLOONG64_OpMod8u(v) + case OpMove: + return rewriteValueLOONG64_OpMove(v) + case OpMul16: + return rewriteValueLOONG64_OpMul16(v) + case OpMul32: + return rewriteValueLOONG64_OpMul32(v) + case OpMul32F: + v.Op = OpLOONG64MULF + return true + case OpMul64: + return rewriteValueLOONG64_OpMul64(v) + case OpMul64F: + v.Op = OpLOONG64MULD + return true + case OpMul64uhilo: + v.Op = OpLOONG64MULVU + return true + case OpMul8: + return rewriteValueLOONG64_OpMul8(v) + case OpNeg16: + v.Op = OpLOONG64NEGV + return true + case OpNeg32: + v.Op = OpLOONG64NEGV + return true + case OpNeg32F: + v.Op = OpLOONG64NEGF + return true + case OpNeg64: + v.Op = OpLOONG64NEGV + return true + case OpNeg64F: + v.Op = OpLOONG64NEGD + return true + case OpNeg8: + v.Op = OpLOONG64NEGV + return true + case OpNeq16: + return rewriteValueLOONG64_OpNeq16(v) + case OpNeq32: + return rewriteValueLOONG64_OpNeq32(v) + case OpNeq32F: + return rewriteValueLOONG64_OpNeq32F(v) + case OpNeq64: + return rewriteValueLOONG64_OpNeq64(v) + case OpNeq64F: + return rewriteValueLOONG64_OpNeq64F(v) + case OpNeq8: + return rewriteValueLOONG64_OpNeq8(v) + case OpNeqB: + v.Op = OpLOONG64XOR + return true + case OpNeqPtr: + return rewriteValueLOONG64_OpNeqPtr(v) + case OpNilCheck: + v.Op = OpLOONG64LoweredNilCheck + return true + case OpNot: + return rewriteValueLOONG64_OpNot(v) + case OpOffPtr: + return rewriteValueLOONG64_OpOffPtr(v) + case OpOr16: + v.Op = OpLOONG64OR + return true + case OpOr32: + v.Op = OpLOONG64OR + return true + case OpOr64: + v.Op = OpLOONG64OR + return true + case OpOr8: + v.Op = OpLOONG64OR + return true + case OpOrB: + v.Op = OpLOONG64OR + return true + case OpPanicBounds: + return rewriteValueLOONG64_OpPanicBounds(v) + case OpRotateLeft16: + return rewriteValueLOONG64_OpRotateLeft16(v) + case OpRotateLeft32: + return rewriteValueLOONG64_OpRotateLeft32(v) + case OpRotateLeft64: + return rewriteValueLOONG64_OpRotateLeft64(v) + case OpRotateLeft8: + return rewriteValueLOONG64_OpRotateLeft8(v) + case OpRound32F: + v.Op = OpCopy + return true + case OpRound64F: + v.Op = OpCopy + return true + case OpRsh16Ux16: + return rewriteValueLOONG64_OpRsh16Ux16(v) + case OpRsh16Ux32: + return rewriteValueLOONG64_OpRsh16Ux32(v) + case OpRsh16Ux64: + return rewriteValueLOONG64_OpRsh16Ux64(v) + case OpRsh16Ux8: + return rewriteValueLOONG64_OpRsh16Ux8(v) + case OpRsh16x16: + return rewriteValueLOONG64_OpRsh16x16(v) + case OpRsh16x32: + return rewriteValueLOONG64_OpRsh16x32(v) + case OpRsh16x64: + return rewriteValueLOONG64_OpRsh16x64(v) + case OpRsh16x8: + return rewriteValueLOONG64_OpRsh16x8(v) + case OpRsh32Ux16: + return rewriteValueLOONG64_OpRsh32Ux16(v) + case OpRsh32Ux32: + return rewriteValueLOONG64_OpRsh32Ux32(v) + case OpRsh32Ux64: + return rewriteValueLOONG64_OpRsh32Ux64(v) + case OpRsh32Ux8: + return rewriteValueLOONG64_OpRsh32Ux8(v) + case OpRsh32x16: + return rewriteValueLOONG64_OpRsh32x16(v) + case OpRsh32x32: + return rewriteValueLOONG64_OpRsh32x32(v) + case OpRsh32x64: + return rewriteValueLOONG64_OpRsh32x64(v) + case OpRsh32x8: + return rewriteValueLOONG64_OpRsh32x8(v) + case OpRsh64Ux16: + return rewriteValueLOONG64_OpRsh64Ux16(v) + case OpRsh64Ux32: + return rewriteValueLOONG64_OpRsh64Ux32(v) + case OpRsh64Ux64: + return rewriteValueLOONG64_OpRsh64Ux64(v) + case OpRsh64Ux8: + return rewriteValueLOONG64_OpRsh64Ux8(v) + case OpRsh64x16: + return rewriteValueLOONG64_OpRsh64x16(v) + case OpRsh64x32: + return rewriteValueLOONG64_OpRsh64x32(v) + case OpRsh64x64: + return rewriteValueLOONG64_OpRsh64x64(v) + case OpRsh64x8: + return rewriteValueLOONG64_OpRsh64x8(v) + case OpRsh8Ux16: + return rewriteValueLOONG64_OpRsh8Ux16(v) + case OpRsh8Ux32: + return rewriteValueLOONG64_OpRsh8Ux32(v) + case OpRsh8Ux64: + return rewriteValueLOONG64_OpRsh8Ux64(v) + case OpRsh8Ux8: + return rewriteValueLOONG64_OpRsh8Ux8(v) + case OpRsh8x16: + return rewriteValueLOONG64_OpRsh8x16(v) + case OpRsh8x32: + return rewriteValueLOONG64_OpRsh8x32(v) + case OpRsh8x64: + return rewriteValueLOONG64_OpRsh8x64(v) + case OpRsh8x8: + return rewriteValueLOONG64_OpRsh8x8(v) + case OpSelect0: + return rewriteValueLOONG64_OpSelect0(v) + case OpSelect1: + return rewriteValueLOONG64_OpSelect1(v) + case OpSignExt16to32: + v.Op = OpLOONG64MOVHreg + return true + case OpSignExt16to64: + v.Op = OpLOONG64MOVHreg + return true + case OpSignExt32to64: + v.Op = OpLOONG64MOVWreg + return true + case OpSignExt8to16: + v.Op = OpLOONG64MOVBreg + return true + case OpSignExt8to32: + v.Op = OpLOONG64MOVBreg + return true + case OpSignExt8to64: + v.Op = OpLOONG64MOVBreg + return true + case OpSlicemask: + return rewriteValueLOONG64_OpSlicemask(v) + case OpSqrt: + v.Op = OpLOONG64SQRTD + return true + case OpSqrt32: + v.Op = OpLOONG64SQRTF + return true + case OpStaticCall: + v.Op = OpLOONG64CALLstatic + return true + case OpStore: + return rewriteValueLOONG64_OpStore(v) + case OpSub16: + v.Op = OpLOONG64SUBV + return true + case OpSub32: + v.Op = OpLOONG64SUBV + return true + case OpSub32F: + v.Op = OpLOONG64SUBF + return true + case OpSub64: + v.Op = OpLOONG64SUBV + return true + case OpSub64F: + v.Op = OpLOONG64SUBD + return true + case OpSub8: + v.Op = OpLOONG64SUBV + return true + case OpSubPtr: + v.Op = OpLOONG64SUBV + return true + case OpTailCall: + v.Op = OpLOONG64CALLtail + return true + case OpTrunc16to8: + v.Op = OpCopy + return true + case OpTrunc32to16: + v.Op = OpCopy + return true + case OpTrunc32to8: + v.Op = OpCopy + return true + case OpTrunc64to16: + v.Op = OpCopy + return true + case OpTrunc64to32: + v.Op = OpCopy + return true + case OpTrunc64to8: + v.Op = OpCopy + return true + case OpWB: + v.Op = OpLOONG64LoweredWB + return true + case OpXor16: + v.Op = OpLOONG64XOR + return true + case OpXor32: + v.Op = OpLOONG64XOR + return true + case OpXor64: + v.Op = OpLOONG64XOR + return true + case OpXor8: + v.Op = OpLOONG64XOR + return true + case OpZero: + return rewriteValueLOONG64_OpZero(v) + case OpZeroExt16to32: + v.Op = OpLOONG64MOVHUreg + return true + case OpZeroExt16to64: + v.Op = OpLOONG64MOVHUreg + return true + case OpZeroExt32to64: + v.Op = OpLOONG64MOVWUreg + return true + case OpZeroExt8to16: + v.Op = OpLOONG64MOVBUreg + return true + case OpZeroExt8to32: + v.Op = OpLOONG64MOVBUreg + return true + case OpZeroExt8to64: + v.Op = OpLOONG64MOVBUreg + return true + } + return false +} +func rewriteValueLOONG64_OpAddr(v *Value) bool { + v_0 := v.Args[0] + // match: (Addr {sym} base) + // result: (MOVVaddr {sym} base) + for { + sym := auxToSym(v.Aux) + base := v_0 + v.reset(OpLOONG64MOVVaddr) + v.Aux = symToAux(sym) + v.AddArg(base) + return true + } +} +func rewriteValueLOONG64_OpAvg64u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Avg64u x y) + // result: (ADDV (SRLVconst (SUBV x y) [1]) y) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64ADDV) + v0 := b.NewValue0(v.Pos, OpLOONG64SRLVconst, t) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SUBV, t) + v1.AddArg2(x, y) + v0.AddArg(v1) + v.AddArg2(v0, y) + return true + } +} +func rewriteValueLOONG64_OpCom16(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Com16 x) + // result: (NOR (MOVVconst [0]) x) + for { + x := v_0 + v.reset(OpLOONG64NOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } +} +func rewriteValueLOONG64_OpCom32(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Com32 x) + // result: (NOR (MOVVconst [0]) x) + for { + x := v_0 + v.reset(OpLOONG64NOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } +} +func rewriteValueLOONG64_OpCom64(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Com64 x) + // result: (NOR (MOVVconst [0]) x) + for { + x := v_0 + v.reset(OpLOONG64NOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } +} +func rewriteValueLOONG64_OpCom8(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Com8 x) + // result: (NOR (MOVVconst [0]) x) + for { + x := v_0 + v.reset(OpLOONG64NOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } +} +func rewriteValueLOONG64_OpConst16(v *Value) bool { + // match: (Const16 [val]) + // result: (MOVVconst [int64(val)]) + for { + val := auxIntToInt16(v.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(val)) + return true + } +} +func rewriteValueLOONG64_OpConst32(v *Value) bool { + // match: (Const32 [val]) + // result: (MOVVconst [int64(val)]) + for { + val := auxIntToInt32(v.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(val)) + return true + } +} +func rewriteValueLOONG64_OpConst32F(v *Value) bool { + // match: (Const32F [val]) + // result: (MOVFconst [float64(val)]) + for { + val := auxIntToFloat32(v.AuxInt) + v.reset(OpLOONG64MOVFconst) + v.AuxInt = float64ToAuxInt(float64(val)) + return true + } +} +func rewriteValueLOONG64_OpConst64(v *Value) bool { + // match: (Const64 [val]) + // result: (MOVVconst [int64(val)]) + for { + val := auxIntToInt64(v.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(val)) + return true + } +} +func rewriteValueLOONG64_OpConst64F(v *Value) bool { + // match: (Const64F [val]) + // result: (MOVDconst [float64(val)]) + for { + val := auxIntToFloat64(v.AuxInt) + v.reset(OpLOONG64MOVDconst) + v.AuxInt = float64ToAuxInt(float64(val)) + return true + } +} +func rewriteValueLOONG64_OpConst8(v *Value) bool { + // match: (Const8 [val]) + // result: (MOVVconst [int64(val)]) + for { + val := auxIntToInt8(v.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(val)) + return true + } +} +func rewriteValueLOONG64_OpConstBool(v *Value) bool { + // match: (ConstBool [t]) + // result: (MOVVconst [int64(b2i(t))]) + for { + t := auxIntToBool(v.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(b2i(t))) + return true + } +} +func rewriteValueLOONG64_OpConstNil(v *Value) bool { + // match: (ConstNil) + // result: (MOVVconst [0]) + for { + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } +} +func rewriteValueLOONG64_OpDiv16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div16 x y) + // result: (Select1 (DIVV (SignExt16to64 x) (SignExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv16u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div16u x y) + // result: (Select1 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div32 x y) + // result: (Select1 (DIVV (SignExt32to64 x) (SignExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv32u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div32u x y) + // result: (Select1 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div64 x y) + // result: (Select1 (DIVV x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv64u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div64u x y) + // result: (Select1 (DIVVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div8 x y) + // result: (Select1 (DIVV (SignExt8to64 x) (SignExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpDiv8u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Div8u x y) + // result: (Select1 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpEq16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Eq16 x y) + // result: (SGTU (MOVVconst [1]) (XOR (ZeroExt16to64 x) (ZeroExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpEq32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Eq32 x y) + // result: (SGTU (MOVVconst [1]) (XOR (ZeroExt32to64 x) (ZeroExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpEq32F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Eq32F x y) + // result: (FPFlagTrue (CMPEQF x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPEQF, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpEq64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Eq64 x y) + // result: (SGTU (MOVVconst [1]) (XOR x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v1.AddArg2(x, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpEq64F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Eq64F x y) + // result: (FPFlagTrue (CMPEQD x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPEQD, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpEq8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Eq8 x y) + // result: (SGTU (MOVVconst [1]) (XOR (ZeroExt8to64 x) (ZeroExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpEqB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (EqB x y) + // result: (XOR (MOVVconst [1]) (XOR x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.Bool) + v1.AddArg2(x, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpEqPtr(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (EqPtr x y) + // result: (SGTU (MOVVconst [1]) (XOR x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v1.AddArg2(x, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpHmul32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Hmul32 x y) + // result: (SRAVconst (Select1 (MULV (SignExt32to64 x) (SignExt32to64 y))) [32]) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAVconst) + v.AuxInt = int64ToAuxInt(32) + v0 := b.NewValue0(v.Pos, OpSelect1, typ.Int64) + v1 := b.NewValue0(v.Pos, OpLOONG64MULV, types.NewTuple(typ.Int64, typ.Int64)) + v2 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpHmul32u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Hmul32u x y) + // result: (SRLVconst (Select1 (MULVU (ZeroExt32to64 x) (ZeroExt32to64 y))) [32]) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SRLVconst) + v.AuxInt = int64ToAuxInt(32) + v0 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpHmul64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Hmul64 x y) + // result: (Select0 (MULV x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64MULV, types.NewTuple(typ.Int64, typ.Int64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpHmul64u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Hmul64u x y) + // result: (Select0 (MULVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpIsInBounds(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (IsInBounds idx len) + // result: (SGTU len idx) + for { + idx := v_0 + len := v_1 + v.reset(OpLOONG64SGTU) + v.AddArg2(len, idx) + return true + } +} +func rewriteValueLOONG64_OpIsNonNil(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (IsNonNil ptr) + // result: (SGTU ptr (MOVVconst [0])) + for { + ptr := v_0 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(ptr, v0) + return true + } +} +func rewriteValueLOONG64_OpIsSliceInBounds(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (IsSliceInBounds idx len) + // result: (XOR (MOVVconst [1]) (SGTU idx len)) + for { + idx := v_0 + len := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v1.AddArg2(idx, len) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLOONG64ADDV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ADDV x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (ADDVconst [c] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + continue + } + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + // match: (ADDV x (NEGV y)) + // result: (SUBV x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64NEGV { + continue + } + y := v_1.Args[0] + v.reset(OpLOONG64SUBV) + v.AddArg2(x, y) + return true + } + break + } + return false +} +func rewriteValueLOONG64_OpLOONG64ADDVconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) + // cond: is32Bit(off1+int64(off2)) + // result: (MOVVaddr [int32(off1)+int32(off2)] {sym} ptr) + for { + off1 := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + if !(is32Bit(off1 + int64(off2))) { + break + } + v.reset(OpLOONG64MOVVaddr) + v.AuxInt = int32ToAuxInt(int32(off1) + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg(ptr) + return true + } + // match: (ADDVconst [0] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (ADDVconst [c] (MOVVconst [d])) + // result: (MOVVconst [c+d]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c + d) + return true + } + // match: (ADDVconst [c] (ADDVconst [d] x)) + // cond: is32Bit(c+d) + // result: (ADDVconst [c+d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ADDVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(c + d)) { + break + } + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(c + d) + v.AddArg(x) + return true + } + // match: (ADDVconst [c] (SUBVconst [d] x)) + // cond: is32Bit(c-d) + // result: (ADDVconst [c-d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64SUBVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(c - d)) { + break + } + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(c - d) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64AND(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (AND x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (ANDconst [c] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + continue + } + v.reset(OpLOONG64ANDconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + // match: (AND x x) + // result: x + for { + x := v_0 + if x != v_1 { + break + } + v.copyOf(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64ANDconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ANDconst [0] _) + // result: (MOVVconst [0]) + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (ANDconst [-1] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != -1 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (ANDconst [c] (MOVVconst [d])) + // result: (MOVVconst [c&d]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c & d) + return true + } + // match: (ANDconst [c] (ANDconst [d] x)) + // result: (ANDconst [c&d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ANDconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + v.reset(OpLOONG64ANDconst) + v.AuxInt = int64ToAuxInt(c & d) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64LoweredAtomicAdd32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) + // cond: is32Bit(c) + // result: (LoweredAtomicAddconst32 [int32(c)] ptr mem) + for { + ptr := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + mem := v_2 + if !(is32Bit(c)) { + break + } + v.reset(OpLOONG64LoweredAtomicAddconst32) + v.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64LoweredAtomicAdd64(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (LoweredAtomicAdd64 ptr (MOVVconst [c]) mem) + // cond: is32Bit(c) + // result: (LoweredAtomicAddconst64 [c] ptr mem) + for { + ptr := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + mem := v_2 + if !(is32Bit(c)) { + break + } + v.reset(OpLOONG64LoweredAtomicAddconst64) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64LoweredAtomicStore32(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (LoweredAtomicStore32 ptr (MOVVconst [0]) mem) + // result: (LoweredAtomicStorezero32 ptr mem) + for { + ptr := v_0 + if v_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + mem := v_2 + v.reset(OpLOONG64LoweredAtomicStorezero32) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64LoweredAtomicStore64(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (LoweredAtomicStore64 ptr (MOVVconst [0]) mem) + // result: (LoweredAtomicStorezero64 ptr mem) + for { + ptr := v_0 + if v_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + mem := v_2 + v.reset(OpLOONG64LoweredAtomicStorezero64) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVBUload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVBUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVBUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBUreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVBUreg x:(MOVBUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(MOVBUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg (MOVVconst [c])) + // result: (MOVVconst [int64(uint8(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint8(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVBload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVBload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVBload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVBreg x:(MOVBload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVBreg x:(MOVBreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVBreg (MOVVconst [c])) + // result: (MOVVconst [int64(int8(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(int8(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVBstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVBreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVBUreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVBUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVHreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVHreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVHUreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVHUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVWreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVBstore [off] {sym} ptr (MOVWUreg x) mem) + // result: (MOVBstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVBstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVBstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVDload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVDload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVDload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVDstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVDstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVDstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVFload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVFload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVFload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVFstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVFstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVFstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVHUload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVHUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVHUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHUreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVHUreg x:(MOVBUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHUreg x:(MOVHUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHUreg x:(MOVBUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHUreg x:(MOVHUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHUreg (MOVVconst [c])) + // result: (MOVVconst [int64(uint16(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint16(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVHload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVHload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVHload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVHreg x:(MOVBload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg x:(MOVBUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg x:(MOVHload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg x:(MOVBreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg x:(MOVBUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg x:(MOVHreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVHreg (MOVVconst [c])) + // result: (MOVVconst [int64(int16(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(int16(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVHstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem) + // result: (MOVHstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVHreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVHstore [off] {sym} ptr (MOVHUreg x) mem) + // result: (MOVHstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVHUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVHstore [off] {sym} ptr (MOVWreg x) mem) + // result: (MOVHstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVHstore [off] {sym} ptr (MOVWUreg x) mem) + // result: (MOVHstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVHstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVHstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVVload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVVload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVVload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVVreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVVreg x) + // cond: x.Uses == 1 + // result: (MOVVnop x) + for { + x := v_0 + if !(x.Uses == 1) { + break + } + v.reset(OpLOONG64MOVVnop) + v.AddArg(x) + return true + } + // match: (MOVVreg (MOVVconst [c])) + // result: (MOVVconst [c]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVVstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVVstorezero [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVVstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVVstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVWUload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVWUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVWUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWUreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVWUreg x:(MOVBUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg x:(MOVHUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg x:(MOVWUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVWUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg x:(MOVBUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg x:(MOVHUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg x:(MOVWUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVWUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWUreg (MOVVconst [c])) + // result: (MOVVconst [int64(uint32(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint32(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVWload [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVWload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVWload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWreg(v *Value) bool { + v_0 := v.Args[0] + // match: (MOVWreg x:(MOVBload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVBUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVHload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVHUload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHUload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVWload _ _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVWload { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVBreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVBUreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVBUreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVHreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVHreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg x:(MOVWreg _)) + // result: (MOVVreg x) + for { + x := v_0 + if x.Op != OpLOONG64MOVWreg { + break + } + v.reset(OpLOONG64MOVVreg) + v.AddArg(x) + return true + } + // match: (MOVWreg (MOVVconst [c])) + // result: (MOVVconst [int64(int32(c))]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(int32(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVWstore [off1+int32(off2)] {sym} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + val := v_1 + mem := v_2 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg3(ptr, val, mem) + return true + } + // match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem) + // result: (MOVWstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + // match: (MOVWstore [off] {sym} ptr (MOVWUreg x) mem) + // result: (MOVWstore [off] {sym} ptr x mem) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + ptr := v_0 + if v_1.Op != OpLOONG64MOVWUreg { + break + } + x := v_1.Args[0] + mem := v_2 + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpLOONG64ADDVconst { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + ptr := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpLOONG64MOVWstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(ptr, mem) + return true + } + // match: (MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // result: (MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpLOONG64MOVVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + ptr := v_0.Args[0] + mem := v_1 + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + break + } + v.reset(OpLOONG64MOVWstorezero) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64NEGV(v *Value) bool { + v_0 := v.Args[0] + // match: (NEGV (MOVVconst [c])) + // result: (MOVVconst [-c]) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(-c) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64NOR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (NOR x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (NORconst [c] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + continue + } + v.reset(OpLOONG64NORconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValueLOONG64_OpLOONG64NORconst(v *Value) bool { + v_0 := v.Args[0] + // match: (NORconst [c] (MOVVconst [d])) + // result: (MOVVconst [^(c|d)]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(^(c | d)) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64OR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (OR x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (ORconst [c] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + continue + } + v.reset(OpLOONG64ORconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + // match: (OR x x) + // result: x + for { + x := v_0 + if x != v_1 { + break + } + v.copyOf(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64ORconst(v *Value) bool { + v_0 := v.Args[0] + // match: (ORconst [0] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (ORconst [-1] _) + // result: (MOVVconst [-1]) + for { + if auxIntToInt64(v.AuxInt) != -1 { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + // match: (ORconst [c] (MOVVconst [d])) + // result: (MOVVconst [c|d]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c | d) + return true + } + // match: (ORconst [c] (ORconst [d] x)) + // cond: is32Bit(c|d) + // result: (ORconst [c|d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ORconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(c | d)) { + break + } + v.reset(OpLOONG64ORconst) + v.AuxInt = int64ToAuxInt(c | d) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64ROTR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROTR x (MOVVconst [c])) + // result: (ROTRconst x [c&31]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpLOONG64ROTRconst) + v.AuxInt = int64ToAuxInt(c & 31) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64ROTRV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROTRV x (MOVVconst [c])) + // result: (ROTRVconst x [c&63]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpLOONG64ROTRVconst) + v.AuxInt = int64ToAuxInt(c & 63) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SGT(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SGT (MOVVconst [c]) x) + // cond: is32Bit(c) + // result: (SGTconst [c] x) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + if !(is32Bit(c)) { + break + } + v.reset(OpLOONG64SGTconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SGTU(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SGTU (MOVVconst [c]) x) + // cond: is32Bit(c) + // result: (SGTUconst [c] x) + for { + if v_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + if !(is32Bit(c)) { + break + } + v.reset(OpLOONG64SGTUconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SGTUconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SGTUconst [c] (MOVVconst [d])) + // cond: uint64(c)>uint64(d) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(uint64(c) > uint64(d)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTUconst [c] (MOVVconst [d])) + // cond: uint64(c)<=uint64(d) + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(uint64(c) <= uint64(d)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTUconst [c] (MOVBUreg _)) + // cond: 0xff < uint64(c) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVBUreg || !(0xff < uint64(c)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTUconst [c] (MOVHUreg _)) + // cond: 0xffff < uint64(c) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVHUreg || !(0xffff < uint64(c)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTUconst [c] (ANDconst [m] _)) + // cond: uint64(m) < uint64(c) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ANDconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + if !(uint64(m) < uint64(c)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTUconst [c] (SRLVconst _ [d])) + // cond: 0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64SRLVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SGTconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SGTconst [c] (MOVVconst [d])) + // cond: c>d + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(c > d) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (MOVVconst [d])) + // cond: c<=d + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(c <= d) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (MOVBreg _)) + // cond: 0x7f < c + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVBreg || !(0x7f < c) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (MOVBreg _)) + // cond: c <= -0x80 + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVBreg || !(c <= -0x80) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (MOVBUreg _)) + // cond: 0xff < c + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVBUreg || !(0xff < c) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (MOVBUreg _)) + // cond: c < 0 + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVBUreg || !(c < 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (MOVHreg _)) + // cond: 0x7fff < c + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVHreg || !(0x7fff < c) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (MOVHreg _)) + // cond: c <= -0x8000 + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVHreg || !(c <= -0x8000) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (MOVHUreg _)) + // cond: 0xffff < c + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVHUreg || !(0xffff < c) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (MOVHUreg _)) + // cond: c < 0 + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVHUreg || !(c < 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (MOVWUreg _)) + // cond: c < 0 + // result: (MOVVconst [0]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVWUreg || !(c < 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SGTconst [c] (ANDconst [m] _)) + // cond: 0 <= m && m < c + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ANDconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + if !(0 <= m && m < c) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (SGTconst [c] (SRLVconst _ [d])) + // cond: 0 <= c && 0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c) + // result: (MOVVconst [1]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64SRLVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + if !(0 <= c && 0 < d && d <= 63 && 0xffffffffffffffff>>uint64(d) < uint64(c)) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(1) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SLLV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SLLV _ (MOVVconst [c])) + // cond: uint64(c)>=64 + // result: (MOVVconst [0]) + for { + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 64) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SLLV x (MOVVconst [c])) + // result: (SLLVconst x [c]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpLOONG64SLLVconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SLLVconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SLLVconst [c] (MOVVconst [d])) + // result: (MOVVconst [d<=64 + // result: (SRAVconst x [63]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 64) { + break + } + v.reset(OpLOONG64SRAVconst) + v.AuxInt = int64ToAuxInt(63) + v.AddArg(x) + return true + } + // match: (SRAV x (MOVVconst [c])) + // result: (SRAVconst x [c]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpLOONG64SRAVconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SRAVconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SRAVconst [c] (MOVVconst [d])) + // result: (MOVVconst [d>>uint64(c)]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(d >> uint64(c)) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SRLV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SRLV _ (MOVVconst [c])) + // cond: uint64(c)>=64 + // result: (MOVVconst [0]) + for { + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 64) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SRLV x (MOVVconst [c])) + // result: (SRLVconst x [c]) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpLOONG64SRLVconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SRLVconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SRLVconst [c] (MOVVconst [d])) + // result: (MOVVconst [int64(uint64(d)>>uint64(c))]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint64(d) >> uint64(c))) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SUBV(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SUBV x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (SUBVconst [c] x) + for { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + break + } + v.reset(OpLOONG64SUBVconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + // match: (SUBV x x) + // result: (MOVVconst [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SUBV (MOVVconst [0]) x) + // result: (NEGV x) + for { + if v_0.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_1 + v.reset(OpLOONG64NEGV) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64SUBVconst(v *Value) bool { + v_0 := v.Args[0] + // match: (SUBVconst [0] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (SUBVconst [c] (MOVVconst [d])) + // result: (MOVVconst [d-c]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(d - c) + return true + } + // match: (SUBVconst [c] (SUBVconst [d] x)) + // cond: is32Bit(-c-d) + // result: (ADDVconst [-c-d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64SUBVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(-c - d)) { + break + } + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(-c - d) + v.AddArg(x) + return true + } + // match: (SUBVconst [c] (ADDVconst [d] x)) + // cond: is32Bit(-c+d) + // result: (ADDVconst [-c+d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64ADDVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(-c + d)) { + break + } + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(-c + d) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64XOR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (XOR x (MOVVconst [c])) + // cond: is32Bit(c) + // result: (XORconst [c] x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_1.AuxInt) + if !(is32Bit(c)) { + continue + } + v.reset(OpLOONG64XORconst) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(x) + return true + } + break + } + // match: (XOR x x) + // result: (MOVVconst [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} +func rewriteValueLOONG64_OpLOONG64XORconst(v *Value) bool { + v_0 := v.Args[0] + // match: (XORconst [0] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (XORconst [-1] x) + // result: (NORconst [0] x) + for { + if auxIntToInt64(v.AuxInt) != -1 { + break + } + x := v_0 + v.reset(OpLOONG64NORconst) + v.AuxInt = int64ToAuxInt(0) + v.AddArg(x) + return true + } + // match: (XORconst [c] (MOVVconst [d])) + // result: (MOVVconst [c^d]) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c ^ d) + return true + } + // match: (XORconst [c] (XORconst [d] x)) + // cond: is32Bit(c^d) + // result: (XORconst [c^d] x) + for { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpLOONG64XORconst { + break + } + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + if !(is32Bit(c ^ d)) { + break + } + v.reset(OpLOONG64XORconst) + v.AuxInt = int64ToAuxInt(c ^ d) + v.AddArg(x) + return true + } + return false +} +func rewriteValueLOONG64_OpLeq16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq16 x y) + // result: (XOR (MOVVconst [1]) (SGT (SignExt16to64 x) (SignExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGT, typ.Bool) + v2 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq16U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq16U x y) + // result: (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq32 x y) + // result: (XOR (MOVVconst [1]) (SGT (SignExt32to64 x) (SignExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGT, typ.Bool) + v2 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq32F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Leq32F x y) + // result: (FPFlagTrue (CMPGEF y x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPGEF, types.TypeFlags) + v0.AddArg2(y, x) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpLeq32U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq32U x y) + // result: (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq64 x y) + // result: (XOR (MOVVconst [1]) (SGT x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGT, typ.Bool) + v1.AddArg2(x, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq64F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Leq64F x y) + // result: (FPFlagTrue (CMPGED y x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPGED, types.TypeFlags) + v0.AddArg2(y, x) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpLeq64U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq64U x y) + // result: (XOR (MOVVconst [1]) (SGTU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v1.AddArg2(x, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq8 x y) + // result: (XOR (MOVVconst [1]) (SGT (SignExt8to64 x) (SignExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGT, typ.Bool) + v2 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLeq8U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Leq8U x y) + // result: (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64XOR) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v2.AddArg(x) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less16 x y) + // result: (SGT (SignExt16to64 y) (SignExt16to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGT) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess16U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less16U x y) + // result: (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less32 x y) + // result: (SGT (SignExt32to64 y) (SignExt32to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGT) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess32F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Less32F x y) + // result: (FPFlagTrue (CMPGTF y x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPGTF, types.TypeFlags) + v0.AddArg2(y, x) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpLess32U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less32U x y) + // result: (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Less64 x y) + // result: (SGT y x) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGT) + v.AddArg2(y, x) + return true + } +} +func rewriteValueLOONG64_OpLess64F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Less64F x y) + // result: (FPFlagTrue (CMPGTD y x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagTrue) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPGTD, types.TypeFlags) + v0.AddArg2(y, x) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpLess64U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Less64U x y) + // result: (SGTU y x) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v.AddArg2(y, x) + return true + } +} +func rewriteValueLOONG64_OpLess8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less8 x y) + // result: (SGT (SignExt8to64 y) (SignExt8to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGT) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLess8U(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Less8U x y) + // result: (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(y) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v1.AddArg(x) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpLoad(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Load ptr mem) + // cond: t.IsBoolean() + // result: (MOVBUload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(t.IsBoolean()) { + break + } + v.reset(OpLOONG64MOVBUload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is8BitInt(t) && isSigned(t)) + // result: (MOVBload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is8BitInt(t) && isSigned(t)) { + break + } + v.reset(OpLOONG64MOVBload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is8BitInt(t) && !isSigned(t)) + // result: (MOVBUload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is8BitInt(t) && !isSigned(t)) { + break + } + v.reset(OpLOONG64MOVBUload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is16BitInt(t) && isSigned(t)) + // result: (MOVHload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is16BitInt(t) && isSigned(t)) { + break + } + v.reset(OpLOONG64MOVHload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is16BitInt(t) && !isSigned(t)) + // result: (MOVHUload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is16BitInt(t) && !isSigned(t)) { + break + } + v.reset(OpLOONG64MOVHUload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is32BitInt(t) && isSigned(t)) + // result: (MOVWload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is32BitInt(t) && isSigned(t)) { + break + } + v.reset(OpLOONG64MOVWload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is32BitInt(t) && !isSigned(t)) + // result: (MOVWUload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is32BitInt(t) && !isSigned(t)) { + break + } + v.reset(OpLOONG64MOVWUload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: (is64BitInt(t) || isPtr(t)) + // result: (MOVVload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is64BitInt(t) || isPtr(t)) { + break + } + v.reset(OpLOONG64MOVVload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: is32BitFloat(t) + // result: (MOVFload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is32BitFloat(t)) { + break + } + v.reset(OpLOONG64MOVFload) + v.AddArg2(ptr, mem) + return true + } + // match: (Load ptr mem) + // cond: is64BitFloat(t) + // result: (MOVDload ptr mem) + for { + t := v.Type + ptr := v_0 + mem := v_1 + if !(is64BitFloat(t)) { + break + } + v.reset(OpLOONG64MOVDload) + v.AddArg2(ptr, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpLocalAddr(v *Value) bool { + v_0 := v.Args[0] + // match: (LocalAddr {sym} base _) + // result: (MOVVaddr {sym} base) + for { + sym := auxToSym(v.Aux) + base := v_0 + v.reset(OpLOONG64MOVVaddr) + v.Aux = symToAux(sym) + v.AddArg(base) + return true + } +} +func rewriteValueLOONG64_OpLsh16x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh16x16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh16x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh16x32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh16x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh16x64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v3.AddArg2(x, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpLsh16x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh16x8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh32x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh32x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh32x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v3.AddArg2(x, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpLsh32x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh32x8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh64x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh64x16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh64x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh64x32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh64x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh64x64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v3.AddArg2(x, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpLsh64x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh64x8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh8x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh8x16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SLLV x (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh8x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh8x32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SLLV x (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpLsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh8x64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SLLV x y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v3.AddArg2(x, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpLsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Lsh8x8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SLLV x (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SLLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpMod16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod16 x y) + // result: (Select0 (DIVV (SignExt16to64 x) (SignExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod16u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod16u x y) + // result: (Select0 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod32 x y) + // result: (Select0 (DIVV (SignExt32to64 x) (SignExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod32u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod32u x y) + // result: (Select0 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod64 x y) + // result: (Select0 (DIVV x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod64u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod64u x y) + // result: (Select0 (DIVVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod8 x y) + // result: (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVV, types.NewTuple(typ.Int64, typ.Int64)) + v1 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMod8u(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mod8u x y) + // result: (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y))) + for { + x := v_0 + y := v_1 + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpLOONG64DIVVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMove(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + typ := &b.Func.Config.Types + // match: (Move [0] _ _ mem) + // result: mem + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + mem := v_2 + v.copyOf(mem) + return true + } + // match: (Move [1] dst src mem) + // result: (MOVBstore dst (MOVBload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 1 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } + // match: (Move [2] {t} dst src mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore dst (MOVHload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 2 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } + // match: (Move [2] dst src mem) + // result: (MOVBstore [1] dst (MOVBload [1] src mem) (MOVBstore dst (MOVBload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 2 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v0.AuxInt = int32ToAuxInt(1) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [4] {t} dst src mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore dst (MOVWload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } + // match: (Move [4] {t} dst src mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [2] dst (MOVHload [2] src mem) (MOVHstore dst (MOVHload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v0.AuxInt = int32ToAuxInt(2) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [4] dst src mem) + // result: (MOVBstore [3] dst (MOVBload [3] src mem) (MOVBstore [2] dst (MOVBload [2] src mem) (MOVBstore [1] dst (MOVBload [1] src mem) (MOVBstore dst (MOVBload src mem) mem)))) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(3) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v0.AuxInt = int32ToAuxInt(3) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(2) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v2.AuxInt = int32ToAuxInt(2) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v3.AuxInt = int32ToAuxInt(1) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v4.AuxInt = int32ToAuxInt(1) + v4.AddArg2(src, mem) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v6 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v6.AddArg2(src, mem) + v5.AddArg3(dst, v6, mem) + v3.AddArg3(dst, v4, v5) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [8] {t} dst src mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore dst (MOVVload src mem) mem) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v0.AddArg2(src, mem) + v.AddArg3(dst, v0, mem) + return true + } + // match: (Move [8] {t} dst src mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore [4] dst (MOVWload [4] src mem) (MOVWstore dst (MOVWload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(4) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v0.AuxInt = int32ToAuxInt(4) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [8] {t} dst src mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [6] dst (MOVHload [6] src mem) (MOVHstore [4] dst (MOVHload [4] src mem) (MOVHstore [2] dst (MOVHload [2] src mem) (MOVHstore dst (MOVHload src mem) mem)))) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(6) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v0.AuxInt = int32ToAuxInt(6) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(4) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v2.AuxInt = int32ToAuxInt(4) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v3.AuxInt = int32ToAuxInt(2) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v4.AuxInt = int32ToAuxInt(2) + v4.AddArg2(src, mem) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v6 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v6.AddArg2(src, mem) + v5.AddArg3(dst, v6, mem) + v3.AddArg3(dst, v4, v5) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [3] dst src mem) + // result: (MOVBstore [2] dst (MOVBload [2] src mem) (MOVBstore [1] dst (MOVBload [1] src mem) (MOVBstore dst (MOVBload src mem) mem))) + for { + if auxIntToInt64(v.AuxInt) != 3 { + break + } + dst := v_0 + src := v_1 + mem := v_2 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v0.AuxInt = int32ToAuxInt(2) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(1) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v2.AuxInt = int32ToAuxInt(1) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVBload, typ.Int8) + v4.AddArg2(src, mem) + v3.AddArg3(dst, v4, mem) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [6] {t} dst src mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [4] dst (MOVHload [4] src mem) (MOVHstore [2] dst (MOVHload [2] src mem) (MOVHstore dst (MOVHload src mem) mem))) + for { + if auxIntToInt64(v.AuxInt) != 6 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(4) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v0.AuxInt = int32ToAuxInt(4) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(2) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v2.AuxInt = int32ToAuxInt(2) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVHload, typ.Int16) + v4.AddArg2(src, mem) + v3.AddArg3(dst, v4, mem) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [12] {t} dst src mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore [8] dst (MOVWload [8] src mem) (MOVWstore [4] dst (MOVWload [4] src mem) (MOVWstore dst (MOVWload src mem) mem))) + for { + if auxIntToInt64(v.AuxInt) != 12 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(8) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v0.AuxInt = int32ToAuxInt(8) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(4) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v2.AuxInt = int32ToAuxInt(4) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVWload, typ.Int32) + v4.AddArg2(src, mem) + v3.AddArg3(dst, v4, mem) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [16] {t} dst src mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore [8] dst (MOVVload [8] src mem) (MOVVstore dst (MOVVload src mem) mem)) + for { + if auxIntToInt64(v.AuxInt) != 16 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(8) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(8) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v2.AddArg2(src, mem) + v1.AddArg3(dst, v2, mem) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [24] {t} dst src mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore [16] dst (MOVVload [16] src mem) (MOVVstore [8] dst (MOVVload [8] src mem) (MOVVstore dst (MOVVload src mem) mem))) + for { + if auxIntToInt64(v.AuxInt) != 24 { + break + } + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(16) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v0.AuxInt = int32ToAuxInt(16) + v0.AddArg2(src, mem) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(8) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v2.AuxInt = int32ToAuxInt(8) + v2.AddArg2(src, mem) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVload, typ.UInt64) + v4.AddArg2(src, mem) + v3.AddArg3(dst, v4, mem) + v1.AddArg3(dst, v2, v3) + v.AddArg3(dst, v0, v1) + return true + } + // match: (Move [s] {t} dst src mem) + // cond: s%8 == 0 && s >= 24 && s <= 8*128 && t.Alignment()%8 == 0 && !config.noDuffDevice && logLargeCopy(v, s) + // result: (DUFFCOPY [16 * (128 - s/8)] dst src mem) + for { + s := auxIntToInt64(v.AuxInt) + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(s%8 == 0 && s >= 24 && s <= 8*128 && t.Alignment()%8 == 0 && !config.noDuffDevice && logLargeCopy(v, s)) { + break + } + v.reset(OpLOONG64DUFFCOPY) + v.AuxInt = int64ToAuxInt(16 * (128 - s/8)) + v.AddArg3(dst, src, mem) + return true + } + // match: (Move [s] {t} dst src mem) + // cond: s > 24 && logLargeCopy(v, s) || t.Alignment()%8 != 0 + // result: (LoweredMove [t.Alignment()] dst src (ADDVconst src [s-moveSize(t.Alignment(), config)]) mem) + for { + s := auxIntToInt64(v.AuxInt) + t := auxToType(v.Aux) + dst := v_0 + src := v_1 + mem := v_2 + if !(s > 24 && logLargeCopy(v, s) || t.Alignment()%8 != 0) { + break + } + v.reset(OpLOONG64LoweredMove) + v.AuxInt = int64ToAuxInt(t.Alignment()) + v0 := b.NewValue0(v.Pos, OpLOONG64ADDVconst, src.Type) + v0.AuxInt = int64ToAuxInt(s - moveSize(t.Alignment(), config)) + v0.AddArg(src) + v.AddArg4(dst, src, v0, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpMul16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mul16 x y) + // result: (Select1 (MULVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMul32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mul32 x y) + // result: (Select1 (MULVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMul64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mul64 x y) + // result: (Select1 (MULVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpMul8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Mul8 x y) + // result: (Select1 (MULVU x y)) + for { + x := v_0 + y := v_1 + v.reset(OpSelect1) + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpNeq16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Neq16 x y) + // result: (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to64 y)) (MOVVconst [0])) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpZeroExt16to32, typ.UInt32) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpNeq32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Neq32 x y) + // result: (SGTU (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)) (MOVVconst [0])) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpNeq32F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Neq32F x y) + // result: (FPFlagFalse (CMPEQF x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagFalse) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPEQF, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpNeq64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Neq64 x y) + // result: (SGTU (XOR x y) (MOVVconst [0])) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v1.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpNeq64F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Neq64F x y) + // result: (FPFlagFalse (CMPEQD x y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64FPFlagFalse) + v0 := b.NewValue0(v.Pos, OpLOONG64CMPEQD, types.TypeFlags) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpNeq8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Neq8 x y) + // result: (SGTU (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)) (MOVVconst [0])) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v1.AddArg(x) + v2 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v2.AddArg(y) + v0.AddArg2(v1, v2) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpNeqPtr(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (NeqPtr x y) + // result: (SGTU (XOR x y) (MOVVconst [0])) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64SGTU) + v0 := b.NewValue0(v.Pos, OpLOONG64XOR, typ.UInt64) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v1.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpNot(v *Value) bool { + v_0 := v.Args[0] + // match: (Not x) + // result: (XORconst [1] x) + for { + x := v_0 + v.reset(OpLOONG64XORconst) + v.AuxInt = int64ToAuxInt(1) + v.AddArg(x) + return true + } +} +func rewriteValueLOONG64_OpOffPtr(v *Value) bool { + v_0 := v.Args[0] + // match: (OffPtr [off] ptr:(SP)) + // result: (MOVVaddr [int32(off)] ptr) + for { + off := auxIntToInt64(v.AuxInt) + ptr := v_0 + if ptr.Op != OpSP { + break + } + v.reset(OpLOONG64MOVVaddr) + v.AuxInt = int32ToAuxInt(int32(off)) + v.AddArg(ptr) + return true + } + // match: (OffPtr [off] ptr) + // result: (ADDVconst [off] ptr) + for { + off := auxIntToInt64(v.AuxInt) + ptr := v_0 + v.reset(OpLOONG64ADDVconst) + v.AuxInt = int64ToAuxInt(off) + v.AddArg(ptr) + return true + } +} +func rewriteValueLOONG64_OpPanicBounds(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (PanicBounds [kind] x y mem) + // cond: boundsABI(kind) == 0 + // result: (LoweredPanicBoundsA [kind] x y mem) + for { + kind := auxIntToInt64(v.AuxInt) + x := v_0 + y := v_1 + mem := v_2 + if !(boundsABI(kind) == 0) { + break + } + v.reset(OpLOONG64LoweredPanicBoundsA) + v.AuxInt = int64ToAuxInt(kind) + v.AddArg3(x, y, mem) + return true + } + // match: (PanicBounds [kind] x y mem) + // cond: boundsABI(kind) == 1 + // result: (LoweredPanicBoundsB [kind] x y mem) + for { + kind := auxIntToInt64(v.AuxInt) + x := v_0 + y := v_1 + mem := v_2 + if !(boundsABI(kind) == 1) { + break + } + v.reset(OpLOONG64LoweredPanicBoundsB) + v.AuxInt = int64ToAuxInt(kind) + v.AddArg3(x, y, mem) + return true + } + // match: (PanicBounds [kind] x y mem) + // cond: boundsABI(kind) == 2 + // result: (LoweredPanicBoundsC [kind] x y mem) + for { + kind := auxIntToInt64(v.AuxInt) + x := v_0 + y := v_1 + mem := v_2 + if !(boundsABI(kind) == 2) { + break + } + v.reset(OpLOONG64LoweredPanicBoundsC) + v.AuxInt = int64ToAuxInt(kind) + v.AddArg3(x, y, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpRotateLeft16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (RotateLeft16 x (MOVVconst [c])) + // result: (Or16 (Lsh16x64 x (MOVVconst [c&15])) (Rsh16Ux64 x (MOVVconst [-c&15]))) + for { + t := v.Type + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpOr16) + v0 := b.NewValue0(v.Pos, OpLsh16x64, t) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v1.AuxInt = int64ToAuxInt(c & 15) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpRsh16Ux64, t) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(-c & 15) + v2.AddArg2(x, v3) + v.AddArg2(v0, v2) + return true + } + return false +} +func rewriteValueLOONG64_OpRotateLeft32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (RotateLeft32 x y) + // result: (ROTR x (NEGV y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64ROTR) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRotateLeft64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (RotateLeft64 x y) + // result: (ROTRV x (NEGV y)) + for { + x := v_0 + y := v_1 + v.reset(OpLOONG64ROTRV) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRotateLeft8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (RotateLeft8 x (MOVVconst [c])) + // result: (Or8 (Lsh8x64 x (MOVVconst [c&7])) (Rsh8Ux64 x (MOVVconst [-c&7]))) + for { + t := v.Type + x := v_0 + if v_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpOr8) + v0 := b.NewValue0(v.Pos, OpLsh8x64, t) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v1.AuxInt = int64ToAuxInt(c & 7) + v0.AddArg2(x, v1) + v2 := b.NewValue0(v.Pos, OpRsh8Ux64, t) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(-c & 7) + v2.AddArg2(x, v3) + v.AddArg2(v0, v2) + return true + } + return false +} +func rewriteValueLOONG64_OpRsh16Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16Ux16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh16Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16Ux32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh16Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16Ux64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt16to64 x) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(x) + v3.AddArg2(v4, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpRsh16Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16Ux8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt16to64 x) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh16x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16x16 x y) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh16x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16x32 x y) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh16x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16x64 x y) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v3.AddArg2(y, v4) + v2.AddArg(v3) + v1.AddArg2(v2, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh16x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16x8 x y) + // result: (SRAV (SignExt16to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh32Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh32Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh32Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt32to64 x) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(x) + v3.AddArg2(v4, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpRsh32Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt32to64 x) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh32x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x16 x y) + // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh32x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x32 x y) + // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh32x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x64 x y) + // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v3.AddArg2(y, v4) + v2.AddArg(v3) + v1.AddArg2(v2, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh32x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x8 x y) + // result: (SRAV (SignExt32to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh64Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64Ux16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV x (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh64Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64Ux32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV x (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh64Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64Ux64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV x y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v3.AddArg2(x, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpRsh64Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64Ux8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV x (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4.AddArg2(x, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh64x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64x16 x y) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v2.AddArg2(v3, v4) + v1.AddArg(v2) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRsh64x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64x32 x y) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v2.AddArg2(v3, v4) + v1.AddArg(v2) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRsh64x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64x64 x y) + // result: (SRAV x (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v3.AuxInt = int64ToAuxInt(63) + v2.AddArg2(y, v3) + v1.AddArg(v2) + v0.AddArg2(v1, y) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRsh64x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64x8 x y) + // result: (SRAV x (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v1 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v2 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v2.AddArg2(v3, v4) + v1.AddArg(v2) + v0.AddArg2(v1, v3) + v.AddArg2(x, v0) + return true + } +} +func rewriteValueLOONG64_OpRsh8Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8Ux16 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt16to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh8Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8Ux32 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt32to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh8Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8Ux64 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) y)) (SRLV (ZeroExt8to64 x) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v1.AddArg2(v2, y) + v0.AddArg(v1) + v3 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(x) + v3.AddArg2(v4, y) + v.AddArg2(v0, v3) + return true + } +} +func rewriteValueLOONG64_OpRsh8Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8Ux8 x y) + // result: (AND (NEGV (SGTU (MOVVconst [64]) (ZeroExt8to64 y))) (SRLV (ZeroExt8to64 x) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64AND) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v1 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(64) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) + v1.AddArg2(v2, v3) + v0.AddArg(v1) + v4 := b.NewValue0(v.Pos, OpLOONG64SRLV, t) + v5 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v5.AddArg(x) + v4.AddArg2(v5, v3) + v.AddArg2(v0, v4) + return true + } +} +func rewriteValueLOONG64_OpRsh8x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x16 x y) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt16to64 y) (MOVVconst [63]))) (ZeroExt16to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh8x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x32 x y) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt32to64 y) (MOVVconst [63]))) (ZeroExt32to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh8x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x64 x y) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU y (MOVVconst [63]))) y)) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v4.AuxInt = int64ToAuxInt(63) + v3.AddArg2(y, v4) + v2.AddArg(v3) + v1.AddArg2(v2, y) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpRsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x8 x y) + // result: (SRAV (SignExt8to64 x) (OR (NEGV (SGTU (ZeroExt8to64 y) (MOVVconst [63]))) (ZeroExt8to64 y))) + for { + t := v.Type + x := v_0 + y := v_1 + v.reset(OpLOONG64SRAV) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpLOONG64OR, t) + v2 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v3 := b.NewValue0(v.Pos, OpLOONG64SGTU, typ.Bool) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v5.AuxInt = int64ToAuxInt(63) + v3.AddArg2(v4, v5) + v2.AddArg(v3) + v1.AddArg2(v2, v4) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValueLOONG64_OpSelect0(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select0 (Mul64uover x y)) + // result: (Select1 (MULVU x y)) + for { + if v_0.Op != OpMul64uover { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpSelect1) + v.Type = typ.UInt64 + v0 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v0.AddArg2(x, y) + v.AddArg(v0) + return true + } + // match: (Select0 (DIVVU _ (MOVVconst [1]))) + // result: (MOVVconst [0]) + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 1 { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (Select0 (DIVVU x (MOVVconst [c]))) + // cond: isPowerOfTwo64(c) + // result: (ANDconst [c-1] x) + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_1.AuxInt) + if !(isPowerOfTwo64(c)) { + break + } + v.reset(OpLOONG64ANDconst) + v.AuxInt = int64ToAuxInt(c - 1) + v.AddArg(x) + return true + } + // match: (Select0 (DIVV (MOVVconst [c]) (MOVVconst [d]))) + // cond: d != 0 + // result: (MOVVconst [c%d]) + for { + if v_0.Op != OpLOONG64DIVV { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_0.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0_1.AuxInt) + if !(d != 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c % d) + return true + } + // match: (Select0 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) + // cond: d != 0 + // result: (MOVVconst [int64(uint64(c)%uint64(d))]) + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_0.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0_1.AuxInt) + if !(d != 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint64(c) % uint64(d))) + return true + } + return false +} +func rewriteValueLOONG64_OpSelect1(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select1 (Mul64uover x y)) + // result: (SGTU (Select0 (MULVU x y)) (MOVVconst [0])) + for { + if v_0.Op != OpMul64uover { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpLOONG64SGTU) + v.Type = typ.Bool + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpLOONG64MULVU, types.NewTuple(typ.UInt64, typ.UInt64)) + v1.AddArg2(x, y) + v0.AddArg(v1) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v2.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, v2) + return true + } + // match: (Select1 (MULVU x (MOVVconst [-1]))) + // result: (NEGV x) + for { + if v_0.Op != OpLOONG64MULVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != -1 { + continue + } + v.reset(OpLOONG64NEGV) + v.AddArg(x) + return true + } + break + } + // match: (Select1 (MULVU _ (MOVVconst [0]))) + // result: (MOVVconst [0]) + for { + if v_0.Op != OpLOONG64MULVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 0 { + continue + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + break + } + // match: (Select1 (MULVU x (MOVVconst [1]))) + // result: x + for { + if v_0.Op != OpLOONG64MULVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 1 { + continue + } + v.copyOf(x) + return true + } + break + } + // match: (Select1 (MULVU x (MOVVconst [c]))) + // cond: isPowerOfTwo64(c) + // result: (SLLVconst [log64(c)] x) + for { + if v_0.Op != OpLOONG64MULVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + if v_0_1.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_0_1.AuxInt) + if !(isPowerOfTwo64(c)) { + continue + } + v.reset(OpLOONG64SLLVconst) + v.AuxInt = int64ToAuxInt(log64(c)) + v.AddArg(x) + return true + } + break + } + // match: (Select1 (DIVVU x (MOVVconst [1]))) + // result: x + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 1 { + break + } + v.copyOf(x) + return true + } + // match: (Select1 (DIVVU x (MOVVconst [c]))) + // cond: isPowerOfTwo64(c) + // result: (SRLVconst [log64(c)] x) + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_1.AuxInt) + if !(isPowerOfTwo64(c)) { + break + } + v.reset(OpLOONG64SRLVconst) + v.AuxInt = int64ToAuxInt(log64(c)) + v.AddArg(x) + return true + } + // match: (Select1 (MULVU (MOVVconst [c]) (MOVVconst [d]))) + // result: (MOVVconst [c*d]) + for { + if v_0.Op != OpLOONG64MULVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpLOONG64MOVVconst { + continue + } + c := auxIntToInt64(v_0_0.AuxInt) + if v_0_1.Op != OpLOONG64MOVVconst { + continue + } + d := auxIntToInt64(v_0_1.AuxInt) + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c * d) + return true + } + break + } + // match: (Select1 (DIVV (MOVVconst [c]) (MOVVconst [d]))) + // cond: d != 0 + // result: (MOVVconst [c/d]) + for { + if v_0.Op != OpLOONG64DIVV { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_0.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0_1.AuxInt) + if !(d != 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(c / d) + return true + } + // match: (Select1 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) + // cond: d != 0 + // result: (MOVVconst [int64(uint64(c)/uint64(d))]) + for { + if v_0.Op != OpLOONG64DIVVU { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpLOONG64MOVVconst { + break + } + c := auxIntToInt64(v_0_0.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst { + break + } + d := auxIntToInt64(v_0_1.AuxInt) + if !(d != 0) { + break + } + v.reset(OpLOONG64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(uint64(c) / uint64(d))) + return true + } + return false +} +func rewriteValueLOONG64_OpSlicemask(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + // match: (Slicemask x) + // result: (SRAVconst (NEGV x) [63]) + for { + t := v.Type + x := v_0 + v.reset(OpLOONG64SRAVconst) + v.AuxInt = int64ToAuxInt(63) + v0 := b.NewValue0(v.Pos, OpLOONG64NEGV, t) + v0.AddArg(x) + v.AddArg(v0) + return true + } +} +func rewriteValueLOONG64_OpStore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Store {t} ptr val mem) + // cond: t.Size() == 1 + // result: (MOVBstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 1) { + break + } + v.reset(OpLOONG64MOVBstore) + v.AddArg3(ptr, val, mem) + return true + } + // match: (Store {t} ptr val mem) + // cond: t.Size() == 2 + // result: (MOVHstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 2) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AddArg3(ptr, val, mem) + return true + } + // match: (Store {t} ptr val mem) + // cond: t.Size() == 4 && !is32BitFloat(val.Type) + // result: (MOVWstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 4 && !is32BitFloat(val.Type)) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AddArg3(ptr, val, mem) + return true + } + // match: (Store {t} ptr val mem) + // cond: t.Size() == 8 && !is64BitFloat(val.Type) + // result: (MOVVstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 8 && !is64BitFloat(val.Type)) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AddArg3(ptr, val, mem) + return true + } + // match: (Store {t} ptr val mem) + // cond: t.Size() == 4 && is32BitFloat(val.Type) + // result: (MOVFstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 4 && is32BitFloat(val.Type)) { + break + } + v.reset(OpLOONG64MOVFstore) + v.AddArg3(ptr, val, mem) + return true + } + // match: (Store {t} ptr val mem) + // cond: t.Size() == 8 && is64BitFloat(val.Type) + // result: (MOVDstore ptr val mem) + for { + t := auxToType(v.Aux) + ptr := v_0 + val := v_1 + mem := v_2 + if !(t.Size() == 8 && is64BitFloat(val.Type)) { + break + } + v.reset(OpLOONG64MOVDstore) + v.AddArg3(ptr, val, mem) + return true + } + return false +} +func rewriteValueLOONG64_OpZero(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + typ := &b.Func.Config.Types + // match: (Zero [0] _ mem) + // result: mem + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + mem := v_1 + v.copyOf(mem) + return true + } + // match: (Zero [1] ptr mem) + // result: (MOVBstore ptr (MOVVconst [0]) mem) + for { + if auxIntToInt64(v.AuxInt) != 1 { + break + } + ptr := v_0 + mem := v_1 + v.reset(OpLOONG64MOVBstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (Zero [2] {t} ptr mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore ptr (MOVVconst [0]) mem) + for { + if auxIntToInt64(v.AuxInt) != 2 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (Zero [2] ptr mem) + // result: (MOVBstore [1] ptr (MOVVconst [0]) (MOVBstore [0] ptr (MOVVconst [0]) mem)) + for { + if auxIntToInt64(v.AuxInt) != 2 { + break + } + ptr := v_0 + mem := v_1 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(0) + v1.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [4] {t} ptr mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore ptr (MOVVconst [0]) mem) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (Zero [4] {t} ptr mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [2] ptr (MOVVconst [0]) (MOVHstore [0] ptr (MOVVconst [0]) mem)) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(0) + v1.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [4] ptr mem) + // result: (MOVBstore [3] ptr (MOVVconst [0]) (MOVBstore [2] ptr (MOVVconst [0]) (MOVBstore [1] ptr (MOVVconst [0]) (MOVBstore [0] ptr (MOVVconst [0]) mem)))) + for { + if auxIntToInt64(v.AuxInt) != 4 { + break + } + ptr := v_0 + mem := v_1 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(3) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(2) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(1) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v3.AuxInt = int32ToAuxInt(0) + v3.AddArg3(ptr, v0, mem) + v2.AddArg3(ptr, v0, v3) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [8] {t} ptr mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore ptr (MOVVconst [0]) mem) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg3(ptr, v0, mem) + return true + } + // match: (Zero [8] {t} ptr mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore [4] ptr (MOVVconst [0]) (MOVWstore [0] ptr (MOVVconst [0]) mem)) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(4) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(0) + v1.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [8] {t} ptr mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [6] ptr (MOVVconst [0]) (MOVHstore [4] ptr (MOVVconst [0]) (MOVHstore [2] ptr (MOVVconst [0]) (MOVHstore [0] ptr (MOVVconst [0]) mem)))) + for { + if auxIntToInt64(v.AuxInt) != 8 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(6) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(4) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(2) + v3 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v3.AuxInt = int32ToAuxInt(0) + v3.AddArg3(ptr, v0, mem) + v2.AddArg3(ptr, v0, v3) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [3] ptr mem) + // result: (MOVBstore [2] ptr (MOVVconst [0]) (MOVBstore [1] ptr (MOVVconst [0]) (MOVBstore [0] ptr (MOVVconst [0]) mem))) + for { + if auxIntToInt64(v.AuxInt) != 3 { + break + } + ptr := v_0 + mem := v_1 + v.reset(OpLOONG64MOVBstore) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(1) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVBstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(0) + v2.AddArg3(ptr, v0, mem) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [6] {t} ptr mem) + // cond: t.Alignment()%2 == 0 + // result: (MOVHstore [4] ptr (MOVVconst [0]) (MOVHstore [2] ptr (MOVVconst [0]) (MOVHstore [0] ptr (MOVVconst [0]) mem))) + for { + if auxIntToInt64(v.AuxInt) != 6 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%2 == 0) { + break + } + v.reset(OpLOONG64MOVHstore) + v.AuxInt = int32ToAuxInt(4) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(2) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVHstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(0) + v2.AddArg3(ptr, v0, mem) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [12] {t} ptr mem) + // cond: t.Alignment()%4 == 0 + // result: (MOVWstore [8] ptr (MOVVconst [0]) (MOVWstore [4] ptr (MOVVconst [0]) (MOVWstore [0] ptr (MOVVconst [0]) mem))) + for { + if auxIntToInt64(v.AuxInt) != 12 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%4 == 0) { + break + } + v.reset(OpLOONG64MOVWstore) + v.AuxInt = int32ToAuxInt(8) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(4) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVWstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(0) + v2.AddArg3(ptr, v0, mem) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [16] {t} ptr mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore [8] ptr (MOVVconst [0]) (MOVVstore [0] ptr (MOVVconst [0]) mem)) + for { + if auxIntToInt64(v.AuxInt) != 16 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(8) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(0) + v1.AddArg3(ptr, v0, mem) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [24] {t} ptr mem) + // cond: t.Alignment()%8 == 0 + // result: (MOVVstore [16] ptr (MOVVconst [0]) (MOVVstore [8] ptr (MOVVconst [0]) (MOVVstore [0] ptr (MOVVconst [0]) mem))) + for { + if auxIntToInt64(v.AuxInt) != 24 { + break + } + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(t.Alignment()%8 == 0) { + break + } + v.reset(OpLOONG64MOVVstore) + v.AuxInt = int32ToAuxInt(16) + v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) + v0.AuxInt = int64ToAuxInt(0) + v1 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v1.AuxInt = int32ToAuxInt(8) + v2 := b.NewValue0(v.Pos, OpLOONG64MOVVstore, types.TypeMem) + v2.AuxInt = int32ToAuxInt(0) + v2.AddArg3(ptr, v0, mem) + v1.AddArg3(ptr, v0, v2) + v.AddArg3(ptr, v0, v1) + return true + } + // match: (Zero [s] {t} ptr mem) + // cond: s%8 == 0 && s > 24 && s <= 8*128 && t.Alignment()%8 == 0 && !config.noDuffDevice + // result: (DUFFZERO [8 * (128 - s/8)] ptr mem) + for { + s := auxIntToInt64(v.AuxInt) + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !(s%8 == 0 && s > 24 && s <= 8*128 && t.Alignment()%8 == 0 && !config.noDuffDevice) { + break + } + v.reset(OpLOONG64DUFFZERO) + v.AuxInt = int64ToAuxInt(8 * (128 - s/8)) + v.AddArg2(ptr, mem) + return true + } + // match: (Zero [s] {t} ptr mem) + // cond: (s > 8*128 || config.noDuffDevice) || t.Alignment()%8 != 0 + // result: (LoweredZero [t.Alignment()] ptr (ADDVconst ptr [s-moveSize(t.Alignment(), config)]) mem) + for { + s := auxIntToInt64(v.AuxInt) + t := auxToType(v.Aux) + ptr := v_0 + mem := v_1 + if !((s > 8*128 || config.noDuffDevice) || t.Alignment()%8 != 0) { + break + } + v.reset(OpLOONG64LoweredZero) + v.AuxInt = int64ToAuxInt(t.Alignment()) + v0 := b.NewValue0(v.Pos, OpLOONG64ADDVconst, ptr.Type) + v0.AuxInt = int64ToAuxInt(s - moveSize(t.Alignment(), config)) + v0.AddArg(ptr) + v.AddArg3(ptr, v0, mem) + return true + } + return false +} +func rewriteBlockLOONG64(b *Block) bool { + switch b.Kind { + case BlockLOONG64EQ: + // match: (EQ (FPFlagTrue cmp) yes no) + // result: (FPF cmp yes no) + for b.Controls[0].Op == OpLOONG64FPFlagTrue { + v_0 := b.Controls[0] + cmp := v_0.Args[0] + b.resetWithControl(BlockLOONG64FPF, cmp) + return true + } + // match: (EQ (FPFlagFalse cmp) yes no) + // result: (FPT cmp yes no) + for b.Controls[0].Op == OpLOONG64FPFlagFalse { + v_0 := b.Controls[0] + cmp := v_0.Args[0] + b.resetWithControl(BlockLOONG64FPT, cmp) + return true + } + // match: (EQ (XORconst [1] cmp:(SGT _ _)) yes no) + // result: (NE cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGT { + break + } + b.resetWithControl(BlockLOONG64NE, cmp) + return true + } + // match: (EQ (XORconst [1] cmp:(SGTU _ _)) yes no) + // result: (NE cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTU { + break + } + b.resetWithControl(BlockLOONG64NE, cmp) + return true + } + // match: (EQ (XORconst [1] cmp:(SGTconst _)) yes no) + // result: (NE cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTconst { + break + } + b.resetWithControl(BlockLOONG64NE, cmp) + return true + } + // match: (EQ (XORconst [1] cmp:(SGTUconst _)) yes no) + // result: (NE cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTUconst { + break + } + b.resetWithControl(BlockLOONG64NE, cmp) + return true + } + // match: (EQ (SGTUconst [1] x) yes no) + // result: (NE x yes no) + for b.Controls[0].Op == OpLOONG64SGTUconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + x := v_0.Args[0] + b.resetWithControl(BlockLOONG64NE, x) + return true + } + // match: (EQ (SGTU x (MOVVconst [0])) yes no) + // result: (EQ x yes no) + for b.Controls[0].Op == OpLOONG64SGTU { + v_0 := b.Controls[0] + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockLOONG64EQ, x) + return true + } + // match: (EQ (SGTconst [0] x) yes no) + // result: (GEZ x yes no) + for b.Controls[0].Op == OpLOONG64SGTconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + b.resetWithControl(BlockLOONG64GEZ, x) + return true + } + // match: (EQ (SGT x (MOVVconst [0])) yes no) + // result: (LEZ x yes no) + for b.Controls[0].Op == OpLOONG64SGT { + v_0 := b.Controls[0] + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockLOONG64LEZ, x) + return true + } + // match: (EQ (MOVVconst [0]) yes no) + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + b.Reset(BlockFirst) + return true + } + // match: (EQ (MOVVconst [c]) yes no) + // cond: c != 0 + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c != 0) { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + case BlockLOONG64GEZ: + // match: (GEZ (MOVVconst [c]) yes no) + // cond: c >= 0 + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c >= 0) { + break + } + b.Reset(BlockFirst) + return true + } + // match: (GEZ (MOVVconst [c]) yes no) + // cond: c < 0 + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c < 0) { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + case BlockLOONG64GTZ: + // match: (GTZ (MOVVconst [c]) yes no) + // cond: c > 0 + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c > 0) { + break + } + b.Reset(BlockFirst) + return true + } + // match: (GTZ (MOVVconst [c]) yes no) + // cond: c <= 0 + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c <= 0) { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + case BlockIf: + // match: (If cond yes no) + // result: (NE cond yes no) + for { + cond := b.Controls[0] + b.resetWithControl(BlockLOONG64NE, cond) + return true + } + case BlockLOONG64LEZ: + // match: (LEZ (MOVVconst [c]) yes no) + // cond: c <= 0 + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c <= 0) { + break + } + b.Reset(BlockFirst) + return true + } + // match: (LEZ (MOVVconst [c]) yes no) + // cond: c > 0 + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c > 0) { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + case BlockLOONG64LTZ: + // match: (LTZ (MOVVconst [c]) yes no) + // cond: c < 0 + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c < 0) { + break + } + b.Reset(BlockFirst) + return true + } + // match: (LTZ (MOVVconst [c]) yes no) + // cond: c >= 0 + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c >= 0) { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + case BlockLOONG64NE: + // match: (NE (FPFlagTrue cmp) yes no) + // result: (FPT cmp yes no) + for b.Controls[0].Op == OpLOONG64FPFlagTrue { + v_0 := b.Controls[0] + cmp := v_0.Args[0] + b.resetWithControl(BlockLOONG64FPT, cmp) + return true + } + // match: (NE (FPFlagFalse cmp) yes no) + // result: (FPF cmp yes no) + for b.Controls[0].Op == OpLOONG64FPFlagFalse { + v_0 := b.Controls[0] + cmp := v_0.Args[0] + b.resetWithControl(BlockLOONG64FPF, cmp) + return true + } + // match: (NE (XORconst [1] cmp:(SGT _ _)) yes no) + // result: (EQ cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGT { + break + } + b.resetWithControl(BlockLOONG64EQ, cmp) + return true + } + // match: (NE (XORconst [1] cmp:(SGTU _ _)) yes no) + // result: (EQ cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTU { + break + } + b.resetWithControl(BlockLOONG64EQ, cmp) + return true + } + // match: (NE (XORconst [1] cmp:(SGTconst _)) yes no) + // result: (EQ cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTconst { + break + } + b.resetWithControl(BlockLOONG64EQ, cmp) + return true + } + // match: (NE (XORconst [1] cmp:(SGTUconst _)) yes no) + // result: (EQ cmp yes no) + for b.Controls[0].Op == OpLOONG64XORconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + cmp := v_0.Args[0] + if cmp.Op != OpLOONG64SGTUconst { + break + } + b.resetWithControl(BlockLOONG64EQ, cmp) + return true + } + // match: (NE (SGTUconst [1] x) yes no) + // result: (EQ x yes no) + for b.Controls[0].Op == OpLOONG64SGTUconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 1 { + break + } + x := v_0.Args[0] + b.resetWithControl(BlockLOONG64EQ, x) + return true + } + // match: (NE (SGTU x (MOVVconst [0])) yes no) + // result: (NE x yes no) + for b.Controls[0].Op == OpLOONG64SGTU { + v_0 := b.Controls[0] + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockLOONG64NE, x) + return true + } + // match: (NE (SGTconst [0] x) yes no) + // result: (LTZ x yes no) + for b.Controls[0].Op == OpLOONG64SGTconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + b.resetWithControl(BlockLOONG64LTZ, x) + return true + } + // match: (NE (SGT x (MOVVconst [0])) yes no) + // result: (GTZ x yes no) + for b.Controls[0].Op == OpLOONG64SGT { + v_0 := b.Controls[0] + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpLOONG64MOVVconst || auxIntToInt64(v_0_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockLOONG64GTZ, x) + return true + } + // match: (NE (MOVVconst [0]) yes no) + // result: (First no yes) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + b.Reset(BlockFirst) + b.swapSuccessors() + return true + } + // match: (NE (MOVVconst [c]) yes no) + // cond: c != 0 + // result: (First yes no) + for b.Controls[0].Op == OpLOONG64MOVVconst { + v_0 := b.Controls[0] + c := auxIntToInt64(v_0.AuxInt) + if !(c != 0) { + break + } + b.Reset(BlockFirst) + return true + } + } + return false +} diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go index 429369d631eb37..811ea9d9d326f2 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go @@ -544,6 +544,9 @@ func rewriteValueMIPS(v *Value) bool { case OpSubPtr: v.Op = OpMIPSSUB return true + case OpTailCall: + v.Op = OpMIPSCALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go index 772d7b66efeebd..1fbd556b5cbcd7 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go @@ -625,6 +625,9 @@ func rewriteValueMIPS64(v *Value) bool { case OpSubPtr: v.Op = OpMIPS64SUBV return true + case OpTailCall: + v.Op = OpMIPS64CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 96dee0bd21baf6..0df82d428521ec 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -27,9 +27,6 @@ func rewriteValuePPC64(v *Value) bool { case OpAdd64F: v.Op = OpPPC64FADD return true - case OpAdd64carry: - v.Op = OpPPC64LoweredAdd64Carry - return true case OpAdd8: v.Op = OpPPC64ADD return true @@ -436,14 +433,14 @@ func rewriteValuePPC64(v *Value) bool { return true case OpPPC64ADD: return rewriteValuePPC64_OpPPC64ADD(v) + case OpPPC64ADDE: + return rewriteValuePPC64_OpPPC64ADDE(v) case OpPPC64ADDconst: return rewriteValuePPC64_OpPPC64ADDconst(v) case OpPPC64AND: return rewriteValuePPC64_OpPPC64AND(v) case OpPPC64ANDN: return rewriteValuePPC64_OpPPC64ANDN(v) - case OpPPC64ANDconst: - return rewriteValuePPC64_OpPPC64ANDconst(v) case OpPPC64CLRLSLDI: return rewriteValuePPC64_OpPPC64CLRLSLDI(v) case OpPPC64CMP: @@ -622,6 +619,8 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpPPC64SRWconst(v) case OpPPC64SUB: return rewriteValuePPC64_OpPPC64SUB(v) + case OpPPC64SUBE: + return rewriteValuePPC64_OpPPC64SUBE(v) case OpPPC64SUBFCconst: return rewriteValuePPC64_OpPPC64SUBFCconst(v) case OpPPC64XOR: @@ -639,12 +638,21 @@ func rewriteValuePPC64(v *Value) bool { return true case OpPopCount8: return rewriteValuePPC64_OpPopCount8(v) + case OpPrefetchCache: + return rewriteValuePPC64_OpPrefetchCache(v) + case OpPrefetchCacheStreamed: + return rewriteValuePPC64_OpPrefetchCacheStreamed(v) + case OpPubBarrier: + v.Op = OpPPC64LoweredPubBarrier + return true case OpRotateLeft16: return rewriteValuePPC64_OpRotateLeft16(v) case OpRotateLeft32: - return rewriteValuePPC64_OpRotateLeft32(v) + v.Op = OpPPC64ROTLW + return true case OpRotateLeft64: - return rewriteValuePPC64_OpRotateLeft64(v) + v.Op = OpPPC64ROTL + return true case OpRotateLeft8: return rewriteValuePPC64_OpRotateLeft8(v) case OpRound: @@ -720,6 +728,12 @@ func rewriteValuePPC64(v *Value) bool { return rewriteValuePPC64_OpRsh8x64(v) case OpRsh8x8: return rewriteValuePPC64_OpRsh8x8(v) + case OpSelect0: + return rewriteValuePPC64_OpSelect0(v) + case OpSelect1: + return rewriteValuePPC64_OpSelect1(v) + case OpSelectN: + return rewriteValuePPC64_OpSelectN(v) case OpSignExt16to32: v.Op = OpPPC64MOVHreg return true @@ -772,6 +786,9 @@ func rewriteValuePPC64(v *Value) bool { case OpSubPtr: v.Op = OpPPC64SUB return true + case OpTailCall: + v.Op = OpPPC64CALLtail + return true case OpTrunc: v.Op = OpPPC64FTRUNC return true @@ -1159,23 +1176,8 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool { v_0 := v.Args[0] b := v.Block // match: (CondSelect x y bool) - // cond: flagArg(bool) != nil - // result: (ISEL [2] x y bool) - for { - x := v_0 - y := v_1 - bool := v_2 - if !(flagArg(bool) != nil) { - break - } - v.reset(OpPPC64ISEL) - v.AuxInt = int32ToAuxInt(2) - v.AddArg3(x, y, bool) - return true - } - // match: (CondSelect x y bool) // cond: flagArg(bool) == nil - // result: (ISEL [2] x y (CMPWconst [0] bool)) + // result: (ISEL [6] x y (CMPWconst [0] bool)) for { x := v_0 y := v_1 @@ -1184,7 +1186,7 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool { break } v.reset(OpPPC64ISEL) - v.AuxInt = int32ToAuxInt(2) + v.AuxInt = int32ToAuxInt(6) v0 := b.NewValue0(v.Pos, OpPPC64CMPWconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) v0.AddArg(bool) @@ -1763,14 +1765,17 @@ func rewriteValuePPC64_OpEqB(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (EqB x y) - // result: (ANDconst [1] (EQV x y)) + // result: (Select0 (ANDCCconst [1] (EQV x y))) for { x := v_0 y := v_1 - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(1) - v0 := b.NewValue0(v.Pos, OpPPC64EQV, typ.Int64) - v0.AddArg2(x, y) + v.reset(OpSelect0) + v.Type = typ.Int + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(1) + v1 := b.NewValue0(v.Pos, OpPPC64EQV, typ.Int64) + v1.AddArg2(x, y) + v0.AddArg(v1) v.AddArg(v0) return true } @@ -2406,7 +2411,7 @@ func rewriteValuePPC64_OpLsh16x32(v *Value) bool { typ := &b.Func.Config.Types // match: (Lsh16x32 x (MOVDconst [c])) // cond: uint32(c) < 16 - // result: (SLWconst x [c&31]) + // result: (SLWconst x [c&15]) for { x := v_0 if v_1.Op != OpPPC64MOVDconst { @@ -2417,7 +2422,7 @@ func rewriteValuePPC64_OpLsh16x32(v *Value) bool { break } v.reset(OpPPC64SLWconst) - v.AuxInt = int64ToAuxInt(c & 31) + v.AuxInt = int64ToAuxInt(c & 15) v.AddArg(x) return true } @@ -2705,7 +2710,7 @@ func rewriteValuePPC64_OpLsh32x64(v *Value) bool { return true } // match: (Lsh32x64 x (AND y (MOVDconst [31]))) - // result: (SLW x (ANDconst [31] y)) + // result: (SLW x (Select0 (ANDCCconst [31] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -2720,26 +2725,34 @@ func rewriteValuePPC64_OpLsh32x64(v *Value) bool { continue } v.reset(OpPPC64SLW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int32) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int32) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Lsh32x64 x (ANDconst [31] y)) - // result: (SLW x (ANDconst [31] y)) + // match: (Lsh32x64 x (Select0 (ANDCCconst [31] y))) + // result: (SLW x (Select0 (ANDCCconst [31] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.Int32 || auxIntToInt64(v_1.AuxInt) != 31 { + if v_1.Op != OpSelect0 || v_1.Type != typ.Int32 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_1_0.AuxInt) != 31 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SLW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int32) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int32) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } @@ -2945,7 +2958,7 @@ func rewriteValuePPC64_OpLsh64x64(v *Value) bool { return true } // match: (Lsh64x64 x (AND y (MOVDconst [63]))) - // result: (SLD x (ANDconst [63] y)) + // result: (SLD x (Select0 (ANDCCconst [63] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -2960,26 +2973,34 @@ func rewriteValuePPC64_OpLsh64x64(v *Value) bool { continue } v.reset(OpPPC64SLD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int64) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Lsh64x64 x (ANDconst [63] y)) - // result: (SLD x (ANDconst [63] y)) + // match: (Lsh64x64 x (Select0 (ANDCCconst [63] y))) + // result: (SLD x (Select0 (ANDCCconst [63] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.Int64 || auxIntToInt64(v_1.AuxInt) != 63 { + if v_1.Op != OpSelect0 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || v_1_0.Type != typ.Int64 || auxIntToInt64(v_1_0.AuxInt) != 63 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SLD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int64) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } @@ -3879,8 +3900,6 @@ func rewriteValuePPC64_OpOffPtr(v *Value) bool { func rewriteValuePPC64_OpPPC64ADD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types // match: (ADD l:(MULLD x y) z) // cond: buildcfg.GOPPC64 >= 9 && l.Uses == 1 && clobber(l) // result: (MADDLD x y z) @@ -3902,204 +3921,6 @@ func rewriteValuePPC64_OpPPC64ADD(v *Value) bool { } break } - // match: (ADD (SLDconst x [c]) (SRDconst x [d])) - // cond: d == 64-c - // result: (ROTLconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLDconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRDconst { - continue - } - d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { - continue - } - v.reset(OpPPC64ROTLconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (ADD (SLWconst x [c]) (SRWconst x [d])) - // cond: d == 32-c - // result: (ROTLWconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLWconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRWconst { - continue - } - d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(OpPPC64ROTLWconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: (ADD (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) - // result: (ROTL x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 63 || y != v_1_1_1.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } - break - } - // match: (ADD (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) - // result: (ROTL x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } - break - } - // match: (ADD (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) - // result: (ROTLW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true - } - break - } - // match: (ADD (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) - // result: (ROTLW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 32 { - continue - } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 31 || y != v_1_1_1.Args[0] { - continue - } - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true - } - break - } // match: (ADD x (MOVDconst [c])) // cond: is32Bit(c) // result: (ADDconst [c] x) @@ -4122,6 +3943,34 @@ func rewriteValuePPC64_OpPPC64ADD(v *Value) bool { } return false } +func rewriteValuePPC64_OpPPC64ADDE(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (ADDE x y (Select1 (ADDCconst (MOVDconst [0]) [-1]))) + // result: (ADDC x y) + for { + x := v_0 + y := v_1 + if v_2.Op != OpSelect1 || v_2.Type != typ.UInt64 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpPPC64ADDCconst || auxIntToInt64(v_2_0.AuxInt) != -1 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_2_0_0.AuxInt) != 0 { + break + } + v.reset(OpPPC64ADDC) + v.AddArg2(x, y) + return true + } + return false +} func rewriteValuePPC64_OpPPC64ADDconst(v *Value) bool { v_0 := v.Args[0] // match: (ADDconst [c] (ADDconst [d] x)) @@ -4209,6 +4058,8 @@ func rewriteValuePPC64_OpPPC64ADDconst(v *Value) bool { func rewriteValuePPC64_OpPPC64AND(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types // match: (AND (MOVDconst [m]) (ROTLWconst [r] x)) // cond: isPPC64WordRotateMask(m) // result: (RLWINM [encodePPC64RotateMask(r,m,32)] x) @@ -4341,7 +4192,7 @@ func rewriteValuePPC64_OpPPC64AND(v *Value) bool { } // match: (AND x (MOVDconst [c])) // cond: isU16Bit(c) - // result: (ANDconst [c] x) + // result: (Select0 (ANDCCconst [c] x)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 @@ -4352,9 +4203,11 @@ func rewriteValuePPC64_OpPPC64AND(v *Value) bool { if !(isU16Bit(c)) { continue } - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c) + v0.AddArg(x) + v.AddArg(v0) return true } break @@ -4396,7 +4249,7 @@ func rewriteValuePPC64_OpPPC64AND(v *Value) bool { break } // match: (AND (MOVDconst [c]) x:(MOVBZload _ _)) - // result: (ANDconst [c&0xFF] x) + // result: (Select0 (ANDCCconst [c&0xFF] x)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { if v_0.Op != OpPPC64MOVDconst { @@ -4407,9 +4260,11 @@ func rewriteValuePPC64_OpPPC64AND(v *Value) bool { if x.Op != OpPPC64MOVBZload { continue } - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFF) - v.AddArg(x) + v.reset(OpSelect0) + v0 := b.NewValue0(x.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFF) + v0.AddArg(x) + v.AddArg(v0) return true } break @@ -4436,293 +4291,57 @@ func rewriteValuePPC64_OpPPC64ANDN(v *Value) bool { } return false } -func rewriteValuePPC64_OpPPC64ANDconst(v *Value) bool { +func rewriteValuePPC64_OpPPC64CLRLSLDI(v *Value) bool { v_0 := v.Args[0] - // match: (ANDconst [m] (ROTLWconst [r] x)) - // cond: isPPC64WordRotateMask(m) - // result: (RLWINM [encodePPC64RotateMask(r,m,32)] x) + // match: (CLRLSLDI [c] (SRWconst [s] x)) + // cond: mergePPC64ClrlsldiSrw(int64(c),s) != 0 + // result: (RLWINM [mergePPC64ClrlsldiSrw(int64(c),s)] x) for { - m := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ROTLWconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpPPC64SRWconst { break } - r := auxIntToInt64(v_0.AuxInt) + s := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isPPC64WordRotateMask(m)) { + if !(mergePPC64ClrlsldiSrw(int64(c), s) != 0) { break } v.reset(OpPPC64RLWINM) - v.AuxInt = int64ToAuxInt(encodePPC64RotateMask(r, m, 32)) + v.AuxInt = int64ToAuxInt(mergePPC64ClrlsldiSrw(int64(c), s)) v.AddArg(x) return true } - // match: (ANDconst [m] (ROTLW x r)) - // cond: isPPC64WordRotateMask(m) - // result: (RLWNM [encodePPC64RotateMask(0,m,32)] x r) + // match: (CLRLSLDI [c] i:(RLWINM [s] x)) + // cond: mergePPC64ClrlsldiRlwinm(c,s) != 0 + // result: (RLWINM [mergePPC64ClrlsldiRlwinm(c,s)] x) for { - m := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ROTLW { + c := auxIntToInt32(v.AuxInt) + i := v_0 + if i.Op != OpPPC64RLWINM { break } - r := v_0.Args[1] - x := v_0.Args[0] - if !(isPPC64WordRotateMask(m)) { + s := auxIntToInt64(i.AuxInt) + x := i.Args[0] + if !(mergePPC64ClrlsldiRlwinm(c, s) != 0) { break } - v.reset(OpPPC64RLWNM) - v.AuxInt = int64ToAuxInt(encodePPC64RotateMask(0, m, 32)) - v.AddArg2(x, r) + v.reset(OpPPC64RLWINM) + v.AuxInt = int64ToAuxInt(mergePPC64ClrlsldiRlwinm(c, s)) + v.AddArg(x) return true } - // match: (ANDconst [m] (SRWconst x [s])) - // cond: mergePPC64RShiftMask(m,s,32) == 0 - // result: (MOVDconst [0]) + return false +} +func rewriteValuePPC64_OpPPC64CMP(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (CMP x (MOVDconst [c])) + // cond: is16Bit(c) + // result: (CMPconst x [c]) for { - m := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64SRWconst { - break - } - s := auxIntToInt64(v_0.AuxInt) - if !(mergePPC64RShiftMask(m, s, 32) == 0) { - break - } - v.reset(OpPPC64MOVDconst) - v.AuxInt = int64ToAuxInt(0) - return true - } - // match: (ANDconst [m] (SRWconst x [s])) - // cond: mergePPC64AndSrwi(m,s) != 0 - // result: (RLWINM [mergePPC64AndSrwi(m,s)] x) - for { - m := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64SRWconst { - break - } - s := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if !(mergePPC64AndSrwi(m, s) != 0) { - break - } - v.reset(OpPPC64RLWINM) - v.AuxInt = int64ToAuxInt(mergePPC64AndSrwi(m, s)) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (ANDconst [d] x)) - // result: (ANDconst [c&d] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ANDconst { - break - } - d := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & d) - v.AddArg(x) - return true - } - // match: (ANDconst [-1] x) - // result: x - for { - if auxIntToInt64(v.AuxInt) != -1 { - break - } - x := v_0 - v.copyOf(x) - return true - } - // match: (ANDconst [0] _) - // result: (MOVDconst [0]) - for { - if auxIntToInt64(v.AuxInt) != 0 { - break - } - v.reset(OpPPC64MOVDconst) - v.AuxInt = int64ToAuxInt(0) - return true - } - // match: (ANDconst [c] y:(MOVBZreg _)) - // cond: c&0xFF == 0xFF - // result: y - for { - c := auxIntToInt64(v.AuxInt) - y := v_0 - if y.Op != OpPPC64MOVBZreg || !(c&0xFF == 0xFF) { - break - } - v.copyOf(y) - return true - } - // match: (ANDconst [0xFF] y:(MOVBreg _)) - // result: y - for { - if auxIntToInt64(v.AuxInt) != 0xFF { - break - } - y := v_0 - if y.Op != OpPPC64MOVBreg { - break - } - v.copyOf(y) - return true - } - // match: (ANDconst [c] y:(MOVHZreg _)) - // cond: c&0xFFFF == 0xFFFF - // result: y - for { - c := auxIntToInt64(v.AuxInt) - y := v_0 - if y.Op != OpPPC64MOVHZreg || !(c&0xFFFF == 0xFFFF) { - break - } - v.copyOf(y) - return true - } - // match: (ANDconst [0xFFFF] y:(MOVHreg _)) - // result: y - for { - if auxIntToInt64(v.AuxInt) != 0xFFFF { - break - } - y := v_0 - if y.Op != OpPPC64MOVHreg { - break - } - v.copyOf(y) - return true - } - // match: (ANDconst [c] (MOVBreg x)) - // result: (ANDconst [c&0xFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVBreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFF) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (MOVBZreg x)) - // result: (ANDconst [c&0xFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVBZreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFF) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (MOVHreg x)) - // result: (ANDconst [c&0xFFFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVHreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFFFF) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (MOVHZreg x)) - // result: (ANDconst [c&0xFFFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVHZreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFFFF) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (MOVWreg x)) - // result: (ANDconst [c&0xFFFFFFFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVWreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFFFFFFFF) - v.AddArg(x) - return true - } - // match: (ANDconst [c] (MOVWZreg x)) - // result: (ANDconst [c&0xFFFFFFFF] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64MOVWZreg { - break - } - x := v_0.Args[0] - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(c & 0xFFFFFFFF) - v.AddArg(x) - return true - } - return false -} -func rewriteValuePPC64_OpPPC64CLRLSLDI(v *Value) bool { - v_0 := v.Args[0] - // match: (CLRLSLDI [c] (SRWconst [s] x)) - // cond: mergePPC64ClrlsldiSrw(int64(c),s) != 0 - // result: (RLWINM [mergePPC64ClrlsldiSrw(int64(c),s)] x) - for { - c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpPPC64SRWconst { - break - } - s := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if !(mergePPC64ClrlsldiSrw(int64(c), s) != 0) { - break - } - v.reset(OpPPC64RLWINM) - v.AuxInt = int64ToAuxInt(mergePPC64ClrlsldiSrw(int64(c), s)) - v.AddArg(x) - return true - } - // match: (CLRLSLDI [c] i:(RLWINM [s] x)) - // cond: mergePPC64ClrlsldiRlwinm(c,s) != 0 - // result: (RLWINM [mergePPC64ClrlsldiRlwinm(c,s)] x) - for { - c := auxIntToInt32(v.AuxInt) - i := v_0 - if i.Op != OpPPC64RLWINM { - break - } - s := auxIntToInt64(i.AuxInt) - x := i.Args[0] - if !(mergePPC64ClrlsldiRlwinm(c, s) != 0) { - break - } - v.reset(OpPPC64RLWINM) - v.AuxInt = int64ToAuxInt(mergePPC64ClrlsldiRlwinm(c, s)) - v.AddArg(x) - return true - } - return false -} -func rewriteValuePPC64_OpPPC64CMP(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (CMP x (MOVDconst [c])) - // cond: is16Bit(c) - // result: (CMPconst x [c]) - for { - x := v_0 - if v_1.Op != OpPPC64MOVDconst { + x := v_0 + if v_1.Op != OpPPC64MOVDconst { break } c := auxIntToInt64(v_1.AuxInt) @@ -5849,21 +5468,31 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] - // match: (ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPU (ANDconst [d] y) (MOVDconst [c]))) + b := v.Block + typ := &b.Func.Config.Types + // match: (ISEL [0] (Select0 (ANDCCconst [d] y)) (MOVDconst [-1]) (CMPU (Select0 (ANDCCconst [d] y)) (MOVDconst [c]))) // cond: c >= d - // result: (ANDconst [d] y) + // result: (Select0 (ANDCCconst [d] y)) for { - if auxIntToInt32(v.AuxInt) != 0 || v_0.Op != OpPPC64ANDconst { + if auxIntToInt32(v.AuxInt) != 0 || v_0.Op != OpSelect0 { break } - d := auxIntToInt64(v_0.AuxInt) - y := v_0.Args[0] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ANDCCconst { + break + } + d := auxIntToInt64(v_0_0.AuxInt) + y := v_0_0.Args[0] if v_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 || v_2.Op != OpPPC64CMPU { break } _ = v_2.Args[1] v_2_0 := v_2.Args[0] - if v_2_0.Op != OpPPC64ANDconst || auxIntToInt64(v_2_0.AuxInt) != d || y != v_2_0.Args[0] { + if v_2_0.Op != OpSelect0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != d || y != v_2_0_0.Args[0] { break } v_2_1 := v_2.Args[1] @@ -5874,31 +5503,65 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool { if !(c >= d) { break } - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(d) - v.AddArg(y) + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(y) + v.AddArg(v0) return true } - // match: (ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPUconst [c] (ANDconst [d] y))) + // match: (ISEL [0] (Select0 (ANDCCconst [d] y)) (MOVDconst [-1]) (CMPUconst [c] (Select0 (ANDCCconst [d] y)))) // cond: c >= d - // result: (ANDconst [d] y) + // result: (Select0 (ANDCCconst [d] y)) for { - if auxIntToInt32(v.AuxInt) != 0 || v_0.Op != OpPPC64ANDconst { + if auxIntToInt32(v.AuxInt) != 0 || v_0.Op != OpSelect0 { break } - d := auxIntToInt64(v_0.AuxInt) - y := v_0.Args[0] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ANDCCconst { + break + } + d := auxIntToInt64(v_0_0.AuxInt) + y := v_0_0.Args[0] if v_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1.AuxInt) != -1 || v_2.Op != OpPPC64CMPUconst { break } c := auxIntToInt64(v_2.AuxInt) v_2_0 := v_2.Args[0] - if v_2_0.Op != OpPPC64ANDconst || auxIntToInt64(v_2_0.AuxInt) != d || y != v_2_0.Args[0] || !(c >= d) { + if v_2_0.Op != OpSelect0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != d || y != v_2_0_0.Args[0] || !(c >= d) { + break + } + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (ISEL [6] x y (CMPWconst [0] (ISELB [c] one cmp))) + // result: (ISEL [c] x y cmp) + for { + if auxIntToInt32(v.AuxInt) != 6 { + break + } + x := v_0 + y := v_1 + if v_2.Op != OpPPC64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpPPC64ISELB { break } - v.reset(OpPPC64ANDconst) - v.AuxInt = int64ToAuxInt(d) - v.AddArg(y) + c := auxIntToInt32(v_2_0.AuxInt) + cmp := v_2_0.Args[1] + v.reset(OpPPC64ISEL) + v.AuxInt = int32ToAuxInt(c) + v.AddArg3(x, y, cmp) return true } // match: (ISEL [2] x _ (FlagEQ)) @@ -6135,31 +5798,151 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool { v.copyOf(y) return true } - // match: (ISEL [n] x y (InvertFlags bool)) - // cond: n%4 == 0 - // result: (ISEL [n+1] x y bool) + // match: (ISEL [2] x y (CMPconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (ISEL [2] x y (Select1 (ANDCCconst [1] z ))) for { - n := auxIntToInt32(v.AuxInt) + if auxIntToInt32(v.AuxInt) != 2 { + break + } x := v_0 y := v_1 - if v_2.Op != OpPPC64InvertFlags { + if v_2.Op != OpPPC64CMPconst || auxIntToInt64(v_2.AuxInt) != 0 { break } - bool := v_2.Args[0] - if !(n%4 == 0) { + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpSelect0 { break } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != 1 { + break + } + z := v_2_0_0.Args[0] v.reset(OpPPC64ISEL) - v.AuxInt = int32ToAuxInt(n + 1) - v.AddArg3(x, y, bool) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg3(x, y, v0) return true } - // match: (ISEL [n] x y (InvertFlags bool)) - // cond: n%4 == 1 - // result: (ISEL [n-1] x y bool) + // match: (ISEL [2] x y (CMPWconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (ISEL [2] x y (Select1 (ANDCCconst [1] z ))) for { - n := auxIntToInt32(v.AuxInt) - x := v_0 + if auxIntToInt32(v.AuxInt) != 2 { + break + } + x := v_0 + y := v_1 + if v_2.Op != OpPPC64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpSelect0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != 1 { + break + } + z := v_2_0_0.Args[0] + v.reset(OpPPC64ISEL) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg3(x, y, v0) + return true + } + // match: (ISEL [6] x y (CMPconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (ISEL [6] x y (Select1 (ANDCCconst [1] z ))) + for { + if auxIntToInt32(v.AuxInt) != 6 { + break + } + x := v_0 + y := v_1 + if v_2.Op != OpPPC64CMPconst || auxIntToInt64(v_2.AuxInt) != 0 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpSelect0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != 1 { + break + } + z := v_2_0_0.Args[0] + v.reset(OpPPC64ISEL) + v.AuxInt = int32ToAuxInt(6) + v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg3(x, y, v0) + return true + } + // match: (ISEL [6] x y (CMPWconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (ISEL [6] x y (Select1 (ANDCCconst [1] z ))) + for { + if auxIntToInt32(v.AuxInt) != 6 { + break + } + x := v_0 + y := v_1 + if v_2.Op != OpPPC64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpSelect0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0_0.AuxInt) != 1 { + break + } + z := v_2_0_0.Args[0] + v.reset(OpPPC64ISEL) + v.AuxInt = int32ToAuxInt(6) + v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg3(x, y, v0) + return true + } + // match: (ISEL [n] x y (InvertFlags bool)) + // cond: n%4 == 0 + // result: (ISEL [n+1] x y bool) + for { + n := auxIntToInt32(v.AuxInt) + x := v_0 + y := v_1 + if v_2.Op != OpPPC64InvertFlags { + break + } + bool := v_2.Args[0] + if !(n%4 == 0) { + break + } + v.reset(OpPPC64ISEL) + v.AuxInt = int32ToAuxInt(n + 1) + v.AddArg3(x, y, bool) + return true + } + // match: (ISEL [n] x y (InvertFlags bool)) + // cond: n%4 == 1 + // result: (ISEL [n-1] x y bool) + for { + n := auxIntToInt32(v.AuxInt) + x := v_0 y := v_1 if v_2.Op != OpPPC64InvertFlags { break @@ -6379,6 +6162,114 @@ func rewriteValuePPC64_OpPPC64ISELB(v *Value) bool { v.AuxInt = int64ToAuxInt(1) return true } + // match: (ISELB [2] x (CMPconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (XORconst [1] (Select0 (ANDCCconst [1] z ))) + for { + if auxIntToInt32(v.AuxInt) != 2 { + break + } + if v_1.Op != OpPPC64CMPconst || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSelect0 { + break + } + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + break + } + z := v_1_0_0.Args[0] + v.reset(OpPPC64XORconst) + v.AuxInt = int64ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + // match: (ISELB [2] x (CMPWconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (XORconst [1] (Select0 (ANDCCconst [1] z ))) + for { + if auxIntToInt32(v.AuxInt) != 2 { + break + } + if v_1.Op != OpPPC64CMPWconst || auxIntToInt32(v_1.AuxInt) != 0 { + break + } + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSelect0 { + break + } + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + break + } + z := v_1_0_0.Args[0] + v.reset(OpPPC64XORconst) + v.AuxInt = int64ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(1) + v1.AddArg(z) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + // match: (ISELB [6] x (CMPconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (Select0 (ANDCCconst [1] z )) + for { + if auxIntToInt32(v.AuxInt) != 6 { + break + } + if v_1.Op != OpPPC64CMPconst || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSelect0 { + break + } + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + break + } + z := v_1_0_0.Args[0] + v.reset(OpSelect0) + v.Type = typ.UInt64 + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(1) + v0.AddArg(z) + v.AddArg(v0) + return true + } + // match: (ISELB [6] x (CMPWconst [0] (Select0 (ANDCCconst [1] z)))) + // result: (Select0 (ANDCCconst [1] z )) + for { + if auxIntToInt32(v.AuxInt) != 6 { + break + } + if v_1.Op != OpPPC64CMPWconst || auxIntToInt32(v_1.AuxInt) != 0 { + break + } + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSelect0 { + break + } + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 { + break + } + z := v_1_0_0.Args[0] + v.reset(OpSelect0) + v.Type = typ.UInt64 + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(1) + v0.AddArg(z) + v.AddArg(v0) + return true + } // match: (ISELB [n] (MOVDconst [1]) (InvertFlags bool)) // cond: n%4 == 0 // result: (ISELB [n+1] (MOVDconst [1]) bool) @@ -6709,15 +6600,19 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVBZreg y:(ANDconst [c] _)) + // match: (MOVBZreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0xFF // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { + break + } + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { break } - c := auxIntToInt64(y.AuxInt) + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0xFF) { break } @@ -7031,15 +6926,19 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool { } break } - // match: (MOVBZreg z:(ANDconst [c] (MOVBZload ptr x))) + // match: (MOVBZreg z:(Select0 (ANDCCconst [c] (MOVBZload ptr x)))) // result: z for { z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVBZload { + if z_0.Op != OpPPC64ANDCCconst { + break + } + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVBZload { break } v.copyOf(z) @@ -7084,6 +6983,20 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool { v.copyOf(x) return true } + // match: (MOVBZreg x:(Select0 (LoweredAtomicLoad8 _ _))) + // result: x + for { + x := v_0 + if x.Op != OpSelect0 { + break + } + x_0 := x.Args[0] + if x_0.Op != OpPPC64LoweredAtomicLoad8 { + break + } + v.copyOf(x) + return true + } // match: (MOVBZreg x:(Arg )) // cond: is8BitInt(t) && !isSigned(t) // result: x @@ -7116,15 +7029,19 @@ func rewriteValuePPC64_OpPPC64MOVBreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVBreg y:(ANDconst [c] _)) + // match: (MOVBreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0x7F // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { break } - c := auxIntToInt64(y.AuxInt) + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0x7F) { break } @@ -8860,15 +8777,19 @@ func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVHZreg y:(ANDconst [c] _)) + // match: (MOVHZreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0xFFFF // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { + break + } + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { break } - c := auxIntToInt64(y.AuxInt) + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0xFFFF) { break } @@ -9150,29 +9071,19 @@ func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value) bool { } break } - // match: (MOVHZreg z:(ANDconst [c] (MOVBZload ptr x))) + // match: (MOVHZreg z:(Select0 (ANDCCconst [c] (MOVBZload ptr x)))) // result: z for { z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVBZload { - break - } - v.copyOf(z) - return true - } - // match: (MOVHZreg z:(ANDconst [c] (MOVHZload ptr x))) - // result: z - for { - z := v_0 - if z.Op != OpPPC64ANDconst { + if z_0.Op != OpPPC64ANDCCconst { break } - z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVHZload { + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVBZload { break } v.copyOf(z) @@ -9197,6 +9108,24 @@ func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value) bool { } break } + // match: (MOVHZreg z:(Select0 (ANDCCconst [c] (MOVHZload ptr x)))) + // result: z + for { + z := v_0 + if z.Op != OpSelect0 { + break + } + z_0 := z.Args[0] + if z_0.Op != OpPPC64ANDCCconst { + break + } + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVHZload { + break + } + v.copyOf(z) + return true + } // match: (MOVHZreg x:(MOVBZload _ _)) // result: x for { @@ -9382,15 +9311,19 @@ func rewriteValuePPC64_OpPPC64MOVHreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVHreg y:(ANDconst [c] _)) + // match: (MOVHreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0x7FFF // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { break } - c := auxIntToInt64(y.AuxInt) + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0x7FFF) { break } @@ -10156,15 +10089,19 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVWZreg y:(ANDconst [c] _)) + // match: (MOVWZreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0xFFFFFFFF // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { + break + } + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { break } - c := auxIntToInt64(y.AuxInt) + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0xFFFFFFFF) { break } @@ -10419,66 +10356,78 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool { } break } - // match: (MOVWZreg z:(ANDconst [c] (MOVBZload ptr x))) + // match: (MOVWZreg z:(Select0 (ANDCCconst [c] (MOVBZload ptr x)))) // result: z for { z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVBZload { + if z_0.Op != OpPPC64ANDCCconst { + break + } + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVBZload { break } v.copyOf(z) return true } - // match: (MOVWZreg z:(ANDconst [c] (MOVHZload ptr x))) + // match: (MOVWZreg z:(AND y (MOVWZload ptr x))) // result: z for { z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpPPC64AND { break } + _ = z.Args[1] z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVHZload { - break + z_1 := z.Args[1] + for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { + if z_1.Op != OpPPC64MOVWZload { + continue + } + v.copyOf(z) + return true } - v.copyOf(z) - return true + break } - // match: (MOVWZreg z:(ANDconst [c] (MOVWZload ptr x))) + // match: (MOVWZreg z:(Select0 (ANDCCconst [c] (MOVHZload ptr x)))) // result: z for { z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } z_0 := z.Args[0] - if z_0.Op != OpPPC64MOVWZload { + if z_0.Op != OpPPC64ANDCCconst { + break + } + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVHZload { break } v.copyOf(z) return true } - // match: (MOVWZreg z:(AND y (MOVWZload ptr x))) + // match: (MOVWZreg z:(Select0 (ANDCCconst [c] (MOVWZload ptr x)))) // result: z for { z := v_0 - if z.Op != OpPPC64AND { + if z.Op != OpSelect0 { break } - _ = z.Args[1] z_0 := z.Args[0] - z_1 := z.Args[1] - for _i0 := 0; _i0 <= 1; _i0, z_0, z_1 = _i0+1, z_1, z_0 { - if z_1.Op != OpPPC64MOVWZload { - continue - } - v.copyOf(z) - return true + if z_0.Op != OpPPC64ANDCCconst { + break } - break + z_0_0 := z_0.Args[0] + if z_0_0.Op != OpPPC64MOVWZload { + break + } + v.copyOf(z) + return true } // match: (MOVWZreg x:(MOVBZload _ _)) // result: x @@ -10540,6 +10489,20 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool { v.copyOf(x) return true } + // match: (MOVWZreg x:(Select0 (LoweredAtomicLoad32 _ _))) + // result: x + for { + x := v_0 + if x.Op != OpSelect0 { + break + } + x_0 := x.Args[0] + if x_0.Op != OpPPC64LoweredAtomicLoad32 { + break + } + v.copyOf(x) + return true + } // match: (MOVWZreg x:(Arg )) // cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t) // result: x @@ -10685,15 +10648,19 @@ func rewriteValuePPC64_OpPPC64MOVWreg(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types - // match: (MOVWreg y:(ANDconst [c] _)) + // match: (MOVWreg y:(Select0 (ANDCCconst [c] _))) // cond: uint64(c) <= 0xFFFF // result: y for { y := v_0 - if y.Op != OpPPC64ANDconst { + if y.Op != OpSelect0 { + break + } + y_0 := y.Args[0] + if y_0.Op != OpPPC64ANDCCconst { break } - c := auxIntToInt64(y.AuxInt) + c := auxIntToInt64(y_0.AuxInt) if !(uint64(c) <= 0xFFFF) { break } @@ -11335,18 +11302,40 @@ func rewriteValuePPC64_OpPPC64NEG(v *Value) bool { v.AddArg(x) return true } - return false -} -func rewriteValuePPC64_OpPPC64NOR(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (NOR (MOVDconst [c]) (MOVDconst [d])) - // result: (MOVDconst [^(c|d)]) + // match: (NEG (SUB x y)) + // result: (SUB y x) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64MOVDconst { - continue - } + if v_0.Op != OpPPC64SUB { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + v.reset(OpPPC64SUB) + v.AddArg2(y, x) + return true + } + // match: (NEG (NEG x)) + // result: x + for { + if v_0.Op != OpPPC64NEG { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + return false +} +func rewriteValuePPC64_OpPPC64NOR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (NOR (MOVDconst [c]) (MOVDconst [d])) + // result: (MOVDconst [^(c|d)]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpPPC64MOVDconst { + continue + } c := auxIntToInt64(v_0.AuxInt) if v_1.Op != OpPPC64MOVDconst { continue @@ -11423,199 +11412,19 @@ func rewriteValuePPC64_OpPPC64OR(v *Value) bool { b := v.Block config := b.Func.Config typ := &b.Func.Config.Types - // match: ( OR (SLDconst x [c]) (SRDconst x [d])) - // cond: d == 64-c - // result: (ROTLconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLDconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRDconst { - continue - } - d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { - continue - } - v.reset(OpPPC64ROTLconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: ( OR (SLWconst x [c]) (SRWconst x [d])) - // cond: d == 32-c - // result: (ROTLWconst [c] x) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLWconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRWconst { - continue - } - d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { - continue - } - v.reset(OpPPC64ROTLWconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true - } - break - } - // match: ( OR (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) - // result: (ROTL x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 63 || y != v_1_1_1.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } - break - } - // match: ( OR (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) - // result: (ROTL x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } - break - } - // match: ( OR (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) - // result: (ROTLW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true - } - break - } - // match: ( OR (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) - // result: (ROTLW x y) + // match: (OR x (NOR y y)) + // result: (ORN x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 32 { + x := v_0 + if v_1.Op != OpPPC64NOR { continue } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 31 || y != v_1_1_1.Args[0] { + y := v_1.Args[1] + if y != v_1.Args[0] { continue } - v.reset(OpPPC64ROTLW) + v.reset(OpPPC64ORN) v.AddArg2(x, y) return true } @@ -13081,16 +12890,20 @@ func rewriteValuePPC64_OpPPC64ROTLWconst(v *Value) bool { } break } - // match: (ROTLWconst [r] (ANDconst [m] x)) + // match: (ROTLWconst [r] (Select0 (ANDCCconst [m] x))) // cond: isPPC64WordRotateMask(m) // result: (RLWINM [encodePPC64RotateMask(r,rotateLeft32(m,r),32)] x) for { r := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ANDconst { + if v_0.Op != OpSelect0 { break } - m := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0_0.AuxInt) + x := v_0_0.Args[0] if !(isPPC64WordRotateMask(m)) { break } @@ -13193,17 +13006,21 @@ func rewriteValuePPC64_OpPPC64SLDconst(v *Value) bool { v.AddArg(x) return true } - // match: (SLDconst [c] z:(ANDconst [d] x)) + // match: (SLDconst [c] z:(Select0 (ANDCCconst [d] x))) // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) && c <= (64-getPPC64ShiftMaskLength(d)) // result: (CLRLSLDI [newPPC64ShiftAuxInt(c,64-getPPC64ShiftMaskLength(d),63,64)] x) for { c := auxIntToInt64(v.AuxInt) z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } - d := auxIntToInt64(z.AuxInt) - x := z.Args[0] + z_0 := z.Args[0] + if z_0.Op != OpPPC64ANDCCconst { + break + } + d := auxIntToInt64(z_0.AuxInt) + x := z_0.Args[0] if !(z.Uses == 1 && isPPC64ValidShiftMask(d) && c <= (64-getPPC64ShiftMaskLength(d))) { break } @@ -13316,17 +13133,21 @@ func rewriteValuePPC64_OpPPC64SLWconst(v *Value) bool { v.AddArg(x) return true } - // match: (SLWconst [c] z:(ANDconst [d] x)) + // match: (SLWconst [c] z:(Select0 (ANDCCconst [d] x))) // cond: z.Uses == 1 && isPPC64ValidShiftMask(d) && c<=(32-getPPC64ShiftMaskLength(d)) // result: (CLRLSLWI [newPPC64ShiftAuxInt(c,32-getPPC64ShiftMaskLength(d),31,32)] x) for { c := auxIntToInt64(v.AuxInt) z := v_0 - if z.Op != OpPPC64ANDconst { + if z.Op != OpSelect0 { break } - d := auxIntToInt64(z.AuxInt) - x := z.Args[0] + z_0 := z.Args[0] + if z_0.Op != OpPPC64ANDCCconst { + break + } + d := auxIntToInt64(z_0.AuxInt) + x := z_0.Args[0] if !(z.Uses == 1 && isPPC64ValidShiftMask(d) && c <= (32-getPPC64ShiftMaskLength(d))) { break } @@ -13457,15 +13278,19 @@ func rewriteValuePPC64_OpPPC64SRW(v *Value) bool { } func rewriteValuePPC64_OpPPC64SRWconst(v *Value) bool { v_0 := v.Args[0] - // match: (SRWconst (ANDconst [m] x) [s]) + // match: (SRWconst (Select0 (ANDCCconst [m] x)) [s]) // cond: mergePPC64RShiftMask(m>>uint(s),s,32) == 0 // result: (MOVDconst [0]) for { s := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ANDconst { + if v_0.Op != OpSelect0 { break } - m := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0_0.AuxInt) if !(mergePPC64RShiftMask(m>>uint(s), s, 32) == 0) { break } @@ -13473,16 +13298,20 @@ func rewriteValuePPC64_OpPPC64SRWconst(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (SRWconst (ANDconst [m] x) [s]) + // match: (SRWconst (Select0 (ANDCCconst [m] x)) [s]) // cond: mergePPC64AndSrwi(m>>uint(s),s) != 0 // result: (RLWINM [mergePPC64AndSrwi(m>>uint(s),s)] x) for { s := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64ANDconst { + if v_0.Op != OpSelect0 { break } - m := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0_0.AuxInt) + x := v_0_0.Args[0] if !(mergePPC64AndSrwi(m>>uint(s), s) != 0) { break } @@ -13584,6 +13413,34 @@ func rewriteValuePPC64_OpPPC64SUB(v *Value) bool { } return false } +func rewriteValuePPC64_OpPPC64SUBE(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (SUBE x y (Select1 (SUBCconst (MOVDconst [0]) [0]))) + // result: (SUBC x y) + for { + x := v_0 + y := v_1 + if v_2.Op != OpSelect1 || v_2.Type != typ.UInt64 { + break + } + v_2_0 := v_2.Args[0] + if v_2_0.Op != OpPPC64SUBCconst || auxIntToInt64(v_2_0.AuxInt) != 0 { + break + } + v_2_0_0 := v_2_0.Args[0] + if v_2_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_2_0_0.AuxInt) != 0 { + break + } + v.reset(OpPPC64SUBC) + v.AddArg2(x, y) + return true + } + return false +} func rewriteValuePPC64_OpPPC64SUBFCconst(v *Value) bool { v_0 := v.Args[0] // match: (SUBFCconst [c] (NEG x)) @@ -13633,270 +13490,126 @@ func rewriteValuePPC64_OpPPC64SUBFCconst(v *Value) bool { func rewriteValuePPC64_OpPPC64XOR(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (XOR (SLDconst x [c]) (SRDconst x [d])) - // cond: d == 64-c - // result: (ROTLconst [c] x) + // match: (XOR (MOVDconst [c]) (MOVDconst [d])) + // result: (MOVDconst [c^d]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLDconst { + if v_0.Op != OpPPC64MOVDconst { continue } c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRDconst { + if v_1.Op != OpPPC64MOVDconst { continue } d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 64-c) { - continue - } - v.reset(OpPPC64ROTLconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) + v.reset(OpPPC64MOVDconst) + v.AuxInt = int64ToAuxInt(c ^ d) return true } break } - // match: (XOR (SLWconst x [c]) (SRWconst x [d])) - // cond: d == 32-c - // result: (ROTLWconst [c] x) + // match: (XOR x (MOVDconst [c])) + // cond: isU32Bit(c) + // result: (XORconst [c] x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLWconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpPPC64SRWconst { + x := v_0 + if v_1.Op != OpPPC64MOVDconst { continue } - d := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(d == 32-c) { + c := auxIntToInt64(v_1.AuxInt) + if !(isU32Bit(c)) { continue } - v.reset(OpPPC64ROTLWconst) + v.reset(OpPPC64XORconst) v.AuxInt = int64ToAuxInt(c) v.AddArg(x) return true } break } - // match: (XOR (SLD x (ANDconst [63] y)) (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y)))) - // result: (ROTL x y) + return false +} +func rewriteValuePPC64_OpPPC64XORconst(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (XORconst [c] (XORconst [d] x)) + // result: (XORconst [c^d] x) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 64 { - continue - } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 63 || y != v_1_1_1.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpPPC64XORconst { + break } - break + d := auxIntToInt64(v_0.AuxInt) + x := v_0.Args[0] + v.reset(OpPPC64XORconst) + v.AuxInt = int64ToAuxInt(c ^ d) + v.AddArg(x) + return true } - // match: (XOR (SLD x (ANDconst [63] y)) (SRD x (SUBFCconst [64] (ANDconst [63] y)))) - // result: (ROTL x y) + // match: (XORconst [0] x) + // result: x for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLD { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRD { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } - break - } - // match: (XOR (SLW x (ANDconst [31] y)) (SRW x (SUBFCconst [32] (ANDconst [31] y)))) - // result: (ROTLW x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 { - continue - } - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] { - continue - } - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true + if auxIntToInt64(v.AuxInt) != 0 { + break } - break + x := v_0 + v.copyOf(x) + return true } - // match: (XOR (SLW x (ANDconst [31] y)) (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y)))) - // result: (ROTLW x y) + // match: (XORconst [1] (ISELB [6] (MOVDconst [1]) cmp)) + // result: (ISELB [2] (MOVDconst [1]) cmp) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64SLW { - continue - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 { - continue - } - y := v_0_1.Args[0] - if v_1.Op != OpPPC64SRW { - continue - } - _ = v_1.Args[1] - if x != v_1.Args[0] { - continue - } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64SUB || v_1_1.Type != typ.UInt { - continue - } - _ = v_1_1.Args[1] - v_1_1_0 := v_1_1.Args[0] - if v_1_1_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_1_0.AuxInt) != 32 { - continue - } - v_1_1_1 := v_1_1.Args[1] - if v_1_1_1.Op != OpPPC64ANDconst || v_1_1_1.Type != typ.UInt || auxIntToInt64(v_1_1_1.AuxInt) != 31 || y != v_1_1_1.Args[0] { - continue - } - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 6 { + break } - break - } - // match: (XOR (MOVDconst [c]) (MOVDconst [d])) - // result: (MOVDconst [c^d]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpPPC64MOVDconst { - continue - } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpPPC64MOVDconst { - continue - } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpPPC64MOVDconst) - v.AuxInt = int64ToAuxInt(c ^ d) - return true + cmp := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 { + break } - break + v.reset(OpPPC64ISELB) + v.AuxInt = int32ToAuxInt(2) + v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v0.AuxInt = int64ToAuxInt(1) + v.AddArg2(v0, cmp) + return true } - // match: (XOR x (MOVDconst [c])) - // cond: isU32Bit(c) - // result: (XORconst [c] x) + // match: (XORconst [1] (ISELB [5] (MOVDconst [1]) cmp)) + // result: (ISELB [1] (MOVDconst [1]) cmp) for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpPPC64MOVDconst { - continue - } - c := auxIntToInt64(v_1.AuxInt) - if !(isU32Bit(c)) { - continue - } - v.reset(OpPPC64XORconst) - v.AuxInt = int64ToAuxInt(c) - v.AddArg(x) - return true + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 5 { + break } - break - } - return false -} -func rewriteValuePPC64_OpPPC64XORconst(v *Value) bool { - v_0 := v.Args[0] - // match: (XORconst [c] (XORconst [d] x)) - // result: (XORconst [c^d] x) - for { - c := auxIntToInt64(v.AuxInt) - if v_0.Op != OpPPC64XORconst { + cmp := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 { break } - d := auxIntToInt64(v_0.AuxInt) - x := v_0.Args[0] - v.reset(OpPPC64XORconst) - v.AuxInt = int64ToAuxInt(c ^ d) - v.AddArg(x) + v.reset(OpPPC64ISELB) + v.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v0.AuxInt = int64ToAuxInt(1) + v.AddArg2(v0, cmp) return true } - // match: (XORconst [0] x) - // result: x + // match: (XORconst [1] (ISELB [4] (MOVDconst [1]) cmp)) + // result: (ISELB [0] (MOVDconst [1]) cmp) for { - if auxIntToInt64(v.AuxInt) != 0 { + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 4 { break } - x := v_0 - v.copyOf(x) + cmp := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 { + break + } + v.reset(OpPPC64ISELB) + v.AuxInt = int32ToAuxInt(0) + v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v0.AuxInt = int64ToAuxInt(1) + v.AddArg2(v0, cmp) return true } return false @@ -14000,6 +13713,34 @@ func rewriteValuePPC64_OpPopCount8(v *Value) bool { return true } } +func rewriteValuePPC64_OpPrefetchCache(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (PrefetchCache ptr mem) + // result: (DCBT ptr mem [0]) + for { + ptr := v_0 + mem := v_1 + v.reset(OpPPC64DCBT) + v.AuxInt = int64ToAuxInt(0) + v.AddArg2(ptr, mem) + return true + } +} +func rewriteValuePPC64_OpPrefetchCacheStreamed(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (PrefetchCacheStreamed ptr mem) + // result: (DCBT ptr mem [16]) + for { + ptr := v_0 + mem := v_1 + v.reset(OpPPC64DCBT) + v.AuxInt = int64ToAuxInt(16) + v.AddArg2(ptr, mem) + return true + } +} func rewriteValuePPC64_OpRotateLeft16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -14028,58 +13769,6 @@ func rewriteValuePPC64_OpRotateLeft16(v *Value) bool { } return false } -func rewriteValuePPC64_OpRotateLeft32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft32 x (MOVDconst [c])) - // result: (ROTLWconst [c&31] x) - for { - x := v_0 - if v_1.Op != OpPPC64MOVDconst { - break - } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpPPC64ROTLWconst) - v.AuxInt = int64ToAuxInt(c & 31) - v.AddArg(x) - return true - } - // match: (RotateLeft32 x y) - // result: (ROTLW x y) - for { - x := v_0 - y := v_1 - v.reset(OpPPC64ROTLW) - v.AddArg2(x, y) - return true - } -} -func rewriteValuePPC64_OpRotateLeft64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft64 x (MOVDconst [c])) - // result: (ROTLconst [c&63] x) - for { - x := v_0 - if v_1.Op != OpPPC64MOVDconst { - break - } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpPPC64ROTLconst) - v.AuxInt = int64ToAuxInt(c & 63) - v.AddArg(x) - return true - } - // match: (RotateLeft64 x y) - // result: (ROTL x y) - for { - x := v_0 - y := v_1 - v.reset(OpPPC64ROTL) - v.AddArg2(x, y) - return true - } -} func rewriteValuePPC64_OpRotateLeft8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -14698,7 +14387,7 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { return true } // match: (Rsh32Ux64 x (AND y (MOVDconst [31]))) - // result: (SRW x (ANDconst [31] y)) + // result: (SRW x (Select0 (ANDCCconst [31] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -14713,31 +14402,39 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { continue } v.reset(OpPPC64SRW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int32) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int32) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Rsh32Ux64 x (ANDconst [31] y)) - // result: (SRW x (ANDconst [31] y)) + // match: (Rsh32Ux64 x (Select0 (ANDCCconst [31] y))) + // result: (SRW x (Select0 (ANDCCconst [31] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 31 { + if v_1.Op != OpSelect0 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SRW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 x (SUB (MOVDconst [32]) (ANDconst [31] y))) - // result: (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y))) + // match: (Rsh32Ux64 x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) + // result: (SRW x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -14749,45 +14446,57 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { break } v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64ANDconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 31 { + if v_1_1.Op != OpSelect0 { + break + } + v_1_1_0 := v_1_1.Args[0] + if v_1_1_0.Op != OpPPC64ANDCCconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 { break } - y := v_1_1.Args[0] + y := v_1_1_0.Args[0] v.reset(OpPPC64SRW) v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(32) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(31) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(31) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 x (SUBFCconst [32] (ANDconst [31] y))) - // result: (SRW x (SUBFCconst [32] (ANDconst [31] y))) + // match: (Rsh32Ux64 x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) + // result: (SRW x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 { break } v_1_0 := v_1.Args[0] - if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 { + if v_1_0.Op != OpSelect0 { break } - y := v_1_0.Args[0] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || v_1_0_0.Type != typ.UInt || auxIntToInt64(v_1_0_0.AuxInt) != 31 { + break + } + y := v_1_0_0.Args[0] v.reset(OpPPC64SRW) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(32) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(31) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(31) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true } // match: (Rsh32Ux64 x (SUB (MOVDconst [32]) (AND y (MOVDconst [31])))) - // result: (SRW x (SUB (MOVDconst [32]) (ANDconst [31] y))) + // result: (SRW x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -14814,9 +14523,11 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(32) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(31) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(31) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true @@ -14824,7 +14535,7 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { break } // match: (Rsh32Ux64 x (SUBFCconst [32] (AND y (MOVDconst [31])))) - // result: (SRW x (SUBFCconst [32] (ANDconst [31] y))) + // result: (SRW x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 { @@ -14845,9 +14556,11 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool { v.reset(OpPPC64SRW) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(32) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(31) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(31) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true @@ -15058,7 +14771,7 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { return true } // match: (Rsh32x64 x (AND y (MOVDconst [31]))) - // result: (SRAW x (ANDconst [31] y)) + // result: (SRAW x (Select0 (ANDCCconst [31] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -15073,31 +14786,39 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { continue } v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int32) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int32) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Rsh32x64 x (ANDconst [31] y)) - // result: (SRAW x (ANDconst [31] y)) + // match: (Rsh32x64 x (Select0 (ANDCCconst [31] y))) + // result: (SRAW x (Select0 (ANDCCconst [31] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 31 { + if v_1.Op != OpSelect0 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v0.AuxInt = int64ToAuxInt(31) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(31) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } - // match: (Rsh32x64 x (SUB (MOVDconst [32]) (ANDconst [31] y))) - // result: (SRAW x (SUB (MOVDconst [32]) (ANDconst [31] y))) + // match: (Rsh32x64 x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) + // result: (SRAW x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15109,45 +14830,57 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { break } v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64ANDconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 31 { + if v_1_1.Op != OpSelect0 { + break + } + v_1_1_0 := v_1_1.Args[0] + if v_1_1_0.Op != OpPPC64ANDCCconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 { break } - y := v_1_1.Args[0] + y := v_1_1_0.Args[0] v.reset(OpPPC64SRAW) v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(32) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(31) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(31) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true } - // match: (Rsh32x64 x (SUBFCconst [32] (ANDconst [31] y))) - // result: (SRAW x (SUBFCconst [32] (ANDconst [31] y))) + // match: (Rsh32x64 x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) + // result: (SRAW x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 { break } v_1_0 := v_1.Args[0] - if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 { + if v_1_0.Op != OpSelect0 { break } - y := v_1_0.Args[0] - v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || v_1_0_0.Type != typ.UInt || auxIntToInt64(v_1_0_0.AuxInt) != 31 { + break + } + y := v_1_0_0.Args[0] + v.reset(OpPPC64SRAW) + v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(32) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(31) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(31) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true } // match: (Rsh32x64 x (SUB (MOVDconst [32]) (AND y (MOVDconst [31])))) - // result: (SRAW x (SUB (MOVDconst [32]) (ANDconst [31] y))) + // result: (SRAW x (SUB (MOVDconst [32]) (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15174,9 +14907,11 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(32) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(31) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(31) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true @@ -15184,7 +14919,7 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { break } // match: (Rsh32x64 x (SUBFCconst [32] (AND y (MOVDconst [31])))) - // result: (SRAW x (SUBFCconst [32] (ANDconst [31] y))) + // result: (SRAW x (SUBFCconst [32] (Select0 (ANDCCconst [31] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 { @@ -15205,9 +14940,11 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool { v.reset(OpPPC64SRAW) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(32) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(31) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(31) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true @@ -15416,7 +15153,7 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { return true } // match: (Rsh64Ux64 x (AND y (MOVDconst [63]))) - // result: (SRD x (ANDconst [63] y)) + // result: (SRD x (Select0 (ANDCCconst [63] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -15431,31 +15168,39 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { continue } v.reset(OpPPC64SRD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int64) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Rsh64Ux64 x (ANDconst [63] y)) - // result: (SRD x (ANDconst [63] y)) + // match: (Rsh64Ux64 x (Select0 (ANDCCconst [63] y))) + // result: (SRD x (Select0 (ANDCCconst [63] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 63 { + if v_1.Op != OpSelect0 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SRD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 x (SUB (MOVDconst [64]) (ANDconst [63] y))) - // result: (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y))) + // match: (Rsh64Ux64 x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) + // result: (SRD x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15467,45 +15212,57 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { break } v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64ANDconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 63 { + if v_1_1.Op != OpSelect0 { break } - y := v_1_1.Args[0] + v_1_1_0 := v_1_1.Args[0] + if v_1_1_0.Op != OpPPC64ANDCCconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 { + break + } + y := v_1_1_0.Args[0] v.reset(OpPPC64SRD) v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(64) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(63) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(63) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 x (SUBFCconst [64] (ANDconst [63] y))) - // result: (SRD x (SUBFCconst [64] (ANDconst [63] y))) + // match: (Rsh64Ux64 x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) + // result: (SRD x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 { break } v_1_0 := v_1.Args[0] - if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 { + if v_1_0.Op != OpSelect0 { break } - y := v_1_0.Args[0] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || v_1_0_0.Type != typ.UInt || auxIntToInt64(v_1_0_0.AuxInt) != 63 { + break + } + y := v_1_0_0.Args[0] v.reset(OpPPC64SRD) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(64) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(63) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(63) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true } // match: (Rsh64Ux64 x (SUB (MOVDconst [64]) (AND y (MOVDconst [63])))) - // result: (SRD x (SUB (MOVDconst [64]) (ANDconst [63] y))) + // result: (SRD x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15532,9 +15289,11 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(64) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(63) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(63) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true @@ -15542,7 +15301,7 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { break } // match: (Rsh64Ux64 x (SUBFCconst [64] (AND y (MOVDconst [63])))) - // result: (SRD x (SUBFCconst [64] (ANDconst [63] y))) + // result: (SRD x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 { @@ -15563,9 +15322,11 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool { v.reset(OpPPC64SRD) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(64) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(63) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(63) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true @@ -15776,7 +15537,7 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { return true } // match: (Rsh64x64 x (AND y (MOVDconst [63]))) - // result: (SRAD x (ANDconst [63] y)) + // result: (SRAD x (Select0 (ANDCCconst [63] y))) for { x := v_0 if v_1.Op != OpPPC64AND { @@ -15791,31 +15552,39 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { continue } v.reset(OpPPC64SRAD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.Int64) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.Int64) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } break } - // match: (Rsh64x64 x (ANDconst [63] y)) - // result: (SRAD x (ANDconst [63] y)) + // match: (Rsh64x64 x (Select0 (ANDCCconst [63] y))) + // result: (SRAD x (Select0 (ANDCCconst [63] y))) for { x := v_0 - if v_1.Op != OpPPC64ANDconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 63 { + if v_1.Op != OpSelect0 { break } - y := v_1.Args[0] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpPPC64ANDCCconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 { + break + } + y := v_1_0.Args[0] v.reset(OpPPC64SRAD) - v0 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v0.AuxInt = int64ToAuxInt(63) - v0.AddArg(y) + v0 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(63) + v1.AddArg(y) + v0.AddArg(v1) v.AddArg2(x, v0) return true } - // match: (Rsh64x64 x (SUB (MOVDconst [64]) (ANDconst [63] y))) - // result: (SRAD x (SUB (MOVDconst [64]) (ANDconst [63] y))) + // match: (Rsh64x64 x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) + // result: (SRAD x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15827,45 +15596,57 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { break } v_1_1 := v_1.Args[1] - if v_1_1.Op != OpPPC64ANDconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 63 { + if v_1_1.Op != OpSelect0 { + break + } + v_1_1_0 := v_1_1.Args[0] + if v_1_1_0.Op != OpPPC64ANDCCconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 { break } - y := v_1_1.Args[0] + y := v_1_1_0.Args[0] v.reset(OpPPC64SRAD) v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(64) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(63) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(63) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true } - // match: (Rsh64x64 x (SUBFCconst [64] (ANDconst [63] y))) - // result: (SRAD x (SUBFCconst [64] (ANDconst [63] y))) + // match: (Rsh64x64 x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) + // result: (SRAD x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 { break } v_1_0 := v_1.Args[0] - if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 { + if v_1_0.Op != OpSelect0 { break } - y := v_1_0.Args[0] + v_1_0_0 := v_1_0.Args[0] + if v_1_0_0.Op != OpPPC64ANDCCconst || v_1_0_0.Type != typ.UInt || auxIntToInt64(v_1_0_0.AuxInt) != 63 { + break + } + y := v_1_0_0.Args[0] v.reset(OpPPC64SRAD) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(64) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(63) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(63) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true } // match: (Rsh64x64 x (SUB (MOVDconst [64]) (AND y (MOVDconst [63])))) - // result: (SRAD x (SUB (MOVDconst [64]) (ANDconst [63] y))) + // result: (SRAD x (SUB (MOVDconst [64]) (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUB || v_1.Type != typ.UInt { @@ -15892,9 +15673,11 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { v0 := b.NewValue0(v.Pos, OpPPC64SUB, typ.UInt) v1 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) v1.AuxInt = int64ToAuxInt(64) - v2 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v2.AuxInt = int64ToAuxInt(63) - v2.AddArg(y) + v2 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v3 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v3.AuxInt = int64ToAuxInt(63) + v3.AddArg(y) + v2.AddArg(v3) v0.AddArg2(v1, v2) v.AddArg2(x, v0) return true @@ -15902,7 +15685,7 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { break } // match: (Rsh64x64 x (SUBFCconst [64] (AND y (MOVDconst [63])))) - // result: (SRAD x (SUBFCconst [64] (ANDconst [63] y))) + // result: (SRAD x (SUBFCconst [64] (Select0 (ANDCCconst [63] y)))) for { x := v_0 if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 { @@ -15923,9 +15706,11 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool { v.reset(OpPPC64SRAD) v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt) v0.AuxInt = int64ToAuxInt(64) - v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt) - v1.AuxInt = int64ToAuxInt(63) - v1.AddArg(y) + v1 := b.NewValue0(v.Pos, OpSelect0, typ.UInt) + v2 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(63) + v2.AddArg(y) + v1.AddArg(v2) v0.AddArg(v1) v.AddArg2(x, v0) return true @@ -16338,103 +16123,619 @@ func rewriteValuePPC64_OpRsh8x64(v *Value) bool { v.AddArg(v0) return true } - // match: (Rsh8x64 x (MOVDconst [c])) - // cond: uint64(c) < 8 - // result: (SRAWconst (SignExt8to32 x) [c]) + // match: (Rsh8x64 x (MOVDconst [c])) + // cond: uint64(c) < 8 + // result: (SRAWconst (SignExt8to32 x) [c]) + for { + x := v_0 + if v_1.Op != OpPPC64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) < 8) { + break + } + v.reset(OpPPC64SRAWconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Rsh8x64 x y) + // cond: shiftIsBounded(v) + // result: (SRAW (MOVBreg x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpPPC64SRAW) + v0 := b.NewValue0(v.Pos, OpPPC64MOVBreg, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8x64 x y) + // result: (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) + for { + x := v_0 + y := v_1 + v.reset(OpPPC64SRAW) + v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpPPC64ISEL, typ.Int32) + v1.AuxInt = int32ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v2.AuxInt = int64ToAuxInt(-1) + v3 := b.NewValue0(v.Pos, OpPPC64CMPU, types.TypeFlags) + v4 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v4.AuxInt = int64ToAuxInt(8) + v3.AddArg2(y, v4) + v1.AddArg3(y, v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValuePPC64_OpRsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8x8 x y) + // cond: shiftIsBounded(v) + // result: (SRAW (MOVBreg x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpPPC64SRAW) + v0 := b.NewValue0(v.Pos, OpPPC64MOVBreg, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + // match: (Rsh8x8 x y) + // result: (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) + for { + x := v_0 + y := v_1 + v.reset(OpPPC64SRAW) + v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) + v0.AddArg(x) + v1 := b.NewValue0(v.Pos, OpPPC64ISEL, typ.Int32) + v1.AuxInt = int32ToAuxInt(0) + v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v2.AuxInt = int64ToAuxInt(-1) + v3 := b.NewValue0(v.Pos, OpPPC64CMPU, types.TypeFlags) + v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v4.AddArg(y) + v5 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) + v5.AuxInt = int64ToAuxInt(8) + v3.AddArg2(v4, v5) + v1.AddArg3(y, v2, v3) + v.AddArg2(v0, v1) + return true + } +} +func rewriteValuePPC64_OpSelect0(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select0 (Add64carry x y c)) + // result: (Select0 (ADDE x y (Select1 (ADDCconst c [-1])))) + for { + if v_0.Op != OpAdd64carry { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpSelect0) + v.Type = typ.UInt64 + v0 := b.NewValue0(v.Pos, OpPPC64ADDE, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpPPC64ADDCconst, types.NewTuple(typ.UInt64, typ.UInt64)) + v2.AuxInt = int64ToAuxInt(-1) + v2.AddArg(c) + v1.AddArg(v2) + v0.AddArg3(x, y, v1) + v.AddArg(v0) + return true + } + // match: (Select0 (Sub64borrow x y c)) + // result: (Select0 (SUBE x y (Select1 (SUBCconst c [0])))) + for { + if v_0.Op != OpSub64borrow { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpSelect0) + v.Type = typ.UInt64 + v0 := b.NewValue0(v.Pos, OpPPC64SUBE, types.NewTuple(typ.UInt64, typ.UInt64)) + v1 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpPPC64SUBCconst, types.NewTuple(typ.UInt64, typ.UInt64)) + v2.AuxInt = int64ToAuxInt(0) + v2.AddArg(c) + v1.AddArg(v2) + v0.AddArg3(x, y, v1) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [m] (ROTLWconst [r] x))) + // cond: isPPC64WordRotateMask(m) + // result: (RLWINM [encodePPC64RotateMask(r,m,32)] x) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ROTLWconst { + break + } + r := auxIntToInt64(v_0_0.AuxInt) + x := v_0_0.Args[0] + if !(isPPC64WordRotateMask(m)) { + break + } + v.reset(OpPPC64RLWINM) + v.AuxInt = int64ToAuxInt(encodePPC64RotateMask(r, m, 32)) + v.AddArg(x) + return true + } + // match: (Select0 (ANDCCconst [m] (ROTLW x r))) + // cond: isPPC64WordRotateMask(m) + // result: (RLWNM [encodePPC64RotateMask(0,m,32)] x r) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64ROTLW { + break + } + r := v_0_0.Args[1] + x := v_0_0.Args[0] + if !(isPPC64WordRotateMask(m)) { + break + } + v.reset(OpPPC64RLWNM) + v.AuxInt = int64ToAuxInt(encodePPC64RotateMask(0, m, 32)) + v.AddArg2(x, r) + return true + } + // match: (Select0 (ANDCCconst [m] (SRWconst x [s]))) + // cond: mergePPC64RShiftMask(m,s,32) == 0 + // result: (MOVDconst [0]) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64SRWconst { + break + } + s := auxIntToInt64(v_0_0.AuxInt) + if !(mergePPC64RShiftMask(m, s, 32) == 0) { + break + } + v.reset(OpPPC64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (Select0 (ANDCCconst [m] (SRWconst x [s]))) + // cond: mergePPC64AndSrwi(m,s) != 0 + // result: (RLWINM [mergePPC64AndSrwi(m,s)] x) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + m := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64SRWconst { + break + } + s := auxIntToInt64(v_0_0.AuxInt) + x := v_0_0.Args[0] + if !(mergePPC64AndSrwi(m, s) != 0) { + break + } + v.reset(OpPPC64RLWINM) + v.AuxInt = int64ToAuxInt(mergePPC64AndSrwi(m, s)) + v.AddArg(x) + return true + } + // match: (Select0 (ANDCCconst [c] (Select0 (ANDCCconst [d] x)))) + // result: (Select0 (ANDCCconst [c&d] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + d := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [-1] x)) + // result: x + for { + if v_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0.AuxInt) != -1 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Select0 (ANDCCconst [0] _)) + // result: (MOVDconst [0]) + for { + if v_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + v.reset(OpPPC64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (Select0 (ANDCCconst [c] y:(MOVBZreg _))) + // cond: c&0xFF == 0xFF + // result: y + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + y := v_0.Args[0] + if y.Op != OpPPC64MOVBZreg || !(c&0xFF == 0xFF) { + break + } + v.copyOf(y) + return true + } + // match: (Select0 (ANDCCconst [0xFF] y:(MOVBreg _))) + // result: y + for { + if v_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0.AuxInt) != 0xFF { + break + } + y := v_0.Args[0] + if y.Op != OpPPC64MOVBreg { + break + } + v.copyOf(y) + return true + } + // match: (Select0 (ANDCCconst [c] y:(MOVHZreg _))) + // cond: c&0xFFFF == 0xFFFF + // result: y + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + y := v_0.Args[0] + if y.Op != OpPPC64MOVHZreg || !(c&0xFFFF == 0xFFFF) { + break + } + v.copyOf(y) + return true + } + // match: (Select0 (ANDCCconst [0xFFFF] y:(MOVHreg _))) + // result: y + for { + if v_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0.AuxInt) != 0xFFFF { + break + } + y := v_0.Args[0] + if y.Op != OpPPC64MOVHreg { + break + } + v.copyOf(y) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVBreg x))) + // result: (Select0 (ANDCCconst [c&0xFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVBreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVBZreg x))) + // result: (Select0 (ANDCCconst [c&0xFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVBZreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVHreg x))) + // result: (Select0 (ANDCCconst [c&0xFFFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVHreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFFFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVHZreg x))) + // result: (Select0 (ANDCCconst [c&0xFFFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVHZreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFFFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVWreg x))) + // result: (Select0 (ANDCCconst [c&0xFFFFFFFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVWreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFFFFFFFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Select0 (ANDCCconst [c] (MOVWZreg x))) + // result: (Select0 (ANDCCconst [c&0xFFFFFFFF] x)) + for { + if v_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpPPC64MOVWZreg { + break + } + x := v_0_0.Args[0] + v.reset(OpSelect0) + v0 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v0.AuxInt = int64ToAuxInt(c & 0xFFFFFFFF) + v0.AddArg(x) + v.AddArg(v0) + return true + } + return false +} +func rewriteValuePPC64_OpSelect1(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select1 (Add64carry x y c)) + // result: (ADDZEzero (Select1 (ADDE x y (Select1 (ADDCconst c [-1]))))) + for { + if v_0.Op != OpAdd64carry { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpPPC64ADDZEzero) + v0 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpPPC64ADDE, types.NewTuple(typ.UInt64, typ.UInt64)) + v2 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v3 := b.NewValue0(v.Pos, OpPPC64ADDCconst, types.NewTuple(typ.UInt64, typ.UInt64)) + v3.AuxInt = int64ToAuxInt(-1) + v3.AddArg(c) + v2.AddArg(v3) + v1.AddArg3(x, y, v2) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + // match: (Select1 (ADDCconst n:(ADDZEzero x) [-1])) + // cond: n.Uses <= 2 + // result: x for { - x := v_0 - if v_1.Op != OpPPC64MOVDconst { + if v_0.Op != OpPPC64ADDCconst || auxIntToInt64(v_0.AuxInt) != -1 { break } - c := auxIntToInt64(v_1.AuxInt) - if !(uint64(c) < 8) { + n := v_0.Args[0] + if n.Op != OpPPC64ADDZEzero { break } - v.reset(OpPPC64SRAWconst) - v.AuxInt = int64ToAuxInt(c) - v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) - v0.AddArg(x) - v.AddArg(v0) + x := n.Args[0] + if !(n.Uses <= 2) { + break + } + v.copyOf(x) return true } - // match: (Rsh8x64 x y) - // cond: shiftIsBounded(v) - // result: (SRAW (MOVBreg x) y) + // match: (Select1 (Sub64borrow x y c)) + // result: (NEG (SUBZEzero (Select1 (SUBE x y (Select1 (SUBCconst c [0])))))) for { - x := v_0 - y := v_1 - if !(shiftIsBounded(v)) { + if v_0.Op != OpSub64borrow { break } - v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpPPC64MOVBreg, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpPPC64NEG) + v0 := b.NewValue0(v.Pos, OpPPC64SUBZEzero, typ.UInt64) + v1 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v2 := b.NewValue0(v.Pos, OpPPC64SUBE, types.NewTuple(typ.UInt64, typ.UInt64)) + v3 := b.NewValue0(v.Pos, OpSelect1, typ.UInt64) + v4 := b.NewValue0(v.Pos, OpPPC64SUBCconst, types.NewTuple(typ.UInt64, typ.UInt64)) + v4.AuxInt = int64ToAuxInt(0) + v4.AddArg(c) + v3.AddArg(v4) + v2.AddArg3(x, y, v3) + v1.AddArg(v2) + v0.AddArg(v1) + v.AddArg(v0) return true } - // match: (Rsh8x64 x y) - // result: (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [8])))) + // match: (Select1 (SUBCconst n:(NEG (SUBZEzero x)) [0])) + // cond: n.Uses <= 2 + // result: x for { - x := v_0 - y := v_1 - v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpPPC64ISEL, typ.Int32) - v1.AuxInt = int32ToAuxInt(0) - v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpPPC64CMPU, types.TypeFlags) - v4 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) - v4.AuxInt = int64ToAuxInt(8) - v3.AddArg2(y, v4) - v1.AddArg3(y, v2, v3) - v.AddArg2(v0, v1) + if v_0.Op != OpPPC64SUBCconst || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + n := v_0.Args[0] + if n.Op != OpPPC64NEG { + break + } + n_0 := n.Args[0] + if n_0.Op != OpPPC64SUBZEzero { + break + } + x := n_0.Args[0] + if !(n.Uses <= 2) { + break + } + v.copyOf(x) return true } + return false } -func rewriteValuePPC64_OpRsh8x8(v *Value) bool { - v_1 := v.Args[1] +func rewriteValuePPC64_OpSelectN(v *Value) bool { v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh8x8 x y) - // cond: shiftIsBounded(v) - // result: (SRAW (MOVBreg x) y) + config := b.Func.Config + // match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem))))) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call) + // result: (Move [sz] dst src mem) for { - x := v_0 - y := v_1 - if !(shiftIsBounded(v)) { + if auxIntToInt64(v.AuxInt) != 0 { break } - v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpPPC64MOVBreg, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + call := v_0 + if call.Op != OpPPC64CALLstatic || len(call.Args) != 1 { + break + } + sym := auxToCall(call.Aux) + s1 := call.Args[0] + if s1.Op != OpPPC64MOVDstore { + break + } + _ = s1.Args[2] + s1_1 := s1.Args[1] + if s1_1.Op != OpPPC64MOVDconst { + break + } + sz := auxIntToInt64(s1_1.AuxInt) + s2 := s1.Args[2] + if s2.Op != OpPPC64MOVDstore { + break + } + _ = s2.Args[2] + src := s2.Args[1] + s3 := s2.Args[2] + if s3.Op != OpPPC64MOVDstore { + break + } + mem := s3.Args[2] + dst := s3.Args[1] + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(sz) + v.AddArg3(dst, src, mem) return true } - // match: (Rsh8x8 x y) - // result: (SRAW (SignExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8])))) + // match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem)) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call) + // result: (Move [sz] dst src mem) for { - x := v_0 - y := v_1 - v.reset(OpPPC64SRAW) - v0 := b.NewValue0(v.Pos, OpSignExt8to32, typ.Int32) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpPPC64ISEL, typ.Int32) - v1.AuxInt = int32ToAuxInt(0) - v2 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpPPC64CMPU, types.TypeFlags) - v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v4.AddArg(y) - v5 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64) - v5.AuxInt = int64ToAuxInt(8) - v3.AddArg2(v4, v5) - v1.AddArg3(y, v2, v3) - v.AddArg2(v0, v1) + if auxIntToInt64(v.AuxInt) != 0 { + break + } + call := v_0 + if call.Op != OpPPC64CALLstatic || len(call.Args) != 4 { + break + } + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpPPC64MOVDconst { + break + } + sz := auxIntToInt64(call_2.AuxInt) + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(sz) + v.AddArg3(dst, src, mem) return true } + return false } func rewriteValuePPC64_OpSlicemask(v *Value) bool { v_0 := v.Args[0] @@ -16502,14 +16803,14 @@ func rewriteValuePPC64_OpStore(v *Value) bool { return true } // match: (Store {t} ptr val mem) - // cond: t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type)) + // cond: t.Size() == 8 && !is64BitFloat(val.Type) // result: (MOVDstore ptr val mem) for { t := auxToType(v.Aux) ptr := v_0 val := v_1 mem := v_2 - if !(t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type))) { + if !(t.Size() == 8 && !is64BitFloat(val.Type)) { break } v.reset(OpPPC64MOVDstore) @@ -16968,43 +17269,56 @@ func rewriteValuePPC64_OpZero(v *Value) bool { return false } func rewriteBlockPPC64(b *Block) bool { + typ := &b.Func.Config.Types switch b.Kind { case BlockPPC64EQ: - // match: (EQ (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (EQ (ANDCCconst [c] x) yes no) + // match: (EQ (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (EQ (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64EQ, v0) return true } - // match: (EQ (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (EQ (ANDCCconst [c] x) yes no) + // match: (EQ (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (EQ (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64EQ, v0) return true } @@ -17036,41 +17350,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64EQ, cmp) return true } - // match: (EQ (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (EQ (ANDCCconst [c] x) yes no) + // match: (EQ (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (EQ (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64EQ, v0) return true } - // match: (EQ (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (EQ (ANDCCconst [c] x) yes no) + // match: (EQ (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (EQ (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64EQ, v0) return true } @@ -17186,41 +17512,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64LE, cmp) return true } - // match: (GE (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (GE (ANDCCconst [c] x) yes no) + // match: (GE (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (GE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64GE, v0) return true } - // match: (GE (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (GE (ANDCCconst [c] x) yes no) + // match: (GE (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (GE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64GE, v0) return true } @@ -17337,41 +17675,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64LT, cmp) return true } - // match: (GT (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (GT (ANDCCconst [c] x) yes no) + // match: (GT (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (GT (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64GT, v0) return true } - // match: (GT (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (GT (ANDCCconst [c] x) yes no) + // match: (GT (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (GT (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64GT, v0) return true } @@ -17541,12 +17891,17 @@ func rewriteBlockPPC64(b *Block) bool { return true } // match: (If cond yes no) - // result: (NE (CMPWconst [0] cond) yes no) + // result: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] cond))) yes no) for { cond := b.Controls[0] v0 := b.NewValue0(cond.Pos, OpPPC64CMPWconst, types.TypeFlags) v0.AuxInt = int32ToAuxInt(0) - v0.AddArg(cond) + v1 := b.NewValue0(cond.Pos, OpSelect0, typ.UInt32) + v2 := b.NewValue0(cond.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v2.AuxInt = int64ToAuxInt(1) + v2.AddArg(cond) + v1.AddArg(v2) + v0.AddArg(v1) b.resetWithControl(BlockPPC64NE, v0) return true } @@ -17578,41 +17933,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64GE, cmp) return true } - // match: (LE (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (LE (ANDCCconst [c] x) yes no) + // match: (LE (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (LE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64LE, v0) return true } - // match: (LE (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (LE (ANDCCconst [c] x) yes no) + // match: (LE (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (LE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64LE, v0) return true } @@ -17729,41 +18096,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64GT, cmp) return true } - // match: (LT (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (LT (ANDCCconst [c] x) yes no) + // match: (LT (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (LT (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64LT, v0) return true } - // match: (LT (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (LT (ANDCCconst [c] x) yes no) + // match: (LT (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (LT (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64LT, v0) return true } @@ -17852,7 +18231,7 @@ func rewriteBlockPPC64(b *Block) bool { break } case BlockPPC64NE: - // match: (NE (CMPWconst [0] (Equal cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (Equal cc)))) yes no) // result: (EQ cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17860,14 +18239,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64Equal { + if v_0_0.Op != OpSelect0 { break } - cc := v_0_0.Args[0] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64Equal { + break + } + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64EQ, cc) return true } - // match: (NE (CMPWconst [0] (NotEqual cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (NotEqual cc)))) yes no) // result: (NE cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17875,14 +18262,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64NotEqual { + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64NotEqual { break } - cc := v_0_0.Args[0] + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64NE, cc) return true } - // match: (NE (CMPWconst [0] (LessThan cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (LessThan cc)))) yes no) // result: (LT cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17890,14 +18285,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64LessThan { + if v_0_0.Op != OpSelect0 { break } - cc := v_0_0.Args[0] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64LessThan { + break + } + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64LT, cc) return true } - // match: (NE (CMPWconst [0] (LessEqual cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (LessEqual cc)))) yes no) // result: (LE cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17905,14 +18308,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64LessEqual { + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64LessEqual { break } - cc := v_0_0.Args[0] + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64LE, cc) return true } - // match: (NE (CMPWconst [0] (GreaterThan cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (GreaterThan cc)))) yes no) // result: (GT cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17920,14 +18331,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64GreaterThan { + if v_0_0.Op != OpSelect0 { break } - cc := v_0_0.Args[0] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64GreaterThan { + break + } + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64GT, cc) return true } - // match: (NE (CMPWconst [0] (GreaterEqual cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (GreaterEqual cc)))) yes no) // result: (GE cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17935,14 +18354,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64GreaterEqual { + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64GreaterEqual { break } - cc := v_0_0.Args[0] + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64GE, cc) return true } - // match: (NE (CMPWconst [0] (FLessThan cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (FLessThan cc)))) yes no) // result: (FLT cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17950,14 +18377,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64FLessThan { + if v_0_0.Op != OpSelect0 { break } - cc := v_0_0.Args[0] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64FLessThan { + break + } + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64FLT, cc) return true } - // match: (NE (CMPWconst [0] (FLessEqual cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (FLessEqual cc)))) yes no) // result: (FLE cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17965,14 +18400,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64FLessEqual { + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64FLessEqual { break } - cc := v_0_0.Args[0] + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64FLE, cc) return true } - // match: (NE (CMPWconst [0] (FGreaterThan cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (FGreaterThan cc)))) yes no) // result: (FGT cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17980,14 +18423,22 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64FGreaterThan { + if v_0_0.Op != OpSelect0 { break } - cc := v_0_0.Args[0] + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64FGreaterThan { + break + } + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64FGT, cc) return true } - // match: (NE (CMPWconst [0] (FGreaterEqual cc)) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [1] (FGreaterEqual cc)))) yes no) // result: (FGE cc yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] @@ -17995,48 +18446,68 @@ func rewriteBlockPPC64(b *Block) bool { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64FGreaterEqual { + if v_0_0.Op != OpSelect0 { + break + } + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 { + break + } + v_0_0_0_0 := v_0_0_0.Args[0] + if v_0_0_0_0.Op != OpPPC64FGreaterEqual { break } - cc := v_0_0.Args[0] + cc := v_0_0_0_0.Args[0] b.resetWithControl(BlockPPC64FGE, cc) return true } - // match: (NE (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (NE (ANDCCconst [c] x) yes no) + // match: (NE (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (NE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64NE, v0) return true } - // match: (NE (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (NE (ANDCCconst [c] x) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (NE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64NE, v0) return true } @@ -18067,41 +18538,53 @@ func rewriteBlockPPC64(b *Block) bool { b.resetWithControl(BlockPPC64NE, cmp) return true } - // match: (NE (CMPconst [0] (ANDconst [c] x)) yes no) - // result: (NE (ANDCCconst [c] x) yes no) + // match: (NE (CMPconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (NE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPconst { v_0 := b.Controls[0] if auxIntToInt64(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64NE, v0) return true } - // match: (NE (CMPWconst [0] (ANDconst [c] x)) yes no) - // result: (NE (ANDCCconst [c] x) yes no) + // match: (NE (CMPWconst [0] (Select0 (ANDCCconst [c] x))) yes no) + // result: (NE (Select1 (ANDCCconst [c] x)) yes no) for b.Controls[0].Op == OpPPC64CMPWconst { v_0 := b.Controls[0] if auxIntToInt32(v_0.AuxInt) != 0 { break } v_0_0 := v_0.Args[0] - if v_0_0.Op != OpPPC64ANDconst { + if v_0_0.Op != OpSelect0 { break } - c := auxIntToInt64(v_0_0.AuxInt) - x := v_0_0.Args[0] - v0 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.TypeFlags) - v0.AuxInt = int64ToAuxInt(c) - v0.AddArg(x) + v_0_0_0 := v_0_0.Args[0] + if v_0_0_0.Op != OpPPC64ANDCCconst { + break + } + c := auxIntToInt64(v_0_0_0.AuxInt) + x := v_0_0_0.Args[0] + v0 := b.NewValue0(v_0.Pos, OpSelect1, types.TypeFlags) + v1 := b.NewValue0(v_0.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags)) + v1.AuxInt = int64ToAuxInt(c) + v1.AddArg(x) + v0.AddArg(v1) b.resetWithControl(BlockPPC64NE, v0) return true } diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index 431fb1aaf66e0d..66b729f046591c 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -8,6 +8,9 @@ import "cmd/compile/internal/types" func rewriteValueRISCV64(v *Value) bool { switch v.Op { + case OpAbs: + v.Op = OpRISCV64FABSD + return true case OpAdd16: v.Op = OpRISCV64ADD return true @@ -134,6 +137,9 @@ func rewriteValueRISCV64(v *Value) bool { case OpConvert: v.Op = OpRISCV64MOVconvert return true + case OpCopysign: + v.Op = OpRISCV64FSGNJD + return true case OpCvt32Fto32: v.Op = OpRISCV64FCVTWS return true @@ -209,6 +215,9 @@ func rewriteValueRISCV64(v *Value) bool { return rewriteValueRISCV64_OpEqB(v) case OpEqPtr: return rewriteValueRISCV64_OpEqPtr(v) + case OpFMA: + v.Op = OpRISCV64FMADDD + return true case OpGetCallerPC: v.Op = OpRISCV64LoweredGetCallerPC return true @@ -356,6 +365,12 @@ func rewriteValueRISCV64(v *Value) bool { case OpMul64F: v.Op = OpRISCV64FMULD return true + case OpMul64uhilo: + v.Op = OpRISCV64LoweredMuluhilo + return true + case OpMul64uover: + v.Op = OpRISCV64LoweredMuluover + return true case OpMul8: return rewriteValueRISCV64_OpMul8(v) case OpNeg16: @@ -426,6 +441,16 @@ func rewriteValueRISCV64(v *Value) bool { return rewriteValueRISCV64_OpRISCV64ADDI(v) case OpRISCV64AND: return rewriteValueRISCV64_OpRISCV64AND(v) + case OpRISCV64ANDI: + return rewriteValueRISCV64_OpRISCV64ANDI(v) + case OpRISCV64FMADDD: + return rewriteValueRISCV64_OpRISCV64FMADDD(v) + case OpRISCV64FMSUBD: + return rewriteValueRISCV64_OpRISCV64FMSUBD(v) + case OpRISCV64FNMADDD: + return rewriteValueRISCV64_OpRISCV64FNMADDD(v) + case OpRISCV64FNMSUBD: + return rewriteValueRISCV64_OpRISCV64FNMSUBD(v) case OpRISCV64MOVBUload: return rewriteValueRISCV64_OpRISCV64MOVBUload(v) case OpRISCV64MOVBUreg: @@ -472,14 +497,38 @@ func rewriteValueRISCV64(v *Value) bool { return rewriteValueRISCV64_OpRISCV64MOVWstore(v) case OpRISCV64MOVWstorezero: return rewriteValueRISCV64_OpRISCV64MOVWstorezero(v) + case OpRISCV64NEG: + return rewriteValueRISCV64_OpRISCV64NEG(v) + case OpRISCV64NEGW: + return rewriteValueRISCV64_OpRISCV64NEGW(v) case OpRISCV64OR: return rewriteValueRISCV64_OpRISCV64OR(v) + case OpRISCV64ORI: + return rewriteValueRISCV64_OpRISCV64ORI(v) + case OpRISCV64SEQZ: + return rewriteValueRISCV64_OpRISCV64SEQZ(v) case OpRISCV64SLL: return rewriteValueRISCV64_OpRISCV64SLL(v) + case OpRISCV64SLLI: + return rewriteValueRISCV64_OpRISCV64SLLI(v) + case OpRISCV64SLT: + return rewriteValueRISCV64_OpRISCV64SLT(v) + case OpRISCV64SLTI: + return rewriteValueRISCV64_OpRISCV64SLTI(v) + case OpRISCV64SLTIU: + return rewriteValueRISCV64_OpRISCV64SLTIU(v) + case OpRISCV64SLTU: + return rewriteValueRISCV64_OpRISCV64SLTU(v) + case OpRISCV64SNEZ: + return rewriteValueRISCV64_OpRISCV64SNEZ(v) case OpRISCV64SRA: return rewriteValueRISCV64_OpRISCV64SRA(v) + case OpRISCV64SRAI: + return rewriteValueRISCV64_OpRISCV64SRAI(v) case OpRISCV64SRL: return rewriteValueRISCV64_OpRISCV64SRL(v) + case OpRISCV64SRLI: + return rewriteValueRISCV64_OpRISCV64SRLI(v) case OpRISCV64SUB: return rewriteValueRISCV64_OpRISCV64SUB(v) case OpRISCV64SUBW: @@ -564,6 +613,10 @@ func rewriteValueRISCV64(v *Value) bool { return rewriteValueRISCV64_OpRsh8x64(v) case OpRsh8x8: return rewriteValueRISCV64_OpRsh8x8(v) + case OpSelect0: + return rewriteValueRISCV64_OpSelect0(v) + case OpSelect1: + return rewriteValueRISCV64_OpSelect1(v) case OpSignExt16to32: v.Op = OpRISCV64MOVHreg return true @@ -616,6 +669,9 @@ func rewriteValueRISCV64(v *Value) bool { case OpSubPtr: v.Op = OpRISCV64SUB return true + case OpTailCall: + v.Op = OpRISCV64CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true @@ -1080,13 +1136,14 @@ func rewriteValueRISCV64_OpEqPtr(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + typ := &b.Func.Config.Types // match: (EqPtr x y) - // result: (SEQZ (SUB x y)) + // result: (SEQZ (SUB x y)) for { x := v_0 y := v_1 v.reset(OpRISCV64SEQZ) - v0 := b.NewValue0(v.Pos, OpRISCV64SUB, x.Type) + v0 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.Uintptr) v0.AddArg2(x, y) v.AddArg(v0) return true @@ -1550,11 +1607,15 @@ func rewriteValueRISCV64_OpLsh16x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1568,6 +1629,20 @@ func rewriteValueRISCV64_OpLsh16x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh16x16 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh16x32(v *Value) bool { v_1 := v.Args[1] @@ -1575,11 +1650,15 @@ func rewriteValueRISCV64_OpLsh16x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1593,17 +1672,35 @@ func rewriteValueRISCV64_OpLsh16x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh16x32 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh16x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh16x64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg16 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1615,6 +1712,20 @@ func rewriteValueRISCV64_OpLsh16x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh16x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh16x8(v *Value) bool { v_1 := v.Args[1] @@ -1622,11 +1733,15 @@ func rewriteValueRISCV64_OpLsh16x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh16x8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1640,6 +1755,20 @@ func rewriteValueRISCV64_OpLsh16x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh16x8 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh32x16(v *Value) bool { v_1 := v.Args[1] @@ -1647,11 +1776,15 @@ func rewriteValueRISCV64_OpLsh32x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1665,6 +1798,20 @@ func rewriteValueRISCV64_OpLsh32x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh32x16 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh32x32(v *Value) bool { v_1 := v.Args[1] @@ -1672,11 +1819,15 @@ func rewriteValueRISCV64_OpLsh32x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1690,17 +1841,35 @@ func rewriteValueRISCV64_OpLsh32x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh32x32 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh32x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh32x64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg32 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1712,6 +1881,20 @@ func rewriteValueRISCV64_OpLsh32x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh32x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh32x8(v *Value) bool { v_1 := v.Args[1] @@ -1719,11 +1902,15 @@ func rewriteValueRISCV64_OpLsh32x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh32x8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1737,6 +1924,20 @@ func rewriteValueRISCV64_OpLsh32x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh32x8 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh64x16(v *Value) bool { v_1 := v.Args[1] @@ -1744,11 +1945,15 @@ func rewriteValueRISCV64_OpLsh64x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1762,6 +1967,20 @@ func rewriteValueRISCV64_OpLsh64x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh64x16 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh64x32(v *Value) bool { v_1 := v.Args[1] @@ -1769,11 +1988,15 @@ func rewriteValueRISCV64_OpLsh64x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1787,17 +2010,35 @@ func rewriteValueRISCV64_OpLsh64x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh64x32 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh64x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh64x64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg64 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1809,6 +2050,20 @@ func rewriteValueRISCV64_OpLsh64x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh64x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh64x8(v *Value) bool { v_1 := v.Args[1] @@ -1816,11 +2071,15 @@ func rewriteValueRISCV64_OpLsh64x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh64x8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1834,6 +2093,20 @@ func rewriteValueRISCV64_OpLsh64x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh64x8 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh8x16(v *Value) bool { v_1 := v.Args[1] @@ -1841,11 +2114,15 @@ func rewriteValueRISCV64_OpLsh8x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh8x16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1859,6 +2136,20 @@ func rewriteValueRISCV64_OpLsh8x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh8x16 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh8x32(v *Value) bool { v_1 := v.Args[1] @@ -1866,11 +2157,15 @@ func rewriteValueRISCV64_OpLsh8x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh8x32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1884,17 +2179,35 @@ func rewriteValueRISCV64_OpLsh8x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh8x32 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh8x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Lsh8x64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg8 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1906,6 +2219,20 @@ func rewriteValueRISCV64_OpLsh8x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh8x64 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpLsh8x8(v *Value) bool { v_1 := v.Args[1] @@ -1913,11 +2240,15 @@ func rewriteValueRISCV64_OpLsh8x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Lsh8x8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SLL x y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SLL, t) v0.AddArg2(x, y) @@ -1931,6 +2262,20 @@ func rewriteValueRISCV64_OpLsh8x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Lsh8x8 x y) + // cond: shiftIsBounded(v) + // result: (SLL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SLL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpMod16(v *Value) bool { v_1 := v.Args[1] @@ -2629,13 +2974,14 @@ func rewriteValueRISCV64_OpNeqPtr(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + typ := &b.Func.Config.Types // match: (NeqPtr x y) - // result: (SNEZ (SUB x y)) + // result: (SNEZ (SUB x y)) for { x := v_0 y := v_1 v.reset(OpRISCV64SNEZ) - v0 := b.NewValue0(v.Pos, OpRISCV64SUB, x.Type) + v0 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.Uintptr) v0.AddArg2(x, y) v.AddArg(v0) return true @@ -2796,6 +3142,22 @@ func rewriteValueRISCV64_OpRISCV64ADDI(v *Value) bool { v.copyOf(x) return true } + // match: (ADDI [x] (MOVDconst [y])) + // cond: is32Bit(x + y) + // result: (MOVDconst [x + y]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + if !(is32Bit(x + y)) { + break + } + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(x + y) + return true + } return false } func rewriteValueRISCV64_OpRISCV64AND(v *Value) bool { @@ -2823,72 +3185,423 @@ func rewriteValueRISCV64_OpRISCV64AND(v *Value) bool { } return false } -func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool { - v_1 := v.Args[1] +func rewriteValueRISCV64_OpRISCV64ANDI(v *Value) bool { v_0 := v.Args[0] - // match: (MOVBUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) - // result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} base mem) + // match: (ANDI [0] x) + // result: (MOVDconst [0]) for { - off1 := auxIntToInt32(v.AuxInt) - sym1 := auxToSym(v.Aux) - if v_0.Op != OpRISCV64MOVaddr { - break - } - off2 := auxIntToInt32(v_0.AuxInt) - sym2 := auxToSym(v_0.Aux) - base := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if auxIntToInt64(v.AuxInt) != 0 { break } - v.reset(OpRISCV64MOVBUload) - v.AuxInt = int32ToAuxInt(off1 + off2) - v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(base, mem) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVBUload [off1] {sym} (ADDI [off2] base) mem) - // cond: is32Bit(int64(off1)+off2) - // result: (MOVBUload [off1+int32(off2)] {sym} base mem) + // match: (ANDI [-1] x) + // result: x for { - off1 := auxIntToInt32(v.AuxInt) - sym := auxToSym(v.Aux) - if v_0.Op != OpRISCV64ADDI { - break - } - off2 := auxIntToInt64(v_0.AuxInt) - base := v_0.Args[0] - mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if auxIntToInt64(v.AuxInt) != -1 { break } - v.reset(OpRISCV64MOVBUload) - v.AuxInt = int32ToAuxInt(off1 + int32(off2)) - v.Aux = symToAux(sym) - v.AddArg2(base, mem) + x := v_0 + v.copyOf(x) return true } - return false -} -func rewriteValueRISCV64_OpRISCV64MOVBUreg(v *Value) bool { - v_0 := v.Args[0] - b := v.Block - // match: (MOVBUreg (MOVDconst [c])) - // result: (MOVDconst [int64(uint8(c))]) + // match: (ANDI [x] (MOVDconst [y])) + // result: (MOVDconst [x & y]) for { + x := auxIntToInt64(v.AuxInt) if v_0.Op != OpRISCV64MOVDconst { break } - c := auxIntToInt64(v_0.AuxInt) + y := auxIntToInt64(v_0.AuxInt) v.reset(OpRISCV64MOVDconst) - v.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AuxInt = int64ToAuxInt(x & y) return true } - // match: (MOVBUreg x:(MOVBUload _ _)) - // result: (MOVDreg x) + return false +} +func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (FMADDD neg:(FNEGD x) y z) + // cond: neg.Uses == 1 + // result: (FNMADDD x y z) for { - x := v_0 + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + neg := v_0 + if neg.Op != OpRISCV64FNEGD { + continue + } + x := neg.Args[0] + y := v_1 + z := v_2 + if !(neg.Uses == 1) { + continue + } + v.reset(OpRISCV64FNMADDD) + v.AddArg3(x, y, z) + return true + } + break + } + // match: (FMADDD x y neg:(FNEGD z)) + // cond: neg.Uses == 1 + // result: (FMSUBD x y z) + for { + x := v_0 + y := v_1 + neg := v_2 + if neg.Op != OpRISCV64FNEGD { + break + } + z := neg.Args[0] + if !(neg.Uses == 1) { + break + } + v.reset(OpRISCV64FMSUBD) + v.AddArg3(x, y, z) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (FMSUBD neg:(FNEGD x) y z) + // cond: neg.Uses == 1 + // result: (FNMSUBD x y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + neg := v_0 + if neg.Op != OpRISCV64FNEGD { + continue + } + x := neg.Args[0] + y := v_1 + z := v_2 + if !(neg.Uses == 1) { + continue + } + v.reset(OpRISCV64FNMSUBD) + v.AddArg3(x, y, z) + return true + } + break + } + // match: (FMSUBD x y neg:(FNEGD z)) + // cond: neg.Uses == 1 + // result: (FMADDD x y z) + for { + x := v_0 + y := v_1 + neg := v_2 + if neg.Op != OpRISCV64FNEGD { + break + } + z := neg.Args[0] + if !(neg.Uses == 1) { + break + } + v.reset(OpRISCV64FMADDD) + v.AddArg3(x, y, z) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (FNMADDD neg:(FNEGD x) y z) + // cond: neg.Uses == 1 + // result: (FMADDD x y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + neg := v_0 + if neg.Op != OpRISCV64FNEGD { + continue + } + x := neg.Args[0] + y := v_1 + z := v_2 + if !(neg.Uses == 1) { + continue + } + v.reset(OpRISCV64FMADDD) + v.AddArg3(x, y, z) + return true + } + break + } + // match: (FNMADDD x y neg:(FNEGD z)) + // cond: neg.Uses == 1 + // result: (FNMSUBD x y z) + for { + x := v_0 + y := v_1 + neg := v_2 + if neg.Op != OpRISCV64FNEGD { + break + } + z := neg.Args[0] + if !(neg.Uses == 1) { + break + } + v.reset(OpRISCV64FNMSUBD) + v.AddArg3(x, y, z) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (FNMSUBD neg:(FNEGD x) y z) + // cond: neg.Uses == 1 + // result: (FMSUBD x y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + neg := v_0 + if neg.Op != OpRISCV64FNEGD { + continue + } + x := neg.Args[0] + y := v_1 + z := v_2 + if !(neg.Uses == 1) { + continue + } + v.reset(OpRISCV64FMSUBD) + v.AddArg3(x, y, z) + return true + } + break + } + // match: (FNMSUBD x y neg:(FNEGD z)) + // cond: neg.Uses == 1 + // result: (FNMADDD x y z) + for { + x := v_0 + y := v_1 + neg := v_2 + if neg.Op != OpRISCV64FNEGD { + break + } + z := neg.Args[0] + if !(neg.Uses == 1) { + break + } + v.reset(OpRISCV64FNMADDD) + v.AddArg3(x, y, z) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} base mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym1 := auxToSym(v.Aux) + if v_0.Op != OpRISCV64MOVaddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym2 := auxToSym(v_0.Aux) + base := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + break + } + v.reset(OpRISCV64MOVBUload) + v.AuxInt = int32ToAuxInt(off1 + off2) + v.Aux = symToAux(mergeSym(sym1, sym2)) + v.AddArg2(base, mem) + return true + } + // match: (MOVBUload [off1] {sym} (ADDI [off2] base) mem) + // cond: is32Bit(int64(off1)+off2) + // result: (MOVBUload [off1+int32(off2)] {sym} base mem) + for { + off1 := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpRISCV64ADDI { + break + } + off2 := auxIntToInt64(v_0.AuxInt) + base := v_0.Args[0] + mem := v_1 + if !(is32Bit(int64(off1) + off2)) { + break + } + v.reset(OpRISCV64MOVBUload) + v.AuxInt = int32ToAuxInt(off1 + int32(off2)) + v.Aux = symToAux(sym) + v.AddArg2(base, mem) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64MOVBUreg(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + // match: (MOVBUreg x:(FLES _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FLES { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FLTS _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FLTS { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FEQS _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FEQS { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FNES _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FNES { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FLED _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FLED { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FLTD _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FLTD { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FEQD _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FEQD { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(FNED _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64FNED { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(SEQZ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64SEQZ { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(SNEZ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64SNEZ { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(SLT _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64SLT { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(SLTU _ _)) + // result: x + for { + x := v_0 + if x.Op != OpRISCV64SLTU { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(uint8(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(uint8(c)) == c) { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg (MOVDconst [c])) + // result: (MOVDconst [int64(uint8(c))]) + for { + if v_0.Op != OpRISCV64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(uint8(c))) + return true + } + // match: (MOVBUreg x:(MOVBUload _ _)) + // result: (MOVDreg x) + for { + x := v_0 if x.Op != OpRISCV64MOVBUload { break } @@ -2896,6 +3609,51 @@ func rewriteValueRISCV64_OpRISCV64MOVBUreg(v *Value) bool { v.AddArg(x) return true } + // match: (MOVBUreg x:(Select0 (LoweredAtomicLoad8 _ _))) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpSelect0 { + break + } + x_0 := x.Args[0] + if x_0.Op != OpRISCV64LoweredAtomicLoad8 { + break + } + v.reset(OpRISCV64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(Select0 (LoweredAtomicCas32 _ _ _ _))) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpSelect0 { + break + } + x_0 := x.Args[0] + if x_0.Op != OpRISCV64LoweredAtomicCas32 { + break + } + v.reset(OpRISCV64MOVDreg) + v.AddArg(x) + return true + } + // match: (MOVBUreg x:(Select0 (LoweredAtomicCas64 _ _ _ _))) + // result: (MOVDreg x) + for { + x := v_0 + if x.Op != OpSelect0 { + break + } + x_0 := x.Args[0] + if x_0.Op != OpRISCV64LoweredAtomicCas64 { + break + } + v.reset(OpRISCV64MOVDreg) + v.AddArg(x) + return true + } // match: (MOVBUreg x:(MOVBUreg _)) // result: (MOVDreg x) for { @@ -2984,6 +3742,21 @@ func rewriteValueRISCV64_OpRISCV64MOVBload(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVBreg(v *Value) bool { v_0 := v.Args[0] b := v.Block + // match: (MOVBreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(int8(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(int8(c)) == c) { + break + } + v.copyOf(x) + return true + } // match: (MOVBreg (MOVDconst [c])) // result: (MOVDconst [int64(int8(c))]) for { @@ -3505,6 +4278,21 @@ func rewriteValueRISCV64_OpRISCV64MOVHUload(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVHUreg(v *Value) bool { v_0 := v.Args[0] b := v.Block + // match: (MOVHUreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(uint16(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(uint16(c)) == c) { + break + } + v.copyOf(x) + return true + } // match: (MOVHUreg (MOVDconst [c])) // result: (MOVDconst [int64(uint16(c))]) for { @@ -3637,6 +4425,21 @@ func rewriteValueRISCV64_OpRISCV64MOVHload(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVHreg(v *Value) bool { v_0 := v.Args[0] b := v.Block + // match: (MOVHreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(int16(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(int16(c)) == c) { + break + } + v.copyOf(x) + return true + } // match: (MOVHreg (MOVDconst [c])) // result: (MOVDconst [int64(int16(c))]) for { @@ -3974,6 +4777,21 @@ func rewriteValueRISCV64_OpRISCV64MOVWUload(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVWUreg(v *Value) bool { v_0 := v.Args[0] b := v.Block + // match: (MOVWUreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(uint32(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(uint32(c)) == c) { + break + } + v.copyOf(x) + return true + } // match: (MOVWUreg (MOVDconst [c])) // result: (MOVDconst [int64(uint32(c))]) for { @@ -4128,6 +4946,21 @@ func rewriteValueRISCV64_OpRISCV64MOVWload(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVWreg(v *Value) bool { v_0 := v.Args[0] b := v.Block + // match: (MOVWreg x:(ANDI [c] y)) + // cond: c >= 0 && int64(int32(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpRISCV64ANDI { + break + } + c := auxIntToInt64(x.AuxInt) + if !(c >= 0 && int64(int32(c)) == c) { + break + } + v.copyOf(x) + return true + } // match: (MOVWreg (MOVDconst [c])) // result: (MOVDconst [int64(int32(c))]) for { @@ -4413,6 +5246,36 @@ func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool { } return false } +func rewriteValueRISCV64_OpRISCV64NEG(v *Value) bool { + v_0 := v.Args[0] + // match: (NEG (MOVDconst [x])) + // result: (MOVDconst [-x]) + for { + if v_0.Op != OpRISCV64MOVDconst { + break + } + x := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(-x) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64NEGW(v *Value) bool { + v_0 := v.Args[0] + // match: (NEGW (MOVDconst [x])) + // result: (MOVDconst [int64(int32(-x))]) + for { + if v_0.Op != OpRISCV64MOVDconst { + break + } + x := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(int32(-x))) + return true + } + return false +} func rewriteValueRISCV64_OpRISCV64OR(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -4438,6 +5301,79 @@ func rewriteValueRISCV64_OpRISCV64OR(v *Value) bool { } return false } +func rewriteValueRISCV64_OpRISCV64ORI(v *Value) bool { + v_0 := v.Args[0] + // match: (ORI [0] x) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 { + break + } + x := v_0 + v.copyOf(x) + return true + } + // match: (ORI [-1] x) + // result: (MOVDconst [-1]) + for { + if auxIntToInt64(v.AuxInt) != -1 { + break + } + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + // match: (ORI [x] (MOVDconst [y])) + // result: (MOVDconst [x | y]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(x | y) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SEQZ(v *Value) bool { + v_0 := v.Args[0] + // match: (SEQZ (NEG x)) + // result: (SEQZ x) + for { + if v_0.Op != OpRISCV64NEG { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SEQZ) + v.AddArg(x) + return true + } + // match: (SEQZ (SEQZ x)) + // result: (SNEZ x) + for { + if v_0.Op != OpRISCV64SEQZ { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SNEZ) + v.AddArg(x) + return true + } + // match: (SEQZ (SNEZ x)) + // result: (SEQZ x) + for { + if v_0.Op != OpRISCV64SNEZ { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SEQZ) + v.AddArg(x) + return true + } + return false +} func rewriteValueRISCV64_OpRISCV64SLL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -4456,6 +5392,127 @@ func rewriteValueRISCV64_OpRISCV64SLL(v *Value) bool { } return false } +func rewriteValueRISCV64_OpRISCV64SLLI(v *Value) bool { + v_0 := v.Args[0] + // match: (SLLI [x] (MOVDconst [y])) + // cond: is32Bit(y << uint32(x)) + // result: (MOVDconst [y << uint32(x)]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + if !(is32Bit(y << uint32(x))) { + break + } + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(y << uint32(x)) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SLT(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SLT x x) + // result: (MOVDconst [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SLTI(v *Value) bool { + v_0 := v.Args[0] + // match: (SLTI [x] (MOVDconst [y])) + // result: (MOVDconst [b2i(int64(y) < int64(x))]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(b2i(int64(y) < int64(x))) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SLTIU(v *Value) bool { + v_0 := v.Args[0] + // match: (SLTIU [x] (MOVDconst [y])) + // result: (MOVDconst [b2i(uint64(y) < uint64(x))]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(b2i(uint64(y) < uint64(x))) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SLTU(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SLTU x x) + // result: (MOVDconst [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} +func rewriteValueRISCV64_OpRISCV64SNEZ(v *Value) bool { + v_0 := v.Args[0] + // match: (SNEZ (NEG x)) + // result: (SNEZ x) + for { + if v_0.Op != OpRISCV64NEG { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SNEZ) + v.AddArg(x) + return true + } + // match: (SNEZ (SEQZ x)) + // result: (SEQZ x) + for { + if v_0.Op != OpRISCV64SEQZ { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SEQZ) + v.AddArg(x) + return true + } + // match: (SNEZ (SNEZ x)) + // result: (SNEZ x) + for { + if v_0.Op != OpRISCV64SNEZ { + break + } + x := v_0.Args[0] + v.reset(OpRISCV64SNEZ) + v.AddArg(x) + return true + } + return false +} func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -4474,6 +5531,22 @@ func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool { } return false } +func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool { + v_0 := v.Args[0] + // match: (SRAI [x] (MOVDconst [y])) + // result: (MOVDconst [int64(y) >> uint32(x)]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(y) >> uint32(x)) + return true + } + return false +} func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -4492,9 +5565,26 @@ func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool { } return false } +func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool { + v_0 := v.Args[0] + // match: (SRLI [x] (MOVDconst [y])) + // result: (MOVDconst [int64(uint64(y) >> uint32(x))]) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVDconst { + break + } + y := auxIntToInt64(v_0.AuxInt) + v.reset(OpRISCV64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(uint64(y) >> uint32(x))) + return true + } + return false +} func rewriteValueRISCV64_OpRISCV64SUB(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (SUB x (MOVDconst [val])) // cond: is32Bit(-val) // result: (ADDI [-val] x) @@ -4512,6 +5602,26 @@ func rewriteValueRISCV64_OpRISCV64SUB(v *Value) bool { v.AddArg(x) return true } + // match: (SUB (MOVDconst [val]) y) + // cond: is32Bit(-val) + // result: (NEG (ADDI [-val] y)) + for { + t := v.Type + if v_0.Op != OpRISCV64MOVDconst { + break + } + val := auxIntToInt64(v_0.AuxInt) + y := v_1 + if !(is32Bit(-val)) { + break + } + v.reset(OpRISCV64NEG) + v0 := b.NewValue0(v.Pos, OpRISCV64ADDI, t) + v0.AuxInt = int64ToAuxInt(-val) + v0.AddArg(y) + v.AddArg(v0) + return true + } // match: (SUB x (MOVDconst [0])) // result: x for { @@ -4706,11 +5816,15 @@ func rewriteValueRISCV64_OpRsh16Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) @@ -4726,6 +5840,22 @@ func rewriteValueRISCV64_OpRsh16Ux16(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh16Ux16 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16Ux32(v *Value) bool { v_1 := v.Args[1] @@ -4733,11 +5863,15 @@ func rewriteValueRISCV64_OpRsh16Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) @@ -4753,6 +5887,22 @@ func rewriteValueRISCV64_OpRsh16Ux32(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh16Ux32 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16Ux64(v *Value) bool { v_1 := v.Args[1] @@ -4760,11 +5910,15 @@ func rewriteValueRISCV64_OpRsh16Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) @@ -4778,6 +5932,22 @@ func rewriteValueRISCV64_OpRsh16Ux64(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh16Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16Ux8(v *Value) bool { v_1 := v.Args[1] @@ -4785,11 +5955,15 @@ func rewriteValueRISCV64_OpRsh16Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16Ux8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) @@ -4805,6 +5979,22 @@ func rewriteValueRISCV64_OpRsh16Ux8(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh16Ux8 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16x16(v *Value) bool { v_1 := v.Args[1] @@ -4812,11 +6002,15 @@ func rewriteValueRISCV64_OpRsh16x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16x16 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) @@ -4834,6 +6028,22 @@ func rewriteValueRISCV64_OpRsh16x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh16x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16x32(v *Value) bool { v_1 := v.Args[1] @@ -4841,11 +6051,15 @@ func rewriteValueRISCV64_OpRsh16x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16x32 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) @@ -4863,6 +6077,22 @@ func rewriteValueRISCV64_OpRsh16x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh16x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16x64(v *Value) bool { v_1 := v.Args[1] @@ -4870,11 +6100,15 @@ func rewriteValueRISCV64_OpRsh16x64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) @@ -4890,6 +6124,22 @@ func rewriteValueRISCV64_OpRsh16x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh16x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh16x8(v *Value) bool { v_1 := v.Args[1] @@ -4897,11 +6147,15 @@ func rewriteValueRISCV64_OpRsh16x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh16x8 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) @@ -4919,6 +6173,22 @@ func rewriteValueRISCV64_OpRsh16x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh16x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt16to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt16to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32Ux16(v *Value) bool { v_1 := v.Args[1] @@ -4926,11 +6196,15 @@ func rewriteValueRISCV64_OpRsh32Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) @@ -4946,6 +6220,22 @@ func rewriteValueRISCV64_OpRsh32Ux16(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh32Ux16 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32Ux32(v *Value) bool { v_1 := v.Args[1] @@ -4953,11 +6243,15 @@ func rewriteValueRISCV64_OpRsh32Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) @@ -4973,6 +6267,22 @@ func rewriteValueRISCV64_OpRsh32Ux32(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh32Ux32 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32Ux64(v *Value) bool { v_1 := v.Args[1] @@ -4980,11 +6290,15 @@ func rewriteValueRISCV64_OpRsh32Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) @@ -4998,6 +6312,22 @@ func rewriteValueRISCV64_OpRsh32Ux64(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh32Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32Ux8(v *Value) bool { v_1 := v.Args[1] @@ -5005,11 +6335,15 @@ func rewriteValueRISCV64_OpRsh32Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32Ux8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) @@ -5025,6 +6359,22 @@ func rewriteValueRISCV64_OpRsh32Ux8(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh32Ux8 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32x16(v *Value) bool { v_1 := v.Args[1] @@ -5032,11 +6382,15 @@ func rewriteValueRISCV64_OpRsh32x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32x16 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) @@ -5054,6 +6408,22 @@ func rewriteValueRISCV64_OpRsh32x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh32x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32x32(v *Value) bool { v_1 := v.Args[1] @@ -5061,11 +6431,15 @@ func rewriteValueRISCV64_OpRsh32x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32x32 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) @@ -5083,6 +6457,22 @@ func rewriteValueRISCV64_OpRsh32x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh32x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32x64(v *Value) bool { v_1 := v.Args[1] @@ -5090,11 +6480,15 @@ func rewriteValueRISCV64_OpRsh32x64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) @@ -5110,6 +6504,22 @@ func rewriteValueRISCV64_OpRsh32x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh32x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh32x8(v *Value) bool { v_1 := v.Args[1] @@ -5117,11 +6527,15 @@ func rewriteValueRISCV64_OpRsh32x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh32x8 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) @@ -5139,6 +6553,22 @@ func rewriteValueRISCV64_OpRsh32x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh32x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt32to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64Ux16(v *Value) bool { v_1 := v.Args[1] @@ -5146,11 +6576,15 @@ func rewriteValueRISCV64_OpRsh64Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v0.AddArg2(x, y) @@ -5164,6 +6598,20 @@ func rewriteValueRISCV64_OpRsh64Ux16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh64Ux16 x y) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64Ux32(v *Value) bool { v_1 := v.Args[1] @@ -5171,11 +6619,15 @@ func rewriteValueRISCV64_OpRsh64Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v0.AddArg2(x, y) @@ -5189,17 +6641,35 @@ func rewriteValueRISCV64_OpRsh64Ux32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh64Ux32 x y) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Rsh64Ux64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL x y) (Neg64 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v0.AddArg2(x, y) @@ -5211,6 +6681,20 @@ func rewriteValueRISCV64_OpRsh64Ux64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh64Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64Ux8(v *Value) bool { v_1 := v.Args[1] @@ -5218,11 +6702,15 @@ func rewriteValueRISCV64_OpRsh64Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64Ux8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v0.AddArg2(x, y) @@ -5236,6 +6724,20 @@ func rewriteValueRISCV64_OpRsh64Ux8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh64Ux8 x y) + // cond: shiftIsBounded(v) + // result: (SRL x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64x16(v *Value) bool { v_1 := v.Args[1] @@ -5243,11 +6745,15 @@ func rewriteValueRISCV64_OpRsh64x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64x16 x y) + // cond: !shiftIsBounded(v) // result: (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) @@ -5263,6 +6769,20 @@ func rewriteValueRISCV64_OpRsh64x16(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Rsh64x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64x32(v *Value) bool { v_1 := v.Args[1] @@ -5270,11 +6790,15 @@ func rewriteValueRISCV64_OpRsh64x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64x32 x y) + // cond: !shiftIsBounded(v) // result: (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) @@ -5290,17 +6814,35 @@ func rewriteValueRISCV64_OpRsh64x32(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Rsh64x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block // match: (Rsh64x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA x (OR y (ADDI [-1] (SLTIU [64] y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) @@ -5314,6 +6856,20 @@ func rewriteValueRISCV64_OpRsh64x64(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Rsh64x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh64x8(v *Value) bool { v_1 := v.Args[1] @@ -5321,11 +6877,15 @@ func rewriteValueRISCV64_OpRsh64x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh64x8 x y) + // cond: !shiftIsBounded(v) // result: (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) @@ -5341,6 +6901,20 @@ func rewriteValueRISCV64_OpRsh64x8(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Rsh64x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA x y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8Ux16(v *Value) bool { v_1 := v.Args[1] @@ -5348,11 +6922,15 @@ func rewriteValueRISCV64_OpRsh8Ux16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux16 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) @@ -5368,6 +6946,22 @@ func rewriteValueRISCV64_OpRsh8Ux16(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh8Ux16 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8Ux32(v *Value) bool { v_1 := v.Args[1] @@ -5375,11 +6969,15 @@ func rewriteValueRISCV64_OpRsh8Ux32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux32 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) @@ -5395,6 +6993,22 @@ func rewriteValueRISCV64_OpRsh8Ux32(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh8Ux32 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8Ux64(v *Value) bool { v_1 := v.Args[1] @@ -5402,11 +7016,15 @@ func rewriteValueRISCV64_OpRsh8Ux64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux64 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] y))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) @@ -5420,6 +7038,22 @@ func rewriteValueRISCV64_OpRsh8Ux64(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh8Ux64 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8Ux8(v *Value) bool { v_1 := v.Args[1] @@ -5427,11 +7061,15 @@ func rewriteValueRISCV64_OpRsh8Ux8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8Ux8 x y) + // cond: !shiftIsBounded(v) // result: (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64AND) v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) v1 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) @@ -5447,6 +7085,22 @@ func rewriteValueRISCV64_OpRsh8Ux8(v *Value) bool { v.AddArg2(v0, v2) return true } + // match: (Rsh8Ux8 x y) + // cond: shiftIsBounded(v) + // result: (SRL (ZeroExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRL) + v0 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8x16(v *Value) bool { v_1 := v.Args[1] @@ -5454,11 +7108,15 @@ func rewriteValueRISCV64_OpRsh8x16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8x16 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) @@ -5476,6 +7134,22 @@ func rewriteValueRISCV64_OpRsh8x16(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh8x16 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8x32(v *Value) bool { v_1 := v.Args[1] @@ -5483,11 +7157,15 @@ func rewriteValueRISCV64_OpRsh8x32(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8x32 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) @@ -5505,6 +7183,22 @@ func rewriteValueRISCV64_OpRsh8x32(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh8x32 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8x64(v *Value) bool { v_1 := v.Args[1] @@ -5512,11 +7206,15 @@ func rewriteValueRISCV64_OpRsh8x64(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8x64 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) @@ -5532,6 +7230,22 @@ func rewriteValueRISCV64_OpRsh8x64(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh8x64 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false } func rewriteValueRISCV64_OpRsh8x8(v *Value) bool { v_1 := v.Args[1] @@ -5539,11 +7253,15 @@ func rewriteValueRISCV64_OpRsh8x8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Rsh8x8 x y) + // cond: !shiftIsBounded(v) // result: (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) for { t := v.Type x := v_0 y := v_1 + if !(!shiftIsBounded(v)) { + break + } v.reset(OpRISCV64SRA) v.Type = t v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) @@ -5561,22 +7279,153 @@ func rewriteValueRISCV64_OpRsh8x8(v *Value) bool { v.AddArg2(v0, v1) return true } + // match: (Rsh8x8 x y) + // cond: shiftIsBounded(v) + // result: (SRA (SignExt8to64 x) y) + for { + x := v_0 + y := v_1 + if !(shiftIsBounded(v)) { + break + } + v.reset(OpRISCV64SRA) + v0 := b.NewValue0(v.Pos, OpSignExt8to64, typ.Int64) + v0.AddArg(x) + v.AddArg2(v0, y) + return true + } + return false +} +func rewriteValueRISCV64_OpSelect0(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select0 (Add64carry x y c)) + // result: (ADD (ADD x y) c) + for { + if v_0.Op != OpAdd64carry { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpRISCV64ADD) + v0 := b.NewValue0(v.Pos, OpRISCV64ADD, typ.UInt64) + v0.AddArg2(x, y) + v.AddArg2(v0, c) + return true + } + // match: (Select0 (Sub64borrow x y c)) + // result: (SUB (SUB x y) c) + for { + if v_0.Op != OpSub64borrow { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpRISCV64SUB) + v0 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.UInt64) + v0.AddArg2(x, y) + v.AddArg2(v0, c) + return true + } + // match: (Select0 m:(LoweredMuluhilo x y)) + // cond: m.Uses == 1 + // result: (MULHU x y) + for { + m := v_0 + if m.Op != OpRISCV64LoweredMuluhilo { + break + } + y := m.Args[1] + x := m.Args[0] + if !(m.Uses == 1) { + break + } + v.reset(OpRISCV64MULHU) + v.AddArg2(x, y) + return true + } + return false +} +func rewriteValueRISCV64_OpSelect1(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Select1 (Add64carry x y c)) + // result: (OR (SLTU s:(ADD x y) x) (SLTU (ADD s c) s)) + for { + if v_0.Op != OpAdd64carry { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpRISCV64OR) + v0 := b.NewValue0(v.Pos, OpRISCV64SLTU, typ.UInt64) + s := b.NewValue0(v.Pos, OpRISCV64ADD, typ.UInt64) + s.AddArg2(x, y) + v0.AddArg2(s, x) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTU, typ.UInt64) + v3 := b.NewValue0(v.Pos, OpRISCV64ADD, typ.UInt64) + v3.AddArg2(s, c) + v2.AddArg2(v3, s) + v.AddArg2(v0, v2) + return true + } + // match: (Select1 (Sub64borrow x y c)) + // result: (OR (SLTU x s:(SUB x y)) (SLTU s (SUB s c))) + for { + if v_0.Op != OpSub64borrow { + break + } + c := v_0.Args[2] + x := v_0.Args[0] + y := v_0.Args[1] + v.reset(OpRISCV64OR) + v0 := b.NewValue0(v.Pos, OpRISCV64SLTU, typ.UInt64) + s := b.NewValue0(v.Pos, OpRISCV64SUB, typ.UInt64) + s.AddArg2(x, y) + v0.AddArg2(x, s) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTU, typ.UInt64) + v3 := b.NewValue0(v.Pos, OpRISCV64SUB, typ.UInt64) + v3.AddArg2(s, c) + v2.AddArg2(s, v3) + v.AddArg2(v0, v2) + return true + } + // match: (Select1 m:(LoweredMuluhilo x y)) + // cond: m.Uses == 1 + // result: (MUL x y) + for { + m := v_0 + if m.Op != OpRISCV64LoweredMuluhilo { + break + } + y := m.Args[1] + x := m.Args[0] + if !(m.Uses == 1) { + break + } + v.reset(OpRISCV64MUL) + v.AddArg2(x, y) + return true + } + return false } func rewriteValueRISCV64_OpSlicemask(v *Value) bool { v_0 := v.Args[0] b := v.Block // match: (Slicemask x) - // result: (NOT (SRAI [63] (ADDI [-1] x))) + // result: (SRAI [63] (NEG x)) for { t := v.Type x := v_0 - v.reset(OpRISCV64NOT) - v0 := b.NewValue0(v.Pos, OpRISCV64SRAI, t) - v0.AuxInt = int64ToAuxInt(63) - v1 := b.NewValue0(v.Pos, OpRISCV64ADDI, t) - v1.AuxInt = int64ToAuxInt(-1) - v1.AddArg(x) - v0.AddArg(v1) + v.reset(OpRISCV64SRAI) + v.AuxInt = int64ToAuxInt(63) + v0 := b.NewValue0(v.Pos, OpRISCV64NEG, t) + v0.AddArg(x) v.AddArg(v0) return true } @@ -6055,6 +7904,7 @@ func rewriteValueRISCV64_OpZero(v *Value) bool { } } func rewriteBlockRISCV64(b *Block) bool { + typ := &b.Func.Config.Types switch b.Kind { case BlockRISCV64BEQ: // match: (BEQ (MOVDconst [0]) cond yes no) @@ -6096,6 +7946,48 @@ func rewriteBlockRISCV64(b *Block) bool { b.resetWithControl(BlockRISCV64BEQZ, x) return true } + // match: (BEQZ (NEG x) yes no) + // result: (BEQZ x yes no) + for b.Controls[0].Op == OpRISCV64NEG { + v_0 := b.Controls[0] + x := v_0.Args[0] + b.resetWithControl(BlockRISCV64BEQZ, x) + return true + } + // match: (BEQZ (FNES x y) yes no) + // result: (BNEZ (FEQS x y) yes no) + for b.Controls[0].Op == OpRISCV64FNES { + v_0 := b.Controls[0] + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpRISCV64FEQS, t) + v0.AddArg2(x, y) + b.resetWithControl(BlockRISCV64BNEZ, v0) + return true + } + } + // match: (BEQZ (FNED x y) yes no) + // result: (BNEZ (FEQD x y) yes no) + for b.Controls[0].Op == OpRISCV64FNED { + v_0 := b.Controls[0] + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpRISCV64FEQD, t) + v0.AddArg2(x, y) + b.resetWithControl(BlockRISCV64BNEZ, v0) + return true + } + } // match: (BEQZ (SUB x y) yes no) // result: (BEQ x y yes no) for b.Controls[0].Op == OpRISCV64SUB { @@ -6123,6 +8015,52 @@ func rewriteBlockRISCV64(b *Block) bool { b.resetWithControl2(BlockRISCV64BGEU, x, y) return true } + case BlockRISCV64BGE: + // match: (BGE (MOVDconst [0]) cond yes no) + // result: (BLEZ cond yes no) + for b.Controls[0].Op == OpRISCV64MOVDconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + cond := b.Controls[1] + b.resetWithControl(BlockRISCV64BLEZ, cond) + return true + } + // match: (BGE cond (MOVDconst [0]) yes no) + // result: (BGEZ cond yes no) + for b.Controls[1].Op == OpRISCV64MOVDconst { + cond := b.Controls[0] + v_1 := b.Controls[1] + if auxIntToInt64(v_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockRISCV64BGEZ, cond) + return true + } + case BlockRISCV64BLT: + // match: (BLT (MOVDconst [0]) cond yes no) + // result: (BGTZ cond yes no) + for b.Controls[0].Op == OpRISCV64MOVDconst { + v_0 := b.Controls[0] + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + cond := b.Controls[1] + b.resetWithControl(BlockRISCV64BGTZ, cond) + return true + } + // match: (BLT cond (MOVDconst [0]) yes no) + // result: (BLTZ cond yes no) + for b.Controls[1].Op == OpRISCV64MOVDconst { + cond := b.Controls[0] + v_1 := b.Controls[1] + if auxIntToInt64(v_1.AuxInt) != 0 { + break + } + b.resetWithControl(BlockRISCV64BLTZ, cond) + return true + } case BlockRISCV64BNE: // match: (BNE (MOVDconst [0]) cond yes no) // result: (BNEZ cond yes no) @@ -6163,6 +8101,48 @@ func rewriteBlockRISCV64(b *Block) bool { b.resetWithControl(BlockRISCV64BNEZ, x) return true } + // match: (BNEZ (NEG x) yes no) + // result: (BNEZ x yes no) + for b.Controls[0].Op == OpRISCV64NEG { + v_0 := b.Controls[0] + x := v_0.Args[0] + b.resetWithControl(BlockRISCV64BNEZ, x) + return true + } + // match: (BNEZ (FNES x y) yes no) + // result: (BEQZ (FEQS x y) yes no) + for b.Controls[0].Op == OpRISCV64FNES { + v_0 := b.Controls[0] + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpRISCV64FEQS, t) + v0.AddArg2(x, y) + b.resetWithControl(BlockRISCV64BEQZ, v0) + return true + } + } + // match: (BNEZ (FNED x y) yes no) + // result: (BEQZ (FEQD x y) yes no) + for b.Controls[0].Op == OpRISCV64FNED { + v_0 := b.Controls[0] + t := v_0.Type + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + v0 := b.NewValue0(v_0.Pos, OpRISCV64FEQD, t) + v0.AddArg2(x, y) + b.resetWithControl(BlockRISCV64BEQZ, v0) + return true + } + } // match: (BNEZ (SUB x y) yes no) // result: (BNE x y yes no) for b.Controls[0].Op == OpRISCV64SUB { @@ -6192,10 +8172,12 @@ func rewriteBlockRISCV64(b *Block) bool { } case BlockIf: // match: (If cond yes no) - // result: (BNEZ cond yes no) + // result: (BNEZ (MOVBUreg cond) yes no) for { cond := b.Controls[0] - b.resetWithControl(BlockRISCV64BNEZ, cond) + v0 := b.NewValue0(cond.Pos, OpRISCV64MOVBUreg, typ.UInt64) + v0.AddArg(cond) + b.resetWithControl(BlockRISCV64BNEZ, v0) return true } } diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go index 8b41d62c315bd2..08bbd437596653 100644 --- a/src/cmd/compile/internal/ssa/rewriteS390X.go +++ b/src/cmd/compile/internal/ssa/rewriteS390X.go @@ -819,6 +819,9 @@ func rewriteValueS390X(v *Value) bool { case OpSubPtr: v.Op = OpS390XSUB return true + case OpTailCall: + v.Op = OpS390XCALLtail + return true case OpTrunc: return rewriteValueS390X_OpTrunc(v) case OpTrunc16to8: @@ -5277,25 +5280,6 @@ func rewriteValueS390X_OpS390XADD(v *Value) bool { } break } - // match: (ADD (SLDconst x [c]) (SRDconst x [64-c])) - // result: (RISBGZ x {s390x.NewRotateParams(0, 63, c)}) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLDconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRDconst || auxIntToUint8(v_1.AuxInt) != 64-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRISBGZ) - v.Aux = s390xRotateParamsToAux(s390x.NewRotateParams(0, 63, c)) - v.AddArg(x) - return true - } - break - } // match: (ADD idx (MOVDaddr [c] {s} ptr)) // cond: ptr.Op != OpSB // result: (MOVDaddridx [c] {s} ptr idx) @@ -5470,25 +5454,6 @@ func rewriteValueS390X_OpS390XADDW(v *Value) bool { } break } - // match: (ADDW (SLWconst x [c]) (SRWconst x [32-c])) - // result: (RLLconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLWconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRWconst || auxIntToUint8(v_1.AuxInt) != 32-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRLLconst) - v.AuxInt = uint8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (ADDW x (NEGW y)) // result: (SUBW x y) for { @@ -11686,25 +11651,6 @@ func rewriteValueS390X_OpS390XOR(v *Value) bool { } break } - // match: (OR (SLDconst x [c]) (SRDconst x [64-c])) - // result: (RISBGZ x {s390x.NewRotateParams(0, 63, c)}) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLDconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRDconst || auxIntToUint8(v_1.AuxInt) != 64-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRISBGZ) - v.Aux = s390xRotateParamsToAux(s390x.NewRotateParams(0, 63, c)) - v.AddArg(x) - return true - } - break - } // match: (OR (MOVDconst [-1<<63]) (LGDR x)) // result: (LGDR (LNDFR x)) for { @@ -12384,25 +12330,6 @@ func rewriteValueS390X_OpS390XORW(v *Value) bool { } break } - // match: (ORW (SLWconst x [c]) (SRWconst x [32-c])) - // result: (RLLconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLWconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRWconst || auxIntToUint8(v_1.AuxInt) != 32-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRLLconst) - v.AuxInt = uint8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (ORW x x) // result: x for { @@ -14969,25 +14896,6 @@ func rewriteValueS390X_OpS390XXOR(v *Value) bool { } break } - // match: (XOR (SLDconst x [c]) (SRDconst x [64-c])) - // result: (RISBGZ x {s390x.NewRotateParams(0, 63, c)}) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLDconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRDconst || auxIntToUint8(v_1.AuxInt) != 64-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRISBGZ) - v.Aux = s390xRotateParamsToAux(s390x.NewRotateParams(0, 63, c)) - v.AddArg(x) - return true - } - break - } // match: (XOR (MOVDconst [c]) (MOVDconst [d])) // result: (MOVDconst [c^d]) for { @@ -15065,25 +14973,6 @@ func rewriteValueS390X_OpS390XXORW(v *Value) bool { } break } - // match: (XORW (SLWconst x [c]) (SRWconst x [32-c])) - // result: (RLLconst x [c]) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpS390XSLWconst { - continue - } - c := auxIntToUint8(v_0.AuxInt) - x := v_0.Args[0] - if v_1.Op != OpS390XSRWconst || auxIntToUint8(v_1.AuxInt) != 32-c || x != v_1.Args[0] { - continue - } - v.reset(OpS390XRLLconst) - v.AuxInt = uint8ToAuxInt(c) - v.AddArg(x) - return true - } - break - } // match: (XORW x x) // result: (MOVDconst [0]) for { diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go index 5dab09f85b3578..defd40ddd195e6 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -556,6 +556,9 @@ func rewriteValueWasm(v *Value) bool { case OpSubPtr: v.Op = OpWasmI64Sub return true + case OpTailCall: + v.Op = OpWasmLoweredTailCall + return true case OpTrunc: v.Op = OpWasmF64Trunc return true diff --git a/src/cmd/compile/internal/ssa/rewritedec64.go b/src/cmd/compile/internal/ssa/rewritedec64.go index 7d9656a4c827a1..848b0aa1e4c446 100644 --- a/src/cmd/compile/internal/ssa/rewritedec64.go +++ b/src/cmd/compile/internal/ssa/rewritedec64.go @@ -66,6 +66,14 @@ func rewriteValuedec64(v *Value) bool { return rewriteValuedec64_OpOr32(v) case OpOr64: return rewriteValuedec64_OpOr64(v) + case OpRotateLeft16: + return rewriteValuedec64_OpRotateLeft16(v) + case OpRotateLeft32: + return rewriteValuedec64_OpRotateLeft32(v) + case OpRotateLeft64: + return rewriteValuedec64_OpRotateLeft64(v) + case OpRotateLeft8: + return rewriteValuedec64_OpRotateLeft8(v) case OpRsh16Ux64: return rewriteValuedec64_OpRsh16Ux64(v) case OpRsh16x64: @@ -1266,6 +1274,74 @@ func rewriteValuedec64_OpOr64(v *Value) bool { return true } } +func rewriteValuedec64_OpRotateLeft16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RotateLeft16 x (Int64Make hi lo)) + // result: (RotateLeft16 x lo) + for { + x := v_0 + if v_1.Op != OpInt64Make { + break + } + lo := v_1.Args[1] + v.reset(OpRotateLeft16) + v.AddArg2(x, lo) + return true + } + return false +} +func rewriteValuedec64_OpRotateLeft32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RotateLeft32 x (Int64Make hi lo)) + // result: (RotateLeft32 x lo) + for { + x := v_0 + if v_1.Op != OpInt64Make { + break + } + lo := v_1.Args[1] + v.reset(OpRotateLeft32) + v.AddArg2(x, lo) + return true + } + return false +} +func rewriteValuedec64_OpRotateLeft64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RotateLeft64 x (Int64Make hi lo)) + // result: (RotateLeft64 x lo) + for { + x := v_0 + if v_1.Op != OpInt64Make { + break + } + lo := v_1.Args[1] + v.reset(OpRotateLeft64) + v.AddArg2(x, lo) + return true + } + return false +} +func rewriteValuedec64_OpRotateLeft8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RotateLeft8 x (Int64Make hi lo)) + // result: (RotateLeft8 x lo) + for { + x := v_0 + if v_1.Op != OpInt64Make { + break + } + lo := v_1.Args[1] + v.reset(OpRotateLeft8) + v.AddArg2(x, lo) + return true + } + return false +} func rewriteValuedec64_OpRsh16Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 52258201ca1051..080a0b7030435d 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -34,6 +34,8 @@ func rewriteValuegeneric(v *Value) bool { return rewriteValuegeneric_OpAndB(v) case OpArraySelect: return rewriteValuegeneric_OpArraySelect(v) + case OpCeil: + return rewriteValuegeneric_OpCeil(v) case OpCom16: return rewriteValuegeneric_OpCom16(v) case OpCom32: @@ -120,6 +122,8 @@ func rewriteValuegeneric(v *Value) bool { return rewriteValuegeneric_OpEqPtr(v) case OpEqSlice: return rewriteValuegeneric_OpEqSlice(v) + case OpFloor: + return rewriteValuegeneric_OpFloor(v) case OpIMake: return rewriteValuegeneric_OpIMake(v) case OpInterLECall: @@ -298,6 +302,8 @@ func rewriteValuegeneric(v *Value) bool { return rewriteValuegeneric_OpRound32F(v) case OpRound64F: return rewriteValuegeneric_OpRound64F(v) + case OpRoundToEven: + return rewriteValuegeneric_OpRoundToEven(v) case OpRsh16Ux16: return rewriteValuegeneric_OpRsh16Ux16(v) case OpRsh16Ux32: @@ -412,6 +418,8 @@ func rewriteValuegeneric(v *Value) bool { return rewriteValuegeneric_OpSub64F(v) case OpSub8: return rewriteValuegeneric_OpSub8(v) + case OpTrunc: + return rewriteValuegeneric_OpTrunc(v) case OpTrunc16to8: return rewriteValuegeneric_OpTrunc16to8(v) case OpTrunc32to16: @@ -453,6 +461,7 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Add16 (Const16 [c]) (Const16 [d])) // result: (Const16 [c+d]) for { @@ -519,6 +528,38 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool { } break } + // match: (Add16 x (Neg16 y)) + // result: (Sub16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpNeg16 { + continue + } + y := v_1.Args[0] + v.reset(OpSub16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add16 (Com16 x) x) + // result: (Const16 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom16 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(-1) + return true + } + break + } // match: (Add16 (Const16 [1]) (Com16 x)) // result: (Neg16 x) for { @@ -533,6 +574,52 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool { } break } + // match: (Add16 x (Sub16 y x)) + // result: y + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpSub16 { + continue + } + _ = v_1.Args[1] + y := v_1.Args[0] + if x != v_1.Args[1] { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Add16 x (Add16 y (Sub16 z x))) + // result: (Add16 y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAdd16 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpSub16 { + continue + } + _ = v_1_1.Args[1] + z := v_1_1.Args[0] + if x != v_1_1.Args[1] { + continue + } + v.reset(OpAdd16) + v.AddArg2(y, z) + return true + } + } + break + } // match: (Add16 (Add16 i:(Const16 ) z) x) // cond: (z.Op != OpConst16 && x.Op != OpConst16) // result: (Add16 i (Add16 z x)) @@ -646,12 +733,321 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool { } break } + // match: (Add16 (Lsh16x64 x z:(Const64 [c])) (Rsh16Ux64 x (Const64 [d]))) + // cond: c < 16 && d == 16-c && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh16x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh16Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 16 && d == 16-c && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add16 left:(Lsh16x64 x y) right:(Rsh16Ux64 x (Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add16 left:(Lsh16x32 x y) right:(Rsh16Ux32 x (Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add16 left:(Lsh16x16 x y) right:(Rsh16Ux16 x (Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add16 left:(Lsh16x8 x y) right:(Rsh16Ux8 x (Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add16 right:(Rsh16Ux64 x y) left:(Lsh16x64 x z:(Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add16 right:(Rsh16Ux32 x y) left:(Lsh16x32 x z:(Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add16 right:(Rsh16Ux16 x y) left:(Lsh16x16 x z:(Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add16 right:(Rsh16Ux8 x y) left:(Lsh16x8 x z:(Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpAdd32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Add32 (Const32 [c]) (Const32 [d])) // result: (Const32 [c+d]) for { @@ -718,6 +1114,38 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool { } break } + // match: (Add32 x (Neg32 y)) + // result: (Sub32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpNeg32 { + continue + } + y := v_1.Args[0] + v.reset(OpSub32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add32 (Com32 x) x) + // result: (Const32 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom32 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(-1) + return true + } + break + } // match: (Add32 (Const32 [1]) (Com32 x)) // result: (Neg32 x) for { @@ -732,9 +1160,55 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool { } break } - // match: (Add32 (Add32 i:(Const32 ) z) x) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Add32 i (Add32 z x)) + // match: (Add32 x (Sub32 y x)) + // result: y + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpSub32 { + continue + } + _ = v_1.Args[1] + y := v_1.Args[0] + if x != v_1.Args[1] { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Add32 x (Add32 y (Sub32 z x))) + // result: (Add32 y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAdd32 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpSub32 { + continue + } + _ = v_1_1.Args[1] + z := v_1_1.Args[0] + if x != v_1_1.Args[1] { + continue + } + v.reset(OpAdd32) + v.AddArg2(y, z) + return true + } + } + break + } + // match: (Add32 (Add32 i:(Const32 ) z) x) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Add32 i (Add32 z x)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { if v_0.Op != OpAdd32 { @@ -845,6 +1319,314 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool { } break } + // match: (Add32 (Lsh32x64 x z:(Const64 [c])) (Rsh32Ux64 x (Const64 [d]))) + // cond: c < 32 && d == 32-c && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh32x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh32Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 32 && d == 32-c && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add32 left:(Lsh32x64 x y) right:(Rsh32Ux64 x (Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add32 left:(Lsh32x32 x y) right:(Rsh32Ux32 x (Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add32 left:(Lsh32x16 x y) right:(Rsh32Ux16 x (Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add32 left:(Lsh32x8 x y) right:(Rsh32Ux8 x (Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add32 right:(Rsh32Ux64 x y) left:(Lsh32x64 x z:(Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add32 right:(Rsh32Ux32 x y) left:(Lsh32x32 x z:(Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add32 right:(Rsh32Ux16 x y) left:(Lsh32x16 x z:(Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add32 right:(Rsh32Ux8 x y) left:(Lsh32x8 x z:(Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpAdd32F(v *Value) bool { @@ -878,6 +1660,7 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Add64 (Const64 [c]) (Const64 [d])) // result: (Const64 [c+d]) for { @@ -944,67 +1727,145 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool { } break } - // match: (Add64 (Const64 [1]) (Com64 x)) - // result: (Neg64 x) + // match: (Add64 x (Neg64 y)) + // result: (Sub64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 1 || v_1.Op != OpCom64 { + x := v_0 + if v_1.Op != OpNeg64 { continue } - x := v_1.Args[0] - v.reset(OpNeg64) - v.AddArg(x) + y := v_1.Args[0] + v.reset(OpSub64) + v.AddArg2(x, y) return true } break } - // match: (Add64 (Add64 i:(Const64 ) z) x) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Add64 i (Add64 z x)) + // match: (Add64 (Com64 x) x) + // result: (Const64 [-1]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpAdd64 { + if v_0.Op != OpCom64 { continue } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { - i := v_0_0 - if i.Op != OpConst64 { - continue - } - t := i.Type - z := v_0_1 - x := v_1 - if !(z.Op != OpConst64 && x.Op != OpConst64) { - continue - } - v.reset(OpAdd64) - v0 := b.NewValue0(v.Pos, OpAdd64, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + x := v_0.Args[0] + if x != v_1 { + continue } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(-1) + return true } break } - // match: (Add64 (Sub64 i:(Const64 ) z) x) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Add64 i (Sub64 x z)) + // match: (Add64 (Const64 [1]) (Com64 x)) + // result: (Neg64 x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpSub64 { - continue - } - z := v_0.Args[1] - i := v_0.Args[0] - if i.Op != OpConst64 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 1 || v_1.Op != OpCom64 { continue } - t := i.Type - x := v_1 - if !(z.Op != OpConst64 && x.Op != OpConst64) { + x := v_1.Args[0] + v.reset(OpNeg64) + v.AddArg(x) + return true + } + break + } + // match: (Add64 x (Sub64 y x)) + // result: y + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpSub64 { + continue + } + _ = v_1.Args[1] + y := v_1.Args[0] + if x != v_1.Args[1] { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Add64 x (Add64 y (Sub64 z x))) + // result: (Add64 y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAdd64 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpSub64 { + continue + } + _ = v_1_1.Args[1] + z := v_1_1.Args[0] + if x != v_1_1.Args[1] { + continue + } + v.reset(OpAdd64) + v.AddArg2(y, z) + return true + } + } + break + } + // match: (Add64 (Add64 i:(Const64 ) z) x) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Add64 i (Add64 z x)) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAdd64 { + continue + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { + i := v_0_0 + if i.Op != OpConst64 { + continue + } + t := i.Type + z := v_0_1 + x := v_1 + if !(z.Op != OpConst64 && x.Op != OpConst64) { + continue + } + v.reset(OpAdd64) + v0 := b.NewValue0(v.Pos, OpAdd64, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + } + break + } + // match: (Add64 (Sub64 i:(Const64 ) z) x) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Add64 i (Sub64 x z)) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpSub64 { + continue + } + z := v_0.Args[1] + i := v_0.Args[0] + if i.Op != OpConst64 { + continue + } + t := i.Type + x := v_1 + if !(z.Op != OpConst64 && x.Op != OpConst64) { continue } v.reset(OpAdd64) @@ -1071,6 +1932,314 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool { } break } + // match: (Add64 (Lsh64x64 x z:(Const64 [c])) (Rsh64Ux64 x (Const64 [d]))) + // cond: c < 64 && d == 64-c && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh64x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh64Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 64 && d == 64-c && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add64 left:(Lsh64x64 x y) right:(Rsh64Ux64 x (Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add64 left:(Lsh64x32 x y) right:(Rsh64Ux32 x (Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add64 left:(Lsh64x16 x y) right:(Rsh64Ux16 x (Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add64 left:(Lsh64x8 x y) right:(Rsh64Ux8 x (Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add64 right:(Rsh64Ux64 x y) left:(Lsh64x64 x z:(Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add64 right:(Rsh64Ux32 x y) left:(Lsh64x32 x z:(Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add64 right:(Rsh64Ux16 x y) left:(Lsh64x16 x z:(Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add64 right:(Rsh64Ux8 x y) left:(Lsh64x8 x z:(Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpAdd64F(v *Value) bool { @@ -1104,6 +2273,7 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Add8 (Const8 [c]) (Const8 [d])) // result: (Const8 [c+d]) for { @@ -1170,11 +2340,43 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool { } break } - // match: (Add8 (Const8 [1]) (Com8 x)) - // result: (Neg8 x) + // match: (Add8 x (Neg8 y)) + // result: (Sub8 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 1 || v_1.Op != OpCom8 { + x := v_0 + if v_1.Op != OpNeg8 { + continue + } + y := v_1.Args[0] + v.reset(OpSub8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add8 (Com8 x) x) + // result: (Const8 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom8 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(-1) + return true + } + break + } + // match: (Add8 (Const8 [1]) (Com8 x)) + // result: (Neg8 x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 1 || v_1.Op != OpCom8 { continue } x := v_1.Args[0] @@ -1184,6 +2386,52 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool { } break } + // match: (Add8 x (Sub8 y x)) + // result: y + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpSub8 { + continue + } + _ = v_1.Args[1] + y := v_1.Args[0] + if x != v_1.Args[1] { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Add8 x (Add8 y (Sub8 z x))) + // result: (Add8 y z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpAdd8 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpSub8 { + continue + } + _ = v_1_1.Args[1] + z := v_1_1.Args[0] + if x != v_1_1.Args[1] { + continue + } + v.reset(OpAdd8) + v.AddArg2(y, z) + return true + } + } + break + } // match: (Add8 (Add8 i:(Const8 ) z) x) // cond: (z.Op != OpConst8 && x.Op != OpConst8) // result: (Add8 i (Add8 z x)) @@ -1297,6 +2545,314 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool { } break } + // match: (Add8 (Lsh8x64 x z:(Const64 [c])) (Rsh8Ux64 x (Const64 [d]))) + // cond: c < 8 && d == 8-c && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh8x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh8Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 8 && d == 8-c && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add8 left:(Lsh8x64 x y) right:(Rsh8Ux64 x (Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add8 left:(Lsh8x32 x y) right:(Rsh8Ux32 x (Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add8 left:(Lsh8x16 x y) right:(Rsh8Ux16 x (Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add8 left:(Lsh8x8 x y) right:(Rsh8Ux8 x (Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Add8 right:(Rsh8Ux64 x y) left:(Lsh8x64 x z:(Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add8 right:(Rsh8Ux32 x y) left:(Lsh8x32 x z:(Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add8 right:(Rsh8Ux16 x y) left:(Lsh8x16 x z:(Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Add8 right:(Rsh8Ux8 x y) left:(Lsh8x8 x z:(Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpAddPtr(v *Value) bool { @@ -1446,6 +3002,23 @@ func rewriteValuegeneric_OpAnd16(v *Value) bool { } break } + // match: (And16 (Com16 x) x) + // result: (Const16 [0]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom16 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + break + } // match: (And16 x (And16 x y)) // result: (And16 x y) for { @@ -1644,6 +3217,23 @@ func rewriteValuegeneric_OpAnd32(v *Value) bool { } break } + // match: (And32 (Com32 x) x) + // result: (Const32 [0]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom32 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true + } + break + } // match: (And32 x (And32 x y)) // result: (And32 x y) for { @@ -1842,6 +3432,23 @@ func rewriteValuegeneric_OpAnd64(v *Value) bool { } break } + // match: (And64 (Com64 x) x) + // result: (Const64 [0]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom64 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) + return true + } + break + } // match: (And64 x (And64 x y)) // result: (And64 x y) for { @@ -2040,6 +3647,23 @@ func rewriteValuegeneric_OpAnd8(v *Value) bool { } break } + // match: (And8 (Com8 x) x) + // result: (Const8 [0]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom8 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) + return true + } + break + } // match: (And8 x (And8 x y)) // result: (And8 x y) for { @@ -3469,6 +5093,21 @@ func rewriteValuegeneric_OpArraySelect(v *Value) bool { } return false } +func rewriteValuegeneric_OpCeil(v *Value) bool { + v_0 := v.Args[0] + // match: (Ceil (Const64F [c])) + // result: (Const64F [math.Ceil(c)]) + for { + if v_0.Op != OpConst64F { + break + } + c := auxIntToFloat64(v_0.AuxInt) + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(math.Ceil(c)) + return true + } + return false +} func rewriteValuegeneric_OpCom16(v *Value) bool { v_0 := v.Args[0] // match: (Com16 (Com16 x)) @@ -8529,6 +10168,21 @@ func rewriteValuegeneric_OpEqSlice(v *Value) bool { return true } } +func rewriteValuegeneric_OpFloor(v *Value) bool { + v_0 := v.Args[0] + // match: (Floor (Const64F [c])) + // result: (Const64F [math.Floor(c)]) + for { + if v_0.Op != OpConst64F { + break + } + c := auxIntToFloat64(v_0.AuxInt) + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(math.Floor(c)) + return true + } + return false +} func rewriteValuegeneric_OpIMake(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -9899,6 +11553,7 @@ func rewriteValuegeneric_OpLeq8U(v *Value) bool { func rewriteValuegeneric_OpLess16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (Less16 (Const16 [c]) (Const16 [d])) // result: (ConstBool [c < d]) for { @@ -9914,6 +11569,45 @@ func rewriteValuegeneric_OpLess16(v *Value) bool { v.AuxInt = boolToAuxInt(c < d) return true } + // match: (Less16 (Const16 [0]) x) + // cond: isNonNegative(x) + // result: (Neq16 (Const16 [0]) x) + for { + if v_0.Op != OpConst16 { + break + } + t := v_0.Type + if auxIntToInt16(v_0.AuxInt) != 0 { + break + } + x := v_1 + if !(isNonNegative(x)) { + break + } + v.reset(OpNeq16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(0) + v.AddArg2(v0, x) + return true + } + // match: (Less16 x (Const16 [1])) + // cond: isNonNegative(x) + // result: (Eq16 (Const16 [0]) x) + for { + x := v_0 + if v_1.Op != OpConst16 { + break + } + t := v_1.Type + if auxIntToInt16(v_1.AuxInt) != 1 || !(isNonNegative(x)) { + break + } + v.reset(OpEq16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(0) + v.AddArg2(v0, x) + return true + } return false } func rewriteValuegeneric_OpLess16U(v *Value) bool { @@ -9949,6 +11643,7 @@ func rewriteValuegeneric_OpLess16U(v *Value) bool { func rewriteValuegeneric_OpLess32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (Less32 (Const32 [c]) (Const32 [d])) // result: (ConstBool [c < d]) for { @@ -9964,6 +11659,45 @@ func rewriteValuegeneric_OpLess32(v *Value) bool { v.AuxInt = boolToAuxInt(c < d) return true } + // match: (Less32 (Const32 [0]) x) + // cond: isNonNegative(x) + // result: (Neq32 (Const32 [0]) x) + for { + if v_0.Op != OpConst32 { + break + } + t := v_0.Type + if auxIntToInt32(v_0.AuxInt) != 0 { + break + } + x := v_1 + if !(isNonNegative(x)) { + break + } + v.reset(OpNeq32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg2(v0, x) + return true + } + // match: (Less32 x (Const32 [1])) + // cond: isNonNegative(x) + // result: (Eq32 (Const32 [0]) x) + for { + x := v_0 + if v_1.Op != OpConst32 { + break + } + t := v_1.Type + if auxIntToInt32(v_1.AuxInt) != 1 || !(isNonNegative(x)) { + break + } + v.reset(OpEq32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(0) + v.AddArg2(v0, x) + return true + } return false } func rewriteValuegeneric_OpLess32F(v *Value) bool { @@ -10019,6 +11753,7 @@ func rewriteValuegeneric_OpLess32U(v *Value) bool { func rewriteValuegeneric_OpLess64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (Less64 (Const64 [c]) (Const64 [d])) // result: (ConstBool [c < d]) for { @@ -10034,6 +11769,45 @@ func rewriteValuegeneric_OpLess64(v *Value) bool { v.AuxInt = boolToAuxInt(c < d) return true } + // match: (Less64 (Const64 [0]) x) + // cond: isNonNegative(x) + // result: (Neq64 (Const64 [0]) x) + for { + if v_0.Op != OpConst64 { + break + } + t := v_0.Type + if auxIntToInt64(v_0.AuxInt) != 0 { + break + } + x := v_1 + if !(isNonNegative(x)) { + break + } + v.reset(OpNeq64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } + // match: (Less64 x (Const64 [1])) + // cond: isNonNegative(x) + // result: (Eq64 (Const64 [0]) x) + for { + x := v_0 + if v_1.Op != OpConst64 { + break + } + t := v_1.Type + if auxIntToInt64(v_1.AuxInt) != 1 || !(isNonNegative(x)) { + break + } + v.reset(OpEq64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(0) + v.AddArg2(v0, x) + return true + } return false } func rewriteValuegeneric_OpLess64F(v *Value) bool { @@ -10089,6 +11863,7 @@ func rewriteValuegeneric_OpLess64U(v *Value) bool { func rewriteValuegeneric_OpLess8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (Less8 (Const8 [c]) (Const8 [d])) // result: (ConstBool [c < d]) for { @@ -10104,6 +11879,45 @@ func rewriteValuegeneric_OpLess8(v *Value) bool { v.AuxInt = boolToAuxInt(c < d) return true } + // match: (Less8 (Const8 [0]) x) + // cond: isNonNegative(x) + // result: (Neq8 (Const8 [0]) x) + for { + if v_0.Op != OpConst8 { + break + } + t := v_0.Type + if auxIntToInt8(v_0.AuxInt) != 0 { + break + } + x := v_1 + if !(isNonNegative(x)) { + break + } + v.reset(OpNeq8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(0) + v.AddArg2(v0, x) + return true + } + // match: (Less8 x (Const8 [1])) + // cond: isNonNegative(x) + // result: (Eq8 (Const8 [0]) x) + for { + x := v_0 + if v_1.Op != OpConst8 { + break + } + t := v_1.Type + if auxIntToInt8(v_1.AuxInt) != 1 || !(isNonNegative(x)) { + break + } + v.reset(OpEq8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(0) + v.AddArg2(v0, x) + return true + } return false } func rewriteValuegeneric_OpLess8U(v *Value) bool { @@ -10978,6 +12792,54 @@ func rewriteValuegeneric_OpLsh16x64(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Lsh16x64 i:(Rsh16x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 16 && i.Uses == 1 + // result: (And16 x (Const16 [int16(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh16x64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 16 && i.Uses == 1) { + break + } + v.reset(OpAnd16) + v0 := b.NewValue0(v.Pos, OpConst16, v.Type) + v0.AuxInt = int16ToAuxInt(int16(-1) << c) + v.AddArg2(x, v0) + return true + } + // match: (Lsh16x64 i:(Rsh16Ux64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 16 && i.Uses == 1 + // result: (And16 x (Const16 [int16(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh16Ux64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 16 && i.Uses == 1) { + break + } + v.reset(OpAnd16) + v0 := b.NewValue0(v.Pos, OpConst16, v.Type) + v0.AuxInt = int16ToAuxInt(int16(-1) << c) + v.AddArg2(x, v0) + return true + } // match: (Lsh16x64 (Rsh16Ux64 (Lsh16x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) // result: (Lsh16x64 x (Const64 [c1-c2+c3])) @@ -11193,6 +13055,54 @@ func rewriteValuegeneric_OpLsh32x64(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Lsh32x64 i:(Rsh32x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 32 && i.Uses == 1 + // result: (And32 x (Const32 [int32(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh32x64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 32 && i.Uses == 1) { + break + } + v.reset(OpAnd32) + v0 := b.NewValue0(v.Pos, OpConst32, v.Type) + v0.AuxInt = int32ToAuxInt(int32(-1) << c) + v.AddArg2(x, v0) + return true + } + // match: (Lsh32x64 i:(Rsh32Ux64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 32 && i.Uses == 1 + // result: (And32 x (Const32 [int32(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh32Ux64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 32 && i.Uses == 1) { + break + } + v.reset(OpAnd32) + v0 := b.NewValue0(v.Pos, OpConst32, v.Type) + v0.AuxInt = int32ToAuxInt(int32(-1) << c) + v.AddArg2(x, v0) + return true + } // match: (Lsh32x64 (Rsh32Ux64 (Lsh32x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) // result: (Lsh32x64 x (Const64 [c1-c2+c3])) @@ -11408,6 +13318,54 @@ func rewriteValuegeneric_OpLsh64x64(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (Lsh64x64 i:(Rsh64x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 64 && i.Uses == 1 + // result: (And64 x (Const64 [int64(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh64x64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 64 && i.Uses == 1) { + break + } + v.reset(OpAnd64) + v0 := b.NewValue0(v.Pos, OpConst64, v.Type) + v0.AuxInt = int64ToAuxInt(int64(-1) << c) + v.AddArg2(x, v0) + return true + } + // match: (Lsh64x64 i:(Rsh64Ux64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 64 && i.Uses == 1 + // result: (And64 x (Const64 [int64(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh64Ux64 { + break + } + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 64 && i.Uses == 1) { + break + } + v.reset(OpAnd64) + v0 := b.NewValue0(v.Pos, OpConst64, v.Type) + v0.AuxInt = int64ToAuxInt(int64(-1) << c) + v.AddArg2(x, v0) + return true + } // match: (Lsh64x64 (Rsh64Ux64 (Lsh64x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) // result: (Lsh64x64 x (Const64 [c1-c2+c3])) @@ -11623,30 +13581,78 @@ func rewriteValuegeneric_OpLsh8x64(v *Value) bool { v.AddArg2(x, v0) return true } - // match: (Lsh8x64 (Rsh8Ux64 (Lsh8x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) - // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) - // result: (Lsh8x64 x (Const64 [c1-c2+c3])) + // match: (Lsh8x64 i:(Rsh8x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 8 && i.Uses == 1 + // result: (And8 x (Const8 [int8(-1) << c])) for { - if v_0.Op != OpRsh8Ux64 { + i := v_0 + if i.Op != OpRsh8x64 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpLsh8x64 { + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { break } - _ = v_0_0.Args[1] - x := v_0_0.Args[0] - v_0_0_1 := v_0_0.Args[1] - if v_0_0_1.Op != OpConst64 { + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 8 && i.Uses == 1) { break } - c1 := auxIntToInt64(v_0_0_1.AuxInt) - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + v.reset(OpAnd8) + v0 := b.NewValue0(v.Pos, OpConst8, v.Type) + v0.AuxInt = int8ToAuxInt(int8(-1) << c) + v.AddArg2(x, v0) + return true + } + // match: (Lsh8x64 i:(Rsh8Ux64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 8 && i.Uses == 1 + // result: (And8 x (Const8 [int8(-1) << c])) + for { + i := v_0 + if i.Op != OpRsh8Ux64 { break } - c2 := auxIntToInt64(v_0_1.AuxInt) + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 8 && i.Uses == 1) { + break + } + v.reset(OpAnd8) + v0 := b.NewValue0(v.Pos, OpConst8, v.Type) + v0.AuxInt = int8ToAuxInt(int8(-1) << c) + v.AddArg2(x, v0) + return true + } + // match: (Lsh8x64 (Rsh8Ux64 (Lsh8x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) + // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) + // result: (Lsh8x64 x (Const64 [c1-c2+c3])) + for { + if v_0.Op != OpRsh8Ux64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpLsh8x64 { + break + } + _ = v_0_0.Args[1] + x := v_0_0.Args[0] + v_0_0_1 := v_0_0.Args[1] + if v_0_0_1.Op != OpConst64 { + break + } + c1 := auxIntToInt64(v_0_0_1.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { + break + } + c2 := auxIntToInt64(v_0_1.AuxInt) if v_1.Op != OpConst64 { break } @@ -16566,6 +18572,7 @@ func rewriteValuegeneric_OpOr16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Or16 (Const16 [c]) (Const16 [d])) // result: (Const16 [c|d]) for { @@ -16620,6 +18627,23 @@ func rewriteValuegeneric_OpOr16(v *Value) bool { } break } + // match: (Or16 (Com16 x) x) + // result: (Const16 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom16 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(-1) + return true + } + break + } // match: (Or16 x (Or16 x y)) // result: (Or16 x y) for { @@ -16738,12 +18762,321 @@ func rewriteValuegeneric_OpOr16(v *Value) bool { } break } + // match: (Or16 (Lsh16x64 x z:(Const64 [c])) (Rsh16Ux64 x (Const64 [d]))) + // cond: c < 16 && d == 16-c && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh16x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh16Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 16 && d == 16-c && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or16 left:(Lsh16x64 x y) right:(Rsh16Ux64 x (Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or16 left:(Lsh16x32 x y) right:(Rsh16Ux32 x (Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or16 left:(Lsh16x16 x y) right:(Rsh16Ux16 x (Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or16 left:(Lsh16x8 x y) right:(Rsh16Ux8 x (Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or16 right:(Rsh16Ux64 x y) left:(Lsh16x64 x z:(Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or16 right:(Rsh16Ux32 x y) left:(Lsh16x32 x z:(Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or16 right:(Rsh16Ux16 x y) left:(Lsh16x16 x z:(Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or16 right:(Rsh16Ux8 x y) left:(Lsh16x8 x z:(Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpOr32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Or32 (Const32 [c]) (Const32 [d])) // result: (Const32 [c|d]) for { @@ -16798,6 +19131,23 @@ func rewriteValuegeneric_OpOr32(v *Value) bool { } break } + // match: (Or32 (Com32 x) x) + // result: (Const32 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom32 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(-1) + return true + } + break + } // match: (Or32 x (Or32 x y)) // result: (Or32 x y) for { @@ -16916,32 +19266,341 @@ func rewriteValuegeneric_OpOr32(v *Value) bool { } break } - return false -} -func rewriteValuegeneric_OpOr64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Or64 (Const64 [c]) (Const64 [d])) - // result: (Const64 [c|d]) + // match: (Or32 (Lsh32x64 x z:(Const64 [c])) (Rsh32Ux64 x (Const64 [d]))) + // cond: c < 32 && d == 32-c && canRotate(config, 32) + // result: (RotateLeft32 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst64 { + if v_0.Op != OpLsh32x64 { continue } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpConst64 { + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { continue } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(c | d) + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh32Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 32 && d == 32-c && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) return true } break } - // match: (Or64 x x) - // result: x + // match: (Or32 left:(Lsh32x64 x y) right:(Rsh32Ux64 x (Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or32 left:(Lsh32x32 x y) right:(Rsh32Ux32 x (Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or32 left:(Lsh32x16 x y) right:(Rsh32Ux16 x (Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or32 left:(Lsh32x8 x y) right:(Rsh32Ux8 x (Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or32 right:(Rsh32Ux64 x y) left:(Lsh32x64 x z:(Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or32 right:(Rsh32Ux32 x y) left:(Lsh32x32 x z:(Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or32 right:(Rsh32Ux16 x y) left:(Lsh32x16 x z:(Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or32 right:(Rsh32Ux8 x y) left:(Lsh32x8 x z:(Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpOr64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + // match: (Or64 (Const64 [c]) (Const64 [d])) + // result: (Const64 [c|d]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(c | d) + return true + } + break + } + // match: (Or64 x x) + // result: x for { x := v_0 if x != v_1 { @@ -16976,6 +19635,23 @@ func rewriteValuegeneric_OpOr64(v *Value) bool { } break } + // match: (Or64 (Com64 x) x) + // result: (Const64 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom64 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(-1) + return true + } + break + } // match: (Or64 x (Or64 x y)) // result: (Or64 x y) for { @@ -17094,68 +19770,394 @@ func rewriteValuegeneric_OpOr64(v *Value) bool { } break } - return false -} -func rewriteValuegeneric_OpOr8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Or8 (Const8 [c]) (Const8 [d])) - // result: (Const8 [c|d]) + // match: (Or64 (Lsh64x64 x z:(Const64 [c])) (Rsh64Ux64 x (Const64 [d]))) + // cond: c < 64 && d == 64-c && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst8 { + if v_0.Op != OpLsh64x64 { continue } - c := auxIntToInt8(v_0.AuxInt) - if v_1.Op != OpConst8 { + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { continue } - d := auxIntToInt8(v_1.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(c | d) + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh64Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 64 && d == 64-c && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) return true } break } - // match: (Or8 x x) - // result: x + // match: (Or64 left:(Lsh64x64 x y) right:(Rsh64Ux64 x (Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { - x := v_0 - if x != v_1 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true } - v.copyOf(x) - return true + break } - // match: (Or8 (Const8 [0]) x) - // result: x + // match: (Or64 left:(Lsh64x32 x y) right:(Rsh64Ux32 x (Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + left := v_0 + if left.Op != OpLsh64x32 { continue } - x := v_1 - v.copyOf(x) + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) return true } break } - // match: (Or8 (Const8 [-1]) _) - // result: (Const8 [-1]) + // match: (Or64 left:(Lsh64x16 x y) right:(Rsh64Ux16 x (Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != -1 { + left := v_0 + if left.Op != OpLsh64x16 { continue } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(-1) + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) return true } break } - // match: (Or8 x (Or8 x y)) - // result: (Or8 x y) + // match: (Or64 left:(Lsh64x8 x y) right:(Rsh64Ux8 x (Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh64x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or64 right:(Rsh64Ux64 x y) left:(Lsh64x64 x z:(Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or64 right:(Rsh64Ux32 x y) left:(Lsh64x32 x z:(Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or64 right:(Rsh64Ux16 x y) left:(Lsh64x16 x z:(Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or64 right:(Rsh64Ux8 x y) left:(Lsh64x8 x z:(Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh64Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpOr8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + // match: (Or8 (Const8 [c]) (Const8 [d])) + // result: (Const8 [c|d]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpConst8 { + continue + } + d := auxIntToInt8(v_1.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(c | d) + return true + } + break + } + // match: (Or8 x x) + // result: x + for { + x := v_0 + if x != v_1 { + break + } + v.copyOf(x) + return true + } + // match: (Or8 (Const8 [0]) x) + // result: x + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + continue + } + x := v_1 + v.copyOf(x) + return true + } + break + } + // match: (Or8 (Const8 [-1]) _) + // result: (Const8 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != -1 { + continue + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(-1) + return true + } + break + } + // match: (Or8 (Com8 x) x) + // result: (Const8 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom8 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(-1) + return true + } + break + } + // match: (Or8 x (Or8 x y)) + // result: (Or8 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 @@ -17272,27 +20274,22 @@ func rewriteValuegeneric_OpOr8(v *Value) bool { } break } - return false -} -func rewriteValuegeneric_OpOrB(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (OrB (Less64 (Const64 [c]) x) (Less64 x (Const64 [d]))) - // cond: c >= d - // result: (Less64U (Const64 [c-d]) (Sub64 x (Const64 [d]))) + // match: (Or8 (Lsh8x64 x z:(Const64 [c])) (Rsh8Ux64 x (Const64 [d]))) + // cond: c < 8 && d == 8-c && canRotate(config, 8) + // result: (RotateLeft8 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLess64 { + if v_0.Op != OpLsh8x64 { continue } - x := v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst64 { + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { continue } - c := auxIntToInt64(v_0_0.AuxInt) - if v_1.Op != OpLess64 { + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh8Ux64 { continue } _ = v_1.Args[1] @@ -17304,130 +20301,443 @@ func rewriteValuegeneric_OpOrB(v *Value) bool { continue } d := auxIntToInt64(v_1_1.AuxInt) - if !(c >= d) { + if !(c < 8 && d == 8-c && canRotate(config, 8)) { continue } - v.reset(OpLess64U) - v0 := b.NewValue0(v.Pos, OpConst64, x.Type) - v0.AuxInt = int64ToAuxInt(c - d) - v1 := b.NewValue0(v.Pos, OpSub64, x.Type) - v2 := b.NewValue0(v.Pos, OpConst64, x.Type) - v2.AuxInt = int64ToAuxInt(d) - v1.AddArg2(x, v2) - v.AddArg2(v0, v1) + v.reset(OpRotateLeft8) + v.AddArg2(x, z) return true } break } - // match: (OrB (Leq64 (Const64 [c]) x) (Less64 x (Const64 [d]))) - // cond: c >= d - // result: (Leq64U (Const64 [c-d]) (Sub64 x (Const64 [d]))) + // match: (Or8 left:(Lsh8x64 x y) right:(Rsh8Ux64 x (Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLeq64 { - continue - } - x := v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst64 { + left := v_0 + if left.Op != OpLsh8x64 { continue } - c := auxIntToInt64(v_0_0.AuxInt) - if v_1.Op != OpLess64 { + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux64 { continue } - _ = v_1.Args[1] - if x != v_1.Args[0] { + _ = right.Args[1] + if x != right.Args[0] { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpConst64 { + right_1 := right.Args[1] + if right_1.Op != OpSub64 { continue } - d := auxIntToInt64(v_1_1.AuxInt) - if !(c >= d) { + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { continue } - v.reset(OpLeq64U) - v0 := b.NewValue0(v.Pos, OpConst64, x.Type) - v0.AuxInt = int64ToAuxInt(c - d) - v1 := b.NewValue0(v.Pos, OpSub64, x.Type) - v2 := b.NewValue0(v.Pos, OpConst64, x.Type) - v2.AuxInt = int64ToAuxInt(d) - v1.AddArg2(x, v2) - v.AddArg2(v0, v1) + v.reset(OpRotateLeft8) + v.AddArg2(x, y) return true } break } - // match: (OrB (Less32 (Const32 [c]) x) (Less32 x (Const32 [d]))) - // cond: c >= d - // result: (Less32U (Const32 [c-d]) (Sub32 x (Const32 [d]))) + // match: (Or8 left:(Lsh8x32 x y) right:(Rsh8Ux32 x (Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLess32 { - continue - } - x := v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst32 { + left := v_0 + if left.Op != OpLsh8x32 { continue } - c := auxIntToInt32(v_0_0.AuxInt) - if v_1.Op != OpLess32 { + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux32 { continue } - _ = v_1.Args[1] - if x != v_1.Args[0] { + _ = right.Args[1] + if x != right.Args[0] { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpConst32 { + right_1 := right.Args[1] + if right_1.Op != OpSub32 { continue } - d := auxIntToInt32(v_1_1.AuxInt) - if !(c >= d) { + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { continue } - v.reset(OpLess32U) - v0 := b.NewValue0(v.Pos, OpConst32, x.Type) - v0.AuxInt = int32ToAuxInt(c - d) - v1 := b.NewValue0(v.Pos, OpSub32, x.Type) - v2 := b.NewValue0(v.Pos, OpConst32, x.Type) - v2.AuxInt = int32ToAuxInt(d) - v1.AddArg2(x, v2) - v.AddArg2(v0, v1) + v.reset(OpRotateLeft8) + v.AddArg2(x, y) return true } break } - // match: (OrB (Leq32 (Const32 [c]) x) (Less32 x (Const32 [d]))) - // cond: c >= d - // result: (Leq32U (Const32 [c-d]) (Sub32 x (Const32 [d]))) + // match: (Or8 left:(Lsh8x16 x y) right:(Rsh8Ux16 x (Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLeq32 { - continue - } - x := v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst32 { + left := v_0 + if left.Op != OpLsh8x16 { continue } - c := auxIntToInt32(v_0_0.AuxInt) - if v_1.Op != OpLess32 { + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux16 { continue } - _ = v_1.Args[1] - if x != v_1.Args[0] { + _ = right.Args[1] + if x != right.Args[0] { continue } - v_1_1 := v_1.Args[1] - if v_1_1.Op != OpConst32 { + right_1 := right.Args[1] + if right_1.Op != OpSub16 { continue } - d := auxIntToInt32(v_1_1.AuxInt) - if !(c >= d) { + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or8 left:(Lsh8x8 x y) right:(Rsh8Ux8 x (Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Or8 right:(Rsh8Ux64 x y) left:(Lsh8x64 x z:(Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or8 right:(Rsh8Ux32 x y) left:(Lsh8x32 x z:(Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or8 right:(Rsh8Ux16 x y) left:(Lsh8x16 x z:(Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Or8 right:(Rsh8Ux8 x y) left:(Lsh8x8 x z:(Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpOrB(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (OrB (Less64 (Const64 [c]) x) (Less64 x (Const64 [d]))) + // cond: c >= d + // result: (Less64U (Const64 [c-d]) (Sub64 x (Const64 [d]))) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLess64 { + continue + } + x := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_0_0.AuxInt) + if v_1.Op != OpLess64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c >= d) { + continue + } + v.reset(OpLess64U) + v0 := b.NewValue0(v.Pos, OpConst64, x.Type) + v0.AuxInt = int64ToAuxInt(c - d) + v1 := b.NewValue0(v.Pos, OpSub64, x.Type) + v2 := b.NewValue0(v.Pos, OpConst64, x.Type) + v2.AuxInt = int64ToAuxInt(d) + v1.AddArg2(x, v2) + v.AddArg2(v0, v1) + return true + } + break + } + // match: (OrB (Leq64 (Const64 [c]) x) (Less64 x (Const64 [d]))) + // cond: c >= d + // result: (Leq64U (Const64 [c-d]) (Sub64 x (Const64 [d]))) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLeq64 { + continue + } + x := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_0_0.AuxInt) + if v_1.Op != OpLess64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c >= d) { + continue + } + v.reset(OpLeq64U) + v0 := b.NewValue0(v.Pos, OpConst64, x.Type) + v0.AuxInt = int64ToAuxInt(c - d) + v1 := b.NewValue0(v.Pos, OpSub64, x.Type) + v2 := b.NewValue0(v.Pos, OpConst64, x.Type) + v2.AuxInt = int64ToAuxInt(d) + v1.AddArg2(x, v2) + v.AddArg2(v0, v1) + return true + } + break + } + // match: (OrB (Less32 (Const32 [c]) x) (Less32 x (Const32 [d]))) + // cond: c >= d + // result: (Less32U (Const32 [c-d]) (Sub32 x (Const32 [d]))) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLess32 { + continue + } + x := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_0_0.AuxInt) + if v_1.Op != OpLess32 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst32 { + continue + } + d := auxIntToInt32(v_1_1.AuxInt) + if !(c >= d) { + continue + } + v.reset(OpLess32U) + v0 := b.NewValue0(v.Pos, OpConst32, x.Type) + v0.AuxInt = int32ToAuxInt(c - d) + v1 := b.NewValue0(v.Pos, OpSub32, x.Type) + v2 := b.NewValue0(v.Pos, OpConst32, x.Type) + v2.AuxInt = int32ToAuxInt(d) + v1.AddArg2(x, v2) + v.AddArg2(v0, v1) + return true + } + break + } + // match: (OrB (Leq32 (Const32 [c]) x) (Less32 x (Const32 [d]))) + // cond: c >= d + // result: (Leq32U (Const32 [c-d]) (Sub32 x (Const32 [d]))) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLeq32 { + continue + } + x := v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_0_0.AuxInt) + if v_1.Op != OpLess32 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst32 { + continue + } + d := auxIntToInt32(v_1_1.AuxInt) + if !(c >= d) { continue } v.reset(OpLeq32U) @@ -18722,6 +22032,8 @@ func rewriteValuegeneric_OpPtrIndex(v *Value) bool { func rewriteValuegeneric_OpRotateLeft16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (RotateLeft16 x (Const16 [c])) // cond: c%16 == 0 // result: x @@ -18737,1747 +22049,2215 @@ func rewriteValuegeneric_OpRotateLeft16(v *Value) bool { v.copyOf(x) return true } - return false -} -func rewriteValuegeneric_OpRotateLeft32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft32 x (Const32 [c])) - // cond: c%32 == 0 - // result: x + // match: (RotateLeft16 x (And64 y (Const64 [c]))) + // cond: c&15 == 15 + // result: (RotateLeft16 x y) for { x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpAnd64 { break } - c := auxIntToInt32(v_1.AuxInt) - if !(c%32 == 0) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true } - v.copyOf(x) - return true + break } - return false -} -func rewriteValuegeneric_OpRotateLeft64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft64 x (Const64 [c])) - // cond: c%64 == 0 - // result: x + // match: (RotateLeft16 x (And32 y (Const32 [c]))) + // cond: c&15 == 15 + // result: (RotateLeft16 x y) for { x := v_0 - if v_1.Op != OpConst64 { + if v_1.Op != OpAnd32 { break } - c := auxIntToInt64(v_1.AuxInt) - if !(c%64 == 0) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true } - v.copyOf(x) - return true + break } - return false -} -func rewriteValuegeneric_OpRotateLeft8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (RotateLeft8 x (Const8 [c])) - // cond: c%8 == 0 - // result: x + // match: (RotateLeft16 x (And16 y (Const16 [c]))) + // cond: c&15 == 15 + // result: (RotateLeft16 x y) for { x := v_0 - if v_1.Op != OpConst8 { + if v_1.Op != OpAnd16 { break } - c := auxIntToInt8(v_1.AuxInt) - if !(c%8 == 0) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true } - v.copyOf(x) - return true + break } - return false -} -func rewriteValuegeneric_OpRound32F(v *Value) bool { - v_0 := v.Args[0] - // match: (Round32F x:(Const32F)) - // result: x + // match: (RotateLeft16 x (And8 y (Const8 [c]))) + // cond: c&15 == 15 + // result: (RotateLeft16 x y) for { x := v_0 - if x.Op != OpConst32F { + if v_1.Op != OpAnd8 { break } - v.copyOf(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRound64F(v *Value) bool { - v_0 := v.Args[0] - // match: (Round64F x:(Const64F)) - // result: x + // match: (RotateLeft16 x (Neg64 (And64 y (Const64 [c])))) + // cond: c&15 == 15 + // result: (RotateLeft16 x (Neg64 y)) for { x := v_0 - if x.Op != OpConst64F { + if v_1.Op != OpNeg64 { break } - v.copyOf(x) - return true + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd64 { + break + } + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_0_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh16Ux16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16Ux16 x (Const16 [c])) - // result: (Rsh16Ux64 x (Const64 [int64(uint16(c))])) + // match: (RotateLeft16 x (Neg32 (And32 y (Const32 [c])))) + // cond: c&15 == 15 + // result: (RotateLeft16 x (Neg32 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst16 { + if v_1.Op != OpNeg32 { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh16Ux16 (Const16 [0]) _) - // result: (Const16 [0]) - for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd32 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_0_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh16Ux32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16Ux32 x (Const32 [c])) - // result: (Rsh16Ux64 x (Const64 [int64(uint32(c))])) + // match: (RotateLeft16 x (Neg16 (And16 y (Const16 [c])))) + // cond: c&15 == 15 + // result: (RotateLeft16 x (Neg16 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpNeg16 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh16Ux32 (Const16 [0]) _) - // result: (Const16 [0]) - for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd16 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_0_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh16Ux64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh16Ux64 (Const16 [c]) (Const64 [d])) - // result: (Const16 [int16(uint16(c) >> uint64(d))]) + // match: (RotateLeft16 x (Neg8 (And8 y (Const8 [c])))) + // cond: c&15 == 15 + // result: (RotateLeft16 x (Neg8 y)) for { - if v_0.Op != OpConst16 { + x := v_0 + if v_1.Op != OpNeg8 { break } - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpConst64 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd8 { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(int16(uint16(c) >> uint64(d))) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_0_1.AuxInt) + if !(c&15 == 15) { + continue + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh16Ux64 x (Const64 [0])) - // result: x + // match: (RotateLeft16 x (Add64 y (Const64 [c]))) + // cond: c&15 == 0 + // result: (RotateLeft16 x y) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpAdd64 { break } - v.copyOf(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&15 == 0) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh16Ux64 (Const16 [0]) _) - // result: (Const16 [0]) + // match: (RotateLeft16 x (Add32 y (Const32 [c]))) + // cond: c&15 == 0 + // result: (RotateLeft16 x y) for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAdd32 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&15 == 0) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh16Ux64 _ (Const64 [c])) - // cond: uint64(c) >= 16 - // result: (Const16 [0]) + // match: (RotateLeft16 x (Add16 y (Const16 [c]))) + // cond: c&15 == 0 + // result: (RotateLeft16 x y) for { - if v_1.Op != OpConst64 { - break - } - c := auxIntToInt64(v_1.AuxInt) - if !(uint64(c) >= 16) { + x := v_0 + if v_1.Op != OpAdd16 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&15 == 0) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh16Ux64 (Rsh16Ux64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh16Ux64 x (Const64 [c+d])) + // match: (RotateLeft16 x (Add8 y (Const8 [c]))) + // cond: c&15 == 0 + // result: (RotateLeft16 x y) for { - t := v.Type - if v_0.Op != OpRsh16Ux64 { + x := v_0 + if v_1.Op != OpAdd8 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&15 == 0) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (RotateLeft16 x (Sub64 (Const64 [c]) y)) + // cond: c&15 == 0 + // result: (RotateLeft16 x (Neg64 y)) + for { + x := v_0 + if v_1.Op != OpSub64 { break } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst64 { break } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + c := auxIntToInt64(v_1_0.AuxInt) + if !(c&15 == 0) { break } - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh16Ux64 (Rsh16x64 x _) (Const64 [15])) - // result: (Rsh16Ux64 x (Const64 [15])) + // match: (RotateLeft16 x (Sub32 (Const32 [c]) y)) + // cond: c&15 == 0 + // result: (RotateLeft16 x (Neg32 y)) for { - if v_0.Op != OpRsh16x64 { + x := v_0 + if v_1.Op != OpSub32 { break } - x := v_0.Args[0] - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst32 { break } - t := v_1.Type - if auxIntToInt64(v_1.AuxInt) != 15 { + c := auxIntToInt32(v_1_0.AuxInt) + if !(c&15 == 0) { break } - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(15) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh16Ux64 (Lsh16x64 (Rsh16Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) - // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) - // result: (Rsh16Ux64 x (Const64 [c1-c2+c3])) + // match: (RotateLeft16 x (Sub16 (Const16 [c]) y)) + // cond: c&15 == 0 + // result: (RotateLeft16 x (Neg16 y)) for { - if v_0.Op != OpLsh16x64 { + x := v_0 + if v_1.Op != OpSub16 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpRsh16Ux64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst16 { break } - _ = v_0_0.Args[1] - x := v_0_0.Args[0] - v_0_0_1 := v_0_0.Args[1] - if v_0_0_1.Op != OpConst64 { + c := auxIntToInt16(v_1_0.AuxInt) + if !(c&15 == 0) { break } - c1 := auxIntToInt64(v_0_0_1.AuxInt) - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (RotateLeft16 x (Sub8 (Const8 [c]) y)) + // cond: c&15 == 0 + // result: (RotateLeft16 x (Neg8 y)) + for { + x := v_0 + if v_1.Op != OpSub8 { break } - c2 := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst8 { break } - c3 := auxIntToInt64(v_1.AuxInt) - if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + c := auxIntToInt8(v_1_0.AuxInt) + if !(c&15 == 0) { break } - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) - v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh16Ux64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) - // result: (ZeroExt8to16 (Trunc16to8 x)) + // match: (RotateLeft16 x (Const64 [c])) + // cond: config.PtrSize == 4 + // result: (RotateLeft16 x (Const32 [int32(c)])) for { - if v_0.Op != OpLsh16x64 { + x := v_0 + if v_1.Op != OpConst64 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 8 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 8 { + t := v_1.Type + c := auxIntToInt64(v_1.AuxInt) + if !(config.PtrSize == 4) { break } - v.reset(OpZeroExt8to16) - v0 := b.NewValue0(v.Pos, OpTrunc16to8, typ.UInt8) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpRsh16Ux8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16Ux8 x (Const8 [c])) - // result: (Rsh16Ux64 x (Const64 [int64(uint8(c))])) + // match: (RotateLeft16 (RotateLeft16 x c) d) + // cond: c.Type.Size() == 8 && d.Type.Size() == 8 + // result: (RotateLeft16 x (Add64 c d)) for { - t := v.Type - x := v_0 - if v_1.Op != OpConst8 { + if v_0.Op != OpRotateLeft16 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh16Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 8 && d.Type.Size() == 8) { + break + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpAdd64, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh16Ux8 (Const16 [0]) _) - // result: (Const16 [0]) + // match: (RotateLeft16 (RotateLeft16 x c) d) + // cond: c.Type.Size() == 4 && d.Type.Size() == 4 + // result: (RotateLeft16 x (Add32 c d)) for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + if v_0.Op != OpRotateLeft16 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true - } - return false -} -func rewriteValuegeneric_OpRsh16x16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16x16 x (Const16 [c])) - // result: (Rsh16x64 x (Const64 [int64(uint16(c))])) - for { - t := v.Type - x := v_0 - if v_1.Op != OpConst16 { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 4 && d.Type.Size() == 4) { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh16x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpAdd32, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh16x16 (Const16 [0]) _) - // result: (Const16 [0]) + // match: (RotateLeft16 (RotateLeft16 x c) d) + // cond: c.Type.Size() == 2 && d.Type.Size() == 2 + // result: (RotateLeft16 x (Add16 c d)) for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + if v_0.Op != OpRotateLeft16 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true - } - return false -} -func rewriteValuegeneric_OpRsh16x32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16x32 x (Const32 [c])) - // result: (Rsh16x64 x (Const64 [int64(uint32(c))])) - for { - t := v.Type - x := v_0 - if v_1.Op != OpConst32 { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 2 && d.Type.Size() == 2) { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh16x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpAdd16, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh16x32 (Const16 [0]) _) - // result: (Const16 [0]) + // match: (RotateLeft16 (RotateLeft16 x c) d) + // cond: c.Type.Size() == 1 && d.Type.Size() == 1 + // result: (RotateLeft16 x (Add8 c d)) for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + if v_0.Op != OpRotateLeft16 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 1 && d.Type.Size() == 1) { + break + } + v.reset(OpRotateLeft16) + v0 := b.NewValue0(v.Pos, OpAdd8, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } return false } -func rewriteValuegeneric_OpRsh16x64(v *Value) bool { +func rewriteValuegeneric_OpRotateLeft32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh16x64 (Const16 [c]) (Const64 [d])) - // result: (Const16 [c >> uint64(d)]) + config := b.Func.Config + // match: (RotateLeft32 x (Const32 [c])) + // cond: c%32 == 0 + // result: x for { - if v_0.Op != OpConst16 { + x := v_0 + if v_1.Op != OpConst32 { break } - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpConst64 { + c := auxIntToInt32(v_1.AuxInt) + if !(c%32 == 0) { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(c >> uint64(d)) + v.copyOf(x) return true } - // match: (Rsh16x64 x (Const64 [0])) - // result: x + // match: (RotateLeft32 x (And64 y (Const64 [c]))) + // cond: c&31 == 31 + // result: (RotateLeft32 x y) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpAnd64 { break } - v.copyOf(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh16x64 (Const16 [0]) _) - // result: (Const16 [0]) + // match: (RotateLeft32 x (And32 y (Const32 [c]))) + // cond: c&31 == 31 + // result: (RotateLeft32 x y) for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAnd32 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh16x64 (Rsh16x64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh16x64 x (Const64 [c+d])) + // match: (RotateLeft32 x (And16 y (Const16 [c]))) + // cond: c&31 == 31 + // result: (RotateLeft32 x y) for { - t := v.Type - if v_0.Op != OpRsh16x64 { + x := v_0 + if v_1.Op != OpAnd16 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { - break - } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { - break - } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - v.reset(OpRsh16x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) - v.AddArg2(x, v0) - return true + break } - // match: (Rsh16x64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) - // result: (SignExt8to16 (Trunc16to8 x)) + // match: (RotateLeft32 x (And8 y (Const8 [c]))) + // cond: c&31 == 31 + // result: (RotateLeft32 x y) for { - if v_0.Op != OpLsh16x64 { + x := v_0 + if v_1.Op != OpAnd8 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 8 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 8 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - v.reset(OpSignExt8to16) - v0 := b.NewValue0(v.Pos, OpTrunc16to8, typ.Int8) - v0.AddArg(x) - v.AddArg(v0) - return true + break } - return false -} -func rewriteValuegeneric_OpRsh16x8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh16x8 x (Const8 [c])) - // result: (Rsh16x64 x (Const64 [int64(uint8(c))])) + // match: (RotateLeft32 x (Neg64 (And64 y (Const64 [c])))) + // cond: c&31 == 31 + // result: (RotateLeft32 x (Neg64 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst8 { + if v_1.Op != OpNeg64 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh16x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh16x8 (Const16 [0]) _) - // result: (Const16 [0]) - for { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd64 { break } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_0_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh32Ux16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh32Ux16 x (Const16 [c])) - // result: (Rsh32Ux64 x (Const64 [int64(uint16(c))])) + // match: (RotateLeft32 x (Neg32 (And32 y (Const32 [c])))) + // cond: c&31 == 31 + // result: (RotateLeft32 x (Neg32 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst16 { + if v_1.Op != OpNeg32 { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh32Ux16 (Const32 [0]) _) - // result: (Const32 [0]) - for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd32 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_0_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh32Ux32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh32Ux32 x (Const32 [c])) - // result: (Rsh32Ux64 x (Const64 [int64(uint32(c))])) + // match: (RotateLeft32 x (Neg16 (And16 y (Const16 [c])))) + // cond: c&31 == 31 + // result: (RotateLeft32 x (Neg16 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpNeg16 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh32Ux32 (Const32 [0]) _) - // result: (Const32 [0]) - for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd16 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_0_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh32Ux64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh32Ux64 (Const32 [c]) (Const64 [d])) - // result: (Const32 [int32(uint32(c) >> uint64(d))]) + // match: (RotateLeft32 x (Neg8 (And8 y (Const8 [c])))) + // cond: c&31 == 31 + // result: (RotateLeft32 x (Neg8 y)) for { - if v_0.Op != OpConst32 { + x := v_0 + if v_1.Op != OpNeg8 { break } - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpConst64 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd8 { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(int32(uint32(c) >> uint64(d))) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_0_1.AuxInt) + if !(c&31 == 31) { + continue + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh32Ux64 x (Const64 [0])) - // result: x + // match: (RotateLeft32 x (Add64 y (Const64 [c]))) + // cond: c&31 == 0 + // result: (RotateLeft32 x y) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpAdd64 { break } - v.copyOf(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&31 == 0) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh32Ux64 (Const32 [0]) _) - // result: (Const32 [0]) + // match: (RotateLeft32 x (Add32 y (Const32 [c]))) + // cond: c&31 == 0 + // result: (RotateLeft32 x y) for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAdd32 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&31 == 0) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh32Ux64 _ (Const64 [c])) - // cond: uint64(c) >= 32 - // result: (Const32 [0]) + // match: (RotateLeft32 x (Add16 y (Const16 [c]))) + // cond: c&31 == 0 + // result: (RotateLeft32 x y) for { - if v_1.Op != OpConst64 { + x := v_0 + if v_1.Op != OpAdd16 { break } - c := auxIntToInt64(v_1.AuxInt) - if !(uint64(c) >= 32) { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&31 == 0) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + break } - // match: (Rsh32Ux64 (Rsh32Ux64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh32Ux64 x (Const64 [c+d])) + // match: (RotateLeft32 x (Add8 y (Const8 [c]))) + // cond: c&31 == 0 + // result: (RotateLeft32 x y) for { - t := v.Type - if v_0.Op != OpRsh32Ux64 { + x := v_0 + if v_1.Op != OpAdd8 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&31 == 0) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true + } + break + } + // match: (RotateLeft32 x (Sub64 (Const64 [c]) y)) + // cond: c&31 == 0 + // result: (RotateLeft32 x (Neg64 y)) + for { + x := v_0 + if v_1.Op != OpSub64 { break } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst64 { break } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + c := auxIntToInt64(v_1_0.AuxInt) + if !(c&31 == 0) { break } - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 (Rsh32x64 x _) (Const64 [31])) - // result: (Rsh32Ux64 x (Const64 [31])) + // match: (RotateLeft32 x (Sub32 (Const32 [c]) y)) + // cond: c&31 == 0 + // result: (RotateLeft32 x (Neg32 y)) for { - if v_0.Op != OpRsh32x64 { + x := v_0 + if v_1.Op != OpSub32 { break } - x := v_0.Args[0] - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst32 { break } - t := v_1.Type - if auxIntToInt64(v_1.AuxInt) != 31 { + c := auxIntToInt32(v_1_0.AuxInt) + if !(c&31 == 0) { break } - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(31) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 (Lsh32x64 (Rsh32Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) - // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) - // result: (Rsh32Ux64 x (Const64 [c1-c2+c3])) + // match: (RotateLeft32 x (Sub16 (Const16 [c]) y)) + // cond: c&31 == 0 + // result: (RotateLeft32 x (Neg16 y)) for { - if v_0.Op != OpLsh32x64 { + x := v_0 + if v_1.Op != OpSub16 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpRsh32Ux64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst16 { break } - _ = v_0_0.Args[1] - x := v_0_0.Args[0] - v_0_0_1 := v_0_0.Args[1] - if v_0_0_1.Op != OpConst64 { + c := auxIntToInt16(v_1_0.AuxInt) + if !(c&31 == 0) { break } - c1 := auxIntToInt64(v_0_0_1.AuxInt) - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + // match: (RotateLeft32 x (Sub8 (Const8 [c]) y)) + // cond: c&31 == 0 + // result: (RotateLeft32 x (Neg8 y)) + for { + x := v_0 + if v_1.Op != OpSub8 { break } - c2 := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst8 { break } - c3 := auxIntToInt64(v_1.AuxInt) - if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + c := auxIntToInt8(v_1_0.AuxInt) + if !(c&31 == 0) { break } - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) - v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) - // result: (ZeroExt8to32 (Trunc32to8 x)) + // match: (RotateLeft32 x (Const64 [c])) + // cond: config.PtrSize == 4 + // result: (RotateLeft32 x (Const32 [int32(c)])) for { - if v_0.Op != OpLsh32x64 { + x := v_0 + if v_1.Op != OpConst64 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 24 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 24 { + t := v_1.Type + c := auxIntToInt64(v_1.AuxInt) + if !(config.PtrSize == 4) { break } - v.reset(OpZeroExt8to32) - v0 := b.NewValue0(v.Pos, OpTrunc32to8, typ.UInt8) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg2(x, v0) return true } - // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) - // result: (ZeroExt16to32 (Trunc32to16 x)) + // match: (RotateLeft32 (RotateLeft32 x c) d) + // cond: c.Type.Size() == 8 && d.Type.Size() == 8 + // result: (RotateLeft32 x (Add64 c d)) for { - if v_0.Op != OpLsh32x64 { + if v_0.Op != OpRotateLeft32 { break } - _ = v_0.Args[1] + c := v_0.Args[1] x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 16 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 16 { + d := v_1 + if !(c.Type.Size() == 8 && d.Type.Size() == 8) { break } - v.reset(OpZeroExt16to32) - v0 := b.NewValue0(v.Pos, OpTrunc32to16, typ.UInt16) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpAdd64, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpRsh32Ux8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh32Ux8 x (Const8 [c])) - // result: (Rsh32Ux64 x (Const64 [int64(uint8(c))])) + // match: (RotateLeft32 (RotateLeft32 x c) d) + // cond: c.Type.Size() == 4 && d.Type.Size() == 4 + // result: (RotateLeft32 x (Add32 c d)) for { - t := v.Type - x := v_0 - if v_1.Op != OpConst8 { + if v_0.Op != OpRotateLeft32 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh32Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 4 && d.Type.Size() == 4) { + break + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpAdd32, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh32Ux8 (Const32 [0]) _) - // result: (Const32 [0]) + // match: (RotateLeft32 (RotateLeft32 x c) d) + // cond: c.Type.Size() == 2 && d.Type.Size() == 2 + // result: (RotateLeft32 x (Add16 c d)) for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + if v_0.Op != OpRotateLeft32 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true - } - return false -} -func rewriteValuegeneric_OpRsh32x16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh32x16 x (Const16 [c])) - // result: (Rsh32x64 x (Const64 [int64(uint16(c))])) - for { - t := v.Type - x := v_0 - if v_1.Op != OpConst16 { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 2 && d.Type.Size() == 2) { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh32x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpAdd16, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh32x16 (Const32 [0]) _) - // result: (Const32 [0]) + // match: (RotateLeft32 (RotateLeft32 x c) d) + // cond: c.Type.Size() == 1 && d.Type.Size() == 1 + // result: (RotateLeft32 x (Add8 c d)) for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + if v_0.Op != OpRotateLeft32 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 1 && d.Type.Size() == 1) { + break + } + v.reset(OpRotateLeft32) + v0 := b.NewValue0(v.Pos, OpAdd8, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } return false } -func rewriteValuegeneric_OpRsh32x32(v *Value) bool { +func rewriteValuegeneric_OpRotateLeft64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (Rsh32x32 x (Const32 [c])) - // result: (Rsh32x64 x (Const64 [int64(uint32(c))])) + config := b.Func.Config + // match: (RotateLeft64 x (Const64 [c])) + // cond: c%64 == 0 + // result: x for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpConst64 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh32x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh32x32 (Const32 [0]) _) - // result: (Const32 [0]) - for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + c := auxIntToInt64(v_1.AuxInt) + if !(c%64 == 0) { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) + v.copyOf(x) return true } - return false -} -func rewriteValuegeneric_OpRsh32x64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh32x64 (Const32 [c]) (Const64 [d])) - // result: (Const32 [c >> uint64(d)]) + // match: (RotateLeft64 x (And64 y (Const64 [c]))) + // cond: c&63 == 63 + // result: (RotateLeft64 x y) for { - if v_0.Op != OpConst32 { + x := v_0 + if v_1.Op != OpAnd64 { break } - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpConst64 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(c >> uint64(d)) - return true + break } - // match: (Rsh32x64 x (Const64 [0])) - // result: x + // match: (RotateLeft64 x (And32 y (Const32 [c]))) + // cond: c&63 == 63 + // result: (RotateLeft64 x y) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpAnd32 { break } - v.copyOf(x) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh32x64 (Const32 [0]) _) - // result: (Const32 [0]) + // match: (RotateLeft64 x (And16 y (Const16 [c]))) + // cond: c&63 == 63 + // result: (RotateLeft64 x y) for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAnd16 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh32x64 (Rsh32x64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh32x64 x (Const64 [c+d])) + // match: (RotateLeft64 x (And8 y (Const8 [c]))) + // cond: c&63 == 63 + // result: (RotateLeft64 x y) for { - t := v.Type - if v_0.Op != OpRsh32x64 { + x := v_0 + if v_1.Op != OpAnd8 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + break + } + // match: (RotateLeft64 x (Neg64 (And64 y (Const64 [c])))) + // cond: c&63 == 63 + // result: (RotateLeft64 x (Neg64 y)) + for { + x := v_0 + if v_1.Op != OpNeg64 { break } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd64 { break } - v.reset(OpRsh32x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) - v.AddArg2(x, v0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_0_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh32x64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) - // result: (SignExt8to32 (Trunc32to8 x)) + // match: (RotateLeft64 x (Neg32 (And32 y (Const32 [c])))) + // cond: c&63 == 63 + // result: (RotateLeft64 x (Neg32 y)) for { - if v_0.Op != OpLsh32x64 { + x := v_0 + if v_1.Op != OpNeg32 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 24 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 24 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd32 { break } - v.reset(OpSignExt8to32) - v0 := b.NewValue0(v.Pos, OpTrunc32to8, typ.Int8) - v0.AddArg(x) - v.AddArg(v0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_0_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh32x64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) - // result: (SignExt16to32 (Trunc32to16 x)) + // match: (RotateLeft64 x (Neg16 (And16 y (Const16 [c])))) + // cond: c&63 == 63 + // result: (RotateLeft64 x (Neg16 y)) for { - if v_0.Op != OpLsh32x64 { + x := v_0 + if v_1.Op != OpNeg16 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 16 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 16 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd16 { break } - v.reset(OpSignExt16to32) - v0 := b.NewValue0(v.Pos, OpTrunc32to16, typ.Int16) - v0.AddArg(x) - v.AddArg(v0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_0_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh32x8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh32x8 x (Const8 [c])) - // result: (Rsh32x64 x (Const64 [int64(uint8(c))])) + // match: (RotateLeft64 x (Neg8 (And8 y (Const8 [c])))) + // cond: c&63 == 63 + // result: (RotateLeft64 x (Neg8 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst8 { + if v_1.Op != OpNeg8 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh32x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh32x8 (Const32 [0]) _) - // result: (Const32 [0]) - for { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd8 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_0_1.AuxInt) + if !(c&63 == 63) { + continue + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh64Ux16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh64Ux16 x (Const16 [c])) - // result: (Rsh64Ux64 x (Const64 [int64(uint16(c))])) + // match: (RotateLeft64 x (Add64 y (Const64 [c]))) + // cond: c&63 == 0 + // result: (RotateLeft64 x y) for { - t := v.Type x := v_0 - if v_1.Op != OpConst16 { + if v_1.Op != OpAdd64 { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) - v.AddArg2(x, v0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&63 == 0) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh64Ux16 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft64 x (Add32 y (Const32 [c]))) + // cond: c&63 == 0 + // result: (RotateLeft64 x y) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAdd32 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&63 == 0) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh64Ux32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh64Ux32 x (Const32 [c])) - // result: (Rsh64Ux64 x (Const64 [int64(uint32(c))])) + // match: (RotateLeft64 x (Add16 y (Const16 [c]))) + // cond: c&63 == 0 + // result: (RotateLeft64 x y) for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpAdd16 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) - v.AddArg2(x, v0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&63 == 0) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh64Ux32 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft64 x (Add8 y (Const8 [c]))) + // cond: c&63 == 0 + // result: (RotateLeft64 x y) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAdd8 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&63 == 0) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh64Ux64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh64Ux64 (Const64 [c]) (Const64 [d])) - // result: (Const64 [int64(uint64(c) >> uint64(d))]) + // match: (RotateLeft64 x (Sub64 (Const64 [c]) y)) + // cond: c&63 == 0 + // result: (RotateLeft64 x (Neg64 y)) for { - if v_0.Op != OpConst64 { + x := v_0 + if v_1.Op != OpSub64 { break } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst64 { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(int64(uint64(c) >> uint64(d))) + c := auxIntToInt64(v_1_0.AuxInt) + if !(c&63 == 0) { + break + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 x (Const64 [0])) - // result: x + // match: (RotateLeft64 x (Sub32 (Const32 [c]) y)) + // cond: c&63 == 0 + // result: (RotateLeft64 x (Neg32 y)) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpSub32 { break } - v.copyOf(x) + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst32 { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + if !(c&63 == 0) { + break + } + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft64 x (Sub16 (Const16 [c]) y)) + // cond: c&63 == 0 + // result: (RotateLeft64 x (Neg16 y)) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpSub16 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true - } - // match: (Rsh64Ux64 _ (Const64 [c])) - // cond: uint64(c) >= 64 - // result: (Const64 [0]) - for { - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst16 { break } - c := auxIntToInt64(v_1.AuxInt) - if !(uint64(c) >= 64) { + c := auxIntToInt16(v_1_0.AuxInt) + if !(c&63 == 0) { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Rsh64Ux64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh64Ux64 x (Const64 [c+d])) + // match: (RotateLeft64 x (Sub8 (Const8 [c]) y)) + // cond: c&63 == 0 + // result: (RotateLeft64 x (Neg8 y)) for { - t := v.Type - if v_0.Op != OpRsh64Ux64 { - break - } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + x := v_0 + if v_1.Op != OpSub8 { break } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst8 { break } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + c := auxIntToInt8(v_1_0.AuxInt) + if !(c&63 == 0) { break } - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Rsh64x64 x _) (Const64 [63])) - // result: (Rsh64Ux64 x (Const64 [63])) + // match: (RotateLeft64 x (Const64 [c])) + // cond: config.PtrSize == 4 + // result: (RotateLeft64 x (Const32 [int32(c)])) for { - if v_0.Op != OpRsh64x64 { - break - } - x := v_0.Args[0] + x := v_0 if v_1.Op != OpConst64 { break } t := v_1.Type - if auxIntToInt64(v_1.AuxInt) != 63 { + c := auxIntToInt64(v_1.AuxInt) + if !(config.PtrSize == 4) { break } - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(63) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(int32(c)) v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Lsh64x64 (Rsh64Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) - // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) - // result: (Rsh64Ux64 x (Const64 [c1-c2+c3])) + // match: (RotateLeft64 (RotateLeft64 x c) d) + // cond: c.Type.Size() == 8 && d.Type.Size() == 8 + // result: (RotateLeft64 x (Add64 c d)) for { - if v_0.Op != OpLsh64x64 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpRsh64Ux64 { - break - } - _ = v_0_0.Args[1] - x := v_0_0.Args[0] - v_0_0_1 := v_0_0.Args[1] - if v_0_0_1.Op != OpConst64 { - break - } - c1 := auxIntToInt64(v_0_0_1.AuxInt) - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { - break - } - c2 := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + if v_0.Op != OpRotateLeft64 { break } - c3 := auxIntToInt64(v_1.AuxInt) - if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 8 && d.Type.Size() == 8) { break } - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) - v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpAdd64, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) - // result: (ZeroExt8to64 (Trunc64to8 x)) + // match: (RotateLeft64 (RotateLeft64 x c) d) + // cond: c.Type.Size() == 4 && d.Type.Size() == 4 + // result: (RotateLeft64 x (Add32 c d)) for { - if v_0.Op != OpLsh64x64 { + if v_0.Op != OpRotateLeft64 { break } - _ = v_0.Args[1] + c := v_0.Args[1] x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 56 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 56 { + d := v_1 + if !(c.Type.Size() == 4 && d.Type.Size() == 4) { break } - v.reset(OpZeroExt8to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to8, typ.UInt8) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpAdd32, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) - // result: (ZeroExt16to64 (Trunc64to16 x)) + // match: (RotateLeft64 (RotateLeft64 x c) d) + // cond: c.Type.Size() == 2 && d.Type.Size() == 2 + // result: (RotateLeft64 x (Add16 c d)) for { - if v_0.Op != OpLsh64x64 { + if v_0.Op != OpRotateLeft64 { break } - _ = v_0.Args[1] + c := v_0.Args[1] x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 48 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 48 { + d := v_1 + if !(c.Type.Size() == 2 && d.Type.Size() == 2) { break } - v.reset(OpZeroExt16to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to16, typ.UInt16) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpAdd16, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } - // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) - // result: (ZeroExt32to64 (Trunc64to32 x)) + // match: (RotateLeft64 (RotateLeft64 x c) d) + // cond: c.Type.Size() == 1 && d.Type.Size() == 1 + // result: (RotateLeft64 x (Add8 c d)) for { - if v_0.Op != OpLsh64x64 { + if v_0.Op != OpRotateLeft64 { break } - _ = v_0.Args[1] + c := v_0.Args[1] x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 32 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 32 { + d := v_1 + if !(c.Type.Size() == 1 && d.Type.Size() == 1) { break } - v.reset(OpZeroExt32to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to32, typ.UInt32) - v0.AddArg(x) - v.AddArg(v0) + v.reset(OpRotateLeft64) + v0 := b.NewValue0(v.Pos, OpAdd8, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } return false } -func rewriteValuegeneric_OpRsh64Ux8(v *Value) bool { +func rewriteValuegeneric_OpRotateLeft8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (Rsh64Ux8 x (Const8 [c])) - // result: (Rsh64Ux64 x (Const64 [int64(uint8(c))])) + config := b.Func.Config + // match: (RotateLeft8 x (Const8 [c])) + // cond: c%8 == 0 + // result: x for { - t := v.Type x := v_0 if v_1.Op != OpConst8 { break } c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh64Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh64Ux8 (Const64 [0]) _) - // result: (Const64 [0]) - for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + if !(c%8 == 0) { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) + v.copyOf(x) return true } - return false -} -func rewriteValuegeneric_OpRsh64x16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh64x16 x (Const16 [c])) - // result: (Rsh64x64 x (Const64 [int64(uint16(c))])) + // match: (RotateLeft8 x (And64 y (Const64 [c]))) + // cond: c&7 == 7 + // result: (RotateLeft8 x y) for { - t := v.Type x := v_0 - if v_1.Op != OpConst16 { + if v_1.Op != OpAnd64 { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh64x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) - v.AddArg2(x, v0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh64x16 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft8 x (And32 y (Const32 [c]))) + // cond: c&7 == 7 + // result: (RotateLeft8 x y) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAnd32 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh64x32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh64x32 x (Const32 [c])) - // result: (Rsh64x64 x (Const64 [int64(uint32(c))])) + // match: (RotateLeft8 x (And16 y (Const16 [c]))) + // cond: c&7 == 7 + // result: (RotateLeft8 x y) for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpAnd16 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh64x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) - v.AddArg2(x, v0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh64x32 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft8 x (And8 y (Const8 [c]))) + // cond: c&7 == 7 + // result: (RotateLeft8 x y) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpAnd8 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break } - return false -} -func rewriteValuegeneric_OpRsh64x64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh64x64 (Const64 [c]) (Const64 [d])) - // result: (Const64 [c >> uint64(d)]) + // match: (RotateLeft8 x (Neg64 (And64 y (Const64 [c])))) + // cond: c&7 == 7 + // result: (RotateLeft8 x (Neg64 y)) for { - if v_0.Op != OpConst64 { + x := v_0 + if v_1.Op != OpNeg64 { break } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpConst64 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd64 { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(c >> uint64(d)) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_0_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh64x64 x (Const64 [0])) - // result: x + // match: (RotateLeft8 x (Neg32 (And32 y (Const32 [c])))) + // cond: c&7 == 7 + // result: (RotateLeft8 x (Neg32 y)) for { x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_1.Op != OpNeg32 { break } - v.copyOf(x) - return true - } - // match: (Rsh64x64 (Const64 [0]) _) - // result: (Const64 [0]) - for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd32 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_0_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh64x64 (Rsh64x64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh64x64 x (Const64 [c+d])) + // match: (RotateLeft8 x (Neg16 (And16 y (Const16 [c])))) + // cond: c&7 == 7 + // result: (RotateLeft8 x (Neg16 y)) for { - t := v.Type - if v_0.Op != OpRsh64x64 { + x := v_0 + if v_1.Op != OpNeg16 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd16 { break } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_0_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break + } + // match: (RotateLeft8 x (Neg8 (And8 y (Const8 [c])))) + // cond: c&7 == 7 + // result: (RotateLeft8 x (Neg8 y)) + for { + x := v_0 + if v_1.Op != OpNeg8 { break } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpAnd8 { break } - v.reset(OpRsh64x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) - v.AddArg2(x, v0) - return true + _ = v_1_0.Args[1] + v_1_0_0 := v_1_0.Args[0] + v_1_0_1 := v_1_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 { + y := v_1_0_0 + if v_1_0_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_0_1.AuxInt) + if !(c&7 == 7) { + continue + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) + return true + } + break } - // match: (Rsh64x64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) - // result: (SignExt8to64 (Trunc64to8 x)) + // match: (RotateLeft8 x (Add64 y (Const64 [c]))) + // cond: c&7 == 0 + // result: (RotateLeft8 x y) for { - if v_0.Op != OpLsh64x64 { + x := v_0 + if v_1.Op != OpAdd64 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 56 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 56 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_1_1.AuxInt) + if !(c&7 == 0) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true } - v.reset(OpSignExt8to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to8, typ.Int8) - v0.AddArg(x) - v.AddArg(v0) - return true + break } - // match: (Rsh64x64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) - // result: (SignExt16to64 (Trunc64to16 x)) + // match: (RotateLeft8 x (Add32 y (Const32 [c]))) + // cond: c&7 == 0 + // result: (RotateLeft8 x y) for { - if v_0.Op != OpLsh64x64 { + x := v_0 + if v_1.Op != OpAdd32 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 48 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 48 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_1_1.AuxInt) + if !(c&7 == 0) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true } - v.reset(OpSignExt16to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to16, typ.Int16) - v0.AddArg(x) - v.AddArg(v0) - return true + break } - // match: (Rsh64x64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) - // result: (SignExt32to64 (Trunc64to32 x)) + // match: (RotateLeft8 x (Add16 y (Const16 [c]))) + // cond: c&7 == 0 + // result: (RotateLeft8 x y) for { - if v_0.Op != OpLsh64x64 { + x := v_0 + if v_1.Op != OpAdd16 { break } - _ = v_0.Args[1] - x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 32 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 32 { - break + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_1_1.AuxInt) + if !(c&7 == 0) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true } - v.reset(OpSignExt32to64) - v0 := b.NewValue0(v.Pos, OpTrunc64to32, typ.Int32) - v0.AddArg(x) - v.AddArg(v0) - return true + break } - return false -} -func rewriteValuegeneric_OpRsh64x8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh64x8 x (Const8 [c])) - // result: (Rsh64x64 x (Const64 [int64(uint8(c))])) + // match: (RotateLeft8 x (Add8 y (Const8 [c]))) + // cond: c&7 == 0 + // result: (RotateLeft8 x y) for { - t := v.Type x := v_0 - if v_1.Op != OpConst8 { + if v_1.Op != OpAdd8 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh64x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) - return true + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + y := v_1_0 + if v_1_1.Op != OpConst8 { + continue + } + c := auxIntToInt8(v_1_1.AuxInt) + if !(c&7 == 0) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break } - // match: (Rsh64x8 (Const64 [0]) _) - // result: (Const64 [0]) + // match: (RotateLeft8 x (Sub64 (Const64 [c]) y)) + // cond: c&7 == 0 + // result: (RotateLeft8 x (Neg64 y)) for { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpSub64 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst64 { + break + } + c := auxIntToInt64(v_1_0.AuxInt) + if !(c&7 == 0) { + break + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg64, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpRsh8Ux16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh8Ux16 x (Const16 [c])) - // result: (Rsh8Ux64 x (Const64 [int64(uint16(c))])) + // match: (RotateLeft8 x (Sub32 (Const32 [c]) y)) + // cond: c&7 == 0 + // result: (RotateLeft8 x (Neg32 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst16 { + if v_1.Op != OpSub32 { break } - c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst32 { + break + } + c := auxIntToInt32(v_1_0.AuxInt) + if !(c&7 == 0) { + break + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg32, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh8Ux16 (Const8 [0]) _) - // result: (Const8 [0]) + // match: (RotateLeft8 x (Sub16 (Const16 [c]) y)) + // cond: c&7 == 0 + // result: (RotateLeft8 x (Neg16 y)) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpSub16 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst16 { + break + } + c := auxIntToInt16(v_1_0.AuxInt) + if !(c&7 == 0) { + break + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg16, y.Type) + v0.AddArg(y) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpRsh8Ux32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh8Ux32 x (Const32 [c])) - // result: (Rsh8Ux64 x (Const64 [int64(uint32(c))])) + // match: (RotateLeft8 x (Sub8 (Const8 [c]) y)) + // cond: c&7 == 0 + // result: (RotateLeft8 x (Neg8 y)) for { - t := v.Type x := v_0 - if v_1.Op != OpConst32 { + if v_1.Op != OpSub8 { break } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + y := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst8 { + break + } + c := auxIntToInt8(v_1_0.AuxInt) + if !(c&7 == 0) { + break + } + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpNeg8, y.Type) + v0.AddArg(y) v.AddArg2(x, v0) return true } - // match: (Rsh8Ux32 (Const8 [0]) _) - // result: (Const8 [0]) + // match: (RotateLeft8 x (Const64 [c])) + // cond: config.PtrSize == 4 + // result: (RotateLeft8 x (Const32 [int32(c)])) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpConst64 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) - return true - } - return false -} -func rewriteValuegeneric_OpRsh8Ux64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - typ := &b.Func.Config.Types - // match: (Rsh8Ux64 (Const8 [c]) (Const64 [d])) - // result: (Const8 [int8(uint8(c) >> uint64(d))]) - for { - if v_0.Op != OpConst8 { - break - } - c := auxIntToInt8(v_0.AuxInt) - if v_1.Op != OpConst64 { + t := v_1.Type + c := auxIntToInt64(v_1.AuxInt) + if !(config.PtrSize == 4) { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(int8(uint8(c) >> uint64(d))) + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(int32(c)) + v.AddArg2(x, v0) return true } - // match: (Rsh8Ux64 x (Const64 [0])) - // result: x + // match: (RotateLeft8 (RotateLeft8 x c) d) + // cond: c.Type.Size() == 8 && d.Type.Size() == 8 + // result: (RotateLeft8 x (Add64 c d)) for { - x := v_0 - if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { + if v_0.Op != OpRotateLeft8 { break } - v.copyOf(x) - return true - } - // match: (Rsh8Ux64 (Const8 [0]) _) - // result: (Const8 [0]) - for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 8 && d.Type.Size() == 8) { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpAdd64, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } - // match: (Rsh8Ux64 _ (Const64 [c])) - // cond: uint64(c) >= 8 - // result: (Const8 [0]) + // match: (RotateLeft8 (RotateLeft8 x c) d) + // cond: c.Type.Size() == 4 && d.Type.Size() == 4 + // result: (RotateLeft8 x (Add32 c d)) for { - if v_1.Op != OpConst64 { + if v_0.Op != OpRotateLeft8 { break } - c := auxIntToInt64(v_1.AuxInt) - if !(uint64(c) >= 8) { + c := v_0.Args[1] + x := v_0.Args[0] + d := v_1 + if !(c.Type.Size() == 4 && d.Type.Size() == 4) { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpAdd32, c.Type) + v0.AddArg2(c, d) + v.AddArg2(x, v0) return true } - // match: (Rsh8Ux64 (Rsh8Ux64 x (Const64 [c])) (Const64 [d])) - // cond: !uaddOvf(c,d) - // result: (Rsh8Ux64 x (Const64 [c+d])) + // match: (RotateLeft8 (RotateLeft8 x c) d) + // cond: c.Type.Size() == 2 && d.Type.Size() == 2 + // result: (RotateLeft8 x (Add16 c d)) for { - t := v.Type - if v_0.Op != OpRsh8Ux64 { + if v_0.Op != OpRotateLeft8 { break } - _ = v_0.Args[1] + c := v_0.Args[1] x := v_0.Args[0] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { - break - } - c := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { - break - } - d := auxIntToInt64(v_1.AuxInt) - if !(!uaddOvf(c, d)) { + d := v_1 + if !(c.Type.Size() == 2 && d.Type.Size() == 2) { break } - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c + d) + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpAdd16, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh8Ux64 (Rsh8x64 x _) (Const64 [7] )) - // result: (Rsh8Ux64 x (Const64 [7] )) + // match: (RotateLeft8 (RotateLeft8 x c) d) + // cond: c.Type.Size() == 1 && d.Type.Size() == 1 + // result: (RotateLeft8 x (Add8 c d)) for { - if v_0.Op != OpRsh8x64 { + if v_0.Op != OpRotateLeft8 { break } + c := v_0.Args[1] x := v_0.Args[0] - if v_1.Op != OpConst64 { + d := v_1 + if !(c.Type.Size() == 1 && d.Type.Size() == 1) { break } - t := v_1.Type - if auxIntToInt64(v_1.AuxInt) != 7 { - break - } - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(7) + v.reset(OpRotateLeft8) + v0 := b.NewValue0(v.Pos, OpAdd8, c.Type) + v0.AddArg2(c, d) v.AddArg2(x, v0) return true } - // match: (Rsh8Ux64 (Lsh8x64 (Rsh8Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) - // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) - // result: (Rsh8Ux64 x (Const64 [c1-c2+c3])) + return false +} +func rewriteValuegeneric_OpRound32F(v *Value) bool { + v_0 := v.Args[0] + // match: (Round32F x:(Const32F)) + // result: x for { - if v_0.Op != OpLsh8x64 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpRsh8Ux64 { - break - } - _ = v_0_0.Args[1] - x := v_0_0.Args[0] - v_0_0_1 := v_0_0.Args[1] - if v_0_0_1.Op != OpConst64 { - break - } - c1 := auxIntToInt64(v_0_0_1.AuxInt) - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { - break - } - c2 := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 { - break - } - c3 := auxIntToInt64(v_1.AuxInt) - if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + x := v_0 + if x.Op != OpConst32F { break } - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) - v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) - v.AddArg2(x, v0) + v.copyOf(x) return true } return false } -func rewriteValuegeneric_OpRsh8Ux8(v *Value) bool { - v_1 := v.Args[1] +func rewriteValuegeneric_OpRound64F(v *Value) bool { v_0 := v.Args[0] - b := v.Block - // match: (Rsh8Ux8 x (Const8 [c])) - // result: (Rsh8Ux64 x (Const64 [int64(uint8(c))])) + // match: (Round64F x:(Const64F)) + // result: x for { - t := v.Type x := v_0 - if v_1.Op != OpConst8 { + if x.Op != OpConst64F { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh8Ux64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) + v.copyOf(x) return true } - // match: (Rsh8Ux8 (Const8 [0]) _) - // result: (Const8 [0]) + return false +} +func rewriteValuegeneric_OpRoundToEven(v *Value) bool { + v_0 := v.Args[0] + // match: (RoundToEven (Const64F [c])) + // result: (Const64F [math.RoundToEven(c)]) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + if v_0.Op != OpConst64F { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + c := auxIntToFloat64(v_0.AuxInt) + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(math.RoundToEven(c)) return true } return false } -func rewriteValuegeneric_OpRsh8x16(v *Value) bool { +func rewriteValuegeneric_OpRsh16Ux16(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (Rsh8x16 x (Const16 [c])) - // result: (Rsh8x64 x (Const64 [int64(uint16(c))])) + // match: (Rsh16Ux16 x (Const16 [c])) + // result: (Rsh16Ux64 x (Const64 [int64(uint16(c))])) for { t := v.Type x := v_0 @@ -20485,30 +24265,30 @@ func rewriteValuegeneric_OpRsh8x16(v *Value) bool { break } c := auxIntToInt16(v_1.AuxInt) - v.reset(OpRsh8x64) + v.reset(OpRsh16Ux64) v0 := b.NewValue0(v.Pos, OpConst64, t) v0.AuxInt = int64ToAuxInt(int64(uint16(c))) v.AddArg2(x, v0) return true } - // match: (Rsh8x16 (Const8 [0]) _) - // result: (Const8 [0]) + // match: (Rsh16Ux16 (Const16 [0]) _) + // result: (Const16 [0]) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpRsh8x32(v *Value) bool { +func rewriteValuegeneric_OpRsh16Ux32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (Rsh8x32 x (Const32 [c])) - // result: (Rsh8x64 x (Const64 [int64(uint32(c))])) + // match: (Rsh16Ux32 x (Const32 [c])) + // result: (Rsh16Ux64 x (Const64 [int64(uint32(c))])) for { t := v.Type x := v_0 @@ -20516,44 +24296,45 @@ func rewriteValuegeneric_OpRsh8x32(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) - v.reset(OpRsh8x64) + v.reset(OpRsh16Ux64) v0 := b.NewValue0(v.Pos, OpConst64, t) v0.AuxInt = int64ToAuxInt(int64(uint32(c))) v.AddArg2(x, v0) return true } - // match: (Rsh8x32 (Const8 [0]) _) - // result: (Const8 [0]) + // match: (Rsh16Ux32 (Const16 [0]) _) + // result: (Const16 [0]) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpRsh8x64(v *Value) bool { +func rewriteValuegeneric_OpRsh16Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (Rsh8x64 (Const8 [c]) (Const64 [d])) - // result: (Const8 [c >> uint64(d)]) + typ := &b.Func.Config.Types + // match: (Rsh16Ux64 (Const16 [c]) (Const64 [d])) + // result: (Const16 [int16(uint16(c) >> uint64(d))]) for { - if v_0.Op != OpConst8 { + if v_0.Op != OpConst16 { break } - c := auxIntToInt8(v_0.AuxInt) + c := auxIntToInt16(v_0.AuxInt) if v_1.Op != OpConst64 { break } d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(c >> uint64(d)) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(int16(uint16(c) >> uint64(d))) return true } - // match: (Rsh8x64 x (Const64 [0])) + // match: (Rsh16Ux64 x (Const64 [0])) // result: x for { x := v_0 @@ -20563,22 +24344,37 @@ func rewriteValuegeneric_OpRsh8x64(v *Value) bool { v.copyOf(x) return true } - // match: (Rsh8x64 (Const8 [0]) _) - // result: (Const8 [0]) + // match: (Rsh16Ux64 (Const16 [0]) _) + // result: (Const16 [0]) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) return true } - // match: (Rsh8x64 (Rsh8x64 x (Const64 [c])) (Const64 [d])) + // match: (Rsh16Ux64 _ (Const64 [c])) + // cond: uint64(c) >= 16 + // result: (Const16 [0]) + for { + if v_1.Op != OpConst64 { + break + } + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 16) { + break + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + // match: (Rsh16Ux64 (Rsh16Ux64 x (Const64 [c])) (Const64 [d])) // cond: !uaddOvf(c,d) - // result: (Rsh8x64 x (Const64 [c+d])) + // result: (Rsh16Ux64 x (Const64 [c+d])) for { t := v.Type - if v_0.Op != OpRsh8x64 { + if v_0.Op != OpRsh16Ux64 { break } _ = v_0.Args[1] @@ -20595,756 +24391,808 @@ func rewriteValuegeneric_OpRsh8x64(v *Value) bool { if !(!uaddOvf(c, d)) { break } - v.reset(OpRsh8x64) + v.reset(OpRsh16Ux64) v0 := b.NewValue0(v.Pos, OpConst64, t) v0.AuxInt = int64ToAuxInt(c + d) v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpRsh8x8(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Rsh8x8 x (Const8 [c])) - // result: (Rsh8x64 x (Const64 [int64(uint8(c))])) + // match: (Rsh16Ux64 (Rsh16x64 x _) (Const64 [15])) + // result: (Rsh16Ux64 x (Const64 [15])) for { - t := v.Type - x := v_0 - if v_1.Op != OpConst8 { + if v_0.Op != OpRsh16x64 { break } - c := auxIntToInt8(v_1.AuxInt) - v.reset(OpRsh8x64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(int64(uint8(c))) - v.AddArg2(x, v0) - return true - } - // match: (Rsh8x8 (Const8 [0]) _) - // result: (Const8 [0]) + x := v_0.Args[0] + if v_1.Op != OpConst64 { + break + } + t := v_1.Type + if auxIntToInt64(v_1.AuxInt) != 15 { + break + } + v.reset(OpRsh16Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(15) + v.AddArg2(x, v0) + return true + } + // match: (Rsh16Ux64 i:(Lsh16x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 16 && i.Uses == 1 + // result: (And16 x (Const16 [int16(^uint16(0)>>c)])) for { - if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { + i := v_0 + if i.Op != OpLsh16x64 { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { + break + } + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 16 && i.Uses == 1) { + break + } + v.reset(OpAnd16) + v0 := b.NewValue0(v.Pos, OpConst16, v.Type) + v0.AuxInt = int16ToAuxInt(int16(^uint16(0) >> c)) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpSelect0(v *Value) bool { - v_0 := v.Args[0] - // match: (Select0 (Div128u (Const64 [0]) lo y)) - // result: (Div64u lo y) + // match: (Rsh16Ux64 (Lsh16x64 (Rsh16Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) + // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) + // result: (Rsh16Ux64 x (Const64 [c1-c2+c3])) for { - if v_0.Op != OpDiv128u { + if v_0.Op != OpLsh16x64 { break } - y := v_0.Args[2] + _ = v_0.Args[1] v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { + if v_0_0.Op != OpRsh16Ux64 { break } - lo := v_0.Args[1] - v.reset(OpDiv64u) - v.AddArg2(lo, y) + _ = v_0_0.Args[1] + x := v_0_0.Args[0] + v_0_0_1 := v_0_0.Args[1] + if v_0_0_1.Op != OpConst64 { + break + } + c1 := auxIntToInt64(v_0_0_1.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { + break + } + c2 := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { + break + } + c3 := auxIntToInt64(v_1.AuxInt) + if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + break + } + v.reset(OpRsh16Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpSelect1(v *Value) bool { - v_0 := v.Args[0] - // match: (Select1 (Div128u (Const64 [0]) lo y)) - // result: (Mod64u lo y) + // match: (Rsh16Ux64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) + // result: (ZeroExt8to16 (Trunc16to8 x)) for { - if v_0.Op != OpDiv128u { + if v_0.Op != OpLsh16x64 { break } - y := v_0.Args[2] - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 8 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 8 { break } - lo := v_0.Args[1] - v.reset(OpMod64u) - v.AddArg2(lo, y) + v.reset(OpZeroExt8to16) + v0 := b.NewValue0(v.Pos, OpTrunc16to8, typ.UInt8) + v0.AddArg(x) + v.AddArg(v0) return true } return false } -func rewriteValuegeneric_OpSelectN(v *Value) bool { +func rewriteValuegeneric_OpRsh16Ux8(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - config := b.Func.Config - // match: (SelectN [0] (MakeResult x ___)) - // result: x + // match: (Rsh16Ux8 x (Const8 [c])) + // result: (Rsh16Ux64 x (Const64 [int64(uint8(c))])) for { - if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpMakeResult || len(v_0.Args) < 1 { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - x := v_0.Args[0] - v.copyOf(x) + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh16Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) return true } - // match: (SelectN [1] (MakeResult x y ___)) - // result: y + // match: (Rsh16Ux8 (Const16 [0]) _) + // result: (Const16 [0]) for { - if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpMakeResult || len(v_0.Args) < 2 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - y := v_0.Args[1] - v.copyOf(y) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) return true } - // match: (SelectN [2] (MakeResult x y z ___)) - // result: z + return false +} +func rewriteValuegeneric_OpRsh16x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh16x16 x (Const16 [c])) + // result: (Rsh16x64 x (Const64 [int64(uint16(c))])) for { - if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpMakeResult || len(v_0.Args) < 3 { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - z := v_0.Args[2] - v.copyOf(z) + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh16x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) return true } - // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))) - // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call) - // result: (Move {t.Elem()} [int64(sz)] dst src mem) + // match: (Rsh16x16 (Const16 [0]) _) + // result: (Const16 [0]) for { - if auxIntToInt64(v.AuxInt) != 0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - call := v_0 - if call.Op != OpStaticCall || len(call.Args) != 1 { + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh16x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh16x32 x (Const32 [c])) + // result: (Rsh16x64 x (Const64 [int64(uint32(c))])) + for { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - sym := auxToCall(call.Aux) - s1 := call.Args[0] - if s1.Op != OpStore { + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh16x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh16x32 (Const16 [0]) _) + // result: (Const16 [0]) + for { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - _ = s1.Args[2] - s1_1 := s1.Args[1] - if s1_1.Op != OpConst64 { + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh16x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh16x64 (Const16 [c]) (Const64 [d])) + // result: (Const16 [c >> uint64(d)]) + for { + if v_0.Op != OpConst16 { break } - sz := auxIntToInt64(s1_1.AuxInt) - s2 := s1.Args[2] - if s2.Op != OpStore { + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - _ = s2.Args[2] - src := s2.Args[1] - s3 := s2.Args[2] - if s3.Op != OpStore { + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(c >> uint64(d)) + return true + } + // match: (Rsh16x64 x (Const64 [0])) + // result: x + for { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - t := auxToType(s3.Aux) - mem := s3.Args[2] - dst := s3.Args[1] - if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) { + v.copyOf(x) + return true + } + // match: (Rsh16x64 (Const16 [0]) _) + // result: (Const16 [0]) + for { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(t.Elem()) - v.AddArg3(dst, src, mem) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) return true } - // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))) - // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call) - // result: (Move {t.Elem()} [int64(sz)] dst src mem) + // match: (Rsh16x64 (Rsh16x64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh16x64 x (Const64 [c+d])) for { - if auxIntToInt64(v.AuxInt) != 0 { + t := v.Type + if v_0.Op != OpRsh16x64 { break } - call := v_0 - if call.Op != OpStaticCall || len(call.Args) != 1 { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - sym := auxToCall(call.Aux) - s1 := call.Args[0] - if s1.Op != OpStore { + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - _ = s1.Args[2] - s1_1 := s1.Args[1] - if s1_1.Op != OpConst32 { - break - } - sz := auxIntToInt32(s1_1.AuxInt) - s2 := s1.Args[2] - if s2.Op != OpStore { + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { break } - _ = s2.Args[2] - src := s2.Args[1] - s3 := s2.Args[2] - if s3.Op != OpStore { + v.reset(OpRsh16x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) + return true + } + // match: (Rsh16x64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) + // result: (SignExt8to16 (Trunc16to8 x)) + for { + if v_0.Op != OpLsh16x64 { break } - t := auxToType(s3.Aux) - mem := s3.Args[2] - dst := s3.Args[1] - if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 8 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 8 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(t.Elem()) - v.AddArg3(dst, src, mem) + v.reset(OpSignExt8to16) + v0 := b.NewValue0(v.Pos, OpTrunc16to8, typ.Int8) + v0.AddArg(x) + v.AddArg(v0) return true } - // match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem)) - // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) - // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) + return false +} +func rewriteValuegeneric_OpRsh16x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh16x8 x (Const8 [c])) + // result: (Rsh16x64 x (Const64 [int64(uint8(c))])) for { - if auxIntToInt64(v.AuxInt) != 0 { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - call := v_0 - if call.Op != OpStaticCall || len(call.Args) != 4 { + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh16x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh16x8 (Const16 [0]) _) + // result: (Const16 [0]) + for { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { break } - sym := auxToCall(call.Aux) - mem := call.Args[3] - dst := call.Args[0] - src := call.Args[1] - call_2 := call.Args[2] - if call_2.Op != OpConst64 { + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh32Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh32Ux16 x (Const16 [c])) + // result: (Rsh32Ux64 x (Const64 [int64(uint16(c))])) + for { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - sz := auxIntToInt64(call_2.AuxInt) - if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh32Ux16 (Const32 [0]) _) + // result: (Const32 [0]) + for { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(dst.Type.Elem()) - v.AddArg3(dst, src, mem) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem)) - // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) - // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) + return false +} +func rewriteValuegeneric_OpRsh32Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh32Ux32 x (Const32 [c])) + // result: (Rsh32Ux64 x (Const64 [int64(uint32(c))])) for { - if auxIntToInt64(v.AuxInt) != 0 { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - call := v_0 - if call.Op != OpStaticCall || len(call.Args) != 4 { + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh32Ux32 (Const32 [0]) _) + // result: (Const32 [0]) + for { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - sym := auxToCall(call.Aux) - mem := call.Args[3] - dst := call.Args[0] - src := call.Args[1] - call_2 := call.Args[2] - if call_2.Op != OpConst32 { + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh32Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32Ux64 (Const32 [c]) (Const64 [d])) + // result: (Const32 [int32(uint32(c) >> uint64(d))]) + for { + if v_0.Op != OpConst32 { break } - sz := auxIntToInt32(call_2.AuxInt) - if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(dst.Type.Elem()) - v.AddArg3(dst, src, mem) + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(int32(uint32(c) >> uint64(d))) return true } - // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem)) - // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) - // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) + // match: (Rsh32Ux64 x (Const64 [0])) + // result: x for { - if auxIntToInt64(v.AuxInt) != 0 { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - call := v_0 - if call.Op != OpStaticLECall || len(call.Args) != 4 { + v.copyOf(x) + return true + } + // match: (Rsh32Ux64 (Const32 [0]) _) + // result: (Const32 [0]) + for { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - sym := auxToCall(call.Aux) - mem := call.Args[3] - dst := call.Args[0] - src := call.Args[1] - call_2 := call.Args[2] - if call_2.Op != OpConst64 { + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (Rsh32Ux64 _ (Const64 [c])) + // cond: uint64(c) >= 32 + // result: (Const32 [0]) + for { + if v_1.Op != OpConst64 { break } - sz := auxIntToInt64(call_2.AuxInt) - if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 32) { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(dst.Type.Elem()) - v.AddArg3(dst, src, mem) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem)) - // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) - // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) + // match: (Rsh32Ux64 (Rsh32Ux64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh32Ux64 x (Const64 [c+d])) for { - if auxIntToInt64(v.AuxInt) != 0 { + t := v.Type + if v_0.Op != OpRsh32Ux64 { break } - call := v_0 - if call.Op != OpStaticLECall || len(call.Args) != 4 { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - sym := auxToCall(call.Aux) - mem := call.Args[3] - dst := call.Args[0] - src := call.Args[1] - call_2 := call.Args[2] - if call_2.Op != OpConst32 { + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - sz := auxIntToInt32(call_2.AuxInt) - if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(int64(sz)) - v.Aux = typeToAux(dst.Type.Elem()) - v.AddArg3(dst, src, mem) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) return true } - // match: (SelectN [0] call:(StaticLECall {sym} a x)) - // cond: needRaceCleanup(sym, call) && clobber(call) - // result: x + // match: (Rsh32Ux64 (Rsh32x64 x _) (Const64 [31])) + // result: (Rsh32Ux64 x (Const64 [31])) for { - if auxIntToInt64(v.AuxInt) != 0 { + if v_0.Op != OpRsh32x64 { break } - call := v_0 - if call.Op != OpStaticLECall || len(call.Args) != 2 { + x := v_0.Args[0] + if v_1.Op != OpConst64 { break } - sym := auxToCall(call.Aux) - x := call.Args[1] - if !(needRaceCleanup(sym, call) && clobber(call)) { + t := v_1.Type + if auxIntToInt64(v_1.AuxInt) != 31 { break } - v.copyOf(x) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(31) + v.AddArg2(x, v0) return true } - // match: (SelectN [0] call:(StaticLECall {sym} x)) - // cond: needRaceCleanup(sym, call) && clobber(call) - // result: x + // match: (Rsh32Ux64 i:(Lsh32x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 32 && i.Uses == 1 + // result: (And32 x (Const32 [int32(^uint32(0)>>c)])) for { - if auxIntToInt64(v.AuxInt) != 0 { + i := v_0 + if i.Op != OpLsh32x64 { break } - call := v_0 - if call.Op != OpStaticLECall || len(call.Args) != 1 { + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { break } - sym := auxToCall(call.Aux) - x := call.Args[0] - if !(needRaceCleanup(sym, call) && clobber(call)) { + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 32 && i.Uses == 1) { break } - v.copyOf(x) + v.reset(OpAnd32) + v0 := b.NewValue0(v.Pos, OpConst32, v.Type) + v0.AuxInt = int32ToAuxInt(int32(^uint32(0) >> c)) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpSignExt16to32(v *Value) bool { - v_0 := v.Args[0] - // match: (SignExt16to32 (Const16 [c])) - // result: (Const32 [int32(c)]) + // match: (Rsh32Ux64 (Lsh32x64 (Rsh32Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) + // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) + // result: (Rsh32Ux64 x (Const64 [c1-c2+c3])) for { - if v_0.Op != OpConst16 { + if v_0.Op != OpLsh32x64 { break } - c := auxIntToInt16(v_0.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(int32(c)) - return true - } - // match: (SignExt16to32 (Trunc32to16 x:(Rsh32x64 _ (Const64 [s])))) - // cond: s >= 16 - // result: x - for { - if v_0.Op != OpTrunc32to16 { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpRsh32Ux64 { break } - x := v_0.Args[0] - if x.Op != OpRsh32x64 { + _ = v_0_0.Args[1] + x := v_0_0.Args[0] + v_0_0_1 := v_0_0.Args[1] + if v_0_0_1.Op != OpConst64 { break } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { + c1 := auxIntToInt64(v_0_0_1.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 16) { + c2 := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - v.copyOf(x) - return true - } - return false -} -func rewriteValuegeneric_OpSignExt16to64(v *Value) bool { - v_0 := v.Args[0] - // match: (SignExt16to64 (Const16 [c])) - // result: (Const64 [int64(c)]) - for { - if v_0.Op != OpConst16 { + c3 := auxIntToInt64(v_1.AuxInt) + if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { break } - c := auxIntToInt16(v_0.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(int64(c)) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.AddArg2(x, v0) return true } - // match: (SignExt16to64 (Trunc64to16 x:(Rsh64x64 _ (Const64 [s])))) - // cond: s >= 48 - // result: x + // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) + // result: (ZeroExt8to32 (Trunc32to8 x)) for { - if v_0.Op != OpTrunc64to16 { + if v_0.Op != OpLsh32x64 { break } + _ = v_0.Args[1] x := v_0.Args[0] - if x.Op != OpRsh64x64 { + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 24 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 24 { break } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { + v.reset(OpZeroExt8to32) + v0 := b.NewValue0(v.Pos, OpTrunc32to8, typ.UInt8) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) + // result: (ZeroExt16to32 (Trunc32to16 x)) + for { + if v_0.Op != OpLsh32x64 { break } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 48) { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 16 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 16 { break } - v.copyOf(x) + v.reset(OpZeroExt16to32) + v0 := b.NewValue0(v.Pos, OpTrunc32to16, typ.UInt16) + v0.AddArg(x) + v.AddArg(v0) return true } return false } -func rewriteValuegeneric_OpSignExt32to64(v *Value) bool { +func rewriteValuegeneric_OpRsh32Ux8(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SignExt32to64 (Const32 [c])) - // result: (Const64 [int64(c)]) + b := v.Block + // match: (Rsh32Ux8 x (Const8 [c])) + // result: (Rsh32Ux64 x (Const64 [int64(uint8(c))])) for { - if v_0.Op != OpConst32 { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - c := auxIntToInt32(v_0.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(int64(c)) + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh32Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) return true } - // match: (SignExt32to64 (Trunc64to32 x:(Rsh64x64 _ (Const64 [s])))) - // cond: s >= 32 - // result: x + // match: (Rsh32Ux8 (Const32 [0]) _) + // result: (Const32 [0]) for { - if v_0.Op != OpTrunc64to32 { - break - } - x := v_0.Args[0] - if x.Op != OpRsh64x64 { - break - } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { - break - } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 32) { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - v.copyOf(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpSignExt8to16(v *Value) bool { +func rewriteValuegeneric_OpRsh32x16(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SignExt8to16 (Const8 [c])) - // result: (Const16 [int16(c)]) + b := v.Block + // match: (Rsh32x16 x (Const16 [c])) + // result: (Rsh32x64 x (Const64 [int64(uint16(c))])) for { - if v_0.Op != OpConst8 { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - c := auxIntToInt8(v_0.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(int16(c)) + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh32x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) return true } - // match: (SignExt8to16 (Trunc16to8 x:(Rsh16x64 _ (Const64 [s])))) - // cond: s >= 8 - // result: x + // match: (Rsh32x16 (Const32 [0]) _) + // result: (Const32 [0]) for { - if v_0.Op != OpTrunc16to8 { - break - } - x := v_0.Args[0] - if x.Op != OpRsh16x64 { - break - } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { - break - } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 8) { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - v.copyOf(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpSignExt8to32(v *Value) bool { +func rewriteValuegeneric_OpRsh32x32(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SignExt8to32 (Const8 [c])) - // result: (Const32 [int32(c)]) + b := v.Block + // match: (Rsh32x32 x (Const32 [c])) + // result: (Rsh32x64 x (Const64 [int64(uint32(c))])) for { - if v_0.Op != OpConst8 { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - c := auxIntToInt8(v_0.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(int32(c)) + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh32x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) return true } - // match: (SignExt8to32 (Trunc32to8 x:(Rsh32x64 _ (Const64 [s])))) - // cond: s >= 24 - // result: x + // match: (Rsh32x32 (Const32 [0]) _) + // result: (Const32 [0]) for { - if v_0.Op != OpTrunc32to8 { - break - } - x := v_0.Args[0] - if x.Op != OpRsh32x64 { - break - } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { - break - } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 24) { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - v.copyOf(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpSignExt8to64(v *Value) bool { +func rewriteValuegeneric_OpRsh32x64(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SignExt8to64 (Const8 [c])) - // result: (Const64 [int64(c)]) + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh32x64 (Const32 [c]) (Const64 [d])) + // result: (Const32 [c >> uint64(d)]) for { - if v_0.Op != OpConst8 { + if v_0.Op != OpConst32 { break } - c := auxIntToInt8(v_0.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(int64(c)) - return true - } - // match: (SignExt8to64 (Trunc64to8 x:(Rsh64x64 _ (Const64 [s])))) - // cond: s >= 56 - // result: x - for { - if v_0.Op != OpTrunc64to8 { - break - } - x := v_0.Args[0] - if x.Op != OpRsh64x64 { - break - } - _ = x.Args[1] - x_1 := x.Args[1] - if x_1.Op != OpConst64 { - break - } - s := auxIntToInt64(x_1.AuxInt) - if !(s >= 56) { - break - } - v.copyOf(x) - return true - } - return false -} -func rewriteValuegeneric_OpSliceCap(v *Value) bool { - v_0 := v.Args[0] - // match: (SliceCap (SliceMake _ _ (Const64 [c]))) - // result: (Const64 [c]) - for { - if v_0.Op != OpSliceMake { - break - } - _ = v_0.Args[2] - v_0_2 := v_0.Args[2] - if v_0_2.Op != OpConst64 { - break - } - t := v_0_2.Type - c := auxIntToInt64(v_0_2.AuxInt) - v.reset(OpConst64) - v.Type = t - v.AuxInt = int64ToAuxInt(c) - return true - } - // match: (SliceCap (SliceMake _ _ (Const32 [c]))) - // result: (Const32 [c]) - for { - if v_0.Op != OpSliceMake { - break - } - _ = v_0.Args[2] - v_0_2 := v_0.Args[2] - if v_0_2.Op != OpConst32 { + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - t := v_0_2.Type - c := auxIntToInt32(v_0_2.AuxInt) + d := auxIntToInt64(v_1.AuxInt) v.reset(OpConst32) - v.Type = t - v.AuxInt = int32ToAuxInt(c) + v.AuxInt = int32ToAuxInt(c >> uint64(d)) return true } - // match: (SliceCap (SliceMake _ _ (SliceCap x))) - // result: (SliceCap x) + // match: (Rsh32x64 x (Const64 [0])) + // result: x for { - if v_0.Op != OpSliceMake { - break - } - _ = v_0.Args[2] - v_0_2 := v_0.Args[2] - if v_0_2.Op != OpSliceCap { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - x := v_0_2.Args[0] - v.reset(OpSliceCap) - v.AddArg(x) + v.copyOf(x) return true } - // match: (SliceCap (SliceMake _ _ (SliceLen x))) - // result: (SliceLen x) + // match: (Rsh32x64 (Const32 [0]) _) + // result: (Const32 [0]) for { - if v_0.Op != OpSliceMake { - break - } - _ = v_0.Args[2] - v_0_2 := v_0.Args[2] - if v_0_2.Op != OpSliceLen { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - x := v_0_2.Args[0] - v.reset(OpSliceLen) - v.AddArg(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } - return false -} -func rewriteValuegeneric_OpSliceLen(v *Value) bool { - v_0 := v.Args[0] - // match: (SliceLen (SliceMake _ (Const64 [c]) _)) - // result: (Const64 [c]) + // match: (Rsh32x64 (Rsh32x64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh32x64 x (Const64 [c+d])) for { - if v_0.Op != OpSliceMake { + t := v.Type + if v_0.Op != OpRsh32x64 { break } _ = v_0.Args[1] + x := v_0.Args[0] v_0_1 := v_0.Args[1] if v_0_1.Op != OpConst64 { break } - t := v_0_1.Type c := auxIntToInt64(v_0_1.AuxInt) - v.reset(OpConst64) - v.Type = t - v.AuxInt = int64ToAuxInt(c) - return true - } - // match: (SliceLen (SliceMake _ (Const32 [c]) _)) - // result: (Const32 [c]) - for { - if v_0.Op != OpSliceMake { + if v_1.Op != OpConst64 { break } - _ = v_0.Args[1] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst32 { + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { break } - t := v_0_1.Type - c := auxIntToInt32(v_0_1.AuxInt) - v.reset(OpConst32) - v.Type = t - v.AuxInt = int32ToAuxInt(c) + v.reset(OpRsh32x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) return true } - // match: (SliceLen (SliceMake _ (SliceLen x) _)) - // result: (SliceLen x) + // match: (Rsh32x64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) + // result: (SignExt8to32 (Trunc32to8 x)) for { - if v_0.Op != OpSliceMake { + if v_0.Op != OpLsh32x64 { break } _ = v_0.Args[1] + x := v_0.Args[0] v_0_1 := v_0.Args[1] - if v_0_1.Op != OpSliceLen { + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 24 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 24 { break } - x := v_0_1.Args[0] - v.reset(OpSliceLen) - v.AddArg(x) + v.reset(OpSignExt8to32) + v0 := b.NewValue0(v.Pos, OpTrunc32to8, typ.Int8) + v0.AddArg(x) + v.AddArg(v0) return true } - return false -} -func rewriteValuegeneric_OpSlicePtr(v *Value) bool { - v_0 := v.Args[0] - // match: (SlicePtr (SliceMake (SlicePtr x) _ _)) - // result: (SlicePtr x) + // match: (Rsh32x64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) + // result: (SignExt16to32 (Trunc32to16 x)) for { - if v_0.Op != OpSliceMake { + if v_0.Op != OpLsh32x64 { break } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpSlicePtr { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 16 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 16 { break } - x := v_0_0.Args[0] - v.reset(OpSlicePtr) - v.AddArg(x) + v.reset(OpSignExt16to32) + v0 := b.NewValue0(v.Pos, OpTrunc32to16, typ.Int16) + v0.AddArg(x) + v.AddArg(v0) return true } return false } -func rewriteValuegeneric_OpSlicemask(v *Value) bool { +func rewriteValuegeneric_OpRsh32x8(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (Slicemask (Const32 [x])) - // cond: x > 0 - // result: (Const32 [-1]) + b := v.Block + // match: (Rsh32x8 x (Const8 [c])) + // result: (Rsh32x64 x (Const64 [int64(uint8(c))])) for { - if v_0.Op != OpConst32 { - break - } - x := auxIntToInt32(v_0.AuxInt) - if !(x > 0) { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(-1) + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh32x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) return true } - // match: (Slicemask (Const32 [0])) + // match: (Rsh32x8 (Const32 [0]) _) // result: (Const32 [0]) for { if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { @@ -21354,22 +25202,28 @@ func rewriteValuegeneric_OpSlicemask(v *Value) bool { v.AuxInt = int32ToAuxInt(0) return true } - // match: (Slicemask (Const64 [x])) - // cond: x > 0 - // result: (Const64 [-1]) + return false +} +func rewriteValuegeneric_OpRsh64Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh64Ux16 x (Const16 [c])) + // result: (Rsh64Ux64 x (Const64 [int64(uint16(c))])) for { - if v_0.Op != OpConst64 { - break - } - x := auxIntToInt64(v_0.AuxInt) - if !(x > 0) { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(-1) + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) return true } - // match: (Slicemask (Const64 [0])) + // match: (Rsh64Ux16 (Const64 [0]) _) // result: (Const64 [0]) for { if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { @@ -21381,2757 +25235,5921 @@ func rewriteValuegeneric_OpSlicemask(v *Value) bool { } return false } -func rewriteValuegeneric_OpSqrt(v *Value) bool { +func rewriteValuegeneric_OpRsh64Ux32(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (Sqrt (Const64F [c])) - // cond: !math.IsNaN(math.Sqrt(c)) - // result: (Const64F [math.Sqrt(c)]) + b := v.Block + // match: (Rsh64Ux32 x (Const32 [c])) + // result: (Rsh64Ux64 x (Const64 [int64(uint32(c))])) for { - if v_0.Op != OpConst64F { - break - } - c := auxIntToFloat64(v_0.AuxInt) - if !(!math.IsNaN(math.Sqrt(c))) { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - v.reset(OpConst64F) - v.AuxInt = float64ToAuxInt(math.Sqrt(c)) + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) return true } - return false -} -func rewriteValuegeneric_OpStaticLECall(v *Value) bool { - b := v.Block - typ := &b.Func.Config.Types - // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [1]) mem) - // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) - // result: (MakeResult (Eq8 (Load sptr mem) (Const8 [int8(read8(scon,0))])) mem) + // match: (Rsh64Ux32 (Const64 [0]) _) + // result: (Const64 [0]) for { - if len(v.Args) != 4 { - break - } - callAux := auxToCall(v.Aux) - mem := v.Args[3] - sptr := v.Args[0] - v_1 := v.Args[1] - if v_1.Op != OpAddr { - break - } - scon := auxToSym(v_1.Aux) - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpSB { - break - } - v_2 := v.Args[2] - if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 1 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon)) { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - v.reset(OpMakeResult) - v0 := b.NewValue0(v.Pos, OpEq8, typ.Bool) - v1 := b.NewValue0(v.Pos, OpLoad, typ.Int8) - v1.AddArg2(sptr, mem) - v2 := b.NewValue0(v.Pos, OpConst8, typ.Int8) - v2.AuxInt = int8ToAuxInt(int8(read8(scon, 0))) - v0.AddArg2(v1, v2) - v.AddArg2(v0, mem) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpStore(v *Value) bool { - v_2 := v.Args[2] +func rewriteValuegeneric_OpRsh64Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - fe := b.Func.fe - // match: (Store {t1} p1 (Load p2 mem) mem) - // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() - // result: mem + typ := &b.Func.Config.Types + // match: (Rsh64Ux64 (Const64 [c]) (Const64 [d])) + // result: (Const64 [int64(uint64(c) >> uint64(d))]) for { - t1 := auxToType(v.Aux) - p1 := v_0 - if v_1.Op != OpLoad { + if v_0.Op != OpConst64 { break } - t2 := v_1.Type - mem := v_1.Args[1] - p2 := v_1.Args[0] - if mem != v_2 || !(isSamePtr(p1, p2) && t2.Size() == t1.Size()) { + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - v.copyOf(mem) + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(int64(uint64(c) >> uint64(d))) return true } - // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ oldmem)) - // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) - // result: mem + // match: (Rsh64Ux64 x (Const64 [0])) + // result: x for { - t1 := auxToType(v.Aux) - p1 := v_0 - if v_1.Op != OpLoad { - break - } - t2 := v_1.Type - oldmem := v_1.Args[1] - p2 := v_1.Args[0] - mem := v_2 - if mem.Op != OpStore { - break - } - t3 := auxToType(mem.Aux) - _ = mem.Args[2] - p3 := mem.Args[0] - if oldmem != mem.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size())) { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - v.copyOf(mem) + v.copyOf(x) return true } - // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ (Store {t4} p4 _ oldmem))) - // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) - // result: mem + // match: (Rsh64Ux64 (Const64 [0]) _) + // result: (Const64 [0]) for { - t1 := auxToType(v.Aux) - p1 := v_0 - if v_1.Op != OpLoad { - break - } - t2 := v_1.Type - oldmem := v_1.Args[1] - p2 := v_1.Args[0] - mem := v_2 - if mem.Op != OpStore { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - t3 := auxToType(mem.Aux) - _ = mem.Args[2] - p3 := mem.Args[0] - mem_2 := mem.Args[2] - if mem_2.Op != OpStore { + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (Rsh64Ux64 _ (Const64 [c])) + // cond: uint64(c) >= 64 + // result: (Const64 [0]) + for { + if v_1.Op != OpConst64 { break } - t4 := auxToType(mem_2.Aux) - _ = mem_2.Args[2] - p4 := mem_2.Args[0] - if oldmem != mem_2.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size())) { + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 64) { break } - v.copyOf(mem) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ (Store {t4} p4 _ (Store {t5} p5 _ oldmem)))) - // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) && disjoint(p1, t1.Size(), p5, t5.Size()) - // result: mem + // match: (Rsh64Ux64 (Rsh64Ux64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh64Ux64 x (Const64 [c+d])) for { - t1 := auxToType(v.Aux) - p1 := v_0 - if v_1.Op != OpLoad { - break - } - t2 := v_1.Type - oldmem := v_1.Args[1] - p2 := v_1.Args[0] - mem := v_2 - if mem.Op != OpStore { + t := v.Type + if v_0.Op != OpRsh64Ux64 { break } - t3 := auxToType(mem.Aux) - _ = mem.Args[2] - p3 := mem.Args[0] - mem_2 := mem.Args[2] - if mem_2.Op != OpStore { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - t4 := auxToType(mem_2.Aux) - _ = mem_2.Args[2] - p4 := mem_2.Args[0] - mem_2_2 := mem_2.Args[2] - if mem_2_2.Op != OpStore { + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - t5 := auxToType(mem_2_2.Aux) - _ = mem_2_2.Args[2] - p5 := mem_2_2.Args[0] - if oldmem != mem_2_2.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) && disjoint(p1, t1.Size(), p5, t5.Size())) { + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { break } - v.copyOf(mem) + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) return true } - // match: (Store {t} (OffPtr [o] p1) x mem:(Zero [n] p2 _)) - // cond: isConstZero(x) && o >= 0 && t.Size() + o <= n && isSamePtr(p1, p2) - // result: mem + // match: (Rsh64Ux64 (Rsh64x64 x _) (Const64 [63])) + // result: (Rsh64Ux64 x (Const64 [63])) for { - t := auxToType(v.Aux) - if v_0.Op != OpOffPtr { + if v_0.Op != OpRsh64x64 { break } - o := auxIntToInt64(v_0.AuxInt) - p1 := v_0.Args[0] - x := v_1 - mem := v_2 - if mem.Op != OpZero { + x := v_0.Args[0] + if v_1.Op != OpConst64 { break } - n := auxIntToInt64(mem.AuxInt) - p2 := mem.Args[0] - if !(isConstZero(x) && o >= 0 && t.Size()+o <= n && isSamePtr(p1, p2)) { + t := v_1.Type + if auxIntToInt64(v_1.AuxInt) != 63 { break } - v.copyOf(mem) + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(63) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Zero [n] p3 _))) - // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p3) && disjoint(op, t1.Size(), p2, t2.Size()) - // result: mem + // match: (Rsh64Ux64 i:(Lsh64x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 64 && i.Uses == 1 + // result: (And64 x (Const64 [int64(^uint64(0)>>c)])) for { - t1 := auxToType(v.Aux) - op := v_0 - if op.Op != OpOffPtr { - break - } - o1 := auxIntToInt64(op.AuxInt) - p1 := op.Args[0] - x := v_1 - mem := v_2 - if mem.Op != OpStore { + i := v_0 + if i.Op != OpLsh64x64 { break } - t2 := auxToType(mem.Aux) - _ = mem.Args[2] - p2 := mem.Args[0] - mem_2 := mem.Args[2] - if mem_2.Op != OpZero { + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { break } - n := auxIntToInt64(mem_2.AuxInt) - p3 := mem_2.Args[0] - if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p3) && disjoint(op, t1.Size(), p2, t2.Size())) { + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 64 && i.Uses == 1) { break } - v.copyOf(mem) + v.reset(OpAnd64) + v0 := b.NewValue0(v.Pos, OpConst64, v.Type) + v0.AuxInt = int64ToAuxInt(int64(^uint64(0) >> c)) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Store {t3} p3 _ (Zero [n] p4 _)))) - // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p4) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) - // result: mem + // match: (Rsh64Ux64 (Lsh64x64 (Rsh64Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) + // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) + // result: (Rsh64Ux64 x (Const64 [c1-c2+c3])) for { - t1 := auxToType(v.Aux) - op := v_0 - if op.Op != OpOffPtr { + if v_0.Op != OpLsh64x64 { break } - o1 := auxIntToInt64(op.AuxInt) - p1 := op.Args[0] - x := v_1 - mem := v_2 - if mem.Op != OpStore { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpRsh64Ux64 { break } - t2 := auxToType(mem.Aux) - _ = mem.Args[2] - p2 := mem.Args[0] - mem_2 := mem.Args[2] - if mem_2.Op != OpStore { + _ = v_0_0.Args[1] + x := v_0_0.Args[0] + v_0_0_1 := v_0_0.Args[1] + if v_0_0_1.Op != OpConst64 { break } - t3 := auxToType(mem_2.Aux) - _ = mem_2.Args[2] - p3 := mem_2.Args[0] - mem_2_2 := mem_2.Args[2] - if mem_2_2.Op != OpZero { + c1 := auxIntToInt64(v_0_0_1.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - n := auxIntToInt64(mem_2_2.AuxInt) - p4 := mem_2_2.Args[0] - if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p4) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size())) { + c2 := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - v.copyOf(mem) + c3 := auxIntToInt64(v_1.AuxInt) + if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { + break + } + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Store {t3} p3 _ (Store {t4} p4 _ (Zero [n] p5 _))))) - // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p5) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) && disjoint(op, t1.Size(), p4, t4.Size()) - // result: mem + // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) + // result: (ZeroExt8to64 (Trunc64to8 x)) for { - t1 := auxToType(v.Aux) - op := v_0 - if op.Op != OpOffPtr { + if v_0.Op != OpLsh64x64 { break } - o1 := auxIntToInt64(op.AuxInt) - p1 := op.Args[0] - x := v_1 - mem := v_2 - if mem.Op != OpStore { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 56 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 56 { break } - t2 := auxToType(mem.Aux) - _ = mem.Args[2] - p2 := mem.Args[0] - mem_2 := mem.Args[2] - if mem_2.Op != OpStore { + v.reset(OpZeroExt8to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to8, typ.UInt8) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) + // result: (ZeroExt16to64 (Trunc64to16 x)) + for { + if v_0.Op != OpLsh64x64 { break } - t3 := auxToType(mem_2.Aux) - _ = mem_2.Args[2] - p3 := mem_2.Args[0] - mem_2_2 := mem_2.Args[2] - if mem_2_2.Op != OpStore { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 48 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 48 { break } - t4 := auxToType(mem_2_2.Aux) - _ = mem_2_2.Args[2] - p4 := mem_2_2.Args[0] - mem_2_2_2 := mem_2_2.Args[2] - if mem_2_2_2.Op != OpZero { + v.reset(OpZeroExt16to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to16, typ.UInt16) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) + // result: (ZeroExt32to64 (Trunc64to32 x)) + for { + if v_0.Op != OpLsh64x64 { break } - n := auxIntToInt64(mem_2_2_2.AuxInt) - p5 := mem_2_2_2.Args[0] - if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p5) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) && disjoint(op, t1.Size(), p4, t4.Size())) { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 32 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 32 { break } - v.copyOf(mem) + v.reset(OpZeroExt32to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to32, typ.UInt32) + v0.AddArg(x) + v.AddArg(v0) return true } - // match: (Store _ (StructMake0) mem) - // result: mem + return false +} +func rewriteValuegeneric_OpRsh64Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh64Ux8 x (Const8 [c])) + // result: (Rsh64Ux64 x (Const64 [int64(uint8(c))])) for { - if v_1.Op != OpStructMake0 { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - mem := v_2 - v.copyOf(mem) + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh64Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) return true } - // match: (Store dst (StructMake1 f0) mem) - // result: (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem) + // match: (Rsh64Ux8 (Const64 [0]) _) + // result: (Const64 [0]) for { - dst := v_0 - if v_1.Op != OpStructMake1 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - t := v_1.Type - f0 := v_1.Args[0] - mem := v_2 - v.reset(OpStore) - v.Aux = typeToAux(t.FieldType(0)) - v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) - v0.AuxInt = int64ToAuxInt(0) - v0.AddArg(dst) - v.AddArg3(v0, f0, mem) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Store dst (StructMake2 f0 f1) mem) - // result: (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem)) + return false +} +func rewriteValuegeneric_OpRsh64x16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh64x16 x (Const16 [c])) + // result: (Rsh64x64 x (Const64 [int64(uint16(c))])) for { - dst := v_0 - if v_1.Op != OpStructMake2 { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - t := v_1.Type - f1 := v_1.Args[1] - f0 := v_1.Args[0] - mem := v_2 - v.reset(OpStore) - v.Aux = typeToAux(t.FieldType(1)) - v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) - v0.AuxInt = int64ToAuxInt(t.FieldOff(1)) - v0.AddArg(dst) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t.FieldType(0)) - v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) - v2.AuxInt = int64ToAuxInt(0) - v2.AddArg(dst) - v1.AddArg3(v2, f0, mem) - v.AddArg3(v0, f1, v1) + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh64x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) return true } - // match: (Store dst (StructMake3 f0 f1 f2) mem) - // result: (Store {t.FieldType(2)} (OffPtr [t.FieldOff(2)] dst) f2 (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem))) + // match: (Rsh64x16 (Const64 [0]) _) + // result: (Const64 [0]) for { - dst := v_0 - if v_1.Op != OpStructMake3 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - t := v_1.Type - f2 := v_1.Args[2] - f0 := v_1.Args[0] - f1 := v_1.Args[1] - mem := v_2 - v.reset(OpStore) - v.Aux = typeToAux(t.FieldType(2)) - v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(2).PtrTo()) - v0.AuxInt = int64ToAuxInt(t.FieldOff(2)) - v0.AddArg(dst) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t.FieldType(1)) - v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) - v2.AuxInt = int64ToAuxInt(t.FieldOff(1)) - v2.AddArg(dst) - v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v3.Aux = typeToAux(t.FieldType(0)) - v4 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) - v4.AuxInt = int64ToAuxInt(0) - v4.AddArg(dst) - v3.AddArg3(v4, f0, mem) - v1.AddArg3(v2, f1, v3) - v.AddArg3(v0, f2, v1) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Store dst (StructMake4 f0 f1 f2 f3) mem) - // result: (Store {t.FieldType(3)} (OffPtr [t.FieldOff(3)] dst) f3 (Store {t.FieldType(2)} (OffPtr [t.FieldOff(2)] dst) f2 (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem)))) + return false +} +func rewriteValuegeneric_OpRsh64x32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh64x32 x (Const32 [c])) + // result: (Rsh64x64 x (Const64 [int64(uint32(c))])) for { - dst := v_0 - if v_1.Op != OpStructMake4 { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - t := v_1.Type - f3 := v_1.Args[3] - f0 := v_1.Args[0] - f1 := v_1.Args[1] - f2 := v_1.Args[2] - mem := v_2 - v.reset(OpStore) - v.Aux = typeToAux(t.FieldType(3)) - v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(3).PtrTo()) - v0.AuxInt = int64ToAuxInt(t.FieldOff(3)) - v0.AddArg(dst) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t.FieldType(2)) - v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(2).PtrTo()) - v2.AuxInt = int64ToAuxInt(t.FieldOff(2)) - v2.AddArg(dst) - v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v3.Aux = typeToAux(t.FieldType(1)) - v4 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) - v4.AuxInt = int64ToAuxInt(t.FieldOff(1)) - v4.AddArg(dst) - v5 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v5.Aux = typeToAux(t.FieldType(0)) - v6 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) - v6.AuxInt = int64ToAuxInt(0) - v6.AddArg(dst) - v5.AddArg3(v6, f0, mem) - v3.AddArg3(v4, f1, v5) - v1.AddArg3(v2, f2, v3) - v.AddArg3(v0, f3, v1) + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh64x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) return true } - // match: (Store {t} dst (Load src mem) mem) - // cond: !fe.CanSSA(t) - // result: (Move {t} [t.Size()] dst src mem) + // match: (Rsh64x32 (Const64 [0]) _) + // result: (Const64 [0]) for { - t := auxToType(v.Aux) - dst := v_0 - if v_1.Op != OpLoad { - break - } - mem := v_1.Args[1] - src := v_1.Args[0] - if mem != v_2 || !(!fe.CanSSA(t)) { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(t.Size()) - v.Aux = typeToAux(t) - v.AddArg3(dst, src, mem) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Store {t} dst (Load src mem) (VarDef {x} mem)) - // cond: !fe.CanSSA(t) - // result: (Move {t} [t.Size()] dst src (VarDef {x} mem)) + return false +} +func rewriteValuegeneric_OpRsh64x64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh64x64 (Const64 [c]) (Const64 [d])) + // result: (Const64 [c >> uint64(d)]) for { - t := auxToType(v.Aux) - dst := v_0 - if v_1.Op != OpLoad { - break - } - mem := v_1.Args[1] - src := v_1.Args[0] - if v_2.Op != OpVarDef { + if v_0.Op != OpConst64 { break } - x := auxToSym(v_2.Aux) - if mem != v_2.Args[0] || !(!fe.CanSSA(t)) { + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - v.reset(OpMove) - v.AuxInt = int64ToAuxInt(t.Size()) - v.Aux = typeToAux(t) - v0 := b.NewValue0(v.Pos, OpVarDef, types.TypeMem) - v0.Aux = symToAux(x) - v0.AddArg(mem) - v.AddArg3(dst, src, v0) + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(c >> uint64(d)) return true } - // match: (Store _ (ArrayMake0) mem) - // result: mem + // match: (Rsh64x64 x (Const64 [0])) + // result: x for { - if v_1.Op != OpArrayMake0 { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - mem := v_2 - v.copyOf(mem) + v.copyOf(x) return true } - // match: (Store dst (ArrayMake1 e) mem) - // result: (Store {e.Type} dst e mem) + // match: (Rsh64x64 (Const64 [0]) _) + // result: (Const64 [0]) for { - dst := v_0 - if v_1.Op != OpArrayMake1 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - e := v_1.Args[0] - mem := v_2 - v.reset(OpStore) - v.Aux = typeToAux(e.Type) - v.AddArg3(dst, e, mem) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Store (SelectN [0] call:(StaticLECall _ _)) x mem:(SelectN [1] call)) - // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject") - // result: mem + // match: (Rsh64x64 (Rsh64x64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh64x64 x (Const64 [c+d])) for { - if v_0.Op != OpSelectN || auxIntToInt64(v_0.AuxInt) != 0 { + t := v.Type + if v_0.Op != OpRsh64x64 { break } - call := v_0.Args[0] - if call.Op != OpStaticLECall || len(call.Args) != 2 { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - x := v_1 - mem := v_2 - if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) { + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - v.copyOf(mem) + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { + break + } + v.reset(OpRsh64x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) return true } - // match: (Store (OffPtr (SelectN [0] call:(StaticLECall _ _))) x mem:(SelectN [1] call)) - // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject") - // result: mem + // match: (Rsh64x64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) + // result: (SignExt8to64 (Trunc64to8 x)) for { - if v_0.Op != OpOffPtr { + if v_0.Op != OpLsh64x64 { break } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpSelectN || auxIntToInt64(v_0_0.AuxInt) != 0 { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 56 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 56 { break } - call := v_0_0.Args[0] - if call.Op != OpStaticLECall || len(call.Args) != 2 { + v.reset(OpSignExt8to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to8, typ.Int8) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (Rsh64x64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) + // result: (SignExt16to64 (Trunc64to16 x)) + for { + if v_0.Op != OpLsh64x64 { break } - x := v_1 - mem := v_2 - if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 48 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 48 { break } - v.copyOf(mem) + v.reset(OpSignExt16to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to16, typ.Int16) + v0.AddArg(x) + v.AddArg(v0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [0] p2) d2 m3:(Move [n] p3 _ mem))) - // cond: m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 mem)) + // match: (Rsh64x64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) + // result: (SignExt32to64 (Trunc64to32 x)) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { + if v_0.Op != OpLsh64x64 { break } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 || auxIntToInt64(v_0_1.AuxInt) != 32 || v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 32 { break } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr || auxIntToInt64(op2.AuxInt) != 0 { + v.reset(OpSignExt32to64) + v0 := b.NewValue0(v.Pos, OpTrunc64to32, typ.Int32) + v0.AddArg(x) + v.AddArg(v0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh64x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh64x8 x (Const8 [c])) + // result: (Rsh64x64 x (Const64 [int64(uint8(c))])) + for { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpMove { + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh64x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh64x8 (Const64 [0]) _) + // result: (Const64 [0]) + for { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - n := auxIntToInt64(m3.AuxInt) - mem := m3.Args[2] - p3 := m3.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3)) { + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh8Ux16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh8Ux16 x (Const16 [c])) + // result: (Rsh8Ux64 x (Const64 [int64(uint16(c))])) + for { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v0.AddArg3(op2, d2, mem) - v.AddArg3(op1, d1, v0) + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [0] p3) d3 m4:(Move [n] p4 _ mem)))) - // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 mem))) + // match: (Rsh8Ux16 (Const8 [0]) _) + // result: (Const8 [0]) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh8Ux32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh8Ux32 x (Const32 [c])) + // result: (Rsh8Ux64 x (Const64 [int64(uint32(c))])) + for { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr { + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh8Ux32 (Const8 [0]) _) + // result: (Const8 [0]) + for { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - o2 := auxIntToInt64(op2.AuxInt) - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpStore { + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) + return true + } + return false +} +func rewriteValuegeneric_OpRsh8Ux64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types + // match: (Rsh8Ux64 (Const8 [c]) (Const64 [d])) + // result: (Const8 [int8(uint8(c) >> uint64(d))]) + for { + if v_0.Op != OpConst8 { break } - t3 := auxToType(m3.Aux) - _ = m3.Args[2] - op3 := m3.Args[0] - if op3.Op != OpOffPtr || auxIntToInt64(op3.AuxInt) != 0 { + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpConst64 { break } - p3 := op3.Args[0] - d3 := m3.Args[1] - m4 := m3.Args[2] - if m4.Op != OpMove { + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(int8(uint8(c) >> uint64(d))) + return true + } + // match: (Rsh8Ux64 x (Const64 [0])) + // result: x + for { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - n := auxIntToInt64(m4.AuxInt) - mem := m4.Args[2] - p4 := m4.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4)) { + v.copyOf(x) + return true + } + // match: (Rsh8Ux64 (Const8 [0]) _) + // result: (Const8 [0]) + for { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t3) - v1.AddArg3(op3, d3, mem) - v0.AddArg3(op2, d2, v1) - v.AddArg3(op1, d1, v0) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [o3] p3) d3 m4:(Store {t4} op4:(OffPtr [0] p4) d4 m5:(Move [n] p5 _ mem))))) - // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size() + t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 (Store {t4} op4 d4 mem)))) + // match: (Rsh8Ux64 _ (Const64 [c])) + // cond: uint64(c) >= 8 + // result: (Const8 [0]) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { + if v_1.Op != OpConst64 { break } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { + c := auxIntToInt64(v_1.AuxInt) + if !(uint64(c) >= 8) { break } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr { + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) + return true + } + // match: (Rsh8Ux64 (Rsh8Ux64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh8Ux64 x (Const64 [c+d])) + for { + t := v.Type + if v_0.Op != OpRsh8Ux64 { break } - o2 := auxIntToInt64(op2.AuxInt) - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpStore { + _ = v_0.Args[1] + x := v_0.Args[0] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - t3 := auxToType(m3.Aux) - _ = m3.Args[2] - op3 := m3.Args[0] - if op3.Op != OpOffPtr { + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - o3 := auxIntToInt64(op3.AuxInt) - p3 := op3.Args[0] - d3 := m3.Args[1] - m4 := m3.Args[2] - if m4.Op != OpStore { + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { break } - t4 := auxToType(m4.Aux) - _ = m4.Args[2] - op4 := m4.Args[0] - if op4.Op != OpOffPtr || auxIntToInt64(op4.AuxInt) != 0 { + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) + return true + } + // match: (Rsh8Ux64 (Rsh8x64 x _) (Const64 [7] )) + // result: (Rsh8Ux64 x (Const64 [7] )) + for { + if v_0.Op != OpRsh8x64 { break } - p4 := op4.Args[0] - d4 := m4.Args[1] - m5 := m4.Args[2] - if m5.Op != OpMove { + x := v_0.Args[0] + if v_1.Op != OpConst64 { break } - n := auxIntToInt64(m5.AuxInt) - mem := m5.Args[2] - p5 := m5.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size()+t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5)) { + t := v_1.Type + if auxIntToInt64(v_1.AuxInt) != 7 { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t3) - v2 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v2.Aux = typeToAux(t4) - v2.AddArg3(op4, d4, mem) - v1.AddArg3(op3, d3, v2) - v0.AddArg3(op2, d2, v1) - v.AddArg3(op1, d1, v0) + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(7) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [0] p2) d2 m3:(Zero [n] p3 mem))) - // cond: m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 mem)) + // match: (Rsh8Ux64 i:(Lsh8x64 x (Const64 [c])) (Const64 [c])) + // cond: c >= 0 && c < 8 && i.Uses == 1 + // result: (And8 x (Const8 [int8 (^uint8 (0)>>c)])) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { - break - } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { - break - } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr || auxIntToInt64(op2.AuxInt) != 0 { + i := v_0 + if i.Op != OpLsh8x64 { break } - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpZero { + _ = i.Args[1] + x := i.Args[0] + i_1 := i.Args[1] + if i_1.Op != OpConst64 { break } - n := auxIntToInt64(m3.AuxInt) - mem := m3.Args[1] - p3 := m3.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3)) { + c := auxIntToInt64(i_1.AuxInt) + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != c || !(c >= 0 && c < 8 && i.Uses == 1) { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v0.AddArg3(op2, d2, mem) - v.AddArg3(op1, d1, v0) + v.reset(OpAnd8) + v0 := b.NewValue0(v.Pos, OpConst8, v.Type) + v0.AuxInt = int8ToAuxInt(int8(^uint8(0) >> c)) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [0] p3) d3 m4:(Zero [n] p4 mem)))) - // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 mem))) + // match: (Rsh8Ux64 (Lsh8x64 (Rsh8Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3])) + // cond: uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3) + // result: (Rsh8Ux64 x (Const64 [c1-c2+c3])) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { - break - } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { + if v_0.Op != OpLsh8x64 { break } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr { + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpRsh8Ux64 { break } - o2 := auxIntToInt64(op2.AuxInt) - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpStore { + _ = v_0_0.Args[1] + x := v_0_0.Args[0] + v_0_0_1 := v_0_0.Args[1] + if v_0_0_1.Op != OpConst64 { break } - t3 := auxToType(m3.Aux) - _ = m3.Args[2] - op3 := m3.Args[0] - if op3.Op != OpOffPtr || auxIntToInt64(op3.AuxInt) != 0 { + c1 := auxIntToInt64(v_0_0_1.AuxInt) + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - p3 := op3.Args[0] - d3 := m3.Args[1] - m4 := m3.Args[2] - if m4.Op != OpZero { + c2 := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { break } - n := auxIntToInt64(m4.AuxInt) - mem := m4.Args[1] - p4 := m4.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4)) { + c3 := auxIntToInt64(v_1.AuxInt) + if !(uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)) { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t3) - v1.AddArg3(op3, d3, mem) - v0.AddArg3(op2, d2, v1) - v.AddArg3(op1, d1, v0) + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64) + v0.AuxInt = int64ToAuxInt(c1 - c2 + c3) + v.AddArg2(x, v0) return true } - // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [o3] p3) d3 m4:(Store {t4} op4:(OffPtr [0] p4) d4 m5:(Zero [n] p5 mem))))) - // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size() + t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5) - // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 (Store {t4} op4 d4 mem)))) + return false +} +func rewriteValuegeneric_OpRsh8Ux8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh8Ux8 x (Const8 [c])) + // result: (Rsh8Ux64 x (Const64 [int64(uint8(c))])) for { - t1 := auxToType(v.Aux) - op1 := v_0 - if op1.Op != OpOffPtr { - break - } - o1 := auxIntToInt64(op1.AuxInt) - p1 := op1.Args[0] - d1 := v_1 - m2 := v_2 - if m2.Op != OpStore { - break - } - t2 := auxToType(m2.Aux) - _ = m2.Args[2] - op2 := m2.Args[0] - if op2.Op != OpOffPtr { - break - } - o2 := auxIntToInt64(op2.AuxInt) - p2 := op2.Args[0] - d2 := m2.Args[1] - m3 := m2.Args[2] - if m3.Op != OpStore { - break - } - t3 := auxToType(m3.Aux) - _ = m3.Args[2] - op3 := m3.Args[0] - if op3.Op != OpOffPtr { - break - } - o3 := auxIntToInt64(op3.AuxInt) - p3 := op3.Args[0] - d3 := m3.Args[1] - m4 := m3.Args[2] - if m4.Op != OpStore { - break - } - t4 := auxToType(m4.Aux) - _ = m4.Args[2] - op4 := m4.Args[0] - if op4.Op != OpOffPtr || auxIntToInt64(op4.AuxInt) != 0 { - break - } - p4 := op4.Args[0] - d4 := m4.Args[1] - m5 := m4.Args[2] - if m5.Op != OpZero { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - n := auxIntToInt64(m5.AuxInt) - mem := m5.Args[1] - p5 := m5.Args[0] - if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size()+t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5)) { + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh8Ux64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh8Ux8 (Const8 [0]) _) + // result: (Const8 [0]) + for { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - v.reset(OpStore) - v.Aux = typeToAux(t1) - v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v0.Aux = typeToAux(t2) - v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v1.Aux = typeToAux(t3) - v2 := b.NewValue0(v.Pos, OpStore, types.TypeMem) - v2.Aux = typeToAux(t4) - v2.AddArg3(op4, d4, mem) - v1.AddArg3(op3, d3, v2) - v0.AddArg3(op2, d2, v1) - v.AddArg3(op1, d1, v0) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpStringLen(v *Value) bool { +func rewriteValuegeneric_OpRsh8x16(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (StringLen (StringMake _ (Const64 [c]))) - // result: (Const64 [c]) + b := v.Block + // match: (Rsh8x16 x (Const16 [c])) + // result: (Rsh8x64 x (Const64 [int64(uint16(c))])) for { - if v_0.Op != OpStringMake { + t := v.Type + x := v_0 + if v_1.Op != OpConst16 { break } - _ = v_0.Args[1] - v_0_1 := v_0.Args[1] - if v_0_1.Op != OpConst64 { + c := auxIntToInt16(v_1.AuxInt) + v.reset(OpRsh8x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint16(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh8x16 (Const8 [0]) _) + // result: (Const8 [0]) + for { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - t := v_0_1.Type - c := auxIntToInt64(v_0_1.AuxInt) - v.reset(OpConst64) - v.Type = t - v.AuxInt = int64ToAuxInt(c) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpStringPtr(v *Value) bool { +func rewriteValuegeneric_OpRsh8x32(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (StringPtr (StringMake (Addr {s} base) _)) - // result: (Addr {s} base) + b := v.Block + // match: (Rsh8x32 x (Const32 [c])) + // result: (Rsh8x64 x (Const64 [int64(uint32(c))])) for { - if v_0.Op != OpStringMake { + t := v.Type + x := v_0 + if v_1.Op != OpConst32 { break } - v_0_0 := v_0.Args[0] - if v_0_0.Op != OpAddr { + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpRsh8x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint32(c))) + v.AddArg2(x, v0) + return true + } + // match: (Rsh8x32 (Const8 [0]) _) + // result: (Const8 [0]) + for { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - t := v_0_0.Type - s := auxToSym(v_0_0.Aux) - base := v_0_0.Args[0] - v.reset(OpAddr) - v.Type = t - v.Aux = symToAux(s) - v.AddArg(base) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } return false } -func rewriteValuegeneric_OpStructSelect(v *Value) bool { +func rewriteValuegeneric_OpRsh8x64(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - fe := b.Func.fe - // match: (StructSelect (StructMake1 x)) - // result: x + // match: (Rsh8x64 (Const8 [c]) (Const64 [d])) + // result: (Const8 [c >> uint64(d)]) for { - if v_0.Op != OpStructMake1 { + if v_0.Op != OpConst8 { break } - x := v_0.Args[0] - v.copyOf(x) + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpConst64 { + break + } + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(c >> uint64(d)) return true } - // match: (StructSelect [0] (StructMake2 x _)) + // match: (Rsh8x64 x (Const64 [0])) // result: x for { - if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake2 { + x := v_0 + if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != 0 { break } - x := v_0.Args[0] v.copyOf(x) return true } - // match: (StructSelect [1] (StructMake2 _ x)) - // result: x + // match: (Rsh8x64 (Const8 [0]) _) + // result: (Const8 [0]) for { - if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake2 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - x := v_0.Args[1] - v.copyOf(x) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } - // match: (StructSelect [0] (StructMake3 x _ _)) - // result: x + // match: (Rsh8x64 (Rsh8x64 x (Const64 [c])) (Const64 [d])) + // cond: !uaddOvf(c,d) + // result: (Rsh8x64 x (Const64 [c+d])) for { - if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake3 { + t := v.Type + if v_0.Op != OpRsh8x64 { break } + _ = v_0.Args[1] x := v_0.Args[0] - v.copyOf(x) - return true - } - // match: (StructSelect [1] (StructMake3 _ x _)) - // result: x - for { - if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake3 { + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - x := v_0.Args[1] - v.copyOf(x) + c := auxIntToInt64(v_0_1.AuxInt) + if v_1.Op != OpConst64 { + break + } + d := auxIntToInt64(v_1.AuxInt) + if !(!uaddOvf(c, d)) { + break + } + v.reset(OpRsh8x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c + d) + v.AddArg2(x, v0) return true } - // match: (StructSelect [2] (StructMake3 _ _ x)) - // result: x + return false +} +func rewriteValuegeneric_OpRsh8x8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Rsh8x8 x (Const8 [c])) + // result: (Rsh8x64 x (Const64 [int64(uint8(c))])) for { - if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpStructMake3 { + t := v.Type + x := v_0 + if v_1.Op != OpConst8 { break } - x := v_0.Args[2] - v.copyOf(x) + c := auxIntToInt8(v_1.AuxInt) + v.reset(OpRsh8x64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(int64(uint8(c))) + v.AddArg2(x, v0) return true } - // match: (StructSelect [0] (StructMake4 x _ _ _)) - // result: x + // match: (Rsh8x8 (Const8 [0]) _) + // result: (Const8 [0]) for { - if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake4 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != 0 { break } - x := v_0.Args[0] - v.copyOf(x) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) return true } - // match: (StructSelect [1] (StructMake4 _ x _ _)) - // result: x + return false +} +func rewriteValuegeneric_OpSelect0(v *Value) bool { + v_0 := v.Args[0] + // match: (Select0 (Div128u (Const64 [0]) lo y)) + // result: (Div64u lo y) for { - if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake4 { + if v_0.Op != OpDiv128u { break } - x := v_0.Args[1] - v.copyOf(x) + y := v_0.Args[2] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { + break + } + lo := v_0.Args[1] + v.reset(OpDiv64u) + v.AddArg2(lo, y) return true } - // match: (StructSelect [2] (StructMake4 _ _ x _)) + // match: (Select0 (Mul32uover (Const32 [1]) x)) // result: x for { - if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpStructMake4 { + if v_0.Op != OpMul32uover { break } - x := v_0.Args[2] - v.copyOf(x) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst32 || auxIntToInt32(v_0_0.AuxInt) != 1 { + continue + } + x := v_0_1 + v.copyOf(x) + return true + } + break } - // match: (StructSelect [3] (StructMake4 _ _ _ x)) + // match: (Select0 (Mul64uover (Const64 [1]) x)) // result: x for { - if auxIntToInt64(v.AuxInt) != 3 || v_0.Op != OpStructMake4 { + if v_0.Op != OpMul64uover { break } - x := v_0.Args[3] - v.copyOf(x) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 1 { + continue + } + x := v_0_1 + v.copyOf(x) + return true + } + break } - // match: (StructSelect [i] x:(Load ptr mem)) - // cond: !fe.CanSSA(t) - // result: @x.Block (Load (OffPtr [t.FieldOff(int(i))] ptr) mem) + // match: (Select0 (Mul64uover (Const64 [0]) x)) + // result: (Const64 [0]) for { - i := auxIntToInt64(v.AuxInt) - x := v_0 - if x.Op != OpLoad { + if v_0.Op != OpMul64uover { break } - t := x.Type - mem := x.Args[1] - ptr := x.Args[0] - if !(!fe.CanSSA(t)) { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { + continue + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) + return true } - b = x.Block - v0 := b.NewValue0(v.Pos, OpLoad, v.Type) - v.copyOf(v0) - v1 := b.NewValue0(v.Pos, OpOffPtr, v.Type.PtrTo()) - v1.AuxInt = int64ToAuxInt(t.FieldOff(int(i))) - v1.AddArg(ptr) - v0.AddArg2(v1, mem) - return true + break } - // match: (StructSelect [0] (IData x)) - // result: (IData x) + // match: (Select0 (Mul32uover (Const32 [0]) x)) + // result: (Const32 [0]) for { - if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpIData { + if v_0.Op != OpMul32uover { break } - x := v_0.Args[0] - v.reset(OpIData) - v.AddArg(x) - return true + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst32 || auxIntToInt32(v_0_0.AuxInt) != 0 { + continue + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true + } + break } return false } -func rewriteValuegeneric_OpSub16(v *Value) bool { - v_1 := v.Args[1] +func rewriteValuegeneric_OpSelect1(v *Value) bool { v_0 := v.Args[0] - b := v.Block - // match: (Sub16 (Const16 [c]) (Const16 [d])) - // result: (Const16 [c-d]) + // match: (Select1 (Div128u (Const64 [0]) lo y)) + // result: (Mod64u lo y) for { - if v_0.Op != OpConst16 { + if v_0.Op != OpDiv128u { break } - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpConst16 { + y := v_0.Args[2] + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { break } - d := auxIntToInt16(v_1.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(c - d) + lo := v_0.Args[1] + v.reset(OpMod64u) + v.AddArg2(lo, y) return true } - // match: (Sub16 x (Const16 [c])) - // cond: x.Op != OpConst16 - // result: (Add16 (Const16 [-c]) x) + // match: (Select1 (Mul32uover (Const32 [1]) x)) + // result: (ConstBool [false]) for { - x := v_0 - if v_1.Op != OpConst16 { + if v_0.Op != OpMul32uover { break } - t := v_1.Type - c := auxIntToInt16(v_1.AuxInt) - if !(x.Op != OpConst16) { - break + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst32 || auxIntToInt32(v_0_0.AuxInt) != 1 { + continue + } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) + return true } - v.reset(OpAdd16) - v0 := b.NewValue0(v.Pos, OpConst16, t) - v0.AuxInt = int16ToAuxInt(-c) - v.AddArg2(v0, x) - return true + break } - // match: (Sub16 (Mul16 x y) (Mul16 x z)) - // result: (Mul16 x (Sub16 y z)) + // match: (Select1 (Mul64uover (Const64 [1]) x)) + // result: (ConstBool [false]) for { - t := v.Type - if v_0.Op != OpMul16 { + if v_0.Op != OpMul64uover { break } _ = v_0.Args[1] v_0_0 := v_0.Args[0] v_0_1 := v_0.Args[1] for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if v_1.Op != OpMul16 { + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 1 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - z := v_1_1 - v.reset(OpMul16) - v0 := b.NewValue0(v.Pos, OpSub16, t) - v0.AddArg2(y, z) - v.AddArg2(x, v0) - return true - } + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) + return true } break } - // match: (Sub16 x x) - // result: (Const16 [0]) - for { - x := v_0 - if x != v_1 { - break - } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true - } - // match: (Sub16 (Add16 x y) x) - // result: y + // match: (Select1 (Mul64uover (Const64 [0]) x)) + // result: (ConstBool [false]) for { - if v_0.Op != OpAdd16 { + if v_0.Op != OpMul64uover { break } _ = v_0.Args[1] v_0_0 := v_0.Args[0] v_0_1 := v_0.Args[1] for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if x != v_1 { + if v_0_0.Op != OpConst64 || auxIntToInt64(v_0_0.AuxInt) != 0 { continue } - v.copyOf(y) + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } break } - // match: (Sub16 (Add16 x y) y) - // result: x + // match: (Select1 (Mul32uover (Const32 [0]) x)) + // result: (ConstBool [false]) for { - if v_0.Op != OpAdd16 { + if v_0.Op != OpMul32uover { break } _ = v_0.Args[1] v_0_0 := v_0.Args[0] v_0_1 := v_0.Args[1] for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if y != v_1 { + if v_0_0.Op != OpConst32 || auxIntToInt32(v_0_0.AuxInt) != 0 { continue } - v.copyOf(x) + v.reset(OpConstBool) + v.AuxInt = boolToAuxInt(false) return true } break } - // match: (Sub16 x (Sub16 i:(Const16 ) z)) - // cond: (z.Op != OpConst16 && x.Op != OpConst16) - // result: (Sub16 (Add16 x z) i) + return false +} +func rewriteValuegeneric_OpSelectN(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + // match: (SelectN [0] (MakeResult x ___)) + // result: x for { - x := v_0 - if v_1.Op != OpSub16 { + if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpMakeResult || len(v_0.Args) < 1 { break } - z := v_1.Args[1] - i := v_1.Args[0] - if i.Op != OpConst16 { + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (SelectN [1] (MakeResult x y ___)) + // result: y + for { + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpMakeResult || len(v_0.Args) < 2 { break } - t := i.Type - if !(z.Op != OpConst16 && x.Op != OpConst16) { + y := v_0.Args[1] + v.copyOf(y) + return true + } + // match: (SelectN [2] (MakeResult x y z ___)) + // result: z + for { + if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpMakeResult || len(v_0.Args) < 3 { break } - v.reset(OpSub16) - v0 := b.NewValue0(v.Pos, OpAdd16, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) + z := v_0.Args[2] + v.copyOf(z) return true } - // match: (Sub16 x (Add16 z i:(Const16 ))) - // cond: (z.Op != OpConst16 && x.Op != OpConst16) - // result: (Sub16 (Sub16 x z) i) + // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call) + // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - x := v_0 - if v_1.Op != OpAdd16 { + if auxIntToInt64(v.AuxInt) != 0 { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z := v_1_0 - i := v_1_1 - if i.Op != OpConst16 { - continue - } - t := i.Type - if !(z.Op != OpConst16 && x.Op != OpConst16) { - continue - } - v.reset(OpSub16) - v0 := b.NewValue0(v.Pos, OpSub16, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) - return true + call := v_0 + if call.Op != OpStaticCall || len(call.Args) != 1 { + break } - break - } - // match: (Sub16 (Sub16 i:(Const16 ) z) x) - // cond: (z.Op != OpConst16 && x.Op != OpConst16) - // result: (Sub16 i (Add16 z x)) - for { - if v_0.Op != OpSub16 { + sym := auxToCall(call.Aux) + s1 := call.Args[0] + if s1.Op != OpStore { break } - z := v_0.Args[1] - i := v_0.Args[0] - if i.Op != OpConst16 { + _ = s1.Args[2] + s1_1 := s1.Args[1] + if s1_1.Op != OpConst64 { break } - t := i.Type - x := v_1 - if !(z.Op != OpConst16 && x.Op != OpConst16) { + sz := auxIntToInt64(s1_1.AuxInt) + s2 := s1.Args[2] + if s2.Op != OpStore { break } - v.reset(OpSub16) - v0 := b.NewValue0(v.Pos, OpAdd16, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) + _ = s2.Args[2] + src := s2.Args[1] + s3 := s2.Args[2] + if s3.Op != OpStore { + break + } + t := auxToType(s3.Aux) + mem := s3.Args[2] + dst := s3.Args[1] + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(t.Elem()) + v.AddArg3(dst, src, mem) return true } - // match: (Sub16 (Add16 z i:(Const16 )) x) - // cond: (z.Op != OpConst16 && x.Op != OpConst16) - // result: (Add16 i (Sub16 z x)) + // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))) + // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call) + // result: (Move {t.Elem()} [int64(sz)] dst src mem) for { - if v_0.Op != OpAdd16 { + if auxIntToInt64(v.AuxInt) != 0 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z := v_0_0 - i := v_0_1 - if i.Op != OpConst16 { - continue - } - t := i.Type - x := v_1 - if !(z.Op != OpConst16 && x.Op != OpConst16) { - continue - } - v.reset(OpAdd16) - v0 := b.NewValue0(v.Pos, OpSub16, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + call := v_0 + if call.Op != OpStaticCall || len(call.Args) != 1 { + break } - break - } - // match: (Sub16 (Const16 [c]) (Sub16 (Const16 [d]) x)) - // result: (Add16 (Const16 [c-d]) x) - for { - if v_0.Op != OpConst16 { + sym := auxToCall(call.Aux) + s1 := call.Args[0] + if s1.Op != OpStore { break } - t := v_0.Type - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpSub16 { + _ = s1.Args[2] + s1_1 := s1.Args[1] + if s1_1.Op != OpConst32 { break } - x := v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpConst16 || v_1_0.Type != t { + sz := auxIntToInt32(s1_1.AuxInt) + s2 := s1.Args[2] + if s2.Op != OpStore { break } - d := auxIntToInt16(v_1_0.AuxInt) - v.reset(OpAdd16) - v0 := b.NewValue0(v.Pos, OpConst16, t) - v0.AuxInt = int16ToAuxInt(c - d) - v.AddArg2(v0, x) + _ = s2.Args[2] + src := s2.Args[1] + s3 := s2.Args[2] + if s3.Op != OpStore { + break + } + t := auxToType(s3.Aux) + mem := s3.Args[2] + dst := s3.Args[1] + if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(t.Elem()) + v.AddArg3(dst, src, mem) return true } - // match: (Sub16 (Const16 [c]) (Add16 (Const16 [d]) x)) - // result: (Sub16 (Const16 [c-d]) x) + // match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem)) + // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) + // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) for { - if v_0.Op != OpConst16 { + if auxIntToInt64(v.AuxInt) != 0 { break } - t := v_0.Type - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpAdd16 { + call := v_0 + if call.Op != OpStaticCall || len(call.Args) != 4 { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst16 || v_1_0.Type != t { - continue - } - d := auxIntToInt16(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpSub16) - v0 := b.NewValue0(v.Pos, OpConst16, t) - v0.AuxInt = int16ToAuxInt(c - d) - v.AddArg2(v0, x) - return true + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpConst64 { + break } - break + sz := auxIntToInt64(call_2.AuxInt) + if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(dst.Type.Elem()) + v.AddArg3(dst, src, mem) + return true } - return false -} -func rewriteValuegeneric_OpSub32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Sub32 (Const32 [c]) (Const32 [d])) - // result: (Const32 [c-d]) + // match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem)) + // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) + // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) for { - if v_0.Op != OpConst32 { + if auxIntToInt64(v.AuxInt) != 0 { break } - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpConst32 { + call := v_0 + if call.Op != OpStaticCall || len(call.Args) != 4 { break } - d := auxIntToInt32(v_1.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(c - d) + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpConst32 { + break + } + sz := auxIntToInt32(call_2.AuxInt) + if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(dst.Type.Elem()) + v.AddArg3(dst, src, mem) return true } - // match: (Sub32 x (Const32 [c])) - // cond: x.Op != OpConst32 - // result: (Add32 (Const32 [-c]) x) + // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem)) + // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) + // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) for { - x := v_0 - if v_1.Op != OpConst32 { + if auxIntToInt64(v.AuxInt) != 0 { break } - t := v_1.Type - c := auxIntToInt32(v_1.AuxInt) - if !(x.Op != OpConst32) { + call := v_0 + if call.Op != OpStaticLECall || len(call.Args) != 4 { break } - v.reset(OpAdd32) - v0 := b.NewValue0(v.Pos, OpConst32, t) - v0.AuxInt = int32ToAuxInt(-c) - v.AddArg2(v0, x) + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpConst64 { + break + } + sz := auxIntToInt64(call_2.AuxInt) + if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(dst.Type.Elem()) + v.AddArg3(dst, src, mem) return true } - // match: (Sub32 (Mul32 x y) (Mul32 x z)) - // result: (Mul32 x (Sub32 y z)) + // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem)) + // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call) + // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem) for { - t := v.Type - if v_0.Op != OpMul32 { + if auxIntToInt64(v.AuxInt) != 0 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if v_1.Op != OpMul32 { - continue - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - z := v_1_1 - v.reset(OpMul32) - v0 := b.NewValue0(v.Pos, OpSub32, t) - v0.AddArg2(y, z) - v.AddArg2(x, v0) - return true - } - } - break - } - // match: (Sub32 x x) - // result: (Const32 [0]) - for { - x := v_0 - if x != v_1 { + call := v_0 + if call.Op != OpStaticLECall || len(call.Args) != 4 { break } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (Sub32 (Add32 x y) x) - // result: y - for { - if v_0.Op != OpAdd32 { + sym := auxToCall(call.Aux) + mem := call.Args[3] + dst := call.Args[0] + src := call.Args[1] + call_2 := call.Args[2] + if call_2.Op != OpConst32 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if x != v_1 { - continue - } - v.copyOf(y) - return true + sz := auxIntToInt32(call_2.AuxInt) + if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) { + break } - break + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(int64(sz)) + v.Aux = typeToAux(dst.Type.Elem()) + v.AddArg3(dst, src, mem) + return true } - // match: (Sub32 (Add32 x y) y) + // match: (SelectN [0] call:(StaticLECall {sym} a x)) + // cond: needRaceCleanup(sym, call) && clobber(call) // result: x for { - if v_0.Op != OpAdd32 { + if auxIntToInt64(v.AuxInt) != 0 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if y != v_1 { - continue - } - v.copyOf(x) - return true + call := v_0 + if call.Op != OpStaticLECall || len(call.Args) != 2 { + break } - break + sym := auxToCall(call.Aux) + x := call.Args[1] + if !(needRaceCleanup(sym, call) && clobber(call)) { + break + } + v.copyOf(x) + return true } - // match: (Sub32 x (Sub32 i:(Const32 ) z)) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Sub32 (Add32 x z) i) + // match: (SelectN [0] call:(StaticLECall {sym} x)) + // cond: needRaceCleanup(sym, call) && clobber(call) + // result: x for { - x := v_0 - if v_1.Op != OpSub32 { + if auxIntToInt64(v.AuxInt) != 0 { break } - z := v_1.Args[1] - i := v_1.Args[0] - if i.Op != OpConst32 { + call := v_0 + if call.Op != OpStaticLECall || len(call.Args) != 1 { break } - t := i.Type - if !(z.Op != OpConst32 && x.Op != OpConst32) { + sym := auxToCall(call.Aux) + x := call.Args[0] + if !(needRaceCleanup(sym, call) && clobber(call)) { break } - v.reset(OpSub32) - v0 := b.NewValue0(v.Pos, OpAdd32, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) + v.copyOf(x) return true } - // match: (Sub32 x (Add32 z i:(Const32 ))) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Sub32 (Sub32 x z) i) + return false +} +func rewriteValuegeneric_OpSignExt16to32(v *Value) bool { + v_0 := v.Args[0] + // match: (SignExt16to32 (Const16 [c])) + // result: (Const32 [int32(c)]) for { - x := v_0 - if v_1.Op != OpAdd32 { + if v_0.Op != OpConst16 { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z := v_1_0 - i := v_1_1 - if i.Op != OpConst32 { - continue - } - t := i.Type - if !(z.Op != OpConst32 && x.Op != OpConst32) { - continue - } - v.reset(OpSub32) - v0 := b.NewValue0(v.Pos, OpSub32, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) - return true - } - break + c := auxIntToInt16(v_0.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(int32(c)) + return true } - // match: (Sub32 (Sub32 i:(Const32 ) z) x) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Sub32 i (Add32 z x)) + // match: (SignExt16to32 (Trunc32to16 x:(Rsh32x64 _ (Const64 [s])))) + // cond: s >= 16 + // result: x for { - if v_0.Op != OpSub32 { + if v_0.Op != OpTrunc32to16 { break } - z := v_0.Args[1] - i := v_0.Args[0] - if i.Op != OpConst32 { + x := v_0.Args[0] + if x.Op != OpRsh32x64 { break } - t := i.Type - x := v_1 - if !(z.Op != OpConst32 && x.Op != OpConst32) { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - v.reset(OpSub32) - v0 := b.NewValue0(v.Pos, OpAdd32, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 16) { + break + } + v.copyOf(x) return true } - // match: (Sub32 (Add32 z i:(Const32 )) x) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Add32 i (Sub32 z x)) + return false +} +func rewriteValuegeneric_OpSignExt16to64(v *Value) bool { + v_0 := v.Args[0] + // match: (SignExt16to64 (Const16 [c])) + // result: (Const64 [int64(c)]) for { - if v_0.Op != OpAdd32 { + if v_0.Op != OpConst16 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z := v_0_0 - i := v_0_1 - if i.Op != OpConst32 { - continue - } - t := i.Type - x := v_1 - if !(z.Op != OpConst32 && x.Op != OpConst32) { - continue - } - v.reset(OpAdd32) - v0 := b.NewValue0(v.Pos, OpSub32, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true - } - break + c := auxIntToInt16(v_0.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(int64(c)) + return true } - // match: (Sub32 (Const32 [c]) (Sub32 (Const32 [d]) x)) - // result: (Add32 (Const32 [c-d]) x) + // match: (SignExt16to64 (Trunc64to16 x:(Rsh64x64 _ (Const64 [s])))) + // cond: s >= 48 + // result: x for { - if v_0.Op != OpConst32 { + if v_0.Op != OpTrunc64to16 { break } - t := v_0.Type - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpSub32 { + x := v_0.Args[0] + if x.Op != OpRsh64x64 { break } - x := v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpConst32 || v_1_0.Type != t { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - d := auxIntToInt32(v_1_0.AuxInt) - v.reset(OpAdd32) - v0 := b.NewValue0(v.Pos, OpConst32, t) - v0.AuxInt = int32ToAuxInt(c - d) - v.AddArg2(v0, x) + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 48) { + break + } + v.copyOf(x) return true } - // match: (Sub32 (Const32 [c]) (Add32 (Const32 [d]) x)) - // result: (Sub32 (Const32 [c-d]) x) + return false +} +func rewriteValuegeneric_OpSignExt32to64(v *Value) bool { + v_0 := v.Args[0] + // match: (SignExt32to64 (Const32 [c])) + // result: (Const64 [int64(c)]) for { if v_0.Op != OpConst32 { break } - t := v_0.Type c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpAdd32 { - break - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst32 || v_1_0.Type != t { - continue - } - d := auxIntToInt32(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpSub32) - v0 := b.NewValue0(v.Pos, OpConst32, t) - v0.AuxInt = int32ToAuxInt(c - d) - v.AddArg2(v0, x) - return true - } - break + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(int64(c)) + return true } - return false -} -func rewriteValuegeneric_OpSub32F(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (Sub32F (Const32F [c]) (Const32F [d])) - // cond: c-d == c-d - // result: (Const32F [c-d]) + // match: (SignExt32to64 (Trunc64to32 x:(Rsh64x64 _ (Const64 [s])))) + // cond: s >= 32 + // result: x for { - if v_0.Op != OpConst32F { + if v_0.Op != OpTrunc64to32 { break } - c := auxIntToFloat32(v_0.AuxInt) - if v_1.Op != OpConst32F { + x := v_0.Args[0] + if x.Op != OpRsh64x64 { break } - d := auxIntToFloat32(v_1.AuxInt) - if !(c-d == c-d) { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - v.reset(OpConst32F) - v.AuxInt = float32ToAuxInt(c - d) + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 32) { + break + } + v.copyOf(x) return true } return false } -func rewriteValuegeneric_OpSub64(v *Value) bool { - v_1 := v.Args[1] +func rewriteValuegeneric_OpSignExt8to16(v *Value) bool { v_0 := v.Args[0] - b := v.Block - // match: (Sub64 (Const64 [c]) (Const64 [d])) - // result: (Const64 [c-d]) + // match: (SignExt8to16 (Const8 [c])) + // result: (Const16 [int16(c)]) for { - if v_0.Op != OpConst64 { - break - } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpConst64 { + if v_0.Op != OpConst8 { break } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(c - d) + c := auxIntToInt8(v_0.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(int16(c)) return true } - // match: (Sub64 x (Const64 [c])) - // cond: x.Op != OpConst64 - // result: (Add64 (Const64 [-c]) x) + // match: (SignExt8to16 (Trunc16to8 x:(Rsh16x64 _ (Const64 [s])))) + // cond: s >= 8 + // result: x for { - x := v_0 - if v_1.Op != OpConst64 { + if v_0.Op != OpTrunc16to8 { break } - t := v_1.Type - c := auxIntToInt64(v_1.AuxInt) - if !(x.Op != OpConst64) { + x := v_0.Args[0] + if x.Op != OpRsh16x64 { break } - v.reset(OpAdd64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(-c) - v.AddArg2(v0, x) - return true - } - // match: (Sub64 (Mul64 x y) (Mul64 x z)) - // result: (Mul64 x (Sub64 y z)) - for { - t := v.Type - if v_0.Op != OpMul64 { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if v_1.Op != OpMul64 { - continue - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - z := v_1_1 - v.reset(OpMul64) - v0 := b.NewValue0(v.Pos, OpSub64, t) - v0.AddArg2(y, z) - v.AddArg2(x, v0) - return true - } - } - break - } - // match: (Sub64 x x) - // result: (Const64 [0]) - for { - x := v_0 - if x != v_1 { + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 8) { break } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) + v.copyOf(x) return true } - // match: (Sub64 (Add64 x y) x) - // result: y + return false +} +func rewriteValuegeneric_OpSignExt8to32(v *Value) bool { + v_0 := v.Args[0] + // match: (SignExt8to32 (Const8 [c])) + // result: (Const32 [int32(c)]) for { - if v_0.Op != OpAdd64 { + if v_0.Op != OpConst8 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if x != v_1 { - continue - } - v.copyOf(y) - return true - } - break + c := auxIntToInt8(v_0.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(int32(c)) + return true } - // match: (Sub64 (Add64 x y) y) + // match: (SignExt8to32 (Trunc32to8 x:(Rsh32x64 _ (Const64 [s])))) + // cond: s >= 24 // result: x for { - if v_0.Op != OpAdd64 { + if v_0.Op != OpTrunc32to8 { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if y != v_1 { - continue - } - v.copyOf(x) - return true - } - break - } - // match: (Sub64 x (Sub64 i:(Const64 ) z)) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Sub64 (Add64 x z) i) - for { - x := v_0 - if v_1.Op != OpSub64 { + x := v_0.Args[0] + if x.Op != OpRsh32x64 { break } - z := v_1.Args[1] - i := v_1.Args[0] - if i.Op != OpConst64 { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - t := i.Type - if !(z.Op != OpConst64 && x.Op != OpConst64) { + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 24) { break } - v.reset(OpSub64) - v0 := b.NewValue0(v.Pos, OpAdd64, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) + v.copyOf(x) return true } - // match: (Sub64 x (Add64 z i:(Const64 ))) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Sub64 (Sub64 x z) i) + return false +} +func rewriteValuegeneric_OpSignExt8to64(v *Value) bool { + v_0 := v.Args[0] + // match: (SignExt8to64 (Const8 [c])) + // result: (Const64 [int64(c)]) for { - x := v_0 - if v_1.Op != OpAdd64 { + if v_0.Op != OpConst8 { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z := v_1_0 - i := v_1_1 - if i.Op != OpConst64 { - continue - } - t := i.Type - if !(z.Op != OpConst64 && x.Op != OpConst64) { - continue - } - v.reset(OpSub64) - v0 := b.NewValue0(v.Pos, OpSub64, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) - return true - } - break + c := auxIntToInt8(v_0.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(int64(c)) + return true } - // match: (Sub64 (Sub64 i:(Const64 ) z) x) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Sub64 i (Add64 z x)) + // match: (SignExt8to64 (Trunc64to8 x:(Rsh64x64 _ (Const64 [s])))) + // cond: s >= 56 + // result: x for { - if v_0.Op != OpSub64 { + if v_0.Op != OpTrunc64to8 { break } - z := v_0.Args[1] - i := v_0.Args[0] - if i.Op != OpConst64 { + x := v_0.Args[0] + if x.Op != OpRsh64x64 { break } - t := i.Type - x := v_1 - if !(z.Op != OpConst64 && x.Op != OpConst64) { + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpConst64 { break } - v.reset(OpSub64) - v0 := b.NewValue0(v.Pos, OpAdd64, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) + s := auxIntToInt64(x_1.AuxInt) + if !(s >= 56) { + break + } + v.copyOf(x) return true } - // match: (Sub64 (Add64 z i:(Const64 )) x) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Add64 i (Sub64 z x)) + return false +} +func rewriteValuegeneric_OpSliceCap(v *Value) bool { + v_0 := v.Args[0] + // match: (SliceCap (SliceMake _ _ (Const64 [c]))) + // result: (Const64 [c]) for { - if v_0.Op != OpAdd64 { + if v_0.Op != OpSliceMake { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z := v_0_0 - i := v_0_1 - if i.Op != OpConst64 { - continue - } - t := i.Type - x := v_1 - if !(z.Op != OpConst64 && x.Op != OpConst64) { - continue - } - v.reset(OpAdd64) - v0 := b.NewValue0(v.Pos, OpSub64, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + _ = v_0.Args[2] + v_0_2 := v_0.Args[2] + if v_0_2.Op != OpConst64 { + break } - break + t := v_0_2.Type + c := auxIntToInt64(v_0_2.AuxInt) + v.reset(OpConst64) + v.Type = t + v.AuxInt = int64ToAuxInt(c) + return true } - // match: (Sub64 (Const64 [c]) (Sub64 (Const64 [d]) x)) - // result: (Add64 (Const64 [c-d]) x) + // match: (SliceCap (SliceMake _ _ (Const32 [c]))) + // result: (Const32 [c]) for { - if v_0.Op != OpConst64 { - break - } - t := v_0.Type - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpSub64 { + if v_0.Op != OpSliceMake { break } - x := v_1.Args[1] - v_1_0 := v_1.Args[0] - if v_1_0.Op != OpConst64 || v_1_0.Type != t { + _ = v_0.Args[2] + v_0_2 := v_0.Args[2] + if v_0_2.Op != OpConst32 { break } - d := auxIntToInt64(v_1_0.AuxInt) - v.reset(OpAdd64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c - d) - v.AddArg2(v0, x) + t := v_0_2.Type + c := auxIntToInt32(v_0_2.AuxInt) + v.reset(OpConst32) + v.Type = t + v.AuxInt = int32ToAuxInt(c) return true } - // match: (Sub64 (Const64 [c]) (Add64 (Const64 [d]) x)) - // result: (Sub64 (Const64 [c-d]) x) + // match: (SliceCap (SliceMake _ _ (SliceCap x))) + // result: (SliceCap x) for { - if v_0.Op != OpConst64 { + if v_0.Op != OpSliceMake { break } - t := v_0.Type - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpAdd64 { + _ = v_0.Args[2] + v_0_2 := v_0.Args[2] + if v_0_2.Op != OpSliceCap { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst64 || v_1_0.Type != t { - continue - } - d := auxIntToInt64(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpSub64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c - d) - v.AddArg2(v0, x) - return true - } - break + x := v_0_2.Args[0] + v.reset(OpSliceCap) + v.AddArg(x) + return true } - return false -} -func rewriteValuegeneric_OpSub64F(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - // match: (Sub64F (Const64F [c]) (Const64F [d])) - // cond: c-d == c-d - // result: (Const64F [c-d]) + // match: (SliceCap (SliceMake _ _ (SliceLen x))) + // result: (SliceLen x) for { - if v_0.Op != OpConst64F { - break - } - c := auxIntToFloat64(v_0.AuxInt) - if v_1.Op != OpConst64F { + if v_0.Op != OpSliceMake { break } - d := auxIntToFloat64(v_1.AuxInt) - if !(c-d == c-d) { + _ = v_0.Args[2] + v_0_2 := v_0.Args[2] + if v_0_2.Op != OpSliceLen { break } - v.reset(OpConst64F) - v.AuxInt = float64ToAuxInt(c - d) + x := v_0_2.Args[0] + v.reset(OpSliceLen) + v.AddArg(x) return true } return false } -func rewriteValuegeneric_OpSub8(v *Value) bool { - v_1 := v.Args[1] +func rewriteValuegeneric_OpSliceLen(v *Value) bool { v_0 := v.Args[0] - b := v.Block - // match: (Sub8 (Const8 [c]) (Const8 [d])) - // result: (Const8 [c-d]) + // match: (SliceLen (SliceMake _ (Const64 [c]) _)) + // result: (Const64 [c]) for { - if v_0.Op != OpConst8 { + if v_0.Op != OpSliceMake { break } - c := auxIntToInt8(v_0.AuxInt) - if v_1.Op != OpConst8 { + _ = v_0.Args[1] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { break } - d := auxIntToInt8(v_1.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(c - d) + t := v_0_1.Type + c := auxIntToInt64(v_0_1.AuxInt) + v.reset(OpConst64) + v.Type = t + v.AuxInt = int64ToAuxInt(c) return true } - // match: (Sub8 x (Const8 [c])) - // cond: x.Op != OpConst8 - // result: (Add8 (Const8 [-c]) x) + // match: (SliceLen (SliceMake _ (Const32 [c]) _)) + // result: (Const32 [c]) for { - x := v_0 - if v_1.Op != OpConst8 { + if v_0.Op != OpSliceMake { break } - t := v_1.Type - c := auxIntToInt8(v_1.AuxInt) - if !(x.Op != OpConst8) { + _ = v_0.Args[1] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst32 { break } - v.reset(OpAdd8) - v0 := b.NewValue0(v.Pos, OpConst8, t) - v0.AuxInt = int8ToAuxInt(-c) - v.AddArg2(v0, x) + t := v_0_1.Type + c := auxIntToInt32(v_0_1.AuxInt) + v.reset(OpConst32) + v.Type = t + v.AuxInt = int32ToAuxInt(c) return true } - // match: (Sub8 (Mul8 x y) (Mul8 x z)) - // result: (Mul8 x (Sub8 y z)) + // match: (SliceLen (SliceMake _ (SliceLen x) _)) + // result: (SliceLen x) for { - t := v.Type - if v_0.Op != OpMul8 { + if v_0.Op != OpSliceMake { break } _ = v_0.Args[1] - v_0_0 := v_0.Args[0] v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if v_1.Op != OpMul8 { - continue - } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - z := v_1_1 - v.reset(OpMul8) - v0 := b.NewValue0(v.Pos, OpSub8, t) - v0.AddArg2(y, z) - v.AddArg2(x, v0) - return true - } - } - break - } - // match: (Sub8 x x) - // result: (Const8 [0]) - for { - x := v_0 - if x != v_1 { + if v_0_1.Op != OpSliceLen { break } - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(0) + x := v_0_1.Args[0] + v.reset(OpSliceLen) + v.AddArg(x) return true } - // match: (Sub8 (Add8 x y) x) - // result: y + return false +} +func rewriteValuegeneric_OpSlicePtr(v *Value) bool { + v_0 := v.Args[0] + // match: (SlicePtr (SliceMake (SlicePtr x) _ _)) + // result: (SlicePtr x) for { - if v_0.Op != OpAdd8 { + if v_0.Op != OpSliceMake { break } - _ = v_0.Args[1] v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if x != v_1 { - continue - } - v.copyOf(y) - return true - } - break - } - // match: (Sub8 (Add8 x y) y) - // result: x - for { - if v_0.Op != OpAdd8 { + if v_0_0.Op != OpSlicePtr { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - x := v_0_0 - y := v_0_1 - if y != v_1 { - continue - } - v.copyOf(x) - return true - } - break + x := v_0_0.Args[0] + v.reset(OpSlicePtr) + v.AddArg(x) + return true } - // match: (Sub8 x (Sub8 i:(Const8 ) z)) - // cond: (z.Op != OpConst8 && x.Op != OpConst8) - // result: (Sub8 (Add8 x z) i) + return false +} +func rewriteValuegeneric_OpSlicemask(v *Value) bool { + v_0 := v.Args[0] + // match: (Slicemask (Const32 [x])) + // cond: x > 0 + // result: (Const32 [-1]) for { - x := v_0 - if v_1.Op != OpSub8 { - break - } - z := v_1.Args[1] - i := v_1.Args[0] - if i.Op != OpConst8 { + if v_0.Op != OpConst32 { break } - t := i.Type - if !(z.Op != OpConst8 && x.Op != OpConst8) { + x := auxIntToInt32(v_0.AuxInt) + if !(x > 0) { break } - v.reset(OpSub8) - v0 := b.NewValue0(v.Pos, OpAdd8, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(-1) return true } - // match: (Sub8 x (Add8 z i:(Const8 ))) - // cond: (z.Op != OpConst8 && x.Op != OpConst8) - // result: (Sub8 (Sub8 x z) i) + // match: (Slicemask (Const32 [0])) + // result: (Const32 [0]) for { - x := v_0 - if v_1.Op != OpAdd8 { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { break } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - z := v_1_0 - i := v_1_1 - if i.Op != OpConst8 { - continue - } - t := i.Type - if !(z.Op != OpConst8 && x.Op != OpConst8) { - continue - } - v.reset(OpSub8) - v0 := b.NewValue0(v.Pos, OpSub8, t) - v0.AddArg2(x, z) - v.AddArg2(v0, i) - return true - } - break + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true } - // match: (Sub8 (Sub8 i:(Const8 ) z) x) - // cond: (z.Op != OpConst8 && x.Op != OpConst8) - // result: (Sub8 i (Add8 z x)) + // match: (Slicemask (Const64 [x])) + // cond: x > 0 + // result: (Const64 [-1]) for { - if v_0.Op != OpSub8 { + if v_0.Op != OpConst64 { break } - z := v_0.Args[1] - i := v_0.Args[0] - if i.Op != OpConst8 { + x := auxIntToInt64(v_0.AuxInt) + if !(x > 0) { break } - t := i.Type - x := v_1 - if !(z.Op != OpConst8 && x.Op != OpConst8) { + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(-1) + return true + } + // match: (Slicemask (Const64 [0])) + // result: (Const64 [0]) + for { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { break } - v.reset(OpSub8) - v0 := b.NewValue0(v.Pos, OpAdd8, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Sub8 (Add8 z i:(Const8 )) x) - // cond: (z.Op != OpConst8 && x.Op != OpConst8) - // result: (Add8 i (Sub8 z x)) + return false +} +func rewriteValuegeneric_OpSqrt(v *Value) bool { + v_0 := v.Args[0] + // match: (Sqrt (Const64F [c])) + // cond: !math.IsNaN(math.Sqrt(c)) + // result: (Const64F [math.Sqrt(c)]) for { - if v_0.Op != OpAdd8 { + if v_0.Op != OpConst64F { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - z := v_0_0 - i := v_0_1 - if i.Op != OpConst8 { - continue - } - t := i.Type - x := v_1 - if !(z.Op != OpConst8 && x.Op != OpConst8) { - continue - } - v.reset(OpAdd8) - v0 := b.NewValue0(v.Pos, OpSub8, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + c := auxIntToFloat64(v_0.AuxInt) + if !(!math.IsNaN(math.Sqrt(c))) { + break } - break + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(math.Sqrt(c)) + return true } - // match: (Sub8 (Const8 [c]) (Sub8 (Const8 [d]) x)) - // result: (Add8 (Const8 [c-d]) x) + return false +} +func rewriteValuegeneric_OpStaticLECall(v *Value) bool { + b := v.Block + config := b.Func.Config + typ := &b.Func.Config.Types + // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [1]) mem) + // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) + // result: (MakeResult (Eq8 (Load sptr mem) (Const8 [int8(read8(scon,0))])) mem) for { - if v_0.Op != OpConst8 { + if len(v.Args) != 4 { break } - t := v_0.Type - c := auxIntToInt8(v_0.AuxInt) - if v_1.Op != OpSub8 { + callAux := auxToCall(v.Aux) + mem := v.Args[3] + sptr := v.Args[0] + v_1 := v.Args[1] + if v_1.Op != OpAddr { break } - x := v_1.Args[1] + scon := auxToSym(v_1.Aux) v_1_0 := v_1.Args[0] - if v_1_0.Op != OpConst8 || v_1_0.Type != t { + if v_1_0.Op != OpSB { break } - d := auxIntToInt8(v_1_0.AuxInt) - v.reset(OpAdd8) - v0 := b.NewValue0(v.Pos, OpConst8, t) - v0.AuxInt = int8ToAuxInt(c - d) - v.AddArg2(v0, x) + v_2 := v.Args[2] + if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 1 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon)) { + break + } + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEq8, typ.Bool) + v1 := b.NewValue0(v.Pos, OpLoad, typ.Int8) + v1.AddArg2(sptr, mem) + v2 := b.NewValue0(v.Pos, OpConst8, typ.Int8) + v2.AuxInt = int8ToAuxInt(int8(read8(scon, 0))) + v0.AddArg2(v1, v2) + v.AddArg2(v0, mem) return true } - // match: (Sub8 (Const8 [c]) (Add8 (Const8 [d]) x)) - // result: (Sub8 (Const8 [c-d]) x) + // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [2]) mem) + // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) + // result: (MakeResult (Eq16 (Load sptr mem) (Const16 [int16(read16(scon,0,config.ctxt.Arch.ByteOrder))])) mem) for { - if v_0.Op != OpConst8 { + if len(v.Args) != 4 { break } - t := v_0.Type - c := auxIntToInt8(v_0.AuxInt) - if v_1.Op != OpAdd8 { + callAux := auxToCall(v.Aux) + mem := v.Args[3] + sptr := v.Args[0] + v_1 := v.Args[1] + if v_1.Op != OpAddr { break } - _ = v_1.Args[1] + scon := auxToSym(v_1.Aux) v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst8 || v_1_0.Type != t { - continue - } - d := auxIntToInt8(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpSub8) - v0 := b.NewValue0(v.Pos, OpConst8, t) - v0.AuxInt = int8ToAuxInt(c - d) - v.AddArg2(v0, x) - return true + if v_1_0.Op != OpSB { + break } - break - } - return false -} -func rewriteValuegeneric_OpTrunc16to8(v *Value) bool { - v_0 := v.Args[0] - // match: (Trunc16to8 (Const16 [c])) - // result: (Const8 [int8(c)]) - for { - if v_0.Op != OpConst16 { + v_2 := v.Args[2] + if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 2 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)) { break } - c := auxIntToInt16(v_0.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(int8(c)) + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEq16, typ.Bool) + v1 := b.NewValue0(v.Pos, OpLoad, typ.Int16) + v1.AddArg2(sptr, mem) + v2 := b.NewValue0(v.Pos, OpConst16, typ.Int16) + v2.AuxInt = int16ToAuxInt(int16(read16(scon, 0, config.ctxt.Arch.ByteOrder))) + v0.AddArg2(v1, v2) + v.AddArg2(v0, mem) return true } - // match: (Trunc16to8 (ZeroExt8to16 x)) - // result: x + // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [4]) mem) + // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) + // result: (MakeResult (Eq32 (Load sptr mem) (Const32 [int32(read32(scon,0,config.ctxt.Arch.ByteOrder))])) mem) for { - if v_0.Op != OpZeroExt8to16 { + if len(v.Args) != 4 { break } - x := v_0.Args[0] - v.copyOf(x) + callAux := auxToCall(v.Aux) + mem := v.Args[3] + sptr := v.Args[0] + v_1 := v.Args[1] + if v_1.Op != OpAddr { + break + } + scon := auxToSym(v_1.Aux) + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSB { + break + } + v_2 := v.Args[2] + if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 4 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)) { + break + } + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEq32, typ.Bool) + v1 := b.NewValue0(v.Pos, OpLoad, typ.Int32) + v1.AddArg2(sptr, mem) + v2 := b.NewValue0(v.Pos, OpConst32, typ.Int32) + v2.AuxInt = int32ToAuxInt(int32(read32(scon, 0, config.ctxt.Arch.ByteOrder))) + v0.AddArg2(v1, v2) + v.AddArg2(v0, mem) return true } - // match: (Trunc16to8 (SignExt8to16 x)) - // result: x + // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [8]) mem) + // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) && config.PtrSize == 8 + // result: (MakeResult (Eq64 (Load sptr mem) (Const64 [int64(read64(scon,0,config.ctxt.Arch.ByteOrder))])) mem) for { - if v_0.Op != OpSignExt8to16 { + if len(v.Args) != 4 { break } - x := v_0.Args[0] - v.copyOf(x) - return true - } - // match: (Trunc16to8 (And16 (Const16 [y]) x)) - // cond: y&0xFF == 0xFF - // result: (Trunc16to8 x) - for { - if v_0.Op != OpAnd16 { + callAux := auxToCall(v.Aux) + mem := v.Args[3] + sptr := v.Args[0] + v_1 := v.Args[1] + if v_1.Op != OpAddr { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst16 { - continue - } - y := auxIntToInt16(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFF == 0xFF) { - continue - } - v.reset(OpTrunc16to8) - v.AddArg(x) - return true + scon := auxToSym(v_1.Aux) + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpSB { + break } - break + v_2 := v.Args[2] + if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 8 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) && config.PtrSize == 8) { + break + } + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEq64, typ.Bool) + v1 := b.NewValue0(v.Pos, OpLoad, typ.Int64) + v1.AddArg2(sptr, mem) + v2 := b.NewValue0(v.Pos, OpConst64, typ.Int64) + v2.AuxInt = int64ToAuxInt(int64(read64(scon, 0, config.ctxt.Arch.ByteOrder))) + v0.AddArg2(v1, v2) + v.AddArg2(v0, mem) + return true } return false } -func rewriteValuegeneric_OpTrunc32to16(v *Value) bool { +func rewriteValuegeneric_OpStore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (Trunc32to16 (Const32 [c])) - // result: (Const16 [int16(c)]) + b := v.Block + fe := b.Func.fe + // match: (Store {t1} p1 (Load p2 mem) mem) + // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() + // result: mem for { - if v_0.Op != OpConst32 { + t1 := auxToType(v.Aux) + p1 := v_0 + if v_1.Op != OpLoad { break } - c := auxIntToInt32(v_0.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(int16(c)) + t2 := v_1.Type + mem := v_1.Args[1] + p2 := v_1.Args[0] + if mem != v_2 || !(isSamePtr(p1, p2) && t2.Size() == t1.Size()) { + break + } + v.copyOf(mem) return true } - // match: (Trunc32to16 (ZeroExt8to32 x)) - // result: (ZeroExt8to16 x) + // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ oldmem)) + // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) + // result: mem for { - if v_0.Op != OpZeroExt8to32 { + t1 := auxToType(v.Aux) + p1 := v_0 + if v_1.Op != OpLoad { break } - x := v_0.Args[0] - v.reset(OpZeroExt8to16) - v.AddArg(x) + t2 := v_1.Type + oldmem := v_1.Args[1] + p2 := v_1.Args[0] + mem := v_2 + if mem.Op != OpStore { + break + } + t3 := auxToType(mem.Aux) + _ = mem.Args[2] + p3 := mem.Args[0] + if oldmem != mem.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size())) { + break + } + v.copyOf(mem) return true } - // match: (Trunc32to16 (ZeroExt16to32 x)) - // result: x + // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ (Store {t4} p4 _ oldmem))) + // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) + // result: mem for { - if v_0.Op != OpZeroExt16to32 { + t1 := auxToType(v.Aux) + p1 := v_0 + if v_1.Op != OpLoad { break } - x := v_0.Args[0] - v.copyOf(x) + t2 := v_1.Type + oldmem := v_1.Args[1] + p2 := v_1.Args[0] + mem := v_2 + if mem.Op != OpStore { + break + } + t3 := auxToType(mem.Aux) + _ = mem.Args[2] + p3 := mem.Args[0] + mem_2 := mem.Args[2] + if mem_2.Op != OpStore { + break + } + t4 := auxToType(mem_2.Aux) + _ = mem_2.Args[2] + p4 := mem_2.Args[0] + if oldmem != mem_2.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size())) { + break + } + v.copyOf(mem) return true } - // match: (Trunc32to16 (SignExt8to32 x)) - // result: (SignExt8to16 x) + // match: (Store {t1} p1 (Load p2 oldmem) mem:(Store {t3} p3 _ (Store {t4} p4 _ (Store {t5} p5 _ oldmem)))) + // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) && disjoint(p1, t1.Size(), p5, t5.Size()) + // result: mem for { - if v_0.Op != OpSignExt8to32 { + t1 := auxToType(v.Aux) + p1 := v_0 + if v_1.Op != OpLoad { break } - x := v_0.Args[0] - v.reset(OpSignExt8to16) - v.AddArg(x) + t2 := v_1.Type + oldmem := v_1.Args[1] + p2 := v_1.Args[0] + mem := v_2 + if mem.Op != OpStore { + break + } + t3 := auxToType(mem.Aux) + _ = mem.Args[2] + p3 := mem.Args[0] + mem_2 := mem.Args[2] + if mem_2.Op != OpStore { + break + } + t4 := auxToType(mem_2.Aux) + _ = mem_2.Args[2] + p4 := mem_2.Args[0] + mem_2_2 := mem_2.Args[2] + if mem_2_2.Op != OpStore { + break + } + t5 := auxToType(mem_2_2.Aux) + _ = mem_2_2.Args[2] + p5 := mem_2_2.Args[0] + if oldmem != mem_2_2.Args[2] || !(isSamePtr(p1, p2) && t2.Size() == t1.Size() && disjoint(p1, t1.Size(), p3, t3.Size()) && disjoint(p1, t1.Size(), p4, t4.Size()) && disjoint(p1, t1.Size(), p5, t5.Size())) { + break + } + v.copyOf(mem) return true } - // match: (Trunc32to16 (SignExt16to32 x)) - // result: x + // match: (Store {t} (OffPtr [o] p1) x mem:(Zero [n] p2 _)) + // cond: isConstZero(x) && o >= 0 && t.Size() + o <= n && isSamePtr(p1, p2) + // result: mem for { - if v_0.Op != OpSignExt16to32 { + t := auxToType(v.Aux) + if v_0.Op != OpOffPtr { break } - x := v_0.Args[0] - v.copyOf(x) + o := auxIntToInt64(v_0.AuxInt) + p1 := v_0.Args[0] + x := v_1 + mem := v_2 + if mem.Op != OpZero { + break + } + n := auxIntToInt64(mem.AuxInt) + p2 := mem.Args[0] + if !(isConstZero(x) && o >= 0 && t.Size()+o <= n && isSamePtr(p1, p2)) { + break + } + v.copyOf(mem) return true } - // match: (Trunc32to16 (And32 (Const32 [y]) x)) - // cond: y&0xFFFF == 0xFFFF - // result: (Trunc32to16 x) + // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Zero [n] p3 _))) + // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p3) && disjoint(op, t1.Size(), p2, t2.Size()) + // result: mem for { - if v_0.Op != OpAnd32 { + t1 := auxToType(v.Aux) + op := v_0 + if op.Op != OpOffPtr { break } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst32 { + o1 := auxIntToInt64(op.AuxInt) + p1 := op.Args[0] + x := v_1 + mem := v_2 + if mem.Op != OpStore { + break + } + t2 := auxToType(mem.Aux) + _ = mem.Args[2] + p2 := mem.Args[0] + mem_2 := mem.Args[2] + if mem_2.Op != OpZero { + break + } + n := auxIntToInt64(mem_2.AuxInt) + p3 := mem_2.Args[0] + if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p3) && disjoint(op, t1.Size(), p2, t2.Size())) { + break + } + v.copyOf(mem) + return true + } + // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Store {t3} p3 _ (Zero [n] p4 _)))) + // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p4) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) + // result: mem + for { + t1 := auxToType(v.Aux) + op := v_0 + if op.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op.AuxInt) + p1 := op.Args[0] + x := v_1 + mem := v_2 + if mem.Op != OpStore { + break + } + t2 := auxToType(mem.Aux) + _ = mem.Args[2] + p2 := mem.Args[0] + mem_2 := mem.Args[2] + if mem_2.Op != OpStore { + break + } + t3 := auxToType(mem_2.Aux) + _ = mem_2.Args[2] + p3 := mem_2.Args[0] + mem_2_2 := mem_2.Args[2] + if mem_2_2.Op != OpZero { + break + } + n := auxIntToInt64(mem_2_2.AuxInt) + p4 := mem_2_2.Args[0] + if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p4) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size())) { + break + } + v.copyOf(mem) + return true + } + // match: (Store {t1} op:(OffPtr [o1] p1) x mem:(Store {t2} p2 _ (Store {t3} p3 _ (Store {t4} p4 _ (Zero [n] p5 _))))) + // cond: isConstZero(x) && o1 >= 0 && t1.Size() + o1 <= n && isSamePtr(p1, p5) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) && disjoint(op, t1.Size(), p4, t4.Size()) + // result: mem + for { + t1 := auxToType(v.Aux) + op := v_0 + if op.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op.AuxInt) + p1 := op.Args[0] + x := v_1 + mem := v_2 + if mem.Op != OpStore { + break + } + t2 := auxToType(mem.Aux) + _ = mem.Args[2] + p2 := mem.Args[0] + mem_2 := mem.Args[2] + if mem_2.Op != OpStore { + break + } + t3 := auxToType(mem_2.Aux) + _ = mem_2.Args[2] + p3 := mem_2.Args[0] + mem_2_2 := mem_2.Args[2] + if mem_2_2.Op != OpStore { + break + } + t4 := auxToType(mem_2_2.Aux) + _ = mem_2_2.Args[2] + p4 := mem_2_2.Args[0] + mem_2_2_2 := mem_2_2.Args[2] + if mem_2_2_2.Op != OpZero { + break + } + n := auxIntToInt64(mem_2_2_2.AuxInt) + p5 := mem_2_2_2.Args[0] + if !(isConstZero(x) && o1 >= 0 && t1.Size()+o1 <= n && isSamePtr(p1, p5) && disjoint(op, t1.Size(), p2, t2.Size()) && disjoint(op, t1.Size(), p3, t3.Size()) && disjoint(op, t1.Size(), p4, t4.Size())) { + break + } + v.copyOf(mem) + return true + } + // match: (Store _ (StructMake0) mem) + // result: mem + for { + if v_1.Op != OpStructMake0 { + break + } + mem := v_2 + v.copyOf(mem) + return true + } + // match: (Store dst (StructMake1 f0) mem) + // result: (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem) + for { + dst := v_0 + if v_1.Op != OpStructMake1 { + break + } + t := v_1.Type + f0 := v_1.Args[0] + mem := v_2 + v.reset(OpStore) + v.Aux = typeToAux(t.FieldType(0)) + v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) + v0.AuxInt = int64ToAuxInt(0) + v0.AddArg(dst) + v.AddArg3(v0, f0, mem) + return true + } + // match: (Store dst (StructMake2 f0 f1) mem) + // result: (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem)) + for { + dst := v_0 + if v_1.Op != OpStructMake2 { + break + } + t := v_1.Type + f1 := v_1.Args[1] + f0 := v_1.Args[0] + mem := v_2 + v.reset(OpStore) + v.Aux = typeToAux(t.FieldType(1)) + v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) + v0.AuxInt = int64ToAuxInt(t.FieldOff(1)) + v0.AddArg(dst) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t.FieldType(0)) + v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) + v2.AuxInt = int64ToAuxInt(0) + v2.AddArg(dst) + v1.AddArg3(v2, f0, mem) + v.AddArg3(v0, f1, v1) + return true + } + // match: (Store dst (StructMake3 f0 f1 f2) mem) + // result: (Store {t.FieldType(2)} (OffPtr [t.FieldOff(2)] dst) f2 (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem))) + for { + dst := v_0 + if v_1.Op != OpStructMake3 { + break + } + t := v_1.Type + f2 := v_1.Args[2] + f0 := v_1.Args[0] + f1 := v_1.Args[1] + mem := v_2 + v.reset(OpStore) + v.Aux = typeToAux(t.FieldType(2)) + v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(2).PtrTo()) + v0.AuxInt = int64ToAuxInt(t.FieldOff(2)) + v0.AddArg(dst) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t.FieldType(1)) + v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) + v2.AuxInt = int64ToAuxInt(t.FieldOff(1)) + v2.AddArg(dst) + v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v3.Aux = typeToAux(t.FieldType(0)) + v4 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) + v4.AuxInt = int64ToAuxInt(0) + v4.AddArg(dst) + v3.AddArg3(v4, f0, mem) + v1.AddArg3(v2, f1, v3) + v.AddArg3(v0, f2, v1) + return true + } + // match: (Store dst (StructMake4 f0 f1 f2 f3) mem) + // result: (Store {t.FieldType(3)} (OffPtr [t.FieldOff(3)] dst) f3 (Store {t.FieldType(2)} (OffPtr [t.FieldOff(2)] dst) f2 (Store {t.FieldType(1)} (OffPtr [t.FieldOff(1)] dst) f1 (Store {t.FieldType(0)} (OffPtr [0] dst) f0 mem)))) + for { + dst := v_0 + if v_1.Op != OpStructMake4 { + break + } + t := v_1.Type + f3 := v_1.Args[3] + f0 := v_1.Args[0] + f1 := v_1.Args[1] + f2 := v_1.Args[2] + mem := v_2 + v.reset(OpStore) + v.Aux = typeToAux(t.FieldType(3)) + v0 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(3).PtrTo()) + v0.AuxInt = int64ToAuxInt(t.FieldOff(3)) + v0.AddArg(dst) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t.FieldType(2)) + v2 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(2).PtrTo()) + v2.AuxInt = int64ToAuxInt(t.FieldOff(2)) + v2.AddArg(dst) + v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v3.Aux = typeToAux(t.FieldType(1)) + v4 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(1).PtrTo()) + v4.AuxInt = int64ToAuxInt(t.FieldOff(1)) + v4.AddArg(dst) + v5 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v5.Aux = typeToAux(t.FieldType(0)) + v6 := b.NewValue0(v.Pos, OpOffPtr, t.FieldType(0).PtrTo()) + v6.AuxInt = int64ToAuxInt(0) + v6.AddArg(dst) + v5.AddArg3(v6, f0, mem) + v3.AddArg3(v4, f1, v5) + v1.AddArg3(v2, f2, v3) + v.AddArg3(v0, f3, v1) + return true + } + // match: (Store {t} dst (Load src mem) mem) + // cond: !fe.CanSSA(t) + // result: (Move {t} [t.Size()] dst src mem) + for { + t := auxToType(v.Aux) + dst := v_0 + if v_1.Op != OpLoad { + break + } + mem := v_1.Args[1] + src := v_1.Args[0] + if mem != v_2 || !(!fe.CanSSA(t)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(t.Size()) + v.Aux = typeToAux(t) + v.AddArg3(dst, src, mem) + return true + } + // match: (Store {t} dst (Load src mem) (VarDef {x} mem)) + // cond: !fe.CanSSA(t) + // result: (Move {t} [t.Size()] dst src (VarDef {x} mem)) + for { + t := auxToType(v.Aux) + dst := v_0 + if v_1.Op != OpLoad { + break + } + mem := v_1.Args[1] + src := v_1.Args[0] + if v_2.Op != OpVarDef { + break + } + x := auxToSym(v_2.Aux) + if mem != v_2.Args[0] || !(!fe.CanSSA(t)) { + break + } + v.reset(OpMove) + v.AuxInt = int64ToAuxInt(t.Size()) + v.Aux = typeToAux(t) + v0 := b.NewValue0(v.Pos, OpVarDef, types.TypeMem) + v0.Aux = symToAux(x) + v0.AddArg(mem) + v.AddArg3(dst, src, v0) + return true + } + // match: (Store _ (ArrayMake0) mem) + // result: mem + for { + if v_1.Op != OpArrayMake0 { + break + } + mem := v_2 + v.copyOf(mem) + return true + } + // match: (Store dst (ArrayMake1 e) mem) + // result: (Store {e.Type} dst e mem) + for { + dst := v_0 + if v_1.Op != OpArrayMake1 { + break + } + e := v_1.Args[0] + mem := v_2 + v.reset(OpStore) + v.Aux = typeToAux(e.Type) + v.AddArg3(dst, e, mem) + return true + } + // match: (Store (SelectN [0] call:(StaticLECall _ _)) x mem:(SelectN [1] call)) + // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject") + // result: mem + for { + if v_0.Op != OpSelectN || auxIntToInt64(v_0.AuxInt) != 0 { + break + } + call := v_0.Args[0] + if call.Op != OpStaticLECall || len(call.Args) != 2 { + break + } + x := v_1 + mem := v_2 + if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) { + break + } + v.copyOf(mem) + return true + } + // match: (Store (OffPtr (SelectN [0] call:(StaticLECall _ _))) x mem:(SelectN [1] call)) + // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject") + // result: mem + for { + if v_0.Op != OpOffPtr { + break + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpSelectN || auxIntToInt64(v_0_0.AuxInt) != 0 { + break + } + call := v_0_0.Args[0] + if call.Op != OpStaticLECall || len(call.Args) != 2 { + break + } + x := v_1 + mem := v_2 + if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) { + break + } + v.copyOf(mem) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [0] p2) d2 m3:(Move [n] p3 _ mem))) + // cond: m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 mem)) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr || auxIntToInt64(op2.AuxInt) != 0 { + break + } + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpMove { + break + } + n := auxIntToInt64(m3.AuxInt) + mem := m3.Args[2] + p3 := m3.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v0.AddArg3(op2, d2, mem) + v.AddArg3(op1, d1, v0) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [0] p3) d3 m4:(Move [n] p4 _ mem)))) + // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 mem))) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr { + break + } + o2 := auxIntToInt64(op2.AuxInt) + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpStore { + break + } + t3 := auxToType(m3.Aux) + _ = m3.Args[2] + op3 := m3.Args[0] + if op3.Op != OpOffPtr || auxIntToInt64(op3.AuxInt) != 0 { + break + } + p3 := op3.Args[0] + d3 := m3.Args[1] + m4 := m3.Args[2] + if m4.Op != OpMove { + break + } + n := auxIntToInt64(m4.AuxInt) + mem := m4.Args[2] + p4 := m4.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t3) + v1.AddArg3(op3, d3, mem) + v0.AddArg3(op2, d2, v1) + v.AddArg3(op1, d1, v0) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [o3] p3) d3 m4:(Store {t4} op4:(OffPtr [0] p4) d4 m5:(Move [n] p5 _ mem))))) + // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size() + t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 (Store {t4} op4 d4 mem)))) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr { + break + } + o2 := auxIntToInt64(op2.AuxInt) + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpStore { + break + } + t3 := auxToType(m3.Aux) + _ = m3.Args[2] + op3 := m3.Args[0] + if op3.Op != OpOffPtr { + break + } + o3 := auxIntToInt64(op3.AuxInt) + p3 := op3.Args[0] + d3 := m3.Args[1] + m4 := m3.Args[2] + if m4.Op != OpStore { + break + } + t4 := auxToType(m4.Aux) + _ = m4.Args[2] + op4 := m4.Args[0] + if op4.Op != OpOffPtr || auxIntToInt64(op4.AuxInt) != 0 { + break + } + p4 := op4.Args[0] + d4 := m4.Args[1] + m5 := m4.Args[2] + if m5.Op != OpMove { + break + } + n := auxIntToInt64(m5.AuxInt) + mem := m5.Args[2] + p5 := m5.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size()+t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t3) + v2 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v2.Aux = typeToAux(t4) + v2.AddArg3(op4, d4, mem) + v1.AddArg3(op3, d3, v2) + v0.AddArg3(op2, d2, v1) + v.AddArg3(op1, d1, v0) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [0] p2) d2 m3:(Zero [n] p3 mem))) + // cond: m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 mem)) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr || auxIntToInt64(op2.AuxInt) != 0 { + break + } + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpZero { + break + } + n := auxIntToInt64(m3.AuxInt) + mem := m3.Args[1] + p3 := m3.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && o1 == t2.Size() && n == t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && clobber(m2, m3)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v0.AddArg3(op2, d2, mem) + v.AddArg3(op1, d1, v0) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [0] p3) d3 m4:(Zero [n] p4 mem)))) + // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 mem))) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr { + break + } + o2 := auxIntToInt64(op2.AuxInt) + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpStore { + break + } + t3 := auxToType(m3.Aux) + _ = m3.Args[2] + op3 := m3.Args[0] + if op3.Op != OpOffPtr || auxIntToInt64(op3.AuxInt) != 0 { + break + } + p3 := op3.Args[0] + d3 := m3.Args[1] + m4 := m3.Args[2] + if m4.Op != OpZero { + break + } + n := auxIntToInt64(m4.AuxInt) + mem := m4.Args[1] + p4 := m4.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && o2 == t3.Size() && o1-o2 == t2.Size() && n == t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && clobber(m2, m3, m4)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t3) + v1.AddArg3(op3, d3, mem) + v0.AddArg3(op2, d2, v1) + v.AddArg3(op1, d1, v0) + return true + } + // match: (Store {t1} op1:(OffPtr [o1] p1) d1 m2:(Store {t2} op2:(OffPtr [o2] p2) d2 m3:(Store {t3} op3:(OffPtr [o3] p3) d3 m4:(Store {t4} op4:(OffPtr [0] p4) d4 m5:(Zero [n] p5 mem))))) + // cond: m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size() + t3.Size() + t2.Size() + t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5) + // result: (Store {t1} op1 d1 (Store {t2} op2 d2 (Store {t3} op3 d3 (Store {t4} op4 d4 mem)))) + for { + t1 := auxToType(v.Aux) + op1 := v_0 + if op1.Op != OpOffPtr { + break + } + o1 := auxIntToInt64(op1.AuxInt) + p1 := op1.Args[0] + d1 := v_1 + m2 := v_2 + if m2.Op != OpStore { + break + } + t2 := auxToType(m2.Aux) + _ = m2.Args[2] + op2 := m2.Args[0] + if op2.Op != OpOffPtr { + break + } + o2 := auxIntToInt64(op2.AuxInt) + p2 := op2.Args[0] + d2 := m2.Args[1] + m3 := m2.Args[2] + if m3.Op != OpStore { + break + } + t3 := auxToType(m3.Aux) + _ = m3.Args[2] + op3 := m3.Args[0] + if op3.Op != OpOffPtr { + break + } + o3 := auxIntToInt64(op3.AuxInt) + p3 := op3.Args[0] + d3 := m3.Args[1] + m4 := m3.Args[2] + if m4.Op != OpStore { + break + } + t4 := auxToType(m4.Aux) + _ = m4.Args[2] + op4 := m4.Args[0] + if op4.Op != OpOffPtr || auxIntToInt64(op4.AuxInt) != 0 { + break + } + p4 := op4.Args[0] + d4 := m4.Args[1] + m5 := m4.Args[2] + if m5.Op != OpZero { + break + } + n := auxIntToInt64(m5.AuxInt) + mem := m5.Args[1] + p5 := m5.Args[0] + if !(m2.Uses == 1 && m3.Uses == 1 && m4.Uses == 1 && m5.Uses == 1 && o3 == t4.Size() && o2-o3 == t3.Size() && o1-o2 == t2.Size() && n == t4.Size()+t3.Size()+t2.Size()+t1.Size() && isSamePtr(p1, p2) && isSamePtr(p2, p3) && isSamePtr(p3, p4) && isSamePtr(p4, p5) && clobber(m2, m3, m4, m5)) { + break + } + v.reset(OpStore) + v.Aux = typeToAux(t1) + v0 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v0.Aux = typeToAux(t2) + v1 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v1.Aux = typeToAux(t3) + v2 := b.NewValue0(v.Pos, OpStore, types.TypeMem) + v2.Aux = typeToAux(t4) + v2.AddArg3(op4, d4, mem) + v1.AddArg3(op3, d3, v2) + v0.AddArg3(op2, d2, v1) + v.AddArg3(op1, d1, v0) + return true + } + return false +} +func rewriteValuegeneric_OpStringLen(v *Value) bool { + v_0 := v.Args[0] + // match: (StringLen (StringMake _ (Const64 [c]))) + // result: (Const64 [c]) + for { + if v_0.Op != OpStringMake { + break + } + _ = v_0.Args[1] + v_0_1 := v_0.Args[1] + if v_0_1.Op != OpConst64 { + break + } + t := v_0_1.Type + c := auxIntToInt64(v_0_1.AuxInt) + v.reset(OpConst64) + v.Type = t + v.AuxInt = int64ToAuxInt(c) + return true + } + return false +} +func rewriteValuegeneric_OpStringPtr(v *Value) bool { + v_0 := v.Args[0] + // match: (StringPtr (StringMake (Addr {s} base) _)) + // result: (Addr {s} base) + for { + if v_0.Op != OpStringMake { + break + } + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpAddr { + break + } + t := v_0_0.Type + s := auxToSym(v_0_0.Aux) + base := v_0_0.Args[0] + v.reset(OpAddr) + v.Type = t + v.Aux = symToAux(s) + v.AddArg(base) + return true + } + return false +} +func rewriteValuegeneric_OpStructSelect(v *Value) bool { + v_0 := v.Args[0] + b := v.Block + fe := b.Func.fe + // match: (StructSelect (StructMake1 x)) + // result: x + for { + if v_0.Op != OpStructMake1 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (StructSelect [0] (StructMake2 x _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake2 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (StructSelect [1] (StructMake2 _ x)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake2 { + break + } + x := v_0.Args[1] + v.copyOf(x) + return true + } + // match: (StructSelect [0] (StructMake3 x _ _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake3 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (StructSelect [1] (StructMake3 _ x _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake3 { + break + } + x := v_0.Args[1] + v.copyOf(x) + return true + } + // match: (StructSelect [2] (StructMake3 _ _ x)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpStructMake3 { + break + } + x := v_0.Args[2] + v.copyOf(x) + return true + } + // match: (StructSelect [0] (StructMake4 x _ _ _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpStructMake4 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (StructSelect [1] (StructMake4 _ x _ _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpStructMake4 { + break + } + x := v_0.Args[1] + v.copyOf(x) + return true + } + // match: (StructSelect [2] (StructMake4 _ _ x _)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 2 || v_0.Op != OpStructMake4 { + break + } + x := v_0.Args[2] + v.copyOf(x) + return true + } + // match: (StructSelect [3] (StructMake4 _ _ _ x)) + // result: x + for { + if auxIntToInt64(v.AuxInt) != 3 || v_0.Op != OpStructMake4 { + break + } + x := v_0.Args[3] + v.copyOf(x) + return true + } + // match: (StructSelect [i] x:(Load ptr mem)) + // cond: !fe.CanSSA(t) + // result: @x.Block (Load (OffPtr [t.FieldOff(int(i))] ptr) mem) + for { + i := auxIntToInt64(v.AuxInt) + x := v_0 + if x.Op != OpLoad { + break + } + t := x.Type + mem := x.Args[1] + ptr := x.Args[0] + if !(!fe.CanSSA(t)) { + break + } + b = x.Block + v0 := b.NewValue0(v.Pos, OpLoad, v.Type) + v.copyOf(v0) + v1 := b.NewValue0(v.Pos, OpOffPtr, v.Type.PtrTo()) + v1.AuxInt = int64ToAuxInt(t.FieldOff(int(i))) + v1.AddArg(ptr) + v0.AddArg2(v1, mem) + return true + } + // match: (StructSelect [0] (IData x)) + // result: (IData x) + for { + if auxIntToInt64(v.AuxInt) != 0 || v_0.Op != OpIData { + break + } + x := v_0.Args[0] + v.reset(OpIData) + v.AddArg(x) + return true + } + return false +} +func rewriteValuegeneric_OpSub16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Sub16 (Const16 [c]) (Const16 [d])) + // result: (Const16 [c-d]) + for { + if v_0.Op != OpConst16 { + break + } + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpConst16 { + break + } + d := auxIntToInt16(v_1.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(c - d) + return true + } + // match: (Sub16 x (Const16 [c])) + // cond: x.Op != OpConst16 + // result: (Add16 (Const16 [-c]) x) + for { + x := v_0 + if v_1.Op != OpConst16 { + break + } + t := v_1.Type + c := auxIntToInt16(v_1.AuxInt) + if !(x.Op != OpConst16) { + break + } + v.reset(OpAdd16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(-c) + v.AddArg2(v0, x) + return true + } + // match: (Sub16 (Mul16 x y) (Mul16 x z)) + // result: (Mul16 x (Sub16 y z)) + for { + t := v.Type + if v_0.Op != OpMul16 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if v_1.Op != OpMul16 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + z := v_1_1 + v.reset(OpMul16) + v0 := b.NewValue0(v.Pos, OpSub16, t) + v0.AddArg2(y, z) + v.AddArg2(x, v0) + return true + } + } + break + } + // match: (Sub16 x x) + // result: (Const16 [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + // match: (Sub16 (Neg16 x) (Com16 x)) + // result: (Const16 [1]) + for { + if v_0.Op != OpNeg16 { + break + } + x := v_0.Args[0] + if v_1.Op != OpCom16 || x != v_1.Args[0] { + break + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(1) + return true + } + // match: (Sub16 (Com16 x) (Neg16 x)) + // result: (Const16 [-1]) + for { + if v_0.Op != OpCom16 { + break + } + x := v_0.Args[0] + if v_1.Op != OpNeg16 || x != v_1.Args[0] { + break + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(-1) + return true + } + // match: (Sub16 (Add16 x y) x) + // result: y + for { + if v_0.Op != OpAdd16 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if x != v_1 { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Sub16 (Add16 x y) y) + // result: x + for { + if v_0.Op != OpAdd16 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if y != v_1 { + continue + } + v.copyOf(x) + return true + } + break + } + // match: (Sub16 (Sub16 x y) x) + // result: (Neg16 y) + for { + if v_0.Op != OpSub16 { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpNeg16) + v.AddArg(y) + return true + } + // match: (Sub16 x (Add16 x y)) + // result: (Neg16 y) + for { + x := v_0 + if v_1.Op != OpAdd16 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.reset(OpNeg16) + v.AddArg(y) + return true + } + break + } + // match: (Sub16 x (Sub16 i:(Const16 ) z)) + // cond: (z.Op != OpConst16 && x.Op != OpConst16) + // result: (Sub16 (Add16 x z) i) + for { + x := v_0 + if v_1.Op != OpSub16 { + break + } + z := v_1.Args[1] + i := v_1.Args[0] + if i.Op != OpConst16 { + break + } + t := i.Type + if !(z.Op != OpConst16 && x.Op != OpConst16) { + break + } + v.reset(OpSub16) + v0 := b.NewValue0(v.Pos, OpAdd16, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + // match: (Sub16 x (Add16 z i:(Const16 ))) + // cond: (z.Op != OpConst16 && x.Op != OpConst16) + // result: (Sub16 (Sub16 x z) i) + for { + x := v_0 + if v_1.Op != OpAdd16 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z := v_1_0 + i := v_1_1 + if i.Op != OpConst16 { + continue + } + t := i.Type + if !(z.Op != OpConst16 && x.Op != OpConst16) { + continue + } + v.reset(OpSub16) + v0 := b.NewValue0(v.Pos, OpSub16, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + break + } + // match: (Sub16 (Sub16 i:(Const16 ) z) x) + // cond: (z.Op != OpConst16 && x.Op != OpConst16) + // result: (Sub16 i (Add16 z x)) + for { + if v_0.Op != OpSub16 { + break + } + z := v_0.Args[1] + i := v_0.Args[0] + if i.Op != OpConst16 { + break + } + t := i.Type + x := v_1 + if !(z.Op != OpConst16 && x.Op != OpConst16) { + break + } + v.reset(OpSub16) + v0 := b.NewValue0(v.Pos, OpAdd16, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + // match: (Sub16 (Add16 z i:(Const16 )) x) + // cond: (z.Op != OpConst16 && x.Op != OpConst16) + // result: (Add16 i (Sub16 z x)) + for { + if v_0.Op != OpAdd16 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z := v_0_0 + i := v_0_1 + if i.Op != OpConst16 { + continue + } + t := i.Type + x := v_1 + if !(z.Op != OpConst16 && x.Op != OpConst16) { + continue + } + v.reset(OpAdd16) + v0 := b.NewValue0(v.Pos, OpSub16, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + break + } + // match: (Sub16 (Const16 [c]) (Sub16 (Const16 [d]) x)) + // result: (Add16 (Const16 [c-d]) x) + for { + if v_0.Op != OpConst16 { + break + } + t := v_0.Type + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpSub16 { + break + } + x := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst16 || v_1_0.Type != t { + break + } + d := auxIntToInt16(v_1_0.AuxInt) + v.reset(OpAdd16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + // match: (Sub16 (Const16 [c]) (Add16 (Const16 [d]) x)) + // result: (Sub16 (Const16 [c-d]) x) + for { + if v_0.Op != OpConst16 { + break + } + t := v_0.Type + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpAdd16 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst16 || v_1_0.Type != t { + continue + } + d := auxIntToInt16(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpSub16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpSub32(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Sub32 (Const32 [c]) (Const32 [d])) + // result: (Const32 [c-d]) + for { + if v_0.Op != OpConst32 { + break + } + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpConst32 { + break + } + d := auxIntToInt32(v_1.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(c - d) + return true + } + // match: (Sub32 x (Const32 [c])) + // cond: x.Op != OpConst32 + // result: (Add32 (Const32 [-c]) x) + for { + x := v_0 + if v_1.Op != OpConst32 { + break + } + t := v_1.Type + c := auxIntToInt32(v_1.AuxInt) + if !(x.Op != OpConst32) { + break + } + v.reset(OpAdd32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(-c) + v.AddArg2(v0, x) + return true + } + // match: (Sub32 (Mul32 x y) (Mul32 x z)) + // result: (Mul32 x (Sub32 y z)) + for { + t := v.Type + if v_0.Op != OpMul32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if v_1.Op != OpMul32 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + z := v_1_1 + v.reset(OpMul32) + v0 := b.NewValue0(v.Pos, OpSub32, t) + v0.AddArg2(y, z) + v.AddArg2(x, v0) + return true + } + } + break + } + // match: (Sub32 x x) + // result: (Const32 [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) + return true + } + // match: (Sub32 (Neg32 x) (Com32 x)) + // result: (Const32 [1]) + for { + if v_0.Op != OpNeg32 { + break + } + x := v_0.Args[0] + if v_1.Op != OpCom32 || x != v_1.Args[0] { + break + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(1) + return true + } + // match: (Sub32 (Com32 x) (Neg32 x)) + // result: (Const32 [-1]) + for { + if v_0.Op != OpCom32 { + break + } + x := v_0.Args[0] + if v_1.Op != OpNeg32 || x != v_1.Args[0] { + break + } + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(-1) + return true + } + // match: (Sub32 (Add32 x y) x) + // result: y + for { + if v_0.Op != OpAdd32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if x != v_1 { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Sub32 (Add32 x y) y) + // result: x + for { + if v_0.Op != OpAdd32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if y != v_1 { + continue + } + v.copyOf(x) + return true + } + break + } + // match: (Sub32 (Sub32 x y) x) + // result: (Neg32 y) + for { + if v_0.Op != OpSub32 { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpNeg32) + v.AddArg(y) + return true + } + // match: (Sub32 x (Add32 x y)) + // result: (Neg32 y) + for { + x := v_0 + if v_1.Op != OpAdd32 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.reset(OpNeg32) + v.AddArg(y) + return true + } + break + } + // match: (Sub32 x (Sub32 i:(Const32 ) z)) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Sub32 (Add32 x z) i) + for { + x := v_0 + if v_1.Op != OpSub32 { + break + } + z := v_1.Args[1] + i := v_1.Args[0] + if i.Op != OpConst32 { + break + } + t := i.Type + if !(z.Op != OpConst32 && x.Op != OpConst32) { + break + } + v.reset(OpSub32) + v0 := b.NewValue0(v.Pos, OpAdd32, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + // match: (Sub32 x (Add32 z i:(Const32 ))) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Sub32 (Sub32 x z) i) + for { + x := v_0 + if v_1.Op != OpAdd32 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z := v_1_0 + i := v_1_1 + if i.Op != OpConst32 { + continue + } + t := i.Type + if !(z.Op != OpConst32 && x.Op != OpConst32) { + continue + } + v.reset(OpSub32) + v0 := b.NewValue0(v.Pos, OpSub32, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + break + } + // match: (Sub32 (Sub32 i:(Const32 ) z) x) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Sub32 i (Add32 z x)) + for { + if v_0.Op != OpSub32 { + break + } + z := v_0.Args[1] + i := v_0.Args[0] + if i.Op != OpConst32 { + break + } + t := i.Type + x := v_1 + if !(z.Op != OpConst32 && x.Op != OpConst32) { + break + } + v.reset(OpSub32) + v0 := b.NewValue0(v.Pos, OpAdd32, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + // match: (Sub32 (Add32 z i:(Const32 )) x) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Add32 i (Sub32 z x)) + for { + if v_0.Op != OpAdd32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z := v_0_0 + i := v_0_1 + if i.Op != OpConst32 { + continue + } + t := i.Type + x := v_1 + if !(z.Op != OpConst32 && x.Op != OpConst32) { + continue + } + v.reset(OpAdd32) + v0 := b.NewValue0(v.Pos, OpSub32, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + break + } + // match: (Sub32 (Const32 [c]) (Sub32 (Const32 [d]) x)) + // result: (Add32 (Const32 [c-d]) x) + for { + if v_0.Op != OpConst32 { + break + } + t := v_0.Type + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpSub32 { + break + } + x := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst32 || v_1_0.Type != t { + break + } + d := auxIntToInt32(v_1_0.AuxInt) + v.reset(OpAdd32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + // match: (Sub32 (Const32 [c]) (Add32 (Const32 [d]) x)) + // result: (Sub32 (Const32 [c-d]) x) + for { + if v_0.Op != OpConst32 { + break + } + t := v_0.Type + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpAdd32 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst32 || v_1_0.Type != t { + continue + } + d := auxIntToInt32(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpSub32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpSub32F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Sub32F (Const32F [c]) (Const32F [d])) + // cond: c-d == c-d + // result: (Const32F [c-d]) + for { + if v_0.Op != OpConst32F { + break + } + c := auxIntToFloat32(v_0.AuxInt) + if v_1.Op != OpConst32F { + break + } + d := auxIntToFloat32(v_1.AuxInt) + if !(c-d == c-d) { + break + } + v.reset(OpConst32F) + v.AuxInt = float32ToAuxInt(c - d) + return true + } + return false +} +func rewriteValuegeneric_OpSub64(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Sub64 (Const64 [c]) (Const64 [d])) + // result: (Const64 [c-d]) + for { + if v_0.Op != OpConst64 { + break + } + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpConst64 { + break + } + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(c - d) + return true + } + // match: (Sub64 x (Const64 [c])) + // cond: x.Op != OpConst64 + // result: (Add64 (Const64 [-c]) x) + for { + x := v_0 + if v_1.Op != OpConst64 { + break + } + t := v_1.Type + c := auxIntToInt64(v_1.AuxInt) + if !(x.Op != OpConst64) { + break + } + v.reset(OpAdd64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(-c) + v.AddArg2(v0, x) + return true + } + // match: (Sub64 (Mul64 x y) (Mul64 x z)) + // result: (Mul64 x (Sub64 y z)) + for { + t := v.Type + if v_0.Op != OpMul64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if v_1.Op != OpMul64 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + z := v_1_1 + v.reset(OpMul64) + v0 := b.NewValue0(v.Pos, OpSub64, t) + v0.AddArg2(y, z) + v.AddArg2(x, v0) + return true + } + } + break + } + // match: (Sub64 x x) + // result: (Const64 [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (Sub64 (Neg64 x) (Com64 x)) + // result: (Const64 [1]) + for { + if v_0.Op != OpNeg64 { + break + } + x := v_0.Args[0] + if v_1.Op != OpCom64 || x != v_1.Args[0] { + break + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(1) + return true + } + // match: (Sub64 (Com64 x) (Neg64 x)) + // result: (Const64 [-1]) + for { + if v_0.Op != OpCom64 { + break + } + x := v_0.Args[0] + if v_1.Op != OpNeg64 || x != v_1.Args[0] { + break + } + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(-1) + return true + } + // match: (Sub64 (Add64 x y) x) + // result: y + for { + if v_0.Op != OpAdd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if x != v_1 { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Sub64 (Add64 x y) y) + // result: x + for { + if v_0.Op != OpAdd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if y != v_1 { + continue + } + v.copyOf(x) + return true + } + break + } + // match: (Sub64 (Sub64 x y) x) + // result: (Neg64 y) + for { + if v_0.Op != OpSub64 { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpNeg64) + v.AddArg(y) + return true + } + // match: (Sub64 x (Add64 x y)) + // result: (Neg64 y) + for { + x := v_0 + if v_1.Op != OpAdd64 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.reset(OpNeg64) + v.AddArg(y) + return true + } + break + } + // match: (Sub64 x (Sub64 i:(Const64 ) z)) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Sub64 (Add64 x z) i) + for { + x := v_0 + if v_1.Op != OpSub64 { + break + } + z := v_1.Args[1] + i := v_1.Args[0] + if i.Op != OpConst64 { + break + } + t := i.Type + if !(z.Op != OpConst64 && x.Op != OpConst64) { + break + } + v.reset(OpSub64) + v0 := b.NewValue0(v.Pos, OpAdd64, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + // match: (Sub64 x (Add64 z i:(Const64 ))) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Sub64 (Sub64 x z) i) + for { + x := v_0 + if v_1.Op != OpAdd64 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z := v_1_0 + i := v_1_1 + if i.Op != OpConst64 { + continue + } + t := i.Type + if !(z.Op != OpConst64 && x.Op != OpConst64) { + continue + } + v.reset(OpSub64) + v0 := b.NewValue0(v.Pos, OpSub64, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + break + } + // match: (Sub64 (Sub64 i:(Const64 ) z) x) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Sub64 i (Add64 z x)) + for { + if v_0.Op != OpSub64 { + break + } + z := v_0.Args[1] + i := v_0.Args[0] + if i.Op != OpConst64 { + break + } + t := i.Type + x := v_1 + if !(z.Op != OpConst64 && x.Op != OpConst64) { + break + } + v.reset(OpSub64) + v0 := b.NewValue0(v.Pos, OpAdd64, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + // match: (Sub64 (Add64 z i:(Const64 )) x) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Add64 i (Sub64 z x)) + for { + if v_0.Op != OpAdd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z := v_0_0 + i := v_0_1 + if i.Op != OpConst64 { + continue + } + t := i.Type + x := v_1 + if !(z.Op != OpConst64 && x.Op != OpConst64) { + continue + } + v.reset(OpAdd64) + v0 := b.NewValue0(v.Pos, OpSub64, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + break + } + // match: (Sub64 (Const64 [c]) (Sub64 (Const64 [d]) x)) + // result: (Add64 (Const64 [c-d]) x) + for { + if v_0.Op != OpConst64 { + break + } + t := v_0.Type + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpSub64 { + break + } + x := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst64 || v_1_0.Type != t { + break + } + d := auxIntToInt64(v_1_0.AuxInt) + v.reset(OpAdd64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + // match: (Sub64 (Const64 [c]) (Add64 (Const64 [d]) x)) + // result: (Sub64 (Const64 [c-d]) x) + for { + if v_0.Op != OpConst64 { + break + } + t := v_0.Type + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpAdd64 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst64 || v_1_0.Type != t { + continue + } + d := auxIntToInt64(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpSub64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpSub64F(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (Sub64F (Const64F [c]) (Const64F [d])) + // cond: c-d == c-d + // result: (Const64F [c-d]) + for { + if v_0.Op != OpConst64F { + break + } + c := auxIntToFloat64(v_0.AuxInt) + if v_1.Op != OpConst64F { + break + } + d := auxIntToFloat64(v_1.AuxInt) + if !(c-d == c-d) { + break + } + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(c - d) + return true + } + return false +} +func rewriteValuegeneric_OpSub8(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (Sub8 (Const8 [c]) (Const8 [d])) + // result: (Const8 [c-d]) + for { + if v_0.Op != OpConst8 { + break + } + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpConst8 { + break + } + d := auxIntToInt8(v_1.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(c - d) + return true + } + // match: (Sub8 x (Const8 [c])) + // cond: x.Op != OpConst8 + // result: (Add8 (Const8 [-c]) x) + for { + x := v_0 + if v_1.Op != OpConst8 { + break + } + t := v_1.Type + c := auxIntToInt8(v_1.AuxInt) + if !(x.Op != OpConst8) { + break + } + v.reset(OpAdd8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(-c) + v.AddArg2(v0, x) + return true + } + // match: (Sub8 (Mul8 x y) (Mul8 x z)) + // result: (Mul8 x (Sub8 y z)) + for { + t := v.Type + if v_0.Op != OpMul8 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if v_1.Op != OpMul8 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + z := v_1_1 + v.reset(OpMul8) + v0 := b.NewValue0(v.Pos, OpSub8, t) + v0.AddArg2(y, z) + v.AddArg2(x, v0) + return true + } + } + break + } + // match: (Sub8 x x) + // result: (Const8 [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(0) + return true + } + // match: (Sub8 (Neg8 x) (Com8 x)) + // result: (Const8 [1]) + for { + if v_0.Op != OpNeg8 { + break + } + x := v_0.Args[0] + if v_1.Op != OpCom8 || x != v_1.Args[0] { + break + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(1) + return true + } + // match: (Sub8 (Com8 x) (Neg8 x)) + // result: (Const8 [-1]) + for { + if v_0.Op != OpCom8 { + break + } + x := v_0.Args[0] + if v_1.Op != OpNeg8 || x != v_1.Args[0] { + break + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(-1) + return true + } + // match: (Sub8 (Add8 x y) x) + // result: y + for { + if v_0.Op != OpAdd8 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if x != v_1 { + continue + } + v.copyOf(y) + return true + } + break + } + // match: (Sub8 (Add8 x y) y) + // result: x + for { + if v_0.Op != OpAdd8 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + x := v_0_0 + y := v_0_1 + if y != v_1 { + continue + } + v.copyOf(x) + return true + } + break + } + // match: (Sub8 (Sub8 x y) x) + // result: (Neg8 y) + for { + if v_0.Op != OpSub8 { + break + } + y := v_0.Args[1] + x := v_0.Args[0] + if x != v_1 { + break + } + v.reset(OpNeg8) + v.AddArg(y) + return true + } + // match: (Sub8 x (Add8 x y)) + // result: (Neg8 y) + for { + x := v_0 + if v_1.Op != OpAdd8 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.reset(OpNeg8) + v.AddArg(y) + return true + } + break + } + // match: (Sub8 x (Sub8 i:(Const8 ) z)) + // cond: (z.Op != OpConst8 && x.Op != OpConst8) + // result: (Sub8 (Add8 x z) i) + for { + x := v_0 + if v_1.Op != OpSub8 { + break + } + z := v_1.Args[1] + i := v_1.Args[0] + if i.Op != OpConst8 { + break + } + t := i.Type + if !(z.Op != OpConst8 && x.Op != OpConst8) { + break + } + v.reset(OpSub8) + v0 := b.NewValue0(v.Pos, OpAdd8, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + // match: (Sub8 x (Add8 z i:(Const8 ))) + // cond: (z.Op != OpConst8 && x.Op != OpConst8) + // result: (Sub8 (Sub8 x z) i) + for { + x := v_0 + if v_1.Op != OpAdd8 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + z := v_1_0 + i := v_1_1 + if i.Op != OpConst8 { + continue + } + t := i.Type + if !(z.Op != OpConst8 && x.Op != OpConst8) { + continue + } + v.reset(OpSub8) + v0 := b.NewValue0(v.Pos, OpSub8, t) + v0.AddArg2(x, z) + v.AddArg2(v0, i) + return true + } + break + } + // match: (Sub8 (Sub8 i:(Const8 ) z) x) + // cond: (z.Op != OpConst8 && x.Op != OpConst8) + // result: (Sub8 i (Add8 z x)) + for { + if v_0.Op != OpSub8 { + break + } + z := v_0.Args[1] + i := v_0.Args[0] + if i.Op != OpConst8 { + break + } + t := i.Type + x := v_1 + if !(z.Op != OpConst8 && x.Op != OpConst8) { + break + } + v.reset(OpSub8) + v0 := b.NewValue0(v.Pos, OpAdd8, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + // match: (Sub8 (Add8 z i:(Const8 )) x) + // cond: (z.Op != OpConst8 && x.Op != OpConst8) + // result: (Add8 i (Sub8 z x)) + for { + if v_0.Op != OpAdd8 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + z := v_0_0 + i := v_0_1 + if i.Op != OpConst8 { + continue + } + t := i.Type + x := v_1 + if !(z.Op != OpConst8 && x.Op != OpConst8) { + continue + } + v.reset(OpAdd8) + v0 := b.NewValue0(v.Pos, OpSub8, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + break + } + // match: (Sub8 (Const8 [c]) (Sub8 (Const8 [d]) x)) + // result: (Add8 (Const8 [c-d]) x) + for { + if v_0.Op != OpConst8 { + break + } + t := v_0.Type + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpSub8 { + break + } + x := v_1.Args[1] + v_1_0 := v_1.Args[0] + if v_1_0.Op != OpConst8 || v_1_0.Type != t { + break + } + d := auxIntToInt8(v_1_0.AuxInt) + v.reset(OpAdd8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + // match: (Sub8 (Const8 [c]) (Add8 (Const8 [d]) x)) + // result: (Sub8 (Const8 [c-d]) x) + for { + if v_0.Op != OpConst8 { + break + } + t := v_0.Type + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpAdd8 { + break + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst8 || v_1_0.Type != t { + continue + } + d := auxIntToInt8(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpSub8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(c - d) + v.AddArg2(v0, x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc (Const64F [c])) + // result: (Const64F [math.Trunc(c)]) + for { + if v_0.Op != OpConst64F { + break + } + c := auxIntToFloat64(v_0.AuxInt) + v.reset(OpConst64F) + v.AuxInt = float64ToAuxInt(math.Trunc(c)) + return true + } + return false +} +func rewriteValuegeneric_OpTrunc16to8(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc16to8 (Const16 [c])) + // result: (Const8 [int8(c)]) + for { + if v_0.Op != OpConst16 { + break + } + c := auxIntToInt16(v_0.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(int8(c)) + return true + } + // match: (Trunc16to8 (ZeroExt8to16 x)) + // result: x + for { + if v_0.Op != OpZeroExt8to16 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc16to8 (SignExt8to16 x)) + // result: x + for { + if v_0.Op != OpSignExt8to16 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc16to8 (And16 (Const16 [y]) x)) + // cond: y&0xFF == 0xFF + // result: (Trunc16to8 x) + for { + if v_0.Op != OpAnd16 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst16 { + continue + } + y := auxIntToInt16(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFF == 0xFF) { + continue + } + v.reset(OpTrunc16to8) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc32to16(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc32to16 (Const32 [c])) + // result: (Const16 [int16(c)]) + for { + if v_0.Op != OpConst32 { + break + } + c := auxIntToInt32(v_0.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(int16(c)) + return true + } + // match: (Trunc32to16 (ZeroExt8to32 x)) + // result: (ZeroExt8to16 x) + for { + if v_0.Op != OpZeroExt8to32 { + break + } + x := v_0.Args[0] + v.reset(OpZeroExt8to16) + v.AddArg(x) + return true + } + // match: (Trunc32to16 (ZeroExt16to32 x)) + // result: x + for { + if v_0.Op != OpZeroExt16to32 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc32to16 (SignExt8to32 x)) + // result: (SignExt8to16 x) + for { + if v_0.Op != OpSignExt8to32 { + break + } + x := v_0.Args[0] + v.reset(OpSignExt8to16) + v.AddArg(x) + return true + } + // match: (Trunc32to16 (SignExt16to32 x)) + // result: x + for { + if v_0.Op != OpSignExt16to32 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc32to16 (And32 (Const32 [y]) x)) + // cond: y&0xFFFF == 0xFFFF + // result: (Trunc32to16 x) + for { + if v_0.Op != OpAnd32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst32 { + continue + } + y := auxIntToInt32(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFFFF == 0xFFFF) { + continue + } + v.reset(OpTrunc32to16) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc32to8(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc32to8 (Const32 [c])) + // result: (Const8 [int8(c)]) + for { + if v_0.Op != OpConst32 { + break + } + c := auxIntToInt32(v_0.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(int8(c)) + return true + } + // match: (Trunc32to8 (ZeroExt8to32 x)) + // result: x + for { + if v_0.Op != OpZeroExt8to32 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc32to8 (SignExt8to32 x)) + // result: x + for { + if v_0.Op != OpSignExt8to32 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc32to8 (And32 (Const32 [y]) x)) + // cond: y&0xFF == 0xFF + // result: (Trunc32to8 x) + for { + if v_0.Op != OpAnd32 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst32 { + continue + } + y := auxIntToInt32(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFF == 0xFF) { + continue + } + v.reset(OpTrunc32to8) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc64to16(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc64to16 (Const64 [c])) + // result: (Const16 [int16(c)]) + for { + if v_0.Op != OpConst64 { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(int16(c)) + return true + } + // match: (Trunc64to16 (ZeroExt8to64 x)) + // result: (ZeroExt8to16 x) + for { + if v_0.Op != OpZeroExt8to64 { + break + } + x := v_0.Args[0] + v.reset(OpZeroExt8to16) + v.AddArg(x) + return true + } + // match: (Trunc64to16 (ZeroExt16to64 x)) + // result: x + for { + if v_0.Op != OpZeroExt16to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to16 (SignExt8to64 x)) + // result: (SignExt8to16 x) + for { + if v_0.Op != OpSignExt8to64 { + break + } + x := v_0.Args[0] + v.reset(OpSignExt8to16) + v.AddArg(x) + return true + } + // match: (Trunc64to16 (SignExt16to64 x)) + // result: x + for { + if v_0.Op != OpSignExt16to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to16 (And64 (Const64 [y]) x)) + // cond: y&0xFFFF == 0xFFFF + // result: (Trunc64to16 x) + for { + if v_0.Op != OpAnd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst64 { + continue + } + y := auxIntToInt64(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFFFF == 0xFFFF) { + continue + } + v.reset(OpTrunc64to16) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc64to32(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc64to32 (Const64 [c])) + // result: (Const32 [int32(c)]) + for { + if v_0.Op != OpConst64 { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(int32(c)) + return true + } + // match: (Trunc64to32 (ZeroExt8to64 x)) + // result: (ZeroExt8to32 x) + for { + if v_0.Op != OpZeroExt8to64 { + break + } + x := v_0.Args[0] + v.reset(OpZeroExt8to32) + v.AddArg(x) + return true + } + // match: (Trunc64to32 (ZeroExt16to64 x)) + // result: (ZeroExt16to32 x) + for { + if v_0.Op != OpZeroExt16to64 { + break + } + x := v_0.Args[0] + v.reset(OpZeroExt16to32) + v.AddArg(x) + return true + } + // match: (Trunc64to32 (ZeroExt32to64 x)) + // result: x + for { + if v_0.Op != OpZeroExt32to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to32 (SignExt8to64 x)) + // result: (SignExt8to32 x) + for { + if v_0.Op != OpSignExt8to64 { + break + } + x := v_0.Args[0] + v.reset(OpSignExt8to32) + v.AddArg(x) + return true + } + // match: (Trunc64to32 (SignExt16to64 x)) + // result: (SignExt16to32 x) + for { + if v_0.Op != OpSignExt16to64 { + break + } + x := v_0.Args[0] + v.reset(OpSignExt16to32) + v.AddArg(x) + return true + } + // match: (Trunc64to32 (SignExt32to64 x)) + // result: x + for { + if v_0.Op != OpSignExt32to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to32 (And64 (Const64 [y]) x)) + // cond: y&0xFFFFFFFF == 0xFFFFFFFF + // result: (Trunc64to32 x) + for { + if v_0.Op != OpAnd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst64 { + continue + } + y := auxIntToInt64(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFFFFFFFF == 0xFFFFFFFF) { + continue + } + v.reset(OpTrunc64to32) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpTrunc64to8(v *Value) bool { + v_0 := v.Args[0] + // match: (Trunc64to8 (Const64 [c])) + // result: (Const8 [int8(c)]) + for { + if v_0.Op != OpConst64 { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(int8(c)) + return true + } + // match: (Trunc64to8 (ZeroExt8to64 x)) + // result: x + for { + if v_0.Op != OpZeroExt8to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to8 (SignExt8to64 x)) + // result: x + for { + if v_0.Op != OpSignExt8to64 { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } + // match: (Trunc64to8 (And64 (Const64 [y]) x)) + // cond: y&0xFF == 0xFF + // result: (Trunc64to8 x) + for { + if v_0.Op != OpAnd64 { + break + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { + if v_0_0.Op != OpConst64 { + continue + } + y := auxIntToInt64(v_0_0.AuxInt) + x := v_0_1 + if !(y&0xFF == 0xFF) { + continue + } + v.reset(OpTrunc64to8) + v.AddArg(x) + return true + } + break + } + return false +} +func rewriteValuegeneric_OpXor16(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + config := b.Func.Config + // match: (Xor16 (Const16 [c]) (Const16 [d])) + // result: (Const16 [c^d]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst16 { + continue + } + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpConst16 { + continue + } + d := auxIntToInt16(v_1.AuxInt) + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(c ^ d) + return true + } + break + } + // match: (Xor16 x x) + // result: (Const16 [0]) + for { + x := v_0 + if x != v_1 { + break + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(0) + return true + } + // match: (Xor16 (Const16 [0]) x) + // result: x + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + continue + } + x := v_1 + v.copyOf(x) + return true + } + break + } + // match: (Xor16 (Com16 x) x) + // result: (Const16 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom16 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst16) + v.AuxInt = int16ToAuxInt(-1) + return true + } + break + } + // match: (Xor16 (Const16 [-1]) x) + // result: (Com16 x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != -1 { + continue + } + x := v_1 + v.reset(OpCom16) + v.AddArg(x) + return true + } + break + } + // match: (Xor16 x (Xor16 x y)) + // result: y + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpXor16 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.copyOf(y) + return true + } + } + break + } + // match: (Xor16 (Xor16 i:(Const16 ) z) x) + // cond: (z.Op != OpConst16 && x.Op != OpConst16) + // result: (Xor16 i (Xor16 z x)) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpXor16 { + continue + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { + i := v_0_0 + if i.Op != OpConst16 { + continue + } + t := i.Type + z := v_0_1 + x := v_1 + if !(z.Op != OpConst16 && x.Op != OpConst16) { + continue + } + v.reset(OpXor16) + v0 := b.NewValue0(v.Pos, OpXor16, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } + } + break + } + // match: (Xor16 (Const16 [c]) (Xor16 (Const16 [d]) x)) + // result: (Xor16 (Const16 [c^d]) x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst16 { + continue + } + t := v_0.Type + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpXor16 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst16 || v_1_0.Type != t { + continue + } + d := auxIntToInt16(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpXor16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(c ^ d) + v.AddArg2(v0, x) + return true + } + } + break + } + // match: (Xor16 (Lsh16x64 x z:(Const64 [c])) (Rsh16Ux64 x (Const64 [d]))) + // cond: c < 16 && d == 16-c && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh16x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh16Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 16 && d == 16-c && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor16 left:(Lsh16x64 x y) right:(Rsh16Ux64 x (Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor16 left:(Lsh16x32 x y) right:(Rsh16Ux32 x (Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor16 left:(Lsh16x16 x y) right:(Rsh16Ux16 x (Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor16 left:(Lsh16x8 x y) right:(Rsh16Ux8 x (Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh16x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh16Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 16 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor16 right:(Rsh16Ux64 x y) left:(Lsh16x64 x z:(Sub64 (Const64 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor16 right:(Rsh16Ux32 x y) left:(Lsh16x32 x z:(Sub32 (Const32 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor16 right:(Rsh16Ux16 x y) left:(Lsh16x16 x z:(Sub16 (Const16 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor16 right:(Rsh16Ux8 x y) left:(Lsh16x8 x z:(Sub8 (Const8 [16]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16) + // result: (RotateLeft16 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh16Ux8 { continue } - y := auxIntToInt32(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFFFF == 0xFFFF) { + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh16x8 { continue } - v.reset(OpTrunc32to16) - v.AddArg(x) + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 16 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 16)) { + continue + } + v.reset(OpRotateLeft16) + v.AddArg2(x, z) return true } break } return false } -func rewriteValuegeneric_OpTrunc32to8(v *Value) bool { +func rewriteValuegeneric_OpXor32(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (Trunc32to8 (Const32 [c])) - // result: (Const8 [int8(c)]) + b := v.Block + config := b.Func.Config + // match: (Xor32 (Const32 [c]) (Const32 [d])) + // result: (Const32 [c^d]) for { - if v_0.Op != OpConst32 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst32 { + continue + } + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpConst32 { + continue + } + d := auxIntToInt32(v_1.AuxInt) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(c ^ d) + return true } - c := auxIntToInt32(v_0.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(int8(c)) - return true + break } - // match: (Trunc32to8 (ZeroExt8to32 x)) - // result: x + // match: (Xor32 x x) + // result: (Const32 [0]) for { - if v_0.Op != OpZeroExt8to32 { + x := v_0 + if x != v_1 { break } - x := v_0.Args[0] - v.copyOf(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(0) return true } - // match: (Trunc32to8 (SignExt8to32 x)) + // match: (Xor32 (Const32 [0]) x) // result: x for { - if v_0.Op != OpSignExt8to32 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + continue + } + x := v_1 + v.copyOf(x) + return true } - x := v_0.Args[0] - v.copyOf(x) - return true + break } - // match: (Trunc32to8 (And32 (Const32 [y]) x)) - // cond: y&0xFF == 0xFF - // result: (Trunc32to8 x) + // match: (Xor32 (Com32 x) x) + // result: (Const32 [-1]) for { - if v_0.Op != OpAnd32 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst32 { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom32 { continue } - y := auxIntToInt32(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFF == 0xFF) { + x := v_0.Args[0] + if x != v_1 { continue } - v.reset(OpTrunc32to8) - v.AddArg(x) + v.reset(OpConst32) + v.AuxInt = int32ToAuxInt(-1) return true } break } - return false -} -func rewriteValuegeneric_OpTrunc64to16(v *Value) bool { - v_0 := v.Args[0] - // match: (Trunc64to16 (Const64 [c])) - // result: (Const16 [int16(c)]) + // match: (Xor32 (Const32 [-1]) x) + // result: (Com32 x) for { - if v_0.Op != OpConst64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != -1 { + continue + } + x := v_1 + v.reset(OpCom32) + v.AddArg(x) + return true } - c := auxIntToInt64(v_0.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(int16(c)) - return true + break } - // match: (Trunc64to16 (ZeroExt8to64 x)) - // result: (ZeroExt8to16 x) + // match: (Xor32 x (Xor32 x y)) + // result: y for { - if v_0.Op != OpZeroExt8to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x := v_0 + if v_1.Op != OpXor32 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if x != v_1_0 { + continue + } + y := v_1_1 + v.copyOf(y) + return true + } } - x := v_0.Args[0] - v.reset(OpZeroExt8to16) - v.AddArg(x) - return true + break } - // match: (Trunc64to16 (ZeroExt16to64 x)) - // result: x + // match: (Xor32 (Xor32 i:(Const32 ) z) x) + // cond: (z.Op != OpConst32 && x.Op != OpConst32) + // result: (Xor32 i (Xor32 z x)) for { - if v_0.Op != OpZeroExt16to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpXor32 { + continue + } + _ = v_0.Args[1] + v_0_0 := v_0.Args[0] + v_0_1 := v_0.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { + i := v_0_0 + if i.Op != OpConst32 { + continue + } + t := i.Type + z := v_0_1 + x := v_1 + if !(z.Op != OpConst32 && x.Op != OpConst32) { + continue + } + v.reset(OpXor32) + v0 := b.NewValue0(v.Pos, OpXor32, t) + v0.AddArg2(z, x) + v.AddArg2(i, v0) + return true + } } - x := v_0.Args[0] - v.copyOf(x) - return true + break } - // match: (Trunc64to16 (SignExt8to64 x)) - // result: (SignExt8to16 x) + // match: (Xor32 (Const32 [c]) (Xor32 (Const32 [d]) x)) + // result: (Xor32 (Const32 [c^d]) x) for { - if v_0.Op != OpSignExt8to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst32 { + continue + } + t := v_0.Type + c := auxIntToInt32(v_0.AuxInt) + if v_1.Op != OpXor32 { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst32 || v_1_0.Type != t { + continue + } + d := auxIntToInt32(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpXor32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(c ^ d) + v.AddArg2(v0, x) + return true + } } - x := v_0.Args[0] - v.reset(OpSignExt8to16) - v.AddArg(x) - return true + break } - // match: (Trunc64to16 (SignExt16to64 x)) - // result: x + // match: (Xor32 (Lsh32x64 x z:(Const64 [c])) (Rsh32Ux64 x (Const64 [d]))) + // cond: c < 32 && d == 32-c && canRotate(config, 32) + // result: (RotateLeft32 x z) for { - if v_0.Op != OpSignExt16to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh32x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh32Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 32 && d == 32-c && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true } - x := v_0.Args[0] - v.copyOf(x) - return true + break } - // match: (Trunc64to16 (And64 (Const64 [y]) x)) - // cond: y&0xFFFF == 0xFFFF - // result: (Trunc64to16 x) + // match: (Xor32 left:(Lsh32x64 x y) right:(Rsh32Ux64 x (Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) for { - if v_0.Op != OpAnd64 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst64 { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { continue } - y := auxIntToInt64(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFFFF == 0xFFFF) { + right_1 := right.Args[1] + if right_1.Op != OpSub64 { continue } - v.reset(OpTrunc64to16) - v.AddArg(x) + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) return true } break } - return false -} -func rewriteValuegeneric_OpTrunc64to32(v *Value) bool { - v_0 := v.Args[0] - // match: (Trunc64to32 (Const64 [c])) - // result: (Const32 [int32(c)]) - for { - if v_0.Op != OpConst64 { - break - } - c := auxIntToInt64(v_0.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(int32(c)) - return true - } - // match: (Trunc64to32 (ZeroExt8to64 x)) - // result: (ZeroExt8to32 x) + // match: (Xor32 left:(Lsh32x32 x y) right:(Rsh32Ux32 x (Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) for { - if v_0.Op != OpZeroExt8to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - x := v_0.Args[0] - v.reset(OpZeroExt8to32) - v.AddArg(x) - return true + break } - // match: (Trunc64to32 (ZeroExt16to64 x)) - // result: (ZeroExt16to32 x) + // match: (Xor32 left:(Lsh32x16 x y) right:(Rsh32Ux16 x (Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) for { - if v_0.Op != OpZeroExt16to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - x := v_0.Args[0] - v.reset(OpZeroExt16to32) - v.AddArg(x) - return true + break } - // match: (Trunc64to32 (ZeroExt32to64 x)) - // result: x + // match: (Xor32 left:(Lsh32x8 x y) right:(Rsh32Ux8 x (Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x y) for { - if v_0.Op != OpZeroExt32to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh32x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh32Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 32 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, y) + return true } - x := v_0.Args[0] - v.copyOf(x) - return true + break } - // match: (Trunc64to32 (SignExt8to64 x)) - // result: (SignExt8to32 x) + // match: (Xor32 right:(Rsh32Ux64 x y) left:(Lsh32x64 x z:(Sub64 (Const64 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) for { - if v_0.Op != OpSignExt8to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true } - x := v_0.Args[0] - v.reset(OpSignExt8to32) - v.AddArg(x) - return true + break } - // match: (Trunc64to32 (SignExt16to64 x)) - // result: (SignExt16to32 x) + // match: (Xor32 right:(Rsh32Ux32 x y) left:(Lsh32x32 x z:(Sub32 (Const32 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) for { - if v_0.Op != OpSignExt16to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true } - x := v_0.Args[0] - v.reset(OpSignExt16to32) - v.AddArg(x) - return true + break } - // match: (Trunc64to32 (SignExt32to64 x)) - // result: x + // match: (Xor32 right:(Rsh32Ux16 x y) left:(Lsh32x16 x z:(Sub16 (Const16 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) for { - if v_0.Op != OpSignExt32to64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) + return true } - x := v_0.Args[0] - v.copyOf(x) - return true + break } - // match: (Trunc64to32 (And64 (Const64 [y]) x)) - // cond: y&0xFFFFFFFF == 0xFFFFFFFF - // result: (Trunc64to32 x) + // match: (Xor32 right:(Rsh32Ux8 x y) left:(Lsh32x8 x z:(Sub8 (Const8 [32]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32) + // result: (RotateLeft32 x z) for { - if v_0.Op != OpAnd64 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst64 { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh32Ux8 { continue } - y := auxIntToInt64(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFFFFFFFF == 0xFFFFFFFF) { + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh32x8 { continue } - v.reset(OpTrunc64to32) - v.AddArg(x) + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 32 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 32)) { + continue + } + v.reset(OpRotateLeft32) + v.AddArg2(x, z) return true } break } return false } -func rewriteValuegeneric_OpTrunc64to8(v *Value) bool { +func rewriteValuegeneric_OpXor64(v *Value) bool { + v_1 := v.Args[1] v_0 := v.Args[0] - // match: (Trunc64to8 (Const64 [c])) - // result: (Const8 [int8(c)]) + b := v.Block + config := b.Func.Config + // match: (Xor64 (Const64 [c]) (Const64 [d])) + // result: (Const64 [c^d]) for { - if v_0.Op != OpConst64 { - break + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst64 { + continue + } + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1.AuxInt) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(c ^ d) + return true } - c := auxIntToInt64(v_0.AuxInt) - v.reset(OpConst8) - v.AuxInt = int8ToAuxInt(int8(c)) - return true + break } - // match: (Trunc64to8 (ZeroExt8to64 x)) - // result: x + // match: (Xor64 x x) + // result: (Const64 [0]) for { - if v_0.Op != OpZeroExt8to64 { + x := v_0 + if x != v_1 { break } - x := v_0.Args[0] - v.copyOf(x) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(0) return true } - // match: (Trunc64to8 (SignExt8to64 x)) + // match: (Xor64 (Const64 [0]) x) // result: x for { - if v_0.Op != OpSignExt8to64 { - break - } - x := v_0.Args[0] - v.copyOf(x) - return true - } - // match: (Trunc64to8 (And64 (Const64 [y]) x)) - // cond: y&0xFF == 0xFF - // result: (Trunc64to8 x) - for { - if v_0.Op != OpAnd64 { - break - } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 { - if v_0_0.Op != OpConst64 { - continue - } - y := auxIntToInt64(v_0_0.AuxInt) - x := v_0_1 - if !(y&0xFF == 0xFF) { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { continue } - v.reset(OpTrunc64to8) - v.AddArg(x) + x := v_1 + v.copyOf(x) return true - } - break - } - return false -} -func rewriteValuegeneric_OpXor16(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Xor16 (Const16 [c]) (Const16 [d])) - // result: (Const16 [c^d]) + } + break + } + // match: (Xor64 (Com64 x) x) + // result: (Const64 [-1]) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst16 { + if v_0.Op != OpCom64 { continue } - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpConst16 { + x := v_0.Args[0] + if x != v_1 { continue } - d := auxIntToInt16(v_1.AuxInt) - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(c ^ d) + v.reset(OpConst64) + v.AuxInt = int64ToAuxInt(-1) return true } break } - // match: (Xor16 x x) - // result: (Const16 [0]) - for { - x := v_0 - if x != v_1 { - break - } - v.reset(OpConst16) - v.AuxInt = int16ToAuxInt(0) - return true - } - // match: (Xor16 (Const16 [0]) x) - // result: x + // match: (Xor64 (Const64 [-1]) x) + // result: (Com64 x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != 0 { + if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != -1 { continue } x := v_1 - v.copyOf(x) + v.reset(OpCom64) + v.AddArg(x) return true } break } - // match: (Xor16 x (Xor16 x y)) + // match: (Xor64 x (Xor64 x y)) // result: y for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpXor16 { + if v_1.Op != OpXor64 { continue } _ = v_1.Args[1] @@ -24148,12 +31166,12 @@ func rewriteValuegeneric_OpXor16(v *Value) bool { } break } - // match: (Xor16 (Xor16 i:(Const16 ) z) x) - // cond: (z.Op != OpConst16 && x.Op != OpConst16) - // result: (Xor16 i (Xor16 z x)) + // match: (Xor64 (Xor64 i:(Const64 ) z) x) + // cond: (z.Op != OpConst64 && x.Op != OpConst64) + // result: (Xor64 i (Xor64 z x)) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpXor16 { + if v_0.Op != OpXor64 { continue } _ = v_0.Args[1] @@ -24161,17 +31179,17 @@ func rewriteValuegeneric_OpXor16(v *Value) bool { v_0_1 := v_0.Args[1] for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { i := v_0_0 - if i.Op != OpConst16 { + if i.Op != OpConst64 { continue } t := i.Type z := v_0_1 x := v_1 - if !(z.Op != OpConst16 && x.Op != OpConst16) { + if !(z.Op != OpConst64 && x.Op != OpConst64) { continue } - v.reset(OpXor16) - v0 := b.NewValue0(v.Pos, OpXor16, t) + v.reset(OpXor64) + v0 := b.NewValue0(v.Pos, OpXor64, t) v0.AddArg2(z, x) v.AddArg2(i, v0) return true @@ -24179,295 +31197,341 @@ func rewriteValuegeneric_OpXor16(v *Value) bool { } break } - // match: (Xor16 (Const16 [c]) (Xor16 (Const16 [d]) x)) - // result: (Xor16 (Const16 [c^d]) x) + // match: (Xor64 (Const64 [c]) (Xor64 (Const64 [d]) x)) + // result: (Xor64 (Const64 [c^d]) x) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst16 { + if v_0.Op != OpConst64 { continue } t := v_0.Type - c := auxIntToInt16(v_0.AuxInt) - if v_1.Op != OpXor16 { + c := auxIntToInt64(v_0.AuxInt) + if v_1.Op != OpXor64 { continue } _ = v_1.Args[1] v_1_0 := v_1.Args[0] v_1_1 := v_1.Args[1] for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst16 || v_1_0.Type != t { + if v_1_0.Op != OpConst64 || v_1_0.Type != t { continue } - d := auxIntToInt16(v_1_0.AuxInt) + d := auxIntToInt64(v_1_0.AuxInt) x := v_1_1 - v.reset(OpXor16) - v0 := b.NewValue0(v.Pos, OpConst16, t) - v0.AuxInt = int16ToAuxInt(c ^ d) + v.reset(OpXor64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(c ^ d) v.AddArg2(v0, x) return true } } break } - return false -} -func rewriteValuegeneric_OpXor32(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Xor32 (Const32 [c]) (Const32 [d])) - // result: (Const32 [c^d]) + // match: (Xor64 (Lsh64x64 x z:(Const64 [c])) (Rsh64Ux64 x (Const64 [d]))) + // cond: c < 64 && d == 64-c && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst32 { + if v_0.Op != OpLsh64x64 { continue } - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpConst32 { + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { continue } - d := auxIntToInt32(v_1.AuxInt) - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(c ^ d) + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh64Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 64 && d == 64-c && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) return true } break } - // match: (Xor32 x x) - // result: (Const32 [0]) - for { - x := v_0 - if x != v_1 { - break - } - v.reset(OpConst32) - v.AuxInt = int32ToAuxInt(0) - return true - } - // match: (Xor32 (Const32 [0]) x) - // result: x + // match: (Xor64 left:(Lsh64x64 x y) right:(Rsh64Ux64 x (Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != 0 { + left := v_0 + if left.Op != OpLsh64x64 { continue } - x := v_1 - v.copyOf(x) + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) return true } break } - // match: (Xor32 x (Xor32 x y)) - // result: y + // match: (Xor64 left:(Lsh64x32 x y) right:(Rsh64Ux32 x (Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpXor32 { + left := v_0 + if left.Op != OpLsh64x32 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - y := v_1_1 - v.copyOf(y) - return true + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux32 { + continue } - } - break - } - // match: (Xor32 (Xor32 i:(Const32 ) z) x) - // cond: (z.Op != OpConst32 && x.Op != OpConst32) - // result: (Xor32 i (Xor32 z x)) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpXor32 { + _ = right.Args[1] + if x != right.Args[0] { continue } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { - i := v_0_0 - if i.Op != OpConst32 { - continue - } - t := i.Type - z := v_0_1 - x := v_1 - if !(z.Op != OpConst32 && x.Op != OpConst32) { - continue - } - v.reset(OpXor32) - v0 := b.NewValue0(v.Pos, OpXor32, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true } break } - // match: (Xor32 (Const32 [c]) (Xor32 (Const32 [d]) x)) - // result: (Xor32 (Const32 [c^d]) x) + // match: (Xor64 left:(Lsh64x16 x y) right:(Rsh64Ux16 x (Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst32 { + left := v_0 + if left.Op != OpLsh64x16 { continue } - t := v_0.Type - c := auxIntToInt32(v_0.AuxInt) - if v_1.Op != OpXor32 { + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux16 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst32 || v_1_0.Type != t { - continue - } - d := auxIntToInt32(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpXor32) - v0 := b.NewValue0(v.Pos, OpConst32, t) - v0.AuxInt = int32ToAuxInt(c ^ d) - v.AddArg2(v0, x) - return true + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) + return true } break } - return false -} -func rewriteValuegeneric_OpXor64(v *Value) bool { - v_1 := v.Args[1] - v_0 := v.Args[0] - b := v.Block - // match: (Xor64 (Const64 [c]) (Const64 [d])) - // result: (Const64 [c^d]) + // match: (Xor64 left:(Lsh64x8 x y) right:(Rsh64Ux8 x (Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst64 { + left := v_0 + if left.Op != OpLsh64x8 { continue } - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpConst64 { + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh64Ux8 { continue } - d := auxIntToInt64(v_1.AuxInt) - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(c ^ d) + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 64 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, y) return true } break } - // match: (Xor64 x x) - // result: (Const64 [0]) - for { - x := v_0 - if x != v_1 { - break - } - v.reset(OpConst64) - v.AuxInt = int64ToAuxInt(0) - return true - } - // match: (Xor64 (Const64 [0]) x) - // result: x + // match: (Xor64 right:(Rsh64Ux64 x y) left:(Lsh64x64 x z:(Sub64 (Const64 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != 0 { + right := v_0 + if right.Op != OpRsh64Ux64 { continue } - x := v_1 - v.copyOf(x) + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue + } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) return true } break } - // match: (Xor64 x (Xor64 x y)) - // result: y + // match: (Xor64 right:(Rsh64Ux32 x y) left:(Lsh64x32 x z:(Sub32 (Const32 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpXor64 { + right := v_0 + if right.Op != OpRsh64Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if x != v_1_0 { - continue - } - y := v_1_1 - v.copyOf(y) - return true + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true } break } - // match: (Xor64 (Xor64 i:(Const64 ) z) x) - // cond: (z.Op != OpConst64 && x.Op != OpConst64) - // result: (Xor64 i (Xor64 z x)) + // match: (Xor64 right:(Rsh64Ux16 x y) left:(Lsh64x16 x z:(Sub16 (Const16 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpXor64 { + right := v_0 + if right.Op != OpRsh64Ux16 { continue } - _ = v_0.Args[1] - v_0_0 := v_0.Args[0] - v_0_1 := v_0.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 { - i := v_0_0 - if i.Op != OpConst64 { - continue - } - t := i.Type - z := v_0_1 - x := v_1 - if !(z.Op != OpConst64 && x.Op != OpConst64) { - continue - } - v.reset(OpXor64) - v0 := b.NewValue0(v.Pos, OpXor64, t) - v0.AddArg2(z, x) - v.AddArg2(i, v0) - return true + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true } break } - // match: (Xor64 (Const64 [c]) (Xor64 (Const64 [d]) x)) - // result: (Xor64 (Const64 [c^d]) x) + // match: (Xor64 right:(Rsh64Ux8 x y) left:(Lsh64x8 x z:(Sub8 (Const8 [64]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64) + // result: (RotateLeft64 x z) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpConst64 { + right := v_0 + if right.Op != OpRsh64Ux8 { continue } - t := v_0.Type - c := auxIntToInt64(v_0.AuxInt) - if v_1.Op != OpXor64 { + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh64x8 { continue } - _ = v_1.Args[1] - v_1_0 := v_1.Args[0] - v_1_1 := v_1.Args[1] - for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { - if v_1_0.Op != OpConst64 || v_1_0.Type != t { - continue - } - d := auxIntToInt64(v_1_0.AuxInt) - x := v_1_1 - v.reset(OpXor64) - v0 := b.NewValue0(v.Pos, OpConst64, t) - v0.AuxInt = int64ToAuxInt(c ^ d) - v.AddArg2(v0, x) - return true + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 64 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 64)) { + continue } + v.reset(OpRotateLeft64) + v.AddArg2(x, z) + return true } break } @@ -24477,6 +31541,7 @@ func rewriteValuegeneric_OpXor8(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block + config := b.Func.Config // match: (Xor8 (Const8 [c]) (Const8 [d])) // result: (Const8 [c^d]) for { @@ -24519,6 +31584,37 @@ func rewriteValuegeneric_OpXor8(v *Value) bool { } break } + // match: (Xor8 (Com8 x) x) + // result: (Const8 [-1]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpCom8 { + continue + } + x := v_0.Args[0] + if x != v_1 { + continue + } + v.reset(OpConst8) + v.AuxInt = int8ToAuxInt(-1) + return true + } + break + } + // match: (Xor8 (Const8 [-1]) x) + // result: (Com8 x) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != -1 { + continue + } + x := v_1 + v.reset(OpCom8) + v.AddArg(x) + return true + } + break + } // match: (Xor8 x (Xor8 x y)) // result: y for { @@ -24602,6 +31698,314 @@ func rewriteValuegeneric_OpXor8(v *Value) bool { } break } + // match: (Xor8 (Lsh8x64 x z:(Const64 [c])) (Rsh8Ux64 x (Const64 [d]))) + // cond: c < 8 && d == 8-c && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpLsh8x64 { + continue + } + _ = v_0.Args[1] + x := v_0.Args[0] + z := v_0.Args[1] + if z.Op != OpConst64 { + continue + } + c := auxIntToInt64(z.AuxInt) + if v_1.Op != OpRsh8Ux64 { + continue + } + _ = v_1.Args[1] + if x != v_1.Args[0] { + continue + } + v_1_1 := v_1.Args[1] + if v_1_1.Op != OpConst64 { + continue + } + d := auxIntToInt64(v_1_1.AuxInt) + if !(c < 8 && d == 8-c && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor8 left:(Lsh8x64 x y) right:(Rsh8Ux64 x (Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x64 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux64 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub64 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst64 || auxIntToInt64(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor8 left:(Lsh8x32 x y) right:(Rsh8Ux32 x (Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x32 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux32 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub32 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst32 || auxIntToInt32(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor8 left:(Lsh8x16 x y) right:(Rsh8Ux16 x (Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x16 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux16 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub16 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst16 || auxIntToInt16(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor8 left:(Lsh8x8 x y) right:(Rsh8Ux8 x (Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + left := v_0 + if left.Op != OpLsh8x8 { + continue + } + y := left.Args[1] + x := left.Args[0] + right := v_1 + if right.Op != OpRsh8Ux8 { + continue + } + _ = right.Args[1] + if x != right.Args[0] { + continue + } + right_1 := right.Args[1] + if right_1.Op != OpSub8 { + continue + } + _ = right_1.Args[1] + right_1_0 := right_1.Args[0] + if right_1_0.Op != OpConst8 || auxIntToInt8(right_1_0.AuxInt) != 8 || y != right_1.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, y) + return true + } + break + } + // match: (Xor8 right:(Rsh8Ux64 x y) left:(Lsh8x64 x z:(Sub64 (Const64 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux64 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x64 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub64 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst64 || auxIntToInt64(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor8 right:(Rsh8Ux32 x y) left:(Lsh8x32 x z:(Sub32 (Const32 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux32 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x32 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub32 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst32 || auxIntToInt32(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor8 right:(Rsh8Ux16 x y) left:(Lsh8x16 x z:(Sub16 (Const16 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux16 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x16 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub16 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst16 || auxIntToInt16(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } + // match: (Xor8 right:(Rsh8Ux8 x y) left:(Lsh8x8 x z:(Sub8 (Const8 [8]) y))) + // cond: (shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8) + // result: (RotateLeft8 x z) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + right := v_0 + if right.Op != OpRsh8Ux8 { + continue + } + y := right.Args[1] + x := right.Args[0] + left := v_1 + if left.Op != OpLsh8x8 { + continue + } + _ = left.Args[1] + if x != left.Args[0] { + continue + } + z := left.Args[1] + if z.Op != OpSub8 { + continue + } + _ = z.Args[1] + z_0 := z.Args[0] + if z_0.Op != OpConst8 || auxIntToInt8(z_0.AuxInt) != 8 || y != z.Args[1] || !((shiftIsBounded(left) || shiftIsBounded(right)) && canRotate(config, 8)) { + continue + } + v.reset(OpRotateLeft8) + v.AddArg2(x, z) + return true + } + break + } return false } func rewriteValuegeneric_OpZero(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go index 4e3e5e75e358d7..ebf84d59b38ec6 100644 --- a/src/cmd/compile/internal/ssa/schedule.go +++ b/src/cmd/compile/internal/ssa/schedule.go @@ -14,6 +14,7 @@ const ( ScorePhi = iota // towards top of block ScoreArg ScoreNilCheck + ScoreCarryChainTail ScoreReadTuple ScoreVarDef ScoreMemory @@ -78,7 +79,7 @@ func (h ValHeap) Less(i, j int) bool { func (op Op) isLoweredGetClosurePtr() bool { switch op { case OpAMD64LoweredGetClosurePtr, OpPPC64LoweredGetClosurePtr, OpARMLoweredGetClosurePtr, OpARM64LoweredGetClosurePtr, - Op386LoweredGetClosurePtr, OpMIPS64LoweredGetClosurePtr, OpS390XLoweredGetClosurePtr, OpMIPSLoweredGetClosurePtr, + Op386LoweredGetClosurePtr, OpMIPS64LoweredGetClosurePtr, OpLOONG64LoweredGetClosurePtr, OpS390XLoweredGetClosurePtr, OpMIPSLoweredGetClosurePtr, OpRISCV64LoweredGetClosurePtr, OpWasmLoweredGetClosurePtr: return true } @@ -128,7 +129,8 @@ func schedule(f *Func) { v.Op == OpARMLoweredNilCheck || v.Op == OpARM64LoweredNilCheck || v.Op == Op386LoweredNilCheck || v.Op == OpMIPS64LoweredNilCheck || v.Op == OpS390XLoweredNilCheck || v.Op == OpMIPSLoweredNilCheck || - v.Op == OpRISCV64LoweredNilCheck || v.Op == OpWasmLoweredNilCheck: + v.Op == OpRISCV64LoweredNilCheck || v.Op == OpWasmLoweredNilCheck || + v.Op == OpLOONG64LoweredNilCheck: // Nil checks must come before loads from the same address. score[v.ID] = ScoreNilCheck case v.Op == OpPhi: @@ -153,12 +155,34 @@ func schedule(f *Func) { // VARDEF ops are scheduled before the corresponding LEA. score[v.ID] = ScoreMemory case v.Op == OpSelect0 || v.Op == OpSelect1 || v.Op == OpSelectN: - // Schedule the pseudo-op of reading part of a tuple - // immediately after the tuple-generating op, since - // this value is already live. This also removes its - // false dependency on the other part of the tuple. - // Also ensures tuple is never spilled. - score[v.ID] = ScoreReadTuple + if (v.Op == OpSelect1 || v.Op == OpSelect0) && (v.Args[0].Op.isCarry() || v.Type.IsFlags()) { + // When the Select pseudo op is being used for a carry or flag from + // a tuple then score it as ScoreFlags so it happens later. This + // prevents the bit from being clobbered before it is used. + score[v.ID] = ScoreFlags + } else { + score[v.ID] = ScoreReadTuple + } + case v.Op.isCarry(): + if w := v.getCarryProducer(); w != nil { + // The producing op is not the final user of the carry bit. Its + // current score is one of unscored, Flags, or CarryChainTail. + // These occur if the producer has not been scored, another user + // of the producers carry flag was scored (there are >1 users of + // the carry out flag), or it was visited earlier and already + // scored CarryChainTail (and prove w is not a tail). + score[w.ID] = ScoreFlags + } + // Verify v has not been scored. If v has not been visited, v may be + // the final (tail) operation in a carry chain. If v is not, v will be + // rescored above when v's carry-using op is scored. When scoring is done, + // only tail operations will retain the CarryChainTail score. + if score[v.ID] != ScoreFlags { + // Score the tail of carry chain operations to a lower (earlier in the + // block) priority. This creates a priority inversion which allows only + // one chain to be scheduled, if possible. + score[v.ID] = ScoreCarryChainTail + } case v.Type.IsFlags() || v.Type.IsTuple() && v.Type.FieldType(1).IsFlags(): // Schedule flag register generation as late as possible. // This makes sure that we only have one live flags @@ -220,7 +244,7 @@ func schedule(f *Func) { // unless they are phi values (which must be first). // OpArg also goes first -- if it is stack it register allocates // to a LoadReg, if it is register it is from the beginning anyway. - if c.Op == OpPhi || c.Op == OpArg { + if score[c.ID] == ScorePhi || score[c.ID] == ScoreArg { continue } score[c.ID] = ScoreControl @@ -263,6 +287,14 @@ func schedule(f *Func) { v := heap.Pop(priq).(*Value) + if f.pass.debug > 1 && score[v.ID] == ScoreCarryChainTail && v.Op.isCarry() { + // Add some debugging noise if the chain of carrying ops will not + // likely be scheduled without potential carry flag clobbers. + if !isCarryChainReady(v, uses) { + f.Warnl(v.Pos, "carry chain ending with %v not ready", v) + } + } + // Add it to the schedule. // Do not emit tuple-reading ops until we're ready to emit the tuple-generating op. //TODO: maybe remove ReadTuple score above, if it does not help on performance @@ -338,13 +370,15 @@ func schedule(f *Func) { // if v transitively depends on store s, v is ordered after s, // otherwise v is ordered before s. // Specifically, values are ordered like -// store1 -// NilCheck that depends on store1 -// other values that depends on store1 -// store2 -// NilCheck that depends on store2 -// other values that depends on store2 -// ... +// +// store1 +// NilCheck that depends on store1 +// other values that depends on store1 +// store2 +// NilCheck that depends on store2 +// other values that depends on store2 +// ... +// // The order of non-store and non-NilCheck values are undefined // (not necessarily dependency order). This should be cheaper // than a full scheduling as done above. @@ -517,6 +551,46 @@ func storeOrder(values []*Value, sset *sparseSet, storeNumber []int32) []*Value return order } +// Return whether all dependent carry ops can be scheduled after this. +func isCarryChainReady(v *Value, uses []int32) bool { + // A chain can be scheduled in it's entirety if + // the use count of each dependent op is 1. If none, + // schedule the first. + j := 1 // The first op uses[k.ID] == 0. Dependent ops are always >= 1. + for k := v; k != nil; k = k.getCarryProducer() { + j += int(uses[k.ID]) - 1 + } + return j == 0 +} + +// Return whether op is an operation which produces a carry bit value, but does not consume it. +func (op Op) isCarryCreator() bool { + switch op { + case OpPPC64SUBC, OpPPC64ADDC, OpPPC64SUBCconst, OpPPC64ADDCconst: + return true + } + return false +} + +// Return whether op consumes or creates a carry a bit value. +func (op Op) isCarry() bool { + switch op { + case OpPPC64SUBE, OpPPC64ADDE, OpPPC64SUBZEzero, OpPPC64ADDZEzero: + return true + } + return op.isCarryCreator() +} + +// Return the producing *Value of the carry bit of this op, or nil if none. +func (v *Value) getCarryProducer() *Value { + if v.Op.isCarry() && !v.Op.isCarryCreator() { + // PPC64 carry dependencies are conveyed through their final argument. + // Likewise, there is always an OpSelect1 between them. + return v.Args[len(v.Args)-1].Args[0] + } + return nil +} + type bySourcePos []*Value func (s bySourcePos) Len() int { return len(s) } diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go index 3876d8df12c264..06c2f6720ff7ff 100644 --- a/src/cmd/compile/internal/ssa/shift_test.go +++ b/src/cmd/compile/internal/ssa/shift_test.go @@ -85,7 +85,7 @@ func TestShiftToExtensionAMD64(t *testing.T) { // makeShiftExtensionFunc generates a function containing: // -// (rshift (lshift (Const64 [amount])) (Const64 [amount])) +// (rshift (lshift (Const64 [amount])) (Const64 [amount])) // // This may be equivalent to a sign or zero extension. func makeShiftExtensionFunc(c *Conf, amount int64, lshift, rshift Op, typ *types.Type) fun { diff --git a/src/cmd/compile/internal/ssa/shortcircuit.go b/src/cmd/compile/internal/ssa/shortcircuit.go index 29abf3c591f902..5f1f8921207a49 100644 --- a/src/cmd/compile/internal/ssa/shortcircuit.go +++ b/src/cmd/compile/internal/ssa/shortcircuit.go @@ -67,11 +67,11 @@ func shortcircuit(f *Func) { // // (1) Look for a CFG of the form // -// p other pred(s) -// \ / -// b -// / \ -// t other succ +// p other pred(s) +// \ / +// b +// / \ +// t other succ // // in which b is an If block containing a single phi value with a single use (b's Control), // which has a ConstBool arg. @@ -80,21 +80,21 @@ func shortcircuit(f *Func) { // // Rewrite this into // -// p other pred(s) -// | / -// | b -// |/ \ -// t u +// p other pred(s) +// | / +// | b +// |/ \ +// t u // // and remove the appropriate phi arg(s). // // (2) Look for a CFG of the form // -// p q -// \ / -// b -// / \ -// t u +// p q +// \ / +// b +// / \ +// t u // // in which b is as described in (1). // However, b may also contain other phi values. @@ -196,11 +196,7 @@ func shortcircuitBlock(b *Block) bool { // Remove b's incoming edge from p. b.removePred(cidx) - n := len(b.Preds) - ctl.Args[cidx].Uses-- - ctl.Args[cidx] = ctl.Args[n] - ctl.Args[n] = nil - ctl.Args = ctl.Args[:n] + b.removePhiArg(ctl, cidx) // Redirect p's outgoing edge to t. p.Succs[pi] = Edge{t, len(t.Preds)} diff --git a/src/cmd/compile/internal/ssa/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go index a8a8f836294b97..351f824a9f579a 100644 --- a/src/cmd/compile/internal/ssa/softfloat.go +++ b/src/cmd/compile/internal/ssa/softfloat.go @@ -63,6 +63,7 @@ func softfloat(f *Func) { v.Aux = f.Config.Types.UInt32 case 8: v.Aux = f.Config.Types.UInt64 + newInt64 = true default: v.Fatalf("bad float type with size %d", size) } diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go index be914c8644de42..9f4e0007d3a931 100644 --- a/src/cmd/compile/internal/ssa/sparsetree.go +++ b/src/cmd/compile/internal/ssa/sparsetree.go @@ -207,9 +207,10 @@ func (t SparseTree) isAncestor(x, y *Block) bool { // 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, +// 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 { diff --git a/src/cmd/compile/internal/ssa/stmtlines_test.go b/src/cmd/compile/internal/ssa/stmtlines_test.go index a510d0b3d0607c..673c88a6009902 100644 --- a/src/cmd/compile/internal/ssa/stmtlines_test.go +++ b/src/cmd/compile/internal/ssa/stmtlines_test.go @@ -2,6 +2,7 @@ package ssa_test import ( cmddwarf "cmd/internal/dwarf" + "cmd/internal/quoted" "debug/dwarf" "debug/elf" "debug/macho" @@ -57,7 +58,11 @@ func TestStmtLines(t *testing.T) { if extld == "" { extld = "gcc" } - enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extld) + extldArgs, err := quoted.Split(extld) + if err != nil { + t.Fatal(err) + } + enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs) if err != nil { t.Fatal(err) } @@ -84,6 +89,9 @@ func TestStmtLines(t *testing.T) { if pkgname == "runtime" { continue } + if pkgname == "crypto/internal/nistec/fiat" { + continue // golang.org/issue/49372 + } if e.Val(dwarf.AttrStmtList) == nil { continue } @@ -110,12 +118,17 @@ func TestStmtLines(t *testing.T) { } } + var m int if runtime.GOARCH == "amd64" { - if len(nonStmtLines)*100 > len(lines) { // > 99% obtained on amd64, no backsliding - t.Errorf("Saw too many (amd64, > 1%%) lines without statement marks, total=%d, nostmt=%d ('-run TestStmtLines -v' lists failing lines)\n", len(lines), len(nonStmtLines)) - } - } else if len(nonStmtLines)*100 > 2*len(lines) { // expect 98% elsewhere. - t.Errorf("Saw too many (not amd64, > 2%%) lines without statement marks, total=%d, nostmt=%d ('-run TestStmtLines -v' lists failing lines)\n", len(lines), len(nonStmtLines)) + m = 1 // > 99% obtained on amd64, no backsliding + } else if runtime.GOARCH == "riscv64" { + m = 3 // XXX temporary update threshold to 97% for regabi + } else { + m = 2 // expect 98% elsewhere. + } + + if len(nonStmtLines)*100 > m*len(lines) { + t.Errorf("Saw too many (%s, > %d%%) lines without statement marks, total=%d, nostmt=%d ('-run TestStmtLines -v' lists failing lines)\n", runtime.GOARCH, m, len(lines), len(nonStmtLines)) } t.Logf("Saw %d out of %d lines without statement marks", len(nonStmtLines), len(lines)) if testing.Verbose() { diff --git a/src/cmd/compile/internal/ssa/testdata/b53456.go b/src/cmd/compile/internal/ssa/testdata/b53456.go new file mode 100644 index 00000000000000..8104d3ed473862 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/b53456.go @@ -0,0 +1,19 @@ +package main + +type T struct { + m map[int]int +} + +func main() { + t := T{ + m: make(map[int]int), + } + t.Inc(5) + t.Inc(7) +} + +func (s *T) Inc(key int) { + v := s.m[key] // break, line 16 + v++ + s.m[key] = v // also here +} diff --git a/src/cmd/compile/internal/ssa/testdata/convertline.go b/src/cmd/compile/internal/ssa/testdata/convertline.go new file mode 100644 index 00000000000000..08f3ae8a35be3f --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/convertline.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func F[T any](n T) { + fmt.Printf("called\n") +} + +func G[T any](n T) { + F(n) + fmt.Printf("after\n") +} + +func main() { + G(3) +} diff --git a/src/cmd/compile/internal/ssa/testdata/inline-dump.go b/src/cmd/compile/internal/ssa/testdata/inline-dump.go new file mode 100644 index 00000000000000..97893b6f212f98 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/inline-dump.go @@ -0,0 +1,17 @@ +package foo + +func f(m, n int) int { + a := g(n) + b := g(m) + return a + b +} + +func g(x int) int { + y := h(x + 1) + z := h(x - 1) + return y + z +} + +func h(x int) int { + return x * x +} diff --git a/src/cmd/compile/internal/ssa/testdata/pushback.go b/src/cmd/compile/internal/ssa/testdata/pushback.go new file mode 100644 index 00000000000000..754e6cbb230787 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/pushback.go @@ -0,0 +1,30 @@ +package main + +type Node struct { + Circular bool +} + +type ExtNode[V any] struct { + v V + Node +} + +type List[V any] struct { + root *ExtNode[V] + len int +} + +func (list *List[V]) PushBack(arg V) { + if list.len == 0 { + list.root = &ExtNode[V]{v: arg} + list.root.Circular = true + list.len++ + return + } + list.len++ +} + +func main() { + var v List[int] + v.PushBack(1) +} diff --git a/src/cmd/compile/internal/ssa/testdata/sayhi.go b/src/cmd/compile/internal/ssa/testdata/sayhi.go new file mode 100644 index 00000000000000..680e1eb3a18f35 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/sayhi.go @@ -0,0 +1,12 @@ +package foo + +import ( + "fmt" + "sync" +) + +func sayhi(n int, wg *sync.WaitGroup) { + fmt.Println("hi", n) + fmt.Println("hi", n) + wg.Done() +} diff --git a/src/cmd/compile/internal/ssa/trim.go b/src/cmd/compile/internal/ssa/trim.go index c930a205c17f3b..1fd7b33d5f2d5b 100644 --- a/src/cmd/compile/internal/ssa/trim.go +++ b/src/cmd/compile/internal/ssa/trim.go @@ -130,11 +130,11 @@ func emptyBlock(b *Block) bool { // trimmableBlock reports whether the block can be trimmed from the CFG, // subject to the following criteria: -// - it should not be the first block -// - it should be BlockPlain -// - it should not loop back to itself -// - it either is the single predecessor of the successor block or -// contains no actual instructions +// - it should not be the first block +// - it should be BlockPlain +// - it should not loop back to itself +// - it either is the single predecessor of the successor block or +// contains no actual instructions func trimmableBlock(b *Block) bool { if b.Kind != BlockPlain || b == b.Func.Entry { return false diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 630e4814b99f1b..9f2f4689e745e2 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -228,6 +228,7 @@ func (v *Value) auxString() string { // If/when midstack inlining is enabled (-l=4), the compiler gets both larger and slower. // Not-inlining this method is a help (*Value.reset and *Block.NewValue0 are similar). +// //go:noinline func (v *Value) AddArg(w *Value) { if v.Args == nil { @@ -302,12 +303,6 @@ func (v *Value) SetArg(i int, w *Value) { v.Args[i] = w w.Uses++ } -func (v *Value) RemoveArg(i int) { - v.Args[i].Uses-- - copy(v.Args[i:], v.Args[i+1:]) - v.Args[len(v.Args)-1] = nil // aid GC - v.Args = v.Args[:len(v.Args)-1] -} func (v *Value) SetArgs1(a *Value) { v.resetArgs() v.AddArg(a) @@ -337,6 +332,7 @@ func (v *Value) resetArgs() { // reset is called from most rewrite rules. // Allowing it to be inlined increases the size // of cmd/compile by almost 10%, and slows it down. +// //go:noinline func (v *Value) reset(op Op) { if v.InCache { @@ -351,11 +347,13 @@ func (v *Value) reset(op Op) { // invalidateRecursively marks a value as invalid (unused) // and after decrementing reference counts on its Args, // also recursively invalidates any of those whose use -// count goes to zero. +// count goes to zero. It returns whether any of the +// invalidated values was marked with IsStmt. // // BEWARE of doing this *before* you've applied intended // updates to SSA. -func (v *Value) invalidateRecursively() { +func (v *Value) invalidateRecursively() bool { + lostStmt := v.Pos.IsStmt() == src.PosIsStmt if v.InCache { v.Block.Func.unCache(v) } @@ -364,7 +362,8 @@ func (v *Value) invalidateRecursively() { for _, a := range v.Args { a.Uses-- if a.Uses == 0 { - a.invalidateRecursively() + lost := a.invalidateRecursively() + lostStmt = lost || lostStmt } } @@ -375,10 +374,12 @@ func (v *Value) invalidateRecursively() { v.AuxInt = 0 v.Aux = nil + return lostStmt } // copyOf is called from rewrite rules. // It modifies v to be (Copy a). +// //go:noinline func (v *Value) copyOf(a *Value) { if v == a { @@ -519,7 +520,7 @@ func (v *Value) LackingPos() bool { // The exact definition of LackingPos is somewhat heuristically defined and may change // in the future, for example if some of these operations are generated more carefully // with respect to their source position. - return v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive || v.Op == OpPhi || + return v.Op == OpVarDef || v.Op == OpVarLive || v.Op == OpPhi || (v.Op == OpFwdRef || v.Op == OpCopy) && v.Type == types.TypeMem } diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 419d91d0d367e6..cb8c0a5e0eb7b4 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -80,11 +80,11 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool { // when necessary (the condition above). It rewrites store ops to branches // and runtime calls, like // -// if writeBarrier.enabled { -// gcWriteBarrier(ptr, val) // Not a regular Go call -// } else { -// *ptr = val -// } +// if writeBarrier.enabled { +// gcWriteBarrier(ptr, val) // Not a regular Go call +// } else { +// *ptr = val +// } // // A sequence of WB stores for many pointer fields of a single type will // be emitted together, with a single branch. @@ -163,7 +163,7 @@ func writebarrier(f *Func) { last = w end = i + 1 } - case OpVarDef, OpVarLive, OpVarKill: + case OpVarDef, OpVarLive: continue default: if last == nil { @@ -279,7 +279,7 @@ func writebarrier(f *Func) { fn = typedmemclr typ = reflectdata.TypeLinksym(w.Aux.(*types.Type)) nWBops-- - case OpVarDef, OpVarLive, OpVarKill: + case OpVarDef, OpVarLive: } // then block: emit write barrier call @@ -301,7 +301,7 @@ func writebarrier(f *Func) { } // Note that we set up a writebarrier function call. f.fe.SetWBPos(pos) - case OpVarDef, OpVarLive, OpVarKill: + case OpVarDef, OpVarLive: memThen = bThen.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, memThen) } @@ -315,17 +315,11 @@ func writebarrier(f *Func) { case OpZeroWB: memElse = bElse.NewValue2I(pos, OpZero, types.TypeMem, w.AuxInt, ptr, memElse) memElse.Aux = w.Aux - case OpVarDef, OpVarLive, OpVarKill: + case OpVarDef, OpVarLive: memElse = bElse.NewValue1A(pos, w.Op, types.TypeMem, w.Aux, memElse) } } - // mark volatile temps dead - for _, c := range volatiles { - tmpNode := c.tmp.Aux - memThen = bThen.NewValue1A(memThen.Pos, OpVarKill, types.TypeMem, tmpNode, memThen) - } - // merge memory // Splice memory Phi into the last memory of the original sequence, // which may be used in subsequent blocks. Other memories in the @@ -392,6 +386,14 @@ func (f *Func) computeZeroMap() map[ID]ZeroRegion { for _, b := range f.Blocks { for _, v := range b.Values { if mem, ok := IsNewObject(v); ok { + // While compiling package runtime itself, we might see user + // calls to newobject, which will have result type + // unsafe.Pointer instead. We can't easily infer how large the + // allocated memory is, so just skip it. + if types.LocalPkg.Path == "runtime" && v.Type.IsUnsafePtr() { + continue + } + nptr := v.Type.Elem().Size() / ptrSize if nptr > 64 { nptr = 64 @@ -486,7 +488,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va inRegs := b.Func.ABIDefault == b.Func.ABI1 && len(config.intParamRegs) >= 3 // put arguments on stack - off := config.ctxt.FixedFrameSize() + off := config.ctxt.Arch.FixedFrameSize var argTypes []*types.Type if typ != nil { // for typedmemmove @@ -529,7 +531,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va // issue call call := b.NewValue0A(pos, OpStaticCall, types.TypeResultMem, StaticAuxCall(fn, b.Func.ABIDefault.ABIAnalyzeTypes(nil, argTypes, nil))) call.AddArgs(wbargs...) - call.AuxInt = off - config.ctxt.FixedFrameSize() + call.AuxInt = off - config.ctxt.Arch.FixedFrameSize return b.NewValue1I(pos, OpSelectN, types.TypeMem, 0, call) } @@ -544,7 +546,7 @@ func IsStackAddr(v *Value) bool { v = v.Args[0] } switch v.Op { - case OpSP, OpLocalAddr, OpSelectNAddr: + case OpSP, OpLocalAddr, OpSelectNAddr, OpGetCallerSP: return true } return false @@ -552,6 +554,9 @@ func IsStackAddr(v *Value) bool { // IsGlobalAddr reports whether v is known to be an address of a global (or nil). func IsGlobalAddr(v *Value) bool { + for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy { + v = v.Args[0] + } if v.Op == OpAddr && v.Args[0].Op == OpSB { return true // address of a global } @@ -626,7 +631,7 @@ func IsNewObject(v *Value) (mem *Value, ok bool) { if v.Args[0].Args[0].Op != OpSP { return nil, false } - if v.Args[0].AuxInt != c.ctxt.FixedFrameSize()+c.RegSize { // offset of return value + if v.Args[0].AuxInt != c.ctxt.Arch.FixedFrameSize+c.RegSize { // offset of return value return nil, false } return mem, true @@ -647,7 +652,7 @@ func IsSanitizerSafeAddr(v *Value) bool { // read-only once initialized. return true case OpAddr: - return v.Aux.(*obj.LSym).Type == objabi.SRODATA + return v.Aux.(*obj.LSym).Type == objabi.SRODATA || v.Aux.(*obj.LSym).Type == objabi.SLIBFUZZER_8BIT_COUNTER } return false } diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go index e460adaf95d143..50ea86d8fcf70e 100644 --- a/src/cmd/compile/internal/ssagen/abi.go +++ b/src/cmd/compile/internal/ssagen/abi.go @@ -14,11 +14,9 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" - "cmd/compile/internal/staticdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" - "cmd/internal/objabi" ) // SymABIs records information provided by the assembler about symbol @@ -26,33 +24,27 @@ import ( type SymABIs struct { defs map[string]obj.ABI refs map[string]obj.ABISet - - localPrefix string } -func NewSymABIs(myimportpath string) *SymABIs { - var localPrefix string - if myimportpath != "" { - localPrefix = objabi.PathToPrefix(myimportpath) + "." - } - +func NewSymABIs() *SymABIs { return &SymABIs{ - defs: make(map[string]obj.ABI), - refs: make(map[string]obj.ABISet), - localPrefix: localPrefix, + defs: make(map[string]obj.ABI), + refs: make(map[string]obj.ABISet), } } // canonicalize returns the canonical name used for a linker symbol in // s's maps. Symbols in this package may be written either as "".X or // with the package's import path already in the symbol. This rewrites -// both to `"".`, which matches compiler-generated linker symbol names. +// both to use the full path, which matches compiler-generated linker +// symbol names. func (s *SymABIs) canonicalize(linksym string) string { - // If the symbol is already prefixed with localPrefix, - // rewrite it to start with "" so it matches the - // compiler's internal symbol names. - if s.localPrefix != "" && strings.HasPrefix(linksym, s.localPrefix) { - return `"".` + linksym[len(s.localPrefix):] + // If the symbol is already prefixed with "", rewrite it to start + // with LocalPkg.Prefix. + // + // TODO(mdempsky): Have cmd/asm stop writing out symbols like this. + if strings.HasPrefix(linksym, `"".`) { + return types.LocalPkg.Prefix + linksym[2:] } return linksym } @@ -141,17 +133,19 @@ func (s *SymABIs) GenABIWrappers() { continue } sym := nam.Sym() - var symName string - if sym.Linkname != "" { - symName = s.canonicalize(sym.Linkname) - } else { - // These names will already be canonical. + + symName := sym.Linkname + if symName == "" { symName = sym.Pkg.Prefix + "." + sym.Name } + symName = s.canonicalize(symName) // Apply definitions. defABI, hasDefABI := s.defs[symName] if hasDefABI { + if len(fn.Body) != 0 { + base.ErrorfAt(fn.Pos(), "%v defined in both Go and assembly", fn) + } fn.ABI = defABI } @@ -215,8 +209,6 @@ func (s *SymABIs) GenABIWrappers() { } if !buildcfg.Experiment.RegabiWrappers { - // We'll generate ABI aliases instead of - // wrappers once we have LSyms in InitLSym. continue } @@ -242,22 +234,6 @@ func InitLSym(f *ir.Func, hasBody bool) { if f.Pragma&ir.Systemstack != 0 { f.LSym.Set(obj.AttrCFunc, true) } - if f.ABI == obj.ABIInternal || !buildcfg.Experiment.RegabiWrappers { - // Function values can only point to - // ABIInternal entry points. This will create - // the funcsym for either the defining - // function or its wrapper as appropriate. - // - // If we're using ABI aliases instead of - // wrappers, we only InitLSym for the defining - // ABI of a function, so we make the funcsym - // when we see that. - staticdata.NeedFuncSym(f) - } - if !buildcfg.Experiment.RegabiWrappers { - // Create ABI aliases instead of wrappers. - forEachWrapperABI(f, makeABIAlias) - } } if hasBody { setupTextLSym(f, 0) @@ -278,22 +254,6 @@ func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) { } } -// makeABIAlias creates a new ABI alias so calls to f via wrapperABI -// will be resolved directly to f's ABI by the linker. -func makeABIAlias(f *ir.Func, wrapperABI obj.ABI) { - // These LSyms have the same name as the native function, so - // we create them directly rather than looking them up. - // The uniqueness of f.lsym ensures uniqueness of asym. - asym := &obj.LSym{ - Name: f.LSym.Name, - Type: objabi.SABIALIAS, - R: []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational" - } - asym.SetABI(wrapperABI) - asym.Set(obj.AttrDuplicateOK, true) - base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym) -} - // makeABIWrapper creates a new function that will be called with // wrapperABI and calls "f" using f.ABI. func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { @@ -313,18 +273,14 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { // below to handle the receiver. Panic if we see this scenario. ft := f.Nname.Type() if ft.NumRecvs() != 0 { - panic("makeABIWrapper support for wrapping methods not implemented") + base.ErrorfAt(f.Pos(), "makeABIWrapper support for wrapping methods not implemented") + return } - // Manufacture a new func type to use for the wrapper. - var noReceiver *ir.Field - tfn := ir.NewFuncType(base.Pos, - noReceiver, + // Reuse f's types.Sym to create a new ODCLFUNC/function. + fn := typecheck.DeclFunc(f.Nname.Sym(), nil, typecheck.NewFuncParams(ft.Params(), true), typecheck.NewFuncParams(ft.Results(), false)) - - // Reuse f's types.Sym to create a new ODCLFUNC/function. - fn := typecheck.DeclFunc(f.Nname.Sym(), tfn) fn.ABI = wrapperABI fn.SetABIWrapper(true) @@ -366,7 +322,7 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { // to allocate any stack space). Doing this will require some // extra work in typecheck/walk/ssa, might want to add a new node // OTAILCALL or something to this effect. - tailcall := tfn.Type().NumResults() == 0 && tfn.Type().NumParams() == 0 && tfn.Type().NumRecvs() == 0 + tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0 if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink { // cannot tailcall on PPC64 with dynamic linking, as we need // to restore R2 after call. @@ -379,18 +335,16 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { } var tail ir.Node + call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil) + call.Args = ir.ParamNames(fn.Type()) + call.IsDDD = fn.Type().IsVariadic() + tail = call if tailcall { - tail = ir.NewTailCallStmt(base.Pos, f.Nname) - } else { - call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil) - call.Args = ir.ParamNames(tfn.Type()) - call.IsDDD = tfn.Type().IsVariadic() - tail = call - if tfn.Type().NumResults() > 0 { - n := ir.NewReturnStmt(base.Pos, nil) - n.Results = []ir.Node{call} - tail = n - } + tail = ir.NewTailCallStmt(base.Pos, call) + } else if fn.Type().NumResults() > 0 { + n := ir.NewReturnStmt(base.Pos, nil) + n.Results = []ir.Node{call} + tail = n } fn.Body.Append(tail) diff --git a/src/cmd/compile/internal/ssagen/arch.go b/src/cmd/compile/internal/ssagen/arch.go index 7215f42c059a14..483e45cad43c74 100644 --- a/src/cmd/compile/internal/ssagen/arch.go +++ b/src/cmd/compile/internal/ssagen/arch.go @@ -29,8 +29,7 @@ type ArchInfo struct { // at function entry, and it is ok to clobber registers. ZeroRange func(*objw.Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog - Ginsnop func(*objw.Progs) *obj.Prog - Ginsnopdefer func(*objw.Progs) *obj.Prog // special ginsnop for deferreturn + Ginsnop func(*objw.Progs) *obj.Prog // SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. SSAMarkMoves func(*State, *ssa.Block) @@ -42,10 +41,10 @@ type ArchInfo struct { // for all values in the block before SSAGenBlock. SSAGenBlock func(s *State, b, next *ssa.Block) - // LoadRegResults emits instructions that loads register-assigned results - // into registers. They are already in memory (PPARAMOUT nodes). - // Used in open-coded defer return path. - LoadRegResults func(s *State, f *ssa.Func) + // LoadRegResult emits instructions that loads register-assigned result + // at n+off (n is PPARAMOUT) to register reg. The result is already in + // memory. Used in open-coded defer return path. + LoadRegResult func(s *State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog // SpillArgReg emits instructions that spill reg to n+off. SpillArgReg func(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog diff --git a/src/cmd/compile/internal/ssagen/nowb.go b/src/cmd/compile/internal/ssagen/nowb.go index 1fbc6a847d0f07..2085618b889d37 100644 --- a/src/cmd/compile/internal/ssagen/nowb.go +++ b/src/cmd/compile/internal/ssagen/nowb.go @@ -5,8 +5,8 @@ package ssagen import ( - "bytes" "fmt" + "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" @@ -179,7 +179,7 @@ func (c *nowritebarrierrecChecker) check() { // Check fn. if fn.WBPos.IsKnown() { - var err bytes.Buffer + var err strings.Builder call := funcs[fn] for call.target != nil { fmt.Fprintf(&err, "\n\t%v: called by %v", base.FmtPos(call.lineno), call.target.Nname) diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go index 62567535d76cac..9aaf4b81e0e60a 100644 --- a/src/cmd/compile/internal/ssagen/pgen.go +++ b/src/cmd/compile/internal/ssagen/pgen.go @@ -57,8 +57,8 @@ func cmpstackvarlt(a, b *ir.Name) bool { return ap } - if a.Type().Width != b.Type().Width { - return a.Type().Width > b.Type().Width + if a.Type().Size() != b.Type().Size() { + return a.Type().Size() > b.Type().Size() } return a.Sym().Name < b.Sym().Name @@ -75,12 +75,28 @@ func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // allocate space. In particular, it excludes arguments and results, which are in // the callers frame. func needAlloc(n *ir.Name) bool { - return n.Class == ir.PAUTO || n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters() + if n.Op() != ir.ONAME { + base.FatalfAt(n.Pos(), "%v has unexpected Op %v", n, n.Op()) + } + + switch n.Class { + case ir.PAUTO: + return true + case ir.PPARAM: + return false + case ir.PPARAMOUT: + return n.IsOutputParamInRegisters() + + default: + base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class) + return false + } } func (s *ssafn) AllocFrame(f *ssa.Func) { s.stksize = 0 s.stkptrsize = 0 + s.stkalign = int64(types.RegSize) fn := s.curfn // Mark the PAUTO's unused. @@ -114,7 +130,10 @@ func (s *ssafn) AllocFrame(f *ssa.Func) { } } - sort.Sort(byStackVar(fn.Dcl)) + // Use sort.Stable instead of sort.Sort so stack layout (and thus + // compiler output) is less sensitive to frontend changes that + // introduce or remove unused variables. + sort.Stable(byStackVar(fn.Dcl)) // Reassign stack offsets of the locals that are used. lastHasPtr := false @@ -129,7 +148,7 @@ func (s *ssafn) AllocFrame(f *ssa.Func) { } types.CalcSize(n.Type()) - w := n.Type().Width + w := n.Type().Size() if w >= types.MaxWidth || w < 0 { base.Fatalf("bad width") } @@ -141,7 +160,10 @@ func (s *ssafn) AllocFrame(f *ssa.Func) { w = 1 } s.stksize += w - s.stksize = types.Rnd(s.stksize, int64(n.Type().Align)) + s.stksize = types.RoundUp(s.stksize, n.Type().Alignment()) + if n.Type().Alignment() > int64(types.RegSize) { + s.stkalign = n.Type().Alignment() + } if n.Type().HasPointers() { s.stkptrsize = s.stksize lastHasPtr = true @@ -151,8 +173,8 @@ func (s *ssafn) AllocFrame(f *ssa.Func) { n.SetFrameOffset(-s.stksize) } - s.stksize = types.Rnd(s.stksize, int64(types.RegSize)) - s.stkptrsize = types.Rnd(s.stkptrsize, int64(types.RegSize)) + s.stksize = types.RoundUp(s.stksize, s.stkalign) + s.stkptrsize = types.RoundUp(s.stkptrsize, s.stkalign) } const maxStackSize = 1 << 30 @@ -207,13 +229,13 @@ func StackOffset(slot ssa.LocalSlot) int32 { switch n.Class { case ir.PPARAM, ir.PPARAMOUT: if !n.IsOutputParamInRegisters() { - off = n.FrameOffset() + base.Ctxt.FixedFrameSize() + off = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize break } fallthrough // PPARAMOUT in registers allocates like an AUTO case ir.PAUTO: off = n.FrameOffset() - if base.Ctxt.FixedFrameSize() == 0 { + if base.Ctxt.Arch.FixedFrameSize == 0 { off -= int64(types.PtrSize) } if buildcfg.FramePointerEnabled { diff --git a/src/cmd/compile/internal/ssagen/pgen_test.go b/src/cmd/compile/internal/ssagen/pgen_test.go deleted file mode 100644 index 69ed8ad74e9744..00000000000000 --- a/src/cmd/compile/internal/ssagen/pgen_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssagen - -import ( - "reflect" - "sort" - "testing" - - "cmd/compile/internal/ir" - "cmd/compile/internal/typecheck" - "cmd/compile/internal/types" - "cmd/internal/src" -) - -func typeWithoutPointers() *types.Type { - return types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(src.NoXPos, nil, types.New(types.TINT)), - }) -} - -func typeWithPointers() *types.Type { - return types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(src.NoXPos, nil, types.NewPtr(types.New(types.TINT))), - }) -} - -func markUsed(n *ir.Name) *ir.Name { - n.SetUsed(true) - return n -} - -func markNeedZero(n *ir.Name) *ir.Name { - n.SetNeedzero(true) - return n -} - -// Test all code paths for cmpstackvarlt. -func TestCmpstackvar(t *testing.T) { - nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name { - if s == nil { - s = &types.Sym{Name: "."} - } - n := typecheck.NewName(s) - n.SetType(t) - n.SetFrameOffset(xoffset) - n.Class = cl - return n - } - testdata := []struct { - a, b *ir.Name - lt bool - }{ - { - nod(0, nil, nil, ir.PAUTO), - nod(0, nil, nil, ir.PFUNC), - false, - }, - { - nod(0, nil, nil, ir.PFUNC), - nod(0, nil, nil, ir.PAUTO), - true, - }, - { - nod(0, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - true, - }, - { - nod(20, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - false, - }, - { - nod(10, nil, nil, ir.PFUNC), - nod(10, nil, nil, ir.PFUNC), - false, - }, - { - nod(10, nil, nil, ir.PPARAM), - nod(20, nil, nil, ir.PPARAMOUT), - true, - }, - { - nod(10, nil, nil, ir.PPARAMOUT), - nod(20, nil, nil, ir.PPARAM), - true, - }, - { - markUsed(nod(0, nil, nil, ir.PAUTO)), - nod(0, nil, nil, ir.PAUTO), - true, - }, - { - nod(0, nil, nil, ir.PAUTO), - markUsed(nod(0, nil, nil, ir.PAUTO)), - false, - }, - { - nod(0, typeWithoutPointers(), nil, ir.PAUTO), - nod(0, typeWithPointers(), nil, ir.PAUTO), - false, - }, - { - nod(0, typeWithPointers(), nil, ir.PAUTO), - nod(0, typeWithoutPointers(), nil, ir.PAUTO), - true, - }, - { - markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)), - nod(0, &types.Type{}, nil, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, nil, ir.PAUTO), - markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)), - false, - }, - { - nod(0, &types.Type{Width: 1}, nil, ir.PAUTO), - nod(0, &types.Type{Width: 2}, nil, ir.PAUTO), - false, - }, - { - nod(0, &types.Type{Width: 2}, nil, ir.PAUTO), - nod(0, &types.Type{Width: 1}, nil, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - true, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - false, - }, - { - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - false, - }, - } - for _, d := range testdata { - got := cmpstackvarlt(d.a, d.b) - if got != d.lt { - t.Errorf("want %v < %v", d.a, d.b) - } - // If we expect a < b to be true, check that b < a is false. - if d.lt && cmpstackvarlt(d.b, d.a) { - t.Errorf("unexpected %v < %v", d.b, d.a) - } - } -} - -func TestStackvarSort(t *testing.T) { - nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name { - n := typecheck.NewName(s) - n.SetType(t) - n.SetFrameOffset(xoffset) - n.Class = cl - return n - } - inp := []*ir.Name{ - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC), - markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - } - want := []*ir.Name{ - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC), - nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC), - markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)), - nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO), - nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO), - nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO), - } - sort.Sort(byStackVar(inp)) - if !reflect.DeepEqual(want, inp) { - t.Error("sort failed") - for i := range inp { - g := inp[i] - w := want[i] - eq := reflect.DeepEqual(w, g) - if !eq { - t.Log(i, w, g) - } - } - } -} diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index dfa76006de7901..21b8fbc8ec36e8 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -87,8 +87,7 @@ func InitConfig() { _ = types.NewPtr(types.Types[types.TINT64]) // *int64 _ = types.NewPtr(types.ErrorType) // *error types.NewPtrCacheEnabled = false - ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0) - ssaConfig.SoftFloat = Arch.SoftFloat + ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat) ssaConfig.Race = base.Flag.Race ssaCaches = make([]ssa.Cache, base.Flag.LowerC) @@ -97,6 +96,7 @@ func InitConfig() { ir.Syms.AssertE2I2 = typecheck.LookupRuntimeFunc("assertE2I2") ir.Syms.AssertI2I = typecheck.LookupRuntimeFunc("assertI2I") ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2") + ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment") ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc") ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack") ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn") @@ -105,9 +105,12 @@ func InitConfig() { ir.Syms.GCWriteBarrier = typecheck.LookupRuntimeFunc("gcWriteBarrier") ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded") ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice") + ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove") ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread") ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite") ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove") + ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread") + ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite") ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject") ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc") ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide") @@ -222,11 +225,6 @@ func AbiForBodylessFuncStackMap(fn *ir.Func) *abi.ABIConfig { return ssaConfig.ABI0.Copy() // No idea what races will result, be safe } -// These are disabled but remain ready for use in case they are needed for the next regabi port. -// TODO if they are not needed for 1.18 / next register abi port, delete them. -const magicNameDotSuffix = ".*disabled*MagicMethodNameForTestingRegisterABI" -const magicLastTypeName = "*disabled*MagicLastTypeNameForTestingRegisterABI" - // abiForFunc implements ABI policy for a function, but does not return a copy of the ABI. // Passing a nil function returns the default ABI based on experiment configuration. func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig { @@ -249,48 +247,13 @@ func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig { a := abi0 if fn != nil { - name := ir.FuncName(fn) - magicName := strings.HasSuffix(name, magicNameDotSuffix) if fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working - if strings.Contains(name, ".") { - if !magicName { - base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name) - } - } - a = abi1 - } else if magicName { - if base.FmtPos(fn.Pos()) == ":1" { - // no way to put a pragma here, and it will error out in the real source code if they did not do it there. - a = abi1 - } else { - base.ErrorfAt(fn.Pos(), "Methods with magic name %s (method %s) must also specify //go:registerparams", magicNameDotSuffix[1:], name) - } - } - if regAbiForFuncType(fn.Type().FuncType()) { - // fmt.Printf("Saw magic last type name for function %s\n", name) a = abi1 } } return a } -func regAbiForFuncType(ft *types.Func) bool { - np := ft.Params.NumFields() - return np > 0 && strings.Contains(ft.Params.FieldType(np-1).String(), magicLastTypeName) -} - -// getParam returns the Field of ith param of node n (which is a -// function/method/interface call), where the receiver of a method call is -// considered as the 0th parameter. This does not include the receiver of an -// interface call. -func getParam(n *ir.CallExpr, i int) *types.Field { - t := n.X.Type() - if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } - return t.Params().Field(i) -} - // dvarint writes a varint v to the funcdata in symbol x and returns the new offset func dvarint(x *obj.LSym, off int, v int64) int { if v < 0 || v > 1e9 { @@ -324,66 +287,22 @@ func dvarint(x *obj.LSym, off int, v int64) int { // for stack variables are specified as the number of bytes below varp (pointer to the // top of the local variables) for their starting address. The format is: // -// - Max total argument size among all the defers -// - Offset of the deferBits variable -// - Number of defers in the function -// - Information about each defer call, in reverse order of appearance in the function: -// - Total argument size of the call -// - Offset of the closure value to call -// - Number of arguments (including interface receiver or method receiver as first arg) -// - Information about each argument -// - Offset of the stored defer argument in this function's frame -// - Size of the argument -// - Offset of where argument should be placed in the args frame when making call +// - Offset of the deferBits variable +// - Number of defers in the function +// - Information about each defer call, in reverse order of appearance in the function: +// - Offset of the closure value to call func (s *state) emitOpenDeferInfo() { x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer") + x.Set(obj.AttrContentAddressable, true) s.curfn.LSym.Func().OpenCodedDeferInfo = x off := 0 - - // Compute maxargsize (max size of arguments for all defers) - // first, so we can output it first to the funcdata - var maxargsize int64 - for i := len(s.openDefers) - 1; i >= 0; i-- { - r := s.openDefers[i] - argsize := r.n.X.Type().ArgWidth() // TODO register args: but maybe use of abi0 will make this easy - if argsize > maxargsize { - maxargsize = argsize - } - } - off = dvarint(x, off, maxargsize) off = dvarint(x, off, -s.deferBitsTemp.FrameOffset()) off = dvarint(x, off, int64(len(s.openDefers))) // Write in reverse-order, for ease of running in that order at runtime for i := len(s.openDefers) - 1; i >= 0; i-- { r := s.openDefers[i] - off = dvarint(x, off, r.n.X.Type().ArgWidth()) off = dvarint(x, off, -r.closureNode.FrameOffset()) - numArgs := len(r.argNodes) - if r.rcvrNode != nil { - // If there's an interface receiver, treat/place it as the first - // arg. (If there is a method receiver, it's already included as - // first arg in r.argNodes.) - numArgs++ - } - off = dvarint(x, off, int64(numArgs)) - argAdjust := 0 // presence of receiver offsets the parameter count. - if r.rcvrNode != nil { - off = dvarint(x, off, -okOffset(r.rcvrNode.FrameOffset())) - off = dvarint(x, off, s.config.PtrSize) - off = dvarint(x, off, 0) // This is okay because defer records use ABI0 (for now) - argAdjust++ - } - - // TODO(register args) assume abi0 for this? - ab := s.f.ABI0 - pri := ab.ABIAnalyzeFuncType(r.n.X.Type().FuncType()) - for j, arg := range r.argNodes { - f := getParam(r.n, j) - off = dvarint(x, off, -okOffset(arg.FrameOffset())) - off = dvarint(x, off, f.Type.Size()) - off = dvarint(x, off, okOffset(pri.InParam(j+argAdjust).FrameOffset(pri))) - } } } @@ -424,6 +343,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { if fn.Pragma&ir.CgoUnsafeArgs != 0 { s.cgoUnsafeArgs = true } + s.checkPtrEnabled = ir.ShouldCheckPtr(fn, 1) fe := ssafn{ curfn: fn, @@ -438,7 +358,6 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { s.f.Cache = &ssaCaches[worker] s.f.Cache.Reset() s.f.Name = name - s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH") s.f.PrintOrHtmlSSA = printssa if fn.Pragma&ir.Nosplit != 0 { s.f.NoSplit = true @@ -537,6 +456,19 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { var params *abi.ABIParamResultInfo params = s.f.ABISelf.ABIAnalyze(fn.Type(), true) + // The backend's stackframe pass prunes away entries from the fn's + // Dcl list, including PARAMOUT nodes that correspond to output + // params passed in registers. Walk the Dcl list and capture these + // nodes to a side list, so that we'll have them available during + // DWARF-gen later on. See issue 48573 for more details. + var debugInfo ssa.FuncDebug + for _, n := range fn.Dcl { + if n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters() { + debugInfo.RegOutputParams = append(debugInfo.RegOutputParams, n) + } + } + fn.DebugInfo = &debugInfo + // Generate addresses of local declarations s.decladdrs = map[*ir.Name]*ssa.Value{} for _, n := range fn.Dcl { @@ -580,7 +512,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { } // Populate closure variables. - if !fn.ClosureCalled() { + if fn.Needctxt() { clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr) offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field for _, n := range fn.ClosureVars { @@ -589,7 +521,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { typ = types.NewPtr(typ) } - offset = types.Rnd(offset, typ.Alignment()) + offset = types.RoundUp(offset, typ.Alignment()) ptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo) offset += typ.Size() @@ -650,7 +582,6 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { // it mimics the behavior of the former ABI (everything stored) and because it's not 100% // clear if naming conventions are respected in autogenerated code. // TODO figure out exactly what's unused, don't spill it. Make liveness fine-grained, also. - // TODO non-amd64 architectures have link registers etc that may require adjustment here. for _, p := range params.InParams() { typs, offs := p.RegisterTypesAndOffsets() for i, t := range typs { @@ -700,7 +631,9 @@ func (s *state) zeroResults() { if typ := n.Type(); TypeOK(typ) { s.assign(n, s.zeroVal(typ), false, 0) } else { - s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem()) + if typ.HasPointers() { + s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem()) + } s.zero(n.Type(), s.decladdrs[n]) } } @@ -733,7 +666,7 @@ func (s *state) paramsToHeap() { // newHeapaddr allocates heap memory for n and sets its heap address. func (s *state) newHeapaddr(n *ir.Name) { - s.setHeapaddr(n.Pos(), n, s.newObject(n.Type())) + s.setHeapaddr(n.Pos(), n, s.newObject(n.Type(), nil)) } // setHeapaddr allocates a new PAUTO variable to store ptr (which must be non-nil) @@ -761,16 +694,52 @@ func (s *state) setHeapaddr(pos src.XPos, n *ir.Name, ptr *ssa.Value) { } // newObject returns an SSA value denoting new(typ). -func (s *state) newObject(typ *types.Type) *ssa.Value { +func (s *state) newObject(typ *types.Type, rtype *ssa.Value) *ssa.Value { if typ.Size() == 0 { return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb) } - return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0] + if rtype == nil { + rtype = s.reflectType(typ) + } + return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, rtype)[0] +} + +func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) { + if !n.Type().IsPtr() { + s.Fatalf("expected pointer type: %v", n.Type()) + } + elem, rtypeExpr := n.Type().Elem(), n.ElemRType + if count != nil { + if !elem.IsArray() { + s.Fatalf("expected array type: %v", elem) + } + elem, rtypeExpr = elem.Elem(), n.ElemElemRType + } + size := elem.Size() + // Casting from larger type to smaller one is ok, so for smallest type, do nothing. + if elem.Alignment() == 1 && (size == 0 || size == 1 || count == nil) { + return + } + if count == nil { + count = s.constInt(types.Types[types.TUINTPTR], 1) + } + if count.Type.Size() != s.config.PtrSize { + s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize) + } + var rtype *ssa.Value + if rtypeExpr != nil { + rtype = s.expr(rtypeExpr) + } else { + rtype = s.reflectType(elem) + } + s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, rtype, count) } // reflectType returns an SSA value representing a pointer to typ's // reflection type descriptor. func (s *state) reflectType(typ *types.Type) *ssa.Value { + // TODO(mdempsky): Make this Fatalf under Unified IR; frontend needs + // to supply RType expressions. lsym := reflectdata.TypeLinksym(typ) return s.entryNewValue1A(ssa.OpAddr, types.NewPtr(types.Types[types.TUINT8]), lsym, s.sb) } @@ -865,16 +834,6 @@ type openDeferInfo struct { // function, method, or interface call, to store a closure that panic // processing can use for this defer. closureNode *ir.Name - // If defer call is interface call, the address of the argtmp where the - // receiver is stored - rcvr *ssa.Value - // The node representing the argtmp where the receiver is stored - rcvrNode *ir.Name - // The addresses of the argtmps where the evaluated arguments of the defer - // function call are stored. - argVals []*ssa.Value - // The nodes representing the argtmps where the args of the defer are stored - argNodes []*ir.Name } type state struct { @@ -930,10 +889,11 @@ type state struct { // Used to deduplicate panic calls. panics map[funcLine]*ssa.Block - cgoUnsafeArgs bool - hasdefer bool // whether the function contains a defer statement - softFloat bool - hasOpenDefers bool // whether we are doing open-coded defers + cgoUnsafeArgs bool + hasdefer bool // whether the function contains a defer statement + softFloat bool + hasOpenDefers bool // whether we are doing open-coded defers + checkPtrEnabled bool // whether to insert checkptr instrumentation // If doing open-coded defers, list of info about the defer calls in // scanning order. Hence, at exit we should run these defers in reverse @@ -991,7 +951,6 @@ var ( // marker nodes for temporary variables ptrVar = ssaMarker("ptr") lenVar = ssaMarker("len") - newlenVar = ssaMarker("newlen") capVar = ssaMarker("cap") typVar = ssaMarker("typ") okVar = ssaMarker("ok") @@ -1285,10 +1244,10 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) } // instrumentFields instruments a read/write operation on addr. -// If it is instrumenting for MSAN and t is a struct type, it instruments +// If it is instrumenting for MSAN or ASAN and t is a struct type, it instruments // operation for each field, instead of for the whole struct. func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) { - if !base.Flag.MSan || !t.IsStruct() { + if !(base.Flag.MSan || base.Flag.ASan) || !t.IsStruct() { s.instrument(t, addr, kind) return } @@ -1367,6 +1326,16 @@ func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrume default: panic("unreachable") } + } else if base.Flag.ASan { + switch kind { + case instrumentRead: + fn = ir.Syms.Asanread + case instrumentWrite: + fn = ir.Syms.Asanwrite + default: + panic("unreachable") + } + needWidth = true } else { panic("unreachable") } @@ -1402,7 +1371,47 @@ func (s *state) zero(t *types.Type, dst *ssa.Value) { } func (s *state) move(t *types.Type, dst, src *ssa.Value) { + s.moveWhichMayOverlap(t, dst, src, false) +} +func (s *state) moveWhichMayOverlap(t *types.Type, dst, src *ssa.Value, mayOverlap bool) { s.instrumentMove(t, dst, src) + if mayOverlap && t.IsArray() && t.NumElem() > 1 && !ssa.IsInlinableMemmove(dst, src, t.Size(), s.f.Config) { + // Normally, when moving Go values of type T from one location to another, + // we don't need to worry about partial overlaps. The two Ts must either be + // in disjoint (nonoverlapping) memory or in exactly the same location. + // There are 2 cases where this isn't true: + // 1) Using unsafe you can arrange partial overlaps. + // 2) Since Go 1.17, you can use a cast from a slice to a ptr-to-array. + // https://go.dev/ref/spec#Conversions_from_slice_to_array_pointer + // This feature can be used to construct partial overlaps of array types. + // var a [3]int + // p := (*[2]int)(a[:]) + // q := (*[2]int)(a[1:]) + // *p = *q + // We don't care about solving 1. Or at least, we haven't historically + // and no one has complained. + // For 2, we need to ensure that if there might be partial overlap, + // then we can't use OpMove; we must use memmove instead. + // (memmove handles partial overlap by copying in the correct + // direction. OpMove does not.) + // + // Note that we have to be careful here not to introduce a call when + // we're marshaling arguments to a call or unmarshaling results from a call. + // Cases where this is happening must pass mayOverlap to false. + // (Currently this only happens when unmarshaling results of a call.) + if t.HasPointers() { + s.rtcall(ir.Syms.Typedmemmove, true, nil, s.reflectType(t), dst, src) + // We would have otherwise implemented this move with straightline code, + // including a write barrier. Pretend we issue a write barrier here, + // so that the write barrier tests work. (Otherwise they'd need to know + // the details of IsInlineableMemmove.) + s.curfn.SetWBPos(s.peekPos()) + } else { + s.rtcall(ir.Syms.Memmove, true, nil, dst, src, s.constInt(types.Types[types.TUINTPTR], t.Size())) + } + ssa.LogLargeCopy(s.f.Name, s.peekPos(), t.Size()) + return + } store := s.newValue3I(ssa.OpMove, types.TypeMem, t.Size(), dst, src, s.mem()) store.Aux = t s.vars[memVar] = store @@ -1417,11 +1426,8 @@ func (s *state) stmtList(l ir.Nodes) { // stmt converts the statement n to SSA and adds it to s. func (s *state) stmt(n ir.Node) { - if !(n.Op() == ir.OVARKILL || n.Op() == ir.OVARLIVE || n.Op() == ir.OVARDEF) { - // OVARKILL, OVARLIVE, and OVARDEF are invisible to the programmer, so we don't use their line numbers to avoid confusion in debugging. - s.pushLine(n.Pos()) - defer s.popLine() - } + s.pushLine(n.Pos()) + defer s.popLine() // If s.curBlock is nil, and n isn't a label (which might have an associated goto somewhere), // then this code is dead. Stop here. @@ -1453,7 +1459,7 @@ func (s *state) stmt(n ir.Node) { s.callResult(n, callNormal) if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PFUNC { if fn := n.X.Sym().Name; base.Flag.CompilingRuntime && fn == "throw" || - n.X.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap") { + n.X.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap" || fn == "panicunsafeslicelen" || fn == "panicunsafeslicenilptr" || fn == "panicunsafestringlen" || fn == "panicunsafestringnilptr") { m := s.mem() b := s.endBlock() b.Kind = ssa.BlockExit @@ -1491,16 +1497,18 @@ func (s *state) stmt(n ir.Node) { case ir.OAS2DOTTYPE: n := n.(*ir.AssignListStmt) - res, resok := s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true) + var res, resok *ssa.Value + if n.Rhs[0].Op() == ir.ODOTTYPE2 { + res, resok = s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true) + } else { + res, resok = s.dynamicDottype(n.Rhs[0].(*ir.DynamicTypeAssertExpr), true) + } deref := false if !TypeOK(n.Rhs[0].Type()) { if res.Op != ssa.OpLoad { s.Fatalf("dottype of non-load") } mem := s.mem() - if mem.Op == ssa.OpVarKill { - mem = mem.Args[0] - } if res.Args[1] != mem { s.Fatalf("memory no longer live from 2-result dottype load") } @@ -1534,6 +1542,10 @@ func (s *state) stmt(n ir.Node) { case ir.OLABEL: n := n.(*ir.LabelStmt) sym := n.Label + if sym.IsBlank() { + // Nothing to do because the label isn't targetable. See issue 52278. + break + } lab := s.label(sym) // The label might already have a target block via a goto. @@ -1575,6 +1587,36 @@ func (s *state) stmt(n ir.Node) { return } + // mayOverlap keeps track of whether the LHS and RHS might + // refer to overlapping memory. + mayOverlap := true + if n.Y == nil { + // Not a move at all, mayOverlap is not relevant. + } else if n.Def { + // A variable being defined cannot overlap anything else. + mayOverlap = false + } else if n.X.Op() == ir.ONAME && n.Y.Op() == ir.ONAME { + // Two named things never overlap. + // (Or they are identical, which we treat as nonoverlapping.) + mayOverlap = false + } else if n.Y.Op() == ir.ODEREF { + p := n.Y.(*ir.StarExpr).X + for p.Op() == ir.OCONVNOP { + p = p.(*ir.ConvExpr).X + } + if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() { + // Pointer fields of strings point to unmodifiable memory. + // That memory can't overlap with the memory being written. + mayOverlap = false + } + } else if n.Y.Op() == ir.ORESULT || n.Y.Op() == ir.OCALLFUNC || n.Y.Op() == ir.OCALLINTER { + // When copying values out of the return area of a call, we know + // the source and destination don't overlap. Importantly, we must + // set mayOverlap so we don't introduce a call to memmove while + // we still have live data in the argument area. + mayOverlap = false + } + // Evaluate RHS. rhs := n.Y if rhs != nil { @@ -1675,7 +1717,7 @@ func (s *state) stmt(n ir.Node) { } } - s.assign(n.X, r, deref, skip) + s.assignWhichMayOverlap(n.X, r, deref, skip, mayOverlap) case ir.OIF: n := n.(*ir.IfStmt) @@ -1732,9 +1774,11 @@ func (s *state) stmt(n ir.Node) { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) - b := s.exit() - b.Kind = ssa.BlockRetJmp // override BlockRet - b.Aux = callTargetLSym(n.Target) + s.callResult(n.Call, callTail) + call := s.mem() + b := s.endBlock() + b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity. + b.SetControl(call) case ir.OCONTINUE, ir.OBREAK: n := n.(*ir.BranchStmt) @@ -1763,12 +1807,9 @@ func (s *state) stmt(n ir.Node) { b.Pos = s.lastPos.WithIsStmt() // Do this even if b is an empty block. b.AddEdgeTo(to) - case ir.OFOR, ir.OFORUNTIL: + case ir.OFOR: // OFOR: for Ninit; Left; Right { Nbody } // cond (Left); body (Nbody); incr (Right) - // - // OFORUNTIL: for Ninit; Left; Right; List { Nbody } - // => body: { Nbody }; incr: Right; if Left { lateincr: List; goto body }; end: n := n.(*ir.ForStmt) bCond := s.f.NewBlock(ssa.BlockPlain) bBody := s.f.NewBlock(ssa.BlockPlain) @@ -1778,21 +1819,17 @@ func (s *state) stmt(n ir.Node) { // ensure empty for loops have correct position; issue #30167 bBody.Pos = n.Pos() - // first, jump to condition test (OFOR) or body (OFORUNTIL) + // first, jump to condition test b := s.endBlock() - if n.Op() == ir.OFOR { - b.AddEdgeTo(bCond) - // generate code to test condition - s.startBlock(bCond) - if n.Cond != nil { - s.condBranch(n.Cond, bBody, bEnd, 1) - } else { - b := s.endBlock() - b.Kind = ssa.BlockPlain - b.AddEdgeTo(bBody) - } + b.AddEdgeTo(bCond) + // generate code to test condition + s.startBlock(bCond) + if n.Cond != nil { + s.condBranch(n.Cond, bBody, bEnd, 1) } else { + b := s.endBlock() + b.Kind = ssa.BlockPlain b.AddEdgeTo(bBody) } @@ -1826,29 +1863,18 @@ func (s *state) stmt(n ir.Node) { b.AddEdgeTo(bIncr) } - // generate incr (and, for OFORUNTIL, condition) + // generate incr s.startBlock(bIncr) if n.Post != nil { s.stmt(n.Post) } - if n.Op() == ir.OFOR { - if b := s.endBlock(); b != nil { - b.AddEdgeTo(bCond) - // It can happen that bIncr ends in a block containing only VARKILL, - // and that muddles the debugging experience. - if b.Pos == src.NoXPos { - b.Pos = bCond.Pos - } + if b := s.endBlock(); b != nil { + b.AddEdgeTo(bCond) + // It can happen that bIncr ends in a block containing only VARKILL, + // and that muddles the debugging experience. + if b.Pos == src.NoXPos { + b.Pos = bCond.Pos } - } else { - // bCond is unused in OFORUNTIL, so repurpose it. - bLateIncr := bCond - // test condition - s.condBranch(n.Cond, bLateIncr, bEnd, 1) - // generate late increment - s.startBlock(bLateIncr) - s.stmtList(n.Late) - s.endBlock().AddEdgeTo(bBody) } s.startBlock(bEnd) @@ -1897,34 +1923,83 @@ func (s *state) stmt(n ir.Node) { } s.startBlock(bEnd) - case ir.OVARDEF: - n := n.(*ir.UnaryExpr) - if !s.canSSA(n.X) { - s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, n.X.(*ir.Name), s.mem(), false) - } - case ir.OVARKILL: - // Insert a varkill op to record that a variable is no longer live. - // We only care about liveness info at call sites, so putting the - // varkill in the store chain is enough to keep it correctly ordered - // with respect to call ops. - n := n.(*ir.UnaryExpr) - if !s.canSSA(n.X) { - s.vars[memVar] = s.newValue1Apos(ssa.OpVarKill, types.TypeMem, n.X.(*ir.Name), s.mem(), false) - } + case ir.OJUMPTABLE: + n := n.(*ir.JumpTableStmt) - case ir.OVARLIVE: - // Insert a varlive op to record that a variable is still live. - n := n.(*ir.UnaryExpr) - v := n.X.(*ir.Name) - if !v.Addrtaken() { - s.Fatalf("VARLIVE variable %v must have Addrtaken set", v) + // Make blocks we'll need. + jt := s.f.NewBlock(ssa.BlockJumpTable) + bEnd := s.f.NewBlock(ssa.BlockPlain) + + // The only thing that needs evaluating is the index we're looking up. + idx := s.expr(n.Idx) + unsigned := idx.Type.IsUnsigned() + + // Extend so we can do everything in uintptr arithmetic. + t := types.Types[types.TUINTPTR] + idx = s.conv(nil, idx, idx.Type, t) + + // The ending condition for the current block decides whether we'll use + // the jump table at all. + // We check that min <= idx <= max and jump around the jump table + // if that test fails. + // We implement min <= idx <= max with 0 <= idx-min <= max-min, because + // we'll need idx-min anyway as the control value for the jump table. + var min, max uint64 + if unsigned { + min, _ = constant.Uint64Val(n.Cases[0]) + max, _ = constant.Uint64Val(n.Cases[len(n.Cases)-1]) + } else { + mn, _ := constant.Int64Val(n.Cases[0]) + mx, _ := constant.Int64Val(n.Cases[len(n.Cases)-1]) + min = uint64(mn) + max = uint64(mx) + } + // Compare idx-min with max-min, to see if we can use the jump table. + idx = s.newValue2(s.ssaOp(ir.OSUB, t), t, idx, s.uintptrConstant(min)) + width := s.uintptrConstant(max - min) + cmp := s.newValue2(s.ssaOp(ir.OLE, t), types.Types[types.TBOOL], idx, width) + b := s.endBlock() + b.Kind = ssa.BlockIf + b.SetControl(cmp) + b.AddEdgeTo(jt) // in range - use jump table + b.AddEdgeTo(bEnd) // out of range - no case in the jump table will trigger + b.Likely = ssa.BranchLikely // TODO: assumes missing the table entirely is unlikely. True? + + // Build jump table block. + s.startBlock(jt) + jt.Pos = n.Pos() + if base.Flag.Cfg.SpectreIndex { + idx = s.newValue2(ssa.OpSpectreSliceIndex, t, idx, width) + } + jt.SetControl(idx) + + // Figure out where we should go for each index in the table. + table := make([]*ssa.Block, max-min+1) + for i := range table { + table[i] = bEnd // default target + } + for i := range n.Targets { + c := n.Cases[i] + lab := s.label(n.Targets[i]) + if lab.target == nil { + lab.target = s.f.NewBlock(ssa.BlockPlain) + } + var val uint64 + if unsigned { + val, _ = constant.Uint64Val(c) + } else { + vl, _ := constant.Int64Val(c) + val = uint64(vl) + } + // Overwrite the default target. + table[val-min] = lab.target } - switch v.Class { - case ir.PAUTO, ir.PPARAM, ir.PPARAMOUT: - default: - s.Fatalf("VARLIVE variable %v must be Auto or Arg", v) + for _, t := range table { + jt.AddEdgeTo(t) } - s.vars[memVar] = s.newValue1A(ssa.OpVarLive, types.TypeMem, v, s.mem()) + s.endBlock() + + s.startBlock(bEnd) case ir.OCHECKNIL: n := n.(*ir.UnaryExpr) @@ -1975,14 +2050,16 @@ func (s *state) exit() *ssa.Block { for i, f := range resultFields { n := f.Nname.(*ir.Name) if s.canSSA(n) { // result is in some SSA variable - if !n.IsOutputParamInRegisters() { + if !n.IsOutputParamInRegisters() && n.Type().HasPointers() { // We are about to store to the result slot. s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem()) } results[i] = s.variable(n, n.Type()) } else if !n.OnStack() { // result is actually heap allocated // We are about to copy the in-heap result to the result slot. - s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem()) + if n.Type().HasPointers() { + s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, n, s.mem()) + } ha := s.expr(n.Heapaddr) s.instrumentFields(n.Type(), ha, instrumentRead) results[i] = s.newValue2(ssa.OpDereference, n.Type(), ha, s.mem()) @@ -2017,175 +2094,175 @@ type opAndType struct { } var opToSSA = map[opAndType]ssa.Op{ - opAndType{ir.OADD, types.TINT8}: ssa.OpAdd8, - opAndType{ir.OADD, types.TUINT8}: ssa.OpAdd8, - opAndType{ir.OADD, types.TINT16}: ssa.OpAdd16, - opAndType{ir.OADD, types.TUINT16}: ssa.OpAdd16, - opAndType{ir.OADD, types.TINT32}: ssa.OpAdd32, - opAndType{ir.OADD, types.TUINT32}: ssa.OpAdd32, - opAndType{ir.OADD, types.TINT64}: ssa.OpAdd64, - opAndType{ir.OADD, types.TUINT64}: ssa.OpAdd64, - opAndType{ir.OADD, types.TFLOAT32}: ssa.OpAdd32F, - opAndType{ir.OADD, types.TFLOAT64}: ssa.OpAdd64F, - - opAndType{ir.OSUB, types.TINT8}: ssa.OpSub8, - opAndType{ir.OSUB, types.TUINT8}: ssa.OpSub8, - opAndType{ir.OSUB, types.TINT16}: ssa.OpSub16, - opAndType{ir.OSUB, types.TUINT16}: ssa.OpSub16, - opAndType{ir.OSUB, types.TINT32}: ssa.OpSub32, - opAndType{ir.OSUB, types.TUINT32}: ssa.OpSub32, - opAndType{ir.OSUB, types.TINT64}: ssa.OpSub64, - opAndType{ir.OSUB, types.TUINT64}: ssa.OpSub64, - opAndType{ir.OSUB, types.TFLOAT32}: ssa.OpSub32F, - opAndType{ir.OSUB, types.TFLOAT64}: ssa.OpSub64F, - - opAndType{ir.ONOT, types.TBOOL}: ssa.OpNot, - - opAndType{ir.ONEG, types.TINT8}: ssa.OpNeg8, - opAndType{ir.ONEG, types.TUINT8}: ssa.OpNeg8, - opAndType{ir.ONEG, types.TINT16}: ssa.OpNeg16, - opAndType{ir.ONEG, types.TUINT16}: ssa.OpNeg16, - opAndType{ir.ONEG, types.TINT32}: ssa.OpNeg32, - opAndType{ir.ONEG, types.TUINT32}: ssa.OpNeg32, - opAndType{ir.ONEG, types.TINT64}: ssa.OpNeg64, - opAndType{ir.ONEG, types.TUINT64}: ssa.OpNeg64, - opAndType{ir.ONEG, types.TFLOAT32}: ssa.OpNeg32F, - opAndType{ir.ONEG, types.TFLOAT64}: ssa.OpNeg64F, - - opAndType{ir.OBITNOT, types.TINT8}: ssa.OpCom8, - opAndType{ir.OBITNOT, types.TUINT8}: ssa.OpCom8, - opAndType{ir.OBITNOT, types.TINT16}: ssa.OpCom16, - opAndType{ir.OBITNOT, types.TUINT16}: ssa.OpCom16, - opAndType{ir.OBITNOT, types.TINT32}: ssa.OpCom32, - opAndType{ir.OBITNOT, types.TUINT32}: ssa.OpCom32, - opAndType{ir.OBITNOT, types.TINT64}: ssa.OpCom64, - opAndType{ir.OBITNOT, types.TUINT64}: ssa.OpCom64, - - opAndType{ir.OIMAG, types.TCOMPLEX64}: ssa.OpComplexImag, - opAndType{ir.OIMAG, types.TCOMPLEX128}: ssa.OpComplexImag, - opAndType{ir.OREAL, types.TCOMPLEX64}: ssa.OpComplexReal, - opAndType{ir.OREAL, types.TCOMPLEX128}: ssa.OpComplexReal, - - opAndType{ir.OMUL, types.TINT8}: ssa.OpMul8, - opAndType{ir.OMUL, types.TUINT8}: ssa.OpMul8, - opAndType{ir.OMUL, types.TINT16}: ssa.OpMul16, - opAndType{ir.OMUL, types.TUINT16}: ssa.OpMul16, - opAndType{ir.OMUL, types.TINT32}: ssa.OpMul32, - opAndType{ir.OMUL, types.TUINT32}: ssa.OpMul32, - opAndType{ir.OMUL, types.TINT64}: ssa.OpMul64, - opAndType{ir.OMUL, types.TUINT64}: ssa.OpMul64, - opAndType{ir.OMUL, types.TFLOAT32}: ssa.OpMul32F, - opAndType{ir.OMUL, types.TFLOAT64}: ssa.OpMul64F, - - opAndType{ir.ODIV, types.TFLOAT32}: ssa.OpDiv32F, - opAndType{ir.ODIV, types.TFLOAT64}: ssa.OpDiv64F, - - opAndType{ir.ODIV, types.TINT8}: ssa.OpDiv8, - opAndType{ir.ODIV, types.TUINT8}: ssa.OpDiv8u, - opAndType{ir.ODIV, types.TINT16}: ssa.OpDiv16, - opAndType{ir.ODIV, types.TUINT16}: ssa.OpDiv16u, - opAndType{ir.ODIV, types.TINT32}: ssa.OpDiv32, - opAndType{ir.ODIV, types.TUINT32}: ssa.OpDiv32u, - opAndType{ir.ODIV, types.TINT64}: ssa.OpDiv64, - opAndType{ir.ODIV, types.TUINT64}: ssa.OpDiv64u, - - opAndType{ir.OMOD, types.TINT8}: ssa.OpMod8, - opAndType{ir.OMOD, types.TUINT8}: ssa.OpMod8u, - opAndType{ir.OMOD, types.TINT16}: ssa.OpMod16, - opAndType{ir.OMOD, types.TUINT16}: ssa.OpMod16u, - opAndType{ir.OMOD, types.TINT32}: ssa.OpMod32, - opAndType{ir.OMOD, types.TUINT32}: ssa.OpMod32u, - opAndType{ir.OMOD, types.TINT64}: ssa.OpMod64, - opAndType{ir.OMOD, types.TUINT64}: ssa.OpMod64u, - - opAndType{ir.OAND, types.TINT8}: ssa.OpAnd8, - opAndType{ir.OAND, types.TUINT8}: ssa.OpAnd8, - opAndType{ir.OAND, types.TINT16}: ssa.OpAnd16, - opAndType{ir.OAND, types.TUINT16}: ssa.OpAnd16, - opAndType{ir.OAND, types.TINT32}: ssa.OpAnd32, - opAndType{ir.OAND, types.TUINT32}: ssa.OpAnd32, - opAndType{ir.OAND, types.TINT64}: ssa.OpAnd64, - opAndType{ir.OAND, types.TUINT64}: ssa.OpAnd64, - - opAndType{ir.OOR, types.TINT8}: ssa.OpOr8, - opAndType{ir.OOR, types.TUINT8}: ssa.OpOr8, - opAndType{ir.OOR, types.TINT16}: ssa.OpOr16, - opAndType{ir.OOR, types.TUINT16}: ssa.OpOr16, - opAndType{ir.OOR, types.TINT32}: ssa.OpOr32, - opAndType{ir.OOR, types.TUINT32}: ssa.OpOr32, - opAndType{ir.OOR, types.TINT64}: ssa.OpOr64, - opAndType{ir.OOR, types.TUINT64}: ssa.OpOr64, - - opAndType{ir.OXOR, types.TINT8}: ssa.OpXor8, - opAndType{ir.OXOR, types.TUINT8}: ssa.OpXor8, - opAndType{ir.OXOR, types.TINT16}: ssa.OpXor16, - opAndType{ir.OXOR, types.TUINT16}: ssa.OpXor16, - opAndType{ir.OXOR, types.TINT32}: ssa.OpXor32, - opAndType{ir.OXOR, types.TUINT32}: ssa.OpXor32, - opAndType{ir.OXOR, types.TINT64}: ssa.OpXor64, - opAndType{ir.OXOR, types.TUINT64}: ssa.OpXor64, - - opAndType{ir.OEQ, types.TBOOL}: ssa.OpEqB, - opAndType{ir.OEQ, types.TINT8}: ssa.OpEq8, - opAndType{ir.OEQ, types.TUINT8}: ssa.OpEq8, - opAndType{ir.OEQ, types.TINT16}: ssa.OpEq16, - opAndType{ir.OEQ, types.TUINT16}: ssa.OpEq16, - opAndType{ir.OEQ, types.TINT32}: ssa.OpEq32, - opAndType{ir.OEQ, types.TUINT32}: ssa.OpEq32, - opAndType{ir.OEQ, types.TINT64}: ssa.OpEq64, - opAndType{ir.OEQ, types.TUINT64}: ssa.OpEq64, - opAndType{ir.OEQ, types.TINTER}: ssa.OpEqInter, - opAndType{ir.OEQ, types.TSLICE}: ssa.OpEqSlice, - opAndType{ir.OEQ, types.TFUNC}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TMAP}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TCHAN}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TPTR}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TUINTPTR}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TUNSAFEPTR}: ssa.OpEqPtr, - opAndType{ir.OEQ, types.TFLOAT64}: ssa.OpEq64F, - opAndType{ir.OEQ, types.TFLOAT32}: ssa.OpEq32F, - - opAndType{ir.ONE, types.TBOOL}: ssa.OpNeqB, - opAndType{ir.ONE, types.TINT8}: ssa.OpNeq8, - opAndType{ir.ONE, types.TUINT8}: ssa.OpNeq8, - opAndType{ir.ONE, types.TINT16}: ssa.OpNeq16, - opAndType{ir.ONE, types.TUINT16}: ssa.OpNeq16, - opAndType{ir.ONE, types.TINT32}: ssa.OpNeq32, - opAndType{ir.ONE, types.TUINT32}: ssa.OpNeq32, - opAndType{ir.ONE, types.TINT64}: ssa.OpNeq64, - opAndType{ir.ONE, types.TUINT64}: ssa.OpNeq64, - opAndType{ir.ONE, types.TINTER}: ssa.OpNeqInter, - opAndType{ir.ONE, types.TSLICE}: ssa.OpNeqSlice, - opAndType{ir.ONE, types.TFUNC}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TMAP}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TCHAN}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TPTR}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TUINTPTR}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TUNSAFEPTR}: ssa.OpNeqPtr, - opAndType{ir.ONE, types.TFLOAT64}: ssa.OpNeq64F, - opAndType{ir.ONE, types.TFLOAT32}: ssa.OpNeq32F, - - opAndType{ir.OLT, types.TINT8}: ssa.OpLess8, - opAndType{ir.OLT, types.TUINT8}: ssa.OpLess8U, - opAndType{ir.OLT, types.TINT16}: ssa.OpLess16, - opAndType{ir.OLT, types.TUINT16}: ssa.OpLess16U, - opAndType{ir.OLT, types.TINT32}: ssa.OpLess32, - opAndType{ir.OLT, types.TUINT32}: ssa.OpLess32U, - opAndType{ir.OLT, types.TINT64}: ssa.OpLess64, - opAndType{ir.OLT, types.TUINT64}: ssa.OpLess64U, - opAndType{ir.OLT, types.TFLOAT64}: ssa.OpLess64F, - opAndType{ir.OLT, types.TFLOAT32}: ssa.OpLess32F, - - opAndType{ir.OLE, types.TINT8}: ssa.OpLeq8, - opAndType{ir.OLE, types.TUINT8}: ssa.OpLeq8U, - opAndType{ir.OLE, types.TINT16}: ssa.OpLeq16, - opAndType{ir.OLE, types.TUINT16}: ssa.OpLeq16U, - opAndType{ir.OLE, types.TINT32}: ssa.OpLeq32, - opAndType{ir.OLE, types.TUINT32}: ssa.OpLeq32U, - opAndType{ir.OLE, types.TINT64}: ssa.OpLeq64, - opAndType{ir.OLE, types.TUINT64}: ssa.OpLeq64U, - opAndType{ir.OLE, types.TFLOAT64}: ssa.OpLeq64F, - opAndType{ir.OLE, types.TFLOAT32}: ssa.OpLeq32F, + {ir.OADD, types.TINT8}: ssa.OpAdd8, + {ir.OADD, types.TUINT8}: ssa.OpAdd8, + {ir.OADD, types.TINT16}: ssa.OpAdd16, + {ir.OADD, types.TUINT16}: ssa.OpAdd16, + {ir.OADD, types.TINT32}: ssa.OpAdd32, + {ir.OADD, types.TUINT32}: ssa.OpAdd32, + {ir.OADD, types.TINT64}: ssa.OpAdd64, + {ir.OADD, types.TUINT64}: ssa.OpAdd64, + {ir.OADD, types.TFLOAT32}: ssa.OpAdd32F, + {ir.OADD, types.TFLOAT64}: ssa.OpAdd64F, + + {ir.OSUB, types.TINT8}: ssa.OpSub8, + {ir.OSUB, types.TUINT8}: ssa.OpSub8, + {ir.OSUB, types.TINT16}: ssa.OpSub16, + {ir.OSUB, types.TUINT16}: ssa.OpSub16, + {ir.OSUB, types.TINT32}: ssa.OpSub32, + {ir.OSUB, types.TUINT32}: ssa.OpSub32, + {ir.OSUB, types.TINT64}: ssa.OpSub64, + {ir.OSUB, types.TUINT64}: ssa.OpSub64, + {ir.OSUB, types.TFLOAT32}: ssa.OpSub32F, + {ir.OSUB, types.TFLOAT64}: ssa.OpSub64F, + + {ir.ONOT, types.TBOOL}: ssa.OpNot, + + {ir.ONEG, types.TINT8}: ssa.OpNeg8, + {ir.ONEG, types.TUINT8}: ssa.OpNeg8, + {ir.ONEG, types.TINT16}: ssa.OpNeg16, + {ir.ONEG, types.TUINT16}: ssa.OpNeg16, + {ir.ONEG, types.TINT32}: ssa.OpNeg32, + {ir.ONEG, types.TUINT32}: ssa.OpNeg32, + {ir.ONEG, types.TINT64}: ssa.OpNeg64, + {ir.ONEG, types.TUINT64}: ssa.OpNeg64, + {ir.ONEG, types.TFLOAT32}: ssa.OpNeg32F, + {ir.ONEG, types.TFLOAT64}: ssa.OpNeg64F, + + {ir.OBITNOT, types.TINT8}: ssa.OpCom8, + {ir.OBITNOT, types.TUINT8}: ssa.OpCom8, + {ir.OBITNOT, types.TINT16}: ssa.OpCom16, + {ir.OBITNOT, types.TUINT16}: ssa.OpCom16, + {ir.OBITNOT, types.TINT32}: ssa.OpCom32, + {ir.OBITNOT, types.TUINT32}: ssa.OpCom32, + {ir.OBITNOT, types.TINT64}: ssa.OpCom64, + {ir.OBITNOT, types.TUINT64}: ssa.OpCom64, + + {ir.OIMAG, types.TCOMPLEX64}: ssa.OpComplexImag, + {ir.OIMAG, types.TCOMPLEX128}: ssa.OpComplexImag, + {ir.OREAL, types.TCOMPLEX64}: ssa.OpComplexReal, + {ir.OREAL, types.TCOMPLEX128}: ssa.OpComplexReal, + + {ir.OMUL, types.TINT8}: ssa.OpMul8, + {ir.OMUL, types.TUINT8}: ssa.OpMul8, + {ir.OMUL, types.TINT16}: ssa.OpMul16, + {ir.OMUL, types.TUINT16}: ssa.OpMul16, + {ir.OMUL, types.TINT32}: ssa.OpMul32, + {ir.OMUL, types.TUINT32}: ssa.OpMul32, + {ir.OMUL, types.TINT64}: ssa.OpMul64, + {ir.OMUL, types.TUINT64}: ssa.OpMul64, + {ir.OMUL, types.TFLOAT32}: ssa.OpMul32F, + {ir.OMUL, types.TFLOAT64}: ssa.OpMul64F, + + {ir.ODIV, types.TFLOAT32}: ssa.OpDiv32F, + {ir.ODIV, types.TFLOAT64}: ssa.OpDiv64F, + + {ir.ODIV, types.TINT8}: ssa.OpDiv8, + {ir.ODIV, types.TUINT8}: ssa.OpDiv8u, + {ir.ODIV, types.TINT16}: ssa.OpDiv16, + {ir.ODIV, types.TUINT16}: ssa.OpDiv16u, + {ir.ODIV, types.TINT32}: ssa.OpDiv32, + {ir.ODIV, types.TUINT32}: ssa.OpDiv32u, + {ir.ODIV, types.TINT64}: ssa.OpDiv64, + {ir.ODIV, types.TUINT64}: ssa.OpDiv64u, + + {ir.OMOD, types.TINT8}: ssa.OpMod8, + {ir.OMOD, types.TUINT8}: ssa.OpMod8u, + {ir.OMOD, types.TINT16}: ssa.OpMod16, + {ir.OMOD, types.TUINT16}: ssa.OpMod16u, + {ir.OMOD, types.TINT32}: ssa.OpMod32, + {ir.OMOD, types.TUINT32}: ssa.OpMod32u, + {ir.OMOD, types.TINT64}: ssa.OpMod64, + {ir.OMOD, types.TUINT64}: ssa.OpMod64u, + + {ir.OAND, types.TINT8}: ssa.OpAnd8, + {ir.OAND, types.TUINT8}: ssa.OpAnd8, + {ir.OAND, types.TINT16}: ssa.OpAnd16, + {ir.OAND, types.TUINT16}: ssa.OpAnd16, + {ir.OAND, types.TINT32}: ssa.OpAnd32, + {ir.OAND, types.TUINT32}: ssa.OpAnd32, + {ir.OAND, types.TINT64}: ssa.OpAnd64, + {ir.OAND, types.TUINT64}: ssa.OpAnd64, + + {ir.OOR, types.TINT8}: ssa.OpOr8, + {ir.OOR, types.TUINT8}: ssa.OpOr8, + {ir.OOR, types.TINT16}: ssa.OpOr16, + {ir.OOR, types.TUINT16}: ssa.OpOr16, + {ir.OOR, types.TINT32}: ssa.OpOr32, + {ir.OOR, types.TUINT32}: ssa.OpOr32, + {ir.OOR, types.TINT64}: ssa.OpOr64, + {ir.OOR, types.TUINT64}: ssa.OpOr64, + + {ir.OXOR, types.TINT8}: ssa.OpXor8, + {ir.OXOR, types.TUINT8}: ssa.OpXor8, + {ir.OXOR, types.TINT16}: ssa.OpXor16, + {ir.OXOR, types.TUINT16}: ssa.OpXor16, + {ir.OXOR, types.TINT32}: ssa.OpXor32, + {ir.OXOR, types.TUINT32}: ssa.OpXor32, + {ir.OXOR, types.TINT64}: ssa.OpXor64, + {ir.OXOR, types.TUINT64}: ssa.OpXor64, + + {ir.OEQ, types.TBOOL}: ssa.OpEqB, + {ir.OEQ, types.TINT8}: ssa.OpEq8, + {ir.OEQ, types.TUINT8}: ssa.OpEq8, + {ir.OEQ, types.TINT16}: ssa.OpEq16, + {ir.OEQ, types.TUINT16}: ssa.OpEq16, + {ir.OEQ, types.TINT32}: ssa.OpEq32, + {ir.OEQ, types.TUINT32}: ssa.OpEq32, + {ir.OEQ, types.TINT64}: ssa.OpEq64, + {ir.OEQ, types.TUINT64}: ssa.OpEq64, + {ir.OEQ, types.TINTER}: ssa.OpEqInter, + {ir.OEQ, types.TSLICE}: ssa.OpEqSlice, + {ir.OEQ, types.TFUNC}: ssa.OpEqPtr, + {ir.OEQ, types.TMAP}: ssa.OpEqPtr, + {ir.OEQ, types.TCHAN}: ssa.OpEqPtr, + {ir.OEQ, types.TPTR}: ssa.OpEqPtr, + {ir.OEQ, types.TUINTPTR}: ssa.OpEqPtr, + {ir.OEQ, types.TUNSAFEPTR}: ssa.OpEqPtr, + {ir.OEQ, types.TFLOAT64}: ssa.OpEq64F, + {ir.OEQ, types.TFLOAT32}: ssa.OpEq32F, + + {ir.ONE, types.TBOOL}: ssa.OpNeqB, + {ir.ONE, types.TINT8}: ssa.OpNeq8, + {ir.ONE, types.TUINT8}: ssa.OpNeq8, + {ir.ONE, types.TINT16}: ssa.OpNeq16, + {ir.ONE, types.TUINT16}: ssa.OpNeq16, + {ir.ONE, types.TINT32}: ssa.OpNeq32, + {ir.ONE, types.TUINT32}: ssa.OpNeq32, + {ir.ONE, types.TINT64}: ssa.OpNeq64, + {ir.ONE, types.TUINT64}: ssa.OpNeq64, + {ir.ONE, types.TINTER}: ssa.OpNeqInter, + {ir.ONE, types.TSLICE}: ssa.OpNeqSlice, + {ir.ONE, types.TFUNC}: ssa.OpNeqPtr, + {ir.ONE, types.TMAP}: ssa.OpNeqPtr, + {ir.ONE, types.TCHAN}: ssa.OpNeqPtr, + {ir.ONE, types.TPTR}: ssa.OpNeqPtr, + {ir.ONE, types.TUINTPTR}: ssa.OpNeqPtr, + {ir.ONE, types.TUNSAFEPTR}: ssa.OpNeqPtr, + {ir.ONE, types.TFLOAT64}: ssa.OpNeq64F, + {ir.ONE, types.TFLOAT32}: ssa.OpNeq32F, + + {ir.OLT, types.TINT8}: ssa.OpLess8, + {ir.OLT, types.TUINT8}: ssa.OpLess8U, + {ir.OLT, types.TINT16}: ssa.OpLess16, + {ir.OLT, types.TUINT16}: ssa.OpLess16U, + {ir.OLT, types.TINT32}: ssa.OpLess32, + {ir.OLT, types.TUINT32}: ssa.OpLess32U, + {ir.OLT, types.TINT64}: ssa.OpLess64, + {ir.OLT, types.TUINT64}: ssa.OpLess64U, + {ir.OLT, types.TFLOAT64}: ssa.OpLess64F, + {ir.OLT, types.TFLOAT32}: ssa.OpLess32F, + + {ir.OLE, types.TINT8}: ssa.OpLeq8, + {ir.OLE, types.TUINT8}: ssa.OpLeq8U, + {ir.OLE, types.TINT16}: ssa.OpLeq16, + {ir.OLE, types.TUINT16}: ssa.OpLeq16U, + {ir.OLE, types.TINT32}: ssa.OpLeq32, + {ir.OLE, types.TUINT32}: ssa.OpLeq32U, + {ir.OLE, types.TINT64}: ssa.OpLeq64, + {ir.OLE, types.TUINT64}: ssa.OpLeq64U, + {ir.OLE, types.TFLOAT64}: ssa.OpLeq64F, + {ir.OLE, types.TFLOAT32}: ssa.OpLeq32F, } func (s *state) concreteEtype(t *types.Type) types.Kind { @@ -2239,142 +2316,142 @@ type twoOpsAndType struct { var fpConvOpToSSA = map[twoTypes]twoOpsAndType{ - twoTypes{types.TINT8, types.TFLOAT32}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to32F, types.TINT32}, - twoTypes{types.TINT16, types.TFLOAT32}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to32F, types.TINT32}, - twoTypes{types.TINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to32F, types.TINT32}, - twoTypes{types.TINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to32F, types.TINT64}, - - twoTypes{types.TINT8, types.TFLOAT64}: twoOpsAndType{ssa.OpSignExt8to32, ssa.OpCvt32to64F, types.TINT32}, - twoTypes{types.TINT16, types.TFLOAT64}: twoOpsAndType{ssa.OpSignExt16to32, ssa.OpCvt32to64F, types.TINT32}, - twoTypes{types.TINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32to64F, types.TINT32}, - twoTypes{types.TINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64to64F, types.TINT64}, - - twoTypes{types.TFLOAT32, types.TINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32}, - twoTypes{types.TFLOAT32, types.TINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32}, - twoTypes{types.TFLOAT32, types.TINT32}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpCopy, types.TINT32}, - twoTypes{types.TFLOAT32, types.TINT64}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpCopy, types.TINT64}, - - twoTypes{types.TFLOAT64, types.TINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32}, - twoTypes{types.TFLOAT64, types.TINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32}, - twoTypes{types.TFLOAT64, types.TINT32}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpCopy, types.TINT32}, - twoTypes{types.TFLOAT64, types.TINT64}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpCopy, types.TINT64}, + {types.TINT8, types.TFLOAT32}: {ssa.OpSignExt8to32, ssa.OpCvt32to32F, types.TINT32}, + {types.TINT16, types.TFLOAT32}: {ssa.OpSignExt16to32, ssa.OpCvt32to32F, types.TINT32}, + {types.TINT32, types.TFLOAT32}: {ssa.OpCopy, ssa.OpCvt32to32F, types.TINT32}, + {types.TINT64, types.TFLOAT32}: {ssa.OpCopy, ssa.OpCvt64to32F, types.TINT64}, + + {types.TINT8, types.TFLOAT64}: {ssa.OpSignExt8to32, ssa.OpCvt32to64F, types.TINT32}, + {types.TINT16, types.TFLOAT64}: {ssa.OpSignExt16to32, ssa.OpCvt32to64F, types.TINT32}, + {types.TINT32, types.TFLOAT64}: {ssa.OpCopy, ssa.OpCvt32to64F, types.TINT32}, + {types.TINT64, types.TFLOAT64}: {ssa.OpCopy, ssa.OpCvt64to64F, types.TINT64}, + + {types.TFLOAT32, types.TINT8}: {ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32}, + {types.TFLOAT32, types.TINT16}: {ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32}, + {types.TFLOAT32, types.TINT32}: {ssa.OpCvt32Fto32, ssa.OpCopy, types.TINT32}, + {types.TFLOAT32, types.TINT64}: {ssa.OpCvt32Fto64, ssa.OpCopy, types.TINT64}, + + {types.TFLOAT64, types.TINT8}: {ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32}, + {types.TFLOAT64, types.TINT16}: {ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32}, + {types.TFLOAT64, types.TINT32}: {ssa.OpCvt64Fto32, ssa.OpCopy, types.TINT32}, + {types.TFLOAT64, types.TINT64}: {ssa.OpCvt64Fto64, ssa.OpCopy, types.TINT64}, // unsigned - twoTypes{types.TUINT8, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to32F, types.TINT32}, - twoTypes{types.TUINT16, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to32F, types.TINT32}, - twoTypes{types.TUINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to32F, types.TINT64}, // go wide to dodge unsigned - twoTypes{types.TUINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto32F, branchy code expansion instead - - twoTypes{types.TUINT8, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt8to32, ssa.OpCvt32to64F, types.TINT32}, - twoTypes{types.TUINT16, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt16to32, ssa.OpCvt32to64F, types.TINT32}, - twoTypes{types.TUINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpZeroExt32to64, ssa.OpCvt64to64F, types.TINT64}, // go wide to dodge unsigned - twoTypes{types.TUINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto64F, branchy code expansion instead - - twoTypes{types.TFLOAT32, types.TUINT8}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32}, - twoTypes{types.TFLOAT32, types.TUINT16}: twoOpsAndType{ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32}, - twoTypes{types.TFLOAT32, types.TUINT32}: twoOpsAndType{ssa.OpCvt32Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned - twoTypes{types.TFLOAT32, types.TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt32Fto64U, branchy code expansion instead - - twoTypes{types.TFLOAT64, types.TUINT8}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32}, - twoTypes{types.TFLOAT64, types.TUINT16}: twoOpsAndType{ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32}, - twoTypes{types.TFLOAT64, types.TUINT32}: twoOpsAndType{ssa.OpCvt64Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned - twoTypes{types.TFLOAT64, types.TUINT64}: twoOpsAndType{ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt64Fto64U, branchy code expansion instead + {types.TUINT8, types.TFLOAT32}: {ssa.OpZeroExt8to32, ssa.OpCvt32to32F, types.TINT32}, + {types.TUINT16, types.TFLOAT32}: {ssa.OpZeroExt16to32, ssa.OpCvt32to32F, types.TINT32}, + {types.TUINT32, types.TFLOAT32}: {ssa.OpZeroExt32to64, ssa.OpCvt64to32F, types.TINT64}, // go wide to dodge unsigned + {types.TUINT64, types.TFLOAT32}: {ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto32F, branchy code expansion instead + + {types.TUINT8, types.TFLOAT64}: {ssa.OpZeroExt8to32, ssa.OpCvt32to64F, types.TINT32}, + {types.TUINT16, types.TFLOAT64}: {ssa.OpZeroExt16to32, ssa.OpCvt32to64F, types.TINT32}, + {types.TUINT32, types.TFLOAT64}: {ssa.OpZeroExt32to64, ssa.OpCvt64to64F, types.TINT64}, // go wide to dodge unsigned + {types.TUINT64, types.TFLOAT64}: {ssa.OpCopy, ssa.OpInvalid, types.TUINT64}, // Cvt64Uto64F, branchy code expansion instead + + {types.TFLOAT32, types.TUINT8}: {ssa.OpCvt32Fto32, ssa.OpTrunc32to8, types.TINT32}, + {types.TFLOAT32, types.TUINT16}: {ssa.OpCvt32Fto32, ssa.OpTrunc32to16, types.TINT32}, + {types.TFLOAT32, types.TUINT32}: {ssa.OpCvt32Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned + {types.TFLOAT32, types.TUINT64}: {ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt32Fto64U, branchy code expansion instead + + {types.TFLOAT64, types.TUINT8}: {ssa.OpCvt64Fto32, ssa.OpTrunc32to8, types.TINT32}, + {types.TFLOAT64, types.TUINT16}: {ssa.OpCvt64Fto32, ssa.OpTrunc32to16, types.TINT32}, + {types.TFLOAT64, types.TUINT32}: {ssa.OpCvt64Fto64, ssa.OpTrunc64to32, types.TINT64}, // go wide to dodge unsigned + {types.TFLOAT64, types.TUINT64}: {ssa.OpInvalid, ssa.OpCopy, types.TUINT64}, // Cvt64Fto64U, branchy code expansion instead // float - twoTypes{types.TFLOAT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCvt64Fto32F, ssa.OpCopy, types.TFLOAT32}, - twoTypes{types.TFLOAT64, types.TFLOAT64}: twoOpsAndType{ssa.OpRound64F, ssa.OpCopy, types.TFLOAT64}, - twoTypes{types.TFLOAT32, types.TFLOAT32}: twoOpsAndType{ssa.OpRound32F, ssa.OpCopy, types.TFLOAT32}, - twoTypes{types.TFLOAT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCvt32Fto64F, ssa.OpCopy, types.TFLOAT64}, + {types.TFLOAT64, types.TFLOAT32}: {ssa.OpCvt64Fto32F, ssa.OpCopy, types.TFLOAT32}, + {types.TFLOAT64, types.TFLOAT64}: {ssa.OpRound64F, ssa.OpCopy, types.TFLOAT64}, + {types.TFLOAT32, types.TFLOAT32}: {ssa.OpRound32F, ssa.OpCopy, types.TFLOAT32}, + {types.TFLOAT32, types.TFLOAT64}: {ssa.OpCvt32Fto64F, ssa.OpCopy, types.TFLOAT64}, } // this map is used only for 32-bit arch, and only includes the difference // on 32-bit arch, don't use int64<->float conversion for uint32 var fpConvOpToSSA32 = map[twoTypes]twoOpsAndType{ - twoTypes{types.TUINT32, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto32F, types.TUINT32}, - twoTypes{types.TUINT32, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto64F, types.TUINT32}, - twoTypes{types.TFLOAT32, types.TUINT32}: twoOpsAndType{ssa.OpCvt32Fto32U, ssa.OpCopy, types.TUINT32}, - twoTypes{types.TFLOAT64, types.TUINT32}: twoOpsAndType{ssa.OpCvt64Fto32U, ssa.OpCopy, types.TUINT32}, + {types.TUINT32, types.TFLOAT32}: {ssa.OpCopy, ssa.OpCvt32Uto32F, types.TUINT32}, + {types.TUINT32, types.TFLOAT64}: {ssa.OpCopy, ssa.OpCvt32Uto64F, types.TUINT32}, + {types.TFLOAT32, types.TUINT32}: {ssa.OpCvt32Fto32U, ssa.OpCopy, types.TUINT32}, + {types.TFLOAT64, types.TUINT32}: {ssa.OpCvt64Fto32U, ssa.OpCopy, types.TUINT32}, } // uint64<->float conversions, only on machines that have instructions for that var uint64fpConvOpToSSA = map[twoTypes]twoOpsAndType{ - twoTypes{types.TUINT64, types.TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto32F, types.TUINT64}, - twoTypes{types.TUINT64, types.TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto64F, types.TUINT64}, - twoTypes{types.TFLOAT32, types.TUINT64}: twoOpsAndType{ssa.OpCvt32Fto64U, ssa.OpCopy, types.TUINT64}, - twoTypes{types.TFLOAT64, types.TUINT64}: twoOpsAndType{ssa.OpCvt64Fto64U, ssa.OpCopy, types.TUINT64}, + {types.TUINT64, types.TFLOAT32}: {ssa.OpCopy, ssa.OpCvt64Uto32F, types.TUINT64}, + {types.TUINT64, types.TFLOAT64}: {ssa.OpCopy, ssa.OpCvt64Uto64F, types.TUINT64}, + {types.TFLOAT32, types.TUINT64}: {ssa.OpCvt32Fto64U, ssa.OpCopy, types.TUINT64}, + {types.TFLOAT64, types.TUINT64}: {ssa.OpCvt64Fto64U, ssa.OpCopy, types.TUINT64}, } var shiftOpToSSA = map[opAndTwoTypes]ssa.Op{ - opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT8}: ssa.OpLsh8x8, - opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT8}: ssa.OpLsh8x8, - opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT16}: ssa.OpLsh8x16, - opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT16}: ssa.OpLsh8x16, - opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT32}: ssa.OpLsh8x32, - opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT32}: ssa.OpLsh8x32, - opAndTwoTypes{ir.OLSH, types.TINT8, types.TUINT64}: ssa.OpLsh8x64, - opAndTwoTypes{ir.OLSH, types.TUINT8, types.TUINT64}: ssa.OpLsh8x64, - - opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT8}: ssa.OpLsh16x8, - opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT8}: ssa.OpLsh16x8, - opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT16}: ssa.OpLsh16x16, - opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT16}: ssa.OpLsh16x16, - opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT32}: ssa.OpLsh16x32, - opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT32}: ssa.OpLsh16x32, - opAndTwoTypes{ir.OLSH, types.TINT16, types.TUINT64}: ssa.OpLsh16x64, - opAndTwoTypes{ir.OLSH, types.TUINT16, types.TUINT64}: ssa.OpLsh16x64, - - opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT8}: ssa.OpLsh32x8, - opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT8}: ssa.OpLsh32x8, - opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT16}: ssa.OpLsh32x16, - opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT16}: ssa.OpLsh32x16, - opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT32}: ssa.OpLsh32x32, - opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT32}: ssa.OpLsh32x32, - opAndTwoTypes{ir.OLSH, types.TINT32, types.TUINT64}: ssa.OpLsh32x64, - opAndTwoTypes{ir.OLSH, types.TUINT32, types.TUINT64}: ssa.OpLsh32x64, - - opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT8}: ssa.OpLsh64x8, - opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT8}: ssa.OpLsh64x8, - opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT16}: ssa.OpLsh64x16, - opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT16}: ssa.OpLsh64x16, - opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT32}: ssa.OpLsh64x32, - opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT32}: ssa.OpLsh64x32, - opAndTwoTypes{ir.OLSH, types.TINT64, types.TUINT64}: ssa.OpLsh64x64, - opAndTwoTypes{ir.OLSH, types.TUINT64, types.TUINT64}: ssa.OpLsh64x64, - - opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT8}: ssa.OpRsh8x8, - opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT8}: ssa.OpRsh8Ux8, - opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT16}: ssa.OpRsh8x16, - opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT16}: ssa.OpRsh8Ux16, - opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT32}: ssa.OpRsh8x32, - opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT32}: ssa.OpRsh8Ux32, - opAndTwoTypes{ir.ORSH, types.TINT8, types.TUINT64}: ssa.OpRsh8x64, - opAndTwoTypes{ir.ORSH, types.TUINT8, types.TUINT64}: ssa.OpRsh8Ux64, - - opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT8}: ssa.OpRsh16x8, - opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT8}: ssa.OpRsh16Ux8, - opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT16}: ssa.OpRsh16x16, - opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT16}: ssa.OpRsh16Ux16, - opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT32}: ssa.OpRsh16x32, - opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT32}: ssa.OpRsh16Ux32, - opAndTwoTypes{ir.ORSH, types.TINT16, types.TUINT64}: ssa.OpRsh16x64, - opAndTwoTypes{ir.ORSH, types.TUINT16, types.TUINT64}: ssa.OpRsh16Ux64, - - opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT8}: ssa.OpRsh32x8, - opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT8}: ssa.OpRsh32Ux8, - opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT16}: ssa.OpRsh32x16, - opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT16}: ssa.OpRsh32Ux16, - opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT32}: ssa.OpRsh32x32, - opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT32}: ssa.OpRsh32Ux32, - opAndTwoTypes{ir.ORSH, types.TINT32, types.TUINT64}: ssa.OpRsh32x64, - opAndTwoTypes{ir.ORSH, types.TUINT32, types.TUINT64}: ssa.OpRsh32Ux64, - - opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT8}: ssa.OpRsh64x8, - opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT8}: ssa.OpRsh64Ux8, - opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT16}: ssa.OpRsh64x16, - opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT16}: ssa.OpRsh64Ux16, - opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT32}: ssa.OpRsh64x32, - opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT32}: ssa.OpRsh64Ux32, - opAndTwoTypes{ir.ORSH, types.TINT64, types.TUINT64}: ssa.OpRsh64x64, - opAndTwoTypes{ir.ORSH, types.TUINT64, types.TUINT64}: ssa.OpRsh64Ux64, + {ir.OLSH, types.TINT8, types.TUINT8}: ssa.OpLsh8x8, + {ir.OLSH, types.TUINT8, types.TUINT8}: ssa.OpLsh8x8, + {ir.OLSH, types.TINT8, types.TUINT16}: ssa.OpLsh8x16, + {ir.OLSH, types.TUINT8, types.TUINT16}: ssa.OpLsh8x16, + {ir.OLSH, types.TINT8, types.TUINT32}: ssa.OpLsh8x32, + {ir.OLSH, types.TUINT8, types.TUINT32}: ssa.OpLsh8x32, + {ir.OLSH, types.TINT8, types.TUINT64}: ssa.OpLsh8x64, + {ir.OLSH, types.TUINT8, types.TUINT64}: ssa.OpLsh8x64, + + {ir.OLSH, types.TINT16, types.TUINT8}: ssa.OpLsh16x8, + {ir.OLSH, types.TUINT16, types.TUINT8}: ssa.OpLsh16x8, + {ir.OLSH, types.TINT16, types.TUINT16}: ssa.OpLsh16x16, + {ir.OLSH, types.TUINT16, types.TUINT16}: ssa.OpLsh16x16, + {ir.OLSH, types.TINT16, types.TUINT32}: ssa.OpLsh16x32, + {ir.OLSH, types.TUINT16, types.TUINT32}: ssa.OpLsh16x32, + {ir.OLSH, types.TINT16, types.TUINT64}: ssa.OpLsh16x64, + {ir.OLSH, types.TUINT16, types.TUINT64}: ssa.OpLsh16x64, + + {ir.OLSH, types.TINT32, types.TUINT8}: ssa.OpLsh32x8, + {ir.OLSH, types.TUINT32, types.TUINT8}: ssa.OpLsh32x8, + {ir.OLSH, types.TINT32, types.TUINT16}: ssa.OpLsh32x16, + {ir.OLSH, types.TUINT32, types.TUINT16}: ssa.OpLsh32x16, + {ir.OLSH, types.TINT32, types.TUINT32}: ssa.OpLsh32x32, + {ir.OLSH, types.TUINT32, types.TUINT32}: ssa.OpLsh32x32, + {ir.OLSH, types.TINT32, types.TUINT64}: ssa.OpLsh32x64, + {ir.OLSH, types.TUINT32, types.TUINT64}: ssa.OpLsh32x64, + + {ir.OLSH, types.TINT64, types.TUINT8}: ssa.OpLsh64x8, + {ir.OLSH, types.TUINT64, types.TUINT8}: ssa.OpLsh64x8, + {ir.OLSH, types.TINT64, types.TUINT16}: ssa.OpLsh64x16, + {ir.OLSH, types.TUINT64, types.TUINT16}: ssa.OpLsh64x16, + {ir.OLSH, types.TINT64, types.TUINT32}: ssa.OpLsh64x32, + {ir.OLSH, types.TUINT64, types.TUINT32}: ssa.OpLsh64x32, + {ir.OLSH, types.TINT64, types.TUINT64}: ssa.OpLsh64x64, + {ir.OLSH, types.TUINT64, types.TUINT64}: ssa.OpLsh64x64, + + {ir.ORSH, types.TINT8, types.TUINT8}: ssa.OpRsh8x8, + {ir.ORSH, types.TUINT8, types.TUINT8}: ssa.OpRsh8Ux8, + {ir.ORSH, types.TINT8, types.TUINT16}: ssa.OpRsh8x16, + {ir.ORSH, types.TUINT8, types.TUINT16}: ssa.OpRsh8Ux16, + {ir.ORSH, types.TINT8, types.TUINT32}: ssa.OpRsh8x32, + {ir.ORSH, types.TUINT8, types.TUINT32}: ssa.OpRsh8Ux32, + {ir.ORSH, types.TINT8, types.TUINT64}: ssa.OpRsh8x64, + {ir.ORSH, types.TUINT8, types.TUINT64}: ssa.OpRsh8Ux64, + + {ir.ORSH, types.TINT16, types.TUINT8}: ssa.OpRsh16x8, + {ir.ORSH, types.TUINT16, types.TUINT8}: ssa.OpRsh16Ux8, + {ir.ORSH, types.TINT16, types.TUINT16}: ssa.OpRsh16x16, + {ir.ORSH, types.TUINT16, types.TUINT16}: ssa.OpRsh16Ux16, + {ir.ORSH, types.TINT16, types.TUINT32}: ssa.OpRsh16x32, + {ir.ORSH, types.TUINT16, types.TUINT32}: ssa.OpRsh16Ux32, + {ir.ORSH, types.TINT16, types.TUINT64}: ssa.OpRsh16x64, + {ir.ORSH, types.TUINT16, types.TUINT64}: ssa.OpRsh16Ux64, + + {ir.ORSH, types.TINT32, types.TUINT8}: ssa.OpRsh32x8, + {ir.ORSH, types.TUINT32, types.TUINT8}: ssa.OpRsh32Ux8, + {ir.ORSH, types.TINT32, types.TUINT16}: ssa.OpRsh32x16, + {ir.ORSH, types.TUINT32, types.TUINT16}: ssa.OpRsh32Ux16, + {ir.ORSH, types.TINT32, types.TUINT32}: ssa.OpRsh32x32, + {ir.ORSH, types.TUINT32, types.TUINT32}: ssa.OpRsh32Ux32, + {ir.ORSH, types.TINT32, types.TUINT64}: ssa.OpRsh32x64, + {ir.ORSH, types.TUINT32, types.TUINT64}: ssa.OpRsh32Ux64, + + {ir.ORSH, types.TINT64, types.TUINT8}: ssa.OpRsh64x8, + {ir.ORSH, types.TUINT64, types.TUINT8}: ssa.OpRsh64Ux8, + {ir.ORSH, types.TINT64, types.TUINT16}: ssa.OpRsh64x16, + {ir.ORSH, types.TUINT64, types.TUINT16}: ssa.OpRsh64Ux16, + {ir.ORSH, types.TINT64, types.TUINT32}: ssa.OpRsh64x32, + {ir.ORSH, types.TUINT64, types.TUINT32}: ssa.OpRsh64Ux32, + {ir.ORSH, types.TINT64, types.TUINT64}: ssa.OpRsh64x64, + {ir.ORSH, types.TUINT64, types.TUINT64}: ssa.OpRsh64Ux64, } func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op { @@ -2387,8 +2464,195 @@ func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op { return x } +func (s *state) uintptrConstant(v uint64) *ssa.Value { + if s.config.PtrSize == 4 { + return s.newValue0I(ssa.OpConst32, types.Types[types.TUINTPTR], int64(v)) + } + return s.newValue0I(ssa.OpConst64, types.Types[types.TUINTPTR], int64(v)) +} + +func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value { + if ft.IsBoolean() && tt.IsKind(types.TUINT8) { + // Bool -> uint8 is generated internally when indexing into runtime.staticbyte. + return s.newValue1(ssa.OpCvtBoolToUint8, tt, v) + } + if ft.IsInteger() && tt.IsInteger() { + var op ssa.Op + if tt.Size() == ft.Size() { + op = ssa.OpCopy + } else if tt.Size() < ft.Size() { + // truncation + switch 10*ft.Size() + tt.Size() { + case 21: + op = ssa.OpTrunc16to8 + case 41: + op = ssa.OpTrunc32to8 + case 42: + op = ssa.OpTrunc32to16 + case 81: + op = ssa.OpTrunc64to8 + case 82: + op = ssa.OpTrunc64to16 + case 84: + op = ssa.OpTrunc64to32 + default: + s.Fatalf("weird integer truncation %v -> %v", ft, tt) + } + } else if ft.IsSigned() { + // sign extension + switch 10*ft.Size() + tt.Size() { + case 12: + op = ssa.OpSignExt8to16 + case 14: + op = ssa.OpSignExt8to32 + case 18: + op = ssa.OpSignExt8to64 + case 24: + op = ssa.OpSignExt16to32 + case 28: + op = ssa.OpSignExt16to64 + case 48: + op = ssa.OpSignExt32to64 + default: + s.Fatalf("bad integer sign extension %v -> %v", ft, tt) + } + } else { + // zero extension + switch 10*ft.Size() + tt.Size() { + case 12: + op = ssa.OpZeroExt8to16 + case 14: + op = ssa.OpZeroExt8to32 + case 18: + op = ssa.OpZeroExt8to64 + case 24: + op = ssa.OpZeroExt16to32 + case 28: + op = ssa.OpZeroExt16to64 + case 48: + op = ssa.OpZeroExt32to64 + default: + s.Fatalf("weird integer sign extension %v -> %v", ft, tt) + } + } + return s.newValue1(op, tt, v) + } + + if ft.IsComplex() && tt.IsComplex() { + var op ssa.Op + if ft.Size() == tt.Size() { + switch ft.Size() { + case 8: + op = ssa.OpRound32F + case 16: + op = ssa.OpRound64F + default: + s.Fatalf("weird complex conversion %v -> %v", ft, tt) + } + } else if ft.Size() == 8 && tt.Size() == 16 { + op = ssa.OpCvt32Fto64F + } else if ft.Size() == 16 && tt.Size() == 8 { + op = ssa.OpCvt64Fto32F + } else { + s.Fatalf("weird complex conversion %v -> %v", ft, tt) + } + ftp := types.FloatForComplex(ft) + ttp := types.FloatForComplex(tt) + return s.newValue2(ssa.OpComplexMake, tt, + s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, v)), + s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v))) + } + + if tt.IsComplex() { // and ft is not complex + // Needed for generics support - can't happen in normal Go code. + et := types.FloatForComplex(tt) + v = s.conv(n, v, ft, et) + return s.newValue2(ssa.OpComplexMake, tt, v, s.zeroVal(et)) + } + + if ft.IsFloat() || tt.IsFloat() { + conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}] + if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat { + if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { + conv = conv1 + } + } + if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat { + if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { + conv = conv1 + } + } + + if Arch.LinkArch.Family == sys.MIPS && !s.softFloat { + if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() { + // tt is float32 or float64, and ft is also unsigned + if tt.Size() == 4 { + return s.uint32Tofloat32(n, v, ft, tt) + } + if tt.Size() == 8 { + return s.uint32Tofloat64(n, v, ft, tt) + } + } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() { + // ft is float32 or float64, and tt is unsigned integer + if ft.Size() == 4 { + return s.float32ToUint32(n, v, ft, tt) + } + if ft.Size() == 8 { + return s.float64ToUint32(n, v, ft, tt) + } + } + } + + if !ok { + s.Fatalf("weird float conversion %v -> %v", ft, tt) + } + op1, op2, it := conv.op1, conv.op2, conv.intermediateType + + if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid { + // normal case, not tripping over unsigned 64 + if op1 == ssa.OpCopy { + if op2 == ssa.OpCopy { + return v + } + return s.newValueOrSfCall1(op2, tt, v) + } + if op2 == ssa.OpCopy { + return s.newValueOrSfCall1(op1, tt, v) + } + return s.newValueOrSfCall1(op2, tt, s.newValueOrSfCall1(op1, types.Types[it], v)) + } + // Tricky 64-bit unsigned cases. + if ft.IsInteger() { + // tt is float32 or float64, and ft is also unsigned + if tt.Size() == 4 { + return s.uint64Tofloat32(n, v, ft, tt) + } + if tt.Size() == 8 { + return s.uint64Tofloat64(n, v, ft, tt) + } + s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt) + } + // ft is float32 or float64, and tt is unsigned integer + if ft.Size() == 4 { + return s.float32ToUint64(n, v, ft, tt) + } + if ft.Size() == 8 { + return s.float64ToUint64(n, v, ft, tt) + } + s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt) + return nil + } + + s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind()) + return nil +} + // expr converts the expression n to ssa, adds it to s and returns the ssa result. func (s *state) expr(n ir.Node) *ssa.Value { + return s.exprCheckPtr(n, true) +} + +func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { if ir.HasUniquePos(n) { // ONAMEs and named OLITERALs have the line number // of the decl, not the use. See issue 14742. @@ -2536,6 +2800,9 @@ func (s *state) expr(n ir.Node) *ssa.Value { // unsafe.Pointer <--> *T if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() { + if s.checkPtrEnabled && checkPtrOK && to.IsPtr() && from.IsUnsafePtr() { + s.checkPtrAlignment(n, v, nil) + } return v } @@ -2547,8 +2814,8 @@ func (s *state) expr(n ir.Node) *ssa.Value { types.CalcSize(from) types.CalcSize(to) - if from.Width != to.Width { - s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Width, to, to.Width) + if from.Size() != to.Size() { + s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Size(), to, to.Size()) return nil } if etypesign(from.Kind()) != etypesign(to.Kind()) { @@ -2574,180 +2841,18 @@ func (s *state) expr(n ir.Node) *ssa.Value { case ir.OCONV: n := n.(*ir.ConvExpr) x := s.expr(n.X) - ft := n.X.Type() // from type - tt := n.Type() // to type - if ft.IsBoolean() && tt.IsKind(types.TUINT8) { - // Bool -> uint8 is generated internally when indexing into runtime.staticbyte. - return s.newValue1(ssa.OpCopy, n.Type(), x) - } - if ft.IsInteger() && tt.IsInteger() { - var op ssa.Op - if tt.Size() == ft.Size() { - op = ssa.OpCopy - } else if tt.Size() < ft.Size() { - // truncation - switch 10*ft.Size() + tt.Size() { - case 21: - op = ssa.OpTrunc16to8 - case 41: - op = ssa.OpTrunc32to8 - case 42: - op = ssa.OpTrunc32to16 - case 81: - op = ssa.OpTrunc64to8 - case 82: - op = ssa.OpTrunc64to16 - case 84: - op = ssa.OpTrunc64to32 - default: - s.Fatalf("weird integer truncation %v -> %v", ft, tt) - } - } else if ft.IsSigned() { - // sign extension - switch 10*ft.Size() + tt.Size() { - case 12: - op = ssa.OpSignExt8to16 - case 14: - op = ssa.OpSignExt8to32 - case 18: - op = ssa.OpSignExt8to64 - case 24: - op = ssa.OpSignExt16to32 - case 28: - op = ssa.OpSignExt16to64 - case 48: - op = ssa.OpSignExt32to64 - default: - s.Fatalf("bad integer sign extension %v -> %v", ft, tt) - } - } else { - // zero extension - switch 10*ft.Size() + tt.Size() { - case 12: - op = ssa.OpZeroExt8to16 - case 14: - op = ssa.OpZeroExt8to32 - case 18: - op = ssa.OpZeroExt8to64 - case 24: - op = ssa.OpZeroExt16to32 - case 28: - op = ssa.OpZeroExt16to64 - case 48: - op = ssa.OpZeroExt32to64 - default: - s.Fatalf("weird integer sign extension %v -> %v", ft, tt) - } - } - return s.newValue1(op, n.Type(), x) - } - - if ft.IsFloat() || tt.IsFloat() { - conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}] - if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat { - if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { - conv = conv1 - } - } - if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat { - if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { - conv = conv1 - } - } - - if Arch.LinkArch.Family == sys.MIPS && !s.softFloat { - if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() { - // tt is float32 or float64, and ft is also unsigned - if tt.Size() == 4 { - return s.uint32Tofloat32(n, x, ft, tt) - } - if tt.Size() == 8 { - return s.uint32Tofloat64(n, x, ft, tt) - } - } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() { - // ft is float32 or float64, and tt is unsigned integer - if ft.Size() == 4 { - return s.float32ToUint32(n, x, ft, tt) - } - if ft.Size() == 8 { - return s.float64ToUint32(n, x, ft, tt) - } - } - } - - if !ok { - s.Fatalf("weird float conversion %v -> %v", ft, tt) - } - op1, op2, it := conv.op1, conv.op2, conv.intermediateType - - if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid { - // normal case, not tripping over unsigned 64 - if op1 == ssa.OpCopy { - if op2 == ssa.OpCopy { - return x - } - return s.newValueOrSfCall1(op2, n.Type(), x) - } - if op2 == ssa.OpCopy { - return s.newValueOrSfCall1(op1, n.Type(), x) - } - return s.newValueOrSfCall1(op2, n.Type(), s.newValueOrSfCall1(op1, types.Types[it], x)) - } - // Tricky 64-bit unsigned cases. - if ft.IsInteger() { - // tt is float32 or float64, and ft is also unsigned - if tt.Size() == 4 { - return s.uint64Tofloat32(n, x, ft, tt) - } - if tt.Size() == 8 { - return s.uint64Tofloat64(n, x, ft, tt) - } - s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt) - } - // ft is float32 or float64, and tt is unsigned integer - if ft.Size() == 4 { - return s.float32ToUint64(n, x, ft, tt) - } - if ft.Size() == 8 { - return s.float64ToUint64(n, x, ft, tt) - } - s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt) - return nil - } - - if ft.IsComplex() && tt.IsComplex() { - var op ssa.Op - if ft.Size() == tt.Size() { - switch ft.Size() { - case 8: - op = ssa.OpRound32F - case 16: - op = ssa.OpRound64F - default: - s.Fatalf("weird complex conversion %v -> %v", ft, tt) - } - } else if ft.Size() == 8 && tt.Size() == 16 { - op = ssa.OpCvt32Fto64F - } else if ft.Size() == 16 && tt.Size() == 8 { - op = ssa.OpCvt64Fto32F - } else { - s.Fatalf("weird complex conversion %v -> %v", ft, tt) - } - ftp := types.FloatForComplex(ft) - ttp := types.FloatForComplex(tt) - return s.newValue2(ssa.OpComplexMake, tt, - s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)), - s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x))) - } - - s.Fatalf("unhandled OCONV %s -> %s", n.X.Type().Kind(), n.Type().Kind()) - return nil + return s.conv(n, x, n.X.Type(), n.Type()) case ir.ODOTTYPE: n := n.(*ir.TypeAssertExpr) res, _ := s.dottype(n, false) return res + case ir.ODYNAMICDOTTYPE: + n := n.(*ir.DynamicTypeAssertExpr) + res, _ := s.dynamicDottype(n, false) + return res + // binary ops case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT: n := n.(*ir.BinaryExpr) @@ -3021,7 +3126,7 @@ func (s *state) expr(n ir.Node) *ssa.Value { } // If n is addressable and can't be represented in // SSA, then load just the selected field. This - // prevents false memory dependencies in race/msan + // prevents false memory dependencies in race/msan/asan // instrumentation. if ir.IsAddressable(n) && !s.canSSA(n) { p := s.addr(n) @@ -3073,7 +3178,8 @@ func (s *state) expr(n ir.Node) *ssa.Value { z := s.constInt(types.Types[types.TINT], 0) s.boundsCheck(z, z, ssa.BoundsIndex, false) // The return value won't be live, return junk. - return s.newValue0(ssa.OpUnknown, n.Type()) + // But not quite junk, in case bounds checks are turned off. See issue 48092. + return s.zeroVal(n.Type()) } len := s.constInt(types.Types[types.TINT], bound) s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded()) // checks i == 0 @@ -3135,9 +3241,16 @@ func (s *state) expr(n ir.Node) *ssa.Value { c := s.expr(n.Cap) return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c) + case ir.OSTRINGHEADER: + n := n.(*ir.StringHeaderExpr) + p := s.expr(n.Ptr) + l := s.expr(n.Len) + return s.newValue2(ssa.OpStringMake, n.Type(), p, l) + case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR: n := n.(*ir.SliceExpr) - v := s.expr(n.X) + check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr() + v := s.exprCheckPtr(n.X, !check) var i, j, k *ssa.Value if n.Low != nil { i = s.expr(n.Low) @@ -3149,6 +3262,10 @@ func (s *state) expr(n ir.Node) *ssa.Value { k = s.expr(n.Max) } p, l, c := s.slice(v, i, j, k, n.Bounded()) + if check { + // Emit checkptr instrumentation after bound check to prevent false positive, see #46938. + s.checkPtrAlignment(n.X.(*ir.ConvExpr), v, s.conv(n.Max, k, k.Type, types.Types[types.TUINTPTR])) + } return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c) case ir.OSLICESTR: @@ -3183,7 +3300,7 @@ func (s *state) expr(n ir.Node) *ssa.Value { } fallthrough - case ir.OCALLINTER, ir.OCALLMETH: + case ir.OCALLINTER: n := n.(*ir.CallExpr) return s.callResult(n, callNormal) @@ -3191,6 +3308,14 @@ func (s *state) expr(n ir.Node) *ssa.Value { n := n.(*ir.CallExpr) return s.newValue1(ssa.OpGetG, n.Type(), s.mem()) + case ir.OGETCALLERPC: + n := n.(*ir.CallExpr) + return s.newValue0(ssa.OpGetCallerPC, n.Type()) + + case ir.OGETCALLERSP: + n := n.(*ir.CallExpr) + return s.newValue0(ssa.OpGetCallerSP, n.Type()) + case ir.OAPPEND: return s.append(n.(*ir.CallExpr), false) @@ -3206,12 +3331,21 @@ func (s *state) expr(n ir.Node) *ssa.Value { case ir.ONEW: n := n.(*ir.UnaryExpr) - return s.newObject(n.Type().Elem()) + var rtype *ssa.Value + if x, ok := n.X.(*ir.DynamicType); ok && x.Op() == ir.ODYNAMICTYPE { + rtype = s.expr(x.RType) + } + return s.newObject(n.Type().Elem(), rtype) case ir.OUNSAFEADD: n := n.(*ir.BinaryExpr) ptr := s.expr(n.X) len := s.expr(n.Y) + + // Force len to uintptr to prevent misuse of garbage bits in the + // upper part of the register (#48536). + len = s.conv(n, len, len.Type, types.Types[types.TUINTPTR]) + return s.newValue2(ssa.OpAddPtr, n.Type(), ptr, len) default: @@ -3250,46 +3384,46 @@ func (s *state) resultAddrOfCall(c *ssa.Value, which int64, t *types.Type) *ssa. // If inplace is true, it writes the result of the OAPPEND expression n // back to the slice being appended to, and returns nil. // inplace MUST be set to false if the slice can be SSA'd. +// Note: this code only handles fixed-count appends. Dotdotdot appends +// have already been rewritten at this point (by walk). func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { // If inplace is false, process as expression "append(s, e1, e2, e3)": // // ptr, len, cap := s - // newlen := len + 3 - // if newlen > cap { - // ptr, len, cap = growslice(s, newlen) - // newlen = len + 3 // recalculate to avoid a spill + // len += 3 + // if uint(len) > uint(cap) { + // ptr, len, cap = growslice(ptr, len, cap, 3, typ) + // Note that len is unmodified by growslice. // } // // with write barriers, if needed: - // *(ptr+len) = e1 - // *(ptr+len+1) = e2 - // *(ptr+len+2) = e3 - // return makeslice(ptr, newlen, cap) + // *(ptr+(len-3)) = e1 + // *(ptr+(len-2)) = e2 + // *(ptr+(len-1)) = e3 + // return makeslice(ptr, len, cap) // // // If inplace is true, process as statement "s = append(s, e1, e2, e3)": // // a := &s // ptr, len, cap := s - // newlen := len + 3 - // if uint(newlen) > uint(cap) { - // newptr, len, newcap = growslice(ptr, len, cap, newlen) - // vardef(a) // if necessary, advise liveness we are writing a new a - // *a.cap = newcap // write before ptr to avoid a spill - // *a.ptr = newptr // with write barrier + // len += 3 + // if uint(len) > uint(cap) { + // ptr, len, cap = growslice(ptr, len, cap, 3, typ) + // vardef(a) // if necessary, advise liveness we are writing a new a + // *a.cap = cap // write before ptr to avoid a spill + // *a.ptr = ptr // with write barrier // } - // newlen = len + 3 // recalculate to avoid a spill - // *a.len = newlen + // *a.len = len // // with write barriers, if needed: - // *(ptr+len) = e1 - // *(ptr+len+1) = e2 - // *(ptr+len+2) = e3 + // *(ptr+(len-3)) = e1 + // *(ptr+(len-2)) = e2 + // *(ptr+(len-1)) = e3 et := n.Type().Elem() pt := types.NewPtr(et) // Evaluate slice sn := n.Args[0] // the slice node is the first in the list - var slice, addr *ssa.Value if inplace { addr = s.addr(sn) @@ -3302,21 +3436,23 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { grow := s.f.NewBlock(ssa.BlockPlain) assign := s.f.NewBlock(ssa.BlockPlain) - // Decide if we need to grow - nargs := int64(len(n.Args) - 1) + // Decomposse input slice. p := s.newValue1(ssa.OpSlicePtr, pt, slice) l := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], slice) c := s.newValue1(ssa.OpSliceCap, types.Types[types.TINT], slice) - nl := s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], l, s.constInt(types.Types[types.TINT], nargs)) - cmp := s.newValue2(s.ssaOp(ir.OLT, types.Types[types.TUINT]), types.Types[types.TBOOL], c, nl) - s.vars[ptrVar] = p + // Add number of new elements to length. + nargs := s.constInt(types.Types[types.TINT], int64(len(n.Args)-1)) + l = s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], l, nargs) + + // Decide if we need to grow + cmp := s.newValue2(s.ssaOp(ir.OLT, types.Types[types.TUINT]), types.Types[types.TBOOL], c, l) + // Record values of ptr/len/cap before branch. + s.vars[ptrVar] = p + s.vars[lenVar] = l if !inplace { - s.vars[newlenVar] = nl s.vars[capVar] = c - } else { - s.vars[lenVar] = l } b := s.endBlock() @@ -3329,8 +3465,16 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { // Call growslice s.startBlock(grow) taddr := s.expr(n.X) - r := s.rtcall(ir.Syms.Growslice, true, []*types.Type{pt, types.Types[types.TINT], types.Types[types.TINT]}, taddr, p, l, c, nl) + r := s.rtcall(ir.Syms.Growslice, true, []*types.Type{n.Type()}, p, l, c, nargs, taddr) + + // Decompose output slice + p = s.newValue1(ssa.OpSlicePtr, pt, r[0]) + l = s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], r[0]) + c = s.newValue1(ssa.OpSliceCap, types.Types[types.TINT], r[0]) + s.vars[ptrVar] = p + s.vars[lenVar] = l + s.vars[capVar] = c if inplace { if sn.Op() == ir.ONAME { sn := sn.(*ir.Name) @@ -3340,15 +3484,8 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { } } capaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, types.SliceCapOffset, addr) - s.store(types.Types[types.TINT], capaddr, r[2]) - s.store(pt, addr, r[0]) - // load the value we just stored to avoid having to spill it - s.vars[ptrVar] = s.load(pt, addr) - s.vars[lenVar] = r[1] // avoid a spill in the fast path - } else { - s.vars[ptrVar] = r[0] - s.vars[newlenVar] = s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], r[1], s.constInt(types.Types[types.TINT], nargs)) - s.vars[capVar] = r[2] + s.store(types.Types[types.TINT], capaddr, c) + s.store(pt, addr, p) } b = s.endBlock() @@ -3356,12 +3493,17 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { // assign new elements to slots s.startBlock(assign) + p = s.variable(ptrVar, pt) // generates phi for ptr + l = s.variable(lenVar, types.Types[types.TINT]) // generates phi for len + if !inplace { + c = s.variable(capVar, types.Types[types.TINT]) // generates phi for cap + } if inplace { - l = s.variable(lenVar, types.Types[types.TINT]) // generates phi for len - nl = s.newValue2(s.ssaOp(ir.OADD, types.Types[types.TINT]), types.Types[types.TINT], l, s.constInt(types.Types[types.TINT], nargs)) + // Update length in place. + // We have to wait until here to make sure growslice succeeded. lenaddr := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.IntPtr, types.SliceLenOffset, addr) - s.store(types.Types[types.TINT], lenaddr, nl) + s.store(types.Types[types.TINT], lenaddr, l) } // Evaluate args @@ -3371,7 +3513,7 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { v *ssa.Value store bool } - args := make([]argRec, 0, nargs) + args := make([]argRec, 0, len(n.Args[1:])) for _, n := range n.Args[1:] { if TypeOK(n.Type()) { args = append(args, argRec{v: s.expr(n), store: true}) @@ -3381,12 +3523,9 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { } } - p = s.variable(ptrVar, pt) // generates phi for ptr - if !inplace { - nl = s.variable(newlenVar, types.Types[types.TINT]) // generates phi for nl - c = s.variable(capVar, types.Types[types.TINT]) // generates phi for cap - } - p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l) + // Write args into slice. + oldLen := s.newValue2(s.ssaOp(ir.OSUB, types.Types[types.TINT]), types.Types[types.TINT], l, nargs) + p2 := s.newValue2(ssa.OpPtrIndex, pt, p, oldLen) for i, arg := range args { addr := s.newValue2(ssa.OpPtrIndex, pt, p2, s.constInt(types.Types[types.TINT], int64(i))) if arg.store { @@ -3397,14 +3536,16 @@ func (s *state) append(n *ir.CallExpr, inplace bool) *ssa.Value { } delete(s.vars, ptrVar) + delete(s.vars, lenVar) + if !inplace { + delete(s.vars, capVar) + } + + // make result if inplace { - delete(s.vars, lenVar) return nil } - delete(s.vars, newlenVar) - delete(s.vars, capVar) - // make result - return s.newValue3(ssa.OpSliceMake, n.Type(), p, nl, c) + return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c) } // condBranch evaluates the boolean expression cond and branches to yes @@ -3471,7 +3612,11 @@ const ( // If deref is true, then we do left = *right instead (and right has already been nil-checked). // If deref is true and right == nil, just do left = 0. // skip indicates assignments (at the top level) that can be avoided. +// mayOverlap indicates whether left&right might partially overlap in memory. Default is false. func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask) { + s.assignWhichMayOverlap(left, right, deref, skip, false) +} +func (s *state) assignWhichMayOverlap(left ir.Node, right *ssa.Value, deref bool, skip skipMask, mayOverlap bool) { if left.Op() == ir.ONAME && ir.IsBlank(left) { return } @@ -3553,7 +3698,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask // If this assignment clobbers an entire local variable, then emit // OpVarDef so liveness analysis knows the variable is redefined. - if base, ok := clobberBase(left).(*ir.Name); ok && base.OnStack() && skip == 0 { + if base, ok := clobberBase(left).(*ir.Name); ok && base.OnStack() && skip == 0 && t.HasPointers() { s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, base, s.mem(), !ir.IsAutoTmp(base)) } @@ -3572,7 +3717,7 @@ func (s *state) assign(left ir.Node, right *ssa.Value, deref bool, skip skipMask if right == nil { s.zero(t, addr) } else { - s.move(t, addr, right) + s.moveWhichMayOverlap(t, addr, right, mayOverlap) } return } @@ -3653,6 +3798,7 @@ const ( callDefer callDeferStack callGo + callTail ) type sfRtCallDef struct { @@ -3665,44 +3811,54 @@ var softFloatOps map[ssa.Op]sfRtCallDef func softfloatInit() { // Some of these operations get transformed by sfcall. softFloatOps = map[ssa.Op]sfRtCallDef{ - ssa.OpAdd32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32}, - ssa.OpAdd64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64}, - ssa.OpSub32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32}, - ssa.OpSub64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64}, - ssa.OpMul32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fmul32"), types.TFLOAT32}, - ssa.OpMul64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fmul64"), types.TFLOAT64}, - ssa.OpDiv32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fdiv32"), types.TFLOAT32}, - ssa.OpDiv64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fdiv64"), types.TFLOAT64}, - - ssa.OpEq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq64"), types.TBOOL}, - ssa.OpEq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq32"), types.TBOOL}, - ssa.OpNeq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq64"), types.TBOOL}, - ssa.OpNeq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("feq32"), types.TBOOL}, - ssa.OpLess64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fgt64"), types.TBOOL}, - ssa.OpLess32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fgt32"), types.TBOOL}, - ssa.OpLeq64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fge64"), types.TBOOL}, - ssa.OpLeq32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fge32"), types.TBOOL}, - - ssa.OpCvt32to32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint32to32"), types.TFLOAT32}, - ssa.OpCvt32Fto32: sfRtCallDef{typecheck.LookupRuntimeFunc("f32toint32"), types.TINT32}, - ssa.OpCvt64to32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint64to32"), types.TFLOAT32}, - ssa.OpCvt32Fto64: sfRtCallDef{typecheck.LookupRuntimeFunc("f32toint64"), types.TINT64}, - ssa.OpCvt64Uto32F: sfRtCallDef{typecheck.LookupRuntimeFunc("fuint64to32"), types.TFLOAT32}, - ssa.OpCvt32Fto64U: sfRtCallDef{typecheck.LookupRuntimeFunc("f32touint64"), types.TUINT64}, - ssa.OpCvt32to64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint32to64"), types.TFLOAT64}, - ssa.OpCvt64Fto32: sfRtCallDef{typecheck.LookupRuntimeFunc("f64toint32"), types.TINT32}, - ssa.OpCvt64to64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fint64to64"), types.TFLOAT64}, - ssa.OpCvt64Fto64: sfRtCallDef{typecheck.LookupRuntimeFunc("f64toint64"), types.TINT64}, - ssa.OpCvt64Uto64F: sfRtCallDef{typecheck.LookupRuntimeFunc("fuint64to64"), types.TFLOAT64}, - ssa.OpCvt64Fto64U: sfRtCallDef{typecheck.LookupRuntimeFunc("f64touint64"), types.TUINT64}, - ssa.OpCvt32Fto64F: sfRtCallDef{typecheck.LookupRuntimeFunc("f32to64"), types.TFLOAT64}, - ssa.OpCvt64Fto32F: sfRtCallDef{typecheck.LookupRuntimeFunc("f64to32"), types.TFLOAT32}, + ssa.OpAdd32F: {typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32}, + ssa.OpAdd64F: {typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64}, + ssa.OpSub32F: {typecheck.LookupRuntimeFunc("fadd32"), types.TFLOAT32}, + ssa.OpSub64F: {typecheck.LookupRuntimeFunc("fadd64"), types.TFLOAT64}, + ssa.OpMul32F: {typecheck.LookupRuntimeFunc("fmul32"), types.TFLOAT32}, + ssa.OpMul64F: {typecheck.LookupRuntimeFunc("fmul64"), types.TFLOAT64}, + ssa.OpDiv32F: {typecheck.LookupRuntimeFunc("fdiv32"), types.TFLOAT32}, + ssa.OpDiv64F: {typecheck.LookupRuntimeFunc("fdiv64"), types.TFLOAT64}, + + ssa.OpEq64F: {typecheck.LookupRuntimeFunc("feq64"), types.TBOOL}, + ssa.OpEq32F: {typecheck.LookupRuntimeFunc("feq32"), types.TBOOL}, + ssa.OpNeq64F: {typecheck.LookupRuntimeFunc("feq64"), types.TBOOL}, + ssa.OpNeq32F: {typecheck.LookupRuntimeFunc("feq32"), types.TBOOL}, + ssa.OpLess64F: {typecheck.LookupRuntimeFunc("fgt64"), types.TBOOL}, + ssa.OpLess32F: {typecheck.LookupRuntimeFunc("fgt32"), types.TBOOL}, + ssa.OpLeq64F: {typecheck.LookupRuntimeFunc("fge64"), types.TBOOL}, + ssa.OpLeq32F: {typecheck.LookupRuntimeFunc("fge32"), types.TBOOL}, + + ssa.OpCvt32to32F: {typecheck.LookupRuntimeFunc("fint32to32"), types.TFLOAT32}, + ssa.OpCvt32Fto32: {typecheck.LookupRuntimeFunc("f32toint32"), types.TINT32}, + ssa.OpCvt64to32F: {typecheck.LookupRuntimeFunc("fint64to32"), types.TFLOAT32}, + ssa.OpCvt32Fto64: {typecheck.LookupRuntimeFunc("f32toint64"), types.TINT64}, + ssa.OpCvt64Uto32F: {typecheck.LookupRuntimeFunc("fuint64to32"), types.TFLOAT32}, + ssa.OpCvt32Fto64U: {typecheck.LookupRuntimeFunc("f32touint64"), types.TUINT64}, + ssa.OpCvt32to64F: {typecheck.LookupRuntimeFunc("fint32to64"), types.TFLOAT64}, + ssa.OpCvt64Fto32: {typecheck.LookupRuntimeFunc("f64toint32"), types.TINT32}, + ssa.OpCvt64to64F: {typecheck.LookupRuntimeFunc("fint64to64"), types.TFLOAT64}, + ssa.OpCvt64Fto64: {typecheck.LookupRuntimeFunc("f64toint64"), types.TINT64}, + ssa.OpCvt64Uto64F: {typecheck.LookupRuntimeFunc("fuint64to64"), types.TFLOAT64}, + ssa.OpCvt64Fto64U: {typecheck.LookupRuntimeFunc("f64touint64"), types.TUINT64}, + ssa.OpCvt32Fto64F: {typecheck.LookupRuntimeFunc("f32to64"), types.TFLOAT64}, + ssa.OpCvt64Fto32F: {typecheck.LookupRuntimeFunc("f64to32"), types.TFLOAT32}, } } // TODO: do not emit sfcall if operation can be optimized to constant in later // opt phase func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) { + f2i := func(t *types.Type) *types.Type { + switch t.Kind() { + case types.TFLOAT32: + return types.Types[types.TUINT32] + case types.TFLOAT64: + return types.Types[types.TUINT64] + } + return t + } + if callDef, ok := softFloatOps[op]; ok { switch op { case ssa.OpLess32F, @@ -3715,7 +3871,19 @@ func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) { args[1] = s.newValue1(s.ssaOp(ir.ONEG, types.Types[callDef.rtype]), args[1].Type, args[1]) } - result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0] + // runtime functions take uints for floats and returns uints. + // Convert to uints so we use the right calling convention. + for i, a := range args { + if a.Type.IsFloat() { + args[i] = s.newValue1(ssa.OpCopy, f2i(a.Type), a) + } + } + + rt := types.Types[callDef.rtype] + result := s.rtcall(callDef.rtfn, true, []*types.Type{f2i(rt)}, args...)[0] + if rt.IsFloat() { + result = s.newValue1(ssa.OpCopy, rt, result) + } if op == ssa.OpNeq32F || op == ssa.OpNeq64F { result = s.newValue1(ssa.OpNot, result.Type, result) } @@ -3808,7 +3976,8 @@ func InitTables() { } return s.newValue2(ssa.OpMul64uover, types.NewTuple(types.Types[types.TUINT], types.Types[types.TUINT]), args[0], args[1]) }, - sys.AMD64, sys.I386, sys.MIPS64) + sys.AMD64, sys.I386, sys.Loong64, sys.MIPS64, sys.RISCV64, sys.ARM64) + alias("runtime", "mulUintptr", "runtime/internal/math", "MulUintptr", all...) add("runtime", "KeepAlive", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { data := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, args[0]) @@ -3834,6 +4003,13 @@ func InitTables() { }, all...) + addF("runtime", "publicationBarrier", + func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + s.vars[memVar] = s.newValue1(ssa.OpPubBarrier, types.TypeMem, s.mem()) + return nil + }, + sys.ARM64, sys.PPC64) + /******** runtime/internal/sys ********/ addF("runtime/internal/sys", "Ctz32", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { @@ -3856,6 +4032,21 @@ func InitTables() { }, sys.AMD64, sys.ARM64, sys.ARM, sys.S390X) + /****** Prefetch ******/ + makePrefetchFunc := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + s.vars[memVar] = s.newValue2(op, types.TypeMem, args[0], s.mem()) + return nil + } + } + + // Make Prefetch intrinsics for supported platforms + // On the unsupported platforms stub function will be eliminated + addF("runtime/internal/sys", "Prefetch", makePrefetchFunc(ssa.OpPrefetchCache), + sys.AMD64, sys.ARM64, sys.PPC64) + addF("runtime/internal/sys", "PrefetchStreamed", makePrefetchFunc(ssa.OpPrefetchCacheStreamed), + sys.AMD64, sys.ARM64, sys.PPC64) + /******** runtime/internal/atomic ********/ addF("runtime/internal/atomic", "Load", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { @@ -3863,21 +4054,21 @@ func InitTables() { s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v) }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Load8", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue2(ssa.OpAtomicLoad8, types.NewTuple(types.Types[types.TUINT8], types.TypeMem), args[0], s.mem()) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT8], v) }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Load64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue2(ssa.OpAtomicLoad64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], s.mem()) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v) }, - sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "LoadAcq", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue2(ssa.OpAtomicLoadAcq32, types.NewTuple(types.Types[types.TUINT32], types.TypeMem), args[0], s.mem()) @@ -3898,32 +4089,32 @@ func InitTables() { s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, s.f.Config.Types.BytePtr, v) }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Store", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { s.vars[memVar] = s.newValue3(ssa.OpAtomicStore32, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Store8", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { s.vars[memVar] = s.newValue3(ssa.OpAtomicStore8, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Store64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { s.vars[memVar] = s.newValue3(ssa.OpAtomicStore64, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "StorepNoWB", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { s.vars[memVar] = s.newValue3(ssa.OpAtomicStorePtrNoWB, types.TypeMem, args[0], args[1], s.mem()) return nil }, - sys.AMD64, sys.ARM64, sys.MIPS, sys.MIPS64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "StoreRel", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { s.vars[memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem()) @@ -3943,14 +4134,14 @@ func InitTables() { s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v) }, - sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Xchg64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue3(ssa.OpAtomicExchange64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], args[1], s.mem()) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v) }, - sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) type atomicOpEmitter func(s *state, n *ir.CallExpr, args []*ssa.Value, op ssa.Op, typ types.Kind) @@ -4008,14 +4199,14 @@ func InitTables() { s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT32], v) }, - sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Xadd64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue3(ssa.OpAtomicAdd64, types.NewTuple(types.Types[types.TUINT64], types.TypeMem), args[0], args[1], s.mem()) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TUINT64], v) }, - sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Xadd", makeAtomicGuardedIntrinsicARM64(ssa.OpAtomicAdd32, ssa.OpAtomicAdd32Variant, types.TUINT32, types.TUINT32, atomicXchgXaddEmitterARM64), @@ -4030,14 +4221,14 @@ func InitTables() { s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TBOOL], v) }, - sys.AMD64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "Cas64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue4(ssa.OpAtomicCompareAndSwap64, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) s.vars[memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) return s.newValue1(ssa.OpSelect0, types.Types[types.TBOOL], v) }, - sys.AMD64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) + sys.AMD64, sys.Loong64, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X) addF("runtime/internal/atomic", "CasRel", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[types.TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem()) @@ -4149,11 +4340,11 @@ func InitTables() { alias("runtime/internal/atomic", "CasRel", "runtime/internal/atomic", "Cas", lwatomics...) /******** math ********/ - addF("math", "Sqrt", + addF("math", "sqrt", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue1(ssa.OpSqrt, types.Types[types.TFLOAT64], args[0]) }, - sys.I386, sys.AMD64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm) + sys.I386, sys.AMD64, sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm) addF("math", "Trunc", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue1(ssa.OpTrunc, types.Types[types.TFLOAT64], args[0]) @@ -4183,23 +4374,28 @@ func InitTables() { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue1(ssa.OpAbs, types.Types[types.TFLOAT64], args[0]) }, - sys.ARM64, sys.ARM, sys.PPC64, sys.Wasm) + sys.ARM64, sys.ARM, sys.PPC64, sys.RISCV64, sys.Wasm) addF("math", "Copysign", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue2(ssa.OpCopysign, types.Types[types.TFLOAT64], args[0], args[1]) }, - sys.PPC64, sys.Wasm) + sys.PPC64, sys.RISCV64, sys.Wasm) addF("math", "FMA", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2]) }, - sys.ARM64, sys.PPC64, sys.S390X) + sys.ARM64, sys.PPC64, sys.RISCV64, sys.S390X) addF("math", "FMA", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { if !s.config.UseFMA { s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64] return s.variable(n, types.Types[types.TFLOAT64]) } + + if buildcfg.GOAMD64 >= 3 { + return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2]) + } + v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasFMA) b := s.endBlock() b.Kind = ssa.BlockIf @@ -4262,6 +4458,10 @@ func InitTables() { makeRoundAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + if buildcfg.GOAMD64 >= 2 { + return s.newValue1(op, types.Types[types.TFLOAT64], args[0]) + } + v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasSSE41) b := s.endBlock() b.Kind = ssa.BlockIf @@ -4367,7 +4567,7 @@ func InitTables() { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0]) }, - sys.AMD64, sys.ARM64) + sys.AMD64, sys.ARM64, sys.PPC64) addF("math/bits", "Len32", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { if s.config.PtrSize == 4 { @@ -4376,7 +4576,7 @@ func InitTables() { x := s.newValue1(ssa.OpZeroExt32to64, types.Types[types.TUINT64], args[0]) return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], x) }, - sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm) + sys.ARM, sys.S390X, sys.MIPS, sys.Wasm) addF("math/bits", "Len16", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { if s.config.PtrSize == 4 { @@ -4438,9 +4638,6 @@ func InitTables() { sys.ARM64) addF("math/bits", "Reverse", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { - if s.config.PtrSize == 4 { - return s.newValue1(ssa.OpBitRev32, types.Types[types.TINT], args[0]) - } return s.newValue1(ssa.OpBitRev64, types.Types[types.TINT], args[0]) }, sys.ARM64) @@ -4458,16 +4655,20 @@ func InitTables() { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue2(ssa.OpRotateLeft32, types.Types[types.TUINT32], args[0], args[1]) }, - sys.AMD64, sys.ARM, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm) + sys.AMD64, sys.ARM, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm, sys.Loong64) addF("math/bits", "RotateLeft64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue2(ssa.OpRotateLeft64, types.Types[types.TUINT64], args[0], args[1]) }, - sys.AMD64, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm) + sys.AMD64, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm, sys.Loong64) alias("math/bits", "RotateLeft", "math/bits", "RotateLeft64", p8...) - makeOnesCountAMD64 := func(op64 ssa.Op, op32 ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + makeOnesCountAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { + if buildcfg.GOAMD64 >= 2 { + return s.newValue1(op, types.Types[types.TINT], args[0]) + } + v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasPOPCNT) b := s.endBlock() b.Kind = ssa.BlockIf @@ -4481,10 +4682,6 @@ func InitTables() { // We have the intrinsic - use it directly. s.startBlock(bTrue) - op := op64 - if s.config.PtrSize == 4 { - op = op32 - } s.vars[n] = s.newValue1(op, types.Types[types.TINT], args[0]) s.endBlock().AddEdgeTo(bEnd) @@ -4499,7 +4696,7 @@ func InitTables() { } } addF("math/bits", "OnesCount64", - makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount64), + makeOnesCountAMD64(ssa.OpPopCount64), sys.AMD64) addF("math/bits", "OnesCount64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { @@ -4507,7 +4704,7 @@ func InitTables() { }, sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm) addF("math/bits", "OnesCount32", - makeOnesCountAMD64(ssa.OpPopCount32, ssa.OpPopCount32), + makeOnesCountAMD64(ssa.OpPopCount32), sys.AMD64) addF("math/bits", "OnesCount32", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { @@ -4515,7 +4712,7 @@ func InitTables() { }, sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm) addF("math/bits", "OnesCount16", - makeOnesCountAMD64(ssa.OpPopCount16, ssa.OpPopCount16), + makeOnesCountAMD64(ssa.OpPopCount16), sys.AMD64) addF("math/bits", "OnesCount16", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { @@ -4528,27 +4725,27 @@ func InitTables() { }, sys.S390X, sys.PPC64, sys.Wasm) addF("math/bits", "OnesCount", - makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount32), + makeOnesCountAMD64(ssa.OpPopCount64), sys.AMD64) addF("math/bits", "Mul64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1]) }, - sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.MIPS64) - alias("math/bits", "Mul", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE) - alias("runtime/internal/math", "Mul64", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE) + sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.MIPS64, sys.RISCV64, sys.Loong64) + alias("math/bits", "Mul", "math/bits", "Mul64", p8...) + alias("runtime/internal/math", "Mul64", "math/bits", "Mul64", p8...) addF("math/bits", "Add64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue3(ssa.OpAdd64carry, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2]) }, - sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X) - alias("math/bits", "Add", "math/bits", "Add64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X) + sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.RISCV64) + alias("math/bits", "Add", "math/bits", "Add64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchRISCV64) addF("math/bits", "Sub64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue3(ssa.OpSub64borrow, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2]) }, - sys.AMD64, sys.ARM64, sys.S390X) - alias("math/bits", "Sub", "math/bits", "Sub64", sys.ArchAMD64, sys.ArchARM64, sys.ArchS390X) + sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.RISCV64) + alias("math/bits", "Sub", "math/bits", "Sub64", sys.ArchAMD64, sys.ArchARM64, sys.ArchS390X, sys.ArchRISCV64) addF("math/bits", "Div64", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { // check for divide-by-zero/overflow and panic with appropriate message @@ -4609,11 +4806,7 @@ func InitTables() { alias("sync/atomic", "AddUintptr", "runtime/internal/atomic", "Xadd64", p8...) /******** math/big ********/ - add("math/big", "mulWW", - func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { - return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1]) - }, - sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64, sys.ArchS390X) + alias("math/big", "mulWW", "math/bits", "Mul64", p8...) } // findIntrinsic returns a function which builds the SSA equivalent of the @@ -4623,9 +4816,6 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder { return nil } pkg := sym.Pkg.Path - if sym.Pkg == types.LocalPkg { - pkg = base.Ctxt.Pkgpath - } if sym.Pkg == ir.Pkgs.Runtime { pkg = "runtime" } @@ -4687,17 +4877,14 @@ func (s *state) intrinsicArgs(n *ir.CallExpr) []*ssa.Value { return args } -// openDeferRecord adds code to evaluate and store the args for an open-code defer +// openDeferRecord adds code to evaluate and store the function for an open-code defer // call, and records info about the defer, so we can generate proper code on the // exit paths. n is the sub-node of the defer node that is the actual function -// call. We will also record funcdata information on where the args are stored +// call. We will also record funcdata information on where the function is stored // (as well as the deferBits variable), and this will enable us to run the proper // defer calls during panics. func (s *state) openDeferRecord(n *ir.CallExpr) { - var args []*ssa.Value - var argNodes []*ir.Name - - if buildcfg.Experiment.RegabiDefer && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { + if len(n.Args) != 0 || n.Op() != ir.OCALLFUNC || n.X.Type().NumResults() != 0 { s.Fatalf("defer call with arguments or results: %v", n) } @@ -4705,48 +4892,20 @@ func (s *state) openDeferRecord(n *ir.CallExpr) { n: n, } fn := n.X - if n.Op() == ir.OCALLFUNC { - // We must always store the function value in a stack slot for the - // runtime panic code to use. But in the defer exit code, we will - // call the function directly if it is a static function. - closureVal := s.expr(fn) - closure := s.openDeferSave(nil, fn.Type(), closureVal) - opendefer.closureNode = closure.Aux.(*ir.Name) - if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) { - opendefer.closure = closure - } - } else if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } else { - if fn.Op() != ir.ODOTINTER { - base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op()) - } - fn := fn.(*ir.SelectorExpr) - closure, rcvr := s.getClosureAndRcvr(fn) - opendefer.closure = s.openDeferSave(nil, closure.Type, closure) - // Important to get the receiver type correct, so it is recognized - // as a pointer for GC purposes. - opendefer.rcvr = s.openDeferSave(nil, fn.Type().Recv().Type, rcvr) - opendefer.closureNode = opendefer.closure.Aux.(*ir.Name) - opendefer.rcvrNode = opendefer.rcvr.Aux.(*ir.Name) - } - for _, argn := range n.Args { - var v *ssa.Value - if TypeOK(argn.Type()) { - v = s.openDeferSave(nil, argn.Type(), s.expr(argn)) - } else { - v = s.openDeferSave(argn, argn.Type(), nil) - } - args = append(args, v) - argNodes = append(argNodes, v.Aux.(*ir.Name)) + // We must always store the function value in a stack slot for the + // runtime panic code to use. But in the defer exit code, we will + // call the function directly if it is a static function. + closureVal := s.expr(fn) + closure := s.openDeferSave(fn.Type(), closureVal) + opendefer.closureNode = closure.Aux.(*ir.Name) + if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) { + opendefer.closure = closure } - opendefer.argVals = args - opendefer.argNodes = argNodes index := len(s.openDefers) s.openDefers = append(s.openDefers, opendefer) // Update deferBits only after evaluation and storage to stack of - // args/receiver/interface is successful. + // the function is successful. bitvalue := s.constInt8(types.Types[types.TUINT8], 1< int64(4*types.PtrSize) { + if t.Size() > int64(4*types.PtrSize) { // 4*Widthptr is an arbitrary constant. We want it // to be at least 3*Widthptr so slices can be registerized. // Too big and we'll introduce too much register pressure. @@ -5584,26 +5670,19 @@ func (s *state) intDivide(n ir.Node, a, b *ssa.Value) *ssa.Value { func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value { s.prevCall = nil // Write args to the stack - off := base.Ctxt.FixedFrameSize() + off := base.Ctxt.Arch.FixedFrameSize var callArgs []*ssa.Value var callArgTypes []*types.Type for _, arg := range args { t := arg.Type - off = types.Rnd(off, t.Alignment()) + off = types.RoundUp(off, t.Alignment()) size := t.Size() callArgs = append(callArgs, arg) callArgTypes = append(callArgTypes, t) off += size } - off = types.Rnd(off, int64(types.RegSize)) - - // Accumulate results types and offsets - offR := off - for _, t := range results { - offR = types.Rnd(offR, t.Alignment()) - offR += t.Size() - } + off = types.RoundUp(off, int64(types.RegSize)) // Issue call var call *ssa.Value @@ -5618,7 +5697,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . b := s.endBlock() b.Kind = ssa.BlockExit b.SetControl(call) - call.AuxInt = off - base.Ctxt.FixedFrameSize() + call.AuxInt = off - base.Ctxt.Arch.FixedFrameSize if len(results) > 0 { s.Fatalf("panic call can't have results") } @@ -5628,11 +5707,11 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args . // Load results res := make([]*ssa.Value, len(results)) for i, t := range results { - off = types.Rnd(off, t.Alignment()) + off = types.RoundUp(off, t.Alignment()) res[i] = s.resultOfCall(call, int64(i), t) off += t.Size() } - off = types.Rnd(off, int64(types.PtrSize)) + off = types.RoundUp(off, int64(types.PtrSize)) // Remember how much callee stack space we needed. call.AuxInt = off @@ -5881,7 +5960,7 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value) // // Where mask(x) is 0 if x==0 and -1 if x>0 and stride is the width // of the element type. - stride := s.constInt(types.Types[types.TINT], ptr.Type.Elem().Width) + stride := s.constInt(types.Types[types.TINT], ptr.Type.Elem().Size()) // The delta is the number of bytes to offset ptr by. delta := s.newValue2(mulOp, types.Types[types.TINT], i, stride) @@ -5936,7 +6015,6 @@ func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n ir.Node, x *ssa.Value, ft, // } else { // y = uintX(x) ; y = x & 1 // z = uintX(x) ; z = z >> 1 - // z = z >> 1 // z = z | y // result = floatY(z) // result = result + result @@ -6089,7 +6167,7 @@ func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value { s.vars[n] = s.load(lenType, x) case ir.OCAP: // capacity is stored in the second word for chan - sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Width, x) + sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Size(), x) s.vars[n] = s.load(lenType, sw) default: s.Fatalf("op must be OLEN or OCAP") @@ -6210,14 +6288,45 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n ir.Node, x *ssa.Value, ft, tt * func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Value) { iface := s.expr(n.X) // input interface target := s.reflectType(n.Type()) // target type - byteptr := s.f.Config.Types.BytePtr + var targetItab *ssa.Value + if n.ITab != nil { + targetItab = s.expr(n.ITab) + } + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, nil, target, targetItab, commaok) +} + +func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, resok *ssa.Value) { + iface := s.expr(n.X) + var source, target, targetItab *ssa.Value + if n.SrcRType != nil { + source = s.expr(n.SrcRType) + } + if !n.X.Type().IsEmptyInterface() && !n.Type().IsInterface() { + byteptr := s.f.Config.Types.BytePtr + targetItab = s.expr(n.ITab) + // TODO(mdempsky): Investigate whether compiling n.RType could be + // better than loading itab.typ. + target = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), targetItab)) // itab.typ + } else { + target = s.expr(n.RType) + } + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, source, target, targetItab, commaok) +} - if n.Type().IsInterface() { - if n.Type().IsEmptyInterface() { +// dottype1 implements a x.(T) operation. iface is the argument (x), dst is the type we're asserting to (T) +// and src is the type we're asserting from. +// source is the *runtime._type of src +// target is the *runtime._type of dst. +// If src is a nonempty interface and dst is not an interface, targetItab is an itab representing (dst, src). Otherwise it is nil. +// commaok is true if the caller wants a boolean success value. Otherwise, the generated code panics if the conversion fails. +func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, source, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) { + byteptr := s.f.Config.Types.BytePtr + if dst.IsInterface() { + if dst.IsEmptyInterface() { // Converting to an empty interface. // Input could be an empty or nonempty interface. if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } // Get itab/type field from input. @@ -6225,7 +6334,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // Conversion succeeds iff that field is not nil. cond := s.newValue2(ssa.OpNeqPtr, types.Types[types.TBOOL], itab, s.constNil(byteptr)) - if n.X.Type().IsEmptyInterface() && commaok { + if src.IsEmptyInterface() && commaok { // Converting empty interface to empty interface with ,ok is just a nil check. return iface, cond } @@ -6247,7 +6356,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // On success, return (perhaps modified) input interface. s.startBlock(bOk) - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { res = iface // Use input interface unchanged. return } @@ -6255,7 +6364,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab) typ := s.load(byteptr, off) idata := s.newValue1(ssa.OpIData, byteptr, iface) - res = s.newValue2(ssa.OpIMake, n.Type(), typ, idata) + res = s.newValue2(ssa.OpIMake, dst, typ, idata) return } @@ -6277,62 +6386,62 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val bFail.AddEdgeTo(bEnd) s.startBlock(bEnd) idata := s.newValue1(ssa.OpIData, byteptr, iface) - res = s.newValue2(ssa.OpIMake, n.Type(), s.variable(typVar, byteptr), idata) + res = s.newValue2(ssa.OpIMake, dst, s.variable(typVar, byteptr), idata) resok = cond delete(s.vars, typVar) return } // converting to a nonempty interface needs a runtime call. if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion not inlined") + base.WarnfAt(pos, "type assertion not inlined") } if !commaok { fn := ir.Syms.AssertI2I - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { fn = ir.Syms.AssertE2I } data := s.newValue1(ssa.OpIData, types.Types[types.TUNSAFEPTR], iface) tab := s.newValue1(ssa.OpITab, byteptr, iface) tab = s.rtcall(fn, true, []*types.Type{byteptr}, target, tab)[0] - return s.newValue2(ssa.OpIMake, n.Type(), tab, data), nil + return s.newValue2(ssa.OpIMake, dst, tab, data), nil } fn := ir.Syms.AssertI2I2 - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { fn = ir.Syms.AssertE2I2 } - res = s.rtcall(fn, true, []*types.Type{n.Type()}, target, iface)[0] - resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(n.Type())) + res = s.rtcall(fn, true, []*types.Type{dst}, target, iface)[0] + resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(dst)) return } if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } // Converting to a concrete type. - direct := types.IsDirectIface(n.Type()) + direct := types.IsDirectIface(dst) itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } - var targetITab *ssa.Value - if n.X.Type().IsEmptyInterface() { + var wantedFirstWord *ssa.Value + if src.IsEmptyInterface() { // Looking for pointer to target type. - targetITab = target + wantedFirstWord = target } else { // Looking for pointer to itab for target type and source interface. - targetITab = s.expr(n.Itab) + wantedFirstWord = targetItab } var tmp ir.Node // temporary for use with large types var addr *ssa.Value // address of tmp - if commaok && !TypeOK(n.Type()) { + if commaok && !TypeOK(dst) { // unSSAable type, use temporary. // TODO: get rid of some of these temporaries. - tmp, addr = s.temp(n.Pos(), n.Type()) + tmp, addr = s.temp(pos, dst) } - cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, targetITab) + cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, wantedFirstWord) b := s.endBlock() b.Kind = ssa.BlockIf b.SetControl(cond) @@ -6346,8 +6455,11 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val if !commaok { // on failure, panic by calling panicdottype s.startBlock(bFail) - taddr := s.reflectType(n.X.Type()) - if n.X.Type().IsEmptyInterface() { + taddr := source + if taddr == nil { + taddr = s.reflectType(src) + } + if src.IsEmptyInterface() { s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr) } else { s.rtcall(ir.Syms.PanicdottypeI, false, nil, itab, target, taddr) @@ -6356,10 +6468,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // on success, return data from interface s.startBlock(bOk) if direct { - return s.newValue1(ssa.OpIData, n.Type(), iface), nil + return s.newValue1(ssa.OpIData, dst, iface), nil } - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - return s.load(n.Type(), p), nil + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + return s.load(dst, p), nil } // commaok is the more complicated case because we have @@ -6373,14 +6485,14 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val s.startBlock(bOk) if tmp == nil { if direct { - s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type(), iface) + s.vars[valVar] = s.newValue1(ssa.OpIData, dst, iface) } else { - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - s.vars[valVar] = s.load(n.Type(), p) + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + s.vars[valVar] = s.load(dst, p) } } else { - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - s.move(n.Type(), addr, p) + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + s.move(dst, addr, p) } s.vars[okVar] = s.constBool(true) s.endBlock() @@ -6389,9 +6501,9 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // type assertion failed s.startBlock(bFail) if tmp == nil { - s.vars[valVar] = s.zeroVal(n.Type()) + s.vars[valVar] = s.zeroVal(dst) } else { - s.zero(n.Type(), addr) + s.zero(dst, addr) } s.vars[okVar] = s.constBool(false) s.endBlock() @@ -6400,11 +6512,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // merge point s.startBlock(bEnd) if tmp == nil { - res = s.variable(valVar, n.Type()) + res = s.variable(valVar, dst) delete(s.vars, valVar) } else { - res = s.load(n.Type(), addr) - s.vars[memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp.(*ir.Name), s.mem()) + res = s.load(dst, addr) } resok = s.variable(okVar, types.Types[types.TBOOL]) delete(s.vars, okVar) @@ -6414,7 +6525,9 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // temp allocates a temp of type t at position pos func (s *state) temp(pos src.XPos, t *types.Type) (*ir.Name, *ssa.Value) { tmp := typecheck.TempAt(pos, s.curfn, t) - s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp, s.mem()) + if t.HasPointers() { + s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, tmp, s.mem()) + } addr := s.addr(tmp) return tmp, addr } @@ -6487,6 +6600,9 @@ type State struct { // and where they would like to go. Branches []Branch + // JumpTables remembers all the jump tables we've seen. + JumpTables []*ssa.Block + // bstart remembers where each block starts (indexed by block ID) bstart []*obj.Prog @@ -6568,6 +6684,22 @@ func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) { // explicit statement boundaries should appear // in the generated code. if p.IsStmt() != src.PosIsStmt { + if s.pp.Pos.IsStmt() == src.PosIsStmt && s.pp.Pos.SameFileAndLine(p) { + // If s.pp.Pos already has a statement mark, then it was set here (below) for + // the previous value. If an actual instruction had been emitted for that + // value, then the statement mark would have been reset. Since the statement + // mark of s.pp.Pos was not reset, this position (file/line) still needs a + // statement mark on an instruction. If file and line for this value are + // the same as the previous value, then the first instruction for this + // value will work to take the statement mark. Return early to avoid + // resetting the statement mark. + // + // The reset of s.pp.Pos occurs in (*Progs).Prog() -- if it emits + // an instruction, and the instruction's statement mark was set, + // and it is not one of the LosesStmtMark instructions, + // then Prog() resets the statement mark on the (*Progs).Pos. + return + } p = p.WithNotStmt() // Calls use the pos attached to v, but copy the statement mark from State } @@ -6586,6 +6718,7 @@ func emitArgInfo(e *ssafn, f *ssa.Func, pp *objw.Progs) { } x := EmitArgInfo(e.curfn, f.OwnAux.ABIInfo()) + x.Set(obj.AttrContentAddressable, true) e.curfn.LSym.Func().ArgInfo = x // Emit a funcdata pointing at the arg info data. @@ -6599,6 +6732,9 @@ func emitArgInfo(e *ssafn, f *ssa.Func, pp *objw.Progs) { // emit argument info (locations on stack) of f for traceback. func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym { x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI)) + // NOTE: do not set ContentAddressable here. This may be referenced from + // assembly code by name (in this case f is a declaration). + // Instead, set it in emitArgInfo above. PtrSize := int64(types.PtrSize) uintptrTyp := types.Types[types.TUINTPTR] @@ -6713,7 +6849,13 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym { return true } - for _, a := range abiInfo.InParams() { + start := 0 + if strings.Contains(f.LSym.Name, "[") { + // Skip the dictionary argument - it is implicit and the user doesn't need to see it. + start = 1 + } + + for _, a := range abiInfo.InParams()[start:] { if !visitType(a.FrameOffset(abiInfo), a.Type, 0) { break } @@ -6726,6 +6868,34 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym { return x } +// for wrapper, emit info of wrapped function. +func emitWrappedFuncInfo(e *ssafn, pp *objw.Progs) { + if base.Ctxt.Flag_linkshared { + // Relative reference (SymPtrOff) to another shared object doesn't work. + // Unfortunate. + return + } + + wfn := e.curfn.WrappedFunc + if wfn == nil { + return + } + + wsym := wfn.Linksym() + x := base.Ctxt.LookupInit(fmt.Sprintf("%s.wrapinfo", wsym.Name), func(x *obj.LSym) { + objw.SymPtrOff(x, 0, wsym) + x.Set(obj.AttrContentAddressable, true) + }) + e.curfn.LSym.Func().WrapInfo = x + + // Emit a funcdata pointing at the wrap info data. + p := pp.Prog(obj.AFUNCDATA) + p.From.SetConst(objabi.FUNCDATA_WrapInfo) + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = x +} + // genssa appends entries to pp for each instruction in f. func genssa(f *ssa.Func, pp *objw.Progs) { var s State @@ -6735,6 +6905,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { s.livenessMap, s.partLiveArgs = liveness.Compute(e.curfn, f, e.stkptrsize, pp) emitArgInfo(e, f, pp) + argLiveBlockMap, argLiveValueMap := liveness.ArgLiveness(e.curfn, f, pp) openDeferInfo := e.curfn.LSym.Func().OpenCodedDeferInfo if openDeferInfo != nil { @@ -6747,13 +6918,16 @@ func genssa(f *ssa.Func, pp *objw.Progs) { p.To.Sym = openDeferInfo } + emitWrappedFuncInfo(e, pp) + // Remember where each block starts. s.bstart = make([]*obj.Prog, f.NumBlocks()) s.pp = pp var progToValue map[*obj.Prog]*ssa.Value var progToBlock map[*obj.Prog]*ssa.Block var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point. - if f.PrintOrHtmlSSA { + gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name] + if gatherPrintInfo { progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues()) progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks()) f.Logf("genssa %s\n", f.Name) @@ -6791,10 +6965,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) { // Progs that are in the set above and have that source position. var inlMarksByPos map[src.XPos][]*obj.Prog + var argLiveIdx int = -1 // argument liveness info index + // Emit basic blocks for i, b := range f.Blocks { s.bstart[b.ID] = s.pp.Next s.lineRunStart = nil + s.SetPos(s.pp.Pos.WithNotStmt()) // It needs a non-empty Pos, but cannot be a statement boundary (yet). // Attach a "default" liveness info. Normally this will be // overwritten in the Values loop below for each Value. But @@ -6804,6 +6981,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) { // preemptible, unless this function is "all unsafe". s.pp.NextLive = objw.LivenessIndex{StackMapIndex: -1, IsUnsafePoint: liveness.IsUnsafe(f)} + if idx, ok := argLiveBlockMap[b.ID]; ok && idx != argLiveIdx { + argLiveIdx = idx + p := s.pp.Prog(obj.APCDATA) + p.From.SetConst(objabi.PCDATA_ArgLiveIndex) + p.To.SetConst(int64(idx)) + } + // Emit values in block Arch.SSAMarkMoves(&s, b) for _, v := range b.Values { @@ -6826,7 +7010,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { case ssa.OpGetG: // nothing to do when there's a g register, // and checkLower complains if there's not - case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive, ssa.OpVarKill: + case ssa.OpVarDef, ssa.OpVarLive, ssa.OpKeepAlive: // nothing to do; already used by liveness case ssa.OpPhi: CheckLoweredPhi(v) @@ -6860,11 +7044,18 @@ func genssa(f *ssa.Func, pp *objw.Progs) { Arch.SSAGenValue(&s, v) } + if idx, ok := argLiveValueMap[v.ID]; ok && idx != argLiveIdx { + argLiveIdx = idx + p := s.pp.Prog(obj.APCDATA) + p.From.SetConst(objabi.PCDATA_ArgLiveIndex) + p.To.SetConst(int64(idx)) + } + if base.Ctxt.Flag_locationlists { valueToProgAfter[v.ID] = s.pp.Next } - if f.PrintOrHtmlSSA { + if gatherPrintInfo { for ; x != s.pp.Next; x = x.Link { progToValue[x] = v } @@ -6894,7 +7085,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { x := s.pp.Next s.SetPos(b.Pos) Arch.SSAGenBlock(&s, b, next) - if f.PrintOrHtmlSSA { + if gatherPrintInfo { for ; x != s.pp.Next; x = x.Link { progToBlock[x] = b } @@ -6921,8 +7112,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) { // recovers a panic, it will return to caller with right results. // The results are already in memory, because they are not SSA'd // when the function has defers (see canSSAName). - if f.OwnAux.ABIInfo().OutRegistersUsed() != 0 { - Arch.LoadRegResults(&s, f) + for _, o := range f.OwnAux.ABIInfo().OutParams() { + n := o.Name.(*ir.Name) + rts, offs := o.RegisterTypesAndOffsets() + for i := range o.Registers { + Arch.LoadRegResult(&s, f, rts[i], ssa.ObjRegForAbiReg(o.Registers[i], f.Config), n, offs[i]) + } } pp.Prog(obj.ARET) @@ -6975,12 +7170,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) { if base.Ctxt.Flag_locationlists { var debugInfo *ssa.FuncDebug + debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug) if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 { - debugInfo = ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset) + ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo) } else { - debugInfo = ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset) + ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists, StackOffset, debugInfo) } - e.curfn.DebugInfo = debugInfo bstart := s.bstart idToIdx := make([]int, f.NumBlocks()) for i, b := range f.Blocks { @@ -7020,6 +7215,20 @@ func genssa(f *ssa.Func, pp *objw.Progs) { } + // Resolve jump table destinations. + for _, jt := range s.JumpTables { + // Convert from *Block targets to *Prog targets. + targets := make([]*obj.Prog, len(jt.Succs)) + for i, e := range jt.Succs { + targets[i] = s.bstart[e.Block().ID] + } + // Add to list of jump tables to be resolved at assembly time. + // The assembler converts from *Prog entries to absolute addresses + // once it knows instruction byte offsets. + fi := pp.CurFunc.LSym.Func() + fi.JumpTables = append(fi.JumpTables, obj.JumpTable{Sym: jt.Aux.(*obj.LSym), Targets: targets}) + } + if e.log { // spew to stdout filename := "" for p := pp.Text; p != nil; p = p.Link { @@ -7040,7 +7249,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { } } if f.HTMLWriter != nil { // spew to ssa.html - var buf bytes.Buffer + var buf strings.Builder buf.WriteString("") buf.WriteString("
        ") filename := "" @@ -7069,6 +7278,54 @@ func genssa(f *ssa.Func, pp *objw.Progs) { buf.WriteString("") f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String()) } + if ssa.GenssaDump[f.Name] { + fi := f.DumpFileForPhase("genssa") + if fi != nil { + + // inliningDiffers if any filename changes or if any line number except the innermost (index 0) changes. + inliningDiffers := func(a, b []src.Pos) bool { + if len(a) != len(b) { + return true + } + for i := range a { + if a[i].Filename() != b[i].Filename() { + return true + } + if i > 0 && a[i].Line() != b[i].Line() { + return true + } + } + return false + } + + var allPosOld []src.Pos + var allPos []src.Pos + + for p := pp.Text; p != nil; p = p.Link { + if p.Pos.IsKnown() { + allPos = p.AllPos(allPos) + if inliningDiffers(allPos, allPosOld) { + for i := len(allPos) - 1; i >= 0; i-- { + pos := allPos[i] + fmt.Fprintf(fi, "# %s:%d\n", pos.Filename(), pos.Line()) + } + allPos, allPosOld = allPosOld, allPos // swap, not copy, so that they do not share slice storage. + } + } + + var s string + if v, ok := progToValue[p]; ok { + s = v.String() + } else if b, ok := progToBlock[p]; ok { + s = b.String() + } else { + s = " " // most value and branch strings are 2-3 characters long + } + fmt.Fprintf(fi, " %-6s\t%.5d %s\t%s\n", s, p.Pc, ssa.StmtString(p.Pos), p.InstructionString()) + } + fi.Close() + } + } defframe(&s, e, f) @@ -7079,14 +7336,15 @@ func genssa(f *ssa.Func, pp *objw.Progs) { func defframe(s *State, e *ssafn, f *ssa.Func) { pp := s.pp - frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize)) + s.maxarg = types.RoundUp(s.maxarg, e.stkalign) + frame := s.maxarg + e.stksize if Arch.PadFrame != nil { frame = Arch.PadFrame(frame) } // Fill in argument and frame size. pp.Text.To.Type = obj.TYPE_TEXTSIZE - pp.Text.To.Val = int32(types.Rnd(f.OwnAux.ArgWidth(), int64(types.RegSize))) + pp.Text.To.Val = int32(types.RoundUp(f.OwnAux.ArgWidth(), int64(types.RegSize))) pp.Text.To.Offset = frame p := pp.Text @@ -7436,7 +7694,7 @@ func (s *State) Call(v *ssa.Value) *obj.Prog { switch Arch.LinkArch.Family { case sys.AMD64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X, sys.Wasm: p.To.Type = obj.TYPE_REG - case sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: + case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64: p.To.Type = obj.TYPE_MEM default: base.Fatalf("unknown indirect call family") @@ -7446,6 +7704,14 @@ func (s *State) Call(v *ssa.Value) *obj.Prog { return p } +// TailCall returns a new tail call instruction for the SSA value v. +// It is like Call, but for a tail call. +func (s *State) TailCall(v *ssa.Value) *obj.Prog { + p := s.Call(v) + p.As = obj.ARET + return p +} + // PrepareCall prepares to emit a CALL instruction for v and does call-related bookkeeping. // It must be called immediately before emitting the actual CALL instruction, // since it emits PCDATA for the stack map at the call (calls are safe points). @@ -7460,18 +7726,6 @@ func (s *State) PrepareCall(v *ssa.Value) { call, ok := v.Aux.(*ssa.AuxCall) - if ok && call.Fn == ir.Syms.Deferreturn { - // Deferred calls will appear to be returning to - // the CALL deferreturn(SB) that we are about to emit. - // However, the stack trace code will show the line - // of the instruction byte before the return PC. - // To avoid that being an unrelated instruction, - // insert an actual hardware NOP that will have the right line number. - // This is different from obj.ANOP, which is a virtual no-op - // that doesn't make it into the instruction stream. - Arch.Ginsnopdefer(s.pp) - } - if ok { // Record call graph information for nowritebarrierrec // analysis. @@ -7521,7 +7775,14 @@ type ssafn struct { strings map[string]*obj.LSym // map from constant string to data symbols stksize int64 // stack size for current frame stkptrsize int64 // prefix of stack containing pointers - log bool // print ssa debug to the stdout + + // alignment for current frame. + // NOTE: when stkalign > PtrSize, currently this only ensures the offsets of + // objects in the stack frame are aligned. The stack pointer is still aligned + // only PtrSize. + stkalign int64 + + log bool // print ssa debug to the stdout } // StringData returns a symbol which @@ -7542,10 +7803,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name { return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list } -func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym { - return reflectdata.ITabSym(it, offset) -} - // SplitSlot returns a slot representing the data of parent starting at offset. func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot { node := parent.N @@ -7633,6 +7890,10 @@ func (e *ssafn) MyImportPath() string { return base.Ctxt.Pkgpath } +func (e *ssafn) LSym() string { + return e.curfn.LSym.Name +} + func clobberBase(n ir.Node) ir.Node { if n.Op() == ir.ODOT { n := n.(*ir.SelectorExpr) @@ -7676,9 +7937,8 @@ func max8(a, b int8) int8 { return b } -// deferstruct makes a runtime._defer structure, with additional space for -// stksize bytes of args. -func deferstruct(stksize int64) *types.Type { +// deferstruct makes a runtime._defer structure. +func deferstruct() *types.Type { makefield := func(name string, typ *types.Type) *types.Field { // Unlike the global makefield function, this one needs to set Pkg // because these types might be compared (in SSA CSE sorting). @@ -7686,13 +7946,9 @@ func deferstruct(stksize int64) *types.Type { sym := &types.Sym{Name: name, Pkg: types.LocalPkg} return types.NewField(src.NoXPos, sym, typ) } - argtype := types.NewArray(types.Types[types.TUINT8], stksize) - argtype.Width = stksize - argtype.Align = 1 // These fields must match the ones in runtime/runtime2.go:_defer and - // cmd/compile/internal/gc/ssa.go:(*state).call. + // (*state).call above. fields := []*types.Field{ - makefield("siz", types.Types[types.TUINT32]), makefield("started", types.Types[types.TBOOL]), makefield("heap", types.Types[types.TBOOL]), makefield("openDefer", types.Types[types.TBOOL]), @@ -7704,10 +7960,9 @@ func deferstruct(stksize int64) *types.Type { makefield("fn", types.Types[types.TUINTPTR]), makefield("_panic", types.Types[types.TUINTPTR]), makefield("link", types.Types[types.TUINTPTR]), - makefield("framepc", types.Types[types.TUINTPTR]), - makefield("varp", types.Types[types.TUINTPTR]), makefield("fd", types.Types[types.TUINTPTR]), - makefield("args", argtype), + makefield("varp", types.Types[types.TUINTPTR]), + makefield("framepc", types.Types[types.TUINTPTR]), } // build struct holding the above fields diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go index abb0bba646e0ca..da0db5a573d07a 100644 --- a/src/cmd/compile/internal/staticdata/data.go +++ b/src/cmd/compile/internal/staticdata/data.go @@ -5,10 +5,9 @@ package staticdata import ( - "crypto/sha256" + "encoding/base64" "fmt" "go/constant" - "internal/buildcfg" "io" "io/ioutil" "os" @@ -21,6 +20,7 @@ import ( "cmd/compile/internal/objw" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/notsha256" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" @@ -61,10 +61,16 @@ func InitSliceBytes(nam *ir.Name, off int64, s string) { } const ( - stringSymPrefix = "go.string." - stringSymPattern = ".gostring.%d.%x" + stringSymPrefix = "go:string." + stringSymPattern = ".gostring.%d.%s" ) +// shortHashString converts the hash to a string for use with stringSymPattern. +// We cut it to 16 bytes and then base64-encode to make it even smaller. +func shortHashString(hash []byte) string { + return base64.StdEncoding.EncodeToString(hash[:16]) +} + // StringSym returns a symbol containing the string s. // The symbol contains the string data, not a string header. func StringSym(pos src.XPos, s string) (data *obj.LSym) { @@ -74,9 +80,9 @@ func StringSym(pos src.XPos, s string) (data *obj.LSym) { // Indulge in some paranoia by writing the length of s, too, // as protection against length extension attacks. // Same pattern is known to fileStringSym below. - h := sha256.New() + h := notsha256.New() io.WriteString(h, s) - symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil)) + symname = fmt.Sprintf(stringSymPattern, len(s), shortHashString(h.Sum(nil))) } else { // Small strings get named directly by their contents. symname = strconv.Quote(s) @@ -92,6 +98,10 @@ func StringSym(pos src.XPos, s string) (data *obj.LSym) { return symdata } +// maxFileSize is the maximum file size permitted by the linker +// (see issue #9862). +const maxFileSize = int64(2e9) + // fileStringSym returns a symbol for the contents and the size of file. // If readonly is true, the symbol shares storage with any literal string // or other file with the same content and is placed in a read-only section. @@ -128,24 +138,24 @@ func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj. sym = slicedata(pos, string(data)).Linksym() } if len(hash) > 0 { - sum := sha256.Sum256(data) + sum := notsha256.Sum256(data) copy(hash, sum[:]) } return sym, size, nil } - if size > 2e9 { + if size > maxFileSize { // ggloblsym takes an int32, // and probably the rest of the toolchain // can't handle such big symbols either. // See golang.org/issue/9862. - return nil, 0, fmt.Errorf("file too large") + return nil, 0, fmt.Errorf("file too large (%d bytes > %d bytes)", size, maxFileSize) } // File is too big to read and keep in memory. // Compute hash if needed for read-only content hashing or if the caller wants it. var sum []byte if readonly || len(hash) > 0 { - h := sha256.New() + h := notsha256.New() n, err := io.Copy(h, f) if err != nil { return nil, 0, err @@ -159,7 +169,7 @@ func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj. var symdata *obj.LSym if readonly { - symname := fmt.Sprintf(stringSymPattern, size, sum) + symname := fmt.Sprintf(stringSymPattern, size, shortHashString(sum)) symdata = base.Ctxt.Lookup(stringSymPrefix + symname) if !symdata.OnList() { info := symdata.NewFileInfo() @@ -232,15 +242,9 @@ func FuncLinksym(n *ir.Name) *obj.LSym { // except for the types package, which is protected separately. // Reusing funcsymsmu to also cover this package lookup // avoids a general, broader, expensive package lookup mutex. - // Note NeedFuncSym also does package look-up of func sym names, - // but that it is only called serially, from the front end. funcsymsmu.Lock() sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s)) - // Don't export s·f when compiling for dynamic linking. - // When dynamically linking, the necessary function - // symbols will be created explicitly with NeedFuncSym. - // See the NeedFuncSym comment for details. - if !base.Ctxt.Flag_dynlink && !existed { + if !existed { funcsyms = append(funcsyms, n) } funcsymsmu.Unlock() @@ -255,48 +259,6 @@ func GlobalLinksym(n *ir.Name) *obj.LSym { return n.Linksym() } -// NeedFuncSym ensures that fn·f is exported, if needed. -// It is only used with -dynlink. -// When not compiling for dynamic linking, -// the funcsyms are created as needed by -// the packages that use them. -// Normally we emit the fn·f stubs as DUPOK syms, -// but DUPOK doesn't work across shared library boundaries. -// So instead, when dynamic linking, we only create -// the fn·f stubs in fn's package. -func NeedFuncSym(fn *ir.Func) { - if base.Ctxt.InParallel { - // The append below probably just needs to lock - // funcsymsmu, like in FuncSym. - base.Fatalf("NeedFuncSym must be called in serial") - } - if fn.ABI != obj.ABIInternal && buildcfg.Experiment.RegabiWrappers { - // Function values must always reference ABIInternal - // entry points, so it doesn't make sense to create a - // funcsym for other ABIs. - // - // (If we're using ABI aliases, it doesn't matter.) - base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI) - } - if ir.IsBlank(fn.Nname) { - // Blank functions aren't unique, so we can't make a - // funcsym for them. - base.Fatalf("NeedFuncSym called for _") - } - if !base.Ctxt.Flag_dynlink { - return - } - s := fn.Nname.Sym() - if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") || - (base.Ctxt.Pkgpath == "internal/abi" && (s.Name == "FuncPCABI0" || s.Name == "FuncPCABIInternal")) { - // runtime.getg(), getclosureptr(), getcallerpc(), getcallersp(), - // and internal/abi.FuncPCABIxxx() are not real functions and so - // do not get funcsyms. - return - } - funcsyms = append(funcsyms, fn.Nname) -} - func WriteFuncSyms() { sort.Slice(funcsyms, func(i, j int) bool { return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name @@ -304,6 +266,14 @@ func WriteFuncSyms() { for _, nam := range funcsyms { s := nam.Sym() sf := s.Pkg.Lookup(ir.FuncSymName(s)).Linksym() + + // While compiling package runtime, we might try to create + // funcsyms for functions from both types.LocalPkg and + // ir.Pkgs.Runtime. + if base.Flag.CompilingRuntime && sf.OnList() { + continue + } + // Function values must always reference ABIInternal // entry points. target := s.Linksym() diff --git a/src/cmd/compile/internal/staticdata/embed.go b/src/cmd/compile/internal/staticdata/embed.go index 8936c4f5b44e44..8d4dedff598fb3 100644 --- a/src/cmd/compile/internal/staticdata/embed.go +++ b/src/cmd/compile/internal/staticdata/embed.go @@ -67,13 +67,13 @@ func embedFileList(v *ir.Name, kind int) []string { // embedKind determines the kind of embedding variable. func embedKind(typ *types.Type) int { - if typ.Sym() != nil && typ.Sym().Name == "FS" && (typ.Sym().Pkg.Path == "embed" || (typ.Sym().Pkg == types.LocalPkg && base.Ctxt.Pkgpath == "embed")) { + if typ.Sym() != nil && typ.Sym().Name == "FS" && typ.Sym().Pkg.Path == "embed" { return embedFiles } if typ.Kind() == types.TSTRING { return embedString } - if typ.Sym() == nil && typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 { + if typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 { return embedBytes } return embedUnknown @@ -108,13 +108,6 @@ func WriteEmbed(v *ir.Name) { // TODO(mdempsky): User errors should be reported by the frontend. commentPos := (*v.Embed)[0].Pos - if !types.AllowsGoVersion(types.LocalPkg, 1, 16) { - prevPos := base.Pos - base.Pos = commentPos - base.ErrorfVers("go1.16", "go:embed") - base.Pos = prevPos - return - } if base.Flag.Cfg.Embed.Patterns == nil { base.ErrorfAt(commentPos, "invalid go:embed: build system did not supply embed configuration") return @@ -142,7 +135,7 @@ func WriteEmbed(v *ir.Name) { } case embedFiles: - slicedata := base.Ctxt.Lookup(`"".` + v.Sym().Name + `.files`) + slicedata := v.Sym().Pkg.Lookup(v.Sym().Name + `.files`).Linksym() off := 0 // []files pointed at by Files off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize) // []file, pointing just past slice diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go index 0c97b6de747660..e9b97e6c87e9e7 100644 --- a/src/cmd/compile/internal/staticinit/sched.go +++ b/src/cmd/compile/internal/staticinit/sched.go @@ -133,7 +133,7 @@ func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Ty if ir.IsZero(r) { return true } - staticdata.InitConst(l, loff, r, int(typ.Width)) + staticdata.InitConst(l, loff, r, int(typ.Size())) return true case ir.OADDR: @@ -165,7 +165,7 @@ func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Ty e := &p.E[i] typ := e.Expr.Type() if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { - staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Width)) + staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size())) continue } x := e.Expr @@ -229,7 +229,7 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty if ir.IsZero(r) { return true } - staticdata.InitConst(l, loff, r, int(typ.Width)) + staticdata.InitConst(l, loff, r, int(typ.Size())) return true case ir.OADDR: @@ -286,7 +286,7 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty for i := range p.E { e := &p.E[i] if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL { - staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Width)) + staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size())) continue } ir.SetPos(e.Expr) @@ -392,7 +392,7 @@ func (s *Schedule) initplan(n ir.Node) { } a = kv.Value } - s.addvalue(p, k*n.Type().Elem().Width, a) + s.addvalue(p, k*n.Type().Elem().Size(), a) k++ } @@ -403,10 +403,10 @@ func (s *Schedule) initplan(n ir.Node) { base.Fatalf("initplan structlit") } a := a.(*ir.StructKeyExpr) - if a.Field.IsBlank() { + if a.Sym().IsBlank() { continue } - s.addvalue(p, a.Offset, a.Value) + s.addvalue(p, a.Field.Offset, a.Value) } case ir.OMAPLIT: @@ -459,6 +459,7 @@ func StaticName(t *types.Type) *ir.Name { statuniqgen++ typecheck.Declare(n, ir.PEXTERN) n.SetType(t) + n.Linksym().Set(obj.AttrStatic, true) return n } @@ -499,10 +500,10 @@ func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) { } // Check for overflow. - if n.Type().Width != 0 && types.MaxWidth/n.Type().Width <= int64(l) { + if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) { break } - offset += int64(l) * n.Type().Width + offset += int64(l) * n.Type().Size() return name, offset, true } @@ -521,7 +522,6 @@ func AnySideEffects(n ir.Node) bool { case ir.ONAME, ir.ONONAME, ir.OTYPE, - ir.OPACK, ir.OLITERAL, ir.ONIL, ir.OADD, diff --git a/src/cmd/compile/internal/syntax/branches.go b/src/cmd/compile/internal/syntax/branches.go index 56e97c71d8aced..3d7ffed37423ba 100644 --- a/src/cmd/compile/internal/syntax/branches.go +++ b/src/cmd/compile/internal/syntax/branches.go @@ -6,15 +6,13 @@ package syntax import "fmt" -// TODO(gri) consider making this part of the parser code - // checkBranches checks correct use of labels and branch -// statements (break, continue, goto) in a function body. +// statements (break, continue, fallthrough, goto) in a function body. // It catches: -// - misplaced breaks and continues -// - bad labeled breaks and continues -// - invalid, unused, duplicate, and missing labels -// - gotos jumping over variable declarations and into blocks +// - misplaced breaks, continues, and fallthroughs +// - bad labeled breaks and continues +// - invalid, unused, duplicate, and missing labels +// - gotos jumping over variable declarations and into blocks func checkBranches(body *BlockStmt, errh ErrorHandler) { if body == nil { return @@ -123,6 +121,7 @@ func (ls *labelScope) enclosingTarget(b *block, name string) *LabeledStmt { type targets struct { breaks Stmt // *ForStmt, *SwitchStmt, *SelectStmt, or nil continues *ForStmt // or nil + caseIndex int // case index of immediately enclosing switch statement, or < 0 } // blockBranches processes a block's body starting at start and returns the @@ -163,7 +162,10 @@ func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledS fwdGotos = append(fwdGotos, ls.blockBranches(b, ctxt, lstmt, start, body)...) } - for _, stmt := range body { + // A fallthrough statement counts as last statement in a statement + // list even if there are trailing empty statements; remove them. + stmtList := trimTrailingEmptyStmts(body) + for stmtIndex, stmt := range stmtList { lstmt = nil L: switch s := stmt.(type) { @@ -222,7 +224,20 @@ func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledS ls.err(s.Pos(), "continue is not in a loop") } case _Fallthrough: - // nothing to do + msg := "fallthrough statement out of place" + if t, _ := ctxt.breaks.(*SwitchStmt); t != nil { + if _, ok := t.Tag.(*TypeSwitchGuard); ok { + msg = "cannot fallthrough in type switch" + } else if ctxt.caseIndex < 0 || stmtIndex+1 < len(stmtList) { + // fallthrough nested in a block or not the last statement + // use msg as is + } else if ctxt.caseIndex+1 == len(t.Body) { + msg = "cannot fallthrough final case in switch" + } else { + break // fallthrough ok + } + } + ls.err(s.Pos(), msg) case _Goto: fallthrough // should always have a label default: @@ -282,25 +297,29 @@ func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledS } case *BlockStmt: - innerBlock(ctxt, s.Pos(), s.List) + inner := targets{ctxt.breaks, ctxt.continues, -1} + innerBlock(inner, s.Pos(), s.List) case *IfStmt: - innerBlock(ctxt, s.Then.Pos(), s.Then.List) + inner := targets{ctxt.breaks, ctxt.continues, -1} + innerBlock(inner, s.Then.Pos(), s.Then.List) if s.Else != nil { - innerBlock(ctxt, s.Else.Pos(), []Stmt{s.Else}) + innerBlock(inner, s.Else.Pos(), []Stmt{s.Else}) } case *ForStmt: - innerBlock(targets{s, s}, s.Body.Pos(), s.Body.List) + inner := targets{s, s, -1} + innerBlock(inner, s.Body.Pos(), s.Body.List) case *SwitchStmt: - inner := targets{s, ctxt.continues} - for _, cc := range s.Body { + inner := targets{s, ctxt.continues, -1} + for i, cc := range s.Body { + inner.caseIndex = i innerBlock(inner, cc.Pos(), cc.Body) } case *SelectStmt: - inner := targets{s, ctxt.continues} + inner := targets{s, ctxt.continues, -1} for _, cc := range s.Body { innerBlock(inner, cc.Pos(), cc.Body) } @@ -309,3 +328,12 @@ func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledS return fwdGotos } + +func trimTrailingEmptyStmts(list []Stmt) []Stmt { + for i := len(list); i > 0; i-- { + if _, ok := list[i-1].(*EmptyStmt); !ok { + return list[:i] + } + } + return nil +} diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go index 22680dce786c52..1ba85cc8d9a987 100644 --- a/src/cmd/compile/internal/syntax/dumper_test.go +++ b/src/cmd/compile/internal/syntax/dumper_test.go @@ -13,11 +13,7 @@ func TestDump(t *testing.T) { t.Skip("skipping test in short mode") } - // provide a no-op error handler so parsing doesn't stop after first error - ast, err := ParseFile(*src_, func(error) {}, nil, CheckBranches) - if err != nil { - t.Error(err) - } + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, CheckBranches) if ast != nil { Fdump(testOut(), ast) diff --git a/src/cmd/compile/internal/syntax/error_test.go b/src/cmd/compile/internal/syntax/error_test.go index e4bedf54fdc04f..2f70b5278e7aed 100644 --- a/src/cmd/compile/internal/syntax/error_test.go +++ b/src/cmd/compile/internal/syntax/error_test.go @@ -128,10 +128,6 @@ func testSyntaxErrors(t *testing.T, filename string) { } defer f.Close() - var mode Mode - if strings.HasSuffix(filename, ".go2") { - mode = AllowGenerics - } ParseFile(filename, func(err error) { e, ok := err.(Error) if !ok { @@ -154,11 +150,11 @@ func testSyntaxErrors(t *testing.T, filename string) { if found { rx, err := regexp.Compile(pattern) if err != nil { - t.Errorf("%s: %v", pos, err) + t.Errorf("%s:%s: %v", filename, pos, err) return } if match := rx.MatchString(e.Msg); !match { - t.Errorf("%s: %q does not match %q", pos, e.Msg, pattern) + t.Errorf("%s:%s: %q does not match %q", filename, pos, e.Msg, pattern) return } // we have a match - eliminate this error @@ -166,7 +162,7 @@ func testSyntaxErrors(t *testing.T, filename string) { } else { t.Errorf("%s:%s: unexpected error: %s", filename, orig, e.Msg) } - }, nil, mode) + }, nil, CheckBranches) if *print { fmt.Println() diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index fb9786daa325c5..10af3c597b03e2 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -275,14 +275,14 @@ type ( // Name Type // Type Field struct { - Name *Name // nil means anonymous field/parameter (structs/parameters), or embedded interface (interfaces) + Name *Name // nil means anonymous field/parameter (structs/parameters), or embedded element (interfaces) Type Expr // field names declared in a list share the same Type (identical pointers) node } // interface { MethodList[0]; MethodList[1]; ... } InterfaceType struct { - MethodList []*Field // a field named "type" means a type constraint + MethodList []*Field expr } @@ -385,7 +385,7 @@ type ( CallStmt struct { Tok token // Go or Defer - Call *CallExpr + Call Expr stmt } @@ -462,7 +462,7 @@ func (simpleStmt) aSimpleStmt() {} // Comments // TODO(gri) Consider renaming to CommentPos, CommentPlacement, etc. -// Kind = Above doesn't make much sense. +// Kind = Above doesn't make much sense. type CommentKind uint const ( diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index e7b8840b337e67..1f5e8bc449f882 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -146,11 +146,13 @@ func (p *parser) updateBase(pos Pos, tline, tcol uint, text string) { // If we have a column (//line filename:line:col form), // an empty filename means to use the previous filename. filename := text[:i-1] // lop off ":line" + trimmed := false if filename == "" && ok2 { filename = p.base.Filename() + trimmed = p.base.Trimmed() } - p.base = NewLineBase(pos, filename, line, col) + p.base = NewLineBase(pos, filename, trimmed, line, col) } func commentText(s string) string { @@ -188,7 +190,7 @@ func (p *parser) got(tok token) bool { func (p *parser) want(tok token) { if !p.got(tok) { - p.syntaxError("expecting " + tokstring(tok)) + p.syntaxError("expected " + tokstring(tok)) p.advance() } } @@ -198,7 +200,7 @@ func (p *parser) want(tok token) { func (p *parser) gotAssign() bool { switch p.tok { case _Define: - p.syntaxError("expecting =") + p.syntaxError("expected =") fallthrough case _Assign: p.next() @@ -244,7 +246,7 @@ func (p *parser) syntaxErrorAt(pos Pos, msg string) { // nothing to do case strings.HasPrefix(msg, "in "), strings.HasPrefix(msg, "at "), strings.HasPrefix(msg, "after "): msg = " " + msg - case strings.HasPrefix(msg, "expecting "): + case strings.HasPrefix(msg, "expected "): msg = ", " + msg default: // plain error - we don't care about current token @@ -270,11 +272,15 @@ func (p *parser) syntaxErrorAt(pos Pos, msg string) { tok = tokstring(p.tok) } + // TODO(gri) This may print "unexpected X, expected Y". + // Consider "got X, expected Y" in this case. p.errorAt(pos, "syntax error: unexpected "+tok+msg) } // tokstring returns the English word for selected punctuation tokens -// for more readable error messages. +// for more readable error messages. Use tokstring (not tok.String()) +// for user-facing (error) messages; use tok.String() for debugging +// output. func tokstring(tok token) string { switch tok { case _Comma: @@ -395,15 +401,20 @@ func (p *parser) fileOrNil() *File { return nil } - // { ImportDecl ";" } - for p.got(_Import) { - f.DeclList = p.appendGroup(f.DeclList, p.importDecl) - p.want(_Semi) - } - - // { TopLevelDecl ";" } + // Accept import declarations anywhere for error tolerance, but complain. + // { ( ImportDecl | TopLevelDecl ) ";" } + prev := _Import for p.tok != _EOF { + if p.tok == _Import && prev != _Import { + p.syntaxError("imports must appear before other declarations") + } + prev = p.tok + switch p.tok { + case _Import: + p.next() + f.DeclList = p.appendGroup(f.DeclList, p.importDecl) + case _Const: p.next() f.DeclList = p.appendGroup(f.DeclList, p.constDecl) @@ -429,7 +440,7 @@ func (p *parser) fileOrNil() *File { } else { p.syntaxError("non-declaration statement outside function body") } - p.advance(_Const, _Type, _Var, _Func) + p.advance(_Import, _Const, _Type, _Var, _Func) continue } @@ -439,7 +450,7 @@ func (p *parser) fileOrNil() *File { if p.tok != _EOF && !p.got(_Semi) { p.syntaxError("after top level declaration") - p.advance(_Const, _Type, _Var, _Func) + p.advance(_Import, _Const, _Type, _Var, _Func) } } // p.tok == _EOF @@ -467,8 +478,7 @@ func isEmptyFuncDecl(dcl Decl) bool { // elements are accepted. list returns the position of the closing token. // // list = [ f { sep f } [sep] ] close . -// -func (p *parser) list(sep, close token, f func() bool) Pos { +func (p *parser) list(context string, sep, close token, f func() bool) Pos { if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) { panic("invalid sep or close argument for list") } @@ -478,7 +488,7 @@ func (p *parser) list(sep, close token, f func() bool) Pos { done = f() // sep is optional before close if !p.got(sep) && p.tok != close { - p.syntaxError(fmt.Sprintf("expecting %s or %s", tokstring(sep), tokstring(close))) + p.syntaxError(fmt.Sprintf("in %s; possibly missing %s or %s", context, tokstring(sep), tokstring(close))) p.advance(_Rparen, _Rbrack, _Rbrace) if p.tok != close { // position could be better but we had an error so we don't care @@ -498,7 +508,7 @@ func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl { g := new(Group) p.clearPragma() p.next() // must consume "(" after calling clearPragma! - p.list(_Semi, _Rparen, func() bool { + p.list("grouped declaration", _Semi, _Rparen, func() bool { if x := f(g); x != nil { list = append(list, x) } @@ -538,7 +548,7 @@ func (p *parser) importDecl(group *Group) Decl { return d } if !d.Path.Bad && d.Path.Kind != StringLit { - p.syntaxError("import path must be a string") + p.syntaxErrorAt(d.Path.Pos(), "import path must be a string") d.Path.Bad = true } // d.Path.Bad || d.Path.Kind == StringLit @@ -581,34 +591,61 @@ func (p *parser) typeDecl(group *Group) Decl { d.Name = p.name() if p.tok == _Lbrack { - // array/slice or generic type + // d.Name "[" ... + // array/slice type or type parameter list pos := p.pos() p.next() switch p.tok { - case _Rbrack: - p.next() - d.Type = p.sliceType(pos) case _Name: - // array or generic type - p.xnest++ - x := p.expr() - p.xnest-- - if name0, ok := x.(*Name); p.mode&AllowGenerics != 0 && ok && p.tok != _Rbrack { - // generic type - d.TParamList = p.paramList(name0, _Rbrack, true) - pos := p.pos() - if p.gotAssign() { - p.syntaxErrorAt(pos, "generic type cannot be alias") - } + // We may have an array type or a type parameter list. + // In either case we expect an expression x (which may + // just be a name, or a more complex expression) which + // we can analyze further. + // + // A type parameter list may have a type bound starting + // with a "[" as in: P []E. In that case, simply parsing + // an expression would lead to an error: P[] is invalid. + // But since index or slice expressions are never constant + // and thus invalid array length expressions, if the name + // is followed by "[" it must be the start of an array or + // slice constraint. Only if we don't see a "[" do we + // need to parse a full expression. Notably, name <- x + // is not a concern because name <- x is a statement and + // not an expression. + var x Expr = p.name() + if p.tok != _Lbrack { + // To parse the expression starting with name, expand + // the call sequence we would get by passing in name + // to parser.expr, and pass in name to parser.pexpr. + p.xnest++ + x = p.binaryExpr(p.pexpr(x, false), 0) + p.xnest-- + } + // Analyze expression x. If we can split x into a type parameter + // name, possibly followed by a type parameter type, we consider + // this the start of a type parameter list, with some caveats: + // a single name followed by "]" tilts the decision towards an + // array declaration; a type parameter type that could also be + // an ordinary expression but which is followed by a comma tilts + // the decision towards a type parameter list. + if pname, ptype := extractName(x, p.tok == _Comma); pname != nil && (ptype != nil || p.tok != _Rbrack) { + // d.Name "[" pname ... + // d.Name "[" pname ptype ... + // d.Name "[" pname ptype "," ... + d.TParamList = p.paramList(pname, ptype, _Rbrack, true) // ptype may be nil + d.Alias = p.gotAssign() d.Type = p.typeOrNil() } else { - // x is the array length expression - if debug && x == nil { - panic("internal error: nil expression") - } + // d.Name "[" pname "]" ... + // d.Name "[" x ... d.Type = p.arrayType(pos, x) } + case _Rbrack: + // d.Name "[" "]" ... + p.next() + d.Type = p.sliceType(pos) default: + // d.Name "[" ... d.Type = p.arrayType(pos, nil) } } else { @@ -625,6 +662,73 @@ func (p *parser) typeDecl(group *Group) Decl { return d } +// extractName splits the expression x into (name, expr) if syntactically +// x can be written as name expr. The split only happens if expr is a type +// element (per the isTypeElem predicate) or if force is set. +// If x is just a name, the result is (name, nil). If the split succeeds, +// the result is (name, expr). Otherwise the result is (nil, x). +// Examples: +// +// x force name expr +// ------------------------------------ +// P*[]int T/F P *[]int +// P*E T P *E +// P*E F nil P*E +// P([]int) T/F P []int +// P(E) T P E +// P(E) F nil P(E) +// P*E|F|~G T/F P *E|F|~G +// P*E|F|G T P *E|F|G +// P*E|F|G F nil P*E|F|G +func extractName(x Expr, force bool) (*Name, Expr) { + switch x := x.(type) { + case *Name: + return x, nil + case *Operation: + if x.Y == nil { + break // unary expr + } + switch x.Op { + case Mul: + if name, _ := x.X.(*Name); name != nil && (force || isTypeElem(x.Y)) { + // x = name *x.Y + op := *x + op.X, op.Y = op.Y, nil // change op into unary *op.Y + return name, &op + } + case Or: + if name, lhs := extractName(x.X, force || isTypeElem(x.Y)); name != nil && lhs != nil { + // x = name lhs|x.Y + op := *x + op.X = lhs + return name, &op + } + } + case *CallExpr: + if name, _ := x.Fun.(*Name); name != nil { + if len(x.ArgList) == 1 && !x.HasDots && (force || isTypeElem(x.ArgList[0])) { + // x = name "(" x.ArgList[0] ")" + return name, x.ArgList[0] + } + } + } + return nil, x +} + +// isTypeElem reports whether x is a (possibly parenthesized) type element expression. +// The result is false if x could be a type element OR an ordinary (value) expression. +func isTypeElem(x Expr) bool { + switch x := x.(type) { + case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType: + return true + case *Operation: + return isTypeElem(x.X) || (x.Y != nil && isTypeElem(x.Y)) || x.Op == Tilde + case *ParenExpr: + return isTypeElem(x.X) + } + return false +} + // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) . func (p *parser) varDecl(group *Group) Decl { if trace { @@ -664,7 +768,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl { f.Pragma = p.takePragma() if p.got(_Lparen) { - rcvr := p.paramList(nil, _Rparen, false) + rcvr := p.paramList(nil, nil, _Rparen, false) switch len(rcvr) { case 0: p.error("method has no receiver") @@ -677,21 +781,19 @@ func (p *parser) funcDeclOrNil() *FuncDecl { } if p.tok != _Name { - p.syntaxError("expecting name or (") + p.syntaxError("expected name or (") p.advance(_Lbrace, _Semi) return nil } f.Name = p.name() - if p.mode&AllowGenerics != 0 && p.got(_Lbrack) { - if p.tok == _Rbrack { - p.syntaxError("empty type parameter list") - p.next() - } else { - f.TParamList = p.paramList(nil, _Rbrack, true) - } + + context := "" + if f.Recv != nil { + context = "method" // don't permit (method) type parameters in funcType } - f.Type = p.funcType() + f.TParamList, f.Type = p.funcType(context) + if p.tok == _Lbrace { f.Body = p.funcBody() } @@ -723,14 +825,16 @@ func (p *parser) expr() Expr { defer p.trace("expr")() } - return p.binaryExpr(0) + return p.binaryExpr(nil, 0) } // Expression = UnaryExpr | Expression binary_op Expression . -func (p *parser) binaryExpr(prec int) Expr { +func (p *parser) binaryExpr(x Expr, prec int) Expr { // don't trace binaryExpr - only leads to overly nested trace output - x := p.unaryExpr() + if x == nil { + x = p.unaryExpr() + } for (p.tok == _Operator || p.tok == _Star) && p.prec > prec { t := new(Operation) t.pos = p.pos() @@ -738,7 +842,7 @@ func (p *parser) binaryExpr(prec int) Expr { tprec := p.prec p.next() t.X = x - t.Y = p.binaryExpr(tprec) + t.Y = p.binaryExpr(nil, tprec) x = t } return x @@ -753,7 +857,7 @@ func (p *parser) unaryExpr() Expr { switch p.tok { case _Operator, _Star: switch p.op { - case Mul, Add, Sub, Not, Xor: + case Mul, Add, Sub, Not, Xor, Tilde: x := new(Operation) x.pos = p.pos() x.Op = p.op @@ -807,7 +911,7 @@ func (p *parser) unaryExpr() Expr { if dir == RecvOnly { // t is type <-chan E but <-<-chan E is not permitted // (report same error as for "type _ <-<-chan E") - p.syntaxError("unexpected <-, expecting chan") + p.syntaxError("unexpected <-, expected chan") // already progressed, no need to advance } c.Dir = RecvOnly @@ -816,7 +920,7 @@ func (p *parser) unaryExpr() Expr { if dir == SendOnly { // channel dir is <- but channel element E is not a channel // (report same error as for "type _ <-chan<-E") - p.syntaxError(fmt.Sprintf("unexpected %s, expecting chan", String(t))) + p.syntaxError(fmt.Sprintf("unexpected %s, expected chan", String(t))) // already progressed, no need to advance } return x @@ -833,7 +937,7 @@ func (p *parser) unaryExpr() Expr { // TODO(mdempsky): We need parens here so we can report an // error for "(x) := true". It should be possible to detect // and reject that more efficiently though. - return p.pexpr(true) + return p.pexpr(nil, true) } // callStmt parses call-like statements that can be preceded by 'defer' and 'go'. @@ -847,23 +951,14 @@ func (p *parser) callStmt() *CallStmt { s.Tok = p.tok // _Defer or _Go p.next() - x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below + x := p.pexpr(nil, p.tok == _Lparen) // keep_parens so we can report error below if t := unparen(x); t != x { p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must not be parenthesized", s.Tok)) // already progressed, no need to advance x = t } - cx, ok := x.(*CallExpr) - if !ok { - p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must be function call", s.Tok)) - // already progressed, no need to advance - cx = new(CallExpr) - cx.pos = x.Pos() - cx.Fun = x // assume common error of missing parentheses (function invocation) - } - - s.Call = cx + s.Call = x return s } @@ -917,7 +1012,7 @@ func (p *parser) operand(keep_parens bool) Expr { case _Func: pos := p.pos() p.next() - ftyp := p.funcType() + _, ftyp := p.funcType("function type") if p.tok == _Lbrace { p.xnest++ @@ -936,7 +1031,7 @@ func (p *parser) operand(keep_parens bool) Expr { default: x := p.badExpr() - p.syntaxError("expecting expression") + p.syntaxError("expected expression") p.advance(_Rparen, _Rbrack, _Rbrace) return x } @@ -947,28 +1042,32 @@ func (p *parser) operand(keep_parens bool) Expr { // as well (operand is only called from pexpr). } -// PrimaryExpr = -// Operand | -// Conversion | -// PrimaryExpr Selector | -// PrimaryExpr Index | -// PrimaryExpr Slice | -// PrimaryExpr TypeAssertion | -// PrimaryExpr Arguments . +// pexpr parses a PrimaryExpr. +// +// PrimaryExpr = +// Operand | +// Conversion | +// PrimaryExpr Selector | +// PrimaryExpr Index | +// PrimaryExpr Slice | +// PrimaryExpr TypeAssertion | +// PrimaryExpr Arguments . // -// Selector = "." identifier . -// Index = "[" Expression "]" . -// Slice = "[" ( [ Expression ] ":" [ Expression ] ) | -// ( [ Expression ] ":" Expression ":" Expression ) -// "]" . -// TypeAssertion = "." "(" Type ")" . -// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . -func (p *parser) pexpr(keep_parens bool) Expr { +// Selector = "." identifier . +// Index = "[" Expression "]" . +// Slice = "[" ( [ Expression ] ":" [ Expression ] ) | +// ( [ Expression ] ":" Expression ":" Expression ) +// "]" . +// TypeAssertion = "." "(" Type ")" . +// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . +func (p *parser) pexpr(x Expr, keep_parens bool) Expr { if trace { defer p.trace("pexpr")() } - x := p.operand(keep_parens) + if x == nil { + x = p.operand(keep_parens) + } loop: for { @@ -1003,53 +1102,41 @@ loop: p.want(_Rparen) default: - p.syntaxError("expecting name or (") + p.syntaxError("expected name or (") p.advance(_Semi, _Rparen) } case _Lbrack: p.next() - if p.tok == _Rbrack { - // invalid empty instance, slice or index expression; accept but complain - p.syntaxError("expecting operand") - p.next() - break - } - var i Expr if p.tok != _Colon { - if p.mode&AllowGenerics == 0 { - p.xnest++ - i = p.expr() - p.xnest-- - if p.got(_Rbrack) { - // x[i] - t := new(IndexExpr) - t.pos = pos - t.X = x - t.Index = i - x = t - break - } + var comma bool + if p.tok == _Rbrack { + // invalid empty instance, slice or index expression; accept but complain + p.syntaxError("expected operand") + i = p.badExpr() } else { - var comma bool - i, comma = p.typeList() - if comma || p.tok == _Rbrack { - p.want(_Rbrack) - // x[i,] or x[i, j, ...] - t := new(IndexExpr) - t.pos = pos - t.X = x - t.Index = i - x = t - break - } + i, comma = p.typeList(false) + } + if comma || p.tok == _Rbrack { + p.want(_Rbrack) + // x[], x[i,] or x[i, j, ...] + t := new(IndexExpr) + t.pos = pos + t.X = x + t.Index = i + x = t + break } } // x[i:... - p.want(_Colon) + // For better error message, don't simply use p.want(_Colon) here (issue #47704). + if !p.got(_Colon) { + p.syntaxError("expected comma, : or ]") + p.advance(_Comma, _Colon, _Rbrack) + } p.xnest++ t := new(SliceExpr) t.pos = pos @@ -1100,7 +1187,7 @@ loop: complit_ok = true } case *IndexExpr: - if p.xnest >= 0 { + if p.xnest >= 0 && !isValue(t) { // x is possibly a composite literal type complit_ok = true } @@ -1127,6 +1214,21 @@ loop: return x } +// isValue reports whether x syntactically must be a value (and not a type) expression. +func isValue(x Expr) bool { + switch x := x.(type) { + case *BasicLit, *CompositeLit, *FuncLit, *SliceExpr, *AssertExpr, *TypeSwitchGuard, *CallExpr: + return true + case *Operation: + return x.Op != Mul || x.Y != nil // *T may be a type + case *ParenExpr: + return isValue(x.X) + case *IndexExpr: + return isValue(x.X) || isValue(x.Index) + } + return false +} + // Element = Expression | LiteralValue . func (p *parser) bare_complitexpr() Expr { if trace { @@ -1152,7 +1254,7 @@ func (p *parser) complitexpr() *CompositeLit { p.xnest++ p.want(_Lbrace) - x.Rbrace = p.list(_Comma, _Rbrace, func() bool { + x.Rbrace = p.list("composite literal", _Comma, _Rbrace, func() bool { // value e := p.bare_complitexpr() if p.tok == _Colon { @@ -1184,7 +1286,7 @@ func (p *parser) type_() Expr { typ := p.typeOrNil() if typ == nil { typ = p.badExpr() - p.syntaxError("expecting type") + p.syntaxError("expected type") p.advance(_Comma, _Colon, _Semi, _Rparen, _Rbrack, _Rbrace) } @@ -1202,10 +1304,10 @@ func newIndirect(pos Pos, typ Expr) Expr { // typeOrNil is like type_ but it returns nil if there was no type // instead of reporting an error. // -// Type = TypeName | TypeLit | "(" Type ")" . -// TypeName = identifier | QualifiedIdent . -// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | -// SliceType | MapType | Channel_Type . +// Type = TypeName | TypeLit | "(" Type ")" . +// TypeName = identifier | QualifiedIdent . +// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | +// SliceType | MapType | Channel_Type . func (p *parser) typeOrNil() Expr { if trace { defer p.trace("typeOrNil")() @@ -1231,7 +1333,8 @@ func (p *parser) typeOrNil() Expr { case _Func: // fntype p.next() - return p.funcType() + _, t := p.funcType("function type") + return t case _Lbrack: // '[' oexpr ']' ntype @@ -1295,27 +1398,43 @@ func (p *parser) typeInstance(typ Expr) Expr { x.pos = pos x.X = typ if p.tok == _Rbrack { - p.syntaxError("expecting type") + p.syntaxError("expected type argument list") x.Index = p.badExpr() } else { - x.Index, _ = p.typeList() + x.Index, _ = p.typeList(true) } p.want(_Rbrack) return x } -func (p *parser) funcType() *FuncType { +// If context != "", type parameters are not permitted. +func (p *parser) funcType(context string) ([]*Field, *FuncType) { if trace { defer p.trace("funcType")() } typ := new(FuncType) typ.pos = p.pos() + + var tparamList []*Field + if p.got(_Lbrack) { + if context != "" { + // accept but complain + p.syntaxErrorAt(typ.pos, context+" must have no type parameters") + } + if p.tok == _Rbrack { + p.syntaxError("empty type parameter list") + p.next() + } else { + tparamList = p.paramList(nil, nil, _Rbrack, true) + } + } + p.want(_Lparen) - typ.ParamList = p.paramList(nil, _Rparen, false) + typ.ParamList = p.paramList(nil, nil, _Rparen, false) typ.ResultList = p.funcResult() - return typ + return tparamList, typ } // "[" has already been consumed, and pos is its position. @@ -1330,6 +1449,13 @@ func (p *parser) arrayType(pos Pos, len Expr) Expr { len = p.expr() p.xnest-- } + if p.tok == _Comma { + // Trailing commas are accepted in type parameter + // lists but not in array type declarations. + // Accept for better error handling but complain. + p.syntaxError("unexpected comma; expected ]") + p.next() + } p.want(_Rbrack) t := new(ArrayType) t.pos = pos @@ -1372,7 +1498,7 @@ func (p *parser) structType() *StructType { p.want(_Struct) p.want(_Lbrace) - p.list(_Semi, _Rbrace, func() bool { + p.list("struct type", _Semi, _Rbrace, func() bool { p.fieldDecl(typ) return false }) @@ -1382,7 +1508,6 @@ func (p *parser) structType() *StructType { // InterfaceType = "interface" "{" { ( MethodDecl | EmbeddedElem | TypeList ) ";" } "}" . // TypeList = "type" Type { "," Type } . -// TODO(gri) remove TypeList syntax if we accept #45346 func (p *parser) interfaceType() *InterfaceType { if trace { defer p.trace("interfaceType")() @@ -1393,66 +1518,15 @@ func (p *parser) interfaceType() *InterfaceType { p.want(_Interface) p.want(_Lbrace) - p.list(_Semi, _Rbrace, func() bool { - switch p.tok { - case _Name: - f := p.methodDecl() - if f.Name == nil && p.mode&AllowGenerics != 0 { - f = p.embeddedElem(f) - } - typ.MethodList = append(typ.MethodList, f) - return false - - case _Lparen: - // TODO(gri) Need to decide how to adjust this restriction. - p.syntaxError("cannot parenthesize embedded type") - f := new(Field) - f.pos = p.pos() - p.next() - f.Type = p.qualifiedName(nil) - p.want(_Rparen) - typ.MethodList = append(typ.MethodList, f) - return false - - case _Operator: - if p.op == Tilde && p.mode&AllowGenerics != 0 { - typ.MethodList = append(typ.MethodList, p.embeddedElem(nil)) - return false - } - - case _Type: - // TODO(gri) remove TypeList syntax if we accept #45346 - if p.mode&AllowGenerics != 0 { - type_ := NewName(p.pos(), "type") // cannot have a method named "type" - p.next() - if p.tok != _Semi && p.tok != _Rbrace { - f := new(Field) - f.pos = p.pos() - f.Name = type_ - f.Type = p.type_() - typ.MethodList = append(typ.MethodList, f) - for p.got(_Comma) { - f := new(Field) - f.pos = p.pos() - f.Name = type_ - f.Type = p.type_() - typ.MethodList = append(typ.MethodList, f) - } - } else { - p.syntaxError("expecting type") - } - return false - } + p.list("interface type", _Semi, _Rbrace, func() bool { + var f *Field + if p.tok == _Name { + f = p.methodDecl() } - - if p.mode&AllowGenerics != 0 { - p.syntaxError("expecting method, type list, or embedded element") - p.advance(_Semi, _Rbrace, _Type) // TODO(gri) remove _Type if we don't accept it anymore - return false + if f == nil || f.Name == nil { + f = p.embeddedElem(f) } - - p.syntaxError("expecting method or interface name") - p.advance(_Semi, _Rbrace) + typ.MethodList = append(typ.MethodList, f) return false }) @@ -1466,7 +1540,7 @@ func (p *parser) funcResult() []*Field { } if p.got(_Lparen) { - return p.paramList(nil, _Rparen, false) + return p.paramList(nil, nil, _Rparen, false) } pos := p.pos() @@ -1525,7 +1599,7 @@ func (p *parser) fieldDecl(styp *StructType) { // Careful dance: We don't know if we have an embedded instantiated // type T[P1, P2, ...] or a field T of array/slice type [P]E or []E. - if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack { + if len(names) == 1 && p.tok == _Lbrack { typ = p.arrayOrTArgs() if typ, ok := typ.(*IndexExpr); ok { // embedded type T[P1, P2, ...] @@ -1579,7 +1653,7 @@ func (p *parser) fieldDecl(styp *StructType) { p.addField(styp, pos, nil, typ, tag) default: - p.syntaxError("expecting field name or embedded type") + p.syntaxError("expected field name or embedded type") p.advance(_Semi, _Rbrace) } } @@ -1596,7 +1670,7 @@ func (p *parser) arrayOrTArgs() Expr { } // x [n]E or x[n,], x[n1, n2], ... - n, comma := p.typeList() + n, comma := p.typeList(false) p.want(_Rbrack) if !comma { if elem := p.typeOrNil(); elem != nil { @@ -1642,100 +1716,81 @@ func (p *parser) methodDecl() *Field { f.pos = p.pos() name := p.name() - // accept potential name list but complain - // TODO(gri) We probably don't need this special check anymore. - // Nobody writes this kind of code. It's from ancient - // Go beginnings. - hasNameList := false - for p.got(_Comma) { - p.name() - hasNameList = true - } - if hasNameList { - p.syntaxError("name list not allowed in interface type") - // already progressed, no need to advance - } + const context = "interface method" switch p.tok { case _Lparen: // method f.Name = name - f.Type = p.funcType() + _, f.Type = p.funcType(context) case _Lbrack: - if p.mode&AllowGenerics != 0 { - // Careful dance: We don't know if we have a generic method m[T C](x T) - // or an embedded instantiated type T[P1, P2] (we accept generic methods - // for generality and robustness of parsing). + // Careful dance: We don't know if we have a generic method m[T C](x T) + // or an embedded instantiated type T[P1, P2] (we accept generic methods + // for generality and robustness of parsing but complain with an error). + pos := p.pos() + p.next() + + // Empty type parameter or argument lists are not permitted. + // Treat as if [] were absent. + if p.tok == _Rbrack { + // name[] pos := p.pos() p.next() - - // Empty type parameter or argument lists are not permitted. - // Treat as if [] were absent. - if p.tok == _Rbrack { - // name[] - pos := p.pos() - p.next() - if p.tok == _Lparen { - // name[]( - p.errorAt(pos, "empty type parameter list") - f.Name = name - f.Type = p.funcType() - } else { - p.errorAt(pos, "empty type argument list") - f.Type = name - } - break - } - - // A type argument list looks like a parameter list with only - // types. Parse a parameter list and decide afterwards. - list := p.paramList(nil, _Rbrack, false) - if len(list) == 0 { - // The type parameter list is not [] but we got nothing - // due to other errors (reported by paramList). Treat - // as if [] were absent. - if p.tok == _Lparen { - f.Name = name - f.Type = p.funcType() - } else { - f.Type = name - } - break - } - - // len(list) > 0 - if list[0].Name != nil { - // generic method + if p.tok == _Lparen { + // name[]( + p.errorAt(pos, "empty type parameter list") f.Name = name - f.Type = p.funcType() - // TODO(gri) Record list as type parameter list with f.Type - // if we want to type-check the generic method. - // For now, report an error so this is not a silent event. - p.errorAt(pos, "interface method cannot have type parameters") - break + _, f.Type = p.funcType(context) + } else { + p.errorAt(pos, "empty type argument list") + f.Type = name } + break + } - // embedded instantiated type - t := new(IndexExpr) - t.pos = pos - t.X = name - if len(list) == 1 { - t.Index = list[0].Type + // A type argument list looks like a parameter list with only + // types. Parse a parameter list and decide afterwards. + list := p.paramList(nil, nil, _Rbrack, false) + if len(list) == 0 { + // The type parameter list is not [] but we got nothing + // due to other errors (reported by paramList). Treat + // as if [] were absent. + if p.tok == _Lparen { + f.Name = name + _, f.Type = p.funcType(context) } else { - // len(list) > 1 - l := new(ListExpr) - l.pos = list[0].Pos() - l.ElemList = make([]Expr, len(list)) - for i := range list { - l.ElemList[i] = list[i].Type - } - t.Index = l + f.Type = name } - f.Type = t break } - fallthrough + + // len(list) > 0 + if list[0].Name != nil { + // generic method + f.Name = name + _, f.Type = p.funcType(context) + p.errorAt(pos, "interface method must have no type parameters") + break + } + + // embedded instantiated type + t := new(IndexExpr) + t.pos = pos + t.X = name + if len(list) == 1 { + t.Index = list[0].Type + } else { + // len(list) > 1 + l := new(ListExpr) + l.pos = list[0].Pos() + l.ElemList = make([]Expr, len(list)) + for i := range list { + l.ElemList[i] = list[i].Type + } + t.Index = l + } + f.Type = t default: // embedded type @@ -1788,7 +1843,7 @@ func (p *parser) embeddedTerm() Expr { t := p.typeOrNil() if t == nil { t = p.badExpr() - p.syntaxError("expecting ~ term or type") + p.syntaxError("expected ~ term or type") p.advance(_Operator, _Semi, _Rparen, _Rbrack, _Rbrace) } @@ -1796,39 +1851,70 @@ func (p *parser) embeddedTerm() Expr { } // ParameterDecl = [ IdentifierList ] [ "..." ] Type . -func (p *parser) paramDeclOrNil(name *Name) *Field { +func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { if trace { - defer p.trace("paramDecl")() + defer p.trace("paramDeclOrNil")() + } + + // type set notation is ok in type parameter lists + typeSetsOk := follow == _Rbrack + + pos := p.pos() + if name != nil { + pos = name.pos + } else if typeSetsOk && p.tok == _Operator && p.op == Tilde { + // "~" ... + return p.embeddedElem(nil) } f := new(Field) - f.pos = p.pos() + f.pos = pos if p.tok == _Name || name != nil { + // name if name == nil { name = p.name() } - if p.mode&AllowGenerics != 0 && p.tok == _Lbrack { + if p.tok == _Lbrack { + // name "[" ... f.Type = p.arrayOrTArgs() if typ, ok := f.Type.(*IndexExpr); ok { + // name "[" ... "]" typ.X = name } else { + // name "[" n "]" E f.Name = name } + if typeSetsOk && p.tok == _Operator && p.op == Or { + // name "[" ... "]" "|" ... + // name "[" n "]" E "|" ... + f = p.embeddedElem(f) + } return f } if p.tok == _Dot { - // name_or_type + // name "." ... f.Type = p.qualifiedName(name) + if typeSetsOk && p.tok == _Operator && p.op == Or { + // name "." name "|" ... + f = p.embeddedElem(f) + } return f } + if typeSetsOk && p.tok == _Operator && p.op == Or { + // name "|" ... + f.Type = name + return p.embeddedElem(f) + } + f.Name = name } if p.tok == _DotDotDot { + // [name] "..." ... t := new(DotsType) t.pos = p.pos() p.next() @@ -1841,13 +1927,23 @@ func (p *parser) paramDeclOrNil(name *Name) *Field { return f } + if typeSetsOk && p.tok == _Operator && p.op == Tilde { + // [name] "~" ... + f.Type = p.embeddedElem(nil).Type + return f + } + f.Type = p.typeOrNil() + if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil { + // [name] type "|" + f = p.embeddedElem(f) + } if f.Name != nil || f.Type != nil { return f } - p.syntaxError("expecting )") - p.advance(_Comma, _Rparen) + p.syntaxError("expected " + tokstring(follow)) + p.advance(_Comma, follow) return nil } @@ -1855,16 +1951,41 @@ func (p *parser) paramDeclOrNil(name *Name) *Field { // ParameterList = ParameterDecl { "," ParameterDecl } . // "(" or "[" has already been consumed. // If name != nil, it is the first name after "(" or "[". +// If typ != nil, name must be != nil, and (name, typ) is the first field in the list. // In the result list, either all fields have a name, or no field has a name. -func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*Field) { +func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) { if trace { defer p.trace("paramList")() } - var named int // number of parameters that have an explicit name and type/bound - p.list(_Comma, close, func() bool { - par := p.paramDeclOrNil(name) + // p.list won't invoke its function argument if we're at the end of the + // parameter list. If we have a complete field, handle this case here. + if name != nil && typ != nil && p.tok == close { + p.next() + par := new(Field) + par.pos = name.pos + par.Name = name + par.Type = typ + return []*Field{par} + } + + var named int // number of parameters that have an explicit name and type + var typed int // number of parameters that have an explicit type + end := p.list("parameter list", _Comma, close, func() bool { + var par *Field + if typ != nil { + if debug && name == nil { + panic("initial type provided without name") + } + par = new(Field) + par.pos = name.pos + par.Name = name + par.Type = typ + } else { + par = p.paramDeclOrNil(name, close) + } name = nil // 1st name was consumed if present + typ = nil // 1st type was consumed if present if par != nil { if debug && par.Name == nil && par.Type == nil { panic("parameter without name or type") @@ -1872,6 +1993,9 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* if par.Name != nil && par.Type != nil { named++ } + if par.Type != nil { + typed++ + } list = append(list, par) } return false @@ -1882,7 +2006,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* } // distribute parameter types (len(list) > 0) - if named == 0 { + if named == 0 && !requireNames { // all unnamed => found names are named types for _, par := range list { if typ := par.Name; typ != nil { @@ -1890,18 +2014,16 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* par.Name = nil } } - if requireNames { - p.syntaxErrorAt(list[0].Type.Pos(), "type parameters must be named") - } } else if named != len(list) { // some named => all must have names and types - var pos Pos // left-most error position (or unknown) - var typ Expr + var pos Pos // left-most error position (or unknown) + var typ Expr // current type (from right to left) for i := len(list) - 1; i >= 0; i-- { - if par := list[i]; par.Type != nil { + par := list[i] + if par.Type != nil { typ = par.Type if par.Name == nil { - pos = typ.Pos() + pos = StartPos(typ) par.Name = NewName(pos, "_") } } else if typ != nil { @@ -1917,7 +2039,12 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* if pos.IsKnown() { var msg string if requireNames { - msg = "type parameters must be named" + if named == typed { + pos = end // position error at closing ] + msg = "missing type constraint" + } else { + msg = "type parameters must be named" + } } else { msg = "mixed named and unnamed parameters" } @@ -2021,7 +2148,7 @@ func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt { return p.newAssignStmt(pos, op, lhs, rhs) default: - p.syntaxError("expecting := or = or comma") + p.syntaxError("expected := or = or comma") p.advance(_Semi, _Rbrace) // make the best of what we have if x, ok := lhs.(*ListExpr); ok { @@ -2096,7 +2223,7 @@ func (p *parser) blockStmt(context string) *BlockStmt { // people coming from C may forget that braces are mandatory in Go if !p.got(_Lbrace) { - p.syntaxError("expecting { after " + context) + p.syntaxError("expected { after " + context) p.advance(_Name, _Rbrace) s.Rbrace = p.pos() // in case we found "}" if p.got(_Rbrace) { @@ -2157,7 +2284,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS if p.tok != _Semi { // accept potential varDecl but complain if p.got(_Var) { - p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String())) + p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", tokstring(keyword))) } init = p.simpleStmt(nil, keyword) // If we have a range clause, we are done (can only happen for keyword == _For). @@ -2187,7 +2314,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS if keyword == _For { if p.tok != _Semi { if p.tok == _Lbrace { - p.syntaxError("expecting for loop condition") + p.syntaxError("expected for loop condition") goto done } condStmt = p.simpleStmt(nil, 0 /* range not permitted */) @@ -2213,7 +2340,7 @@ done: case nil: if keyword == _If && semi.pos.IsKnown() { if semi.lit != "semicolon" { - p.syntaxErrorAt(semi.pos, fmt.Sprintf("unexpected %s, expecting { after if clause", semi.lit)) + p.syntaxErrorAt(semi.pos, fmt.Sprintf("unexpected %s, expected { after if clause", semi.lit)) } else { p.syntaxErrorAt(semi.pos, "missing condition in if statement") } @@ -2332,7 +2459,7 @@ func (p *parser) caseClause() *CaseClause { p.next() default: - p.syntaxError("expecting case or default or }") + p.syntaxError("expected case or default or }") p.advance(_Colon, _Case, _Default, _Rbrace) } @@ -2365,14 +2492,12 @@ func (p *parser) commClause() *CommClause { // // All these (and more) are recognized by simpleStmt and invalid // syntax trees are flagged later, during type checking. - // TODO(gri) eventually may want to restrict valid syntax trees - // here. case _Default: p.next() default: - p.syntaxError("expecting case or default or }") + p.syntaxError("expected case or default or }") p.advance(_Colon, _Case, _Default, _Rbrace) } @@ -2383,11 +2508,13 @@ func (p *parser) commClause() *CommClause { return c } -// Statement = -// Declaration | LabeledStmt | SimpleStmt | -// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | -// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | -// DeferStmt . +// stmtOrNil parses a statement if one is present, or else returns nil. +// +// Statement = +// Declaration | LabeledStmt | SimpleStmt | +// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | +// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | +// DeferStmt . func (p *parser) stmtOrNil() Stmt { if trace { defer p.trace("stmt " + p.tok.String())() @@ -2524,7 +2651,7 @@ func (p *parser) argList() (list []Expr, hasDots bool) { } p.xnest++ - p.list(_Comma, _Rparen, func() bool { + p.list("argument list", _Comma, _Rparen, func() bool { list = append(list, p.expr()) hasDots = p.got(_DotDotDot) return hasDots @@ -2547,7 +2674,7 @@ func (p *parser) name() *Name { } n := NewName(p.pos(), "_") - p.syntaxError("expecting name") + p.syntaxError("expected name") p.advance() return n } @@ -2585,7 +2712,7 @@ func (p *parser) qualifiedName(name *Name) Expr { x = p.name() default: x = NewName(p.pos(), "_") - p.syntaxError("expecting name") + p.syntaxError("expected name") p.advance(_Dot, _Semi, _Rbrace) } @@ -2598,7 +2725,7 @@ func (p *parser) qualifiedName(name *Name) Expr { x = s } - if p.mode&AllowGenerics != 0 && p.tok == _Lbrack { + if p.tok == _Lbrack { x = p.typeInstance(x) } @@ -2625,21 +2752,25 @@ func (p *parser) exprList() Expr { return x } -// typeList parses a non-empty, comma-separated list of expressions, -// optionally followed by a comma. The first list element may be any -// expression, all other list elements must be type expressions. +// typeList parses a non-empty, comma-separated list of types, +// optionally followed by a comma. If strict is set to false, +// the first element may also be a (non-type) expression. // If there is more than one argument, the result is a *ListExpr. // The comma result indicates whether there was a (separating or // trailing) comma. // // typeList = arg { "," arg } [ "," ] . -func (p *parser) typeList() (x Expr, comma bool) { +func (p *parser) typeList(strict bool) (x Expr, comma bool) { if trace { defer p.trace("typeList")() } p.xnest++ - x = p.expr() + if strict { + x = p.type_() + } else { + x = p.expr() + } if p.got(_Comma) { comma = true if t := p.typeOrNil(); t != nil { diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 340ca6bb6f637b..b3d45739352329 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -8,6 +8,7 @@ import ( "bytes" "flag" "fmt" + "internal/testenv" "io/ioutil" "path/filepath" "regexp" @@ -26,35 +27,18 @@ var ( ) func TestParse(t *testing.T) { - ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) + ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) } func TestVerify(t *testing.T) { - ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) + ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) if err != nil { return // error already reported } verifyPrint(t, *src_, ast) } -func TestParseGo2(t *testing.T) { - dir := filepath.Join(testdata, "go2") - list, err := ioutil.ReadDir(dir) - if err != nil { - t.Fatal(err) - } - for _, fi := range list { - name := fi.Name() - if !fi.IsDir() && !strings.HasPrefix(name, ".") { - ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics) - } - } -} - -func TestStdLib(t *testing.T) { testStdLib(t, 0) } -func TestStdLibGeneric(t *testing.T) { testStdLib(t, AllowGenerics) } - -func testStdLib(t *testing.T, mode Mode) { +func TestStdLib(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode") } @@ -77,11 +61,14 @@ func testStdLib(t *testing.T, mode Mode) { lines uint } + goroot := testenv.GOROOT(t) + results := make(chan parseResult) go func() { defer close(results) for _, dir := range []string{ - runtime.GOROOT(), + filepath.Join(goroot, "src"), + filepath.Join(goroot, "misc"), } { walkDirs(t, dir, func(filename string) { if skipRx != nil && skipRx.MatchString(filename) { @@ -93,7 +80,7 @@ func testStdLib(t *testing.T, mode Mode) { if debug { fmt.Printf("parsing %s\n", filename) } - ast, err := ParseFile(filename, nil, nil, mode) + ast, err := ParseFile(filename, nil, nil, 0) if err != nil { t.Error(err) return diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go index baebcc995c759b..b5e53d268bd247 100644 --- a/src/cmd/compile/internal/syntax/pos.go +++ b/src/cmd/compile/internal/syntax/pos.go @@ -24,7 +24,7 @@ type Pos struct { func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line), sat32(col)} } // TODO(gri) IsKnown makes an assumption about linebase < 1. -// Maybe we should check for Base() != nil instead. +// Maybe we should check for Base() != nil instead. func (pos Pos) Pos() Pos { return pos } func (pos Pos) IsKnown() bool { return pos.line > 0 } @@ -133,13 +133,19 @@ type PosBase struct { pos Pos filename string line, col uint32 + trimmed bool // whether -trimpath has been applied } // NewFileBase returns a new PosBase for the given filename. // A file PosBase's position is relative to itself, with the // position being filename:1:1. func NewFileBase(filename string) *PosBase { - base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase} + return NewTrimmedFileBase(filename, false) +} + +// NewTrimmedFileBase is like NewFileBase, but allows specifying Trimmed. +func NewTrimmedFileBase(filename string, trimmed bool) *PosBase { + base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase, trimmed} base.pos.base = base return base } @@ -149,8 +155,8 @@ func NewFileBase(filename string) *PosBase { // the comment containing the line directive. For a directive in a line comment, // that position is the beginning of the next line (i.e., the newline character // belongs to the line comment). -func NewLineBase(pos Pos, filename string, line, col uint) *PosBase { - return &PosBase{pos, filename, sat32(line), sat32(col)} +func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint) *PosBase { + return &PosBase{pos, filename, sat32(line), sat32(col), trimmed} } func (base *PosBase) IsFileBase() bool { @@ -188,6 +194,13 @@ func (base *PosBase) Col() uint { return uint(base.col) } +func (base *PosBase) Trimmed() bool { + if base == nil { + return false + } + return base.trimmed +} + func sat32(x uint) uint32 { if x > PosMax { return PosMax diff --git a/src/cmd/compile/internal/syntax/positions.go b/src/cmd/compile/internal/syntax/positions.go index b00f86c67cdab3..93596559a02328 100644 --- a/src/cmd/compile/internal/syntax/positions.go +++ b/src/cmd/compile/internal/syntax/positions.go @@ -12,7 +12,7 @@ func StartPos(n Node) Pos { for m := n; ; { switch n := m.(type) { case nil: - panic("internal error: nil") + panic("nil node") // packages case *File: @@ -124,7 +124,7 @@ func EndPos(n Node) Pos { for m := n; ; { switch n := m.(type) { case nil: - panic("internal error: nil") + panic("nil node") // packages case *File: diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go index e557f5d9247b52..62de68ed661723 100644 --- a/src/cmd/compile/internal/syntax/printer.go +++ b/src/cmd/compile/internal/syntax/printer.go @@ -7,7 +7,6 @@ package syntax import ( - "bytes" "fmt" "io" "strings" @@ -44,10 +43,10 @@ func Fprint(w io.Writer, x Node, form Form) (n int, err error) { return } -// String is a convenience functions that prints n in ShortForm +// String is a convenience function that prints n in ShortForm // and returns the printed string. func String(n Node) string { - var buf bytes.Buffer + var buf strings.Builder _, err := Fprint(&buf, n, ShortForm) if err != nil { fmt.Fprintf(&buf, "<<< ERROR: %s", err) @@ -494,39 +493,16 @@ func (p *printer) printRawNode(n Node) { p.printSignature(n) case *InterfaceType: - // separate type list and method list - var types []Expr - var methods []*Field - for _, f := range n.MethodList { - if f.Name != nil && f.Name.Value == "type" { - types = append(types, f.Type) - } else { - // method or embedded interface - methods = append(methods, f) - } - } - - multiLine := len(n.MethodList) > 0 && p.linebreaks p.print(_Interface) - if multiLine { + if p.linebreaks && len(n.MethodList) > 1 { p.print(blank) - } - p.print(_Lbrace) - if multiLine { + p.print(_Lbrace) p.print(newline, indent) - } - if len(types) > 0 { - p.print(_Type, blank) - p.printExprList(types) - if len(methods) > 0 { - p.print(_Semi, blank) - } - } - if len(methods) > 0 { - p.printMethodList(methods) - } - if multiLine { + p.printMethodList(n.MethodList) p.print(outdent, newline) + } else { + p.print(_Lbrace) + p.printMethodList(n.MethodList) } p.print(_Rbrace) @@ -689,9 +665,7 @@ func (p *printer) printRawNode(n Node) { } p.print(n.Name) if n.TParamList != nil { - p.print(_Lbrack) - p.printFieldList(n.TParamList, nil, _Comma) - p.print(_Rbrack) + p.printParameterList(n.TParamList, _Type) } p.print(blank) if n.Alias { @@ -723,9 +697,7 @@ func (p *printer) printRawNode(n Node) { } p.print(n.Name) if n.TParamList != nil { - p.print(_Lbrack) - p.printFieldList(n.TParamList, nil, _Comma) - p.print(_Rbrack) + p.printParameterList(n.TParamList, _Func) } p.printSignature(n.Type) if n.Body != nil { @@ -910,38 +882,72 @@ func (p *printer) printDeclList(list []Decl) { } func (p *printer) printSignature(sig *FuncType) { - p.printParameterList(sig.ParamList) + p.printParameterList(sig.ParamList, 0) if list := sig.ResultList; list != nil { p.print(blank) if len(list) == 1 && list[0].Name == nil { p.printNode(list[0].Type) } else { - p.printParameterList(list) + p.printParameterList(list, 0) } } } -func (p *printer) printParameterList(list []*Field) { - p.print(_Lparen) - if len(list) > 0 { - for i, f := range list { - if i > 0 { - p.print(_Comma, blank) - } - if f.Name != nil { - p.printNode(f.Name) - if i+1 < len(list) { - f1 := list[i+1] - if f1.Name != nil && f1.Type == f.Type { - continue // no need to print type - } +// If tok != 0 print a type parameter list: tok == _Type means +// a type parameter list for a type, tok == _Func means a type +// parameter list for a func. +func (p *printer) printParameterList(list []*Field, tok token) { + open, close := _Lparen, _Rparen + if tok != 0 { + open, close = _Lbrack, _Rbrack + } + p.print(open) + for i, f := range list { + if i > 0 { + p.print(_Comma, blank) + } + if f.Name != nil { + p.printNode(f.Name) + if i+1 < len(list) { + f1 := list[i+1] + if f1.Name != nil && f1.Type == f.Type { + continue // no need to print type } - p.print(blank) } - p.printNode(f.Type) + p.print(blank) + } + p.printNode(unparen(f.Type)) // no need for (extra) parentheses around parameter types + } + // A type parameter list [P T] where the name P and the type expression T syntactically + // combine to another valid (value) expression requires a trailing comma, as in [P *T,] + // (or an enclosing interface as in [P interface(*T)]), so that the type parameter list + // is not parsed as an array length [P*T]. + if tok == _Type && len(list) == 1 && combinesWithName(list[0].Type) { + p.print(_Comma) + } + p.print(close) +} + +// combinesWithName reports whether a name followed by the expression x +// syntactically combines to another valid (value) expression. For instance +// using *T for x, "name *T" syntactically appears as the expression x*T. +// On the other hand, using P|Q or *P|~Q for x, "name P|Q" or name *P|~Q" +// cannot be combined into a valid (value) expression. +func combinesWithName(x Expr) bool { + switch x := x.(type) { + case *Operation: + if x.Y == nil { + // name *x.X combines to name*x.X if x.X is not a type element + return x.Op == Mul && !isTypeElem(x.X) } + // binary expressions + return combinesWithName(x.X) && !isTypeElem(x.Y) + case *ParenExpr: + // name(x) combines but we are making sure at + // the call site that x is never parenthesized. + panic("unexpected parenthesized expression") } - p.print(_Rparen) + return false } func (p *printer) printStmtList(list []Stmt, braces bool) { diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index ec4b1de573f6be..863713c12da50c 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -18,11 +18,7 @@ func TestPrint(t *testing.T) { t.Skip("skipping test in short mode") } - // provide a no-op error handler so parsing doesn't stop after first error - ast, err := ParseFile(*src_, func(error) {}, nil, 0) - if err != nil { - t.Error(err) - } + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) if ast != nil { Fprint(testOut(), ast, LineForm) @@ -57,50 +53,100 @@ func TestPrintError(t *testing.T) { } } -var stringTests = []string{ - "package p", - "package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )", - - // generic type declarations - "package p; type _[T any] struct{}", - "package p; type _[A, B, C interface{m()}] struct{}", - "package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{type int}] struct{}", +var stringTests = [][2]string{ + dup("package p"), + dup("package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )"), + + // generic type declarations (given type separated with blank from LHS) + dup("package p; type _[T any] struct{}"), + dup("package p; type _[A, B, C interface{m()}] struct{}"), + dup("package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}] struct{}"), + + dup("package p; type _[P *struct{}] struct{}"), + dup("package p; type _[P *T,] struct{}"), + dup("package p; type _[P *T, _ any] struct{}"), + {"package p; type _[P (*T),] struct{}", "package p; type _[P *T,] struct{}"}, + {"package p; type _[P (*T), _ any] struct{}", "package p; type _[P *T, _ any] struct{}"}, + {"package p; type _[P (T),] struct{}", "package p; type _[P T] struct{}"}, + {"package p; type _[P (T), _ any] struct{}", "package p; type _[P T, _ any] struct{}"}, + + {"package p; type _[P (*struct{})] struct{}", "package p; type _[P *struct{}] struct{}"}, + {"package p; type _[P ([]int)] struct{}", "package p; type _[P []int] struct{}"}, + {"package p; type _[P ([]int) | int] struct{}", "package p; type _[P []int | int] struct{}"}, + + // a type literal in an |-expression indicates a type parameter list (blank after type parameter list and type) + dup("package p; type _[P *[]int] struct{}"), + dup("package p; type _[P T | T] struct{}"), + dup("package p; type _[P T | T | T | T] struct{}"), + dup("package p; type _[P *T | T, Q T] struct{}"), + dup("package p; type _[P *[]T | T] struct{}"), + dup("package p; type _[P *T | T | T | T | ~T] struct{}"), + dup("package p; type _[P *T | T | T | ~T | T] struct{}"), + dup("package p; type _[P *T | T | struct{} | T] struct{}"), + dup("package p; type _[P <-chan int] struct{}"), + dup("package p; type _[P *T | struct{} | T] struct{}"), + + // a trailing comma always indicates a (possibly invalid) type parameter list (blank after type parameter list and type) + dup("package p; type _[P *T,] struct{}"), + dup("package p; type _[P *T | T,] struct{}"), + dup("package p; type _[P *T | <-T | T,] struct{}"), + + // slice/array type declarations (no blank between array length and element type) + dup("package p; type _ []byte"), + dup("package p; type _ [n]byte"), + dup("package p; type _ [P(T)]byte"), + dup("package p; type _ [P((T))]byte"), + dup("package p; type _ [P * *T]byte"), + dup("package p; type _ [P * T]byte"), + dup("package p; type _ [P(*T)]byte"), + dup("package p; type _ [P(**T)]byte"), + dup("package p; type _ [P * T - T]byte"), + dup("package p; type _ [P * T - T]byte"), + dup("package p; type _ [P * T | T]byte"), + dup("package p; type _ [P * T | <-T | T]byte"), // generic function declarations - "package p; func _[T any]()", - "package p; func _[A, B, C interface{m()}]()", - "package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{type int}]()", + dup("package p; func _[T any]()"), + dup("package p; func _[A, B, C interface{m()}]()"), + dup("package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}]()"), + + // generic functions with elided interfaces in type constraints + dup("package p; func _[P *T]() {}"), + dup("package p; func _[P *T | T | T | T | ~T]() {}"), + dup("package p; func _[P *T | T | struct{} | T]() {}"), + dup("package p; func _[P ~int, Q int | string]() {}"), + dup("package p; func _[P struct{f int}, Q *P]() {}"), // methods with generic receiver types - "package p; func (R[T]) _()", - "package p; func (*R[A, B, C]) _()", - "package p; func (_ *R[A, B, C]) _()", + dup("package p; func (R[T]) _()"), + dup("package p; func (*R[A, B, C]) _()"), + dup("package p; func (_ *R[A, B, C]) _()"), // channels - "package p; type _ chan chan int", - "package p; type _ chan (<-chan int)", - "package p; type _ chan chan<- int", + dup("package p; type _ chan chan int"), + dup("package p; type _ chan (<-chan int)"), + dup("package p; type _ chan chan<- int"), - "package p; type _ <-chan chan int", - "package p; type _ <-chan <-chan int", - "package p; type _ <-chan chan<- int", + dup("package p; type _ <-chan chan int"), + dup("package p; type _ <-chan <-chan int"), + dup("package p; type _ <-chan chan<- int"), - "package p; type _ chan<- chan int", - "package p; type _ chan<- <-chan int", - "package p; type _ chan<- chan<- int", + dup("package p; type _ chan<- chan int"), + dup("package p; type _ chan<- <-chan int"), + dup("package p; type _ chan<- chan<- int"), // TODO(gri) expand } func TestPrintString(t *testing.T) { - for _, want := range stringTests { - ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics) + for _, test := range stringTests { + ast, err := Parse(nil, strings.NewReader(test[0]), nil, nil, 0) if err != nil { t.Error(err) continue } - if got := String(ast); got != want { - t.Errorf("%q: got %q", want, got) + if got := String(ast); got != test[1] { + t.Errorf("%q: got %q", test[1], got) } } } @@ -140,10 +186,10 @@ var exprTests = [][2]string{ dup("func(int, float32) string"), dup("interface{m()}"), dup("interface{m() string; n(x int)}"), - dup("interface{type int}"), - dup("interface{type int, float64, string}"), - dup("interface{type int; m()}"), - dup("interface{type int, float64, string; m() string; n(x int)}"), + dup("interface{~int}"), + dup("interface{~int | ~float64 | ~string}"), + dup("interface{~int; m()}"), + dup("interface{~int | ~float64 | ~string; m() string; n(x int)}"), dup("map[string]int"), dup("chan E"), dup("<-chan E"), @@ -155,7 +201,7 @@ var exprTests = [][2]string{ dup("interface{~int}"), dup("interface{int | string}"), dup("interface{~int | ~string; float64; m()}"), - dup("interface{type a, b, c; ~int | ~string; float64; m()}"), + dup("interface{~a | ~b | ~c; ~int | ~string; float64; m()}"), dup("interface{~T[int, string] | string}"), // non-type expressions @@ -214,7 +260,7 @@ var exprTests = [][2]string{ func TestShortString(t *testing.T) { for _, test := range exprTests { src := "package p; var _ = " + test[0] - ast, err := Parse(nil, strings.NewReader(src), nil, nil, AllowGenerics) + ast, err := Parse(nil, strings.NewReader(src), nil, nil, 0) if err != nil { t.Errorf("%s: %s", test[0], err) continue diff --git a/src/cmd/compile/internal/syntax/scanner.go b/src/cmd/compile/internal/syntax/scanner.go index 218bc24e61fb7b..807d8383866dcb 100644 --- a/src/cmd/compile/internal/syntax/scanner.go +++ b/src/cmd/compile/internal/syntax/scanner.go @@ -39,8 +39,8 @@ type scanner struct { lit string // valid if tok is _Name, _Literal, or _Semi ("semicolon", "newline", or "EOF"); may be malformed if bad is true bad bool // valid if tok is _Literal, true if a syntax error occurred, lit may be malformed kind LitKind // valid if tok is _Literal - op Operator // valid if tok is _Operator, _AssignOp, or _IncOp - prec int // valid if tok is _Operator, _AssignOp, or _IncOp + op Operator // valid if tok is _Operator, _Star, _AssignOp, or _IncOp + prec int // valid if tok is _Operator, _Star, _AssignOp, or _IncOp } func (s *scanner) init(src io.Reader, errh func(line, col uint, msg string), mode uint) { diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go index f3d4c09ed5e045..83b102da9f564e 100644 --- a/src/cmd/compile/internal/syntax/syntax.go +++ b/src/cmd/compile/internal/syntax/syntax.go @@ -16,7 +16,6 @@ type Mode uint // Modes supported by the parser. const ( CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements - AllowGenerics ) // Error describes a syntax error. Error implements the error interface. @@ -64,7 +63,6 @@ type PragmaHandler func(pos Pos, blank bool, text string, current Pragma) Pragma // error, and the returned syntax tree is nil. // // If pragh != nil, it is called with each pragma encountered. -// func Parse(base *PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { defer func() { if p := recover(); p != nil { diff --git a/src/cmd/compile/internal/syntax/testdata/chans.go b/src/cmd/compile/internal/syntax/testdata/chans.go new file mode 100644 index 00000000000000..d4c4207a4caaf4 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/chans.go @@ -0,0 +1,66 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package chans + +import "runtime" + +// Ranger returns a Sender and a Receiver. The Receiver provides a +// Next method to retrieve values. The Sender provides a Send method +// to send values and a Close method to stop sending values. The Next +// method indicates when the Sender has been closed, and the Send +// method indicates when the Receiver has been freed. +// +// This is a convenient way to exit a goroutine sending values when +// the receiver stops reading them. +func Ranger[T any]() (*Sender[T], *Receiver[T]) { + c := make(chan T) + d := make(chan bool) + s := &Sender[T]{values: c, done: d} + r := &Receiver[T]{values: c, done: d} + runtime.SetFinalizer(r, r.finalize) + return s, r +} + +// A sender is used to send values to a Receiver. +type Sender[T any] struct { + values chan<- T + done <-chan bool +} + +// Send sends a value to the receiver. It returns whether any more +// values may be sent; if it returns false the value was not sent. +func (s *Sender[T]) Send(v T) bool { + select { + case s.values <- v: + return true + case <-s.done: + return false + } +} + +// Close tells the receiver that no more values will arrive. +// After Close is called, the Sender may no longer be used. +func (s *Sender[T]) Close() { + close(s.values) +} + +// A Receiver receives values from a Sender. +type Receiver[T any] struct { + values <-chan T + done chan<- bool +} + +// Next returns the next value from the channel. The bool result +// indicates whether the value is valid, or whether the Sender has +// been closed and no more values will be received. +func (r *Receiver[T]) Next() (T, bool) { + v, ok := <-r.values + return v, ok +} + +// finalize is a finalizer for the receiver. +func (r *Receiver[T]) finalize() { + close(r.done) +} diff --git a/src/cmd/compile/internal/syntax/testdata/fallthrough.go b/src/cmd/compile/internal/syntax/testdata/fallthrough.go new file mode 100644 index 00000000000000..851da81ea04584 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/fallthrough.go @@ -0,0 +1,55 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fallthroughs + +func _() { + var x int + switch x { + case 0: + fallthrough + + case 1: + fallthrough // ERROR fallthrough statement out of place + { + } + + case 2: + { + fallthrough // ERROR fallthrough statement out of place + } + + case 3: + for { + fallthrough // ERROR fallthrough statement out of place + } + + case 4: + fallthrough // trailing empty statements are ok + ; + ; + + case 5: + fallthrough + + default: + fallthrough // ERROR cannot fallthrough final case in switch + } + + fallthrough // ERROR fallthrough statement out of place + + if true { + fallthrough // ERROR fallthrough statement out of place + } + + for { + fallthrough // ERROR fallthrough statement out of place + } + + var t any + switch t.(type) { + case int: + fallthrough // ERROR cannot fallthrough in type switch + } +} diff --git a/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 b/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 deleted file mode 100644 index 0d27603a5837a2..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package linalg - -import "math" - -// Numeric is type bound that matches any numeric type. -// It would likely be in a constraints package in the standard library. -type Numeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - complex64, complex128 -} - -func DotProduct[T Numeric](s1, s2 []T) T { - if len(s1) != len(s2) { - panic("DotProduct: slices of unequal length") - } - var r T - for i := range s1 { - r += s1[i] * s2[i] - } - return r -} - -// NumericAbs matches numeric types with an Abs method. -type NumericAbs[T any] interface { - Numeric - - Abs() T -} - -// AbsDifference computes the absolute value of the difference of -// a and b, where the absolute value is determined by the Abs method. -func AbsDifference[T NumericAbs[T]](a, b T) T { - d := a - b - return d.Abs() -} - -// OrderedNumeric is a type bound that matches numeric types that support the < operator. -type OrderedNumeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64 -} - -// Complex is a type bound that matches the two complex types, which do not have a < operator. -type Complex interface { - type complex64, complex128 -} - -// OrderedAbs is a helper type that defines an Abs method for -// ordered numeric types. -type OrderedAbs[T OrderedNumeric] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] { - if a < 0 { - return -a - } - return a -} - -// ComplexAbs is a helper type that defines an Abs method for -// complex types. -type ComplexAbs[T Complex] T - -func (a ComplexAbs[T]) Abs() ComplexAbs[T] { - r := float64(real(a)) - i := float64(imag(a)) - d := math.Sqrt(r * r + i * i) - return ComplexAbs[T](complex(d, 0)) -} - -func OrderedAbsDifference[T OrderedNumeric](a, b T) T { - return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b))) -} - -func ComplexAbsDifference[T Complex](a, b T) T { - return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b))) -} diff --git a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 b/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 deleted file mode 100644 index e5cfba06125416..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains basic generic code snippets. - -package p - -// type parameter lists -type B[P any] struct{} -type _[P interface{}] struct{} -type _[P B] struct{} -type _[P B[P]] struct{} - -type _[A, B, C any] struct{} -type _[A, B, C B] struct{} -type _[A, B, C B[A, B, C]] struct{} -type _[A1, A2 B1, A3 B2, A4, A5, A6 B3] struct{} - -type _[A interface{}] struct{} -type _[A, B interface{ m() }] struct{} - -type _[A, B, C any] struct{} - -// in functions -func _[P any]() -func _[P interface{}]() -func _[P B]() -func _[P B[P]]() - -// in methods -func (T) _[P any]() -func (T) _[P interface{}]() -func (T) _[P B]() -func (T) _[P B[P]]() - -// type instantiations -type _ T[int] - -// in expressions -var _ = T[int]{} - -// in embedded types -type _ struct{ T[int] } - -// interfaces -type _ interface{ - m() - type int -} - -type _ interface{ - type int, float, string - type complex128 - underlying(underlying underlying) underlying -} - -type _ interface{ - T - T[int] -} - -// tricky cases -func _(T[P], T[P1, P2]) -func _(a [N]T) - -type _ struct{ - T[P] - T[P1, P2] - f [N] -} -type _ interface{ - m() - - // generic methods - disabled for now - // m[] /* ERROR empty type parameter list */ () - // m[ /* ERROR cannot have type parameters */ P any](P) - - // instantiated types - // T[] /* ERROR empty type argument list */ - T[P] - T[P1, P2] -} diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 deleted file mode 100644 index a422d5e5684efe..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type myInt int - -// Parameterized type declarations - -type T1[P any] P - -type T2[P any] struct { - f P - g int // int should still be in scope chain -} - -type List[P any] []P - -// Alias type declarations cannot have type parameters. Syntax error. -// TODO(gri) Disabled for now as we don't check syntax error here. -// type A1[P any] = /* ERROR cannot be alias */ P - -// But an alias may refer to a generic, uninstantiated type. -type A2 = List -var _ A2[int] -var _ A2 /* ERROR without instantiation */ - -type A3 = List[int] -var _ A3 - -// Parameterized type instantiations - -var x int -type _ x /* ERROR not a type */ [int] - -type _ int /* ERROR not a generic type */ [int] -type _ myInt /* ERROR not a generic type */ [int] - -// TODO(gri) better error messages -type _ T1[int] -type _ T1[x /* ERROR not a type */ ] -type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32] - -var _ T2[int] = T2[int]{} - -var _ List[int] = []int{1, 2, 3} -var _ List[[]int] = [][]int{{1, 2, 3}} -var _ List[List[List[int]]] - -// Parameterized types containing parameterized types - -type T3[P any] List[P] - -var _ T3[int] = T3[int](List[int]{1, 2, 3}) - -// Self-recursive generic types are not permitted - -type self1[P any] self1 /* ERROR illegal cycle */ [P] -type self2[P any] *self2[P] // this is ok diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 deleted file mode 100644 index 6e2104a5150da8..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type List[E any] []E -var _ List[List[List[int]]] -var _ List[List[List[int]]] = []List[List[int]]{} - -type ( - T1[P1 any] struct { - f1 T2[P1, float32] - } - - T2[P2, P3 any] struct { - f2 P2 - f3 P3 - } -) - -func _() { - var x1 T1[int] - var x2 T2[int, float32] - - x1.f1.f2 = 0 - x1.f1 = x2 -} - -type T3[P any] T1[T2[P, P]] - -func _() { - var x1 T3[int] - var x2 T2[int, int] - x1.f1.f2 = x2 -} - -func f[P any] (x P) List[P] { - return List[P]{x} -} - -var ( - _ []int = f(0) - _ []float32 = f[float32](10) - _ List[complex128] = f(1i) - _ []List[int] = f(List[int]{}) - _ List[List[int]] = []List[int]{} - _ = []List[int]{} -) - -// Parameterized types with methods - -func (l List[E]) Head() (_ E, _ bool) { - if len(l) > 0 { - return l[0], true - } - return -} - -// A test case for instantiating types with other types (extracted from map.go2) - -type Pair[K any] struct { - key K -} - -type Receiver[T any] struct { - values T -} - -type Iterator[K any] struct { - r Receiver[Pair[K]] -} - -func Values [T any] (r Receiver[T]) T { - return r.values -} - -func (it Iterator[K]) Next() K { - return Values[Pair[K]](it.r).key -} - -// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) - -type NumericAbs[T any] interface { - Abs() T -} - -func AbsDifference[T NumericAbs[T]](x T) - -type OrderedAbs[T any] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] - -func OrderedAbsDifference[T any](x T) { - AbsDifference(OrderedAbs[T](x)) -} - -// same code, reduced to essence - -func g[P interface{ m() P }](x P) - -type T4[P any] P - -func (_ T4[P]) m() T4[P] - -func _[Q any](x Q) { - g(T4[Q](x)) -} - -// Another test case that caused problems in the past - -type T5[_ interface { a() }, _ interface{}] struct{} - -type A[P any] struct{ x P } - -func (_ A[P]) a() {} - -var _ T5[A[int], int] - -// Invoking methods with parameterized receiver types uses -// type inference to determine the actual type arguments matching -// the receiver type parameters from the actual receiver argument. -// Go does implicit address-taking and dereferenciation depending -// on the actual receiver and the method's receiver type. To make -// type inference work, the type-checker matches "pointer-ness" -// of the actual receiver and the method's receiver type. -// The following code tests this mechanism. - -type R1[A any] struct{} -func (_ R1[A]) vm() -func (_ *R1[A]) pm() - -func _[T any](r R1[T], p *R1[T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -type R2[A, B any] struct{} -func (_ R2[A, B]) vm() -func (_ *R2[A, B]) pm() - -func _[T any](r R2[T, int], p *R2[string, T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -// An interface can (explicitly) declare at most one type list. -type _ interface { - m0() - type int, string, bool - type /* ERROR multiple type lists */ float32, float64 - m1() - m2() - type /* ERROR multiple type lists */ complex64, complex128 - type /* ERROR multiple type lists */ rune -} - -// Interface type lists may contain each type at most once. -// (If there are multiple lists, we assume the author intended -// for them to be all in a single list, and we report the error -// as well.) -type _ interface { - type int, int /* ERROR duplicate type int */ - type /* ERROR multiple type lists */ int /* ERROR duplicate type int */ -} - -type _ interface { - type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int} -} - -// Interface type lists can contain any type, incl. *Named types. -// Verify that we use the underlying type to compute the operational type. -type MyInt int -func add1[T interface{type MyInt}](x T) T { - return x + 1 -} - -type MyString string -func double[T interface{type MyInt, MyString}](x T) T { - return x + x -} - -// Embedding of interfaces with type lists leads to interfaces -// with type lists that are the intersection of the embedded -// type lists. - -type E0 interface { - type int, bool, string -} - -type E1 interface { - type int, float64, string -} - -type E2 interface { - type float64 -} - -type I0 interface { - E0 -} - -func f0[T I0]() -var _ = f0[int] -var _ = f0[bool] -var _ = f0[string] -var _ = f0[float64 /* ERROR does not satisfy I0 */ ] - -type I01 interface { - E0 - E1 -} - -func f01[T I01]() -var _ = f01[int] -var _ = f01[bool /* ERROR does not satisfy I0 */ ] -var _ = f01[string] -var _ = f01[float64 /* ERROR does not satisfy I0 */ ] - -type I012 interface { - E0 - E1 - E2 -} - -func f012[T I012]() -var _ = f012[int /* ERROR does not satisfy I012 */ ] -var _ = f012[bool /* ERROR does not satisfy I012 */ ] -var _ = f012[string /* ERROR does not satisfy I012 */ ] -var _ = f012[float64 /* ERROR does not satisfy I012 */ ] - -type I12 interface { - E1 - E2 -} - -func f12[T I12]() -var _ = f12[int /* ERROR does not satisfy I12 */ ] -var _ = f12[bool /* ERROR does not satisfy I12 */ ] -var _ = f12[string /* ERROR does not satisfy I12 */ ] -var _ = f12[float64] - -type I0_ interface { - E0 - type int -} - -func f0_[T I0_]() -var _ = f0_[int] -var _ = f0_[bool /* ERROR does not satisfy I0_ */ ] -var _ = f0_[string /* ERROR does not satisfy I0_ */ ] -var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ] diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 deleted file mode 100644 index f78037f0f5d033..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// import "io" // for type assertion tests - -// The predeclared identifier "any" is only visible as a constraint -// in a type parameter list. -var _ any // ERROR undeclared -func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) { - var _ any /* ERROR undeclared */ -} - -func identity[T any](x T) T { return x } - -func _[_ any](x int) int -func _[T any](T /* ERROR redeclared */ T)() -func _[T, T /* ERROR redeclared */ any]() - -func reverse[T any](list []T) []T { - rlist := make([]T, len(list)) - i := len(list) - for _, x := range list { - i-- - rlist[i] = x - } - return rlist -} - -var _ = reverse /* ERROR cannot use generic function reverse */ -var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3}) -var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3}) -var f = reverse[chan int] -var _ = f(0 /* ERROR cannot convert 0 .* to \[\]chan int */ ) - -func swap[A, B any](a A, b B) (B, A) { return b, a } - -var _ = swap /* ERROR single value is expected */ [int, float32](1, 2) -var f32, i = swap[int, float32](swap(float32, int)(1, 2)) -var _ float32 = f32 -var _ int = i - -func swapswap[A, B any](a A, b B) (A, B) { - return swap[B, A](b, a) -} - -type F[A, B any] func(A, B) (B, A) - -func min[T interface{ type int }](x, y T) T { - if x < y { - return x - } - return y -} - -func _[T interface{type int, float32}](x, y T) bool { return x < y } -func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y } - -func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T C2[T]](x, y T) bool { return x < y } - -type C1[T any] interface{} -type C2[T any] interface{ type int, float32 } - -func new[T any]() *T { - var x T - return &x -} - -var _ = new /* ERROR cannot use generic function new */ -var _ *int = new[int]() - -func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable - -func f1[T1 any](struct{T1}) int -var _ = f1(int)(struct{T1}{}) -type T1 = int - -func f2[t1 any](struct{t1; x float32}) int -var _ = f2(t1)(struct{t1; x float32}{}) -type t1 = int - - -func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int - -var _ = f3[int, rune, bool](1, struct{x rune}{}, nil) - -// indexing - -func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int }] (x T, i int) { _ = x[i] } -func _[T interface{ type [10]int, *[20]int, map[string]int }] (x T, i int) { _ = x[i] } -func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } - -// slicing -// TODO(gri) implement this - -func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] } - -// len/cap built-ins - -func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = len(x) } -func _[T interface{ type [10]int }](x T) { _ = len(x) } -func _[T interface{ type []byte }](x T) { _ = len(x) } -func _[T interface{ type map[int]int }](x T) { _ = len(x) } -func _[T interface{ type chan int }](x T) { _ = len(x) } -func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) } - -func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type [10]int }](x T) { _ = cap(x) } -func _[T interface{ type []byte }](x T) { _ = cap(x) } -func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type chan int }](x T) { _ = cap(x) } -func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) } - -// range iteration - -func _[T interface{}](x T) { - for range x /* ERROR cannot range */ {} -} - -func _[T interface{ type string, []string }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } - for i, e := range x /* ERROR must have the same element type */ { _ = i } - for _, e := range x /* ERROR must have the same element type */ {} - var e rune - _ = e - for _, (e) = range x /* ERROR must have the same element type */ {} -} - - -func _[T interface{ type string, []rune, map[int]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x { _ = i; _ = e } -} - -func _[T interface{ type string, []rune, map[string]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x /* ERROR must have the same key type */ { _ = e } -} - -func _[T interface{ type string, chan int }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value -} - -func _[T interface{ type string, chan<-int }](x T) { - for i := range x /* ERROR send-only channel */ { _ = i } -} - -// type inference checks - -var _ = new() /* ERROR cannot infer T */ - -func f4[A, B, C any](A, B) C - -var _ = f4(1, 2) /* ERROR cannot infer C */ -var _ = f4[int, float32, complex128](1, 2) - -func f5[A, B, C any](A, []*B, struct{f []C}) int - -var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{}) -var _ = f5(0, nil, struct{f []complex128}{}) // ERROR cannot infer -var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{}) - -func f6[A any](A, []A) int - -var _ = f6(0, nil) - -func f6nil[A any](A) int - -var _ = f6nil(nil) // ERROR cannot infer - -// type inference with variadic functions - -func f7[T any](...T) T - -var _ int = f7() /* ERROR cannot infer T */ -var _ int = f7(1) -var _ int = f7(1, 2) -var _ int = f7([]int{}...) -var _ int = f7 /* ERROR cannot use */ ([]float64{}...) -var _ float64 = f7([]float64{}...) -var _ = f7[float64](1, 2.3) -var _ = f7(float64(1), 2.3) -var _ = f7(1, 2.3 /* ERROR does not match */ ) -var _ = f7(1.2, 3 /* ERROR does not match */ ) - -func f8[A, B any](A, B, ...B) int - -var _ = f8(1) /* ERROR not enough arguments */ -var _ = f8(1, 2.3) -var _ = f8(1, 2.3, 3.4, 4.5) -var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) -var _ = f8(int, float64)(1, 2.3, 3.4, 4) - -var _ = f8(int, float64)(0, 0, nil...) // test case for #18268 - -// init functions cannot have type parameters - -func init() {} -func init[/* ERROR func init must have no type parameters */ _ any]() {} -func init[/* ERROR func init must have no type parameters */ P any]() {} - -type T struct {} - -func (T) m1() {} -// The type checker accepts method type parameters if configured accordingly. -func (T) m2[_ any]() {} -func (T) m3[P any]() {} - -// type inference across parameterized types - -type S1[P any] struct { f P } - -func f9[P any](x S1[P]) - -func _() { - f9[int](S1[int]{42}) - f9(S1[int]{42}) -} - -type S2[A, B, C any] struct{} - -func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) - -func _[P any]() { - f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[P, int, P]{}, S2[P, float32, bool]{}) -} - -// corner case for type inference -// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) - -func f11[T any]() - -func _() { - f11[int]() -} - -// the previous example was extracted from - -func f12[T interface{m() T}]() - -type A[T any] T - -func (a A[T]) m() A[T] - -func _[T any]() { - f12(A[T])() -} - -// method expressions - -func (_ S1[P]) m() - -func _() { - m := S1[int].m - m(struct { f int }{42}) -} - -func _[T any] (x T) { - m := S1[T].m - m(S1[T]{x}) -} - -// type parameters in methods (generalization) - -type R0 struct{} - -func (R0) _[T any](x T) -func (R0 /* ERROR invalid receiver */ ) _[R0 any]() // scope of type parameters starts at "func" - -type R1[A, B any] struct{} - -func (_ R1[A, B]) m0(A, B) -func (_ R1[A, B]) m1[T any](A, B, T) T -func (_ R1 /* ERROR not a generic type */ [R1, _]) _() -func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B) - -func _() { - var r R1[int, string] - r.m1[rune](42, "foo", 'a') - r.m1[rune](42, "foo", 1.2 /* ERROR truncated to rune */) - r.m1(42, "foo", 1.2) // using type inference - var _ float64 = r.m1(42, "foo", 1.2) -} - -type I1[A any] interface { - m1(A) -} - -var _ I1[int] = r1[int]{} - -type r1[T any] struct{} - -func (_ r1[T]) m1(T) - -type I2[A, B any] interface { - m1(A) - m2(A) B -} - -var _ I2[int, float32] = R2[int, float32]{} - -type R2[P, Q any] struct{} - -func (_ R2[X, Y]) m1(X) -func (_ R2[X, Y]) m2(X) Y - -// type assertions and type switches over generic types -// NOTE: These are currently disabled because it's unclear what the correct -// approach is, and one can always work around by assigning the variable to -// an interface first. - -// // ReadByte1 corresponds to the ReadByte example in the draft design. -// func ReadByte1[T io.Reader](r T) (byte, error) { -// if br, ok := r.(io.ByteReader); ok { -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // ReadBytes2 is like ReadByte1 but uses a type switch instead. -// func ReadByte2[T io.Reader](r T) (byte, error) { -// switch br := r.(type) { -// case io.ByteReader: -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // type assertions and type switches over generic types are strict -// type I3 interface { -// m(int) -// } -// -// type I4 interface { -// m() int // different signature from I3.m -// } -// -// func _[T I3](x I3, p T) { -// // type assertions and type switches over interfaces are not strict -// _ = x.(I4) -// switch x.(type) { -// case I4: -// } -// -// // type assertions and type switches over generic types are strict -// _ = p /* ERROR cannot have dynamic type I4 */.(I4) -// switch p.(type) { -// case I4 /* ERROR cannot have dynamic type I4 */ : -// } -// } - -// type assertions and type switches over generic types lead to errors for now - -func _[T any](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -func _[T interface{type int}](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -// error messages related to type bounds mention those bounds -type C[P any] interface{} - -func _[P C[P]] (x P) { - x.m /* ERROR x.m undefined */ () -} - -type I interface {} - -func _[P I] (x P) { - x.m /* ERROR interface I has no method m */ () -} - -func _[P interface{}] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} - -func _[P any] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} - -// automatic distinguishing between array and generic types -// NOTE: Disabled when using unified parameter list syntax. -/* -const P = 10 -type A1 [P]byte -func _(a A1) { - assert(len(a) == 10) -} - -type A2 [P]struct{ - f [P]byte -} -func _(a A2) { - assert(len(a) == 10) - assert(len(a[0].f) == 10) -} - -type A3 [P]func(x [P]A3) -func _(a A3) { - assert(len(a) == 10) -} - -type T2[P] struct{ P } -var _ T2[int] - -type T3[P] func(P) -var _ T3[int] -*/ \ No newline at end of file diff --git a/src/cmd/compile/internal/syntax/testdata/interface.go b/src/cmd/compile/internal/syntax/testdata/interface.go new file mode 100644 index 00000000000000..dbc41879896920 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/interface.go @@ -0,0 +1,74 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains test cases for interfaces containing +// constraint elements. + +package p + +type _ interface { + m() + E +} + +type _ interface { + m() + ~int + int | string + int | ~string + ~int | ~string +} + +type _ interface { + m() + ~int + T[int, string] | string + int | ~T[string, struct{}] + ~int | ~string +} + +type _ interface { + int + []byte + [10]int + struct{} + *int + func() + interface{} + map[string]int + chan T + chan<- T + <-chan T + T[int] +} + +type _ interface { + int | string + []byte | string + [10]int | string + struct{} | string + *int | string + func() | string + interface{} | string + map[string]int | string + chan T | string + chan<- T | string + <-chan T | string + T[int] | string +} + +type _ interface { + ~int | string + ~[]byte | string + ~[10]int | string + ~struct{} | string + ~*int | string + ~func() | string + ~interface{} | string + ~map[string]int | string + ~chan T | string + ~chan<- T | string + ~<-chan T | string + ~T[int] | string +} diff --git a/src/cmd/compile/internal/syntax/testdata/interface.go2 b/src/cmd/compile/internal/syntax/testdata/interface.go2 deleted file mode 100644 index a817327a43f4c2..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/interface.go2 +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains test cases for interfaces containing -// constraint elements. -// -// For now, we accept both ordinary type lists and the -// more complex constraint elements. - -package p - -type _ interface { - m() - type int - type int, string - E -} - -type _ interface { - m() - ~int - int | string - int | ~string - ~int | ~string -} - - -type _ interface { - m() - ~int - T[int, string] | string - int | ~T[string, struct{}] - ~int | ~string - type bool, int, float64 -} diff --git a/src/cmd/compile/internal/syntax/testdata/issue20789.go b/src/cmd/compile/internal/syntax/testdata/issue20789.go new file mode 100644 index 00000000000000..0d5988b9a608c2 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue20789.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure this doesn't crash the compiler. +// Line 9 must end in EOF for this test (no newline). + +package e +func([<-chan<-[func /* ERROR unexpected u */ u){go \ No newline at end of file diff --git a/src/cmd/compile/internal/syntax/testdata/issue20789.src b/src/cmd/compile/internal/syntax/testdata/issue20789.src deleted file mode 100644 index 5f150db7e6b842..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/issue20789.src +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Make sure this doesn't crash the compiler. -// Line 9 must end in EOF for this test (no newline). - -package e -func([<-chan<-[func /* ERROR unexpected u */ u){go /* ERROR must be function call */ \ No newline at end of file diff --git a/src/cmd/compile/internal/syntax/testdata/issue23385.src b/src/cmd/compile/internal/syntax/testdata/issue23385.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue23385.src rename to src/cmd/compile/internal/syntax/testdata/issue23385.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue23434.src b/src/cmd/compile/internal/syntax/testdata/issue23434.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue23434.src rename to src/cmd/compile/internal/syntax/testdata/issue23434.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue31092.src b/src/cmd/compile/internal/syntax/testdata/issue31092.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue31092.src rename to src/cmd/compile/internal/syntax/testdata/issue31092.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue43527.go b/src/cmd/compile/internal/syntax/testdata/issue43527.go new file mode 100644 index 00000000000000..dd2c9b1272f79d --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue43527.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + // 0 and 1-element []-lists are syntactically valid + _[A, B /* ERROR missing type constraint */ ] int + _[A, /* ERROR type parameters must be named */ interface{}] int + _[A, B, C /* ERROR missing type constraint */ ] int + _[A B, C /* ERROR missing type constraint */ ] int + _[A B, /* ERROR type parameters must be named */ interface{}] int + _[A B, /* ERROR type parameters must be named */ interface{}, C D] int + _[A B, /* ERROR type parameters must be named */ interface{}, C, D] int + _[A B, /* ERROR type parameters must be named */ interface{}, C, interface{}] int + _[A B, C interface{}, D, /* ERROR type parameters must be named */ interface{}] int +) + +// function type parameters use the same parsing routine - just have a couple of tests + +func _[A, B /* ERROR missing type constraint */ ]() {} +func _[A, /* ERROR type parameters must be named */ interface{}]() {} diff --git a/src/cmd/compile/internal/syntax/testdata/issue43674.src b/src/cmd/compile/internal/syntax/testdata/issue43674.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue43674.src rename to src/cmd/compile/internal/syntax/testdata/issue43674.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue46558.go b/src/cmd/compile/internal/syntax/testdata/issue46558.go new file mode 100644 index 00000000000000..a22b6008258464 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue46558.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. 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(s string) { + switch s[0] { + case 'a': + case s[2] { // ERROR unexpected { + case 'b': + } + } +} // ERROR non-declaration statement diff --git a/src/cmd/compile/internal/syntax/testdata/issue47704.go b/src/cmd/compile/internal/syntax/testdata/issue47704.go new file mode 100644 index 00000000000000..e4cdad148f62c0 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue47704.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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 _() { + _ = m[] // ERROR expected operand + _ = m[x,] + _ = m[x /* ERROR unexpected a */ a b c d] +} + +// test case from the issue +func f(m map[int]int) int { + return m[0 // ERROR expected comma, \: or \] + ] +} diff --git a/src/cmd/compile/internal/syntax/testdata/issue48382.go b/src/cmd/compile/internal/syntax/testdata/issue48382.go new file mode 100644 index 00000000000000..7c024a051f6cf6 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue48382.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type _ func /* ERROR function type must have no type parameters */ [ /* ERROR empty type parameter list */ ]() +type _ func /* ERROR function type must have no type parameters */ [ x /* ERROR missing type constraint */ ]() +type _ func /* ERROR function type must have no type parameters */ [P any]() + +var _ = (func /* ERROR function type must have no type parameters */ [P any]())(nil) +var _ = func /* ERROR function type must have no type parameters */ [P any]() {} + +type _ interface{ + m /* ERROR interface method must have no type parameters */ [P any]() +} diff --git a/src/cmd/compile/internal/syntax/testdata/issue49205.go b/src/cmd/compile/internal/syntax/testdata/issue49205.go new file mode 100644 index 00000000000000..bbcc950c5c7905 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue49205.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. 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 + +// test case from issue + +type _ interface{ + m /* ERROR unexpected int in interface type; possibly missing semicolon or newline or } */ int +} + +// other cases where the fix for this issue affects the error message + +const ( + x int = 10 /* ERROR unexpected literal "foo" in grouped declaration; possibly missing semicolon or newline or \) */ "foo" +) + +var _ = []int{1, 2, 3 /* ERROR unexpected int in composite literal; possibly missing comma or } */ int } + +type _ struct { + x y /* ERROR syntax error: unexpected comma in struct type; possibly missing semicolon or newline or } */ , +} + +func f(a, b c /* ERROR unexpected d in parameter list; possibly missing comma or \) */ d) { + f(a, b, c /* ERROR unexpected d in argument list; possibly missing comma or \) */ d) +} diff --git a/src/cmd/compile/internal/syntax/testdata/issue49482.go b/src/cmd/compile/internal/syntax/testdata/issue49482.go new file mode 100644 index 00000000000000..1fc303d169bd97 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue49482.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + // these need a comma to disambiguate + _[P *T,] struct{} + _[P *T, _ any] struct{} + _[P (*T),] struct{} + _[P (*T), _ any] struct{} + _[P (T),] struct{} + _[P (T), _ any] struct{} + + // these parse as name followed by type + _[P *struct{}] struct{} + _[P (*struct{})] struct{} + _[P ([]int)] struct{} + + // array declarations + _ [P(T)]struct{} + _ [P((T))]struct{} + _ [P * *T] struct{} // this could be a name followed by a type but it makes the rules more complicated + _ [P * T]struct{} + _ [P(*T)]struct{} + _ [P(**T)]struct{} + _ [P * T - T]struct{} + _ [P*T-T /* ERROR unexpected comma */ ,]struct{} + _ [10 /* ERROR unexpected comma */ ,]struct{} +) diff --git a/src/cmd/compile/internal/syntax/testdata/issue52391.go b/src/cmd/compile/internal/syntax/testdata/issue52391.go new file mode 100644 index 00000000000000..f2098ceadb2c46 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue52391.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type _ interface { + int + (int) + (*int) + *([]byte) + ~(int) + (int) | (string) + (int) | ~(string) + (/* ERROR unexpected ~ */ ~int) + (int /* ERROR unexpected \| */ | /* ERROR unexpected string */ string /* ERROR unexpected \) */ ) +} diff --git a/src/cmd/compile/internal/syntax/testdata/linalg.go b/src/cmd/compile/internal/syntax/testdata/linalg.go new file mode 100644 index 00000000000000..822d0287e7490a --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/linalg.go @@ -0,0 +1,83 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package linalg + +import "math" + +// Numeric is type bound that matches any numeric type. +// It would likely be in a constraints package in the standard library. +type Numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + float32 | ~float64 | + complex64 | ~complex128 +} + +func DotProduct[T Numeric](s1, s2 []T) T { + if len(s1) != len(s2) { + panic("DotProduct: slices of unequal length") + } + var r T + for i := range s1 { + r += s1[i] * s2[i] + } + return r +} + +// NumericAbs matches numeric types with an Abs method. +type NumericAbs[T any] interface { + Numeric + + Abs() T +} + +// AbsDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func AbsDifference[T NumericAbs[T]](a, b T) T { + d := a - b + return d.Abs() +} + +// OrderedNumeric is a type bound that matches numeric types that support the < operator. +type OrderedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + float32 | ~float64 +} + +// Complex is a type bound that matches the two complex types, which do not have a < operator. +type Complex interface { + ~complex64 | ~complex128 +} + +// OrderedAbs is a helper type that defines an Abs method for +// ordered numeric types. +type OrderedAbs[T OrderedNumeric] T + +func (a OrderedAbs[T]) Abs() OrderedAbs[T] { + if a < 0 { + return -a + } + return a +} + +// ComplexAbs is a helper type that defines an Abs method for +// complex types. +type ComplexAbs[T Complex] T + +func (a ComplexAbs[T]) Abs() ComplexAbs[T] { + r := float64(real(a)) + i := float64(imag(a)) + d := math.Sqrt(r * r + i * i) + return ComplexAbs[T](complex(d, 0)) +} + +func OrderedAbsDifference[T OrderedNumeric](a, b T) T { + return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b))) +} + +func ComplexAbsDifference[T Complex](a, b T) T { + return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b))) +} diff --git a/src/cmd/compile/internal/syntax/testdata/map.go b/src/cmd/compile/internal/syntax/testdata/map.go new file mode 100644 index 00000000000000..a508d214b8a4a0 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/map.go @@ -0,0 +1,112 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package orderedmap provides an ordered map, implemented as a binary tree. +package orderedmap + +import "chans" + +// Map is an ordered map. +type Map[K, V any] struct { + root *node[K, V] + compare func(K, K) int +} + +// node is the type of a node in the binary tree. +type node[K, V any] struct { + key K + val V + left, right *node[K, V] +} + +// New returns a new map. +func New[K, V any](compare func(K, K) int) *Map[K, V] { + return &Map[K, V]{compare: compare} +} + +// find looks up key in the map, and returns either a pointer +// to the node holding key, or a pointer to the location where +// such a node would go. +func (m *Map[K, V]) find(key K) **node[K, V] { + pn := &m.root + for *pn != nil { + switch cmp := m.compare(key, (*pn).key); { + case cmp < 0: + pn = &(*pn).left + case cmp > 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Returns true if this is a new key, false if already present. +func (m *Map[K, V]) Insert(key K, val V) bool { + pn := m.find(key) + if *pn != nil { + (*pn).val = val + return false + } + *pn = &node[K, V]{key: key, val: val} + return true +} + +// Find returns the value associated with a key, or zero if not present. +// The found result reports whether the key was found. +func (m *Map[K, V]) Find(key K) (V, bool) { + pn := m.find(key) + if *pn == nil { + var zero V // see the discussion of zero values, above + return zero, false + } + return (*pn).val, true +} + +// keyValue is a pair of key and value used when iterating. +type keyValue[K, V any] struct { + key K + val V +} + +// InOrder returns an iterator that does an in-order traversal of the map. +func (m *Map[K, V]) InOrder() *Iterator[K, V] { + sender, receiver := chans.Ranger[keyValue[K, V]]() + var f func(*node[K, V]) bool + f = func(n *node[K, V]) bool { + if n == nil { + return true + } + // Stop sending values if sender.Send returns false, + // meaning that nothing is listening at the receiver end. + return f(n.left) && + sender.Send(keyValue[K, V]{n.key, n.val}) && + f(n.right) + } + go func() { + f(m.root) + sender.Close() + }() + return &Iterator[K, V]{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator[K, V any] struct { + r *chans.Receiver[keyValue[K, V]] +} + +// Next returns the next key and value pair, and a boolean indicating +// whether they are valid or whether we have reached the end. +func (it *Iterator[K, V]) Next() (K, V, bool) { + keyval, ok := it.r.Next() + if !ok { + var zerok K + var zerov V + return zerok, zerov, false + } + return keyval.key, keyval.val, true +} diff --git a/src/cmd/compile/internal/syntax/testdata/go2/map2.go2 b/src/cmd/compile/internal/syntax/testdata/map2.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/go2/map2.go2 rename to src/cmd/compile/internal/syntax/testdata/map2.go diff --git a/src/cmd/compile/internal/syntax/testdata/sample.src b/src/cmd/compile/internal/syntax/testdata/sample.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/sample.src rename to src/cmd/compile/internal/syntax/testdata/sample.go diff --git a/src/cmd/compile/internal/syntax/testdata/slices.go b/src/cmd/compile/internal/syntax/testdata/slices.go new file mode 100644 index 00000000000000..92651095568d7e --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/slices.go @@ -0,0 +1,68 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package slices implements various slice algorithms. +package slices + +// Map turns a []T1 to a []T2 using a mapping function. +func Map[T1, T2 any](s []T1, f func(T1) T2) []T2 { + r := make([]T2, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} + +// Reduce reduces a []T1 to a single value using a reduction function. +func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1) T2) T2 { + r := initializer + for _, v := range s { + r = f(r, v) + } + return r +} + +// Filter filters values from a slice using a filter function. +func Filter[T any](s []T, f func(T) bool) []T { + var r []T + for _, v := range s { + if f(v) { + r = append(r, v) + } + } + return r +} + +// Example uses + +func limiter(x int) byte { + switch { + case x < 0: + return 0 + default: + return byte(x) + case x > 255: + return 255 + } +} + +var input = []int{-4, 68954, 7, 44, 0, -555, 6945} +var limited1 = Map[int, byte](input, limiter) +var limited2 = Map(input, limiter) // using type inference + +func reducer(x float64, y int) float64 { + return x + float64(y) +} + +var reduced1 = Reduce[int, float64](input, 0, reducer) +var reduced2 = Reduce(input, 1i, reducer) // using type inference +var reduced3 = Reduce(input, 1, reducer) // using type inference + +func filter(x int) bool { + return x&1 != 0 +} + +var filtered1 = Filter[int](input, filter) +var filtered2 = Filter(input, filter) // using type inference + diff --git a/src/cmd/compile/internal/syntax/testdata/smoketest.go b/src/cmd/compile/internal/syntax/testdata/smoketest.go new file mode 100644 index 00000000000000..6b3593ac7ad122 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/smoketest.go @@ -0,0 +1,73 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains basic generic code snippets. + +package p + +// type parameter lists +type B[P any] struct{} +type _[P interface{}] struct{} +type _[P B] struct{} +type _[P B[P]] struct{} + +type _[A, B, C any] struct{} +type _[A, B, C B] struct{} +type _[A, B, C B[A, B, C]] struct{} +type _[A1, A2 B1, A3 B2, A4, A5, A6 B3] struct{} + +type _[A interface{}] struct{} +type _[A, B interface{ m() }] struct{} + +type _[A, B, C any] struct{} + +// in functions +func _[P any]() +func _[P interface{}]() +func _[P B]() +func _[P B[P]]() + +// type instantiations +type _ T[int] + +// in expressions +var _ = T[int]{} + +// in embedded types +type _ struct{ T[int] } + +// interfaces +type _ interface { + m() + ~int +} + +type _ interface { + ~int | ~float | ~string + ~complex128 + underlying(underlying underlying) underlying +} + +type _ interface { + T + T[int] +} + +// tricky cases +func _(T[P], T[P1, P2]) +func _(a [N]T) + +type _ struct { + T[P] + T[P1, P2] + f[N] +} +type _ interface { + m() + + // instantiated types + T[ /* ERROR empty type argument list */ ] + T[P] + T[P1, P2] +} diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go b/src/cmd/compile/internal/syntax/testdata/tparams.go new file mode 100644 index 00000000000000..646fbbebc89408 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/tparams.go @@ -0,0 +1,46 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type t[a, b /* ERROR missing type constraint */ ] struct{} +type t[a t, b t, c /* ERROR missing type constraint */ ] struct{} +type t struct { + t [n]byte + t[a] + t[a, b] +} +type t interface { + t[a] + m /* ERROR method must have no type parameters */ [_ _, /* ERROR mixed */ _]() + t[a, b] +} + +func f[ /* ERROR empty type parameter list */ ]() +func f[a, b /* ERROR missing type constraint */ ]() +func f[a t, b t, c /* ERROR missing type constraint */ ]() + +func f[a b, /* ERROR expected ] */ 0] () + +// issue #49482 +type ( + t[a *[]int] struct{} + t[a *t,] struct{} + t[a *t|[]int] struct{} + t[a *t|t,] struct{} + t[a *t|~t,] struct{} + t[a *struct{}|t] struct{} + t[a *t|struct{}] struct{} + t[a *struct{}|~t] struct{} +) + +// issue #51488 +type ( + t[a *t|t,] struct{} + t[a *t|t, b t] struct{} + t[a *t|t] struct{} + t[a *[]t|t] struct{} + t[a ([]t)] struct{} + t[a ([]t)|t] struct{} +) diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go2 deleted file mode 100644 index 42031c32774f94..00000000000000 --- a/src/cmd/compile/internal/syntax/testdata/tparams.go2 +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type t[ /* ERROR type parameters must be named */ a, b] struct{} -type t[a t, b t, /* ERROR type parameters must be named */ c] struct{} -type t struct { - t [n]byte - t[a] - t[a, b] -} -type t interface { - t[a] - m /* ERROR method cannot have type parameters */ [_ _, /* ERROR mixed */ _]() - t[a, b] -} - -func f[ /* ERROR empty type parameter list */ ]() -func f[ /* ERROR type parameters must be named */ a, b]() -func f[a t, b t, /* ERROR type parameters must be named */ c]() diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go b/src/cmd/compile/internal/syntax/testdata/typeset.go new file mode 100644 index 00000000000000..fe5c3f45a815f9 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/typeset.go @@ -0,0 +1,91 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains test cases for typeset-only constraint elements. + +package p + +type ( + _[_ t] t + _[_ ~t] t + _[_ t|t] t + _[_ ~t|t] t + _[_ t|~t] t + _[_ ~t|~t] t + + _[_ t, _, _ t|t] t + _[_ t, _, _ ~t|t] t + _[_ t, _, _ t|~t] t + _[_ t, _, _ ~t|~t] t + + _[_ t.t] t + _[_ ~t.t] t + _[_ t.t|t.t] t + _[_ ~t.t|t.t] t + _[_ t.t|~t.t] t + _[_ ~t.t|~t.t] t + + _[_ t, _, _ t.t|t.t] t + _[_ t, _, _ ~t.t|t.t] t + _[_ t, _, _ t.t|~t.t] t + _[_ t, _, _ ~t.t|~t.t] t + + _[_ struct{}] t + _[_ ~struct{}] t + + _[_ struct{}|t] t + _[_ ~struct{}|t] t + _[_ struct{}|~t] t + _[_ ~struct{}|~t] t + + _[_ t|struct{}] t + _[_ ~t|struct{}] t + _[_ t|~struct{}] t + _[_ ~t|~struct{}] t + + // test cases for issue #49175 + _[_ []t]t + _[_ [1]t]t + _[_ ~[]t]t + _[_ ~[1]t]t + t [ /* ERROR type parameters must be named */ t[0]]t +) + +// test cases for issue #49174 +func _[_ t]() {} +func _[_ []t]() {} +func _[_ [1]t]() {} +func _[_ []t | t]() {} +func _[_ [1]t | t]() {} +func _[_ t | []t]() {} +func _[_ []t | []t]() {} +func _[_ [1]t | [1]t]() {} +func _[_ t[t] | t[t]]() {} + +// Single-expression type parameter lists and those that don't start +// with a (type parameter) name are considered array sizes. +// The term must be a valid expression (it could be a type incl. a +// tilde term) but the type-checker will complain. +type ( + _[t] t + _[t|t] t + + // These are invalid and the type-checker will complain. + _[~t] t + _[~t|t] t + _[t|~t] t + _[~t|~t] t +) + +type ( + _[_ t, t /* ERROR missing type constraint */ ] t + _[_ ~t, t /* ERROR missing type constraint */ ] t + _[_ t, /* ERROR type parameters must be named */ ~t] t + _[_ ~t, /* ERROR type parameters must be named */ ~t] t + + _[_ t|t, /* ERROR type parameters must be named */ t|t] t + _[_ ~t|t, /* ERROR type parameters must be named */ t|t] t + _[_ t|t, /* ERROR type parameters must be named */ ~t|t] t + _[_ ~t|t, /* ERROR type parameters must be named */ ~t|t] t +) diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go index 60eae36ec92491..6dece1aa5bacbf 100644 --- a/src/cmd/compile/internal/syntax/tokens.go +++ b/src/cmd/compile/internal/syntax/tokens.go @@ -93,8 +93,8 @@ func contains(tokset uint64, tok token) bool { type LitKind uint8 // TODO(gri) With the 'i' (imaginary) suffix now permitted on integer -// and floating-point numbers, having a single ImagLit does -// not represent the literal kind well anymore. Remove it? +// and floating-point numbers, having a single ImagLit does +// not represent the literal kind well anymore. Remove it? const ( IntLit LitKind = iota FloatLit diff --git a/src/cmd/compile/internal/syntax/walk.go b/src/cmd/compile/internal/syntax/walk.go index c26e97a0d8f887..8f1d566155d232 100644 --- a/src/cmd/compile/internal/syntax/walk.go +++ b/src/cmd/compile/internal/syntax/walk.go @@ -8,31 +8,73 @@ package syntax import "fmt" -// Walk traverses a syntax in pre-order: It starts by calling f(root); -// root must not be nil. If f returns false (== "continue"), Walk calls +// Inspect traverses an AST in pre-order: It starts by calling +// f(node); node must not be nil. If f returns true, Inspect invokes f +// recursively for each of the non-nil children of node, followed by a +// call of f(nil). +// +// See Walk for caveats about shared nodes. +func Inspect(root Node, f func(Node) bool) { + Walk(root, inspector(f)) +} + +type inspector func(Node) bool + +func (v inspector) Visit(node Node) Visitor { + if v(node) { + return v + } + return nil +} + +// Crawl traverses a syntax in pre-order: It starts by calling f(root); +// root must not be nil. If f returns false (== "continue"), Crawl calls // f recursively for each of the non-nil children of that node; if f -// returns true (== "stop"), Walk does not traverse the respective node's +// returns true (== "stop"), Crawl does not traverse the respective node's // children. +// +// See Walk for caveats about shared nodes. +// +// Deprecated: Use Inspect instead. +func Crawl(root Node, f func(Node) bool) { + Inspect(root, func(node Node) bool { + return node != nil && !f(node) + }) +} + +// Walk traverses an AST in pre-order: It starts by calling +// v.Visit(node); node must not be nil. If the visitor w returned by +// v.Visit(node) is not nil, Walk is invoked recursively with visitor +// w for each of the non-nil children of node, followed by a call of +// w.Visit(nil). +// // Some nodes may be shared among multiple parent nodes (e.g., types in // field lists such as type T in "a, b, c T"). Such shared nodes are // walked multiple times. // TODO(gri) Revisit this design. It may make sense to walk those nodes -// only once. A place where this matters is types2.TestResolveIdents. -func Walk(root Node, f func(Node) bool) { - w := walker{f} - w.node(root) +// only once. A place where this matters is types2.TestResolveIdents. +func Walk(root Node, v Visitor) { + walker{v}.node(root) +} + +// A Visitor's Visit method is invoked for each node encountered by Walk. +// If the result visitor w is not nil, Walk visits each of the children +// of node with the visitor w, followed by a call of w.Visit(nil). +type Visitor interface { + Visit(node Node) (w Visitor) } type walker struct { - f func(Node) bool + v Visitor } -func (w *walker) node(n Node) { +func (w walker) node(n Node) { if n == nil { - panic("invalid syntax tree: nil node") + panic("nil node") } - if w.f(n) { + w.v = w.v.Visit(n) + if w.v == nil { return } @@ -285,33 +327,35 @@ func (w *walker) node(n Node) { default: panic(fmt.Sprintf("internal error: unknown node type %T", n)) } + + w.v.Visit(nil) } -func (w *walker) declList(list []Decl) { +func (w walker) declList(list []Decl) { for _, n := range list { w.node(n) } } -func (w *walker) exprList(list []Expr) { +func (w walker) exprList(list []Expr) { for _, n := range list { w.node(n) } } -func (w *walker) stmtList(list []Stmt) { +func (w walker) stmtList(list []Stmt) { for _, n := range list { w.node(n) } } -func (w *walker) nameList(list []*Name) { +func (w walker) nameList(list []*Name) { for _, n := range list { w.node(n) } } -func (w *walker) fieldList(list []*Field) { +func (w walker) fieldList(list []*Field) { for _, n := range list { w.node(n) } diff --git a/src/cmd/compile/internal/test/abiutils_test.go b/src/cmd/compile/internal/test/abiutils_test.go index b752c486126dc3..3f8ee3dbe92555 100644 --- a/src/cmd/compile/internal/test/abiutils_test.go +++ b/src/cmd/compile/internal/test/abiutils_test.go @@ -33,6 +33,8 @@ func TestMain(m *testing.M) { base.Ctxt.DiagFunc = base.Errorf base.Ctxt.DiagFlush = base.FlushErrors base.Ctxt.Bso = bufio.NewWriter(os.Stdout) + types.LocalPkg = types.NewPkg("p", "local") + types.LocalPkg.Prefix = "p" types.PtrSize = ssagen.Arch.LinkArch.PtrSize types.RegSize = ssagen.Arch.LinkArch.RegSize typecheck.InitUniverse() @@ -245,7 +247,7 @@ func TestABIUtilsSliceString(t *testing.T) { // p6 int64, p6 []intr32) (r1 string, r2 int64, r3 string, r4 []int32) i32 := types.Types[types.TINT32] sli32 := types.NewSlice(i32) - str := types.New(types.TSTRING) + str := types.Types[types.TSTRING] i8 := types.Types[types.TINT8] i64 := types.Types[types.TINT64] ft := mkFuncType(nil, []*types.Type{sli32, i8, sli32, i8, str, i8, i64, sli32}, @@ -309,9 +311,9 @@ func TestABIUtilsInterfaces(t *testing.T) { ei := types.Types[types.TINTER] // interface{} pei := types.NewPtr(ei) // *interface{} fldt := mkFuncType(types.FakeRecvType(), []*types.Type{}, - []*types.Type{types.UntypedString}) - field := types.NewField(src.NoXPos, nil, fldt) - nei := types.NewInterface(types.LocalPkg, []*types.Field{field}) + []*types.Type{types.Types[types.TSTRING]}) + field := types.NewField(src.NoXPos, typecheck.Lookup("F"), fldt) + nei := types.NewInterface(types.LocalPkg, []*types.Field{field}, false) i16 := types.Types[types.TINT16] tb := types.Types[types.TBOOL] s1 := mkstruct([]*types.Type{i16, i16, tb}) @@ -322,12 +324,12 @@ func TestABIUtilsInterfaces(t *testing.T) { IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; bool } IN 1: R{ I3 I4 } spilloffset: 8 typ: interface {} IN 2: R{ I5 I6 } spilloffset: 24 typ: interface {} - IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { () untyped string } + IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { F() string } IN 4: R{ } offset: 0 typ: *interface {} - IN 5: R{ } offset: 8 typ: interface { () untyped string } + IN 5: R{ } offset: 8 typ: interface { F() string } IN 6: R{ } offset: 24 typ: int16 OUT 0: R{ I0 I1 } spilloffset: -1 typ: interface {} - OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { () untyped string } + OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { F() string } OUT 2: R{ I4 } spilloffset: -1 typ: *interface {} offsetToSpillArea: 32 spillAreaSize: 56 `) diff --git a/src/cmd/compile/internal/test/fixedbugs_test.go b/src/cmd/compile/internal/test/fixedbugs_test.go index 376b45edfcf00c..cd0d5fc3536e71 100644 --- a/src/cmd/compile/internal/test/fixedbugs_test.go +++ b/src/cmd/compile/internal/test/fixedbugs_test.go @@ -72,7 +72,7 @@ func TestIssue16214(t *testing.T) { t.Fatalf("could not write file: %v", err) } - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=main", "-S", "-o", filepath.Join(dir, "out.o"), src) out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("go tool compile: %v\n%s", err, out) diff --git a/src/cmd/compile/internal/test/float_test.go b/src/cmd/compile/internal/test/float_test.go index 884a983bdd7a57..c736f970f99417 100644 --- a/src/cmd/compile/internal/test/float_test.go +++ b/src/cmd/compile/internal/test/float_test.go @@ -170,6 +170,7 @@ func cvt8(a float32) int32 { } // make sure to cover int, uint cases (issue #16738) +// //go:noinline func cvt9(a float64) int { return int(a) diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go index 6f100033cf5b8b..fd3b489d1329d9 100644 --- a/src/cmd/compile/internal/test/inl_test.go +++ b/src/cmd/compile/internal/test/inl_test.go @@ -6,6 +6,7 @@ package test import ( "bufio" + "internal/buildcfg" "internal/testenv" "io" "math/bits" @@ -42,14 +43,11 @@ func TestIntendedInlining(t *testing.T) { "bucketMask", "bucketShift", "chanbuf", - "deferArgs", - "deferclass", "evacuated", "fastlog2", "fastrand", "float64bits", - "funcPC", - "getArgInfoFast", + "funcspdelta", "getm", "getMCache", "isDirectIface", @@ -65,20 +63,16 @@ func TestIntendedInlining(t *testing.T) { "subtract1", "subtractb", "tophash", - "totaldefersize", "(*bmap).keys", "(*bmap).overflow", "(*waitq).enqueue", + "funcInfo.entry", // GC-related ones "cgoInRange", "gclinkptr.ptr", "guintptr.ptr", - "heapBits.bits", - "heapBits.isPointer", - "heapBits.morePointers", - "heapBits.next", - "heapBitsForAddr", + "writeHeapBitsForAddr", "markBits.isMarked", "muintptr.ptr", "puintptr.ptr", @@ -126,13 +120,37 @@ func TestIntendedInlining(t *testing.T) { "FullRune", "FullRuneInString", "RuneLen", + "AppendRune", "ValidRune", }, "reflect": { + "Value.Bool", + "Value.Bytes", "Value.CanAddr", - "Value.CanSet", + "Value.CanComplex", + "Value.CanFloat", + "Value.CanInt", "Value.CanInterface", + "Value.CanSet", + "Value.CanUint", + "Value.Cap", + "Value.Complex", + "Value.Float", + "Value.Int", + "Value.Interface", + "Value.IsNil", "Value.IsValid", + "Value.Kind", + "Value.Len", + "Value.MapRange", + "Value.OverflowComplex", + "Value.OverflowFloat", + "Value.OverflowInt", + "Value.OverflowUint", + "Value.String", + "Value.Type", + "Value.Uint", + "Value.UnsafeAddr", "Value.pointer", "add", "align", @@ -158,14 +176,48 @@ func TestIntendedInlining(t *testing.T) { "net": { "(*UDPConn).ReadFromUDP", }, + "sync/atomic": { + // (*Bool).CompareAndSwap handled below. + "(*Bool).Load", + "(*Bool).Store", + "(*Bool).Swap", + "(*Int32).Add", + "(*Int32).CompareAndSwap", + "(*Int32).Load", + "(*Int32).Store", + "(*Int32).Swap", + "(*Int64).Add", + "(*Int64).CompareAndSwap", + "(*Int64).Load", + "(*Int64).Store", + "(*Int64).Swap", + "(*Uint32).Add", + "(*Uint32).CompareAndSwap", + "(*Uint32).Load", + "(*Uint32).Store", + "(*Uint32).Swap", + "(*Uint64).Add", + "(*Uint64).CompareAndSwap", + "(*Uint64).Load", + "(*Uint64).Store", + "(*Uint64).Swap", + "(*Uintptr).Add", + "(*Uintptr).CompareAndSwap", + "(*Uintptr).Load", + "(*Uintptr).Store", + "(*Uintptr).Swap", + // (*Pointer[T])'s methods' handled below. + }, } - if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" { + if runtime.GOARCH != "386" && runtime.GOARCH != "loong64" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" && runtime.GOARCH != "riscv64" { // nextFreeFast calls sys.Ctz64, which on 386 is implemented in asm and is not inlinable. // We currently don't have midstack inlining so nextFreeFast is also not inlinable on 386. - // On mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive + // On loong64, mips64x and riscv64, Ctz64 is not intrinsified and causes nextFreeFast too expensive // to inline (Issue 22239). want["runtime"] = append(want["runtime"], "nextFreeFast") + // Same behavior for heapBits.nextFast. + want["runtime"] = append(want["runtime"], "heapBits.nextFast") } if runtime.GOARCH != "386" { // As explained above, Ctz64 and Ctz32 are not Go code on 386. @@ -177,6 +229,16 @@ func TestIntendedInlining(t *testing.T) { if bits.UintSize == 64 { // mix is only defined on 64-bit architectures want["runtime"] = append(want["runtime"], "mix") + // (*Bool).CompareAndSwap is just over budget on 32-bit systems (386, arm). + want["sync/atomic"] = append(want["sync/atomic"], "(*Bool).CompareAndSwap") + } + if buildcfg.Experiment.Unified { + // Non-unified IR does not report "inlining call ..." for atomic.Pointer[T]'s methods. + // TODO(cuonglm): remove once non-unified IR frontend gone. + want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).CompareAndSwap") + want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).Load") + want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).Store") + want["sync/atomic"] = append(want["sync/atomic"], "(*Pointer[go.shape.int]).Swap") } switch runtime.GOARCH { @@ -215,7 +277,7 @@ func TestIntendedInlining(t *testing.T) { } } - args := append([]string{"build", "-a", "-gcflags=all=-m -m", "-tags=math_big_pure_go"}, pkgs...) + args := append([]string{"build", "-gcflags=-m -m", "-tags=math_big_pure_go"}, pkgs...) cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) pr, pw := io.Pipe() cmd.Stdout = pw diff --git a/src/cmd/compile/internal/test/inst_test.go b/src/cmd/compile/internal/test/inst_test.go new file mode 100644 index 00000000000000..d171bd51111fc6 --- /dev/null +++ b/src/cmd/compile/internal/test/inst_test.go @@ -0,0 +1,69 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "testing" +) + +// TestInst tests that only one instantiation of Sort is created, even though generic +// Sort is used for multiple pointer types across two packages. +func TestInst(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveGoRun(t) + + var tmpdir string + var err error + tmpdir, err = ioutil.TempDir("", "TestDict") + if err != nil { + t.Fatalf("Failed to create temporary directory: %v", err) + } + defer os.RemoveAll(tmpdir) + + // Build ptrsort.go, which uses package mysort. + var output []byte + filename := "ptrsort.go" + exename := "ptrsort" + outname := "ptrsort.out" + gotool := testenv.GoToolPath(t) + dest := filepath.Join(tmpdir, exename) + cmd := exec.Command(gotool, "build", "-o", dest, filepath.Join("testdata", filename)) + if output, err = cmd.CombinedOutput(); err != nil { + t.Fatalf("Failed: %v:\nOutput: %s\n", err, output) + } + + // Test that there is exactly one shape-based instantiation of Sort in + // the executable. + cmd = exec.Command(gotool, "tool", "nm", dest) + if output, err = cmd.CombinedOutput(); err != nil { + t.Fatalf("Failed: %v:\nOut: %s\n", err, output) + } + // Look for shape-based instantiation of Sort, but ignore any extra wrapper + // ending in "-tramp" (which are created on riscv). + re := regexp.MustCompile(`\bSort\[.*shape.*\][^-]`) + r := re.FindAllIndex(output, -1) + if len(r) != 1 { + t.Fatalf("Wanted 1 instantiations of Sort function, got %d\n", len(r)) + } + + // Actually run the test and make sure output is correct. + cmd = exec.Command(gotool, "run", filepath.Join("testdata", filename)) + if output, err = cmd.CombinedOutput(); err != nil { + t.Fatalf("Failed: %v:\nOut: %s\n", err, output) + } + out, err := ioutil.ReadFile(filepath.Join("testdata", outname)) + if err != nil { + t.Fatalf("Could not find %s\n", outname) + } + if string(out) != string(output) { + t.Fatalf("Wanted output %v, got %v\n", string(out), string(output)) + } +} diff --git a/src/cmd/compile/internal/test/intrinsics_test.go b/src/cmd/compile/internal/test/intrinsics_test.go new file mode 100644 index 00000000000000..b89198c5058d85 --- /dev/null +++ b/src/cmd/compile/internal/test/intrinsics_test.go @@ -0,0 +1,62 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "math/bits" + "testing" +) + +func TestBitLen64(t *testing.T) { + for i := 0; i <= 64; i++ { + got := bits.Len64(1 << i) + want := i + 1 + if want == 65 { + want = 0 + } + if got != want { + t.Errorf("Len64(1<<%d) = %d, want %d", i, got, want) + } + } +} + +func TestBitLen32(t *testing.T) { + for i := 0; i <= 32; i++ { + got := bits.Len32(1 << i) + want := i + 1 + if want == 33 { + want = 0 + } + if got != want { + t.Errorf("Len32(1<<%d) = %d, want %d", i, got, want) + } + } +} + +func TestBitLen16(t *testing.T) { + for i := 0; i <= 16; i++ { + got := bits.Len16(1 << i) + want := i + 1 + if want == 17 { + want = 0 + } + if got != want { + t.Errorf("Len16(1<<%d) = %d, want %d", i, got, want) + } + } +} + +func TestBitLen8(t *testing.T) { + for i := 0; i <= 8; i++ { + got := bits.Len8(1 << i) + want := i + 1 + if want == 9 { + want = 0 + } + if got != want { + t.Errorf("Len8(1<<%d) = %d, want %d", i, got, want) + } + } +} diff --git a/src/cmd/compile/internal/test/issue50182_test.go b/src/cmd/compile/internal/test/issue50182_test.go new file mode 100644 index 00000000000000..cd277fa285eab5 --- /dev/null +++ b/src/cmd/compile/internal/test/issue50182_test.go @@ -0,0 +1,62 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "fmt" + "sort" + "testing" +) + +// Test that calling methods on generic types doesn't cause allocations. +func genericSorted[T sort.Interface](data T) bool { + n := data.Len() + for i := n - 1; i > 0; i-- { + if data.Less(i, i-1) { + return false + } + } + return true +} +func TestGenericSorted(t *testing.T) { + var data = sort.IntSlice{-10, -5, 0, 1, 2, 3, 5, 7, 11, 100, 100, 100, 1000, 10000} + f := func() { + genericSorted(data) + } + if n := testing.AllocsPerRun(10, f); n > 0 { + t.Errorf("got %f allocs, want 0", n) + } +} + +// Test that escape analysis correctly tracks escaping inside of methods +// called on generic types. +type fooer interface { + foo() +} +type P struct { + p *int + q int +} + +var esc []*int + +func (p P) foo() { + esc = append(esc, p.p) // foo escapes the pointer from inside of p +} +func f[T fooer](t T) { + t.foo() +} +func TestGenericEscape(t *testing.T) { + for i := 0; i < 4; i++ { + var x int = 77 + i + var p P = P{p: &x} + f(p) + } + for i, p := range esc { + if got, want := *p, 77+i; got != want { + panic(fmt.Sprintf("entry %d: got %d, want %d", i, got, want)) + } + } +} diff --git a/src/cmd/compile/internal/test/issue53888_test.go b/src/cmd/compile/internal/test/issue53888_test.go new file mode 100644 index 00000000000000..0d5b13b5c877b2 --- /dev/null +++ b/src/cmd/compile/internal/test/issue53888_test.go @@ -0,0 +1,46 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !race + +package test + +import ( + "internal/testenv" + "testing" +) + +func TestAppendOfMake(t *testing.T) { + testenv.SkipIfOptimizationOff(t) + for n := 32; n < 33; n++ { // avoid stack allocation of make() + b := make([]byte, n) + f := func() { + b = append(b[:0], make([]byte, n)...) + } + if n := testing.AllocsPerRun(10, f); n > 0 { + t.Errorf("got %f allocs, want 0", n) + } + type S []byte + + s := make(S, n) + g := func() { + s = append(s[:0], make(S, n)...) + } + if n := testing.AllocsPerRun(10, g); n > 0 { + t.Errorf("got %f allocs, want 0", n) + } + h := func() { + s = append(s[:0], make([]byte, n)...) + } + if n := testing.AllocsPerRun(10, h); n > 0 { + t.Errorf("got %f allocs, want 0", n) + } + i := func() { + b = append(b[:0], make(S, n)...) + } + if n := testing.AllocsPerRun(10, i); n > 0 { + t.Errorf("got %f allocs, want 0", n) + } + } +} diff --git a/src/cmd/compile/internal/test/lang_test.go b/src/cmd/compile/internal/test/lang_test.go index 67c15512922983..66ab8401c680fa 100644 --- a/src/cmd/compile/internal/test/lang_test.go +++ b/src/cmd/compile/internal/test/lang_test.go @@ -56,7 +56,7 @@ func TestInvalidLang(t *testing.T) { } func testLang(t *testing.T, lang, src, outfile string) error { - run := []string{testenv.GoToolPath(t), "tool", "compile", "-lang", lang, "-o", outfile, src} + run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=p", "-lang", lang, "-o", outfile, src} t.Log(run) out, err := exec.Command(run[0], run[1:]...).CombinedOutput() t.Logf("%s", out) diff --git a/src/cmd/compile/internal/test/pgo_inl_test.go b/src/cmd/compile/internal/test/pgo_inl_test.go new file mode 100644 index 00000000000000..d5ca15667a3b03 --- /dev/null +++ b/src/cmd/compile/internal/test/pgo_inl_test.go @@ -0,0 +1,154 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "bufio" + "bytes" + "internal/testenv" + "io" + "io/ioutil" + "os/exec" + "path/filepath" + "regexp" + "strings" + "testing" +) + +// TestIntendedInlining tests that specific functions are inlined. +// This allows refactoring for code clarity and re-use without fear that +// changes to the compiler will cause silent performance regressions. +func TestPGOIntendedInlining(t *testing.T) { + testenv.MustHaveGoRun(t) + t.Parallel() + + // Make a temporary directory to work in. + tmpdir, err := ioutil.TempDir("", "TestCode") + if err != nil { + t.Fatalf("Failed to create temporary directory: %v", err) + } + //defer os.RemoveAll(tmpdir) + + want := map[string][]string{ + "pgo/inline": { + "(*BS).NS", + }, + } + + // The functions which are not expected to be inlined are as follows. + wantNot := map[string][]string{ + "pgo/inline": { + // The calling edge main->A is hot and the cost of A is large than + // inlineHotCalleeMaxBudget. + "A", + // The calling edge BenchmarkA" -> benchmarkB is cold + // and the cost of A is large than inlineMaxBudget. + "benchmarkB", + }, + } + + must := map[string]bool{ + "(*BS).NS": true, + } + + notInlinedReason := make(map[string]string) + pkgs := make([]string, 0, len(want)) + for pname, fnames := range want { + pkgs = append(pkgs, pname) + for _, fname := range fnames { + fullName := pname + "." + fname + if _, ok := notInlinedReason[fullName]; ok { + t.Errorf("duplicate func: %s", fullName) + } + notInlinedReason[fullName] = "unknown reason" + } + } + + // If the compiler emit "cannot inline for function A", the entry A + // in expectedNotInlinedList will be removed. + expectedNotInlinedList := make(map[string]struct{}) + for pname, fnames := range wantNot { + for _, fname := range fnames { + fullName := pname + "." + fname + expectedNotInlinedList[fullName] = struct{}{} + } + } + + args := append([]string{"test", "-o", filepath.Join(tmpdir, "inline_hot.test"), "-bench=.", "-cpuprofile", filepath.Join(tmpdir, "inline_hot.pprof")}, pkgs...) + gotool := testenv.GoToolPath(t) + cmd := exec.Command(gotool, args...) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err = cmd.Run() + if err != nil { + t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr) + } + + args = append([]string{"test", "-run=nope", "-tags=", "-timeout=9m0s", "-gcflags=-m -m -profileuse " + filepath.Join(tmpdir, "inline_hot.pprof")}, pkgs...) + cmd = testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) + + pr, pw := io.Pipe() + cmd.Stdout = pw + cmd.Stderr = pw + cmdErr := make(chan error, 1) + go func() { + cmdErr <- cmd.Run() + pw.Close() + }() + scanner := bufio.NewScanner(pr) + curPkg := "" + canInline := regexp.MustCompile(`: can inline ([^ ]*)`) + haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`) + cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "# ") { + curPkg = line[2:] + splits := strings.Split(curPkg, " ") + curPkg = splits[0] + continue + } + if m := haveInlined.FindStringSubmatch(line); m != nil { + fname := m[1] + delete(notInlinedReason, curPkg+"."+fname) + continue + } + if m := canInline.FindStringSubmatch(line); m != nil { + fname := m[1] + fullname := curPkg + "." + fname + // If function must be inlined somewhere, being inlinable is not enough + if _, ok := must[fullname]; !ok { + delete(notInlinedReason, fullname) + continue + } + } + if m := cannotInline.FindStringSubmatch(line); m != nil { + fname, reason := m[1], m[2] + fullName := curPkg + "." + fname + if _, ok := notInlinedReason[fullName]; ok { + // cmd/compile gave us a reason why + notInlinedReason[fullName] = reason + } + delete(expectedNotInlinedList, fullName) + continue + } + } + if err := <-cmdErr; err != nil { + t.Fatal(err) + } + if err := scanner.Err(); err != nil { + t.Fatal(err) + } + for fullName, reason := range notInlinedReason { + t.Errorf("%s was not inlined: %s", fullName, reason) + } + + // If the list expectedNotInlinedList is not empty, it indicates + // the functions in the expectedNotInlinedList are marked with caninline. + for fullName, _ := range expectedNotInlinedList { + t.Errorf("%s was expected not inlined", fullName) + } +} diff --git a/src/cmd/compile/internal/test/reproduciblebuilds_test.go b/src/cmd/compile/internal/test/reproduciblebuilds_test.go index 4d84f9cdeffc9a..10913ae32fc3a5 100644 --- a/src/cmd/compile/internal/test/reproduciblebuilds_test.go +++ b/src/cmd/compile/internal/test/reproduciblebuilds_test.go @@ -41,7 +41,7 @@ func TestReproducibleBuilds(t *testing.T) { for i := 0; i < iters; i++ { // Note: use -c 2 to expose any nondeterminism which is the result // of the runtime scheduler. - out, err := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-c", "2", "-o", tmp.Name(), filepath.Join("testdata", "reproducible", test)).CombinedOutput() + out, err := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=p", "-c", "2", "-o", tmp.Name(), filepath.Join("testdata", "reproducible", test)).CombinedOutput() if err != nil { t.Fatalf("failed to compile: %v\n%s", err, out) } @@ -68,7 +68,7 @@ func TestIssue38068(t *testing.T) { // Compile a small package with and without the concurrent // backend, then check to make sure that the resulting archives // are identical. Note: this uses "go tool compile" instead of - // "go build" since the latter will generate differnent build IDs + // "go build" since the latter will generate different build IDs // if it sees different command line flags. scenarios := []struct { tag string @@ -89,7 +89,7 @@ func TestIssue38068(t *testing.T) { s := &scenarios[i] s.libpath = filepath.Join(tmpdir, s.tag+".a") // Note: use of "-p" required in order for DWARF to be generated. - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-trimpath", "-p=issue38068", "-buildid=", s.args, "-o", s.libpath, src) + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=issue38068", "-buildid=", s.args, "-o", s.libpath, src) out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("%v: %v:\n%s", cmd.Args, err, out) diff --git a/src/cmd/compile/internal/test/shift_test.go b/src/cmd/compile/internal/test/shift_test.go index ea88f0a70ae223..278a47da29cd53 100644 --- a/src/cmd/compile/internal/test/shift_test.go +++ b/src/cmd/compile/internal/test/shift_test.go @@ -1029,3 +1029,35 @@ func TestShiftGeneric(t *testing.T) { } } } + +var shiftSink64 int64 + +func BenchmarkShiftArithmeticRight(b *testing.B) { + x := shiftSink64 + for i := 0; i < b.N; i++ { + x = x >> (i & 63) + } + shiftSink64 = x +} + +//go:noinline +func incorrectRotate1(x, c uint64) uint64 { + // This should not compile to a rotate instruction. + return x<>(64-c) +} + +//go:noinline +func incorrectRotate2(x uint64) uint64 { + var c uint64 = 66 + // This should not compile to a rotate instruction. + return x<>(64-c) +} + +func TestIncorrectRotate(t *testing.T) { + if got := incorrectRotate1(1, 66); got != 0 { + t.Errorf("got %x want 0", got) + } + if got := incorrectRotate2(1); got != 0 { + t.Errorf("got %x want 0", got) + } +} diff --git a/src/cmd/compile/internal/test/ssa_test.go b/src/cmd/compile/internal/test/ssa_test.go index 2f3e24c2d37ca0..af7d9626f95f9e 100644 --- a/src/cmd/compile/internal/test/ssa_test.go +++ b/src/cmd/compile/internal/test/ssa_test.go @@ -162,7 +162,7 @@ func TestCode(t *testing.T) { } flags := []string{""} - if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" { + if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" || runtime.GOARCH == "386" { flags = append(flags, ",softfloat") } for _, flag := range flags { diff --git a/src/cmd/compile/internal/test/switch_test.go b/src/cmd/compile/internal/test/switch_test.go new file mode 100644 index 00000000000000..30dee6257e3804 --- /dev/null +++ b/src/cmd/compile/internal/test/switch_test.go @@ -0,0 +1,137 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "math/bits" + "testing" +) + +func BenchmarkSwitch8Predictable(b *testing.B) { + benchmarkSwitch8(b, true) +} +func BenchmarkSwitch8Unpredictable(b *testing.B) { + benchmarkSwitch8(b, false) +} +func benchmarkSwitch8(b *testing.B, predictable bool) { + n := 0 + rng := newRNG() + for i := 0; i < b.N; i++ { + rng = rng.next(predictable) + switch rng.value() & 7 { + case 0: + n += 1 + case 1: + n += 2 + case 2: + n += 3 + case 3: + n += 4 + case 4: + n += 5 + case 5: + n += 6 + case 6: + n += 7 + case 7: + n += 8 + } + } + sink = n +} + +func BenchmarkSwitch32Predictable(b *testing.B) { + benchmarkSwitch32(b, true) +} +func BenchmarkSwitch32Unpredictable(b *testing.B) { + benchmarkSwitch32(b, false) +} +func benchmarkSwitch32(b *testing.B, predictable bool) { + n := 0 + rng := newRNG() + for i := 0; i < b.N; i++ { + rng = rng.next(predictable) + switch rng.value() & 31 { + case 0, 1, 2: + n += 1 + case 4, 5, 6: + n += 2 + case 8, 9, 10: + n += 3 + case 12, 13, 14: + n += 4 + case 16, 17, 18: + n += 5 + case 20, 21, 22: + n += 6 + case 24, 25, 26: + n += 7 + case 28, 29, 30: + n += 8 + default: + n += 9 + } + } + sink = n +} + +func BenchmarkSwitchStringPredictable(b *testing.B) { + benchmarkSwitchString(b, true) +} +func BenchmarkSwitchStringUnpredictable(b *testing.B) { + benchmarkSwitchString(b, false) +} +func benchmarkSwitchString(b *testing.B, predictable bool) { + a := []string{ + "foo", + "foo1", + "foo22", + "foo333", + "foo4444", + "foo55555", + "foo666666", + "foo7777777", + } + n := 0 + rng := newRNG() + for i := 0; i < b.N; i++ { + rng = rng.next(predictable) + switch a[rng.value()&7] { + case "foo": + n += 1 + case "foo1": + n += 2 + case "foo22": + n += 3 + case "foo333": + n += 4 + case "foo4444": + n += 5 + case "foo55555": + n += 6 + case "foo666666": + n += 7 + case "foo7777777": + n += 8 + } + } + sink = n +} + +// A simple random number generator used to make switches conditionally predictable. +type rng uint64 + +func newRNG() rng { + return 1 +} +func (r rng) next(predictable bool) rng { + if predictable { + return r + 1 + } + return rng(bits.RotateLeft64(uint64(r), 13) * 0x3c374d) +} +func (r rng) value() uint64 { + return uint64(r) +} diff --git a/src/cmd/compile/internal/test/testdata/addressed_test.go b/src/cmd/compile/internal/test/testdata/addressed_test.go index cdabf978f086b0..4cc9ac4d5b27cd 100644 --- a/src/cmd/compile/internal/test/testdata/addressed_test.go +++ b/src/cmd/compile/internal/test/testdata/addressed_test.go @@ -145,6 +145,7 @@ func (v V) val() int64 { // and y.val() should be equal to which and y.p.val() should // be equal to z.val(). Also, x(.p)**8 == x; that is, the // autos are all linked into a ring. +// //go:noinline func (v V) autos_ssa(which, w1, x1, w2, x2 int64) (y, z V) { fill_ssa(v.w, v.x, &v, v.p) // gratuitous no-op to force addressing @@ -191,6 +192,7 @@ func (v V) autos_ssa(which, w1, x1, w2, x2 int64) (y, z V) { // gets is an address-mentioning way of implementing // structure assignment. +// //go:noinline func (to *V) gets(from *V) { *to = *from @@ -198,12 +200,14 @@ func (to *V) gets(from *V) { // gets is an address-and-interface-mentioning way of // implementing structure assignment. +// //go:noinline func (to *V) getsI(from interface{}) { *to = *from.(*V) } // fill_ssa initializes r with V{w:w, x:x, p:p} +// //go:noinline func fill_ssa(w, x int64, r, p *V) { *r = V{w: w, x: x, p: p} diff --git a/src/cmd/compile/internal/test/testdata/arith_test.go b/src/cmd/compile/internal/test/testdata/arith_test.go index 7d54a9181d1ef2..253142a0fbc512 100644 --- a/src/cmd/compile/internal/test/testdata/arith_test.go +++ b/src/cmd/compile/internal/test/testdata/arith_test.go @@ -225,6 +225,7 @@ func testArithConstShift(t *testing.T) { // overflowConstShift_ssa verifes that constant folding for shift // doesn't wrap (i.e. x << MAX_INT << 1 doesn't get folded to x << 0). +// //go:noinline func overflowConstShift64_ssa(x int64) int64 { return x << uint64(0xffffffffffffffff) << uint64(1) diff --git a/src/cmd/compile/internal/test/testdata/ctl_test.go b/src/cmd/compile/internal/test/testdata/ctl_test.go index 16d571ce2cbf40..ff3a1609c5a995 100644 --- a/src/cmd/compile/internal/test/testdata/ctl_test.go +++ b/src/cmd/compile/internal/test/testdata/ctl_test.go @@ -117,6 +117,7 @@ type junk struct { // flagOverwrite_ssa is intended to reproduce an issue seen where a XOR // was scheduled between a compare and branch, clearing flags. +// //go:noinline func flagOverwrite_ssa(s *junk, c int) int { if '0' <= c && c <= '9' { diff --git a/src/cmd/compile/internal/test/testdata/fp_test.go b/src/cmd/compile/internal/test/testdata/fp_test.go index 7d61a8063ee9a9..b96ce84a6ca466 100644 --- a/src/cmd/compile/internal/test/testdata/fp_test.go +++ b/src/cmd/compile/internal/test/testdata/fp_test.go @@ -14,6 +14,7 @@ import ( // manysub_ssa is designed to tickle bugs that depend on register // pressure or unfriendly operand ordering in registers (and at // least once it succeeded in this). +// //go:noinline func manysub_ssa(a, b, c, d float64) (aa, ab, ac, ad, ba, bb, bc, bd, ca, cb, cc, cd, da, db, dc, dd float64) { aa = a + 11.0 - a @@ -37,6 +38,7 @@ func manysub_ssa(a, b, c, d float64) (aa, ab, ac, ad, ba, bb, bc, bd, ca, cb, cc // fpspill_ssa attempts to trigger a bug where phis with floating point values // were stored in non-fp registers causing an error in doasm. +// //go:noinline func fpspill_ssa(a int) float64 { diff --git a/src/cmd/compile/internal/test/testdata/loadstore_test.go b/src/cmd/compile/internal/test/testdata/loadstore_test.go index 57571f5d170ec7..052172819a7a64 100644 --- a/src/cmd/compile/internal/test/testdata/loadstore_test.go +++ b/src/cmd/compile/internal/test/testdata/loadstore_test.go @@ -73,6 +73,7 @@ var b int // testDeadStorePanic_ssa ensures that we don't optimize away stores // that could be read by after recover(). Modeled after fixedbugs/issue1304. +// //go:noinline func testDeadStorePanic_ssa(a int) (r int) { defer func() { diff --git a/src/cmd/compile/internal/test/testdata/mysort/mysort.go b/src/cmd/compile/internal/test/testdata/mysort/mysort.go new file mode 100644 index 00000000000000..14852c868a7945 --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/mysort/mysort.go @@ -0,0 +1,40 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Generic sort function, tested with two different pointer types. + +package mysort + +import ( + "fmt" +) + +type LessConstraint[T any] interface { + Less(T) bool +} + +//go:noinline +func Sort[T LessConstraint[T]](x []T) { + n := len(x) + for i := 1; i < n; i++ { + for j := i; j > 0 && x[j].Less(x[j-1]); j-- { + x[j], x[j-1] = x[j-1], x[j] + } + } +} + +type MyInt struct { + Value int +} + +func (a *MyInt) Less(b *MyInt) bool { + return a.Value < b.Value +} + +//go:noinline +func F() { + sl1 := []*MyInt{&MyInt{4}, &MyInt{3}, &MyInt{8}, &MyInt{7}} + Sort(sl1) + fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3]) +} diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.go b/src/cmd/compile/internal/test/testdata/ptrsort.go new file mode 100644 index 00000000000000..d26ba581d91dd9 --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/ptrsort.go @@ -0,0 +1,30 @@ +package main + +// Test generic sort function with two different pointer types in different packages, +// make sure only one instantiation is created. + +import ( + "fmt" + + "cmd/compile/internal/test/testdata/mysort" +) + +type MyString struct { + string +} + +func (a *MyString) Less(b *MyString) bool { + return a.string < b.string +} + +func main() { + mysort.F() + + sl1 := []*mysort.MyInt{{7}, {1}, {4}, {6}} + mysort.Sort(sl1) + fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3]) + + sl2 := []*MyString{{"when"}, {"in"}, {"the"}, {"course"}, {"of"}} + mysort.Sort(sl2) + fmt.Printf("%v %v %v %v %v\n", sl2[0], sl2[1], sl2[2], sl2[3], sl2[4]) +} diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.out b/src/cmd/compile/internal/test/testdata/ptrsort.out new file mode 100644 index 00000000000000..41f1621d1a29b5 --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/ptrsort.out @@ -0,0 +1,3 @@ +&{3} &{4} &{7} &{8} +&{1} &{4} &{6} &{7} +&{course} &{in} &{of} &{the} &{when} diff --git a/src/cmd/compile/internal/test/zerorange_test.go b/src/cmd/compile/internal/test/zerorange_test.go index ec87136157209c..e92b5d342fe136 100644 --- a/src/cmd/compile/internal/test/zerorange_test.go +++ b/src/cmd/compile/internal/test/zerorange_test.go @@ -170,7 +170,6 @@ func triggerZerorangeSmall(f, g, h uint64) (rv0 uint64) { // depending on the size of the thing that needs to be zeroed out // (I've verified at the time of the writing of this test that it // exercises the various cases). -// func TestZerorange45372(t *testing.T) { if r := triggerZerorangeLarge(101, 303, 505); r != 1010 { t.Errorf("large: wanted %d got %d", 1010, r) diff --git a/src/cmd/compile/internal/typebits/typebits.go b/src/cmd/compile/internal/typebits/typebits.go index 1c1b077423dc97..06c1d12a342b83 100644 --- a/src/cmd/compile/internal/typebits/typebits.go +++ b/src/cmd/compile/internal/typebits/typebits.go @@ -14,11 +14,11 @@ import ( // the first run and then simply copied into bv at the correct offset // on future calls with the same type t. func Set(t *types.Type, off int64, bv bitvec.BitVec) { - if t.Align > 0 && off&int64(t.Align-1) != 0 { - base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off) + if uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 { + base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, uint8(t.Alignment()), off) } if !t.HasPointers() { - // Note: this case ensures that pointers to go:notinheap types + // Note: this case ensures that pointers to not-in-heap types // are not considered pointers by garbage collection and stack copying. return } @@ -67,13 +67,13 @@ func Set(t *types.Type, off int64, bv bitvec.BitVec) { case types.TARRAY: elt := t.Elem() - if elt.Width == 0 { + if elt.Size() == 0 { // Short-circuit for #20739. break } for i := int64(0); i < t.NumElem(); i++ { Set(elt, off, bv) - off += elt.Width + off += elt.Size() } case types.TSTRUCT: diff --git a/src/cmd/compile/internal/typecheck/bexport.go b/src/cmd/compile/internal/typecheck/bexport.go index 4a84bb13fa48eb..352f7a96ad3eac 100644 --- a/src/cmd/compile/internal/typecheck/bexport.go +++ b/src/cmd/compile/internal/typecheck/bexport.go @@ -96,6 +96,12 @@ func predeclared() []*types.Type { // any type, for builtin export data types.Types[types.TANY], + + // comparable + types.ComparableType, + + // any + types.AnyType, } } return predecl diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go index 833b17b4148ed5..7718985aae7bbb 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -71,137 +71,145 @@ var runtimeDecls = [...]struct { {"slicecopy", funcTag, 54}, {"decoderune", funcTag, 55}, {"countrunes", funcTag, 56}, - {"convI2I", funcTag, 57}, - {"convT16", funcTag, 59}, - {"convT32", funcTag, 61}, - {"convT64", funcTag, 62}, - {"convTstring", funcTag, 63}, - {"convTslice", funcTag, 66}, - {"convT2E", funcTag, 67}, - {"convT2Enoptr", funcTag, 67}, - {"convT2I", funcTag, 67}, - {"convT2Inoptr", funcTag, 67}, - {"assertE2I", funcTag, 68}, - {"assertE2I2", funcTag, 57}, - {"assertI2I", funcTag, 68}, - {"assertI2I2", funcTag, 57}, - {"panicdottypeE", funcTag, 69}, - {"panicdottypeI", funcTag, 69}, - {"panicnildottype", funcTag, 70}, - {"ifaceeq", funcTag, 72}, - {"efaceeq", funcTag, 72}, - {"fastrand", funcTag, 73}, - {"makemap64", funcTag, 75}, - {"makemap", funcTag, 76}, - {"makemap_small", funcTag, 77}, - {"mapaccess1", funcTag, 78}, - {"mapaccess1_fast32", funcTag, 79}, - {"mapaccess1_fast64", funcTag, 80}, - {"mapaccess1_faststr", funcTag, 81}, - {"mapaccess1_fat", funcTag, 82}, - {"mapaccess2", funcTag, 83}, - {"mapaccess2_fast32", funcTag, 84}, - {"mapaccess2_fast64", funcTag, 85}, - {"mapaccess2_faststr", funcTag, 86}, - {"mapaccess2_fat", funcTag, 87}, - {"mapassign", funcTag, 78}, - {"mapassign_fast32", funcTag, 79}, - {"mapassign_fast32ptr", funcTag, 88}, - {"mapassign_fast64", funcTag, 80}, - {"mapassign_fast64ptr", funcTag, 88}, - {"mapassign_faststr", funcTag, 81}, - {"mapiterinit", funcTag, 89}, - {"mapdelete", funcTag, 89}, - {"mapdelete_fast32", funcTag, 90}, - {"mapdelete_fast64", funcTag, 91}, - {"mapdelete_faststr", funcTag, 92}, - {"mapiternext", funcTag, 93}, - {"mapclear", funcTag, 94}, - {"makechan64", funcTag, 96}, - {"makechan", funcTag, 97}, - {"chanrecv1", funcTag, 99}, - {"chanrecv2", funcTag, 100}, - {"chansend1", funcTag, 102}, + {"convI2I", funcTag, 58}, + {"convT", funcTag, 59}, + {"convTnoptr", funcTag, 59}, + {"convT16", funcTag, 61}, + {"convT32", funcTag, 63}, + {"convT64", funcTag, 64}, + {"convTstring", funcTag, 65}, + {"convTslice", funcTag, 68}, + {"assertE2I", funcTag, 69}, + {"assertE2I2", funcTag, 70}, + {"assertI2I", funcTag, 69}, + {"assertI2I2", funcTag, 70}, + {"panicdottypeE", funcTag, 71}, + {"panicdottypeI", funcTag, 71}, + {"panicnildottype", funcTag, 72}, + {"ifaceeq", funcTag, 73}, + {"efaceeq", funcTag, 73}, + {"fastrand", funcTag, 74}, + {"makemap64", funcTag, 76}, + {"makemap", funcTag, 77}, + {"makemap_small", funcTag, 78}, + {"mapaccess1", funcTag, 79}, + {"mapaccess1_fast32", funcTag, 80}, + {"mapaccess1_fast64", funcTag, 81}, + {"mapaccess1_faststr", funcTag, 82}, + {"mapaccess1_fat", funcTag, 83}, + {"mapaccess2", funcTag, 84}, + {"mapaccess2_fast32", funcTag, 85}, + {"mapaccess2_fast64", funcTag, 86}, + {"mapaccess2_faststr", funcTag, 87}, + {"mapaccess2_fat", funcTag, 88}, + {"mapassign", funcTag, 79}, + {"mapassign_fast32", funcTag, 80}, + {"mapassign_fast32ptr", funcTag, 89}, + {"mapassign_fast64", funcTag, 81}, + {"mapassign_fast64ptr", funcTag, 89}, + {"mapassign_faststr", funcTag, 82}, + {"mapiterinit", funcTag, 90}, + {"mapdelete", funcTag, 90}, + {"mapdelete_fast32", funcTag, 91}, + {"mapdelete_fast64", funcTag, 92}, + {"mapdelete_faststr", funcTag, 93}, + {"mapiternext", funcTag, 94}, + {"mapclear", funcTag, 95}, + {"makechan64", funcTag, 97}, + {"makechan", funcTag, 98}, + {"chanrecv1", funcTag, 100}, + {"chanrecv2", funcTag, 101}, + {"chansend1", funcTag, 103}, {"closechan", funcTag, 30}, - {"writeBarrier", varTag, 104}, - {"typedmemmove", funcTag, 105}, - {"typedmemclr", funcTag, 106}, - {"typedslicecopy", funcTag, 107}, - {"selectnbsend", funcTag, 108}, - {"selectnbrecv", funcTag, 109}, - {"selectsetpc", funcTag, 110}, - {"selectgo", funcTag, 111}, + {"writeBarrier", varTag, 105}, + {"typedmemmove", funcTag, 106}, + {"typedmemclr", funcTag, 107}, + {"typedslicecopy", funcTag, 108}, + {"selectnbsend", funcTag, 109}, + {"selectnbrecv", funcTag, 110}, + {"selectsetpc", funcTag, 111}, + {"selectgo", funcTag, 112}, {"block", funcTag, 9}, - {"makeslice", funcTag, 112}, - {"makeslice64", funcTag, 113}, - {"makeslicecopy", funcTag, 114}, - {"growslice", funcTag, 116}, - {"unsafeslice", funcTag, 117}, - {"unsafeslice64", funcTag, 118}, + {"makeslice", funcTag, 113}, + {"makeslice64", funcTag, 114}, + {"makeslicecopy", funcTag, 115}, + {"growslice", funcTag, 117}, {"unsafeslicecheckptr", funcTag, 118}, - {"memmove", funcTag, 119}, - {"memclrNoHeapPointers", funcTag, 120}, - {"memclrHasPointers", funcTag, 120}, - {"memequal", funcTag, 121}, - {"memequal0", funcTag, 122}, - {"memequal8", funcTag, 122}, - {"memequal16", funcTag, 122}, - {"memequal32", funcTag, 122}, - {"memequal64", funcTag, 122}, - {"memequal128", funcTag, 122}, - {"f32equal", funcTag, 123}, - {"f64equal", funcTag, 123}, - {"c64equal", funcTag, 123}, - {"c128equal", funcTag, 123}, - {"strequal", funcTag, 123}, - {"interequal", funcTag, 123}, - {"nilinterequal", funcTag, 123}, - {"memhash", funcTag, 124}, - {"memhash0", funcTag, 125}, - {"memhash8", funcTag, 125}, - {"memhash16", funcTag, 125}, - {"memhash32", funcTag, 125}, - {"memhash64", funcTag, 125}, - {"memhash128", funcTag, 125}, - {"f32hash", funcTag, 125}, - {"f64hash", funcTag, 125}, - {"c64hash", funcTag, 125}, - {"c128hash", funcTag, 125}, - {"strhash", funcTag, 125}, - {"interhash", funcTag, 125}, - {"nilinterhash", funcTag, 125}, - {"int64div", funcTag, 126}, - {"uint64div", funcTag, 127}, - {"int64mod", funcTag, 126}, - {"uint64mod", funcTag, 127}, - {"float64toint64", funcTag, 128}, - {"float64touint64", funcTag, 129}, - {"float64touint32", funcTag, 130}, - {"int64tofloat64", funcTag, 131}, - {"uint64tofloat64", funcTag, 132}, - {"uint32tofloat64", funcTag, 133}, - {"complex128div", funcTag, 134}, - {"getcallerpc", funcTag, 135}, - {"getcallersp", funcTag, 135}, + {"panicunsafeslicelen", funcTag, 9}, + {"panicunsafeslicenilptr", funcTag, 9}, + {"unsafestringcheckptr", funcTag, 119}, + {"panicunsafestringlen", funcTag, 9}, + {"panicunsafestringnilptr", funcTag, 9}, + {"mulUintptr", funcTag, 120}, + {"memmove", funcTag, 121}, + {"memclrNoHeapPointers", funcTag, 122}, + {"memclrHasPointers", funcTag, 122}, + {"memequal", funcTag, 123}, + {"memequal0", funcTag, 124}, + {"memequal8", funcTag, 124}, + {"memequal16", funcTag, 124}, + {"memequal32", funcTag, 124}, + {"memequal64", funcTag, 124}, + {"memequal128", funcTag, 124}, + {"f32equal", funcTag, 125}, + {"f64equal", funcTag, 125}, + {"c64equal", funcTag, 125}, + {"c128equal", funcTag, 125}, + {"strequal", funcTag, 125}, + {"interequal", funcTag, 125}, + {"nilinterequal", funcTag, 125}, + {"memhash", funcTag, 126}, + {"memhash0", funcTag, 127}, + {"memhash8", funcTag, 127}, + {"memhash16", funcTag, 127}, + {"memhash32", funcTag, 127}, + {"memhash64", funcTag, 127}, + {"memhash128", funcTag, 127}, + {"f32hash", funcTag, 127}, + {"f64hash", funcTag, 127}, + {"c64hash", funcTag, 127}, + {"c128hash", funcTag, 127}, + {"strhash", funcTag, 127}, + {"interhash", funcTag, 127}, + {"nilinterhash", funcTag, 127}, + {"int64div", funcTag, 128}, + {"uint64div", funcTag, 129}, + {"int64mod", funcTag, 128}, + {"uint64mod", funcTag, 129}, + {"float64toint64", funcTag, 130}, + {"float64touint64", funcTag, 131}, + {"float64touint32", funcTag, 132}, + {"int64tofloat64", funcTag, 133}, + {"int64tofloat32", funcTag, 135}, + {"uint64tofloat64", funcTag, 136}, + {"uint64tofloat32", funcTag, 137}, + {"uint32tofloat64", funcTag, 138}, + {"complex128div", funcTag, 139}, + {"getcallerpc", funcTag, 140}, + {"getcallersp", funcTag, 140}, {"racefuncenter", funcTag, 31}, {"racefuncexit", funcTag, 9}, {"raceread", funcTag, 31}, {"racewrite", funcTag, 31}, - {"racereadrange", funcTag, 136}, - {"racewriterange", funcTag, 136}, - {"msanread", funcTag, 136}, - {"msanwrite", funcTag, 136}, - {"msanmove", funcTag, 137}, - {"checkptrAlignment", funcTag, 138}, - {"checkptrArithmetic", funcTag, 140}, - {"libfuzzerTraceCmp1", funcTag, 141}, - {"libfuzzerTraceCmp2", funcTag, 142}, - {"libfuzzerTraceCmp4", funcTag, 143}, - {"libfuzzerTraceCmp8", funcTag, 144}, - {"libfuzzerTraceConstCmp1", funcTag, 141}, - {"libfuzzerTraceConstCmp2", funcTag, 142}, - {"libfuzzerTraceConstCmp4", funcTag, 143}, - {"libfuzzerTraceConstCmp8", funcTag, 144}, + {"racereadrange", funcTag, 141}, + {"racewriterange", funcTag, 141}, + {"msanread", funcTag, 141}, + {"msanwrite", funcTag, 141}, + {"msanmove", funcTag, 142}, + {"asanread", funcTag, 141}, + {"asanwrite", funcTag, 141}, + {"checkptrAlignment", funcTag, 143}, + {"checkptrArithmetic", funcTag, 145}, + {"libfuzzerTraceCmp1", funcTag, 146}, + {"libfuzzerTraceCmp2", funcTag, 147}, + {"libfuzzerTraceCmp4", funcTag, 148}, + {"libfuzzerTraceCmp8", funcTag, 149}, + {"libfuzzerTraceConstCmp1", funcTag, 146}, + {"libfuzzerTraceConstCmp2", funcTag, 147}, + {"libfuzzerTraceConstCmp4", funcTag, 148}, + {"libfuzzerTraceConstCmp8", funcTag, 149}, + {"libfuzzerHookStrCmp", funcTag, 150}, + {"libfuzzerHookEqualFold", funcTag, 150}, {"x86HasPOPCNT", varTag, 6}, {"x86HasSSE41", varTag, 6}, {"x86HasFMA", varTag, 6}, @@ -210,6 +218,7 @@ var runtimeDecls = [...]struct { } // Not inlining this function removes a significant chunk of init code. +// //go:noinline func newSig(params, results []*types.Field) *types.Type { return types.NewSignature(types.NoPkg, nil, nil, params, results) @@ -224,7 +233,7 @@ func params(tlist ...*types.Type) []*types.Field { } func runtimeTypes() []*types.Type { - var typs [145]*types.Type + var typs [151]*types.Type typs[0] = types.ByteType typs[1] = types.NewPtr(typs[0]) typs[2] = types.Types[types.TANY] @@ -282,93 +291,99 @@ func runtimeTypes() []*types.Type { typs[54] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15])) typs[55] = newSig(params(typs[28], typs[15]), params(typs[46], typs[15])) typs[56] = newSig(params(typs[28]), params(typs[15])) - typs[57] = newSig(params(typs[1], typs[2]), params(typs[2])) - typs[58] = types.Types[types.TUINT16] - typs[59] = newSig(params(typs[58]), params(typs[7])) - typs[60] = types.Types[types.TUINT32] + typs[57] = types.NewPtr(typs[5]) + typs[58] = newSig(params(typs[1], typs[57]), params(typs[57])) + typs[59] = newSig(params(typs[1], typs[3]), params(typs[7])) + typs[60] = types.Types[types.TUINT16] typs[61] = newSig(params(typs[60]), params(typs[7])) - typs[62] = newSig(params(typs[24]), params(typs[7])) - typs[63] = newSig(params(typs[28]), params(typs[7])) - typs[64] = types.Types[types.TUINT8] - typs[65] = types.NewSlice(typs[64]) - typs[66] = newSig(params(typs[65]), params(typs[7])) - typs[67] = newSig(params(typs[1], typs[3]), params(typs[2])) - typs[68] = newSig(params(typs[1], typs[1]), params(typs[1])) - typs[69] = newSig(params(typs[1], typs[1], typs[1]), nil) - typs[70] = newSig(params(typs[1]), nil) - typs[71] = types.NewPtr(typs[5]) - typs[72] = newSig(params(typs[71], typs[7], typs[7]), params(typs[6])) - typs[73] = newSig(nil, params(typs[60])) - typs[74] = types.NewMap(typs[2], typs[2]) - typs[75] = newSig(params(typs[1], typs[22], typs[3]), params(typs[74])) - typs[76] = newSig(params(typs[1], typs[15], typs[3]), params(typs[74])) - typs[77] = newSig(nil, params(typs[74])) - typs[78] = newSig(params(typs[1], typs[74], typs[3]), params(typs[3])) - typs[79] = newSig(params(typs[1], typs[74], typs[60]), params(typs[3])) - typs[80] = newSig(params(typs[1], typs[74], typs[24]), params(typs[3])) - typs[81] = newSig(params(typs[1], typs[74], typs[28]), params(typs[3])) - typs[82] = newSig(params(typs[1], typs[74], typs[3], typs[1]), params(typs[3])) - typs[83] = newSig(params(typs[1], typs[74], typs[3]), params(typs[3], typs[6])) - typs[84] = newSig(params(typs[1], typs[74], typs[60]), params(typs[3], typs[6])) - typs[85] = newSig(params(typs[1], typs[74], typs[24]), params(typs[3], typs[6])) - typs[86] = newSig(params(typs[1], typs[74], typs[28]), params(typs[3], typs[6])) - typs[87] = newSig(params(typs[1], typs[74], typs[3], typs[1]), params(typs[3], typs[6])) - typs[88] = newSig(params(typs[1], typs[74], typs[7]), params(typs[3])) - typs[89] = newSig(params(typs[1], typs[74], typs[3]), nil) - typs[90] = newSig(params(typs[1], typs[74], typs[60]), nil) - typs[91] = newSig(params(typs[1], typs[74], typs[24]), nil) - typs[92] = newSig(params(typs[1], typs[74], typs[28]), nil) - typs[93] = newSig(params(typs[3]), nil) - typs[94] = newSig(params(typs[1], typs[74]), nil) - typs[95] = types.NewChan(typs[2], types.Cboth) - typs[96] = newSig(params(typs[1], typs[22]), params(typs[95])) - typs[97] = newSig(params(typs[1], typs[15]), params(typs[95])) - typs[98] = types.NewChan(typs[2], types.Crecv) - typs[99] = newSig(params(typs[98], typs[3]), nil) - typs[100] = newSig(params(typs[98], typs[3]), params(typs[6])) - typs[101] = types.NewChan(typs[2], types.Csend) - typs[102] = newSig(params(typs[101], typs[3]), nil) - typs[103] = types.NewArray(typs[0], 3) - typs[104] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[103]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])}) - typs[105] = newSig(params(typs[1], typs[3], typs[3]), nil) - typs[106] = newSig(params(typs[1], typs[3]), nil) - typs[107] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15])) - typs[108] = newSig(params(typs[101], typs[3]), params(typs[6])) - typs[109] = newSig(params(typs[3], typs[98]), params(typs[6], typs[6])) - typs[110] = newSig(params(typs[71]), nil) - typs[111] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6])) - typs[112] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7])) - typs[113] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7])) - typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) - typs[115] = types.NewSlice(typs[2]) - typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115])) - typs[117] = newSig(params(typs[1], typs[7], typs[15]), nil) + typs[62] = types.Types[types.TUINT32] + typs[63] = newSig(params(typs[62]), params(typs[7])) + typs[64] = newSig(params(typs[24]), params(typs[7])) + typs[65] = newSig(params(typs[28]), params(typs[7])) + typs[66] = types.Types[types.TUINT8] + typs[67] = types.NewSlice(typs[66]) + typs[68] = newSig(params(typs[67]), params(typs[7])) + typs[69] = newSig(params(typs[1], typs[1]), params(typs[1])) + typs[70] = newSig(params(typs[1], typs[2]), params(typs[2])) + typs[71] = newSig(params(typs[1], typs[1], typs[1]), nil) + typs[72] = newSig(params(typs[1]), nil) + typs[73] = newSig(params(typs[57], typs[7], typs[7]), params(typs[6])) + typs[74] = newSig(nil, params(typs[62])) + typs[75] = types.NewMap(typs[2], typs[2]) + typs[76] = newSig(params(typs[1], typs[22], typs[3]), params(typs[75])) + typs[77] = newSig(params(typs[1], typs[15], typs[3]), params(typs[75])) + typs[78] = newSig(nil, params(typs[75])) + typs[79] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3])) + typs[80] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3])) + typs[81] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3])) + typs[82] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3])) + typs[83] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3])) + typs[84] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3], typs[6])) + typs[85] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3], typs[6])) + typs[86] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3], typs[6])) + typs[87] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3], typs[6])) + typs[88] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3], typs[6])) + typs[89] = newSig(params(typs[1], typs[75], typs[7]), params(typs[3])) + typs[90] = newSig(params(typs[1], typs[75], typs[3]), nil) + typs[91] = newSig(params(typs[1], typs[75], typs[62]), nil) + typs[92] = newSig(params(typs[1], typs[75], typs[24]), nil) + typs[93] = newSig(params(typs[1], typs[75], typs[28]), nil) + typs[94] = newSig(params(typs[3]), nil) + typs[95] = newSig(params(typs[1], typs[75]), nil) + typs[96] = types.NewChan(typs[2], types.Cboth) + typs[97] = newSig(params(typs[1], typs[22]), params(typs[96])) + typs[98] = newSig(params(typs[1], typs[15]), params(typs[96])) + typs[99] = types.NewChan(typs[2], types.Crecv) + typs[100] = newSig(params(typs[99], typs[3]), nil) + typs[101] = newSig(params(typs[99], typs[3]), params(typs[6])) + typs[102] = types.NewChan(typs[2], types.Csend) + typs[103] = newSig(params(typs[102], typs[3]), nil) + typs[104] = types.NewArray(typs[0], 3) + typs[105] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[104]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])}) + typs[106] = newSig(params(typs[1], typs[3], typs[3]), nil) + typs[107] = newSig(params(typs[1], typs[3]), nil) + typs[108] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15])) + typs[109] = newSig(params(typs[102], typs[3]), params(typs[6])) + typs[110] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6])) + typs[111] = newSig(params(typs[57]), nil) + typs[112] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6])) + typs[113] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7])) + typs[114] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7])) + typs[115] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) + typs[116] = types.NewSlice(typs[2]) + typs[117] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[116])) typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil) - typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil) - typs[120] = newSig(params(typs[7], typs[5]), nil) - typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) - typs[122] = newSig(params(typs[3], typs[3]), params(typs[6])) - typs[123] = newSig(params(typs[7], typs[7]), params(typs[6])) - typs[124] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5])) - typs[125] = newSig(params(typs[7], typs[5]), params(typs[5])) - typs[126] = newSig(params(typs[22], typs[22]), params(typs[22])) - typs[127] = newSig(params(typs[24], typs[24]), params(typs[24])) - typs[128] = newSig(params(typs[20]), params(typs[22])) - typs[129] = newSig(params(typs[20]), params(typs[24])) - typs[130] = newSig(params(typs[20]), params(typs[60])) - typs[131] = newSig(params(typs[22]), params(typs[20])) - typs[132] = newSig(params(typs[24]), params(typs[20])) - typs[133] = newSig(params(typs[60]), params(typs[20])) - typs[134] = newSig(params(typs[26], typs[26]), params(typs[26])) - typs[135] = newSig(nil, params(typs[5])) - typs[136] = newSig(params(typs[5], typs[5]), nil) - typs[137] = newSig(params(typs[5], typs[5], typs[5]), nil) - typs[138] = newSig(params(typs[7], typs[1], typs[5]), nil) - typs[139] = types.NewSlice(typs[7]) - typs[140] = newSig(params(typs[7], typs[139]), nil) - typs[141] = newSig(params(typs[64], typs[64]), nil) - typs[142] = newSig(params(typs[58], typs[58]), nil) - typs[143] = newSig(params(typs[60], typs[60]), nil) - typs[144] = newSig(params(typs[24], typs[24]), nil) + typs[119] = newSig(params(typs[7], typs[22]), nil) + typs[120] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6])) + typs[121] = newSig(params(typs[3], typs[3], typs[5]), nil) + typs[122] = newSig(params(typs[7], typs[5]), nil) + typs[123] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) + typs[124] = newSig(params(typs[3], typs[3]), params(typs[6])) + typs[125] = newSig(params(typs[7], typs[7]), params(typs[6])) + typs[126] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5])) + typs[127] = newSig(params(typs[7], typs[5]), params(typs[5])) + typs[128] = newSig(params(typs[22], typs[22]), params(typs[22])) + typs[129] = newSig(params(typs[24], typs[24]), params(typs[24])) + typs[130] = newSig(params(typs[20]), params(typs[22])) + typs[131] = newSig(params(typs[20]), params(typs[24])) + typs[132] = newSig(params(typs[20]), params(typs[62])) + typs[133] = newSig(params(typs[22]), params(typs[20])) + typs[134] = types.Types[types.TFLOAT32] + typs[135] = newSig(params(typs[22]), params(typs[134])) + typs[136] = newSig(params(typs[24]), params(typs[20])) + typs[137] = newSig(params(typs[24]), params(typs[134])) + typs[138] = newSig(params(typs[62]), params(typs[20])) + typs[139] = newSig(params(typs[26], typs[26]), params(typs[26])) + typs[140] = newSig(nil, params(typs[5])) + typs[141] = newSig(params(typs[5], typs[5]), nil) + typs[142] = newSig(params(typs[5], typs[5], typs[5]), nil) + typs[143] = newSig(params(typs[7], typs[1], typs[5]), nil) + typs[144] = types.NewSlice(typs[7]) + typs[145] = newSig(params(typs[7], typs[144]), nil) + typs[146] = newSig(params(typs[66], typs[66], typs[15]), nil) + typs[147] = newSig(params(typs[60], typs[60], typs[15]), nil) + typs[148] = newSig(params(typs[62], typs[62], typs[15]), nil) + typs[149] = newSig(params(typs[24], typs[24], typs[15]), nil) + typs[150] = newSig(params(typs[28], typs[28], typs[15]), nil) return typs[:] } diff --git a/src/cmd/compile/internal/typecheck/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go index 2b29ea3c08ca72..b862594c92ac4c 100644 --- a/src/cmd/compile/internal/typecheck/builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go @@ -84,10 +84,15 @@ func decoderune(string, int) (retv rune, retk int) func countrunes(string) int // Non-empty-interface to non-empty-interface conversion. -func convI2I(typ *byte, elem any) (ret any) +func convI2I(typ *byte, itab *uintptr) (ret *uintptr) -// Specialized type-to-interface conversion. -// These return only a data pointer. +// Convert non-interface type to the data word of a (empty or nonempty) interface. +func convT(typ *byte, elem *any) unsafe.Pointer + +// Same as convT, for types with no pointers in them. +func convTnoptr(typ *byte, elem *any) unsafe.Pointer + +// Specialized versions of convT for specific types. // These functions take concrete types in the runtime. But they may // be used for a wider range of types, which have the same memory // layout as the parameter type. The compiler converts the @@ -99,14 +104,6 @@ func convT64(val uint64) unsafe.Pointer func convTstring(val string) unsafe.Pointer func convTslice(val []uint8) unsafe.Pointer -// Type to empty-interface conversion. -func convT2E(typ *byte, elem *any) (ret any) -func convT2Enoptr(typ *byte, elem *any) (ret any) - -// Type to non-empty-interface conversion. -func convT2I(tab *byte, elem *any) (ret any) -func convT2Inoptr(tab *byte, elem *any) (ret any) - // interface type assertions x.(T) func assertE2I(inter *byte, typ *byte) *byte func assertE2I2(inter *byte, eface any) (ret any) @@ -182,10 +179,15 @@ func block() func makeslice(typ *byte, len int, cap int) unsafe.Pointer func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer -func growslice(typ *byte, old []any, cap int) (ary []any) -func unsafeslice(typ *byte, ptr unsafe.Pointer, len int) -func unsafeslice64(typ *byte, ptr unsafe.Pointer, len int64) +func growslice(oldPtr *any, newLen, oldCap, num int, et *byte) (ary []any) func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64) +func panicunsafeslicelen() +func panicunsafeslicenilptr() +func unsafestringcheckptr(ptr unsafe.Pointer, len int64) +func panicunsafestringlen() +func panicunsafestringnilptr() + +func mulUintptr(x, y uintptr) (uintptr, bool) func memmove(to *any, frm *any, length uintptr) func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) @@ -230,7 +232,9 @@ func float64toint64(float64) int64 func float64touint64(float64) uint64 func float64touint32(float64) uint32 func int64tofloat64(int64) float64 +func int64tofloat32(int64) float32 func uint64tofloat64(uint64) float64 +func uint64tofloat32(uint64) float32 func uint32tofloat64(uint32) float64 func complex128div(num complex128, den complex128) (quo complex128) @@ -251,17 +255,23 @@ func msanread(addr, size uintptr) func msanwrite(addr, size uintptr) func msanmove(dst, src, size uintptr) +// address sanitizer +func asanread(addr, size uintptr) +func asanwrite(addr, size uintptr) + func checkptrAlignment(unsafe.Pointer, *byte, uintptr) func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer) -func libfuzzerTraceCmp1(uint8, uint8) -func libfuzzerTraceCmp2(uint16, uint16) -func libfuzzerTraceCmp4(uint32, uint32) -func libfuzzerTraceCmp8(uint64, uint64) -func libfuzzerTraceConstCmp1(uint8, uint8) -func libfuzzerTraceConstCmp2(uint16, uint16) -func libfuzzerTraceConstCmp4(uint32, uint32) -func libfuzzerTraceConstCmp8(uint64, uint64) +func libfuzzerTraceCmp1(uint8, uint8, int) +func libfuzzerTraceCmp2(uint16, uint16, int) +func libfuzzerTraceCmp4(uint32, uint32, int) +func libfuzzerTraceCmp8(uint64, uint64, int) +func libfuzzerTraceConstCmp1(uint8, uint8, int) +func libfuzzerTraceConstCmp2(uint16, uint16, int) +func libfuzzerTraceConstCmp4(uint32, uint32, int) +func libfuzzerTraceConstCmp8(uint64, uint64, int) +func libfuzzerHookStrCmp(string, string, int) +func libfuzzerHookEqualFold(string, string, int) // architecture variants var x86HasPOPCNT bool diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go index 761b043794062d..edc399ffd74c77 100644 --- a/src/cmd/compile/internal/typecheck/const.go +++ b/src/cmd/compile/internal/typecheck/const.go @@ -16,7 +16,6 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" - "cmd/internal/src" ) func roundFloat(v constant.Value, sz int64) constant.Value { @@ -99,10 +98,7 @@ func convlit1(n ir.Node, t *types.Type, explicit bool, context func() string) ir } n = ir.Copy(n) if t == nil { - base.Errorf("use of untyped nil") - n.SetDiag(true) - n.SetType(nil) - return n + base.Fatalf("use of untyped nil") } if !t.HasNil() { @@ -199,18 +195,14 @@ func convlit1(n ir.Node, t *types.Type, explicit bool, context func() string) ir return n } - if !n.Diag() { - if !t.Broke() { - if explicit { - base.Errorf("cannot convert %L to type %v", n, t) - } else if context != nil { - base.Errorf("cannot use %L as type %v in %s", n, t, context()) - } else { - base.Errorf("cannot use %L as type %v", n, t) - } - } - n.SetDiag(true) + if explicit { + base.Fatalf("cannot convert %L to type %v", n, t) + } else if context != nil { + base.Fatalf("cannot use %L as type %v in %s", n, t, context()) + } else { + base.Fatalf("cannot use %L as type %v", n, t) } + n.SetType(nil) return n } @@ -628,7 +620,8 @@ func OrigInt(n ir.Node, v int64) ir.Node { // get the same type going out. // force means must assign concrete (non-ideal) type. // The results of defaultlit2 MUST be assigned back to l and r, e.g. -// n.Left, n.Right = defaultlit2(n.Left, n.Right, force) +// +// n.Left, n.Right = defaultlit2(n.Left, n.Right, force) func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) { if l.Type() == nil || r.Type() == nil { return l, r @@ -741,126 +734,46 @@ func IndexConst(n ir.Node) int64 { return ir.IntVal(types.Types[types.TINT], v) } +// callOrChan reports whether n is a call or channel operation. +func callOrChan(n ir.Node) bool { + switch n.Op() { + case ir.OAPPEND, + ir.OCALL, + ir.OCALLFUNC, + ir.OCALLINTER, + ir.OCALLMETH, + ir.OCAP, + ir.OCLOSE, + ir.OCOMPLEX, + ir.OCOPY, + ir.ODELETE, + ir.OIMAG, + ir.OLEN, + ir.OMAKE, + ir.ONEW, + ir.OPANIC, + ir.OPRINT, + ir.OPRINTN, + ir.OREAL, + ir.ORECOVER, + ir.ORECV, + ir.OUNSAFEADD, + ir.OUNSAFESLICE, + ir.OUNSAFESLICEDATA, + ir.OUNSAFESTRING, + ir.OUNSAFESTRINGDATA: + return true + } + return false +} + // anyCallOrChan reports whether n contains any calls or channel operations. func anyCallOrChan(n ir.Node) bool { return ir.Any(n, func(n ir.Node) bool { - switch n.Op() { - case ir.OAPPEND, - ir.OCALL, - ir.OCALLFUNC, - ir.OCALLINTER, - ir.OCALLMETH, - ir.OCAP, - ir.OCLOSE, - ir.OCOMPLEX, - ir.OCOPY, - ir.ODELETE, - ir.OIMAG, - ir.OLEN, - ir.OMAKE, - ir.ONEW, - ir.OPANIC, - ir.OPRINT, - ir.OPRINTN, - ir.OREAL, - ir.ORECOVER, - ir.ORECV, - ir.OUNSAFEADD, - ir.OUNSAFESLICE: - return true - } - return false + return callOrChan(n) }) } -// A constSet represents a set of Go constant expressions. -type constSet struct { - m map[constSetKey]src.XPos -} - -type constSetKey struct { - typ *types.Type - val interface{} -} - -// add adds constant expression n to s. If a constant expression of -// equal value and identical type has already been added, then add -// reports an error about the duplicate value. -// -// pos provides position information for where expression n occurred -// (in case n does not have its own position information). what and -// where are used in the error message. -// -// n must not be an untyped constant. -func (s *constSet) add(pos src.XPos, n ir.Node, what, where string) { - if conv := n; conv.Op() == ir.OCONVIFACE { - conv := conv.(*ir.ConvExpr) - if conv.Implicit() { - n = conv.X - } - } - - if !ir.IsConstNode(n) || n.Type() == nil { - return - } - if n.Type().IsUntyped() { - base.Fatalf("%v is untyped", n) - } - - // Consts are only duplicates if they have the same value and - // identical types. - // - // In general, we have to use types.Identical to test type - // identity, because == gives false negatives for anonymous - // types and the byte/uint8 and rune/int32 builtin type - // aliases. However, this is not a problem here, because - // constant expressions are always untyped or have a named - // type, and we explicitly handle the builtin type aliases - // below. - // - // This approach may need to be revisited though if we fix - // #21866 by treating all type aliases like byte/uint8 and - // rune/int32. - - typ := n.Type() - switch typ { - case types.ByteType: - typ = types.Types[types.TUINT8] - case types.RuneType: - typ = types.Types[types.TINT32] - } - k := constSetKey{typ, ir.ConstValue(n)} - - if ir.HasUniquePos(n) { - pos = n.Pos() - } - - if s.m == nil { - s.m = make(map[constSetKey]src.XPos) - } - - if prevPos, isDup := s.m[k]; isDup { - base.ErrorfAt(pos, "duplicate %s %s in %s\n\tprevious %s at %v", - what, nodeAndVal(n), where, - what, base.FmtPos(prevPos)) - } else { - s.m[k] = pos - } -} - -// nodeAndVal reports both an expression and its constant value, if -// the latter is non-obvious. -// -// TODO(mdempsky): This could probably be a fmt.go flag. -func nodeAndVal(n ir.Node) string { - show := fmt.Sprint(n) - val := ir.ConstValue(n) - if s := fmt.Sprintf("%#v", val); show != s { - show += " (value " + s + ")" - } - return show -} - // evalunsafe evaluates a package unsafe operation and returns the result. func evalunsafe(n ir.Node) int64 { switch n.Op() { @@ -874,14 +787,16 @@ func evalunsafe(n ir.Node) int64 { } types.CalcSize(tr) if n.Op() == ir.OALIGNOF { - return int64(tr.Align) + return tr.Alignment() } - return tr.Width + return tr.Size() case ir.OOFFSETOF: // must be a selector. n := n.(*ir.UnaryExpr) - if n.X.Op() != ir.OXDOT { + // ODOT and ODOTPTR are allowed in case the OXDOT transformation has + // already happened (e.g. during -G=3 stenciling). + if n.X.Op() != ir.OXDOT && n.X.Op() != ir.ODOT && n.X.Op() != ir.ODOTPTR { base.Errorf("invalid expression %v", n) return 0 } @@ -893,6 +808,18 @@ func evalunsafe(n ir.Node) int64 { sel.X = Expr(sel.X) sbase := sel.X + // Implicit dot may already be resolved for instantiating generic function. So we + // need to remove any implicit dot until we reach the first non-implicit one, it's + // the right base selector. See issue #53137. + var clobberBase func(n ir.Node) ir.Node + clobberBase = func(n ir.Node) ir.Node { + if sel, ok := n.(*ir.SelectorExpr); ok && sel.Implicit() { + return clobberBase(sel.X) + } + return n + } + sbase = clobberBase(sbase) + tsel := Expr(sel) n.X = tsel if tsel.Type() == nil { @@ -901,7 +828,7 @@ func evalunsafe(n ir.Node) int64 { switch tsel.Op() { case ir.ODOT, ir.ODOTPTR: break - case ir.OCALLPART: + case ir.OMETHVALUE: base.Errorf("invalid expression %v: argument is a method value", n) return 0 default: diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go new file mode 100644 index 00000000000000..f14d885564230b --- /dev/null +++ b/src/cmd/compile/internal/typecheck/crawler.go @@ -0,0 +1,399 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package typecheck + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// crawlExports crawls the type/object graph rooted at the given list of exported +// objects (which are variables, functions, and types). It descends through all parts +// of types and follows methods on defined types. Any functions that are found to be +// potentially callable by importers directly or after inlining are marked with +// ExportInline, so that iexport.go knows to export their inline body. +// +// The overall purpose of crawlExports is to AVOID exporting inlineable methods +// that cannot actually be referenced, thereby reducing the size of the exports +// significantly. +// +// For non-generic defined types reachable from global variables, we only set +// ExportInline for exported methods. For defined types that are directly named or are +// embedded recursively in such a type, we set ExportInline for all methods, since +// these types can be embedded in another local type. For instantiated types that are +// used anywhere in a inlineable function, we set ExportInline on all methods of the +// base generic type, since all methods will be needed for creating any instantiated +// type. +func crawlExports(exports []*ir.Name) { + p := crawler{ + marked: make(map[*types.Type]bool), + embedded: make(map[*types.Type]bool), + generic: make(map[*types.Type]bool), + checkFullyInst: make(map[*types.Type]bool), + } + for _, n := range exports { + p.markObject(n) + } +} + +type crawler struct { + marked map[*types.Type]bool // types already seen by markType + embedded map[*types.Type]bool // types already seen by markEmbed + generic map[*types.Type]bool // types already seen by markGeneric + checkFullyInst map[*types.Type]bool // types already seen by checkForFullyInst +} + +// markObject visits a reachable object (function, method, global type, or global variable) +func (p *crawler) markObject(n *ir.Name) { + if n.Op() == ir.ONAME && n.Class == ir.PFUNC { + p.markInlBody(n) + } + + // If a declared type name is reachable, users can embed it in their + // own types, which makes even its unexported methods reachable. + if n.Op() == ir.OTYPE { + p.markEmbed(n.Type()) + } + + p.markType(n.Type()) +} + +// markType recursively visits types reachable from t to identify functions whose +// inline bodies may be needed. For instantiated generic types, it visits the base +// generic type, which has the relevant methods. +func (p *crawler) markType(t *types.Type) { + if orig := t.OrigType(); orig != nil { + // Convert to the base generic type. + t = orig + } + if p.marked[t] { + return + } + p.marked[t] = true + + // If this is a defined type, mark all of its associated + // methods. Skip interface types because t.Methods contains + // only their unexpanded method set (i.e., exclusive of + // interface embeddings), and the switch statement below + // handles their full method set. + if t.Sym() != nil && t.Kind() != types.TINTER { + for _, m := range t.Methods().Slice() { + if types.IsExported(m.Sym.Name) { + p.markObject(m.Nname.(*ir.Name)) + } + } + } + + // Recursively mark any types that can be produced given a + // value of type t: dereferencing a pointer; indexing or + // iterating over an array, slice, or map; receiving from a + // channel; accessing a struct field or interface method; or + // calling a function. + // + // Notably, we don't mark function parameter types, because + // the user already needs some way to construct values of + // those types. + switch t.Kind() { + case types.TPTR, types.TARRAY, types.TSLICE: + p.markType(t.Elem()) + + case types.TCHAN: + if t.ChanDir().CanRecv() { + p.markType(t.Elem()) + } + + case types.TMAP: + p.markType(t.Key()) + p.markType(t.Elem()) + + case types.TSTRUCT: + if t.IsFuncArgStruct() { + break + } + for _, f := range t.FieldSlice() { + // Mark the type of a unexported field if it is a + // fully-instantiated type, since we create and instantiate + // the methods of any fully-instantiated type that we see + // during import (see end of typecheck.substInstType). + if types.IsExported(f.Sym.Name) || f.Embedded != 0 || + isPtrFullyInstantiated(f.Type) { + p.markType(f.Type) + } + } + + case types.TFUNC: + for _, f := range t.Results().FieldSlice() { + p.markType(f.Type) + } + + case types.TINTER: + for _, f := range t.AllMethods().Slice() { + if types.IsExported(f.Sym.Name) { + p.markType(f.Type) + } + } + + case types.TTYPEPARAM: + // No other type that needs to be followed. + } +} + +// markEmbed is similar to markType, but handles finding methods that +// need to be re-exported because t can be embedded in user code +// (possibly transitively). +func (p *crawler) markEmbed(t *types.Type) { + if t.IsPtr() { + // Defined pointer type; not allowed to embed anyway. + if t.Sym() != nil { + return + } + t = t.Elem() + } + + if orig := t.OrigType(); orig != nil { + // Convert to the base generic type. + t = orig + } + + if p.embedded[t] { + return + } + p.embedded[t] = true + + // If t is a defined type, then re-export all of its methods. Unlike + // in markType, we include even unexported methods here, because we + // still need to generate wrappers for them, even if the user can't + // refer to them directly. + if t.Sym() != nil && t.Kind() != types.TINTER { + for _, m := range t.Methods().Slice() { + p.markObject(m.Nname.(*ir.Name)) + } + } + + // If t is a struct, recursively visit its embedded fields. + if t.IsStruct() { + for _, f := range t.FieldSlice() { + if f.Embedded != 0 { + p.markEmbed(f.Type) + } + } + } +} + +// markGeneric takes an instantiated type or a base generic type t, and marks all the +// methods of the base generic type of t. If a base generic type is written out for +// export, even if not explicitly marked for export, then all of its methods need to +// be available for instantiation, since we always create all methods of a specified +// instantiated type. Non-exported methods must generally be instantiated, since they may +// be called by the exported methods or other generic function in the same package. +func (p *crawler) markGeneric(t *types.Type) { + if t.IsPtr() { + t = t.Elem() + } + if orig := t.OrigType(); orig != nil { + // Convert to the base generic type. + t = orig + } + if p.generic[t] { + return + } + p.generic[t] = true + + if t.Sym() != nil && t.Kind() != types.TINTER { + for _, m := range t.Methods().Slice() { + p.markObject(m.Nname.(*ir.Name)) + } + } +} + +// checkForFullyInst looks for fully-instantiated types in a type (at any nesting +// level). If it finds a fully-instantiated type, it ensures that the necessary +// dictionary and shape methods are exported. It updates p.checkFullyInst, so it +// traverses each particular type only once. +func (p *crawler) checkForFullyInst(t *types.Type) { + if p.checkFullyInst[t] { + return + } + p.checkFullyInst[t] = true + + if t.IsFullyInstantiated() && !t.HasShape() && !t.IsInterface() && t.Methods().Len() > 0 { + // For any fully-instantiated type, the relevant + // dictionaries and shape instantiations will have + // already been created or are in the import data. + // Make sure that they are exported, so that any + // other package that inlines this function will have + // them available for import, and so will not need + // another round of method and dictionary + // instantiation after inlining. + baseType := t.OrigType() + shapes := make([]*types.Type, len(t.RParams())) + for i, t1 := range t.RParams() { + shapes[i] = Shapify(t1, i, baseType.RParams()[i]) + } + for j, tmethod := range t.Methods().Slice() { + baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name) + dictsym := MakeDictSym(baseNname.Sym(), t.RParams(), true) + if dictsym.Def == nil { + in := Resolve(ir.NewIdent(src.NoXPos, dictsym)) + dictsym = in.Sym() + } + Export(dictsym.Def.(*ir.Name)) + methsym := MakeFuncInstSym(baseNname.Sym(), shapes, false, true) + if methsym.Def == nil { + in := Resolve(ir.NewIdent(src.NoXPos, methsym)) + methsym = in.Sym() + } + methNode := methsym.Def.(*ir.Name) + Export(methNode) + if HaveInlineBody(methNode.Func) { + // Export the body as well if + // instantiation is inlineable. + ImportedBody(methNode.Func) + methNode.Func.SetExportInline(true) + } + // Make sure that any associated types are also exported. (See #52279) + p.checkForFullyInst(tmethod.Type) + } + } + + // Descend into the type. We descend even if it is a fully-instantiated type, + // since the instantiated type may have other instantiated types inside of + // it (in fields, methods, etc.). + switch t.Kind() { + case types.TPTR, types.TARRAY, types.TSLICE: + p.checkForFullyInst(t.Elem()) + + case types.TCHAN: + p.checkForFullyInst(t.Elem()) + + case types.TMAP: + p.checkForFullyInst(t.Key()) + p.checkForFullyInst(t.Elem()) + + case types.TSTRUCT: + if t.IsFuncArgStruct() { + break + } + for _, f := range t.FieldSlice() { + p.checkForFullyInst(f.Type) + } + + case types.TFUNC: + if recv := t.Recv(); recv != nil { + p.checkForFullyInst(t.Recv().Type) + } + for _, f := range t.Params().FieldSlice() { + p.checkForFullyInst(f.Type) + } + for _, f := range t.Results().FieldSlice() { + p.checkForFullyInst(f.Type) + } + + case types.TINTER: + for _, f := range t.AllMethods().Slice() { + p.checkForFullyInst(f.Type) + } + } +} + +// markInlBody marks n's inline body for export and recursively +// ensures all called functions are marked too. +func (p *crawler) markInlBody(n *ir.Name) { + if n == nil { + return + } + if n.Op() != ir.ONAME || n.Class != ir.PFUNC { + base.Fatalf("markInlBody: unexpected %v, %v, %v", n, n.Op(), n.Class) + } + fn := n.Func + if fn == nil { + base.Fatalf("markInlBody: missing Func on %v", n) + } + if !HaveInlineBody(fn) { + return + } + + if fn.ExportInline() { + return + } + fn.SetExportInline(true) + + ImportedBody(fn) + + var doFlood func(n ir.Node) + doFlood = func(n ir.Node) { + t := n.Type() + if t != nil { + if t.HasTParam() { + // If any generic types are used, then make sure that + // the methods of the generic type are exported and + // scanned for other possible exports. + p.markGeneric(t) + } else { + p.checkForFullyInst(t) + } + if base.Debug.Unified == 0 { + // If a method of un-exported type is promoted and accessible by + // embedding in an exported type, it makes that type reachable. + // + // Example: + // + // type t struct {} + // func (t) M() {} + // + // func F() interface{} { return struct{ t }{} } + // + // We generate the wrapper for "struct{ t }".M, and inline call + // to "struct{ t }".M, which makes "t.M" reachable. + if t.IsStruct() { + for _, f := range t.FieldSlice() { + if f.Embedded != 0 { + p.markEmbed(f.Type) + } + } + } + } + } + + switch n.Op() { + case ir.OMETHEXPR, ir.ODOTMETH: + p.markInlBody(ir.MethodExprName(n)) + case ir.ONAME: + n := n.(*ir.Name) + switch n.Class { + case ir.PFUNC: + p.markInlBody(n) + // Note: this Export() and the one below seem unneeded, + // since any function/extern name encountered in an + // exported function body will be exported + // automatically via qualifiedIdent() in iexport.go. + Export(n) + case ir.PEXTERN: + Export(n) + } + case ir.OMETHVALUE: + // Okay, because we don't yet inline indirect + // calls to method values. + case ir.OCLOSURE: + // VisitList doesn't visit closure bodies, so force a + // recursive call to VisitList on the body of the closure. + ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood) + } + } + + // Recursively identify all referenced functions for + // reexport. We want to include even non-called functions, + // because after inlining they might be callable. + ir.VisitList(fn.Inl.Body, doFlood) +} + +// isPtrFullyInstantiated returns true if t is a fully-instantiated type, or it is a +// pointer to a fully-instantiated type. +func isPtrFullyInstantiated(t *types.Type) bool { + return t.IsPtr() && t.Elem().IsFullyInstantiated() || + t.IsFullyInstantiated() +} diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go index 5b771e3c0b1d83..4001fa5f5ea662 100644 --- a/src/cmd/compile/internal/typecheck/dcl.go +++ b/src/cmd/compile/internal/typecheck/dcl.go @@ -6,7 +6,7 @@ package typecheck import ( "fmt" - "strconv" + "sync" "cmd/compile/internal/base" "cmd/compile/internal/ir" @@ -16,19 +16,24 @@ import ( var DeclContext ir.Class = ir.PEXTERN // PEXTERN/PAUTO -func DeclFunc(sym *types.Sym, tfn ir.Ntype) *ir.Func { - if tfn.Op() != ir.OTFUNC { - base.Fatalf("expected OTFUNC node, got %v", tfn) - } - +func DeclFunc(sym *types.Sym, recv *ir.Field, params, results []*ir.Field) *ir.Func { fn := ir.NewFunc(base.Pos) fn.Nname = ir.NewNameAt(base.Pos, sym) fn.Nname.Func = fn fn.Nname.Defn = fn - fn.Nname.Ntype = tfn ir.MarkFunc(fn.Nname) StartFuncBody(fn) - fn.Nname.Ntype = typecheckNtype(fn.Nname.Ntype) + + var recv1 *types.Field + if recv != nil { + recv1 = declareParam(ir.PPARAM, -1, recv) + } + + typ := types.NewSignature(types.LocalPkg, recv1, nil, declareParams(ir.PPARAM, params), declareParams(ir.PPARAMOUT, results)) + checkdupfields("argument", typ.Recvs().FieldSlice(), typ.Params().FieldSlice(), typ.Results().FieldSlice()) + fn.Nname.SetType(typ) + fn.Nname.SetTypecheck(1) + return fn } @@ -70,16 +75,6 @@ func Declare(n *ir.Name, ctxt ir.Class) { n.SetFrameOffset(0) } - if s.Block == types.Block { - // functype will print errors about duplicate function arguments. - // Don't repeat the error here. - if ctxt != ir.PPARAM && ctxt != ir.PPARAMOUT { - Redeclared(n.Pos(), s, "in this block") - } - } - - s.Block = types.Block - s.Lastlineno = base.Pos s.Def = n n.Class = ctxt if ctxt == ir.PFUNC { @@ -103,38 +98,6 @@ func Export(n *ir.Name) { Target.Exports = append(Target.Exports, n) } -// Redeclared emits a diagnostic about symbol s being redeclared at pos. -func Redeclared(pos src.XPos, s *types.Sym, where string) { - if !s.Lastlineno.IsKnown() { - var pkgName *ir.PkgName - if s.Def == nil { - for id, pkg := range DotImportRefs { - if id.Sym().Name == s.Name { - pkgName = pkg - break - } - } - } else { - pkgName = DotImportRefs[s.Def.(*ir.Ident)] - } - base.ErrorfAt(pos, "%v redeclared %s\n"+ - "\t%v: previous declaration during import %q", s, where, base.FmtPos(pkgName.Pos()), pkgName.Pkg.Path) - } else { - prevPos := s.Lastlineno - - // When an import and a declaration collide in separate files, - // present the import as the "redeclared", because the declaration - // is visible where the import is, but not vice versa. - // See issue 4510. - if s.Def == nil { - pos, prevPos = prevPos, pos - } - - base.ErrorfAt(pos, "%v redeclared %s\n"+ - "\t%v: previous declaration", s, where, base.FmtPos(prevPos)) - } -} - // declare the function proper // and declare the arguments. // called in extern-declaration context @@ -146,12 +109,6 @@ func StartFuncBody(fn *ir.Func) { DeclContext = ir.PAUTO types.Markdcl() - - if fn.Nname.Ntype != nil { - funcargs(fn.Nname.Ntype.(*ir.FuncType)) - } else { - funcargs2(fn.Type()) - } } // finish the body. @@ -171,90 +128,6 @@ func CheckFuncStack() { } } -// Add a method, declared as a function. -// - msym is the method symbol -// - t is function type (with receiver) -// Returns a pointer to the existing or added Field; or nil if there's an error. -func addmethod(n *ir.Func, msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field { - if msym == nil { - base.Fatalf("no method symbol") - } - - // get parent type sym - rf := t.Recv() // ptr to this structure - if rf == nil { - base.Errorf("missing receiver") - return nil - } - - mt := types.ReceiverBaseType(rf.Type) - if mt == nil || mt.Sym() == nil { - pa := rf.Type - t := pa - if t != nil && t.IsPtr() { - if t.Sym() != nil { - base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t) - return nil - } - t = t.Elem() - } - - switch { - case t == nil || t.Broke(): - // rely on typecheck having complained before - case t.Sym() == nil: - base.Errorf("invalid receiver type %v (%v is not a defined type)", pa, t) - case t.IsPtr(): - base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t) - case t.IsInterface(): - base.Errorf("invalid receiver type %v (%v is an interface type)", pa, t) - default: - // Should have picked off all the reasons above, - // but just in case, fall back to generic error. - base.Errorf("invalid receiver type %v (%L / %L)", pa, pa, t) - } - return nil - } - - if local && mt.Sym().Pkg != types.LocalPkg { - base.Errorf("cannot define new methods on non-local type %v", mt) - return nil - } - - if msym.IsBlank() { - return nil - } - - if mt.IsStruct() { - for _, f := range mt.Fields().Slice() { - if f.Sym == msym { - base.Errorf("type %v has both field and method named %v", mt, msym) - f.SetBroke(true) - return nil - } - } - } - - for _, f := range mt.Methods().Slice() { - if msym.Name != f.Sym.Name { - continue - } - // types.Identical only checks that incoming and result parameters match, - // so explicitly check that the receiver parameters match too. - if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) { - base.Errorf("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t) - } - return f - } - - f := types.NewField(base.Pos, msym, t) - f.Nname = n.Nname - f.SetNointerface(nointerface) - - mt.Methods().Append(f) - return f -} - func autoexport(n *ir.Name, ctxt ir.Class) { if n.Sym().Pkg != types.LocalPkg { return @@ -314,13 +187,6 @@ func checkembeddedtype(t *types.Type) { } } -// TODO(mdempsky): Move to package types. -func FakeRecv() *types.Field { - return types.NewField(src.NoXPos, nil, types.FakeRecvType()) -} - -var fakeRecvField = FakeRecv - var funcStack []funcStackEnt // stack of previous values of ir.CurFunc/DeclContext type funcStackEnt struct { @@ -328,80 +194,44 @@ type funcStackEnt struct { dclcontext ir.Class } -func funcarg(n *ir.Field, ctxt ir.Class) { - if n.Sym == nil { - return +func declareParams(ctxt ir.Class, l []*ir.Field) []*types.Field { + fields := make([]*types.Field, len(l)) + for i, n := range l { + fields[i] = declareParam(ctxt, i, n) } - - name := ir.NewNameAt(n.Pos, n.Sym) - n.Decl = name - name.Ntype = n.Ntype - Declare(name, ctxt) + return fields } -func funcarg2(f *types.Field, ctxt ir.Class) { - if f.Sym == nil { - return - } - n := ir.NewNameAt(f.Pos, f.Sym) - f.Nname = n - n.SetType(f.Type) - Declare(n, ctxt) -} - -func funcargs(nt *ir.FuncType) { - if nt.Op() != ir.OTFUNC { - base.Fatalf("funcargs %v", nt.Op()) - } - - // declare the receiver and in arguments. - if nt.Recv != nil { - funcarg(nt.Recv, ir.PPARAM) - } - for _, n := range nt.Params { - funcarg(n, ir.PPARAM) - } +func declareParam(ctxt ir.Class, i int, param *ir.Field) *types.Field { + f := types.NewField(param.Pos, param.Sym, param.Type) + f.SetIsDDD(param.IsDDD) - // declare the out arguments. - gen := len(nt.Params) - for _, n := range nt.Results { - if n.Sym == nil { + sym := param.Sym + if ctxt == ir.PPARAMOUT { + if sym == nil { // Name so that escape analysis can track it. ~r stands for 'result'. - n.Sym = LookupNum("~r", gen) - gen++ - } - if n.Sym.IsBlank() { + sym = LookupNum("~r", i) + } else if sym.IsBlank() { // Give it a name so we can assign to it during return. ~b stands for 'blank'. // The name must be different from ~r above because if you have // func f() (_ int) // func g() int // f is allowed to use a plain 'return' with no arguments, while g is not. // So the two cases must be distinguished. - n.Sym = LookupNum("~b", gen) - gen++ + sym = LookupNum("~b", i) } - - funcarg(n, ir.PPARAMOUT) } -} -// Same as funcargs, except run over an already constructed TFUNC. -// This happens during import, where the hidden_fndcl rule has -// used functype directly to parse the function's type. -func funcargs2(t *types.Type) { - if t.Kind() != types.TFUNC { - base.Fatalf("funcargs2 %v", t) - } + if sym != nil { + name := ir.NewNameAt(param.Pos, sym) + name.SetType(f.Type) + name.SetTypecheck(1) + Declare(name, ctxt) - for _, f := range t.Recvs().Fields().Slice() { - funcarg2(f, ir.PPARAM) - } - for _, f := range t.Params().Fields().Slice() { - funcarg2(f, ir.PPARAM) - } - for _, f := range t.Results().Fields().Slice() { - funcarg2(f, ir.PPARAMOUT) + f.Nname = name } + + return f } func Temp(t *types.Type) *ir.Name { @@ -431,6 +261,7 @@ func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name { n := ir.NewNameAt(pos, s) s.Def = n n.SetType(t) + n.SetTypecheck(1) n.Class = ir.PAUTO n.SetEsc(ir.EscNever) n.Curfn = curfn @@ -443,20 +274,43 @@ func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name { return n } +var ( + autotmpnamesmu sync.Mutex + autotmpnames []string +) + // autotmpname returns the name for an autotmp variable numbered n. func autotmpname(n int) string { - // Give each tmp a different name so that they can be registerized. - // Add a preceding . to avoid clashing with legal names. - const prefix = ".autotmp_" - // Start with a buffer big enough to hold a large n. - b := []byte(prefix + " ")[:len(prefix)] - b = strconv.AppendInt(b, int64(n), 10) - return types.InternString(b) + autotmpnamesmu.Lock() + defer autotmpnamesmu.Unlock() + + // Grow autotmpnames, if needed. + if n >= len(autotmpnames) { + autotmpnames = append(autotmpnames, make([]string, n+1-len(autotmpnames))...) + autotmpnames = autotmpnames[:cap(autotmpnames)] + } + + s := autotmpnames[n] + if s == "" { + // Give each tmp a different name so that they can be registerized. + // Add a preceding . to avoid clashing with legal names. + prefix := ".autotmp_%d" + + s = fmt.Sprintf(prefix, n) + autotmpnames[n] = s + } + return s } // f is method type, with receiver. // return function type, receiver as first argument (or not). func NewMethodType(sig *types.Type, recv *types.Type) *types.Type { + if sig.HasTParam() { + base.Fatalf("NewMethodType with type parameters in signature %+v", sig) + } + if recv != nil && recv.HasTParam() { + base.Fatalf("NewMethodType with type parameters in receiver %+v", recv) + } nrecvs := 0 if recv != nil { nrecvs++ diff --git a/src/cmd/compile/internal/typecheck/export.go b/src/cmd/compile/internal/typecheck/export.go index 63d0a1ec6c656c..30726d4327f7ce 100644 --- a/src/cmd/compile/internal/typecheck/export.go +++ b/src/cmd/compile/internal/typecheck/export.go @@ -15,22 +15,22 @@ import ( // importalias declares symbol s as an imported type alias with type t. // ipkg is the package being imported -func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { - return importobj(ipkg, pos, s, ir.OTYPE, ir.PEXTERN, t) +func importalias(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { + return importobj(pos, s, ir.OTYPE, ir.PEXTERN, t) } // importconst declares symbol s as an imported constant with type t and value val. // ipkg is the package being imported -func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name { - n := importobj(ipkg, pos, s, ir.OLITERAL, ir.PEXTERN, t) +func importconst(pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name { + n := importobj(pos, s, ir.OLITERAL, ir.PEXTERN, t) n.SetVal(val) return n } // importfunc declares symbol s as an imported function with type t. // ipkg is the package being imported -func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { - n := importobj(ipkg, pos, s, ir.ONAME, ir.PFUNC, t) +func importfunc(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { + n := importobj(pos, s, ir.ONAME, ir.PFUNC, t) n.Func = ir.NewFunc(pos) n.Func.Nname = n return n @@ -38,8 +38,8 @@ func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir. // importobj declares symbol s as an imported object representable by op. // ipkg is the package being imported -func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name { - n := importsym(ipkg, pos, s, op, ctxt) +func importobj(pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name { + n := importsym(pos, s, op, ctxt) n.SetType(t) if ctxt == ir.PFUNC { n.Sym().SetFunc(true) @@ -47,7 +47,7 @@ func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Cl return n } -func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name { +func importsym(pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name { if n := s.PkgDef(); n != nil { base.Fatalf("importsym of symbol that already exists: %v", n) } @@ -61,14 +61,14 @@ func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Cl // importtype returns the named type declared by symbol s. // If no such type has been declared yet, a forward declaration is returned. // ipkg is the package being imported -func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *ir.Name { - n := importsym(ipkg, pos, s, ir.OTYPE, ir.PEXTERN) +func importtype(pos src.XPos, s *types.Sym) *ir.Name { + n := importsym(pos, s, ir.OTYPE, ir.PEXTERN) n.SetType(types.NewNamed(n)) return n } // importvar declares symbol s as an imported variable with type t. // ipkg is the package being imported -func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { - return importobj(ipkg, pos, s, ir.ONAME, ir.PEXTERN, t) +func importvar(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name { + return importobj(pos, s, ir.ONAME, ir.PEXTERN, t) } diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go index 24d141e8a2ce91..96f368363a2c65 100644 --- a/src/cmd/compile/internal/typecheck/expr.go +++ b/src/cmd/compile/internal/typecheck/expr.go @@ -58,10 +58,6 @@ func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) { base.Errorf("invalid operation: %v (shift count type %v, must be integer)", n, r.Type()) return l, r, nil } - if t.IsSigned() && !types.AllowsGoVersion(curpkg(), 1, 13) { - base.ErrorfVers("go1.13", "invalid operation: %v (signed shift count type %v)", n, r.Type()) - return l, r, nil - } t = l.Type() if t != nil && t.Kind() != types.TIDEAL && !t.IsInteger() { base.Errorf("invalid operation: %v (shift of type %v)", n, t) @@ -77,15 +73,12 @@ func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) { return l, r, t } -func IsCmp(op ir.Op) bool { - return iscmp[op] -} - // tcArith typechecks operands of a binary arithmetic expression. // The result of tcArith MUST be assigned back to original operands, // t is the type of the expression, and should be set by the caller. e.g: -// n.X, n.Y, t = tcArith(n, op, n.X, n.Y) -// n.SetType(t) +// +// n.X, n.Y, t = tcArith(n, op, n.X, n.Y) +// n.SetType(t) func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) { l, r = defaultlit2(l, r, false) if l.Type() == nil || r.Type() == nil { @@ -96,7 +89,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) t = r.Type() } aop := ir.OXXX - if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { + if n.Op().IsCmp() && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) { // comparison is okay as long as one side is // assignable to the other. convert so they have // the same type. @@ -114,7 +107,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) } types.CalcSize(l.Type()) - if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 { + if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Size() >= 1<<16 { l = ir.NewConvExpr(base.Pos, aop, r.Type(), l) l.SetTypecheck(1) } @@ -133,7 +126,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) } types.CalcSize(r.Type()) - if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 { + if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Size() >= 1<<16 { r = ir.NewConvExpr(base.Pos, aop, l.Type(), r) r.SetTypecheck(1) } @@ -202,7 +195,8 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) } // The result of tcCompLit MUST be assigned back to n, e.g. -// n.Left = tcCompLit(n.Left) +// +// n.Left = tcCompLit(n.Left) func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { if base.EnableTrace && base.Flag.LowerT { defer tracePrint("tcCompLit", n)(&res) @@ -213,39 +207,13 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { base.Pos = lno }() - if n.Ntype == nil { - base.ErrorfAt(n.Pos(), "missing type in composite literal") - n.SetType(nil) - return n - } - // Save original node (including n.Right) n.SetOrig(ir.Copy(n)) - ir.SetPos(n.Ntype) - - // Need to handle [...]T arrays specially. - if array, ok := n.Ntype.(*ir.ArrayType); ok && array.Elem != nil && array.Len == nil { - array.Elem = typecheckNtype(array.Elem) - elemType := array.Elem.Type() - if elemType == nil { - n.SetType(nil) - return n - } - length := typecheckarraylit(elemType, -1, n.List, "array literal") - n.SetOp(ir.OARRAYLIT) - n.SetType(types.NewArray(elemType, length)) - n.Ntype = nil - return n - } + ir.SetPos(n) - n.Ntype = typecheckNtype(n.Ntype) - t := n.Ntype.Type() - if t == nil { - n.SetType(nil) - return n - } - n.SetType(t) + t := n.Type() + base.AssertfAt(t != nil, n.Pos(), "missing type in composite literal") switch t.Kind() { default: @@ -255,16 +223,13 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { case types.TARRAY: typecheckarraylit(t.Elem(), t.NumElem(), n.List, "array literal") n.SetOp(ir.OARRAYLIT) - n.Ntype = nil case types.TSLICE: length := typecheckarraylit(t.Elem(), -1, n.List, "slice literal") n.SetOp(ir.OSLICELIT) - n.Ntype = nil n.Len = length case types.TMAP: - var cs constSet for i3, l := range n.List { ir.SetPos(l) if l.Op() != ir.OKEY { @@ -275,19 +240,15 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { l := l.(*ir.KeyExpr) r := l.Key - r = pushtype(r, t.Key()) r = Expr(r) l.Key = AssignConv(r, t.Key(), "map key") - cs.add(base.Pos, l.Key, "key", "map literal") r = l.Value - r = pushtype(r, t.Elem()) r = Expr(r) l.Value = AssignConv(r, t.Elem(), "map value") } n.SetOp(ir.OMAPLIT) - n.Ntype = nil case types.TSTRUCT: // Need valid field offsets for Xoffset below. @@ -311,14 +272,23 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { f := t.Field(i) s := f.Sym - if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg { - base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t) + + // Do the test for assigning to unexported fields. + // But if this is an instantiated function, then + // the function has already been typechecked. In + // that case, don't do the test, since it can fail + // for the closure structs created in + // walkClosure(), because the instantiated + // function is compiled as if in the source + // package of the generic function. + if !(ir.CurFunc != nil && strings.Index(ir.CurFunc.Nname.Sym().Name, "[") >= 0) { + if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg { + base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t) + } } // No pushtype allowed here. Must name fields for that. n1 = AssignConv(n1, f.Type, "field value") - sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1) - sk.Offset = f.Offset - ls[i] = sk + ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1) } if len(ls) < t.NumFields() { base.Errorf("too few values in %v", n) @@ -328,87 +298,90 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) { // keyed list ls := n.List - for i, l := range ls { - ir.SetPos(l) - - if l.Op() == ir.OKEY { - kv := l.(*ir.KeyExpr) - key := kv.Key - - // Sym might have resolved to name in other top-level - // package, because of import dot. Redirect to correct sym - // before we do the lookup. - s := key.Sym() - if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil { - s = Lookup(s.Name) - } - - // An OXDOT uses the Sym field to hold - // the field to the right of the dot, - // so s will be non-nil, but an OXDOT - // is never a valid struct literal key. - if s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank() { - base.Errorf("invalid field name %v in struct initializer", key) - continue - } - - l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value) - ls[i] = l - } - - if l.Op() != ir.OSTRUCTKEY { - if !errored { - base.Errorf("mixture of field:value and value initializers") - errored = true - } - ls[i] = Expr(ls[i]) - continue - } - l := l.(*ir.StructKeyExpr) - - f := Lookdot1(nil, l.Field, t, t.Fields(), 0) - if f == nil { - if ci := Lookdot1(nil, l.Field, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup. - if visible(ci.Sym) { - base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", l.Field, t, ci.Sym) - } else if nonexported(l.Field) && l.Field.Name == ci.Sym.Name { // Ensure exactness before the suggestion. - base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", l.Field, t) - } else { - base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) + for i, n := range ls { + ir.SetPos(n) + + sk, ok := n.(*ir.StructKeyExpr) + if !ok { + kv, ok := n.(*ir.KeyExpr) + if !ok { + if !errored { + base.Errorf("mixture of field:value and value initializers") + errored = true } + ls[i] = Expr(n) continue } - var f *types.Field - p, _ := dotpath(l.Field, t, &f, true) - if p == nil || f.IsMethod() { - base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t) + + sk = tcStructLitKey(t, kv) + if sk == nil { continue } - // dotpath returns the parent embedded types in reverse order. - var ep []string - for ei := len(p) - 1; ei >= 0; ei-- { - ep = append(ep, p[ei].field.Sym.Name) - } - ep = append(ep, l.Field.Name) - base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t) - continue + + fielddup(sk.Sym().Name, hash) } - fielddup(f.Sym.Name, hash) - l.Offset = f.Offset // No pushtype allowed here. Tried and rejected. - l.Value = Expr(l.Value) - l.Value = AssignConv(l.Value, f.Type, "field value") + sk.Value = Expr(sk.Value) + sk.Value = AssignConv(sk.Value, sk.Field.Type, "field value") + ls[i] = sk } } n.SetOp(ir.OSTRUCTLIT) - n.Ntype = nil } return n } +// tcStructLitKey typechecks an OKEY node that appeared within a +// struct literal. +func tcStructLitKey(typ *types.Type, kv *ir.KeyExpr) *ir.StructKeyExpr { + key := kv.Key + + sym := key.Sym() + + // An OXDOT uses the Sym field to hold + // the field to the right of the dot, + // so s will be non-nil, but an OXDOT + // is never a valid struct literal key. + if sym == nil || sym.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || sym.IsBlank() { + base.Errorf("invalid field name %v in struct initializer", key) + return nil + } + + if f := Lookdot1(nil, sym, typ, typ.Fields(), 0); f != nil { + return ir.NewStructKeyExpr(kv.Pos(), f, kv.Value) + } + + if ci := Lookdot1(nil, sym, typ, typ.Fields(), 2); ci != nil { // Case-insensitive lookup. + if visible(ci.Sym) { + base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", sym, typ, ci.Sym) + } else if nonexported(sym) && sym.Name == ci.Sym.Name { // Ensure exactness before the suggestion. + base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", sym, typ) + } else { + base.Errorf("unknown field '%v' in struct literal of type %v", sym, typ) + } + return nil + } + + var f *types.Field + p, _ := dotpath(sym, typ, &f, true) + if p == nil || f.IsMethod() { + base.Errorf("unknown field '%v' in struct literal of type %v", sym, typ) + return nil + } + + // dotpath returns the parent embedded types in reverse order. + var ep []string + for ei := len(p) - 1; ei >= 0; ei-- { + ep = append(ep, p[ei].field.Sym.Name) + } + ep = append(ep, sym.Name) + base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), typ) + return nil +} + // tcConv typechecks an OCONV node. func tcConv(n *ir.ConvExpr) ir.Node { types.CheckSize(n.Type()) // ensure width is calculated for backend @@ -421,13 +394,7 @@ func tcConv(n *ir.ConvExpr) ir.Node { } op, why := Convertop(n.X.Op() == ir.OLITERAL, t, n.Type()) if op == ir.OXXX { - if !n.Diag() && !n.Type().Broke() && !n.X.Diag() { - base.Errorf("cannot convert %L to type %v%s", n.X, n.Type(), why) - n.SetDiag(true) - } - n.SetOp(ir.OCONV) - n.SetType(nil) - return n + base.Fatalf("cannot convert %L to type %v%s", n.X, n.Type(), why) } n.SetOp(op) @@ -451,6 +418,27 @@ func tcConv(n *ir.ConvExpr) ir.Node { if n.X.Op() == ir.OLITERAL { return stringtoruneslit(n) } + + case ir.OBYTES2STR: + if t.Elem() != types.ByteType && t.Elem() != types.Types[types.TUINT8] { + // If t is a slice of a user-defined byte type B (not uint8 + // or byte), then add an extra CONVNOP from []B to []byte, so + // that the call to slicebytetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.ByteType), n.X) + n.X.SetTypecheck(1) + } + + case ir.ORUNES2STR: + if t.Elem() != types.RuneType && t.Elem() != types.Types[types.TINT32] { + // If t is a slice of a user-defined rune type B (not uint32 + // or rune), then add an extra CONVNOP from []B to []rune, so + // that the call to slicerunetostring() added in walk will + // typecheck correctly. + n.X = ir.NewConvExpr(n.X.Pos(), ir.OCONVNOP, types.NewSlice(types.RuneType), n.X) + n.X.SetTypecheck(1) + } + } return n } @@ -522,8 +510,8 @@ func tcDot(n *ir.SelectorExpr, top int) ir.Node { } if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 { - n.SetOp(ir.OCALLPART) - n.SetType(MethodValueWrapper(n).Type()) + n.SetOp(ir.OMETHVALUE) + n.SetType(NewMethodType(n.Type(), nil)) } return n } @@ -544,14 +532,7 @@ func tcDotType(n *ir.TypeAssertExpr) ir.Node { return n } - if n.Ntype != nil { - n.Ntype = typecheckNtype(n.Ntype) - n.SetType(n.Ntype.Type()) - n.Ntype = nil - if n.Type() == nil { - return n - } - } + base.AssertfAt(n.Type() != nil, n.Pos(), "missing type: %v", n) if n.Type() != nil && !n.Type().IsInterface() { var missing, have *types.Field @@ -678,6 +659,40 @@ func tcLenCap(n *ir.UnaryExpr) ir.Node { return n } +// tcUnsafeData typechecks an OUNSAFESLICEDATA or OUNSAFESTRINGDATA node. +func tcUnsafeData(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + n.X = DefaultLit(n.X, nil) + l := n.X + t := l.Type() + if t == nil { + n.SetType(nil) + return n + } + + var kind types.Kind + if n.Op() == ir.OUNSAFESLICEDATA { + kind = types.TSLICE + } else { + /* kind is string */ + kind = types.TSTRING + } + + if t.Kind() != kind { + base.Errorf("invalid argument %L for %v", l, n.Op()) + n.SetType(nil) + return n + } + + if kind == types.TSTRING { + t = types.ByteType + } else { + t = t.Elem() + } + n.SetType(types.NewPtr(t)) + return n +} + // tcRecv typechecks an ORECV node. func tcRecv(n *ir.UnaryExpr) ir.Node { n.X = Expr(n.X) @@ -831,6 +846,31 @@ func tcSliceHeader(n *ir.SliceHeaderExpr) ir.Node { return n } +// tcStringHeader typechecks an OSTRINGHEADER node. +func tcStringHeader(n *ir.StringHeaderExpr) ir.Node { + t := n.Type() + if t == nil { + base.Fatalf("no type specified for OSTRINGHEADER") + } + + if !t.IsString() { + base.Fatalf("invalid type %v for OSTRINGHEADER", n.Type()) + } + + if n.Ptr == nil || n.Ptr.Type() == nil || !n.Ptr.Type().IsUnsafePtr() { + base.Fatalf("need unsafe.Pointer for OSTRINGHEADER") + } + + n.Ptr = Expr(n.Ptr) + n.Len = DefaultLit(Expr(n.Len), types.Types[types.TINT]) + + if ir.IsConst(n.Len, constant.Int) && ir.Int64Val(n.Len) < 0 { + base.Fatalf("len for OSTRINGHEADER must be non-negative") + } + + return n +} + // tcStar typechecks an ODEREF node, which may be an expression or a type. func tcStar(n *ir.StarExpr, top int) ir.Node { n.X = typecheck(n.X, ctxExpr|ctxType) @@ -840,11 +880,11 @@ func tcStar(n *ir.StarExpr, top int) ir.Node { n.SetType(nil) return n } + + // TODO(mdempsky): Remove (along with ctxType above) once I'm + // confident this code path isn't needed any more. if l.Op() == ir.OTYPE { - n.SetOTYPE(types.NewPtr(l.Type())) - // Ensure l.Type gets CalcSize'd for the backend. Issue 20174. - types.CheckSize(l.Type()) - return n + base.Fatalf("unexpected type in deref expression: %v", l) } if !t.IsPtr() { diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go index fbcc784627d6ce..7cf9d5cb404f90 100644 --- a/src/cmd/compile/internal/typecheck/func.go +++ b/src/cmd/compile/internal/typecheck/func.go @@ -8,28 +8,29 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" + "cmd/internal/src" "fmt" "go/constant" "go/token" ) -// package all the arguments that match a ... T parameter into a []T. -func MakeDotArgs(typ *types.Type, args []ir.Node) ir.Node { +// MakeDotArgs package all the arguments that match a ... T parameter into a []T. +func MakeDotArgs(pos src.XPos, typ *types.Type, args []ir.Node) ir.Node { var n ir.Node if len(args) == 0 { - n = NodNil() + n = ir.NewNilExpr(pos) n.SetType(typ) } else { - lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil) - lit.List.Append(args...) + args = append([]ir.Node(nil), args...) + lit := ir.NewCompLitExpr(pos, ir.OCOMPLIT, typ, args) lit.SetImplicit(true) n = lit } n = Expr(n) if n.Type() == nil { - base.Fatalf("mkdotargslice: typecheck failed") + base.FatalfAt(pos, "mkdotargslice: typecheck failed") } return n } @@ -47,7 +48,7 @@ func FixVariadicCall(call *ir.CallExpr) { args := call.Args extra := args[vi:] - slice := MakeDotArgs(vt, extra) + slice := MakeDotArgs(call.Pos(), vt, extra) for i := range extra { extra[i] = nil // allow GC } @@ -56,6 +57,25 @@ func FixVariadicCall(call *ir.CallExpr) { call.IsDDD = true } +// FixMethodCall rewrites a method call t.M(...) into a function call T.M(t, ...). +func FixMethodCall(call *ir.CallExpr) { + if call.X.Op() != ir.ODOTMETH { + return + } + + dot := call.X.(*ir.SelectorExpr) + + fn := Expr(ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym)) + + args := make([]ir.Node, 1+len(call.Args)) + args[0] = dot.X + copy(args[1:], call.Args) + + call.SetOp(ir.OCALLFUNC) + call.X = fn + call.Args = args +} + // ClosureType returns the struct type used to hold all the information // needed in the closure for clo (clo must be a OCLOSURE node). // The address of a variable of the returned type can be cast to a func. @@ -73,8 +93,25 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type { // The information appears in the binary in the form of type descriptors; // the struct is unnamed so that closures in multiple packages with the // same struct type can share the descriptor. + + // Make sure the .F field is in the same package as the rest of the + // fields. This deals with closures in instantiated functions, which are + // compiled as if from the source package of the generic function. + var pkg *types.Pkg + if len(clo.Func.ClosureVars) == 0 { + pkg = types.LocalPkg + } else { + for _, v := range clo.Func.ClosureVars { + if pkg == nil { + pkg = v.Sym().Pkg + } else if pkg != v.Sym().Pkg { + base.Fatalf("Closure variables from multiple packages: %+v", clo) + } + } + } + fields := []*types.Field{ - types.NewField(base.Pos, Lookup(".F"), types.Types[types.TUINTPTR]), + types.NewField(base.Pos, pkg.Lookup(".F"), types.Types[types.TUINTPTR]), } for _, v := range clo.Func.ClosureVars { typ := v.Type() @@ -88,10 +125,10 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type { return typ } -// PartialCallType returns the struct type used to hold all the information -// needed in the closure for n (n must be a OCALLPART node). -// The address of a variable of the returned type can be cast to a func. -func PartialCallType(n *ir.SelectorExpr) *types.Type { +// MethodValueType returns the struct type used to hold all the information +// needed in the closure for a OMETHVALUE node. The address of a variable of +// the returned type can be cast to a func. +func MethodValueType(n *ir.SelectorExpr) *types.Type { t := types.NewStruct(types.NoPkg, []*types.Field{ types.NewField(base.Pos, Lookup("F"), types.Types[types.TUINTPTR]), types.NewField(base.Pos, Lookup("R"), n.X.Type()), @@ -100,11 +137,6 @@ func PartialCallType(n *ir.SelectorExpr) *types.Type { return t } -// True if we are typechecking an inline body in ImportedBody below. We use this -// flag to not create a new closure function in tcClosure when we are just -// typechecking an inline body, as opposed to the body of a real function. -var inTypeCheckInl bool - // ImportedBody returns immediately if the inlining information for fn is // populated. Otherwise, fn must be an imported function. If so, ImportedBody // loads in the dcls and body for fn, and typechecks as needed. @@ -123,7 +155,12 @@ func ImportedBody(fn *ir.Func) { IncrementalAddrtaken = false defer func() { if DirtyAddrtaken { - ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available + // We do ComputeAddrTaken on function instantiations, but not + // generic functions (since we may not yet know if x in &x[i] + // is an array or a slice). + if !fn.Type().HasTParam() { + ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available + } DirtyAddrtaken = false } IncrementalAddrtaken = true @@ -145,19 +182,6 @@ func ImportedBody(fn *ir.Func) { fmt.Printf("typecheck import [%v] %L { %v }\n", fn.Sym(), fn, ir.Nodes(fn.Inl.Body)) } - if !go117ExportTypes { - // If we didn't export & import types, typecheck the code here. - savefn := ir.CurFunc - ir.CurFunc = fn - if inTypeCheckInl { - base.Fatalf("inTypeCheckInl should not be set recursively") - } - inTypeCheckInl = true - Stmts(fn.Inl.Body) - inTypeCheckInl = false - ir.CurFunc = savefn - } - base.Pos = lno } @@ -181,153 +205,32 @@ func fnpkg(fn *ir.Name) *types.Pkg { return fn.Sym().Pkg } -// ClosureName generates a new unique name for a closure within -// outerfunc. -func ClosureName(outerfunc *ir.Func) *types.Sym { - outer := "glob." - prefix := "func" - gen := &globClosgen - - if outerfunc != nil { - if outerfunc.OClosure != nil { - prefix = "" - } - - outer = ir.FuncName(outerfunc) - - // There may be multiple functions named "_". In those - // cases, we can't use their individual Closgens as it - // would lead to name clashes. - if !ir.IsBlank(outerfunc.Nname) { - gen = &outerfunc.Closgen - } - } - - *gen++ - return Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) -} - -// globClosgen is like Func.Closgen, but for the global scope. -var globClosgen int32 - -// MethodValueWrapper returns the DCLFUNC node representing the -// wrapper function (*-fm) needed for the given method value. If the -// wrapper function hasn't already been created yet, it's created and -// added to Target.Decls. -// -// TODO(mdempsky): Move into walk. This isn't part of type checking. -func MethodValueWrapper(dot *ir.SelectorExpr) *ir.Func { - if dot.Op() != ir.OCALLPART { - base.Fatalf("MethodValueWrapper: unexpected %v (%v)", dot, dot.Op()) - } - - t0 := dot.Type() - meth := dot.Sel - rcvrtype := dot.X.Type() - sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm") - - if sym.Uniq() { - return sym.Def.(*ir.Func) - } - sym.SetUniq(true) - - savecurfn := ir.CurFunc - saveLineNo := base.Pos - ir.CurFunc = nil - - // Set line number equal to the line number where the method is declared. - if pos := dot.Selection.Pos; pos.IsKnown() { - base.Pos = pos - } - // Note: !dot.Selection.Pos.IsKnown() happens for method expressions where - // the method is implicitly declared. The Error method of the - // built-in error type is one such method. We leave the line - // number at the use of the method expression in this - // case. See issue 29389. - - tfn := ir.NewFuncType(base.Pos, nil, - NewFuncParams(t0.Params(), true), - NewFuncParams(t0.Results(), false)) - - fn := DeclFunc(sym, tfn) - fn.SetDupok(true) - fn.SetNeedctxt(true) - fn.SetWrapper(true) - - // Declare and initialize variable holding receiver. - ptr := ir.NewNameAt(base.Pos, Lookup(".this")) - ptr.Class = ir.PAUTOHEAP - ptr.SetType(rcvrtype) - ptr.Curfn = fn - ptr.SetIsClosureVar(true) - ptr.SetByval(true) - fn.ClosureVars = append(fn.ClosureVars, ptr) - - call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil) - call.Args = ir.ParamNames(tfn.Type()) - call.IsDDD = tfn.Type().IsVariadic() - - var body ir.Node = call - if t0.NumResults() != 0 { - ret := ir.NewReturnStmt(base.Pos, nil) - ret.Results = []ir.Node{call} - body = ret - } - - fn.Body = []ir.Node{body} - FinishFuncBody() - - Func(fn) - // Need to typecheck the body of the just-generated wrapper. - // typecheckslice() requires that Curfn is set when processing an ORETURN. - ir.CurFunc = fn - Stmts(fn.Body) - sym.Def = fn - Target.Decls = append(Target.Decls, fn) - ir.CurFunc = savecurfn - base.Pos = saveLineNo - - return fn -} - // tcClosure typechecks an OCLOSURE node. It also creates the named // function associated with the closure. // TODO: This creation of the named function should probably really be done in a // separate pass from type-checking. -func tcClosure(clo *ir.ClosureExpr, top int) { +func tcClosure(clo *ir.ClosureExpr, top int) ir.Node { fn := clo.Func - // Set current associated iota value, so iota can be used inside - // function in ConstSpec, see issue #22344 - if x := getIotaValue(); x >= 0 { - fn.Iota = x - } - - fn.SetClosureCalled(top&ctxCallee != 0) - // Do not typecheck fn twice, otherwise, we will end up pushing - // fn to Target.Decls multiple times, causing InitLSym called twice. - // See #30709 + // We used to allow IR builders to typecheck the underlying Func + // themselves, but that led to too much variety and inconsistency + // around who's responsible for naming the function, typechecking + // it, or adding it to Target.Decls. + // + // It's now all or nothing. Callers are still allowed to do these + // themselves, but then they assume responsibility for all of them. if fn.Typecheck() == 1 { - clo.SetType(fn.Type()) - return + base.FatalfAt(fn.Pos(), "underlying closure func already typechecked: %v", fn) } - // Don't give a name and add to Target.Decls if we are typechecking an inlined - // body in ImportedBody(), since we only want to create the named function - // when the closure is actually inlined (and then we force a typecheck - // explicitly in (*inlsubst).node()). - if !inTypeCheckInl { - fn.Nname.SetSym(ClosureName(ir.CurFunc)) - ir.MarkFunc(fn.Nname) - } + ir.NameClosure(clo, ir.CurFunc) Func(fn) - clo.SetType(fn.Type()) // Type check the body now, but only if we're inside a function. // At top level (in a variable initialization: curfn==nil) we're not // ready to type check code yet; we'll check it later, because the // underlying closure function we create is added to Target.Decls. - if ir.CurFunc != nil && clo.Type() != nil { + if ir.CurFunc != nil { oldfn := ir.CurFunc ir.CurFunc = fn Stmts(fn.Body) @@ -353,14 +256,9 @@ func tcClosure(clo *ir.ClosureExpr, top int) { } fn.ClosureVars = fn.ClosureVars[:out] - if base.Flag.W > 1 { - s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn)) - ir.Dump(s, fn) - } - if !inTypeCheckInl { - // Add function to Target.Decls once only when we give it a name - Target.Decls = append(Target.Decls, fn) - } + clo.SetType(fn.Type()) + + return ir.UseClosure(clo, Target) } // type check function definition @@ -371,34 +269,16 @@ func tcFunc(n *ir.Func) { defer tracePrint("tcFunc", n)(nil) } - n.Nname = AssignExpr(n.Nname).(*ir.Name) - t := n.Nname.Type() - if t == nil { - return - } - rcvr := t.Recv() - if rcvr != nil && n.Shortname != nil { - m := addmethod(n, n.Shortname, t, true, n.Pragma&ir.Nointerface != 0) - if m == nil { - return - } - - n.Nname.SetSym(ir.MethodSym(rcvr.Type, n.Shortname)) - Declare(n.Nname, ir.PFUNC) + if name := n.Nname; name.Typecheck() == 0 { + base.AssertfAt(name.Type() != nil, n.Pos(), "missing type: %v", name) + name.SetTypecheck(1) } } // tcCall typechecks an OCALL node. func tcCall(n *ir.CallExpr, top int) ir.Node { - n.Use = ir.CallUseExpr - if top == ctxStmt { - n.Use = ir.CallUseStmt - } Stmts(n.Init()) // imported rewritten f(g()) calls (#30907) n.X = typecheck(n.X, ctxExpr|ctxType|ctxCallee) - if n.X.Diag() { - n.SetDiag(true) - } l := n.X @@ -419,7 +299,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { n.SetTypecheck(0) // re-typechecking new op is OK, not a loop return typecheck(n, top) - case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: + case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: typecheckargs(n) fallthrough case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: @@ -431,7 +311,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg) return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init - case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: + case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: typecheckargs(n) arg1, arg2, ok := needTwoArgs(n) if !ok { @@ -448,10 +328,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { l = n.X if l.Op() == ir.OTYPE { if n.IsDDD { - if !l.Type().Broke() { - base.Errorf("invalid use of ... in type conversion to %v", l.Type()) - } - n.SetDiag(true) + base.Fatalf("invalid use of ... in type conversion to %v", l.Type()) } // pick off before type-checking arguments @@ -466,6 +343,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { return tcConv(n) } + RewriteNonNameCall(n) typecheckargs(n) t := l.Type() if t == nil { @@ -509,6 +387,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node { } typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) }) + FixMethodCall(n) if t.NumResults() == 0 { return n } @@ -581,12 +460,11 @@ func tcAppend(n *ir.CallExpr) ir.Node { return n } - if t.Elem().IsKind(types.TUINT8) && args[1].Type().IsString() { - args[1] = DefaultLit(args[1], types.Types[types.TSTRING]) - return n - } - - args[1] = AssignConv(args[1], t.Underlying(), "append") + // AssignConv is of args[1] not required here, as the + // types of args[0] and args[1] don't need to match + // (They will both have an underlying type which are + // slices of identical base types, or be []byte and string.) + // See issue 53888. return n } @@ -979,14 +857,23 @@ func tcRecover(n *ir.CallExpr) ir.Node { return n } -// tcUnsafeAdd typechecks an OUNSAFEADD node. -func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr { - if !types.AllowsGoVersion(curpkg(), 1, 17) { - base.ErrorfVers("go1.17", "unsafe.Add") - n.SetType(nil) - return n +// tcRecoverFP typechecks an ORECOVERFP node. +func tcRecoverFP(n *ir.CallExpr) ir.Node { + if len(n.Args) != 1 { + base.FatalfAt(n.Pos(), "wrong number of arguments: %v", n) + } + + n.Args[0] = Expr(n.Args[0]) + if !n.Args[0].Type().IsPtrShaped() { + base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.Args[0]) } + n.SetType(types.Types[types.TINTER]) + return n +} + +// tcUnsafeAdd typechecks an OUNSAFEADD node. +func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr { n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add") n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT]) if n.X.Type() == nil || n.Y.Type() == nil { @@ -1003,12 +890,6 @@ func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr { // tcUnsafeSlice typechecks an OUNSAFESLICE node. func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr { - if !types.AllowsGoVersion(curpkg(), 1, 17) { - base.ErrorfVers("go1.17", "unsafe.Slice") - n.SetType(nil) - return n - } - n.X = Expr(n.X) n.Y = Expr(n.Y) if n.X.Type() == nil || n.Y.Type() == nil { @@ -1020,16 +901,37 @@ func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr { base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t) } else if t.Elem().NotInHeap() { // TODO(mdempsky): This can be relaxed, but should only affect the - // Go runtime itself. End users should only see //go:notinheap + // Go runtime itself. End users should only see not-in-heap // types due to incomplete C structs in cgo, and those types don't // have a meaningful size anyway. base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed") } - if !checkunsafeslice(&n.Y) { + if !checkunsafesliceorstring(n.Op(), &n.Y) { n.SetType(nil) return n } n.SetType(types.NewSlice(t.Elem())) return n } + +// tcUnsafeString typechecks an OUNSAFESTRING node. +func tcUnsafeString(n *ir.BinaryExpr) *ir.BinaryExpr { + n.X = Expr(n.X) + n.Y = Expr(n.Y) + if n.X.Type() == nil || n.Y.Type() == nil { + n.SetType(nil) + return n + } + t := n.X.Type() + if !t.IsPtr() || !types.Identical(t.Elem(), types.Types[types.TUINT8]) { + base.Errorf("first argument to unsafe.String must be *byte; have %L", t) + } + + if !checkunsafesliceorstring(n.Op(), &n.Y) { + n.SetType(nil) + return n + } + n.SetType(types.Types[types.TSTRING]) + return n +} diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index 64d68ef62550d8..848408d240fa6a 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -63,8 +63,9 @@ // } // // type Func struct { -// Tag byte // 'F' +// Tag byte // 'F' or 'G' // Pos Pos +// TypeParams []typeOff // only present if Tag == 'G' // Signature Signature // } // @@ -75,8 +76,9 @@ // } // // type Type struct { -// Tag byte // 'T' +// Tag byte // 'T' or 'U' // Pos Pos +// TypeParams []typeOff // only present if Tag == 'U' // Underlying typeOff // // Methods []struct{ // omitted if Underlying is an interface type @@ -93,6 +95,13 @@ // Type typeOff // } // +// // "Automatic" declaration of each typeparam +// type TypeParam struct { +// Tag byte // 'P' +// Pos Pos +// Implicit bool +// Constraint typeOff +// } // // typeOff means a uvarint that either indicates a predeclared type, // or an offset into the Data section. If the uvarint is less than @@ -100,11 +109,11 @@ // types list (see predeclared in bexport.go for order). Otherwise, // subtracting predeclReserved yields the offset of a type descriptor. // -// Value means a type and type-specific value. See +// Value means a type, kind, and type-specific value. See // (*exportWriter).value for details. // // -// There are nine kinds of type descriptors, distinguished by an itag: +// There are twelve kinds of type descriptors, distinguished by an itag: // // type DefinedType struct { // Tag itag // definedType @@ -172,6 +181,30 @@ // } // } // +// // Reference to a type param declaration +// type TypeParamType struct { +// Tag itag // typeParamType +// Name stringOff +// PkgPath stringOff +// } +// +// // Instantiation of a generic type (like List[T2] or List[int]) +// type InstanceType struct { +// Tag itag // instanceType +// Pos pos +// TypeArgs []typeOff +// BaseType typeOff +// } +// +// type UnionType struct { +// Tag itag // interfaceType +// Terms []struct { +// tilde bool +// Type typeOff +// } +// } +// +// // // type Signature struct { // Params []Param @@ -202,28 +235,38 @@ package typecheck import ( - "bufio" "bytes" - "crypto/md5" "encoding/binary" "fmt" "go/constant" "io" "math/big" "sort" + "strconv" "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" "cmd/internal/goobj" + "cmd/internal/notsha256" "cmd/internal/src" ) // Current indexed export format version. Increase with each format change. -// 1: added column details to Pos // 0: Go1.11 encoding -const iexportVersion = 1 +// 1: added column details to Pos +// 2: added information for generic function/types. The export of non-generic +// functions/types remains largely backward-compatible. Breaking changes include: +// - a 'kind' byte is added to constant values +const ( + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + iexportVersionGenerics = 2 + iexportVersionGo1_18 = 2 + + iexportVersionCurrent = 2 +) // predeclReserved is the number of type offsets reserved for types // implicitly declared in the universe block. @@ -244,6 +287,9 @@ const ( signatureType structType interfaceType + typeParamType + instanceType // Instantiation of a generic type + unionType ) const ( @@ -251,13 +297,22 @@ const ( magic = 0x6742937dc293105 ) -func WriteExports(out *bufio.Writer) { +// WriteExports writes the indexed export format to out. If extensions +// is true, then the compiler-only extensions are included. +func WriteExports(out io.Writer, extensions bool) { + if extensions { + // If we're exporting inline bodies, invoke the crawler to mark + // which bodies to include. + crawlExports(Target.Exports) + } + p := iexporter{ allPkgs: map[*types.Pkg]bool{}, stringIndex: map[string]uint64{}, declIndex: map[*types.Sym]uint64{}, inlineIndex: map[*types.Sym]uint64{}, typIndex: map[*types.Type]uint64{}, + extensions: extensions, } for i, pt := range predeclared() { @@ -293,12 +348,12 @@ func WriteExports(out *bufio.Writer) { // Assemble header. var hdr intWriter hdr.WriteByte('i') - hdr.uint64(iexportVersion) + hdr.uint64(iexportVersionCurrent) hdr.uint64(uint64(p.strings.Len())) hdr.uint64(dataLen) // Flush output. - h := md5.New() + h := notsha256.New() wr := io.MultiWriter(out, h) io.Copy(wr, &hdr) io.Copy(wr, &p.strings) @@ -339,15 +394,18 @@ func (w *exportWriter) writeIndex(index map[*types.Sym]uint64, mainIndex bool) { pkgs = append(pkgs, pkg) } sort.Slice(pkgs, func(i, j int) bool { - return pkgs[i].Path < pkgs[j].Path + return exportPath(pkgs[i]) < exportPath(pkgs[j]) }) + if mainIndex { + base.Assertf(pkgs[0] == types.LocalPkg, "LocalPkg must be first") + } w.uint64(uint64(len(pkgs))) for _, pkg := range pkgs { - w.string(pkg.Path) + w.string(exportPath(pkg)) if mainIndex { w.string(pkg.Name) - w.uint64(uint64(pkg.Height)) + w.uint64(0) // was package height, but not necessary anymore. } // Sort symbols within a package by name. @@ -379,6 +437,8 @@ type iexporter struct { declIndex map[*types.Sym]uint64 inlineIndex map[*types.Sym]uint64 typIndex map[*types.Type]uint64 + + extensions bool } // stringOff returns the offset of s within the string section. @@ -406,7 +466,7 @@ func (p *iexporter) pushDecl(n *ir.Name) { } // Don't export predeclared declarations. - if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == ir.Pkgs.Unsafe { + if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == types.UnsafePkg { return } @@ -449,7 +509,9 @@ func (p *iexporter) doDecl(n *ir.Name) { w.tag('V') w.pos(n.Pos()) w.typ(n.Type()) - w.varExt(n) + if w.p.extensions { + w.varExt(n) + } case ir.PFUNC: if ir.IsMethod(n) { @@ -457,10 +519,25 @@ func (p *iexporter) doDecl(n *ir.Name) { } // Function. - w.tag('F') + if n.Type().TParams().NumFields() == 0 { + w.tag('F') + } else { + w.tag('G') + } w.pos(n.Pos()) + // The tparam list of the function type is the + // declaration of the type params. So, write out the type + // params right now. Then those type params will be + // referenced via their type offset (via typOff) in all + // other places in the signature and function that they + // are used. + if n.Type().TParams().NumFields() > 0 { + w.tparamList(n.Type().TParams().FieldSlice()) + } w.signature(n.Type()) - w.funcExt(n) + if w.p.extensions { + w.funcExt(n) + } default: base.Fatalf("unexpected class: %v, %v", n, n.Class) @@ -476,10 +553,29 @@ func (p *iexporter) doDecl(n *ir.Name) { w.tag('C') w.pos(n.Pos()) w.value(n.Type(), n.Val()) - w.constExt(n) + if w.p.extensions { + w.constExt(n) + } case ir.OTYPE: - if types.IsDotAlias(n.Sym()) { + if n.Type().IsTypeParam() && n.Type().Underlying() == n.Type() { + // Even though it has local scope, a typeparam requires a + // declaration via its package and unique name, because it + // may be referenced within its type bound during its own + // definition. + w.tag('P') + // A typeparam has a name, and has a type bound rather + // than an underlying type. + w.pos(n.Pos()) + if iexportVersionCurrent >= iexportVersionGo1_18 { + implicit := n.Type().Bound().IsImplicit() + w.bool(implicit) + } + w.typ(n.Type().Bound()) + break + } + + if n.Alias() { // Alias. w.tag('A') w.pos(n.Pos()) @@ -488,9 +584,18 @@ func (p *iexporter) doDecl(n *ir.Name) { } // Defined type. - w.tag('T') + if len(n.Type().RParams()) == 0 { + w.tag('T') + } else { + w.tag('U') + } w.pos(n.Pos()) + if len(n.Type().RParams()) > 0 { + // Export type parameters, if any, needed for this type + w.typeList(n.Type().RParams()) + } + underlying := n.Type().Underlying() if underlying == types.ErrorType.Underlying() { // For "type T error", use error as the @@ -501,26 +606,38 @@ func (p *iexporter) doDecl(n *ir.Name) { // for predeclared objects). underlying = types.ErrorType } + if underlying == types.ComparableType.Underlying() { + // Do same for ComparableType as for ErrorType. + underlying = types.ComparableType + } + if underlying == types.AnyType.Underlying() { + // Do same for AnyType as for ErrorType. + underlying = types.AnyType + } w.typ(underlying) t := n.Type() if t.IsInterface() { - w.typeExt(t) + if w.p.extensions { + w.typeExt(t) + } break } - ms := t.Methods() - w.uint64(uint64(ms.Len())) - for _, m := range ms.Slice() { + methods := t.Methods().Slice() + w.uint64(uint64(len(methods))) + for _, m := range methods { w.pos(m.Pos) w.selector(m.Sym) w.param(m.Type.Recv()) w.signature(m.Type) } - w.typeExt(t) - for _, m := range ms.Slice() { - w.methExt(m) + if w.p.extensions { + w.typeExt(t) + for _, m := range methods { + w.methExt(m) + } } default: @@ -600,7 +717,18 @@ func (w *exportWriter) pkg(pkg *types.Pkg) { // Ensure any referenced packages are declared in the main index. w.p.allPkgs[pkg] = true - w.string(pkg.Path) + w.string(exportPath(pkg)) +} + +// exportPath returns the path for pkg as it appears in the iexport +// file format. For historical reasons (before cmd/compile required +// the -p flag), the local package is represented as the empty string, +// instead of its actual path. +func exportPath(pkg *types.Pkg) string { + if pkg == types.LocalPkg { + return "" + } + return pkg.Path } func (w *exportWriter) qualifiedIdent(n *ir.Name) { @@ -612,6 +740,36 @@ func (w *exportWriter) qualifiedIdent(n *ir.Name) { w.pkg(s.Pkg) } +const blankMarker = "$" + +// TparamExportName creates a unique name for type param in a method or a generic +// type, using the specified unique prefix and the index of the type param. The index +// is only used if the type param is blank, in which case the blank is replace by +// "$". A unique name is needed for later substitution in the compiler and +// export/import that keeps blank type params associated with the correct constraint. +func TparamExportName(prefix string, name string, index int) string { + if name == "_" { + name = blankMarker + strconv.Itoa(index) + } + return prefix + "." + name +} + +// TparamName returns the real name of a type parameter, after stripping its +// qualifying prefix and reverting blank-name encoding. See TparamExportName +// for details. +func TparamName(exportName string) string { + // Remove the "path" from the type param name that makes it unique. + ix := strings.LastIndex(exportName, ".") + if ix < 0 { + return "" + } + name := exportName[ix+1:] + if strings.HasPrefix(name, blankMarker) { + return "_" + } + return name +} + func (w *exportWriter) selector(s *types.Sym) { if w.currPkg == nil { base.Fatalf("missing currPkg") @@ -803,8 +961,44 @@ func (w *exportWriter) startType(k itag) { } func (w *exportWriter) doTyp(t *types.Type) { - if t.Sym() != nil { - if t.Sym().Pkg == types.BuiltinPkg || t.Sym().Pkg == ir.Pkgs.Unsafe { + s := t.Sym() + if s != nil && t.OrigType() != nil { + // This is an instantiated type - could be a re-instantiation like + // Value[T2] or a full instantiation like Value[int]. + if strings.Index(s.Name, "[") < 0 { + base.Fatalf("incorrect name for instantiated type") + } + w.startType(instanceType) + w.pos(t.Pos()) + // Export the type arguments for the instantiated type. The + // instantiated type could be in a method header (e.g. "func (v + // *Value[T2]) set (...) { ... }"), so the type args are "new" + // typeparams. Or the instantiated type could be in a + // function/method body, so the type args are either concrete + // types or existing typeparams from the function/method header. + w.typeList(t.RParams()) + // Export a reference to the base type. + baseType := t.OrigType() + w.typ(baseType) + return + } + + // The 't.Underlying() == t' check is to confirm this is a base typeparam + // type, rather than a defined type with typeparam underlying type, like: + // type orderedAbs[T any] T + if t.IsTypeParam() && t.Underlying() == t { + if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg { + base.Fatalf("builtin type missing from typIndex: %v", t) + } + // Write out the first use of a type param as a qualified ident. + // This will force a "declaration" of the type param. + w.startType(typeParamType) + w.qualifiedIdent(t.Obj().(*ir.Name)) + return + } + + if s != nil { + if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg { base.Fatalf("builtin type missing from typIndex: %v", t) } @@ -881,6 +1075,18 @@ func (w *exportWriter) doTyp(t *types.Type) { w.signature(f.Type) } + case types.TUNION: + // TODO(danscales): possibly put out the tilde bools in more + // compact form. + w.startType(unionType) + nt := t.NumTerms() + w.uint64(uint64(nt)) + for i := 0; i < nt; i++ { + typ, tilde := t.Term(i) + w.bool(tilde) + w.typ(typ) + } + default: base.Fatalf("unexpected type: %v", t) } @@ -906,6 +1112,23 @@ func (w *exportWriter) signature(t *types.Type) { } } +func (w *exportWriter) typeList(ts []*types.Type) { + w.uint64(uint64(len(ts))) + for _, rparam := range ts { + w.typ(rparam) + } +} + +func (w *exportWriter) tparamList(fs []*types.Field) { + w.uint64(uint64(len(fs))) + for _, f := range fs { + if !f.Type.IsTypeParam() { + base.Fatalf("unexpected non-typeparam") + } + w.typ(f.Type) + } +} + func (w *exportWriter) paramList(fs []*types.Field) { w.uint64(uint64(len(fs))) for _, f := range fs { @@ -948,26 +1171,57 @@ func constTypeOf(typ *types.Type) constant.Kind { } func (w *exportWriter) value(typ *types.Type, v constant.Value) { - ir.AssertValidTypeForConst(typ, v) w.typ(typ) - // Each type has only one admissible constant representation, - // so we could type switch directly on v.U here. However, - // switching on the type increases symmetry with import logic - // and provides a useful consistency check. + if iexportVersionCurrent >= iexportVersionGo1_18 { + w.int64(int64(v.Kind())) + } + + var kind constant.Kind + var valType *types.Type + + if typ.IsTypeParam() { + kind = v.Kind() + if iexportVersionCurrent < iexportVersionGo1_18 { + // A constant will have a TYPEPARAM type if it appears in a place + // where it must match that typeparam type (e.g. in a binary + // operation with a variable of that typeparam type). If so, then + // we must write out its actual constant kind as well, so its + // constant val can be read in properly during import. + w.int64(int64(kind)) + } - switch constTypeOf(typ) { + switch kind { + case constant.Int: + valType = types.Types[types.TINT64] + case constant.Float: + valType = types.Types[types.TFLOAT64] + case constant.Complex: + valType = types.Types[types.TCOMPLEX128] + } + } else { + ir.AssertValidTypeForConst(typ, v) + kind = constTypeOf(typ) + valType = typ + } + + // Each type has only one admissible constant representation, so we could + // type switch directly on v.Kind() here. However, switching on the type + // (in the non-typeparam case) increases symmetry with import logic and + // provides a useful consistency check. + + switch kind { case constant.Bool: w.bool(constant.BoolVal(v)) case constant.String: w.string(constant.StringVal(v)) case constant.Int: - w.mpint(v, typ) + w.mpint(v, valType) case constant.Float: - w.mpfloat(v, typ) + w.mpfloat(v, valType) case constant.Complex: - w.mpfloat(constant.Real(v), typ) - w.mpfloat(constant.Imag(v), typ) + w.mpfloat(constant.Real(v), valType) + w.mpfloat(constant.Imag(v), valType) } } @@ -1185,10 +1439,20 @@ func (w *exportWriter) funcExt(n *ir.Name) { } } - // Inline body. + // Write out inline body or body of a generic function/method. + if n.Type().HasTParam() && n.Func.Body != nil && n.Func.Inl == nil { + base.FatalfAt(n.Pos(), "generic function is not marked inlineable") + } if n.Func.Inl != nil { w.uint64(1 + uint64(n.Func.Inl.Cost)) - if n.Func.ExportInline() { + w.bool(n.Func.Inl.CanDelayResults) + if n.Func.ExportInline() || n.Type().HasTParam() { + if n.Type().HasTParam() { + // If this generic function/method is from another + // package, but we didn't use for instantiation in + // this package, we may not yet have imported it. + ImportedBody(n.Func) + } w.p.doInline(n) } @@ -1270,10 +1534,23 @@ func (w *exportWriter) node(n ir.Node) { } } -// Caution: stmt will emit more than one node for statement nodes n that have a non-empty -// n.Ninit and where n cannot have a natural init section (such as in "if", "for", etc.). +func isNonEmptyAssign(n ir.Node) bool { + switch n.Op() { + case ir.OAS: + if n.(*ir.AssignStmt).Y != nil { + return true + } + case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: + return true + } + return false +} + +// Caution: stmt will emit more than one node for statement nodes n that have a +// non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init +// section (such as in "if", "for", etc.). func (w *exportWriter) stmt(n ir.Node) { - if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) { + if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE { // can't use stmtList here since we don't want the final OEND for _, n := range n.Init() { w.stmt(n) @@ -1309,8 +1586,10 @@ func (w *exportWriter) stmt(n ir.Node) { if n.Y != nil { w.op(ir.OAS) w.pos(n.Pos()) + w.stmtList(n.Init()) w.expr(n.X) w.expr(n.Y) + w.bool(n.Def) } case ir.OASOP: @@ -1325,14 +1604,12 @@ func (w *exportWriter) stmt(n ir.Node) { case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: n := n.(*ir.AssignListStmt) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OAS2) - } + w.op(n.Op()) w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprList(n.Lhs) w.exprList(n.Rhs) + w.bool(n.Def) case ir.ORETURN: n := n.(*ir.ReturnStmt) @@ -1370,6 +1647,7 @@ func (w *exportWriter) stmt(n ir.Node) { n := n.(*ir.RangeStmt) w.op(ir.ORANGE) w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprsOrNil(n.Key, n.Value) w.expr(n.X) w.stmtList(n.Body) @@ -1432,7 +1710,12 @@ func (w *exportWriter) commList(cases []*ir.CommClause) { w.uint64(uint64(len(cases))) for _, cas := range cases { w.pos(cas.Pos()) - w.node(cas.Comm) + defaultCase := cas.Comm == nil + w.bool(defaultCase) + if !defaultCase { + // Only call w.node for non-default cause (cas.Comm is non-nil) + w.node(cas.Comm) + } w.stmtList(cas.Body) } } @@ -1460,7 +1743,9 @@ func (w *exportWriter) expr(n ir.Node) { // (somewhat closely following the structure of exprfmt in fmt.go) case ir.ONIL: n := n.(*ir.NilExpr) - if !n.Type().HasNil() { + // If n is a typeparam, it will have already been checked + // for proper use by the types2 typechecker. + if !n.Type().IsTypeParam() && !n.Type().HasNil() { base.Fatalf("unexpected type for nil: %v", n.Type()) } w.op(ir.ONIL) @@ -1469,7 +1754,11 @@ func (w *exportWriter) expr(n ir.Node) { case ir.OLITERAL: w.op(ir.OLITERAL) - w.pos(n.Pos()) + if ir.HasUniquePos(n) { + w.pos(n.Pos()) + } else { + w.pos(src.NoXPos) + } w.value(n.Type(), n.Val()) case ir.ONAME: @@ -1477,10 +1766,10 @@ func (w *exportWriter) expr(n ir.Node) { n := n.(*ir.Name) if (n.Class == ir.PEXTERN || n.Class == ir.PFUNC) && !ir.IsBlank(n) { w.op(ir.ONONAME) + // Indicate that this is not an OKEY entry. + w.bool(false) w.qualifiedIdent(n) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) break } @@ -1488,15 +1777,46 @@ func (w *exportWriter) expr(n ir.Node) { // We don't need a type here, as the type will be provided at the // declaration of n. w.op(ir.ONAME) + + // This handles the case where we haven't yet transformed a call + // to a builtin, so we must write out the builtin as a name in the + // builtin package. + isBuiltin := n.BuiltinOp != ir.OXXX + w.bool(isBuiltin) + if isBuiltin { + w.bool(n.Sym().Pkg == types.UnsafePkg) + w.string(n.Sym().Name) + break + } w.localName(n) - // case OPACK, ONONAME: + case ir.ONONAME: + w.op(ir.ONONAME) + // This can only be for OKEY nodes in generic functions. Mark it + // as a key entry. + w.bool(true) + s := n.Sym() + w.string(s.Name) + w.pkg(s.Pkg) + w.typ(n.Type()) + + // case OPACK: // should have been resolved by typechecking - handled by default case case ir.OTYPE: w.op(ir.OTYPE) w.typ(n.Type()) + case ir.ODYNAMICTYPE: + n := n.(*ir.DynamicType) + w.op(ir.ODYNAMICTYPE) + w.pos(n.Pos()) + w.expr(n.RType) + if w.bool(n.ITab != nil) { + w.expr(n.ITab) + } + w.typ(n.Type()) + case ir.OTYPESW: n := n.(*ir.TypeSwitchGuard) w.op(ir.OTYPESW) @@ -1518,7 +1838,10 @@ func (w *exportWriter) expr(n ir.Node) { n := n.(*ir.ClosureExpr) w.op(ir.OCLOSURE) w.pos(n.Pos()) + old := w.currPkg + w.setPkg(n.Type().Pkg(), true) w.signature(n.Type()) + w.setPkg(old, true) // Write out id for the Outer of each conditional variable. The // conditional variable itself for this closure will be re-created @@ -1544,16 +1867,10 @@ func (w *exportWriter) expr(n ir.Node) { case ir.OPTRLIT: n := n.(*ir.AddrExpr) - if go117ExportTypes { - w.op(ir.OPTRLIT) - } else { - w.op(ir.OADDR) - } + w.op(ir.OPTRLIT) w.pos(n.Pos()) w.expr(n.X) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OSTRUCTLIT: n := n.(*ir.CompLitExpr) @@ -1562,17 +1879,13 @@ func (w *exportWriter) expr(n ir.Node) { w.typ(n.Type()) w.fieldList(n.List) // special handling of field names - case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT: + case ir.OCOMPLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT: n := n.(*ir.CompLitExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OCOMPLIT) - } + w.op(n.Op()) w.pos(n.Pos()) w.typ(n.Type()) w.exprList(n.List) - if go117ExportTypes && n.Op() == ir.OSLICELIT { + if n.Op() == ir.OSLICELIT { w.uint64(uint64(n.Len)) } case ir.OKEY: @@ -1585,126 +1898,104 @@ func (w *exportWriter) expr(n ir.Node) { // case OSTRUCTKEY: // unreachable - handled in case OSTRUCTLIT by elemList - case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR: + case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR: n := n.(*ir.SelectorExpr) - if go117ExportTypes { - if n.Op() == ir.OXDOT { - base.Fatalf("shouldn't encounter XDOT in new exporter") - } - w.op(n.Op()) - } else { - w.op(ir.OXDOT) - } + w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.exoticSelector(n.Sel) - if go117ExportTypes { - w.exoticType(n.Type()) - if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER { - w.exoticField(n.Selection) - } - // n.Selection is not required for OMETHEXPR, ODOTMETH, and OCALLPART. It will - // be reconstructed during import. - } + w.exoticType(n.Type()) + if n.Op() == ir.OXDOT { + // n.Selection for method references will be + // reconstructed during import. + w.bool(n.Selection != nil) + } else if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER { + w.exoticField(n.Selection) + } + // n.Selection is not required for OMETHEXPR, ODOTMETH, and OMETHVALUE. It will + // be reconstructed during import. n.Selection is computed during + // transformDot() for OXDOT. case ir.ODOTTYPE, ir.ODOTTYPE2: n := n.(*ir.TypeAssertExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.ODOTTYPE) - } + w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.typ(n.Type()) + case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: + n := n.(*ir.DynamicTypeAssertExpr) + w.op(n.Op()) + w.pos(n.Pos()) + w.expr(n.X) + if w.bool(n.RType != nil) { + w.expr(n.RType) + } + if w.bool(n.ITab != nil) { + w.expr(n.ITab) + } + w.typ(n.Type()) + case ir.OINDEX, ir.OINDEXMAP: n := n.(*ir.IndexExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OINDEX) - } + w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.expr(n.Index) - if go117ExportTypes { - w.typ(n.Type()) - if n.Op() == ir.OINDEXMAP { - w.bool(n.Assigned) - } + w.exoticType(n.Type()) + if n.Op() == ir.OINDEXMAP { + w.bool(n.Assigned) } case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR: n := n.(*ir.SliceExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OSLICE) - } + w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.exprsOrNil(n.Low, n.High) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OSLICE3, ir.OSLICE3ARR: n := n.(*ir.SliceExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OSLICE3) - } + w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.exprsOrNil(n.Low, n.High) w.expr(n.Max) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) - case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE: + case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: // treated like other builtin calls (see e.g., OREAL) n := n.(*ir.BinaryExpr) w.op(n.Op()) w.pos(n.Pos()) + w.stmtList(n.Init()) w.expr(n.X) w.expr(n.Y) - if go117ExportTypes { - w.typ(n.Type()) - } else { - w.op(ir.OEND) - } + w.typ(n.Type()) - case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR: + case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR: n := n.(*ir.ConvExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OCONV) - } + w.op(n.Op()) w.pos(n.Pos()) w.typ(n.Type()) w.expr(n.X) + w.bool(n.Implicit()) - case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: + case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: n := n.(*ir.UnaryExpr) w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) - if go117ExportTypes { - if n.Op() != ir.OPANIC { - w.typ(n.Type()) - } - } else { - w.op(ir.OEND) + if n.Op() != ir.OPANIC { + w.typ(n.Type()) } case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN: n := n.(*ir.CallExpr) w.op(n.Op()) w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprList(n.Args) // emits terminating OEND // only append() calls may contain '...' arguments if n.Op() == ir.OAPPEND { @@ -1712,28 +2003,19 @@ func (w *exportWriter) expr(n ir.Node) { } else if n.IsDDD { base.Fatalf("exporter: unexpected '...' with %v call", n.Op()) } - if go117ExportTypes { - if n.Op() != ir.ODELETE && n.Op() != ir.OPRINT && n.Op() != ir.OPRINTN { - w.typ(n.Type()) - } + if n.Op() != ir.ODELETE && n.Op() != ir.OPRINT && n.Op() != ir.OPRINTN { + w.typ(n.Type()) } case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG: n := n.(*ir.CallExpr) - if go117ExportTypes { - w.op(n.Op()) - } else { - w.op(ir.OCALL) - } + w.op(n.Op()) w.pos(n.Pos()) w.stmtList(n.Init()) w.expr(n.X) w.exprList(n.Args) w.bool(n.IsDDD) - if go117ExportTypes { - w.exoticType(n.Type()) - w.uint64(uint64(n.Use)) - } + w.exoticType(n.Type()) case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE: n := n.(*ir.MakeExpr) @@ -1754,38 +2036,40 @@ func (w *exportWriter) expr(n ir.Node) { // an argument. Don't serialize that argument here. w.expr(n.Len) w.op(ir.OEND) - case n.Len != nil && go117ExportTypes: + case n.Len != nil: w.expr(n.Len) w.op(ir.OEND) } + case ir.OLINKSYMOFFSET: + n := n.(*ir.LinksymOffsetExpr) + w.op(ir.OLINKSYMOFFSET) + w.pos(n.Pos()) + w.string(n.Linksym.Name) + w.uint64(uint64(n.Offset_)) + w.typ(n.Type()) + // unary expressions - case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV: + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA: n := n.(*ir.UnaryExpr) w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OADDR: n := n.(*ir.AddrExpr) w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.ODEREF: n := n.(*ir.StarExpr) w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OSEND: n := n.(*ir.SendStmt) @@ -1796,15 +2080,13 @@ func (w *exportWriter) expr(n ir.Node) { // binary expressions case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT, - ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR: + ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE: n := n.(*ir.BinaryExpr) w.op(n.Op()) w.pos(n.Pos()) w.expr(n.X) w.expr(n.Y) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OANDAND, ir.OOROR: n := n.(*ir.LogicalExpr) @@ -1812,23 +2094,39 @@ func (w *exportWriter) expr(n ir.Node) { w.pos(n.Pos()) w.expr(n.X) w.expr(n.Y) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.OADDSTR: n := n.(*ir.AddStringExpr) w.op(ir.OADDSTR) w.pos(n.Pos()) w.exprList(n.List) - if go117ExportTypes { - w.typ(n.Type()) - } + w.typ(n.Type()) case ir.ODCLCONST: // if exporting, DCLCONST should just be removed as its usage // has already been replaced with literals + case ir.OFUNCINST: + n := n.(*ir.InstExpr) + w.op(ir.OFUNCINST) + w.pos(n.Pos()) + w.expr(n.X) + w.uint64(uint64(len(n.Targs))) + for _, targ := range n.Targs { + w.typ(targ.Type()) + } + w.typ(n.Type()) + + case ir.OSELRECV2: + n := n.(*ir.AssignListStmt) + w.op(ir.OSELRECV2) + w.pos(n.Pos()) + w.stmtList(n.Init()) + w.exprList(n.Lhs) + w.exprList(n.Rhs) + w.bool(n.Def) + default: base.Fatalf("cannot export %v (%d) node\n"+ "\t==> please file an issue and assign to gri@", n.Op(), int(n.Op())) @@ -1864,11 +2162,8 @@ func (w *exportWriter) fieldList(list ir.Nodes) { for _, n := range list { n := n.(*ir.StructKeyExpr) w.pos(n.Pos()) - w.selector(n.Field) + w.exoticField(n.Field) w.expr(n.Value) - if go117ExportTypes { - w.uint64(uint64(n.Offset)) - } } } @@ -1902,8 +2197,15 @@ func (w *exportWriter) localIdent(s *types.Sym) { return } - // TODO(mdempsky): Fix autotmp hack. - if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") { + // The name of autotmp variables isn't important; they just need to + // be unique. To stabilize the export data, simply write out "$" as + // a marker and let the importer generate its own unique name. + if strings.HasPrefix(name, ".autotmp_") { + w.string("$autotmp") + return + } + + if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, LocalDictName) && !strings.HasPrefix(name, ".rcvr") { base.Fatalf("unexpected dot in identifier: %v", name) } @@ -1930,12 +2232,5 @@ func (w *intWriter) uint64(x uint64) { w.Write(buf[:n]) } -// If go117ExportTypes is true, then we write type information when -// exporting function bodies, so those function bodies don't need to -// be re-typechecked on import. -// This flag adds some other info to the serialized stream as well -// which was previously recomputed during typechecking, like -// specializing opcodes (e.g. OXDOT to ODOTPTR) and ancillary -// information (e.g. length field for OSLICELIT). -const go117ExportTypes = true -const Go117ExportTypes = go117ExportTypes +// The name used for dictionary parameters or local variables. +const LocalDictName = ".dict" diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 37f5a7bba0ac9a..80ae0259e861b9 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -13,14 +13,11 @@ import ( "go/constant" "io" "math/big" - "os" "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" - "cmd/internal/bio" - "cmd/internal/goobj" "cmd/internal/obj" "cmd/internal/src" ) @@ -84,6 +81,24 @@ func ImportBody(fn *ir.Func) { inimport = false } +// HaveInlineBody reports whether we have fn's inline body available +// for inlining. +// +// It's a function literal so that it can be overriden for +// GOEXPERIMENT=unified. +var HaveInlineBody = func(fn *ir.Func) bool { + if fn.Inl == nil { + return false + } + + if fn.Inl.Body != nil { + return true + } + + _, ok := inlineImporter[fn.Nname.Sym()] + return ok +} + func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader { x, ok := importers[sym] if !ok { @@ -94,7 +109,7 @@ func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset } type intReader struct { - *bio.Reader + *strings.Reader pkg *types.Pkg } @@ -116,33 +131,28 @@ func (r *intReader) uint64() uint64 { return i } -func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) { - ird := &intReader{in, pkg} +func ReadImports(pkg *types.Pkg, data string) { + ird := &intReader{strings.NewReader(data), pkg} version := ird.uint64() - if version != iexportVersion { + switch version { + case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: + default: base.Errorf("import %q: unknown export format version %d", pkg.Path, version) base.ErrorExit() } - sLen := ird.uint64() - dLen := ird.uint64() + sLen := int64(ird.uint64()) + dLen := int64(ird.uint64()) - // Map string (and data) section into memory as a single large - // string. This reduces heap fragmentation and allows - // returning individual substrings very efficiently. - data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen)) - if err != nil { - base.Errorf("import %q: mapping input: %v", pkg.Path, err) - base.ErrorExit() - } - stringData := data[:sLen] - declData := data[sLen:] - - in.MustSeek(int64(sLen+dLen), os.SEEK_CUR) + whence, _ := ird.Seek(0, io.SeekCurrent) + stringData := data[whence : whence+sLen] + declData := data[whence+sLen : whence+sLen+dLen] + ird.Seek(sLen+dLen, io.SeekCurrent) p := &iimporter{ - ipkg: pkg, + exportVersion: version, + ipkg: pkg, pkgCache: map[uint64]*types.Pkg{}, posBaseCache: map[uint64]*src.PosBase{}, @@ -160,10 +170,9 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- { pkg := p.pkgAt(ird.uint64()) pkgName := p.stringAt(ird.uint64()) - pkgHeight := int(ird.uint64()) + _ = int(ird.uint64()) // was package height, but not necessary anymore. if pkg.Name == "" { pkg.Name = pkgName - pkg.Height = pkgHeight types.NumImport[pkgName]++ // TODO(mdempsky): This belongs somewhere else. @@ -172,9 +181,6 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT if pkg.Name != pkgName { base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path) } - if pkg.Height != pkgHeight { - base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path) - } } for nSyms := ird.uint64(); nSyms > 0; nSyms-- { @@ -200,18 +206,11 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT } } } - - // Fingerprint. - _, err = io.ReadFull(in, fingerprint[:]) - if err != nil { - base.Errorf("import %s: error reading fingerprint", pkg.Path) - base.ErrorExit() - } - return fingerprint } type iimporter struct { - ipkg *types.Pkg + exportVersion uint64 + ipkg *types.Pkg pkgCache map[uint64]*types.Pkg posBaseCache map[uint64]*src.PosBase @@ -273,6 +272,7 @@ type importReader struct { // Slice of all dcls for function, including any interior closures allDcls []*ir.Name allClosureVars []*ir.Name + autotmpgen int } func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader { @@ -280,9 +280,7 @@ func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader { p: p, currPkg: pkg, } - // (*strings.Reader).Reset wasn't added until Go 1.7, and we - // need to build with Go 1.4. - r.Reader = *strings.NewReader(p.declData[off:]) + r.Reader.Reset(p.declData[off:]) return r } @@ -302,37 +300,52 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name { case 'A': typ := r.typ() - return importalias(r.p.ipkg, pos, sym, typ) + return importalias(pos, sym, typ) case 'C': typ := r.typ() val := r.value(typ) - n := importconst(r.p.ipkg, pos, sym, typ, val) + n := importconst(pos, sym, typ, val) r.constExt(n) return n - case 'F': - typ := r.signature(nil) + case 'F', 'G': + var tparams []*types.Field + if tag == 'G' { + tparams = r.tparamList() + } + typ := r.signature(nil, tparams) - n := importfunc(r.p.ipkg, pos, sym, typ) + n := importfunc(pos, sym, typ) r.funcExt(n) return n - case 'T': + case 'T', 'U': // Types can be recursive. We need to setup a stub // declaration before recursing. - n := importtype(r.p.ipkg, pos, sym) + n := importtype(pos, sym) t := n.Type() - // We also need to defer width calculations until - // after the underlying type has been assigned. + // Because of recursion, we need to defer width calculations and + // instantiations on intermediate types until the top-level type is + // fully constructed. Note that we can have recursion via type + // constraints. types.DeferCheckSize() + deferDoInst() + if tag == 'U' { + rparams := r.typeList() + t.SetRParams(rparams) + } + underlying := r.typ() t.SetUnderlying(underlying) - types.ResumeCheckSize() if underlying.IsInterface() { + // Finish up all type instantiations and CheckSize calls + // now that a top-level type is fully constructed. + resumeDoInst() + types.ResumeCheckSize() r.typeExt(t) return n } @@ -342,7 +355,7 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name { mpos := r.pos() msym := r.selector() recv := r.param() - mtyp := r.signature(recv) + mtyp := r.signature(recv, nil) // MethodSym already marked m.Sym as a function. m := ir.NewNameAt(mpos, ir.MethodSym(recv.Type, msym)) @@ -358,16 +371,47 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name { } t.Methods().Set(ms) + // Finish up all instantiations and CheckSize calls now + // that a top-level type is fully constructed. + resumeDoInst() + types.ResumeCheckSize() + r.typeExt(t) for _, m := range ms { r.methExt(m) } return n + case 'P': + if r.p.exportVersion < iexportVersionGenerics { + base.Fatalf("unexpected type param type") + } + if sym.Def != nil { + // Make sure we use the same type param type for the same + // name, whether it is created during types1-import or + // this types2-to-types1 translation. + return sym.Def.(*ir.Name) + } + n := importsym(pos, sym, ir.OTYPE, ir.PTYPEPARAM) + // The typeparam index is set at the point where the containing type + // param list is imported. + t := types.NewTypeParam(n, 0) + n.SetType(t) + implicit := false + if r.p.exportVersion >= iexportVersionGo1_18 { + implicit = r.bool() + } + bound := r.typ() + if implicit { + bound.MarkImplicit() + } + t.SetBound(bound) + return n + case 'V': typ := r.typ() - n := importvar(r.p.ipkg, pos, sym, typ) + n := importvar(pos, sym, typ) r.varExt(n) return n @@ -377,27 +421,54 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name { } } -func (p *importReader) value(typ *types.Type) constant.Value { - switch constTypeOf(typ) { +func (r *importReader) value(typ *types.Type) constant.Value { + var kind constant.Kind + var valType *types.Type + + if r.p.exportVersion >= iexportVersionGo1_18 { + // TODO: add support for using the kind in the non-typeparam case. + kind = constant.Kind(r.int64()) + } + + if typ.IsTypeParam() { + if r.p.exportVersion < iexportVersionGo1_18 { + // If a constant had a typeparam type, then we wrote out its + // actual constant kind as well. + kind = constant.Kind(r.int64()) + } + switch kind { + case constant.Int: + valType = types.Types[types.TINT64] + case constant.Float: + valType = types.Types[types.TFLOAT64] + case constant.Complex: + valType = types.Types[types.TCOMPLEX128] + } + } else { + kind = constTypeOf(typ) + valType = typ + } + + switch kind { case constant.Bool: - return constant.MakeBool(p.bool()) + return constant.MakeBool(r.bool()) case constant.String: - return constant.MakeString(p.string()) + return constant.MakeString(r.string()) case constant.Int: var i big.Int - p.mpint(&i, typ) + r.mpint(&i, valType) return constant.Make(&i) case constant.Float: - return p.float(typ) + return r.float(valType) case constant.Complex: - return makeComplex(p.float(typ), p.float(typ)) + return makeComplex(r.float(valType), r.float(valType)) } base.Fatalf("unexpected value type: %v", typ) panic("unreachable") } -func (p *importReader) mpint(x *big.Int, typ *types.Type) { +func (r *importReader) mpint(x *big.Int, typ *types.Type) { signed, maxBytes := intSize(typ) maxSmall := 256 - maxBytes @@ -408,7 +479,7 @@ func (p *importReader) mpint(x *big.Int, typ *types.Type) { maxSmall = 256 } - n, _ := p.ReadByte() + n, _ := r.ReadByte() if uint(n) < maxSmall { v := int64(n) if signed { @@ -429,30 +500,30 @@ func (p *importReader) mpint(x *big.Int, typ *types.Type) { base.Fatalf("weird decoding: %v, %v => %v", n, signed, v) } b := make([]byte, v) - p.Read(b) + r.Read(b) x.SetBytes(b) if signed && n&1 != 0 { x.Neg(x) } } -func (p *importReader) float(typ *types.Type) constant.Value { +func (r *importReader) float(typ *types.Type) constant.Value { var mant big.Int - p.mpint(&mant, typ) + r.mpint(&mant, typ) var f big.Float f.SetInt(&mant) if f.Sign() != 0 { - f.SetMantExp(&f, int(p.int64())) + f.SetMantExp(&f, int(r.int64())) } return constant.Make(&f) } -func (p *importReader) mprat(orig constant.Value) constant.Value { - if !p.bool() { +func (r *importReader) mprat(orig constant.Value) constant.Value { + if !r.bool() { return orig } var rat big.Rat - rat.SetString(p.string()) + rat.SetString(r.string()) return constant.Make(&rat) } @@ -462,8 +533,15 @@ func (r *importReader) ident(selector bool) *types.Sym { return nil } pkg := r.currPkg - if selector && types.IsExported(name) { - pkg = types.LocalPkg + if selector { + if types.IsExported(name) { + pkg = types.LocalPkg + } + } else { + if name == "$autotmp" { + name = autotmpname(r.autotmpgen) + r.autotmpgen++ + } } return pkg.Lookup(name) } @@ -503,7 +581,14 @@ func (r *importReader) pos() src.XPos { } func (r *importReader) typ() *types.Type { - return r.p.typAt(r.uint64()) + // If this is a top-level type call, defer type instantiations until the + // type is fully constructed. + types.DeferCheckSize() + deferDoInst() + t := r.p.typAt(r.uint64()) + resumeDoInst() + types.ResumeCheckSize() + return t } func (r *importReader) exoticType() *types.Type { @@ -538,7 +623,7 @@ func (r *importReader) exoticType() *types.Type { case exoticTypeRecv: var rcvr *types.Field if r.bool() { // isFakeRecv - rcvr = fakeRecvField() + rcvr = types.FakeRecv() } else { rcvr = r.exoticParam() } @@ -637,11 +722,17 @@ func (p *iimporter) typAt(off uint64) *types.Type { } t = p.newReader(off-predeclReserved, nil).typ1() // Ensure size is calculated for imported types. Since CL 283313, the compiler - // does not compile the function immediately when it sees them. Instead, funtions + // does not compile the function immediately when it sees them. Instead, functions // are pushed to compile queue, then draining from the queue for compiling. // During this process, the size calculation is disabled, so it is not safe for // calculating size during SSA generation anymore. See issue #44732. - types.CheckSize(t) + // + // No need to calc sizes for re-instantiated generic types, and + // they are not necessarily resolved until the top-level type is + // defined (because of recursive types). + if t.OrigType() == nil || !t.HasTParam() { + types.CheckSize(t) + } p.typCache[off] = t } return t @@ -680,7 +771,7 @@ func (r *importReader) typ1() *types.Type { case signatureType: r.setPkg() - return r.signature(nil) + return r.signature(nil, nil) case structType: r.setPkg() @@ -718,16 +809,64 @@ func (r *importReader) typ1() *types.Type { for i := range methods { pos := r.pos() sym := r.selector() - typ := r.signature(fakeRecvField()) + typ := r.signature(types.FakeRecv(), nil) methods[i] = types.NewField(pos, sym, typ) } - t := types.NewInterface(r.currPkg, append(embeddeds, methods...)) + if len(embeddeds)+len(methods) == 0 { + return types.Types[types.TINTER] + } + + t := types.NewInterface(r.currPkg, append(embeddeds, methods...), false) // Ensure we expand the interface in the frontend (#25055). types.CheckSize(t) return t + + case typeParamType: + if r.p.exportVersion < iexportVersionGenerics { + base.Fatalf("unexpected type param type") + } + // Similar to code for defined types, since we "declared" + // typeparams to deal with recursion (typeparam is used within its + // own type bound). + ident := r.qualifiedIdent() + if ident.Sym().Def != nil { + return ident.Sym().Def.(*ir.Name).Type() + } + n := expandDecl(ident) + if n.Op() != ir.OTYPE { + base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n) + } + return n.Type() + + case instanceType: + if r.p.exportVersion < iexportVersionGenerics { + base.Fatalf("unexpected instantiation type") + } + pos := r.pos() + len := r.uint64() + targs := make([]*types.Type, len) + for i := range targs { + targs[i] = r.typ() + } + baseType := r.typ() + t := Instantiate(pos, baseType, targs) + return t + + case unionType: + if r.p.exportVersion < iexportVersionGenerics { + base.Fatalf("unexpected instantiation type") + } + nt := int(r.uint64()) + terms := make([]*types.Type, nt) + tildes := make([]bool, nt) + for i := range terms { + tildes[i] = r.bool() + terms[i] = r.typ() + } + return types.NewUnion(terms, tildes) } } @@ -735,13 +874,42 @@ func (r *importReader) kind() itag { return itag(r.uint64()) } -func (r *importReader) signature(recv *types.Field) *types.Type { +func (r *importReader) signature(recv *types.Field, tparams []*types.Field) *types.Type { params := r.paramList() results := r.paramList() if n := len(params); n > 0 { params[n-1].SetIsDDD(r.bool()) } - return types.NewSignature(r.currPkg, recv, nil, params, results) + return types.NewSignature(r.currPkg, recv, tparams, params, results) +} + +func (r *importReader) typeList() []*types.Type { + n := r.uint64() + if n == 0 { + return nil + } + ts := make([]*types.Type, n) + for i := range ts { + ts[i] = r.typ() + if ts[i].IsTypeParam() { + ts[i].SetIndex(i) + } + } + return ts +} + +func (r *importReader) tparamList() []*types.Field { + n := r.uint64() + if n == 0 { + return nil + } + fs := make([]*types.Field, n) + for i := range fs { + typ := r.typ() + typ.SetIndex(i) + fs[i] = types.NewField(typ.Pos(), typ.Sym(), typ) + } + return fs } func (r *importReader) paramList() []*types.Field { @@ -809,7 +977,9 @@ func (r *importReader) funcExt(n *ir.Name) { n.Func.ABI = obj.ABI(r.uint64()) - n.SetPragma(ir.PragmaFlag(r.uint64())) + // Make sure //go:noinline pragma is imported (so stenciled functions have + // same noinline status as the corresponding generic function.) + n.Func.Pragma = ir.PragmaFlag(r.uint64()) // Escape analysis. for _, fs := range &types.RecvsParams { @@ -821,7 +991,8 @@ func (r *importReader) funcExt(n *ir.Name) { // Inline body. if u := r.uint64(); u > 0 { n.Func.Inl = &ir.Inline{ - Cost: int32(u - 1), + Cost: int32(u - 1), + CanDelayResults: r.bool(), } n.Func.Endlineno = r.pos() } @@ -852,7 +1023,13 @@ func (r *importReader) symIdx(s *types.Sym) { func (r *importReader) typeExt(t *types.Type) { t.SetNotInHeap(r.bool()) - i, pi := r.int64(), r.int64() + SetBaseTypeIndex(t, r.int64(), r.int64()) +} + +func SetBaseTypeIndex(t *types.Type, i, pi int64) { + if t.Obj() == nil { + base.Fatalf("SetBaseTypeIndex on non-defined type %v", t) + } if i != -1 && pi != -1 { typeSymIdx[t] = [2]int64{i, pi} } @@ -860,6 +1037,7 @@ func (r *importReader) typeExt(t *types.Type) { // Map imported type T to the index of type descriptor symbols of T and *T, // so we can use index to reference the symbol. +// TODO(mdempsky): Store this information directly in the Type's Name. var typeSymIdx = make(map[*types.Type][2]int64) func BaseTypeIndex(t *types.Type) int64 { @@ -928,14 +1106,16 @@ func (r *importReader) funcBody(fn *ir.Func) { // functions). body = []ir.Node{} } - if go117ExportTypes { - ir.VisitList(body, func(n ir.Node) { - n.SetTypecheck(1) - }) - } + ir.VisitList(body, func(n ir.Node) { + n.SetTypecheck(1) + }) fn.Inl.Body = body r.curfn = outerfn + if base.Flag.W >= 3 { + fmt.Printf("Imported for %v", fn) + ir.DumpList("", fn.Inl.Body) + } } func (r *importReader) readNames(fn *ir.Func) []*ir.Name { @@ -1004,10 +1184,26 @@ func (r *importReader) stmtList() []ir.Node { if n.Op() == ir.OBLOCK { n := n.(*ir.BlockStmt) list = append(list, n.List...) - } else { - list = append(list, n) + continue } - + if len(list) > 0 { + // check for an optional label that can only immediately + // precede a for/range/select/switch statement. + if last := list[len(list)-1]; last.Op() == ir.OLABEL { + label := last.(*ir.LabelStmt).Label + switch n.Op() { + case ir.OFOR: + n.(*ir.ForStmt).Label = label + case ir.ORANGE: + n.(*ir.RangeStmt).Label = label + case ir.OSELECT: + n.(*ir.SelectStmt).Label = label + case ir.OSWITCH: + n.(*ir.SwitchStmt).Label = label + } + } + } + list = append(list, n) } return list } @@ -1032,7 +1228,13 @@ func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause { func (r *importReader) commList() []*ir.CommClause { cases := make([]*ir.CommClause, r.uint64()) for i := range cases { - cases[i] = ir.NewCommStmt(r.pos(), r.node(), r.stmtList()) + pos := r.pos() + defaultCase := r.bool() + var comm ir.Node + if !defaultCase { + comm = r.node() + } + cases[i] = ir.NewCommStmt(pos, comm, r.stmtList()) } return cases } @@ -1083,18 +1285,28 @@ func (r *importReader) node() ir.Node { return n case ir.ONONAME: - n := r.qualifiedIdent() - if go117ExportTypes { - n2 := Resolve(n) - typ := r.typ() - if n2.Type() == nil { - n2.SetType(typ) - } - return n2 + isKey := r.bool() + var n ir.Node = r.qualifiedIdent() + // Key ONONAME entries should not be resolved - they should + // stay as identifiers. + if !isKey { + n = Resolve(n) + } + typ := r.typ() + if n.Type() == nil { + n.SetType(typ) } return n case ir.ONAME: + isBuiltin := r.bool() + if isBuiltin { + pkg := types.BuiltinPkg + if r.bool() { + pkg = types.UnsafePkg + } + return pkg.Lookup(r.string()).Def.(*ir.Name) + } return r.localName() // case OPACK, ONONAME: @@ -1103,6 +1315,14 @@ func (r *importReader) node() ir.Node { case ir.OTYPE: return ir.TypeNode(r.typ()) + case ir.ODYNAMICTYPE: + n := ir.NewDynamicType(r.pos(), r.expr()) + if r.bool() { + n.ITab = r.expr() + } + n.SetType(r.typ()) + return n + case ir.OTYPESW: pos := r.pos() var tag *ir.Ident @@ -1117,28 +1337,20 @@ func (r *importReader) node() ir.Node { case ir.OCLOSURE: //println("Importing CLOSURE") pos := r.pos() - typ := r.signature(nil) + r.setPkg() + typ := r.signature(nil, nil) + r.setPkg() // All the remaining code below is similar to (*noder).funcLit(), but // with Dcls and ClosureVars lists already set up - fn := ir.NewFunc(pos) - fn.SetIsHiddenClosure(true) - fn.Nname = ir.NewNameAt(pos, ir.BlankNode.Sym()) - fn.Nname.Func = fn - fn.Nname.Ntype = ir.TypeNode(typ) - fn.Nname.Defn = fn + fn := ir.NewClosureFunc(pos, true) fn.Nname.SetType(typ) cvars := make([]*ir.Name, r.int64()) for i := range cvars { cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical()) - if go117ExportTypes { - if cvars[i].Type() != nil || cvars[i].Defn == nil { - base.Fatalf("bad import of closure variable") - } - // Closure variable should have Defn set, which is its captured - // variable, and it gets the same type as the captured variable. - cvars[i].SetType(cvars[i].Defn.Type()) + if cvars[i].Defn == nil { + base.Fatalf("bad import of closure variable") } } fn.ClosureVars = cvars @@ -1159,38 +1371,26 @@ func (r *importReader) node() ir.Node { ir.FinishCaptureNames(pos, r.curfn, fn) - clo := ir.NewClosureExpr(pos, fn) - fn.OClosure = clo - if go117ExportTypes { - clo.SetType(typ) - } - + clo := fn.OClosure + clo.SetType(typ) return clo case ir.OSTRUCTLIT: - if go117ExportTypes { - pos := r.pos() - typ := r.typ() - list := r.fieldList() - n := ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, nil, list) - n.SetType(typ) - return n - } - return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.fieldList()) + pos := r.pos() + typ := r.typ() + list := r.fieldList() + return ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, typ, list) case ir.OCOMPLIT: - return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.exprList()) + pos := r.pos() + t := r.typ() + return ir.NewCompLitExpr(pos, ir.OCOMPLIT, t, r.exprList()) case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT: - if !go117ExportTypes { - // unreachable - mapped to OCOMPLIT by exporter - goto error - } pos := r.pos() typ := r.typ() list := r.exprList() - n := ir.NewCompLitExpr(pos, op, ir.TypeNode(typ), list) - n.SetType(typ) + n := ir.NewCompLitExpr(pos, op, typ, list) if op == ir.OSLICELIT { n.Len = int64(r.uint64()) } @@ -1202,32 +1402,45 @@ func (r *importReader) node() ir.Node { // case OSTRUCTKEY: // unreachable - handled in case OSTRUCTLIT by elemList - case ir.OXDOT: - // see parser.new_dotname - if go117ExportTypes { - base.Fatalf("shouldn't encounter XDOT in new importer") - } - return ir.NewSelectorExpr(r.pos(), ir.OXDOT, r.expr(), r.exoticSelector()) - - case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR: - if !go117ExportTypes { - // unreachable - mapped to case OXDOT by exporter - goto error - } + case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR: pos := r.pos() expr := r.expr() sel := r.exoticSelector() n := ir.NewSelectorExpr(pos, op, expr, sel) n.SetType(r.exoticType()) switch op { + case ir.OXDOT: + hasSelection := r.bool() + // We reconstruct n.Selection for method calls on + // generic types and method calls due to type param + // bounds. Otherwise, n.Selection is nil. + if hasSelection { + n1 := ir.NewSelectorExpr(pos, op, expr, sel) + AddImplicitDots(n1) + var m *types.Field + if n1.X.Type().IsTypeParam() { + genType := n1.X.Type().Bound() + m = Lookdot1(n1, sel, genType, genType.AllMethods(), 1) + } else { + genType := types.ReceiverBaseType(n1.X.Type()) + if genType.IsInstantiatedGeneric() { + genType = genType.OrigType() + } + m = Lookdot1(n1, sel, genType, genType.Methods(), 1) + } + assert(m != nil) + n.Selection = m + } case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER: n.Selection = r.exoticField() - case ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR: + case ir.OMETHEXPR: + n = typecheckMethodExpr(n).(*ir.SelectorExpr) + case ir.ODOTMETH, ir.OMETHVALUE: // These require a Lookup to link to the correct declaration. rcvrType := expr.Type() typ := n.Type() n.Selection = Lookdot(n, rcvrType, 1) - if op == ir.OCALLPART || op == ir.OMETHEXPR { + if op == ir.OMETHVALUE { // Lookdot clobbers the opcode and type, undo that. n.SetOp(op) n.SetType(typ) @@ -1236,21 +1449,27 @@ func (r *importReader) node() ir.Node { return n case ir.ODOTTYPE, ir.ODOTTYPE2: - n := ir.NewTypeAssertExpr(r.pos(), r.expr(), nil) - n.SetType(r.typ()) - if go117ExportTypes { - n.SetOp(op) + n := ir.NewTypeAssertExpr(r.pos(), r.expr(), r.typ()) + n.SetOp(op) + return n + + case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: + n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), nil) + if r.bool() { + n.RType = r.expr() + } + if r.bool() { + n.ITab = r.expr() } + n.SetType(r.typ()) return n case ir.OINDEX, ir.OINDEXMAP: n := ir.NewIndexExpr(r.pos(), r.expr(), r.expr()) - if go117ExportTypes { - n.SetOp(op) - n.SetType(r.typ()) - if op == ir.OINDEXMAP { - n.Assigned = r.bool() - } + n.SetOp(op) + n.SetType(r.exoticType()) + if op == ir.OINDEXMAP { + n.Assigned = r.bool() } return n @@ -1262,124 +1481,108 @@ func (r *importReader) node() ir.Node { max = r.expr() } n := ir.NewSliceExpr(pos, op, x, low, high, max) - if go117ExportTypes { - n.SetType(r.typ()) - } + n.SetType(r.typ()) return n - case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR: - if !go117ExportTypes && op != ir.OCONV { - // unreachable - mapped to OCONV case by exporter - goto error - } - return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr()) + case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR: + n := ir.NewConvExpr(r.pos(), op, r.typ(), r.expr()) + n.SetImplicit(r.bool()) + return n - case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, ir.OUNSAFEADD, ir.OUNSAFESLICE: - if go117ExportTypes { - switch op { - case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE: - n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr()) + case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, + ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, + ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA: + pos := r.pos() + switch op { + case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING: + init := r.stmtList() + n := ir.NewBinaryExpr(pos, op, r.expr(), r.expr()) + n.SetInit(init) + n.SetType(r.typ()) + return n + case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + n := ir.NewUnaryExpr(pos, op, r.expr()) + if op != ir.OPANIC { n.SetType(r.typ()) - return n - case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: - n := ir.NewUnaryExpr(r.pos(), op, r.expr()) - if op != ir.OPANIC { - n.SetType(r.typ()) - } - return n - case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN: - n := ir.NewCallExpr(r.pos(), op, nil, r.exprList()) - if op == ir.OAPPEND { - n.IsDDD = r.bool() - } - if op == ir.OAPPEND || op == ir.ORECOVER { - n.SetType(r.typ()) - } - return n } - // ir.OMAKE - goto error - } - n := builtinCall(r.pos(), op) - n.Args = r.exprList() - if op == ir.OAPPEND { - n.IsDDD = r.bool() + return n + case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN: + init := r.stmtList() + n := ir.NewCallExpr(pos, op, nil, r.exprList()) + n.SetInit(init) + if op == ir.OAPPEND { + n.IsDDD = r.bool() + } + if op == ir.OAPPEND || op == ir.ORECOVER { + n.SetType(r.typ()) + } + return n } - return n + // ir.OMAKE + goto error case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG: pos := r.pos() init := r.stmtList() n := ir.NewCallExpr(pos, ir.OCALL, r.expr(), r.exprList()) - if go117ExportTypes { - n.SetOp(op) - } - *n.PtrInit() = init + n.SetOp(op) + n.SetInit(init) n.IsDDD = r.bool() - if go117ExportTypes { - n.SetType(r.exoticType()) - n.Use = ir.CallUse(r.uint64()) - } + n.SetType(r.exoticType()) return n case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE: - if go117ExportTypes { - pos := r.pos() - typ := r.typ() - list := r.exprList() - var len_, cap_ ir.Node - if len(list) > 0 { - len_ = list[0] - } - if len(list) > 1 { - cap_ = list[1] - } - n := ir.NewMakeExpr(pos, op, len_, cap_) - n.SetType(typ) - return n + pos := r.pos() + typ := r.typ() + list := r.exprList() + var len_, cap_ ir.Node + if len(list) > 0 { + len_ = list[0] + } + if len(list) > 1 { + cap_ = list[1] } - n := builtinCall(r.pos(), ir.OMAKE) - n.Args.Append(ir.TypeNode(r.typ())) - n.Args.Append(r.exprList()...) + n := ir.NewMakeExpr(pos, op, len_, cap_) + n.SetType(typ) return n + case ir.OLINKSYMOFFSET: + pos := r.pos() + name := r.string() + off := r.uint64() + typ := r.typ() + return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ) + // unary expressions - case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV: + case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA: n := ir.NewUnaryExpr(r.pos(), op, r.expr()) - if go117ExportTypes { - n.SetType(r.typ()) - } + n.SetType(r.typ()) return n case ir.OADDR, ir.OPTRLIT: - n := NodAddrAt(r.pos(), r.expr()) - if go117ExportTypes { - n.SetOp(op) - n.SetType(r.typ()) - } + pos := r.pos() + expr := r.expr() + expr.SetTypecheck(1) // we do this for all nodes after importing, but do it now so markAddrOf can see it. + n := NodAddrAt(pos, expr) + n.SetOp(op) + n.SetType(r.typ()) return n case ir.ODEREF: n := ir.NewStarExpr(r.pos(), r.expr()) - if go117ExportTypes { - n.SetType(r.typ()) - } + n.SetType(r.typ()) return n // binary expressions case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT, - ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR: + ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE: n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr()) - if go117ExportTypes { - n.SetType(r.typ()) - } + n.SetType(r.typ()) return n case ir.OANDAND, ir.OOROR: n := ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr()) - if go117ExportTypes { - n.SetType(r.typ()) - } + n.SetType(r.typ()) return n case ir.OSEND: @@ -1388,16 +1591,9 @@ func (r *importReader) node() ir.Node { case ir.OADDSTR: pos := r.pos() list := r.exprList() - if go117ExportTypes { - n := ir.NewAddStringExpr(pos, list) - n.SetType(r.typ()) - return n - } - x := list[0] - for _, y := range list[1:] { - x = ir.NewBinaryExpr(pos, ir.OADD, x, y) - } - return x + n := ir.NewAddStringExpr(pos, list) + n.SetType(r.typ()) + return n // -------------------------------------------------------------------- // statements @@ -1412,7 +1608,12 @@ func (r *importReader) node() ir.Node { // unreachable - never exported case ir.OAS: - return ir.NewAssignStmt(r.pos(), r.expr(), r.expr()) + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignStmt(pos, r.expr(), r.expr()) + n.SetInit(init) + n.Def = r.bool() + return n case ir.OASOP: n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil) @@ -1425,11 +1626,12 @@ func (r *importReader) node() ir.Node { return n case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: - if !go117ExportTypes && op != ir.OAS2 { - // unreachable - mapped to case OAS2 by exporter - goto error - } - return ir.NewAssignListStmt(r.pos(), op, r.exprList(), r.exprList()) + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList()) + n.SetInit(init) + n.Def = r.bool() + return n case ir.ORETURN: return ir.NewReturnStmt(r.pos(), r.exprList()) @@ -1443,26 +1645,28 @@ func (r *importReader) node() ir.Node { case ir.OIF: pos, init := r.pos(), r.stmtList() n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.OFOR: pos, init := r.pos(), r.stmtList() cond, post := r.exprsOrNil() n := ir.NewForStmt(pos, nil, cond, post, r.stmtList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.ORANGE: - pos := r.pos() + pos, init := r.pos(), r.stmtList() k, v := r.exprsOrNil() - return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList()) + n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList()) + n.SetInit(init) + return n case ir.OSELECT: pos := r.pos() init := r.stmtList() n := ir.NewSelectStmt(pos, r.commList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.OSWITCH: @@ -1470,7 +1674,7 @@ func (r *importReader) node() ir.Node { init := r.stmtList() x, _ := r.exprsOrNil() n := ir.NewSwitchStmt(pos, x, r.caseList(x)) - *n.PtrInit() = init + n.SetInit(init) return n // case OCASE: @@ -1496,6 +1700,25 @@ func (r *importReader) node() ir.Node { case ir.OEND: return nil + case ir.OFUNCINST: + pos := r.pos() + x := r.expr() + targs := make([]ir.Ntype, r.uint64()) + for i := range targs { + targs[i] = ir.TypeNode(r.typ()) + } + n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs) + n.SetType(r.typ()) + return n + + case ir.OSELRECV2: + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList()) + n.SetInit(init) + n.Def = r.bool() + return n + default: base.Fatalf("cannot import %v (%d) node\n"+ "\t==> please file an issue and assign to gri@", op, int(op)) @@ -1517,11 +1740,7 @@ func (r *importReader) op() ir.Op { func (r *importReader) fieldList() []ir.Node { list := make([]ir.Node, r.uint64()) for i := range list { - x := ir.NewStructKeyExpr(r.pos(), r.selector(), r.expr()) - if go117ExportTypes { - x.Offset = int64(r.uint64()) - } - list[i] = x + list[i] = ir.NewStructKeyExpr(r.pos(), r.exoticField(), r.expr()) } return list } @@ -1537,10 +1756,146 @@ func (r *importReader) exprsOrNil() (a, b ir.Node) { return } -func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr { - if go117ExportTypes { - // These should all be encoded as direct ops, not OCALL. - base.Fatalf("builtinCall should not be invoked when types are included in import/export") +// NewIncompleteNamedType returns a TFORW type t with name specified by sym, such +// that t.nod and sym.Def are set correctly. If there are any RParams for the type, +// they should be set soon after creating the TFORW type, before creating the +// underlying type. That ensures that the HasTParam and HasShape flags will be set +// properly, in case this type is part of some mutually recursive type. +func NewIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type { + name := ir.NewDeclNameAt(pos, ir.OTYPE, sym) + forw := types.NewNamed(name) + name.SetType(forw) + sym.Def = name + return forw +} + +// Instantiate creates a new named type which is the instantiation of the base +// named generic type, with the specified type args. +func Instantiate(pos src.XPos, baseType *types.Type, targs []*types.Type) *types.Type { + baseSym := baseType.Sym() + if strings.Index(baseSym.Name, "[") >= 0 { + base.Fatalf("arg to Instantiate is not a base generic type") + } + name := InstTypeName(baseSym.Name, targs) + instSym := baseSym.Pkg.Lookup(name) + if instSym.Def != nil { + // May match existing type from previous import or + // types2-to-types1 conversion. + t := instSym.Def.Type() + if t.Kind() != types.TFORW { + return t + } + // Or, we have started creating this type in (*TSubster).Typ, but its + // underlying type was not completed yet, so we need to add this type + // to deferredInstStack, if not already there. + found := false + for _, t2 := range deferredInstStack { + if t2 == t { + found = true + break + } + } + if !found { + deferredInstStack = append(deferredInstStack, t) + } + return t + } + + t := NewIncompleteNamedType(baseType.Pos(), instSym) + t.SetRParams(targs) + t.SetOrigType(baseType) + + // baseType may still be TFORW or its methods may not be fully filled in + // (since we are in the middle of importing it). So, delay call to + // substInstType until we get back up to the top of the current top-most + // type import. + deferredInstStack = append(deferredInstStack, t) + + return t +} + +var deferredInstStack []*types.Type +var deferInst int + +// deferDoInst defers substitution on instantiated types until we are at the +// top-most defined type, so the base types are fully defined. +func deferDoInst() { + deferInst++ +} + +func resumeDoInst() { + if deferInst == 1 { + for len(deferredInstStack) > 0 { + t := deferredInstStack[0] + deferredInstStack = deferredInstStack[1:] + substInstType(t, t.OrigType(), t.RParams()) + } + } + deferInst-- +} + +// doInst creates a new instantiation type (which will be added to +// deferredInstStack for completion later) for an incomplete type encountered +// during a type substitution for an instantiation. This is needed for +// instantiations of mutually recursive types. +func doInst(t *types.Type) *types.Type { + assert(t.Kind() == types.TFORW) + return Instantiate(t.Pos(), t.OrigType(), t.RParams()) +} + +// substInstType completes the instantiation of a generic type by doing a +// substitution on the underlying type itself and any methods. t is the +// instantiation being created, baseType is the base generic type, and targs are +// the type arguments that baseType is being instantiated with. +func substInstType(t *types.Type, baseType *types.Type, targs []*types.Type) { + assert(t.Kind() == types.TFORW) + subst := Tsubster{ + Tparams: baseType.RParams(), + Targs: targs, + SubstForwFunc: doInst, + } + t.SetUnderlying(subst.Typ(baseType.Underlying())) + + newfields := make([]*types.Field, baseType.Methods().Len()) + for i, f := range baseType.Methods().Slice() { + if !f.IsMethod() || types.IsInterfaceMethod(f.Type) { + // Do a normal substitution if this is a non-method (which + // means this must be an interface used as a constraint) or + // an interface method. + t2 := subst.Typ(f.Type) + newfields[i] = types.NewField(f.Pos, f.Sym, t2) + continue + } + recvType := f.Type.Recv().Type + if recvType.IsPtr() { + recvType = recvType.Elem() + } + // Substitute in the method using the type params used in the + // method (not the type params in the definition of the generic type). + msubst := Tsubster{ + Tparams: recvType.RParams(), + Targs: targs, + SubstForwFunc: doInst, + } + t2 := msubst.Typ(f.Type) + oldsym := f.Nname.Sym() + newsym := MakeFuncInstSym(oldsym, targs, true, true) + var nname *ir.Name + if newsym.Def != nil { + nname = newsym.Def.(*ir.Name) + } else { + nname = ir.NewNameAt(f.Pos, newsym) + nname.SetType(t2) + ir.MarkFunc(nname) + newsym.Def = nname + } + newfields[i] = types.NewField(f.Pos, f.Sym, t2) + newfields[i].Nname = nname + } + t.Methods().Set(newfields) + if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TINTER && t.Methods().Len() > 0 { + // Generate all the methods for a new fully-instantiated, + // non-interface, non-shape type. + NeedInstType(t) } - return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil) } diff --git a/src/cmd/compile/internal/typecheck/mapfile_mmap.go b/src/cmd/compile/internal/typecheck/mapfile_mmap.go deleted file mode 100644 index 298b385bcb0f76..00000000000000 --- a/src/cmd/compile/internal/typecheck/mapfile_mmap.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd - -package typecheck - -import ( - "os" - "reflect" - "syscall" - "unsafe" -) - -// TODO(mdempsky): Is there a higher-level abstraction that still -// works well for iimport? - -// mapFile returns length bytes from the file starting at the -// specified offset as a string. -func mapFile(f *os.File, offset, length int64) (string, error) { - // POSIX mmap: "The implementation may require that off is a - // multiple of the page size." - x := offset & int64(os.Getpagesize()-1) - offset -= x - length += x - - buf, err := syscall.Mmap(int(f.Fd()), offset, int(length), syscall.PROT_READ, syscall.MAP_SHARED) - keepAlive(f) - if err != nil { - return "", err - } - - buf = buf[x:] - pSlice := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) - - var res string - pString := (*reflect.StringHeader)(unsafe.Pointer(&res)) - - pString.Data = pSlice.Data - pString.Len = pSlice.Len - - return res, nil -} - -// keepAlive is a reimplementation of runtime.KeepAlive, which wasn't -// added until Go 1.7, whereas we need to compile with Go 1.4. -var keepAlive = func(interface{}) {} diff --git a/src/cmd/compile/internal/typecheck/mkbuiltin.go b/src/cmd/compile/internal/typecheck/mkbuiltin.go index 6dbd1869b3e11b..9b27557956dafe 100644 --- a/src/cmd/compile/internal/typecheck/mkbuiltin.go +++ b/src/cmd/compile/internal/typecheck/mkbuiltin.go @@ -105,6 +105,7 @@ func mkbuiltin(w io.Writer, name string) { fmt.Fprintln(w, ` // Not inlining this function removes a significant chunk of init code. +// //go:noinline func newSig(params, results []*types.Field) *types.Type { return types.NewSignature(types.NoPkg, nil, nil, params, results) diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index 922a01bfbe9aa7..5eeab4115e3a61 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -78,7 +78,7 @@ func typecheckrangeExpr(n *ir.RangeStmt) { base.ErrorfAt(n.Pos(), "cannot assign type %v to %L in range%s", t, nn, why) } } - checkassign(n, nn) + checkassign(nn) } } do(n.Key, tk) @@ -127,17 +127,14 @@ func assign(stmt ir.Node, lhs, rhs []ir.Node) { checkLHS := func(i int, typ *types.Type) { lhs[i] = Resolve(lhs[i]) - if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Name().Ntype == nil { - if typ.Kind() != types.TNIL { - n.SetType(defaultType(typ)) - } else { - base.Errorf("use of untyped nil") - } + if n := lhs[i]; typ != nil && ir.DeclaredBy(n, stmt) && n.Type() == nil { + base.Assertf(typ.Kind() == types.TNIL, "unexpected untyped nil") + n.SetType(defaultType(typ)) } if lhs[i].Typecheck() == 0 { lhs[i] = AssignExpr(lhs[i]) } - checkassign(stmt, lhs[i]) + checkassign(lhs[i]) } assignType := func(i int, typ *types.Type) { @@ -172,6 +169,10 @@ assignOK: r := r.(*ir.TypeAssertExpr) stmt.SetOp(ir.OAS2DOTTYPE) r.SetOp(ir.ODOTTYPE2) + case ir.ODYNAMICDOTTYPE: + r := r.(*ir.DynamicTypeAssertExpr) + stmt.SetOp(ir.OAS2DOTTYPE) + r.SetOp(ir.ODYNAMICDOTTYPE2) default: break assignOK } @@ -201,7 +202,6 @@ assignOK: stmt := stmt.(*ir.AssignListStmt) stmt.SetOp(ir.OAS2FUNC) r := rhs[0].(*ir.CallExpr) - r.Use = ir.CallUseList rtyp := r.Type() mismatched := false @@ -217,7 +217,7 @@ assignOK: } } if mismatched && !failed { - rewriteMultiValueCall(stmt, r) + RewriteMultiValueCall(stmt, r) } return } @@ -237,6 +237,15 @@ func plural(n int) string { return "s" } +// tcCheckNil typechecks an OCHECKNIL node. +func tcCheckNil(n *ir.UnaryExpr) ir.Node { + n.X = Expr(n.X) + if !n.X.Type().IsPtrShaped() { + base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.X) + } + return n +} + // tcFor typechecks an OFOR node. func tcFor(n *ir.ForStmt) ir.Node { Stmts(n.Init()) @@ -249,9 +258,6 @@ func tcFor(n *ir.ForStmt) ir.Node { } } n.Post = Stmt(n.Post) - if n.Op() == ir.OFORUNTIL { - Stmts(n.Late) - } Stmts(n.Body) return n } @@ -297,16 +303,13 @@ func tcGoDefer(n *ir.GoDeferStmt) { // type is broken or missing, most likely a method call on a broken type // we will warn about the broken type elsewhere. no need to emit a potentially confusing error - if n.Call.Type() == nil || n.Call.Type().Broke() { + if n.Call.Type() == nil { return } - if !n.Diag() { - // The syntax made sure it was a call, so this must be - // a conversion. - n.SetDiag(true) - base.ErrorfAt(n.Pos(), "%s requires function call, not conversion", what) - } + // The syntax made sure it was a call, so this must be + // a conversion. + base.FatalfAt(n.Pos(), "%s requires function call, not conversion", what) } // tcIf typechecks an OIF node. @@ -383,10 +386,11 @@ func tcSelect(sel *ir.SelectStmt) { n := Stmt(ncase.Comm) ncase.Comm = n oselrecv2 := func(dst, recv ir.Node, def bool) { - n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) - n.Def = def - n.SetTypecheck(1) - ncase.Comm = n + selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) + selrecv.Def = def + selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) + ncase.Comm = selrecv } switch n.Op() { default: @@ -506,7 +510,6 @@ func tcSwitchExpr(n *ir.SwitchStmt) { } var defCase ir.Node - var cs constSet for _, ncase := range n.Cases { ls := ncase.List if len(ls) == 0 { // default: @@ -541,16 +544,6 @@ func tcSwitchExpr(n *ir.SwitchStmt) { } } } - - // Don't check for duplicate bools. Although the spec allows it, - // (1) the compiler hasn't checked it in the past, so compatibility mandates it, and - // (2) it would disallow useful things like - // case GOARCH == "arm" && GOARM == "5": - // case GOARCH == "arm": - // which would both evaluate to false for non-ARM compiles. - if !n1.Type().IsBoolean() { - cs.add(ncase.Pos(), n1, "case", "switch") - } } Stmts(ncase.Body) @@ -602,12 +595,15 @@ func tcSwitchType(n *ir.SwitchStmt) { } continue } + if n1.Op() == ir.ODYNAMICTYPE { + continue + } if n1.Op() != ir.OTYPE { base.ErrorfAt(ncase.Pos(), "%L is not a type", n1) continue } - if !n1.Type().IsInterface() && !implements(n1.Type(), t, &missing, &have, &ptr) && !missing.Broke() { - if have != nil && !have.Broke() { + if !n1.Type().IsInterface() && !implements(n1.Type(), t, &missing, &have, &ptr) { + if have != nil { base.ErrorfAt(ncase.Pos(), "impossible type switch case: %L cannot have dynamic type %v"+ " (wrong type for %v method)\n\thave %v%S\n\twant %v%S", guard.X, n1.Type(), missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) } else if ptr != 0 { @@ -627,7 +623,7 @@ func tcSwitchType(n *ir.SwitchStmt) { // Assign the clause variable's type. vt := t if len(ls) == 1 { - if ls[0].Op() == ir.OTYPE { + if ls[0].Op() == ir.OTYPE || ls[0].Op() == ir.ODYNAMICTYPE { vt = ls[0].Type() } else if !ir.IsNil(ls[0]) { // Invalid single-type case; @@ -643,7 +639,6 @@ func tcSwitchType(n *ir.SwitchStmt) { } else { // Clause variable is broken; prevent typechecking. nvar.SetTypecheck(1) - nvar.SetWalkdef(1) } ncase.Var = nvar } @@ -653,29 +648,18 @@ func tcSwitchType(n *ir.SwitchStmt) { } type typeSet struct { - m map[string][]typeSetEntry -} - -type typeSetEntry struct { - pos src.XPos - typ *types.Type + m map[string]src.XPos } func (s *typeSet) add(pos src.XPos, typ *types.Type) { if s.m == nil { - s.m = make(map[string][]typeSetEntry) + s.m = make(map[string]src.XPos) } - // LongString does not uniquely identify types, so we need to - // disambiguate collisions with types.Identical. - // TODO(mdempsky): Add a method that *is* unique. - ls := typ.LongString() - prevs := s.m[ls] - for _, prev := range prevs { - if types.Identical(typ, prev.typ) { - base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos)) - return - } + ls := typ.LinkString() + if prev, ok := s.m[ls]; ok { + base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev)) + return } - s.m[ls] = append(prevs, typeSetEntry{pos, typ}) + s.m[ls] = pos } diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 9ee7a94b1f24af..eab71556d3f776 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -5,14 +5,16 @@ package typecheck import ( + "bytes" "fmt" "sort" - "strconv" "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" + "cmd/internal/obj" + "cmd/internal/objabi" "cmd/internal/src" ) @@ -20,17 +22,9 @@ func AssignConv(n ir.Node, t *types.Type, context string) ir.Node { return assignconvfn(n, t, func() string { return context }) } -// DotImportRefs maps idents introduced by importDot back to the -// ir.PkgName they were dot-imported through. -var DotImportRefs map[*ir.Ident]*ir.PkgName - -// LookupNum looks up the symbol starting with prefix and ending with -// the decimal n. If prefix is too long, LookupNum panics. +// LookupNum returns types.LocalPkg.LookupNum(prefix, n). func LookupNum(prefix string, n int) *types.Sym { - var buf [20]byte // plenty long enough for all current users - copy(buf[:], prefix) - b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10) - return types.LocalPkg.LookupBytes(b) + return types.LocalPkg.LookupNum(prefix, n) } // Given funarg struct list, return list of fn args. @@ -47,7 +41,7 @@ func NewFuncParams(tl *types.Type, mustname bool) []*ir.Field { // TODO(mdempsky): Preserve original position, name, and package. s = Lookup(s.Name) } - a := ir.NewField(base.Pos, s, nil, t.Type) + a := ir.NewField(base.Pos, s, t.Type) a.Pos = t.Pos a.IsDDD = t.IsDDD() args = append(args, a) @@ -78,7 +72,7 @@ func markAddrOf(n ir.Node) ir.Node { if IncrementalAddrtaken { // We can only do incremental addrtaken computation when it is ok // to typecheck the argument of the OADDR. That's only safe after the - // main typecheck has completed. + // main typecheck has completed, and not loading the inlined body. // The argument to OADDR needs to be typechecked because &x[i] takes // the address of x if x is an array, but not if x is a slice. // Note: OuterValue doesn't work correctly until n is typechecked. @@ -126,6 +120,13 @@ func ComputeAddrtaken(top []ir.Node) { } } +// LinksymAddr returns a new expression that evaluates to the address +// of lsym. typ specifies the type of the addressed memory. +func LinksymAddr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *ir.AddrExpr { + n := ir.NewLinksymExpr(pos, lsym, typ) + return Expr(NodAddrAt(pos, n)).(*ir.AddrExpr) +} + func NodNil() ir.Node { n := ir.NewNilExpr(base.Pos) n.SetType(types.Types[types.TNIL]) @@ -137,9 +138,6 @@ func NodNil() ir.Node { // modifies the tree with missing field names. func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr { n.X = typecheck(n.X, ctxType|ctxExpr) - if n.X.Diag() { - n.SetDiag(true) - } t := n.X.Type() if t == nil { return n @@ -158,7 +156,7 @@ func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr { case path != nil: // rebuild elided dots for c := len(path) - 1; c >= 0; c-- { - dot := ir.NewSelectorExpr(base.Pos, ir.ODOT, n.X, path[c].field.Sym) + dot := ir.NewSelectorExpr(n.Pos(), ir.ODOT, n.X, path[c].field.Sym) dot.SetImplicit(true) dot.SetType(path[c].field.Type) n.X = dot @@ -171,6 +169,8 @@ func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr { return n } +// CalcMethods calculates all the methods (including embedding) of a non-interface +// type t. func CalcMethods(t *types.Type) { if t == nil || t.AllMethods().Len() != 0 { return @@ -291,7 +291,7 @@ var dotlist = make([]dlist, 10) // Convert node n for assignment to type t. func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node { - if n == nil || n.Type() == nil || n.Type().Broke() { + if n == nil || n.Type() == nil { return n } @@ -301,24 +301,14 @@ func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node { n = convlit1(n, t, false, context) if n.Type() == nil { - return n + base.Fatalf("cannot assign %v to %v", n, t) + } + if n.Type().IsUntyped() { + base.Fatalf("%L has untyped type", n) } if t.Kind() == types.TBLANK { return n } - - // Convert ideal bool from comparison to plain bool - // if the next step is non-bool (like interface{}). - if n.Type() == types.UntypedBool && !t.IsBoolean() { - if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL { - r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n) - r.SetType(types.Types[types.TBOOL]) - r.SetTypecheck(1) - r.SetImplicit(true) - n = r - } - } - if types.Identical(n.Type(), t) { return n } @@ -351,10 +341,14 @@ func Assignop(src, dst *types.Type) (ir.Op, string) { if types.Identical(src, dst) { return ir.OCONVNOP, "" } + return Assignop1(src, dst) +} - // 2. src and dst have identical underlying types - // and either src or dst is not a named type or - // both are empty interface types. +func Assignop1(src, dst *types.Type) (ir.Op, string) { + // 2. src and dst have identical underlying types and + // a. either src or dst is not a named type, or + // b. both are empty interface types, or + // c. at least one is a gcshape type. // For assignable but different non-empty interface types, // we want to recompute the itab. Recomputing the itab ensures // that itabs are unique (thus an interface with a compile-time @@ -371,26 +365,29 @@ func Assignop(src, dst *types.Type) (ir.Op, string) { // which need to have their itab updated. return ir.OCONVNOP, "" } + if src.IsShape() || dst.IsShape() { + // Conversion between a shape type and one of the types + // it represents also needs no conversion. + return ir.OCONVNOP, "" + } } // 3. dst is an interface type and src implements dst. if dst.IsInterface() && src.Kind() != types.TNIL { var missing, have *types.Field var ptr int - if implements(src, dst, &missing, &have, &ptr) { - // Call NeedITab/ITabAddr so that (src, dst) - // gets added to itabs early, which allows - // us to de-virtualize calls through this - // type/interface pair later. See CompileITabs in reflect.go - if types.IsDirectIface(src) && !dst.IsEmptyInterface() { - NeedITab(src, dst) - } - + if src.IsShape() { + // Shape types implement things they have already + // been typechecked to implement, even if they + // don't have the methods for them. return ir.OCONVIFACE, "" } - - // we'll have complained about this method anyway, suppress spurious messages. - if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) { + if base.Debug.Unified != 0 && src.HasShape() { + // Unified IR uses OCONVIFACE for converting all derived types + // to interface type, not just type arguments themselves. + return ir.OCONVIFACE, "" + } + if implements(src, dst, &missing, &have, &ptr) { return ir.OCONVIFACE, "" } @@ -474,15 +471,15 @@ func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) { return ir.OXXX, "" } - // Conversions from regular to go:notinheap are not allowed + // Conversions from regular to not-in-heap are not allowed // (unless it's unsafe.Pointer). These are runtime-specific // rules. - // (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't. + // (a) Disallow (*T) to (*U) where T is not-in-heap but U isn't. if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() { why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem()) return ir.OXXX, why } - // (b) Disallow string to []T where T is go:notinheap. + // (b) Disallow string to []T where T is not-in-heap. if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Kind() == types.ByteType.Kind() || dst.Elem().Kind() == types.RuneType.Kind()) { why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem()) return ir.OXXX, why @@ -586,9 +583,6 @@ func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) { // They must have same element type. if src.IsSlice() && dst.IsPtr() && dst.Elem().IsArray() && types.Identical(src.Elem(), dst.Elem().Elem()) { - if !types.AllowsGoVersion(curpkg(), 1, 17) { - return ir.OXXX, ":\n\tconversion of slices to array pointers only supported as of -lang=go1.17" - } return ir.OSLICE2ARRPTR, "" } @@ -722,13 +716,30 @@ func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, return m, followptr } +// implements reports whether t implements the interface iface. t can be +// an interface, a type parameter, or a concrete type. If implements returns +// false, it stores a method of iface that is not implemented in *m. If the +// method name matches but the type is wrong, it additionally stores the type +// of the method (on t) in *samename. func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool { t0 := t if t == nil { return false } - if t.IsInterface() { + if t.IsInterface() || t.IsTypeParam() { + if t.IsTypeParam() { + // If t is a simple type parameter T, its type and underlying is the same. + // If t is a type definition:'type P[T any] T', its type is P[T] and its + // underlying is T. Therefore we use 't.Underlying() != t' to distinguish them. + if t.Underlying() != t { + CalcMethods(t) + } else { + // A typeparam satisfies an interface if its type bound + // has all the methods of that interface. + t = t.Bound() + } + } i := 0 tms := t.AllMethods().Slice() for _, im := range iface.AllMethods().Slice() { @@ -761,9 +772,6 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool } i := 0 for _, im := range iface.AllMethods().Slice() { - if im.Broke() { - continue - } for i < len(tms) && tms[i].Sym != im.Sym { i++ } @@ -874,3 +882,689 @@ var slist []symlink type symlink struct { field *types.Field } + +// TypesOf converts a list of nodes to a list +// of types of those nodes. +func TypesOf(x []ir.Ntype) []*types.Type { + r := make([]*types.Type, len(x)) + for i, n := range x { + r[i] = n.Type() + } + return r +} + +// addTargs writes out the targs to buffer b as a comma-separated list enclosed by +// brackets. +func addTargs(b *bytes.Buffer, targs []*types.Type) { + b.WriteByte('[') + for i, targ := range targs { + if i > 0 { + b.WriteByte(',') + } + // Make sure that type arguments (including type params), are + // uniquely specified. LinkString() eliminates all spaces + // and includes the package path (local package path is "" before + // linker substitution). + tstring := targ.LinkString() + b.WriteString(tstring) + } + b.WriteString("]") +} + +// InstTypeName creates a name for an instantiated type, based on the name of the +// generic type and the type args. +func InstTypeName(name string, targs []*types.Type) string { + b := bytes.NewBufferString(name) + addTargs(b, targs) + return b.String() +} + +// makeInstName1 returns the name of the generic function instantiated with the +// given types, which can have type params or shapes, or be concrete types. name is +// the name of the generic function or method. +func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string { + b := bytes.NewBufferString("") + i := strings.Index(name, "[") + assert(hasBrackets == (i >= 0)) + if i >= 0 { + b.WriteString(name[0:i]) + } else { + b.WriteString(name) + } + addTargs(b, targs) + if i >= 0 { + i2 := strings.LastIndex(name[i:], "]") + assert(i2 >= 0) + b.WriteString(name[i+i2+1:]) + } + return b.String() +} + +// MakeFuncInstSym makes the unique sym for a stenciled generic function or method, +// based on the name of the function gf and the targs. It replaces any +// existing bracket type list in the name. MakeInstName asserts that gf has +// brackets in its name if and only if hasBrackets is true. +// +// Names of declared generic functions have no brackets originally, so hasBrackets +// should be false. Names of generic methods already have brackets, since the new +// type parameter is specified in the generic type of the receiver (e.g. func +// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set. +// +// The standard naming is something like: 'genFn[int,bool]' for functions and +// '(*genType[int,bool]).methodName' for methods +// +// isMethodNode specifies if the name of a method node is being generated (as opposed +// to a name of an instantiation of generic function or name of the shape-based +// function that helps implement a method of an instantiated type). For method nodes +// on shape types, we prepend "nofunc.", because method nodes for shape types will +// have no body, and we want to avoid a name conflict with the shape-based function +// that helps implement the same method for fully-instantiated types. Function names +// are also created at the end of (*Tsubster).typ1, so we append "nofunc" there as +// well, as needed. +func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, isMethodNode, hasBrackets bool) *types.Sym { + nm := makeInstName1(gf.Name, targs, hasBrackets) + if targs[0].HasShape() && isMethodNode { + nm = "nofunc." + nm + } + return gf.Pkg.Lookup(nm) +} + +func MakeDictSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym { + for _, targ := range targs { + if targ.HasTParam() { + fmt.Printf("FUNCTION %s\n", gf.Name) + for _, targ := range targs { + fmt.Printf(" PARAM %+v\n", targ) + } + panic("dictionary should always have concrete type args") + } + } + name := makeInstName1(gf.Name, targs, hasBrackets) + name = fmt.Sprintf("%s.%s", objabi.GlobalDictPrefix, name) + return gf.Pkg.Lookup(name) +} + +func assert(p bool) { + base.Assert(p) +} + +// List of newly fully-instantiated types who should have their methods generated. +var instTypeList []*types.Type + +// NeedInstType adds a new fully-instantiated type to instTypeList. +func NeedInstType(t *types.Type) { + instTypeList = append(instTypeList, t) +} + +// GetInstTypeList returns the current contents of instTypeList. +func GetInstTypeList() []*types.Type { + r := instTypeList + return r +} + +// ClearInstTypeList clears the contents of instTypeList. +func ClearInstTypeList() { + instTypeList = nil +} + +// General type substituter, for replacing typeparams with type args. +type Tsubster struct { + Tparams []*types.Type + Targs []*types.Type + // If non-nil, the substitution map from name nodes in the generic function to the + // name nodes in the new stenciled function. + Vars map[*ir.Name]*ir.Name + // If non-nil, function to substitute an incomplete (TFORW) type. + SubstForwFunc func(*types.Type) *types.Type + // Prevent endless recursion on functions. See #51832. + Funcs map[*types.Type]bool +} + +// Typ computes the type obtained by substituting any type parameter or shape in t +// that appears in subst.Tparams with the corresponding type argument in subst.Targs. +// If t contains no type parameters, the result is t; otherwise the result is a new +// type. It deals with recursive types by using TFORW types and finding partially or +// fully created types via sym.Def. +func (ts *Tsubster) Typ(t *types.Type) *types.Type { + // Defer the CheckSize calls until we have fully-defined + // (possibly-recursive) top-level type. + types.DeferCheckSize() + r := ts.typ1(t) + types.ResumeCheckSize() + return r +} + +func (ts *Tsubster) typ1(t *types.Type) *types.Type { + hasParamOrShape := t.HasTParam() || t.HasShape() + if !hasParamOrShape && t.Kind() != types.TFUNC { + // Note: function types need to be copied regardless, as the + // types of closures may contain declarations that need + // to be copied. See #45738. + return t + } + + if t.IsTypeParam() || t.IsShape() { + for i, tp := range ts.Tparams { + if tp == t { + return ts.Targs[i] + } + } + // If t is a simple typeparam T, then t has the name/symbol 'T' + // and t.Underlying() == t. + // + // However, consider the type definition: 'type P[T any] T'. We + // might use this definition so we can have a variant of type T + // that we can add new methods to. Suppose t is a reference to + // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM, + // because P[T] is defined as T. If we look at t.Underlying(), it + // is different, because the name of t.Underlying() is 'T' rather + // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM. + // In this case, we do the needed recursive substitution in the + // case statement below. + if t.Underlying() == t { + // t is a simple typeparam that didn't match anything in tparam + return t + } + // t is a more complex typeparam (e.g. P[T], as above, whose + // definition is just T). + assert(t.Sym() != nil) + } + + var newsym *types.Sym + var neededTargs []*types.Type + var targsChanged bool // == are there any substitutions from this + var forw *types.Type + + if t.Sym() != nil && hasParamOrShape { + // Need to test for t.HasTParam() again because of special TFUNC case above. + // Translate the type params for this type according to + // the tparam/targs mapping from subst. + neededTargs = make([]*types.Type, len(t.RParams())) + for i, rparam := range t.RParams() { + neededTargs[i] = ts.typ1(rparam) + if !types.IdenticalStrict(neededTargs[i], rparam) { + targsChanged = true + } + } + // For a named (defined) type, we have to change the name of the + // type as well. We do this first, so we can look up if we've + // already seen this type during this substitution or other + // definitions/substitutions. + genName := genericTypeName(t.Sym()) + newsym = t.Sym().Pkg.Lookup(InstTypeName(genName, neededTargs)) + if newsym.Def != nil { + // We've already created this instantiated defined type. + return newsym.Def.Type() + } + + // In order to deal with recursive generic types, create a TFORW + // type initially and set the Def field of its sym, so it can be + // found if this type appears recursively within the type. + forw = NewIncompleteNamedType(t.Pos(), newsym) + //println("Creating new type by sub", newsym.Name, forw.HasTParam()) + forw.SetRParams(neededTargs) + // Copy the OrigType from the re-instantiated type (which is the sym of + // the base generic type). + assert(t.OrigType() != nil) + forw.SetOrigType(t.OrigType()) + } + + var newt *types.Type + + switch t.Kind() { + case types.TTYPEPARAM: + if t.Sym() == newsym && !targsChanged { + // The substitution did not change the type. + return t + } + // Substitute the underlying typeparam (e.g. T in P[T], see + // the example describing type P[T] above). + newt = ts.typ1(t.Underlying()) + assert(newt != t) + + case types.TARRAY: + elem := t.Elem() + newelem := ts.typ1(elem) + if newelem != elem || targsChanged { + newt = types.NewArray(newelem, t.NumElem()) + } + + case types.TPTR: + elem := t.Elem() + newelem := ts.typ1(elem) + if newelem != elem || targsChanged { + newt = types.NewPtr(newelem) + } + + case types.TSLICE: + elem := t.Elem() + newelem := ts.typ1(elem) + if newelem != elem || targsChanged { + newt = types.NewSlice(newelem) + } + + case types.TSTRUCT: + newt = ts.tstruct(t, targsChanged) + if newt == t { + newt = nil + } + + case types.TFUNC: + // watch out for endless recursion on plain function types that mention themselves, e.g. "type T func() T" + if !hasParamOrShape { + if ts.Funcs[t] { // Visit such function types only once. + return t + } + if ts.Funcs == nil { + // allocate lazily + ts.Funcs = make(map[*types.Type]bool) + } + ts.Funcs[t] = true + } + newrecvs := ts.tstruct(t.Recvs(), false) + newparams := ts.tstruct(t.Params(), false) + newresults := ts.tstruct(t.Results(), false) + // Translate the tparams of a signature. + newtparams := ts.tstruct(t.TParams(), false) + if newrecvs != t.Recvs() || newparams != t.Params() || + newresults != t.Results() || newtparams != t.TParams() || targsChanged { + // If any types have changed, then the all the fields of + // of recv, params, and results must be copied, because they have + // offset fields that are dependent, and so must have an + // independent copy for each new signature. + var newrecv *types.Field + if newrecvs.NumFields() > 0 { + if newrecvs == t.Recvs() { + newrecvs = ts.tstruct(t.Recvs(), true) + } + newrecv = newrecvs.Field(0) + } + if newparams == t.Params() { + newparams = ts.tstruct(t.Params(), true) + } + if newresults == t.Results() { + newresults = ts.tstruct(t.Results(), true) + } + var tparamfields []*types.Field + if newtparams.HasTParam() { + tparamfields = newtparams.FieldSlice() + } else { + // Completely remove the tparams from the resulting + // signature, if the tparams are now concrete types. + tparamfields = nil + } + newt = types.NewSignature(t.Pkg(), newrecv, tparamfields, + newparams.FieldSlice(), newresults.FieldSlice()) + } + if !hasParamOrShape { + delete(ts.Funcs, t) + } + + case types.TINTER: + newt = ts.tinter(t, targsChanged) + if newt == t { + newt = nil + } + + case types.TMAP: + newkey := ts.typ1(t.Key()) + newval := ts.typ1(t.Elem()) + if newkey != t.Key() || newval != t.Elem() || targsChanged { + newt = types.NewMap(newkey, newval) + } + + case types.TCHAN: + elem := t.Elem() + newelem := ts.typ1(elem) + if newelem != elem || targsChanged { + newt = types.NewChan(newelem, t.ChanDir()) + } + case types.TFORW: + if ts.SubstForwFunc != nil { + return ts.SubstForwFunc(forw) + } else { + assert(false) + } + case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64, + types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64, + types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR: + newt = t.Underlying() + case types.TUNION: + nt := t.NumTerms() + newterms := make([]*types.Type, nt) + tildes := make([]bool, nt) + changed := false + for i := 0; i < nt; i++ { + term, tilde := t.Term(i) + tildes[i] = tilde + newterms[i] = ts.typ1(term) + if newterms[i] != term { + changed = true + } + } + if changed { + newt = types.NewUnion(newterms, tildes) + } + default: + panic(fmt.Sprintf("Bad type in (*TSubster).Typ: %v", t.Kind())) + } + if newt == nil { + // Even though there were typeparams in the type, there may be no + // change if this is a function type for a function call (which will + // have its own tparams/targs in the function instantiation). + return t + } + + if forw != nil { + forw.SetUnderlying(newt) + newt = forw + } + + if !newt.HasTParam() && !newt.IsFuncArgStruct() { + // Calculate the size of any new types created. These will be + // deferred until the top-level ts.Typ() or g.typ() (if this is + // called from g.fillinMethods()). + types.CheckSize(newt) + } + + if t.Kind() != types.TINTER && t.Methods().Len() > 0 { + // Fill in the method info for the new type. + var newfields []*types.Field + newfields = make([]*types.Field, t.Methods().Len()) + for i, f := range t.Methods().Slice() { + t2 := ts.typ1(f.Type) + oldsym := f.Nname.Sym() + + // Use the name of the substituted receiver to create the + // method name, since the receiver name may have many levels + // of nesting (brackets) with type names to be substituted. + recvType := t2.Recv().Type + var nm string + if recvType.IsPtr() { + recvType = recvType.Elem() + nm = "(*" + recvType.Sym().Name + ")." + f.Sym.Name + } else { + nm = recvType.Sym().Name + "." + f.Sym.Name + } + if recvType.RParams()[0].HasShape() { + // We add "nofunc" to methods of shape type to avoid + // conflict with the name of the shape-based helper + // function. See header comment of MakeFuncInstSym. + nm = "nofunc." + nm + } + newsym := oldsym.Pkg.Lookup(nm) + var nname *ir.Name + if newsym.Def != nil { + nname = newsym.Def.(*ir.Name) + } else { + nname = ir.NewNameAt(f.Pos, newsym) + nname.SetType(t2) + ir.MarkFunc(nname) + newsym.Def = nname + } + newfields[i] = types.NewField(f.Pos, f.Sym, t2) + newfields[i].Nname = nname + } + newt.Methods().Set(newfields) + if !newt.HasTParam() && !newt.HasShape() { + // Generate all the methods for a new fully-instantiated type. + + NeedInstType(newt) + } + } + return newt +} + +// tstruct substitutes type params in types of the fields of a structure type. For +// each field, tstruct copies the Nname, and translates it if Nname is in +// ts.vars. To always force the creation of a new (top-level) struct, +// regardless of whether anything changed with the types or names of the struct's +// fields, set force to true. +func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type { + if t.NumFields() == 0 { + if t.HasTParam() || t.HasShape() { + // For an empty struct, we need to return a new type, if + // substituting from a generic type or shape type, since it + // will change HasTParam/HasShape flags. + return types.NewStruct(t.Pkg(), nil) + } + return t + } + var newfields []*types.Field + if force { + newfields = make([]*types.Field, t.NumFields()) + } + for i, f := range t.Fields().Slice() { + t2 := ts.typ1(f.Type) + if (t2 != f.Type || f.Nname != nil) && newfields == nil { + newfields = make([]*types.Field, t.NumFields()) + for j := 0; j < i; j++ { + newfields[j] = t.Field(j) + } + } + if newfields != nil { + newfields[i] = types.NewField(f.Pos, f.Sym, t2) + newfields[i].Embedded = f.Embedded + newfields[i].Note = f.Note + if f.IsDDD() { + newfields[i].SetIsDDD(true) + } + if f.Nointerface() { + newfields[i].SetNointerface(true) + } + if f.Nname != nil && ts.Vars != nil { + v := ts.Vars[f.Nname.(*ir.Name)] + if v != nil { + // This is the case where we are + // translating the type of the function we + // are substituting, so its dcls are in + // the subst.ts.vars table, and we want to + // change to reference the new dcl. + newfields[i].Nname = v + } else { + // This is the case where we are + // translating the type of a function + // reference inside the function we are + // substituting, so we leave the Nname + // value as is. + newfields[i].Nname = f.Nname + } + } + } + } + if newfields != nil { + news := types.NewStruct(t.Pkg(), newfields) + news.StructType().Funarg = t.StructType().Funarg + return news + } + return t + +} + +// tinter substitutes type params in types of the methods of an interface type. +func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type { + if t.Methods().Len() == 0 { + if t.HasTParam() || t.HasShape() { + // For an empty interface, we need to return a new type, if + // substituting from a generic type or shape type, since + // since it will change HasTParam/HasShape flags. + return types.NewInterface(t.Pkg(), nil, false) + } + return t + } + var newfields []*types.Field + if force { + newfields = make([]*types.Field, t.Methods().Len()) + } + for i, f := range t.Methods().Slice() { + t2 := ts.typ1(f.Type) + if (t2 != f.Type || f.Nname != nil) && newfields == nil { + newfields = make([]*types.Field, t.Methods().Len()) + for j := 0; j < i; j++ { + newfields[j] = t.Methods().Index(j) + } + } + if newfields != nil { + newfields[i] = types.NewField(f.Pos, f.Sym, t2) + } + } + if newfields != nil { + return types.NewInterface(t.Pkg(), newfields, t.IsImplicit()) + } + return t +} + +// genericSym returns the name of the base generic type for the type named by +// sym. It simply returns the name obtained by removing everything after the +// first bracket ("["). +func genericTypeName(sym *types.Sym) string { + return sym.Name[0:strings.Index(sym.Name, "[")] +} + +// getShapes appends the list of the shape types that are used within type t to +// listp. The type traversal is simplified for two reasons: (1) we can always stop a +// type traversal when t.HasShape() is false; and (2) shape types can't appear inside +// a named type, except for the type args of a generic type. So, the traversal will +// always stop before we have to deal with recursive types. +func getShapes(t *types.Type, listp *[]*types.Type) { + if !t.HasShape() { + return + } + if t.IsShape() { + *listp = append(*listp, t) + return + } + + if t.Sym() != nil { + // A named type can't have shapes in it, except for type args of a + // generic type. We will have to deal with this differently once we + // alloc local types in generic functions (#47631). + for _, rparam := range t.RParams() { + getShapes(rparam, listp) + } + return + } + + switch t.Kind() { + case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN: + getShapes(t.Elem(), listp) + + case types.TSTRUCT: + for _, f := range t.FieldSlice() { + getShapes(f.Type, listp) + } + + case types.TFUNC: + for _, f := range t.Recvs().FieldSlice() { + getShapes(f.Type, listp) + } + for _, f := range t.Params().FieldSlice() { + getShapes(f.Type, listp) + } + for _, f := range t.Results().FieldSlice() { + getShapes(f.Type, listp) + } + for _, f := range t.TParams().FieldSlice() { + getShapes(f.Type, listp) + } + + case types.TINTER: + for _, f := range t.Methods().Slice() { + getShapes(f.Type, listp) + } + + case types.TMAP: + getShapes(t.Key(), listp) + getShapes(t.Elem(), listp) + + default: + panic(fmt.Sprintf("Bad type in getShapes: %v", t.Kind())) + } + +} + +// Shapify takes a concrete type and a type param index, and returns a GCshape type that can +// be used in place of the input type and still generate identical code. +// No methods are added - all methods calls directly on a shape should +// be done by converting to an interface using the dictionary. +// +// For now, we only consider two types to have the same shape, if they have exactly +// the same underlying type or they are both pointer types. +// +// tparam is the associated typeparam - it must be TTYPEPARAM type. If there is a +// structural type for the associated type param (not common), then a pointer type t +// is mapped to its underlying type, rather than being merged with other pointers. +// +// Shape types are also distinguished by the index of the type in a type param/arg +// list. We need to do this so we can distinguish and substitute properly for two +// type params in the same function that have the same shape for a particular +// instantiation. +func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type { + assert(!t.IsShape()) + if t.HasShape() { + // We are sometimes dealing with types from a shape instantiation + // that were constructed from existing shape types, so t may + // sometimes have shape types inside it. In that case, we find all + // those shape types with getShapes() and replace them with their + // underlying type. + // + // If we don't do this, we may create extra unneeded shape types that + // have these other shape types embedded in them. This may lead to + // generating extra shape instantiations, and a mismatch between the + // instantiations that we used in generating dictionaries and the + // instantations that are actually called. (#51303). + list := []*types.Type{} + getShapes(t, &list) + list2 := make([]*types.Type, len(list)) + for i, shape := range list { + list2[i] = shape.Underlying() + } + ts := Tsubster{ + Tparams: list, + Targs: list2, + } + t = ts.Typ(t) + } + // Map all types with the same underlying type to the same shape. + u := t.Underlying() + + // All pointers have the same shape. + // TODO: Make unsafe.Pointer the same shape as normal pointers. + // Note: pointers to arrays are special because of slice-to-array-pointer + // conversions. See issue 49295. + if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY && + tparam.Bound().StructuralType() == nil && !u.Elem().NotInHeap() { + u = types.Types[types.TUINT8].PtrTo() + } + + submap := shapeMap[index] + if submap == nil { + submap = map[*types.Type]*types.Type{} + shapeMap[index] = submap + } + if s := submap[u]; s != nil { + return s + } + + // LinkString specifies the type uniquely, but has no spaces. + nm := fmt.Sprintf("%s_%d", u.LinkString(), index) + sym := types.ShapePkg.Lookup(nm) + if sym.Def != nil { + // Use any existing type with the same name + submap[u] = sym.Def.Type() + return submap[u] + } + name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym) + s := types.NewNamed(name) + sym.Def = name + s.SetUnderlying(u) + s.SetIsShape(true) + s.SetHasShape(true) + types.CalcSize(s) + name.SetType(s) + name.SetTypecheck(1) + submap[u] = s + return s +} + +var shapeMap = map[int]map[*types.Type]*types.Type{} diff --git a/src/cmd/compile/internal/typecheck/syms.go b/src/cmd/compile/internal/typecheck/syms.go index f29af82db2cc25..1f60f318510a9c 100644 --- a/src/cmd/compile/internal/typecheck/syms.go +++ b/src/cmd/compile/internal/typecheck/syms.go @@ -24,7 +24,8 @@ func LookupRuntime(name string) *ir.Name { // successive occurrences of the "any" placeholder in the // type syntax expression n.Type. // The result of SubstArgTypes MUST be assigned back to old, e.g. -// n.Left = SubstArgTypes(n.Left, t1, t2) +// +// n.Left = SubstArgTypes(n.Left, t1, t2) func SubstArgTypes(old *ir.Name, types_ ...*types.Type) *ir.Name { for _, t := range types_ { types.CalcSize(t) @@ -67,7 +68,6 @@ func Lookup(name string) *types.Sym { // but does not make them visible to user code. func InitRuntime() { base.Timer.Start("fe", "loadsys") - types.Block = 1 typs := runtimeTypes() for _, d := range &runtimeDecls { @@ -75,9 +75,9 @@ func InitRuntime() { typ := typs[d.typ] switch d.tag { case funcTag: - importfunc(ir.Pkgs.Runtime, src.NoXPos, sym, typ) + importfunc(src.NoXPos, sym, typ) case varTag: - importvar(ir.Pkgs.Runtime, src.NoXPos, sym, typ) + importvar(src.NoXPos, sym, typ) default: base.Fatalf("unhandled declaration tag %v", d.tag) } diff --git a/src/cmd/compile/internal/typecheck/type.go b/src/cmd/compile/internal/typecheck/type.go index af694c2d94a30f..37c394393a1a73 100644 --- a/src/cmd/compile/internal/typecheck/type.go +++ b/src/cmd/compile/internal/typecheck/type.go @@ -3,186 +3,3 @@ // license that can be found in the LICENSE file. package typecheck - -import ( - "go/constant" - - "cmd/compile/internal/base" - "cmd/compile/internal/ir" - "cmd/compile/internal/types" -) - -// tcArrayType typechecks an OTARRAY node. -func tcArrayType(n *ir.ArrayType) ir.Node { - n.Elem = typecheckNtype(n.Elem) - if n.Elem.Type() == nil { - return n - } - if n.Len == nil { // [...]T - if !n.Diag() { - n.SetDiag(true) - base.Errorf("use of [...] array outside of array literal") - } - return n - } - n.Len = indexlit(Expr(n.Len)) - size := n.Len - if ir.ConstType(size) != constant.Int { - switch { - case size.Type() == nil: - // Error already reported elsewhere. - case size.Type().IsInteger() && size.Op() != ir.OLITERAL: - base.Errorf("non-constant array bound %v", size) - default: - base.Errorf("invalid array bound %v", size) - } - return n - } - - v := size.Val() - if ir.ConstOverflow(v, types.Types[types.TINT]) { - base.Errorf("array bound is too large") - return n - } - - if constant.Sign(v) < 0 { - base.Errorf("array bound must be non-negative") - return n - } - - bound, _ := constant.Int64Val(v) - t := types.NewArray(n.Elem.Type(), bound) - n.SetOTYPE(t) - types.CheckSize(t) - return n -} - -// tcChanType typechecks an OTCHAN node. -func tcChanType(n *ir.ChanType) ir.Node { - n.Elem = typecheckNtype(n.Elem) - l := n.Elem - if l.Type() == nil { - return n - } - if l.Type().NotInHeap() { - base.Errorf("chan of incomplete (or unallocatable) type not allowed") - } - n.SetOTYPE(types.NewChan(l.Type(), n.Dir)) - return n -} - -// tcFuncType typechecks an OTFUNC node. -func tcFuncType(n *ir.FuncType) ir.Node { - misc := func(f *types.Field, nf *ir.Field) { - f.SetIsDDD(nf.IsDDD) - if nf.Decl != nil { - nf.Decl.SetType(f.Type) - f.Nname = nf.Decl - } - } - - lno := base.Pos - - var recv *types.Field - if n.Recv != nil { - recv = tcField(n.Recv, misc) - } - - t := types.NewSignature(types.LocalPkg, recv, nil, tcFields(n.Params, misc), tcFields(n.Results, misc)) - checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice()) - - base.Pos = lno - - n.SetOTYPE(t) - return n -} - -// tcInterfaceType typechecks an OTINTER node. -func tcInterfaceType(n *ir.InterfaceType) ir.Node { - if len(n.Methods) == 0 { - n.SetOTYPE(types.Types[types.TINTER]) - return n - } - - lno := base.Pos - methods := tcFields(n.Methods, nil) - base.Pos = lno - - n.SetOTYPE(types.NewInterface(types.LocalPkg, methods)) - return n -} - -// tcMapType typechecks an OTMAP node. -func tcMapType(n *ir.MapType) ir.Node { - n.Key = typecheckNtype(n.Key) - n.Elem = typecheckNtype(n.Elem) - l := n.Key - r := n.Elem - if l.Type() == nil || r.Type() == nil { - return n - } - if l.Type().NotInHeap() { - base.Errorf("incomplete (or unallocatable) map key not allowed") - } - if r.Type().NotInHeap() { - base.Errorf("incomplete (or unallocatable) map value not allowed") - } - n.SetOTYPE(types.NewMap(l.Type(), r.Type())) - mapqueue = append(mapqueue, n) // check map keys when all types are settled - return n -} - -// tcSliceType typechecks an OTSLICE node. -func tcSliceType(n *ir.SliceType) ir.Node { - n.Elem = typecheckNtype(n.Elem) - if n.Elem.Type() == nil { - return n - } - t := types.NewSlice(n.Elem.Type()) - n.SetOTYPE(t) - types.CheckSize(t) - return n -} - -// tcStructType typechecks an OTSTRUCT node. -func tcStructType(n *ir.StructType) ir.Node { - lno := base.Pos - - fields := tcFields(n.Fields, func(f *types.Field, nf *ir.Field) { - if nf.Embedded { - checkembeddedtype(f.Type) - f.Embedded = 1 - } - f.Note = nf.Note - }) - checkdupfields("field", fields) - - base.Pos = lno - n.SetOTYPE(types.NewStruct(types.LocalPkg, fields)) - return n -} - -// tcField typechecks a generic Field. -// misc can be provided to handle specialized typechecking. -func tcField(n *ir.Field, misc func(*types.Field, *ir.Field)) *types.Field { - base.Pos = n.Pos - if n.Ntype != nil { - n.Type = typecheckNtype(n.Ntype).Type() - n.Ntype = nil - } - f := types.NewField(n.Pos, n.Sym, n.Type) - if misc != nil { - misc(f, n) - } - return f -} - -// tcFields typechecks a slice of generic Fields. -// misc can be provided to handle specialized typechecking. -func tcFields(l []*ir.Field, misc func(*types.Field, *ir.Field)) []*types.Field { - fields := make([]*types.Field, len(l)) - for i, n := range l { - fields[i] = tcField(n, misc) - } - return fields -} diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index 359f66236969fb..f4174d1a600b2b 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -13,6 +13,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" + "cmd/internal/src" ) // Function collecting autotmps generated during typechecking, @@ -24,7 +25,6 @@ var inimport bool // set during import var TypecheckAllowed bool var ( - NeedITab = func(t, itype *types.Type) {} NeedRuntimeType = func(*types.Type) {} ) @@ -35,35 +35,16 @@ func Stmt(n ir.Node) ir.Node { return typecheck(n, ctxStmt) } func Exprs(exprs []ir.Node) { typecheckslice(exprs, ctxExpr) } func Stmts(stmts []ir.Node) { typecheckslice(stmts, ctxStmt) } -func Call(call *ir.CallExpr) { - t := call.X.Type() - if t == nil { - panic("misuse of Call") - } - ctx := ctxStmt - if t.NumResults() > 0 { - ctx = ctxExpr | ctxMultiOK - } - if typecheck(call, ctx) != call { - panic("bad typecheck") - } +func Call(pos src.XPos, callee ir.Node, args []ir.Node, dots bool) ir.Node { + call := ir.NewCallExpr(pos, ir.OCALL, callee, args) + call.IsDDD = dots + return typecheck(call, ctxStmt|ctxExpr) } func Callee(n ir.Node) ir.Node { return typecheck(n, ctxExpr|ctxCallee) } -func FuncBody(n *ir.Func) { - ir.CurFunc = n - errorsBefore := base.Errors() - Stmts(n.Body) - CheckUnused(n) - CheckReturn(n) - if base.Errors() > errorsBefore { - n.Body = nil // type errors; do not compile - } -} - var importlist []*ir.Func // AllImportedBodies reads in the bodies of all imported functions and typechecks @@ -135,9 +116,11 @@ const ( // marks variables that escape the local frame. // rewrites n.Op to be more specific in some cases. -var typecheckdefstack []*ir.Name - -// Resolve ONONAME to definition, if any. +// Resolve resolves an ONONAME node to a definition, if any. If n is not an ONONAME node, +// Resolve returns n unchanged. If n is an ONONAME node and not in the same package, +// then n.Sym() is resolved using import data. Otherwise, Resolve returns +// n.Sym().Def. An ONONAME node can be created using ir.NewIdent(), so an imported +// symbol can be resolved via Resolve(ir.NewIdent(src.NoXPos, sym)). func Resolve(n ir.Node) (res ir.Node) { if n == nil || n.Op() != ir.ONONAME { return n @@ -149,13 +132,6 @@ func Resolve(n ir.Node) (res ir.Node) { } if sym := n.Sym(); sym.Pkg != types.LocalPkg { - // We might have an ir.Ident from oldname or importDot. - if id, ok := n.(*ir.Ident); ok { - if pkgName := DotImportRefs[id]; pkgName != nil { - pkgName.Used = true - } - } - return expandDecl(n) } @@ -164,13 +140,6 @@ func Resolve(n ir.Node) (res ir.Node) { return n } - if r.Op() == ir.OIOTA { - if x := getIotaValue(); x >= 0 { - return ir.NewInt(x) - } - return n - } - return r } @@ -265,13 +234,10 @@ func Func(fn *ir.Func) { } } -func typecheckNtype(n ir.Ntype) ir.Ntype { - return typecheck(n, ctxType).(ir.Ntype) -} - // typecheck type checks node n. // The result of typecheck MUST be assigned back to n, e.g. -// n.Left = typecheck(n.Left, top) +// +// n.Left = typecheck(n.Left, top) func typecheck(n ir.Node, top int) (res ir.Node) { // cannot type check until all the source has been parsed if !TypecheckAllowed { @@ -301,7 +267,7 @@ func typecheck(n ir.Node, top int) (res ir.Node) { // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. if n.Typecheck() == 1 || n.Typecheck() == 3 { switch n.Op() { - case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.OPACK: + case ir.ONAME, ir.OTYPE, ir.OLITERAL: break default: @@ -393,7 +359,7 @@ func typecheck(n ir.Node, top int) (res ir.Node) { case ir.OAPPEND: // Must be used (and not BinaryExpr/UnaryExpr). isStmt = false - case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.OVARKILL, ir.OVARLIVE: + case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN: // Must not be used. isExpr = false isStmt = true @@ -424,37 +390,16 @@ func typecheck(n ir.Node, top int) (res ir.Node) { // this code a bit, especially the final case. switch { case top&(ctxStmt|ctxExpr) == ctxExpr && !isExpr && n.Op() != ir.OTYPE && !isMulti: - if !n.Diag() { - base.Errorf("%v used as value", n) - n.SetDiag(true) - } - if t != nil { - n.SetType(nil) - } + base.Fatalf("%v used as value", n) case top&ctxType == 0 && n.Op() == ir.OTYPE && t != nil: - if !n.Type().Broke() { - base.Errorf("type %v is not an expression", n.Type()) - n.SetDiag(true) - } + base.Fatalf("type %v is not an expression", n.Type()) case top&(ctxStmt|ctxExpr) == ctxStmt && !isStmt && t != nil: - if !n.Diag() { - base.Errorf("%v evaluated but not used", n) - n.SetDiag(true) - } - n.SetType(nil) + base.Fatalf("%v evaluated but not used", n) case top&(ctxType|ctxExpr) == ctxType && n.Op() != ir.OTYPE && n.Op() != ir.ONONAME && (t != nil || n.Op() == ir.ONAME): - base.Errorf("%v is not a type", n) - if t != nil { - if n.Op() == ir.ONAME { - t.SetBroke(true) - } else { - n.SetType(nil) - } - } - + base.Fatalf("%v is not a type", n) } base.Pos = lno @@ -466,7 +411,8 @@ func typecheck(n ir.Node, top int) (res ir.Node) { // but also accepts untyped numeric values representable as // value of type int (see also checkmake for comparison). // The result of indexlit MUST be assigned back to n, e.g. -// n.Left = indexlit(n.Left) +// +// n.Left = indexlit(n.Left) func indexlit(n ir.Node) ir.Node { if n != nil && n.Type() != nil && n.Type().Kind() == types.TIDEAL { return DefaultLit(n, types.Types[types.TINT]) @@ -476,10 +422,6 @@ func indexlit(n ir.Node) ir.Node { // typecheck1 should ONLY be called from typecheck. func typecheck1(n ir.Node, top int) ir.Node { - if n, ok := n.(*ir.Name); ok { - typecheckdef(n) - } - switch n.Op() { default: ir.Dump("typecheck", n) @@ -488,9 +430,7 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OLITERAL: if n.Sym() == nil && n.Type() == nil { - if !n.Diag() { - base.Fatalf("literal missing type: %v", n) - } + base.Fatalf("literal missing type: %v", n) } return n @@ -499,14 +439,10 @@ func typecheck1(n ir.Node, top int) ir.Node { // names case ir.ONONAME: - if !n.Diag() { - // Note: adderrorname looks for this string and - // adds context about the outer expression - base.ErrorfAt(n.Pos(), "undefined: %v", n.Sym()) - n.SetDiag(true) - } - n.SetType(nil) - return n + // Note: adderrorname looks for this string and + // adds context about the outer expression + base.FatalfAt(n.Pos(), "undefined: %v", n.Sym()) + panic("unreachable") case ir.ONAME: n := n.(*ir.Name) @@ -533,43 +469,10 @@ func typecheck1(n ir.Node, top int) ir.Node { // type already set return n - case ir.OPACK: - n := n.(*ir.PkgName) - base.Errorf("use of package %v without selector", n.Sym()) - n.SetDiag(true) - return n - // types (ODEREF is with exprs) case ir.OTYPE: return n - case ir.OTSLICE: - n := n.(*ir.SliceType) - return tcSliceType(n) - - case ir.OTARRAY: - n := n.(*ir.ArrayType) - return tcArrayType(n) - - case ir.OTMAP: - n := n.(*ir.MapType) - return tcMapType(n) - - case ir.OTCHAN: - n := n.(*ir.ChanType) - return tcChanType(n) - - case ir.OTSTRUCT: - n := n.(*ir.StructType) - return tcStructType(n) - - case ir.OTINTER: - n := n.(*ir.InterfaceType) - return tcInterfaceType(n) - - case ir.OTFUNC: - n := n.(*ir.FuncType) - return tcFuncType(n) // type or expr case ir.ODEREF: n := n.(*ir.StarExpr) @@ -579,7 +482,7 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OASOP: n := n.(*ir.AssignOpStmt) n.X, n.Y = Expr(n.X), Expr(n.Y) - checkassign(n, n.X) + checkassign(n.X) if n.IncDec && !okforarith[n.X.Type().Kind()] { base.Errorf("invalid operation: %v (non-numeric type %v)", n, n.X.Type()) return n @@ -707,6 +610,10 @@ func typecheck1(n ir.Node, top int) ir.Node { n := n.(*ir.SliceHeaderExpr) return tcSliceHeader(n) + case ir.OSTRINGHEADER: + n := n.(*ir.StringHeaderExpr) + return tcStringHeader(n) + case ir.OMAKESLICECOPY: n := n.(*ir.MakeExpr) return tcMakeSliceCopy(n) @@ -777,6 +684,10 @@ func typecheck1(n ir.Node, top int) ir.Node { n := n.(*ir.CallExpr) return tcRecover(n) + case ir.ORECOVERFP: + n := n.(*ir.CallExpr) + return tcRecoverFP(n) + case ir.OUNSAFEADD: n := n.(*ir.BinaryExpr) return tcUnsafeAdd(n) @@ -785,13 +696,21 @@ func typecheck1(n ir.Node, top int) ir.Node { n := n.(*ir.BinaryExpr) return tcUnsafeSlice(n) + case ir.OUNSAFESLICEDATA: + n := n.(*ir.UnaryExpr) + return tcUnsafeData(n) + + case ir.OUNSAFESTRING: + n := n.(*ir.BinaryExpr) + return tcUnsafeString(n) + + case ir.OUNSAFESTRINGDATA: + n := n.(*ir.UnaryExpr) + return tcUnsafeData(n) + case ir.OCLOSURE: n := n.(*ir.ClosureExpr) - tcClosure(n, top) - if n.Type() == nil { - return n - } - return n + return tcClosure(n, top) case ir.OITAB: n := n.(*ir.UnaryExpr) @@ -814,6 +733,14 @@ func typecheck1(n ir.Node, top int) ir.Node { n.SetType(types.Types[types.TUINTPTR]) return n + case ir.OGETCALLERPC, ir.OGETCALLERSP: + n := n.(*ir.CallExpr) + if len(n.Args) != 0 { + base.FatalfAt(n.Pos(), "unexpected arguments: %v", n) + } + n.SetType(types.Types[types.TUINTPTR]) + return n + case ir.OCONVNOP: n := n.(*ir.ConvExpr) n.X = Expr(n.X) @@ -838,9 +765,7 @@ func typecheck1(n ir.Node, top int) ir.Node { ir.OCONTINUE, ir.ODCL, ir.OGOTO, - ir.OFALL, - ir.OVARKILL, - ir.OVARLIVE: + ir.OFALL: return n case ir.OBLOCK: @@ -860,12 +785,10 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.ODEFER, ir.OGO: n := n.(*ir.GoDeferStmt) n.Call = typecheck(n.Call, ctxStmt|ctxExpr) - if !n.Call.Diag() { - tcGoDefer(n) - } + tcGoDefer(n) return n - case ir.OFOR, ir.OFORUNTIL: + case ir.OFOR: n := n.(*ir.ForStmt) return tcFor(n) @@ -879,8 +802,13 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) + n.Call = typecheck(n.Call, ctxStmt|ctxExpr).(*ir.CallExpr) return n + case ir.OCHECKNIL: + n := n.(*ir.UnaryExpr) + return tcCheckNil(n) + case ir.OSELECT: tcSelect(n.(*ir.SelectStmt)) return n @@ -895,8 +823,7 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OTYPESW: n := n.(*ir.TypeSwitchGuard) - base.Errorf("use of .(type) outside type switch") - n.SetDiag(true) + base.Fatalf("use of .(type) outside type switch") return n case ir.ODCLFUNC: @@ -951,17 +878,53 @@ func typecheckargs(n ir.InitNode) { } // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...). - rewriteMultiValueCall(n, list[0]) + RewriteMultiValueCall(n, list[0]) } -// rewriteMultiValueCall rewrites multi-valued f() to use temporaries, +// RewriteNonNameCall replaces non-Name call expressions with temps, +// rewriting f()(...) to t0 := f(); t0(...). +func RewriteNonNameCall(n *ir.CallExpr) { + np := &n.X + if inst, ok := (*np).(*ir.InstExpr); ok && inst.Op() == ir.OFUNCINST { + np = &inst.X + } + if dot, ok := (*np).(*ir.SelectorExpr); ok && (dot.Op() == ir.ODOTMETH || dot.Op() == ir.ODOTINTER || dot.Op() == ir.OMETHVALUE) { + np = &dot.X // peel away method selector + } + + // Check for side effects in the callee expression. + // We explicitly special case new(T) though, because it doesn't have + // observable side effects, and keeping it in place allows better escape analysis. + if !ir.Any(*np, func(n ir.Node) bool { return n.Op() != ir.ONEW && callOrChan(n) }) { + return + } + + // See comment (1) in RewriteMultiValueCall. + static := ir.CurFunc == nil + if static { + ir.CurFunc = InitTodoFunc + } + + tmp := Temp((*np).Type()) + as := ir.NewAssignStmt(base.Pos, tmp, *np) + as.Def = true + *np = tmp + + if static { + ir.CurFunc = nil + } + + n.PtrInit().Append(Stmt(as)) +} + +// RewriteMultiValueCall rewrites multi-valued f() to use temporaries, // so the backend wouldn't need to worry about tuple-valued expressions. -func rewriteMultiValueCall(n ir.InitNode, call ir.Node) { +func RewriteMultiValueCall(n ir.InitNode, call ir.Node) { // If we're outside of function context, then this call will // be executed during the generated init function. However, // init.go hasn't yet created it. Instead, associate the // temporary variables with InitTodoFunc for now, and init.go - // will reassociate them later when it's appropriate. + // will reassociate them later when it's appropriate. (1) static := ir.CurFunc == nil if static { ir.CurFunc = InitTodoFunc @@ -1042,7 +1005,8 @@ func checksliceconst(lo ir.Node, hi ir.Node) bool { } // The result of implicitstar MUST be assigned back to n, e.g. -// n.Left = implicitstar(n.Left) +// +// n.Left = implicitstar(n.Left) func implicitstar(n ir.Node) ir.Node { // insert implicit * if needed for fixed array t := n.Type() @@ -1216,7 +1180,7 @@ func Lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field { } if f1 != nil { - if dostrcmp > 1 || f1.Broke() { + if dostrcmp > 1 { // Already in the process of diagnosing an error. return f1 } @@ -1337,10 +1301,6 @@ func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl i lno := base.Pos defer func() { base.Pos = lno }() - if tstruct.Broke() { - return - } - var n ir.Node if len(nl) == 1 { n = nl[0] @@ -1429,7 +1389,7 @@ invalidddd: return notenough: - if n == nil || (!n.Diag() && n.Type() != nil) { + if n == nil || n.Type() != nil { details := errorDetails(nl, tstruct, isddd) if call != nil { // call is the expression being called, not the overall call. @@ -1445,7 +1405,7 @@ notenough: base.Errorf("not enough arguments to %v%s", op, details) } if n != nil { - n.SetDiag(true) + base.Fatalf("invalid call") } } return @@ -1533,43 +1493,6 @@ func fielddup(name string, hash map[string]bool) { hash[name] = true } -// iscomptype reports whether type t is a composite literal type. -func iscomptype(t *types.Type) bool { - switch t.Kind() { - case types.TARRAY, types.TSLICE, types.TSTRUCT, types.TMAP: - return true - default: - return false - } -} - -// pushtype adds elided type information for composite literals if -// appropriate, and returns the resulting expression. -func pushtype(nn ir.Node, t *types.Type) ir.Node { - if nn == nil || nn.Op() != ir.OCOMPLIT { - return nn - } - n := nn.(*ir.CompLitExpr) - if n.Ntype != nil { - return n - } - - switch { - case iscomptype(t): - // For T, return T{...}. - n.Ntype = ir.TypeNode(t) - - case t.IsPtr() && iscomptype(t.Elem()): - // For *T, return &T{...}. - n.Ntype = ir.TypeNode(t.Elem()) - - addr := NodAddrAt(n.Pos(), n) - addr.SetImplicit(true) - return addr - } - return n -} - // typecheckarraylit type-checks a sequence of slice/array literal elements. func typecheckarraylit(elemType *types.Type, bound int64, elts []ir.Node, ctx string) int64 { // If there are key/value pairs, create a map to keep seen @@ -1592,21 +1515,12 @@ func typecheckarraylit(elemType *types.Type, bound int64, elts []ir.Node, ctx st elt.Key = Expr(elt.Key) key = IndexConst(elt.Key) if key < 0 { - if !elt.Key.Diag() { - if key == -2 { - base.Errorf("index too large") - } else { - base.Errorf("index must be non-negative integer constant") - } - elt.Key.SetDiag(true) - } - key = -(1 << 30) // stay negative for a while + base.Fatalf("invalid index: %v", elt.Key) } kv = elt r = elt.Value } - r = pushtype(r, elemType) r = Expr(r) r = AssignConv(r, elemType, ctx) if kv != nil { @@ -1655,7 +1569,7 @@ func checklvalue(n ir.Node, verb string) { } } -func checkassign(stmt ir.Node, n ir.Node) { +func checkassign(n ir.Node) { // have already complained about n being invalid if n.Type() == nil { if base.Errors() == 0 { @@ -1674,9 +1588,7 @@ func checkassign(stmt ir.Node, n ir.Node) { } defer n.SetType(nil) - if n.Diag() { - return - } + switch { case n.Op() == ir.ODOT && n.(*ir.SelectorExpr).X.Op() == ir.OINDEXMAP: base.Errorf("cannot assign to struct field %v in map", n) @@ -1702,7 +1614,8 @@ func checkassignto(src *types.Type, dst ir.Node) { } // The result of stringtoruneslit MUST be assigned back to n, e.g. -// n.Left = stringtoruneslit(n.Left) +// +// n.Left = stringtoruneslit(n.Left) func stringtoruneslit(n *ir.ConvExpr) ir.Node { if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String { base.Fatalf("stringtoarraylit %v", n) @@ -1715,230 +1628,7 @@ func stringtoruneslit(n *ir.ConvExpr) ir.Node { i++ } - nn := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(n.Type()), nil) - nn.List = l - return Expr(nn) -} - -var mapqueue []*ir.MapType - -func CheckMapKeys() { - for _, n := range mapqueue { - k := n.Type().MapType().Key - if !k.Broke() && !types.IsComparable(k) { - base.ErrorfAt(n.Pos(), "invalid map key type %v", k) - } - } - mapqueue = nil -} - -// TypeGen tracks the number of function-scoped defined types that -// have been declared. It's used to generate unique linker symbols for -// their runtime type descriptors. -var TypeGen int32 - -func typecheckdeftype(n *ir.Name) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckdeftype", n)(nil) - } - - t := types.NewNamed(n) - if n.Curfn != nil { - TypeGen++ - t.Vargen = TypeGen - } - - if n.Pragma()&ir.NotInHeap != 0 { - t.SetNotInHeap(true) - } - - n.SetType(t) - n.SetTypecheck(1) - n.SetWalkdef(1) - - types.DeferCheckSize() - errorsBefore := base.Errors() - n.Ntype = typecheckNtype(n.Ntype) - if underlying := n.Ntype.Type(); underlying != nil { - t.SetUnderlying(underlying) - } else { - n.SetDiag(true) - n.SetType(nil) - } - if t.Kind() == types.TFORW && base.Errors() > errorsBefore { - // Something went wrong during type-checking, - // but it was reported. Silence future errors. - t.SetBroke(true) - } - types.ResumeCheckSize() -} - -func typecheckdef(n *ir.Name) { - if base.EnableTrace && base.Flag.LowerT { - defer tracePrint("typecheckdef", n)(nil) - } - - if n.Walkdef() == 1 { - return - } - - if n.Type() != nil { // builtin - // Mark as Walkdef so that if n.SetType(nil) is called later, we - // won't try walking again. - if got := n.Walkdef(); got != 0 { - base.Fatalf("unexpected walkdef: %v", got) - } - n.SetWalkdef(1) - return - } - - lno := ir.SetPos(n) - typecheckdefstack = append(typecheckdefstack, n) - if n.Walkdef() == 2 { - base.FlushErrors() - fmt.Printf("typecheckdef loop:") - for i := len(typecheckdefstack) - 1; i >= 0; i-- { - n := typecheckdefstack[i] - fmt.Printf(" %v", n.Sym()) - } - fmt.Printf("\n") - base.Fatalf("typecheckdef loop") - } - - n.SetWalkdef(2) - - switch n.Op() { - default: - base.Fatalf("typecheckdef %v", n.Op()) - - case ir.OLITERAL: - if n.Ntype != nil { - n.Ntype = typecheckNtype(n.Ntype) - n.SetType(n.Ntype.Type()) - n.Ntype = nil - if n.Type() == nil { - n.SetDiag(true) - goto ret - } - } - - e := n.Defn - n.Defn = nil - if e == nil { - ir.Dump("typecheckdef nil defn", n) - base.ErrorfAt(n.Pos(), "xxx") - } - - e = Expr(e) - if e.Type() == nil { - goto ret - } - if !ir.IsConstNode(e) { - if !e.Diag() { - if e.Op() == ir.ONIL { - base.ErrorfAt(n.Pos(), "const initializer cannot be nil") - } else { - base.ErrorfAt(n.Pos(), "const initializer %v is not a constant", e) - } - e.SetDiag(true) - } - goto ret - } - - t := n.Type() - if t != nil { - if !ir.OKForConst[t.Kind()] { - base.ErrorfAt(n.Pos(), "invalid constant type %v", t) - goto ret - } - - if !e.Type().IsUntyped() && !types.Identical(t, e.Type()) { - base.ErrorfAt(n.Pos(), "cannot use %L as type %v in const initializer", e, t) - goto ret - } - - e = convlit(e, t) - } - - n.SetType(e.Type()) - if n.Type() != nil { - n.SetVal(e.Val()) - } - - case ir.ONAME: - if n.Ntype != nil { - n.Ntype = typecheckNtype(n.Ntype) - n.SetType(n.Ntype.Type()) - if n.Type() == nil { - n.SetDiag(true) - goto ret - } - } - - if n.Type() != nil { - break - } - if n.Defn == nil { - if n.BuiltinOp != 0 { // like OPRINTN - break - } - if base.Errors() > 0 { - // Can have undefined variables in x := foo - // that make x have an n.name.Defn == nil. - // If there are other errors anyway, don't - // bother adding to the noise. - break - } - - base.Fatalf("var without type, init: %v", n.Sym()) - } - - if n.Defn.Op() == ir.ONAME { - n.Defn = Expr(n.Defn) - n.SetType(n.Defn.Type()) - break - } - - n.Defn = Stmt(n.Defn) // fills in n.Type - - case ir.OTYPE: - if n.Alias() { - // Type alias declaration: Simply use the rhs type - no need - // to create a new type. - // If we have a syntax error, name.Ntype may be nil. - if n.Ntype != nil { - n.Ntype = typecheckNtype(n.Ntype) - n.SetType(n.Ntype.Type()) - if n.Type() == nil { - n.SetDiag(true) - goto ret - } - // For package-level type aliases, set n.Sym.Def so we can identify - // it as a type alias during export. See also #31959. - if n.Curfn == nil { - n.Sym().Def = n.Ntype - } - } - break - } - - // regular type declaration - typecheckdeftype(n) - } - -ret: - if n.Op() != ir.OLITERAL && n.Type() != nil && n.Type().IsUntyped() { - base.Fatalf("got %v for %v", n.Type(), n) - } - last := len(typecheckdefstack) - 1 - if typecheckdefstack[last] != n { - base.Fatalf("typecheckdefstack mismatch") - } - typecheckdefstack[last] = nil - typecheckdefstack = typecheckdefstack[:last] - - base.Pos = lno - n.SetWalkdef(1) + return Expr(ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, n.Type(), l)) } func checkmake(t *types.Type, arg string, np *ir.Node) bool { @@ -1973,11 +1663,11 @@ func checkmake(t *types.Type, arg string, np *ir.Node) bool { return true } -// checkunsafeslice is like checkmake but for unsafe.Slice. -func checkunsafeslice(np *ir.Node) bool { +// checkunsafesliceorstring is like checkmake but for unsafe.{Slice,String}. +func checkunsafesliceorstring(op ir.Op, np *ir.Node) bool { n := *np if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL { - base.Errorf("non-integer len argument in unsafe.Slice - %v", n.Type()) + base.Errorf("non-integer len argument in %v - %v", op, n.Type()) return false } @@ -1986,11 +1676,11 @@ func checkunsafeslice(np *ir.Node) bool { if n.Op() == ir.OLITERAL { v := toint(n.Val()) if constant.Sign(v) < 0 { - base.Errorf("negative len argument in unsafe.Slice") + base.Errorf("negative len argument in %v", op) return false } if ir.ConstOverflow(v, types.Types[types.TINT]) { - base.Errorf("len argument too large in unsafe.Slice") + base.Errorf("len argument too large in %v", op) return false } } @@ -2021,7 +1711,7 @@ func markBreak(fn *ir.Func) { setHasBreak(labels[n.Label]) } - case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OSELECT, ir.ORANGE: + case ir.OFOR, ir.OSWITCH, ir.OSELECT, ir.ORANGE: old := implicit implicit = n var sym *types.Sym @@ -2054,22 +1744,6 @@ func markBreak(fn *ir.Func) { mark(fn) } -func controlLabel(n ir.Node) *types.Sym { - switch n := n.(type) { - default: - base.Fatalf("controlLabel %+v", n.Op()) - return nil - case *ir.ForStmt: - return n.Label - case *ir.RangeStmt: - return n.Label - case *ir.SelectStmt: - return n.Label - case *ir.SwitchStmt: - return n.Label - } -} - func setHasBreak(n ir.Node) { switch n := n.(type) { default: @@ -2113,7 +1787,7 @@ func isTermNode(n ir.Node) bool { case ir.OGOTO, ir.ORETURN, ir.OTAILCALL, ir.OPANIC, ir.OFALL: return true - case ir.OFOR, ir.OFORUNTIL: + case ir.OFOR: n := n.(*ir.ForStmt) if n.Cond != nil { return false @@ -2159,77 +1833,8 @@ func isTermNode(n ir.Node) bool { return false } -// CheckUnused checks for any declared variables that weren't used. -func CheckUnused(fn *ir.Func) { - // Only report unused variables if we haven't seen any type-checking - // errors yet. - if base.Errors() != 0 { - return - } - - // Propagate the used flag for typeswitch variables up to the NONAME in its definition. - for _, ln := range fn.Dcl { - if ln.Op() == ir.ONAME && ln.Class == ir.PAUTO && ln.Used() { - if guard, ok := ln.Defn.(*ir.TypeSwitchGuard); ok { - guard.Used = true - } - } - } - - for _, ln := range fn.Dcl { - if ln.Op() != ir.ONAME || ln.Class != ir.PAUTO || ln.Used() { - continue - } - if defn, ok := ln.Defn.(*ir.TypeSwitchGuard); ok { - if defn.Used { - continue - } - base.ErrorfAt(defn.Tag.Pos(), "%v declared but not used", ln.Sym()) - defn.Used = true // suppress repeats - } else { - base.ErrorfAt(ln.Pos(), "%v declared but not used", ln.Sym()) - } - } -} - -// CheckReturn makes sure that fn terminates appropriately. -func CheckReturn(fn *ir.Func) { - if fn.Type() != nil && fn.Type().NumResults() != 0 && len(fn.Body) != 0 { - markBreak(fn) - if !isTermNodes(fn.Body) { - base.ErrorfAt(fn.Endlineno, "missing return at end of function") - } - } -} - -// getIotaValue returns the current value for "iota", -// or -1 if not within a ConstSpec. -func getIotaValue() int64 { - if i := len(typecheckdefstack); i > 0 { - if x := typecheckdefstack[i-1]; x.Op() == ir.OLITERAL { - return x.Iota() - } - } - - if ir.CurFunc != nil && ir.CurFunc.Iota >= 0 { - return ir.CurFunc.Iota - } - - return -1 -} - -// curpkg returns the current package, based on Curfn. -func curpkg() *types.Pkg { - fn := ir.CurFunc - if fn == nil { - // Initialization expressions for package-scope variables. - return types.LocalPkg - } - return fnpkg(fn.Nname) -} - func Conv(n ir.Node, t *types.Type) ir.Node { - if types.Identical(n.Type(), t) { + if types.IdenticalStrict(n.Type(), t) { return n } n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) @@ -2241,7 +1846,7 @@ func Conv(n ir.Node, t *types.Type) ir.Node { // ConvNop converts node n to type t using the OCONVNOP op // and typechecks the result with ctxExpr. func ConvNop(n ir.Node, t *types.Type) ir.Node { - if types.Identical(n.Type(), t) { + if types.IdenticalStrict(n.Type(), t) { return n } n = ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n) diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go index de185ab94471d6..828a8db3e7f71a 100644 --- a/src/cmd/compile/internal/typecheck/universe.go +++ b/src/cmd/compile/internal/typecheck/universe.go @@ -7,7 +7,6 @@ package typecheck import ( "go/constant" - "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/types" "cmd/internal/src" @@ -29,37 +28,6 @@ var ( okforarith [types.NTYPE]bool ) -var basicTypes = [...]struct { - name string - etype types.Kind -}{ - {"int8", types.TINT8}, - {"int16", types.TINT16}, - {"int32", types.TINT32}, - {"int64", types.TINT64}, - {"uint8", types.TUINT8}, - {"uint16", types.TUINT16}, - {"uint32", types.TUINT32}, - {"uint64", types.TUINT64}, - {"float32", types.TFLOAT32}, - {"float64", types.TFLOAT64}, - {"complex64", types.TCOMPLEX64}, - {"complex128", types.TCOMPLEX128}, - {"bool", types.TBOOL}, - {"string", types.TSTRING}, -} - -var typedefs = [...]struct { - name string - etype types.Kind - sameas32 types.Kind - sameas64 types.Kind -}{ - {"int", types.TINT, types.TINT32, types.TINT64}, - {"uint", types.TUINT, types.TUINT32, types.TUINT64}, - {"uintptr", types.TUINTPTR, types.TUINT32, types.TUINT64}, -} - var builtinFuncs = [...]struct { name string op ir.Op @@ -90,81 +58,19 @@ var unsafeFuncs = [...]struct { {"Offsetof", ir.OOFFSETOF}, {"Sizeof", ir.OSIZEOF}, {"Slice", ir.OUNSAFESLICE}, + {"SliceData", ir.OUNSAFESLICEDATA}, + {"String", ir.OUNSAFESTRING}, + {"StringData", ir.OUNSAFESTRINGDATA}, } // InitUniverse initializes the universe block. func InitUniverse() { - if types.PtrSize == 0 { - base.Fatalf("typeinit before betypeinit") - } - - types.SlicePtrOffset = 0 - types.SliceLenOffset = types.Rnd(types.SlicePtrOffset+int64(types.PtrSize), int64(types.PtrSize)) - types.SliceCapOffset = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize)) - types.SliceSize = types.Rnd(types.SliceCapOffset+int64(types.PtrSize), int64(types.PtrSize)) - - // string is same as slice wo the cap - types.StringSize = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize)) - - for et := types.Kind(0); et < types.NTYPE; et++ { - types.SimType[et] = et - } - - types.Types[types.TANY] = types.New(types.TANY) - types.Types[types.TINTER] = types.NewInterface(types.LocalPkg, nil) - - defBasic := func(kind types.Kind, pkg *types.Pkg, name string) *types.Type { - sym := pkg.Lookup(name) + types.InitTypes(func(sym *types.Sym, typ *types.Type) types.Object { n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym) - t := types.NewBasic(kind, n) - n.SetType(t) + n.SetType(typ) sym.Def = n - if kind != types.TANY { - types.CalcSize(t) - } - return t - } - - for _, s := range &basicTypes { - types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name) - } - - for _, s := range &typedefs { - sameas := s.sameas32 - if types.PtrSize == 8 { - sameas = s.sameas64 - } - types.SimType[s.etype] = sameas - - types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name) - } - - // We create separate byte and rune types for better error messages - // rather than just creating type alias *types.Sym's for the uint8 and - // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false. - // TODO(gri) Should we get rid of this special case (at the cost - // of less informative error messages involving bytes and runes)? - // (Alternatively, we could introduce an OTALIAS node representing - // type aliases, albeit at the cost of having to deal with it everywhere). - types.ByteType = defBasic(types.TUINT8, types.BuiltinPkg, "byte") - types.RuneType = defBasic(types.TINT32, types.BuiltinPkg, "rune") - - // error type - s := types.BuiltinPkg.Lookup("error") - n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s) - types.ErrorType = types.NewNamed(n) - types.ErrorType.SetUnderlying(makeErrorInterface()) - n.SetType(types.ErrorType) - s.Def = n - types.CalcSize(types.ErrorType) - - types.Types[types.TUNSAFEPTR] = defBasic(types.TUNSAFEPTR, ir.Pkgs.Unsafe, "Pointer") - - // simple aliases - types.SimType[types.TMAP] = types.TPTR - types.SimType[types.TCHAN] = types.TPTR - types.SimType[types.TFUNC] = types.TPTR - types.SimType[types.TUNSAFEPTR] = types.TPTR + return n + }) for _, s := range &builtinFuncs { s2 := types.BuiltinPkg.Lookup(s.name) @@ -174,13 +80,13 @@ func InitUniverse() { } for _, s := range &unsafeFuncs { - s2 := ir.Pkgs.Unsafe.Lookup(s.name) + s2 := types.UnsafePkg.Lookup(s.name) def := NewName(s2) def.BuiltinOp = s.op s2.Def = def } - s = types.BuiltinPkg.Lookup("true") + s := types.BuiltinPkg.Lookup("true") s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(true)) s = types.BuiltinPkg.Lookup("false") @@ -188,40 +94,17 @@ func InitUniverse() { s = Lookup("_") types.BlankSym = s - s.Block = -100 s.Def = NewName(s) - types.Types[types.TBLANK] = types.New(types.TBLANK) ir.AsNode(s.Def).SetType(types.Types[types.TBLANK]) ir.BlankNode = ir.AsNode(s.Def) ir.BlankNode.SetTypecheck(1) s = types.BuiltinPkg.Lookup("_") - s.Block = -100 s.Def = NewName(s) - types.Types[types.TBLANK] = types.New(types.TBLANK) ir.AsNode(s.Def).SetType(types.Types[types.TBLANK]) - types.Types[types.TNIL] = types.New(types.TNIL) s = types.BuiltinPkg.Lookup("nil") - nnil := NodNil() - nnil.(*ir.NilExpr).SetSym(s) - s.Def = nnil - - s = types.BuiltinPkg.Lookup("iota") - s.Def = ir.NewIota(base.Pos, s) - - for et := types.TINT8; et <= types.TUINT64; et++ { - types.IsInt[et] = true - } - types.IsInt[types.TINT] = true - types.IsInt[types.TUINT] = true - types.IsInt[types.TUINTPTR] = true - - types.IsFloat[types.TFLOAT32] = true - types.IsFloat[types.TFLOAT64] = true - - types.IsComplex[types.TCOMPLEX64] = true - types.IsComplex[types.TCOMPLEX128] = true + s.Def = NodNil() // initialize okfor for et := types.Kind(0); et < types.NTYPE; et++ { @@ -320,22 +203,6 @@ func InitUniverse() { // special okfor[ir.OCAP] = okforcap[:] okfor[ir.OLEN] = okforlen[:] - - // comparison - iscmp[ir.OLT] = true - iscmp[ir.OGT] = true - iscmp[ir.OGE] = true - iscmp[ir.OLE] = true - iscmp[ir.OEQ] = true - iscmp[ir.ONE] = true -} - -func makeErrorInterface() *types.Type { - sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, nil, []*types.Field{ - types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]), - }) - method := types.NewField(src.NoXPos, Lookup("Error"), sig) - return types.NewInterface(types.NoPkg, []*types.Field{method}) } // DeclareUniverse makes the universe block visible within the current package. @@ -354,6 +221,5 @@ func DeclareUniverse() { } s1.Def = s.Def - s1.Block = s.Block } } diff --git a/src/cmd/compile/internal/types/alg.go b/src/cmd/compile/internal/types/alg.go index 2c2700f345730e..c1f93fc1c3b5d9 100644 --- a/src/cmd/compile/internal/types/alg.go +++ b/src/cmd/compile/internal/types/alg.go @@ -40,9 +40,6 @@ const ( // If it returns ANOEQ, it also returns the component type of t that // makes it incomparable. func AlgType(t *Type) (AlgKind, *Type) { - if t.Broke() { - return AMEM, nil - } if t.Noalg() { return ANOEQ, t } @@ -165,7 +162,7 @@ func IsPaddedField(t *Type, i int) bool { if !t.IsStruct() { base.Fatalf("IsPaddedField called non-struct %v", t) } - end := t.Width + end := t.width if i+1 < t.NumFields() { end = t.Field(i + 1).Offset } diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 8b988952a78d2c..c6e99d26c265d7 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -6,7 +6,6 @@ package types import ( "bytes" - "crypto/md5" "encoding/binary" "fmt" "go/constant" @@ -15,6 +14,7 @@ import ( "sync" "cmd/compile/internal/base" + "cmd/internal/notsha256" ) // BuiltinPkg is a fake package that declares the universe block. @@ -23,6 +23,9 @@ var BuiltinPkg *Pkg // LocalPkg is the package being compiled. var LocalPkg *Pkg +// UnsafePkg is package unsafe. +var UnsafePkg *Pkg + // BlankSym is the blank (_) symbol. var BlankSym *Sym @@ -61,7 +64,7 @@ var NumImport = make(map[string]int) // The default is regular Go syntax (fmtGo). // fmtDebug is like fmtGo but for debugging dumps and prints the type kind too. // fmtTypeID and fmtTypeIDName are for generating various unique representations -// of types used in hashes and the linker. +// of types used in hashes, the linker, and function/method instantiations. type fmtMode int const ( @@ -79,7 +82,6 @@ const ( // %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols. // %+v Debug syntax: always include PkgName. prefix even for local names. // %S Short syntax: Name only, no matter what. -// func (s *Sym) Format(f fmt.State, verb rune) { mode := fmtGo switch verb { @@ -137,17 +139,21 @@ func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { } func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { + name := s.Name if q := pkgqual(s.Pkg, verb, mode); q != "" { b.WriteString(q) b.WriteByte('.') } - b.WriteString(s.Name) + b.WriteString(name) } // pkgqual returns the qualifier that should be used for printing // symbols from the given package in the given mode. // If it returns the empty string, no qualification is needed. func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string { + if pkg == nil { + return "" + } if verb != 'S' { switch mode { case fmtGo: // This is for the user @@ -217,7 +223,6 @@ var fmtBufferPool = sync.Pool{ // %L Go syntax for underlying type if t is named // %S short Go syntax: drop leading "func" in function type // %-S special case for method receiver symbol -// func (t *Type) Format(s fmt.State, verb rune) { mode := fmtGo switch verb { @@ -239,17 +244,26 @@ func (t *Type) String() string { return tconv(t, 0, fmtGo) } -// ShortString generates a short description of t. -// It is used in autogenerated method names, reflection, -// and itab names. -func (t *Type) ShortString() string { +// LinkString returns a string description of t, suitable for use in +// link symbols. +// +// The description corresponds to type identity. That is, for any pair +// of types t1 and t2, Identical(t1, t2) == (t1.LinkString() == +// t2.LinkString()) is true. Thus it's safe to use as a map key to +// implement a type-identity-keyed map. +func (t *Type) LinkString() string { return tconv(t, 0, fmtTypeID) } -// LongString generates a complete description of t. -// It is useful for reflection, -// or when a unique fingerprint or hash of a type is required. -func (t *Type) LongString() string { +// NameString generates a user-readable, mostly unique string +// description of t. NameString always returns the same description +// for identical types, even across compilation units. +// +// NameString qualifies identifiers by package name, so it has +// collisions when different packages share the same names and +// identifiers. It also does not distinguish function-scope defined +// types from package-scoped defined types or from each other. +func (t *Type) NameString() string { return tconv(t, 0, fmtTypeIDName) } @@ -278,7 +292,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type return } if t.Kind() == TSSA { - b.WriteString(t.Extra.(string)) + b.WriteString(t.extra.(string)) return } if t.Kind() == TTUPLE { @@ -289,7 +303,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type } if t.Kind() == TRESULTS { - tys := t.Extra.(*Results).Types + tys := t.extra.(*Results).Types for i, et := range tys { if i > 0 { b.WriteByte(',') @@ -299,8 +313,8 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type return } - if t == ByteType || t == RuneType { - // in %-T mode collapse rune and byte with their originals. + if t == AnyType || t == ByteType || t == RuneType { + // in %-T mode collapse predeclared aliases with their originals. switch mode { case fmtTypeIDName, fmtTypeID: t = Types[t.Kind()] @@ -316,31 +330,30 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type // Unless the 'L' flag was specified, if the type has a name, just print that name. if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] { - switch mode { - case fmtTypeID, fmtTypeIDName: - if verb == 'S' { - if t.Vargen != 0 { - sconv2(b, t.Sym(), 'S', mode) - fmt.Fprintf(b, "·%d", t.Vargen) - return - } - sconv2(b, t.Sym(), 'S', mode) - return - } - - if mode == fmtTypeIDName { - sconv2(b, t.Sym(), 'v', fmtTypeIDName) - return - } + // Default to 'v' if verb is invalid. + if verb != 'S' { + verb = 'v' + } - if t.Sym().Pkg == LocalPkg && t.Vargen != 0 { - sconv2(b, t.Sym(), 'v', mode) - fmt.Fprintf(b, "·%d", t.Vargen) - return + // In unified IR, function-scope defined types will have a ·N + // suffix embedded directly in their Name. Trim this off for + // non-fmtTypeID modes. + sym := t.Sym() + if mode != fmtTypeID { + base, _ := SplitVargenSuffix(sym.Name) + if len(base) < len(sym.Name) { + sym = &Sym{Pkg: sym.Pkg, Name: base} } } - - sconv2(b, t.Sym(), 'v', mode) + sconv2(b, sym, verb, mode) + + // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName + // output too. It seems like it should, but that mode is currently + // used in string representation used by reflection, which is + // user-visible and doesn't expect this. + if mode == fmtTypeID && t.vargen != 0 { + fmt.Fprintf(b, "·%d", t.vargen) + } return } @@ -567,6 +580,18 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type b.WriteString(fmt.Sprintf("%p", t)) } + case TUNION: + for i := 0; i < t.NumTerms(); i++ { + if i > 0 { + b.WriteString("|") + } + elem, tilde := t.Term(i) + if tilde { + b.WriteString("~") + } + tconv2(b, elem, 0, mode, visited) + } + case Txxx: b.WriteString("Txxx") @@ -587,6 +612,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty } var name string + nameSep := " " if verb != 'S' { s := f.Sym @@ -595,7 +621,47 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty s = OrigSym(s) } - if s != nil && f.Embedded == 0 { + // Using type aliases and embedded fields, it's possible to + // construct types that can't be directly represented as a + // type literal. For example, given "type Int = int" (#50190), + // it would be incorrect to format "struct{ Int }" as either + // "struct{ int }" or "struct{ Int int }", because those each + // represent other, distinct types. + // + // So for the purpose of LinkString (i.e., fmtTypeID), we use + // the non-standard syntax "struct{ Int = int }" to represent + // embedded fields that have been renamed through the use of + // type aliases. + if f.Embedded != 0 { + if mode == fmtTypeID { + nameSep = " = " + + // Compute tsym, the symbol that would normally be used as + // the field name when embedding f.Type. + // TODO(mdempsky): Check for other occurrences of this logic + // and deduplicate. + typ := f.Type + if typ.IsPtr() { + base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ) + typ = typ.Elem() + } + tsym := typ.Sym() + + // If the field name matches the embedded type's name, then + // suppress printing of the field name. For example, format + // "struct{ T }" as simply that instead of "struct{ T = T }". + if tsym != nil && (s == tsym || IsExported(tsym.Name) && s.Name == tsym.Name) { + s = nil + } + } else { + // Suppress the field name for embedded fields for + // non-LinkString formats, to match historical behavior. + // TODO(mdempsky): Re-evaluate this. + s = nil + } + } + + if s != nil { if funarg != FunargNone { name = fmt.Sprint(f.Nname) } else if verb == 'L' { @@ -614,7 +680,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty if name != "" { b.WriteString(name) - b.WriteString(" ") + b.WriteString(nameSep) } if f.IsDDD() { @@ -634,6 +700,21 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty } } +// SplitVargenSuffix returns name split into a base string and a ·N +// suffix, if any. +func SplitVargenSuffix(name string) (base, suffix string) { + i := len(name) + for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' { + i-- + } + const dot = "·" + if i >= len(dot) && name[i-len(dot):i] == dot { + i -= len(dot) + return name[:i], name[i:] + } + return name, "" +} + // Val func FmtConst(v constant.Value, sharp bool) string { @@ -671,9 +752,9 @@ func FmtConst(v constant.Value, sharp bool) string { // TypeHash computes a hash value for type t to use in type switch statements. func TypeHash(t *Type) uint32 { - p := t.LongString() + p := t.LinkString() - // Using MD5 is overkill, but reduces accidental collisions. - h := md5.Sum([]byte(p)) + // Using SHA256 is overkill, but reduces accidental collisions. + h := notsha256.Sum256([]byte(p)) return binary.LittleEndian.Uint32(h[:4]) } diff --git a/src/cmd/compile/internal/types/goversion.go b/src/cmd/compile/internal/types/goversion.go index 1a324aa42fdb70..ceb2ed366edae0 100644 --- a/src/cmd/compile/internal/types/goversion.go +++ b/src/cmd/compile/internal/types/goversion.go @@ -24,19 +24,9 @@ type lang struct { // any language version is supported. var langWant lang -// AllowsGoVersion reports whether a particular package -// is allowed to use Go version major.minor. -// We assume the imported packages have all been checked, -// so we only have to check the local package against the -lang flag. -func AllowsGoVersion(pkg *Pkg, major, minor int) bool { - if pkg == nil { - // TODO(mdempsky): Set Pkg for local types earlier. - pkg = LocalPkg - } - if pkg != LocalPkg { - // Assume imported packages passed type-checking. - return true - } +// AllowsGoVersion reports whether local package is allowed +// to use Go version major.minor. +func AllowsGoVersion(major, minor int) bool { if langWant.major == 0 && langWant.minor == 0 { return true } diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go index dde9f5185687ad..6b3bc2ded1fa92 100644 --- a/src/cmd/compile/internal/types/identity.go +++ b/src/cmd/compile/internal/types/identity.go @@ -4,16 +4,30 @@ package types -// Identical reports whether t1 and t2 are identical types, following -// the spec rules. Receiver parameter types are ignored. +const ( + identIgnoreTags = 1 << iota + identStrict +) + +// Identical reports whether t1 and t2 are identical types, following the spec rules. +// Receiver parameter types are ignored. Named (defined) types are only equal if they +// are pointer-equal - i.e. there must be a unique types.Type for each specific named +// type. Also, a type containing a shape type is considered identical to another type +// (shape or not) if their underlying types are the same, or they are both pointers. func Identical(t1, t2 *Type) bool { - return identical(t1, t2, true, nil) + return identical(t1, t2, 0, nil) } // IdenticalIgnoreTags is like Identical, but it ignores struct tags // for struct identity. func IdenticalIgnoreTags(t1, t2 *Type) bool { - return identical(t1, t2, false, nil) + return identical(t1, t2, identIgnoreTags, nil) +} + +// IdenticalStrict is like Identical, but matches types exactly, without the +// exception for shapes. +func IdenticalStrict(t1, t2 *Type) bool { + return identical(t1, t2, identStrict, nil) } type typePair struct { @@ -21,14 +35,22 @@ type typePair struct { t2 *Type } -func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool { +func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool { if t1 == t2 { return true } - if t1 == nil || t2 == nil || t1.kind != t2.kind || t1.Broke() || t2.Broke() { + if t1 == nil || t2 == nil || t1.kind != t2.kind { return false } - if t1.sym != nil || t2.sym != nil { + if t1.obj != nil || t2.obj != nil { + if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) { + switch t1.kind { + case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR: + return true + } + // fall through to unnamed type comparison for complex types. + goto cont + } // Special case: we keep byte/uint8 and rune/int32 // separate for error messages. Treat them as equal. switch t1.kind { @@ -36,10 +58,19 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b return (t1 == Types[TUINT8] || t1 == ByteType) && (t2 == Types[TUINT8] || t2 == ByteType) case TINT32: return (t1 == Types[TINT32] || t1 == RuneType) && (t2 == Types[TINT32] || t2 == RuneType) + case TINTER: + // Make sure named any type matches any unnamed empty interface + // (but not a shape type, if identStrict). + isUnnamedEface := func(t *Type) bool { return t.IsEmptyInterface() && t.Sym() == nil } + if flags&identStrict != 0 { + return t1 == AnyType && isUnnamedEface(t2) && !t2.HasShape() || t2 == AnyType && isUnnamedEface(t1) && !t1.HasShape() + } + return t1 == AnyType && isUnnamedEface(t2) || t2 == AnyType && isUnnamedEface(t1) default: return false } } +cont: // Any cyclic type must go through a named type, and if one is // named, it is only identical to the other if they are the @@ -66,7 +97,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b } for i, f1 := range t1.AllMethods().Slice() { f2 := t2.AllMethods().Index(i) - if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } } @@ -78,10 +109,10 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b } for i, f1 := range t1.FieldSlice() { f2 := t2.Field(i) - if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } - if cmpTags && f1.Note != f2.Note { + if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note { return false } } @@ -99,7 +130,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b } for i, f1 := range fs1 { f2 := fs2[i] - if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } } @@ -117,10 +148,10 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b } case TMAP: - if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) { + if !identical(t1.Key(), t2.Key(), flags, assumedEqual) { return false } } - return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual) + return identical(t1.Elem(), t2.Elem(), flags, assumedEqual) } diff --git a/src/cmd/compile/internal/types/kind_string.go b/src/cmd/compile/internal/types/kind_string.go index ae24a58b9219d4..3e6a8bc064edf4 100644 --- a/src/cmd/compile/internal/types/kind_string.go +++ b/src/cmd/compile/internal/types/kind_string.go @@ -38,20 +38,21 @@ func _() { _ = x[TSTRING-27] _ = x[TUNSAFEPTR-28] _ = x[TTYPEPARAM-29] - _ = x[TIDEAL-30] - _ = x[TNIL-31] - _ = x[TBLANK-32] - _ = x[TFUNCARGS-33] - _ = x[TCHANARGS-34] - _ = x[TSSA-35] - _ = x[TTUPLE-36] - _ = x[TRESULTS-37] - _ = x[NTYPE-38] + _ = x[TUNION-30] + _ = x[TIDEAL-31] + _ = x[TNIL-32] + _ = x[TBLANK-33] + _ = x[TFUNCARGS-34] + _ = x[TCHANARGS-35] + _ = x[TSSA-36] + _ = x[TTUPLE-37] + _ = x[TRESULTS-38] + _ = x[NTYPE-39] } -const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE" +const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMUNIONIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE" -var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 170, 175, 183, 191, 194, 199, 206, 211} +var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 172, 175, 180, 188, 196, 199, 204, 211, 216} func (i Kind) String() string { if i >= Kind(len(_Kind_index)-1) { diff --git a/src/cmd/compile/internal/types/pkg.go b/src/cmd/compile/internal/types/pkg.go index a6d2e2007b0424..9a2149401729c8 100644 --- a/src/cmd/compile/internal/types/pkg.go +++ b/src/cmd/compile/internal/types/pkg.go @@ -9,15 +9,13 @@ import ( "cmd/internal/objabi" "fmt" "sort" + "strconv" "sync" ) // pkgMap maps a package path to a package. var pkgMap = make(map[string]*Pkg) -// MaxPkgHeight is a height greater than any likely package height. -const MaxPkgHeight = 1e9 - type Pkg struct { Path string // string literal used in import statement, e.g. "runtime/internal/sys" Name string // package name, e.g. "sys" @@ -25,12 +23,6 @@ type Pkg struct { Syms map[string]*Sym Pathsym *obj.LSym - // Height is the package's height in the import graph. Leaf - // packages (i.e., packages with no imports) have height 0, - // and all other packages have height 1 plus the maximum - // height of their imported packages. - Height int - Direct bool // imported directly } @@ -48,7 +40,15 @@ func NewPkg(path, name string) *Pkg { p := new(Pkg) p.Path = path p.Name = name - p.Prefix = objabi.PathToPrefix(path) + if path == "go.shape" { + // Don't escape "go.shape", since it's not needed (it's a builtin + // package), and we don't want escape codes showing up in shape type + // names, which also appear in names of function/method + // instantiations. + p.Prefix = path + } else { + p.Prefix = objabi.PathToPrefix(path) + } p.Syms = make(map[string]*Sym) pkgMap[path] = p @@ -113,6 +113,15 @@ func (pkg *Pkg) LookupBytes(name []byte) *Sym { return pkg.Lookup(str) } +// LookupNum looks up the symbol starting with prefix and ending with +// the decimal n. If prefix is too long, LookupNum panics. +func (pkg *Pkg) LookupNum(prefix string, n int) *Sym { + var buf [20]byte // plenty long enough for all current users + copy(buf[:], prefix) + b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10) + return pkg.LookupBytes(b) +} + var ( internedStringsmu sync.Mutex // protects internedStrings internedStrings = map[string]string{} @@ -137,7 +146,3 @@ func CleanroomDo(f func()) { f() pkgMap = saved } - -func IsDotAlias(sym *Sym) bool { - return sym.Def != nil && sym.Def.Sym() != sym -} diff --git a/src/cmd/compile/internal/types/scope.go b/src/cmd/compile/internal/types/scope.go index d7c454f3795e41..e577b7aa532cb1 100644 --- a/src/cmd/compile/internal/types/scope.go +++ b/src/cmd/compile/internal/types/scope.go @@ -6,21 +6,15 @@ package types import ( "cmd/compile/internal/base" - "cmd/internal/src" ) // Declaration stack & operations -var blockgen int32 = 1 // max block number -var Block int32 = 1 // current block number - // A dsym stores a symbol's shadowed declaration so that it can be // restored once the block scope ends. type dsym struct { - sym *Sym // sym == nil indicates stack mark - def Object - block int32 - lastlineno src.XPos // last declaration for diagnostic + sym *Sym // sym == nil indicates stack mark + def Object } // dclstack maintains a stack of shadowed symbol declarations so that @@ -31,10 +25,8 @@ var dclstack []dsym // it can be shadowed by a new declaration within a nested block scope. func Pushdcl(s *Sym) { dclstack = append(dclstack, dsym{ - sym: s, - def: s.Def, - block: s.Block, - lastlineno: s.Lastlineno, + sym: s, + def: s.Def, }) } @@ -46,14 +38,11 @@ func Popdcl() { s := d.sym if s == nil { // pop stack mark - Block = d.block dclstack = dclstack[:i-1] return } s.Def = d.def - s.Block = d.block - s.Lastlineno = d.lastlineno // Clear dead pointer fields. d.sym = nil @@ -65,11 +54,8 @@ func Popdcl() { // Markdcl records the start of a new block scope for declarations. func Markdcl() { dclstack = append(dclstack, dsym{ - sym: nil, // stack mark - block: Block, + sym: nil, // stack mark }) - blockgen++ - Block = blockgen } func isDclstackValid() bool { diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go index f0e695ab964ac5..e655a3672d0fa4 100644 --- a/src/cmd/compile/internal/types/size.go +++ b/src/cmd/compile/internal/types/size.go @@ -5,8 +5,6 @@ package types import ( - "bytes" - "fmt" "sort" "cmd/compile/internal/base" @@ -19,18 +17,18 @@ var RegSize int // Slices in the runtime are represented by three components: // -// type slice struct { -// ptr unsafe.Pointer -// len int -// cap int -// } +// type slice struct { +// ptr unsafe.Pointer +// len int +// cap int +// } // // Strings in the runtime are represented by two components: // -// type string struct { -// ptr unsafe.Pointer -// len int -// } +// type string struct { +// ptr unsafe.Pointer +// len int +// } // // These variables are the offsets of fields and sizes of these structs. var ( @@ -62,12 +60,13 @@ var MaxWidth int64 var CalcSizeDisabled bool // machine size and rounding alignment is dictated around -// the size of a pointer, set in betypeinit (see ../amd64/galign.go). +// the size of a pointer, set in gc.Main (see ../gc/main.go). var defercalc int -func Rnd(o int64, r int64) int64 { +// RoundUp rounds o to a multiple of r, r is a power of 2. +func RoundUp(o int64, r int64) int64 { if r < 1 || r > 8 || r&(r-1) != 0 { - base.Fatalf("rnd %d", r) + base.Fatalf("Round %d", r) } return (o + r - 1) &^ (r - 1) } @@ -82,7 +81,7 @@ func expandiface(t *Type) { switch prev := seen[m.Sym]; { case prev == nil: seen[m.Sym] = m - case AllowsGoVersion(t.Pkg(), 1, 14) && !explicit && Identical(m.Type, prev.Type): + case !explicit && Identical(m.Type, prev.Type): return default: base.ErrorfAt(m.Pos, "duplicate method %s", m.Sym.Name) @@ -90,6 +89,26 @@ func expandiface(t *Type) { methods = append(methods, m) } + { + methods := t.Methods().Slice() + sort.SliceStable(methods, func(i, j int) bool { + mi, mj := methods[i], methods[j] + + // Sort embedded types by type name (if any). + if mi.Sym == nil && mj.Sym == nil { + return mi.Type.Sym().Less(mj.Type.Sym()) + } + + // Sort methods before embedded types. + if mi.Sym == nil || mj.Sym == nil { + return mi.Sym != nil + } + + // Sort methods by symbol name. + return mi.Sym.Less(mj.Sym) + }) + } + for _, m := range t.Methods().Slice() { if m.Sym == nil { continue @@ -104,26 +123,29 @@ func expandiface(t *Type) { continue } + if m.Type.IsUnion() { + continue + } + + // In 1.18, embedded types can be anything. In Go 1.17, we disallow + // embedding anything other than interfaces. This requirement was caught + // by types2 already, so allow non-interface here. if !m.Type.IsInterface() { - base.ErrorfAt(m.Pos, "interface contains embedded non-interface %v", m.Type) - m.SetBroke(true) - t.SetBroke(true) - // Add to fields so that error messages - // include the broken embedded type when - // printing t. - // TODO(mdempsky): Revisit this. - methods = append(methods, m) continue } // Embedded interface: duplicate all methods - // (including broken ones, if any) and add to t's - // method set. + // and add to t's method set. for _, t1 := range m.Type.AllMethods().Slice() { - // Use m.Pos rather than t1.Pos to preserve embedding position. f := NewField(m.Pos, t1.Sym, t1.Type) addMethod(f, false) + + // Clear position after typechecking, for consistency with types2. + f.Pos = src.NoXPos } + + // Clear position after typechecking, for consistency with types2. + m.Pos = src.NoXPos } sort.Sort(MethodsByName(methods)) @@ -146,6 +168,13 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 { if maxalign < 1 { maxalign = 1 } + // Special case: sync/atomic.align64 is an empty struct we recognize + // as a signal that the struct it contains must be 64-bit-aligned. + // + // This logic is duplicated in go/types and cmd/compile/internal/types2. + if isStruct && t.NumFields() == 0 && t.Sym() != nil && t.Sym().Name == "align64" && isAtomicStdPkg(t.Sym().Pkg) { + maxalign = 8 + } lastzero := int64(0) for _, f := range t.Fields().Slice() { if f.Type == nil { @@ -155,19 +184,26 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 { } CalcSize(f.Type) - if int32(f.Type.Align) > maxalign { - maxalign = int32(f.Type.Align) + // If type T contains a field F marked as not-in-heap, + // then T must also be a not-in-heap type. Otherwise, + // you could heap allocate T and then get a pointer F, + // which would be a heap pointer to a not-in-heap type. + if f.Type.NotInHeap() { + t.SetNotInHeap(true) + } + if int32(f.Type.align) > maxalign { + maxalign = int32(f.Type.align) } - if f.Type.Align > 0 { - o = Rnd(o, int64(f.Type.Align)) + if f.Type.align > 0 { + o = RoundUp(o, int64(f.Type.align)) } if isStruct { // For receiver/args/results, do not set, it depends on ABI f.Offset = o } - w := f.Type.Width + w := f.Type.width if w < 0 { - base.Fatalf("invalid width %d", f.Type.Width) + base.Fatalf("invalid width %d", f.Type.width) } if w == 0 { lastzero = o @@ -195,106 +231,19 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 { // final width is rounded if flag != 0 { - o = Rnd(o, int64(maxalign)) + o = RoundUp(o, int64(maxalign)) } - t.Align = uint8(maxalign) + t.align = uint8(maxalign) // type width only includes back to first field's offset - t.Width = o - starto + t.width = o - starto return o } -// findTypeLoop searches for an invalid type declaration loop involving -// type t and reports whether one is found. If so, path contains the -// loop. -// -// path points to a slice used for tracking the sequence of types -// visited. Using a pointer to a slice allows the slice capacity to -// grow and limit reallocations. -func findTypeLoop(t *Type, path *[]*Type) bool { - // We implement a simple DFS loop-finding algorithm. This - // could be faster, but type cycles are rare. - - if t.Sym() != nil { - // Declared type. Check for loops and otherwise - // recurse on the type expression used in the type - // declaration. - - // Type imported from package, so it can't be part of - // a type loop (otherwise that package should have - // failed to compile). - if t.Sym().Pkg != LocalPkg { - return false - } - - for i, x := range *path { - if x == t { - *path = (*path)[i:] - return true - } - } - - *path = append(*path, t) - if findTypeLoop(t.Obj().(TypeObject).TypeDefn(), path) { - return true - } - *path = (*path)[:len(*path)-1] - } else { - // Anonymous type. Recurse on contained types. - - switch t.Kind() { - case TARRAY: - if findTypeLoop(t.Elem(), path) { - return true - } - case TSTRUCT: - for _, f := range t.Fields().Slice() { - if findTypeLoop(f.Type, path) { - return true - } - } - case TINTER: - for _, m := range t.Methods().Slice() { - if m.Type.IsInterface() { // embedded interface - if findTypeLoop(m.Type, path) { - return true - } - } - } - } - } - - return false -} - -func reportTypeLoop(t *Type) { - if t.Broke() { - return - } - - var l []*Type - if !findTypeLoop(t, &l) { - base.Fatalf("failed to find type loop for: %v", t) - } - - // Rotate loop so that the earliest type declaration is first. - i := 0 - for j, t := range l[1:] { - if typePos(t).Before(typePos(l[i])) { - i = j + 1 - } - } - l = append(l[i:], l[:i]...) - - var msg bytes.Buffer - fmt.Fprintf(&msg, "invalid recursive type %v\n", l[0]) - for _, t := range l { - fmt.Fprintf(&msg, "\t%v: %v refers to\n", base.FmtPos(typePos(t)), t) - t.SetBroke(true) - } - fmt.Fprintf(&msg, "\t%v: %v", base.FmtPos(typePos(l[0])), l[0]) - base.ErrorfAt(typePos(l[0]), msg.String()) +func isAtomicStdPkg(p *Pkg) bool { + return (p.Prefix == "sync/atomic" || p.Prefix == `""` && base.Ctxt.Pkgpath == "sync/atomic") || + (p.Prefix == "runtime/internal/atomic" || p.Prefix == `""` && base.Ctxt.Pkgpath == "runtime/internal/atomic") } // CalcSize calculates and stores the size and alignment for t. @@ -316,32 +265,21 @@ func CalcSize(t *Type) { return } - if t.Width == -2 { - reportTypeLoop(t) - t.Width = 0 - t.Align = 1 + if t.width == -2 { + t.width = 0 + t.align = 1 + base.Fatalf("invalid recursive type %v", t) return } - if t.WidthCalculated() { + if t.widthCalculated() { return } if CalcSizeDisabled { - if t.Broke() { - // break infinite recursion from Fatal call below - return - } - t.SetBroke(true) base.Fatalf("width not calculated: %v", t) } - // break infinite recursion if the broken recursive type - // is referenced again - if t.Broke() && t.Width == 0 { - return - } - // defer CheckSize calls until after we're done DeferCheckSize() @@ -350,8 +288,8 @@ func CalcSize(t *Type) { base.Pos = pos } - t.Width = -2 - t.Align = 0 // 0 means use t.Width, below + t.width = -2 + t.align = 0 // 0 means use t.Width, below et := t.Kind() switch et { @@ -383,15 +321,15 @@ func CalcSize(t *Type) { case TINT64, TUINT64, TFLOAT64: w = 8 - t.Align = uint8(RegSize) + t.align = uint8(RegSize) case TCOMPLEX64: w = 8 - t.Align = 4 + t.align = 4 case TCOMPLEX128: w = 16 - t.Align = uint8(RegSize) + t.align = uint8(RegSize) case TPTR: w = int64(PtrSize) @@ -402,24 +340,35 @@ func CalcSize(t *Type) { case TINTER: // implemented as 2 pointers w = 2 * int64(PtrSize) - t.Align = uint8(PtrSize) + t.align = uint8(PtrSize) expandiface(t) + case TUNION: + // Always part of an interface for now, so size/align don't matter. + // Pretend a union is represented like an interface. + w = 2 * int64(PtrSize) + t.align = uint8(PtrSize) + case TCHAN: // implemented as pointer w = int64(PtrSize) CheckSize(t.Elem()) - // make fake type to check later to - // trigger channel argument check. + // Make fake type to trigger channel element size check after + // any top-level recursive type has been completed. t1 := NewChanArgs(t) CheckSize(t1) case TCHANARGS: t1 := t.ChanArgs() CalcSize(t1) // just in case - if t1.Elem().Width >= 1<<16 { - base.ErrorfAt(typePos(t1), "channel element type too large (>64kB)") + // Make sure size of t1.Elem() is calculated at this point. We can + // use CalcSize() here rather than CheckSize(), because the top-level + // (possibly recursive) type will have been calculated before the fake + // chanargs is handled. + CalcSize(t1.Elem()) + if t1.Elem().width >= 1<<16 { + base.Errorf("channel element type too large (>64kB)") } w = 1 // anything will do @@ -429,7 +378,7 @@ func CalcSize(t *Type) { CheckSize(t.Key()) case TFORW: // should have been filled in - reportTypeLoop(t) + base.Fatalf("invalid recursive type %v", t) w = 1 // anything will do case TANY: @@ -441,7 +390,7 @@ func CalcSize(t *Type) { base.Fatalf("early CalcSize string") } w = StringSize - t.Align = uint8(PtrSize) + t.align = uint8(PtrSize) case TARRAY: if t.Elem() == nil { @@ -449,14 +398,15 @@ func CalcSize(t *Type) { } CalcSize(t.Elem()) - if t.Elem().Width != 0 { - cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().Width) + t.SetNotInHeap(t.Elem().NotInHeap()) + if t.Elem().width != 0 { + cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().width) if uint64(t.NumElem()) > cap { - base.ErrorfAt(typePos(t), "type %L larger than address space", t) + base.Errorf("type %L larger than address space", t) } } - w = t.NumElem() * t.Elem().Width - t.Align = t.Elem().Align + w = t.NumElem() * t.Elem().width + t.align = t.Elem().align case TSLICE: if t.Elem() == nil { @@ -464,12 +414,16 @@ func CalcSize(t *Type) { } w = SliceSize CheckSize(t.Elem()) - t.Align = uint8(PtrSize) + t.align = uint8(PtrSize) case TSTRUCT: if t.IsFuncArgStruct() { base.Fatalf("CalcSize fn struct %v", t) } + // Recognize and mark runtime/internal/sys.nih as not-in-heap. + if sym := t.Sym(); sym != nil && sym.Pkg.Path == "runtime/internal/sys" && sym.Name == "nih" { + t.SetNotInHeap(true) + } w = calcStructOffset(t, t, 0, 1) // make fake type to check later to @@ -486,11 +440,11 @@ func CalcSize(t *Type) { w = calcStructOffset(t1, t1.Recvs(), 0, 0) w = calcStructOffset(t1, t1.Params(), w, RegSize) w = calcStructOffset(t1, t1.Results(), w, RegSize) - t1.Extra.(*Func).Argwid = w + t1.extra.(*Func).Argwid = w if w%int64(RegSize) != 0 { base.Warn("bad type %v %d\n", t1, w) } - t.Align = 1 + t.align = 1 case TTYPEPARAM: // TODO(danscales) - remove when we eliminate the need @@ -499,15 +453,15 @@ func CalcSize(t *Type) { } if PtrSize == 4 && w != int64(int32(w)) { - base.ErrorfAt(typePos(t), "type %v too large", t) + base.Errorf("type %v too large", t) } - t.Width = w - if t.Align == 0 { + t.width = w + if t.align == 0 { if w == 0 || w > 8 || w&(w-1) != 0 { base.Fatalf("invalid alignment for %v", t) } - t.Align = uint8(w) + t.align = uint8(w) } base.Pos = lno @@ -519,7 +473,19 @@ func CalcSize(t *Type) { // filling in s.Width and s.Align, // even if size calculation is otherwise disabled. func CalcStructSize(s *Type) { - s.Width = calcStructOffset(s, s, 0, 1) // sets align + s.width = calcStructOffset(s, s, 0, 1) // sets align +} + +// RecalcSize is like CalcSize, but recalculates t's size even if it +// has already been calculated before. It does not recalculate other +// types. +func RecalcSize(t *Type) { + t.align = 0 + CalcSize(t) +} + +func (t *Type) widthCalculated() bool { + return t.align > 0 } // when a type's width should be known, we call CheckSize @@ -582,17 +548,23 @@ func ResumeCheckSize() { // PtrDataSize returns the length in bytes of the prefix of t // containing pointer data. Anything after this offset is scalar data. +// +// PtrDataSize is only defined for actual Go types. It's an error to +// use it on compiler-internal types (e.g., TSSA, TRESULTS). func PtrDataSize(t *Type) int64 { - if !t.HasPointers() { + switch t.Kind() { + case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32, + TUINT32, TINT64, TUINT64, TINT, TUINT, + TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64: return 0 - } - switch t.Kind() { - case TPTR, - TUNSAFEPTR, - TFUNC, - TCHAN, - TMAP: + case TPTR: + if t.Elem().NotInHeap() { + return 0 + } + return int64(PtrSize) + + case TUNSAFEPTR, TFUNC, TCHAN, TMAP: return int64(PtrSize) case TSTRING: @@ -606,24 +578,38 @@ func PtrDataSize(t *Type) int64 { return 2 * int64(PtrSize) case TSLICE: + if t.Elem().NotInHeap() { + return 0 + } // struct { byte *array; uintgo len; uintgo cap; } return int64(PtrSize) case TARRAY: - // haspointers already eliminated t.NumElem() == 0. - return (t.NumElem()-1)*t.Elem().Width + PtrDataSize(t.Elem()) + if t.NumElem() == 0 { + return 0 + } + // t.NumElem() > 0 + size := PtrDataSize(t.Elem()) + if size == 0 { + return 0 + } + return (t.NumElem()-1)*t.Elem().Size() + size case TSTRUCT: - // Find the last field that has pointers. - var lastPtrField *Field + // Find the last field that has pointers, if any. fs := t.Fields().Slice() for i := len(fs) - 1; i >= 0; i-- { - if fs[i].Type.HasPointers() { - lastPtrField = fs[i] - break + if size := PtrDataSize(fs[i].Type); size > 0 { + return fs[i].Offset + size } } - return lastPtrField.Offset + PtrDataSize(lastPtrField.Type) + return 0 + + case TSSA: + if t != TypeInt128 { + base.Fatalf("PtrDataSize: unexpected ssa type %v", t) + } + return 0 default: base.Fatalf("PtrDataSize: unexpected type, %v", t) diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go index 70289387421487..e83426654cb0e1 100644 --- a/src/cmd/compile/internal/types/sizeof_test.go +++ b/src/cmd/compile/internal/types/sizeof_test.go @@ -20,13 +20,13 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Sym{}, 44, 72}, + {Sym{}, 32, 64}, {Type{}, 60, 104}, {Map{}, 20, 40}, {Forward{}, 20, 32}, {Func{}, 28, 48}, {Struct{}, 16, 32}, - {Interface{}, 4, 8}, + {Interface{}, 8, 16}, {Chan{}, 8, 16}, {Array{}, 12, 16}, {FuncArgs{}, 4, 8}, diff --git a/src/cmd/compile/internal/types/sort.go b/src/cmd/compile/internal/types/sort.go index dc59b064153282..765c070cd94193 100644 --- a/src/cmd/compile/internal/types/sort.go +++ b/src/cmd/compile/internal/types/sort.go @@ -4,11 +4,16 @@ package types -// MethodsByName sorts methods by symbol. +// MethodsByName sorts methods by name. type MethodsByName []*Field -func (x MethodsByName) Len() int { return len(x) } +func (x MethodsByName) Len() int { return len(x) } +func (x MethodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x MethodsByName) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) } -func (x MethodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +// EmbeddedsByName sorts embedded types by name. +type EmbeddedsByName []*Field -func (x MethodsByName) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) } +func (x EmbeddedsByName) Len() int { return len(x) } +func (x EmbeddedsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x EmbeddedsByName) Less(i, j int) bool { return x[i].Type.Sym().Less(x[j].Type.Sym()) } diff --git a/src/cmd/compile/internal/types/structuraltype.go b/src/cmd/compile/internal/types/structuraltype.go new file mode 100644 index 00000000000000..ee1341be210daf --- /dev/null +++ b/src/cmd/compile/internal/types/structuraltype.go @@ -0,0 +1,191 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +// Implementation of structural type computation for types. + +// TODO: we would like to depend only on the types2 computation of structural type, +// but we can only do that the next time we change the export format and export +// structural type info along with each constraint type, since the compiler imports +// types directly into types1 format. + +// A term describes elementary type sets: +// +// term{false, T} set of type T +// term{true, T} set of types with underlying type t +// term{} empty set (we specifically check for typ == nil) +type term struct { + tilde bool + typ *Type +} + +// StructuralType returns the structural type of an interface, or nil if it has no +// structural type. +func (t *Type) StructuralType() *Type { + sts, _ := specificTypes(t) + var su *Type + for _, st := range sts { + u := st.typ.Underlying() + if su != nil { + u = match(su, u) + if u == nil { + return nil + } + } + // su == nil || match(su, u) != nil + su = u + } + return su +} + +// If x and y are identical, match returns x. +// If x and y are identical channels but for their direction +// and one of them is unrestricted, match returns the channel +// with the restricted direction. +// In all other cases, match returns nil. +// x and y are assumed to be underlying types, hence are not named types. +func match(x, y *Type) *Type { + if IdenticalStrict(x, y) { + return x + } + + if x.IsChan() && y.IsChan() && IdenticalStrict(x.Elem(), y.Elem()) { + // We have channels that differ in direction only. + // If there's an unrestricted channel, select the restricted one. + // If both have the same direction, return x (either is fine). + switch { + case x.ChanDir().CanSend() && x.ChanDir().CanRecv(): + return y + case y.ChanDir().CanSend() && y.ChanDir().CanRecv(): + return x + } + } + return nil +} + +// specificTypes returns the list of specific types of an interface type or nil if +// there are none. It also returns a flag that indicates, for an empty term list +// result, whether it represents the empty set, or the infinite set of all types (in +// both cases, there are no specific types). +func specificTypes(t *Type) (list []term, inf bool) { + t.wantEtype(TINTER) + + // We have infinite term list before processing any type elements + // (or if there are no type elements). + inf = true + for _, m := range t.Methods().Slice() { + var r2 []term + inf2 := false + + switch { + case m.IsMethod(): + inf2 = true + + case m.Type.IsUnion(): + nt := m.Type.NumTerms() + for i := 0; i < nt; i++ { + t, tilde := m.Type.Term(i) + if t.IsInterface() { + r3, r3inf := specificTypes(t) + if r3inf { + // Union with an infinite set of types is + // infinite, so skip remaining terms. + r2 = nil + inf2 = true + break + } + // Add the elements of r3 to r2. + for _, r3e := range r3 { + r2 = insertType(r2, r3e) + } + } else { + r2 = insertType(r2, term{tilde, t}) + } + } + + case m.Type.IsInterface(): + r2, inf2 = specificTypes(m.Type) + + default: + // m.Type is a single non-interface type, so r2 is just a + // one-element list, inf2 is false. + r2 = []term{{false, m.Type}} + } + + if inf2 { + // If the current type element has infinite types, + // its intersection with r is just r, so skip this type element. + continue + } + + if inf { + // If r is infinite, then the intersection of r and r2 is just r2. + list = r2 + inf = false + continue + } + + // r and r2 are finite, so intersect r and r2. + var r3 []term + for _, re := range list { + for _, r2e := range r2 { + if tm := intersect(re, r2e); tm.typ != nil { + r3 = append(r3, tm) + } + } + } + list = r3 + } + return +} + +// insertType adds t to the returned list if it is not already in list. +func insertType(list []term, tm term) []term { + for i, elt := range list { + if new := union(elt, tm); new.typ != nil { + // Replace existing elt with the union of elt and new. + list[i] = new + return list + } + } + return append(list, tm) +} + +// If x and y are disjoint, return term with nil typ (which means the union should +// include both types). If x and y are not disjoint, return the single type which is +// the union of x and y. +func union(x, y term) term { + if disjoint(x, y) { + return term{false, nil} + } + if x.tilde || !y.tilde { + return x + } + return y +} + +// intersect returns the intersection x ∩ y. +func intersect(x, y term) term { + if disjoint(x, y) { + return term{false, nil} + } + if !x.tilde || y.tilde { + return x + } + return y +} + +// disjoint reports whether x ∩ y == ∅. +func disjoint(x, y term) bool { + ux := x.typ + if y.tilde { + ux = ux.Underlying() + } + uy := y.typ + if x.tilde { + uy = uy.Underlying() + } + return !IdenticalStrict(ux, uy) +} diff --git a/src/cmd/compile/internal/types/structuraltype_test.go b/src/cmd/compile/internal/types/structuraltype_test.go new file mode 100644 index 00000000000000..cce3334a1b7dd9 --- /dev/null +++ b/src/cmd/compile/internal/types/structuraltype_test.go @@ -0,0 +1,138 @@ +// Copyright 2022 The Go 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 StructuralType() calculates the correct value of structural type for +// unusual cases. + +package types_test + +import ( + "cmd/compile/internal/ir" + . "cmd/compile/internal/types" + "cmd/internal/src" + "testing" +) + +type test struct { + typ *Type + structuralType *Type +} + +func TestStructuralType(t *testing.T) { + // These are the few constants that need to be initialized in order to use + // the types package without using the typecheck package by calling + // typecheck.InitUniverse() (the normal way to initialize the types package). + PtrSize = 8 + RegSize = 8 + MaxWidth = 1 << 50 + + InitTypes(func(sym *Sym, typ *Type) Object { + obj := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym) + obj.SetType(typ) + sym.Def = obj + return obj + }) + + // type intType = int + intType := Types[TINT] + // type structf = struct { f int } + structf := NewStruct(nil, []*Field{ + NewField(src.NoXPos, LocalPkg.Lookup("f"), intType), + }) + + defNamed := func(name string, underlying *Type) *Type { + sym := LocalPkg.Lookup(name) + obj := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym) + typ := NewNamed(obj) + typ.SetUnderlying(underlying) + return typ + } + + Sf := defNamed("Sf", structf) // type Sf structf + A := defNamed("A", intType) // type A int + B := defNamed("B", intType) // type B int + + any := AnyType + + // The tests marked NONE have no structural type; all the others have a + // structural type of structf - "struct { f int }" + tests := []*test{ + { + // interface { struct { f int } } + embed(structf), + structf, + }, + { + // interface { struct { f int }; any } + embed(structf, any), + structf, + }, + { + // interface { Sf } + embed(Sf), + structf, + }, + { + // interface { any; Sf } + embed(any, Sf), + structf, + }, + { + // interface { struct { f int }; Sf } - NONE + embed(structf, Sf), + nil, + }, + { + // interface { struct { f int } | ~struct { f int } } + embed(NewUnion([]*Type{structf, structf}, []bool{false, true})), + structf, + }, + { + // interface { ~struct { f int } ; Sf } + embed(NewUnion([]*Type{structf}, []bool{true}), Sf), + structf, + }, + { + // interface { struct { f int } ; Sf } - NONE + embed(NewUnion([]*Type{structf}, []bool{false}), Sf), + nil, + }, + { + // interface { Sf | A; B | Sf} + embed(NewUnion([]*Type{Sf, A}, []bool{false, false}), + NewUnion([]*Type{B, Sf}, []bool{false, false})), + structf, + }, + { + // interface { Sf | A; A | Sf } - NONE + embed(NewUnion([]*Type{Sf, A}, []bool{false, false}), + NewUnion([]*Type{A, Sf}, []bool{false, false})), + nil, + }, + { + // interface { Sf | any } - NONE + embed(NewUnion([]*Type{Sf, any}, []bool{false, false})), + nil, + }, + { + // interface { Sf | any; Sf } + embed(NewUnion([]*Type{Sf, any}, []bool{false, false}), Sf), + structf, + }, + } + for i, tst := range tests { + if got, want := tst.typ.StructuralType(), tst.structuralType; got != want { + t.Errorf("#%v: StructuralType(%v) = %v, wanted %v", + i, tst.typ, got, want) + } + } +} + +func embed(types ...*Type) *Type { + fields := make([]*Field, len(types)) + for i, t := range types { + fields[i] = NewField(src.NoXPos, nil, t) + } + return NewInterface(LocalPkg, fields, false) +} diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go index 534cf7e2376d72..9d8707befa594b 100644 --- a/src/cmd/compile/internal/types/sym.go +++ b/src/cmd/compile/internal/types/sym.go @@ -7,7 +7,6 @@ package types import ( "cmd/compile/internal/base" "cmd/internal/obj" - "cmd/internal/src" "unicode" "unicode/utf8" ) @@ -32,14 +31,15 @@ type Sym struct { Pkg *Pkg Name string // object name - // Def, Block, and Lastlineno are saved and restored by Pushdcl/Popdcl. - // The unique ONAME, OTYPE, OPACK, or OLITERAL node that this symbol is // bound to within the current scope. (Most parts of the compiler should // prefer passing the Node directly, rather than relying on this field.) - Def Object - Block int32 // blocknumber to catch redeclaration - Lastlineno src.XPos // last declaration for diagnostic + // + // Def is saved and restored by Pushdcl/Popdcl. + // + // Deprecated: New code should avoid depending on Sym.Def. Add + // mdempsky@ as a reviewer for any CLs involving Sym.Def. + Def Object flags bitset8 } @@ -97,19 +97,20 @@ func (sym *Sym) LinksymABI(abi obj.ABI) *obj.LSym { // Less reports whether symbol a is ordered before symbol b. // // Symbols are ordered exported before non-exported, then by name, and -// finally (for non-exported symbols) by package height and path. -// -// Ordering by package height is necessary to establish a consistent -// ordering for non-exported names with the same spelling but from -// different packages. We don't necessarily know the path for the -// package being compiled, but by definition it will have a height -// greater than any other packages seen within the compilation unit. -// For more background, see issue #24693. +// finally (for non-exported symbols) by package path. func (a *Sym) Less(b *Sym) bool { if a == b { return false } + // Nil before non-nil. + if a == nil { + return true + } + if b == nil { + return false + } + // Exported symbols before non-exported. ea := IsExported(a.Name) eb := IsExported(b.Name) @@ -123,9 +124,6 @@ func (a *Sym) Less(b *Sym) bool { return a.Name < b.Name } if !ea { - if a.Pkg.Height != b.Pkg.Height { - return a.Pkg.Height < b.Pkg.Height - } return a.Pkg.Path < b.Pkg.Path } return false diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 1a9aa6916a2fac..a69245ea6967b7 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -8,6 +8,7 @@ import ( "cmd/compile/internal/base" "cmd/internal/src" "fmt" + "strings" "sync" ) @@ -20,18 +21,6 @@ type Object interface { Type() *Type } -// A TypeObject is an Object representing a named type. -type TypeObject interface { - Object - TypeDefn() *Type // for "type T Defn", returns Defn -} - -// A VarObject is an Object representing a function argument, variable, or struct field. -type VarObject interface { - Object - RecordFrameOffset(int64) // save frame offset -} - //go:generate stringer -type Kind -trimprefix T type.go // Kind describes a kind of type. @@ -73,13 +62,14 @@ const ( TSTRING TUNSAFEPTR TTYPEPARAM + TUNION // pseudo-types for literals TIDEAL // untyped numeric constants TNIL TBLANK - // pseudo-types for frame layout + // pseudo-types used temporarily only during frame layout (CalcSize()) TFUNCARGS TCHANARGS @@ -110,32 +100,46 @@ const ( // It also stores pointers to several special types: // - Types[TANY] is the placeholder "any" type recognized by SubstArgTypes. // - Types[TBLANK] represents the blank variable's type. +// - Types[TINTER] is the canonical "interface{}" type. // - Types[TNIL] represents the predeclared "nil" value's type. // - Types[TUNSAFEPTR] is package unsafe's Pointer type. var Types [NTYPE]*Type var ( - // Predeclared alias types. Kept separate for better error messages. + // Predeclared alias types. These are actually created as distinct + // defined types for better error messages, but are then specially + // treated as identical to their respective underlying types. + AnyType *Type ByteType *Type RuneType *Type // Predeclared error interface type. ErrorType *Type + // Predeclared comparable interface type. + ComparableType *Type // Types to represent untyped string and boolean constants. - UntypedString = New(TSTRING) - UntypedBool = New(TBOOL) + UntypedString = newType(TSTRING) + UntypedBool = newType(TBOOL) // Types to represent untyped numeric constants. - UntypedInt = New(TIDEAL) - UntypedRune = New(TIDEAL) - UntypedFloat = New(TIDEAL) - UntypedComplex = New(TIDEAL) + UntypedInt = newType(TIDEAL) + UntypedRune = newType(TIDEAL) + UntypedFloat = newType(TIDEAL) + UntypedComplex = newType(TIDEAL) ) // A Type represents a Go type. +// +// There may be multiple unnamed types with identical structure. However, there must +// be a unique Type object for each unique named (defined) type. After noding, a +// package-level type can be looked up by building its unique symbol sym (sym = +// package.Lookup(name)) and checking sym.Def. If sym.Def is non-nil, the type +// already exists at package scope and is available at sym.Def.(*ir.Name).Type(). +// Local types (which may have the same name as a package-level type) are +// distinguished by the value of vargen. type Type struct { - // Extra contains extra etype-specific fields. + // extra contains extra etype-specific fields. // As an optimization, those etype-specific structs which contain exactly // one pointer-shaped field are stored as values rather than pointers when possible. // @@ -151,11 +155,12 @@ type Type struct { // TARRAY: *Array // TSLICE: Slice // TSSA: string - // TTYPEPARAM: *Interface (though we may not need to store/use the Interface info) - Extra interface{} + // TTYPEPARAM: *Typeparam + // TUNION: *Union + extra interface{} - // Width is the width of this Type in bytes. - Width int64 // valid if Align > 0 + // width is the width of this Type in bytes. + width int64 // valid if Align > 0 // list of base methods (excluding embedding) methods Fields @@ -163,7 +168,7 @@ type Type struct { allMethods Fields // canonical OTYPE node for a named type (should be an ir.Name node with same sym) - nod Object + obj Object // the underlying type (type literal or predeclared type) for a defined type underlying *Type @@ -173,72 +178,85 @@ type Type struct { slice *Type // []T, or nil } - sym *Sym // symbol containing name, for named types - Vargen int32 // unique name for OTYPE/ONAME + vargen int32 // unique name for OTYPE/ONAME kind Kind // kind of type - Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed) + align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed) flags bitset8 // For defined (named) generic types, a pointer to the list of type params - // (in order) of this type that need to be instantiated. For - // fully-instantiated generic types, this is the targs used to instantiate - // them (which are used when generating the corresponding instantiated - // methods). rparams is only set for named types that are generic or are - // fully-instantiated from a generic type, and is otherwise set to nil. + // (in order) of this type that need to be instantiated. For instantiated + // generic types, this is the targs used to instantiate them. These targs + // may be typeparams (for re-instantiated types such as Value[T2]) or + // concrete types (for fully instantiated types such as Value[int]). + // rparams is only set for named types that are generic or are fully + // instantiated from a generic type, and is otherwise set to nil. + // TODO(danscales): choose a better name. rparams *[]*Type + + // For an instantiated generic type, the base generic type. + // This backpointer is useful, because the base type is the type that has + // the method bodies. + origType *Type } func (*Type) CanBeAnSSAAux() {} const ( typeNotInHeap = 1 << iota // type cannot be heap allocated - typeBroke // broken type definition typeNoalg // suppress hash and eq algorithm generation typeDeferwidth // width computation has been deferred and type is on deferredTypeStack typeRecur typeHasTParam // there is a typeparam somewhere in the type (generic function or type) + typeIsShape // represents a set of closely related types, for generics + typeHasShape // there is a shape somewhere in the type ) func (t *Type) NotInHeap() bool { return t.flags&typeNotInHeap != 0 } -func (t *Type) Broke() bool { return t.flags&typeBroke != 0 } func (t *Type) Noalg() bool { return t.flags&typeNoalg != 0 } func (t *Type) Deferwidth() bool { return t.flags&typeDeferwidth != 0 } func (t *Type) Recur() bool { return t.flags&typeRecur != 0 } func (t *Type) HasTParam() bool { return t.flags&typeHasTParam != 0 } +func (t *Type) IsShape() bool { return t.flags&typeIsShape != 0 } +func (t *Type) HasShape() bool { return t.flags&typeHasShape != 0 } func (t *Type) SetNotInHeap(b bool) { t.flags.set(typeNotInHeap, b) } -func (t *Type) SetBroke(b bool) { t.flags.set(typeBroke, b) } func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) } func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) } func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) } -func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b) } + +// Generic types should never have alg functions. +func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b); t.flags.set(typeNoalg, b) } + +// Should always do SetHasShape(true) when doing SetIsShape(true). +func (t *Type) SetIsShape(b bool) { t.flags.set(typeIsShape, b) } +func (t *Type) SetHasShape(b bool) { t.flags.set(typeHasShape, b) } // Kind returns the kind of type t. func (t *Type) Kind() Kind { return t.kind } // Sym returns the name of type t. -func (t *Type) Sym() *Sym { return t.sym } -func (t *Type) SetSym(sym *Sym) { t.sym = sym } +func (t *Type) Sym() *Sym { + if t.obj != nil { + return t.obj.Sym() + } + return nil +} + +// OrigType returns the original generic type that t is an +// instantiation of, if any. +func (t *Type) OrigType() *Type { return t.origType } +func (t *Type) SetOrigType(orig *Type) { t.origType = orig } // Underlying returns the underlying type of type t. func (t *Type) Underlying() *Type { return t.underlying } -// SetNod associates t with syntax node n. -func (t *Type) SetNod(n Object) { - // t.nod can be non-nil already - // in the case of shared *Types, like []byte or interface{}. - if t.nod == nil { - t.nod = n - } -} - // Pos returns a position associated with t, if any. // This should only be used for diagnostics. func (t *Type) Pos() src.XPos { - if t.nod != nil { - return t.nod.Pos() + if t.obj != nil { + return t.obj.Pos() } return src.NoXPos } @@ -255,9 +273,6 @@ func (t *Type) SetRParams(rparams []*Type) { base.Fatalf("Setting nil or zero-length rparams") } t.rparams = &rparams - if t.HasTParam() { - return - } // HasTParam should be set if any rparam is or has a type param. This is // to handle the case of a generic type which doesn't reference any of its // type params (e.g. most commonly, an empty struct). @@ -266,9 +281,33 @@ func (t *Type) SetRParams(rparams []*Type) { t.SetHasTParam(true) break } + if rparam.HasShape() { + t.SetHasShape(true) + break + } } } +// IsBaseGeneric returns true if t is a generic type (not reinstantiated with +// another type params or fully instantiated. +func (t *Type) IsBaseGeneric() bool { + return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") < 0 +} + +// IsInstantiatedGeneric returns t if t ia generic type that has been +// reinstantiated with new typeparams (i.e. is not fully instantiated). +func (t *Type) IsInstantiatedGeneric() bool { + return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") >= 0 && + t.HasTParam() +} + +// IsFullyInstantiated reports whether t is a fully instantiated generic type; i.e. an +// instantiated generic type where all type arguments are non-generic or fully +// instantiated generic types. +func (t *Type) IsFullyInstantiated() bool { + return len(t.RParams()) > 0 && !t.HasTParam() +} + // NoPkg is a nil *Pkg value for clarity. // It's intended for use when constructing types that aren't exported // and thus don't need to be associated with any package. @@ -283,11 +322,11 @@ var NoPkg *Pkg = nil func (t *Type) Pkg() *Pkg { switch t.kind { case TFUNC: - return t.Extra.(*Func).pkg + return t.extra.(*Func).pkg case TSTRUCT: - return t.Extra.(*Struct).pkg + return t.extra.(*Struct).pkg case TINTER: - return t.Extra.(*Interface).pkg + return t.extra.(*Interface).pkg default: base.Fatalf("Pkg: unexpected kind: %v", t) return nil @@ -307,7 +346,7 @@ type Map struct { // MapType returns t's extra map-specific fields. func (t *Type) MapType() *Map { t.wantEtype(TMAP) - return t.Extra.(*Map) + return t.extra.(*Map) } // Forward contains Type fields specific to forward types. @@ -319,7 +358,7 @@ type Forward struct { // ForwardType returns t's extra forward-type-specific fields. func (t *Type) ForwardType() *Forward { t.wantEtype(TFORW) - return t.Extra.(*Forward) + return t.extra.(*Forward) } // Func contains Type fields specific to func types. @@ -340,7 +379,7 @@ type Func struct { // FuncType returns t's extra func-specific fields. func (t *Type) FuncType() *Func { t.wantEtype(TFUNC) - return t.Extra.(*Func) + return t.extra.(*Func) } // StructType contains Type fields specific to struct types. @@ -369,12 +408,25 @@ const ( // StructType returns t's extra struct-specific fields. func (t *Type) StructType() *Struct { t.wantEtype(TSTRUCT) - return t.Extra.(*Struct) + return t.extra.(*Struct) } // Interface contains Type fields specific to interface types. type Interface struct { - pkg *Pkg + pkg *Pkg + implicit bool +} + +// Typeparam contains Type fields specific to typeparam types. +type Typeparam struct { + index int // type parameter index in source order, starting at 0 + bound *Type +} + +// Union contains Type fields specific to union types. +type Union struct { + terms []*Type + tildes []bool // whether terms[i] is of form ~T } // Ptr contains Type fields specific to pointer types. @@ -401,7 +453,7 @@ type Chan struct { // ChanType returns t's extra channel-specific fields. func (t *Type) ChanType() *Chan { t.wantEtype(TCHAN) - return t.Extra.(*Chan) + return t.extra.(*Chan) } type Tuple struct { @@ -428,21 +480,25 @@ type Slice struct { // A Field is a (Sym, Type) pairing along with some other information, and, // depending on the context, is used to represent: -// - a field in a struct -// - a method in an interface or associated with a named type -// - a function parameter +// - a field in a struct +// - a method in an interface or associated with a named type +// - a function parameter type Field struct { flags bitset8 Embedded uint8 // embedded field - Pos src.XPos + Pos src.XPos + + // Name of field/method/parameter. Can be nil for interface fields embedded + // in interfaces and unnamed parameters. Sym *Sym Type *Type // field type Note string // literal string annotation - // For fields that represent function parameters, Nname points - // to the associated ONAME Node. + // For fields that represent function parameters, Nname points to the + // associated ONAME Node. For fields that represent methods, Nname points to + // the function name node. Nname Object // Offset in bytes of this field or method within its enclosing struct @@ -453,21 +509,18 @@ type Field struct { const ( fieldIsDDD = 1 << iota // field is ... argument - fieldBroke // broken field definition fieldNointerface ) func (f *Field) IsDDD() bool { return f.flags&fieldIsDDD != 0 } -func (f *Field) Broke() bool { return f.flags&fieldBroke != 0 } func (f *Field) Nointerface() bool { return f.flags&fieldNointerface != 0 } func (f *Field) SetIsDDD(b bool) { f.flags.set(fieldIsDDD, b) } -func (f *Field) SetBroke(b bool) { f.flags.set(fieldBroke, b) } func (f *Field) SetNointerface(b bool) { f.flags.set(fieldNointerface, b) } // End returns the offset of the first byte immediately after this field. func (f *Field) End() int64 { - return f.Offset + f.Type.Width + return f.Offset + f.Type.width } // IsMethod reports whether f represents a method rather than a struct field. @@ -527,38 +580,40 @@ func (f *Fields) Append(s ...*Field) { } // New returns a new Type of the specified kind. -func New(et Kind) *Type { +func newType(et Kind) *Type { t := &Type{ kind: et, - Width: BADWIDTH, + width: BADWIDTH, } t.underlying = t // TODO(josharian): lazily initialize some of these? switch t.kind { case TMAP: - t.Extra = new(Map) + t.extra = new(Map) case TFORW: - t.Extra = new(Forward) + t.extra = new(Forward) case TFUNC: - t.Extra = new(Func) + t.extra = new(Func) case TSTRUCT: - t.Extra = new(Struct) + t.extra = new(Struct) case TINTER: - t.Extra = new(Interface) + t.extra = new(Interface) case TPTR: - t.Extra = Ptr{} + t.extra = Ptr{} case TCHANARGS: - t.Extra = ChanArgs{} + t.extra = ChanArgs{} case TFUNCARGS: - t.Extra = FuncArgs{} + t.extra = FuncArgs{} case TCHAN: - t.Extra = new(Chan) + t.extra = new(Chan) case TTUPLE: - t.Extra = new(Tuple) + t.extra = new(Tuple) case TRESULTS: - t.Extra = new(Results) + t.extra = new(Results) case TTYPEPARAM: - t.Extra = new(Interface) + t.extra = new(Typeparam) + case TUNION: + t.extra = new(Union) } return t } @@ -568,12 +623,14 @@ func NewArray(elem *Type, bound int64) *Type { if bound < 0 { base.Fatalf("NewArray: invalid bound %v", bound) } - t := New(TARRAY) - t.Extra = &Array{Elem: elem, Bound: bound} - t.SetNotInHeap(elem.NotInHeap()) + t := newType(TARRAY) + t.extra = &Array{Elem: elem, Bound: bound} if elem.HasTParam() { t.SetHasTParam(true) } + if elem.HasShape() { + t.SetHasShape(true) + } return t } @@ -583,43 +640,55 @@ func NewSlice(elem *Type) *Type { if t.Elem() != elem { base.Fatalf("elem mismatch") } + if elem.HasTParam() != t.HasTParam() || elem.HasShape() != t.HasShape() { + base.Fatalf("Incorrect HasTParam/HasShape flag for cached slice type") + } return t } - t := New(TSLICE) - t.Extra = Slice{Elem: elem} + t := newType(TSLICE) + t.extra = Slice{Elem: elem} elem.cache.slice = t if elem.HasTParam() { t.SetHasTParam(true) } + if elem.HasShape() { + t.SetHasShape(true) + } return t } // NewChan returns a new chan Type with direction dir. func NewChan(elem *Type, dir ChanDir) *Type { - t := New(TCHAN) + t := newType(TCHAN) ct := t.ChanType() ct.Elem = elem ct.Dir = dir if elem.HasTParam() { t.SetHasTParam(true) } + if elem.HasShape() { + t.SetHasShape(true) + } return t } func NewTuple(t1, t2 *Type) *Type { - t := New(TTUPLE) - t.Extra.(*Tuple).first = t1 - t.Extra.(*Tuple).second = t2 + t := newType(TTUPLE) + t.extra.(*Tuple).first = t1 + t.extra.(*Tuple).second = t2 if t1.HasTParam() || t2.HasTParam() { t.SetHasTParam(true) } + if t1.HasShape() || t2.HasShape() { + t.SetHasShape(true) + } return t } func newResults(types []*Type) *Type { - t := New(TRESULTS) - t.Extra.(*Results).Types = types + t := newType(TRESULTS) + t.extra.(*Results).Types = types return t } @@ -631,20 +700,23 @@ func NewResults(types []*Type) *Type { } func newSSA(name string) *Type { - t := New(TSSA) - t.Extra = name + t := newType(TSSA) + t.extra = name return t } // NewMap returns a new map Type with key type k and element (aka value) type v. func NewMap(k, v *Type) *Type { - t := New(TMAP) + t := newType(TMAP) mt := t.MapType() mt.Key = k mt.Elem = v if k.HasTParam() || v.HasTParam() { t.SetHasTParam(true) } + if k.HasShape() || v.HasShape() { + t.SetHasShape(true) + } return t } @@ -663,39 +735,39 @@ func NewPtr(elem *Type) *Type { if t.Elem() != elem { base.Fatalf("NewPtr: elem mismatch") } - if elem.HasTParam() { - // Extra check when reusing the cache, since the elem - // might have still been undetermined (i.e. a TFORW type) - // when this entry was cached. - t.SetHasTParam(true) + if elem.HasTParam() != t.HasTParam() || elem.HasShape() != t.HasShape() { + base.Fatalf("Incorrect HasTParam/HasShape flag for cached pointer type") } return t } - t := New(TPTR) - t.Extra = Ptr{Elem: elem} - t.Width = int64(PtrSize) - t.Align = uint8(PtrSize) + t := newType(TPTR) + t.extra = Ptr{Elem: elem} + t.width = int64(PtrSize) + t.align = uint8(PtrSize) if NewPtrCacheEnabled { elem.cache.ptr = t } if elem.HasTParam() { t.SetHasTParam(true) } + if elem.HasShape() { + t.SetHasShape(true) + } return t } // NewChanArgs returns a new TCHANARGS type for channel type c. func NewChanArgs(c *Type) *Type { - t := New(TCHANARGS) - t.Extra = ChanArgs{T: c} + t := newType(TCHANARGS) + t.extra = ChanArgs{T: c} return t } // NewFuncArgs returns a new TFUNCARGS type for func type f. func NewFuncArgs(f *Type) *Type { - t := New(TFUNCARGS) - t.Extra = FuncArgs{T: f} + t := newType(TFUNCARGS) + t.extra = FuncArgs{T: f} return t } @@ -707,7 +779,7 @@ func NewField(pos src.XPos, sym *Sym, typ *Type) *Field { Offset: BADWIDTH, } if typ == nil { - f.SetBroke(true) + base.Fatalf("typ is nil") } return f } @@ -734,28 +806,28 @@ func SubstAny(t *Type, types *[]*Type) *Type { elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra = Ptr{Elem: elem} + t.extra = Ptr{Elem: elem} } case TARRAY: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra.(*Array).Elem = elem + t.extra.(*Array).Elem = elem } case TSLICE: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra = Slice{Elem: elem} + t.extra = Slice{Elem: elem} } case TCHAN: elem := SubstAny(t.Elem(), types) if elem != t.Elem() { t = t.copy() - t.Extra.(*Chan).Elem = elem + t.extra.(*Chan).Elem = elem } case TMAP: @@ -763,8 +835,8 @@ func SubstAny(t *Type, types *[]*Type) *Type { elem := SubstAny(t.Elem(), types) if key != t.Key() || elem != t.Elem() { t = t.copy() - t.Extra.(*Map).Key = key - t.Extra.(*Map).Elem = elem + t.extra.(*Map).Key = key + t.extra.(*Map).Elem = elem } case TFUNC: @@ -805,26 +877,28 @@ func (t *Type) copy() *Type { // copy any *T Extra fields, to avoid aliasing switch t.kind { case TMAP: - x := *t.Extra.(*Map) - nt.Extra = &x + x := *t.extra.(*Map) + nt.extra = &x case TFORW: - x := *t.Extra.(*Forward) - nt.Extra = &x + x := *t.extra.(*Forward) + nt.extra = &x case TFUNC: - x := *t.Extra.(*Func) - nt.Extra = &x + x := *t.extra.(*Func) + nt.extra = &x case TSTRUCT: - x := *t.Extra.(*Struct) - nt.Extra = &x + x := *t.extra.(*Struct) + nt.extra = &x case TINTER: - x := *t.Extra.(*Interface) - nt.Extra = &x + x := *t.extra.(*Interface) + nt.extra = &x case TCHAN: - x := *t.Extra.(*Chan) - nt.Extra = &x + x := *t.extra.(*Chan) + nt.extra = &x case TARRAY: - x := *t.Extra.(*Array) - nt.Extra = &x + x := *t.extra.(*Array) + nt.extra = &x + case TTYPEPARAM: + base.Fatalf("typeparam types cannot be copied") case TTUPLE, TSSA, TRESULTS: base.Fatalf("ssa types cannot be copied") } @@ -891,7 +965,7 @@ var ParamsResults = [2]func(*Type) *Type{ // Key returns the key type of map type t. func (t *Type) Key() *Type { t.wantEtype(TMAP) - return t.Extra.(*Map).Key + return t.extra.(*Map).Key } // Elem returns the type of elements of t. @@ -899,15 +973,15 @@ func (t *Type) Key() *Type { func (t *Type) Elem() *Type { switch t.kind { case TPTR: - return t.Extra.(Ptr).Elem + return t.extra.(Ptr).Elem case TARRAY: - return t.Extra.(*Array).Elem + return t.extra.(*Array).Elem case TSLICE: - return t.Extra.(Slice).Elem + return t.extra.(Slice).Elem case TCHAN: - return t.Extra.(*Chan).Elem + return t.extra.(*Chan).Elem case TMAP: - return t.Extra.(*Map).Elem + return t.extra.(*Map).Elem } base.Fatalf("Type.Elem %s", t.kind) return nil @@ -916,18 +990,18 @@ func (t *Type) Elem() *Type { // ChanArgs returns the channel type for TCHANARGS type t. func (t *Type) ChanArgs() *Type { t.wantEtype(TCHANARGS) - return t.Extra.(ChanArgs).T + return t.extra.(ChanArgs).T } // FuncArgs returns the func type for TFUNCARGS type t. func (t *Type) FuncArgs() *Type { t.wantEtype(TFUNCARGS) - return t.Extra.(FuncArgs).T + return t.extra.(FuncArgs).T } -// IsFuncArgStruct reports whether t is a struct representing function parameters. +// IsFuncArgStruct reports whether t is a struct representing function parameters or results. func (t *Type) IsFuncArgStruct() bool { - return t.kind == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone + return t.kind == TSTRUCT && t.extra.(*Struct).Funarg != FunargNone } // Methods returns a pointer to the base methods (excluding embedding) for type t. @@ -938,7 +1012,9 @@ func (t *Type) Methods() *Fields { } // AllMethods returns a pointer to all the methods (including embedding) for type t. -// For an interface type, this is the set of methods that are typically iterated over. +// For an interface type, this is the set of methods that are typically iterated +// over. For non-interface types, AllMethods() only returns a valid result after +// CalcMethods() has been called at least once. func (t *Type) AllMethods() *Fields { if t.kind == TINTER { // Calculate the full method set of an interface type on the fly @@ -958,7 +1034,7 @@ func (t *Type) SetAllMethods(fs []*Field) { // Fields returns the fields of struct type t. func (t *Type) Fields() *Fields { t.wantEtype(TSTRUCT) - return &t.Extra.(*Struct).fields + return &t.extra.(*Struct).fields } // Field returns the i'th field of struct type t. @@ -980,21 +1056,10 @@ func (t *Type) SetFields(fields []*Field) { // Rather than try to track and invalidate those, // enforce that SetFields cannot be called once // t's width has been calculated. - if t.WidthCalculated() { + if t.widthCalculated() { base.Fatalf("SetFields of %v: width previously calculated", t) } t.wantEtype(TSTRUCT) - for _, f := range fields { - // If type T contains a field F with a go:notinheap - // type, then T must also be go:notinheap. Otherwise, - // you could heap allocate T and then get a pointer F, - // which would be a heap pointer to a go:notinheap - // type. - if f.Type != nil && f.Type.NotInHeap() { - t.SetNotInHeap(true) - break - } - } t.Fields().Set(fields) } @@ -1004,15 +1069,11 @@ func (t *Type) SetInterface(methods []*Field) { t.Methods().Set(methods) } -func (t *Type) WidthCalculated() bool { - return t.Align > 0 -} - // ArgWidth returns the total aligned argument size for a function. // It includes the receiver, parameters, and results. func (t *Type) ArgWidth() int64 { t.wantEtype(TFUNC) - return t.Extra.(*Func).Argwid + return t.extra.(*Func).Argwid } func (t *Type) Size() int64 { @@ -1023,12 +1084,12 @@ func (t *Type) Size() int64 { return 0 } CalcSize(t) - return t.Width + return t.width } func (t *Type) Alignment() int64 { CalcSize(t) - return int64(t.Align) + return int64(t.align) } func (t *Type) SimpleString() string { @@ -1036,9 +1097,10 @@ func (t *Type) SimpleString() string { } // Cmp is a comparison between values a and b. -// -1 if a < b -// 0 if a == b -// 1 if a > b +// +// -1 if a < b +// 0 if a == b +// 1 if a > b type Cmp int8 const ( @@ -1120,7 +1182,7 @@ func (t *Type) cmp(x *Type) Cmp { return cmpForNe(t.kind < x.kind) } - if t.sym != nil || x.sym != nil { + if t.obj != nil || x.obj != nil { // Special case: we keep byte and uint8 separate // for error messages. Treat them as equal. switch t.kind { @@ -1133,17 +1195,23 @@ func (t *Type) cmp(x *Type) Cmp { if (t == Types[RuneType.kind] || t == RuneType) && (x == Types[RuneType.kind] || x == RuneType) { return CMPeq } + + case TINTER: + // Make sure named any type matches any empty interface. + if t == AnyType && x.IsEmptyInterface() || x == AnyType && t.IsEmptyInterface() { + return CMPeq + } } } - if c := t.sym.cmpsym(x.sym); c != CMPeq { + if c := t.Sym().cmpsym(x.Sym()); c != CMPeq { return c } - if x.sym != nil { + if x.obj != nil { // Syms non-nil, if vargens match then equal. - if t.Vargen != x.Vargen { - return cmpForNe(t.Vargen < x.Vargen) + if t.vargen != x.vargen { + return cmpForNe(t.vargen < x.vargen) } return CMPeq } @@ -1155,8 +1223,8 @@ func (t *Type) cmp(x *Type) Cmp { return CMPeq case TSSA: - tname := t.Extra.(string) - xname := x.Extra.(string) + tname := t.extra.(string) + xname := x.extra.(string) // desire fast sorting, not pretty sorting. if len(tname) == len(xname) { if tname == xname { @@ -1173,16 +1241,16 @@ func (t *Type) cmp(x *Type) Cmp { return CMPlt case TTUPLE: - xtup := x.Extra.(*Tuple) - ttup := t.Extra.(*Tuple) + xtup := x.extra.(*Tuple) + ttup := t.extra.(*Tuple) if c := ttup.first.Compare(xtup.first); c != CMPeq { return c } return ttup.second.Compare(xtup.second) case TRESULTS: - xResults := x.Extra.(*Results) - tResults := t.Extra.(*Results) + xResults := x.extra.(*Results) + tResults := t.extra.(*Results) xl, tl := len(xResults.Types), len(tResults.Types) if tl != xl { if tl < xl { @@ -1436,6 +1504,14 @@ func (t *Type) IsInterface() bool { return t.kind == TINTER } +func (t *Type) IsUnion() bool { + return t.kind == TUNION +} + +func (t *Type) IsTypeParam() bool { + return t.kind == TTYPEPARAM +} + // IsEmptyInterface reports whether t is an empty interface type. func (t *Type) IsEmptyInterface() bool { return t.IsInterface() && t.AllMethods().Len() == 0 @@ -1461,7 +1537,7 @@ func (t *Type) PtrTo() *Type { func (t *Type) NumFields() int { if t.kind == TRESULTS { - return len(t.Extra.(*Results).Types) + return len(t.extra.(*Results).Types) } return t.Fields().Len() } @@ -1469,15 +1545,15 @@ func (t *Type) FieldType(i int) *Type { if t.kind == TTUPLE { switch i { case 0: - return t.Extra.(*Tuple).first + return t.extra.(*Tuple).first case 1: - return t.Extra.(*Tuple).second + return t.extra.(*Tuple).second default: panic("bad tuple index") } } if t.kind == TRESULTS { - return t.Extra.(*Results).Types[i] + return t.extra.(*Results).Types[i] } return t.Field(i).Type } @@ -1490,7 +1566,7 @@ func (t *Type) FieldName(i int) string { func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) - return t.Extra.(*Array).Bound + return t.extra.(*Array).Bound } type componentsIncludeBlankFields bool @@ -1529,6 +1605,7 @@ func (t *Type) NumComponents(countBlank componentsIncludeBlankFields) int64 { // SoleComponent returns the only primitive component in t, // if there is exactly one. Otherwise, it returns nil. // Components are counted as in NumComponents, including blank fields. +// Keep in sync with cmd/compile/internal/walk/convert.go:soleComponent. func (t *Type) SoleComponent() *Type { switch t.kind { case TSTRUCT: @@ -1552,15 +1629,15 @@ func (t *Type) SoleComponent() *Type { // The direction will be one of Crecv, Csend, or Cboth. func (t *Type) ChanDir() ChanDir { t.wantEtype(TCHAN) - return t.Extra.(*Chan).Dir + return t.extra.(*Chan).Dir } func (t *Type) IsMemory() bool { - if t == TypeMem || t.kind == TTUPLE && t.Extra.(*Tuple).second == TypeMem { + if t == TypeMem || t.kind == TTUPLE && t.extra.(*Tuple).second == TypeMem { return true } if t.kind == TRESULTS { - if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { + if types := t.extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem { return true } } @@ -1587,58 +1664,9 @@ func (t *Type) IsUntyped() bool { } // HasPointers reports whether t contains a heap pointer. -// Note that this function ignores pointers to go:notinheap types. +// Note that this function ignores pointers to not-in-heap types. func (t *Type) HasPointers() bool { - switch t.kind { - case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, - TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA: - return false - - case TARRAY: - if t.NumElem() == 0 { // empty array has no pointers - return false - } - return t.Elem().HasPointers() - - case TSTRUCT: - for _, t1 := range t.Fields().Slice() { - if t1.Type.HasPointers() { - return true - } - } - return false - - case TPTR, TSLICE: - return !t.Elem().NotInHeap() - - case TTUPLE: - ttup := t.Extra.(*Tuple) - return ttup.first.HasPointers() || ttup.second.HasPointers() - - case TRESULTS: - types := t.Extra.(*Results).Types - for _, et := range types { - if et.HasPointers() { - return true - } - } - return false - } - - return true -} - -// Tie returns 'T' if t is a concrete type, -// 'I' if t is an interface type, and 'E' if t is an empty interface type. -// It is used to build calls to the conv* and assert* runtime routines. -func (t *Type) Tie() byte { - if t.IsEmptyInterface() { - return 'E' - } - if t.IsInterface() { - return 'I' - } - return 'T' + return PtrDataSize(t) > 0 } var recvType *Type @@ -1646,11 +1674,15 @@ var recvType *Type // FakeRecvType returns the singleton type used for interface method receivers. func FakeRecvType() *Type { if recvType == nil { - recvType = NewPtr(New(TSTRUCT)) + recvType = NewPtr(newType(TSTRUCT)) } return recvType } +func FakeRecv() *Field { + return NewField(src.NoXPos, nil, FakeRecvType()) +} + var ( // TSSA types. HasPointers assumes these are pointer-free. TypeInvalid = newSSA("invalid") @@ -1661,28 +1693,53 @@ var ( TypeResultMem = newResults([]*Type{TypeMem}) ) +func init() { + TypeInt128.width = 16 + TypeInt128.align = 8 +} + // NewNamed returns a new named type for the given type name. obj should be an // ir.Name. The new type is incomplete (marked as TFORW kind), and the underlying // type should be set later via SetUnderlying(). References to the type are // maintained until the type is filled in, so those references can be updated when // the type is complete. func NewNamed(obj Object) *Type { - t := New(TFORW) - t.sym = obj.Sym() - t.nod = obj + t := newType(TFORW) + t.obj = obj + if obj.Sym().Pkg == ShapePkg { + t.SetIsShape(true) + t.SetHasShape(true) + } return t } // Obj returns the canonical type name node for a named type t, nil for an unnamed type. func (t *Type) Obj() Object { - if t.sym != nil { - return t.nod - } - return nil + return t.obj } -// SetUnderlying sets the underlying type. SetUnderlying automatically updates any -// types that were waiting for this type to be completed. +// typeGen tracks the number of function-scoped defined types that +// have been declared. It's used to generate unique linker symbols for +// their runtime type descriptors. +var typeGen int32 + +// SetVargen assigns a unique generation number to type t, which must +// be a defined type declared within function scope. The generation +// number is used to distinguish it from other similarly spelled +// defined types from the same package. +// +// TODO(mdempsky): Come up with a better solution. +func (t *Type) SetVargen() { + base.Assertf(t.Sym() != nil, "SetVargen on anonymous type %v", t) + base.Assertf(t.vargen == 0, "type %v already has Vargen %v", t, t.vargen) + + typeGen++ + t.vargen = typeGen +} + +// SetUnderlying sets the underlying type of an incomplete type (i.e. type whose kind +// is currently TFORW). SetUnderlying automatically updates any types that were waiting +// for this type to be completed. func (t *Type) SetUnderlying(underlying *Type) { if underlying.kind == TFORW { // This type isn't computed yet; when it is, update n. @@ -1694,20 +1751,20 @@ func (t *Type) SetUnderlying(underlying *Type) { // TODO(mdempsky): Fix Type rekinding. t.kind = underlying.kind - t.Extra = underlying.Extra - t.Width = underlying.Width - t.Align = underlying.Align + t.extra = underlying.extra + t.width = underlying.width + t.align = underlying.align t.underlying = underlying.underlying if underlying.NotInHeap() { t.SetNotInHeap(true) } - if underlying.Broke() { - t.SetBroke(true) - } if underlying.HasTParam() { t.SetHasTParam(true) } + if underlying.HasShape() { + t.SetHasShape(true) + } // spec: "The declared type does not inherit any methods bound // to the existing type, but the method set of an interface @@ -1739,18 +1796,26 @@ func fieldsHasTParam(fields []*Field) bool { return false } +func fieldsHasShape(fields []*Field) bool { + for _, f := range fields { + if f.Type != nil && f.Type.HasShape() { + return true + } + } + return false +} + // NewBasic returns a new basic type of the given kind. -func NewBasic(kind Kind, obj Object) *Type { - t := New(kind) - t.sym = obj.Sym() - t.nod = obj +func newBasic(kind Kind, obj Object) *Type { + t := newType(kind) + t.obj = obj return t } // NewInterface returns a new interface for the given methods and // embedded types. Embedded types are specified as fields with no Sym. -func NewInterface(pkg *Pkg, methods []*Field) *Type { - t := New(TINTER) +func NewInterface(pkg *Pkg, methods []*Field, implicit bool) *Type { + t := newType(TINTER) t.SetInterface(methods) for _, f := range methods { // f.Type could be nil for a broken interface declaration @@ -1758,22 +1823,98 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type { t.SetHasTParam(true) break } + if f.Type != nil && f.Type.HasShape() { + t.SetHasShape(true) + break + } } - if anyBroke(methods) { - t.SetBroke(true) - } - t.Extra.(*Interface).pkg = pkg + t.extra.(*Interface).pkg = pkg + t.extra.(*Interface).implicit = implicit return t } -// NewTypeParam returns a new type param. -func NewTypeParam(pkg *Pkg) *Type { - t := New(TTYPEPARAM) - t.Extra.(*Interface).pkg = pkg +// NewTypeParam returns a new type param with the specified sym (package and name) +// and specified index within the typeparam list. +func NewTypeParam(obj Object, index int) *Type { + t := newType(TTYPEPARAM) + t.obj = obj + t.extra.(*Typeparam).index = index t.SetHasTParam(true) return t } +// Index returns the index of the type param within its param list. +func (t *Type) Index() int { + t.wantEtype(TTYPEPARAM) + return t.extra.(*Typeparam).index +} + +// SetIndex sets the index of the type param within its param list. +func (t *Type) SetIndex(i int) { + t.wantEtype(TTYPEPARAM) + t.extra.(*Typeparam).index = i +} + +// SetBound sets the bound of a typeparam. +func (t *Type) SetBound(bound *Type) { + t.wantEtype(TTYPEPARAM) + t.extra.(*Typeparam).bound = bound +} + +// Bound returns the bound of a typeparam. +func (t *Type) Bound() *Type { + t.wantEtype(TTYPEPARAM) + return t.extra.(*Typeparam).bound +} + +// IsImplicit reports whether an interface is implicit (i.e. elided from a type +// parameter constraint). +func (t *Type) IsImplicit() bool { + t.wantEtype(TINTER) + return t.extra.(*Interface).implicit +} + +// MarkImplicit marks the interface as implicit. +func (t *Type) MarkImplicit() { + t.wantEtype(TINTER) + t.extra.(*Interface).implicit = true +} + +// NewUnion returns a new union with the specified set of terms (types). If +// tildes[i] is true, then terms[i] represents ~T, rather than just T. +func NewUnion(terms []*Type, tildes []bool) *Type { + t := newType(TUNION) + if len(terms) != len(tildes) { + base.Fatalf("Mismatched terms and tildes for NewUnion") + } + t.extra.(*Union).terms = terms + t.extra.(*Union).tildes = tildes + nt := len(terms) + for i := 0; i < nt; i++ { + if terms[i].HasTParam() { + t.SetHasTParam(true) + } + if terms[i].HasShape() { + t.SetHasShape(true) + } + } + return t +} + +// NumTerms returns the number of terms in a union type. +func (t *Type) NumTerms() int { + t.wantEtype(TUNION) + return len(t.extra.(*Union).terms) +} + +// Term returns ith term of a union type as (term, tilde). If tilde is true, term +// represents ~T, rather than just T. +func (t *Type) Term(i int) (*Type, bool) { + t.wantEtype(TUNION) + u := t.extra.(*Union) + return u.terms[i], u.tildes[i] +} + const BOGUS_FUNARG_OFFSET = -1000000000 func unzeroFieldOffsets(f []*Field) { @@ -1790,15 +1931,12 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ recvs = []*Field{recv} } - t := New(TFUNC) + t := newType(TFUNC) ft := t.FuncType() funargs := func(fields []*Field, funarg Funarg) *Type { s := NewStruct(NoPkg, fields) s.StructType().Funarg = funarg - if s.Broke() { - t.SetBroke(true) - } return s } @@ -1817,31 +1955,25 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ fieldsHasTParam(results) { t.SetHasTParam(true) } + if fieldsHasShape(recvs) || fieldsHasShape(params) || fieldsHasShape(results) { + t.SetHasShape(true) + } return t } // NewStruct returns a new struct with the given fields. func NewStruct(pkg *Pkg, fields []*Field) *Type { - t := New(TSTRUCT) + t := newType(TSTRUCT) t.SetFields(fields) - if anyBroke(fields) { - t.SetBroke(true) - } - t.Extra.(*Struct).pkg = pkg + t.extra.(*Struct).pkg = pkg if fieldsHasTParam(fields) { t.SetHasTParam(true) } - return t -} - -func anyBroke(fields []*Field) bool { - for _, f := range fields { - if f.Broke() { - return true - } + if fieldsHasShape(fields) { + t.SetHasShape(true) } - return false + return t } var ( @@ -1902,10 +2034,6 @@ func IsReflexive(t *Type) bool { // Can this type be stored directly in an interface word? // Yes, if the representation is a single pointer. func IsDirectIface(t *Type) bool { - if t.Broke() { - return false - } - switch t.Kind() { case TPTR: // Pointers to notinheap types must be stored indirectly. See issue 42076. @@ -1953,9 +2081,6 @@ func IsRuntimePkg(p *Pkg) bool { // IsReflectPkg reports whether p is package reflect. func IsReflectPkg(p *Pkg) bool { - if p == LocalPkg { - return base.Ctxt.Pkgpath == "reflect" - } return p.Path == "reflect" } @@ -2028,7 +2153,7 @@ func TypeSymLookup(name string) *Sym { } func TypeSymName(t *Type) string { - name := t.ShortString() + name := t.LinkString() // Use a separate symbol name for Noalg types for #17752. if TypeHasNoAlg(t) { name = "noalg." + name @@ -2044,3 +2169,6 @@ var ( ) var SimType [NTYPE]Kind + +// Fake package for shape types (see typecheck.Shapify()). +var ShapePkg = NewPkg("go.shape", "go.shape") diff --git a/src/cmd/compile/internal/types/type_test.go b/src/cmd/compile/internal/types/type_test.go index fe3f380b21f2d9..1fd05b3f5e8c76 100644 --- a/src/cmd/compile/internal/types/type_test.go +++ b/src/cmd/compile/internal/types/type_test.go @@ -2,26 +2,25 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types_test +package types import ( - "cmd/compile/internal/types" "testing" ) func TestSSACompare(t *testing.T) { - a := []*types.Type{ - types.TypeInvalid, - types.TypeMem, - types.TypeFlags, - types.TypeVoid, - types.TypeInt128, + a := []*Type{ + TypeInvalid, + TypeMem, + TypeFlags, + TypeVoid, + TypeInt128, } for _, x := range a { for _, y := range a { c := x.Compare(y) - if x == y && c != types.CMPeq || x != y && c == types.CMPeq { - t.Errorf("%s compare %s == %d\n", x.Extra, y.Extra, c) + if x == y && c != CMPeq || x != y && c == CMPeq { + t.Errorf("%s compare %s == %d\n", x.extra, y.extra, c) } } } diff --git a/src/cmd/compile/internal/types/universe.go b/src/cmd/compile/internal/types/universe.go new file mode 100644 index 00000000000000..473311034e4dee --- /dev/null +++ b/src/cmd/compile/internal/types/universe.go @@ -0,0 +1,154 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "cmd/compile/internal/base" + "cmd/internal/src" +) + +var basicTypes = [...]struct { + name string + etype Kind +}{ + {"int8", TINT8}, + {"int16", TINT16}, + {"int32", TINT32}, + {"int64", TINT64}, + {"uint8", TUINT8}, + {"uint16", TUINT16}, + {"uint32", TUINT32}, + {"uint64", TUINT64}, + {"float32", TFLOAT32}, + {"float64", TFLOAT64}, + {"complex64", TCOMPLEX64}, + {"complex128", TCOMPLEX128}, + {"bool", TBOOL}, + {"string", TSTRING}, +} + +var typedefs = [...]struct { + name string + etype Kind + sameas32 Kind + sameas64 Kind +}{ + {"int", TINT, TINT32, TINT64}, + {"uint", TUINT, TUINT32, TUINT64}, + {"uintptr", TUINTPTR, TUINT32, TUINT64}, +} + +func InitTypes(defTypeName func(sym *Sym, typ *Type) Object) { + if PtrSize == 0 { + base.Fatalf("InitTypes called before PtrSize was set") + } + + SlicePtrOffset = 0 + SliceLenOffset = RoundUp(SlicePtrOffset+int64(PtrSize), int64(PtrSize)) + SliceCapOffset = RoundUp(SliceLenOffset+int64(PtrSize), int64(PtrSize)) + SliceSize = RoundUp(SliceCapOffset+int64(PtrSize), int64(PtrSize)) + + // string is same as slice wo the cap + StringSize = RoundUp(SliceLenOffset+int64(PtrSize), int64(PtrSize)) + + for et := Kind(0); et < NTYPE; et++ { + SimType[et] = et + } + + Types[TANY] = newType(TANY) // note: an old placeholder type, NOT the new builtin 'any' alias for interface{} + Types[TINTER] = NewInterface(LocalPkg, nil, false) + CheckSize(Types[TINTER]) + + defBasic := func(kind Kind, pkg *Pkg, name string) *Type { + typ := newType(kind) + obj := defTypeName(pkg.Lookup(name), typ) + typ.obj = obj + if kind != TANY { + CheckSize(typ) + } + return typ + } + + for _, s := range &basicTypes { + Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name) + } + + for _, s := range &typedefs { + sameas := s.sameas32 + if PtrSize == 8 { + sameas = s.sameas64 + } + SimType[s.etype] = sameas + + Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name) + } + + // We create separate byte and rune types for better error messages + // rather than just creating type alias *Sym's for the uint8 and + // int32 Hence, (bytetype|runtype).Sym.isAlias() is false. + // TODO(gri) Should we get rid of this special case (at the cost + // of less informative error messages involving bytes and runes)? + // NOTE(rsc): No, the error message quality is important. + // (Alternatively, we could introduce an OTALIAS node representing + // type aliases, albeit at the cost of having to deal with it everywhere). + ByteType = defBasic(TUINT8, BuiltinPkg, "byte") + RuneType = defBasic(TINT32, BuiltinPkg, "rune") + + // error type + DeferCheckSize() + ErrorType = defBasic(TFORW, BuiltinPkg, "error") + ErrorType.SetUnderlying(makeErrorInterface()) + ResumeCheckSize() + + // comparable type (interface) + DeferCheckSize() + ComparableType = defBasic(TFORW, BuiltinPkg, "comparable") + ComparableType.SetUnderlying(makeComparableInterface()) + ResumeCheckSize() + + // any type (interface) + DeferCheckSize() + AnyType = defBasic(TFORW, BuiltinPkg, "any") + AnyType.SetUnderlying(NewInterface(BuiltinPkg, []*Field{}, false)) + ResumeCheckSize() + + Types[TUNSAFEPTR] = defBasic(TUNSAFEPTR, UnsafePkg, "Pointer") + + Types[TBLANK] = newType(TBLANK) + Types[TNIL] = newType(TNIL) + + // simple aliases + SimType[TMAP] = TPTR + SimType[TCHAN] = TPTR + SimType[TFUNC] = TPTR + SimType[TUNSAFEPTR] = TPTR + + for et := TINT8; et <= TUINT64; et++ { + IsInt[et] = true + } + IsInt[TINT] = true + IsInt[TUINT] = true + IsInt[TUINTPTR] = true + + IsFloat[TFLOAT32] = true + IsFloat[TFLOAT64] = true + + IsComplex[TCOMPLEX64] = true + IsComplex[TCOMPLEX128] = true +} + +func makeErrorInterface() *Type { + sig := NewSignature(NoPkg, FakeRecv(), nil, nil, []*Field{ + NewField(src.NoXPos, nil, Types[TSTRING]), + }) + method := NewField(src.NoXPos, LocalPkg.Lookup("Error"), sig) + return NewInterface(NoPkg, []*Field{method}, false) +} + +// makeComparableInterface makes the predefined "comparable" interface in the +// built-in package. It has a unique name, but no methods. +func makeComparableInterface() *Type { + return NewInterface(NoPkg, nil, false) +} diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index 2939dcc0bdf273..07939488380857 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -21,14 +21,13 @@ // Type inference computes the type (Type) of every expression (syntax.Expr) // and checks for compliance with the language specification. // Use Info.Types[expr].Type for the results of type inference. -// package types2 import ( - "bytes" "cmd/compile/internal/syntax" "fmt" "go/constant" + "strings" ) // An Error describes a type-checking error; it implements the error interface. @@ -55,6 +54,15 @@ func (err Error) FullError() string { return fmt.Sprintf("%s: %s", err.Pos, err.Full) } +// An ArgumentError holds an error associated with an argument index. +type ArgumentError struct { + Index int + Err error +} + +func (e *ArgumentError) Error() string { return e.Err.Error() } +func (e *ArgumentError) Unwrap() error { return e.Err } + // An Importer resolves import paths to Packages. // // CAUTION: This interface does not support the import of locally @@ -96,6 +104,10 @@ type ImporterFrom interface { // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { + // Context is the context used for resolving global identifiers. If nil, the + // type checker will initialize this field with a newly created context. + Context *Context + // GoVersion describes the accepted Go language version. The string // must follow the format "go%d.%d" (e.g. "go1.12") or ist must be // empty; an empty string indicates the latest language version. @@ -116,9 +128,8 @@ type Config struct { // Do not use casually! FakeImportC bool - // If IgnoreLabels is set, correct label use is not checked. - // TODO(gri) Consolidate label checking and remove this flag. - IgnoreLabels bool + // If IgnoreBranchErrors is set, branch/label errors are ignored. + IgnoreBranchErrors bool // If CompilerErrorMessages is set, errors are reported using // cmd/compile error strings to match $GOROOT/test errors. @@ -191,11 +202,19 @@ type Info struct { // qualified identifiers are collected in the Uses map. Types map[syntax.Expr]TypeAndValue - // Inferred maps calls of parameterized functions that use - // type inference to the inferred type arguments and signature - // of the function called. The recorded "call" expression may be - // an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]). - Inferred map[syntax.Expr]Inferred + // Instances maps identifiers denoting generic types or functions to their + // type arguments and instantiated type. + // + // For example, Instances will map the identifier for 'T' in the type + // instantiation T[int, string] to the type arguments [int, string] and + // resulting instantiated *Named type. Given a generic function + // func F[A any](A), Instances will map the identifier for 'F' in the call + // expression F(int(1)) to the inferred type arguments [int], and resulting + // instantiated *Signature. + // + // Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs + // results in an equivalent of Instances[id].Type. + Instances map[*syntax.Name]Instance // Defs maps identifiers to the objects they define (including // package names, dots "." of dot-imports, and blank "_" identifiers). @@ -244,6 +263,7 @@ type Info struct { // // *syntax.File // *syntax.FuncType + // *syntax.TypeDecl // *syntax.BlockStmt // *syntax.IfStmt // *syntax.SwitchStmt @@ -263,7 +283,6 @@ type Info struct { // TypeOf returns the type of expression e, or nil if not found. // Precondition: the Types, Uses and Defs maps are populated. -// func (info *Info) TypeOf(e syntax.Expr) Type { if t, ok := info.Types[e]; ok { return t.Type @@ -283,7 +302,6 @@ func (info *Info) TypeOf(e syntax.Expr) Type { // it defines, not the type (*TypeName) it uses. // // Precondition: the Uses and Defs maps are populated. -// func (info *Info) ObjectOf(id *syntax.Name) Object { if obj := info.Defs[id]; obj != nil { return obj @@ -352,11 +370,13 @@ func (tv TypeAndValue) HasOk() bool { return tv.mode == commaok || tv.mode == mapindex } -// Inferred reports the inferred type arguments and signature -// for a parameterized function call that uses type inference. -type Inferred struct { - Targs []Type - Sig *Signature +// Instance reports the type arguments and instantiated type for type and +// function instantiations. For type instantiations, Type will be of dynamic +// type *Named. For function instantiations, Type will be of dynamic type +// *Signature. +type Instance struct { + TypeArgs *TypeList + Type Type } // An Initializer describes a package-level variable, or a list of variables in case @@ -368,7 +388,7 @@ type Initializer struct { } func (init *Initializer) String() string { - var buf bytes.Buffer + var buf strings.Builder for i, lhs := range init.Lhs { if i > 0 { buf.WriteString(", ") @@ -397,38 +417,67 @@ func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Packa } // AssertableTo reports whether a value of type V can be asserted to have type T. +// +// The behavior of AssertableTo is unspecified in three cases: +// - if T is Typ[Invalid] +// - if V is a generalized interface; i.e., an interface that may only be used +// as a type constraint in Go code +// - if T is an uninstantiated generic type func AssertableTo(V *Interface, T Type) bool { - m, _ := (*Checker)(nil).assertableTo(V, T) - return m == nil + // Checker.newAssertableTo suppresses errors for invalid types, so we need special + // handling here. + if T.Underlying() == Typ[Invalid] { + return false + } + return (*Checker)(nil).newAssertableTo(V, T) } -// AssignableTo reports whether a value of type V is assignable to a variable of type T. +// AssignableTo reports whether a value of type V is assignable to a variable +// of type T. +// +// The behavior of AssignableTo is unspecified if V or T is Typ[Invalid] or an +// uninstantiated generic type. func AssignableTo(V, T Type) bool { x := operand{mode: value, typ: V} ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x return ok } -// ConvertibleTo reports whether a value of type V is convertible to a value of type T. +// ConvertibleTo reports whether a value of type V is convertible to a value of +// type T. +// +// The behavior of ConvertibleTo is unspecified if V or T is Typ[Invalid] or an +// uninstantiated generic type. func ConvertibleTo(V, T Type) bool { x := operand{mode: value, typ: V} - return x.convertibleTo(nil, T) // check not needed for non-constant x + return x.convertibleTo(nil, T, nil) // check not needed for non-constant x } // Implements reports whether type V implements interface T. +// +// The behavior of Implements is unspecified if V is Typ[Invalid] or an uninstantiated +// generic type. func Implements(V Type, T *Interface) bool { - f, _ := MissingMethod(V, T, true) - return f == nil + if T.Empty() { + // All types (even Typ[Invalid]) implement the empty interface. + return true + } + // Checker.implements suppresses errors for invalid types, so we need special + // handling here. + if V.Underlying() == Typ[Invalid] { + return false + } + return (*Checker)(nil).implements(V, T, nil) } // Identical reports whether x and y are identical types. // Receivers of Signature types are ignored. func Identical(x, y Type) bool { - return (*Checker)(nil).identical(x, y) + return identical(x, y, true, nil) } // IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored. // Receivers of Signature types are ignored. func IdenticalIgnoreTags(x, y Type) bool { - return (*Checker)(nil).identicalIgnoreTags(x, y) + return identical(x, y, false, nil) } diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 873390c1e9280d..9a3e76a07dc1ff 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -5,37 +5,27 @@ package types2_test import ( - "bytes" "cmd/compile/internal/syntax" + "errors" "fmt" "internal/testenv" "reflect" "regexp" + "sort" "strings" "testing" . "cmd/compile/internal/types2" ) -func unimplemented() { - panic("unimplemented") -} - -// genericPkg is a source prefix for packages that contain generic code. -const genericPkg = "package generic_" - // brokenPkg is a source prefix for packages that are not expected to parse // or type-check cleanly. They are always parsed assuming that they contain // generic code. const brokenPkg = "package broken_" func parseSrc(path, src string) (*syntax.File, error) { - var mode syntax.Mode - if strings.HasPrefix(src, genericPkg) || strings.HasPrefix(src, brokenPkg) { - mode = syntax.AllowGenerics - } errh := func(error) {} // dummy error handler so that parsing continues in presence of errors - return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, mode) + return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, 0) } func pkgFor(path, source string, info *Info) (*Package, error) { @@ -47,7 +37,7 @@ func pkgFor(path, source string, info *Info) (*Package, error) { return conf.Check(f.PkgName.Value, []*syntax.File{f}, info) } -func mustTypecheck(t *testing.T, path, source string, info *Info) string { +func mustTypecheck(t testing.TB, path, source string, info *Info) string { pkg, err := pkgFor(path, source, info) if err != nil { name := path @@ -119,7 +109,6 @@ func TestValuesInfo(t *testing.T) { {`package c5d; var _ = string(65)`, `65`, `untyped int`, `65`}, {`package c5e; var _ = string('A')`, `'A'`, `untyped rune`, `65`}, {`package c5f; type T string; var _ = T('A')`, `'A'`, `untyped rune`, `65`}, - {`package c5g; var s uint; var _ = string(1 << s)`, `1 << s`, `untyped int`, ``}, {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`}, {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`}, @@ -149,6 +138,7 @@ func TestValuesInfo(t *testing.T) { {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`}, {`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341 + {`package g1; var(j int32; s int; n = 1.0< 0 { @@ -1166,8 +1366,6 @@ func (m testImporter) Import(path string) (*Package, error) { } func TestSelection(t *testing.T) { - t.Skip("requires fixes around source positions") - selections := make(map[*syntax.SelectorExpr]*Selection) imports := make(testImporter) @@ -1211,15 +1409,23 @@ type C struct { c int } +type G[P any] struct { + p P +} + +func (G[P]) m(P) {} + +var Inst G[int] + func (C) g() func (*C) h() func main() { // qualified identifiers var _ lib.T - _ = lib.C - _ = lib.F - _ = lib.V + _ = lib.C + _ = lib.F + _ = lib.V _ = lib.T.M // fields @@ -1235,25 +1441,30 @@ func main() { _ = A{}.c _ = new(A).c + _ = Inst.p + _ = G[string]{}.p + // methods - _ = A{}.f - _ = new(A).f - _ = A{}.g - _ = new(A).g - _ = new(A).h + _ = A{}.f + _ = new(A).f + _ = A{}.g + _ = new(A).g + _ = new(A).h - _ = B{}.f - _ = new(B).f + _ = B{}.f + _ = new(B).f - _ = C{}.g - _ = new(C).g - _ = new(C).h + _ = C{}.g + _ = new(C).g + _ = new(C).h + _ = Inst.m // method expressions - _ = A.f - _ = (*A).f - _ = B.f - _ = (*B).f + _ = A.f + _ = (*A).f + _ = B.f + _ = (*B).f + _ = G[string].m }` wantOut := map[string][2]string{ @@ -1267,6 +1478,7 @@ func main() { "new(A).b": {"field (*main.A) b int", "->[0 0]"}, "A{}.c": {"field (main.A) c int", ".[1 0]"}, "new(A).c": {"field (*main.A) c int", "->[1 0]"}, + "Inst.p": {"field (main.G[int]) p int", ".[0]"}, "A{}.f": {"method (main.A) f(int)", "->[0 0]"}, "new(A).f": {"method (*main.A) f(int)", "->[0 0]"}, @@ -1278,11 +1490,14 @@ func main() { "C{}.g": {"method (main.C) g()", ".[0]"}, "new(C).g": {"method (*main.C) g()", "->[0]"}, "new(C).h": {"method (*main.C) h()", "->[1]"}, // TODO(gri) should this report .[1] ? - - "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"}, - "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"}, - "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"}, - "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"}, + "Inst.m": {"method (main.G[int]) m(int)", ".[0]"}, + + "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"}, + "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"}, + "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"}, + "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"}, + "G[string].m": {"method expr (main.G[string]) m(main.G[string], string)", ".[0]"}, + "G[string]{}.p": {"field (main.G[string]) p string", ".[0]"}, } makePkg("lib", libSrc) @@ -1291,11 +1506,9 @@ func main() { for e, sel := range selections { _ = sel.String() // assertion: must not panic - unimplemented() - _ = e - // start := fset.Position(e.Pos()).Offset - // end := fset.Position(e.End()).Offset - // syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib) + start := indexFor(mainSrc, syntax.StartPos(e)) + end := indexFor(mainSrc, syntax.EndPos(e)) + segment := mainSrc[start:end] // (all SelectorExprs are in main, not lib) direct := "." if sel.Indirect() { @@ -1305,13 +1518,11 @@ func main() { sel.String(), fmt.Sprintf("%s%v", direct, sel.Index()), } - unimplemented() - _ = got - // want := wantOut[syntax] - // if want != got { - // t.Errorf("%s: got %q; want %q", syntax, got, want) - // } - // delete(wantOut, syntax) + want := wantOut[segment] + if want != got { + t.Errorf("%s: got %q; want %q", segment, got, want) + } + delete(wantOut, segment) // We must explicitly assert properties of the // Signature's receiver since it doesn't participate @@ -1321,19 +1532,31 @@ func main() { got := sig.Recv().Type() want := sel.Recv() if !Identical(got, want) { - unimplemented() - // t.Errorf("%s: Recv() = %s, want %s", syntax, got, want) + t.Errorf("%s: Recv() = %s, want %s", segment, got, want) } } else if sig != nil && sig.Recv() != nil { t.Errorf("%s: signature has receiver %s", sig, sig.Recv().Type()) } } // Assert that all wantOut entries were used exactly once. - for syntax := range wantOut { - t.Errorf("no syntax.Selection found with syntax %q", syntax) + for segment := range wantOut { + t.Errorf("no syntax.Selection found with syntax %q", segment) } } +// indexFor returns the index into s corresponding to the position pos. +func indexFor(s string, pos syntax.Pos) int { + i, line := 0, 1 // string index and corresponding line + target := int(pos.Line()) + for line < target && i < len(s) { + if s[i] == '\n' { + line++ + } + i++ + } + return i + int(pos.Col()-1) // columns are 1-based +} + func TestIssue8518(t *testing.T) { imports := make(testImporter) conf := Config{ @@ -1367,6 +1590,18 @@ var _ = a.C2 makePkg("main", mainSrc) // don't crash when type-checking this package } +func TestLookupFieldOrMethodOnNil(t *testing.T) { + // LookupFieldOrMethod on a nil type is expected to produce a run-time panic. + defer func() { + const want = "LookupFieldOrMethod on nil type" + p := recover() + if s, ok := p.(string); !ok || s != want { + t.Fatalf("got %v, want %s", p, want) + } + }() + LookupFieldOrMethod(nil, false, nil, "") +} + func TestLookupFieldOrMethod(t *testing.T) { // Test cases assume a lookup of the form a.f or x.f, where a stands for an // addressable value, and x for a non-addressable value (even though a variable @@ -1382,19 +1617,41 @@ func TestLookupFieldOrMethod(t *testing.T) { {"var x T; type T struct{ f int }", true, []int{0}, false}, {"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false}, + // field lookups on a generic type + {"var x T[int]; type T[P any] struct{}", false, nil, false}, + {"var x T[int]; type T[P any] struct{ f P }", true, []int{0}, false}, + {"var x T[int]; type T[P any] struct{ a, b, f, c P }", true, []int{2}, false}, + // method lookups {"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false}, {"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true}, {"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false}, {"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false? + // method lookups on a generic type + {"var a T[int]; type T[P any] struct{}; func (T[P]) f() {}", true, []int{0}, false}, + {"var a *T[int]; type T[P any] struct{}; func (T[P]) f() {}", true, []int{0}, true}, + {"var a T[int]; type T[P any] struct{}; func (*T[P]) f() {}", true, []int{0}, false}, + {"var a *T[int]; type T[P any] struct{}; func (*T[P]) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false? + // collisions {"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false}, {"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false}, + // collisions on a generic type + {"type ( E1[P any] struct{ f P }; E2[P any] struct{ f P }; x struct{ E1[int]; *E2[int] })", false, []int{1, 0}, false}, + {"type ( E1[P any] struct{ f P }; E2[P any] struct{}; x struct{ E1[int]; *E2[int] }); func (E2[P]) f() {}", false, []int{1, 0}, false}, + // outside methodset // (*T).f method exists, but value of type T is not addressable {"var x T; type T struct{}; func (*T) f() {}", false, nil, true}, + + // outside method set of a generic type + {"var x T[int]; type T[P any] struct{}; func (*T[P]) f() {}", false, nil, true}, + + // recursive generic types; see golang/go#52715 + {"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (N[P]) f() {}", true, []int{0, 0}, true}, + {"var a T[int]; type ( T[P any] struct { *N[P] }; N[P any] struct { *T[P] } ); func (T[P]) f() {}", true, []int{0}, false}, } for _, test := range tests { @@ -1429,6 +1686,37 @@ func TestLookupFieldOrMethod(t *testing.T) { } } +// Test for golang/go#52715 +func TestLookupFieldOrMethod_RecursiveGeneric(t *testing.T) { + const src = ` +package pkg + +type Tree[T any] struct { + *Node[T] +} + +func (*Tree[R]) N(r R) R { return r } + +type Node[T any] struct { + *Tree[T] +} + +type Instance = *Tree[int] +` + + f, err := parseSrc("foo.go", src) + if err != nil { + panic(err) + } + pkg := NewPackage("pkg", f.PkgName.Value) + if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil { + panic(err) + } + + T := pkg.Scope().Lookup("Instance").Type() + _, _, _ = LookupFieldOrMethod(T, false, pkg, "M") // verify that LookupFieldOrMethod terminates +} + func sameSlice(a, b []int) bool { if len(a) != len(b) { return false @@ -1481,7 +1769,7 @@ func F(){ var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F var a []int - for i, x := range /*i=undef*/ /*x=var:16*/ a /*i=var:20*/ /*x=var:20*/ { _ = i; _ = x } + for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x } var i interface{} switch y := i.(type) { /*y=undef*/ @@ -1568,6 +1856,14 @@ func F(){ } } +var nopos syntax.Pos + +// newDefined creates a new defined type named T with the given underlying type. +func newDefined(underlying Type) *Named { + tname := NewTypeName(nopos, nil, "T", nil) + return NewNamed(tname, underlying, nil) +} + func TestConvertibleTo(t *testing.T) { for _, test := range []struct { v, t Type @@ -1575,15 +1871,16 @@ func TestConvertibleTo(t *testing.T) { }{ {Typ[Int], Typ[Int], true}, {Typ[Int], Typ[Float32], true}, + {Typ[Int], Typ[String], true}, {newDefined(Typ[Int]), Typ[Int], true}, {newDefined(new(Struct)), new(Struct), true}, {newDefined(Typ[Int]), new(Struct), false}, {Typ[UntypedInt], Typ[Int], true}, + {NewSlice(Typ[Int]), NewArray(Typ[Int], 10), true}, + {NewSlice(Typ[Int]), NewArray(Typ[Uint], 10), false}, {NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Int], 10)), true}, - {NewSlice(Typ[Int]), NewArray(Typ[Int], 10), false}, {NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Uint], 10)), false}, - // Untyped string values are not permitted by the spec, so the below - // behavior is undefined. + // Untyped string values are not permitted by the spec, so the behavior below is undefined. {Typ[UntypedString], Typ[String], true}, } { if got := ConvertibleTo(test.v, test.t); got != test.want { @@ -1615,6 +1912,56 @@ func TestAssignableTo(t *testing.T) { } } +func TestIdentical(t *testing.T) { + // For each test, we compare the types of objects X and Y in the source. + tests := []struct { + src string + want bool + }{ + // Basic types. + {"var X int; var Y int", true}, + {"var X int; var Y string", false}, + + // TODO: add more tests for complex types. + + // Named types. + {"type X int; type Y int", false}, + + // Aliases. + {"type X = int; type Y = int", true}, + + // Functions. + {`func X(int) string { return "" }; func Y(int) string { return "" }`, true}, + {`func X() string { return "" }; func Y(int) string { return "" }`, false}, + {`func X(int) string { return "" }; func Y(int) {}`, false}, + + // Generic functions. Type parameters should be considered identical modulo + // renaming. See also issue #49722. + {`func X[P ~int](){}; func Y[Q ~int]() {}`, true}, + {`func X[P1 any, P2 ~*P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, true}, + {`func X[P1 any, P2 ~[]P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, false}, + {`func X[P ~int](P){}; func Y[Q ~int](Q) {}`, true}, + {`func X[P ~string](P){}; func Y[Q ~int](Q) {}`, false}, + {`func X[P ~int]([]P){}; func Y[Q ~int]([]Q) {}`, true}, + } + + for _, test := range tests { + pkg, err := pkgFor("test", "package p;"+test.src, nil) + if err != nil { + t.Errorf("%s: incorrect test case: %s", test.src, err) + continue + } + X := pkg.Scope().Lookup("X") + Y := pkg.Scope().Lookup("Y") + if X == nil || Y == nil { + t.Fatal("test must declare both X and Y") + } + if got := Identical(X.Type(), Y.Type()); got != test.want { + t.Errorf("Identical(%s, %s) = %t, want %t", X.Type(), Y.Type(), got, test.want) + } + } +} + func TestIdentical_issue15173(t *testing.T) { // Identical should allow nil arguments and be symmetric. for _, test := range []struct { @@ -1632,6 +1979,48 @@ func TestIdentical_issue15173(t *testing.T) { } } +func TestIdenticalUnions(t *testing.T) { + tname := NewTypeName(nopos, nil, "myInt", nil) + myInt := NewNamed(tname, Typ[Int], nil) + tmap := map[string]*Term{ + "int": NewTerm(false, Typ[Int]), + "~int": NewTerm(true, Typ[Int]), + "string": NewTerm(false, Typ[String]), + "~string": NewTerm(true, Typ[String]), + "myInt": NewTerm(false, myInt), + } + makeUnion := func(s string) *Union { + parts := strings.Split(s, "|") + var terms []*Term + for _, p := range parts { + term := tmap[p] + if term == nil { + t.Fatalf("missing term %q", p) + } + terms = append(terms, term) + } + return NewUnion(terms) + } + for _, test := range []struct { + x, y string + want bool + }{ + // These tests are just sanity checks. The tests for type sets and + // interfaces provide much more test coverage. + {"int|~int", "~int", true}, + {"myInt|~int", "~int", true}, + {"int|string", "string|int", true}, + {"int|int|string", "string|int", true}, + {"myInt|string", "int|string", false}, + } { + x := makeUnion(test.x) + y := makeUnion(test.y) + if got := Identical(x, y); got != test.want { + t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got) + } + } +} + func TestIssue15305(t *testing.T) { const src = "package p; func f() int16; var _ = f(undef)" f, err := parseSrc("issue15305.go", src) @@ -1847,3 +2236,412 @@ func f(x T) T { return foo.F(x) } } } } + +func TestInstantiate(t *testing.T) { + // eventually we like more tests but this is a start + const src = "package p; type T[P any] *T[P]" + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + + // type T should have one type parameter + T := pkg.Scope().Lookup("T").Type().(*Named) + if n := T.TypeParams().Len(); n != 1 { + t.Fatalf("expected 1 type parameter; found %d", n) + } + + // instantiation should succeed (no endless recursion) + // even with a nil *Checker + res, err := Instantiate(nil, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + + // instantiated type should point to itself + if p := res.Underlying().(*Pointer).Elem(); p != res { + t.Fatalf("unexpected result type: %s points to %s", res, p) + } +} + +func TestInstantiateErrors(t *testing.T) { + tests := []struct { + src string // by convention, T must be the type being instantiated + targs []Type + wantAt int // -1 indicates no error + }{ + {"type T[P interface{~string}] int", []Type{Typ[Int]}, 0}, + {"type T[P1 interface{int}, P2 interface{~string}] int", []Type{Typ[Int], Typ[Int]}, 1}, + {"type T[P1 any, P2 interface{~[]P1}] int", []Type{Typ[Int], NewSlice(Typ[String])}, 1}, + {"type T[P1 interface{~[]P2}, P2 any] int", []Type{NewSlice(Typ[String]), Typ[Int]}, 0}, + } + + for _, test := range tests { + src := "package p; " + test.src + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + + T := pkg.Scope().Lookup("T").Type().(*Named) + + _, err = Instantiate(nil, T, test.targs, true) + if err == nil { + t.Fatalf("Instantiate(%v, %v) returned nil error, want non-nil", T, test.targs) + } + + var argErr *ArgumentError + if !errors.As(err, &argErr) { + t.Fatalf("Instantiate(%v, %v): error is not an *ArgumentError", T, test.targs) + } + + if argErr.Index != test.wantAt { + t.Errorf("Instantiate(%v, %v): error at index %d, want index %d", T, test.targs, argErr.Index, test.wantAt) + } + } +} + +func TestArgumentErrorUnwrapping(t *testing.T) { + var err error = &ArgumentError{ + Index: 1, + Err: Error{Msg: "test"}, + } + var e Error + if !errors.As(err, &e) { + t.Fatalf("error %v does not wrap types.Error", err) + } + if e.Msg != "test" { + t.Errorf("e.Msg = %q, want %q", e.Msg, "test") + } +} + +func TestInstanceIdentity(t *testing.T) { + imports := make(testImporter) + conf := Config{Importer: imports} + makePkg := func(src string) { + f, err := parseSrc("", src) + if err != nil { + t.Fatal(err) + } + name := f.PkgName.Value + pkg, err := conf.Check(name, []*syntax.File{f}, nil) + if err != nil { + t.Fatal(err) + } + imports[name] = pkg + } + makePkg(`package lib; type T[P any] struct{}`) + makePkg(`package a; import "lib"; var A lib.T[int]`) + makePkg(`package b; import "lib"; var B lib.T[int]`) + a := imports["a"].Scope().Lookup("A") + b := imports["b"].Scope().Lookup("B") + if !Identical(a.Type(), b.Type()) { + t.Errorf("mismatching types: a.A: %s, b.B: %s", a.Type(), b.Type()) + } +} + +// TestInstantiatedObjects verifies properties of instantiated objects. +func TestInstantiatedObjects(t *testing.T) { + const src = ` +package p + +type T[P any] struct { + field P +} + +func (recv *T[Q]) concreteMethod(mParam Q) (mResult Q) { return } + +type FT[P any] func(ftParam P) (ftResult P) + +func F[P any](fParam P) (fResult P){ return } + +type I[P any] interface { + interfaceMethod(P) +} + +type R[P any] T[P] + +func (R[P]) m() {} // having a method triggers expansion of R + +var ( + t T[int] + ft FT[int] + f = F[int] + i I[int] +) + +func fn() { + var r R[int] + _ = r +} +` + info := &Info{ + Defs: make(map[*syntax.Name]Object), + } + f, err := parseSrc("p.go", src) + if err != nil { + t.Fatal(err) + } + conf := Config{} + pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info) + if err != nil { + t.Fatal(err) + } + + lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() } + fnScope := pkg.Scope().Lookup("fn").(*Func).Scope() + + tests := []struct { + name string + obj Object + }{ + // Struct fields + {"field", lookup("t").Underlying().(*Struct).Field(0)}, + {"field", fnScope.Lookup("r").Type().Underlying().(*Struct).Field(0)}, + + // Methods and method fields + {"concreteMethod", lookup("t").(*Named).Method(0)}, + {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()}, + {"mParam", lookup("t").(*Named).Method(0).Type().(*Signature).Params().At(0)}, + {"mResult", lookup("t").(*Named).Method(0).Type().(*Signature).Results().At(0)}, + + // Interface methods + {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)}, + + // Function type fields + {"ftParam", lookup("ft").Underlying().(*Signature).Params().At(0)}, + {"ftResult", lookup("ft").Underlying().(*Signature).Results().At(0)}, + + // Function fields + {"fParam", lookup("f").(*Signature).Params().At(0)}, + {"fResult", lookup("f").(*Signature).Results().At(0)}, + } + + // Collect all identifiers by name. + idents := make(map[string][]*syntax.Name) + syntax.Inspect(f, func(n syntax.Node) bool { + if id, ok := n.(*syntax.Name); ok { + idents[id.Value] = append(idents[id.Value], id) + } + return true + }) + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if got := len(idents[test.name]); got != 1 { + t.Fatalf("found %d identifiers named %s, want 1", got, test.name) + } + ident := idents[test.name][0] + def := info.Defs[ident] + if def == test.obj { + t.Fatalf("info.Defs[%s] contains the test object", test.name) + } + if orig := originObject(test.obj); def != orig { + t.Errorf("info.Defs[%s] does not match obj.Origin()", test.name) + } + if def.Pkg() != test.obj.Pkg() { + t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg()) + } + if def.Name() != test.obj.Name() { + t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name()) + } + if def.Pos() != test.obj.Pos() { + t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos()) + } + if def.Parent() != test.obj.Parent() { + t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent()) + } + if def.Exported() != test.obj.Exported() { + t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported()) + } + if def.Id() != test.obj.Id() { + t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id()) + } + // String and Type are expected to differ. + }) + } +} + +func originObject(obj Object) Object { + switch obj := obj.(type) { + case *Var: + return obj.Origin() + case *Func: + return obj.Origin() + } + return obj +} + +func TestImplements(t *testing.T) { + const src = ` +package p + +type EmptyIface interface{} + +type I interface { + m() +} + +type C interface { + m() + ~int +} + +type Integer interface{ + int8 | int16 | int32 | int64 +} + +type EmptyTypeSet interface{ + Integer + ~string +} + +type N1 int +func (N1) m() {} + +type N2 int +func (*N2) m() {} + +type N3 int +func (N3) m(int) {} + +type N4 string +func (N4) m() + +type Bad Bad // invalid type +` + + f, err := parseSrc("p.go", src) + if err != nil { + t.Fatal(err) + } + conf := Config{Error: func(error) {}} + pkg, _ := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil) + + lookup := func(tname string) Type { return pkg.Scope().Lookup(tname).Type() } + var ( + EmptyIface = lookup("EmptyIface").Underlying().(*Interface) + I = lookup("I").(*Named) + II = I.Underlying().(*Interface) + C = lookup("C").(*Named) + CI = C.Underlying().(*Interface) + Integer = lookup("Integer").Underlying().(*Interface) + EmptyTypeSet = lookup("EmptyTypeSet").Underlying().(*Interface) + N1 = lookup("N1") + N1p = NewPointer(N1) + N2 = lookup("N2") + N2p = NewPointer(N2) + N3 = lookup("N3") + N4 = lookup("N4") + Bad = lookup("Bad") + ) + + tests := []struct { + V Type + T *Interface + want bool + }{ + {I, II, true}, + {I, CI, false}, + {C, II, true}, + {C, CI, true}, + {Typ[Int8], Integer, true}, + {Typ[Int64], Integer, true}, + {Typ[String], Integer, false}, + {EmptyTypeSet, II, true}, + {EmptyTypeSet, EmptyTypeSet, true}, + {Typ[Int], EmptyTypeSet, false}, + {N1, II, true}, + {N1, CI, true}, + {N1p, II, true}, + {N1p, CI, false}, + {N2, II, false}, + {N2, CI, false}, + {N2p, II, true}, + {N2p, CI, false}, + {N3, II, false}, + {N3, CI, false}, + {N4, II, true}, + {N4, CI, false}, + {Bad, II, false}, + {Bad, CI, false}, + {Bad, EmptyIface, true}, + } + + for _, test := range tests { + if got := Implements(test.V, test.T); got != test.want { + t.Errorf("Implements(%s, %s) = %t, want %t", test.V, test.T, got, test.want) + } + + // The type assertion x.(T) is valid if T is an interface or if T implements the type of x. + // The assertion is never valid if T is a bad type. + V := test.T + T := test.V + want := false + if _, ok := T.Underlying().(*Interface); (ok || Implements(T, V)) && T != Bad { + want = true + } + if got := AssertableTo(V, T); got != want { + t.Errorf("AssertableTo(%s, %s) = %t, want %t", V, T, got, want) + } + } +} + +func TestMissingMethodAlternative(t *testing.T) { + const src = ` +package p +type T interface { + m() +} + +type V0 struct{} +func (V0) m() {} + +type V1 struct{} + +type V2 struct{} +func (V2) m() int + +type V3 struct{} +func (*V3) m() + +type V4 struct{} +func (V4) M() +` + + pkg, err := pkgFor("p.go", src, nil) + if err != nil { + t.Fatal(err) + } + + T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface) + lookup := func(name string) (*Func, bool) { + return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true) + } + + // V0 has method m with correct signature. Should not report wrongType. + method, wrongType := lookup("V0") + if method != nil || wrongType { + t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType) + } + + checkMissingMethod := func(tname string, reportWrongType bool) { + method, wrongType := lookup(tname) + if method == nil || method.Name() != "m" || wrongType != reportWrongType { + t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType) + } + } + + // V1 has no method m. Should not report wrongType. + checkMissingMethod("V1", false) + + // V2 has method m with wrong signature type (ignoring receiver). Should report wrongType. + checkMissingMethod("V2", true) + + // V3 has no method m but it exists on *V3. Should report wrongType. + checkMissingMethod("V3", true) + + // V4 has no method m but has M. Should not report wrongType. + checkMissingMethod("V4", false) +} diff --git a/src/cmd/compile/internal/types2/array.go b/src/cmd/compile/internal/types2/array.go new file mode 100644 index 00000000000000..502d49bc25770f --- /dev/null +++ b/src/cmd/compile/internal/types2/array.go @@ -0,0 +1,25 @@ +// 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 types2 + +// An Array represents an array type. +type Array struct { + len int64 + elem Type +} + +// NewArray returns a new array type for the given element type and length. +// A negative length indicates an unknown length. +func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} } + +// Len returns the length of array a. +// A negative result indicates an unknown length. +func (a *Array) Len() int64 { return a.len } + +// Elem returns element type of array a. +func (a *Array) Elem() Type { return a.elem } + +func (a *Array) Underlying() Type { return a } +func (a *Array) String() string { return TypeString(a, nil) } diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 583118c8b235c5..f766c0b31d6b39 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -6,7 +6,11 @@ package types2 -import "cmd/compile/internal/syntax" +import ( + "cmd/compile/internal/syntax" + "fmt" + "strings" +) // assignment reports whether x can be assigned to a variable of type T, // if necessary by attempting to convert untyped values to the appropriate @@ -40,7 +44,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) { x.mode = invalid return } - } else if T == nil || IsInterface(T) { + } else if T == nil || isNonTypeParamInterface(T) { target = Default(x.typ) } newType, val, code := check.implicitTypeAndValue(x, target) @@ -68,7 +72,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) { // x.typ is typed // A generic (non-instantiated) function value cannot be assigned to a variable. - if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 { + if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 { check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context) } @@ -82,7 +86,11 @@ func (check *Checker) assignment(x *operand, T Type, context string) { reason := "" if ok, _ := x.assignableTo(check, T, &reason); !ok { if check.conf.CompilerErrorMessages { - check.errorf(x, "incompatible type: cannot use %s as %s value", x, T) + if reason != "" { + check.errorf(x, "cannot use %s as type %s in %s:\n\t%s", x, T, context, reason) + } else { + check.errorf(x, "cannot use %s as type %s in %s", x, T, context) + } } else { if reason != "" { check.errorf(x, "cannot use %s as %s value in %s: %s", x, T, context, reason) @@ -153,6 +161,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type { check.assignment(x, lhs.typ, context) if x.mode == invalid { + lhs.used = true // avoid follow-on "declared but not used" errors return nil } @@ -161,7 +170,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type { func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { if x.mode == invalid || x.typ == Typ[Invalid] { - check.useLHS(lhs) + check.use(lhs) return nil } @@ -212,9 +221,6 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { return nil case variable, mapindex: // ok - case nilvalue: - check.error(&z, "cannot assign to nil") // default would print "untyped nil" - return nil default: if sel, ok := z.expr.(*syntax.SelectorExpr); ok { var op operand @@ -236,14 +242,88 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type { return x.typ } -// If returnPos is valid, initVars is called to type-check the assignment of -// return expressions, and returnPos is the position of the return statement. -func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) { - rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsKnown()) +// operandTypes returns the list of types for the given operands. +func operandTypes(list []*operand) (res []Type) { + for _, x := range list { + res = append(res, x.typ) + } + return res +} + +// varTypes returns the list of types for the given variables. +func varTypes(list []*Var) (res []Type) { + for _, x := range list { + res = append(res, x.typ) + } + return res +} + +// typesSummary returns a string of the form "(t1, t2, ...)" where the +// ti's are user-friendly string representations for the given types. +// If variadic is set and the last type is a slice, its string is of +// the form "...E" where E is the slice's element type. +func (check *Checker) typesSummary(list []Type, variadic bool) string { + var res []string + for i, t := range list { + var s string + switch { + case t == nil: + fallthrough // should not happen but be cautious + case t == Typ[Invalid]: + s = "" + case isUntyped(t): + if isNumeric(t) { + // Do not imply a specific type requirement: + // "have number, want float64" is better than + // "have untyped int, want float64" or + // "have int, want float64". + s = "number" + } else { + // If we don't have a number, omit the "untyped" qualifier + // for compactness. + s = strings.Replace(t.(*Basic).name, "untyped ", "", -1) + } + case variadic && i == len(list)-1: + s = check.sprintf("...%s", t.(*Slice).elem) + } + if s == "" { + s = check.sprintf("%s", t) + } + res = append(res, s) + } + return "(" + strings.Join(res, ", ") + ")" +} + +func measure(x int, unit string) string { + if x != 1 { + unit += "s" + } + return fmt.Sprintf("%d %s", x, unit) +} + +func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) { + vars := measure(nvars, "variable") + vals := measure(nvals, "value") + rhs0 := rhs[0] + + if len(rhs) == 1 { + if call, _ := unparen(rhs0).(*syntax.CallExpr); call != nil { + check.errorf(rhs0, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals) + return + } + } + check.errorf(rhs0, "assignment mismatch: %s but %s", vars, vals) +} + +// If returnStmt != nil, initVars is called to type-check the assignment +// of return expressions, and returnStmt is the return statement. +func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt syntax.Stmt) { + rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && returnStmt == nil) if len(lhs) != len(rhs) { // invalidate lhs for _, obj := range lhs { + obj.used = true // avoid declared but not used errors if obj.typ == nil { obj.typ = Typ[Invalid] } @@ -254,16 +334,32 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn return } } - if returnPos.IsKnown() { - check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs)) + if returnStmt != nil { + var at poser = returnStmt + qualifier := "not enough" + if len(rhs) > len(lhs) { + at = rhs[len(lhs)].expr // report at first extra value + qualifier = "too many" + } else if len(rhs) > 0 { + at = rhs[len(rhs)-1].expr // report at last value + } + var err error_ + err.errorf(at, "%s return values", qualifier) + err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false)) + err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false)) + check.report(&err) return } - check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs)) + if check.conf.CompilerErrorMessages { + check.assignError(orig_rhs, len(lhs), len(rhs)) + } else { + check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs)) + } return } context := "assignment" - if returnPos.IsKnown() { + if returnStmt != nil { context = "return statement" } @@ -276,8 +372,18 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn return } + ok := true for i, lhs := range lhs { - check.initVar(lhs, rhs[i], context) + if check.initVar(lhs, rhs[i], context) == nil { + ok = false + } + } + + // avoid follow-on "declared but not used" errors if any initialization failed + if !ok { + for _, lhs := range lhs { + lhs.used = true + } } } @@ -285,14 +391,18 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) { rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2) if len(lhs) != len(rhs) { - check.useLHS(lhs...) + check.use(lhs...) // don't report an error if we already reported one for _, x := range rhs { if x.mode == invalid { return } } - check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs)) + if check.conf.CompilerErrorMessages { + check.assignError(orig_rhs, len(lhs), len(rhs)) + } else { + check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs)) + } return } @@ -305,16 +415,34 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) { return } + ok := true for i, lhs := range lhs { - check.assignVar(lhs, rhs[i]) + if check.assignVar(lhs, rhs[i]) == nil { + ok = false + } + } + + // avoid follow-on "declared but not used" errors if any assignment failed + if !ok { + // don't call check.use to avoid re-evaluation of the lhs expressions + for _, lhs := range lhs { + if name, _ := unparen(lhs).(*syntax.Name); name != nil { + if obj := check.lookup(name.Value); obj != nil { + // see comment in assignVar + if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg { + v.used = true + } + } + } + } } } // unpack unpacks a *syntax.ListExpr into a list of syntax.Expr. // Helper introduced for the go/types -> types2 port. // TODO(gri) Should find a more efficient solution that doesn't -// require introduction of a new slice for simple -// expressions. +// require introduction of a new slice for simple +// expressions. func unpackExpr(x syntax.Expr) []syntax.Expr { if x, _ := x.(*syntax.ListExpr); x != nil { return x.ElemList @@ -337,7 +465,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) { for i, lhs := range lhs { ident, _ := lhs.(*syntax.Name) if ident == nil { - check.useLHS(lhs) + check.use(lhs) check.errorf(lhs, "non-name %s on left side of :=", lhs) hasErr = true continue @@ -385,7 +513,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) { } } - check.initVars(lhsVars, rhs, nopos) + check.initVars(lhsVars, rhs, nil) // process function literals in rhs expressions before scope changes check.processDelayed(top) diff --git a/src/cmd/compile/internal/types2/basic.go b/src/cmd/compile/internal/types2/basic.go new file mode 100644 index 00000000000000..2fd973cafbc5e6 --- /dev/null +++ b/src/cmd/compile/internal/types2/basic.go @@ -0,0 +1,82 @@ +// 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 types2 + +// BasicKind describes the kind of basic type. +type BasicKind int + +const ( + Invalid BasicKind = iota // type is invalid + + // predeclared types + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + String + UnsafePointer + + // types for untyped values + UntypedBool + UntypedInt + UntypedRune + UntypedFloat + UntypedComplex + UntypedString + UntypedNil + + // aliases + Byte = Uint8 + Rune = Int32 +) + +// BasicInfo is a set of flags describing properties of a basic type. +type BasicInfo int + +// Properties of basic types. +const ( + IsBoolean BasicInfo = 1 << iota + IsInteger + IsUnsigned + IsFloat + IsComplex + IsString + IsUntyped + + IsOrdered = IsInteger | IsFloat | IsString + IsNumeric = IsInteger | IsFloat | IsComplex + IsConstType = IsBoolean | IsNumeric | IsString +) + +// A Basic represents a basic type. +type Basic struct { + kind BasicKind + info BasicInfo + name string +} + +// Kind returns the kind of basic type b. +func (b *Basic) Kind() BasicKind { return b.kind } + +// Info returns information about properties of basic type b. +func (b *Basic) Info() BasicInfo { return b.info } + +// Name returns the name of basic type b. +func (b *Basic) Name() string { return b.name } + +func (b *Basic) Underlying() Type { return b } +func (b *Basic) String() string { return TypeString(b, nil) } diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index f90e06f226791d..440a53239651a1 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -16,7 +16,6 @@ import ( // reports whether the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. -// func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] @@ -46,7 +45,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( default: // make argument getter xlist, _ := check.exprList(call.ArgList, false) - arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) } + arg = func(x *operand, i int) { *x = *xlist[i] } nargs = len(xlist) // evaluate first argument, if present if nargs > 0 { @@ -82,10 +81,24 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( // of S and the respective parameter passing rules apply." S := x.typ var T Type - if s := asSlice(S); s != nil { + if s, _ := coreType(S).(*Slice); s != nil { T = s.elem } else { - check.errorf(x, invalidArg+"%s is not a slice", x) + var cause string + switch { + case x.isNil(): + cause = "have untyped nil" + case isTypeParam(S): + if u := coreType(S); u != nil { + cause = check.sprintf("%s has core type %s", x, u) + } else { + cause = check.sprintf("%s has no core type", x) + } + default: + cause = check.sprintf("have %s", x) + } + // don't use invalidArg prefix here as it would repeat "argument" in the error message + check.errorf(x, "first argument to append must be a slice; %s", cause) return } @@ -101,7 +114,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( if x.mode == invalid { return } - if isString(x.typ) { + if t := coreString(x.typ); t != nil && isString(t) { if check.Types != nil { sig := makeSig(S, S, x.typ) sig.variadic = true @@ -129,7 +142,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( arg(&x, i) xlist = append(xlist, &x) } - check.arguments(call, sig, nil, xlist) // discard result (we know the result type) + check.arguments(call, sig, nil, xlist, nil) // discard result (we know the result type) // ok to continue even if check.arguments reported errors x.mode = value @@ -142,9 +155,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( // cap(x) // len(x) mode := invalid - var typ Type var val constant.Value - switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) { + switch t := arrayPtrDeref(under(x.typ)).(type) { case *Basic: if isString(t) && id == _Len { if x.mode == constant_ { @@ -178,9 +190,12 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( mode = value } - case *Sum: - if t.is(func(t Type) bool { - switch t := under(t).(type) { + case *Interface: + if !isTypeParam(x.typ) { + break + } + if t.typeSet().underIs(func(t Type) bool { + switch t := arrayPtrDeref(t).(type) { case *Basic: if isString(t) && id == _Len { return true @@ -198,33 +213,39 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } } - if mode == invalid && typ != Typ[Invalid] { + if mode == invalid && under(x.typ) != Typ[Invalid] { check.errorf(x, invalidArg+"%s for %s", x, bin.name) return } + // record the signature before changing x.typ + if check.Types != nil && mode != constant_ { + check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ)) + } + x.mode = mode x.typ = Typ[Int] x.val = val - if check.Types != nil && mode != constant_ { - check.recordBuiltinType(call.Fun, makeSig(x.typ, typ)) - } case _Close: // close(c) - c := asChan(x.typ) - if c == nil { - check.errorf(x, invalidArg+"%s is not a channel", x) - return - } - if c.dir == RecvOnly { - check.errorf(x, invalidArg+"%s must not be a receive-only channel", x) + if !underIs(x.typ, func(u Type) bool { + uch, _ := u.(*Chan) + if uch == nil { + check.errorf(x, invalidOp+"cannot close non-channel %s", x) + return false + } + if uch.dir == RecvOnly { + check.errorf(x, invalidOp+"cannot close receive-only channel %s", x) + return false + } + return true + }) { return } - x.mode = novalue if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, c)) + check.recordBuiltinType(call.Fun, makeSig(nil, x.typ)) } case _Complex: @@ -281,14 +302,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } // both argument types must be identical - if !check.identical(x.typ, y.typ) { + if !Identical(x.typ, y.typ) { check.errorf(x, invalidOp+"%v (mismatched types %s and %s)", call, x.typ, y.typ) return } // the argument types must be of floating-point type - f := func(x Type) Type { - if t := asBasic(x); t != nil { + // (applyTypeFunc never calls f with a type parameter) + f := func(typ Type) Type { + assert(!isTypeParam(typ)) + if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Float32: return Typ[Complex64] @@ -300,7 +323,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } return nil } - resTyp := check.applyTypeFunc(f, x.typ) + resTyp := check.applyTypeFunc(f, x, id) if resTyp == nil { check.errorf(x, invalidArg+"arguments have type %s, expected floating-point", x.typ) return @@ -321,33 +344,26 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( case _Copy: // copy(x, y []T) int - var dst Type - if t := asSlice(x.typ); t != nil { - dst = t.elem - } + dst, _ := coreType(x.typ).(*Slice) var y operand arg(&y, 1) if y.mode == invalid { return } - var src Type - switch t := optype(y.typ).(type) { - case *Basic: - if isString(y.typ) { - src = universeByte - } - case *Slice: - src = t.elem + src0 := coreString(y.typ) + if src0 != nil && isString(src0) { + src0 = NewSlice(universeByte) } + src, _ := src0.(*Slice) if dst == nil || src == nil { check.errorf(x, invalidArg+"copy expects slice arguments; found %s and %s", x, &y) return } - if !check.identical(dst, src) { - check.errorf(x, invalidArg+"arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src) + if !Identical(dst.elem, src.elem) { + check.errorf(x, invalidArg+"arguments to copy %s and %s have different element types %s and %s", x, &y, dst.elem, src.elem) return } @@ -358,25 +374,40 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( x.typ = Typ[Int] case _Delete: - // delete(m, k) - m := asMap(x.typ) - if m == nil { - check.errorf(x, invalidArg+"%s is not a map", x) + // delete(map_, key) + // map_ must be a map type or a type parameter describing map types. + // The key cannot be a type parameter for now. + map_ := x.typ + var key Type + if !underIs(map_, func(u Type) bool { + map_, _ := u.(*Map) + if map_ == nil { + check.errorf(x, invalidArg+"%s is not a map", x) + return false + } + if key != nil && !Identical(map_.key, key) { + check.errorf(x, invalidArg+"maps of %s must have identical key types", x) + return false + } + key = map_.key + return true + }) { return } + arg(x, 1) // k if x.mode == invalid { return } - check.assignment(x, m.key, "argument to delete") + check.assignment(x, key, "argument to delete") if x.mode == invalid { return } x.mode = novalue if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key)) + check.recordBuiltinType(call.Fun, makeSig(nil, map_, key)) } case _Imag, _Real: @@ -405,8 +436,10 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } // the argument must be of complex type - f := func(x Type) Type { - if t := asBasic(x); t != nil { + // (applyTypeFunc never calls f with a type parameter) + f := func(typ Type) Type { + assert(!isTypeParam(typ)) + if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Complex64: return Typ[Float32] @@ -418,7 +451,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } return nil } - resTyp := check.applyTypeFunc(f, x.typ) + resTyp := check.applyTypeFunc(f, x, id) if resTyp == nil { check.errorf(x, invalidArg+"argument has type %s, expected complex type", x.typ) return @@ -451,39 +484,21 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( return } - min, max := -1, 10 - var valid func(t Type) bool - valid = func(t Type) bool { - var m int - switch t := optype(t).(type) { - case *Slice: - m = 2 - case *Map, *Chan: - m = 1 - case *Sum: - return t.is(valid) - default: - return false - } - if m > min { - min = m - } - if m+1 < max { - max = m + 1 - } - return true - } - - if !valid(T) { + var min int // minimum number of arguments + switch coreType(T).(type) { + case *Slice: + min = 2 + case *Map, *Chan: + min = 1 + case nil: + check.errorf(arg0, invalidArg+"cannot make %s: no core type", arg0) + return + default: check.errorf(arg0, invalidArg+"cannot make %s; type must be slice, map, or channel", arg0) return } - if nargs < min || max < nargs { - if min == max { - check.errorf(call, "%v expects %d arguments; found %d", call, min, nargs) - } else { - check.errorf(call, "%v expects %d or %d arguments; found %d", call, min, max, nargs) - } + if nargs < min || min+1 < nargs { + check.errorf(call, invalidOp+"%v expects %d or %d arguments; found %d", call, min, min+1, nargs) return } @@ -580,7 +595,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( case _Add: // unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer if !check.allowVersion(check.pkg, 1, 17) { - check.error(call.Fun, "unsafe.Add requires go1.17 or later") + check.versionErrorf(call.Fun, "go1.17", "unsafe.Add") return } @@ -603,19 +618,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( case _Alignof: // unsafe.Alignof(x T) uintptr - if asTypeParam(x.typ) != nil { - check.errorf(call, invalidOp+"unsafe.Alignof undefined for %s", x) - return - } check.assignment(x, nil, "argument to unsafe.Alignof") if x.mode == invalid { return } - x.mode = constant_ - x.val = constant.MakeInt64(check.conf.alignof(x.typ)) + if hasVarSize(x.typ, nil) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ)) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.alignof(x.typ)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Offsetof: // unsafe.Offsetof(x T) uintptr, where x must be a selector @@ -635,7 +653,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( base := derefStructPtr(x.typ) sel := selx.Sel.Value - obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel) + obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel) switch obj.(type) { case nil: check.errorf(x, invalidArg+"%s has no single field %s", base, sel) @@ -653,40 +671,62 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( return } - // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? + // TODO(gri) Should we pass x.typ instead of base (and have indirect report if derefStructPtr indirected)? check.recordSelection(selx, FieldVal, base, obj, index, false) - offs := check.conf.offsetof(base, index) - x.mode = constant_ - x.val = constant.MakeInt64(offs) + // record the selector expression (was bug - issue #47895) + { + mode := value + if x.mode == variable || indirect { + mode = variable + } + check.record(&operand{mode, selx, obj.Type(), nil, 0}) + } + + // The field offset is considered a variable even if the field is declared before + // the part of the struct which is variable-sized. This makes both the rules + // simpler and also permits (or at least doesn't prevent) a compiler from re- + // arranging struct fields if it wanted to. + if hasVarSize(base, nil) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type())) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.offsetof(base, index)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Sizeof: // unsafe.Sizeof(x T) uintptr - if asTypeParam(x.typ) != nil { - check.errorf(call, invalidOp+"unsafe.Sizeof undefined for %s", x) - return - } check.assignment(x, nil, "argument to unsafe.Sizeof") if x.mode == invalid { return } - x.mode = constant_ - x.val = constant.MakeInt64(check.conf.sizeof(x.typ)) + if hasVarSize(x.typ, nil) { + x.mode = value + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ)) + } + } else { + x.mode = constant_ + x.val = constant.MakeInt64(check.conf.sizeof(x.typ)) + // result is constant - no need to record signature + } x.typ = Typ[Uintptr] - // result is constant - no need to record signature case _Slice: // unsafe.Slice(ptr *T, len IntegerType) []T if !check.allowVersion(check.pkg, 1, 17) { - check.error(call.Fun, "unsafe.Slice requires go1.17 or later") + check.versionErrorf(call.Fun, "go1.17", "unsafe.Slice") return } - typ := asPointer(x.typ) - if typ == nil { + ptr, _ := under(x.typ).(*Pointer) // TODO(gri) should this be coreType rather than under? + if ptr == nil { check.errorf(x, invalidArg+"%s is not a pointer", x) return } @@ -698,9 +738,70 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( } x.mode = value - x.typ = NewSlice(typ.base) + x.typ = NewSlice(ptr.base) + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(x.typ, ptr, y.typ)) + } + + case _SliceData: + // unsafe.SliceData(slice []T) *T + if !check.allowVersion(check.pkg, 1, 20) { + check.versionErrorf(call.Fun, "go1.20", "unsafe.SliceData") + return + } + + slice, _ := under(x.typ).(*Slice) // TODO(gri) should this be coreType rather than under? + if slice == nil { + check.errorf(x, invalidArg+"%s is not a slice", x) + return + } + + x.mode = value + x.typ = NewPointer(slice.elem) + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(x.typ, slice)) + } + + case _String: + // unsafe.String(ptr *byte, len IntegerType) string + if !check.allowVersion(check.pkg, 1, 20) { + check.versionErrorf(call.Fun, "go1.20", "unsafe.String") + return + } + + check.assignment(x, NewPointer(universeByte), "argument to unsafe.String") + if x.mode == invalid { + return + } + + var y operand + arg(&y, 1) + if !check.isValidIndex(&y, "length", false) { + return + } + + x.mode = value + x.typ = Typ[String] if check.Types != nil { - check.recordBuiltinType(call.Fun, makeSig(x.typ, typ, y.typ)) + check.recordBuiltinType(call.Fun, makeSig(x.typ, NewPointer(universeByte), y.typ)) + } + + case _StringData: + // unsafe.StringData(str string) *byte + if !check.allowVersion(check.pkg, 1, 20) { + check.versionErrorf(call.Fun, "go1.20", "unsafe.StringData") + return + } + + check.assignment(x, Typ[String], "argument to unsafe.StringData") + if x.mode == invalid { + return + } + + x.mode = value + x.typ = NewPointer(universeByte) + if check.Types != nil { + check.recordBuiltinType(call.Fun, makeSig(x.typ, Typ[String])) } case _Assert: @@ -735,7 +836,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( var t operand x1 := x for _, arg := range call.ArgList { - check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T)) + check.rawExpr(x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T)) check.dump("%v: %s", posFor(x1), x1) x1 = &t // use incoming x only for first argument } @@ -748,6 +849,43 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( return true } +// hasVarSize reports if the size of type t is variable due to type parameters +// or if the type is infinitely-sized due to a cycle for which the type has not +// yet been checked. +func hasVarSize(t Type, seen map[*Named]bool) (varSized bool) { + // Cycles are only possible through *Named types. + // The seen map is used to detect cycles and track + // the results of previously seen types. + if named, _ := t.(*Named); named != nil { + if v, ok := seen[named]; ok { + return v + } + if seen == nil { + seen = make(map[*Named]bool) + } + seen[named] = true // possibly cyclic until proven otherwise + defer func() { + seen[named] = varSized // record final determination for named + }() + } + + switch u := under(t).(type) { + case *Array: + return hasVarSize(u.elem, seen) + case *Struct: + for _, f := range u.fields { + if hasVarSize(f.typ, seen) { + return true + } + } + case *Interface: + return isTypeParam(t) + case *Named, *Union: + unreachable() + } + return false +} + // applyTypeFunc applies f to x. If x is a type parameter, // the result is a type parameter constrained by an new // interface bound. The type bounds for that interface @@ -755,14 +893,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( // of x. If any of these applications of f return nil, // applyTypeFunc returns nil. // If x is not a type parameter, the result is f(x). -func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { - if tp := asTypeParam(x); tp != nil { +func (check *Checker) applyTypeFunc(f func(Type) Type, x *operand, id builtinId) Type { + if tp, _ := x.typ.(*TypeParam); tp != nil { // Test if t satisfies the requirements for the argument // type and collect possible result types at the same time. - var rtypes []Type - if !tp.Bound().is(func(x Type) bool { - if r := f(x); r != nil { - rtypes = append(rtypes, r) + var terms []*Term + if !tp.is(func(t *term) bool { + if t == nil { + return false + } + if r := f(t.typ); r != nil { + terms = append(terms, NewTerm(t.tilde, r)) return true } return false @@ -770,21 +911,23 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { return nil } - // TODO(gri) Would it be ok to return just the one type - // if len(rtypes) == 1? What about top-level - // uses of real() where the result is used to - // define type and initialize a variable? + // We can type-check this fine but we're introducing a synthetic + // type parameter for the result. It's not clear what the API + // implications are here. Report an error for 1.18 but continue + // type-checking. + check.softErrorf(x, "%s not supported as argument to %s for go1.18 (see issue #50937)", x, predeclaredFuncs[id].name) - // construct a suitable new type parameter - tpar := NewTypeName(nopos, nil /* = Universe pkg */, "", nil) - ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect - tsum := NewSum(rtypes) - ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum} + // Construct a suitable new type parameter for the result type. + // The type parameter is placed in the current package so export/import + // works as expected. + tpar := NewTypeName(nopos, check.pkg, tp.obj.name, nil) + ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect + ptyp.index = tp.index return ptyp } - return f(x) + return f(x.typ) } // makeSig makes a signature for the given argument and result types. @@ -803,12 +946,11 @@ func makeSig(res Type, args ...Type) *Signature { return &Signature{params: params, results: result} } -// implicitArrayDeref returns A if typ is of the form *A and A is an array; +// arrayPtrDeref returns A if typ is of the form *A and A is an array; // otherwise it returns typ. -// -func implicitArrayDeref(typ Type) Type { +func arrayPtrDeref(typ Type) Type { if p, ok := typ.(*Pointer); ok { - if a := asArray(p.base); a != nil { + if a, _ := under(p.base).(*Array); a != nil { return a } } diff --git a/src/cmd/compile/internal/types2/builtins_test.go b/src/cmd/compile/internal/types2/builtins_test.go index 82c786b86ea20e..e382c47b914cdf 100644 --- a/src/cmd/compile/internal/types2/builtins_test.go +++ b/src/cmd/compile/internal/types2/builtins_test.go @@ -7,6 +7,7 @@ package types2_test import ( "cmd/compile/internal/syntax" "fmt" + "strings" "testing" . "cmd/compile/internal/types2" @@ -27,6 +28,8 @@ var builtinCalls = []struct { {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`}, {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`}, + {"cap", `type S []byte; var s S; _ = cap(s)`, `func(p.S) int`}, + {"cap", `var s P; _ = cap(s)`, `func(P) int`}, {"len", `_ = len("foo")`, `invalid type`}, // constant {"len", `var s string; _ = len(s)`, `func(string) int`}, @@ -35,6 +38,8 @@ var builtinCalls = []struct { {"len", `var s []int64; _ = len(s)`, `func([]int64) int`}, {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`}, {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`}, + {"len", `type S []byte; var s S; _ = len(s)`, `func(p.S) int`}, + {"len", `var s P; _ = len(s)`, `func(P) int`}, {"close", `var c chan int; close(c)`, `func(chan int)`}, {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`}, @@ -111,15 +116,28 @@ var builtinCalls = []struct { {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant + {"Alignof", `var x P; _ = unsafe.Alignof(x)`, `func(P) uintptr`}, {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant + {"Offsetof", `var x struct{_ int; f P}; _ = unsafe.Offsetof((&x).f)`, `func(P) uintptr`}, {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant + {"Sizeof", `var x P; _ = unsafe.Sizeof(x)`, `func(P) uintptr`}, {"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`}, {"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`}, + {"Slice", `type B *byte; var b B; _ = unsafe.Slice(b, 0)`, `func(*byte, int) []byte`}, + + {"SliceData", "var s []int; _ = unsafe.SliceData(s)", `func([]int) *int`}, + {"SliceData", "type S []int; var s S; _ = unsafe.SliceData(s)", `func([]int) *int`}, + + {"String", `var p *byte; _ = unsafe.String(p, 1)`, `func(*byte, int) string`}, + {"String", `type B *byte; var b B; _ = unsafe.String(b, 0)`, `func(*byte, int) string`}, + + {"StringData", `var s string; _ = unsafe.StringData(s)`, `func(string) *byte`}, + {"StringData", `_ = unsafe.StringData("foo")`, `func(string) *byte`}, {"assert", `assert(true)`, `invalid type`}, // constant {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant @@ -149,9 +167,14 @@ func TestBuiltinSignatures(t *testing.T) { } } +func parseGenericSrc(path, src string) (*syntax.File, error) { + errh := func(error) {} // dummy error handler so that parsing continues in presence of errors + return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, 0) +} + func testBuiltinSignature(t *testing.T, name, src0, want string) { - src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) - f, err := parseSrc("", src) + src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P ~[]byte]() { %s }`, src0) + f, err := parseGenericSrc("", src) if err != nil { t.Errorf("%s: %s", src0, err) return diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 6d149340b285b3..bb0f86b2d63afa 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -15,6 +15,10 @@ import ( // funcInst type-checks a function instantiation inst and returns the result in x. // The operand x must be the evaluation of inst.X and its type must be a signature. func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { + if !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(inst.Pos(), "go1.18", "function instantiation") + } + xlist := unpackExpr(inst.Index) targs := check.typeList(xlist) if targs == nil { @@ -26,7 +30,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // check number of type arguments (got) vs number of type parameters (want) sig := x.typ.(*Signature) - got, want := len(targs), len(sig.tparams) + got, want := len(targs), sig.TypeParams().Len() if !useConstraintTypeInference && got != want || got > want { check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want) x.mode = invalid @@ -34,10 +38,8 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { return } - // if we don't have enough type arguments, try type inference - inferred := false if got < want { - targs = check.infer(inst.Pos(), sig.tparams, targs, nil, nil, true) + targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil) if targs == nil { // error was already reported x.mode = invalid @@ -45,27 +47,52 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { return } got = len(targs) - inferred = true } assert(got == want) - // determine argument positions (for error reporting) - poslist := make([]syntax.Pos, len(xlist)) - for i, x := range xlist { - poslist[i] = syntax.StartPos(x) - } - // instantiate function signature - res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) - assert(res.tparams == nil) // signature is not generic anymore - if inferred { - check.recordInferred(inst, targs, res) - } + res := check.instantiateSignature(x.Pos(), sig, targs, xlist) + assert(res.TypeParams().Len() == 0) // signature is not generic anymore + check.recordInstance(inst.X, targs, res) x.typ = res x.mode = value x.expr = inst } +func (check *Checker) instantiateSignature(pos syntax.Pos, typ *Signature, targs []Type, xlist []syntax.Expr) (res *Signature) { + assert(check != nil) + assert(len(targs) == typ.TypeParams().Len()) + + if check.conf.Trace { + check.trace(pos, "-- instantiating signature %s with %s", typ, targs) + check.indent++ + defer func() { + check.indent-- + check.trace(pos, "=> %s (under = %s)", res, res.Underlying()) + }() + } + + inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) + assert(len(xlist) <= len(targs)) + + // verify instantiation lazily (was issue #50450) + check.later(func() { + tparams := typ.TypeParams().list() + if i, err := check.verify(pos, tparams, targs, check.context()); err != nil { + // best position for error reporting + pos := pos + if i < len(xlist) { + pos = syntax.StartPos(xlist[i]) + } + check.softErrorf(pos, "%s", err) + } else { + check.mono.recordInstance(check.pkg, pos, tparams, targs, xlist) + } + }).describef(pos, "verify instantiation") + + return inst +} + func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { var inst *syntax.IndexExpr // function instantiation, if any if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil { @@ -79,8 +106,9 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { x.expr = iexpr check.record(x) } else { - check.exprOrType(x, call.Fun) + check.exprOrType(x, call.Fun, true) } + // x.typ may be generic switch x.mode { case invalid: @@ -90,6 +118,10 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { case typexpr: // conversion + check.nonGeneric(x) + if x.mode == invalid { + return conversion + } T := x.typ x.mode = invalid switch n := len(call.ArgList); n { @@ -98,15 +130,14 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { case 1: check.expr(x, call.ArgList[0]) if x.mode != invalid { - if t := asInterface(T); t != nil { - check.completeInterface(nopos, t) - if t.IsConstraint() { - check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T) + if t, _ := under(T).(*Interface); t != nil && !isTypeParam(T) { + if !t.IsMethodSet() { + check.errorf(call, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", T) break } } if call.HasDots { - check.errorf(call.ArgList[0], "invalid use of ... in type conversion to %s", T) + check.errorf(call.ArgList[0], "invalid use of ... in conversion to %s", T) break } check.conversion(x, T) @@ -119,6 +150,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { return conversion case builtin: + // no need to check for non-genericity here id := x.id if !check.builtin(x, call, id) { x.mode = invalid @@ -132,9 +164,11 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { } // ordinary function/method call + // signature may be generic cgocall := x.mode == cgofunc - sig := asSignature(x.typ) + // a type parameter may be "called" if all types have the same signature + sig, _ := coreType(x.typ).(*Signature) if sig == nil { check.errorf(x, invalidOp+"cannot call non-function %s", x) x.mode = invalid @@ -143,9 +177,10 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { } // evaluate type arguments, if any + var xlist []syntax.Expr var targs []Type if inst != nil { - xlist := unpackExpr(inst.Index) + xlist = unpackExpr(inst.Index) targs = check.typeList(xlist) if targs == nil { check.use(call.ArgList...) @@ -156,7 +191,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { assert(len(targs) == len(xlist)) // check number of type arguments (got) vs number of type parameters (want) - got, want := len(targs), len(sig.tparams) + got, want := len(targs), sig.TypeParams().Len() if got > want { check.errorf(xlist[want], "got %d type arguments but want %d", got, want) check.use(call.ArgList...) @@ -168,7 +203,13 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { // evaluate arguments args, _ := check.exprList(call.ArgList, false) - sig = check.arguments(call, sig, targs, args) + isGeneric := sig.TypeParams().Len() > 0 + sig = check.arguments(call, sig, targs, args, xlist) + + if isGeneric && sig.TypeParams().Len() == 0 { + // update the recorded type of call.Fun to its instantiated type + check.recordTypeAndValue(call.Fun, value, sig, nil) + } // determine result switch sig.results.Len() { @@ -190,7 +231,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { // if type inference failed, a parametrized result must be invalidated // (operands cannot have a parametrized type) - if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) { + if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) { x.mode = invalid } @@ -237,7 +278,8 @@ func (check *Checker) exprList(elist []syntax.Expr, allowCommaOk bool) (xlist [] return } -func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []Type, args []*operand) (rsig *Signature) { +// xlist is the list of type argument expressions supplied in the source code. +func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []Type, args []*operand, xlist []syntax.Expr) (rsig *Signature) { rsig = sig // TODO(gri) try to eliminate this extra verification loop @@ -308,42 +350,63 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // check argument count - switch { - case nargs < npars: - check.errorf(call, "not enough arguments in call to %s", call.Fun) - return - case nargs > npars: - check.errorf(args[npars], "too many arguments in call to %s", call.Fun) // report at first extra argument + if nargs != npars { + var at poser = call + qualifier := "not enough" + if nargs > npars { + at = args[npars].expr // report at first extra argument + qualifier = "too many" + } else if nargs > 0 { + at = args[nargs-1].expr // report at last argument + } + // take care of empty parameter lists represented by nil tuples + var params []*Var + if sig.params != nil { + params = sig.params.vars + } + var err error_ + err.errorf(at, "%s arguments in call to %s", qualifier, call.Fun) + err.errorf(nopos, "have %s", check.typesSummary(operandTypes(args), false)) + err.errorf(nopos, "want %s", check.typesSummary(varTypes(params), sig.variadic)) + check.report(&err) return } // infer type arguments and instantiate signature if necessary - if len(sig.tparams) > 0 { - // TODO(gri) provide position information for targs so we can feed - // it to the instantiate call for better error reporting - targs = check.infer(call.Pos(), sig.tparams, targs, sigParams, args, true) + if sig.TypeParams().Len() > 0 { + if !check.allowVersion(check.pkg, 1, 18) { + if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil { + check.versionErrorf(iexpr.Pos(), "go1.18", "function instantiation") + } else { + check.versionErrorf(call.Pos(), "go1.18", "implicit function instantiation") + } + } + targs := check.infer(call.Pos(), sig.TypeParams().list(), targs, sigParams, args) if targs == nil { return // error already reported } // compute result signature - rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) - assert(rsig.tparams == nil) // signature is not generic anymore - check.recordInferred(call, targs, rsig) + rsig = check.instantiateSignature(call.Pos(), sig, targs, xlist) + assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore + check.recordInstance(call.Fun, targs, rsig) // Optimization: Only if the parameter list was adjusted do we // need to compute it from the adjusted list; otherwise we can // simply use the result signature's parameter list. if adjusted { - sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil, check.context()).(*Tuple) } else { sigParams = rsig.params } } // check arguments - for i, a := range args { - check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun)) + if len(args) > 0 { + context := check.sprintf("argument to %s", call.Fun) + for i, a := range args { + check.assignment(a, sigParams.vars[i].typ, context) + } } return @@ -360,7 +423,7 @@ var cgoPrefixes = [...]string{ "_Cmacro_", // function to evaluate the expanded expression } -func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { +func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *Named) { // these must be declared before the "goto Error" statements var ( obj Object @@ -461,36 +524,47 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { } } - check.exprOrType(x, e.X) - if x.mode == invalid { + check.exprOrType(x, e.X, false) + switch x.mode { + case typexpr: + // don't crash for "type T T.x" (was issue #51509) + if def != nil && x.typ == def { + check.cycleError([]Object{def.obj}) + goto Error + } + case builtin: + check.errorf(e.Pos(), "cannot select on %s", x) + goto Error + case invalid: goto Error } - check.instantiatedOperand(x) - - obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) + obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) if obj == nil { - switch { - case index != nil: + // Don't report another error if the underlying type was invalid (issue #49541). + if under(x.typ) == Typ[Invalid] { + goto Error + } + + if index != nil { // TODO(gri) should provide actual type where the conflict happens check.errorf(e.Sel, "ambiguous selector %s.%s", x.expr, sel) - case indirect: + goto Error + } + + if indirect { check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ) - default: - var why string - if tpar := asTypeParam(x.typ); tpar != nil { - // Type parameter bounds don't specify fields, so don't mention "field". - switch obj := tpar.Bound().obj.(type) { - case nil: - why = check.sprintf("type bound for %s has no method %s", x.typ, sel) - case *TypeName: - why = check.sprintf("interface %s has no method %s", obj.name, sel) - } - } else { - why = check.sprintf("type %s has no field or method %s", x.typ, sel) - } + goto Error + } + var why string + if isInterfacePtr(x.typ) { + why = check.interfacePtrError(x.typ) + } else { + why = check.sprintf("type %s has no field or method %s", x.typ, sel) // Check if capitalization of sel matters and provide better error message in that case. + // TODO(gri) This code only looks at the first character but LookupFieldOrMethod has an + // (internal) mechanism for case-insensitive lookup. Should use that instead. if len(sel) > 0 { var changeCase string if r := rune(sel[0]); unicode.IsUpper(r) { @@ -498,71 +572,18 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { } else { changeCase = string(unicode.ToUpper(r)) + sel[1:] } - if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { + if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { why += ", but does have " + changeCase } } - - check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why) - } + check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why) goto Error } // methods may not have a fully set up signature yet if m, _ := obj.(*Func); m != nil { - // check.dump("### found method %s", m) check.objDecl(m, nil) - // If m has a parameterized receiver type, infer the type arguments from - // the actual receiver provided and then substitute the type parameters in - // the signature accordingly. - // TODO(gri) factor this code out - sig := m.typ.(*Signature) - if len(sig.rparams) > 0 { - // For inference to work, we must use the receiver type - // matching the receiver in the actual method declaration. - // If the method is embedded, the matching receiver is the - // embedded struct or interface that declared the method. - // Traverse the embedding to find that type (issue #44688). - recv := x.typ - for i := 0; i < len(index)-1; i++ { - // The embedded type is either a struct or a pointer to - // a struct except for the last one (which we don't need). - recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ - } - //check.dump("### recv = %s", recv) - //check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams) - // The method may have a pointer receiver, but the actually provided receiver - // may be a (hopefully addressable) non-pointer value, or vice versa. Here we - // only care about inferring receiver type parameters; to make the inference - // work, match up pointer-ness of receiver and argument. - if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) { - if ptrRecv { - recv = NewPointer(recv) - } else { - recv = recv.(*Pointer).base - } - } - // Disable reporting of errors during inference below. If we're unable to infer - // the receiver type arguments here, the receiver must be be otherwise invalid - // and an error has been reported elsewhere. - arg := operand{mode: variable, expr: x.expr, typ: recv} - targs := check.infer(m.pos, sig.rparams, nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) - //check.dump("### inferred targs = %s", targs) - if targs == nil { - // We may reach here if there were other errors (see issue #40056). - goto Error - } - // Don't modify m. Instead - for now - make a copy of m and use that instead. - // (If we modify m, some tests will fail; possibly because the m is in use.) - // TODO(gri) investigate and provide a correct explanation here - copy := *m - copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs)) - obj = © - } - // TODO(gri) we also need to do substitution for parameterized interface methods - // (this breaks code in testdata/linalg.go2 at the moment) - // 12/20/2019: Is this TODO still correct? } if x.mode == typexpr { @@ -576,17 +597,37 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) - // the receiver type becomes the type of the first function - // argument of the method expression's function type - var params []*Var sig := m.typ.(*Signature) + if sig.recv == nil { + check.error(e, "illegal cycle in method declaration") + goto Error + } + + // The receiver type becomes the type of the first function + // argument of the method expression's function type. + var params []*Var if sig.params != nil { params = sig.params.vars } + // Be consistent about named/unnamed parameters. This is not needed + // for type-checking, but the newly constructed signature may appear + // in an error message and then have mixed named/unnamed parameters. + // (An alternative would be to not print parameter names in errors, + // but it's useful to see them; this is cheap and method expressions + // are rare.) + name := "" + if len(params) > 0 && params[0].name != "" { + // name needed + name = sig.recv.name + if name == "" { + name = "_" + } + } + params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...) x.mode = value x.typ = &Signature{ tparams: sig.tparams, - params: NewTuple(append([]*Var{NewVar(nopos, check.pkg, "_", x.typ)}, params...)...), + params: NewTuple(params...), results: sig.results, variadic: sig.variadic, } @@ -641,56 +682,20 @@ Error: func (check *Checker) use(arg ...syntax.Expr) { var x operand for _, e := range arg { - // Certain AST fields may legally be nil (e.g., the ast.SliceExpr.High field). - if e == nil { - continue - } - if l, _ := e.(*syntax.ListExpr); l != nil { - check.use(l.ElemList...) + switch n := e.(type) { + case nil: + // some AST fields may be nil (e.g., elements of syntax.SliceExpr.Index) + // TODO(gri) can those fields really make it here? continue - } - check.rawExpr(&x, e, nil) - } -} - -// useLHS is like use, but doesn't "use" top-level identifiers. -// It should be called instead of use if the arguments are -// expressions on the lhs of an assignment. -// The arguments must not be nil. -func (check *Checker) useLHS(arg ...syntax.Expr) { - var x operand - for _, e := range arg { - // If the lhs is an identifier denoting a variable v, this assignment - // is not a 'use' of v. Remember current value of v.used and restore - // after evaluating the lhs via check.rawExpr. - var v *Var - var v_used bool - if ident, _ := unparen(e).(*syntax.Name); ident != nil { - // never type-check the blank name on the lhs - if ident.Value == "_" { + case *syntax.Name: + // don't report an error evaluating blank + if n.Value == "_" { continue } - if _, obj := check.scope.LookupParent(ident.Value, nopos); obj != nil { - // It's ok to mark non-local variables, but ignore variables - // from other packages to avoid potential race conditions with - // dot-imported variables. - if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg { - v = w - v_used = v.used - } - } - } - check.rawExpr(&x, e, nil) - if v != nil { - v.used = v_used // restore v.used + case *syntax.ListExpr: + check.use(n.ElemList...) + continue } - } -} - -// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid]. -func (check *Checker) instantiatedOperand(x *operand) { - if x.mode == typexpr && isGeneric(x.typ) { - check.errorf(x, "cannot use generic type %s without instantiation", x.typ) - x.typ = Typ[Invalid] + check.rawExpr(&x, e, nil, false) } } diff --git a/src/cmd/compile/internal/types2/chan.go b/src/cmd/compile/internal/types2/chan.go new file mode 100644 index 00000000000000..77650dfb09daad --- /dev/null +++ b/src/cmd/compile/internal/types2/chan.go @@ -0,0 +1,35 @@ +// 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 types2 + +// A Chan represents a channel type. +type Chan struct { + dir ChanDir + elem Type +} + +// A ChanDir value indicates a channel direction. +type ChanDir int + +// The direction of a channel is indicated by one of these constants. +const ( + SendRecv ChanDir = iota + SendOnly + RecvOnly +) + +// NewChan returns a new channel type for the given direction and element type. +func NewChan(dir ChanDir, elem Type) *Chan { + return &Chan{dir: dir, elem: elem} +} + +// Dir returns the direction of channel c. +func (c *Chan) Dir() ChanDir { return c.dir } + +// Elem returns the element type of channel c. +func (c *Chan) Elem() Type { return c.elem } + +func (c *Chan) Underlying() Type { return c } +func (c *Chan) String() string { return TypeString(c, nil) } diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 8d6cd1edab9d4c..ff8ae3bc7e8478 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -18,19 +18,6 @@ var nopos syntax.Pos // debugging/development support const debug = false // leave on during development -// If forceStrict is set, the type-checker enforces additional -// rules not specified by the Go 1 spec, but which will -// catch guaranteed run-time errors if the respective -// code is executed. In other words, programs passing in -// strict mode are Go 1 compliant, but not all Go 1 programs -// will pass in strict mode. The additional rules are: -// -// - A type assertion x.(T) where T is an interface type -// is invalid if any (statically known) method that exists -// for both x and T have different signatures. -// -const forceStrict = false - // exprInfo stores information about an untyped expression. type exprInfo struct { isLhs bool // expression is lhs operand of a shift with delayed type-check @@ -39,22 +26,24 @@ type exprInfo struct { val constant.Value // constant value; or nil (if not a constant) } -// A context represents the context within which an object is type-checked. -type context struct { +// An environment represents the environment within which an object is +// type-checked. +type environment struct { decl *declInfo // package-level declaration whose init expression/function body is checked scope *Scope // top-most scope for lookups pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) iota constant.Value // value of iota in a constant declaration; nil otherwise errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer + inTParamList bool // set if inside a type parameter list sig *Signature // function signature if inside a function; nil otherwise isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check) hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions hasCallOrRecv bool // set if an expression contains a function call or channel receive operation } -// lookup looks up name in the current context and returns the matching object, or nil. -func (ctxt *context) lookup(name string) Object { - _, obj := ctxt.scope.LookupParent(name, ctxt.pos) +// lookup looks up name in the current environment and returns the matching object, or nil. +func (env *environment) lookup(name string) Object { + _, obj := env.scope.LookupParent(name, env.pos) return obj } @@ -71,7 +60,29 @@ type importKey struct { // A dotImportKey describes a dot-imported object in the given scope. type dotImportKey struct { scope *Scope - obj Object + name string +} + +// An action describes a (delayed) action. +type action struct { + f func() // action to be executed + desc *actionDesc // action description; may be nil, requires debug to be set +} + +// If debug is set, describef sets a printf-formatted description for action a. +// Otherwise, it is a no-op. +func (a *action) describef(pos poser, format string, args ...interface{}) { + if debug { + a.desc = &actionDesc{pos, format, args} + } +} + +// An actionDesc provides information on an action. +// For debugging only. +type actionDesc struct { + pos poser + format string + args []interface{} } // A Checker maintains the state of the type checker. @@ -80,13 +91,14 @@ type Checker struct { // package information // (initialized by NewChecker, valid for the life-time of checker) conf *Config + ctxt *Context // context for de-duplicating instances pkg *Package *Info - version version // accepted language version - objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info - impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - posMap map[*Interface][]syntax.Pos // maps interface types to lists of embedded interface positions - typMap map[string]*Named // maps an instantiated named type hash to a *Named type + version version // accepted language version + nextID uint64 // unique Id for type parameters (first valid Id is 1) + objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info + impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package + valids instanceLookup // valid *Named (incl. instantiated) types per the validType check // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -101,19 +113,24 @@ type Checker struct { // information collected during type-checking of a set of package files // (initialized by Files, valid only for the duration of check.Files; // maps and lists are allocated on demand) - files []*syntax.File // list of package files - imports []*PkgName // list of imported packages - dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through + files []*syntax.File // list of package files + imports []*PkgName // list of imported packages + dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through + recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type + brokenAliases map[*TypeName]bool // set of aliases with broken (not yet determined) types + unionTypeSets map[*Union]*_TypeSet // computed type sets for union types + mono monoGraph // graph for detecting non-monomorphizable instantiation loops firstErr error // first error encountered methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods untyped map[syntax.Expr]exprInfo // map of expressions without final type - delayed []func() // stack of delayed action segments; segments are processed in FIFO order + delayed []action // stack of delayed action segments; segments are processed in FIFO order objPath []Object // path of object dependencies during type inference (for cycle reporting) + cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking - // context within which the current object is type-checked - // (valid only for the duration of type-checking a specific object) - context + // environment within which the current object is type-checked (valid only + // for the duration of type-checking a specific object) + environment // debugging indent int // indentation for tracing @@ -131,6 +148,27 @@ func (check *Checker) addDeclDep(to Object) { from.addDep(to) } +// brokenAlias records that alias doesn't have a determined type yet. +// It also sets alias.typ to Typ[Invalid]. +func (check *Checker) brokenAlias(alias *TypeName) { + if check.brokenAliases == nil { + check.brokenAliases = make(map[*TypeName]bool) + } + check.brokenAliases[alias] = true + alias.typ = Typ[Invalid] +} + +// validAlias records that alias has the valid type typ (possibly Typ[Invalid]). +func (check *Checker) validAlias(alias *TypeName, typ Type) { + delete(check.brokenAliases, alias) + alias.typ = typ +} + +// isBrokenAlias reports whether alias doesn't have a determined type yet. +func (check *Checker) isBrokenAlias(alias *TypeName) bool { + return alias.typ == Typ[Invalid] && check.brokenAliases[alias] +} + func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { m := check.untyped if m == nil { @@ -144,8 +182,12 @@ func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, // either at the end of the current statement, or in case of a local constant // or variable declaration, before the constant or variable is in scope // (so that f still sees the scope before any new declarations). -func (check *Checker) later(f func()) { - check.delayed = append(check.delayed, f) +// later returns the pushed action so one can provide a description +// via action.describef for debugging, if desired. +func (check *Checker) later(f func()) *action { + i := len(check.delayed) + check.delayed = append(check.delayed, action{f: f}) + return &check.delayed[i] } // push pushes obj onto the object path and returns its index in the path. @@ -163,6 +205,16 @@ func (check *Checker) pop() Object { return obj } +type cleaner interface { + cleanup() +} + +// needsCleanup records objects/types that implement the cleanup method +// which will be called at the end of type-checking. +func (check *Checker) needsCleanup(c cleaner) { + check.cleaners = append(check.cleaners, c) +} + // NewChecker returns a new Checker instance for a given package. // Package files may be added incrementally via checker.Files. func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { @@ -183,13 +235,12 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { return &Checker{ conf: conf, + ctxt: conf.Context, pkg: pkg, Info: info, version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - posMap: make(map[*Interface][]syntax.Pos), - typMap: make(map[string]*Named), } } @@ -205,6 +256,8 @@ func (check *Checker) initFiles(files []*syntax.File) { check.methods = nil check.untyped = nil check.delayed = nil + check.objPath = nil + check.cleaners = nil // determine package name and collect valid files pkg := check.pkg @@ -256,6 +309,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) { print := func(msg string) { if check.conf.Trace { + fmt.Println() fmt.Println(msg) } } @@ -272,6 +326,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) { print("== processDelayed ==") check.processDelayed(0) // incl. all functions + print("== cleanup ==") + check.cleanup() + print("== initOrder ==") check.initOrder() @@ -283,9 +340,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) { print("== recordUntyped ==") check.recordUntyped() - if check.Info != nil { - print("== sanitizeInfo ==") - sanitizeInfo(check.Info) + if check.firstErr == nil { + // TODO(mdempsky): Ensure monomorph is safe when errors exist. + check.monomorph() } check.pkg.complete = true @@ -295,6 +352,10 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) { check.dotImportMap = nil check.pkgPathMap = nil check.seenPkgMap = nil + check.recvTParamMap = nil + check.brokenAliases = nil + check.unionTypeSets = nil + check.ctxt = nil // TODO(gri) There's more memory we should release at this point. @@ -310,12 +371,32 @@ func (check *Checker) processDelayed(top int) { // add more actions (such as nested functions), so // this is a sufficiently bounded process. for i := top; i < len(check.delayed); i++ { - check.delayed[i]() // may append to check.delayed + a := &check.delayed[i] + if check.conf.Trace { + if a.desc != nil { + check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...) + } else { + check.trace(nopos, "-- delayed %p", a.f) + } + } + a.f() // may append to check.delayed + if check.conf.Trace { + fmt.Println() + } } assert(top <= len(check.delayed)) // stack must not have shrunk check.delayed = check.delayed[:top] } +// cleanup runs cleanup for all collected cleaners. +func (check *Checker) cleanup() { + // Don't use a range clause since Named.cleanup may add more cleaners. + for i := 0; i < len(check.cleaners); i++ { + check.cleaners[i].cleanup() + } + check.cleaners = nil +} + func (check *Checker) record(x *operand) { // convert x into a user-friendly set of values // TODO(gri) this code can be simplified @@ -365,9 +446,9 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty } if mode == constant_ { assert(val != nil) - // We check is(typ, IsConstType) here as constant expressions may be + // We check allBasic(typ, IsConstType) here as constant expressions may be // recorded as type parameters. - assert(typ == Typ[Invalid] || is(typ, IsConstType)) + assert(typ == Typ[Invalid] || allBasic(typ, IsConstType)) } if m := check.Types; m != nil { m[x] = TypeAndValue{mode, typ, val} @@ -418,12 +499,36 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) { } } -func (check *Checker) recordInferred(call syntax.Expr, targs []Type, sig *Signature) { - assert(call != nil) - assert(sig != nil) - if m := check.Inferred; m != nil { - m[call] = Inferred{targs, sig} +// recordInstance records instantiation information into check.Info, if the +// Instances map is non-nil. The given expr must be an ident, selector, or +// index (list) expr with ident or selector operand. +// +// TODO(rfindley): the expr parameter is fragile. See if we can access the +// instantiated identifier in some other way. +func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) { + ident := instantiatedIdent(expr) + assert(ident != nil) + assert(typ != nil) + if m := check.Instances; m != nil { + m[ident] = Instance{newTypeList(targs), typ} + } +} + +func instantiatedIdent(expr syntax.Expr) *syntax.Name { + var selOrIdent syntax.Expr + switch e := expr.(type) { + case *syntax.IndexExpr: + selOrIdent = e.X + case *syntax.SelectorExpr, *syntax.Name: + selOrIdent = e + } + switch x := selOrIdent.(type) { + case *syntax.Name: + return x + case *syntax.SelectorExpr: + return x.Sel } + panic("instantiated ident not found") } func (check *Checker) recordDef(id *syntax.Name, obj Object) { diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go index 41b0c54702d8d7..98813ad5a76955 100644 --- a/src/cmd/compile/internal/types2/check_test.go +++ b/src/cmd/compile/internal/types2/check_test.go @@ -20,14 +20,13 @@ // _ = x /* ERROR "not declared" */ + 1 // } -// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */ -// and test against strict mode. - package types2_test import ( + "bytes" "cmd/compile/internal/syntax" "flag" + "fmt" "internal/testenv" "os" "path/filepath" @@ -82,18 +81,45 @@ func delta(x, y uint) uint { } } -// goVersionRx matches a Go version string using '_', e.g. "go1_12". -var goVersionRx = regexp.MustCompile(`^go[1-9][0-9]*_(0|[1-9][0-9]*)$`) +// Note: parseFlags is identical to the version in go/types which is +// why it has a src argument even though here it is always nil. + +// parseFlags parses flags from the first line of the given source +// (from src if present, or by reading from the file) if the line +// starts with "//" (line comment) followed by "-" (possibly with +// spaces between). Otherwise the line is ignored. +func parseFlags(filename string, src []byte, flags *flag.FlagSet) error { + // If there is no src, read from the file. + const maxLen = 256 + if len(src) == 0 { + f, err := os.Open(filename) + if err != nil { + return err + } + + var buf [maxLen]byte + n, err := f.Read(buf[:]) + if err != nil { + return err + } + src = buf[:n] + } -// asGoVersion returns a regular Go language version string -// if s is a Go version string using '_' rather than '.' to -// separate the major and minor version numbers (e.g. "go1_12"). -// Otherwise it returns the empty string. -func asGoVersion(s string) string { - if goVersionRx.MatchString(s) { - return strings.Replace(s, "_", ".", 1) + // we must have a line comment that starts with a "-" + const prefix = "//" + if !bytes.HasPrefix(src, []byte(prefix)) { + return nil // first line is not a line comment + } + src = src[len(prefix):] + if i := bytes.Index(src, []byte("-")); i < 0 || len(bytes.TrimSpace(src[:i])) != 0 { + return nil // comment doesn't start with a "-" + } + end := bytes.Index(src, []byte("\n")) + if end < 0 || end > maxLen { + return fmt.Errorf("flags comment line too long") } - return "" + + return flags.Parse(strings.Fields(string(src[:end]))) } func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) { @@ -101,24 +127,21 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) { t.Fatal("no source files") } - var mode syntax.Mode - if strings.HasSuffix(filenames[0], ".go2") { - mode |= syntax.AllowGenerics + var conf Config + flags := flag.NewFlagSet("", flag.PanicOnError) + flags.StringVar(&conf.GoVersion, "lang", "", "") + flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "") + if err := parseFlags(filenames[0], nil, flags); err != nil { + t.Fatal(err) } - // parse files and collect parser errors - files, errlist := parseFiles(t, filenames, mode) + + files, errlist := parseFiles(t, filenames, 0) pkgName := "" if len(files) > 0 { pkgName = files[0].PkgName.Value } - // if no Go version is given, consider the package name - goVersion := *goVersion - if goVersion == "" { - goVersion = asGoVersion(pkgName) - } - listErrors := manual && !*verifyErrors if listErrors && len(errlist) > 0 { t.Errorf("--- %s:", pkgName) @@ -128,12 +151,6 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) { } // typecheck and collect typechecker errors - var conf Config - conf.GoVersion = goVersion - // special case for importC.src - if len(filenames) == 1 && strings.HasSuffix(filenames[0], "importC.src") { - conf.FakeImportC = true - } conf.Trace = manual && testing.Verbose() conf.Importer = defaultImporter() conf.Error = func(err error) { @@ -246,9 +263,9 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) { // (and a separating "--"). For instance, to test the package made // of the files foo.go and bar.go, use: // -// go test -run Manual -- foo.go bar.go +// go test -run Manual -- foo.go bar.go // -// If no source arguments are provided, the file testdata/manual.go2 +// If no source arguments are provided, the file testdata/manual.go // is used instead. // Provide the -verify flag to verify errors against ERROR comments // in the input files rather than having a list of errors reported. @@ -259,7 +276,7 @@ func TestManual(t *testing.T) { filenames := flag.Args() if len(filenames) == 0 { - filenames = []string{filepath.FromSlash("testdata/manual.go2")} + filenames = []string{filepath.FromSlash("testdata/manual.go")} } info, err := os.Stat(filenames[0]) @@ -280,9 +297,18 @@ func TestManual(t *testing.T) { // TODO(gri) go/types has extra TestLongConstants and TestIndexRepresentability tests -func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", 75, false) } // TODO(gri) narrow column tolerance -func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", 0, false) } -func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", 0, false) } +func TestCheck(t *testing.T) { + DefPredeclaredTestFuncs() + testDirFiles(t, "../../../../internal/types/testdata/check", 50, false) // TODO(gri) narrow column tolerance +} +func TestSpec(t *testing.T) { testDirFiles(t, "../../../../internal/types/testdata/spec", 0, false) } +func TestExamples(t *testing.T) { + testDirFiles(t, "../../../../internal/types/testdata/examples", 45, false) +} // TODO(gri) narrow column tolerance +func TestFixedbugs(t *testing.T) { + testDirFiles(t, "../../../../internal/types/testdata/fixedbugs", 100, false) +} // TODO(gri) narrow column tolerance +func TestLocal(t *testing.T) { testDirFiles(t, "testdata/local", 0, false) } func testDirFiles(t *testing.T, dir string, colDelta uint, manual bool) { testenv.MustHaveGoBuild(t) diff --git a/src/cmd/compile/internal/types2/compilersupport.go b/src/cmd/compile/internal/types2/compilersupport.go new file mode 100644 index 00000000000000..33dd8e8baace43 --- /dev/null +++ b/src/cmd/compile/internal/types2/compilersupport.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Helper functions exported for the compiler. +// Do not use internally. + +package types2 + +// If t is a pointer, AsPointer returns that type, otherwise it returns nil. +func AsPointer(t Type) *Pointer { + u, _ := t.Underlying().(*Pointer) + return u +} + +// If t is a signature, AsSignature returns that type, otherwise it returns nil. +func AsSignature(t Type) *Signature { + u, _ := t.Underlying().(*Signature) + return u +} + +// If typ is a type parameter, CoreType returns the single underlying +// type of all types in the corresponding type constraint if it exists, or +// nil otherwise. If the type set contains only unrestricted and restricted +// channel types (with identical element types), the single underlying type +// is the restricted channel type if the restrictions are always the same. +// If typ is not a type parameter, CoreType returns the underlying type. +func CoreType(t Type) Type { + return coreType(t) +} diff --git a/src/cmd/compile/internal/types2/context.go b/src/cmd/compile/internal/types2/context.go new file mode 100644 index 00000000000000..ae39c7b83097a9 --- /dev/null +++ b/src/cmd/compile/internal/types2/context.go @@ -0,0 +1,144 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "sync" +) + +// This file contains a definition of the type-checking context; an opaque type +// that may be supplied by users during instantiation. +// +// Contexts serve two purposes: +// - reduce the duplication of identical instances +// - short-circuit instantiation cycles +// +// For the latter purpose, we must always have a context during instantiation, +// whether or not it is supplied by the user. For both purposes, it must be the +// case that hashing a pointer-identical type produces consistent results +// (somewhat obviously). +// +// However, neither of these purposes require that our hash is perfect, and so +// this was not an explicit design goal of the context type. In fact, due to +// concurrent use it is convenient not to guarantee de-duplication. +// +// Nevertheless, in the future it could be helpful to allow users to leverage +// contexts to canonicalize instances, and it would probably be possible to +// achieve such a guarantee. + +// A Context is an opaque type checking context. It may be used to share +// identical type instances across type-checked packages or calls to +// Instantiate. Contexts are safe for concurrent use. +// +// The use of a shared context does not guarantee that identical instances are +// deduplicated in all cases. +type Context struct { + mu sync.Mutex + typeMap map[string][]ctxtEntry // type hash -> instances entries + nextID int // next unique ID + originIDs map[Type]int // origin type -> unique ID +} + +type ctxtEntry struct { + orig Type + targs []Type + instance Type // = orig[targs] +} + +// NewContext creates a new Context. +func NewContext() *Context { + return &Context{ + typeMap: make(map[string][]ctxtEntry), + originIDs: make(map[Type]int), + } +} + +// instanceHash returns a string representation of typ instantiated with targs. +// The hash should be a perfect hash, though out of caution the type checker +// does not assume this. The result is guaranteed to not contain blanks. +func (ctxt *Context) instanceHash(orig Type, targs []Type) string { + assert(ctxt != nil) + assert(orig != nil) + var buf bytes.Buffer + + h := newTypeHasher(&buf, ctxt) + h.string(strconv.Itoa(ctxt.getID(orig))) + // Because we've already written the unique origin ID this call to h.typ is + // unnecessary, but we leave it for hash readability. It can be removed later + // if performance is an issue. + h.typ(orig) + if len(targs) > 0 { + // TODO(rfindley): consider asserting on isGeneric(typ) here, if and when + // isGeneric handles *Signature types. + h.typeList(targs) + } + + return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 +} + +// lookup returns an existing instantiation of orig with targs, if it exists. +// Otherwise, it returns nil. +func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type { + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + + for _, e := range ctxt.typeMap[h] { + if identicalInstance(orig, targs, e.orig, e.targs) { + return e.instance + } + if debug { + // Panic during development to surface any imperfections in our hash. + panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance)) + } + } + + return nil +} + +// update de-duplicates n against previously seen types with the hash h. If an +// identical type is found with the type hash h, the previously seen type is +// returned. Otherwise, n is returned, and recorded in the Context for the hash +// h. +func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type { + assert(inst != nil) + + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + + for _, e := range ctxt.typeMap[h] { + if inst == nil || Identical(inst, e.instance) { + return e.instance + } + if debug { + // Panic during development to surface any imperfections in our hash. + panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance)) + } + } + + ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{ + orig: orig, + targs: targs, + instance: inst, + }) + + return inst +} + +// getID returns a unique ID for the type t. +func (ctxt *Context) getID(t Type) int { + ctxt.mu.Lock() + defer ctxt.mu.Unlock() + id, ok := ctxt.originIDs[t] + if !ok { + id = ctxt.nextID + ctxt.originIDs[t] = id + ctxt.nextID++ + } + return id +} diff --git a/src/cmd/compile/internal/types2/context_test.go b/src/cmd/compile/internal/types2/context_test.go new file mode 100644 index 00000000000000..aa649b14481f76 --- /dev/null +++ b/src/cmd/compile/internal/types2/context_test.go @@ -0,0 +1,69 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "testing" +) + +func TestContextHashCollisions(t *testing.T) { + if debug { + t.Skip("hash collisions are expected, and would fail debug assertions") + } + // Unit test the de-duplication fall-back logic in Context. + // + // We can't test this via Instantiate because this is only a fall-back in + // case our hash is imperfect. + // + // These lookups and updates use reasonable looking types in an attempt to + // make them robust to internal type assertions, but could equally well use + // arbitrary types. + + // Create some distinct origin types. nullaryP and nullaryQ have no + // parameters and are identical (but have different type parameter names). + // unaryP has a parameter. + var nullaryP, nullaryQ, unaryP Type + { + // type nullaryP = func[P any]() + tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface) + nullaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false) + } + { + // type nullaryQ = func[Q any]() + tparam := NewTypeParam(NewTypeName(nopos, nil, "Q", nil), &emptyInterface) + nullaryQ = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false) + } + { + // type unaryP = func[P any](_ P) + tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface) + params := NewTuple(NewVar(nopos, nil, "_", tparam)) + unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false) + } + + ctxt := NewContext() + + // Update the context with an instantiation of nullaryP. + inst := NewSignatureType(nil, nil, nil, nil, nil, false) + if got := ctxt.update("", nullaryP, []Type{Typ[Int]}, inst); got != inst { + t.Error("bad") + } + + // unaryP is not identical to nullaryP, so we should not get inst when + // instantiated with identical type arguments. + if got := ctxt.lookup("", unaryP, []Type{Typ[Int]}); got != nil { + t.Error("bad") + } + + // nullaryQ is identical to nullaryP, so we *should* get inst when + // instantiated with identical type arguments. + if got := ctxt.lookup("", nullaryQ, []Type{Typ[Int]}); got != inst { + t.Error("bad") + } + + // ...but verify we don't get inst with different type arguments. + if got := ctxt.lookup("", nullaryQ, []Type{Typ[String]}); got != nil { + t.Error("bad") + } +} diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go index 30201e2b7f4232..d15645499b33f6 100644 --- a/src/cmd/compile/internal/types2/conversions.go +++ b/src/cmd/compile/internal/types2/conversions.go @@ -7,6 +7,7 @@ package types2 import ( + "fmt" "go/constant" "unicode" ) @@ -16,32 +17,77 @@ import ( func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant_ - var ok bool - switch { - case constArg && isConstType(T): - // constant conversion - switch t := asBasic(T); { - case representableConst(x.val, check, t, &x.val): - ok = true + constConvertibleTo := func(T Type, val *constant.Value) bool { + switch t, _ := under(T).(*Basic); { + case t == nil: + // nothing to do + case representableConst(x.val, check, t, val): + return true case isInteger(x.typ) && isString(t): codepoint := unicode.ReplacementChar if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune { codepoint = rune(i) } - x.val = constant.MakeString(string(codepoint)) - ok = true + if val != nil { + *val = constant.MakeString(string(codepoint)) + } + return true } - case x.convertibleTo(check, T): + return false + } + + var ok bool + var cause string + switch { + case constArg && isConstType(T): + // constant conversion + ok = constConvertibleTo(T, &x.val) + case constArg && isTypeParam(T): + // x is convertible to T if it is convertible + // to each specific type in the type set of T. + // If T's type set is empty, or if it doesn't + // have specific types, constant x cannot be + // converted. + ok = T.(*TypeParam).underIs(func(u Type) bool { + // u is nil if there are no specific type terms + if u == nil { + cause = check.sprintf("%s does not contain specific types", T) + return false + } + if isString(x.typ) && isBytesOrRunes(u) { + return true + } + if !constConvertibleTo(u, nil) { + cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T) + return false + } + return true + }) + x.mode = value // type parameters are not constants + case x.convertibleTo(check, T, &cause): // non-constant conversion - x.mode = value ok = true + x.mode = value } if !ok { - if x.mode != invalid { - check.errorf(x, "cannot convert %s to %s", x, T) - x.mode = invalid + var err error_ + if check.conf.CompilerErrorMessages { + if cause != "" { + // Add colon at end of line if we have a following cause. + err.errorf(x, "cannot convert %s to type %s:", x, T) + err.errorf(nopos, cause) + } else { + err.errorf(x, "cannot convert %s to type %s", x, T) + } + } else { + err.errorf(x, "cannot convert %s to %s", x, T) + if cause != "" { + err.errorf(nopos, cause) + } } + check.report(&err) + x.mode = invalid return } @@ -55,13 +101,13 @@ func (check *Checker) conversion(x *operand, T Type) { // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). - // - For integer to string conversions, keep the argument type. + // - For constant integer to string conversions, keep the argument type. // (See also the TODO below.) if x.typ == Typ[UntypedNil] { // ok - } else if IsInterface(T) || constArg && !isConstType(T) { + } else if isNonTypeParamInterface(T) || constArg && !isConstType(T) { final = Default(x.typ) - } else if isInteger(x.typ) && isString(T) { + } else if x.mode == constant_ && isInteger(x.typ) && allString(T) { final = x.typ } check.updateExprType(x.expr, final, true) @@ -80,108 +126,189 @@ func (check *Checker) conversion(x *operand, T Type) { // is tricky because we'd have to run updateExprType on the argument first. // (Issue #21982.) -// convertibleTo reports whether T(x) is valid. +// convertibleTo reports whether T(x) is valid. In the failure case, *cause +// may be set to the cause for the failure. // The check parameter may be nil if convertibleTo is invoked through an // exported API call, i.e., when all methods have been type-checked. -func (x *operand) convertibleTo(check *Checker, T Type) bool { +func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool { // "x is assignable to T" - if ok, _ := x.assignableTo(check, T, nil); ok { + if ok, _ := x.assignableTo(check, T, cause); ok { return true } - // "x's type and T have identical underlying types if tags are ignored" + // "V and T have identical underlying types if tags are ignored + // and V and T are not type parameters" V := x.typ Vu := under(V) Tu := under(T) - if check.identicalIgnoreTags(Vu, Tu) { + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) + if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil { return true } - // "x's type and T are unnamed pointer types and their pointer base types - // have identical underlying types if tags are ignored" + // "V and T are unnamed pointer types and their pointer base types + // have identical underlying types if tags are ignored + // and their pointer base types are not type parameters" if V, ok := V.(*Pointer); ok { if T, ok := T.(*Pointer); ok { - if check.identicalIgnoreTags(under(V.base), under(T.base)) { + if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) { return true } } } - // "x's type and T are both integer or floating point types" - if isIntegerOrFloat(V) && isIntegerOrFloat(T) { + // "V and T are both integer or floating point types" + if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) { return true } - // "x's type and T are both complex types" - if isComplex(V) && isComplex(T) { + // "V and T are both complex types" + if isComplex(Vu) && isComplex(Tu) { return true } - // "x is an integer or a slice of bytes or runes and T is a string type" - if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) { + // "V is an integer or a slice of bytes or runes and T is a string type" + if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) { return true } - // "x is a string and T is a slice of bytes or runes" - if isString(V) && isBytesOrRunes(Tu) { + // "V is a string and T is a slice of bytes or runes" + if isString(Vu) && isBytesOrRunes(Tu) { return true } // package unsafe: // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" - if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) { + if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) { return true } // "and vice versa" - if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) { + if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) { return true } - // "x is a slice, T is a pointer-to-array type, + // "V is a slice, T is an array or pointer-to-array type, // and the slice and array types have identical element types." - if s := asSlice(V); s != nil { - if p := asPointer(T); p != nil { - if a := asArray(p.Elem()); a != nil { - if check.identical(s.Elem(), a.Elem()) { + if s, _ := Vu.(*Slice); s != nil { + switch a := Tu.(type) { + case *Array: + if Identical(s.Elem(), a.Elem()) { + if check == nil || check.allowVersion(check.pkg, 1, 20) { + return true + } + // check != nil + if cause != nil { + // TODO(gri) consider restructuring versionErrorf so we can use it here and below + *cause = "conversion of slices to arrays requires go1.20 or later" + if check.conf.CompilerErrorMessages { + *cause += fmt.Sprintf(" (-lang was set to %s; check go.mod)", check.conf.GoVersion) + } + } + return false + } + case *Pointer: + if a, _ := under(a.Elem()).(*Array); a != nil { + if Identical(s.Elem(), a.Elem()) { if check == nil || check.allowVersion(check.pkg, 1, 17) { return true } // check != nil - if check.conf.CompilerErrorMessages { - check.error(x, "conversion of slices to array pointers only supported as of -lang=go1.17") - } else { - check.error(x, "conversion of slices to array pointers requires go1.17 or later") + if cause != nil { + *cause = "conversion of slices to array pointers requires go1.17 or later" + if check.conf.CompilerErrorMessages { + *cause += fmt.Sprintf(" (-lang was set to %s; check go.mod)", check.conf.GoVersion) + } } - x.mode = invalid // avoid follow-up error + return false } } } } + // optimization: if we don't have type parameters, we're done + if Vp == nil && Tp == nil { + return false + } + + errorf := func(format string, args ...interface{}) { + if check != nil && cause != nil { + msg := check.sprintf(format, args...) + if *cause != "" { + msg += "\n\t" + *cause + } + *cause = msg + } + } + + // generic cases with specific type terms + // (generic operands cannot be constants, so we can ignore x.val) + switch { + case Vp != nil && Tp != nil: + x := *x // don't clobber outer x + return Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + return Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + if !x.convertibleTo(check, T.typ, cause) { + errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp) + return false + } + return true + }) + }) + case Vp != nil: + x := *x // don't clobber outer x + return Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + if !x.convertibleTo(check, T, cause) { + errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T) + return false + } + return true + }) + case Tp != nil: + return Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + if !x.convertibleTo(check, T.typ, cause) { + errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp) + return false + } + return true + }) + } + return false } func isUintptr(typ Type) bool { - t := asBasic(typ) + t, _ := under(typ).(*Basic) return t != nil && t.kind == Uintptr } func isUnsafePointer(typ Type) bool { - // TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct? - // (The former calls under(), while the latter doesn't.) - // The spec does not say so, but gc claims it is. See also - // issue 6326. - t := asBasic(typ) + t, _ := under(typ).(*Basic) return t != nil && t.kind == UnsafePointer } func isPointer(typ Type) bool { - return asPointer(typ) != nil + _, ok := under(typ).(*Pointer) + return ok } func isBytesOrRunes(typ Type) bool { - if s := asSlice(typ); s != nil { - t := asBasic(s.elem) + if s, _ := under(typ).(*Slice); s != nil { + t, _ := under(s.elem).(*Basic) return t != nil && (t.kind == Byte || t.kind == Rune) } return false diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 1333e4c0eca323..1db28fc002414d 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -5,6 +5,7 @@ package types2 import ( + "bytes" "cmd/compile/internal/syntax" "fmt" "go/constant" @@ -51,7 +52,7 @@ func pathString(path []Object) string { return s } -// objDecl type-checks the declaration of obj in its respective (file) context. +// objDecl type-checks the declaration of obj in its respective (file) environment. // For the meaning of def, see Checker.definedType, in typexpr.go. func (check *Checker) objDecl(obj Object, def *Named) { if check.conf.Trace && obj.Type() == nil { @@ -118,7 +119,7 @@ func (check *Checker) objDecl(obj Object, def *Named) { fallthrough case grey: - // We have a cycle. + // We have a (possibly invalid) cycle. // In the existing code, this is marked by a non-nil type // for the object except for constants and variables whose // type may be non-nil (known), or nil if it depends on the @@ -130,17 +131,17 @@ func (check *Checker) objDecl(obj Object, def *Named) { // order code. switch obj := obj.(type) { case *Const: - if check.cycle(obj) || obj.typ == nil { + if !check.validCycle(obj) || obj.typ == nil { obj.typ = Typ[Invalid] } case *Var: - if check.cycle(obj) || obj.typ == nil { + if !check.validCycle(obj) || obj.typ == nil { obj.typ = Typ[Invalid] } case *TypeName: - if check.cycle(obj) { + if !check.validCycle(obj) { // break cycle // (without this, calling underlying() // below may lead to an endless loop @@ -150,7 +151,7 @@ func (check *Checker) objDecl(obj Object, def *Named) { } case *Func: - if check.cycle(obj) { + if !check.validCycle(obj) { // Don't set obj.typ to Typ[Invalid] here // because plenty of code type-asserts that // functions have a *Signature type. Grey @@ -172,11 +173,11 @@ func (check *Checker) objDecl(obj Object, def *Named) { unreachable() } - // save/restore current context and setup object context - defer func(ctxt context) { - check.context = ctxt - }(check.context) - check.context = context{ + // save/restore current environment and set up object environment + defer func(env environment) { + check.environment = env + }(check.environment) + check.environment = environment{ scope: d.file, } @@ -204,9 +205,9 @@ func (check *Checker) objDecl(obj Object, def *Named) { } } -// cycle checks if the cycle starting with obj is valid and +// validCycle reports whether the cycle starting with obj is valid and // reports an error if it is not. -func (check *Checker) cycle(obj Object) (isCycle bool) { +func (check *Checker) validCycle(obj Object) (valid bool) { // The object map contains the package scope objects and the non-interface methods. if debug { info := check.objMap[obj] @@ -222,13 +223,23 @@ func (check *Checker) cycle(obj Object) (isCycle bool) { assert(obj.color() >= grey) start := obj.color() - grey // index of obj in objPath cycle := check.objPath[start:] - nval := 0 // number of (constant or variable) values in the cycle - ndef := 0 // number of type definitions in the cycle + tparCycle := false // if set, the cycle is through a type parameter list + nval := 0 // number of (constant or variable) values in the cycle; valid if !generic + ndef := 0 // number of type definitions in the cycle; valid if !generic +loop: for _, obj := range cycle { switch obj := obj.(type) { case *Const, *Var: nval++ case *TypeName: + // If we reach a generic type that is part of a cycle + // and we are in a type parameter list, we have a cycle + // through a type parameter list, which is invalid. + if check.inTParamList && isGeneric(obj.typ) { + tparCycle = true + break loop + } + // Determine if the type name is an alias or not. For // package-level objects, use the object map which // provides syntactic information (which doesn't rely @@ -256,130 +267,81 @@ func (check *Checker) cycle(obj Object) (isCycle bool) { if check.conf.Trace { check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle)) - check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef) + if tparCycle { + check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list") + } else { + check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef) + } defer func() { - if isCycle { + if valid { + check.trace(obj.Pos(), "=> cycle is valid") + } else { check.trace(obj.Pos(), "=> error: cycle is invalid") } }() } - // A cycle involving only constants and variables is invalid but we - // ignore them here because they are reported via the initialization - // cycle check. - if nval == len(cycle) { - return false - } - - // A cycle involving only types (and possibly functions) must have at least - // one type definition to be permitted: If there is no type definition, we - // have a sequence of alias type names which will expand ad infinitum. - if nval == 0 && ndef > 0 { - return false // cycle is permitted - } - - check.cycleError(cycle) - - return true -} - -type typeInfo uint - -// validType verifies that the given type does not "expand" infinitely -// producing a cycle in the type graph. Cycles are detected by marking -// defined types. -// (Cycles involving alias types, as in "type A = [10]A" are detected -// earlier, via the objDecl cycle detection mechanism.) -func (check *Checker) validType(typ Type, path []Object) typeInfo { - const ( - unknown typeInfo = iota - marked - valid - invalid - ) - - switch t := typ.(type) { - case *Array: - return check.validType(t.elem, path) - - case *Struct: - for _, f := range t.fields { - if check.validType(f.typ, path) == invalid { - return invalid - } - } - - case *Interface: - for _, etyp := range t.embeddeds { - if check.validType(etyp, path) == invalid { - return invalid - } + if !tparCycle { + // A cycle involving only constants and variables is invalid but we + // ignore them here because they are reported via the initialization + // cycle check. + if nval == len(cycle) { + return true } - case *Named: - // don't touch the type if it is from a different package or the Universe scope - // (doing so would lead to a race condition - was issue #35049) - if t.obj.pkg != check.pkg { - return valid - } - - // don't report a 2nd error if we already know the type is invalid - // (e.g., if a cycle was detected earlier, via under). - if t.underlying == Typ[Invalid] { - t.info = invalid - return invalid - } - - switch t.info { - case unknown: - t.info = marked - t.info = check.validType(t.fromRHS, append(path, t.obj)) // only types of current package added to path - case marked: - // cycle detected - for i, tn := range path { - if t.obj.pkg != check.pkg { - panic("internal error: type cycle via package-external type") - } - if tn == t.obj { - check.cycleError(path[i:]) - t.info = invalid - return t.info - } - } - panic("internal error: cycle start not found") + // A cycle involving only types (and possibly functions) must have at least + // one type definition to be permitted: If there is no type definition, we + // have a sequence of alias type names which will expand ad infinitum. + if nval == 0 && ndef > 0 { + return true } - return t.info - - case *instance: - return check.validType(t.expand(), path) } - return valid + check.cycleError(cycle) + return false } // cycleError reports a declaration cycle starting with // the object in cycle that is "first" in the source. func (check *Checker) cycleError(cycle []Object) { + // name returns the (possibly qualified) object name. + // This is needed because with generic types, cycles + // may refer to imported types. See issue #50788. + // TODO(gri) Thus functionality is used elsewhere. Factor it out. + name := func(obj Object) string { + var buf bytes.Buffer + writePackage(&buf, obj.Pkg(), check.qualifier) + buf.WriteString(obj.Name()) + return buf.String() + } + // TODO(gri) Should we start with the last (rather than the first) object in the cycle // since that is the earliest point in the source where we start seeing the // cycle? That would be more consistent with other error messages. i := firstInSrc(cycle) obj := cycle[i] + objName := name(obj) + // If obj is a type alias, mark it as valid (not broken) in order to avoid follow-on errors. + tname, _ := obj.(*TypeName) + if tname != nil && tname.IsAlias() { + check.validAlias(tname, Typ[Invalid]) + } var err error_ - if check.conf.CompilerErrorMessages { - err.errorf(obj, "invalid recursive type %s", obj.Name()) + if tname != nil && check.conf.CompilerErrorMessages { + err.errorf(obj, "invalid recursive type %s", objName) } else { - err.errorf(obj, "illegal cycle in declaration of %s", obj.Name()) + err.errorf(obj, "illegal cycle in declaration of %s", objName) } for range cycle { - err.errorf(obj, "%s refers to", obj.Name()) + err.errorf(obj, "%s refers to", objName) i++ if i >= len(cycle) { i = 0 } obj = cycle[i] + objName = name(obj) } - err.errorf(obj, "%s", obj.Name()) + err.errorf(obj, "%s", objName) check.report(&err) } @@ -512,84 +474,32 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) { } } - check.initVars(lhs, []syntax.Expr{init}, nopos) -} - -// under returns the expanded underlying type of n0; possibly by following -// forward chains of named types. If an underlying type is found, resolve -// the chain by setting the underlying type for each defined type in the -// chain before returning it. If no underlying type is found or a cycle -// is detected, the result is Typ[Invalid]. If a cycle is detected and -// n0.check != nil, the cycle is reported. -func (n0 *Named) under() Type { - u := n0.underlying - if u == nil { - return Typ[Invalid] - } - - // If the underlying type of a defined type is not a defined - // type, then that is the desired underlying type. - n := asNamed(u) - if n == nil { - return u // common case - } - - // Otherwise, follow the forward chain. - seen := map[*Named]int{n0: 0} - path := []Object{n0.obj} - for { - u = n.underlying - if u == nil { - u = Typ[Invalid] - break - } - n1 := asNamed(u) - if n1 == nil { - break // end of chain - } - - seen[n] = len(seen) - path = append(path, n.obj) - n = n1 - - if i, ok := seen[n]; ok { - // cycle - // TODO(gri) revert this to a method on Checker. Having a possibly - // nil Checker on Named and TypeParam is too subtle. - if n0.check != nil { - n0.check.cycleError(path[i:]) - } - u = Typ[Invalid] - break - } - } - - for n := range seen { - // We should never have to update the underlying type of an imported type; - // those underlying types should have been resolved during the import. - // Also, doing so would lead to a race condition (was issue #31749). - // Do this check always, not just in debug more (it's cheap). - if n0.check != nil && n.obj.pkg != n0.check.pkg { - panic("internal error: imported type with unresolved underlying type") - } - n.underlying = u - } - - return u + check.initVars(lhs, []syntax.Expr{init}, nil) } -func (n *Named) setUnderlying(typ Type) { - if n != nil { - n.underlying = typ +// isImportedConstraint reports whether typ is an imported type constraint. +func (check *Checker) isImportedConstraint(typ Type) bool { + named, _ := typ.(*Named) + if named == nil || named.obj.pkg == check.pkg || named.obj.pkg == nil { + return false } + u, _ := named.under().(*Interface) + return u != nil && !u.IsMethodSet() } func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named) { assert(obj.typ == nil) + var rhs Type check.later(func() { - check.validType(obj.typ, nil) - }) + if t, _ := obj.typ.(*Named); t != nil { // type may be invalid + check.validType(t) + } + // If typ is local, an error was already reported where typ is specified/defined. + if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs) + } + }).describef(obj, "validType(%s)", obj.Name()) alias := tdecl.Alias if alias && tdecl.TParamList != nil { @@ -599,123 +509,125 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named alias = false } + // alias declaration if alias { - // type alias declaration if !check.allowVersion(check.pkg, 1, 9) { - if check.conf.CompilerErrorMessages { - check.error(tdecl, "type aliases only supported as of -lang=go1.9") - } else { - check.error(tdecl, "type aliases requires go1.9 or later") - } + check.versionErrorf(tdecl, "go1.9", "type aliases") } - obj.typ = Typ[Invalid] - obj.typ = check.anyType(tdecl.Type) - - } else { - // defined type declaration + check.brokenAlias(obj) + rhs = check.typ(tdecl.Type) + check.validAlias(obj, rhs) + return + } - named := check.newNamed(obj, nil, nil, nil, nil) - def.setUnderlying(named) + // type definition or generic type declaration + named := check.newNamed(obj, nil, nil) + def.setUnderlying(named) - if tdecl.TParamList != nil { - check.openScope(tdecl, "type parameters") - defer check.closeScope() - named.tparams = check.collectTypeParams(tdecl.TParamList) - } + if tdecl.TParamList != nil { + check.openScope(tdecl, "type parameters") + defer check.closeScope() + check.collectTypeParams(&named.tparams, tdecl.TParamList) + } - // determine underlying type of named - named.fromRHS = check.definedType(tdecl.Type, named) + // determine underlying type of named + rhs = check.definedType(tdecl.Type, named) + assert(rhs != nil) + named.fromRHS = rhs - // The underlying type of named may be itself a named type that is - // incomplete: - // - // type ( - // A B - // B *C - // C A - // ) - // - // The type of C is the (named) type of A which is incomplete, - // and which has as its underlying type the named type B. - // Determine the (final, unnamed) underlying type by resolving - // any forward chain. - // TODO(gri) Investigate if we can just use named.fromRHS here - // and rely on lazy computation of the underlying type. - named.underlying = under(named) + // If the underlying type was not set while type-checking the right-hand + // side, it is invalid and an error should have been reported elsewhere. + if named.underlying == nil { + named.underlying = Typ[Invalid] } + // Disallow a lone type parameter as the RHS of a type declaration (issue #45639). + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if the RHS is a lone type parameter, we will + // use its underlying type (like we do for any RHS in a type declaration), and its + // underlying type is an interface and the type declaration is well defined. + if isTypeParam(rhs) { + check.error(tdecl.Type, "cannot use a type parameter as RHS in type declaration") + named.underlying = Typ[Invalid] + } } -func (check *Checker) collectTypeParams(list []*syntax.Field) (tparams []*TypeName) { - // Type parameter lists should not be empty. The parser will - // complain but we still may get an incorrect AST: ignore it. - if len(list) == 0 { - return - } +func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Field) { + tparams := make([]*TypeParam, len(list)) - // Declare type parameters up-front, with empty interface as type bound. + // Declare type parameters up-front. // The scope of type parameters starts at the beginning of the type parameter - // list (so we can have mutually recursive parameterized interfaces). - for _, f := range list { - tparams = check.declareTypeParam(tparams, f.Name) - } + // list (so we can have mutually recursive parameterized type bounds). + for i, f := range list { + tparams[i] = check.declareTypeParam(f.Name) + } + + // Set the type parameters before collecting the type constraints because + // the parameterized type may be used by the constraints (issue #47887). + // Example: type T[P T[P]] interface{} + *dst = bindTParams(tparams) + + // Signal to cycle detection that we are in a type parameter list. + // We can only be inside one type parameter list at any given time: + // function closures may appear inside a type parameter list but they + // cannot be generic, and their bodies are processed in delayed and + // sequential fashion. Note that with each new declaration, we save + // the existing environment and restore it when done; thus inTParamList + // is true exactly only when we are in a specific type parameter list. + assert(!check.inTParamList) + check.inTParamList = true + defer func() { + check.inTParamList = false + }() + // Keep track of bounds for later validation. var bound Type - for i, j := 0, 0; i < len(list); i = j { - f := list[i] - - // determine the range of type parameters list[i:j] with identical type bound - // (declared as in (type a, b, c B)) - j = i + 1 - for j < len(list) && list[j].Type == f.Type { - j++ - } - - // this should never be the case, but be careful - if f.Type == nil { - continue - } - - // The predeclared identifier "any" is visible only as a constraint - // in a type parameter list. Look for it before general constraint - // resolution. - if tident, _ := unparen(f.Type).(*syntax.Name); tident != nil && tident.Value == "any" && check.lookup("any") == nil { - bound = universeAny - } else { - bound = check.typ(f.Type) - } - - // type bound must be an interface - // TODO(gri) We should delay the interface check because - // we may not have a complete interface yet: - // type C(type T C) interface {} - // (issue #39724). - if _, ok := under(bound).(*Interface); ok { - // set the type bounds - for i < j { - tparams[i].typ.(*TypeParam).bound = bound - i++ + for i, f := range list { + // Optimization: Re-use the previous type bound if it hasn't changed. + // This also preserves the grouped output of type parameter lists + // when printing type strings. + if i == 0 || f.Type != list[i-1].Type { + bound = check.bound(f.Type) + if isTypeParam(bound) { + // We may be able to allow this since it is now well-defined what + // the underlying type and thus type set of a type parameter is. + // But we may need some additional form of cycle detection within + // type parameter lists. + check.error(f.Type, "cannot use a type parameter as constraint") + bound = Typ[Invalid] } - } else if bound != Typ[Invalid] { - check.errorf(f.Type, "%s is not an interface", bound) } + tparams[i].bound = bound } - - return } -func (check *Checker) declareTypeParam(tparams []*TypeName, name *syntax.Name) []*TypeName { - tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil) - check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect - check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position - tparams = append(tparams, tpar) - - if check.conf.Trace { - check.trace(name.Pos(), "type param = %v", tparams[len(tparams)-1]) +func (check *Checker) bound(x syntax.Expr) Type { + // A type set literal of the form ~T and A|B may only appear as constraint; + // embed it in an implicit interface so that only interface type-checking + // needs to take care of such type expressions. + if op, _ := x.(*syntax.Operation); op != nil && (op.Op == syntax.Tilde || op.Op == syntax.Or) { + t := check.typ(&syntax.InterfaceType{MethodList: []*syntax.Field{{Type: x}}}) + // mark t as implicit interface if all went well + if t, _ := t.(*Interface); t != nil { + t.implicit = true + } + return t } + return check.typ(x) +} - return tparams +func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam { + // Use Typ[Invalid] for the type constraint to ensure that a type + // is present even if the actual constraint has not been assigned + // yet. + // TODO(gri) Need to systematically review all uses of type parameter + // constraints to make sure we don't rely on them if they + // are not properly set yet. + tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil) + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect + check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position + return tpar } func (check *Checker) collectMethods(obj *TypeName) { @@ -735,20 +647,21 @@ func (check *Checker) collectMethods(obj *TypeName) { // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." - base := asNamed(obj.typ) // shouldn't fail but be conservative + base, _ := obj.typ.(*Named) // shouldn't fail but be conservative if base != nil { - if t, _ := base.underlying.(*Struct); t != nil { - for _, fld := range t.fields { - if fld.name != "_" { - assert(mset.insert(fld) == nil) - } - } - } + assert(base.TypeArgs().Len() == 0) // collectMethods should not be called on an instantiated type + + // See issue #52529: we must delay the expansion of underlying here, as + // base may not be fully set-up. + check.later(func() { + check.checkFieldUniqueness(base) + }).describef(obj, "verifying field uniqueness for %v", base) // Checker.Files may be called multiple times; additional package files // may add methods to already type-checked types. Add pre-existing methods // so that we can detect redeclarations. - for _, m := range base.methods { + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) assert(m.name != "_") assert(mset.insert(m) == nil) } @@ -761,17 +674,10 @@ func (check *Checker) collectMethods(obj *TypeName) { assert(m.name != "_") if alt := mset.insert(m); alt != nil { var err error_ - switch alt.(type) { - case *Var: - err.errorf(m.pos, "field and method with the same name %s", m.name) - case *Func: - if check.conf.CompilerErrorMessages { - err.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name) - } else { - err.errorf(m.pos, "method %s already declared for %s", m.name, obj) - } - default: - unreachable() + if check.conf.CompilerErrorMessages { + err.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name) + } else { + err.errorf(m.pos, "method %s already declared for %s", m.name, obj) } err.recordAltDecl(alt) check.report(&err) @@ -779,7 +685,37 @@ func (check *Checker) collectMethods(obj *TypeName) { } if base != nil { - base.methods = append(base.methods, m) + base.AddMethod(m) + } + } +} + +func (check *Checker) checkFieldUniqueness(base *Named) { + if t, _ := base.under().(*Struct); t != nil { + var mset objset + for i := 0; i < base.NumMethods(); i++ { + m := base.Method(i) + assert(m.name != "_") + assert(mset.insert(m) == nil) + } + + // Check that any non-blank field names of base are distinct from its + // method names. + for _, fld := range t.fields { + if fld.name != "_" { + if alt := mset.insert(fld); alt != nil { + // Struct fields should already be unique, so we should only + // encounter an alternate via collision with a method name. + _ = alt.(*Func) + + // For historical consistency, we report the primary error on the + // method, and the alt decl on the field. + var err error_ + err.errorf(alt, "field and method with the same name %s", fld.name) + err.recordAltDecl(fld) + check.report(&err) + } + } } } } @@ -805,12 +741,16 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type) obj.color_ = saved + if len(fdecl.TParamList) > 0 && fdecl.Body == nil { + check.softErrorf(fdecl, "parameterized function is missing function body") + } + // function body must be type-checked after global declarations // (functions implemented elsewhere have no body) if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { check.later(func() { check.funcBody(decl, obj.name, sig, fdecl.Body, nil) - }) + }).describef(obj, "func %s", obj.name) } } @@ -829,7 +769,7 @@ func (check *Checker) declStmt(list []syntax.Decl) { top := len(check.delayed) // iota is the index of the current constDecl within the group - if first < 0 || list[index-1].(*syntax.ConstDecl).Group != s.Group { + if first < 0 || s.Group == nil || list[index-1].(*syntax.ConstDecl).Group != s.Group { first = index last = nil } diff --git a/src/cmd/compile/internal/types2/errorcalls_test.go b/src/cmd/compile/internal/types2/errorcalls_test.go index 28bb33aaffd004..80b05f9f0f0688 100644 --- a/src/cmd/compile/internal/types2/errorcalls_test.go +++ b/src/cmd/compile/internal/types2/errorcalls_test.go @@ -18,7 +18,7 @@ func TestErrorCalls(t *testing.T) { } for _, file := range files { - syntax.Walk(file, func(n syntax.Node) bool { + syntax.Crawl(file, func(n syntax.Node) bool { call, _ := n.(*syntax.CallExpr) if call == nil { return false diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go index af4ecb2300a58c..3734db59107355 100644 --- a/src/cmd/compile/internal/types2/errors.go +++ b/src/cmd/compile/internal/types2/errors.go @@ -7,9 +7,9 @@ package types2 import ( - "bytes" "cmd/compile/internal/syntax" "fmt" + "runtime" "strconv" "strings" ) @@ -20,7 +20,13 @@ func unimplemented() { func assert(p bool) { if !p { - panic("assertion failed") + msg := "assertion failed" + // Include information about the assertion location. Due to panic recovery, + // this location is otherwise buried in the middle of the panicking stack. + if _, file, line, ok := runtime.Caller(1); ok { + msg = fmt.Sprintf("%s:%d: %s", file, line, msg) + } + panic(msg) } } @@ -57,13 +63,16 @@ func (err *error_) msg(qf Qualifier) string { if err.empty() { return "no error" } - var buf bytes.Buffer + var buf strings.Builder for i := range err.desc { p := &err.desc[i] if i > 0 { - fmt.Fprintf(&buf, "\n\t%s: ", p.pos) + fmt.Fprint(&buf, "\n\t") + if p.pos.IsKnown() { + fmt.Fprintf(&buf, "%s: ", p.pos) + } } - buf.WriteString(sprintf(qf, p.format, p.args...)) + buf.WriteString(sprintf(qf, false, p.format, p.args...)) } return buf.String() } @@ -82,23 +91,56 @@ func (err *error_) errorf(at poser, format string, args ...interface{}) { err.desc = append(err.desc, errorDesc{posFor(at), format, args}) } -func sprintf(qf Qualifier, format string, args ...interface{}) string { +func sprintf(qf Qualifier, debug bool, format string, args ...interface{}) string { for i, arg := range args { switch a := arg.(type) { case nil: arg = "" case operand: - panic("internal error: should always pass *operand") + panic("got operand instead of *operand") case *operand: arg = operandString(a, qf) case syntax.Pos: arg = a.String() case syntax.Expr: arg = syntax.String(a) + case []syntax.Expr: + var buf strings.Builder + buf.WriteByte('[') + for i, x := range a { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(syntax.String(x)) + } + buf.WriteByte(']') + arg = buf.String() case Object: arg = ObjectString(a, qf) case Type: - arg = TypeString(a, qf) + arg = typeString(a, qf, debug) + case []Type: + var buf strings.Builder + buf.WriteByte('[') + for i, x := range a { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(typeString(x, qf, debug)) + } + buf.WriteByte(']') + arg = buf.String() + case []*TypeParam: + var buf strings.Builder + buf.WriteByte('[') + for i, x := range a { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(typeString(x, qf, debug)) // use typeString so we get subscripts when debugging + } + buf.WriteByte(']') + arg = buf.String() } args[i] = arg } @@ -111,7 +153,7 @@ func (check *Checker) qualifier(pkg *Package) string { if check.pkgPathMap == nil { check.pkgPathMap = make(map[string]map[string]bool) check.seenPkgMap = make(map[*Package]bool) - check.markImports(pkg) + check.markImports(check.pkg) } // If the same package name was used by multiple packages, display the full path. if len(check.pkgPathMap[pkg.name]) > 1 { @@ -142,13 +184,18 @@ func (check *Checker) markImports(pkg *Package) { } } +// check may be nil. func (check *Checker) sprintf(format string, args ...interface{}) string { - return sprintf(check.qualifier, format, args...) + var qf Qualifier + if check != nil { + qf = check.qualifier + } + return sprintf(qf, false, format, args...) } func (check *Checker) report(err *error_) { if err.empty() { - panic("internal error: reporting no error") + panic("no error to report") } check.err(err.pos(), err.msg(check.qualifier), err.soft) } @@ -157,13 +204,13 @@ func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) fmt.Printf("%s:\t%s%s\n", pos, strings.Repeat(". ", check.indent), - check.sprintf(format, args...), + sprintf(check.qualifier, true, format, args...), ) } // dump is only needed for debugging func (check *Checker) dump(format string, args ...interface{}) { - fmt.Println(check.sprintf(format, args...)) + fmt.Println(sprintf(check.qualifier, true, format, args...)) } func (check *Checker) err(at poser, msg string, soft bool) { @@ -227,6 +274,16 @@ func (check *Checker) softErrorf(at poser, format string, args ...interface{}) { check.err(at, check.sprintf(format, args...), true) } +func (check *Checker) versionErrorf(at poser, goVersion string, format string, args ...interface{}) { + msg := check.sprintf(format, args...) + if check.conf.CompilerErrorMessages { + msg = fmt.Sprintf("%s requires %s or later (-lang was set to %s; check go.mod)", msg, goVersion, check.conf.GoVersion) + } else { + msg = fmt.Sprintf("%s requires %s or later", msg, goVersion) + } + check.err(at, msg, true) +} + // posFor reports the left (= start) position of at. func posFor(at poser) syntax.Pos { switch x := at.(type) { @@ -242,16 +299,15 @@ func posFor(at poser) syntax.Pos { // stripAnnotations removes internal (type) annotations from s. func stripAnnotations(s string) string { - // Would like to use strings.Builder but it's not available in Go 1.4. - var b bytes.Buffer + var buf strings.Builder for _, r := range s { // strip #'s and subscript digits - if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080 - b.WriteRune(r) + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 + buf.WriteRune(r) } } - if b.Len() < len(s) { - return b.String() + if buf.Len() < len(s) { + return buf.String() } return s } diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go index e1f0e83fc97d16..ac73ca4650dc4a 100644 --- a/src/cmd/compile/internal/types2/errors_test.go +++ b/src/cmd/compile/internal/types2/errors_test.go @@ -19,7 +19,7 @@ func TestError(t *testing.T) { t.Errorf("simple error: got %q, want %q", got, want) } - want = ": foo 42\n\t: bar 43" + want = ": foo 42\n\tbar 43" err.errorf(nopos, "bar %d", 43) if got := err.String(); got != want { t.Errorf("simple error: got %q, want %q", got, want) @@ -35,7 +35,6 @@ func TestStripAnnotations(t *testing.T) { {"foo", "foo"}, {"foo₀", "foo"}, {"foo(T₀)", "foo(T)"}, - {"#foo(T₀)", "foo(T)"}, } { got := stripAnnotations(test.in) if got != test.want { diff --git a/src/cmd/compile/internal/types2/example_test.go b/src/cmd/compile/internal/types2/example_test.go index 714bf778213996..ad0f22fcec4c88 100644 --- a/src/cmd/compile/internal/types2/example_test.go +++ b/src/cmd/compile/internal/types2/example_test.go @@ -17,7 +17,6 @@ package types2_test // from source, use golang.org/x/tools/go/loader. import ( - "bytes" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" "fmt" @@ -68,7 +67,7 @@ func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get // Print the tree of scopes. // For determinism, we redact addresses. - var buf bytes.Buffer + var buf strings.Builder pkg.Scope().WriteTo(&buf, 0, true) rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`) fmt.Println(rx.ReplaceAllString(buf.String(), "")) @@ -173,7 +172,7 @@ func fib(x int) int { // fmt.Println("Types and Values of each expression:") // items = nil // for expr, tv := range info.Types { - // var buf bytes.Buffer + // var buf strings.Builder // posn := expr.Pos() // tvstr := tv.Type.String() // if tv.Value != nil { @@ -216,36 +215,36 @@ func fib(x int) int { // var x int: // defined at fib.go:8:10 // used at 10:10, 12:13, 12:24, 9:5 - - // TODO(gri) Enable once positions are updated/verified - // Types and Values of each expression: - // 4: 8 | string | type : string - // 6:15 | len | builtin : func(string) int - // 6:15 | len(b) | value : int - // 6:19 | b | var : fib.S - // 6:23 | S | type : fib.S - // 6:23 | S(c) | value : fib.S - // 6:25 | c | var : string - // 6:29 | "hello" | value : string = "hello" - // 8:12 | int | type : int - // 8:17 | int | type : int - // 9: 5 | x | var : int - // 9: 5 | x < 2 | value : untyped bool - // 9: 9 | 2 | value : int = 2 - // 10:10 | x | var : int - // 12: 9 | fib | value : func(x int) int - // 12: 9 | fib(x - 1) | value : int - // 12: 9 | fib(x - 1) - fib(x - 2) | value : int - // 12:13 | x | var : int - // 12:13 | x - 1 | value : int - // 12:15 | 1 | value : int = 1 - // 12:20 | fib | value : func(x int) int - // 12:20 | fib(x - 2) | value : int - // 12:24 | x | var : int - // 12:24 | x - 2 | value : int - // 12:26 | 2 | value : int = 2 } +// TODO(gri) Enable once positions are updated/verified +// Types and Values of each expression: +// 4: 8 | string | type : string +// 6:15 | len | builtin : func(string) int +// 6:15 | len(b) | value : int +// 6:19 | b | var : fib.S +// 6:23 | S | type : fib.S +// 6:23 | S(c) | value : fib.S +// 6:25 | c | var : string +// 6:29 | "hello" | value : string = "hello" +// 8:12 | int | type : int +// 8:17 | int | type : int +// 9: 5 | x | var : int +// 9: 5 | x < 2 | value : untyped bool +// 9: 9 | 2 | value : int = 2 +// 10:10 | x | var : int +// 12: 9 | fib | value : func(x int) int +// 12: 9 | fib(x - 1) | value : int +// 12: 9 | fib(x - 1) - fib(x - 2) | value : int +// 12:13 | x | var : int +// 12:13 | x - 1 | value : int +// 12:15 | 1 | value : int = 1 +// 12:20 | fib | value : func(x int) int +// 12:20 | fib(x - 2) | value : int +// 12:24 | x | var : int +// 12:24 | x - 2 | value : int +// 12:26 | 2 | value : int = 2 + func mode(tv types2.TypeAndValue) string { switch { case tv.IsVoid(): diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 23b79656bb5fb0..6e1e47c08ff154 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -63,21 +63,17 @@ var unaryOpPredicates opPredicates func init() { // Setting unaryOpPredicates in init avoids declaration cycles. unaryOpPredicates = opPredicates{ - syntax.Add: isNumeric, - syntax.Sub: isNumeric, - syntax.Xor: isInteger, - syntax.Not: isBoolean, + syntax.Add: allNumeric, + syntax.Sub: allNumeric, + syntax.Xor: allInteger, + syntax.Not: allBoolean, } } func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool { if pred := m[op]; pred != nil { if !pred(x.typ) { - if check.conf.CompilerErrorMessages { - check.errorf(x, invalidOp+"operator %s not defined on %s", op, x) - } else { - check.errorf(x, invalidOp+"operator %s not defined for %s", op, x) - } + check.errorf(x, invalidOp+"operator %s not defined on %s", op, x) return false } } else { @@ -93,52 +89,61 @@ func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool { func (check *Checker) overflow(x *operand) { assert(x.mode == constant_) - // If the corresponding expression is an operation, use the - // operator position rather than the start of the expression - // as error position. - pos := syntax.StartPos(x.expr) - what := "" // operator description, if any - if op, _ := x.expr.(*syntax.Operation); op != nil { - pos = op.Pos() - what = opName(op) - } - if x.val.Kind() == constant.Unknown { // TODO(gri) We should report exactly what went wrong. At the // moment we don't have the (go/constant) API for that. // See also TODO in go/constant/value.go. - check.error(pos, "constant result is not representable") + check.error(opPos(x.expr), "constant result is not representable") return } // Typed constants must be representable in // their type after each constant operation. + // x.typ cannot be a type parameter (type + // parameters cannot be constant types). if isTyped(x.typ) { - check.representable(x, asBasic(x.typ)) + check.representable(x, under(x.typ).(*Basic)) return } // Untyped integer values must not grow arbitrarily. const prec = 512 // 512 is the constant precision if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec { - check.errorf(pos, "constant %s overflow", what) + op := opName(x.expr) + if op != "" { + op += " " + } + check.errorf(opPos(x.expr), "constant %soverflow", op) x.val = constant.MakeUnknown() } } -// opName returns the name of an operation, or the empty string. -// For now, only operations that might overflow are handled. -// TODO(gri) Expand this to a general mechanism giving names to -// nodes? -func opName(e *syntax.Operation) string { - op := int(e.Op) - if e.Y == nil { - if op < len(op2str1) { - return op2str1[op] - } - } else { - if op < len(op2str2) { - return op2str2[op] +// opPos returns the position of the operator if x is an operation; +// otherwise it returns the start position of x. +func opPos(x syntax.Expr) syntax.Pos { + switch op := x.(type) { + case nil: + return nopos // don't crash + case *syntax.Operation: + return op.Pos() + default: + return syntax.StartPos(x) + } +} + +// opName returns the name of the operation if x is an operation +// that might overflow; otherwise it returns the empty string. +func opName(x syntax.Expr) string { + if e, _ := x.(*syntax.Operation); e != nil { + op := int(e.Op) + if e.Y == nil { + if op < len(op2str1) { + return op2str1[op] + } + } else { + if op < len(op2str2) { + return op2str2[op] + } } } return "" @@ -157,6 +162,15 @@ var op2str2 = [...]string{ syntax.Shl: "shift", } +// If typ is a type parameter, underIs returns the result of typ.underIs(f). +// Otherwise, underIs returns the result of f(under(typ)). +func underIs(typ Type, f func(Type) bool) bool { + if tpar, _ := typ.(*TypeParam); tpar != nil { + return tpar.underIs(f) + } + return f(under(typ)) +} + func (check *Checker) unary(x *operand, e *syntax.Operation) { check.expr(x, e.X) if x.mode == invalid { @@ -177,21 +191,33 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) { return case syntax.Recv: - typ := asChan(x.typ) - if typ == nil { + u := coreType(x.typ) + if u == nil { + check.errorf(x, invalidOp+"cannot receive from %s: no core type", x) + x.mode = invalid + return + } + ch, _ := u.(*Chan) + if ch == nil { check.errorf(x, invalidOp+"cannot receive from non-channel %s", x) x.mode = invalid return } - if typ.dir == SendOnly { + if ch.dir == SendOnly { check.errorf(x, invalidOp+"cannot receive from send-only channel %s", x) x.mode = invalid return } x.mode = commaok - x.typ = typ.elem + x.typ = ch.elem check.hasCallOrRecv = true return + + case syntax.Tilde: + // Provide a better error position and message than what check.op below could do. + check.error(e, "cannot use ~ outside of interface or type constraint") + x.mode = invalid + return } if !check.op(unaryOpPredicates, x, e.Op) { @@ -493,8 +519,11 @@ func (check *Checker) invalidConversion(code errorCode, x *operand, target Type) // Also, if x is a constant, it must be representable as a value of typ, // and if x is the (formerly untyped) lhs operand of a non-constant // shift, it must be an integer value. -// func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { + check.updateExprType0(nil, x, typ, final) +} + +func (check *Checker) updateExprType0(parent, x syntax.Expr, typ Type, final bool) { old, found := check.untyped[x] if !found { return // nothing to do @@ -537,7 +566,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { // No operands to take care of. case *syntax.ParenExpr: - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) // case *syntax.UnaryExpr: // // If x is a constant, the operands were constants. @@ -548,7 +577,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { // if old.val != nil { // break // } - // check.updateExprType(x.X, typ, final) + // check.updateExprType0(x, x.X, typ, final) case *syntax.Operation: if x.Y == nil { @@ -569,7 +598,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { if old.val != nil { break } - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) break } @@ -583,11 +612,11 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { } else if isShift(x.Op) { // The result type depends only on lhs operand. // The rhs type was updated when checking the shift. - check.updateExprType(x.X, typ, final) + check.updateExprType0(x, x.X, typ, final) } else { // The operand types match the result type. - check.updateExprType(x.X, typ, final) - check.updateExprType(x.Y, typ, final) + check.updateExprType0(x, x.X, typ, final) + check.updateExprType0(x, x.Y, typ, final) } default: @@ -597,7 +626,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { // If the new type is not final and still untyped, just // update the recorded type. if !final && isUntyped(typ) { - old.typ = asBasic(typ) + old.typ = under(typ).(*Basic) check.untyped[x] = old return } @@ -610,8 +639,12 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) { // If x is the lhs of a shift, its final type must be integer. // We already know from the shift check that it is representable // as an integer if it is a constant. - if !isInteger(typ) { - check.errorf(x, invalidOp+"shifted operand %s (type %s) must be integer", x, typ) + if !allInteger(typ) { + if check.conf.CompilerErrorMessages { + check.errorf(x, invalidOp+"%s (shift of type %s)", parent, typ) + } else { + check.errorf(x, invalidOp+"shifted operand %s (type %s) must be integer", x, typ) + } return } // Even if we have an integer, if the value is a constant we @@ -643,7 +676,11 @@ func (check *Checker) updateExprVal(x syntax.Expr, val constant.Value) { func (check *Checker) convertUntyped(x *operand, target Type) { newType, val, code := check.implicitTypeAndValue(x, target) if code != 0 { - check.invalidConversion(code, x, target.Underlying()) + t := target + if !isTypeParam(target) { + t = safeUnderlying(target) + } + check.invalidConversion(code, x, t) x.mode = invalid return } @@ -664,7 +701,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) { // If x is a constant operand, the returned constant.Value will be the // representation of x in this context. func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) { - target = expand(target) if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { return x.typ, nil, 0 } @@ -691,10 +727,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const return nil, nil, _InvalidUntypedConversion } - switch t := optype(target).(type) { + switch u := under(target).(type) { case *Basic: if x.mode == constant_ { - v, code := check.representation(x, t) + v, code := check.representation(x, u) if code != 0 { return nil, nil, code } @@ -723,20 +759,23 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const default: return nil, nil, _InvalidUntypedConversion } - case *Sum: - ok := t.is(func(t Type) bool { - target, _, _ := check.implicitTypeAndValue(x, t) - return target != nil - }) - if !ok { - return nil, nil, _InvalidUntypedConversion - } case *Interface: + if isTypeParam(target) { + if !u.typeSet().underIs(func(u Type) bool { + if u == nil { + return false + } + t, _, _ := check.implicitTypeAndValue(x, u) + return t != nil + }) { + return nil, nil, _InvalidUntypedConversion + } + break + } // Update operand types to the default type rather than the target // (interface) type: values must have concrete dynamic types. // Untyped nil was handled upfront. - check.completeInterface(nopos, t) - if !t.Empty() { + if !u.Empty() { return nil, nil, _InvalidUntypedConversion // cannot assign untyped values to non-empty interfaces } return Default(x.typ), nil, 0 // default type for nil is nil @@ -746,46 +785,88 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const return target, nil, 0 } -func (check *Checker) comparison(x, y *operand, op syntax.Operator) { +// If switchCase is true, the operator op is ignored. +func (check *Checker) comparison(x, y *operand, op syntax.Operator, switchCase bool) { + // Avoid spurious errors if any of the operands has an invalid type (issue #54405). + if x.typ == Typ[Invalid] || y.typ == Typ[Invalid] { + x.mode = invalid + return + } + + if switchCase { + op = syntax.Eql + } + + errOp := x // operand for which error is reported, if any + cause := "" // specific error cause, if any + // spec: "In any comparison, the first operand must be assignable // to the type of the second operand, or vice versa." - err := "" - xok, _ := x.assignableTo(check, y.typ, nil) - yok, _ := y.assignableTo(check, x.typ, nil) - if xok || yok { - defined := false - switch op { - case syntax.Eql, syntax.Neq: - // spec: "The equality operators == and != apply to operands that are comparable." - defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ) - case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq: - // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." - defined = isOrdered(x.typ) && isOrdered(y.typ) - default: - unreachable() + ok, _ := x.assignableTo(check, y.typ, nil) + if !ok { + ok, _ = y.assignableTo(check, x.typ, nil) + } + if !ok { + // Report the error on the 2nd operand since we only + // know after seeing the 2nd operand whether we have + // a type mismatch. + errOp = y + // For now, if we're not running the compiler, use the + // position of x to minimize changes to existing tests. + if !check.conf.CompilerErrorMessages { + errOp = x } - if !defined { + cause = check.sprintf("mismatched types %s and %s", x.typ, y.typ) + goto Error + } + + // check if comparison is defined for operands + switch op { + case syntax.Eql, syntax.Neq: + // spec: "The equality operators == and != apply to operands that are comparable." + switch { + case x.isNil() || y.isNil(): + // Comparison against nil requires that the other operand type has nil. typ := x.typ if x.isNil() { typ = y.typ } - if check.conf.CompilerErrorMessages { - err = check.sprintf("operator %s not defined on %s", op, typ) - } else { - err = check.sprintf("operator %s not defined for %s", op, typ) + if !hasNil(typ) { + // This case should only be possible for "nil == nil". + // Report the error on the 2nd operand since we only + // know after seeing the 2nd operand whether we have + // an invalid comparison. + errOp = y + goto Error } + + case !Comparable(x.typ): + errOp = x + cause = check.incomparableCause(x.typ) + goto Error + + case !Comparable(y.typ): + errOp = y + cause = check.incomparableCause(y.typ) + goto Error } - } else { - err = check.sprintf("mismatched types %s and %s", x.typ, y.typ) - } - if err != "" { - // TODO(gri) better error message for cases where one can only compare against nil - check.errorf(x, invalidOp+"cannot compare %s %s %s (%s)", x.expr, op, y.expr, err) - x.mode = invalid - return + case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq: + // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered." + switch { + case !allOrdered(x.typ): + errOp = x + goto Error + case !allOrdered(y.typ): + errOp = y + goto Error + } + + default: + unreachable() } + // comparison is ok if x.mode == constant_ && y.mode == constant_ { x.val = constant.MakeBool(constant.Compare(x.val, op2tok[op], y.val)) // The operands are never materialized; no need to update @@ -803,6 +884,73 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) { // spec: "Comparison operators compare two operands and yield // an untyped boolean value." x.typ = Typ[UntypedBool] + return + +Error: + // We have an offending operand errOp and possibly an error cause. + if cause == "" { + if isTypeParam(x.typ) || isTypeParam(y.typ) { + // TODO(gri) should report the specific type causing the problem, if any + if !isTypeParam(x.typ) { + errOp = y + } + cause = check.sprintf("type parameter %s is not comparable with %s", errOp.typ, op) + } else { + cause = check.sprintf("operator %s not defined on %s", op, check.kindString(errOp.typ)) // catch-all + } + } + if switchCase { + check.errorf(x, "invalid case %s in switch on %s (%s)", x.expr, y.expr, cause) // error position always at 1st operand + } else { + if check.conf.CompilerErrorMessages { + check.errorf(errOp, invalidOp+"%s %s %s (%s)", x.expr, op, y.expr, cause) + } else { + check.errorf(errOp, invalidOp+"cannot compare %s %s %s (%s)", x.expr, op, y.expr, cause) + } + } + x.mode = invalid +} + +// incomparableCause returns a more specific cause why typ is not comparable. +// If there is no more specific cause, the result is "". +func (check *Checker) incomparableCause(typ Type) string { + switch under(typ).(type) { + case *Slice, *Signature, *Map: + return check.kindString(typ) + " can only be compared to nil" + } + // see if we can extract a more specific error + var cause string + comparable(typ, true, nil, func(format string, args ...interface{}) { + cause = check.sprintf(format, args...) + }) + return cause +} + +// kindString returns the type kind as a string. +func (check *Checker) kindString(typ Type) string { + switch under(typ).(type) { + case *Array: + return "array" + case *Slice: + return "slice" + case *Struct: + return "struct" + case *Pointer: + return "pointer" + case *Signature: + return "func" + case *Interface: + if isTypeParam(typ) { + return check.sprintf("type parameter %s", typ) + } + return "interface" + case *Map: + return "map" + case *Chan: + return "chan" + default: + return check.sprintf("%s", typ) // catch-all + } } // If e != nil, it must be the shift expression; it may be nil for non-constant shifts. @@ -814,7 +962,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) { xval = constant.ToInt(x.val) } - if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int { + if allInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int { // The lhs is of integer type or an untyped constant representable // as an integer. Nothing to do. } else { @@ -827,32 +975,48 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) { // spec: "The right operand in a shift expression must have integer type // or be an untyped constant representable by a value of type uint." - // Provide a good error message for negative shift counts. + // Check that constants are representable by uint, but do not convert them + // (see also issue #47243). if y.mode == constant_ { + // Provide a good error message for negative shift counts. yval := constant.ToInt(y.val) // consider -1, 1.0, but not -1.1 if yval.Kind() == constant.Int && constant.Sign(yval) < 0 { check.errorf(y, invalidOp+"negative shift count %s", y) x.mode = invalid return } - } - // Caution: Check for isUntyped first because isInteger includes untyped - // integers (was bug #43697). - if isUntyped(y.typ) { - check.convertUntyped(y, Typ[Uint]) - if y.mode == invalid { + if isUntyped(y.typ) { + // Caution: Check for representability here, rather than in the switch + // below, because isInteger includes untyped integers (was bug #43697). + check.representable(y, Typ[Uint]) + if y.mode == invalid { + x.mode = invalid + return + } + } + } else { + // Check that RHS is otherwise at least of integer type. + switch { + case allInteger(y.typ): + if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { + check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y) + x.mode = invalid + return + } + case isUntyped(y.typ): + // This is incorrect, but preserves pre-existing behavior. + // See also bug #47410. + check.convertUntyped(y, Typ[Uint]) + if y.mode == invalid { + x.mode = invalid + return + } + default: + check.errorf(y, invalidOp+"shift count %s must be integer", y) x.mode = invalid return } - } else if !isInteger(y.typ) { - check.errorf(y, invalidOp+"shift count %s must be integer", y) - x.mode = invalid - return - } else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { - check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y) - x.mode = invalid - return } if x.mode == constant_ { @@ -920,7 +1084,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) { } // non-constant shift - lhs must be an integer - if !isInteger(x.typ) { + if !allInteger(x.typ) { check.errorf(x, invalidOp+"shifted operand %s must be integer", x) x.mode = invalid return @@ -934,19 +1098,19 @@ var binaryOpPredicates opPredicates func init() { // Setting binaryOpPredicates in init avoids declaration cycles. binaryOpPredicates = opPredicates{ - syntax.Add: isNumericOrString, - syntax.Sub: isNumeric, - syntax.Mul: isNumeric, - syntax.Div: isNumeric, - syntax.Rem: isInteger, - - syntax.And: isInteger, - syntax.Or: isInteger, - syntax.Xor: isInteger, - syntax.AndNot: isInteger, - - syntax.AndAnd: isBoolean, - syntax.OrOr: isBoolean, + syntax.Add: allNumericOrString, + syntax.Sub: allNumeric, + syntax.Mul: allNumeric, + syntax.Div: allNumeric, + syntax.Rem: allInteger, + + syntax.And: allInteger, + syntax.Or: allInteger, + syntax.Xor: allInteger, + syntax.AndNot: allInteger, + + syntax.AndAnd: allBoolean, + syntax.OrOr: allBoolean, } } @@ -972,26 +1136,51 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op return } - check.convertUntyped(x, y.typ) - if x.mode == invalid { - return + // TODO(gri) make canMix more efficient - called for each binary operation + canMix := func(x, y *operand) bool { + if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) { + return true + } + if allBoolean(x.typ) != allBoolean(y.typ) { + return false + } + if allString(x.typ) != allString(y.typ) { + return false + } + if x.isNil() && !hasNil(y.typ) { + return false + } + if y.isNil() && !hasNil(x.typ) { + return false + } + return true } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { - x.mode = invalid - return + if canMix(x, &y) { + check.convertUntyped(x, y.typ) + if x.mode == invalid { + return + } + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + x.mode = invalid + return + } } if isComparison(op) { - check.comparison(x, &y, op) + check.comparison(x, &y, op, false) return } - if !check.identical(x.typ, y.typ) { + if !Identical(x.typ, y.typ) { // only report an error if we have valid types // (otherwise we had an error reported elsewhere already) if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] { - check.errorf(x, invalidOp+"mismatched types %s and %s", x.typ, y.typ) + if e != nil { + check.errorf(x, invalidOp+"%s (mismatched types %s and %s)", e, x.typ, y.typ) + } else { + check.errorf(x, invalidOp+"%s %s= %s (mismatched types %s and %s)", lhs, op, rhs, x.typ, y.typ) + } } x.mode = invalid return @@ -1004,7 +1193,7 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op if op == syntax.Div || op == syntax.Rem { // check for zero divisor - if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 { + if (x.mode == constant_ || allInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 { check.error(&y, invalidOp+"division by zero") x.mode = invalid return @@ -1057,10 +1246,11 @@ const ( // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. // If hint != nil, it is the type of a composite literal element. -// -func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type) exprKind { +// If allowGeneric is set, the operand type may be an uninstantiated +// parameterized type or function value. +func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { if check.conf.Trace { - check.trace(e.Pos(), "expr %s", e) + check.trace(e.Pos(), "-- expr %s", e) check.indent++ defer func() { check.indent-- @@ -1069,14 +1259,42 @@ func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type) exprKind { } kind := check.exprInternal(x, e, hint) + + if !allowGeneric { + check.nonGeneric(x) + } + check.record(x) return kind } +// If x is a generic function or type, nonGeneric reports an error and invalidates x.mode and x.typ. +// Otherwise it leaves x alone. +func (check *Checker) nonGeneric(x *operand) { + if x.mode == invalid || x.mode == novalue { + return + } + var what string + switch t := x.typ.(type) { + case *Named: + if isGeneric(t) { + what = "type" + } + case *Signature: + if t.tparams != nil { + what = "function" + } + } + if what != "" { + check.errorf(x.expr, "cannot use generic %s %s without instantiation", what, x.expr) + x.mode = invalid + x.typ = Typ[Invalid] + } +} + // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. -// func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) @@ -1130,6 +1348,9 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin check.errorf(e, "malformed constant: %s", e.Value) goto Error } + // Ensure that integer values don't overflow (issue #54280). + x.expr = e // make sure that check.overflow below has an error position + check.overflow(x) case *syntax.FuncLit: if sig, ok := check.typ(e.Type).(*Signature); ok { @@ -1145,7 +1366,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin // but before the enclosing scope contents changes (#22992). check.later(func() { check.funcBody(decl, "", sig, e.Body, iota) - }) + }).describef(e, "func literal") } x.mode = value x.typ = sig @@ -1176,7 +1397,11 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin case hint != nil: // no composite literal type present - use hint (element type of enclosing type) typ = hint - base, _ = deref(under(typ)) // *T implies &T{} + base, _ = deref(coreType(typ)) // *T implies &T{} + if base == nil { + check.errorf(e, "invalid composite literal element type %s: no core type", typ) + goto Error + } default: // TODO(gri) provide better error messages depending on context @@ -1184,8 +1409,14 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin goto Error } - switch utyp := optype(base).(type) { + switch utyp := coreType(base).(type) { case *Struct: + // Prevent crash if the struct referred to is not yet set up. + // See analogous comment for *Array. + if utyp.fields == nil { + check.error(e, "illegal cycle in type declaration") + goto Error + } if len(e.ElemList) == 0 { break } @@ -1236,7 +1467,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin } check.expr(x, e) if i >= len(fields) { - check.error(x, "too many values in struct literal") + check.errorf(x, "too many values in %s{…}", base) break // cannot continue } // i < len(fields) @@ -1249,7 +1480,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin check.assignment(x, etyp, "struct literal") } if len(e.ElemList) < len(fields) { - check.error(e.Rbrace, "too few values in struct literal") + check.errorf(e.Rbrace, "too few values in %s{…}", base) // ok to continue } } @@ -1298,6 +1529,10 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin check.error(e, "illegal cycle in type declaration") goto Error } + // If the map key type is an interface (but not a type parameter), + // the type of a constant key must be considered when checking for + // duplicates. + keyIsInterface := isNonTypeParamInterface(utyp.key) visited := make(map[interface{}][]Type, len(e.ElemList)) for _, e := range e.ElemList { kv, _ := e.(*syntax.KeyValueExpr) @@ -1312,11 +1547,10 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin } if x.mode == constant_ { duplicate := false - // if the key is of interface type, the type is also significant when checking for duplicates xkey := keyVal(x.val) - if asInterface(utyp.key) != nil { + if keyIsInterface { for _, vtyp := range visited[xkey] { - if check.identical(vtyp, x.typ) { + if Identical(vtyp, x.typ) { duplicate = true break } @@ -1358,12 +1592,12 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin x.typ = typ case *syntax.ParenExpr: - kind := check.rawExpr(x, e.X, nil) + kind := check.rawExpr(x, e.X, nil, false) x.expr = e return kind case *syntax.SelectorExpr: - check.selector(x, e) + check.selector(x, e, nil) case *syntax.IndexExpr: if check.indexExpr(x, e) { @@ -1384,12 +1618,15 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin if x.mode == invalid { goto Error } - xtyp, _ := under(x.typ).(*Interface) - if xtyp == nil { - check.errorf(x, "%s is not an interface type", x) + // TODO(gri) we may want to permit type assertions on type parameter values at some point + if isTypeParam(x.typ) { + check.errorf(x, invalidOp+"cannot use type assertion on type parameter value %s", x) + goto Error + } + if _, ok := under(x.typ).(*Interface); !ok { + check.errorf(x, invalidOp+"%s is not an interface", x) goto Error } - check.ordinaryType(x.Pos(), xtyp) // x.(type) expressions are encoded via TypeSwitchGuards if e.Type == nil { check.error(e, invalidAST+"invalid use of AssertExpr") @@ -1399,7 +1636,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin if T == Typ[Invalid] { goto Error } - check.typeAssertion(posFor(x), x, xtyp, T) + check.typeAssertion(e, x, T, false) x.mode = commaok x.typ = T @@ -1441,20 +1678,32 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin // unary expression if e.Op == syntax.Mul { // pointer indirection - check.exprOrType(x, e.X) + check.exprOrType(x, e.X, false) switch x.mode { case invalid: goto Error case typexpr: + check.validVarType(e.X, x.typ) x.typ = &Pointer{base: x.typ} default: - if typ := asPointer(x.typ); typ != nil { - x.mode = variable - x.typ = typ.base - } else { - check.errorf(x, invalidOp+"cannot indirect %s", x) + var base Type + if !underIs(x.typ, func(u Type) bool { + p, _ := u.(*Pointer) + if p == nil { + check.errorf(x, invalidOp+"cannot indirect %s", x) + return false + } + if base != nil && !Identical(p.base, base) { + check.errorf(x, invalidOp+"pointers of %s must have identical base types", x) + return false + } + base = p.base + return true + }) { goto Error } + x.mode = variable + x.typ = base } break } @@ -1505,12 +1754,33 @@ Error: return statement // avoid follow-up errors } +// keyVal maps a complex, float, integer, string or boolean constant value +// to the corresponding complex128, float64, int64, uint64, string, or bool +// Go value if possible; otherwise it returns x. +// A complex constant that can be represented as a float (such as 1.2 + 0i) +// is returned as a floating point value; if a floating point value can be +// represented as an integer (such as 1.0) it is returned as an integer value. +// This ensures that constants of different kind but equal value (such as +// 1.0 + 0i, 1.0, 1) result in the same value. func keyVal(x constant.Value) interface{} { switch x.Kind() { - case constant.Bool: - return constant.BoolVal(x) - case constant.String: - return constant.StringVal(x) + case constant.Complex: + f := constant.ToFloat(x) + if f.Kind() != constant.Float { + r, _ := constant.Float64Val(constant.Real(x)) + i, _ := constant.Float64Val(constant.Imag(x)) + return complex(r, i) + } + x = f + fallthrough + case constant.Float: + i := constant.ToInt(x) + if i.Kind() != constant.Int { + v, _ := constant.Float64Val(x) + return v + } + x = i + fallthrough case constant.Int: if v, ok := constant.Int64Val(x); ok { return v @@ -1518,72 +1788,62 @@ func keyVal(x constant.Value) interface{} { if v, ok := constant.Uint64Val(x); ok { return v } - case constant.Float: - v, _ := constant.Float64Val(x) - return v - case constant.Complex: - r, _ := constant.Float64Val(constant.Real(x)) - i, _ := constant.Float64Val(constant.Imag(x)) - return complex(r, i) + case constant.String: + return constant.StringVal(x) + case constant.Bool: + return constant.BoolVal(x) } return x } -// typeAssertion checks that x.(T) is legal; xtyp must be the type of x. -func (check *Checker) typeAssertion(pos syntax.Pos, x *operand, xtyp *Interface, T Type) { - method, wrongType := check.assertableTo(xtyp, T) +// typeAssertion checks x.(T). The type of x must be an interface. +func (check *Checker) typeAssertion(e syntax.Expr, x *operand, T Type, typeSwitch bool) { + method, alt := check.assertableTo(under(x.typ).(*Interface), T) if method == nil { - return - } - var msg string - if wrongType != nil { - if check.identical(method.typ, wrongType.typ) { - msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name) - } else { - msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ) - } - } else { - msg = "missing method " + method.name + return // success } - if check.conf.CompilerErrorMessages { - check.errorf(pos, "impossible type assertion: %s (%s)", x, msg) - } else { - check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg) + + cause := check.missingMethodReason(T, x.typ, method, alt) + + if typeSwitch { + check.errorf(e, "impossible type switch case: %s\n\t%s cannot have dynamic type %s %s", e, x, T, cause) + return } + + check.errorf(e, "impossible type assertion: %s\n\t%s does not implement %s %s", e, T, x.typ, cause) } // expr typechecks expression e and initializes x with the expression value. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -// func (check *Checker) expr(x *operand, e syntax.Expr) { - check.rawExpr(x, e, nil) + check.rawExpr(x, e, nil, false) check.exclude(x, 1< 0 { + if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 { // function instantiation return true } } + // x should not be generic at this point, but be safe and check + check.nonGeneric(x) + if x.mode == invalid { + return false + } + // ordinary index expression valid := false length := int64(-1) // valid if >= 0 - switch typ := optype(x.typ).(type) { + switch typ := under(x.typ).(type) { case *Basic: if isString(typ) { valid = true @@ -64,7 +72,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo x.typ = typ.elem case *Pointer: - if typ := asArray(typ.base); typ != nil { + if typ, _ := under(typ.base).(*Array); typ != nil { valid = true length = typ.len x.mode = variable @@ -80,7 +88,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } var key operand check.expr(&key, index) @@ -89,101 +97,100 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo x.mode = mapindex x.typ = typ.elem x.expr = e - return + return false - case *Sum: - // A sum type can be indexed if all of the sum's types - // support indexing and have the same index and element - // type. Special rules apply for maps in the sum type. - var tkey, telem Type // key is for map types only - nmaps := 0 // number of map types in sum type - if typ.is(func(t Type) bool { - var e Type - switch t := under(t).(type) { + case *Interface: + if !isTypeParam(x.typ) { + break + } + // TODO(gri) report detailed failure cause for better error messages + var key, elem Type // key != nil: we must have all maps + mode := variable // non-maps result mode + // TODO(gri) factor out closure and use it for non-typeparam cases as well + if typ.typeSet().underIs(func(u Type) bool { + l := int64(-1) // valid if >= 0 + var k, e Type // k is only set for maps + switch t := u.(type) { case *Basic: if isString(t) { e = universeByte + mode = value } case *Array: + l = t.len e = t.elem + if x.mode != variable { + mode = value + } case *Pointer: - if t := asArray(t.base); t != nil { + if t, _ := under(t.base).(*Array); t != nil { + l = t.len e = t.elem } case *Slice: e = t.elem case *Map: - // If there are multiple maps in the sum type, - // they must have identical key types. - // TODO(gri) We may be able to relax this rule - // but it becomes complicated very quickly. - if tkey != nil && !Identical(t.key, tkey) { - return false - } - tkey = t.key + k = t.key e = t.elem - nmaps++ - case *TypeParam: - check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x) - case *instance: - panic("unimplemented") } - if e == nil || telem != nil && !Identical(e, telem) { + if e == nil { + return false + } + if elem == nil { + // first type + length = l + key, elem = k, e + return true + } + // all map keys must be identical (incl. all nil) + // (that is, we cannot mix maps with other types) + if !Identical(key, k) { + return false + } + // all element types must be identical + if !Identical(elem, e) { return false } - telem = e + // track the minimal length for arrays, if any + if l >= 0 && l < length { + length = l + } return true }) { - // If there are maps, the index expression must be assignable - // to the map key type (as for simple map index expressions). - if nmaps > 0 { + // For maps, the index expression must be assignable to the map key type. + if key != nil { index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } - var key operand - check.expr(&key, index) - check.assignment(&key, tkey, "map index") + var k operand + check.expr(&k, index) + check.assignment(&k, key, "map index") // ok to continue even if indexing failed - map element type is known - - // If there are only maps, we are done. - if nmaps == len(typ.types) { - x.mode = mapindex - x.typ = telem - x.expr = e - return - } - - // Otherwise we have mix of maps and other types. For - // now we require that the map key be an integer type. - // TODO(gri) This is probably not good enough. - valid = isInteger(tkey) - // avoid 2nd indexing error if indexing failed above - if !valid && key.mode == invalid { - x.mode = invalid - return - } - x.mode = value // map index expressions are not addressable - } else { - // no maps - valid = true - x.mode = variable + x.mode = mapindex + x.typ = elem + x.expr = e + return false } - x.typ = telem + + // no maps + valid = true + x.mode = mode + x.typ = elem } } if !valid { - check.errorf(x, invalidOp+"cannot index %s", x) + check.errorf(e.Pos(), invalidOp+"cannot index %s", x) x.mode = invalid - return + return false } index := check.singleIndex(e) if index == nil { x.mode = invalid - return + return false } // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0) @@ -206,11 +213,20 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) { valid := false length := int64(-1) // valid if >= 0 - switch typ := optype(x.typ).(type) { + switch u := coreString(x.typ).(type) { + case nil: + check.errorf(x, invalidOp+"cannot slice %s: %s has no core type", x, x.typ) + x.mode = invalid + return + case *Basic: - if isString(typ) { + if isString(u) { if e.Full { - check.error(x, invalidOp+"3-index slice of string") + at := e.Index[2] + if at == nil { + at = e // e.Index[2] should be present but be careful + } + check.error(at, invalidOp+"3-index slice of string") x.mode = invalid return } @@ -220,36 +236,31 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) { } // spec: "For untyped string operands the result // is a non-constant value of type string." - if typ.kind == UntypedString { + if isUntyped(x.typ) { x.typ = Typ[String] } } case *Array: valid = true - length = typ.len + length = u.len if x.mode != variable { check.errorf(x, invalidOp+"%s (slice of unaddressable value)", x) x.mode = invalid return } - x.typ = &Slice{elem: typ.elem} + x.typ = &Slice{elem: u.elem} case *Pointer: - if typ := asArray(typ.base); typ != nil { + if u, _ := under(u.base).(*Array); u != nil { valid = true - length = typ.len - x.typ = &Slice{elem: typ.elem} + length = u.len + x.typ = &Slice{elem: u.elem} } case *Slice: valid = true // x.typ doesn't change - - case *Sum, *TypeParam: - check.error(x, "generic slice expressions not yet implemented") - x.mode = invalid - return } if !valid { @@ -298,9 +309,12 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) { L: for i, x := range ind[:len(ind)-1] { if x > 0 { - for _, y := range ind[i+1:] { - if y >= 0 && x > y { - check.errorf(e, "invalid slice indices: %d > %d", x, y) + for j, y := range ind[i+1:] { + if y >= 0 && y < x { + // The value y corresponds to the expression e.Index[i+1+j]. + // Because y >= 0, it must have been set from the expression + // when checking indices and thus e.Index[i+1+j] is not nil. + check.errorf(e.Index[i+1+j], "invalid slice indices: %d < %d", y, x) break L // only report one error, ok to continue } } @@ -354,11 +368,7 @@ func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64) v, ok := constant.Int64Val(x.val) assert(ok) if max >= 0 && v >= max { - if check.conf.CompilerErrorMessages { - check.errorf(&x, invalidArg+"array index %s out of bounds [0:%d]", x.val.String(), max) - } else { - check.errorf(&x, invalidArg+"index %s is out of bounds", &x) - } + check.errorf(&x, invalidArg+"index %s out of bounds [0:%d]", x.val.String(), max) return } @@ -382,7 +392,7 @@ func (check *Checker) isValidIndex(x *operand, what string, allowNegative bool) } // spec: "the index x must be of integer type or an untyped constant" - if !isInteger(x.typ) { + if !allInteger(x.typ) { check.errorf(x, invalidArg+"%s %s must be integer", what, x) return false } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index f37d7f6477e96a..26e01e9ae5cfc1 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -7,8 +7,9 @@ package types2 import ( - "bytes" "cmd/compile/internal/syntax" + "fmt" + "strings" ) const useConstraintTypeInference = true @@ -18,17 +19,17 @@ const useConstraintTypeInference = true // function arguments args, if any. There must be at least one type parameter, no more type arguments // than type parameters, and params and args must match in number (incl. zero). // If successful, infer returns the complete list of type arguments, one for each type parameter. -// Otherwise the result is nil and appropriate errors will be reported unless report is set to false. +// Otherwise the result is nil and appropriate errors will be reported. // -// Inference proceeds in 3 steps: +// Inference proceeds as follows. Starting with given type arguments: // -// 1) Start with given type arguments. -// 2) Infer type arguments from typed function arguments. -// 3) Infer type arguments from untyped function arguments. +// 1. apply FTI (function type inference) with typed arguments, +// 2. apply CTI (constraint type inference), +// 3. apply FTI with untyped function arguments, +// 4. apply CTI. // -// Constraint type inference is used after each step to expand the set of type arguments. -// -func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) { +// The process stops as soon as all type arguments are known or an error occurs. +func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) { if debug { defer func() { assert(result == nil || len(result) == len(tparams)) @@ -39,6 +40,13 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p }() } + if traceInference { + check.dump("-- inferA %s%s ➞ %s", tparams, params, targs) + defer func() { + check.dump("=> inferA %s ➞ %s", tparams, result) + }() + } + // There must be at least one type parameter, and no more type arguments than type parameters. n := len(tparams) assert(n > 0 && len(targs) <= n) @@ -46,34 +54,124 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p // Function parameters and arguments must match in number. assert(params.Len() == len(args)) - // --- 0 --- // If we already have all type arguments, we're done. if len(targs) == n { return targs } // len(targs) < n - // --- 1 --- - // Explicitly provided type arguments take precedence over any inferred types; - // and types inferred via constraint type inference take precedence over types - // inferred from function arguments. - // If we have type arguments, see how far we get with constraint type inference. - if len(targs) > 0 && useConstraintTypeInference { - var index int - targs, index = check.inferB(tparams, targs, report) - if targs == nil || index < 0 { - return targs + const enableTparamRenaming = true + if enableTparamRenaming { + // For the purpose of type inference we must differentiate type parameters + // occurring in explicit type or value function arguments from the type + // parameters we are solving for via unification, because they may be the + // same in self-recursive calls. For example: + // + // func f[P *Q, Q any](p P, q Q) { + // f(p) + // } + // + // In this example, the fact that the P used in the instantation f[P] has + // the same pointer identity as the P we are trying to solve for via + // unification is coincidental: there is nothing special about recursive + // calls that should cause them to conflate the identity of type arguments + // with type parameters. To put it another way: any such self-recursive + // call is equivalent to a mutually recursive call, which does not run into + // any problems of type parameter identity. For example, the following code + // is equivalent to the code above. + // + // func f[P interface{*Q}, Q any](p P, q Q) { + // f2(p) + // } + // + // func f2[P interface{*Q}, Q any](p P, q Q) { + // f(p) + // } + // + // We can turn the first example into the second example by renaming type + // parameters in the original signature to give them a new identity. As an + // optimization, we do this only for self-recursive calls. + + // We can detect if we are in a self-recursive call by comparing the + // identity of the first type parameter in the current function with the + // first type parameter in tparams. This works because type parameters are + // unique to their type parameter list. + selfRecursive := check.sig != nil && check.sig.tparams.Len() > 0 && tparams[0] == check.sig.tparams.At(0) + + if selfRecursive { + // In self-recursive inference, rename the type parameters with new type + // parameters that are the same but for their pointer identity. + tparams2 := make([]*TypeParam, len(tparams)) + for i, tparam := range tparams { + tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil) + tparams2[i] = NewTypeParam(tname, nil) + tparams2[i].index = tparam.index // == i + } + + renameMap := makeRenameMap(tparams, tparams2) + for i, tparam := range tparams { + tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context()) + } + + tparams = tparams2 + params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple) } } - // Continue with the type arguments we have now. Avoid matching generic + // If we have more than 2 arguments, we may have arguments with named and unnamed types. + // If that is the case, permutate params and args such that the arguments with named + // types are first in the list. This doesn't affect type inference if all types are taken + // as is. But when we have inexact unification enabled (as is the case for function type + // inference), when a named type is unified with an unnamed type, unification proceeds + // with the underlying type of the named type because otherwise unification would fail + // right away. This leads to an asymmetry in type inference: in cases where arguments of + // named and unnamed types are passed to parameters with identical type, different types + // (named vs underlying) may be inferred depending on the order of the arguments. + // By ensuring that named types are seen first, order dependence is avoided and unification + // succeeds where it can (issue #43056). + const enableArgSorting = true + if m := len(args); m >= 2 && enableArgSorting { + // Determine indices of arguments with named and unnamed types. + var named, unnamed []int + for i, arg := range args { + if hasName(arg.typ) { + named = append(named, i) + } else { + unnamed = append(unnamed, i) + } + } + + // If we have named and unnamed types, move the arguments with + // named types first. Update the parameter list accordingly. + // Make copies so as not to clobber the incoming slices. + if len(named) != 0 && len(unnamed) != 0 { + params2 := make([]*Var, m) + args2 := make([]*operand, m) + i := 0 + for _, j := range named { + params2[i] = params.At(j) + args2[i] = args[j] + i++ + } + for _, j := range unnamed { + params2[i] = params.At(j) + args2[i] = args[j] + i++ + } + params = NewTuple(params2...) + args = args2 + } + } + + // --- 1 --- + // Continue with the type arguments we have. Avoid matching generic // parameters that already have type arguments against function arguments: // It may fail because matching uses type identity while parameter passing // uses assignment rules. Instantiate the parameter list with the type // arguments we have, and continue with that parameter list. - // First, make sure we have a "full" list of type arguments, so of which - // may be nil (unknown). + // First, make sure we have a "full" list of type arguments, some of which + // may be nil (unknown). Make a copy so as to not clobber the incoming slice. if len(targs) < n { targs2 := make([]Type, n) copy(targs2, targs) @@ -83,18 +181,17 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p // Substitute type arguments for their respective type parameters in params, // if any. Note that nil targs entries are ignored by check.subst. - // TODO(gri) Can we avoid this (we're setting known type argumemts below, + // TODO(gri) Can we avoid this (we're setting known type arguments below, // but that doesn't impact the isParameterized check for now). if params.Len() > 0 { smap := makeSubstMap(tparams, targs) - params = check.subst(nopos, params, smap).(*Tuple) + params = check.subst(nopos, params, smap, nil, check.context()).(*Tuple) } - // --- 2 --- // Unify parameter and argument types for generic parameters with typed arguments // and collect the indices of generic parameters with untyped arguments. // Terminology: generic parameter = function parameter with a type-parameterized type - u := newUnifier(check, false) + u := newUnifier(false) u.x.init(tparams) // Set the type arguments which we know already. @@ -105,9 +202,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p } errorf := func(kind string, tpar, targ Type, arg *operand) { - if !report { - return - } // provide a better error message if we can targs, index := u.x.types() if index == 0 { @@ -122,12 +216,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p } } if allFailed { - check.errorf(arg, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams)) + check.errorf(arg, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams)) return } } smap := makeSubstMap(tparams, targs) - inferred := check.subst(arg.Pos(), tpar, smap) + inferred := check.subst(arg.Pos(), tpar, smap, nil, check.context()) if inferred != tpar { check.errorf(arg, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) } else { @@ -146,7 +240,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p if arg.mode == invalid { // An error was reported earlier. Ignore this targ // and continue, we may still be able to infer all - // targs resulting in fewer follon-on errors. + // targs resulting in fewer follow-on errors. continue } if targ := arg.typ; isTyped(targ) { @@ -157,7 +251,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p errorf("type", par.typ, targ, arg) return nil } - } else { + } else if _, ok := par.typ.(*TypeParam); ok { + // Since default types are all basic (i.e., non-composite) types, an + // untyped argument will never match a composite parameter type; the + // only parameter type it can possibly match against is a *TypeParam. + // Thus, for untyped arguments we only need to look at parameter types + // that are single type parameters. indices = append(indices, i) } } @@ -170,11 +269,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p return targs } + // --- 2 --- // See how far we get with constraint type inference. // Note that even if we don't have any type arguments, constraint type inference // may produce results for constraints that explicitly specify a type. if useConstraintTypeInference { - targs, index = check.inferB(tparams, targs, report) + targs, index = check.inferB(pos, tparams, targs) if targs == nil || index < 0 { return targs } @@ -185,20 +285,17 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p // Some generic parameters with untyped arguments may have been given // a type by now, we can ignore them. for _, i := range indices { - par := params.At(i) - // Since untyped types are all basic (i.e., non-composite) types, an - // untyped argument will never match a composite parameter type; the - // only parameter type it can possibly match against is a *TypeParam. - // Thus, only consider untyped arguments for generic parameters that - // are not of composite types and which don't have a type inferred yet. - if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil { + tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of indices + // Only consider untyped arguments for which the corresponding type + // parameter doesn't have an inferred type yet. + if targs[tpar.index] == nil { arg := args[i] targ := Default(arg.typ) // The default type for an untyped nil is untyped nil. We must not // infer an untyped nil type as type parameter type. Ignore untyped // nil by making sure all default argument types are typed. - if isTyped(targ) && !u.unify(par.typ, targ) { - errorf("default type", par.typ, targ, arg) + if isTyped(targ) && !u.unify(tpar, targ) { + errorf("default type", tpar, targ, arg) return nil } } @@ -210,9 +307,10 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p return targs } + // --- 4 --- // Again, follow up with constraint type inference. if useConstraintTypeInference { - targs, index = check.inferB(tparams, targs, report) + targs, index = check.inferB(pos, tparams, targs) if targs == nil || index < 0 { return targs } @@ -221,42 +319,39 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p // At least one type argument couldn't be inferred. assert(targs != nil && index >= 0 && targs[index] == nil) tpar := tparams[index] - if report { - check.errorf(pos, "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs) - } + check.errorf(pos, "cannot infer %s (%s)", tpar.obj.name, tpar.obj.pos) return nil } -// typeNamesString produces a string containing all the -// type names in list suitable for human consumption. -func typeNamesString(list []*TypeName) string { +// typeParamsString produces a string of the type parameter names +// in list suitable for human consumption. +func typeParamsString(list []*TypeParam) string { // common cases n := len(list) switch n { case 0: return "" case 1: - return list[0].name + return list[0].obj.name case 2: - return list[0].name + " and " + list[1].name + return list[0].obj.name + " and " + list[1].obj.name } // general case (n > 2) - // Would like to use strings.Builder but it's not available in Go 1.4. - var b bytes.Buffer + var buf strings.Builder for i, tname := range list[:n-1] { if i > 0 { - b.WriteString(", ") + buf.WriteString(", ") } - b.WriteString(tname.name) + buf.WriteString(tname.obj.name) } - b.WriteString(", and ") - b.WriteString(list[n-1].name) - return b.String() + buf.WriteString(", and ") + buf.WriteString(list[n-1].obj.name) + return buf.String() } -// IsParameterized reports whether typ contains any of the type parameters of tparams. -func isParameterized(tparams []*TypeName, typ Type) bool { +// isParameterized reports whether typ contains any of the type parameters of tparams. +func isParameterized(tparams []*TypeParam, typ Type) bool { w := tpWalker{ seen: make(map[Type]bool), tparams: tparams, @@ -266,7 +361,7 @@ func isParameterized(tparams []*TypeName, typ Type) bool { type tpWalker struct { seen map[Type]bool - tparams []*TypeName + tparams []*TypeParam } func (w *tpWalker) isParameterized(typ Type) (res bool) { @@ -307,9 +402,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { } } - case *Sum: - return w.isParameterizedList(t.types) - case *Signature: // t.tparams may not be nil if we are looking at a signature // of a generic function type (or an interface method) that is @@ -321,24 +413,15 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.params) || w.isParameterized(t.results) case *Interface: - if t.allMethods != nil { - // interface is complete - quick test - for _, m := range t.allMethods { - if w.isParameterized(m.typ) { - return true - } + tset := t.typeSet() + for _, m := range tset.methods { + if w.isParameterized(m.typ) { + return true } - return w.isParameterizedList(unpack(t.allTypes)) } - - return t.iterate(func(t *Interface) bool { - for _, m := range t.methods { - if w.isParameterized(m.typ) { - return true - } - } - return w.isParameterizedList(unpack(t.types)) - }, nil) + return tset.is(func(t *term) bool { + return t != nil && w.isParameterized(t.typ) + }) case *Map: return w.isParameterized(t.key) || w.isParameterized(t.elem) @@ -347,14 +430,11 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return w.isParameterized(t.elem) case *Named: - return w.isParameterizedList(t.targs) + return w.isParameterizedTypeList(t.TypeArgs().list()) case *TypeParam: // t must be one of w.tparams - return t.index < len(w.tparams) && w.tparams[t.index].typ == t - - case *instance: - return w.isParameterizedList(t.targs) + return tparamIndex(w.tparams, t) >= 0 default: unreachable() @@ -363,7 +443,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) { return false } -func (w *tpWalker) isParameterizedList(list []Type) bool { +func (w *tpWalker) isParameterizedTypeList(list []Type) bool { for _, t := range list { if w.isParameterized(t) { return true @@ -380,12 +460,19 @@ func (w *tpWalker) isParameterizedList(list []Type) bool { // first type argument in that list that couldn't be inferred (and thus is nil). If all // type arguments were inferred successfully, index is < 0. The number of type arguments // provided may be less than the number of type parameters, but there must be at least one. -func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (types []Type, index int) { +func (check *Checker) inferB(pos syntax.Pos, tparams []*TypeParam, targs []Type) (types []Type, index int) { assert(len(tparams) >= len(targs) && len(targs) > 0) - // Setup bidirectional unification between those structural bounds + if traceInference { + check.dump("-- inferB %s ➞ %s", tparams, targs) + defer func() { + check.dump("=> inferB %s ➞ %s", tparams, types) + }() + } + + // Setup bidirectional unification between constraints // and the corresponding type arguments (which may be nil!). - u := newUnifier(check, false) + u := newUnifier(false) u.x.init(tparams) u.y = u.x // type parameters between LHS and RHS of unification are identical @@ -396,24 +483,89 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty } } - // Unify type parameters with their structural constraints, if any. - for _, tpar := range tparams { - typ := tpar.typ.(*TypeParam) - sbound := check.structuralType(typ.bound) - if sbound != nil { - if !u.unify(typ, sbound) { - if report { - check.errorf(tpar, "%s does not match %s", tpar, sbound) + // Repeatedly apply constraint type inference as long as + // there are still unknown type arguments and progress is + // being made. + // + // This is an O(n^2) algorithm where n is the number of + // type parameters: if there is progress (and iteration + // continues), at least one type argument is inferred + // per iteration and we have a doubly nested loop. + // In practice this is not a problem because the number + // of type parameters tends to be very small (< 5 or so). + // (It should be possible for unification to efficiently + // signal newly inferred type arguments; then the loops + // here could handle the respective type parameters only, + // but that will come at a cost of extra complexity which + // may not be worth it.) + for n := u.x.unknowns(); n > 0; { + nn := n + + for i, tpar := range tparams { + // If there is a core term (i.e., a core type with tilde information) + // unify the type parameter with the core type. + if core, single := coreTerm(tpar); core != nil { + // A type parameter can be unified with its core type in two cases. + tx := u.x.at(i) + switch { + case tx != nil: + // The corresponding type argument tx is known. + // In this case, if the core type has a tilde, the type argument's underlying + // type must match the core type, otherwise the type argument and the core type + // must match. + // If tx is an external type parameter, don't consider its underlying type + // (which is an interface). Core type unification will attempt to unify against + // core.typ. + // Note also that even with inexact unification we cannot leave away the under + // call here because it's possible that both tx and core.typ are named types, + // with under(tx) being a (named) basic type matching core.typ. Such cases do + // not match with inexact unification. + if core.tilde && !isTypeParam(tx) { + tx = under(tx) + } + if !u.unify(tx, core.typ) { + // TODO(gri) improve error message by providing the type arguments + // which we know already + // Don't use term.String() as it always qualifies types, even if they + // are in the current package. + tilde := "" + if core.tilde { + tilde = "~" + } + check.errorf(pos, "%s does not match %s%s", tpar, tilde, core.typ) + return nil, 0 + } + + case single && !core.tilde: + // The corresponding type argument tx is unknown and there's a single + // specific type and no tilde. + // In this case the type argument must be that single type; set it. + u.x.set(i, core.typ) + + default: + // Unification is not possible and no progress was made. + continue + } + + // The number of known type arguments may have changed. + nn = u.x.unknowns() + if nn == 0 { + break // all type arguments are known } - return nil, 0 } } + + assert(nn <= n) + if nn == n { + break // no progress + } + n = nn } // u.x.types() now contains the incoming type arguments plus any additional type - // arguments for which there were structural constraints. The newly inferred non- - // nil entries may still contain references to other type parameters. For instance, - // for [A any, B interface{type []C}, C interface{type *A}], if A == int + // arguments which were inferred from core terms. The newly inferred non-nil + // entries may still contain references to other type parameters. + // For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int // was given, unification produced the type list [int, []C, *A]. We eliminate the // remaining type parameters by substituting the type parameters in this type list // until nothing changes anymore. @@ -424,6 +576,34 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty } } + // The data structure of each (provided or inferred) type represents a graph, where + // each node corresponds to a type and each (directed) vertice points to a component + // type. The substitution process described above repeatedly replaces type parameter + // nodes in these graphs with the graphs of the types the type parameters stand for, + // which creates a new (possibly bigger) graph for each type. + // The substitution process will not stop if the replacement graph for a type parameter + // also contains that type parameter. + // For instance, for [A interface{ *A }], without any type argument provided for A, + // unification produces the type list [*A]. Substituting A in *A with the value for + // A will lead to infinite expansion by producing [**A], [****A], [********A], etc., + // because the graph A -> *A has a cycle through A. + // Generally, cycles may occur across multiple type parameters and inferred types + // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]). + // We eliminate cycles by walking the graphs for all type parameters. If a cycle + // through a type parameter is detected, cycleFinder nils out the respectice type + // which kills the cycle; this also means that the respective type could not be + // inferred. + // + // TODO(gri) If useful, we could report the respective cycle as an error. We don't + // do this now because type inference will fail anyway, and furthermore, + // constraints with cycles of this kind cannot currently be satisfied by + // any user-suplied type. But should that change, reporting an error + // would be wrong. + w := cycleFinder{tparams, types, make(map[Type]bool)} + for _, t := range tparams { + w.typ(t) // t != nil + } + // dirty tracks the indices of all types that may still contain type parameters. // We know that nil type entries and entries corresponding to provided (non-nil) // type arguments are clean, so exclude them from the start. @@ -442,7 +622,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty n := 0 for _, index := range dirty { t0 := types[index] - if t1 := check.subst(nopos, t0, smap); t1 != t0 { + if t1 := check.subst(nopos, t0, smap, nil, check.context()); t1 != t0 { types[index] = t1 dirty[n] = index n++ @@ -452,8 +632,8 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty } // Once nothing changes anymore, we may still have type parameters left; - // e.g., a structural constraint *P may match a type parameter Q but we - // don't have any type arguments to fill in for *P or Q (issue #45548). + // e.g., a constraint with core type *P may match a type parameter Q but + // we don't have any type arguments to fill in for *P or Q (issue #45548). // Don't let such inferences escape, instead nil them out. for i, typ := range types { if typ != nil && isParameterized(tparams, typ) { @@ -473,15 +653,131 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty return } -// structuralType returns the structural type of a constraint, if any. -func (check *Checker) structuralType(constraint Type) Type { - if iface, _ := under(constraint).(*Interface); iface != nil { - check.completeInterface(nopos, iface) - types := unpack(iface.allTypes) - if len(types) == 1 { - return types[0] +// If the type parameter has a single specific type S, coreTerm returns (S, true). +// Otherwise, if tpar has a core type T, it returns a term corresponding to that +// core type and false. In that case, if any term of tpar has a tilde, the core +// term has a tilde. In all other cases coreTerm returns (nil, false). +func coreTerm(tpar *TypeParam) (*term, bool) { + n := 0 + var single *term // valid if n == 1 + var tilde bool + tpar.is(func(t *term) bool { + if t == nil { + assert(n == 0) + return false // no terms + } + n++ + single = t + if t.tilde { + tilde = true + } + return true + }) + if n == 1 { + if debug { + assert(debug && under(single.typ) == coreType(tpar)) } - return nil + return single, true + } + if typ := coreType(tpar); typ != nil { + // A core type is always an underlying type. + // If any term of tpar has a tilde, we don't + // have a precise core type and we must return + // a tilde as well. + return &term{tilde, typ}, false + } + return nil, false +} + +type cycleFinder struct { + tparams []*TypeParam + types []Type + seen map[Type]bool +} + +func (w *cycleFinder) typ(typ Type) { + if w.seen[typ] { + // We have seen typ before. If it is one of the type parameters + // in tparams, iterative substitution will lead to infinite expansion. + // Nil out the corresponding type which effectively kills the cycle. + if tpar, _ := typ.(*TypeParam); tpar != nil { + if i := tparamIndex(w.tparams, tpar); i >= 0 { + // cycle through tpar + w.types[i] = nil + } + } + // If we don't have one of our type parameters, the cycle is due + // to an ordinary recursive type and we can just stop walking it. + return + } + w.seen[typ] = true + defer delete(w.seen, typ) + + switch t := typ.(type) { + case *Basic: + // nothing to do + + case *Array: + w.typ(t.elem) + + case *Slice: + w.typ(t.elem) + + case *Struct: + w.varList(t.fields) + + case *Pointer: + w.typ(t.base) + + // case *Tuple: + // This case should not occur because tuples only appear + // in signatures where they are handled explicitly. + + case *Signature: + if t.params != nil { + w.varList(t.params.vars) + } + if t.results != nil { + w.varList(t.results.vars) + } + + case *Union: + for _, t := range t.terms { + w.typ(t.typ) + } + + case *Interface: + for _, m := range t.methods { + w.typ(m.typ) + } + for _, t := range t.embeddeds { + w.typ(t) + } + + case *Map: + w.typ(t.key) + w.typ(t.elem) + + case *Chan: + w.typ(t.elem) + + case *Named: + for _, tpar := range t.TypeArgs().list() { + w.typ(tpar) + } + + case *TypeParam: + if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil { + w.typ(w.types[i]) + } + + default: + panic(fmt.Sprintf("unexpected %T", typ)) + } +} + +func (w *cycleFinder) varList(list []*Var) { + for _, v := range list { + w.typ(v.typ) } - return constraint } diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go index 40816276665117..cf6110baa92c0f 100644 --- a/src/cmd/compile/internal/types2/initorder.go +++ b/src/cmd/compile/internal/types2/initorder.go @@ -7,6 +7,7 @@ package types2 import ( "container/heap" "fmt" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -190,6 +191,12 @@ type graphNode struct { ndeps int // number of outstanding dependencies before this object can be initialized } +// cost returns the cost of removing this node, which involves copying each +// predecessor to each successor (and vice-versa). +func (n *graphNode) cost() int { + return len(n.pred) * len(n.succ) +} + type nodeSet map[*graphNode]bool func (s *nodeSet) add(p *graphNode) { @@ -227,35 +234,48 @@ func dependencyGraph(objMap map[Object]*declInfo) []*graphNode { } } + var G, funcG []*graphNode // separate non-functions and functions + for _, n := range M { + if _, ok := n.obj.(*Func); ok { + funcG = append(funcG, n) + } else { + G = append(G, n) + } + } + // remove function nodes and collect remaining graph nodes in G // (Mutually recursive functions may introduce cycles among themselves // which are permitted. Yet such cycles may incorrectly inflate the dependency // count for variables which in turn may not get scheduled for initialization // in correct order.) - var G []*graphNode - for obj, n := range M { - if _, ok := obj.(*Func); ok { - // connect each predecessor p of n with each successor s - // and drop the function node (don't collect it in G) - for p := range n.pred { - // ignore self-cycles - if p != n { - // Each successor s of n becomes a successor of p, and - // each predecessor p of n becomes a predecessor of s. - for s := range n.succ { - // ignore self-cycles - if s != n { - p.succ.add(s) - s.pred.add(p) - delete(s.pred, n) // remove edge to n - } + // + // Note that because we recursively copy predecessors and successors + // throughout the function graph, the cost of removing a function at + // position X is proportional to cost * (len(funcG)-X). Therefore, we should + // remove high-cost functions last. + sort.Slice(funcG, func(i, j int) bool { + return funcG[i].cost() < funcG[j].cost() + }) + for _, n := range funcG { + // connect each predecessor p of n with each successor s + // and drop the function node (don't collect it in G) + for p := range n.pred { + // ignore self-cycles + if p != n { + // Each successor s of n becomes a successor of p, and + // each predecessor p of n becomes a predecessor of s. + for s := range n.succ { + // ignore self-cycles + if s != n { + p.succ.add(s) + s.pred.add(p) } - delete(p.succ, n) // remove edge to n } + delete(p.succ, n) // remove edge to n } - } else { - // collect non-function nodes - G = append(G, n) + } + for s := range n.succ { + delete(s.pred, n) // remove edge to n } } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 0df52e851c9de4..ddabeab72ea407 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -2,62 +2,304 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// This file implements instantiation of generic types +// through substitution of type parameters by type arguments. + package types2 import ( "cmd/compile/internal/syntax" + "errors" "fmt" ) -// Instantiate instantiates the type typ with the given type arguments. -// typ must be a *Named or a *Signature type, it must be generic, and -// its number of type parameters must match the number of provided type -// arguments. The result is a new, instantiated (not generic) type of -// the same kind (either a *Named or a *Signature). The type arguments -// are not checked against the constraints of the type parameters. -// Any methods attached to a *Named are simply copied; they are not -// instantiated. -func Instantiate(pos syntax.Pos, typ Type, targs []Type) (res Type) { - // TODO(gri) This code is basically identical to the prolog - // in Checker.instantiate. Factor. - var tparams []*TypeName - switch t := typ.(type) { +// Instantiate instantiates the type orig with the given type arguments targs. +// orig must be a *Named or a *Signature type. If there is no error, the +// resulting Type is an instantiated type of the same kind (either a *Named or +// a *Signature). Methods attached to a *Named type are also instantiated, and +// associated with a new *Func that has the same position as the original +// method, but nil function scope. +// +// If ctxt is non-nil, it may be used to de-duplicate the instance against +// previous instances with the same identity. As a special case, generic +// *Signature origin types are only considered identical if they are pointer +// equivalent, so that instantiating distinct (but possibly identical) +// signatures will yield different instances. The use of a shared context does +// not guarantee that identical instances are deduplicated in all cases. +// +// If validate is set, Instantiate verifies that the number of type arguments +// and parameters match, and that the type arguments satisfy their +// corresponding type constraints. If verification fails, the resulting error +// may wrap an *ArgumentError indicating which type argument did not satisfy +// its corresponding type parameter constraint, and why. +// +// If validate is not set, Instantiate does not verify the type argument count +// or whether the type arguments satisfy their constraints. Instantiate is +// guaranteed to not return an error, but may panic. Specifically, for +// *Signature types, Instantiate will panic immediately if the type argument +// count is incorrect; for *Named types, a panic may occur later inside the +// *Named API. +func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) { + if ctxt == nil { + ctxt = NewContext() + } + if validate { + var tparams []*TypeParam + switch t := orig.(type) { + case *Named: + tparams = t.TypeParams().list() + case *Signature: + tparams = t.TypeParams().list() + } + if len(targs) != len(tparams) { + return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams)) + } + if i, err := (*Checker)(nil).verify(nopos, tparams, targs, ctxt); err != nil { + return nil, &ArgumentError{i, err} + } + } + + inst := (*Checker)(nil).instance(nopos, orig, targs, nil, ctxt) + return inst, nil +} + +// instance instantiates the given original (generic) function or type with the +// provided type arguments and returns the resulting instance. If an identical +// instance exists already in the given contexts, it returns that instance, +// otherwise it creates a new one. +// +// If expanding is non-nil, it is the Named instance type currently being +// expanded. If ctxt is non-nil, it is the context associated with the current +// type-checking pass or call to Instantiate. At least one of expanding or ctxt +// must be non-nil. +// +// For Named types the resulting instance may be unexpanded. +func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) { + // The order of the contexts below matters: we always prefer instances in the + // expanding instance context in order to preserve reference cycles. + // + // Invariant: if expanding != nil, the returned instance will be the instance + // recorded in expanding.inst.ctxt. + var ctxts []*Context + if expanding != nil { + ctxts = append(ctxts, expanding.inst.ctxt) + } + if ctxt != nil { + ctxts = append(ctxts, ctxt) + } + assert(len(ctxts) > 0) + + // Compute all hashes; hashes may differ across contexts due to different + // unique IDs for Named types within the hasher. + hashes := make([]string, len(ctxts)) + for i, ctxt := range ctxts { + hashes[i] = ctxt.instanceHash(orig, targs) + } + + // If local is non-nil, updateContexts return the type recorded in + // local. + updateContexts := func(res Type) Type { + for i := len(ctxts) - 1; i >= 0; i-- { + res = ctxts[i].update(hashes[i], orig, targs, res) + } + return res + } + + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + for i, ctxt := range ctxts { + if inst := ctxt.lookup(hashes[i], orig, targs); inst != nil { + return updateContexts(inst) + } + } + + switch orig := orig.(type) { case *Named: - tparams = t.tparams + res = check.newNamedInstance(pos, orig, targs, expanding) // substituted lazily + case *Signature: - tparams = t.tparams - defer func() { - // If we had an unexpected failure somewhere don't panic below when - // asserting res.(*Signature). Check for *Signature in case Typ[Invalid] - // is returned. - if _, ok := res.(*Signature); !ok { - return - } - // If the signature doesn't use its type parameters, subst - // will not make a copy. In that case, make a copy now (so - // we can set tparams to nil w/o causing side-effects). - if t == res { - copy := *t - res = © - } - // After instantiating a generic signature, it is not generic - // anymore; we need to set tparams to nil. - res.(*Signature).tparams = nil - }() + assert(expanding == nil) // function instances cannot be reached from Named types + + tparams := orig.TypeParams() + if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { + return Typ[Invalid] + } + if tparams.Len() == 0 { + return orig // nothing to do (minor optimization) + } + sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), nil, ctxt).(*Signature) + // If the signature doesn't use its type parameters, subst + // will not make a copy. In that case, make a copy now (so + // we can set tparams to nil w/o causing side-effects). + if sig == orig { + copy := *sig + sig = © + } + // After instantiating a generic signature, it is not generic + // anymore; we need to set tparams to nil. + sig.tparams = nil + res = sig default: - panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) + // only types and functions can be generic + panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig)) } - // the number of supplied types must match the number of type parameters - if len(targs) != len(tparams) { - panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams))) - } + // Update all contexts; it's possible that we've lost a race. + return updateContexts(res) +} - if len(tparams) == 0 { - return typ // nothing to do (minor optimization) +// validateTArgLen verifies that the length of targs and tparams matches, +// reporting an error if not. If validation fails and check is nil, +// validateTArgLen panics. +func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool { + if ntargs != ntparams { + // TODO(gri) provide better error message + if check != nil { + check.errorf(pos, "got %d arguments but %d type parameters", ntargs, ntparams) + return false + } + panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams)) } + return true +} +func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { smap := makeSubstMap(tparams, targs) - return (*Checker)(nil).subst(pos, typ, smap) + for i, tpar := range tparams { + // Ensure that we have a (possibly implicit) interface as type bound (issue #51048). + tpar.iface() + // The type parameter bound is parameterized with the same type parameters + // as the instantiated type; before we can use it for bounds checking we + // need to instantiate it with the type arguments with which we instantiated + // the parameterized type. + bound := check.subst(pos, tpar.bound, smap, nil, ctxt) + var reason string + if !check.implements(targs[i], bound, &reason) { + return i, errors.New(reason) + } + } + return -1, nil +} + +// implements checks if V implements T. The receiver may be nil if implements +// is called through an exported API call such as AssignableTo. +// +// If the provided reason is non-nil, it may be set to an error string +// explaining why V does not implement T. +func (check *Checker) implements(V, T Type, reason *string) bool { + Vu := under(V) + Tu := under(T) + if Vu == Typ[Invalid] || Tu == Typ[Invalid] { + return true // avoid follow-on errors + } + if p, _ := Vu.(*Pointer); p != nil && under(p.base) == Typ[Invalid] { + return true // avoid follow-on errors (see issue #49541 for an example) + } + + Ti, _ := Tu.(*Interface) + if Ti == nil { + var cause string + if isInterfacePtr(Tu) { + cause = check.sprintf("type %s is pointer to interface, not interface", T) + } else { + cause = check.sprintf("%s is not an interface", T) + } + if reason != nil { + *reason = check.sprintf("%s does not implement %s (%s)", V, T, cause) + } + return false + } + + // Every type satisfies the empty interface. + if Ti.Empty() { + return true + } + // T is not the empty interface (i.e., the type set of T is restricted) + + // An interface V with an empty type set satisfies any interface. + // (The empty set is a subset of any set.) + Vi, _ := Vu.(*Interface) + if Vi != nil && Vi.typeSet().IsEmpty() { + return true + } + // type set of V is not empty + + // No type with non-empty type set satisfies the empty type set. + if Ti.typeSet().IsEmpty() { + if reason != nil { + *reason = check.sprintf("cannot implement %s (empty type set)", T) + } + return false + } + + // V must implement T's methods, if any. + if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ { + if reason != nil { + *reason = check.sprintf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong)) + } + return false + } + + // Only check comparability if we don't have a more specific error. + checkComparability := func() bool { + // If T is comparable, V must be comparable. + if Ti.IsComparable() && !comparable(V, false, nil, nil) { + if reason != nil { + *reason = check.sprintf("%s does not implement comparable", V) + } + return false + } + return true + } + + // V must also be in the set of types of T, if any. + // Constraints with empty type sets were already excluded above. + if !Ti.typeSet().hasTerms() { + return checkComparability() // nothing to do + } + + // If V is itself an interface, each of its possible types must be in the set + // of T types (i.e., the V type set must be a subset of the T type set). + // Interfaces V with empty type sets were already excluded above. + if Vi != nil { + if !Vi.typeSet().subsetOf(Ti.typeSet()) { + // TODO(gri) report which type is missing + if reason != nil { + *reason = check.sprintf("%s does not implement %s", V, T) + } + return false + } + return checkComparability() + } + + // Otherwise, V's type must be included in the iface type set. + var alt Type + if Ti.typeSet().is(func(t *term) bool { + if !t.includes(V) { + // If V ∉ t.typ but V ∈ ~t.typ then remember this type + // so we can suggest it as an alternative in the error + // message. + if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) { + tt := *t + tt.tilde = true + if tt.includes(V) { + alt = t.typ + } + } + return true + } + return false + }) { + if reason != nil { + if alt != nil { + *reason = check.sprintf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T) + } else { + *reason = check.sprintf("%s does not implement %s (%s missing in %s)", V, T, V, Ti.typeSet().terms) + } + } + return false + } + + return checkComparability() } diff --git a/src/cmd/compile/internal/types2/instantiate_test.go b/src/cmd/compile/internal/types2/instantiate_test.go new file mode 100644 index 00000000000000..3c897869fc26f3 --- /dev/null +++ b/src/cmd/compile/internal/types2/instantiate_test.go @@ -0,0 +1,247 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package types2_test + +import ( + . "cmd/compile/internal/types2" + "strings" + "testing" +) + +func TestInstantiateEquality(t *testing.T) { + emptySignature := NewSignatureType(nil, nil, nil, nil, nil, false) + tests := []struct { + src string + name1 string + targs1 []Type + name2 string + targs2 []Type + wantEqual bool + }{ + { + "package basictype; type T[P any] int", + "T", []Type{Typ[Int]}, + "T", []Type{Typ[Int]}, + true, + }, + { + "package differenttypeargs; type T[P any] int", + "T", []Type{Typ[Int]}, + "T", []Type{Typ[String]}, + false, + }, + { + "package typeslice; type T[P any] int", + "T", []Type{NewSlice(Typ[Int])}, + "T", []Type{NewSlice(Typ[Int])}, + true, + }, + { + // interface{interface{...}} is equivalent to interface{...} + "package equivalentinterfaces; type T[P any] int", + "T", []Type{ + NewInterfaceType([]*Func{NewFunc(nopos, nil, "M", emptySignature)}, nil), + }, + "T", []Type{ + NewInterfaceType( + nil, + []Type{ + NewInterfaceType([]*Func{NewFunc(nopos, nil, "M", emptySignature)}, nil), + }, + ), + }, + true, + }, + { + // int|string is equivalent to string|int + "package equivalenttypesets; type T[P any] int", + "T", []Type{ + NewInterfaceType(nil, []Type{ + NewUnion([]*Term{NewTerm(false, Typ[Int]), NewTerm(false, Typ[String])}), + }), + }, + "T", []Type{ + NewInterfaceType(nil, []Type{ + NewUnion([]*Term{NewTerm(false, Typ[String]), NewTerm(false, Typ[Int])}), + }), + }, + true, + }, + { + "package basicfunc; func F[P any]() {}", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[Int]}, + true, + }, + { + "package funcslice; func F[P any]() {}", + "F", []Type{NewSlice(Typ[Int])}, + "F", []Type{NewSlice(Typ[Int])}, + true, + }, + { + "package funcwithparams; func F[P any](x string) float64 { return 0 }", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[Int]}, + true, + }, + { + "package differentfuncargs; func F[P any](x string) float64 { return 0 }", + "F", []Type{Typ[Int]}, + "F", []Type{Typ[String]}, + false, + }, + { + "package funcequality; func F1[P any](x int) {}; func F2[Q any](x int) {}", + "F1", []Type{Typ[Int]}, + "F2", []Type{Typ[Int]}, + false, + }, + { + "package funcsymmetry; func F1[P any](x P) {}; func F2[Q any](x Q) {}", + "F1", []Type{Typ[Int]}, + "F2", []Type{Typ[Int]}, + false, + }, + } + + for _, test := range tests { + pkg, err := pkgFor(".", test.src, nil) + if err != nil { + t.Fatal(err) + } + + t.Run(pkg.Name(), func(t *testing.T) { + ctxt := NewContext() + + T1 := pkg.Scope().Lookup(test.name1).Type() + res1, err := Instantiate(ctxt, T1, test.targs1, false) + if err != nil { + t.Fatal(err) + } + + T2 := pkg.Scope().Lookup(test.name2).Type() + res2, err := Instantiate(ctxt, T2, test.targs2, false) + if err != nil { + t.Fatal(err) + } + + if gotEqual := res1 == res2; gotEqual != test.wantEqual { + t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual) + } + }) + } +} + +func TestInstantiateNonEquality(t *testing.T) { + const src = "package p; type T[P any] int" + pkg1, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + pkg2, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + // We consider T1 and T2 to be distinct types, so their instances should not + // be deduplicated by the context. + T1 := pkg1.Scope().Lookup("T").Type().(*Named) + T2 := pkg2.Scope().Lookup("T").Type().(*Named) + ctxt := NewContext() + res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + if res1 == res2 { + t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2) + } + if Identical(res1, res2) { + t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2) + } +} + +func TestMethodInstantiation(t *testing.T) { + const prefix = `package p + +type T[P any] struct{} + +var X T[int] + +` + tests := []struct { + decl string + want string + }{ + {"func (r T[P]) m() P", "func (T[int]).m() int"}, + {"func (r T[P]) m(P)", "func (T[int]).m(int)"}, + {"func (r *T[P]) m(P)", "func (*T[int]).m(int)"}, + {"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"}, + {"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"}, + {"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"}, + {"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"}, + } + + for _, test := range tests { + src := prefix + test.decl + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + typ := NewPointer(pkg.Scope().Lookup("X").Type()) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + m, _ := obj.(*Func) + if m == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + if got := ObjectString(m, RelativeTo(pkg)); got != test.want { + t.Errorf("instantiated %q, want %q", got, test.want) + } + } +} + +func TestImmutableSignatures(t *testing.T) { + const src = `package p + +type T[P any] struct{} + +func (T[P]) m() {} + +var _ T[int] +` + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + typ := pkg.Scope().Lookup("T").Type().(*Named) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + if obj == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + + // Verify that the original method is not mutated by instantiating T (this + // bug manifested when subst did not return a new signature). + want := "func (T[P]).m()" + if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want { + t.Errorf("instantiated %q, want %q", got, want) + } +} + +// Copied from errors.go. +func stripAnnotations(s string) string { + var buf strings.Builder + for _, r := range s { + // strip #'s and subscript digits + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 + buf.WriteRune(r) + } + } + if buf.Len() < len(s) { + return buf.String() + } + return s +} diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go new file mode 100644 index 00000000000000..431b91f270c759 --- /dev/null +++ b/src/cmd/compile/internal/types2/interface.go @@ -0,0 +1,180 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import "cmd/compile/internal/syntax" + +// ---------------------------------------------------------------------------- +// API + +// An Interface represents an interface type. +type Interface struct { + check *Checker // for error reporting; nil once type set is computed + methods []*Func // ordered list of explicitly declared methods + embeddeds []Type // ordered list of explicitly embedded elements + embedPos *[]syntax.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space + implicit bool // interface is wrapper for type set literal (non-interface T, ~T, or A|B) + complete bool // indicates that all fields (except for tset) are set up + + tset *_TypeSet // type set described by this interface, computed lazily +} + +// typeSet returns the type set for interface t. +func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) } + +// emptyInterface represents the empty interface +var emptyInterface = Interface{complete: true, tset: &topTypeSet} + +// NewInterfaceType returns a new interface for the given methods and embedded types. +// NewInterfaceType takes ownership of the provided methods and may modify their types +// by setting missing receivers. +func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface { + if len(methods) == 0 && len(embeddeds) == 0 { + return &emptyInterface + } + + // set method receivers if necessary + typ := (*Checker)(nil).newInterface() + for _, m := range methods { + if sig := m.typ.(*Signature); sig.recv == nil { + sig.recv = NewVar(m.pos, m.pkg, "", typ) + } + } + + // sort for API stability + sortMethods(methods) + + typ.methods = methods + typ.embeddeds = embeddeds + typ.complete = true + + return typ +} + +// check may be nil +func (check *Checker) newInterface() *Interface { + typ := &Interface{check: check} + if check != nil { + check.needsCleanup(typ) + } + return typ +} + +// MarkImplicit marks the interface t as implicit, meaning this interface +// corresponds to a constraint literal such as ~T or A|B without explicit +// interface embedding. MarkImplicit should be called before any concurrent use +// of implicit interfaces. +func (t *Interface) MarkImplicit() { + t.implicit = true +} + +// NumExplicitMethods returns the number of explicitly declared methods of interface t. +func (t *Interface) NumExplicitMethods() int { return len(t.methods) } + +// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). +// The methods are ordered by their unique Id. +func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } + +// NumEmbeddeds returns the number of embedded types in interface t. +func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } + +// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). +func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] } + +// NumMethods returns the total number of methods of interface t. +func (t *Interface) NumMethods() int { return t.typeSet().NumMethods() } + +// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). +// The methods are ordered by their unique Id. +func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) } + +// Empty reports whether t is the empty interface. +func (t *Interface) Empty() bool { return t.typeSet().IsAll() } + +// IsComparable reports whether each type in interface t's type set is comparable. +func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) } + +// IsMethodSet reports whether the interface t is fully described by its method set. +func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() } + +// IsImplicit reports whether the interface t is a wrapper for a type set literal. +func (t *Interface) IsImplicit() bool { return t.implicit } + +func (t *Interface) Underlying() Type { return t } +func (t *Interface) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +func (t *Interface) cleanup() { + t.check = nil + t.embedPos = nil +} + +func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) { + addEmbedded := func(pos syntax.Pos, typ Type) { + ityp.embeddeds = append(ityp.embeddeds, typ) + if ityp.embedPos == nil { + ityp.embedPos = new([]syntax.Pos) + } + *ityp.embedPos = append(*ityp.embedPos, pos) + } + + for _, f := range iface.MethodList { + if f.Name == nil { + addEmbedded(posFor(f.Type), parseUnion(check, f.Type)) + continue + } + // f.Name != nil + + // We have a method with name f.Name. + name := f.Name.Value + if name == "_" { + check.error(f.Name, "methods must have a unique non-blank name") + continue // ignore + } + + typ := check.typ(f.Type) + sig, _ := typ.(*Signature) + if sig == nil { + if typ != Typ[Invalid] { + check.errorf(f.Type, invalidAST+"%s is not a method signature", typ) + } + continue // ignore + } + + // use named receiver type if available (for better error messages) + var recvTyp Type = ityp + if def != nil { + recvTyp = def + } + sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp) + + m := NewFunc(f.Name.Pos(), check.pkg, name, sig) + check.recordDef(f.Name, m) + ityp.methods = append(ityp.methods, m) + } + + // All methods and embedded elements for this interface are collected; + // i.e., this interface may be used in a type set computation. + ityp.complete = true + + if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 { + // empty interface + ityp.tset = &topTypeSet + return + } + + // sort for API stability + // (don't sort embeddeds: they must correspond to *embedPos entries) + sortMethods(ityp.methods) + + // Compute type set as soon as possible to report any errors. + // Subsequent uses of type sets will use this computed type + // set and won't need to pass in a *Checker. + check.later(func() { + computeInterfaceTypeSet(check, iface.Pos(), ityp) + }).describef(iface, "compute type set for %s", ityp) +} diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index e716a48038510a..8588687803169e 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -7,7 +7,6 @@ package types2_test import ( - "bytes" "cmd/compile/internal/syntax" "fmt" "internal/testenv" @@ -321,7 +320,7 @@ func TestIssue25627(t *testing.T) { } } - syntax.Walk(f, func(n syntax.Node) bool { + syntax.Crawl(f, func(n syntax.Node) bool { if decl, _ := n.(*syntax.TypeDecl); decl != nil { if tv, ok := info.Types[decl.Type]; ok && decl.Name.Value == "T" { want := strings.Count(src, ";") + 1 @@ -402,8 +401,9 @@ func TestIssue28282(t *testing.T) { // create type interface { error } et := Universe.Lookup("error").Type() it := NewInterfaceType(nil, []Type{et}) - it.Complete() // verify that after completing the interface, the embedded method remains unchanged + // (interfaces are "completed" lazily now, so the completion happens implicitly when + // accessing Method(0)) want := et.Underlying().(*Interface).Method(0) got := it.Method(0) if got != want { @@ -426,7 +426,7 @@ func TestIssue29029(t *testing.T) { // printInfo prints the *Func definitions recorded in info, one *Func per line. printInfo := func(info *Info) string { - var buf bytes.Buffer + var buf strings.Builder for _, obj := range info.Defs { if fn, ok := obj.(*Func); ok { fmt.Fprintln(&buf, fn) @@ -610,3 +610,29 @@ func TestIssue43124(t *testing.T) { t.Errorf("type checking error for c does not disambiguate package template: %q", err) } } + +func TestIssue50646(t *testing.T) { + anyType := Universe.Lookup("any").Type() + comparableType := Universe.Lookup("comparable").Type() + + if !Comparable(anyType) { + t.Errorf("any is not a comparable type") + } + if !Comparable(comparableType) { + t.Errorf("comparable is not a comparable type") + } + + if Implements(anyType, comparableType.Underlying().(*Interface)) { + t.Errorf("any implements comparable") + } + if !Implements(comparableType, anyType.(*Interface)) { + t.Errorf("comparable does not implement any") + } + + if AssignableTo(anyType, comparableType) { + t.Errorf("any assignable to comparable") + } + if !AssignableTo(comparableType, anyType) { + t.Errorf("comparable not assignable to any") + } +} diff --git a/src/cmd/compile/internal/types2/labels.go b/src/cmd/compile/internal/types2/labels.go index d3206988b54b69..6f02e2fc969b08 100644 --- a/src/cmd/compile/internal/types2/labels.go +++ b/src/cmd/compile/internal/types2/labels.go @@ -32,7 +32,8 @@ func (check *Checker) labels(body *syntax.BlockStmt) { } // spec: "It is illegal to define a label that is never used." - for _, obj := range all.elems { + for name, obj := range all.elems { + obj = resolve(name, obj) if lbl := obj.(*Label); !lbl.used { check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name) } diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 78299502e9c00d..b9770ae23efe9c 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -6,18 +6,28 @@ package types2 +import ( + "bytes" + "strings" +) + +// Internal use of LookupFieldOrMethod: If the obj result is a method +// associated with a concrete (non-interface) type, the method's signature +// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing +// the method's type. + // LookupFieldOrMethod looks up a field or method with given package and name // in T and returns the corresponding *Var or *Func, an index sequence, and a // bool indicating if there were any pointer indirections on the path to the // field or method. If addressable is set, T is the type of an addressable -// variable (only matters for method lookups). +// variable (only matters for method lookups). T must not be nil. // // The last index entry is the field or method index in the (possibly embedded) // type where the entry was found, either: // -// 1) the list of declared methods of a named type; or -// 2) the list of all methods (method set) of an interface type; or -// 3) the list of fields of a struct type. +// 1. the list of declared methods of a named type; or +// 2. the list of all methods (method set) of an interface type; or +// 3. the list of fields of a struct type. // // The earlier index entries are the indices of the embedded struct fields // traversed to get to the found entry, starting at depth 0. @@ -25,37 +35,27 @@ package types2 // If no entry is found, a nil object is returned. In this case, the returned // index and indirect values have the following meaning: // -// - If index != nil, the index sequence points to an ambiguous entry -// (the same name appeared more than once at the same embedding level). -// -// - If indirect is set, a method with a pointer receiver type was found -// but there was no pointer on the path from the actual receiver type to -// the method's formal receiver base type, nor was the receiver addressable. +// - If index != nil, the index sequence points to an ambiguous entry +// (the same name appeared more than once at the same embedding level). // +// - If indirect is set, a method with a pointer receiver type was found +// but there was no pointer on the path from the actual receiver type to +// the method's formal receiver base type, nor was the receiver addressable. func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name) -} - -// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method -// associated with a concrete (non-interface) type, the method's signature -// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing -// the method's type. -// TODO(gri) Now that we provide the *Checker, we can probably remove this -// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate. + if T == nil { + panic("LookupFieldOrMethod on nil type") + } -// lookupFieldOrMethod is like the external version but completes interfaces -// as necessary. -func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { - // Methods cannot be associated to a named pointer type + // Methods cannot be associated to a named pointer type. // (spec: "The type denoted by T is called the receiver base type; // it must not be a pointer or interface type and it must be declared // in the same package as the method."). // Thus, if we have a named pointer type, proceed with the underlying // pointer type but discard the result if it is a method since we would // not have found it for T (see also issue 8590). - if t := asNamed(T); t != nil { - if p, _ := t.underlying.(*Pointer); p != nil { - obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name) + if t, _ := T.(*Named); t != nil { + if p, _ := t.Underlying().(*Pointer); p != nil { + obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name, false) if _, ok := obj.(*Func); ok { return nil, nil, false } @@ -63,18 +63,31 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package } } - return check.rawLookupFieldOrMethod(T, addressable, pkg, name) + obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name, false) + + // If we didn't find anything and if we have a type parameter with a core type, + // see if there is a matching field (but not a method, those need to be declared + // explicitly in the constraint). If the constraint is a named pointer type (see + // above), we are ok here because only fields are accepted as results. + const enableTParamFieldLookup = false // see issue #51576 + if enableTParamFieldLookup && obj == nil && isTypeParam(T) { + if t := coreType(T); t != nil { + obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name, false) + if _, ok := obj.(*Var); !ok { + obj, index, indirect = nil, nil, false // accept fields (variables) only + } + } + } + return } -// TODO(gri) The named type consolidation and seen maps below must be -// indexed by unique keys for a given type. Verify that named -// types always have only one representation (even when imported -// indirectly via different packages.) - -// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod. -func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { +// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. +// If foldCase is true, the lookup for methods will include looking for any method +// which case-folds to the same as 'name' (used for giving helpful error messages). +// +// The resulting object may not be fully type-checked. +func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) { // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and NewMethodSet should be kept in sync. if name == "_" { return // blank fields/methods are never found @@ -82,37 +95,35 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack typ, isPtr := deref(T) - // *typ where typ is an interface has no methods. - // Be cautious: typ may be nil (issue 39634, crash #3). - if typ == nil || isPtr && IsInterface(typ) { - return + // *typ where typ is an interface (incl. a type parameter) has no methods. + if isPtr { + if _, ok := under(typ).(*Interface); ok { + return + } } // Start with typ as single entry at shallowest depth. current := []embeddedType{{typ, nil, isPtr, false}} - // Named types that we have seen already, allocated lazily. + // seen tracks named types that we have seen already, allocated lazily. // Used to avoid endless searches in case of recursive types. - // Since only Named types can be used for recursive types, we - // only need to track those. - // (If we ever allow type aliases to construct recursive types, - // we must use type identity rather than pointer equality for - // the map key comparison, as we do in consolidateMultiples.) - var seen map[*Named]bool + // + // We must use a lookup on identity rather than a simple map[*Named]bool as + // instantiated types may be identical but not equal. + var seen instanceLookup // search current depth for len(current) > 0 { var next []embeddedType // embedded types found at current depth // look for (pkg, name) in all types at current depth - var tpar *TypeParam // set if obj receiver is a type parameter for _, e := range current { typ := e.typ // If we have a named type, we may have associated methods. // Look for those first. - if named := asNamed(typ); named != nil { - if seen[named] { + if named, _ := typ.(*Named); named != nil { + if alt := seen.lookup(named); alt != nil { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth // were consolidated before). The type at that depth shadows @@ -120,13 +131,10 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack // this one. continue } - if seen == nil { - seen = make(map[*Named]bool) - } - seen[named] = true + seen.add(named) // look for a matching attached method - if i, m := lookupMethod(named.methods, pkg, name); m != nil { + if i, m := named.lookupMethod(pkg, name, foldCase); m != nil { // potential match // caution: method may not have a proper signature yet index = concat(e.index, i) @@ -137,17 +145,9 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack indirect = e.indirect continue // we can't have a matching field or interface method } - - // continue with underlying type, but only if it's not a type parameter - // TODO(gri) is this what we want to do for type parameters? (spec question) - typ = named.under() - if asTypeParam(typ) != nil { - continue - } } - tpar = nil - switch t := typ.(type) { + switch t := under(typ).(type) { case *Struct: // look for a matching field and collect embedded types for i, f := range t.fields { @@ -180,10 +180,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack } case *Interface: - // look for a matching method - // TODO(gri) t.allMethods is sorted - use binary search - check.completeInterface(nopos, t) - if i, m := lookupMethod(t.allMethods, pkg, name); m != nil { + // look for a matching method (interface may be a type parameter) + if i, m := t.typeSet().LookupMethod(pkg, name, foldCase); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -192,24 +190,6 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack obj = m indirect = e.indirect } - - case *TypeParam: - if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil { - assert(m.typ != nil) - index = concat(e.index, i) - if obj != nil || e.multiples { - return nil, index, false // collision - } - tpar = t - obj = m - indirect = e.indirect - } - if obj == nil { - // At this point we're not (yet) looking into methods - // that any underlying type of the types in the type list - // might have. - // TODO(gri) Do we want to specify the language that way? - } } } @@ -221,15 +201,14 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack // is shorthand for (&x).m()". if f, _ := obj.(*Func); f != nil { // determine if method has a pointer receiver - hasPtrRecv := tpar == nil && ptrRecv(f) - if hasPtrRecv && !indirect && !addressable { + if f.hasPtrRecv() && !indirect && !addressable { return nil, nil, true // pointer/addressable receiver required } } return } - current = check.consolidateMultiples(next) + current = consolidateMultiples(next) } return nil, nil, false // not found @@ -246,7 +225,7 @@ type embeddedType struct { // consolidateMultiples collects multiple list entries with the same type // into a single entry marked as containing multiples. The result is the // consolidated list. -func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { +func consolidateMultiples(list []embeddedType) []embeddedType { if len(list) <= 1 { return list // at most one entry - nothing to do } @@ -254,7 +233,7 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { n := 0 // number of entries w/ unique type prev := make(map[Type]int) // index at which type was previously seen for _, e := range list { - if i, found := check.lookupType(prev, e.typ); found { + if i, found := lookupType(prev, e.typ); found { list[i].multiples = true // ignore this entry } else { @@ -266,14 +245,14 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType { return list[:n] } -func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { +func lookupType(m map[Type]int, typ Type) (int, bool) { // fast path: maybe the types are equal if i, found := m[typ]; found { return i, true } for t, i := range m { - if check.identical(t, typ) { + if Identical(t, typ) { return i, true } } @@ -281,6 +260,27 @@ func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { return 0, false } +type instanceLookup struct { + m map[*Named][]*Named +} + +func (l *instanceLookup) lookup(inst *Named) *Named { + for _, t := range l.m[inst.Origin()] { + if Identical(inst, t) { + return t + } + } + return nil +} + +func (l *instanceLookup) add(inst *Named) { + if l.m == nil { + l.m = make(map[*Named][]*Named) + } + insts := l.m[inst.Origin()] + l.m[inst.Origin()] = append(insts, inst) +} + // MissingMethod returns (nil, false) if V implements T, otherwise it // returns a missing method required by T and whether it is missing or // just has the wrong type. @@ -290,58 +290,39 @@ func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { // is not set), MissingMethod only checks that methods of T which are also // present in V have matching types (e.g., for a type assertion x.(T) where // x is of interface type V). -// func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { - m, typ := (*Checker)(nil).missingMethod(V, T, static) - return m, typ != nil + m, alt := (*Checker)(nil).missingMethod(V, T, static) + // Only report a wrong type if the alternative method has the same name as m. + return m, alt != nil && alt.name == m.name // alt != nil implies m != nil } -// missingMethod is like MissingMethod but accepts a *Checker as -// receiver and an addressable flag. -// The receiver may be nil if missingMethod is invoked through -// an exported API call (such as MissingMethod), i.e., when all -// methods have been type-checked. -// If the type has the correctly named method, but with the wrong -// signature, the existing method is returned as well. -// To improve error messages, also report the wrong signature -// when the method exists on *V instead of V. -func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) { - check.completeInterface(nopos, T) - - // fast path for common case - if T.Empty() { +// missingMethod is like MissingMethod but accepts a *Checker as receiver. +// The receiver may be nil if missingMethod is invoked through an exported +// API call (such as MissingMethod), i.e., when all methods have been type- +// checked. +// +// If a method is missing on T but is found on *T, or if a method is found +// on T when looked up with case-folding, this alternative method is returned +// as the second result. +func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) { + if T.NumMethods() == 0 { return } - if ityp := asInterface(V); ityp != nil { - check.completeInterface(nopos, ityp) - // TODO(gri) allMethods is sorted - can do this more efficiently - for _, m := range T.allMethods { - _, f := lookupMethod(ityp.allMethods, m.pkg, m.name) + // V is an interface + if u, _ := under(V).(*Interface); u != nil { + tset := u.typeSet() + for _, m := range T.typeSet().methods { + _, f := tset.LookupMethod(m.pkg, m.name, false) if f == nil { - // if m is the magic method == we're ok (interfaces are comparable) - if m.name == "==" || !static { + if !static { continue } - return m, f + return m, nil } - // both methods must have the same number of type parameters - ftyp := f.typ.(*Signature) - mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { - return m, f - } - - // If the methods have type parameters we don't care whether they - // are the same or not, as long as they match up. Use unification - // to see if they can be made to match. - // TODO(gri) is this always correct? what about type bounds? - // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) - if !u.unify(ftyp, mtyp) { + if !Identical(f.typ, m.typ) { return m, f } } @@ -349,29 +330,24 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, return } - // A concrete type implements T if it implements all methods of T. - Vd, _ := deref(V) - Vn := asNamed(Vd) - for _, m := range T.allMethods { - // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? - obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name) - - // Check if *V implements this method of T. - if obj == nil { - ptr := NewPointer(V) - obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name) - if obj != nil { - return m, obj.(*Func) + // V is not an interface + for _, m := range T.typeSet().methods { + // TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)? + obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name, false) + + // check if m is on *V, or on V with case-folding + found := obj != nil + if !found { + // TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument? + obj, _, _ = lookupFieldOrMethod(NewPointer(V), false, m.pkg, m.name, false) + if obj == nil { + obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true /* fold case */) } } - // we must have a method (not a field of matching function type) + // we must have a method (not a struct field) f, _ := obj.(*Func) if f == nil { - // if m is the magic method == and V is comparable, we're ok - if m.name == "==" && Comparable(V) { - continue - } return m, nil } @@ -380,46 +356,77 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, check.objDecl(f, nil) } - // both methods must have the same number of type parameters - ftyp := f.typ.(*Signature) - mtyp := m.typ.(*Signature) - if len(ftyp.tparams) != len(mtyp.tparams) { + if !found || !Identical(f.typ, m.typ) { return m, f } + } - // If V is a (instantiated) generic type, its methods are still - // parameterized using the original (declaration) receiver type - // parameters (subst simply copies the existing method list, it - // does not instantiate the methods). - // In order to compare the signatures, substitute the receiver - // type parameters of ftyp with V's instantiation type arguments. - // This lazily instantiates the signature of method f. - if Vn != nil && len(Vn.tparams) > 0 { - // Be careful: The number of type arguments may not match - // the number of receiver parameters. If so, an error was - // reported earlier but the length discrepancy is still - // here. Exit early in this case to prevent an assertion - // failure in makeSubstMap. - // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.rparams) != len(Vn.targs) { - return - } - ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature) + return +} + +// missingMethodReason returns a string giving the detailed reason for a missing method m, +// where m is missing from V, but required by T. It puts the reason in parentheses, +// and may include more have/want info after that. If non-nil, alt is a relevant +// method that matches in some way. It may have the correct name, but wrong type, or +// it may have a pointer receiver, or it may have the correct name except wrong case. +// check may be nil. +func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string { + var mname string + if check != nil && check.conf.CompilerErrorMessages { + mname = m.Name() + " method" + } else { + mname = "method " + m.Name() + } + + if alt != nil { + if m.Name() != alt.Name() { + return check.sprintf("(missing %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - // If the methods have type parameters we don't care whether they - // are the same or not, as long as they match up. Use unification - // to see if they can be made to match. - // TODO(gri) is this always correct? what about type bounds? - // (Alternative is to rename/subst type parameters and compare.) - u := newUnifier(check, true) - u.x.init(ftyp.tparams) - if !u.unify(ftyp, mtyp) { - return m, f + if Identical(m.typ, alt.typ) { + return check.sprintf("(%s has pointer receiver)", mname) } + + return check.sprintf("(wrong type for %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - return + if isInterfacePtr(V) { + return "(" + check.interfacePtrError(V) + ")" + } + + if isInterfacePtr(T) { + return "(" + check.interfacePtrError(T) + ")" + } + + return check.sprintf("(missing %s)", mname) +} + +func isInterfacePtr(T Type) bool { + p, _ := under(T).(*Pointer) + return p != nil && IsInterface(p.base) +} + +// check may be nil. +func (check *Checker) interfacePtrError(T Type) string { + assert(isInterfacePtr(T)) + if p, _ := under(T).(*Pointer); isTypeParam(p.base) { + return check.sprintf("type %s is pointer to type parameter, not type parameter", T) + } + return check.sprintf("type %s is pointer to interface, not interface", T) +} + +// funcString returns a string of the form name + signature for f. +// check may be nil. +func (check *Checker) funcString(f *Func) string { + buf := bytes.NewBufferString(f.name) + var qf Qualifier + if check != nil { + qf = check.qualifier + } + WriteSignature(buf, f.typ.(*Signature), qf) + return buf.String() } // assertableTo reports whether a value of type V can be asserted to have type T. @@ -427,22 +434,42 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // method required by V and whether it is missing or just has the wrong type. // The receiver may be nil if assertableTo is invoked through an exported API call // (such as AssertableTo), i.e., when all methods have been type-checked. -// If the global constant forceStrict is set, assertions that are known to fail -// are not permitted. +// TODO(gri) replace calls to this function with calls to newAssertableTo. func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) { // no static check is required if T is an interface // spec: "If T is an interface type, x.(T) asserts that the // dynamic type of x implements the interface T." - if asInterface(T) != nil && !forceStrict { + if IsInterface(T) { return } + // TODO(gri) fix this for generalized interfaces return check.missingMethod(T, V, false) } +// newAssertableTo reports whether a value of type V can be asserted to have type T. +// It also implements behavior for interfaces that currently are only permitted +// in constraint position (we have not yet defined that behavior in the spec). +func (check *Checker) newAssertableTo(V *Interface, T Type) bool { + // no static check is required if T is an interface + // spec: "If T is an interface type, x.(T) asserts that the + // dynamic type of x implements the interface T." + if IsInterface(T) { + return true + } + return check.implements(T, V, nil) +} + // deref dereferences typ if it is a *Pointer and returns its base and true. // Otherwise it returns (typ, false). func deref(typ Type) (Type, bool) { if p, _ := typ.(*Pointer); p != nil { + // p.base should never be nil, but be conservative + if p.base == nil { + if debug { + panic("pointer with nil base type (possibly due to an invalid cyclic declaration)") + } + return Typ[Invalid], true + } return p.base, true } return typ, false @@ -451,8 +478,8 @@ func deref(typ Type) (Type, bool) { // derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a // (named or unnamed) struct and returns its base. Otherwise it returns typ. func derefStructPtr(typ Type) Type { - if p := asPointer(typ); p != nil { - if asStruct(p.base) != nil { + if p, _ := under(typ).(*Pointer); p != nil { + if _, ok := under(p.base).(*Struct); ok { return p.base } } @@ -480,32 +507,14 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int { } // lookupMethod returns the index of and method with matching package and name, or (-1, nil). -func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { +// If foldCase is true, method names are considered equal if they are equal with case folding. +func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) { if name != "_" { for i, m := range methods { - if m.sameId(pkg, name) { + if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) { return i, m } } } return -1, nil } - -// ptrRecv reports whether the receiver is of the form *T. -func ptrRecv(f *Func) bool { - // If a method's receiver type is set, use that as the source of truth for the receiver. - // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty - // signature. We may reach here before the signature is fully set up: we must explicitly - // check if the receiver is set (we cannot just look for non-nil f.typ). - if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil { - _, isPtr := deref(sig.recv.typ) - return isPtr - } - - // If a method's type is not set it may be a method/function that is: - // 1) client-supplied (via NewFunc with no signature), or - // 2) internally created but not yet type-checked. - // For case 1) we can't do anything; the client must know what they are doing. - // For case 2) we can use the information gathered by the resolver. - return f.hasPtrRecv -} diff --git a/src/cmd/compile/internal/types2/main_test.go b/src/cmd/compile/internal/types2/main_test.go new file mode 100644 index 00000000000000..42d26943c40c29 --- /dev/null +++ b/src/cmd/compile/internal/types2/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2_test + +import ( + "go/build" + "internal/testenv" + "os" + "testing" +) + +func TestMain(m *testing.M) { + build.Default.GOROOT = testenv.GOROOT(nil) + os.Exit(m.Run()) +} diff --git a/src/cmd/compile/internal/types2/map.go b/src/cmd/compile/internal/types2/map.go new file mode 100644 index 00000000000000..0d3464caae728b --- /dev/null +++ b/src/cmd/compile/internal/types2/map.go @@ -0,0 +1,24 @@ +// 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 types2 + +// A Map represents a map type. +type Map struct { + key, elem Type +} + +// NewMap returns a new map for the given key and element types. +func NewMap(key, elem Type) *Map { + return &Map{key: key, elem: elem} +} + +// Key returns the key type of map m. +func (m *Map) Key() Type { return m.key } + +// Elem returns the element type of map m. +func (m *Map) Elem() Type { return m.elem } + +func (t *Map) Underlying() Type { return t } +func (t *Map) String() string { return TypeString(t, nil) } diff --git a/src/cmd/compile/internal/types2/mono.go b/src/cmd/compile/internal/types2/mono.go new file mode 100644 index 00000000000000..7bd79f4282efdc --- /dev/null +++ b/src/cmd/compile/internal/types2/mono.go @@ -0,0 +1,337 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "cmd/compile/internal/syntax" +) + +// This file implements a check to validate that a Go package doesn't +// have unbounded recursive instantiation, which is not compatible +// with compilers using static instantiation (such as +// monomorphization). +// +// It implements a sort of "type flow" analysis by detecting which +// type parameters are instantiated with other type parameters (or +// types derived thereof). A package cannot be statically instantiated +// if the graph has any cycles involving at least one derived type. +// +// Concretely, we construct a directed, weighted graph. Vertices are +// used to represent type parameters as well as some defined +// types. Edges are used to represent how types depend on each other: +// +// * Everywhere a type-parameterized function or type is instantiated, +// we add edges to each type parameter from the vertices (if any) +// representing each type parameter or defined type referenced by +// the type argument. If the type argument is just the referenced +// type itself, then the edge has weight 0, otherwise 1. +// +// * For every defined type declared within a type-parameterized +// function or method, we add an edge of weight 1 to the defined +// type from each ambient type parameter. +// +// For example, given: +// +// func f[A, B any]() { +// type T int +// f[T, map[A]B]() +// } +// +// we construct vertices representing types A, B, and T. Because of +// declaration "type T int", we construct edges T<-A and T<-B with +// weight 1; and because of instantiation "f[T, map[A]B]" we construct +// edges A<-T with weight 0, and B<-A and B<-B with weight 1. +// +// Finally, we look for any positive-weight cycles. Zero-weight cycles +// are allowed because static instantiation will reach a fixed point. + +type monoGraph struct { + vertices []monoVertex + edges []monoEdge + + // canon maps method receiver type parameters to their respective + // receiver type's type parameters. + canon map[*TypeParam]*TypeParam + + // nameIdx maps a defined type or (canonical) type parameter to its + // vertex index. + nameIdx map[*TypeName]int +} + +type monoVertex struct { + weight int // weight of heaviest known path to this vertex + pre int // previous edge (if any) in the above path + len int // length of the above path + + // obj is the defined type or type parameter represented by this + // vertex. + obj *TypeName +} + +type monoEdge struct { + dst, src int + weight int + + pos syntax.Pos + typ Type +} + +func (check *Checker) monomorph() { + // We detect unbounded instantiation cycles using a variant of + // Bellman-Ford's algorithm. Namely, instead of always running |V| + // iterations, we run until we either reach a fixed point or we've + // found a path of length |V|. This allows us to terminate earlier + // when there are no cycles, which should be the common case. + + again := true + for again { + again = false + + for i, edge := range check.mono.edges { + src := &check.mono.vertices[edge.src] + dst := &check.mono.vertices[edge.dst] + + // N.B., we're looking for the greatest weight paths, unlike + // typical Bellman-Ford. + w := src.weight + edge.weight + if w <= dst.weight { + continue + } + + dst.pre = i + dst.len = src.len + 1 + if dst.len == len(check.mono.vertices) { + check.reportInstanceLoop(edge.dst) + return + } + + dst.weight = w + again = true + } + } +} + +func (check *Checker) reportInstanceLoop(v int) { + var stack []int + seen := make([]bool, len(check.mono.vertices)) + + // We have a path that contains a cycle and ends at v, but v may + // only be reachable from the cycle, not on the cycle itself. We + // start by walking backwards along the path until we find a vertex + // that appears twice. + for !seen[v] { + stack = append(stack, v) + seen[v] = true + v = check.mono.edges[check.mono.vertices[v].pre].src + } + + // Trim any vertices we visited before visiting v the first + // time. Since v is the first vertex we found within the cycle, any + // vertices we visited earlier cannot be part of the cycle. + for stack[0] != v { + stack = stack[1:] + } + + // TODO(mdempsky): Pivot stack so we report the cycle from the top? + + var err error_ + obj0 := check.mono.vertices[v].obj + err.errorf(obj0, "instantiation cycle:") + + qf := RelativeTo(check.pkg) + for _, v := range stack { + edge := check.mono.edges[check.mono.vertices[v].pre] + obj := check.mono.vertices[edge.dst].obj + + switch obj.Type().(type) { + default: + panic("unexpected type") + case *Named: + err.errorf(edge.pos, "%s implicitly parameterized by %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented + case *TypeParam: + err.errorf(edge.pos, "%s instantiated as %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented + } + } + check.report(&err) +} + +// recordCanon records that tpar is the canonical type parameter +// corresponding to method type parameter mpar. +func (w *monoGraph) recordCanon(mpar, tpar *TypeParam) { + if w.canon == nil { + w.canon = make(map[*TypeParam]*TypeParam) + } + w.canon[mpar] = tpar +} + +// recordInstance records that the given type parameters were +// instantiated with the corresponding type arguments. +func (w *monoGraph) recordInstance(pkg *Package, pos syntax.Pos, tparams []*TypeParam, targs []Type, xlist []syntax.Expr) { + for i, tpar := range tparams { + pos := pos + if i < len(xlist) { + pos = syntax.StartPos(xlist[i]) + } + w.assign(pkg, pos, tpar, targs[i]) + } +} + +// assign records that tpar was instantiated as targ at pos. +func (w *monoGraph) assign(pkg *Package, pos syntax.Pos, tpar *TypeParam, targ Type) { + // Go generics do not have an analog to C++`s template-templates, + // where a template parameter can itself be an instantiable + // template. So any instantiation cycles must occur within a single + // package. Accordingly, we can ignore instantiations of imported + // type parameters. + // + // TODO(mdempsky): Push this check up into recordInstance? All type + // parameters in a list will appear in the same package. + if tpar.Obj().Pkg() != pkg { + return + } + + // flow adds an edge from vertex src representing that typ flows to tpar. + flow := func(src int, typ Type) { + weight := 1 + if typ == targ { + weight = 0 + } + + w.addEdge(w.typeParamVertex(tpar), src, weight, pos, targ) + } + + // Recursively walk the type argument to find any defined types or + // type parameters. + var do func(typ Type) + do = func(typ Type) { + switch typ := typ.(type) { + default: + panic("unexpected type") + + case *TypeParam: + assert(typ.Obj().Pkg() == pkg) + flow(w.typeParamVertex(typ), typ) + + case *Named: + if src := w.localNamedVertex(pkg, typ.Origin()); src >= 0 { + flow(src, typ) + } + + targs := typ.TypeArgs() + for i := 0; i < targs.Len(); i++ { + do(targs.At(i)) + } + + case *Array: + do(typ.Elem()) + case *Basic: + // ok + case *Chan: + do(typ.Elem()) + case *Map: + do(typ.Key()) + do(typ.Elem()) + case *Pointer: + do(typ.Elem()) + case *Slice: + do(typ.Elem()) + + case *Interface: + for i := 0; i < typ.NumMethods(); i++ { + do(typ.Method(i).Type()) + } + case *Signature: + tuple := func(tup *Tuple) { + for i := 0; i < tup.Len(); i++ { + do(tup.At(i).Type()) + } + } + tuple(typ.Params()) + tuple(typ.Results()) + case *Struct: + for i := 0; i < typ.NumFields(); i++ { + do(typ.Field(i).Type()) + } + } + } + do(targ) +} + +// localNamedVertex returns the index of the vertex representing +// named, or -1 if named doesn't need representation. +func (w *monoGraph) localNamedVertex(pkg *Package, named *Named) int { + obj := named.Obj() + if obj.Pkg() != pkg { + return -1 // imported type + } + + root := pkg.Scope() + if obj.Parent() == root { + return -1 // package scope, no ambient type parameters + } + + if idx, ok := w.nameIdx[obj]; ok { + return idx + } + + idx := -1 + + // Walk the type definition's scope to find any ambient type + // parameters that it's implicitly parameterized by. + for scope := obj.Parent(); scope != root; scope = scope.Parent() { + for _, elem := range scope.elems { + if elem, ok := elem.(*TypeName); ok && !elem.IsAlias() && elem.Pos().Cmp(obj.Pos()) < 0 { + if tpar, ok := elem.Type().(*TypeParam); ok { + if idx < 0 { + idx = len(w.vertices) + w.vertices = append(w.vertices, monoVertex{obj: obj}) + } + + w.addEdge(idx, w.typeParamVertex(tpar), 1, obj.Pos(), tpar) + } + } + } + } + + if w.nameIdx == nil { + w.nameIdx = make(map[*TypeName]int) + } + w.nameIdx[obj] = idx + return idx +} + +// typeParamVertex returns the index of the vertex representing tpar. +func (w *monoGraph) typeParamVertex(tpar *TypeParam) int { + if x, ok := w.canon[tpar]; ok { + tpar = x + } + + obj := tpar.Obj() + + if idx, ok := w.nameIdx[obj]; ok { + return idx + } + + if w.nameIdx == nil { + w.nameIdx = make(map[*TypeName]int) + } + + idx := len(w.vertices) + w.vertices = append(w.vertices, monoVertex{obj: obj}) + w.nameIdx[obj] = idx + return idx +} + +func (w *monoGraph) addEdge(dst, src, weight int, pos syntax.Pos, typ Type) { + // TODO(mdempsky): Deduplicate redundant edges? + w.edges = append(w.edges, monoEdge{ + dst: dst, + src: src, + weight: weight, + + pos: pos, + typ: typ, + }) +} diff --git a/src/cmd/compile/internal/types2/mono_test.go b/src/cmd/compile/internal/types2/mono_test.go new file mode 100644 index 00000000000000..890099207c90fe --- /dev/null +++ b/src/cmd/compile/internal/types2/mono_test.go @@ -0,0 +1,88 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2_test + +import ( + "cmd/compile/internal/syntax" + "cmd/compile/internal/types2" + "errors" + "fmt" + "strings" + "testing" +) + +func checkMono(t *testing.T, body string) error { + src := "package x; import `unsafe`; var _ unsafe.Pointer;\n" + body + file, err := syntax.Parse(syntax.NewFileBase("x.go"), strings.NewReader(src), nil, nil, 0) + if err != nil { + t.Fatal(err) + } + files := []*syntax.File{file} + + var buf strings.Builder + conf := types2.Config{ + Error: func(err error) { fmt.Fprintln(&buf, err) }, + Importer: defaultImporter(), + } + conf.Check("x", files, nil) + if buf.Len() == 0 { + return nil + } + return errors.New(strings.TrimRight(buf.String(), "\n")) +} + +func TestMonoGood(t *testing.T) { + for i, good := range goods { + if err := checkMono(t, good); err != nil { + t.Errorf("%d: unexpected failure: %v", i, err) + } + } +} + +func TestMonoBad(t *testing.T) { + for i, bad := range bads { + if err := checkMono(t, bad); err == nil { + t.Errorf("%d: unexpected success", i) + } else { + t.Log(err) + } + } +} + +var goods = []string{ + "func F[T any](x T) { F(x) }", + "func F[T, U, V any]() { F[U, V, T](); F[V, T, U]() }", + "type Ring[A, B, C any] struct { L *Ring[B, C, A]; R *Ring[C, A, B] }", + "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte }", + "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte; var _ U[int] }", + "type U[T any] [unsafe.Sizeof(F[*T])]byte; func F[T any]() { var _ U[U[int]] }", + "func F[T any]() { type A = int; F[A]() }", +} + +// TODO(mdempsky): Validate specific error messages and positioning. + +var bads = []string{ + "func F[T any](x T) { F(&x) }", + "func F[T any]() { F[*T]() }", + "func F[T any]() { F[[]T]() }", + "func F[T any]() { F[[1]T]() }", + "func F[T any]() { F[chan T]() }", + "func F[T any]() { F[map[*T]int]() }", + "func F[T any]() { F[map[error]T]() }", + "func F[T any]() { F[func(T)]() }", + "func F[T any]() { F[func() T]() }", + "func F[T any]() { F[struct{ t T }]() }", + "func F[T any]() { F[interface{ t() T }]() }", + "type U[_ any] int; func F[T any]() { F[U[T]]() }", + "func F[T any]() { type U int; F[U]() }", + "func F[T any]() { type U int; F[*U]() }", + "type U[T any] int; func (U[T]) m() { var _ U[*T] }", + "type U[T any] int; func (*U[T]) m() { var _ U[*T] }", + "type U[T1 any] [unsafe.Sizeof(F[*T1])]byte; func F[T2 any]() { var _ U[T2] }", + "func F[A, B, C, D, E any]() { F[B, C, D, E, *A]() }", + "type U[_ any] int; const X = unsafe.Sizeof(func() { type A[T any] U[A[*T]] })", + "func F[T any]() { type A = *T; F[A]() }", + "type A[T any] struct { _ A[*T] }", +} diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go new file mode 100644 index 00000000000000..2cf6d3871f96f4 --- /dev/null +++ b/src/cmd/compile/internal/types2/named.go @@ -0,0 +1,656 @@ +// 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 types2 + +import ( + "cmd/compile/internal/syntax" + "sync" + "sync/atomic" +) + +// Type-checking Named types is subtle, because they may be recursively +// defined, and because their full details may be spread across multiple +// declarations (via methods). For this reason they are type-checked lazily, +// to avoid information being accessed before it is complete. +// +// Conceptually, it is helpful to think of named types as having two distinct +// sets of information: +// - "LHS" information, defining their identity: Obj() and TypeArgs() +// - "RHS" information, defining their details: TypeParams(), Underlying(), +// and methods. +// +// In this taxonomy, LHS information is available immediately, but RHS +// information is lazy. Specifically, a named type N may be constructed in any +// of the following ways: +// 1. type-checked from the source +// 2. loaded eagerly from export data +// 3. loaded lazily from export data (when using unified IR) +// 4. instantiated from a generic type +// +// In cases 1, 3, and 4, it is possible that the underlying type or methods of +// N may not be immediately available. +// - During type-checking, we allocate N before type-checking its underlying +// type or methods, so that we may resolve recursive references. +// - When loading from export data, we may load its methods and underlying +// type lazily using a provided load function. +// - After instantiating, we lazily expand the underlying type and methods +// (note that instances may be created while still in the process of +// type-checking the original type declaration). +// +// In cases 3 and 4 this lazy construction may also occur concurrently, due to +// concurrent use of the type checker API (after type checking or importing has +// finished). It is critical that we keep track of state, so that Named types +// are constructed exactly once and so that we do not access their details too +// soon. +// +// We achieve this by tracking state with an atomic state variable, and +// guarding potentially concurrent calculations with a mutex. At any point in +// time this state variable determines which data on N may be accessed. As +// state monotonically progresses, any data available at state M may be +// accessed without acquiring the mutex at state N, provided N >= M. +// +// GLOSSARY: Here are a few terms used in this file to describe Named types: +// - We say that a Named type is "instantiated" if it has been constructed by +// instantiating a generic named type with type arguments. +// - We say that a Named type is "declared" if it corresponds to a type +// declaration in the source. Instantiated named types correspond to a type +// instantiation in the source, not a declaration. But their Origin type is +// a declared type. +// - We say that a Named type is "resolved" if its RHS information has been +// loaded or fully type-checked. For Named types constructed from export +// data, this may involve invoking a loader function to extract information +// from export data. For instantiated named types this involves reading +// information from their origin. +// - We say that a Named type is "expanded" if it is an instantiated type and +// type parameters in its underlying type and methods have been substituted +// with the type arguments from the instantiation. A type may be partially +// expanded if some but not all of these details have been substituted. +// Similarly, we refer to these individual details (underlying type or +// method) as being "expanded". +// - When all information is known for a named type, we say it is "complete". +// +// Some invariants to keep in mind: each declared Named type has a single +// corresponding object, and that object's type is the (possibly generic) Named +// type. Declared Named types are identical if and only if their pointers are +// identical. On the other hand, multiple instantiated Named types may be +// identical even though their pointers are not identical. One has to use +// Identical to compare them. For instantiated named types, their obj is a +// synthetic placeholder that records their position of the corresponding +// instantiation in the source (if they were constructed during type checking). +// +// To prevent infinite expansion of named instances that are created outside of +// type-checking, instances share a Context with other instances created during +// their expansion. Via the pidgeonhole principle, this guarantees that in the +// presence of a cycle of named types, expansion will eventually find an +// existing instance in the Context and short-circuit the expansion. +// +// Once an instance is complete, we can nil out this shared Context to unpin +// memory, though this Context may still be held by other incomplete instances +// in its "lineage". + +// A Named represents a named (defined) type. +type Named struct { + check *Checker // non-nil during type-checking; nil otherwise + obj *TypeName // corresponding declared object for declared types; see above for instantiated types + + // fromRHS holds the type (on RHS of declaration) this *Named type is derived + // from (for cycle reporting). Only used by validType, and therefore does not + // require synchronization. + fromRHS Type + + // information for instantiated types; nil otherwise + inst *instance + + mu sync.Mutex // guards all fields below + state_ uint32 // the current state of this type; must only be accessed atomically + underlying Type // possibly a *Named during setup; never a *Named once set up completely + tparams *TypeParamList // type parameters, or nil + + // methods declared for this type (not the method set of this type) + // Signatures are type-checked lazily. + // For non-instantiated types, this is a fully populated list of methods. For + // instantiated types, methods are individually expanded when they are first + // accessed. + methods []*Func + + // loader may be provided to lazily load type parameters, underlying type, and methods. + loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func) +} + +// instance holds information that is only necessary for instantiated named +// types. +type instance struct { + orig *Named // original, uninstantiated type + targs *TypeList // type arguments + expandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods) + ctxt *Context // local Context; set to nil after full expansion +} + +// namedState represents the possible states that a named type may assume. +type namedState uint32 + +const ( + unresolved namedState = iota // tparams, underlying type and methods might be unavailable + resolved // resolve has run; methods might be incomplete (for instances) + complete // all data is known +) + +// NewNamed returns a new named type for the given type name, underlying type, and associated methods. +// If the given type name obj doesn't have a type yet, its type is set to the returned named type. +// The underlying type must not be a *Named. +func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { + if _, ok := underlying.(*Named); ok { + panic("underlying type must not be *Named") + } + return (*Checker)(nil).newNamed(obj, underlying, methods) +} + +// resolve resolves the type parameters, methods, and underlying type of n. +// This information may be loaded from a provided loader function, or computed +// from an origin type (in the case of instances). +// +// After resolution, the type parameters, methods, and underlying type of n are +// accessible; but if n is an instantiated type, its methods may still be +// unexpanded. +func (n *Named) resolve() *Named { + if n.state() >= resolved { // avoid locking below + return n + } + + // TODO(rfindley): if n.check is non-nil we can avoid locking here, since + // type-checking is not concurrent. Evaluate if this is worth doing. + n.mu.Lock() + defer n.mu.Unlock() + + if n.state() >= resolved { + return n + } + + if n.inst != nil { + assert(n.underlying == nil) // n is an unresolved instance + assert(n.loader == nil) // instances are created by instantiation, in which case n.loader is nil + + orig := n.inst.orig + orig.resolve() + underlying := n.expandUnderlying() + + n.tparams = orig.tparams + n.underlying = underlying + n.fromRHS = orig.fromRHS // for cycle detection + + if len(orig.methods) == 0 { + n.setState(complete) // nothing further to do + n.inst.ctxt = nil + } else { + n.setState(resolved) + } + return n + } + + // TODO(mdempsky): Since we're passing n to the loader anyway + // (necessary because types2 expects the receiver type for methods + // on defined interface types to be the Named rather than the + // underlying Interface), maybe it should just handle calling + // SetTypeParams, SetUnderlying, and AddMethod instead? Those + // methods would need to support reentrant calls though. It would + // also make the API more future-proof towards further extensions. + if n.loader != nil { + assert(n.underlying == nil) + assert(n.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil + + tparams, underlying, methods := n.loader(n) + + n.tparams = bindTParams(tparams) + n.underlying = underlying + n.fromRHS = underlying // for cycle detection + n.methods = methods + n.loader = nil + } + + n.setState(complete) + return n +} + +// state atomically accesses the current state of the receiver. +func (n *Named) state() namedState { + return namedState(atomic.LoadUint32(&n.state_)) +} + +// setState atomically stores the given state for n. +// Must only be called while holding n.mu. +func (n *Named) setState(state namedState) { + atomic.StoreUint32(&n.state_, uint32(state)) +} + +// newNamed is like NewNamed but with a *Checker receiver and additional orig argument. +func (check *Checker) newNamed(obj *TypeName, underlying Type, methods []*Func) *Named { + typ := &Named{check: check, obj: obj, fromRHS: underlying, underlying: underlying, methods: methods} + if obj.typ == nil { + obj.typ = typ + } + // Ensure that typ is always sanity-checked. + if check != nil { + check.needsCleanup(typ) + } + return typ +} + +// newNamedInstance creates a new named instance for the given origin and type +// arguments, recording pos as the position of its synthetic object (for error +// reporting). +// +// If set, expanding is the named type instance currently being expanded, that +// led to the creation of this instance. +func (check *Checker) newNamedInstance(pos syntax.Pos, orig *Named, targs []Type, expanding *Named) *Named { + assert(len(targs) > 0) + + obj := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil) + inst := &instance{orig: orig, targs: newTypeList(targs)} + + // Only pass the expanding context to the new instance if their packages + // match. Since type reference cycles are only possible within a single + // package, this is sufficient for the purposes of short-circuiting cycles. + // Avoiding passing the context in other cases prevents unnecessary coupling + // of types across packages. + if expanding != nil && expanding.Obj().pkg == obj.pkg { + inst.ctxt = expanding.inst.ctxt + } + typ := &Named{check: check, obj: obj, inst: inst} + obj.typ = typ + // Ensure that typ is always sanity-checked. + if check != nil { + check.needsCleanup(typ) + } + return typ +} + +func (t *Named) cleanup() { + assert(t.inst == nil || t.inst.orig.inst == nil) + // Ensure that every defined type created in the course of type-checking has + // either non-*Named underlying type, or is unexpanded. + // + // This guarantees that we don't leak any types whose underlying type is + // *Named, because any unexpanded instances will lazily compute their + // underlying type by substituting in the underlying type of their origin. + // The origin must have either been imported or type-checked and expanded + // here, and in either case its underlying type will be fully expanded. + switch t.underlying.(type) { + case nil: + if t.TypeArgs().Len() == 0 { + panic("nil underlying") + } + case *Named: + t.under() // t.under may add entries to check.cleaners + } + t.check = nil +} + +// Obj returns the type name for the declaration defining the named type t. For +// instantiated types, this is same as the type name of the origin type. +func (t *Named) Obj() *TypeName { + if t.inst == nil { + return t.obj + } + return t.inst.orig.obj +} + +// Origin returns the generic type from which the named type t is +// instantiated. If t is not an instantiated type, the result is t. +func (t *Named) Origin() *Named { + if t.inst == nil { + return t + } + return t.inst.orig +} + +// TypeParams returns the type parameters of the named type t, or nil. +// The result is non-nil for an (originally) generic type even if it is instantiated. +func (t *Named) TypeParams() *TypeParamList { return t.resolve().tparams } + +// SetTypeParams sets the type parameters of the named type t. +// t must not have type arguments. +func (t *Named) SetTypeParams(tparams []*TypeParam) { + assert(t.inst == nil) + t.resolve().tparams = bindTParams(tparams) +} + +// TypeArgs returns the type arguments used to instantiate the named type t. +func (t *Named) TypeArgs() *TypeList { + if t.inst == nil { + return nil + } + return t.inst.targs +} + +// NumMethods returns the number of explicit methods defined for t. +func (t *Named) NumMethods() int { + return len(t.Origin().resolve().methods) +} + +// Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). +// +// For an ordinary or instantiated type t, the receiver base type of this +// method is the named type t. For an uninstantiated generic type t, each +// method receiver is instantiated with its receiver type parameters. +func (t *Named) Method(i int) *Func { + t.resolve() + + if t.state() >= complete { + return t.methods[i] + } + + assert(t.inst != nil) // only instances should have incomplete methods + orig := t.inst.orig + + t.mu.Lock() + defer t.mu.Unlock() + + if len(t.methods) != len(orig.methods) { + assert(len(t.methods) == 0) + t.methods = make([]*Func, len(orig.methods)) + } + + if t.methods[i] == nil { + assert(t.inst.ctxt != nil) // we should still have a context remaining from the resolution phase + t.methods[i] = t.expandMethod(i) + t.inst.expandedMethods++ + + // Check if we've created all methods at this point. If we have, mark the + // type as fully expanded. + if t.inst.expandedMethods == len(orig.methods) { + t.setState(complete) + t.inst.ctxt = nil // no need for a context anymore + } + } + + return t.methods[i] +} + +// expandMethod substitutes type arguments in the i'th method for an +// instantiated receiver. +func (t *Named) expandMethod(i int) *Func { + // t.orig.methods is not lazy. origm is the method instantiated with its + // receiver type parameters (the "origin" method). + origm := t.inst.orig.Method(i) + assert(origm != nil) + + check := t.check + // Ensure that the original method is type-checked. + if check != nil { + check.objDecl(origm, nil) + } + + origSig := origm.typ.(*Signature) + rbase, _ := deref(origSig.Recv().Type()) + + // If rbase is t, then origm is already the instantiated method we're looking + // for. In this case, we return origm to preserve the invariant that + // traversing Method->Receiver Type->Method should get back to the same + // method. + // + // This occurs if t is instantiated with the receiver type parameters, as in + // the use of m in func (r T[_]) m() { r.m() }. + if rbase == t { + return origm + } + + sig := origSig + // We can only substitute if we have a correspondence between type arguments + // and type parameters. This check is necessary in the presence of invalid + // code. + if origSig.RecvTypeParams().Len() == t.inst.targs.Len() { + smap := makeSubstMap(origSig.RecvTypeParams().list(), t.inst.targs.list()) + var ctxt *Context + if check != nil { + ctxt = check.context() + } + sig = check.subst(origm.pos, origSig, smap, t, ctxt).(*Signature) + } + + if sig == origSig { + // No substitution occurred, but we still need to create a new signature to + // hold the instantiated receiver. + copy := *origSig + sig = © + } + + var rtyp Type + if origm.hasPtrRecv() { + rtyp = NewPointer(t) + } else { + rtyp = t + } + + sig.recv = substVar(origSig.recv, rtyp) + return substFunc(origm, sig) +} + +// SetUnderlying sets the underlying type and marks t as complete. +// t must not have type arguments. +func (t *Named) SetUnderlying(underlying Type) { + assert(t.inst == nil) + if underlying == nil { + panic("underlying type must not be nil") + } + if _, ok := underlying.(*Named); ok { + panic("underlying type must not be *Named") + } + t.resolve().underlying = underlying + if t.fromRHS == nil { + t.fromRHS = underlying // for cycle detection + } +} + +// AddMethod adds method m unless it is already in the method list. +// t must not have type arguments. +func (t *Named) AddMethod(m *Func) { + assert(t.inst == nil) + t.resolve() + if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 { + t.methods = append(t.methods, m) + } +} + +func (t *Named) Underlying() Type { return t.resolve().underlying } +func (t *Named) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation +// +// TODO(rfindley): reorganize the loading and expansion methods under this +// heading. + +// under returns the expanded underlying type of n0; possibly by following +// forward chains of named types. If an underlying type is found, resolve +// the chain by setting the underlying type for each defined type in the +// chain before returning it. If no underlying type is found or a cycle +// is detected, the result is Typ[Invalid]. If a cycle is detected and +// n0.check != nil, the cycle is reported. +// +// This is necessary because the underlying type of named may be itself a +// named type that is incomplete: +// +// type ( +// A B +// B *C +// C A +// ) +// +// The type of C is the (named) type of A which is incomplete, +// and which has as its underlying type the named type B. +func (n0 *Named) under() Type { + u := n0.Underlying() + + // If the underlying type of a defined type is not a defined + // (incl. instance) type, then that is the desired underlying + // type. + var n1 *Named + switch u1 := u.(type) { + case nil: + // After expansion via Underlying(), we should never encounter a nil + // underlying. + panic("nil underlying") + default: + // common case + return u + case *Named: + // handled below + n1 = u1 + } + + if n0.check == nil { + panic("Named.check == nil but type is incomplete") + } + + // Invariant: after this point n0 as well as any named types in its + // underlying chain should be set up when this function exits. + check := n0.check + n := n0 + + seen := make(map[*Named]int) // types that need their underlying type resolved + var path []Object // objects encountered, for cycle reporting + +loop: + for { + seen[n] = len(seen) + path = append(path, n.obj) + n = n1 + if i, ok := seen[n]; ok { + // cycle + check.cycleError(path[i:]) + u = Typ[Invalid] + break + } + u = n.Underlying() + switch u1 := u.(type) { + case nil: + u = Typ[Invalid] + break loop + default: + break loop + case *Named: + // Continue collecting *Named types in the chain. + n1 = u1 + } + } + + for n := range seen { + // We should never have to update the underlying type of an imported type; + // those underlying types should have been resolved during the import. + // Also, doing so would lead to a race condition (was issue #31749). + // Do this check always, not just in debug mode (it's cheap). + if n.obj.pkg != check.pkg { + panic("imported type with unresolved underlying type") + } + n.underlying = u + } + + return u +} + +func (n *Named) setUnderlying(typ Type) { + if n != nil { + n.underlying = typ + } +} + +func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) { + n.resolve() + // If n is an instance, we may not have yet instantiated all of its methods. + // Look up the method index in orig, and only instantiate method at the + // matching index (if any). + i, _ := lookupMethod(n.Origin().methods, pkg, name, foldCase) + if i < 0 { + return -1, nil + } + // For instances, m.Method(i) will be different from the orig method. + return i, n.Method(i) +} + +// context returns the type-checker context. +func (check *Checker) context() *Context { + if check.ctxt == nil { + check.ctxt = NewContext() + } + return check.ctxt +} + +// expandUnderlying substitutes type arguments in the underlying type n.orig, +// returning the result. Returns Typ[Invalid] if there was an error. +func (n *Named) expandUnderlying() Type { + check := n.check + if check != nil && check.conf.Trace { + check.trace(n.obj.pos, "-- Named.expandUnderlying %s", n) + check.indent++ + defer func() { + check.indent-- + check.trace(n.obj.pos, "=> %s (tparams = %s, under = %s)", n, n.tparams.list(), n.underlying) + }() + } + + assert(n.inst.orig.underlying != nil) + if n.inst.ctxt == nil { + n.inst.ctxt = NewContext() + } + + orig := n.inst.orig + targs := n.inst.targs + + if _, unexpanded := orig.underlying.(*Named); unexpanded { + // We should only get a Named underlying type here during type checking + // (for example, in recursive type declarations). + assert(check != nil) + } + + if orig.tparams.Len() != targs.Len() { + // Mismatching arg and tparam length may be checked elsewhere. + return Typ[Invalid] + } + + // Ensure that an instance is recorded before substituting, so that we + // resolve n for any recursive references. + h := n.inst.ctxt.instanceHash(orig, targs.list()) + n2 := n.inst.ctxt.update(h, orig, n.TypeArgs().list(), n) + assert(n == n2) + + smap := makeSubstMap(orig.tparams.list(), targs.list()) + var ctxt *Context + if check != nil { + ctxt = check.context() + } + underlying := n.check.subst(n.obj.pos, orig.underlying, smap, n, ctxt) + // If the underlying type of n is an interface, we need to set the receiver of + // its methods accurately -- we set the receiver of interface methods on + // the RHS of a type declaration to the defined type. + if iface, _ := underlying.(*Interface); iface != nil { + if methods, copied := replaceRecvType(iface.methods, orig, n); copied { + // If the underlying type doesn't actually use type parameters, it's + // possible that it wasn't substituted. In this case we need to create + // a new *Interface before modifying receivers. + if iface == orig.underlying { + old := iface + iface = check.newInterface() + iface.embeddeds = old.embeddeds + iface.complete = old.complete + iface.implicit = old.implicit // should be false but be conservative + underlying = iface + } + iface.methods = methods + } + } + + return underlying +} + +// safeUnderlying returns the underlying type of typ without expanding +// instances, to avoid infinite recursion. +// +// TODO(rfindley): eliminate this function or give it a better name. +func safeUnderlying(typ Type) Type { + if t, _ := typ.(*Named); t != nil { + return t.underlying + } + return typ.Underlying() +} diff --git a/src/cmd/compile/internal/types2/named_test.go b/src/cmd/compile/internal/types2/named_test.go new file mode 100644 index 00000000000000..e5e8eddb054ef6 --- /dev/null +++ b/src/cmd/compile/internal/types2/named_test.go @@ -0,0 +1,120 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2_test + +import ( + "testing" + + "cmd/compile/internal/syntax" + . "cmd/compile/internal/types2" +) + +func BenchmarkNamed(b *testing.B) { + const src = ` +package p + +type T struct { + P int +} + +func (T) M(int) {} +func (T) N() (i int) { return } + +type G[P any] struct { + F P +} + +func (G[P]) M(P) {} +func (G[P]) N() (p P) { return } + +type Inst = G[int] + ` + pkg, err := pkgFor("p", src, nil) + if err != nil { + b.Fatal(err) + } + + var ( + T = pkg.Scope().Lookup("T").Type() + G = pkg.Scope().Lookup("G").Type() + SrcInst = pkg.Scope().Lookup("Inst").Type() + UserInst = mustInstantiate(b, G, Typ[Int]) + ) + + tests := []struct { + name string + typ Type + }{ + {"nongeneric", T}, + {"generic", G}, + {"src instance", SrcInst}, + {"user instance", UserInst}, + } + + b.Run("Underlying", func(b *testing.B) { + for _, test := range tests { + b.Run(test.name, func(b *testing.B) { + // Access underlying once, to trigger any lazy calculation. + _ = test.typ.Underlying() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = test.typ.Underlying() + } + }) + } + }) +} + +func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type { + inst, err := Instantiate(nil, orig, targs, true) + if err != nil { + tb.Fatal(err) + } + return inst +} + +// Test that types do not expand infinitely, as in golang/go#52715. +func TestFiniteTypeExpansion(t *testing.T) { + const src = ` +package p + +type Tree[T any] struct { + *Node[T] +} + +func (*Tree[R]) N(r R) R { return r } + +type Node[T any] struct { + *Tree[T] +} + +func (Node[Q]) M(Q) {} + +type Inst = *Tree[int] +` + + f, err := parseSrc("foo.go", src) + if err != nil { + t.Fatal(err) + } + pkg := NewPackage("p", f.PkgName.Value) + if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil { + t.Fatal(err) + } + + firstFieldType := func(n *Named) *Named { + return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named) + } + + Inst := pkg.Scope().Lookup("Inst").Type().(*Pointer).Elem().(*Named) + Node := firstFieldType(Inst) + Tree := firstFieldType(Node) + if !Identical(Inst, Tree) { + t.Fatalf("Not a cycle: got %v, want %v", Tree, Inst) + } + if Inst != Tree { + t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree) + } +} diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 844bc34b6a3d1e..f37356180973ab 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -16,7 +16,6 @@ import ( // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // All objects implement the Object interface. -// type Object interface { Parent() *Scope // scope in which this object is declared; nil for methods and struct fields Pos() syntax.Pos // position of object identifier in declaration @@ -186,6 +185,42 @@ func (obj *object) sameId(pkg *Package, name string) bool { return pkg.path == obj.pkg.path } +// less reports whether object a is ordered before object b. +// +// Objects are ordered nil before non-nil, exported before +// non-exported, then by name, and finally (for non-exported +// functions) by package path. +func (a *object) less(b *object) bool { + if a == b { + return false + } + + // Nil before non-nil. + if a == nil { + return true + } + if b == nil { + return false + } + + // Exported functions before non-exported. + ea := isExported(a.name) + eb := isExported(b.name) + if ea != eb { + return ea + } + + // Order by name and then (for non-exported names) by package. + if a.name != b.name { + return a.name < b.name + } + if !ea { + return a.pkg.path < b.pkg.path + } + + return false +} + // A PkgName represents an imported Go package. // PkgNames don't have a type. type PkgName struct { @@ -237,6 +272,14 @@ func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}} } +// NewTypeNameLazy returns a new defined type like NewTypeName, but it +// lazily calls resolve to finish constructing the Named object. +func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { + obj := NewTypeName(pos, pkg, name, nil) + NewNamed(obj, nil, nil).loader = load + return obj +} + // IsAlias reports whether obj is an alias name for a type. func (obj *TypeName) IsAlias() bool { switch t := obj.typ.(type) { @@ -256,6 +299,8 @@ func (obj *TypeName) IsAlias() bool { return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune case *Named: return obj != t.obj + case *TypeParam: + return obj != t.obj default: return true } @@ -267,6 +312,7 @@ type Var struct { embedded bool // if set, the variable is an embedded struct field, and name is the type name isField bool // var is struct field used bool // set if the variable was used + origin *Var // if non-nil, the Var from which this one was instantiated } // NewVar returns a new variable. @@ -282,7 +328,7 @@ func NewParam(pos syntax.Pos, pkg *Package, name string, typ Type) *Var { // NewField returns a new variable representing a struct field. // For embedded fields, the name is the unqualified type name -/// under which the field is accessible. +// under which the field is accessible. func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true} } @@ -297,6 +343,20 @@ func (obj *Var) Embedded() bool { return obj.embedded } // IsField reports whether the variable is a struct field. func (obj *Var) IsField() bool { return obj.isField } +// Origin returns the canonical Var for its receiver, i.e. the Var object +// recorded in Info.Defs. +// +// For synthetic Vars created during instantiation (such as struct fields or +// function parameters that depend on type arguments), this will be the +// corresponding Var on the generic (uninstantiated) type. For all other Vars +// Origin returns the receiver. +func (obj *Var) Origin() *Var { + if obj.origin != nil { + return obj.origin + } + return obj +} + func (*Var) isDependency() {} // a variable may be a dependency of an initialization expression // A Func represents a declared function, concrete method, or abstract @@ -304,7 +364,8 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv bool // only valid for methods that don't have a type yet + hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read + origin *Func // if non-nil, the Func from which this one was instantiated } // NewFunc returns a new function with the given signature, representing @@ -315,7 +376,7 @@ func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func { if sig != nil { typ = sig } - return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false} + return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false, nil} } // FullName returns the package- or receiver-type-qualified name of @@ -327,36 +388,41 @@ func (obj *Func) FullName() string { } // Scope returns the scope of the function's body block. +// The result is nil for imported or instantiated functions and methods +// (but there is also no mechanism to get to an instantiated function). func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope } -// Less reports whether function a is ordered before function b. -// -// Functions are ordered exported before non-exported, then by name, -// and finally (for non-exported functions) by package path. +// Origin returns the canonical Func for its receiver, i.e. the Func object +// recorded in Info.Defs. // -// TODO(gri) The compiler also sorts by package height before package -// path for non-exported names. -func (a *Func) less(b *Func) bool { - if a == b { - return false - } - - // Exported functions before non-exported. - ea := isExported(a.name) - eb := isExported(b.name) - if ea != eb { - return ea +// For synthetic functions created during instantiation (such as methods on an +// instantiated Named type or interface methods that depend on type arguments), +// this will be the corresponding Func on the generic (uninstantiated) type. +// For all other Funcs Origin returns the receiver. +func (obj *Func) Origin() *Func { + if obj.origin != nil { + return obj.origin } + return obj +} - // Order by name and then (for non-exported names) by package. - if a.name != b.name { - return a.name < b.name - } - if !ea { - return a.pkg.path < b.pkg.path +// hasPtrRecv reports whether the receiver is of the form *T for the given method obj. +func (obj *Func) hasPtrRecv() bool { + // If a method's receiver type is set, use that as the source of truth for the receiver. + // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty + // signature. We may reach here before the signature is fully set up: we must explicitly + // check if the receiver is set (we cannot just look for non-nil obj.typ). + if sig, _ := obj.typ.(*Signature); sig != nil && sig.recv != nil { + _, isPtr := deref(sig.recv.typ) + return isPtr } - return false + // If a method's type is not set it may be a method/function that is: + // 1) client-supplied (via NewFunc with no signature), or + // 2) internally created but not yet type-checked. + // For case 1) we can't do anything; the client must know what they are doing. + // For case 2) we can use the information gathered by the resolver. + return obj.hasPtrRecv_ } func (*Func) isDependency() {} // a function may be a dependency of an initialization expression @@ -407,6 +473,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { case *TypeName: tname = obj buf.WriteString("type") + if isTypeParam(typ) { + buf.WriteString(" parameter") + } case *Var: if obj.isField { @@ -452,19 +521,34 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { } if tname != nil { - // We have a type object: Don't print anything more for - // basic types since there's no more information (names - // are the same; see also comment in TypeName.IsAlias). - if _, ok := typ.(*Basic); ok { + switch t := typ.(type) { + case *Basic: + // Don't print anything more for basic types since there's + // no more information. return + case *Named: + if t.TypeParams().Len() > 0 { + newTypeWriter(buf, qf).tParamList(t.TypeParams().list()) + } } if tname.IsAlias() { buf.WriteString(" =") + } else if t, _ := typ.(*TypeParam); t != nil { + typ = t.bound } else { + // TODO(gri) should this be fromRHS for *Named? typ = under(typ) } } + // Special handling for any: because WriteType will format 'any' as 'any', + // resulting in the object string `type any = any` rather than `type any = + // interface{}`. To avoid this, swap in a different empty interface. + if obj == universeAny { + assert(Identical(typ, &emptyInterface)) + typ = &emptyInterface + } + buf.WriteByte(' ') WriteType(buf, typ, qf) } diff --git a/src/cmd/compile/internal/types2/object_test.go b/src/cmd/compile/internal/types2/object_test.go index 7f63c793325721..8f0303d4b24578 100644 --- a/src/cmd/compile/internal/types2/object_test.go +++ b/src/cmd/compile/internal/types2/object_test.go @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package types2 +package types2_test import ( "cmd/compile/internal/syntax" + "internal/testenv" "strings" "testing" -) -func parseSrc(path, src string) (*syntax.File, error) { - return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), nil, nil, 0) -} + . "cmd/compile/internal/types2" +) func TestIsAlias(t *testing.T) { check := func(obj *TypeName, want bool) { @@ -25,7 +24,7 @@ func TestIsAlias(t *testing.T) { check(Unsafe.Scope().Lookup("Pointer").(*TypeName), false) for _, name := range Universe.Names() { if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil { - check(obj, name == "byte" || name == "rune") + check(obj, name == "any" || name == "byte" || name == "rune") } } @@ -33,6 +32,8 @@ func TestIsAlias(t *testing.T) { pkg := NewPackage("p", "p") t1 := NewTypeName(nopos, pkg, "t1", nil) n1 := NewNamed(t1, new(Struct), nil) + t5 := NewTypeName(nopos, pkg, "t5", nil) + NewTypeParam(t5, nil) for _, test := range []struct { name *TypeName alias bool @@ -40,12 +41,13 @@ func TestIsAlias(t *testing.T) { {NewTypeName(nopos, nil, "t0", nil), false}, // no type yet {NewTypeName(nopos, pkg, "t0", nil), false}, // no type yet {t1, false}, // type name refers to named type and vice versa - {NewTypeName(nopos, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type - {NewTypeName(nopos, pkg, "t3", n1), true}, // type name refers to named type with different type name - {NewTypeName(nopos, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name - {NewTypeName(nopos, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name - {NewTypeName(nopos, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe) - {NewTypeName(nopos, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already + {NewTypeName(nopos, nil, "t2", NewInterfaceType(nil, nil)), true}, // type name refers to unnamed type + {NewTypeName(nopos, pkg, "t3", n1), true}, // type name refers to named type with different type name + {NewTypeName(nopos, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name + {NewTypeName(nopos, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name + {NewTypeName(nopos, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe) + {NewTypeName(nopos, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already + {t5, false}, // type name refers to type parameter and vice versa } { check(test.name, test.alias) } @@ -86,3 +88,80 @@ func TestEmbeddedMethod(t *testing.T) { t.Fatalf("%s (%p) != %s (%p)", orig, orig, embed, embed) } } + +var testObjects = []struct { + src string + obj string + want string +}{ + {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"}, + + {"const c = 1.2", "c", "const p.c untyped float"}, + {"const c float64 = 3.14", "c", "const p.c float64"}, + + {"type t struct{f int}", "t", "type p.t struct{f int}"}, + {"type t func(int)", "t", "type p.t func(int)"}, + {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"}, + {"type t[P any] struct{f P}", "t.P", "type parameter P any"}, + {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"}, + + {"type t = struct{f int}", "t", "type p.t = struct{f int}"}, + {"type t = func(int)", "t", "type p.t = func(int)"}, + + {"var v int", "v", "var p.v int"}, + + {"func f(int) string", "f", "func p.f(int) string"}, + {"func g[P any](x P){}", "g", "func p.g[P any](x P)"}, + {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"}, + {"", "any", "type any = interface{}"}, +} + +func TestObjectString(t *testing.T) { + testenv.MustHaveGoBuild(t) + + for _, test := range testObjects { + src := "package p; " + test.src + pkg, err := makePkg(src) + if err != nil { + t.Errorf("%s: %s", src, err) + continue + } + + names := strings.Split(test.obj, ".") + if len(names) != 1 && len(names) != 2 { + t.Errorf("%s: invalid object path %s", test.src, test.obj) + continue + } + _, obj := pkg.Scope().LookupParent(names[0], nopos) + if obj == nil { + t.Errorf("%s: %s not found", test.src, names[0]) + continue + } + if len(names) == 2 { + if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok { + obj = lookupTypeParamObj(typ.TypeParams(), names[1]) + if obj == nil { + t.Errorf("%s: %s not found", test.src, test.obj) + continue + } + } else { + t.Errorf("%s: %s has no type parameters", test.src, names[0]) + continue + } + } + + if got := obj.String(); got != test.want { + t.Errorf("%s: got %s, want %s", test.src, got, test.want) + } + } +} + +func lookupTypeParamObj(list *TypeParamList, name string) Object { + for i := 0; i < list.Len(); i++ { + tpar := list.At(i) + if tpar.Obj().Name() == name { + return tpar.Obj() + } + } + return nil +} diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go index 455d8b5dd1df22..1c58c2d7af26c3 100644 --- a/src/cmd/compile/internal/types2/operand.go +++ b/src/cmd/compile/internal/types2/operand.go @@ -52,7 +52,6 @@ var operandModeString = [...]string{ // the operand, the operand's type, a value for constants, and an id // for built-in functions. // The zero value of operand is a ready to use invalid operand. -// type operand struct { mode operandMode expr syntax.Expr @@ -63,7 +62,6 @@ type operand struct { // Pos returns the position of the expression corresponding to x. // If x is invalid the position is nopos. -// func (x *operand) Pos() syntax.Pos { // x.expr may not be set if x is invalid if x.expr == nil { @@ -108,7 +106,6 @@ func (x *operand) Pos() syntax.Pos { // // cgofunc ( ) // cgofunc ( of type ) -// func operandString(x *operand, qf Qualifier) string { // special-case nil if x.mode == nilvalue { @@ -116,7 +113,7 @@ func operandString(x *operand, qf Qualifier) string { case nil, Typ[Invalid]: return "nil (with invalid type)" case Typ[UntypedNil]: - return "untyped nil" + return "nil" default: return fmt.Sprintf("nil (of type %s)", TypeString(x.typ, qf)) } @@ -176,16 +173,17 @@ func operandString(x *operand, qf Qualifier) string { if hasType { if x.typ != Typ[Invalid] { var intro string - switch { - case isGeneric(x.typ): - intro = " of generic type " - case asTypeParam(x.typ) != nil: - intro = " of type parameter type " - default: + if isGeneric(x.typ) { + intro = " of parameterized type " + } else { intro = " of type " } buf.WriteString(intro) WriteType(&buf, x.typ, qf) + if tpar, _ := x.typ.(*TypeParam); tpar != nil { + buf.WriteString(" constrained by ") + WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here + } } else { buf.WriteString(" with invalid type") } @@ -249,62 +247,129 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er V := x.typ // x's type is identical to T - if check.identical(V, T) { + if Identical(V, T) { return true, 0 } - Vu := optype(V) - Tu := optype(T) + Vu := under(V) + Tu := under(T) + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) // x is an untyped value representable by a value of type T. if isUntyped(Vu) { - if t, ok := Tu.(*Sum); ok { - return t.is(func(t Type) bool { - // TODO(gri) this could probably be more efficient - ok, _ := x.assignableTo(check, t, reason) - return ok + assert(Vp == nil) + if Tp != nil { + // T is a type parameter: x is assignable to T if it is + // representable by each specific type in the type set of T. + return Tp.is(func(t *term) bool { + if t == nil { + return false + } + // A term may be a tilde term but the underlying + // type of an untyped value doesn't change so we + // don't need to do anything special. + newType, _, _ := check.implicitTypeAndValue(x, t.typ) + return newType != nil }), _IncompatibleAssign } - newType, _, _ := check.implicitTypeAndValue(x, Tu) + newType, _, _ := check.implicitTypeAndValue(x, T) return newType != nil, _IncompatibleAssign } // Vu is typed // x's type V and T have identical underlying types // and at least one of V or T is not a named type - if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) { + // and neither V nor T is a type parameter. + if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) && Vp == nil && Tp == nil { return true, 0 } - // T is an interface type and x implements T - if Ti, ok := Tu.(*Interface); ok { - if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ { - if reason != nil { - if wrongType != nil { - if check.identical(m.typ, wrongType.typ) { - *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name) - } else { - *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ) - } - - } else { - *reason = "missing method " + m.Name() - } - } + // T is an interface type and x implements T and T is not a type parameter. + // Also handle the case where T is a pointer to an interface. + if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) { + if !check.implements(V, T, reason) { return false, _InvalidIfaceAssign } return true, 0 } + // If V is an interface, check if a missing type assertion is the problem. + if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil { + if check.implements(T, V, nil) { + // T implements V, so give hint about type assertion. + if reason != nil { + *reason = "need type assertion" + } + return false, _IncompatibleAssign + } + } + // x is a bidirectional channel value, T is a channel // type, x's type V and T have identical element types, - // and at least one of V or T is not a named type + // and at least one of V or T is not a named type. if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv { - if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) { - return !isNamed(V) || !isNamed(T), _InvalidChanAssign + if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) { + return !hasName(V) || !hasName(T), _InvalidChanAssign } } + // optimization: if we don't have type parameters, we're done + if Vp == nil && Tp == nil { + return false, _IncompatibleAssign + } + + errorf := func(format string, args ...interface{}) { + if check != nil && reason != nil { + msg := check.sprintf(format, args...) + if *reason != "" { + msg += "\n\t" + *reason + } + *reason = msg + } + } + + // x's type V is not a named type and T is a type parameter, and + // x is assignable to each specific type in T's type set. + if !hasName(V) && Tp != nil { + ok := false + code := _IncompatibleAssign + Tp.is(func(T *term) bool { + if T == nil { + return false // no specific types + } + ok, code = x.assignableTo(check, T.typ, reason) + if !ok { + errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp) + return false + } + return true + }) + return ok, code + } + + // x's type V is a type parameter and T is not a named type, + // and values x' of each specific type in V's type set are + // assignable to T. + if Vp != nil && !hasName(T) { + x := *x // don't clobber outer x + ok := false + code := _IncompatibleAssign + Vp.is(func(V *term) bool { + if V == nil { + return false // no specific types + } + x.typ = V.typ + ok, code = x.assignableTo(check, T, reason) + if !ok { + errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T) + return false + } + return true + }) + return ok, code + } + return false, _IncompatibleAssign } diff --git a/src/cmd/compile/internal/types2/package.go b/src/cmd/compile/internal/types2/package.go index 31b1e7178771f5..26f10645d28be6 100644 --- a/src/cmd/compile/internal/types2/package.go +++ b/src/cmd/compile/internal/types2/package.go @@ -13,8 +13,8 @@ type Package struct { path string name string scope *Scope - complete bool imports []*Package + complete bool fake bool // scope lookup errors are silently dropped if package is fake (internal use only) cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go } @@ -38,7 +38,13 @@ func (pkg *Package) SetName(name string) { pkg.name = name } // Scope returns the (complete or incomplete) package scope // holding the objects declared at package level (TypeNames, // Consts, Vars, and Funcs). -func (pkg *Package) Scope() *Scope { return pkg.scope } +// For a nil pkg receiver, Scope returns the Universe scope. +func (pkg *Package) Scope() *Scope { + if pkg != nil { + return pkg.scope + } + return Universe +} // A package is complete if its scope contains (at least) all // exported objects; otherwise it is incomplete. diff --git a/src/cmd/compile/internal/types2/pointer.go b/src/cmd/compile/internal/types2/pointer.go new file mode 100644 index 00000000000000..63055fc6b056a7 --- /dev/null +++ b/src/cmd/compile/internal/types2/pointer.go @@ -0,0 +1,19 @@ +// 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 types2 + +// A Pointer represents a pointer type. +type Pointer struct { + base Type // element type +} + +// NewPointer returns a new pointer type for the given element (base) type. +func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } + +// Elem returns the element type for the given pointer p. +func (p *Pointer) Elem() Type { return p.base } + +func (p *Pointer) Underlying() Type { return p } +func (p *Pointer) String() string { return TypeString(p, nil) } diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index ae186a0b5d1efd..c4d11dcac4b12c 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -6,88 +6,113 @@ package types2 -// isNamed reports whether typ has a name. -// isNamed may be called with types that are not fully set up. -func isNamed(typ Type) bool { - switch typ.(type) { - case *Basic, *Named, *TypeParam, *instance: - return true - } - return false +// The isX predicates below report whether t is an X. +// If t is a type parameter the result is false; i.e., +// these predicates don't look inside a type parameter. + +func isBoolean(t Type) bool { return isBasic(t, IsBoolean) } +func isInteger(t Type) bool { return isBasic(t, IsInteger) } +func isUnsigned(t Type) bool { return isBasic(t, IsUnsigned) } +func isFloat(t Type) bool { return isBasic(t, IsFloat) } +func isComplex(t Type) bool { return isBasic(t, IsComplex) } +func isNumeric(t Type) bool { return isBasic(t, IsNumeric) } +func isString(t Type) bool { return isBasic(t, IsString) } +func isIntegerOrFloat(t Type) bool { return isBasic(t, IsInteger|IsFloat) } +func isConstType(t Type) bool { return isBasic(t, IsConstType) } + +// isBasic reports whether under(t) is a basic type with the specified info. +// If t is a type parameter the result is false; i.e., +// isBasic does not look inside a type parameter. +func isBasic(t Type, info BasicInfo) bool { + u, _ := under(t).(*Basic) + return u != nil && u.info&info != 0 } -// isGeneric reports whether a type is a generic, uninstantiated type (generic -// signatures are not included). -func isGeneric(typ Type) bool { - // A parameterized type is only instantiated if it doesn't have an instantiation already. - named, _ := typ.(*Named) - return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil +// The allX predicates below report whether t is an X. +// If t is a type parameter the result is true if isX is true +// for all specified types of the type parameter's type set. +// allX is an optimized version of isX(coreType(t)) (which +// is the same as underIs(t, isX)). + +func allBoolean(t Type) bool { return allBasic(t, IsBoolean) } +func allInteger(t Type) bool { return allBasic(t, IsInteger) } +func allUnsigned(t Type) bool { return allBasic(t, IsUnsigned) } +func allNumeric(t Type) bool { return allBasic(t, IsNumeric) } +func allString(t Type) bool { return allBasic(t, IsString) } +func allOrdered(t Type) bool { return allBasic(t, IsOrdered) } +func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) } + +// allBasic reports whether under(t) is a basic type with the specified info. +// If t is a type parameter, the result is true if isBasic(t, info) is true +// for all specific types of the type parameter's type set. +// allBasic(t, info) is an optimized version of isBasic(coreType(t), info). +func allBasic(t Type, info BasicInfo) bool { + if tpar, _ := t.(*TypeParam); tpar != nil { + return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) }) + } + return isBasic(t, info) } -func is(typ Type, what BasicInfo) bool { - switch t := optype(typ).(type) { - case *Basic: - return t.info&what != 0 - case *Sum: - return t.is(func(typ Type) bool { return is(typ, what) }) +// hasName reports whether t has a name. This includes +// predeclared types, defined types, and type parameters. +// hasName may be called with types that are not fully set up. +func hasName(t Type) bool { + switch t.(type) { + case *Basic, *Named, *TypeParam: + return true } return false } -func isBoolean(typ Type) bool { return is(typ, IsBoolean) } -func isInteger(typ Type) bool { return is(typ, IsInteger) } -func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) } -func isFloat(typ Type) bool { return is(typ, IsFloat) } -func isComplex(typ Type) bool { return is(typ, IsComplex) } -func isNumeric(typ Type) bool { return is(typ, IsNumeric) } -func isString(typ Type) bool { return is(typ, IsString) } - -// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not -// produce the expected result because a type list that contains both an integer -// and a floating-point type is neither (all) integers, nor (all) floats. -// Use isIntegerOrFloat instead. -func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) } - -// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ). -func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) } - -// isTyped reports whether typ is typed; i.e., not an untyped +// isTyped reports whether t is typed; i.e., not an untyped // constant or boolean. isTyped may be called with types that // are not fully set up. -func isTyped(typ Type) bool { +func isTyped(t Type) bool { // isTyped is called with types that are not fully - // set up. Must not call Basic()! - // A *Named or *instance type is always typed, so - // we only need to check if we have a true *Basic - // type. - t, _ := typ.(*Basic) - return t == nil || t.info&IsUntyped == 0 + // set up. Must not call under()! + b, _ := t.(*Basic) + return b == nil || b.info&IsUntyped == 0 +} + +// isUntyped(t) is the same as !isTyped(t). +func isUntyped(t Type) bool { + return !isTyped(t) } -// isUntyped(typ) is the same as !isTyped(typ). -func isUntyped(typ Type) bool { - return !isTyped(typ) +// IsInterface reports whether t is an interface type. +func IsInterface(t Type) bool { + _, ok := under(t).(*Interface) + return ok } -func isOrdered(typ Type) bool { return is(typ, IsOrdered) } +// isNonTypeParamInterface reports whether t is an interface type but not a type parameter. +func isNonTypeParamInterface(t Type) bool { + return !isTypeParam(t) && IsInterface(t) +} -func isConstType(typ Type) bool { - // Type parameters are never const types. - t, _ := under(typ).(*Basic) - return t != nil && t.info&IsConstType != 0 +// isTypeParam reports whether t is a type parameter. +func isTypeParam(t Type) bool { + _, ok := t.(*TypeParam) + return ok } -// IsInterface reports whether typ is an interface type. -func IsInterface(typ Type) bool { - return asInterface(typ) != nil +// isGeneric reports whether a type is a generic, uninstantiated type +// (generic signatures are not included). +// TODO(gri) should we include signatures or assert that they are not present? +func isGeneric(t Type) bool { + // A parameterized type is only generic if it doesn't have an instantiation already. + named, _ := t.(*Named) + return named != nil && named.obj != nil && named.inst == nil && named.TypeParams().Len() > 0 } // Comparable reports whether values of type T are comparable. func Comparable(T Type) bool { - return comparable(T, nil) + return comparable(T, true, nil, nil) } -func comparable(T Type, seen map[Type]bool) bool { +// If dynamic is set, non-type parameter interfaces are always comparable. +// If reportf != nil, it may be used to report why T is not comparable. +func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool { if seen[T] { return true } @@ -96,70 +121,62 @@ func comparable(T Type, seen map[Type]bool) bool { } seen[T] = true - // If T is a type parameter not constrained by any type - // list (i.e., it's underlying type is the top type), - // T is comparable if it has the == method. Otherwise, - // the underlying type "wins". For instance - // - // interface{ comparable; type []byte } - // - // is not comparable because []byte is not comparable. - if t := asTypeParam(T); t != nil && optype(t) == theTop { - return t.Bound().IsComparable() - } - - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Basic: // assume invalid types to be comparable // to avoid follow-up errors return t.kind != UntypedNil - case *Pointer, *Interface, *Chan: + case *Pointer, *Chan: return true case *Struct: for _, f := range t.fields { - if !comparable(f.typ, seen) { + if !comparable(f.typ, dynamic, seen, nil) { + if reportf != nil { + reportf("struct containing %s cannot be compared", f.typ) + } return false } } return true case *Array: - return comparable(t.elem, seen) - case *Sum: - pred := func(t Type) bool { - return comparable(t, seen) + if !comparable(t.elem, dynamic, seen, nil) { + if reportf != nil { + reportf("%s cannot be compared", t) + } + return false } - return t.is(pred) - case *TypeParam: - return t.Bound().IsComparable() + return true + case *Interface: + if dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen) { + return true + } + if reportf != nil { + if t.typeSet().IsEmpty() { + reportf("empty type set") + } else { + reportf("incomparable types in type set") + } + } + // fallthrough } return false } -// hasNil reports whether a type includes the nil value. -func hasNil(typ Type) bool { - switch t := optype(typ).(type) { +// hasNil reports whether type t includes the nil value. +func hasNil(t Type) bool { + switch u := under(t).(type) { case *Basic: - return t.kind == UnsafePointer - case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: + return u.kind == UnsafePointer + case *Slice, *Pointer, *Signature, *Map, *Chan: return true - case *Sum: - return t.is(hasNil) + case *Interface: + return !isTypeParam(t) || u.typeSet().underIs(func(u Type) bool { + return u != nil && hasNil(u) + }) } return false } -// identical reports whether x and y are identical types. -// Receivers of Signature types are ignored. -func (check *Checker) identical(x, y Type) bool { - return check.identical0(x, y, true, nil) -} - -// identicalIgnoreTags reports whether x and y are identical types if tags are ignored. -// Receivers of Signature types are ignored. -func (check *Checker) identicalIgnoreTags(x, y Type) bool { - return check.identical0(x, y, false, nil) -} - // An ifacePair is a node in a stack of interface type pairs compared for identity. type ifacePair struct { x, y *Interface @@ -171,11 +188,7 @@ func (p *ifacePair) identical(q *ifacePair) bool { } // For changes to this code the corresponding changes should be made to unifier.nify. -func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { - // types must be expanded for comparison - x = expandf(x) - y = expandf(y) - +func identical(x, y Type, cmpTags bool, p *ifacePair) bool { if x == y { return true } @@ -195,13 +208,13 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if y, ok := y.(*Array); ok { // If one or both array lengths are unknown (< 0) due to some error, // assume they are the same to avoid spurious follow-on errors. - return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p) + return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p) } case *Slice: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { - return check.identical0(x.elem, y.elem, cmpTags, p) + return identical(x.elem, y.elem, cmpTags, p) } case *Struct: @@ -216,7 +229,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if f.embedded != g.embedded || cmpTags && x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || - !check.identical0(f.typ, g.typ, cmpTags, p) { + !identical(f.typ, g.typ, cmpTags, p) { return false } } @@ -227,7 +240,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { - return check.identical0(x.base, y.base, cmpTags, p) + return identical(x.base, y.base, cmpTags, p) } case *Tuple: @@ -238,7 +251,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if x != nil { for i, v := range x.vars { w := y.vars[i] - if !check.identical0(v.typ, w.typ, cmpTags, p) { + if !identical(v.typ, w.typ, cmpTags, p) { return false } } @@ -248,57 +261,86 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { } case *Signature: - // Two function types are identical if they have the same number of parameters - // and result values, corresponding parameter and result types are identical, - // and either both functions are variadic or neither is. Parameter and result - // names are not required to match. - // Generic functions must also have matching type parameter lists, but for the - // parameter names. - if y, ok := y.(*Signature); ok { - return x.variadic == y.variadic && - check.identicalTParams(x.tparams, y.tparams, cmpTags, p) && - check.identical0(x.params, y.params, cmpTags, p) && - check.identical0(x.results, y.results, cmpTags, p) + y, _ := y.(*Signature) + if y == nil { + return false } - case *Sum: - // Two sum types are identical if they contain the same types. - // (Sum types always consist of at least two types. Also, the - // the set (list) of types in a sum type consists of unique - // types - each type appears exactly once. Thus, two sum types - // must contain the same number of types to have chance of - // being equal. - if y, ok := y.(*Sum); ok && len(x.types) == len(y.types) { - // Every type in x.types must be in y.types. - // Quadratic algorithm, but probably good enough for now. - // TODO(gri) we need a fast quick type ID/hash for all types. - L: - for _, x := range x.types { - for _, y := range y.types { - if Identical(x, y) { - continue L // x is in y.types - } + // Two function types are identical if they have the same number of + // parameters and result values, corresponding parameter and result types + // are identical, and either both functions are variadic or neither is. + // Parameter and result names are not required to match, and type + // parameters are considered identical modulo renaming. + + if x.TypeParams().Len() != y.TypeParams().Len() { + return false + } + + // In the case of generic signatures, we will substitute in yparams and + // yresults. + yparams := y.params + yresults := y.results + + if x.TypeParams().Len() > 0 { + // We must ignore type parameter names when comparing x and y. The + // easiest way to do this is to substitute x's type parameters for y's. + xtparams := x.TypeParams().list() + ytparams := y.TypeParams().list() + + var targs []Type + for i := range xtparams { + targs = append(targs, x.TypeParams().At(i)) + } + smap := makeSubstMap(ytparams, targs) + + var check *Checker // ok to call subst on a nil *Checker + ctxt := NewContext() // need a non-nil Context for the substitution below + + // Constraints must be pair-wise identical, after substitution. + for i, xtparam := range xtparams { + ybound := check.subst(nopos, ytparams[i].bound, smap, nil, ctxt) + if !identical(xtparam.bound, ybound, cmpTags, p) { + return false } - return false // x is not in y.types } - return true + + yparams = check.subst(nopos, y.params, smap, nil, ctxt).(*Tuple) + yresults = check.subst(nopos, y.results, smap, nil, ctxt).(*Tuple) + } + + return x.variadic == y.variadic && + identical(x.params, yparams, cmpTags, p) && + identical(x.results, yresults, cmpTags, p) + + case *Union: + if y, _ := y.(*Union); y != nil { + // TODO(rfindley): can this be reached during type checking? If so, + // consider passing a type set map. + unionSets := make(map[*Union]*_TypeSet) + xset := computeUnionTypeSet(nil, unionSets, nopos, x) + yset := computeUnionTypeSet(nil, unionSets, nopos, y) + return xset.terms.equal(yset.terms) } case *Interface: + // Two interface types are identical if they describe the same type sets. + // With the existing implementation restriction, this simplifies to: + // // Two interface types are identical if they have the same set of methods with - // the same names and identical function types. Lower-case method names from - // different packages are always different. The order of the methods is irrelevant. + // the same names and identical function types, and if any type restrictions + // are the same. Lower-case method names from different packages are always + // different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - // If identical0 is called (indirectly) via an external API entry point - // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in - // that case, interfaces are expected to be complete and lazy completion - // here is not needed. - if check != nil { - check.completeInterface(nopos, x) - check.completeInterface(nopos, y) + xset := x.typeSet() + yset := y.typeSet() + if xset.comparable != yset.comparable { + return false + } + if !xset.terms.equal(yset.terms) { + return false } - a := x.allMethods - b := y.allMethods + a := xset.methods + b := yset.methods if len(a) == len(b) { // Interface types are the only types where cycles can occur // that are not "terminated" via named types; and such cycles @@ -335,7 +377,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { } for i, f := range a { g := b[i] - if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) { + if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) { return false } } @@ -346,20 +388,41 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Map: // Two map types are identical if they have identical key and value types. if y, ok := y.(*Map); ok { - return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p) + return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p) } case *Chan: // Two channel types are identical if they have identical value types // and the same direction. if y, ok := y.(*Chan); ok { - return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p) + return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p) } case *Named: // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() + + if len(xargs) != len(yargs) { + return false + } + + if len(xargs) > 0 { + // Instances are identical if their original type and type arguments + // are identical. + if !Identical(x.Origin(), y.Origin()) { + return false + } + for i, xa := range xargs { + if !Identical(xa, yargs[i]) { + return false + } + } + return true + } + // TODO(gri) Why is x == y not sufficient? And if it is, // we can just return false here because x == y // is caught in the very beginning of this function. @@ -369,14 +432,6 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *TypeParam: // nothing to do (x and y being equal is caught in the very beginning of this function) - // case *instance: - // unreachable since types are expanded - - case *bottom, *top: - // Either both types are theBottom, or both are theTop in which - // case the initial x == y check will have caught them. Otherwise - // they are not identical. - case nil: // avoid a crash in case of nil type @@ -387,25 +442,28 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { return false } -func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool { - if len(x) != len(y) { +// identicalInstance reports if two type instantiations are identical. +// Instantiations are identical if their origin and type arguments are +// identical. +func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool { + if len(xargs) != len(yargs) { return false } - for i, x := range x { - y := y[i] - if !check.identical0(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) { + + for i, xa := range xargs { + if !Identical(xa, yargs[i]) { return false } } - return true + + return Identical(xorig, yorig) } // Default returns the default "typed" type for an "untyped" type; // it returns the incoming type for all other types. The default type // for untyped nil is untyped nil. -// -func Default(typ Type) Type { - if t, ok := typ.(*Basic); ok { +func Default(t Type) Type { + if t, ok := t.(*Basic); ok { switch t.kind { case UntypedBool: return Typ[Bool] @@ -421,5 +479,5 @@ func Default(typ Type) Type { return Typ[String] } } - return typ + return t } diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go index fa30650bd444f3..b7ba083627da55 100644 --- a/src/cmd/compile/internal/types2/resolver.go +++ b/src/cmd/compile/internal/types2/resolver.go @@ -179,8 +179,9 @@ func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package { // package should be complete or marked fake, but be cautious if imp.complete || imp.fake { check.impMap[key] = imp - // Once we've formatted an error message once, keep the pkgPathMap - // up-to-date on subsequent imports. + // Once we've formatted an error message, keep the pkgPathMap + // up-to-date on subsequent imports. It is used for package + // qualification in error messages. if check.pkgPathMap != nil { check.markImports(imp) } @@ -258,14 +259,14 @@ func (check *Checker) collectObjects() { if s.LocalPkgName != nil { name = s.LocalPkgName.Value if path == "C" { - // match cmd/compile (not prescribed by spec) + // match 1.17 cmd/compile (not prescribed by spec) check.error(s.LocalPkgName, `cannot rename import "C"`) continue } } if name == "init" { - check.error(s.LocalPkgName, "cannot import package as init - init must be a func") + check.error(s, "cannot import package as init - init must be a func") continue } @@ -285,8 +286,8 @@ func (check *Checker) collectObjects() { check.recordImplicit(s, pkgName) } - if path == "C" { - // match cmd/compile (not prescribed by spec) + if imp.fake { + // match 1.17 cmd/compile (not prescribed by spec) pkgName.used = true } @@ -298,22 +299,26 @@ func (check *Checker) collectObjects() { check.dotImportMap = make(map[dotImportKey]*PkgName) } // merge imported scope with file scope - for _, obj := range imp.scope.elems { + for name, obj := range imp.scope.elems { + // Note: Avoid eager resolve(name, obj) here, so we only + // resolve dot-imported objects as needed. + // A package scope may contain non-exported objects, // do not import them! - if obj.Exported() { + if isExported(name) { // declare dot-imported object // (Do not use check.declare because it modifies the object // via Object.setScopePos, which leads to a race condition; // the object may be imported into more than one file scope // concurrently. See issue #32154.) - if alt := fileScope.Insert(obj); alt != nil { + if alt := fileScope.Lookup(name); alt != nil { var err error_ - err.errorf(s.LocalPkgName, "%s redeclared in this block", obj.Name()) + err.errorf(s.LocalPkgName, "%s redeclared in this block", alt.Name()) err.recordAltDecl(alt) check.report(&err) } else { - check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName + fileScope.insert(name, obj) + check.dotImportMap[dotImportKey{fileScope, name}] = pkgName } } } @@ -325,7 +330,7 @@ func (check *Checker) collectObjects() { case *syntax.ConstDecl: // iota is the index of the current constDecl within the group - if first < 0 || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group { + if first < 0 || s.Group == nil || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group { first = index last = nil } @@ -398,52 +403,55 @@ func (check *Checker) collectObjects() { } case *syntax.TypeDecl: + if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) { + check.versionErrorf(s.TParamList[0], "go1.18", "type parameter") + } obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil) check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s}) case *syntax.FuncDecl: - d := s // TODO(gri) get rid of this - name := d.Name.Value - obj := NewFunc(d.Name.Pos(), pkg, name, nil) - if d.Recv == nil { + name := s.Name.Value + obj := NewFunc(s.Name.Pos(), pkg, name, nil) + hasTParamError := false // avoid duplicate type parameter errors + if s.Recv == nil { // regular function if name == "init" || name == "main" && pkg.name == "main" { - if d.TParamList != nil { - check.softErrorf(d, "func %s must have no type parameters", name) + if len(s.TParamList) != 0 { + check.softErrorf(s.TParamList[0], "func %s must have no type parameters", name) + hasTParamError = true } - if t := d.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 { - check.softErrorf(d, "func %s must have no arguments and no return values", name) + if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 { + check.softErrorf(s.Name, "func %s must have no arguments and no return values", name) } } // don't declare init functions in the package scope - they are invisible if name == "init" { obj.parent = pkg.scope - check.recordDef(d.Name, obj) + check.recordDef(s.Name, obj) // init functions must have a body - if d.Body == nil { + if s.Body == nil { // TODO(gri) make this error message consistent with the others above check.softErrorf(obj.pos, "missing function body") } } else { - check.declare(pkg.scope, d.Name, obj, nopos) + check.declare(pkg.scope, s.Name, obj, nopos) } } else { // method // d.Recv != nil - if !acceptMethodTypeParams && len(d.TParamList) != 0 { - //check.error(d.TParamList.Pos(), invalidAST + "method must have no type parameters") - check.error(d, invalidAST+"method must have no type parameters") - } - ptr, recv, _ := check.unpackRecv(d.Recv.Type, false) - // (Methods with invalid receiver cannot be associated to a type, and + ptr, recv, _ := check.unpackRecv(s.Recv.Type, false) + // Methods with invalid receiver cannot be associated to a type, and // methods with blank _ names are never found; no need to collect any - // of them. They will still be type-checked with all the other functions.) + // of them. They will still be type-checked with all the other functions. if recv != nil && name != "_" { methods = append(methods, methodInfo{obj, ptr, recv}) } - check.recordDef(d.Name, obj) + check.recordDef(s.Name, obj) } - info := &declInfo{file: fileScope, fdecl: d} + if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError { + check.versionErrorf(s.TParamList[0], "go1.18", "type parameter") + } + info := &declInfo{file: fileScope, fdecl: s} // Methods are not package-level objects but we still track them in the // object map so that we can handle them like regular functions (if the // receiver is invalid); also we need their fdecl info when associating @@ -459,8 +467,9 @@ func (check *Checker) collectObjects() { // verify that objects in package and file scopes have different names for _, scope := range fileScopes { - for _, obj := range scope.elems { - if alt := pkg.scope.Lookup(obj.Name()); alt != nil { + for name, obj := range scope.elems { + if alt := pkg.scope.Lookup(name); alt != nil { + obj = resolve(name, obj) var err error_ if pkg, ok := obj.(*PkgName); ok { err.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported()) @@ -486,7 +495,7 @@ func (check *Checker) collectObjects() { // Determine the receiver base type and associate m with it. ptr, base := check.resolveBaseTypeName(m.ptr, m.recv) if base != nil { - m.obj.hasPtrRecv = ptr + m.obj.hasPtrRecv_ = ptr check.methods[base] = append(check.methods[base], m.obj) } } @@ -638,27 +647,33 @@ func (check *Checker) packageObjects() { } } - // We process non-alias declarations first, in order to avoid situations where - // the type of an alias declaration is needed before it is available. In general - // this is still not enough, as it is possible to create sufficiently convoluted - // recursive type definitions that will cause a type alias to be needed before it - // is available (see issue #25838 for examples). - // As an aside, the cmd/compiler suffers from the same problem (#25838). + // We process non-alias type declarations first, followed by alias declarations, + // and then everything else. This appears to avoid most situations where the type + // of an alias is needed before it is available. + // There may still be cases where this is not good enough (see also issue #25838). + // In those cases Checker.ident will report an error ("invalid use of type alias"). var aliasList []*TypeName - // phase 1 + var othersList []Object // everything that's not a type + // phase 1: non-alias type declarations for _, obj := range objList { - // If we have a type alias, collect it for the 2nd phase. - if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].tdecl.Alias { - aliasList = append(aliasList, tname) - continue + if tname, _ := obj.(*TypeName); tname != nil { + if check.objMap[tname].tdecl.Alias { + aliasList = append(aliasList, tname) + } else { + check.objDecl(obj, nil) + } + } else { + othersList = append(othersList, obj) } - - check.objDecl(obj, nil) } - // phase 2 + // phase 2: alias type declarations for _, obj := range aliasList { check.objDecl(obj, nil) } + // phase 3: all other declarations + for _, obj := range othersList { + check.objDecl(obj, nil) + } // At this point we may have a non-empty check.methods map; this means that not all // entries were deleted at the end of typeDecl because the respective receiver base @@ -676,7 +691,7 @@ func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // unusedImports checks for unused imports. func (check *Checker) unusedImports() { - // if function bodies are not checked, packages' uses are likely missing - don't check + // If function bodies are not checked, packages' uses are likely missing - don't check. if check.conf.IgnoreFuncBodies { return } diff --git a/src/cmd/compile/internal/types2/resolver_test.go b/src/cmd/compile/internal/types2/resolver_test.go index aee435ff5fb6cd..a02abce081b833 100644 --- a/src/cmd/compile/internal/types2/resolver_test.go +++ b/src/cmd/compile/internal/types2/resolver_test.go @@ -143,7 +143,7 @@ func TestResolveIdents(t *testing.T) { // check that qualified identifiers are resolved for _, f := range files { - syntax.Walk(f, func(n syntax.Node) bool { + syntax.Crawl(f, func(n syntax.Node) bool { if s, ok := n.(*syntax.SelectorExpr); ok { if x, ok := s.X.(*syntax.Name); ok { obj := uses[x] @@ -177,7 +177,7 @@ func TestResolveIdents(t *testing.T) { foundDefs := make(map[*syntax.Name]bool) var both []string for _, f := range files { - syntax.Walk(f, func(n syntax.Node) bool { + syntax.Crawl(f, func(n syntax.Node) bool { if x, ok := n.(*syntax.Name); ok { var objects int if _, found := uses[x]; found { diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go index 204e456a916fb1..7cdea99e0865ac 100644 --- a/src/cmd/compile/internal/types2/return.go +++ b/src/cmd/compile/internal/types2/return.go @@ -62,6 +62,11 @@ func (check *Checker) isTerminating(s syntax.Stmt, label string) bool { return true case *syntax.ForStmt: + if _, ok := s.Init.(*syntax.RangeClause); ok { + // Range clauses guarantee that the loop terminates, + // so the loop is not a terminating statement. See issue 49003. + break + } if s.Cond == nil && !hasBreak(s.Body, label, true) { return true } @@ -94,8 +99,8 @@ func (check *Checker) isTerminatingSwitch(body []*syntax.CaseClause, label strin } // TODO(gri) For nested breakable statements, the current implementation of hasBreak -// will traverse the same subtree repeatedly, once for each label. Replace -// with a single-pass label/break matching phase. +// will traverse the same subtree repeatedly, once for each label. Replace +// with a single-pass label/break matching phase. // hasBreak reports if s is or contains a break statement // referring to the label-ed statement or implicit-ly the diff --git a/src/cmd/compile/internal/types2/sanitize.go b/src/cmd/compile/internal/types2/sanitize.go deleted file mode 100644 index 64a2dedc7d83d9..00000000000000 --- a/src/cmd/compile/internal/types2/sanitize.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package types2 - -// sanitizeInfo walks the types contained in info to ensure that all instances -// are expanded. -// -// This includes some objects that may be shared across concurrent -// type-checking passes (such as those in the universe scope), so we are -// careful here not to write types that are already sanitized. This avoids a -// data race as any shared types should already be sanitized. -func sanitizeInfo(info *Info) { - var s sanitizer = make(map[Type]Type) - - // Note: Some map entries are not references. - // If modified, they must be assigned back. - - for e, tv := range info.Types { - if typ := s.typ(tv.Type); typ != tv.Type { - tv.Type = typ - info.Types[e] = tv - } - } - - for e, inf := range info.Inferred { - changed := false - for i, targ := range inf.Targs { - if typ := s.typ(targ); typ != targ { - inf.Targs[i] = typ - changed = true - } - } - if typ := s.typ(inf.Sig); typ != inf.Sig { - inf.Sig = typ.(*Signature) - changed = true - } - if changed { - info.Inferred[e] = inf - } - } - - for _, obj := range info.Defs { - if obj != nil { - if typ := s.typ(obj.Type()); typ != obj.Type() { - obj.setType(typ) - } - } - } - - for _, obj := range info.Uses { - if obj != nil { - if typ := s.typ(obj.Type()); typ != obj.Type() { - obj.setType(typ) - } - } - } - - // TODO(gri) sanitize as needed - // - info.Implicits - // - info.Selections - // - info.Scopes - // - info.InitOrder -} - -type sanitizer map[Type]Type - -func (s sanitizer) typ(typ Type) Type { - if typ == nil { - return nil - } - - if t, found := s[typ]; found { - return t - } - s[typ] = typ - - switch t := typ.(type) { - case *Basic, *bottom, *top: - // nothing to do - - case *Array: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Slice: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Struct: - s.varList(t.fields) - - case *Pointer: - if base := s.typ(t.base); base != t.base { - t.base = base - } - - case *Tuple: - s.tuple(t) - - case *Signature: - s.var_(t.recv) - s.tuple(t.params) - s.tuple(t.results) - - case *Sum: - s.typeList(t.types) - - case *Interface: - s.funcList(t.methods) - if types := s.typ(t.types); types != t.types { - t.types = types - } - s.typeList(t.embeddeds) - s.funcList(t.allMethods) - if allTypes := s.typ(t.allTypes); allTypes != t.allTypes { - t.allTypes = allTypes - } - - case *Map: - if key := s.typ(t.key); key != t.key { - t.key = key - } - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Chan: - if elem := s.typ(t.elem); elem != t.elem { - t.elem = elem - } - - case *Named: - if orig := s.typ(t.fromRHS); orig != t.fromRHS { - t.fromRHS = orig - } - if under := s.typ(t.underlying); under != t.underlying { - t.underlying = under - } - s.typeList(t.targs) - s.funcList(t.methods) - - case *TypeParam: - if bound := s.typ(t.bound); bound != t.bound { - t.bound = bound - } - - case *instance: - typ = t.expand() - s[t] = typ - - default: - panic("unimplemented") - } - - return typ -} - -func (s sanitizer) var_(v *Var) { - if v != nil { - if typ := s.typ(v.typ); typ != v.typ { - v.typ = typ - } - } -} - -func (s sanitizer) varList(list []*Var) { - for _, v := range list { - s.var_(v) - } -} - -func (s sanitizer) tuple(t *Tuple) { - if t != nil { - s.varList(t.vars) - } -} - -func (s sanitizer) func_(f *Func) { - if f != nil { - if typ := s.typ(f.typ); typ != f.typ { - f.typ = typ - } - } -} - -func (s sanitizer) funcList(list []*Func) { - for _, f := range list { - s.func_(f) - } -} - -func (s sanitizer) typeList(list []Type) { - for i, t := range list { - if typ := s.typ(t); typ != t { - list[i] = typ - } - } -} diff --git a/src/cmd/compile/internal/types2/scope.go b/src/cmd/compile/internal/types2/scope.go index ade0a79b31d11d..a679a3d9545512 100644 --- a/src/cmd/compile/internal/types2/scope.go +++ b/src/cmd/compile/internal/types2/scope.go @@ -7,12 +7,12 @@ package types2 import ( - "bytes" "cmd/compile/internal/syntax" "fmt" "io" "sort" "strings" + "sync" ) // A Scope maintains a set of objects and links to its containing @@ -22,6 +22,7 @@ import ( type Scope struct { parent *Scope children []*Scope + number int // parent.children[number-1] is this scope; 0 if there is no parent elems map[string]Object // lazily allocated pos, end syntax.Pos // scope extent; may be invalid comment string // for debugging only @@ -31,10 +32,11 @@ type Scope struct { // NewScope returns a new, empty scope contained in the given parent // scope, if any. The comment is for debugging only. func NewScope(parent *Scope, pos, end syntax.Pos, comment string) *Scope { - s := &Scope{parent, nil, nil, pos, end, comment, false} + s := &Scope{parent, nil, 0, nil, pos, end, comment, false} // don't add children to Universe scope! if parent != nil && parent != Universe { parent.children = append(parent.children, s) + s.number = len(parent.children) } return s } @@ -66,7 +68,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] } // Lookup returns the object in scope s with the given name if such an // object exists; otherwise the result is nil. func (s *Scope) Lookup(name string) Object { - return s.elems[name] + return resolve(name, s.elems[name]) } // LookupParent follows the parent chain of scopes starting with s until @@ -81,7 +83,7 @@ func (s *Scope) Lookup(name string) Object { // whose scope is the scope of the package that exported them. func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) { for ; s != nil; s = s.parent { - if obj := s.elems[name]; obj != nil && (!pos.IsKnown() || obj.scopePos().Cmp(pos) <= 0) { + if obj := s.Lookup(name); obj != nil && (!pos.IsKnown() || obj.scopePos().Cmp(pos) <= 0) { return s, obj } } @@ -95,19 +97,38 @@ func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) { // if not already set, and returns nil. func (s *Scope) Insert(obj Object) Object { name := obj.Name() - if alt := s.elems[name]; alt != nil { + if alt := s.Lookup(name); alt != nil { return alt } - if s.elems == nil { - s.elems = make(map[string]Object) - } - s.elems[name] = obj + s.insert(name, obj) if obj.Parent() == nil { obj.setParent(s) } return nil } +// InsertLazy is like Insert, but allows deferring construction of the +// inserted object until it's accessed with Lookup. The Object +// returned by resolve must have the same name as given to InsertLazy. +// If s already contains an alternative object with the same name, +// InsertLazy leaves s unchanged and returns false. Otherwise it +// records the binding and returns true. The object's parent scope +// will be set to s after resolve is called. +func (s *Scope) InsertLazy(name string, resolve func() Object) bool { + if s.elems[name] != nil { + return false + } + s.insert(name, &lazyObject{parent: s, resolve: resolve}) + return true +} + +func (s *Scope) insert(name string, obj Object) { + if s.elems == nil { + s.elems = make(map[string]Object) + } + s.elems[name] = obj +} + // Squash merges s with its parent scope p by adding all // objects of s to p, adding all children of s to the // children of p, and removing s from p's children. @@ -117,7 +138,8 @@ func (s *Scope) Insert(obj Object) Object { func (s *Scope) Squash(err func(obj, alt Object)) { p := s.parent assert(p != nil) - for _, obj := range s.elems { + for name, obj := range s.elems { + obj = resolve(name, obj) obj.setParent(nil) if alt := p.Insert(obj); alt != nil { err(obj, alt) @@ -196,7 +218,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { indn1 := indn + ind for _, name := range s.Names() { - fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name]) + fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name)) } if recurse { @@ -210,7 +232,61 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) { // String returns a string representation of the scope, for debugging. func (s *Scope) String() string { - var buf bytes.Buffer + var buf strings.Builder s.WriteTo(&buf, 0, false) return buf.String() } + +// A lazyObject represents an imported Object that has not been fully +// resolved yet by its importer. +type lazyObject struct { + parent *Scope + resolve func() Object + obj Object + once sync.Once +} + +// resolve returns the Object represented by obj, resolving lazy +// objects as appropriate. +func resolve(name string, obj Object) Object { + if lazy, ok := obj.(*lazyObject); ok { + lazy.once.Do(func() { + obj := lazy.resolve() + + if _, ok := obj.(*lazyObject); ok { + panic("recursive lazy object") + } + if obj.Name() != name { + panic("lazy object has unexpected name") + } + + if obj.Parent() == nil { + obj.setParent(lazy.parent) + } + lazy.obj = obj + }) + + obj = lazy.obj + } + return obj +} + +// stub implementations so *lazyObject implements Object and we can +// store them directly into Scope.elems. +func (*lazyObject) Parent() *Scope { panic("unreachable") } +func (*lazyObject) Pos() syntax.Pos { panic("unreachable") } +func (*lazyObject) Pkg() *Package { panic("unreachable") } +func (*lazyObject) Name() string { panic("unreachable") } +func (*lazyObject) Type() Type { panic("unreachable") } +func (*lazyObject) Exported() bool { panic("unreachable") } +func (*lazyObject) Id() string { panic("unreachable") } +func (*lazyObject) String() string { panic("unreachable") } +func (*lazyObject) order() uint32 { panic("unreachable") } +func (*lazyObject) color() color { panic("unreachable") } +func (*lazyObject) setType(Type) { panic("unreachable") } +func (*lazyObject) setOrder(uint32) { panic("unreachable") } +func (*lazyObject) setColor(color color) { panic("unreachable") } +func (*lazyObject) setParent(*Scope) { panic("unreachable") } +func (*lazyObject) sameId(pkg *Package, name string) bool { panic("unreachable") } +func (*lazyObject) scopePos() syntax.Pos { panic("unreachable") } +func (*lazyObject) setScopePos(pos syntax.Pos) { panic("unreachable") } diff --git a/src/cmd/compile/internal/types2/selection.go b/src/cmd/compile/internal/types2/selection.go index 8128aeee2e5113..c820a29fad2c7d 100644 --- a/src/cmd/compile/internal/types2/selection.go +++ b/src/cmd/compile/internal/types2/selection.go @@ -36,7 +36,6 @@ const ( // p.x FieldVal T x int {0} true // p.m MethodVal *T m func() {1, 0} true // T.m MethodExpr T m func(T) {1, 0} false -// type Selection struct { kind SelectionKind recv Type // type of x @@ -93,9 +92,9 @@ func (s *Selection) Type() Type { // The last index entry is the field or method index of the type declaring f; // either: // -// 1) the list of declared methods of a named type; or -// 2) the list of methods of an interface type; or -// 3) the list of fields of a struct type. +// 1. the list of declared methods of a named type; or +// 2. the list of methods of an interface type; or +// 3. the list of fields of a struct type. // // The earlier index entries are the indices of the embedded fields implicitly // traversed to get from (the type of) x to f, starting at embedding depth 0. @@ -112,10 +111,10 @@ func (s *Selection) String() string { return SelectionString(s, nil) } // package-level objects, and may be nil. // // Examples: +// // "field (T) f int" // "method (T) f(X) Y" // "method expr (T) f(X) Y" -// func SelectionString(s *Selection, qf Qualifier) string { var k string switch s.kind { diff --git a/src/cmd/compile/internal/types2/self_test.go b/src/cmd/compile/internal/types2/self_test.go index 4722fec9889f0f..9a01ccdf7a28bb 100644 --- a/src/cmd/compile/internal/types2/self_test.go +++ b/src/cmd/compile/internal/types2/self_test.go @@ -24,12 +24,7 @@ func TestSelf(t *testing.T) { conf := Config{Importer: defaultImporter()} _, err = conf.Check("cmd/compile/internal/types2", files, nil) if err != nil { - // Importing go/constant doesn't work in the - // build dashboard environment. Don't report an error - // for now so that the build remains green. - // TODO(gri) fix this - t.Log(err) // replace w/ t.Fatal eventually - return + t.Fatal(err) } } @@ -38,6 +33,7 @@ func BenchmarkCheck(b *testing.B) { filepath.Join("src", "net", "http"), filepath.Join("src", "go", "parser"), filepath.Join("src", "go", "constant"), + filepath.Join("src", "runtime"), filepath.Join("src", "go", "internal", "gcimporter"), } { b.Run(path.Base(p), func(b *testing.B) { diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go new file mode 100644 index 00000000000000..d6a8e70902f572 --- /dev/null +++ b/src/cmd/compile/internal/types2/signature.go @@ -0,0 +1,316 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import "cmd/compile/internal/syntax" + +// ---------------------------------------------------------------------------- +// API + +// A Signature represents a (non-builtin) function or method type. +// The receiver is ignored when comparing signatures for identity. +type Signature struct { + // We need to keep the scope in Signature (rather than passing it around + // and store it in the Func Object) because when type-checking a function + // literal we call the general type checker which returns a general Type. + // We then unpack the *Signature and use the scope for the literal body. + rparams *TypeParamList // receiver type parameters from left to right, or nil + tparams *TypeParamList // type parameters from left to right, or nil + scope *Scope // function scope for package-local and non-instantiated signatures; nil otherwise + recv *Var // nil if not a method + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) +} + +// NewSignatureType creates a new function type for the given receiver, +// receiver type parameters, type parameters, parameters, and results. If +// variadic is set, params must hold at least one parameter and the last +// parameter must be of unnamed slice type. If recv is non-nil, typeParams must +// be empty. If recvTypeParams is non-empty, recv must be non-nil. +func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature { + if variadic { + n := params.Len() + if n == 0 { + panic("variadic function must have at least one parameter") + } + if _, ok := params.At(n - 1).typ.(*Slice); !ok { + panic("variadic parameter must be of unnamed slice type") + } + } + sig := &Signature{recv: recv, params: params, results: results, variadic: variadic} + if len(recvTypeParams) != 0 { + if recv == nil { + panic("function with receiver type parameters must have a receiver") + } + sig.rparams = bindTParams(recvTypeParams) + } + if len(typeParams) != 0 { + if recv != nil { + panic("function with type parameters cannot have a receiver") + } + sig.tparams = bindTParams(typeParams) + } + return sig +} + +// Recv returns the receiver of signature s (if a method), or nil if a +// function. It is ignored when comparing signatures for identity. +// +// For an abstract method, Recv returns the enclosing interface either +// as a *Named or an *Interface. Due to embedding, an interface may +// contain methods whose receiver type is a different interface. +func (s *Signature) Recv() *Var { return s.recv } + +// TypeParams returns the type parameters of signature s, or nil. +func (s *Signature) TypeParams() *TypeParamList { return s.tparams } + +// SetTypeParams sets the type parameters of signature s. +func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } + +// RecvTypeParams returns the receiver type parameters of signature s, or nil. +func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams } + +// Params returns the parameters of signature s, or nil. +func (s *Signature) Params() *Tuple { return s.params } + +// Results returns the results of signature s, or nil. +func (s *Signature) Results() *Tuple { return s.results } + +// Variadic reports whether the signature s is variadic. +func (s *Signature) Variadic() bool { return s.variadic } + +func (s *Signature) Underlying() Type { return s } +func (s *Signature) String() string { return TypeString(s, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +// funcType type-checks a function or method type. +func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []*syntax.Field, ftyp *syntax.FuncType) { + check.openScope(ftyp, "function") + check.scope.isFunc = true + check.recordScope(ftyp, check.scope) + sig.scope = check.scope + defer check.closeScope() + + if recvPar != nil { + // collect generic receiver type parameters, if any + // - a receiver type parameter is like any other type parameter, except that it is declared implicitly + // - the receiver specification acts as local declaration for its type parameters, which may be blank + _, rname, rparams := check.unpackRecv(recvPar.Type, true) + if len(rparams) > 0 { + tparams := make([]*TypeParam, len(rparams)) + for i, rparam := range rparams { + tparams[i] = check.declareTypeParam(rparam) + } + sig.rparams = bindTParams(tparams) + // Blank identifiers don't get declared, so naive type-checking of the + // receiver type expression would fail in Checker.collectParams below, + // when Checker.ident cannot resolve the _ to a type. + // + // Checker.recvTParamMap maps these blank identifiers to their type parameter + // types, so that they may be resolved in Checker.ident when they fail + // lookup in the scope. + for i, p := range rparams { + if p.Value == "_" { + if check.recvTParamMap == nil { + check.recvTParamMap = make(map[*syntax.Name]*TypeParam) + } + check.recvTParamMap[p] = tparams[i] + } + } + // determine receiver type to get its type parameters + // and the respective type parameter bounds + var recvTParams []*TypeParam + if rname != nil { + // recv should be a Named type (otherwise an error is reported elsewhere) + // Also: Don't report an error via genericType since it will be reported + // again when we type-check the signature. + // TODO(gri) maybe the receiver should be marked as invalid instead? + if recv, _ := check.genericType(rname, nil).(*Named); recv != nil { + recvTParams = recv.TypeParams().list() + } + } + // provide type parameter bounds + if len(tparams) == len(recvTParams) { + smap := makeRenameMap(recvTParams, tparams) + for i, tpar := range tparams { + recvTPar := recvTParams[i] + check.mono.recordCanon(tpar, recvTPar) + // recvTPar.bound is (possibly) parameterized in the context of the + // receiver type declaration. Substitute parameters for the current + // context. + tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil, check.context()) + } + } else if len(tparams) < len(recvTParams) { + // Reporting an error here is a stop-gap measure to avoid crashes in the + // compiler when a type parameter/argument cannot be inferred later. It + // may lead to follow-on errors (see issues #51339, #51343). + // TODO(gri) find a better solution + got := measure(len(tparams), "type parameter") + check.errorf(recvPar, "got %s, but receiver base type declares %d", got, len(recvTParams)) + } + } + } + + if tparams != nil { + // The parser will complain about invalid type parameters for methods. + check.collectTypeParams(&sig.tparams, tparams) + } + + // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their + // declarations and then squash that scope into the parent scope (and report any redeclarations at + // that time). + scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)") + var recvList []*Var // TODO(gri) remove the need for making a list here + if recvPar != nil { + recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any + } + params, variadic := check.collectParams(scope, ftyp.ParamList, true) + results, _ := check.collectParams(scope, ftyp.ResultList, false) + scope.Squash(func(obj, alt Object) { + var err error_ + err.errorf(obj, "%s redeclared in this block", obj.Name()) + err.recordAltDecl(alt) + check.report(&err) + }) + + if recvPar != nil { + // recv parameter list present (may be empty) + // spec: "The receiver is specified via an extra parameter section preceding the + // method name. That parameter section must declare a single parameter, the receiver." + var recv *Var + switch len(recvList) { + case 0: + // error reported by resolver + recv = NewParam(nopos, nil, "", Typ[Invalid]) // ignore recv below + default: + // more than one receiver + check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver") + fallthrough // continue with first receiver + case 1: + recv = recvList[0] + } + sig.recv = recv + + // Delay validation of receiver type as it may cause premature expansion + // of types the receiver type is dependent on (see issues #51232, #51233). + check.later(func() { + // spec: "The receiver type must be of the form T or *T where T is a type name." + rtyp, _ := deref(recv.typ) + if rtyp == Typ[Invalid] { + return // error was reported before + } + // spec: "The type denoted by T is called the receiver base type; it must not + // be a pointer or interface type and it must be declared in the same package + // as the method." + switch T := rtyp.(type) { + case *Named: + // The receiver type may be an instantiated type referred to + // by an alias (which cannot have receiver parameters for now). + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { + check.errorf(recv, "cannot define new methods on instantiated type %s", rtyp) + break + } + if T.obj.pkg != check.pkg { + check.errorf(recv, "cannot define new methods on non-local type %s", rtyp) + break + } + var cause string + switch u := T.under().(type) { + case *Basic: + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + cause = "unsafe.Pointer" + } + case *Pointer, *Interface: + cause = "pointer or interface type" + case *TypeParam: + // The underlying type of a receiver base type cannot be a + // type parameter: "type T[P any] P" is not a valid declaration. + unreachable() + } + if cause != "" { + check.errorf(recv, "invalid receiver type %s (%s)", rtyp, cause) + } + case *Basic: + check.errorf(recv, "cannot define new methods on non-local type %s", rtyp) + default: + check.errorf(recv, "invalid receiver type %s", recv.typ) + } + }).describef(recv, "validate receiver %s", recv) + } + + sig.params = NewTuple(params...) + sig.results = NewTuple(results...) + sig.variadic = variadic +} + +// collectParams declares the parameters of list in scope and returns the corresponding +// variable list. +func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) { + if list == nil { + return + } + + var named, anonymous bool + + var typ Type + var prev syntax.Expr + for i, field := range list { + ftype := field.Type + // type-check type of grouped fields only once + if ftype != prev { + prev = ftype + if t, _ := ftype.(*syntax.DotsType); t != nil { + ftype = t.Elem + if variadicOk && i == len(list)-1 { + variadic = true + } else { + check.softErrorf(t, "can only use ... with final parameter in list") + // ignore ... and continue + } + } + typ = check.varType(ftype) + } + // The parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag. + if field.Name != nil { + // named parameter + name := field.Name.Value + if name == "" { + check.error(field.Name, invalidAST+"anonymous parameter") + // ok to continue + } + par := NewParam(field.Name.Pos(), check.pkg, name, typ) + check.declare(scope, field.Name, par, scope.pos) + params = append(params, par) + named = true + } else { + // anonymous parameter + par := NewParam(field.Pos(), check.pkg, "", typ) + check.recordImplicit(field, par) + params = append(params, par) + anonymous = true + } + } + + if named && anonymous { + check.error(list[0], invalidAST+"list contains both named and anonymous parameters") + // ok to continue + } + + // For a variadic function, change the last parameter's type from T to []T. + // Since we type-checked T rather than ...T, we also need to retro-actively + // record the type for ...T. + if variadic { + last := params[len(params)-1] + last.typ = &Slice{elem: last.typ} + check.recordTypeAndValue(list[len(list)-1].Type, typexpr, last.typ, nil) + } + + return +} diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 236feb0404ebbd..af82b3fa7add82 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -26,30 +26,29 @@ func TestSizeof(t *testing.T) { {Struct{}, 24, 48}, {Pointer{}, 8, 16}, {Tuple{}, 12, 24}, - {Signature{}, 44, 88}, - {Sum{}, 12, 24}, - {Interface{}, 60, 120}, + {Signature{}, 28, 56}, + {Union{}, 12, 24}, + {Interface{}, 40, 80}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 68, 136}, + {Named{}, 60, 112}, {TypeParam{}, 28, 48}, - {instance{}, 52, 96}, - {bottom{}, 0, 0}, - {top{}, 0, 0}, + {term{}, 12, 24}, // Objects {PkgName{}, 64, 104}, {Const{}, 64, 104}, {TypeName{}, 56, 88}, - {Var{}, 60, 96}, - {Func{}, 60, 96}, + {Var{}, 64, 104}, + {Func{}, 64, 104}, {Label{}, 60, 96}, {Builtin{}, 60, 96}, {Nil{}, 56, 88}, // Misc - {Scope{}, 56, 96}, - {Package{}, 40, 80}, + {Scope{}, 60, 104}, + {Package{}, 36, 72}, + {_TypeSet{}, 28, 56}, } for _, test := range tests { diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go index aa0fbf40fced43..c99a12b2e92057 100644 --- a/src/cmd/compile/internal/types2/sizes.go +++ b/src/cmd/compile/internal/types2/sizes.go @@ -24,22 +24,21 @@ type Sizes interface { // StdSizes is a convenience type for creating commonly used Sizes. // It makes the following simplifying assumptions: // -// - The size of explicitly sized basic types (int16, etc.) is the -// specified size. -// - The size of strings and interfaces is 2*WordSize. -// - The size of slices is 3*WordSize. -// - The size of an array of n elements corresponds to the size of -// a struct of n consecutive fields of the array's element type. -// - The size of a struct is the offset of the last field plus that -// field's size. As with all element types, if the struct is used -// in an array its size must first be aligned to a multiple of the -// struct's alignment. -// - All other types have size WordSize. -// - Arrays and structs are aligned per spec definition; all other -// types are naturally aligned with a maximum alignment MaxAlign. +// - The size of explicitly sized basic types (int16, etc.) is the +// specified size. +// - The size of strings and interfaces is 2*WordSize. +// - The size of slices is 3*WordSize. +// - The size of an array of n elements corresponds to the size of +// a struct of n consecutive fields of the array's element type. +// - The size of a struct is the offset of the last field plus that +// field's size. As with all element types, if the struct is used +// in an array its size must first be aligned to a multiple of the +// struct's alignment. +// - All other types have size WordSize. +// - Arrays and structs are aligned per spec definition; all other +// types are naturally aligned with a maximum alignment MaxAlign. // // *StdSizes implements Sizes. -// type StdSizes struct { WordSize int64 // word size in bytes - must be >= 4 (32bits) MaxAlign int64 // maximum alignment in bytes - must be >= 1 @@ -48,12 +47,23 @@ type StdSizes struct { func (s *StdSizes) Alignof(T Type) int64 { // For arrays and structs, alignment is defined in terms // of alignment of the elements and fields, respectively. - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Array: // spec: "For a variable x of array type: unsafe.Alignof(x) // is the same as unsafe.Alignof(x[0]), but at least 1." return s.Alignof(t.elem) case *Struct: + if len(t.fields) == 0 && IsSyncAtomicAlign64(T) { + // Special case: sync/atomic.align64 is an + // empty struct we recognize as a signal that + // the struct it contains must be + // 64-bit-aligned. + // + // This logic is equivalent to the logic in + // cmd/compile/internal/types/size.go:calcStructOffset + return 8 + } + // spec: "For a variable x of struct type: unsafe.Alignof(x) // is the largest of the values unsafe.Alignof(x.f) for each // field f of x, but at least 1." @@ -67,12 +77,17 @@ func (s *StdSizes) Alignof(T Type) int64 { case *Slice, *Interface: // Multiword data structures are effectively structs // in which each element has size WordSize. + // Type parameters lead to variable sizes/alignments; + // StdSizes.Alignof won't be called for them. + assert(!isTypeParam(T)) return s.WordSize case *Basic: // Strings are like slices and interfaces. if t.Info()&IsString != 0 { return s.WordSize } + case *TypeParam, *Union: + unreachable() } a := s.Sizeof(T) // may be 0 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." @@ -89,6 +104,18 @@ func (s *StdSizes) Alignof(T Type) int64 { return a } +func IsSyncAtomicAlign64(T Type) bool { + named, ok := T.(*Named) + if !ok { + return false + } + obj := named.Obj() + return obj.Name() == "align64" && + obj.Pkg() != nil && + (obj.Pkg().Path() == "sync/atomic" || + obj.Pkg().Path() == "runtime/internal/atomic") +} + func (s *StdSizes) Offsetsof(fields []*Var) []int64 { offsets := make([]int64, len(fields)) var o int64 @@ -118,7 +145,7 @@ var basicSizes = [...]byte{ } func (s *StdSizes) Sizeof(T Type) int64 { - switch t := optype(T).(type) { + switch t := under(T).(type) { case *Basic: assert(isTyped(T)) k := t.kind @@ -148,10 +175,13 @@ func (s *StdSizes) Sizeof(T Type) int64 { } offsets := s.Offsetsof(t.fields) return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) - case *Sum: - panic("Sizeof unimplemented for type sum") case *Interface: + // Type parameters lead to variable sizes/alignments; + // StdSizes.Sizeof won't be called for them. + assert(!isTypeParam(T)) return s.WordSize * 2 + case *TypeParam, *Union: + unreachable() } return s.WordSize // catch-all } @@ -159,10 +189,11 @@ func (s *StdSizes) Sizeof(T Type) int64 { // common architecture word sizes and alignments var gcArchSizes = map[string]*StdSizes{ "386": {4, 4}, - "arm": {4, 4}, - "arm64": {8, 8}, "amd64": {8, 8}, "amd64p32": {4, 8}, + "arm": {4, 4}, + "arm64": {8, 8}, + "loong64": {8, 8}, "mips": {4, 4}, "mipsle": {4, 4}, "mips64": {8, 8}, @@ -181,7 +212,7 @@ var gcArchSizes = map[string]*StdSizes{ // The result is nil if a compiler/architecture pair is not known. // // Supported architectures for compiler "gc": -// "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle", +// "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle", // "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm". func SizesFor(compiler, arch string) Sizes { var m map[string]*StdSizes @@ -241,7 +272,7 @@ func (conf *Config) offsetsof(T *Struct) []int64 { func (conf *Config) offsetof(typ Type, index []int) int64 { var o int64 for _, i := range index { - s := asStruct(typ) + s := under(typ).(*Struct) o += conf.offsetsof(s)[i] typ = s.fields[i].typ } diff --git a/src/cmd/compile/internal/types2/sizes_test.go b/src/cmd/compile/internal/types2/sizes_test.go index c9a4942bed80c6..824ec838e2c339 100644 --- a/src/cmd/compile/internal/types2/sizes_test.go +++ b/src/cmd/compile/internal/types2/sizes_test.go @@ -14,12 +14,15 @@ import ( // findStructType typechecks src and returns the first struct type encountered. func findStructType(t *testing.T, src string) *types2.Struct { + return findStructTypeConfig(t, src, &types2.Config{}) +} + +func findStructTypeConfig(t *testing.T, src string, conf *types2.Config) *types2.Struct { f, err := parseSrc("x.go", src) if err != nil { t.Fatal(err) } info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)} - var conf types2.Config _, err = conf.Check("x", []*syntax.File{f}, &info) if err != nil { t.Fatal(err) @@ -105,3 +108,39 @@ const _ = unsafe.Offsetof(struct{ x int64 }{}.x) _ = conf.Sizes.Alignof(tv.Type) } } + +// Issue #53884. +func TestAtomicAlign(t *testing.T) { + const src = ` +package main + +import "sync/atomic" + +var s struct { + x int32 + y atomic.Int64 + z int64 +} +` + + want := []int64{0, 8, 16} + for _, arch := range []string{"386", "amd64"} { + t.Run(arch, func(t *testing.T) { + conf := types2.Config{ + Importer: defaultImporter(), + Sizes: types2.SizesFor("gc", arch), + } + ts := findStructTypeConfig(t, src, &conf) + var fields []*types2.Var + // Make a copy manually :( + for i := 0; i < ts.NumFields(); i++ { + fields = append(fields, ts.Field(i)) + } + + offsets := conf.Sizes.Offsetsof(fields) + if offsets[0] != want[0] || offsets[1] != want[1] || offsets[2] != want[2] { + t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, want) + } + }) + } +} diff --git a/src/cmd/compile/internal/types2/slice.go b/src/cmd/compile/internal/types2/slice.go new file mode 100644 index 00000000000000..9c22a6fb1b6853 --- /dev/null +++ b/src/cmd/compile/internal/types2/slice.go @@ -0,0 +1,19 @@ +// 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 types2 + +// A Slice represents a slice type. +type Slice struct { + elem Type +} + +// NewSlice returns a new slice type for the given element type. +func NewSlice(elem Type) *Slice { return &Slice{elem: elem} } + +// Elem returns the element type of slice s. +func (s *Slice) Elem() Type { return s.elem } + +func (s *Slice) Underlying() Type { return s } +func (s *Slice) String() string { return TypeString(s, nil) } diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go index cde35c17b6f717..5933c29d61ba5e 100644 --- a/src/cmd/compile/internal/types2/stdlib_test.go +++ b/src/cmd/compile/internal/types2/stdlib_test.go @@ -15,7 +15,6 @@ import ( "internal/testenv" "os" "path/filepath" - "runtime" "strings" "testing" "time" @@ -29,7 +28,7 @@ func TestStdlib(t *testing.T) { testenv.MustHaveGoBuild(t) pkgCount := 0 - duration := walkPkgDirs(filepath.Join(runtime.GOROOT(), "src"), func(dir string, filenames []string) { + duration := walkPkgDirs(filepath.Join(testenv.GOROOT(t), "src"), func(dir string, filenames []string) { typecheck(t, dir, filenames) pkgCount++ }, t.Error) @@ -162,12 +161,14 @@ func TestStdTest(t *testing.T) { t.Skip("skipping in short mode") } - testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), + testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"), "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore "directive.go", // tests compiler rejection of bad directive placement - ignore + "directive2.go", // tests compiler rejection of bad directive placement - ignore "embedfunc.go", // tests //go:embed "embedvers.go", // tests //go:embed "linkname2.go", // types2 doesn't check validity of //go:xxx directives + "linkname3.go", // types2 doesn't check validity of //go:xxx directives ) } @@ -178,7 +179,7 @@ func TestStdFixed(t *testing.T) { t.Skip("skipping in short mode") } - testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), + testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"), "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore "issue6889.go", // gc-specific test "issue11362.go", // canonical import path check @@ -192,13 +193,28 @@ func TestStdFixed(t *testing.T) { "issue20780.go", // types2 does not have constraints on stack size "issue42058a.go", // types2 does not have constraints on channel element size "issue42058b.go", // types2 does not have constraints on channel element size + "issue48097.go", // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function + "issue48230.go", // go/types doesn't check validity of //go:xxx directives + "issue49767.go", // go/types does not have constraints on channel element size + "issue49814.go", // go/types does not have constraints on array size + + // These tests requires runtime/cgo.Incomplete, which is only available on some platforms. + // However, types2 does not know about build constraints. + "bug514.go", + "issue40954.go", + "issue42032.go", + "issue42076.go", + "issue46903.go", + "issue51733.go", + "notinheap2.go", + "notinheap3.go", ) } func TestStdKen(t *testing.T) { testenv.MustHaveGoBuild(t) - testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) + testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken")) } // Package paths of excluded packages. @@ -206,7 +222,7 @@ var excluded = map[string]bool{ "builtin": true, // See #46027: some imports are missing for this submodule. - "crypto/ed25519/internal/edwards25519/field/_asm": true, + "crypto/internal/edwards25519/field/_asm": true, } // typecheck typechecks the given package files. @@ -305,16 +321,13 @@ func (w *walker) walk(dir string) { } // apply pkgh to the files in directory dir - // but ignore files directly under $GOROOT/src (might be temporary test files). - if dir != filepath.Join(runtime.GOROOT(), "src") { - files, err := pkgFilenames(dir) - if err != nil { - w.errh(err) - return - } - if files != nil { - w.pkgh(dir, files) - } + pkgFiles, err := pkgFilenames(dir) + if err != nil { + w.errh(err) + return + } + if pkgFiles != nil { + w.pkgh(dir, pkgFiles) } // traverse subdirectories, but don't walk into testdata diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index c3e646c80c142d..adb24d495dca32 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -14,27 +14,24 @@ import ( func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *syntax.BlockStmt, iota constant.Value) { if check.conf.IgnoreFuncBodies { - panic("internal error: function body not ignored") + panic("function body not ignored") } if check.conf.Trace { - check.trace(body.Pos(), "--- %s: %s", name, sig) - defer func() { - check.trace(syntax.EndPos(body), "--- ") - }() + check.trace(body.Pos(), "-- %s: %s", name, sig) } // set function scope extent sig.scope.pos = body.Pos() sig.scope.end = syntax.EndPos(body) - // save/restore current context and setup function context + // save/restore current environment and set up function environment // (and use 0 indentation at function start) - defer func(ctxt context, indent int) { - check.context = ctxt + defer func(env environment, indent int) { + check.environment = env check.indent = indent - }(check.context, check.indent) - check.context = context{ + }(check.environment, check.indent) + check.environment = environment{ decl: decl, scope: sig.scope, iota: iota, @@ -44,7 +41,7 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.stmtList(0, body.List) - if check.hasLabel && !check.conf.IgnoreLabels { + if check.hasLabel && !check.conf.IgnoreBranchErrors { check.labels(body) } @@ -52,11 +49,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.error(body.Rbrace, "missing return") } - // TODO(gri) Should we make it an error to declare generic functions - // where the type parameters are not used? - // 12/19/2018: Probably not - it can make sense to have an API with - // all functions uniformly sharing the same type parameters. - // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." check.usage(sig.scope) @@ -64,7 +56,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body func (check *Checker) usage(scope *Scope) { var unused []*Var - for _, elem := range scope.elems { + for name, elem := range scope.elems { + elem = resolve(name, elem) if v, _ := elem.(*Var); v != nil && !v.used { unused = append(unused, v) } @@ -99,6 +92,7 @@ const ( // additional context information finalSwitchCase + inTypeSwitch ) func (check *Checker) simpleStmt(s syntax.Stmt) { @@ -171,10 +165,16 @@ func (check *Checker) closeScope() { check.scope = check.scope.Parent() } -func (check *Checker) suspendedCall(keyword string, call *syntax.CallExpr) { +func (check *Checker) suspendedCall(keyword string, call syntax.Expr) { + if _, ok := call.(*syntax.CallExpr); !ok { + check.errorf(call, "expression in %s must be function call", keyword) + check.use(call) + return + } + var x operand var msg string - switch check.rawExpr(&x, call, nil) { + switch check.rawExpr(&x, call, nil, false) { case conversion: msg = "requires function call, not conversion" case expression: @@ -243,7 +243,7 @@ L: } // Order matters: By comparing v against x, error positions are at the case values. res := v // keep original v unchanged - check.comparison(&res, x, syntax.Eql) + check.comparison(&res, x, syntax.Eql, true) if res.mode == invalid { continue L } @@ -255,7 +255,7 @@ L: // look for duplicate types for a given value // (quadratic algorithm, but these lists tend to be very short) for _, vt := range seen[val] { - if check.identical(v.typ, vt.typ) { + if Identical(v.typ, vt.typ) { var err error_ err.errorf(&v, "duplicate case %s in expression switch", &v) err.errorf(vt.pos, "previous case") @@ -268,24 +268,39 @@ L: } } -func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) { +// isNil reports whether the expression e denotes the predeclared value nil. +func (check *Checker) isNil(e syntax.Expr) bool { + // The only way to express the nil value is by literally writing nil (possibly in parentheses). + if name, _ := unparen(e).(*syntax.Name); name != nil { + _, ok := check.lookup(name.Value).(*Nil) + return ok + } + return false +} + +// If the type switch expression is invalid, x is nil. +func (check *Checker) caseTypes(x *operand, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) { + var dummy operand L: for _, e := range types { - T = check.typOrNil(e) - if T == Typ[Invalid] { - continue L - } - if T != nil { - check.ordinaryType(e.Pos(), T) + // The spec allows the value nil instead of a type. + if check.isNil(e) { + T = nil + check.expr(&dummy, e) // run e through expr so we get the usual Info recordings + } else { + T = check.varType(e) + if T == Typ[Invalid] { + continue L + } } // look for duplicate types // (quadratic algorithm, but type switches tend to be reasonably small) for t, other := range seen { - if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) { + if T == nil && t == nil || T != nil && t != nil && Identical(T, t) { // talk about "case" rather than "type" because of nil case Ts := "nil" if T != nil { - Ts = T.String() + Ts = TypeString(T, check.qualifier) } var err error_ err.errorf(e, "duplicate case %s in type switch", Ts) @@ -295,13 +310,54 @@ L: } } seen[T] = e - if T != nil { - check.typeAssertion(e.Pos(), x, xtyp, T) + if x != nil && T != nil { + check.typeAssertion(e, x, T, true) } } return } +// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead. +// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) +// +// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[string]syntax.Expr) (T Type) { +// var dummy operand +// L: +// for _, e := range types { +// // The spec allows the value nil instead of a type. +// var hash string +// if check.isNil(e) { +// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings +// T = nil +// hash = "" // avoid collision with a type named nil +// } else { +// T = check.varType(e) +// if T == Typ[Invalid] { +// continue L +// } +// hash = typeHash(T, nil) +// } +// // look for duplicate types +// if other := seen[hash]; other != nil { +// // talk about "case" rather than "type" because of nil case +// Ts := "nil" +// if T != nil { +// Ts = TypeString(T, check.qualifier) +// } +// var err error_ +// err.errorf(e, "duplicate case %s in type switch", Ts) +// err.errorf(other, "previous case") +// check.report(&err) +// continue L +// } +// seen[hash] = e +// if T != nil { +// check.typeAssertion(e, x, xtyp, T, true) +// } +// } +// return +// } + // stmt typechecks statement s. func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { // statements must end with the same top scope as they started with @@ -318,7 +374,9 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { // process collected function literals before scope changes defer check.processDelayed(len(check.delayed)) - inner := ctxt &^ (fallthroughOk | finalSwitchCase) + // reset context for statements of inner blocks + inner := ctxt &^ (fallthroughOk | finalSwitchCase | inTypeSwitch) + switch s := s.(type) { case *syntax.EmptyStmt: // ignore @@ -335,7 +393,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { // function and method calls and receive operations can appear // in statement context. Such statements may be parenthesized." var x operand - kind := check.rawExpr(&x, s.X, nil) + kind := check.rawExpr(&x, s.X, nil, false) var msg string switch x.mode { default: @@ -351,25 +409,27 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { check.errorf(&x, "%s %s", &x, msg) case *syntax.SendStmt: - var ch, x operand + var ch, val operand check.expr(&ch, s.Chan) - check.expr(&x, s.Value) - if ch.mode == invalid || x.mode == invalid { + check.expr(&val, s.Value) + if ch.mode == invalid || val.mode == invalid { return } - - tch := asChan(ch.typ) - if tch == nil { - check.errorf(s, invalidOp+"cannot send to non-chan type %s", ch.typ) + u := coreType(ch.typ) + if u == nil { + check.errorf(s, invalidOp+"cannot send to %s: no core type", &ch) return } - - if tch.dir == RecvOnly { - check.errorf(s, invalidOp+"cannot send to receive-only type %s", tch) + uch, _ := u.(*Chan) + if uch == nil { + check.errorf(s, invalidOp+"cannot send to non-channel %s", &ch) return } - - check.assignment(&x, tch.elem, "send") + if uch.dir == RecvOnly { + check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch) + return + } + check.assignment(&val, uch.elem, "send") case *syntax.AssignStmt: lhs := unpackExpr(s.Lhs) @@ -384,7 +444,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { if x.mode == invalid { return } - if !isNumeric(x.typ) { + if !allNumeric(x.typ) { check.errorf(lhs[0], invalidOp+"%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ) return } @@ -413,7 +473,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { check.assignVar(lhs[0], &x) case *syntax.CallStmt: - // TODO(gri) get rid of this conversion to string kind := "go" if s.Tok == syntax.Defer { kind = "defer" @@ -422,30 +481,28 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { case *syntax.ReturnStmt: res := check.sig.results + // Return with implicit results allowed for function with named results. + // (If one is named, all are named.) results := unpackExpr(s.Results) - if res.Len() > 0 { - // function returns results - // (if one, say the first, result parameter is named, all of them are named) - if len(results) == 0 && res.vars[0].name != "" { - // spec: "Implementation restriction: A compiler may disallow an empty expression - // list in a "return" statement if a different entity (constant, type, or variable) - // with the same name as a result parameter is in scope at the place of the return." - for _, obj := range res.vars { - if alt := check.lookup(obj.name); alt != nil && alt != obj { - var err error_ - err.errorf(s, "result parameter %s not in scope at return", obj.name) - err.errorf(alt, "inner declaration of %s", obj) - check.report(&err) - // ok to continue - } + if len(results) == 0 && res.Len() > 0 && res.vars[0].name != "" { + // spec: "Implementation restriction: A compiler may disallow an empty expression + // list in a "return" statement if a different entity (constant, type, or variable) + // with the same name as a result parameter is in scope at the place of the return." + for _, obj := range res.vars { + if alt := check.lookup(obj.name); alt != nil && alt != obj { + var err error_ + err.errorf(s, "result parameter %s not in scope at return", obj.name) + err.errorf(alt, "inner declaration of %s", obj) + check.report(&err) + // ok to continue } - } else { - // return has results or result parameters are unnamed - check.initVars(res.vars, results, s.Pos()) } - } else if len(results) > 0 { - check.error(results[0], "no result values expected") - check.use(results...) + } else { + var lhs []*Var + if res.Len() > 0 { + lhs = res.vars + } + check.initVars(lhs, results, s) } case *syntax.BranchStmt: @@ -453,28 +510,28 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { check.hasLabel = true break // checked in 2nd pass (check.labels) } + if check.conf.IgnoreBranchErrors { + break + } switch s.Tok { case syntax.Break: if ctxt&breakOk == 0 { - if check.conf.CompilerErrorMessages { - check.error(s, "break is not in a loop, switch, or select statement") - } else { - check.error(s, "break not in for, switch, or select statement") - } + check.error(s, "break not in for, switch, or select statement") } case syntax.Continue: if ctxt&continueOk == 0 { - if check.conf.CompilerErrorMessages { - check.error(s, "continue is not in a loop") - } else { - check.error(s, "continue not in for statement") - } + check.error(s, "continue not in for statement") } case syntax.Fallthrough: if ctxt&fallthroughOk == 0 { - msg := "fallthrough statement out of place" - if ctxt&finalSwitchCase != 0 { + var msg string + switch { + case ctxt&finalSwitchCase != 0: msg = "cannot fallthrough final case in switch" + case ctxt&inTypeSwitch != 0: + msg = "cannot fallthrough in type switch" + default: + msg = "fallthrough statement out of place" } check.error(s, msg) } @@ -498,7 +555,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { check.simpleStmt(s.Init) var x operand check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { + if x.mode != invalid && !allBoolean(x.typ) { check.error(s.Cond, "non-boolean condition in if statement") } check.stmt(inner, s.Then) @@ -521,7 +578,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { check.simpleStmt(s.Init) if g, _ := s.Tag.(*syntax.TypeSwitchGuard); g != nil { - check.typeSwitchStmt(inner, s, g) + check.typeSwitchStmt(inner|inTypeSwitch, s, g) } else { check.switchStmt(inner, s) } @@ -575,19 +632,20 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { case *syntax.ForStmt: inner |= breakOk | continueOk - check.openScope(s, "for") - defer check.closeScope() if rclause, _ := s.Init.(*syntax.RangeClause); rclause != nil { check.rangeStmt(inner, s, rclause) break } + check.openScope(s, "for") + defer check.closeScope() + check.simpleStmt(s.Init) if s.Cond != nil { var x operand check.expr(&x, s.Cond) - if x.mode != invalid && !isBoolean(x.typ) { + if x.mode != invalid && !allBoolean(x.typ) { check.error(s.Cond, "non-boolean condition in for statement") } } @@ -596,9 +654,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { // declaration, but the post statement must not." if s, _ := s.Post.(*syntax.AssignStmt); s != nil && s.Op == syntax.Def { // The parser already reported an error. - // Don't call useLHS here because we want to use the lhs in - // this erroneous statement so that we don't get errors about - // these lhs variables being declared but not used. check.use(s.Lhs) // avoid follow-up errors } check.stmt(inner, s.Body) @@ -684,16 +739,18 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu if x.mode == invalid { return } - // Caution: We're not using asInterface here because we don't want - // to switch on a suitably constrained type parameter (for - // now). - // TODO(gri) Need to revisit this. - xtyp, _ := under(x.typ).(*Interface) - if xtyp == nil { - check.errorf(&x, "%s is not an interface type", &x) - return + + // TODO(gri) we may want to permit type switches on type parameter values at some point + var sx *operand // switch expression against which cases are compared against; nil if invalid + if isTypeParam(x.typ) { + check.errorf(&x, "cannot use type switch on type parameter value %s", &x) + } else { + if _, ok := under(x.typ).(*Interface); ok { + sx = &x + } else { + check.errorf(&x, "%s is not an interface", &x) + } } - check.ordinaryType(x.Pos(), xtyp) check.multipleSwitchDefaults(s.Body) @@ -710,7 +767,7 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu } // Check each type in this type switch case. cases := unpackExpr(clause.Cases) - T := check.caseTypes(&x, xtyp, cases, seen) + T := check.caseTypes(sx, cases, seen) check.openScopeUntil(clause, end, "case") // If lhs exists, declare a corresponding variable in the case-local scope. if lhs != nil { @@ -759,44 +816,66 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu } func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *syntax.RangeClause) { - // scope already opened - - // check expression to iterate over - var x operand - check.expr(&x, rclause.X) - // determine lhs, if any sKey := rclause.Lhs // possibly nil - var sValue syntax.Expr + var sValue, sExtra syntax.Expr if p, _ := sKey.(*syntax.ListExpr); p != nil { - if len(p.ElemList) != 2 { + if len(p.ElemList) < 2 { check.error(s, invalidAST+"invalid lhs in range clause") return } + // len(p.ElemList) >= 2 sKey = p.ElemList[0] sValue = p.ElemList[1] + if len(p.ElemList) > 2 { + // delay error reporting until we know more + sExtra = p.ElemList[2] + } } + // check expression to iterate over + var x operand + check.expr(&x, rclause.X) + // determine key/value types var key, val Type if x.mode != invalid { - typ := optype(x.typ) - if _, ok := typ.(*Chan); ok && sValue != nil { - // TODO(gri) this also needs to happen for channels in generic variables - check.softErrorf(sValue, "range over %s permits only one iteration variable", &x) - // ok to continue + // Ranging over a type parameter is permitted if it has a core type. + var cause string + u := coreType(x.typ) + if t, _ := u.(*Chan); t != nil { + if sValue != nil { + check.softErrorf(sValue, "range over %s permits only one iteration variable", &x) + // ok to continue + } + if t.dir == SendOnly { + cause = "receive from send-only channel" + } + } else { + if sExtra != nil { + check.softErrorf(sExtra, "range clause permits at most two iteration variables") + // ok to continue + } + if u == nil { + cause = check.sprintf("%s has no core type", x.typ) + } } - var msg string - key, val, msg = rangeKeyVal(typ, isVarName(sKey), isVarName(sValue)) - if key == nil || msg != "" { - if msg != "" { - msg = ": " + msg + key, val = rangeKeyVal(u) + if key == nil || cause != "" { + if cause == "" { + check.softErrorf(&x, "cannot range over %s", &x) + } else { + check.softErrorf(&x, "cannot range over %s (%s)", &x, cause) } - check.softErrorf(&x, "cannot range over %s%s", &x, msg) // ok to continue } } + // Open the for-statement block scope now, after the range clause. + // Iteration variables declared with := need to go in this scope (was issue #51437). + check.openScope(s, "range") + defer check.closeScope() + // check assignment to/declaration of iteration variables // (irregular assignment, cannot easily map to existing assignment checks) @@ -805,9 +884,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s rhs := [2]Type{key, val} // key, val may be nil if rclause.Def { - // short variable declaration; variable scope starts after the range clause - // (the for loop opens a new scope, so variables on the lhs never redeclare - // previously declared variables) + // short variable declaration var vars []*Var for i, lhs := range lhs { if lhs == nil { @@ -844,12 +921,8 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s // declare variables if len(vars) > 0 { - scopePos := syntax.EndPos(rclause.X) // TODO(gri) should this just be s.Body.Pos (spec clarification)? + scopePos := s.Body.Pos() for _, obj := range vars { - // spec: "The scope of a constant or variable identifier declared inside - // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl - // for short variable declarations) and ends at the end of the innermost - // containing block." check.declare(check.scope, nil /* recordDef already called */, obj, scopePos) } } else { @@ -873,71 +946,23 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s check.stmt(inner, s.Body) } -// isVarName reports whether x is a non-nil, non-blank (_) expression. -func isVarName(x syntax.Expr) bool { - if x == nil { - return false - } - ident, _ := unparen(x).(*syntax.Name) - return ident == nil || ident.Value != "_" -} - // rangeKeyVal returns the key and value type produced by a range clause -// over an expression of type typ, and possibly an error message. If the -// range clause is not permitted the returned key is nil or msg is not -// empty (in that case we still may have a non-nil key type which can be -// used to reduce the chance for follow-on errors). -// The wantKey, wantVal, and hasVal flags indicate which of the iteration -// variables are used or present; this matters if we range over a generic -// type where not all keys or values are of the same type. -func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) { - switch typ := typ.(type) { +// over an expression of type typ. If the range clause is not permitted +// the results are nil. +func rangeKeyVal(typ Type) (key, val Type) { + switch typ := arrayPtrDeref(typ).(type) { case *Basic: if isString(typ) { - return Typ[Int], universeRune, "" // use 'rune' name + return Typ[Int], universeRune // use 'rune' name } case *Array: - return Typ[Int], typ.elem, "" + return Typ[Int], typ.elem case *Slice: - return Typ[Int], typ.elem, "" - case *Pointer: - if typ := asArray(typ.base); typ != nil { - return Typ[Int], typ.elem, "" - } + return Typ[Int], typ.elem case *Map: - return typ.key, typ.elem, "" + return typ.key, typ.elem case *Chan: - var msg string - if typ.dir == SendOnly { - msg = "receive from send-only channel" - } - return typ.elem, Typ[Invalid], msg - case *Sum: - first := true - var key, val Type - var msg string - typ.is(func(t Type) bool { - k, v, m := rangeKeyVal(under(t), wantKey, wantVal) - if k == nil || m != "" { - key, val, msg = k, v, m - return false - } - if first { - key, val, msg = k, v, m - first = false - return true - } - if wantKey && !Identical(key, k) { - key, val, msg = nil, nil, "all possible values must have the same key type" - return false - } - if wantVal && !Identical(val, v) { - key, val, msg = nil, nil, "all possible values must have the same element type" - return false - } - return true - }) - return key, val, msg - } - return nil, nil, "" + return typ.elem, Typ[Invalid] + } + return } diff --git a/src/cmd/compile/internal/types2/struct.go b/src/cmd/compile/internal/types2/struct.go new file mode 100644 index 00000000000000..31a3b1af5bc338 --- /dev/null +++ b/src/cmd/compile/internal/types2/struct.go @@ -0,0 +1,225 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "cmd/compile/internal/syntax" + "strconv" +) + +// ---------------------------------------------------------------------------- +// API + +// A Struct represents a struct type. +type Struct struct { + fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0) + tags []string // field tags; nil if there are no tags +} + +// NewStruct returns a new struct with the given fields and corresponding field tags. +// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be +// only as long as required to hold the tag with the largest index i. Consequently, +// if no field has a tag, tags may be nil. +func NewStruct(fields []*Var, tags []string) *Struct { + var fset objset + for _, f := range fields { + if f.name != "_" && fset.insert(f) != nil { + panic("multiple fields with the same name") + } + } + if len(tags) > len(fields) { + panic("more tags than fields") + } + s := &Struct{fields: fields, tags: tags} + s.markComplete() + return s +} + +// NumFields returns the number of fields in the struct (including blank and embedded fields). +func (s *Struct) NumFields() int { return len(s.fields) } + +// Field returns the i'th field for 0 <= i < NumFields(). +func (s *Struct) Field(i int) *Var { return s.fields[i] } + +// Tag returns the i'th field tag for 0 <= i < NumFields(). +func (s *Struct) Tag(i int) string { + if i < len(s.tags) { + return s.tags[i] + } + return "" +} + +func (s *Struct) Underlying() Type { return s } +func (s *Struct) String() string { return TypeString(s, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +func (s *Struct) markComplete() { + if s.fields == nil { + s.fields = make([]*Var, 0) + } +} + +func (check *Checker) structType(styp *Struct, e *syntax.StructType) { + if e.FieldList == nil { + styp.markComplete() + return + } + + // struct fields and tags + var fields []*Var + var tags []string + + // for double-declaration checks + var fset objset + + // current field typ and tag + var typ Type + var tag string + add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) { + if tag != "" && tags == nil { + tags = make([]string, len(fields)) + } + if tags != nil { + tags = append(tags, tag) + } + + name := ident.Value + fld := NewField(pos, check.pkg, name, typ, embedded) + // spec: "Within a struct, non-blank field names must be unique." + if name == "_" || check.declareInSet(&fset, pos, fld) { + fields = append(fields, fld) + check.recordDef(ident, fld) + } + } + + // addInvalid adds an embedded field of invalid type to the struct for + // fields with errors; this keeps the number of struct fields in sync + // with the source as long as the fields are _ or have different names + // (issue #25627). + addInvalid := func(ident *syntax.Name, pos syntax.Pos) { + typ = Typ[Invalid] + tag = "" + add(ident, true, pos) + } + + var prev syntax.Expr + for i, f := range e.FieldList { + // Fields declared syntactically with the same type (e.g.: a, b, c T) + // share the same type expression. Only check type if it's a new type. + if i == 0 || f.Type != prev { + typ = check.varType(f.Type) + prev = f.Type + } + tag = "" + if i < len(e.TagList) { + tag = check.tag(e.TagList[i]) + } + if f.Name != nil { + // named field + add(f.Name, false, f.Name.Pos()) + } else { + // embedded field + // spec: "An embedded type must be specified as a type name T or as a + // pointer to a non-interface type name *T, and T itself may not be a + // pointer type." + pos := syntax.StartPos(f.Type) + name := embeddedFieldIdent(f.Type) + if name == nil { + check.errorf(pos, "invalid embedded field type %s", f.Type) + name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos + addInvalid(name, pos) + continue + } + add(name, true, pos) + + // Because we have a name, typ must be of the form T or *T, where T is the name + // of a (named or alias) type, and t (= deref(typ)) must be the type of T. + // We must delay this check to the end because we don't want to instantiate + // (via under(t)) a possibly incomplete type. + embeddedTyp := typ // for closure below + embeddedPos := pos + check.later(func() { + t, isPtr := deref(embeddedTyp) + switch u := under(t).(type) { + case *Basic: + if t == Typ[Invalid] { + // error was reported before + return + } + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer") + } + case *Pointer: + check.error(embeddedPos, "embedded field type cannot be a pointer") + case *Interface: + if isTypeParam(t) { + check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter") + break + } + if isPtr { + check.error(embeddedPos, "embedded field type cannot be a pointer to an interface") + } + } + }).describef(embeddedPos, "check embedded type %s", embeddedTyp) + } + } + + styp.fields = fields + styp.tags = tags + styp.markComplete() +} + +func embeddedFieldIdent(e syntax.Expr) *syntax.Name { + switch e := e.(type) { + case *syntax.Name: + return e + case *syntax.Operation: + if base := ptrBase(e); base != nil { + // *T is valid, but **T is not + if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil { + return embeddedFieldIdent(e.X) + } + } + case *syntax.SelectorExpr: + return e.Sel + case *syntax.IndexExpr: + return embeddedFieldIdent(e.X) + } + return nil // invalid embedded field +} + +func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool { + if alt := oset.insert(obj); alt != nil { + var err error_ + err.errorf(pos, "%s redeclared", obj.Name()) + err.recordAltDecl(alt) + check.report(&err) + return false + } + return true +} + +func (check *Checker) tag(t *syntax.BasicLit) string { + // If t.Bad, an error was reported during parsing. + if t != nil && !t.Bad { + if t.Kind == syntax.StringLit { + if val, err := strconv.Unquote(t.Value); err == nil { + return val + } + } + check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value) + } + return "" +} + +func ptrBase(x *syntax.Operation) syntax.Expr { + if x.Op == syntax.Mul && x.Y == nil { + return x.X + } + return nil +} diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index c8e428c1832311..d5a48c699561d0 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -2,215 +2,57 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements instantiation of generic types -// through substitution of type parameters by actual -// types. +// This file implements type parameter substitution. package types2 -import ( - "bytes" - "cmd/compile/internal/syntax" - "fmt" -) - -type substMap struct { - // The targs field is currently needed for *Named type substitution. - // TODO(gri) rewrite that code, get rid of this field, and make this - // struct just the map (proj) - targs []Type - proj map[*TypeParam]Type -} +import "cmd/compile/internal/syntax" + +type substMap map[*TypeParam]Type // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. // If targs[i] is nil, tpars[i] is not substituted. -func makeSubstMap(tpars []*TypeName, targs []Type) *substMap { +func makeSubstMap(tpars []*TypeParam, targs []Type) substMap { assert(len(tpars) == len(targs)) - proj := make(map[*TypeParam]Type, len(tpars)) + proj := make(substMap, len(tpars)) for i, tpar := range tpars { - // We must expand type arguments otherwise *instance - // types end up as components in composite types. - // TODO(gri) explain why this causes problems, if it does - targ := expand(targs[i]) // possibly nil - targs[i] = targ - proj[tpar.typ.(*TypeParam)] = targ + proj[tpar] = targs[i] } - return &substMap{targs, proj} + return proj } -func (m *substMap) String() string { - return fmt.Sprintf("%s", m.proj) +// makeRenameMap is like makeSubstMap, but creates a map used to rename type +// parameters in from with the type parameters in to. +func makeRenameMap(from, to []*TypeParam) substMap { + assert(len(from) == len(to)) + proj := make(substMap, len(from)) + for i, tpar := range from { + proj[tpar] = to[i] + } + return proj } -func (m *substMap) empty() bool { - return len(m.proj) == 0 +func (m substMap) empty() bool { + return len(m) == 0 } -func (m *substMap) lookup(tpar *TypeParam) Type { - if t := m.proj[tpar]; t != nil { +func (m substMap) lookup(tpar *TypeParam) Type { + if t := m[tpar]; t != nil { return t } return tpar } -func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslist []syntax.Pos) (res Type) { - if check.conf.Trace { - check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs)) - check.indent++ - defer func() { - check.indent-- - var under Type - if res != nil { - // Calling under() here may lead to endless instantiations. - // Test case: type T[P any] T[P] - // TODO(gri) investigate if that's a bug or to be expected. - under = res.Underlying() - } - check.trace(pos, "=> %s (under = %s)", res, under) - }() - } - - assert(len(poslist) <= len(targs)) - - // TODO(gri) What is better here: work with TypeParams, or work with TypeNames? - var tparams []*TypeName - switch t := typ.(type) { - case *Named: - tparams = t.tparams - case *Signature: - tparams = t.tparams - defer func() { - // If we had an unexpected failure somewhere don't panic below when - // asserting res.(*Signature). Check for *Signature in case Typ[Invalid] - // is returned. - if _, ok := res.(*Signature); !ok { - return - } - // If the signature doesn't use its type parameters, subst - // will not make a copy. In that case, make a copy now (so - // we can set tparams to nil w/o causing side-effects). - if t == res { - copy := *t - res = © - } - // After instantiating a generic signature, it is not generic - // anymore; we need to set tparams to nil. - res.(*Signature).tparams = nil - }() - - default: - check.dump("%v: cannot instantiate %v", pos, typ) - unreachable() // only defined types and (defined) functions can be generic - - } - - // the number of supplied types must match the number of type parameters - if len(targs) != len(tparams) { - // TODO(gri) provide better error message - check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams)) - return Typ[Invalid] - } - - if len(tparams) == 0 { - return typ // nothing to do (minor optimization) - } - - smap := makeSubstMap(tparams, targs) - - // check bounds - for i, tname := range tparams { - tpar := tname.typ.(*TypeParam) - iface := tpar.Bound() - if iface.Empty() { - continue // no type bound - } - - targ := targs[i] - - // best position for error reporting - pos := pos - if i < len(poslist) { - pos = poslist[i] - } - - // The type parameter bound is parameterized with the same type parameters - // as the instantiated type; before we can use it for bounds checking we - // need to instantiate it with the type arguments with which we instantiate - // the parameterized type. - iface = check.subst(pos, iface, smap).(*Interface) - - // targ must implement iface (methods) - // - check only if we have methods - check.completeInterface(nopos, iface) - if len(iface.allMethods) > 0 { - // If the type argument is a pointer to a type parameter, the type argument's - // method set is empty. - // TODO(gri) is this what we want? (spec question) - if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil { - check.errorf(pos, "%s has no methods", targ) - break - } - if m, wrong := check.missingMethod(targ, iface, true); m != nil { - // TODO(gri) needs to print updated name to avoid major confusion in error message! - // (print warning for now) - // Old warning: - // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m) - if m.name == "==" { - // We don't want to report "missing method ==". - check.softErrorf(pos, "%s does not satisfy comparable", targ) - } else if wrong != nil { - // TODO(gri) This can still report uninstantiated types which makes the error message - // more difficult to read then necessary. - check.softErrorf(pos, - "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s", - targ, tpar.bound, wrong, m, - ) - } else { - check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name) - } - break - } - } - - // targ's underlying type must also be one of the interface types listed, if any - if iface.allTypes == nil { - continue // nothing to do - } - - // If targ is itself a type parameter, each of its possible types, but at least one, must be in the - // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types). - if targ := asTypeParam(targ); targ != nil { - targBound := targ.Bound() - if targBound.allTypes == nil { - check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ) - break - } - for _, t := range unpack(targBound.allTypes) { - if !iface.isSatisfiedBy(t) { - // TODO(gri) match this error message with the one below (or vice versa) - check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes) - break - } - } - break - } - - // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any. - if !iface.isSatisfiedBy(targ) { - check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes) - break - } - } - - return check.subst(pos, typ, smap) -} - -// subst returns the type typ with its type parameters tpars replaced by -// the corresponding type arguments targs, recursively. -// subst is functional in the sense that it doesn't modify the incoming -// type. If a substitution took place, the result type is different from +// subst returns the type typ with its type parameters tpars replaced by the +// corresponding type arguments targs, recursively. subst doesn't modify the +// incoming type. If a substitution took place, the result type is different // from the incoming type. -func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type { +// +// If expanding is non-nil, it is the instance type currently being expanded. +// One of expanding or ctxt must be non-nil. +func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, expanding *Named, ctxt *Context) Type { + assert(expanding != nil || ctxt != nil) + if smap.empty() { return typ } @@ -224,15 +66,22 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type { } // general case - subst := subster{check, pos, make(map[Type]Type), smap} + subst := subster{ + pos: pos, + smap: smap, + check: check, + expanding: expanding, + ctxt: ctxt, + } return subst.typ(typ) } type subster struct { - check *Checker - pos syntax.Pos - cache map[Type]Type - smap *substMap + pos syntax.Pos + smap substMap + check *Checker // nil if called via Instantiate + expanding *Named // if non-nil, the instance that is being expanded + ctxt *Context } func (subst *subster) typ(typ Type) Type { @@ -241,7 +90,7 @@ func (subst *subster) typ(typ Type) Type { // Call typOrNil if it's possible that typ is nil. panic("nil typ") - case *Basic, *bottom, *top: + case *Basic: // nothing to do case *Array: @@ -258,7 +107,9 @@ func (subst *subster) typ(typ Type) Type { case *Struct: if fields, copied := subst.varList(t.fields); copied { - return &Struct{fields: fields, tags: t.tags} + s := &Struct{fields: fields, tags: t.tags} + s.markComplete() + return s } case *Pointer: @@ -271,18 +122,29 @@ func (subst *subster) typ(typ Type) Type { return subst.tuple(t) case *Signature: - // TODO(gri) rethink the recv situation with respect to methods on parameterized types - // recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain + // Preserve the receiver: it is handled during *Interface and *Named type + // substitution. + // + // Naively doing the substitution here can lead to an infinite recursion in + // the case where the receiver is an interface. For example, consider the + // following declaration: + // + // type T[A any] struct { f interface{ m() } } + // + // In this case, the type of f is an interface that is itself the receiver + // type of all of its methods. Because we have no type name to break + // cycles, substituting in the recv results in an infinite loop of + // recv->interface->recv->interface->... recv := t.recv + params := subst.tuple(t.params) results := subst.tuple(t.results) - if recv != t.recv || params != t.params || results != t.results { + if params != t.params || results != t.results { return &Signature{ rparams: t.rparams, - // TODO(gri) Why can't we nil out tparams here, rather than in - // instantiate above? - tparams: t.tparams, - scope: t.scope, + // TODO(gri) why can't we nil out tparams here, rather than in instantiate? + tparams: t.tparams, + // instantiated signatures have a nil scope recv: recv, params: params, results: results, @@ -290,29 +152,37 @@ func (subst *subster) typ(typ Type) Type { } } - case *Sum: - types, copied := subst.typeList(t.types) + case *Union: + terms, copied := subst.termlist(t.terms) if copied { - // Don't do it manually, with a Sum literal: the new - // types list may not be unique and NewSum may remove - // duplicates. - return NewSum(types) + // term list substitution may introduce duplicate terms (unlikely but possible). + // This is ok; lazy type set computation will determine the actual type set + // in normal form. + return &Union{terms} } case *Interface: methods, mcopied := subst.funcList(t.methods) - types := t.types - if t.types != nil { - types = subst.typ(t.types) - } embeddeds, ecopied := subst.typeList(t.embeddeds) - if mcopied || types != t.types || ecopied { - iface := &Interface{methods: methods, types: types, embeddeds: embeddeds} - if subst.check == nil { - panic("internal error: cannot instantiate interfaces yet") - } - subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement - subst.check.completeInterface(nopos, iface) + if mcopied || ecopied { + iface := subst.check.newInterface() + iface.embeddeds = embeddeds + iface.implicit = t.implicit + iface.complete = t.complete + // If we've changed the interface type, we may need to replace its + // receiver if the receiver type is the original interface. Receivers of + // *Named type are replaced during named type expansion. + // + // Notably, it's possible to reach here and not create a new *Interface, + // even though the receiver type may be parameterized. For example: + // + // type T[P any] interface{ m() } + // + // In this case the interface will not be substituted here, because its + // method signatures do not depend on the type parameter P, but we still + // need to create new interface methods to hold the instantiated + // receiver. This is handled by Named.expandUnderlying. + iface.methods, _ = replaceRecvType(methods, t, iface) return iface } @@ -342,77 +212,55 @@ func (subst *subster) typ(typ Type) Type { } } - if t.tparams == nil { + // subst is called during expansion, so in this function we need to be + // careful not to call any methods that would cause t to be expanded: doing + // so would result in deadlock. + // + // So we call t.Origin().TypeParams() rather than t.TypeParams(). + orig := t.Origin() + n := orig.TypeParams().Len() + if n == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } - var new_targs []Type - - if len(t.targs) > 0 { - // already instantiated - dump(">>> %s already instantiated", t) - assert(len(t.targs) == len(t.tparams)) - // For each (existing) type argument targ, determine if it needs - // to be substituted; i.e., if it is or contains a type parameter - // that has a type argument for it. - for i, targ := range t.targs { - dump(">>> %d targ = %s", i, targ) - new_targ := subst.typ(targ) - if new_targ != targ { - dump(">>> substituted %d targ %s => %s", i, targ, new_targ) - if new_targs == nil { - new_targs = make([]Type, len(t.tparams)) - copy(new_targs, t.targs) - } - new_targs[i] = new_targ - } - } - - if new_targs == nil { - dump(">>> nothing to substitute in %s", t) - return t // nothing to substitute - } - } else { - // not yet instantiated - dump(">>> first instantiation of %s", t) - new_targs = subst.smap.targs + var newTArgs []Type + if t.TypeArgs().Len() != n { + return Typ[Invalid] // error reported elsewhere } - // before creating a new named type, check if we have this one already - h := instantiatedHash(t, new_targs) - dump(">>> new type hash: %s", h) - if subst.check != nil { - if named, found := subst.check.typMap[h]; found { - dump(">>> found %s", named) - subst.cache[t] = named - return named + // already instantiated + dump(">>> %s already instantiated", t) + // For each (existing) type argument targ, determine if it needs + // to be substituted; i.e., if it is or contains a type parameter + // that has a type argument for it. + for i, targ := range t.TypeArgs().list() { + dump(">>> %d targ = %s", i, targ) + new_targ := subst.typ(targ) + if new_targ != targ { + dump(">>> substituted %d targ %s => %s", i, targ, new_targ) + if newTArgs == nil { + newTArgs = make([]Type, n) + copy(newTArgs, t.TypeArgs().list()) + } + newTArgs[i] = new_targ } } - // create a new named type and populate caches to avoid endless recursion - tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) - named := subst.check.newNamed(tname, t, t.underlying, t.tparams, t.methods) // method signatures are updated lazily - named.targs = new_targs - if subst.check != nil { - subst.check.typMap[h] = named + if newTArgs == nil { + dump(">>> nothing to substitute in %s", t) + return t // nothing to substitute } - subst.cache[t] = named - - // do the substitution - dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs) - named.underlying = subst.typOrNil(t.underlying) - named.fromRHS = named.underlying // for cycle detection (Checker.validType) - return named + // Create a new instance and populate the context to avoid endless + // recursion. The position used here is irrelevant because validation only + // occurs on t (we don't call validType on named), but we use subst.pos to + // help with debugging. + return subst.check.instance(subst.pos, orig, newTArgs, subst.expanding, subst.ctxt) case *TypeParam: return subst.smap.lookup(t) - case *instance: - // TODO(gri) can we avoid the expansion here and just substitute the type parameters? - return subst.typ(t.expand()) - default: unimplemented() } @@ -420,37 +268,6 @@ func (subst *subster) typ(typ Type) Type { return typ } -// TODO(gri) Eventually, this should be more sophisticated. -// It won't work correctly for locally declared types. -func instantiatedHash(typ *Named, targs []Type) string { - var buf bytes.Buffer - writeTypeName(&buf, typ.obj, nil) - buf.WriteByte('[') - writeTypeList(&buf, targs, nil, nil) - buf.WriteByte(']') - - // With respect to the represented type, whether a - // type is fully expanded or stored as instance - // does not matter - they are the same types. - // Remove the instanceMarkers printed for instances. - res := buf.Bytes() - i := 0 - for _, b := range res { - if b != instanceMarker { - res[i] = b - i++ - } - } - - return string(res[:i]) -} - -func typeListString(list []Type) string { - var buf bytes.Buffer - writeTypeList(&buf, list, nil, nil) - return buf.String() -} - // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) // where an array/slice element is accessed before it is set up. @@ -464,14 +281,19 @@ func (subst *subster) typOrNil(typ Type) Type { func (subst *subster) var_(v *Var) *Var { if v != nil { if typ := subst.typ(v.typ); typ != v.typ { - copy := *v - copy.typ = typ - return © + return substVar(v, typ) } } return v } +func substVar(v *Var, typ Type) *Var { + copy := *v + copy.typ = typ + copy.origin = v.Origin() + return © +} + func (subst *subster) tuple(t *Tuple) *Tuple { if t != nil { if vars, copied := subst.varList(t.vars); copied { @@ -502,14 +324,19 @@ func (subst *subster) varList(in []*Var) (out []*Var, copied bool) { func (subst *subster) func_(f *Func) *Func { if f != nil { if typ := subst.typ(f.typ); typ != f.typ { - copy := *f - copy.typ = typ - return © + return substFunc(f, typ) } } return f } +func substFunc(f *Func, typ Type) *Func { + copy := *f + copy.typ = typ + copy.origin = f.Origin() + return © +} + func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) { out = in for i, f := range in { @@ -545,3 +372,48 @@ func (subst *subster) typeList(in []Type) (out []Type, copied bool) { } return } + +func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) { + out = in + for i, t := range in { + if u := subst.typ(t.typ); u != t.typ { + if !copied { + // first function that got substituted => allocate new out slice + // and copy all functions + new := make([]*Term, len(in)) + copy(new, out) + out = new + copied = true + } + out[i] = NewTerm(t.tilde, u) + } + } + return +} + +// replaceRecvType updates any function receivers that have type old to have +// type new. It does not modify the input slice; if modifications are required, +// the input slice and any affected signatures will be copied before mutating. +// +// The resulting out slice contains the updated functions, and copied reports +// if anything was modified. +func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { + out = in + for i, method := range in { + sig := method.Type().(*Signature) + if sig.recv != nil && sig.recv.Type() == old { + if !copied { + // Allocate a new methods slice before mutating for the first time. + // This is defensive, as we may share methods across instantiations of + // a given interface type if they do not get substituted. + out = make([]*Func, len(in)) + copy(out, in) + copied = true + } + newsig := *sig + newsig.recv = substVar(sig.recv, new) + out[i] = substFunc(method, &newsig) + } + } + return +} diff --git a/src/cmd/compile/internal/types2/termlist.go b/src/cmd/compile/internal/types2/termlist.go new file mode 100644 index 00000000000000..196f8abf724f94 --- /dev/null +++ b/src/cmd/compile/internal/types2/termlist.go @@ -0,0 +1,161 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import "strings" + +// A termlist represents the type set represented by the union +// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. +// A termlist is in normal form if all terms are disjoint. +// termlist operations don't require the operands to be in +// normal form. +type termlist []*term + +// allTermlist represents the set of all types. +// It is in normal form. +var allTermlist = termlist{new(term)} + +// termSep is the separator used between individual terms. +const termSep = " | " + +// String prints the termlist exactly (without normalization). +func (xl termlist) String() string { + if len(xl) == 0 { + return "∅" + } + var buf strings.Builder + for i, x := range xl { + if i > 0 { + buf.WriteString(termSep) + } + buf.WriteString(x.String()) + } + return buf.String() +} + +// isEmpty reports whether the termlist xl represents the empty set of types. +func (xl termlist) isEmpty() bool { + // If there's a non-nil term, the entire list is not empty. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil { + return false + } + } + return true +} + +// isAll reports whether the termlist xl represents the set of all types. +func (xl termlist) isAll() bool { + // If there's a 𝓤 term, the entire list is 𝓤. + // If the termlist is in normal form, this requires at most + // one iteration. + for _, x := range xl { + if x != nil && x.typ == nil { + return true + } + } + return false +} + +// norm returns the normal form of xl. +func (xl termlist) norm() termlist { + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + used := make([]bool, len(xl)) + var rl termlist + for i, xi := range xl { + if xi == nil || used[i] { + continue + } + for j := i + 1; j < len(xl); j++ { + xj := xl[j] + if xj == nil || used[j] { + continue + } + if u1, u2 := xi.union(xj); u2 == nil { + // If we encounter a 𝓤 term, the entire list is 𝓤. + // Exit early. + // (Note that this is not just an optimization; + // if we continue, we may end up with a 𝓤 term + // and other terms and the result would not be + // in normal form.) + if u1.typ == nil { + return allTermlist + } + xi = u1 + used[j] = true // xj is now unioned into xi - ignore it in future iterations + } + } + rl = append(rl, xi) + } + return rl +} + +// union returns the union xl ∪ yl. +func (xl termlist) union(yl termlist) termlist { + return append(xl, yl...).norm() +} + +// intersect returns the intersection xl ∩ yl. +func (xl termlist) intersect(yl termlist) termlist { + if xl.isEmpty() || yl.isEmpty() { + return nil + } + + // Quadratic algorithm, but good enough for now. + // TODO(gri) fix asymptotic performance + var rl termlist + for _, x := range xl { + for _, y := range yl { + if r := x.intersect(y); r != nil { + rl = append(rl, r) + } + } + } + return rl.norm() +} + +// equal reports whether xl and yl represent the same type set. +func (xl termlist) equal(yl termlist) bool { + // TODO(gri) this should be more efficient + return xl.subsetOf(yl) && yl.subsetOf(xl) +} + +// includes reports whether t ∈ xl. +func (xl termlist) includes(t Type) bool { + for _, x := range xl { + if x.includes(t) { + return true + } + } + return false +} + +// supersetOf reports whether y ⊆ xl. +func (xl termlist) supersetOf(y *term) bool { + for _, x := range xl { + if y.subsetOf(x) { + return true + } + } + return false +} + +// subsetOf reports whether xl ⊆ yl. +func (xl termlist) subsetOf(yl termlist) bool { + if yl.isEmpty() { + return xl.isEmpty() + } + + // each term x of xl must be a subset of yl + for _, x := range xl { + if !yl.supersetOf(x) { + return false // x is not a subset yl + } + } + return true +} diff --git a/src/cmd/compile/internal/types2/termlist_test.go b/src/cmd/compile/internal/types2/termlist_test.go new file mode 100644 index 00000000000000..3005d0edea00ca --- /dev/null +++ b/src/cmd/compile/internal/types2/termlist_test.go @@ -0,0 +1,284 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "strings" + "testing" +) + +// maketl makes a term list from a string of the term list. +func maketl(s string) termlist { + s = strings.ReplaceAll(s, " ", "") + names := strings.Split(s, "|") + r := make(termlist, len(names)) + for i, n := range names { + r[i] = testTerm(n) + } + return r +} + +func TestTermlistAll(t *testing.T) { + if !allTermlist.isAll() { + t.Errorf("allTermlist is not the set of all types") + } +} + +func TestTermlistString(t *testing.T) { + for _, want := range []string{ + "∅", + "𝓤", + "int", + "~int", + "myInt", + "∅ | ∅", + "𝓤 | 𝓤", + "∅ | 𝓤 | int", + "∅ | 𝓤 | int | myInt", + } { + if got := maketl(want).String(); got != want { + t.Errorf("(%v).String() == %v", want, got) + } + } +} + +func TestTermlistIsEmpty(t *testing.T) { + for test, want := range map[string]bool{ + "∅": true, + "∅ | ∅": true, + "∅ | ∅ | 𝓤": false, + "∅ | ∅ | myInt": false, + "𝓤": false, + "𝓤 | int": false, + "𝓤 | myInt | ∅": false, + } { + xl := maketl(test) + got := xl.isEmpty() + if got != want { + t.Errorf("(%v).isEmpty() == %v; want %v", test, got, want) + } + } +} + +func TestTermlistIsAll(t *testing.T) { + for test, want := range map[string]bool{ + "∅": false, + "∅ | ∅": false, + "int | ~string": false, + "~int | myInt": false, + "∅ | ∅ | 𝓤": true, + "𝓤": true, + "𝓤 | int": true, + "myInt | 𝓤": true, + } { + xl := maketl(test) + got := xl.isAll() + if got != want { + t.Errorf("(%v).isAll() == %v; want %v", test, got, want) + } + } +} + +func TestTermlistNorm(t *testing.T) { + for _, test := range []struct { + xl, want string + }{ + {"∅", "∅"}, + {"∅ | ∅", "∅"}, + {"∅ | int", "int"}, + {"∅ | myInt", "myInt"}, + {"𝓤 | int", "𝓤"}, + {"𝓤 | myInt", "𝓤"}, + {"int | myInt", "int | myInt"}, + {"~int | int", "~int"}, + {"~int | myInt", "~int"}, + {"int | ~string | int", "int | ~string"}, + {"~int | string | 𝓤 | ~string | int", "𝓤"}, + {"~int | string | myInt | ~string | int", "~int | ~string"}, + } { + xl := maketl(test.xl) + got := maketl(test.xl).norm() + if got.String() != test.want { + t.Errorf("(%v).norm() = %v; want %v", xl, got, test.want) + } + } +} + +func TestTermlistUnion(t *testing.T) { + for _, test := range []struct { + xl, yl, want string + }{ + + {"∅", "∅", "∅"}, + {"∅", "𝓤", "𝓤"}, + {"∅", "int", "int"}, + {"𝓤", "~int", "𝓤"}, + {"int", "~int", "~int"}, + {"int", "string", "int | string"}, + {"int", "myInt", "int | myInt"}, + {"~int", "myInt", "~int"}, + {"int | string", "~string", "int | ~string"}, + {"~int | string", "~string | int", "~int | ~string"}, + {"~int | string | ∅", "~string | int", "~int | ~string"}, + {"~int | myInt | ∅", "~string | int", "~int | ~string"}, + {"~int | string | 𝓤", "~string | int", "𝓤"}, + {"~int | string | myInt", "~string | int", "~int | ~string"}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.union(yl).String() + if got != test.want { + t.Errorf("(%v).union(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistIntersect(t *testing.T) { + for _, test := range []struct { + xl, yl, want string + }{ + + {"∅", "∅", "∅"}, + {"∅", "𝓤", "∅"}, + {"∅", "int", "∅"}, + {"∅", "myInt", "∅"}, + {"𝓤", "~int", "~int"}, + {"𝓤", "myInt", "myInt"}, + {"int", "~int", "int"}, + {"int", "string", "∅"}, + {"int", "myInt", "∅"}, + {"~int", "myInt", "myInt"}, + {"int | string", "~string", "string"}, + {"~int | string", "~string | int", "int | string"}, + {"~int | string | ∅", "~string | int", "int | string"}, + {"~int | myInt | ∅", "~string | int", "int"}, + {"~int | string | 𝓤", "~string | int", "int | ~string"}, + {"~int | string | myInt", "~string | int", "int | string"}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.intersect(yl).String() + if got != test.want { + t.Errorf("(%v).intersect(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistEqual(t *testing.T) { + for _, test := range []struct { + xl, yl string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", false}, + {"𝓤", "𝓤", true}, + {"𝓤 | int", "𝓤", true}, + {"𝓤 | int", "string | 𝓤", true}, + {"𝓤 | myInt", "string | 𝓤", true}, + {"int | ~string", "string | int", false}, + {"~int | string", "string | myInt", false}, + {"int | ~string | ∅", "string | int | ~string", true}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.equal(yl) + if got != test.want { + t.Errorf("(%v).equal(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} + +func TestTermlistIncludes(t *testing.T) { + for _, test := range []struct { + xl, typ string + want bool + }{ + {"∅", "int", false}, + {"𝓤", "int", true}, + {"~int", "int", true}, + {"int", "string", false}, + {"~int", "string", false}, + {"~int", "myInt", true}, + {"int | string", "string", true}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | myInt | ∅", "myInt", true}, + {"myInt | ∅ | 𝓤", "int", true}, + } { + xl := maketl(test.xl) + yl := testTerm(test.typ).typ + got := xl.includes(yl) + if got != test.want { + t.Errorf("(%v).includes(%v) = %v; want %v", test.xl, yl, got, test.want) + } + } +} + +func TestTermlistSupersetOf(t *testing.T) { + for _, test := range []struct { + xl, typ string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", false}, + {"∅", "int", false}, + {"𝓤", "∅", true}, + {"𝓤", "𝓤", true}, + {"𝓤", "int", true}, + {"𝓤", "~int", true}, + {"𝓤", "myInt", true}, + {"~int", "int", true}, + {"~int", "~int", true}, + {"~int", "myInt", true}, + {"int", "~int", false}, + {"myInt", "~int", false}, + {"int", "string", false}, + {"~int", "string", false}, + {"int | string", "string", true}, + {"int | string", "~string", false}, + {"~int | string", "int", true}, + {"~int | string", "myInt", true}, + {"~int | string | ∅", "string", true}, + {"~string | ∅ | 𝓤", "myInt", true}, + } { + xl := maketl(test.xl) + y := testTerm(test.typ) + got := xl.supersetOf(y) + if got != test.want { + t.Errorf("(%v).supersetOf(%v) = %v; want %v", test.xl, y, got, test.want) + } + } +} + +func TestTermlistSubsetOf(t *testing.T) { + for _, test := range []struct { + xl, yl string + want bool + }{ + {"∅", "∅", true}, + {"∅", "𝓤", true}, + {"𝓤", "∅", false}, + {"𝓤", "𝓤", true}, + {"int", "int | string", true}, + {"~int", "int | string", false}, + {"~int", "myInt | string", false}, + {"myInt", "~int | string", true}, + {"~int", "string | string | int | ~int", true}, + {"myInt", "string | string | ~int", true}, + {"int | string", "string", false}, + {"int | string", "string | int", true}, + {"int | ~string", "string | int", false}, + {"myInt | ~string", "string | int | 𝓤", true}, + {"int | ~string", "string | int | ∅ | string", false}, + {"int | myInt", "string | ~int | ∅ | string", true}, + } { + xl := maketl(test.xl) + yl := maketl(test.yl) + got := xl.subsetOf(yl) + if got != test.want { + t.Errorf("(%v).subsetOf(%v) = %v; want %v", test.xl, test.yl, got, test.want) + } + } +} diff --git a/src/cmd/compile/internal/types2/testdata/check/builtins.go2 b/src/cmd/compile/internal/types2/testdata/check/builtins.go2 deleted file mode 100644 index 3918d836b5277a..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/builtins.go2 +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file tests built-in calls on generic types. - -package builtins - -type Bmc interface { - type map[rune]string, chan int -} - -type Bms interface { - type map[string]int, []int -} - -type Bcs interface { - type chan bool, []float64 -} - -type Bss interface { - type []int, []string -} - -func _[T any] () { - _ = make(T /* ERROR invalid argument */ ) - _ = make(T /* ERROR invalid argument */ , 10) - _ = make(T /* ERROR invalid argument */ , 10, 20) -} - -func _[T Bmc] () { - _ = make(T) - _ = make(T, 10) - _ = make /* ERROR expects 1 or 2 arguments */ (T, 10, 20) -} - -func _[T Bms] () { - _ = make /* ERROR expects 2 arguments */ (T) - _ = make(T, 10) - _ = make /* ERROR expects 2 arguments */ (T, 10, 20) -} - -func _[T Bcs] () { - _ = make /* ERROR expects 2 arguments */ (T) - _ = make(T, 10) - _ = make /* ERROR expects 2 arguments */ (T, 10, 20) -} - -func _[T Bss] () { - _ = make /* ERROR expects 2 or 3 arguments */ (T) - _ = make(T, 10) - _ = make(T, 10, 20) -} diff --git a/src/cmd/compile/internal/types2/testdata/check/builtins.src b/src/cmd/compile/internal/types2/testdata/check/builtins.src deleted file mode 100644 index 6d1f47129b9c16..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/builtins.src +++ /dev/null @@ -1,902 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// builtin calls - -package builtins - -import "unsafe" - -func f0() {} - -func append1() { - var b byte - var x int - var s []byte - _ = append() // ERROR not enough arguments - _ = append("foo" /* ERROR not a slice */ ) - _ = append(nil /* ERROR not a slice */ , s) - _ = append(x /* ERROR not a slice */ , s) - _ = append(s) - _ = append(s, nil...) - append /* ERROR not used */ (s) - - _ = append(s, b) - _ = append(s, x /* ERROR cannot use x */ ) - _ = append(s, s /* ERROR cannot use s */ ) - _ = append(s... ) /* ERROR not enough arguments */ - _ = append(s, b, s /* ERROR too many arguments */ ... ) - _ = append(s, 1, 2, 3) - _ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6) - _ = append(s, 1, 2 /* ERROR too many arguments */ , s... ) - _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) - - type S []byte - type T string - var t T - _ = append(s, "foo" /* ERROR cannot use .* in argument to append */ ) - _ = append(s, "foo"...) - _ = append(S(s), "foo" /* ERROR cannot use .* in argument to append */ ) - _ = append(S(s), "foo"...) - _ = append(s, t /* ERROR cannot use t */ ) - _ = append(s, t...) - _ = append(s, T("foo")...) - _ = append(S(s), t /* ERROR cannot use t */ ) - _ = append(S(s), t...) - _ = append(S(s), T("foo")...) - _ = append([]string{}, t /* ERROR cannot use t */ , "foo") - _ = append([]T{}, t, "foo") -} - -// from the spec -func append2() { - s0 := []int{0, 0} - s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2} - s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7} - s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} - s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} - - var t []interface{} - t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} - - var b []byte - b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' } - - _ = s4 -} - -func append3() { - f1 := func() (s []int) { return } - f2 := func() (s []int, x int) { return } - f3 := func() (s []int, x, y int) { return } - f5 := func() (s []interface{}, x int, y float32, z string, b bool) { return } - ff := func() (int, float32) { return 0, 0 } - _ = append(f0 /* ERROR used as value */ ()) - _ = append(f1()) - _ = append(f2()) - _ = append(f3()) - _ = append(f5()) - _ = append(ff /* ERROR not a slice */ ()) // TODO(gri) better error message -} - -func cap1() { - var a [10]bool - var p *[20]int - var c chan string - _ = cap() // ERROR not enough arguments - _ = cap(1, 2) // ERROR too many arguments - _ = cap(42 /* ERROR invalid */) - const _3 = cap(a) - assert(_3 == 10) - const _4 = cap(p) - assert(_4 == 20) - _ = cap(c) - cap /* ERROR not used */ (c) - - // issue 4744 - type T struct{ a [10]int } - const _ = cap(((*T)(nil)).a) - - var s [][]byte - _ = cap(s) - _ = cap(s... /* ERROR invalid use of \.\.\. */ ) -} - -func cap2() { - f1a := func() (a [10]int) { return } - f1s := func() (s []int) { return } - f2 := func() (s []int, x int) { return } - _ = cap(f0 /* ERROR used as value */ ()) - _ = cap(f1a()) - _ = cap(f1s()) - _ = cap(f2()) // ERROR too many arguments -} - -// test cases for issue 7387 -func cap3() { - var f = func() int { return 0 } - var x = f() - const ( - _ = cap([4]int{}) - _ = cap([4]int{x}) - _ = cap /* ERROR not constant */ ([4]int{f()}) - _ = cap /* ERROR not constant */ ([4]int{cap([]int{})}) - _ = cap([4]int{cap([4]int{})}) - ) - var y float64 - var z complex128 - const ( - _ = cap([4]float64{}) - _ = cap([4]float64{y}) - _ = cap([4]float64{real(2i)}) - _ = cap /* ERROR not constant */ ([4]float64{real(z)}) - ) - var ch chan [10]int - const ( - _ = cap /* ERROR not constant */ (<-ch) - _ = cap /* ERROR not constant */ ([4]int{(<-ch)[0]}) - ) -} - -func close1() { - var c chan int - var r <-chan int - close() // ERROR not enough arguments - close(1, 2) // ERROR too many arguments - close(42 /* ERROR not a channel */) - close(r /* ERROR receive-only channel */) - close(c) - _ = close /* ERROR used as value */ (c) - - var s []chan int - close(s... /* ERROR invalid use of \.\.\. */ ) -} - -func close2() { - f1 := func() (ch chan int) { return } - f2 := func() (ch chan int, x int) { return } - close(f0 /* ERROR used as value */ ()) - close(f1()) - close(f2()) // ERROR too many arguments -} - -func complex1() { - var i32 int32 - var f32 float32 - var f64 float64 - var c64 complex64 - var c128 complex128 - _ = complex() // ERROR not enough arguments - _ = complex(1) // ERROR not enough arguments - _ = complex(true /* ERROR mismatched types */ , 0) - _ = complex(i32 /* ERROR expected floating-point */ , 0) - _ = complex("foo" /* ERROR mismatched types */ , 0) - _ = complex(c64 /* ERROR expected floating-point */ , 0) - _ = complex(0 /* ERROR mismatched types */ , true) - _ = complex(0 /* ERROR expected floating-point */ , i32) - _ = complex(0 /* ERROR mismatched types */ , "foo") - _ = complex(0 /* ERROR expected floating-point */ , c64) - _ = complex(f32, f32) - _ = complex(f32, 1) - _ = complex(f32, 1.0) - _ = complex(f32, 'a') - _ = complex(f64, f64) - _ = complex(f64, 1) - _ = complex(f64, 1.0) - _ = complex(f64, 'a') - _ = complex(f32 /* ERROR mismatched types */ , f64) - _ = complex(f64 /* ERROR mismatched types */ , f32) - _ = complex(1, 1) - _ = complex(1, 1.1) - _ = complex(1, 'a') - complex /* ERROR not used */ (1, 2) - - var _ complex64 = complex(f32, f32) - var _ complex64 = complex /* ERROR cannot use .* in variable declaration */ (f64, f64) - - var _ complex128 = complex /* ERROR cannot use .* in variable declaration */ (f32, f32) - var _ complex128 = complex(f64, f64) - - // untyped constants - const _ int = complex(1, 0) - const _ float32 = complex(1, 0) - const _ complex64 = complex(1, 0) - const _ complex128 = complex(1, 0) - const _ = complex(0i, 0i) - const _ = complex(0i, 0) - const _ int = 1.0 + complex(1, 0i) - - const _ int = complex /* ERROR int */ (1.1, 0) - const _ float32 = complex /* ERROR float32 */ (1, 2) - - // untyped values - var s uint - _ = complex(1 /* ERROR integer */ <X, T2->X - ) - - var t T3 - _ = t.X /* ERROR "ambiguous selector t.X" */ -} - -func _() { - type ( - T1 struct { X int } - T2 struct { T1 } - T3 struct { T1 } - T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X - ) - - var t T4 - _ = t.X /* ERROR "ambiguous selector t.X" */ -} - -func issue4355() { - type ( - T1 struct {X int} - T2 struct {T1} - T3 struct {T2} - T4 struct {T2} - T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X - ) - - var t T5 - _ = t.X /* ERROR "ambiguous selector t.X" */ -} - -func _() { - type State int - type A struct{ State } - type B struct{ fmt.State } - type T struct{ A; B } - - var t T - _ = t.State /* ERROR "ambiguous selector t.State" */ -} - -// Embedded fields can be predeclared types. - -func _() { - type T0 struct{ - int - float32 - f int - } - var x T0 - _ = x.int - _ = x.float32 - _ = x.f - - type T1 struct{ - T0 - } - var y T1 - _ = y.int - _ = y.float32 - _ = y.f -} - -// Restrictions on embedded field types. - -func _() { - type I1 interface{} - type I2 interface{} - type P1 *int - type P2 *int - type UP unsafe.Pointer - - type T1 struct { - I1 - * /* ERROR "cannot be a pointer to an interface" */ I2 - * /* ERROR "cannot be a pointer to an interface" */ error - P1 /* ERROR "cannot be a pointer" */ - * /* ERROR "cannot be a pointer" */ P2 - } - - // unsafe.Pointers are treated like regular pointers when embedded - type T2 struct { - unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer - */* ERROR "cannot be unsafe.Pointer" */ /* ERROR "Pointer redeclared" */ unsafe.Pointer - UP /* ERROR "cannot be unsafe.Pointer" */ - * /* ERROR "cannot be unsafe.Pointer" */ /* ERROR "UP redeclared" */ UP - } -} - -// Named types that are pointers. - -type S struct{ x int } -func (*S) m() {} -type P *S - -func _() { - var s *S - _ = s.x - _ = s.m - - var p P - _ = p.x - _ = p.m /* ERROR "no field or method" */ - _ = P.m /* ERROR "no field or method" */ -} - -// Borrowed from the FieldByName test cases in reflect/all_test.go. - -type D1 struct { - d int -} -type D2 struct { - d int -} - -type S0 struct { - A, B, C int - D1 - D2 -} - -type S1 struct { - B int - S0 -} - -type S2 struct { - A int - *S1 -} - -type S1x struct { - S1 -} - -type S1y struct { - S1 -} - -type S3 struct { - S1x - S2 - D, E int - *S1y -} - -type S4 struct { - *S4 - A int -} - -// The X in S6 and S7 annihilate, but they also block the X in S8.S9. -type S5 struct { - S6 - S7 - S8 -} - -type S6 struct { - X int -} - -type S7 S6 - -type S8 struct { - S9 -} - -type S9 struct { - X int - Y int -} - -// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. -type S10 struct { - S11 - S12 - S13 -} - -type S11 struct { - S6 -} - -type S12 struct { - S6 -} - -type S13 struct { - S8 -} - -func _() { - _ = struct{}{}.Foo /* ERROR "no field or method" */ - _ = S0{}.A - _ = S0{}.D /* ERROR "no field or method" */ - _ = S1{}.A - _ = S1{}.B - _ = S1{}.S0 - _ = S1{}.C - _ = S2{}.A - _ = S2{}.S1 - _ = S2{}.B - _ = S2{}.C - _ = S2{}.D /* ERROR "no field or method" */ - _ = S3{}.S1 /* ERROR "ambiguous selector S3\{\}.S1" */ - _ = S3{}.A - _ = S3{}.B /* ERROR "ambiguous selector" S3\{\}.B */ - _ = S3{}.D - _ = S3{}.E - _ = S4{}.A - _ = S4{}.B /* ERROR "no field or method" */ - _ = S5{}.X /* ERROR "ambiguous selector S5\{\}.X" */ - _ = S5{}.Y - _ = S10{}.X /* ERROR "ambiguous selector S10\{\}.X" */ - _ = S10{}.Y -} - -// Borrowed from the FieldByName benchmark in reflect/all_test.go. - -type R0 struct { - *R1 - *R2 - *R3 - *R4 -} - -type R1 struct { - *R5 - *R6 - *R7 - *R8 -} - -type R2 R1 -type R3 R1 -type R4 R1 - -type R5 struct { - *R9 - *R10 - *R11 - *R12 -} - -type R6 R5 -type R7 R5 -type R8 R5 - -type R9 struct { - *R13 - *R14 - *R15 - *R16 -} - -type R10 R9 -type R11 R9 -type R12 R9 - -type R13 struct { - *R17 - *R18 - *R19 - *R20 -} - -type R14 R13 -type R15 R13 -type R16 R13 - -type R17 struct { - *R21 - *R22 - *R23 - *R24 -} - -type R18 R17 -type R19 R17 -type R20 R17 - -type R21 struct { - X int -} - -type R22 R21 -type R23 R21 -type R24 R21 - -var _ = R0{}.X /* ERROR "ambiguous selector R0\{\}.X" */ \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/decls4.src b/src/cmd/compile/internal/types2/testdata/check/decls4.src deleted file mode 100644 index eb08421beee001..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/decls4.src +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// type aliases - -package decls4 - -type ( - T0 [10]int - T1 []byte - T2 struct { - x int - } - T3 interface{ - m() T2 - } - T4 func(int, T0) chan T2 -) - -type ( - Ai = int - A0 = T0 - A1 = T1 - A2 = T2 - A3 = T3 - A4 = T4 - - A10 = [10]int - A11 = []byte - A12 = struct { - x int - } - A13 = interface{ - m() A2 - } - A14 = func(int, A0) chan A2 -) - -// check assignment compatibility due to equality of types -var ( - xi_ int - ai Ai = xi_ - - x0 T0 - a0 A0 = x0 - - x1 T1 - a1 A1 = x1 - - x2 T2 - a2 A2 = x2 - - x3 T3 - a3 A3 = x3 - - x4 T4 - a4 A4 = x4 -) - -// alias receiver types -func (Ai /* ERROR "invalid receiver" */) m1() {} -func (T0) m1() {} -func (A0) m1 /* ERROR already declared */ () {} -func (A0) m2 () {} -func (A3 /* ERROR invalid receiver */ ) m1 () {} -func (A10 /* ERROR invalid receiver */ ) m1() {} - -// x0 has methods m1, m2 declared via receiver type names T0 and A0 -var _ interface{ m1(); m2() } = x0 - -// alias receiver types (test case for issue #23042) -type T struct{} - -var ( - _ = T.m - _ = T{}.m - _ interface{m()} = T{} -) - -var ( - _ = T.n - _ = T{}.n - _ interface{m(); n()} = T{} -) - -type U = T -func (U) m() {} - -// alias receiver types (long type declaration chains) -type ( - V0 = V1 - V1 = (V2) - V2 = ((V3)) - V3 = T -) - -func (V0) m /* ERROR already declared */ () {} -func (V1) n() {} - -// alias receiver types (invalid due to cycles) -type ( - W0 /* ERROR illegal cycle */ = W1 - W1 = (W2) - W2 = ((W0)) -) - -func (W0) m() {} // no error expected (due to above cycle error) -func (W1) n() {} - -// alias receiver types (invalid due to builtin underlying type) -type ( - B0 = B1 - B1 = B2 - B2 = int -) - -func (B0 /* ERROR invalid receiver */ ) m() {} -func (B1 /* ERROR invalid receiver */ ) n() {} - -// cycles -type ( - C2 /* ERROR illegal cycle */ = C2 - C3 /* ERROR illegal cycle */ = C4 - C4 = C3 - C5 struct { - f *C6 - } - C6 = C5 - C7 /* ERROR illegal cycle */ struct { - f C8 - } - C8 = C7 -) - -// embedded fields -var ( - s0 struct { T0 } - s1 struct { A0 } = s0 /* ERROR cannot use */ // embedded field names are different -) - -// embedding and lookup of fields and methods -func _(s struct{A0}) { s.A0 = x0 } - -type eX struct{xf int} - -func (eX) xm() - -type eY = struct{eX} // field/method set of eY includes xf, xm - -type eZ = *struct{eX} // field/method set of eZ includes xf, xm - -type eA struct { - eX // eX contributes xf, xm to eA -} - -type eA2 struct { - *eX // *eX contributes xf, xm to eA -} - -type eB struct { - eY // eY contributes xf, xm to eB -} - -type eB2 struct { - *eY // *eY contributes xf, xm to eB -} - -type eC struct { - eZ // eZ contributes xf, xm to eC -} - -var ( - _ = eA{}.xf - _ = eA{}.xm - _ = eA2{}.xf - _ = eA2{}.xm - _ = eB{}.xf - _ = eB{}.xm - _ = eB2{}.xf - _ = eB2{}.xm - _ = eC{}.xf - _ = eC{}.xm -) - -// ambiguous selectors due to embedding via type aliases -type eD struct { - eY - eZ -} - -var ( - _ = eD{}.xf /* ERROR ambiguous selector eD\{\}.xf */ - _ = eD{}.xm /* ERROR ambiguous selector eD\{\}.xm */ -) - -var ( - _ interface{ xm() } = eD /* ERROR missing method xm */ {} -) \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/errors.src b/src/cmd/compile/internal/types2/testdata/check/errors.src deleted file mode 100644 index ff929217c4c865..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/errors.src +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package errors - -// Testing precise operand formatting in error messages -// (matching messages are regular expressions, hence the \'s). -func f(x int, m map[string]int) { - // no values - _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m) - - // built-ins - _ = println /* ERROR "println \(built-in\) must be called" */ - - // types - _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */ - - // constants - const c1 = 991 - const c2 float32 = 0.5 - 0 /* ERROR "0 \(untyped int constant\) is not used" */ - c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */ - c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */ - c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2 - - // variables - x /* ERROR "x \(variable of type int\) is not used" */ - - // values - x /* ERROR "x != x \(untyped bool value\) is not used" */ != x - x /* ERROR "x \+ x \(value of type int\) is not used" */ + x - - // value, ok's - const s = "foo" - m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s] -} - -// Valid ERROR comments can have a variety of forms. -func _() { - 0 /* ERROR "0 .* is not used" */ - 0 /* ERROR 0 .* is not used */ - 0 // ERROR "0 .* is not used" - 0 // ERROR 0 .* is not used -} - -// Don't report spurious errors as a consequence of earlier errors. -// Add more tests as needed. -func _() { - if err := foo /* ERROR undeclared */ (); err != nil /* no error here */ {} -} - -// Use unqualified names for package-local objects. -type T struct{} -var _ int = T /* ERROR value of type T */ {} // use T in error message rather then errors.T - -// Don't report errors containing "invalid type" (issue #24182). -func _(x *missing /* ERROR undeclared name: missing */ ) { - x.m() // there shouldn't be an error here referring to *invalid type -} diff --git a/src/cmd/compile/internal/types2/testdata/check/expr0.src b/src/cmd/compile/internal/types2/testdata/check/expr0.src deleted file mode 100644 index 1aac726327b282..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/expr0.src +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// unary expressions - -package expr0 - -type mybool bool - -var ( - // bool - b0 = true - b1 bool = b0 - b2 = !true - b3 = !b1 - b4 bool = !true - b5 bool = !b4 - b6 = +b0 /* ERROR "not defined" */ - b7 = -b0 /* ERROR "not defined" */ - b8 = ^b0 /* ERROR "not defined" */ - b9 = *b0 /* ERROR "cannot indirect" */ - b10 = &true /* ERROR "cannot take address" */ - b11 = &b0 - b12 = <-b0 /* ERROR "cannot receive" */ - b13 = & & /* ERROR "cannot take address" */ b0 - - // byte - _ = byte(0) - _ = byte(- /* ERROR "cannot convert" */ 1) - _ = - /* ERROR "-byte\(1\) \(constant -1 of type byte\) overflows byte" */ byte(1) // test for issue 11367 - _ = byte /* ERROR "overflows byte" */ (0) - byte(1) - - // int - i0 = 1 - i1 int = i0 - i2 = +1 - i3 = +i0 - i4 int = +1 - i5 int = +i4 - i6 = -1 - i7 = -i0 - i8 int = -1 - i9 int = -i4 - i10 = !i0 /* ERROR "not defined" */ - i11 = ^1 - i12 = ^i0 - i13 int = ^1 - i14 int = ^i4 - i15 = *i0 /* ERROR "cannot indirect" */ - i16 = &i0 - i17 = *i16 - i18 = <-i16 /* ERROR "cannot receive" */ - - // uint - u0 = uint(1) - u1 uint = u0 - u2 = +1 - u3 = +u0 - u4 uint = +1 - u5 uint = +u4 - u6 = -1 - u7 = -u0 - u8 uint = - /* ERROR "overflows" */ 1 - u9 uint = -u4 - u10 = !u0 /* ERROR "not defined" */ - u11 = ^1 - u12 = ^i0 - u13 uint = ^ /* ERROR "overflows" */ 1 - u14 uint = ^u4 - u15 = *u0 /* ERROR "cannot indirect" */ - u16 = &u0 - u17 = *u16 - u18 = <-u16 /* ERROR "cannot receive" */ - u19 = ^uint(0) - - // float64 - f0 = float64(1) - f1 float64 = f0 - f2 = +1 - f3 = +f0 - f4 float64 = +1 - f5 float64 = +f4 - f6 = -1 - f7 = -f0 - f8 float64 = -1 - f9 float64 = -f4 - f10 = !f0 /* ERROR "not defined" */ - f11 = ^1 - f12 = ^i0 - f13 float64 = ^1 - f14 float64 = ^f4 /* ERROR "not defined" */ - f15 = *f0 /* ERROR "cannot indirect" */ - f16 = &f0 - f17 = *u16 - f18 = <-u16 /* ERROR "cannot receive" */ - - // complex128 - c0 = complex128(1) - c1 complex128 = c0 - c2 = +1 - c3 = +c0 - c4 complex128 = +1 - c5 complex128 = +c4 - c6 = -1 - c7 = -c0 - c8 complex128 = -1 - c9 complex128 = -c4 - c10 = !c0 /* ERROR "not defined" */ - c11 = ^1 - c12 = ^i0 - c13 complex128 = ^1 - c14 complex128 = ^c4 /* ERROR "not defined" */ - c15 = *c0 /* ERROR "cannot indirect" */ - c16 = &c0 - c17 = *u16 - c18 = <-u16 /* ERROR "cannot receive" */ - - // string - s0 = "foo" - s1 = +"foo" /* ERROR "not defined" */ - s2 = -s0 /* ERROR "not defined" */ - s3 = !s0 /* ERROR "not defined" */ - s4 = ^s0 /* ERROR "not defined" */ - s5 = *s4 - s6 = &s4 - s7 = *s6 - s8 = <-s7 - - // channel - ch chan int - rc <-chan float64 - sc chan <- string - ch0 = +ch /* ERROR "not defined" */ - ch1 = -ch /* ERROR "not defined" */ - ch2 = !ch /* ERROR "not defined" */ - ch3 = ^ch /* ERROR "not defined" */ - ch4 = *ch /* ERROR "cannot indirect" */ - ch5 = &ch - ch6 = *ch5 - ch7 = <-ch - ch8 = <-rc - ch9 = <-sc /* ERROR "cannot receive" */ - ch10, ok = <-ch - // ok is of type bool - ch11, myok = <-ch - _ mybool = myok /* ERROR "cannot use .* in variable declaration" */ -) - -// address of composite literals -type T struct{x, y int} - -func f() T { return T{} } - -var ( - _ = &T{1, 2} - _ = &[...]int{} - _ = &[]int{} - _ = &[]int{} - _ = &map[string]T{} - _ = &(T{1, 2}) - _ = &((((T{1, 2})))) - _ = &f /* ERROR "cannot take address" */ () -) - -// recursive pointer types -type P *P - -var ( - p1 P = new(P) - p2 P = *p1 - p3 P = &p2 -) - -func g() (a, b int) { return } - -func _() { - _ = -g /* ERROR 2-valued g */ () - _ = <-g /* ERROR 2-valued g */ () -} diff --git a/src/cmd/compile/internal/types2/testdata/check/expr1.src b/src/cmd/compile/internal/types2/testdata/check/expr1.src deleted file mode 100644 index 4ead815158f61e..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/expr1.src +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// binary expressions - -package expr1 - -type mybool bool - -func _(x, y bool, z mybool) { - x = x || y - x = x || true - x = x || false - x = x && y - x = x && true - x = x && false - - z = z /* ERROR mismatched types */ || y - z = z || true - z = z || false - z = z /* ERROR mismatched types */ && y - z = z && true - z = z && false -} - -type myint int - -func _(x, y int, z myint) { - x = x + 1 - x = x + 1.0 - x = x + 1.1 // ERROR truncated to int - x = x + y - x = x - y - x = x * y - x = x / y - x = x % y - x = x << y - x = x >> y - - z = z + 1 - z = z + 1.0 - z = z + 1.1 // ERROR truncated to int - z = z /* ERROR mismatched types */ + y - z = z /* ERROR mismatched types */ - y - z = z /* ERROR mismatched types */ * y - z = z /* ERROR mismatched types */ / y - z = z /* ERROR mismatched types */ % y - z = z << y - z = z >> y -} - -type myuint uint - -func _(x, y uint, z myuint) { - x = x + 1 - x = x + - /* ERROR overflows uint */ 1 - x = x + 1.0 - x = x + 1.1 // ERROR truncated to uint - x = x + y - x = x - y - x = x * y - x = x / y - x = x % y - x = x << y - x = x >> y - - z = z + 1 - z = x + - /* ERROR overflows uint */ 1 - z = z + 1.0 - z = z + 1.1 // ERROR truncated to uint - z = z /* ERROR mismatched types */ + y - z = z /* ERROR mismatched types */ - y - z = z /* ERROR mismatched types */ * y - z = z /* ERROR mismatched types */ / y - z = z /* ERROR mismatched types */ % y - z = z << y - z = z >> y -} - -type myfloat64 float64 - -func _(x, y float64, z myfloat64) { - x = x + 1 - x = x + -1 - x = x + 1.0 - x = x + 1.1 - x = x + y - x = x - y - x = x * y - x = x / y - x = x /* ERROR not defined */ % y - x = x /* ERROR operand x .* must be integer */ << y - x = x /* ERROR operand x .* must be integer */ >> y - - z = z + 1 - z = z + -1 - z = z + 1.0 - z = z + 1.1 - z = z /* ERROR mismatched types */ + y - z = z /* ERROR mismatched types */ - y - z = z /* ERROR mismatched types */ * y - z = z /* ERROR mismatched types */ / y - z = z /* ERROR mismatched types */ % y - z = z /* ERROR operand z .* must be integer */ << y - z = z /* ERROR operand z .* must be integer */ >> y -} - -type mystring string - -func _(x, y string, z mystring) { - x = x + "foo" - x = x /* ERROR not defined */ - "foo" - x = x + 1 // ERROR cannot convert - x = x + y - x = x /* ERROR not defined */ - y - x = x * 10 // ERROR cannot convert -} - -func f() (a, b int) { return } - -func _(x int) { - _ = f /* ERROR 2-valued f */ () + 1 - _ = x + f /* ERROR 2-valued f */ () - _ = f /* ERROR 2-valued f */ () + f - _ = f /* ERROR 2-valued f */ () + f /* ERROR 2-valued f */ () -} diff --git a/src/cmd/compile/internal/types2/testdata/check/expr2.src b/src/cmd/compile/internal/types2/testdata/check/expr2.src deleted file mode 100644 index 0c959e8011944f..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/expr2.src +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// comparisons - -package expr2 - -func _bool() { - const t = true == true - const f = true == false - _ = t /* ERROR "cannot compare" */ < f - _ = 0 /* ERROR "cannot convert" */ == t - var b bool - var x, y float32 - b = x < y - _ = b - _ = struct{b bool}{x < y} -} - -// corner cases -var ( - v0 = nil /* ERROR "cannot compare" */ == nil -) - -func arrays() { - // basics - var a, b [10]int - _ = a == b - _ = a != b - _ = a /* ERROR < not defined */ < b - _ = a == nil /* ERROR cannot convert */ - - type C [10]int - var c C - _ = a == c - - type D [10]int - var d D - _ = c /* ERROR mismatched types */ == d - - var e [10]func() int - _ = e /* ERROR == not defined */ == e -} - -func structs() { - // basics - var s, t struct { - x int - a [10]float32 - _ bool - } - _ = s == t - _ = s != t - _ = s /* ERROR < not defined */ < t - _ = s == nil /* ERROR cannot convert */ - - type S struct { - x int - a [10]float32 - _ bool - } - type T struct { - x int - a [10]float32 - _ bool - } - var ss S - var tt T - _ = s == ss - _ = ss /* ERROR mismatched types */ == tt - - var u struct { - x int - a [10]map[string]int - } - _ = u /* ERROR cannot compare */ == u -} - -func pointers() { - // nil - _ = nil /* ERROR == not defined */ == nil - _ = nil /* ERROR != not defined */ != nil - _ = nil /* ERROR < not defined */ < nil - _ = nil /* ERROR <= not defined */ <= nil - _ = nil /* ERROR > not defined */ > nil - _ = nil /* ERROR >= not defined */ >= nil - - // basics - var p, q *int - _ = p == q - _ = p != q - - _ = p == nil - _ = p != nil - _ = nil == q - _ = nil != q - - _ = p /* ERROR < not defined */ < q - _ = p /* ERROR <= not defined */ <= q - _ = p /* ERROR > not defined */ > q - _ = p /* ERROR >= not defined */ >= q - - // various element types - type ( - S1 struct{} - S2 struct{} - P1 *S1 - P2 *S2 - ) - var ( - ps1 *S1 - ps2 *S2 - p1 P1 - p2 P2 - ) - _ = ps1 == ps1 - _ = ps1 /* ERROR mismatched types */ == ps2 - _ = ps2 /* ERROR mismatched types */ == ps1 - - _ = p1 == p1 - _ = p1 /* ERROR mismatched types */ == p2 - - _ = p1 == ps1 -} - -func channels() { - // basics - var c, d chan int - _ = c == d - _ = c != d - _ = c == nil - _ = c /* ERROR < not defined */ < d - - // various element types (named types) - type ( - C1 chan int - C1r <-chan int - C1s chan<- int - C2 chan float32 - ) - var ( - c1 C1 - c1r C1r - c1s C1s - c1a chan int - c2 C2 - ) - _ = c1 == c1 - _ = c1 /* ERROR mismatched types */ == c1r - _ = c1 /* ERROR mismatched types */ == c1s - _ = c1r /* ERROR mismatched types */ == c1s - _ = c1 == c1a - _ = c1a == c1 - _ = c1 /* ERROR mismatched types */ == c2 - _ = c1a /* ERROR mismatched types */ == c2 - - // various element types (unnamed types) - var ( - d1 chan int - d1r <-chan int - d1s chan<- int - d1a chan<- int - d2 chan float32 - ) - _ = d1 == d1 - _ = d1 == d1r - _ = d1 == d1s - _ = d1r /* ERROR mismatched types */ == d1s - _ = d1 == d1a - _ = d1a == d1 - _ = d1 /* ERROR mismatched types */ == d2 - _ = d1a /* ERROR mismatched types */ == d2 -} - -// for interfaces test -type S1 struct{} -type S11 struct{} -type S2 struct{} -func (*S1) m() int -func (*S11) m() int -func (*S11) n() -func (*S2) m() float32 - -func interfaces() { - // basics - var i, j interface{ m() int } - _ = i == j - _ = i != j - _ = i == nil - _ = i /* ERROR < not defined */ < j - - // various interfaces - var ii interface { m() int; n() } - var k interface { m() float32 } - _ = i == ii - _ = i /* ERROR mismatched types */ == k - - // interfaces vs values - var s1 S1 - var s11 S11 - var s2 S2 - - _ = i == 0 /* ERROR cannot convert */ - _ = i /* ERROR mismatched types */ == s1 - _ = i == &s1 - _ = i == &s11 - - _ = i /* ERROR mismatched types */ == s2 - _ = i /* ERROR mismatched types */ == &s2 - - // issue #28164 - // testcase from issue - _ = interface /* ERROR cannot compare */ {}(nil) == []int(nil) - - // related cases - var e interface{} - var s []int - var x int - _ = e /* ERROR cannot compare */ == s - _ = s /* ERROR cannot compare */ == e - _ = e /* ERROR cannot compare */ < x - _ = x /* ERROR cannot compare */ < e -} - -func slices() { - // basics - var s []int - _ = s == nil - _ = s != nil - _ = s /* ERROR < not defined */ < nil - - // slices are not otherwise comparable - _ = s /* ERROR == not defined */ == s - _ = s /* ERROR < not defined */ < s -} - -func maps() { - // basics - var m map[string]int - _ = m == nil - _ = m != nil - _ = m /* ERROR < not defined */ < nil - - // maps are not otherwise comparable - _ = m /* ERROR == not defined */ == m - _ = m /* ERROR < not defined */ < m -} - -func funcs() { - // basics - var f func(int) float32 - _ = f == nil - _ = f != nil - _ = f /* ERROR < not defined */ < nil - - // funcs are not otherwise comparable - _ = f /* ERROR == not defined */ == f - _ = f /* ERROR < not defined */ < f -} diff --git a/src/cmd/compile/internal/types2/testdata/check/expr3.src b/src/cmd/compile/internal/types2/testdata/check/expr3.src deleted file mode 100644 index eab3f72c4d5ff0..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/expr3.src +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package expr3 - -import "time" - -func indexes() { - var x int - _ = 1 /* ERROR "cannot index" */ [0] - _ = x /* ERROR "cannot index" */ [0] - _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2] - - var a [10]int - _ = a[true /* ERROR "cannot convert" */ ] - _ = a["foo" /* ERROR "cannot convert" */ ] - _ = a[1.1 /* ERROR "truncated" */ ] - _ = a[1.0] - _ = a[- /* ERROR "negative" */ 1] - _ = a[- /* ERROR "negative" */ 1 :] - _ = a[: - /* ERROR "negative" */ 1] - _ = a[: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] - _ = a[0: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] - _ = a[0: /* ERROR "middle index required" */ :10] - _ = a[:10:10] - - var a0 int - a0 = a[0] - _ = a0 - var a1 int32 - a1 = a /* ERROR "cannot use .* in assignment" */ [1] - _ = a1 - - _ = a[9] - _ = a[10 /* ERROR "index .* out of bounds" */ ] - _ = a[1 /* ERROR "overflows" */ <<100] - _ = a[1<< /* ERROR "constant shift overflow" */ 1000] // no out-of-bounds follow-on error - _ = a[10:] - _ = a[:10] - _ = a[10:10] - _ = a[11 /* ERROR "index .* out of bounds" */ :] - _ = a[: 11 /* ERROR "index .* out of bounds" */ ] - _ = a[: 1 /* ERROR "overflows" */ <<100] - _ = a[:10:10] - _ = a[:11 /* ERROR "index .* out of bounds" */ :10] - _ = a[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = a[10:0:10] /* ERROR "invalid slice indices" */ - _ = a[0:10:0] /* ERROR "invalid slice indices" */ - _ = a[10:0:0] /* ERROR "invalid slice indices" */ - _ = &a /* ERROR "cannot take address" */ [:10] - - pa := &a - _ = pa[9] - _ = pa[10 /* ERROR "index .* out of bounds" */ ] - _ = pa[1 /* ERROR "overflows" */ <<100] - _ = pa[10:] - _ = pa[:10] - _ = pa[10:10] - _ = pa[11 /* ERROR "index .* out of bounds" */ :] - _ = pa[: 11 /* ERROR "index .* out of bounds" */ ] - _ = pa[: 1 /* ERROR "overflows" */ <<100] - _ = pa[:10:10] - _ = pa[:11 /* ERROR "index .* out of bounds" */ :10] - _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ] - _ = pa[10:0:10] /* ERROR "invalid slice indices" */ - _ = pa[0:10:0] /* ERROR "invalid slice indices" */ - _ = pa[10:0:0] /* ERROR "invalid slice indices" */ - _ = &pa /* ERROR "cannot take address" */ [:10] - - var b [0]int - _ = b[0 /* ERROR "index .* out of bounds" */ ] - _ = b[:] - _ = b[0:] - _ = b[:0] - _ = b[0:0] - _ = b[0:0:0] - _ = b[1 /* ERROR "index .* out of bounds" */ :0:0] - - var s []int - _ = s[- /* ERROR "negative" */ 1] - _ = s[- /* ERROR "negative" */ 1 :] - _ = s[: - /* ERROR "negative" */ 1] - _ = s[0] - _ = s[1:2] - _ = s[2:1] /* ERROR "invalid slice indices" */ - _ = s[2:] - _ = s[: 1 /* ERROR "overflows" */ <<100] - _ = s[1 /* ERROR "overflows" */ <<100 :] - _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] - _ = s[: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] - _ = s[:10:10] - _ = s[10:0:10] /* ERROR "invalid slice indices" */ - _ = s[0:10:0] /* ERROR "invalid slice indices" */ - _ = s[10:0:0] /* ERROR "invalid slice indices" */ - _ = &s /* ERROR "cannot take address" */ [:10] - - var m map[string]int - _ = m[0 /* ERROR "cannot use .* in map index" */ ] - _ = m /* ERROR "cannot slice" */ ["foo" : "bar"] - _ = m["foo"] - // ok is of type bool - type mybool bool - var ok mybool - _, ok = m["bar"] - _ = ok - _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "cannot convert" - - var t string - _ = t[- /* ERROR "negative" */ 1] - _ = t[- /* ERROR "negative" */ 1 :] - _ = t[: - /* ERROR "negative" */ 1] - _ = t /* ERROR "3-index slice of string" */ [1:2:3] - _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3] - var t0 byte - t0 = t[0] - _ = t0 - var t1 rune - t1 = t /* ERROR "cannot use .* in assignment" */ [2] - _ = t1 - _ = ("foo" + "bar")[5] - _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ] - - const c = "foo" - _ = c[- /* ERROR "negative" */ 1] - _ = c[- /* ERROR "negative" */ 1 :] - _ = c[: - /* ERROR "negative" */ 1] - var c0 byte - c0 = c[0] - _ = c0 - var c2 float32 - c2 = c /* ERROR "cannot use .* in assignment" */ [2] - _ = c[3 /* ERROR "index .* out of bounds" */ ] - _ = ""[0 /* ERROR "index .* out of bounds" */ ] - _ = c2 - - _ = s[1<<30] // no compile-time error here - - // issue 4913 - type mystring string - var ss string - var ms mystring - var i, j int - ss = "foo"[1:2] - ss = "foo"[i:j] - ms = "foo" /* ERROR "cannot use .* in assignment" */ [1:2] - ms = "foo" /* ERROR "cannot use .* in assignment" */ [i:j] - _, _ = ss, ms -} - -type T struct { - x int - y func() -} - -func (*T) m() {} - -func method_expressions() { - _ = T.a /* ERROR "no field or method" */ - _ = T.x /* ERROR "has no method" */ - _ = T.m /* ERROR "cannot call pointer method m on T" */ - _ = (*T).m - - var f func(*T) = T.m /* ERROR "cannot call pointer method m on T" */ - var g func(*T) = (*T).m - _, _ = f, g - - _ = T.y /* ERROR "has no method" */ - _ = (*T).y /* ERROR "has no method" */ -} - -func struct_literals() { - type T0 struct { - a, b, c int - } - - type T1 struct { - T0 - a, b int - u float64 - s string - } - - // keyed elements - _ = T1{} - _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ } - _ = T1{aa /* ERROR "unknown field" */ : 0} - _ = T1{1 /* ERROR "invalid field name" */ : 0} - _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10} - _ = T1{a: "foo" /* ERROR "cannot use .* in struct literal" */ } - _ = T1{c /* ERROR "unknown field" */ : 0} - _ = T1{T0: { /* ERROR "missing type" */ }} // struct literal element type may not be elided - _ = T1{T0: T0{}} - _ = T1{T0 /* ERROR "invalid field name" */ .a: 0} - - // unkeyed elements - _ = T0{1, 2, 3} - _ = T0{1, b /* ERROR "mixture" */ : 2, 3} - _ = T0{1, 2} /* ERROR "too few values" */ - _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } - _ = T0{1, "foo" /* ERROR "cannot use .* in struct literal" */, 3.4 /* ERROR "cannot use .*\(truncated\)" */} - - // invalid type - type P *struct{ - x int - } - _ = P /* ERROR "invalid composite literal type" */ {} - - // unexported fields - _ = time.Time{} - _ = time.Time{sec /* ERROR "unknown field" */ : 0} - _ = time.Time{ - 0 /* ERROR implicit assignment to unexported field wall in time.Time literal */, - 0 /* ERROR implicit assignment */ , - nil /* ERROR implicit assignment */ , - } -} - -func array_literals() { - type A0 [0]int - _ = A0{} - _ = A0{0 /* ERROR "index .* out of bounds" */} - _ = A0{0 /* ERROR "index .* out of bounds" */ : 0} - - type A1 [10]int - _ = A1{} - _ = A1{0, 1, 2} - _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ } - _ = A1{- /* ERROR "negative" */ 1: 0} - _ = A1{8: 8, 9} - _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ } - _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - _ = A1{5: 5, 6, 7, 3: 3, 4} - _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } - _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10} - _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } - _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} - _ = A1{2.0} - _ = A1{2.1 /* ERROR "truncated" */ } - _ = A1{"foo" /* ERROR "cannot use .* in array or slice literal" */ } - - // indices must be integer constants - i := 1 - const f = 2.1 - const s = "foo" - _ = A1{i /* ERROR "index i must be integer constant" */ : 0} - _ = A1{f /* ERROR "truncated" */ : 0} - _ = A1{s /* ERROR "cannot convert" */ : 0} - - a0 := [...]int{} - assert(len(a0) == 0) - - a1 := [...]int{0, 1, 2} - assert(len(a1) == 3) - var a13 [3]int - var a14 [4]int - a13 = a1 - a14 = a1 /* ERROR "cannot use .* in assignment" */ - _, _ = a13, a14 - - a2 := [...]int{- /* ERROR "negative" */ 1: 0} - _ = a2 - - a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - assert(len(a3) == 5) // somewhat arbitrary - - a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14} - assert(len(a4) == 1024) - - // composite literal element types may be elided - type T []int - _ = [10]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - a6 := [...]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - assert(len(a6) == 8) - - // recursively so - _ = [10][10]T{{}, [10]T{{}}, {{1, 2, 3}}} - - // from the spec - type Point struct { x, y float32 } - _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}} - _ = [...]Point{{1.5, -3.5}, {0, 0}} - _ = [][]int{[]int{1, 2, 3}, []int{4, 5}} - _ = [][]int{{1, 2, 3}, {4, 5}} - _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}} - _ = [...]*Point{{1.5, -3.5}, {0, 0}} -} - -func slice_literals() { - type S0 []int - _ = S0{} - _ = S0{0, 1, 2} - _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} - _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - _ = S0{- /* ERROR "negative" */ 1: 0} - _ = S0{8: 8, 9} - _ = S0{8: 8, 9, 10} - _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} - _ = S0{5: 5, 6, 7, 3: 3, 4} - _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } - _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10} - _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } - _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} - _ = S0{2.0} - _ = S0{2.1 /* ERROR "truncated" */ } - _ = S0{"foo" /* ERROR "cannot use .* in array or slice literal" */ } - - // indices must be resolved correctly - const index1 = 1 - _ = S0{index1: 1} - _ = S0{index2: 2} - _ = S0{index3 /* ERROR "undeclared name" */ : 3} - - // indices must be integer constants - i := 1 - const f = 2.1 - const s = "foo" - _ = S0{i /* ERROR "index i must be integer constant" */ : 0} - _ = S0{f /* ERROR "truncated" */ : 0} - _ = S0{s /* ERROR "cannot convert" */ : 0} - - // composite literal element types may be elided - type T []int - _ = []T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} - _ = [][]int{{1, 2, 3}, {4, 5}} - - // recursively so - _ = [][]T{{}, []T{{}}, {{1, 2, 3}}} - - // issue 17954 - type T0 *struct { s string } - _ = []T0{{}} - _ = []T0{{"foo"}} - - type T1 *struct{ int } - _ = []T1{} - _ = []T1{{0}, {1}, {2}} - - type T2 T1 - _ = []T2{} - _ = []T2{{0}, {1}, {2}} - - _ = map[T0]T2{} - _ = map[T0]T2{{}: {}} -} - -const index2 int = 2 - -type N int -func (N) f() {} - -func map_literals() { - type M0 map[string]int - type M1 map[bool]int - type M2 map[*int]int - - _ = M0{} - _ = M0{1 /* ERROR "missing key" */ } - _ = M0{1 /* ERROR "cannot use .* in map literal" */ : 2} - _ = M0{"foo": "bar" /* ERROR "cannot use .* in map literal" */ } - _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } - - _ = map[interface{}]int{2: 1, 2 /* ERROR "duplicate key" */ : 1} - _ = map[interface{}]int{int(2): 1, int16(2): 1} - _ = map[interface{}]int{int16(2): 1, int16 /* ERROR "duplicate key" */ (2): 1} - - type S string - - _ = map[interface{}]int{"a": 1, "a" /* ERROR "duplicate key" */ : 1} - _ = map[interface{}]int{"a": 1, S("a"): 1} - _ = map[interface{}]int{S("a"): 1, S /* ERROR "duplicate key" */ ("a"): 1} - _ = map[interface{}]int{1.0: 1, 1.0 /* ERROR "duplicate key" */: 1} - _ = map[interface{}]int{int64(-1): 1, int64 /* ERROR "duplicate key" */ (-1) : 1} - _ = map[interface{}]int{^uint64(0): 1, ^ /* ERROR "duplicate key" */ uint64(0): 1} - _ = map[interface{}]int{complex(1,2): 1, complex /* ERROR "duplicate key" */ (1,2) : 1} - - type I interface { - f() - } - - _ = map[I]int{N(0): 1, N(2): 1} - _ = map[I]int{N(2): 1, N /* ERROR "duplicate key" */ (2): 1} - - // map keys must be resolved correctly - key1 := "foo" - _ = M0{key1: 1} - _ = M0{key2: 2} - _ = M0{key3 /* ERROR "undeclared name" */ : 2} - - var value int - _ = M1{true: 1, false: 0} - _ = M2{nil: 0, &value: 1} - - // composite literal element types may be elided - type T [2]int - _ = map[int]T{0: T{3, 4}, 1: {5, 6}} - - // recursively so - _ = map[int][]T{0: {}, 1: {{}, T{1, 2}}} - - // composite literal key types may be elided - _ = map[T]int{T{3, 4}: 0, {5, 6}: 1} - - // recursively so - _ = map[[2]T]int{{}: 0, {{}}: 1, [2]T{{}}: 2, {T{1, 2}}: 3} - - // composite literal element and key types may be elided - _ = map[T]T{{}: {}, {1, 2}: T{3, 4}, T{4, 5}: {}} - _ = map[T]M0{{} : {}, T{1, 2}: M0{"foo": 0}, {1, 3}: {"foo": 1}} - - // recursively so - _ = map[[2]T][]T{{}: {}, {{}}: {{}, T{1, 2}}, [2]T{{}}: nil, {T{1, 2}}: {{}, {}}} - - // from the spec - type Point struct { x, y float32 } - _ = map[string]Point{"orig": {0, 0}} - _ = map[*Point]string{{0, 0}: "orig"} - - // issue 17954 - type T0 *struct{ s string } - type T1 *struct{ int } - type T2 T1 - - _ = map[T0]T2{} - _ = map[T0]T2{{}: {}} -} - -var key2 string = "bar" - -type I interface { - m() -} - -type I2 interface { - m(int) -} - -type T1 struct{} -type T2 struct{} - -func (T2) m(int) {} - -type mybool bool - -func type_asserts() { - var x int - _ = x /* ERROR "not an interface" */ .(int) - - var e interface{} - var ok bool - x, ok = e.(int) - _ = ok - - // ok value is of type bool - var myok mybool - _, myok = e.(int) - _ = myok - - var t I - _ = t /* ERROR "use of .* outside type switch" */ .(type) - _ = t /* ERROR "missing method m" */ .(T) - _ = t.(*T) - _ = t /* ERROR "missing method m" */ .(T1) - _ = t /* ERROR "wrong type for method m" */ .(T2) - _ = t /* STRICT "wrong type for method m" */ .(I2) // only an error in strict mode (issue 8561) - - // e doesn't statically have an m, but may have one dynamically. - _ = e.(I2) -} - -func f0() {} -func f1(x int) {} -func f2(u float32, s string) {} -func fs(s []byte) {} -func fv(x ...int) {} -func fi(x ... interface{}) {} -func (T) fm(x ...int) - -func g0() {} -func g1() int { return 0} -func g2() (u float32, s string) { return } -func gs() []byte { return nil } - -func _calls() { - var x int - var y float32 - var s []int - - f0() - _ = f0 /* ERROR "used as value" */ () - f0(g0 /* ERROR "too many arguments" */ ) - - f1(0) - f1(x) - f1(10.0) - f1() /* ERROR "not enough arguments" */ - f1(x, y /* ERROR "too many arguments" */ ) - f1(s /* ERROR "cannot use .* in argument" */ ) - f1(x ... /* ERROR "cannot use ..." */ ) - f1(g0 /* ERROR "used as value" */ ()) - f1(g1()) - f1(g2 /* ERROR "too many arguments" */ ()) - - f2() /* ERROR "not enough arguments" */ - f2(3.14) /* ERROR "not enough arguments" */ - f2(3.14, "foo") - f2(x /* ERROR "cannot use .* in argument" */ , "foo") - f2(g0 /* ERROR "used as value" */ ()) - f2(g1()) /* ERROR "not enough arguments" */ - f2(g2()) - - fs() /* ERROR "not enough arguments" */ - fs(g0 /* ERROR "used as value" */ ()) - fs(g1 /* ERROR "cannot use .* in argument" */ ()) - fs(g2 /* ERROR "too many arguments" */ ()) - fs(gs()) - - fv() - fv(1, 2.0, x) - fv(s /* ERROR "cannot use .* in argument" */ ) - fv(s...) - fv(x /* ERROR "cannot use" */ ...) - fv(1, s /* ERROR "too many arguments" */ ... ) - fv(gs /* ERROR "cannot use .* in argument" */ ()) - fv(gs /* ERROR "cannot use .* in argument" */ ()...) - - var t T - t.fm() - t.fm(1, 2.0, x) - t.fm(s /* ERROR "cannot use .* in argument" */ ) - t.fm(g1()) - t.fm(1, s /* ERROR "too many arguments" */ ... ) - t.fm(gs /* ERROR "cannot use .* in argument" */ ()) - t.fm(gs /* ERROR "cannot use .* in argument" */ ()...) - - T.fm(t, ) - T.fm(t, 1, 2.0, x) - T.fm(t, s /* ERROR "cannot use .* in argument" */ ) - T.fm(t, g1()) - T.fm(t, 1, s /* ERROR "too many arguments" */ ... ) - T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()) - T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...) - - var i interface{ fm(x ...int) } = t - i.fm() - i.fm(1, 2.0, x) - i.fm(s /* ERROR "cannot use .* in argument" */ ) - i.fm(g1()) - i.fm(1, s /* ERROR "too many arguments" */ ... ) - i.fm(gs /* ERROR "cannot use .* in argument" */ ()) - i.fm(gs /* ERROR "cannot use .* in argument" */ ()...) - - fi() - fi(1, 2.0, x, 3.14, "foo") - fi(g2()) - fi(0, g2) - fi(0, g2 /* ERROR "2-valued g2" */ ()) -} - -func issue6344() { - type T []interface{} - var x T - fi(x...) // ... applies also to named slices -} diff --git a/src/cmd/compile/internal/types2/testdata/check/go1_12.src b/src/cmd/compile/internal/types2/testdata/check/go1_12.src deleted file mode 100644 index 75a602b8ff9bd9..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/go1_12.src +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check Go language version-specific errors. - -package go1_12 // go1.12 - -// numeric literals -const ( - _ = 1_000 // ERROR "underscores in numeric literals requires go1.13 or later" - _ = 0b111 // ERROR "binary literals requires go1.13 or later" - _ = 0o567 // ERROR "0o/0O-style octal literals requires go1.13 or later" - _ = 0xabc // ok - _ = 0x0p1 // ERROR "hexadecimal floating-point literals requires go1.13 or later" - - _ = 0B111 // ERROR "binary" - _ = 0O567 // ERROR "octal" - _ = 0Xabc // ok - _ = 0X0P1 // ERROR "hexadecimal floating-point" - - _ = 1_000i // ERROR "underscores" - _ = 0b111i // ERROR "binary" - _ = 0o567i // ERROR "octal" - _ = 0xabci // ERROR "hexadecimal floating-point" - _ = 0x0p1i // ERROR "hexadecimal floating-point" -) - -// signed shift counts -var ( - s int - _ = 1 << s // ERROR "invalid operation: signed shift count s \(variable of type int\) requires go1.13 or later" - _ = 1 >> s // ERROR "signed shift count" -) diff --git a/src/cmd/compile/internal/types2/testdata/check/go1_13.src b/src/cmd/compile/internal/types2/testdata/check/go1_13.src deleted file mode 100644 index 93cb4c72a7e1a6..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/go1_13.src +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check Go language version-specific errors. - -package go1_13 // go1.13 - -// interface embedding - -type I interface { m() } - -type _ interface { - m() - I // ERROR "duplicate method m" -} - -type _ interface { - I - I // ERROR "duplicate method m" -} diff --git a/src/cmd/compile/internal/types2/testdata/check/go1_16.src b/src/cmd/compile/internal/types2/testdata/check/go1_16.src deleted file mode 100644 index fdf5c99d7e3248..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/go1_16.src +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check Go language version-specific errors. - -package go1_16 // go1.16 - -type Slice []byte -type Array [8]byte - -var s Slice -var p = (*Array)(s /* ERROR requires go1.17 or later */ ) diff --git a/src/cmd/compile/internal/types2/testdata/check/go1_8.src b/src/cmd/compile/internal/types2/testdata/check/go1_8.src deleted file mode 100644 index 0f3ba9443bd531..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/go1_8.src +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check Go language version-specific errors. - -package go1_8 // go1.8 - -// type alias declarations -type any /* ERROR type aliases requires go1.9 or later */ = interface{} diff --git a/src/cmd/compile/internal/types2/testdata/check/importC.src b/src/cmd/compile/internal/types2/testdata/check/importC.src deleted file mode 100644 index f55be2d5c53c89..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/importC.src +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package importC - -import "C" -import _ /* ERROR cannot rename import "C" */ "C" -import foo /* ERROR cannot rename import "C" */ "C" -import . /* ERROR cannot rename import "C" */ "C" - -// Test cases extracted from issue #22090. - -import "unsafe" - -const _ C.int = 0xff // no error due to invalid constant type - -type T struct { - Name string - Ordinal int -} - -func _(args []T) { - var s string - for i, v := range args { - cname := C.CString(v.Name) - args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s, cname)) // no error due to i not being "used" - C.free(unsafe.Pointer(cname)) - } -} - -type CType C.Type - -const _ CType = C.X // no error due to invalid constant type -const _ = C.X - -// Test cases extracted from issue #23712. - -func _() { - var a [C.ArrayLength]byte - _ = a[0] // no index out of bounds error here -} - -// Additional tests to verify fix for #23712. - -func _() { - var a [C.ArrayLength1]byte - _ = 1 / len(a) // no division by zero error here and below - _ = 1 / cap(a) - _ = uint(unsafe.Sizeof(a)) // must not be negative - - var b [C.ArrayLength2]byte - a = b // should be valid -} diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.go2 b/src/cmd/compile/internal/types2/testdata/check/issues.go2 deleted file mode 100644 index 1c73b5da9219c0..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/issues.go2 +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains regression tests for bugs found. - -package p - -import "io" -import "context" - -// Interfaces are always comparable (though the comparison may panic at runtime). -func eql[T comparable](x, y T) bool { - return x == y -} - -func _() { - var x interface{} - var y interface{ m() } - eql(x, y /* ERROR does not match */ ) // interfaces of different types - eql(x, x) - eql(y, y) - eql(y, nil) - eql[io.Reader](nil, nil) -} - -// If we have a receiver of pointer type (below: *T) we must ignore -// the pointer in the implementation of the method lookup because -// the type bound of T is an interface and pointer to interface types -// have no methods and then the lookup would fail. -type C[T any] interface { - m() -} - -// using type bound C -func _[T C[T]](x *T) { - x.m() -} - -// using an interface literal as bound -func _[T interface{ m() }](x *T) { - x.m() -} - -func f2[_ interface{ m1(); m2() }]() - -type T struct{} -func (T) m1() -func (*T) m2() - -func _() { - f2[T /* ERROR wrong method signature */ ]() - f2[*T]() -} - -// When a type parameter is used as an argument to instantiate a parameterized -// type with a type list constraint, all of the type argument's types in its -// bound, but at least one (!), must be in the type list of the bound of the -// corresponding parameterized type's type parameter. -type T1[P interface{type uint}] struct{} - -func _[P any]() { - _ = T1[P /* ERROR P has no type constraints */ ]{} -} - -// This is the original (simplified) program causing the same issue. -type Unsigned interface { - type uint -} - -type T2[U Unsigned] struct { - s U -} - -func (u T2[U]) Add1() U { - return u.s + 1 -} - -func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] { - return T2[U /* ERROR U has no type constraints */ ]{} -} - -func _() { - u := NewT2[string]() - _ = u.Add1() -} - -// When we encounter an instantiated type such as Elem[T] we must -// not "expand" the instantiation when the type to be instantiated -// (Elem in this case) is not yet fully set up. -type Elem[T any] struct { - next *Elem[T] - list *List[T] -} - -type List[T any] struct { - root Elem[T] -} - -func (l *List[T]) Init() { - l.root.next = &l.root -} - -// This is the original program causing the same issue. -type Element2[TElem any] struct { - next, prev *Element2[TElem] - list *List2[TElem] - Value TElem -} - -type List2[TElem any] struct { - root Element2[TElem] - len int -} - -func (l *List2[TElem]) Init() *List2[TElem] { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// Self-recursive instantiations must work correctly. -type A[P any] struct { _ *A[P] } - -type AB[P any] struct { _ *BA[P] } -type BA[P any] struct { _ *AB[P] } - -// And a variation that also caused a problem with an -// unresolved underlying type. -type Element3[TElem any] struct { - next, prev *Element3[TElem] - list *List3[TElem] - Value TElem -} - -func (e *Element3[TElem]) Next() *Element3[TElem] { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -type List3[TElem any] struct { - root Element3[TElem] - len int -} - -// Infinite generic type declarations must lead to an error. -type inf1[T any] struct{ _ inf1 /* ERROR illegal cycle */ [T] } -type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] } - -// The implementation of conversions T(x) between integers and floating-point -// numbers checks that both T and x have either integer or floating-point -// type. When the type of T or x is a type parameter, the respective simple -// predicate disjunction in the implementation was wrong because if a type list -// contains both an integer and a floating-point type, the type parameter is -// neither an integer or a floating-point number. -func convert[T1, T2 interface{type int, uint, float32}](v T1) T2 { - return T2(v) -} - -func _() { - convert[int, uint](5) -} - -// When testing binary operators, for +, the operand types must either be -// both numeric, or both strings. The implementation had the same problem -// with this check as the conversion issue above (issue #39623). - -func issue39623[T interface{type int, string}](x, y T) T { - return x + y -} - -// Simplified, from https://go2goplay.golang.org/p/efS6x6s-9NI: -func Sum[T interface{type int, string}](s []T) (sum T) { - for _, v := range s { - sum += v - } - return -} - -// Assignability of an unnamed pointer type to a type parameter that -// has a matching underlying type. -func _[T interface{}, PT interface{type *T}] (x T) PT { - return &x -} - -// Indexing of generic types containing type parameters in their type list: -func at[T interface{ type []E }, E interface{}](x T, i int) E { - return x[i] -} - -// A generic type inside a function acts like a named type. Its underlying -// type is itself, its "operational type" is defined by the type list in -// the tybe bound, if any. -func _[T interface{type int}](x T) { - type myint int - var _ int = int(x) - var _ T = 42 - var _ T = T(myint(42)) -} - -// Indexing a generic type with an array type bound checks length. -// (Example by mdempsky@.) -func _[T interface { type [10]int }](x T) { - _ = x[9] // ok - _ = x[20 /* ERROR out of bounds */ ] -} - -// Pointer indirection of a generic type. -func _[T interface{ type *int }](p T) int { - return *p -} - -// Channel sends and receives on generic types. -func _[T interface{ type chan int }](ch T) int { - ch <- 0 - return <- ch -} - -// Calling of a generic variable. -func _[T interface{ type func() }](f T) { - f() - go f() -} - -// We must compare against the underlying type of type list entries -// when checking if a constraint is satisfied by a type. The under- -// lying type of each type list entry must be computed after the -// interface has been instantiated as its typelist may contain a -// type parameter that was substituted with a defined type. -// Test case from an (originally) failing example. - -type sliceOf[E any] interface{ type []E } - -func append[T interface{}, S sliceOf[T], T2 interface{ type T }](s S, t ...T2) S - -var f func() -var cancelSlice []context.CancelFunc -var _ = append[context.CancelFunc, []context.CancelFunc, context.CancelFunc](cancelSlice, f) - -// A generic function must be instantiated with a type, not a value. - -func g[T any](T) T - -var _ = g[int] -var _ = g[nil /* ERROR is not a type */ ] -var _ = g(0) diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.src b/src/cmd/compile/internal/types2/testdata/check/issues.src deleted file mode 100644 index 21aa208cc769d2..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/issues.src +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package issues - -import ( - "fmt" - syn "cmd/compile/internal/syntax" - t1 "text/template" - t2 "html/template" -) - -func issue7035() { - type T struct{ X int } - _ = func() { - fmt.Println() // must refer to imported fmt rather than the fmt below - } - fmt := new(T) - _ = fmt.X -} - -func issue8066() { - const ( - _ = float32(340282356779733661637539395458142568447) - _ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ ) - ) -} - -// Check that a missing identifier doesn't lead to a spurious error cascade. -func issue8799a() { - x, ok := missing /* ERROR undeclared */ () - _ = !ok - _ = x -} - -func issue8799b(x int, ok bool) { - x, ok = missing /* ERROR undeclared */ () - _ = !ok - _ = x -} - -func issue9182() { - type Point C /* ERROR undeclared */ .Point - // no error for composite literal based on unknown type - _ = Point{x: 1, y: 2} -} - -func f0() (a []int) { return } -func f1() (a []int, b int) { return } -func f2() (a, b []int) { return } - -func append_([]int, ...int) {} - -func issue9473(a []int, b ...int) { - // variadic builtin function - _ = append(f0()) - _ = append(f0(), f0()...) - _ = append(f1()) - _ = append(f2 /* ERROR cannot use .* in argument */ ()) - _ = append(f2()... /* ERROR cannot use ... */ ) - _ = append(f0(), f1 /* ERROR 2-valued f1 */ ()) - _ = append(f0(), f2 /* ERROR 2-valued f2 */ ()) - _ = append(f0(), f1 /* ERROR 2-valued f1 */ ()...) - _ = append(f0(), f2 /* ERROR 2-valued f2 */ ()...) - - // variadic user-defined function - append_(f0()) - append_(f0(), f0()...) - append_(f1()) - append_(f2 /* ERROR cannot use .* in argument */ ()) - append_(f2()... /* ERROR cannot use ... */ ) - append_(f0(), f1 /* ERROR 2-valued f1 */ ()) - append_(f0(), f2 /* ERROR 2-valued f2 */ ()) - append_(f0(), f1 /* ERROR 2-valued f1 */ ()...) - append_(f0(), f2 /* ERROR 2-valued f2 */ ()...) -} - -// Check that embedding a non-interface type in an interface results in a good error message. -func issue10979() { - type _ interface { - int /* ERROR int is not an interface */ - } - type T struct{} - type _ interface { - T /* ERROR T is not an interface */ - } - type _ interface { - nosuchtype /* ERROR undeclared name: nosuchtype */ - } - type _ interface { - fmt.Nosuchtype /* ERROR Nosuchtype not declared by package fmt */ - } - type _ interface { - nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype - } - type I interface { - I.m /* ERROR no field or method m */ - m() - } -} - -// issue11347 -// These should not crash. -var a1, b1 /* ERROR cycle */ , c1 /* ERROR cycle */ b1 = 0 > 0<<""[""[c1]]>c1 -var a2, b2 /* ERROR cycle */ = 0 /* ERROR cannot initialize */ /* ERROR cannot initialize */ > 0<<""[b2] -var a3, b3 /* ERROR cycle */ = int /* ERROR cannot initialize */ /* ERROR cannot initialize */ (1<<""[b3]) - -// issue10260 -// Check that error messages explain reason for interface assignment failures. -type ( - I0 interface{} - I1 interface{ foo() } - I2 interface{ foo(x int) } - T0 struct{} - T1 struct{} - T2 struct{} -) - -func (*T1) foo() {} -func (*T2) foo(x int) {} - -func issue10260() { - var ( - i0 I0 - i1 I1 - i2 I2 - t0 *T0 - t1 *T1 - t2 *T2 - ) - - var x I1 - x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {} - _ = x /* ERROR .* cannot have dynamic type T1 \(missing method foo \(foo has pointer receiver\)\) */ .(T1) - - T1{}.foo /* ERROR cannot call pointer method foo on T1 */ () - x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ () - - _ = i2 /* ERROR i2 .* cannot have dynamic type \*T1 \(wrong type for method foo \(have func\(\), want func\(x int\)\)\) */ .(*T1) - - i1 = i0 /* ERROR cannot use .* missing method foo */ - i1 = t0 /* ERROR cannot use .* missing method foo */ - i1 = i2 /* ERROR cannot use .* wrong type for method foo */ - i1 = t2 /* ERROR cannot use .* wrong type for method foo */ - i2 = i1 /* ERROR cannot use .* wrong type for method foo */ - i2 = t1 /* ERROR cannot use .* wrong type for method foo */ - - _ = func() I1 { return i0 /* ERROR cannot use .* missing method foo */ } - _ = func() I1 { return t0 /* ERROR cannot use .* missing method foo */ } - _ = func() I1 { return i2 /* ERROR cannot use .* wrong type for method foo */ } - _ = func() I1 { return t2 /* ERROR cannot use .* wrong type for method foo */ } - _ = func() I2 { return i1 /* ERROR cannot use .* wrong type for method foo */ } - _ = func() I2 { return t1 /* ERROR cannot use .* wrong type for method foo */ } - - // a few more - less exhaustive now - - f := func(I1, I2){} - f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo \(have func\(\), want func\(x int\)\) */ ) - - _ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ } - _ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ } - _ = []I1{i0 /* ERROR cannot use .* missing method foo */ } - _ = []I1{i2 /* ERROR cannot use .* wrong type for method foo */ } - _ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ } - _ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ } - - make(chan I1) <- i0 /* ERROR cannot use .* in send: missing method foo */ - make(chan I1) <- i2 /* ERROR cannot use .* in send: wrong type for method foo */ -} - -// Check that constants representable as integers are in integer form -// before being used in operations that are only defined on integers. -func issue14229() { - // from the issue - const _ = int64(-1<<63) % 1e6 - - // related - const ( - a int = 3 - b = 4.0 - _ = a / b - _ = a % b - _ = b / a - _ = 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 -} - -// Test that we don't get "declared but not used" -// errors in the context of invalid/C objects. -func issue20358() { - var F C /* ERROR "undeclared" */ .F - var A C /* ERROR "undeclared" */ .A - var S C /* ERROR "undeclared" */ .S - type T C /* ERROR "undeclared" */ .T - type P C /* ERROR "undeclared" */ .P - - // these variables must be "used" even though - // the LHS expressions/types below in which - // context they are used are unknown/invalid - var f, a, s1, s2, s3, t, p int - - _ = F(f) - _ = A[a] - _ = S[s1:s2:s3] - _ = T{t} - _ = P{f: p} -} - -// Test that we don't declare lhs variables in short variable -// declarations before we type-check function literals on the -// rhs. -func issue24026() { - f := func() int { f(0) /* must refer to outer f */; return 0 } - _ = f - - _ = func() { - f := func() { _ = f() /* must refer to outer f */ } - _ = f - } - - // b and c must not be visible inside function literal - a := 0 - a, b, c := func() (int, int, int) { - return a, b /* ERROR undeclared */ , c /* ERROR undeclared */ - }() - _, _ = b, c -} - -func f(int) {} // for issue24026 - -// Test that we don't report a "missing return statement" error -// (due to incorrect context when type-checking interfaces). -func issue24140(x interface{}) int { - switch x.(type) { - case interface{}: - return 0 - default: - panic(0) - } -} - -// Test that we don't crash when the 'if' condition is missing. -func issue25438() { - if { /* ERROR missing condition */ } - if x := 0; /* ERROR missing condition */ { _ = x } - if - { /* ERROR missing condition */ } -} - -// Test that we can embed alias type names in interfaces. -type issue25301 interface { - E -} - -type E = interface { - m() -} - -// Test case from issue. -// cmd/compile reports a cycle as well. -type issue25301b /* ERROR cycle */ = interface { - m() interface{ issue25301b } -} - -type issue25301c interface { - notE // ERROR struct\{\} is not an interface -} - -type notE = struct{} - -// Test that method declarations don't introduce artificial cycles -// (issue #26124). -const CC TT = 1 -type TT int -func (TT) MM() [CC]TT - -// Reduced test case from issue #26124. -const preloadLimit LNumber = 128 -type LNumber float64 -func (LNumber) assertFunction() *LFunction -type LFunction struct { - GFunction LGFunction -} -type LGFunction func(*LState) -type LState struct { - reg *registry -} -type registry struct { - alloc *allocator -} -type allocator struct { - _ [int(preloadLimit)]int -} - -// Test that we don't crash when type-checking composite literals -// containing errors in the type. -var issue27346 = [][n /* ERROR undeclared */ ]int{ - 0: {}, -} - -var issue22467 = map[int][... /* ERROR invalid use of ... */ ]int{0: {}} - -// Test that invalid use of ... in parameter lists is recognized -// (issue #28281). -func issue28281a(int, int, ...int) -func issue28281b(a, b int, c ...int) -func issue28281c(a, b, c ... /* ERROR can only use ... with final parameter */ int) -func issue28281d(... /* ERROR can only use ... with final parameter */ int, int) -func issue28281e(a, b, c ... /* ERROR can only use ... with final parameter */ int, d int) -func issue28281f(... /* ERROR can only use ... with final parameter */ int, ... /* ERROR can only use ... with final parameter */ int, int) -func (... /* ERROR can only use ... with final parameter in list */ TT) f() -func issue28281g() (... /* ERROR can only use ... with final parameter in list */ TT) - -// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output -func issue26234a(f *syn.File) { - // The error message below should refer to the actual package name (syntax) - // not the local package name (syn). - f.foo /* ERROR f.foo undefined \(type \*syntax.File has no field or method foo\) */ -} - -type T struct { - x int - E1 - E2 -} - -type E1 struct{ f int } -type E2 struct{ f int } - -func issue26234b(x T) { - _ = x.f /* ERROR ambiguous selector x.f */ -} - -func issue26234c() { - T.x /* ERROR T.x undefined \(type T has no method x\) */ () -} - -func issue35895() { - // T is defined in this package, don't qualify its name with the package name. - var _ T = 0 // ERROR cannot use 0 \(untyped int constant\) as T - - // There is only one package with name syntax imported, only use the (global) package name in error messages. - var _ *syn.File = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.File - - // Because both t1 and t2 have the same global package name (template), - // qualify packages with full path name in this case. - var _ t1.Template = t2 /* ERROR cannot use .* \(value of type "html/template".Template\) as "text/template".Template */ .Template{} -} - -func issue42989(s uint) { - var m map[int]string - delete(m, 1< 0: - pn = &(*pn).right - default: - return pn - } - } - return pn -} - -// Insert inserts a new key/value into the map. -// If the key is already present, the value is replaced. -// Returns true if this is a new key, false if already present. -func (m *Map[K, V]) Insert(key K, val V) bool { - pn := m.find(key) - if *pn != nil { - (*pn).val = val - return false - } - *pn = &node[K, V]{key: key, val: val} - return true -} - -// Find returns the value associated with a key, or zero if not present. -// The found result reports whether the key was found. -func (m *Map[K, V]) Find(key K) (V, bool) { - pn := m.find(key) - if *pn == nil { - var zero V // see the discussion of zero values, above - return zero, false - } - return (*pn).val, true -} - -// keyValue is a pair of key and value used when iterating. -type keyValue[K, V any] struct { - key K - val V -} - -// InOrder returns an iterator that does an in-order traversal of the map. -func (m *Map[K, V]) InOrder() *Iterator[K, V] { - sender, receiver := chans.Ranger[keyValue[K, V]]() - var f func(*node[K, V]) bool - f = func(n *node[K, V]) bool { - if n == nil { - return true - } - // Stop sending values if sender.Send returns false, - // meaning that nothing is listening at the receiver end. - return f(n.left) && - sender.Send(keyValue[K, V]{n.key, n.val}) && - f(n.right) - } - go func() { - f(m.root) - sender.Close() - }() - return &Iterator[K, V]{receiver} -} - -// Iterator is used to iterate over the map. -type Iterator[K, V any] struct { - r *chans.Receiver[keyValue[K, V]] -} - -// Next returns the next key and value pair, and a boolean indicating -// whether they are valid or whether we have reached the end. -func (it *Iterator[K, V]) Next() (K, V, bool) { - keyval, ok := it.r.Next() - if !ok { - var zerok K - var zerov V - return zerok, zerov, false - } - return keyval.key, keyval.val, true -} diff --git a/src/cmd/compile/internal/types2/testdata/check/map2.go2 b/src/cmd/compile/internal/types2/testdata/check/map2.go2 deleted file mode 100644 index 2833445662de81..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/map2.go2 +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 The Go 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 is like map.go2, but instead if importing chans, it contains -// the necessary functionality at the end of the file. - -// Package orderedmap provides an ordered map, implemented as a binary tree. -package orderedmap - -// Map is an ordered map. -type Map[K, V any] struct { - root *node[K, V] - compare func(K, K) int -} - -// node is the type of a node in the binary tree. -type node[K, V any] struct { - key K - val V - left, right *node[K, V] -} - -// New returns a new map. -func New[K, V any](compare func(K, K) int) *Map[K, V] { - return &Map[K, V]{compare: compare} -} - -// find looks up key in the map, and returns either a pointer -// to the node holding key, or a pointer to the location where -// such a node would go. -func (m *Map[K, V]) find(key K) **node[K, V] { - pn := &m.root - for *pn != nil { - switch cmp := m.compare(key, (*pn).key); { - case cmp < 0: - pn = &(*pn).left - case cmp > 0: - pn = &(*pn).right - default: - return pn - } - } - return pn -} - -// Insert inserts a new key/value into the map. -// If the key is already present, the value is replaced. -// Returns true if this is a new key, false if already present. -func (m *Map[K, V]) Insert(key K, val V) bool { - pn := m.find(key) - if *pn != nil { - (*pn).val = val - return false - } - *pn = &node[K, V]{key: key, val: val} - return true -} - -// Find returns the value associated with a key, or zero if not present. -// The found result reports whether the key was found. -func (m *Map[K, V]) Find(key K) (V, bool) { - pn := m.find(key) - if *pn == nil { - var zero V // see the discussion of zero values, above - return zero, false - } - return (*pn).val, true -} - -// keyValue is a pair of key and value used when iterating. -type keyValue[K, V any] struct { - key K - val V -} - -// InOrder returns an iterator that does an in-order traversal of the map. -func (m *Map[K, V]) InOrder() *Iterator[K, V] { - sender, receiver := chans_Ranger[keyValue[K, V]]() - var f func(*node[K, V]) bool - f = func(n *node[K, V]) bool { - if n == nil { - return true - } - // Stop sending values if sender.Send returns false, - // meaning that nothing is listening at the receiver end. - return f(n.left) && - sender.Send(keyValue[K, V]{n.key, n.val}) && - f(n.right) - } - go func() { - f(m.root) - sender.Close() - }() - return &Iterator[K, V]{receiver} -} - -// Iterator is used to iterate over the map. -type Iterator[K, V any] struct { - r *chans_Receiver[keyValue[K, V]] -} - -// Next returns the next key and value pair, and a boolean indicating -// whether they are valid or whether we have reached the end. -func (it *Iterator[K, V]) Next() (K, V, bool) { - keyval, ok := it.r.Next() - if !ok { - var zerok K - var zerov V - return zerok, zerov, false - } - return keyval.key, keyval.val, true -} - -// chans - -func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) - -// A sender is used to send values to a Receiver. -type chans_Sender[T any] struct { - values chan<- T - done <-chan bool -} - -func (s *chans_Sender[T]) Send(v T) bool { - select { - case s.values <- v: - return true - case <-s.done: - return false - } -} - -func (s *chans_Sender[T]) Close() { - close(s.values) -} - -type chans_Receiver[T any] struct { - values <-chan T - done chan<- bool -} - -func (r *chans_Receiver[T]) Next() (T, bool) { - v, ok := <-r.values - return v, ok -} \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/methodsets.src b/src/cmd/compile/internal/types2/testdata/check/methodsets.src deleted file mode 100644 index 9fb10deb9a2566..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/methodsets.src +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package methodsets - -type T0 struct {} - -func (T0) v0() {} -func (*T0) p0() {} - -type T1 struct {} // like T0 with different method names - -func (T1) v1() {} -func (*T1) p1() {} - -type T2 interface { - v2() - p2() -} - -type T3 struct { - T0 - *T1 - T2 -} - -// Method expressions -func _() { - var ( - _ func(T0) = T0.v0 - _ = T0.p0 /* ERROR "cannot call pointer method p0 on T0" */ - - _ func (*T0) = (*T0).v0 - _ func (*T0) = (*T0).p0 - - // T1 is like T0 - - _ func(T2) = T2.v2 - _ func(T2) = T2.p2 - - _ func(T3) = T3.v0 - _ func(T3) = T3.p0 /* ERROR "cannot call pointer method p0 on T3" */ - _ func(T3) = T3.v1 - _ func(T3) = T3.p1 - _ func(T3) = T3.v2 - _ func(T3) = T3.p2 - - _ func(*T3) = (*T3).v0 - _ func(*T3) = (*T3).p0 - _ func(*T3) = (*T3).v1 - _ func(*T3) = (*T3).p1 - _ func(*T3) = (*T3).v2 - _ func(*T3) = (*T3).p2 - ) -} - -// Method values with addressable receivers -func _() { - var ( - v0 T0 - _ func() = v0.v0 - _ func() = v0.p0 - ) - - var ( - p0 *T0 - _ func() = p0.v0 - _ func() = p0.p0 - ) - - // T1 is like T0 - - var ( - v2 T2 - _ func() = v2.v2 - _ func() = v2.p2 - ) - - var ( - v4 T3 - _ func() = v4.v0 - _ func() = v4.p0 - _ func() = v4.v1 - _ func() = v4.p1 - _ func() = v4.v2 - _ func() = v4.p2 - ) - - var ( - p4 *T3 - _ func() = p4.v0 - _ func() = p4.p0 - _ func() = p4.v1 - _ func() = p4.p1 - _ func() = p4.v2 - _ func() = p4.p2 - ) -} - -// Method calls with addressable receivers -func _() { - var v0 T0 - v0.v0() - v0.p0() - - var p0 *T0 - p0.v0() - p0.p0() - - // T1 is like T0 - - var v2 T2 - v2.v2() - v2.p2() - - var v4 T3 - v4.v0() - v4.p0() - v4.v1() - v4.p1() - v4.v2() - v4.p2() - - var p4 *T3 - p4.v0() - p4.p0() - p4.v1() - p4.p1() - p4.v2() - p4.p2() -} - -// Method values with value receivers -func _() { - var ( - _ func() = T0{}.v0 - _ func() = T0{}.p0 /* ERROR "cannot call pointer method p0 on T0" */ - - _ func() = (&T0{}).v0 - _ func() = (&T0{}).p0 - - // T1 is like T0 - - // no values for T2 - - _ func() = T3{}.v0 - _ func() = T3{}.p0 /* ERROR "cannot call pointer method p0 on T3" */ - _ func() = T3{}.v1 - _ func() = T3{}.p1 - _ func() = T3{}.v2 - _ func() = T3{}.p2 - - _ func() = (&T3{}).v0 - _ func() = (&T3{}).p0 - _ func() = (&T3{}).v1 - _ func() = (&T3{}).p1 - _ func() = (&T3{}).v2 - _ func() = (&T3{}).p2 - ) -} - -// Method calls with value receivers -func _() { - T0{}.v0() - T0{}.p0 /* ERROR "cannot call pointer method p0 on T0" */ () - - (&T0{}).v0() - (&T0{}).p0() - - // T1 is like T0 - - // no values for T2 - - T3{}.v0() - T3{}.p0 /* ERROR "cannot call pointer method p0 on T3" */ () - T3{}.v1() - T3{}.p1() - T3{}.v2() - T3{}.p2() - - (&T3{}).v0() - (&T3{}).p0() - (&T3{}).v1() - (&T3{}).p1() - (&T3{}).v2() - (&T3{}).p2() -} - -// *T has no methods if T is an interface type -func issue5918() { - var ( - err error - _ = err.Error() - _ func() string = err.Error - _ func(error) string = error.Error - - perr = &err - _ = perr.Error /* ERROR "no field or method" */ () - _ func() string = perr.Error /* ERROR "no field or method" */ - _ func(*error) string = (*error).Error /* ERROR "no field or method" */ - ) - - type T *interface{ m() int } - var ( - x T - _ = (*x).m() - _ = (*x).m - - _ = x.m /* ERROR "no field or method" */ () - _ = x.m /* ERROR "no field or method" */ - _ = T.m /* ERROR "no field or method" */ - ) -} diff --git a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2 deleted file mode 100644 index c2f282bae11a98..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2 +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// If types2.Config.AcceptMethodTypeParams is set, -// the type checker accepts methods that have their -// own type parameter list. - -package p - -type S struct{} - -func (S) m[T any](v T) - -// TODO(gri) Once we collect interface method type parameters -// in the parser, we can enable these tests again. -/* -type I interface { - m[T any](v T) -} - -type J interface { - m[T any](v T) -} - -var _ I = S{} -var _ I = J(nil) - -type C interface{ n() } - -type Sc struct{} - -func (Sc) m[T C](v T) - -type Ic interface { - m[T C](v T) -} - -type Jc interface { - m[T C](v T) -} - -var _ Ic = Sc{} -var _ Ic = Jc(nil) - -// TODO(gri) These should fail because the constraints don't match. -var _ I = Sc{} -var _ I = Jc(nil) - -var _ Ic = S{} -var _ Ic = J(nil) -*/ \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/shifts.src b/src/cmd/compile/internal/types2/testdata/check/shifts.src deleted file mode 100644 index 60db731cf4e03d..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/shifts.src +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package shifts - -func shifts0() { - // basic constant shifts - const ( - s = 10 - _ = 0<<0 - _ = 1<> s) - _, _, _ = u, v, x -} - -func shifts4() { - // shifts in comparisons w/ untyped operands - var s uint - - _ = 1<> 1.1 /* ERROR "truncated to uint" */ // example from issue 11325 - _ = 0 >> 1.1 /* ERROR "truncated to uint" */ - _ = 0 << 1.1 /* ERROR "truncated to uint" */ - _ = 0 >> 1. - _ = 1 >> 1.1 /* ERROR "truncated to uint" */ - _ = 1 >> 1. - _ = 1. >> 1 - _ = 1. >> 1. - _ = 1.1 /* ERROR "must be integer" */ >> 1 -} - -func issue11594() { - var _ = complex64 /* ERROR "must be integer" */ (1) << 2 // example from issue 11594 - _ = float32 /* ERROR "must be integer" */ (0) << 1 - _ = float64 /* ERROR "must be integer" */ (0) >> 2 - _ = complex64 /* ERROR "must be integer" */ (0) << 3 - _ = complex64 /* ERROR "must be integer" */ (0) >> 4 -} - -func issue21727() { - var s uint - var a = make([]int, 1< 255: - return 255 - } -} - -var input = []int{-4, 68954, 7, 44, 0, -555, 6945} -var limited1 = Map[int, byte](input, limiter) -var limited2 = Map(input, limiter) // using type inference - -func reducer(x float64, y int) float64 { - return x + float64(y) -} - -var reduced1 = Reduce[int, float64](input, 0, reducer) -var reduced2 = Reduce(input, 1i /* ERROR overflows */, reducer) // using type inference -var reduced3 = Reduce(input, 1, reducer) // using type inference - -func filter(x int) bool { - return x&1 != 0 -} - -var filtered1 = Filter[int](input, filter) -var filtered2 = Filter(input, filter) // using type inference - diff --git a/src/cmd/compile/internal/types2/testdata/check/stmt0.src b/src/cmd/compile/internal/types2/testdata/check/stmt0.src deleted file mode 100644 index bedcbe5fce3573..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/stmt0.src +++ /dev/null @@ -1,980 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// statements - -package stmt0 - -func assignments0() (int, int) { - var a, b, c int - var ch chan int - f0 := func() {} - f1 := func() int { return 1 } - f2 := func() (int, int) { return 1, 2 } - f3 := func() (int, int, int) { return 1, 2, 3 } - - a, b, c = 1, 2, 3 - a, b, c = 1 /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ , 2 - a, b, c = 1 /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ , 2, 3, 4 - _, _, _ = a, b, c - - a = f0 /* ERROR "used as value" */ () - a = f1() - a = f2 /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ () - a, b = f2() - a, b, c = f2 /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ () - a, b, c = f3() - a, b = f3 /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ () - - a, b, c = <- /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ ch - - return /* ERROR "wrong number of return values" */ - return /* ERROR "wrong number of return values" */ 1 - return 1, 2 - return /* ERROR "wrong number of return values" */ 1, 2, 3 -} - -func assignments1() { - b, i, f, c, s := false, 1, 1.0, 1i, "foo" - b = i /* ERROR "cannot use .* in assignment" */ - i = f /* ERROR "cannot use .* in assignment" */ - f = c /* ERROR "cannot use .* in assignment" */ - c = s /* ERROR "cannot use .* in assignment" */ - s = b /* ERROR "cannot use .* in assignment" */ - - v0, v1, v2 := 1 /* ERROR "cannot initialize" */ , 2, 3, 4 - _, _, _ = v0, v1, v2 - - b = true - - i += 1 - i += "foo" /* ERROR "cannot convert.*int" */ - - f -= 1 - f /= 0 - f = float32(0)/0 /* ERROR "division by zero" */ - f -= "foo" /* ERROR "cannot convert.*float64" */ - - c *= 1 - c /= 0 - - s += "bar" - s += 1 /* ERROR "cannot convert.*string" */ - - var u64 uint64 - u64 += 1< 0 { - return l[0], true - } - return -} - -// A test case for instantiating types with other types (extracted from map.go2) - -type Pair[K any] struct { - key K -} - -type Receiver[T any] struct { - values T -} - -type Iterator[K any] struct { - r Receiver[Pair[K]] -} - -func Values [T any] (r Receiver[T]) T { - return r.values -} - -func (it Iterator[K]) Next() K { - return Values[Pair[K]](it.r).key -} - -// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) - -type NumericAbs[T any] interface { - Abs() T -} - -func AbsDifference[T NumericAbs[T]](x T) - -type OrderedAbs[T any] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] - -func OrderedAbsDifference[T any](x T) { - AbsDifference(OrderedAbs[T](x)) -} - -// same code, reduced to essence - -func g[P interface{ m() P }](x P) - -type T4[P any] P - -func (_ T4[P]) m() T4[P] - -func _[Q any](x Q) { - g(T4[Q](x)) -} - -// Another test case that caused problems in the past - -type T5[_ interface { a() }, _ interface{}] struct{} - -type A[P any] struct{ x P } - -func (_ A[P]) a() {} - -var _ T5[A[int], int] - -// Invoking methods with parameterized receiver types uses -// type inference to determine the actual type arguments matching -// the receiver type parameters from the actual receiver argument. -// Go does implicit address-taking and dereferenciation depending -// on the actual receiver and the method's receiver type. To make -// type inference work, the type-checker matches "pointer-ness" -// of the actual receiver and the method's receiver type. -// The following code tests this mechanism. - -type R1[A any] struct{} -func (_ R1[A]) vm() -func (_ *R1[A]) pm() - -func _[T any](r R1[T], p *R1[T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -type R2[A, B any] struct{} -func (_ R2[A, B]) vm() -func (_ *R2[A, B]) pm() - -func _[T any](r R2[T, int], p *R2[string, T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -// An interface can (explicitly) declare at most one type list. -type _ interface { - m0() - type int, string, bool - type /* ERROR multiple type lists */ float32, float64 - m1() - m2() - type /* ERROR multiple type lists */ complex64, complex128 - type /* ERROR multiple type lists */ rune -} - -// Interface type lists may contain each type at most once. -// (If there are multiple lists, we assume the author intended -// for them to be all in a single list, and we report the error -// as well.) -type _ interface { - type int, int /* ERROR duplicate type int */ - type /* ERROR multiple type lists */ int /* ERROR duplicate type int */ -} - -type _ interface { - type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int} -} - -// Interface type lists can contain any type, incl. *Named types. -// Verify that we use the underlying type to compute the operational type. -type MyInt int -func add1[T interface{type MyInt}](x T) T { - return x + 1 -} - -type MyString string -func double[T interface{type MyInt, MyString}](x T) T { - return x + x -} - -// Embedding of interfaces with type lists leads to interfaces -// with type lists that are the intersection of the embedded -// type lists. - -type E0 interface { - type int, bool, string -} - -type E1 interface { - type int, float64, string -} - -type E2 interface { - type float64 -} - -type I0 interface { - E0 -} - -func f0[T I0]() -var _ = f0[int] -var _ = f0[bool] -var _ = f0[string] -var _ = f0[float64 /* ERROR does not satisfy I0 */ ] - -type I01 interface { - E0 - E1 -} - -func f01[T I01]() -var _ = f01[int] -var _ = f01[bool /* ERROR does not satisfy I0 */ ] -var _ = f01[string] -var _ = f01[float64 /* ERROR does not satisfy I0 */ ] - -type I012 interface { - E0 - E1 - E2 -} - -func f012[T I012]() -var _ = f012[int /* ERROR does not satisfy I012 */ ] -var _ = f012[bool /* ERROR does not satisfy I012 */ ] -var _ = f012[string /* ERROR does not satisfy I012 */ ] -var _ = f012[float64 /* ERROR does not satisfy I012 */ ] - -type I12 interface { - E1 - E2 -} - -func f12[T I12]() -var _ = f12[int /* ERROR does not satisfy I12 */ ] -var _ = f12[bool /* ERROR does not satisfy I12 */ ] -var _ = f12[string /* ERROR does not satisfy I12 */ ] -var _ = f12[float64] - -type I0_ interface { - E0 - type int -} - -func f0_[T I0_]() -var _ = f0_[int] -var _ = f0_[bool /* ERROR does not satisfy I0_ */ ] -var _ = f0_[string /* ERROR does not satisfy I0_ */ ] -var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ] diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 deleted file mode 100644 index badda01105bd46..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// import "io" // for type assertion tests - -// The predeclared identifier "any" is only visible as a constraint -// in a type parameter list. -var _ any // ERROR undeclared -func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) { - var _ any /* ERROR undeclared */ -} - -func identity[T any](x T) T { return x } - -func _[_ any](x int) int -func _[T any](T /* ERROR redeclared */ T)() -func _[T, T /* ERROR redeclared */ any]() - -// Constraints (incl. any) may be parenthesized. -func _[_ (any)]() {} -func _[_ (interface{})]() {} - -func reverse[T any](list []T) []T { - rlist := make([]T, len(list)) - i := len(list) - for _, x := range list { - i-- - rlist[i] = x - } - return rlist -} - -var _ = reverse /* ERROR cannot use generic function reverse */ -var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3}) -var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3}) -var f = reverse[chan int] -var _ = f(0 /* ERROR cannot use 0 .* as \[\]chan int */ ) - -func swap[A, B any](a A, b B) (B, A) { return b, a } - -var _ = swap /* ERROR single value is expected */ [int, float32](1, 2) -var f32, i = swap[int, float32](swap[float32, int](1, 2)) -var _ float32 = f32 -var _ int = i - -func swapswap[A, B any](a A, b B) (A, B) { - return swap[B, A](b, a) -} - -type F[A, B any] func(A, B) (B, A) - -func min[T interface{ type int }](x, y T) T { - if x < y { - return x - } - return y -} - -func _[T interface{type int, float32}](x, y T) bool { return x < y } -func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y } - -func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T C2[T]](x, y T) bool { return x < y } - -type C1[T any] interface{} -type C2[T any] interface{ type int, float32 } - -func new[T any]() *T { - var x T - return &x -} - -var _ = new /* ERROR cannot use generic function new */ -var _ *int = new[int]() - -func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable - -func f1[T1 any](struct{T1}) int -var _ = f1[int](struct{T1}{}) -type T1 = int - -func f2[t1 any](struct{t1; x float32}) int -var _ = f2[t1](struct{t1; x float32}{}) -type t1 = int - - -func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int - -var _ = f3[int, rune, bool](1, struct{x rune}{}, nil) - -// indexing - -func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int }] (x T, i int) { _ = x[i] } -func _[T interface{ type [10]int, *[20]int, map[int]int }] (x T, i int) { _ = x[i] } -func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] } -func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } - -// indexing with various combinations of map types in type lists (see issue #42616) -func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = x[i] } -func _[T interface{ type []E }, E any](x T, i int) { _ = &x[i] } -func _[T interface{ type map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted -func _[T interface{ type []E, map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] } -func _[T interface{ type []E, map[int]E, map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types -func _[T interface{ type []E, map[string]E }, E any](x T, i int) { _ = x[i /* ERROR cannot use i */ ] } - -// slicing -// TODO(gri) implement this - -func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] } - -// len/cap built-ins - -func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = len(x) } -func _[T interface{ type [10]int }](x T) { _ = len(x) } -func _[T interface{ type []byte }](x T) { _ = len(x) } -func _[T interface{ type map[int]int }](x T) { _ = len(x) } -func _[T interface{ type chan int }](x T) { _ = len(x) } -func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) } - -func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type [10]int }](x T) { _ = cap(x) } -func _[T interface{ type []byte }](x T) { _ = cap(x) } -func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ type chan int }](x T) { _ = cap(x) } -func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) } - -// range iteration - -func _[T interface{}](x T) { - for range x /* ERROR cannot range */ {} -} - -func _[T interface{ type string, []string }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } - for i, e := range x /* ERROR must have the same element type */ { _ = i } - for _, e := range x /* ERROR must have the same element type */ {} - var e rune - _ = e - for _, (e) = range x /* ERROR must have the same element type */ {} -} - - -func _[T interface{ type string, []rune, map[int]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x { _ = i; _ = e } -} - -func _[T interface{ type string, []rune, map[string]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x /* ERROR must have the same key type */ { _ = e } -} - -func _[T interface{ type string, chan int }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value -} - -func _[T interface{ type string, chan<-int }](x T) { - for i := range x /* ERROR send-only channel */ { _ = i } -} - -// type inference checks - -var _ = new() /* ERROR cannot infer T */ - -func f4[A, B, C any](A, B) C - -var _ = f4(1, 2) /* ERROR cannot infer C */ -var _ = f4[int, float32, complex128](1, 2) - -func f5[A, B, C any](A, []*B, struct{f []C}) int - -var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{}) -var _ = f5(0, nil, struct{f []complex128}{}) // ERROR cannot infer -var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{}) - -func f6[A any](A, []A) int - -var _ = f6(0, nil) - -func f6nil[A any](A) int - -var _ = f6nil(nil) // ERROR cannot infer - -// type inference with variadic functions - -func f7[T any](...T) T - -var _ int = f7() /* ERROR cannot infer T */ -var _ int = f7(1) -var _ int = f7(1, 2) -var _ int = f7([]int{}...) -var _ int = f7 /* ERROR cannot use */ ([]float64{}...) -var _ float64 = f7([]float64{}...) -var _ = f7[float64](1, 2.3) -var _ = f7(float64(1), 2.3) -var _ = f7(1, 2.3 /* ERROR does not match */ ) -var _ = f7(1.2, 3 /* ERROR does not match */ ) - -func f8[A, B any](A, B, ...B) int - -var _ = f8(1) /* ERROR not enough arguments */ -var _ = f8(1, 2.3) -var _ = f8(1, 2.3, 3.4, 4.5) -var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) -var _ = f8[int, float64](1, 2.3, 3.4, 4) - -var _ = f8[int, float64](0, 0, nil...) // test case for #18268 - -// init functions cannot have type parameters - -func init() {} -func init[/* ERROR func init must have no type parameters */ _ any]() {} -func init[/* ERROR func init must have no type parameters */ P any]() {} - -type T struct {} - -func (T) m1() {} -// The type checker accepts method type parameters if configured accordingly. -func (T) m2[_ any]() {} -func (T) m3[P any]() {} - -// type inference across parameterized types - -type S1[P any] struct { f P } - -func f9[P any](x S1[P]) - -func _() { - f9[int](S1[int]{42}) - f9(S1[int]{42}) -} - -type S2[A, B, C any] struct{} - -func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) - -func _[P any]() { - f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[P, int, P]{}, S2[P, float32, bool]{}) -} - -// corner case for type inference -// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) - -func f11[T any]() - -func _() { - f11[int]() -} - -// the previous example was extracted from - -func f12[T interface{m() T}]() - -type A[T any] T - -func (a A[T]) m() A[T] - -func _[T any]() { - f12[A[T]]() -} - -// method expressions - -func (_ S1[P]) m() - -func _() { - m := S1[int].m - m(struct { f int }{42}) -} - -func _[T any] (x T) { - m := S1[T].m - m(S1[T]{x}) -} - -// type parameters in methods (generalization) - -type R0 struct{} - -func (R0) _[T any](x T) -func (R0 /* ERROR invalid receiver */ ) _[R0 any]() // scope of type parameters starts at "func" - -type R1[A, B any] struct{} - -func (_ R1[A, B]) m0(A, B) -func (_ R1[A, B]) m1[T any](A, B, T) T -func (_ R1 /* ERROR not a generic type */ [R1, _]) _() -func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B) - -func _() { - var r R1[int, string] - r.m1[rune](42, "foo", 'a') - r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */) - r.m1(42, "foo", 1.2) // using type inference - var _ float64 = r.m1(42, "foo", 1.2) -} - -type I1[A any] interface { - m1(A) -} - -var _ I1[int] = r1[int]{} - -type r1[T any] struct{} - -func (_ r1[T]) m1(T) - -type I2[A, B any] interface { - m1(A) - m2(A) B -} - -var _ I2[int, float32] = R2[int, float32]{} - -type R2[P, Q any] struct{} - -func (_ R2[X, Y]) m1(X) -func (_ R2[X, Y]) m2(X) Y - -// type assertions and type switches over generic types -// NOTE: These are currently disabled because it's unclear what the correct -// approach is, and one can always work around by assigning the variable to -// an interface first. - -// // ReadByte1 corresponds to the ReadByte example in the draft design. -// func ReadByte1[T io.Reader](r T) (byte, error) { -// if br, ok := r.(io.ByteReader); ok { -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // ReadBytes2 is like ReadByte1 but uses a type switch instead. -// func ReadByte2[T io.Reader](r T) (byte, error) { -// switch br := r.(type) { -// case io.ByteReader: -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // type assertions and type switches over generic types are strict -// type I3 interface { -// m(int) -// } -// -// type I4 interface { -// m() int // different signature from I3.m -// } -// -// func _[T I3](x I3, p T) { -// // type assertions and type switches over interfaces are not strict -// _ = x.(I4) -// switch x.(type) { -// case I4: -// } -// -// // type assertions and type switches over generic types are strict -// _ = p /* ERROR cannot have dynamic type I4 */.(I4) -// switch p.(type) { -// case I4 /* ERROR cannot have dynamic type I4 */ : -// } -// } - -// type assertions and type switches over generic types lead to errors for now - -func _[T any](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -func _[T interface{type int}](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -// error messages related to type bounds mention those bounds -type C[P any] interface{} - -func _[P C[P]] (x P) { - x.m /* ERROR x.m undefined */ () -} - -type I interface {} - -func _[P I] (x P) { - x.m /* ERROR interface I has no method m */ () -} - -func _[P interface{}] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} - -func _[P any] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} diff --git a/src/cmd/compile/internal/types2/testdata/check/vardecl.src b/src/cmd/compile/internal/types2/testdata/check/vardecl.src deleted file mode 100644 index 9e48cdf847740e..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/check/vardecl.src +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package vardecl - -// Prerequisites. -import "math" -func f() {} -func g() (x, y int) { return } -var m map[string]int - -// Var decls must have a type or an initializer. -var _ int -var _, _ int - -var _ /* ERROR "expecting type" */ -var _, _ /* ERROR "expecting type" */ -var _, _, _ /* ERROR "expecting type" */ - -// The initializer must be an expression. -var _ = int /* ERROR "not an expression" */ -var _ = f /* ERROR "used as value" */ () - -// Identifier and expression arity must match. -var _, _ = 1, 2 -var _ = 1, 2 /* ERROR "extra init expr 2" */ -var _, _ = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ -var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 - -var _ = g /* ERROR "2-valued g" */ () -var _, _ = g() -var _, _, _ = g /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ () - -var _ = m["foo"] -var _, _ = m["foo"] -var _, _, _ = m /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ ["foo"] - -var _, _ int = 1, 2 -var _ int = 1, 2 /* ERROR "extra init expr 2" */ -var _, _ int = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ -var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 - -var ( - _, _ = 1, 2 - _ = 1, 2 /* ERROR "extra init expr 2" */ - _, _ = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ - _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 - - _ = g /* ERROR "2-valued g" */ () - _, _ = g() - _, _, _ = g /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ () - - _ = m["foo"] - _, _ = m["foo"] - _, _, _ = m /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ ["foo"] - - _, _ int = 1, 2 - _ int = 1, 2 /* ERROR "extra init expr 2" */ - _, _ int = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ - _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 -) - -// Variables declared in function bodies must be 'used'. -type T struct{} -func (r T) _(a, b, c int) (u, v, w int) { - var x1 /* ERROR "declared but not used" */ int - var x2 /* ERROR "declared but not used" */ int - x1 = 1 - (x2) = 2 - - y1 /* ERROR "declared but not used" */ := 1 - y2 /* ERROR "declared but not used" */ := 2 - y1 = 1 - (y1) = 2 - - { - var x1 /* ERROR "declared but not used" */ int - var x2 /* ERROR "declared but not used" */ int - x1 = 1 - (x2) = 2 - - y1 /* ERROR "declared but not used" */ := 1 - y2 /* ERROR "declared but not used" */ := 2 - y1 = 1 - (y1) = 2 - } - - if x /* ERROR "declared but not used" */ := 0; a < b {} - - switch x /* ERROR "declared but not used" */, y := 0, 1; a { - case 0: - _ = y - case 1: - x /* ERROR "declared but not used" */ := 0 - } - - var t interface{} - switch t /* ERROR "declared but not used" */ := t.(type) {} - - switch t /* ERROR "declared but not used" */ := t.(type) { - case int: - } - - switch t /* ERROR "declared but not used" */ := t.(type) { - case int: - case float32, complex64: - t = nil - } - - switch t := t.(type) { - case int: - case float32, complex64: - _ = t - } - - switch t := t.(type) { - case int: - case float32: - case string: - _ = func() string { - return t - } - } - - switch t := t; t /* ERROR "declared but not used" */ := t.(type) {} - - var z1 /* ERROR "declared but not used" */ int - var z2 int - _ = func(a, b, c int) (u, v, w int) { - z1 = a - (z1) = b - a = z2 - return - } - - var s []int - var i /* ERROR "declared but not used" */ , j int - for i, j = range s { - _ = j - } - - for i, j /* ERROR "declared but not used" */ := range s { - _ = func() int { - return i - } - } - return -} - -// Unused variables in function literals must lead to only one error (issue #22524). -func _() { - _ = func() { - var x /* ERROR declared but not used */ int - } -} - -// Invalid variable declarations must not lead to "declared but not used errors". -func _() { - var a x // ERROR undeclared name: x - var b = x // ERROR undeclared name: x - var c int = x // ERROR undeclared name: x - var d, e, f x /* ERROR x */ /* ERROR x */ /* ERROR x */ - var g, h, i = x, x, x /* ERROR x */ /* ERROR x */ /* ERROR x */ - var j, k, l float32 = x, x, x /* ERROR x */ /* ERROR x */ /* ERROR x */ - // but no "declared but not used" errors -} - -// Invalid (unused) expressions must not lead to spurious "declared but not used errors". -func _() { - var a, b, c int - var x, y int - x, y = a /* ERROR cannot assign [0-9]+ values to [0-9]+ variables */ , b, c - _ = x - _ = y -} - -func _() { - var x int - return x /* ERROR no result values expected */ - return math /* ERROR no result values expected */ .Sin(0) -} - -func _() int { - var x, y int - return /* ERROR wrong number of return values */ x, y -} - -// Short variable declarations must declare at least one new non-blank variable. -func _() { - _ := /* ERROR no new variables */ 0 - _, a := 0, 1 - _, a := /* ERROR no new variables */ 0, 1 - _, a, b := 0, 1, 2 - _, _, _ := /* ERROR no new variables */ 0, 1, 2 - - _ = a - _ = b -} - -// Test case for variables depending on function literals (see also #22992). -var A /* ERROR initialization cycle */ = func() int { return A }() - -func _() { - // The function literal below must not see a. - var a = func() int { return a /* ERROR "undeclared name" */ }() - var _ = func() int { return a }() - - // The function literal below must not see x, y, or z. - var x, y, z = 0, 1, func() int { return x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ }() - _, _, _ = x, y, z -} - -// TODO(gri) consolidate other var decl checks in this file \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/examples/functions.go2 b/src/cmd/compile/internal/types2/testdata/examples/functions.go2 deleted file mode 100644 index 0c2a408f02162e..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/examples/functions.go2 +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019 The Go 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 shows some examples of type-parameterized functions. - -package p - -// Reverse is a generic function that takes a []T argument and -// reverses that slice in place. -func Reverse[T any](list []T) { - i := 0 - j := len(list)-1 - for i < j { - list[i], list[j] = list[j], list[i] - i++ - j-- - } -} - -func _() { - // Reverse can be called with an explicit type argument. - Reverse[int](nil) - Reverse[string]([]string{"foo", "bar"}) - Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) - - // Since the type parameter is used for an incoming argument, - // it can be inferred from the provided argument's type. - Reverse([]string{"foo", "bar"}) - Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) - - // But the incoming argument must have a type, even if it's a - // default type. An untyped nil won't work. - // Reverse(nil) // this won't type-check - - // A typed nil will work, though. - Reverse([]int(nil)) -} - -// Certain functions, such as the built-in `new` could be written using -// type parameters. -func new[T any]() *T { - var x T - return &x -} - -// When calling our own `new`, we need to pass the type parameter -// explicitly since there is no (value) argument from which the -// result type could be inferred. We don't try to infer the -// result type from the assignment to keep things simple and -// easy to understand. -var _ = new[int]() -var _ *float64 = new[float64]() // the result type is indeed *float64 - -// A function may have multiple type parameters, of course. -func foo[A, B, C any](a A, b []B, c *C) B { - // do something here - return b[0] -} - -// As before, we can pass type parameters explicitly. -var s = foo[int, string, float64](1, []string{"first"}, new[float64]()) - -// Or we can use type inference. -var _ float64 = foo(42, []float64{1.0}, &s) - -// Type inference works in a straight-forward manner even -// for variadic functions. -func variadic[A, B any](A, B, ...B) int - -// var _ = variadic(1) // ERROR not enough arguments -var _ = variadic(1, 2.3) -var _ = variadic(1, 2.3, 3.4, 4.5) -var _ = variadic[int, float64](1, 2.3, 3.4, 4) - -// Type inference also works in recursive function calls where -// the inferred type is the type parameter of the caller. -func f1[T any](x T) { - f1(x) -} - -func f2a[T any](x, y T) { - f2a(x, y) -} - -func f2b[T any](x, y T) { - f2b(y, x) -} - -func g2a[P, Q any](x P, y Q) { - g2a(x, y) -} - -func g2b[P, Q any](x P, y Q) { - g2b(y, x) -} - -// Here's an example of a recursive function call with variadic -// arguments and type inference inferring the type parameter of -// the caller (i.e., itself). -func max[T interface{ type int }](x ...T) T { - var x0 T - if len(x) > 0 { - x0 = x[0] - } - if len(x) > 1 { - x1 := max(x[1:]...) - if x1 > x0 { - return x1 - } - } - return x0 -} - -// When inferring channel types, the channel direction is ignored -// for the purpose of type inference. Once the type has been in- -// fered, the usual parameter passing rules are applied. -// Thus even if a type can be inferred successfully, the function -// call may not be valid. - -func fboth[T any](chan T) -func frecv[T any](<-chan T) -func fsend[T any](chan<- T) - -func _() { - var both chan int - var recv <-chan int - var send chan<-int - - fboth(both) - fboth(recv /* ERROR cannot use */ ) - fboth(send /* ERROR cannot use */ ) - - frecv(both) - frecv(recv) - frecv(send /* ERROR cannot use */ ) - - fsend(both) - fsend(recv /* ERROR cannot use */) - fsend(send) -} - -func ffboth[T any](func(chan T)) -func ffrecv[T any](func(<-chan T)) -func ffsend[T any](func(chan<- T)) - -func _() { - var both func(chan int) - var recv func(<-chan int) - var send func(chan<- int) - - ffboth(both) - ffboth(recv /* ERROR cannot use */ ) - ffboth(send /* ERROR cannot use */ ) - - ffrecv(both /* ERROR cannot use */ ) - ffrecv(recv) - ffrecv(send /* ERROR cannot use */ ) - - ffsend(both /* ERROR cannot use */ ) - ffsend(recv /* ERROR cannot use */ ) - ffsend(send) -} - -// When inferring elements of unnamed composite parameter types, -// if the arguments are defined types, use their underlying types. -// Even though the matching types are not exactly structurally the -// same (one is a type literal, the other a named type), because -// assignment is permitted, parameter passing is permitted as well, -// so type inference should be able to handle these cases well. - -func g1[T any]([]T) -func g2[T any]([]T, T) -func g3[T any](*T, ...T) - -func _() { - type intSlize []int - g1([]int{}) - g1(intSlize{}) - g2(nil, 0) - - type myString string - var s1 string - g3(nil, "1", myString("2"), "3") - g3(&s1, "1", myString /* ERROR does not match */ ("2"), "3") - _ = s1 - - type myStruct struct{x int} - var s2 myStruct - g3(nil, struct{x int}{}, myStruct{}) - g3(&s2, struct{x int}{}, myStruct{}) - g3(nil, myStruct{}, struct{x int}{}) - g3(&s2, myStruct{}, struct{x int}{}) -} - -// Here's a realistic example. - -func append[T any](s []T, t ...T) []T - -func _() { - var f func() - type Funcs []func() - var funcs Funcs - _ = append(funcs, f) -} - -// Generic type declarations cannot have empty type parameter lists -// (that would indicate a slice type). Thus, generic functions cannot -// have empty type parameter lists, either. This is a syntax error. - -func h[] /* ERROR empty type parameter list */ () - -func _() { - h[] /* ERROR operand */ () -} diff --git a/src/cmd/compile/internal/types2/testdata/examples/inference.go2 b/src/cmd/compile/internal/types2/testdata/examples/inference.go2 deleted file mode 100644 index b47ce758054805..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/examples/inference.go2 +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2021 The Go 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 shows some examples of type inference. - -package p - -type Ordered interface { - type int, float64, string -} - -func min[T Ordered](x, y T) T - -func _() { - // min can be called with explicit instantiation. - _ = min[int](1, 2) - - // Alternatively, the type argument can be inferred from - // one of the arguments. Untyped arguments will be considered - // last. - var x int - _ = min(x, x) - _ = min(x, 1) - _ = min(x, 1.0) - _ = min(1, 2) - _ = min(1, 2.3 /* ERROR default type float64 .* does not match */ ) - - var y float64 - _ = min(1, y) - _ = min(1.2, y) - _ = min(1.2, 3.4) - _ = min(1.2, 3 /* ERROR default type int .* does not match */ ) - - var s string - _ = min(s, "foo") - _ = min("foo", "bar") -} - -func mixed[T1, T2, T3 any](T1, T2, T3) - -func _() { - // mixed can be called with explicit instantiation. - mixed[int, string, bool](0, "", false) - - // Alternatively, partial type arguments may be provided - // (from left to right), and the other may be inferred. - mixed[int, string](0, "", false) - mixed[int](0, "", false) - mixed(0, "", false) - - // Provided type arguments always take precedence over - // inferred types. - mixed[int, string](1.1 /* ERROR cannot use 1.1 */ , "", false) -} - -func related1[Slice interface{type []Elem}, Elem any](s Slice, e Elem) - -func _() { - // related1 can be called with explicit instantiation. - var si []int - related1[[]int, int](si, 0) - - // Alternatively, the 2nd type argument can be inferred - // from the first one through constraint type inference. - var ss []string - _ = related1[[]string] - related1[[]string](ss, "foo") - - // A type argument inferred from another explicitly provided - // type argument overrides whatever value argument type is given. - related1[[]string](ss, 0 /* ERROR cannot use 0 */ ) - - // A type argument may be inferred from a value argument - // and then help infer another type argument via constraint - // type inference. - related1(si, 0) - related1(si, "foo" /* ERROR cannot use "foo" */ ) -} - -func related2[Elem any, Slice interface{type []Elem}](e Elem, s Slice) - -func _() { - // related2 can be called with explicit instantiation. - var si []int - related2[int, []int](0, si) - - // Alternatively, the 2nd type argument can be inferred - // from the first one through constraint type inference. - var ss []string - _ = related2[string] - related2[string]("foo", ss) - - // A type argument may be inferred from a value argument - // and then help infer another type argument via constraint - // type inference. Untyped arguments are always considered - // last. - related2(1.2, []float64{}) - related2(1.0, []int{}) - related2( /* ERROR does not satisfy */ float64(1.0), []int{}) // TODO(gri) fix error position -} diff --git a/src/cmd/compile/internal/types2/testdata/examples/methods.go2 b/src/cmd/compile/internal/types2/testdata/examples/methods.go2 deleted file mode 100644 index 76c6539e1b7e7d..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/examples/methods.go2 +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2019 The Go 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 shows some examples of methods on type-parameterized types. - -package p - -// Parameterized types may have methods. -type T1[A any] struct{ a A } - -// When declaring a method for a parameterized type, the "instantiated" -// receiver type acts as an implicit declaration of the type parameters -// for the receiver type. In the example below, method m1 on type T1 has -// the receiver type T1[A] which declares the type parameter A for use -// with this method. That is, within the method m1, A stands for the -// actual type argument provided to an instantiated T1. -func (t T1[A]) m1() A { return t.a } - -// For instance, if T1 is instantiated with the type int, the type -// parameter A in m1 assumes that type (int) as well and we can write -// code like this: -var x T1[int] -var _ int = x.m1() - -// Because the type parameter provided to a parameterized receiver type -// is declared through that receiver declaration, it must be an identifier. -// It cannot possibly be some other type because the receiver type is not -// instantiated with concrete types, it is standing for the parameterized -// receiver type. -func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {} - -// Note that using what looks like a predeclared identifier, say int, -// as type parameter in this situation is deceptive and considered bad -// style. In m3 below, int is the name of the local receiver type parameter -// and it shadows the predeclared identifier int which then cannot be used -// anymore as expected. -// This is no different from locally redelaring a predeclared identifier -// and usually should be avoided. There are some notable exceptions; e.g., -// sometimes it makes sense to use the identifier "copy" which happens to -// also be the name of a predeclared built-in function. -func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot use 42 .* as int */ } - -// The names of the type parameters used in a parameterized receiver -// type don't have to match the type parameter names in the declaration -// of the type used for the receiver. In our example, even though T1 is -// declared with type parameter named A, methods using that receiver type -// are free to use their own name for that type parameter. That is, the -// name of type parameters is always local to the declaration where they -// are introduced. In our example we can write a method m2 and use the -// name X instead of A for the type parameter w/o any difference. -func (t T1[X]) m4() X { return t.a } - -// If the receiver type is parameterized, type parameters must always be -// provided: this simply follows from the general rule that a parameterized -// type must be instantiated before it can be used. A method receiver -// declaration using a parameterized receiver type is no exception. It is -// simply that such receiver type expressions perform two tasks simultaneously: -// they declare the (local) type parameters and then use them to instantiate -// the receiver type. Forgetting to provide a type parameter leads to an error. -func (t T1 /* ERROR generic type .* without instantiation */ ) m5() {} - -// However, sometimes we don't need the type parameter, and thus it is -// inconvenient to have to choose a name. Since the receiver type expression -// serves as a declaration for its type parameters, we are free to choose the -// blank identifier: -func (t T1[_]) m6() {} - -// Naturally, these rules apply to any number of type parameters on the receiver -// type. Here are some more complex examples. -type T2[A, B, C any] struct { - a A - b B - c C -} - -// Naming of the type parameters is local and has no semantic impact: -func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c } -func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c } -func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c } - -// Type parameters may be left blank if they are not needed: -func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c } -func (t T2[_, _, X]) m5() X { return t.c } -func (t T2[_, _, _]) m6() {} - -// As usual, blank names may be used for any object which we don't care about -// using later. For instance, we may write an unnamed method with a receiver -// that cannot be accessed: -func (_ T2[_, _, _]) _() int { return 42 } - -// Because a receiver parameter list is simply a parameter list, we can -// leave the receiver argument away for receiver types. -type T0 struct{} -func (T0) _() {} -func (T1[A]) _() {} diff --git a/src/cmd/compile/internal/types2/testdata/examples/types.go2 b/src/cmd/compile/internal/types2/testdata/examples/types.go2 deleted file mode 100644 index a7825ed2d9bf7c..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/examples/types.go2 +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2019 The Go 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 shows some examples of generic types. - -package p - -// List is just what it says - a slice of E elements. -type List[E any] []E - -// A generic (parameterized) type must always be instantiated -// before it can be used to designate the type of a variable -// (including a struct field, or function parameter); though -// for the latter cases, the provided type may be another type -// parameter. So: -var _ List[byte] = []byte{} - -// A generic binary tree might be declared as follows. -type Tree[E any] struct { - left, right *Tree[E] - payload E -} - -// A simple instantiation of Tree: -var root1 Tree[int] - -// The actual type parameter provided may be a generic type itself: -var root2 Tree[List[int]] - -// A couple of more complex examples. -// We don't need extra parentheses around the element type of the slices on -// the right (unlike when we use ()'s rather than []'s for type parameters). -var _ List[List[int]] = []List[int]{} -var _ List[List[List[Tree[int]]]] = []List[List[Tree[int]]]{} - -// Type parameters act like type aliases when used in generic types -// in the sense that we can "emulate" a specific type instantiation -// with type aliases. -type T1[P any] struct { - f P -} - -type T2[P any] struct { - f struct { - g P - } -} - -var x1 T1[struct{ g int }] -var x2 T2[int] - -func _() { - // This assignment is invalid because the types of x1, x2 are T1(...) - // and T2(...) respectively, which are two different defined types. - x1 = x2 // ERROR assignment - - // This assignment is valid because the types of x1.f and x2.f are - // both struct { g int }; the type parameters act like type aliases - // and their actual names don't come into play here. - x1.f = x2.f -} - -// We can verify this behavior using type aliases instead: -type T1a struct { - f A1 -} -type A1 = struct { g int } - -type T2a struct { - f struct { - g A2 - } -} -type A2 = int - -var x1a T1a -var x2a T2a - -func _() { - x1a = x2a // ERROR assignment - x1a.f = x2a.f -} - -// Another interesting corner case are generic types that don't use -// their type arguments. For instance: -type T[P any] struct{} - -var xint T[int] -var xbool T[bool] - -// Are these two variables of the same type? After all, their underlying -// types are identical. We consider them to be different because each type -// instantiation creates a new named type, in this case T and T -// even if their underlying types are identical. This is sensible because -// we might still have methods that have different signatures or behave -// differently depending on the type arguments, and thus we can't possibly -// consider such types identical. Consequently: -func _() { - xint = xbool // ERROR assignment -} - -// Generic types cannot be used without instantiation. -var _ T // ERROR cannot use generic type T - -// In type context, generic (parameterized) types cannot be parenthesized before -// being instantiated. See also NOTES entry from 12/4/2019. -var _ (T /* ERROR cannot use generic type T */ )[ /* ERROR unexpected \[ */ int] - -// All types may be parameterized, including interfaces. -type I1[T any] interface{ - m1(T) -} - -// There is no such thing as a variadic generic type. -type _[T ... /* ERROR invalid use of ... */ interface{}] struct{} - -// Generic interfaces may be embedded as one would expect. -type I2 interface { - I1(int) // method! - I1[string] // embedded I1 -} - -func _() { - var x I2 - x.I1(0) - x.m1("foo") -} - -type I0 interface { - m0() -} - -type I3 interface { - I0 - I1[bool] - m(string) -} - -func _() { - var x I3 - x.m0() - x.m1(true) - x.m("foo") -} - -type _ struct { - ( /* ERROR cannot parenthesize */ int8) - ( /* ERROR cannot parenthesize */ *int16) - *( /* ERROR cannot parenthesize */ int32) - List[int] - - int8 /* ERROR int8 redeclared */ - * /* ERROR int16 redeclared */ int16 - List /* ERROR List redeclared */ [int] -} - -// It's possible to declare local types whose underlying types -// are type parameters. As with ordinary type definitions, the -// types underlying properties are "inherited" but the methods -// are not. -func _[T interface{ m(); type int }]() { - type L T - var x L - - // m is not defined on L (it is not "inherited" from - // its underlying type). - x.m /* ERROR x.m undefined */ () - - // But the properties of T, such that as that it supports - // the operations of the types given by its type bound, - // are also the properties of L. - x++ - _ = x - x - - // On the other hand, if we define a local alias for T, - // that alias stands for T as expected. - type A = T - var y A - y.m() - _ = y < 0 -} - -// As a special case, an explicit type argument may be omitted -// from a type parameter bound if the type bound expects exactly -// one type argument. In that case, the type argument is the -// respective type parameter to which the type bound applies. -// Note: We may not permit this syntactic sugar at first. -// Note: This is now disabled. All examples below are adjusted. -type Adder[T any] interface { - Add(T) T -} - -// We don't need to explicitly instantiate the Adder bound -// if we have exactly one type parameter. -func Sum[T Adder[T]](list []T) T { - var sum T - for _, x := range list { - sum = sum.Add(x) - } - return sum -} - -// Valid and invalid variations. -type B0 interface {} -type B1[_ any] interface{} -type B2[_, _ any] interface{} - -func _[T1 B0]() -func _[T1 B1[T1]]() -func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() - -func _[T1, T2 B0]() -func _[T1 B1[T1], T2 B1[T2]]() -func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() - -func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2 - -// When the type argument is left away, the type bound is -// instantiated for each type parameter with that type -// parameter. -// Note: We may not permit this syntactic sugar at first. -func _[A Adder[A], B Adder[B], C Adder[A]]() { - var a A // A's type bound is Adder[A] - a = a.Add(a) - var b B // B's type bound is Adder[B] - b = b.Add(b) - var c C // C's type bound is Adder[A] - a = c.Add(a) -} - -// The type of variables (incl. parameters and return values) cannot -// be an interface with type constraints or be/embed comparable. -type I interface { - type int -} - -var ( - _ interface /* ERROR contains type constraints */ {type int} - _ I /* ERROR contains type constraints */ -) - -func _(I /* ERROR contains type constraints */ ) -func _(x, y, z I /* ERROR contains type constraints */ ) -func _() I /* ERROR contains type constraints */ - -func _() { - var _ I /* ERROR contains type constraints */ -} - -type C interface { - comparable -} - -var _ comparable /* ERROR comparable */ -var _ C /* ERROR comparable */ - -func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ ) - -func _() { - var _ comparable /* ERROR comparable */ - var _ C /* ERROR comparable */ -} - -// Type parameters are never const types, i.e., it's -// not possible to declare a constant of type parameter type. -// (If a type list contains just a single const type, we could -// allow it, but such type lists don't make much sense in the -// first place.) -func _[T interface { type int, float64 }]() { - // not valid - const _ = T /* ERROR not constant */ (0) - const _ T /* ERROR invalid constant type T */ = 1 - - // valid - var _ = T(0) - var _ T = 1 - _ = T(0) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue20583.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue20583.src deleted file mode 100644 index 85f11ecd38367d..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue20583.src +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package issue20583 -const ( - _ = 6e886451608 /* ERROR malformed constant */ /2 - _ = 6e886451608i /* ERROR malformed constant */ /2 - _ = 0 * 1e+1000000000 // ERROR malformed constant - x = 1e100000000 - _ = x*x*x*x*x*x* /* ERROR not representable */ x -) diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue26390.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue26390.src deleted file mode 100644 index b8e67e9bddb6a8..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue26390.src +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package issue26390 - -type A = T - -func (t *T) m() *A { return t } - -type T struct{} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2 deleted file mode 100644 index 2c1299feb09519..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2 +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Examples adjusted to match new [T any] syntax for type parameters. -// Also, previously permitted empty type parameter lists and instantiations -// are now syntax errors. - -package p - -// crash 1 -type nt1[_ any]interface{g /* ERROR undeclared name */ } -type ph1[e nt1[e],g(d /* ERROR undeclared name */ )]s /* ERROR undeclared name */ -func(*ph1[e,e /* ERROR redeclared */ ])h(d /* ERROR undeclared name */ ) - -// crash 2 -// Disabled: empty []'s are now syntax errors. This example leads to too many follow-on errors. -// type Numeric2 interface{t2 /* ERROR not a type */ } -// func t2[T Numeric2](s[]T){0 /* ERROR not a type */ []{s /* ERROR cannot index */ [0][0]}} - -// crash 3 -type t3 *interface{ t3.p /* ERROR no field or method p */ } - -// crash 4 -type Numeric4 interface{t4 /* ERROR not a type */ } -func t4[T Numeric4](s[]T){if( /* ERROR non-boolean */ 0){*s /* ERROR cannot indirect */ [0]}} - -// crash 7 -type foo7 interface { bar() } -type x7[A any] struct{ foo7 } -func main7() { var _ foo7 = x7[int]{} } - -// crash 8 -type foo8[A any] interface { type A } -func bar8[A foo8[A]](a A) {} -func main8() {} - -// crash 9 -type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] } -func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) } - -// crash 12 -var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len /* ERROR must be called */ /* ERROR must be called */ )]c /* ERROR undeclared */ /* ERROR undeclared */ - -// crash 15 -func y15() { var a /* ERROR declared but not used */ interface{ p() } = G15[string]{} } -type G15[X any] s /* ERROR undeclared name */ -func (G15 /* ERROR generic type .* without instantiation */ ) p() - -// crash 16 -type Foo16[T any] r16 /* ERROR not a type */ -func r16[T any]() Foo16[Foo16[T]] - -// crash 17 -type Y17 interface{ c() } -type Z17 interface { - c() Y17 - Y17 /* ERROR duplicate method */ -} -func F17[T Z17](T) - -// crash 18 -type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ]) - -// crash 19 -type Z19 [][[]Z19{}[0][0]]c19 /* ERROR undeclared */ - -// crash 20 -type Z20 /* ERROR illegal cycle */ interface{ Z20 } -func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) } - -// crash 21 -type Z21 /* ERROR illegal cycle */ interface{ Z21 } -func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) } - -// crash 24 -type T24[P any] P -func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() } - -// crash 25 -type T25[A any] int -func (t T25[A]) m1() {} -var x T25 /* ERROR without instantiation */ .m1 - -// crash 26 -type T26 = interface{ F26[ /* ERROR cannot have type parameters */ Z any]() } -func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ /* ERROR missing method */ [] /* ERROR operand */ } - -// crash 27 -func e27[T any]() interface{ x27 /* ERROR not a type */ } -func x27() { e27( /* ERROR cannot infer T */ ) } \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2 deleted file mode 100644 index 9bc26f35461345..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2 +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -import "fmt" - -// Minimal test case. -func _[T interface{type T}](x T) T{ - return x -} - -// Test case from issue. -type constr[T any] interface { - type T -} - -func Print[T constr[T]](s []T) { - for _, v := range s { - fmt.Print(v) - } -} - -func f() { - Print([]string{"Hello, ", "playground\n"}) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2 deleted file mode 100644 index 316ab1982e89cf..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2 +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type Number interface { - int /* ERROR int is not an interface */ - float64 /* ERROR float64 is not an interface */ -} - -func Add[T Number](a, b T) T { - return a /* ERROR not defined */ + b -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2 deleted file mode 100644 index 75491e7e26f00c..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2 +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type T0 interface{ -} - -type T1 interface{ - type int -} - -type T2 interface{ - comparable -} - -type T3 interface { - T0 - T1 - T2 -} - -func _() { - _ = T0(0) - _ = T1 /* ERROR cannot use interface T1 in conversion */ (1) - _ = T2 /* ERROR cannot use interface T2 in conversion */ (2) - _ = T3 /* ERROR cannot use interface T3 in conversion */ (3) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2 deleted file mode 100644 index df621a4c1730db..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2 +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// Do not report a duplicate type error for this type list. -// (Check types after interfaces have been completed.) -type _ interface { - type interface{ Error() string }, interface{ String() string } -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2 deleted file mode 100644 index 55464e6b7759f0..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2 +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// A constraint must be an interface; it cannot -// be a type parameter, for instance. -func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]() diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2 deleted file mode 100644 index e19b6770bfe4bd..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2 +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) -func _() { - f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{}) -} - -// simplified test case from issue -func f2[T any](_ []T, _ func(T)) -func _() { - f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {}) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2 deleted file mode 100644 index f70b8d0ce03887..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2 +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type Optional[T any] struct {} - -func (_ Optional[T]) Val() (T, bool) - -type Box[T any] interface { - Val() (T, bool) -} - -func f[V interface{}, A, B Box[V]]() {} - -func _() { - f[int, Optional[int], Optional[int]]() - _ = f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]] - // TODO(gri) Provide better position information here. - // See TODO in call.go, Checker.arguments. - f[int, Optional[int], Optional[string]]( /* ERROR does not satisfy Box */ ) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2 deleted file mode 100644 index b7ab68818e9587..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2 +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -func _[T interface{type map[string]int}](x T) { - _ = x == nil -} - -// simplified test case from issue - -type PathParamsConstraint interface { - type map[string]string, []struct{key, value string} -} - -type PathParams[T PathParamsConstraint] struct { - t T -} - -func (pp *PathParams[T]) IsNil() bool { - return pp.t == nil // this must succeed -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2 deleted file mode 100644 index abac141d7f08dd..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2 +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type T[P any] P -type A = T -var x A[int] -var _ A /* ERROR cannot use generic type */ - -type B = T[int] -var y B = x -var _ B /* ERROR not a generic type */ [int] - -// test case from issue - -type Vector[T any] []T -type VectorAlias = Vector -var v Vector[int] diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2 deleted file mode 100644 index 76e7e369ca12bb..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2 +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check "infinite expansion" cycle errors across instantiated types. - -package p - -type E0[P any] P -type E1[P any] *P -type E2[P any] struct{ P } -type E3[P any] struct{ *P } - -type T0 /* ERROR illegal cycle */ struct { - _ E0[T0] -} - -type T0_ /* ERROR illegal cycle */ struct { - E0[T0_] -} - -type T1 struct { - _ E1[T1] -} - -type T2 /* ERROR illegal cycle */ struct { - _ E2[T2] -} - -type T3 struct { - _ E3[T3] -} - -// some more complex cases - -type T4 /* ERROR illegal cycle */ struct { - _ E0[E2[T4]] -} - -type T5 struct { - _ E0[E2[E0[E1[E2[[10]T5]]]]] -} - -type T6 /* ERROR illegal cycle */ struct { - _ E0[[10]E2[E0[E2[E2[T6]]]]] -} - -type T7 struct { - _ E0[[]E2[E0[E2[E2[T6]]]]] -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2 deleted file mode 100644 index c2b460902cc643..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2 +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type T[P any] interface{ - P // ERROR P is a type parameter, not an interface -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2 deleted file mode 100644 index 3db4eae0123930..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2 +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type policy[K, V any] interface{} -type LRU[K, V any] struct{} - -func NewCache[K, V any](p policy[K, V]) - -func _() { - var lru LRU[int, string] - NewCache[int, string](&lru) - NewCache(& /* ERROR does not match policy\[K, V\] \(cannot infer K and V\) */ lru) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2 deleted file mode 100644 index 8948d61caa477f..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2 +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type A[T any] int - -func (A[T]) m(A[T]) - -func f[P interface{m(P)}]() - -func _() { - _ = f[A[int]] -} \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2 deleted file mode 100644 index 747aab49dd1639..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2 +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -func _() { - NewS( /* ERROR cannot infer T */ ) .M() -} - -type S struct {} - -func NewS[T any]() *S - -func (_ *S /* ERROR S is not a generic type */ [T]) M() diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2 deleted file mode 100644 index 5d97855f8a172f..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2 +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -import "unsafe" - -func _[T any](x T) { - _ = unsafe /* ERROR undefined */ .Alignof(x) - _ = unsafe /* ERROR undefined */ .Sizeof(x) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2 deleted file mode 100644 index 0269c3a62ce6d0..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2 +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type T[_ any] int - -func f[_ any]() -func g[_, _ any]() - -func _() { - _ = f[T /* ERROR without instantiation */ ] - _ = g[T /* ERROR without instantiation */ , T /* ERROR without instantiation */ ] -} \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2 deleted file mode 100644 index 61f766bcbd7891..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2 +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// Test case from issue. - -type Nat interface { - type Zero, Succ -} - -type Zero struct{} -type Succ struct{ - Nat // ERROR interface contains type constraints -} - -// Struct tests. - -type I1 interface { - comparable -} - -type I2 interface { - type int -} - -type I3 interface { - I1 - I2 -} - -type _ struct { - f I1 // ERROR interface is .* comparable -} - -type _ struct { - comparable // ERROR interface is .* comparable -} - -type _ struct{ - I1 // ERROR interface is .* comparable -} - -type _ struct{ - I2 // ERROR interface contains type constraints -} - -type _ struct{ - I3 // ERROR interface contains type constraints -} - -// General composite types. - -type ( - _ [10]I1 // ERROR interface is .* comparable - _ [10]I2 // ERROR interface contains type constraints - - _ []I1 // ERROR interface is .* comparable - _ []I2 // ERROR interface contains type constraints - - _ *I3 // ERROR interface contains type constraints - _ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints - _ chan I3 // ERROR interface contains type constraints - _ func(I1 /* ERROR interface is .* comparable */ ) - _ func() I2 // ERROR interface contains type constraints -) - -// Other cases. - -var _ = [...]I3 /* ERROR interface contains type constraints */ {} - -func _(x interface{}) { - _ = x.(I3 /* ERROR interface contains type constraints */ ) -} - -type T1[_ any] struct{} -type T3[_, _, _ any] struct{} -var _ T1[I2 /* ERROR interface contains type constraints */ ] -var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32] - -func f1[_ any]() int -var _ = f1[I2 /* ERROR interface contains type constraints */ ]() -func f3[_, _, _ any]() int -var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]() - -func _(x interface{}) { - switch x.(type) { - case I2 /* ERROR interface contains type constraints */ : - } -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2 deleted file mode 100644 index 698cb8a16bad26..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2 +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -func _[T any](x interface{}){ - switch x.(type) { - case T: // ok to use a type parameter - case int: - } - - switch x.(type) { - case T: - case T /* ERROR duplicate case */ : - } -} - -type constraint interface { - type int -} - -func _[T constraint](x interface{}){ - switch x.(type) { - case T: // ok to use a type parameter even if type list contains int - case int: - } -} - -func _(x constraint /* ERROR contains type constraints */ ) { - switch x /* ERROR contains type constraints */ .(type) { - } -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42987.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42987.src deleted file mode 100644 index 8aa3544272e743..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42987.src +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Check that there is only one error (no follow-on errors). - -package p -var _ = [ /* ERROR invalid use of .* array */ ...]byte("foo") diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43110.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43110.src deleted file mode 100644 index 4a469452392b5c..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43110.src +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -type P *struct{} - -func _() { - // want an error even if the switch is empty - var a struct{ _ func() } - switch a /* ERROR cannot switch on a */ { - } - - switch a /* ERROR cannot switch on a */ { - case a: // no follow-on error here - } - - // this is ok because f can be compared to nil - var f func() - switch f { - } - - switch f { - case nil: - } - - switch (func())(nil) { - case nil: - } - - switch (func())(nil) { - case f /* ERROR cannot compare */ : - } - - switch nil /* ERROR use of untyped nil in switch expression */ { - } - - // this is ok - switch P(nil) { - case P(nil): - } -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43124.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43124.src deleted file mode 100644 index 7e48c2211b2c42..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43124.src +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -var _ = int(0 /* ERROR invalid use of \.\.\. in type conversion */ ...) - -// test case from issue - -type M []string - -var ( - x = []string{"a", "b"} - _ = M(x /* ERROR invalid use of \.\.\. in type conversion */ ...) -) diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43125.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43125.src deleted file mode 100644 index c2bd970e251c24..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43125.src +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -var _ = new(- /* ERROR not a type */ 1) -var _ = new(1 /* ERROR not a type */ + 1) diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43190.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43190.src deleted file mode 100644 index ae42719ad75c5e..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43190.src +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -import ; // ERROR missing import path -import -var /* ERROR missing import path */ _ int -import .; // ERROR missing import path - -import () -import (.) // ERROR missing import path -import ( - "fmt" - . -) // ERROR missing import path - -var _ = fmt.Println // avoid imported but not used error diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2 deleted file mode 100644 index b1e42497e8586c..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2 +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 The Go Authors. 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[F interface{type *Q}, G interface{type *R}, Q, R any](q Q, r R) {} - -func _() { - f[*float64, *int](1, 2) - f[*float64](1, 2) - f(1, 2) -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2 deleted file mode 100644 index 65662cdc766293..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2 +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func main() { - some /* ERROR "undeclared name" */ [int, int]() -} - -type N[T any] struct{} - -var _ N[] /* ERROR expecting type */ - -type I interface { - type map[int]int, []int -} - -func _[T I](i, j int) { - var m map[int]int - _ = m[i, j /* ERROR more than one index */ ] - - var a [3]int - _ = a[i, j /* ERROR more than one index */ ] - - var s []int - _ = s[i, j /* ERROR more than one index */ ] - - var t T - // TODO(gri) fix multiple error below - _ = t[i, j /* ERROR more than one index */ /* ERROR more than one index */ ] -} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2 deleted file mode 100644 index 7678e348ef99ce..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2 +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package issue45985 - -// TODO(gri): this error should be on app[int] below. -func app[S /* ERROR "type S = S does not match" */ interface{ type []T }, T any](s S, e T) S { - return append(s, e) -} - -func _() { - _ = app[int] -} diff --git a/src/cmd/compile/internal/types2/testdata/local/issue47996.go b/src/cmd/compile/internal/types2/testdata/local/issue47996.go new file mode 100644 index 00000000000000..2c4b6610fecb69 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/local/issue47996.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. 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 + +// don't crash +func T /* ERROR missing */ [P] /* ERROR missing */ m /* ERROR unexpected */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ diff --git a/src/cmd/compile/internal/types2/testdata/manual.go b/src/cmd/compile/internal/types2/testdata/manual.go new file mode 100644 index 00000000000000..96d4ba67c225ff --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/manual.go @@ -0,0 +1,8 @@ +// Copyright 2022 The Go 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 is tested when running "go test -run Manual" +// without source arguments. Use for one-off debugging. + +package p diff --git a/src/cmd/compile/internal/types2/testdata/manual.go2 b/src/cmd/compile/internal/types2/testdata/manual.go2 deleted file mode 100644 index efe13cf8bc56c1..00000000000000 --- a/src/cmd/compile/internal/types2/testdata/manual.go2 +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2021 The Go 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 is tested when running "go test -run Manual" -// without source arguments. Use for one-off debugging. - -package p diff --git a/src/cmd/compile/internal/types2/tuple.go b/src/cmd/compile/internal/types2/tuple.go new file mode 100644 index 00000000000000..1356aae0b018a4 --- /dev/null +++ b/src/cmd/compile/internal/types2/tuple.go @@ -0,0 +1,34 @@ +// 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 types2 + +// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. +// Tuples are used as components of signatures and to represent the type of multiple +// assignments; they are not first class types of Go. +type Tuple struct { + vars []*Var +} + +// NewTuple returns a new tuple for the given variables. +func NewTuple(x ...*Var) *Tuple { + if len(x) > 0 { + return &Tuple{vars: x} + } + return nil +} + +// Len returns the number variables of tuple t. +func (t *Tuple) Len() int { + if t != nil { + return len(t.vars) + } + return 0 +} + +// At returns the i'th variable of tuple t. +func (t *Tuple) At(i int) *Var { return t.vars[i] } + +func (t *Tuple) Underlying() Type { return t } +func (t *Tuple) String() string { return TypeString(t, nil) } diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go index e6c260ff6790c6..0fe39dbca4645d 100644 --- a/src/cmd/compile/internal/types2/type.go +++ b/src/cmd/compile/internal/types2/type.go @@ -4,990 +4,121 @@ package types2 -import ( - "cmd/compile/internal/syntax" - "fmt" - "sync/atomic" -) - // A Type represents a type of Go. // All types implement the Type interface. type Type interface { - // Underlying returns the underlying type of a type - // w/o following forwarding chains. Only used by - // client packages (here for backward-compatibility). + // Underlying returns the underlying type of a type. Underlying() Type // String returns a string representation of a type. String() string } -// BasicKind describes the kind of basic type. -type BasicKind int - -const ( - Invalid BasicKind = iota // type is invalid - - // predeclared types - Bool - Int - Int8 - Int16 - Int32 - Int64 - Uint - Uint8 - Uint16 - Uint32 - Uint64 - Uintptr - Float32 - Float64 - Complex64 - Complex128 - String - UnsafePointer - - // types for untyped values - UntypedBool - UntypedInt - UntypedRune - UntypedFloat - UntypedComplex - UntypedString - UntypedNil - - // aliases - Byte = Uint8 - Rune = Int32 -) - -// BasicInfo is a set of flags describing properties of a basic type. -type BasicInfo int - -// Properties of basic types. -const ( - IsBoolean BasicInfo = 1 << iota - IsInteger - IsUnsigned - IsFloat - IsComplex - IsString - IsUntyped - - IsOrdered = IsInteger | IsFloat | IsString - IsNumeric = IsInteger | IsFloat | IsComplex - IsConstType = IsBoolean | IsNumeric | IsString -) - -// A Basic represents a basic type. -type Basic struct { - kind BasicKind - info BasicInfo - name string -} - -// Kind returns the kind of basic type b. -func (b *Basic) Kind() BasicKind { return b.kind } - -// Info returns information about properties of basic type b. -func (b *Basic) Info() BasicInfo { return b.info } - -// Name returns the name of basic type b. -func (b *Basic) Name() string { return b.name } - -// An Array represents an array type. -type Array struct { - len int64 - elem Type -} - -// NewArray returns a new array type for the given element type and length. -// A negative length indicates an unknown length. -func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} } - -// Len returns the length of array a. -// A negative result indicates an unknown length. -func (a *Array) Len() int64 { return a.len } - -// Elem returns element type of array a. -func (a *Array) Elem() Type { return a.elem } - -// A Slice represents a slice type. -type Slice struct { - elem Type -} - -// NewSlice returns a new slice type for the given element type. -func NewSlice(elem Type) *Slice { return &Slice{elem: elem} } - -// Elem returns the element type of slice s. -func (s *Slice) Elem() Type { return s.elem } - -// A Struct represents a struct type. -type Struct struct { - fields []*Var - tags []string // field tags; nil if there are no tags -} - -// NewStruct returns a new struct with the given fields and corresponding field tags. -// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be -// only as long as required to hold the tag with the largest index i. Consequently, -// if no field has a tag, tags may be nil. -func NewStruct(fields []*Var, tags []string) *Struct { - var fset objset - for _, f := range fields { - if f.name != "_" && fset.insert(f) != nil { - panic("multiple fields with the same name") - } - } - if len(tags) > len(fields) { - panic("more tags than fields") - } - return &Struct{fields: fields, tags: tags} -} - -// NumFields returns the number of fields in the struct (including blank and embedded fields). -func (s *Struct) NumFields() int { return len(s.fields) } - -// Field returns the i'th field for 0 <= i < NumFields(). -func (s *Struct) Field(i int) *Var { return s.fields[i] } - -// Tag returns the i'th field tag for 0 <= i < NumFields(). -func (s *Struct) Tag(i int) string { - if i < len(s.tags) { - return s.tags[i] - } - return "" -} - -// A Pointer represents a pointer type. -type Pointer struct { - base Type // element type -} - -// NewPointer returns a new pointer type for the given element (base) type. -func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } - -// Elem returns the element type for the given pointer p. -func (p *Pointer) Elem() Type { return p.base } - -// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. -// Tuples are used as components of signatures and to represent the type of multiple -// assignments; they are not first class types of Go. -type Tuple struct { - vars []*Var -} - -// NewTuple returns a new tuple for the given variables. -func NewTuple(x ...*Var) *Tuple { - if len(x) > 0 { - return &Tuple{vars: x} - } - // TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer; - // it's too subtle and causes problems. - return nil -} - -// Len returns the number variables of tuple t. -func (t *Tuple) Len() int { - if t != nil { - return len(t.vars) - } - return 0 -} - -// At returns the i'th variable of tuple t. -func (t *Tuple) At(i int) *Var { return t.vars[i] } - -// A Signature represents a (non-builtin) function or method type. -// The receiver is ignored when comparing signatures for identity. -type Signature struct { - // We need to keep the scope in Signature (rather than passing it around - // and store it in the Func Object) because when type-checking a function - // literal we call the general type checker which returns a general Type. - // We then unpack the *Signature and use the scope for the literal body. - rparams []*TypeName // receiver type parameters from left to right; or nil - tparams []*TypeName // type parameters from left to right; or nil - scope *Scope // function scope, present for package-local signatures - recv *Var // nil if not a method - params *Tuple // (incoming) parameters from left to right; or nil - results *Tuple // (outgoing) results from left to right; or nil - variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) -} - -// NewSignature returns a new function type for the given receiver, parameters, -// and results, either of which may be nil. If variadic is set, the function -// is variadic, it must have at least one parameter, and the last parameter -// must be of unnamed slice type. -func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { - if variadic { - n := params.Len() - if n == 0 { - panic("types2.NewSignature: variadic function must have at least one parameter") - } - if _, ok := params.At(n - 1).typ.(*Slice); !ok { - panic("types2.NewSignature: variadic parameter must be of unnamed slice type") - } - } - return &Signature{recv: recv, params: params, results: results, variadic: variadic} -} - -// Recv returns the receiver of signature s (if a method), or nil if a -// function. It is ignored when comparing signatures for identity. -// -// For an abstract method, Recv returns the enclosing interface either -// as a *Named or an *Interface. Due to embedding, an interface may -// contain methods whose receiver type is a different interface. -func (s *Signature) Recv() *Var { return s.recv } - -// TParams returns the type parameters of signature s, or nil. -func (s *Signature) TParams() []*TypeName { return s.tparams } - -// RParams returns the receiver type params of signature s, or nil. -func (s *Signature) RParams() []*TypeName { return s.rparams } - -// SetTParams sets the type parameters of signature s. -func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams } - -// Params returns the parameters of signature s, or nil. -func (s *Signature) Params() *Tuple { return s.params } - -// Results returns the results of signature s, or nil. -func (s *Signature) Results() *Tuple { return s.results } - -// Variadic reports whether the signature s is variadic. -func (s *Signature) Variadic() bool { return s.variadic } - -// A Sum represents a set of possible types. -// Sums are currently used to represent type lists of interfaces -// and thus the underlying types of type parameters; they are not -// first class types of Go. -type Sum struct { - types []Type // types are unique -} - -// NewSum returns a new Sum type consisting of the provided -// types if there are more than one. If there is exactly one -// type, it returns that type. If the list of types is empty -// the result is nil. -func NewSum(types []Type) Type { - if len(types) == 0 { - return nil - } - - // What should happen if types contains a sum type? - // Do we flatten the types list? For now we check - // and panic. This should not be possible for the - // current use case of type lists. - // TODO(gri) Come up with the rules for sum types. - for _, t := range types { - if _, ok := t.(*Sum); ok { - panic("sum type contains sum type - unimplemented") - } - } - - if len(types) == 1 { - return types[0] +// under returns the true expanded underlying type. +// If it doesn't exist, the result is Typ[Invalid]. +// under must only be called when a type is known +// to be fully set up. +func under(t Type) Type { + if t, _ := t.(*Named); t != nil { + return t.under() } - return &Sum{types: types} + return t.Underlying() } -// is reports whether all types in t satisfy pred. -func (s *Sum) is(pred func(Type) bool) bool { - if s == nil { - return false +// If t is not a type parameter, coreType returns the underlying type. +// If t is a type parameter, coreType returns the single underlying +// type of all types in its type set if it exists, or nil otherwise. If the +// type set contains only unrestricted and restricted channel types (with +// identical element types), the single underlying type is the restricted +// channel type if the restrictions are always the same, or nil otherwise. +func coreType(t Type) Type { + tpar, _ := t.(*TypeParam) + if tpar == nil { + return under(t) } - for _, t := range s.types { - if !pred(t) { - return false - } - } - return true -} - -// An Interface represents an interface type. -type Interface struct { - methods []*Func // ordered list of explicitly declared methods - types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name) - embeddeds []Type // ordered list of explicitly embedded types - allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) - allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name) - - obj Object // type declaration defining this interface; or nil (for better error messages) -} - -// unpack unpacks a type into a list of types. -// TODO(gri) Try to eliminate the need for this function. -func unpack(typ Type) []Type { - if typ == nil { - return nil - } - if sum := asSum(typ); sum != nil { - return sum.types - } - return []Type{typ} -} - -// is reports whether interface t represents types that all satisfy pred. -func (t *Interface) is(pred func(Type) bool) bool { - if t.allTypes == nil { - return false // we must have at least one type! (was bug) - } - for _, t := range unpack(t.allTypes) { - if !pred(t) { + var su Type + if tpar.underIs(func(u Type) bool { + if u == nil { return false } - } - return true -} - -// emptyInterface represents the empty (completed) interface -var emptyInterface = Interface{allMethods: markComplete} - -// markComplete is used to mark an empty interface as completely -// set up by setting the allMethods field to a non-nil empty slice. -var markComplete = make([]*Func, 0) - -// NewInterface returns a new (incomplete) interface for the given methods and embedded types. -// Each embedded type must have an underlying type of interface type. -// NewInterface takes ownership of the provided methods and may modify their types by setting -// missing receivers. To compute the method set of the interface, Complete must be called. -// -// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types -// to be embedded. This is necessary for interfaces that embed alias type names referring to -// non-defined (literal) interface types. -func NewInterface(methods []*Func, embeddeds []*Named) *Interface { - tnames := make([]Type, len(embeddeds)) - for i, t := range embeddeds { - tnames[i] = t - } - return NewInterfaceType(methods, tnames) -} - -// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types. -// Each embedded type must have an underlying type of interface type (this property is not -// verified for defined types, which may be in the process of being set up and which don't -// have a valid underlying type yet). -// NewInterfaceType takes ownership of the provided methods and may modify their types by setting -// missing receivers. To compute the method set of the interface, Complete must be called. -func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface { - if len(methods) == 0 && len(embeddeds) == 0 { - return &emptyInterface - } - - // set method receivers if necessary - typ := new(Interface) - for _, m := range methods { - if sig := m.typ.(*Signature); sig.recv == nil { - sig.recv = NewVar(m.pos, m.pkg, "", typ) - } - } - - // All embedded types should be interfaces; however, defined types - // may not yet be fully resolved. Only verify that non-defined types - // are interfaces. This matches the behavior of the code before the - // fix for #25301 (issue #25596). - for _, t := range embeddeds { - if _, ok := t.(*Named); !ok && !IsInterface(t) { - panic("embedded type is not an interface") - } - } - - // sort for API stability - sortMethods(methods) - sortTypes(embeddeds) - - typ.methods = methods - typ.embeddeds = embeddeds - return typ -} - -// NumExplicitMethods returns the number of explicitly declared methods of interface t. -func (t *Interface) NumExplicitMethods() int { return len(t.methods) } - -// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods(). -// The methods are ordered by their unique Id. -func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] } - -// NumEmbeddeds returns the number of embedded types in interface t. -func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) } - -// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds(). -// The result is nil if the i'th embedded type is not a defined type. -// -// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types. -func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname } - -// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds(). -func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] } - -// NumMethods returns the total number of methods of interface t. -// The interface must have been completed. -func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) } - -func (t *Interface) assertCompleteness() { - if t.allMethods == nil { - panic("interface is incomplete") - } -} - -// Method returns the i'th method of interface t for 0 <= i < t.NumMethods(). -// The methods are ordered by their unique Id. -// The interface must have been completed. -func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] } - -// Empty reports whether t is the empty interface. -func (t *Interface) Empty() bool { - if t.allMethods != nil { - // interface is complete - quick test - // A non-nil allTypes may still be empty and represents the bottom type. - return len(t.allMethods) == 0 && t.allTypes == nil - } - return !t.iterate(func(t *Interface) bool { - return len(t.methods) > 0 || t.types != nil - }, nil) -} - -// HasTypeList reports whether interface t has a type list, possibly from an embedded type. -func (t *Interface) HasTypeList() bool { - if t.allMethods != nil { - // interface is complete - quick test - return t.allTypes != nil - } - - return t.iterate(func(t *Interface) bool { - return t.types != nil - }, nil) -} - -// IsComparable reports whether interface t is or embeds the predeclared interface "comparable". -func (t *Interface) IsComparable() bool { - if t.allMethods != nil { - // interface is complete - quick test - _, m := lookupMethod(t.allMethods, nil, "==") - return m != nil - } - - return t.iterate(func(t *Interface) bool { - _, m := lookupMethod(t.methods, nil, "==") - return m != nil - }, nil) -} - -// IsConstraint reports t.HasTypeList() || t.IsComparable(). -func (t *Interface) IsConstraint() bool { - if t.allMethods != nil { - // interface is complete - quick test - if t.allTypes != nil { - return true - } - _, m := lookupMethod(t.allMethods, nil, "==") - return m != nil - } - - return t.iterate(func(t *Interface) bool { - if t.types != nil { - return true - } - _, m := lookupMethod(t.methods, nil, "==") - return m != nil - }, nil) -} - -// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true. -// iterate reports whether any call to f returned true. -func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool { - if f(t) { - return true - } - for _, e := range t.embeddeds { - // e should be an interface but be careful (it may be invalid) - if e := asInterface(e); e != nil { - // Cyclic interfaces such as "type E interface { E }" are not permitted - // but they are still constructed and we need to detect such cycles. - if seen[e] { - continue - } - if seen == nil { - seen = make(map[*Interface]bool) - } - seen[e] = true - if e.iterate(f, seen) { - return true + if su != nil { + u = match(su, u) + if u == nil { + return false } } - } - return false -} - -// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ. -// If the type list is empty (absent), typ trivially satisfies the interface. -// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive -// "implements" predicate. -func (t *Interface) isSatisfiedBy(typ Type) bool { - t.Complete() - if t.allTypes == nil { + // su == nil || match(su, u) != nil + su = u return true + }) { + return su } - types := unpack(t.allTypes) - return includes(types, typ) || includes(types, under(typ)) + return nil } -// Complete computes the interface's method set. It must be called by users of -// NewInterfaceType and NewInterface after the interface's embedded types are -// fully defined and before using the interface type in any way other than to -// form other types. The interface must not contain duplicate methods or a -// panic occurs. Complete returns the receiver. -func (t *Interface) Complete() *Interface { - // TODO(gri) consolidate this method with Checker.completeInterface - if t.allMethods != nil { - return t +// coreString is like coreType but also considers []byte +// and strings as identical. In this case, if successful and we saw +// a string, the result is of type (possibly untyped) string. +func coreString(t Type) Type { + tpar, _ := t.(*TypeParam) + if tpar == nil { + return under(t) // string or untyped string } - t.allMethods = markComplete // avoid infinite recursion - - var todo []*Func - var methods []*Func - var seen objset - addMethod := func(m *Func, explicit bool) { - switch other := seen.insert(m); { - case other == nil: - methods = append(methods, m) - case explicit: - panic("duplicate method " + m.name) - default: - // check method signatures after all locally embedded interfaces are computed - todo = append(todo, m, other.(*Func)) - } - } - - for _, m := range t.methods { - addMethod(m, true) - } - - allTypes := t.types - - for _, typ := range t.embeddeds { - utyp := under(typ) - etyp := asInterface(utyp) - if etyp == nil { - if utyp != Typ[Invalid] { - panic(fmt.Sprintf("%s is not an interface", typ)) - } - continue + var su Type + hasString := false + if tpar.underIs(func(u Type) bool { + if u == nil { + return false } - etyp.Complete() - for _, m := range etyp.allMethods { - addMethod(m, false) + if isString(u) { + u = NewSlice(universeByte) + hasString = true } - allTypes = intersect(allTypes, etyp.allTypes) - } - - for i := 0; i < len(todo); i += 2 { - m := todo[i] - other := todo[i+1] - if !Identical(m.typ, other.typ) { - panic("duplicate method " + m.name) + if su != nil { + u = match(su, u) + if u == nil { + return false + } } - } - - if methods != nil { - sortMethods(methods) - t.allMethods = methods - } - t.allTypes = allTypes - - return t -} - -// A Map represents a map type. -type Map struct { - key, elem Type -} - -// NewMap returns a new map for the given key and element types. -func NewMap(key, elem Type) *Map { - return &Map{key: key, elem: elem} -} - -// Key returns the key type of map m. -func (m *Map) Key() Type { return m.key } - -// Elem returns the element type of map m. -func (m *Map) Elem() Type { return m.elem } - -// A Chan represents a channel type. -type Chan struct { - dir ChanDir - elem Type -} - -// A ChanDir value indicates a channel direction. -type ChanDir int - -// The direction of a channel is indicated by one of these constants. -const ( - SendRecv ChanDir = iota - SendOnly - RecvOnly -) - -// NewChan returns a new channel type for the given direction and element type. -func NewChan(dir ChanDir, elem Type) *Chan { - return &Chan{dir: dir, elem: elem} -} - -// Dir returns the direction of channel c. -func (c *Chan) Dir() ChanDir { return c.dir } - -// Elem returns the element type of channel c. -func (c *Chan) Elem() Type { return c.elem } - -// TODO(gri) Clean up Named struct below; specifically the fromRHS field (can we use underlying?). - -// A Named represents a named (defined) type. -type Named struct { - check *Checker // for Named.under implementation - info typeInfo // for cycle detection - obj *TypeName // corresponding declared object - orig *Named // original, uninstantiated type - fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) - underlying Type // possibly a *Named during setup; never a *Named once set up completely - tparams []*TypeName // type parameters, or nil - targs []Type // type arguments (after instantiation), or nil - methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily -} - -// NewNamed returns a new named type for the given type name, underlying type, and associated methods. -// If the given type name obj doesn't have a type yet, its type is set to the returned named type. -// The underlying type must not be a *Named. -func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { - if _, ok := underlying.(*Named); ok { - panic("types2.NewNamed: underlying type must not be *Named") - } - return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods) -} - -// newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams []*TypeName, methods []*Func) *Named { - typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods} - if typ.orig == nil { - typ.orig = typ - } - if obj.typ == nil { - obj.typ = typ - } - return typ -} - -// Obj returns the type name for the named type t. -func (t *Named) Obj() *TypeName { return t.obj } - -// Orig returns the original generic type an instantiated type is derived from. -// If t is not an instantiated type, the result is t. -func (t *Named) Orig() *Named { return t.orig } - -// TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. - -// TParams returns the type parameters of the named type t, or nil. -// The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) TParams() []*TypeName { return t.tparams } - -// SetTParams sets the type parameters of the named type t. -func (t *Named) SetTParams(tparams []*TypeName) { t.tparams = tparams } - -// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated. -func (t *Named) TArgs() []Type { return t.targs } - -// SetTArgs sets the type arguments of the named type t. -func (t *Named) SetTArgs(args []Type) { t.targs = args } - -// NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.methods) } - -// Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). -func (t *Named) Method(i int) *Func { return t.methods[i] } - -// SetUnderlying sets the underlying type and marks t as complete. -func (t *Named) SetUnderlying(underlying Type) { - if underlying == nil { - panic("types2.Named.SetUnderlying: underlying type must not be nil") - } - if _, ok := underlying.(*Named); ok { - panic("types2.Named.SetUnderlying: underlying type must not be *Named") - } - t.underlying = underlying -} - -// AddMethod adds method m unless it is already in the method list. -func (t *Named) AddMethod(m *Func) { - if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { - t.methods = append(t.methods, m) - } -} - -// Note: This is a uint32 rather than a uint64 because the -// respective 64 bit atomic instructions are not available -// on all platforms. -var lastId uint32 - -// nextId returns a value increasing monotonically by 1 with -// each call, starting with 1. It may be called concurrently. -func nextId() uint64 { return uint64(atomic.AddUint32(&lastId, 1)) } - -// A TypeParam represents a type parameter type. -type TypeParam struct { - check *Checker // for lazy type bound completion - id uint64 // unique id, for debugging only - obj *TypeName // corresponding type name - index int // type parameter index in source order, starting at 0 - bound Type // *Named or *Interface; underlying type is always *Interface -} - -// Obj returns the type name for the type parameter t. -func (t *TypeParam) Obj() *TypeName { return t.obj } - -// NewTypeParam returns a new TypeParam. -func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam { - assert(bound != nil) - typ := &TypeParam{check: check, id: nextId(), obj: obj, index: index, bound: bound} - if obj.typ == nil { - obj.typ = typ - } - return typ -} - -func (t *TypeParam) Bound() *Interface { - iface := asInterface(t.bound) - // use the type bound position if we have one - pos := nopos - if n, _ := t.bound.(*Named); n != nil { - pos = n.obj.pos - } - // TODO(gri) switch this to an unexported method on Checker. - t.check.completeInterface(pos, iface) - return iface -} - -// optype returns a type's operational type. Except for -// type parameters, the operational type is the same -// as the underlying type (as returned by under). For -// Type parameters, the operational type is determined -// by the corresponding type bound's type list. The -// result may be the bottom or top type, but it is never -// the incoming type parameter. -func optype(typ Type) Type { - if t := asTypeParam(typ); t != nil { - // If the optype is typ, return the top type as we have - // no information. It also prevents infinite recursion - // via the asTypeParam converter function. This can happen - // for a type parameter list of the form: - // (type T interface { type T }). - // See also issue #39680. - if u := t.Bound().allTypes; u != nil && u != typ { - // u != typ and u is a type parameter => under(u) != typ, so this is ok - return under(u) + // su == nil || match(su, u) != nil + su = u + return true + }) { + if hasString { + return Typ[String] } - return theTop + return su } - return under(typ) -} - -// An instance represents an instantiated generic type syntactically -// (without expanding the instantiation). Type instances appear only -// during type-checking and are replaced by their fully instantiated -// (expanded) types before the end of type-checking. -type instance struct { - check *Checker // for lazy instantiation - pos syntax.Pos // position of type instantiation; for error reporting only - base *Named // parameterized type to be instantiated - targs []Type // type arguments - poslist []syntax.Pos // position of each targ; for error reporting only - value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set + return nil } -// expand returns the instantiated (= expanded) type of t. -// The result is either an instantiated *Named type, or -// Typ[Invalid] if there was an error. -func (t *instance) expand() Type { - v := t.value - if v == nil { - v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist) - if v == nil { - v = Typ[Invalid] +// If x and y are identical, match returns x. +// If x and y are identical channels but for their direction +// and one of them is unrestricted, match returns the channel +// with the restricted direction. +// In all other cases, match returns nil. +func match(x, y Type) Type { + // Common case: we don't have channels. + if Identical(x, y) { + return x + } + + // We may have channels that differ in direction only. + if x, _ := x.(*Chan); x != nil { + if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) { + // We have channels that differ in direction only. + // If there's an unrestricted channel, select the restricted one. + switch { + case x.dir == SendRecv: + return y + case y.dir == SendRecv: + return x + } } - t.value = v - } - // After instantiation we must have an invalid or a *Named type. - if debug && v != Typ[Invalid] { - _ = v.(*Named) - } - return v -} - -// expand expands a type instance into its instantiated -// type and leaves all other types alone. expand does -// not recurse. -func expand(typ Type) Type { - if t, _ := typ.(*instance); t != nil { - return t.expand() - } - return typ -} - -// expandf is set to expand. -// Call expandf when calling expand causes compile-time cycle error. -var expandf func(Type) Type - -func init() { expandf = expand } - -// bottom represents the bottom of the type lattice. -// It is the underlying type of a type parameter that -// cannot be satisfied by any type, usually because -// the intersection of type constraints left nothing). -type bottom struct{} - -// theBottom is the singleton bottom type. -var theBottom = &bottom{} - -// top represents the top of the type lattice. -// It is the underlying type of a type parameter that -// can be satisfied by any type (ignoring methods), -// usually because the type constraint has no type -// list. -type top struct{} - -// theTop is the singleton top type. -var theTop = &top{} - -// Type-specific implementations of Underlying. -func (t *Basic) Underlying() Type { return t } -func (t *Array) Underlying() Type { return t } -func (t *Slice) Underlying() Type { return t } -func (t *Struct) Underlying() Type { return t } -func (t *Pointer) Underlying() Type { return t } -func (t *Tuple) Underlying() Type { return t } -func (t *Signature) Underlying() Type { return t } -func (t *Sum) Underlying() Type { return t } -func (t *Interface) Underlying() Type { return t } -func (t *Map) Underlying() Type { return t } -func (t *Chan) Underlying() Type { return t } -func (t *Named) Underlying() Type { return t.underlying } -func (t *TypeParam) Underlying() Type { return t } -func (t *instance) Underlying() Type { return t } -func (t *bottom) Underlying() Type { return t } -func (t *top) Underlying() Type { return t } - -// Type-specific implementations of String. -func (t *Basic) String() string { return TypeString(t, nil) } -func (t *Array) String() string { return TypeString(t, nil) } -func (t *Slice) String() string { return TypeString(t, nil) } -func (t *Struct) String() string { return TypeString(t, nil) } -func (t *Pointer) String() string { return TypeString(t, nil) } -func (t *Tuple) String() string { return TypeString(t, nil) } -func (t *Signature) String() string { return TypeString(t, nil) } -func (t *Sum) String() string { return TypeString(t, nil) } -func (t *Interface) String() string { return TypeString(t, nil) } -func (t *Map) String() string { return TypeString(t, nil) } -func (t *Chan) String() string { return TypeString(t, nil) } -func (t *Named) String() string { return TypeString(t, nil) } -func (t *TypeParam) String() string { return TypeString(t, nil) } -func (t *instance) String() string { return TypeString(t, nil) } -func (t *bottom) String() string { return TypeString(t, nil) } -func (t *top) String() string { return TypeString(t, nil) } - -// under returns the true expanded underlying type. -// If it doesn't exist, the result is Typ[Invalid]. -// under must only be called when a type is known -// to be fully set up. -func under(t Type) Type { - // TODO(gri) is this correct for *Sum? - if n := asNamed(t); n != nil { - return n.under() } - return t -} - -// Converters -// -// A converter must only be called when a type is -// known to be fully set up. A converter returns -// a type's operational type (see comment for optype) -// or nil if the type argument is not of the -// respective type. - -func asBasic(t Type) *Basic { - op, _ := optype(t).(*Basic) - return op -} - -func asArray(t Type) *Array { - op, _ := optype(t).(*Array) - return op -} - -func asSlice(t Type) *Slice { - op, _ := optype(t).(*Slice) - return op -} - -func asStruct(t Type) *Struct { - op, _ := optype(t).(*Struct) - return op -} - -func asPointer(t Type) *Pointer { - op, _ := optype(t).(*Pointer) - return op -} - -// asTuple is not needed - not provided -func asSignature(t Type) *Signature { - op, _ := optype(t).(*Signature) - return op -} - -func asSum(t Type) *Sum { - op, _ := optype(t).(*Sum) - return op -} - -func asInterface(t Type) *Interface { - op, _ := optype(t).(*Interface) - return op -} - -func asMap(t Type) *Map { - op, _ := optype(t).(*Map) - return op -} - -func asChan(t Type) *Chan { - op, _ := optype(t).(*Chan) - return op -} - -// If the argument to asNamed and asTypeParam is of the respective types -// (possibly after expanding an instance type), these methods return that type. -// Otherwise the result is nil. - -func asNamed(t Type) *Named { - e, _ := expand(t).(*Named) - return e -} - -func asTypeParam(t Type) *TypeParam { - u, _ := under(t).(*TypeParam) - return u + // types are different + return nil } - -// Exported for the compiler. - -func AsPointer(t Type) *Pointer { return asPointer(t) } -func AsNamed(t Type) *Named { return asNamed(t) } -func AsSignature(t Type) *Signature { return asSignature(t) } -func AsInterface(t Type) *Interface { return asInterface(t) } diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go new file mode 100644 index 00000000000000..a2aba4a9a553c9 --- /dev/null +++ b/src/cmd/compile/internal/types2/typelists.go @@ -0,0 +1,69 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +// TypeParamList holds a list of type parameters. +type TypeParamList struct{ tparams []*TypeParam } + +// Len returns the number of type parameters in the list. +// It is safe to call on a nil receiver. +func (l *TypeParamList) Len() int { return len(l.list()) } + +// At returns the i'th type parameter in the list. +func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] } + +// list is for internal use where we expect a []*TypeParam. +// TODO(rfindley): list should probably be eliminated: we can pass around a +// TypeParamList instead. +func (l *TypeParamList) list() []*TypeParam { + if l == nil { + return nil + } + return l.tparams +} + +// TypeList holds a list of types. +type TypeList struct{ types []Type } + +// newTypeList returns a new TypeList with the types in list. +func newTypeList(list []Type) *TypeList { + if len(list) == 0 { + return nil + } + return &TypeList{list} +} + +// Len returns the number of types in the list. +// It is safe to call on a nil receiver. +func (l *TypeList) Len() int { return len(l.list()) } + +// At returns the i'th type in the list. +func (l *TypeList) At(i int) Type { return l.types[i] } + +// list is for internal use where we expect a []Type. +// TODO(rfindley): list should probably be eliminated: we can pass around a +// TypeList instead. +func (l *TypeList) list() []Type { + if l == nil { + return nil + } + return l.types +} + +// ---------------------------------------------------------------------------- +// Implementation + +func bindTParams(list []*TypeParam) *TypeParamList { + if len(list) == 0 { + return nil + } + for i, typ := range list { + if typ.index >= 0 { + panic("type parameter bound more than once") + } + typ.index = i + } + return &TypeParamList{tparams: list} +} diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go new file mode 100644 index 00000000000000..2e9a2adae672c1 --- /dev/null +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -0,0 +1,156 @@ +// 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 types2 + +import "sync/atomic" + +// Note: This is a uint32 rather than a uint64 because the +// respective 64 bit atomic instructions are not available +// on all platforms. +var lastID uint32 + +// nextID returns a value increasing monotonically by 1 with +// each call, starting with 1. It may be called concurrently. +func nextID() uint64 { return uint64(atomic.AddUint32(&lastID, 1)) } + +// A TypeParam represents a type parameter type. +type TypeParam struct { + check *Checker // for lazy type bound completion + id uint64 // unique id, for debugging only + obj *TypeName // corresponding type name + index int // type parameter index in source order, starting at 0 + bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface) +} + +// Obj returns the type name for the type parameter t. +func (t *TypeParam) Obj() *TypeName { return t.obj } + +// NewTypeParam returns a new TypeParam. Type parameters may be set on a Named +// or Signature type by calling SetTypeParams. Setting a type parameter on more +// than one type will result in a panic. +// +// The constraint argument can be nil, and set later via SetConstraint. If the +// constraint is non-nil, it must be fully defined. +func NewTypeParam(obj *TypeName, constraint Type) *TypeParam { + return (*Checker)(nil).newTypeParam(obj, constraint) +} + +// check may be nil +func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam { + // Always increment lastID, even if it is not used. + id := nextID() + if check != nil { + check.nextID++ + id = check.nextID + } + typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint} + if obj.typ == nil { + obj.typ = typ + } + // iface may mutate typ.bound, so we must ensure that iface() is called + // at least once before the resulting TypeParam escapes. + if check != nil { + check.needsCleanup(typ) + } else if constraint != nil { + typ.iface() + } + return typ +} + +// Index returns the index of the type param within its param list, or -1 if +// the type parameter has not yet been bound to a type. +func (t *TypeParam) Index() int { + return t.index +} + +// Constraint returns the type constraint specified for t. +func (t *TypeParam) Constraint() Type { + return t.bound +} + +// SetConstraint sets the type constraint for t. +// +// It must be called by users of NewTypeParam after the bound's underlying is +// fully defined, and before using the type parameter in any way other than to +// form other types. Once SetConstraint returns the receiver, t is safe for +// concurrent use. +func (t *TypeParam) SetConstraint(bound Type) { + if bound == nil { + panic("nil constraint") + } + t.bound = bound + // iface may mutate t.bound (if bound is not an interface), so ensure that + // this is done before returning. + t.iface() +} + +func (t *TypeParam) Underlying() Type { + return t.iface() +} + +func (t *TypeParam) String() string { return TypeString(t, nil) } + +// ---------------------------------------------------------------------------- +// Implementation + +func (t *TypeParam) cleanup() { + t.iface() + t.check = nil +} + +// iface returns the constraint interface of t. +func (t *TypeParam) iface() *Interface { + bound := t.bound + + // determine constraint interface + var ityp *Interface + switch u := under(bound).(type) { + case *Basic: + if u == Typ[Invalid] { + // error is reported elsewhere + return &emptyInterface + } + case *Interface: + if isTypeParam(bound) { + // error is reported in Checker.collectTypeParams + return &emptyInterface + } + ityp = u + } + + // If we don't have an interface, wrap constraint into an implicit interface. + if ityp == nil { + ityp = NewInterfaceType(nil, []Type{bound}) + ityp.implicit = true + t.bound = ityp // update t.bound for next time (optimization) + } + + // compute type set if necessary + if ityp.tset == nil { + // pos is used for tracing output; start with the type parameter position. + pos := t.obj.pos + // use the (original or possibly instantiated) type bound position if we have one + if n, _ := bound.(*Named); n != nil { + pos = n.obj.pos + } + computeInterfaceTypeSet(t.check, pos, ityp) + } + + return ityp +} + +// is calls f with the specific type terms of t's constraint and reports whether +// all calls to f returned true. If there are no specific terms, is +// returns the result of f(nil). +func (t *TypeParam) is(f func(*term) bool) bool { + return t.iface().typeSet().is(f) +} + +// underIs calls f with the underlying types of the specific type terms +// of t's constraint and reports whether all calls to f returned true. +// If there are no specific terms, underIs returns the result of f(nil). +func (t *TypeParam) underIs(f func(Type) bool) bool { + return t.iface().typeSet().underIs(f) +} diff --git a/src/cmd/compile/internal/types2/types_test.go b/src/cmd/compile/internal/types2/types_test.go deleted file mode 100644 index 096402148d38ad..00000000000000 --- a/src/cmd/compile/internal/types2/types_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package types2 - -import "sync/atomic" - -func init() { - acceptMethodTypeParams = true -} - -// Upon calling ResetId, nextId starts with 1 again. -// It may be called concurrently. This is only needed -// for tests where we may want to have a consistent -// numbering for each individual test case. -func ResetId() { atomic.StoreUint32(&lastId, 0) } diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go new file mode 100644 index 00000000000000..9ac3b6349c51b6 --- /dev/null +++ b/src/cmd/compile/internal/types2/typeset.go @@ -0,0 +1,433 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "cmd/compile/internal/syntax" + "fmt" + "sort" + "strings" +) + +// ---------------------------------------------------------------------------- +// API + +// A _TypeSet represents the type set of an interface. +// Because of existing language restrictions, methods can be "factored out" +// from the terms. The actual type set is the intersection of the type set +// implied by the methods and the type set described by the terms and the +// comparable bit. To test whether a type is included in a type set +// ("implements" relation), the type must implement all methods _and_ be +// an element of the type set described by the terms and the comparable bit. +// If the term list describes the set of all types and comparable is true, +// only comparable types are meant; in all other cases comparable is false. +type _TypeSet struct { + methods []*Func // all methods of the interface; sorted by unique ID + terms termlist // type terms of the type set + comparable bool // invariant: !comparable || terms.isAll() +} + +// IsEmpty reports whether type set s is the empty set. +func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() } + +// IsAll reports whether type set s is the set of all types (corresponding to the empty interface). +func (s *_TypeSet) IsAll() bool { return s.IsMethodSet() && len(s.methods) == 0 } + +// IsMethodSet reports whether the interface t is fully described by its method set. +func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() } + +// IsComparable reports whether each type in the set is comparable. +func (s *_TypeSet) IsComparable(seen map[Type]bool) bool { + if s.terms.isAll() { + return s.comparable + } + return s.is(func(t *term) bool { + return t != nil && comparable(t.typ, false, seen, nil) + }) +} + +// NumMethods returns the number of methods available. +func (s *_TypeSet) NumMethods() int { return len(s.methods) } + +// Method returns the i'th method of type set s for 0 <= i < s.NumMethods(). +// The methods are ordered by their unique ID. +func (s *_TypeSet) Method(i int) *Func { return s.methods[i] } + +// LookupMethod returns the index of and method with matching package and name, or (-1, nil). +func (s *_TypeSet) LookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) { + return lookupMethod(s.methods, pkg, name, foldCase) +} + +func (s *_TypeSet) String() string { + switch { + case s.IsEmpty(): + return "∅" + case s.IsAll(): + return "𝓤" + } + + hasMethods := len(s.methods) > 0 + hasTerms := s.hasTerms() + + var buf strings.Builder + buf.WriteByte('{') + if s.comparable { + buf.WriteString("comparable") + if hasMethods || hasTerms { + buf.WriteString("; ") + } + } + for i, m := range s.methods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.String()) + } + if hasMethods && hasTerms { + buf.WriteString("; ") + } + if hasTerms { + buf.WriteString(s.terms.String()) + } + buf.WriteString("}") + return buf.String() +} + +// ---------------------------------------------------------------------------- +// Implementation + +// hasTerms reports whether the type set has specific type terms. +func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() } + +// subsetOf reports whether s1 ⊆ s2. +func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) } + +// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go + +// is calls f with the specific type terms of s and reports whether +// all calls to f returned true. If there are no specific terms, is +// returns the result of f(nil). +func (s *_TypeSet) is(f func(*term) bool) bool { + if !s.hasTerms() { + return f(nil) + } + for _, t := range s.terms { + assert(t.typ != nil) + if !f(t) { + return false + } + } + return true +} + +// underIs calls f with the underlying types of the specific type terms +// of s and reports whether all calls to f returned true. If there are +// no specific terms, underIs returns the result of f(nil). +func (s *_TypeSet) underIs(f func(Type) bool) bool { + if !s.hasTerms() { + return f(nil) + } + for _, t := range s.terms { + assert(t.typ != nil) + // x == under(x) for ~x terms + u := t.typ + if !t.tilde { + u = under(u) + } + if debug { + assert(Identical(u, under(u))) + } + if !f(u) { + return false + } + } + return true +} + +// topTypeSet may be used as type set for the empty interface. +var topTypeSet = _TypeSet{terms: allTermlist} + +// computeInterfaceTypeSet may be called with check == nil. +func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_TypeSet { + if ityp.tset != nil { + return ityp.tset + } + + // If the interface is not fully set up yet, the type set will + // not be complete, which may lead to errors when using the + // type set (e.g. missing method). Don't compute a partial type + // set (and don't store it!), so that we still compute the full + // type set eventually. Instead, return the top type set and + // let any follow-on errors play out. + if !ityp.complete { + return &topTypeSet + } + + if check != nil && check.conf.Trace { + // Types don't generally have position information. + // If we don't have a valid pos provided, try to use + // one close enough. + if !pos.IsKnown() && len(ityp.methods) > 0 { + pos = ityp.methods[0].pos + } + + check.trace(pos, "-- type set for %s", ityp) + check.indent++ + defer func() { + check.indent-- + check.trace(pos, "=> %s ", ityp.typeSet()) + }() + } + + // An infinitely expanding interface (due to a cycle) is detected + // elsewhere (Checker.validType), so here we simply assume we only + // have valid interfaces. Mark the interface as complete to avoid + // infinite recursion if the validType check occurs later for some + // reason. + ityp.tset = &_TypeSet{terms: allTermlist} // TODO(gri) is this sufficient? + + var unionSets map[*Union]*_TypeSet + if check != nil { + if check.unionTypeSets == nil { + check.unionTypeSets = make(map[*Union]*_TypeSet) + } + unionSets = check.unionTypeSets + } else { + unionSets = make(map[*Union]*_TypeSet) + } + + // Methods of embedded interfaces are collected unchanged; i.e., the identity + // of a method I.m's Func Object of an interface I is the same as that of + // the method m in an interface that embeds interface I. On the other hand, + // if a method is embedded via multiple overlapping embedded interfaces, we + // don't provide a guarantee which "original m" got chosen for the embedding + // interface. See also issue #34421. + // + // If we don't care to provide this identity guarantee anymore, instead of + // reusing the original method in embeddings, we can clone the method's Func + // Object and give it the position of a corresponding embedded interface. Then + // we can get rid of the mpos map below and simply use the cloned method's + // position. + + var todo []*Func + var seen objset + var allMethods []*Func + mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages + addMethod := func(pos syntax.Pos, m *Func, explicit bool) { + switch other := seen.insert(m); { + case other == nil: + allMethods = append(allMethods, m) + mpos[m] = pos + case explicit: + if check == nil { + panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name)) + } + // check != nil + var err error_ + err.errorf(pos, "duplicate method %s", m.name) + err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name) + check.report(&err) + default: + // We have a duplicate method name in an embedded (not explicitly declared) method. + // Check method signatures after all types are computed (issue #33656). + // If we're pre-go1.14 (overlapping embeddings are not permitted), report that + // error here as well (even though we could do it eagerly) because it's the same + // error message. + if check == nil { + // check method signatures after all locally embedded interfaces are computed + todo = append(todo, m, other.(*Func)) + break + } + // check != nil + check.later(func() { + if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) { + var err error_ + err.errorf(pos, "duplicate method %s", m.name) + err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name) + check.report(&err) + } + }).describef(pos, "duplicate method check for %s", m.name) + } + } + + for _, m := range ityp.methods { + addMethod(m.pos, m, true) + } + + // collect embedded elements + allTerms := allTermlist + allComparable := false + for i, typ := range ityp.embeddeds { + // The embedding position is nil for imported interfaces + // and also for interface copies after substitution (but + // in that case we don't need to report errors again). + var pos syntax.Pos // embedding position + if ityp.embedPos != nil { + pos = (*ityp.embedPos)[i] + } + var comparable bool + var terms termlist + switch u := under(typ).(type) { + case *Interface: + // For now we don't permit type parameters as constraints. + assert(!isTypeParam(typ)) + tset := computeInterfaceTypeSet(check, pos, u) + // If typ is local, an error was already reported where typ is specified/defined. + if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ) + continue + } + comparable = tset.comparable + for _, m := range tset.methods { + addMethod(pos, m, false) // use embedding position pos rather than m.pos + } + terms = tset.terms + case *Union: + if check != nil && !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(pos, "go1.18", "embedding interface element %s", u) + continue + } + tset := computeUnionTypeSet(check, unionSets, pos, u) + if tset == &invalidTypeSet { + continue // ignore invalid unions + } + assert(!tset.comparable) + assert(len(tset.methods) == 0) + terms = tset.terms + default: + if u == Typ[Invalid] { + continue + } + if check != nil && !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(pos, "go1.18", "embedding non-interface type %s", typ) + continue + } + terms = termlist{{false, typ}} + } + + // The type set of an interface is the intersection of the type sets of all its elements. + // Due to language restrictions, only embedded interfaces can add methods, they are handled + // separately. Here we only need to intersect the term lists and comparable bits. + allTerms, allComparable = intersectTermLists(allTerms, allComparable, terms, comparable) + } + ityp.embedPos = nil // not needed anymore (errors have been reported) + + // process todo's (this only happens if check == nil) + for i := 0; i < len(todo); i += 2 { + m := todo[i] + other := todo[i+1] + if !Identical(m.typ, other.typ) { + panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name)) + } + } + + ityp.tset.comparable = allComparable + if len(allMethods) != 0 { + sortMethods(allMethods) + ityp.tset.methods = allMethods + } + ityp.tset.terms = allTerms + + return ityp.tset +} + +// TODO(gri) The intersectTermLists function belongs to the termlist implementation. +// The comparable type set may also be best represented as a term (using +// a special type). + +// intersectTermLists computes the intersection of two term lists and respective comparable bits. +// xcomp, ycomp are valid only if xterms.isAll() and yterms.isAll() respectively. +func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool) (termlist, bool) { + terms := xterms.intersect(yterms) + // If one of xterms or yterms is marked as comparable, + // the result must only include comparable types. + comp := xcomp || ycomp + if comp && !terms.isAll() { + // only keep comparable terms + i := 0 + for _, t := range terms { + assert(t.typ != nil) + if Comparable(t.typ) { + terms[i] = t + i++ + } + } + terms = terms[:i] + if !terms.isAll() { + comp = false + } + } + assert(!comp || terms.isAll()) // comparable invariant + return terms, comp +} + +func sortMethods(list []*Func) { + sort.Sort(byUniqueMethodName(list)) +} + +func assertSortedMethods(list []*Func) { + if !debug { + panic("assertSortedMethods called outside debug mode") + } + if !sort.IsSorted(byUniqueMethodName(list)) { + panic("methods not sorted") + } +} + +// byUniqueMethodName method lists can be sorted by their unique method names. +type byUniqueMethodName []*Func + +func (a byUniqueMethodName) Len() int { return len(a) } +func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(&a[j].object) } +func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// invalidTypeSet is a singleton type set to signal an invalid type set +// due to an error. It's also a valid empty type set, so consumers of +// type sets may choose to ignore it. +var invalidTypeSet _TypeSet + +// computeUnionTypeSet may be called with check == nil. +// The result is &invalidTypeSet if the union overflows. +func computeUnionTypeSet(check *Checker, unionSets map[*Union]*_TypeSet, pos syntax.Pos, utyp *Union) *_TypeSet { + if tset, _ := unionSets[utyp]; tset != nil { + return tset + } + + // avoid infinite recursion (see also computeInterfaceTypeSet) + unionSets[utyp] = new(_TypeSet) + + var allTerms termlist + for _, t := range utyp.terms { + var terms termlist + u := under(t.typ) + if ui, _ := u.(*Interface); ui != nil { + // For now we don't permit type parameters as constraints. + assert(!isTypeParam(t.typ)) + terms = computeInterfaceTypeSet(check, pos, ui).terms + } else if u == Typ[Invalid] { + continue + } else { + if t.tilde && !Identical(t.typ, u) { + // There is no underlying type which is t.typ. + // The corresponding type set is empty. + t = nil // ∅ term + } + terms = termlist{(*term)(t)} + } + // The type set of a union expression is the union + // of the type sets of each term. + allTerms = allTerms.union(terms) + if len(allTerms) > maxTermCount { + if check != nil { + check.errorf(pos, "cannot handle more than %d union terms (implementation limitation)", maxTermCount) + } + unionSets[utyp] = &invalidTypeSet + return unionSets[utyp] + } + } + unionSets[utyp].terms = allTerms + + return unionSets[utyp] +} diff --git a/src/cmd/compile/internal/types2/typeset_test.go b/src/cmd/compile/internal/types2/typeset_test.go new file mode 100644 index 00000000000000..40ca28e525feb5 --- /dev/null +++ b/src/cmd/compile/internal/types2/typeset_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "cmd/compile/internal/syntax" + "strings" + "testing" +) + +func TestInvalidTypeSet(t *testing.T) { + if !invalidTypeSet.IsEmpty() { + t.Error("invalidTypeSet is not empty") + } +} + +func TestTypeSetString(t *testing.T) { + for body, want := range map[string]string{ + "{}": "𝓤", + "{int}": "{int}", + "{~int}": "{~int}", + "{int|string}": "{int | string}", + "{int; string}": "∅", + + "{comparable}": "{comparable}", + "{comparable; int}": "{int}", + "{~int; comparable}": "{~int}", + "{int|string; comparable}": "{int | string}", + "{comparable; int; string}": "∅", + + "{m()}": "{func (p.T).m()}", + "{m1(); m2() int }": "{func (p.T).m1(); func (p.T).m2() int}", + "{error}": "{func (error).Error() string}", + "{m(); comparable}": "{comparable; func (p.T).m()}", + "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}", + "{comparable; error}": "{comparable; func (error).Error() string}", + + "{m(); comparable; int|float32|string}": "{func (p.T).m(); int | float32 | string}", + "{m1(); int; m2(); comparable }": "{func (p.T).m1(); func (p.T).m2(); int}", + + "{E}; type E interface{}": "𝓤", + "{E}; type E interface{int;string}": "∅", + "{E}; type E interface{comparable}": "{comparable}", + } { + // parse + errh := func(error) {} // dummy error handler so that parsing continues in presence of errors + src := "package p; type T interface" + body + file, err := syntax.Parse(nil, strings.NewReader(src), errh, nil, 0) + if err != nil { + t.Fatalf("%s: %v (invalid test case)", body, err) + } + + // type check + var conf Config + pkg, err := conf.Check(file.PkgName.Value, []*syntax.File{file}, nil) + if err != nil { + t.Fatalf("%s: %v (invalid test case)", body, err) + } + + // lookup T + obj := pkg.scope.Lookup("T") + if obj == nil { + t.Fatalf("%s: T not found (invalid test case)", body) + } + T, ok := under(obj.Type()).(*Interface) + if !ok { + t.Fatalf("%s: %v is not an interface (invalid test case)", body, obj) + } + + // verify test case + got := T.typeSet().String() + if got != want { + t.Errorf("%s: got %s; want %s", body, got, want) + } + } +} + +// TODO(gri) add more tests diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 40016697b7c90f..dbee4bf6bc22c3 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -9,6 +9,9 @@ package types2 import ( "bytes" "fmt" + "sort" + "strconv" + "strings" "unicode/utf8" ) @@ -22,7 +25,6 @@ import ( // // Using a nil Qualifier is equivalent to using (*Package).Path: the // object is qualified by the import path, e.g., "encoding/json.Marshal". -// type Qualifier func(*Package) string // RelativeTo returns a Qualifier that fully qualifies members of @@ -39,33 +41,18 @@ func RelativeTo(pkg *Package) Qualifier { } } -// If gcCompatibilityMode is set, printing of types is modified -// to match the representation of some types in the gc compiler: -// -// - byte and rune lose their alias name and simply stand for -// uint8 and int32 respectively -// - embedded interfaces get flattened (the embedding info is lost, -// and certain recursive interface types cannot be printed anymore) -// -// This makes it easier to compare packages computed with the type- -// checker vs packages imported from gc export data. -// -// Caution: This flag affects all uses of WriteType, globally. -// It is only provided for testing in conjunction with -// gc-generated data. -// -// This flag is exported in the x/tools/go/types package. We don't -// need it at the moment in the std repo and so we don't export it -// anymore. We should eventually try to remove it altogether. -// TODO(gri) remove this -var gcCompatibilityMode bool - // TypeString returns the string representation of typ. // The Qualifier controls the printing of // package-level objects, and may be nil. func TypeString(typ Type, qf Qualifier) string { + return typeString(typ, qf, false) +} + +func typeString(typ Type, qf Qualifier, debug bool) string { var buf bytes.Buffer - WriteType(&buf, typ, qf) + w := newTypeWriter(&buf, qf) + w.debug = debug + w.typ(typ) return buf.String() } @@ -73,172 +60,196 @@ func TypeString(typ Type, qf Qualifier) string { // The Qualifier controls the printing of // package-level objects, and may be nil. func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { - writeType(buf, typ, qf, make([]Type, 0, 8)) + newTypeWriter(buf, qf).typ(typ) +} + +// WriteSignature writes the representation of the signature sig to buf, +// without a leading "func" keyword. +// The Qualifier controls the printing of +// package-level objects, and may be nil. +func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { + newTypeWriter(buf, qf).signature(sig) +} + +type typeWriter struct { + buf *bytes.Buffer + seen map[Type]bool + qf Qualifier + ctxt *Context // if non-nil, we are type hashing + tparams *TypeParamList // local type parameters + debug bool // if true, write debug annotations +} + +func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { + return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false} +} + +func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter { + assert(ctxt != nil) + return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false} } -// instanceMarker is the prefix for an instantiated type -// in "non-evaluated" instance form. -const instanceMarker = '#' - -func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { - // Theoretically, this is a quadratic lookup algorithm, but in - // practice deeply nested composite types with unnamed component - // types are uncommon. This code is likely more efficient than - // using a map. - for _, t := range visited { - if t == typ { - fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ - return +func (w *typeWriter) byte(b byte) { + if w.ctxt != nil { + if b == ' ' { + b = '#' } + w.buf.WriteByte(b) + return + } + w.buf.WriteByte(b) + if b == ',' || b == ';' { + w.buf.WriteByte(' ') + } +} + +func (w *typeWriter) string(s string) { + w.buf.WriteString(s) +} + +func (w *typeWriter) error(msg string) { + if w.ctxt != nil { + panic(msg) + } + w.buf.WriteString("<" + msg + ">") +} + +func (w *typeWriter) typ(typ Type) { + if w.seen[typ] { + w.error("cycle to " + goTypeName(typ)) + return } - visited = append(visited, typ) + w.seen[typ] = true + defer delete(w.seen, typ) switch t := typ.(type) { case nil: - buf.WriteString("") + w.error("nil") case *Basic: // exported basic types go into package unsafe // (currently this is just unsafe.Pointer) if isExported(t.name) { if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil { - writeTypeName(buf, obj, qf) + w.typeName(obj) break } } - - if gcCompatibilityMode { - // forget the alias names - switch t.kind { - case Byte: - t = Typ[Uint8] - case Rune: - t = Typ[Int32] - } - } - buf.WriteString(t.name) + w.string(t.name) case *Array: - fmt.Fprintf(buf, "[%d]", t.len) - writeType(buf, t.elem, qf, visited) + w.byte('[') + w.string(strconv.FormatInt(t.len, 10)) + w.byte(']') + w.typ(t.elem) case *Slice: - buf.WriteString("[]") - writeType(buf, t.elem, qf, visited) + w.string("[]") + w.typ(t.elem) case *Struct: - buf.WriteString("struct{") + w.string("struct{") for i, f := range t.fields { if i > 0 { - buf.WriteString("; ") + w.byte(';') } // This doesn't do the right thing for embedded type // aliases where we should print the alias name, not // the aliased type (see issue #44410). if !f.embedded { - buf.WriteString(f.name) - buf.WriteByte(' ') + w.string(f.name) + w.byte(' ') } - writeType(buf, f.typ, qf, visited) + w.typ(f.typ) if tag := t.Tag(i); tag != "" { - fmt.Fprintf(buf, " %q", tag) + w.byte(' ') + // TODO(gri) If tag contains blanks, replacing them with '#' + // in Context.TypeHash may produce another tag + // accidentally. + w.string(strconv.Quote(tag)) } } - buf.WriteByte('}') + w.byte('}') case *Pointer: - buf.WriteByte('*') - writeType(buf, t.base, qf, visited) + w.byte('*') + w.typ(t.base) case *Tuple: - writeTuple(buf, t, false, qf, visited) + w.tuple(t, false) case *Signature: - buf.WriteString("func") - writeSignature(buf, t, qf, visited) - - case *Sum: - for i, t := range t.types { + w.string("func") + w.signature(t) + + case *Union: + // Unions only appear as (syntactic) embedded elements + // in interfaces and syntactically cannot be empty. + if t.Len() == 0 { + w.error("empty union") + break + } + for i, t := range t.terms { if i > 0 { - buf.WriteString(", ") + w.string(termSep) } - writeType(buf, t, qf, visited) + if t.tilde { + w.byte('~') + } + w.typ(t.typ) } case *Interface: - // We write the source-level methods and embedded types rather - // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have embedded - // interface types that (directly or indirectly) embed the - // current interface. For instance, consider the result type - // of m: - // - // type T interface{ - // m() interface{ T } - // } - // - buf.WriteString("interface{") - empty := true - if gcCompatibilityMode { - // print flattened interface - // (useful to compare against gc-generated interfaces) - for i, m := range t.allMethods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - empty = false + if w.ctxt == nil { + if t == universeAny.Type() { + // When not hashing, we can try to improve type strings by writing "any" + // for a type that is pointer-identical to universeAny. This logic should + // be deprecated by more robust handling for aliases. + w.string("any") + break } - if !empty && t.allTypes != nil { - buf.WriteString("; ") + if t == universeComparable.Type().(*Named).underlying { + w.string("interface{comparable}") + break } - if t.allTypes != nil { - buf.WriteString("type ") - writeType(buf, t.allTypes, qf, visited) + } + if t.implicit { + if len(t.methods) == 0 && len(t.embeddeds) == 1 { + w.typ(t.embeddeds[0]) + break } + // Something's wrong with the implicit interface. + // Print it as such and continue. + w.string("/* implicit */ ") + } + w.string("interface{") + first := true + if w.ctxt != nil { + w.typeSet(t.typeSet()) } else { - // print explicit interface methods and embedded types - for i, m := range t.methods { - if i > 0 { - buf.WriteString("; ") + for _, m := range t.methods { + if !first { + w.byte(';') } - buf.WriteString(m.name) - writeSignature(buf, m.typ.(*Signature), qf, visited) - empty = false + first = false + w.string(m.name) + w.signature(m.typ.(*Signature)) } - if !empty && t.types != nil { - buf.WriteString("; ") - } - if t.types != nil { - buf.WriteString("type ") - writeType(buf, t.types, qf, visited) - empty = false - } - if !empty && len(t.embeddeds) > 0 { - buf.WriteString("; ") - } - for i, typ := range t.embeddeds { - if i > 0 { - buf.WriteString("; ") + for _, typ := range t.embeddeds { + if !first { + w.byte(';') } - writeType(buf, typ, qf, visited) - empty = false - } - } - if t.allMethods == nil || len(t.methods) > len(t.allMethods) { - if !empty { - buf.WriteByte(' ') + first = false + w.typ(typ) } - buf.WriteString("/* incomplete */") } - buf.WriteByte('}') + w.byte('}') case *Map: - buf.WriteString("map[") - writeType(buf, t.key, qf, visited) - buf.WriteByte(']') - writeType(buf, t.elem, qf, visited) + w.string("map[") + w.typ(t.key) + w.byte(']') + w.typ(t.elem) case *Chan: var s string @@ -255,157 +266,187 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { case RecvOnly: s = "<-chan " default: - panic("unreachable") + w.error("unknown channel direction") } - buf.WriteString(s) + w.string(s) if parens { - buf.WriteByte('(') + w.byte('(') } - writeType(buf, t.elem, qf, visited) + w.typ(t.elem) if parens { - buf.WriteByte(')') + w.byte(')') } case *Named: - writeTypeName(buf, t.obj, qf) - if t.targs != nil { + // If hashing, write a unique prefix for t to represent its identity, since + // named type identity is pointer identity. + if w.ctxt != nil { + w.string(strconv.Itoa(w.ctxt.getID(t))) + } + w.typeName(t.obj) // when hashing written for readability of the hash only + if t.inst != nil { // instantiated type - buf.WriteByte('[') - writeTypeList(buf, t.targs, qf, visited) - buf.WriteByte(']') - } else if t.tparams != nil { + w.typeList(t.inst.targs.list()) + } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams // parameterized type - writeTParamList(buf, t.tparams, qf, visited) + w.tParamList(t.TypeParams().list()) } case *TypeParam: - s := "?" - if t.obj != nil { - s = t.obj.name + if t.obj == nil { + w.error("unnamed type parameter") + break + } + if i := tparamIndex(w.tparams.list(), t); i >= 0 { + // The names of type parameters that are declared by the type being + // hashed are not part of the type identity. Replace them with a + // placeholder indicating their index. + w.string(fmt.Sprintf("$%d", i)) + } else { + w.string(t.obj.name) + if w.debug || w.ctxt != nil { + w.string(subscript(t.id)) + } } - buf.WriteString(s + subscript(t.id)) - - case *instance: - buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance - writeTypeName(buf, t.base.obj, qf) - buf.WriteByte('[') - writeTypeList(buf, t.targs, qf, visited) - buf.WriteByte(']') - - case *bottom: - buf.WriteString("⊥") - - case *top: - buf.WriteString("⊤") default: // For externally defined implementations of Type. - buf.WriteString(t.String()) + // Note: In this case cycles won't be caught. + w.string(t.String()) } } -func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) { +// typeSet writes a canonical hash for an interface type set. +func (w *typeWriter) typeSet(s *_TypeSet) { + assert(w.ctxt != nil) + first := true + for _, m := range s.methods { + if !first { + w.byte(';') + } + first = false + w.string(m.name) + w.signature(m.typ.(*Signature)) + } + switch { + case s.terms.isAll(): + // nothing to do + case s.terms.isEmpty(): + w.string(s.terms.String()) + default: + var termHashes []string + for _, term := range s.terms { + // terms are not canonically sorted, so we sort their hashes instead. + var buf bytes.Buffer + if term.tilde { + buf.WriteByte('~') + } + newTypeHasher(&buf, w.ctxt).typ(term.typ) + termHashes = append(termHashes, buf.String()) + } + sort.Strings(termHashes) + if !first { + w.byte(';') + } + w.string(strings.Join(termHashes, "|")) + } +} + +func (w *typeWriter) typeList(list []Type) { + w.byte('[') for i, typ := range list { if i > 0 { - buf.WriteString(", ") + w.byte(',') } - writeType(buf, typ, qf, visited) + w.typ(typ) } + w.byte(']') } -func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) { - buf.WriteString("[") +func (w *typeWriter) tParamList(list []*TypeParam) { + w.byte('[') var prev Type - for i, p := range list { - // TODO(gri) support 'any' sugar here. - var b Type = &emptyInterface - if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil { - b = t.bound + for i, tpar := range list { + // Determine the type parameter and its constraint. + // list is expected to hold type parameter names, + // but don't crash if that's not the case. + if tpar == nil { + w.error("nil type parameter") + continue } if i > 0 { - if b != prev { - // type bound changed - write previous one before advancing - buf.WriteByte(' ') - writeType(buf, prev, qf, visited) + if tpar.bound != prev { + // bound changed - write previous one before advancing + w.byte(' ') + w.typ(prev) } - buf.WriteString(", ") - } - prev = b - - if t, _ := p.typ.(*TypeParam); t != nil { - writeType(buf, t, qf, visited) - } else { - buf.WriteString(p.name) + w.byte(',') } + prev = tpar.bound + w.typ(tpar) } if prev != nil { - buf.WriteByte(' ') - writeType(buf, prev, qf, visited) + w.byte(' ') + w.typ(prev) } - buf.WriteByte(']') + w.byte(']') } -func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) { - s := "" - if obj != nil { - if obj.pkg != nil { - writePackage(buf, obj.pkg, qf) - } - // TODO(gri): function-local named types should be displayed - // differently from named types at package level to avoid - // ambiguity. - s = obj.name +func (w *typeWriter) typeName(obj *TypeName) { + if obj.pkg != nil { + writePackage(w.buf, obj.pkg, w.qf) } - buf.WriteString(s) + w.string(obj.name) } -func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) { - buf.WriteByte('(') +func (w *typeWriter) tuple(tup *Tuple, variadic bool) { + w.byte('(') if tup != nil { for i, v := range tup.vars { if i > 0 { - buf.WriteString(", ") + w.byte(',') } - if v.name != "" { - buf.WriteString(v.name) - buf.WriteByte(' ') + // parameter names are ignored for type identity and thus type hashes + if w.ctxt == nil && v.name != "" { + w.string(v.name) + w.byte(' ') } typ := v.typ if variadic && i == len(tup.vars)-1 { if s, ok := typ.(*Slice); ok { - buf.WriteString("...") + w.string("...") typ = s.elem } else { // special case: // append(s, "foo"...) leads to signature func([]byte, string...) - if t := asBasic(typ); t == nil || t.kind != String { - panic("internal error: string type expected") + if t, _ := under(typ).(*Basic); t == nil || t.kind != String { + w.error("expected string type") + continue } - writeType(buf, typ, qf, visited) - buf.WriteString("...") + w.typ(typ) + w.string("...") continue } } - writeType(buf, typ, qf, visited) + w.typ(typ) } } - buf.WriteByte(')') + w.byte(')') } -// WriteSignature writes the representation of the signature sig to buf, -// without a leading "func" keyword. -// The Qualifier controls the printing of -// package-level objects, and may be nil. -func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { - writeSignature(buf, sig, qf, make([]Type, 0, 8)) -} - -func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { - if sig.tparams != nil { - writeTParamList(buf, sig.tparams, qf, visited) +func (w *typeWriter) signature(sig *Signature) { + if sig.TypeParams().Len() != 0 { + if w.ctxt != nil { + assert(w.tparams == nil) + w.tparams = sig.TypeParams() + defer func() { + w.tparams = nil + }() + } + w.tParamList(sig.TypeParams().list()) } - writeTuple(buf, sig.params, sig.variadic, qf, visited) + w.tuple(sig.params, sig.variadic) n := sig.results.Len() if n == 0 { @@ -413,15 +454,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T return } - buf.WriteByte(' ') - if n == 1 && sig.results.vars[0].name == "" { - // single unnamed result - writeType(buf, sig.results.vars[0].typ, qf, visited) + w.byte(' ') + if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { + // single unnamed result (if type hashing, name must be ignored) + w.typ(sig.results.vars[0].typ) return } // multiple or named result(s) - writeTuple(buf, sig.results, false, qf, visited) + w.tuple(sig.results, false) } // subscript returns the decimal (utf8) representation of x using subscript digits. diff --git a/src/cmd/compile/internal/types2/typestring_test.go b/src/cmd/compile/internal/types2/typestring_test.go index d98e9a5ade6e9b..42b1f126f518a1 100644 --- a/src/cmd/compile/internal/types2/typestring_test.go +++ b/src/cmd/compile/internal/types2/typestring_test.go @@ -91,7 +91,12 @@ var independentTestTypes = []testEntry{ dup("interface{}"), dup("interface{m()}"), dup(`interface{String() string; m(int) float32}`), - dup(`interface{type int, float32, complex128}`), + dup("interface{int | float32 | complex128}"), + dup("interface{int | ~float32 | ~complex128}"), + dup("any"), + dup("interface{comparable}"), + {"comparable", "interface{comparable}"}, + {"error", "interface{Error() string}"}, // maps dup("map[string]int"), @@ -128,67 +133,18 @@ func TestTypeString(t *testing.T) { t.Errorf("%s: %s", src, err) continue } - typ := pkg.Scope().Lookup("T").Type().Underlying() + obj := pkg.Scope().Lookup("T") + if obj == nil { + t.Errorf("%s: T not found", test.src) + continue + } + typ := obj.Type().Underlying() if got := typ.String(); got != test.str { t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } } -var nopos syntax.Pos - -func TestIncompleteInterfaces(t *testing.T) { - sig := NewSignature(nil, nil, nil, false) - m := NewFunc(nopos, nil, "m", sig) - for _, test := range []struct { - typ *Interface - want string - }{ - {new(Interface), "interface{/* incomplete */}"}, - {new(Interface).Complete(), "interface{}"}, - - {NewInterface(nil, nil), "interface{}"}, - {NewInterface(nil, nil).Complete(), "interface{}"}, - {NewInterface([]*Func{}, nil), "interface{}"}, - {NewInterface([]*Func{}, nil).Complete(), "interface{}"}, - {NewInterface(nil, []*Named{}), "interface{}"}, - {NewInterface(nil, []*Named{}).Complete(), "interface{}"}, - {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"}, - {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"}, - {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"}, - {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"}, - - {NewInterfaceType(nil, nil), "interface{}"}, - {NewInterfaceType(nil, nil).Complete(), "interface{}"}, - {NewInterfaceType([]*Func{}, nil), "interface{}"}, - {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"}, - {NewInterfaceType(nil, []Type{}), "interface{}"}, - {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"}, - {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"}, - {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"}, - {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"}, - {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"}, - } { - got := test.typ.String() - if got != test.want { - t.Errorf("got: %s, want: %s", got, test.want) - } - } -} - -// newDefined creates a new defined type named T with the given underlying type. -// Helper function for use with TestIncompleteInterfaces only. -func newDefined(underlying Type) *Named { - tname := NewTypeName(nopos, nil, "T", nil) - return NewNamed(tname, underlying, nil) -} - func TestQualifiedTypeString(t *testing.T) { p, _ := pkgFor("p.go", "package p; type T int", nil) q, _ := pkgFor("q.go", "package q", nil) diff --git a/src/cmd/compile/internal/types2/typeterm.go b/src/cmd/compile/internal/types2/typeterm.go new file mode 100644 index 00000000000000..97791324e1e75c --- /dev/null +++ b/src/cmd/compile/internal/types2/typeterm.go @@ -0,0 +1,165 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +// A term describes elementary type sets: +// +// ∅: (*term)(nil) == ∅ // set of no types (empty set) +// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) +// T: &term{false, T} == {T} // set of type T +// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t +type term struct { + tilde bool // valid if typ != nil + typ Type +} + +func (x *term) String() string { + switch { + case x == nil: + return "∅" + case x.typ == nil: + return "𝓤" + case x.tilde: + return "~" + x.typ.String() + default: + return x.typ.String() + } +} + +// equal reports whether x and y represent the same type set. +func (x *term) equal(y *term) bool { + // easy cases + switch { + case x == nil || y == nil: + return x == y + case x.typ == nil || y.typ == nil: + return x.typ == y.typ + } + // ∅ ⊂ x, y ⊂ 𝓤 + + return x.tilde == y.tilde && Identical(x.typ, y.typ) +} + +// union returns the union x ∪ y: zero, one, or two non-nil terms. +func (x *term) union(y *term) (_, _ *term) { + // easy cases + switch { + case x == nil && y == nil: + return nil, nil // ∅ ∪ ∅ == ∅ + case x == nil: + return y, nil // ∅ ∪ y == y + case y == nil: + return x, nil // x ∪ ∅ == x + case x.typ == nil: + return x, nil // 𝓤 ∪ y == 𝓤 + case y.typ == nil: + return y, nil // x ∪ 𝓤 == 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return x, y // x ∪ y == (x, y) if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∪ ~t == ~t + // ~t ∪ T == ~t + // T ∪ ~t == ~t + // T ∪ T == T + if x.tilde || !y.tilde { + return x, nil + } + return y, nil +} + +// intersect returns the intersection x ∩ y. +func (x *term) intersect(y *term) *term { + // easy cases + switch { + case x == nil || y == nil: + return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ + case x.typ == nil: + return y // 𝓤 ∩ y == y + case y.typ == nil: + return x // x ∩ 𝓤 == x + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return nil // x ∩ y == ∅ if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ∩ ~t == ~t + // ~t ∩ T == T + // T ∩ ~t == T + // T ∩ T == T + if !x.tilde || y.tilde { + return x + } + return y +} + +// includes reports whether t ∈ x. +func (x *term) includes(t Type) bool { + // easy cases + switch { + case x == nil: + return false // t ∈ ∅ == false + case x.typ == nil: + return true // t ∈ 𝓤 == true + } + // ∅ ⊂ x ⊂ 𝓤 + + u := t + if x.tilde { + u = under(u) + } + return Identical(x.typ, u) +} + +// subsetOf reports whether x ⊆ y. +func (x *term) subsetOf(y *term) bool { + // easy cases + switch { + case x == nil: + return true // ∅ ⊆ y == true + case y == nil: + return false // x ⊆ ∅ == false since x != ∅ + case y.typ == nil: + return true // x ⊆ 𝓤 == true + case x.typ == nil: + return false // 𝓤 ⊆ y == false since y != 𝓤 + } + // ∅ ⊂ x, y ⊂ 𝓤 + + if x.disjoint(y) { + return false // x ⊆ y == false if x ∩ y == ∅ + } + // x.typ == y.typ + + // ~t ⊆ ~t == true + // ~t ⊆ T == false + // T ⊆ ~t == true + // T ⊆ T == true + return !x.tilde || y.tilde +} + +// disjoint reports whether x ∩ y == ∅. +// x.typ and y.typ must not be nil. +func (x *term) disjoint(y *term) bool { + if debug && (x.typ == nil || y.typ == nil) { + panic("invalid argument(s)") + } + ux := x.typ + if y.tilde { + ux = under(ux) + } + uy := y.typ + if x.tilde { + uy = under(uy) + } + return !Identical(ux, uy) +} diff --git a/src/cmd/compile/internal/types2/typeterm_test.go b/src/cmd/compile/internal/types2/typeterm_test.go new file mode 100644 index 00000000000000..6d9c8db0348ecf --- /dev/null +++ b/src/cmd/compile/internal/types2/typeterm_test.go @@ -0,0 +1,239 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import ( + "strings" + "testing" +) + +var myInt = func() Type { + tname := NewTypeName(nopos, nil, "myInt", nil) + return NewNamed(tname, Typ[Int], nil) +}() + +var testTerms = map[string]*term{ + "∅": nil, + "𝓤": {}, + "int": {false, Typ[Int]}, + "~int": {true, Typ[Int]}, + "string": {false, Typ[String]}, + "~string": {true, Typ[String]}, + "myInt": {false, myInt}, +} + +func TestTermString(t *testing.T) { + for want, x := range testTerms { + if got := x.String(); got != want { + t.Errorf("%v.String() == %v; want %v", x, got, want) + } + } +} + +func split(s string, n int) []string { + r := strings.Split(s, " ") + if len(r) != n { + panic("invalid test case: " + s) + } + return r +} + +func testTerm(name string) *term { + r, ok := testTerms[name] + if !ok { + panic("invalid test argument: " + name) + } + return r +} + +func TestTermEqual(t *testing.T) { + for _, test := range []string{ + "∅ ∅ T", + "𝓤 𝓤 T", + "int int T", + "~int ~int T", + "myInt myInt T", + "∅ 𝓤 F", + "∅ int F", + "∅ ~int F", + "𝓤 int F", + "𝓤 ~int F", + "𝓤 myInt F", + "int ~int F", + "int myInt F", + "~int myInt F", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.equal(y); got != want { + t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want) + } + // equal is symmetric + x, y = y, x + if got := x.equal(y); got != want { + t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermUnion(t *testing.T) { + for _, test := range []string{ + "∅ ∅ ∅ ∅", + "∅ 𝓤 𝓤 ∅", + "∅ int int ∅", + "∅ ~int ~int ∅", + "∅ myInt myInt ∅", + "𝓤 𝓤 𝓤 ∅", + "𝓤 int 𝓤 ∅", + "𝓤 ~int 𝓤 ∅", + "𝓤 myInt 𝓤 ∅", + "int int int ∅", + "int ~int ~int ∅", + "int string int string", + "int ~string int ~string", + "int myInt int myInt", + "~int ~string ~int ~string", + "~int myInt ~int ∅", + + // union is symmetric, but the result order isn't - repeat symmetric cases explicitly + "𝓤 ∅ 𝓤 ∅", + "int ∅ int ∅", + "~int ∅ ~int ∅", + "myInt ∅ myInt ∅", + "int 𝓤 𝓤 ∅", + "~int 𝓤 𝓤 ∅", + "myInt 𝓤 𝓤 ∅", + "~int int ~int ∅", + "string int string int", + "~string int ~string int", + "myInt int myInt int", + "~string ~int ~string ~int", + "myInt ~int ~int ∅", + } { + args := split(test, 4) + x := testTerm(args[0]) + y := testTerm(args[1]) + want1 := testTerm(args[2]) + want2 := testTerm(args[3]) + if got1, got2 := x.union(y); !got1.equal(want1) || !got2.equal(want2) { + t.Errorf("%v.union(%v) = %v, %v; want %v, %v", x, y, got1, got2, want1, want2) + } + } +} + +func TestTermIntersection(t *testing.T) { + for _, test := range []string{ + "∅ ∅ ∅", + "∅ 𝓤 ∅", + "∅ int ∅", + "∅ ~int ∅", + "∅ myInt ∅", + "𝓤 𝓤 𝓤", + "𝓤 int int", + "𝓤 ~int ~int", + "𝓤 myInt myInt", + "int int int", + "int ~int int", + "int string ∅", + "int ~string ∅", + "int string ∅", + "~int ~string ∅", + "~int myInt myInt", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := testTerm(args[2]) + if got := x.intersect(y); !got.equal(want) { + t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want) + } + // intersect is symmetric + x, y = y, x + if got := x.intersect(y); !got.equal(want) { + t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermIncludes(t *testing.T) { + for _, test := range []string{ + "∅ int F", + "𝓤 int T", + "int int T", + "~int int T", + "~int myInt T", + "string int F", + "~string int F", + "myInt int F", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]).typ + want := args[2] == "T" + if got := x.includes(y); got != want { + t.Errorf("%v.includes(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermSubsetOf(t *testing.T) { + for _, test := range []string{ + "∅ ∅ T", + "𝓤 𝓤 T", + "int int T", + "~int ~int T", + "myInt myInt T", + "∅ 𝓤 T", + "∅ int T", + "∅ ~int T", + "∅ myInt T", + "𝓤 int F", + "𝓤 ~int F", + "𝓤 myInt F", + "int ~int T", + "int myInt F", + "~int myInt F", + "myInt int F", + "myInt ~int T", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.subsetOf(y); got != want { + t.Errorf("%v.subsetOf(%v) = %v; want %v", x, y, got, want) + } + } +} + +func TestTermDisjoint(t *testing.T) { + for _, test := range []string{ + "int int F", + "~int ~int F", + "int ~int F", + "int string T", + "int ~string T", + "int myInt T", + "~int ~string T", + "~int myInt F", + "string myInt T", + "~string myInt T", + } { + args := split(test, 3) + x := testTerm(args[0]) + y := testTerm(args[1]) + want := args[2] == "T" + if got := x.disjoint(y); got != want { + t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want) + } + // disjoint is symmetric + x, y = y, x + if got := x.disjoint(y); got != want { + t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want) + } + } +} diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index e64d804c30b8cc..262f5af3322a37 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -10,19 +10,13 @@ import ( "cmd/compile/internal/syntax" "fmt" "go/constant" - "sort" - "strconv" "strings" ) -// Disabled by default, but enabled when running tests (via types_test.go). -var acceptMethodTypeParams bool - // ident type-checks identifier e and initializes x with the value or type of e. // If an error occurred, x.mode is set to invalid. // For the meaning of def, see Checker.definedType, below. // If wantType is set, the identifier e is expected to denote a type. -// func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType bool) { x.mode = invalid x.expr = e @@ -30,9 +24,18 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo // Note that we cannot use check.lookup here because the returned scope // may be different from obj.Parent(). See also Scope.LookupParent doc. scope, obj := check.scope.LookupParent(e.Value, check.pos) - if obj == nil { + switch obj { + case nil: if e.Value == "_" { - check.error(e, "cannot use _ as value or type") + // Blank identifiers are never declared, but the current identifier may + // be a placeholder for a receiver type parameter. In this case we can + // resolve its type and object from Checker.recvTParamMap. + if tpar := check.recvTParamMap[e]; tpar != nil { + x.mode = typexpr + x.typ = tpar + } else { + check.error(e, "cannot use _ as value or type") + } } else { if check.conf.CompilerErrorMessages { check.errorf(e, "undefined: %s", e.Value) @@ -41,6 +44,11 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo } } return + case universeAny, universeComparable: + if !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(e, "go1.18", "predeclared %s", e.Value) + return // avoid follow-on errors + } } check.recordUse(e, obj) @@ -63,7 +71,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo // If so, mark the respective package as used. // (This code is only needed for dot-imports. Without them, // we only have to mark variables, see *Var case below). - if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil { + if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil { pkgName.used = true } @@ -90,6 +98,10 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo x.mode = constant_ case *TypeName: + if check.isBrokenAlias(obj) { + check.errorf(e, "invalid use of type alias %s in recursive type (see issue #50729)", obj.name) + return + } x.mode = typexpr case *Var: @@ -130,48 +142,44 @@ func (check *Checker) typ(e syntax.Expr) Type { } // varType type-checks the type expression e and returns its type, or Typ[Invalid]. -// The type must not be an (uninstantiated) generic type and it must be ordinary -// (see ordinaryType). +// The type must not be an (uninstantiated) generic type and it must not be a +// constraint interface. func (check *Checker) varType(e syntax.Expr) Type { typ := check.definedType(e, nil) - check.ordinaryType(syntax.StartPos(e), typ) + check.validVarType(e, typ) return typ } -// ordinaryType reports an error if typ is an interface type containing -// type lists or is (or embeds) the predeclared type comparable. -func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) { - // We don't want to call under() (via Interface) or complete interfaces while we - // are in the middle of type-checking parameter declarations that might belong to - // interface methods. Delay this check to the end of type-checking. +// validVarType reports an error if typ is a constraint interface. +// The expression e is used for error reporting, if any. +func (check *Checker) validVarType(e syntax.Expr, typ Type) { + // If we have a type parameter there's nothing to do. + if isTypeParam(typ) { + return + } + + // We don't want to call under() or complete interfaces while we are in + // the middle of type-checking parameter declarations that might belong + // to interface methods. Delay this check to the end of type-checking. check.later(func() { - if t := asInterface(typ); t != nil { - check.completeInterface(pos, t) // TODO(gri) is this the correct position? - if t.allTypes != nil { - check.softErrorf(pos, "interface contains type constraints (%s)", t.allTypes) - return - } - if t.IsComparable() { - check.softErrorf(pos, "interface is (or embeds) comparable") + if t, _ := under(typ).(*Interface); t != nil { + pos := syntax.StartPos(e) + tset := computeInterfaceTypeSet(check, pos, t) // TODO(gri) is this the correct position? + if !tset.IsMethodSet() { + if tset.comparable { + check.softErrorf(pos, "cannot use type %s outside a type constraint: interface is (or embeds) comparable", typ) + } else { + check.softErrorf(pos, "cannot use type %s outside a type constraint: interface contains type constraints", typ) + } } } - }) -} - -// anyType type-checks the type expression e and returns its type, or Typ[Invalid]. -// The type may be generic or instantiated. -func (check *Checker) anyType(e syntax.Expr) Type { - typ := check.typInternal(e, nil) - assert(isTyped(typ)) - check.recordTypeAndValue(e, typexpr, typ, nil) - return typ + }).describef(e, "check var type %s", typ) } // definedType is like typ but also accepts a type name def. // If def != nil, e is the type specification for the defined type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. -// func (check *Checker) definedType(e syntax.Expr, def *Named) Type { typ := check.typInternal(e, def) assert(isTyped(typ)) @@ -183,13 +191,15 @@ func (check *Checker) definedType(e syntax.Expr, def *Named) Type { return typ } -// genericType is like typ but the type must be an (uninstantiated) generic type. -func (check *Checker) genericType(e syntax.Expr, reportErr bool) Type { +// genericType is like typ but the type must be an (uninstantiated) generic +// type. If reason is non-nil and the type expression was a valid type but not +// generic, reason will be populated with a message describing the error. +func (check *Checker) genericType(e syntax.Expr, reason *string) Type { typ := check.typInternal(e, nil) assert(isTyped(typ)) if typ != Typ[Invalid] && !isGeneric(typ) { - if reportErr { - check.errorf(e, "%s is not a generic type", typ) + if reason != nil { + *reason = check.sprintf("%s is not a generic type", typ) } typ = Typ[Invalid] } @@ -198,238 +208,6 @@ func (check *Checker) genericType(e syntax.Expr, reportErr bool) Type { return typ } -// isubst returns an x with identifiers substituted per the substitution map smap. -// isubst only handles the case of (valid) method receiver type expressions correctly. -func isubst(x syntax.Expr, smap map[*syntax.Name]*syntax.Name) syntax.Expr { - switch n := x.(type) { - case *syntax.Name: - if alt := smap[n]; alt != nil { - return alt - } - // case *syntax.StarExpr: - // X := isubst(n.X, smap) - // if X != n.X { - // new := *n - // new.X = X - // return &new - // } - case *syntax.Operation: - if n.Op == syntax.Mul && n.Y == nil { - X := isubst(n.X, smap) - if X != n.X { - new := *n - new.X = X - return &new - } - } - case *syntax.IndexExpr: - Index := isubst(n.Index, smap) - if Index != n.Index { - new := *n - new.Index = Index - return &new - } - case *syntax.ListExpr: - var elems []syntax.Expr - for i, elem := range n.ElemList { - new := isubst(elem, smap) - if new != elem { - if elems == nil { - elems = make([]syntax.Expr, len(n.ElemList)) - copy(elems, n.ElemList) - } - elems[i] = new - } - } - if elems != nil { - new := *n - new.ElemList = elems - return &new - } - case *syntax.ParenExpr: - return isubst(n.X, smap) // no need to keep parentheses - default: - // Other receiver type expressions are invalid. - // It's fine to ignore those here as they will - // be checked elsewhere. - } - return x -} - -// funcType type-checks a function or method type. -func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []*syntax.Field, ftyp *syntax.FuncType) { - check.openScope(ftyp, "function") - check.scope.isFunc = true - check.recordScope(ftyp, check.scope) - sig.scope = check.scope - defer check.closeScope() - - var recvTyp syntax.Expr // rewritten receiver type; valid if != nil - if recvPar != nil { - // collect generic receiver type parameters, if any - // - a receiver type parameter is like any other type parameter, except that it is declared implicitly - // - the receiver specification acts as local declaration for its type parameters, which may be blank - _, rname, rparams := check.unpackRecv(recvPar.Type, true) - if len(rparams) > 0 { - // Blank identifiers don't get declared and regular type-checking of the instantiated - // parameterized receiver type expression fails in Checker.collectParams of receiver. - // Identify blank type parameters and substitute each with a unique new identifier named - // "n_" (where n is the parameter index) and which cannot conflict with any user-defined - // name. - var smap map[*syntax.Name]*syntax.Name // substitution map from "_" to "!n" identifiers - for i, p := range rparams { - if p.Value == "_" { - new := *p - new.Value = fmt.Sprintf("%d_", i) - rparams[i] = &new // use n_ identifier instead of _ so it can be looked up - if smap == nil { - smap = make(map[*syntax.Name]*syntax.Name) - } - smap[p] = &new - } - } - if smap != nil { - // blank identifiers were found => use rewritten receiver type - recvTyp = isubst(recvPar.Type, smap) - } - // TODO(gri) rework declareTypeParams - sig.rparams = nil - for _, rparam := range rparams { - sig.rparams = check.declareTypeParam(sig.rparams, rparam) - } - // determine receiver type to get its type parameters - // and the respective type parameter bounds - var recvTParams []*TypeName - if rname != nil { - // recv should be a Named type (otherwise an error is reported elsewhere) - // Also: Don't report an error via genericType since it will be reported - // again when we type-check the signature. - // TODO(gri) maybe the receiver should be marked as invalid instead? - if recv := asNamed(check.genericType(rname, false)); recv != nil { - recvTParams = recv.tparams - } - } - // provide type parameter bounds - // - only do this if we have the right number (otherwise an error is reported elsewhere) - if len(sig.rparams) == len(recvTParams) { - // We have a list of *TypeNames but we need a list of Types. - list := make([]Type, len(sig.rparams)) - for i, t := range sig.rparams { - list[i] = t.typ - } - smap := makeSubstMap(recvTParams, list) - for i, tname := range sig.rparams { - bound := recvTParams[i].typ.(*TypeParam).bound - // bound is (possibly) parameterized in the context of the - // receiver type declaration. Substitute parameters for the - // current context. - // TODO(gri) should we assume now that bounds always exist? - // (no bound == empty interface) - if bound != nil { - bound = check.subst(tname.pos, bound, smap) - tname.typ.(*TypeParam).bound = bound - } - } - } - } - } - - if tparams != nil { - sig.tparams = check.collectTypeParams(tparams) - // Always type-check method type parameters but complain if they are not enabled. - // (A separate check is needed when type-checking interface method signatures because - // they don't have a receiver specification.) - if recvPar != nil && !acceptMethodTypeParams { - check.error(ftyp, "methods cannot have type parameters") - } - } - - // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their - // declarations and then squash that scope into the parent scope (and report any redeclarations at - // that time). - scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)") - var recvList []*Var // TODO(gri) remove the need for making a list here - if recvPar != nil { - recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, recvTyp, false) // use rewritten receiver type, if any - } - params, variadic := check.collectParams(scope, ftyp.ParamList, nil, true) - results, _ := check.collectParams(scope, ftyp.ResultList, nil, false) - scope.Squash(func(obj, alt Object) { - var err error_ - err.errorf(obj, "%s redeclared in this block", obj.Name()) - err.recordAltDecl(alt) - check.report(&err) - }) - - if recvPar != nil { - // recv parameter list present (may be empty) - // spec: "The receiver is specified via an extra parameter section preceding the - // method name. That parameter section must declare a single parameter, the receiver." - var recv *Var - switch len(recvList) { - case 0: - // error reported by resolver - recv = NewParam(nopos, nil, "", Typ[Invalid]) // ignore recv below - default: - // more than one receiver - check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver") - fallthrough // continue with first receiver - case 1: - recv = recvList[0] - } - - // TODO(gri) We should delay rtyp expansion to when we actually need the - // receiver; thus all checks here should be delayed to later. - rtyp, _ := deref(recv.typ) - rtyp = expand(rtyp) - - // spec: "The receiver type must be of the form T or *T where T is a type name." - // (ignore invalid types - error was reported before) - if t := rtyp; t != Typ[Invalid] { - var err string - if T := asNamed(t); T != nil { - // spec: "The type denoted by T is called the receiver base type; it must not - // be a pointer or interface type and it must be declared in the same package - // as the method." - if T.obj.pkg != check.pkg { - err = "type not defined in this package" - if check.conf.CompilerErrorMessages { - check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ) - err = "" - } - } else { - switch u := optype(T).(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" - } - case *Pointer, *Interface: - err = "pointer or interface type" - } - } - } else if T := asBasic(t); T != nil { - err = "basic or unnamed type" - if check.conf.CompilerErrorMessages { - check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ) - err = "" - } - } else { - check.errorf(recv.pos, "invalid receiver type %s", recv.typ) - } - if err != "" { - check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err) - // ok to continue - } - } - sig.recv = recv - } - - sig.params = NewTuple(params...) - sig.results = NewTuple(results...) - sig.variadic = variadic -} - // goTypeName returns the Go type name for typ and // removes any occurrences of "types2." from that name. func goTypeName(typ Type) string { @@ -438,10 +216,9 @@ func goTypeName(typ Type) string { // typInternal drives type checking of types. // Must only be called by definedType or genericType. -// func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { if check.conf.Trace { - check.trace(e0.Pos(), "type %s", e0) + check.trace(e0.Pos(), "-- type %s", e0) check.indent++ defer func() { check.indent-- @@ -449,9 +226,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { if T != nil { // Calling under() here may lead to endless instantiations. // Test case: type T[P any] *T[P] - // TODO(gri) investigate if that's a bug or to be expected - // (see also analogous comment in Checker.instantiate). - under = T.Underlying() + under = safeUnderlying(T) } if T == under { check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T)) @@ -484,7 +259,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { case *syntax.SelectorExpr: var x operand - check.selector(&x, e) + check.selector(&x, e, def) switch x.mode { case typexpr: @@ -500,6 +275,9 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { } case *syntax.IndexExpr: + if !check.allowVersion(check.pkg, 1, 18) { + check.versionErrorf(e.Pos(), "go1.18", "type instantiation") + } return check.instantiatedType(e.X, unpackExpr(e.Index), def) case *syntax.ParenExpr: @@ -544,8 +322,16 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { case *syntax.Operation: if e.Op == syntax.Mul && e.Y == nil { typ := new(Pointer) + typ.base = Typ[Invalid] // avoid nil base in invalid recursive type declaration def.setUnderlying(typ) typ.base = check.varType(e.X) + // If typ.base is invalid, it's unlikely that *base is particularly + // useful - even a valid dereferenciation will lead to an invalid + // type again, and in some cases we get unexpected follow-on errors + // (e.g., see #49005). Return an invalid type instead. + if typ.base == Typ[Invalid] { + return Typ[Invalid] + } return typ } @@ -559,11 +345,8 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { return typ case *syntax.InterfaceType: - typ := new(Interface) + typ := check.newInterface() def.setUnderlying(typ) - if def != nil { - typ.obj = def.obj - } check.interfaceType(typ, e, def) return typ @@ -583,12 +366,12 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { check.later(func() { if !Comparable(typ.key) { var why string - if asTypeParam(typ.key) != nil { + if isTypeParam(typ.key) { why = " (missing comparable constraint)" } check.errorf(e.Key, "invalid map key type %s%s", typ.key, why) } - }) + }).describef(e.Key, "check map key %s", typ.key) return typ @@ -623,77 +406,91 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { return typ } -// typeOrNil type-checks the type expression (or nil value) e -// and returns the type of e, or nil. If e is a type, it must -// not be an (uninstantiated) generic type. -// If e is neither a type nor nil, typeOrNil returns Typ[Invalid]. -// TODO(gri) should we also disallow non-var types? -func (check *Checker) typOrNil(e syntax.Expr) Type { - var x operand - check.rawExpr(&x, e, nil) - switch x.mode { - case invalid: - // ignore - error reported before - case novalue: - check.errorf(&x, "%s used as type", &x) - case typexpr: - check.instantiatedOperand(&x) - return x.typ - case nilvalue: - return nil - default: - check.errorf(&x, "%s is not a type", &x) +func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *Named) (res Type) { + if check.conf.Trace { + check.trace(x.Pos(), "-- instantiating type %s with %s", x, xlist) + check.indent++ + defer func() { + check.indent-- + // Don't format the underlying here. It will always be nil. + check.trace(x.Pos(), "=> %s", res) + }() } - return Typ[Invalid] -} -func (check *Checker) instantiatedType(x syntax.Expr, targs []syntax.Expr, def *Named) Type { - b := check.genericType(x, true) // TODO(gri) what about cycles? - if b == Typ[Invalid] { - return b // error already reported + var reason string + gtyp := check.genericType(x, &reason) + if reason != "" { + check.errorf(x, invalidOp+"%s%s (%s)", x, xlist, reason) } - base := asNamed(b) - if base == nil { - unreachable() // should have been caught by genericType + if gtyp == Typ[Invalid] { + return gtyp // error already reported } - // create a new type instance rather than instantiate the type - // TODO(gri) should do argument number check here rather than - // when instantiating the type? - typ := new(instance) - def.setUnderlying(typ) - - typ.check = check - typ.pos = x.Pos() - typ.base = base + orig, _ := gtyp.(*Named) + if orig == nil { + panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp)) + } - // evaluate arguments (always) - typ.targs = check.typeList(targs) - if typ.targs == nil { - def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation + // evaluate arguments + targs := check.typeList(xlist) + if targs == nil { + def.setUnderlying(Typ[Invalid]) // avoid errors later due to lazy instantiation return Typ[Invalid] } - // determine argument positions (for error reporting) - typ.poslist = make([]syntax.Pos, len(targs)) - for i, arg := range targs { - typ.poslist[i] = syntax.StartPos(arg) - } + // create the instance + inst := check.instance(x.Pos(), orig, targs, nil, check.context()).(*Named) + def.setUnderlying(inst) - // make sure we check instantiation works at least once - // and that the resulting type is valid + // orig.tparams may not be set up, so we need to do expansion later. check.later(func() { - t := typ.expand() - check.validType(t, nil) - }) + // This is an instance from the source, not from recursive substitution, + // and so it must be resolved during type-checking so that we can report + // errors. + check.recordInstance(x, inst.TypeArgs().list(), inst) + + if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { + if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil { + // best position for error reporting + pos := x.Pos() + if i < len(xlist) { + pos = syntax.StartPos(xlist[i]) + } + check.softErrorf(pos, "%s", err) + } else { + check.mono.recordInstance(check.pkg, x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), xlist) + } + } - return typ + // TODO(rfindley): remove this call: we don't need to call validType here, + // as cycles can only occur for types used inside a Named type declaration, + // and so it suffices to call validType from declared types. + check.validType(inst) + }).describef(x, "resolve instance %s", inst) + + return inst } // arrayLength type-checks the array length expression e // and returns the constant length >= 0, or a value < 0 // to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e syntax.Expr) int64 { + // If e is an identifier, the array declaration might be an + // attempt at a parameterized type declaration with missing + // constraint. Provide an error message that mentions array + // length. + if name, _ := e.(*syntax.Name); name != nil { + obj := check.lookup(name.Value) + if obj == nil { + check.errorf(name, "undeclared name %s for array length", name.Value) + return -1 + } + if _, ok := obj.(*Const); !ok { + check.errorf(name, "invalid array length %s", name.Value) + return -1 + } + } + var x operand check.expr(&x, e) if x.mode != constant_ { @@ -702,6 +499,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 { } return -1 } + if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check, Typ[Int], nil) { @@ -713,6 +511,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 { } } } + check.errorf(&x, "array length %s must be integer", &x) return -1 } @@ -732,537 +531,3 @@ func (check *Checker) typeList(list []syntax.Expr) []Type { } return res } - -// collectParams declares the parameters of list in scope and returns the corresponding -// variable list. If type0 != nil, it is used instead of the first type in list. -func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 syntax.Expr, variadicOk bool) (params []*Var, variadic bool) { - if list == nil { - return - } - - var named, anonymous bool - - var typ Type - var prev syntax.Expr - for i, field := range list { - ftype := field.Type - // type-check type of grouped fields only once - if ftype != prev { - prev = ftype - if i == 0 && type0 != nil { - ftype = type0 - } - if t, _ := ftype.(*syntax.DotsType); t != nil { - ftype = t.Elem - if variadicOk && i == len(list)-1 { - variadic = true - } else { - check.softErrorf(t, "can only use ... with final parameter in list") - // ignore ... and continue - } - } - typ = check.varType(ftype) - } - // The parser ensures that f.Tag is nil and we don't - // care if a constructed AST contains a non-nil tag. - if field.Name != nil { - // named parameter - name := field.Name.Value - if name == "" { - check.error(field.Name, invalidAST+"anonymous parameter") - // ok to continue - } - par := NewParam(field.Name.Pos(), check.pkg, name, typ) - check.declare(scope, field.Name, par, scope.pos) - params = append(params, par) - named = true - } else { - // anonymous parameter - par := NewParam(ftype.Pos(), check.pkg, "", typ) - check.recordImplicit(field, par) - params = append(params, par) - anonymous = true - } - } - - if named && anonymous { - check.error(list[0], invalidAST+"list contains both named and anonymous parameters") - // ok to continue - } - - // For a variadic function, change the last parameter's type from T to []T. - // Since we type-checked T rather than ...T, we also need to retro-actively - // record the type for ...T. - if variadic { - last := params[len(params)-1] - last.typ = &Slice{elem: last.typ} - check.recordTypeAndValue(list[len(list)-1].Type, typexpr, last.typ, nil) - } - - return -} - -func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool { - if alt := oset.insert(obj); alt != nil { - var err error_ - err.errorf(pos, "%s redeclared", obj.Name()) - err.recordAltDecl(alt) - check.report(&err) - return false - } - return true -} - -func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) { - var tname *syntax.Name // most recent "type" name - var types []syntax.Expr - for _, f := range iface.MethodList { - if f.Name != nil { - // We have a method with name f.Name, or a type - // of a type list (f.Name.Value == "type"). - name := f.Name.Value - if name == "_" { - if check.conf.CompilerErrorMessages { - check.error(f.Name, "methods must have a unique non-blank name") - } else { - check.error(f.Name, "invalid method name _") - } - continue // ignore - } - - if name == "type" { - // Always collect all type list entries, even from - // different type lists, under the assumption that - // the author intended to include all types. - types = append(types, f.Type) - if tname != nil && tname != f.Name { - check.error(f.Name, "cannot have multiple type lists in an interface") - } - tname = f.Name - continue - } - - typ := check.typ(f.Type) - sig, _ := typ.(*Signature) - if sig == nil { - if typ != Typ[Invalid] { - check.errorf(f.Type, invalidAST+"%s is not a method signature", typ) - } - continue // ignore - } - - // Always type-check method type parameters but complain if they are not enabled. - // (This extra check is needed here because interface method signatures don't have - // a receiver specification.) - if sig.tparams != nil && !acceptMethodTypeParams { - check.error(f.Type, "methods cannot have type parameters") - } - - // use named receiver type if available (for better error messages) - var recvTyp Type = ityp - if def != nil { - recvTyp = def - } - sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp) - - m := NewFunc(f.Name.Pos(), check.pkg, name, sig) - check.recordDef(f.Name, m) - ityp.methods = append(ityp.methods, m) - } else { - // We have an embedded type. completeInterface will - // eventually verify that we have an interface. - ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type)) - check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos()) - } - } - - // type constraints - ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types)) - - if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 { - // empty interface - ityp.allMethods = markComplete - return - } - - // sort for API stability - sortMethods(ityp.methods) - sortTypes(ityp.embeddeds) - - check.later(func() { check.completeInterface(iface.Pos(), ityp) }) -} - -func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) { - if ityp.allMethods != nil { - return - } - - // completeInterface may be called via the LookupFieldOrMethod, - // MissingMethod, Identical, or IdenticalIgnoreTags external API - // in which case check will be nil. In this case, type-checking - // must be finished and all interfaces should have been completed. - if check == nil { - panic("internal error: incomplete interface") - } - - if check.conf.Trace { - // Types don't generally have position information. - // If we don't have a valid pos provided, try to use - // one close enough. - if !pos.IsKnown() && len(ityp.methods) > 0 { - pos = ityp.methods[0].pos - } - - check.trace(pos, "complete %s", ityp) - check.indent++ - defer func() { - check.indent-- - check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes) - }() - } - - // An infinitely expanding interface (due to a cycle) is detected - // elsewhere (Checker.validType), so here we simply assume we only - // have valid interfaces. Mark the interface as complete to avoid - // infinite recursion if the validType check occurs later for some - // reason. - ityp.allMethods = markComplete - - // Methods of embedded interfaces are collected unchanged; i.e., the identity - // of a method I.m's Func Object of an interface I is the same as that of - // the method m in an interface that embeds interface I. On the other hand, - // if a method is embedded via multiple overlapping embedded interfaces, we - // don't provide a guarantee which "original m" got chosen for the embedding - // interface. See also issue #34421. - // - // If we don't care to provide this identity guarantee anymore, instead of - // reusing the original method in embeddings, we can clone the method's Func - // Object and give it the position of a corresponding embedded interface. Then - // we can get rid of the mpos map below and simply use the cloned method's - // position. - - var seen objset - var methods []*Func - mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages - addMethod := func(pos syntax.Pos, m *Func, explicit bool) { - switch other := seen.insert(m); { - case other == nil: - methods = append(methods, m) - mpos[m] = pos - case explicit: - var err error_ - err.errorf(pos, "duplicate method %s", m.name) - err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name) - check.report(&err) - default: - // We have a duplicate method name in an embedded (not explicitly declared) method. - // Check method signatures after all types are computed (issue #33656). - // If we're pre-go1.14 (overlapping embeddings are not permitted), report that - // error here as well (even though we could do it eagerly) because it's the same - // error message. - check.later(func() { - if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) { - var err error_ - err.errorf(pos, "duplicate method %s", m.name) - err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name) - check.report(&err) - } - }) - } - } - - for _, m := range ityp.methods { - addMethod(m.pos, m, true) - } - - // collect types - allTypes := ityp.types - - posList := check.posMap[ityp] - for i, typ := range ityp.embeddeds { - pos := posList[i] // embedding position - utyp := under(typ) - etyp := asInterface(utyp) - if etyp == nil { - if utyp != Typ[Invalid] { - var format string - if _, ok := utyp.(*TypeParam); ok { - format = "%s is a type parameter, not an interface" - } else { - format = "%s is not an interface" - } - check.errorf(pos, format, typ) - } - continue - } - check.completeInterface(pos, etyp) - for _, m := range etyp.allMethods { - addMethod(pos, m, false) // use embedding position pos rather than m.pos - } - allTypes = intersect(allTypes, etyp.allTypes) - } - - if methods != nil { - sortMethods(methods) - ityp.allMethods = methods - } - ityp.allTypes = allTypes -} - -// intersect computes the intersection of the types x and y. -// Note: A incomming nil type stands for the top type. A top -// type result is returned as nil. -func intersect(x, y Type) (r Type) { - defer func() { - if r == theTop { - r = nil - } - }() - - switch { - case x == theBottom || y == theBottom: - return theBottom - case x == nil || x == theTop: - return y - case y == nil || x == theTop: - return x - } - - xtypes := unpack(x) - ytypes := unpack(y) - // Compute the list rtypes which includes only - // types that are in both xtypes and ytypes. - // Quadratic algorithm, but good enough for now. - // TODO(gri) fix this - var rtypes []Type - for _, x := range xtypes { - if includes(ytypes, x) { - rtypes = append(rtypes, x) - } - } - - if rtypes == nil { - return theBottom - } - return NewSum(rtypes) -} - -func sortTypes(list []Type) { - sort.Stable(byUniqueTypeName(list)) -} - -// byUniqueTypeName named type lists can be sorted by their unique type names. -type byUniqueTypeName []Type - -func (a byUniqueTypeName) Len() int { return len(a) } -func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) } -func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func sortName(t Type) string { - if named := asNamed(t); named != nil { - return named.obj.Id() - } - return "" -} - -func sortMethods(list []*Func) { - sort.Sort(byUniqueMethodName(list)) -} - -func assertSortedMethods(list []*Func) { - if !debug { - panic("internal error: assertSortedMethods called outside debug mode") - } - if !sort.IsSorted(byUniqueMethodName(list)) { - panic("internal error: methods not sorted") - } -} - -// byUniqueMethodName method lists can be sorted by their unique method names. -type byUniqueMethodName []*Func - -func (a byUniqueMethodName) Len() int { return len(a) } -func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(a[j]) } -func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -func (check *Checker) tag(t *syntax.BasicLit) string { - // If t.Bad, an error was reported during parsing. - if t != nil && !t.Bad { - if t.Kind == syntax.StringLit { - if val, err := strconv.Unquote(t.Value); err == nil { - return val - } - } - check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value) - } - return "" -} - -func (check *Checker) structType(styp *Struct, e *syntax.StructType) { - if e.FieldList == nil { - return - } - - // struct fields and tags - var fields []*Var - var tags []string - - // for double-declaration checks - var fset objset - - // current field typ and tag - var typ Type - var tag string - add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) { - if tag != "" && tags == nil { - tags = make([]string, len(fields)) - } - if tags != nil { - tags = append(tags, tag) - } - - name := ident.Value - fld := NewField(pos, check.pkg, name, typ, embedded) - // spec: "Within a struct, non-blank field names must be unique." - if name == "_" || check.declareInSet(&fset, pos, fld) { - fields = append(fields, fld) - check.recordDef(ident, fld) - } - } - - // addInvalid adds an embedded field of invalid type to the struct for - // fields with errors; this keeps the number of struct fields in sync - // with the source as long as the fields are _ or have different names - // (issue #25627). - addInvalid := func(ident *syntax.Name, pos syntax.Pos) { - typ = Typ[Invalid] - tag = "" - add(ident, true, pos) - } - - var prev syntax.Expr - for i, f := range e.FieldList { - // Fields declared syntactically with the same type (e.g.: a, b, c T) - // share the same type expression. Only check type if it's a new type. - if i == 0 || f.Type != prev { - typ = check.varType(f.Type) - prev = f.Type - } - tag = "" - if i < len(e.TagList) { - tag = check.tag(e.TagList[i]) - } - if f.Name != nil { - // named field - add(f.Name, false, f.Name.Pos()) - } else { - // embedded field - // spec: "An embedded type must be specified as a type name T or as a - // pointer to a non-interface type name *T, and T itself may not be a - // pointer type." - pos := syntax.StartPos(f.Type) - name := embeddedFieldIdent(f.Type) - if name == nil { - check.errorf(pos, "invalid embedded field type %s", f.Type) - name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos - addInvalid(name, pos) - continue - } - add(name, true, pos) - - // Because we have a name, typ must be of the form T or *T, where T is the name - // of a (named or alias) type, and t (= deref(typ)) must be the type of T. - // We must delay this check to the end because we don't want to instantiate - // (via under(t)) a possibly incomplete type. - embeddedTyp := typ // for closure below - embeddedPos := pos - check.later(func() { - t, isPtr := deref(embeddedTyp) - switch t := optype(t).(type) { - case *Basic: - if t == Typ[Invalid] { - // error was reported before - return - } - // unsafe.Pointer is treated like a regular pointer - if t.kind == UnsafePointer { - check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer") - } - case *Pointer: - check.error(embeddedPos, "embedded field type cannot be a pointer") - case *Interface: - if isPtr { - check.error(embeddedPos, "embedded field type cannot be a pointer to an interface") - } - } - }) - } - } - - styp.fields = fields - styp.tags = tags -} - -func embeddedFieldIdent(e syntax.Expr) *syntax.Name { - switch e := e.(type) { - case *syntax.Name: - return e - case *syntax.Operation: - if base := ptrBase(e); base != nil { - // *T is valid, but **T is not - if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil { - return embeddedFieldIdent(e.X) - } - } - case *syntax.SelectorExpr: - return e.Sel - case *syntax.IndexExpr: - return embeddedFieldIdent(e.X) - } - return nil // invalid embedded field -} - -func (check *Checker) collectTypeConstraints(pos syntax.Pos, types []syntax.Expr) []Type { - list := make([]Type, 0, len(types)) // assume all types are correct - for _, texpr := range types { - if texpr == nil { - check.error(pos, invalidAST+"missing type constraint") - continue - } - list = append(list, check.varType(texpr)) - } - - // Ensure that each type is only present once in the type list. Types may be - // interfaces, which may not be complete yet. It's ok to do this check at the - // end because it's not a requirement for correctness of the code. - // Note: This is a quadratic algorithm, but type lists tend to be short. - check.later(func() { - for i, t := range list { - if t := asInterface(t); t != nil { - check.completeInterface(types[i].Pos(), t) - } - if includes(list[:i], t) { - check.softErrorf(types[i], "duplicate type %s in type list", t) - } - } - }) - - return list -} - -// includes reports whether typ is in list -func includes(list []Type, typ Type) bool { - for _, e := range list { - if Identical(typ, e) { - return true - } - } - return false -} - -func ptrBase(x *syntax.Operation) syntax.Expr { - if x.Op == syntax.Mul && x.Y == nil { - return x.X - } - return nil -} diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index e1832bbb2a3623..7063789f3f0c0f 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -6,7 +6,11 @@ package types2 -import "bytes" +import ( + "bytes" + "fmt" + "strings" +) // The unifier maintains two separate sets of type parameters x and y // which are used to resolve type parameters in the x and y arguments @@ -23,21 +27,45 @@ import "bytes" // parameter P ("x" side), but the argument type P must be left alone so // that unification resolves the type parameter P to P. // -// For bidirection unification, both sets are provided. This enables +// For bidirectional unification, both sets are provided. This enables // unification to go from argument to parameter type and vice versa. // For constraint type inference, we use bidirectional unification // where both the x and y type parameters are identical. This is done // by setting up one of them (using init) and then assigning its value // to the other. +const ( + // Upper limit for recursion depth. Used to catch infinite recursions + // due to implementation issues (e.g., see issues #48619, #48656). + unificationDepthLimit = 50 + + // Whether to panic when unificationDepthLimit is reached. + // If disabled, a recursion depth overflow results in a (quiet) + // unification failure. + panicAtUnificationDepthLimit = true + + // If enableCoreTypeUnification is set, unification will consider + // the core types, if any, of non-local (unbound) type parameters. + enableCoreTypeUnification = true + + // If traceInference is set, unification will print a trace of its operation. + // Interpretation of trace: + // x ≡ y attempt to unify types x and y + // p ➞ y type parameter p is set to type y (p is inferred to be y) + // p ⇄ q type parameters p and q match (p is inferred to be q and vice versa) + // x ≢ y types x and y cannot be unified + // [p, q, ...] ➞ [x, y, ...] mapping from type parameters to types + traceInference = false +) + // A unifier maintains the current type parameters for x and y // and the respective types inferred for each type parameter. // A unifier is created by calling newUnifier. type unifier struct { - check *Checker exact bool x, y tparamsList // x and y must initialized via tparamsList.init types []Type // inferred types, shared by x and y + depth int // recursion depth during unification } // newUnifier returns a new unifier. @@ -45,8 +73,9 @@ type unifier struct { // exactly. If exact is not set, a named type's underlying type // is considered if unification would fail otherwise, and the // direction of channels is ignored. -func newUnifier(check *Checker, exact bool) *unifier { - u := &unifier{check: check, exact: exact} +// TODO(gri) exact is not set anymore by a caller. Consider removing it. +func newUnifier(exact bool) *unifier { + u := &unifier{exact: exact} u.x.unifier = u u.y.unifier = u return u @@ -57,10 +86,14 @@ func (u *unifier) unify(x, y Type) bool { return u.nify(x, y, nil) } +func (u *unifier) tracef(format string, args ...interface{}) { + fmt.Println(strings.Repeat(". ", u.depth) + sprintf(nil, true, format, args...)) +} + // A tparamsList describes a list of type parameters and the types inferred for them. type tparamsList struct { unifier *unifier - tparams []*TypeName + tparams []*TypeParam // For each tparams element, there is a corresponding type slot index in indices. // index < 0: unifier.types[-index-1] == nil // index == 0: no type slot allocated yet @@ -74,29 +107,30 @@ type tparamsList struct { // String returns a string representation for a tparamsList. For debugging. func (d *tparamsList) String() string { var buf bytes.Buffer - buf.WriteByte('[') - for i, tname := range d.tparams { + w := newTypeWriter(&buf, nil) + w.byte('[') + for i, tpar := range d.tparams { if i > 0 { - buf.WriteString(", ") + w.string(", ") } - writeType(&buf, tname.typ, nil, nil) - buf.WriteString(": ") - writeType(&buf, d.at(i), nil, nil) + w.typ(tpar) + w.string(": ") + w.typ(d.at(i)) } - buf.WriteByte(']') + w.byte(']') return buf.String() } // init initializes d with the given type parameters. // The type parameters must be in the order in which they appear in their declaration // (this ensures that the tparams indices match the respective type parameter index). -func (d *tparamsList) init(tparams []*TypeName) { +func (d *tparamsList) init(tparams []*TypeParam) { if len(tparams) == 0 { return } if debug { for i, tpar := range tparams { - assert(i == tpar.typ.(*TypeParam).index) + assert(i == tpar.index) } } d.tparams = tparams @@ -105,8 +139,11 @@ func (d *tparamsList) init(tparams []*TypeName) { // join unifies the i'th type parameter of x with the j'th type parameter of y. // If both type parameters already have a type associated with them and they are -// not joined, join fails and return false. +// not joined, join fails and returns false. func (u *unifier) join(i, j int) bool { + if traceInference { + u.tracef("%s ⇄ %s", u.x.tparams[i], u.y.tparams[j]) + } ti := u.x.indices[i] tj := u.y.indices[j] switch { @@ -129,6 +166,7 @@ func (u *unifier) join(i, j int) bool { break case ti > 0 && tj > 0: // Both type parameters have (possibly different) inferred types. Cannot join. + // TODO(gri) Should we check if types are identical? Investigate. return false case ti > 0: // Only the type parameter for x has an inferred type. Use x slot for y. @@ -148,10 +186,23 @@ func (u *unifier) join(i, j int) bool { // If typ is a type parameter of d, index returns the type parameter index. // Otherwise, the result is < 0. func (d *tparamsList) index(typ Type) int { - if t, ok := typ.(*TypeParam); ok { - if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t { - return i - } + if tpar, ok := typ.(*TypeParam); ok { + return tparamIndex(d.tparams, tpar) + } + return -1 +} + +// If tpar is a type parameter in list, tparamIndex returns the type parameter index. +// Otherwise, the result is < 0. tpar must not be nil. +func tparamIndex(list []*TypeParam, tpar *TypeParam) int { + // Once a type parameter is bound its index is >= 0. However, there are some + // code paths (namely tracing and type hashing) by which it is possible to + // arrive here with a type parameter that has not been bound, hence the check + // for 0 <= i below. + // TODO(rfindley): investigate a better approach for guarding against using + // unbound type parameters. + if i := tpar.index; 0 <= i && i < len(list) && list[i] == tpar { + return i } return -1 } @@ -182,6 +233,9 @@ func (d *tparamsList) at(i int) Type { func (d *tparamsList) set(i int, typ Type) { assert(typ != nil) u := d.unifier + if traceInference { + u.tracef("%s ➞ %s", d.tparams[i], typ) + } switch ti := d.indices[i]; { case ti < 0: u.types[-ti-1] = typ @@ -194,6 +248,17 @@ func (d *tparamsList) set(i int, typ Type) { } } +// unknowns returns the number of type parameters for which no type has been set yet. +func (d *tparamsList) unknowns() int { + n := 0 + for _, ti := range d.indices { + if ti <= 0 { + n++ + } + } + return n +} + // types returns the list of inferred types (via unification) for the type parameters // described by d, and an index. If all types were inferred, the returned index is < 0. // Otherwise, it is the index of the first type parameter which couldn't be inferred; @@ -216,26 +281,48 @@ func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool { } // nify implements the core unification algorithm which is an -// adapted version of Checker.identical0. For changes to that +// adapted version of Checker.identical. For changes to that // code the corresponding changes should be made here. // Must not be called directly from outside the unifier. -func (u *unifier) nify(x, y Type, p *ifacePair) bool { - // types must be expanded for comparison - x = expand(x) - y = expand(y) +func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) { + if traceInference { + u.tracef("%s ≡ %s", x, y) + } + + // Stop gap for cases where unification fails. + if u.depth >= unificationDepthLimit { + if traceInference { + u.tracef("depth %d >= %d", u.depth, unificationDepthLimit) + } + if panicAtUnificationDepthLimit { + panic("unification reached recursion depth limit") + } + return false + } + u.depth++ + defer func() { + u.depth-- + if traceInference && !result { + u.tracef("%s ≢ %s", x, y) + } + }() if !u.exact { // If exact unification is known to fail because we attempt to // match a type name against an unnamed type literal, consider // the underlying type of the named type. - // (Subtle: We use isNamed to include any type with a name (incl. - // basic types and type parameters. We use asNamed because we only - // want *Named types.) - switch { - case !isNamed(x) && y != nil && asNamed(y) != nil: - return u.nify(x, under(y), p) - case x != nil && asNamed(x) != nil && !isNamed(y): - return u.nify(under(x), y, p) + // (We use !hasName to exclude any type with a name, including + // basic types and type parameters; the rest are unamed types.) + if nx, _ := x.(*Named); nx != nil && !hasName(y) { + if traceInference { + u.tracef("under %s ≡ %s", nx, y) + } + return u.nify(nx.under(), y, p) + } else if ny, _ := y.(*Named); ny != nil && !hasName(x) { + if traceInference { + u.tracef("%s ≡ under %s", x, ny) + } + return u.nify(x, ny.under(), p) } } @@ -268,6 +355,39 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { return true } + // If we get here and x or y is a type parameter, they are type parameters + // from outside our declaration list. Try to unify their core types, if any + // (see issue #50755 for a test case). + if enableCoreTypeUnification && !u.exact { + if isTypeParam(x) && !hasName(y) { + // When considering the type parameter for unification + // we look at the adjusted core term (adjusted core type + // with tilde information). + // If the adjusted core type is a named type N; the + // corresponding core type is under(N). Since !u.exact + // and y doesn't have a name, unification will end up + // comparing under(N) to y, so we can just use the core + // type instead. And we can ignore the tilde because we + // already look at the underlying types on both sides + // and we have known types on both sides. + // Optimization. + if cx := coreType(x); cx != nil { + if traceInference { + u.tracef("core %s ≡ %s", x, y) + } + return u.nify(cx, y, p) + } + } else if isTypeParam(y) && !hasName(x) { + // see comment above + if cy := coreType(y); cy != nil { + if traceInference { + u.tracef("%s ≡ core %s", x, y) + } + return u.nify(x, cy, p) + } + } + } + // For type unification, do not shortcut (x == y) for identical // types. Instead keep comparing them element-wise to unify the // matching (and equal type parameter types). A simple test case @@ -352,25 +472,21 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { u.nify(x.results, y.results, p) } - case *Sum: - // This should not happen with the current internal use of sum types. - panic("type inference across sum types not implemented") - case *Interface: // Two interface types are identical if they have the same set of methods with // the same names and identical function types. Lower-case method names from // different packages are always different. The order of the methods is irrelevant. if y, ok := y.(*Interface); ok { - // If identical0 is called (indirectly) via an external API entry point - // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in - // that case, interfaces are expected to be complete and lazy completion - // here is not needed. - if u.check != nil { - u.check.completeInterface(nopos, x) - u.check.completeInterface(nopos, y) + xset := x.typeSet() + yset := y.typeSet() + if xset.comparable != yset.comparable { + return false + } + if !xset.terms.equal(yset.terms) { + return false } - a := x.allMethods - b := y.allMethods + a := xset.methods + b := yset.methods if len(a) == len(b) { // Interface types are the only types where cycles can occur // that are not "terminated" via named types; and such cycles @@ -428,19 +544,21 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { } case *Named: - // Two named types are identical if their type names originate - // in the same type declaration. - // if y, ok := y.(*Named); ok { - // return x.obj == y.obj - // } + // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate. if y, ok := y.(*Named); ok { + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() + + if len(xargs) != len(yargs) { + return false + } + // TODO(gri) This is not always correct: two types may have the same names // in the same package if one of them is nested in a function. // Extremely unlikely but we need an always correct solution. if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name { - assert(len(x.targs) == len(y.targs)) - for i, x := range x.targs { - if !u.nify(x, y.targs[i], p) { + for i, x := range xargs { + if !u.nify(x, yargs[i], p) { return false } } @@ -454,15 +572,11 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { // are identical if they originate in the same declaration. return x == y - // case *instance: - // unreachable since types are expanded - case nil: // avoid a crash in case of nil type default: - u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams) - unreachable() + panic(sprintf(nil, true, "u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)) } return false diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go new file mode 100644 index 00000000000000..0ed125fb29ceb8 --- /dev/null +++ b/src/cmd/compile/internal/types2/union.go @@ -0,0 +1,196 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +import "cmd/compile/internal/syntax" + +// ---------------------------------------------------------------------------- +// API + +// A Union represents a union of terms embedded in an interface. +type Union struct { + terms []*Term // list of syntactical terms (not a canonicalized termlist) +} + +// NewUnion returns a new Union type with the given terms. +// It is an error to create an empty union; they are syntactically not possible. +func NewUnion(terms []*Term) *Union { + if len(terms) == 0 { + panic("empty union") + } + return &Union{terms} +} + +func (u *Union) Len() int { return len(u.terms) } +func (u *Union) Term(i int) *Term { return u.terms[i] } + +func (u *Union) Underlying() Type { return u } +func (u *Union) String() string { return TypeString(u, nil) } + +// A Term represents a term in a Union. +type Term term + +// NewTerm returns a new union term. +func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} } + +func (t *Term) Tilde() bool { return t.tilde } +func (t *Term) Type() Type { return t.typ } +func (t *Term) String() string { return (*term)(t).String() } + +// ---------------------------------------------------------------------------- +// Implementation + +// Avoid excessive type-checking times due to quadratic termlist operations. +const maxTermCount = 100 + +// parseUnion parses uexpr as a union of expressions. +// The result is a Union type, or Typ[Invalid] for some errors. +func parseUnion(check *Checker, uexpr syntax.Expr) Type { + blist, tlist := flattenUnion(nil, uexpr) + assert(len(blist) == len(tlist)-1) + + var terms []*Term + + var u Type + for i, x := range tlist { + term := parseTilde(check, x) + if len(tlist) == 1 && !term.tilde { + // Single type. Ok to return early because all relevant + // checks have been performed in parseTilde (no need to + // run through term validity check below). + return term.typ // typ already recorded through check.typ in parseTilde + } + if len(terms) >= maxTermCount { + if u != Typ[Invalid] { + check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount) + u = Typ[Invalid] + } + } else { + terms = append(terms, term) + u = &Union{terms} + } + + if i > 0 { + check.recordTypeAndValue(blist[i-1], typexpr, u, nil) + } + } + + if u == Typ[Invalid] { + return u + } + + // Check validity of terms. + // Do this check later because it requires types to be set up. + // Note: This is a quadratic algorithm, but unions tend to be short. + check.later(func() { + for i, t := range terms { + if t.typ == Typ[Invalid] { + continue + } + + u := under(t.typ) + f, _ := u.(*Interface) + if t.tilde { + if f != nil { + check.errorf(tlist[i], "invalid use of ~ (%s is an interface)", t.typ) + continue // don't report another error for t + } + + if !Identical(u, t.typ) { + check.errorf(tlist[i], "invalid use of ~ (underlying type of %s is %s)", t.typ, u) + continue + } + } + + // Stand-alone embedded interfaces are ok and are handled by the single-type case + // in the beginning. Embedded interfaces with tilde are excluded above. If we reach + // here, we must have at least two terms in the syntactic term list (but not necessarily + // in the term list of the union's type set). + if f != nil { + tset := f.typeSet() + switch { + case tset.NumMethods() != 0: + check.errorf(tlist[i], "cannot use %s in union (%s contains methods)", t, t) + case t.typ == universeComparable.Type(): + check.error(tlist[i], "cannot use comparable in union") + case tset.comparable: + check.errorf(tlist[i], "cannot use %s in union (%s embeds comparable)", t, t) + } + continue // terms with interface types are not subject to the no-overlap rule + } + + // Report overlapping (non-disjoint) terms such as + // a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a). + if j := overlappingTerm(terms[:i], t); j >= 0 { + check.softErrorf(tlist[i], "overlapping terms %s and %s", t, terms[j]) + } + } + }).describef(uexpr, "check term validity %s", uexpr) + + return u +} + +func parseTilde(check *Checker, tx syntax.Expr) *Term { + x := tx + var tilde bool + if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde { + x = op.X + tilde = true + } + typ := check.typ(x) + // Embedding stand-alone type parameters is not permitted (issue #47127). + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if we embed a lone type parameter, we will + // simply use its underlying type (like we do for other named, embedded interfaces), + // and since the underlying type is an interface the embedding is well defined. + if isTypeParam(typ) { + if tilde { + check.errorf(x, "type in term %s cannot be a type parameter", tx) + } else { + check.error(x, "term cannot be a type parameter") + } + typ = Typ[Invalid] + } + term := NewTerm(tilde, typ) + if tilde { + check.recordTypeAndValue(tx, typexpr, &Union{[]*Term{term}}, nil) + } + return term +} + +// overlappingTerm reports the index of the term x in terms which is +// overlapping (not disjoint) from y. The result is < 0 if there is no +// such term. The type of term y must not be an interface, and terms +// with an interface type are ignored in the terms list. +func overlappingTerm(terms []*Term, y *Term) int { + assert(!IsInterface(y.typ)) + for i, x := range terms { + if IsInterface(x.typ) { + continue + } + // disjoint requires non-nil, non-top arguments, + // and non-interface types as term types. + if debug { + if x == nil || x.typ == nil || y == nil || y.typ == nil { + panic("empty or top union term") + } + } + if !(*term)(x).disjoint((*term)(y)) { + return i + } + } + return -1 +} + +// flattenUnion walks a union type expression of the form A | B | C | ..., +// extracting both the binary exprs (blist) and leaf types (tlist). +func flattenUnion(list []syntax.Expr, x syntax.Expr) (blist, tlist []syntax.Expr) { + if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or { + blist, tlist = flattenUnion(list, o.X) + blist = append(blist, o) + x = o.Y + } + return blist, append(tlist, x) +} diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index 76d4e55e84b591..301526c8d60951 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -20,11 +20,12 @@ var Universe *Scope var Unsafe *Package var ( - universeIota *Const - universeByte *Basic // uint8 alias, but has name "byte" - universeRune *Basic // int32 alias, but has name "rune" - universeAny *Interface - universeError *Named + universeIota Object + universeByte Type // uint8 alias, but has name "byte" + universeRune Type // int32 alias, but has name "rune" + universeAny Object + universeError Type + universeComparable Object ) // Typ contains the predeclared *Basic types indexed by their @@ -77,20 +78,43 @@ func defPredeclaredTypes() { def(NewTypeName(nopos, nil, t.name, t)) } - // any - // (Predeclared and entered into universe scope so we do all the - // usual checks; but removed again from scope later since it's - // only visible as constraint in a type parameter list.) - def(NewTypeName(nopos, nil, "any", &emptyInterface)) + // type any = interface{} + // Note: don't use &emptyInterface for the type of any. Using a unique + // pointer allows us to detect any and format it as "any" rather than + // interface{}, which clarifies user-facing error messages significantly. + def(NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet})) - // Error has a nil package in its qualified name since it is in no package + // type error interface{ Error() string } { + obj := NewTypeName(nopos, nil, "error", nil) + obj.setColor(black) + typ := NewNamed(obj, nil, nil) + + // error.Error() string + recv := NewVar(nopos, nil, "", typ) res := NewVar(nopos, nil, "", Typ[String]) - sig := &Signature{results: NewTuple(res)} + sig := NewSignatureType(recv, nil, nil, nil, NewTuple(res), false) err := NewFunc(nopos, nil, "Error", sig) - typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()} - sig.recv = NewVar(nopos, nil, "", typ) - def(NewTypeName(nopos, nil, "error", typ)) + + // interface{ Error() string } + ityp := &Interface{methods: []*Func{err}, complete: true} + computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset + + typ.SetUnderlying(ityp) + def(obj) + } + + // type comparable interface{} // marked as comparable + { + obj := NewTypeName(nopos, nil, "comparable", nil) + obj.setColor(black) + typ := NewNamed(obj, nil, nil) + + // interface{} // marked as comparable + ityp := &Interface{complete: true, tset: &_TypeSet{nil, allTermlist, true}} + + typ.SetUnderlying(ityp) + def(obj) } } @@ -141,6 +165,9 @@ const ( _Offsetof _Sizeof _Slice + _SliceData + _String + _StringData // testing support _Assert @@ -169,11 +196,14 @@ var predeclaredFuncs = [...]struct { _Real: {"real", 1, false, expression}, _Recover: {"recover", 0, false, statement}, - _Add: {"Add", 2, false, expression}, - _Alignof: {"Alignof", 1, false, expression}, - _Offsetof: {"Offsetof", 1, false, expression}, - _Sizeof: {"Sizeof", 1, false, expression}, - _Slice: {"Slice", 2, false, expression}, + _Add: {"Add", 2, false, expression}, + _Alignof: {"Alignof", 1, false, expression}, + _Offsetof: {"Offsetof", 1, false, expression}, + _Sizeof: {"Sizeof", 1, false, expression}, + _Slice: {"Slice", 2, false, expression}, + _SliceData: {"SliceData", 1, false, expression}, + _String: {"String", 2, false, expression}, + _StringData: {"StringData", 1, false, expression}, _Assert: {"assert", 1, false, statement}, _Trace: {"trace", 0, true, statement}, @@ -200,33 +230,6 @@ func DefPredeclaredTestFuncs() { def(newBuiltin(_Trace)) } -func defPredeclaredComparable() { - // The "comparable" interface can be imagined as defined like - // - // type comparable interface { - // == () untyped bool - // != () untyped bool - // } - // - // == and != cannot be user-declared but we can declare - // a magic method == and check for its presence when needed. - - // Define interface { == () }. We don't care about the signature - // for == so leave it empty except for the receiver, which is - // set up later to match the usual interface method assumptions. - sig := new(Signature) - eql := NewFunc(nopos, nil, "==", sig) - iface := NewInterfaceType([]*Func{eql}, nil).Complete() - - // set up the defined type for the interface - obj := NewTypeName(nopos, nil, "comparable", nil) - named := NewNamed(obj, iface, nil) - obj.color_ = black - sig.recv = NewVar(nopos, nil, "", named) // complete == signature - - def(obj) -} - func init() { Universe = NewScope(nil, nopos, nopos, "universe") Unsafe = NewPackage("unsafe", "unsafe") @@ -236,22 +239,18 @@ func init() { defPredeclaredConsts() defPredeclaredNil() defPredeclaredFuncs() - defPredeclaredComparable() - - universeIota = Universe.Lookup("iota").(*Const) - universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic) - universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic) - universeAny = Universe.Lookup("any").(*TypeName).typ.(*Interface) - universeError = Universe.Lookup("error").(*TypeName).typ.(*Named) - // "any" is only visible as constraint in a type parameter list - delete(Universe.elems, "any") + universeIota = Universe.Lookup("iota") + universeByte = Universe.Lookup("byte").Type() + universeRune = Universe.Lookup("rune").Type() + universeAny = Universe.Lookup("any") + universeError = Universe.Lookup("error").Type() + universeComparable = Universe.Lookup("comparable") } // Objects with names containing blanks are internal and not entered into // a scope. Objects with exported names are inserted in the unsafe package // scope; other objects are inserted in the universe scope. -// func def(obj Object) { assert(obj.color() == black) name := obj.Name() @@ -259,7 +258,7 @@ func def(obj Object) { return // nothing to do } // fix Obj link for named types - if typ := asNamed(obj.Type()); typ != nil { + if typ, _ := obj.Type().(*Named); typ != nil { typ.obj = obj.(*TypeName) } // exported identifiers go into package unsafe @@ -277,6 +276,6 @@ func def(obj Object) { } } if scope.Insert(obj) != nil { - panic("internal error: double declaration") + panic("double declaration of predeclared identifier") } } diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go new file mode 100644 index 00000000000000..99fdebc978f5a9 --- /dev/null +++ b/src/cmd/compile/internal/types2/validtype.go @@ -0,0 +1,235 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types2 + +// validType verifies that the given type does not "expand" indefinitely +// producing a cycle in the type graph. +// (Cycles involving alias types, as in "type A = [10]A" are detected +// earlier, via the objDecl cycle detection mechanism.) +func (check *Checker) validType(typ *Named) { + check.validType0(typ, nil, nil) +} + +// validType0 checks if the given type is valid. If typ is a type parameter +// its value is looked up in the type argument list of the instantiated +// (enclosing) type, if it exists. Otherwise the type parameter must be from +// an enclosing function and can be ignored. +// The nest list describes the stack (the "nest in memory") of types which +// contain (or embed in the case of interfaces) other types. For instance, a +// struct named S which contains a field of named type F contains (the memory +// of) F in S, leading to the nest S->F. If a type appears in its own nest +// (say S->F->S) we have an invalid recursive type. The path list is the full +// path of named types in a cycle, it is only needed for error reporting. +func (check *Checker) validType0(typ Type, nest, path []*Named) bool { + switch t := typ.(type) { + case nil: + // We should never see a nil type but be conservative and panic + // only in debug mode. + if debug { + panic("validType0(nil)") + } + + case *Array: + return check.validType0(t.elem, nest, path) + + case *Struct: + for _, f := range t.fields { + if !check.validType0(f.typ, nest, path) { + return false + } + } + + case *Union: + for _, t := range t.terms { + if !check.validType0(t.typ, nest, path) { + return false + } + } + + case *Interface: + for _, etyp := range t.embeddeds { + if !check.validType0(etyp, nest, path) { + return false + } + } + + case *Named: + // Exit early if we already know t is valid. + // This is purely an optimization but it prevents excessive computation + // times in pathological cases such as testdata/fixedbugs/issue6977.go. + // (Note: The valids map could also be allocated locally, once for each + // validType call.) + if check.valids.lookup(t) != nil { + break + } + + // Don't report a 2nd error if we already know the type is invalid + // (e.g., if a cycle was detected earlier, via under). + // Note: ensure that t.orig is fully resolved by calling Underlying(). + if t.Underlying() == Typ[Invalid] { + return false + } + + // If the current type t is also found in nest, (the memory of) t is + // embedded in itself, indicating an invalid recursive type. + for _, e := range nest { + if Identical(e, t) { + // t cannot be in an imported package otherwise that package + // would have reported a type cycle and couldn't have been + // imported in the first place. + assert(t.obj.pkg == check.pkg) + t.underlying = Typ[Invalid] // t is in the current package (no race possibility) + // Find the starting point of the cycle and report it. + // Because each type in nest must also appear in path (see invariant below), + // type t must be in path since it was found in nest. But not every type in path + // is in nest. Specifically t may appear in path with an earlier index than the + // index of t in nest. Search again. + for start, p := range path { + if Identical(p, t) { + check.cycleError(makeObjList(path[start:])) + return false + } + } + panic("cycle start not found") + } + } + + // No cycle was found. Check the RHS of t. + // Every type added to nest is also added to path; thus every type that is in nest + // must also be in path (invariant). But not every type in path is in nest, since + // nest may be pruned (see below, *TypeParam case). + if !check.validType0(t.Origin().fromRHS, append(nest, t), append(path, t)) { + return false + } + + check.valids.add(t) // t is valid + + case *TypeParam: + // A type parameter stands for the type (argument) it was instantiated with. + // Check the corresponding type argument for validity if we are in an + // instantiated type. + if len(nest) > 0 { + inst := nest[len(nest)-1] // the type instance + // Find the corresponding type argument for the type parameter + // and proceed with checking that type argument. + for i, tparam := range inst.TypeParams().list() { + // The type parameter and type argument lists should + // match in length but be careful in case of errors. + if t == tparam && i < inst.TypeArgs().Len() { + targ := inst.TypeArgs().At(i) + // The type argument must be valid in the enclosing + // type (where inst was instantiated), hence we must + // check targ's validity in the type nest excluding + // the current (instantiated) type (see the example + // at the end of this file). + // For error reporting we keep the full path. + return check.validType0(targ, nest[:len(nest)-1], path) + } + } + } + } + + return true +} + +// makeObjList returns the list of type name objects for the given +// list of named types. +func makeObjList(tlist []*Named) []Object { + olist := make([]Object, len(tlist)) + for i, t := range tlist { + olist[i] = t.obj + } + return olist +} + +// Here is an example illustrating why we need to exclude the +// instantiated type from nest when evaluating the validity of +// a type parameter. Given the declarations +// +// var _ A[A[string]] +// +// type A[P any] struct { _ B[P] } +// type B[P any] struct { _ P } +// +// we want to determine if the type A[A[string]] is valid. +// We start evaluating A[A[string]] outside any type nest: +// +// A[A[string]] +// nest = +// path = +// +// The RHS of A is now evaluated in the A[A[string]] nest: +// +// struct{_ B[P₁]} +// nest = A[A[string]] +// path = A[A[string]] +// +// The struct has a single field of type B[P₁] with which +// we continue: +// +// B[P₁] +// nest = A[A[string]] +// path = A[A[string]] +// +// struct{_ P₂} +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// Eventutally we reach the type parameter P of type B (P₂): +// +// P₂ +// nest = A[A[string]]->B[P] +// path = A[A[string]]->B[P] +// +// The type argument for P of B is the type parameter P of A (P₁). +// It must be evaluated in the type nest that existed when B was +// instantiated: +// +// P₁ +// nest = A[A[string]] <== type nest at B's instantiation time +// path = A[A[string]]->B[P] +// +// If we'd use the current nest it would correspond to the path +// which will be wrong as we will see shortly. P's type argument +// is A[string], which again must be evaluated in the type nest +// that existed when A was instantiated with A[string]. That type +// nest is empty: +// +// A[string] +// nest = <== type nest at A's instantiation time +// path = A[A[string]]->B[P] +// +// Evaluation then proceeds as before for A[string]: +// +// struct{_ B[P₁]} +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] +// +// Now we reach B[P] again. If we had not adjusted nest, it would +// correspond to path, and we would find B[P] in nest, indicating +// a cycle, which would clearly be wrong since there's no cycle in +// A[string]: +// +// B[P₁] +// nest = A[string] +// path = A[A[string]]->B[P]->A[string] <== path contains B[P]! +// +// But because we use the correct type nest, evaluation proceeds without +// errors and we get the evaluation sequence: +// +// struct{_ P₂} +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₂ +// nest = A[string]->B[P] +// path = A[A[string]]->B[P]->A[string]->B[P] +// P₁ +// nest = A[string] +// path = A[A[string]]->B[P]->A[string]->B[P] +// string +// nest = +// path = A[A[string]]->B[P]->A[string]->B[P] +// +// At this point we're done and A[A[string]] and is valid. diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go index d9d18b6f7a7f48..b649f09c3aa95a 100644 --- a/src/cmd/compile/internal/types2/version.go +++ b/src/cmd/compile/internal/types2/version.go @@ -21,7 +21,7 @@ func (check *Checker) langCompat(lit *syntax.BasicLit) { } // len(s) > 2 if strings.Contains(s, "_") { - check.error(lit, "underscores in numeric literals requires go1.13 or later") + check.versionErrorf(lit, "go1.13", "underscores in numeric literals") return } if s[0] != '0' { @@ -29,15 +29,15 @@ func (check *Checker) langCompat(lit *syntax.BasicLit) { } radix := s[1] if radix == 'b' || radix == 'B' { - check.error(lit, "binary literals requires go1.13 or later") + check.versionErrorf(lit, "go1.13", "binary literals") return } if radix == 'o' || radix == 'O' { - check.error(lit, "0o/0O-style octal literals requires go1.13 or later") + check.versionErrorf(lit, "go1.13", "0o/0O-style octal literals") return } if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') { - check.error(lit, "hexadecimal floating-point literals requires go1.13 or later") + check.versionErrorf(lit, "go1.13", "hexadecimal floating-point literals") } } diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go index 6d697a53ae3aaa..1450ec6ba2b994 100644 --- a/src/cmd/compile/internal/walk/assign.go +++ b/src/cmd/compile/internal/walk/assign.go @@ -99,10 +99,11 @@ func walkAssign(init *ir.Nodes, n ir.Node) ir.Node { } as.Y = r if r.Op() == ir.OAPPEND { + r := r.(*ir.CallExpr) // Left in place for back end. // Do not add a new write barrier. // Set up address of type for back end. - r.(*ir.CallExpr).X = reflectdata.TypePtr(r.Type().Elem()) + r.X = reflectdata.AppendElemRType(base.Pos, r) return as } // Otherwise, lowered for race detector. @@ -157,7 +158,7 @@ func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { t := r.X.Type() fast := mapfast(t) - key := mapKeyArg(fast, r, r.Index) + key := mapKeyArg(fast, r, r.Index, false) // from: // a,b = m[i] @@ -167,13 +168,13 @@ func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { a := n.Lhs[0] var call *ir.CallExpr - if w := t.Elem().Width; w <= zeroValSize { + if w := t.Elem().Size(); w <= zeroValSize { fn := mapfn(mapaccess2[fast], t, false) - call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key) + call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key) } else { fn := mapfn("mapaccess2_fat", t, true) z := reflectdata.ZeroAddr(w) - call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key, z) + call = mkcall1(fn, fn.Type().Results(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key, z) } // mapaccess2* returns a typed bool, but due to spec changes, @@ -242,6 +243,7 @@ func walkReturn(n *ir.ReturnStmt) ir.Node { // check assign type list to // an expression list. called in +// // expr-list = func() func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node { if len(nl) != nr.NumFields() { @@ -273,6 +275,7 @@ func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node { // check assign expression list to // an expression list. called in +// // expr-list = expr-list func ascompatee(op ir.Op, nl, nr []ir.Node) []ir.Node { // cannot happen: should have been rejected during type checking @@ -429,6 +432,7 @@ func readsMemory(n ir.Node) bool { ir.OBITNOT, ir.OCONV, ir.OCONVIFACE, + ir.OCONVIDATA, ir.OCONVNOP, ir.ODIV, ir.ODOT, @@ -454,17 +458,19 @@ func readsMemory(n ir.Node) bool { } // expand append(l1, l2...) to -// init { -// s := l1 -// n := len(s) + len(l2) -// // Compare as uint so growslice can panic on overflow. -// if uint(n) > uint(cap(s)) { -// s = growslice(s, n) -// } -// s = s[:n] -// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) -// } -// s +// +// init { +// s := l1 +// newLen := s.len + l2.len +// // Compare as uint so growslice can panic on overflow. +// if uint(newLen) <= uint(s.cap) { +// s = s[:newLen] +// } else { +// s = growslice(s.ptr, s.len, s.cap, l2.len, T) +// } +// memmove(&s[s.len-l2.len], &l2[0], l2.len*sizeof(T)) +// } +// s // // l2 is allowed to be a string. func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { @@ -483,34 +489,54 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { elemtype := s.Type().Elem() - // n := len(s) + len(l2) - nn := typecheck.Temp(types.Types[types.TINT]) - nodes.Append(ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), ir.NewUnaryExpr(base.Pos, ir.OLEN, l2)))) + // Decompose slice. + oldPtr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) + oldLen := ir.NewUnaryExpr(base.Pos, ir.OLEN, s) + oldCap := ir.NewUnaryExpr(base.Pos, ir.OCAP, s) - // if uint(n) > uint(cap(s)) + // Number of elements we are adding + num := ir.NewUnaryExpr(base.Pos, ir.OLEN, l2) + + // newLen := oldLen + num + newLen := typecheck.Temp(types.Types[types.TINT]) + nodes.Append(ir.NewAssignStmt(base.Pos, newLen, ir.NewBinaryExpr(base.Pos, ir.OADD, oldLen, num))) + + // if uint(newLen) <= uint(oldCap) nif := ir.NewIfStmt(base.Pos, nil, nil, nil) - nuint := typecheck.Conv(nn, types.Types[types.TUINT]) - scapuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT]) - nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, scapuint) + nuint := typecheck.Conv(newLen, types.Types[types.TUINT]) + scapuint := typecheck.Conv(oldCap, types.Types[types.TUINT]) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, nuint, scapuint) + nif.Likely = true - // instantiate growslice(typ *type, []any, int) []any + // then { s = s[:newLen] } + slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, newLen, nil) + slice.SetBounded(true) + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, slice)} + + // func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) []T fn := typecheck.LookupRuntime("growslice") fn = typecheck.SubstArgTypes(fn, elemtype, elemtype) - // s = growslice(T, s, n) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))} + // else { s = growslice(oldPtr, newLen, oldCap, num, T) } + call := mkcall1(fn, s.Type(), nif.PtrInit(), oldPtr, newLen, oldCap, num, reflectdata.TypePtr(elemtype)) + nif.Else = []ir.Node{ir.NewAssignStmt(base.Pos, s, call)} + nodes.Append(nif) - // s = s[:n] - nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil) - nt.SetBounded(true) - nodes.Append(ir.NewAssignStmt(base.Pos, s, nt)) + // Index to start copying into s. + // idx = newLen - len(l2) + // We use this expression instead of oldLen because it avoids + // a spill/restore of oldLen. + // Note: this doesn't work optimally currently because + // the compiler optimizer undoes this arithmetic. + idx := ir.NewBinaryExpr(base.Pos, ir.OSUB, newLen, ir.NewUnaryExpr(base.Pos, ir.OLEN, l2)) var ncopy ir.Node if elemtype.HasPointers() { - // copy(s[len(l1):], l2) - slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil) + // copy(s[idx:], l2) + slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, idx, nil, nil) slice.SetType(s.Type()) + slice.SetBounded(true) ir.CurFunc.SetWBPos(n.Pos()) @@ -519,30 +545,31 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { fn = typecheck.SubstArgTypes(fn, l1.Type().Elem(), l2.Type().Elem()) ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes)) ptr2, len2 := backingArrayPtrLen(l2) - ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.TypePtr(elemtype), ptr1, len1, ptr2, len2) + ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, reflectdata.AppendElemRType(base.Pos, n), ptr1, len1, ptr2, len2) } else if base.Flag.Cfg.Instrumenting && !base.Flag.CompilingRuntime { // rely on runtime to instrument: - // copy(s[len(l1):], l2) + // copy(s[idx:], l2) // l2 can be a slice or string. - slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1), nil, nil) + slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, idx, nil, nil) slice.SetType(s.Type()) + slice.SetBounded(true) ptr1, len1 := backingArrayPtrLen(cheapExpr(slice, &nodes)) ptr2, len2 := backingArrayPtrLen(l2) fn := typecheck.LookupRuntime("slicecopy") fn = typecheck.SubstArgTypes(fn, ptr1.Type().Elem(), ptr2.Type().Elem()) - ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Width)) + ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Size())) } else { - // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) - ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1)) + // memmove(&s[idx], &l2[0], len(l2)*sizeof(T)) + ix := ir.NewIndexExpr(base.Pos, s, idx) ix.SetBounded(true) addr := typecheck.NodAddr(ix) sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l2) nwid := cheapExpr(typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, l2), types.Types[types.TUINTPTR]), &nodes) - nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Width)) + nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Size())) // instantiate func memmove(to *any, frm *any, length uintptr) fn := typecheck.LookupRuntime("memmove") @@ -596,32 +623,34 @@ func isAppendOfMake(n ir.Node) bool { } // extendSlice rewrites append(l1, make([]T, l2)...) to -// init { -// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true) -// } else { -// panicmakeslicelen() -// } -// s := l1 -// n := len(s) + l2 -// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. -// // cap is a positive int and n can become negative when len(s) + l2 -// // overflows int. Interpreting n when negative as uint makes it larger -// // than cap(s). growslice will check the int n arg and panic if n is -// // negative. This prevents the overflow from being undetected. -// if uint(n) > uint(cap(s)) { -// s = growslice(T, s, n) -// } -// s = s[:n] -// lptr := &l1[0] -// sptr := &s[0] -// if lptr == sptr || !T.HasPointers() { -// // growslice did not clear the whole underlying array (or did not get called) -// hp := &s[len(l1)] -// hn := l2 * sizeof(T) -// memclr(hp, hn) -// } -// } -// s +// +// init { +// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true) +// } else { +// panicmakeslicelen() +// } +// s := l1 +// n := len(s) + l2 +// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2. +// // cap is a positive int and n can become negative when len(s) + l2 +// // overflows int. Interpreting n when negative as uint makes it larger +// // than cap(s). growslice will check the int n arg and panic if n is +// // negative. This prevents the overflow from being undetected. +// if uint(n) <= uint(cap(s)) { +// s = s[:n] +// } else { +// s = growslice(T, s.ptr, n, s.cap, l2, T) +// } +// // clear the new portion of the underlying array. +// hp := &s[len(s)-l2] +// hn := l2 * sizeof(T) +// memclr(hp, hn) +// } +// s +// +// if T has pointers, the final memclr can go inside the "then" branch, as +// growslice will have done the clearing for us. + func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { // isAppendOfMake made sure all possible positive values of l2 fit into an uint. // The case of l2 overflow when converting from e.g. uint to int is handled by an explicit @@ -651,45 +680,45 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { elemtype := s.Type().Elem() - // n := len(s) + l2 + // n := s.len + l2 nn := typecheck.Temp(types.Types[types.TINT]) nodes = append(nodes, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2))) - // if uint(n) > uint(cap(s)) + // if uint(n) <= uint(s.cap) nuint := typecheck.Conv(nn, types.Types[types.TUINT]) capuint := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT]) - nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OGT, nuint, capuint), nil, nil) - - // instantiate growslice(typ *type, old []any, newcap int) []any - fn := typecheck.LookupRuntime("growslice") - fn = typecheck.SubstArgTypes(fn, elemtype, elemtype) - - // s = growslice(T, s, n) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), reflectdata.TypePtr(elemtype), s, nn))} - nodes = append(nodes, nif) + nif := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OLE, nuint, capuint), nil, nil) + nif.Likely = true - // s = s[:n] + // then { s = s[:n] } nt := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, nn, nil) nt.SetBounded(true) - nodes = append(nodes, ir.NewAssignStmt(base.Pos, s, nt)) + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, s, nt)} - // lptr := &l1[0] - l1ptr := typecheck.Temp(l1.Type().Elem().PtrTo()) - tmp := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l1) - nodes = append(nodes, ir.NewAssignStmt(base.Pos, l1ptr, tmp)) + // instantiate growslice(oldPtr *any, newLen, oldCap, num int, typ *type) []any + fn := typecheck.LookupRuntime("growslice") + fn = typecheck.SubstArgTypes(fn, elemtype, elemtype) + + // else { s = growslice(s.ptr, n, s.cap, l2, T) } + nif.Else = []ir.Node{ + ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), + ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), + nn, + ir.NewUnaryExpr(base.Pos, ir.OCAP, s), + l2, + reflectdata.TypePtr(elemtype))), + } - // sptr := &s[0] - sptr := typecheck.Temp(elemtype.PtrTo()) - tmp = ir.NewUnaryExpr(base.Pos, ir.OSPTR, s) - nodes = append(nodes, ir.NewAssignStmt(base.Pos, sptr, tmp)) + nodes = append(nodes, nif) - // hp := &s[len(l1)] - ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1)) + // hp := &s[s.len - l2] + // TODO: &s[s.len] - hn? + ix := ir.NewIndexExpr(base.Pos, s, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), l2)) ix.SetBounded(true) hp := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR]) // hn := l2 * sizeof(elem(s)) - hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Width)), types.Types[types.TUINTPTR]) + hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Size())), types.Types[types.TUINTPTR]) clrname := "memclrNoHeapPointers" hasPointers := elemtype.HasPointers() @@ -701,12 +730,10 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node { var clr ir.Nodes clrfn := mkcall(clrname, nil, &clr, hp, hn) clr.Append(clrfn) - if hasPointers { - // if l1ptr == sptr - nifclr := ir.NewIfStmt(base.Pos, ir.NewBinaryExpr(base.Pos, ir.OEQ, l1ptr, sptr), nil, nil) - nifclr.Body = clr - nodes = append(nodes, nifclr) + // growslice will have cleared the new entries, so only + // if growslice isn't called do we need to do the zeroing ourselves. + nif.Body = append(nif.Body, clr...) } else { nodes = append(nodes, clr...) } diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index 14efc05e327537..7d554947374a27 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -22,23 +22,24 @@ import ( // x, y, z (including runtime panics) are evaluated in // initialization statements before the append. // For normal code generation, stop there and leave the -// rest to cgen_append. +// rest to ssagen. // // For race detector, expand append(src, a [, b]* ) to // -// init { -// s := src -// const argc = len(args) - 1 -// if cap(s) - len(s) < argc { -// s = growslice(s, len(s)+argc) -// } -// n := len(s) -// s = s[:n+argc] -// s[n] = a -// s[n+1] = b -// ... -// } -// s +// init { +// s := src +// const argc = len(args) - 1 +// newLen := s.len + argc +// if uint(newLen) <= uint(s.cap) { +// s = s[:newLen] +// } else { +// s = growslice(s.ptr, newLen, s.cap, argc, elemType) +// } +// s[s.len - argc] = a +// s[s.len - argc + 1] = b +// ... +// } +// s func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node { if !ir.SameSafeExpr(dst, n.Args[0]) { n.Args[0] = safeExpr(n.Args[0], init) @@ -70,49 +71,63 @@ func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node { } // General case, with no function calls left as arguments. - // Leave for gen, except that instrumentation requires old form. + // Leave for ssagen, except that instrumentation requires the old form. if !base.Flag.Cfg.Instrumenting || base.Flag.CompilingRuntime { return n } var l []ir.Node - ns := typecheck.Temp(nsrc.Type()) - l = append(l, ir.NewAssignStmt(base.Pos, ns, nsrc)) // s = src + // s = slice to append to + s := typecheck.Temp(nsrc.Type()) + l = append(l, ir.NewAssignStmt(base.Pos, s, nsrc)) - na := ir.NewInt(int64(argc)) // const argc - nif := ir.NewIfStmt(base.Pos, nil, nil, nil) // if cap(s) - len(s) < argc - nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, ir.NewBinaryExpr(base.Pos, ir.OSUB, ir.NewUnaryExpr(base.Pos, ir.OCAP, ns), ir.NewUnaryExpr(base.Pos, ir.OLEN, ns)), na) + // num = number of things to append + num := ir.NewInt(int64(argc)) - fn := typecheck.LookupRuntime("growslice") // growslice(, old []T, mincap int) (ret []T) - fn = typecheck.SubstArgTypes(fn, ns.Type().Elem(), ns.Type().Elem()) + // newLen := s.len + num + newLen := typecheck.Temp(types.Types[types.TINT]) + l = append(l, ir.NewAssignStmt(base.Pos, newLen, ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, s), num))) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, ns, mkcall1(fn, ns.Type(), nif.PtrInit(), reflectdata.TypePtr(ns.Type().Elem()), ns, - ir.NewBinaryExpr(base.Pos, ir.OADD, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns), na)))} + // if uint(newLen) <= uint(s.cap) + nif := ir.NewIfStmt(base.Pos, nil, nil, nil) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, typecheck.Conv(newLen, types.Types[types.TUINT]), typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OCAP, s), types.Types[types.TUINT])) + nif.Likely = true - l = append(l, nif) + // then { s = s[:n] } + slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, s, nil, newLen, nil) + slice.SetBounded(true) + nif.Body = []ir.Node{ + ir.NewAssignStmt(base.Pos, s, slice), + } - nn := typecheck.Temp(types.Types[types.TINT]) - l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewUnaryExpr(base.Pos, ir.OLEN, ns))) // n = len(s) + fn := typecheck.LookupRuntime("growslice") // growslice(ptr *T, newLen, oldCap, num int, ) (ret []T) + fn = typecheck.SubstArgTypes(fn, s.Type().Elem(), s.Type().Elem()) - slice := ir.NewSliceExpr(base.Pos, ir.OSLICE, ns, nil, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, na), nil) // ...s[:n+argc] - slice.SetBounded(true) - l = append(l, ir.NewAssignStmt(base.Pos, ns, slice)) // s = s[:n+argc] + // else { s = growslice(s.ptr, n, s.cap, a, T) } + nif.Else = []ir.Node{ + ir.NewAssignStmt(base.Pos, s, mkcall1(fn, s.Type(), nif.PtrInit(), + ir.NewUnaryExpr(base.Pos, ir.OSPTR, s), + newLen, + ir.NewUnaryExpr(base.Pos, ir.OCAP, s), + num, + reflectdata.TypePtr(s.Type().Elem()))), + } + + l = append(l, nif) ls = n.Args[1:] for i, n := range ls { - ix := ir.NewIndexExpr(base.Pos, ns, nn) // s[n] ... + // s[s.len-argc+i] = arg + ix := ir.NewIndexExpr(base.Pos, s, ir.NewBinaryExpr(base.Pos, ir.OSUB, newLen, ir.NewInt(int64(argc-i)))) ix.SetBounded(true) - l = append(l, ir.NewAssignStmt(base.Pos, ix, n)) // s[n] = arg - if i+1 < len(ls) { - l = append(l, ir.NewAssignStmt(base.Pos, nn, ir.NewBinaryExpr(base.Pos, ir.OADD, nn, ir.NewInt(1)))) // n = n + 1 - } + l = append(l, ir.NewAssignStmt(base.Pos, ix, n)) } typecheck.Stmts(l) walkStmtList(l) init.Append(l...) - return ns + return s } // walkClose walks an OCLOSE node. @@ -125,15 +140,14 @@ func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { // Lower copy(a, b) to a memmove call or a runtime call. // -// init { -// n := len(a) -// if n > len(b) { n = len(b) } -// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } -// } -// n; +// init { +// n := len(a) +// if n > len(b) { n = len(b) } +// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } +// } +// n; // // Also works if b is a string. -// func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { if n.X.Type().Elem().HasPointers() { ir.CurFunc.SetWBPos(n.Pos()) @@ -142,7 +156,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { ptrL, lenL := backingArrayPtrLen(n.X) n.Y = cheapExpr(n.Y, init) ptrR, lenR := backingArrayPtrLen(n.Y) - return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.X.Type().Elem()), ptrL, lenL, ptrR, lenR) + return mkcall1(fn, n.Type(), init, reflectdata.CopyElemRType(base.Pos, n), ptrL, lenL, ptrR, lenR) } if runtimecall { @@ -158,7 +172,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { fn := typecheck.LookupRuntime("slicecopy") fn = typecheck.SubstArgTypes(fn, ptrL.Type().Elem(), ptrR.Type().Elem()) - return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Width)) + return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Size())) } n.X = walkExpr(n.X, init) @@ -194,7 +208,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { nwid := ir.Node(typecheck.Temp(types.Types[types.TUINTPTR])) setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR])) ne.Body.Append(setwid) - nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Width)) + nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Size())) call := mkcall1(fn, nil, init, nto, nfrm, nwid) ne.Body.Append(call) @@ -214,8 +228,8 @@ func walkDelete(init *ir.Nodes, n *ir.CallExpr) ir.Node { t := map_.Type() fast := mapfast(t) - key = mapKeyArg(fast, n, key) - return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.TypePtr(t), map_, key) + key = mapKeyArg(fast, n, key, false) + return mkcall1(mapfndel(mapdelete[fast], t), nil, init, reflectdata.DeleteMapRType(base.Pos, n), map_, key) } // walkLenCap walks an OLEN or OCAP node. @@ -259,7 +273,7 @@ func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node { argtype = types.Types[types.TINT] } - return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(size, argtype)) + return mkcall1(chanfn(fnname, 1, n.Type()), n.Type(), init, reflectdata.MakeChanRType(base.Pos, n), typecheck.Conv(size, argtype)) } // walkMakeMap walks an OMAKEMAP node. @@ -357,7 +371,7 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { fn := typecheck.LookupRuntime(fnname) fn = typecheck.SubstArgTypes(fn, hmapType, t.Key(), t.Elem()) - return mkcall1(fn, n.Type(), init, reflectdata.TypePtr(n.Type()), typecheck.Conv(hint, argtype), h) + return mkcall1(fn, n.Type(), init, reflectdata.MakeMapRType(base.Pos, n), typecheck.Conv(hint, argtype), h) } // walkMakeSlice walks an OMAKESLICE node. @@ -422,7 +436,7 @@ func walkMakeSlice(n *ir.MakeExpr, init *ir.Nodes) ir.Node { argtype = types.Types[types.TINT] } fn := typecheck.LookupRuntime(fnname) - ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype)) + ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), typecheck.Conv(len, argtype), typecheck.Conv(cap, argtype)) ptr.MarkNonNil() len = typecheck.Conv(len, types.Types[types.TINT]) cap = typecheck.Conv(cap, types.Types[types.TINT]) @@ -452,7 +466,7 @@ func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node { // We do not check for overflow of len(to)*elem.Width here // since len(from) is an existing checked slice capacity // with same elem.Width for the from slice. - size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Width), types.Types[types.TUINTPTR])) + size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Size()), types.Types[types.TUINTPTR])) // instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer fn := typecheck.LookupRuntime("mallocgc") @@ -476,7 +490,7 @@ func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node { // Replace make+copy with runtime.makeslicecopy. // instantiate makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer fn := typecheck.LookupRuntime("makeslicecopy") - ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.TypePtr(t.Elem()), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR])) + ptr := mkcall1(fn, types.Types[types.TUNSAFEPTR], init, reflectdata.MakeSliceElemRType(base.Pos, n), length, copylen, typecheck.Conv(copyptr, types.Types[types.TUNSAFEPTR])) ptr.MarkNonNil() sh := ir.NewSliceHeaderExpr(base.Pos, t, ptr, length, length) return walkExpr(typecheck.Expr(sh), init) @@ -622,10 +636,7 @@ func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node { r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil) if params := on.Type().Params().FieldSlice(); len(params) > 0 { t := params[0].Type - if !types.Identical(t, n.Type()) { - n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n) - n.SetType(t) - } + n = typecheck.Conv(n, t) r.Args.Append(n) } calls = append(calls, r) @@ -641,49 +652,166 @@ func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node { return walkStmt(typecheck.Stmt(r)) } -// walkRecover walks an ORECOVER node. -func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node { - // Call gorecover with the FP of this frame. - // FP is equal to caller's SP plus FixedFrameSize(). - var fp ir.Node = mkcall("getcallersp", types.Types[types.TUINTPTR], init) - if off := base.Ctxt.FixedFrameSize(); off != 0 { - fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off)) - } - fp = ir.NewConvExpr(fp.Pos(), ir.OCONVNOP, types.NewPtr(types.Types[types.TINT32]), fp) - return mkcall("gorecover", nn.Type(), init, fp) +// walkRecover walks an ORECOVERFP node. +func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node { + return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init)) +} + +// walkUnsafeData walks an OUNSAFESLICEDATA or OUNSAFESTRINGDATA expression. +func walkUnsafeData(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { + slice := walkExpr(n.X, init) + res := typecheck.Expr(ir.NewUnaryExpr(n.Pos(), ir.OSPTR, slice)) + res.SetType(n.Type()) + return walkExpr(res, init) } func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { ptr := safeExpr(n.X, init) len := safeExpr(n.Y, init) + sliceType := n.Type() - fnname := "unsafeslice64" lenType := types.Types[types.TINT64] + unsafePtr := typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]) + // If checkptr enabled, call runtime.unsafeslicecheckptr to check ptr and len. + // for simplicity, unsafeslicecheckptr always uses int64. // Type checking guarantees that TIDEAL len/cap are positive and fit in an int. // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT // will be handled by the negative range checks in unsafeslice during runtime. if ir.ShouldCheckPtr(ir.CurFunc, 1) { - fnname = "unsafeslicecheckptr" - // for simplicity, unsafeslicecheckptr always uses int64 - } else if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() { - fnname = "unsafeslice" - lenType = types.Types[types.TINT] - } + fnname := "unsafeslicecheckptr" + fn := typecheck.LookupRuntime(fnname) + init.Append(mkcall1(fn, nil, init, reflectdata.UnsafeSliceElemRType(base.Pos, n), unsafePtr, typecheck.Conv(len, lenType))) + } else { + // Otherwise, open code unsafe.Slice to prevent runtime call overhead. + // Keep this code in sync with runtime.unsafeslice{,64} + if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() { + lenType = types.Types[types.TINT] + } else { + // len64 := int64(len) + // if int64(int(len64)) != len64 { + // panicunsafeslicelen() + // } + len64 := typecheck.Conv(len, lenType) + nif := ir.NewIfStmt(base.Pos, nil, nil, nil) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, typecheck.Conv(typecheck.Conv(len64, types.Types[types.TINT]), lenType), len64) + nif.Body.Append(mkcall("panicunsafeslicelen", nil, &nif.Body)) + appendWalkStmt(init, nif) + } - t := n.Type() + // if len < 0 { panicunsafeslicelen() } + nif := ir.NewIfStmt(base.Pos, nil, nil, nil) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, typecheck.Conv(len, lenType), ir.NewInt(0)) + nif.Body.Append(mkcall("panicunsafeslicelen", nil, &nif.Body)) + appendWalkStmt(init, nif) - // Call runtime.unsafeslice{,64,checkptr} to check ptr and len. - fn := typecheck.LookupRuntime(fnname) - init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, lenType))) + if sliceType.Elem().Size() == 0 { + // if ptr == nil && len > 0 { + // panicunsafesliceptrnil() + // } + nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil) + isNil := ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil()) + gtZero := ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(len, lenType), ir.NewInt(0)) + nifPtr.Cond = + ir.NewLogicalExpr(base.Pos, ir.OANDAND, isNil, gtZero) + nifPtr.Body.Append(mkcall("panicunsafeslicenilptr", nil, &nifPtr.Body)) + appendWalkStmt(init, nifPtr) + + h := ir.NewSliceHeaderExpr(n.Pos(), sliceType, + typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), + typecheck.Conv(len, types.Types[types.TINT]), + typecheck.Conv(len, types.Types[types.TINT])) + return walkExpr(typecheck.Expr(h), init) + } + + // mem, overflow := runtime.mulUintptr(et.size, len) + mem := typecheck.Temp(types.Types[types.TUINTPTR]) + overflow := typecheck.Temp(types.Types[types.TBOOL]) + fn := typecheck.LookupRuntime("mulUintptr") + call := mkcall1(fn, fn.Type().Results(), init, ir.NewInt(sliceType.Elem().Size()), typecheck.Conv(typecheck.Conv(len, lenType), types.Types[types.TUINTPTR])) + appendWalkStmt(init, ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{mem, overflow}, []ir.Node{call})) + + // if overflow || mem > -uintptr(ptr) { + // if ptr == nil { + // panicunsafesliceptrnil() + // } + // panicunsafeslicelen() + // } + nif = ir.NewIfStmt(base.Pos, nil, nil, nil) + memCond := ir.NewBinaryExpr(base.Pos, ir.OGT, mem, ir.NewUnaryExpr(base.Pos, ir.ONEG, typecheck.Conv(unsafePtr, types.Types[types.TUINTPTR]))) + nif.Cond = ir.NewLogicalExpr(base.Pos, ir.OOROR, overflow, memCond) + nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil) + nifPtr.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil()) + nifPtr.Body.Append(mkcall("panicunsafeslicenilptr", nil, &nifPtr.Body)) + nif.Body.Append(nifPtr, mkcall("panicunsafeslicelen", nil, &nif.Body)) + appendWalkStmt(init, nif) + } - h := ir.NewSliceHeaderExpr(n.Pos(), t, + h := ir.NewSliceHeaderExpr(n.Pos(), sliceType, typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, types.Types[types.TINT]), typecheck.Conv(len, types.Types[types.TINT])) return walkExpr(typecheck.Expr(h), init) } +func walkUnsafeString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { + ptr := safeExpr(n.X, init) + len := safeExpr(n.Y, init) + + lenType := types.Types[types.TINT64] + unsafePtr := typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]) + + // If checkptr enabled, call runtime.unsafestringcheckptr to check ptr and len. + // for simplicity, unsafestringcheckptr always uses int64. + // Type checking guarantees that TIDEAL len are positive and fit in an int. + if ir.ShouldCheckPtr(ir.CurFunc, 1) { + fnname := "unsafestringcheckptr" + fn := typecheck.LookupRuntime(fnname) + init.Append(mkcall1(fn, nil, init, unsafePtr, typecheck.Conv(len, lenType))) + } else { + // Otherwise, open code unsafe.String to prevent runtime call overhead. + // Keep this code in sync with runtime.unsafestring{,64} + if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() { + lenType = types.Types[types.TINT] + } else { + // len64 := int64(len) + // if int64(int(len64)) != len64 { + // panicunsafestringlen() + // } + len64 := typecheck.Conv(len, lenType) + nif := ir.NewIfStmt(base.Pos, nil, nil, nil) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, typecheck.Conv(typecheck.Conv(len64, types.Types[types.TINT]), lenType), len64) + nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body)) + appendWalkStmt(init, nif) + } + + // if len < 0 { panicunsafestringlen() } + nif := ir.NewIfStmt(base.Pos, nil, nil, nil) + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, typecheck.Conv(len, lenType), ir.NewInt(0)) + nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body)) + appendWalkStmt(init, nif) + + // if uintpr(len) > -uintptr(ptr) { + // if ptr == nil { + // panicunsafestringnilptr() + // } + // panicunsafeslicelen() + // } + nifLen := ir.NewIfStmt(base.Pos, nil, nil, nil) + nifLen.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(len, types.Types[types.TUINTPTR]), ir.NewUnaryExpr(base.Pos, ir.ONEG, typecheck.Conv(unsafePtr, types.Types[types.TUINTPTR]))) + nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil) + nifPtr.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil()) + nifPtr.Body.Append(mkcall("panicunsafestringnilptr", nil, &nifPtr.Body)) + nifLen.Body.Append(nifPtr, mkcall("panicunsafestringlen", nil, &nifLen.Body)) + appendWalkStmt(init, nifLen) + } + h := ir.NewStringHeaderExpr(n.Pos(), + typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), + typecheck.Conv(len, types.Types[types.TINT]), + ) + return walkExpr(typecheck.Expr(h), init) +} + func badtype(op ir.Op, tl, tr *types.Type) { var s string if tl != nil { diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go index 2194e1c5b0c7a3..590c9a3ad46cf8 100644 --- a/src/cmd/compile/internal/walk/closure.go +++ b/src/cmd/compile/internal/walk/closure.go @@ -37,14 +37,6 @@ func directClosureCall(n *ir.CallExpr) { return // leave for walkClosure to handle } - // If wrapGoDefer() in the order phase has flagged this call, - // avoid eliminating the closure even if there is a direct call to - // (the closure is needed to simplify the register ABI). See - // wrapGoDefer for more details. - if n.PreserveClosure { - return - } - // We are going to insert captured variables before input args. var params []*types.Field var decls []*ir.Name @@ -115,13 +107,25 @@ func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node { // The closure is not trivial or directly called, so it's going to stay a closure. ir.ClosureDebugRuntimeCheck(clo) clofn.SetNeedctxt(true) - ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn) + + // The closure expression may be walked more than once if it appeared in composite + // literal initialization (e.g, see issue #49029). + // + // Don't add the closure function to compilation queue more than once, since when + // compiling a function twice would lead to an ICE. + if !clofn.Walked() { + clofn.SetWalked(true) + ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn) + } typ := typecheck.ClosureType(clo) - clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil) + clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil) clos.SetEsc(clo.Esc()) clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...) + for i, value := range clos.List { + clos.List[i] = ir.NewStructKeyExpr(base.Pos, typ.Field(i), value) + } addr := typecheck.NodAddr(clos) addr.SetEsc(clo.Esc()) @@ -161,7 +165,7 @@ func closureArgs(clo *ir.ClosureExpr) []ir.Node { return args } -func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { +func walkMethodValue(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { // Create closure in the form of a composite literal. // For x.M with receiver (x) type T, the generated code looks like: // @@ -175,18 +179,16 @@ func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { n.X = cheapExpr(n.X, init) n.X = walkExpr(n.X, nil) - tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X)) - - c := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab) - c.SetTypecheck(1) - init.Append(c) + tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X) + check := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab) + init.Append(typecheck.Stmt(check)) } - typ := typecheck.PartialCallType(n) + typ := typecheck.MethodValueType(n) - clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil) + clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil) clos.SetEsc(n.Esc()) - clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, typecheck.MethodValueWrapper(n).Nname), n.X} + clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, methodValueWrapper(n)), n.X} addr := typecheck.NodAddr(clos) addr.SetEsc(n.Esc()) @@ -205,3 +207,68 @@ func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { return walkExpr(cfn, init) } + +// methodValueWrapper returns the ONAME node representing the +// wrapper function (*-fm) needed for the given method value. If the +// wrapper function hasn't already been created yet, it's created and +// added to typecheck.Target.Decls. +func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name { + if dot.Op() != ir.OMETHVALUE { + base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op()) + } + + t0 := dot.Type() + meth := dot.Sel + rcvrtype := dot.X.Type() + sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm") + + if sym.Uniq() { + return sym.Def.(*ir.Name) + } + sym.SetUniq(true) + + if base.Debug.Unified != 0 { + base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth) + } + + savecurfn := ir.CurFunc + saveLineNo := base.Pos + ir.CurFunc = nil + + base.Pos = base.AutogeneratedPos + + fn := typecheck.DeclFunc(sym, nil, + typecheck.NewFuncParams(t0.Params(), true), + typecheck.NewFuncParams(t0.Results(), false)) + fn.SetDupok(true) + fn.SetWrapper(true) + + // Declare and initialize variable holding receiver. + ptr := ir.NewHiddenParam(base.Pos, fn, typecheck.Lookup(".this"), rcvrtype) + + call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil) + call.Args = ir.ParamNames(fn.Type()) + call.IsDDD = fn.Type().IsVariadic() + + var body ir.Node = call + if t0.NumResults() != 0 { + ret := ir.NewReturnStmt(base.Pos, nil) + ret.Results = []ir.Node{call} + body = ret + } + + fn.Body = []ir.Node{body} + typecheck.FinishFuncBody() + + typecheck.Func(fn) + // Need to typecheck the body of the just-generated wrapper. + // typecheckslice() requires that Curfn is set when processing an ORETURN. + ir.CurFunc = fn + typecheck.Stmts(fn.Body) + sym.Def = fn.Nname + typecheck.Target.Decls = append(typecheck.Target.Decls, fn) + ir.CurFunc = savecurfn + base.Pos = saveLineNo + + return fn.Nname +} diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go index b18615f61a3e3b..fe9c5d883339ac 100644 --- a/src/cmd/compile/internal/walk/compare.go +++ b/src/cmd/compile/internal/walk/compare.go @@ -6,19 +6,39 @@ package walk import ( "encoding/binary" + "fmt" "go/constant" + "hash/fnv" + "io" "cmd/compile/internal/base" + "cmd/compile/internal/compare" "cmd/compile/internal/ir" "cmd/compile/internal/reflectdata" "cmd/compile/internal/ssagen" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" - "cmd/internal/sys" ) +func fakePC(n ir.Node) ir.Node { + // In order to get deterministic IDs, we include the package path, absolute filename, line number, column number + // in the calculation of the fakePC for the IR node. + hash := fnv.New32() + // We ignore the errors here because the `io.Writer` in the `hash.Hash` interface never returns an error. + io.WriteString(hash, base.Ctxt.Pkgpath) + io.WriteString(hash, base.Ctxt.PosTable.Pos(n.Pos()).AbsFilename()) + binary.Write(hash, binary.LittleEndian, int64(n.Pos().Line())) + binary.Write(hash, binary.LittleEndian, int64(n.Pos().Col())) + // We also include the string representation of the node to distinguish autogenerated expression since + // those get the same `src.XPos` + io.WriteString(hash, fmt.Sprintf("%v", n)) + + return ir.NewInt(int64(hash.Sum32())) +} + // The result of walkCompare MUST be assigned back to n, e.g. -// n.Left = walkCompare(n.Left, init) +// +// n.Left = walkCompare(n.Left, init) func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { if n.X.Type().IsInterface() && n.Y.Type().IsInterface() && n.X.Op() != ir.ONIL && n.Y.Op() != ir.ONIL { return walkCompareInterface(n, init) @@ -34,6 +54,10 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { // Given mixed interface/concrete comparison, // rewrite into types-equal && data-equal. // This is efficient, avoids allocations, and avoids runtime calls. + // + // TODO(mdempsky): It would be more general and probably overall + // simpler to just extend walkCompareInterface to optimize when one + // operand is an OCONVIFACE. if n.X.Type().IsInterface() != n.Y.Type().IsInterface() { // Preserve side-effects in case of short-circuiting; see #32187. l := cheapExpr(n.X, init) @@ -54,9 +78,12 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { // l.tab == type(r) // For non-empty interface, this is: // l.tab != nil && l.tab._type == type(r) + // + // TODO(mdempsky): For non-empty interface comparisons, just + // compare against the itab address directly? var eqtype ir.Node tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, l) - rtyp := reflectdata.TypePtr(r.Type()) + rtyp := reflectdata.CompareRType(base.Pos, n) if l.Type().IsEmptyInterface() { tab.SetType(types.NewPtr(types.Types[types.TUINT8])) tab.SetTypecheck(1) @@ -81,7 +108,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { var inline bool maxcmpsize := int64(4) - unalignedLoad := canMergeLoads() + unalignedLoad := ssagen.Arch.LinkArch.CanMergeLoads if unalignedLoad { // Keep this low enough to generate less code than a function call. maxcmpsize = 2 * int64(ssagen.Arch.LinkArch.RegSize) @@ -89,7 +116,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { switch t.Kind() { default: - if base.Debug.Libfuzzer != 0 && t.IsInteger() { + if base.Debug.Libfuzzer != 0 && t.IsInteger() && (n.X.Name() == nil || !n.X.Name().Libfuzzer8BitCounter()) { n.X = cheapExpr(n.X, init) n.Y = cheapExpr(n.Y, init) @@ -133,14 +160,14 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { default: base.Fatalf("unexpected integer size %d for %v", t.Size(), t) } - init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init))) + init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init), fakePC(n))) } return n case types.TARRAY: // We can compare several elements at once with 2/4/8 byte integer compares - inline = t.NumElem() <= 1 || (types.IsSimple[t.Elem().Kind()] && (t.NumElem() <= 4 || t.Elem().Width*t.NumElem() <= maxcmpsize)) + inline = t.NumElem() <= 1 || (types.IsSimple[t.Elem().Kind()] && (t.NumElem() <= 4 || t.Elem().Size()*t.NumElem() <= maxcmpsize)) case types.TSTRUCT: - inline = t.NumComponents(types.IgnoreBlankFields) <= 4 + inline = compare.EqStructCost(t) <= 4 } cmpl := n.X @@ -164,7 +191,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { call.Args.Append(typecheck.NodAddr(cmpl)) call.Args.Append(typecheck.NodAddr(cmpr)) if needsize { - call.Args.Append(ir.NewInt(t.Width)) + call.Args.Append(ir.NewInt(t.Size())) } res := ir.Node(call) if n.Op() != ir.OEQ { @@ -179,7 +206,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { andor = ir.OOROR } var expr ir.Node - compare := func(el, er ir.Node) { + comp := func(el, er ir.Node) { a := ir.NewBinaryExpr(base.Pos, n.Op(), el, er) if expr == nil { expr = a @@ -187,47 +214,55 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { expr = ir.NewLogicalExpr(base.Pos, andor, expr, a) } } + and := func(cond ir.Node) { + if expr == nil { + expr = cond + } else { + expr = ir.NewLogicalExpr(base.Pos, andor, expr, cond) + } + } cmpl = safeExpr(cmpl, init) cmpr = safeExpr(cmpr, init) if t.IsStruct() { - for _, f := range t.Fields().Slice() { - sym := f.Sym - if sym.IsBlank() { - continue + conds := compare.EqStruct(t, cmpl, cmpr) + if n.Op() == ir.OEQ { + for _, cond := range conds { + and(cond) + } + } else { + for _, cond := range conds { + notCond := ir.NewUnaryExpr(base.Pos, ir.ONOT, cond) + and(notCond) } - compare( - ir.NewSelectorExpr(base.Pos, ir.OXDOT, cmpl, sym), - ir.NewSelectorExpr(base.Pos, ir.OXDOT, cmpr, sym), - ) } } else { step := int64(1) - remains := t.NumElem() * t.Elem().Width - combine64bit := unalignedLoad && types.RegSize == 8 && t.Elem().Width <= 4 && t.Elem().IsInteger() - combine32bit := unalignedLoad && t.Elem().Width <= 2 && t.Elem().IsInteger() - combine16bit := unalignedLoad && t.Elem().Width == 1 && t.Elem().IsInteger() + remains := t.NumElem() * t.Elem().Size() + combine64bit := unalignedLoad && types.RegSize == 8 && t.Elem().Size() <= 4 && t.Elem().IsInteger() + combine32bit := unalignedLoad && t.Elem().Size() <= 2 && t.Elem().IsInteger() + combine16bit := unalignedLoad && t.Elem().Size() == 1 && t.Elem().IsInteger() for i := int64(0); remains > 0; { var convType *types.Type switch { case remains >= 8 && combine64bit: convType = types.Types[types.TINT64] - step = 8 / t.Elem().Width + step = 8 / t.Elem().Size() case remains >= 4 && combine32bit: convType = types.Types[types.TUINT32] - step = 4 / t.Elem().Width + step = 4 / t.Elem().Size() case remains >= 2 && combine16bit: convType = types.Types[types.TUINT16] - step = 2 / t.Elem().Width + step = 2 / t.Elem().Size() default: step = 1 } if step == 1 { - compare( + comp( ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i)), ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i)), ) i++ - remains -= t.Elem().Width + remains -= t.Elem().Size() } else { elemType := t.Elem().ToUnsigned() cmplw := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i))) @@ -242,17 +277,17 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { lb := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i+offset))) lb = typecheck.Conv(lb, elemType) lb = typecheck.Conv(lb, convType) - lb = ir.NewBinaryExpr(base.Pos, ir.OLSH, lb, ir.NewInt(8*t.Elem().Width*offset)) + lb = ir.NewBinaryExpr(base.Pos, ir.OLSH, lb, ir.NewInt(8*t.Elem().Size()*offset)) cmplw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmplw, lb) rb := ir.Node(ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i+offset))) rb = typecheck.Conv(rb, elemType) rb = typecheck.Conv(rb, convType) - rb = ir.NewBinaryExpr(base.Pos, ir.OLSH, rb, ir.NewInt(8*t.Elem().Width*offset)) + rb = ir.NewBinaryExpr(base.Pos, ir.OLSH, rb, ir.NewInt(8*t.Elem().Size()*offset)) cmprw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmprw, rb) } - compare(cmplw, cmprw) + comp(cmplw, cmprw) i += step - remains -= step * t.Elem().Width + remains -= step * t.Elem().Size() } } } @@ -260,9 +295,8 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { expr = ir.NewBool(n.Op() == ir.OEQ) // We still need to use cmpl and cmpr, in case they contain // an expression which might panic. See issue 23837. - t := typecheck.Temp(cmpl.Type()) - a1 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, t, cmpl)) - a2 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, t, cmpr)) + a1 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.BlankNode, cmpl)) + a2 := typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.BlankNode, cmpr)) init.Append(a1, a2) } return finishCompare(n, expr, init) @@ -271,7 +305,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { n.Y = cheapExpr(n.Y, init) n.X = cheapExpr(n.X, init) - eqtab, eqdata := reflectdata.EqInterface(n.X, n.Y) + eqtab, eqdata := compare.EqInterface(n.X, n.Y) var cmp ir.Node if n.Op() == ir.OEQ { cmp = ir.NewLogicalExpr(base.Pos, ir.OANDAND, eqtab, eqdata) @@ -283,6 +317,15 @@ func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { } func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { + if base.Debug.Libfuzzer != 0 { + if !ir.IsConst(n.X, constant.String) || !ir.IsConst(n.Y, constant.String) { + fn := "libfuzzerHookStrCmp" + n.X = cheapExpr(n.X, init) + n.Y = cheapExpr(n.Y, init) + paramType := types.Types[types.TSTRING] + init.Append(mkcall(fn, nil, init, tracecmpArg(n.X, paramType, init), tracecmpArg(n.Y, paramType, init), fakePC(n))) + } + } // Rewrite comparisons to short constant strings as length+byte-wise comparisons. var cs, ncs ir.Node // const string, non-const string switch { @@ -311,7 +354,7 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { maxRewriteLen := 6 // Some architectures can load unaligned byte sequence as 1 word. // So we can cover longer strings with the same amount of code. - canCombineLoads := canMergeLoads() + canCombineLoads := ssagen.Arch.LinkArch.CanMergeLoads combine64bit := false if canCombineLoads { // Keep this low enough to generate less code than a function call. @@ -385,7 +428,7 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { // prepare for rewrite below n.X = cheapExpr(n.X, init) n.Y = cheapExpr(n.Y, init) - eqlen, eqmem := reflectdata.EqString(n.X, n.Y) + eqlen, eqmem := compare.EqString(n.X, n.Y) // quick check of len before full compare for == or !=. // memequal then tests equality up to length len. if n.Op() == ir.OEQ { @@ -406,7 +449,8 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { } // The result of finishCompare MUST be assigned back to n, e.g. -// n.Left = finishCompare(n.Left, x, r, init) +// +// n.Left = finishCompare(n.Left, x, r, init) func finishCompare(n *ir.BinaryExpr, r ir.Node, init *ir.Nodes) ir.Node { r = typecheck.Expr(r) r = typecheck.Conv(r, n.Type()) @@ -491,18 +535,3 @@ func tracecmpArg(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node { return typecheck.Conv(n, t) } - -// canMergeLoads reports whether the backend optimization passes for -// the current architecture can combine adjacent loads into a single -// larger, possibly unaligned, load. Note that currently the -// optimizations must be able to handle little endian byte order. -func canMergeLoads() bool { - switch ssagen.Arch.LinkArch.Family { - case sys.ARM64, sys.AMD64, sys.I386, sys.S390X: - return true - case sys.PPC64: - // Load combining only supported on ppc64le. - return ssagen.Arch.LinkArch.ByteOrder == binary.LittleEndian - } - return false -} diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go index abd920d64612ff..ce7b731ca6d3c2 100644 --- a/src/cmd/compile/internal/walk/complit.go +++ b/src/cmd/compile/internal/walk/complit.go @@ -72,10 +72,6 @@ func isSimpleName(nn ir.Node) bool { return n.OnStack() } -func litas(l ir.Node, r ir.Node, init *ir.Nodes) { - appendWalkStmt(init, ir.NewAssignStmt(base.Pos, l, r)) -} - // initGenType is a bitmap indicating the types of generation that will occur for a static value. type initGenType uint8 @@ -218,11 +214,11 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node, case ir.OSTRUCTLIT: splitnode = func(rn ir.Node) (ir.Node, ir.Node) { r := rn.(*ir.StructKeyExpr) - if r.Field.IsBlank() || isBlank { + if r.Sym().IsBlank() || isBlank { return ir.BlankNode, r.Value } ir.SetPos(r) - return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Field), r.Value + return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Sym()), r.Value } default: base.Fatalf("fixedlit bad op: %v", n.Op()) @@ -239,7 +235,16 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node, case ir.OSLICELIT: value := value.(*ir.CompLitExpr) if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) { - slicelit(ctxt, value, a, init) + var sinit ir.Nodes + slicelit(ctxt, value, a, &sinit) + if kind == initKindStatic { + // When doing static initialization, init statements may contain dynamic + // expression, which will be initialized later, causing liveness analysis + // confuses about variables lifetime. So making sure those expressions + // are ordered correctly here. See issue #52673. + orderBlock(&sinit, map[string][]*ir.Name{}) + } + init.Append(sinit...) continue } @@ -262,9 +267,7 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node, case initKindStatic: genAsStatic(as) case initKindDynamic, initKindLocalCode: - a = orderStmtInPlace(as, map[string][]*ir.Name{}) - a = walkStmt(a) - init.Append(a) + appendWalkStmt(init, orderStmtInPlace(as, map[string][]*ir.Name{})) default: base.Fatalf("fixedlit: bad kind %d", kind) } @@ -277,7 +280,7 @@ func isSmallSliceLit(n *ir.CompLitExpr) bool { return false } - return n.Type().Elem().Width == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Width + return n.Type().Elem().Size() == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Size() } func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) { @@ -400,27 +403,22 @@ func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) // build list of vauto[c] = expr ir.SetPos(value) - as := typecheck.Stmt(ir.NewAssignStmt(base.Pos, a, value)) - as = orderStmtInPlace(as, map[string][]*ir.Name{}) - as = walkStmt(as) - init.Append(as) + as := ir.NewAssignStmt(base.Pos, a, value) + appendWalkStmt(init, orderStmtInPlace(typecheck.Stmt(as), map[string][]*ir.Name{})) } // make slice out of heap (6) a = ir.NewAssignStmt(base.Pos, var_, ir.NewSliceExpr(base.Pos, ir.OSLICE, vauto, nil, nil, nil)) - - a = typecheck.Stmt(a) - a = orderStmtInPlace(a, map[string][]*ir.Name{}) - a = walkStmt(a) - init.Append(a) + appendWalkStmt(init, orderStmtInPlace(typecheck.Stmt(a), map[string][]*ir.Name{})) } func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // make the map var - a := ir.NewCallExpr(base.Pos, ir.OMAKE, nil, nil) + args := []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(n.Len + int64(len(n.List)))} + a := typecheck.Expr(ir.NewCallExpr(base.Pos, ir.OMAKE, nil, args)).(*ir.MakeExpr) + a.RType = n.RType a.SetEsc(n.Esc()) - a.Args = []ir.Node{ir.TypeNode(n.Type()), ir.NewInt(int64(len(n.List)))} - litas(m, a, init) + appendWalkStmt(init, ir.NewAssignStmt(base.Pos, m, a)) entries := n.List @@ -440,8 +438,8 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { tk := types.NewArray(n.Type().Key(), int64(len(entries))) te := types.NewArray(n.Type().Elem(), int64(len(entries))) - tk.SetNoalg(true) - te.SetNoalg(true) + // TODO(#47904): mark tk and te NoAlg here once the + // compiler/linker can handle NoAlg types correctly. types.CalcSize(tk) types.CalcSize(te) @@ -470,19 +468,23 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { kidx := ir.NewIndexExpr(base.Pos, vstatk, i) kidx.SetBounded(true) - lhs := ir.NewIndexExpr(base.Pos, m, kidx) + + // typechecker rewrites OINDEX to OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, kidx)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType zero := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0)) cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(tk.NumElem())) incr := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1))) var body ir.Node = ir.NewAssignStmt(base.Pos, lhs, rhs) - body = typecheck.Stmt(body) // typechecker rewrites OINDEX to OINDEXMAP + body = typecheck.Stmt(body) body = orderStmtInPlace(body, map[string][]*ir.Name{}) loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil) loop.Body = []ir.Node{body} - *loop.PtrInit() = []ir.Node{zero} + loop.SetInit([]ir.Node{zero}) appendWalkStmt(init, loop) return @@ -492,6 +494,7 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { // Build list of var[c] = expr. // Use temporaries so that mapassign1 can have addressable key, elem. // TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys. + // TODO(khr): assign these temps in order phase so we can reuse them across multiple maplits? tmpkey := typecheck.Temp(m.Type().Key()) tmpelem := typecheck.Temp(m.Type().Elem()) @@ -506,14 +509,17 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) { appendWalkStmt(init, ir.NewAssignStmt(base.Pos, tmpelem, elem)) ir.SetPos(tmpelem) - var a ir.Node = ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, tmpkey), tmpelem) - a = typecheck.Stmt(a) // typechecker rewrites OINDEX to OINDEXMAP + + // typechecker rewrites OINDEX to OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, tmpkey)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType + + var a ir.Node = ir.NewAssignStmt(base.Pos, lhs, tmpelem) + a = typecheck.Stmt(a) a = orderStmtInPlace(a, map[string][]*ir.Name{}) appendWalkStmt(init, a) } - - appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpkey)) - appendWalkStmt(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, tmpelem)) } func anylit(n ir.Node, var_ ir.Node, init *ir.Nodes) { @@ -621,6 +627,12 @@ func oaslit(n *ir.AssignStmt, init *ir.Nodes) bool { // not a special composite literal assignment return false } + if x.Addrtaken() { + // If x is address-taken, the RHS may (implicitly) uses LHS. + // Not safe to do a special composite literal assignment + // (which may expand to multiple assignments). + return false + } switch n.Y.Op() { default: @@ -629,7 +641,7 @@ func oaslit(n *ir.AssignStmt, init *ir.Nodes) bool { case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT: if ir.Any(n.Y, func(y ir.Node) bool { return ir.Uses(y, x) }) { - // not a special composite literal assignment + // not safe to do a special composite literal assignment if RHS uses LHS. return false } anylit(n.Y, n.X, init) @@ -650,7 +662,7 @@ func genAsStatic(as *ir.AssignStmt) { switch r := as.Y; r.Op() { case ir.OLITERAL: - staticdata.InitConst(name, offset, r, int(r.Type().Width)) + staticdata.InitConst(name, offset, r, int(r.Type().Size())) return case ir.OMETHEXPR: r := r.(*ir.SelectorExpr) diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go index 26e17a126f2215..57f28e980026bc 100644 --- a/src/cmd/compile/internal/walk/convert.go +++ b/src/cmd/compile/internal/walk/convert.go @@ -24,9 +24,6 @@ func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node { return n.X } if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) { - if n.Type().IsPtr() && n.X.Type().IsUnsafePtr() { // unsafe.Pointer to *T - return walkCheckPtrAlignment(n, init, nil) - } if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer return walkCheckPtrArithmetic(n, init) } @@ -41,46 +38,107 @@ func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node { // walkConvInterface walks an OCONVIFACE node. func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { + n.X = walkExpr(n.X, init) fromType := n.X.Type() toType := n.Type() - - if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { // skip unnamed functions (func _()) - reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym) - } - - // typeword generates the type word of the interface value. - typeword := func() ir.Node { - if toType.IsEmptyInterface() { - return reflectdata.TypePtr(fromType) + if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { + // skip unnamed functions (func _()) + if base.Debug.Unified != 0 && fromType.HasShape() { + // Unified IR uses OCONVIFACE for converting all derived types + // to interface type. Avoid assertion failure in + // MarkTypeUsedInInterface, because we've marked used types + // separately anyway. + } else { + reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym) } - return reflectdata.ITabAddr(fromType, toType) } - // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped. - if types.IsDirectIface(fromType) { - l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), n.X) + if !fromType.IsInterface() { + typeWord := reflectdata.ConvIfaceTypeWord(base.Pos, n) + l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n, init)) l.SetType(toType) l.SetTypecheck(n.Typecheck()) return l } + if fromType.IsEmptyInterface() { + base.Fatalf("OCONVIFACE can't operate on an empty interface") + } - // Optimize convT2{E,I} for many cases in which T is not pointer-shaped, - // by using an existing addressable value identical to n.Left - // or creating one on the stack. + // Evaluate the input interface. + c := typecheck.Temp(fromType) + init.Append(ir.NewAssignStmt(base.Pos, c, n.X)) + + // Grab its parts. + itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c) + itab.SetType(types.Types[types.TUINTPTR].PtrTo()) + itab.SetTypecheck(1) + data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c) + data.SetType(types.Types[types.TUINT8].PtrTo()) // Type is generic pointer - we're just passing it through. + data.SetTypecheck(1) + + var typeWord ir.Node + if toType.IsEmptyInterface() { + // Implement interface to empty interface conversion. + // res = itab + // if res != nil { + // res = res.type + // } + typeWord = typecheck.Temp(types.NewPtr(types.Types[types.TUINT8])) + init.Append(ir.NewAssignStmt(base.Pos, typeWord, itab)) + nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil) + nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))} + init.Append(nif) + } else { + // Must be converting I2I (more specific to less specific interface). + // res = convI2I(toType, itab) + fn := typecheck.LookupRuntime("convI2I") + types.CalcSize(fn.Type()) + call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) + call.Args = []ir.Node{reflectdata.ConvIfaceTypeWord(base.Pos, n), itab} + typeWord = walkExpr(typecheck.Expr(call), init) + } + + // Build the result. + // e = iface{typeWord, data} + e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, data) + e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE. + e.SetTypecheck(1) + return e +} + +// Returns the data word (the second word) used to represent conv.X in +// an interface. +func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node { + pos, n := conv.Pos(), conv.X + fromType := n.Type() + + // If it's a pointer, it is its own representation. + if types.IsDirectIface(fromType) { + return n + } + + isInteger := fromType.IsInteger() + isBool := fromType.IsBoolean() + if sc := fromType.SoleComponent(); sc != nil { + isInteger = sc.IsInteger() + isBool = sc.IsBoolean() + } + // Try a bunch of cases to avoid an allocation. var value ir.Node switch { case fromType.Size() == 0: - // n.Left is zero-sized. Use zerobase. - cheapExpr(n.X, init) // Evaluate n.Left for side-effects. See issue 19246. + // n is zero-sized. Use zerobase. + cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246. value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR]) - case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()): - // n.Left is a bool/byte. Use staticuint64s[n.Left * 8] on little-endian - // and staticuint64s[n.Left * 8 + 7] on big-endian. - n.X = cheapExpr(n.X, init) - // byteindex widens n.Left so that the multiplication doesn't overflow. - index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n.X), ir.NewInt(3)) + case isBool || fromType.Size() == 1 && isInteger: + // n is a bool/byte. Use staticuint64s[n * 8] on little-endian + // and staticuint64s[n * 8 + 7] on big-endian. + n = cheapExpr(n, init) + n = soleComponent(init, n) + // byteindex widens n so that the multiplication doesn't overflow. + index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3)) if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian { index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7)) } @@ -90,118 +148,71 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node { xe := ir.NewIndexExpr(base.Pos, staticuint64s, index) xe.SetBounded(true) value = xe - case n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PEXTERN && n.X.(*ir.Name).Readonly(): - // n.Left is a readonly global; use it directly. - value = n.X - case !fromType.IsInterface() && n.Esc() == ir.EscNone && fromType.Width <= 1024: - // n.Left does not escape. Use a stack temporary initialized to n.Left. + case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly(): + // n is a readonly global; use it directly. + value = n + case conv.Esc() == ir.EscNone && fromType.Size() <= 1024: + // n does not escape. Use a stack temporary initialized to n. value = typecheck.Temp(fromType) - init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n.X))) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n))) } - if value != nil { - // Value is identical to n.Left. - // Construct the interface directly: {type/itab, &value}. - l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), typecheck.Expr(typecheck.NodAddr(value))) - l.SetType(toType) - l.SetTypecheck(n.Typecheck()) - return l - } - - // Implement interface to empty interface conversion. - // tmp = i.itab - // if tmp != nil { - // tmp = tmp.type - // } - // e = iface{tmp, i.data} - if toType.IsEmptyInterface() && fromType.IsInterface() && !fromType.IsEmptyInterface() { - // Evaluate the input interface. - c := typecheck.Temp(fromType) - init.Append(ir.NewAssignStmt(base.Pos, c, n.X)) - - // Get the itab out of the interface. - tmp := typecheck.Temp(types.NewPtr(types.Types[types.TUINT8])) - init.Append(ir.NewAssignStmt(base.Pos, tmp, typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, c)))) - - // Get the type out of the itab. - nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, tmp, typecheck.NodNil())), nil, nil) - nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, tmp, itabType(tmp))} - init.Append(nif) - - // Build the result. - e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, tmp, ifaceData(n.Pos(), c, types.NewPtr(types.Types[types.TUINT8]))) - e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE. - e.SetTypecheck(1) - return e + // The interface data word is &value. + return typecheck.Expr(typecheck.NodAddr(value)) } - fnname, argType, needsaddr := convFuncName(fromType, toType) - - if !needsaddr && !fromType.IsInterface() { - // Use a specialized conversion routine that only returns a data pointer. - // ptr = convT2X(val) - // e = iface{typ/tab, ptr} - fn := typecheck.LookupRuntime(fnname) - types.CalcSize(fromType) + // Time to do an allocation. We'll call into the runtime for that. + fnname, argType, needsaddr := dataWordFuncName(fromType) + fn := typecheck.LookupRuntime(fnname) - arg := n.X + var args []ir.Node + if needsaddr { + // Types of large or unknown size are passed by reference. + // Orderexpr arranged for n to be a temporary for all + // the conversions it could see. Comparison of an interface + // with a non-interface, especially in a switch on interface value + // with non-interface cases, is not visible to order.stmt, so we + // have to fall back on allocating a temp here. + if !ir.IsAddressable(n) { + n = copyExpr(n, fromType, init) + } + fn = typecheck.SubstArgTypes(fn, fromType) + args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)} + } else { + // Use a specialized conversion routine that takes the type being + // converted by value, not by pointer. + var arg ir.Node switch { case fromType == argType: // already in the right type, nothing to do + arg = n case fromType.Kind() == argType.Kind(), fromType.IsPtrShaped() && argType.IsPtrShaped(): // can directly convert (e.g. named type to underlying type, or one pointer to another) - arg = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, argType, arg) + // TODO: never happens because pointers are directIface? + arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n) case fromType.IsInteger() && argType.IsInteger(): // can directly convert (e.g. int32 to uint32) - arg = ir.NewConvExpr(n.Pos(), ir.OCONV, argType, arg) + arg = ir.NewConvExpr(pos, ir.OCONV, argType, n) default: // unsafe cast through memory - arg = copyExpr(arg, arg.Type(), init) + arg = copyExpr(n, fromType, init) var addr ir.Node = typecheck.NodAddr(arg) - addr = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, argType.PtrTo(), addr) - arg = ir.NewStarExpr(n.Pos(), addr) + addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr) + arg = ir.NewStarExpr(pos, addr) arg.SetType(argType) } - - call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) - call.Args = []ir.Node{arg} - e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), safeExpr(walkExpr(typecheck.Expr(call), init), init)) - e.SetType(toType) - e.SetTypecheck(1) - return e - } - - var tab ir.Node - if fromType.IsInterface() { - // convI2I - tab = reflectdata.TypePtr(toType) - } else { - // convT2x - tab = typeword() - } - - v := n.X - if needsaddr { - // Types of large or unknown size are passed by reference. - // Orderexpr arranged for n.Left to be a temporary for all - // the conversions it could see. Comparison of an interface - // with a non-interface, especially in a switch on interface value - // with non-interface cases, is not visible to order.stmt, so we - // have to fall back on allocating a temp here. - if !ir.IsAddressable(v) { - v = copyExpr(v, v.Type(), init) - } - v = typecheck.NodAddr(v) + args = []ir.Node{arg} } - - types.CalcSize(fromType) - fn := typecheck.LookupRuntime(fnname) - fn = typecheck.SubstArgTypes(fn, fromType, toType) - types.CalcSize(fn.Type()) call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil) - call.Args = []ir.Node{tab, v} - return walkExpr(typecheck.Expr(call), init) + call.Args = args + return safeExpr(walkExpr(typecheck.Expr(call), init), init) +} + +// walkConvIData walks an OCONVIDATA node. +func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node { + n.X = walkExpr(n.X, init) + return dataWord(n, init) } // walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node. @@ -312,50 +323,35 @@ func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node { return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING])) } -// convFuncName builds the runtime function name for interface conversion. -// It also returns the argument type that the runtime function takes, and -// whether the function expects the data by address. -// Not all names are possible. For example, we never generate convE2E or convE2I. -func convFuncName(from, to *types.Type) (fnname string, argType *types.Type, needsaddr bool) { - tkind := to.Tie() - switch from.Tie() { - case 'I': - if tkind == 'I' { - return "convI2I", types.Types[types.TINTER], false - } - case 'T': +// dataWordFuncName returns the name of the function used to convert a value of type "from" +// to the data word of an interface. +// argType is the type the argument needs to be coerced to. +// needsaddr reports whether the value should be passed (needaddr==false) or its address (needsaddr==true). +func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) { + if from.IsInterface() { + base.Fatalf("can only handle non-interfaces") + } + switch { + case from.Size() == 2 && uint8(from.Alignment()) == 2: + return "convT16", types.Types[types.TUINT16], false + case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers(): + return "convT32", types.Types[types.TUINT32], false + case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers(): + return "convT64", types.Types[types.TUINT64], false + } + if sc := from.SoleComponent(); sc != nil { switch { - case from.Size() == 2 && from.Align == 2: - return "convT16", types.Types[types.TUINT16], false - case from.Size() == 4 && from.Align == 4 && !from.HasPointers(): - return "convT32", types.Types[types.TUINT32], false - case from.Size() == 8 && from.Align == types.Types[types.TUINT64].Align && !from.HasPointers(): - return "convT64", types.Types[types.TUINT64], false - } - if sc := from.SoleComponent(); sc != nil { - switch { - case sc.IsString(): - return "convTstring", types.Types[types.TSTRING], false - case sc.IsSlice(): - return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter - } + case sc.IsString(): + return "convTstring", types.Types[types.TSTRING], false + case sc.IsSlice(): + return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter } + } - switch tkind { - case 'E': - if !from.HasPointers() { - return "convT2Enoptr", types.Types[types.TUNSAFEPTR], true - } - return "convT2E", types.Types[types.TUNSAFEPTR], true - case 'I': - if !from.HasPointers() { - return "convT2Inoptr", types.Types[types.TUNSAFEPTR], true - } - return "convT2I", types.Types[types.TUNSAFEPTR], true - } + if from.HasPointers() { + return "convT", types.Types[types.TUNSAFEPTR], true } - base.Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie()) - panic("unreachable") + return "convTnoptr", types.Types[types.TUNSAFEPTR], true } // rtconvfn returns the parameter and result types that will be used by a @@ -379,7 +375,7 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) { if dst.IsFloat() { switch src.Kind() { case types.TINT64, types.TUINT64: - return src.Kind(), types.TFLOAT64 + return src.Kind(), dst.Kind() } } @@ -395,7 +391,7 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) { if dst.IsFloat() { switch src.Kind() { case types.TINT64, types.TUINT64: - return src.Kind(), types.TFLOAT64 + return src.Kind(), dst.Kind() case types.TUINT32, types.TUINT, types.TUINTPTR: return types.TUINT32, types.TFLOAT64 } @@ -404,6 +400,29 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) { return types.Txxx, types.Txxx } +func soleComponent(init *ir.Nodes, n ir.Node) ir.Node { + if n.Type().SoleComponent() == nil { + return n + } + // Keep in sync with cmd/compile/internal/types/type.go:Type.SoleComponent. + for { + switch { + case n.Type().IsStruct(): + if n.Type().Field(0).Sym.IsBlank() { + // Treat blank fields as the zero value as the Go language requires. + n = typecheck.Temp(n.Type().Field(0).Type) + appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil)) + continue + } + n = typecheck.Expr(ir.NewSelectorExpr(n.Pos(), ir.OXDOT, n, n.Type().Field(0).Sym)) + case n.Type().IsArray(): + n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(0))) + default: + return n + } + } +} + // byteindex converts n, which is byte-sized, to an int used to index into an array. // We cannot use conv, because we allow converting bool to int here, // which is forbidden in user code. @@ -423,32 +442,6 @@ func byteindex(n ir.Node) ir.Node { return n } -func walkCheckPtrAlignment(n *ir.ConvExpr, init *ir.Nodes, count ir.Node) ir.Node { - if !n.Type().IsPtr() { - base.Fatalf("expected pointer type: %v", n.Type()) - } - elem := n.Type().Elem() - if count != nil { - if !elem.IsArray() { - base.Fatalf("expected array type: %v", elem) - } - elem = elem.Elem() - } - - size := elem.Size() - if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) { - return n - } - - if count == nil { - count = ir.NewInt(1) - } - - n.X = cheapExpr(n.X, init) - init.Append(mkcall("checkptrAlignment", nil, init, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]), reflectdata.TypePtr(elem), typecheck.Conv(count, types.Types[types.TUINTPTR]))) - return n -} - func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node { // Calling cheapExpr(n, init) below leads to a recursive call to // walkExpr, which leads us back here again. Use n.Checkptr to @@ -462,7 +455,9 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node { // TODO(mdempsky): Make stricter. We only need to exempt // reflect.Value.Pointer and reflect.Value.UnsafeAddr. switch n.X.Op() { - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: + case ir.OCALLMETH: + base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck") + case ir.OCALLFUNC, ir.OCALLINTER: return n } @@ -499,7 +494,7 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node { cheap := cheapExpr(n, init) - slice := typecheck.MakeDotArgs(types.NewSlice(types.Types[types.TUNSAFEPTR]), originals) + slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals) slice.SetEsc(ir.EscNone) init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice)) diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go index 2fb907710bbbda..2842c53df256e8 100644 --- a/src/cmd/compile/internal/walk/expr.go +++ b/src/cmd/compile/internal/walk/expr.go @@ -20,7 +20,8 @@ import ( ) // The result of walkExpr MUST be assigned back to n, e.g. -// n.Left = walkExpr(n.Left, init) +// +// n.Left = walkExpr(n.Left, init) func walkExpr(n ir.Node, init *ir.Nodes) ir.Node { if n == nil { return n @@ -82,7 +83,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { base.Fatalf("walkExpr: switch 1 unknown op %+v", n.Op()) panic("unreachable") - case ir.ONONAME, ir.OGETG: + case ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP: return n case ir.OTYPE, ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET: @@ -128,6 +129,14 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { n := n.(*ir.BinaryExpr) return walkUnsafeSlice(n, init) + case ir.OUNSAFESTRING: + n := n.(*ir.BinaryExpr) + return walkUnsafeString(n, init) + + case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA: + n := n.(*ir.UnaryExpr) + return walkUnsafeData(n, init) + case ir.ODOT, ir.ODOTPTR: n := n.(*ir.SelectorExpr) return walkDot(n, init) @@ -136,6 +145,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { n := n.(*ir.TypeAssertExpr) return walkDotType(n, init) + case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: + n := n.(*ir.DynamicTypeAssertExpr) + return walkDynamicDotType(n, init) + case ir.OLEN, ir.OCAP: n := n.(*ir.UnaryExpr) return walkLenCap(n, init) @@ -161,13 +174,13 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { n := n.(*ir.UnaryExpr) return mkcall("gopanic", nil, init, n.X) - case ir.ORECOVER: - return walkRecover(n.(*ir.CallExpr), init) + case ir.ORECOVERFP: + return walkRecoverFP(n.(*ir.CallExpr), init) case ir.OCFUNC: return n - case ir.OCALLINTER, ir.OCALLFUNC, ir.OCALLMETH: + case ir.OCALLINTER, ir.OCALLFUNC: n := n.(*ir.CallExpr) return walkCall(n, init) @@ -206,6 +219,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { n := n.(*ir.ConvExpr) return walkConvInterface(n, init) + case ir.OCONVIDATA: + n := n.(*ir.ConvExpr) + return walkConvIData(n, init) + case ir.OCONV, ir.OCONVNOP: n := n.(*ir.ConvExpr) return walkConv(n, init) @@ -235,6 +252,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { n := n.(*ir.SliceHeaderExpr) return walkSliceHeader(n, init) + case ir.OSTRINGHEADER: + n := n.(*ir.StringHeaderExpr) + return walkStringHeader(n, init) + case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR: n := n.(*ir.SliceExpr) return walkSlice(n, init) @@ -308,8 +329,8 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node { case ir.OCLOSURE: return walkClosure(n.(*ir.ClosureExpr), init) - case ir.OCALLPART: - return walkCallPart(n.(*ir.SelectorExpr), init) + case ir.OMETHVALUE: + return walkMethodValue(n.(*ir.SelectorExpr), init) } // No return! Each case must return (or panic), @@ -471,7 +492,7 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node { t := types.NewSlice(types.Types[types.TSTRING]) // args[1:] to skip buf arg - slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(t), args[1:]) + slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, t, args[1:]) slice.Prealloc = n.Prealloc args = []ir.Node{buf, slice} slice.SetEsc(ir.EscNone) @@ -487,9 +508,22 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node { return r1 } -// walkCall walks an OCALLFUNC, OCALLINTER, or OCALLMETH node. +type hookInfo struct { + paramType types.Kind + argsNum int + runtimeFunc string +} + +var hooks = map[string]hookInfo{ + "strings.EqualFold": {paramType: types.TSTRING, argsNum: 2, runtimeFunc: "libfuzzerHookEqualFold"}, +} + +// walkCall walks an OCALLFUNC or OCALLINTER node. func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node { - if n.Op() == ir.OCALLINTER || n.Op() == ir.OCALLMETH { + if n.Op() == ir.OCALLMETH { + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") + } + if n.Op() == ir.OCALLINTER || n.X.Op() == ir.OMETHEXPR { // We expect both interface call reflect.Type.Method and concrete // call reflect.(*rtype).Method. usemethod(n) @@ -523,8 +557,7 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node { var e ir.Node = ir.NewLinksymExpr(n.Pos(), fn.Sym().LinksymABI(abi), types.Types[types.TUINTPTR]) e = ir.NewAddrExpr(n.Pos(), e) e.SetType(types.Types[types.TUINTPTR].PtrTo()) - e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, n.Type(), e) - return e + return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, n.Type(), e)) } // fn is not a defined function. It must be ABIInternal. // Read the address from func value, i.e. *(*uintptr)(idata(fn)). @@ -534,8 +567,10 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node { arg = walkExpr(arg, init) var e ir.Node = ir.NewUnaryExpr(n.Pos(), ir.OIDATA, arg) e.SetType(n.Type().PtrTo()) + e.SetTypecheck(1) e = ir.NewStarExpr(n.Pos(), e) e.SetType(n.Type()) + e.SetTypecheck(1) return e } @@ -549,20 +584,8 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) { } n.SetWalked(true) - // If this is a method call t.M(...), - // rewrite into a function call T.M(t, ...). - // TODO(mdempsky): Do this right after type checking. if n.Op() == ir.OCALLMETH { - withRecv := make([]ir.Node, len(n.Args)+1) - dot := n.X.(*ir.SelectorExpr) - withRecv[0] = dot.X - copy(withRecv[1:], n.Args) - n.Args = withRecv - - dot = ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym) - - n.SetOp(ir.OCALLFUNC) - n.X = typecheck.Expr(dot) + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") } args := n.Args @@ -591,6 +614,20 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) { } n.Args = args + funSym := n.X.Sym() + if base.Debug.Libfuzzer != 0 && funSym != nil { + if hook, found := hooks[funSym.Pkg.Path+"."+funSym.Name]; found { + if len(args) != hook.argsNum { + panic(fmt.Sprintf("%s.%s expects %d arguments, but received %d", funSym.Pkg.Path, funSym.Name, hook.argsNum, len(args))) + } + var hookArgs []ir.Node + for _, arg := range args { + hookArgs = append(hookArgs, tracecmpArg(arg, types.Types[hook.paramType], init)) + } + hookArgs = append(hookArgs, fakePC(n)) + init.Append(mkcall(hook.runtimeFunc, nil, init, hookArgs...)) + } + } } // walkDivMod walks an ODIV or OMOD node. @@ -666,11 +703,19 @@ func walkDotType(n *ir.TypeAssertExpr, init *ir.Nodes) ir.Node { n.X = walkExpr(n.X, init) // Set up interface type addresses for back end. if !n.Type().IsInterface() && !n.X.Type().IsEmptyInterface() { - n.Itab = reflectdata.ITabAddr(n.Type(), n.X.Type()) + n.ITab = reflectdata.ITabAddr(n.Type(), n.X.Type()) } return n } +// walkDynamicdotType walks an ODYNAMICDOTTYPE or ODYNAMICDOTTYPE2 node. +func walkDynamicDotType(n *ir.DynamicTypeAssertExpr, init *ir.Nodes) ir.Node { + n.X = walkExpr(n.X, init) + n.RType = walkExpr(n.RType, init) + n.ITab = walkExpr(n.ITab, init) + return n +} + // walkIndex walks an OINDEX node. func walkIndex(n *ir.IndexExpr, init *ir.Nodes) ir.Node { n.X = walkExpr(n.X, init) @@ -695,43 +740,33 @@ func walkIndex(n *ir.IndexExpr, init *ir.Nodes) ir.Node { if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) { base.Warn("index bounds check elided") } - if ir.IsSmallIntConst(n.Index) && !n.Bounded() { - base.Errorf("index out of bounds") - } } else if ir.IsConst(n.X, constant.String) { n.SetBounded(bounded(r, int64(len(ir.StringVal(n.X))))) if base.Flag.LowerM != 0 && n.Bounded() && !ir.IsConst(n.Index, constant.Int) { base.Warn("index bounds check elided") } - if ir.IsSmallIntConst(n.Index) && !n.Bounded() { - base.Errorf("index out of bounds") - } - } - - if ir.IsConst(n.Index, constant.Int) { - if v := n.Index.Val(); constant.Sign(v) < 0 || ir.ConstOverflow(v, types.Types[types.TINT]) { - base.Errorf("index out of bounds") - } } return n } // mapKeyArg returns an expression for key that is suitable to be passed -// as the key argument for mapaccess and mapdelete functions. +// as the key argument for runtime map* functions. // n is is the map indexing or delete Node (to provide Pos). -// Note: this is not used for mapassign, which does distinguish pointer vs. -// integer key. -func mapKeyArg(fast int, n, key ir.Node) ir.Node { - switch fast { - case mapslow: +func mapKeyArg(fast int, n, key ir.Node, assigned bool) ir.Node { + if fast == mapslow { // standard version takes key by reference. - // order.expr made sure key is addressable. + // orderState.expr made sure key is addressable. return typecheck.NodAddr(key) + } + if assigned { + // mapassign does distinguish pointer vs. integer key. + return key + } + // mapaccess and mapdelete don't distinguish pointer vs. integer key. + switch fast { case mapfast32ptr: - // mapaccess and mapdelete don't distinguish pointer vs. integer key. return ir.NewConvExpr(n.Pos(), ir.OCONVNOP, types.Types[types.TUINT32], key) case mapfast64ptr: - // mapaccess and mapdelete don't distinguish pointer vs. integer key. return ir.NewConvExpr(n.Pos(), ir.OCONVNOP, types.Types[types.TUINT64], key) default: // fast version takes key by value. @@ -740,34 +775,27 @@ func mapKeyArg(fast int, n, key ir.Node) ir.Node { } // walkIndexMap walks an OINDEXMAP node. +// It replaces m[k] with *map{access1,assign}(maptype, m, &k) func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node { - // Replace m[k] with *map{access1,assign}(maptype, m, &k) n.X = walkExpr(n.X, init) n.Index = walkExpr(n.Index, init) map_ := n.X - key := n.Index t := map_.Type() - var call *ir.CallExpr - if n.Assigned { - // This m[k] expression is on the left-hand side of an assignment. - fast := mapfast(t) - if fast == mapslow { - // standard version takes key by reference. - // order.expr made sure key is addressable. - key = typecheck.NodAddr(key) - } - call = mkcall1(mapfn(mapassign[fast], t, false), nil, init, reflectdata.TypePtr(t), map_, key) - } else { - // m[k] is not the target of an assignment. - fast := mapfast(t) - key = mapKeyArg(fast, n, key) - if w := t.Elem().Width; w <= zeroValSize { - call = mkcall1(mapfn(mapaccess1[fast], t, false), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key) - } else { - z := reflectdata.ZeroAddr(w) - call = mkcall1(mapfn("mapaccess1_fat", t, true), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key, z) - } + fast := mapfast(t) + key := mapKeyArg(fast, n, n.Index, n.Assigned) + args := []ir.Node{reflectdata.IndexMapRType(base.Pos, n), map_, key} + + var mapFn ir.Node + switch { + case n.Assigned: + mapFn = mapfn(mapassign[fast], t, false) + case t.Elem().Size() > zeroValSize: + args = append(args, reflectdata.ZeroAddr(t.Elem().Size())) + mapFn = mapfn("mapaccess1_fat", t, true) + default: + mapFn = mapfn(mapaccess1[fast], t, false) } + call := mkcall1(mapFn, nil, init, args...) call.SetType(types.NewPtr(t.Elem())) call.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers. star := ir.NewStarExpr(base.Pos, call) @@ -801,15 +829,7 @@ func walkSend(n *ir.SendStmt, init *ir.Nodes) ir.Node { // walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node. func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node { - - checkSlice := ir.ShouldCheckPtr(ir.CurFunc, 1) && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr() - if checkSlice { - conv := n.X.(*ir.ConvExpr) - conv.X = walkExpr(conv.X, init) - } else { - n.X = walkExpr(n.X, init) - } - + n.X = walkExpr(n.X, init) n.Low = walkExpr(n.Low, init) if n.Low != nil && ir.IsZero(n.Low) { // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k]. @@ -817,9 +837,6 @@ func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node { } n.High = walkExpr(n.High, init) n.Max = walkExpr(n.Max, init) - if checkSlice { - n.X = walkCheckPtrAlignment(n.X.(*ir.ConvExpr), init, n.Max) - } if n.Op().IsSlice3() { if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) { @@ -844,6 +861,13 @@ func walkSliceHeader(n *ir.SliceHeaderExpr, init *ir.Nodes) ir.Node { return n } +// walkStringHeader walks an OSTRINGHEADER node. +func walkStringHeader(n *ir.StringHeaderExpr, init *ir.Nodes) ir.Node { + n.Ptr = walkExpr(n.Ptr, init) + n.Len = walkExpr(n.Len, init) + return n +} + // TODO(josharian): combine this with its caller and simplify func reduceSlice(n *ir.SliceExpr) ir.Node { if n.High != nil && n.High.Op() == ir.OLEN && ir.SameSafeExpr(n.X, n.High.(*ir.UnaryExpr).X) { @@ -867,7 +891,7 @@ func bounded(n ir.Node, max int64) bool { } sign := n.Type().IsSigned() - bits := int32(8 * n.Type().Width) + bits := int32(8 * n.Type().Size()) if ir.IsSmallIntConst(n) { v := ir.Int64Val(n) @@ -931,56 +955,55 @@ func bounded(n ir.Node, max int64) bool { return false } -// usemethod checks interface method calls for uses of reflect.Type.Method. +// usemethod checks calls for uses of reflect.Type.{Method,MethodByName}. func usemethod(n *ir.CallExpr) { - t := n.X.Type() + // Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package. + // Those functions may be alive via the itab, which should not cause all methods + // alive. We only want to mark their callers. + if base.Ctxt.Pkgpath == "reflect" { + switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names? + case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName": + return + } + } - // Looking for either of: - // Method(int) reflect.Method - // MethodByName(string) (reflect.Method, bool) - // - // TODO(crawshaw): improve precision of match by working out - // how to check the method name. - if n := t.NumParams(); n != 1 { + dot, ok := n.X.(*ir.SelectorExpr) + if !ok { return } - if n := t.NumResults(); n != 1 && n != 2 { + + // Looking for either direct method calls and interface method calls of: + // reflect.Type.Method - func(int) reflect.Method + // reflect.Type.MethodByName - func(string) (reflect.Method, bool) + var pKind types.Kind + + switch dot.Sel.Name { + case "Method": + pKind = types.TINT + case "MethodByName": + pKind = types.TSTRING + default: return } - p0 := t.Params().Field(0) - res0 := t.Results().Field(0) - var res1 *types.Field - if t.NumResults() == 2 { - res1 = t.Results().Field(1) - } - if res1 == nil { - if p0.Type.Kind() != types.TINT { - return - } - } else { - if !p0.Type.IsString() { - return - } - if !res1.Type.IsBoolean() { - return - } + t := dot.Selection.Type + if t.NumParams() != 1 || t.Params().Field(0).Type.Kind() != pKind { + return } - - // Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package. - // Those functions may be alive via the itab, which should not cause all methods - // alive. We only want to mark their callers. - if base.Ctxt.Pkgpath == "reflect" { - switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names? - case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName": + switch t.NumResults() { + case 1: + // ok + case 2: + if t.Results().Field(1).Type.Kind() != types.TBOOL { return } + default: + return } - // Note: Don't rely on res0.Type.String() since its formatting depends on multiple factors - // (including global variables such as numImports - was issue #19028). - // Also need to check for reflect package itself (see Issue #38515). - if s := res0.Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) { + // Check that first result type is "reflect.Method". Note that we have to check sym name and sym package + // separately, as we can't check for exact string "reflect.Method" reliably (e.g., see #19028 and #38515). + if s := t.Results().Field(0).Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) { ir.CurFunc.SetReflectMethod(true) // The LSym is initialized at this point. We need to set the attribute on the LSym. ir.CurFunc.LSym.Set(obj.AttrReflectMethod, true) @@ -1018,9 +1041,6 @@ func usefield(n *ir.SelectorExpr) { if outer.Sym() == nil { base.Errorf("tracked field must be in named struct type") } - if !types.IsExported(field.Sym.Name) { - base.Errorf("tracked field must be exported (upper case)") - } sym := reflectdata.TrackSym(outer, field) if ir.CurFunc.FieldTrack == nil { diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index b733d3a29f67cc..d4239347219d90 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -7,15 +7,14 @@ package walk import ( "fmt" "go/constant" - "internal/buildcfg" "cmd/compile/internal/base" - "cmd/compile/internal/escape" "cmd/compile/internal/ir" "cmd/compile/internal/reflectdata" "cmd/compile/internal/staticinit" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/objabi" "cmd/internal/src" ) @@ -37,23 +36,11 @@ import ( // Arrange that receive expressions only appear in direct assignments // x = <-c or as standalone statements <-c, never in larger expressions. -// TODO(rsc): The temporary introduction during multiple assignments -// should be moved into this file, so that the temporaries can be cleaned -// and so that conversions implicit in the OAS2FUNC and OAS2RECV -// nodes can be made explicit and then have their temporaries cleaned. - -// TODO(rsc): Goto and multilevel break/continue can jump over -// inserted VARKILL annotations. Work out a way to handle these. -// The current implementation is safe, in that it will execute correctly. -// But it won't reuse temporaries as aggressively as it might, and -// it can result in unnecessary zeroing of those variables in the function -// prologue. - // orderState holds state during the ordering process. type orderState struct { out []ir.Node // list of generated statements temp []*ir.Name // stack of temporary variables - free map[string][]*ir.Name // free list of unused temporaries, by type.LongString(). + free map[string][]*ir.Name // free list of unused temporaries, by type.LinkString(). edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS } @@ -64,7 +51,7 @@ func order(fn *ir.Func) { s := fmt.Sprintf("\nbefore order %v", fn.Sym()) ir.DumpList(s, fn.Body) } - + ir.SetPos(fn) // Set reasonable position for instrumenting code. See issue 53688. orderBlock(&fn.Body, map[string][]*ir.Name{}) } @@ -78,20 +65,14 @@ func (o *orderState) append(stmt ir.Node) { // If clear is true, newTemp emits code to zero the temporary. func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name { var v *ir.Name - // Note: LongString is close to the type equality we want, - // but not exactly. We still need to double-check with types.Identical. - key := t.LongString() - a := o.free[key] - for i, n := range a { - if types.Identical(t, n.Type()) { - v = a[i] - a[i] = a[len(a)-1] - a = a[:len(a)-1] - o.free[key] = a - break + key := t.LinkString() + if a := o.free[key]; len(a) > 0 { + v = a[len(a)-1] + if !types.Identical(t, v.Type()) { + base.Fatalf("expected %L to have type %v", v, t) } - } - if v == nil { + o.free[key] = a[:len(a)-1] + } else { v = typecheck.Temp(t) } if clear { @@ -230,21 +211,12 @@ func (o *orderState) safeExpr(n ir.Node) ir.Node { } } -// isaddrokay reports whether it is okay to pass n's address to runtime routines. -// Taking the address of a variable makes the liveness and optimization analyses -// lose track of where the variable's lifetime ends. To avoid hurting the analyses -// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay, -// because we emit explicit VARKILL instructions marking the end of those -// temporaries' lifetimes. -func isaddrokay(n ir.Node) bool { - return ir.IsAddressable(n) && (n.Op() != ir.ONAME || n.(*ir.Name).Class == ir.PEXTERN || ir.IsAutoTmp(n)) -} - // addrTemp ensures that n is okay to pass by address to runtime routines. // If the original argument n is not okay, addrTemp creates a tmp, emits // tmp = n, and then returns tmp. // The result of addrTemp MUST be assigned back to n, e.g. -// n.Left = o.addrTemp(n.Left) +// +// n.Left = o.addrTemp(n.Left) func (o *orderState) addrTemp(n ir.Node) ir.Node { if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL { // TODO: expand this to all static composite literal nodes? @@ -259,7 +231,7 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node { vstat = typecheck.Expr(vstat).(*ir.Name) return vstat } - if isaddrokay(n) { + if ir.IsAddressable(n) { return n } return o.copyExpr(n) @@ -267,7 +239,13 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node { // mapKeyTemp prepares n to be a key in a map runtime call and returns n. // It should only be used for map runtime calls which have *_fast* versions. -func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node { +// The first parameter is the position of n's containing node, for use in case +// that n's position is not unique (e.g., if n is an ONAME). +func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir.Node { + pos := outerPos + if ir.HasUniquePos(n) { + pos = n.Pos() + } // Most map calls need to take the address of the key. // Exception: map*_fast* calls. See golang.org/issue/19015. alg := mapfast(t) @@ -291,7 +269,7 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node { return n case nt.Kind() == kt.Kind(), nt.IsPtrShaped() && kt.IsPtrShaped(): // can directly convert (e.g. named type to underlying type, or one pointer to another) - return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONVNOP, kt, n)) + return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONVNOP, kt, n)) case nt.IsInteger() && kt.IsInteger(): // can directly convert (e.g. int32 to uint32) if n.Op() == ir.OLITERAL && nt.IsSigned() { @@ -300,20 +278,20 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node { n.SetType(kt) return n } - return typecheck.Expr(ir.NewConvExpr(n.Pos(), ir.OCONV, kt, n)) + return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, kt, n)) default: // Unsafe cast through memory. // We'll need to do a load with type kt. Create a temporary of type kt to // ensure sufficient alignment. nt may be under-aligned. - if kt.Align < nt.Align { + if uint8(kt.Alignment()) < uint8(nt.Alignment()) { base.Fatalf("mapKeyTemp: key type is not sufficiently aligned, kt=%v nt=%v", kt, nt) } tmp := o.newTemp(kt, true) // *(*nt)(&tmp) = n var e ir.Node = typecheck.NodAddr(tmp) - e = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, nt.PtrTo(), e) - e = ir.NewStarExpr(n.Pos(), e) - o.append(ir.NewAssignStmt(base.Pos, e, n)) + e = ir.NewConvExpr(pos, ir.OCONVNOP, nt.PtrTo(), e) + e = ir.NewStarExpr(pos, e) + o.append(ir.NewAssignStmt(pos, e, n)) return tmp } } @@ -323,8 +301,10 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node { // Returns a bool that signals if a modification was made. // // For: -// x = m[string(k)] -// x = m[T1{... Tn{..., string(k), ...}] +// +// x = m[string(k)] +// x = m[T1{... Tn{..., string(k), ...}] +// // where k is []byte, T1 to Tn is a nesting of struct and array literals, // the allocation of backing bytes for the string can be avoided // by reusing the []byte backing array. These are special cases @@ -372,31 +352,12 @@ func (o *orderState) markTemp() ordermarker { // which must have been returned by markTemp. func (o *orderState) popTemp(mark ordermarker) { for _, n := range o.temp[mark:] { - key := n.Type().LongString() + key := n.Type().LinkString() o.free[key] = append(o.free[key], n) } o.temp = o.temp[:mark] } -// cleanTempNoPop emits VARKILL instructions to *out -// for each temporary above the mark on the temporary stack. -// It does not pop the temporaries from the stack. -func (o *orderState) cleanTempNoPop(mark ordermarker) []ir.Node { - var out []ir.Node - for i := len(o.temp) - 1; i >= int(mark); i-- { - n := o.temp[i] - out = append(out, typecheck.Stmt(ir.NewUnaryExpr(base.Pos, ir.OVARKILL, n))) - } - return out -} - -// cleanTemp emits VARKILL instructions for each temporary above the -// mark on the temporary stack and removes them from the stack. -func (o *orderState) cleanTemp(top ordermarker) { - o.out = append(o.out, o.cleanTempNoPop(top)...) - o.popTemp(top) -} - // stmtList orders each of the statements in the list. func (o *orderState) stmtList(l ir.Nodes) { s := l @@ -407,9 +368,12 @@ func (o *orderState) stmtList(l ir.Nodes) { } // orderMakeSliceCopy matches the pattern: -// m = OMAKESLICE([]T, x); OCOPY(m, s) +// +// m = OMAKESLICE([]T, x); OCOPY(m, s) +// // and rewrites it to: -// m = OMAKESLICECOPY([]T, x, s); nil +// +// m = OMAKESLICECOPY([]T, x, s); nil func orderMakeSliceCopy(s []ir.Node) { if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting { return @@ -447,33 +411,57 @@ func (o *orderState) edge() { return } - // Create a new uint8 counter to be allocated in section - // __libfuzzer_extra_counters. + // Create a new uint8 counter to be allocated in section __sancov_cntrs counter := staticinit.StaticName(types.Types[types.TUINT8]) - counter.SetLibfuzzerExtraCounter(true) - - // counter += 1 - incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1)) - o.append(incr) + counter.SetLibfuzzer8BitCounter(true) + // As well as setting SetLibfuzzer8BitCounter, we preemptively set the + // symbol type to SLIBFUZZER_8BIT_COUNTER so that the race detector + // instrumentation pass (which does not have access to the flags set by + // SetLibfuzzer8BitCounter) knows to ignore them. This information is + // lost by the time it reaches the compile step, so SetLibfuzzer8BitCounter + // is still necessary. + counter.Linksym().Type = objabi.SLIBFUZZER_8BIT_COUNTER + + // We guarantee that the counter never becomes zero again once it has been + // incremented once. This implementation follows the NeverZero optimization + // presented by the paper: + // "AFL++: Combining Incremental Steps of Fuzzing Research" + // The NeverZero policy avoids the overflow to 0 by setting the counter to one + // after it reaches 255 and so, if an edge is executed at least one time, the entry is + // never 0. + // Another policy presented in the paper is the Saturated Counters policy which + // freezes the counter when it reaches the value of 255. However, a range + // of experiments showed that that decreases overall performance. + o.append(ir.NewIfStmt(base.Pos, + ir.NewBinaryExpr(base.Pos, ir.OEQ, counter, ir.NewInt(0xff)), + []ir.Node{ir.NewAssignStmt(base.Pos, counter, ir.NewInt(1))}, + []ir.Node{ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))})) } // orderBlock orders the block of statements in n into a new slice, // and then replaces the old slice in n with the new slice. // free is a map that can be used to obtain temporary variables by type. func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) { + if len(*n) != 0 { + // Set reasonable position for instrumenting code. See issue 53688. + // It would be nice if ir.Nodes had a position (the opening {, probably), + // but it doesn't. So we use the first statement's position instead. + ir.SetPos((*n)[0]) + } var order orderState order.free = free mark := order.markTemp() order.edge() order.stmtList(*n) - order.cleanTemp(mark) + order.popTemp(mark) *n = order.out } // exprInPlace orders the side effects in *np and // leaves them as the init list of the final *np. // The result of exprInPlace MUST be assigned back to n, e.g. -// n.Left = o.exprInPlace(n.Left) +// +// n.Left = o.exprInPlace(n.Left) func (o *orderState) exprInPlace(n ir.Node) ir.Node { var order orderState order.free = o.free @@ -489,14 +477,16 @@ func (o *orderState) exprInPlace(n ir.Node) ir.Node { // orderStmtInPlace orders the side effects of the single statement *np // and replaces it with the resulting statement list. // The result of orderStmtInPlace MUST be assigned back to n, e.g. -// n.Left = orderStmtInPlace(n.Left) +// +// n.Left = orderStmtInPlace(n.Left) +// // free is a map that can be used to obtain temporary variables by type. func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node { var order orderState order.free = free mark := order.markTemp() order.stmt(n) - order.cleanTemp(mark) + order.popTemp(mark) return ir.NewBlockStmt(src.NoXPos, order.out) } @@ -514,15 +504,18 @@ func (o *orderState) init(n ir.Node) { } // call orders the call expression n. -// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. +// n.Op is OCALLFUNC/OCALLINTER or a builtin like OCOPY. func (o *orderState) call(nn ir.Node) { if len(nn.Init()) > 0 { // Caller should have already called o.init(nn). base.Fatalf("%v with unexpected ninit", nn.Op()) } + if nn.Op() == ir.OCALLMETH { + base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck") + } // Builtin functions. - if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER { + if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER { switch n := nn.(type) { default: base.Fatalf("unexpected call: %+v", n) @@ -554,39 +547,6 @@ func (o *orderState) call(nn ir.Node) { n.X = o.expr(n.X, nil) o.exprList(n.Args) - - if n.Op() == ir.OCALLINTER { - return - } - keepAlive := func(arg ir.Node) { - // If the argument is really a pointer being converted to uintptr, - // arrange for the pointer to be kept alive until the call returns, - // by copying it into a temp and marking that temp - // still alive when we pop the temp stack. - if arg.Op() == ir.OCONVNOP { - arg := arg.(*ir.ConvExpr) - if arg.X.Type().IsUnsafePtr() { - x := o.copyExpr(arg.X) - arg.X = x - x.SetAddrtaken(true) // ensure SSA keeps the x variable - n.KeepAlive = append(n.KeepAlive, x) - } - } - } - - // Check for "unsafe-uintptr" tag provided by escape analysis. - for i, param := range n.X.Type().Params().FieldSlice() { - if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote { - if arg := n.Args[i]; arg.Op() == ir.OSLICELIT { - arg := arg.(*ir.CompLitExpr) - for _, elt := range arg.List { - keepAlive(elt) - } - } else { - keepAlive(arg) - } - } - } } // mapAssign appends n to o.out. @@ -625,8 +585,6 @@ func (o *orderState) safeMapRHS(r ir.Node) ir.Node { } // stmt orders the statement n, appending to o.out. -// Temporaries created during the statement are cleaned -// up using VARKILL instructions as possible. func (o *orderState) stmt(n ir.Node) { if n == nil { return @@ -639,7 +597,7 @@ func (o *orderState) stmt(n ir.Node) { default: base.Fatalf("order.stmt %v", n.Op()) - case ir.OVARKILL, ir.OVARLIVE, ir.OINLMARK: + case ir.OINLMARK: o.out = append(o.out, n) case ir.OAS: @@ -648,7 +606,7 @@ func (o *orderState) stmt(n ir.Node) { n.X = o.expr(n.X, nil) n.Y = o.expr(n.Y, n.X) o.mapAssign(n) - o.cleanTemp(t) + o.popTemp(t) case ir.OASOP: n := n.(*ir.AssignOpStmt) @@ -673,12 +631,12 @@ func (o *orderState) stmt(n ir.Node) { r := o.expr(typecheck.Expr(ir.NewBinaryExpr(n.Pos(), n.AsOp, l2, n.Y)), nil) as := typecheck.Stmt(ir.NewAssignStmt(n.Pos(), l1, r)) o.mapAssign(as) - o.cleanTemp(t) + o.popTemp(t) return } o.mapAssign(n) - o.cleanTemp(t) + o.popTemp(t) case ir.OAS2: n := n.(*ir.AssignListStmt) @@ -686,17 +644,28 @@ func (o *orderState) stmt(n ir.Node) { o.exprList(n.Lhs) o.exprList(n.Rhs) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) // Special: avoid copy of func call n.Right case ir.OAS2FUNC: n := n.(*ir.AssignListStmt) t := o.markTemp() o.exprList(n.Lhs) - o.init(n.Rhs[0]) - o.call(n.Rhs[0]) - o.as2func(n) - o.cleanTemp(t) + call := n.Rhs[0] + o.init(call) + if ic, ok := call.(*ir.InlinedCallExpr); ok { + o.stmtList(ic.Body) + + n.SetOp(ir.OAS2) + n.Rhs = ic.ReturnVars + + o.exprList(n.Rhs) + o.out = append(o.out, n) + } else { + o.call(call) + o.as2func(n) + } + o.popTemp(t) // Special: use temporary variables to hold result, // so that runtime can take address of temporary. @@ -713,6 +682,11 @@ func (o *orderState) stmt(n ir.Node) { case ir.ODOTTYPE2: r := r.(*ir.TypeAssertExpr) r.X = o.expr(r.X, nil) + case ir.ODYNAMICDOTTYPE2: + r := r.(*ir.DynamicTypeAssertExpr) + r.X = o.expr(r.X, nil) + r.RType = o.expr(r.RType, nil) + r.ITab = o.expr(r.ITab, nil) case ir.ORECV: r := r.(*ir.UnaryExpr) r.X = o.expr(r.X, nil) @@ -722,13 +696,13 @@ func (o *orderState) stmt(n ir.Node) { r.Index = o.expr(r.Index, nil) // See similar conversion for OINDEXMAP below. _ = mapKeyReplaceStrConv(r.Index) - r.Index = o.mapKeyTemp(r.X.Type(), r.Index) + r.Index = o.mapKeyTemp(r.Pos(), r.X.Type(), r.Index) default: base.Fatalf("order.stmt: %v", r.Op()) } o.as2ok(n) - o.cleanTemp(t) + o.popTemp(t) // Special: does not save n onto out. case ir.OBLOCK: @@ -748,19 +722,30 @@ func (o *orderState) stmt(n ir.Node) { o.out = append(o.out, n) // Special: handle call arguments. - case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH: + case ir.OCALLFUNC, ir.OCALLINTER: n := n.(*ir.CallExpr) t := o.markTemp() o.call(n) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) + + case ir.OINLCALL: + n := n.(*ir.InlinedCallExpr) + o.stmtList(n.Body) - case ir.OCLOSE, ir.ORECV: + // discard results; double-check for no side effects + for _, result := range n.ReturnVars { + if staticinit.AnySideEffects(result) { + base.FatalfAt(result.Pos(), "inlined call result has side effects: %v", result) + } + } + + case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV: n := n.(*ir.UnaryExpr) t := o.markTemp() n.X = o.expr(n.X, nil) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) case ir.OCOPY: n := n.(*ir.BinaryExpr) @@ -768,14 +753,14 @@ func (o *orderState) stmt(n ir.Node) { n.X = o.expr(n.X, nil) n.Y = o.expr(n.Y, nil) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) - case ir.OPRINT, ir.OPRINTN, ir.ORECOVER: + case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP: n := n.(*ir.CallExpr) t := o.markTemp() - o.exprList(n.Args) + o.call(n) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) // Special: order arguments to inner call but not call itself. case ir.ODEFER, ir.OGO: @@ -783,27 +768,17 @@ func (o *orderState) stmt(n ir.Node) { t := o.markTemp() o.init(n.Call) o.call(n.Call) - if n.Call.Op() == ir.ORECOVER { - // Special handling of "defer recover()". We need to evaluate the FP - // argument before wrapping. - var init ir.Nodes - n.Call = walkRecover(n.Call.(*ir.CallExpr), &init) - o.stmtList(init) - } - if buildcfg.Experiment.RegabiDefer { - o.wrapGoDefer(n) - } o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) case ir.ODELETE: n := n.(*ir.CallExpr) t := o.markTemp() n.Args[0] = o.expr(n.Args[0], nil) n.Args[1] = o.expr(n.Args[1], nil) - n.Args[1] = o.mapKeyTemp(n.Args[0].Type(), n.Args[1]) + n.Args[1] = o.mapKeyTemp(n.Pos(), n.Args[0].Type(), n.Args[1]) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) // Clean temporaries from condition evaluation at // beginning of loop body and after for statement. @@ -811,11 +786,10 @@ func (o *orderState) stmt(n ir.Node) { n := n.(*ir.ForStmt) t := o.markTemp() n.Cond = o.exprInPlace(n.Cond) - n.Body.Prepend(o.cleanTempNoPop(t)...) orderBlock(&n.Body, o.free) n.Post = orderStmtInPlace(n.Post, o.free) o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) // Clean temporaries from condition at // beginning of both branches. @@ -823,23 +797,11 @@ func (o *orderState) stmt(n ir.Node) { n := n.(*ir.IfStmt) t := o.markTemp() n.Cond = o.exprInPlace(n.Cond) - n.Body.Prepend(o.cleanTempNoPop(t)...) - n.Else.Prepend(o.cleanTempNoPop(t)...) o.popTemp(t) orderBlock(&n.Body, o.free) orderBlock(&n.Else, o.free) o.out = append(o.out, n) - case ir.OPANIC: - n := n.(*ir.UnaryExpr) - t := o.markTemp() - n.X = o.expr(n.X, nil) - if !n.X.Type().IsEmptyInterface() { - base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X) - } - o.out = append(o.out, n) - o.cleanTemp(t) - case ir.ORANGE: // n.Right is the expression being ranged over. // order it, and then make a copy if we need one. @@ -914,7 +876,7 @@ func (o *orderState) stmt(n ir.Node) { orderBlock(&n.Body, o.free) } o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) case ir.ORETURN: n := n.(*ir.ReturnStmt) @@ -973,6 +935,12 @@ func (o *orderState) stmt(n ir.Node) { if colas { if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n { init = init[1:] + + // iimport may have added a default initialization assignment, + // due to how it handles ODCL statements. + if len(init) > 0 && init[0].Op() == ir.OAS && init[0].(*ir.AssignStmt).X == n { + init = init[1:] + } } dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name))) ncas.PtrInit().Append(dcl) @@ -985,7 +953,7 @@ func (o *orderState) stmt(n ir.Node) { do(0, recv.X.Type().Elem()) do(1, types.Types[types.TBOOL]) if len(init) != 0 { - ir.DumpList("ninit", r.Init()) + ir.DumpList("ninit", init) base.Fatalf("ninit on select recv") } orderBlock(ncas.PtrInit(), o.free) @@ -1015,7 +983,6 @@ func (o *orderState) stmt(n ir.Node) { // (The temporary cleaning must follow that ninit work.) for _, cas := range n.Cases { orderBlock(&cas.Body, o.free) - cas.Body.Prepend(o.cleanTempNoPop(t)...) // TODO(mdempsky): Is this actually necessary? // walkSelect appears to walk Ninit. @@ -1039,7 +1006,7 @@ func (o *orderState) stmt(n ir.Node) { n.Value = o.addrTemp(n.Value) } o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) // TODO(rsc): Clean temporaries more aggressively. // Note that because walkSwitch will rewrite some of the @@ -1063,7 +1030,7 @@ func (o *orderState) stmt(n ir.Node) { } o.out = append(o.out, n) - o.cleanTemp(t) + o.popTemp(t) } base.Pos = lno @@ -1105,7 +1072,8 @@ func (o *orderState) exprNoLHS(n ir.Node) ir.Node { // Otherwise lhs == nil. (When lhs != nil it may be possible // to avoid copying the result of the expression to a temporary.) // The result of expr MUST be assigned back to n, e.g. -// n.Left = o.expr(n.Left, lhs) +// +// n.Left = o.expr(n.Left, lhs) func (o *orderState) expr(n, lhs ir.Node) ir.Node { if n == nil { return n @@ -1184,7 +1152,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { } // key must be addressable - n.Index = o.mapKeyTemp(n.X.Type(), n.Index) + n.Index = o.mapKeyTemp(n.Pos(), n.X.Type(), n.Index) if needCopy { return o.copyExpr(n) } @@ -1192,23 +1160,26 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { // concrete type (not interface) argument might need an addressable // temporary to pass to the runtime conversion routine. - case ir.OCONVIFACE: + case ir.OCONVIFACE, ir.OCONVIDATA: n := n.(*ir.ConvExpr) n.X = o.expr(n.X, nil) if n.X.Type().IsInterface() { return n } - if _, _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) { + if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) { // Need a temp if we need to pass the address to the conversion function. // We also process static composite literal node here, making a named static global - // whose address we can put directly in an interface (see OCONVIFACE case in walk). + // whose address we can put directly in an interface (see OCONVIFACE/OCONVIDATA case in walk). n.X = o.addrTemp(n.X) } return n case ir.OCONVNOP: n := n.(*ir.ConvExpr) - if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) { + if n.X.Op() == ir.OCALLMETH { + base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck") + } + if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) { call := n.X.(*ir.CallExpr) // When reordering unsafe.Pointer(f()) into a separate // statement, the conversion and function call must stay @@ -1247,7 +1218,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { o.edge() rhs := o.expr(n.Y, nil) o.out = append(o.out, typecheck.Stmt(ir.NewAssignStmt(base.Pos, r, rhs))) - o.cleanTemp(t) + o.popTemp(t) gen := o.out o.out = saveout @@ -1261,9 +1232,12 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { o.out = append(o.out, nif) return r + case ir.OCALLMETH: + base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") + panic("unreachable") + case ir.OCALLFUNC, ir.OCALLINTER, - ir.OCALLMETH, ir.OCAP, ir.OCOMPLEX, ir.OCOPY, @@ -1275,7 +1249,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { ir.OMAKESLICECOPY, ir.ONEW, ir.OREAL, - ir.ORECOVER, + ir.ORECOVERFP, ir.OSTR2BYTES, ir.OSTR2BYTESTMP, ir.OSTR2RUNES: @@ -1293,6 +1267,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { } return n + case ir.OINLCALL: + n := n.(*ir.InlinedCallExpr) + o.stmtList(n.Body) + return n.SingleResult() + case ir.OAPPEND: // Check for append(x, make([]T, y)...) . n := n.(*ir.CallExpr) @@ -1327,11 +1306,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { } return n - case ir.OCALLPART: + case ir.OMETHVALUE: n := n.(*ir.SelectorExpr) n.X = o.expr(n.X, nil) if n.Transient() { - t := typecheck.PartialCallType(n) + t := typecheck.MethodValueType(n) n.Prealloc = o.newTemp(t, false) } return n @@ -1436,10 +1415,23 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { // Emit eval+insert of dynamic entries, one at a time. for _, r := range dynamics { - as := ir.NewAssignStmt(base.Pos, ir.NewIndexExpr(base.Pos, m, r.Key), r.Value) - typecheck.Stmt(as) // Note: this converts the OINDEX to an OINDEXMAP + lhs := typecheck.AssignExpr(ir.NewIndexExpr(base.Pos, m, r.Key)).(*ir.IndexExpr) + base.AssertfAt(lhs.Op() == ir.OINDEXMAP, lhs.Pos(), "want OINDEXMAP, have %+v", lhs) + lhs.RType = n.RType + + as := ir.NewAssignStmt(base.Pos, lhs, r.Value) + typecheck.Stmt(as) o.stmt(as) } + + // Remember that we issued these assignments so we can include that count + // in the map alloc hint. + // We're assuming here that all the keys in the map literal are distinct. + // If any are equal, this will be an overcount. Probably not worth accounting + // for that, as equal keys in map literals are rare, and at worst we waste + // a bit of space. + n.Len += int64(len(dynamics)) + return m } @@ -1449,10 +1441,14 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { // as2func orders OAS2FUNC nodes. It creates temporaries to ensure left-to-right assignment. // The caller should order the right-hand side of the assignment before calling order.as2func. // It rewrites, +// // a, b, a = ... +// // as +// // tmp1, tmp2, tmp3 = ... // a, b, a = tmp1, tmp2, tmp3 +// // This is necessary to ensure left to right assignment order. func (o *orderState) as2func(n *ir.AssignListStmt) { results := n.Rhs[0].Type() @@ -1498,313 +1494,6 @@ func (o *orderState) as2ok(n *ir.AssignListStmt) { o.stmt(typecheck.Stmt(as)) } -var wrapGoDefer_prgen int - -// wrapGoDefer wraps the target of a "go" or "defer" statement with a -// new "function with no arguments" closure. Specifically, it converts -// -// defer f(x, y) -// -// to -// -// x1, y1 := x, y -// defer func() { f(x1, y1) }() -// -// This is primarily to enable a quicker bringup of defers under the -// new register ABI; by doing this conversion, we can simplify the -// code in the runtime that invokes defers on the panic path. -func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) { - call := n.Call - - var callX ir.Node // thing being called - var callArgs []ir.Node // call arguments - var keepAlive []*ir.Name // KeepAlive list from call, if present - - // A helper to recreate the call within the closure. - var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node - - // Defer calls come in many shapes and sizes; not all of them - // are ir.CallExpr's. Examine the type to see what we're dealing with. - switch x := call.(type) { - case *ir.CallExpr: - callX = x.X - callArgs = x.Args - keepAlive = x.KeepAlive - mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node { - newcall := ir.NewCallExpr(pos, op, fun, args) - newcall.IsDDD = x.IsDDD - return ir.Node(newcall) - } - case *ir.UnaryExpr: // ex: OCLOSE - callArgs = []ir.Node{x.X} - mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node { - if len(args) != 1 { - panic("internal error, expecting single arg") - } - return ir.Node(ir.NewUnaryExpr(pos, op, args[0])) - } - case *ir.BinaryExpr: // ex: OCOPY - callArgs = []ir.Node{x.X, x.Y} - mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node { - if len(args) != 2 { - panic("internal error, expecting two args") - } - return ir.Node(ir.NewBinaryExpr(pos, op, args[0], args[1])) - } - default: - panic("unhandled op") - } - - // No need to wrap if called func has no args, no receiver, and no results. - // However in the case of "defer func() { ... }()" we need to - // protect against the possibility of directClosureCall rewriting - // things so that the call does have arguments. - // - // Do wrap method calls (OCALLMETH, OCALLINTER), because it has - // a receiver. - // - // Also do wrap builtin functions, because they may be expanded to - // calls with arguments (e.g. ORECOVER). - // - // TODO: maybe not wrap if the called function has no arguments and - // only in-register results? - if len(callArgs) == 0 && call.Op() == ir.OCALLFUNC && callX.Type().NumResults() == 0 { - if c, ok := call.(*ir.CallExpr); ok && callX != nil && callX.Op() == ir.OCLOSURE { - cloFunc := callX.(*ir.ClosureExpr).Func - cloFunc.SetClosureCalled(false) - c.PreserveClosure = true - } - return - } - - if c, ok := call.(*ir.CallExpr); ok { - // To simplify things, turn f(a, b, []T{c, d, e}...) back - // into f(a, b, c, d, e) -- when the final call is run through the - // type checker below, it will rebuild the proper slice literal. - undoVariadic(c) - callX = c.X - callArgs = c.Args - } - - // This is set to true if the closure we're generating escapes - // (needs heap allocation). - cloEscapes := func() bool { - if n.Op() == ir.OGO { - // For "go", assume that all closures escape. - return true - } - // For defer, just use whatever result escape analysis - // has determined for the defer. - return n.Esc() != ir.EscNever - }() - - // A helper for making a copy of an argument. Note that it is - // not safe to use o.copyExpr(arg) if we're putting a - // reference to the temp into the closure (as opposed to - // copying it in by value), since in the by-reference case we - // need a temporary whose lifetime extends to the end of the - // function (as opposed to being local to the current block or - // statement being ordered). - mkArgCopy := func(arg ir.Node) *ir.Name { - t := arg.Type() - byval := t.Size() <= 128 || cloEscapes - var argCopy *ir.Name - if byval { - argCopy = o.copyExpr(arg) - } else { - argCopy = typecheck.Temp(t) - o.append(ir.NewAssignStmt(base.Pos, argCopy, arg)) - } - // The value of 128 below is meant to be consistent with code - // in escape analysis that picks byval/byaddr based on size. - argCopy.SetByval(byval) - return argCopy - } - - // getUnsafeArg looks for an unsafe.Pointer arg that has been - // previously captured into the call's keepalive list, returning - // the name node for it if found. - getUnsafeArg := func(arg ir.Node) *ir.Name { - // Look for uintptr(unsafe.Pointer(name)) - if arg.Op() != ir.OCONVNOP { - return nil - } - if !arg.Type().IsUintptr() { - return nil - } - if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() { - return nil - } - arg = arg.(*ir.ConvExpr).X - argname, ok := arg.(*ir.Name) - if !ok { - return nil - } - for i := range keepAlive { - if argname == keepAlive[i] { - return argname - } - } - return nil - } - - // Copy the arguments to the function into temps. - // - // For calls with uintptr(unsafe.Pointer(...)) args that are being - // kept alive (see code in (*orderState).call that does this), use - // the existing arg copy instead of creating a new copy. - unsafeArgs := make([]*ir.Name, len(callArgs)) - origArgs := callArgs - var newNames []*ir.Name - for i := range callArgs { - arg := callArgs[i] - var argname *ir.Name - unsafeArgName := getUnsafeArg(arg) - if unsafeArgName != nil { - // arg has been copied already, use keepalive copy - argname = unsafeArgName - unsafeArgs[i] = unsafeArgName - } else { - argname = mkArgCopy(arg) - } - newNames = append(newNames, argname) - } - - // Deal with cases where the function expression (what we're - // calling) is not a simple function symbol. - var fnExpr *ir.Name - var methSelectorExpr *ir.SelectorExpr - if callX != nil { - switch { - case callX.Op() == ir.ODOTMETH || callX.Op() == ir.ODOTINTER: - // Handle defer of a method call, e.g. "defer v.MyMethod(x, y)" - n := callX.(*ir.SelectorExpr) - n.X = mkArgCopy(n.X) - methSelectorExpr = n - if callX.Op() == ir.ODOTINTER { - // Currently for "defer i.M()" if i is nil it panics at the - // point of defer statement, not when deferred function is called. - // (I think there is an issue discussing what is the intended - // behavior but I cannot find it.) - // We need to do the nil check outside of the wrapper. - tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X)) - c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, tab) - c.SetTypecheck(1) - o.append(c) - } - case !(callX.Op() == ir.ONAME && callX.(*ir.Name).Class == ir.PFUNC): - // Deal with "defer returnsafunc()(x, y)" (for - // example) by copying the callee expression. - fnExpr = mkArgCopy(callX) - if callX.Op() == ir.OCLOSURE { - // For "defer func(...)", in addition to copying the - // closure into a temp, mark it as no longer directly - // called. - callX.(*ir.ClosureExpr).Func.SetClosureCalled(false) - } - } - } - - // Create a new no-argument function that we'll hand off to defer. - var noFuncArgs []*ir.Field - noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil) - wrapGoDefer_prgen++ - outerfn := ir.CurFunc - wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen) - sym := types.LocalPkg.Lookup(wrapname) - fn := typecheck.DeclFunc(sym, noargst) - fn.SetIsHiddenClosure(true) - fn.SetWrapper(true) - - // helper for capturing reference to a var declared in an outer scope. - capName := func(pos src.XPos, fn *ir.Func, n *ir.Name) *ir.Name { - t := n.Type() - cv := ir.CaptureName(pos, fn, n) - cv.SetType(t) - return typecheck.Expr(cv).(*ir.Name) - } - - // Call args (x1, y1) need to be captured as part of the newly - // created closure. - newCallArgs := []ir.Node{} - for i := range newNames { - var arg ir.Node - arg = capName(callArgs[i].Pos(), fn, newNames[i]) - if unsafeArgs[i] != nil { - arg = ir.NewConvExpr(arg.Pos(), origArgs[i].Op(), origArgs[i].Type(), arg) - } - newCallArgs = append(newCallArgs, arg) - } - // Also capture the function or method expression (if needed) into - // the closure. - if fnExpr != nil { - callX = capName(callX.Pos(), fn, fnExpr) - } - if methSelectorExpr != nil { - methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name)) - } - ir.FinishCaptureNames(n.Pos(), outerfn, fn) - - // This flags a builtin as opposed to a regular call. - irregular := (call.Op() != ir.OCALLFUNC && - call.Op() != ir.OCALLMETH && - call.Op() != ir.OCALLINTER) - - // Construct new function body: f(x1, y1) - op := ir.OCALL - if irregular { - op = call.Op() - } - newcall := mkNewCall(call.Pos(), op, callX, newCallArgs) - - // Type-check the result. - if !irregular { - typecheck.Call(newcall.(*ir.CallExpr)) - } else { - typecheck.Stmt(newcall) - } - - // Finalize body, register function on the main decls list. - fn.Body = []ir.Node{newcall} - typecheck.FinishFuncBody() - typecheck.Func(fn) - typecheck.Target.Decls = append(typecheck.Target.Decls, fn) - - // Create closure expr - clo := ir.NewClosureExpr(n.Pos(), fn) - fn.OClosure = clo - clo.SetType(fn.Type()) - - // Set escape properties for closure. - if n.Op() == ir.OGO { - // For "go", assume that the closure is going to escape - // (with an exception for the runtime, which doesn't - // permit heap-allocated closures). - if base.Ctxt.Pkgpath != "runtime" { - clo.SetEsc(ir.EscHeap) - } - } else { - // For defer, just use whatever result escape analysis - // has determined for the defer. - if n.Esc() == ir.EscNever { - clo.SetTransient(true) - clo.SetEsc(ir.EscNone) - } - } - - // Create new top level call to closure over argless function. - topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{}) - typecheck.Call(topcall) - - // Tag the call to insure that directClosureCall doesn't undo our work. - topcall.PreserveClosure = true - - fn.SetClosureCalled(false) - - // Finally, point the defer statement at the newly generated call. - n.Call = topcall -} - // isFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions. func isFuncPCIntrinsic(n *ir.CallExpr) bool { if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME { diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index b1169fdae8a1ab..f2591c362af910 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -38,11 +38,7 @@ func cheapComputableIndex(width int64) bool { // the returned node. func walkRange(nrange *ir.RangeStmt) ir.Node { if isMapClear(nrange) { - m := nrange.X - lno := ir.SetPos(m) - n := mapClear(m) - base.Pos = lno - return n + return mapClear(nrange) } nfor := ir.NewForStmt(nrange.Pos(), nil, nil, nil, nil) @@ -57,7 +53,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // a, v1, v2: not hidden aggregate, val 1, 2 a := nrange.X - t := typecheck.RangeExprType(a.Type()) + t := a.Type() lno := ir.SetPos(a) v1, v2 := nrange.Key, nrange.Value @@ -74,20 +70,27 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { base.Fatalf("walkRange: v2 != nil while v1 == nil") } - var ifGuard *ir.IfStmt - var body []ir.Node var init []ir.Node switch t.Kind() { default: base.Fatalf("walkRange") - case types.TARRAY, types.TSLICE: + case types.TARRAY, types.TSLICE, types.TPTR: // TPTR is pointer-to-array if nn := arrayClear(nrange, v1, v2, a); nn != nil { base.Pos = lno return nn } + // Element type of the iteration + var elem *types.Type + switch t.Kind() { + case types.TSLICE, types.TARRAY: + elem = t.Elem() + case types.TPTR: + elem = t.Elem().Elem() + } + // order.stmt arranged for a copy of the array/slice variable if needed. ha := a @@ -107,55 +110,53 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // for v1 := range ha { body } if v2 == nil { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)} + body = []ir.Node{rangeAssign(nrange, hv1)} break } // for v1, v2 := range ha { body } - if cheapComputableIndex(t.Elem().Width) { + if cheapComputableIndex(elem.Size()) { // v1, v2 = hv1, ha[hv1] tmp := ir.NewIndexExpr(base.Pos, ha, hv1) tmp.SetBounded(true) - // Use OAS2 to correctly handle assignments - // of the form "v1, a[v1] := range". - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, tmp}) - body = []ir.Node{a} + body = []ir.Node{rangeAssign2(nrange, hv1, tmp)} break } - // TODO(austin): OFORUNTIL is a strange beast, but is - // necessary for expressing the control flow we need - // while also making "break" and "continue" work. It - // would be nice to just lower ORANGE during SSA, but - // racewalk needs to see many of the operations - // involved in ORANGE's implementation. If racewalk - // moves into SSA, consider moving ORANGE into SSA and - // eliminating OFORUNTIL. - - // TODO(austin): OFORUNTIL inhibits bounds-check - // elimination on the index variable (see #20711). - // Enhance the prove pass to understand this. - ifGuard = ir.NewIfStmt(base.Pos, nil, nil, nil) - ifGuard.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv1, hn) - nfor.SetOp(ir.OFORUNTIL) - - hp := typecheck.Temp(types.NewPtr(t.Elem())) - tmp := ir.NewIndexExpr(base.Pos, ha, ir.NewInt(0)) - tmp.SetBounded(true) - init = append(init, ir.NewAssignStmt(base.Pos, hp, typecheck.NodAddr(tmp))) - - // Use OAS2 to correctly handle assignments - // of the form "v1, a[v1] := range". - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1, ir.NewStarExpr(base.Pos, hp)}) + // Slice to iterate over + var hs ir.Node + if t.IsSlice() { + hs = ha + } else { + var arr ir.Node + if t.IsPtr() { + arr = ha + } else { + arr = typecheck.NodAddr(ha) + arr.SetType(t.PtrTo()) + arr.SetTypecheck(1) + } + hs = ir.NewSliceExpr(base.Pos, ir.OSLICEARR, arr, nil, nil, nil) + // old typechecker doesn't know OSLICEARR, so we set types explicitly + hs.SetType(types.NewSlice(elem)) + hs.SetTypecheck(1) + } + + // Pointer to current iteration position + hp := typecheck.Temp(types.NewPtr(elem)) + init = append(init, ir.NewAssignStmt(base.Pos, hp, ir.NewUnaryExpr(base.Pos, ir.OSPTR, hs))) + + a := rangeAssign2(nrange, hv1, ir.NewStarExpr(base.Pos, hp)) body = append(body, a) - // Advance pointer as part of the late increment. - // - // This runs *after* the condition check, so we know - // advancing the pointer is safe and won't go past the - // end of the allocation. - as := ir.NewAssignStmt(base.Pos, hp, addptr(hp, t.Elem().Width)) - nfor.Late = []ir.Node{typecheck.Stmt(as)} + // Advance pointer for next iteration of the loop. + // Note: this pointer is now potentially a past-the-end pointer, so + // we need to make sure this pointer is never seen by the GC except + // during a conservative scan. Fortunately, the next thing we're going + // to do is check the loop bounds and exit, so it doesn't live very long + // (in particular, it doesn't live across any function call). + as := ir.NewAssignStmt(base.Pos, hp, addptr(hp, elem.Size())) + nfor.Post = ir.NewBlockStmt(base.Pos, []ir.Node{nfor.Post, as}) case types.TMAP: // order.stmt allocated the iterator for us. @@ -172,7 +173,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { fn := typecheck.LookupRuntime("mapiterinit") fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem(), th) - init = append(init, mkcallstmt1(fn, reflectdata.TypePtr(t), ha, typecheck.NodAddr(hit))) + init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit))) nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil()) fn = typecheck.LookupRuntime("mapiternext") @@ -183,11 +184,10 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 == nil { body = nil } else if v2 == nil { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, key)} + body = []ir.Node{rangeAssign(nrange, key)} } else { elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym)) - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{key, elem}) - body = []ir.Node{a} + body = []ir.Node{rangeAssign2(nrange, key, elem)} } case types.TCHAN: @@ -210,7 +210,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 == nil { body = nil } else { - body = []ir.Node{ir.NewAssignStmt(base.Pos, v1, hv1)} + body = []ir.Node{rangeAssign(nrange, hv1)} } // Zero hv1. This prevents hv1 from being the sole, inaccessible // reference to an otherwise GC-able value during the next channel receive. @@ -275,23 +275,17 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { if v1 != nil { if v2 != nil { // v1, v2 = hv1t, hv2 - a := ir.NewAssignListStmt(base.Pos, ir.OAS2, []ir.Node{v1, v2}, []ir.Node{hv1t, hv2}) - body = append(body, a) + body = append(body, rangeAssign2(nrange, hv1t, hv2)) } else { // v1 = hv1t - body = append(body, ir.NewAssignStmt(base.Pos, v1, hv1t)) + body = append(body, rangeAssign(nrange, hv1t)) } } } typecheck.Stmts(init) - if ifGuard != nil { - ifGuard.PtrInit().Append(init...) - ifGuard = typecheck.Stmt(ifGuard).(*ir.IfStmt) - } else { - nfor.PtrInit().Append(init...) - } + nfor.PtrInit().Append(init...) typecheck.Stmts(nfor.Cond.Init()) @@ -303,10 +297,6 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { nfor.Body.Append(nrange.Body...) var n ir.Node = nfor - if ifGuard != nil { - ifGuard.Body = []ir.Node{n} - n = ifGuard - } n = walkStmt(n) @@ -314,11 +304,41 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { return n } +// rangeAssign returns "n.Key = key". +func rangeAssign(n *ir.RangeStmt, key ir.Node) ir.Node { + key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType) + return ir.NewAssignStmt(n.Pos(), n.Key, key) +} + +// rangeAssign2 returns "n.Key, n.Value = key, value". +func rangeAssign2(n *ir.RangeStmt, key, value ir.Node) ir.Node { + // Use OAS2 to correctly handle assignments + // of the form "v1, a[v1] = range". + key = rangeConvert(n, n.Key.Type(), key, n.KeyTypeWord, n.KeySrcRType) + value = rangeConvert(n, n.Value.Type(), value, n.ValueTypeWord, n.ValueSrcRType) + return ir.NewAssignListStmt(n.Pos(), ir.OAS2, []ir.Node{n.Key, n.Value}, []ir.Node{key, value}) +} + +// rangeConvert returns src, converted to dst if necessary. If a +// conversion is necessary, then typeWord and srcRType are copied to +// their respective ConvExpr fields. +func rangeConvert(nrange *ir.RangeStmt, dst *types.Type, src, typeWord, srcRType ir.Node) ir.Node { + src = typecheck.Expr(src) + if dst.Kind() == types.TBLANK || types.Identical(dst, src.Type()) { + return src + } + + n := ir.NewConvExpr(nrange.Pos(), ir.OCONV, dst, src) + n.TypeWord = typeWord + n.SrcRType = srcRType + return typecheck.Expr(n) +} + // isMapClear checks if n is of the form: // -// for k := range m { -// delete(m, k) -// } +// for k := range m { +// delete(m, k) +// } // // where == for keys of map m is reflexive. func isMapClear(n *ir.RangeStmt) bool { @@ -360,13 +380,17 @@ func isMapClear(n *ir.RangeStmt) bool { } // mapClear constructs a call to runtime.mapclear for the map m. -func mapClear(m ir.Node) ir.Node { +func mapClear(nrange *ir.RangeStmt) ir.Node { + m := nrange.X + origPos := ir.SetPos(m) + defer func() { base.Pos = origPos }() + t := m.Type() // instantiate mapclear(typ *type, hmap map[any]any) fn := typecheck.LookupRuntime("mapclear") fn = typecheck.SubstArgTypes(fn, t.Key(), t.Elem()) - n := mkcallstmt1(fn, reflectdata.TypePtr(t), m) + n := mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), m) return walkStmt(typecheck.Stmt(n)) } @@ -374,9 +398,9 @@ func mapClear(m ir.Node) ir.Node { // fast zeroing of slices and arrays (issue 5373). // Look for instances of // -// for i := range a { -// a[i] = zero -// } +// for i := range a { +// a[i] = zero +// } // // in which the evaluation of a is side-effect-free. // @@ -403,12 +427,18 @@ func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node { return nil } lhs := stmt.X.(*ir.IndexExpr) + x := lhs.X + if a.Type().IsPtr() && a.Type().Elem().IsArray() { + if s, ok := x.(*ir.StarExpr); ok && s.Op() == ir.ODEREF { + x = s.X + } + } - if !ir.SameSafeExpr(lhs.X, a) || !ir.SameSafeExpr(lhs.Index, v1) { + if !ir.SameSafeExpr(x, a) || !ir.SameSafeExpr(lhs.Index, v1) { return nil } - elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Width + elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Size() if elemsize <= 0 || !ir.IsZero(stmt.Y) { return nil } diff --git a/src/cmd/compile/internal/walk/select.go b/src/cmd/compile/internal/walk/select.go index d2b67ddf55a7ab..570e9b54ab23e7 100644 --- a/src/cmd/compile/internal/walk/select.go +++ b/src/cmd/compile/internal/walk/select.go @@ -105,7 +105,7 @@ func walkSelectCases(cases []*ir.CommClause) []ir.Node { n := cas.Comm ir.SetPos(n) r := ir.NewIfStmt(base.Pos, nil, nil, nil) - *r.PtrInit() = cas.Init() + r.SetInit(cas.Init()) var cond ir.Node switch n.Op() { default: @@ -230,30 +230,32 @@ func walkSelectCases(cases []*ir.CommClause) []ir.Node { init = append(init, fnInit...) init = append(init, typecheck.Stmt(r)) - // selv and order are no longer alive after selectgo. - init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, selv)) - init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, order)) - if base.Flag.Race { - init = append(init, ir.NewUnaryExpr(base.Pos, ir.OVARKILL, pcs)) - } + // selv, order, and pcs (if race) are no longer alive after selectgo. // dispatch cases dispatch := func(cond ir.Node, cas *ir.CommClause) { - cond = typecheck.Expr(cond) - cond = typecheck.DefaultLit(cond, nil) - - r := ir.NewIfStmt(base.Pos, cond, nil, nil) + var list ir.Nodes if n := cas.Comm; n != nil && n.Op() == ir.OSELRECV2 { n := n.(*ir.AssignListStmt) if !ir.IsBlank(n.Lhs[1]) { x := ir.NewAssignStmt(base.Pos, n.Lhs[1], recvOK) - r.Body.Append(typecheck.Stmt(x)) + list.Append(typecheck.Stmt(x)) } } - r.Body.Append(cas.Body.Take()...) - r.Body.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)) + list.Append(cas.Body.Take()...) + list.Append(ir.NewBranchStmt(base.Pos, ir.OBREAK, nil)) + + var r ir.Node + if cond != nil { + cond = typecheck.Expr(cond) + cond = typecheck.DefaultLit(cond, nil) + r = ir.NewIfStmt(base.Pos, cond, list, nil) + } else { + r = ir.NewBlockStmt(base.Pos, list) + } + init = append(init, r) } @@ -263,6 +265,10 @@ func walkSelectCases(cases []*ir.CommClause) []ir.Node { } for i, cas := range casorder { ir.SetPos(cas) + if i == len(casorder)-1 { + dispatch(nil, cas) + break + } dispatch(ir.NewBinaryExpr(base.Pos, ir.OEQ, chosen, ir.NewInt(int64(i))), cas) } diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go index 0bf76680c46265..ceee1b1b75ff66 100644 --- a/src/cmd/compile/internal/walk/stmt.go +++ b/src/cmd/compile/internal/walk/stmt.go @@ -7,11 +7,11 @@ package walk import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" - "cmd/compile/internal/typecheck" ) // The result of walkStmt MUST be assigned back to n, e.g. -// n.Left = walkStmt(n.Left) +// +// n.Left = walkStmt(n.Left) func walkStmt(n ir.Node) ir.Node { if n == nil { return n @@ -41,7 +41,6 @@ func walkStmt(n ir.Node) ir.Node { ir.OAS2MAPR, ir.OCLOSE, ir.OCOPY, - ir.OCALLMETH, ir.OCALLINTER, ir.OCALL, ir.OCALLFUNC, @@ -50,7 +49,7 @@ func walkStmt(n ir.Node) ir.Node { ir.OPRINT, ir.OPRINTN, ir.OPANIC, - ir.ORECOVER, + ir.ORECOVERFP, ir.OGETG: if n.Typecheck() == 0 { base.Fatalf("missing typecheck: %+v", n) @@ -86,13 +85,11 @@ func walkStmt(n ir.Node) ir.Node { ir.OFALL, ir.OGOTO, ir.OLABEL, + ir.OJUMPTABLE, ir.ODCL, ir.ODCLCONST, ir.ODCLTYPE, - ir.OCHECKNIL, - ir.OVARDEF, - ir.OVARKILL, - ir.OVARLIVE: + ir.OCHECKNIL: return n case ir.OBLOCK: @@ -124,7 +121,7 @@ func walkStmt(n ir.Node) ir.Node { n := n.(*ir.GoDeferStmt) return walkGoDefer(n) - case ir.OFOR, ir.OFORUNTIL: + case ir.OFOR: n := n.(*ir.ForStmt) return walkFor(n) @@ -138,6 +135,14 @@ func walkStmt(n ir.Node) ir.Node { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) + + var init ir.Nodes + n.Call.X = walkExpr(n.Call.X, &init) + + if len(init) > 0 { + init.Append(n) + return ir.NewBlockStmt(n.Pos(), init) + } return n case ir.OINLMARK: @@ -170,7 +175,7 @@ func walkStmtList(s []ir.Node) { } } -// walkFor walks an OFOR or OFORUNTIL node. +// walkFor walks an OFOR node. func walkFor(n *ir.ForStmt) ir.Node { if n.Cond != nil { init := ir.TakeInit(n.Cond) @@ -180,40 +185,32 @@ func walkFor(n *ir.ForStmt) ir.Node { } n.Post = walkStmt(n.Post) - if n.Op() == ir.OFORUNTIL { - walkStmtList(n.Late) - } walkStmtList(n.Body) return n } +// validGoDeferCall reports whether call is a valid call to appear in +// a go or defer statement; that is, whether it's a regular function +// call without arguments or results. +func validGoDeferCall(call ir.Node) bool { + if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 { + sig := call.X.Type() + return sig.NumParams()+sig.NumResults() == 0 + } + return false +} + // walkGoDefer walks an OGO or ODEFER node. func walkGoDefer(n *ir.GoDeferStmt) ir.Node { + if !validGoDeferCall(n.Call) { + base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call) + } + var init ir.Nodes - switch call := n.Call; call.Op() { - case ir.OPRINT, ir.OPRINTN: - call := call.(*ir.CallExpr) - n.Call = wrapCall(call, &init) - - case ir.ODELETE: - call := call.(*ir.CallExpr) - n.Call = wrapCall(call, &init) - - case ir.OCOPY: - call := call.(*ir.BinaryExpr) - n.Call = walkCopy(call, &init, true) - - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: - call := call.(*ir.CallExpr) - if len(call.KeepAlive) > 0 { - n.Call = wrapCall(call, &init) - } else { - n.Call = walkExpr(call, &init) - } - default: - n.Call = walkExpr(call, &init) - } + call := n.Call.(*ir.CallExpr) + call.X = walkExpr(call.X, &init) + if len(init) > 0 { init.Append(n) return ir.NewBlockStmt(n.Pos(), init) @@ -228,110 +225,3 @@ func walkIf(n *ir.IfStmt) ir.Node { walkStmtList(n.Else) return n } - -// Rewrite -// go builtin(x, y, z) -// into -// go func(a1, a2, a3) { -// builtin(a1, a2, a3) -// }(x, y, z) -// for print, println, and delete. -// -// Rewrite -// go f(x, y, uintptr(unsafe.Pointer(z))) -// into -// go func(a1, a2, a3) { -// f(a1, a2, uintptr(a3)) -// }(x, y, unsafe.Pointer(z)) -// for function contains unsafe-uintptr arguments. - -var wrapCall_prgen int - -// The result of wrapCall MUST be assigned back to n, e.g. -// n.Left = wrapCall(n.Left, init) -func wrapCall(n *ir.CallExpr, init *ir.Nodes) ir.Node { - if len(n.Init()) != 0 { - walkStmtList(n.Init()) - init.Append(ir.TakeInit(n)...) - } - - isBuiltinCall := n.Op() != ir.OCALLFUNC && n.Op() != ir.OCALLMETH && n.Op() != ir.OCALLINTER - - // Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e). - if !isBuiltinCall && n.IsDDD { - undoVariadic(n) - } - - wrapArgs := n.Args - // If there's a receiver argument, it needs to be passed through the wrapper too. - if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER { - recv := n.X.(*ir.SelectorExpr).X - wrapArgs = append([]ir.Node{recv}, wrapArgs...) - } - - // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion. - origArgs := make([]ir.Node, len(wrapArgs)) - var funcArgs []*ir.Field - for i, arg := range wrapArgs { - s := typecheck.LookupNum("a", i) - if !isBuiltinCall && arg.Op() == ir.OCONVNOP && arg.Type().IsUintptr() && arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() { - origArgs[i] = arg - arg = arg.(*ir.ConvExpr).X - wrapArgs[i] = arg - } - funcArgs = append(funcArgs, ir.NewField(base.Pos, s, nil, arg.Type())) - } - t := ir.NewFuncType(base.Pos, nil, funcArgs, nil) - - wrapCall_prgen++ - sym := typecheck.LookupNum("wrap·", wrapCall_prgen) - fn := typecheck.DeclFunc(sym, t) - - args := ir.ParamNames(t.Type()) - for i, origArg := range origArgs { - if origArg == nil { - continue - } - args[i] = ir.NewConvExpr(base.Pos, origArg.Op(), origArg.Type(), args[i]) - } - if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER { - // Move wrapped receiver argument back to its appropriate place. - recv := typecheck.Expr(args[0]) - n.X.(*ir.SelectorExpr).X = recv - args = args[1:] - } - call := ir.NewCallExpr(base.Pos, n.Op(), n.X, args) - if !isBuiltinCall { - call.SetOp(ir.OCALL) - call.IsDDD = n.IsDDD - } - fn.Body = []ir.Node{call} - - typecheck.FinishFuncBody() - - typecheck.Func(fn) - typecheck.Stmts(fn.Body) - typecheck.Target.Decls = append(typecheck.Target.Decls, fn) - - call = ir.NewCallExpr(base.Pos, ir.OCALL, fn.Nname, wrapArgs) - return walkExpr(typecheck.Stmt(call), init) -} - -// undoVariadic turns a call to a variadic function of the form -// -// f(a, b, []T{c, d, e}...) -// -// back into -// -// f(a, b, c, d, e) -// -func undoVariadic(call *ir.CallExpr) { - if call.IsDDD { - last := len(call.Args) - 1 - if va := call.Args[last]; va.Op() == ir.OSLICELIT { - va := va.(*ir.CompLitExpr) - call.Args = append(call.Args[:last], va.List...) - call.IsDDD = false - } - } -} diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go index 162de018f637ef..d38ba500f2f3ce 100644 --- a/src/cmd/compile/internal/walk/switch.go +++ b/src/cmd/compile/internal/walk/switch.go @@ -11,6 +11,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/ssagen" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/src" @@ -66,6 +67,7 @@ func walkSwitchExpr(sw *ir.SwitchStmt) { base.Pos = lno s := exprSwitch{ + pos: lno, exprname: cond, } @@ -83,8 +85,12 @@ func walkSwitchExpr(sw *ir.SwitchStmt) { defaultGoto = jmp } - for _, n1 := range ncase.List { - s.Add(ncase.Pos(), n1, jmp) + for i, n1 := range ncase.List { + var rtype ir.Node + if i < len(ncase.RTypes) { + rtype = ncase.RTypes[i] + } + s.Add(ncase.Pos(), n1, rtype, jmp) } // Process body. @@ -112,6 +118,7 @@ func walkSwitchExpr(sw *ir.SwitchStmt) { // An exprSwitch walks an expression switch. type exprSwitch struct { + pos src.XPos exprname ir.Node // value being switched on done ir.Nodes @@ -121,11 +128,12 @@ type exprSwitch struct { type exprClause struct { pos src.XPos lo, hi ir.Node + rtype ir.Node // *runtime._type for OEQ node jmp ir.Node } -func (s *exprSwitch) Add(pos src.XPos, expr, jmp ir.Node) { - c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp} +func (s *exprSwitch) Add(pos src.XPos, expr, rtype, jmp ir.Node) { + c := exprClause{pos: pos, lo: expr, hi: expr, rtype: rtype, jmp: jmp} if types.IsOrdered[s.exprname.Type().Kind()] && expr.Op() == ir.OLITERAL { s.clauses = append(s.clauses, c) return @@ -182,17 +190,55 @@ func (s *exprSwitch) flush() { } runs = append(runs, cc[start:]) - // Perform two-level binary search. - binarySearch(len(runs), &s.done, - func(i int) ir.Node { - return ir.NewBinaryExpr(base.Pos, ir.OLE, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(runs[i-1]))) - }, - func(i int, nif *ir.IfStmt) { - run := runs[i] - nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(run))) - s.search(run, &nif.Body) - }, - ) + // We have strings of more than one length. Generate an + // outer switch which switches on the length of the string + // and an inner switch in each case which resolves all the + // strings of the same length. The code looks something like this: + + // goto outerLabel + // len5: + // ... search among length 5 strings ... + // goto endLabel + // len8: + // ... search among length 8 strings ... + // goto endLabel + // ... other lengths ... + // outerLabel: + // switch len(s) { + // case 5: goto len5 + // case 8: goto len8 + // ... other lengths ... + // } + // endLabel: + + outerLabel := typecheck.AutoLabel(".s") + endLabel := typecheck.AutoLabel(".s") + + // Jump around all the individual switches for each length. + s.done.Append(ir.NewBranchStmt(s.pos, ir.OGOTO, outerLabel)) + + var outer exprSwitch + outer.exprname = ir.NewUnaryExpr(s.pos, ir.OLEN, s.exprname) + outer.exprname.SetType(types.Types[types.TINT]) + + for _, run := range runs { + // Target label to jump to when we match this length. + label := typecheck.AutoLabel(".s") + + // Search within this run of same-length strings. + pos := run[0].pos + s.done.Append(ir.NewLabelStmt(pos, label)) + stringSearch(s.exprname, run, &s.done) + s.done.Append(ir.NewBranchStmt(pos, ir.OGOTO, endLabel)) + + // Add length case to outer switch. + cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run))) + jmp := ir.NewBranchStmt(pos, ir.OGOTO, label) + outer.Add(pos, cas, nil, jmp) + } + s.done.Append(ir.NewLabelStmt(s.pos, outerLabel)) + outer.Emit(&s.done) + s.done.Append(ir.NewLabelStmt(s.pos, endLabel)) return } @@ -223,6 +269,9 @@ func (s *exprSwitch) flush() { } func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) { + if s.tryJumpTable(cc, out) { + return + } binarySearch(len(cc), out, func(i int) ir.Node { return ir.NewBinaryExpr(base.Pos, ir.OLE, s.exprname, cc[i-1].hi) @@ -235,6 +284,48 @@ func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) { ) } +// Try to implement the clauses with a jump table. Returns true if successful. +func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool { + const go119UseJumpTables = true + const minCases = 8 // have at least minCases cases in the switch + const minDensity = 4 // use at least 1 out of every minDensity entries + + if !go119UseJumpTables || base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable { + return false + } + if len(cc) < minCases { + return false // not enough cases for it to be worth it + } + if cc[0].lo.Val().Kind() != constant.Int { + return false // e.g. float + } + if s.exprname.Type().Size() > int64(types.PtrSize) { + return false // 64-bit switches on 32-bit archs + } + min := cc[0].lo.Val() + max := cc[len(cc)-1].hi.Val() + width := constant.BinaryOp(constant.BinaryOp(max, token.SUB, min), token.ADD, constant.MakeInt64(1)) + limit := constant.MakeInt64(int64(len(cc)) * minDensity) + if constant.Compare(width, token.GTR, limit) { + // We disable jump tables if we use less than a minimum fraction of the entries. + // i.e. for switch x {case 0: case 1000: case 2000:} we don't want to use a jump table. + return false + } + jt := ir.NewJumpTableStmt(base.Pos, s.exprname) + for _, c := range cc { + jmp := c.jmp.(*ir.BranchStmt) + if jmp.Op() != ir.OGOTO || jmp.Label == nil { + panic("bad switch case body") + } + for i := c.lo.Val(); constant.Compare(i, token.LEQ, c.hi.Val()); i = constant.BinaryOp(i, token.ADD, constant.MakeInt64(1)) { + jt.Cases = append(jt.Cases, i) + jt.Targets = append(jt.Targets, jmp.Label) + } + } + out.Append(jt) + return true +} + func (c *exprClause) test(exprname ir.Node) ir.Node { // Integer range. if c.hi != c.lo { @@ -252,7 +343,9 @@ func (c *exprClause) test(exprname ir.Node) ir.Node { } } - return ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo) + n := ir.NewBinaryExpr(c.pos, ir.OEQ, exprname, c.lo) + n.RType = c.rtype + return n } func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool { @@ -275,19 +368,10 @@ func allCaseExprsAreSideEffectFree(sw *ir.SwitchStmt) bool { // endsInFallthrough reports whether stmts ends with a "fallthrough" statement. func endsInFallthrough(stmts []ir.Node) (bool, src.XPos) { - // Search backwards for the index of the fallthrough - // statement. Do not assume it'll be in the last - // position, since in some cases (e.g. when the statement - // list contains autotmp_ variables), one or more OVARKILL - // nodes will be at the end of the list. - - i := len(stmts) - 1 - for i >= 0 && stmts[i].Op() == ir.OVARKILL { - i-- - } - if i < 0 { + if len(stmts) == 0 { return false, src.NoXPos } + i := len(stmts) - 1 return stmts[i].Op() == ir.OFALL, stmts[i].Pos() } @@ -360,10 +444,10 @@ func walkSwitchType(sw *ir.SwitchStmt) { } if singleType != nil && singleType.IsInterface() { - s.Add(ncase.Pos(), n1.Type(), caseVar, jmp) + s.Add(ncase.Pos(), n1, caseVar, jmp) caseVarInitialized = true } else { - s.Add(ncase.Pos(), n1.Type(), nil, jmp) + s.Add(ncase.Pos(), n1, nil, jmp) } } @@ -377,6 +461,14 @@ func walkSwitchType(sw *ir.SwitchStmt) { } val = ifaceData(ncase.Pos(), s.facename, singleType) } + if len(ncase.List) == 1 && ncase.List[0].Op() == ir.ODYNAMICTYPE { + dt := ncase.List[0].(*ir.DynamicType) + x := ir.NewDynamicTypeAssertExpr(ncase.Pos(), ir.ODYNAMICDOTTYPE, val, dt.RType) + x.ITab = dt.ITab + x.SetType(caseVar.Type()) + x.SetTypecheck(1) + val = x + } l := []ir.Node{ ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar), ir.NewAssignStmt(ncase.Pos(), caseVar, val), @@ -446,7 +538,8 @@ type typeClause struct { body ir.Nodes } -func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir.Node) { +func (s *typeSwitch) Add(pos src.XPos, n1 ir.Node, caseVar *ir.Name, jmp ir.Node) { + typ := n1.Type() var body ir.Nodes if caseVar != nil { l := []ir.Node{ @@ -462,9 +555,22 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir // cv, ok = iface.(type) as := ir.NewAssignListStmt(pos, ir.OAS2, nil, nil) as.Lhs = []ir.Node{caseVar, s.okname} // cv, ok = - dot := ir.NewTypeAssertExpr(pos, s.facename, nil) - dot.SetType(typ) // iface.(type) - as.Rhs = []ir.Node{dot} + switch n1.Op() { + case ir.OTYPE: + // Static type assertion (non-generic) + dot := ir.NewTypeAssertExpr(pos, s.facename, typ) // iface.(type) + as.Rhs = []ir.Node{dot} + case ir.ODYNAMICTYPE: + // Dynamic type assertion (generic) + dt := n1.(*ir.DynamicType) + dot := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, s.facename, dt.RType) + dot.ITab = dt.ITab + dot.SetType(typ) + dot.SetTypecheck(1) + as.Rhs = []ir.Node{dot} + default: + base.Fatalf("unhandled type case %s", n1.Op()) + } appendWalkStmt(&body, as) // if ok { goto label } @@ -473,9 +579,10 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir nif.Body = []ir.Node{jmp} body.Append(nif) - if !typ.IsInterface() { + if n1.Op() == ir.OTYPE && !typ.IsInterface() { + // Defer static, noninterface cases so they can be binary searched by hash. s.clauses = append(s.clauses, typeClause{ - hash: types.TypeHash(typ), + hash: types.TypeHash(n1.Type()), body: body, }) return @@ -511,6 +618,7 @@ func (s *typeSwitch) flush() { } cc = merged + // TODO: figure out if we could use a jump table using some low bits of the type hashes. binarySearch(len(cc), &s.done, func(i int) ir.Node { return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashname, ir.NewInt(int64(cc[i-1].hash))) @@ -533,7 +641,7 @@ func (s *typeSwitch) flush() { // then cases before i will be tested; otherwise, cases i and later. // // leaf(i, nif) should setup nif (an OIF node) to test case i. In -// particular, it should set nif.Left and nif.Nbody. +// particular, it should set nif.Cond and nif.Body. func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) { const binarySearchMin = 4 // minimum number of cases for binary search @@ -566,3 +674,89 @@ func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i in do(0, n, out) } + +func stringSearch(expr ir.Node, cc []exprClause, out *ir.Nodes) { + if len(cc) < 4 { + // Short list, just do brute force equality checks. + for _, c := range cc { + nif := ir.NewIfStmt(base.Pos.WithNotStmt(), typecheck.DefaultLit(typecheck.Expr(c.test(expr)), nil), []ir.Node{c.jmp}, nil) + out.Append(nif) + out = &nif.Else + } + return + } + + // The strategy here is to find a simple test to divide the set of possible strings + // that might match expr approximately in half. + // The test we're going to use is to do an ordered comparison of a single byte + // of expr to a constant. We will pick the index of that byte and the value we're + // comparing against to make the split as even as possible. + // if expr[3] <= 'd' { ... search strings with expr[3] at 'd' or lower ... } + // else { ... search strings with expr[3] at 'e' or higher ... } + // + // To add complication, we will do the ordered comparison in the signed domain. + // The reason for this is to prevent CSE from merging the load used for the + // ordered comparison with the load used for the later equality check. + // if expr[3] <= 'd' { ... if expr[0] == 'f' && expr[1] == 'o' && expr[2] == 'o' && expr[3] == 'd' { ... } } + // If we did both expr[3] loads in the unsigned domain, they would be CSEd, and that + // would in turn defeat the combining of expr[0]...expr[3] into a single 4-byte load. + // See issue 48222. + // By using signed loads for the ordered comparison and unsigned loads for the + // equality comparison, they don't get CSEd and the equality comparisons will be + // done using wider loads. + + n := len(ir.StringVal(cc[0].lo)) // Length of the constant strings. + bestScore := int64(0) // measure of how good the split is. + bestIdx := 0 // split using expr[bestIdx] + bestByte := int8(0) // compare expr[bestIdx] against bestByte + for idx := 0; idx < n; idx++ { + for b := int8(-128); b < 127; b++ { + le := 0 + for _, c := range cc { + s := ir.StringVal(c.lo) + if int8(s[idx]) <= b { + le++ + } + } + score := int64(le) * int64(len(cc)-le) + if score > bestScore { + bestScore = score + bestIdx = idx + bestByte = b + } + } + } + + // The split must be at least 1:n-1 because we have at least 2 distinct strings; they + // have to be different somewhere. + // TODO: what if the best split is still pretty bad? + if bestScore == 0 { + base.Fatalf("unable to split string set") + } + + // Convert expr to a []int8 + slice := ir.NewConvExpr(base.Pos, ir.OSTR2BYTESTMP, types.NewSlice(types.Types[types.TINT8]), expr) + slice.SetTypecheck(1) // legacy typechecker doesn't handle this op + // Load the byte we're splitting on. + load := ir.NewIndexExpr(base.Pos, slice, ir.NewInt(int64(bestIdx))) + // Compare with the value we're splitting on. + cmp := ir.Node(ir.NewBinaryExpr(base.Pos, ir.OLE, load, ir.NewInt(int64(bestByte)))) + cmp = typecheck.DefaultLit(typecheck.Expr(cmp), nil) + nif := ir.NewIfStmt(base.Pos, cmp, nil, nil) + + var le []exprClause + var gt []exprClause + for _, c := range cc { + s := ir.StringVal(c.lo) + if int8(s[bestIdx]) <= bestByte { + le = append(le, c) + } else { + gt = append(gt, c) + } + } + stringSearch(expr, le, &nif.Body) + stringSearch(expr, gt, &nif.Else) + out.Append(nif) + + // TODO: if expr[bestIdx] has enough different possible values, use a jump table. +} diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go index 26da6e31457443..d6b09866f71e91 100644 --- a/src/cmd/compile/internal/walk/walk.go +++ b/src/cmd/compile/internal/walk/walk.go @@ -113,8 +113,7 @@ func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallEx base.Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va)) } - call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, va) - typecheck.Call(call) + call := typecheck.Call(base.Pos, fn, va, false).(*ir.CallExpr) call.SetType(t) return walkExpr(call, init).(*ir.CallExpr) } @@ -206,7 +205,7 @@ var mapdelete = mkmapnames("mapdelete", "") func mapfast(t *types.Type) int { // Check runtime/map.go:maxElemSize before changing. - if t.Elem().Width > 128 { + if t.Elem().Size() > 128 { return mapslow } switch reflectdata.AlgType(t.Key()) { @@ -308,12 +307,12 @@ func mayCall(n ir.Node) bool { default: base.FatalfAt(n.Pos(), "mayCall %+v", n) - case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, + case ir.OCALLFUNC, ir.OCALLINTER, ir.OUNSAFEADD, ir.OUNSAFESLICE: return true case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR, - ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR: + ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODYNAMICDOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR: // These ops might panic, make sure they are done // before we start marshaling args for a call. See issue 16760. return true @@ -343,7 +342,7 @@ func mayCall(n ir.Node) bool { ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL, ir.OCONVNOP, ir.ODOT, ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR, - ir.OBYTES2STRTMP, ir.OGETG, ir.OSLICEHEADER: + ir.OBYTES2STRTMP, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OSLICEHEADER, ir.OSTRINGHEADER: // ok: operations that don't require function calls. // Expand as needed. } diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go index 31b09016eb9829..765051c9445b29 100644 --- a/src/cmd/compile/internal/wasm/ssa.go +++ b/src/cmd/compile/internal/wasm/ssa.go @@ -24,7 +24,6 @@ func Init(arch *ssagen.ArchInfo) { arch.ZeroRange = zeroRange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = ssaMarkMoves arch.SSAGenValue = ssaGenValue @@ -89,13 +88,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockDefer: p := s.Prog(wasm.AGet) @@ -123,10 +116,14 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { func ssaGenValue(s *ssagen.State, v *ssa.Value) { switch v.Op { - case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall: + case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall, ssa.OpWasmLoweredTailCall: s.PrepareCall(v) if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn { - // add a resume point before call to deferreturn so it can be called again via jmpdefer + // The runtime needs to inject jumps to + // deferreturn calls using the address in + // _func.deferreturn. Hence, the call to + // deferreturn must itself be a resumption + // point so it gets a target PC. s.Prog(wasm.ARESUMEPOINT) } if v.Op == ssa.OpWasmLoweredClosureCall { @@ -138,6 +135,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p := s.Prog(obj.ACALL) p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym} p.Pos = v.Pos + if v.Op == ssa.OpWasmLoweredTailCall { + p.As = obj.ARET + } } else { getValue64(s, v.Args[0]) p := s.Prog(obj.ACALL) diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go index 00a20e429f1d61..5565bd32c765b3 100644 --- a/src/cmd/compile/internal/x86/galign.go +++ b/src/cmd/compile/internal/x86/galign.go @@ -34,7 +34,6 @@ func Init(arch *ssagen.ArchInfo) { arch.ZeroRange = zerorange arch.Ginsnop = ginsnop - arch.Ginsnopdefer = ginsnop arch.SSAMarkMoves = ssaMarkMoves } diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go index a06fdbcb71770e..90bb0b9c09e789 100644 --- a/src/cmd/compile/internal/x86/ssa.go +++ b/src/cmd/compile/internal/x86/ssa.go @@ -106,7 +106,9 @@ func moveByType(t *types.Type) obj.As { } // opregreg emits instructions for -// dest := dest(To) op src(From) +// +// dest := dest(To) op src(From) +// // and also returns the created obj.Prog so it // may be further adjusted (offset, scale, etc). func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog { @@ -156,6 +158,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.Op386SHLL, ssa.Op386SHRL, ssa.Op386SHRW, ssa.Op386SHRB, ssa.Op386SARL, ssa.Op386SARW, ssa.Op386SARB, + ssa.Op386ROLL, ssa.Op386ROLW, ssa.Op386ROLB, ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD, ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD, ssa.Op386PXOR, @@ -725,7 +728,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // caller's SP is the address of the first arg p := s.Prog(x86.AMOVL) p.From.Type = obj.TYPE_ADDR - p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on 386, just to be consistent with other architectures + p.From.Offset = -base.Ctxt.Arch.FixedFrameSize // 0 on 386, just to be consistent with other architectures p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() @@ -752,6 +755,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter: s.Call(v) + case ssa.Op386CALLtail: + s.TailCall(v) case ssa.Op386NEGL, ssa.Op386BSWAPL, ssa.Op386NOTL: @@ -892,14 +897,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.AJMP) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.Block386EQF: s.CombJump(b, next, &eqfJumps) diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go index 3af1e1fafdfef2..7d38bea7fa27a8 100644 --- a/src/cmd/compile/main.go +++ b/src/cmd/compile/main.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/arm64" "cmd/compile/internal/base" "cmd/compile/internal/gc" + "cmd/compile/internal/loong64" "cmd/compile/internal/mips" "cmd/compile/internal/mips64" "cmd/compile/internal/ppc64" @@ -29,6 +30,7 @@ var archInits = map[string]func(*ssagen.ArchInfo){ "amd64": amd64.Init, "arm": arm.Init, "arm64": arm64.Init, + "loong64": loong64.Init, "mips": mips.Init, "mipsle": mips.Init, "mips64": mips64.Init, diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go index 7ee000861bcc2a..86ef128f2cccb1 100644 --- a/src/cmd/cover/cover.go +++ b/src/cmd/cover/cover.go @@ -40,8 +40,8 @@ Finally, to generate modified source code with coverage annotations ` func usage() { - fmt.Fprintln(os.Stderr, usageMessage) - fmt.Fprintln(os.Stderr, "Flags:") + fmt.Fprint(os.Stderr, usageMessage) + fmt.Fprintln(os.Stderr, "\nFlags:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.") os.Exit(2) @@ -377,7 +377,7 @@ func (f *File) newCounter(start, end token.Pos, numStmt int) string { // S1 // if cond { // S2 -// } +// } // S3 // // counters will be added before S1 and before S3. The block containing S2 diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go index 86c95d15c5d28f..d9d63e4587f0be 100644 --- a/src/cmd/cover/cover_test.go +++ b/src/cmd/cover/cover_test.go @@ -162,10 +162,9 @@ func buildCover(t *testing.T) { // Run this shell script, but do it in Go so it can be run by "go test". // // replace the word LINE with the line number < testdata/test.go > testdata/test_line.go -// go build -o testcover -// testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go +// go build -o testcover +// testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go // go run ./testdata/main.go ./testdata/test.go -// func TestCover(t *testing.T) { t.Parallel() testenv.MustHaveGoRun(t) @@ -401,7 +400,7 @@ func TestCoverHTML(t *testing.T) { if err != nil { t.Fatal(err) } - var out bytes.Buffer + var out strings.Builder scan := bufio.NewScanner(bytes.NewReader(entireHTML)) in := false for scan.Scan() { diff --git a/src/cmd/cover/doc.go b/src/cmd/cover/doc.go index e2c849419abc88..e091ce9e30084d 100644 --- a/src/cmd/cover/doc.go +++ b/src/cmd/cover/doc.go @@ -19,6 +19,7 @@ must be applied to the output of cgo preprocessing, not the input, because cover deletes comments that are significant to cgo. For usage information, please see: + go help testflag go tool cover -help */ diff --git a/src/cmd/cover/func.go b/src/cmd/cover/func.go index 76a16b3fc4a11e..dffd3c1a0553ac 100644 --- a/src/cmd/cover/func.go +++ b/src/cmd/cover/func.go @@ -15,9 +15,9 @@ import ( "go/ast" "go/parser" "go/token" - exec "internal/execabs" "io" "os" + "os/exec" "path" "path/filepath" "runtime" diff --git a/src/cmd/cover/html.go b/src/cmd/cover/html.go index 3c1d17e7b956af..400a7d879d0f65 100644 --- a/src/cmd/cover/html.go +++ b/src/cmd/cover/html.go @@ -6,7 +6,6 @@ package main import ( "bufio" - "bytes" "cmd/internal/browser" "fmt" "html/template" @@ -157,7 +156,7 @@ func rgb(n int) string { // colors generates the CSS rules for coverage colors. func colors() template.CSS { - var buf bytes.Buffer + var buf strings.Builder for i := 0; i < 11; i++ { fmt.Fprintf(&buf, ".cov%v { color: %v }\n", i, rgb(i)) } diff --git a/src/cmd/cover/testdata/test.go b/src/cmd/cover/testdata/test.go index b794962205d3da..0e1dbc61943112 100644 --- a/src/cmd/cover/testdata/test.go +++ b/src/cmd/cover/testdata/test.go @@ -13,6 +13,7 @@ package main import _ "unsafe" // for go:linkname //go:linkname some_name some_name +var some_name int const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" @@ -150,7 +151,7 @@ func testSwitch() { } func testTypeSwitch() { - var x = []interface{}{1, 2.0, "hi"} + var x = []any{1, 2.0, "hi"} for _, v := range x { switch func() { check(LINE, 3) }(); v.(type) { case int: @@ -214,7 +215,7 @@ func testEmptySwitches() { switch 3 { } check(LINE, 1) - switch i := (interface{})(3).(int); i { + switch i := (any)(3).(int); i { } check(LINE, 1) c := make(chan int) diff --git a/src/cmd/cover/testdata/toolexec.go b/src/cmd/cover/testdata/toolexec.go index 458adaeaaa5369..1769efedbeb678 100644 --- a/src/cmd/cover/testdata/toolexec.go +++ b/src/cmd/cover/testdata/toolexec.go @@ -15,8 +15,8 @@ package main import ( - exec "internal/execabs" "os" + "os/exec" "strings" ) diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index bec17696f3040a..05676035bad27b 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -26,12 +26,14 @@ import ( // The usual variables. var ( goarch string - gobin string + gorootBin string + gorootBinGo string gohostarch string gohostos string goos string goarm string go386 string + goamd64 string gomips string gomips64 string goppc64 string @@ -48,13 +50,12 @@ var ( exe string defaultcc map[string]string defaultcxx map[string]string - defaultcflags string - defaultldflags string defaultpkgconfig string defaultldso string rebuildall bool defaultclang bool + noOpt bool vflag int // verbosity ) @@ -65,6 +66,7 @@ var okgoarch = []string{ "amd64", "arm", "arm64", + "loong64", "mips", "mipsle", "mips64", @@ -113,6 +115,13 @@ func xinit() { fatalf("$GOROOT must be set") } goroot = filepath.Clean(b) + gorootBin = pathf("%s/bin", goroot) + + // Don't run just 'go' because the build infrastructure + // runs cmd/dist inside go/bin often, and on Windows + // it will be found in the current directory and refuse to exec. + // All exec calls rewrite "go" into gorootBinGo. + gorootBinGo = pathf("%s/bin/go", goroot) b = os.Getenv("GOROOT_FINAL") if b == "" { @@ -120,12 +129,6 @@ func xinit() { } goroot_final = b - b = os.Getenv("GOBIN") - if b == "" { - b = pathf("%s/bin", goroot) - } - gobin = b - b = os.Getenv("GOOS") if b == "" { b = gohostos @@ -147,6 +150,12 @@ func xinit() { } go386 = b + b = os.Getenv("GOAMD64") + if b == "" { + b = "v1" + } + goamd64 = b + b = os.Getenv("GOMIPS") if b == "" { b = "hardfloat" @@ -209,9 +218,6 @@ func xinit() { defaultcc = compilerEnv("CC", cc) defaultcxx = compilerEnv("CXX", cxx) - defaultcflags = os.Getenv("CFLAGS") - defaultldflags = os.Getenv("LDFLAGS") - b = os.Getenv("PKG_CONFIG") if b == "" { b = "pkg-config" @@ -222,6 +228,7 @@ func xinit() { // For tools being invoked but also for os.ExpandEnv. os.Setenv("GO386", go386) + os.Setenv("GOAMD64", goamd64) os.Setenv("GOARCH", goarch) os.Setenv("GOARM", goarm) os.Setenv("GOHOSTARCH", gohostarch) @@ -238,9 +245,19 @@ func xinit() { // make.bash really does start from a clean slate. os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot)) + // Set GOBIN to GOROOT/bin. The meaning of GOBIN has drifted over time + // (see https://go.dev/issue/3269, https://go.dev/cl/183058, + // https://go.dev/issue/31576). Since we want binaries installed by 'dist' to + // always go to GOROOT/bin anyway. + os.Setenv("GOBIN", gorootBin) + // Make the environment more predictable. os.Setenv("LANG", "C") os.Setenv("LANGUAGE", "en_US.UTF8") + os.Unsetenv("GO111MODULE") + os.Setenv("GOENV", "off") + os.Unsetenv("GOFLAGS") + os.Setenv("GOWORK", "off") workdir = xworkdir() if err := ioutil.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap"), 0666); err != nil { @@ -313,34 +330,6 @@ func chomp(s string) string { return strings.TrimRight(s, " \t\r\n") } -func branchtag(branch string) (tag string, precise bool) { - log := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch) - tag = branch - for row, line := range strings.Split(log, "\n") { - // Each line is either blank, or looks like - // (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4) - // We need to find an element starting with refs/tags/. - const s = " refs/tags/" - i := strings.Index(line, s) - if i < 0 { - continue - } - // Trim off known prefix. - line = line[i+len(s):] - // The tag name ends at a comma or paren. - j := strings.IndexAny(line, ",)") - if j < 0 { - continue // malformed line; ignore it - } - tag = line[:j] - if row == 0 { - precise = true // tag denotes HEAD - } - break - } - return -} - // findgoversion determines the Go version to use in the version string. func findgoversion() string { // The $GOROOT/VERSION file takes priority, for distributions @@ -392,42 +381,26 @@ func findgoversion() string { } // Otherwise, use Git. - // What is the current branch? - branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD")) - - // What are the tags along the current branch? - tag := "devel" - precise := false - - // If we're on a release branch, use the closest matching tag - // that is on the release branch (and not on the master branch). - if strings.HasPrefix(branch, "release-branch.") { - tag, precise = branchtag(branch) - } - - if !precise { - // Tag does not point at HEAD; add 1.x base version, hash, and date to version. - // - // Note that we lightly parse internal/goversion/goversion.go to - // obtain the base version. We can't just import the package, - // because cmd/dist is built with a bootstrap GOROOT which could - // be an entirely different version of Go, like 1.4. We assume - // that the file contains "const Version = ". - - goversionSource := readfile(pathf("%s/src/internal/goversion/goversion.go", goroot)) - m := regexp.MustCompile(`(?m)^const Version = (\d+)`).FindStringSubmatch(goversionSource) - if m == nil { - fatalf("internal/goversion/goversion.go does not contain 'const Version = ...'") - } - tag += fmt.Sprintf(" go1.%s-", m[1]) - - tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format:%h %cd", "HEAD")) - } + // + // Include 1.x base version, hash, and date in the version. + // + // Note that we lightly parse internal/goversion/goversion.go to + // obtain the base version. We can't just import the package, + // because cmd/dist is built with a bootstrap GOROOT which could + // be an entirely different version of Go, like 1.4. We assume + // that the file contains "const Version = ". + goversionSource := readfile(pathf("%s/src/internal/goversion/goversion.go", goroot)) + m := regexp.MustCompile(`(?m)^const Version = (\d+)`).FindStringSubmatch(goversionSource) + if m == nil { + fatalf("internal/goversion/goversion.go does not contain 'const Version = ...'") + } + version := fmt.Sprintf("devel go1.%s-", m[1]) + version += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format:%h %cd", "HEAD")) // Cache version. - writefile(tag, path, 0) + writefile(version, path, 0) - return tag + return version } // isGitRepo reports whether the working directory is inside a Git repository. @@ -531,16 +504,6 @@ func setup() { xremove(pathf("%s/bin/%s", goroot, old)) } - // If $GOBIN is set and has a Go compiler, it must be cleaned. - for _, char := range "56789" { - if isfile(pathf("%s/%c%s", gobin, char, "g")) { - for _, old := range oldtool { - xremove(pathf("%s/%s", gobin, old)) - } - break - } - } - // For release, make sure excluded things are excluded. goversion := findgoversion() if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) { @@ -773,6 +736,8 @@ func runInstall(pkg string, ch chan struct{}) { pathf("%s/src/runtime/funcdata.h", goroot), 0) copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot), pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0) + copyfile(pathf("%s/pkg/include/asm_amd64.h", goroot), + pathf("%s/src/runtime/asm_amd64.h", goroot), 0) } // Generate any missing files; regenerate existing ones. @@ -809,6 +774,9 @@ func runInstall(pkg string, ch chan struct{}) { importMap := make(map[string]string) for _, p := range gofiles { for _, imp := range readimports(p) { + if imp == "C" { + fatalf("%s imports C", p) + } importMap[imp] = resolveVendor(imp, dir) } } @@ -819,6 +787,9 @@ func runInstall(pkg string, ch chan struct{}) { sort.Strings(sortedImports) for _, dep := range importMap { + if dep == "C" { + fatalf("%s imports C", pkg) + } startInstall(dep) } for _, dep := range importMap { @@ -967,32 +938,42 @@ func packagefile(pkg string) string { return pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, pkg) } -// matchfield reports whether the field (x,y,z) matches this build. -// all the elements in the field must be satisfied. -func matchfield(f string) bool { - for _, tag := range strings.Split(f, ",") { - if !matchtag(tag) { - return false - } - } - return true +// unixOS is the set of GOOS values matched by the "unix" build tag. +// This is the same list as in go/build/syslist.go and +// cmd/go/internal/imports/build.go. +var unixOS = map[string]bool{ + "aix": true, + "android": true, + "darwin": true, + "dragonfly": true, + "freebsd": true, + "hurd": true, + "illumos": true, + "ios": true, + "linux": true, + "netbsd": true, + "openbsd": true, + "solaris": true, } -// matchtag reports whether the tag (x or !x) matches this build. +// matchtag reports whether the tag matches this build. func matchtag(tag string) bool { - if tag == "" { + switch tag { + case "gc", "cmd_go_bootstrap", "go1.1": + return true + case "linux": + return goos == "linux" || goos == "android" + case "solaris": + return goos == "solaris" || goos == "illumos" + case "darwin": + return goos == "darwin" || goos == "ios" + case goos, goarch: + return true + case "unix": + return unixOS[goos] + default: return false } - if tag[0] == '!' { - if len(tag) == 1 || tag[1] == '!' { - return false - } - return !matchtag(tag[1:]) - } - return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || - (goos == "android" && tag == "linux") || - (goos == "illumos" && tag == "solaris") || - (goos == "ios" && tag == "darwin") } // shouldbuild reports whether we should build this file. @@ -1029,7 +1010,7 @@ func shouldbuild(file, pkg string) bool { return false } - // Check file contents for // +build lines. + // Check file contents for //go:build lines. for _, p := range strings.Split(readfile(file), "\n") { p = strings.TrimSpace(p) if p == "" { @@ -1049,20 +1030,13 @@ func shouldbuild(file, pkg string) bool { if !strings.HasPrefix(p, "//") { break } - if !strings.Contains(p, "+build") { - continue - } - fields := strings.Fields(p[2:]) - if len(fields) < 1 || fields[0] != "+build" { - continue - } - for _, p := range fields[1:] { - if matchfield(p) { - goto fieldmatch + if strings.HasPrefix(p, "//go:build ") { + matched, err := matchexpr(p[len("//go:build "):]) + if err != nil { + errprintf("%s: %v", file, err) } + return matched } - return false - fieldmatch: } return true @@ -1157,8 +1131,8 @@ func clean() { // The env command prints the default environment. func cmdenv() { path := flag.Bool("p", false, "emit updated PATH") - plan9 := flag.Bool("9", false, "emit plan 9 syntax") - windows := flag.Bool("w", false, "emit windows syntax") + plan9 := flag.Bool("9", gohostos == "plan9", "emit plan 9 syntax") + windows := flag.Bool("w", gohostos == "windows", "emit windows syntax") xflagparse(0) format := "%s=\"%s\"\n" @@ -1169,10 +1143,13 @@ func cmdenv() { format = "set %s=%s\r\n" } + xprintf(format, "GO111MODULE", "") xprintf(format, "GOARCH", goarch) - xprintf(format, "GOBIN", gobin) + xprintf(format, "GOBIN", gorootBin) xprintf(format, "GOCACHE", os.Getenv("GOCACHE")) xprintf(format, "GODEBUG", os.Getenv("GODEBUG")) + xprintf(format, "GOENV", "off") + xprintf(format, "GOFLAGS", "") xprintf(format, "GOHOSTARCH", gohostarch) xprintf(format, "GOHOSTOS", gohostos) xprintf(format, "GOOS", goos) @@ -1186,6 +1163,9 @@ func cmdenv() { if goarch == "386" { xprintf(format, "GO386", go386) } + if goarch == "amd64" { + xprintf(format, "GOAMD64", goamd64) + } if goarch == "mips" || goarch == "mipsle" { xprintf(format, "GOMIPS", gomips) } @@ -1195,13 +1175,14 @@ func cmdenv() { if goarch == "ppc64" || goarch == "ppc64le" { xprintf(format, "GOPPC64", goppc64) } + xprintf(format, "GOWORK", "off") if *path { sep := ":" if gohostos == "windows" { sep = ";" } - xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH"))) + xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gorootBin, sep, os.Getenv("PATH"))) } } @@ -1254,7 +1235,9 @@ var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link"} // commands (like "go tool dist test" in run.bash) can rely on bug fixes // made since Go 1.4, but this function cannot. In particular, the uses // of os/exec in this function cannot assume that +// // cmd.Env = append(os.Environ(), "X=Y") +// // sets $X to Y in the command's environment. That guarantee was // added after Go 1.4, and in fact in Go 1.4 it was typically the opposite: // if $X was already present in os.Environ(), most systems preferred @@ -1344,9 +1327,10 @@ func cmdbootstrap() { } gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now + setNoOpt() goldflags = os.Getenv("GO_LDFLAGS") // we were using $BOOT_GO_LDFLAGS until now goBootstrap := pathf("%s/go_bootstrap", tooldir) - cmdGo := pathf("%s/go", gobin) + cmdGo := pathf("%s/go", gorootBin) if debug { run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec) @@ -1485,7 +1469,7 @@ func cmdbootstrap() { os.Setenv("GOOS", gohostos) os.Setenv("GOARCH", gohostarch) os.Setenv("CC", compilerEnvLookup(defaultcc, gohostos, gohostarch)) - goCmd(cmdGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gobin, goos, goarch, exe), wrapperPath) + goCmd(cmdGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gorootBin, goos, goarch, exe), wrapperPath) // Restore environment. // TODO(elias.naur): support environment variables in goCmd? os.Setenv("GOOS", goos) @@ -1517,8 +1501,22 @@ func goInstall(goBinary string, args ...string) { goCmd(goBinary, "install", args...) } +func appendCompilerFlags(args []string) []string { + if gogcflags != "" { + args = append(args, "-gcflags=all="+gogcflags) + } + if goldflags != "" { + args = append(args, "-ldflags=all="+goldflags) + } + return args +} + func goCmd(goBinary string, cmd string, args ...string) { - goCmd := []string{goBinary, cmd, "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags} + goCmd := []string{goBinary, cmd} + if noOpt { + goCmd = append(goCmd, "-tags=noopt") + } + goCmd = appendCompilerFlags(goCmd) if vflag > 0 { goCmd = append(goCmd, "-v") } @@ -1532,12 +1530,14 @@ func goCmd(goBinary string, cmd string, args ...string) { } func checkNotStale(goBinary string, targets ...string) { - out := run(workdir, CheckExit, - append([]string{ - goBinary, - "list", "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags, - "-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}", - }, targets...)...) + goCmd := []string{goBinary, "list"} + if noOpt { + goCmd = append(goCmd, "-tags=noopt") + } + goCmd = appendCompilerFlags(goCmd) + goCmd = append(goCmd, "-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}") + + out := run(workdir, CheckExit, append(goCmd, targets...)...) if strings.Contains(out, "\tSTALE ") { os.Setenv("GODEBUG", "gocachehash=1") for _, target := range []string{"runtime/internal/sys", "cmd/dist", "cmd/link"} { @@ -1571,6 +1571,7 @@ var cgoEnabled = map[string]bool{ "linux/amd64": true, "linux/arm": true, "linux/arm64": true, + "linux/loong64": true, "linux/ppc64": false, "linux/ppc64le": true, "linux/mips": true, @@ -1638,7 +1639,13 @@ func checkCC() { if !needCC() { return } - if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil { + cc, err := quotedSplit(defaultcc[""]) + if err != nil { + fatalf("split CC: %v", err) + } + var ccHelp = append(cc, "--help") + + if output, err := exec.Command(ccHelp[0], ccHelp[1:]...).CombinedOutput(); err != nil { outputHdr := "" if len(output) > 0 { outputHdr = "\nCommand output:\n\n" @@ -1646,7 +1653,7 @@ func checkCC() { fatalf("cannot invoke C compiler %q: %v\n\n"+ "Go needs a system C compiler for use with cgo.\n"+ "To set a C compiler, set CC=the-compiler.\n"+ - "To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc[""], err, outputHdr, output) + "To disable cgo, set CGO_ENABLED=0.\n%s%s", cc, err, outputHdr, output) } } @@ -1699,26 +1706,26 @@ func banner() { } xprintf("---\n") xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot) - xprintf("Installed commands in %s\n", gobin) + xprintf("Installed commands in %s\n", gorootBin) if !xsamefile(goroot_final, goroot) { // If the files are to be moved, don't check that gobin // is on PATH; assume they know what they are doing. } else if gohostos == "plan9" { - // Check that gobin is bound before /bin. + // Check that GOROOT/bin is bound before /bin. pid := strings.Replace(readfile("#c/pid"), " ", "", -1) ns := fmt.Sprintf("/proc/%s/ns", pid) - if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) { - xprintf("*** You need to bind %s before /bin.\n", gobin) + if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gorootBin)) { + xprintf("*** You need to bind %s before /bin.\n", gorootBin) } } else { - // Check that gobin appears in $PATH. + // Check that GOROOT/bin appears in $PATH. pathsep := ":" if gohostos == "windows" { pathsep = ";" } - if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) { - xprintf("*** You need to add %s to your PATH.\n", gobin) + if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gorootBin+pathsep) { + xprintf("*** You need to add %s to your PATH.\n", gorootBin) } } @@ -1802,3 +1809,12 @@ func IsRuntimePackagePath(pkgpath string) bool { } return rval } + +func setNoOpt() { + for _, gcflag := range strings.Split(gogcflags, " ") { + if gcflag == "-N" || gcflag == "-l" { + noOpt = true + break + } + } +} diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go index caafc13da888ad..29b0167f470ecd 100644 --- a/src/cmd/dist/buildgo.go +++ b/src/cmd/dist/buildgo.go @@ -5,7 +5,6 @@ package main import ( - "bytes" "fmt" "os" "path/filepath" @@ -28,7 +27,7 @@ import ( // but we also write cmd/cgo/zdefaultcc.go func mkzdefaultcc(dir, file string) { if strings.Contains(file, filepath.FromSlash("go/internal/cfg")) { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package cfg\n") @@ -40,7 +39,7 @@ func mkzdefaultcc(dir, file string) { return } - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package main\n") @@ -52,7 +51,7 @@ func mkzdefaultcc(dir, file string) { } func defaultCCFunc(name string, defaultcc map[string]string) string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "func %s(goos, goarch string) string {\n", name) fmt.Fprintf(&buf, "\tswitch goos+`/`+goarch {\n") @@ -82,7 +81,7 @@ func mkzosarch(dir, file string) { } sort.Strings(list) - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n\n") fmt.Fprintf(&buf, "package cfg\n\n") fmt.Fprintf(&buf, "var OSArchSupportsCgo = map[string]bool{\n") @@ -97,7 +96,7 @@ func mkzosarch(dir, file string) { // mkzcgo writes zcgo.go for the go/build package: // // package build -// var cgoEnabled = map[string]bool{} +// var cgoEnabled = map[string]bool{} // // It is invoked to write go/build/zcgo.go. func mkzcgo(dir, file string) { @@ -110,7 +109,7 @@ func mkzcgo(dir, file string) { } sort.Strings(list) - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package build\n") diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index 54e935ad3bec1d..932c509fa4db5a 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -5,9 +5,7 @@ package main import ( - "bytes" "fmt" - "os" "strings" ) @@ -19,16 +17,12 @@ import ( // // package sys // -// const StackGuardMultiplier = -// +// (Nothing right now!) func mkzversion(dir, file string) { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package sys\n") - fmt.Fprintln(&buf) - fmt.Fprintf(&buf, "const StackGuardMultiplierDefault = %d\n", stackGuardMultiplierDefault()) - writefile(buf.String(), file, writeSkipSame) } @@ -52,7 +46,7 @@ func mkzversion(dir, file string) { // This is more useful than having it default to generating objects for the // original target (in this example, a Mac). func mkbuildcfg(file string) { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package buildcfg\n") @@ -60,6 +54,7 @@ func mkbuildcfg(file string) { fmt.Fprintf(&buf, "import \"runtime\"\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386) + fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64) fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm) fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips) fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64) @@ -78,27 +73,12 @@ func mkbuildcfg(file string) { // // package objabi // -// const stackGuardMultiplierDefault = -// +// (Nothing right now!) func mkobjabi(file string) { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "package objabi\n") - fmt.Fprintln(&buf) - fmt.Fprintf(&buf, "const stackGuardMultiplierDefault = %d\n", stackGuardMultiplierDefault()) writefile(buf.String(), file, writeSkipSame) } - -// stackGuardMultiplierDefault returns a multiplier to apply to the default -// stack guard size. Larger multipliers are used for non-optimized -// builds that have larger stack frames. -func stackGuardMultiplierDefault() int { - for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") { - if s == "-N" { - return 2 - } - } - return 1 -} diff --git a/src/cmd/dist/buildtag.go b/src/cmd/dist/buildtag.go new file mode 100644 index 00000000000000..24776a0aaf735f --- /dev/null +++ b/src/cmd/dist/buildtag.go @@ -0,0 +1,133 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "strings" +) + +// exprParser is a //go:build expression parser and evaluator. +// The parser is a trivial precedence-based parser which is still +// almost overkill for these very simple expressions. +type exprParser struct { + x string + t exprToken // upcoming token +} + +// val is the value type result of parsing. +// We don't keep a parse tree, just the value of the expression. +type val bool + +// exprToken describes a single token in the input. +// Prefix operators define a prefix func that parses the +// upcoming value. Binary operators define an infix func +// that combines two values according to the operator. +// In that case, the parsing loop parses the two values. +type exprToken struct { + tok string + prec int + prefix func(*exprParser) val + infix func(val, val) val +} + +var exprTokens []exprToken + +func init() { // init to break init cycle + exprTokens = []exprToken{ + {tok: "&&", prec: 1, infix: func(x, y val) val { return x && y }}, + {tok: "||", prec: 2, infix: func(x, y val) val { return x || y }}, + {tok: "!", prec: 3, prefix: (*exprParser).not}, + {tok: "(", prec: 3, prefix: (*exprParser).paren}, + {tok: ")"}, + } +} + +// matchexpr parses and evaluates the //go:build expression x. +func matchexpr(x string) (matched bool, err error) { + defer func() { + if e := recover(); e != nil { + matched = false + err = fmt.Errorf("parsing //go:build line: %v", e) + } + }() + + p := &exprParser{x: x} + p.next() + v := p.parse(0) + if p.t.tok != "end of expression" { + panic("unexpected " + p.t.tok) + } + return bool(v), nil +} + +// parse parses an expression, including binary operators at precedence >= prec. +func (p *exprParser) parse(prec int) val { + if p.t.prefix == nil { + panic("unexpected " + p.t.tok) + } + v := p.t.prefix(p) + for p.t.prec >= prec && p.t.infix != nil { + t := p.t + p.next() + v = t.infix(v, p.parse(t.prec+1)) + } + return v +} + +// not is the prefix parser for a ! token. +func (p *exprParser) not() val { + p.next() + return !p.parse(100) +} + +// paren is the prefix parser for a ( token. +func (p *exprParser) paren() val { + p.next() + v := p.parse(0) + if p.t.tok != ")" { + panic("missing )") + } + p.next() + return v +} + +// next advances the parser to the next token, +// leaving the token in p.t. +func (p *exprParser) next() { + p.x = strings.TrimSpace(p.x) + if p.x == "" { + p.t = exprToken{tok: "end of expression"} + return + } + for _, t := range exprTokens { + if strings.HasPrefix(p.x, t.tok) { + p.x = p.x[len(t.tok):] + p.t = t + return + } + } + + i := 0 + for i < len(p.x) && validtag(p.x[i]) { + i++ + } + if i == 0 { + panic(fmt.Sprintf("syntax error near %#q", rune(p.x[i]))) + } + tag := p.x[:i] + p.x = p.x[i:] + p.t = exprToken{ + tok: "tag", + prefix: func(p *exprParser) val { + p.next() + return val(matchtag(tag)) + }, + } +} + +func validtag(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '.' || c == '_' +} diff --git a/src/cmd/dist/buildtag_test.go b/src/cmd/dist/buildtag_test.go new file mode 100644 index 00000000000000..f64abfd1f18c25 --- /dev/null +++ b/src/cmd/dist/buildtag_test.go @@ -0,0 +1,43 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" + "testing" +) + +var buildParserTests = []struct { + x string + matched bool + err error +}{ + {"gc", true, nil}, + {"gccgo", false, nil}, + {"!gc", false, nil}, + {"gc && gccgo", false, nil}, + {"gc || gccgo", true, nil}, + {"gc || (gccgo && !gccgo)", true, nil}, + {"gc && (gccgo || !gccgo)", true, nil}, + {"!(gc && (gccgo || !gccgo))", false, nil}, + {"gccgo || gc", true, nil}, + {"!(!(!(gccgo || gc)))", false, nil}, + {"compiler_bootstrap", false, nil}, + {"cmd_go_bootstrap", true, nil}, + {"syntax(error", false, fmt.Errorf("parsing //go:build line: unexpected (")}, + {"(gc", false, fmt.Errorf("parsing //go:build line: missing )")}, + {"gc gc", false, fmt.Errorf("parsing //go:build line: unexpected tag")}, + {"(gc))", false, fmt.Errorf("parsing //go:build line: unexpected )")}, +} + +func TestBuildParser(t *testing.T) { + for _, tt := range buildParserTests { + matched, err := matchexpr(tt.x) + if matched != tt.matched || !reflect.DeepEqual(err, tt.err) { + t.Errorf("matchexpr(%q) = %v, %v; want %v, %v", tt.x, matched, err, tt.matched, tt.err) + } + } +} diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 26b33e389fe928..a7901d93632384 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -15,7 +15,7 @@ import ( "fmt" "os" "path/filepath" - "runtime" + "regexp" "strings" ) @@ -43,9 +43,11 @@ var bootstrapDirs = []string{ "cmd/internal/edit", "cmd/internal/gcprog", "cmd/internal/goobj", + "cmd/internal/notsha256", "cmd/internal/obj/...", "cmd/internal/objabi", "cmd/internal/pkgpath", + "cmd/internal/quoted", "cmd/internal/src", "cmd/internal/sys", "cmd/link", @@ -61,7 +63,10 @@ var bootstrapDirs = []string{ "internal/buildcfg", "internal/goexperiment", "internal/goversion", + "internal/pkgbits", + "internal/profile", "internal/race", + "internal/saferio", "internal/unsafeheader", "internal/xcoff", "math/big", @@ -84,6 +89,8 @@ var ignorePrefixes = []string{ var ignoreSuffixes = []string{ "_arm64.s", "_arm64.go", + "_loong64.s", + "_loong64.go", "_riscv64.s", "_riscv64.go", "_wasm.s", @@ -92,10 +99,21 @@ var ignoreSuffixes = []string{ "_test.go", } +var tryDirs = []string{ + "sdk/go1.17", + "go1.17", +} + func bootstrapBuildTools() { goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP") if goroot_bootstrap == "" { - goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME")) + home := os.Getenv("HOME") + goroot_bootstrap = pathf("%s/go1.4", home) + for _, d := range tryDirs { + if p := pathf("%s/%s", home, d); isdir(p) { + goroot_bootstrap = p + } + } } xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap) @@ -188,6 +206,8 @@ func bootstrapBuildTools() { // https://groups.google.com/d/msg/golang-dev/Ss7mCKsvk8w/Gsq7VYI0AwAJ // Use the math_big_pure_go build tag to disable the assembly in math/big // which may contain unsupported instructions. + // Use the purego build tag to disable other assembly code, + // such as in cmd/internal/notsha256. // Note that if we are using Go 1.10 or later as bootstrap, the -gcflags=-l // only applies to the final cmd/go binary, but that's OK: if this is Go 1.10 // or later we don't need to disable inlining to work around bugs in the Go 1.4 compiler. @@ -195,7 +215,7 @@ func bootstrapBuildTools() { pathf("%s/bin/go", goroot_bootstrap), "install", "-gcflags=-l", - "-tags=math_big_pure_go compiler_bootstrap", + "-tags=math_big_pure_go compiler_bootstrap purego", } if vflag > 0 { cmd = append(cmd, "-v") @@ -226,11 +246,11 @@ var ssaRewriteFileSubstring = filepath.FromSlash("src/cmd/compile/internal/ssa/r // isUnneededSSARewriteFile reports whether srcFile is a // src/cmd/compile/internal/ssa/rewriteARCHNAME.go file for an -// architecture that isn't for the current runtime.GOARCH. +// architecture that isn't for the given GOARCH. // // When unneeded is true archCaps is the rewrite base filename without // the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc. -func isUnneededSSARewriteFile(srcFile string) (archCaps string, unneeded bool) { +func isUnneededSSARewriteFile(srcFile, goArch string) (archCaps string, unneeded bool) { if !strings.Contains(srcFile, ssaRewriteFileSubstring) { return "", false } @@ -245,13 +265,10 @@ func isUnneededSSARewriteFile(srcFile string) (archCaps string, unneeded bool) { archCaps = fileArch fileArch = strings.ToLower(fileArch) fileArch = strings.TrimSuffix(fileArch, "splitload") - if fileArch == os.Getenv("GOHOSTARCH") { + if fileArch == goArch { return "", false } - if fileArch == strings.TrimSuffix(runtime.GOARCH, "le") { - return "", false - } - if fileArch == strings.TrimSuffix(os.Getenv("GOARCH"), "le") { + if fileArch == strings.TrimSuffix(goArch, "le") { return "", false } return archCaps, true @@ -260,9 +277,9 @@ func isUnneededSSARewriteFile(srcFile string) (archCaps string, unneeded bool) { func bootstrapRewriteFile(srcFile string) string { // During bootstrap, generate dummy rewrite files for // irrelevant architectures. We only need to build a bootstrap - // binary that works for the current runtime.GOARCH. + // binary that works for the current gohostarch. // This saves 6+ seconds of bootstrap. - if archCaps, ok := isUnneededSSARewriteFile(srcFile); ok { + if archCaps, ok := isUnneededSSARewriteFile(srcFile, gohostarch); ok { return fmt.Sprintf(`// Code generated by go tool dist; DO NOT EDIT. package ssa @@ -276,7 +293,11 @@ func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") } } func bootstrapFixImports(srcFile string) string { - lines := strings.SplitAfter(readfile(srcFile), "\n") + text := readfile(srcFile) + if !strings.Contains(srcFile, "/cmd/") && !strings.Contains(srcFile, `\cmd\`) { + text = regexp.MustCompile(`\bany\b`).ReplaceAllString(text, "interface{}") + } + lines := strings.SplitAfter(text, "\n") inBlock := false for i, line := range lines { if strings.HasPrefix(line, "import (") { @@ -290,8 +311,6 @@ func bootstrapFixImports(srcFile string) string { if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) || inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"") || strings.HasPrefix(line, "\texec \"")) { line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1) - // During bootstrap, must use plain os/exec. - line = strings.Replace(line, `exec "internal/execabs"`, `"os/exec"`, -1) for _, dir := range bootstrapDirs { if strings.HasPrefix(dir, "cmd/") { continue diff --git a/src/cmd/dist/doc.go b/src/cmd/dist/doc.go index a4e6aa5cbfd6f6..ad26aa2dc06ee2 100644 --- a/src/cmd/dist/doc.go +++ b/src/cmd/dist/doc.go @@ -5,15 +5,17 @@ // Dist helps bootstrap, build, and test the Go distribution. // // Usage: -// go tool dist [command] +// +// go tool dist [command] // // The commands are: -// banner print installation banner -// bootstrap rebuild everything -// clean deletes all built files -// env [-p] print environment (-p: include $PATH) -// install [dir] install individual directory -// list [-json] list all supported platforms -// test [-h] run Go test(s) -// version print Go version +// +// banner print installation banner +// bootstrap rebuild everything +// clean deletes all built files +// env [-p] print environment (-p: include $PATH) +// install [dir] install individual directory +// list [-json] list all supported platforms +// test [-h] run Go test(s) +// version print Go version package main diff --git a/src/cmd/dist/exec.go b/src/cmd/dist/exec.go new file mode 100644 index 00000000000000..67305530ae83fc --- /dev/null +++ b/src/cmd/dist/exec.go @@ -0,0 +1,53 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/exec" + "strings" +) + +// setDir sets cmd.Dir to dir, and also adds PWD=dir to cmd's environment. +func setDir(cmd *exec.Cmd, dir string) { + cmd.Dir = dir + setEnv(cmd, "PWD", dir) +} + +// setEnv sets cmd.Env so that key = value. +// +// It first removes any existing values for key, so it is safe to call +// even from within cmdbootstrap. +func setEnv(cmd *exec.Cmd, key, value string) { + kv := key + "=" + value + if cmd.Env == nil { + cmd.Env = os.Environ() + } + + prefix := kv[:len(key)+1] + for i, entry := range cmd.Env { + if strings.HasPrefix(entry, prefix) { + cmd.Env[i] = kv + return + } + } + + cmd.Env = append(cmd.Env, kv) +} + +// unsetEnv sets cmd.Env so that key is not present in the environment. +func unsetEnv(cmd *exec.Cmd, key string) { + if cmd.Env == nil { + cmd.Env = os.Environ() + } + + prefix := key + "=" + for i, entry := range cmd.Env { + if strings.HasPrefix(entry, prefix) { + cmd.Env = append(cmd.Env[:i], cmd.Env[i+1:]...) + return + } + } +} diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go index 37de1acc317b34..2651ecb05fcc27 100644 --- a/src/cmd/dist/main.go +++ b/src/cmd/dist/main.go @@ -94,7 +94,15 @@ func main() { if gohostarch == "" { // Default Unix system. out := run("", CheckExit, "uname", "-m") + outAll := run("", CheckExit, "uname", "-a") switch { + case strings.Contains(outAll, "RELEASE_ARM64"): + // MacOS prints + // Darwin p1.local 21.1.0 Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:01 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T6000 x86_64 + // on ARM64 laptops when there is an x86 parent in the + // process tree. Look for the RELEASE_ARM64 to avoid being + // confused into building an x86 toolchain. + gohostarch = "arm64" case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): gohostarch = "amd64" case strings.Contains(out, "86"): @@ -125,6 +133,8 @@ func main() { if elfIsLittleEndian(os.Args[0]) { gohostarch = "mipsle" } + case strings.Contains(out, "loongarch64"): + gohostarch = "loong64" case strings.Contains(out, "riscv64"): gohostarch = "riscv64" case strings.Contains(out, "s390x"): diff --git a/src/cmd/dist/notgo117.go b/src/cmd/dist/notgo117.go new file mode 100644 index 00000000000000..eb1c949b00e1b8 --- /dev/null +++ b/src/cmd/dist/notgo117.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Go 1.20 and later requires Go 1.17 as the bootstrap toolchain. +// If cmd/dist is built using an earlier Go version, this file will be +// included in the build and cause an error like: +// +// % GOROOT_BOOTSTRAP=$HOME/sdk/go1.16 ./make.bash +// Building Go cmd/dist using /Users/rsc/sdk/go1.16. (go1.16 darwin/amd64) +// found packages main (build.go) and building_Go_requires_Go_1_17_or_later (notgo117.go) in /Users/rsc/go/src/cmd/dist +// % +// +// which is the best we can do under the circumstances. +// +// See go.dev/issue/44505 for more background on +// why Go moved on from Go 1.4 for bootstrap. + +//go:build !go1.17 +// +build !go1.17 + +package building_Go_requires_Go_1_17_or_later diff --git a/src/cmd/dist/quoted.go b/src/cmd/dist/quoted.go new file mode 100644 index 00000000000000..e87b8a3965d025 --- /dev/null +++ b/src/cmd/dist/quoted.go @@ -0,0 +1,49 @@ +package main + +import "fmt" + +// quotedSplit is a verbatim copy from cmd/internal/quoted.go:Split and its +// dependencies (isSpaceByte). Since this package is built using the host's +// Go compiler, it cannot use `cmd/internal/...`. We also don't want to export +// it to all Go users. +// +// Please keep those in sync. +func quotedSplit(s string) ([]string, error) { + // Split fields allowing '' or "" around elements. + // Quotes further inside the string do not count. + var f []string + for len(s) > 0 { + for len(s) > 0 && isSpaceByte(s[0]) { + s = s[1:] + } + if len(s) == 0 { + break + } + // Accepted quoted string. No unescaping inside. + if s[0] == '"' || s[0] == '\'' { + quote := s[0] + s = s[1:] + i := 0 + for i < len(s) && s[i] != quote { + i++ + } + if i >= len(s) { + return nil, fmt.Errorf("unterminated %c string", quote) + } + f = append(f, s[:i]) + s = s[i+1:] + continue + } + i := 0 + for i < len(s) && !isSpaceByte(s[i]) { + i++ + } + f = append(f, s[:i]) + s = s[i:] + } + return f, nil +} + +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index f40fa926dfd398..37fc5eaae06596 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -25,8 +25,10 @@ import ( func cmdtest() { gogcflags = os.Getenv("GO_GCFLAGS") + setNoOpt() var t tester + var noRebuild bool flag.BoolVar(&t.listMode, "list", false, "list available tests") flag.BoolVar(&t.rebuild, "rebuild", false, "rebuild everything first") @@ -38,6 +40,9 @@ func cmdtest() { flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"), "run only those tests matching the regular expression; empty means to run all. "+ "Special exception: if the string begins with '!', the match is inverted.") + flag.BoolVar(&t.msan, "msan", false, "run in memory sanitizer builder mode") + flag.BoolVar(&t.asan, "asan", false, "run in address sanitizer builder mode") + xflagparse(-1) // any number of args if noRebuild { t.rebuild = false @@ -49,6 +54,8 @@ func cmdtest() { // tester executes cmdtest. type tester struct { race bool + msan bool + asan bool listMode bool rebuild bool failed bool @@ -91,15 +98,9 @@ type distTest struct { func (t *tester) run() { timelog("start", "dist test") - var exeSuffix string - if goos == "windows" { - exeSuffix = ".exe" - } - if _, err := os.Stat(filepath.Join(gobin, "go"+exeSuffix)); err == nil { - os.Setenv("PATH", fmt.Sprintf("%s%c%s", gobin, os.PathListSeparator, os.Getenv("PATH"))) - } + os.Setenv("PATH", fmt.Sprintf("%s%c%s", gorootBin, os.PathListSeparator, os.Getenv("PATH"))) - cmd := exec.Command("go", "env", "CGO_ENABLED") + cmd := exec.Command(gorootBinGo, "env", "CGO_ENABLED") cmd.Stderr = new(bytes.Buffer) slurp, err := cmd.Output() if err != nil { @@ -139,27 +140,35 @@ func (t *tester) run() { goInstall("go", append([]string{"-a", "-i"}, toolchain...)...) } - // Complete rebuild bootstrap, even with -no-rebuild. - // If everything is up-to-date, this is a no-op. - // If everything is not up-to-date, the first checkNotStale - // during the test process will kill the tests, so we might - // as well install the world. - // Now that for example "go install cmd/compile" does not - // also install runtime (you need "go install -i cmd/compile" - // for that), it's easy for previous workflows like - // "rebuild the compiler and then run run.bash" - // to break if we don't automatically refresh things here. - // Rebuilding is a shortened bootstrap. - // See cmdbootstrap for a description of the overall process. - // - // But don't do this if we're running in the Go build system, - // where cmd/dist is invoked many times. This just slows that - // down (Issue 24300). - if !t.listMode && os.Getenv("GO_BUILDER_NAME") == "" { - goInstall("go", append([]string{"-i"}, toolchain...)...) - goInstall("go", append([]string{"-i"}, toolchain...)...) - goInstall("go", "std", "cmd") - checkNotStale("go", "std", "cmd") + if !t.listMode { + if os.Getenv("GO_BUILDER_NAME") == "" { + // Complete rebuild bootstrap, even with -no-rebuild. + // If everything is up-to-date, this is a no-op. + // If everything is not up-to-date, the first checkNotStale + // during the test process will kill the tests, so we might + // as well install the world. + // Now that for example "go install cmd/compile" does not + // also install runtime (you need "go install -i cmd/compile" + // for that), it's easy for previous workflows like + // "rebuild the compiler and then run run.bash" + // to break if we don't automatically refresh things here. + // Rebuilding is a shortened bootstrap. + // See cmdbootstrap for a description of the overall process. + goInstall("go", append([]string{"-i"}, toolchain...)...) + goInstall("go", append([]string{"-i"}, toolchain...)...) + goInstall("go", "std", "cmd") + } else { + // The Go builder infrastructure should always begin running tests from a + // clean, non-stale state, so there is no need to rebuild the world. + // Instead, we can just check that it is not stale, which may be less + // expensive (and is also more likely to catch bugs in the builder + // implementation). + willTest := []string{"std"} + if t.shouldTestCmd() { + willTest = append(willTest, "cmd") + } + checkNotStale("go", willTest...) + } } t.timeoutScale = 1 @@ -210,6 +219,15 @@ func (t *tester) run() { } } + if err := t.maybeLogMetadata(); err != nil { + t.failed = true + if t.keepGoing { + log.Printf("Failed logging metadata: %v", err) + } else { + fatalf("Failed logging metadata: %v", err) + } + } + for _, dt := range t.tests { if !t.shouldRunTest(dt.name) { t.partial = true @@ -260,6 +278,22 @@ func (t *tester) shouldRunTest(name string) bool { return false } +func (t *tester) maybeLogMetadata() error { + if t.compileOnly { + // We need to run a subprocess to log metadata. Don't do that + // on compile-only runs. + return nil + } + t.out("Test execution environment.") + // Helper binary to print system metadata (CPU model, etc). This is a + // separate binary from dist so it need not build with the bootstrap + // toolchain. + // + // TODO(prattmic): If we split dist bootstrap and dist test then this + // could be simplified to directly use internal/sysinfo here. + return t.dirCmd(filepath.Join(goroot, "src/cmd/internal/metadata"), "go", []string{"run", "main.go"}).Run() +} + // short returns a -short flag value to use with 'go test' // or a test binary for tests intended to run in short mode. // It returns "true", unless the environment variable @@ -292,10 +326,17 @@ func (t *tester) goTest() []string { } func (t *tester) tags() string { - if t.iOS() { + ios := t.iOS() + switch { + case ios && noOpt: + return "-tags=lldb,noopt" + case ios: return "-tags=lldb" + case noOpt: + return "-tags=noopt" + default: + return "-tags=" } - return "-tags=" } // timeoutDuration converts the provided number of seconds into a @@ -325,15 +366,10 @@ var ( benchMatches []string ) -func (t *tester) registerStdTest(pkg string, useG3 bool) { +func (t *tester) registerStdTest(pkg string) { heading := "Testing packages." testPrefix := "go_test:" gcflags := gogcflags - if useG3 { - heading = "Testing packages with -G=3." - testPrefix = "go_test_g3:" - gcflags += " -G=3" - } testName := testPrefix + pkg if t.runRx == nil || t.runRx.MatchString(testName) == t.runRxWant { @@ -369,16 +405,24 @@ func (t *tester) registerStdTest(pkg string, useG3 bool) { "-short=" + short(), t.tags(), t.timeout(timeoutSec), - "-gcflags=all=" + gcflags, + } + if gcflags != "" { + args = append(args, "-gcflags=all="+gcflags) } if t.race { args = append(args, "-race") } + if t.msan { + args = append(args, "-msan") + } + if t.asan { + args = append(args, "-asan") + } if t.compileOnly { args = append(args, "-run=^$") } args = append(args, stdMatches...) - cmd := exec.Command("go", args...) + cmd := exec.Command(gorootBinGo, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() @@ -415,7 +459,7 @@ func (t *tester) registerRaceBenchTest(pkg string) { args = append(args, "-bench=.*") } args = append(args, benchMatches...) - cmd := exec.Command("go", args...) + cmd := exec.Command(gorootBinGo, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() @@ -434,10 +478,7 @@ func (t *tester) registerTests() { if len(t.runNames) > 0 { for _, name := range t.runNames { if strings.HasPrefix(name, "go_test:") { - t.registerStdTest(strings.TrimPrefix(name, "go_test:"), false) - } - if strings.HasPrefix(name, "go_test_g3:") { - t.registerStdTest(strings.TrimPrefix(name, "go_test_g3:"), true) + t.registerStdTest(strings.TrimPrefix(name, "go_test:")) } if strings.HasPrefix(name, "go_test_bench:") { t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:")) @@ -446,7 +487,7 @@ func (t *tester) registerTests() { } else { // Use a format string to only list packages and commands that have tests. const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}" - cmd := exec.Command("go", "list", "-f", format) + cmd := exec.Command(gorootBinGo, "list", "-f", format) if t.race { cmd.Args = append(cmd.Args, "-tags=race") } @@ -460,15 +501,8 @@ func (t *tester) registerTests() { fatalf("Error running go list std cmd: %v:\n%s", err, cmd.Stderr) } pkgs := strings.Fields(string(all)) - if false { - // Disable -G=3 option for standard tests for now, since - // they are flaky on the builder. - for _, pkg := range pkgs { - t.registerStdTest(pkg, true /* -G=3 flag */) - } - } for _, pkg := range pkgs { - t.registerStdTest(pkg, false) + t.registerStdTest(pkg) } if t.race { for _, pkg := range pkgs { @@ -491,30 +525,6 @@ func (t *tester) registerTests() { }) } - // Test go/... cmd/gofmt with type parameters enabled. - if !t.compileOnly { - t.tests = append(t.tests, distTest{ - name: "tyepparams", - heading: "go/... and cmd/gofmt tests with tag typeparams", - fn: func(dt *distTest) error { - t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=typeparams", "go/...") - t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=typeparams", "cmd/gofmt") - return nil - }, - }) - } - - if t.iOS() && !t.compileOnly { - t.tests = append(t.tests, distTest{ - name: "x509omitbundledroots", - heading: "crypto/x509 without bundled roots", - fn: func(dt *distTest) error { - t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=x509omitbundledroots", "-run=OmitBundledRoots", "crypto/x509") - return nil - }, - }) - } - // Test ios/amd64 for the iOS simulator. if goos == "darwin" && goarch == "amd64" && t.cgoEnabled { t.tests = append(t.tests, distTest{ @@ -522,7 +532,8 @@ func (t *tester) registerTests() { heading: "GOOS=ios on darwin/amd64", fn: func(dt *distTest) error { cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-run=SystemRoots", "crypto/x509") - cmd.Env = append(os.Environ(), "GOOS=ios", "CGO_ENABLED=1") + setEnv(cmd, "GOOS", "ios") + setEnv(cmd, "CGO_ENABLED", "1") return nil }, }) @@ -539,15 +550,64 @@ func (t *tester) registerTests() { name: testName, heading: "GOMAXPROCS=2 runtime -cpu=1,2,4 -quick", fn: func(dt *distTest) error { - cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "runtime", "-cpu=1,2,4", "-quick") + cmd := t.addCmd(dt, "src", t.goTest(), "-short=true", t.timeout(300), "runtime", "-cpu=1,2,4", "-quick") // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code, // creation of first goroutines and first garbage collections in the parallel setting. - cmd.Env = append(os.Environ(), "GOMAXPROCS=2") + setEnv(cmd, "GOMAXPROCS", "2") return nil }, }) } + // morestack tests. We only run these on in long-test mode + // (with GO_TEST_SHORT=false) because the runtime test is + // already quite long and mayMoreStackMove makes it about + // twice as slow. + if !t.compileOnly && short() == "false" { + // hooks is the set of maymorestack hooks to test with. + hooks := []string{"mayMoreStackPreempt", "mayMoreStackMove"} + // pkgs is the set of test packages to run. + pkgs := []string{"runtime", "reflect", "sync"} + // hookPkgs is the set of package patterns to apply + // the maymorestack hook to. + hookPkgs := []string{"runtime/...", "reflect", "sync"} + // unhookPkgs is the set of package patterns to + // exclude from hookPkgs. + unhookPkgs := []string{"runtime/testdata/..."} + for _, hook := range hooks { + // Construct the build flags to use the + // maymorestack hook in the compiler and + // assembler. We pass this via the GOFLAGS + // environment variable so that it applies to + // both the test itself and to binaries built + // by the test. + goFlagsList := []string{} + for _, flag := range []string{"-gcflags", "-asmflags"} { + for _, hookPkg := range hookPkgs { + goFlagsList = append(goFlagsList, flag+"="+hookPkg+"=-d=maymorestack=runtime."+hook) + } + for _, unhookPkg := range unhookPkgs { + goFlagsList = append(goFlagsList, flag+"="+unhookPkg+"=") + } + } + goFlags := strings.Join(goFlagsList, " ") + + for _, pkg := range pkgs { + pkg := pkg + testName := hook + ":" + pkg + t.tests = append(t.tests, distTest{ + name: testName, + heading: "maymorestack=" + hook, + fn: func(dt *distTest) error { + cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(600), pkg, "-short") + setEnv(cmd, "GOFLAGS", goFlags) + return nil + }, + }) + } + } + } + // This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests. // See issue 18153. if goos == "linux" { @@ -562,8 +622,8 @@ func (t *tester) registerTests() { fmt.Println("skipping terminal test; stdout/stderr not terminals") return nil } - cmd := exec.Command("go", "test") - cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153") + cmd := exec.Command(gorootBinGo, "test") + setDir(cmd, filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() @@ -600,16 +660,13 @@ func (t *tester) registerTests() { return err } - // Run `go test fmt` in the moved GOROOT. + // Run `go test fmt` in the moved GOROOT, without explicitly setting + // GOROOT in the environment. The 'go' command should find itself. cmd := exec.Command(filepath.Join(moved, "bin", "go"), "test", "fmt") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - // Don't set GOROOT in the environment. - for _, e := range os.Environ() { - if !strings.HasPrefix(e, "GOROOT=") && !strings.HasPrefix(e, "GOCACHE=") { - cmd.Env = append(cmd.Env, e) - } - } + unsetEnv(cmd, "GOROOT") + unsetEnv(cmd, "GOCACHE") // TODO(bcmills): ...why‽ err := cmd.Run() if rerr := os.Rename(moved, goroot); rerr != nil { @@ -651,18 +708,23 @@ func (t *tester) registerTests() { }) } + // Stub out following test on alpine until 54354 resolved. + builderName := os.Getenv("GO_BUILDER_NAME") + disablePIE := strings.HasSuffix(builderName, "-alpine") + // Test internal linking of PIE binaries where it is supported. - if t.internalLinkPIE() { + if t.internalLinkPIE() && !disablePIE { t.tests = append(t.tests, distTest{ name: "pie_internal", heading: "internal linking of -buildmode=pie", fn: func(dt *distTest) error { - t.addCmd(dt, "src", t.goTest(), "reflect", "-buildmode=pie", "-ldflags=-linkmode=internal", t.timeout(60)) + cmd := t.addCmd(dt, "src", t.goTest(), "reflect", "-buildmode=pie", "-ldflags=-linkmode=internal", t.timeout(60)) + setEnv(cmd, "CGO_ENABLED", "0") return nil }, }) // Also test a cgo package. - if t.cgoEnabled && t.internalLink() { + if t.cgoEnabled && t.internalLink() && !disablePIE { t.tests = append(t.tests, distTest{ name: "pie_internal_cgo", heading: "internal linking of -buildmode=pie", @@ -717,7 +779,7 @@ func (t *tester) registerTests() { name: "swig_stdio", heading: "../misc/swig/stdio", fn: func(dt *distTest) error { - t.addCmd(dt, "misc/swig/stdio", t.goTest()) + t.addCmd(dt, "misc/swig/stdio", t.goTest(), ".") return nil }, }) @@ -727,7 +789,7 @@ func (t *tester) registerTests() { name: "swig_callback", heading: "../misc/swig/callback", fn: func(dt *distTest) error { - t.addCmd(dt, "misc/swig/callback", t.goTest()) + t.addCmd(dt, "misc/swig/callback", t.goTest(), ".") return nil }, }, @@ -735,12 +797,10 @@ func (t *tester) registerTests() { name: "swig_callback_lto", heading: "../misc/swig/callback", fn: func(dt *distTest) error { - cmd := t.addCmd(dt, "misc/swig/callback", t.goTest()) - cmd.Env = append(os.Environ(), - "CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - "CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - "CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option", - ) + cmd := t.addCmd(dt, "misc/swig/callback", t.goTest(), ".") + setEnv(cmd, "CGO_CFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") + setEnv(cmd, "CGO_CXXFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") + setEnv(cmd, "CGO_LDFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option") return nil }, }, @@ -777,12 +837,11 @@ func (t *tester) registerTests() { if t.supportedBuildmode("plugin") { t.registerTest("testplugin", "../misc/cgo/testplugin", t.goTest(), t.timeout(600), ".") } - if gohostos == "linux" && goarch == "amd64" { + if gohostos == "linux" && (goarch == "amd64" || goarch == "ppc64le") { t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", ".") } - if goos == "linux" && goarch != "ppc64le" { + if goos == "linux" { // because syscall.SysProcAttr struct used in misc/cgo/testsanitizers is only built on linux. - // Some inconsistent failures happen on ppc64le so disable for now. t.registerHostTest("testsanitizers", "../misc/cgo/testsanitizers", "misc/cgo/testsanitizers", ".") } if t.hasBash() && goos != "android" && !t.iOS() && gohostos != "windows" { @@ -819,12 +878,12 @@ func (t *tester) registerTests() { }) } } - // Only run the API check on fast development platforms. Android, iOS, and JS - // are always cross-compiled, and the filesystems on our only plan9 builders - // are too slow to complete in a reasonable timeframe. Every platform checks - // the API on every GOOS/GOARCH/CGO_ENABLED combination anyway, so we really - // only need to run this check once anywhere to get adequate coverage. - if goos != "android" && !t.iOS() && goos != "js" && goos != "plan9" { + // Only run the API check on fast development platforms. + // Every platform checks the API on every GOOS/GOARCH/CGO_ENABLED combination anyway, + // so we really only need to run this check once anywhere to get adequate coverage. + // To help developers avoid trybot-only failures, we try to run on typical developer machines + // which is darwin/linux/windows and amd64/arm64. + if (goos == "darwin" || goos == "linux" || goos == "windows") && (goarch == "amd64" || goarch == "arm64") { t.tests = append(t.tests, distTest{ name: "api", heading: "API check", @@ -892,9 +951,9 @@ func (t *tester) registerSeqTest(name, dirBanner string, cmdline ...interface{}) func (t *tester) bgDirCmd(dir, bin string, args ...string) *exec.Cmd { cmd := exec.Command(bin, args...) if filepath.IsAbs(dir) { - cmd.Dir = dir + setDir(cmd, dir) } else { - cmd.Dir = filepath.Join(goroot, dir) + setDir(cmd, filepath.Join(goroot, dir)) } return cmd } @@ -951,7 +1010,11 @@ func flattenCmdline(cmdline []interface{}) (bin string, args []string) { } list = out - return list[0], list[1:] + bin = list[0] + if bin == "go" { + bin = gorootBinGo + } + return bin, list[1:] } func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.Cmd { @@ -983,7 +1046,7 @@ func (t *tester) extLink() bool { "darwin-amd64", "darwin-arm64", "dragonfly-amd64", "freebsd-386", "freebsd-amd64", "freebsd-arm", - "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-riscv64", "linux-s390x", + "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-loong64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-riscv64", "linux-s390x", "netbsd-386", "netbsd-amd64", "openbsd-386", "openbsd-amd64", "windows-386", "windows-amd64": @@ -997,11 +1060,6 @@ func (t *tester) internalLink() bool { // linkmode=internal fails on dragonfly since errno is a TLS relocation. return false } - if gohostarch == "ppc64le" { - // linkmode=internal fails on ppc64le because cmd/link doesn't - // handle the TOC correctly (issue 15409). - return false - } if goos == "android" { return false } @@ -1014,7 +1072,7 @@ func (t *tester) internalLink() bool { // Internally linking cgo is incomplete on some architectures. // https://golang.org/issue/10373 // https://golang.org/issue/14449 - if goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" { + if goarch == "loong64" || goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" || goarch == "riscv64" { return false } if goos == "aix" { @@ -1027,7 +1085,7 @@ func (t *tester) internalLink() bool { func (t *tester) internalLinkPIE() bool { switch goos + "-" + goarch { case "darwin-amd64", "darwin-arm64", - "linux-amd64", "linux-arm64", + "linux-amd64", "linux-arm64", "linux-ppc64le", "android-arm64", "windows-amd64", "windows-386", "windows-arm": return true @@ -1045,7 +1103,7 @@ func (t *tester) supportedBuildmode(mode string) bool { switch pair { case "aix-ppc64", "darwin-amd64", "darwin-arm64", "ios-arm64", - "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x", + "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x", "freebsd-amd64", "windows-amd64", "windows-386": return true @@ -1053,7 +1111,7 @@ func (t *tester) supportedBuildmode(mode string) bool { return false case "c-shared": switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x", + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x", "darwin-amd64", "darwin-arm64", "freebsd-amd64", "android-arm", "android-arm64", "android-386", @@ -1068,10 +1126,8 @@ func (t *tester) supportedBuildmode(mode string) bool { } return false case "plugin": - // linux-arm64 is missing because it causes the external linker - // to crash, see https://golang.org/issue/17138 switch pair { - case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le": + case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-s390x", "linux-ppc64le": return true case "darwin-amd64", "darwin-arm64": return true @@ -1112,7 +1168,7 @@ func (t *tester) registerHostTest(name, heading, dir, pkg string) { } func (t *tester) runHostTest(dir, pkg string) error { - out, err := exec.Command("go", "env", "GOEXE", "GOTMPDIR").Output() + out, err := exec.Command(gorootBinGo, "env", "GOEXE", "GOTMPDIR").Output() if err != nil { return err } @@ -1132,24 +1188,25 @@ func (t *tester) runHostTest(dir, pkg string) error { defer os.Remove(f.Name()) cmd := t.dirCmd(dir, t.goTest(), "-c", "-o", f.Name(), pkg) - cmd.Env = append(os.Environ(), "GOARCH="+gohostarch, "GOOS="+gohostos) + setEnv(cmd, "GOARCH", gohostarch) + setEnv(cmd, "GOOS", gohostos) if err := cmd.Run(); err != nil { return err } - return t.dirCmd(dir, f.Name(), "-test.short="+short()).Run() + return t.dirCmd(dir, f.Name(), "-test.short="+short(), "-test.timeout="+t.timeoutDuration(300).String()).Run() } func (t *tester) cgoTest(dt *distTest) error { - cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=auto") + cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), ".") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto") - // Skip internal linking cases on linux/arm64 to support GCC-9.4 and above. - // See issue #39466. - skipInternalLink := goarch == "arm64" && goos == "linux" + // Stub out various buildmode=pie tests on alpine until 54354 resolved. + builderName := os.Getenv("GO_BUILDER_NAME") + disablePIE := strings.HasSuffix(builderName, "-alpine") - if t.internalLink() && !skipInternalLink { - cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal") - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=internal") + if t.internalLink() { + cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal", ".") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=internal") } pair := gohostos + "-" + goarch @@ -1160,15 +1217,16 @@ func (t *tester) cgoTest(dt *distTest) error { if !t.extLink() { break } - cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") + cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), ".") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external") + + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s", ".") - cmd = t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s") + if t.supportedBuildmode("pie") && !disablePIE { - if t.supportedBuildmode("pie") { - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", ".") if t.internalLink() && t.internalLinkPIE() { - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie") + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie", ".") } } @@ -1180,14 +1238,14 @@ func (t *tester) cgoTest(dt *distTest) error { "netbsd-386", "netbsd-amd64", "openbsd-386", "openbsd-amd64", "openbsd-arm", "openbsd-arm64", "openbsd-mips64": - cmd := t.addCmd(dt, "misc/cgo/test", t.goTest()) - cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external") + cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), ".") + setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external") // cgo should be able to cope with both -g arguments and colored // diagnostics. - cmd.Env = append(cmd.Env, "CGO_CFLAGS=-g0 -fdiagnostics-color") + setEnv(cmd, "CGO_CFLAGS", "-g0 -fdiagnostics-color") - t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto") - t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external") + t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto", ".") + t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external", ".") switch pair { case "aix-ppc64", "netbsd-386", "netbsd-amd64": @@ -1206,28 +1264,28 @@ func (t *tester) cgoTest(dt *distTest) error { fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.") } else { if goos != "android" { - t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) + t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`, ".") } - t.addCmd(dt, "misc/cgo/nocgo", t.goTest()) - t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external`) + t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), ".") + t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external`, ".") if goos != "android" { - t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) + t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`, ".") + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`, ".") // -static in CGO_LDFLAGS triggers a different code path // than -static in -extldflags, so test both. // See issue #16651. - cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static") - cmd.Env = append(os.Environ(), "CGO_LDFLAGS=-static -pthread") + cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static", ".") + setEnv(cmd, "CGO_LDFLAGS", "-static -pthread") } } - if t.supportedBuildmode("pie") { - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie") - if t.internalLink() && t.internalLinkPIE() && !skipInternalLink { - t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie") + if t.supportedBuildmode("pie") && !disablePIE { + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", ".") + if t.internalLink() && t.internalLinkPIE() { + t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie", "-ldflags=-linkmode=internal", "-tags=internal,internal_pie", ".") } - t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie") - t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-buildmode=pie") + t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie", ".") + t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-buildmode=pie", ".") } } } @@ -1456,7 +1514,7 @@ func (t *tester) raceTest(dt *distTest) error { // We shouldn't need to redo all of misc/cgo/test too. // The race buildler will take care of this. // cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-race") - // cmd.Env = append(os.Environ(), "GOTRACEBACK=2") + // setEnv(cmd, "GOTRACEBACK", "2") } if t.extLink() { // Test with external linking; see issue 9133. @@ -1486,7 +1544,8 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error { }) cmd := t.dirCmd("test", "go", "build", "-o", runtest.exe, "run.go") - cmd.Env = append(os.Environ(), "GOOS="+gohostos, "GOARCH="+gohostarch) + setEnv(cmd, "GOOS", gohostos) + setEnv(cmd, "GOARCH", gohostarch) runtest.err = cmd.Run() }) if runtest.err != nil { @@ -1650,7 +1709,7 @@ func (t *tester) runPrecompiledStdTest(timeout time.Duration) error { bin := t.prebuiltGoPackageTestBinary() fmt.Fprintf(os.Stderr, "# %s: using pre-built %s...\n", stdMatches[0], bin) cmd := exec.Command(bin, "-test.short="+short(), "-test.timeout="+timeout.String()) - cmd.Dir = filepath.Dir(bin) + setDir(cmd, filepath.Dir(bin)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { @@ -1678,7 +1737,7 @@ func (t *tester) runPrecompiledStdTest(timeout time.Duration) error { func raceDetectorSupported(goos, goarch string) bool { switch goos { case "linux": - return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" + return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x" case "darwin": return goarch == "amd64" || goarch == "arm64" case "freebsd", "netbsd", "openbsd", "windows": diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index df60145d1e21e8..ee8ba910c7ded8 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -71,8 +71,12 @@ func run(dir string, mode int, cmd ...string) string { errprintf("run: %s\n", strings.Join(cmd, " ")) } - xcmd := exec.Command(cmd[0], cmd[1:]...) - xcmd.Dir = dir + bin := cmd[0] + if bin == "go" { + bin = gorootBinGo + } + xcmd := exec.Command(bin, cmd[1:]...) + setDir(xcmd, dir) var data []byte var err error @@ -172,6 +176,9 @@ func bgwait(wg *sync.WaitGroup) { select { case <-done: case <-dying: + // Don't return to the caller, to avoid reporting additional errors + // to the user. + select {} } } diff --git a/src/cmd/doc/dirs.go b/src/cmd/doc/dirs.go index 661624cfe4c168..60ad6d30e6a99b 100644 --- a/src/cmd/doc/dirs.go +++ b/src/cmd/doc/dirs.go @@ -7,9 +7,9 @@ package main import ( "bytes" "fmt" - exec "internal/execabs" "log" "os" + "os/exec" "path/filepath" "regexp" "strings" @@ -41,12 +41,31 @@ var dirs Dirs // dirsInit starts the scanning of package directories in GOROOT and GOPATH. Any // extra paths passed to it are included in the channel. func dirsInit(extra ...Dir) { + if buildCtx.GOROOT == "" { + stdout, err := exec.Command("go", "env", "GOROOT").Output() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + log.Fatalf("failed to determine GOROOT: $GOROOT is not set and 'go env GOROOT' failed:\n%s", ee.Stderr) + } + log.Fatalf("failed to determine GOROOT: $GOROOT is not set and could not run 'go env GOROOT':\n\t%s", err) + } + buildCtx.GOROOT = string(bytes.TrimSpace(stdout)) + } + dirs.hist = make([]Dir, 0, 1000) dirs.hist = append(dirs.hist, extra...) dirs.scan = make(chan Dir) go dirs.walk(codeRoots()) } +// goCmd returns the "go" command path corresponding to buildCtx.GOROOT. +func goCmd() string { + if buildCtx.GOROOT == "" { + return "go" + } + return filepath.Join(buildCtx.GOROOT, "bin", "go") +} + // Reset puts the scan back at the beginning. func (d *Dirs) Reset() { d.offset = 0 @@ -170,11 +189,11 @@ func findCodeRoots() []Dir { if !testGOPATH { // Check for use of modules by 'go env GOMOD', // which reports a go.mod file path if modules are enabled. - stdout, _ := exec.Command("go", "env", "GOMOD").Output() + stdout, _ := exec.Command(goCmd(), "env", "GOMOD").Output() gomod := string(bytes.TrimSpace(stdout)) usingModules = len(gomod) > 0 - if usingModules { + if usingModules && buildCtx.GOROOT != "" { list = append(list, Dir{dir: filepath.Join(buildCtx.GOROOT, "src"), inModule: true}, Dir{importPath: "cmd", dir: filepath.Join(buildCtx.GOROOT, "src", "cmd"), inModule: true}) @@ -190,7 +209,9 @@ func findCodeRoots() []Dir { } if !usingModules { - list = append(list, Dir{dir: filepath.Join(buildCtx.GOROOT, "src")}) + if buildCtx.GOROOT != "" { + list = append(list, Dir{dir: filepath.Join(buildCtx.GOROOT, "src")}) + } for _, root := range splitGopath() { list = append(list, Dir{dir: filepath.Join(root, "src")}) } @@ -217,15 +238,11 @@ func findCodeRoots() []Dir { return list } - cmd := exec.Command("go", "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all") + cmd := exec.Command(goCmd(), "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all") cmd.Stderr = os.Stderr out, _ := cmd.Output() for _, line := range strings.Split(string(out), "\n") { - i := strings.Index(line, "\t") - if i < 0 { - continue - } - path, dir := line[:i], line[i+1:] + path, dir, _ := strings.Cut(line, "\t") if dir != "" { list = append(list, Dir{importPath: path, dir: dir, inModule: true}) } @@ -250,7 +267,7 @@ func vendorEnabled() (*moduleJSON, bool, error) { return nil, false, err } - stdout, _ := exec.Command("go", "env", "GOFLAGS").Output() + stdout, _ := exec.Command(goCmd(), "env", "GOFLAGS").Output() goflags := string(bytes.TrimSpace(stdout)) matches := modFlagRegexp.FindStringSubmatch(goflags) var modFlag string @@ -284,7 +301,7 @@ func getMainModuleAnd114() (*moduleJSON, bool, error) { {{.GoVersion}} {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} ` - cmd := exec.Command("go", "list", "-m", "-f", format) + cmd := exec.Command(goCmd(), "list", "-m", "-f", format) cmd.Stderr = os.Stderr stdout, err := cmd.Output() if err != nil { diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go index af7793133ef68b..6a259ae19e35bf 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/doc/doc_test.go @@ -7,6 +7,9 @@ package main import ( "bytes" "flag" + "go/build" + "internal/testenv" + "log" "os" "path/filepath" "regexp" @@ -20,6 +23,12 @@ func TestMain(m *testing.M) { buildCtx.GOPATH = "" testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt + // Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was + // built with -trimpath). dirsInit would identify it using 'go env GOROOT', + // but we can't be sure that the 'go' in $PATH is the right one either. + buildCtx.GOROOT = testenv.GOROOT(nil) + build.Default.GOROOT = testenv.GOROOT(nil) + // Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test. // Normally testdata directories are ignored, but sending it to dirs.scan directly is // a hack that works around the check. @@ -125,6 +134,9 @@ var tests = []test{ `func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function. `var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`, // Long list of arguments. `type T1 = T2`, // Type alias + `type SimpleConstraint interface{ ... }`, + `type TildeConstraint interface{ ... }`, + `type StructConstraint interface{ ... }`, }, []string{ `const internalConstant = 2`, // No internal constants. @@ -199,6 +211,9 @@ var tests = []test{ `Comment about exported method`, `type T1 = T2`, `type T2 int`, + `type SimpleConstraint interface {`, + `type TildeConstraint interface {`, + `type StructConstraint interface {`, }, []string{ `constThree`, @@ -822,13 +837,19 @@ var tests = []test{ func TestDoc(t *testing.T) { maybeSkip(t) + defer log.SetOutput(log.Writer()) for _, test := range tests { var b bytes.Buffer var flagSet flag.FlagSet + var logbuf bytes.Buffer + log.SetOutput(&logbuf) err := do(&b, &flagSet, test.args) if err != nil { t.Fatalf("%s %v: %s\n", test.name, test.args, err) } + if logbuf.Len() > 0 { + t.Errorf("%s %v: unexpected log messages:\n%s", test.name, test.args, logbuf.Bytes()) + } output := b.Bytes() failed := false for j, yes := range test.yes { @@ -861,7 +882,9 @@ func TestDoc(t *testing.T) { } // Test the code to try multiple packages. Our test case is +// // go doc rand.Float64 +// // This needs to find math/rand.Float64; however crypto/rand, which doesn't // have the symbol, usually appears first in the directory listing. func TestMultiplePackages(t *testing.T) { @@ -918,11 +941,15 @@ func TestMultiplePackages(t *testing.T) { } // Test the code to look up packages when given two args. First test case is +// // go doc binary BigEndian +// // This needs to find encoding/binary.BigEndian, which means // finding the package encoding/binary given only "binary". // Second case is +// // go doc rand Float64 +// // which again needs to find math/rand and not give up after crypto/rand, // which has no such function. func TestTwoArgLookup(t *testing.T) { @@ -985,7 +1012,7 @@ func TestDotSlashLookup(t *testing.T) { if err := os.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text")); err != nil { t.Fatal(err) } - var b bytes.Buffer + var b strings.Builder var flagSet flag.FlagSet err = do(&b, &flagSet, []string{"./template"}) if err != nil { @@ -1003,7 +1030,7 @@ func TestDotSlashLookup(t *testing.T) { // when there should be no output at all. Issue 37969. func TestNoPackageClauseWhenNoMatch(t *testing.T) { maybeSkip(t) - var b bytes.Buffer + var b strings.Builder var flagSet flag.FlagSet err := do(&b, &flagSet, []string{"template.ZZZ"}) // Expect an error. diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go index 0499c403695247..3c45dd76dfa38d 100644 --- a/src/cmd/doc/main.go +++ b/src/cmd/doc/main.go @@ -5,20 +5,25 @@ // Doc (usually run as go doc) accepts zero, one or two arguments. // // Zero arguments: +// // go doc +// // Show the documentation for the package in the current directory. // // One argument: +// // go doc // go doc [.] // go doc [.][.] // go doc [.][.] +// // The first item in this list that succeeds is the one whose documentation // is printed. If there is a symbol but no package, the package in the current // directory is chosen. However, if the argument begins with a capital // letter it is always assumed to be a symbol in the current directory. // // Two arguments: +// // go doc [.] // // Show the documentation for the package, symbol, and method or field. The @@ -110,6 +115,13 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { if buildPackage == nil { return fmt.Errorf("no such package: %s", userPath) } + + // The builtin package needs special treatment: its symbols are lower + // case but we want to see them, always. + if buildPackage.ImportPath == "builtin" { + unexported = true + } + symbol, method = parseSymbol(sym) pkg := parsePackage(writer, buildPackage, userPath) paths = append(paths, pkg.prettyPath()) @@ -128,12 +140,6 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { panic(e) }() - // The builtin package needs special treatment: its symbols are lower - // case but we want to see them, always. - if pkg.build.ImportPath == "builtin" { - unexported = true - } - // We have a package. if showAll && symbol == "" { pkg.allDoc() diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go index 587f0bdc1468f2..4cebdc957ada42 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/doc/pkg.go @@ -25,8 +25,7 @@ import ( ) const ( - punchedCardWidth = 80 // These things just won't leave us alone. - indentedWidth = punchedCardWidth - len(indent) + punchedCardWidth = 80 indent = " " ) @@ -44,6 +43,14 @@ type Package struct { buf pkgBuffer } +func (p *Package) ToText(w io.Writer, text, prefix, codePrefix string) { + d := p.doc.Parser().Parse(text) + pr := p.doc.Printer() + pr.TextPrefix = prefix + pr.TextCodePrefix = codePrefix + w.Write(pr.Text(d)) +} + // pkgBuffer is a wrapper for bytes.Buffer that prints a package clause the // first time Write is called. type pkgBuffer struct { @@ -89,9 +96,11 @@ func (pkg *Package) prettyPath() string { // Also convert everything to slash-separated paths for uniform handling. path = filepath.Clean(filepath.ToSlash(pkg.build.Dir)) // Can we find a decent prefix? - goroot := filepath.Join(buildCtx.GOROOT, "src") - if p, ok := trim(path, filepath.ToSlash(goroot)); ok { - return p + if buildCtx.GOROOT != "" { + goroot := filepath.Join(buildCtx.GOROOT, "src") + if p, ok := trim(path, filepath.ToSlash(goroot)); ok { + return p + } } for _, gopath := range splitGopath() { if p, ok := trim(path, filepath.ToSlash(gopath)); ok { @@ -122,7 +131,7 @@ func trim(path, prefix string) (string, bool) { // main do function, so it doesn't cause an exit. Allows testing to work // without running a subprocess. The log prefix will be added when // logged in main; it is not added here. -func (pkg *Package) Fatalf(format string, args ...interface{}) { +func (pkg *Package) Fatalf(format string, args ...any) { panic(PackageError(fmt.Sprintf(format, args...))) } @@ -209,7 +218,7 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag return p } -func (pkg *Package) Printf(format string, args ...interface{}) { +func (pkg *Package) Printf(format string, args ...any) { fmt.Fprintf(&pkg.buf, format, args...) } @@ -235,7 +244,7 @@ func (pkg *Package) newlines(n int) { // clears the stuff we don't want to print anyway. It's a bit of a magic trick. func (pkg *Package) emit(comment string, node ast.Node) { if node != nil { - var arg interface{} = node + var arg any = node if showSrc { // Need an extra little dance to get internal comments to appear. arg = &printer.CommentedNode{ @@ -249,7 +258,7 @@ func (pkg *Package) emit(comment string, node ast.Node) { } if comment != "" && !showSrc { pkg.newlines(1) - doc.ToText(&pkg.buf, comment, indent, indent+indent, indentedWidth) + pkg.ToText(&pkg.buf, comment, indent, indent+indent) pkg.newlines(2) // Blank line after comment to separate from next item. } else { pkg.newlines(1) @@ -315,9 +324,7 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { recv = "(" + recv + ") " } fnc := pkg.oneLineNodeDepth(n.Type, depth) - if strings.Index(fnc, "func") == 0 { - fnc = fnc[4:] - } + fnc = strings.TrimPrefix(fnc, "func") return fmt.Sprintf("func %s%s%s", recv, name, fnc) case *ast.TypeSpec: @@ -325,7 +332,8 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { if n.Assign.IsValid() { sep = " = " } - return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth)) + tparams := pkg.formatTypeParams(n.TypeParams, depth) + return fmt.Sprintf("type %s%s%s%s", n.Name.Name, tparams, sep, pkg.oneLineNodeDepth(n.Type, depth)) case *ast.FuncType: var params []string @@ -344,15 +352,16 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { } } + tparam := pkg.formatTypeParams(n.TypeParams, depth) param := joinStrings(params) if len(results) == 0 { - return fmt.Sprintf("func(%s)", param) + return fmt.Sprintf("func%s(%s)", tparam, param) } result := joinStrings(results) if !needParens { - return fmt.Sprintf("func(%s) %s", param, result) + return fmt.Sprintf("func%s(%s) %s", tparam, param, result) } - return fmt.Sprintf("func(%s) (%s)", param, result) + return fmt.Sprintf("func%s(%s) (%s)", tparam, param, result) case *ast.StructType: if n.Fields == nil || len(n.Fields.List) == 0 { @@ -411,7 +420,7 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { default: // As a fallback, use default formatter for all unknown node types. - buf := new(bytes.Buffer) + buf := new(strings.Builder) format.Node(buf, pkg.fs, node) s := buf.String() if strings.Contains(s, "\n") { @@ -421,6 +430,17 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { } } +func (pkg *Package) formatTypeParams(list *ast.FieldList, depth int) string { + if list.NumFields() == 0 { + return "" + } + var tparams []string + for _, field := range list.List { + tparams = append(tparams, pkg.oneLineField(field, depth)) + } + return "[" + joinStrings(tparams) + "]" +} + // oneLineField returns a one-line summary of the field. func (pkg *Package) oneLineField(field *ast.Field, depth int) string { var names []string @@ -450,7 +470,7 @@ func joinStrings(ss []string) string { // allDoc prints all the docs for the package. func (pkg *Package) allDoc() { pkg.Printf("") // Trigger the package clause; we know the package exists. - doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) + pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent) pkg.newlines(1) printed := make(map[*ast.GenDecl]bool) @@ -510,7 +530,7 @@ func (pkg *Package) allDoc() { func (pkg *Package) packageDoc() { pkg.Printf("") // Trigger the package clause; we know the package exists. if !short { - doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) + pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent) pkg.newlines(1) } @@ -854,6 +874,7 @@ func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldLis if len(names) == 0 { // Embedded type. Use the name of the type. It must be of the form ident or // pkg.ident (for structs and interfaces), or *ident or *pkg.ident (structs only). + // Or a type embedded in a constraint. // Nothing else is allowed. ty := field.Type if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok { @@ -861,6 +882,7 @@ func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldLis // embedded types in structs. ty = se.X } + constraint := false switch ident := ty.(type) { case *ast.Ident: if isInterface && ident.Name == "error" && ident.Obj == nil { @@ -874,8 +896,12 @@ func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldLis case *ast.SelectorExpr: // An embedded type may refer to a type in another package. names = []*ast.Ident{ident.Sel} + default: + // An approximation or union or type + // literal in an interface. + constraint = true } - if names == nil { + if names == nil && !constraint { // Can only happen if AST is incorrect. Safe to continue with a nil list. log.Print("invalid program: unexpected type for embedded field") } @@ -1014,9 +1040,9 @@ func (pkg *Package) printFieldDoc(symbol, fieldName string) bool { if field.Doc != nil { // To present indented blocks in comments correctly, process the comment as // a unit before adding the leading // to each line. - docBuf := bytes.Buffer{} - doc.ToText(&docBuf, field.Doc.Text(), "", indent, indentedWidth) - scanner := bufio.NewScanner(&docBuf) + docBuf := new(bytes.Buffer) + pkg.ToText(docBuf, field.Doc.Text(), "", indent) + scanner := bufio.NewScanner(docBuf) for scanner.Scan() { fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes()) } diff --git a/src/cmd/doc/testdata/nested/ignore.go b/src/cmd/doc/testdata/nested/ignore.go index c497f1b5bc4100..5fa811d0a859c1 100644 --- a/src/cmd/doc/testdata/nested/ignore.go +++ b/src/cmd/doc/testdata/nested/ignore.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore // Ignored package diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/doc/testdata/pkg.go index 5ece8325651eba..a693c749189799 100644 --- a/src/cmd/doc/testdata/pkg.go +++ b/src/cmd/doc/testdata/pkg.go @@ -238,3 +238,15 @@ type ExportedFormattedType struct { // Text after pre-formatted block. ExportedField int } + +type SimpleConstraint interface { + ~int | ~float64 +} + +type TildeConstraint interface { + ~int +} + +type StructConstraint interface { + struct { F int } +} diff --git a/src/cmd/fix/buildtag.go b/src/cmd/fix/buildtag.go new file mode 100644 index 00000000000000..5f4fbfef16f15b --- /dev/null +++ b/src/cmd/fix/buildtag.go @@ -0,0 +1,51 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "strings" +) + +func init() { + register(buildtagFix) +} + +const buildtagGoVersionCutoff = 1_18 + +var buildtagFix = fix{ + name: "buildtag", + date: "2021-08-25", + f: buildtag, + desc: `Remove +build comments from modules using Go 1.18 or later`, +} + +func buildtag(f *ast.File) bool { + if goVersion < buildtagGoVersionCutoff { + return false + } + + // File is already gofmt-ed, so we know that if there are +build lines, + // they are in a comment group that starts with a //go:build line followed + // by a blank line. While we cannot delete comments from an AST and + // expect consistent output in general, this specific case - deleting only + // some lines from a comment block - does format correctly. + fixed := false + for _, g := range f.Comments { + sawGoBuild := false + for i, c := range g.List { + if strings.HasPrefix(c.Text, "//go:build ") { + sawGoBuild = true + } + if sawGoBuild && strings.HasPrefix(c.Text, "// +build ") { + g.List = g.List[:i] + fixed = true + break + } + } + } + + return fixed +} diff --git a/src/cmd/fix/buildtag_test.go b/src/cmd/fix/buildtag_test.go new file mode 100644 index 00000000000000..1c6efbe9e03262 --- /dev/null +++ b/src/cmd/fix/buildtag_test.go @@ -0,0 +1,34 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(buildtagTests, buildtag) +} + +var buildtagTests = []testCase{ + { + Name: "buildtag.oldGo", + Version: 1_10, + In: `//go:build yes +// +build yes + +package main +`, + }, + { + Name: "buildtag.new", + Version: 1_99, + In: `//go:build yes +// +build yes + +package main +`, + Out: `//go:build yes + +package main +`, + }, +} diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go index b47b06682add80..e4988b1c625b0f 100644 --- a/src/cmd/fix/cftype.go +++ b/src/cmd/fix/cftype.go @@ -24,9 +24,13 @@ var cftypeFix = fix{ } // Old state: -// type CFTypeRef unsafe.Pointer +// +// type CFTypeRef unsafe.Pointer +// // New state: -// type CFTypeRef uintptr +// +// type CFTypeRef uintptr +// // and similar for other *Ref types. // This fix finds nils initializing these types and replaces the nils with 0s. func cftypefix(f *ast.File) bool { @@ -45,8 +49,8 @@ func typefix(f *ast.File, badType func(string) bool) bool { // step 1: Find all the nils with the offending types. // Compute their replacement. - badNils := map[interface{}]ast.Expr{} - walk(f, func(n interface{}) { + badNils := map[any]ast.Expr{} + walk(f, func(n any) { if i, ok := n.(*ast.Ident); ok && i.Name == "nil" && badType(typeof[n]) { badNils[n] = &ast.BasicLit{ValuePos: i.NamePos, Kind: token.INT, Value: "0"} } @@ -58,12 +62,12 @@ func typefix(f *ast.File, badType func(string) bool) bool { if len(badNils) > 0 { exprType := reflect.TypeOf((*ast.Expr)(nil)).Elem() exprSliceType := reflect.TypeOf(([]ast.Expr)(nil)) - walk(f, func(n interface{}) { + walk(f, func(n any) { if n == nil { return } v := reflect.ValueOf(n) - if v.Type().Kind() != reflect.Ptr { + if v.Type().Kind() != reflect.Pointer { return } if v.IsNil() { @@ -99,7 +103,7 @@ func typefix(f *ast.File, badType func(string) bool) bool { // Now we need unsafe.Pointer as an intermediate cast. // (*unsafe.Pointer)(x) where x is type *bad -> (*unsafe.Pointer)(unsafe.Pointer(x)) // (*bad.type)(x) where x is type *unsafe.Pointer -> (*bad.type)(unsafe.Pointer(x)) - walk(f, func(n interface{}) { + walk(f, func(n any) { if n == nil { return } diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go index 0570169576b623..062eb792856285 100644 --- a/src/cmd/fix/doc.go +++ b/src/cmd/fix/doc.go @@ -8,6 +8,7 @@ newer ones. After you update to a new Go release, fix helps make the necessary changes to your programs. Usage: + go tool fix [-r name,...] [path ...] Without an explicit path, fix reads standard input and writes the @@ -30,7 +31,7 @@ Fix prints the full list of fixes it can apply in its help output; to see them, run go tool fix -help. Fix does not make backup copies of the files that it edits. -Instead, use a version control system's ``diff'' functionality to inspect +Instead, use a version control system's “diff” functionality to inspect the changes that fix makes before committing them. */ package main diff --git a/src/cmd/fix/egltype.go b/src/cmd/fix/egltype.go index cb0f7a73de1ddd..a096db6665a5fb 100644 --- a/src/cmd/fix/egltype.go +++ b/src/cmd/fix/egltype.go @@ -22,9 +22,13 @@ var eglFixDisplay = fix{ } // Old state: -// type EGLDisplay unsafe.Pointer +// +// type EGLDisplay unsafe.Pointer +// // New state: -// type EGLDisplay uintptr +// +// type EGLDisplay uintptr +// // This fix finds nils initializing these types and replaces the nils with 0s. func eglfixDisp(f *ast.File) bool { return typefix(f, func(s string) bool { @@ -41,9 +45,13 @@ var eglFixConfig = fix{ } // Old state: -// type EGLConfig unsafe.Pointer +// +// type EGLConfig unsafe.Pointer +// // New state: -// type EGLConfig uintptr +// +// type EGLConfig uintptr +// // This fix finds nils initializing these types and replaces the nils with 0s. func eglfixConfig(f *ast.File) bool { return typefix(f, func(s string) bool { diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go index b49db375710c3c..7abdab28a8df21 100644 --- a/src/cmd/fix/fix.go +++ b/src/cmd/fix/fix.go @@ -43,15 +43,15 @@ func register(f fix) { // walk traverses the AST x, calling visit(y) for each node y in the tree but // also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt, // in a bottom-up traversal. -func walk(x interface{}, visit func(interface{})) { +func walk(x any, visit func(any)) { walkBeforeAfter(x, nop, visit) } -func nop(interface{}) {} +func nop(any) {} // walkBeforeAfter is like walk but calls before(x) before traversing // x's children and after(x) afterward. -func walkBeforeAfter(x interface{}, before, after func(interface{})) { +func walkBeforeAfter(x any, before, after func(any)) { before(x) switch n := x.(type) { @@ -125,6 +125,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { case *ast.IndexExpr: walkBeforeAfter(&n.X, before, after) walkBeforeAfter(&n.Index, before, after) + case *ast.IndexListExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Indices, before, after) case *ast.SliceExpr: walkBeforeAfter(&n.X, before, after) if n.Low != nil { @@ -156,6 +159,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { case *ast.StructType: walkBeforeAfter(&n.Fields, before, after) case *ast.FuncType: + if n.TypeParams != nil { + walkBeforeAfter(&n.TypeParams, before, after) + } walkBeforeAfter(&n.Params, before, after) if n.Results != nil { walkBeforeAfter(&n.Results, before, after) @@ -231,6 +237,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) { walkBeforeAfter(&n.Values, before, after) walkBeforeAfter(&n.Names, before, after) case *ast.TypeSpec: + if n.TypeParams != nil { + walkBeforeAfter(&n.TypeParams, before, after) + } walkBeforeAfter(&n.Type, before, after) case *ast.BadDecl: @@ -381,7 +390,7 @@ func renameTop(f *ast.File, old, new string) bool { // Rename top-level old to new, both unresolved names // (probably defined in another file) and names that resolve // to a declaration we renamed. - walk(f, func(n interface{}) { + walk(f, func(n any) { id, ok := n.(*ast.Ident) if ok && isTopName(id, old) { id.Name = new diff --git a/src/cmd/fix/gotypes.go b/src/cmd/fix/gotypes.go index 031f85c9cc5c18..6085816ada45c0 100644 --- a/src/cmd/fix/gotypes.go +++ b/src/cmd/fix/gotypes.go @@ -36,7 +36,7 @@ func fixGoExact(f *ast.File) bool { // This one is harder because the import name changes. // First find the import spec. var importSpec *ast.ImportSpec - walk(f, func(n interface{}) { + walk(f, func(n any) { if importSpec != nil { return } diff --git a/src/cmd/fix/jnitype.go b/src/cmd/fix/jnitype.go index 29abe0f0078302..111be8e70c6be3 100644 --- a/src/cmd/fix/jnitype.go +++ b/src/cmd/fix/jnitype.go @@ -21,9 +21,13 @@ var jniFix = fix{ } // Old state: -// type jobject *_jobject +// +// type jobject *_jobject +// // New state: -// type jobject uintptr +// +// type jobject uintptr +// // and similar for subtypes of jobject. // This fix finds nils initializing these types and replaces the nils with 0s. func jnifix(f *ast.File) bool { diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go index d055929aac0767..70874d4306e287 100644 --- a/src/cmd/fix/main.go +++ b/src/cmd/fix/main.go @@ -13,14 +13,14 @@ import ( "go/parser" "go/scanner" "go/token" + "internal/diff" "io" "io/fs" "os" "path/filepath" "sort" + "strconv" "strings" - - "cmd/internal/diff" ) var ( @@ -36,7 +36,12 @@ var forceRewrites = flag.String("force", "", var allowed, force map[string]bool -var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files") +var ( + doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files") + goVersionStr = flag.String("go", "", "go language version for files") + + goVersion int // 115 for go1.15 +) // enable for debugging fix failures const debug = false // display incorrectly reformatted source and exit @@ -63,6 +68,26 @@ func main() { flag.Usage = usage flag.Parse() + if *goVersionStr != "" { + if !strings.HasPrefix(*goVersionStr, "go") { + report(fmt.Errorf("invalid -go=%s", *goVersionStr)) + os.Exit(exitCode) + } + majorStr := (*goVersionStr)[len("go"):] + minorStr := "0" + if i := strings.Index(majorStr, "."); i >= 0 { + majorStr, minorStr = majorStr[:i], majorStr[i+len("."):] + } + major, err1 := strconv.Atoi(majorStr) + minor, err2 := strconv.Atoi(minorStr) + if err1 != nil || err2 != nil || major < 0 || major >= 100 || minor < 0 || minor >= 100 { + report(fmt.Errorf("invalid -go=%s", *goVersionStr)) + os.Exit(exitCode) + } + + goVersion = major*100 + minor + } + sort.Sort(byDate(fixes)) if *allowedRewrites != "" { @@ -116,7 +141,7 @@ func gofmtFile(f *ast.File) ([]byte, error) { func processFile(filename string, useStdin bool) error { var f *os.File var err error - var fixlog bytes.Buffer + var fixlog strings.Builder if useStdin { f = os.Stdin @@ -202,12 +227,7 @@ func processFile(filename string, useStdin bool) error { } if *doDiff { - data, err := diff.Diff("go-fix", src, newSrc) - if err != nil { - return fmt.Errorf("computing diff: %s", err) - } - fmt.Printf("diff %s fixed/%s\n", filename, filename) - os.Stdout.Write(data) + os.Stdout.Write(diff.Diff(filename, src, "fixed/"+filename, newSrc)) return nil } @@ -219,8 +239,8 @@ func processFile(filename string, useStdin bool) error { return os.WriteFile(f.Name(), newSrc, 0) } -func gofmt(n interface{}) string { - var gofmtBuf bytes.Buffer +func gofmt(n any) string { + var gofmtBuf strings.Builder if err := format.Node(&gofmtBuf, fset, n); err != nil { return "<" + err.Error() + ">" } diff --git a/src/cmd/fix/main_test.go b/src/cmd/fix/main_test.go index af16bcaa31ed94..755007bc0d353c 100644 --- a/src/cmd/fix/main_test.go +++ b/src/cmd/fix/main_test.go @@ -7,17 +7,17 @@ package main import ( "go/ast" "go/parser" + "internal/diff" "strings" "testing" - - "cmd/internal/diff" ) type testCase struct { - Name string - Fn func(*ast.File) bool - In string - Out string + Name string + Fn func(*ast.File) bool + Version int + In string + Out string } var testCases []testCase @@ -51,7 +51,7 @@ func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustB if s := string(outb); in != s && mustBeGofmt { t.Errorf("not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s", desc, in, desc, s) - tdiff(t, in, s) + tdiff(t, "want", in, "have", s) return } @@ -78,7 +78,16 @@ func TestRewrite(t *testing.T) { for _, tt := range testCases { tt := tt t.Run(tt.Name, func(t *testing.T) { - t.Parallel() + if tt.Version == 0 { + t.Parallel() + } else { + old := goVersion + goVersion = tt.Version + defer func() { + goVersion = old + }() + } + // Apply fix: should get tt.Out. out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true) if !ok { @@ -91,12 +100,15 @@ func TestRewrite(t *testing.T) { return } + if tt.Out == "" { + tt.Out = tt.In + } if out != tt.Out { t.Errorf("incorrect output.\n") if !strings.HasPrefix(tt.Name, "testdata/") { t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out) } - tdiff(t, out, tt.Out) + tdiff(t, "have", out, "want", tt.Out) return } @@ -119,17 +131,12 @@ func TestRewrite(t *testing.T) { if out2 != out { t.Errorf("changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s", out, out2) - tdiff(t, out, out2) + tdiff(t, "first", out, "second", out2) } }) } } -func tdiff(t *testing.T, a, b string) { - data, err := diff.Diff("go-fix-test", []byte(a), []byte(b)) - if err != nil { - t.Error(err) - return - } - t.Error(string(data)) +func tdiff(t *testing.T, aname, a, bname, b string) { + t.Errorf("%s", diff.Diff(aname, []byte(a), bname, []byte(b))) } diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go index 3e502bda07cb96..199fcf5bf59765 100644 --- a/src/cmd/fix/netipv6zone.go +++ b/src/cmd/fix/netipv6zone.go @@ -26,7 +26,7 @@ func netipv6zone(f *ast.File) bool { } fixed := false - walk(f, func(n interface{}) { + walk(f, func(n any) { cl, ok := n.(*ast.CompositeLit) if !ok { return diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go index 6d939968728113..bad69531964214 100644 --- a/src/cmd/fix/printerconfig.go +++ b/src/cmd/fix/printerconfig.go @@ -23,7 +23,7 @@ func printerconfig(f *ast.File) bool { } fixed := false - walk(f, func(n interface{}) { + walk(f, func(n any) { cl, ok := n.(*ast.CompositeLit) if !ok { return diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go index 39a53785b724d4..015a0eef2f8611 100644 --- a/src/cmd/fix/typecheck.go +++ b/src/cmd/fix/typecheck.go @@ -9,8 +9,8 @@ import ( "go/ast" "go/parser" "go/token" - exec "internal/execabs" "os" + "os/exec" "path/filepath" "reflect" "runtime" @@ -142,9 +142,9 @@ func (typ *Type) dot(cfg *TypeConfig, name string) string { // typeof maps AST nodes to type information in gofmt string form. // assign maps type strings to lists of expressions that were assigned // to values of another type that were assigned to that type. -func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, assign map[string][]interface{}) { - typeof = make(map[interface{}]string) - assign = make(map[string][]interface{}) +func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[any]string, assign map[string][]any) { + typeof = make(map[any]string) + assign = make(map[string][]any) cfg1 := &TypeConfig{} *cfg1 = *cfg // make copy so we can add locally copied := false @@ -296,7 +296,7 @@ func makeExprList(a []*ast.Ident) []ast.Expr { // Typecheck1 is the recursive form of typecheck. // It is like typecheck but adds to the information in typeof // instead of allocating a new map. -func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, assign map[string][]interface{}) { +func typecheck1(cfg *TypeConfig, f any, typeof map[any]string, assign map[string][]any) { // set sets the type of n to typ. // If isDecl is true, n is being declared. set := func(n ast.Expr, typ string, isDecl bool) { @@ -368,7 +368,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a // the curfn stack. var curfn []*ast.FuncType - before := func(n interface{}) { + before := func(n any) { // push function type on stack switch n := n.(type) { case *ast.FuncDecl: @@ -379,11 +379,11 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a } // After is the real type checker. - after := func(n interface{}) { + after := func(n any) { if n == nil { return } - if false && reflect.TypeOf(n).Kind() == reflect.Ptr { // debugging trace + if false && reflect.TypeOf(n).Kind() == reflect.Pointer { // debugging trace defer func() { if t := typeof[n]; t != "" { pos := fset.Position(n.(ast.Node).Pos()) @@ -544,8 +544,8 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") { // Lazy: assume there are no nested [] in the array // length or map key type. - if i := strings.Index(t, "]"); i >= 0 { - typeof[n] = t[i+1:] + if _, elem, ok := strings.Cut(t, "]"); ok { + typeof[n] = elem } } @@ -575,8 +575,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a t := expand(typeof[n]) if strings.HasPrefix(t, "[") { // array or slice // Lazy: assume there are no nested [] in the array length. - if i := strings.Index(t, "]"); i >= 0 { - et := t[i+1:] + if _, et, ok := strings.Cut(t, "]"); ok { for _, e := range n.Elts { if kv, ok := e.(*ast.KeyValueExpr); ok { e = kv.Value @@ -589,8 +588,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a } if strings.HasPrefix(t, "map[") { // map // Lazy: assume there are no nested [] in the map key type. - if i := strings.Index(t, "]"); i >= 0 { - kt, vt := t[4:i], t[i+1:] + if kt, vt, ok := strings.Cut(t[len("map["):], "]"); ok { for _, e := range n.Elts { if kv, ok := e.(*ast.KeyValueExpr); ok { if typeof[kv.Key] == "" { @@ -629,12 +627,10 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a key, value = "int", "rune" } else if strings.HasPrefix(t, "[") { key = "int" - if i := strings.Index(t, "]"); i >= 0 { - value = t[i+1:] - } + _, value, _ = strings.Cut(t, "]") } else if strings.HasPrefix(t, "map[") { - if i := strings.Index(t, "]"); i >= 0 { - key, value = t[4:i], t[i+1:] + if k, v, ok := strings.Cut(t[len("map["):], "]"); ok { + key, value = k, v } } changed := false diff --git a/src/cmd/go.mod b/src/cmd/go.mod index cd03968eedcf61..326992ddd2e491 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -1,15 +1,18 @@ module cmd -go 1.17 +go 1.20 require ( - github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a - github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect - golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e - golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect - golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a - golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect - golang.org/x/term v0.0.0-20210503060354-a79de5458b56 - golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 + golang.org/x/arch v0.0.0-20220722155209-00200b7164a7 + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 + golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 + golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa +) + +require ( + github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index d728acaec9925e..dd5852ef764367 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -1,45 +1,18 @@ -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a h1:jmAp/2PZAScNd62lTD3Mcb0Ey9FvIIJtLohPhtxZJ+Q= -github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e h1:pv3V0NlNSh5Q6AX/StwGLBjcLS7UN4m4Gq+V+uSecqM= -golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= -golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a h1:e8qnjKz4EE6OjRki9wTadWSIogINvq10sMcuBRORxMY= -golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q= -golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 h1:2XlR/j4I4xz5GQZI7zBjqTfezYyRIE2jD5IMousB2rg= -golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ= +github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2 h1:rcanfLhLDA8nozr/K289V1zcntHr3V+SHlXwzz1ZI2g= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +golang.org/x/arch v0.0.0-20220722155209-00200b7164a7 h1:VBQqJMNMRfQsWSiCTLgz9XjAfWlgnJAPv8nsp1HF8Tw= +golang.org/x/arch v0.0.0-20220722155209-00200b7164a7/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME= +golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa h1:uKcci2q7Qtp6nMTC/AAvfNUAldFtJuHWV9/5QWiypts= +golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 7f88d3216cf080..f8800eef732e9d 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -9,70 +9,69 @@ // // Usage: // -// go [arguments] +// go [arguments] // // The commands are: // -// bug start a bug report -// build compile packages and dependencies -// clean remove object files and cached files -// doc show documentation for package or symbol -// env print Go environment information -// fix update packages to use new APIs -// fmt gofmt (reformat) package sources -// generate generate Go files by processing source -// get add dependencies to current module and install them -// install compile and install packages and dependencies -// list list packages or modules -// mod module maintenance -// run compile and run Go program -// test test packages -// tool run specified go tool -// version print Go version -// vet report likely mistakes in packages +// bug start a bug report +// build compile packages and dependencies +// clean remove object files and cached files +// doc show documentation for package or symbol +// env print Go environment information +// fix update packages to use new APIs +// fmt gofmt (reformat) package sources +// generate generate Go files by processing source +// get add dependencies to current module and install them +// install compile and install packages and dependencies +// list list packages or modules +// mod module maintenance +// work workspace maintenance +// run compile and run Go program +// test test packages +// tool run specified go tool +// version print Go version +// vet report likely mistakes in packages // // Use "go help " for more information about a command. // // Additional help topics: // -// buildconstraint build constraints -// buildmode build modes -// c calling between Go and C -// cache build and test caching -// environment environment variables -// filetype file types -// go.mod the go.mod file -// gopath GOPATH environment variable -// gopath-get legacy GOPATH go get -// goproxy module proxy protocol -// importpath import path syntax -// modules modules, module versions, and more -// module-get module-aware go get -// module-auth module authentication using go.sum -// packages package lists and patterns -// private configuration for downloading non-public code -// testflag testing flags -// testfunc testing functions -// vcs controlling version control with GOVCS +// buildconstraint build constraints +// buildmode build modes +// c calling between Go and C +// cache build and test caching +// environment environment variables +// filetype file types +// go.mod the go.mod file +// gopath GOPATH environment variable +// gopath-get legacy GOPATH go get +// goproxy module proxy protocol +// importpath import path syntax +// modules modules, module versions, and more +// module-get module-aware go get +// module-auth module authentication using go.sum +// packages package lists and patterns +// private configuration for downloading non-public code +// testflag testing flags +// testfunc testing functions +// vcs controlling version control with GOVCS // // Use "go help " for more information about that topic. // -// -// Start a bug report +// # Start a bug report // // Usage: // -// go bug +// go bug // // Bug opens the default browser and starts a new bug report. // The report includes useful system information. // -// -// Compile packages and dependencies +// # Compile packages and dependencies // // Usage: // -// go build [-o output] [build flags] [packages] +// go build [-o output] [build flags] [packages] // // Build compiles the packages named by the import paths, // along with their dependencies, but it does not install the results. @@ -104,102 +103,113 @@ // The build flags are shared by the build, clean, get, install, list, run, // and test commands: // -// -a -// force rebuilding of packages that are already up-to-date. -// -n -// print the commands but do not run them. -// -p n -// the number of programs, such as build commands or -// test binaries, that can be run in parallel. -// The default is GOMAXPROCS, normally the number of CPUs available. -// -race -// enable data race detection. -// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, -// linux/ppc64le and linux/arm64 (only for 48-bit VMA). -// -msan -// enable interoperation with memory sanitizer. -// Supported only on linux/amd64, linux/arm64 -// and only with Clang/LLVM as the host C compiler. -// On linux/arm64, pie build mode will be used. -// -v -// print the names of packages as they are compiled. -// -work -// print the name of the temporary work directory and -// do not delete it when exiting. -// -x -// print the commands. -// -// -asmflags '[pattern=]arg list' -// arguments to pass on each go tool asm invocation. -// -buildmode mode -// build mode to use. See 'go help buildmode' for more. -// -compiler name -// name of compiler to use, as in runtime.Compiler (gccgo or gc). -// -gccgoflags '[pattern=]arg list' -// arguments to pass on each gccgo compiler/linker invocation. -// -gcflags '[pattern=]arg list' -// arguments to pass on each go tool compile invocation. -// -installsuffix suffix -// a suffix to use in the name of the package installation directory, -// in order to keep output separate from default builds. -// If using the -race flag, the install suffix is automatically set to race -// or, if set explicitly, has _race appended to it. Likewise for the -msan -// flag. Using a -buildmode option that requires non-default compile flags -// has a similar effect. -// -ldflags '[pattern=]arg list' -// arguments to pass on each go tool link invocation. -// -linkshared -// build code that will be linked against shared libraries previously -// created with -buildmode=shared. -// -mod mode -// module download mode to use: readonly, vendor, or mod. -// By default, if a vendor directory is present and the go version in go.mod -// is 1.14 or higher, the go command acts as if -mod=vendor were set. -// Otherwise, the go command acts as if -mod=readonly were set. -// See https://golang.org/ref/mod#build-commands for details. -// -modcacherw -// leave newly-created directories in the module cache read-write -// instead of making them read-only. -// -modfile file -// in module aware mode, read (and possibly write) an alternate go.mod -// file instead of the one in the module root directory. A file named -// "go.mod" must still be present in order to determine the module root -// directory, but it is not accessed. When -modfile is specified, an -// alternate go.sum file is also used: its path is derived from the -// -modfile flag by trimming the ".mod" extension and appending ".sum". -// -overlay file -// read a JSON config file that provides an overlay for build operations. -// The file is a JSON struct with a single field, named 'Replace', that -// maps each disk file path (a string) to its backing file path, so that -// a build will run as if the disk file path exists with the contents -// given by the backing file paths, or as if the disk file path does not -// exist if its backing file path is empty. Support for the -overlay flag -// has some limitations: importantly, cgo files included from outside the -// include path must be in the same directory as the Go package they are -// included from, and overlays will not appear when binaries and tests are -// run through go run and go test respectively. -// -pkgdir dir -// install and load all packages from dir instead of the usual locations. -// For example, when building with a non-standard configuration, -// use -pkgdir to keep generated packages in a separate location. -// -tags tag,list -// a comma-separated list of build tags to consider satisfied during the -// build. For more information about build tags, see the description of -// build constraints in the documentation for the go/build package. -// (Earlier versions of Go used a space-separated list, and that form -// is deprecated but still recognized.) -// -trimpath -// remove all file system paths from the resulting executable. -// Instead of absolute file system paths, the recorded file names -// will begin with either "go" (for the standard library), -// or a module path@version (when using modules), -// or a plain import path (when using GOPATH). -// -toolexec 'cmd args' -// a program to use to invoke toolchain programs like vet and asm. -// For example, instead of running asm, the go command will run -// 'cmd args /path/to/asm '. -// The TOOLEXEC_IMPORTPATH environment variable will be set, -// matching 'go list -f {{.ImportPath}}' for the package being built. +// -a +// force rebuilding of packages that are already up-to-date. +// -n +// print the commands but do not run them. +// -p n +// the number of programs, such as build commands or +// test binaries, that can be run in parallel. +// The default is GOMAXPROCS, normally the number of CPUs available. +// -race +// enable data race detection. +// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64, +// linux/ppc64le and linux/arm64 (only for 48-bit VMA). +// -msan +// enable interoperation with memory sanitizer. +// Supported only on linux/amd64, linux/arm64 +// and only with Clang/LLVM as the host C compiler. +// On linux/arm64, pie build mode will be used. +// -asan +// enable interoperation with address sanitizer. +// Supported only on linux/arm64, linux/amd64. +// Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher +// or Clang/LLVM 9 and higher. +// -v +// print the names of packages as they are compiled. +// -work +// print the name of the temporary work directory and +// do not delete it when exiting. +// -x +// print the commands. +// +// -asmflags '[pattern=]arg list' +// arguments to pass on each go tool asm invocation. +// -buildmode mode +// build mode to use. See 'go help buildmode' for more. +// -buildvcs +// Whether to stamp binaries with version control information +// ("true", "false", or "auto"). By default ("auto"), version control +// information is stamped into a binary if the main package, the main module +// containing it, and the current directory are all in the same repository. +// Use -buildvcs=false to always omit version control information, or +// -buildvcs=true to error out if version control information is available but +// cannot be included due to a missing tool or ambiguous directory structure. +// -compiler name +// name of compiler to use, as in runtime.Compiler (gccgo or gc). +// -gccgoflags '[pattern=]arg list' +// arguments to pass on each gccgo compiler/linker invocation. +// -gcflags '[pattern=]arg list' +// arguments to pass on each go tool compile invocation. +// -installsuffix suffix +// a suffix to use in the name of the package installation directory, +// in order to keep output separate from default builds. +// If using the -race flag, the install suffix is automatically set to race +// or, if set explicitly, has _race appended to it. Likewise for the -msan +// and -asan flags. Using a -buildmode option that requires non-default compile +// flags has a similar effect. +// -ldflags '[pattern=]arg list' +// arguments to pass on each go tool link invocation. +// -linkshared +// build code that will be linked against shared libraries previously +// created with -buildmode=shared. +// -mod mode +// module download mode to use: readonly, vendor, or mod. +// By default, if a vendor directory is present and the go version in go.mod +// is 1.14 or higher, the go command acts as if -mod=vendor were set. +// Otherwise, the go command acts as if -mod=readonly were set. +// See https://golang.org/ref/mod#build-commands for details. +// -modcacherw +// leave newly-created directories in the module cache read-write +// instead of making them read-only. +// -modfile file +// in module aware mode, read (and possibly write) an alternate go.mod +// file instead of the one in the module root directory. A file named +// "go.mod" must still be present in order to determine the module root +// directory, but it is not accessed. When -modfile is specified, an +// alternate go.sum file is also used: its path is derived from the +// -modfile flag by trimming the ".mod" extension and appending ".sum". +// -overlay file +// read a JSON config file that provides an overlay for build operations. +// The file is a JSON struct with a single field, named 'Replace', that +// maps each disk file path (a string) to its backing file path, so that +// a build will run as if the disk file path exists with the contents +// given by the backing file paths, or as if the disk file path does not +// exist if its backing file path is empty. Support for the -overlay flag +// has some limitations: importantly, cgo files included from outside the +// include path must be in the same directory as the Go package they are +// included from, and overlays will not appear when binaries and tests are +// run through go run and go test respectively. +// -pkgdir dir +// install and load all packages from dir instead of the usual locations. +// For example, when building with a non-standard configuration, +// use -pkgdir to keep generated packages in a separate location. +// -tags tag,list +// a comma-separated list of additional build tags to consider satisfied +// during the build. For more information about build tags, see +// 'go help buildconstraint'. (Earlier versions of Go used a +// space-separated list, and that form is deprecated but still recognized.) +// -trimpath +// remove all file system paths from the resulting executable. +// Instead of absolute file system paths, the recorded file names +// will begin either a module path@version (when using modules), +// or a plain import path (when using the standard library, or GOPATH). +// -toolexec 'cmd args' +// a program to use to invoke toolchain programs like vet and asm. +// For example, instead of running asm, the go command will run +// 'cmd args /path/to/asm '. +// The TOOLEXEC_IMPORTPATH environment variable will be set, +// matching 'go list -f {{.ImportPath}}' for the package being built. // // The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a // space-separated list of arguments to pass to an underlying tool @@ -231,12 +241,11 @@ // // See also: go install, go get, go clean. // -// -// Remove object files and cached files +// # Remove object files and cached files // // Usage: // -// go clean [clean flags] [build flags] [packages] +// go clean [clean flags] [build flags] [packages] // // Clean removes object files from package source directories. // The go command builds most objects in a temporary directory, @@ -247,17 +256,17 @@ // clean removes the following files from each of the // source directories corresponding to the import paths: // -// _obj/ old object directory, left from Makefiles -// _test/ old test directory, left from Makefiles -// _testmain.go old gotest file, left from Makefiles -// test.out old test log, left from Makefiles -// build.out old test log, left from Makefiles -// *.[568ao] object files, left from Makefiles +// _obj/ old object directory, left from Makefiles +// _test/ old test directory, left from Makefiles +// _testmain.go old gotest file, left from Makefiles +// test.out old test log, left from Makefiles +// build.out old test log, left from Makefiles +// *.[568ao] object files, left from Makefiles // -// DIR(.exe) from go build -// DIR.test(.exe) from go test -c -// MAINFILE(.exe) from go build MAINFILE.go -// *.so from SWIG +// DIR(.exe) from go build +// DIR.test(.exe) from go test -c +// MAINFILE(.exe) from go build MAINFILE.go +// *.so from SWIG // // In the list, DIR represents the final path element of the // directory, and MAINFILE is the base name of any Go source @@ -284,16 +293,22 @@ // download cache, including unpacked source code of versioned // dependencies. // +// The -fuzzcache flag causes clean to remove files stored in the Go build +// cache for fuzz testing. The fuzzing engine caches files that expand +// code coverage, so removing them may make fuzzing less effective until +// new inputs are found that provide the same coverage. These files are +// distinct from those stored in testdata directory; clean does not remove +// those files. +// // For more about build flags, see 'go help build'. // // For more about specifying packages, see 'go help packages'. // -// -// Show documentation for package or symbol +// # Show documentation for package or symbol // // Usage: // -// go doc [doc flags] [package|[package.]symbol[.methodOrField]] +// go doc [doc flags] [package|[package.]symbol[.methodOrField]] // // Doc prints the documentation comments associated with the item identified by its // arguments (a package, const, func, type, var, method, or struct field) @@ -305,7 +320,7 @@ // // Given no arguments, that is, when run as // -// go doc +// go doc // // it prints the package documentation for the package in the current directory. // If the package is a command (package main), the exported symbols of the package @@ -316,10 +331,10 @@ // on what is installed in GOROOT and GOPATH, as well as the form of the argument, // which is schematically one of these: // -// go doc -// go doc [.] -// go doc [.][.] -// go doc [.][.] +// go doc +// go doc [.] +// go doc [.][.] +// go doc [.][.] // // The first item in this list matched by the argument is the one whose documentation // is printed. (See the examples below.) However, if the argument starts with a capital @@ -338,11 +353,10 @@ // path. The go tool's usual package mechanism does not apply: package path // elements like . and ... are not implemented by go doc. // -// When run with two arguments, the first must be a full package path (not just a -// suffix), and the second is a symbol, or symbol with method or struct field. -// This is similar to the syntax accepted by godoc: +// When run with two arguments, the first is a package path (full path or suffix), +// and the second is a symbol, or symbol with method or struct field: // -// go doc [.] +// go doc [.] // // In all forms, when matching symbols, lower-case letters in the argument match // either case but upper-case letters match exactly. This means that there may be @@ -350,68 +364,69 @@ // different cases. If this occurs, documentation for all matches is printed. // // Examples: -// go doc -// Show documentation for current package. -// go doc Foo -// Show documentation for Foo in the current package. -// (Foo starts with a capital letter so it cannot match -// a package path.) -// go doc encoding/json -// Show documentation for the encoding/json package. -// go doc json -// Shorthand for encoding/json. -// go doc json.Number (or go doc json.number) -// Show documentation and method summary for json.Number. -// go doc json.Number.Int64 (or go doc json.number.int64) -// Show documentation for json.Number's Int64 method. -// go doc cmd/doc -// Show package docs for the doc command. -// go doc -cmd cmd/doc -// Show package docs and exported symbols within the doc command. -// go doc template.new -// Show documentation for html/template's New function. -// (html/template is lexically before text/template) -// go doc text/template.new # One argument -// Show documentation for text/template's New function. -// go doc text/template new # Two arguments -// Show documentation for text/template's New function. -// -// At least in the current tree, these invocations all print the -// documentation for json.Decoder's Decode method: -// -// go doc json.Decoder.Decode -// go doc json.decoder.decode -// go doc json.decode -// cd go/src/encoding/json; go doc decode +// +// go doc +// Show documentation for current package. +// go doc Foo +// Show documentation for Foo in the current package. +// (Foo starts with a capital letter so it cannot match +// a package path.) +// go doc encoding/json +// Show documentation for the encoding/json package. +// go doc json +// Shorthand for encoding/json. +// go doc json.Number (or go doc json.number) +// Show documentation and method summary for json.Number. +// go doc json.Number.Int64 (or go doc json.number.int64) +// Show documentation for json.Number's Int64 method. +// go doc cmd/doc +// Show package docs for the doc command. +// go doc -cmd cmd/doc +// Show package docs and exported symbols within the doc command. +// go doc template.new +// Show documentation for html/template's New function. +// (html/template is lexically before text/template) +// go doc text/template.new # One argument +// Show documentation for text/template's New function. +// go doc text/template new # Two arguments +// Show documentation for text/template's New function. +// +// At least in the current tree, these invocations all print the +// documentation for json.Decoder's Decode method: +// +// go doc json.Decoder.Decode +// go doc json.decoder.decode +// go doc json.decode +// cd go/src/encoding/json; go doc decode // // Flags: -// -all -// Show all the documentation for the package. -// -c -// Respect case when matching symbols. -// -cmd -// Treat a command (package main) like a regular package. -// Otherwise package main's exported symbols are hidden -// when showing the package's top-level documentation. -// -short -// One-line representation for each symbol. -// -src -// Show the full source code for the symbol. This will -// display the full Go source of its declaration and -// definition, such as a function definition (including -// the body), type declaration or enclosing const -// block. The output may therefore include unexported -// details. -// -u -// Show documentation for unexported as well as exported -// symbols, methods, and fields. -// -// -// Print Go environment information +// +// -all +// Show all the documentation for the package. +// -c +// Respect case when matching symbols. +// -cmd +// Treat a command (package main) like a regular package. +// Otherwise package main's exported symbols are hidden +// when showing the package's top-level documentation. +// -short +// One-line representation for each symbol. +// -src +// Show the full source code for the symbol. This will +// display the full Go source of its declaration and +// definition, such as a function definition (including +// the body), type declaration or enclosing const +// block. The output may therefore include unexported +// details. +// -u +// Show documentation for unexported as well as exported +// symbols, methods, and fields. +// +// # Print Go environment information // // Usage: // -// go env [-json] [-u] [-w] [var ...] +// go env [-json] [-u] [-w] [var ...] // // Env prints Go environment information. // @@ -433,28 +448,30 @@ // // For more about environment variables, see 'go help environment'. // -// -// Update packages to use new APIs +// # Update packages to use new APIs // // Usage: // -// go fix [packages] +// go fix [-fix list] [packages] // // Fix runs the Go fix command on the packages named by the import paths. // +// The -fix flag sets a comma-separated list of fixes to run. +// The default is all known fixes. +// (Its value is passed to 'go tool fix -r'.) +// // For more about fix, see 'go doc cmd/fix'. // For more about specifying packages, see 'go help packages'. // -// To run fix with specific options, run 'go tool fix'. +// To run fix with other options, run 'go tool fix'. // // See also: go fmt, go vet. // -// -// Gofmt (reformat) package sources +// # Gofmt (reformat) package sources // // Usage: // -// go fmt [-n] [-x] [packages] +// go fmt [-n] [-x] [packages] // // Fmt runs the command 'gofmt -l -w' on the packages named // by the import paths. It prints the names of the files that are modified. @@ -472,24 +489,23 @@ // // See also: go fix, go vet. // -// -// Generate Go files by processing source +// # Generate Go files by processing source // // Usage: // -// go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages] +// go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages] // // Generate runs commands described by directives within existing // files. Those commands can run any process but the intent is to // create or update Go source files. // -// Go generate is never run automatically by go build, go get, go test, +// Go generate is never run automatically by go build, go test, // and so on. It must be run explicitly. // // Go generate scans the file for directives, which are lines of // the form, // -// //go:generate command argument... +// //go:generate command argument... // // (note: no leading spaces and no space in "//go") where command // is the generator to be run, corresponding to an executable file @@ -512,25 +528,28 @@ // generated source should have a line that matches the following // regular expression (in Go syntax): // -// ^// Code generated .* DO NOT EDIT\.$ +// ^// Code generated .* DO NOT EDIT\.$ // // This line must appear before the first non-comment, non-blank // text in the file. // // Go generate sets several variables when it runs the generator: // -// $GOARCH -// The execution architecture (arm, amd64, etc.) -// $GOOS -// The execution operating system (linux, windows, etc.) -// $GOFILE -// The base name of the file. -// $GOLINE -// The line number of the directive in the source file. -// $GOPACKAGE -// The name of the package of the file containing the directive. -// $DOLLAR -// A dollar sign. +// $GOARCH +// The execution architecture (arm, amd64, etc.) +// $GOOS +// The execution operating system (linux, windows, etc.) +// $GOFILE +// The base name of the file. +// $GOLINE +// The line number of the directive in the source file. +// $GOPACKAGE +// The name of the package of the file containing the directive. +// $GOROOT +// The GOROOT directory for the 'go' command that invoked the +// generator, containing the Go toolchain and standard library. +// $DOLLAR +// A dollar sign. // // Other than variable substitution and quoted-string evaluation, no // special processing such as "globbing" is performed on the command @@ -546,14 +565,14 @@ // // A directive of the form, // -// //go:generate -command xxx args... +// //go:generate -command xxx args... // // specifies, for the remainder of this source file only, that the // string xxx represents the command identified by the arguments. This // can be used to create aliases or to handle multiword generators. // For example, // -// //go:generate -command foo go tool foo +// //go:generate -command foo go tool foo // // specifies that the command "foo" represents the generator // "go tool foo". @@ -575,13 +594,20 @@ // // The generator is run in the package's source directory. // -// Go generate accepts one specific flag: +// Go generate accepts two specific flags: +// +// -run="" +// if non-empty, specifies a regular expression to select +// directives whose full original source text (excluding +// any trailing spaces and final newline) matches the +// expression. // -// -run="" -// if non-empty, specifies a regular expression to select -// directives whose full original source text (excluding -// any trailing spaces and final newline) matches the -// expression. +// -skip="" +// if non-empty, specifies a regular expression to suppress +// directives whose full original source text (excluding +// any trailing spaces and final newline) matches the +// expression. If a directive matches both the -run and +// the -skip arguments, it is skipped. // // It also accepts the standard build flags including -v, -n, and -x. // The -v flag prints the names of packages and files as they are @@ -593,42 +619,42 @@ // // For more about specifying packages, see 'go help packages'. // -// -// Add dependencies to current module and install them +// # Add dependencies to current module and install them // // Usage: // -// go get [-d] [-t] [-u] [-v] [build flags] [packages] +// go get [-t] [-u] [-v] [build flags] [packages] // // Get resolves its command-line arguments to packages at specific module versions, -// updates go.mod to require those versions, downloads source code into the -// module cache, then builds and installs the named packages. +// updates go.mod to require those versions, and downloads source code into the +// module cache. // // To add a dependency for a package or upgrade it to its latest version: // -// go get example.com/pkg +// go get example.com/pkg // // To upgrade or downgrade a package to a specific version: // -// go get example.com/pkg@v1.2.3 +// go get example.com/pkg@v1.2.3 // // To remove a dependency on a module and downgrade modules that require it: // -// go get example.com/mod@none +// go get example.com/mod@none // // See https://golang.org/ref/mod#go-get for details. // -// The 'go install' command may be used to build and install packages. When a -// version is specified, 'go install' runs in module-aware mode and ignores -// the go.mod file in the current directory. For example: +// In earlier versions of Go, 'go get' was used to build and install packages. +// Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install' +// may be used to build and install commands instead. When a version is specified, +// 'go install' runs in module-aware mode and ignores the go.mod file in the +// current directory. For example: // -// go install example.com/pkg@v1.2.3 -// go install example.com/pkg@latest +// go install example.com/pkg@v1.2.3 +// go install example.com/pkg@latest // // See 'go help install' or https://golang.org/ref/mod#go-install for details. // -// In addition to build flags (listed in 'go help build') 'go get' accepts the -// following flags. +// 'go get' accepts the following flags. // // The -t flag instructs get to consider modules needed to build tests of // packages specified on the command line. @@ -643,15 +669,9 @@ // When the -t and -u flags are used together, get will update // test dependencies as well. // -// The -d flag instructs get not to build or install packages. get will only -// update go.mod and download source code needed to build packages. -// -// Building and installing packages with get is deprecated. In a future release, -// the -d flag will be enabled by default, and 'go get' will be only be used to -// adjust dependencies of the current module. To install a package using -// dependencies from the current module, use 'go install'. To install a package -// ignoring the current module, use 'go install' with an @version suffix like -// "@latest" after each argument. +// The -x flag prints commands as they are executed. This is useful for +// debugging version control commands when a module is downloaded directly +// from a repository. // // For more about modules, see https://golang.org/ref/mod. // @@ -664,12 +684,11 @@ // // See also: go build, go install, go clean, go mod. // -// -// Compile and install packages and dependencies +// # Compile and install packages and dependencies // // Usage: // -// go install [build flags] [packages] +// go install [build flags] [packages] // // Install compiles and installs the packages named by the import paths. // @@ -694,14 +713,17 @@ // // - All arguments must refer to packages in the same module at the same version. // +// - Package path arguments must refer to main packages. Pattern arguments +// will only match main packages. +// // - No module is considered the "main" module. If the module containing // packages named on the command line has a go.mod file, it must not contain // directives (replace and exclude) that would cause it to be interpreted // differently than if it were the main module. The module must not require // a higher version of itself. // -// - Package path arguments must refer to main packages. Pattern arguments -// will only match main packages. +// - Vendor directories are not used in any module. (Vendor directories are not +// included in the module zip files downloaded by 'go install'.) // // If the arguments don't have version suffixes, "go install" may run in // module-aware mode or GOPATH mode, depending on the GO111MODULE environment @@ -721,12 +743,11 @@ // // See also: go build, go get, go clean. // -// -// List packages or modules +// # List packages or modules // // Usage: // -// go list [-f format] [-json] [-m] [list flags] [build flags] [packages] +// go list [-f format] [-json] [-m] [list flags] [build flags] [packages] // // List lists the named packages, one per line. // The most commonly-used flags are -f and -json, which control the form @@ -735,83 +756,83 @@ // // The default output shows the package import path: // -// bytes -// encoding/json -// github.com/gorilla/mux -// golang.org/x/net/html +// bytes +// encoding/json +// github.com/gorilla/mux +// golang.org/x/net/html // // The -f flag specifies an alternate format for the list, using the // syntax of package template. The default output is equivalent // to -f '{{.ImportPath}}'. The struct being passed to the template is: // -// type Package struct { -// Dir string // directory containing package sources -// ImportPath string // import path of package in dir -// ImportComment string // path in import comment on package statement -// Name string // package name -// Doc string // package documentation string -// Target string // install path -// Shlib string // the shared library that contains this package (only set when -linkshared) -// Goroot bool // is this package in the Go root? -// Standard bool // is this package part of the standard Go library? -// 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 (no longer supported) -// ForTest string // package is only for use in named test -// Export string // file containing export data (when using -export) -// BuildID string // build ID of the compiled package (when using -export) -// Module *Module // info about package's containing module, if any (can be nil) -// Match []string // command-line patterns matching this package -// DepOnly bool // package is only a dependency, not explicitly listed -// -// // Source files -// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) -// CgoFiles []string // .go source files that import "C" -// CompiledGoFiles []string // .go files presented to compiler (when using -compiled) -// IgnoredGoFiles []string // .go source files ignored due to build constraints -// IgnoredOtherFiles []string // non-.go source files ignored due to build constraints -// CFiles []string // .c source files -// CXXFiles []string // .cc, .cxx and .cpp source files -// MFiles []string // .m source files -// HFiles []string // .h, .hh, .hpp and .hxx source files -// FFiles []string // .f, .F, .for and .f90 Fortran source files -// SFiles []string // .s source files -// SwigFiles []string // .swig files -// SwigCXXFiles []string // .swigcxx files -// SysoFiles []string // .syso object files to add to archive -// TestGoFiles []string // _test.go files in package -// XTestGoFiles []string // _test.go files outside package -// -// // Embedded files -// EmbedPatterns []string // //go:embed patterns -// EmbedFiles []string // files matched by EmbedPatterns -// TestEmbedPatterns []string // //go:embed patterns in TestGoFiles -// TestEmbedFiles []string // files matched by TestEmbedPatterns -// XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles -// XTestEmbedFiles []string // files matched by XTestEmbedPatterns -// -// // Cgo directives -// CgoCFLAGS []string // cgo: flags for C compiler -// CgoCPPFLAGS []string // cgo: flags for C preprocessor -// CgoCXXFLAGS []string // cgo: flags for C++ compiler -// CgoFFLAGS []string // cgo: flags for Fortran compiler -// CgoLDFLAGS []string // cgo: flags for linker -// CgoPkgConfig []string // cgo: pkg-config names -// -// // Dependency information -// Imports []string // import paths used by this package -// ImportMap map[string]string // map from source import to ImportPath (identity entries omitted) -// Deps []string // all (recursively) imported dependencies -// TestImports []string // imports from TestGoFiles -// XTestImports []string // imports from XTestGoFiles -// -// // Error information -// Incomplete bool // this package or a dependency has an error -// Error *PackageError // error loading package -// DepsErrors []*PackageError // errors loading dependencies -// } +// type Package struct { +// Dir string // directory containing package sources +// ImportPath string // import path of package in dir +// ImportComment string // path in import comment on package statement +// Name string // package name +// Doc string // package documentation string +// Target string // install path +// Shlib string // the shared library that contains this package (only set when -linkshared) +// Goroot bool // is this package in the Go root? +// Standard bool // is this package part of the standard Go library? +// 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 (no longer supported) +// ForTest string // package is only for use in named test +// Export string // file containing export data (when using -export) +// BuildID string // build ID of the compiled package (when using -export) +// Module *Module // info about package's containing module, if any (can be nil) +// Match []string // command-line patterns matching this package +// DepOnly bool // package is only a dependency, not explicitly listed +// +// // Source files +// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) +// CgoFiles []string // .go source files that import "C" +// CompiledGoFiles []string // .go files presented to compiler (when using -compiled) +// IgnoredGoFiles []string // .go source files ignored due to build constraints +// IgnoredOtherFiles []string // non-.go source files ignored due to build constraints +// CFiles []string // .c source files +// CXXFiles []string // .cc, .cxx and .cpp source files +// MFiles []string // .m source files +// HFiles []string // .h, .hh, .hpp and .hxx source files +// FFiles []string // .f, .F, .for and .f90 Fortran source files +// SFiles []string // .s source files +// SwigFiles []string // .swig files +// SwigCXXFiles []string // .swigcxx files +// SysoFiles []string // .syso object files to add to archive +// TestGoFiles []string // _test.go files in package +// XTestGoFiles []string // _test.go files outside package +// +// // Embedded files +// EmbedPatterns []string // //go:embed patterns +// EmbedFiles []string // files matched by EmbedPatterns +// TestEmbedPatterns []string // //go:embed patterns in TestGoFiles +// TestEmbedFiles []string // files matched by TestEmbedPatterns +// XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles +// XTestEmbedFiles []string // files matched by XTestEmbedPatterns +// +// // Cgo directives +// CgoCFLAGS []string // cgo: flags for C compiler +// CgoCPPFLAGS []string // cgo: flags for C preprocessor +// CgoCXXFLAGS []string // cgo: flags for C++ compiler +// CgoFFLAGS []string // cgo: flags for Fortran compiler +// CgoLDFLAGS []string // cgo: flags for linker +// CgoPkgConfig []string // cgo: pkg-config names +// +// // Dependency information +// Imports []string // import paths used by this package +// ImportMap map[string]string // map from source import to ImportPath (identity entries omitted) +// Deps []string // all (recursively) imported dependencies +// TestImports []string // imports from TestGoFiles +// XTestImports []string // imports from XTestGoFiles +// +// // Error information +// Incomplete bool // this package or a dependency has an error +// Error *PackageError // error loading package +// DepsErrors []*PackageError // errors loading dependencies +// } // // Packages stored in vendor directories report an ImportPath that includes the // path to the vendor directory (for example, "d/vendor/p" instead of "p"), @@ -821,11 +842,11 @@ // // The error information, if any, is // -// type PackageError struct { -// ImportStack []string // shortest path from package named on command line to this one -// Pos string // position of error (if present, file:line:col) -// Err string // the error itself -// } +// type PackageError struct { +// ImportStack []string // shortest path from package named on command line to this one +// Pos string // position of error (if present, file:line:col) +// Err string // the error itself +// } // // The module information is a Module struct, defined in the discussion // of list -m below. @@ -834,25 +855,28 @@ // // The template function "context" returns the build context, defined as: // -// type Context struct { -// GOARCH string // target architecture -// GOOS string // target operating system -// GOROOT string // Go root -// GOPATH string // Go path -// CgoEnabled bool // whether cgo can be used -// UseAllFiles bool // use files regardless of +build lines, file names -// Compiler string // compiler to assume when computing target paths -// BuildTags []string // build constraints to match in +build lines -// ToolTags []string // toolchain-specific build constraints -// ReleaseTags []string // releases the current release is compatible with -// InstallSuffix string // suffix to use in the name of the install dir -// } +// type Context struct { +// GOARCH string // target architecture +// GOOS string // target operating system +// GOROOT string // Go root +// GOPATH string // Go path +// CgoEnabled bool // whether cgo can be used +// UseAllFiles bool // use files regardless of +build lines, file names +// Compiler string // compiler to assume when computing target paths +// BuildTags []string // build constraints to match in +build lines +// ToolTags []string // toolchain-specific build constraints +// ReleaseTags []string // releases the current release is compatible with +// InstallSuffix string // suffix to use in the name of the install dir +// } // // For more information about the meaning of these fields see the documentation // for the go/build package's Context type. // // The -json flag causes the package data to be printed in JSON format -// instead of using the template format. +// instead of using the template format. The JSON flag can optionally be +// provided with a set of comma-separated required field names to be output. +// If so, those required fields will always appear in JSON output, but +// others may be omitted to save work in computing the JSON struct. // // The -compiled flag causes list to set CompiledGoFiles to the Go source // files presented to the compiler. Typically this means that it repeats @@ -877,7 +901,8 @@ // (zeroed). // // The -export flag causes list to set the Export field to the name of a -// file containing up-to-date export information for the given package. +// file containing up-to-date export information for the given package, +// and the BuildID field to the build ID of the compiled package. // // The -find flag causes list to identify the named packages but not // resolve their dependencies: the Imports and Deps lists will be empty. @@ -910,25 +935,29 @@ // When listing modules, the -f flag still specifies a format template // applied to a Go struct, but now a Module struct: // -// type Module struct { -// Path string // module path -// Version string // module version -// Versions []string // available module versions (with -versions) -// Replace *Module // replaced by this module -// Time *time.Time // time version was created -// Update *Module // available update, if any (with -u) -// Main bool // is this the main module? -// Indirect bool // is this module only an indirect dependency of main module? -// Dir string // directory holding files for this module, if any -// GoMod string // path to go.mod file used when loading this module, if any -// GoVersion string // go version used in module -// Retracted string // retraction information, if any (with -retracted or -u) -// Error *ModuleError // error loading module -// } -// -// type ModuleError struct { -// Err string // the error itself -// } +// type Module struct { +// Path string // module path +// Query string // version query corresponding to this version +// Version string // module version +// Versions []string // available module versions +// Replace *Module // replaced by this module +// Time *time.Time // time version was created +// Update *Module // available update (with -u) +// Main bool // is this the main module? +// Indirect bool // module is only indirectly needed by main module +// Dir string // directory holding local copy of files, if any +// GoMod string // path to go.mod file describing module, if any +// GoVersion string // go version used in module +// Retracted []string // retraction information, if any (with -retracted or -u) +// Deprecated string // deprecation message, if any (with -u) +// Error *ModuleError // error loading module +// Origin any // provenance of module +// Reuse bool // reuse of old module info is safe +// } +// +// type ModuleError struct { +// Err string // the error itself +// } // // The file GoMod refers to may be outside the module directory if the // module is in the module cache or if the -modfile flag is used. @@ -937,9 +966,9 @@ // information about the version and replacement if any. // For example, 'go list -m all' might print: // -// my/main/module -// golang.org/x/text v0.3.0 => /tmp/text -// rsc.io/pdf v0.1.1 +// my/main/module +// golang.org/x/text v0.3.0 => /tmp/text +// rsc.io/pdf v0.1.1 // // The Module struct has a String method that formats this // line of output, so that the default format is equivalent @@ -961,9 +990,9 @@ // If a version is retracted, the string "(retracted)" will follow it. // For example, 'go list -m -u all' might print: // -// my/main/module -// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text -// rsc.io/pdf v0.1.1 (retracted) [v0.1.2] +// my/main/module +// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text +// rsc.io/pdf v0.1.1 (retracted) [v0.1.2] // // (For tools, 'go list -m -u -json all' may be more convenient to parse.) // @@ -1000,14 +1029,23 @@ // module as a Module struct. If an error occurs, the result will // be a Module struct with a non-nil Error field. // +// When using -m, the -reuse=old.json flag accepts the name of file containing +// the JSON output of a previous 'go list -m -json' invocation with the +// same set of modifier flags (such as -u, -retracted, and -versions). +// The go command may use this file to determine that a module is unchanged +// since the previous invocation and avoid redownloading information about it. +// Modules that are not redownloaded will be marked in the new output by +// setting the Reuse field to true. Normally the module cache provides this +// kind of reuse automatically; the -reuse flag can be useful on systems that +// do not preserve the module cache. +// // For more about build flags, see 'go help build'. // // For more about specifying packages, see 'go help packages'. // // For more about modules, see https://golang.org/ref/mod. // -// -// Module maintenance +// # Module maintenance // // Go mod provides access to operations on modules. // @@ -1018,31 +1056,34 @@ // // Usage: // -// go mod [arguments] +// go mod [arguments] // // The commands are: // -// download download modules to local cache -// edit edit go.mod from tools or scripts -// graph print module requirement graph -// init initialize new module in current directory -// tidy add missing and remove unused modules -// vendor make vendored copy of dependencies -// verify verify dependencies have expected content -// why explain why packages or modules are needed +// download download modules to local cache +// edit edit go.mod from tools or scripts +// graph print module requirement graph +// init initialize new module in current directory +// tidy add missing and remove unused modules +// vendor make vendored copy of dependencies +// verify verify dependencies have expected content +// why explain why packages or modules are needed // // Use "go help mod " for more information about a command. // -// Download modules to local cache +// # Download modules to local cache // // Usage: // -// go mod download [-x] [-json] [modules] +// go mod download [-x] [-json] [-reuse=old.json] [modules] // // Download downloads the named modules, which can be module patterns selecting // dependencies of the main module or module queries of the form path@version. -// With no arguments, download applies to all dependencies of the main module -// (equivalent to 'go mod download all'). +// +// With no arguments, download applies to the modules needed to build and test +// the packages in the main module: the modules explicitly required by the main +// module if it is at 'go 1.17' or higher, or all transitively-required modules +// if at 'go 1.16' or lower. // // The go command will automatically download modules as needed during ordinary // execution. The "go mod download" command is useful mainly for pre-filling @@ -1055,17 +1096,28 @@ // to standard output, describing each downloaded module (or failure), // corresponding to this Go struct: // -// type Module struct { -// Path string // module path -// Version string // module version -// Error string // error loading module -// Info string // absolute path to cached .info file -// GoMod string // absolute path to cached .mod file -// Zip string // absolute path to cached .zip file -// Dir string // absolute path to cached source root directory -// Sum string // checksum for path, version (as in go.sum) -// GoModSum string // checksum for go.mod (as in go.sum) -// } +// type Module struct { +// Path string // module path +// Query string // version query corresponding to this version +// Version string // module version +// Error string // error loading module +// Info string // absolute path to cached .info file +// GoMod string // absolute path to cached .mod file +// Zip string // absolute path to cached .zip file +// Dir string // absolute path to cached source root directory +// Sum string // checksum for path, version (as in go.sum) +// GoModSum string // checksum for go.mod (as in go.sum) +// Origin any // provenance of module +// Reuse bool // reuse of old module info is safe +// } +// +// The -reuse flag accepts the name of file containing the JSON output of a +// previous 'go mod download -json' invocation. The go command may use this +// file to determine that a module is unchanged since the previous invocation +// and avoid redownloading it. Modules that are not redownloaded will be marked +// in the new output by setting the Reuse field to true. Normally the module +// cache provides this kind of reuse automatically; the -reuse flag can be +// useful on systems that do not preserve the module cache. // // The -x flag causes download to print the commands download executes. // @@ -1073,12 +1125,11 @@ // // See https://golang.org/ref/mod#version-queries for more about version queries. // -// -// Edit go.mod from tools or scripts +// # Edit go.mod from tools or scripts // // Usage: // -// go mod edit [editing flags] [-fmt|-print|-json] [go.mod] +// go mod edit [editing flags] [-fmt|-print|-json] [go.mod] // // Edit provides a command-line interface for editing go.mod, // for use primarily by tools or scripts. It reads only go.mod; @@ -1136,41 +1187,41 @@ // The -json flag prints the final go.mod file in JSON format instead of // writing it back to go.mod. The JSON output corresponds to these Go types: // -// type Module struct { -// Path string -// Version string -// } -// -// type GoMod struct { -// Module ModPath -// Go string -// Require []Require -// Exclude []Module -// Replace []Replace -// Retract []Retract -// } -// -// type ModPath struct { -// Path string -// Deprecated string -// } -// -// type Require struct { -// Path string -// Version string -// Indirect bool -// } -// -// type Replace struct { -// Old Module -// New Module -// } -// -// type Retract struct { -// Low string -// High string -// Rationale string -// } +// type Module struct { +// Path string +// Version string +// } +// +// type GoMod struct { +// Module ModPath +// Go string +// Require []Require +// Exclude []Module +// Replace []Replace +// Retract []Retract +// } +// +// type ModPath struct { +// Path string +// Deprecated string +// } +// +// type Require struct { +// Path string +// Version string +// Indirect bool +// } +// +// type Replace struct { +// Old Module +// New Module +// } +// +// type Retract struct { +// Low string +// High string +// Rationale string +// } // // Retract entries representing a single version (not an interval) will have // the "Low" and "High" fields set to the same value. @@ -1181,12 +1232,11 @@ // // See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'. // -// -// Print module requirement graph +// # Print module requirement graph // // Usage: // -// go mod graph [-go=version] +// go mod graph [-go=version] // // Graph prints the module requirement graph (with replacements applied) // in text form. Each line in the output has two space-separated fields: a module @@ -1199,12 +1249,11 @@ // // See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'. // -// -// Initialize new module in current directory +// # Initialize new module in current directory // // Usage: // -// go mod init [module-path] +// go mod init [module-path] // // Init initializes and writes a new go.mod file in the current directory, in // effect creating a new module rooted at the current directory. The go.mod file @@ -1220,12 +1269,11 @@ // // See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'. // -// -// Add missing and remove unused modules +// # Add missing and remove unused modules // // Usage: // -// go mod tidy [-e] [-v] [-go=version] [-compat=version] +// go mod tidy [-e] [-v] [-go=version] [-compat=version] // // Tidy makes sure go.mod matches the source code in the module. // It adds any missing modules necessary to build the current module's @@ -1255,12 +1303,11 @@ // // See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'. // -// -// Make vendored copy of dependencies +// # Make vendored copy of dependencies // // Usage: // -// go mod vendor [-e] [-v] +// go mod vendor [-e] [-v] [-o outdir] // // Vendor resets the main module's vendor directory to include all packages // needed to build and test all the main module's packages. @@ -1272,14 +1319,18 @@ // The -e flag causes vendor to attempt to proceed despite errors // encountered while loading packages. // -// See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'. +// The -o flag causes vendor to create the vendor directory at the given +// path instead of "vendor". The go command can only use a vendor directory +// named "vendor" within the module root directory, so this flag is +// primarily useful for other tools. // +// See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'. // -// Verify dependencies have expected content +// # Verify dependencies have expected content // // Usage: // -// go mod verify +// go mod verify // // Verify checks that the dependencies of the current module, // which are stored in a local downloaded source cache, have not been @@ -1290,12 +1341,11 @@ // // See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'. // -// -// Explain why packages or modules are needed +// # Explain why packages or modules are needed // // Usage: // -// go mod why [-m] [-vendor] packages... +// go mod why [-m] [-vendor] packages... // // Why shows a shortest path in the import graph from the main module to // each of the listed packages. If the -m flag is given, why treats the @@ -1316,24 +1366,229 @@ // // For example: // -// $ go mod why golang.org/x/text/language golang.org/x/text/encoding -// # golang.org/x/text/language -// rsc.io/quote -// rsc.io/sampler -// golang.org/x/text/language +// $ go mod why golang.org/x/text/language golang.org/x/text/encoding +// # golang.org/x/text/language +// rsc.io/quote +// rsc.io/sampler +// golang.org/x/text/language // -// # golang.org/x/text/encoding -// (main module does not need package golang.org/x/text/encoding) -// $ +// # golang.org/x/text/encoding +// (main module does not need package golang.org/x/text/encoding) +// $ // // See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'. // +// # Workspace maintenance +// +// Work provides access to operations on workspaces. +// +// Note that support for workspaces is built into many other commands, not +// just 'go work'. +// +// See 'go help modules' for information about Go's module system of which +// workspaces are a part. +// +// See https://go.dev/ref/mod#workspaces for an in-depth reference on +// workspaces. +// +// See https://go.dev/doc/tutorial/workspaces for an introductory +// tutorial on workspaces. +// +// A workspace is specified by a go.work file that specifies a set of +// module directories with the "use" directive. These modules are used as +// root modules by the go command for builds and related operations. A +// workspace that does not specify modules to be used cannot be used to do +// builds from local modules. // -// Compile and run Go program +// go.work files are line-oriented. Each line holds a single directive, +// made up of a keyword followed by arguments. For example: +// +// go 1.18 +// +// use ../foo/bar +// use ./baz +// +// replace example.com/foo v1.2.3 => example.com/bar v1.4.5 +// +// The leading keyword can be factored out of adjacent lines to create a block, +// like in Go imports. +// +// use ( +// ../foo/bar +// ./baz +// ) +// +// The use directive specifies a module to be included in the workspace's +// set of main modules. The argument to the use directive is the directory +// containing the module's go.mod file. +// +// The go directive specifies the version of Go the file was written at. It +// is possible there may be future changes in the semantics of workspaces +// that could be controlled by this version, but for now the version +// specified has no effect. +// +// The replace directive has the same syntax as the replace directive in a +// go.mod file and takes precedence over replaces in go.mod files. It is +// primarily intended to override conflicting replaces in different workspace +// modules. +// +// To determine whether the go command is operating in workspace mode, use +// the "go env GOWORK" command. This will specify the workspace file being +// used. // // Usage: // -// go run [build flags] [-exec xprog] package [arguments...] +// go work [arguments] +// +// The commands are: +// +// edit edit go.work from tools or scripts +// init initialize workspace file +// sync sync workspace build list to modules +// use add modules to workspace file +// +// Use "go help work " for more information about a command. +// +// # Edit go.work from tools or scripts +// +// Usage: +// +// go work edit [editing flags] [go.work] +// +// Edit provides a command-line interface for editing go.work, +// for use primarily by tools or scripts. It only reads go.work; +// it does not look up information about the modules involved. +// If no file is specified, Edit looks for a go.work file in the current +// directory and its parent directories +// +// The editing flags specify a sequence of editing operations. +// +// The -fmt flag reformats the go.work file without making other changes. +// This reformatting is also implied by any other modifications that use or +// rewrite the go.mod file. The only time this flag is needed is if no other +// flags are specified, as in 'go work edit -fmt'. +// +// The -use=path and -dropuse=path flags +// add and drop a use directive from the go.work file's set of module directories. +// +// The -replace=old[@v]=new[@v] flag adds a replacement of the given +// module path and version pair. If the @v in old@v is omitted, a +// replacement without a version on the left side is added, which applies +// to all versions of the old module path. If the @v in new@v is omitted, +// the new path should be a local module root directory, not a module +// path. Note that -replace overrides any redundant replacements for old[@v], +// so omitting @v will drop existing replacements for specific versions. +// +// The -dropreplace=old[@v] flag drops a replacement of the given +// module path and version pair. If the @v is omitted, a replacement without +// a version on the left side is dropped. +// +// The -use, -dropuse, -replace, and -dropreplace, +// editing flags may be repeated, and the changes are applied in the order given. +// +// The -go=version flag sets the expected Go language version. +// +// The -print flag prints the final go.work in its text format instead of +// writing it back to go.mod. +// +// The -json flag prints the final go.work file in JSON format instead of +// writing it back to go.mod. The JSON output corresponds to these Go types: +// +// type GoWork struct { +// Go string +// Use []Use +// Replace []Replace +// } +// +// type Use struct { +// DiskPath string +// ModulePath string +// } +// +// type Replace struct { +// Old Module +// New Module +// } +// +// type Module struct { +// Path string +// Version string +// } +// +// See the workspaces reference at https://go.dev/ref/mod#workspaces +// for more information. +// +// # Initialize workspace file +// +// Usage: +// +// go work init [moddirs] +// +// Init initializes and writes a new go.work file in the +// current directory, in effect creating a new workspace at the current +// directory. +// +// go work init optionally accepts paths to the workspace modules as +// arguments. If the argument is omitted, an empty workspace with no +// modules will be created. +// +// Each argument path is added to a use directive in the go.work file. The +// current go version will also be listed in the go.work file. +// +// See the workspaces reference at https://go.dev/ref/mod#workspaces +// for more information. +// +// # Sync workspace build list to modules +// +// Usage: +// +// go work sync +// +// Sync syncs the workspace's build list back to the +// workspace's modules +// +// The workspace's build list is the set of versions of all the +// (transitive) dependency modules used to do builds in the workspace. go +// work sync generates that build list using the Minimal Version Selection +// algorithm, and then syncs those versions back to each of modules +// specified in the workspace (with use directives). +// +// The syncing is done by sequentially upgrading each of the dependency +// modules specified in a workspace module to the version in the build list +// if the dependency module's version is not already the same as the build +// list's version. Note that Minimal Version Selection guarantees that the +// build list's version of each module is always the same or higher than +// that in each workspace module. +// +// See the workspaces reference at https://go.dev/ref/mod#workspaces +// for more information. +// +// # Add modules to workspace file +// +// Usage: +// +// go work use [-r] moddirs +// +// Use provides a command-line interface for adding +// directories, optionally recursively, to a go.work file. +// +// A use directive will be added to the go.work file for each argument +// directory listed on the command line go.work file, if it exists on disk, +// or removed from the go.work file if it does not exist on disk. +// +// The -r flag searches recursively for modules in the argument +// directories, and the use command operates as if each of the directories +// were specified as arguments: namely, use directives will be added for +// directories that exist, and removed for directories that do not exist. +// +// See the workspaces reference at https://go.dev/ref/mod#workspaces +// for more information. +// +// # Compile and run Go program +// +// Usage: +// +// go run [build flags] [-exec xprog] package [arguments...] // // Run compiles and runs the named main Go package. // Typically the package is specified as a list of .go source files from a single @@ -1353,7 +1608,9 @@ // // By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. // If the -exec flag is given, 'go run' invokes the binary using xprog: -// 'xprog a.out arguments...'. +// +// 'xprog a.out arguments...'. +// // If the -exec flag is not given, GOOS or GOARCH is different from the system // default, and a program named go_$GOOS_$GOARCH_exec can be found // on the current search path, 'go run' invokes the binary using that program, @@ -1361,6 +1618,10 @@ // cross-compiled programs when a simulator or other execution method is // available. // +// By default, 'go run' compiles the binary without generating the information +// used by debuggers, to reduce build time. To include debugger information in +// the binary, use 'go build'. +// // The exit status of Run is not the exit status of the compiled binary. // // For more about build flags, see 'go help build'. @@ -1368,27 +1629,26 @@ // // See also: go build. // -// -// Test packages +// # Test packages // // Usage: // -// go test [build/test flags] [packages] [build/test flags & test binary flags] +// go test [build/test flags] [packages] [build/test flags & test binary flags] // // 'Go test' automates testing the packages named by the import paths. // It prints a summary of the test results in the format: // -// ok archive/tar 0.011s -// FAIL archive/zip 0.022s -// ok compress/gzip 0.033s -// ... +// ok archive/tar 0.011s +// FAIL archive/zip 0.022s +// ok compress/gzip 0.033s +// ... // // followed by detailed output for each failed package. // // 'Go test' recompiles each package along with any files with names matching // the file pattern "*_test.go". -// These additional files can contain test functions, benchmark functions, and -// example functions. See 'go help testfunc' for more. +// These additional files can contain test functions, benchmark functions, fuzz +// tests and example functions. See 'go help testfunc' for more. // Each listed package causes the execution of a separate test binary. // Files whose names begin with "_" (including "_test.go") or "." are ignored. // @@ -1405,7 +1665,8 @@ // used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas', // 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see // the documentation for these and other vet tests via "go doc cmd/vet". -// To disable the running of go vet, use the -vet=off flag. +// To disable the running of go vet, use the -vet=off flag. To run all +// checks, use the -vet=all flag. // // All test output and summary lines are printed to the go command's // standard output, even if the test printed them to its own standard @@ -1446,46 +1707,46 @@ // The rule for a match in the cache is that the run involves the same // test binary and the flags on the command line come entirely from a // restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, -// -list, -parallel, -run, -short, and -v. If a run of go test has any test -// or non-test flags outside this set, the result is not cached. To -// disable test caching, use any test flag or argument other than the -// cacheable flags. The idiomatic way to disable test caching explicitly -// is to use -count=1. Tests that open files within the package's source -// root (usually $GOPATH) or that consult environment variables only -// match future runs in which the files and environment variables are unchanged. -// A cached test result is treated as executing in no time at all, -// so a successful package test result will be cached and reused -// regardless of -timeout setting. +// -list, -parallel, -run, -short, -timeout, -failfast, and -v. +// If a run of go test has any test or non-test flags outside this set, +// the result is not cached. To disable test caching, use any test flag +// or argument other than the cacheable flags. The idiomatic way to disable +// test caching explicitly is to use -count=1. Tests that open files within +// the package's source root (usually $GOPATH) or that consult environment +// variables only match future runs in which the files and environment +// variables are unchanged. A cached test result is treated as executing +// in no time at all, so a successful package test result will be cached and +// reused regardless of -timeout setting. // // In addition to the build flags, the flags handled by 'go test' itself are: // -// -args -// Pass the remainder of the command line (everything after -args) -// to the test binary, uninterpreted and unchanged. -// Because this flag consumes the remainder of the command line, -// the package list (if present) must appear before this flag. +// -args +// Pass the remainder of the command line (everything after -args) +// to the test binary, uninterpreted and unchanged. +// Because this flag consumes the remainder of the command line, +// the package list (if present) must appear before this flag. // -// -c -// Compile the test binary to pkg.test but do not run it -// (where pkg is the last element of the package's import path). -// The file name can be changed with the -o flag. +// -c +// Compile the test binary to pkg.test but do not run it +// (where pkg is the last element of the package's import path). +// The file name can be changed with the -o flag. // -// -exec xprog -// Run the test binary using xprog. The behavior is the same as -// in 'go run'. See 'go help run' for details. +// -exec xprog +// Run the test binary using xprog. The behavior is the same as +// in 'go run'. See 'go help run' for details. // -// -i -// Install packages that are dependencies of the test. -// Do not run the test. -// The -i flag is deprecated. Compiled packages are cached automatically. +// -i +// Install packages that are dependencies of the test. +// Do not run the test. +// The -i flag is deprecated. Compiled packages are cached automatically. // -// -json -// Convert test output to JSON suitable for automated processing. -// See 'go doc test2json' for the encoding details. +// -json +// Convert test output to JSON suitable for automated processing. +// See 'go doc test2json' for the encoding details. // -// -o file -// Compile the test binary to the named file. -// The test still runs (unless -c or -i is specified). +// -o file +// Compile the test binary to the named file. +// The test still runs (unless -c or -i is specified). // // The test binary also accepts flags that control execution of the test; these // flags are also accessible by 'go test'. See 'go help testflag' for details. @@ -1495,12 +1756,11 @@ // // See also: go build, go vet. // -// -// Run specified go tool +// # Run specified go tool // // Usage: // -// go tool [-n] command [args...] +// go tool [-n] command [args...] // // Tool runs the go tool command identified by the arguments. // With no arguments it prints the list of known tools. @@ -1510,12 +1770,11 @@ // // For more about each tool command, see 'go doc cmd/'. // -// -// Print Go version +// # Print Go version // // Usage: // -// go version [-m] [-v] [file ...] +// go version [-m] [-v] [file ...] // // Version prints the build information for Go executables. // @@ -1537,12 +1796,11 @@ // // See also: go doc runtime/debug.BuildInfo. // -// -// Report likely mistakes in packages +// # Report likely mistakes in packages // // Usage: // -// go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages] +// go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages] // // Vet runs the Go vet command on the packages named by the import paths. // @@ -1558,8 +1816,8 @@ // or additional checks. // For example, the 'shadow' analyzer can be built and run using these commands: // -// go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow -// go vet -vettool=$(which shadow) +// go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow +// go vet -vettool=$(which shadow) // // The build flags supported by go vet are those that control package resolution // and execution, such as -n, -x, -v, -tags, and -toolexec. @@ -1567,14 +1825,14 @@ // // See also: go fmt, go fix. // +// # Build constraints // -// Build constraints -// -// A build constraint, also known as a build tag, is a line comment that begins +// A build constraint, also known as a build tag, is a condition under which a +// file should be included in the package. Build constraints are given by a +// line comment that begins // -// //go:build +// //go:build // -// that lists the conditions under which a file should be included in the package. // Constraints may appear in any kind of source file (not just Go), but // they must appear near the top of the file, preceded // only by blank lines and other line comments. These rules mean that in Go @@ -1583,38 +1841,41 @@ // To distinguish build constraints from package documentation, // a build constraint should be followed by a blank line. // -// A build constraint is evaluated as an expression containing options -// combined by ||, &&, and ! operators and parentheses. Operators have -// the same meaning as in Go. +// A build constraint comment is evaluated as an expression containing +// build tags combined by ||, &&, and ! operators and parentheses. +// Operators have the same meaning as in Go. // // For example, the following build constraint constrains a file to // build when the "linux" and "386" constraints are satisfied, or when // "darwin" is satisfied and "cgo" is not: // -// //go:build (linux && 386) || (darwin && !cgo) +// //go:build (linux && 386) || (darwin && !cgo) // // It is an error for a file to have more than one //go:build line. // -// During a particular build, the following words are satisfied: +// During a particular build, the following build tags are satisfied: // -// - the target operating system, as spelled by runtime.GOOS, set with the -// GOOS environment variable. -// - the target architecture, as spelled by runtime.GOARCH, set with the -// GOARCH environment variable. -// - the compiler being used, either "gc" or "gccgo" -// - "cgo", if the cgo command is supported (see CGO_ENABLED in -// 'go help environment'). -// - a term for each Go major release, through the current version: -// "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on. -// - any additional tags given by the -tags flag (see 'go help build'). +// - the target operating system, as spelled by runtime.GOOS, set with the +// GOOS environment variable. +// - the target architecture, as spelled by runtime.GOARCH, set with the +// GOARCH environment variable. +// - "unix", if GOOS is a Unix or Unix-like system. +// - the compiler being used, either "gc" or "gccgo" +// - "cgo", if the cgo command is supported (see CGO_ENABLED in +// 'go help environment'). +// - a term for each Go major release, through the current version: +// "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on. +// - any additional tags given by the -tags flag (see 'go help build'). // // There are no separate build tags for beta or minor releases. // // If a file's name, after stripping the extension and a possible _test suffix, // matches any of the following patterns: -// *_GOOS -// *_GOARCH -// *_GOOS_GOARCH +// +// *_GOOS +// *_GOARCH +// *_GOOS_GOARCH +// // (example: source_windows_amd64.go) where GOOS and GOARCH represent // any known operating system and architecture values respectively, then // the file is considered to have an implicit build constraint requiring @@ -1631,19 +1892,19 @@ // // To keep a file from being considered for the build: // -// //go:build ignore +// //go:build ignore // // (any other unsatisfied word will work as well, but "ignore" is conventional.) // // To build a file only when using cgo, and only on Linux and OS X: // -// //go:build cgo && (linux || darwin) +// //go:build cgo && (linux || darwin) // // Such a file is usually paired with another file implementing the // default functionality for other systems, which in this case would // carry the constraint: // -// //go:build !(cgo && (linux || darwin)) +// //go:build !(cgo && (linux || darwin)) // // Naming a file dns_windows.go will cause it to be included only when // building the package for Windows; similarly, math_386.s will be included @@ -1653,57 +1914,55 @@ // with a "// +build" prefix. The gofmt command will add an equivalent //go:build // constraint when encountering the older syntax. // -// -// Build modes +// # Build modes // // The 'go build' and 'go install' commands take a -buildmode argument which // indicates which kind of object file is to be built. Currently supported values // are: // -// -buildmode=archive -// Build the listed non-main packages into .a files. Packages named -// main are ignored. -// -// -buildmode=c-archive -// Build the listed main package, plus all packages it imports, -// into a C archive file. The only callable symbols will be those -// functions exported using a cgo //export comment. Requires -// exactly one main package to be listed. -// -// -buildmode=c-shared -// Build the listed main package, plus all packages it imports, -// into a C shared library. The only callable symbols will -// be those functions exported using a cgo //export comment. -// Requires exactly one main package to be listed. -// -// -buildmode=default -// Listed main packages are built into executables and listed -// non-main packages are built into .a files (the default -// behavior). -// -// -buildmode=shared -// Combine all the listed non-main packages into a single shared -// library that will be used when building with the -linkshared -// option. Packages named main are ignored. -// -// -buildmode=exe -// Build the listed main packages and everything they import into -// executables. Packages not named main are ignored. -// -// -buildmode=pie -// Build the listed main packages and everything they import into -// position independent executables (PIE). Packages not named -// main are ignored. -// -// -buildmode=plugin -// Build the listed main packages, plus all packages that they -// import, into a Go plugin. Packages not named main are ignored. +// -buildmode=archive +// Build the listed non-main packages into .a files. Packages named +// main are ignored. +// +// -buildmode=c-archive +// Build the listed main package, plus all packages it imports, +// into a C archive file. The only callable symbols will be those +// functions exported using a cgo //export comment. Requires +// exactly one main package to be listed. +// +// -buildmode=c-shared +// Build the listed main package, plus all packages it imports, +// into a C shared library. The only callable symbols will +// be those functions exported using a cgo //export comment. +// Requires exactly one main package to be listed. +// +// -buildmode=default +// Listed main packages are built into executables and listed +// non-main packages are built into .a files (the default +// behavior). +// +// -buildmode=shared +// Combine all the listed non-main packages into a single shared +// library that will be used when building with the -linkshared +// option. Packages named main are ignored. +// +// -buildmode=exe +// Build the listed main packages and everything they import into +// executables. Packages not named main are ignored. +// +// -buildmode=pie +// Build the listed main packages and everything they import into +// position independent executables (PIE). Packages not named +// main are ignored. +// +// -buildmode=plugin +// Build the listed main packages, plus all packages that they +// import, into a Go plugin. Packages not named main are ignored. // // On AIX, when linking a C program that uses a Go archive built with // -buildmode=c-archive, you must pass -Wl,-bnoobjreorder to the C compiler. // -// -// Calling between Go and C +// # Calling between Go and C // // There are two different ways to call between Go and C/C++ code. // @@ -1721,8 +1980,7 @@ // compiler. The CC or CXX environment variables may be set to determine // the C or C++ compiler, respectively, to use. // -// -// Build and test caching +// # Build and test caching // // The go command caches build outputs for reuse in future builds. // The default location for cache data is a subdirectory named go-build @@ -1746,6 +2004,13 @@ // See 'go help test' for details. Running 'go clean -testcache' removes // all cached test results (but not cached build results). // +// The go command also caches values used in fuzzing with 'go test -fuzz', +// specifically, values that expanded code coverage when passed to a +// fuzz function. These values are not used for regular building and +// testing, but they're stored in a subdirectory of the build cache. +// Running 'go clean -fuzzcache' removes all cached fuzzing values. +// This may make fuzzing less effective, temporarily. +// // The GODEBUG environment variable can enable printing of debugging // information about the state of the cache: // @@ -1760,8 +2025,7 @@ // GODEBUG=gocachetest=1 causes the go command to print details of its // decisions about whether to reuse a cached test result. // -// -// Environment variables +// # Environment variables // // The go command and the tools it invokes consult environment variables // for configuration. If an environment variable is unset, the go command @@ -1777,203 +2041,216 @@ // // General-purpose environment variables: // -// GO111MODULE -// Controls whether the go command runs in module-aware mode or GOPATH mode. -// May be "off", "on", or "auto". -// See https://golang.org/ref/mod#mod-commands. -// GCCGO -// The gccgo command to run for 'go build -compiler=gccgo'. -// GOARCH -// The architecture, or processor, for which to compile code. -// Examples are amd64, 386, arm, ppc64. -// GOBIN -// The directory where 'go install' will install a command. -// GOCACHE -// The directory where the go command will store cached -// information for reuse in future builds. -// GOMODCACHE -// The directory where the go command will store downloaded modules. -// GODEBUG -// Enable various debugging facilities. See 'go doc runtime' -// for details. -// GOENV -// The location of the Go environment configuration file. -// Cannot be set using 'go env -w'. -// GOFLAGS -// A space-separated list of -flag=value settings to apply -// to go commands by default, when the given flag is known by -// the current command. Each entry must be a standalone flag. -// Because the entries are space-separated, flag values must -// not contain spaces. Flags listed on the command line -// are applied after this list and therefore override it. -// GOINSECURE -// Comma-separated list of glob patterns (in the syntax of Go's path.Match) -// of module path prefixes that should always be fetched in an insecure -// manner. Only applies to dependencies that are being fetched directly. -// GOINSECURE does not disable checksum database validation. GOPRIVATE or -// GONOSUMDB may be used to achieve that. -// GOOS -// The operating system for which to compile code. -// Examples are linux, darwin, windows, netbsd. -// GOPATH -// For more details see: 'go help gopath'. -// GOPROXY -// URL of Go module proxy. See https://golang.org/ref/mod#environment-variables -// and https://golang.org/ref/mod#module-proxy for details. -// GOPRIVATE, GONOPROXY, GONOSUMDB -// Comma-separated list of glob patterns (in the syntax of Go's path.Match) -// of module path prefixes that should always be fetched directly -// or that should not be compared against the checksum database. -// See https://golang.org/ref/mod#private-modules. -// GOROOT -// The root of the go tree. -// GOSUMDB -// The name of checksum database to use and optionally its public key and -// URL. See https://golang.org/ref/mod#authenticating. -// GOTMPDIR -// The directory where the go command will write -// temporary source files, packages, and binaries. -// GOVCS -// Lists version control commands that may be used with matching servers. -// See 'go help vcs'. +// GO111MODULE +// Controls whether the go command runs in module-aware mode or GOPATH mode. +// May be "off", "on", or "auto". +// See https://golang.org/ref/mod#mod-commands. +// GCCGO +// The gccgo command to run for 'go build -compiler=gccgo'. +// GOARCH +// The architecture, or processor, for which to compile code. +// Examples are amd64, 386, arm, ppc64. +// GOBIN +// The directory where 'go install' will install a command. +// GOCACHE +// The directory where the go command will store cached +// information for reuse in future builds. +// GOMODCACHE +// The directory where the go command will store downloaded modules. +// GODEBUG +// Enable various debugging facilities. See 'go doc runtime' +// for details. +// GOENV +// The location of the Go environment configuration file. +// Cannot be set using 'go env -w'. +// Setting GOENV=off in the environment disables the use of the +// default configuration file. +// GOFLAGS +// A space-separated list of -flag=value settings to apply +// to go commands by default, when the given flag is known by +// the current command. Each entry must be a standalone flag. +// Because the entries are space-separated, flag values must +// not contain spaces. Flags listed on the command line +// are applied after this list and therefore override it. +// GOINSECURE +// Comma-separated list of glob patterns (in the syntax of Go's path.Match) +// of module path prefixes that should always be fetched in an insecure +// manner. Only applies to dependencies that are being fetched directly. +// GOINSECURE does not disable checksum database validation. GOPRIVATE or +// GONOSUMDB may be used to achieve that. +// GOOS +// The operating system for which to compile code. +// Examples are linux, darwin, windows, netbsd. +// GOPATH +// For more details see: 'go help gopath'. +// GOPROXY +// URL of Go module proxy. See https://golang.org/ref/mod#environment-variables +// and https://golang.org/ref/mod#module-proxy for details. +// GOPRIVATE, GONOPROXY, GONOSUMDB +// Comma-separated list of glob patterns (in the syntax of Go's path.Match) +// of module path prefixes that should always be fetched directly +// or that should not be compared against the checksum database. +// See https://golang.org/ref/mod#private-modules. +// GOROOT +// The root of the go tree. +// GOSUMDB +// The name of checksum database to use and optionally its public key and +// URL. See https://golang.org/ref/mod#authenticating. +// GOTMPDIR +// The directory where the go command will write +// temporary source files, packages, and binaries. +// GOVCS +// Lists version control commands that may be used with matching servers. +// See 'go help vcs'. +// GOWORK +// In module aware mode, use the given go.work file as a workspace file. +// By default or when GOWORK is "auto", the go command searches for a +// file named go.work in the current directory and then containing directories +// until one is found. If a valid go.work file is found, the modules +// specified will collectively be used as the main modules. If GOWORK +// is "off", or a go.work file is not found in "auto" mode, workspace +// mode is disabled. // // Environment variables for use with cgo: // -// AR -// The command to use to manipulate library archives when -// building with the gccgo compiler. -// The default is 'ar'. -// CC -// The command to use to compile C code. -// CGO_ENABLED -// Whether the cgo command is supported. Either 0 or 1. -// CGO_CFLAGS -// Flags that cgo will pass to the compiler when compiling -// C code. -// CGO_CFLAGS_ALLOW -// A regular expression specifying additional flags to allow -// to appear in #cgo CFLAGS source code directives. -// Does not apply to the CGO_CFLAGS environment variable. -// CGO_CFLAGS_DISALLOW -// A regular expression specifying flags that must be disallowed -// from appearing in #cgo CFLAGS source code directives. -// Does not apply to the CGO_CFLAGS environment variable. -// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the C preprocessor. -// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the C++ compiler. -// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the Fortran compiler. -// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW -// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, -// but for the linker. -// CXX -// The command to use to compile C++ code. -// FC -// The command to use to compile Fortran code. -// PKG_CONFIG -// Path to pkg-config tool. +// AR +// The command to use to manipulate library archives when +// building with the gccgo compiler. +// The default is 'ar'. +// CC +// The command to use to compile C code. +// CGO_ENABLED +// Whether the cgo command is supported. Either 0 or 1. +// CGO_CFLAGS +// Flags that cgo will pass to the compiler when compiling +// C code. +// CGO_CFLAGS_ALLOW +// A regular expression specifying additional flags to allow +// to appear in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CFLAGS_DISALLOW +// A regular expression specifying flags that must be disallowed +// from appearing in #cgo CFLAGS source code directives. +// Does not apply to the CGO_CFLAGS environment variable. +// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C preprocessor. +// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the C++ compiler. +// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the Fortran compiler. +// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW +// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW, +// but for the linker. +// CXX +// The command to use to compile C++ code. +// FC +// The command to use to compile Fortran code. +// PKG_CONFIG +// Path to pkg-config tool. // // Architecture-specific environment variables: // -// GOARM -// For GOARCH=arm, the ARM architecture for which to compile. -// Valid values are 5, 6, 7. -// GO386 -// For GOARCH=386, how to implement floating point instructions. -// Valid values are sse2 (default), softfloat. -// GOMIPS -// For GOARCH=mips{,le}, whether to use floating point instructions. -// Valid values are hardfloat (default), softfloat. -// GOMIPS64 -// For GOARCH=mips64{,le}, whether to use floating point instructions. -// Valid values are hardfloat (default), softfloat. -// GOPPC64 -// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture). -// Valid values are power8 (default), power9. -// GOWASM -// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. -// Valid values are satconv, signext. +// GOARM +// For GOARCH=arm, the ARM architecture for which to compile. +// Valid values are 5, 6, 7. +// GO386 +// For GOARCH=386, how to implement floating point instructions. +// Valid values are sse2 (default), softfloat. +// GOAMD64 +// For GOARCH=amd64, the microarchitecture level for which to compile. +// Valid values are v1 (default), v2, v3, v4. +// See https://golang.org/wiki/MinimumRequirements#amd64 +// GOMIPS +// For GOARCH=mips{,le}, whether to use floating point instructions. +// Valid values are hardfloat (default), softfloat. +// GOMIPS64 +// For GOARCH=mips64{,le}, whether to use floating point instructions. +// Valid values are hardfloat (default), softfloat. +// GOPPC64 +// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture). +// Valid values are power8 (default), power9, power10. +// GOWASM +// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. +// Valid values are satconv, signext. // // Special-purpose environment variables: // -// GCCGOTOOLDIR -// If set, where to find gccgo tools, such as cgo. -// The default is based on how gccgo was configured. -// GOEXPERIMENT -// Comma-separated list of toolchain experiments to enable or disable. -// The list of available experiments may change arbitrarily over time. -// See src/internal/goexperiment/flags.go for currently valid values. -// Warning: This variable is provided for the development and testing -// of the Go toolchain itself. Use beyond that purpose is unsupported. -// GOROOT_FINAL -// The root of the installed Go tree, when it is -// installed in a location other than where it is built. -// File names in stack traces are rewritten from GOROOT to -// GOROOT_FINAL. -// GO_EXTLINK_ENABLED -// Whether the linker should use external linking mode -// when using -linkmode=auto with code that uses cgo. -// Set to 0 to disable external linking mode, 1 to enable it. -// GIT_ALLOW_PROTOCOL -// Defined by Git. A colon-separated list of schemes that are allowed -// to be used with git fetch/clone. If set, any scheme not explicitly -// mentioned will be considered insecure by 'go get'. -// Because the variable is defined by Git, the default value cannot -// be set using 'go env -w'. +// GCCGOTOOLDIR +// If set, where to find gccgo tools, such as cgo. +// The default is based on how gccgo was configured. +// GOEXPERIMENT +// Comma-separated list of toolchain experiments to enable or disable. +// The list of available experiments may change arbitrarily over time. +// See src/internal/goexperiment/flags.go for currently valid values. +// Warning: This variable is provided for the development and testing +// of the Go toolchain itself. Use beyond that purpose is unsupported. +// GOROOT_FINAL +// The root of the installed Go tree, when it is +// installed in a location other than where it is built. +// File names in stack traces are rewritten from GOROOT to +// GOROOT_FINAL. +// GO_EXTLINK_ENABLED +// Whether the linker should use external linking mode +// when using -linkmode=auto with code that uses cgo. +// Set to 0 to disable external linking mode, 1 to enable it. +// GIT_ALLOW_PROTOCOL +// Defined by Git. A colon-separated list of schemes that are allowed +// to be used with git fetch/clone. If set, any scheme not explicitly +// mentioned will be considered insecure by 'go get'. +// Because the variable is defined by Git, the default value cannot +// be set using 'go env -w'. // // Additional information available from 'go env' but not read from the environment: // -// GOEXE -// The executable file name suffix (".exe" on Windows, "" on other systems). -// GOGCCFLAGS -// A space-separated list of arguments supplied to the CC command. -// GOHOSTARCH -// The architecture (GOARCH) of the Go toolchain binaries. -// GOHOSTOS -// The operating system (GOOS) of the Go toolchain binaries. -// GOMOD -// The absolute path to the go.mod of the main module. -// If module-aware mode is enabled, but there is no go.mod, GOMOD will be -// os.DevNull ("/dev/null" on Unix-like systems, "NUL" on Windows). -// If module-aware mode is disabled, GOMOD will be the empty string. -// GOTOOLDIR -// The directory where the go tools (compile, cover, doc, etc...) are installed. -// GOVERSION -// The version of the installed Go tree, as reported by runtime.Version. -// -// -// File types +// GOEXE +// The executable file name suffix (".exe" on Windows, "" on other systems). +// GOGCCFLAGS +// A space-separated list of arguments supplied to the CC command. +// GOHOSTARCH +// The architecture (GOARCH) of the Go toolchain binaries. +// GOHOSTOS +// The operating system (GOOS) of the Go toolchain binaries. +// GOMOD +// The absolute path to the go.mod of the main module. +// If module-aware mode is enabled, but there is no go.mod, GOMOD will be +// os.DevNull ("/dev/null" on Unix-like systems, "NUL" on Windows). +// If module-aware mode is disabled, GOMOD will be the empty string. +// GOTOOLDIR +// The directory where the go tools (compile, cover, doc, etc...) are installed. +// GOVERSION +// The version of the installed Go tree, as reported by runtime.Version. +// +// # File types // // The go command examines the contents of a restricted set of files // in each directory. It identifies which files to examine based on // the extension of the file name. These extensions are: // -// .go -// Go source files. -// .c, .h -// C source files. -// If the package uses cgo or SWIG, these will be compiled with the -// OS-native compiler (typically gcc); otherwise they will -// trigger an error. -// .cc, .cpp, .cxx, .hh, .hpp, .hxx -// C++ source files. Only useful with cgo or SWIG, and always -// compiled with the OS-native compiler. -// .m -// Objective-C source files. Only useful with cgo, and always -// compiled with the OS-native compiler. -// .s, .S, .sx -// Assembler source files. -// If the package uses cgo or SWIG, these will be assembled with the -// OS-native assembler (typically gcc (sic)); otherwise they -// will be assembled with the Go assembler. -// .swig, .swigcxx -// SWIG definition files. -// .syso -// System object files. +// .go +// Go source files. +// .c, .h +// C source files. +// If the package uses cgo or SWIG, these will be compiled with the +// OS-native compiler (typically gcc); otherwise they will +// trigger an error. +// .cc, .cpp, .cxx, .hh, .hpp, .hxx +// C++ source files. Only useful with cgo or SWIG, and always +// compiled with the OS-native compiler. +// .m +// Objective-C source files. Only useful with cgo, and always +// compiled with the OS-native compiler. +// .s, .S, .sx +// Assembler source files. +// If the package uses cgo or SWIG, these will be assembled with the +// OS-native assembler (typically gcc (sic)); otherwise they +// will be assembled with the Go assembler. +// .swig, .swigcxx +// SWIG definition files. +// .syso +// System object files. // // Files of each of these types except .syso may contain build // constraints, but the go command stops scanning for build constraints @@ -1981,8 +2258,7 @@ // line comment. See the go/build package documentation for // more details. // -// -// The go.mod file +// # The go.mod file // // A module version is defined by a tree of source files, with a go.mod // file in its root. When the go command is run, it looks in the current @@ -2007,8 +2283,7 @@ // use 'go mod edit'. See 'go help mod edit' or // https://golang.org/ref/mod#go-mod-edit. // -// -// GOPATH environment variable +// # GOPATH environment variable // // The Go path is used to resolve import statements. // It is implemented by and documented in the go/build package. @@ -2052,21 +2327,21 @@ // // Here's an example directory layout: // -// GOPATH=/home/user/go -// -// /home/user/go/ -// src/ -// foo/ -// bar/ (go code in package bar) -// x.go -// quux/ (go code in package main) -// y.go -// bin/ -// quux (installed command) -// pkg/ -// linux_amd64/ -// foo/ -// bar.a (installed package object) +// GOPATH=/home/user/go +// +// /home/user/go/ +// src/ +// foo/ +// bar/ (go code in package bar) +// x.go +// quux/ (go code in package main) +// y.go +// bin/ +// quux (installed command) +// pkg/ +// linux_amd64/ +// foo/ +// bar.a (installed package object) // // Go searches each directory listed in GOPATH to find source code, // but new packages are always downloaded into the first directory @@ -2074,33 +2349,32 @@ // // See https://golang.org/doc/code.html for an example. // -// GOPATH and Modules +// # GOPATH and Modules // // When using modules, GOPATH is no longer used for resolving imports. // However, it is still used to store downloaded source code (in GOPATH/pkg/mod) // and compiled commands (in GOPATH/bin). // -// Internal Directories +// # Internal Directories // // Code in or below a directory named "internal" is importable only // by code in the directory tree rooted at the parent of "internal". // Here's an extended version of the directory layout above: // -// /home/user/go/ -// src/ -// crash/ -// bang/ (go code in package bang) -// b.go -// foo/ (go code in package foo) -// f.go -// bar/ (go code in package bar) -// x.go -// internal/ -// baz/ (go code in package baz) -// z.go -// quux/ (go code in package main) -// y.go -// +// /home/user/go/ +// src/ +// crash/ +// bang/ (go code in package bang) +// b.go +// foo/ (go code in package foo) +// f.go +// bar/ (go code in package bar) +// x.go +// internal/ +// baz/ (go code in package baz) +// z.go +// quux/ (go code in package main) +// y.go // // The code in z.go is imported as "foo/internal/baz", but that // import statement can only appear in source files in the subtree @@ -2110,7 +2384,7 @@ // // See https://golang.org/s/go14internal for details. // -// Vendor Directories +// # Vendor Directories // // Go 1.6 includes support for using local copies of external dependencies // to satisfy imports of those dependencies, often referred to as vendoring. @@ -2124,23 +2398,23 @@ // but with the "internal" directory renamed to "vendor" // and a new foo/vendor/crash/bang directory added: // -// /home/user/go/ -// src/ -// crash/ -// bang/ (go code in package bang) -// b.go -// foo/ (go code in package foo) -// f.go -// bar/ (go code in package bar) -// x.go -// vendor/ -// crash/ -// bang/ (go code in package bang) -// b.go -// baz/ (go code in package baz) -// z.go -// quux/ (go code in package main) -// y.go +// /home/user/go/ +// src/ +// crash/ +// bang/ (go code in package bang) +// b.go +// foo/ (go code in package foo) +// f.go +// bar/ (go code in package bar) +// x.go +// vendor/ +// crash/ +// bang/ (go code in package bang) +// b.go +// baz/ (go code in package baz) +// z.go +// quux/ (go code in package main) +// y.go // // The same visibility rules apply as for internal, but the code // in z.go is imported as "baz", not as "foo/vendor/baz". @@ -2162,8 +2436,7 @@ // // See https://golang.org/s/go15vendor for details. // -// -// Legacy GOPATH go get +// # Legacy GOPATH go get // // The 'go get' command changes behavior depending on whether the // go command is running in module-aware mode or legacy GOPATH mode. @@ -2225,8 +2498,7 @@ // // See also: go build, go install, go clean. // -// -// Module proxy protocol +// # Module proxy protocol // // A Go module proxy is any web server that can respond to GET requests for // URLs of a specified form. The requests have no query parameters, so even @@ -2236,15 +2508,14 @@ // For details on the GOPROXY protocol, see // https://golang.org/ref/mod#goproxy-protocol. // -// -// Import path syntax +// # Import path syntax // // An import path (see 'go help packages') denotes a package stored in the local // file system. In general, an import path denotes either a standard package (such // as "unicode/utf8") or a package found in one of the work spaces (For more // details see: 'go help gopath'). // -// Relative import paths +// # Relative import paths // // An import path beginning with ./ or ../ is called a relative path. // The toolchain supports relative import paths as a shortcut in two ways. @@ -2268,7 +2539,7 @@ // To avoid ambiguity, Go programs cannot use relative import paths // within a work space. // -// Remote import paths +// # Remote import paths // // Certain import paths also // describe how to obtain the source code for the package using @@ -2276,29 +2547,29 @@ // // A few common code hosting sites have special syntax: // -// Bitbucket (Git, Mercurial) +// Bitbucket (Git, Mercurial) // -// import "bitbucket.org/user/project" -// import "bitbucket.org/user/project/sub/directory" +// import "bitbucket.org/user/project" +// import "bitbucket.org/user/project/sub/directory" // -// GitHub (Git) +// GitHub (Git) // -// import "github.com/user/project" -// import "github.com/user/project/sub/directory" +// import "github.com/user/project" +// import "github.com/user/project/sub/directory" // -// Launchpad (Bazaar) +// Launchpad (Bazaar) // -// import "launchpad.net/project" -// import "launchpad.net/project/series" -// import "launchpad.net/project/series/sub/directory" +// import "launchpad.net/project" +// import "launchpad.net/project/series" +// import "launchpad.net/project/series/sub/directory" // -// import "launchpad.net/~user/project/branch" -// import "launchpad.net/~user/project/branch/sub/directory" +// import "launchpad.net/~user/project/branch" +// import "launchpad.net/~user/project/branch/sub/directory" // -// IBM DevOps Services (Git) +// IBM DevOps Services (Git) // -// import "hub.jazz.net/git/user/project" -// import "hub.jazz.net/git/user/project/sub/directory" +// import "hub.jazz.net/git/user/project" +// import "hub.jazz.net/git/user/project/sub/directory" // // For code hosted on other servers, import paths may either be qualified // with the version control type, or the go tool can dynamically fetch @@ -2307,26 +2578,26 @@ // // To declare the code location, an import path of the form // -// repository.vcs/path +// repository.vcs/path // // specifies the given repository, with or without the .vcs suffix, // using the named version control system, and then the path inside // that repository. The supported version control systems are: // -// Bazaar .bzr -// Fossil .fossil -// Git .git -// Mercurial .hg -// Subversion .svn +// Bazaar .bzr +// Fossil .fossil +// Git .git +// Mercurial .hg +// Subversion .svn // // For example, // -// import "example.org/user/foo.hg" +// import "example.org/user/foo.hg" // // denotes the root directory of the Mercurial repository at // example.org/user/foo or foo.hg, and // -// import "example.org/repo.git/foo/bar" +// import "example.org/repo.git/foo/bar" // // denotes the foo/bar directory of the Git repository at // example.org/repo or repo.git. @@ -2347,7 +2618,7 @@ // // The meta tag has the form: // -// +// // // The import-prefix is the import path corresponding to the repository // root. It must be a prefix or an exact match of the package being @@ -2365,16 +2636,16 @@ // // For example, // -// import "example.org/pkg/foo" +// import "example.org/pkg/foo" // // will result in the following requests: // -// https://example.org/pkg/foo?go-get=1 (preferred) -// http://example.org/pkg/foo?go-get=1 (fallback, only with use of correctly set GOINSECURE) +// https://example.org/pkg/foo?go-get=1 (preferred) +// http://example.org/pkg/foo?go-get=1 (fallback, only with use of correctly set GOINSECURE) // // If that page contains the meta tag // -// +// // // the go tool will verify that https://example.org/?go-get=1 contains the // same meta tag and then git clone https://code.org/r/p/exproj into @@ -2391,14 +2662,14 @@ // recognized and is preferred over those listing version control systems. // That variant uses "mod" as the vcs in the content value, as in: // -// +// // // This tag means to fetch modules with paths beginning with example.org // from the module proxy available at the URL https://code.org/moduleproxy. // See https://golang.org/ref/mod#goproxy-protocol for details about the // proxy protocol. // -// Import path checking +// # Import path checking // // When the custom import path feature described above redirects to a // known code hosting site, each of the resulting packages has two possible @@ -2407,8 +2678,8 @@ // A package statement is said to have an "import comment" if it is immediately // followed (before the next newline) by a comment of one of these two forms: // -// package math // import "path" -// package math /* import "path" */ +// package math // import "path" +// package math /* import "path" */ // // The go command will refuse to install a package with an import comment // unless it is being referred to by that import path. In this way, import comments @@ -2424,8 +2695,7 @@ // // See https://golang.org/s/go14customimport for details. // -// -// Modules, module versions, and more +// # Modules, module versions, and more // // Modules are how Go manages dependencies. // @@ -2449,8 +2719,7 @@ // GOPRIVATE, and other environment variables. See 'go help environment' // and https://golang.org/ref/mod#private-module-privacy for more information. // -// -// Module authentication using go.sum +// # Module authentication using go.sum // // When the go command downloads a module zip file or go.mod file into the // module cache, it computes a cryptographic hash and compares it with a known @@ -2461,12 +2730,11 @@ // // For details, see https://golang.org/ref/mod#authenticating. // -// -// Package lists and patterns +// # Package lists and patterns // // Many commands apply to a set of packages: // -// go action [packages] +// go action [packages] // // Usually, [packages] is a list of import paths. // @@ -2545,8 +2813,7 @@ // Directory and file names that begin with "." or "_" are ignored // by the go tool, as are directories named "testdata". // -// -// Configuration for downloading non-public code +// # Configuration for downloading non-public code // // The go command defaults to downloading modules from the public Go module // mirror at proxy.golang.org. It also defaults to validating downloaded modules, @@ -2559,7 +2826,7 @@ // glob patterns (in the syntax of Go's path.Match) of module path prefixes. // For example, // -// GOPRIVATE=*.corp.example.com,rsc.io/private +// GOPRIVATE=*.corp.example.com,rsc.io/private // // causes the go command to treat as private any module with a path prefix // matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private, @@ -2573,9 +2840,9 @@ // For example, if a company ran a module proxy serving private modules, // users would configure go using: // -// GOPRIVATE=*.corp.example.com -// GOPROXY=proxy.example.com -// GONOPROXY=none +// GOPRIVATE=*.corp.example.com +// GOPROXY=proxy.example.com +// GONOPROXY=none // // The GOPRIVATE variable is also used to define the "public" and "private" // patterns for the GOVCS variable; see 'go help vcs'. For that usage, @@ -2587,8 +2854,7 @@ // // For more details, see https://golang.org/ref/mod#private-modules. // -// -// Testing flags +// # Testing flags // // The 'go test' command takes both flags that apply to 'go test' itself // and flags that apply to the resulting test binary. @@ -2601,168 +2867,204 @@ // The following flags are recognized by the 'go test' command and // control the execution of any test: // -// -bench regexp -// Run only those benchmarks matching a regular expression. -// By default, no benchmarks are run. -// To run all benchmarks, use '-bench .' or '-bench=.'. -// The regular expression is split by unbracketed slash (/) -// characters into a sequence of regular expressions, and each -// part of a benchmark's identifier must match the corresponding -// element in the sequence, if any. Possible parents of matches -// are run with b.N=1 to identify sub-benchmarks. For example, -// given -bench=X/Y, top-level benchmarks matching X are run -// with b.N=1 to find any sub-benchmarks matching Y, which are -// then run in full. -// -// -benchtime t -// Run enough iterations of each benchmark to take t, specified -// as a time.Duration (for example, -benchtime 1h30s). -// The default is 1 second (1s). -// The special syntax Nx means to run the benchmark N times -// (for example, -benchtime 100x). -// -// -count n -// Run each test and benchmark n times (default 1). -// If -cpu is set, run n times for each GOMAXPROCS value. -// Examples are always run once. -// -// -cover -// Enable coverage analysis. -// Note that because coverage works by annotating the source -// code before compilation, compilation and test failures with -// coverage enabled may report line numbers that don't correspond -// to the original sources. -// -// -covermode set,count,atomic -// Set the mode for coverage analysis for the package[s] -// being tested. The default is "set" unless -race is enabled, -// in which case it is "atomic". -// The values: -// set: bool: does this statement run? -// count: int: how many times does this statement run? -// atomic: int: count, but correct in multithreaded tests; -// significantly more expensive. -// Sets -cover. -// -// -coverpkg pattern1,pattern2,pattern3 -// Apply coverage analysis in each test to packages matching the patterns. -// The default is for each test to analyze only the package being tested. -// See 'go help packages' for a description of package patterns. -// Sets -cover. -// -// -cpu 1,2,4 -// Specify a list of GOMAXPROCS values for which the tests or -// benchmarks should be executed. The default is the current value -// of GOMAXPROCS. -// -// -failfast -// Do not start new tests after the first test failure. -// -// -list regexp -// List tests, benchmarks, or examples matching the regular expression. -// No tests, benchmarks or examples will be run. This will only -// list top-level tests. No subtest or subbenchmarks will be shown. -// -// -parallel n -// Allow parallel execution of test functions that call t.Parallel. -// The value of this flag is the maximum number of tests to run -// simultaneously; by default, it is set to the value of GOMAXPROCS. -// Note that -parallel only applies within a single test binary. -// The 'go test' command may run tests for different packages -// in parallel as well, according to the setting of the -p flag -// (see 'go help build'). -// -// -run regexp -// Run only those tests and examples matching the regular expression. -// For tests, the regular expression is split by unbracketed slash (/) -// characters into a sequence of regular expressions, and each part -// of a test's identifier must match the corresponding element in -// the sequence, if any. Note that possible parents of matches are -// run too, so that -run=X/Y matches and runs and reports the result -// of all tests matching X, even those without sub-tests matching Y, -// because it must run them to look for those sub-tests. -// -// -short -// Tell long-running tests to shorten their run time. -// It is off by default but set during all.bash so that installing -// the Go tree can run a sanity check but not spend time running -// exhaustive tests. -// -// -shuffle off,on,N -// Randomize the execution order of tests and benchmarks. -// It is off by default. If -shuffle is set to on, then it will seed -// the randomizer using the system clock. If -shuffle is set to an -// integer N, then N will be used as the seed value. In both cases, -// the seed will be reported for reproducibility. -// -// -timeout d -// If a test binary runs longer than duration d, panic. -// If d is 0, the timeout is disabled. -// The default is 10 minutes (10m). -// -// -v -// Verbose output: log all tests as they are run. Also print all -// text from Log and Logf calls even if the test succeeds. -// -// -vet list -// Configure the invocation of "go vet" during "go test" -// to use the comma-separated list of vet checks. -// If list is empty, "go test" runs "go vet" with a curated list of -// checks believed to be always worth addressing. -// If list is "off", "go test" does not run "go vet" at all. +// -bench regexp +// Run only those benchmarks matching a regular expression. +// By default, no benchmarks are run. +// To run all benchmarks, use '-bench .' or '-bench=.'. +// The regular expression is split by unbracketed slash (/) +// characters into a sequence of regular expressions, and each +// part of a benchmark's identifier must match the corresponding +// element in the sequence, if any. Possible parents of matches +// are run with b.N=1 to identify sub-benchmarks. For example, +// given -bench=X/Y, top-level benchmarks matching X are run +// with b.N=1 to find any sub-benchmarks matching Y, which are +// then run in full. +// +// -benchtime t +// Run enough iterations of each benchmark to take t, specified +// as a time.Duration (for example, -benchtime 1h30s). +// The default is 1 second (1s). +// The special syntax Nx means to run the benchmark N times +// (for example, -benchtime 100x). +// +// -count n +// Run each test, benchmark, and fuzz seed n times (default 1). +// If -cpu is set, run n times for each GOMAXPROCS value. +// Examples are always run once. -count does not apply to +// fuzz tests matched by -fuzz. +// +// -cover +// Enable coverage analysis. +// Note that because coverage works by annotating the source +// code before compilation, compilation and test failures with +// coverage enabled may report line numbers that don't correspond +// to the original sources. +// +// -covermode set,count,atomic +// Set the mode for coverage analysis for the package[s] +// being tested. The default is "set" unless -race is enabled, +// in which case it is "atomic". +// The values: +// set: bool: does this statement run? +// count: int: how many times does this statement run? +// atomic: int: count, but correct in multithreaded tests; +// significantly more expensive. +// Sets -cover. +// +// -coverpkg pattern1,pattern2,pattern3 +// Apply coverage analysis in each test to packages matching the patterns. +// The default is for each test to analyze only the package being tested. +// See 'go help packages' for a description of package patterns. +// Sets -cover. +// +// -cpu 1,2,4 +// Specify a list of GOMAXPROCS values for which the tests, benchmarks or +// fuzz tests should be executed. The default is the current value +// of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz. +// +// -failfast +// Do not start new tests after the first test failure. +// +// -fuzz regexp +// Run the fuzz test matching the regular expression. When specified, +// the command line argument must match exactly one package within the +// main module, and regexp must match exactly one fuzz test within +// that package. Fuzzing will occur after tests, benchmarks, seed corpora +// of other fuzz tests, and examples have completed. See the Fuzzing +// section of the testing package documentation for details. +// +// -fuzztime t +// Run enough iterations of the fuzz target during fuzzing to take t, +// specified as a time.Duration (for example, -fuzztime 1h30s). +// The default is to run forever. +// The special syntax Nx means to run the fuzz target N times +// (for example, -fuzztime 1000x). +// +// -fuzzminimizetime t +// Run enough iterations of the fuzz target during each minimization +// attempt to take t, as specified as a time.Duration (for example, +// -fuzzminimizetime 30s). +// The default is 60s. +// The special syntax Nx means to run the fuzz target N times +// (for example, -fuzzminimizetime 100x). +// +// -json +// Log verbose output and test results in JSON. This presents the +// same information as the -v flag in a machine-readable format. +// +// -list regexp +// List tests, benchmarks, fuzz tests, or examples matching the regular +// expression. No tests, benchmarks, fuzz tests, or examples will be run. +// This will only list top-level tests. No subtest or subbenchmarks will be +// shown. +// +// -parallel n +// Allow parallel execution of test functions that call t.Parallel, and +// fuzz targets that call t.Parallel when running the seed corpus. +// The value of this flag is the maximum number of tests to run +// simultaneously. +// While fuzzing, the value of this flag is the maximum number of +// subprocesses that may call the fuzz function simultaneously, regardless of +// whether T.Parallel is called. +// By default, -parallel is set to the value of GOMAXPROCS. +// Setting -parallel to values higher than GOMAXPROCS may cause degraded +// performance due to CPU contention, especially when fuzzing. +// Note that -parallel only applies within a single test binary. +// The 'go test' command may run tests for different packages +// in parallel as well, according to the setting of the -p flag +// (see 'go help build'). +// +// -run regexp +// Run only those tests, examples, and fuzz tests matching the regular +// expression. For tests, the regular expression is split by unbracketed +// slash (/) characters into a sequence of regular expressions, and each +// part of a test's identifier must match the corresponding element in +// the sequence, if any. Note that possible parents of matches are +// run too, so that -run=X/Y matches and runs and reports the result +// of all tests matching X, even those without sub-tests matching Y, +// because it must run them to look for those sub-tests. +// +// -short +// Tell long-running tests to shorten their run time. +// It is off by default but set during all.bash so that installing +// the Go tree can run a sanity check but not spend time running +// exhaustive tests. +// +// -shuffle off,on,N +// Randomize the execution order of tests and benchmarks. +// It is off by default. If -shuffle is set to on, then it will seed +// the randomizer using the system clock. If -shuffle is set to an +// integer N, then N will be used as the seed value. In both cases, +// the seed will be reported for reproducibility. +// +// -timeout d +// If a test binary runs longer than duration d, panic. +// If d is 0, the timeout is disabled. +// The default is 10 minutes (10m). +// +// -v +// Verbose output: log all tests as they are run. Also print all +// text from Log and Logf calls even if the test succeeds. +// +// -vet list +// Configure the invocation of "go vet" during "go test" +// to use the comma-separated list of vet checks. +// If list is empty, "go test" runs "go vet" with a curated list of +// checks believed to be always worth addressing. +// If list is "off", "go test" does not run "go vet" at all. // // The following flags are also recognized by 'go test' and can be used to // profile the tests during execution: // -// -benchmem -// Print memory allocation statistics for benchmarks. +// -benchmem +// Print memory allocation statistics for benchmarks. // -// -blockprofile block.out -// Write a goroutine blocking profile to the specified file -// when all tests are complete. -// Writes test binary as -c would. +// -blockprofile block.out +// Write a goroutine blocking profile to the specified file +// when all tests are complete. +// Writes test binary as -c would. // -// -blockprofilerate n -// Control the detail provided in goroutine blocking profiles by -// calling runtime.SetBlockProfileRate with n. -// See 'go doc runtime.SetBlockProfileRate'. -// The profiler aims to sample, on average, one blocking event every -// n nanoseconds the program spends blocked. By default, -// if -test.blockprofile is set without this flag, all blocking events -// are recorded, equivalent to -test.blockprofilerate=1. +// -blockprofilerate n +// Control the detail provided in goroutine blocking profiles by +// calling runtime.SetBlockProfileRate with n. +// See 'go doc runtime.SetBlockProfileRate'. +// The profiler aims to sample, on average, one blocking event every +// n nanoseconds the program spends blocked. By default, +// if -test.blockprofile is set without this flag, all blocking events +// are recorded, equivalent to -test.blockprofilerate=1. // -// -coverprofile cover.out -// Write a coverage profile to the file after all tests have passed. -// Sets -cover. +// -coverprofile cover.out +// Write a coverage profile to the file after all tests have passed. +// Sets -cover. // -// -cpuprofile cpu.out -// Write a CPU profile to the specified file before exiting. -// Writes test binary as -c would. +// -cpuprofile cpu.out +// Write a CPU profile to the specified file before exiting. +// Writes test binary as -c would. // -// -memprofile mem.out -// Write an allocation profile to the file after all tests have passed. -// Writes test binary as -c would. +// -memprofile mem.out +// Write an allocation profile to the file after all tests have passed. +// Writes test binary as -c would. // -// -memprofilerate n -// Enable more precise (and expensive) memory allocation profiles by -// setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. -// To profile all memory allocations, use -test.memprofilerate=1. +// -memprofilerate n +// Enable more precise (and expensive) memory allocation profiles by +// setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'. +// To profile all memory allocations, use -test.memprofilerate=1. // -// -mutexprofile mutex.out -// Write a mutex contention profile to the specified file -// when all tests are complete. -// Writes test binary as -c would. +// -mutexprofile mutex.out +// Write a mutex contention profile to the specified file +// when all tests are complete. +// Writes test binary as -c would. // -// -mutexprofilefraction n -// Sample 1 in n stack traces of goroutines holding a -// contended mutex. +// -mutexprofilefraction n +// Sample 1 in n stack traces of goroutines holding a +// contended mutex. // -// -outputdir directory -// Place output files from profiling in the specified directory, -// by default the directory in which "go test" is running. +// -outputdir directory +// Place output files from profiling in the specified directory, +// by default the directory in which "go test" is running. // -// -trace trace.out -// Write an execution trace to the specified file before exiting. +// -trace trace.out +// Write an execution trace to the specified file before exiting. // // Each of these flags is also recognized with an optional 'test.' prefix, // as in -test.v. When invoking the generated test binary (the result of @@ -2774,11 +3076,11 @@ // // For instance, the command // -// go test -v -myflag testdata -cpuprofile=prof.out -x +// go test -v -myflag testdata -cpuprofile=prof.out -x // // will compile the test binary and then run it as // -// pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out +// pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out // // (The -x flag is removed because it applies only to the go command's // execution, not to the test itself.) @@ -2789,7 +3091,11 @@ // When 'go test' runs a test binary, it does so from within the // corresponding package's source code directory. Depending on the test, // it may be necessary to do the same when invoking a generated test -// binary directly. +// binary directly. Because that directory may be located within the +// module cache, which may be read-only and is verified by checksums, the +// test must not write to it or any other directory within the module +// unless explicitly requested by the user (such as with the -fuzz flag, +// which writes failures to testdata/fuzz). // // The command-line package list, if present, must appear before any // flag not known to the go test command. Continuing the example above, @@ -2809,27 +3115,26 @@ // // For instance, the command // -// go test -v -args -x -v +// go test -v -args -x -v // // will compile the test binary and then run it as // -// pkg.test -test.v -x -v +// pkg.test -test.v -x -v // // Similarly, // -// go test -args math +// go test -args math // // will compile the test binary and then run it as // -// pkg.test math +// pkg.test math // // In the first example, the -x and the second -v are passed through to the // test binary unchanged and with no effect on the go command itself. // In the second example, the argument math is passed through to the test // binary, instead of being interpreted as the package list. // -// -// Testing functions +// # Testing functions // // The 'go test' command expects to find test, benchmark, and example functions // in the "*_test.go" files corresponding to the package under test. @@ -2837,11 +3142,15 @@ // A test function is one named TestXxx (where Xxx does not start with a // lower case letter) and should have the signature, // -// func TestXxx(t *testing.T) { ... } +// func TestXxx(t *testing.T) { ... } // // A benchmark function is one named BenchmarkXxx and should have the signature, // -// func BenchmarkXxx(b *testing.B) { ... } +// func BenchmarkXxx(b *testing.B) { ... } +// +// A fuzz test is one named FuzzXxx and should have the signature, +// +// func FuzzXxx(f *testing.F) { ... } // // An example function is similar to a test function but, instead of using // *testing.T to report success or failure, prints output to os.Stdout. @@ -2860,34 +3169,33 @@ // // Here is an example of an example: // -// func ExamplePrintln() { -// Println("The output of\nthis example.") -// // Output: The output of -// // this example. -// } +// func ExamplePrintln() { +// Println("The output of\nthis example.") +// // Output: The output of +// // this example. +// } // // Here is another example where the ordering of the output is ignored: // -// func ExamplePerm() { -// for _, value := range Perm(4) { -// fmt.Println(value) -// } +// func ExamplePerm() { +// for _, value := range Perm(4) { +// fmt.Println(value) +// } // -// // Unordered output: 4 -// // 2 -// // 1 -// // 3 -// // 0 -// } +// // Unordered output: 4 +// // 2 +// // 1 +// // 3 +// // 0 +// } // // The entire test file is presented as the example when it contains a single // example function, at least one other function, type, variable, or constant -// declaration, and no test or benchmark functions. +// declaration, and no tests, benchmarks, or fuzz tests. // // See the documentation of the testing package for more information. // -// -// Controlling version control with GOVCS +// # Controlling version control with GOVCS // // The 'go get' command can run version control commands like git // to download imported code. This functionality is critical to the decentralized @@ -2936,7 +3244,7 @@ // // For example, consider: // -// GOVCS=github.com:git,evil.com:off,*:git|hg +// GOVCS=github.com:git,evil.com:off,*:git|hg // // With this setting, code with a module or import path beginning with // github.com/ can only use git; paths on evil.com cannot use any version @@ -2953,14 +3261,12 @@ // // To allow unfettered use of any version control system for any package, use: // -// GOVCS=*:all +// GOVCS=*:all // // To disable all use of version control, use: // -// GOVCS=*:off +// GOVCS=*:off // // The 'go env -w' command (see 'go help env') can be used to set the GOVCS // variable for future go command invocations. -// -// package main diff --git a/src/cmd/go/export_test.go b/src/cmd/go/export_test.go new file mode 100644 index 00000000000000..155ab8c1bbeb19 --- /dev/null +++ b/src/cmd/go/export_test.go @@ -0,0 +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 main + +func Main() { main() } diff --git a/src/cmd/go/go11.go b/src/cmd/go/go11.go index a1f2727825ede1..9faa7cba42e097 100644 --- a/src/cmd/go/go11.go +++ b/src/cmd/go/go11.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.1 -// +build go1.1 package main diff --git a/src/cmd/go/go_boring_test.go b/src/cmd/go/go_boring_test.go new file mode 100644 index 00000000000000..ed0fbf3d53d75b --- /dev/null +++ b/src/cmd/go/go_boring_test.go @@ -0,0 +1,22 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build boringcrypto + +package main_test + +import "testing" + +func TestBoringInternalLink(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("main.go", `package main + import "crypto/sha1" + func main() { + sha1.New() + }`) + tg.run("build", "-ldflags=-w -extld=false", tg.path("main.go")) + tg.run("build", "-ldflags=-extld=false", tg.path("main.go")) +} diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 6ce276537babd8..c3e248a49f6a75 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -13,7 +13,7 @@ import ( "flag" "fmt" "go/format" - "internal/race" + "internal/godebug" "internal/testenv" "io" "io/fs" @@ -31,8 +31,11 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/robustio" + "cmd/go/internal/search" "cmd/go/internal/work" "cmd/internal/sys" + + cmdgo "cmd/go" ) func init() { @@ -44,9 +47,17 @@ func init() { } var ( - canRace = false // whether we can run the race detector - canCgo = false // whether we can use cgo - canMSan = false // whether we can run the memory sanitizer + canRace = false // whether we can run the race detector + canCgo = false // whether we can use cgo + canMSan = false // whether we can run the memory sanitizer + canASan = false // whether we can run the address sanitizer + canFuzz = false // whether we can search for new fuzz failures + fuzzInstrumented = false // whether fuzzing uses instrumentation +) + +var ( + goHostOS, goHostArch string + cgoEnabled string // raw value from 'go env CGO_ENABLED' ) var exeSuffix string = func() string { @@ -72,6 +83,10 @@ func tooSlow(t *testing.T) { // (temp) directory. var testGOROOT string +// testGOROOT_FINAL is the GOROOT_FINAL with which the test binary is assumed to +// have been built. +var testGOROOT_FINAL = os.Getenv("GOROOT_FINAL") + var testGOCACHE string var testGo string @@ -81,6 +96,45 @@ var testBin string // The TestMain function creates a go command for testing purposes and // deletes it after the tests have been run. func TestMain(m *testing.M) { + // When CMDGO_TEST_RUN_MAIN is set, we're reusing the test binary as cmd/go. + // Enable the special behavior needed in cmd/go/internal/work, + // run the main func exported via export_test.go, and exit. + // We set CMDGO_TEST_RUN_MAIN via os.Setenv and testScript.setup. + if os.Getenv("CMDGO_TEST_RUN_MAIN") != "" { + cfg.SetGOROOT(cfg.GOROOT, true) + + if v := os.Getenv("TESTGO_VERSION"); v != "" { + work.RuntimeVersion = v + } + + if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" { + // Disallow installs to the GOROOT from which testgo was built. + // Installs to other GOROOTs — such as one set explicitly within a test — are ok. + work.AllowInstall = func(a *work.Action) error { + if cfg.BuildN { + return nil + } + + rel := search.InDir(a.Target, testGOROOT) + if rel == "" { + return nil + } + + callerPos := "" + if _, file, line, ok := runtime.Caller(1); ok { + if shortFile := search.InDir(file, filepath.Join(testGOROOT, "src")); shortFile != "" { + file = shortFile + } + callerPos = fmt.Sprintf("%s:%d: ", file, line) + } + return fmt.Errorf("%stestgo must not write to GOROOT (installing to %s)", callerPos, filepath.Join("GOROOT", rel)) + } + } + cmdgo.Main() + os.Exit(0) + } + os.Setenv("CMDGO_TEST_RUN_MAIN", "true") + // $GO_GCFLAGS a compiler debug flag known to cmd/dist, make.bash, etc. // It is not a standard go command flag; use os.Getenv, not cfg.Getenv. if os.Getenv("GO_GCFLAGS") != "" { @@ -124,13 +178,9 @@ func TestMain(m *testing.M) { log.Fatal(err) } testGo = filepath.Join(testBin, "go"+exeSuffix) - args := []string{"build", "-tags", "testgo", "-o", testGo} - if race.Enabled { - args = append(args, "-race") - } gotool, err := testenv.GoTool() if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr, "locating go tool: ", err) os.Exit(2) } @@ -161,36 +211,47 @@ func TestMain(m *testing.M) { // which will cause many tests to do unnecessary rebuilds and some // tests to attempt to overwrite the installed standard library. // Bail out entirely in this case. - hostGOOS := goEnv("GOHOSTOS") - hostGOARCH := goEnv("GOHOSTARCH") - if hostGOOS != runtime.GOOS || hostGOARCH != runtime.GOARCH { - fmt.Fprintf(os.Stderr, "testing: warning: no tests to run\n") // magic string for cmd/go - fmt.Printf("cmd/go test is not compatible with GOOS/GOARCH != GOHOSTOS/GOHOSTARCH (%s/%s != %s/%s)\n", runtime.GOOS, runtime.GOARCH, hostGOOS, hostGOARCH) - fmt.Printf("SKIP\n") - return - } + goHostOS = goEnv("GOHOSTOS") + os.Setenv("TESTGO_GOHOSTOS", goHostOS) + goHostArch = goEnv("GOHOSTARCH") + os.Setenv("TESTGO_GOHOSTARCH", goHostArch) - buildCmd := exec.Command(gotool, args...) - buildCmd.Env = append(os.Environ(), "GOFLAGS=-mod=vendor") - out, err := buildCmd.CombinedOutput() + cgoEnabled = goEnv("CGO_ENABLED") + canCgo, err = strconv.ParseBool(cgoEnabled) if err != nil { - fmt.Fprintf(os.Stderr, "building testgo failed: %v\n%s", err, out) + fmt.Fprintf(os.Stderr, "can't parse go env CGO_ENABLED output: %q\n", strings.TrimSpace(cgoEnabled)) os.Exit(2) } - cmd := exec.Command(testGo, "env", "CGO_ENABLED") - cmd.Stderr = new(strings.Builder) - if out, err := cmd.Output(); err != nil { - fmt.Fprintf(os.Stderr, "running testgo failed: %v\n%s", err, cmd.Stderr) - os.Exit(2) - } else { - canCgo, err = strconv.ParseBool(strings.TrimSpace(string(out))) + // Duplicate the test executable into the path at testGo, for $PATH. + // If the OS supports symlinks, use them instead of copying bytes. + testExe, err := os.Executable() + if err != nil { + log.Fatal(err) + } + if err := os.Symlink(testExe, testGo); err != nil { + // Otherwise, copy the bytes. + src, err := os.Open(testExe) + if err != nil { + log.Fatal(err) + } + defer src.Close() + + dst, err := os.OpenFile(testGo, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o777) + if err != nil { + log.Fatal(err) + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } if err != nil { - fmt.Fprintf(os.Stderr, "can't parse go env CGO_ENABLED output: %v\n", strings.TrimSpace(string(out))) + log.Fatal(err) } } - out, err = exec.Command(gotool, "env", "GOCACHE").CombinedOutput() + out, err := exec.Command(gotool, "env", "GOCACHE").CombinedOutput() if err != nil { fmt.Fprintf(os.Stderr, "could not find testing GOCACHE: %v\n%s", err, out) os.Exit(2) @@ -198,6 +259,7 @@ func TestMain(m *testing.M) { testGOCACHE = strings.TrimSpace(string(out)) canMSan = canCgo && sys.MSanSupported(runtime.GOOS, runtime.GOARCH) + canASan = canCgo && sys.ASanSupported(runtime.GOOS, runtime.GOARCH) canRace = canCgo && sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) // The race detector doesn't work on Alpine Linux: // golang.org/issue/14481 @@ -205,7 +267,10 @@ func TestMain(m *testing.M) { if isAlpineLinux() || runtime.Compiler == "gccgo" { canRace = false } + canFuzz = sys.FuzzSupported(runtime.GOOS, runtime.GOARCH) + fuzzInstrumented = sys.FuzzInstrumented(runtime.GOOS, runtime.GOARCH) } + // Don't let these environment variables confuse the test. os.Setenv("GOENV", "off") os.Unsetenv("GOFLAGS") @@ -221,6 +286,14 @@ func TestMain(m *testing.M) { os.Setenv("GOCACHE", testGOCACHE) // because $HOME is gone } + if testenv.Builder() != "" || os.Getenv("GIT_TRACE_CURL") == "1" { + // To help diagnose https://go.dev/issue/52545, + // enable tracing for Git HTTPS requests. + os.Setenv("GIT_TRACE_CURL", "1") + os.Setenv("GIT_TRACE_CURL_NO_DATA", "1") + os.Setenv("GIT_REDACT_COOKIES", "o,SSO,GSSO_Uberproxy") + } + r := m.Run() if !*testWork { removeAll(testTmpDir) // os.Exit won't run defer @@ -806,11 +879,13 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { "src/internal/abi", "src/internal/bytealg", "src/internal/cpu", + "src/internal/goarch", "src/internal/goexperiment", + "src/internal/goos", "src/math/bits", "src/unsafe", filepath.Join("pkg", runtime.GOOS+"_"+runtime.GOARCH), - filepath.Join("pkg/tool", runtime.GOOS+"_"+runtime.GOARCH), + filepath.Join("pkg/tool", goHostOS+"_"+goHostArch), "pkg/include", } { srcdir := filepath.Join(testGOROOT, copydir) @@ -915,21 +990,6 @@ func TestIssue10952(t *testing.T) { tg.run("get", "-d", "-u", importPath) } -func TestIssue16471(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - testenv.MustHaveExecPath(t, "git") - - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempDir("src") - tg.setenv("GOPATH", tg.path(".")) - tg.must(os.MkdirAll(tg.path("src/rsc.io/go-get-issue-10952"), 0755)) - tg.runGit(tg.path("src/rsc.io"), "clone", "https://github.com/zombiezen/go-get-issue-10952") - tg.runFail("get", "-u", "rsc.io/go-get-issue-10952") - tg.grepStderr("rsc.io/go-get-issue-10952 is a custom import path for https://github.com/rsc/go-get-issue-10952, but .* is checked out from https://github.com/zombiezen/go-get-issue-10952", "did not detect updated import path") -} - // Test git clone URL that uses SCP-like syntax and custom import path checking. func TestIssue11457(t *testing.T) { testenv.MustHaveExternalNetwork(t) @@ -981,29 +1041,6 @@ func TestGetGitDefaultBranch(t *testing.T) { tg.grepStdout(`\* another-branch`, "not on correct default branch") } -// Security issue. Don't disable. See golang.org/issue/22125. -func TestAccidentalGitCheckout(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - testenv.MustHaveExecPath(t, "git") - testenv.MustHaveExecPath(t, "svn") - - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempDir("src") - - tg.setenv("GOPATH", tg.path(".")) - - tg.runFail("get", "-u", "vcs-test.golang.org/go/test1-svn-git") - tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason") - - if _, err := os.Stat(tg.path("SrC")); err == nil { - // This case only triggers on a case-insensitive file system. - tg.runFail("get", "-u", "vcs-test.golang.org/go/test2-svn-git/test2main") - tg.grepStderr("src[\\\\/]vcs-test.* uses git, but parent .*src[\\\\/]vcs-test.* uses svn", "get did not fail for right reason") - } -} - func TestPackageMainTestCompilerFlags(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -1120,11 +1157,11 @@ func TestGoListTest(t *testing.T) { tg.grepStdoutNot(`^testing \[sort.test\]$`, "unexpected test copy of testing") tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing") - tg.run("list", "-test", "cmd/dist", "cmd/doc") - tg.grepStdout(`^cmd/dist$`, "missing cmd/dist") + tg.run("list", "-test", "cmd/buildid", "cmd/doc") + tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid") tg.grepStdout(`^cmd/doc$`, "missing cmd/doc") tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test") - tg.grepStdoutNot(`^cmd/dist\.test$`, "unexpected cmd/dist test") + tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test") tg.grepStdoutNot(`^testing`, "unexpected testing") tg.run("list", "-test", "runtime/cgo") @@ -1287,6 +1324,15 @@ func tempEnvName() string { } } +func pathEnvName() string { + switch runtime.GOOS { + case "plan9": + return "path" + default: + return "PATH" + } +} + func TestDefaultGOPATH(t *testing.T) { tg := testgo(t) defer tg.cleanup() @@ -1376,7 +1422,7 @@ func TestLdFlagsLongArgumentsIssue42295(t *testing.T) { }`) testStr := "test test test test test \n\\ " var buf bytes.Buffer - for buf.Len() < work.ArgLengthForResponseFile+1 { + for buf.Len() < sys.ExecArgLengthLimit+1 { buf.WriteString(testStr) } tg.run("run", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go")) @@ -1503,7 +1549,7 @@ func TestCgoHandlesWlORIGIN(t *testing.T) { defer tg.cleanup() tg.parallel() tg.tempFile("src/origin/origin.go", `package origin - // #cgo !darwin LDFLAGS: -Wl,-rpath,$ORIGIN + // #cgo !darwin,!windows LDFLAGS: -Wl,-rpath,$ORIGIN // void f(void) {} import "C" func f() { C.f() }`) @@ -1567,7 +1613,7 @@ func TestListTemplateContextFunction(t *testing.T) { }{ {"GOARCH", runtime.GOARCH}, {"GOOS", runtime.GOOS}, - {"GOROOT", filepath.Clean(runtime.GOROOT())}, + {"GOROOT", testGOROOT}, {"GOPATH", os.Getenv("GOPATH")}, {"CgoEnabled", ""}, {"UseAllFiles", ""}, @@ -1835,8 +1881,12 @@ func TestBinaryOnlyPackages(t *testing.T) { tg.grepStdout("p2: false", "p2 listed as BinaryOnly") } -// Issue 16050. -func TestAlwaysLinkSysoFiles(t *testing.T) { +// Issue 16050 and 21884. +func TestLinkSysoFiles(t *testing.T) { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("not linux/amd64") + } + tg := testgo(t) defer tg.cleanup() tg.parallel() @@ -1855,6 +1905,10 @@ func TestAlwaysLinkSysoFiles(t *testing.T) { tg.setenv("CGO_ENABLED", "0") tg.run("list", "-f", "{{.SysoFiles}}", "syso") tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=0") + + tg.setenv("CGO_ENABLED", "1") + tg.run("list", "-msan", "-f", "{{.SysoFiles}}", "syso") + tg.grepStdoutNot("a.syso", "unexpected syso file with -msan") } // Issue 16120. @@ -2069,6 +2123,10 @@ func TestBuildmodePIE(t *testing.T) { default: t.Skipf("skipping test because buildmode=pie is not supported on %s", platform) } + // Skip on alpine until https://go.dev/issues/54354 resolved. + if strings.HasSuffix(testenv.Builder(), "-alpine") { + t.Skip("skipping PIE tests on alpine; see https://go.dev/issues/54354") + } t.Run("non-cgo", func(t *testing.T) { testBuildmodePIE(t, false, true) }) @@ -2274,7 +2332,7 @@ func TestUpxCompression(t *testing.T) { func TestCacheListStale(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2297,7 +2355,7 @@ func TestCacheListStale(t *testing.T) { func TestCacheCoverage(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } @@ -2318,6 +2376,8 @@ func TestIssue22588(t *testing.T) { defer tg.cleanup() tg.parallel() + tg.wantNotStale("runtime", "", "must be non-stale to compare staleness under -toolexec") + if _, err := os.Stat("/usr/bin/time"); err != nil { t.Skip(err) } @@ -2329,7 +2389,7 @@ func TestIssue22588(t *testing.T) { func TestIssue22531(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2358,7 +2418,7 @@ func TestIssue22531(t *testing.T) { func TestIssue22596(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) @@ -2388,7 +2448,7 @@ func TestIssue22596(t *testing.T) { func TestTestCache(t *testing.T) { tooSlow(t) - if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { + if godebug.Get("gocacheverify") == "1" { t.Skip("GODEBUG gocacheverify") } tg := testgo(t) diff --git a/src/cmd/go/go_unix_test.go b/src/cmd/go/go_unix_test.go index 7d5ff9bbb74839..bab94944014afa 100644 --- a/src/cmd/go/go_unix_test.go +++ b/src/cmd/go/go_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package main_test diff --git a/src/cmd/go/help_test.go b/src/cmd/go/help_test.go index abfc3db9936bb9..3e1d817ca5b236 100644 --- a/src/cmd/go/help_test.go +++ b/src/cmd/go/help_test.go @@ -6,6 +6,8 @@ package main_test import ( "bytes" + "go/format" + diffpkg "internal/diff" "os" "testing" @@ -23,11 +25,17 @@ func TestDocsUpToDate(t *testing.T) { buf := new(bytes.Buffer) // Match the command in mkalldocs.sh that generates alldocs.go. help.Help(buf, []string{"documentation"}) - data, err := os.ReadFile("alldocs.go") + internal := buf.Bytes() + internal, err := format.Source(internal) + if err != nil { + t.Fatalf("gofmt docs: %v", err) + } + alldocs, err := os.ReadFile("alldocs.go") if err != nil { t.Fatalf("error reading alldocs.go: %v", err) } - if !bytes.Equal(data, buf.Bytes()) { - t.Errorf("alldocs.go is not up to date; run mkalldocs.sh to regenerate it") + if !bytes.Equal(internal, alldocs) { + t.Errorf("alldocs.go is not up to date; run mkalldocs.sh to regenerate it\n%s", + diffpkg.Diff("go help documentation | gofmt", internal, "alldocs.go", alldocs)) } } diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go index 954ce47a989932..d4af4dbc4b0ff3 100644 --- a/src/cmd/go/internal/base/base.go +++ b/src/cmd/go/internal/base/base.go @@ -10,9 +10,9 @@ import ( "context" "flag" "fmt" - exec "internal/execabs" "log" "os" + "os/exec" "strings" "sync" @@ -117,12 +117,12 @@ func Exit() { os.Exit(exitStatus) } -func Fatalf(format string, args ...interface{}) { +func Fatalf(format string, args ...any) { Errorf(format, args...) Exit() } -func Errorf(format string, args ...interface{}) { +func Errorf(format string, args ...any) { log.Printf(format, args...) SetExitStatus(1) } @@ -151,7 +151,7 @@ func GetExitStatus() int { // Run runs the command, with stdout and stderr // connected to the go command's own stdout and stderr. // If the command fails, Run reports the error using Errorf. -func Run(cmdargs ...interface{}) { +func Run(cmdargs ...any) { cmdline := str.StringList(cmdargs...) if cfg.BuildN || cfg.BuildX { fmt.Printf("%s\n", strings.Join(cmdline, " ")) diff --git a/src/cmd/go/internal/base/env.go b/src/cmd/go/internal/base/env.go index 5f2665d2367082..20ae06d67b4cb1 100644 --- a/src/cmd/go/internal/base/env.go +++ b/src/cmd/go/internal/base/env.go @@ -4,12 +4,43 @@ package base +import ( + "cmd/go/internal/cfg" + "fmt" + "os" + "path/filepath" + "runtime" +) + // AppendPWD returns the result of appending PWD=dir to the environment base. // // The resulting environment makes os.Getwd more efficient for a subprocess -// running in dir. +// running in dir, and also improves the accuracy of paths relative to dir +// if one or more elements of dir is a symlink. func AppendPWD(base []string, dir string) []string { - // Internally we only use absolute paths, so dir is absolute. - // Even if dir is not absolute, no harm done. + // POSIX requires PWD to be absolute. + // Internally we only use absolute paths, so dir should already be absolute. + if !filepath.IsAbs(dir) { + panic(fmt.Sprintf("AppendPWD with relative path %q", dir)) + } return append(base, "PWD="+dir) } + +// AppendPATH returns the result of appending PATH=$GOROOT/bin:$PATH +// (or the platform equivalent) to the environment base. +func AppendPATH(base []string) []string { + if cfg.GOROOTbin == "" { + return base + } + + pathVar := "PATH" + if runtime.GOOS == "plan9" { + pathVar = "path" + } + + path := os.Getenv(pathVar) + if path == "" { + return append(base, pathVar+"="+cfg.GOROOTbin) + } + return append(base, pathVar+"="+cfg.GOROOTbin+string(os.PathListSeparator)+path) +} diff --git a/src/cmd/go/internal/base/flag.go b/src/cmd/go/internal/base/flag.go index 677f8196827f4c..120420a1265598 100644 --- a/src/cmd/go/internal/base/flag.go +++ b/src/cmd/go/internal/base/flag.go @@ -9,7 +9,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/fsys" - "cmd/go/internal/str" + "cmd/internal/quoted" ) // A StringsFlag is a command-line flag that interprets its argument @@ -18,7 +18,7 @@ type StringsFlag []string func (v *StringsFlag) Set(s string) error { var err error - *v, err = str.SplitQuotedFields(s) + *v, err = quoted.Split(s) if *v == nil { *v = []string{} } diff --git a/src/cmd/go/internal/base/signal_notunix.go b/src/cmd/go/internal/base/signal_notunix.go index 5cc0b0f1011e5a..682705f9b2cb41 100644 --- a/src/cmd/go/internal/base/signal_notunix.go +++ b/src/cmd/go/internal/base/signal_notunix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 || windows -// +build plan9 windows package base diff --git a/src/cmd/go/internal/base/signal_unix.go b/src/cmd/go/internal/base/signal_unix.go index cdc2658372e429..f198df6abcdbc5 100644 --- a/src/cmd/go/internal/base/signal_unix.go +++ b/src/cmd/go/internal/base/signal_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || js || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js linux netbsd openbsd solaris +//go:build unix || js package base diff --git a/src/cmd/go/internal/base/tool.go b/src/cmd/go/internal/base/tool.go index d0da65e03ced64..202e314b9442e4 100644 --- a/src/cmd/go/internal/base/tool.go +++ b/src/cmd/go/internal/base/tool.go @@ -9,34 +9,20 @@ import ( "go/build" "os" "path/filepath" - "runtime" "cmd/go/internal/cfg" ) -// Configuration for finding tool binaries. -var ( - ToolGOOS = runtime.GOOS - ToolGOARCH = runtime.GOARCH - ToolIsWindows = ToolGOOS == "windows" - ToolDir = build.ToolDir -) - -const ToolWindowsExtension = ".exe" - // Tool returns the path to the named tool (for example, "vet"). // If the tool cannot be found, Tool exits the process. func Tool(toolName string) string { - toolPath := filepath.Join(ToolDir, toolName) - if ToolIsWindows { - toolPath += ToolWindowsExtension - } + toolPath := filepath.Join(build.ToolDir, toolName) + cfg.ToolExeSuffix() if len(cfg.BuildToolexec) > 0 { return toolPath } // Give a nice message if there is no tool with that name. if _, err := os.Stat(toolPath); err != nil { - fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName) SetExitStatus(2) Exit() } diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go index 307527c695cbed..f1c6b413286bdb 100644 --- a/src/cmd/go/internal/bug/bug.go +++ b/src/cmd/go/internal/bug/bug.go @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package bug implements the ``go bug'' command. +// Package bug implements the “go bug” command. package bug import ( "bytes" "context" "fmt" - exec "internal/execabs" "io" urlpkg "net/url" "os" + "os/exec" "path/filepath" "regexp" "runtime" @@ -22,6 +22,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/envcmd" "cmd/go/internal/web" + "cmd/go/internal/work" ) var CmdBug = &base.Command{ @@ -40,8 +41,10 @@ func init() { func runBug(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go bug: bug takes no arguments") + base.Fatalf("go: bug takes no arguments") } + work.BuildInit() + var buf bytes.Buffer buf.WriteString(bugHeader) printGoVersion(&buf) @@ -106,8 +109,9 @@ func printGoEnv(w io.Writer) { } func printGoDetails(w io.Writer) { - printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version") - printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V") + gocmd := filepath.Join(runtime.GOROOT(), "bin/go") + printCmdOut(w, "GOROOT/bin/go version: ", gocmd, "version") + printCmdOut(w, "GOROOT/bin/go tool compile -V: ", gocmd, "tool", "compile", "-V") } func printOSDetails(w io.Writer) { diff --git a/src/cmd/go/internal/cache/cache.go b/src/cmd/go/internal/cache/cache.go index d592d7049786ce..c30d7c864bf0f7 100644 --- a/src/cmd/go/internal/cache/cache.go +++ b/src/cmd/go/internal/cache/cache.go @@ -20,6 +20,7 @@ import ( "time" "cmd/go/internal/lockedfile" + "cmd/go/internal/mmap" ) // An ActionID is a cache action key, the hash of a complete description of a @@ -47,7 +48,6 @@ type Cache struct { // to share a cache directory (for example, if the directory were stored // in a network file system). File locking is notoriously unreliable in // network file systems and may not suffice to protect the cache. -// func Open(dir string) (*Cache, error) { info, err := os.Stat(dir) if err != nil { @@ -245,6 +245,24 @@ func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) { return data, entry, nil } +// GetMmap looks up the action ID in the cache and returns +// the corresponding output bytes. +// GetMmap should only be used for data that can be expected to fit in memory. +func (c *Cache) GetMmap(id ActionID) ([]byte, Entry, error) { + entry, err := c.Get(id) + if err != nil { + return nil, entry, err + } + md, err := mmap.Mmap(c.OutputFile(entry.OutputID)) + if err != nil { + return nil, Entry{}, err + } + if int64(len(md.Data)) != entry.Size { + return nil, Entry{}, &entryNotFoundError{Err: errors.New("file incomplete")} + } + return md.Data, entry, nil +} + // OutputFile returns the name of the cache file storing output with the given OutputID. func (c *Cache) OutputFile(out OutputID) string { file := c.fileName(out, "d") @@ -533,3 +551,15 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error { return nil } + +// FuzzDir returns a subdirectory within the cache for storing fuzzing data. +// The subdirectory may not exist. +// +// This directory is managed by the internal/fuzz package. Files in this +// directory aren't removed by the 'go clean -cache' command or by Trim. +// They may be removed with 'go clean -fuzzcache'. +// +// TODO(#48526): make Trim remove unused files from this directory. +func (c *Cache) FuzzDir() string { + return filepath.Join(c.dir, "fuzz") +} diff --git a/src/cmd/go/internal/cache/default.go b/src/cmd/go/internal/cache/default.go index 0b1c1e0c203511..426dddfb978624 100644 --- a/src/cmd/go/internal/cache/default.go +++ b/src/cmd/go/internal/cache/default.go @@ -30,6 +30,7 @@ var ( // README as a courtesy to explain where it came from. const cacheREADME = `This directory holds cached build artifacts from the Go build system. Run "go clean -cache" if the directory is getting too large. +Run "go clean -fuzzcache" to delete the fuzz cache. See golang.org to learn more about Go. ` diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index 57a3c1ff6fbdc1..2a1475ef2e7aea 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -22,10 +22,50 @@ import ( "cmd/go/internal/fsys" ) +// Global build parameters (used during package load) +var ( + Goos = envOr("GOOS", build.Default.GOOS) + Goarch = envOr("GOARCH", build.Default.GOARCH) + + ExeSuffix = exeSuffix() + + // ModulesEnabled specifies whether the go command is running + // in module-aware mode (as opposed to GOPATH mode). + // It is equal to modload.Enabled, but not all packages can import modload. + ModulesEnabled bool +) + +func exeSuffix() string { + if Goos == "windows" { + return ".exe" + } + return "" +} + +// Configuration for tools installed to GOROOT/bin. +// Normally these match runtime.GOOS and runtime.GOARCH, +// but when testing a cross-compiled cmd/go they will +// indicate the GOOS and GOARCH of the installed cmd/go +// rather than the test binary. +var ( + installedGOOS string + installedGOARCH string +) + +// ToolExeSuffix returns the suffix for executables installed +// in build.ToolDir. +func ToolExeSuffix() string { + if installedGOOS == "windows" { + return ".exe" + } + return "" +} + // These are general "build flags" used by build and other commands. var ( - BuildA bool // -a flag - BuildBuildmode string // -buildmode flag + BuildA bool // -a flag + BuildBuildmode string // -buildmode flag + BuildBuildvcs = "auto" // -buildvcs flag: "true", "false", or "auto" BuildContext = defaultContext() BuildMod string // -mod flag BuildModExplicit bool // whether -mod was set explicitly @@ -33,6 +73,7 @@ var ( BuildI bool // -i flag BuildLinkshared bool // -linkshared flag BuildMSan bool // -msan flag + BuildASan bool // -asan flag BuildN bool // -n flag BuildO string // -o flag BuildP = runtime.GOMAXPROCS(0) // -p flag @@ -54,36 +95,31 @@ var ( DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable) DebugTrace string // -debug-trace flag + + // GoPathError is set when GOPATH is not set. it contains an + // explanation why GOPATH is unset. + GoPathError string ) func defaultContext() build.Context { ctxt := build.Default - ctxt.JoinPath = filepath.Join // back door to say "do not use go command" - ctxt.GOROOT = findGOROOT() - if runtime.Compiler != "gccgo" { - // Note that we must use runtime.GOOS and runtime.GOARCH here, - // as the tool directory does not move based on environment - // variables. This matches the initialization of ToolDir in - // go/build, except for using ctxt.GOROOT rather than - // runtime.GOROOT. - build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH) - } - - ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH) + ctxt.JoinPath = filepath.Join // back door to say "do not use go command" // Override defaults computed in go/build with defaults // from go environment configuration file, if known. - ctxt.GOOS = envOr("GOOS", ctxt.GOOS) - ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH) - - // The experiments flags are based on GOARCH, so they may - // need to change. TODO: This should be cleaned up. - buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)) - ctxt.ToolTags = nil - for _, exp := range buildcfg.EnabledExperiments() { - ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp) + ctxt.GOPATH = envOr("GOPATH", gopath(ctxt)) + ctxt.GOOS = Goos + ctxt.GOARCH = Goarch + + // Clear the GOEXPERIMENT-based tool tags, which we will recompute later. + var save []string + for _, tag := range ctxt.ToolTags { + if !strings.HasPrefix(tag, "goexperiment.") { + save = append(save, tag) + } } + ctxt.ToolTags = save // The go/build rule for whether cgo is enabled is: // 1. If $CGO_ENABLED is set, respect it. @@ -124,10 +160,91 @@ func defaultContext() build.Context { } func init() { + SetGOROOT(findGOROOT(), false) BuildToolchainCompiler = func() string { return "missing-compiler" } BuildToolchainLinker = func() string { return "missing-linker" } } +// SetGOROOT sets GOROOT and associated variables to the given values. +// +// If isTestGo is true, build.ToolDir is set based on the TESTGO_GOHOSTOS and +// TESTGO_GOHOSTARCH environment variables instead of runtime.GOOS and +// runtime.GOARCH. +func SetGOROOT(goroot string, isTestGo bool) { + BuildContext.GOROOT = goroot + + GOROOT = goroot + if goroot == "" { + GOROOTbin = "" + GOROOTpkg = "" + GOROOTsrc = "" + } else { + GOROOTbin = filepath.Join(goroot, "bin") + GOROOTpkg = filepath.Join(goroot, "pkg") + GOROOTsrc = filepath.Join(goroot, "src") + } + GOROOT_FINAL = findGOROOT_FINAL(goroot) + + installedGOOS = runtime.GOOS + installedGOARCH = runtime.GOARCH + if isTestGo { + if testOS := os.Getenv("TESTGO_GOHOSTOS"); testOS != "" { + installedGOOS = testOS + } + if testArch := os.Getenv("TESTGO_GOHOSTARCH"); testArch != "" { + installedGOARCH = testArch + } + } + + if runtime.Compiler != "gccgo" { + if goroot == "" { + build.ToolDir = "" + } else { + // Note that we must use the installed OS and arch here: the tool + // directory does not move based on environment variables, and even if we + // are testing a cross-compiled cmd/go all of the installed packages and + // tools would have been built using the native compiler and linker (and + // would spuriously appear stale if we used a cross-compiled compiler and + // linker). + // + // This matches the initialization of ToolDir in go/build, except for + // using ctxt.GOROOT and the installed GOOS and GOARCH rather than the + // GOROOT, GOOS, and GOARCH reported by the runtime package. + build.ToolDir = filepath.Join(GOROOTpkg, "tool", installedGOOS+"_"+installedGOARCH) + } + } +} + +// Experiment configuration. +var ( + // RawGOEXPERIMENT is the GOEXPERIMENT value set by the user. + RawGOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT) + // CleanGOEXPERIMENT is the minimal GOEXPERIMENT value needed to reproduce the + // experiments enabled by RawGOEXPERIMENT. + CleanGOEXPERIMENT = RawGOEXPERIMENT + + Experiment *buildcfg.ExperimentFlags + ExperimentErr error +) + +func init() { + Experiment, ExperimentErr = buildcfg.ParseGOEXPERIMENT(Goos, Goarch, RawGOEXPERIMENT) + if ExperimentErr != nil { + return + } + + // GOEXPERIMENT is valid, so convert it to canonical form. + CleanGOEXPERIMENT = Experiment.String() + + // Add build tags based on the experiments in effect. + exps := Experiment.Enabled() + expTags := make([]string, 0, len(exps)+len(BuildContext.ToolTags)) + for _, exp := range exps { + expTags = append(expTags, "goexperiment."+exp) + } + BuildContext.ToolTags = append(expTags, BuildContext.ToolTags...) +} + // An EnvVar is an environment variable Name=Value. type EnvVar struct { Name string @@ -142,26 +259,6 @@ var OrigEnv []string // not CmdEnv. var CmdEnv []EnvVar -// Global build parameters (used during package load) -var ( - Goarch = BuildContext.GOARCH - Goos = BuildContext.GOOS - - ExeSuffix = exeSuffix() - - // ModulesEnabled specifies whether the go command is running - // in module-aware mode (as opposed to GOPATH mode). - // It is equal to modload.Enabled, but not all packages can import modload. - ModulesEnabled bool -) - -func exeSuffix() string { - if Goos == "windows" { - return ".exe" - } - return "" -} - var envCache struct { once sync.Once m map[string]string @@ -250,17 +347,18 @@ func CanGetenv(key string) bool { } var ( - GOROOT = BuildContext.GOROOT + GOROOT string + GOROOTbin string + GOROOTpkg string + GOROOTsrc string + GOROOT_FINAL string GOBIN = Getenv("GOBIN") - GOROOTbin = filepath.Join(GOROOT, "bin") - GOROOTpkg = filepath.Join(GOROOT, "pkg") - GOROOTsrc = filepath.Join(GOROOT, "src") - GOROOT_FINAL = findGOROOT_FINAL() GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod")) // Used in envcmd.MkEnv and build ID computations. GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM)) GO386 = envOr("GO386", buildcfg.GO386) + GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64)) GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS) GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64)) @@ -287,6 +385,8 @@ func GetArchEnv() (key, val string) { return "GOARM", GOARM case "386": return "GO386", GO386 + case "amd64": + return "GOAMD64", GOAMD64 case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": @@ -322,7 +422,10 @@ func findGOROOT() string { if env := Getenv("GOROOT"); env != "" { return filepath.Clean(env) } - def := filepath.Clean(runtime.GOROOT()) + def := "" + if r := runtime.GOROOT(); r != "" { + def = filepath.Clean(r) + } if runtime.Compiler == "gccgo" { // gccgo has no real GOROOT, and it certainly doesn't // depend on the executable's location. @@ -354,10 +457,10 @@ func findGOROOT() string { return def } -func findGOROOT_FINAL() string { +func findGOROOT_FINAL(goroot string) string { // $GOROOT_FINAL is only for use during make.bash // so it is not settable using go/env, so we use os.Getenv here. - def := GOROOT + def := goroot if env := os.Getenv("GOROOT_FINAL"); env != "" { def = filepath.Clean(env) } @@ -396,3 +499,24 @@ func gopathDir(rel string) string { } return filepath.Join(list[0], rel) } + +func gopath(ctxt build.Context) string { + if len(ctxt.GOPATH) > 0 { + return ctxt.GOPATH + } + env := "HOME" + if runtime.GOOS == "windows" { + env = "USERPROFILE" + } else if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + def := filepath.Join(home, "go") + if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) { + GoPathError = "cannot set GOROOT as GOPATH" + } + return "" + } + GoPathError = fmt.Sprintf("%s is not set", env) + return "" +} diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go index fd4cb205591105..2417cc077e8daa 100644 --- a/src/cmd/go/internal/clean/clean.go +++ b/src/cmd/go/internal/clean/clean.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 clean implements the ``go clean'' command. +// Package clean implements the “go clean” command. package clean import ( @@ -11,6 +11,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strconv" "strings" "time" @@ -22,6 +23,7 @@ import ( "cmd/go/internal/lockedfile" "cmd/go/internal/modfetch" "cmd/go/internal/modload" + "cmd/go/internal/str" "cmd/go/internal/work" ) @@ -75,6 +77,13 @@ The -modcache flag causes clean to remove the entire module download cache, including unpacked source code of versioned dependencies. +The -fuzzcache flag causes clean to remove files stored in the Go build +cache for fuzz testing. The fuzzing engine caches files that expand +code coverage, so removing them may make fuzzing less effective until +new inputs are found that provide the same coverage. These files are +distinct from those stored in testdata directory; clean does not remove +those files. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -85,6 +94,7 @@ var ( cleanI bool // clean -i flag cleanR bool // clean -r flag cleanCache bool // clean -cache flag + cleanFuzzcache bool // clean -fuzzcache flag cleanModcache bool // clean -modcache flag cleanTestcache bool // clean -testcache flag ) @@ -96,6 +106,7 @@ func init() { CmdClean.Flag.BoolVar(&cleanI, "i", false, "") CmdClean.Flag.BoolVar(&cleanR, "r", false, "") CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "") + CmdClean.Flag.BoolVar(&cleanFuzzcache, "fuzzcache", false, "") CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "") CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "") @@ -107,12 +118,29 @@ func init() { } func runClean(ctx context.Context, cmd *base.Command, args []string) { + if len(args) > 0 { + cacheFlag := "" + switch { + case cleanCache: + cacheFlag = "-cache" + case cleanTestcache: + cacheFlag = "-testcache" + case cleanFuzzcache: + cacheFlag = "-fuzzcache" + case cleanModcache: + cacheFlag = "-modcache" + } + if cacheFlag != "" { + base.Fatalf("go: clean %s cannot be used with package arguments", cacheFlag) + } + } + // golang.org/issue/29925: only load packages before cleaning if // either the flags and arguments explicitly imply a package, // or no other target (such as a cache) was requested to be cleaned. cleanPkg := len(args) > 0 || cleanI || cleanR if (!modload.Enabled() || modload.HasModRoot()) && - !cleanCache && !cleanModcache && !cleanTestcache { + !cleanCache && !cleanModcache && !cleanTestcache && !cleanFuzzcache { cleanPkg = true } @@ -132,7 +160,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // The top cache directory may have been created with special permissions // and not something that we want to remove. Also, we'd like to preserve // the access log for future analysis, even if the cache is cleared. - subdirs, _ := filepath.Glob(filepath.Join(dir, "[0-9a-f][0-9a-f]")) + subdirs, _ := filepath.Glob(filepath.Join(str.QuoteGlob(dir), "[0-9a-f][0-9a-f]")) printedErrors := false if len(subdirs) > 0 { if cfg.BuildN || cfg.BuildX { @@ -144,7 +172,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // This also mimics what os.RemoveAll(dir) would do. if err := os.RemoveAll(d); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -157,7 +185,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildN { if err := os.RemoveAll(logFile); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -187,7 +215,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { } if err != nil { if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) { - base.Errorf("go clean -testcache: %v", err) + base.Errorf("go: %v", err) } } } @@ -195,14 +223,26 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if cleanModcache { if cfg.GOMODCACHE == "" { - base.Fatalf("go clean -modcache: no module cache") + base.Fatalf("go: cannot clean -modcache without a module cache") } if cfg.BuildN || cfg.BuildX { b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE) } if !cfg.BuildN { if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil { - base.Errorf("go clean -modcache: %v", err) + base.Errorf("go: %v", err) + } + } + } + + if cleanFuzzcache { + fuzzDir := cache.Default().FuzzDir() + if cfg.BuildN || cfg.BuildX { + b.Showcmd("", "rm -rf %s", fuzzDir) + } + if !cfg.BuildN { + if err := os.RemoveAll(fuzzDir); err != nil { + base.Errorf("go: %v", err) } } } @@ -245,7 +285,7 @@ func clean(p *load.Package) { } dirs, err := os.ReadDir(p.Dir) if err != nil { - base.Errorf("go clean %s: %v", p.Dir, err) + base.Errorf("go: %s: %v", p.Dir, err) return } @@ -334,7 +374,7 @@ func clean(p *load.Package) { } } if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } } continue @@ -373,7 +413,7 @@ func removeFile(f string) { return } // Windows does not allow deletion of a binary file while it is executing. - if base.ToolIsWindows { + if runtime.GOOS == "windows" { // Remove lingering ~ file from last attempt. if _, err2 := os.Stat(f + "~"); err2 == nil { os.Remove(f + "~") @@ -386,5 +426,5 @@ func removeFile(f string) { return } } - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } diff --git a/src/cmd/go/internal/cmdflag/flag.go b/src/cmd/go/internal/cmdflag/flag.go index 8abb7e559f5ab5..a634bc1ab8d7ea 100644 --- a/src/cmd/go/internal/cmdflag/flag.go +++ b/src/cmd/go/internal/cmdflag/flag.go @@ -92,7 +92,7 @@ func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []st // Use fs.Set instead of f.Value.Set below so that any subsequent call to // fs.Visit will correctly visit the flags that have been set. - failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) { + failf := func(format string, a ...any) (*flag.Flag, []string, error) { return f, args, fmt.Errorf(format, a...) } diff --git a/src/cmd/go/internal/doc/doc.go b/src/cmd/go/internal/doc/doc.go index 8580a5dc4d2482..3b6cd94799ada3 100644 --- a/src/cmd/go/internal/doc/doc.go +++ b/src/cmd/go/internal/doc/doc.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 doc implements the ``go doc'' command. +// Package doc implements the “go doc” command. package doc import ( @@ -60,9 +60,8 @@ The package path must be either a qualified path or a proper suffix of a path. The go tool's usual package mechanism does not apply: package path elements like . and ... are not implemented by go doc. -When run with two arguments, the first must be a full package path (not just a -suffix), and the second is a symbol, or symbol with method or struct field. -This is similar to the syntax accepted by godoc: +When run with two arguments, the first is a package path (full path or suffix), +and the second is a symbol, or symbol with method or struct field: go doc [.] diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 1553d263914541..6dd8657bfc61b3 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.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 envcmd implements the ``go env'' command. +// Package envcmd implements the “go env” command. package envcmd import ( @@ -26,6 +26,7 @@ import ( "cmd/go/internal/load" "cmd/go/internal/modload" "cmd/go/internal/work" + "cmd/internal/quoted" ) var CmdEnv = &base.Command{ @@ -73,7 +74,14 @@ func MkEnv() []cfg.EnvVar { {Name: "GOCACHE", Value: cache.DefaultDir()}, {Name: "GOENV", Value: envFile}, {Name: "GOEXE", Value: cfg.ExeSuffix}, - {Name: "GOEXPERIMENT", Value: buildcfg.GOEXPERIMENT()}, + + // List the raw value of GOEXPERIMENT, not the cleaned one. + // The set of default experiments may change from one release + // to the next, so a GOEXPERIMENT setting that is redundant + // with the current toolchain might actually be relevant with + // a different version (for example, when bisecting a regression). + {Name: "GOEXPERIMENT", Value: cfg.RawGOEXPERIMENT}, + {Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")}, {Name: "GOHOSTARCH", Value: runtime.GOARCH}, {Name: "GOHOSTOS", Value: runtime.GOOS}, @@ -88,7 +96,7 @@ func MkEnv() []cfg.EnvVar { {Name: "GOROOT", Value: cfg.GOROOT}, {Name: "GOSUMDB", Value: cfg.GOSUMDB}, {Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")}, - {Name: "GOTOOLDIR", Value: base.ToolDir}, + {Name: "GOTOOLDIR", Value: build.ToolDir}, {Name: "GOVCS", Value: cfg.GOVCS}, {Name: "GOVERSION", Value: runtime.Version()}, } @@ -104,13 +112,13 @@ func MkEnv() []cfg.EnvVar { env = append(env, cfg.EnvVar{Name: key, Value: val}) } - cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch) - if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 { - cc = env[0] + cc := cfg.Getenv("CC") + if cc == "" { + cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch) } - cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch) - if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 { - cxx = env[0] + cxx := cfg.Getenv("CXX") + if cxx == "" { + cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch) } env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")}) env = append(env, cfg.EnvVar{Name: "CC", Value: cc}) @@ -145,21 +153,34 @@ func findEnv(env []cfg.EnvVar, name string) string { // ExtraEnvVars returns environment variables that should not leak into child processes. func ExtraEnvVars() []cfg.EnvVar { gomod := "" + modload.Init() if modload.HasModRoot() { - gomod = filepath.Join(modload.ModRoot(), "go.mod") + gomod = modload.ModFilePath() } else if modload.Enabled() { gomod = os.DevNull } + modload.InitWorkfile() + gowork := modload.WorkFilePath() + // As a special case, if a user set off explicitly, report that in GOWORK. + if cfg.Getenv("GOWORK") == "off" { + gowork = "off" + } return []cfg.EnvVar{ {Name: "GOMOD", Value: gomod}, + {Name: "GOWORK", Value: gowork}, } } // ExtraEnvVarsCostly returns environment variables that should not leak into child processes // but are costly to evaluate. func ExtraEnvVarsCostly() []cfg.EnvVar { - var b work.Builder - b.Init() + b := work.NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() + cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{}) if err != nil { // Should not happen - b.CFlags was given an empty package. @@ -168,15 +189,23 @@ func ExtraEnvVarsCostly() []cfg.EnvVar { } cmd := b.GccCmd(".", "") + join := func(s []string) string { + q, err := quoted.Join(s) + if err != nil { + return strings.Join(s, " ") + } + return q + } + return []cfg.EnvVar{ // Note: Update the switch in runEnv below when adding to this list. - {Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")}, - {Name: "CGO_CPPFLAGS", Value: strings.Join(cppflags, " ")}, - {Name: "CGO_CXXFLAGS", Value: strings.Join(cxxflags, " ")}, - {Name: "CGO_FFLAGS", Value: strings.Join(fflags, " ")}, - {Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")}, + {Name: "CGO_CFLAGS", Value: join(cflags)}, + {Name: "CGO_CPPFLAGS", Value: join(cppflags)}, + {Name: "CGO_CXXFLAGS", Value: join(cxxflags)}, + {Name: "CGO_FFLAGS", Value: join(fflags)}, + {Name: "CGO_LDFLAGS", Value: join(ldflags)}, {Name: "PKG_CONFIG", Value: b.PkgconfigCmd()}, - {Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")}, + {Name: "GOGCCFLAGS", Value: join(cmd[3:])}, } } @@ -191,13 +220,13 @@ func argKey(arg string) string { func runEnv(ctx context.Context, cmd *base.Command, args []string) { if *envJson && *envU { - base.Fatalf("go env: cannot use -json with -u") + base.Fatalf("go: cannot use -json with -u") } if *envJson && *envW { - base.Fatalf("go env: cannot use -json with -w") + base.Fatalf("go: cannot use -json with -w") } if *envU && *envW { - base.Fatalf("go env: cannot use -u with -w") + base.Fatalf("go: cannot use -u with -w") } // Handle 'go env -w' and 'go env -u' before calling buildcfg.Check, @@ -213,6 +242,9 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { } buildcfg.Check() + if cfg.ExperimentErr != nil { + base.Fatalf("go: %v", cfg.ExperimentErr) + } env := cfg.CmdEnv env = append(env, ExtraEnvVars()...) @@ -245,6 +277,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { } } if needCostly { + work.BuildInit() env = append(env, ExtraEnvVarsCostly()...) } @@ -275,7 +308,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { func runEnvW(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -w: no KEY=VALUE arguments given") + base.Fatalf("go: no KEY=VALUE arguments given") } osEnv := make(map[string]string) for _, e := range cfg.OrigEnv { @@ -287,14 +320,14 @@ func runEnvW(args []string) { for _, arg := range args { i := strings.Index(arg, "=") if i < 0 { - base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg) + base.Fatalf("go: arguments must be KEY=VALUE: invalid argument: %s", arg) } key, val := arg[:i], arg[i+1:] if err := checkEnvWrite(key, val); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } if _, ok := add[key]; ok { - base.Fatalf("go env -w: multiple values for key: %s", key) + base.Fatalf("go: multiple values for key: %s", key) } add[key] = val if osVal := osEnv[key]; osVal != "" && osVal != val { @@ -303,13 +336,13 @@ func runEnvW(args []string) { } if err := checkBuildConfig(add, nil); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } gotmp, okGOTMP := add["GOTMPDIR"] if okGOTMP { if !filepath.IsAbs(gotmp) && gotmp != "" { - base.Fatalf("go env -w: GOTMPDIR must be an absolute path") + base.Fatalf("go: GOTMPDIR must be an absolute path") } } @@ -319,18 +352,18 @@ func runEnvW(args []string) { func runEnvU(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -u: no arguments given") + base.Fatalf("go: 'go env -u' requires an argument") } del := make(map[string]bool) for _, arg := range args { if err := checkEnvWrite(arg, ""); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } del[arg] = true } if err := checkBuildConfig(nil, del); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } updateEnvFile(nil, del) @@ -365,9 +398,9 @@ func checkBuildConfig(add map[string]string, del map[string]bool) error { } } - goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", buildcfg.GOEXPERIMENT(), "") + goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", cfg.RawGOEXPERIMENT, buildcfg.DefaultGOEXPERIMENT) if okGOEXPERIMENT { - if _, _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil { + if _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil { return err } } @@ -414,7 +447,7 @@ func printEnvAsJSON(env []cfg.EnvVar) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", "\t") if err := enc.Encode(m); err != nil { - base.Fatalf("go env -json: %s", err) + base.Fatalf("go: %s", err) } } @@ -429,7 +462,7 @@ func getOrigEnv(key string) string { func checkEnvWrite(key, val string) error { switch key { - case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR", "GOVERSION": + case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOWORK", "GOTOOLDIR", "GOVERSION": return fmt.Errorf("%s cannot be modified", key) case "GOENV": return fmt.Errorf("%s can only be set using the OS environment", key) @@ -457,10 +490,23 @@ func checkEnvWrite(key, val string) error { if !filepath.IsAbs(val) && val != "" { return fmt.Errorf("GOPATH entry is relative; must be absolute path: %q", val) } - // Make sure CC and CXX are absolute paths - case "CC", "CXX", "GOMODCACHE": - if !filepath.IsAbs(val) && val != "" && val != filepath.Base(val) { - return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, val) + case "GOMODCACHE": + if !filepath.IsAbs(val) && val != "" { + return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q", val) + } + case "CC", "CXX": + if val == "" { + break + } + args, err := quoted.Split(val) + if err != nil { + return fmt.Errorf("invalid %s: %v", key, err) + } + if len(args) == 0 { + return fmt.Errorf("%s entry cannot contain only space", key) + } + if !filepath.IsAbs(args[0]) && args[0] != filepath.Base(args[0]) { + return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, args[0]) } } @@ -479,11 +525,11 @@ func checkEnvWrite(key, val string) error { func updateEnvFile(add map[string]string, del map[string]bool) { file, err := cfg.EnvFile() if file == "" { - base.Fatalf("go env: cannot find go env config: %v", err) + base.Fatalf("go: cannot find go env config: %v", err) } data, err := os.ReadFile(file) if err != nil && (!os.IsNotExist(err) || len(add) == 0) { - base.Fatalf("go env: reading go env config: %v", err) + base.Fatalf("go: reading go env config: %v", err) } lines := strings.SplitAfter(string(data), "\n") @@ -541,7 +587,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) { os.MkdirAll(filepath.Dir(file), 0777) err = os.WriteFile(file, data, 0666) if err != nil { - base.Fatalf("go env: writing go env config: %v", err) + base.Fatalf("go: writing go env config: %v", err) } } } diff --git a/src/cmd/go/internal/fix/fix.go b/src/cmd/go/internal/fix/fix.go index 988d45e71ccfe2..3705b30ef9533c 100644 --- a/src/cmd/go/internal/fix/fix.go +++ b/src/cmd/go/internal/fix/fix.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 fix implements the ``go fix'' command. +// Package fix implements the “go fix” command. package fix import ( @@ -11,27 +11,39 @@ import ( "cmd/go/internal/load" "cmd/go/internal/modload" "cmd/go/internal/str" + "cmd/go/internal/work" "context" "fmt" + "go/build" "os" ) var CmdFix = &base.Command{ - Run: runFix, - UsageLine: "go fix [packages]", + UsageLine: "go fix [-fix list] [packages]", Short: "update packages to use new APIs", Long: ` Fix runs the Go fix command on the packages named by the import paths. +The -fix flag sets a comma-separated list of fixes to run. +The default is all known fixes. +(Its value is passed to 'go tool fix -r'.) + For more about fix, see 'go doc cmd/fix'. For more about specifying packages, see 'go help packages'. -To run fix with specific options, run 'go tool fix'. +To run fix with other options, run 'go tool fix'. See also: go fmt, go vet. `, } +var fixes = CmdFix.Flag.String("fix", "", "comma-separated list of fixes to apply") + +func init() { + work.AddBuildFlags(CmdFix, work.DefaultBuildFlags) + CmdFix.Run = runFix // fix cycle +} + func runFix(ctx context.Context, cmd *base.Command, args []string) { pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) w := 0 @@ -58,6 +70,16 @@ func runFix(ctx context.Context, cmd *base.Command, args []string) { // the command only applies to this package, // not to packages in subdirectories. files := base.RelPaths(pkg.InternalAllGoFiles()) - base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), files)) + goVersion := "" + if pkg.Module != nil { + goVersion = "go" + pkg.Module.GoVersion + } else if pkg.Standard { + goVersion = build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1] + } + var fixArg []string + if *fixes != "" { + fixArg = []string{"-r=" + *fixes} + } + base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), "-go="+goVersion, fixArg, files)) } } diff --git a/src/cmd/go/internal/fmtcmd/fmt.go b/src/cmd/go/internal/fmtcmd/fmt.go index 8a040087539e8f..f6a8d207cdd160 100644 --- a/src/cmd/go/internal/fmtcmd/fmt.go +++ b/src/cmd/go/internal/fmtcmd/fmt.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 fmtcmd implements the ``go fmt'' command. +// Package fmtcmd implements the “go fmt” command. package fmtcmd import ( @@ -11,14 +11,12 @@ import ( "fmt" "os" "path/filepath" - "runtime" - "sync" "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" "cmd/go/internal/modload" - "cmd/go/internal/str" + "cmd/internal/sys" ) func init() { @@ -53,18 +51,13 @@ See also: go fix, go vet. func runFmt(ctx context.Context, cmd *base.Command, args []string) { printed := false gofmt := gofmtPath() - procs := runtime.GOMAXPROCS(0) - var wg sync.WaitGroup - wg.Add(procs) - fileC := make(chan string, 2*procs) - for i := 0; i < procs; i++ { - go func() { - defer wg.Done() - for file := range fileC { - base.Run(str.StringList(gofmt, "-l", "-w", file)) - } - }() - } + + gofmtArgs := []string{gofmt, "-l", "-w"} + gofmtArgLen := len(gofmt) + len(" -l -w") + + baseGofmtArgs := len(gofmtArgs) + baseGofmtArgLen := gofmtArgLen + for _, pkg := range load.PackagesAndErrors(ctx, load.PackageOpts{}, args) { if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main { if !printed { @@ -89,18 +82,22 @@ func runFmt(ctx context.Context, cmd *base.Command, args []string) { // not to packages in subdirectories. files := base.RelPaths(pkg.InternalAllGoFiles()) for _, file := range files { - fileC <- file + gofmtArgs = append(gofmtArgs, file) + gofmtArgLen += 1 + len(file) // plus separator + if gofmtArgLen >= sys.ExecArgLengthLimit { + base.Run(gofmtArgs) + gofmtArgs = gofmtArgs[:baseGofmtArgs] + gofmtArgLen = baseGofmtArgLen + } } } - close(fileC) - wg.Wait() + if len(gofmtArgs) > baseGofmtArgs { + base.Run(gofmtArgs) + } } func gofmtPath() string { - gofmt := "gofmt" - if base.ToolIsWindows { - gofmt += base.ToolWindowsExtension - } + gofmt := "gofmt" + cfg.ToolExeSuffix() gofmtPath := filepath.Join(cfg.GOBIN, gofmt) if _, err := os.Stat(gofmtPath); err == nil { diff --git a/src/cmd/go/internal/fsys/fsys.go b/src/cmd/go/internal/fsys/fsys.go index 0b806027e6469a..0d7bef911266b0 100644 --- a/src/cmd/go/internal/fsys/fsys.go +++ b/src/cmd/go/internal/fsys/fsys.go @@ -6,16 +6,65 @@ import ( "encoding/json" "errors" "fmt" + "internal/godebug" "io/fs" "io/ioutil" + "log" "os" + pathpkg "path" "path/filepath" "runtime" + "runtime/debug" "sort" "strings" + "sync" "time" ) +// Trace emits a trace event for the operation and file path to the trace log, +// but only when $GODEBUG contains gofsystrace=1. +// The traces are appended to the file named by the $GODEBUG setting gofsystracelog, or else standard error. +// For debugging, if the $GODEBUG setting gofsystracestack is non-empty, then trace events for paths +// matching that glob pattern (using path.Match) will be followed by a full stack trace. +func Trace(op, path string) { + if !doTrace { + return + } + traceMu.Lock() + defer traceMu.Unlock() + fmt.Fprintf(traceFile, "%d gofsystrace %s %s\n", os.Getpid(), op, path) + if traceStack != "" { + if match, _ := pathpkg.Match(traceStack, path); match { + traceFile.Write(debug.Stack()) + } + } +} + +var ( + doTrace bool + traceStack string + traceFile *os.File + traceMu sync.Mutex +) + +func init() { + if godebug.Get("gofsystrace") != "1" { + return + } + doTrace = true + traceStack = godebug.Get("gofsystracestack") + if f := godebug.Get("gofsystracelog"); f != "" { + // Note: No buffering on writes to this file, so no need to worry about closing it at exit. + var err error + traceFile, err = os.OpenFile(f, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + if err != nil { + log.Fatal(err) + } + } else { + traceFile = os.Stderr + } +} + // OverlayFile is the path to a text file in the OverlayJSON format. // It is the value of the -overlay flag. var OverlayFile string @@ -86,6 +135,7 @@ func Init(wd string) error { return nil } + Trace("ReadFile", OverlayFile) b, err := os.ReadFile(OverlayFile) if err != nil { return fmt.Errorf("reading overlay file: %v", err) @@ -191,6 +241,7 @@ func initFromJSON(overlayJSON OverlayJSON) error { // IsDir returns true if path is a directory on disk or in the // overlay. func IsDir(path string) (bool, error) { + Trace("IsDir", path) path = canonicalize(path) if _, ok := parentIsOverlayFile(path); ok { @@ -260,6 +311,7 @@ func readDir(dir string) ([]fs.FileInfo, error) { // ReadDir provides a slice of fs.FileInfo entries corresponding // to the overlaid files in the directory. func ReadDir(dir string) ([]fs.FileInfo, error) { + Trace("ReadDir", dir) dir = canonicalize(dir) if _, ok := parentIsOverlayFile(dir); ok { return nil, &fs.PathError{Op: "ReadDir", Path: dir, Err: errNotDir} @@ -295,7 +347,7 @@ func ReadDir(dir string) ([]fs.FileInfo, error) { files[name] = missingFile(name) continue } else if f.IsDir() { - return nil, fmt.Errorf("for overlay of %q to %q: overlay Replace entries can't point to dirctories", + return nil, fmt.Errorf("for overlay of %q to %q: overlay Replace entries can't point to directories", filepath.Join(dir, name), to.actualFilePath) } // Add a fileinfo for the overlaid file, so that it has @@ -327,11 +379,17 @@ func OverlayPath(path string) (string, bool) { // Open opens the file at or overlaid on the given path. func Open(path string) (*os.File, error) { - return OpenFile(path, os.O_RDONLY, 0) + Trace("Open", path) + return openFile(path, os.O_RDONLY, 0) } // OpenFile opens the file at or overlaid on the given path with the flag and perm. func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { + Trace("OpenFile", path) + return openFile(path, flag, perm) +} + +func openFile(path string, flag int, perm os.FileMode) (*os.File, error) { cpath := canonicalize(path) if node, ok := overlay[cpath]; ok { // Opening a file in the overlay. @@ -360,6 +418,7 @@ func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { // IsDirWithGoFiles reports whether dir is a directory containing Go files // either on disk or in the overlay. func IsDirWithGoFiles(dir string) (bool, error) { + Trace("IsDirWithGoFiles", dir) fis, err := ReadDir(dir) if os.IsNotExist(err) || errors.Is(err, errNotDir) { return false, nil @@ -405,28 +464,20 @@ func IsDirWithGoFiles(dir string) (bool, error) { // walk recursively descends path, calling walkFn. Copied, with some // modifications from path/filepath.walk. func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error { - if !info.IsDir() { - return walkFn(path, info, nil) + if err := walkFn(path, info, nil); err != nil || !info.IsDir() { + return err } - fis, readErr := ReadDir(path) - walkErr := walkFn(path, info, readErr) - // If readErr != nil, walk can't walk into this directory. - // walkErr != nil means walkFn want walk to skip this directory or stop walking. - // Therefore, if one of readErr and walkErr isn't nil, walk will return. - if readErr != nil || walkErr != nil { - // The caller's behavior is controlled by the return value, which is decided - // by walkFn. walkFn may ignore readErr and return nil. - // If walkFn returns SkipDir, it will be handled by the caller. - // So walk should return whatever walkFn returns. - return walkErr + fis, err := ReadDir(path) + if err != nil { + return walkFn(path, info, err) } for _, fi := range fis { filename := filepath.Join(path, fi.Name()) - if walkErr = walk(filename, fi, walkFn); walkErr != nil { - if !fi.IsDir() || walkErr != filepath.SkipDir { - return walkErr + if err := walk(filename, fi, walkFn); err != nil { + if !fi.IsDir() || err != filepath.SkipDir { + return err } } } @@ -436,6 +487,7 @@ func walk(path string, info fs.FileInfo, walkFn filepath.WalkFunc) error { // Walk walks the file tree rooted at root, calling walkFn for each file or // directory in the tree, including root. func Walk(root string, walkFn filepath.WalkFunc) error { + Trace("Walk", root) info, err := Lstat(root) if err != nil { err = walkFn(root, nil, err) @@ -450,11 +502,13 @@ func Walk(root string, walkFn filepath.WalkFunc) error { // lstat implements a version of os.Lstat that operates on the overlay filesystem. func Lstat(path string) (fs.FileInfo, error) { + Trace("Lstat", path) return overlayStat(path, os.Lstat, "lstat") } // Stat implements a version of os.Stat that operates on the overlay filesystem. func Stat(path string) (fs.FileInfo, error) { + Trace("Stat", path) return overlayStat(path, os.Stat, "stat") } @@ -499,7 +553,7 @@ func (f fakeFile) Size() int64 { return f.real.Size() } func (f fakeFile) Mode() fs.FileMode { return f.real.Mode() } func (f fakeFile) ModTime() time.Time { return f.real.ModTime() } func (f fakeFile) IsDir() bool { return f.real.IsDir() } -func (f fakeFile) Sys() interface{} { return f.real.Sys() } +func (f fakeFile) Sys() any { return f.real.Sys() } // missingFile provides an fs.FileInfo for an overlaid file where the // destination file in the overlay doesn't exist. It returns zero values @@ -512,7 +566,7 @@ func (f missingFile) Size() int64 { return 0 } func (f missingFile) Mode() fs.FileMode { return fs.ModeIrregular } func (f missingFile) ModTime() time.Time { return time.Unix(0, 0) } func (f missingFile) IsDir() bool { return false } -func (f missingFile) Sys() interface{} { return nil } +func (f missingFile) Sys() any { return nil } // fakeDir provides an fs.FileInfo implementation for directories that are // implicitly created by overlaid files. Each directory in the @@ -524,10 +578,11 @@ func (f fakeDir) Size() int64 { return 0 } func (f fakeDir) Mode() fs.FileMode { return fs.ModeDir | 0500 } func (f fakeDir) ModTime() time.Time { return time.Unix(0, 0) } func (f fakeDir) IsDir() bool { return true } -func (f fakeDir) Sys() interface{} { return nil } +func (f fakeDir) Sys() any { return nil } // Glob is like filepath.Glob but uses the overlay file system. func Glob(pattern string) (matches []string, err error) { + Trace("Glob", pattern) // Check pattern is well-formed. if _, err := filepath.Match(pattern, ""); err != nil { return nil, err diff --git a/src/cmd/go/internal/fsys/fsys_test.go b/src/cmd/go/internal/fsys/fsys_test.go index 7f175c7031169d..41da4f4b02a1ad 100644 --- a/src/cmd/go/internal/fsys/fsys_test.go +++ b/src/cmd/go/internal/fsys/fsys_test.go @@ -1,11 +1,11 @@ package fsys import ( - "cmd/go/internal/txtar" "encoding/json" "errors" "fmt" "internal/testenv" + "internal/txtar" "io" "io/fs" "os" @@ -760,6 +760,42 @@ func TestWalkSkipDir(t *testing.T) { } } +func TestWalkSkipAll(t *testing.T) { + initOverlay(t, ` +{ + "Replace": { + "dir/subdir1/foo1": "dummy.txt", + "dir/subdir1/foo2": "dummy.txt", + "dir/subdir1/foo3": "dummy.txt", + "dir/subdir2/foo4": "dummy.txt", + "dir/zzlast": "dummy.txt" + } +} +-- dummy.txt -- +`) + + var seen []string + Walk("dir", func(path string, info fs.FileInfo, err error) error { + seen = append(seen, filepath.ToSlash(path)) + if info.Name() == "foo2" { + return filepath.SkipAll + } + return nil + }) + + wantSeen := []string{"dir", "dir/subdir1", "dir/subdir1/foo1", "dir/subdir1/foo2"} + + if len(seen) != len(wantSeen) { + t.Errorf("paths seen in walk: got %v entries; want %v entries", len(seen), len(wantSeen)) + } + + for i := 0; i < len(seen) && i < len(wantSeen); i++ { + if seen[i] != wantSeen[i] { + t.Errorf("path %#v seen walking tree: got %q, want %q", i, seen[i], wantSeen[i]) + } + } +} + func TestWalkError(t *testing.T) { initOverlay(t, "{}") diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go index 80ea32b4284011..3eda6c71453f16 100644 --- a/src/cmd/go/internal/generate/generate.go +++ b/src/cmd/go/internal/generate/generate.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 generate implements the ``go generate'' command. +// Package generate implements the “go generate” command. package generate import ( @@ -12,10 +12,10 @@ import ( "fmt" "go/parser" "go/token" - exec "internal/execabs" "io" "log" "os" + "os/exec" "path/filepath" "regexp" "strconv" @@ -38,7 +38,7 @@ Generate runs commands described by directives within existing files. Those commands can run any process but the intent is to create or update Go source files. -Go generate is never run automatically by go build, go get, go test, +Go generate is never run automatically by go build, go test, and so on. It must be run explicitly. Go generate scans the file for directives, which are lines of @@ -84,6 +84,9 @@ Go generate sets several variables when it runs the generator: The line number of the directive in the source file. $GOPACKAGE The name of the package of the file containing the directive. + $GOROOT + The GOROOT directory for the 'go' command that invoked the + generator, containing the Go toolchain and standard library. $DOLLAR A dollar sign. @@ -130,7 +133,7 @@ all further processing for that package. The generator is run in the package's source directory. -Go generate accepts one specific flag: +Go generate accepts two specific flags: -run="" if non-empty, specifies a regular expression to select @@ -138,6 +141,13 @@ Go generate accepts one specific flag: any trailing spaces and final newline) matches the expression. + -skip="" + if non-empty, specifies a regular expression to suppress + directives whose full original source text (excluding + any trailing spaces and final newline) matches the + expression. If a directive matches both the -run and + the -skip arguments, it is skipped. + It also accepts the standard build flags including -v, -n, and -x. The -v flag prints the names of packages and files as they are processed. @@ -153,11 +163,15 @@ For more about specifying packages, see 'go help packages'. var ( generateRunFlag string // generate -run flag generateRunRE *regexp.Regexp // compiled expression for -run + + generateSkipFlag string // generate -skip flag + generateSkipRE *regexp.Regexp // compiled expression for -skip ) func init() { work.AddBuildFlags(CmdGenerate, work.DefaultBuildFlags) CmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "") + CmdGenerate.Flag.StringVar(&generateSkipFlag, "skip", "", "") } func runGenerate(ctx context.Context, cmd *base.Command, args []string) { @@ -168,6 +182,13 @@ func runGenerate(ctx context.Context, cmd *base.Command, args []string) { log.Fatalf("generate: %s", err) } } + if generateSkipFlag != "" { + var err error + generateSkipRE, err = regexp.Compile(generateSkipFlag) + if err != nil { + log.Fatalf("generate: %s", err) + } + } cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, "generate") @@ -288,10 +309,11 @@ func (g *Generator) run() (ok bool) { if !isGoGenerate(buf) { continue } - if generateRunFlag != "" { - if !generateRunRE.Match(bytes.TrimSpace(buf)) { - continue - } + if generateRunFlag != "" && !generateRunRE.Match(bytes.TrimSpace(buf)) { + continue + } + if generateSkipFlag != "" && generateSkipRE.Match(bytes.TrimSpace(buf)) { + continue } g.setEnv() @@ -325,7 +347,8 @@ func isGoGenerate(buf []byte) bool { // setEnv sets the extra environment variables used when executing a // single go:generate command. func (g *Generator) setEnv() { - g.env = []string{ + env := []string{ + "GOROOT=" + cfg.GOROOT, "GOARCH=" + cfg.BuildContext.GOARCH, "GOOS=" + cfg.BuildContext.GOOS, "GOFILE=" + g.file, @@ -333,7 +356,9 @@ func (g *Generator) setEnv() { "GOPACKAGE=" + g.pkg, "DOLLAR=" + "$", } - g.env = base.AppendPWD(g.env, g.dir) + env = base.AppendPATH(env) + env = base.AppendPWD(env, g.dir) + g.env = env } // split breaks the line into words, evaluating quoted @@ -408,7 +433,7 @@ var stop = fmt.Errorf("error in generation") // errorf logs an error message prefixed with the file and line number. // It then exits the program (with exit status 1) because generation stops // at the first error. -func (g *Generator) errorf(format string, args ...interface{}) { +func (g *Generator) errorf(format string, args ...any) { fmt.Fprintf(os.Stderr, "%s:%d: %s\n", base.ShortPath(g.path), g.lineNum, fmt.Sprintf(format, args...)) panic(stop) @@ -442,7 +467,20 @@ func (g *Generator) setShorthand(words []string) { // exec runs the command specified by the argument. The first word is // the command name itself. func (g *Generator) exec(words []string) { - cmd := exec.Command(words[0], words[1:]...) + path := words[0] + if path != "" && !strings.Contains(path, string(os.PathSeparator)) { + // If a generator says '//go:generate go run ' it almost certainly + // intends to use the same 'go' as 'go generate' itself. + // Prefer to resolve the binary from GOROOT/bin, and for consistency + // prefer to resolve any other commands there too. + gorootBinPath, err := exec.LookPath(filepath.Join(cfg.GOROOTbin, path)) + if err == nil { + path = gorootBinPath + } + } + cmd := exec.Command(path, words[1:]...) + cmd.Args[0] = words[0] // Overwrite with the original in case it was rewritten above. + // Standard in and out of generator should be the usual. cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/src/cmd/go/internal/generate/generate_test.go b/src/cmd/go/internal/generate/generate_test.go index b546218a3c589f..d61ecf104a1ec5 100644 --- a/src/cmd/go/internal/generate/generate_test.go +++ b/src/cmd/go/internal/generate/generate_test.go @@ -5,7 +5,9 @@ package generate import ( + "internal/testenv" "os" + "path/filepath" "reflect" "runtime" "testing" @@ -41,10 +43,11 @@ var splitTests = []splitTest{ } func TestGenerateCommandParse(t *testing.T) { + dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. - path: "/usr/ken/sys/proc.go", - dir: "/usr/ken/sys", + path: filepath.Join(dir, "proc.go"), + dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), @@ -78,16 +81,17 @@ var defEnvMap = map[string]string{ // TestGenerateCommandShortHand - similar to TestGenerateCommandParse, // except: -// 1. if the result starts with -command, record that shorthand -// before moving on to the next test. -// 2. If a source line number is specified, set that in the parser -// before executing the test. i.e., execute the split as if it -// processing that source line. +// 1. if the result starts with -command, record that shorthand +// before moving on to the next test. +// 2. If a source line number is specified, set that in the parser +// before executing the test. i.e., execute the split as if it +// processing that source line. func TestGenerateCommandShorthand(t *testing.T) { + dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. - path: "/usr/ken/sys/proc.go", - dir: "/usr/ken/sys", + path: filepath.Join(dir, "proc.go"), + dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), @@ -216,16 +220,17 @@ var splitTestsLines = []splitTestWithLine{ // TestGenerateCommandShortHand - similar to TestGenerateCommandParse, // except: -// 1. if the result starts with -command, record that shorthand -// before moving on to the next test. -// 2. If a source line number is specified, set that in the parser -// before executing the test. i.e., execute the split as if it -// processing that source line. +// 1. if the result starts with -command, record that shorthand +// before moving on to the next test. +// 2. If a source line number is specified, set that in the parser +// before executing the test. i.e., execute the split as if it +// processing that source line. func TestGenerateCommandShortHand2(t *testing.T) { + dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. - path: "/usr/ken/sys/proc.go", - dir: "/usr/ken/sys", + path: filepath.Join(dir, "proc.go"), + dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go index 3c16dc3040facb..586427ff33d698 100644 --- a/src/cmd/go/internal/get/get.go +++ b/src/cmd/go/internal/get/get.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 get implements the ``go get'' command. +// Package get implements the “go get” command. package get import ( @@ -114,16 +114,16 @@ func init() { func runGet(ctx context.Context, cmd *base.Command, args []string) { if cfg.ModulesEnabled { // Should not happen: main.go should install the separate module-enabled get code. - base.Fatalf("go get: modules not implemented") + base.Fatalf("go: modules not implemented") } work.BuildInit() if *getF && !*getU { - base.Fatalf("go get: cannot use -f flag without -u") + base.Fatalf("go: cannot use -f flag without -u") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } // Disable any prompting for passwords by Git itself. @@ -206,7 +206,6 @@ func downloadPaths(patterns []string) []string { for _, arg := range patterns { if strings.Contains(arg, "@") { base.Fatalf("go: can only use path@version syntax with 'go get' and 'go install' in module-aware mode") - continue } // Guard against 'go get x.go', a common mistake. @@ -214,18 +213,19 @@ func downloadPaths(patterns []string) []string { // if the argument has no slash or refers to an existing file. if strings.HasSuffix(arg, ".go") { if !strings.Contains(arg, "/") { - base.Errorf("go get %s: arguments must be package or module paths", arg) + base.Errorf("go: %s: arguments must be package or module paths", arg) continue } if fi, err := os.Stat(arg); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg) } } } base.ExitIfErrors() var pkgs []string - for _, m := range search.ImportPathsQuiet(patterns) { + noModRoots := []string{} + for _, m := range search.ImportPathsQuiet(patterns, noModRoots) { if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") { pkgs = append(pkgs, m.Pattern()) } else { @@ -315,7 +315,8 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) if wildcardOkay && strings.Contains(arg, "...") { match := search.NewMatch(arg) if match.IsLocal() { - match.MatchDirs() + noModRoots := []string{} // We're in gopath mode, so there are no modroots. + match.MatchDirs(noModRoots) args = match.Dirs } else { match.MatchPackages() @@ -415,10 +416,10 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) // to make the first copy of or update a copy of the given package. func downloadPackage(p *load.Package) error { var ( - vcsCmd *vcs.Cmd - repo, rootPath string - err error - blindRepo bool // set if the repo has unusual configuration + vcsCmd *vcs.Cmd + repo, rootPath, repoDir string + err error + blindRepo bool // set if the repo has unusual configuration ) // p can be either a real package, or a pseudo-package whose “import path” is @@ -444,10 +445,19 @@ func downloadPackage(p *load.Package) error { if p.Internal.Build.SrcRoot != "" { // Directory exists. Look for checkout along path to src. - vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot) + const allowNesting = false + repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting) if err != nil { return err } + if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) { + panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot)) + } + rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) + if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil { + return err + } + repo = "" // should be unused; make distinctive // Double-check where it came from. diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go index 7a730fc8eb8c5e..f73097af8455aa 100644 --- a/src/cmd/go/internal/help/help.go +++ b/src/cmd/go/internal/help/help.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 help implements the ``go help'' command. +// Package help implements the “go help” command. package help import ( @@ -162,7 +162,7 @@ func (w *errWriter) Write(b []byte) (int, error) { } // tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) { +func tmpl(w io.Writer, text string, data any) { t := template.New("top") t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) template.Must(t.Parse(text)) diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 490ff1fb7cf05b..2398260536c073 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -506,6 +506,8 @@ General-purpose environment variables: GOENV The location of the Go environment configuration file. Cannot be set using 'go env -w'. + Setting GOENV=off in the environment disables the use of the + default configuration file. GOFLAGS A space-separated list of -flag=value settings to apply to go commands by default, when the given flag is known by @@ -543,6 +545,14 @@ General-purpose environment variables: GOVCS Lists version control commands that may be used with matching servers. See 'go help vcs'. + GOWORK + In module aware mode, use the given go.work file as a workspace file. + By default or when GOWORK is "auto", the go command searches for a + file named go.work in the current directory and then containing directories + until one is found. If a valid go.work file is found, the modules + specified will collectively be used as the main modules. If GOWORK + is "off", or a go.work file is not found in "auto" mode, workspace + mode is disabled. Environment variables for use with cgo: @@ -592,6 +602,10 @@ Architecture-specific environment variables: GO386 For GOARCH=386, how to implement floating point instructions. Valid values are sse2 (default), softfloat. + GOAMD64 + For GOARCH=amd64, the microarchitecture level for which to compile. + Valid values are v1 (default), v2, v3, v4. + See https://golang.org/wiki/MinimumRequirements#amd64 GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. @@ -600,7 +614,7 @@ Architecture-specific environment variables: Valid values are hardfloat (default), softfloat. GOPPC64 For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture). - Valid values are power8 (default), power9. + Valid values are power8 (default), power9, power10. GOWASM For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. Valid values are satconv, signext. @@ -771,6 +785,13 @@ The go command also caches successful package test results. See 'go help test' for details. Running 'go clean -testcache' removes all cached test results (but not cached build results). +The go command also caches values used in fuzzing with 'go test -fuzz', +specifically, values that expanded code coverage when passed to a +fuzz function. These values are not used for regular building and +testing, but they're stored in a subdirectory of the build cache. +Running 'go clean -fuzzcache' removes all cached fuzzing values. +This may make fuzzing less effective, temporarily. + The GODEBUG environment variable can enable printing of debugging information about the state of the cache: @@ -791,11 +812,12 @@ var HelpBuildConstraint = &base.Command{ UsageLine: "buildconstraint", Short: "build constraints", Long: ` -A build constraint, also known as a build tag, is a line comment that begins +A build constraint, also known as a build tag, is a condition under which a +file should be included in the package. Build constraints are given by a +line comment that begins //go:build -that lists the conditions under which a file should be included in the package. Constraints may appear in any kind of source file (not just Go), but they must appear near the top of the file, preceded only by blank lines and other line comments. These rules mean that in Go @@ -804,9 +826,9 @@ files a build constraint must appear before the package clause. To distinguish build constraints from package documentation, a build constraint should be followed by a blank line. -A build constraint is evaluated as an expression containing options -combined by ||, &&, and ! operators and parentheses. Operators have -the same meaning as in Go. +A build constraint comment is evaluated as an expression containing +build tags combined by ||, &&, and ! operators and parentheses. +Operators have the same meaning as in Go. For example, the following build constraint constrains a file to build when the "linux" and "386" constraints are satisfied, or when @@ -816,12 +838,13 @@ build when the "linux" and "386" constraints are satisfied, or when It is an error for a file to have more than one //go:build line. -During a particular build, the following words are satisfied: +During a particular build, the following build tags are satisfied: - the target operating system, as spelled by runtime.GOOS, set with the GOOS environment variable. - the target architecture, as spelled by runtime.GOARCH, set with the GOARCH environment variable. + - "unix", if GOOS is a Unix or Unix-like system. - the compiler being used, either "gc" or "gccgo" - "cgo", if the cgo command is supported (see CGO_ENABLED in 'go help environment'). diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go index 50aeabc578c930..bbe08da6b36e9e 100644 --- a/src/cmd/go/internal/imports/build.go +++ b/src/cmd/go/internal/imports/build.go @@ -2,17 +2,52 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Copied from Go distribution src/go/build/build.go, syslist.go +// Copied from Go distribution src/go/build/build.go, syslist.go. +// That package does not export the ability to process raw file data, +// although we could fake it with an appropriate build.Context +// and a lot of unwrapping. +// More importantly, that package does not implement the tags["*"] +// special case, in which both tag and !tag are considered to be true +// for essentially all tags (except "ignore"). +// +// If we added this API to go/build directly, we wouldn't need this +// file anymore, but this API is not terribly general-purpose and we +// don't really want to commit to any public form of it, nor do we +// want to move the core parts of go/build into a top-level internal package. +// These details change very infrequently, so the copy is fine. package imports import ( "bytes" + "cmd/go/internal/cfg" + "errors" + "fmt" + "go/build/constraint" "strings" "unicode" ) -var slashslash = []byte("//") +var ( + bSlashSlash = []byte("//") + bStarSlash = []byte("*/") + bSlashStar = []byte("/*") + bPlusBuild = []byte("+build") + + goBuildComment = []byte("//go:build") + + errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment") + errMultipleGoBuild = errors.New("multiple //go:build comments") +) + +func isGoBuildComment(line []byte) bool { + if !bytes.HasPrefix(line, goBuildComment) { + return false + } + line = bytes.TrimSpace(line) + rest := line[len(goBuildComment):] + return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest) +} // ShouldBuild reports whether it is okay to use this file, // The rule is that in the file's leading run of // comments @@ -32,12 +67,62 @@ var slashslash = []byte("//") // the purpose of satisfying build tags, in order to estimate // (conservatively) whether a file could ever possibly be used // in any build. -// func ShouldBuild(content []byte, tags map[string]bool) bool { - // Pass 1. Identify leading run of // comments and blank lines, + // Identify leading run of // comments and blank lines, // which must be followed by a blank line. + // Also identify any //go:build comments. + content, goBuild, _, err := parseFileHeader(content) + if err != nil { + return false + } + + // If //go:build line is present, it controls. + // Otherwise fall back to +build processing. + var shouldBuild bool + switch { + case goBuild != nil: + x, err := constraint.Parse(string(goBuild)) + if err != nil { + return false + } + shouldBuild = eval(x, tags, true) + + default: + shouldBuild = true + p := content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) { + continue + } + text := string(line) + if !constraint.IsPlusBuild(text) { + continue + } + if x, err := constraint.Parse(text); err == nil { + if !eval(x, tags, true) { + shouldBuild = false + } + } + } + } + + return shouldBuild +} + +func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) { end := 0 p := content + ended := false // found non-blank, non-// line, so stopped accepting // +build lines + inSlashStar := false // in /* */ comment + +Lines: for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { @@ -46,78 +131,61 @@ func ShouldBuild(content []byte, tags map[string]bool) bool { p = p[len(p):] } line = bytes.TrimSpace(line) - if len(line) == 0 { // Blank line + if len(line) == 0 && !ended { // Blank line + // Remember position of most recent blank line. + // When we find the first non-blank, non-// line, + // this "end" position marks the latest file position + // where a // +build line can appear. + // (It must appear _before_ a blank line before the non-blank, non-// line. + // Yes, that's confusing, which is part of why we moved to //go:build lines.) + // Note that ended==false here means that inSlashStar==false, + // since seeing a /* would have set ended==true. end = len(content) - len(p) - continue + continue Lines } - if !bytes.HasPrefix(line, slashslash) { // Not comment line - break + if !bytes.HasPrefix(line, bSlashSlash) { // Not comment line + ended = true } - } - content = content[:end] - // Pass 2. Process each line in the run. - p = content - allok := true - for len(p) > 0 { - line := p - if i := bytes.IndexByte(line, '\n'); i >= 0 { - line, p = line[:i], p[i+1:] - } else { - p = p[len(p):] - } - line = bytes.TrimSpace(line) - if !bytes.HasPrefix(line, slashslash) { - continue + if !inSlashStar && isGoBuildComment(line) { + if goBuild != nil { + return nil, nil, false, errMultipleGoBuild + } + goBuild = line } - line = bytes.TrimSpace(line[len(slashslash):]) - if len(line) > 0 && line[0] == '+' { - // Looks like a comment +line. - f := strings.Fields(string(line)) - if f[0] == "+build" { - ok := false - for _, tok := range f[1:] { - if matchTags(tok, tags) { - ok = true - } - } - if !ok { - allok = false + + Comments: + for len(line) > 0 { + if inSlashStar { + if i := bytes.Index(line, bStarSlash); i >= 0 { + inSlashStar = false + line = bytes.TrimSpace(line[i+len(bStarSlash):]) + continue Comments } + continue Lines + } + if bytes.HasPrefix(line, bSlashSlash) { + continue Lines } + if bytes.HasPrefix(line, bSlashStar) { + inSlashStar = true + line = bytes.TrimSpace(line[len(bSlashStar):]) + continue Comments + } + // Found non-comment text. + break Lines } } - return allok + return content[:end], goBuild, sawBinaryOnly, nil } -// matchTags reports whether the name is one of: -// -// tag (if tags[tag] is true) -// !tag (if tags[tag] is false) -// a comma-separated list of any of these -// -func matchTags(name string, tags map[string]bool) bool { - if name == "" { - return false - } - if i := strings.Index(name, ","); i >= 0 { - // comma-separated list - ok1 := matchTags(name[:i], tags) - ok2 := matchTags(name[i+1:], tags) - return ok1 && ok2 - } - if strings.HasPrefix(name, "!!") { // bad syntax, reject always - return false - } - if strings.HasPrefix(name, "!") { // negation - return len(name) > 1 && matchTag(name[1:], tags, false) - } - return matchTag(name, tags, true) -} - -// matchTag reports whether the tag name is valid and satisfied by tags[name]==want. -func matchTag(name string, tags map[string]bool, want bool) bool { +// matchTag reports whether the tag name is valid and tags[name] is true. +// As a special case, if tags["*"] is true and name is not empty or ignore, +// then matchTag will return prefer instead of the actual answer, +// which allows the caller to pretend in that case that most tags are +// both true and false. +func matchTag(name string, tags map[string]bool, prefer bool) bool { // Tags must be letters, digits, underscores or dots. // Unlike in Go identifiers, all digits are fine (e.g., "386"). for _, c := range name { @@ -131,37 +199,73 @@ func matchTag(name string, tags map[string]bool, want bool) bool { // if we put * in the tags map then all tags // except "ignore" are considered both present and not // (so we return true no matter how 'want' is set). - return true + return prefer } - have := tags[name] - if name == "linux" { - have = have || tags["android"] + if tags[name] { + return true } - if name == "solaris" { - have = have || tags["illumos"] + + switch name { + case "linux": + return tags["android"] + case "solaris": + return tags["illumos"] + case "darwin": + return tags["ios"] + case "unix": + return unixOS[cfg.BuildContext.GOOS] + default: + return false } - if name == "darwin" { - have = have || tags["ios"] +} + +// eval is like +// +// x.Eval(func(tag string) bool { return matchTag(tag, tags) }) +// +// except that it implements the special case for tags["*"] meaning +// all tags are both true and false at the same time. +func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool { + switch x := x.(type) { + case *constraint.TagExpr: + return matchTag(x.Tag, tags, prefer) + case *constraint.NotExpr: + return !eval(x.X, tags, !prefer) + case *constraint.AndExpr: + return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer) + case *constraint.OrExpr: + return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer) } - return have == want + panic(fmt.Sprintf("unexpected constraint expression %T", x)) +} + +// Eval is like +// +// x.Eval(func(tag string) bool { return matchTag(tag, tags) }) +// +// except that it implements the special case for tags["*"] meaning +// all tags are both true and false at the same time. +func Eval(x constraint.Expr, tags map[string]bool, prefer bool) bool { + return eval(x, tags, prefer) } // MatchFile returns false if the name contains a $GOOS or $GOARCH // suffix which does not match the current system. // The recognized name formats are: // -// name_$(GOOS).* -// name_$(GOARCH).* -// name_$(GOOS)_$(GOARCH).* -// name_$(GOOS)_test.* -// name_$(GOARCH)_test.* -// name_$(GOOS)_$(GOARCH)_test.* +// name_$(GOOS).* +// name_$(GOARCH).* +// name_$(GOOS)_$(GOARCH).* +// name_$(GOOS)_test.* +// name_$(GOARCH)_test.* +// name_$(GOOS)_$(GOARCH)_test.* // // Exceptions: -// if GOOS=android, then files with GOOS=linux are also matched. -// if GOOS=illumos, then files with GOOS=solaris are also matched. -// if GOOS=ios, then files with GOOS=darwin are also matched. +// +// if GOOS=android, then files with GOOS=linux are also matched. +// if GOOS=illumos, then files with GOOS=solaris are also matched. +// if GOOS=ios, then files with GOOS=darwin are also matched. // // If tags["*"] is true, then MatchFile will consider all possible // GOOS and GOARCH to be available and will consequently @@ -224,6 +328,24 @@ var KnownOS = map[string]bool{ "zos": true, } +// unixOS is the set of GOOS values matched by the "unix" build tag. +// This is not used for filename matching. +// This is the same list as in go/build/syslist.go and cmd/dist/build.go. +var unixOS = map[string]bool{ + "aix": true, + "android": true, + "darwin": true, + "dragonfly": true, + "freebsd": true, + "hurd": true, + "illumos": true, + "ios": true, + "linux": true, + "netbsd": true, + "openbsd": true, + "solaris": true, +} + var KnownArch = map[string]bool{ "386": true, "amd64": true, @@ -240,6 +362,7 @@ var KnownArch = map[string]bool{ "mips64le": true, "mips64p32": true, "mips64p32le": true, + "loong64": true, "ppc": true, "riscv": true, "riscv64": true, diff --git a/src/cmd/go/internal/imports/scan_test.go b/src/cmd/go/internal/imports/scan_test.go index 2d245ee7872e73..56efa9023f1990 100644 --- a/src/cmd/go/internal/imports/scan_test.go +++ b/src/cmd/go/internal/imports/scan_test.go @@ -10,7 +10,6 @@ import ( "os" "path" "path/filepath" - "runtime" "strings" "testing" ) @@ -18,7 +17,7 @@ import ( func TestScan(t *testing.T) { testenv.MustHaveGoBuild(t) - imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags()) + imports, testImports, err := ScanDir(filepath.Join(testenv.GOROOT(t), "src/encoding/json"), Tags()) if err != nil { t.Fatal(err) } @@ -33,7 +32,7 @@ func TestScan(t *testing.T) { } if p == "net/http" { // A test import but not an import - t.Errorf("json reported as importing encoding/binary but does not") + t.Errorf("json reported as importing net/http but does not") } } if !foundBase64 { diff --git a/src/cmd/go/internal/imports/tags.go b/src/cmd/go/internal/imports/tags.go index 01b448b9142e57..d1467b81b0b660 100644 --- a/src/cmd/go/internal/imports/tags.go +++ b/src/cmd/go/internal/imports/tags.go @@ -36,6 +36,9 @@ func loadTags() map[string]bool { for _, tag := range cfg.BuildContext.BuildTags { tags[tag] = true } + for _, tag := range cfg.BuildContext.ToolTags { + tags[tag] = true + } for _, tag := range cfg.BuildContext.ReleaseTags { tags[tag] = true } diff --git a/src/cmd/go/internal/imports/testdata/android/e.go b/src/cmd/go/internal/imports/testdata/android/e.go index d9b2db769b5f0c..f1b9c888c2cafd 100644 --- a/src/cmd/go/internal/imports/testdata/android/e.go +++ b/src/cmd/go/internal/imports/testdata/android/e.go @@ -1,3 +1,4 @@ +//go:build android // +build android package android diff --git a/src/cmd/go/internal/imports/testdata/android/f.go b/src/cmd/go/internal/imports/testdata/android/f.go index 281e4dd6b9898e..bb0ff7b73f67c1 100644 --- a/src/cmd/go/internal/imports/testdata/android/f.go +++ b/src/cmd/go/internal/imports/testdata/android/f.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package android diff --git a/src/cmd/go/internal/imports/testdata/android/g.go b/src/cmd/go/internal/imports/testdata/android/g.go index 66a789c0ada4b5..ee19424890a963 100644 --- a/src/cmd/go/internal/imports/testdata/android/g.go +++ b/src/cmd/go/internal/imports/testdata/android/g.go @@ -1,3 +1,4 @@ +//go:build !android // +build !android package android diff --git a/src/cmd/go/internal/imports/testdata/illumos/e.go b/src/cmd/go/internal/imports/testdata/illumos/e.go index 5e1ed3cb9decae..fddf2c429909b7 100644 --- a/src/cmd/go/internal/imports/testdata/illumos/e.go +++ b/src/cmd/go/internal/imports/testdata/illumos/e.go @@ -1,3 +1,4 @@ +//go:build illumos // +build illumos package illumos diff --git a/src/cmd/go/internal/imports/testdata/illumos/f.go b/src/cmd/go/internal/imports/testdata/illumos/f.go index f3e3f728bce5d8..4b6d528e4c2225 100644 --- a/src/cmd/go/internal/imports/testdata/illumos/f.go +++ b/src/cmd/go/internal/imports/testdata/illumos/f.go @@ -1,3 +1,4 @@ +//go:build solaris // +build solaris package illumos diff --git a/src/cmd/go/internal/imports/testdata/illumos/g.go b/src/cmd/go/internal/imports/testdata/illumos/g.go index b30f1eb4037322..1bf826b81510b4 100644 --- a/src/cmd/go/internal/imports/testdata/illumos/g.go +++ b/src/cmd/go/internal/imports/testdata/illumos/g.go @@ -1,3 +1,4 @@ +//go:build !illumos // +build !illumos package illumos diff --git a/src/cmd/go/internal/imports/testdata/star/x1.go b/src/cmd/go/internal/imports/testdata/star/x1.go index 6a9594aed035e6..eaaea979e9dc82 100644 --- a/src/cmd/go/internal/imports/testdata/star/x1.go +++ b/src/cmd/go/internal/imports/testdata/star/x1.go @@ -1,8 +1,5 @@ -// +build blahblh -// +build linux -// +build !linux -// +build windows -// +build darwin +//go:build blahblh && linux && !linux && windows && darwin +// +build blahblh,linux,!linux,windows,darwin package x diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 7cb9ec6d949242..66c33d9adefae8 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.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 list implements the ``go list'' command. +// Package list implements the “go list” command. package list import ( @@ -13,7 +13,9 @@ import ( "fmt" "io" "os" + "reflect" "sort" + "strconv" "strings" "text/template" @@ -157,7 +159,10 @@ For more information about the meaning of these fields see the documentation for the go/build package's Context type. The -json flag causes the package data to be printed in JSON format -instead of using the template format. +instead of using the template format. The JSON flag can optionally be +provided with a set of comma-separated required field names to be output. +If so, those required fields will always appear in JSON output, but +others may be omitted to save work in computing the JSON struct. The -compiled flag causes list to set CompiledGoFiles to the Go source files presented to the compiler. Typically this means that it repeats @@ -182,7 +187,8 @@ a non-nil Error field; other information may or may not be missing (zeroed). The -export flag causes list to set the Export field to the name of a -file containing up-to-date export information for the given package. +file containing up-to-date export information for the given package, +and the BuildID field to the build ID of the compiled package. The -find flag causes list to identify the named packages but not resolve their dependencies: the Imports and Deps lists will be empty. @@ -216,19 +222,23 @@ When listing modules, the -f flag still specifies a format template applied to a Go struct, but now a Module struct: type Module struct { - Path string // module path - Version string // module version - Versions []string // available module versions (with -versions) - Replace *Module // replaced by this module - Time *time.Time // time version was created - Update *Module // available update, if any (with -u) - Main bool // is this the main module? - Indirect bool // is this module only an indirect dependency of main module? - Dir string // directory holding files for this module, if any - GoMod string // path to go.mod file used when loading this module, if any - GoVersion string // go version used in module - Retracted string // retraction information, if any (with -retracted or -u) - Error *ModuleError // error loading module + Path string // module path + Query string // version query corresponding to this version + Version string // module version + Versions []string // available module versions + Replace *Module // replaced by this module + Time *time.Time // time version was created + Update *Module // available update (with -u) + Main bool // is this the main module? + Indirect bool // module is only indirectly needed by main module + Dir string // directory holding local copy of files, if any + GoMod string // path to go.mod file describing module, if any + GoVersion string // go version used in module + Retracted []string // retraction information, if any (with -retracted or -u) + Deprecated string // deprecation message, if any (with -u) + Error *ModuleError // error loading module + Origin any // provenance of module + Reuse bool // reuse of old module info is safe } type ModuleError struct { @@ -305,6 +315,16 @@ that must be a module path or query and returns the specified module as a Module struct. If an error occurs, the result will be a Module struct with a non-nil Error field. +When using -m, the -reuse=old.json flag accepts the name of file containing +the JSON output of a previous 'go list -m -json' invocation with the +same set of modifier flags (such as -u, -retracted, and -versions). +The go command may use this file to determine that a module is unchanged +since the previous invocation and avoid redownloading information about it. +Modules that are not redownloaded will be marked in the new output by +setting the Reuse field to true. Normally the module cache provides this +kind of reuse automatically; the -reuse flag can be useful on systems that +do not preserve the module cache. + For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'. @@ -316,29 +336,88 @@ For more about modules, see https://golang.org/ref/mod. func init() { CmdList.Run = runList // break init cycle work.AddBuildFlags(CmdList, work.DefaultBuildFlags) + CmdList.Flag.Var(&listJsonFields, "json", "") } var ( - listCompiled = CmdList.Flag.Bool("compiled", false, "") - listDeps = CmdList.Flag.Bool("deps", false, "") - listE = CmdList.Flag.Bool("e", false, "") - listExport = CmdList.Flag.Bool("export", false, "") - listFmt = CmdList.Flag.String("f", "", "") - listFind = CmdList.Flag.Bool("find", false, "") - listJson = CmdList.Flag.Bool("json", false, "") - listM = CmdList.Flag.Bool("m", false, "") - listRetracted = CmdList.Flag.Bool("retracted", false, "") - listTest = CmdList.Flag.Bool("test", false, "") - listU = CmdList.Flag.Bool("u", false, "") - listVersions = CmdList.Flag.Bool("versions", false, "") + listCompiled = CmdList.Flag.Bool("compiled", false, "") + listDeps = CmdList.Flag.Bool("deps", false, "") + listE = CmdList.Flag.Bool("e", false, "") + listExport = CmdList.Flag.Bool("export", false, "") + listFmt = CmdList.Flag.String("f", "", "") + listFind = CmdList.Flag.Bool("find", false, "") + listJson bool + listJsonFields jsonFlag // If not empty, only output these fields. + listM = CmdList.Flag.Bool("m", false, "") + listRetracted = CmdList.Flag.Bool("retracted", false, "") + listReuse = CmdList.Flag.String("reuse", "", "") + listTest = CmdList.Flag.Bool("test", false, "") + listU = CmdList.Flag.Bool("u", false, "") + listVersions = CmdList.Flag.Bool("versions", false, "") ) +// A StringsFlag is a command-line flag that interprets its argument +// as a space-separated list of possibly-quoted strings. +type jsonFlag map[string]bool + +func (v *jsonFlag) Set(s string) error { + if v, err := strconv.ParseBool(s); err == nil { + listJson = v + return nil + } + listJson = true + if *v == nil { + *v = make(map[string]bool) + } + for _, f := range strings.Split(s, ",") { + (*v)[f] = true + } + return nil +} + +func (v *jsonFlag) String() string { + var fields []string + for f := range *v { + fields = append(fields, f) + } + sort.Strings(fields) + return strings.Join(fields, ",") +} + +func (v *jsonFlag) IsBoolFlag() bool { + return true +} + +func (v *jsonFlag) needAll() bool { + return len(*v) == 0 +} + +func (v *jsonFlag) needAny(fields ...string) bool { + if v.needAll() { + return true + } + for _, f := range fields { + if (*v)[f] { + return true + } + } + return false +} + var nl = []byte{'\n'} func runList(ctx context.Context, cmd *base.Command, args []string) { - if *listFmt != "" && *listJson == true { + modload.InitWorkfile() + + if *listFmt != "" && listJson == true { base.Fatalf("go list -f cannot be used with -json") } + if *listReuse != "" && !*listM { + base.Fatalf("go list -reuse cannot be used without -m") + } + if *listReuse != "" && modload.HasModRoot() { + base.Fatalf("go list -reuse cannot be used inside a module") + } work.BuildInit() out := newTrackingWriter(os.Stdout) @@ -355,9 +434,18 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } } - var do func(interface{}) - if *listJson { - do = func(x interface{}) { + var do func(x any) + if listJson { + do = func(x any) { + if !listJsonFields.needAll() { + v := reflect.ValueOf(x).Elem() // do is always called with a non-nil pointer. + // Clear all non-requested fields. + for i := 0; i < v.NumField(); i++ { + if !listJsonFields.needAny(v.Type().Field(i).Name) { + v.Field(i).Set(reflect.Zero(v.Type().Field(i).Type)) + } + } + } b, err := json.MarshalIndent(x, "", "\t") if err != nil { out.Flush() @@ -383,7 +471,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { if err != nil { base.Fatalf("%s", err) } - do = func(x interface{}) { + do = func(x any) { if err := tmpl.Execute(out, x); err != nil { out.Flush() base.Fatalf("%s", err) @@ -424,12 +512,12 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } if modload.Init(); !modload.Enabled() { - base.Fatalf("go list -m: not using modules") + base.Fatalf("go: list -m cannot be used with GO111MODULE=off") } modload.LoadModFile(ctx) // Sets cfg.BuildMod as a side-effect. if cfg.BuildMod == "vendor" { - const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" + const actionDisabledFormat = "go: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" if *listVersions { base.Fatalf(actionDisabledFormat, "determine available versions") @@ -464,15 +552,18 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { mode |= modload.ListRetractedVersions } } - mods, err := modload.ListModules(ctx, args, mode) + if *listReuse != "" && len(args) == 0 { + base.Fatalf("go: list -m -reuse only has an effect with module@version arguments") + } + mods, err := modload.ListModules(ctx, args, mode, *listReuse) if !*listE { for _, m := range mods { if m.Error != nil { - base.Errorf("go list -m: %v", m.Error.Err) + base.Errorf("go: %v", m.Error.Err) } } if err != nil { - base.Errorf("go list -m: %v", err) + base.Errorf("go: %v", err) } base.ExitIfErrors() } @@ -501,6 +592,15 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { pkgOpts := load.PackageOpts{ IgnoreImports: *listFind, ModResolveTests: *listTest, + AutoVCS: true, + // SuppressDeps is set if the user opts to explicitly ask for the json fields they + // need, don't ask for Deps or DepsErrors. It's not set when using a template string, + // even if *listFmt doesn't contain .Deps because Deps are used to find import cycles + // for test variants of packages and users who have been providing format strings + // might not expect those errors to stop showing up. + // See issue #52443. + SuppressDeps: !listJsonFields.needAny("Deps", "DepsErrors"), + SuppressBuildInfo: !listJsonFields.needAny("Stale", "StaleReason"), } pkgs := load.PackagesAndErrors(ctx, pkgOpts, args) if !*listE { @@ -587,10 +687,15 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } // Do we need to run a build to gather information? - needStale := *listJson || strings.Contains(*listFmt, ".Stale") + needStale := (listJson && listJsonFields.needAny("Stale", "StaleReason")) || strings.Contains(*listFmt, ".Stale") if needStale || *listExport || *listCompiled { - var b work.Builder - b.Init() + b := work.NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() + b.IsCmdList = true b.NeedExport = *listExport b.NeedCompiledGoFiles = *listCompiled @@ -706,9 +811,9 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { if *listRetracted { mode |= modload.ListRetracted } - rmods, err := modload.ListModules(ctx, args, mode) + rmods, err := modload.ListModules(ctx, args, mode, *listReuse) if err != nil && !*listE { - base.Errorf("go list -retracted: %v", err) + base.Errorf("go: %v", err) } for i, arg := range args { rmod := rmods[i] diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go index 440cb86134489a..55bdab013505ab 100644 --- a/src/cmd/go/internal/load/flag.go +++ b/src/cmd/go/internal/load/flag.go @@ -6,7 +6,7 @@ package load import ( "cmd/go/internal/base" - "cmd/go/internal/str" + "cmd/internal/quoted" "fmt" "strings" ) @@ -22,6 +22,7 @@ var ( // that allows specifying different effective flags for different packages. // See 'go help build' for more details about per-package flags. type PerPackageFlag struct { + raw string present bool values []ppfValue } @@ -39,6 +40,7 @@ func (f *PerPackageFlag) Set(v string) error { // set is the implementation of Set, taking a cwd (current working directory) for easier testing. func (f *PerPackageFlag) set(v, cwd string) error { + f.raw = v f.present = true match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern // For backwards compatibility with earlier flag splitting, ignore spaces around flags. @@ -57,11 +59,14 @@ func (f *PerPackageFlag) set(v, cwd string) error { if i == 0 { return fmt.Errorf("missing in =") } + if v[0] == '\'' || v[0] == '"' { + return fmt.Errorf("parameter may not start with quote character %c", v[0]) + } pattern := strings.TrimSpace(v[:i]) match = MatchPackage(pattern, cwd) v = v[i+1:] } - flags, err := str.SplitQuotedFields(v) + flags, err := quoted.Split(v) if err != nil { return err } @@ -72,9 +77,7 @@ func (f *PerPackageFlag) set(v, cwd string) error { return nil } -// String is required to implement flag.Value. -// It is not used, because cmd/go never calls flag.PrintDefaults. -func (f *PerPackageFlag) String() string { return "" } +func (f *PerPackageFlag) String() string { return f.raw } // Present reports whether the flag appeared on the command line. func (f *PerPackageFlag) Present() bool { diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index a83cc9a812b674..79219d3f8dea99 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -14,16 +14,17 @@ import ( "go/build" "go/scanner" "go/token" - "internal/goroot" "io/fs" "os" - "path" + "os/exec" pathpkg "path" "path/filepath" "runtime" + "runtime/debug" "sort" "strconv" "strings" + "time" "unicode" "unicode/utf8" @@ -32,12 +33,14 @@ import ( "cmd/go/internal/fsys" "cmd/go/internal/imports" "cmd/go/internal/modfetch" + "cmd/go/internal/modindex" "cmd/go/internal/modinfo" "cmd/go/internal/modload" "cmd/go/internal/par" "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" + "cmd/go/internal/vcs" "cmd/internal/sys" "golang.org/x/mod/modfile" @@ -190,6 +193,18 @@ func (p *Package) Desc() string { return p.ImportPath } +// IsTestOnly reports whether p is a test-only package. +// +// A “test-only” package is one that: +// - is a test-only variant of an ordinary package, or +// - is a synthesized "main" package for a test binary, or +// - contains only _test.go files. +func (p *Package) IsTestOnly() bool { + return p.ForTest != "" || + p.Internal.TestmainGo != nil || + len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 && len(p.GoFiles)+len(p.CgoFiles) == 0 +} + type PackageInternal struct { // Unexported fields are not part of the public API. Build *build.Package @@ -203,6 +218,7 @@ type PackageInternal struct { Local bool // imported via local path (./ or ../) LocalPrefix string // interpret ./ and ../ imports relative to this prefix ExeName string // desired name for temporary executable + FuzzInstrument bool // package should be instrumented for fuzzing CoverMode string // preprocess Go source files with the coverage tool in this mode CoverVars map[string]*CoverVar // variables created by coverage analysis OmitDebug bool // tell linker not to write debug information @@ -384,6 +400,12 @@ func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) { p.SwigFiles = pp.SwigFiles p.SwigCXXFiles = pp.SwigCXXFiles p.SysoFiles = pp.SysoFiles + if cfg.BuildMSan { + // There's no way for .syso files to be built both with and without + // support for memory sanitizer. Assume they are built without, + // and drop them. + p.SysoFiles = nil + } p.CgoCFLAGS = pp.CgoCFLAGS p.CgoCPPFLAGS = pp.CgoCPPFLAGS p.CgoCXXFLAGS = pp.CgoCXXFLAGS @@ -494,7 +516,7 @@ type importError struct { err error // created with fmt.Errorf } -func ImportErrorf(path, format string, args ...interface{}) ImportPathError { +func ImportErrorf(path, format string, args ...any) ImportPathError { err := &importError{importPath: path, err: fmt.Errorf(format, args...)} if errStr := err.Error(); !strings.Contains(errStr, path) { panic(fmt.Sprintf("path %q not in error %q", path, errStr)) @@ -585,10 +607,10 @@ func ClearPackageCachePartial(args []string) { delete(packageCache, arg) } } - resolvedImportCache.DeleteIf(func(key interface{}) bool { + resolvedImportCache.DeleteIf(func(key any) bool { return shouldDelete[key.(importSpec).path] }) - packageDataCache.DeleteIf(func(key interface{}) bool { + packageDataCache.DeleteIf(func(key any) bool { return shouldDelete[key.(string)] }) } @@ -601,7 +623,7 @@ func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package { p := packageCache[arg] if p != nil { delete(packageCache, arg) - resolvedImportCache.DeleteIf(func(key interface{}) bool { + resolvedImportCache.DeleteIf(func(key any) bool { return key.(importSpec).path == p.ImportPath }) packageDataCache.Delete(p.ImportPath) @@ -663,6 +685,9 @@ func LoadImport(ctx context.Context, opts PackageOpts, path, srcDir string, pare } func loadImport(ctx context.Context, opts PackageOpts, pre *preload, path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package { + ctx, span := trace.StartSpan(ctx, "modload.loadImport "+path) + defer span.Done() + if path == "" { panic("LoadImport called with empty package path") } @@ -778,6 +803,9 @@ func loadImport(ctx context.Context, opts PackageOpts, pre *preload, path, srcDi // loadPackageData returns a boolean, loaded, which is true if this is the // first time the package was loaded. Callers may preload imports in this case. func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoot string, parentIsStd bool, mode int) (bp *build.Package, loaded bool, err error) { + ctx, span := trace.StartSpan(ctx, "load.loadPackageData "+path) + defer span.Done() + if path == "" { panic("loadPackageData called with empty package path") } @@ -813,13 +841,13 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo parentIsStd: parentIsStd, mode: mode, } - r := resolvedImportCache.Do(importKey, func() interface{} { + r := resolvedImportCache.Do(importKey, func() any { var r resolvedImport - if build.IsLocalImport(path) { + if cfg.ModulesEnabled { + r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path) + } else if build.IsLocalImport(path) { r.dir = filepath.Join(parentDir, path) r.path = dirToImportPath(r.dir) - } else if cfg.ModulesEnabled { - r.dir, r.path, r.err = modload.Lookup(parentPath, parentIsStd, path) } else if mode&ResolveImport != 0 { // We do our own path resolution, because we want to // find out the key to use in packageCache without the @@ -840,7 +868,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo // Load the package from its directory. If we already found the package's // directory when resolving its import path, use that. - data := packageDataCache.Do(r.path, func() interface{} { + data := packageDataCache.Do(r.path, func() any { loaded = true var data packageData if r.dir != "" { @@ -848,7 +876,23 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo if !cfg.ModulesEnabled { buildMode = build.ImportComment } + modroot := modload.PackageModRoot(ctx, r.path) + if modroot == "" && str.HasPathPrefix(r.dir, cfg.GOROOTsrc) { + modroot = cfg.GOROOTsrc + if str.HasPathPrefix(r.dir, cfg.GOROOTsrc+string(filepath.Separator)+"cmd") { + modroot += string(filepath.Separator) + "cmd" + } + } + if modroot != "" { + if rp, err := modindex.GetPackage(modroot, r.dir); err == nil { + data.p, data.err = rp.Import(cfg.BuildContext, buildMode) + goto Happy + } else if !errors.Is(err, modindex.ErrNotIndexed) { + base.Fatalf("go: %v", err) + } + } data.p, data.err = cfg.BuildContext.ImportDir(r.dir, buildMode) + Happy: if cfg.ModulesEnabled { // Override data.p.Root, since ImportDir sets it to $GOPATH, if // the module is inside $GOPATH/src. @@ -1059,7 +1103,7 @@ func cleanImport(path string) string { var isDirCache par.Cache func isDir(path string) bool { - return isDirCache.Do(path, func() interface{} { + return isDirCache.Do(path, func() any { fi, err := fsys.Stat(path) return err == nil && fi.IsDir() }).(bool) @@ -1109,6 +1153,7 @@ func dirAndRoot(path string, dir, root string) (string, string) { } if !str.HasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || path != "command-line-arguments" && !build.IsLocalImport(path) && filepath.Join(root, path) != dir { + debug.PrintStack() base.Fatalf("unexpected directory layout:\n"+ " import path: %s\n"+ " root: %s\n"+ @@ -1187,7 +1232,7 @@ var ( // goModPath returns the module path in the go.mod in dir, if any. func goModPath(dir string) (path string) { - return goModPathCache.Do(dir, func() interface{} { + return goModPathCache.Do(dir, func() any { data, err := os.ReadFile(filepath.Join(dir, "go.mod")) if err != nil { return "" @@ -1450,9 +1495,9 @@ func disallowInternal(ctx context.Context, srcDir string, importer *Package, imp // The importer is a list of command-line files. // Pretend that the import path is the import path of the // directory containing them. - // If the directory is outside the main module, this will resolve to ".", + // If the directory is outside the main modules, this will resolve to ".", // which is not a prefix of any valid module. - importerPath = modload.DirImportPath(ctx, importer.Dir) + importerPath, _ = modload.MainModules.DirImportPath(ctx, importer.Dir) } parentOfInternal := p.ImportPath[:i] if str.HasPathPrefix(importerPath, parentOfInternal) { @@ -1622,6 +1667,7 @@ var cgoSyscallExclude = map[string]bool{ "runtime/cgo": true, "runtime/race": true, "runtime/msan": true, + "runtime/asan": true, } var foldPath = make(map[string]string) @@ -1677,9 +1723,10 @@ func (p *Package) DefaultExecName() string { func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) { p.copyBuild(opts, bp) - // The localPrefix is the path we interpret ./ imports relative to. + // The localPrefix is the path we interpret ./ imports relative to, + // if we support them at all (not in module mode!). // Synthesized main packages sometimes override this. - if p.Internal.Local { + if p.Internal.Local && !cfg.ModulesEnabled { p.Internal.LocalPrefix = dirToImportPath(p.Dir) } @@ -1728,9 +1775,9 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * setError(e) return } - elem := p.DefaultExecName() - full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem - if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH { + elem := p.DefaultExecName() + cfg.ExeSuffix + full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + string(filepath.Separator) + elem + if cfg.BuildContext.GOOS != runtime.GOOS || cfg.BuildContext.GOARCH != runtime.GOARCH { // Install cross-compiled binaries to subdirectories of bin. elem = full } @@ -1740,7 +1787,7 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * if p.Internal.Build.BinDir != "" { // Install to GOBIN or bin of GOPATH entry. p.Target = filepath.Join(p.Internal.Build.BinDir, elem) - if !p.Goroot && strings.Contains(elem, "/") && cfg.GOBIN != "" { + if !p.Goroot && strings.Contains(elem, string(filepath.Separator)) && cfg.GOBIN != "" { // Do not create $GOBIN/goos_goarch/elem. p.Target = "" p.Internal.GobinSubdir = true @@ -1750,14 +1797,11 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * // This is for 'go tool'. // Override all the usual logic and force it into the tool directory. if cfg.BuildToolchainName == "gccgo" { - p.Target = filepath.Join(base.ToolDir, elem) + p.Target = filepath.Join(build.ToolDir, elem) } else { p.Target = filepath.Join(cfg.GOROOTpkg, "tool", full) } } - if p.Target != "" && cfg.BuildContext.GOOS == "windows" { - p.Target += ".exe" - } } else if p.Internal.Local { // Local import turned into absolute path. // No permanent install target. @@ -1918,10 +1962,15 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk * } } p.Internal.Imports = imports - p.collectDeps() - - if cfg.ModulesEnabled && p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 { - p.Internal.BuildInfo = modload.PackageBuildInfo(pkgPath, p.Deps) + if !opts.SuppressDeps { + p.collectDeps() + } + if p.Error == nil && p.Name == "main" && !p.Internal.ForceLibrary && len(p.DepsErrors) == 0 && !opts.SuppressBuildInfo { + // TODO(bcmills): loading VCS metadata can be fairly slow. + // Consider starting this as a background goroutine and retrieving the result + // asynchronously when we're actually ready to build the package, or when we + // actually need to evaluate whether the package's metadata is stale. + p.setBuildInfo(opts.AutoVCS) } // unsafe is a fake package. @@ -2012,13 +2061,18 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st for _, pattern = range patterns { pid++ + glob := pattern + all := strings.HasPrefix(pattern, "all:") + if all { + glob = pattern[len("all:"):] + } // Check pattern is valid for //go:embed. - if _, err := path.Match(pattern, ""); err != nil || !validEmbedPattern(pattern) { + if _, err := pathpkg.Match(glob, ""); err != nil || !validEmbedPattern(glob) { return nil, nil, fmt.Errorf("invalid pattern syntax") } // Glob to find matches. - match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(pattern)) + match, err := fsys.Glob(str.QuoteGlob(pkgdir) + string(filepath.Separator) + filepath.FromSlash(glob)) if err != nil { return nil, nil, err } @@ -2029,7 +2083,8 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st // then there may be other things lying around, like symbolic links or .git directories.) var list []string for _, file := range match { - rel := filepath.ToSlash(file[len(pkgdir)+1:]) // file, relative to p.Dir + // relative path to p.Dir which begins without prefix slash + rel := filepath.ToSlash(str.TrimFilePathPrefix(file, pkgdir)) what := "file" info, err := fsys.Lstat(file) @@ -2079,9 +2134,9 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st if err != nil { return err } - rel := filepath.ToSlash(path[len(pkgdir)+1:]) + rel := filepath.ToSlash(str.TrimFilePathPrefix(path, pkgdir)) name := info.Name() - if path != file && (isBadEmbedName(name) || name[0] == '.' || name[0] == '_') { + if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) { // Ignore bad names, assuming they won't go into modules. // Also avoid hidden files that user may not know about. // See golang.org/issue/42328. @@ -2193,6 +2248,267 @@ func (p *Package) collectDeps() { } } +// vcsStatusCache maps repository directories (string) +// to their VCS information (vcsStatusError). +var vcsStatusCache par.Cache + +// setBuildInfo gathers build information, formats it as a string to be +// embedded in the binary, then sets p.Internal.BuildInfo to that string. +// setBuildInfo should only be called on a main package with no errors. +// +// This information can be retrieved using debug.ReadBuildInfo. +// +// Note that the GoVersion field is not set here to avoid encoding it twice. +// It is stored separately in the binary, mostly for historical reasons. +func (p *Package) setBuildInfo(autoVCS bool) { + setPkgErrorf := func(format string, args ...any) { + if p.Error == nil { + p.Error = &PackageError{Err: fmt.Errorf(format, args...)} + } + } + + var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module + debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module { + version := mi.Version + if version == "" { + version = "(devel)" + } + dm := &debug.Module{ + Path: mi.Path, + Version: version, + } + if mi.Replace != nil { + dm.Replace = debugModFromModinfo(mi.Replace) + } else if mi.Version != "" { + dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version}) + } + return dm + } + + var main debug.Module + if p.Module != nil { + main = *debugModFromModinfo(p.Module) + } + + visited := make(map[*Package]bool) + mdeps := make(map[module.Version]*debug.Module) + var q []*Package + q = append(q, p.Internal.Imports...) + for len(q) > 0 { + p1 := q[0] + q = q[1:] + if visited[p1] { + continue + } + visited[p1] = true + if p1.Module != nil { + m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version} + if p1.Module.Path != main.Path && mdeps[m] == nil { + mdeps[m] = debugModFromModinfo(p1.Module) + } + } + q = append(q, p1.Internal.Imports...) + } + sortedMods := make([]module.Version, 0, len(mdeps)) + for mod := range mdeps { + sortedMods = append(sortedMods, mod) + } + module.Sort(sortedMods) + deps := make([]*debug.Module, len(sortedMods)) + for i, mod := range sortedMods { + deps[i] = mdeps[mod] + } + + pkgPath := p.ImportPath + if p.Internal.CmdlineFiles { + pkgPath = "command-line-arguments" + } + info := &debug.BuildInfo{ + Path: pkgPath, + Main: main, + Deps: deps, + } + appendSetting := func(key, value string) { + value = strings.ReplaceAll(value, "\n", " ") // make value safe + info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value}) + } + + // Add command-line flags relevant to the build. + // This is informational, not an exhaustive list. + // Please keep the list sorted. + if cfg.BuildASan { + appendSetting("-asan", "true") + } + if BuildAsmflags.present { + appendSetting("-asmflags", BuildAsmflags.String()) + } + appendSetting("-compiler", cfg.BuildContext.Compiler) + if gccgoflags := BuildGccgoflags.String(); gccgoflags != "" && cfg.BuildContext.Compiler == "gccgo" { + appendSetting("-gccgoflags", gccgoflags) + } + if gcflags := BuildGcflags.String(); gcflags != "" && cfg.BuildContext.Compiler == "gc" { + appendSetting("-gcflags", gcflags) + } + if ldflags := BuildLdflags.String(); ldflags != "" { + // https://go.dev/issue/52372: only include ldflags if -trimpath is not set, + // since it can include system paths through various linker flags (notably + // -extar, -extld, and -extldflags). + // + // TODO: since we control cmd/link, in theory we can parse ldflags to + // determine whether they may refer to system paths. If we do that, we can + // redact only those paths from the recorded -ldflags setting and still + // record the system-independent parts of the flags. + if !cfg.BuildTrimpath { + appendSetting("-ldflags", ldflags) + } + } + if cfg.BuildMSan { + appendSetting("-msan", "true") + } + if cfg.BuildRace { + appendSetting("-race", "true") + } + if tags := cfg.BuildContext.BuildTags; len(tags) > 0 { + appendSetting("-tags", strings.Join(tags, ",")) + } + if cfg.BuildTrimpath { + appendSetting("-trimpath", "true") + } + cgo := "0" + if cfg.BuildContext.CgoEnabled { + cgo = "1" + } + appendSetting("CGO_ENABLED", cgo) + // https://go.dev/issue/52372: only include CGO flags if -trimpath is not set. + // (If -trimpath is set, it is possible that these flags include system paths.) + // If cgo is involved, reproducibility is already pretty well ruined anyway, + // given that we aren't stamping header or library versions. + // + // TODO(bcmills): perhaps we could at least parse the flags and stamp the + // subset of flags that are known not to be paths? + if cfg.BuildContext.CgoEnabled && !cfg.BuildTrimpath { + for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} { + appendSetting(name, cfg.Getenv(name)) + } + } + appendSetting("GOARCH", cfg.BuildContext.GOARCH) + if cfg.RawGOEXPERIMENT != "" { + appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT) + } + appendSetting("GOOS", cfg.BuildContext.GOOS) + if key, val := cfg.GetArchEnv(); key != "" && val != "" { + appendSetting(key, val) + } + + // Add VCS status if all conditions are true: + // + // - -buildvcs is enabled. + // - p is a non-test contained within a main module (there may be multiple + // main modules in a workspace, but local replacements don't count). + // - Both the current directory and p's module's root directory are contained + // in the same local repository. + // - We know the VCS commands needed to get the status. + setVCSError := func(err error) { + setPkgErrorf("error obtaining VCS status: %v\n\tUse -buildvcs=false to disable VCS stamping.", err) + } + + var repoDir string + var vcsCmd *vcs.Cmd + var err error + const allowNesting = true + + wantVCS := false + switch cfg.BuildBuildvcs { + case "true": + wantVCS = true // Include VCS metadata even for tests if requested explicitly; see https://go.dev/issue/52648. + case "auto": + wantVCS = autoVCS && !p.IsTestOnly() + case "false": + default: + panic(fmt.Sprintf("unexpected value for cfg.BuildBuildvcs: %q", cfg.BuildBuildvcs)) + } + + if wantVCS && p.Module != nil && p.Module.Version == "" && !p.Standard { + repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting) + if err != nil && !errors.Is(err, os.ErrNotExist) { + setVCSError(err) + return + } + if !str.HasFilePathPrefix(p.Module.Dir, repoDir) && + !str.HasFilePathPrefix(repoDir, p.Module.Dir) { + // The module containing the main package does not overlap with the + // repository containing the working directory. Don't include VCS info. + // If the repo contains the module or vice versa, but they are not + // the same directory, it's likely an error (see below). + goto omitVCS + } + if cfg.BuildBuildvcs == "auto" && vcsCmd != nil && vcsCmd.Cmd != "" { + if _, err := exec.LookPath(vcsCmd.Cmd); err != nil { + // We fould a repository, but the required VCS tool is not present. + // "-buildvcs=auto" means that we should silently drop the VCS metadata. + goto omitVCS + } + } + } + if repoDir != "" && vcsCmd.Status != nil { + // Check that the current directory, package, and module are in the same + // repository. vcs.FromDir allows nested Git repositories, but nesting + // is not allowed for other VCS tools. The current directory may be outside + // p.Module.Dir when a workspace is used. + pkgRepoDir, _, err := vcs.FromDir(p.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if pkgRepoDir != repoDir { + if cfg.BuildBuildvcs != "auto" { + setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir)) + return + } + goto omitVCS + } + modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting) + if err != nil { + setVCSError(err) + return + } + if modRepoDir != repoDir { + if cfg.BuildBuildvcs != "auto" { + setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir)) + return + } + goto omitVCS + } + + type vcsStatusError struct { + Status vcs.Status + Err error + } + cached := vcsStatusCache.Do(repoDir, func() any { + st, err := vcsCmd.Status(vcsCmd, repoDir) + return vcsStatusError{st, err} + }).(vcsStatusError) + if err := cached.Err; err != nil { + setVCSError(err) + return + } + st := cached.Status + + appendSetting("vcs", vcsCmd.Cmd) + if st.Revision != "" { + appendSetting("vcs.revision", st.Revision) + } + if !st.CommitTime.IsZero() { + stamp := st.CommitTime.UTC().Format(time.RFC3339Nano) + appendSetting("vcs.time", stamp) + } + appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted)) + } +omitVCS: + + p.Internal.BuildInfo = info.String() +} + // SafeArg reports whether arg is a "safe" command-line argument, // meaning that when it appears in a command-line, it probably // doesn't have some special meaning other than its own name. @@ -2231,6 +2547,10 @@ func LinkerDeps(p *Package) []string { if cfg.BuildMSan { deps = append(deps, "runtime/msan") } + // Using address sanitizer forces an import of runtime/asan. + if cfg.BuildASan { + deps = append(deps, "runtime/asan") + } return deps } @@ -2411,6 +2731,20 @@ type PackageOpts struct { // are not be matched, and their dependencies may not be loaded. A warning // may be printed for non-literal arguments that match no main packages. MainOnly bool + + // AutoVCS controls whether we also load version-control metadata for main packages + // when -buildvcs=auto (the default). + AutoVCS bool + + // SuppressDeps is true if the caller does not need Deps and DepsErrors to be populated + // on the package. TestPackagesAndErrors examines the Deps field to determine if the test + // variant has an import cycle, so SuppressDeps should not be set if TestPackagesAndErrors + // will be called on the package. + SuppressDeps bool + + // SuppressBuildInfo is true if the caller does not need p.Stale, p.StaleReason, or p.Internal.BuildInfo + // to be populated on the package. + SuppressBuildInfo bool } // PackagesAndErrors returns the packages named by the command line arguments @@ -2447,7 +2781,8 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string) } matches, _ = modload.LoadPackages(ctx, modOpts, patterns...) } else { - matches = search.ImportPaths(patterns) + noModRoots := []string{} + matches = search.ImportPaths(patterns, noModRoots) } var ( @@ -2576,7 +2911,7 @@ func mainPackagesOnly(pkgs []*Package, matches []*search.Match) []*Package { var mains []*Package for _, pkg := range pkgs { - if pkg.Name == "main" { + if pkg.Name == "main" || (pkg.Name == "" && pkg.Error != nil) { treatAsMain[pkg.ImportPath] = true mains = append(mains, pkg) continue @@ -2673,10 +3008,7 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa if fi.IsDir() { base.Fatalf("%s is a directory, should be a Go file", file) } - dir1, _ := filepath.Split(file) - if dir1 == "" { - dir1 = "./" - } + dir1 := filepath.Dir(file) if dir == "" { dir = dir1 } else if dir != dir1 { @@ -2704,7 +3036,9 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa pkg.Internal.Local = true pkg.Internal.CmdlineFiles = true pkg.load(ctx, opts, "command-line-arguments", &stk, nil, bp, err) - pkg.Internal.LocalPrefix = dirToImportPath(dir) + if !cfg.ModulesEnabled { + pkg.Internal.LocalPrefix = dirToImportPath(dir) + } pkg.ImportPath = "command-line-arguments" pkg.Target = "" pkg.Match = gofiles @@ -2764,7 +3098,7 @@ func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args patterns := make([]string, len(args)) for i, arg := range args { if !strings.HasSuffix(arg, "@"+version) { - return nil, fmt.Errorf("%s: all arguments must have the same version (@%s)", arg, version) + return nil, fmt.Errorf("%s: all arguments must refer to packages in the same module at the same version (@%s)", arg, version) } p := arg[:len(arg)-len(version)-1] switch { @@ -2774,9 +3108,9 @@ func PackagesAndErrorsOutsideModule(ctx context.Context, opts PackageOpts, args return nil, fmt.Errorf("%s: argument must be a package path, not an absolute path", arg) case search.IsMetaPackage(p): return nil, fmt.Errorf("%s: argument must be a package path, not a meta-package", arg) - case path.Clean(p) != p: + case pathpkg.Clean(p) != p: return nil, fmt.Errorf("%s: argument must be a clean package path", arg) - case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): + case !strings.Contains(p, "...") && search.IsStandardImportPath(p) && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, p): return nil, fmt.Errorf("%s: argument must not be a package in the standard library", arg) default: patterns[i] = p diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go index c8282965669c56..1abefd8ad1cd70 100644 --- a/src/cmd/go/internal/load/test.go +++ b/src/cmd/go/internal/load/test.go @@ -76,9 +76,9 @@ func TestPackagesFor(ctx context.Context, opts PackageOpts, p *Package, cover *T } // TestPackagesAndErrors returns three packages: -// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). -// - ptest, the package p compiled with added "package p" test files. -// - pxtest, the result of compiling any "package p_test" (external) test files. +// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest). +// - ptest, the package p compiled with added "package p" test files. +// - pxtest, the result of compiling any "package p_test" (external) test files. // // If the package has no "package p_test" test files, pxtest will be nil. // If the non-test compilation of package p can be reused @@ -368,9 +368,9 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co if err != nil && pmain.Error == nil { pmain.Error = &PackageError{Err: err} } - if data != nil { - pmain.Internal.TestmainGo = &data - } + // Set TestmainGo even if it is empty: the presence of a TestmainGo + // indicates that this package is, in fact, a test main. + pmain.Internal.TestmainGo = &data return pmain, ptest, pxtest } @@ -555,6 +555,7 @@ func formatTestmain(t *testFuncs) ([]byte, error) { type testFuncs struct { Tests []testFunc Benchmarks []testFunc + FuzzTargets []testFunc Examples []testFunc TestMain *testFunc Package *Package @@ -610,7 +611,7 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { return err } defer src.Close() - f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments) + f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { return err } @@ -653,6 +654,13 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false}) *doImport, *seen = true, true + case isTest(name, "Fuzz"): + err := checkTestFunc(n, "F") + if err != nil { + return err + } + t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false}) + *doImport, *seen = true, true } } ex := doc.Examples(f) @@ -670,10 +678,16 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } func checkTestFunc(fn *ast.FuncDecl, arg string) error { + var why string if !isTestFunc(fn, arg) { - name := fn.Name.String() + why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg) + } + if fn.Type.TypeParams.NumFields() > 0 { + why = "test functions cannot have type parameters" + } + if why != "" { pos := testFileSet.Position(fn.Pos()) - return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg) + return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why) } return nil } @@ -716,6 +730,12 @@ var benchmarks = []testing.InternalBenchmark{ {{end}} } +var fuzzTargets = []testing.InternalFuzzTarget{ +{{range .FuzzTargets}} + {"{{.Name}}", {{.Package}}.{{.Name}}}, +{{end}} +} + var examples = []testing.InternalExample{ {{range .Examples}} {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, @@ -774,7 +794,7 @@ func main() { CoveredPackages: {{printf "%q" .Covered}}, }) {{end}} - m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples) + m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) {{with .TestMain}} {{.Package}}.{{.Name}}(m) os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go index a37b2ad6d184b0..09354d23061ec7 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || (solaris && !illumos) -// +build aix solaris,!illumos // This code implements the filelock API using POSIX 'fcntl' locks, which attach // to an (inode, process) pair rather than a file descriptor. To avoid unlocking diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go index 70f5d7a688a070..c18dbdf8503ad1 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris,!windows +//go:build !unix && !plan9 && !windows package filelock diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go index 908afb6c8cb768..54b2c946e0d6b2 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package filelock diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go index 640d4406f4200c..7bd7bd28f55ab7 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 -// +build !js,!plan9 package filelock_test diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go index 878a1e770d4d42..d7778d05de1884 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build darwin dragonfly freebsd illumos linux netbsd openbsd package filelock diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go index dd27ce92bd8d61..e2ca5383046bbe 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package filelock diff --git a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go index e4923f68764dad..1a677a7fe4a60d 100644 --- a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go +++ b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package lockedfile diff --git a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go index 979118b10ae83d..a2ce794b967521 100644 --- a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go +++ b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package lockedfile @@ -18,9 +17,9 @@ import ( // Opening an exclusive-use file returns an error. // The expected error strings are: // -// - "open/create -- file is locked" (cwfs, kfs) -// - "exclusive lock" (fossil) -// - "exclusive use file already open" (ramfs) +// - "open/create -- file is locked" (cwfs, kfs) +// - "exclusive lock" (fossil) +// - "exclusive use file already open" (ramfs) var lockedErrStrings = [...]string{ "file is locked", "exclusive lock", diff --git a/src/cmd/go/internal/lockedfile/lockedfile_test.go b/src/cmd/go/internal/lockedfile/lockedfile_test.go index 3acc6695a74800..79352bc8c7360d 100644 --- a/src/cmd/go/internal/lockedfile/lockedfile_test.go +++ b/src/cmd/go/internal/lockedfile/lockedfile_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // js does not support inter-process file locking. +// //go:build !js -// +build !js package lockedfile_test diff --git a/src/cmd/go/internal/lockedfile/transform_test.go b/src/cmd/go/internal/lockedfile/transform_test.go index b753346e7da50b..833cbf787958c4 100644 --- a/src/cmd/go/internal/lockedfile/transform_test.go +++ b/src/cmd/go/internal/lockedfile/transform_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // js does not support inter-process file locking. +// //go:build !js -// +build !js package lockedfile_test diff --git a/src/cmd/go/internal/mmap/mmap.go b/src/cmd/go/internal/mmap/mmap.go new file mode 100644 index 00000000000000..0cad9caf276001 --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap.go @@ -0,0 +1,31 @@ +// 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. + +// This package is a lightly modified version of the mmap code +// in github.com/google/codesearch/index. + +// The mmap package provides an abstraction for memory mapping files +// on different platforms. +package mmap + +import ( + "os" +) + +// Data is mmap'ed read-only data from a file. +// The backing file is never closed, so Data +// remains valid for the lifetime of the process. +type Data struct { + f *os.File + Data []byte +} + +// Mmap maps the given file into memory. +func Mmap(file string) (Data, error) { + f, err := os.Open(file) + if err != nil { + return Data{}, err + } + return mmapFile(f) +} diff --git a/src/cmd/go/internal/mmap/mmap_plan9.go b/src/cmd/go/internal/mmap/mmap_plan9.go new file mode 100644 index 00000000000000..faa5d5fc665e10 --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap_plan9.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mmap + +import ( + "io" + "os" +) + +// mmapFile on other systems doesn't mmap the file. It just reads everything. +func mmapFile(f *os.File) (Data, error) { + b, err := io.ReadAll(f) + if err != nil { + return Data{}, err + } + return Data{f, b}, nil +} diff --git a/src/cmd/go/internal/mmap/mmap_unix.go b/src/cmd/go/internal/mmap/mmap_unix.go new file mode 100644 index 00000000000000..53bcbb92a8ebb2 --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap_unix.go @@ -0,0 +1,36 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package mmap + +import ( + "fmt" + "io/fs" + "os" + "syscall" +) + +func mmapFile(f *os.File) (Data, error) { + st, err := f.Stat() + if err != nil { + return Data{}, err + } + size := st.Size() + pagesize := int64(os.Getpagesize()) + if int64(int(size+(pagesize-1))) != size+(pagesize-1) { + return Data{}, fmt.Errorf("%s: too large for mmap", f.Name()) + } + n := int(size) + if n == 0 { + return Data{f, nil}, nil + } + mmapLength := int(((size + pagesize - 1) / pagesize) * pagesize) // round up to page size + data, err := syscall.Mmap(int(f.Fd()), 0, mmapLength, syscall.PROT_READ, syscall.MAP_SHARED) + if err != nil { + return Data{}, &fs.PathError{Op: "mmap", Path: f.Name(), Err: err} + } + return Data{f, data[:n]}, nil +} diff --git a/src/cmd/go/internal/mmap/mmap_windows.go b/src/cmd/go/internal/mmap/mmap_windows.go new file mode 100644 index 00000000000000..1cf62feca3fd8d --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap_windows.go @@ -0,0 +1,41 @@ +// 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 mmap + +import ( + "fmt" + "os" + "syscall" + "unsafe" + + "internal/syscall/windows" +) + +func mmapFile(f *os.File) (Data, error) { + st, err := f.Stat() + if err != nil { + return Data{}, err + } + size := st.Size() + if size == 0 { + return Data{f, nil}, nil + } + h, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, 0, 0, nil) + if err != nil { + return Data{}, fmt.Errorf("CreateFileMapping %s: %w", f.Name(), err) + } + + addr, err := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, 0) + if err != nil { + return Data{}, fmt.Errorf("MapViewOfFile %s: %w", f.Name(), err) + } + var info windows.MemoryBasicInformation + err = windows.VirtualQuery(addr, &info, unsafe.Sizeof(info)) + if err != nil { + return Data{}, fmt.Errorf("VirtualQuery %s: %w", f.Name(), err) + } + data := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(info.RegionSize)) + return Data{f, data}, nil +} diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go index 0e5af852376e76..0b50afb668adcc 100644 --- a/src/cmd/go/internal/modcmd/download.go +++ b/src/cmd/go/internal/modcmd/download.go @@ -13,19 +13,24 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" "cmd/go/internal/modload" "golang.org/x/mod/module" + "golang.org/x/mod/semver" ) var cmdDownload = &base.Command{ - UsageLine: "go mod download [-x] [-json] [modules]", + UsageLine: "go mod download [-x] [-json] [-reuse=old.json] [modules]", Short: "download modules to local cache", Long: ` Download downloads the named modules, which can be module patterns selecting dependencies of the main module or module queries of the form path@version. -With no arguments, download applies to all dependencies of the main module -(equivalent to 'go mod download all'). + +With no arguments, download applies to the modules needed to build and test +the packages in the main module: the modules explicitly required by the main +module if it is at 'go 1.17' or higher, or all transitively-required modules +if at 'go 1.16' or lower. The go command will automatically download modules as needed during ordinary execution. The "go mod download" command is useful mainly for pre-filling @@ -40,6 +45,7 @@ corresponding to this Go struct: type Module struct { Path string // module path + Query string // version query corresponding to this version Version string // module version Error string // error loading module Info string // absolute path to cached .info file @@ -48,8 +54,18 @@ corresponding to this Go struct: Dir string // absolute path to cached source root directory Sum string // checksum for path, version (as in go.sum) GoModSum string // checksum for go.mod (as in go.sum) + Origin any // provenance of module + Reuse bool // reuse of old module info is safe } +The -reuse flag accepts the name of file containing the JSON output of a +previous 'go mod download -json' invocation. The go command may use this +file to determine that a module is unchanged since the previous invocation +and avoid redownloading it. Modules that are not redownloaded will be marked +in the new output by setting the Reuse field to true. Normally the module +cache provides this kind of reuse automatically; the -reuse flag can be +useful on systems that do not preserve the module cache. + The -x flag causes download to print the commands download executes. See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'. @@ -58,7 +74,10 @@ See https://golang.org/ref/mod#version-queries for more about version queries. `, } -var downloadJSON = cmdDownload.Flag.Bool("json", false, "") +var ( + downloadJSON = cmdDownload.Flag.Bool("json", false, "") + downloadReuse = cmdDownload.Flag.String("reuse", "", "") +) func init() { cmdDownload.Run = runDownload // break init cycle @@ -71,6 +90,7 @@ func init() { type moduleJSON struct { Path string `json:",omitempty"` Version string `json:",omitempty"` + Query string `json:",omitempty"` Error string `json:",omitempty"` Info string `json:",omitempty"` GoMod string `json:",omitempty"` @@ -78,37 +98,81 @@ type moduleJSON struct { Dir string `json:",omitempty"` Sum string `json:",omitempty"` GoModSum string `json:",omitempty"` + + Origin *codehost.Origin `json:",omitempty"` + Reuse bool `json:",omitempty"` } func runDownload(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + // Check whether modules are enabled and whether we're in a module. modload.ForceUseModules = true - if !modload.HasModRoot() && len(args) == 0 { - base.Fatalf("go mod download: no modules specified (see 'go help mod download')") - } + modload.ExplicitWriteGoMod = true haveExplicitArgs := len(args) > 0 - if !haveExplicitArgs { - args = []string{"all"} - } - if modload.HasModRoot() { - modload.LoadModFile(ctx) // to fill Target - targetAtUpgrade := modload.Target.Path + "@upgrade" - targetAtPatch := modload.Target.Path + "@patch" - for _, arg := range args { - switch arg { - case modload.Target.Path, targetAtUpgrade, targetAtPatch: - os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n") + + if modload.HasModRoot() || modload.WorkFilePath() != "" { + modload.LoadModFile(ctx) // to fill MainModules + + if haveExplicitArgs { + for _, mainModule := range modload.MainModules.Versions() { + targetAtUpgrade := mainModule.Path + "@upgrade" + targetAtPatch := mainModule.Path + "@patch" + for _, arg := range args { + switch arg { + case mainModule.Path, targetAtUpgrade, targetAtPatch: + os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n") + } + } + } + } else if modload.WorkFilePath() != "" { + // TODO(#44435): Think about what the correct query is to download the + // right set of modules. Also see code review comment at + // https://go-review.googlesource.com/c/go/+/359794/comments/ce946a80_6cf53992. + args = []string{"all"} + } else { + mainModule := modload.MainModules.Versions()[0] + modFile := modload.MainModules.ModFile(mainModule) + if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, modload.ExplicitIndirectVersionV) < 0 { + if len(modFile.Require) > 0 { + args = []string{"all"} + } + } else { + // As of Go 1.17, the go.mod file explicitly requires every module + // that provides any package imported by the main module. + // 'go mod download' is typically run before testing packages in the + // main module, so by default we shouldn't download the others + // (which are presumed irrelevant to the packages in the main module). + // See https://golang.org/issue/44435. + // + // However, we also need to load the full module graph, to ensure that + // we have downloaded enough of the module graph to run 'go list all', + // 'go mod graph', and similar commands. + _ = modload.LoadModGraph(ctx, "") + + for _, m := range modFile.Require { + args = append(args, m.Mod.Path) + } } } } + if len(args) == 0 { + if modload.HasModRoot() { + os.Stderr.WriteString("go: no module dependencies to download\n") + } else { + base.Errorf("go: no modules specified (see 'go help mod download')") + } + base.Exit() + } + downloadModule := func(m *moduleJSON) { - var err error - m.Info, err = modfetch.InfoFile(m.Path, m.Version) + _, file, err := modfetch.InfoFile(m.Path, m.Version) if err != nil { m.Error = err.Error() return } + m.Info = file m.GoMod, err = modfetch.GoModFile(m.Path, m.Version) if err != nil { m.Error = err.Error() @@ -134,19 +198,28 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { } var mods []*moduleJSON + + if *downloadReuse != "" && modload.HasModRoot() { + base.Fatalf("go mod download -reuse cannot be used inside a module") + } + type token struct{} sem := make(chan token, runtime.GOMAXPROCS(0)) - infos, infosErr := modload.ListModules(ctx, args, 0) - if !haveExplicitArgs { + infos, infosErr := modload.ListModules(ctx, args, 0, *downloadReuse) + if !haveExplicitArgs && modload.WorkFilePath() == "" { // 'go mod download' is sometimes run without arguments to pre-populate the - // module cache. It may fetch modules that aren't needed to build packages - // in the main mdoule. This is usually not intended, so don't save sums for - // downloaded modules (golang.org/issue/45332). - // TODO(golang.org/issue/45551): For now, in ListModules, save sums needed - // to load the build list (same as 1.15 behavior). In the future, report an - // error if go.mod or go.sum need to be updated after loading the build - // list. - modload.DisallowWriteGoMod() + // module cache. In modules that aren't at go 1.17 or higher, it may fetch + // modules that aren't needed to build packages in the main module. This is + // usually not intended, so don't save sums for downloaded modules + // (golang.org/issue/45332). We do still fix inconsistencies in go.mod + // though. + // + // TODO(#45551): In the future, report an error if go.mod or go.sum need to + // be updated after loading the build list. This may require setting + // the mode to "mod" or "readonly" depending on haveExplicitArgs. + if err := modload.WriteGoMod(ctx); err != nil { + base.Fatalf("go: %v", err) + } } for _, info := range infos { @@ -161,12 +234,18 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { m := &moduleJSON{ Path: info.Path, Version: info.Version, + Query: info.Query, + Reuse: info.Reuse, + Origin: info.Origin, } mods = append(mods, m) if info.Error != nil { m.Error = info.Error.Err continue } + if m.Reuse { + continue + } sem <- token{} go func() { downloadModule(m) @@ -183,7 +262,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { for _, m := range mods { b, err := json.MarshalIndent(m, "", "\t") if err != nil { - base.Fatalf("go mod download: %v", err) + base.Fatalf("go: %v", err) } os.Stdout.Write(append(b, '\n')) if m.Error != "" { @@ -193,7 +272,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { } else { for _, m := range mods { if m.Error != "" { - base.Errorf("go mod download: %v", m.Error) + base.Errorf("go: %v", m.Error) } } base.ExitIfErrors() @@ -204,15 +283,28 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { // 'go get mod@version', which may have other side effects. We print this in // some error message hints. // - // Don't save sums for 'go mod download' without arguments; see comment above. - if haveExplicitArgs { - modload.WriteGoMod(ctx) + // If we're in workspace mode, update go.work.sum with checksums for all of + // the modules we downloaded that aren't already recorded. Since a requirement + // in one module may upgrade a dependency of another, we can't be sure that + // the import graph matches the import graph of any given module in isolation, + // so we may end up needing to load packages from modules that wouldn't + // otherwise be relevant. + // + // TODO(#44435): If we adjust the set of modules downloaded in workspace mode, + // we may also need to adjust the logic for saving checksums here. + // + // Don't save sums for 'go mod download' without arguments unless we're in + // workspace mode; see comment above. + if haveExplicitArgs || modload.WorkFilePath() != "" { + if err := modload.WriteGoMod(ctx); err != nil { + base.Errorf("go: %v", err) + } } // If there was an error matching some of the requested packages, emit it now // (after we've written the checksums for the modules that were downloaded // successfully). if infosErr != nil { - base.Errorf("go mod download: %v", infosErr) + base.Errorf("go: %v", infosErr) } } diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go index bb3d5210926aef..e5182a9590adc8 100644 --- a/src/cmd/go/internal/modcmd/edit.go +++ b/src/cmd/go/internal/modcmd/edit.go @@ -171,15 +171,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { len(edits) > 0 if !anyFlags { - base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').") + base.Fatalf("go: no flags specified (see 'go help mod edit').") } if *editJSON && *editPrint { - base.Fatalf("go mod edit: cannot use both -json and -print") + base.Fatalf("go: cannot use both -json and -print") } if len(args) > 1 { - base.Fatalf("go mod edit: too many arguments") + base.Fatalf("go: too many arguments") } var gomod string if len(args) == 1 { @@ -190,7 +190,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { if *editModule != "" { if err := module.CheckImportPath(*editModule); err != nil { - base.Fatalf("go mod: invalid -module: %v", err) + base.Fatalf("go: invalid -module: %v", err) } } @@ -264,15 +264,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { func parsePathVersion(flag, arg string) (path, version string) { i := strings.Index(arg, "@") if i < 0 { - base.Fatalf("go mod: -%s=%s: need path@version", flag, arg) + base.Fatalf("go: -%s=%s: need path@version", flag, arg) } path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } if !allowedVersionArg(version) { - base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version) + base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version) } return path, version @@ -281,11 +281,11 @@ func parsePathVersion(flag, arg string) (path, version string) { // parsePath parses -flag=arg expecting arg to be path (not path@version). func parsePath(flag, arg string) (path string) { if strings.Contains(arg, "@") { - base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg) + base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg) } path = arg if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } return path } @@ -350,7 +350,7 @@ func flagRequire(arg string) { path, version := parsePathVersion("require", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddRequire(path, version); err != nil { - base.Fatalf("go mod: -require=%s: %v", arg, err) + base.Fatalf("go: -require=%s: %v", arg, err) } }) } @@ -360,7 +360,7 @@ func flagDropRequire(arg string) { path := parsePath("droprequire", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropRequire(path); err != nil { - base.Fatalf("go mod: -droprequire=%s: %v", arg, err) + base.Fatalf("go: -droprequire=%s: %v", arg, err) } }) } @@ -370,7 +370,7 @@ func flagExclude(arg string) { path, version := parsePathVersion("exclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddExclude(path, version); err != nil { - base.Fatalf("go mod: -exclude=%s: %v", arg, err) + base.Fatalf("go: -exclude=%s: %v", arg, err) } }) } @@ -380,7 +380,7 @@ func flagDropExclude(arg string) { path, version := parsePathVersion("dropexclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropExclude(path, version); err != nil { - base.Fatalf("go mod: -dropexclude=%s: %v", arg, err) + base.Fatalf("go: -dropexclude=%s: %v", arg, err) } }) } @@ -389,27 +389,27 @@ func flagDropExclude(arg string) { func flagReplace(arg string) { var i int if i = strings.Index(arg, "="); i < 0 { - base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) } old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if strings.HasPrefix(new, ">") { - base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg) + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) } oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } newPath, newVersion, err := parsePathVersionOptional("new", new, true) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } if newPath == new && !modfile.IsDirectoryPath(new) { - base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg) + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) } edits = append(edits, func(f *modfile.File) { if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } }) } @@ -418,11 +418,11 @@ func flagReplace(arg string) { func flagDropReplace(arg string) { path, version, err := parsePathVersionOptional("old", arg, true) if err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropReplace(path, version); err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } }) } @@ -431,11 +431,11 @@ func flagDropReplace(arg string) { func flagRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.AddRetract(vi, ""); err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } }) } @@ -444,11 +444,11 @@ func flagRetract(arg string) { func flagDropRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropRetract(vi); err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } }) } diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go index ac81f26dadea69..9568c657404439 100644 --- a/src/cmd/go/internal/modcmd/graph.go +++ b/src/cmd/go/internal/modcmd/graph.go @@ -45,8 +45,10 @@ func init() { } func runGraph(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if len(args) > 0 { - base.Fatalf("go mod graph: graph takes no arguments") + base.Fatalf("go: 'go mod graph' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go index 958c3066ac1108..bc4620a2a8d3f2 100644 --- a/src/cmd/go/internal/modcmd/init.go +++ b/src/cmd/go/internal/modcmd/init.go @@ -39,7 +39,7 @@ func init() { func runInit(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 1 { - base.Fatalf("go mod init: too many arguments") + base.Fatalf("go: 'go mod init' accepts at most one argument") } var modPath string if len(args) == 1 { diff --git a/src/cmd/go/internal/modcmd/mod.go b/src/cmd/go/internal/modcmd/mod.go index d72d0cacd68dda..125ba336a0edda 100644 --- a/src/cmd/go/internal/modcmd/mod.go +++ b/src/cmd/go/internal/modcmd/mod.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 modcmd implements the ``go mod'' command. +// Package modcmd implements the “go mod” command. package modcmd import ( diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go index fe25507e94f4fe..d35476eb5393d3 100644 --- a/src/cmd/go/internal/modcmd/tidy.go +++ b/src/cmd/go/internal/modcmd/tidy.go @@ -75,8 +75,8 @@ type goVersionFlag struct { v string } -func (f *goVersionFlag) String() string { return f.v } -func (f *goVersionFlag) Get() interface{} { return f.v } +func (f *goVersionFlag) String() string { return f.v } +func (f *goVersionFlag) Get() any { return f.v } func (f *goVersionFlag) Set(s string) error { if s != "" { @@ -95,7 +95,7 @@ func (f *goVersionFlag) Set(s string) error { func runTidy(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go mod tidy: no arguments allowed") + base.Fatalf("go: 'go mod tidy' accepts no arguments") } // Tidy aims to make 'go test' reproducible for any package in 'all', so we diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go index 713d5f9f3faa6a..a93c52dbb33117 100644 --- a/src/cmd/go/internal/modcmd/vendor.go +++ b/src/cmd/go/internal/modcmd/vendor.go @@ -31,7 +31,7 @@ import ( ) var cmdVendor = &base.Command{ - UsageLine: "go mod vendor [-e] [-v]", + UsageLine: "go mod vendor [-e] [-v] [-o outdir]", Short: "make vendored copy of dependencies", Long: ` Vendor resets the main module's vendor directory to include all packages @@ -44,22 +44,29 @@ modules and packages to standard error. The -e flag causes vendor to attempt to proceed despite errors encountered while loading packages. +The -o flag causes vendor to create the vendor directory at the given +path instead of "vendor". The go command can only use a vendor directory +named "vendor" within the module root directory, so this flag is +primarily useful for other tools. + See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'. `, Run: runVendor, } -var vendorE bool // if true, report errors but proceed anyway +var vendorE bool // if true, report errors but proceed anyway +var vendorO string // if set, overrides the default output directory func init() { cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "") cmdVendor.Flag.BoolVar(&vendorE, "e", false, "") + cmdVendor.Flag.StringVar(&vendorO, "o", "", "") base.AddModCommonFlags(&cmdVendor.Flag) } func runVendor(ctx context.Context, cmd *base.Command, args []string) { if len(args) != 0 { - base.Fatalf("go mod vendor: vendor takes no arguments") + base.Fatalf("go: 'go mod vendor' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot @@ -74,15 +81,23 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } _, pkgs := modload.LoadPackages(ctx, loadOpts, "all") - vdir := filepath.Join(modload.ModRoot(), "vendor") + var vdir string + switch { + case filepath.IsAbs(vendorO): + vdir = vendorO + case vendorO != "": + vdir = filepath.Join(base.Cwd(), vendorO) + default: + vdir = filepath.Join(modload.VendorDir()) + } if err := os.RemoveAll(vdir); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } modpkgs := make(map[module.Version][]string) for _, pkg := range pkgs { m := modload.PackageModule(pkg) - if m.Path == "" || m == modload.Target { + if m.Path == "" || m.Version == "" && modload.MainModules.Contains(m.Path) { continue } modpkgs[m] = append(modpkgs[m], pkg) @@ -128,7 +143,8 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } for _, m := range vendorMods { - line := moduleLine(m, modload.Replacement(m)) + replacement := modload.Replacement(m) + line := moduleLine(m, replacement) io.WriteString(w, line) goVersion := "" @@ -177,11 +193,11 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } if err := os.MkdirAll(vdir, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } @@ -206,20 +222,25 @@ func moduleLine(m, r module.Version) string { } func vendorPkg(vdir, pkg string) { - // TODO(#42504): Instead of calling modload.ImportMap then build.ImportDir, - // just call load.PackagesAndErrors. To do that, we need to add a good way - // to ignore build constraints. - realPath := modload.ImportMap(pkg) - if realPath != pkg && modload.ImportMap(realPath) != "" { + src, realPath, _ := modload.Lookup("", false, pkg) + if src == "" { + base.Errorf("internal error: no pkg for %s\n", pkg) + return + } + if realPath != pkg { + // TODO(#26904): Revisit whether this behavior still makes sense. + // This should actually be impossible today, because the import map is the + // identity function for packages outside of the standard library. + // + // Part of the purpose of the vendor directory is to allow the packages in + // the module to continue to build in GOPATH mode, and GOPATH-mode users + // won't know about replacement aliasing. How important is it to maintain + // compatibility? fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg) } copiedFiles := make(map[string]bool) dst := filepath.Join(vdir, pkg) - src := modload.PackageDir(realPath) - if src == "" { - fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath) - } copyDir(dst, src, matchPotentialSourceFile, copiedFiles) if m := modload.PackageModule(realPath); m.Path != "" { copyMetadata(m.Path, realPath, dst, src, copiedFiles) @@ -242,14 +263,14 @@ func vendorPkg(vdir, pkg string) { if err != nil { if errors.As(err, &noGoError) { return // No source files in this package are built. Skip embeds in ignored files. - } else if !errors.As(err, &multiplePackageError) { // multiplePackgeErrors are okay, but others are not. + } else if !errors.As(err, &multiplePackageError) { // multiplePackageErrors are OK, but others are not. base.Fatalf("internal error: failed to find embedded files of %s: %v\n", pkg, err) } } embedPatterns := str.StringList(bp.EmbedPatterns, bp.TestEmbedPatterns, bp.XTestEmbedPatterns) embeds, err := load.ResolveEmbed(bp.Dir, embedPatterns) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, embed := range embeds { embedDst := filepath.Join(dst, embed) @@ -260,21 +281,21 @@ func vendorPkg(vdir, pkg string) { // Copy the file as is done by copyDir below. r, err := os.Open(filepath.Join(src, embed)) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(filepath.Dir(embedDst), 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } w, err := os.Create(embedDst) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } @@ -353,7 +374,7 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { if strings.HasSuffix(info.Name(), ".go") { f, err := fsys.Open(filepath.Join(dir, info.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } defer f.Close() @@ -375,10 +396,10 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, copiedFiles map[string]bool) { files, err := os.ReadDir(src) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(dst, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, file := range files { if file.IsDir() || !file.Type().IsRegular() || !match(src, file) { @@ -387,20 +408,20 @@ func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, cop copiedFiles[file.Name()] = true r, err := os.Open(filepath.Join(src, file.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } dstPath := filepath.Join(dst, file.Name()) copiedFiles[dstPath] = true w, err := os.Create(dstPath) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go index 5a6eca32cfb706..459bf5d070060f 100644 --- a/src/cmd/go/internal/modcmd/verify.go +++ b/src/cmd/go/internal/modcmd/verify.go @@ -42,9 +42,11 @@ func init() { } func runVerify(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + if len(args) != 0 { // NOTE(rsc): Could take a module pattern. - base.Fatalf("go mod verify: verify takes no arguments") + base.Fatalf("go: verify takes no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go index 3b14b27c8c780d..8e929a00016bfc 100644 --- a/src/cmd/go/internal/modcmd/why.go +++ b/src/cmd/go/internal/modcmd/why.go @@ -12,8 +12,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/imports" "cmd/go/internal/modload" - - "golang.org/x/mod/module" ) var cmdWhy = &base.Command{ @@ -64,8 +62,10 @@ func init() { } func runWhy(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() modload.ForceUseModules = true modload.RootMode = modload.NeedRoot + modload.ExplicitWriteGoMod = true // don't write go.mod in ListModules loadOpts := modload.PackageOpts{ Tags: imports.AnyTags(), @@ -78,28 +78,28 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) { if *whyM { for _, arg := range args { if strings.Contains(arg, "@") { - base.Fatalf("go mod why: module query not allowed") + base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg) } } - mods, err := modload.ListModules(ctx, args, 0) + mods, err := modload.ListModules(ctx, args, 0, "") if err != nil { - base.Fatalf("go mod why: %v", err) + base.Fatalf("go: %v", err) } - byModule := make(map[module.Version][]string) + byModule := make(map[string][]string) _, pkgs := modload.LoadPackages(ctx, loadOpts, "all") for _, path := range pkgs { m := modload.PackageModule(path) if m.Path != "" { - byModule[m] = append(byModule[m], path) + byModule[m.Path] = append(byModule[m.Path], path) } } sep := "" for _, m := range mods { best := "" bestDepth := 1000000000 - for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] { + for _, path := range byModule[m.Path] { d := modload.WhyDepth(path) if d > 0 && d < bestDepth { best = path diff --git a/src/cmd/go/internal/modfetch/bootstrap.go b/src/cmd/go/internal/modfetch/bootstrap.go index ed694581a7c6af..e23669fb00c76b 100644 --- a/src/cmd/go/internal/modfetch/bootstrap.go +++ b/src/cmd/go/internal/modfetch/bootstrap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cmd_go_bootstrap -// +build cmd_go_bootstrap package modfetch diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go index b01b4674131e7a..c1ed18736c3941 100644 --- a/src/cmd/go/internal/modfetch/cache.go +++ b/src/cmd/go/internal/modfetch/cache.go @@ -164,7 +164,7 @@ func SideLock() (unlock func(), err error) { } // A cachingRepo is a cache around an underlying Repo, -// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip). +// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not CheckReuse or Zip). // It is also safe for simultaneous use by multiple goroutines // (so that it can be returned from Lookup multiple times). // It serializes calls to the underlying Repo. @@ -195,24 +195,32 @@ func (r *cachingRepo) repo() Repo { return r.r } +func (r *cachingRepo) CheckReuse(old *codehost.Origin) error { + return r.repo().CheckReuse(old) +} + func (r *cachingRepo) ModulePath() string { return r.path } -func (r *cachingRepo) Versions(prefix string) ([]string, error) { +func (r *cachingRepo) Versions(prefix string) (*Versions, error) { type cached struct { - list []string - err error + v *Versions + err error } - c := r.cache.Do("versions:"+prefix, func() interface{} { - list, err := r.repo().Versions(prefix) - return cached{list, err} + c := r.cache.Do("versions:"+prefix, func() any { + v, err := r.repo().Versions(prefix) + return cached{v, err} }).(cached) if c.err != nil { return nil, c.err } - return append([]string(nil), c.list...), nil + v := &Versions{ + Origin: c.v.Origin, + List: append([]string(nil), c.v.List...), + } + return v, nil } type cachedInfo struct { @@ -221,7 +229,7 @@ type cachedInfo struct { } func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { - c := r.cache.Do("stat:"+rev, func() interface{} { + c := r.cache.Do("stat:"+rev, func() any { file, info, err := readDiskStat(r.path, rev) if err == nil { return cachedInfo{info, nil} @@ -233,7 +241,7 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { // then save the information under the proper version, for future use. if info.Version != rev { file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info") - r.cache.Do("stat:"+info.Version, func() interface{} { + r.cache.Do("stat:"+info.Version, func() any { return cachedInfo{info, err} }) } @@ -245,20 +253,21 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) { return cachedInfo{info, err} }).(cachedInfo) - if c.err != nil { - return nil, c.err + info := c.info + if info != nil { + copy := *info + info = © } - info := *c.info - return &info, nil + return info, c.err } func (r *cachingRepo) Latest() (*RevInfo, error) { - c := r.cache.Do("latest:", func() interface{} { + c := r.cache.Do("latest:", func() any { info, err := r.repo().Latest() // Save info for likely future Stat call. if err == nil { - r.cache.Do("stat:"+info.Version, func() interface{} { + r.cache.Do("stat:"+info.Version, func() any { return cachedInfo{info, err} }) if file, _, err := readDiskStat(r.path, info.Version); err != nil { @@ -269,11 +278,12 @@ func (r *cachingRepo) Latest() (*RevInfo, error) { return cachedInfo{info, err} }).(cachedInfo) - if c.err != nil { - return nil, c.err + info := c.info + if info != nil { + copy := *info + info = © } - info := *c.info - return &info, nil + return info, c.err } func (r *cachingRepo) GoMod(version string) ([]byte, error) { @@ -281,7 +291,7 @@ func (r *cachingRepo) GoMod(version string) ([]byte, error) { text []byte err error } - c := r.cache.Do("gomod:"+version, func() interface{} { + c := r.cache.Do("gomod:"+version, func() any { file, text, err := readDiskGoMod(r.path, version) if err == nil { // Note: readDiskGoMod already called checkGoMod. @@ -310,31 +320,41 @@ func (r *cachingRepo) Zip(dst io.Writer, version string) error { return r.repo().Zip(dst, version) } -// InfoFile is like Lookup(path).Stat(version) but returns the name of the file +// InfoFile is like Lookup(path).Stat(version) but also returns the name of the file // containing the cached information. -func InfoFile(path, version string) (string, error) { +func InfoFile(path, version string) (*RevInfo, string, error) { if !semver.IsValid(version) { - return "", fmt.Errorf("invalid version %q", version) + return nil, "", fmt.Errorf("invalid version %q", version) } - if file, _, err := readDiskStat(path, version); err == nil { - return file, nil + if file, info, err := readDiskStat(path, version); err == nil { + return info, file, nil } + var info *RevInfo + var err2info map[error]*RevInfo err := TryProxies(func(proxy string) error { - _, err := Lookup(proxy, path).Stat(version) + i, err := Lookup(proxy, path).Stat(version) + if err == nil { + info = i + } else { + if err2info == nil { + err2info = make(map[error]*RevInfo) + } + err2info[err] = info + } return err }) if err != nil { - return "", err + return err2info[err], "", err } // Stat should have populated the disk cache for us. file, err := CachePath(module.Version{Path: path, Version: version}, "info") if err != nil { - return "", err + return nil, "", err } - return file, nil + return info, file, nil } // GoMod is like Lookup(path).GoMod(rev) but avoids the @@ -561,6 +581,26 @@ func writeDiskStat(file string, info *RevInfo) error { if file == "" { return nil } + + if info.Origin != nil { + // Clean the origin information, which might have too many + // validation criteria, for example if we are saving the result of + // m@master as m@pseudo-version. + clean := *info + info = &clean + o := *info.Origin + info.Origin = &o + + // Tags never matter if you are starting with a semver version, + // as we would be when finding this cache entry. + o.TagSum = "" + o.TagPrefix = "" + // Ref doesn't matter if you have a pseudoversion. + if module.IsPseudoVersion(info.Version) { + o.Ref = "" + } + } + js, err := json.Marshal(info) if err != nil { return err @@ -642,7 +682,7 @@ func rewriteVersionList(dir string) (err error) { // Lock listfile when writing to it to try to avoid corruption to the file. // Under rare circumstances, for instance, if the system loses power in the // middle of a write it is possible for corrupt data to be written. This is - // not a problem for the go command itself, but may be an issue if the the + // not a problem for the go command itself, but may be an issue if the // cache is being served by a GOPROXY HTTP server. This will be corrected // the next time a new version of the module is fetched and the file is rewritten. // TODO(matloob): golang.org/issue/43313 covers adding a go mod verify @@ -720,7 +760,7 @@ func checkCacheDir() error { if cfg.GOMODCACHE == "" { // modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE // is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen. - return fmt.Errorf("internal error: cfg.GOMODCACHE not set") + return fmt.Errorf("module cache not found: neither GOMODCACHE nor GOPATH is set") } if !filepath.IsAbs(cfg.GOMODCACHE) { return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q.\n", cfg.GOMODCACHE) diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go index 378fbae34f9530..855b6946ca4e42 100644 --- a/src/cmd/go/internal/modfetch/codehost/codehost.go +++ b/src/cmd/go/internal/modfetch/codehost/codehost.go @@ -10,10 +10,10 @@ import ( "bytes" "crypto/sha256" "fmt" - exec "internal/execabs" "io" "io/fs" "os" + "os/exec" "path/filepath" "strings" "sync" @@ -22,6 +22,9 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/lockedfile" "cmd/go/internal/str" + + "golang.org/x/mod/module" + "golang.org/x/mod/semver" ) // Downloaded size limits. @@ -34,10 +37,19 @@ const ( // A Repo represents a code hosting source. // Typical implementations include local version control repositories, // remote version control servers, and code hosting sites. -// A Repo must be safe for simultaneous use by multiple goroutines. +// +// A Repo must be safe for simultaneous use by multiple goroutines, +// and callers must not modify returned values, which may be cached and shared. type Repo interface { + // CheckReuse checks whether the old origin information + // remains up to date. If so, whatever cached object it was + // taken from can be reused. + // The subdir gives subdirectory name where the module root is expected to be found, + // "" for the root or "sub/dir" for a subdirectory (no trailing slash). + CheckReuse(old *Origin, subdir string) error + // List lists all tags with the given prefix. - Tags(prefix string) (tags []string, err error) + Tags(prefix string) (*Tags, error) // Stat returns information about the revision rev. // A revision can be any identifier known to the underlying service: @@ -55,21 +67,6 @@ type Repo interface { // os.IsNotExist(err) returns true. ReadFile(rev, file string, maxSize int64) (data []byte, err error) - // ReadFileRevs reads a single file at multiple versions. - // It should refuse to read more than maxSize bytes. - // The result is a map from each requested rev strings - // to the associated FileRev. The map must have a non-nil - // entry for every requested rev (unless ReadFileRevs returned an error). - // A file simply being missing or even corrupted in revs[i] - // should be reported only in files[revs[i]].Err, not in the error result - // from ReadFileRevs. - // The overall call should return an error (and no map) only - // in the case of a problem with obtaining the data, such as - // a network failure. - // Implementations may assume that revs only contain tags, - // not direct commit hashes. - ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error) - // ReadZip downloads a zip file for the subdir subdirectory // of the given revision to a new file in a given temporary directory. // It should refuse to read more than maxSize bytes. @@ -80,7 +77,7 @@ type Repo interface { // RecentTag returns the most recent tag on rev or one of its predecessors // with the given prefix. allowed may be used to filter out unwanted versions. - RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) + RecentTag(rev, prefix string, allowed func(tag string) bool) (tag string, err error) // DescendsFrom reports whether rev or any of its ancestors has the given tag. // @@ -89,8 +86,88 @@ type Repo interface { DescendsFrom(rev, tag string) (bool, error) } -// A Rev describes a single revision in a source code repository. +// An Origin describes the provenance of a given repo method result. +// It can be passed to CheckReuse (usually in a different go command invocation) +// to see whether the result remains up-to-date. +type Origin struct { + VCS string `json:",omitempty"` // "git" etc + URL string `json:",omitempty"` // URL of repository + Subdir string `json:",omitempty"` // subdirectory in repo + + // If TagSum is non-empty, then the resolution of this module version + // depends on the set of tags present in the repo, specifically the tags + // of the form TagPrefix + a valid semver version. + // If the matching repo tags and their commit hashes still hash to TagSum, + // the Origin is still valid (at least as far as the tags are concerned). + // The exact checksum is up to the Repo implementation; see (*gitRepo).Tags. + TagPrefix string `json:",omitempty"` + TagSum string `json:",omitempty"` + + // If Ref is non-empty, then the resolution of this module version + // depends on Ref resolving to the revision identified by Hash. + // If Ref still resolves to Hash, the Origin is still valid (at least as far as Ref is concerned). + // For Git, the Ref is a full ref like "refs/heads/main" or "refs/tags/v1.2.3", + // and the Hash is the Git object hash the ref maps to. + // Other VCS might choose differently, but the idea is that Ref is the name + // with a mutable meaning while Hash is a name with an immutable meaning. + Ref string `json:",omitempty"` + Hash string `json:",omitempty"` + + // If RepoSum is non-empty, then the resolution of this module version + // failed due to the repo being available but the version not being present. + // This depends on the entire state of the repo, which RepoSum summarizes. + // For Git, this is a hash of all the refs and their hashes. + RepoSum string `json:",omitempty"` +} + +// Checkable reports whether the Origin contains anything that can be checked. +// If not, the Origin is purely informational and should fail a CheckReuse call. +func (o *Origin) Checkable() bool { + return o.TagSum != "" || o.Ref != "" || o.Hash != "" || o.RepoSum != "" +} + +// ClearCheckable clears the Origin enough to make Checkable return false. +func (o *Origin) ClearCheckable() { + o.TagSum = "" + o.TagPrefix = "" + o.Ref = "" + o.Hash = "" + o.RepoSum = "" +} + +// A Tags describes the available tags in a code repository. +type Tags struct { + Origin *Origin + List []Tag +} + +// A Tag describes a single tag in a code repository. +type Tag struct { + Name string + Hash string // content hash identifying tag's content, if available +} + +// isOriginTag reports whether tag should be preserved +// in the Tags method's Origin calculation. +// We can safely ignore tags that are not look like pseudo-versions, +// because ../coderepo.go's (*codeRepo).Versions ignores them too. +// We can also ignore non-semver tags, but we have to include semver +// tags with extra suffixes, because the pseudo-version base finder uses them. +func isOriginTag(tag string) bool { + // modfetch.(*codeRepo).Versions uses Canonical == tag, + // but pseudo-version calculation has a weaker condition that + // the canonical is a prefix of the tag. + // Include those too, so that if any new one appears, we'll invalidate the cache entry. + // This will lead to spurious invalidation of version list results, + // but tags of this form being created should be fairly rare + // (and invalidate pseudo-version results anyway). + c := semver.Canonical(tag) + return c != "" && strings.HasPrefix(tag, c) && !module.IsPseudoVersion(tag) +} + +// A RevInfo describes a single revision in a source code repository. type RevInfo struct { + Origin *Origin Name string // complete ID in underlying repository Short string // shortened ID, for use in pseudo-version Version string // version used in lookup @@ -98,13 +175,6 @@ type RevInfo struct { Tags []string // known tags for commit } -// A FileRev describes the result of reading a file at a given revision. -type FileRev struct { - Rev string // requested revision - Data []byte // file data - Err error // error if any; os.IsNotExist(Err)==true if rev exists but file does not exist in that rev -} - // UnknownRevisionError is an error equivalent to fs.ErrNotExist, but for a // revision rather than a file. type UnknownRevisionError struct { @@ -131,6 +201,19 @@ func (noCommitsError) Is(err error) bool { return err == fs.ErrNotExist } +// ErrUnsupported indicates that a requested operation cannot be performed, +// because it is unsupported. This error indicates that there is no alternative +// way to perform the operation. +// +// TODO(#41198): Remove this declaration and use errors.ErrUnsupported instead. +var ErrUnsupported = unsupportedOperationError{} + +type unsupportedOperationError struct{} + +func (unsupportedOperationError) Error() string { + return "unsupported operation" +} + // AllHex reports whether the revision rev is entirely lower-case hexadecimal digits. func AllHex(rev string) bool { for i := 0; i < len(rev); i++ { @@ -179,7 +262,7 @@ func WorkDir(typ, name string) (dir, lockfile string, err error) { lockfile = dir + ".lock" if cfg.BuildX { - fmt.Fprintf(os.Stderr, "# lock %s", lockfile) + fmt.Fprintf(os.Stderr, "# lock %s\n", lockfile) } unlock, err := lockedfile.MutexAt(lockfile).Lock() @@ -243,7 +326,7 @@ var dirLock sync.Map // It returns the standard output and, for a non-zero exit, // a *RunError indicating the command, exit status, and standard error. // Standard error is unavailable for commands that exit successfully. -func Run(dir string, cmdline ...interface{}) ([]byte, error) { +func Run(dir string, cmdline ...any) ([]byte, error) { return RunWithStdin(dir, nil, cmdline...) } @@ -251,7 +334,7 @@ func Run(dir string, cmdline ...interface{}) ([]byte, error) { // See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html. var bashQuoter = strings.NewReplacer(`"`, `\"`, `$`, `\$`, "`", "\\`", `\`, `\\`) -func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) { +func RunWithStdin(dir string, stdin io.Reader, cmdline ...any) ([]byte, error) { if dir != "" { muIface, ok := dirLock.Load(dir) if !ok { diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go index 4d4964edf447eb..ac2dc2348eeb22 100644 --- a/src/cmd/go/internal/modfetch/codehost/git.go +++ b/src/cmd/go/internal/modfetch/codehost/git.go @@ -6,13 +6,15 @@ package codehost import ( "bytes" + "crypto/sha256" + "encoding/base64" "errors" "fmt" - exec "internal/execabs" "io" "io/fs" "net/url" "os" + "os/exec" "path/filepath" "sort" "strconv" @@ -56,7 +58,7 @@ func newGitRepoCached(remote string, localOK bool) (Repo, error) { err error } - c := gitRepoCache.Do(key{remote, localOK}, func() interface{} { + c := gitRepoCache.Do(key{remote, localOK}, func() any { repo, err := newGitRepo(remote, localOK) return cached{repo, err} }).(cached) @@ -169,60 +171,122 @@ func (r *gitRepo) loadLocalTags() { } } -// loadRefs loads heads and tags references from the remote into the map r.refs. -// Should only be called as r.refsOnce.Do(r.loadRefs). -func (r *gitRepo) loadRefs() { - // The git protocol sends all known refs and ls-remote filters them on the client side, - // so we might as well record both heads and tags in one shot. - // Most of the time we only care about tags but sometimes we care about heads too. - out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote) - if gitErr != nil { - if rerr, ok := gitErr.(*RunError); ok { - if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) { - rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information." - } - } +func (r *gitRepo) CheckReuse(old *Origin, subdir string) error { + if old == nil { + return fmt.Errorf("missing origin") + } + if old.VCS != "git" || old.URL != r.remoteURL { + return fmt.Errorf("origin moved from %v %q to %v %q", old.VCS, old.URL, "git", r.remoteURL) + } + if old.Subdir != subdir { + return fmt.Errorf("origin moved from %v %q %q to %v %q %q", old.VCS, old.URL, old.Subdir, "git", r.remoteURL, subdir) + } - // If the remote URL doesn't exist at all, ideally we should treat the whole - // repository as nonexistent by wrapping the error in a notExistError. - // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL - // ourselves and see what code it serves. - if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") { - if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) { - gitErr = notExistError{gitErr} - } - } + // Note: Can have Hash with no Ref and no TagSum and no RepoSum, + // meaning the Hash simply has to remain in the repo. + // In that case we assume it does in the absence of any real way to check. + // But if neither Hash nor TagSum is present, we have nothing to check, + // which we take to mean we didn't record enough information to be sure. + if old.Hash == "" && old.TagSum == "" && old.RepoSum == "" { + return fmt.Errorf("non-specific origin") + } - r.refsErr = gitErr - return + r.loadRefs() + if r.refsErr != nil { + return r.refsErr } - r.refs = make(map[string]string) - for _, line := range strings.Split(string(out), "\n") { - f := strings.Fields(line) - if len(f) != 2 { - continue + if old.Ref != "" { + hash, ok := r.refs[old.Ref] + if !ok { + return fmt.Errorf("ref %q deleted", old.Ref) } - if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") { - r.refs[f[1]] = f[0] + if hash != old.Hash { + return fmt.Errorf("ref %q moved from %s to %s", old.Ref, old.Hash, hash) } } - for ref, hash := range r.refs { - if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag - r.refs[strings.TrimSuffix(ref, "^{}")] = hash - delete(r.refs, ref) + if old.TagSum != "" { + tags, err := r.Tags(old.TagPrefix) + if err != nil { + return err + } + if tags.Origin.TagSum != old.TagSum { + return fmt.Errorf("tags changed") + } + } + if old.RepoSum != "" { + if r.repoSum(r.refs) != old.RepoSum { + return fmt.Errorf("refs changed") } } + return nil } -func (r *gitRepo) Tags(prefix string) ([]string, error) { - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr +// loadRefs loads heads and tags references from the remote into the map r.refs. +// The result is cached in memory. +func (r *gitRepo) loadRefs() (map[string]string, error) { + r.refsOnce.Do(func() { + // The git protocol sends all known refs and ls-remote filters them on the client side, + // so we might as well record both heads and tags in one shot. + // Most of the time we only care about tags but sometimes we care about heads too. + out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote) + if gitErr != nil { + if rerr, ok := gitErr.(*RunError); ok { + if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) { + rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information." + } + } + + // If the remote URL doesn't exist at all, ideally we should treat the whole + // repository as nonexistent by wrapping the error in a notExistError. + // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL + // ourselves and see what code it serves. + if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") { + if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) { + gitErr = notExistError{gitErr} + } + } + + r.refsErr = gitErr + return + } + + refs := make(map[string]string) + for _, line := range strings.Split(string(out), "\n") { + f := strings.Fields(line) + if len(f) != 2 { + continue + } + if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") { + refs[f[1]] = f[0] + } + } + for ref, hash := range refs { + if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag + refs[strings.TrimSuffix(ref, "^{}")] = hash + delete(refs, ref) + } + } + r.refs = refs + }) + return r.refs, r.refsErr +} + +func (r *gitRepo) Tags(prefix string) (*Tags, error) { + refs, err := r.loadRefs() + if err != nil { + return nil, err } - tags := []string{} - for ref := range r.refs { + tags := &Tags{ + Origin: &Origin{ + VCS: "git", + URL: r.remoteURL, + TagPrefix: prefix, + }, + List: []Tag{}, + } + for ref, hash := range refs { if !strings.HasPrefix(ref, "refs/tags/") { continue } @@ -230,21 +294,76 @@ func (r *gitRepo) Tags(prefix string) ([]string, error) { if !strings.HasPrefix(tag, prefix) { continue } - tags = append(tags, tag) + tags.List = append(tags.List, Tag{tag, hash}) + } + sort.Slice(tags.List, func(i, j int) bool { + return tags.List[i].Name < tags.List[j].Name + }) + + dir := prefix[:strings.LastIndex(prefix, "/")+1] + h := sha256.New() + for _, tag := range tags.List { + if isOriginTag(strings.TrimPrefix(tag.Name, dir)) { + fmt.Fprintf(h, "%q %s\n", tag.Name, tag.Hash) + } } - sort.Strings(tags) + tags.Origin.TagSum = "t1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)) return tags, nil } +// repoSum returns a checksum of the entire repo state, +// which can be checked (as Origin.RepoSum) to cache +// the absence of a specific module version. +// The caller must supply refs, the result of a successful r.loadRefs. +func (r *gitRepo) repoSum(refs map[string]string) string { + var list []string + for ref := range refs { + list = append(list, ref) + } + sort.Strings(list) + h := sha256.New() + for _, ref := range list { + fmt.Fprintf(h, "%q %s\n", ref, refs[ref]) + } + return "r1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +// unknownRevisionInfo returns a RevInfo containing an Origin containing a RepoSum of refs, +// for use when returning an UnknownRevisionError. +func (r *gitRepo) unknownRevisionInfo(refs map[string]string) *RevInfo { + return &RevInfo{ + Origin: &Origin{ + VCS: "git", + URL: r.remoteURL, + RepoSum: r.repoSum(refs), + }, + } +} + func (r *gitRepo) Latest() (*RevInfo, error) { - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr + refs, err := r.loadRefs() + if err != nil { + return nil, err } - if r.refs["HEAD"] == "" { + if refs["HEAD"] == "" { return nil, ErrNoCommits } - return r.Stat(r.refs["HEAD"]) + statInfo, err := r.Stat(refs["HEAD"]) + if err != nil { + return nil, err + } + + // Stat may return cached info, so make a copy to modify here. + info := new(RevInfo) + *info = *statInfo + info.Origin = new(Origin) + if statInfo.Origin != nil { + *info.Origin = *statInfo.Origin + } + info.Origin.Ref = "HEAD" + info.Origin.Hash = refs["HEAD"] + + return info, nil } // findRef finds some ref name for the given hash, @@ -252,8 +371,11 @@ func (r *gitRepo) Latest() (*RevInfo, error) { // There may be multiple ref names for a given hash, // in which case this returns some name - it doesn't matter which. func (r *gitRepo) findRef(hash string) (ref string, ok bool) { - r.refsOnce.Do(r.loadRefs) - for ref, h := range r.refs { + refs, err := r.loadRefs() + if err != nil { + return "", false + } + for ref, h := range refs { if h == hash { return ref, true } @@ -271,7 +393,7 @@ const minHashDigits = 7 // stat stats the given rev in the local repository, // or else it fetches more info from the remote repository and tries again. -func (r *gitRepo) stat(rev string) (*RevInfo, error) { +func (r *gitRepo) stat(rev string) (info *RevInfo, err error) { if r.local { return r.statLocal(rev, rev) } @@ -295,29 +417,32 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) { // Maybe rev is the name of a tag or branch on the remote server. // Or maybe it's the prefix of a hash of a named ref. // Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash. - r.refsOnce.Do(r.loadRefs) + refs, err := r.loadRefs() + if err != nil { + return nil, err + } // loadRefs may return an error if git fails, for example segfaults, or // could not load a private repo, but defer checking to the else block // below, in case we already have the rev in question in the local cache. var ref, hash string - if r.refs["refs/tags/"+rev] != "" { + if refs["refs/tags/"+rev] != "" { ref = "refs/tags/" + rev - hash = r.refs[ref] + hash = refs[ref] // Keep rev as is: tags are assumed not to change meaning. - } else if r.refs["refs/heads/"+rev] != "" { + } else if refs["refs/heads/"+rev] != "" { ref = "refs/heads/" + rev - hash = r.refs[ref] + hash = refs[ref] rev = hash // Replace rev, because meaning of refs/heads/foo can change. - } else if rev == "HEAD" && r.refs["HEAD"] != "" { + } else if rev == "HEAD" && refs["HEAD"] != "" { ref = "HEAD" - hash = r.refs[ref] + hash = refs[ref] rev = hash // Replace rev, because meaning of HEAD can change. } else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) { // At the least, we have a hash prefix we can look up after the fetch below. // Maybe we can map it to a full hash using the known refs. prefix := rev // Check whether rev is prefix of known ref hash. - for k, h := range r.refs { + for k, h := range refs { if strings.HasPrefix(h, prefix) { if hash != "" && hash != h { // Hash is an ambiguous hash prefix. @@ -335,12 +460,19 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) { hash = rev } } else { - if r.refsErr != nil { - return nil, r.refsErr - } - return nil, &UnknownRevisionError{Rev: rev} + return r.unknownRevisionInfo(refs), &UnknownRevisionError{Rev: rev} } + defer func() { + if info != nil { + info.Origin.Hash = info.Name + // There's a ref = hash below; don't write that hash down as Origin.Ref. + if ref != info.Origin.Hash { + info.Origin.Ref = ref + } + } + }() + // Protect r.fetchLevel and the "fetch more and more" sequence. unlock, err := r.mu.Lock() if err != nil { @@ -437,12 +569,17 @@ func (r *gitRepo) fetchRefsLocked() error { return nil } -// statLocal returns a RevInfo describing rev in the local git repository. +// statLocal returns a new RevInfo describing rev in the local git repository. // It uses version as info.Version. func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) { - out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "-n1", "--format=format:%H %ct %D", rev, "--") + out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "--no-decorate", "-n1", "--format=format:%H %ct %D", rev, "--") if err != nil { - return nil, &UnknownRevisionError{Rev: rev} + // Return info with Origin.RepoSum if possible to allow caching of negative lookup. + var info *RevInfo + if refs, err := r.loadRefs(); err == nil { + info = r.unknownRevisionInfo(refs) + } + return info, &UnknownRevisionError{Rev: rev} } f := strings.Fields(string(out)) if len(f) < 2 { @@ -458,11 +595,19 @@ func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) { } info := &RevInfo{ + Origin: &Origin{ + VCS: "git", + URL: r.remoteURL, + Hash: hash, + }, Name: hash, Short: ShortenSHA1(hash), Time: time.Unix(t, 0).UTC(), Version: hash, } + if !strings.HasPrefix(hash, rev) { + info.Origin.Ref = rev + } // Add tags. Output looks like: // ede458df7cd0fdca520df19a33158086a8a68e81 1523994202 HEAD -> master, tag: v1.2.4-annotated, tag: v1.2.3, origin/master, origin/HEAD @@ -496,7 +641,7 @@ func (r *gitRepo) Stat(rev string) (*RevInfo, error) { info *RevInfo err error } - c := r.statCache.Do(rev, func() interface{} { + c := r.statCache.Do(rev, func() any { info, err := r.stat(rev) return cached{info, err} }).(cached) @@ -516,141 +661,7 @@ func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { return out, nil } -func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { - // Create space to hold results. - files := make(map[string]*FileRev) - for _, rev := range revs { - f := &FileRev{Rev: rev} - files[rev] = f - } - - // Collect locally-known revs. - need, err := r.readFileRevs(revs, file, files) - if err != nil { - return nil, err - } - if len(need) == 0 { - return files, nil - } - - // Build list of known remote refs that might help. - var redo []string - r.refsOnce.Do(r.loadRefs) - if r.refsErr != nil { - return nil, r.refsErr - } - for _, tag := range need { - if r.refs["refs/tags/"+tag] != "" { - redo = append(redo, tag) - } - } - if len(redo) == 0 { - return files, nil - } - - // Protect r.fetchLevel and the "fetch more and more" sequence. - // See stat method above. - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() - - if err := r.fetchRefsLocked(); err != nil { - return nil, err - } - - if _, err := r.readFileRevs(redo, file, files); err != nil { - return nil, err - } - - return files, nil -} - -func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*FileRev) (missing []string, err error) { - var stdin bytes.Buffer - for _, tag := range tags { - fmt.Fprintf(&stdin, "refs/tags/%s\n", tag) - fmt.Fprintf(&stdin, "refs/tags/%s:%s\n", tag, file) - } - - data, err := RunWithStdin(r.dir, &stdin, "git", "cat-file", "--batch") - if err != nil { - return nil, err - } - - next := func() (typ string, body []byte, ok bool) { - var line string - i := bytes.IndexByte(data, '\n') - if i < 0 { - return "", nil, false - } - line, data = string(bytes.TrimSpace(data[:i])), data[i+1:] - if strings.HasSuffix(line, " missing") { - return "missing", nil, true - } - f := strings.Fields(line) - if len(f) != 3 { - return "", nil, false - } - n, err := strconv.Atoi(f[2]) - if err != nil || n > len(data) { - return "", nil, false - } - body, data = data[:n], data[n:] - if len(data) > 0 && data[0] == '\r' { - data = data[1:] - } - if len(data) > 0 && data[0] == '\n' { - data = data[1:] - } - return f[1], body, true - } - - badGit := func() ([]string, error) { - return nil, fmt.Errorf("malformed output from git cat-file --batch") - } - - for _, tag := range tags { - commitType, _, ok := next() - if !ok { - return badGit() - } - fileType, fileData, ok := next() - if !ok { - return badGit() - } - f := fileMap[tag] - f.Data = nil - f.Err = nil - switch commitType { - default: - f.Err = fmt.Errorf("unexpected non-commit type %q for rev %s", commitType, tag) - - case "missing": - // Note: f.Err must not satisfy os.IsNotExist. That's reserved for the file not existing in a valid commit. - f.Err = fmt.Errorf("no such rev %s", tag) - missing = append(missing, tag) - - case "tag", "commit": - switch fileType { - default: - f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fmt.Errorf("unexpected non-blob type %q", fileType)} - case "missing": - f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fs.ErrNotExist} - case "blob": - f.Data = fileData - } - } - } - if len(bytes.TrimSpace(data)) != 0 { - return badGit() - } - - return missing, nil -} - -func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) { +func (r *gitRepo) RecentTag(rev, prefix string, allowed func(tag string) bool) (tag string, err error) { info, err := r.Stat(rev) if err != nil { return "", err @@ -680,15 +691,11 @@ func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag if !strings.HasPrefix(line, prefix) { continue } - - semtag := line[len(prefix):] - // Consider only tags that are valid and complete (not just major.minor prefixes). - // NOTE: Do not replace the call to semver.Compare with semver.Max. - // We want to return the actual tag, not a canonicalized version of it, - // and semver.Max currently canonicalizes (see golang.org/issue/32700). - if c := semver.Canonical(semtag); c == "" || !strings.HasPrefix(semtag, c) || !allowed(semtag) { + if !allowed(line) { continue } + + semtag := line[len(prefix):] if semver.Compare(semtag, highest) > 0 { highest = semtag } @@ -711,7 +718,7 @@ func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag if err != nil { return "", err } - if len(tags) == 0 { + if len(tags.List) == 0 { return "", nil } @@ -765,7 +772,7 @@ func (r *gitRepo) DescendsFrom(rev, tag string) (bool, error) { if err != nil { return false, err } - if len(tags) == 0 { + if len(tags.List) == 0 { return false, nil } diff --git a/src/cmd/go/internal/modfetch/codehost/git_test.go b/src/cmd/go/internal/modfetch/codehost/git_test.go index a684fa1a9bba9e..6a4212fc5ae212 100644 --- a/src/cmd/go/internal/modfetch/codehost/git_test.go +++ b/src/cmd/go/internal/modfetch/codehost/git_test.go @@ -43,7 +43,7 @@ var altRepos = []string{ // For now, at least the hgrepo1 tests check the general vcs.go logic. // localGitRepo is like gitrepo1 but allows archive access. -var localGitRepo string +var localGitRepo, localGitURL string func testMain(m *testing.M) int { dir, err := os.MkdirTemp("", "gitrepo-test-") @@ -65,6 +65,15 @@ func testMain(m *testing.M) int { if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil { log.Fatal(err) } + + // Convert absolute path to file URL. LocalGitRepo will not accept + // Windows absolute paths because they look like a host:path remote. + // TODO(golang.org/issue/32456): use url.FromFilePath when implemented. + if strings.HasPrefix(localGitRepo, "/") { + localGitURL = "file://" + localGitRepo + } else { + localGitURL = "file:///" + filepath.ToSlash(localGitRepo) + } } } @@ -73,17 +82,8 @@ func testMain(m *testing.M) int { func testRepo(t *testing.T, remote string) (Repo, error) { if remote == "localGitRepo" { - // Convert absolute path to file URL. LocalGitRepo will not accept - // Windows absolute paths because they look like a host:path remote. - // TODO(golang.org/issue/32456): use url.FromFilePath when implemented. - var url string - if strings.HasPrefix(localGitRepo, "/") { - url = "file://" + localGitRepo - } else { - url = "file:///" + filepath.ToSlash(localGitRepo) - } testenv.MustHaveExecPath(t, "git") - return LocalGitRepo(url) + return LocalGitRepo(localGitURL) } vcs := "git" for _, k := range []string{"hg"} { @@ -98,13 +98,28 @@ func testRepo(t *testing.T, remote string) (Repo, error) { var tagsTests = []struct { repo string prefix string - tags []string + tags []Tag }{ - {gitrepo1, "xxx", []string{}}, - {gitrepo1, "", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, - {gitrepo1, "v", []string{"v1.2.3", "v1.2.4-annotated", "v2.0.1", "v2.0.2", "v2.3"}}, - {gitrepo1, "v1", []string{"v1.2.3", "v1.2.4-annotated"}}, - {gitrepo1, "2", []string{}}, + {gitrepo1, "xxx", []Tag{}}, + {gitrepo1, "", []Tag{ + {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + {"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"}, + {"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"}, + {"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"}, + }}, + {gitrepo1, "v", []Tag{ + {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + {"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"}, + {"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"}, + {"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"}, + }}, + {gitrepo1, "v1", []Tag{ + {"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + {"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"}, + }}, + {gitrepo1, "2", []Tag{}}, } func TestTags(t *testing.T) { @@ -121,13 +136,24 @@ func TestTags(t *testing.T) { if err != nil { t.Fatal(err) } - if !reflect.DeepEqual(tags, tt.tags) { - t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags) + if tags == nil || !reflect.DeepEqual(tags.List, tt.tags) { + t.Errorf("Tags(%q): incorrect tags\nhave %v\nwant %v", tt.prefix, tags, tt.tags) } } t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) if tt.repo == gitrepo1 { + // Clear hashes. + clearTags := []Tag{} + for _, tag := range tt.tags { + clearTags = append(clearTags, Tag{tag.Name, ""}) + } + tags := tt.tags for _, tt.repo = range altRepos { + if strings.Contains(tt.repo, "Git") { + tt.tags = tags + } else { + tt.tags = clearTags + } t.Run(path.Base(tt.repo)+"/"+tt.prefix, f) } } @@ -141,6 +167,12 @@ var latestTests = []struct { { gitrepo1, &RevInfo{ + Origin: &Origin{ + VCS: "git", + URL: "https://vcs-test.golang.org/git/gitrepo1", + Ref: "HEAD", + Hash: "ede458df7cd0fdca520df19a33158086a8a68e81", + }, Name: "ede458df7cd0fdca520df19a33158086a8a68e81", Short: "ede458df7cd0", Version: "ede458df7cd0fdca520df19a33158086a8a68e81", @@ -151,6 +183,11 @@ var latestTests = []struct { { hgrepo1, &RevInfo{ + Origin: &Origin{ + VCS: "hg", + URL: "https://vcs-test.golang.org/hg/hgrepo1", + Hash: "18518c07eb8ed5c80221e997e518cccaa8c0c287", + }, Name: "18518c07eb8ed5c80221e997e518cccaa8c0c287", Short: "18518c07eb8e", Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287", @@ -174,12 +211,17 @@ func TestLatest(t *testing.T) { t.Fatal(err) } if !reflect.DeepEqual(info, tt.info) { - t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) + t.Errorf("Latest: incorrect info\nhave %+v (origin %+v)\nwant %+v (origin %+v)", info, info.Origin, tt.info, tt.info.Origin) } } t.Run(path.Base(tt.repo), f) if tt.repo == gitrepo1 { tt.repo = "localGitRepo" + info := *tt.info + tt.info = &info + o := *info.Origin + info.Origin = &o + o.URL = localGitURL t.Run(path.Base(tt.repo), f) } } @@ -590,11 +632,12 @@ func TestStat(t *testing.T) { if !strings.Contains(err.Error(), tt.err) { t.Fatalf("Stat: wrong error %q, want %q", err, tt.err) } - if info != nil { - t.Errorf("Stat: non-nil info with error %q", err) + if info != nil && info.Origin == nil { + t.Errorf("Stat: non-nil info with nil Origin with error %q", err) } return } + info.Origin = nil // TestLatest and ../../../testdata/script/reuse_git.txt test Origin well enough if !reflect.DeepEqual(info, tt.info) { t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) } diff --git a/src/cmd/go/internal/modfetch/codehost/shell.go b/src/cmd/go/internal/modfetch/codehost/shell.go index 0e9f38196676b1..eaa01950b95ef3 100644 --- a/src/cmd/go/internal/modfetch/codehost/shell.go +++ b/src/cmd/go/internal/modfetch/codehost/shell.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Interactive debugging shell for codehost.Repo implementations. diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go index c2cca084e3077a..4d0e863182b3fc 100644 --- a/src/cmd/go/internal/modfetch/codehost/vcs.go +++ b/src/cmd/go/internal/modfetch/codehost/vcs.go @@ -38,7 +38,9 @@ type VCSError struct { func (e *VCSError) Error() string { return e.Err.Error() } -func vcsErrorf(format string, a ...interface{}) error { +func (e *VCSError) Unwrap() error { return e.Err } + +func vcsErrorf(format string, a ...any) error { return &VCSError{Err: fmt.Errorf(format, a...)} } @@ -51,7 +53,7 @@ func NewRepo(vcs, remote string) (Repo, error) { repo Repo err error } - c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} { + c := vcsRepoCache.Do(key{vcs, remote}, func() any { repo, err := newVCSRepo(vcs, remote) if err != nil { err = &VCSError{err} @@ -290,7 +292,11 @@ func (r *vcsRepo) loadBranches() { } } -func (r *vcsRepo) Tags(prefix string) ([]string, error) { +func (r *vcsRepo) CheckReuse(old *Origin, subdir string) error { + return fmt.Errorf("vcs %s: CheckReuse: %w", r.cmd.vcs, ErrUnsupported) +} + +func (r *vcsRepo) Tags(prefix string) (*Tags, error) { unlock, err := r.mu.Lock() if err != nil { return nil, err @@ -298,14 +304,24 @@ func (r *vcsRepo) Tags(prefix string) ([]string, error) { defer unlock() r.tagsOnce.Do(r.loadTags) - - tags := []string{} + tags := &Tags{ + // None of the other VCS provide a reasonable way to compute TagSum + // without downloading the whole repo, so we only include VCS and URL + // in the Origin. + Origin: &Origin{ + VCS: r.cmd.vcs, + URL: r.remote, + }, + List: []Tag{}, + } for tag := range r.tags { if strings.HasPrefix(tag, prefix) { - tags = append(tags, tag) + tags.List = append(tags.List, Tag{tag, ""}) } } - sort.Strings(tags) + sort.Slice(tags.List, func(i, j int) bool { + return tags.List[i].Name < tags.List[j].Name + }) return tags, nil } @@ -352,7 +368,16 @@ func (r *vcsRepo) statLocal(rev string) (*RevInfo, error) { if err != nil { return nil, &UnknownRevisionError{Rev: rev} } - return r.cmd.parseStat(rev, string(out)) + info, err := r.cmd.parseStat(rev, string(out)) + if err != nil { + return nil, err + } + if info.Origin == nil { + info.Origin = new(Origin) + } + info.Origin.VCS = r.cmd.vcs + info.Origin.URL = r.remote + return info, nil } func (r *vcsRepo) Latest() (*RevInfo, error) { @@ -382,19 +407,6 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) { return out, nil } -func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) { - // We don't technically need to lock here since we're returning an error - // uncondititonally, but doing so anyway will help to avoid baking in - // lock-inversion bugs. - unlock, err := r.mu.Lock() - if err != nil { - return nil, err - } - defer unlock() - - return nil, vcsErrorf("ReadFileRevs not implemented") -} - func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) { // We don't technically need to lock here since we're returning an error // uncondititonally, but doing so anyway will help to avoid baking in @@ -405,7 +417,7 @@ func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag } defer unlock() - return "", vcsErrorf("RecentTag not implemented") + return "", vcsErrorf("vcs %s: RecentTag: %w", r.cmd.vcs, ErrUnsupported) } func (r *vcsRepo) DescendsFrom(rev, tag string) (bool, error) { @@ -415,12 +427,12 @@ func (r *vcsRepo) DescendsFrom(rev, tag string) (bool, error) { } defer unlock() - return false, vcsErrorf("DescendsFrom not implemented") + return false, vcsErrorf("vcs %s: DescendsFrom: %w", r.cmd.vcs, ErrUnsupported) } func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error) { if r.cmd.readZip == nil && r.cmd.doReadZip == nil { - return nil, vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs) + return nil, vcsErrorf("vcs %s: ReadZip: %w", r.cmd.vcs, ErrUnsupported) } unlock, err := r.mu.Lock() @@ -504,6 +516,9 @@ func hgParseStat(rev, out string) (*RevInfo, error) { sort.Strings(tags) info := &RevInfo{ + Origin: &Origin{ + Hash: hash, + }, Name: hash, Short: ShortenSHA1(hash), Time: time.Unix(t, 0).UTC(), @@ -582,6 +597,9 @@ func fossilParseStat(rev, out string) (*RevInfo, error) { version = hash // extend to full hash } info := &RevInfo{ + Origin: &Origin{ + Hash: hash, + }, Name: hash, Short: ShortenSHA1(hash), Time: t, diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index dfef9f73c27aef..b72989b2a85620 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -130,12 +130,16 @@ func (r *codeRepo) ModulePath() string { return r.modPath } -func (r *codeRepo) Versions(prefix string) ([]string, error) { +func (r *codeRepo) CheckReuse(old *codehost.Origin) error { + return r.code.CheckReuse(old, r.codeDir) +} + +func (r *codeRepo) Versions(prefix string) (*Versions, error) { // Special case: gopkg.in/macaroon-bakery.v2-unstable // does not use the v2 tags (those are for macaroon-bakery.v2). // It has no possible tags at all. if strings.HasPrefix(r.modPath, "gopkg.in/") && strings.HasSuffix(r.modPath, "-unstable") { - return nil, nil + return &Versions{}, nil } p := prefix @@ -149,17 +153,33 @@ func (r *codeRepo) Versions(prefix string) ([]string, error) { Err: err, } } + if tags.Origin != nil { + tags.Origin.Subdir = r.codeDir + } var list, incompatible []string - for _, tag := range tags { - if !strings.HasPrefix(tag, p) { + for _, tag := range tags.List { + if !strings.HasPrefix(tag.Name, p) { continue } - v := tag + v := tag.Name if r.codeDir != "" { v = v[len(r.codeDir)+1:] } - if v == "" || v != module.CanonicalVersion(v) || module.IsPseudoVersion(v) { + // Note: ./codehost/codehost.go's isOriginTag knows about these conditions too. + // If these are relaxed, isOriginTag will need to be relaxed as well. + if v == "" || v != semver.Canonical(v) { + // Ignore non-canonical tags: Stat rewrites those to canonical + // pseudo-versions. Note that we compare against semver.Canonical here + // instead of module.CanonicalVersion: revToRev strips "+incompatible" + // suffixes before looking up tags, so a tag like "v2.0.0+incompatible" + // would not resolve at all. (The Go version string "v2.0.0+incompatible" + // refers to the "v2.0.0" version tag, which we handle below.) + continue + } + if module.IsPseudoVersion(v) { + // Ignore tags that look like pseudo-versions: Stat rewrites those + // unambiguously to the underlying commit, and tagToVersion drops them. continue } @@ -175,7 +195,7 @@ func (r *codeRepo) Versions(prefix string) ([]string, error) { semver.Sort(list) semver.Sort(incompatible) - return r.appendIncompatibleVersions(list, incompatible) + return r.appendIncompatibleVersions(tags.Origin, list, incompatible) } // appendIncompatibleVersions appends "+incompatible" versions to list if @@ -185,10 +205,14 @@ func (r *codeRepo) Versions(prefix string) ([]string, error) { // prefix. // // Both list and incompatible must be sorted in semantic order. -func (r *codeRepo) appendIncompatibleVersions(list, incompatible []string) ([]string, error) { +func (r *codeRepo) appendIncompatibleVersions(origin *codehost.Origin, list, incompatible []string) (*Versions, error) { + versions := &Versions{ + Origin: origin, + List: list, + } if len(incompatible) == 0 || r.pathMajor != "" { // No +incompatible versions are possible, so no need to check them. - return list, nil + return versions, nil } versionHasGoMod := func(v string) (bool, error) { @@ -221,7 +245,7 @@ func (r *codeRepo) appendIncompatibleVersions(list, incompatible []string) ([]st // (github.com/russross/blackfriday@v2.0.0 and // github.com/libp2p/go-libp2p@v6.0.23), and (as of 2019-10-29) have no // concrete examples for which it is undesired. - return list, nil + return versions, nil } } @@ -260,10 +284,10 @@ func (r *codeRepo) appendIncompatibleVersions(list, incompatible []string) ([]st // bounds. continue } - list = append(list, v+"+incompatible") + versions.List = append(versions.List, v+"+incompatible") } - return list, nil + return versions, nil } func (r *codeRepo) Stat(rev string) (*RevInfo, error) { @@ -273,7 +297,15 @@ func (r *codeRepo) Stat(rev string) (*RevInfo, error) { codeRev := r.revToRev(rev) info, err := r.code.Stat(codeRev) if err != nil { - return nil, &module.ModuleError{ + // Note: info may be non-nil to supply Origin for caching error. + var revInfo *RevInfo + if info != nil { + revInfo = &RevInfo{ + Origin: info.Origin, + Version: rev, + } + } + return revInfo, &module.ModuleError{ Path: r.modPath, Err: &module.InvalidVersionError{ Version: rev, @@ -298,42 +330,61 @@ func (r *codeRepo) Latest() (*RevInfo, error) { // If statVers is a valid module version, it is used for the Version field. // Otherwise, the Version is derived from the passed-in info and recent tags. func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) { - info2 := &RevInfo{ - Name: info.Name, - Short: info.Short, - Time: info.Time, - } - // If this is a plain tag (no dir/ prefix) // and the module path is unversioned, // and if the underlying file tree has no go.mod, // then allow using the tag with a +incompatible suffix. - var canUseIncompatible func() bool - canUseIncompatible = func() bool { - var ok bool - if r.codeDir == "" && r.pathMajor == "" { + // + // (If the version is +incompatible, then the go.mod file must not exist: + // +incompatible is not an ongoing opt-out from semantic import versioning.) + incompatibleOk := map[string]bool{} + canUseIncompatible := func(v string) bool { + if r.codeDir != "" || r.pathMajor != "" { + // A non-empty codeDir indicates a module within a subdirectory, + // which necessarily has a go.mod file indicating the module boundary. + // A non-empty pathMajor indicates a module path with a major-version + // suffix, which must match. + return false + } + + ok, seen := incompatibleOk[""] + if !seen { _, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod) - if errGoMod != nil { - ok = true - } + ok = (errGoMod != nil) + incompatibleOk[""] = ok + } + if !ok { + // A go.mod file exists at the repo root. + return false } - canUseIncompatible = func() bool { return ok } - return ok - } - invalidf := func(format string, args ...interface{}) error { - return &module.ModuleError{ - Path: r.modPath, - Err: &module.InvalidVersionError{ - Version: info2.Version, - Err: fmt.Errorf(format, args...), - }, + // Per https://go.dev/issue/51324, previous versions of the 'go' command + // didn't always check for go.mod files in subdirectories, so if the user + // requests a +incompatible version explicitly, we should continue to allow + // it. Otherwise, if vN/go.mod exists, expect that release tags for that + // major version are intended for the vN module. + if v != "" && !strings.HasSuffix(statVers, "+incompatible") { + major := semver.Major(v) + ok, seen = incompatibleOk[major] + if !seen { + _, errGoModSub := r.code.ReadFile(info.Name, path.Join(major, "go.mod"), codehost.MaxGoMod) + ok = (errGoModSub != nil) + incompatibleOk[major] = ok + } + if !ok { + return false + } } + + return true } - // checkGoMod verifies that the go.mod file for the module exists or does not - // exist as required by info2.Version and the module path represented by r. - checkGoMod := func() (*RevInfo, error) { + // checkCanonical verifies that the canonical version v is compatible with the + // module path represented by r, adding a "+incompatible" suffix if needed. + // + // If statVers is also canonical, checkCanonical also verifies that v is + // either statVers or statVers with the added "+incompatible" suffix. + checkCanonical := func(v string) (*RevInfo, error) { // If r.codeDir is non-empty, then the go.mod file must exist: the module // author — not the module consumer, — gets to decide how to carve up the repo // into modules. @@ -344,73 +395,115 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e // r.findDir verifies both of these conditions. Execute it now so that // r.Stat will correctly return a notExistError if the go.mod location or // declared module path doesn't match. - _, _, _, err := r.findDir(info2.Version) + _, _, _, err := r.findDir(v) if err != nil { // TODO: It would be nice to return an error like "not a module". // Right now we return "missing go.mod", which is a little confusing. return nil, &module.ModuleError{ Path: r.modPath, Err: &module.InvalidVersionError{ - Version: info2.Version, + Version: v, Err: notExistError{err: err}, }, } } - // If the version is +incompatible, then the go.mod file must not exist: - // +incompatible is not an ongoing opt-out from semantic import versioning. - if strings.HasSuffix(info2.Version, "+incompatible") { - if !canUseIncompatible() { + invalidf := func(format string, args ...any) error { + return &module.ModuleError{ + Path: r.modPath, + Err: &module.InvalidVersionError{ + Version: v, + Err: fmt.Errorf(format, args...), + }, + } + } + + // Add the +incompatible suffix if needed or requested explicitly, and + // verify that its presence or absence is appropriate for this version + // (which depends on whether it has an explicit go.mod file). + + if v == strings.TrimSuffix(statVers, "+incompatible") { + v = statVers + } + base := strings.TrimSuffix(v, "+incompatible") + var errIncompatible error + if !module.MatchPathMajor(base, r.pathMajor) { + if canUseIncompatible(base) { + v = base + "+incompatible" + } else { if r.pathMajor != "" { - return nil, invalidf("+incompatible suffix not allowed: module path includes a major version suffix, so major version must match") + errIncompatible = invalidf("module path includes a major version suffix, so major version must match") } else { - return nil, invalidf("+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required") + errIncompatible = invalidf("module contains a go.mod file, so module path must match major version (%q)", path.Join(r.pathPrefix, semver.Major(v))) } } + } else if strings.HasSuffix(v, "+incompatible") { + errIncompatible = invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(v)) + } - if err := module.CheckPathMajor(strings.TrimSuffix(info2.Version, "+incompatible"), r.pathMajor); err == nil { - return nil, invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(info2.Version)) + if statVers != "" && statVers == module.CanonicalVersion(statVers) { + // Since the caller-requested version is canonical, it would be very + // confusing to resolve it to anything but itself, possibly with a + // "+incompatible" suffix. Error out explicitly. + if statBase := strings.TrimSuffix(statVers, "+incompatible"); statBase != base { + return nil, &module.ModuleError{ + Path: r.modPath, + Err: &module.InvalidVersionError{ + Version: statVers, + Err: fmt.Errorf("resolves to version %v (%s is not a tag)", v, statBase), + }, + } } } - return info2, nil - } - - // Determine version. - // - // If statVers is canonical, then the original call was repo.Stat(statVers). - // Since the version is canonical, we must not resolve it to anything but - // itself, possibly with a '+incompatible' annotation: we do not need to do - // the work required to look for an arbitrary pseudo-version. - if statVers != "" && statVers == module.CanonicalVersion(statVers) { - info2.Version = statVers - - if module.IsPseudoVersion(info2.Version) { - if err := r.validatePseudoVersion(info, info2.Version); err != nil { - return nil, err - } - return checkGoMod() + if errIncompatible != nil { + return nil, errIncompatible } - if err := module.CheckPathMajor(info2.Version, r.pathMajor); err != nil { - if canUseIncompatible() { - info2.Version += "+incompatible" - return checkGoMod() - } else { - if vErr, ok := err.(*module.InvalidVersionError); ok { - // We're going to describe why the version is invalid in more detail, - // so strip out the existing “invalid version” wrapper. - err = vErr.Err + origin := info.Origin + if origin != nil { + o := *origin + origin = &o + origin.Subdir = r.codeDir + if module.IsPseudoVersion(v) && (v != statVers || !strings.HasPrefix(v, "v0.0.0-")) { + // Add tags that are relevant to pseudo-version calculation to origin. + prefix := r.codeDir + if prefix != "" { + prefix += "/" } - return nil, invalidf("module contains a go.mod file, so major version must be compatible: %v", err) + if r.pathMajor != "" { // "/v2" or "/.v2" + prefix += r.pathMajor[1:] + "." // += "v2." + } + tags, err := r.code.Tags(prefix) + if err != nil { + return nil, err + } + origin.TagPrefix = tags.Origin.TagPrefix + origin.TagSum = tags.Origin.TagSum } } - return checkGoMod() + return &RevInfo{ + Origin: origin, + Name: info.Name, + Short: info.Short, + Time: info.Time, + Version: v, + }, nil + } + + // Determine version. + + if module.IsPseudoVersion(statVers) { + if err := r.validatePseudoVersion(info, statVers); err != nil { + return nil, err + } + return checkCanonical(statVers) } - // statVers is empty or non-canonical, so we need to resolve it to a canonical - // version or pseudo-version. + // statVers is not a pseudo-version, so we need to either resolve it to a + // canonical version or verify that it is already a canonical tag + // (not a branch). // Derive or verify a version from a code repo tag. // Tag must have a prefix matching codeDir. @@ -441,98 +534,89 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e if v == "" || !strings.HasPrefix(trimmed, v) { return "", false // Invalid or incomplete version (just vX or vX.Y). } - if isRetracted(v) { - return "", false - } if v == trimmed { tagIsCanonical = true } - - if err := module.CheckPathMajor(v, r.pathMajor); err != nil { - if canUseIncompatible() { - return v + "+incompatible", tagIsCanonical - } - return "", false - } - return v, tagIsCanonical } // If the VCS gave us a valid version, use that. if v, tagIsCanonical := tagToVersion(info.Version); tagIsCanonical { - info2.Version = v - return checkGoMod() + if info, err := checkCanonical(v); err == nil { + return info, err + } } // Look through the tags on the revision for either a usable canonical version // or an appropriate base for a pseudo-version. - var pseudoBase string + var ( + highestCanonical string + pseudoBase string + ) for _, pathTag := range info.Tags { v, tagIsCanonical := tagToVersion(pathTag) - if tagIsCanonical { - if statVers != "" && semver.Compare(v, statVers) == 0 { - // The user requested a non-canonical version, but the tag for the - // canonical equivalent refers to the same revision. Use it. - info2.Version = v - return checkGoMod() + if statVers != "" && semver.Compare(v, statVers) == 0 { + // The tag is equivalent to the version requested by the user. + if tagIsCanonical { + // This tag is the canonical form of the requested version, + // not some other form with extra build metadata. + // Use this tag so that the resolved version will match exactly. + // (If it isn't actually allowed, we'll error out in checkCanonical.) + return checkCanonical(v) } else { - // Save the highest canonical tag for the revision. If we don't find a - // better match, we'll use it as the canonical version. + // The user explicitly requested something equivalent to this tag. We + // can't use the version from the tag directly: since the tag is not + // canonical, it could be ambiguous. For example, tags v0.0.1+a and + // v0.0.1+b might both exist and refer to different revisions. // - // NOTE: Do not replace this with semver.Max. Despite the name, - // semver.Max *also* canonicalizes its arguments, which uses - // semver.Canonical instead of module.CanonicalVersion and thereby - // strips our "+incompatible" suffix. - if semver.Compare(info2.Version, v) < 0 { - info2.Version = v - } + // The tag is otherwise valid for the module, so we can at least use it as + // the base of an unambiguous pseudo-version. + // + // If multiple tags match, tagToVersion will canonicalize them to the same + // base version. + pseudoBase = v + } + } + // Save the highest non-retracted canonical tag for the revision. + // If we don't find a better match, we'll use it as the canonical version. + if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) { + if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible(v) { + highestCanonical = v } - } else if v != "" && semver.Compare(v, statVers) == 0 { - // The user explicitly requested something equivalent to this tag. We - // can't use the version from the tag directly: since the tag is not - // canonical, it could be ambiguous. For example, tags v0.0.1+a and - // v0.0.1+b might both exist and refer to different revisions. - // - // The tag is otherwise valid for the module, so we can at least use it as - // the base of an unambiguous pseudo-version. - // - // If multiple tags match, tagToVersion will canonicalize them to the same - // base version. - pseudoBase = v } } - // If we found any canonical tag for the revision, return it. + // If we found a valid canonical tag for the revision, return it. // Even if we found a good pseudo-version base, a canonical version is better. - if info2.Version != "" { - return checkGoMod() + if highestCanonical != "" { + return checkCanonical(highestCanonical) } // Find the highest tagged version in the revision's history, subject to // major version and +incompatible constraints. Use that version as the // pseudo-version base so that the pseudo-version sorts higher. Ignore // retracted versions. - allowedMajor := func(major string) func(v string) bool { - return func(v string) bool { - return (major == "" || semver.Major(v) == major) && !isRetracted(v) + tagAllowed := func(tag string) bool { + v, _ := tagToVersion(tag) + if v == "" { + return false + } + if !module.MatchPathMajor(v, r.pathMajor) && !canUseIncompatible(v) { + return false } + return !isRetracted(v) } if pseudoBase == "" { - var tag string - if r.pseudoMajor != "" || canUseIncompatible() { - tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor)) - } else { - // Allow either v1 or v0, but not incompatible higher versions. - tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v1")) - if tag == "" { - tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0")) - } + tag, err := r.code.RecentTag(info.Name, tagPrefix, tagAllowed) + if err != nil && !errors.Is(err, codehost.ErrUnsupported) { + return nil, err + } + if tag != "" { + pseudoBase, _ = tagToVersion(tag) } - pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid } - info2.Version = module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short) - return checkGoMod() + return checkCanonical(module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short)) } // validatePseudoVersion checks that version has a major version compatible with @@ -556,10 +640,6 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string) } }() - if err := module.CheckPathMajor(version, r.pathMajor); err != nil { - return err - } - rev, err := module.PseudoVersionRev(version) if err != nil { return err @@ -567,11 +647,11 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string) if rev != info.Short { switch { case strings.HasPrefix(rev, info.Short): - return fmt.Errorf("revision is longer than canonical (%s)", info.Short) + return fmt.Errorf("revision is longer than canonical (expected %s)", info.Short) case strings.HasPrefix(info.Short, rev): - return fmt.Errorf("revision is shorter than canonical (%s)", info.Short) + return fmt.Errorf("revision is shorter than canonical (expected %s)", info.Short) default: - return fmt.Errorf("does not match short name of revision (%s)", info.Short) + return fmt.Errorf("does not match short name of revision (expected %s)", info.Short) } } @@ -642,11 +722,11 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string) var lastTag string // Prefer to log some real tag rather than a canonically-equivalent base. ancestorFound := false - for _, tag := range tags { - versionOnly := strings.TrimPrefix(tag, tagPrefix) + for _, tag := range tags.List { + versionOnly := strings.TrimPrefix(tag.Name, tagPrefix) if semver.Compare(versionOnly, base) == 0 { - lastTag = tag - ancestorFound, err = r.code.DescendsFrom(info.Name, tag) + lastTag = tag.Name + ancestorFound, err = r.code.DescendsFrom(info.Name, tag.Name) if ancestorFound { break } @@ -715,7 +795,7 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e file1 := path.Join(r.codeDir, "go.mod") gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod) if err1 != nil && !os.IsNotExist(err1) { - return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file1, rev, err1) + return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.codeRoot, file1, rev, err1) } mpath1 := modfile.ModulePath(gomod1) found1 := err1 == nil && (isMajor(mpath1, r.pathMajor) || r.canReplaceMismatchedVersionDueToBug(mpath1)) @@ -733,7 +813,7 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e file2 = path.Join(dir2, "go.mod") gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod) if err2 != nil && !os.IsNotExist(err2) { - return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file2, rev, err2) + return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.codeRoot, file2, rev, err2) } mpath2 := modfile.ModulePath(gomod2) found2 := err2 == nil && isMajor(mpath2, r.pathMajor) @@ -746,9 +826,9 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e } if err2 == nil { if mpath2 == "" { - return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.pathPrefix, file2, rev) + return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.codeRoot, file2, rev) } - return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.pathPrefix, file2, r.pathMajor, mpath2, rev) + return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.codeRoot, file2, r.pathMajor, mpath2, rev) } } @@ -890,13 +970,18 @@ func (r *codeRepo) modPrefix(rev string) string { } func (r *codeRepo) retractedVersions() (func(string) bool, error) { - versions, err := r.Versions("") + vs, err := r.Versions("") if err != nil { return nil, err } + versions := vs.List for i, v := range versions { if strings.HasSuffix(v, "+incompatible") { + // We're looking for the latest release tag that may list retractions in a + // go.mod file. +incompatible versions necessarily do not, and they start + // at major version 2 — which is higher than any version that could + // validly contain a go.mod file. versions = versions[:i] break } @@ -1066,7 +1151,7 @@ func (fi dataFileInfo) Size() int64 { return int64(len(fi.f.data)) } func (fi dataFileInfo) Mode() fs.FileMode { return 0644 } func (fi dataFileInfo) ModTime() time.Time { return time.Time{} } func (fi dataFileInfo) IsDir() bool { return false } -func (fi dataFileInfo) Sys() interface{} { return nil } +func (fi dataFileInfo) Sys() any { return nil } // hasPathPrefix reports whether the path s begins with the // elements in prefix. diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go index 02e399f3525959..3dd1b1cca6af07 100644 --- a/src/cmd/go/internal/modfetch/coderepo_test.go +++ b/src/cmd/go/internal/modfetch/coderepo_test.go @@ -418,171 +418,319 @@ var codeRepoTests = []codeRepoTest{ zipSum: "h1:JItBZ+gwA5WvtZEGEbuDL4lUttGtLrs53lmdurq3bOg=", zipFileHash: "9ea9ae1673cffcc44b7fdd3cc89953d68c102449b46c982dbf085e4f2e394da5", }, + { + // Git branch with a semver name, +incompatible version, and no go.mod file. + vcs: "git", + path: "vcs-test.golang.org/go/mod/gitrepo1", + rev: "v2.3.4+incompatible", + err: `resolves to version v2.0.1+incompatible (v2.3.4 is not a tag)`, + }, + { + // Git branch with a semver name, matching go.mod file, and compatible version. + vcs: "git", + path: "vcs-test.golang.org/git/semver-branch.git", + rev: "v1.0.0", + err: `resolves to version v0.1.1-0.20220202191944-09c4d8f6938c (v1.0.0 is not a tag)`, + }, + { + // Git branch with a semver name, matching go.mod file, and disallowed +incompatible version. + // The version/tag mismatch takes precedence over the +incompatible mismatched. + vcs: "git", + path: "vcs-test.golang.org/git/semver-branch.git", + rev: "v2.0.0+incompatible", + err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`, + }, + { + // Git branch with a semver name, matching go.mod file, and mismatched version. + // The version/tag mismatch takes precedence over the +incompatible mismatched. + vcs: "git", + path: "vcs-test.golang.org/git/semver-branch.git", + rev: "v2.0.0", + err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`, + }, + { + // v3.0.0-devel is the same as tag v4.0.0-beta.1, but v4.0.0-beta.1 would + // not be allowed because it is incompatible and a go.mod file exists. + // The error message should refer to a valid pseudo-version, not the + // unusable semver tag. + vcs: "git", + path: "vcs-test.golang.org/git/semver-branch.git", + rev: "v3.0.0-devel", + err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`, + }, + + // If v2/go.mod exists, then we should prefer to match the "v2" + // pseudo-versions to the nested module, and resolve the module in the parent + // directory to only compatible versions. + // + // However (https://go.dev/issue/51324), previous versions of the 'go' command + // didn't always do so, so if the user explicitly requests a +incompatible + // version (as would be present in an existing go.mod file), we should + // continue to allow it. + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "80beb17a1603", + version: "v0.0.0-20220222205507-80beb17a1603", + name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", + short: "80beb17a1603", + time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.0", + err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.1-0.20220222205507-80beb17a1603", + err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.0+incompatible", + version: "v2.0.0+incompatible", + name: "5fcd3eaeeb391d399f562fd45a50dac9fc34ae8b", + short: "5fcd3eaeeb39", + time: time.Date(2022, 2, 22, 20, 53, 33, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/v2sub.git", + rev: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", + version: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", + name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", + short: "80beb17a1603", + time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), + }, + + // A version tag with explicit build metadata is valid but not canonical. + // It should resolve to a pseudo-version based on the same tag. + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "v0.1.0+build-metadata", + version: "v0.1.1-0.20220223184835-9d863d525bbf", + name: "9d863d525bbfcc8eda09364738c4032393711a56", + short: "9d863d525bbf", + time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "9d863d525bbf", + version: "v0.1.1-0.20220223184835-9d863d525bbf", + name: "9d863d525bbfcc8eda09364738c4032393711a56", + short: "9d863d525bbf", + time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "latest", + version: "v0.1.1-0.20220223184835-9d863d525bbf", + name: "9d863d525bbfcc8eda09364738c4032393711a56", + short: "9d863d525bbf", + time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), + }, + + // A version tag with an erroneous "+incompatible" suffix should resolve using + // only the prefix before the "+incompatible" suffix, not the "+incompatible" + // tag itself. (Otherwise, we would potentially have two different commits + // both named "v2.0.0+incompatible".) However, the tag is still valid semver + // and can still be used as the base for an unambiguous pseudo-version. + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "v2.0.0+incompatible", + err: `unknown revision v2.0.0`, + }, + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "12d19af20458", + version: "v2.0.1-0.20220223184802-12d19af20458+incompatible", + name: "12d19af204585b0db3d2a876ceddf5b9323f5a4a", + short: "12d19af20458", + time: time.Date(2022, 2, 23, 18, 48, 2, 0, time.UTC), + }, + + // Similarly, a pseudo-version must resolve to the named commit, even if a tag + // matching that pseudo-version is present on a *different* commit. + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + rev: "v3.0.0-20220223184802-12d19af20458", + version: "v3.0.0-20220223184802-12d19af20458+incompatible", + name: "12d19af204585b0db3d2a876ceddf5b9323f5a4a", + short: "12d19af20458", + time: time.Date(2022, 2, 23, 18, 48, 2, 0, time.UTC), + }, } func TestCodeRepo(t *testing.T) { testenv.MustHaveExternalNetwork(t) + tmpdir := t.TempDir() - tmpdir, err := os.MkdirTemp("", "modfetch-test-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) + for _, tt := range codeRepoTests { + f := func(tt codeRepoTest) func(t *testing.T) { + return func(t *testing.T) { + if strings.Contains(tt.path, "gopkg.in") { + testenv.SkipFlaky(t, 54503) + } - t.Run("parallel", func(t *testing.T) { - for _, tt := range codeRepoTests { - f := func(tt codeRepoTest) func(t *testing.T) { - return func(t *testing.T) { - t.Parallel() - if tt.vcs != "mod" { - testenv.MustHaveExecPath(t, tt.vcs) - } + t.Parallel() + if tt.vcs != "mod" { + testenv.MustHaveExecPath(t, tt.vcs) + } - repo := Lookup("direct", tt.path) + repo := Lookup("direct", tt.path) - if tt.mpath == "" { - tt.mpath = tt.path - } - if mpath := repo.ModulePath(); mpath != tt.mpath { - t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath) - } + if tt.mpath == "" { + tt.mpath = tt.path + } + if mpath := repo.ModulePath(); mpath != tt.mpath { + t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath) + } - info, err := repo.Stat(tt.rev) - if err != nil { - if tt.err != "" { - if !strings.Contains(err.Error(), tt.err) { - t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err) - } - return - } - t.Fatalf("repo.Stat(%q): %v", tt.rev, err) - } + info, err := repo.Stat(tt.rev) + if err != nil { if tt.err != "" { - t.Errorf("repo.Stat(%q): success, wanted error", tt.rev) - } - if info.Version != tt.version { - t.Errorf("info.Version = %q, want %q", info.Version, tt.version) + if !strings.Contains(err.Error(), tt.err) { + t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err) + } + return } - if info.Name != tt.name { - t.Errorf("info.Name = %q, want %q", info.Name, tt.name) + t.Fatalf("repo.Stat(%q): %v", tt.rev, err) + } + if tt.err != "" { + t.Errorf("repo.Stat(%q): success, wanted error", tt.rev) + } + if info.Version != tt.version { + t.Errorf("info.Version = %q, want %q", info.Version, tt.version) + } + if info.Name != tt.name { + t.Errorf("info.Name = %q, want %q", info.Name, tt.name) + } + if info.Short != tt.short { + t.Errorf("info.Short = %q, want %q", info.Short, tt.short) + } + if !info.Time.Equal(tt.time) { + t.Errorf("info.Time = %v, want %v", info.Time, tt.time) + } + + if tt.gomod != "" || tt.gomodErr != "" { + data, err := repo.GoMod(tt.version) + if err != nil && tt.gomodErr == "" { + t.Errorf("repo.GoMod(%q): %v", tt.version, err) + } else if err != nil && tt.gomodErr != "" { + if err.Error() != tt.gomodErr { + t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr) + } + } else if tt.gomodErr != "" { + t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr) + } else if string(data) != tt.gomod { + t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod) } - if info.Short != tt.short { - t.Errorf("info.Short = %q, want %q", info.Short, tt.short) + } + + needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "") + if tt.zip != nil || tt.zipErr != "" || needHash { + f, err := os.CreateTemp(tmpdir, tt.version+".zip.") + if err != nil { + t.Fatalf("os.CreateTemp: %v", err) } - if !info.Time.Equal(tt.time) { - t.Errorf("info.Time = %v, want %v", info.Time, tt.time) + zipfile := f.Name() + defer func() { + f.Close() + os.Remove(zipfile) + }() + + var w io.Writer + var h hash.Hash + if needHash { + h = sha256.New() + w = io.MultiWriter(f, h) + } else { + w = f } - - if tt.gomod != "" || tt.gomodErr != "" { - data, err := repo.GoMod(tt.version) - if err != nil && tt.gomodErr == "" { - t.Errorf("repo.GoMod(%q): %v", tt.version, err) - } else if err != nil && tt.gomodErr != "" { - if err.Error() != tt.gomodErr { - t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr) + err = repo.Zip(w, tt.version) + f.Close() + if err != nil { + if tt.zipErr != "" { + if err.Error() == tt.zipErr { + return } - } else if tt.gomodErr != "" { - t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr) - } else if string(data) != tt.gomod { - t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod) + t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr) } + t.Fatalf("repo.Zip(%q): %v", tt.version, err) + } + if tt.zipErr != "" { + t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr) } - needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "") - if tt.zip != nil || tt.zipErr != "" || needHash { - f, err := os.CreateTemp(tmpdir, tt.version+".zip.") + if tt.zip != nil { + prefix := tt.path + "@" + tt.version + "/" + z, err := zip.OpenReader(zipfile) if err != nil { - t.Fatalf("os.CreateTemp: %v", err) + t.Fatalf("open zip %s: %v", zipfile, err) } - zipfile := f.Name() - defer func() { - f.Close() - os.Remove(zipfile) - }() - - var w io.Writer - var h hash.Hash - if needHash { - h = sha256.New() - w = io.MultiWriter(f, h) - } else { - w = f - } - err = repo.Zip(w, tt.version) - f.Close() - if err != nil { - if tt.zipErr != "" { - if err.Error() == tt.zipErr { - return - } - t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr) + var names []string + for _, file := range z.File { + if !strings.HasPrefix(file.Name, prefix) { + t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix) + continue } - t.Fatalf("repo.Zip(%q): %v", tt.version, err) + names = append(names, file.Name[len(prefix):]) } - if tt.zipErr != "" { - t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr) - } - - if tt.zip != nil { - prefix := tt.path + "@" + tt.version + "/" - z, err := zip.OpenReader(zipfile) - if err != nil { - t.Fatalf("open zip %s: %v", zipfile, err) - } - var names []string - for _, file := range z.File { - if !strings.HasPrefix(file.Name, prefix) { - t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix) - continue - } - names = append(names, file.Name[len(prefix):]) - } - z.Close() - if !reflect.DeepEqual(names, tt.zip) { - t.Fatalf("zip = %v\nwant %v\n", names, tt.zip) - } + z.Close() + if !reflect.DeepEqual(names, tt.zip) { + t.Fatalf("zip = %v\nwant %v\n", names, tt.zip) } + } - if needHash { - sum, err := dirhash.HashZip(zipfile, dirhash.Hash1) - if err != nil { - t.Errorf("repo.Zip(%q): %v", tt.version, err) - } else if sum != tt.zipSum { - t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum) - } else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash { - t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash) - } + if needHash { + sum, err := dirhash.HashZip(zipfile, dirhash.Hash1) + if err != nil { + t.Errorf("repo.Zip(%q): %v", tt.version, err) + } else if sum != tt.zipSum { + t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum) + } else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash { + t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash) } } } } - t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt)) - if strings.HasPrefix(tt.path, vgotest1git) { - for vcs, alt := range altVgotests { - altTest := tt - altTest.vcs = vcs - altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git) - if strings.HasPrefix(altTest.mpath, vgotest1git) { - altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git) - } - var m map[string]string - if alt == vgotest1hg { - m = hgmap - } - altTest.version = remap(altTest.version, m) - altTest.name = remap(altTest.name, m) - altTest.short = remap(altTest.short, m) - altTest.rev = remap(altTest.rev, m) - altTest.err = remap(altTest.err, m) - altTest.gomodErr = remap(altTest.gomodErr, m) - altTest.zipErr = remap(altTest.zipErr, m) - altTest.zipSum = "" - altTest.zipFileHash = "" - t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest)) + } + t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt)) + if strings.HasPrefix(tt.path, vgotest1git) { + for vcs, alt := range altVgotests { + altTest := tt + altTest.vcs = vcs + altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git) + if strings.HasPrefix(altTest.mpath, vgotest1git) { + altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git) } + var m map[string]string + if alt == vgotest1hg { + m = hgmap + } + altTest.version = remap(altTest.version, m) + altTest.name = remap(altTest.name, m) + altTest.short = remap(altTest.short, m) + altTest.rev = remap(altTest.rev, m) + altTest.err = remap(altTest.err, m) + altTest.gomodErr = remap(altTest.gomodErr, m) + altTest.zipErr = remap(altTest.zipErr, m) + altTest.zipSum = "" + altTest.zipFileHash = "" + t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest)) } } - }) + } } var hgmap = map[string]string{ @@ -649,6 +797,11 @@ var codeRepoVersionsTests = []struct { path: "gopkg.in/natefinch/lumberjack.v2", versions: []string{"v2.0.0"}, }, + { + vcs: "git", + path: "vcs-test.golang.org/git/odd-tags.git", + versions: nil, + }, } func TestCodeRepoVersions(t *testing.T) { @@ -662,8 +815,12 @@ func TestCodeRepoVersions(t *testing.T) { t.Run("parallel", func(t *testing.T) { for _, tt := range codeRepoVersionsTests { + tt := tt t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { - tt := tt + if strings.Contains(tt.path, "gopkg.in") { + testenv.SkipFlaky(t, 54503) + } + t.Parallel() if tt.vcs != "mod" { testenv.MustHaveExecPath(t, tt.vcs) @@ -674,7 +831,7 @@ func TestCodeRepoVersions(t *testing.T) { if err != nil { t.Fatalf("Versions(%q): %v", tt.prefix, err) } - if !reflect.DeepEqual(list, tt.versions) { + if !reflect.DeepEqual(list.List, tt.versions) { t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions) } }) @@ -772,7 +929,13 @@ type fixedTagsRepo struct { codehost.Repo } -func (ch *fixedTagsRepo) Tags(string) ([]string, error) { return ch.tags, nil } +func (ch *fixedTagsRepo) Tags(string) (*codehost.Tags, error) { + tags := &codehost.Tags{} + for _, t := range ch.tags { + tags.List = append(tags.List, codehost.Tag{Name: t}) + } + return tags, nil +} func TestNonCanonicalSemver(t *testing.T) { root := "golang.org/x/issue24476" @@ -796,7 +959,7 @@ func TestNonCanonicalSemver(t *testing.T) { if err != nil { t.Fatal(err) } - if len(v) != 1 || v[0] != "v1.0.1" { + if len(v.List) != 1 || v.List[0] != "v1.0.1" { t.Fatal("unexpected versions returned:", v) } } diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go index d3d30d970b3b97..2e8c4c8acaaceb 100644 --- a/src/cmd/go/internal/modfetch/fetch.go +++ b/src/cmd/go/internal/modfetch/fetch.go @@ -26,6 +26,7 @@ import ( "cmd/go/internal/lockedfile" "cmd/go/internal/par" "cmd/go/internal/robustio" + "cmd/go/internal/str" "cmd/go/internal/trace" "golang.org/x/mod/module" @@ -48,7 +49,7 @@ func Download(ctx context.Context, mod module.Version) (dir string, err error) { dir string err error } - c := downloadCache.Do(mod, func() interface{} { + c := downloadCache.Do(mod, func() any { dir, err := download(ctx, mod) if err != nil { return cached{"", err} @@ -102,7 +103,7 @@ func download(ctx context.Context, mod module.Version) (dir string, err error) { // active. parentDir := filepath.Dir(dir) tmpPrefix := filepath.Base(dir) + ".tmp-" - if old, err := filepath.Glob(filepath.Join(parentDir, tmpPrefix+"*")); err == nil { + if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(parentDir), str.QuoteGlob(tmpPrefix)+"*")); err == nil { for _, path := range old { RemoveAll(path) // best effort } @@ -165,7 +166,7 @@ func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err e zipfile string err error } - c := downloadZipCache.Do(mod, func() interface{} { + c := downloadZipCache.Do(mod, func() any { zipfile, err := CachePath(mod, "zip") if err != nil { return cached{"", err} @@ -224,7 +225,7 @@ func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err e // This is only safe to do because the lock file ensures that their // writers are no longer active. tmpPattern := filepath.Base(zipfile) + "*.tmp" - if old, err := filepath.Glob(filepath.Join(filepath.Dir(zipfile), tmpPattern)); err == nil { + if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(filepath.Dir(zipfile)), tmpPattern)); err == nil { for _, path := range old { os.Remove(path) // best effort } @@ -241,7 +242,7 @@ func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err e // contents of the file (by hashing it) before we commit it. Because the file // is zip-compressed, we need an actual file — or at least an io.ReaderAt — to // validate it: we can't just tee the stream as we write it. - f, err := os.CreateTemp(filepath.Dir(zipfile), tmpPattern) + f, err := tempFile(filepath.Dir(zipfile), filepath.Base(zipfile), 0666) if err != nil { return err } @@ -319,7 +320,7 @@ func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err e // // If the hash does not match go.sum (or the sumdb if enabled), hashZip returns // an error and does not write ziphashfile. -func hashZip(mod module.Version, zipfile, ziphashfile string) error { +func hashZip(mod module.Version, zipfile, ziphashfile string) (err error) { hash, err := dirhash.HashZip(zipfile, dirhash.DefaultHash) if err != nil { return err @@ -331,16 +332,17 @@ func hashZip(mod module.Version, zipfile, ziphashfile string) error { if err != nil { return err } + defer func() { + if closeErr := hf.Close(); err == nil && closeErr != nil { + err = closeErr + } + }() if err := hf.Truncate(int64(len(hash))); err != nil { return err } if _, err := hf.WriteAt([]byte(hash), 0); err != nil { return err } - if err := hf.Close(); err != nil { - return err - } - return nil } @@ -384,7 +386,8 @@ func RemoveAll(dir string) error { return robustio.RemoveAll(dir) } -var GoSumFile string // path to go.sum; set by package modload +var GoSumFile string // path to go.sum; set by package modload +var WorkspaceGoSumFiles []string // path to module go.sums in workspace; set by package modload type modSum struct { mod module.Version @@ -393,16 +396,39 @@ type modSum struct { var goSum struct { mu sync.Mutex - m map[module.Version][]string // content of go.sum file - status map[modSum]modSumStatus // state of sums in m - overwrite bool // if true, overwrite go.sum without incorporating its contents - enabled bool // whether to use go.sum at all + m map[module.Version][]string // content of go.sum file + w map[string]map[module.Version][]string // sum file in workspace -> content of that sum file + status map[modSum]modSumStatus // state of sums in m + overwrite bool // if true, overwrite go.sum without incorporating its contents + enabled bool // whether to use go.sum at all } type modSumStatus struct { used, dirty bool } +// Reset resets globals in the modfetch package, so previous loads don't affect +// contents of go.sum files +func Reset() { + GoSumFile = "" + WorkspaceGoSumFiles = nil + + // Uses of lookupCache and downloadCache both can call checkModSum, + // which in turn sets the used bit on goSum.status for modules. + // Reset them so used can be computed properly. + lookupCache = par.Cache{} + downloadCache = par.Cache{} + + // Clear all fields on goSum. It will be initialized later + goSum.mu.Lock() + goSum.m = nil + goSum.w = nil + goSum.status = nil + goSum.overwrite = false + goSum.enabled = false + goSum.mu.Unlock() +} + // initGoSum initializes the go.sum data. // The boolean it returns reports whether the // use of go.sum is now enabled. @@ -417,23 +443,38 @@ func initGoSum() (bool, error) { goSum.m = make(map[module.Version][]string) goSum.status = make(map[modSum]modSumStatus) + goSum.w = make(map[string]map[module.Version][]string) + + for _, f := range WorkspaceGoSumFiles { + goSum.w[f] = make(map[module.Version][]string) + _, err := readGoSumFile(goSum.w[f], f) + if err != nil { + return false, err + } + } + + enabled, err := readGoSumFile(goSum.m, GoSumFile) + goSum.enabled = enabled + return enabled, err +} + +func readGoSumFile(dst map[module.Version][]string, file string) (bool, error) { var ( data []byte err error ) - if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok { + if actualSumFile, ok := fsys.OverlayPath(file); ok { // Don't lock go.sum if it's part of the overlay. // On Plan 9, locking requires chmod, and we don't want to modify any file // in the overlay. See #44700. data, err = os.ReadFile(actualSumFile) } else { - data, err = lockedfile.Read(GoSumFile) + data, err = lockedfile.Read(file) } if err != nil && !os.IsNotExist(err) { return false, err } - goSum.enabled = true - readGoSum(goSum.m, GoSumFile, data) + readGoSum(dst, file, data) return true, nil } @@ -485,6 +526,16 @@ func HaveSum(mod module.Version) bool { if err != nil || !inited { return false } + for _, goSums := range goSum.w { + for _, h := range goSums[mod] { + if !strings.HasPrefix(h, "h1:") { + continue + } + if !goSum.status[modSum{mod, h}].dirty { + return true + } + } + } for _, h := range goSum.m[mod] { if !strings.HasPrefix(h, "h1:") { continue @@ -602,15 +653,32 @@ func checkModSum(mod module.Version, h string) error { // If it finds a conflicting pair instead, it calls base.Fatalf. // goSum.mu must be locked. func haveModSumLocked(mod module.Version, h string) bool { + sumFileName := "go.sum" + if strings.HasSuffix(GoSumFile, "go.work.sum") { + sumFileName = "go.work.sum" + } for _, vh := range goSum.m[mod] { if h == vh { return true } if strings.HasPrefix(vh, "h1:") { - base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v"+goSumMismatch, mod.Path, mod.Version, h, vh) + base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, sumFileName, vh) + } + } + // Also check workspace sums. + foundMatch := false + // Check sums from all files in case there are conflicts between + // the files. + for goSumFile, goSums := range goSum.w { + for _, vh := range goSums[mod] { + if h == vh { + foundMatch = true + } else if strings.HasPrefix(vh, "h1:") { + base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, goSumFile, vh) + } } } - return false + return foundMatch } // addModSumLocked adds the pair mod,h to go.sum. @@ -693,19 +761,21 @@ func isValidSum(data []byte) bool { return true } +var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly") + // WriteGoSum writes the go.sum file if it needs to be updated. // // keep is used to check whether a newly added sum should be saved in go.sum. // It should have entries for both module content sums and go.mod sums // (version ends with "/go.mod"). Existing sums will be preserved unless they // have been marked for deletion with TrimGoSum. -func WriteGoSum(keep map[module.Version]bool) { +func WriteGoSum(keep map[module.Version]bool, readonly bool) error { goSum.mu.Lock() defer goSum.mu.Unlock() // If we haven't read the go.sum file yet, don't bother writing it. if !goSum.enabled { - return + return nil } // Check whether we need to add sums for which keep[m] is true or remove @@ -723,10 +793,10 @@ Outer: } } if !dirty { - return + return nil } - if cfg.BuildMod == "readonly" { - base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly") + if readonly { + return ErrGoSumDirty } if _, ok := fsys.OverlayPath(GoSumFile); ok { base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay") @@ -747,7 +817,7 @@ Outer: goSum.m = make(map[module.Version][]string, len(goSum.m)) readGoSum(goSum.m, GoSumFile, data) for ms, st := range goSum.status { - if st.used { + if st.used && !sumInWorkspaceModulesLocked(ms.mod) { addModSumLocked(ms.mod, ms.sum) } } @@ -763,9 +833,10 @@ Outer: for _, m := range mods { list := goSum.m[m] sort.Strings(list) + str.Uniq(&list) for _, h := range list { st := goSum.status[modSum{m, h}] - if !st.dirty || (st.used && keep[m]) { + if (!st.dirty || (st.used && keep[m])) && !sumInWorkspaceModulesLocked(m) { fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h) } } @@ -774,11 +845,21 @@ Outer: }) if err != nil { - base.Fatalf("go: updating go.sum: %v", err) + return fmt.Errorf("updating go.sum: %w", err) } goSum.status = make(map[modSum]modSumStatus) goSum.overwrite = false + return nil +} + +func sumInWorkspaceModulesLocked(m module.Version) bool { + for _, goSums := range goSum.w { + if _, ok := goSums[m]; ok { + return true + } + } + return false } // TrimGoSum trims go.sum to contain only the modules needed for reproducible diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go index 31d453c8074e6a..d2374680d8e3a4 100644 --- a/src/cmd/go/internal/modfetch/proxy.go +++ b/src/cmd/go/internal/modfetch/proxy.go @@ -187,6 +187,10 @@ type proxyRepo struct { url *url.URL path string redactedURL string + + listLatestOnce sync.Once + listLatest *RevInfo + listLatestErr error } func newProxyRepo(baseURL, path string) (Repo, error) { @@ -214,13 +218,19 @@ func newProxyRepo(baseURL, path string) (Repo, error) { redactedURL := base.Redacted() base.Path = strings.TrimSuffix(base.Path, "/") + "/" + enc base.RawPath = strings.TrimSuffix(base.RawPath, "/") + "/" + pathEscape(enc) - return &proxyRepo{base, path, redactedURL}, nil + return &proxyRepo{base, path, redactedURL, sync.Once{}, nil, nil}, nil } func (p *proxyRepo) ModulePath() string { return p.path } +var errProxyReuse = fmt.Errorf("proxy does not support CheckReuse") + +func (p *proxyRepo) CheckReuse(old *codehost.Origin) error { + return errProxyReuse +} + // versionError returns err wrapped in a ModuleError for p.path. func (p *proxyRepo) versionError(version string, err error) error { if version != "" && version != module.CanonicalVersion(version) { @@ -247,10 +257,17 @@ func (p *proxyRepo) getBytes(path string) ([]byte, error) { return nil, err } defer body.Close() - return io.ReadAll(body) + + b, err := io.ReadAll(body) + if err != nil { + // net/http doesn't add context to Body errors, so add it here. + // (See https://go.dev/issue/52727.) + return b, &url.Error{Op: "read", URL: pathpkg.Join(p.redactedURL, path), Err: err} + } + return b, nil } -func (p *proxyRepo) getBody(path string) (io.ReadCloser, error) { +func (p *proxyRepo) getBody(path string) (r io.ReadCloser, err error) { fullPath := pathpkg.Join(p.url.Path, path) target := *p.url @@ -268,48 +285,59 @@ func (p *proxyRepo) getBody(path string) (io.ReadCloser, error) { return resp.Body, nil } -func (p *proxyRepo) Versions(prefix string) ([]string, error) { +func (p *proxyRepo) Versions(prefix string) (*Versions, error) { data, err := p.getBytes("@v/list") if err != nil { + p.listLatestOnce.Do(func() { + p.listLatest, p.listLatestErr = nil, p.versionError("", err) + }) return nil, p.versionError("", err) } var list []string - for _, line := range strings.Split(string(data), "\n") { + allLine := strings.Split(string(data), "\n") + for _, line := range allLine { f := strings.Fields(line) if len(f) >= 1 && semver.IsValid(f[0]) && strings.HasPrefix(f[0], prefix) && !module.IsPseudoVersion(f[0]) { list = append(list, f[0]) } } + p.listLatestOnce.Do(func() { + p.listLatest, p.listLatestErr = p.latestFromList(allLine) + }) semver.Sort(list) - return list, nil + return &Versions{List: list}, nil } func (p *proxyRepo) latest() (*RevInfo, error) { - data, err := p.getBytes("@v/list") - if err != nil { - return nil, p.versionError("", err) - } + p.listLatestOnce.Do(func() { + data, err := p.getBytes("@v/list") + if err != nil { + p.listLatestErr = p.versionError("", err) + return + } + list := strings.Split(string(data), "\n") + p.listLatest, p.listLatestErr = p.latestFromList(list) + }) + return p.listLatest, p.listLatestErr +} +func (p *proxyRepo) latestFromList(allLine []string) (*RevInfo, error) { var ( - bestTime time.Time - bestTimeIsFromPseudo bool - bestVersion string + bestTime time.Time + bestVersion string ) - - for _, line := range strings.Split(string(data), "\n") { + for _, line := range allLine { f := strings.Fields(line) if len(f) >= 1 && semver.IsValid(f[0]) { // If the proxy includes timestamps, prefer the timestamp it reports. // Otherwise, derive the timestamp from the pseudo-version. var ( - ft time.Time - ftIsFromPseudo = false + ft time.Time ) if len(f) >= 2 { ft, _ = time.Parse(time.RFC3339, f[1]) } else if module.IsPseudoVersion(f[0]) { ft, _ = module.PseudoVersionTime(f[0]) - ftIsFromPseudo = true } else { // Repo.Latest promises that this method is only called where there are // no tagged versions. Ignore any tagged versions that were added in the @@ -318,7 +346,6 @@ func (p *proxyRepo) latest() (*RevInfo, error) { } if bestTime.Before(ft) { bestTime = ft - bestTimeIsFromPseudo = ftIsFromPseudo bestVersion = f[0] } } @@ -327,22 +354,8 @@ func (p *proxyRepo) latest() (*RevInfo, error) { return nil, p.versionError("", codehost.ErrNoCommits) } - if bestTimeIsFromPseudo { - // We parsed bestTime from the pseudo-version, but that's in UTC and we're - // supposed to report the timestamp as reported by the VCS. - // Stat the selected version to canonicalize the timestamp. - // - // TODO(bcmills): Should we also stat other versions to ensure that we - // report the correct Name and Short for the revision? - return p.Stat(bestVersion) - } - - return &RevInfo{ - Version: bestVersion, - Name: bestVersion, - Short: bestVersion, - Time: bestTime, - }, nil + // Call Stat to get all the other fields, including Origin information. + return p.Stat(bestVersion) } func (p *proxyRepo) Stat(rev string) (*RevInfo, error) { @@ -407,7 +420,8 @@ func (p *proxyRepo) Zip(dst io.Writer, version string) error { if err != nil { return p.versionError(version, err) } - body, err := p.getBody("@v/" + encVer + ".zip") + path := "@v/" + encVer + ".zip" + body, err := p.getBody(path) if err != nil { return p.versionError(version, err) } @@ -415,6 +429,9 @@ func (p *proxyRepo) Zip(dst io.Writer, version string) error { lr := &io.LimitedReader{R: body, N: codehost.MaxZipFile + 1} if _, err := io.Copy(dst, lr); err != nil { + // net/http doesn't add context to Body errors, so add it here. + // (See https://go.dev/issue/52727.) + err = &url.Error{Op: "read", URL: pathpkg.Join(p.redactedURL, path), Err: err} return p.versionError(version, err) } if lr.N <= 0 { diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go index 0bffa55af6f2ed..d4c57bb300afba 100644 --- a/src/cmd/go/internal/modfetch/repo.go +++ b/src/cmd/go/internal/modfetch/repo.go @@ -29,6 +29,12 @@ type Repo interface { // ModulePath returns the module path. ModulePath() string + // CheckReuse checks whether the validation criteria in the origin + // are still satisfied on the server corresponding to this module. + // If so, the caller can reuse any cached Versions or RevInfo containing + // this origin rather than redownloading those from the server. + CheckReuse(old *codehost.Origin) error + // Versions lists all known versions with the given prefix. // Pseudo-versions are not included. // @@ -42,7 +48,7 @@ type Repo interface { // // If the underlying repository does not exist, // Versions returns an error matching errors.Is(_, os.NotExist). - Versions(prefix string) ([]string, error) + Versions(prefix string) (*Versions, error) // Stat returns information about the revision rev. // A revision can be any identifier known to the underlying service: @@ -61,7 +67,14 @@ type Repo interface { Zip(dst io.Writer, version string) error } -// A Rev describes a single revision in a module repository. +// A Versions describes the available versions in a module repository. +type Versions struct { + Origin *codehost.Origin `json:",omitempty"` // origin information for reuse + + List []string // semver versions +} + +// A RevInfo describes a single revision in a module repository. type RevInfo struct { Version string // suggested version string for this revision Time time.Time // commit time @@ -70,6 +83,8 @@ type RevInfo struct { // but they are not recorded when talking about module versions. Name string `json:"-"` // complete ID in underlying repository Short string `json:"-"` // shortened ID, for use in pseudo-version + + Origin *codehost.Origin `json:",omitempty"` // provenance for reuse } // Re: module paths, import paths, repository roots, and lookups @@ -196,7 +211,7 @@ func Lookup(proxy, path string) Repo { type cached struct { r Repo } - c := lookupCache.Do(lookupCacheKey{proxy, path}, func() interface{} { + c := lookupCache.Do(lookupCacheKey{proxy, path}, func() any { r := newCachingRepo(path, func() (Repo, error) { r, err := lookup(proxy, path) if err == nil && traceRepo { @@ -308,7 +323,7 @@ func newLoggingRepo(r Repo) *loggingRepo { // defer logCall("hello %s", arg)() // // Note the final (). -func logCall(format string, args ...interface{}) func() { +func logCall(format string, args ...any) func() { start := time.Now() fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...)) return func() { @@ -320,7 +335,14 @@ func (l *loggingRepo) ModulePath() string { return l.r.ModulePath() } -func (l *loggingRepo) Versions(prefix string) (tags []string, err error) { +func (l *loggingRepo) CheckReuse(old *codehost.Origin) (err error) { + defer func() { + logCall("CheckReuse[%s]: %v", l.r.ModulePath(), err) + }() + return l.r.CheckReuse(old) +} + +func (l *loggingRepo) Versions(prefix string) (*Versions, error) { defer logCall("Repo[%s]: Versions(%q)", l.r.ModulePath(), prefix)() return l.r.Versions(prefix) } @@ -360,18 +382,19 @@ type errRepo struct { func (r errRepo) ModulePath() string { return r.modulePath } -func (r errRepo) Versions(prefix string) (tags []string, err error) { return nil, r.err } -func (r errRepo) Stat(rev string) (*RevInfo, error) { return nil, r.err } -func (r errRepo) Latest() (*RevInfo, error) { return nil, r.err } -func (r errRepo) GoMod(version string) ([]byte, error) { return nil, r.err } -func (r errRepo) Zip(dst io.Writer, version string) error { return r.err } +func (r errRepo) CheckReuse(old *codehost.Origin) error { return r.err } +func (r errRepo) Versions(prefix string) (*Versions, error) { return nil, r.err } +func (r errRepo) Stat(rev string) (*RevInfo, error) { return nil, r.err } +func (r errRepo) Latest() (*RevInfo, error) { return nil, r.err } +func (r errRepo) GoMod(version string) ([]byte, error) { return nil, r.err } +func (r errRepo) Zip(dst io.Writer, version string) error { return r.err } // A notExistError is like fs.ErrNotExist, but with a custom message type notExistError struct { err error } -func notExistErrorf(format string, args ...interface{}) error { +func notExistErrorf(format string, args ...any) error { return notExistError{fmt.Errorf(format, args...)} } diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go index f233cba6df1bb7..492b03bd84ac8b 100644 --- a/src/cmd/go/internal/modfetch/sumdb.go +++ b/src/cmd/go/internal/modfetch/sumdb.go @@ -5,7 +5,6 @@ // Go checksum database lookup //go:build !cmd_go_bootstrap -// +build !cmd_go_bootstrap package modfetch @@ -201,7 +200,8 @@ func (c *dbClient) ReadConfig(file string) (data []byte, err error) { } if cfg.SumdbDir == "" { - return nil, errors.New("could not locate sumdb file: missing $GOPATH") + return nil, fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s", + cfg.GoPathError) } targ := filepath.Join(cfg.SumdbDir, file) data, err = lockedfile.Read(targ) @@ -220,7 +220,8 @@ func (*dbClient) WriteConfig(file string, old, new []byte) error { return fmt.Errorf("cannot write key") } if cfg.SumdbDir == "" { - return errors.New("could not locate sumdb file: missing $GOPATH") + return fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s", + cfg.GoPathError) } targ := filepath.Join(cfg.SumdbDir, file) os.MkdirAll(filepath.Dir(targ), 0777) diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 9672e5598e0d3a..08a474f61b3ab5 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.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 modget implements the module-aware ``go get'' command. +// Package modget implements the module-aware “go get” command. package modget // The arguments to 'go get' are patterns with optional version queries, with @@ -37,7 +37,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/imports" - "cmd/go/internal/load" "cmd/go/internal/modfetch" "cmd/go/internal/modload" "cmd/go/internal/par" @@ -50,14 +49,14 @@ import ( ) var CmdGet = &base.Command{ - // Note: -d -u are listed explicitly because they are the most common get flags. + // Note: flags below are listed explicitly because they're the most common. // Do not send CLs removing them because they're covered by [get flags]. - UsageLine: "go get [-d] [-t] [-u] [-v] [build flags] [packages]", + UsageLine: "go get [-t] [-u] [-v] [build flags] [packages]", Short: "add dependencies to current module and install them", Long: ` Get resolves its command-line arguments to packages at specific module versions, -updates go.mod to require those versions, downloads source code into the -module cache, then builds and installs the named packages. +updates go.mod to require those versions, and downloads source code into the +module cache. To add a dependency for a package or upgrade it to its latest version: @@ -73,17 +72,18 @@ To remove a dependency on a module and downgrade modules that require it: See https://golang.org/ref/mod#go-get for details. -The 'go install' command may be used to build and install packages. When a -version is specified, 'go install' runs in module-aware mode and ignores -the go.mod file in the current directory. For example: +In earlier versions of Go, 'go get' was used to build and install packages. +Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install' +may be used to build and install commands instead. When a version is specified, +'go install' runs in module-aware mode and ignores the go.mod file in the +current directory. For example: go install example.com/pkg@v1.2.3 go install example.com/pkg@latest See 'go help install' or https://golang.org/ref/mod#go-install for details. -In addition to build flags (listed in 'go help build') 'go get' accepts the -following flags. +'go get' accepts the following flags. The -t flag instructs get to consider modules needed to build tests of packages specified on the command line. @@ -98,15 +98,9 @@ but changes the default to select patch releases. When the -t and -u flags are used together, get will update test dependencies as well. -The -d flag instructs get not to build or install packages. get will only -update go.mod and download source code needed to build packages. - -Building and installing packages with get is deprecated. In a future release, -the -d flag will be enabled by default, and 'go get' will be only be used to -adjust dependencies of the current module. To install a package using -dependencies from the current module, use 'go install'. To install a package -ignoring the current module, use 'go install' with an @version suffix like -"@latest" after each argument. +The -x flag prints commands as they are executed. This is useful for +debugging version control commands when a module is downloaded directly +from a repository. For more about modules, see https://golang.org/ref/mod. @@ -218,7 +212,7 @@ variable for future go command invocations. } var ( - getD = CmdGet.Flag.Bool("d", false, "") + getD = CmdGet.Flag.Bool("d", true, "") getF = CmdGet.Flag.Bool("f", false, "") getFix = CmdGet.Flag.Bool("fix", false, "") getM = CmdGet.Flag.Bool("m", false, "") @@ -263,30 +257,50 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { case "", "upgrade", "patch": // ok default: - base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion) + base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion) + } + // TODO(#43684): in the future (Go 1.20), warn that -d is a no-op. + if !*getD { + base.Fatalf("go: -d flag may not be disabled") } if *getF { - fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -f flag is a no-op when using modules\n") } if *getFix { - fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op when using modules\n") } if *getM { - base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages") + base.Fatalf("go: -m flag is no longer supported") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } + modload.ForceUseModules = true + // Do not allow any updating of go.mod until we've applied // all the requested changes and checked that the result matches // what was requested. - modload.DisallowWriteGoMod() + modload.ExplicitWriteGoMod = true // Allow looking up modules for import paths when outside of a module. // 'go get' is expected to do this, unlike other commands. modload.AllowMissingModuleImports() + // 'go get' no longer builds or installs packages, so there's nothing to do + // if there's no go.mod file. + // TODO(#40775): make modload.Init return ErrNoModRoot instead of exiting. + // We could handle that here by printing a different message. + modload.Init() + if !modload.HasModRoot() { + base.Fatalf("go: go.mod file not found in current directory or any parent directory.\n" + + "\t'go get' is no longer supported outside a module.\n" + + "\tTo build and install a command, use 'go install' with a version,\n" + + "\tlike 'go install example.com/cmd@latest'\n" + + "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n" + + "\tor run 'go help get' or 'go help install'.") + } + queries := parseArgs(ctx, args) r := newResolver(ctx, queries) @@ -356,74 +370,12 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } r.checkPackageProblems(ctx, pkgPatterns) - // We've already downloaded modules (and identified direct and indirect - // dependencies) by loading packages in findAndUpgradeImports. - // So if -d is set, we're done after the module work. - // - // Otherwise, we need to build and install the packages matched by - // command line arguments. - // Note that 'go get -u' without arguments is equivalent to - // 'go get -u .', so we'll typically build the package in the current - // directory. - if !*getD && len(pkgPatterns) > 0 { - work.BuildInit() - - pkgOpts := load.PackageOpts{ModResolveTests: *getT} - var pkgs []*load.Package - for _, pkg := range load.PackagesAndErrors(ctx, pkgOpts, pkgPatterns) { - if pkg.Error != nil { - var noGo *load.NoGoError - if errors.As(pkg.Error.Err, &noGo) { - if m := modload.PackageModule(pkg.ImportPath); m.Path == pkg.ImportPath { - // pkg is at the root of a module, and doesn't exist with the current - // build tags. Probably the user just wanted to change the version of - // that module — not also build the package — so suppress the error. - // (See https://golang.org/issue/33526.) - continue - } - } - } - pkgs = append(pkgs, pkg) - } - load.CheckPackageErrors(pkgs) - - haveExternalExe := false - for _, pkg := range pkgs { - if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path { - haveExternalExe = true - break - } - } - if haveExternalExe { - fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.") - var altMsg string - if modload.HasModRoot() { - altMsg = ` - To adjust and download dependencies of the current module, use 'go get -d'. - To install using requirements of the current module, use 'go install'. - To install ignoring the current module, use 'go install' with a version, - like 'go install example.com/cmd@latest'. -` - } else { - altMsg = "\n\tUse 'go install pkg@version' instead.\n" - } - fmt.Fprint(os.Stderr, altMsg) - fmt.Fprintf(os.Stderr, "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n\tor run 'go help get' or 'go help install'.\n") - } - - work.InstallPackages(ctx, pkgPatterns, pkgs) - } - - if !modload.HasModRoot() { - return - } - // Everything succeeded. Update go.mod. oldReqs := reqsFromGoMod(modload.ModFile()) - modload.AllowWriteGoMod() - modload.WriteGoMod(ctx) - modload.DisallowWriteGoMod() + if err := modload.WriteGoMod(ctx); err != nil { + base.Fatalf("go: %v", err) + } newReqs := reqsFromGoMod(modload.ModFile()) r.reportChanges(oldReqs, newReqs) @@ -440,7 +392,7 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { for _, arg := range search.CleanPatterns(rawArgs) { q, err := newQuery(arg) if err != nil { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) continue } @@ -455,11 +407,11 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { // if the argument has no version and either has no slash or refers to an existing file. if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" { if !strings.Contains(q.raw, "/") { - base.Errorf("go get %s: arguments must be package or module paths", q.raw) + base.Errorf("go: %s: arguments must be package or module paths", q.raw) continue } if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw) continue } } @@ -649,7 +601,7 @@ func (r *resolver) matchInModule(ctx context.Context, pattern string, m module.V err error } - e := r.matchInModuleCache.Do(key{pattern, m}, func() interface{} { + e := r.matchInModuleCache.Do(key{pattern, m}, func() any { match := modload.MatchInModule(ctx, pattern, m, imports.AnyTags()) if len(match.Errs) > 0 { return entry{match.Pkgs, match.Errs[0]} @@ -675,7 +627,9 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { if !q.isWildcard() { q.pathOnce(q.pattern, func() pathSet { - if modload.HasModRoot() && q.pattern == modload.Target.Path { + hasModRoot := modload.HasModRoot() + if hasModRoot && modload.MainModules.Contains(q.pattern) { + v := module.Version{Path: q.pattern} // The user has explicitly requested to downgrade their own module to // version "none". This is not an entirely unreasonable request: it // could plausibly mean “downgrade away everything that depends on any @@ -686,7 +640,7 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { // However, neither of those behaviors would be consistent with the // plain meaning of the query. To try to reduce confusion, reject the // query explicitly. - return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version}) + return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{v}, Pattern: q.pattern, Query: q.version}) } return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}} @@ -698,8 +652,8 @@ func (r *resolver) queryNone(ctx context.Context, q *query) { continue } q.pathOnce(curM.Path, func() pathSet { - if modload.HasModRoot() && curM == modload.Target { - return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version}) + if modload.HasModRoot() && curM.Version == "" && modload.MainModules.Contains(curM.Path) { + return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{curM}, Pattern: q.pattern, Query: q.version}) } return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}} }) @@ -718,28 +672,38 @@ func (r *resolver) performLocalQueries(ctx context.Context) { // Absolute paths like C:\foo and relative paths like ../foo... are // restricted to matching packages in the main module. - pkgPattern := modload.DirImportPath(ctx, q.pattern) + pkgPattern, mainModule := modload.MainModules.DirImportPath(ctx, q.pattern) if pkgPattern == "." { - return errSet(fmt.Errorf("%s%s is not within module rooted at %s", q.pattern, absDetail, modload.ModRoot())) + modload.MustHaveModRoot() + var modRoots []string + for _, m := range modload.MainModules.Versions() { + modRoots = append(modRoots, modload.MainModules.ModRoot(m)) + } + var plural string + if len(modRoots) != 1 { + plural = "s" + } + return errSet(fmt.Errorf("%s%s is not within module%s rooted at %s", q.pattern, absDetail, plural, strings.Join(modRoots, ", "))) } - match := modload.MatchInModule(ctx, pkgPattern, modload.Target, imports.AnyTags()) + match := modload.MatchInModule(ctx, pkgPattern, mainModule, imports.AnyTags()) if len(match.Errs) > 0 { return pathSet{err: match.Errs[0]} } if len(match.Pkgs) == 0 { if q.raw == "" || q.raw == "." { - return errSet(fmt.Errorf("no package in current directory")) + return errSet(fmt.Errorf("no package to get in current directory")) } if !q.isWildcard() { - return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.ModRoot())) + modload.MustHaveModRoot() + return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.MainModules.ModRoot(mainModule))) } search.WarnUnmatched([]*search.Match{match}) return pathSet{} } - return pathSet{pkgMods: []module.Version{modload.Target}} + return pathSet{pkgMods: []module.Version{mainModule}} }) } } @@ -767,10 +731,10 @@ func (r *resolver) performWildcardQueries(ctx context.Context) { } // queryWildcard adds a candidate set to q for each module for which: -// - some version of the module is already in the build list, and -// - that module exists at some version matching q.version, and -// - either the module path itself matches q.pattern, or some package within -// the module at q.version matches q.pattern. +// - some version of the module is already in the build list, and +// - that module exists at some version matching q.version, and +// - either the module path itself matches q.pattern, or some package within +// the module at q.version matches q.pattern. func (r *resolver) queryWildcard(ctx context.Context, q *query) { // For wildcard patterns, modload.QueryPattern only identifies modules // matching the prefix of the path before the wildcard. However, the build @@ -789,11 +753,12 @@ func (r *resolver) queryWildcard(ctx context.Context, q *query) { return pathSet{} } - if curM.Path == modload.Target.Path && !versionOkForMainModule(q.version) { + if modload.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) { if q.matchesPath(curM.Path) { - return errSet(&modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + return errSet(&modload.QueryMatchesMainModulesError{ + MainModules: []module.Version{curM}, + Pattern: q.pattern, + Query: q.version, }) } @@ -928,7 +893,7 @@ func (r *resolver) checkWildcardVersions(ctx context.Context) { // curM at its original version contains a path matching q.pattern, // but at rev.Version it does not, so (somewhat paradoxically) if // we changed the version of curM it would no longer match the query. - var version interface{} = m + var version any = m if rev.Version != q.version { version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version) } @@ -1159,8 +1124,8 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack } opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error { - if m.Path == "" || m == modload.Target { - // Packages in the standard library and main module are already at their + if m.Path == "" || m.Version == "" { + // Packages in the standard library and main modules are already at their // latest (and only) available versions. return nil } @@ -1327,7 +1292,7 @@ func (r *resolver) applyUpgrades(ctx context.Context, upgrades []pathSet) (chang var tentative []module.Version for _, cs := range upgrades { if cs.err != nil { - base.Errorf("go get: %v", cs.err) + base.Errorf("go: %v", cs.err) continue } @@ -1370,11 +1335,11 @@ func (r *resolver) disambiguate(cs pathSet) (filtered pathSet, isPackage bool, m continue } - if m.Path == modload.Target.Path { - if m.Version == modload.Target.Version { + if modload.MainModules.Contains(m.Path) { + if m.Version == "" { return pathSet{}, true, m, true } - // The main module can only be set to its own version. + // A main module can only be set to its own version. continue } @@ -1720,13 +1685,13 @@ func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) { }) for _, c := range sortedChanges { if c.old == "" { - fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new) + fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new) } else if c.new == "none" || c.new == "" { - fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old) + fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old) } else if semver.Compare(c.new, c.old) > 0 { - fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new) } else { - fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new) } } @@ -1744,10 +1709,11 @@ func (r *resolver) resolve(q *query, m module.Version) { panic("internal error: resolving a module.Version with an empty path") } - if m.Path == modload.Target.Path && m.Version != modload.Target.Version { - reportError(q, &modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + if modload.MainModules.Contains(m.Path) && m.Version != "" { + reportError(q, &modload.QueryMatchesMainModulesError{ + MainModules: []module.Version{{Path: m.Path}}, + Pattern: q.pattern, + Query: q.version, }) return } @@ -1775,7 +1741,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi resolved := make([]module.Version, 0, len(r.resolvedVersion)) for mPath, rv := range r.resolvedVersion { - if mPath != modload.Target.Path { + if !modload.MainModules.Contains(mPath) { resolved = append(resolved, module.Version{Path: mPath, Version: rv.version}) } } @@ -1784,7 +1750,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi if err != nil { var constraint *modload.ConstraintError if !errors.As(err, &constraint) { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) return false } @@ -1796,7 +1762,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version}) } for _, c := range constraint.Conflicts { - base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) + base.Errorf("go: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) } return false } diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go index 1a5a60f7eb98fb..887cb51b317f7f 100644 --- a/src/cmd/go/internal/modget/query.go +++ b/src/cmd/go/internal/modget/query.go @@ -192,9 +192,9 @@ func (q *query) validate() error { // TODO(bcmills): "all@none" seems like a totally reasonable way to // request that we remove all module requirements, leaving only the main // module and standard library. Perhaps we should implement that someday. - return &modload.QueryMatchesMainModuleError{ - Pattern: q.pattern, - Query: q.version, + return &modload.QueryUpgradesAllError{ + MainModules: modload.MainModules.Versions(), + Query: q.version, } } } @@ -284,21 +284,21 @@ func reportError(q *query, err error) { patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)") if patternRE.MatchString(errStr) { if q.rawVersion == "" { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)") if versionRE.MatchString(errStr) { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } } if qs := q.String(); qs != "" { - base.Errorf("go get %s: %s", qs, errStr) + base.Errorf("go: %s: %s", qs, errStr) } else { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) } } diff --git a/src/cmd/go/internal/modindex/build.go b/src/cmd/go/internal/modindex/build.go new file mode 100644 index 00000000000000..d6d4ea371ac2b5 --- /dev/null +++ b/src/cmd/go/internal/modindex/build.go @@ -0,0 +1,1021 @@ +// 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. + +// This file is a lightly modified copy go/build/build.go with unused parts +// removed. + +package modindex + +import ( + "bytes" + "cmd/go/internal/fsys" + "errors" + "fmt" + "go/ast" + "go/build/constraint" + "go/token" + "io" + "io/fs" + "path/filepath" + "sort" + "strings" + "unicode" + "unicode/utf8" +) + +// A Context specifies the supporting context for a build. +type Context struct { + GOARCH string // target architecture + GOOS string // target operating system + GOROOT string // Go root + GOPATH string // Go paths + + // Dir is the caller's working directory, or the empty string to use + // the current directory of the running process. In module mode, this is used + // to locate the main module. + // + // If Dir is non-empty, directories passed to Import and ImportDir must + // be absolute. + Dir string + + CgoEnabled bool // whether cgo files are included + UseAllFiles bool // use files regardless of +build lines, file names + Compiler string // compiler to assume when computing target paths + + // The build, tool, and release tags specify build constraints + // that should be considered satisfied when processing +build lines. + // Clients creating a new context may customize BuildTags, which + // defaults to empty, but it is usually an error to customize ToolTags or ReleaseTags. + // ToolTags defaults to build tags appropriate to the current Go toolchain configuration. + // ReleaseTags defaults to the list of Go releases the current release is compatible with. + // BuildTags is not set for the Default build Context. + // In addition to the BuildTags, ToolTags, and ReleaseTags, build constraints + // consider the values of GOARCH and GOOS as satisfied tags. + // The last element in ReleaseTags is assumed to be the current release. + BuildTags []string + ToolTags []string + ReleaseTags []string + + // The install suffix specifies a suffix to use in the name of the installation + // directory. By default it is empty, but custom builds that need to keep + // their outputs separate can set InstallSuffix to do so. For example, when + // using the race detector, the go command uses InstallSuffix = "race", so + // that on a Linux/386 system, packages are written to a directory named + // "linux_386_race" instead of the usual "linux_386". + InstallSuffix string + + // By default, Import uses the operating system's file system calls + // to read directories and files. To read from other sources, + // callers can set the following functions. They all have default + // behaviors that use the local file system, so clients need only set + // the functions whose behaviors they wish to change. + + // JoinPath joins the sequence of path fragments into a single path. + // If JoinPath is nil, Import uses filepath.Join. + JoinPath func(elem ...string) string + + // SplitPathList splits the path list into a slice of individual paths. + // If SplitPathList is nil, Import uses filepath.SplitList. + SplitPathList func(list string) []string + + // IsAbsPath reports whether path is an absolute path. + // If IsAbsPath is nil, Import uses filepath.IsAbs. + IsAbsPath func(path string) bool + + // IsDir reports whether the path names a directory. + // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method. + IsDir func(path string) bool + + // HasSubdir reports whether dir is lexically a subdirectory of + // root, perhaps multiple levels below. It does not try to check + // whether dir exists. + // If so, HasSubdir sets rel to a slash-separated path that + // can be joined to root to produce a path equivalent to dir. + // If HasSubdir is nil, Import uses an implementation built on + // filepath.EvalSymlinks. + HasSubdir func(root, dir string) (rel string, ok bool) + + // ReadDir returns a slice of fs.FileInfo, sorted by Name, + // describing the content of the named directory. + // If ReadDir is nil, Import uses ioutil.ReadDir. + ReadDir func(dir string) ([]fs.FileInfo, error) + + // OpenFile opens a file (not a directory) for reading. + // If OpenFile is nil, Import uses os.Open. + OpenFile func(path string) (io.ReadCloser, error) +} + +// joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join. +func (ctxt *Context) joinPath(elem ...string) string { + if f := ctxt.JoinPath; f != nil { + return f(elem...) + } + return filepath.Join(elem...) +} + +// splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList. +func (ctxt *Context) splitPathList(s string) []string { + if f := ctxt.SplitPathList; f != nil { + return f(s) + } + return filepath.SplitList(s) +} + +// isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs. +func (ctxt *Context) isAbsPath(path string) bool { + if f := ctxt.IsAbsPath; f != nil { + return f(path) + } + return filepath.IsAbs(path) +} + +// isDir calls ctxt.IsDir (if not nil) or else uses fsys.Stat. +func isDir(path string) bool { + fi, err := fsys.Stat(path) + return err == nil && fi.IsDir() +} + +// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses +// the local file system to answer the question. +func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) { + if f := ctxt.HasSubdir; f != nil { + return f(root, dir) + } + + // Try using paths we received. + if rel, ok = hasSubdir(root, dir); ok { + return + } + + // Try expanding symlinks and comparing + // expanded against unexpanded and + // expanded against expanded. + rootSym, _ := filepath.EvalSymlinks(root) + dirSym, _ := filepath.EvalSymlinks(dir) + + if rel, ok = hasSubdir(rootSym, dir); ok { + return + } + if rel, ok = hasSubdir(root, dirSym); ok { + return + } + return hasSubdir(rootSym, dirSym) +} + +// hasSubdir reports if dir is within root by performing lexical analysis only. +func hasSubdir(root, dir string) (rel string, ok bool) { + const sep = string(filepath.Separator) + root = filepath.Clean(root) + if !strings.HasSuffix(root, sep) { + root += sep + } + dir = filepath.Clean(dir) + if !strings.HasPrefix(dir, root) { + return "", false + } + return filepath.ToSlash(dir[len(root):]), true +} + +// gopath returns the list of Go path directories. +func (ctxt *Context) gopath() []string { + var all []string + for _, p := range ctxt.splitPathList(ctxt.GOPATH) { + if p == "" || p == ctxt.GOROOT { + // Empty paths are uninteresting. + // If the path is the GOROOT, ignore it. + // People sometimes set GOPATH=$GOROOT. + // Do not get confused by this common mistake. + continue + } + if strings.HasPrefix(p, "~") { + // Path segments starting with ~ on Unix are almost always + // users who have incorrectly quoted ~ while setting GOPATH, + // preventing it from expanding to $HOME. + // The situation is made more confusing by the fact that + // bash allows quoted ~ in $PATH (most shells do not). + // Do not get confused by this, and do not try to use the path. + // It does not exist, and printing errors about it confuses + // those users even more, because they think "sure ~ exists!". + // The go command diagnoses this situation and prints a + // useful error. + // On Windows, ~ is used in short names, such as c:\progra~1 + // for c:\program files. + continue + } + all = append(all, p) + } + return all +} + +var defaultToolTags, defaultReleaseTags []string + +// A Package describes the Go package found in a directory. +type Package struct { + Dir string // directory containing package sources + Name string // package name + ImportComment string // path in import comment on package statement + Doc string // documentation synopsis + ImportPath string // import path of package ("" if unknown) + Root string // root of Go tree where this package lives + SrcRoot string // package source root directory ("" if unknown) + PkgRoot string // package install root directory ("" if unknown) + PkgTargetRoot string // architecture dependent install root directory ("" if unknown) + BinDir string // command install directory ("" if unknown) + Goroot bool // package found in Go root + PkgObj string // installed .a file + AllTags []string // tags that can influence file selection in this directory + ConflictDir string // this directory shadows Dir in $GOPATH + BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment) + + // Source files + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go source files that import "C" + IgnoredGoFiles []string // .go source files ignored for this build (including ignored _test.go files) + InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on) + IgnoredOtherFiles []string // non-.go source files ignored for this build + CFiles []string // .c source files + CXXFiles []string // .cc, .cpp and .cxx source files + MFiles []string // .m (Objective-C) source files + HFiles []string // .h, .hh, .hpp and .hxx source files + FFiles []string // .f, .F, .for and .f90 Fortran source files + SFiles []string // .s source files + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso system object files to add to archive + + // Cgo directives + CgoCFLAGS []string // Cgo CFLAGS directives + CgoCPPFLAGS []string // Cgo CPPFLAGS directives + CgoCXXFLAGS []string // Cgo CXXFLAGS directives + CgoFFLAGS []string // Cgo FFLAGS directives + CgoLDFLAGS []string // Cgo LDFLAGS directives + CgoPkgConfig []string // Cgo pkg-config directives + + // Test information + TestGoFiles []string // _test.go files in package + XTestGoFiles []string // _test.go files outside package + + // Dependency information + Imports []string // import paths from GoFiles, CgoFiles + ImportPos map[string][]token.Position // line information for Imports + TestImports []string // import paths from TestGoFiles + TestImportPos map[string][]token.Position // line information for TestImports + XTestImports []string // import paths from XTestGoFiles + XTestImportPos map[string][]token.Position // line information for XTestImports + + // //go:embed patterns found in Go source files + // For example, if a source file says + // //go:embed a* b.c + // then the list will contain those two strings as separate entries. + // (See package embed for more details about //go:embed.) + EmbedPatterns []string // patterns from GoFiles, CgoFiles + EmbedPatternPos map[string][]token.Position // line information for EmbedPatterns + TestEmbedPatterns []string // patterns from TestGoFiles + TestEmbedPatternPos map[string][]token.Position // line information for TestEmbedPatterns + XTestEmbedPatterns []string // patterns from XTestGoFiles + XTestEmbedPatternPos map[string][]token.Position // line information for XTestEmbedPatternPos +} + +// IsCommand reports whether the package is considered a +// command to be installed (not just a library). +// Packages named "main" are treated as commands. +func (p *Package) IsCommand() bool { + return p.Name == "main" +} + +// NoGoError is the error used by Import to describe a directory +// containing no buildable Go source files. (It may still contain +// test files, files hidden by build tags, and so on.) +type NoGoError struct { + Dir string +} + +func (e *NoGoError) Error() string { + return "no buildable Go source files in " + e.Dir +} + +// MultiplePackageError describes a directory containing +// multiple buildable Go source files for multiple packages. +type MultiplePackageError struct { + Dir string // directory containing files + Packages []string // package names found + Files []string // corresponding files: Files[i] declares package Packages[i] +} + +func (e *MultiplePackageError) Error() string { + // Error string limited to two entries for compatibility. + return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir) +} + +func nameExt(name string) string { + i := strings.LastIndex(name, ".") + if i < 0 { + return "" + } + return name[i:] +} + +func fileListForExt(p *Package, ext string) *[]string { + switch ext { + case ".c": + return &p.CFiles + case ".cc", ".cpp", ".cxx": + return &p.CXXFiles + case ".m": + return &p.MFiles + case ".h", ".hh", ".hpp", ".hxx": + return &p.HFiles + case ".f", ".F", ".for", ".f90": + return &p.FFiles + case ".s", ".S", ".sx": + return &p.SFiles + case ".swig": + return &p.SwigFiles + case ".swigcxx": + return &p.SwigCXXFiles + case ".syso": + return &p.SysoFiles + } + return nil +} + +var errNoModules = errors.New("not using modules") + +func findImportComment(data []byte) (s string, line int) { + // expect keyword package + word, data := parseWord(data) + if string(word) != "package" { + return "", 0 + } + + // expect package name + _, data = parseWord(data) + + // now ready for import comment, a // or /* */ comment + // beginning and ending on the current line. + for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') { + data = data[1:] + } + + var comment []byte + switch { + case bytes.HasPrefix(data, slashSlash): + comment, _, _ = bytes.Cut(data[2:], newline) + case bytes.HasPrefix(data, slashStar): + var ok bool + comment, _, ok = bytes.Cut(data[2:], starSlash) + if !ok { + // malformed comment + return "", 0 + } + if bytes.Contains(comment, newline) { + return "", 0 + } + } + comment = bytes.TrimSpace(comment) + + // split comment into `import`, `"pkg"` + word, arg := parseWord(comment) + if string(word) != "import" { + return "", 0 + } + + line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline) + return strings.TrimSpace(string(arg)), line +} + +var ( + slashSlash = []byte("//") + slashStar = []byte("/*") + starSlash = []byte("*/") + newline = []byte("\n") +) + +// skipSpaceOrComment returns data with any leading spaces or comments removed. +func skipSpaceOrComment(data []byte) []byte { + for len(data) > 0 { + switch data[0] { + case ' ', '\t', '\r', '\n': + data = data[1:] + continue + case '/': + if bytes.HasPrefix(data, slashSlash) { + i := bytes.Index(data, newline) + if i < 0 { + return nil + } + data = data[i+1:] + continue + } + if bytes.HasPrefix(data, slashStar) { + data = data[2:] + i := bytes.Index(data, starSlash) + if i < 0 { + return nil + } + data = data[i+2:] + continue + } + } + break + } + return data +} + +// parseWord skips any leading spaces or comments in data +// and then parses the beginning of data as an identifier or keyword, +// returning that word and what remains after the word. +func parseWord(data []byte) (word, rest []byte) { + data = skipSpaceOrComment(data) + + // Parse past leading word characters. + rest = data + for { + r, size := utf8.DecodeRune(rest) + if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' { + rest = rest[size:] + continue + } + break + } + + word = data[:len(data)-len(rest)] + if len(word) == 0 { + return nil, nil + } + + return word, rest +} + +var dummyPkg Package + +// fileInfo records information learned about a file included in a build. +type fileInfo struct { + name string // full name including dir + header []byte + fset *token.FileSet + parsed *ast.File + parseErr error + imports []fileImport + embeds []fileEmbed + + // Additional fields added to go/build's fileinfo for the purposes of the modindex package. + binaryOnly bool + goBuildConstraint string + plusBuildConstraints []string +} + +type fileImport struct { + path string + pos token.Pos + doc *ast.CommentGroup +} + +type fileEmbed struct { + pattern string + pos token.Position +} + +// getFileInfo extracts the information needed from each go file for the module +// index. +// +// If Name denotes a Go program, matchFile reads until the end of the +// Imports and returns that section of the file in the FileInfo's Header field, +// even though it only considers text until the first non-comment +// for +build lines. +func getFileInfo(dir, name string, fset *token.FileSet) (*fileInfo, error) { + if strings.HasPrefix(name, "_") || + strings.HasPrefix(name, ".") { + return nil, nil + } + + i := strings.LastIndex(name, ".") + if i < 0 { + i = len(name) + } + ext := name[i:] + + if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil { + // skip + return nil, nil + } + + info := &fileInfo{name: filepath.Join(dir, name), fset: fset} + if ext == ".syso" { + // binary, no reading + return info, nil + } + + f, err := fsys.Open(info.name) + if err != nil { + return nil, err + } + + // TODO(matloob) should we decide whether to ignore binary only here or earlier + // when we create the index file? + var ignoreBinaryOnly bool + if strings.HasSuffix(name, ".go") { + err = readGoInfo(f, info) + if strings.HasSuffix(name, "_test.go") { + ignoreBinaryOnly = true // ignore //go:binary-only-package comments in _test.go files + } + } else { + info.header, err = readComments(f) + } + f.Close() + if err != nil { + return nil, fmt.Errorf("read %s: %v", info.name, err) + } + + // Look for +build comments to accept or reject the file. + info.goBuildConstraint, info.plusBuildConstraints, info.binaryOnly, err = getConstraints(info.header) + if err != nil { + return nil, fmt.Errorf("%s: %v", name, err) + } + + if ignoreBinaryOnly && info.binaryOnly { + info.binaryOnly = false // override info.binaryOnly + } + + return info, nil +} + +func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) { + all := make([]string, 0, len(m)) + for path := range m { + all = append(all, path) + } + sort.Strings(all) + return all, m +} + +var ( + bSlashSlash = []byte(slashSlash) + bStarSlash = []byte(starSlash) + bSlashStar = []byte(slashStar) + bPlusBuild = []byte("+build") + + goBuildComment = []byte("//go:build") + + errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment") + errMultipleGoBuild = errors.New("multiple //go:build comments") +) + +func isGoBuildComment(line []byte) bool { + if !bytes.HasPrefix(line, goBuildComment) { + return false + } + line = bytes.TrimSpace(line) + rest := line[len(goBuildComment):] + return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest) +} + +// Special comment denoting a binary-only package. +// See https://golang.org/design/2775-binary-only-packages +// for more about the design of binary-only packages. +var binaryOnlyComment = []byte("//go:binary-only-package") + +func getConstraints(content []byte) (goBuild string, plusBuild []string, binaryOnly bool, err error) { + // Identify leading run of // comments and blank lines, + // which must be followed by a blank line. + // Also identify any //go:build comments. + content, goBuildBytes, sawBinaryOnly, err := parseFileHeader(content) + if err != nil { + return "", nil, false, err + } + + // If //go:build line is present, it controls, so no need to look for +build . + // Otherwise, get plusBuild constraints. + if goBuildBytes == nil { + p := content + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) { + continue + } + text := string(line) + if !constraint.IsPlusBuild(text) { + continue + } + plusBuild = append(plusBuild, text) + } + } + + return string(goBuildBytes), plusBuild, sawBinaryOnly, nil +} + +func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) { + end := 0 + p := content + ended := false // found non-blank, non-// line, so stopped accepting // +build lines + inSlashStar := false // in /* */ comment + +Lines: + for len(p) > 0 { + line := p + if i := bytes.IndexByte(line, '\n'); i >= 0 { + line, p = line[:i], p[i+1:] + } else { + p = p[len(p):] + } + line = bytes.TrimSpace(line) + if len(line) == 0 && !ended { // Blank line + // Remember position of most recent blank line. + // When we find the first non-blank, non-// line, + // this "end" position marks the latest file position + // where a // +build line can appear. + // (It must appear _before_ a blank line before the non-blank, non-// line. + // Yes, that's confusing, which is part of why we moved to //go:build lines.) + // Note that ended==false here means that inSlashStar==false, + // since seeing a /* would have set ended==true. + end = len(content) - len(p) + continue Lines + } + if !bytes.HasPrefix(line, slashSlash) { // Not comment line + ended = true + } + + if !inSlashStar && isGoBuildComment(line) { + if goBuild != nil { + return nil, nil, false, errMultipleGoBuild + } + goBuild = line + } + if !inSlashStar && bytes.Equal(line, binaryOnlyComment) { + sawBinaryOnly = true + } + + Comments: + for len(line) > 0 { + if inSlashStar { + if i := bytes.Index(line, starSlash); i >= 0 { + inSlashStar = false + line = bytes.TrimSpace(line[i+len(starSlash):]) + continue Comments + } + continue Lines + } + if bytes.HasPrefix(line, bSlashSlash) { + continue Lines + } + if bytes.HasPrefix(line, bSlashStar) { + inSlashStar = true + line = bytes.TrimSpace(line[len(bSlashStar):]) + continue Comments + } + // Found non-comment text. + break Lines + } + } + + return content[:end], goBuild, sawBinaryOnly, nil +} + +// saveCgo saves the information from the #cgo lines in the import "C" comment. +// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives +// that affect the way cgo's C code is built. +func (ctxt *Context) saveCgo(filename string, di *Package, text string) error { + for _, line := range strings.Split(text, "\n") { + orig := line + + // Line is + // #cgo [GOOS/GOARCH...] LDFLAGS: stuff + // + line = strings.TrimSpace(line) + if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { + continue + } + + // Split at colon. + line, argstr, ok := strings.Cut(strings.TrimSpace(line[4:]), ":") + if !ok { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + + // Parse GOOS/GOARCH stuff. + f := strings.Fields(line) + if len(f) < 1 { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + + cond, verb := f[:len(f)-1], f[len(f)-1] + if len(cond) > 0 { + ok := false + for _, c := range cond { + if ctxt.matchAuto(c, nil) { + ok = true + break + } + } + if !ok { + continue + } + } + + args, err := splitQuoted(argstr) + if err != nil { + return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) + } + for i, arg := range args { + if arg, ok = expandSrcDir(arg, di.Dir); !ok { + return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) + } + args[i] = arg + } + + switch verb { + case "CFLAGS", "CPPFLAGS", "CXXFLAGS", "FFLAGS", "LDFLAGS": + // Change relative paths to absolute. + ctxt.makePathsAbsolute(args, di.Dir) + } + + switch verb { + case "CFLAGS": + di.CgoCFLAGS = append(di.CgoCFLAGS, args...) + case "CPPFLAGS": + di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) + case "CXXFLAGS": + di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) + case "FFLAGS": + di.CgoFFLAGS = append(di.CgoFFLAGS, args...) + case "LDFLAGS": + di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) + case "pkg-config": + di.CgoPkgConfig = append(di.CgoPkgConfig, args...) + default: + return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig) + } + } + return nil +} + +// expandSrcDir expands any occurrence of ${SRCDIR}, making sure +// the result is safe for the shell. +func expandSrcDir(str string, srcdir string) (string, bool) { + // "\" delimited paths cause safeCgoName to fail + // so convert native paths with a different delimiter + // to "/" before starting (eg: on windows). + srcdir = filepath.ToSlash(srcdir) + + chunks := strings.Split(str, "${SRCDIR}") + if len(chunks) < 2 { + return str, safeCgoName(str) + } + ok := true + for _, chunk := range chunks { + ok = ok && (chunk == "" || safeCgoName(chunk)) + } + ok = ok && (srcdir == "" || safeCgoName(srcdir)) + res := strings.Join(chunks, srcdir) + return res, ok && res != "" +} + +// makePathsAbsolute looks for compiler options that take paths and +// makes them absolute. We do this because through the 1.8 release we +// ran the compiler in the package directory, so any relative -I or -L +// options would be relative to that directory. In 1.9 we changed to +// running the compiler in the build directory, to get consistent +// build results (issue #19964). To keep builds working, we change any +// relative -I or -L options to be absolute. +// +// Using filepath.IsAbs and filepath.Join here means the results will be +// different on different systems, but that's OK: -I and -L options are +// inherently system-dependent. +func (ctxt *Context) makePathsAbsolute(args []string, srcDir string) { + nextPath := false + for i, arg := range args { + if nextPath { + if !filepath.IsAbs(arg) { + args[i] = filepath.Join(srcDir, arg) + } + nextPath = false + } else if strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-L") { + if len(arg) == 2 { + nextPath = true + } else { + if !filepath.IsAbs(arg[2:]) { + args[i] = arg[:2] + filepath.Join(srcDir, arg[2:]) + } + } + } + } +} + +// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN. +// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay. +// See golang.org/issue/6038. +// The @ is for OS X. See golang.org/issue/13720. +// The % is for Jenkins. See golang.org/issue/16959. +// The ! is because module paths may use them. See golang.org/issue/26716. +// The ~ and ^ are for sr.ht. See golang.org/issue/32260. +const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%! ~^" + +func safeCgoName(s string) bool { + if s == "" { + return false + } + for i := 0; i < len(s); i++ { + if c := s[i]; c < utf8.RuneSelf && strings.IndexByte(safeString, c) < 0 { + return false + } + } + return true +} + +// splitQuoted splits the string s around each instance of one or more consecutive +// white space characters while taking into account quotes and escaping, and +// returns an array of substrings of s or an empty list if s contains only white space. +// Single quotes and double quotes are recognized to prevent splitting within the +// quoted region, and are removed from the resulting substrings. If a quote in s +// isn't closed err will be set and r will have the unclosed argument as the +// last element. The backslash is used for escaping. +// +// For example, the following string: +// +// a b:"c d" 'e''f' "g\"" +// +// Would be parsed as: +// +// []string{"a", "b:c d", "ef", `g"`} +func splitQuoted(s string) (r []string, err error) { + var args []string + arg := make([]rune, len(s)) + escaped := false + quoted := false + quote := '\x00' + i := 0 + for _, rune := range s { + switch { + case escaped: + escaped = false + case rune == '\\': + escaped = true + continue + case quote != '\x00': + if rune == quote { + quote = '\x00' + continue + } + case rune == '"' || rune == '\'': + quoted = true + quote = rune + continue + case unicode.IsSpace(rune): + if quoted || i > 0 { + quoted = false + args = append(args, string(arg[:i])) + i = 0 + } + continue + } + arg[i] = rune + i++ + } + if quoted || i > 0 { + args = append(args, string(arg[:i])) + } + if quote != 0 { + err = errors.New("unclosed quote") + } else if escaped { + err = errors.New("unfinished escaping") + } + return args, err +} + +// matchAuto interprets text as either a +build or //go:build expression (whichever works), +// reporting whether the expression matches the build context. +// +// matchAuto is only used for testing of tag evaluation +// and in #cgo lines, which accept either syntax. +func (ctxt *Context) matchAuto(text string, allTags map[string]bool) bool { + if strings.ContainsAny(text, "&|()") { + text = "//go:build " + text + } else { + text = "// +build " + text + } + x, err := constraint.Parse(text) + if err != nil { + return false + } + return ctxt.eval(x, allTags) +} + +func (ctxt *Context) eval(x constraint.Expr, allTags map[string]bool) bool { + return x.Eval(func(tag string) bool { return ctxt.matchTag(tag, allTags) }) +} + +// matchTag reports whether the name is one of: +// +// cgo (if cgo is enabled) +// $GOOS +// $GOARCH +// boringcrypto +// ctxt.Compiler +// linux (if GOOS == android) +// solaris (if GOOS == illumos) +// tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags) +// +// It records all consulted tags in allTags. +func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { + if allTags != nil { + allTags[name] = true + } + + // special tags + if ctxt.CgoEnabled && name == "cgo" { + return true + } + if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler { + return true + } + if ctxt.GOOS == "android" && name == "linux" { + return true + } + if ctxt.GOOS == "illumos" && name == "solaris" { + return true + } + if ctxt.GOOS == "ios" && name == "darwin" { + return true + } + if name == "unix" && unixOS[ctxt.GOOS] { + return true + } + if name == "boringcrypto" { + name = "goexperiment.boringcrypto" // boringcrypto is an old name for goexperiment.boringcrypto + } + + // other tags + for _, tag := range ctxt.BuildTags { + if tag == name { + return true + } + } + for _, tag := range ctxt.ToolTags { + if tag == name { + return true + } + } + for _, tag := range ctxt.ReleaseTags { + if tag == name { + return true + } + } + + return false +} + +// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH +// suffix which does not match the current system. +// The recognized name formats are: +// +// name_$(GOOS).* +// name_$(GOARCH).* +// name_$(GOOS)_$(GOARCH).* +// name_$(GOOS)_test.* +// name_$(GOARCH)_test.* +// name_$(GOOS)_$(GOARCH)_test.* +// +// Exceptions: +// if GOOS=android, then files with GOOS=linux are also matched. +// if GOOS=illumos, then files with GOOS=solaris are also matched. +// if GOOS=ios, then files with GOOS=darwin are also matched. +func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { + name, _, _ = strings.Cut(name, ".") + + // Before Go 1.4, a file called "linux.go" would be equivalent to having a + // build tag "linux" in that file. For Go 1.4 and beyond, we require this + // auto-tagging to apply only to files with a non-empty prefix, so + // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating + // systems, such as android, to arrive without breaking existing code with + // innocuous source code in "android.go". The easiest fix: cut everything + // in the name before the initial _. + i := strings.Index(name, "_") + if i < 0 { + return true + } + name = name[i:] // ignore everything before first _ + + l := strings.Split(name, "_") + if n := len(l); n > 0 && l[n-1] == "test" { + l = l[:n-1] + } + n := len(l) + if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { + if allTags != nil { + // In case we short-circuit on l[n-1]. + allTags[l[n-2]] = true + } + return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags) + } + if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) { + return ctxt.matchTag(l[n-1], allTags) + } + return true +} diff --git a/src/cmd/go/internal/modindex/build_read.go b/src/cmd/go/internal/modindex/build_read.go new file mode 100644 index 00000000000000..d2fdfcb6d7ab59 --- /dev/null +++ b/src/cmd/go/internal/modindex/build_read.go @@ -0,0 +1,581 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is a lightly modified copy go/build/read.go with unused parts +// removed. + +package modindex + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +type importReader struct { + b *bufio.Reader + buf []byte + peek byte + err error + eof bool + nerr int + pos token.Position +} + +var bom = []byte{0xef, 0xbb, 0xbf} + +func newImportReader(name string, r io.Reader) *importReader { + b := bufio.NewReader(r) + // Remove leading UTF-8 BOM. + // Per https://golang.org/ref/spec#Source_code_representation: + // a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF) + // if it is the first Unicode code point in the source text. + if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) { + b.Discard(3) + } + return &importReader{ + b: b, + pos: token.Position{ + Filename: name, + Line: 1, + Column: 1, + }, + } +} + +func isIdent(c byte) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf +} + +var ( + errSyntax = errors.New("syntax error") + errNUL = errors.New("unexpected NUL in input") +) + +// syntaxError records a syntax error, but only if an I/O error has not already been recorded. +func (r *importReader) syntaxError() { + if r.err == nil { + r.err = errSyntax + } +} + +// readByte reads the next byte from the input, saves it in buf, and returns it. +// If an error occurs, readByte records the error in r.err and returns 0. +func (r *importReader) readByte() byte { + c, err := r.b.ReadByte() + if err == nil { + r.buf = append(r.buf, c) + if c == 0 { + err = errNUL + } + } + if err != nil { + if err == io.EOF { + r.eof = true + } else if r.err == nil { + r.err = err + } + c = 0 + } + return c +} + +// readByteNoBuf is like readByte but doesn't buffer the byte. +// It exhausts r.buf before reading from r.b. +func (r *importReader) readByteNoBuf() byte { + var c byte + var err error + if len(r.buf) > 0 { + c = r.buf[0] + r.buf = r.buf[1:] + } else { + c, err = r.b.ReadByte() + if err == nil && c == 0 { + err = errNUL + } + } + + if err != nil { + if err == io.EOF { + r.eof = true + } else if r.err == nil { + r.err = err + } + return 0 + } + r.pos.Offset++ + if c == '\n' { + r.pos.Line++ + r.pos.Column = 1 + } else { + r.pos.Column++ + } + return c +} + +// peekByte returns the next byte from the input reader but does not advance beyond it. +// If skipSpace is set, peekByte skips leading spaces and comments. +func (r *importReader) peekByte(skipSpace bool) byte { + if r.err != nil { + if r.nerr++; r.nerr > 10000 { + panic("go/build: import reader looping") + } + return 0 + } + + // Use r.peek as first input byte. + // Don't just return r.peek here: it might have been left by peekByte(false) + // and this might be peekByte(true). + c := r.peek + if c == 0 { + c = r.readByte() + } + for r.err == nil && !r.eof { + if skipSpace { + // For the purposes of this reader, semicolons are never necessary to + // understand the input and are treated as spaces. + switch c { + case ' ', '\f', '\t', '\r', '\n', ';': + c = r.readByte() + continue + + case '/': + c = r.readByte() + if c == '/' { + for c != '\n' && r.err == nil && !r.eof { + c = r.readByte() + } + } else if c == '*' { + var c1 byte + for (c != '*' || c1 != '/') && r.err == nil { + if r.eof { + r.syntaxError() + } + c, c1 = c1, r.readByte() + } + } else { + r.syntaxError() + } + c = r.readByte() + continue + } + } + break + } + r.peek = c + return r.peek +} + +// nextByte is like peekByte but advances beyond the returned byte. +func (r *importReader) nextByte(skipSpace bool) byte { + c := r.peekByte(skipSpace) + r.peek = 0 + return c +} + +var goEmbed = []byte("go:embed") + +// findEmbed advances the input reader to the next //go:embed comment. +// It reports whether it found a comment. +// (Otherwise it found an error or EOF.) +func (r *importReader) findEmbed(first bool) bool { + // The import block scan stopped after a non-space character, + // so the reader is not at the start of a line on the first call. + // After that, each //go:embed extraction leaves the reader + // at the end of a line. + startLine := !first + var c byte + for r.err == nil && !r.eof { + c = r.readByteNoBuf() + Reswitch: + switch c { + default: + startLine = false + + case '\n': + startLine = true + + case ' ', '\t': + // leave startLine alone + + case '"': + startLine = false + for r.err == nil { + if r.eof { + r.syntaxError() + } + c = r.readByteNoBuf() + if c == '\\' { + r.readByteNoBuf() + if r.err != nil { + r.syntaxError() + return false + } + continue + } + if c == '"' { + c = r.readByteNoBuf() + goto Reswitch + } + } + goto Reswitch + + case '`': + startLine = false + for r.err == nil { + if r.eof { + r.syntaxError() + } + c = r.readByteNoBuf() + if c == '`' { + c = r.readByteNoBuf() + goto Reswitch + } + } + + case '\'': + startLine = false + for r.err == nil { + if r.eof { + r.syntaxError() + } + c = r.readByteNoBuf() + if c == '\\' { + r.readByteNoBuf() + if r.err != nil { + r.syntaxError() + return false + } + continue + } + if c == '\'' { + c = r.readByteNoBuf() + goto Reswitch + } + } + + case '/': + c = r.readByteNoBuf() + switch c { + default: + startLine = false + goto Reswitch + + case '*': + var c1 byte + for (c != '*' || c1 != '/') && r.err == nil { + if r.eof { + r.syntaxError() + } + c, c1 = c1, r.readByteNoBuf() + } + startLine = false + + case '/': + if startLine { + // Try to read this as a //go:embed comment. + for i := range goEmbed { + c = r.readByteNoBuf() + if c != goEmbed[i] { + goto SkipSlashSlash + } + } + c = r.readByteNoBuf() + if c == ' ' || c == '\t' { + // Found one! + return true + } + } + SkipSlashSlash: + for c != '\n' && r.err == nil && !r.eof { + c = r.readByteNoBuf() + } + startLine = true + } + } + } + return false +} + +// readKeyword reads the given keyword from the input. +// If the keyword is not present, readKeyword records a syntax error. +func (r *importReader) readKeyword(kw string) { + r.peekByte(true) + for i := 0; i < len(kw); i++ { + if r.nextByte(false) != kw[i] { + r.syntaxError() + return + } + } + if isIdent(r.peekByte(false)) { + r.syntaxError() + } +} + +// readIdent reads an identifier from the input. +// If an identifier is not present, readIdent records a syntax error. +func (r *importReader) readIdent() { + c := r.peekByte(true) + if !isIdent(c) { + r.syntaxError() + return + } + for isIdent(r.peekByte(false)) { + r.peek = 0 + } +} + +// readString reads a quoted string literal from the input. +// If an identifier is not present, readString records a syntax error. +func (r *importReader) readString() { + switch r.nextByte(true) { + case '`': + for r.err == nil { + if r.nextByte(false) == '`' { + break + } + if r.eof { + r.syntaxError() + } + } + case '"': + for r.err == nil { + c := r.nextByte(false) + if c == '"' { + break + } + if r.eof || c == '\n' { + r.syntaxError() + } + if c == '\\' { + r.nextByte(false) + } + } + default: + r.syntaxError() + } +} + +// readImport reads an import clause - optional identifier followed by quoted string - +// from the input. +func (r *importReader) readImport() { + c := r.peekByte(true) + if c == '.' { + r.peek = 0 + } else if isIdent(c) { + r.readIdent() + } + r.readString() +} + +// readComments is like io.ReadAll, except that it only reads the leading +// block of comments in the file. +func readComments(f io.Reader) ([]byte, error) { + r := newImportReader("", f) + r.peekByte(true) + if r.err == nil && !r.eof { + // Didn't reach EOF, so must have found a non-space byte. Remove it. + r.buf = r.buf[:len(r.buf)-1] + } + return r.buf, r.err +} + +// readGoInfo expects a Go file as input and reads the file up to and including the import section. +// It records what it learned in *info. +// If info.fset is non-nil, readGoInfo parses the file and sets info.parsed, info.parseErr, +// info.imports and info.embeds. +// +// It only returns an error if there are problems reading the file, +// not for syntax errors in the file itself. +func readGoInfo(f io.Reader, info *fileInfo) error { + r := newImportReader(info.name, f) + + r.readKeyword("package") + r.readIdent() + for r.peekByte(true) == 'i' { + r.readKeyword("import") + if r.peekByte(true) == '(' { + r.nextByte(false) + for r.peekByte(true) != ')' && r.err == nil { + r.readImport() + } + r.nextByte(false) + } else { + r.readImport() + } + } + + info.header = r.buf + + // If we stopped successfully before EOF, we read a byte that told us we were done. + // Return all but that last byte, which would cause a syntax error if we let it through. + if r.err == nil && !r.eof { + info.header = r.buf[:len(r.buf)-1] + } + + // If we stopped for a syntax error, consume the whole file so that + // we are sure we don't change the errors that go/parser returns. + if r.err == errSyntax { + r.err = nil + for r.err == nil && !r.eof { + r.readByte() + } + info.header = r.buf + } + if r.err != nil { + return r.err + } + + if info.fset == nil { + return nil + } + + // Parse file header & record imports. + info.parsed, info.parseErr = parser.ParseFile(info.fset, info.name, info.header, parser.ImportsOnly|parser.ParseComments) + if info.parseErr != nil { + return nil + } + + hasEmbed := false + for _, decl := range info.parsed.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + for _, dspec := range d.Specs { + spec, ok := dspec.(*ast.ImportSpec) + if !ok { + continue + } + quoted := spec.Path.Value + path, err := strconv.Unquote(quoted) + if err != nil { + return fmt.Errorf("parser returned invalid quoted string: <%s>", quoted) + } + if path == "embed" { + hasEmbed = true + } + + doc := spec.Doc + if doc == nil && len(d.Specs) == 1 { + doc = d.Doc + } + info.imports = append(info.imports, fileImport{path, spec.Pos(), doc}) + } + } + + // If the file imports "embed", + // we have to look for //go:embed comments + // in the remainder of the file. + // The compiler will enforce the mapping of comments to + // declared variables. We just need to know the patterns. + // If there were //go:embed comments earlier in the file + // (near the package statement or imports), the compiler + // will reject them. They can be (and have already been) ignored. + if hasEmbed { + var line []byte + for first := true; r.findEmbed(first); first = false { + line = line[:0] + pos := r.pos + for { + c := r.readByteNoBuf() + if c == '\n' || r.err != nil || r.eof { + break + } + line = append(line, c) + } + // Add args if line is well-formed. + // Ignore badly-formed lines - the compiler will report them when it finds them, + // and we can pretend they are not there to help go list succeed with what it knows. + embs, err := parseGoEmbed(string(line), pos) + if err == nil { + info.embeds = append(info.embeds, embs...) + } + } + } + + return nil +} + +// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. +// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. +// This is based on a similar function in cmd/compile/internal/gc/noder.go; +// this version calculates position information as well. +func parseGoEmbed(args string, pos token.Position) ([]fileEmbed, error) { + trimBytes := func(n int) { + pos.Offset += n + pos.Column += utf8.RuneCountInString(args[:n]) + args = args[n:] + } + trimSpace := func() { + trim := strings.TrimLeftFunc(args, unicode.IsSpace) + trimBytes(len(args) - len(trim)) + } + + var list []fileEmbed + for trimSpace(); args != ""; trimSpace() { + var path string + pathPos := pos + Switch: + switch args[0] { + default: + i := len(args) + for j, c := range args { + if unicode.IsSpace(c) { + i = j + break + } + } + path = args[:i] + trimBytes(i) + + case '`': + var ok bool + path, _, ok = strings.Cut(args[1:], "`") + if !ok { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + trimBytes(1 + len(path) + 1) + + case '"': + i := 1 + for ; i < len(args); i++ { + if args[i] == '\\' { + i++ + continue + } + if args[i] == '"' { + q, err := strconv.Unquote(args[:i+1]) + if err != nil { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) + } + path = q + trimBytes(i + 1) + break Switch + } + } + if i >= len(args) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + + if args != "" { + r, _ := utf8.DecodeRuneInString(args) + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + list = append(list, fileEmbed{path, pathPos}) + } + return list, nil +} diff --git a/src/cmd/go/internal/modindex/index_format.txt b/src/cmd/go/internal/modindex/index_format.txt new file mode 100644 index 00000000000000..a593391f7e073d --- /dev/null +++ b/src/cmd/go/internal/modindex/index_format.txt @@ -0,0 +1,57 @@ +This file documents the index format that is read and written by this package. +The index format is an encoding of a series of RawPackage structs + +Field names refer to fields on RawPackage and rawFile. +The file uses little endian encoding for the uint32s. +Strings are written into the string table at the end of the file. Each string +is null-terminated. String offsets are relative to the start of the string table. +Bools are written as uint32s: 0 for false and 1 for true. + +The following is the format for a full module: + +“go index v0\n” +str uint32 - offset of string table +n uint32 - number of packages +dirnames [n]uint32 - offsets to package names in string table; names sorted by raw string +packages [n]uint32 - offset where package begins +for each RawPackage: + error uint32 - string offset // error is produced by fsys.ReadDir or fmt.Errorf + dir uint32 - string offset (directory path relative to module root) + len(sourceFiles) uint32 + sourceFiles [n]uint32 - offset to source file (relative to start of index file) + for each sourceFile: + error - string offset // error is either produced by fmt.Errorf,errors.New or is io.EOF + parseError - string offset // if non-empty, a json-encoded parseError struct (see below). Is either produced by io.ReadAll,os.ReadFile,errors.New or is scanner.Error,scanner.ErrorList + synopsis - string offset + name - string offset + pkgName - string offset + ignoreFile - int32 bool // report the file in Ignored(Go|Other)Files because there was an error reading it or parsing its build constraints. + binaryOnly uint32 bool + cgoDirectives string offset // the #cgo directive lines in the comment on import "C" + goBuildConstraint - string offset + len(plusBuildConstraints) - uint32 + plusBuildConstraints - [n]uint32 (string offsets) + len(imports) uint32 + for each rawImport: + path - string offset + position - file, offset, line, column - uint32 + len(embeds) numEmbeds uint32 + for each embed: + pattern - string offset + position - file, offset, line, column - uint32 +[string table] + +The following is the format for a single indexed package: + +“go index v0\n” +str uint32 - offset of string table +for the single RawPackage: + [same RawPackage format as above] +[string table] + +The following is the definition of the json-serialized parseError struct: + +type parseError struct { + ErrorList *scanner.ErrorList // non-nil if the error was an ErrorList, nil otherwise + ErrorString string // non-empty for all other cases +} diff --git a/src/cmd/go/internal/modindex/index_test.go b/src/cmd/go/internal/modindex/index_test.go new file mode 100644 index 00000000000000..2c072f909d3b64 --- /dev/null +++ b/src/cmd/go/internal/modindex/index_test.go @@ -0,0 +1,87 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modindex + +import ( + "encoding/hex" + "encoding/json" + "go/build" + "internal/diff" + "path/filepath" + "reflect" + "runtime" + "testing" +) + +func init() { + isTest = true + enabled = true // to allow GODEBUG=goindex=0 go test, when things are very broken +} + +func TestIndex(t *testing.T) { + src := filepath.Join(runtime.GOROOT(), "src") + checkPkg := func(t *testing.T, m *Module, pkg string, data []byte) { + p := m.Package(pkg) + bp, err := p.Import(build.Default, build.ImportComment) + if err != nil { + t.Fatal(err) + } + bp1, err := build.Default.Import(pkg, filepath.Join(src, pkg), build.ImportComment) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(bp, bp1) { + t.Errorf("mismatch") + t.Logf("index:\n%s", hex.Dump(data)) + + js, err := json.MarshalIndent(bp, "", "\t") + if err != nil { + t.Fatal(err) + } + js1, err := json.MarshalIndent(bp1, "", "\t") + if err != nil { + t.Fatal(err) + } + t.Logf("diff:\n%s", diff.Diff("index", js, "correct", js1)) + t.FailNow() + } + } + + // Check packages in increasing complexity, one at a time. + pkgs := []string{ + "crypto", + "encoding", + "unsafe", + "encoding/json", + "runtime", + "net", + } + var raws []*rawPackage + for _, pkg := range pkgs { + raw := importRaw(src, pkg) + raws = append(raws, raw) + t.Run(pkg, func(t *testing.T) { + data := encodeModuleBytes([]*rawPackage{raw}) + m, err := fromBytes(src, data) + if err != nil { + t.Fatal(err) + } + checkPkg(t, m, pkg, data) + }) + } + + // Check that a multi-package index works too. + t.Run("all", func(t *testing.T) { + data := encodeModuleBytes(raws) + m, err := fromBytes(src, data) + if err != nil { + t.Fatal(err) + } + for _, pkg := range pkgs { + checkPkg(t, m, pkg, data) + } + }) +} diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go new file mode 100644 index 00000000000000..83a54c3ef8ed21 --- /dev/null +++ b/src/cmd/go/internal/modindex/read.go @@ -0,0 +1,1030 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modindex + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "go/build" + "go/build/constraint" + "go/token" + "internal/godebug" + "internal/goroot" + "path" + "path/filepath" + "runtime" + "runtime/debug" + "sort" + "strings" + "sync" + "time" + "unsafe" + + "cmd/go/internal/base" + "cmd/go/internal/cache" + "cmd/go/internal/cfg" + "cmd/go/internal/fsys" + "cmd/go/internal/imports" + "cmd/go/internal/par" + "cmd/go/internal/str" +) + +// enabled is used to flag off the behavior of the module index on tip. +// It will be removed before the release. +// TODO(matloob): Remove enabled once we have more confidence on the +// module index. +var enabled bool = godebug.Get("goindex") != "0" + +// Module represents and encoded module index file. It is used to +// do the equivalent of build.Import of packages in the module and answer other +// questions based on the index file's data. +type Module struct { + modroot string + d *decoder + n int // number of packages +} + +// moduleHash returns an ActionID corresponding to the state of the module +// located at filesystem path modroot. +func moduleHash(modroot string, ismodcache bool) (cache.ActionID, error) { + // We expect modules stored within the module cache to be checksummed and + // immutable, and we expect released modules within GOROOT to change only + // infrequently (when the Go version changes). + if !ismodcache { + // The contents of this module may change over time. We don't want to pay + // the cost to detect changes and re-index whenever they occur, so just + // don't index it at all. + // + // Note that this is true even for modules in GOROOT/src: non-release builds + // of the Go toolchain may have arbitrary development changes on top of the + // commit reported by runtime.Version, or could be completly artificial due + // to lacking a `git` binary (like "devel gomote.XXXXX", as synthesized by + // "gomote push" as of 2022-06-15). (Release builds shouldn't have + // modifications, but we don't want to use a behavior for releases that we + // haven't tested during development.) + return cache.ActionID{}, ErrNotIndexed + } + + h := cache.NewHash("moduleIndex") + // TODO(bcmills): Since modules in the index are checksummed, we could + // probably improve the cache hit rate by keying off of the module + // path@version (perhaps including the checksum?) instead of the module root + // directory. + fmt.Fprintf(h, "module index %s %s %v\n", runtime.Version(), indexVersion, modroot) + return h.Sum(), nil +} + +const modTimeCutoff = 2 * time.Second + +// dirHash returns an ActionID corresponding to the state of the package +// located at filesystem path pkgdir. +func dirHash(modroot, pkgdir string) (cache.ActionID, error) { + h := cache.NewHash("moduleIndex") + fmt.Fprintf(h, "modroot %s\n", modroot) + fmt.Fprintf(h, "package %s %s %v\n", runtime.Version(), indexVersion, pkgdir) + entries, err := fsys.ReadDir(pkgdir) + if err != nil { + // pkgdir might not be a directory. give up on hashing. + return cache.ActionID{}, ErrNotIndexed + } + cutoff := time.Now().Add(-modTimeCutoff) + for _, info := range entries { + if info.IsDir() { + continue + } + + if !info.Mode().IsRegular() { + return cache.ActionID{}, ErrNotIndexed + } + // To avoid problems for very recent files where a new + // write might not change the mtime due to file system + // mtime precision, reject caching if a file was read that + // is less than modTimeCutoff old. + // + // This is the same strategy used for hashing test inputs. + // See hashOpen in cmd/go/internal/test/test.go for the + // corresponding code. + if info.ModTime().After(cutoff) { + return cache.ActionID{}, ErrNotIndexed + } + + fmt.Fprintf(h, "file %v %v %v\n", info.Name(), info.ModTime(), info.Size()) + } + return h.Sum(), nil +} + +var modrootCache par.Cache + +var ErrNotIndexed = errors.New("not in module index") + +var ( + errDisabled = fmt.Errorf("%w: module indexing disabled", ErrNotIndexed) + errNotFromModuleCache = fmt.Errorf("%w: not from module cache", ErrNotIndexed) +) + +// GetPackage returns the IndexPackage for the package at the given path. +// It will return ErrNotIndexed if the directory should be read without +// using the index, for instance because the index is disabled, or the packgae +// is not in a module. +func GetPackage(modroot, pkgdir string) (*IndexPackage, error) { + mi, err := GetModule(modroot) + if err == nil { + return mi.Package(relPath(pkgdir, modroot)), nil + } + if !errors.Is(err, errNotFromModuleCache) { + return nil, err + } + if cfg.BuildContext.Compiler == "gccgo" && str.HasPathPrefix(modroot, cfg.GOROOTsrc) { + return nil, err // gccgo has no sources for GOROOT packages. + } + return openIndexPackage(modroot, pkgdir) +} + +// GetModule returns the Module for the given modroot. +// It will return ErrNotIndexed if the directory should be read without +// using the index, for instance because the index is disabled, or the packgae +// is not in a module. +func GetModule(modroot string) (*Module, error) { + if !enabled || cache.DefaultDir() == "off" { + return nil, errDisabled + } + if modroot == "" { + panic("modindex.GetPackage called with empty modroot") + } + if cfg.BuildMod == "vendor" { + // Even if the main module is in the module cache, + // its vendored dependencies are not loaded from their + // usual cached locations. + return nil, errNotFromModuleCache + } + modroot = filepath.Clean(modroot) + if !str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) { + return nil, errNotFromModuleCache + } + return openIndexModule(modroot, true) +} + +var mcache par.Cache + +// openIndexModule returns the module index for modPath. +// It will return ErrNotIndexed if the module can not be read +// using the index because it contains symlinks. +func openIndexModule(modroot string, ismodcache bool) (*Module, error) { + type result struct { + mi *Module + err error + } + r := mcache.Do(modroot, func() any { + fsys.Trace("openIndexModule", modroot) + id, err := moduleHash(modroot, ismodcache) + if err != nil { + return result{nil, err} + } + data, _, err := cache.Default().GetMmap(id) + if err != nil { + // Couldn't read from modindex. Assume we couldn't read from + // the index because the module hasn't been indexed yet. + data, err = indexModule(modroot) + if err != nil { + return result{nil, err} + } + if err = cache.Default().PutBytes(id, data); err != nil { + return result{nil, err} + } + } + mi, err := fromBytes(modroot, data) + if err != nil { + return result{nil, err} + } + return result{mi, nil} + }).(result) + return r.mi, r.err +} + +var pcache par.Cache + +func openIndexPackage(modroot, pkgdir string) (*IndexPackage, error) { + type result struct { + pkg *IndexPackage + err error + } + r := pcache.Do([2]string{modroot, pkgdir}, func() any { + fsys.Trace("openIndexPackage", pkgdir) + id, err := dirHash(modroot, pkgdir) + if err != nil { + return result{nil, err} + } + data, _, err := cache.Default().GetMmap(id) + if err != nil { + // Couldn't read from index. Assume we couldn't read from + // the index because the package hasn't been indexed yet. + data = indexPackage(modroot, pkgdir) + if err = cache.Default().PutBytes(id, data); err != nil { + return result{nil, err} + } + } + pkg, err := packageFromBytes(modroot, data) + if err != nil { + return result{nil, err} + } + return result{pkg, nil} + }).(result) + return r.pkg, r.err +} + +var errCorrupt = errors.New("corrupt index") + +// protect marks the start of a large section of code that accesses the index. +// It should be used as: +// +// defer unprotect(protect, &err) +// +// It should not be used for trivial accesses which would be +// dwarfed by the overhead of the defer. +func protect() bool { + return debug.SetPanicOnFault(true) +} + +var isTest = false + +// unprotect marks the end of a large section of code that accesses the index. +// It should be used as: +// +// defer unprotect(protect, &err) +// +// end looks for panics due to errCorrupt or bad mmap accesses. +// When it finds them, it adds explanatory text, consumes the panic, and sets *errp instead. +// If errp is nil, end adds the explanatory text but then calls base.Fatalf. +func unprotect(old bool, errp *error) { + // SetPanicOnFault's errors _may_ satisfy this interface. Even though it's not guaranteed + // that all its errors satisfy this interface, we'll only check for these errors so that + // we don't suppress panics that could have been produced from other sources. + type addrer interface { + Addr() uintptr + } + + debug.SetPanicOnFault(old) + + if e := recover(); e != nil { + if _, ok := e.(addrer); ok || e == errCorrupt { + // This panic was almost certainly caused by SetPanicOnFault or our panic(errCorrupt). + err := fmt.Errorf("error reading module index: %v", e) + if errp != nil { + *errp = err + return + } + if isTest { + panic(err) + } + base.Fatalf("%v", err) + } + // The panic was likely not caused by SetPanicOnFault. + panic(e) + } +} + +// fromBytes returns a *Module given the encoded representation. +func fromBytes(moddir string, data []byte) (m *Module, err error) { + if !enabled { + panic("use of index") + } + + defer unprotect(protect(), &err) + + if !bytes.HasPrefix(data, []byte(indexVersion+"\n")) { + return nil, errCorrupt + } + + const hdr = len(indexVersion + "\n") + d := &decoder{data: data} + str := d.intAt(hdr) + if str < hdr+8 || len(d.data) < str { + return nil, errCorrupt + } + d.data, d.str = data[:str], d.data[str:] + // Check that string table looks valid. + // First string is empty string (length 0), + // and we leave a marker byte 0xFF at the end + // just to make sure that the file is not truncated. + if len(d.str) == 0 || d.str[0] != 0 || d.str[len(d.str)-1] != 0xFF { + return nil, errCorrupt + } + + n := d.intAt(hdr + 4) + if n < 0 || n > (len(d.data)-8)/8 { + return nil, errCorrupt + } + + m = &Module{ + moddir, + d, + n, + } + return m, nil +} + +// packageFromBytes returns a *IndexPackage given the encoded representation. +func packageFromBytes(modroot string, data []byte) (p *IndexPackage, err error) { + m, err := fromBytes(modroot, data) + if err != nil { + return nil, err + } + if m.n != 1 { + return nil, fmt.Errorf("corrupt single-package index") + } + return m.pkg(0), nil +} + +// pkgDir returns the dir string of the i'th package in the index. +func (m *Module) pkgDir(i int) string { + if i < 0 || i >= m.n { + panic(errCorrupt) + } + return m.d.stringAt(12 + 8 + 8*i) +} + +// pkgOff returns the offset of the data for the i'th package in the index. +func (m *Module) pkgOff(i int) int { + if i < 0 || i >= m.n { + panic(errCorrupt) + } + return m.d.intAt(12 + 8 + 8*i + 4) +} + +// Walk calls f for each package in the index, passing the path to that package relative to the module root. +func (m *Module) Walk(f func(path string)) { + defer unprotect(protect(), nil) + for i := 0; i < m.n; i++ { + f(m.pkgDir(i)) + } +} + +// relPath returns the path relative to the module's root. +func relPath(path, modroot string) string { + return str.TrimFilePathPrefix(filepath.Clean(path), filepath.Clean(modroot)) +} + +// Import is the equivalent of build.Import given the information in Module. +func (rp *IndexPackage) Import(bctxt build.Context, mode build.ImportMode) (p *build.Package, err error) { + defer unprotect(protect(), &err) + + ctxt := (*Context)(&bctxt) + + p = &build.Package{} + + p.ImportPath = "." + p.Dir = filepath.Join(rp.modroot, rp.dir) + + var pkgerr error + switch ctxt.Compiler { + case "gccgo", "gc": + default: + // Save error for end of function. + pkgerr = fmt.Errorf("import %q: unknown compiler %q", p.Dir, ctxt.Compiler) + } + + if p.Dir == "" { + return p, fmt.Errorf("import %q: import of unknown directory", p.Dir) + } + + // goroot and gopath + inTestdata := func(sub string) bool { + return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || str.HasPathPrefix(sub, "testdata") + } + if !inTestdata(rp.dir) { + // In build.go, p.Root should only be set in the non-local-import case, or in + // GOROOT or GOPATH. Since module mode only calls Import with path set to "." + // and the module index doesn't apply outside modules, the GOROOT case is + // the only case where GOROOT needs to be set. + // But: p.Root is actually set in the local-import case outside GOROOT, if + // the directory is contained in GOPATH/src + // TODO(#37015): fix that behavior in go/build and remove the gopath case + // below. + if ctxt.GOROOT != "" && str.HasFilePathPrefix(p.Dir, cfg.GOROOTsrc) && p.Dir != cfg.GOROOTsrc { + p.Root = ctxt.GOROOT + p.Goroot = true + modprefix := str.TrimFilePathPrefix(rp.modroot, cfg.GOROOTsrc) + p.ImportPath = rp.dir + if modprefix != "" { + p.ImportPath = filepath.Join(modprefix, p.ImportPath) + } + } + for _, root := range ctxt.gopath() { + // TODO(matloob): do we need to reimplement the conflictdir logic? + + // TODO(matloob): ctxt.hasSubdir evaluates symlinks, so it + // can be slower than we'd like. Find out if we can drop this + // logic before the release. + if sub, ok := ctxt.hasSubdir(filepath.Join(root, "src"), p.Dir); ok { + p.ImportPath = sub + p.Root = root + } + } + } + if p.Root != "" { + // Set GOROOT-specific fields (sometimes for modules in a GOPATH directory). + // The fields set below (SrcRoot, PkgRoot, BinDir, PkgTargetRoot, and PkgObj) + // are only set in build.Import if p.Root != "". As noted in the comment + // on setting p.Root above, p.Root should only be set in the GOROOT case for the + // set of packages we care about, but is also set for modules in a GOPATH src + // directory. + var pkgtargetroot string + var pkga string + suffix := "" + if ctxt.InstallSuffix != "" { + suffix = "_" + ctxt.InstallSuffix + } + switch ctxt.Compiler { + case "gccgo": + pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + dir, elem := path.Split(p.ImportPath) + pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a" + case "gc": + pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + pkga = pkgtargetroot + "/" + p.ImportPath + ".a" + } + p.SrcRoot = ctxt.joinPath(p.Root, "src") + p.PkgRoot = ctxt.joinPath(p.Root, "pkg") + p.BinDir = ctxt.joinPath(p.Root, "bin") + if pkga != "" { + p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot) + p.PkgObj = ctxt.joinPath(p.Root, pkga) + } + } + + if rp.error != nil { + if errors.Is(rp.error, errCannotFindPackage) && ctxt.Compiler == "gccgo" && p.Goroot { + return p, nil + } + return p, rp.error + } + + if mode&build.FindOnly != 0 { + return p, pkgerr + } + + // We need to do a second round of bad file processing. + var badGoError error + badFiles := make(map[string]bool) + badFile := func(name string, err error) { + if badGoError == nil { + badGoError = err + } + if !badFiles[name] { + p.InvalidGoFiles = append(p.InvalidGoFiles, name) + badFiles[name] = true + } + } + + var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems) + var firstFile string + embedPos := make(map[string][]token.Position) + testEmbedPos := make(map[string][]token.Position) + xTestEmbedPos := make(map[string][]token.Position) + importPos := make(map[string][]token.Position) + testImportPos := make(map[string][]token.Position) + xTestImportPos := make(map[string][]token.Position) + allTags := make(map[string]bool) + for _, tf := range rp.sourceFiles { + name := tf.name() + if error := tf.error(); error != "" { + badFile(name, errors.New(tf.error())) + continue + } else if parseError := tf.parseError(); parseError != "" { + badFile(name, parseErrorFromString(tf.parseError())) + // Fall through: we still want to list files with parse errors. + } + + var shouldBuild = true + if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { + shouldBuild = false + } else if goBuildConstraint := tf.goBuildConstraint(); goBuildConstraint != "" { + x, err := constraint.Parse(goBuildConstraint) + if err != nil { + return p, fmt.Errorf("%s: parsing //go:build line: %v", name, err) + } + shouldBuild = ctxt.eval(x, allTags) + } else if plusBuildConstraints := tf.plusBuildConstraints(); len(plusBuildConstraints) > 0 { + for _, text := range plusBuildConstraints { + if x, err := constraint.Parse(text); err == nil { + if !ctxt.eval(x, allTags) { + shouldBuild = false + } + } + } + } + + ext := nameExt(name) + if !shouldBuild || tf.ignoreFile() { + if ext == ".go" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + } else if fileListForExt((*Package)(p), ext) != nil { + p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name) + } + continue + } + + // Going to save the file. For non-Go files, can stop here. + switch ext { + case ".go": + // keep going + case ".S", ".sx": + // special case for cgo, handled at end + Sfiles = append(Sfiles, name) + continue + default: + if list := fileListForExt((*Package)(p), ext); list != nil { + *list = append(*list, name) + } + continue + } + + pkg := tf.pkgName() + if pkg == "documentation" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + continue + } + isTest := strings.HasSuffix(name, "_test.go") + isXTest := false + if isTest && strings.HasSuffix(tf.pkgName(), "_test") && p.Name != tf.pkgName() { + isXTest = true + pkg = pkg[:len(pkg)-len("_test")] + } + + if !isTest && tf.binaryOnly() { + p.BinaryOnly = true + } + + if p.Name == "" { + p.Name = pkg + firstFile = name + } else if pkg != p.Name { + // TODO(#45999): The choice of p.Name is arbitrary based on file iteration + // order. Instead of resolving p.Name arbitrarily, we should clear out the + // existing Name and mark the existing files as also invalid. + badFile(name, &MultiplePackageError{ + Dir: p.Dir, + Packages: []string{p.Name, pkg}, + Files: []string{firstFile, name}, + }) + } + // Grab the first package comment as docs, provided it is not from a test file. + if p.Doc == "" && !isTest && !isXTest { + if synopsis := tf.synopsis(); synopsis != "" { + p.Doc = synopsis + } + } + + // Record Imports and information about cgo. + isCgo := false + imports := tf.imports() + for _, imp := range imports { + if imp.path == "C" { + if isTest { + badFile(name, fmt.Errorf("use of cgo in test %s not supported", name)) + continue + } + isCgo = true + } + } + if directives := tf.cgoDirectives(); directives != "" { + if err := ctxt.saveCgo(name, (*Package)(p), directives); err != nil { + badFile(name, err) + } + } + + var fileList *[]string + var importMap, embedMap map[string][]token.Position + switch { + case isCgo: + allTags["cgo"] = true + if ctxt.CgoEnabled { + fileList = &p.CgoFiles + importMap = importPos + embedMap = embedPos + } else { + // Ignore Imports and Embeds from cgo files if cgo is disabled. + fileList = &p.IgnoredGoFiles + } + case isXTest: + fileList = &p.XTestGoFiles + importMap = xTestImportPos + embedMap = xTestEmbedPos + case isTest: + fileList = &p.TestGoFiles + importMap = testImportPos + embedMap = testEmbedPos + default: + fileList = &p.GoFiles + importMap = importPos + embedMap = embedPos + } + *fileList = append(*fileList, name) + if importMap != nil { + for _, imp := range imports { + importMap[imp.path] = append(importMap[imp.path], imp.position) + } + } + if embedMap != nil { + for _, e := range tf.embeds() { + embedMap[e.pattern] = append(embedMap[e.pattern], e.position) + } + } + } + + p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos) + p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos) + p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos) + + p.Imports, p.ImportPos = cleanDecls(importPos) + p.TestImports, p.TestImportPos = cleanDecls(testImportPos) + p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos) + + for tag := range allTags { + p.AllTags = append(p.AllTags, tag) + } + sort.Strings(p.AllTags) + + if len(p.CgoFiles) > 0 { + p.SFiles = append(p.SFiles, Sfiles...) + sort.Strings(p.SFiles) + } else { + p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...) + sort.Strings(p.IgnoredOtherFiles) + } + + if badGoError != nil { + return p, badGoError + } + if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { + return p, &build.NoGoError{Dir: p.Dir} + } + return p, pkgerr +} + +// IsStandardPackage reports whether path is a standard package +// for the goroot and compiler using the module index if possible, +// and otherwise falling back to internal/goroot.IsStandardPackage +func IsStandardPackage(goroot_, compiler, path string) bool { + if !enabled || compiler != "gc" { + return goroot.IsStandardPackage(goroot_, compiler, path) + } + + reldir := filepath.FromSlash(path) // relative dir path in module index for package + modroot := filepath.Join(goroot_, "src") + if str.HasFilePathPrefix(reldir, "cmd") { + reldir = str.TrimFilePathPrefix(reldir, "cmd") + modroot = filepath.Join(modroot, "cmd") + } + if _, err := GetPackage(modroot, filepath.Join(modroot, reldir)); err == nil { + // Note that goroot.IsStandardPackage doesn't check that the directory + // actually contains any go files-- merely that it exists. GetPackage + // returning a nil error is enough for us to know the directory exists. + return true + } else if errors.Is(err, ErrNotIndexed) { + // Fall back because package isn't indexable. (Probably because + // a file was modified recently) + return goroot.IsStandardPackage(goroot_, compiler, path) + } + return false +} + +// IsDirWithGoFiles is the equivalent of fsys.IsDirWithGoFiles using the information in the index. +func (rp *IndexPackage) IsDirWithGoFiles() (_ bool, err error) { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("error reading module index: %v", e) + } + }() + for _, sf := range rp.sourceFiles { + if strings.HasSuffix(sf.name(), ".go") { + return true, nil + } + } + return false, nil +} + +// ScanDir implements imports.ScanDir using the information in the index. +func (rp *IndexPackage) ScanDir(tags map[string]bool) (sortedImports []string, sortedTestImports []string, err error) { + // TODO(matloob) dir should eventually be relative to indexed directory + // TODO(matloob): skip reading raw package and jump straight to data we need? + + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("error reading module index: %v", e) + } + }() + + imports_ := make(map[string]bool) + testImports := make(map[string]bool) + numFiles := 0 + +Files: + for _, sf := range rp.sourceFiles { + name := sf.name() + if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") || !strings.HasSuffix(name, ".go") || !imports.MatchFile(name, tags) { + continue + } + + // The following section exists for backwards compatibility reasons: + // scanDir ignores files with import "C" when collecting the list + // of imports unless the "cgo" tag is provided. The following comment + // is copied from the original. + // + // import "C" is implicit requirement of cgo tag. + // When listing files on the command line (explicitFiles=true) + // we do not apply build tag filtering but we still do apply + // cgo filtering, so no explicitFiles check here. + // Why? Because we always have, and it's not worth breaking + // that behavior now. + imps := sf.imports() // TODO(matloob): directly read import paths to avoid the extra strings? + for _, imp := range imps { + if imp.path == "C" && !tags["cgo"] && !tags["*"] { + continue Files + } + } + + if !shouldBuild(sf, tags) { + continue + } + numFiles++ + m := imports_ + if strings.HasSuffix(name, "_test.go") { + m = testImports + } + for _, p := range imps { + m[p.path] = true + } + } + if numFiles == 0 { + return nil, nil, imports.ErrNoGo + } + return keys(imports_), keys(testImports), nil +} + +func keys(m map[string]bool) []string { + list := make([]string, 0, len(m)) + for k := range m { + list = append(list, k) + } + sort.Strings(list) + return list +} + +// implements imports.ShouldBuild in terms of an index sourcefile. +func shouldBuild(sf *sourceFile, tags map[string]bool) bool { + if goBuildConstraint := sf.goBuildConstraint(); goBuildConstraint != "" { + x, err := constraint.Parse(goBuildConstraint) + if err != nil { + return false + } + return imports.Eval(x, tags, true) + } + + plusBuildConstraints := sf.plusBuildConstraints() + for _, text := range plusBuildConstraints { + if x, err := constraint.Parse(text); err == nil { + if imports.Eval(x, tags, true) == false { + return false + } + } + } + + return true +} + +// IndexPackage holds the information needed to access information in the +// index needed to load a package in a specific directory. +type IndexPackage struct { + error error + dir string // directory of the package relative to the modroot + + modroot string + + // Source files + sourceFiles []*sourceFile +} + +var errCannotFindPackage = errors.New("cannot find package") + +// Package and returns finds the package with the given path (relative to the module root). +// If the package does not exist, Package returns an IndexPackage that will return an +// appropriate error from its methods. +func (m *Module) Package(path string) *IndexPackage { + defer unprotect(protect(), nil) + + i, ok := sort.Find(m.n, func(i int) int { + return strings.Compare(path, m.pkgDir(i)) + }) + if !ok { + return &IndexPackage{error: fmt.Errorf("%w %q in:\n\t%s", errCannotFindPackage, path, filepath.Join(m.modroot, path))} + } + return m.pkg(i) +} + +// pkgAt returns the i'th IndexPackage in m. +func (m *Module) pkg(i int) *IndexPackage { + r := m.d.readAt(m.pkgOff(i)) + p := new(IndexPackage) + if errstr := r.string(); errstr != "" { + p.error = errors.New(errstr) + } + p.dir = r.string() + p.sourceFiles = make([]*sourceFile, r.int()) + for i := range p.sourceFiles { + p.sourceFiles[i] = &sourceFile{ + d: m.d, + pos: r.int(), + } + } + p.modroot = m.modroot + return p +} + +// sourceFile represents the information of a given source file in the module index. +type sourceFile struct { + d *decoder // encoding of this source file + pos int // start of sourceFile encoding in d + onceReadImports sync.Once + savedImports []rawImport // saved imports so that they're only read once +} + +// Offsets for fields in the sourceFile. +const ( + sourceFileError = 4 * iota + sourceFileParseError + sourceFileSynopsis + sourceFileName + sourceFilePkgName + sourceFileIgnoreFile + sourceFileBinaryOnly + sourceFileCgoDirectives + sourceFileGoBuildConstraint + sourceFileNumPlusBuildConstraints +) + +func (sf *sourceFile) error() string { + return sf.d.stringAt(sf.pos + sourceFileError) +} +func (sf *sourceFile) parseError() string { + return sf.d.stringAt(sf.pos + sourceFileParseError) +} +func (sf *sourceFile) synopsis() string { + return sf.d.stringAt(sf.pos + sourceFileSynopsis) +} +func (sf *sourceFile) name() string { + return sf.d.stringAt(sf.pos + sourceFileName) +} +func (sf *sourceFile) pkgName() string { + return sf.d.stringAt(sf.pos + sourceFilePkgName) +} +func (sf *sourceFile) ignoreFile() bool { + return sf.d.boolAt(sf.pos + sourceFileIgnoreFile) +} +func (sf *sourceFile) binaryOnly() bool { + return sf.d.boolAt(sf.pos + sourceFileBinaryOnly) +} +func (sf *sourceFile) cgoDirectives() string { + return sf.d.stringAt(sf.pos + sourceFileCgoDirectives) +} +func (sf *sourceFile) goBuildConstraint() string { + return sf.d.stringAt(sf.pos + sourceFileGoBuildConstraint) +} + +func (sf *sourceFile) plusBuildConstraints() []string { + pos := sf.pos + sourceFileNumPlusBuildConstraints + n := sf.d.intAt(pos) + pos += 4 + ret := make([]string, n) + for i := 0; i < n; i++ { + ret[i] = sf.d.stringAt(pos) + pos += 4 + } + return ret +} + +func (sf *sourceFile) importsOffset() int { + pos := sf.pos + sourceFileNumPlusBuildConstraints + n := sf.d.intAt(pos) + // each build constraint is 1 uint32 + return pos + 4 + n*4 +} + +func (sf *sourceFile) embedsOffset() int { + pos := sf.importsOffset() + n := sf.d.intAt(pos) + // each import is 5 uint32s (string + tokpos) + return pos + 4 + n*(4*5) +} + +func (sf *sourceFile) imports() []rawImport { + sf.onceReadImports.Do(func() { + importsOffset := sf.importsOffset() + r := sf.d.readAt(importsOffset) + numImports := r.int() + ret := make([]rawImport, numImports) + for i := 0; i < numImports; i++ { + ret[i] = rawImport{r.string(), r.tokpos()} + } + sf.savedImports = ret + }) + return sf.savedImports +} + +func (sf *sourceFile) embeds() []embed { + embedsOffset := sf.embedsOffset() + r := sf.d.readAt(embedsOffset) + numEmbeds := r.int() + ret := make([]embed, numEmbeds) + for i := range ret { + ret[i] = embed{r.string(), r.tokpos()} + } + return ret +} + +func asString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} + +// A decoder helps decode the index format. +type decoder struct { + data []byte // data after header + str []byte // string table +} + +// intAt returns the int at the given offset in d.data. +func (d *decoder) intAt(off int) int { + if off < 0 || len(d.data)-off < 4 { + panic(errCorrupt) + } + i := binary.LittleEndian.Uint32(d.data[off : off+4]) + if int32(i)>>31 != 0 { + panic(errCorrupt) + } + return int(i) +} + +// boolAt returns the bool at the given offset in d.data. +func (d *decoder) boolAt(off int) bool { + return d.intAt(off) != 0 +} + +// stringTableAt returns the string pointed at by the int at the given offset in d.data. +func (d *decoder) stringAt(off int) string { + return d.stringTableAt(d.intAt(off)) +} + +// stringTableAt returns the string at the given offset in the string table d.str. +func (d *decoder) stringTableAt(off int) string { + if off < 0 || off >= len(d.str) { + panic(errCorrupt) + } + s := d.str[off:] + v, n := binary.Uvarint(s) + if n <= 0 || v > uint64(len(s[n:])) { + panic(errCorrupt) + } + return asString(s[n : n+int(v)]) +} + +// A reader reads sequential fields from a section of the index format. +type reader struct { + d *decoder + pos int +} + +// readAt returns a reader starting at the given position in d. +func (d *decoder) readAt(pos int) *reader { + return &reader{d, pos} +} + +// int reads the next int. +func (r *reader) int() int { + i := r.d.intAt(r.pos) + r.pos += 4 + return i +} + +// string reads the next string. +func (r *reader) string() string { + return r.d.stringTableAt(r.int()) +} + +// bool reads the next bool. +func (r *reader) bool() bool { + return r.int() != 0 +} + +// tokpos reads the next token.Position. +func (r *reader) tokpos() token.Position { + return token.Position{ + Filename: r.string(), + Offset: r.int(), + Line: r.int(), + Column: r.int(), + } +} diff --git a/src/cmd/go/internal/modindex/scan.go b/src/cmd/go/internal/modindex/scan.go new file mode 100644 index 00000000000000..d3f059bcfc6a95 --- /dev/null +++ b/src/cmd/go/internal/modindex/scan.go @@ -0,0 +1,278 @@ +package modindex + +import ( + "cmd/go/internal/base" + "cmd/go/internal/fsys" + "cmd/go/internal/par" + "cmd/go/internal/str" + "encoding/json" + "errors" + "fmt" + "go/doc" + "go/scanner" + "go/token" + "io/fs" + "path/filepath" + "strings" +) + +// moduleWalkErr returns filepath.SkipDir if the directory isn't relevant +// when indexing a module or generating a filehash, ErrNotIndexed, +// if the module shouldn't be indexed, and nil otherwise. +func moduleWalkErr(modroot string, path string, info fs.FileInfo, err error) error { + if err != nil { + return ErrNotIndexed + } + // stop at module boundaries + if info.IsDir() && path != modroot { + if fi, err := fsys.Stat(filepath.Join(path, "go.mod")); err == nil && !fi.IsDir() { + return filepath.SkipDir + } + } + if info.Mode()&fs.ModeSymlink != 0 { + if target, err := fsys.Stat(path); err == nil && target.IsDir() { + // return an error to make the module hash invalid. + // Symlink directories in modules are tricky, so we won't index + // modules that contain them. + // TODO(matloob): perhaps don't return this error if the symlink leads to + // a directory with a go.mod file. + return ErrNotIndexed + } + } + return nil +} + +// indexModule indexes the module at the given directory and returns its +// encoded representation. It returns ErrNotIndexed if the module can't +// be indexed because it contains symlinks. +func indexModule(modroot string) ([]byte, error) { + fsys.Trace("indexModule", modroot) + var packages []*rawPackage + err := fsys.Walk(modroot, func(path string, info fs.FileInfo, err error) error { + if err := moduleWalkErr(modroot, path, info, err); err != nil { + return err + } + + if !info.IsDir() { + return nil + } + if !str.HasFilePathPrefix(path, modroot) { + panic(fmt.Errorf("path %v in walk doesn't have modroot %v as prefix", path, modroot)) + } + rel := str.TrimFilePathPrefix(path, modroot) + packages = append(packages, importRaw(modroot, rel)) + return nil + }) + if err != nil { + return nil, err + } + return encodeModuleBytes(packages), nil +} + +// indexModule indexes the package at the given directory and returns its +// encoded representation. It returns ErrNotIndexed if the package can't +// be indexed. +func indexPackage(modroot, pkgdir string) []byte { + fsys.Trace("indexPackage", pkgdir) + p := importRaw(modroot, relPath(pkgdir, modroot)) + return encodePackageBytes(p) +} + +// rawPackage holds the information from each package that's needed to +// fill a build.Package once the context is available. +type rawPackage struct { + error string + dir string // directory containing package sources, relative to the module root + + // Source files + sourceFiles []*rawFile +} + +type parseError struct { + ErrorList *scanner.ErrorList + ErrorString string +} + +// parseErrorToString converts the error from parsing the file into a string +// representation. A nil error is converted to an empty string, and all other +// errors are converted to a JSON-marshalled parseError struct, with ErrorList +// set for errors of type scanner.ErrorList, and ErrorString set to the error's +// string representation for all other errors. +func parseErrorToString(err error) string { + if err == nil { + return "" + } + var p parseError + if e, ok := err.(scanner.ErrorList); ok { + p.ErrorList = &e + } else { + p.ErrorString = e.Error() + } + s, err := json.Marshal(p) + if err != nil { + panic(err) // This should be impossible because scanner.Error contains only strings and ints. + } + return string(s) +} + +// parseErrorFrom string converts a string produced by parseErrorToString back +// to an error. An empty string is converted to a nil error, and all +// other strings are expected to be JSON-marshalled parseError structs. +// The two functions are meant to preserve the structure of an +// error of type scanner.ErrorList in a round trip, but may not preserve the +// structure of other errors. +func parseErrorFromString(s string) error { + if s == "" { + return nil + } + var p parseError + if err := json.Unmarshal([]byte(s), &p); err != nil { + base.Fatalf(`go: invalid parse error value in index: %q. This indicates a corrupted index. Run "go clean -cache" to reset the module cache.`, s) + } + if p.ErrorList != nil { + return *p.ErrorList + } + return errors.New(p.ErrorString) +} + +// rawFile is the struct representation of the file holding all +// information in its fields. +type rawFile struct { + error string + parseError string + + name string + synopsis string // doc.Synopsis of package comment... Compute synopsis on all of these? + pkgName string + ignoreFile bool // starts with _ or . or should otherwise always be ignored + binaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment) + cgoDirectives string // the #cgo directive lines in the comment on import "C" + goBuildConstraint string + plusBuildConstraints []string + imports []rawImport + embeds []embed +} + +type rawImport struct { + path string + position token.Position +} + +type embed struct { + pattern string + position token.Position +} + +var pkgcache par.Cache // for packages not in modcache + +// importRaw fills the rawPackage from the package files in srcDir. +// dir is the package's path relative to the modroot. +func importRaw(modroot, reldir string) *rawPackage { + p := &rawPackage{ + dir: reldir, + } + + absdir := filepath.Join(modroot, reldir) + + // We still haven't checked + // that p.dir directory exists. This is the right time to do that check. + // We can't do it earlier, because we want to gather partial information for the + // non-nil *Package returned when an error occurs. + // We need to do this before we return early on FindOnly flag. + if !isDir(absdir) { + // package was not found + p.error = fmt.Errorf("cannot find package in:\n\t%s", absdir).Error() + return p + } + + entries, err := fsys.ReadDir(absdir) + if err != nil { + p.error = err.Error() + return p + } + + fset := token.NewFileSet() + for _, d := range entries { + if d.IsDir() { + continue + } + if d.Mode()&fs.ModeSymlink != 0 { + if isDir(filepath.Join(absdir, d.Name())) { + // Symlinks to directories are not source files. + continue + } + } + + name := d.Name() + ext := nameExt(name) + + if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") { + continue + } + info, err := getFileInfo(absdir, name, fset) + if err != nil { + p.sourceFiles = append(p.sourceFiles, &rawFile{name: name, error: err.Error()}) + continue + } else if info == nil { + p.sourceFiles = append(p.sourceFiles, &rawFile{name: name, ignoreFile: true}) + continue + } + rf := &rawFile{ + name: name, + goBuildConstraint: info.goBuildConstraint, + plusBuildConstraints: info.plusBuildConstraints, + binaryOnly: info.binaryOnly, + } + if info.parsed != nil { + rf.pkgName = info.parsed.Name.Name + } + + // Going to save the file. For non-Go files, can stop here. + p.sourceFiles = append(p.sourceFiles, rf) + if ext != ".go" { + continue + } + + if info.parseErr != nil { + rf.parseError = parseErrorToString(info.parseErr) + // Fall through: we might still have a partial AST in info.Parsed, + // and we want to list files with parse errors anyway. + } + + if info.parsed != nil && info.parsed.Doc != nil { + rf.synopsis = doc.Synopsis(info.parsed.Doc.Text()) + } + + var cgoDirectives []string + for _, imp := range info.imports { + if imp.path == "C" { + cgoDirectives = append(cgoDirectives, extractCgoDirectives(imp.doc.Text())...) + } + rf.imports = append(rf.imports, rawImport{path: imp.path, position: fset.Position(imp.pos)}) + } + rf.cgoDirectives = strings.Join(cgoDirectives, "\n") + for _, emb := range info.embeds { + rf.embeds = append(rf.embeds, embed{emb.pattern, emb.pos}) + } + + } + return p +} + +// extractCgoDirectives filters only the lines containing #cgo directives from the input, +// which is the comment on import "C". +func extractCgoDirectives(doc string) []string { + var out []string + for _, line := range strings.Split(doc, "\n") { + // Line is + // #cgo [GOOS/GOARCH...] LDFLAGS: stuff + // + line = strings.TrimSpace(line) + if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') { + continue + } + + out = append(out, line) + } + return out +} diff --git a/src/cmd/go/internal/modindex/syslist.go b/src/cmd/go/internal/modindex/syslist.go new file mode 100644 index 00000000000000..69b8fac331e57d --- /dev/null +++ b/src/cmd/go/internal/modindex/syslist.go @@ -0,0 +1,77 @@ +// 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. + +// This file is a lightly modified copy go/build/syslist_test.go. + +package modindex + +// knownOS is the list of past, present, and future known GOOS values. +// Do not remove from this list, as it is used for filename matching. +// If you add an entry to this list, look at unixOS, below. +var knownOS = map[string]bool{ + "aix": true, + "android": true, + "darwin": true, + "dragonfly": true, + "freebsd": true, + "hurd": true, + "illumos": true, + "ios": true, + "js": true, + "linux": true, + "nacl": true, + "netbsd": true, + "openbsd": true, + "plan9": true, + "solaris": true, + "windows": true, + "zos": true, +} + +// unixOS is the set of GOOS values matched by the "unix" build tag. +// This is not used for filename matching. +// This list also appears in cmd/dist/build.go. +var unixOS = map[string]bool{ + "aix": true, + "android": true, + "darwin": true, + "dragonfly": true, + "freebsd": true, + "hurd": true, + "illumos": true, + "ios": true, + "linux": true, + "netbsd": true, + "openbsd": true, + "solaris": true, +} + +// knownArch is the list of past, present, and future known GOARCH values. +// Do not remove from this list, as it is used for filename matching. +var knownArch = map[string]bool{ + "386": true, + "amd64": true, + "amd64p32": true, + "arm": true, + "armbe": true, + "arm64": true, + "arm64be": true, + "loong64": true, + "mips": true, + "mipsle": true, + "mips64": true, + "mips64le": true, + "mips64p32": true, + "mips64p32le": true, + "ppc": true, + "ppc64": true, + "ppc64le": true, + "riscv": true, + "riscv64": true, + "s390": true, + "s390x": true, + "sparc": true, + "sparc64": true, + "wasm": true, +} diff --git a/src/cmd/go/internal/modindex/syslist_test.go b/src/cmd/go/internal/modindex/syslist_test.go new file mode 100644 index 00000000000000..1a61562deffafc --- /dev/null +++ b/src/cmd/go/internal/modindex/syslist_test.go @@ -0,0 +1,65 @@ +// 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. + +// This file is a lightly modified copy go/build/syslist_test.go. + +package modindex + +import ( + "go/build" + "runtime" + "testing" +) + +var ( + thisOS = runtime.GOOS + thisArch = runtime.GOARCH + otherOS = anotherOS() + otherArch = anotherArch() +) + +func anotherOS() string { + if thisOS != "darwin" && thisOS != "ios" { + return "darwin" + } + return "linux" +} + +func anotherArch() string { + if thisArch != "amd64" { + return "amd64" + } + return "386" +} + +type GoodFileTest struct { + name string + result bool +} + +var tests = []GoodFileTest{ + {"file.go", true}, + {"file.c", true}, + {"file_foo.go", true}, + {"file_" + thisArch + ".go", true}, + {"file_" + otherArch + ".go", false}, + {"file_" + thisOS + ".go", true}, + {"file_" + otherOS + ".go", false}, + {"file_" + thisOS + "_" + thisArch + ".go", true}, + {"file_" + otherOS + "_" + thisArch + ".go", false}, + {"file_" + thisOS + "_" + otherArch + ".go", false}, + {"file_" + otherOS + "_" + otherArch + ".go", false}, + {"file_foo_" + thisArch + ".go", true}, + {"file_foo_" + otherArch + ".go", false}, + {"file_" + thisOS + ".c", true}, + {"file_" + otherOS + ".c", false}, +} + +func TestGoodOSArch(t *testing.T) { + for _, test := range tests { + if (*Context)(&build.Default).goodOSArchFile(test.name, make(map[string]bool)) != test.result { + t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) + } + } +} diff --git a/src/cmd/go/internal/modindex/write.go b/src/cmd/go/internal/modindex/write.go new file mode 100644 index 00000000000000..df1467d9d18a63 --- /dev/null +++ b/src/cmd/go/internal/modindex/write.go @@ -0,0 +1,158 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modindex + +import ( + "cmd/go/internal/base" + "encoding/binary" + "go/token" + "sort" +) + +const indexVersion = "go index v1" // 11 bytes (plus \n), to align uint32s in index + +// encodeModuleBytes produces the encoded representation of the module index. +// encodeModuleBytes may modify the packages slice. +func encodeModuleBytes(packages []*rawPackage) []byte { + e := newEncoder() + e.Bytes([]byte(indexVersion + "\n")) + stringTableOffsetPos := e.Pos() // fill this at the end + e.Uint32(0) // string table offset + sort.Slice(packages, func(i, j int) bool { + return packages[i].dir < packages[j].dir + }) + e.Int(len(packages)) + packagesPos := e.Pos() + for _, p := range packages { + e.String(p.dir) + e.Int(0) + } + for i, p := range packages { + e.IntAt(e.Pos(), packagesPos+8*i+4) + encodePackage(e, p) + } + e.IntAt(e.Pos(), stringTableOffsetPos) + e.Bytes(e.stringTable) + e.Bytes([]byte{0xFF}) // end of string table marker + return e.b +} + +func encodePackageBytes(p *rawPackage) []byte { + return encodeModuleBytes([]*rawPackage{p}) +} + +func encodePackage(e *encoder, p *rawPackage) { + e.String(p.error) + e.String(p.dir) + e.Int(len(p.sourceFiles)) // number of source files + sourceFileOffsetPos := e.Pos() // the pos of the start of the source file offsets + for range p.sourceFiles { + e.Int(0) + } + for i, f := range p.sourceFiles { + e.IntAt(e.Pos(), sourceFileOffsetPos+4*i) + encodeFile(e, f) + } +} + +func encodeFile(e *encoder, f *rawFile) { + e.String(f.error) + e.String(f.parseError) + e.String(f.synopsis) + e.String(f.name) + e.String(f.pkgName) + e.Bool(f.ignoreFile) + e.Bool(f.binaryOnly) + e.String(f.cgoDirectives) + e.String(f.goBuildConstraint) + + e.Int(len(f.plusBuildConstraints)) + for _, s := range f.plusBuildConstraints { + e.String(s) + } + + e.Int(len(f.imports)) + for _, m := range f.imports { + e.String(m.path) + e.Position(m.position) + } + + e.Int(len(f.embeds)) + for _, embed := range f.embeds { + e.String(embed.pattern) + e.Position(embed.position) + } +} + +func newEncoder() *encoder { + e := &encoder{strings: make(map[string]int)} + + // place the empty string at position 0 in the string table + e.stringTable = append(e.stringTable, 0) + e.strings[""] = 0 + + return e +} + +func (e *encoder) Position(position token.Position) { + e.String(position.Filename) + e.Int(position.Offset) + e.Int(position.Line) + e.Int(position.Column) +} + +type encoder struct { + b []byte + stringTable []byte + strings map[string]int +} + +func (e *encoder) Pos() int { + return len(e.b) +} + +func (e *encoder) Bytes(b []byte) { + e.b = append(e.b, b...) +} + +func (e *encoder) String(s string) { + if n, ok := e.strings[s]; ok { + e.Int(n) + return + } + pos := len(e.stringTable) + e.strings[s] = pos + e.Int(pos) + e.stringTable = binary.AppendUvarint(e.stringTable, uint64(len(s))) + e.stringTable = append(e.stringTable, s...) +} + +func (e *encoder) Bool(b bool) { + if b { + e.Uint32(1) + } else { + e.Uint32(0) + } +} + +func (e *encoder) Uint32(n uint32) { + e.b = binary.LittleEndian.AppendUint32(e.b, n) +} + +// Int encodes n. Note that all ints are written to the index as uint32s, +// and to avoid problems on 32-bit systems we require fitting into a 32-bit int. +func (e *encoder) Int(n int) { + if n < 0 || int(int32(n)) != n { + base.Fatalf("go: attempting to write an int to the index that overflows int32") + } + e.Uint32(uint32(n)) +} + +func (e *encoder) IntAt(n int, at int) { + if n < 0 || int(int32(n)) != n { + base.Fatalf("go: attempting to write an int to the index that overflows int32") + } + binary.LittleEndian.PutUint32(e.b[at:], uint32(n)) +} diff --git a/src/cmd/go/internal/modinfo/info.go b/src/cmd/go/internal/modinfo/info.go index 19088352f058ba..b0adcbcfb3dc99 100644 --- a/src/cmd/go/internal/modinfo/info.go +++ b/src/cmd/go/internal/modinfo/info.go @@ -4,7 +4,11 @@ package modinfo -import "time" +import ( + "cmd/go/internal/modfetch/codehost" + "encoding/json" + "time" +) // Note that these structs are publicly visible (part of go list's API) // and the fields are documented in the help text in ../list/list.go @@ -12,6 +16,7 @@ import "time" type ModulePublic struct { Path string `json:",omitempty"` // module path Version string `json:",omitempty"` // module version + Query string `json:",omitempty"` // version query corresponding to this version Versions []string `json:",omitempty"` // available module versions Replace *ModulePublic `json:",omitempty"` // replaced by this module Time *time.Time `json:",omitempty"` // time version was created @@ -24,12 +29,27 @@ type ModulePublic struct { Retracted []string `json:",omitempty"` // retraction information, if any (with -retracted or -u) Deprecated string `json:",omitempty"` // deprecation message, if any (with -u) Error *ModuleError `json:",omitempty"` // error loading module + + Origin *codehost.Origin `json:",omitempty"` // provenance of module + Reuse bool `json:",omitempty"` // reuse of old module info is safe } type ModuleError struct { Err string // error text } +type moduleErrorNoMethods ModuleError + +// UnmarshalJSON accepts both {"Err":"text"} and "text", +// so that the output of go mod download -json can still +// be unmarshalled into a ModulePublic during -reuse processing. +func (e *ModuleError) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '"' { + return json.Unmarshal(data, &e.Err) + } + return json.Unmarshal(data, (*moduleErrorNoMethods)(e)) +} + func (m *ModulePublic) String() string { s := m.Path versionString := func(mm *ModulePublic) string { diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go index 76e1ad589f43c1..bbece3f849f099 100644 --- a/src/cmd/go/internal/modload/build.go +++ b/src/cmd/go/internal/modload/build.go @@ -5,12 +5,10 @@ package modload import ( - "bytes" "context" "encoding/hex" "errors" "fmt" - "internal/goroot" "io/fs" "os" "path/filepath" @@ -19,6 +17,8 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modindex" "cmd/go/internal/modinfo" "cmd/go/internal/search" @@ -40,7 +40,7 @@ func findStandardImportPath(path string) string { panic("findStandardImportPath called with empty path") } if search.IsStandardImportPath(path) { - if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + if modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { return filepath.Join(cfg.GOROOT, "src", path) } } @@ -61,7 +61,27 @@ func PackageModuleInfo(ctx context.Context, pkgpath string) *modinfo.ModulePubli } rs := LoadModFile(ctx) - return moduleInfo(ctx, rs, m, 0) + return moduleInfo(ctx, rs, m, 0, nil) +} + +// PackageModRoot returns the module root directory for the module that provides +// a given package. If modules are not enabled or if the package is in the +// standard library or if the package was not successfully loaded with +// LoadPackages or ImportFromFiles, the empty string is returned. +func PackageModRoot(ctx context.Context, pkgpath string) string { + if isStandardImportPath(pkgpath) || !Enabled() || cfg.BuildMod == "vendor" { + return "" + } + m, ok := findModule(loaded, pkgpath) + if !ok { + return "" + } + const needSum = true + root, _, err := fetch(ctx, m, needSum) + if err != nil { + return "" + } + return root } func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic { @@ -71,7 +91,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic { if i := strings.Index(path, "@"); i >= 0 { m := module.Version{Path: path[:i], Version: path[i+1:]} - return moduleInfo(ctx, nil, m, 0) + return moduleInfo(ctx, nil, m, 0, nil) } rs := LoadModFile(ctx) @@ -80,7 +100,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic { v string ok bool ) - if rs.depth == lazy { + if rs.pruning == pruned { v, ok = rs.rootSelected(path) } if !ok { @@ -100,7 +120,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic { } } - return moduleInfo(ctx, rs, module.Version{Path: path, Version: v}, 0) + return moduleInfo(ctx, rs, module.Version{Path: path, Version: v}, 0, nil) } // addUpdate fills in m.Update if an updated version is available. @@ -111,10 +131,15 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) { info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed) var noVersionErr *NoMatchingVersionError - if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) { + if errors.Is(err, ErrDisallowed) || + errors.Is(err, fs.ErrNotExist) || + errors.As(err, &noVersionErr) { // Ignore "not found" and "no matching version" errors. // This means the proxy has no matching version or no versions at all. // + // Ignore "disallowed" errors. This means the current version is + // excluded or retracted and there are no higher allowed versions. + // // We should report other errors though. An attacker that controls the // network shouldn't be able to hide versions by interfering with // the HTTPS connection. An attacker that controls the proxy may still @@ -137,6 +162,49 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) { } } +// mergeOrigin merges two origins, +// returning and possibly modifying one of its arguments. +// If the two origins conflict, mergeOrigin returns a non-specific one +// that will not pass CheckReuse. +// If m1 or m2 is nil, the other is returned unmodified. +// But if m1 or m2 is non-nil and uncheckable, the result is also uncheckable, +// to preserve uncheckability. +func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin { + if m1 == nil { + return m2 + } + if m2 == nil { + return m1 + } + if !m1.Checkable() { + return m1 + } + if !m2.Checkable() { + return m2 + } + + merged := new(codehost.Origin) + *merged = *m1 // Clone to avoid overwriting fields in cached results. + + if m2.TagSum != "" { + if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) { + merged.ClearCheckable() + return merged + } + merged.TagSum = m2.TagSum + merged.TagPrefix = m2.TagPrefix + } + if m2.Hash != "" { + if m1.Hash != "" && (m1.Hash != m2.Hash || m1.Ref != m2.Ref) { + merged.ClearCheckable() + return merged + } + merged.Hash = m2.Hash + merged.Ref = m2.Ref + } + return merged +} + // addVersions fills in m.Versions with the list of known versions. // Excluded versions will be omitted. If listRetracted is false, retracted // versions will also be omitted. @@ -145,11 +213,12 @@ func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted boo if listRetracted { allowed = CheckExclusions } - var err error - m.Versions, err = versions(ctx, m.Path, allowed) + v, origin, err := versions(ctx, m.Path, allowed) if err != nil && m.Error == nil { m.Error = &modinfo.ModuleError{Err: err.Error()} } + m.Versions = v + m.Origin = mergeOrigin(m.Origin, origin) } // addRetraction fills in m.Retracted if the module was retracted by its author. @@ -211,21 +280,21 @@ func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) { // moduleInfo returns information about module m, loaded from the requirements // in rs (which may be nil to indicate that m was not loaded from a requirement // graph). -func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode) *modinfo.ModulePublic { - if m == Target { +func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) *modinfo.ModulePublic { + if m.Version == "" && MainModules.Contains(m.Path) { info := &modinfo.ModulePublic{ Path: m.Path, Version: m.Version, Main: true, } - if v, ok := rawGoVersion.Load(Target); ok { + if v, ok := rawGoVersion.Load(m); ok { info.GoVersion = v.(string) } else { panic("internal error: GoVersion not set for main module") } - if HasModRoot() { - info.Dir = ModRoot() - info.GoMod = ModFilePath() + if modRoot := MainModules.ModRoot(m); modRoot != "" { + info.Dir = modRoot + info.GoMod = modFilePath(modRoot) } return info } @@ -241,6 +310,15 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li // completeFromModCache fills in the extra fields in m using the module cache. completeFromModCache := func(m *modinfo.ModulePublic) { + if old := reuse[module.Version{Path: m.Path, Version: m.Version}]; old != nil { + if err := checkReuse(ctx, m.Path, old.Origin); err == nil { + *m = *old + m.Query = "" + m.Dir = "" + return + } + } + checksumOk := func(suffix string) bool { return rs == nil || m.Version == "" || cfg.BuildMod == "mod" || modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix}) @@ -322,7 +400,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li if filepath.IsAbs(r.Path) { info.Replace.Dir = r.Path } else { - info.Replace.Dir = filepath.Join(ModRoot(), r.Path) + info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path) } info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod") } @@ -336,115 +414,33 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li return info } -// PackageBuildInfo returns a string containing module version information -// for modules providing packages named by path and deps. path and deps must -// name packages that were resolved successfully with LoadPackages. -func PackageBuildInfo(path string, deps []string) string { - if isStandardImportPath(path) || !Enabled() { - return "" - } - - target := mustFindModule(loaded, path, path) - mdeps := make(map[module.Version]bool) - for _, dep := range deps { - if !isStandardImportPath(dep) { - mdeps[mustFindModule(loaded, path, dep)] = true - } - } - var mods []module.Version - delete(mdeps, target) - for mod := range mdeps { - mods = append(mods, mod) - } - module.Sort(mods) - - var buf bytes.Buffer - fmt.Fprintf(&buf, "path\t%s\n", path) - - writeEntry := func(token string, m module.Version) { - mv := m.Version - if mv == "" { - mv = "(devel)" - } - fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv) - if r := Replacement(m); r.Path == "" { - fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m)) - } else { - fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r)) - } - } - - writeEntry("mod", target) - for _, mod := range mods { - writeEntry("dep", mod) - } - - return buf.String() -} - -// mustFindModule is like findModule, but it calls base.Fatalf if the -// module can't be found. -// -// TODO(jayconrod): remove this. Callers should use findModule and return -// errors instead of relying on base.Fatalf. -func mustFindModule(ld *loader, target, path string) module.Version { - pkg, ok := ld.pkgCache.Get(path).(*loadPkg) - if ok { - if pkg.err != nil { - base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err) - } - return pkg.mod - } - - if path == "command-line-arguments" { - return Target - } - - base.Fatalf("build %v: cannot find module for path %v", target, path) - panic("unreachable") -} - // findModule searches for the module that contains the package at path. // If the package was loaded, its containing module and true are returned. -// Otherwise, module.Version{} and false are returend. +// Otherwise, module.Version{} and false are returned. func findModule(ld *loader, path string) (module.Version, bool) { if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok { return pkg.mod, pkg.mod != module.Version{} } - if path == "command-line-arguments" { - return Target, true - } return module.Version{}, false } func ModInfoProg(info string, isgccgo bool) []byte { - // Inject a variable with the debug information as runtime.modinfo, - // but compile it in package main so that it is specific to the binary. - // The variable must be a literal so that it will have the correct value - // before the initializer for package main runs. - // - // The runtime startup code refers to the variable, which keeps it live - // in all binaries. - // - // Note: we use an alternate recipe below for gccgo (based on an - // init function) due to the fact that gccgo does not support - // applying a "//go:linkname" directive to a variable. This has - // drawbacks in that other packages may want to look at the module - // info in their init functions (see issue 29628), which won't - // work for gccgo. See also issue 30344. - - if !isgccgo { - return []byte(fmt.Sprintf(`package main -import _ "unsafe" -//go:linkname __debug_modinfo__ runtime.modinfo -var __debug_modinfo__ = %q -`, string(infoStart)+info+string(infoEnd))) - } else { + // Inject an init function to set runtime.modinfo. + // This is only used for gccgo - with gc we hand the info directly to the linker. + // The init function has the drawback that packages may want to + // look at the module info in their init functions (see issue 29628), + // which won't work. See also issue 30344. + if isgccgo { return []byte(fmt.Sprintf(`package main import _ "unsafe" //go:linkname __set_debug_modinfo__ runtime.setmodinfo func __set_debug_modinfo__(string) func init() { __set_debug_modinfo__(%q) } -`, string(infoStart)+info+string(infoEnd))) +`, ModInfoData(info))) } + return nil +} + +func ModInfoData(info string) []byte { + return []byte(string(infoStart) + info + string(infoEnd)) } diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index bf6956731676d3..aa59611e8193ac 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -30,18 +30,25 @@ func capVersionSlice(s []module.Version) []module.Version { // A Requirements represents a logically-immutable set of root module requirements. type Requirements struct { - // depth is the depth at which the requirement graph is computed. + // pruning is the pruning at which the requirement graph is computed. // - // If eager, the graph includes all transitive requirements regardless of depth. + // If unpruned, the graph includes all transitive requirements regardless + // of whether the requiring module supports pruning. // - // If lazy, the graph includes only the root modules, the explicit + // If pruned, the graph includes only the root modules, the explicit // requirements of those root modules, and the transitive requirements of only - // the *non-lazy* root modules. - depth modDepth - - // rootModules is the set of module versions explicitly required by the main - // module, sorted and capped to length. It may contain duplicates, and may - // contain multiple versions for a given module path. + // the root modules that do not support pruning. + // + // If workspace, the graph includes only the workspace modules, the explicit + // requirements of the workspace modules, and the transitive requirements of + // the workspace modules that do not support pruning. + pruning modPruning + + // rootModules is the set of root modules of the graph, sorted and capped to + // length. It may contain duplicates, and may contain multiple versions for a + // given module path. The root modules of the groph are the set of main + // modules in workspace mode, and the main module's direct requirements + // outside workspace mode. rootModules []module.Version maxRootVersion map[string]string @@ -66,8 +73,8 @@ type Requirements struct { // nested module back into a parent module). direct map[string]bool - graphOnce sync.Once // guards writes to (but not reads from) graph - graph atomic.Value // cachedGraph + graphOnce sync.Once // guards writes to (but not reads from) graph + graph atomic.Pointer[cachedGraph] } // A cachedGraph is a non-nil *ModuleGraph, together with any error discovered @@ -97,10 +104,23 @@ var requirements *Requirements // // If vendoring is in effect, the caller must invoke initVendor on the returned // *Requirements before any other method. -func newRequirements(depth modDepth, rootModules []module.Version, direct map[string]bool) *Requirements { +func newRequirements(pruning modPruning, rootModules []module.Version, direct map[string]bool) *Requirements { + if pruning == workspace { + return &Requirements{ + pruning: pruning, + rootModules: capVersionSlice(rootModules), + maxRootVersion: nil, + direct: direct, + } + } + + if workFilePath != "" && pruning != workspace { + panic("in workspace mode, but pruning is not workspace in newRequirements") + } + for i, m := range rootModules { - if m == Target { - panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is Target", i)) + if m.Version == "" && MainModules.Contains(m.Path) { + panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is a main module", i)) } if m.Path == "" || m.Version == "" { panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m)) @@ -114,7 +134,7 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st } rs := &Requirements{ - depth: depth, + pruning: pruning, rootModules: capVersionSlice(rootModules), maxRootVersion: make(map[string]string, len(rootModules)), direct: direct, @@ -135,13 +155,18 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st func (rs *Requirements) initVendor(vendorList []module.Version) { rs.graphOnce.Do(func() { mg := &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, []module.Version{Target}), + g: mvs.NewGraph(cmpVersion, MainModules.Versions()), } - if rs.depth == lazy { - // The roots of a lazy module should already include every module in the - // vendor list, because the vendored modules are the same as those - // maintained as roots by the lazy loading “import invariant”. + if MainModules.Len() != 1 { + panic("There should be exactly one main module in Vendor mode.") + } + mainModule := MainModules.Versions()[0] + + if rs.pruning == pruned { + // The roots of a pruned module should already include every module in the + // vendor list, because the vendored modules are the same as those needed + // for graph pruning. // // Just to be sure, we'll double-check that here. inconsistent := false @@ -156,9 +181,9 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { } // Now we can treat the rest of the module graph as effectively “pruned - // out”, like a more aggressive version of lazy loading: in vendor mode, - // the root requirements *are* the complete module graph. - mg.g.Require(Target, rs.rootModules) + // out”, as though we are viewing the main module from outside: in vendor + // mode, the root requirements *are* the complete module graph. + mg.g.Require(mainModule, rs.rootModules) } else { // The transitive requirements of the main module are not in general available // from the vendor directory, and we don't actually know how we got from @@ -170,11 +195,11 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { // graph, but still distinguishes between direct and indirect // dependencies. vendorMod := module.Version{Path: "vendor/modules.txt", Version: ""} - mg.g.Require(Target, append(rs.rootModules, vendorMod)) + mg.g.Require(mainModule, append(rs.rootModules, vendorMod)) mg.g.Require(vendorMod, vendorList) } - rs.graph.Store(cachedGraph{mg, nil}) + rs.graph.Store(&cachedGraph{mg, nil}) }) } @@ -182,8 +207,8 @@ func (rs *Requirements) initVendor(vendorList []module.Version) { // path, or the zero module.Version and ok=false if the module is not a root // dependency. func (rs *Requirements) rootSelected(path string) (version string, ok bool) { - if path == Target.Path { - return Target.Version, true + if MainModules.Contains(path) { + return "", true } if v, ok := rs.maxRootVersion[path]; ok { return v, true @@ -197,7 +222,7 @@ func (rs *Requirements) rootSelected(path string) (version string, ok bool) { // selection. func (rs *Requirements) hasRedundantRoot() bool { for i, m := range rs.rootModules { - if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) { + if MainModules.Contains(m.Path) || (i > 0 && m.Path == rs.rootModules[i-1].Path) { return true } } @@ -214,10 +239,10 @@ func (rs *Requirements) hasRedundantRoot() bool { // returns a non-nil error of type *mvs.BuildListError. func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) { rs.graphOnce.Do(func() { - mg, mgErr := readModGraph(ctx, rs.depth, rs.rootModules) - rs.graph.Store(cachedGraph{mg, mgErr}) + mg, mgErr := readModGraph(ctx, rs.pruning, rs.rootModules) + rs.graph.Store(&cachedGraph{mg, mgErr}) }) - cached := rs.graph.Load().(cachedGraph) + cached := rs.graph.Load() return cached.mg, cached.err } @@ -230,7 +255,7 @@ func (rs *Requirements) IsDirect(path string) bool { // A ModuleGraph represents the complete graph of module dependencies // of a main module. // -// If the main module is lazily loaded, the graph does not include +// If the main module supports module graph pruning, the graph does not include // transitive dependencies of non-root (implicit) dependencies. type ModuleGraph struct { g *mvs.Graph @@ -254,8 +279,16 @@ var readModGraphDebugOnce sync.Once // // Unlike LoadModGraph, readModGraph does not attempt to diagnose or update // inconsistent roots. -func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (*ModuleGraph, error) { - if depth == lazy { +func readModGraph(ctx context.Context, pruning modPruning, roots []module.Version) (*ModuleGraph, error) { + if pruning == pruned { + // Enable diagnostics for lazy module loading + // (https://golang.org/ref/mod#lazy-loading) only if the module graph is + // pruned. + // + // In unpruned modules,we load the module graph much more aggressively (in + // order to detect inconsistencies that wouldn't be feasible to spot-check), + // so it wouldn't be useful to log when that occurs (because it happens in + // normal operation all the time). readModGraphDebugOnce.Do(func() { for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") { switch f { @@ -274,21 +307,26 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( mu sync.Mutex // guards mg.g and hasError during loading hasError bool mg = &ModuleGraph{ - g: mvs.NewGraph(cmpVersion, []module.Version{Target}), + g: mvs.NewGraph(cmpVersion, MainModules.Versions()), } ) - mg.g.Require(Target, roots) + if pruning != workspace { + if inWorkspaceMode() { + panic("pruning is not workspace in workspace mode") + } + mg.g.Require(MainModules.mustGetSingleMainModule(), roots) + } var ( - loadQueue = par.NewQueue(runtime.GOMAXPROCS(0)) - loadingEager sync.Map // module.Version → nil; the set of modules that have been or are being loaded via eager roots + loadQueue = par.NewQueue(runtime.GOMAXPROCS(0)) + loadingUnpruned sync.Map // module.Version → nil; the set of modules that have been or are being loaded via roots that do not support pruning ) // loadOne synchronously loads the explicit requirements for module m. // It does not load the transitive requirements of m even if the go version in - // m's go.mod file indicates eager loading. + // m's go.mod file indicates that it supports graph pruning. loadOne := func(m module.Version) (*modFileSummary, error) { - cached := mg.loadCache.Do(m, func() interface{} { + cached := mg.loadCache.Do(m, func() any { summary, err := goModSummary(m) mu.Lock() @@ -305,16 +343,16 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( return cached.summary, cached.err } - var enqueue func(m module.Version, depth modDepth) - enqueue = func(m module.Version, depth modDepth) { + var enqueue func(m module.Version, pruning modPruning) + enqueue = func(m module.Version, pruning modPruning) { if m.Version == "none" { return } - if depth == eager { - if _, dup := loadingEager.LoadOrStore(m, nil); dup { - // m has already been enqueued for loading. Since eager loading may - // follow cycles in the the requirement graph, we need to return early + if pruning == unpruned { + if _, dup := loadingUnpruned.LoadOrStore(m, nil); dup { + // m has already been enqueued for loading. Since unpruned loading may + // follow cycles in the requirement graph, we need to return early // to avoid making the load queue infinitely long. return } @@ -326,24 +364,73 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( return // findError will report the error later. } - // If the version in m's go.mod file implies eager loading, then we cannot - // assume that the explicit requirements of m (added by loadOne) are - // sufficient to build the packages it contains. We must load its full + // If the version in m's go.mod file does not support pruning, then we + // cannot assume that the explicit requirements of m (added by loadOne) + // are sufficient to build the packages it contains. We must load its full // transitive dependency graph to be sure that we see all relevant // dependencies. - if depth == eager || summary.depth == eager { + if pruning != pruned || summary.pruning == unpruned { + nextPruning := summary.pruning + if pruning == unpruned { + nextPruning = unpruned + } for _, r := range summary.require { - enqueue(r, eager) + enqueue(r, nextPruning) } } }) } for _, m := range roots { - enqueue(m, depth) + enqueue(m, pruning) } <-loadQueue.Idle() + // Reload any dependencies of the main modules which are not + // at their selected versions at workspace mode, because the + // requirements don't accurately reflect the transitive imports. + if pruning == workspace { + // hasDepsInAll contains the set of modules that need to be loaded + // at workspace pruning because any of their dependencies may + // provide packages in all. + hasDepsInAll := make(map[string]bool) + seen := map[module.Version]bool{} + for _, m := range roots { + hasDepsInAll[m.Path] = true + } + // This loop will terminate because it will call enqueue on each version of + // each dependency of the modules in hasDepsInAll at most once (and only + // calls enqueue on successively increasing versions of each dependency). + for { + needsEnqueueing := map[module.Version]bool{} + for p := range hasDepsInAll { + m := module.Version{Path: p, Version: mg.g.Selected(p)} + if !seen[m] { + needsEnqueueing[m] = true + continue + } + reqs, _ := mg.g.RequiredBy(m) + for _, r := range reqs { + s := module.Version{Path: r.Path, Version: mg.g.Selected(r.Path)} + if cmpVersion(s.Version, r.Version) > 0 && !seen[s] { + needsEnqueueing[s] = true + } + } + } + // add all needs enqueueing to paths we care about + if len(needsEnqueueing) == 0 { + break + } + + for p := range needsEnqueueing { + enqueue(p, workspace) + seen[p] = true + hasDepsInAll[p.Path] = true + } + <-loadQueue.Idle() + } + } + if hasError { return mg, mg.findError() } @@ -351,8 +438,7 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) ( } // RequiredBy returns the dependencies required by module m in the graph, -// or ok=false if module m's dependencies are not relevant (such as if they -// are pruned out by lazy loading). +// or ok=false if module m's dependencies are pruned out. // // The caller must not modify the returned slice, but may safely append to it // and may rely on it not to be modified. @@ -404,7 +490,12 @@ func (mg *ModuleGraph) findError() error { } func (mg *ModuleGraph) allRootsSelected() bool { - roots, _ := mg.g.RequiredBy(Target) + var roots []module.Version + if inWorkspaceMode() { + roots = MainModules.Versions() + } else { + roots, _ = mg.g.RequiredBy(MainModules.mustGetSingleMainModule()) + } for _, m := range roots { if mg.Selected(m.Path) != m.Version { return false @@ -427,12 +518,12 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { rs := LoadModFile(ctx) if goVersion != "" { - depth := modDepthFromGoVersion(goVersion) - if depth == eager && rs.depth != eager { + pruning := pruningForGoVersion(goVersion) + if pruning == unpruned && rs.pruning != unpruned { // Use newRequirements instead of convertDepth because convertDepth // also updates roots; here, we want to report the unmodified roots // even though they may seem inconsistent. - rs = newRequirements(eager, rs.rootModules, rs.direct) + rs = newRequirements(unpruned, rs.rootModules, rs.direct) } mg, err := rs.Graph(ctx) @@ -447,7 +538,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { base.Fatalf("go: %v", err) } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + return mg } @@ -455,9 +547,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { // // If the complete graph reveals that some root of rs is not actually the // selected version of its path, expandGraph computes a new set of roots that -// are consistent. (When lazy loading is implemented, this may result in -// upgrades to other modules due to requirements that were previously pruned -// out.) +// are consistent. (With a pruned module graph, this may result in upgrades to +// other modules due to requirements that were previously pruned out.) // // expandGraph returns the updated roots, along with the module graph loaded // from those roots and any error encountered while loading that graph. @@ -473,9 +564,9 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG if !mg.allRootsSelected() { // The roots of rs are not consistent with the rest of the graph. Update - // them. In an eager module this is a no-op for the build list as a whole — + // them. In an unpruned module this is a no-op for the build list as a whole — // it just promotes what were previously transitive requirements to be - // roots — but in a lazy module it may pull in previously-irrelevant + // roots — but in a pruned module it may pull in previously-irrelevant // transitive dependencies. newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false) @@ -513,7 +604,7 @@ func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (chang if err != nil { return false, err } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs return changed, err } @@ -544,40 +635,62 @@ type Conflict struct { // tidyRoots trims the root dependencies to the minimal requirements needed to // both retain the same versions of all packages in pkgs and satisfy the -// lazy loading invariants (if applicable). +// graph-pruning invariants (if applicable). func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Requirements, error) { - if rs.depth == eager { - return tidyEagerRoots(ctx, rs.direct, pkgs) + mainModule := MainModules.mustGetSingleMainModule() + if rs.pruning == unpruned { + return tidyUnprunedRoots(ctx, mainModule, rs.direct, pkgs) } - return tidyLazyRoots(ctx, rs.direct, pkgs) + return tidyPrunedRoots(ctx, mainModule, rs.direct, pkgs) } func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { - if rs.depth == eager { - return updateEagerRoots(ctx, direct, rs, add) + switch rs.pruning { + case unpruned: + return updateUnprunedRoots(ctx, direct, rs, add) + case pruned: + return updatePrunedRoots(ctx, direct, rs, pkgs, add, rootsImported) + case workspace: + return updateWorkspaceRoots(ctx, rs, add) + default: + panic(fmt.Sprintf("unsupported pruning mode: %v", rs.pruning)) + } +} + +func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Version) (*Requirements, error) { + if len(add) != 0 { + // add should be empty in workspace mode because workspace mode implies + // -mod=readonly, which in turn implies no new requirements. The code path + // that would result in add being non-empty returns an error before it + // reaches this point: The set of modules to add comes from + // resolveMissingImports, which in turn resolves each package by calling + // queryImport. But queryImport explicitly checks for -mod=readonly, and + // return an error. + panic("add is not empty") } - return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported) + return rs, nil } -// tidyLazyRoots returns a minimal set of root requirements that maintains the -// "lazy loading" invariants of the go.mod file for the given packages: +// tidyPrunedRoots returns a minimal set of root requirements that maintains the +// invariants of the go.mod file needed to support graph pruning for the given +// packages: // -// 1. For each package marked with pkgInAll, the module path that provided that -// package is included as a root. -// 2. For all packages, the module that provided that package either remains -// selected at the same version or is upgraded by the dependencies of a -// root. +// 1. For each package marked with pkgInAll, the module path that provided that +// package is included as a root. +// 2. For all packages, the module that provided that package either remains +// selected at the same version or is upgraded by the dependencies of a +// root. // -// If any module that provided a package has been upgraded above its previous, +// If any module that provided a package has been upgraded above its previous // version, the caller may need to reload and recompute the package graph. // // To ensure that the loading process eventually converges, the caller should // add any needed roots from the tidy root set (without removing existing untidy // roots) until the set of roots has converged. -func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { +func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { var ( roots []module.Version - pathIncluded = map[string]bool{Target.Path: true} + pathIncluded = map[string]bool{mainModule.Path: true} ) // We start by adding roots for every package in "all". // @@ -605,7 +718,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) queued[pkg] = true } module.Sort(roots) - tidy := newRequirements(lazy, roots, direct) + tidy := newRequirements(pruned, roots, direct) for len(queue) > 0 { roots = tidy.rootModules @@ -641,7 +754,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) if len(roots) > len(tidy.rootModules) { module.Sort(roots) - tidy = newRequirements(lazy, roots, tidy.direct) + tidy = newRequirements(pruned, roots, tidy.direct) } } @@ -652,51 +765,51 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) return tidy, nil } -// updateLazyRoots returns a set of root requirements that maintains the “lazy -// loading” invariants of the go.mod file: +// updatePrunedRoots returns a set of root requirements that maintains the +// invariants of the go.mod file needed to support graph pruning: // -// 1. The selected version of the module providing each package marked with -// either pkgInAll or pkgIsRoot is included as a root. -// Note that certain root patterns (such as '...') may explode the root set -// to contain every module that provides any package imported (or merely -// required) by any other module. -// 2. Each root appears only once, at the selected version of its path -// (if rs.graph is non-nil) or at the highest version otherwise present as a -// root (otherwise). -// 3. Every module path that appears as a root in rs remains a root. -// 4. Every version in add is selected at its given version unless upgraded by -// (the dependencies of) an existing root or another module in add. +// 1. The selected version of the module providing each package marked with +// either pkgInAll or pkgIsRoot is included as a root. +// Note that certain root patterns (such as '...') may explode the root set +// to contain every module that provides any package imported (or merely +// required) by any other module. +// 2. Each root appears only once, at the selected version of its path +// (if rs.graph is non-nil) or at the highest version otherwise present as a +// root (otherwise). +// 3. Every module path that appears as a root in rs remains a root. +// 4. Every version in add is selected at its given version unless upgraded by +// (the dependencies of) an existing root or another module in add. // // The packages in pkgs are assumed to have been loaded from either the roots of // rs or the modules selected in the graph of rs. // -// The above invariants together imply the “lazy loading” invariants for the +// The above invariants together imply the graph-pruning invariants for the // go.mod file: // -// 1. (The import invariant.) Every module that provides a package transitively -// imported by any package or test in the main module is included as a root. -// This follows by induction from (1) and (3) above. Transitively-imported -// packages loaded during this invocation are marked with pkgInAll (1), -// and by hypothesis any transitively-imported packages loaded in previous -// invocations were already roots in rs (3). +// 1. (The import invariant.) Every module that provides a package transitively +// imported by any package or test in the main module is included as a root. +// This follows by induction from (1) and (3) above. Transitively-imported +// packages loaded during this invocation are marked with pkgInAll (1), +// and by hypothesis any transitively-imported packages loaded in previous +// invocations were already roots in rs (3). // -// 2. (The argument invariant.) Every module that provides a package matching -// an explicit package pattern is included as a root. This follows directly -// from (1): packages matching explicit package patterns are marked with -// pkgIsRoot. +// 2. (The argument invariant.) Every module that provides a package matching +// an explicit package pattern is included as a root. This follows directly +// from (1): packages matching explicit package patterns are marked with +// pkgIsRoot. // -// 3. (The completeness invariant.) Every module that contributed any package -// to the build is required by either the main module or one of the modules -// it requires explicitly. This invariant is left up to the caller, who must -// not load packages from outside the module graph but may add roots to the -// graph, but is facilited by (3). If the caller adds roots to the graph in -// order to resolve missing packages, then updateLazyRoots will retain them, -// the selected versions of those roots cannot regress, and they will -// eventually be written back to the main module's go.mod file. +// 3. (The completeness invariant.) Every module that contributed any package +// to the build is required by either the main module or one of the modules +// it requires explicitly. This invariant is left up to the caller, who must +// not load packages from outside the module graph but may add roots to the +// graph, but is facilited by (3). If the caller adds roots to the graph in +// order to resolve missing packages, then updatePrunedRoots will retain them, +// the selected versions of those roots cannot regress, and they will +// eventually be written back to the main module's go.mod file. // // (See https://golang.org/design/36460-lazy-module-loading#invariants for more // detail.) -func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { +func updatePrunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) { roots := rs.rootModules rootsUpgraded := false @@ -717,11 +830,11 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // pkg is transitively imported by a package or test in the main module. // We need to promote the module that maintains it to a root: if some // other module depends on the main module, and that other module also - // uses lazy loading, it will expect to find all of our transitive - // dependencies by reading just our go.mod file, not the go.mod files of - // everything we depend on. + // uses a pruned module graph, it will expect to find all of our + // transitive dependencies by reading just our go.mod file, not the go.mod + // files of everything we depend on. // - // (This is the “import invariant” that makes lazy loading possible.) + // (This is the “import invariant” that makes graph pruning possible.) case rootsImported && pkg.flags.has(pkgFromRoot): // pkg is a transitive dependency of some root, and we are treating the @@ -732,17 +845,18 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // it matches a command-line argument.) We want future invocations of the // 'go' command — such as 'go test' on the same package — to continue to // use the same versions of its dependencies that we are using right now. - // So we need to bring this package's dependencies inside the lazy-loading - // horizon. + // So we need to bring this package's dependencies inside the pruned + // module graph. // // Making the module containing this package a root of the module graph - // does exactly that: if the module containing the package is lazy it - // should satisfy the import invariant itself, so all of its dependencies - // should be in its go.mod file, and if the module containing the package - // is eager then if we make it a root we will load all of its transitive - // dependencies into the module graph. + // does exactly that: if the module containing the package supports graph + // pruning then it should satisfy the import invariant itself, so all of + // its dependencies should be in its go.mod file, and if the module + // containing the package does not support pruning then if we make it a + // root we will load all of its (unpruned) transitive dependencies into + // the module graph. // - // (This is the “argument invariant” of lazy loading, and is important for + // (This is the “argument invariant”, and is important for // reproducibility.) default: @@ -807,16 +921,15 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // We've added or upgraded one or more roots, so load the full module // graph so that we can update those roots to be consistent with other // requirements. - if cfg.BuildMod != "mod" { + if mustHaveCompleteRequirements() { // Our changes to the roots may have moved dependencies into or out of - // the lazy-loading horizon, which could in turn change the selected - // versions of other modules. (Unlike for eager modules, for lazy - // modules adding or removing an explicit root is a semantic change, not - // just a cosmetic one.) + // the graph-pruning horizon, which could in turn change the selected + // versions of other modules. (For pruned modules adding or removing an + // explicit root is a semantic change, not just a cosmetic one.) return rs, errGoModDirty } - rs = newRequirements(lazy, roots, direct) + rs = newRequirements(pruned, roots, direct) var err error mg, err = rs.Graph(ctx) if err != nil { @@ -831,7 +944,7 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen if rs.graph.Load() != nil { // We've already loaded the full module graph, which includes the // requirements of all of the root modules — even the transitive - // requirements, if they are eager! + // requirements, if they are unpruned! mg, _ = rs.Graph(ctx) } else if cfg.BuildMod == "vendor" { // We can't spot-check the requirements of other modules because we @@ -855,7 +968,9 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen roots = make([]module.Version, 0, len(rs.rootModules)) rootsUpgraded = false inRootPaths := make(map[string]bool, len(rs.rootModules)+1) - inRootPaths[Target.Path] = true + for _, mm := range MainModules.Versions() { + inRootPaths[mm.Path] = true + } for _, m := range rs.rootModules { if inRootPaths[m.Path] { // This root specifies a redundant path. We already retained the @@ -908,12 +1023,12 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen } } - if rs.depth == lazy && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { - // The root set is unchanged and rs was already lazy, so keep rs to + if rs.pruning == pruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { + // The root set is unchanged and rs was already pruned, so keep rs to // preserve its cached ModuleGraph (if any). return rs, nil } - return newRequirements(lazy, roots, direct), nil + return newRequirements(pruned, roots, direct), nil } // spotCheckRoots reports whether the versions of the roots in rs satisfy the @@ -955,17 +1070,37 @@ func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Versi return true } -// tidyEagerRoots returns a minimal set of root requirements that maintains the -// selected version of every module that provided a package in pkgs, and -// includes the selected version of every such module in direct as a root. -func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { +// tidyUnprunedRoots returns a minimal set of root requirements that maintains +// the selected version of every module that provided or lexically could have +// provided a package in pkgs, and includes the selected version of every such +// module in direct as a root. +func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { var ( + // keep is a set of of modules that provide packages or are needed to + // disambiguate imports. keep []module.Version keptPath = map[string]bool{} - ) - var ( - rootPaths []string // module paths that should be included as roots + + // rootPaths is a list of module paths that provide packages directly + // imported from the main module. They should be included as roots. + rootPaths []string inRootPaths = map[string]bool{} + + // altMods is a set of paths of modules that lexically could have provided + // imported packages. It may be okay to remove these from the list of + // explicit requirements if that removes them from the module graph. If they + // are present in the module graph reachable from rootPaths, they must not + // be at a lower version. That could cause a missing sum error or a new + // import ambiguity. + // + // For example, suppose a developer rewrites imports from example.com/m to + // example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the + // requirement on example.com/m if there is no other transitive requirement + // on it. However, if example.com/m were downgraded to a version not in + // go.sum, when package example.com/m/v2/p is loaded, we'd get an error + // trying to disambiguate the import, since we can't check example.com/m + // without its sum. See #47738. + altMods = map[string]string{} ) for _, pkg := range pkgs { if !pkg.fromExternalModule() { @@ -979,30 +1114,62 @@ func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg inRootPaths[m.Path] = true } } + for _, m := range pkg.altMods { + altMods[m.Path] = m.Version + } } - min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep}) + // Construct a build list with a minimal set of roots. + // This may remove or downgrade modules in altMods. + reqs := &mvsReqs{roots: keep} + min, err := mvs.Req(mainModule, rootPaths, reqs) if err != nil { return nil, err } - return newRequirements(eager, min, direct), nil + buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs) + if err != nil { + return nil, err + } + + // Check if modules in altMods were downgraded but not removed. + // If so, add them to roots, which will retain an "// indirect" requirement + // in go.mod. See comment on altMods above. + keptAltMod := false + for _, m := range buildList { + if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 { + keep = append(keep, module.Version{Path: m.Path, Version: v}) + keptAltMod = true + } + } + if keptAltMod { + // We must run mvs.Req again instead of simply adding altMods to min. + // It's possible that a requirement in altMods makes some other + // explicit indirect requirement unnecessary. + reqs.roots = keep + min, err = mvs.Req(mainModule, rootPaths, reqs) + if err != nil { + return nil, err + } + } + + return newRequirements(unpruned, min, direct), nil } -// updateEagerRoots returns a set of root requirements that includes the selected +// updateUnprunedRoots returns a set of root requirements that includes the selected // version of every module path in direct as a root, and maintains the selected // version of every module selected in the graph of rs. // // The roots are updated such that: // -// 1. The selected version of every module path in direct is included as a root -// (if it is not "none"). -// 2. Each root is the selected version of its path. (We say that such a root -// set is “consistent”.) -// 3. Every version selected in the graph of rs remains selected unless upgraded -// by a dependency in add. -// 4. Every version in add is selected at its given version unless upgraded by -// (the dependencies of) an existing root or another module in add. -func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) { +// 1. The selected version of every module path in direct is included as a root +// (if it is not "none"). +// 2. Each root is the selected version of its path. (We say that such a root +// set is “consistent”.) +// 3. Every version selected in the graph of rs remains selected unless upgraded +// by a dependency in add. +// 4. Every version in add is selected at its given version unless upgraded by +// (the dependencies of) an existing root or another module in add. +func updateUnprunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) { mg, err := rs.Graph(ctx) if err != nil { // We can't ignore errors in the module graph even if the user passed the -e @@ -1011,7 +1178,7 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme return rs, err } - if cfg.BuildMod != "mod" { + if mustHaveCompleteRequirements() { // Instead of actually updating the requirements, just check that no updates // are needed. if rs == nil { @@ -1067,10 +1234,10 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme // “The selected version of every module path in direct is included as a root.” // - // This is only for convenience and clarity for end users: in an eager module, + // This is only for convenience and clarity for end users: in an unpruned module, // the choice of explicit vs. implicit dependency has no impact on MVS // selection (for itself or any other module). - keep := append(mg.BuildList()[1:], add...) + keep := append(mg.BuildList()[MainModules.Len():], add...) for _, m := range keep { if direct[m.Path] && !inRootPaths[m.Path] { rootPaths = append(rootPaths, m.Path) @@ -1078,44 +1245,53 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme } } - min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep}) - if err != nil { - return rs, err + var roots []module.Version + for _, mainModule := range MainModules.Versions() { + min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep}) + if err != nil { + return rs, err + } + roots = append(roots, min...) } - if rs.depth == eager && reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { - // The root set is unchanged and rs was already eager, so keep rs to + if MainModules.Len() > 1 { + module.Sort(roots) + } + if rs.pruning == unpruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) { + // The root set is unchanged and rs was already unpruned, so keep rs to // preserve its cached ModuleGraph (if any). return rs, nil } - return newRequirements(eager, min, direct), nil + + return newRequirements(unpruned, roots, direct), nil } -// convertDepth returns a version of rs with the given depth. -// If rs already has the given depth, convertDepth returns rs unmodified. -func convertDepth(ctx context.Context, rs *Requirements, depth modDepth) (*Requirements, error) { - if rs.depth == depth { +// convertPruning returns a version of rs with the given pruning behavior. +// If rs already has the given pruning, convertPruning returns rs unmodified. +func convertPruning(ctx context.Context, rs *Requirements, pruning modPruning) (*Requirements, error) { + if rs.pruning == pruning { return rs, nil + } else if rs.pruning == workspace || pruning == workspace { + panic("attempthing to convert to/from workspace pruning and another pruning type") } - if depth == eager { - // We are converting a lazy module to an eager one. The roots of an eager - // module graph are a superset of the roots of a lazy graph, so we don't - // need to add any new roots — we just need to prune away the ones that are - // redundant given eager loading, which is exactly what updateEagerRoots - // does. - return updateEagerRoots(ctx, rs.direct, rs, nil) + if pruning == unpruned { + // We are converting a pruned module to an unpruned one. The roots of a + // ppruned module graph are a superset of the roots of an unpruned one, so + // we don't need to add any new roots — we just need to drop the ones that + // are redundant, which is exactly what updateUnprunedRoots does. + return updateUnprunedRoots(ctx, rs.direct, rs, nil) } - // We are converting an eager module to a lazy one. The module graph of an - // eager module includes the transitive dependencies of every module in the - // build list. + // We are converting an unpruned module to a pruned one. // - // Hey, we can express that as a lazy root set! “Include the transitive - // dependencies of every module in the build list” is exactly what happens in - // a lazy module if we promote every module in the build list to a root! + // An unpruned module graph includes the transitive dependencies of every + // module in the build list. As it turns out, we can express that as a pruned + // root set! “Include the transitive dependencies of every module in the build + // list” is exactly what happens in a pruned module if we promote every module + // in the build list to a root. mg, err := rs.Graph(ctx) if err != nil { return rs, err } - return newRequirements(lazy, mg.BuildList()[1:], rs.direct), nil + return newRequirements(pruned, mg.BuildList()[MainModules.Len():], rs.direct), nil } diff --git a/src/cmd/go/internal/modload/edit.go b/src/cmd/go/internal/modload/edit.go index c350b9d1b5c571..f6937a48b4072b 100644 --- a/src/cmd/go/internal/modload/edit.go +++ b/src/cmd/go/internal/modload/edit.go @@ -16,20 +16,20 @@ import ( // editRequirements returns an edited version of rs such that: // -// 1. Each module version in mustSelect is selected. +// 1. Each module version in mustSelect is selected. // -// 2. Each module version in tryUpgrade is upgraded toward the indicated -// version as far as can be done without violating (1). +// 2. Each module version in tryUpgrade is upgraded toward the indicated +// version as far as can be done without violating (1). // -// 3. Each module version in rs.rootModules (or rs.graph, if rs.depth is eager) -// is downgraded from its original version only to the extent needed to -// satisfy (1), or upgraded only to the extent needed to satisfy (1) and -// (2). +// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned) +// is downgraded from its original version only to the extent needed to +// satisfy (1), or upgraded only to the extent needed to satisfy (1) and +// (2). // -// 4. No module is upgraded above the maximum version of its path found in the -// dependency graph of rs, the combined dependency graph of the versions in -// mustSelect, or the dependencies of each individual module version in -// tryUpgrade. +// 4. No module is upgraded above the maximum version of its path found in the +// dependency graph of rs, the combined dependency graph of the versions in +// mustSelect, or the dependencies of each individual module version in +// tryUpgrade. // // Generally, the module versions in mustSelect are due to the module or a // package within the module matching an explicit command line argument to 'go @@ -69,13 +69,14 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel } var roots []module.Version - if rs.depth == eager { - // In an eager module, modules that provide packages imported by the main - // module may either be explicit roots or implicit transitive dependencies. - // We promote the modules in mustSelect to be explicit requirements. + if rs.pruning == unpruned { + // In a module without graph pruning, modules that provide packages imported + // by the main module may either be explicit roots or implicit transitive + // dependencies. We promote the modules in mustSelect to be explicit + // requirements. var rootPaths []string for _, m := range mustSelect { - if m.Version != "none" && m.Path != Target.Path { + if m.Version != "none" && !MainModules.Contains(m.Path) { rootPaths = append(rootPaths, m.Path) } } @@ -97,13 +98,13 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel } } - roots, err = mvs.Req(Target, rootPaths, &mvsReqs{roots: mods}) + roots, err = mvs.Req(MainModules.mustGetSingleMainModule(), rootPaths, &mvsReqs{roots: mods}) if err != nil { return nil, false, err } } else { - // In a lazy module, every module that provides a package imported by the - // main module must be retained as a root. + // In a module with a pruned graph, every module that provides a package + // imported by the main module must be retained as a root. roots = mods if !changed { // Because the roots we just computed are unchanged, the entire graph must @@ -126,7 +127,7 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel direct[m.Path] = true } } - return newRequirements(rs.depth, roots, direct), changed, nil + return newRequirements(rs.pruning, roots, direct), changed, nil } // limiterForEdit returns a versionLimiter with its max versions set such that @@ -149,11 +150,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec } } - if rs.depth == eager { - // Eager go.mod files don't indicate which transitive dependencies are - // actually relevant to the main module, so we have to assume that any module - // that could have provided any package — that is, any module whose selected - // version was not "none" — may be relevant. + if rs.pruning == unpruned { + // go.mod files that do not support graph pruning don't indicate which + // transitive dependencies are actually relevant to the main module, so we + // have to assume that any module that could have provided any package — + // that is, any module whose selected version was not "none" — may be + // relevant. for _, m := range mg.BuildList() { restrictTo(m) } @@ -175,7 +177,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec } } - if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.depth, tryUpgrade, mustSelect); err != nil { + if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.pruning, tryUpgrade, mustSelect); err != nil { return nil, err } @@ -185,22 +187,22 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec restrictTo(m) } - return newVersionLimiter(rs.depth, maxVersion), nil + return newVersionLimiter(rs.pruning, maxVersion), nil } // raiseLimitsForUpgrades increases the module versions in maxVersions to the // versions that would be needed to allow each of the modules in tryUpgrade -// (individually) and all of the modules in mustSelect (simultaneously) to be -// added as roots. +// (individually or in any combination) and all of the modules in mustSelect +// (simultaneously) to be added as roots. // // Versions not present in maxVersion are unrestricted, and it is assumed that // they will not be promoted to root requirements (and thus will not contribute -// their own dependencies if the main module is lazy). +// their own dependencies if the main module supports graph pruning). // // These limits provide an upper bound on how far a module may be upgraded as // part of an incidental downgrade, if downgrades are needed in order to select // the versions in mustSelect. -func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, depth modDepth, tryUpgrade []module.Version, mustSelect []module.Version) error { +func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, pruning modPruning, tryUpgrade []module.Version, mustSelect []module.Version) error { // allow raises the limit for m.Path to at least m.Version. // If m.Path was already unrestricted, it remains unrestricted. allow := func(m module.Version) { @@ -213,51 +215,88 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d } } - var eagerUpgrades []module.Version - if depth == eager { - eagerUpgrades = tryUpgrade + var ( + unprunedUpgrades []module.Version + isPrunedRootPath map[string]bool + ) + if pruning == unpruned { + unprunedUpgrades = tryUpgrade } else { + isPrunedRootPath = make(map[string]bool, len(maxVersion)) + for p := range maxVersion { + isPrunedRootPath[p] = true + } for _, m := range tryUpgrade { - if m.Path == Target.Path { - // Target is already considered to be higher than any possible m, so we - // won't be upgrading to it anyway and there is no point scanning its - // dependencies. - continue + isPrunedRootPath[m.Path] = true + } + for _, m := range mustSelect { + isPrunedRootPath[m.Path] = true + } + + allowedRoot := map[module.Version]bool{} + + var allowRoot func(m module.Version) error + allowRoot = func(m module.Version) error { + if allowedRoot[m] { + return nil + } + allowedRoot[m] = true + + if MainModules.Contains(m.Path) { + // The main module versions are already considered to be higher than any + // possible m, so m cannot be selected as a root and there is no point + // scanning its dependencies. + return nil } + allow(m) + summary, err := goModSummary(m) if err != nil { return err } - if summary.depth == eager { - // For efficiency, we'll load all of the eager upgrades as one big + if summary.pruning == unpruned { + // For efficiency, we'll load all of the unpruned upgrades as one big // graph, rather than loading the (potentially-overlapping) subgraph for // each upgrade individually. - eagerUpgrades = append(eagerUpgrades, m) - continue + unprunedUpgrades = append(unprunedUpgrades, m) + return nil } - for _, r := range summary.require { - allow(r) + if isPrunedRootPath[r.Path] { + // r could become a root as the result of an upgrade or downgrade, + // in which case its dependencies will not be pruned out. + // We need to allow those dependencies to be upgraded too. + if err := allowRoot(r); err != nil { + return err + } + } else { + // r will not become a root, so its dependencies don't matter. + // Allow only r itself. + allow(r) + } } + return nil + } + + for _, m := range tryUpgrade { + allowRoot(m) } } - if len(eagerUpgrades) > 0 { - // Compute the max versions for eager upgrades all together. - // Since these modules are eager, we'll end up scanning all of their + if len(unprunedUpgrades) > 0 { + // Compute the max versions for unpruned upgrades all together. + // Since these modules are unpruned, we'll end up scanning all of their // transitive dependencies no matter which versions end up selected, // and since we have a large dependency graph to scan we might get // a significant benefit from not revisiting dependencies that are at // common versions among multiple upgrades. - upgradeGraph, err := readModGraph(ctx, eager, eagerUpgrades) + upgradeGraph, err := readModGraph(ctx, unpruned, unprunedUpgrades) if err != nil { - if go117LazyTODO { - // Compute the requirement path from a module path in tryUpgrade to the - // error, and the requirement path (if any) from rs.rootModules to the - // tryUpgrade module path. Return a *mvs.BuildListError showing the - // concatenation of the paths (with an upgrade in the middle). - } + // Compute the requirement path from a module path in tryUpgrade to the + // error, and the requirement path (if any) from rs.rootModules to the + // tryUpgrade module path. Return a *mvs.BuildListError showing the + // concatenation of the paths (with an upgrade in the middle). return err } @@ -268,16 +307,41 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d } } - if len(mustSelect) > 0 { - mustGraph, err := readModGraph(ctx, depth, mustSelect) + // Explicitly allow any (transitive) upgrades implied by mustSelect. + nextRoots := append([]module.Version(nil), mustSelect...) + for nextRoots != nil { + module.Sort(nextRoots) + rs := newRequirements(pruning, nextRoots, nil) + nextRoots = nil + + rs, mustGraph, err := expandGraph(ctx, rs) if err != nil { return err } for _, r := range mustGraph.BuildList() { - // Some module in mustSelect requires r, so we must allow at least r.Version - // unless it conflicts with an entry in mustSelect. + // Some module in mustSelect requires r, so we must allow at least + // r.Version (unless it conflicts with another entry in mustSelect, in + // which case we will error out either way). allow(r) + + if isPrunedRootPath[r.Path] { + if v, ok := rs.rootSelected(r.Path); ok && r.Version == v { + // r is already a root, so its requirements are already included in + // the build list. + continue + } + + // The dependencies in mustSelect may upgrade (or downgrade) an existing + // root to match r, which will remain as a root. However, since r is not + // a root of rs, its dependencies have been pruned out of this build + // list. We need to add it back explicitly so that we allow any + // transitive upgrades that r will pull in. + if nextRoots == nil { + nextRoots = rs.rootModules // already capped + } + nextRoots = append(nextRoots, r) + } } } @@ -301,12 +365,12 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit } var initial []module.Version - if rs.depth == eager { + if rs.pruning == unpruned { mg, err := rs.Graph(ctx) if err != nil { return nil, false, err } - initial = mg.BuildList()[1:] + initial = mg.BuildList()[MainModules.Len():] } else { initial = rs.rootModules } @@ -318,7 +382,7 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit mods = make([]module.Version, 0, len(limiter.selected)) for path, v := range limiter.selected { - if v != "none" && path != Target.Path { + if v != "none" && !MainModules.Contains(path) { mods = append(mods, module.Version{Path: path, Version: v}) } } @@ -328,13 +392,13 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit // downgraded module may require a higher (but still allowed) version of // another. The lower version may require extraneous dependencies that aren't // actually relevant, so we need to compute the actual selected versions. - mg, err := readModGraph(ctx, rs.depth, mods) + mg, err := readModGraph(ctx, rs.pruning, mods) if err != nil { return nil, false, err } mods = make([]module.Version, 0, len(limiter.selected)) for path, _ := range limiter.selected { - if path != Target.Path { + if !MainModules.Contains(path) { if v := mg.Selected(path); v != "none" { mods = append(mods, module.Version{Path: path, Version: v}) } @@ -350,16 +414,16 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit // A versionLimiter tracks the versions that may be selected for each module // subject to constraints on the maximum versions of transitive dependencies. type versionLimiter struct { - // depth is the depth at which the dependencies of the modules passed to + // pruning is the pruning at which the dependencies of the modules passed to // Select and UpgradeToward are loaded. - depth modDepth + pruning modPruning // max maps each module path to the maximum version that may be selected for // that path. // // Paths with no entry are unrestricted, and we assume that they will not be // promoted to root dependencies (so will not contribute dependencies if the - // main module is lazy). + // main module supports graph pruning). max map[string]string // selected maps each module path to a version of that path (if known) whose @@ -411,14 +475,18 @@ func (dq dqState) isDisqualified() bool { // in the map are unrestricted. The limiter assumes that unrestricted paths will // not be promoted to root dependencies. // -// If depth is lazy, then if a module passed to UpgradeToward or Select is -// itself lazy, its unrestricted dependencies are skipped when scanning -// requirements. -func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter { +// If module graph pruning is in effect, then if a module passed to +// UpgradeToward or Select supports pruning, its unrestricted dependencies are +// skipped when scanning requirements. +func newVersionLimiter(pruning modPruning, max map[string]string) *versionLimiter { + selected := make(map[string]string) + for _, m := range MainModules.Versions() { + selected[m.Path] = m.Version + } return &versionLimiter{ - depth: depth, + pruning: pruning, max: max, - selected: map[string]string{Target.Path: Target.Version}, + selected: selected, dqReason: map[module.Version]dqState{}, requiring: map[module.Version][]module.Version{}, } @@ -427,8 +495,8 @@ func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter { // UpgradeToward attempts to upgrade the selected version of m.Path as close as // possible to m.Version without violating l's maximum version limits. // -// If depth is lazy and m itself is lazy, the the dependencies of unrestricted -// dependencies of m will not be followed. +// If module graph pruning is in effect and m itself supports pruning, the +// dependencies of unrestricted dependencies of m will not be followed. func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) error { selected, ok := l.selected[m.Path] if ok { @@ -440,8 +508,8 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er selected = "none" } - if l.check(m, l.depth).isDisqualified() { - candidates, err := versions(ctx, m.Path, CheckAllowed) + if l.check(m, l.pruning).isDisqualified() { + candidates, _, err := versions(ctx, m.Path, CheckAllowed) if err != nil { // This is likely a transient error reaching the repository, // rather than a permanent error with the retrieved version. @@ -457,7 +525,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er }) candidates = candidates[:i] - for l.check(m, l.depth).isDisqualified() { + for l.check(m, l.pruning).isDisqualified() { n := len(candidates) if n == 0 || cmpVersion(selected, candidates[n-1]) >= 0 { // We couldn't find a suitable candidate above the already-selected version. @@ -474,7 +542,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er // Select attempts to set the selected version of m.Path to exactly m.Version. func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err error) { - dq := l.check(m, l.depth) + dq := l.check(m, l.pruning) if !dq.isDisqualified() { l.selected[m.Path] = m.Version } @@ -484,15 +552,15 @@ func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err // check determines whether m (or its transitive dependencies) would violate l's // maximum version limits if added to the module requirement graph. // -// If depth is lazy and m itself is lazy, then the dependencies of unrestricted -// dependencies of m will not be followed. If the lazy loading invariants hold -// for the main module up to this point, the packages in those modules are at -// best only imported by tests of dependencies that are themselves loaded from -// outside modules. Although we would like to keep 'go test all' as reproducible -// as is feasible, we don't want to retain test dependencies that are only -// marginally relevant at best. -func (l *versionLimiter) check(m module.Version, depth modDepth) dqState { - if m.Version == "none" || m == Target { +// If pruning is in effect and m itself supports graph pruning, the dependencies +// of unrestricted dependencies of m will not be followed. If the graph-pruning +// invariants hold for the main module up to this point, the packages in those +// modules are at best only imported by tests of dependencies that are +// themselves loaded from outside modules. Although we would like to keep +// 'go test all' as reproducible as is feasible, we don't want to retain test +// dependencies that are only marginally relevant at best. +func (l *versionLimiter) check(m module.Version, pruning modPruning) dqState { + if m.Version == "none" || m == MainModules.mustGetSingleMainModule() { // version "none" has no requirements, and the dependencies of Target are // tautological. return dqState{} @@ -522,20 +590,20 @@ func (l *versionLimiter) check(m module.Version, depth modDepth) dqState { return l.disqualify(m, dqState{err: err}) } - if summary.depth == eager { - depth = eager + if summary.pruning == unpruned { + pruning = unpruned } for _, r := range summary.require { - if depth == lazy { + if pruning == pruned { if _, restricted := l.max[r.Path]; !restricted { // r.Path is unrestricted, so we don't care at what version it is // selected. We assume that r.Path will not become a root dependency, so - // since m is lazy, r's dependencies won't be followed. + // since m supports pruning, r's dependencies won't be followed. continue } } - if dq := l.check(r, depth); dq.isDisqualified() { + if dq := l.check(r, pruning); dq.isDisqualified() { return l.disqualify(m, dq) } diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go index d2bbe5cbe0b1ea..f2c7592a284e57 100644 --- a/src/cmd/go/internal/modload/import.go +++ b/src/cmd/go/internal/modload/import.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "go/build" - "internal/goroot" "io/fs" "os" pathpkg "path" @@ -20,8 +19,10 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/modfetch" + "cmd/go/internal/modindex" "cmd/go/internal/par" "cmd/go/internal/search" + "cmd/go/internal/str" "golang.org/x/mod/module" "golang.org/x/mod/semver" @@ -32,6 +33,8 @@ type ImportMissingError struct { Module module.Version QueryErr error + ImportingMainModule module.Version + // isStd indicates whether we would expect to find the package in the standard // library. This is normally true for all dotless import paths, but replace // directives can cause us to treat the replaced paths as also being in @@ -71,6 +74,9 @@ func (e *ImportMissingError) Error() string { if e.QueryErr != nil { return fmt.Sprintf("%s: %v", message, e.QueryErr) } + if e.ImportingMainModule.Path != "" && e.ImportingMainModule != MainModules.ModContainingCWD() { + return fmt.Sprintf("%s; to add it:\n\tcd %s\n\tgo get %s", message, MainModules.ModRoot(e.ImportingMainModule), e.Path) + } return fmt.Sprintf("%s; to add it:\n\tgo get %s", message, e.Path) } @@ -238,59 +244,85 @@ func (e *invalidImportError) Unwrap() error { // // If the package is not present in any module selected from the requirement // graph, importFromModules returns an *ImportMissingError. -func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) { +// +// If the package is present in exactly one module, importFromModules will +// return the module, its root directory, and a list of other modules that +// lexically could have provided the package but did not. +func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, modroot, dir string, altMods []module.Version, err error) { + invalidf := func(format string, args ...interface{}) (module.Version, string, string, []module.Version, error) { + return module.Version{}, "", "", nil, &invalidImportError{ + importPath: path, + err: fmt.Errorf(format, args...), + } + } + if strings.Contains(path, "@") { - return module.Version{}, "", fmt.Errorf("import path should not have @version") + return invalidf("import path %q should not have @version", path) } if build.IsLocalImport(path) { - return module.Version{}, "", fmt.Errorf("relative import not supported") + return invalidf("%q is relative, but relative import paths are not supported in module mode", path) + } + if filepath.IsAbs(path) { + return invalidf("%q is not a package path; see 'go help packages'", path) + } + if search.IsMetaPackage(path) { + return invalidf("%q is not an importable package; see 'go help packages'", path) } + if path == "C" { // There's no directory for import "C". - return module.Version{}, "", nil + return module.Version{}, "", "", nil, nil } // Before any further lookup, check that the path is valid. if err := module.CheckImportPath(path); err != nil { - return module.Version{}, "", &invalidImportError{importPath: path, err: err} + return module.Version{}, "", "", nil, &invalidImportError{importPath: path, err: err} } // Is the package in the standard library? pathIsStd := search.IsStandardImportPath(path) - if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { - if targetInGorootSrc { - if dir, ok, err := dirInModule(path, targetPrefix, ModRoot(), true); err != nil { - return module.Version{}, dir, err - } else if ok { - return Target, dir, nil + if pathIsStd && modindex.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) { + for _, mainModule := range MainModules.Versions() { + if MainModules.InGorootSrc(mainModule) { + if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil { + return module.Version{}, MainModules.ModRoot(mainModule), dir, nil, err + } else if ok { + return mainModule, MainModules.ModRoot(mainModule), dir, nil, nil + } } } - dir := filepath.Join(cfg.GOROOT, "src", path) - return module.Version{}, dir, nil + dir := filepath.Join(cfg.GOROOTsrc, path) + modroot = cfg.GOROOTsrc + if str.HasPathPrefix(path, "cmd") { + modroot = filepath.Join(cfg.GOROOTsrc, "cmd") + } + return module.Version{}, modroot, dir, nil, nil } // -mod=vendor is special. // Everything must be in the main module or the main module's vendor directory. if cfg.BuildMod == "vendor" { - mainDir, mainOK, mainErr := dirInModule(path, targetPrefix, ModRoot(), true) - vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false) + mainModule := MainModules.mustGetSingleMainModule() + modRoot := MainModules.ModRoot(mainModule) + mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true) + vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false) if mainOK && vendorOK { - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} + return module.Version{}, modRoot, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} } // Prefer to return main directory if there is one, // Note that we're not checking that the package exists. // We'll leave that for load. if !vendorOK && mainDir != "" { - return Target, mainDir, nil + return mainModule, modRoot, mainDir, nil, nil } if mainErr != nil { - return module.Version{}, "", mainErr + return module.Version{}, "", "", nil, mainErr } - readVendorList() - return vendorPkgModule[path], vendorDir, nil + readVendorList(mainModule) + return vendorPkgModule[path], modRoot, vendorDir, nil, nil } // Check each module on the build list. - var dirs []string + var dirs, roots []string var mods []module.Version // Iterate over possible modules for the path, not all selected modules. @@ -307,7 +339,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // already non-nil, then we attempt to load the package using the full // requirements in mg. for { - var sumErrMods []module.Version + var sumErrMods, altMods []module.Version for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) { var ( v string @@ -341,13 +373,16 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // continue the loop and find the package in some other module, // we need to look at this module to make sure the import is // not ambiguous. - return module.Version{}, "", err + return module.Version{}, "", "", nil, err } if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { - return module.Version{}, "", err + return module.Version{}, "", "", nil, err } else if ok { mods = append(mods, m) + roots = append(roots, root) dirs = append(dirs, dir) + } else { + altMods = append(altMods, m) } } @@ -358,9 +393,10 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M for i := 0; i < len(mods)/2; i++ { j := len(mods) - 1 - i mods[i], mods[j] = mods[j], mods[i] + roots[i], roots[j] = roots[j], roots[i] dirs[i], dirs[j] = dirs[j], dirs[i] } - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} + return module.Version{}, "", "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} } if len(sumErrMods) > 0 { @@ -368,7 +404,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M j := len(sumErrMods) - 1 - i sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i] } - return module.Version{}, "", &ImportMissingSumError{ + return module.Version{}, "", "", nil, &ImportMissingSumError{ importPath: path, mods: sumErrMods, found: len(mods) > 0, @@ -376,7 +412,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M } if len(mods) == 1 { - return mods[0], dirs[0], nil + return mods[0], roots[0], dirs[0], altMods, nil } if mg != nil { @@ -386,7 +422,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M if !HasModRoot() { queryErr = ErrNoModRoot } - return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} + return module.Version{}, "", "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} } // So far we've checked the root dependencies. @@ -397,7 +433,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // the module graph, so we can't return an ImportMissingError here — one // of the missing modules might actually contain the package in question, // in which case we shouldn't go looking for it in some new dependency. - return module.Version{}, "", err + return module.Version{}, "", "", nil, err } } } @@ -410,9 +446,9 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M func queryImport(ctx context.Context, path string, rs *Requirements) (module.Version, error) { // To avoid spurious remote fetches, try the latest replacement for each // module (golang.org/issue/26241). - if index != nil { - var mods []module.Version - for mp, mv := range index.highestReplaced { + var mods []module.Version + if MainModules != nil { // TODO(#48912): Ensure MainModules exists at this point, and remove the check. + for mp, mv := range MainModules.HighestReplaced() { if !maybeInModule(path, mp) { continue } @@ -439,40 +475,41 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver } mods = append(mods, module.Version{Path: mp, Version: mv}) } + } - // Every module path in mods is a prefix of the import path. - // As in QueryPattern, prefer the longest prefix that satisfies the import. - sort.Slice(mods, func(i, j int) bool { - return len(mods[i].Path) > len(mods[j].Path) - }) - for _, m := range mods { - needSum := true - root, isLocal, err := fetch(ctx, m, needSum) - if err != nil { - if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { - return module.Version{}, &ImportMissingSumError{importPath: path} - } - return module.Version{}, err - } - if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { - return m, err - } else if ok { - if cfg.BuildMod == "readonly" { - return module.Version{}, &ImportMissingError{Path: path, replaced: m} - } - return m, nil + // Every module path in mods is a prefix of the import path. + // As in QueryPattern, prefer the longest prefix that satisfies the import. + sort.Slice(mods, func(i, j int) bool { + return len(mods[i].Path) > len(mods[j].Path) + }) + for _, m := range mods { + needSum := true + root, isLocal, err := fetch(ctx, m, needSum) + if err != nil { + if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) { + return module.Version{}, &ImportMissingSumError{importPath: path} } + return module.Version{}, err } - if len(mods) > 0 && module.CheckPath(path) != nil { - // The package path is not valid to fetch remotely, - // so it can only exist in a replaced module, - // and we know from the above loop that it is not. - return module.Version{}, &PackageNotInModuleError{ - Mod: mods[0], - Query: "latest", - Pattern: path, - Replacement: Replacement(mods[0]), + if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { + return m, err + } else if ok { + if cfg.BuildMod == "readonly" { + return module.Version{}, &ImportMissingError{Path: path, replaced: m} } + return m, nil + } + } + if len(mods) > 0 && module.CheckPath(path) != nil { + // The package path is not valid to fetch remotely, + // so it can only exist in a replaced module, + // and we know from the above loop that it is not. + replacement := Replacement(mods[0]) + return module.Version{}, &PackageNotInModuleError{ + Mod: mods[0], + Query: "latest", + Pattern: path, + Replacement: replacement, } } @@ -596,7 +633,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // (the main module, and any directory trees pointed at by replace directives). if isLocal { for d := dir; d != mdir && len(d) > len(mdir); { - haveGoMod := haveGoModCache.Do(d, func() interface{} { + haveGoMod := haveGoModCache.Do(d, func() any { fi, err := fsys.Stat(filepath.Join(d, "go.mod")) return err == nil && !fi.IsDir() }).(bool) @@ -619,7 +656,16 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // Are there Go source files in the directory? // We don't care about build tags, not even "+build ignore". // We're just looking for a plausible directory. - res := haveGoFilesCache.Do(dir, func() interface{} { + res := haveGoFilesCache.Do(dir, func() any { + // modindex.GetPackage will return ErrNotIndexed for any directories which + // are reached through a symlink, so that they will be handled by + // fsys.IsDirWithGoFiles below. + if ip, err := modindex.GetPackage(mdir, dir); err == nil { + isDirWithGoFiles, err := ip.IsDirWithGoFiles() + return goFilesEntry{isDirWithGoFiles, err} + } else if !errors.Is(err, modindex.ErrNotIndexed) { + return goFilesEntry{err: err} + } ok, err := fsys.IsDirWithGoFiles(dir) return goFilesEntry{haveGoFiles: ok, err: err} }).(goFilesEntry) @@ -638,14 +684,14 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile // The isLocal return value reports whether the replacement, // if any, is local to the filesystem. func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, isLocal bool, err error) { - if mod == Target { - return ModRoot(), true, nil + if modRoot := MainModules.ModRoot(mod); modRoot != "" { + return modRoot, true, nil } if r := Replacement(mod); r.Path != "" { if r.Version == "" { dir = r.Path if !filepath.IsAbs(dir) { - dir = filepath.Join(ModRoot(), dir) + dir = filepath.Join(replaceRelativeTo(), dir) } // Ensure that the replacement directory actually exists: // dirInModule does not report errors for missing modules, @@ -667,7 +713,7 @@ func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, i mod = r } - if HasModRoot() && cfg.BuildMod == "readonly" && needSum && !modfetch.HaveSum(mod) { + if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && needSum && !modfetch.HaveSum(mod) { return "", false, module.VersionError(mod, &sumMissingError{}) } diff --git a/src/cmd/go/internal/modload/import_test.go b/src/cmd/go/internal/modload/import_test.go index 98145887e9dcc5..65a889ec52f11c 100644 --- a/src/cmd/go/internal/modload/import_test.go +++ b/src/cmd/go/internal/modload/import_test.go @@ -69,7 +69,7 @@ func TestQueryImport(t *testing.T) { RootMode = NoRoot ctx := context.Background() - rs := newRequirements(eager, nil, nil) + rs := LoadModFile(ctx) for _, tt := range importTests { t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 45f724d5e3658f..69a3bc8bfe3df3 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -12,11 +12,13 @@ import ( "fmt" "go/build" "internal/lazyregexp" + "io/ioutil" "os" "path" "path/filepath" "strconv" "strings" + "sync" "cmd/go/internal/base" "cmd/go/internal/cfg" @@ -43,28 +45,198 @@ var ( ForceUseModules bool allowMissingModuleImports bool + + // ExplicitWriteGoMod prevents LoadPackages, ListModules, and other functions + // from updating go.mod and go.sum or reporting errors when updates are + // needed. A package should set this if it would cause go.mod to be written + // multiple times (for example, 'go get' calls LoadPackages multiple times) or + // if it needs some other operation to be successful before go.mod and go.sum + // can be written (for example, 'go mod download' must download modules before + // adding sums to go.sum). Packages that set this are responsible for calling + // WriteGoMod explicitly. + ExplicitWriteGoMod bool ) // Variables set in Init. var ( initialized bool - modRoot string - gopath string + + // These are primarily used to initialize the MainModules, and should be + // eventually superceded by them but are still used in cases where the module + // roots are required but MainModules hasn't been initialized yet. Set to + // the modRoots of the main modules. + // modRoots != nil implies len(modRoots) > 0 + modRoots []string + gopath string ) -// Variables set in initTarget (during {Load,Create}ModFile). +// EnterModule resets MainModules and requirements to refer to just this one module. +func EnterModule(ctx context.Context, enterModroot string) { + MainModules = nil // reset MainModules + requirements = nil + workFilePath = "" // Force module mode + modfetch.Reset() + + modRoots = []string{enterModroot} + LoadModFile(ctx) +} + +// Variable set in InitWorkfile var ( - Target module.Version + // Set to the path to the go.work file, or "" if workspace mode is disabled. + workFilePath string +) + +type MainModuleSet struct { + // versions are the module.Version values of each of the main modules. + // For each of them, the Path fields are ordinary module paths and the Version + // fields are empty strings. + versions []module.Version + + // modRoot maps each module in versions to its absolute filesystem path. + modRoot map[module.Version]string - // targetPrefix is the path prefix for packages in Target, without a trailing - // slash. For most modules, targetPrefix is just Target.Path, but the + // pathPrefix is the path prefix for packages in the module, without a trailing + // slash. For most modules, pathPrefix is just version.Path, but the // standard-library module "std" has an empty prefix. - targetPrefix string + pathPrefix map[module.Version]string - // targetInGorootSrc caches whether modRoot is within GOROOT/src. + // inGorootSrc caches whether modRoot is within GOROOT/src. // The "std" module is special within GOROOT/src, but not otherwise. - targetInGorootSrc bool -) + inGorootSrc map[module.Version]bool + + modFiles map[module.Version]*modfile.File + + modContainingCWD module.Version + + workFileGoVersion string + + workFileReplaceMap map[module.Version]module.Version + // highest replaced version of each module path; empty string for wildcard-only replacements + highestReplaced map[string]string + + indexMu sync.Mutex + indices map[module.Version]*modFileIndex +} + +func (mms *MainModuleSet) PathPrefix(m module.Version) string { + return mms.pathPrefix[m] +} + +// Versions returns the module.Version values of each of the main modules. +// For each of them, the Path fields are ordinary module paths and the Version +// fields are empty strings. +// Callers should not modify the returned slice. +func (mms *MainModuleSet) Versions() []module.Version { + if mms == nil { + return nil + } + return mms.versions +} + +func (mms *MainModuleSet) Contains(path string) bool { + if mms == nil { + return false + } + for _, v := range mms.versions { + if v.Path == path { + return true + } + } + return false +} + +func (mms *MainModuleSet) ModRoot(m module.Version) string { + if mms == nil { + return "" + } + return mms.modRoot[m] +} + +func (mms *MainModuleSet) InGorootSrc(m module.Version) bool { + if mms == nil { + return false + } + return mms.inGorootSrc[m] +} + +func (mms *MainModuleSet) mustGetSingleMainModule() module.Version { + if mms == nil || len(mms.versions) == 0 { + panic("internal error: mustGetSingleMainModule called in context with no main modules") + } + if len(mms.versions) != 1 { + if inWorkspaceMode() { + panic("internal error: mustGetSingleMainModule called in workspace mode") + } else { + panic("internal error: multiple main modules present outside of workspace mode") + } + } + return mms.versions[0] +} + +func (mms *MainModuleSet) GetSingleIndexOrNil() *modFileIndex { + if mms == nil { + return nil + } + if len(mms.versions) == 0 { + return nil + } + return mms.indices[mms.mustGetSingleMainModule()] +} + +func (mms *MainModuleSet) Index(m module.Version) *modFileIndex { + mms.indexMu.Lock() + defer mms.indexMu.Unlock() + return mms.indices[m] +} + +func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) { + mms.indexMu.Lock() + defer mms.indexMu.Unlock() + mms.indices[m] = index +} + +func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File { + return mms.modFiles[m] +} + +func (mms *MainModuleSet) Len() int { + if mms == nil { + return 0 + } + return len(mms.versions) +} + +// ModContainingCWD returns the main module containing the working directory, +// or module.Version{} if none of the main modules contain the working +// directory. +func (mms *MainModuleSet) ModContainingCWD() module.Version { + return mms.modContainingCWD +} + +func (mms *MainModuleSet) HighestReplaced() map[string]string { + return mms.highestReplaced +} + +// GoVersion returns the go version set on the single module, in module mode, +// or the go.work file in workspace mode. +func (mms *MainModuleSet) GoVersion() string { + if !inWorkspaceMode() { + return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule())) + } + v := mms.workFileGoVersion + if v == "" { + // Fall back to 1.18 for go.work files. + v = "1.18" + } + return v +} + +func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version { + return mms.workFileReplaceMap +} + +var MainModules *MainModuleSet type Root int @@ -94,6 +266,7 @@ const ( // in go.mod, edit it before loading. func ModFile() *modfile.File { Init() + modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule()) if modFile == nil { die() } @@ -102,9 +275,43 @@ func ModFile() *modfile.File { func BinDir() string { Init() + if cfg.GOBIN != "" { + return cfg.GOBIN + } + if gopath == "" { + return "" + } return filepath.Join(gopath, "bin") } +// InitWorkfile initializes the workFilePath variable for commands that +// operate in workspace mode. It should not be called by other commands, +// for example 'go mod tidy', that don't operate in workspace mode. +func InitWorkfile() { + if RootMode == NoRoot { + workFilePath = "" + return + } + + switch gowork := cfg.Getenv("GOWORK"); gowork { + case "off": + workFilePath = "" + case "", "auto": + workFilePath = findWorkspaceFile(base.Cwd()) + default: + if !filepath.IsAbs(gowork) { + base.Fatalf("the path provided to GOWORK must be an absolute path") + } + workFilePath = gowork + } +} + +// WorkFilePath returns the absolute path of the go.work file, or "" if not in +// workspace mode. WorkFilePath must be called after InitWorkfile. +func WorkFilePath() string { + return workFilePath +} + // Init determines whether module mode is enabled, locates the root of the // current module (if any), sets environment variables for Git subprocesses, and // configures the cfg, codehost, load, modfetch, and search packages for use @@ -169,18 +376,18 @@ func Init() { if os.Getenv("GCM_INTERACTIVE") == "" { os.Setenv("GCM_INTERACTIVE", "never") } - - if modRoot != "" { + if modRoots != nil { // modRoot set before Init was called ("go mod init" does this). // No need to search for go.mod. } else if RootMode == NoRoot { if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") { base.Fatalf("go: -modfile cannot be used with commands that ignore the current module") } - modRoot = "" + modRoots = nil + } else if inWorkspaceMode() { + // We're in workspace mode. } else { - modRoot = findModuleRoot(base.Cwd()) - if modRoot == "" { + if modRoot := findModuleRoot(base.Cwd()); modRoot == "" { if cfg.ModFile != "" { base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.") } @@ -198,11 +405,12 @@ func Init() { // will find it and get modules when they're not expecting them. // It's a bit of a peculiar thing to disallow but quite mysterious // when it happens. See golang.org/issue/26708. - modRoot = "" fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir()) if !mustUseModules { return } + } else { + modRoots = []string{modRoot} } } if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") { @@ -213,35 +421,11 @@ func Init() { cfg.ModulesEnabled = true setDefaultBuildMod() list := filepath.SplitList(cfg.BuildContext.GOPATH) - if len(list) == 0 || list[0] == "" { - base.Fatalf("missing $GOPATH") - } - gopath = list[0] - if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil { - base.Fatalf("$GOPATH/go.mod exists but should not") - } - - if modRoot == "" { - // We're in module mode, but not inside a module. - // - // Commands like 'go build', 'go run', 'go list' have no go.mod file to - // read or write. They would need to find and download the latest versions - // of a potentially large number of modules with no way to save version - // information. We can succeed slowly (but not reproducibly), but that's - // not usually a good experience. - // - // Instead, we forbid resolving import paths to modules other than std and - // cmd. Users may still build packages specified with .go files on the - // command line, but they'll see an error if those files import anything - // outside std. - // - // This can be overridden by calling AllowMissingModuleImports. - // For example, 'go get' does this, since it is expected to resolve paths. - // - // See golang.org/issue/32027. - } else { - modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum" - search.SetModRoot(modRoot) + if len(list) > 0 && list[0] != "" { + gopath = list[0] + if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil { + base.Fatalf("$GOPATH/go.mod exists but should not") + } } } @@ -255,7 +439,7 @@ func Init() { // be called until the command is installed and flags are parsed. Instead of // calling Init and Enabled, the main package can call this function. func WillBeEnabled() bool { - if modRoot != "" || cfg.ModulesEnabled { + if modRoots != nil || cfg.ModulesEnabled { // Already enabled. return true } @@ -297,16 +481,18 @@ func WillBeEnabled() bool { // (usually through MustModRoot). func Enabled() bool { Init() - return modRoot != "" || cfg.ModulesEnabled + return modRoots != nil || cfg.ModulesEnabled } -// ModRoot returns the root of the main module. -// It calls base.Fatalf if there is no main module. -func ModRoot() string { - if !HasModRoot() { - die() +func VendorDir() string { + return filepath.Join(MainModules.ModRoot(MainModules.mustGetSingleMainModule()), "vendor") +} + +func inWorkspaceMode() bool { + if !initialized { + panic("inWorkspaceMode called before modload.Init called") } - return modRoot + return workFilePath != "" } // HasModRoot reports whether a main module is present. @@ -314,17 +500,27 @@ func ModRoot() string { // does not require a main module. func HasModRoot() bool { Init() - return modRoot != "" + return modRoots != nil } -// ModFilePath returns the effective path of the go.mod file. Normally, this -// "go.mod" in the directory returned by ModRoot, but the -modfile flag may -// change its location. ModFilePath calls base.Fatalf if there is no main -// module, even if -modfile is set. -func ModFilePath() string { +// MustHaveModRoot checks that a main module or main modules are present, +// and calls base.Fatalf if there are no main modules. +func MustHaveModRoot() { + Init() if !HasModRoot() { die() } +} + +// ModFilePath returns the path that would be used for the go.mod +// file, if in module mode. ModFilePath calls base.Fatalf if there is no main +// module, even if -modfile is set. +func ModFilePath() string { + MustHaveModRoot() + return modFilePath(findModuleRoot(base.Cwd())) +} + +func modFilePath(modRoot string) string { if cfg.ModFile != "" { return cfg.ModFile } @@ -335,6 +531,9 @@ func die() { if cfg.Getenv("GO111MODULE") == "off" { base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'") } + if inWorkspaceMode() { + base.Fatalf("go: no modules were found in the current workspace; see 'go help work'") + } if dir, name := findAltConfig(base.Cwd()); dir != "" { rel, err := filepath.Rel(base.Cwd(), dir) if err != nil { @@ -365,12 +564,84 @@ func (goModDirtyError) Error() string { var errGoModDirty error = goModDirtyError{} +func loadWorkFile(path string) (goVersion string, modRoots []string, replaces []*modfile.Replace, err error) { + workDir := filepath.Dir(path) + wf, err := ReadWorkFile(path) + if err != nil { + return "", nil, nil, err + } + if wf.Go != nil { + goVersion = wf.Go.Version + } + seen := map[string]bool{} + for _, d := range wf.Use { + modRoot := d.Path + if !filepath.IsAbs(modRoot) { + modRoot = filepath.Join(workDir, modRoot) + } + + if seen[modRoot] { + return "", nil, nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot) + } + seen[modRoot] = true + modRoots = append(modRoots, modRoot) + } + + return goVersion, modRoots, wf.Replace, nil +} + +// ReadWorkFile reads and parses the go.work file at the given path. +func ReadWorkFile(path string) (*modfile.WorkFile, error) { + workData, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + return modfile.ParseWork(path, workData, nil) +} + +// WriteWorkFile cleans and writes out the go.work file to the given path. +func WriteWorkFile(path string, wf *modfile.WorkFile) error { + wf.SortBlocks() + wf.Cleanup() + out := modfile.Format(wf.Syntax) + + return ioutil.WriteFile(path, out, 0666) +} + +// UpdateWorkFile updates comments on directory directives in the go.work +// file to include the associated module path. +func UpdateWorkFile(wf *modfile.WorkFile) { + missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot + + for _, d := range wf.Use { + if d.Path == "" { + continue // d is marked for deletion. + } + modRoot := d.Path + if d.ModulePath == "" { + missingModulePaths[d.Path] = modRoot + } + } + + // Clean up and annotate directories. + // TODO(matloob): update x/mod to actually add module paths. + for moddir, absmodroot := range missingModulePaths { + _, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil) + if err != nil { + continue // Error will be reported if modules are loaded. + } + wf.AddUse(moddir, f.Module.Mod.Path) + } +} + // LoadModFile sets Target and, if there is a main module, parses the initial // build list from its go.mod file. // // LoadModFile may make changes in memory, like adding a go directive and -// ensuring requirements are consistent, and will write those changes back to -// disk unless DisallowWriteGoMod is in effect. +// ensuring requirements are consistent. The caller is responsible for ensuring +// those changes are written to disk by calling LoadPackages or ListModules +// (unless ExplicitWriteGoMod is set) or by calling WriteGoMod directly. // // As a side-effect, LoadModFile may change cfg.BuildMod to "vendor" if // -mod wasn't set explicitly and automatic vendoring should be enabled. @@ -383,111 +654,142 @@ var errGoModDirty error = goModDirtyError{} // it for global consistency. Most callers outside of the modload package should // use LoadModGraph instead. func LoadModFile(ctx context.Context) *Requirements { - rs, needCommit := loadModFile(ctx) - if needCommit { - commitRequirements(ctx, modFileGoVersion(), rs) - } - return rs -} - -// loadModFile is like LoadModFile, but does not implicitly commit the -// requirements back to disk after fixing inconsistencies. -// -// If needCommit is true, after the caller makes any other needed changes to the -// returned requirements they should invoke commitRequirements to fix any -// inconsistencies that may be present in the on-disk go.mod file. -func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) { if requirements != nil { - return requirements, false + return requirements } Init() - if modRoot == "" { - Target = module.Version{Path: "command-line-arguments"} - targetPrefix = "command-line-arguments" - goVersion := LatestGoVersion() - rawGoVersion.Store(Target, goVersion) - requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil) - return requirements, false - } - - gomod := ModFilePath() - var data []byte - var err error - if gomodActual, ok := fsys.OverlayPath(gomod); ok { - // Don't lock go.mod if it's part of the overlay. - // On Plan 9, locking requires chmod, and we don't want to modify any file - // in the overlay. See #44700. - data, err = os.ReadFile(gomodActual) + var ( + workFileGoVersion string + workFileReplaces []*modfile.Replace + ) + if inWorkspaceMode() { + var err error + workFileGoVersion, modRoots, workFileReplaces, err = loadWorkFile(workFilePath) + if err != nil { + base.Fatalf("reading go.work: %v", err) + } + for _, modRoot := range modRoots { + sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum" + modfetch.WorkspaceGoSumFiles = append(modfetch.WorkspaceGoSumFiles, sumFile) + } + modfetch.GoSumFile = workFilePath + ".sum" + } else if modRoots == nil { + // We're in module mode, but not inside a module. + // + // Commands like 'go build', 'go run', 'go list' have no go.mod file to + // read or write. They would need to find and download the latest versions + // of a potentially large number of modules with no way to save version + // information. We can succeed slowly (but not reproducibly), but that's + // not usually a good experience. + // + // Instead, we forbid resolving import paths to modules other than std and + // cmd. Users may still build packages specified with .go files on the + // command line, but they'll see an error if those files import anything + // outside std. + // + // This can be overridden by calling AllowMissingModuleImports. + // For example, 'go get' does this, since it is expected to resolve paths. + // + // See golang.org/issue/32027. } else { - data, err = lockedfile.Read(gomodActual) - } - if err != nil { - base.Fatalf("go: %v", err) + modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum" + } + if len(modRoots) == 0 { + // TODO(#49228): Instead of creating a fake module with an empty modroot, + // make MainModules.Len() == 0 mean that we're in module mode but not inside + // any module. + mainModule := module.Version{Path: "command-line-arguments"} + MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "", nil) + goVersion := LatestGoVersion() + rawGoVersion.Store(mainModule, goVersion) + pruning := pruningForGoVersion(goVersion) + if inWorkspaceMode() { + pruning = workspace + } + requirements = newRequirements(pruning, nil, nil) + return requirements } - var fixed bool - f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed)) - if err != nil { - // Errors returned by modfile.Parse begin with file:line. - base.Fatalf("go: errors parsing go.mod:\n%s\n", err) - } - if f.Module == nil { - // No module declaration. Must add module path. - base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod") - } + var modFiles []*modfile.File + var mainModules []module.Version + var indices []*modFileIndex + for _, modroot := range modRoots { + gomod := modFilePath(modroot) + var fixed bool + data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed)) + if err != nil { + base.Fatalf("go: %v", err) + } - modFile = f - initTarget(f.Module.Mod) - index = indexModFile(data, f, fixed) + modFiles = append(modFiles, f) + mainModule := f.Module.Mod + mainModules = append(mainModules, mainModule) + indices = append(indices, indexModFile(data, f, mainModule, fixed)) - if err := module.CheckImportPath(f.Module.Mod.Path); err != nil { - if pathErr, ok := err.(*module.InvalidPathError); ok { - pathErr.Kind = "module" + if err := module.CheckImportPath(f.Module.Mod.Path); err != nil { + if pathErr, ok := err.(*module.InvalidPathError); ok { + pathErr.Kind = "module" + } + base.Fatalf("go: %v", err) } - base.Fatalf("go: %v", err) } + MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion, workFileReplaces) setDefaultBuildMod() // possibly enable automatic vendoring - rs = requirementsFromModFile() + rs := requirementsFromModFiles(ctx, modFiles) + + if inWorkspaceMode() { + // We don't need to do anything for vendor or update the mod file so + // return early. + requirements = rs + return rs + } + + mainModule := MainModules.mustGetSingleMainModule() + if cfg.BuildMod == "vendor" { - readVendorList() - checkVendorConsistency() + readVendorList(mainModule) + index := MainModules.Index(mainModule) + modFile := MainModules.ModFile(mainModule) + checkVendorConsistency(index, modFile) rs.initVendor(vendorList) } + if rs.hasRedundantRoot() { // If any module path appears more than once in the roots, we know that the // go.mod file needs to be updated even though we have not yet loaded any // transitive dependencies. + var err error rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) if err != nil { base.Fatalf("go: %v", err) } } - if index.goVersionV == "" { + if MainModules.Index(mainModule).goVersionV == "" && rs.pruning != workspace { // TODO(#45551): Do something more principled instead of checking // cfg.CmdName directly here. if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" { - addGoStmt(LatestGoVersion()) - if go117EnableLazyLoading { - // We need to add a 'go' version to the go.mod file, but we must assume - // that its existing contents match something between Go 1.11 and 1.16. - // Go 1.11 through 1.16 have eager requirements, but the latest Go - // version uses lazy requirements instead — so we need to cnvert the - // requirements to be lazy. - rs, err = convertDepth(ctx, rs, lazy) - if err != nil { - base.Fatalf("go: %v", err) - } + addGoStmt(MainModules.ModFile(mainModule), mainModule, LatestGoVersion()) + + // We need to add a 'go' version to the go.mod file, but we must assume + // that its existing contents match something between Go 1.11 and 1.16. + // Go 1.11 through 1.16 do not support graph pruning, but the latest Go + // version uses a pruned module graph — so we need to convert the + // requirements to support pruning. + var err error + rs, err = convertPruning(ctx, rs, pruned) + if err != nil { + base.Fatalf("go: %v", err) } } else { - rawGoVersion.Store(Target, modFileGoVersion()) + rawGoVersion.Store(mainModule, modFileGoVersion(MainModules.ModFile(mainModule))) } } requirements = rs - return requirements, true + return requirements } // CreateModFile initializes a new module by creating a go.mod file. @@ -500,9 +802,10 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) { // exactly the same as in the legacy configuration (for example, we can't get // packages at multiple versions from the same module). func CreateModFile(ctx context.Context, modPath string) { - modRoot = base.Cwd() + modRoot := base.Cwd() + modRoots = []string{modRoot} Init() - modFilePath := ModFilePath() + modFilePath := modFilePath(modRoot) if _, err := fsys.Stat(modFilePath); err == nil { base.Fatalf("go: %s already exists", modFilePath) } @@ -523,15 +826,22 @@ func CreateModFile(ctx context.Context, modPath string) { } } base.Fatalf("go: %v", err) + } else if _, _, ok := module.SplitPathVersion(modPath); !ok { + if strings.HasPrefix(modPath, "gopkg.in/") { + invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath)) + base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg) + } + invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath)) + base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg) } fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath) - modFile = new(modfile.File) + modFile := new(modfile.File) modFile.AddModuleStmt(modPath) - initTarget(modFile.Module.Mod) - addGoStmt(LatestGoVersion()) // Add the go directive before converted module requirements. + MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "", nil) + addGoStmt(modFile, modFile.Module.Mod, LatestGoVersion()) // Add the go directive before converted module requirements. - convertedFrom, err := convertLegacyConfig(modPath) + convertedFrom, err := convertLegacyConfig(modFile, modRoot) if convertedFrom != "" { fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom)) } @@ -539,12 +849,15 @@ func CreateModFile(ctx context.Context, modPath string) { base.Fatalf("go: %v", err) } - rs := requirementsFromModFile() + rs := requirementsFromModFiles(ctx, []*modfile.File{modFile}) rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) if err != nil { base.Fatalf("go: %v", err) } - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + if err := commitRequirements(ctx); err != nil { + base.Fatalf("go: %v", err) + } // Suggest running 'go mod tidy' unless the project is empty. Even if we // imported all the correct requirements above, we're probably missing @@ -570,6 +883,32 @@ func CreateModFile(ctx context.Context, modPath string) { } } +// CreateWorkFile initializes a new workspace by creating a go.work file. +func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) { + if _, err := fsys.Stat(workFile); err == nil { + base.Fatalf("go: %s already exists", workFile) + } + + goV := LatestGoVersion() // Use current Go version by default + workF := new(modfile.WorkFile) + workF.Syntax = new(modfile.FileSyntax) + workF.AddGoStmt(goV) + + for _, dir := range modDirs { + _, f, err := ReadModFile(filepath.Join(dir, "go.mod"), nil) + if err != nil { + if os.IsNotExist(err) { + base.Fatalf("go: creating workspace file: no go.mod file exists in directory %v", dir) + } + base.Fatalf("go: error parsing go.mod in directory %s: %v", dir, err) + } + workF.AddUse(ToDirectoryPath(dir), f.Module.Mod.Path) + } + + UpdateWorkFile(workF) + WriteWorkFile(workFile, workF) +} + // fixVersion returns a modfile.VersionFixer implemented using the Query function. // // It resolves commit hashes and branch names to versions, @@ -632,49 +971,142 @@ func AllowMissingModuleImports() { allowMissingModuleImports = true } -// initTarget sets Target and associated variables according to modFile, -func initTarget(m module.Version) { - Target = m - targetPrefix = m.Path - - if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" { - targetInGorootSrc = true - if m.Path == "std" { - // The "std" module in GOROOT/src is the Go standard library. Unlike other - // modules, the packages in the "std" module have no import-path prefix. - // - // Modules named "std" outside of GOROOT/src do not receive this special - // treatment, so it is possible to run 'go test .' in other GOROOTs to - // test individual packages using a combination of the modified package - // and the ordinary standard library. - // (See https://golang.org/issue/30756.) - targetPrefix = "" +// makeMainModules creates a MainModuleSet and associated variables according to +// the given main modules. +func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string, workFileReplaces []*modfile.Replace) *MainModuleSet { + for _, m := range ms { + if m.Version != "" { + panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m)) + } + } + modRootContainingCWD := findModuleRoot(base.Cwd()) + mainModules := &MainModuleSet{ + versions: ms[:len(ms):len(ms)], + inGorootSrc: map[module.Version]bool{}, + pathPrefix: map[module.Version]string{}, + modRoot: map[module.Version]string{}, + modFiles: map[module.Version]*modfile.File{}, + indices: map[module.Version]*modFileIndex{}, + workFileGoVersion: workFileGoVersion, + workFileReplaceMap: toReplaceMap(workFileReplaces), + highestReplaced: map[string]string{}, + } + mainModulePaths := make(map[string]bool) + for _, m := range ms { + if mainModulePaths[m.Path] { + base.Errorf("go: module %s appears multiple times in workspace", m.Path) + } + mainModulePaths[m.Path] = true + } + replacedByWorkFile := make(map[string]bool) + replacements := make(map[module.Version]module.Version) + for _, r := range workFileReplaces { + if mainModulePaths[r.Old.Path] && r.Old.Version == "" { + base.Errorf("go: workspace module %v is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.", r.Old.Path) + } + replacedByWorkFile[r.Old.Path] = true + v, ok := mainModules.highestReplaced[r.Old.Path] + if !ok || semver.Compare(r.Old.Version, v) > 0 { + mainModules.highestReplaced[r.Old.Path] = r.Old.Version + } + replacements[r.Old] = r.New + } + for i, m := range ms { + mainModules.pathPrefix[m] = m.Path + mainModules.modRoot[m] = rootDirs[i] + mainModules.modFiles[m] = modFiles[i] + mainModules.indices[m] = indices[i] + + if mainModules.modRoot[m] == modRootContainingCWD { + mainModules.modContainingCWD = m + } + + if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" { + mainModules.inGorootSrc[m] = true + if m.Path == "std" { + // The "std" module in GOROOT/src is the Go standard library. Unlike other + // modules, the packages in the "std" module have no import-path prefix. + // + // Modules named "std" outside of GOROOT/src do not receive this special + // treatment, so it is possible to run 'go test .' in other GOROOTs to + // test individual packages using a combination of the modified package + // and the ordinary standard library. + // (See https://golang.org/issue/30756.) + mainModules.pathPrefix[m] = "" + } + } + + if modFiles[i] != nil { + curModuleReplaces := make(map[module.Version]bool) + for _, r := range modFiles[i].Replace { + if replacedByWorkFile[r.Old.Path] { + continue + } + var newV module.Version = r.New + if WorkFilePath() != "" && newV.Version == "" && !filepath.IsAbs(newV.Path) { + // Since we are in a workspace, we may be loading replacements from + // multiple go.mod files. Relative paths in those replacement are + // relative to the go.mod file, not the workspace, so the same string + // may refer to two different paths and different strings may refer to + // the same path. Convert them all to be absolute instead. + // + // (We could do this outside of a workspace too, but it would mean that + // replacement paths in error strings needlessly differ from what's in + // the go.mod file.) + newV.Path = filepath.Join(rootDirs[i], newV.Path) + } + if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != newV { + base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, newV, r.Old) + } + curModuleReplaces[r.Old] = true + replacements[r.Old] = newV + + v, ok := mainModules.highestReplaced[r.Old.Path] + if !ok || semver.Compare(r.Old.Version, v) > 0 { + mainModules.highestReplaced[r.Old.Path] = r.Old.Version + } + } } } + return mainModules } -// requirementsFromModFile returns the set of non-excluded requirements from +// requirementsFromModFiles returns the set of non-excluded requirements from // the global modFile. -func requirementsFromModFile() *Requirements { - roots := make([]module.Version, 0, len(modFile.Require)) +func requirementsFromModFiles(ctx context.Context, modFiles []*modfile.File) *Requirements { + var roots []module.Version direct := map[string]bool{} - for _, r := range modFile.Require { - if index != nil && index.exclude[r.Mod] { - if cfg.BuildMod == "mod" { - fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) - } else { - fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) - } - continue + var pruning modPruning + if inWorkspaceMode() { + pruning = workspace + roots = make([]module.Version, len(MainModules.Versions())) + copy(roots, MainModules.Versions()) + } else { + pruning = pruningForGoVersion(MainModules.GoVersion()) + if len(modFiles) != 1 { + panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles))) } + modFile := modFiles[0] + roots = make([]module.Version, 0, len(modFile.Require)) + mm := MainModules.mustGetSingleMainModule() + for _, r := range modFile.Require { + if index := MainModules.Index(mm); index != nil && index.exclude[r.Mod] { + if cfg.BuildMod == "mod" { + fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) + } else { + fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version) + } + continue + } - roots = append(roots, r.Mod) - if !r.Indirect { - direct[r.Mod.Path] = true + roots = append(roots, r.Mod) + if !r.Indirect { + direct[r.Mod.Path] = true + } } } module.Sort(roots) - rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct) + rs := newRequirements(pruning, roots, direct) return rs } @@ -682,18 +1114,35 @@ func requirementsFromModFile() *Requirements { // wasn't provided. setDefaultBuildMod may be called multiple times. func setDefaultBuildMod() { if cfg.BuildModExplicit { + if inWorkspaceMode() && cfg.BuildMod != "readonly" { + base.Fatalf("go: -mod may only be set to readonly when in workspace mode, but it is set to %q"+ + "\n\tRemove the -mod flag to use the default readonly value,"+ + "\n\tor set GOWORK=off to disable workspace mode.", cfg.BuildMod) + } // Don't override an explicit '-mod=' argument. return } - if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") { - // 'get' and 'go mod' commands may update go.mod automatically. - // TODO(jayconrod): should this narrower? Should 'go mod download' or - // 'go mod graph' update go.mod by default? + // TODO(#40775): commands should pass in the module mode as an option + // to modload functions instead of relying on an implicit setting + // based on command name. + switch cfg.CmdName { + case "get", "mod download", "mod init", "mod tidy", "work sync": + // These commands are intended to update go.mod and go.sum. + cfg.BuildMod = "mod" + return + case "mod graph", "mod verify", "mod why": + // These commands should not update go.mod or go.sum, but they should be + // able to fetch modules not in go.sum and should not report errors if + // go.mod is inconsistent. They're useful for debugging, and they need + // to work in buggy situations. cfg.BuildMod = "mod" return + case "mod vendor": + cfg.BuildMod = "readonly" + return } - if modRoot == "" { + if modRoots == nil { if allowMissingModuleImports { cfg.BuildMod = "mod" } else { @@ -702,31 +1151,38 @@ func setDefaultBuildMod() { return } - if fi, err := fsys.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() { - modGo := "unspecified" - if index != nil && index.goVersionV != "" { - if semver.Compare(index.goVersionV, "v1.14") >= 0 { - // The Go version is at least 1.14, and a vendor directory exists. - // Set -mod=vendor by default. - cfg.BuildMod = "vendor" - cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists." - return - } else { - modGo = index.goVersionV[1:] + if len(modRoots) == 1 { + index := MainModules.GetSingleIndexOrNil() + if fi, err := fsys.Stat(filepath.Join(modRoots[0], "vendor")); err == nil && fi.IsDir() { + modGo := "unspecified" + if index != nil && index.goVersionV != "" { + if semver.Compare(index.goVersionV, "v1.14") >= 0 { + // The Go version is at least 1.14, and a vendor directory exists. + // Set -mod=vendor by default. + cfg.BuildMod = "vendor" + cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists." + return + } else { + modGo = index.goVersionV[1:] + } } - } - // Since a vendor directory exists, we should record why we didn't use it. - // This message won't normally be shown, but it may appear with import errors. - cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo) + // Since a vendor directory exists, we should record why we didn't use it. + // This message won't normally be shown, but it may appear with import errors. + cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo) + } } cfg.BuildMod = "readonly" } +func mustHaveCompleteRequirements() bool { + return cfg.BuildMod != "mod" && !inWorkspaceMode() +} + // convertLegacyConfig imports module requirements from a legacy vendoring // configuration file, if one is present. -func convertLegacyConfig(modPath string) (from string, err error) { +func convertLegacyConfig(modFile *modfile.File, modRoot string) (from string, err error) { noneSelected := func(path string) (version string) { return "none" } queryPackage := func(path, rev string) (module.Version, error) { pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil) @@ -757,14 +1213,14 @@ func convertLegacyConfig(modPath string) (from string, err error) { // addGoStmt adds a go directive to the go.mod file if it does not already // include one. The 'go' version added, if any, is the latest version supported // by this toolchain. -func addGoStmt(v string) { +func addGoStmt(modFile *modfile.File, mod module.Version, v string) { if modFile.Go != nil && modFile.Go.Version != "" { return } if err := modFile.AddGoStmt(v); err != nil { base.Fatalf("go: internal error: %v", err) } - rawGoVersion.Store(Target, v) + rawGoVersion.Store(mod, v) } // LatestGoVersion returns the latest version of the Go language supported by @@ -815,7 +1271,7 @@ var altConfigs = []string{ ".git/config", } -func findModuleRoot(dir string) (root string) { +func findModuleRoot(dir string) (roots string) { if dir == "" { panic("dir not set") } @@ -835,6 +1291,33 @@ func findModuleRoot(dir string) (root string) { return "" } +func findWorkspaceFile(dir string) (root string) { + if dir == "" { + panic("dir not set") + } + dir = filepath.Clean(dir) + + // Look for enclosing go.mod. + for { + f := filepath.Join(dir, "go.work") + if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() { + return f + } + d := filepath.Dir(dir) + if d == dir { + break + } + if d == cfg.GOROOT { + // As a special case, don't cross GOROOT to find a go.work file. + // The standard library and commands built in go always use the vendored + // dependencies, so avoid using a most likely irrelevant go.work file. + return "" + } + dir = d + } + return "" +} + func findAltConfig(dir string) (root, name string) { if dir == "" { panic("dir not set") @@ -960,66 +1443,62 @@ func findImportComment(file string) string { return path } -var allowWriteGoMod = true - -// DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all. -func DisallowWriteGoMod() { - allowWriteGoMod = false -} - -// AllowWriteGoMod undoes the effect of DisallowWriteGoMod: -// future calls to WriteGoMod will update go.mod if needed. -// Note that any past calls have been discarded, so typically -// a call to AlowWriteGoMod should be followed by a call to WriteGoMod. -func AllowWriteGoMod() { - allowWriteGoMod = true -} - // WriteGoMod writes the current build list back to go.mod. -func WriteGoMod(ctx context.Context) { - if !allowWriteGoMod { - panic("WriteGoMod called while disallowed") - } - commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx)) +func WriteGoMod(ctx context.Context) error { + requirements = LoadModFile(ctx) + return commitRequirements(ctx) } -// commitRequirements writes sets the global requirements variable to rs and -// writes its contents back to the go.mod file on disk. -func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) { - requirements = rs - - if !allowWriteGoMod { - // Some package outside of modload promised to update the go.mod file later. - return - } - - if modRoot == "" { +// commitRequirements ensures go.mod and go.sum are up to date with the current +// requirements. +// +// In "mod" mode, commitRequirements writes changes to go.mod and go.sum. +// +// In "readonly" and "vendor" modes, commitRequirements returns an error if +// go.mod or go.sum are out of date in a semantically significant way. +// +// In workspace mode, commitRequirements only writes changes to go.work.sum. +func commitRequirements(ctx context.Context) (err error) { + if inWorkspaceMode() { + // go.mod files aren't updated in workspace mode, but we still want to + // update the go.work.sum file. + return modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()) + } + if MainModules.Len() != 1 || MainModules.ModRoot(MainModules.Versions()[0]) == "" { // We aren't in a module, so we don't have anywhere to write a go.mod file. - return + return nil } + mainModule := MainModules.mustGetSingleMainModule() + modFile := MainModules.ModFile(mainModule) + if modFile == nil { + // command-line-arguments has no .mod file to write. + return nil + } + modFilePath := modFilePath(MainModules.ModRoot(mainModule)) var list []*modfile.Require - for _, m := range rs.rootModules { + for _, m := range requirements.rootModules { list = append(list, &modfile.Require{ Mod: m, - Indirect: !rs.direct[m.Path], + Indirect: !requirements.direct[m.Path], }) } - if goVersion != "" { - modFile.AddGoStmt(goVersion) + if modFile.Go == nil || modFile.Go.Version == "" { + modFile.AddGoStmt(modFileGoVersion(modFile)) } - if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 { + if semver.Compare("v"+modFileGoVersion(modFile), separateIndirectVersionV) < 0 { modFile.SetRequire(list) } else { modFile.SetRequireSeparateIndirect(list) } modFile.Cleanup() + index := MainModules.GetSingleIndexOrNil() dirty := index.modFileIsDirty(modFile) if dirty && cfg.BuildMod != "mod" { // If we're about to fail due to -mod=readonly, // prefer to report a dirty go.mod over a dirty go.sum - base.Fatalf("go: %v", errGoModDirty) + return errGoModDirty } if !dirty && cfg.CmdName != "mod tidy" { @@ -1028,30 +1507,33 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) // Don't write go.mod, but write go.sum in case we added or trimmed sums. // 'go mod init' shouldn't write go.sum, since it will be incomplete. if cfg.CmdName != "mod init" { - modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums)) + if err := modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()); err != nil { + return err + } } - return + return nil } - gomod := ModFilePath() - if _, ok := fsys.OverlayPath(gomod); ok { + if _, ok := fsys.OverlayPath(modFilePath); ok { if dirty { - base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay") + return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay") } - return + return nil } new, err := modFile.Format() if err != nil { - base.Fatalf("go: %v", err) + return err } defer func() { // At this point we have determined to make the go.mod file on disk equal to new. - index = indexModFile(new, modFile, false) + MainModules.SetIndex(mainModule, indexModFile(new, modFile, mainModule, false)) // Update go.sum after releasing the side lock and refreshing the index. // 'go mod init' shouldn't write go.sum, since it will be incomplete. if cfg.CmdName != "mod init" { - modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums)) + if err == nil { + err = modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()) + } } }() @@ -1063,7 +1545,7 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) errNoChange := errors.New("no update needed") - err = lockedfile.Transform(ModFilePath(), func(old []byte) ([]byte, error) { + err = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) { if bytes.Equal(old, new) { // The go.mod file is already equal to new, possibly as the result of some // other process. @@ -1084,8 +1566,9 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) }) if err != nil && err != errNoChange { - base.Fatalf("go: updating go.mod: %v", err) + return fmt.Errorf("updating go.mod: %w", err) } + return nil } // keepSums returns the set of modules (and go.mod file entries) for which @@ -1113,16 +1596,18 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums continue } - if rs.depth == lazy && pkg.mod.Path != "" { + if rs.pruning == pruned && pkg.mod.Path != "" { if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version { - // pkg was loaded from a root module, and because the main module is - // lazy we do not check non-root modules for conflicts for packages - // that can be found in roots. So we only need the checksums for the - // root modules that may contain pkg, not all possible modules. + // pkg was loaded from a root module, and because the main module has + // a pruned module graph we do not check non-root modules for + // conflicts for packages that can be found in roots. So we only need + // the checksums for the root modules that may contain pkg, not all + // possible modules. for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) { if v, ok := rs.rootSelected(prefix); ok && v != "none" { m := module.Version{Path: prefix, Version: v} - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } continue @@ -1133,15 +1618,15 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) { if v := mg.Selected(prefix); v != "none" { m := module.Version{Path: prefix, Version: v} - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } } } if rs.graph.Load() == nil { - // The module graph was not loaded, possibly because the main module is lazy - // or possibly because we haven't needed to load the graph yet. + // We haven't needed to load the module graph so far. // Save sums for the root modules (or their replacements), but don't // incur the cost of loading the graph just to find and retain the sums. for _, m := range rs.rootModules { @@ -1158,13 +1643,15 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums // The requirements from m's go.mod file are present in the module graph, // so they are relevant to the MVS result regardless of whether m was // actually selected. - keep[modkey(resolveReplacement(m))] = true + r := resolveReplacement(m) + keep[modkey(r)] = true } }) if which == addBuildListZipSums { for _, m := range mg.BuildList() { - keep[resolveReplacement(m)] = true + r := resolveReplacement(m) + keep[r] = true } } } @@ -1184,3 +1671,56 @@ const ( func modkey(m module.Version) module.Version { return module.Version{Path: m.Path, Version: m.Version + "/go.mod"} } + +func suggestModulePath(path string) string { + var m string + + i := len(path) + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + i-- + } + url := path[:i] + url = strings.TrimSuffix(url, "/v") + url = strings.TrimSuffix(url, "/") + + f := func(c rune) bool { + return c > '9' || c < '0' + } + s := strings.FieldsFunc(path[i:], f) + if len(s) > 0 { + m = s[0] + } + m = strings.TrimLeft(m, "0") + if m == "" || m == "1" { + return url + "/v2" + } + + return url + "/v" + m +} + +func suggestGopkgIn(path string) string { + var m string + i := len(path) + for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) { + i-- + } + url := path[:i] + url = strings.TrimSuffix(url, ".v") + url = strings.TrimSuffix(url, "/v") + url = strings.TrimSuffix(url, "/") + + f := func(c rune) bool { + return c > '9' || c < '0' + } + s := strings.FieldsFunc(path, f) + if len(s) > 0 { + m = s[0] + } + + m = strings.TrimLeft(m, "0") + + if m == "" { + return url + ".v1" + } + return url + ".v" + m +} diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go index ccdeb9b1d11e86..e822d0650480d8 100644 --- a/src/cmd/go/internal/modload/list.go +++ b/src/cmd/go/internal/modload/list.go @@ -5,15 +5,19 @@ package modload import ( + "bytes" "context" + "encoding/json" "errors" "fmt" + "io" "os" "runtime" "strings" "cmd/go/internal/base" "cmd/go/internal/cfg" + "cmd/go/internal/modfetch/codehost" "cmd/go/internal/modinfo" "cmd/go/internal/search" @@ -34,13 +38,44 @@ const ( // along with any error preventing additional matches from being identified. // // The returned slice can be nonempty even if the error is non-nil. -func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo.ModulePublic, error) { - rs, mods, err := listModules(ctx, LoadModFile(ctx), args, mode) +func ListModules(ctx context.Context, args []string, mode ListMode, reuseFile string) ([]*modinfo.ModulePublic, error) { + var reuse map[module.Version]*modinfo.ModulePublic + if reuseFile != "" { + data, err := os.ReadFile(reuseFile) + if err != nil { + return nil, err + } + dec := json.NewDecoder(bytes.NewReader(data)) + reuse = make(map[module.Version]*modinfo.ModulePublic) + for { + var m modinfo.ModulePublic + if err := dec.Decode(&m); err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("parsing %s: %v", reuseFile, err) + } + if m.Origin == nil || !m.Origin.Checkable() { + // Nothing to check to validate reuse. + continue + } + m.Reuse = true + reuse[module.Version{Path: m.Path, Version: m.Version}] = &m + if m.Query != "" { + reuse[module.Version{Path: m.Path, Version: m.Query}] = &m + } + } + } + + rs, mods, err := listModules(ctx, LoadModFile(ctx), args, mode, reuse) type token struct{} sem := make(chan token, runtime.GOMAXPROCS(0)) if mode != 0 { for _, m := range mods { + if m.Reuse { + continue + } add := func(m *modinfo.ModulePublic) { sem <- token{} go func() { @@ -72,14 +107,21 @@ func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo. } if err == nil { - commitRequirements(ctx, modFileGoVersion(), rs) + requirements = rs + if !ExplicitWriteGoMod { + err = commitRequirements(ctx) + } } return mods, err } -func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) { +func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) { if len(args) == 0 { - return rs, []*modinfo.ModulePublic{moduleInfo(ctx, rs, Target, mode)}, nil + var ms []*modinfo.ModulePublic + for _, m := range MainModules.Versions() { + ms = append(ms, moduleInfo(ctx, rs, m, mode, reuse)) + } + return rs, ms, nil } needFullGraph := false @@ -101,7 +143,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List path := arg[:i] vers := arg[i+1:] if vers == "upgrade" || vers == "patch" { - if _, ok := rs.rootSelected(path); !ok || rs.depth == eager { + if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned { needFullGraph = true if !HasModRoot() { base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) @@ -110,7 +152,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List } continue } - if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager { + if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned { needFullGraph = true if mode&ListVersions == 0 && !HasModRoot() { base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot) @@ -150,12 +192,17 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List // specific revision or used 'go list -retracted'. allowed = nil } - info, err := Query(ctx, path, vers, current, allowed) + info, err := queryReuse(ctx, path, vers, current, allowed, reuse) if err != nil { + var origin *codehost.Origin + if info != nil { + origin = info.Origin + } mods = append(mods, &modinfo.ModulePublic{ Path: path, Version: vers, Error: modinfoError(path, vers, err), + Origin: origin, }) continue } @@ -164,7 +211,11 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List // *Requirements instead. var noRS *Requirements - mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode) + mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode, reuse) + if vers != mod.Version { + mod.Query = vers + } + mod.Origin = info.Origin mods = append(mods, mod) continue } @@ -193,7 +244,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List continue } if v != "none" { - mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode)) + mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode, reuse)) } else if cfg.BuildMod == "vendor" { // In vendor mode, we can't determine whether a missing module is “a // known dependency” because the module graph is incomplete. @@ -222,7 +273,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List matched = true if !matchedModule[m] { matchedModule[m] = true - mods = append(mods, moduleInfo(ctx, rs, m, mode)) + mods = append(mods, moduleInfo(ctx, rs, m, mode, reuse)) } } } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index bce9ad85f42e6c..060d0cb21a01dc 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -40,9 +40,10 @@ package modload // - the main module specifies a go version ≤ 1.15, and the package is imported // by a *test of* another package in "all". // -// When we implement lazy loading, we will record the modules providing packages -// in "all" even when we are only loading individual packages, so we set the -// pkgInAll flag regardless of the whether the "all" pattern is a root. +// When graph pruning is in effect, we want to spot-check the graph-pruning +// invariants — which depend on which packages are known to be in "all" — even +// when we are only loading individual packages, so we set the pkgInAll flag +// regardless of the whether the "all" pattern is a root. // (This is necessary to maintain the “import invariant” described in // https://golang.org/design/36460-lazy-module-loading.) // @@ -115,6 +116,7 @@ import ( "cmd/go/internal/fsys" "cmd/go/internal/imports" "cmd/go/internal/modfetch" + "cmd/go/internal/modindex" "cmd/go/internal/mvs" "cmd/go/internal/par" "cmd/go/internal/search" @@ -230,6 +232,9 @@ type PackageOpts struct { // SilenceUnmatchedWarnings suppresses the warnings normally emitted for // patterns that did not match any packages. SilenceUnmatchedWarnings bool + + // Resolve the query against this module. + MainModule module.Version } // LoadPackages identifies the set of packages matching the given patterns and @@ -255,7 +260,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma case m.IsLocal(): // Evaluate list of file system directories on first iteration. if m.Dirs == nil { - matchLocalDirs(ctx, m, rs) + matchModRoots := modRoots + if opts.MainModule != (module.Version{}) { + matchModRoots = []string{MainModules.ModRoot(opts.MainModule)} + } + matchLocalDirs(ctx, matchModRoots, m, rs) } // Make a copy of the directory list and translate to import paths. @@ -274,7 +283,9 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // If we're outside of a module, ensure that the failure mode // indicates that. - ModRoot() + if !HasModRoot() { + die() + } if ld != nil { m.AddError(err) @@ -306,7 +317,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // The initial roots are the packages in the main module. // loadFromRoots will expand that to "all". m.Errs = m.Errs[:0] - matchPackages(ctx, m, opts.Tags, omitStd, []module.Version{Target}) + matchModules := MainModules.Versions() + if opts.MainModule != (module.Version{}) { + matchModules = []module.Version{opts.MainModule} + } + matchPackages(ctx, m, opts.Tags, omitStd, matchModules) } else { // Starting with the packages in the main module, // enumerate the full list of "all". @@ -324,7 +339,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } - initialRS, _ := loadModFile(ctx) // Ignore needCommit — we're going to commit at the end regardless. + initialRS := LoadModFile(ctx) ld := loadFromRoots(ctx, loaderParams{ PackageOpts: opts, @@ -365,7 +380,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma for _, m := range initialRS.rootModules { var unused bool - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { // m is unused if it was dropped from the module graph entirely. If it // was only demoted from direct to indirect, it may still be in use via // a transitive import. @@ -384,7 +399,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } keep := keepSums(ctx, ld, ld.requirements, loadedZipSumsOnly) - if compatDepth := modDepthFromGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.depth { + if compatDepth := pruningForGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.pruning { compatRS := newRequirements(compatDepth, ld.requirements.rootModules, ld.requirements.direct) ld.checkTidyCompatibility(ctx, compatRS) @@ -393,7 +408,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } - if allowWriteGoMod { + if !ExplicitWriteGoMod { modfetch.TrimGoSum(keep) // commitRequirements below will also call WriteGoSum, but the "keep" map @@ -401,13 +416,23 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma // loaded.requirements, but here we may have also loaded (and want to // preserve checksums for) additional entities from compatRS, which are // only needed for compatibility with ld.TidyCompatibleVersion. - modfetch.WriteGoSum(keep) + if err := modfetch.WriteGoSum(keep, mustHaveCompleteRequirements()); err != nil { + base.Fatalf("go: %v", err) + } + } + + // Update the go.mod file's Go version if necessary. + if modFile := ModFile(); modFile != nil && ld.GoVersion != "" { + modFile.AddGoStmt(ld.GoVersion) } } // Success! Update go.mod and go.sum (if needed) and return the results. + // We'll skip updating if ExplicitWriteGoMod is true (the caller has opted + // to call WriteGoMod itself) or if ResolveMissingImports is false (the + // command wants to examine the package graph as-is). loaded = ld - commitRequirements(ctx, loaded.GoVersion, loaded.requirements) + requirements = loaded.requirements for _, pkg := range ld.pkgs { if !pkg.isTest() { @@ -415,12 +440,19 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma } } sort.Strings(loadedPackages) + + if !ExplicitWriteGoMod && opts.ResolveMissingImports { + if err := commitRequirements(ctx); err != nil { + base.Fatalf("go: %v", err) + } + } + return matches, loadedPackages } // matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories // outside of the standard library and active modules. -func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) { +func matchLocalDirs(ctx context.Context, modRoots []string, m *search.Match, rs *Requirements) { if !m.IsLocal() { panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern())) } @@ -436,14 +468,27 @@ func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) { if !filepath.IsAbs(dir) { absDir = filepath.Join(base.Cwd(), dir) } - if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(ctx, absDir, rs) == "" { + + modRoot := findModuleRoot(absDir) + found := false + for _, mainModuleRoot := range modRoots { + if mainModuleRoot == modRoot { + found = true + break + } + } + if !found && search.InDir(absDir, cfg.GOROOTsrc) == "" && pathInModuleCache(ctx, absDir, rs) == "" { m.Dirs = []string{} - m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir))) + scope := "main module or its selected dependencies" + if inWorkspaceMode() { + scope = "modules listed in go.work or their selected dependencies" + } + m.AddError(fmt.Errorf("directory prefix %s does not contain %s", base.ShortPath(absDir), scope)) return } } - m.MatchDirs() + m.MatchDirs(modRoots) } // resolveLocalPackage resolves a filesystem path to a package path. @@ -485,49 +530,69 @@ func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (str } } - if modRoot != "" && absDir == modRoot { - if absDir == cfg.GOROOTsrc { - return "", errPkgIsGorootSrc + for _, mod := range MainModules.Versions() { + modRoot := MainModules.ModRoot(mod) + if modRoot != "" && absDir == modRoot { + if absDir == cfg.GOROOTsrc { + return "", errPkgIsGorootSrc + } + return MainModules.PathPrefix(mod), nil } - return targetPrefix, nil } // Note: The checks for @ here are just to avoid misinterpreting // the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar). // It's not strictly necessary but helpful to keep the checks. - if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") { - suffix := filepath.ToSlash(absDir[len(modRoot):]) - if strings.HasPrefix(suffix, "/vendor/") { - if cfg.BuildMod != "vendor" { - return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir) + var pkgNotFoundErr error + pkgNotFoundLongestPrefix := "" + for _, mainModule := range MainModules.Versions() { + modRoot := MainModules.ModRoot(mainModule) + if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") { + suffix := filepath.ToSlash(absDir[len(modRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + if cfg.BuildMod != "vendor" { + return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir) + } + + readVendorList(mainModule) + pkg := strings.TrimPrefix(suffix, "/vendor/") + if _, ok := vendorPkgModule[pkg]; !ok { + return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir) + } + return pkg, nil } - readVendorList() - pkg := strings.TrimPrefix(suffix, "/vendor/") - if _, ok := vendorPkgModule[pkg]; !ok { - return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir) + mainModulePrefix := MainModules.PathPrefix(mainModule) + if mainModulePrefix == "" { + pkg := strings.TrimPrefix(suffix, "/") + if pkg == "builtin" { + // "builtin" is a pseudo-package with a real source file. + // It's not included in "std", so it shouldn't resolve from "." + // within module "std" either. + return "", errPkgIsBuiltin + } + return pkg, nil } - return pkg, nil - } - if targetPrefix == "" { - pkg := strings.TrimPrefix(suffix, "/") - if pkg == "builtin" { - // "builtin" is a pseudo-package with a real source file. - // It's not included in "std", so it shouldn't resolve from "." - // within module "std" either. - return "", errPkgIsBuiltin + pkg := mainModulePrefix + suffix + if _, ok, err := dirInModule(pkg, mainModulePrefix, modRoot, true); err != nil { + return "", err + } else if !ok { + // This main module could contain the directory but doesn't. Other main + // modules might contain the directory, so wait till we finish the loop + // to see if another main module contains directory. But if not, + // return an error. + if len(mainModulePrefix) > len(pkgNotFoundLongestPrefix) { + pkgNotFoundLongestPrefix = mainModulePrefix + pkgNotFoundErr = &PackageNotInModuleError{MainModules: []module.Version{mainModule}, Pattern: pkg} + } + continue } return pkg, nil } - - pkg := targetPrefix + suffix - if _, ok, err := dirInModule(pkg, targetPrefix, modRoot, true); err != nil { - return "", err - } else if !ok { - return "", &PackageNotInModuleError{Mod: Target, Pattern: pkg} - } - return pkg, nil + } + if pkgNotFoundErr != nil { + return "", pkgNotFoundErr } if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") { @@ -540,7 +605,13 @@ func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (str pkg := pathInModuleCache(ctx, absDir, rs) if pkg == "" { - return "", fmt.Errorf("directory %s outside available modules", base.ShortPath(absDir)) + if inWorkspaceMode() { + if mr := findModuleRoot(absDir); mr != "" { + return "", fmt.Errorf("directory %s is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using:\n\tgo work use %s", base.ShortPath(absDir), base.ShortPath(mr)) + } + return "", fmt.Errorf("directory %s outside modules listed in go.work or their selected dependencies", base.ShortPath(absDir)) + } + return "", fmt.Errorf("directory %s outside main module or its selected dependencies", base.ShortPath(absDir)) } return pkg, nil } @@ -560,7 +631,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string if repl := Replacement(m); repl.Path != "" && repl.Version == "" { root = repl.Path if !filepath.IsAbs(root) { - root = filepath.Join(ModRoot(), root) + root = filepath.Join(replaceRelativeTo(), root) } } else if repl.Path != "" { root, err = modfetch.DownloadDir(repl) @@ -583,7 +654,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string return path.Join(m.Path, filepath.ToSlash(sub)), true } - if rs.depth == lazy { + if rs.pruning == pruned { for _, m := range rs.rootModules { if v, _ := rs.rootSelected(m.Path); v != m.Version { continue // m is a root, but we have a higher root for the same path. @@ -596,9 +667,9 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string } } - // None of the roots contained dir, or we're in eager mode and want to load - // the full module graph more aggressively. Either way, check the full graph - // to see if the directory is a non-root dependency. + // None of the roots contained dir, or the graph is unpruned (so we don't want + // to distinguish between roots and transitive dependencies). Either way, + // check the full graph to see if the directory is a non-root dependency. // // If the roots are not consistent with the full module graph, the selected // versions of root modules may differ from what we already checked above. @@ -645,14 +716,20 @@ func ImportFromFiles(ctx context.Context, gofiles []string) { return roots }, }) - commitRequirements(ctx, loaded.GoVersion, loaded.requirements) + requirements = loaded.requirements + + if !ExplicitWriteGoMod { + if err := commitRequirements(ctx); err != nil { + base.Fatalf("go: %v", err) + } + } } // DirImportPath returns the effective import path for dir, -// provided it is within the main module, or else returns ".". -func DirImportPath(ctx context.Context, dir string) string { +// provided it is within a main module, or else returns ".". +func (mms *MainModuleSet) DirImportPath(ctx context.Context, dir string) (path string, m module.Version) { if !HasModRoot() { - return "." + return ".", module.Version{} } LoadModFile(ctx) // Sets targetPrefix. @@ -662,39 +739,32 @@ func DirImportPath(ctx context.Context, dir string) string { dir = filepath.Clean(dir) } - if dir == modRoot { - return targetPrefix - } - if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) { - suffix := filepath.ToSlash(dir[len(modRoot):]) - if strings.HasPrefix(suffix, "/vendor/") { - return strings.TrimPrefix(suffix, "/vendor/") + var longestPrefix string + var longestPrefixPath string + var longestPrefixVersion module.Version + for _, v := range mms.Versions() { + modRoot := mms.ModRoot(v) + if dir == modRoot { + return mms.PathPrefix(v), v + } + if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) { + pathPrefix := MainModules.PathPrefix(v) + if pathPrefix > longestPrefix { + longestPrefix = pathPrefix + longestPrefixVersion = v + suffix := filepath.ToSlash(dir[len(modRoot):]) + if strings.HasPrefix(suffix, "/vendor/") { + longestPrefixPath = strings.TrimPrefix(suffix, "/vendor/") + } + longestPrefixPath = mms.PathPrefix(v) + suffix + } } - return targetPrefix + suffix } - return "." -} - -// ImportMap returns the actual package import path -// for an import path found in source code. -// If the given import path does not appear in the source code -// for the packages that have been loaded, ImportMap returns the empty string. -func ImportMap(path string) string { - pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) - if !ok { - return "" + if len(longestPrefix) > 0 { + return longestPrefixPath, longestPrefixVersion } - return pkg.path -} -// PackageDir returns the directory containing the source code -// for the package named by the import path. -func PackageDir(path string) string { - pkg, ok := loaded.pkgCache.Get(path).(*loadPkg) - if !ok { - return "" - } - return pkg.dir + return ".", module.Version{} } // PackageModule returns the module providing the package named by the import path. @@ -783,7 +853,7 @@ func (ld *loader) reset() { // errorf reports an error via either os.Stderr or base.Errorf, // according to whether ld.AllowErrors is set. -func (ld *loader) errorf(format string, args ...interface{}) { +func (ld *loader) errorf(format string, args ...any) { if ld.AllowErrors { fmt.Fprintf(os.Stderr, format, args...) } else { @@ -807,6 +877,7 @@ type loadPkg struct { imports []*loadPkg // packages imported by this one testImports []string // test-only imports, saved for use by pkg.test. inStd bool + altMods []module.Version // modules that could have contained the package but did not // Populated by (*loader).pkgTest: testOnce sync.Once @@ -861,7 +932,7 @@ func (f loadPkgFlags) has(cond loadPkgFlags) bool { // An atomicLoadPkgFlags stores a loadPkgFlags for which individual flags can be // added atomically. type atomicLoadPkgFlags struct { - bits int32 + bits atomic.Int32 } // update sets the given flags in af (in addition to any flags already set). @@ -870,9 +941,9 @@ type atomicLoadPkgFlags struct { // flags were newly-set. func (af *atomicLoadPkgFlags) update(flags loadPkgFlags) (old loadPkgFlags) { for { - old := atomic.LoadInt32(&af.bits) + old := af.bits.Load() new := old | int32(flags) - if new == old || atomic.CompareAndSwapInt32(&af.bits, old, new) { + if new == old || af.bits.CompareAndSwap(old, new) { return loadPkgFlags(old) } } @@ -880,7 +951,7 @@ func (af *atomicLoadPkgFlags) update(flags loadPkgFlags) (old loadPkgFlags) { // has reports whether all of the flags in cond are set in af. func (af *atomicLoadPkgFlags) has(cond loadPkgFlags) bool { - return loadPkgFlags(atomic.LoadInt32(&af.bits))&cond == cond + return loadPkgFlags(af.bits.Load())&cond == cond } // isTest reports whether pkg is a test of another package. @@ -894,10 +965,7 @@ func (pkg *loadPkg) fromExternalModule() bool { if pkg.mod.Path == "" { return false // loaded from the standard library, not a module } - if pkg.mod.Path == Target.Path { - return false // loaded from the main module. - } - return true + return !MainModules.Contains(pkg.mod.Path) } var errMissing = errors.New("cannot find package") @@ -915,10 +983,10 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } if ld.GoVersion == "" { - ld.GoVersion = modFileGoVersion() + ld.GoVersion = MainModules.GoVersion() if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 { - ld.errorf("go mod tidy: go.mod file indicates go %s, but maximum supported version is %s\n", ld.GoVersion, LatestGoVersion()) + ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion()) base.ExitIfErrors() } } @@ -935,18 +1003,30 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } if semver.Compare("v"+ld.GoVersion, narrowAllVersionV) < 0 && !ld.UseVendorAll { - // The module's go version explicitly predates the change in "all" for lazy - // loading, so continue to use the older interpretation. + // The module's go version explicitly predates the change in "all" for graph + // pruning, so continue to use the older interpretation. ld.allClosesOverTests = true } var err error - ld.requirements, err = convertDepth(ctx, ld.requirements, modDepthFromGoVersion(ld.GoVersion)) + desiredPruning := pruningForGoVersion(ld.GoVersion) + if ld.requirements.pruning == workspace { + desiredPruning = workspace + } + ld.requirements, err = convertPruning(ctx, ld.requirements, desiredPruning) if err != nil { ld.errorf("go: %v\n", err) } - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { + // If the module graph does not support pruning, we assume that we will need + // the full module graph in order to load package dependencies. + // + // This might not be strictly necessary, but it matches the historical + // behavior of the 'go' command and keeps the go.mod file more consistent in + // case of erroneous hand-edits — which are less likely to be detected by + // spot-checks in modules that do not maintain the expanded go.mod + // requirements needed for graph pruning. var err error ld.requirements, _, err = expandGraph(ctx, ld.requirements) if err != nil { @@ -963,7 +1043,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { // build list we're using. rootPkgs := ld.listRoots(ld.requirements) - if ld.requirements.depth == lazy && cfg.BuildMod == "mod" { + if ld.requirements.pruning == pruned && cfg.BuildMod == "mod" { // Before we start loading transitive imports of packages, locate all of // the root packages and promote their containing modules to root modules // dependencies. If their go.mod files are tidy (the common case) and the @@ -1005,7 +1085,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { break } if changed { - // Don't resolve missing imports until the module graph have stabilized. + // Don't resolve missing imports until the module graph has stabilized. // If the roots are still changing, they may turn out to specify a // requirement on the missing package(s), and we would rather use a // version specified by a new root than add a new dependency on an @@ -1074,15 +1154,15 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { ld.errorf("go: %v\n", err) } - if ld.requirements.depth == lazy { + if ld.requirements.pruning == pruned { // We continuously add tidy roots to ld.requirements during loading, so at // this point the tidy roots should be a subset of the roots of // ld.requirements, ensuring that no new dependencies are brought inside - // the lazy-loading horizon. + // the graph-pruning horizon. // If that is not the case, there is a bug in the loading loop above. for _, m := range rs.rootModules { if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version { - ld.errorf("go mod tidy: internal error: a requirement on %v is needed but was not added during package loading\n", m) + ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m) base.ExitIfErrors() } } @@ -1124,21 +1204,20 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } // updateRequirements ensures that ld.requirements is consistent with the -// information gained from ld.pkgs and includes the modules in add as roots at -// at least the given versions. +// information gained from ld.pkgs. // // In particular: // -// - Modules that provide packages directly imported from the main module are -// marked as direct, and are promoted to explicit roots. If a needed root -// cannot be promoted due to -mod=readonly or -mod=vendor, the importing -// package is marked with an error. +// - Modules that provide packages directly imported from the main module are +// marked as direct, and are promoted to explicit roots. If a needed root +// cannot be promoted due to -mod=readonly or -mod=vendor, the importing +// package is marked with an error. // -// - If ld scanned the "all" pattern independent of build constraints, it is -// guaranteed to have seen every direct import. Module dependencies that did -// not provide any directly-imported package are then marked as indirect. +// - If ld scanned the "all" pattern independent of build constraints, it is +// guaranteed to have seen every direct import. Module dependencies that did +// not provide any directly-imported package are then marked as indirect. // -// - Root dependencies are updated to their selected versions. +// - Root dependencies are updated to their selected versions. // // The "changed" return value reports whether the update changed the selected // version of any module that either provided a loaded package or may now @@ -1168,7 +1247,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err } for _, pkg := range ld.pkgs { - if pkg.mod != Target { + if pkg.mod.Version != "" || !MainModules.Contains(pkg.mod.Path) { continue } for _, dep := range pkg.imports { @@ -1176,6 +1255,24 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err continue } + if inWorkspaceMode() { + // In workspace mode / workspace pruning mode, the roots are the main modules + // rather than the main module's direct dependencies. The check below on the selected + // roots does not apply. + if mg, err := rs.Graph(ctx); err != nil { + return false, err + } else if _, ok := mg.RequiredBy(dep.mod); !ok { + // dep.mod is not an explicit dependency, but needs to be. + // See comment on error returned below. + pkg.err = &DirectImportFromImplicitDependencyError{ + ImporterPath: pkg.path, + ImportedPath: dep.path, + Module: dep.mod, + } + } + continue + } + if pkg.err == nil && cfg.BuildMod != "mod" { if v, ok := rs.rootSelected(dep.mod.Path); !ok || v != dep.mod.Version { // dep.mod is not an explicit dependency, but needs to be. @@ -1206,14 +1303,14 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err var addRoots []module.Version if ld.Tidy { - // When we are tidying a lazy module, we may need to add roots to preserve - // the versions of indirect, test-only dependencies that are upgraded - // above or otherwise missing from the go.mod files of direct - // dependencies. (For example, the direct dependency might be a very + // When we are tidying a module with a pruned dependency graph, we may need + // to add roots to preserve the versions of indirect, test-only dependencies + // that are upgraded above or otherwise missing from the go.mod files of + // direct dependencies. (For example, the direct dependency might be a very // stable codebase that predates modules and thus lacks a go.mod file, or - // the author of the direct dependency may have forgotten to commit a - // change to the go.mod file, or may have made an erroneous hand-edit that - // causes it to be untidy.) + // the author of the direct dependency may have forgotten to commit a change + // to the go.mod file, or may have made an erroneous hand-edit that causes + // it to be untidy.) // // Promoting an indirect dependency to a root adds the next layer of its // dependencies to the module graph, which may increase the selected @@ -1283,7 +1380,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err // // In some sense, we can think of this as ‘upgraded the module providing // pkg.path from "none" to a version higher than "none"’. - if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { + if _, _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { changed = true break } @@ -1327,6 +1424,15 @@ func (ld *loader) resolveMissingImports(ctx context.Context) (modAddedBy map[mod var err error mod, err = queryImport(ctx, pkg.path, ld.requirements) if err != nil { + var ime *ImportMissingError + if errors.As(err, &ime) { + for curstack := pkg.stack; curstack != nil; curstack = curstack.stack { + if MainModules.Contains(curstack.mod.Path) { + ime.ImportingMainModule = curstack.mod + break + } + } + } // pkg.err was already non-nil, so we can reasonably attribute the error // for pkg to either the original error or the one returned by // queryImport. The existing error indicates only that we couldn't find @@ -1380,7 +1486,7 @@ func (ld *loader) pkg(ctx context.Context, path string, flags loadPkgFlags) *loa panic("internal error: (*loader).pkg called with pkgImportsLoaded flag set") } - pkg := ld.pkgCache.Do(path, func() interface{} { + pkg := ld.pkgCache.Do(path, func() any { pkg := &loadPkg{ path: path, } @@ -1425,7 +1531,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg // so it's ok if we call it more than is strictly necessary. wantTest := false switch { - case ld.allPatternIsRoot && pkg.mod == Target: + case ld.allPatternIsRoot && MainModules.Contains(pkg.mod.Path): // We are loading the "all" pattern, which includes packages imported by // tests in the main module. This package is in the main module, so we // need to identify the imports of its test even if LoadTests is not set. @@ -1446,7 +1552,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg if wantTest { var testFlags loadPkgFlags - if pkg.mod == Target || (ld.allClosesOverTests && new.has(pkgInAll)) { + if MainModules.Contains(pkg.mod.Path) || (ld.allClosesOverTests && new.has(pkgInAll)) { // Tests of packages in the main module are in "all", in the sense that // they cause the packages they import to also be in "all". So are tests // of packages in "all" if "all" closes over test dependencies. @@ -1485,7 +1591,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // If the main module is tidy and the package is in "all" — or if we're // lucky — we can identify all of its imports without actually loading the // full module graph. - m, _, err := importFromModules(ctx, path, ld.requirements, nil) + m, _, _, _, err := importFromModules(ctx, path, ld.requirements, nil) if err != nil { var missing *ImportMissingError if errors.As(err, &missing) && ld.ResolveMissingImports { @@ -1511,7 +1617,8 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // module to a root to ensure that any other packages this package // imports are resolved from correct dependency versions. // - // (This is the “argument invariant” from the lazy loading design.) + // (This is the “argument invariant” from + // https://golang.org/design/36460-lazy-module-loading.) need := <-needc need[m] = true needc <- need @@ -1554,26 +1661,8 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // load loads an individual package. func (ld *loader) load(ctx context.Context, pkg *loadPkg) { - if strings.Contains(pkg.path, "@") { - // Leave for error during load. - return - } - if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) { - // Leave for error during load. - // (Module mode does not allow local imports.) - return - } - - if search.IsMetaPackage(pkg.path) { - pkg.err = &invalidImportError{ - importPath: pkg.path, - err: fmt.Errorf("%q is not an importable package; see 'go help packages'", pkg.path), - } - return - } - var mg *ModuleGraph - if ld.requirements.depth == eager { + if ld.requirements.pruning == unpruned { var err error mg, err = ld.requirements.Graph(ctx) if err != nil { @@ -1589,11 +1678,12 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) { } } - pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) + var modroot string + pkg.mod, modroot, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) if pkg.dir == "" { return } - if pkg.mod == Target { + if MainModules.Contains(pkg.mod.Path) { // Go ahead and mark pkg as in "all". This provides the invariant that a // package that is *only* imported by other packages in "all" is always // marked as such before loading its imports. @@ -1619,7 +1709,7 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) { // We can't scan standard packages for gccgo. } else { var err error - imports, testImports, err = scanDir(pkg.dir, ld.Tags) + imports, testImports, err = scanDir(modroot, pkg.dir, ld.Tags) if err != nil { pkg.err = err return @@ -1698,13 +1788,14 @@ func (ld *loader) stdVendor(parentPath, path string) string { } if str.HasPathPrefix(parentPath, "cmd") { - if !ld.VendorModulesInGOROOTSrc || Target.Path != "cmd" { + if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("cmd") { vendorPath := pathpkg.Join("cmd", "vendor", path) + if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil { return vendorPath } } - } else if !ld.VendorModulesInGOROOTSrc || Target.Path != "std" || str.HasPathPrefix(parentPath, "vendor") { + } else if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("std") || str.HasPathPrefix(parentPath, "vendor") { // If we are outside of the 'std' module, resolve imports from within 'std' // to the vendor directory. // @@ -1746,7 +1837,7 @@ func (ld *loader) computePatternAll() (all []string) { func (ld *loader) checkMultiplePaths() { mods := ld.requirements.rootModules if cached := ld.requirements.graph.Load(); cached != nil { - if mg := cached.(cachedGraph).mg; mg != nil { + if mg := cached.mg; mg != nil { mods = mg.BuildList() } } @@ -1781,7 +1872,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) fmt.Fprintln(os.Stderr) goFlag := "" - if ld.GoVersion != modFileGoVersion() { + if ld.GoVersion != MainModules.GoVersion() { goFlag = " -go=" + ld.GoVersion } @@ -1813,7 +1904,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) mg, err := rs.Graph(ctx) if err != nil { - ld.errorf("go mod tidy: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) + ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) suggestFixes() return } @@ -1847,7 +1938,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) pkg := pkg ld.work.Add(func() { - mod, _, err := importFromModules(ctx, pkg.path, rs, mg) + mod, _, _, _, err := importFromModules(ctx, pkg.path, rs, mg) if mod != pkg.mod { mismatches := <-mismatchMu mismatches[pkg] = mismatch{mod: mod, err: err} @@ -1900,9 +1991,10 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) case mismatch.err != nil: // pkg resolved successfully, but errors out using the requirements in rs. // - // This could occur because the import is provided by a single lazy root - // (and is thus unambiguous in lazy mode) and also one or more - // transitive dependencies (and is ambiguous in eager mode). + // This could occur because the import is provided by a single root (and + // is thus unambiguous in a main module with a pruned module graph) and + // also one or more transitive dependencies (and is ambiguous with an + // unpruned graph). // // It could also occur because some transitive dependency upgrades the // module that previously provided the package to a version that no @@ -1940,18 +2032,18 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) } case pkg.err != nil: - // pkg had an error in lazy mode (presumably suppressed with the -e flag), - // but not in eager mode. + // pkg had an error in with a pruned module graph (presumably suppressed + // with the -e flag), but the error went away using an unpruned graph. // - // This is possible, if, say, the import is unresolved in lazy mode + // This is possible, if, say, the import is unresolved in the pruned graph // (because the "latest" version of each candidate module either is - // unavailable or does not contain the package), but is resolved in - // eager mode due to a newer-than-latest dependency that is normally - // runed out of the module graph. + // unavailable or does not contain the package), but is resolved in the + // unpruned graph due to a newer-than-latest dependency that is normally + // pruned out. // // This could also occur if the source code for the module providing the - // package in lazy mode has a checksum error, but eager mode upgrades - // that module to a version with a correct checksum. + // package in the pruned graph has a checksum error, but the unpruned + // graph upgrades that module to a version with a correct checksum. // // pkg.err should have already been logged elsewhere — along with a // stack trace — so log only the import path and non-error info here. @@ -1987,8 +2079,16 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) // during "go vendor", we look into "// +build appengine" files and // may see these legacy imports. We drop them so that the module // search does not look for modules to try to satisfy them. -func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) { +func scanDir(modroot string, dir string, tags map[string]bool) (imports_, testImports []string, err error) { + if ip, mierr := modindex.GetPackage(modroot, dir); mierr == nil { + imports_, testImports, err = ip.ScanDir(tags) + goto Happy + } else if !errors.Is(mierr, modindex.ErrNotIndexed) { + return nil, nil, mierr + } + imports_, testImports, err = imports.ScanDir(dir, tags) +Happy: filter := func(x []string) []string { w := 0 @@ -2046,7 +2146,6 @@ func (ld *loader) buildStacks() { // other2 tested by // other2.test imports // pkg -// func (pkg *loadPkg) stackText() string { var stack []*loadPkg for p := pkg; p != nil; p = p.stack { diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index 03e02e73b63f65..75c278a7dfdb5f 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -33,10 +33,13 @@ const ( // tests outside of the main module. narrowAllVersionV = "v1.16" - // lazyLoadingVersionV is the Go version (plus leading "v") at which a + // ExplicitIndirectVersionV is the Go version (plus leading "v") at which a // module's go.mod file is expected to list explicit requirements on every // module that provides any package transitively imported by that module. - lazyLoadingVersionV = "v1.17" + // + // Other indirect dependencies of such a module can be safely pruned out of + // the module graph; see https://golang.org/ref/mod#graph-pruning. + ExplicitIndirectVersionV = "v1.17" // separateIndirectVersionV is the Go version (plus leading "v") at which // "// indirect" dependencies are added in a block separate from the direct @@ -44,23 +47,37 @@ const ( separateIndirectVersionV = "v1.17" ) -const ( - // go117EnableLazyLoading toggles whether lazy-loading code paths should be - // active. It will be removed once the lazy loading implementation is stable - // and well-tested. - go117EnableLazyLoading = true - - // go1117LazyTODO is a constant that exists only until lazy loading is - // implemented. Its use indicates a condition that will need to change if the - // main module is lazy. - go117LazyTODO = false -) +// ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the +// overlay, locks the file while reading, and applies fix, if applicable. +func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) { + if gomodActual, ok := fsys.OverlayPath(gomod); ok { + // Don't lock go.mod if it's part of the overlay. + // On Plan 9, locking requires chmod, and we don't want to modify any file + // in the overlay. See #44700. + data, err = os.ReadFile(gomodActual) + } else { + data, err = lockedfile.Read(gomodActual) + } + if err != nil { + return nil, nil, err + } -var modFile *modfile.File + f, err = modfile.Parse(gomod, data, fix) + if err != nil { + // Errors returned by modfile.Parse begin with file:line. + return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err) + } + if f.Module == nil { + // No module declaration. Must add module path. + return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod") + } + + return data, f, err +} // modFileGoVersion returns the (non-empty) Go version at which the requirements -// in modFile are intepreted, or the latest Go version if modFile is nil. -func modFileGoVersion() string { +// in modFile are interpreted, or the latest Go version if modFile is nil. +func modFileGoVersion(modFile *modfile.File) string { if modFile == nil { return LatestGoVersion() } @@ -71,9 +88,9 @@ func modFileGoVersion() string { // has been erroneously hand-edited. // // The semantics of the go.mod file are more-or-less the same from Go 1.11 - // through Go 1.16, changing at 1.17 for lazy loading. So even though a - // go.mod file without a 'go' directive is theoretically a Go 1.11 file, - // scripts may assume that it ends up as a Go 1.16 module. + // through Go 1.16, changing at 1.17 to support module graph pruning. + // So even though a go.mod file without a 'go' directive is theoretically a + // Go 1.11 file, scripts may assume that it ends up as a Go 1.16 module. return "1.16" } return modFile.Go.Version @@ -82,39 +99,37 @@ func modFileGoVersion() string { // A modFileIndex is an index of data corresponding to a modFile // at a specific point in time. type modFileIndex struct { - data []byte - dataNeedsFix bool // true if fixVersion applied a change while parsing data - module module.Version - goVersionV string // GoVersion with "v" prefix - require map[module.Version]requireMeta - replace map[module.Version]module.Version - highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements - exclude map[module.Version]bool + data []byte + dataNeedsFix bool // true if fixVersion applied a change while parsing data + module module.Version + goVersionV string // GoVersion with "v" prefix + require map[module.Version]requireMeta + replace map[module.Version]module.Version + exclude map[module.Version]bool } -// index is the index of the go.mod file as of when it was last read or written. -var index *modFileIndex - type requireMeta struct { indirect bool } -// A modDepth indicates which dependencies should be loaded for a go.mod file. -type modDepth uint8 +// A modPruning indicates whether transitive dependencies of Go 1.17 dependencies +// are pruned out of the module subgraph rooted at a given module. +// (See https://golang.org/ref/mod#graph-pruning.) +type modPruning uint8 const ( - lazy modDepth = iota // load dependencies only as needed - eager // load all transitive dependencies eagerly + pruned modPruning = iota // transitive dependencies of modules at go 1.17 and higher are pruned out + unpruned // no transitive dependencies are pruned out + workspace // pruned to the union of modules in the workspace ) -func modDepthFromGoVersion(goVersion string) modDepth { - if !go117EnableLazyLoading { - return eager - } - if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 { - return eager +func pruningForGoVersion(goVersion string) modPruning { + if semver.Compare("v"+goVersion, ExplicitIndirectVersionV) < 0 { + // The go.mod file does not duplicate relevant information about transitive + // dependencies, so they cannot be pruned out. + return unpruned } - return lazy + return pruned } // CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by @@ -137,8 +152,10 @@ var ErrDisallowed = errors.New("disallowed module version") // CheckExclusions returns an error equivalent to ErrDisallowed if module m is // excluded by the main module's go.mod file. func CheckExclusions(ctx context.Context, m module.Version) error { - if index != nil && index.exclude[m] { - return module.VersionError(m, errExcluded) + for _, mainModule := range MainModules.Versions() { + if index := MainModules.Index(mainModule); index != nil && index.exclude[m] { + return module.VersionError(m, errExcluded) + } } return nil } @@ -306,23 +323,77 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string return summary.deprecated, nil } -// Replacement returns the replacement for mod, if any, from go.mod. -// If there is no replacement for mod, Replacement returns -// a module.Version with Path == "". +func replacement(mod module.Version, replace map[module.Version]module.Version) (fromVersion string, to module.Version, ok bool) { + if r, ok := replace[mod]; ok { + return mod.Version, r, true + } + if r, ok := replace[module.Version{Path: mod.Path}]; ok { + return "", r, true + } + return "", module.Version{}, false +} + +// Replacement returns the replacement for mod, if any. If the path in the +// module.Version is relative it's relative to the single main module outside +// workspace mode, or the workspace's directory in workspace mode. func Replacement(mod module.Version) module.Version { - if index != nil { - if r, ok := index.replace[mod]; ok { - return r - } - if r, ok := index.replace[module.Version{Path: mod.Path}]; ok { - return r + foundFrom, found, foundModRoot := "", module.Version{}, "" + if MainModules == nil { + return module.Version{} + } else if MainModules.Contains(mod.Path) && mod.Version == "" { + // Don't replace the workspace version of the main module. + return module.Version{} + } + if _, r, ok := replacement(mod, MainModules.WorkFileReplaceMap()); ok { + return r + } + for _, v := range MainModules.Versions() { + if index := MainModules.Index(v); index != nil { + if from, r, ok := replacement(mod, index.replace); ok { + modRoot := MainModules.ModRoot(v) + if foundModRoot != "" && foundFrom != from && found != r { + base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v", + mod, modFilePath(foundModRoot), modFilePath(modRoot)) + return canonicalizeReplacePath(found, foundModRoot) + } + found, foundModRoot = r, modRoot + } } } - return module.Version{} + return canonicalizeReplacePath(found, foundModRoot) +} + +func replaceRelativeTo() string { + if workFilePath := WorkFilePath(); workFilePath != "" { + return filepath.Dir(workFilePath) + } + return MainModules.ModRoot(MainModules.mustGetSingleMainModule()) +} + +// canonicalizeReplacePath ensures that relative, on-disk, replaced module paths +// are relative to the workspace directory (in workspace mode) or to the module's +// directory (in module mode, as they already are). +func canonicalizeReplacePath(r module.Version, modRoot string) module.Version { + if filepath.IsAbs(r.Path) || r.Version != "" { + return r + } + workFilePath := WorkFilePath() + if workFilePath == "" { + return r + } + abs := filepath.Join(modRoot, r.Path) + if rel, err := filepath.Rel(filepath.Dir(workFilePath), abs); err == nil { + return module.Version{Path: rel, Version: r.Version} + } + // We couldn't make the version's path relative to the workspace's path, + // so just return the absolute path. It's the best we can do. + return module.Version{Path: abs, Version: r.Version} } // resolveReplacement returns the module actually used to load the source code // for m: either m itself, or the replacement for m (iff m is replaced). +// It also returns the modroot of the module providing the replacement if +// one was found. func resolveReplacement(m module.Version) module.Version { if r := Replacement(m); r.Path != "" { return r @@ -330,10 +401,21 @@ func resolveReplacement(m module.Version) module.Version { return m } +func toReplaceMap(replacements []*modfile.Replace) map[module.Version]module.Version { + replaceMap := make(map[module.Version]module.Version, len(replacements)) + for _, r := range replacements { + if prev, dup := replaceMap[r.Old]; dup && prev != r.New { + base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) + } + replaceMap[r.Old] = r.New + } + return replaceMap +} + // indexModFile rebuilds the index of modFile. // If modFile has been changed since it was first read, // modFile.Cleanup must be called before indexModFile. -func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex { +func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex { i := new(modFileIndex) i.data = data i.dataNeedsFix = needsFix @@ -345,12 +427,12 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd i.goVersionV = "" if modFile.Go == nil { - rawGoVersion.Store(Target, "") + rawGoVersion.Store(mod, "") } else { // We're going to use the semver package to compare Go versions, so go ahead // and add the "v" prefix it expects once instead of every time. i.goVersionV = "v" + modFile.Go.Version - rawGoVersion.Store(Target, modFile.Go.Version) + rawGoVersion.Store(mod, modFile.Go.Version) } i.require = make(map[module.Version]requireMeta, len(modFile.Require)) @@ -358,21 +440,7 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd i.require[r.Mod] = requireMeta{indirect: r.Indirect} } - i.replace = make(map[module.Version]module.Version, len(modFile.Replace)) - for _, r := range modFile.Replace { - if prev, dup := i.replace[r.Old]; dup && prev != r.New { - base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New) - } - i.replace[r.Old] = r.New - } - - i.highestReplaced = make(map[string]string) - for _, r := range modFile.Replace { - v, ok := i.highestReplaced[r.Old.Path] - if !ok || semver.Compare(r.Old.Version, v) > 0 { - i.highestReplaced[r.Old.Path] = r.Old.Version - } - } + i.replace = toReplaceMap(modFile.Replace) i.exclude = make(map[module.Version]bool, len(modFile.Exclude)) for _, x := range modFile.Exclude { @@ -465,7 +533,7 @@ var rawGoVersion sync.Map // map[module.Version]string type modFileSummary struct { module module.Version goVersion string - depth modDepth + pruning modPruning require []module.Version retract []retraction deprecated string @@ -490,8 +558,8 @@ type retraction struct { // // The caller must not modify the returned summary. func goModSummary(m module.Version) (*modFileSummary, error) { - if m == Target { - panic("internal error: goModSummary called on the Target module") + if m.Version == "" && !inWorkspaceMode() && MainModules.Contains(m.Path) { + panic("internal error: goModSummary called on a main module") } if cfg.BuildMod == "vendor" { @@ -506,7 +574,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) { // For every module other than the target, // return the full list of modules from modules.txt. - readVendorList() + readVendorList(MainModules.mustGetSingleMainModule()) // We don't know what versions the vendored module actually relies on, // so assume that it requires everything. @@ -515,7 +583,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) { } actual := resolveReplacement(m) - if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" { + if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" { key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"} if !modfetch.HaveSum(key) { suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path) @@ -553,27 +621,29 @@ func goModSummary(m module.Version) (*modFileSummary, error) { } } - if index != nil && len(index.exclude) > 0 { - // Drop any requirements on excluded versions. - // Don't modify the cached summary though, since we might need the raw - // summary separately. - haveExcludedReqs := false - for _, r := range summary.require { - if index.exclude[r] { - haveExcludedReqs = true - break - } - } - if haveExcludedReqs { - s := new(modFileSummary) - *s = *summary - s.require = make([]module.Version, 0, len(summary.require)) + for _, mainModule := range MainModules.Versions() { + if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 { + // Drop any requirements on excluded versions. + // Don't modify the cached summary though, since we might need the raw + // summary separately. + haveExcludedReqs := false for _, r := range summary.require { - if !index.exclude[r] { - s.require = append(s.require, r) + if index.exclude[r] { + haveExcludedReqs = true + break } } - summary = s + if haveExcludedReqs { + s := new(modFileSummary) + *s = *summary + s.require = make([]module.Version, 0, len(summary.require)) + for _, r := range summary.require { + if !index.exclude[r] { + s.require = append(s.require, r) + } + } + summary = s + } } } return summary, nil @@ -584,16 +654,20 @@ func goModSummary(m module.Version) (*modFileSummary, error) { // its dependencies. // // rawGoModSummary cannot be used on the Target module. + func rawGoModSummary(m module.Version) (*modFileSummary, error) { - if m == Target { + if m.Path == "" && MainModules.Contains(m.Path) { panic("internal error: rawGoModSummary called on the Target module") } + type key struct { + m module.Version + } type cached struct { summary *modFileSummary err error } - c := rawGoModSummaryCache.Do(m, func() interface{} { + c := rawGoModSummaryCache.Do(key{m}, func() any { summary := new(modFileSummary) name, data, err := rawGoModData(m) if err != nil { @@ -610,9 +684,9 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { if f.Go != nil && f.Go.Version != "" { rawGoVersion.LoadOrStore(m, f.Go.Version) summary.goVersion = f.Go.Version - summary.depth = modDepthFromGoVersion(f.Go.Version) + summary.pruning = pruningForGoVersion(f.Go.Version) } else { - summary.depth = eager + summary.pruning = unpruned } if len(f.Require) > 0 { summary.require = make([]module.Version, 0, len(f.Require)) @@ -648,9 +722,14 @@ var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result func rawGoModData(m module.Version) (name string, data []byte, err error) { if m.Version == "" { // m is a replacement module with only a file path. + dir := m.Path if !filepath.IsAbs(dir) { - dir = filepath.Join(ModRoot(), dir) + if inWorkspaceMode() && MainModules.Contains(m.Path) { + dir = MainModules.ModRoot(m) + } else { + dir = filepath.Join(replaceRelativeTo(), dir) + } } name = filepath.Join(dir, "go.mod") if gomodActual, ok := fsys.OverlayPath(name); ok { @@ -690,7 +769,7 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la latest module.Version err error } - e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} { + e := latestVersionIgnoringRetractionsCache.Do(path, func() any { ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path) defer span.Done() @@ -718,3 +797,15 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la } var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result + +// ToDirectoryPath adds a prefix if necessary so that path in unambiguously +// an absolute path or a relative path starting with a '.' or '..' +// path component. +func ToDirectoryPath(path string) string { + if path == "." || modfile.IsDirectoryPath(path) { + return path + } + // The path is not a relative path or an absolute path, so make it relative + // to the current directory. + return "./" + filepath.ToSlash(filepath.Clean(path)) +} diff --git a/src/cmd/go/internal/modload/mvs.go b/src/cmd/go/internal/modload/mvs.go index 87619b4ace68e2..ea1c21b4f18d9d 100644 --- a/src/cmd/go/internal/modload/mvs.go +++ b/src/cmd/go/internal/modload/mvs.go @@ -11,6 +11,7 @@ import ( "sort" "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" "golang.org/x/mod/module" "golang.org/x/mod/semver" @@ -42,7 +43,7 @@ type mvsReqs struct { } func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) { - if mod == Target { + if mod.Version == "" && MainModules.Contains(mod.Path) { // Use the build list as it existed when r was constructed, not the current // global build list. return r.roots, nil @@ -78,11 +79,10 @@ func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) { return m, nil } -func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string, error) { +func versions(ctx context.Context, path string, allowed AllowedFunc) (versions []string, origin *codehost.Origin, err error) { // Note: modfetch.Lookup and repo.Versions are cached, // so there's no need for us to add extra caching here. - var versions []string - err := modfetch.TryProxies(func(proxy string) error { + err = modfetch.TryProxies(func(proxy string) error { repo, err := lookupRepo(proxy, path) if err != nil { return err @@ -91,8 +91,8 @@ func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string, if err != nil { return err } - allowedVersions := make([]string, 0, len(allVersions)) - for _, v := range allVersions { + allowedVersions := make([]string, 0, len(allVersions.List)) + for _, v := range allVersions.List { if err := allowed(ctx, module.Version{Path: path, Version: v}); err == nil { allowedVersions = append(allowedVersions, v) } else if !errors.Is(err, ErrDisallowed) { @@ -100,24 +100,25 @@ func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string, } } versions = allowedVersions + origin = allVersions.Origin return nil }) - return versions, err + return versions, origin, err } // previousVersion returns the tagged version of m.Path immediately prior to // m.Version, or version "none" if no prior version is tagged. // -// Since the version of Target is not found in the version list, +// Since the version of a main module is not found in the version list, // it has no previous version. func previousVersion(m module.Version) (module.Version, error) { // TODO(golang.org/issue/38714): thread tracing context through MVS. - if m == Target { + if m.Version == "" && MainModules.Contains(m.Path) { return module.Version{Path: m.Path, Version: "none"}, nil } - list, err := versions(context.TODO(), m.Path, CheckAllowed) + list, _, err := versions(context.TODO(), m.Path, CheckAllowed) if err != nil { if errors.Is(err, os.ErrNotExist) { return module.Version{Path: m.Path, Version: "none"}, nil diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index e737ca90fcd79d..9f9674c26be47e 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -20,6 +20,8 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/imports" "cmd/go/internal/modfetch" + "cmd/go/internal/modfetch/codehost" + "cmd/go/internal/modinfo" "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" @@ -32,21 +34,28 @@ import ( // The module must be a complete module path. // The version must take one of the following forms: // -// - the literal string "latest", denoting the latest available, allowed -// tagged version, with non-prereleases preferred over prereleases. -// If there are no tagged versions in the repo, latest returns the most -// recent commit. -// - the literal string "upgrade", equivalent to "latest" except that if -// current is a newer version, current will be returned (see below). -// - the literal string "patch", denoting the latest available tagged version -// with the same major and minor number as current (see below). -// - v1, denoting the latest available tagged version v1.x.x. -// - v1.2, denoting the latest available tagged version v1.2.x. -// - v1.2.3, a semantic version string denoting that tagged version. -// - v1.2.3, >=v1.2.3, -// denoting the version closest to the target and satisfying the given operator, -// with non-prereleases preferred over prereleases. -// - a repository commit identifier or tag, denoting that commit. +// - the literal string "latest", denoting the latest available, allowed +// tagged version, with non-prereleases preferred over prereleases. +// If there are no tagged versions in the repo, latest returns the most +// recent commit. +// +// - the literal string "upgrade", equivalent to "latest" except that if +// current is a newer version, current will be returned (see below). +// +// - the literal string "patch", denoting the latest available tagged version +// with the same major and minor number as current (see below). +// +// - v1, denoting the latest available tagged version v1.x.x. +// +// - v1.2, denoting the latest available tagged version v1.2.x. +// +// - v1.2.3, a semantic version string denoting that tagged version. +// +// - v1.2.3, >=v1.2.3, +// denoting the version closest to the target and satisfying the given operator, +// with non-prereleases preferred over prereleases. +// +// - a repository commit identifier or tag, denoting that commit. // // current denotes the currently-selected version of the module; it may be // "none" if no version is currently selected, or "" if the currently-selected @@ -65,15 +74,39 @@ import ( // // If path is the path of the main module and the query is "latest", // Query returns Target.Version as the version. +// +// Query often returns a non-nil *RevInfo with a non-nil error, +// to provide an info.Origin that can allow the error to be cached. func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) { + ctx, span := trace.StartSpan(ctx, "modload.Query "+path) + defer span.Done() + + return queryReuse(ctx, path, query, current, allowed, nil) +} + +// queryReuse is like Query but also takes a map of module info that can be reused +// if the validation criteria in Origin are met. +func queryReuse(ctx context.Context, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) { var info *modfetch.RevInfo err := modfetch.TryProxies(func(proxy string) (err error) { - info, err = queryProxy(ctx, proxy, path, query, current, allowed) + info, err = queryProxy(ctx, proxy, path, query, current, allowed, reuse) return err }) return info, err } +// checkReuse checks whether a revision of a given module or a version list +// for a given module may be reused, according to the information in origin. +func checkReuse(ctx context.Context, path string, old *codehost.Origin) error { + return modfetch.TryProxies(func(proxy string) error { + repo, err := lookupRepo(proxy, path) + if err != nil { + return err + } + return repo.CheckReuse(old) + }) +} + // AllowedFunc is used by Query and other functions to filter out unsuitable // versions, for example, those listed in exclude directives in the main // module's go.mod file. @@ -96,7 +129,7 @@ func (queryDisabledError) Error() string { return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason) } -func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) { +func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) { ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query) defer span.Done() @@ -110,11 +143,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed allowed = func(context.Context, module.Version) error { return nil } } - if path == Target.Path && (query == "upgrade" || query == "patch") { - if err := allowed(ctx, Target); err != nil { + if MainModules.Contains(path) && (query == "upgrade" || query == "patch") { + m := module.Version{Path: path} + if err := allowed(ctx, m); err != nil { return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err) } - return &modfetch.RevInfo{Version: Target.Version}, nil + return &modfetch.RevInfo{Version: m.Version}, nil } if path == "std" || path == "cmd" { @@ -126,6 +160,19 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed return nil, err } + if old := reuse[module.Version{Path: path, Version: query}]; old != nil { + if err := repo.CheckReuse(old.Origin); err == nil { + info := &modfetch.RevInfo{ + Version: old.Version, + Origin: old.Origin, + } + if old.Time != nil { + info.Time = *old.Time + } + return info, nil + } + } + // Parse query to detect parse errors (and possibly handle query) // before any network I/O. qm, err := newQueryMatcher(path, query, current, allowed) @@ -150,7 +197,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed } } if err != nil { - return nil, queryErr + return info, queryErr } } if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) { @@ -166,15 +213,34 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed if err != nil { return nil, err } - releases, prereleases, err := qm.filterVersions(ctx, versions) + revErr := &modfetch.RevInfo{Origin: versions.Origin} // RevInfo to return with error + + releases, prereleases, err := qm.filterVersions(ctx, versions.List) if err != nil { - return nil, err + return revErr, err + } + + mergeRevOrigin := func(rev *modfetch.RevInfo, origin *codehost.Origin) *modfetch.RevInfo { + merged := mergeOrigin(rev.Origin, origin) + if merged == rev.Origin { + return rev + } + clone := new(modfetch.RevInfo) + *clone = *rev + clone.Origin = merged + return clone } lookup := func(v string) (*modfetch.RevInfo, error) { rev, err := repo.Stat(v) + // Stat can return a non-nil rev and a non-nil err, + // in order to provide origin information to make the error cacheable. + if rev == nil && err != nil { + return revErr, err + } + rev = mergeRevOrigin(rev, versions.Origin) if err != nil { - return nil, err + return rev, err } if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() { @@ -199,9 +265,14 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed currentTime, err := module.PseudoVersionTime(current) if err == nil && rev.Time.Before(currentTime) { if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) { - return nil, err + return revErr, err + } + rev, err = repo.Stat(current) + if rev == nil && err != nil { + return revErr, err } - return repo.Stat(current) + rev = mergeRevOrigin(rev, versions.Origin) + return rev, err } } @@ -231,7 +302,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed return lookup(latest.Version) } } else if !errors.Is(err, fs.ErrNotExist) { - return nil, err + return revErr, err } } @@ -243,7 +314,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed return lookup(current) } - return nil, &NoMatchingVersionError{query: query, current: current} + return revErr, &NoMatchingVersionError{query: query, current: current} } // IsRevisionQuery returns true if vers is a version query that may refer to @@ -432,9 +503,9 @@ func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool { // filterVersions classifies versions into releases and pre-releases, filtering // out: -// 1. versions that do not satisfy the 'allowed' predicate, and -// 2. "+incompatible" versions, if a compatible one satisfies the predicate -// and the incompatible version is not preferred. +// 1. versions that do not satisfy the 'allowed' predicate, and +// 2. "+incompatible" versions, if a compatible one satisfies the predicate +// and the incompatible version is not preferred. // // If the allowed predicate returns an error not equivalent to ErrDisallowed, // filterVersions returns that error. @@ -512,9 +583,10 @@ func QueryPackages(ctx context.Context, pattern, query string, current func(stri pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed) if len(pkgMods) == 0 && err == nil { + replacement := Replacement(modOnly.Mod) return nil, &PackageNotInModuleError{ Mod: modOnly.Mod, - Replacement: Replacement(modOnly.Mod), + Replacement: replacement, Query: query, Pattern: pattern, } @@ -551,7 +623,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return m.Errs[0] } - var match func(mod module.Version, root string, isLocal bool) *search.Match + var match func(mod module.Version, roots []string, isLocal bool) *search.Match matchPattern := search.MatchPattern(pattern) if i := strings.Index(pattern, "..."); i >= 0 { @@ -559,30 +631,32 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if base == "." { return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query} } - match = func(mod module.Version, root string, isLocal bool) *search.Match { + match = func(mod module.Version, roots []string, isLocal bool) *search.Match { m := search.NewMatch(pattern) matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod}) return m } } else { - match = func(mod module.Version, root string, isLocal bool) *search.Match { + match = func(mod module.Version, roots []string, isLocal bool) *search.Match { m := search.NewMatch(pattern) prefix := mod.Path - if mod == Target { - prefix = targetPrefix + if MainModules.Contains(mod.Path) { + prefix = MainModules.PathPrefix(module.Version{Path: mod.Path}) } - if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil { - m.AddError(err) - } else if ok { - m.Pkgs = []string{pattern} + for _, root := range roots { + if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil { + m.AddError(err) + } else if ok { + m.Pkgs = []string{pattern} + } } return m } } - var queryMatchesMainModule bool - if HasModRoot() { - m := match(Target, modRoot, true) + var mainModuleMatches []module.Version + for _, mainModule := range MainModules.Versions() { + m := match(mainModule, modRoots, true) if len(m.Pkgs) > 0 { if query != "upgrade" && query != "patch" { return nil, nil, &QueryMatchesPackagesInMainModuleError{ @@ -591,12 +665,12 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin Packages: m.Pkgs, } } - if err := allowed(ctx, Target); err != nil { - return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err) + if err := allowed(ctx, mainModule); err != nil { + return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err) } return []QueryResult{{ - Mod: Target, - Rev: &modfetch.RevInfo{Version: Target.Version}, + Mod: mainModule, + Rev: &modfetch.RevInfo{Version: mainModule.Version}, Packages: m.Pkgs, }}, nil, nil } @@ -604,15 +678,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return nil, nil, err } - if matchPattern(Target.Path) { - queryMatchesMainModule = true + var matchesMainModule bool + if matchPattern(mainModule.Path) { + mainModuleMatches = append(mainModuleMatches, mainModule) + matchesMainModule = true } - if (query == "upgrade" || query == "patch") && queryMatchesMainModule { - if err := allowed(ctx, Target); err == nil { + if (query == "upgrade" || query == "patch") && matchesMainModule { + if err := allowed(ctx, mainModule); err == nil { modOnly = &QueryResult{ - Mod: Target, - Rev: &modfetch.RevInfo{Version: Target.Version}, + Mod: mainModule, + Rev: &modfetch.RevInfo{Version: mainModule.Version}, } } } @@ -625,16 +701,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if len(candidateModules) == 0 { if modOnly != nil { return nil, modOnly, nil - } else if queryMatchesMainModule { - return nil, nil, &QueryMatchesMainModuleError{ - Pattern: pattern, - Query: query, + } else if len(mainModuleMatches) != 0 { + return nil, nil, &QueryMatchesMainModulesError{ + MainModules: mainModuleMatches, + Pattern: pattern, + Query: query, } } else { return nil, nil, &PackageNotInModuleError{ - Mod: Target, - Query: query, - Pattern: pattern, + MainModules: mainModuleMatches, + Query: query, + Pattern: pattern, } } } @@ -646,7 +723,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin pathCurrent := current(path) r.Mod.Path = path - r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed) + r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil) if err != nil { return r, err } @@ -656,15 +733,16 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin if err != nil { return r, err } - m := match(r.Mod, root, isLocal) + m := match(r.Mod, []string{root}, isLocal) r.Packages = m.Pkgs if len(r.Packages) == 0 && !matchPattern(path) { if err := firstError(m); err != nil { return r, err } + replacement := Replacement(r.Mod) return r, &PackageNotInModuleError{ Mod: r.Mod, - Replacement: Replacement(r.Mod), + Replacement: replacement, Query: query, Pattern: pattern, } @@ -684,8 +762,8 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin return err }) - if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) { - return nil, nil, &QueryMatchesMainModuleError{ + if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) { + return nil, nil, &QueryMatchesMainModulesError{ Pattern: pattern, Query: query, } @@ -701,8 +779,13 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin func modulePrefixesExcludingTarget(path string) []string { prefixes := make([]string, 0, strings.Count(path, "/")+1) + mainModulePrefixes := make(map[string]bool) + for _, m := range MainModules.Versions() { + mainModulePrefixes[m.Path] = true + } + for { - if path != targetPrefix { + if !mainModulePrefixes[path] { if _, _, ok := module.SplitPathVersion(path); ok { prefixes = append(prefixes, path) } @@ -759,7 +842,7 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod case *PackageNotInModuleError: // Given the option, prefer to attribute “package not in module” // to modules other than the main one. - if noPackage == nil || noPackage.Mod == Target { + if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) { noPackage = rErr } case *NoMatchingVersionError: @@ -878,6 +961,7 @@ func (e *WildcardInFirstElementError) Error() string { // code for the versions it knows about, and thus did not have the opportunity // to return a non-400 status code to suppress fallback. type PackageNotInModuleError struct { + MainModules []module.Version Mod module.Version Replacement module.Version Query string @@ -885,11 +969,15 @@ type PackageNotInModuleError struct { } func (e *PackageNotInModuleError) Error() string { - if e.Mod == Target { + if len(e.MainModules) > 0 { + prefix := "workspace modules do" + if len(e.MainModules) == 1 { + prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0]) + } if strings.Contains(e.Pattern, "...") { - return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern) + return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern) } - return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern) + return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern) } found := "" @@ -963,7 +1051,8 @@ func versionHasGoMod(_ context.Context, m module.Version) (bool, error) { // available versions, but cannot fetch specific source files. type versionRepo interface { ModulePath() string - Versions(prefix string) ([]string, error) + CheckReuse(*codehost.Origin) error + Versions(prefix string) (*modfetch.Versions, error) Stat(rev string) (*modfetch.RevInfo, error) Latest() (*modfetch.RevInfo, error) } @@ -978,14 +1067,13 @@ func lookupRepo(proxy, path string) (repo versionRepo, err error) { repo = emptyRepo{path: path, err: err} } - if index == nil { - return repo, err - } - if _, ok := index.highestReplaced[path]; !ok { + if MainModules == nil { return repo, err + } else if _, ok := MainModules.HighestReplaced()[path]; ok { + return &replacementRepo{repo: repo}, nil } - return &replacementRepo{repo: repo}, nil + return repo, err } // An emptyRepo is a versionRepo that contains no versions. @@ -996,8 +1084,13 @@ type emptyRepo struct { var _ versionRepo = emptyRepo{} -func (er emptyRepo) ModulePath() string { return er.path } -func (er emptyRepo) Versions(prefix string) ([]string, error) { return nil, nil } +func (er emptyRepo) ModulePath() string { return er.path } +func (er emptyRepo) CheckReuse(old *codehost.Origin) error { + return fmt.Errorf("empty repo") +} +func (er emptyRepo) Versions(prefix string) (*modfetch.Versions, error) { + return &modfetch.Versions{}, nil +} func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err } func (er emptyRepo) Latest() (*modfetch.RevInfo, error) { return nil, er.err } @@ -1015,38 +1108,56 @@ var _ versionRepo = (*replacementRepo)(nil) func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() } +func (rr *replacementRepo) CheckReuse(old *codehost.Origin) error { + return fmt.Errorf("replacement repo") +} + // Versions returns the versions from rr.repo augmented with any matching // replacement versions. -func (rr *replacementRepo) Versions(prefix string) ([]string, error) { +func (rr *replacementRepo) Versions(prefix string) (*modfetch.Versions, error) { repoVersions, err := rr.repo.Versions(prefix) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, err + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + return nil, err + } + repoVersions = new(modfetch.Versions) } - versions := repoVersions - if index != nil && len(index.replace) > 0 { - path := rr.ModulePath() - for m, _ := range index.replace { - if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) { - versions = append(versions, m.Version) + versions := repoVersions.List + for _, mm := range MainModules.Versions() { + if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 { + path := rr.ModulePath() + for m, _ := range index.replace { + if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) { + versions = append(versions, m.Version) + } } } } - if len(versions) == len(repoVersions) { // No replacement versions added. - return versions, nil + if len(versions) == len(repoVersions.List) { // replacement versions added + return repoVersions, nil } sort.Slice(versions, func(i, j int) bool { return semver.Compare(versions[i], versions[j]) < 0 }) str.Uniq(&versions) - return versions, nil + return &modfetch.Versions{List: versions}, nil } func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) { info, err := rr.repo.Stat(rev) - if err == nil || index == nil || len(index.replace) == 0 { + if err == nil { + return info, err + } + var hasReplacements bool + for _, v := range MainModules.Versions() { + if index := MainModules.Index(v); index != nil && len(index.replace) > 0 { + hasReplacements = true + } + } + if !hasReplacements { return info, err } @@ -1073,26 +1184,24 @@ func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) { func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) { info, err := rr.repo.Latest() + path := rr.ModulePath() - if index != nil { - path := rr.ModulePath() - if v, ok := index.highestReplaced[path]; ok { - if v == "" { - // The only replacement is a wildcard that doesn't specify a version, so - // synthesize a pseudo-version with an appropriate major version and a - // timestamp below any real timestamp. That way, if the main module is - // used from within some other module, the user will be able to upgrade - // the requirement to any real version they choose. - if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 { - v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") - } else { - v = module.PseudoVersion("v0", "", time.Time{}, "000000000000") - } + if v, ok := MainModules.HighestReplaced()[path]; ok { + if v == "" { + // The only replacement is a wildcard that doesn't specify a version, so + // synthesize a pseudo-version with an appropriate major version and a + // timestamp below any real timestamp. That way, if the main module is + // used from within some other module, the user will be able to upgrade + // the requirement to any real version they choose. + if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 { + v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000") + } else { + v = module.PseudoVersion("v0", "", time.Time{}, "000000000000") } + } - if err != nil || semver.Compare(v, info.Version) > 0 { - return rr.replacementStat(v) - } + if err != nil || semver.Compare(v, info.Version) > 0 { + return rr.replacementStat(v) } } @@ -1108,20 +1217,46 @@ func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) return rev, nil } -// A QueryMatchesMainModuleError indicates that a query requests +// A QueryMatchesMainModulesError indicates that a query requests // a version of the main module that cannot be satisfied. // (The main module's version cannot be changed.) -type QueryMatchesMainModuleError struct { - Pattern string - Query string +type QueryMatchesMainModulesError struct { + MainModules []module.Version + Pattern string + Query string } -func (e *QueryMatchesMainModuleError) Error() string { - if e.Pattern == Target.Path { +func (e *QueryMatchesMainModulesError) Error() string { + if MainModules.Contains(e.Pattern) { return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern) } - return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path) + plural := "" + mainModulePaths := make([]string, len(e.MainModules)) + for i := range e.MainModules { + mainModulePaths[i] = e.MainModules[i].Path + } + if len(e.MainModules) > 1 { + plural = "s" + } + return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", ")) +} + +// A QueryUpgradesAllError indicates that a query requests +// an upgrade on the all pattern. +// (The main module's version cannot be changed.) +type QueryUpgradesAllError struct { + MainModules []module.Version + Query string +} + +func (e *QueryUpgradesAllError) Error() string { + var plural string = "" + if len(e.MainModules) != 1 { + plural = "s" + } + + return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural) } // A QueryMatchesPackagesInMainModuleError indicates that a query cannot be diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go index 658fc6f55a9c9b..b2ac7f22b1ea9a 100644 --- a/src/cmd/go/internal/modload/search.go +++ b/src/cmd/go/internal/modload/search.go @@ -6,16 +6,24 @@ package modload import ( "context" + "errors" "fmt" "io/fs" "os" + "path" "path/filepath" + "runtime" + "sort" "strings" + "sync" "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/imports" + "cmd/go/internal/modindex" + "cmd/go/internal/par" "cmd/go/internal/search" + "cmd/go/internal/trace" "golang.org/x/mod/module" ) @@ -31,6 +39,9 @@ const ( // a global) for tags, can include or exclude packages in the standard library, // and is restricted to the given list of modules. func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, filter stdFilter, modules []module.Version) { + ctx, span := trace.StartSpan(ctx, "modload.matchPackages") + defer span.Done() + m.Pkgs = []string{} isMatch := func(string) bool { return true } @@ -40,9 +51,15 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f treeCanMatch = search.TreeCanMatchPattern(m.Pattern()) } + var mu sync.Mutex have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } + addPkg := func(p string) { + mu.Lock() + m.Pkgs = append(m.Pkgs, p) + mu.Unlock() + } if !cfg.BuildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } @@ -53,7 +70,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f pruneGoMod ) + q := par.NewQueue(runtime.GOMAXPROCS(0)) + walkPkgs := func(root, importPathRoot string, prune pruning) { + _, span := trace.StartSpan(ctx, "walkPkgs "+root) + defer span.Done() + root = filepath.Clean(root) err := fsys.Walk(root, func(path string, fi fs.FileInfo, err error) error { if err != nil { @@ -107,9 +129,11 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f if !have[name] { have[name] = true if isMatch(name) { - if _, _, err := scanDir(path, tags); err != imports.ErrNoGo { - m.Pkgs = append(m.Pkgs, name) - } + q.Add(func() { + if _, _, err := scanDir(root, path, tags); err != imports.ErrNoGo { + addPkg(name) + } + }) } } @@ -123,6 +147,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f } } + // Wait for all in-flight operations to complete before returning. + defer func() { + <-q.Idle() + sort.Strings(m.Pkgs) // sort everything we added for determinism + }() + if filter == includeStd { walkPkgs(cfg.GOROOTsrc, "", pruneGoMod) if treeCanMatch("cmd") { @@ -131,9 +161,10 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f } if cfg.BuildMod == "vendor" { - if HasModRoot() { - walkPkgs(ModRoot(), targetPrefix, pruneGoMod|pruneVendor) - walkPkgs(filepath.Join(ModRoot(), "vendor"), "", pruneVendor) + mod := MainModules.mustGetSingleMainModule() + if modRoot := MainModules.ModRoot(mod); modRoot != "" { + walkPkgs(modRoot, MainModules.PathPrefix(mod), pruneGoMod|pruneVendor) + walkPkgs(filepath.Join(modRoot, "vendor"), "", pruneVendor) } return } @@ -147,12 +178,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f root, modPrefix string isLocal bool ) - if mod == Target { - if !HasModRoot() { + if MainModules.Contains(mod.Path) { + if MainModules.ModRoot(mod) == "" { continue // If there is no main module, we can't search in it. } - root = ModRoot() - modPrefix = targetPrefix + root = MainModules.ModRoot(mod) + modPrefix = MainModules.PathPrefix(mod) isLocal = true } else { var err error @@ -164,6 +195,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f } modPrefix = mod.Path } + if mi, err := modindex.GetModule(root); err == nil { + walkFromIndex(mi, modPrefix, isMatch, treeCanMatch, tags, have, addPkg) + continue + } else if !errors.Is(err, modindex.ErrNotIndexed) { + m.AddError(err) + } prune := pruneVendor if isLocal { @@ -175,6 +212,53 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f return } +// walkFromIndex matches packages in a module using the module index. modroot +// is the module's root directory on disk, index is the modindex.Module for the +// module, and importPathRoot is the module's path prefix. +func walkFromIndex(index *modindex.Module, importPathRoot string, isMatch, treeCanMatch func(string) bool, tags, have map[string]bool, addPkg func(string)) { + index.Walk(func(reldir string) { + // Avoid .foo, _foo, and testdata subdirectory trees. + p := reldir + for { + elem, rest, found := strings.Cut(p, string(filepath.Separator)) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { + return + } + if found && elem == "vendor" { + // Ignore this path if it contains the element "vendor" anywhere + // except for the last element (packages named vendor are allowed + // for historical reasons). Note that found is true when this + // isn't the last path element. + return + } + if !found { + // Didn't find the separator, so we're considering the last element. + break + } + p = rest + } + + // Don't use GOROOT/src. + if reldir == "" && importPathRoot == "" { + return + } + + name := path.Join(importPathRoot, filepath.ToSlash(reldir)) + if !treeCanMatch(name) { + return + } + + if !have[name] { + have[name] = true + if isMatch(name) { + if _, _, err := index.Package(reldir).ScanDir(tags); err != imports.ErrNoGo { + addPkg(name) + } + } + } + }) +} + // MatchInModule identifies the packages matching the given pattern within the // given module version, which does not need to be in the build list or module // requirement graph. @@ -207,7 +291,7 @@ func MatchInModule(ctx context.Context, pattern string, m module.Version, tags m return match } if haveGoFiles { - if _, _, err := scanDir(dir, tags); err != imports.ErrNoGo { + if _, _, err := scanDir(root, dir, tags); err != imports.ErrNoGo { // ErrNoGo indicates that the directory is not actually a Go package, // perhaps due to the tags in use. Any other non-nil error indicates a // problem with one or more of the Go source files, but such an error does diff --git a/src/cmd/go/internal/modload/stat_openfile.go b/src/cmd/go/internal/modload/stat_openfile.go index 368f8931984929..5773073d903554 100644 --- a/src/cmd/go/internal/modload/stat_openfile.go +++ b/src/cmd/go/internal/modload/stat_openfile.go @@ -3,13 +3,12 @@ // license that can be found in the LICENSE file. //go:build (js && wasm) || plan9 -// +build js,wasm plan9 // On plan9, per http://9p.io/magic/man2html/2/access: “Since file permissions // are checked by the server and group information is not known to the client, // access must open the file to check permissions.” // -// aix and js,wasm are similar, in that they do not define syscall.Access. +// js,wasm is similar, in that it does not define syscall.Access. package modload diff --git a/src/cmd/go/internal/modload/stat_unix.go b/src/cmd/go/internal/modload/stat_unix.go index e079d7399026fe..a0d5f4d2476ed1 100644 --- a/src/cmd/go/internal/modload/stat_unix.go +++ b/src/cmd/go/internal/modload/stat_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package modload diff --git a/src/cmd/go/internal/modload/stat_windows.go b/src/cmd/go/internal/modload/stat_windows.go index 825e60b27af894..f29a99165e5300 100644 --- a/src/cmd/go/internal/modload/stat_windows.go +++ b/src/cmd/go/internal/modload/stat_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package modload diff --git a/src/cmd/go/internal/modload/vendor.go b/src/cmd/go/internal/modload/vendor.go index 80713b0812eacc..5ea82a862083fd 100644 --- a/src/cmd/go/internal/modload/vendor.go +++ b/src/cmd/go/internal/modload/vendor.go @@ -15,6 +15,7 @@ import ( "cmd/go/internal/base" + "golang.org/x/mod/modfile" "golang.org/x/mod/module" "golang.org/x/mod/semver" ) @@ -35,13 +36,13 @@ type vendorMetadata struct { } // readVendorList reads the list of vendored modules from vendor/modules.txt. -func readVendorList() { +func readVendorList(mainModule module.Version) { vendorOnce.Do(func() { vendorList = nil vendorPkgModule = make(map[string]module.Version) vendorVersion = make(map[string]string) vendorMeta = make(map[module.Version]vendorMetadata) - data, err := os.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt")) + data, err := os.ReadFile(filepath.Join(MainModules.ModRoot(mainModule), "vendor/modules.txt")) if err != nil { if !errors.Is(err, fs.ErrNotExist) { base.Fatalf("go: %s", err) @@ -134,8 +135,8 @@ func readVendorList() { // checkVendorConsistency verifies that the vendor/modules.txt file matches (if // go 1.14) or at least does not contradict (go 1.13 or earlier) the // requirements and replacements listed in the main module's go.mod file. -func checkVendorConsistency() { - readVendorList() +func checkVendorConsistency(index *modFileIndex, modFile *modfile.File) { + readVendorList(MainModules.mustGetSingleMainModule()) pre114 := false if semver.Compare(index.goVersionV, "v1.14") < 0 { @@ -146,7 +147,7 @@ func checkVendorConsistency() { } vendErrors := new(strings.Builder) - vendErrorf := func(mod module.Version, format string, args ...interface{}) { + vendErrorf := func(mod module.Version, format string, args ...any) { detail := fmt.Sprintf(format, args...) if mod.Version == "" { fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail) @@ -219,6 +220,7 @@ func checkVendorConsistency() { } if vendErrors.Len() > 0 { + modRoot := MainModules.ModRoot(MainModules.mustGetSingleMainModule()) base.Fatalf("go: inconsistent vendoring in %s:%s\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor", modRoot, vendErrors) } } diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go index 6969f90f2e681a..a1b51557a3519c 100644 --- a/src/cmd/go/internal/mvs/mvs.go +++ b/src/cmd/go/internal/mvs/mvs.go @@ -8,6 +8,7 @@ package mvs import ( "fmt" + "reflect" "sort" "sync" @@ -85,11 +86,11 @@ type DowngradeReqs interface { // of the list are sorted by path. // // See https://research.swtch.com/vgo-mvs for details. -func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) { - return buildList(target, reqs, nil) +func BuildList(targets []module.Version, reqs Reqs) ([]module.Version, error) { + return buildList(targets, reqs, nil) } -func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) { +func buildList(targets []module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) { cmp := func(v1, v2 string) int { if reqs.Max(v1, v2) != v1 { return -1 @@ -102,7 +103,7 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m var ( mu sync.Mutex - g = NewGraph(cmp, []module.Version{target}) + g = NewGraph(cmp, targets) upgrades = map[module.Version]module.Version{} errs = map[module.Version]error{} // (non-nil errors only) ) @@ -110,8 +111,10 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // Explore work graph in parallel in case reqs.Required // does high-latency network operations. var work par.Work - work.Add(target) - work.Do(10, func(item interface{}) { + for _, target := range targets { + work.Add(target) + } + work.Do(10, func(item any) { m := item.(module.Version) var required []module.Version @@ -168,12 +171,12 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // The final list is the minimum version of each module found in the graph. list := g.BuildList() - if v := list[0]; v != target { + if vs := list[:len(targets)]; !reflect.DeepEqual(vs, targets) { // target.Version will be "" for modload, the main client of MVS. // "" denotes the main module, which has no version. However, MVS treats // version strings as opaque, so "" is not a special value here. // See golang.org/issue/31491, golang.org/issue/29773. - panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target)) + panic(fmt.Sprintf("mistake: chose versions %+v instead of targets %+v", vs, targets)) } return list, nil } @@ -181,8 +184,8 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m // Req returns the minimal requirement list for the target module, // with the constraint that all module paths listed in base must // appear in the returned list. -func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, error) { - list, err := BuildList(target, reqs) +func Req(mainModule module.Version, base []string, reqs Reqs) ([]module.Version, error) { + list, err := BuildList([]module.Version{mainModule}, reqs) if err != nil { return nil, err } @@ -191,10 +194,16 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err // that list came from a previous operation that paged // in all the requirements, so there's no I/O to overlap now. + max := map[string]string{} + for _, m := range list { + max[m.Path] = m.Version + } + // Compute postorder, cache requirements. var postorder []module.Version reqCache := map[module.Version][]module.Version{} - reqCache[target] = nil + reqCache[mainModule] = nil + var walk func(module.Version) error walk = func(m module.Version) error { _, ok := reqCache[m] @@ -232,14 +241,6 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err } return nil } - max := map[string]string{} - for _, m := range list { - if v, ok := max[m.Path]; ok { - max[m.Path] = reqs.Max(m.Version, v) - } else { - max[m.Path] = m.Version - } - } // First walk the base modules that must be listed. var min []module.Version haveBase := map[string]bool{} @@ -273,7 +274,7 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err // UpgradeAll returns a build list for the target module // in which every module is upgraded to its latest version. func UpgradeAll(target module.Version, reqs UpgradeReqs) ([]module.Version, error) { - return buildList(target, reqs, func(m module.Version) (module.Version, error) { + return buildList([]module.Version{target}, reqs, func(m module.Version) (module.Version, error) { if m.Path == target.Path { return target, nil } @@ -308,7 +309,7 @@ func Upgrade(target module.Version, reqs UpgradeReqs, upgrade ...module.Version) } } - return buildList(target, &override{target, list, reqs}, func(m module.Version) (module.Version, error) { + return buildList([]module.Version{target}, &override{target, list, reqs}, func(m module.Version) (module.Version, error) { if v, ok := upgradeTo[m.Path]; ok { return module.Version{Path: m.Path, Version: v}, nil } @@ -331,7 +332,7 @@ func Downgrade(target module.Version, reqs DowngradeReqs, downgrade ...module.Ve // // In order to generate those new requirements, we need to identify versions // for every module in the build list — not just reqs.Required(target). - list, err := BuildList(target, reqs) + list, err := BuildList([]module.Version{target}, reqs) if err != nil { return nil, err } @@ -446,7 +447,7 @@ List: // list with the actual versions of the downgraded modules as selected by MVS, // instead of our initial downgrades. // (See the downhiddenartifact and downhiddencross test cases). - actual, err := BuildList(target, &override{ + actual, err := BuildList([]module.Version{target}, &override{ target: target, list: downgraded, Reqs: reqs, @@ -466,7 +467,7 @@ List: } } - return BuildList(target, &override{ + return BuildList([]module.Version{target}, &override{ target: target, list: downgraded, Reqs: reqs, diff --git a/src/cmd/go/internal/mvs/mvs_test.go b/src/cmd/go/internal/mvs/mvs_test.go index 598ed666889517..26d004fee2856e 100644 --- a/src/cmd/go/internal/mvs/mvs_test.go +++ b/src/cmd/go/internal/mvs/mvs_test.go @@ -507,7 +507,7 @@ func Test(t *testing.T) { t.Fatalf("build takes one argument: %q", line) } fns = append(fns, func(t *testing.T) { - list, err := BuildList(m(kf[1]), reqs) + list, err := BuildList([]module.Version{m(kf[1])}, reqs) checkList(t, key, list, err, val) }) continue diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go index 960cec6fb16994..7626251087bd50 100644 --- a/src/cmd/go/internal/par/work.go +++ b/src/cmd/go/internal/par/work.go @@ -14,24 +14,24 @@ import ( // Work manages a set of work items to be executed in parallel, at most once each. // The items in the set must all be valid map keys. type Work struct { - f func(interface{}) // function to run for each item - running int // total number of runners + f func(any) // function to run for each item + running int // total number of runners mu sync.Mutex - added map[interface{}]bool // items added to set - todo []interface{} // items yet to be run - wait sync.Cond // wait when todo is empty - waiting int // number of runners waiting for todo + added map[any]bool // items added to set + todo []any // items yet to be run + wait sync.Cond // wait when todo is empty + waiting int // number of runners waiting for todo } func (w *Work) init() { if w.added == nil { - w.added = make(map[interface{}]bool) + w.added = make(map[any]bool) } } // Add adds item to the work set, if it hasn't already been added. -func (w *Work) Add(item interface{}) { +func (w *Work) Add(item any) { w.mu.Lock() w.init() if !w.added[item] { @@ -51,7 +51,7 @@ func (w *Work) Add(item interface{}) { // before calling Do (or else Do returns immediately), // but it is allowed for f(item) to add new items to the set. // Do should only be used once on a given Work. -func (w *Work) Do(n int, f func(item interface{})) { +func (w *Work) Do(n int, f func(item any)) { if n < 1 { panic("par.Work.Do: n < 1") } @@ -108,25 +108,25 @@ type Cache struct { } type cacheEntry struct { - done uint32 + done atomic.Bool mu sync.Mutex - result interface{} + result any } // Do calls the function f if and only if Do is being called for the first time with this key. // No call to Do with a given key returns until the one call to f returns. // Do returns the value returned by the one call to f. -func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { +func (c *Cache) Do(key any, f func() any) any { entryIface, ok := c.m.Load(key) if !ok { entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) } e := entryIface.(*cacheEntry) - if atomic.LoadUint32(&e.done) == 0 { + if !e.done.Load() { e.mu.Lock() - if atomic.LoadUint32(&e.done) == 0 { + if !e.done.Load() { e.result = f() - atomic.StoreUint32(&e.done, 1) + e.done.Store(true) } e.mu.Unlock() } @@ -136,13 +136,13 @@ func (c *Cache) Do(key interface{}, f func() interface{}) interface{} { // Get returns the cached result associated with key. // It returns nil if there is no such result. // If the result for key is being computed, Get does not wait for the computation to finish. -func (c *Cache) Get(key interface{}) interface{} { +func (c *Cache) Get(key any) any { entryIface, ok := c.m.Load(key) if !ok { return nil } e := entryIface.(*cacheEntry) - if atomic.LoadUint32(&e.done) == 0 { + if !e.done.Load() { return nil } return e.result @@ -156,7 +156,7 @@ func (c *Cache) Get(key interface{}) interface{} { // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. func (c *Cache) Clear() { - c.m.Range(func(key, value interface{}) bool { + c.m.Range(func(key, value any) bool { c.m.Delete(key) return true }) @@ -169,7 +169,7 @@ func (c *Cache) Clear() { // // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. -func (c *Cache) Delete(key interface{}) { +func (c *Cache) Delete(key any) { c.m.Delete(key) } @@ -180,8 +180,8 @@ func (c *Cache) Delete(key interface{}) { // // TODO(jayconrod): Delete this after the package cache clearing functions // in internal/load have been removed. -func (c *Cache) DeleteIf(pred func(key interface{}) bool) { - c.m.Range(func(key, _ interface{}) bool { +func (c *Cache) DeleteIf(pred func(key any) bool) { + c.m.Range(func(key, _ any) bool { if pred(key) { c.Delete(key) } diff --git a/src/cmd/go/internal/par/work_test.go b/src/cmd/go/internal/par/work_test.go index f104bc4106f208..add0e640d8c093 100644 --- a/src/cmd/go/internal/par/work_test.go +++ b/src/cmd/go/internal/par/work_test.go @@ -16,7 +16,7 @@ func TestWork(t *testing.T) { const N = 10000 n := int32(0) w.Add(N) - w.Do(100, func(x interface{}) { + w.Do(100, func(x any) { atomic.AddInt32(&n, 1) i := x.(int) if i >= 2 { @@ -40,7 +40,7 @@ func TestWorkParallel(t *testing.T) { } start := time.Now() var n int32 - w.Do(N, func(x interface{}) { + w.Do(N, func(x any) { time.Sleep(1 * time.Millisecond) atomic.AddInt32(&n, +1) }) @@ -58,19 +58,19 @@ func TestCache(t *testing.T) { var cache Cache n := 1 - v := cache.Do(1, func() interface{} { n++; return n }) + v := cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not run f") } - v = cache.Do(1, func() interface{} { n++; return n }) + v = cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) ran f again!") } - v = cache.Do(2, func() interface{} { n++; return n }) + v = cache.Do(2, func() any { n++; return n }) if v != 3 { t.Fatalf("cache.Do(2) did not run f") } - v = cache.Do(1, func() interface{} { n++; return n }) + v = cache.Do(1, func() any { n++; return n }) if v != 2 { t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)") } diff --git a/src/cmd/go/internal/robustio/robustio.go b/src/cmd/go/internal/robustio/robustio.go index ce3dbbde6db937..15b33773cf5f5b 100644 --- a/src/cmd/go/internal/robustio/robustio.go +++ b/src/cmd/go/internal/robustio/robustio.go @@ -42,9 +42,9 @@ func RemoveAll(path string) error { // in this package attempt to mitigate. // // Errors considered ephemeral include: -// - syscall.ERROR_ACCESS_DENIED -// - syscall.ERROR_FILE_NOT_FOUND -// - internal/syscall/windows.ERROR_SHARING_VIOLATION +// - syscall.ERROR_ACCESS_DENIED +// - syscall.ERROR_FILE_NOT_FOUND +// - internal/syscall/windows.ERROR_SHARING_VIOLATION // // This set may be expanded in the future; programs must not rely on the // non-ephemerality of any given error. diff --git a/src/cmd/go/internal/robustio/robustio_flaky.go b/src/cmd/go/internal/robustio/robustio_flaky.go index d5c241857b476c..c56e36ca62412a 100644 --- a/src/cmd/go/internal/robustio/robustio_flaky.go +++ b/src/cmd/go/internal/robustio/robustio_flaky.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows || darwin -// +build windows darwin package robustio diff --git a/src/cmd/go/internal/robustio/robustio_other.go b/src/cmd/go/internal/robustio/robustio_other.go index 3a20cac6cf88ae..da9a46e4face36 100644 --- a/src/cmd/go/internal/robustio/robustio_other.go +++ b/src/cmd/go/internal/robustio/robustio_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !darwin -// +build !windows,!darwin package robustio diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 784f7162dfd3f5..2804db2296419d 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.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 run implements the ``go run'' command. +// Package run implements the “go run” command. package run import ( @@ -52,6 +52,10 @@ for example 'go_js_wasm_exec a.out arguments...'. This allows execution of cross-compiled programs when a simulator or other execution method is available. +By default, 'go run' compiles the binary without generating the information +used by debuggers, to reduce build time. To include debugger information in +the binary, use 'go build'. + The exit status of Run is not the exit status of the compiled binary. For more about build flags, see 'go help build'. @@ -68,7 +72,7 @@ func init() { CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "") } -func printStderr(args ...interface{}) (int, error) { +func printStderr(args ...any) (int, error) { return fmt.Fprint(os.Stderr, args...) } @@ -82,10 +86,17 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { modload.RootMode = modload.NoRoot modload.AllowMissingModuleImports() modload.Init() + } else { + modload.InitWorkfile() } + work.BuildInit() - var b work.Builder - b.Init() + b := work.NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() b.Print = printStderr i := 0 @@ -100,7 +111,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if strings.HasSuffix(file, "_test.go") { // GoFilesPackage is going to assign this to TestGoFiles. // Reject since it won't be part of the build. - base.Fatalf("go run: cannot run *_test.go files (%s)", file) + base.Fatalf("go: cannot run *_test.go files (%s)", file) } } p = load.GoFilesPackage(ctx, pkgOpts, files) @@ -111,26 +122,26 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { var err error pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1]) if err != nil { - base.Fatalf("go run: %v", err) + base.Fatalf("go: %v", err) } } else { pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1]) } if len(pkgs) == 0 { - base.Fatalf("go run: no packages loaded from %s", arg) + base.Fatalf("go: no packages loaded from %s", arg) } if len(pkgs) > 1 { var names []string for _, p := range pkgs { names = append(names, p.ImportPath) } - base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) + base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) } p = pkgs[0] i++ } else { - base.Fatalf("go run: no go files listed") + base.Fatalf("go: no go files listed") } cmdArgs := args[i:] load.CheckPackageErrors([]*load.Package{p}) @@ -151,7 +162,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildContext.CgoEnabled { hint = " (cgo is disabled)" } - base.Fatalf("go run: no suitable source files%s", hint) + base.Fatalf("go: no suitable source files%s", hint) } p.Internal.ExeName = src[:len(src)-len(".go")] } else { diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go index a0c806a259329c..ebd4990a68413b 100644 --- a/src/cmd/go/internal/search/search.go +++ b/src/cmd/go/internal/search/search.go @@ -202,12 +202,6 @@ func (m *Match) MatchPackages() { } } -var modRoot string - -func SetModRoot(dir string) { - modRoot = dir -} - // MatchDirs sets m.Dirs to a non-nil slice containing all directories that // potentially match a local pattern. The pattern must begin with an absolute // path, or "./", or "../". On Windows, the pattern may use slash or backslash @@ -215,7 +209,7 @@ func SetModRoot(dir string) { // // If any errors may have caused the set of directories to be incomplete, // MatchDirs appends those errors to m.Errs. -func (m *Match) MatchDirs() { +func (m *Match) MatchDirs(modRoots []string) { m.Dirs = []string{} if !m.IsLocal() { m.AddError(fmt.Errorf("internal error: MatchDirs: %s is not a valid filesystem pattern", m.pattern)) @@ -253,15 +247,24 @@ func (m *Match) MatchDirs() { // We need to preserve the ./ for pattern matching // and in the returned import paths. - if modRoot != "" { + if len(modRoots) > 1 { abs, err := filepath.Abs(dir) if err != nil { m.AddError(err) return } - if !hasFilepathPrefix(abs, modRoot) { - m.AddError(fmt.Errorf("directory %s is outside module root (%s)", abs, modRoot)) - return + var found bool + for _, modRoot := range modRoots { + if modRoot != "" && hasFilepathPrefix(abs, modRoot) { + found = true + } + } + if !found { + plural := "" + if len(modRoots) > 1 { + plural = "s" + } + m.AddError(fmt.Errorf("directory %s is outside module root%s (%s)", abs, plural, strings.Join(modRoots, ", "))) } } @@ -424,19 +427,19 @@ func WarnUnmatched(matches []*Match) { // ImportPaths returns the matching paths to use for the given command line. // It calls ImportPathsQuiet and then WarnUnmatched. -func ImportPaths(patterns []string) []*Match { - matches := ImportPathsQuiet(patterns) +func ImportPaths(patterns, modRoots []string) []*Match { + matches := ImportPathsQuiet(patterns, modRoots) WarnUnmatched(matches) return matches } // ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches. -func ImportPathsQuiet(patterns []string) []*Match { +func ImportPathsQuiet(patterns, modRoots []string) []*Match { var out []*Match for _, a := range CleanPatterns(patterns) { m := NewMatch(a) if m.IsLocal() { - m.MatchDirs() + m.MatchDirs(modRoots) // Change the file import path to a regular import path if the package // is in GOPATH or GOROOT. We don't report errors here; LoadImport diff --git a/src/cmd/go/internal/str/path.go b/src/cmd/go/internal/str/path.go index 51ab2af82b58a6..c165b91785d63d 100644 --- a/src/cmd/go/internal/str/path.go +++ b/src/cmd/go/internal/str/path.go @@ -49,3 +49,38 @@ func HasFilePathPrefix(s, prefix string) bool { return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix } } + +// TrimFilePathPrefix returns s without the leading path elements in prefix. +// If s does not start with prefix (HasFilePathPrefix with the same arguments +// returns false), TrimFilePathPrefix returns s. If s equals prefix, +// TrimFilePathPrefix returns "". +func TrimFilePathPrefix(s, prefix string) string { + if !HasFilePathPrefix(s, prefix) { + return s + } + trimmed := s[len(prefix):] + if len(trimmed) == 0 || trimmed[0] != filepath.Separator { + // Prefix either is equal to s, or ends with a separator + // (for example, if it is exactly "/"). + return trimmed + } + return trimmed[1:] +} + +// QuoteGlob returns s with all Glob metacharacters quoted. +// We don't try to handle backslash here, as that can appear in a +// file path on Windows. +func QuoteGlob(s string) string { + if !strings.ContainsAny(s, `*?[]`) { + return s + } + var sb strings.Builder + for _, c := range s { + switch c { + case '*', '?', '[', ']': + sb.WriteByte('\\') + } + sb.WriteRune(c) + } + return sb.String() +} diff --git a/src/cmd/go/internal/str/str.go b/src/cmd/go/internal/str/str.go index 9106ebf74d5e31..af7c6999722fa5 100644 --- a/src/cmd/go/internal/str/str.go +++ b/src/cmd/go/internal/str/str.go @@ -6,15 +6,15 @@ package str import ( - "bytes" "fmt" + "strings" "unicode" "unicode/utf8" ) // StringList flattens its arguments into a single []string. // Each argument in args must have type string or []string. -func StringList(args ...interface{}) []string { +func StringList(args ...any) []string { var x []string for _, arg := range args { switch arg := arg.(type) { @@ -30,7 +30,9 @@ func StringList(args ...interface{}) []string { } // ToFold returns a string with the property that +// // strings.EqualFold(s, t) iff ToFold(s) == ToFold(t) +// // This lets us test a large set of strings for fold-equivalent // duplicates without making a quadratic number of calls // to EqualFold. Note that strings.ToUpper and strings.ToLower @@ -47,7 +49,7 @@ func ToFold(s string) string { return s Slow: - var buf bytes.Buffer + var b strings.Builder for _, r := range s { // SimpleFold(x) cycles to the next equivalent rune > x // or wraps around to smaller values. Iterate until it wraps, @@ -63,9 +65,9 @@ Slow: if 'A' <= r && r <= 'Z' { r += 'a' - 'A' } - buf.WriteRune(r) + b.WriteRune(r) } - return buf.String() + return b.String() } // FoldDup reports a pair of strings from the list that are @@ -109,47 +111,3 @@ func Uniq(ss *[]string) { } *ss = uniq } - -func isSpaceByte(c byte) bool { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' -} - -// SplitQuotedFields splits s into a list of fields, -// allowing single or double quotes around elements. -// There is no unescaping or other processing within -// quoted fields. -func SplitQuotedFields(s string) ([]string, error) { - // Split fields allowing '' or "" around elements. - // Quotes further inside the string do not count. - var f []string - for len(s) > 0 { - for len(s) > 0 && isSpaceByte(s[0]) { - s = s[1:] - } - if len(s) == 0 { - break - } - // Accepted quoted string. No unescaping inside. - if s[0] == '"' || s[0] == '\'' { - quote := s[0] - s = s[1:] - i := 0 - for i < len(s) && s[i] != quote { - i++ - } - if i >= len(s) { - return nil, fmt.Errorf("unterminated %c string", quote) - } - f = append(f, s[:i]) - s = s[i+1:] - continue - } - i := 0 - for i < len(s) && !isSpaceByte(s[i]) { - i++ - } - f = append(f, s[:i]) - s = s[i:] - } - return f, nil -} diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go index 147ce1a63ef32d..158fe65dc16edf 100644 --- a/src/cmd/go/internal/str/str_test.go +++ b/src/cmd/go/internal/str/str_test.go @@ -4,7 +4,11 @@ package str -import "testing" +import ( + "os" + "runtime" + "testing" +) var foldDupTests = []struct { list []string @@ -25,3 +29,72 @@ func TestFoldDup(t *testing.T) { } } } + +type trimFilePathPrefixTest struct { + s, prefix, want string +} + +func TestTrimFilePathPrefixSlash(t *testing.T) { + if os.PathSeparator != '/' { + t.Skipf("test requires slash-separated file paths") + } + tests := []trimFilePathPrefixTest{ + {"/foo", "", "foo"}, + {"/foo", "/", "foo"}, + {"/foo", "/foo", ""}, + {"/foo/bar", "/foo", "bar"}, + {"/foo/bar", "/foo/", "bar"}, + // if prefix is not s's prefix, return s + {"/foo", "/bar", "/foo"}, + {"/foo", "/foo/bar", "/foo"}, + } + + for _, tt := range tests { + if got := TrimFilePathPrefix(tt.s, tt.prefix); got != tt.want { + t.Errorf("TrimFilePathPrefix(%q, %q) = %q, want %q", tt.s, tt.prefix, got, tt.want) + } + } +} + +func TestTrimFilePathPrefixWindows(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skipf("test requires Windows file paths") + } + tests := []trimFilePathPrefixTest{ + {`C:\foo`, `C:`, `foo`}, + {`C:\foo`, `C:\`, `foo`}, + {`C:\foo`, `C:\foo`, ``}, + {`C:\foo\bar`, `C:\foo`, `bar`}, + {`C:\foo\bar`, `C:\foo\`, `bar`}, + // if prefix is not s's prefix, return s + {`C:\foo`, `C:\bar`, `C:\foo`}, + {`C:\foo`, `C:\foo\bar`, `C:\foo`}, + // if volumes are different, return s + {`C:\foo`, ``, `C:\foo`}, + {`C:\foo`, `\foo`, `C:\foo`}, + {`C:\foo`, `D:\foo`, `C:\foo`}, + + //UNC path + {`\\host\share\foo`, `\\host\share`, `foo`}, + {`\\host\share\foo`, `\\host\share\`, `foo`}, + {`\\host\share\foo`, `\\host\share\foo`, ``}, + {`\\host\share\foo\bar`, `\\host\share\foo`, `bar`}, + {`\\host\share\foo\bar`, `\\host\share\foo\`, `bar`}, + // if prefix is not s's prefix, return s + {`\\host\share\foo`, `\\host\share\bar`, `\\host\share\foo`}, + {`\\host\share\foo`, `\\host\share\foo\bar`, `\\host\share\foo`}, + // if either host or share name is different, return s + {`\\host\share\foo`, ``, `\\host\share\foo`}, + {`\\host\share\foo`, `\foo`, `\\host\share\foo`}, + {`\\host\share\foo`, `\\host\other\`, `\\host\share\foo`}, + {`\\host\share\foo`, `\\other\share\`, `\\host\share\foo`}, + {`\\host\share\foo`, `\\host\`, `\\host\share\foo`}, + {`\\host\share\foo`, `\share\`, `\\host\share\foo`}, + } + + for _, tt := range tests { + if got := TrimFilePathPrefix(tt.s, tt.prefix); got != tt.want { + t.Errorf("TrimFilePathPrefix(%q, %q) = %q, want %q", tt.s, tt.prefix, got, tt.want) + } + } +} diff --git a/src/cmd/go/internal/test/flagdefs.go b/src/cmd/go/internal/test/flagdefs.go index 37ac81c26782ae..1b79314eff3658 100644 --- a/src/cmd/go/internal/test/flagdefs.go +++ b/src/cmd/go/internal/test/flagdefs.go @@ -19,6 +19,9 @@ var passFlagToTest = map[string]bool{ "cpu": true, "cpuprofile": true, "failfast": true, + "fuzz": true, + "fuzzminimizetime": true, + "fuzztime": true, "list": true, "memprofile": true, "memprofilerate": true, @@ -33,3 +36,37 @@ var passFlagToTest = map[string]bool{ "trace": true, "v": true, } + +var passAnalyzersToVet = map[string]bool{ + "asmdecl": true, + "assign": true, + "atomic": true, + "bool": true, + "bools": true, + "buildtag": true, + "buildtags": true, + "cgocall": true, + "composites": true, + "copylocks": true, + "errorsas": true, + "framepointer": true, + "httpresponse": true, + "ifaceassert": true, + "loopclosure": true, + "lostcancel": true, + "methods": true, + "nilfunc": true, + "printf": true, + "rangeloops": true, + "shift": true, + "sigchanyzer": true, + "stdmethods": true, + "stringintconv": true, + "structtag": true, + "testinggoroutine": true, + "tests": true, + "unmarshal": true, + "unreachable": true, + "unsafeptr": true, + "unusedresult": true, +} diff --git a/src/cmd/go/internal/test/flagdefs_test.go b/src/cmd/go/internal/test/flagdefs_test.go index ab5440b3801f15..64317fd04e9da7 100644 --- a/src/cmd/go/internal/test/flagdefs_test.go +++ b/src/cmd/go/internal/test/flagdefs_test.go @@ -5,11 +5,19 @@ package test import ( + "cmd/go/internal/cfg" + "cmd/go/internal/test/internal/genflags" "flag" + "internal/testenv" + "reflect" "strings" "testing" ) +func TestMain(m *testing.M) { + cfg.SetGOROOT(testenv.GOROOT(nil), false) +} + func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) { flag.VisitAll(func(f *flag.Flag) { if !strings.HasPrefix(f.Name, "test.") { @@ -17,7 +25,7 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) { } name := strings.TrimPrefix(f.Name, "test.") switch name { - case "testlogfile", "paniconexit0": + case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker": // These are internal flags. default: if !passFlagToTest[name] { @@ -37,3 +45,20 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) { } } } + +func TestVetAnalyzersSetIsCorrect(t *testing.T) { + vetAns, err := genflags.VetAnalyzers() + if err != nil { + t.Fatal(err) + } + + want := make(map[string]bool) + for _, a := range vetAns { + want[a] = true + } + + if !reflect.DeepEqual(want, passAnalyzersToVet) { + t.Errorf("stale vet analyzers: want %v; got %v", want, passAnalyzersToVet) + t.Logf("(Run 'go generate cmd/go/internal/test' to refresh the set of analyzers.)") + } +} diff --git a/src/cmd/go/internal/test/genflags.go b/src/cmd/go/internal/test/genflags.go index 9277de7fee839e..f50ae5c1e93240 100644 --- a/src/cmd/go/internal/test/genflags.go +++ b/src/cmd/go/internal/test/genflags.go @@ -3,19 +3,20 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore package main import ( "bytes" "flag" - exec "internal/execabs" "log" "os" + "os/exec" "strings" "testing" "text/template" + + "cmd/go/internal/test/internal/genflags" ) func main() { @@ -25,9 +26,18 @@ func main() { } func regenerate() error { + vetAnalyzers, err := genflags.VetAnalyzers() + if err != nil { + return err + } + t := template.Must(template.New("fileTemplate").Parse(fileTemplate)) + tData := map[string][]string{ + "testFlags": testFlags(), + "vetAnalyzers": vetAnalyzers, + } buf := bytes.NewBuffer(nil) - if err := t.Execute(buf, testFlags()); err != nil { + if err := t.Execute(buf, tData); err != nil { return err } @@ -64,7 +74,7 @@ func testFlags() []string { name := strings.TrimPrefix(f.Name, "test.") switch name { - case "testlogfile", "paniconexit0": + case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker": // These flags are only for use by cmd/go. default: names = append(names, name) @@ -85,7 +95,13 @@ package test // passFlagToTest contains the flags that should be forwarded to // the test binary with the prefix "test.". var passFlagToTest = map[string]bool { -{{- range .}} +{{- range .testFlags}} + "{{.}}": true, +{{- end }} +} + +var passAnalyzersToVet = map[string]bool { +{{- range .vetAnalyzers}} "{{.}}": true, {{- end }} } diff --git a/src/cmd/go/internal/test/internal/genflags/vetflag.go b/src/cmd/go/internal/test/internal/genflags/vetflag.go new file mode 100644 index 00000000000000..1448811af053e4 --- /dev/null +++ b/src/cmd/go/internal/test/internal/genflags/vetflag.go @@ -0,0 +1,68 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package genflags + +import ( + "bytes" + "cmd/go/internal/base" + "encoding/json" + "fmt" + "os/exec" + "regexp" + "sort" +) + +// VetAnalyzers computes analyzers and their aliases supported by vet. +func VetAnalyzers() ([]string, error) { + // get supported vet flag information + tool := base.Tool("vet") + vetcmd := exec.Command(tool, "-flags") + out := new(bytes.Buffer) + vetcmd.Stdout = out + if err := vetcmd.Run(); err != nil { + return nil, fmt.Errorf("go vet: can't execute %s -flags: %v\n", tool, err) + } + var analysisFlags []struct { + Name string + Bool bool + Usage string + } + if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil { + return nil, fmt.Errorf("go vet: can't unmarshal JSON from %s -flags: %v", tool, err) + } + + // parse the flags to figure out which ones stand for analyses + analyzerSet := make(map[string]bool) + rEnable := regexp.MustCompile("^enable .+ analysis$") + for _, flag := range analysisFlags { + if rEnable.MatchString(flag.Usage) { + analyzerSet[flag.Name] = true + } + } + + rDeprecated := regexp.MustCompile("^deprecated alias for -(?P(.+))$") + // Returns the original value matched by rDeprecated on input value. + // If there is no match, "" is returned. + originalValue := func(value string) string { + match := rDeprecated.FindStringSubmatch(value) + if len(match) < 2 { + return "" + } + return match[1] + } + // extract deprecated aliases for existing analyses + for _, flag := range analysisFlags { + if o := originalValue(flag.Usage); analyzerSet[o] { + analyzerSet[flag.Name] = true + } + } + + var analyzers []string + for a := range analyzerSet { + analyzers = append(analyzers, a) + } + sort.Strings(analyzers) + return analyzers, nil +} diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 59ea1ef5445178..7e6747055e04ac 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -11,10 +11,10 @@ import ( "errors" "fmt" "go/build" - exec "internal/execabs" "io" "io/fs" "os" + "os/exec" "path" "path/filepath" "regexp" @@ -29,11 +29,15 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/load" "cmd/go/internal/lockedfile" + "cmd/go/internal/modload" "cmd/go/internal/search" "cmd/go/internal/str" "cmd/go/internal/trace" "cmd/go/internal/work" + "cmd/internal/sys" "cmd/internal/test2json" + + "golang.org/x/mod/module" ) // Break init loop. @@ -60,8 +64,8 @@ followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go". -These additional files can contain test functions, benchmark functions, and -example functions. See 'go help testfunc' for more. +These additional files can contain test functions, benchmark functions, fuzz +tests and example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Files whose names begin with "_" (including "_test.go") or "." are ignored. @@ -78,7 +82,8 @@ binary. Only a high-confidence subset of the default go vet checks are used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas', 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see the documentation for these and other vet tests via "go doc cmd/vet". -To disable the running of go vet, use the -vet=off flag. +To disable the running of go vet, use the -vet=off flag. To run all +checks, use the -vet=all flag. All test output and summary lines are printed to the go command's standard output, even if the test printed them to its own standard @@ -119,16 +124,16 @@ elapsed time in the summary line. The rule for a match in the cache is that the run involves the same test binary and the flags on the command line come entirely from a restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, --list, -parallel, -run, -short, and -v. If a run of go test has any test -or non-test flags outside this set, the result is not cached. To -disable test caching, use any test flag or argument other than the -cacheable flags. The idiomatic way to disable test caching explicitly -is to use -count=1. Tests that open files within the package's source -root (usually $GOPATH) or that consult environment variables only -match future runs in which the files and environment variables are unchanged. -A cached test result is treated as executing in no time at all, -so a successful package test result will be cached and reused -regardless of -timeout setting. +-list, -parallel, -run, -short, -timeout, -failfast, and -v. +If a run of go test has any test or non-test flags outside this set, +the result is not cached. To disable test caching, use any test flag +or argument other than the cacheable flags. The idiomatic way to disable +test caching explicitly is to use -count=1. Tests that open files within +the package's source root (usually $GOPATH) or that consult environment +variables only match future runs in which the files and environment +variables are unchanged. A cached test result is treated as executing +in no time at all, so a successful package test result will be cached and +reused regardless of -timeout setting. In addition to the build flags, the flags handled by 'go test' itself are: @@ -206,9 +211,10 @@ control the execution of any test: (for example, -benchtime 100x). -count n - Run each test and benchmark n times (default 1). + Run each test, benchmark, and fuzz seed n times (default 1). If -cpu is set, run n times for each GOMAXPROCS value. - Examples are always run once. + Examples are always run once. -count does not apply to + fuzz tests matched by -fuzz. -cover Enable coverage analysis. @@ -235,32 +241,67 @@ control the execution of any test: Sets -cover. -cpu 1,2,4 - Specify a list of GOMAXPROCS values for which the tests or - benchmarks should be executed. The default is the current value - of GOMAXPROCS. + Specify a list of GOMAXPROCS values for which the tests, benchmarks or + fuzz tests should be executed. The default is the current value + of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz. -failfast Do not start new tests after the first test failure. + -fuzz regexp + Run the fuzz test matching the regular expression. When specified, + the command line argument must match exactly one package within the + main module, and regexp must match exactly one fuzz test within + that package. Fuzzing will occur after tests, benchmarks, seed corpora + of other fuzz tests, and examples have completed. See the Fuzzing + section of the testing package documentation for details. + + -fuzztime t + Run enough iterations of the fuzz target during fuzzing to take t, + specified as a time.Duration (for example, -fuzztime 1h30s). + The default is to run forever. + The special syntax Nx means to run the fuzz target N times + (for example, -fuzztime 1000x). + + -fuzzminimizetime t + Run enough iterations of the fuzz target during each minimization + attempt to take t, as specified as a time.Duration (for example, + -fuzzminimizetime 30s). + The default is 60s. + The special syntax Nx means to run the fuzz target N times + (for example, -fuzzminimizetime 100x). + + -json + Log verbose output and test results in JSON. This presents the + same information as the -v flag in a machine-readable format. + -list regexp - List tests, benchmarks, or examples matching the regular expression. - No tests, benchmarks or examples will be run. This will only - list top-level tests. No subtest or subbenchmarks will be shown. + List tests, benchmarks, fuzz tests, or examples matching the regular + expression. No tests, benchmarks, fuzz tests, or examples will be run. + This will only list top-level tests. No subtest or subbenchmarks will be + shown. -parallel n - Allow parallel execution of test functions that call t.Parallel. + Allow parallel execution of test functions that call t.Parallel, and + fuzz targets that call t.Parallel when running the seed corpus. The value of this flag is the maximum number of tests to run - simultaneously; by default, it is set to the value of GOMAXPROCS. + simultaneously. + While fuzzing, the value of this flag is the maximum number of + subprocesses that may call the fuzz function simultaneously, regardless of + whether T.Parallel is called. + By default, -parallel is set to the value of GOMAXPROCS. + Setting -parallel to values higher than GOMAXPROCS may cause degraded + performance due to CPU contention, especially when fuzzing. Note that -parallel only applies within a single test binary. The 'go test' command may run tests for different packages in parallel as well, according to the setting of the -p flag (see 'go help build'). -run regexp - Run only those tests and examples matching the regular expression. - For tests, the regular expression is split by unbracketed slash (/) - characters into a sequence of regular expressions, and each part - of a test's identifier must match the corresponding element in + Run only those tests, examples, and fuzz tests matching the regular + expression. For tests, the regular expression is split by unbracketed + slash (/) characters into a sequence of regular expressions, and each + part of a test's identifier must match the corresponding element in the sequence, if any. Note that possible parents of matches are run too, so that -run=X/Y matches and runs and reports the result of all tests matching X, even those without sub-tests matching Y, @@ -273,11 +314,11 @@ control the execution of any test: exhaustive tests. -shuffle off,on,N - Randomize the execution order of tests and benchmarks. - It is off by default. If -shuffle is set to on, then it will seed - the randomizer using the system clock. If -shuffle is set to an - integer N, then N will be used as the seed value. In both cases, - the seed will be reported for reproducibility. + Randomize the execution order of tests and benchmarks. + It is off by default. If -shuffle is set to on, then it will seed + the randomizer using the system clock. If -shuffle is set to an + integer N, then N will be used as the seed value. In both cases, + the seed will be reported for reproducibility. -timeout d If a test binary runs longer than duration d, panic. @@ -373,7 +414,11 @@ leave the test binary in pkg.test for use when analyzing the profiles. When 'go test' runs a test binary, it does so from within the corresponding package's source code directory. Depending on the test, it may be necessary to do the same when invoking a generated test -binary directly. +binary directly. Because that directory may be located within the +module cache, which may be read-only and is verified by checksums, the +test must not write to it or any other directory within the module +unless explicitly requested by the user (such as with the -fuzz flag, +which writes failures to testdata/fuzz). The command-line package list, if present, must appear before any flag not known to the go test command. Continuing the example above, @@ -430,6 +475,10 @@ A benchmark function is one named BenchmarkXxx and should have the signature, func BenchmarkXxx(b *testing.B) { ... } +A fuzz test is one named FuzzXxx and should have the signature, + + func FuzzXxx(f *testing.F) { ... } + An example function is similar to a test function but, instead of using *testing.T to report success or failure, prints output to os.Stdout. If the last comment in the function starts with "Output:" then the output @@ -469,7 +518,7 @@ Here is another example where the ordering of the output is ignored: The entire test file is presented as the example when it contains a single example function, at least one other function, type, variable, or constant -declaration, and no test or benchmark functions. +declaration, and no tests, benchmarks, or fuzz tests. See the documentation of the testing package for more information. `, @@ -483,6 +532,7 @@ var ( testCoverPaths []string // -coverpkg flag testCoverPkgs []*load.Package // -coverpkg flag testCoverProfile string // -coverprofile flag + testFuzz string // -fuzz flag testJSON bool // -json flag testList string // -list flag testO string // -o flag @@ -578,6 +628,7 @@ var defaultVetFlags = []string{ func runTest(ctx context.Context, cmd *base.Command, args []string) { pkgArgs, testArgs = testFlags(args) + modload.InitWorkfile() // The test command does custom flag processing; initialize workspaces after that. if cfg.DebugTrace != "" { var close func() error @@ -615,6 +666,52 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { if testO != "" && len(pkgs) != 1 { base.Fatalf("cannot use -o flag with multiple packages") } + if testFuzz != "" { + if !sys.FuzzSupported(cfg.Goos, cfg.Goarch) { + base.Fatalf("-fuzz flag is not supported on %s/%s", cfg.Goos, cfg.Goarch) + } + if len(pkgs) != 1 { + base.Fatalf("cannot use -fuzz flag with multiple packages") + } + if testCoverProfile != "" { + base.Fatalf("cannot use -coverprofile flag with -fuzz flag") + } + if profileFlag := testProfile(); profileFlag != "" { + base.Fatalf("cannot use %s flag with -fuzz flag", profileFlag) + } + + // Reject the '-fuzz' flag if the package is outside the main module. + // Otherwise, if fuzzing identifies a failure it could corrupt checksums in + // the module cache (or permanently alter the behavior of std tests for all + // users) by writing the failing input to the package's testdata directory. + // (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.) + mainMods := modload.MainModules + if m := pkgs[0].Module; m != nil && m.Path != "" { + if !mainMods.Contains(m.Path) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } else if pkgs[0].Standard && modload.Enabled() { + // Because packages in 'std' and 'cmd' are part of the standard library, + // they are only treated as part of a module in 'go mod' subcommands and + // 'go get'. However, we still don't want to accidentally corrupt their + // testdata during fuzzing, nor do we want to fail with surprising errors + // if GOROOT isn't writable (as is often the case for Go toolchains + // installed through package managers). + // + // If the user is requesting to fuzz a standard-library package, ensure + // that they are in the same module as that package (just like when + // fuzzing any other package). + if strings.HasPrefix(pkgs[0].ImportPath, "cmd/") { + if !mainMods.Contains("cmd") || !mainMods.InGorootSrc(module.Version{Path: "cmd"}) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } else { + if !mainMods.Contains("std") || !mainMods.InGorootSrc(module.Version{Path: "std"}) { + base.Fatalf("cannot use -fuzz flag on package outside the main module") + } + } + } + } if testProfile() != "" && len(pkgs) != 1 { base.Fatalf("cannot use %s flag with multiple packages", testProfile()) } @@ -625,7 +722,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // to that timeout plus one minute. This is a backup alarm in case // the test wedges with a goroutine spinning and its background // timer does not get a chance to fire. - if testTimeout > 0 { + // Don't set this if fuzzing, since it should be able to run + // indefinitely. + if testTimeout > 0 && testFuzz == "" { testKillTimeout = testTimeout + 1*time.Minute } @@ -645,11 +744,15 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { } } - var b work.Builder - b.Init() + b := work.NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() if cfg.BuildI { - fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") cfg.BuildV = testV deps := make(map[string]bool) @@ -701,7 +804,19 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { if !testC || a.Failed { return } - b.Init() + + // TODO(bcmills): I have no idea why the Builder must be reset here, but + // without this reset dance, TestGoTestDashIDashOWritesBinary fails with + // lots of "vet config not found" errors. This was added in CL 5699088, + // which had almost no public discussion, a very short commit description, + // and left no comment in the code to explain what is going on here. 🤯 + // + // Maybe this has the effect of removing actions that were registered by the + // call to CompileAction above? + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + b = work.NewBuilder("") } var builds, runs, prints []*work.Action @@ -775,6 +890,41 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { } } + // Inform the compiler that it should instrument the binary at + // build-time when fuzzing is enabled. + if testFuzz != "" { + // Don't instrument packages which may affect coverage guidance but are + // unlikely to be useful. Most of these are used by the testing or + // internal/fuzz packages concurrently with fuzzing. + var skipInstrumentation = map[string]bool{ + "context": true, + "internal/fuzz": true, + "reflect": true, + "runtime": true, + "sync": true, + "sync/atomic": true, + "syscall": true, + "testing": true, + "time": true, + } + for _, p := range load.TestPackageList(ctx, pkgOpts, pkgs) { + if !skipInstrumentation[p.ImportPath] { + p.Internal.FuzzInstrument = true + } + } + } + + // Collect all the packages imported by the packages being tested. + allImports := make(map[*load.Package]bool) + for _, p := range pkgs { + if p.Error != nil && p.Error.IsImportCycle { + continue + } + for _, p1 := range p.Internal.Imports { + allImports[p1] = true + } + } + // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { // sync/atomic import is inserted by the cover tool. See #18486 @@ -782,7 +932,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { ensureImport(p, "sync/atomic") } - buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p) + buildTest, runTest, printTest, err := builderTest(b, ctx, pkgOpts, p, allImports[p]) if err != nil { str := err.Error() str = strings.TrimPrefix(str, "\n") @@ -849,7 +999,7 @@ var windowsBadWords = []string{ "update", } -func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) { +func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool) (buildAction, runAction, printAction *work.Action, err error) { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { build := b.CompileAction(work.ModeBuild, work.ModeBuild, p) run := &work.Action{Mode: "test run", Package: p, Deps: []*work.Action{build}} @@ -877,6 +1027,16 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, return nil, nil, nil, err } + // If imported is true then this package is imported by some + // package being tested. Make building the test version of the + // package depend on building the non-test version, so that we + // only report build errors once. Issue #44624. + if imported && ptest != p { + buildTest := b.CompileAction(work.ModeBuild, work.ModeBuild, ptest) + buildP := b.CompileAction(work.ModeBuild, work.ModeBuild, p) + buildTest.Deps = append(buildTest.Deps, buildP) + } + // Use last element of import path, not package name. // They differ when package name is "main". // But if the import path is "command-line-arguments", @@ -1080,6 +1240,8 @@ func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVa } var noTestsToRun = []byte("\ntesting: warning: no tests to run\n") +var noFuzzTestsToFuzz = []byte("\ntesting: warning: no fuzz tests to fuzz\n") +var tooManyFuzzTestsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one fuzz test, won't fuzz\n") type runCache struct { disableCache bool // cache should be disabled for this run @@ -1127,10 +1289,10 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. } var buf bytes.Buffer - if len(pkgArgs) == 0 || (testBench != "") { + if len(pkgArgs) == 0 || testBench != "" || testFuzz != "" { // Stream test output (no buffering) when no package has // been given on the command line (implicit current directory) - // or when benchmarking. + // or when benchmarking or fuzzing. // No change to stdout. } else { // If we're only running a single package under test or if parallelism is @@ -1183,7 +1345,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"} } panicArg := "-test.paniconexit0" - args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, testArgs) + fuzzArg := []string{} + if testFuzz != "" { + fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath) + fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir} + } + args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, fuzzArg, testArgs) if testCoverProfile != "" { // Write coverage to temporary profile, for merging later. @@ -1203,7 +1370,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. cmd := exec.Command(args[0], args[1:]...) cmd.Dir = a.Package.Dir - cmd.Env = base.AppendPWD(cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)], cmd.Dir) + + env := cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)] + env = base.AppendPATH(env) + env = base.AppendPWD(env, cmd.Dir) + cmd.Env = env + cmd.Stdout = stdout cmd.Stderr = stdout @@ -1276,15 +1448,31 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work. if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) { norun = " [no tests to run]" } + if bytes.HasPrefix(out, noFuzzTestsToFuzz[1:]) || bytes.Contains(out, noFuzzTestsToFuzz) { + norun = " [no fuzz tests to fuzz]" + } + if bytes.HasPrefix(out, tooManyFuzzTestsToFuzz[1:]) || bytes.Contains(out, tooManyFuzzTestsToFuzz) { + norun = "[-fuzz matches more than one fuzz test, won't fuzz]" + } + if len(out) > 0 && !bytes.HasSuffix(out, []byte("\n")) { + // Ensure that the output ends with a newline before the "ok" + // line we're about to print (https://golang.org/issue/49317). + cmd.Stdout.Write([]byte("\n")) + } fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun) c.saveOutput(a) } else { base.SetExitStatus(1) - // If there was test output, assume we don't need to print the exit status. - // Buf there's no test output, do print the exit status. if len(out) == 0 { + // If there was no test output, print the exit status so that the reason + // for failure is clear. fmt.Fprintf(cmd.Stdout, "%s\n", err) + } else if !bytes.HasSuffix(out, []byte("\n")) { + // Otherwise, ensure that the output ends with a newline before the FAIL + // line we're about to print (https://golang.org/issue/49317). + cmd.Stdout.Write([]byte("\n")) } + // NOTE(golang.org/issue/37555): test2json reports that a test passes // unless "FAIL" is printed at the beginning of a line. The test may not // actually print that if it panics, exits, or terminates abnormally, @@ -1347,6 +1535,7 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo "-test.run", "-test.short", "-test.timeout", + "-test.failfast", "-test.v": // These are cacheable. // Note that this list is documented above, @@ -1711,9 +1900,23 @@ func builderNoTest(b *work.Builder, ctx context.Context, a *work.Action) error { return nil } -// printExitStatus is the action for printing the exit status +// printExitStatus is the action for printing the final exit status. +// If we are running multiple test targets, print a final "FAIL" +// in case a failure in an early package has already scrolled +// off of the user's terminal. +// (See https://golang.org/issue/30507#issuecomment-470593235.) +// +// In JSON mode, we need to maintain valid JSON output and +// we assume that the test output is being parsed by a tool +// anyway, so the failure will not be missed and would be +// awkward to try to wedge into the JSON stream. +// +// In fuzz mode, we only allow a single package for now +// (see CL 350156 and https://golang.org/issue/46312), +// so there is no possibility of scrolling off and no need +// to print the final status. func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error { - if !testJSON && len(pkgArgs) != 0 { + if !testJSON && testFuzz == "" && len(pkgArgs) != 0 { if base.GetExitStatus() != 0 { fmt.Println("FAIL") return nil diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go index 08f1efa2c0d26a..f3cd0b1392f00a 100644 --- a/src/cmd/go/internal/test/testflag.go +++ b/src/cmd/go/internal/test/testflag.go @@ -5,6 +5,10 @@ package test import ( + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/cmdflag" + "cmd/go/internal/work" "errors" "flag" "fmt" @@ -13,11 +17,6 @@ import ( "strconv" "strings" "time" - - "cmd/go/internal/base" - "cmd/go/internal/cfg" - "cmd/go/internal/cmdflag" - "cmd/go/internal/work" ) //go:generate go run ./genflags.go @@ -57,6 +56,7 @@ func init() { cf.String("cpu", "", "") cf.StringVar(&testCPUProfile, "cpuprofile", "", "") cf.Bool("failfast", false, "") + cf.StringVar(&testFuzz, "fuzz", "", "") cf.StringVar(&testList, "list", "", "") cf.StringVar(&testMemProfile, "memprofile", "", "") cf.String("memprofilerate", "", "") @@ -67,6 +67,8 @@ func init() { cf.String("run", "", "") cf.Bool("short", false, "") cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "") + cf.String("fuzztime", "", "") + cf.String("fuzzminimizetime", "", "") cf.StringVar(&testTrace, "trace", "", "") cf.BoolVar(&testV, "v", false, "") cf.Var(&testShuffle, "shuffle", "") @@ -134,6 +136,7 @@ type outputdirFlag struct { func (f *outputdirFlag) String() string { return f.abs } + func (f *outputdirFlag) Set(value string) (err error) { if value == "" { f.abs = "" @@ -142,6 +145,7 @@ func (f *outputdirFlag) Set(value string) (err error) { } return err } + func (f *outputdirFlag) getAbs() string { if f.abs == "" { return base.Cwd() @@ -150,8 +154,12 @@ func (f *outputdirFlag) getAbs() string { } // vetFlag implements the special parsing logic for the -vet flag: -// a comma-separated list, with a distinguished value "off" and -// a boolean tracking whether it was set explicitly. +// a comma-separated list, with distinguished values "all" and +// "off", plus a boolean tracking whether it was set explicitly. +// +// "all" is encoded as vetFlag{true, false, nil}, since it will +// pass no flags to the vet binary, and by default, it runs all +// analyzers. type vetFlag struct { explicit bool off bool @@ -159,7 +167,10 @@ type vetFlag struct { } func (f *vetFlag) String() string { - if f.off { + switch { + case !f.off && !f.explicit && len(f.flags) == 0: + return "all" + case f.off: return "off" } @@ -174,31 +185,45 @@ func (f *vetFlag) String() string { } func (f *vetFlag) Set(value string) error { - if value == "" { + switch { + case value == "": *f = vetFlag{flags: defaultVetFlags} return nil - } - - if value == "off" { - *f = vetFlag{ - explicit: true, - off: true, - } - return nil - } - - if strings.Contains(value, "=") { + case strings.Contains(value, "="): return fmt.Errorf("-vet argument cannot contain equal signs") - } - if strings.Contains(value, " ") { + case strings.Contains(value, " "): return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces") } + *f = vetFlag{explicit: true} + var single string for _, arg := range strings.Split(value, ",") { - if arg == "" { + switch arg { + case "": return fmt.Errorf("-vet argument contains empty list element") + case "all": + single = arg + *f = vetFlag{explicit: true} + continue + case "off": + single = arg + *f = vetFlag{ + explicit: true, + off: true, + } + continue + default: + if _, ok := passAnalyzersToVet[arg]; !ok { + return fmt.Errorf("-vet argument must be a supported analyzer or a distinguished value; found %s", arg) + } + f.flags = append(f.flags, "-"+arg) } - f.flags = append(f.flags, "-"+arg) + } + if len(f.flags) > 1 && single != "" { + return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single) + } + if len(f.flags) > 1 && single != "" { + return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single) } return nil } @@ -245,6 +270,7 @@ func (f *shuffleFlag) Set(value string) error { // pkg.test's arguments. // We allow known flags both before and after the package name list, // to allow both +// // go test fmt -custom-flag-for-fmt-test // go test -x math func testFlags(args []string) (packageNames, passToTest []string) { @@ -369,7 +395,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { if !testC { buildFlag = "-i" } - fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) + fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) exitWithUsage() } diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go index 95c90ea7c8da4a..afa3ac404fcff2 100644 --- a/src/cmd/go/internal/tool/tool.go +++ b/src/cmd/go/internal/tool/tool.go @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package tool implements the ``go tool'' command. +// Package tool implements the “go tool” command. package tool import ( "context" "fmt" - exec "internal/execabs" + "go/build" "os" + "os/exec" "os/signal" "sort" "strings" @@ -61,7 +62,7 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { switch { case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_': default: - fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName) base.SetExitStatus(2) return } @@ -115,16 +116,16 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { // listTools prints a list of the available tools in the tools directory. func listTools() { - f, err := os.Open(base.ToolDir) + f, err := os.Open(build.ToolDir) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err) base.SetExitStatus(2) return } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err) base.SetExitStatus(2) return } @@ -132,11 +133,9 @@ func listTools() { sort.Strings(names) for _, name := range names { // Unify presentation by going to lower case. - name = strings.ToLower(name) // If it's windows, don't show the .exe suffix. - if base.ToolIsWindows && strings.HasSuffix(name, base.ToolWindowsExtension) { - name = name[:len(name)-len(base.ToolWindowsExtension)] - } + name = strings.TrimSuffix(strings.ToLower(name), cfg.ToolExeSuffix()) + // The tool directory used by gccgo will have other binaries // in addition to go tools. Only display go tools here. if cfg.BuildToolchainName == "gccgo" && !isGccgoTool(name) { diff --git a/src/cmd/go/internal/trace/trace.go b/src/cmd/go/internal/trace/trace.go index f108a2b6cae19b..d69dc4feac2b40 100644 --- a/src/cmd/go/internal/trace/trace.go +++ b/src/cmd/go/internal/trace/trace.go @@ -27,10 +27,10 @@ const ( bindEnclosingSlice = "e" ) -var traceStarted int32 +var traceStarted atomic.Bool func getTraceContext(ctx context.Context) (traceContext, bool) { - if atomic.LoadInt32(&traceStarted) == 0 { + if !traceStarted.Load() { return traceContext{}, false } v := ctx.Value(traceKey{}) @@ -179,7 +179,7 @@ type traceContext struct { // Start starts a trace which writes to the given file. func Start(ctx context.Context, file string) (context.Context, func() error, error) { - atomic.StoreInt32(&traceStarted, 1) + traceStarted.Store(true) if file == "" { return nil, nil, errors.New("no trace file supplied") } diff --git a/src/cmd/go/internal/txtar/archive_test.go b/src/cmd/go/internal/txtar/archive_test.go deleted file mode 100644 index 3f734f6762531e..00000000000000 --- a/src/cmd/go/internal/txtar/archive_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package txtar - -import ( - "bytes" - "fmt" - "reflect" - "testing" -) - -var tests = []struct { - name string - text string - parsed *Archive -}{ - { - name: "basic", - text: `comment1 -comment2 --- file1 -- -File 1 text. --- foo --- -More file 1 text. --- file 2 -- -File 2 text. --- empty -- --- noNL -- -hello world`, - parsed: &Archive{ - Comment: []byte("comment1\ncomment2\n"), - Files: []File{ - {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")}, - {"file 2", []byte("File 2 text.\n")}, - {"empty", []byte{}}, - {"noNL", []byte("hello world\n")}, - }, - }, - }, -} - -func Test(t *testing.T) { - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := Parse([]byte(tt.text)) - if !reflect.DeepEqual(a, tt.parsed) { - t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) - } - text := Format(a) - a = Parse(text) - if !reflect.DeepEqual(a, tt.parsed) { - t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) - } - }) - } -} - -func shortArchive(a *Archive) string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "comment: %q\n", a.Comment) - for _, f := range a.Files { - fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data) - } - return buf.String() -} diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go index 91485f6f745b1f..4f16bef90ce2fa 100644 --- a/src/cmd/go/internal/vcs/vcs.go +++ b/src/cmd/go/internal/vcs/vcs.go @@ -5,22 +5,23 @@ package vcs import ( - "encoding/json" + "bytes" "errors" "fmt" - exec "internal/execabs" "internal/lazyregexp" "internal/singleflight" "io/fs" "log" urlpkg "net/url" "os" + "os/exec" "path/filepath" "regexp" + "strconv" "strings" "sync" + "time" - "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/search" "cmd/go/internal/str" @@ -29,11 +30,12 @@ import ( "golang.org/x/mod/module" ) -// A vcsCmd describes how to use a version control system +// A Cmd describes how to use a version control system // like Mercurial, Git, or Subversion. type Cmd struct { - Name string - Cmd string // name of binary to invoke command + Name string + Cmd string // name of binary to invoke command + RootNames []string // filename indicating the root of a checkout directory CreateCmd []string // commands to download a fresh copy of a repository DownloadCmd []string // commands to download updates into an existing repository @@ -48,6 +50,14 @@ type Cmd struct { RemoteRepo func(v *Cmd, rootDir string) (remoteRepo string, err error) ResolveRepo func(v *Cmd, rootDir, remoteRepo string) (realRepo string, err error) + Status func(v *Cmd, rootDir string) (Status, error) +} + +// Status is the current state of a local repository. +type Status struct { + Revision string // Optional. + CommitTime time.Time // Optional. + Uncommitted bool // Required. } var defaultSecureScheme = map[string]bool{ @@ -118,8 +128,9 @@ func vcsByCmd(cmd string) *Cmd { // vcsHg describes how to use Mercurial. var vcsHg = &Cmd{ - Name: "Mercurial", - Cmd: "hg", + Name: "Mercurial", + Cmd: "hg", + RootNames: []string{".hg"}, CreateCmd: []string{"clone -U -- {repo} {dir}"}, DownloadCmd: []string{"pull"}, @@ -139,6 +150,7 @@ var vcsHg = &Cmd{ Scheme: []string{"https", "http", "ssh"}, PingCmd: "identify -- {scheme}://{repo}", RemoteRepo: hgRemoteRepo, + Status: hgStatus, } func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) { @@ -149,10 +161,64 @@ func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) { return strings.TrimSpace(string(out)), nil } +func hgStatus(vcsHg *Cmd, rootDir string) (Status, error) { + // Output changeset ID and seconds since epoch. + out, err := vcsHg.runOutputVerboseOnly(rootDir, `log -l1 -T {node}:{date|hgdate}`) + if err != nil { + return Status{}, err + } + + // Successful execution without output indicates an empty repo (no commits). + var rev string + var commitTime time.Time + if len(out) > 0 { + // Strip trailing timezone offset. + if i := bytes.IndexByte(out, ' '); i > 0 { + out = out[:i] + } + rev, commitTime, err = parseRevTime(out) + if err != nil { + return Status{}, err + } + } + + // Also look for untracked files. + out, err = vcsHg.runOutputVerboseOnly(rootDir, "status") + if err != nil { + return Status{}, err + } + uncommitted := len(out) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + +// parseRevTime parses commit details in "revision:seconds" format. +func parseRevTime(out []byte) (string, time.Time, error) { + buf := string(bytes.TrimSpace(out)) + + i := strings.IndexByte(buf, ':') + if i < 1 { + return "", time.Time{}, errors.New("unrecognized VCS tool output") + } + rev := buf[:i] + + secs, err := strconv.ParseInt(string(buf[i+1:]), 10, 64) + if err != nil { + return "", time.Time{}, fmt.Errorf("unrecognized VCS tool output: %v", err) + } + + return rev, time.Unix(secs, 0), nil +} + // vcsGit describes how to use Git. var vcsGit = &Cmd{ - Name: "Git", - Cmd: "git", + Name: "Git", + Cmd: "git", + RootNames: []string{".git"}, CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"}, DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"}, @@ -182,6 +248,7 @@ var vcsGit = &Cmd{ PingCmd: "ls-remote {scheme}://{repo}", RemoteRepo: gitRemoteRepo, + Status: gitStatus, } // scpSyntaxRe matches the SCP-like addresses used by Git to access @@ -232,10 +299,40 @@ func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) { return "", errParse } +func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) { + out, err := vcsGit.runOutputVerboseOnly(rootDir, "status --porcelain") + if err != nil { + return Status{}, err + } + uncommitted := len(out) > 0 + + // "git status" works for empty repositories, but "git show" does not. + // Assume there are no commits in the repo when "git show" fails with + // uncommitted files and skip tagging revision / committime. + var rev string + var commitTime time.Time + out, err = vcsGit.runOutputVerboseOnly(rootDir, "-c log.showsignature=false show -s --format=%H:%ct") + if err != nil && !uncommitted { + return Status{}, err + } else if err == nil { + rev, commitTime, err = parseRevTime(out) + if err != nil { + return Status{}, err + } + } + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + // vcsBzr describes how to use Bazaar. var vcsBzr = &Cmd{ - Name: "Bazaar", - Cmd: "bzr", + Name: "Bazaar", + Cmd: "bzr", + RootNames: []string{".bzr"}, CreateCmd: []string{"branch -- {repo} {dir}"}, @@ -251,6 +348,7 @@ var vcsBzr = &Cmd{ PingCmd: "info -- {scheme}://{repo}", RemoteRepo: bzrRemoteRepo, ResolveRepo: bzrResolveRepo, + Status: bzrStatus, } func bzrRemoteRepo(vcsBzr *Cmd, rootDir string) (remoteRepo string, err error) { @@ -294,10 +392,68 @@ func bzrResolveRepo(vcsBzr *Cmd, rootDir, remoteRepo string) (realRepo string, e return strings.TrimSpace(out), nil } +func bzrStatus(vcsBzr *Cmd, rootDir string) (Status, error) { + outb, err := vcsBzr.runOutputVerboseOnly(rootDir, "version-info") + if err != nil { + return Status{}, err + } + out := string(outb) + + // Expect (non-empty repositories only): + // + // revision-id: gopher@gopher.net-20211021072330-qshok76wfypw9lpm + // date: 2021-09-21 12:00:00 +1000 + // ... + var rev string + var commitTime time.Time + + for _, line := range strings.Split(out, "\n") { + i := strings.IndexByte(line, ':') + if i < 0 { + continue + } + key := line[:i] + value := strings.TrimSpace(line[i+1:]) + + switch key { + case "revision-id": + rev = value + case "date": + var err error + commitTime, err = time.Parse("2006-01-02 15:04:05 -0700", value) + if err != nil { + return Status{}, errors.New("unable to parse output of bzr version-info") + } + } + } + + outb, err = vcsBzr.runOutputVerboseOnly(rootDir, "status") + if err != nil { + return Status{}, err + } + + // Skip warning when working directory is set to an older revision. + if bytes.HasPrefix(outb, []byte("working tree is out of date")) { + i := bytes.IndexByte(outb, '\n') + if i < 0 { + i = len(outb) + } + outb = outb[:i] + } + uncommitted := len(outb) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + // vcsSvn describes how to use Subversion. var vcsSvn = &Cmd{ - Name: "Subversion", - Cmd: "svn", + Name: "Subversion", + Cmd: "svn", + RootNames: []string{".svn"}, CreateCmd: []string{"checkout -- {repo} {dir}"}, DownloadCmd: []string{"update"}, @@ -346,8 +502,9 @@ const fossilRepoName = ".fossil" // vcsFossil describes how to use Fossil (fossil-scm.org) var vcsFossil = &Cmd{ - Name: "Fossil", - Cmd: "fossil", + Name: "Fossil", + Cmd: "fossil", + RootNames: []string{".fslckout", "_FOSSIL_"}, CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"}, DownloadCmd: []string{"up"}, @@ -358,6 +515,7 @@ var vcsFossil = &Cmd{ Scheme: []string{"https", "http"}, RemoteRepo: fossilRemoteRepo, + Status: fossilStatus, } func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err error) { @@ -368,6 +526,60 @@ func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err er return strings.TrimSpace(string(out)), nil } +var errFossilInfo = errors.New("unable to parse output of fossil info") + +func fossilStatus(vcsFossil *Cmd, rootDir string) (Status, error) { + outb, err := vcsFossil.runOutputVerboseOnly(rootDir, "info") + if err != nil { + return Status{}, err + } + out := string(outb) + + // Expect: + // ... + // checkout: 91ed71f22c77be0c3e250920f47bfd4e1f9024d2 2021-09-21 12:00:00 UTC + // ... + + // Extract revision and commit time. + // Ensure line ends with UTC (known timezone offset). + const prefix = "\ncheckout:" + const suffix = " UTC" + i := strings.Index(out, prefix) + if i < 0 { + return Status{}, errFossilInfo + } + checkout := out[i+len(prefix):] + i = strings.Index(checkout, suffix) + if i < 0 { + return Status{}, errFossilInfo + } + checkout = strings.TrimSpace(checkout[:i]) + + i = strings.IndexByte(checkout, ' ') + if i < 0 { + return Status{}, errFossilInfo + } + rev := checkout[:i] + + commitTime, err := time.ParseInLocation("2006-01-02 15:04:05", checkout[i+1:], time.UTC) + if err != nil { + return Status{}, fmt.Errorf("%v: %v", errFossilInfo, err) + } + + // Also look for untracked changes. + outb, err = vcsFossil.runOutputVerboseOnly(rootDir, "changes --differ") + if err != nil { + return Status{}, err + } + uncommitted := len(outb) > 0 + + return Status{ + Revision: rev, + CommitTime: commitTime, + Uncommitted: uncommitted, + }, nil +} + func (v *Cmd) String() string { return v.Name } @@ -395,6 +607,12 @@ func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error return v.run1(dir, cmd, keyval, true) } +// runOutputVerboseOnly is like runOutput but only generates error output to +// standard error in verbose mode. +func (v *Cmd) runOutputVerboseOnly(dir string, cmd string, keyval ...string) ([]byte, error) { + return v.run1(dir, cmd, keyval, false) +} + // run1 is the generalized implementation of run and runOutput. func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { m := make(map[string]string) @@ -438,7 +656,6 @@ func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([ cmd := exec.Command(v.Cmd, args...) cmd.Dir = dir - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) if cfg.BuildX { fmt.Fprintf(os.Stderr, "cd %s\n", dir) fmt.Fprintf(os.Stderr, "%s %s\n", v.Cmd, strings.Join(args, " ")) @@ -450,7 +667,7 @@ func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([ if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { os.Stderr.Write(ee.Stderr) } else { - fmt.Fprintf(os.Stderr, err.Error()) + fmt.Fprintln(os.Stderr, err.Error()) } } } @@ -459,14 +676,24 @@ func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([ // Ping pings to determine scheme to use. func (v *Cmd) Ping(scheme, repo string) error { - return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo) + // Run the ping command in an arbitrary working directory, + // but don't let the current working directory pollute the results. + // In module mode, we expect GOMODCACHE to exist and be a safe place for + // commands; in GOPATH mode, we expect that to be true of GOPATH/src. + dir := cfg.GOMODCACHE + if !cfg.ModulesEnabled { + dir = filepath.Join(cfg.BuildContext.GOPATH, "src") + } + os.MkdirAll(dir, 0777) // Ignore errors — if unsuccessful, the command will likely fail. + + return v.runVerboseOnly(dir, v.PingCmd, "scheme", scheme, "repo", repo) } // Create creates a new copy of repo in dir. // The parent of dir must exist; dir must not. func (v *Cmd) Create(dir, repo string) error { for _, cmd := range v.CreateCmd { - if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil { + if err := v.run(filepath.Dir(dir), cmd, "dir", dir, "repo", repo); err != nil { return err } } @@ -550,58 +777,86 @@ type vcsPath struct { // FromDir inspects dir and its parents to determine the // version control system and code repository to use. -// On return, root is the import path -// corresponding to the root of the repository. -func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) { +// If no repository is found, FromDir returns an error +// equivalent to os.ErrNotExist. +func FromDir(dir, srcRoot string, allowNesting bool) (repoDir string, vcsCmd *Cmd, err error) { // Clean and double-check that dir is in (a subdirectory of) srcRoot. dir = filepath.Clean(dir) - srcRoot = filepath.Clean(srcRoot) - if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { - return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + if srcRoot != "" { + srcRoot = filepath.Clean(srcRoot) + if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { + return "", nil, fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) + } } - var vcsRet *Cmd - var rootRet string - origDir := dir for len(dir) > len(srcRoot) { for _, vcs := range vcsList { - if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil { - root := filepath.ToSlash(dir[len(srcRoot)+1:]) - // Record first VCS we find, but keep looking, - // to detect mistakes like one kind of VCS inside another. - if vcsRet == nil { - vcsRet = vcs - rootRet = root + if _, err := statAny(dir, vcs.RootNames); err == nil { + // Record first VCS we find. + // If allowNesting is false (as it is in GOPATH), keep looking for + // repositories in parent directories and report an error if one is + // found to mitigate VCS injection attacks. + if vcsCmd == nil { + vcsCmd = vcs + repoDir = dir + if allowNesting { + return repoDir, vcsCmd, nil + } continue } // Allow .git inside .git, which can arise due to submodules. - if vcsRet == vcs && vcs.Cmd == "git" { + if vcsCmd == vcs && vcs.Cmd == "git" { continue } // Otherwise, we have one VCS inside a different VCS. - return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s", - filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd) + return "", nil, fmt.Errorf("directory %q uses %s, but parent %q uses %s", + repoDir, vcsCmd.Cmd, dir, vcs.Cmd) } } // Move to parent. ndir := filepath.Dir(dir) if len(ndir) >= len(dir) { - // Shouldn't happen, but just in case, stop. break } dir = ndir } + if vcsCmd == nil { + return "", nil, &vcsNotFoundError{dir: origDir} + } + return repoDir, vcsCmd, nil +} + +// statAny provides FileInfo for the first filename found in the directory. +// Otherwise, it returns the last error seen. +func statAny(dir string, filenames []string) (os.FileInfo, error) { + if len(filenames) == 0 { + return nil, errors.New("invalid argument: no filenames provided") + } - if vcsRet != nil { - if err := checkGOVCS(vcsRet, rootRet); err != nil { - return nil, "", err + var err error + var fi os.FileInfo + for _, name := range filenames { + fi, err = os.Stat(filepath.Join(dir, name)) + if err == nil { + return fi, nil } - return vcsRet, rootRet, nil } - return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir) + return nil, err +} + +type vcsNotFoundError struct { + dir string +} + +func (e *vcsNotFoundError) Error() string { + return fmt.Sprintf("directory %q is not using a known version control system", e.dir) +} + +func (e *vcsNotFoundError) Is(err error) bool { + return err == os.ErrNotExist } // A govcsRule is a single GOVCS rule like private:hg|svn. @@ -707,7 +962,11 @@ var defaultGOVCS = govcsConfig{ {"public", []string{"git", "hg"}}, } -func checkGOVCS(vcs *Cmd, root string) error { +// CheckGOVCS checks whether the policy defined by the environment variable +// GOVCS allows the given vcs command to be used with the given repository +// root path. Note that root may not be a real package or module path; it's +// the same as the root path in the go-import meta tag. +func CheckGOVCS(vcs *Cmd, root string) error { if vcs == vcsMod { // Direct module (proxy protocol) fetches don't // involve an external version control system @@ -745,7 +1004,7 @@ func CheckNested(vcs *Cmd, dir, srcRoot string) error { otherDir := dir for len(otherDir) > len(srcRoot) { for _, otherVCS := range vcsList { - if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil { + if _, err := statAny(otherDir, otherVCS.RootNames); err == nil { // Allow expected vcs in original dir. if otherDir == dir && otherVCS == vcs { continue @@ -885,7 +1144,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths if vcs == nil { return nil, fmt.Errorf("unknown version control system %q", match["vcs"]) } - if err := checkGOVCS(vcs, match["root"]); err != nil { + if err := CheckGOVCS(vcs, match["root"]); err != nil { return nil, err } var repoURL string @@ -1012,7 +1271,7 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se } } - if err := checkGOVCS(vcs, mmi.Prefix); err != nil { + if err := CheckGOVCS(vcs, mmi.Prefix); err != nil { return nil, err } @@ -1063,7 +1322,7 @@ func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.Secu return res, nil } - resi, _, _ := fetchGroup.Do(importPrefix, func() (resi interface{}, err error) { + resi, _, _ := fetchGroup.Do(importPrefix, func() (resi any, err error) { fetchCacheMu.Lock() if res, ok := fetchCache[importPrefix]; ok { fetchCacheMu.Unlock() @@ -1189,8 +1448,9 @@ var vcsPaths = []*vcsPath{ { pathPrefix: "bitbucket.org", regexp: lazyregexp.New(`^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`), + vcs: "git", repo: "https://{root}", - check: bitbucketVCS, + check: noVCSSuffix, }, // IBM DevOps Services (JazzHub) @@ -1262,56 +1522,6 @@ func noVCSSuffix(match map[string]string) error { return nil } -// bitbucketVCS determines the version control system for a -// Bitbucket repository, by using the Bitbucket API. -func bitbucketVCS(match map[string]string) error { - if err := noVCSSuffix(match); err != nil { - return err - } - - var resp struct { - SCM string `json:"scm"` - } - url := &urlpkg.URL{ - Scheme: "https", - Host: "api.bitbucket.org", - Path: expand(match, "/2.0/repositories/{bitname}"), - RawQuery: "fields=scm", - } - data, err := web.GetBytes(url) - if err != nil { - if httpErr, ok := err.(*web.HTTPError); ok && httpErr.StatusCode == 403 { - // this may be a private repository. If so, attempt to determine which - // VCS it uses. See issue 5375. - root := match["root"] - for _, vcs := range []string{"git", "hg"} { - if vcsByCmd(vcs).Ping("https", root) == nil { - resp.SCM = vcs - break - } - } - } - - if resp.SCM == "" { - return err - } - } else { - if err := json.Unmarshal(data, &resp); err != nil { - return fmt.Errorf("decoding %s: %v", url, err) - } - } - - if vcsByCmd(resp.SCM) != nil { - match["vcs"] = resp.SCM - if resp.SCM == "git" { - match["repo"] += ".git" - } - return nil - } - - return fmt.Errorf("unable to detect version control system for bitbucket.org/ path") -} - // launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case, // "foo" could be a series name registered in Launchpad with its own branch, // and it could also be the name of a directory within the main project @@ -1340,7 +1550,7 @@ type importError struct { err error } -func importErrorf(path, format string, args ...interface{}) error { +func importErrorf(path, format string, args ...any) error { err := &importError{importPath: path, err: fmt.Errorf(format, args...)} if errStr := err.Error(); !strings.Contains(errStr, path) { panic(fmt.Sprintf("path %q not in error %q", path, errStr)) diff --git a/src/cmd/go/internal/vcs/vcs_test.go b/src/cmd/go/internal/vcs/vcs_test.go index c5c7a3283bce57..943d520d547461 100644 --- a/src/cmd/go/internal/vcs/vcs_test.go +++ b/src/cmd/go/internal/vcs/vcs_test.go @@ -6,9 +6,9 @@ package vcs import ( "errors" + "fmt" "internal/testenv" "os" - "path" "path/filepath" "strings" "testing" @@ -183,6 +183,13 @@ func TestRepoRootForImportPath(t *testing.T) { "chiselapp.com/user/kyle/fossilgg", nil, }, + { + "bitbucket.org/workspace/pkgname", + &RepoRoot{ + VCS: vcsGit, + Repo: "https://bitbucket.org/workspace/pkgname", + }, + }, } for _, test := range tests { @@ -205,7 +212,8 @@ func TestRepoRootForImportPath(t *testing.T) { } } -// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root. +// Test that vcs.FromDir correctly inspects a given directory and returns the +// right VCS and repo directory. func TestFromDir(t *testing.T) { tempDir, err := os.MkdirTemp("", "vcstest") if err != nil { @@ -214,36 +222,35 @@ func TestFromDir(t *testing.T) { defer os.RemoveAll(tempDir) for j, vcs := range vcsList { - dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd) - if j&1 == 0 { - err := os.MkdirAll(dir, 0755) - if err != nil { - t.Fatal(err) + for r, rootName := range vcs.RootNames { + vcsName := fmt.Sprint(vcs.Name, r) + dir := filepath.Join(tempDir, "example.com", vcsName, rootName) + if j&1 == 0 { + err := os.MkdirAll(dir, 0755) + if err != nil { + t.Fatal(err) + } + } else { + err := os.MkdirAll(filepath.Dir(dir), 0755) + if err != nil { + t.Fatal(err) + } + f, err := os.Create(dir) + if err != nil { + t.Fatal(err) + } + f.Close() } - } else { - err := os.MkdirAll(filepath.Dir(dir), 0755) + + wantRepoDir := filepath.Dir(dir) + gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false) if err != nil { - t.Fatal(err) + t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) + continue } - f, err := os.Create(dir) - if err != nil { - t.Fatal(err) + if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name { + t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name) } - f.Close() - } - - want := RepoRoot{ - VCS: vcs, - Root: path.Join("example.com", vcs.Name), - } - var got RepoRoot - got.VCS, got.Root, err = FromDir(dir, tempDir) - if err != nil { - t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) - continue - } - if got.VCS.Name != want.VCS.Name || got.Root != want.Root { - t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root) } } } diff --git a/src/cmd/go/internal/version/exe.go b/src/cmd/go/internal/version/exe.go deleted file mode 100644 index 0e7deef1491a9c..00000000000000 --- a/src/cmd/go/internal/version/exe.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package version - -import ( - "bytes" - "debug/elf" - "debug/macho" - "debug/pe" - "fmt" - "internal/xcoff" - "io" - "os" -) - -// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). -type exe interface { - // Close closes the underlying file. - Close() error - - // ReadData reads and returns up to size byte starting at virtual address addr. - ReadData(addr, size uint64) ([]byte, error) - - // DataStart returns the writable data segment start address. - DataStart() uint64 -} - -// openExe opens file and returns it as an exe. -func openExe(file string) (exe, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - data := make([]byte, 16) - if _, err := io.ReadFull(f, data); err != nil { - return nil, err - } - f.Seek(0, 0) - if bytes.HasPrefix(data, []byte("\x7FELF")) { - e, err := elf.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &elfExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte("MZ")) { - e, err := pe.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &peExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { - e, err := macho.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &machoExe{f, e}, nil - } - if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) { - e, err := xcoff.NewFile(f) - if err != nil { - f.Close() - return nil, err - } - return &xcoffExe{f, e}, nil - - } - return nil, fmt.Errorf("unrecognized executable format") -} - -// elfExe is the ELF implementation of the exe interface. -type elfExe struct { - os *os.File - f *elf.File -} - -func (x *elfExe) Close() error { - return x.os.Close() -} - -func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { - for _, prog := range x.f.Progs { - if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { - n := prog.Vaddr + prog.Filesz - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *elfExe) DataStart() uint64 { - for _, s := range x.f.Sections { - if s.Name == ".go.buildinfo" { - return s.Addr - } - } - for _, p := range x.f.Progs { - if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W { - return p.Vaddr - } - } - return 0 -} - -// peExe is the PE (Windows Portable Executable) implementation of the exe interface. -type peExe struct { - os *os.File - f *pe.File -} - -func (x *peExe) Close() error { - return x.os.Close() -} - -func (x *peExe) imageBase() uint64 { - switch oh := x.f.OptionalHeader.(type) { - case *pe.OptionalHeader32: - return uint64(oh.ImageBase) - case *pe.OptionalHeader64: - return oh.ImageBase - } - return 0 -} - -func (x *peExe) ReadData(addr, size uint64) ([]byte, error) { - addr -= x.imageBase() - for _, sect := range x.f.Sections { - if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { - n := uint64(sect.VirtualAddress+sect.Size) - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *peExe) DataStart() uint64 { - // Assume data is first writable section. - const ( - IMAGE_SCN_CNT_CODE = 0x00000020 - IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 - IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 - IMAGE_SCN_MEM_EXECUTE = 0x20000000 - IMAGE_SCN_MEM_READ = 0x40000000 - IMAGE_SCN_MEM_WRITE = 0x80000000 - IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 - IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 - IMAGE_SCN_ALIGN_32BYTES = 0x600000 - ) - for _, sect := range x.f.Sections { - if sect.VirtualAddress != 0 && sect.Size != 0 && - sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE { - return uint64(sect.VirtualAddress) + x.imageBase() - } - } - return 0 -} - -// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface. -type machoExe struct { - os *os.File - f *macho.File -} - -func (x *machoExe) Close() error { - return x.os.Close() -} - -func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { - for _, load := range x.f.Loads { - seg, ok := load.(*macho.Segment) - if !ok { - continue - } - if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 { - if seg.Name == "__PAGEZERO" { - continue - } - n := seg.Addr + seg.Filesz - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := seg.ReadAt(data, int64(addr-seg.Addr)) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *machoExe) DataStart() uint64 { - // Look for section named "__go_buildinfo". - for _, sec := range x.f.Sections { - if sec.Name == "__go_buildinfo" { - return sec.Addr - } - } - // Try the first non-empty writable segment. - const RW = 3 - for _, load := range x.f.Loads { - seg, ok := load.(*macho.Segment) - if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW { - return seg.Addr - } - } - return 0 -} - -// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface. -type xcoffExe struct { - os *os.File - f *xcoff.File -} - -func (x *xcoffExe) Close() error { - return x.os.Close() -} - -func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) { - for _, sect := range x.f.Sections { - if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) { - n := uint64(sect.VirtualAddress+sect.Size) - addr - if n > size { - n = size - } - data := make([]byte, n) - _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress))) - if err != nil { - return nil, err - } - return data, nil - } - } - return nil, fmt.Errorf("address not mapped") -} - -func (x *xcoffExe) DataStart() uint64 { - return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress -} diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go index 58cbd32e78d115..5de7b83efaea83 100644 --- a/src/cmd/go/internal/version/version.go +++ b/src/cmd/go/internal/version/version.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package version implements the ``go version'' command. +// Package version implements the “go version” command. package version import ( - "bytes" "context" - "encoding/binary" + "debug/buildinfo" + "errors" "fmt" "io/fs" "os" @@ -62,8 +62,14 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) { // a reasonable use case. For example, imagine GOFLAGS=-v to // turn "verbose mode" on for all Go commands, which should not // break "go version". - if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) { - fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n") + var argOnlyFlag string + if !base.InGOFLAGS("-m") && *versionM { + argOnlyFlag = "-m" + } else if !base.InGOFLAGS("-v") && *versionV { + argOnlyFlag = "-v" + } + if argOnlyFlag != "" { + fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag) base.SetExitStatus(2) return } @@ -135,90 +141,22 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) { return } - x, err := openExe(file) + bi, err := buildinfo.ReadFile(file) if err != nil { if mustPrint { - fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) - } - return - } - defer x.Close() - - vers, mod := findVers(x) - if vers == "" { - if mustPrint { - fmt.Fprintf(os.Stderr, "%s: go version not found\n", file) + if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) { + fmt.Fprintf(os.Stderr, "%v\n", file) + } else { + fmt.Fprintf(os.Stderr, "%s: %v\n", file, err) + } } return } - fmt.Printf("%s: %s\n", file, vers) - if *versionM && mod != "" { + fmt.Printf("%s: %s\n", file, bi.GoVersion) + bi.GoVersion = "" // suppress printing go version again + mod := bi.String() + if *versionM && len(mod) > 0 { fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t")) } } - -// The build info blob left by the linker is identified by -// a 16-byte header, consisting of buildInfoMagic (14 bytes), -// the binary's pointer size (1 byte), -// and whether the binary is big endian (1 byte). -var buildInfoMagic = []byte("\xff Go buildinf:") - -// findVers finds and returns the Go version and module version information -// in the executable x. -func findVers(x exe) (vers, mod string) { - // Read the first 64kB of text to find the build info blob. - text := x.DataStart() - data, err := x.ReadData(text, 64*1024) - if err != nil { - return - } - for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] { - if len(data) < 32 { - return - } - } - - // Decode the blob. - ptrSize := int(data[14]) - bigEndian := data[15] != 0 - var bo binary.ByteOrder - if bigEndian { - bo = binary.BigEndian - } else { - bo = binary.LittleEndian - } - var readPtr func([]byte) uint64 - if ptrSize == 4 { - readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) } - } else { - readPtr = bo.Uint64 - } - vers = readString(x, ptrSize, readPtr, readPtr(data[16:])) - if vers == "" { - return - } - mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:])) - if len(mod) >= 33 && mod[len(mod)-17] == '\n' { - // Strip module framing. - mod = mod[16 : len(mod)-16] - } else { - mod = "" - } - return -} - -// readString returns the string at address addr in the executable x. -func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string { - hdr, err := x.ReadData(addr, uint64(2*ptrSize)) - if err != nil || len(hdr) < 2*ptrSize { - return "" - } - dataAddr := readPtr(hdr) - dataLen := readPtr(hdr[ptrSize:]) - data, err := x.ReadData(dataAddr, dataLen) - if err != nil || uint64(len(data)) < dataLen { - return "" - } - return string(data) -} diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go index 1d419dddb98d6c..ee672d1a30d8a0 100644 --- a/src/cmd/go/internal/vet/vet.go +++ b/src/cmd/go/internal/vet/vet.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 vet implements the ``go vet'' command. +// Package vet implements the “go vet” command. package vet import ( @@ -13,6 +13,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/modload" "cmd/go/internal/trace" "cmd/go/internal/work" ) @@ -54,6 +55,7 @@ See also: go fmt, go fix. func runVet(ctx context.Context, cmd *base.Command, args []string) { vetFlags, pkgArgs := vetFlags(args) + modload.InitWorkfile() // The vet command does custom flag processing; initialize workspaces after that. if cfg.DebugTrace != "" { var close func() error @@ -92,8 +94,12 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) { base.Fatalf("no packages to vet") } - var b work.Builder - b.Init() + b := work.NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() root := &work.Action{Mode: "go vet"} for _, p := range pkgs { @@ -103,7 +109,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) { continue } if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil { - base.Errorf("go vet %s: no Go files in %s", p.ImportPath, p.Dir) + base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir) continue } if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 { diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go index b5b3c462ff2acc..eb7af6508d00be 100644 --- a/src/cmd/go/internal/vet/vetflag.go +++ b/src/cmd/go/internal/vet/vetflag.go @@ -10,9 +10,9 @@ import ( "errors" "flag" "fmt" - exec "internal/execabs" "log" "os" + "os/exec" "path/filepath" "strings" @@ -35,7 +35,6 @@ import ( // implementation. It is also used by tests. // // The default behavior (vetTool=="") runs 'go tool vet'. -// var vetTool string // -vettool func init() { @@ -82,7 +81,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { vetcmd := exec.Command(tool, "-flags") vetcmd.Stdout = out if err := vetcmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err) + fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err) base.SetExitStatus(2) base.Exit() } @@ -92,7 +91,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { Usage string } if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err) + fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err) base.SetExitStatus(2) base.Exit() } diff --git a/src/cmd/go/internal/web/bootstrap.go b/src/cmd/go/internal/web/bootstrap.go index 08686cdfcf9f4c..ab88e9e4781f46 100644 --- a/src/cmd/go/internal/web/bootstrap.go +++ b/src/cmd/go/internal/web/bootstrap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cmd_go_bootstrap -// +build cmd_go_bootstrap // This code is compiled only into the bootstrap 'go' binary. // These stubs avoid importing packages with large dependency diff --git a/src/cmd/go/internal/web/http.go b/src/cmd/go/internal/web/http.go index f177278eba1e7d..a92326db01e629 100644 --- a/src/cmd/go/internal/web/http.go +++ b/src/cmd/go/internal/web/http.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !cmd_go_bootstrap -// +build !cmd_go_bootstrap // This code is compiled into the real 'go' binary, but it is not // compiled into the binary that is built during all.bash, so as @@ -17,6 +16,7 @@ import ( "errors" "fmt" "mime" + "net" "net/http" urlpkg "net/url" "os" @@ -84,8 +84,15 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) { if url.Host == "localhost.localdev" { return nil, fmt.Errorf("no such host localhost.localdev") } - if os.Getenv("TESTGONETWORK") == "panic" && !strings.HasPrefix(url.Host, "127.0.0.1") && !strings.HasPrefix(url.Host, "0.0.0.0") { - panic("use of network: " + url.String()) + if os.Getenv("TESTGONETWORK") == "panic" { + host := url.Host + if h, _, err := net.SplitHostPort(url.Host); err == nil && h != "" { + host = h + } + addr := net.ParseIP(host) + if addr == nil || (!addr.IsLoopback() && !addr.IsUnspecified()) { + panic("use of network: " + url.String()) + } } fetch := func(url *urlpkg.URL) (*urlpkg.URL, *http.Response, error) { diff --git a/src/cmd/go/internal/web/url_other.go b/src/cmd/go/internal/web/url_other.go index 453af402b43dd9..84bbd72820fcab 100644 --- a/src/cmd/go/internal/web/url_other.go +++ b/src/cmd/go/internal/web/url_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package web diff --git a/src/cmd/go/internal/web/url_other_test.go b/src/cmd/go/internal/web/url_other_test.go index 4d6ed2ec7f8c1d..5c197de800dc1f 100644 --- a/src/cmd/go/internal/web/url_other_test.go +++ b/src/cmd/go/internal/web/url_other_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package web diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go index 69940cb00159b6..ae9afd2f123fb7 100644 --- a/src/cmd/go/internal/work/action.go +++ b/src/cmd/go/internal/work/action.go @@ -16,7 +16,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strings" "sync" "time" @@ -25,6 +24,7 @@ import ( "cmd/go/internal/cache" "cmd/go/internal/cfg" "cmd/go/internal/load" + "cmd/go/internal/robustio" "cmd/go/internal/trace" "cmd/internal/buildid" ) @@ -37,7 +37,7 @@ type Builder struct { actionCache map[cacheKey]*Action // a cache of already-constructed actions mkdirCache map[string]bool // a cache of created directories flagCache map[[2]string]bool // a cache of supported compiler flags - Print func(args ...interface{}) (int, error) + Print func(args ...any) (int, error) IsCmdList bool // running as part of go list; set p.Stale and additional fields below NeedError bool // list needs p.Error @@ -120,8 +120,8 @@ type actionQueue []*Action func (q *actionQueue) Len() int { return len(*q) } func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] } func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority } -func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*Action)) } -func (q *actionQueue) Pop() interface{} { +func (q *actionQueue) Push(x any) { *q = append(*q, x.(*Action)) } +func (q *actionQueue) Pop() any { n := len(*q) - 1 x := (*q)[n] *q = (*q)[:n] @@ -240,8 +240,16 @@ const ( ModeVetOnly = 1 << 8 ) -func (b *Builder) Init() { - b.Print = func(a ...interface{}) (int, error) { +// NewBuilder returns a new Builder ready for use. +// +// If workDir is the empty string, NewBuilder creates a WorkDir if needed +// and arranges for it to be removed in case of an unclean exit. +// The caller must Close the builder explicitly to clean up the WorkDir +// before a clean exit. +func NewBuilder(workDir string) *Builder { + b := new(Builder) + + b.Print = func(a ...any) (int, error) { return fmt.Fprint(os.Stderr, a...) } b.actionCache = make(map[cacheKey]*Action) @@ -249,9 +257,14 @@ func (b *Builder) Init() { b.toolIDCache = make(map[string]string) b.buildIDCache = make(map[string]string) - if cfg.BuildN { + if workDir != "" { + b.WorkDir = workDir + } else if cfg.BuildN { b.WorkDir = "$WORK" } else { + if !buildInitStarted { + panic("internal error: NewBuilder called before BuildInit") + } tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build") if err != nil { base.Fatalf("go: creating work dir: %v", err) @@ -265,47 +278,65 @@ func (b *Builder) Init() { tmp = abs } b.WorkDir = tmp + builderWorkDirs.Store(b, b.WorkDir) if cfg.BuildX || cfg.BuildWork { fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir) } - if !cfg.BuildWork { - workdir := b.WorkDir - base.AtExit(func() { - start := time.Now() - for { - err := os.RemoveAll(workdir) - if err == nil { - return - } - - // On some configurations of Windows, directories containing executable - // files may be locked for a while after the executable exits (perhaps - // due to antivirus scans?). It's probably worth a little extra latency - // on exit to avoid filling up the user's temporary directory with leaked - // files. (See golang.org/issue/30789.) - if runtime.GOOS != "windows" || time.Since(start) >= 500*time.Millisecond { - fmt.Fprintf(os.Stderr, "go: failed to remove work dir: %s\n", err) - return - } - time.Sleep(5 * time.Millisecond) - } - }) - } } if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil { - fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) + fmt.Fprintf(os.Stderr, "go: %v\n", err) base.SetExitStatus(2) base.Exit() } for _, tag := range cfg.BuildContext.BuildTags { if strings.Contains(tag, ",") { - fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n") + fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n") base.SetExitStatus(2) base.Exit() } } + + return b +} + +var builderWorkDirs sync.Map // *Builder → WorkDir + +func (b *Builder) Close() error { + wd, ok := builderWorkDirs.Load(b) + if !ok { + return nil + } + defer builderWorkDirs.Delete(b) + + if b.WorkDir != wd.(string) { + base.Errorf("go: internal error: Builder WorkDir unexpectedly changed from %s to %s", wd, b.WorkDir) + } + + if !cfg.BuildWork { + if err := robustio.RemoveAll(b.WorkDir); err != nil { + return err + } + } + b.WorkDir = "" + return nil +} + +func closeBuilders() { + leakedBuilders := 0 + builderWorkDirs.Range(func(bi, _ any) bool { + leakedBuilders++ + if err := bi.(*Builder).Close(); err != nil { + base.Errorf("go: %v", err) + } + return true + }) + + if leakedBuilders > 0 && base.GetExitStatus() == 0 { + fmt.Fprintf(os.Stderr, "go: internal error: Builder leaked on successful exit\n") + base.SetExitStatus(1) + } } func CheckGOOSARCHPair(goos, goarch string) error { diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 0ed2389cd5a81a..bce923a4595511 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -9,10 +9,11 @@ import ( "errors" "fmt" "go/build" - exec "internal/execabs" "os" + "os/exec" "path/filepath" "runtime" + "strconv" "strings" "cmd/go/internal/base" @@ -68,13 +69,18 @@ and test commands: The default is GOMAXPROCS, normally the number of CPUs available. -race enable data race detection. - Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64, + Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64, linux/ppc64le and linux/arm64 (only for 48-bit VMA). -msan enable interoperation with memory sanitizer. Supported only on linux/amd64, linux/arm64 and only with Clang/LLVM as the host C compiler. On linux/arm64, pie build mode will be used. + -asan + enable interoperation with address sanitizer. + Supported only on linux/arm64, linux/amd64. + Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher + or Clang/LLVM 9 and higher. -v print the names of packages as they are compiled. -work @@ -87,6 +93,14 @@ and test commands: arguments to pass on each go tool asm invocation. -buildmode mode build mode to use. See 'go help buildmode' for more. + -buildvcs + Whether to stamp binaries with version control information + ("true", "false", or "auto"). By default ("auto"), version control + information is stamped into a binary if the main package, the main module + containing it, and the current directory are all in the same repository. + Use -buildvcs=false to always omit version control information, or + -buildvcs=true to error out if version control information is available but + cannot be included due to a missing tool or ambiguous directory structure. -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc). -gccgoflags '[pattern=]arg list' @@ -98,8 +112,8 @@ and test commands: in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to race or, if set explicitly, has _race appended to it. Likewise for the -msan - flag. Using a -buildmode option that requires non-default compile flags - has a similar effect. + and -asan flags. Using a -buildmode option that requires non-default compile + flags has a similar effect. -ldflags '[pattern=]arg list' arguments to pass on each go tool link invocation. -linkshared @@ -137,17 +151,15 @@ and test commands: For example, when building with a non-standard configuration, use -pkgdir to keep generated packages in a separate location. -tags tag,list - a comma-separated list of build tags to consider satisfied during the - build. For more information about build tags, see the description of - build constraints in the documentation for the go/build package. - (Earlier versions of Go used a space-separated list, and that form - is deprecated but still recognized.) + a comma-separated list of additional build tags to consider satisfied + during the build. For more information about build tags, see + 'go help buildconstraint'. (Earlier versions of Go used a + space-separated list, and that form is deprecated but still recognized.) -trimpath remove all file system paths from the resulting executable. Instead of absolute file system paths, the recorded file names - will begin with either "go" (for the standard library), - or a module path@version (when using modules), - or a plain import path (when using GOPATH). + will begin either a module path@version (when using modules), + or a plain import path (when using the standard library, or GOPATH). -toolexec 'cmd args' a program to use to invoke toolchain programs like vet and asm. For example, instead of running asm, the go command will run @@ -289,10 +301,12 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) { cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "") cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "") cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "") + cmd.Flag.BoolVar(&cfg.BuildASan, "asan", false, "") cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "") cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "") cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "") cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "") + cmd.Flag.Var((*buildvcsFlag)(&cfg.BuildBuildvcs), "buildvcs", "") // Undocumented, unstable debugging flags. cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "") @@ -322,6 +336,29 @@ func (v *tagsFlag) String() string { return "" } +// buildvcsFlag is the implementation of the -buildvcs flag. +type buildvcsFlag string + +func (f *buildvcsFlag) IsBoolFlag() bool { return true } // allow -buildvcs (without arguments) + +func (f *buildvcsFlag) Set(s string) error { + // https://go.dev/issue/51748: allow "-buildvcs=auto", + // in addition to the usual "true" and "false". + if s == "" || s == "auto" { + *f = "auto" + return nil + } + + b, err := strconv.ParseBool(s) + if err != nil { + return errors.New("value is neither 'auto' nor a valid bool") + } + *f = (buildvcsFlag)(strconv.FormatBool(b)) // convert to canonical "true" or "false" + return nil +} + +func (f *buildvcsFlag) String() string { return string(*f) } + // fileExtSplit expects a filename and returns the name // and ext (without the dot). If the file has no // extension, ext will be empty. @@ -361,14 +398,19 @@ func oneMainPkg(pkgs []*load.Package) []*load.Package { var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs } -var runtimeVersion = runtime.Version() +var RuntimeVersion = runtime.Version() func runBuild(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() BuildInit() - var b Builder - b.Init() + b := NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() - pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) + pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{AutoVCS: true}, args) load.CheckPackageErrors(pkgs) explicitO := len(cfg.BuildO) > 0 @@ -396,7 +438,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { depMode := ModeBuild if cfg.BuildI { depMode = ModeInstall - fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } pkgs = omitTestOnly(pkgsFilter(pkgs)) @@ -415,7 +457,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { strings.HasSuffix(cfg.BuildO, "/") || strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) { if !explicitO { - base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO) + base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO) } a := &Action{Mode: "go build"} for _, p := range pkgs { @@ -430,13 +472,13 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p)) } if len(a.Deps) == 0 { - base.Fatalf("go build: no main packages to build") + base.Fatalf("go: no main packages to build") } b.Do(ctx, a) return } if len(pkgs) > 1 { - base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO) + base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO) } else if len(pkgs) == 0 { base.Fatalf("no packages to build") } @@ -486,14 +528,17 @@ allowed, even if they refer to the same version. - All arguments must refer to packages in the same module at the same version. +- Package path arguments must refer to main packages. Pattern arguments +will only match main packages. + - No module is considered the "main" module. If the module containing packages named on the command line has a go.mod file, it must not contain directives (replace and exclude) that would cause it to be interpreted differently than if it were the main module. The module must not require a higher version of itself. -- Package path arguments must refer to main packages. Pattern arguments -will only match main packages. +- Vendor directories are not used in any module. (Vendor directories are not +included in the module zip files downloaded by 'go install'.) If the arguments don't have version suffixes, "go install" may run in module-aware mode or GOPATH mode, depending on the GO111MODULE environment @@ -518,16 +563,22 @@ See also: go build, go get, go clean. // libname returns the filename to use for the shared library when using // -buildmode=shared. The rules we use are: // Use arguments for special 'meta' packages: +// // std --> libstd.so // std cmd --> libstd,cmd.so +// // A single non-meta argument with trailing "/..." is special cased: +// // foo/... --> libfoo.so // (A relative path like "./..." expands the "." first) +// // Use import paths for other cases, changing '/' to '-': +// // somelib --> libsubdir-somelib.so // ./ or ../ --> libsubdir-somelib.so // gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so // a/... b/... ---> liba/c,b/d.so - all matching import paths +// // Name parts are joined with ','. func libname(args []string, pkgs []*load.Package) (string, error) { var libname string @@ -580,15 +631,16 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { for _, arg := range args { if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { if cfg.BuildI { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } installOutsideModule(ctx, args) return } } + modload.InitWorkfile() BuildInit() - pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args) + pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{AutoVCS: true}, args) if cfg.ModulesEnabled && !modload.HasModRoot() { haveErrors := false allMissingErrors := true @@ -608,7 +660,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { latestArgs[i] = args[i] + "@latest" } hint := strings.Join(latestArgs, " ") - base.Fatalf("go install: version is required when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) + base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) } } load.CheckPackageErrors(pkgs) @@ -621,7 +673,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { } } if !allGoroot { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprintf(os.Stderr, "go: -i flag is deprecated\n") } } @@ -667,21 +719,26 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag case p.Name != "main" && p.Module != nil: // Non-executables have no target (except the cache) when building with modules. case p.Internal.GobinSubdir: - base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName) + base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set") case p.Internal.CmdlineFiles: - base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName) + base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)") case p.ConflictDir != "": - base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir) + base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) default: - base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+ - "\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir) + base.Errorf("go: no install location for directory %s outside GOPATH\n"+ + "\tFor more details see: 'go help gopath'", p.Dir) } } } base.ExitIfErrors() - var b Builder - b.Init() + b := NewBuilder("") + defer func() { + if err := b.Close(); err != nil { + base.Fatalf("go: %v", err) + } + }() + depMode := ModeBuild if cfg.BuildI { depMode = ModeInstall @@ -769,7 +826,7 @@ func installOutsideModule(ctx context.Context, args []string) { pkgOpts := load.PackageOpts{MainOnly: true} pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args) if err != nil { - base.Fatalf("go install: %v", err) + base.Fatalf("go: %v", err) } load.CheckPackageErrors(pkgs) patterns := make([]string, len(args)) diff --git a/src/cmd/go/internal/work/build_test.go b/src/cmd/go/internal/work/build_test.go index 600fc3083f0d50..0b6b83a706cd40 100644 --- a/src/cmd/go/internal/work/build_test.go +++ b/src/cmd/go/internal/work/build_test.go @@ -234,7 +234,7 @@ func TestRespectSetgidDir(t *testing.T) { // of `(*Builder).ShowCmd` afterwards as a sanity check. cfg.BuildX = true var cmdBuf bytes.Buffer - b.Print = func(a ...interface{}) (int, error) { + b.Print = func(a ...any) (int, error) { return cmdBuf.WriteString(fmt.Sprint(a...)) } diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index 4e9189a36320ac..a5b5570e05d047 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -7,8 +7,8 @@ package work import ( "bytes" "fmt" - exec "internal/execabs" "os" + "os/exec" "strings" "cmd/go/internal/base" @@ -160,18 +160,20 @@ func (b *Builder) toolID(name string) string { cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full") cmd := exec.Command(cmdline[0], cmdline[1:]...) - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { - base.Fatalf("%s: %v\n%s%s", desc, err, stdout.Bytes(), stderr.Bytes()) + if stderr.Len() > 0 { + os.Stderr.Write(stderr.Bytes()) + } + base.Fatalf("go: error obtaining buildID for %s: %v", desc, err) } line := stdout.String() f := strings.Fields(line) if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") { - base.Fatalf("%s -V=full: unexpected output:\n\t%s", desc, line) + base.Fatalf("go: parsing buildID from %s -V=full: unexpected output:\n\t%s", desc, line) } if f[2] == "devel" { // On the development branch, use the content ID part of the build ID. @@ -219,9 +221,8 @@ func (b *Builder) gccToolID(name, language string) (string, error) { // compile an empty file on standard input. cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-") cmd := exec.Command(cmdline[0], cmdline[1:]...) - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) // Force untranslated output so that we see the string "version". - cmd.Env = append(cmd.Env, "LC_ALL=C") + cmd.Env = append(os.Environ(), "LC_ALL=C") out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("%s: %v; output: %q", name, err, out) @@ -570,6 +571,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID)))) } if !cfg.BuildN { + b.output.Lock() + defer b.output.Unlock() b.Print(string(stdout)) } } @@ -578,6 +581,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) // flushOutput flushes the output being queued in a. func (b *Builder) flushOutput(a *Action) { + b.output.Lock() + defer b.output.Unlock() b.Print(string(a.output)) a.output = nil } diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 5a225fb9f1f626..bba6e452ed4080 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -12,17 +12,17 @@ import ( "encoding/json" "errors" "fmt" - "internal/buildcfg" - exec "internal/execabs" "internal/lazyregexp" "io" "io/fs" "log" "math/rand" "os" + "os/exec" "path/filepath" "regexp" "runtime" + "sort" "strconv" "strings" "sync" @@ -36,6 +36,8 @@ import ( "cmd/go/internal/modload" "cmd/go/internal/str" "cmd/go/internal/trace" + "cmd/internal/quoted" + "cmd/internal/sys" ) // actionList returns the list of actions in the dag rooted at root @@ -222,18 +224,34 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { // same compiler settings and can reuse each other's results. // If not, the reason is already recorded in buildGcflags. fmt.Fprintf(h, "compile\n") - // Only include the package directory if it may affect the output. - // We trim workspace paths for all packages when -trimpath is set. - // The compiler hides the exact value of $GOROOT - // when building things in GOROOT. - // Assume b.WorkDir is being trimmed properly. - // When -trimpath is used with a package built from the module cache, - // use the module path and version instead of the directory. - if !p.Goroot && !cfg.BuildTrimpath && !strings.HasPrefix(p.Dir, b.WorkDir) { + + // Include information about the origin of the package that + // may be embedded in the debug info for the object file. + if cfg.BuildTrimpath { + // When -trimpath is used with a package built from the module cache, + // its debug information refers to the module path and version + // instead of the directory. + if p.Module != nil { + fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version) + } + } else if p.Goroot { + // The Go compiler always hides the exact value of $GOROOT + // when building things in GOROOT. + // + // The C compiler does not, but for packages in GOROOT we rewrite the path + // as though -trimpath were set, so that we don't invalidate the build cache + // (and especially any precompiled C archive files) when changing + // GOROOT_FINAL. (See https://go.dev/issue/50183.) + // + // b.WorkDir is always either trimmed or rewritten to + // the literal string "/tmp/go-build". + } else if !strings.HasPrefix(p.Dir, b.WorkDir) { + // -trimpath is not set and no other rewrite rules apply, + // so the object file may refer to the absolute directory + // containing the package. fmt.Fprintf(h, "dir %s\n", p.Dir) - } else if cfg.BuildTrimpath && p.Module != nil { - fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version) } + if p.Module != nil { fmt.Fprintf(h, "go %s\n", p.Module.GoVersion) } @@ -281,7 +299,14 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { if p.Internal.CoverMode != "" { fmt.Fprintf(h, "cover %q %q\n", p.Internal.CoverMode, b.toolID("cover")) } - fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo) + if p.Internal.FuzzInstrument { + if fuzzFlags := fuzzInstrumentFlags(); fuzzFlags != nil { + fmt.Fprintf(h, "fuzz %q\n", fuzzFlags) + } + } + if p.Internal.BuildInfo != "" { + fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo) + } // Configuration specific to compiler toolchain. switch cfg.BuildToolchainName { @@ -297,8 +322,8 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) - if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" { - fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment) + if cfg.CleanGOEXPERIMENT != "" { + fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT) } // TODO(rsc): Convince compiler team not to add more magic environment variables, @@ -537,7 +562,7 @@ func (b *Builder) build(ctx context.Context, a *Action) (err error) { return nil } - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -773,10 +798,13 @@ OverlayLoop: } if p.Internal.BuildInfo != "" && cfg.ModulesEnabled { - if err := b.writeFile(objdir+"_gomod_.go", modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil { - return err + prog := modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo") + if len(prog) > 0 { + if err := b.writeFile(objdir+"_gomod_.go", prog); err != nil { + return err + } + gofiles = append(gofiles, objdir+"_gomod_.go") } - gofiles = append(gofiles, objdir+"_gomod_.go") } // Compile Go. @@ -1275,8 +1303,8 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) { key, val := cfg.GetArchEnv() fmt.Fprintf(h, "%s=%s\n", key, val) - if goexperiment := buildcfg.GOEXPERIMENT(); goexperiment != "" { - fmt.Fprintf(h, "GOEXPERIMENT=%q\n", goexperiment) + if cfg.CleanGOEXPERIMENT != "" { + fmt.Fprintf(h, "GOEXPERIMENT=%q\n", cfg.CleanGOEXPERIMENT) } // The linker writes source file paths that say GOROOT_FINAL, but @@ -1320,7 +1348,7 @@ func (b *Builder) link(ctx context.Context, a *Action) (err error) { return err } - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -1373,6 +1401,7 @@ func (b *Builder) writeLinkImportcfg(a *Action, file string) error { fmt.Fprintf(&icfg, "packageshlib %s=%s\n", p1.ImportPath, p1.Shlib) } } + fmt.Fprintf(&icfg, "modinfo %q\n", modload.ModInfoData(a.Package.Internal.BuildInfo)) return b.writeFile(file, icfg.Bytes()) } @@ -1487,6 +1516,8 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, return nil, nil, errPrintedOutput } if len(out) > 0 { + // NOTE: we don't attempt to parse quotes and unescapes here. pkg-config + // is typically used within shell backticks, which treats quotes literally. ldflags = strings.Fields(string(out)) if err := checkLinkerFlags("LDFLAGS", "pkg-config --libs", ldflags); err != nil { return nil, nil, err @@ -1498,7 +1529,7 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, } func (b *Builder) installShlibname(ctx context.Context, a *Action) error { - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -1552,7 +1583,7 @@ func (b *Builder) linkShared(ctx context.Context, a *Action) (err error) { } defer b.flushOutput(a) - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -1623,7 +1654,7 @@ func BuildInstallFunc(b *Builder, ctx context.Context, a *Action) (err error) { if !a.buggyInstall && !b.IsCmdList { if cfg.BuildN { b.Showcmd("", "touch %s", a.Target) - } else if err := allowInstall(a); err == nil { + } else if err := AllowInstall(a); err == nil { now := time.Now() os.Chtimes(a.Target, now, now) } @@ -1637,7 +1668,7 @@ func BuildInstallFunc(b *Builder, ctx context.Context, a *Action) (err error) { a.built = a1.built return nil } - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -1669,12 +1700,12 @@ func BuildInstallFunc(b *Builder, ctx context.Context, a *Action) (err error) { return b.moveOrCopyFile(a.Target, a1.built, perm, false) } -// allowInstall returns a non-nil error if this invocation of the go command is +// AllowInstall returns a non-nil error if this invocation of the go command is // allowed to install a.Target. // -// (The build of cmd/go running under its own test is forbidden from installing -// to its original GOROOT.) -var allowInstall = func(*Action) error { return nil } +// The build of cmd/go running under its own test is forbidden from installing +// to its original GOROOT. The var is exported so it can be set by TestMain. +var AllowInstall = func(*Action) error { return nil } // cleanup removes a's object dir to keep the amount of // on-disk garbage down in a large build. On an operating system @@ -1782,7 +1813,7 @@ func (b *Builder) copyFile(dst, src string, perm fs.FileMode, force bool) error } // On Windows, remove lingering ~ file from last attempt. - if base.ToolIsWindows { + if runtime.GOOS == "windows" { if _, err := os.Stat(dst + "~"); err == nil { os.Remove(dst + "~") } @@ -1790,7 +1821,7 @@ func (b *Builder) copyFile(dst, src string, perm fs.FileMode, force bool) error mayberemovefile(dst) df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) - if err != nil && base.ToolIsWindows { + if err != nil && runtime.GOOS == "windows" { // Windows does not allow deletion of a binary file // while it is executing. Try to move it out of the way. // If the move fails, which is likely, we'll try again the @@ -1839,7 +1870,7 @@ func (b *Builder) installHeader(ctx context.Context, a *Action) error { return nil } - if err := allowInstall(a); err != nil { + if err := AllowInstall(a); err != nil { return err } @@ -1854,6 +1885,7 @@ func (b *Builder) installHeader(ctx context.Context, a *Action) error { } // cover runs, in effect, +// // go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go func (b *Builder) cover(a *Action, dst, src string, varName string) error { return b.run(a, a.Objdir, "cover "+a.Package.ImportPath, nil, @@ -1920,8 +1952,7 @@ func mayberemovefile(s string) { // // fmtcmd replaces the name of the current directory with dot (.) // but only when it is at the beginning of a space-separated token. -// -func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string { +func (b *Builder) fmtcmd(dir string, format string, args ...any) string { cmd := fmt.Sprintf(format, args...) if dir != "" && dir != "/" { dot := " ." @@ -1947,7 +1978,7 @@ func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string // showcmd prints the given command to standard output // for the implementation of -n or -x. -func (b *Builder) Showcmd(dir string, format string, args ...interface{}) { +func (b *Builder) Showcmd(dir string, format string, args ...any) { b.output.Lock() defer b.output.Unlock() b.Print(b.fmtcmd(dir, format, args...) + "\n") @@ -1977,13 +2008,13 @@ func (b *Builder) Showcmd(dir string, format string, args ...interface{}) { // // If a is not nil and a.output is not nil, showOutput appends to that slice instead of // printing to b.Print. -// func (b *Builder) showOutput(a *Action, dir, desc, out string) { prefix := "# " + desc suffix := "\n" + out if reldir := base.ShortPath(dir); reldir != dir { suffix = strings.ReplaceAll(suffix, " "+dir, " "+reldir) suffix = strings.ReplaceAll(suffix, "\n"+dir, "\n"+reldir) + suffix = strings.ReplaceAll(suffix, "\n\t"+dir, "\n\t"+reldir) } suffix = strings.ReplaceAll(suffix, " "+b.WorkDir, " $WORK") @@ -2011,7 +2042,7 @@ var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`) // run runs the command given by cmdline in the directory dir. // If the command fails, run prints information about the failure // and returns a non-nil error. -func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error { +func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...any) error { out, err := b.runOut(a, dir, env, cmdargs...) if len(out) > 0 { if desc == "" { @@ -2045,7 +2076,7 @@ func (b *Builder) processOutput(out []byte) string { // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. // It accumulates execution time in a. -func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interface{}) ([]byte, error) { +func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([]byte, error) { cmdline := str.StringList(cmdargs...) for _, arg := range cmdline { @@ -2086,8 +2117,10 @@ func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interfa cmd.Stderr = &buf cleanup := passLongArgsInResponseFiles(cmd) defer cleanup() - cmd.Dir = dir - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) + if dir != "." { + cmd.Dir = dir + } + cmd.Env = cmd.Environ() // Pre-allocate with correct PWD. // Add the TOOLEXEC_IMPORTPATH environment variable for -toolexec tools. // It doesn't really matter if -toolexec isn't being used. @@ -2310,7 +2343,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s // directives pointing to the source directory. It should not generate those // when -trimpath is enabled. if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { - if cfg.BuildTrimpath { + if cfg.BuildTrimpath || p.Goroot { // Keep in sync with Action.trimpath. // The trimmed paths are a little different, but we need to trim in the // same situations. @@ -2332,8 +2365,6 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s to = filepath.Join("/_", toPath) } flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+from+"="+to) - } else if p.Goroot && cfg.GOROOT_FINAL != cfg.GOROOT { - flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+cfg.GOROOT+"="+cfg.GOROOT_FINAL) } } @@ -2374,6 +2405,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s } // gccld runs the gcc linker to create an executable from a set of object files. +// Any error output is only displayed for BuildN or BuildX. func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flags []string, objs []string) error { var cmd []string if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 { @@ -2382,7 +2414,7 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag cmd = b.GccCmd(p.Dir, objdir) } - cmdargs := []interface{}{cmd, "-o", outfile, objs, flags} + cmdargs := []any{cmd, "-o", outfile, objs, flags} dir := p.Dir out, err := b.runOut(a, base.Cwd(), b.cCompilerEnv(), cmdargs...) @@ -2419,22 +2451,13 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag save = append(save, line) } out = bytes.Join(save, nil) - if len(out) > 0 { + if len(out) > 0 && (cfg.BuildN || cfg.BuildX) { b.showOutput(nil, dir, p.ImportPath, b.processOutput(out)) - if err != nil { - err = errPrintedOutput - } } } return err } -// Grab these before main helpfully overwrites them. -var ( - origCC = cfg.Getenv("CC") - origCXX = cfg.Getenv("CXX") -) - // gccCmd returns a gcc command line prefix // defaultCC is defined in zdefaultcc.go, written by cmd/dist. func (b *Builder) GccCmd(incdir, workdir string) []string { @@ -2454,40 +2477,23 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string { // ccExe returns the CC compiler setting without all the extra flags we add implicitly. func (b *Builder) ccExe() []string { - return b.compilerExe(origCC, cfg.DefaultCC(cfg.Goos, cfg.Goarch)) + return envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) } // cxxExe returns the CXX compiler setting without all the extra flags we add implicitly. func (b *Builder) cxxExe() []string { - return b.compilerExe(origCXX, cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) + return envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) } // fcExe returns the FC compiler setting without all the extra flags we add implicitly. func (b *Builder) fcExe() []string { - return b.compilerExe(cfg.Getenv("FC"), "gfortran") -} - -// compilerExe returns the compiler to use given an -// environment variable setting (the value not the name) -// and a default. The resulting slice is usually just the name -// of the compiler but can have additional arguments if they -// were present in the environment value. -// For example if CC="gcc -DGOPHER" then the result is ["gcc", "-DGOPHER"]. -func (b *Builder) compilerExe(envValue string, def string) []string { - compiler := strings.Fields(envValue) - if len(compiler) == 0 { - compiler = strings.Fields(def) - } - return compiler + return envList("FC", "gfortran") } // compilerCmd returns a command line prefix for the given environment // variable and using the default command when the variable is empty. func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []string { - // NOTE: env.go's mkEnv knows that the first three - // strings returned are "gcc", "-I", incdir (and cuts them off). - a := []string{compiler[0], "-I", incdir} - a = append(a, compiler[1:]...) + a := append(compiler, "-I", incdir) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" @@ -2520,6 +2526,13 @@ func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []strin a = append(a, "-Qunused-arguments") } + // zig cc passes --gc-sections to the underlying linker, which then causes + // undefined symbol errors when compiling with cgo but without C code. + // https://github.com/golang/go/issues/52690 + if b.gccSupportsFlag(compiler, "-Wl,--no-gc-sections") { + a = append(a, "-Wl,--no-gc-sections") + } + // disable word wrapping in error messages a = append(a, "-fmessage-length=0") @@ -2576,7 +2589,12 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { } tmp := os.DevNull - if runtime.GOOS == "windows" { + + // On the iOS builder the command + // $CC -Wl,--no-gc-sections -x c - -o /dev/null < /dev/null + // is failing with: + // Unable to remove existing file: Invalid argument + if runtime.GOOS == "windows" || runtime.GOOS == "ios" { f, err := os.CreateTemp(b.WorkDir, "") if err != nil { return false @@ -2586,13 +2604,21 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { defer os.Remove(tmp) } - // We used to write an empty C file, but that gets complicated with - // go build -n. We tried using a file that does not exist, but that - // fails on systems with GCC version 4.2.1; that is the last GPLv2 - // version of GCC, so some systems have frozen on it. - // Now we pass an empty file on stdin, which should work at least for - // GCC and clang. - cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp) + // We used to write an empty C file, but that gets complicated with go + // build -n. We tried using a file that does not exist, but that fails on + // systems with GCC version 4.2.1; that is the last GPLv2 version of GCC, + // so some systems have frozen on it. Now we pass an empty file on stdin, + // which should work at least for GCC and clang. + // + // If the argument is "-Wl,", then it's testing the linker. In that case, + // skip "-c". If it's not "-Wl,", then we are testing the compiler and + // can emit the linking step with "-c". + cmdArgs := str.StringList(compiler, flag) + if !strings.HasPrefix(flag, "-Wl,") /* linker flag */ { + cmdArgs = append(cmdArgs, "-c") + } + cmdArgs = append(cmdArgs, "-x", "c", "-", "-o", tmp) + if cfg.BuildN || cfg.BuildX { b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs)) if cfg.BuildN { @@ -2601,17 +2627,20 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool { } cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Dir = b.WorkDir - cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir) - cmd.Env = append(cmd.Env, "LC_ALL=C") + cmd.Env = append(cmd.Environ(), "LC_ALL=C") out, _ := cmd.CombinedOutput() // GCC says "unrecognized command line option". // clang says "unknown argument". + // tcc says "unsupported" + // AIX says "not recognized" // Older versions of GCC say "unrecognised debug output level". // For -fsplit-stack GCC says "'-fsplit-stack' is not supported". supported := !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) && !bytes.Contains(out, []byte("unrecognised")) && - !bytes.Contains(out, []byte("is not supported")) + !bytes.Contains(out, []byte("is not supported")) && + !bytes.Contains(out, []byte("not recognized")) && + !bytes.Contains(out, []byte("unsupported")) b.flagCache[key] = supported return supported } @@ -2648,6 +2677,8 @@ func (b *Builder) gccArchArgs() []string { } else if cfg.GOMIPS == "softfloat" { return append(args, "-msoft-float") } + case "loong64": + return []string{"-mabi=lp64d"} case "ppc64": if cfg.Goos == "aix" { return []string{"-maix64"} @@ -2658,12 +2689,20 @@ func (b *Builder) gccArchArgs() []string { // envList returns the value of the given environment variable broken // into fields, using the default value when the variable is empty. +// +// The environment variable must be quoted correctly for +// str.SplitQuotedFields. This should be done before building +// anything, for example, in BuildInit. func envList(key, def string) []string { v := cfg.Getenv(key) if v == "" { v = def } - return strings.Fields(v) + args, err := quoted.Split(v) + if err != nil { + panic(fmt.Sprintf("could not parse environment variable %s with value %q: %v", key, v, err)) + } + return args } // CFlags returns the flags to use when invoking the C, C++ or Fortran compilers, or cgo. @@ -2729,6 +2768,10 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...) cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...) } + if cfg.BuildASan { + cgoCFLAGS = append([]string{"-fsanitize=address"}, cgoCFLAGS...) + cgoLDFLAGS = append([]string{"-fsanitize=address"}, cgoLDFLAGS...) + } // Allows including _cgo_export.h, as well as the user's .h files, // from .[ch] files in the package. @@ -2750,7 +2793,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo" || p.ImportPath == "runtime/asan") { cgoflags = append(cgoflags, "-import_syscall=false") } @@ -2868,10 +2911,16 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo switch cfg.BuildToolchainName { case "gc": importGo := objdir + "_cgo_import.go" - if err := b.dynimport(a, p, objdir, importGo, cgoExe, cflags, cgoLDFLAGS, outObj); err != nil { + dynOutGo, dynOutObj, err := b.dynimport(a, p, objdir, importGo, cgoExe, cflags, cgoLDFLAGS, outObj) + if err != nil { return nil, nil, err } - outGo = append(outGo, importGo) + if dynOutGo != "" { + outGo = append(outGo, dynOutGo) + } + if dynOutObj != "" { + outObj = append(outObj, dynOutObj) + } case "gccgo": defunC := objdir + "_cgo_defun.c" @@ -2966,31 +3015,73 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo // dynimport creates a Go source file named importGo containing // //go:cgo_import_dynamic directives for each symbol or library // dynamically imported by the object files outObj. -func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe string, cflags, cgoLDFLAGS, outObj []string) error { +// dynOutGo, if not empty, is a new Go file to build as part of the package. +// dynOutObj, if not empty, is a new file to add to the generated archive. +func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe string, cflags, cgoLDFLAGS, outObj []string) (dynOutGo, dynOutObj string, err error) { cfile := objdir + "_cgo_main.c" ofile := objdir + "_cgo_main.o" if err := b.gcc(a, p, objdir, ofile, cflags, cfile); err != nil { - return err + return "", "", err } - linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles)) + // Gather .syso files from this package and all (transitive) dependencies. + var syso []string + seen := make(map[*Action]bool) + var gatherSyso func(*Action) + gatherSyso = func(a1 *Action) { + if seen[a1] { + return + } + seen[a1] = true + if p1 := a1.Package; p1 != nil { + syso = append(syso, mkAbsFiles(p1.Dir, p1.SysoFiles)...) + } + for _, a2 := range a1.Deps { + gatherSyso(a2) + } + } + gatherSyso(a) + sort.Strings(syso) + str.Uniq(&syso) + linkobj := str.StringList(ofile, outObj, syso) dynobj := objdir + "_cgo_.o" - // we need to use -pie for Linux/ARM to get accurate imported sym ldflags := cgoLDFLAGS if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" { - // -static -pie doesn't make sense, and causes link errors. - // Issue 26197. - n := make([]string, 0, len(ldflags)) - for _, flag := range ldflags { - if flag != "-static" { - n = append(n, flag) + if !str.Contains(ldflags, "-no-pie") { + // we need to use -pie for Linux/ARM to get accurate imported sym (added in https://golang.org/cl/5989058) + // this seems to be outdated, but we don't want to break existing builds depending on this (Issue 45940) + ldflags = append(ldflags, "-pie") + } + if str.Contains(ldflags, "-pie") && str.Contains(ldflags, "-static") { + // -static -pie doesn't make sense, and causes link errors. + // Issue 26197. + n := make([]string, 0, len(ldflags)-1) + for _, flag := range ldflags { + if flag != "-static" { + n = append(n, flag) + } } + ldflags = n } - ldflags = append(n, "-pie") } if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil { - return err + // We only need this information for internal linking. + // If this link fails, mark the object as requiring + // external linking. This link can fail for things like + // syso files that have unexpected dependencies. + // cmd/link explicitly looks for the name "dynimportfail". + // See issue #52863. + fail := objdir + "dynimportfail" + if cfg.BuildN || cfg.BuildX { + b.Showcmd("", "echo > %s", fail) + } + if !cfg.BuildN { + if err := os.WriteFile(fail, nil, 0666); err != nil { + return "", "", err + } + } + return "", fail, nil } // cgo -dynimport @@ -2998,7 +3089,11 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = []string{"-dynlinker"} // record path to dynamic linker } - return b.run(a, base.Cwd(), p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) + err = b.run(a, base.Cwd(), p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags) + if err != nil { + return "", "", err + } + return importGo, "", nil } // Run SWIG on all SWIG input files. @@ -3048,7 +3143,7 @@ var ( ) func (b *Builder) swigDoVersionCheck() error { - out, err := b.runOut(nil, "", nil, "swig", "-version") + out, err := b.runOut(nil, ".", nil, "swig", "-version") if err != nil { return err } @@ -3309,24 +3404,18 @@ func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) { return cleanup } -// Windows has a limit of 32 KB arguments. To be conservative and not worry -// about whether that includes spaces or not, just use 30 KB. Darwin's limit is -// less clear. The OS claims 256KB, but we've seen failures with arglen as -// small as 50KB. -const ArgLengthForResponseFile = (30 << 10) - func useResponseFile(path string, argLen int) bool { // Unless the program uses objabi.Flagparse, which understands // response files, don't use response files. - // TODO: do we need more commands? asm? cgo? For now, no. + // TODO: Note that other toolchains like CC are missing here for now. prog := strings.TrimSuffix(filepath.Base(path), ".exe") switch prog { - case "compile", "link": + case "compile", "link", "cgo", "asm": default: return false } - if argLen > ArgLengthForResponseFile { + if argLen > sys.ExecArgLengthLimit { return true } diff --git a/src/cmd/go/internal/work/exec_test.go b/src/cmd/go/internal/work/exec_test.go index 4eb762cb289d28..8bbf25bb337e8b 100644 --- a/src/cmd/go/internal/work/exec_test.go +++ b/src/cmd/go/internal/work/exec_test.go @@ -7,6 +7,7 @@ package work import ( "bytes" "cmd/internal/objabi" + "cmd/internal/sys" "fmt" "math/rand" "testing" @@ -56,7 +57,7 @@ func TestEncodeDecodeFuzz(t *testing.T) { } t.Parallel() - nRunes := ArgLengthForResponseFile + 100 + nRunes := sys.ExecArgLengthLimit + 100 rBuffer := make([]rune, nRunes) buf := bytes.NewBuffer([]byte(string(rBuffer))) @@ -67,7 +68,7 @@ func TestEncodeDecodeFuzz(t *testing.T) { for i := 0; i < 50; i++ { // Generate a random string of runes. buf.Reset() - for buf.Len() < ArgLengthForResponseFile+1 { + for buf.Len() < sys.ExecArgLengthLimit+1 { var r rune for { r = rune(rng.Intn(utf8.MaxRune + 1)) diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 85da4f89f991f4..842952911554ef 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -8,7 +8,6 @@ import ( "bufio" "bytes" "fmt" - "internal/buildcfg" "io" "log" "os" @@ -22,12 +21,26 @@ import ( "cmd/go/internal/load" "cmd/go/internal/str" "cmd/internal/objabi" + "cmd/internal/quoted" "cmd/internal/sys" "crypto/sha1" ) // The 'path' used for GOROOT_FINAL when -trimpath is specified -const trimPathGoRootFinal = "go" +const trimPathGoRootFinal string = "$GOROOT" + +var runtimePackages = map[string]struct{}{ + "internal/abi": struct{}{}, + "internal/bytealg": struct{}{}, + "internal/cpu": struct{}{}, + "internal/goarch": struct{}{}, + "internal/goos": struct{}{}, + "runtime": struct{}{}, + "runtime/internal/atomic": struct{}{}, + "runtime/internal/math": struct{}{}, + "runtime/internal/sys": struct{}{}, + "runtime/internal/syscall": struct{}{}, +} // The Go toolchain. @@ -63,7 +76,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } pkgpath := pkgPath(a) - gcargs := []string{"-p", pkgpath} + defaultGcFlags := []string{"-p", pkgpath} if p.Module != nil { v := p.Module.GoVersion if v == "" { @@ -82,22 +95,19 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg v = "1.16" } if allowedVersion(v) { - gcargs = append(gcargs, "-lang=go"+v) + defaultGcFlags = append(defaultGcFlags, "-lang=go"+v) } } if p.Standard { - gcargs = append(gcargs, "-std") - } - compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) - // The runtime package imports a couple of general internal packages. - if p.Standard && (p.ImportPath == "internal/cpu" || p.ImportPath == "internal/bytealg" || p.ImportPath == "internal/abi") { - compilingRuntime = true + defaultGcFlags = append(defaultGcFlags, "-std") } + _, compilingRuntime := runtimePackages[p.ImportPath] + compilingRuntime = compilingRuntime && p.Standard if compilingRuntime { // runtime compiles with a special gc flag to check for // memory allocations that are invalid in the runtime package, // and to implement some special compiler pragmas. - gcargs = append(gcargs, "-+") + defaultGcFlags = append(defaultGcFlags, "-+") } // If we're giving the compiler the entire package (no C etc files), tell it that, @@ -116,25 +126,28 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } } if extFiles == 0 { - gcargs = append(gcargs, "-complete") + defaultGcFlags = append(defaultGcFlags, "-complete") } if cfg.BuildContext.InstallSuffix != "" { - gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix) + defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix) } if a.buildID != "" { - gcargs = append(gcargs, "-buildid", a.buildID) + defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID) } if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" { - gcargs = append(gcargs, "-dwarf=false") + defaultGcFlags = append(defaultGcFlags, "-dwarf=false") } - if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { - gcargs = append(gcargs, "-goversion", runtimeVersion) + if strings.HasPrefix(RuntimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { + defaultGcFlags = append(defaultGcFlags, "-goversion", RuntimeVersion) } if symabis != "" { - gcargs = append(gcargs, "-symabis", symabis) + defaultGcFlags = append(defaultGcFlags, "-symabis", symabis) } gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags) + if p.Internal.FuzzInstrument { + gcflags = append(gcflags, fuzzInstrumentFlags()...) + } if compilingRuntime { // Remove -N, if present. // It is not possible to build the runtime with no optimizations, @@ -147,10 +160,15 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg } } } + // Add -c=N to use concurrent backend compilation, if possible. + if c := gcBackendConcurrency(gcflags); c > 1 { + gcflags = append(gcflags, fmt.Sprintf("-c=%d", c)) + } - args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs} - if p.Internal.LocalPrefix != "" { - // Workaround #43883. + args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags} + if p.Internal.LocalPrefix == "" { + args = append(args, "-nolocalimports") + } else { args = append(args, "-D", p.Internal.LocalPrefix) } if importcfg != nil { @@ -172,11 +190,6 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg args = append(args, "-asmhdr", objdir+"go_asm.h") } - // Add -c=N to use concurrent backend compilation, if possible. - if c := gcBackendConcurrency(gcflags); c > 1 { - args = append(args, fmt.Sprintf("-c=%d", c)) - } - for _, f := range gofiles { f := mkAbs(p.Dir, f) @@ -223,7 +236,7 @@ CheckFlags: // except for known commonly used flags. // If the user knows better, they can manually add their own -c to the gcflags. switch flag { - case "-N", "-l", "-S", "-B", "-C", "-I": + case "-N", "-l", "-S", "-B", "-C", "-I", "-shared": // OK default: canDashC = false @@ -232,7 +245,7 @@ CheckFlags: } // TODO: Test and delete these conditions. - if buildcfg.Experiment.FieldTrack || buildcfg.Experiment.PreemptibleLoops { + if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops { canDashC = false } @@ -349,11 +362,11 @@ func (a *Action) trimpath() string { return rewrite } -func asmArgs(a *Action, p *load.Package) []interface{} { +func asmArgs(a *Action, p *load.Package) []any { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. inc := filepath.Join(cfg.GOROOT, "pkg", "include") pkgpath := pkgPath(a) - args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} + args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} if p.ImportPath == "runtime" && cfg.Goarch == "386" { for _, arg := range forcedAsmflags { if arg == "-dynlink" { @@ -365,6 +378,16 @@ func asmArgs(a *Action, p *load.Package) []interface{} { args = append(args, "-compiling-runtime") } + if cfg.Goarch == "386" { + // Define GO386_value from cfg.GO386. + args = append(args, "-D", "GO386_"+cfg.GO386) + } + + if cfg.Goarch == "amd64" { + // Define GOAMD64_value from cfg.GOAMD64. + args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64) + } + if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" { // Define GOMIPS_value from cfg.GOMIPS. args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS) @@ -432,8 +455,8 @@ func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, erro // toolVerify checks that the command line args writes the same output file // if run using newTool instead. // Unused now but kept around for future use. -func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error { - newArgs := make([]interface{}, len(args)) +func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error { + newArgs := make([]any, len(args)) copy(newArgs, args) newArgs[1] = base.Tool(newTool) newArgs[3] = ofile + ".new" // x.6 becomes x.6.new @@ -536,33 +559,18 @@ func packInternal(afile string, ofiles []string) error { } // setextld sets the appropriate linker flags for the specified compiler. -func setextld(ldflags []string, compiler []string) []string { +func setextld(ldflags []string, compiler []string) ([]string, error) { for _, f := range ldflags { if f == "-extld" || strings.HasPrefix(f, "-extld=") { // don't override -extld if supplied - return ldflags + return ldflags, nil } } - ldflags = append(ldflags, "-extld="+compiler[0]) - if len(compiler) > 1 { - extldflags := false - add := strings.Join(compiler[1:], " ") - for i, f := range ldflags { - if f == "-extldflags" && i+1 < len(ldflags) { - ldflags[i+1] = add + " " + ldflags[i+1] - extldflags = true - break - } else if strings.HasPrefix(f, "-extldflags=") { - ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):] - extldflags = true - break - } - } - if !extldflags { - ldflags = append(ldflags, "-extldflags="+add) - } + joined, err := quoted.Join(compiler) + if err != nil { + return nil, err } - return ldflags + return append(ldflags, "-extld="+joined), nil } // pluginPath computes the package path for a plugin main package. @@ -649,7 +657,10 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) } ldflags = append(ldflags, forcedLdflags...) ldflags = append(ldflags, root.Package.Internal.Ldflags...) - ldflags = setextld(ldflags, compiler) + ldflags, err := setextld(ldflags, compiler) + if err != nil { + return err + } // On OS X when using external linking to build a shared library, // the argument passed here to -o ends up recorded in the final @@ -693,7 +704,10 @@ func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, } else { compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) } - ldflags = setextld(ldflags, compiler) + ldflags, err := setextld(ldflags, compiler) + if err != nil { + return err + } for _, d := range toplevelactions { if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries continue diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go index 1499536932d2cf..d37b8df07b463b 100644 --- a/src/cmd/go/internal/work/gccgo.go +++ b/src/cmd/go/internal/work/gccgo.go @@ -6,8 +6,8 @@ package work import ( "fmt" - exec "internal/execabs" "os" + "os/exec" "path/filepath" "strings" "sync" @@ -605,7 +605,11 @@ var gccgoToSymbolFunc func(string) string func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string { gccgoToSymbolFuncOnce.Do(func() { - fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir) + tmpdir := b.WorkDir + if cfg.BuildN { + tmpdir = os.TempDir() + } + fn, err := pkgpath.ToSymbolFunc(tools.compiler(), tmpdir) if err != nil { fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) base.SetExitStatus(2) diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 37a3e2d0ffd123..67bd6a4c670570 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -7,19 +7,32 @@ package work import ( + "bytes" "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/modload" + "cmd/internal/quoted" "cmd/internal/sys" - "flag" "fmt" "os" + "os/exec" "path/filepath" + "regexp" "runtime" + "strconv" + "sync" ) +var buildInitStarted = false + func BuildInit() { + if buildInitStarted { + base.Fatalf("go: internal error: work.BuildInit called more than once") + } + buildInitStarted = true + base.AtExit(closeBuilders) + modload.Init() instrumentInit() buildModeInit() @@ -32,27 +45,63 @@ func BuildInit() { if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) { p, err := filepath.Abs(cfg.BuildPkgdir) if err != nil { - fmt.Fprintf(os.Stderr, "go %s: evaluating -pkgdir: %v\n", flag.Args()[0], err) + fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err) base.SetExitStatus(2) base.Exit() } cfg.BuildPkgdir = p } - // Make sure CC and CXX are absolute paths - for _, key := range []string{"CC", "CXX"} { - if path := cfg.Getenv(key); !filepath.IsAbs(path) && path != "" && path != filepath.Base(path) { - base.Fatalf("go %s: %s environment variable is relative; must be absolute path: %s\n", flag.Args()[0], key, path) + if cfg.BuildP <= 0 { + base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP) + } + + // Make sure CC, CXX, and FC are absolute paths. + for _, key := range []string{"CC", "CXX", "FC"} { + value := cfg.Getenv(key) + args, err := quoted.Split(value) + if err != nil { + base.Fatalf("go: %s environment variable could not be parsed: %v", key, err) + } + if len(args) == 0 { + continue } + path := args[0] + if !filepath.IsAbs(path) && path != filepath.Base(path) { + base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path) + } + } +} + +// fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation +// on supported platforms. +// +// On unsupported platforms, fuzzInstrumentFlags returns nil, meaning no +// instrumentation is added. 'go test -fuzz' still works without coverage, +// but it generates random inputs without guidance, so it's much less effective. +func fuzzInstrumentFlags() []string { + if !sys.FuzzInstrumented(cfg.Goos, cfg.Goarch) { + return nil } + return []string{"-d=libfuzzer"} } func instrumentInit() { - if !cfg.BuildRace && !cfg.BuildMSan { + if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan { return } if cfg.BuildRace && cfg.BuildMSan { - fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n") + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildRace && cfg.BuildASan { + fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n") + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildMSan && cfg.BuildASan { + fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n") base.SetExitStatus(2) base.Exit() } @@ -61,13 +110,29 @@ func instrumentInit() { base.SetExitStatus(2) base.Exit() } - if cfg.BuildRace { - if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0]) + if cfg.BuildRace && !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { + fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch) + base.SetExitStatus(2) + base.Exit() + } + if cfg.BuildASan && !sys.ASanSupported(cfg.Goos, cfg.Goarch) { + fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch) + base.SetExitStatus(2) + base.Exit() + } + // The current implementation is only compatible with the ASan library from version + // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the + // -asan option must use a compatible version of ASan library, which requires that + // the gcc version is not less than 7 and the clang version is not less than 9, + // otherwise a segmentation fault will occur. + if cfg.BuildASan { + if err := compilerRequiredAsanVersion(); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) base.SetExitStatus(2) base.Exit() } } + mode := "race" if cfg.BuildMSan { mode = "msan" @@ -77,13 +142,16 @@ func instrumentInit() { cfg.BuildBuildmode = "pie" } } + if cfg.BuildASan { + mode = "asan" + } modeFlag := "-" + mode if !cfg.BuildContext.CgoEnabled { if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag) } else { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag) } base.SetExitStatus(2) @@ -96,7 +164,7 @@ func instrumentInit() { cfg.BuildContext.InstallSuffix += "_" } cfg.BuildContext.InstallSuffix += mode - cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, mode) + cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode) } func buildModeInit() { @@ -151,7 +219,11 @@ func buildModeInit() { codegenArg = "-shared" ldBuildmode = "pie" case "windows": - ldBuildmode = "pie" + if cfg.BuildRace { + ldBuildmode = "exe" + } else { + ldBuildmode = "pie" + } case "ios": codegenArg = "-shared" ldBuildmode = "pie" @@ -268,3 +340,85 @@ func buildModeInit() { } } } + +type version struct { + name string + major, minor int +} + +var compiler struct { + sync.Once + version + err error +} + +// compilerVersion detects the version of $(go env CC). +// It returns a non-nil error if the compiler matches a known version schema but +// the version could not be parsed, or if $(go env CC) could not be determined. +func compilerVersion() (version, error) { + compiler.Once.Do(func() { + compiler.err = func() error { + compiler.name = "unknown" + cc := os.Getenv("CC") + out, err := exec.Command(cc, "--version").Output() + if err != nil { + // Compiler does not support "--version" flag: not Clang or GCC. + return err + } + + var match [][]byte + if bytes.HasPrefix(out, []byte("gcc")) { + compiler.name = "gcc" + out, err := exec.Command(cc, "-v").CombinedOutput() + if err != nil { + // gcc, but does not support gcc's "-v" flag?! + return err + } + gccRE := regexp.MustCompile(`gcc version (\d+)\.(\d+)`) + match = gccRE.FindSubmatch(out) + } else { + clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`) + if match = clangRE.FindSubmatch(out); len(match) > 0 { + compiler.name = "clang" + } + } + + if len(match) < 3 { + return nil // "unknown" + } + if compiler.major, err = strconv.Atoi(string(match[1])); err != nil { + return err + } + if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil { + return err + } + return nil + }() + }) + return compiler.version, compiler.err +} + +// compilerRequiredAsanVersion is a copy of the function defined in +// misc/cgo/testsanitizers/cc_test.go +// compilerRequiredAsanVersion reports whether the compiler is the version +// required by Asan. +func compilerRequiredAsanVersion() error { + compiler, err := compilerVersion() + if err != nil { + return fmt.Errorf("-asan: the version of $(go env CC) could not be parsed") + } + + switch compiler.name { + case "gcc": + if compiler.major < 7 { + return fmt.Errorf("-asan is not supported with C compiler %d.%d\n", compiler.major, compiler.minor) + } + case "clang": + if compiler.major < 9 { + return fmt.Errorf("-asan is not supported with C compiler %d.%d\n", compiler.major, compiler.minor) + } + default: + return fmt.Errorf("-asan: C compiler is not gcc or clang") + } + return nil +} diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go index e9b9f6c6c0f244..0bf8763543b14e 100644 --- a/src/cmd/go/internal/work/security.go +++ b/src/cmd/go/internal/work/security.go @@ -131,6 +131,7 @@ var validCompilerFlagsWithNextArg = []string{ "-D", "-U", "-I", + "-F", "-framework", "-include", "-isysroot", @@ -170,7 +171,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{ // Note that any wildcards in -Wl need to exclude comma, // since -Wl splits its argument at commas and passes // them all to the linker uninterpreted. Allowing comma - // in a wildcard would allow tunnelling arbitrary additional + // in a wildcard would allow tunneling arbitrary additional // linker arguments through one of these. re(`-Wl,--(no-)?allow-multiple-definition`), re(`-Wl,--(no-)?allow-shlib-undefined`), diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go index 8d4be0abfc09e6..d2aeb54e0cea14 100644 --- a/src/cmd/go/internal/work/security_test.go +++ b/src/cmd/go/internal/work/security_test.go @@ -15,6 +15,7 @@ var goodCompilerFlags = [][]string{ {"-Ufoo"}, {"-Ufoo1"}, {"-F/Qt"}, + {"-F", "/Qt"}, {"-I/"}, {"-I/etc/passwd"}, {"-I."}, diff --git a/src/cmd/go/internal/work/testgo.go b/src/cmd/go/internal/work/testgo.go deleted file mode 100644 index 8b77871b23f269..00000000000000 --- a/src/cmd/go/internal/work/testgo.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file contains extra hooks for testing the go command. - -//go:build testgo -// +build testgo - -package work - -import ( - "cmd/go/internal/cfg" - "cmd/go/internal/search" - "fmt" - "os" - "path/filepath" - "runtime" -) - -func init() { - if v := os.Getenv("TESTGO_VERSION"); v != "" { - runtimeVersion = v - } - - if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" { - // Disallow installs to the GOROOT from which testgo was built. - // Installs to other GOROOTs — such as one set explicitly within a test — are ok. - allowInstall = func(a *Action) error { - if cfg.BuildN { - return nil - } - - rel := search.InDir(a.Target, testGOROOT) - if rel == "" { - return nil - } - - callerPos := "" - if _, file, line, ok := runtime.Caller(1); ok { - if shortFile := search.InDir(file, filepath.Join(testGOROOT, "src")); shortFile != "" { - file = shortFile - } - callerPos = fmt.Sprintf("%s:%d: ", file, line) - } - return fmt.Errorf("%stestgo must not write to GOROOT (installing to %s)", callerPos, filepath.Join("GOROOT", rel)) - } - } -} diff --git a/src/cmd/go/internal/workcmd/edit.go b/src/cmd/go/internal/workcmd/edit.go new file mode 100644 index 00000000000000..1478c19389fa1f --- /dev/null +++ b/src/cmd/go/internal/workcmd/edit.go @@ -0,0 +1,316 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work edit + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "golang.org/x/mod/module" + + "golang.org/x/mod/modfile" +) + +var cmdEdit = &base.Command{ + UsageLine: "go work edit [editing flags] [go.work]", + Short: "edit go.work from tools or scripts", + Long: `Edit provides a command-line interface for editing go.work, +for use primarily by tools or scripts. It only reads go.work; +it does not look up information about the modules involved. +If no file is specified, Edit looks for a go.work file in the current +directory and its parent directories + +The editing flags specify a sequence of editing operations. + +The -fmt flag reformats the go.work file without making other changes. +This reformatting is also implied by any other modifications that use or +rewrite the go.mod file. The only time this flag is needed is if no other +flags are specified, as in 'go work edit -fmt'. + +The -use=path and -dropuse=path flags +add and drop a use directive from the go.work file's set of module directories. + +The -replace=old[@v]=new[@v] flag adds a replacement of the given +module path and version pair. If the @v in old@v is omitted, a +replacement without a version on the left side is added, which applies +to all versions of the old module path. If the @v in new@v is omitted, +the new path should be a local module root directory, not a module +path. Note that -replace overrides any redundant replacements for old[@v], +so omitting @v will drop existing replacements for specific versions. + +The -dropreplace=old[@v] flag drops a replacement of the given +module path and version pair. If the @v is omitted, a replacement without +a version on the left side is dropped. + +The -use, -dropuse, -replace, and -dropreplace, +editing flags may be repeated, and the changes are applied in the order given. + +The -go=version flag sets the expected Go language version. + +The -print flag prints the final go.work in its text format instead of +writing it back to go.mod. + +The -json flag prints the final go.work file in JSON format instead of +writing it back to go.mod. The JSON output corresponds to these Go types: + + type GoWork struct { + Go string + Use []Use + Replace []Replace + } + + type Use struct { + DiskPath string + ModulePath string + } + + type Replace struct { + Old Module + New Module + } + + type Module struct { + Path string + Version string + } + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, +} + +var ( + editFmt = cmdEdit.Flag.Bool("fmt", false, "") + editGo = cmdEdit.Flag.String("go", "", "") + editJSON = cmdEdit.Flag.Bool("json", false, "") + editPrint = cmdEdit.Flag.Bool("print", false, "") + workedits []func(file *modfile.WorkFile) // edits specified in flags +) + +type flagFunc func(string) + +func (f flagFunc) String() string { return "" } +func (f flagFunc) Set(s string) error { f(s); return nil } + +func init() { + cmdEdit.Run = runEditwork // break init cycle + + cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "") + cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "") +} + +func runEditwork(ctx context.Context, cmd *base.Command, args []string) { + if *editJSON && *editPrint { + base.Fatalf("go: cannot use both -json and -print") + } + + if len(args) > 1 { + base.Fatalf("go: 'go help work edit' accepts at most one argument") + } + var gowork string + if len(args) == 1 { + gowork = args[0] + } else { + modload.InitWorkfile() + gowork = modload.WorkFilePath() + } + + if *editGo != "" { + if !modfile.GoVersionRE.MatchString(*editGo) { + base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion()) + } + } + + if gowork == "" { + base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") + } + + anyFlags := + *editGo != "" || + *editJSON || + *editPrint || + *editFmt || + len(workedits) > 0 + + if !anyFlags { + base.Fatalf("go: no flags specified (see 'go help work edit').") + } + + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err) + } + + if *editGo != "" { + if err := workFile.AddGoStmt(*editGo); err != nil { + base.Fatalf("go: internal error: %v", err) + } + } + + if len(workedits) > 0 { + for _, edit := range workedits { + edit(workFile) + } + } + + modload.UpdateWorkFile(workFile) + + workFile.SortBlocks() + workFile.Cleanup() // clean file after edits + + if *editJSON { + editPrintJSON(workFile) + return + } + + if *editPrint { + os.Stdout.Write(modfile.Format(workFile.Syntax)) + return + } + + modload.WriteWorkFile(gowork, workFile) +} + +// flagEditworkUse implements the -use flag. +func flagEditworkUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + _, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil) + modulePath := "" + if err == nil { + modulePath = mf.Module.Mod.Path + } + f.AddUse(modload.ToDirectoryPath(arg), modulePath) + if err := f.AddUse(modload.ToDirectoryPath(arg), ""); err != nil { + base.Fatalf("go: -use=%s: %v", arg, err) + } + }) +} + +// flagEditworkDropUse implements the -dropuse flag. +func flagEditworkDropUse(arg string) { + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropUse(modload.ToDirectoryPath(arg)); err != nil { + base.Fatalf("go: -dropdirectory=%s: %v", arg, err) + } + }) +} + +// allowedVersionArg returns whether a token may be used as a version in go.mod. +// We don't call modfile.CheckPathVersion, because that insists on versions +// being in semver form, but here we want to allow versions like "master" or +// "1234abcdef", which the go command will resolve the next time it runs (or +// during -fix). Even so, we need to make sure the version is a valid token. +func allowedVersionArg(arg string) bool { + return !modfile.MustQuote(arg) +} + +// parsePathVersionOptional parses path[@version], using adj to +// describe any errors. +func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) { + if i := strings.Index(arg, "@"); i < 0 { + path = arg + } else { + path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + } + if err := module.CheckImportPath(path); err != nil { + if !allowDirPath || !modfile.IsDirectoryPath(path) { + return path, version, fmt.Errorf("invalid %s path: %v", adj, err) + } + } + if path != arg && !allowedVersionArg(version) { + return path, version, fmt.Errorf("invalid %s version: %q", adj, version) + } + return path, version, nil +} + +// flagReplace implements the -replace flag. +func flagEditworkReplace(arg string) { + var i int + if i = strings.Index(arg, "="); i < 0 { + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + } + old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) + if strings.HasPrefix(new, ">") { + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) + } + oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + newPath, newVersion, err := parsePathVersionOptional("new", new, true) + if err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + if newPath == new && !modfile.IsDirectoryPath(new) { + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) + } + + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { + base.Fatalf("go: -replace=%s: %v", arg, err) + } + }) +} + +// flagDropReplace implements the -dropreplace flag. +func flagEditworkDropReplace(arg string) { + path, version, err := parsePathVersionOptional("old", arg, true) + if err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + workedits = append(workedits, func(f *modfile.WorkFile) { + if err := f.DropReplace(path, version); err != nil { + base.Fatalf("go: -dropreplace=%s: %v", arg, err) + } + }) +} + +type replaceJSON struct { + Old module.Version + New module.Version +} + +// editPrintJSON prints the -json output. +func editPrintJSON(workFile *modfile.WorkFile) { + var f workfileJSON + if workFile.Go != nil { + f.Go = workFile.Go.Version + } + for _, d := range workFile.Use { + f.Use = append(f.Use, useJSON{DiskPath: d.Path, ModPath: d.ModulePath}) + } + + for _, r := range workFile.Replace { + f.Replace = append(f.Replace, replaceJSON{r.Old, r.New}) + } + data, err := json.MarshalIndent(&f, "", "\t") + if err != nil { + base.Fatalf("go: internal error: %v", err) + } + data = append(data, '\n') + os.Stdout.Write(data) +} + +// workfileJSON is the -json output data structure. +type workfileJSON struct { + Go string `json:",omitempty"` + Use []useJSON + Replace []replaceJSON +} + +type useJSON struct { + DiskPath string + ModPath string `json:",omitempty"` +} diff --git a/src/cmd/go/internal/workcmd/init.go b/src/cmd/go/internal/workcmd/init.go new file mode 100644 index 00000000000000..c2513bac358479 --- /dev/null +++ b/src/cmd/go/internal/workcmd/init.go @@ -0,0 +1,51 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work init + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/modload" + "context" + "path/filepath" +) + +var cmdInit = &base.Command{ + UsageLine: "go work init [moddirs]", + Short: "initialize workspace file", + Long: `Init initializes and writes a new go.work file in the +current directory, in effect creating a new workspace at the current +directory. + +go work init optionally accepts paths to the workspace modules as +arguments. If the argument is omitted, an empty workspace with no +modules will be created. + +Each argument path is added to a use directive in the go.work file. The +current go version will also be listed in the go.work file. + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, + Run: runInit, +} + +func init() { + base.AddModCommonFlags(&cmdInit.Flag) +} + +func runInit(ctx context.Context, cmd *base.Command, args []string) { + modload.InitWorkfile() + + modload.ForceUseModules = true + + workFile := modload.WorkFilePath() + if workFile == "" { + workFile = filepath.Join(base.Cwd(), "go.work") + } + + modload.CreateWorkFile(ctx, workFile, args) +} diff --git a/src/cmd/go/internal/workcmd/sync.go b/src/cmd/go/internal/workcmd/sync.go new file mode 100644 index 00000000000000..7712eb6b6b8920 --- /dev/null +++ b/src/cmd/go/internal/workcmd/sync.go @@ -0,0 +1,134 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work sync + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/imports" + "cmd/go/internal/modload" + "context" + + "golang.org/x/mod/module" +) + +var cmdSync = &base.Command{ + UsageLine: "go work sync", + Short: "sync workspace build list to modules", + Long: `Sync syncs the workspace's build list back to the +workspace's modules + +The workspace's build list is the set of versions of all the +(transitive) dependency modules used to do builds in the workspace. go +work sync generates that build list using the Minimal Version Selection +algorithm, and then syncs those versions back to each of modules +specified in the workspace (with use directives). + +The syncing is done by sequentially upgrading each of the dependency +modules specified in a workspace module to the version in the build list +if the dependency module's version is not already the same as the build +list's version. Note that Minimal Version Selection guarantees that the +build list's version of each module is always the same or higher than +that in each workspace module. + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, + Run: runSync, +} + +func init() { + base.AddModCommonFlags(&cmdSync.Flag) +} + +func runSync(ctx context.Context, cmd *base.Command, args []string) { + modload.ForceUseModules = true + modload.InitWorkfile() + if modload.WorkFilePath() == "" { + base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") + } + + workGraph := modload.LoadModGraph(ctx, "") + _ = workGraph + mustSelectFor := map[module.Version][]module.Version{} + + mms := modload.MainModules + + opts := modload.PackageOpts{ + Tags: imports.AnyTags(), + VendorModulesInGOROOTSrc: true, + ResolveMissingImports: false, + LoadTests: true, + AllowErrors: true, + SilencePackageErrors: true, + SilenceUnmatchedWarnings: true, + } + for _, m := range mms.Versions() { + opts.MainModule = m + _, pkgs := modload.LoadPackages(ctx, opts, "all") + opts.MainModule = module.Version{} // reset + + var ( + mustSelect []module.Version + inMustSelect = map[module.Version]bool{} + ) + for _, pkg := range pkgs { + if r := modload.PackageModule(pkg); r.Version != "" && !inMustSelect[r] { + // r has a known version, so force that version. + mustSelect = append(mustSelect, r) + inMustSelect[r] = true + } + } + module.Sort(mustSelect) // ensure determinism + mustSelectFor[m] = mustSelect + } + + workFilePath := modload.WorkFilePath() // save go.work path because EnterModule clobbers it. + + for _, m := range mms.Versions() { + if mms.ModRoot(m) == "" && m.Path == "command-line-arguments" { + // This is not a real module. + // TODO(#49228): Remove this special case once the special + // command-line-arguments module is gone. + continue + } + + // Use EnterModule to reset the global state in modload to be in + // single-module mode using the modroot of m. + modload.EnterModule(ctx, mms.ModRoot(m)) + + // Edit the build list in the same way that 'go get' would if we + // requested the relevant module versions explicitly. + changed, err := modload.EditBuildList(ctx, nil, mustSelectFor[m]) + if err != nil { + base.Errorf("go: %v", err) + } + if !changed { + continue + } + + modload.LoadPackages(ctx, modload.PackageOpts{ + Tags: imports.AnyTags(), + Tidy: true, + VendorModulesInGOROOTSrc: true, + ResolveMissingImports: false, + LoadTests: true, + AllowErrors: true, + SilenceMissingStdImports: true, + SilencePackageErrors: true, + }, "all") + modload.WriteGoMod(ctx) + } + + wf, err := modload.ReadWorkFile(workFilePath) + if err != nil { + base.Fatalf("go: %v", err) + } + modload.UpdateWorkFile(wf) + if err := modload.WriteWorkFile(workFilePath, wf); err != nil { + base.Fatalf("go: %v", err) + } +} diff --git a/src/cmd/go/internal/workcmd/use.go b/src/cmd/go/internal/workcmd/use.go new file mode 100644 index 00000000000000..6da64b3f09e053 --- /dev/null +++ b/src/cmd/go/internal/workcmd/use.go @@ -0,0 +1,212 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work use + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/fsys" + "cmd/go/internal/modload" + "cmd/go/internal/str" + "context" + "fmt" + "io/fs" + "os" + "path/filepath" +) + +var cmdUse = &base.Command{ + UsageLine: "go work use [-r] moddirs", + Short: "add modules to workspace file", + Long: `Use provides a command-line interface for adding +directories, optionally recursively, to a go.work file. + +A use directive will be added to the go.work file for each argument +directory listed on the command line go.work file, if it exists on disk, +or removed from the go.work file if it does not exist on disk. + +The -r flag searches recursively for modules in the argument +directories, and the use command operates as if each of the directories +were specified as arguments: namely, use directives will be added for +directories that exist, and removed for directories that do not exist. + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, +} + +var useR = cmdUse.Flag.Bool("r", false, "") + +func init() { + cmdUse.Run = runUse // break init cycle + + base.AddModCommonFlags(&cmdUse.Flag) +} + +func runUse(ctx context.Context, cmd *base.Command, args []string) { + modload.ForceUseModules = true + + var gowork string + modload.InitWorkfile() + gowork = modload.WorkFilePath() + + if gowork == "" { + base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") + } + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: %v", err) + } + workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute. + + haveDirs := make(map[string][]string) // absolute → original(s) + for _, use := range workFile.Use { + var abs string + if filepath.IsAbs(use.Path) { + abs = filepath.Clean(use.Path) + } else { + abs = filepath.Join(workDir, use.Path) + } + haveDirs[abs] = append(haveDirs[abs], use.Path) + } + + // keepDirs maps each absolute path to keep to the literal string to use for + // that path (either an absolute or a relative path), or the empty string if + // all entries for the absolute path should be removed. + keepDirs := make(map[string]string) + + // lookDir updates the entry in keepDirs for the directory dir, + // which is either absolute or relative to the current working directory + // (not necessarily the directory containing the workfile). + lookDir := func(dir string) { + absDir, dir := pathRel(workDir, dir) + + fi, err := fsys.Stat(filepath.Join(absDir, "go.mod")) + if err != nil { + if os.IsNotExist(err) { + keepDirs[absDir] = "" + } else { + base.Errorf("go: %v", err) + } + return + } + + if !fi.Mode().IsRegular() { + base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod")) + } + + if dup := keepDirs[absDir]; dup != "" && dup != dir { + base.Errorf(`go: already added "%s" as "%s"`, dir, dup) + } + keepDirs[absDir] = dir + } + + if len(args) == 0 { + base.Fatalf("go: 'go work use' requires one or more directory arguments") + } + for _, useDir := range args { + absArg, _ := pathRel(workDir, useDir) + + info, err := fsys.Stat(absArg) + if err != nil { + // Errors raised from os.Stat are formatted to be more user-friendly. + if os.IsNotExist(err) { + base.Errorf("go: directory %v does not exist", absArg) + } else { + base.Errorf("go: %v", err) + } + continue + } else if !info.IsDir() { + base.Errorf("go: %s is not a directory", absArg) + continue + } + + if !*useR { + lookDir(useDir) + continue + } + + // Add or remove entries for any subdirectories that still exist. + fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + if info.Mode()&fs.ModeSymlink != 0 { + if target, err := fsys.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + lookDir(path) + return nil + }) + + // Remove entries for subdirectories that no longer exist. + // Because they don't exist, they will be skipped by Walk. + for absDir, _ := range haveDirs { + if str.HasFilePathPrefix(absDir, absArg) { + if _, ok := keepDirs[absDir]; !ok { + keepDirs[absDir] = "" // Mark for deletion. + } + } + } + } + + base.ExitIfErrors() + + for absDir, keepDir := range keepDirs { + nKept := 0 + for _, dir := range haveDirs[absDir] { + if dir == keepDir { // (note that dir is always non-empty) + nKept++ + } else { + workFile.DropUse(dir) + } + } + if keepDir != "" && nKept != 1 { + // If we kept more than one copy, delete them all. + // We'll recreate a unique copy with AddUse. + if nKept > 1 { + workFile.DropUse(keepDir) + } + workFile.AddUse(keepDir, "") + } + } + modload.UpdateWorkFile(workFile) + modload.WriteWorkFile(gowork, workFile) +} + +// pathRel returns the absolute and canonical forms of dir for use in a +// go.work file located in directory workDir. +// +// If dir is relative, it is intepreted relative to base.Cwd() +// and its canonical form is relative to workDir if possible. +// If dir is absolute or cannot be made relative to workDir, +// its canonical form is absolute. +// +// Canonical absolute paths are clean. +// Canonical relative paths are clean and slash-separated. +func pathRel(workDir, dir string) (abs, canonical string) { + if filepath.IsAbs(dir) { + abs = filepath.Clean(dir) + return abs, abs + } + + abs = filepath.Join(base.Cwd(), dir) + rel, err := filepath.Rel(workDir, abs) + if err != nil { + // The path can't be made relative to the go.work file, + // so it must be kept absolute instead. + return abs, abs + } + + // Normalize relative paths to use slashes, so that checked-in go.work + // files with relative paths within the repo are platform-independent. + return abs, modload.ToDirectoryPath(rel) +} diff --git a/src/cmd/go/internal/workcmd/work.go b/src/cmd/go/internal/workcmd/work.go new file mode 100644 index 00000000000000..c99cc2a3fa9d16 --- /dev/null +++ b/src/cmd/go/internal/workcmd/work.go @@ -0,0 +1,78 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package workcmd implements the “go work” command. +package workcmd + +import ( + "cmd/go/internal/base" +) + +var CmdWork = &base.Command{ + UsageLine: "go work", + Short: "workspace maintenance", + Long: `Work provides access to operations on workspaces. + +Note that support for workspaces is built into many other commands, not +just 'go work'. + +See 'go help modules' for information about Go's module system of which +workspaces are a part. + +See https://go.dev/ref/mod#workspaces for an in-depth reference on +workspaces. + +See https://go.dev/doc/tutorial/workspaces for an introductory +tutorial on workspaces. + +A workspace is specified by a go.work file that specifies a set of +module directories with the "use" directive. These modules are used as +root modules by the go command for builds and related operations. A +workspace that does not specify modules to be used cannot be used to do +builds from local modules. + +go.work files are line-oriented. Each line holds a single directive, +made up of a keyword followed by arguments. For example: + + go 1.18 + + use ../foo/bar + use ./baz + + replace example.com/foo v1.2.3 => example.com/bar v1.4.5 + +The leading keyword can be factored out of adjacent lines to create a block, +like in Go imports. + + use ( + ../foo/bar + ./baz + ) + +The use directive specifies a module to be included in the workspace's +set of main modules. The argument to the use directive is the directory +containing the module's go.mod file. + +The go directive specifies the version of Go the file was written at. It +is possible there may be future changes in the semantics of workspaces +that could be controlled by this version, but for now the version +specified has no effect. + +The replace directive has the same syntax as the replace directive in a +go.mod file and takes precedence over replaces in go.mod files. It is +primarily intended to override conflicting replaces in different workspace +modules. + +To determine whether the go command is operating in workspace mode, use +the "go env GOWORK" command. This will specify the workspace file being +used. +`, + + Commands: []*base.Command{ + cmdEdit, + cmdInit, + cmdSync, + cmdUse, + }, +} diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 16361e02ca7f7c..ee705e87e05133 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -7,6 +7,7 @@ package main import ( + "cmd/go/internal/workcmd" "context" "flag" "fmt" @@ -56,6 +57,7 @@ func init() { work.CmdInstall, list.CmdList, modcmd.CmdMod, + workcmd.CmdWork, run.CmdRun, test.CmdTest, tool.CmdTool, @@ -140,6 +142,10 @@ func main() { } } + if cfg.GOROOT == "" { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n") + os.Exit(2) + } if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() { fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT) os.Exit(2) @@ -188,6 +194,9 @@ func invoke(cmd *base.Command, args []string) { // 'go env' handles checking the build config if cmd != envcmd.CmdEnv { buildcfg.Check() + if cfg.ExperimentErr != nil { + base.Fatalf("go: %v", cfg.ExperimentErr) + } } // Set environment (GOOS, GOARCH, etc) explicitly. diff --git a/src/cmd/go/proxy_test.go b/src/cmd/go/proxy_test.go index 74bfecc08dbc6d..fc256968b74bd1 100644 --- a/src/cmd/go/proxy_test.go +++ b/src/cmd/go/proxy_test.go @@ -11,6 +11,7 @@ import ( "errors" "flag" "fmt" + "internal/txtar" "io" "io/fs" "log" @@ -25,7 +26,6 @@ import ( "cmd/go/internal/modfetch/codehost" "cmd/go/internal/par" - "cmd/go/internal/txtar" "golang.org/x/mod/module" "golang.org/x/mod/semver" @@ -357,7 +357,7 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) { zip []byte err error } - c := zipCache.Do(a, func() interface{} { + c := zipCache.Do(a, func() any { var buf bytes.Buffer z := zip.NewWriter(&buf) for _, f := range a.Files { @@ -431,7 +431,7 @@ func readArchive(path, vers string) (*txtar.Archive, error) { prefix := strings.ReplaceAll(enc, "/", "_") name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt") - a := archiveCache.Do(name, func() interface{} { + a := archiveCache.Do(name, func() any { a, err := txtar.ParseFile(name) if err != nil { if testing.Verbose() || !os.IsNotExist(err) { diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go index 639e907db07515..b2f68b67f9eb2b 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go @@ -11,15 +11,18 @@ import ( "bytes" "context" "errors" + "flag" "fmt" "go/build" "internal/testenv" + "internal/txtar" "io/fs" "os" "os/exec" "path/filepath" "regexp" "runtime" + "runtime/debug" "strconv" "strings" "sync" @@ -30,11 +33,12 @@ import ( "cmd/go/internal/imports" "cmd/go/internal/par" "cmd/go/internal/robustio" - "cmd/go/internal/txtar" "cmd/go/internal/work" "cmd/internal/sys" ) +var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`) + // TestScript runs the tests in testdata/script/*.txt. func TestScript(t *testing.T) { testenv.MustHaveGoBuild(t) @@ -77,6 +81,7 @@ func TestScript(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(ctx) + defer cancel() ts := &testScript{ t: t, ctx: ctx, @@ -90,14 +95,13 @@ func TestScript(t *testing.T) { defer removeAll(ts.workdir) } ts.run() - cancel() }) } } // A testScript holds execution state for a single test script. type testScript struct { - t *testing.T + t testing.TB ctx context.Context cancel context.CancelFunc gracePeriod time.Duration @@ -138,6 +142,8 @@ var extraEnvKeys = []string{ "SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210 "WINDIR", // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711 "LD_LIBRARY_PATH", // must be preserved on Unix systems to find shared libraries + "LIBRARY_PATH", // allow override of non-standard static library paths + "C_INCLUDE_PATH", // allow override non-standard include paths "CC", // don't lose user settings when invoking cgo "GO_TESTING_GOTOOLS", // for gccgo testing "GCCGO", // for gccgo testing @@ -155,21 +161,28 @@ func (ts *testScript) setup() { ts.check(os.MkdirAll(filepath.Join(ts.workdir, "tmp"), 0777)) ts.check(os.MkdirAll(filepath.Join(ts.workdir, "gopath/src"), 0777)) ts.cd = filepath.Join(ts.workdir, "gopath/src") + version, err := goVersion() + if err != nil { + ts.t.Fatal(err) + } ts.env = []string{ "WORK=" + ts.workdir, // must be first for ts.abbrev - "PATH=" + testBin + string(filepath.ListSeparator) + os.Getenv("PATH"), + pathEnvName() + "=" + testBin + string(filepath.ListSeparator) + os.Getenv(pathEnvName()), homeEnvName() + "=/no-home", "CCACHE_DISABLE=1", // ccache breaks with non-existent HOME "GOARCH=" + runtime.GOARCH, + "TESTGO_GOHOSTARCH=" + goHostArch, "GOCACHE=" + testGOCACHE, "GODEBUG=" + os.Getenv("GODEBUG"), "GOEXE=" + cfg.ExeSuffix, + "GOEXPERIMENT=" + os.Getenv("GOEXPERIMENT"), "GOOS=" + runtime.GOOS, + "TESTGO_GOHOSTOS=" + goHostOS, "GOPATH=" + filepath.Join(ts.workdir, "gopath"), "GOPROXY=" + proxyURL, "GOPRIVATE=", "GOROOT=" + testGOROOT, - "GOROOT_FINAL=" + os.Getenv("GOROOT_FINAL"), // causes spurious rebuilds and breaks the "stale" built-in if not propagated + "GOROOT_FINAL=" + testGOROOT_FINAL, // causes spurious rebuilds and breaks the "stale" built-in if not propagated "GOTRACEBACK=system", "TESTGO_GOROOT=" + testGOROOT, "GOSUMDB=" + testSumDBVerifierKey, @@ -179,15 +192,25 @@ func (ts *testScript) setup() { "PWD=" + ts.cd, tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"), "devnull=" + os.DevNull, - "goversion=" + goVersion(ts), - ":=" + string(os.PathListSeparator), + "goversion=" + version, + "CMDGO_TEST_RUN_MAIN=true", + } + if testenv.Builder() != "" || os.Getenv("GIT_TRACE_CURL") == "1" { + // To help diagnose https://go.dev/issue/52545, + // enable tracing for Git HTTPS requests. + ts.env = append(ts.env, + "GIT_TRACE_CURL=1", + "GIT_TRACE_CURL_NO_DATA=1", + "GIT_REDACT_COOKIES=o,SSO,GSSO_Uberproxy") } if !testenv.HasExternalNetwork() { ts.env = append(ts.env, "TESTGONETWORK=panic", "TESTGOVCS=panic") } - - if runtime.GOOS == "plan9" { - ts.env = append(ts.env, "path="+testBin+string(filepath.ListSeparator)+os.Getenv("path")) + if os.Getenv("CGO_ENABLED") != "" || runtime.GOOS != goHostOS || runtime.GOARCH != goHostArch { + // If the actual CGO_ENABLED might not match the cmd/go default, set it + // explicitly in the environment. Otherwise, leave it unset so that we also + // cover the default behaviors. + ts.env = append(ts.env, "CGO_ENABLED="+cgoEnabled) } for _, key := range extraEnvKeys { @@ -202,16 +225,23 @@ func (ts *testScript) setup() { ts.envMap[kv[:i]] = kv[i+1:] } } + // Add entries for ${:} and ${/} to make it easier to write platform-independent + // environment variables. + ts.envMap["/"] = string(os.PathSeparator) + ts.envMap[":"] = string(os.PathListSeparator) + + fmt.Fprintf(&ts.log, "# (%s)\n", time.Now().UTC().Format(time.RFC3339)) + ts.mark = ts.log.Len() } // goVersion returns the current Go version. -func goVersion(ts *testScript) string { +func goVersion() (string, error) { tags := build.Default.ReleaseTags version := tags[len(tags)-1] if !regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`).MatchString(version) { - ts.fatalf("invalid go version %q", version) + return "", fmt.Errorf("invalid go version %q", version) } - return version[2:] + return version[2:], nil } var execCache par.Cache @@ -269,6 +299,22 @@ func (ts *testScript) run() { ts.mark = ts.log.Len() } + // With -testsum, if a go.mod file is present in the test's initial + // working directory, run 'go mod tidy'. + if *testSum != "" { + if ts.updateSum(a) { + defer func() { + if ts.t.Failed() { + return + } + data := txtar.Format(a) + if err := os.WriteFile(ts.file, data, 0666); err != nil { + ts.t.Errorf("rewriting test file: %v", err) + } + }() + } + } + // Run script. // See testdata/script/README for documentation of script form. script := string(a.Comment) @@ -326,14 +372,22 @@ Script: switch cond.tag { case runtime.GOOS, runtime.GOARCH, runtime.Compiler: ok = true + case "cross": + ok = goHostOS != runtime.GOOS || goHostArch != runtime.GOARCH case "short": ok = testing.Short() case "cgo": ok = canCgo case "msan": ok = canMSan + case "asan": + ok = canASan case "race": ok = canRace + case "fuzz": + ok = canFuzz + case "fuzz-instrumented": + ok = fuzzInstrumented case "net": ok = testenv.HasExternalNetwork() case "link": @@ -343,11 +397,27 @@ Script: case "symlink": ok = testenv.HasSymlink() case "case-sensitive": - ok = isCaseSensitive(ts.t) + ok, err = isCaseSensitive() + if err != nil { + ts.fatalf("%v", err) + } + case "trimpath": + if info, _ := debug.ReadBuildInfo(); info == nil { + ts.fatalf("missing build info") + } else { + for _, s := range info.Settings { + if s.Key == "-trimpath" && s.Value == "true" { + ok = true + break + } + } + } + case "mismatched-goroot": + ok = testGOROOT_FINAL != "" && testGOROOT_FINAL != testGOROOT default: if strings.HasPrefix(cond.tag, "exec:") { prog := cond.tag[len("exec:"):] - ok = execCache.Do(prog, func() interface{} { + ok = execCache.Do(prog, func() any { if runtime.GOOS == "plan9" && prog == "git" { // The Git command is usually not the real Git on Plan 9. // See https://golang.org/issues/29640. @@ -413,19 +483,22 @@ Script: var ( onceCaseSensitive sync.Once caseSensitive bool + caseSensitiveErr error ) -func isCaseSensitive(t *testing.T) bool { +func isCaseSensitive() (bool, error) { onceCaseSensitive.Do(func() { tmpdir, err := os.MkdirTemp("", "case-sensitive") if err != nil { - t.Fatal("failed to create directory to determine case-sensitivity:", err) + caseSensitiveErr = fmt.Errorf("failed to create directory to determine case-sensitivity: %w", err) + return } defer os.RemoveAll(tmpdir) fcap := filepath.Join(tmpdir, "FILE") if err := os.WriteFile(fcap, []byte{}, 0644); err != nil { - t.Fatal("error writing file to determine case-sensitivity:", err) + caseSensitiveErr = fmt.Errorf("error writing file to determine case-sensitivity: %w", err) + return } flow := filepath.Join(tmpdir, "file") @@ -438,18 +511,17 @@ func isCaseSensitive(t *testing.T) bool { caseSensitive = true return default: - t.Fatal("unexpected error reading file when determining case-sensitivity:", err) + caseSensitiveErr = fmt.Errorf("unexpected error reading file when determining case-sensitivity: %w", err) } }) - return caseSensitive + return caseSensitive, caseSensitiveErr } // scriptCmds are the script command implementations. // Keep list and the implementations below sorted by name. // // NOTE: If you make changes here, update testdata/script/README too! -// var scriptCmds = map[string]func(*testScript, simpleStatus, []string){ "addcrlf": (*testScript).cmdAddcrlf, "cc": (*testScript).cmdCc, @@ -464,8 +536,10 @@ var scriptCmds = map[string]func(*testScript, simpleStatus, []string){ "go": (*testScript).cmdGo, "grep": (*testScript).cmdGrep, "mkdir": (*testScript).cmdMkdir, + "mv": (*testScript).cmdMv, "rm": (*testScript).cmdRm, "skip": (*testScript).cmdSkip, + "sleep": (*testScript).cmdSleep, "stale": (*testScript).cmdStale, "stderr": (*testScript).cmdStderr, "stdout": (*testScript).cmdStdout, @@ -502,10 +576,13 @@ func (ts *testScript) cmdCc(want simpleStatus, args []string) { ts.fatalf("usage: cc args... [&]") } - var b work.Builder - b.Init() + b := work.NewBuilder(ts.workdir) + defer func() { + if err := b.Close(); err != nil { + ts.fatalf("%v", err) + } + }() ts.cmdExec(want, append(b.GccCmd(".", ""), args...)) - robustio.RemoveAll(b.WorkDir) } // cd changes to a different directory. @@ -558,10 +635,6 @@ func (ts *testScript) cmdChmod(want simpleStatus, args []string) { // cmp compares two files. func (ts *testScript) cmdCmp(want simpleStatus, args []string) { - if want != success { - // It would be strange to say "this file can have any content except this precise byte sequence". - ts.fatalf("unsupported: %v cmp", want) - } quiet := false if len(args) > 0 && args[0] == "-q" { quiet = true @@ -570,14 +643,11 @@ func (ts *testScript) cmdCmp(want simpleStatus, args []string) { if len(args) != 2 { ts.fatalf("usage: cmp file1 file2") } - ts.doCmdCmp(args, false, quiet) + ts.doCmdCmp(want, args, false, quiet) } // cmpenv compares two files with environment variable substitution. func (ts *testScript) cmdCmpenv(want simpleStatus, args []string) { - if want != success { - ts.fatalf("unsupported: %v cmpenv", want) - } quiet := false if len(args) > 0 && args[0] == "-q" { quiet = true @@ -586,17 +656,18 @@ func (ts *testScript) cmdCmpenv(want simpleStatus, args []string) { if len(args) != 2 { ts.fatalf("usage: cmpenv file1 file2") } - ts.doCmdCmp(args, true, quiet) + ts.doCmdCmp(want, args, true, quiet) } -func (ts *testScript) doCmdCmp(args []string, env, quiet bool) { +func (ts *testScript) doCmdCmp(want simpleStatus, args []string, env, quiet bool) { name1, name2 := args[0], args[1] var text1, text2 string - if name1 == "stdout" { + switch name1 { + case "stdout": text1 = ts.stdout - } else if name1 == "stderr" { + case "stderr": text1 = ts.stderr - } else { + default: data, err := os.ReadFile(ts.mkabs(name1)) ts.check(err) text1 = string(data) @@ -611,14 +682,28 @@ func (ts *testScript) doCmdCmp(args []string, env, quiet bool) { text2 = ts.expand(text2, false) } - if text1 == text2 { - return - } - - if !quiet { + eq := text1 == text2 + if !eq && !quiet && want != failure { fmt.Fprintf(&ts.log, "[diff -%s +%s]\n%s\n", name1, name2, diff(text1, text2)) } - ts.fatalf("%s and %s differ", name1, name2) + switch want { + case failure: + if eq { + ts.fatalf("%s and %s do not differ", name1, name2) + } + case success: + if !eq { + ts.fatalf("%s and %s differ", name1, name2) + } + case successOrFailure: + if eq { + fmt.Fprintf(&ts.log, "%s and %s do not differ\n", name1, name2) + } else { + fmt.Fprintf(&ts.log, "%s and %s differ\n", name1, name2) + } + default: + ts.fatalf("unsupported: %v cmp", want) + } } // cp copies files, maybe eventually directories. @@ -813,6 +898,16 @@ func (ts *testScript) cmdMkdir(want simpleStatus, args []string) { } } +func (ts *testScript) cmdMv(want simpleStatus, args []string) { + if want != success { + ts.fatalf("unsupported: %v mv", want) + } + if len(args) != 2 { + ts.fatalf("usage: mv old new") + } + ts.check(os.Rename(ts.mkabs(args[0]), ts.mkabs(args[1]))) +} + // rm removes files or directories. func (ts *testScript) cmdRm(want simpleStatus, args []string) { if want != success { @@ -848,6 +943,21 @@ func (ts *testScript) cmdSkip(want simpleStatus, args []string) { ts.t.Skip() } +// sleep sleeps for the given duration +func (ts *testScript) cmdSleep(want simpleStatus, args []string) { + if len(args) != 1 { + ts.fatalf("usage: sleep duration") + } + d, err := time.ParseDuration(args[0]) + if err != nil { + ts.fatalf("sleep: %v", err) + } + if want != success { + ts.fatalf("unsupported: %v sleep", want) + } + time.Sleep(d) +} + // stale checks that the named build targets are stale. func (ts *testScript) cmdStale(want simpleStatus, args []string) { if len(args) == 0 { @@ -856,9 +966,9 @@ func (ts *testScript) cmdStale(want simpleStatus, args []string) { tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{{else}}" switch want { case failure: - tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale{{end}}" + tmpl += `{{if .Stale}}{{.ImportPath}} ({{.Target}}) is unexpectedly stale:{{"\n\t"}}{{.StaleReason}}{{end}}` case success: - tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}" + tmpl += "{{if not .Stale}}{{.ImportPath}} ({{.Target}}) is unexpectedly NOT stale{{end}}" default: ts.fatalf("unsupported: %v stale", want) } @@ -866,10 +976,15 @@ func (ts *testScript) cmdStale(want simpleStatus, args []string) { goArgs := append([]string{"list", "-e", "-f=" + tmpl}, args...) stdout, stderr, err := ts.exec(testGo, goArgs...) if err != nil { + // Print stdout before stderr, because stderr may explain the error + // independent of whatever we may have printed to stdout. ts.fatalf("go list: %v\n%s%s", err, stdout, stderr) } if stdout != "" { - ts.fatalf("%s", stdout) + // Print stderr before stdout, because stderr may contain verbose + // debugging info (for example, if GODEBUG=gocachehash=1 is set) + // and we know that stdout contains a useful summary. + ts.fatalf("%s%s", stderr, stdout) } } @@ -1109,6 +1224,17 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args .. done: done, } + // Use the script's PATH to look up the command if it contains a separator + // instead of the test process's PATH (see lookPath). + // Don't use filepath.Clean, since that changes "./foo" to "foo". + command = filepath.FromSlash(command) + if !strings.Contains(command, string(filepath.Separator)) { + var err error + command, err = ts.lookPath(command) + if err != nil { + return nil, err + } + } cmd := exec.Command(command, args...) cmd.Dir = ts.cd cmd.Env = append(ts.env, "PWD="+ts.cd) @@ -1125,6 +1251,68 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args .. return bg, nil } +// lookPath is (roughly) like exec.LookPath, but it uses the test script's PATH +// instead of the test process's PATH to find the executable. We don't change +// the test process's PATH since it may run scripts in parallel. +func (ts *testScript) lookPath(command string) (string, error) { + var strEqual func(string, string) bool + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + // Using GOOS as a proxy for case-insensitive file system. + strEqual = strings.EqualFold + } else { + strEqual = func(a, b string) bool { return a == b } + } + + var pathExt []string + var searchExt bool + var isExecutable func(os.FileInfo) bool + if runtime.GOOS == "windows" { + // Use the test process's PathExt instead of the script's. + // If PathExt is set in the command's environment, cmd.Start fails with + // "parameter is invalid". Not sure why. + // If the command already has an extension in PathExt (like "cmd.exe") + // don't search for other extensions (not "cmd.bat.exe"). + pathExt = strings.Split(os.Getenv("PathExt"), string(filepath.ListSeparator)) + searchExt = true + cmdExt := filepath.Ext(command) + for _, ext := range pathExt { + if strEqual(cmdExt, ext) { + searchExt = false + break + } + } + isExecutable = func(fi os.FileInfo) bool { + return fi.Mode().IsRegular() + } + } else { + isExecutable = func(fi os.FileInfo) bool { + return fi.Mode().IsRegular() && fi.Mode().Perm()&0111 != 0 + } + } + + for _, dir := range strings.Split(ts.envMap[pathEnvName()], string(filepath.ListSeparator)) { + if searchExt { + ents, err := os.ReadDir(dir) + if err != nil { + continue + } + for _, ent := range ents { + for _, ext := range pathExt { + if !ent.IsDir() && strEqual(ent.Name(), command+ext) { + return dir + string(filepath.Separator) + ent.Name(), nil + } + } + } + } else { + path := dir + string(filepath.Separator) + command + if fi, err := os.Stat(path); err == nil && isExecutable(fi) { + return path, nil + } + } + } + return "", &exec.Error{Name: command, Err: exec.ErrNotFound} +} + // waitOrStop waits for the already-started command cmd by calling its Wait method. // // If cmd does not return before ctx is done, waitOrStop sends it the given interrupt signal. @@ -1151,7 +1339,7 @@ func waitOrStop(ctx context.Context, cmd *exec.Cmd, interrupt os.Signal, killDel err := cmd.Process.Signal(interrupt) if err == nil { err = ctx.Err() // Report ctx.Err() as the reason we interrupted. - } else if err.Error() == "os: process already finished" { + } else if err == os.ErrProcessDone { errc <- nil return } @@ -1204,7 +1392,7 @@ func (ts *testScript) expand(s string, inRegexp bool) string { } // fatalf aborts the test with the given failure message. -func (ts *testScript) fatalf(format string, args ...interface{}) { +func (ts *testScript) fatalf(format string, args ...any) { fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...)) ts.t.FailNow() } @@ -1235,7 +1423,9 @@ type command struct { // parse parses a single line as a list of space-separated arguments // subject to environment variable expansion (but not resplitting). // Single quotes around text disable splitting and expansion. -// To embed a single quote, double it: 'Don''t communicate by sharing memory.' +// To embed a single quote, double it: +// +// 'Don''t communicate by sharing memory.' func (ts *testScript) parse(line string) command { ts.line = line @@ -1341,6 +1531,68 @@ func (ts *testScript) parse(line string) command { return cmd } +// updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or +// 'go list -mod=mod all' in the test's current directory if a file named +// "go.mod" is present after the archive has been extracted. updateSum modifies +// archive and returns true if go.mod or go.sum were changed. +func (ts *testScript) updateSum(archive *txtar.Archive) (rewrite bool) { + gomodIdx, gosumIdx := -1, -1 + for i := range archive.Files { + switch archive.Files[i].Name { + case "go.mod": + gomodIdx = i + case "go.sum": + gosumIdx = i + } + } + if gomodIdx < 0 { + return false + } + + switch *testSum { + case "tidy": + ts.cmdGo(success, []string{"mod", "tidy"}) + case "listm": + ts.cmdGo(success, []string{"list", "-m", "-mod=mod", "all"}) + case "listall": + ts.cmdGo(success, []string{"list", "-mod=mod", "all"}) + default: + ts.t.Fatalf(`unknown value for -testsum %q; may be "tidy", "listm", or "listall"`, *testSum) + } + + newGomodData, err := os.ReadFile(filepath.Join(ts.cd, "go.mod")) + if err != nil { + ts.t.Fatalf("reading go.mod after -testsum: %v", err) + } + if !bytes.Equal(newGomodData, archive.Files[gomodIdx].Data) { + archive.Files[gomodIdx].Data = newGomodData + rewrite = true + } + + newGosumData, err := os.ReadFile(filepath.Join(ts.cd, "go.sum")) + if err != nil && !os.IsNotExist(err) { + ts.t.Fatalf("reading go.sum after -testsum: %v", err) + } + switch { + case os.IsNotExist(err) && gosumIdx >= 0: + // go.sum was deleted. + rewrite = true + archive.Files = append(archive.Files[:gosumIdx], archive.Files[gosumIdx+1:]...) + case err == nil && gosumIdx < 0: + // go.sum was created. + rewrite = true + gosumIdx = gomodIdx + 1 + archive.Files = append(archive.Files, txtar.File{}) + copy(archive.Files[gosumIdx+1:], archive.Files[gosumIdx:]) + archive.Files[gosumIdx] = txtar.File{Name: "go.sum", Data: newGosumData} + case err == nil && gosumIdx >= 0 && !bytes.Equal(newGosumData, archive.Files[gosumIdx].Data): + // go.sum was changed. + rewrite = true + archive.Files[gosumIdx].Data = newGosumData + } + return rewrite +} + // diff returns a formatted diff of the two texts, // showing the entire text and the minimum line-level // additions and removals to turn text1 into text2. diff --git a/src/cmd/go/stop_other_test.go b/src/cmd/go/stop_other_test.go index e1cc6cf8ba755f..cb4569b91d4a77 100644 --- a/src/cmd/go/stop_other_test.go +++ b/src/cmd/go/stop_other_test.go @@ -2,16 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !(aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris) -// +build !aix -// +build !darwin -// +build !dragonfly -// +build !freebsd -// +build !js !wasm -// +build !linux -// +build !netbsd -// +build !openbsd -// +build !solaris +//go:build !(unix || (js && wasm)) package main_test diff --git a/src/cmd/go/stop_unix_test.go b/src/cmd/go/stop_unix_test.go index ac35b240f0ad97..baa1427465d796 100644 --- a/src/cmd/go/stop_unix_test.go +++ b/src/cmd/go/stop_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package main_test diff --git a/src/cmd/go/testdata/addmod.go b/src/cmd/go/testdata/addmod.go index 03869e68defe08..e378d7f31a1597 100644 --- a/src/cmd/go/testdata/addmod.go +++ b/src/cmd/go/testdata/addmod.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Addmod adds a module as a txtar archive to the testdata/mod directory. @@ -22,14 +23,13 @@ import ( "bytes" "flag" "fmt" - exec "internal/execabs" + "internal/txtar" "io/fs" "log" "os" + "os/exec" "path/filepath" "strings" - - "cmd/go/internal/txtar" ) func usage() { @@ -39,7 +39,7 @@ func usage() { var tmpdir string -func fatalf(format string, args ...interface{}) { +func fatalf(format string, args ...any) { os.RemoveAll(tmpdir) log.Fatalf(format, args...) } diff --git a/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt new file mode 100644 index 00000000000000..af005ffb411908 --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt @@ -0,0 +1,20 @@ +-- .mod -- +module example.com/fuzzfail + +go 1.18 +-- .info -- +{"Version":"v0.1.0"} +-- go.mod -- +module example.com/fuzzfail + +go 1.18 +-- fuzzfail_test.go -- +package fuzzfail + +import "testing" + +func FuzzFail(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + t.Fatalf("oops: %q", b) + }) +} diff --git a/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt new file mode 100644 index 00000000000000..ea599aa61109aa --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt @@ -0,0 +1,23 @@ +-- .mod -- +module example.com/fuzzfail + +go 1.18 +-- .info -- +{"Version":"v0.2.0"} +-- go.mod -- +module example.com/fuzzfail + +go 1.18 +-- fuzzfail_test.go -- +package fuzzfail + +import "testing" + +func FuzzFail(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + t.Fatalf("oops: %q", b) + }) +} +-- testdata/fuzz/FuzzFail/bbb0c2d22aa1a24617301566dc7486f8b625d38024603ba62757c1124013b49a -- +go test fuzz v1 +[]byte("\x05") diff --git a/src/cmd/go/testdata/mod/example.com_retract_noupgrade_v1.0.0.txt b/src/cmd/go/testdata/mod/example.com_retract_noupgrade_v1.0.0.txt new file mode 100644 index 00000000000000..466afc576577c4 --- /dev/null +++ b/src/cmd/go/testdata/mod/example.com_retract_noupgrade_v1.0.0.txt @@ -0,0 +1,9 @@ +-- .mod -- +module example.com/retract/noupgrade + +go 1.19 + +retract v1.0.0 // bad + +-- .info -- +{"Version":"v1.0.0"} diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt index 35c3f277103df1..00076d74fc2f88 100644 --- a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt +++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt @@ -1,6 +1,6 @@ Written by hand. Test case for getting a package that has been moved to a nested module, -with a +incompatible verison (and thus no go.mod file) at the root module. +with a +incompatible version (and thus no go.mod file) at the root module. -- .mod -- module example.com/split-incompatible diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt index 917fc0f55992ef..bb1c1fecc9d271 100644 --- a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt +++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt @@ -1,6 +1,6 @@ Written by hand. Test case for getting a package that has been moved to a nested module, -with a +incompatible verison (and thus no go.mod file) at the root module. +with a +incompatible version (and thus no go.mod file) at the root module. -- .mod -- module example.com/split-incompatible diff --git a/src/cmd/go/testdata/modlegacy/src/new/go.mod b/src/cmd/go/testdata/modlegacy/src/new/go.mod deleted file mode 100644 index d0dd46d314c6a3..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/go.mod +++ /dev/null @@ -1 +0,0 @@ -module "new/v2" diff --git a/src/cmd/go/testdata/modlegacy/src/new/new.go b/src/cmd/go/testdata/modlegacy/src/new/new.go deleted file mode 100644 index e99c47a6a8411f..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/new.go +++ /dev/null @@ -1,3 +0,0 @@ -package new - -import _ "new/v2/p2" diff --git a/src/cmd/go/testdata/modlegacy/src/new/p1/p1.go b/src/cmd/go/testdata/modlegacy/src/new/p1/p1.go deleted file mode 100644 index 4539f409196536..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/p1/p1.go +++ /dev/null @@ -1,7 +0,0 @@ -package p1 - -import _ "old/p2" -import _ "new/v2" -import _ "new/v2/p2" -import _ "new/sub/v2/x/v1/y" // v2 is module, v1 is directory in module -import _ "new/sub/inner/x" // new/sub/inner/go.mod overrides new/sub/go.mod diff --git a/src/cmd/go/testdata/modlegacy/src/new/p2/p2.go b/src/cmd/go/testdata/modlegacy/src/new/p2/p2.go deleted file mode 100644 index 9b9052f54197e6..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/p2/p2.go +++ /dev/null @@ -1 +0,0 @@ -package p2 diff --git a/src/cmd/go/testdata/modlegacy/src/new/sub/go.mod b/src/cmd/go/testdata/modlegacy/src/new/sub/go.mod deleted file mode 100644 index 484d20c6b2ea50..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/sub/go.mod +++ /dev/null @@ -1 +0,0 @@ -module new/sub/v2 diff --git a/src/cmd/go/testdata/modlegacy/src/new/sub/inner/go.mod b/src/cmd/go/testdata/modlegacy/src/new/sub/inner/go.mod deleted file mode 100644 index ba3934541f745f..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/sub/inner/go.mod +++ /dev/null @@ -1 +0,0 @@ -module new/sub/inner diff --git a/src/cmd/go/testdata/modlegacy/src/new/sub/inner/x/x.go b/src/cmd/go/testdata/modlegacy/src/new/sub/inner/x/x.go deleted file mode 100644 index 823aafd0712b6c..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/sub/inner/x/x.go +++ /dev/null @@ -1 +0,0 @@ -package x diff --git a/src/cmd/go/testdata/modlegacy/src/new/sub/x/v1/y/y.go b/src/cmd/go/testdata/modlegacy/src/new/sub/x/v1/y/y.go deleted file mode 100644 index 789ca715ec4601..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/new/sub/x/v1/y/y.go +++ /dev/null @@ -1 +0,0 @@ -package y diff --git a/src/cmd/go/testdata/modlegacy/src/old/p1/p1.go b/src/cmd/go/testdata/modlegacy/src/old/p1/p1.go deleted file mode 100644 index 90527483abf2c2..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/old/p1/p1.go +++ /dev/null @@ -1,5 +0,0 @@ -package p1 - -import _ "old/p2" -import _ "new/p1" -import _ "new" diff --git a/src/cmd/go/testdata/modlegacy/src/old/p2/p2.go b/src/cmd/go/testdata/modlegacy/src/old/p2/p2.go deleted file mode 100644 index 9b9052f54197e6..00000000000000 --- a/src/cmd/go/testdata/modlegacy/src/old/p2/p2.go +++ /dev/null @@ -1 +0,0 @@ -package p2 diff --git a/src/cmd/go/testdata/savedir.go b/src/cmd/go/testdata/savedir.go index d469c31a919821..eaafc5e4939b38 100644 --- a/src/cmd/go/testdata/savedir.go +++ b/src/cmd/go/testdata/savedir.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build ignore // +build ignore // Savedir archives a directory tree as a txtar archive printed to standard output. @@ -17,14 +18,13 @@ package main import ( "flag" "fmt" + "internal/txtar" "io/fs" "log" "os" "path/filepath" "strings" "unicode/utf8" - - "../internal/txtar" ) func usage() { diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README index 48e4055b0bdb97..e52917684f8c86 100644 --- a/src/cmd/go/testdata/script/README +++ b/src/cmd/go/testdata/script/README @@ -7,7 +7,7 @@ In general script files should have short names: a few words, not whole sentence The first word should be the general category of behavior being tested, often the name of a go subcommand (list, build, test, ...) or concept (vendor, pattern). -Each script is a text archive (go doc cmd/go/internal/txtar). +Each script is a text archive (go doc internal/txtar). The script begins with an actual command script to run followed by the content of zero or more supporting files to create in the script's temporary file system before it starts executing. @@ -41,12 +41,19 @@ Scripts also have access to these other environment variables: GODEBUG= devnull= goversion= - := -The scripts' supporting files are unpacked relative to $GOPATH/src (aka $WORK/gopath/src) -and then the script begins execution in that directory as well. Thus the example above runs -in $WORK/gopath/src with GOPATH=$WORK/gopath and $WORK/gopath/src/hello.go -containing the listed contents. +On Plan 9, the variables $path and $home are set instead of $PATH and $HOME. +On Windows, the variables $USERPROFILE and $TMP are set instead of +$HOME and $TMPDIR. + +In addition, variables named ':' and '/' are expanded within script arguments +(expanding to the value of os.PathListSeparator and os.PathSeparator +respectively) but are not inherited in subprocess environments. + +The scripts' supporting files are unpacked relative to $GOPATH/src +(aka $WORK/gopath/src) and then the script begins execution in that directory as +well. Thus the example above runs in $WORK/gopath/src with GOPATH=$WORK/gopath +and $WORK/gopath/src/hello.go containing the listed contents. The lines at the top of the script are a sequence of commands to be executed by a tiny script engine in ../../script_test.go (not the system shell). @@ -79,7 +86,9 @@ should only run when the condition is satisfied. The available conditions are: - Compiler names, like [gccgo], [gc]. - Test environment details: - [short] for testing.Short() - - [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used + - [cgo], [msan], [asan], [race] for whether cgo, msan, asan, and the race detector can be used + - [fuzz] for whether 'go test -fuzz' can be used at all + - [fuzz-instrumented] for whether 'go test -fuzz' uses coverage-instrumented binaries - [net] for whether the external network can be used - [link] for testenv.HasLink() - [root] for os.Geteuid() == 0 @@ -88,6 +97,8 @@ should only run when the condition is satisfied. The available conditions are: - [exec:prog] for whether prog is available for execution (found by exec.LookPath) - [GODEBUG:value] for whether value is one of the comma-separated entries in the GODEBUG variable - [buildmode:value] for whether -buildmode=value is supported + - [trimpath] for whether the 'go' binary was built with -trimpath + - [mismatched-goroot] for whether the test's GOROOT_FINAL does not match the real GOROOT A condition can be negated: [!short] means to run the rest of the line when testing.Short() is false. Multiple conditions may be given for a single @@ -108,14 +119,15 @@ The commands are: Change the permissions of the files or directories named by the path arguments to be equal to perm. Only numerical permissions are supported. -- cmp file1 file2 - Check that the named files have the same content. +- [! | ?] cmp file1 file2 + Check that the named files have (or do not have) the same content. By convention, file1 is the actual data and file2 the expected data. File1 can be "stdout" or "stderr" to use the standard output or standard error from the most recent exec or go command. - (If the files have differing content, the failure prints a diff.) + (If the file contents differ and the command is not negated, + the failure prints a diff.) -- cmpenv file1 file2 +- [! | ?] cmpenv file1 file2 Like cmp, but environment variables are substituted in the file contents before the comparison. For example, $GOOS is replaced by the target GOOS. @@ -161,12 +173,21 @@ The commands are: - mkdir path... Create the listed directories, if they do not already exists. +- mv path1 path2 + Rename path1 to path2. OS-specific restrictions may apply when path1 and path2 + are in different directories. + - rm file... Remove the listed files or directories. - skip [message] Mark the test skipped, including the message if given. +- sleep duration + Sleep for the given duration (a time.Duration string). + (Tests should generally poll instead of sleeping, but sleeping may sometimes + be necessary, for example, to ensure that modified files have unique mtimes.) + - [!] stale path... The packages named by the path arguments must (or must not) be reported as "stale" by the go command. diff --git a/src/cmd/go/testdata/script/build_buildvcs_auto.txt b/src/cmd/go/testdata/script/build_buildvcs_auto.txt new file mode 100644 index 00000000000000..dd9eef5f8289e5 --- /dev/null +++ b/src/cmd/go/testdata/script/build_buildvcs_auto.txt @@ -0,0 +1,91 @@ +# Regression test for https://go.dev/issue/51748: by default, 'go build' should +# not attempt to stamp VCS information when the VCS tool is not present. + +[short] skip +[!exec:git] skip + +cd sub +exec git init . +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git add sub.go +exec git commit -m 'initial state' +cd .. + +exec git init +exec git config user.name 'Nameless Gopher' +exec git config user.email 'nobody@golang.org' +exec git submodule add ./sub +exec git add go.mod example.go +exec git commit -m 'initial state' + + +# Control case: with a git binary in $PATH, +# 'go build' on a package in the same git repo +# succeeds and stamps VCS metadata by default. + +go build -o example.exe . +go version -m example.exe +stdout '^\tbuild\tvcs=git$' +stdout '^\tbuild\tvcs.modified=false$' + + +# Building a binary from a different (nested) VCS repo should not stamp VCS +# info. It should be an error if VCS stamps are requested explicitly with +# '-buildvcs' (since we know the VCS metadata exists), but not an error +# with '-buildvcs=auto'. + +go build -o sub.exe ./sub +go version -m sub.exe +! stdout '^\tbuild\tvcs' + +! go build -buildvcs -o sub.exe ./sub +stderr '\Aerror obtaining VCS status: main package is in repository ".*" but current directory is in repository ".*"\n\tUse -buildvcs=false to disable VCS stamping.\n\z' + +cd ./sub +go build -o sub.exe . +go version -m sub.exe +! stdout '^\tbuild\tvcs' + +! go build -buildvcs -o sub.exe . +stderr '\Aerror obtaining VCS status: main module is in repository ".*" but current directory is in repository ".*"\n\tUse -buildvcs=false to disable VCS stamping.\n\z' +cd .. + + +# After removing 'git' from $PATH, 'go build -buildvcs' should fail... + +env PATH= +env path= +! go build -buildvcs -o example.exe . +stderr 'go: missing Git command\. See https://golang\.org/s/gogetcmd$' + +# ...but by default we should omit VCS metadata when the tool is missing. + +go build -o example.exe . +go version -m example.exe +! stdout '^\tbuild\tvcs' + +# The default behavior can be explicitly set with '-buildvcs=auto'. + +go build -buildvcs=auto -o example.exe . +go version -m example.exe +! stdout '^\tbuild\tvcs' + +# Other flag values should be rejected with a useful error message. + +! go build -buildvcs=hg -o example.exe . +stderr '\Ainvalid boolean value "hg" for -buildvcs: value is neither ''auto'' nor a valid bool\nusage: go build .*\nRun ''go help build'' for details.\n\z' + + +-- go.mod -- +module example + +go 1.18 +-- example.go -- +package main + +func main() {} +-- sub/sub.go -- +package main + +func main() {} diff --git a/src/cmd/go/testdata/script/build_cache_disabled.txt b/src/cmd/go/testdata/script/build_cache_disabled.txt index 8b005c857fcf99..cb1a7558fca71f 100644 --- a/src/cmd/go/testdata/script/build_cache_disabled.txt +++ b/src/cmd/go/testdata/script/build_cache_disabled.txt @@ -9,7 +9,7 @@ # * go fix # * go fmt # * go generate -# * go get -d +# * go get # * go list (without -export or -compiled) env GOCACHE=off diff --git a/src/cmd/go/testdata/script/build_cache_trimpath.txt b/src/cmd/go/testdata/script/build_cache_trimpath.txt index 9a4b9d7b40c726..7ee3c3b41d489a 100644 --- a/src/cmd/go/testdata/script/build_cache_trimpath.txt +++ b/src/cmd/go/testdata/script/build_cache_trimpath.txt @@ -16,10 +16,10 @@ stderr 'link( |\.exe)' # Two distinct versions of the same module with identical content should # still be cached separately. # Verifies golang.org/issue/35412. -go get -d example.com/stack@v1.0.0 +go get example.com/stack@v1.0.0 go run -trimpath printstack.go stdout '^example.com/stack@v1.0.0/stack.go$' -go get -d example.com/stack@v1.0.1 +go get example.com/stack@v1.0.1 go run -trimpath printstack.go stdout '^example.com/stack@v1.0.1/stack.go$' diff --git a/src/cmd/go/testdata/script/build_concurrent_backend.txt b/src/cmd/go/testdata/script/build_concurrent_backend.txt new file mode 100644 index 00000000000000..9cac635e5a6760 --- /dev/null +++ b/src/cmd/go/testdata/script/build_concurrent_backend.txt @@ -0,0 +1,10 @@ +# Tests golang.org/issue/48490 +# cmd/go should enable concurrent compilation by default + +# Reset all experiments, since one of them can disable +# concurrent compilation, e.g: fieldtrack. +env GOEXPERIMENT=none + +env GOMAXPROCS=4 +go build -n -x -a fmt +stderr ' -c=4 ' diff --git a/src/cmd/go/testdata/script/build_gcflags_order.txt b/src/cmd/go/testdata/script/build_gcflags_order.txt new file mode 100644 index 00000000000000..0ffe1570f6c6e4 --- /dev/null +++ b/src/cmd/go/testdata/script/build_gcflags_order.txt @@ -0,0 +1,20 @@ +# Tests golang.org/issue/47682 +# Flags specified with -gcflags should appear after other flags generated by cmd/go. + +cd m +go build -n -gcflags=-lang=go1.17 +stderr ' -lang=go1.16.* -lang=go1.17' + +-- m/go.mod -- +module example.com + +go 1.16 + +-- m/main.go -- +package main + +func main() { + var s = []int{1, 2, 3} + var pa = (*[2]int)(s[1:]) + println(pa[1]) +} diff --git a/src/cmd/go/testdata/script/build_i_deprecate.txt b/src/cmd/go/testdata/script/build_i_deprecate.txt index 71356e5321eb1c..5c1799566999e4 100644 --- a/src/cmd/go/testdata/script/build_i_deprecate.txt +++ b/src/cmd/go/testdata/script/build_i_deprecate.txt @@ -2,13 +2,13 @@ # TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test. go build -n -i -stderr '^go build: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go install -n -i -stderr '^go install: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go test -n -i -stderr '^go test: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' # 'go clean -i' should not print a deprecation warning. diff --git a/src/cmd/go/testdata/script/build_internal.txt b/src/cmd/go/testdata/script/build_internal.txt index 25aa18cfcb33c0..5b786f2fbce509 100644 --- a/src/cmd/go/testdata/script/build_internal.txt +++ b/src/cmd/go/testdata/script/build_internal.txt @@ -10,8 +10,10 @@ stderr 'internal' # Test internal packages outside GOROOT are respected cd ../testinternal2 +env GO111MODULE=off ! go build -v . stderr 'p\.go:3:8: use of internal package .*internal/w not allowed' +env GO111MODULE='' [gccgo] skip # gccgo does not have GOROOT cd ../testinternal diff --git a/src/cmd/go/testdata/script/build_issue48319.txt b/src/cmd/go/testdata/script/build_issue48319.txt new file mode 100644 index 00000000000000..3979247f2fd962 --- /dev/null +++ b/src/cmd/go/testdata/script/build_issue48319.txt @@ -0,0 +1,47 @@ +# Regression test for https://go.dev/issue/48319: +# cgo builds should not include debug information from a stale GOROOT_FINAL. + +[short] skip +[!cgo] skip +[windows] skip # The Go Windows builders have an extremely out-of-date gcc that does not support reproducible builds; see https://go.dev/issue/50824. + +# This test is sensitive to cache invalidation, +# so use a separate build cache that we can control. +env GOCACHE=$WORK/gocache +mkdir $GOCACHE + +# Build a binary using a specific value of GOROOT_FINAL. +env GOROOT_FINAL=$WORK${/}goroot1 +go build -o main.exe +mv main.exe main1.exe + +# Now clean the cache and build using a different GOROOT_FINAL. +# The resulting binaries should differ in their debug metadata. +go clean -cache +env GOROOT_FINAL=$WORK${/}goroot2 +go build -o main.exe +mv main.exe main2.exe +! cmp main2.exe main1.exe + +# Set GOROOT_FINAL back to the first value. +# If the build is properly reproducible, the two binaries should match. +env GOROOT_FINAL=$WORK${/}goroot1 +go build -o main.exe +cmp -q main.exe main1.exe + +-- go.mod -- +module main + +go 1.18 +-- main.go -- +package main + +import "C" + +import "runtime" + +var _ C.int + +func main() { + println(runtime.GOROOT()) +} diff --git a/src/cmd/go/testdata/script/build_negative_p.txt b/src/cmd/go/testdata/script/build_negative_p.txt new file mode 100644 index 00000000000000..9123907dc87a40 --- /dev/null +++ b/src/cmd/go/testdata/script/build_negative_p.txt @@ -0,0 +1,5 @@ +! go build -p=-1 example.go +stderr 'go: -p must be a positive integer: -1' + +-- example.go -- +package example \ No newline at end of file diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt index 2932b94e6c42c7..c9c6a7f9dad72e 100644 --- a/src/cmd/go/testdata/script/build_overlay.txt +++ b/src/cmd/go/testdata/script/build_overlay.txt @@ -31,39 +31,44 @@ exec ./print_trimpath_two_files$GOEXE stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go -go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace -exec ./main_cgo_replace$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace +[cgo] exec ./main_cgo_replace$GOEXE +[cgo] stdout '^hello cgo\r?\n' -go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote -exec ./main_cgo_quote$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote +[cgo] exec ./main_cgo_quote$GOEXE +[cgo] stdout '^hello cgo\r?\n' -go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle -exec ./main_cgo_angle$GOEXE -stdout '^hello cgo\r?\n' +[cgo] go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle +[cgo] exec ./main_cgo_angle$GOEXE +[cgo] stdout '^hello cgo\r?\n' go build -overlay overlay.json -o main_call_asm$GOEXE ./call_asm exec ./main_call_asm$GOEXE ! stdout . +[cgo] go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace +[cgo] cp stdout compiled_cgo_sources.txt +[cgo] go run ../print_line_comments.go compiled_cgo_sources.txt +[cgo] stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go +[cgo] ! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c + # Change the contents of a file in the overlay and ensure that makes the target stale -go install -overlay overlay.json ./test_cache -go list -overlay overlay.json -f '{{.Stale}}' ./test_cache -stdout '^false$' +env OLD_GOCACHE=$GOCACHE +env GOCACHE=$WORK/cache # use a fresh cache so that multiple runs of the test don't interfere +go build -x -overlay overlay.json ./test_cache +stderr '(compile|gccgo)( |\.exe).*test_cache.go' +go build -x -overlay overlay.json ./test_cache +! stderr '(compile|gccgo)( |\.exe).*test_cache.go' # cached cp overlay/test_cache_different.go overlay/test_cache.go -go list -overlay overlay.json -f '{{.Stale}}' ./test_cache -stdout '^true$' - -go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace -cp stdout compiled_cgo_sources.txt -go run ../print_line_comments.go compiled_cgo_sources.txt -stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go -! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c +go build -x -overlay overlay.json ./test_cache +stderr '(compile|gccgo)( |\.exe).*test_cache.go' # not cached +env CACHE=$OLD_GOCACHE # Run same tests but with gccgo. env GO111MODULE=off [!exec:gccgo] stop +[cross] stop # gccgo can't necessarily cross-compile ! go build -compiler=gccgo . go build -compiler=gccgo -overlay overlay.json -o main_gccgo$GOEXE . @@ -78,6 +83,11 @@ go build -compiler=gccgo -overlay overlay.json -o print_trimpath_gccgo$GOEXE -tr exec ./print_trimpath_gccgo$GOEXE stdout ^\.[/\\]printpath[/\\]main.go +go build -compiler=gccgo -overlay overlay.json -o main_call_asm_gccgo$GOEXE ./call_asm +exec ./main_call_asm_gccgo$GOEXE +! stdout . + +skip 'broken as of CL 421879: see https://go.dev/issue/54761' go build -compiler=gccgo -overlay overlay.json -o main_cgo_replace_gccgo$GOEXE ./cgo_hello_replace exec ./main_cgo_replace_gccgo$GOEXE @@ -91,10 +101,6 @@ go build -compiler=gccgo -overlay overlay.json -o main_cgo_angle_gccgo$GOEXE ./ exec ./main_cgo_angle_gccgo$GOEXE stdout '^hello cgo\r?\n' -go build -compiler=gccgo -overlay overlay.json -o main_call_asm_gccgo$GOEXE ./call_asm -exec ./main_call_asm_gccgo$GOEXE -! stdout . - -- m/go.mod -- // TODO(matloob): how do overlays work with go.mod (especially if mod=readonly) diff --git a/src/cmd/go/testdata/script/build_runtime_gcflags.txt b/src/cmd/go/testdata/script/build_runtime_gcflags.txt index da1b65f06c8497..c87e480911933a 100644 --- a/src/cmd/go/testdata/script/build_runtime_gcflags.txt +++ b/src/cmd/go/testdata/script/build_runtime_gcflags.txt @@ -8,4 +8,4 @@ mkdir $GOCACHE # Verify the standard library (specifically runtime/internal/atomic) can be # built with -gcflags when -n is given. See golang.org/issue/29346. go build -n -gcflags=all='-l' std -stderr 'compile.* -l .* runtime/internal/atomic' +stderr 'compile.* runtime/internal/atomic .* -l' diff --git a/src/cmd/go/testdata/script/build_single_error.txt b/src/cmd/go/testdata/script/build_single_error.txt new file mode 100644 index 00000000000000..241cdb954ba766 --- /dev/null +++ b/src/cmd/go/testdata/script/build_single_error.txt @@ -0,0 +1,18 @@ +# go test ./... with a bad package should report the error once (#44624). +! go test ./... +stderr -count=1 undefined + +-- go.mod -- +module example.com + +go 1.18 +-- a/a.go -- +package a + +import "example.com/b" +-- b/b.go -- +package b + +var X = Y +-- b/b_test.go -- +package b diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt index 2c3bee8fdc7a92..2a2aa2080a1411 100644 --- a/src/cmd/go/testdata/script/build_trimpath.txt +++ b/src/cmd/go/testdata/script/build_trimpath.txt @@ -32,7 +32,8 @@ stdout 'binary contains GOROOT: false' # A binary from an external module built with -trimpath should not contain # the current workspace or GOROOT. -go get -trimpath rsc.io/fortune +go get rsc.io/fortune +go install -trimpath rsc.io/fortune exec $WORK/paths-a.exe $GOPATH/bin/fortune$GOEXE stdout 'binary contains module root: false' stdout 'binary contains GOROOT: false' @@ -94,6 +95,7 @@ cmp -q paths-a.exe paths-b.exe # Same sequence of tests but with gccgo. # gccgo does not support builds in module mode. [!exec:gccgo] stop +[cross] stop # gccgo can't necessarily cross-compile env GOPATH=$WORK/a # A binary built with gccgo without -trimpath should contain the current diff --git a/src/cmd/go/testdata/script/build_trimpath_goroot.txt b/src/cmd/go/testdata/script/build_trimpath_goroot.txt new file mode 100644 index 00000000000000..a26cfd23be488c --- /dev/null +++ b/src/cmd/go/testdata/script/build_trimpath_goroot.txt @@ -0,0 +1,103 @@ +# Regression test for https://go.dev/issue/51461 and https://go.dev/issue/51483. +# +# When built with -trimpath, runtime.GOROOT() returned the bogus string "go" +# if GOROOT was not set explicitly in the environment. +# It should instead return the empty string, since we know that we don't +# have a valid path to return. +# +# TODO(#51483): when runtime.GOROOT() returns the empty string, +# go/build should default to 'go env GOROOT' instead. + +env GOROOT_FINAL= + +[trimpath] env GOROOT= +[trimpath] ! go env GOROOT +[trimpath] stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$' +[trimpath] env GOROOT=$TESTGO_GOROOT + +[short] stop + +# With GOROOT still set but GOROOT_FINAL unset, 'go build' and 'go test -c' +# should cause runtime.GOROOT() to report either the correct GOROOT +# (without -trimpath) or no GOROOT at all (with -trimpath). + +go build -o example.exe . +go build -trimpath -o example-trimpath.exe . +go test -c -o example.test.exe . +go test -trimpath -c -o example.test-trimpath.exe . + +env GOROOT= + +exec ./example.exe +stdout '^GOROOT '$TESTGO_GOROOT'$' +stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$' + +! exec ./example-trimpath.exe +stdout '^GOROOT $' +stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)\n\z' + +exec ./example.test.exe -test.v +stdout '^GOROOT '$TESTGO_GOROOT'$' +stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$' + +! exec ./example.test-trimpath.exe -test.v +stdout '^GOROOT $' +stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)$' + +# If a correct GOROOT is baked in to the 'go' command itself, 'go run' and +# 'go test' should not implicitly set GOROOT in the process environment +# (because that could mask an unexpected production dependency on the GOROOT +# environment variable), but 'go generate' should (because the generator may +# reasonably expect to be able to locate the GOROOT for which it is generating +# code). + +[trimpath] stop +[mismatched-goroot] stop + +! go run -trimpath . +stdout '^GOROOT $' +stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)\nexit status 1\n\z' + +! go test -trimpath -v . +stdout '^GOROOT $' +stdout 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)$' + +env GOFLAGS=-trimpath +go generate . +stdout '^GOROOT '$TESTGO_GOROOT'$' +stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$' + +-- go.mod -- +module example + +go 1.19 +-- main.go -- +package main + +//go:generate go run . + +import ( + "fmt" + "go/build" + "os" + "runtime" +) + +func main() { + fmt.Println("GOROOT", runtime.GOROOT()) + + p, err := build.Default.Import("runtime", "", build.FindOnly) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println("runtime", p.Dir) +} +-- main_test.go -- +package main + +import "testing" + +func TestMain(*testing.M) { + main() +} diff --git a/src/cmd/go/testdata/script/cgo_path.txt b/src/cmd/go/testdata/script/cgo_path.txt index be9609e86f0ddb..1f84dbc5b40ce3 100644 --- a/src/cmd/go/testdata/script/cgo_path.txt +++ b/src/cmd/go/testdata/script/cgo_path.txt @@ -14,7 +14,7 @@ env GOCACHE=$WORK/gocache # Looking for compile flags, so need a clean cache. [windows] exists -exec p/gcc.bat p/clang.bat ! exists p/bug.txt ! go build -x -stderr '^cgo: exec (clang|gcc): (clang|gcc) resolves to executable relative to current directory \(.[/\\](clang|gcc)(.bat)?\)$' +stderr '^cgo: C compiler "(clang|gcc)" not found: exec: "(clang|gcc)": cannot run executable found relative to current directory' ! exists p/bug.txt -- go.mod -- diff --git a/src/cmd/go/testdata/script/cgo_path_space_quote.txt b/src/cmd/go/testdata/script/cgo_path_space_quote.txt new file mode 100644 index 00000000000000..955610130088d5 --- /dev/null +++ b/src/cmd/go/testdata/script/cgo_path_space_quote.txt @@ -0,0 +1,58 @@ +# This test checks that the CC environment variable may contain quotes and +# spaces. Arguments are normally split on spaces, tabs, newlines. If an +# argument contains these characters, the entire argument may be quoted +# with single or double quotes. This is the same as -gcflags and similar +# options. + +[short] skip +[!exec:clang] [!exec:gcc] skip +[!cgo] skip + +env GOENV=$WORK/go.env +mkdir 'program files' +go build -o 'program files' './which cc/which cc.go' +[exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'clang +[!exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'gcc +go env CC +stdout 'program files[/\\]which cc" (clang|gcc)$' +go env -w CC=$CC +env CC= +go env CC +stdout 'program files[/\\]which cc" (clang|gcc)$' + +go run . +stdout 1 + +-- go.mod -- +module test + +go 1.17 +-- which cc/which cc.go -- +package main + +import ( + "fmt" + "os" + "os/exec" +) + +func main() { + args := append([]string{"-DWRAPPER_WAS_USED=1"}, os.Args[2:]...) + cmd := exec.Command(os.Args[1], args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} +-- hello.go -- +package main + +// int x = WRAPPER_WAS_USED; +import "C" +import "fmt" + +func main() { + fmt.Println(C.x) +} diff --git a/src/cmd/go/testdata/script/cgo_stale_precompiled.txt b/src/cmd/go/testdata/script/cgo_stale_precompiled.txt new file mode 100644 index 00000000000000..80ed751afcd194 --- /dev/null +++ b/src/cmd/go/testdata/script/cgo_stale_precompiled.txt @@ -0,0 +1,28 @@ +# Regression test for https://go.dev/issue/47215 and https://go.dev/issue/50183: +# A mismatched $GOROOT_FINAL or missing $CC caused the C dependencies of the net +# package to appear stale, and it could not be rebuilt due to a missing $CC. + +[!cgo] skip + +# This test may start with the runtime/cgo package already stale. +# Explicitly rebuild it to ensure that it is cached. +# (See https://go.dev/issue/50892.) +# +# If running in non-short mode, explicitly vary CGO_CFLAGS +# as a control case (to ensure that our regexps do catch rebuilds). + +[!short] env GOCACHE=$WORK/cache +[!short] env CGO_CFLAGS=-DTestScript_cgo_stale_precompiled=true +go build -x runtime/cgo +[!short] stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' + +# https://go.dev/issue/47215: a missing $(go env CC) caused the precompiled net to be stale. +[!plan9] env PATH='' # Guaranteed not to include $(go env CC)! +[plan9] env path='' +go build -x runtime/cgo +! stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' + +# https://go.dev/issue/50183: a mismatched GOROOT_FINAL caused net to be stale. +env GOROOT_FINAL=$WORK${/}goroot +go build -x runtime/cgo +! stderr '[/\\]cgo'$GOEXE'["]? .* -importpath runtime/cgo' diff --git a/src/cmd/go/testdata/script/cgo_undef.txt b/src/cmd/go/testdata/script/cgo_undef.txt new file mode 100644 index 00000000000000..30034fbac14fb6 --- /dev/null +++ b/src/cmd/go/testdata/script/cgo_undef.txt @@ -0,0 +1,68 @@ +# Issue 52863. + +# We manually create a .syso and a .a file in package a, +# such that the .syso file only works when linked against the .a file. +# Package a has #cgo LDFLAGS to make this happen. +# +# Package c imports package a, and uses cgo itself. +# The generation of the _cgo_import.go for package c will fail, +# because it won't know that it has to link against a/libb.a +# (because we don't gather the #cgo LDFLAGS from all transitively +# imported packages). +# +# The _cgo_import.go file is only needed for internal linking. +# When generating _cgo_import.go for package c fails, an ordinary +# external link should still work. But an internal link is expected +# to fail, because the failure to create _cgo_import.go should cause +# the linker to report an inability to internally link. + +[short] skip +[!cgo] skip +[!exec:ar] skip + +cc -c -o a/b.syso b/b.c +cc -c -o b/lib.o b/lib.c +exec ar rc a/libb.a b/lib.o +go build +! go build -ldflags=-linkmode=internal +stderr 'some packages could not be built to support internal linking.*m/c|requires external linking|does not support internal cgo' + +-- go.mod -- +module m + +-- a/a.go -- +package a + +// #cgo LDFLAGS: -L. -lb +// extern int CFn(int); +import "C" + +func GoFn(v int) int { return int(C.CFn(C.int(v))) } + +-- b/b.c -- +extern int LibFn(int); +int CFn(int i) { return LibFn(i); } + +-- b/lib.c -- +int LibFn(int i) { return i; } + +-- c/c.go -- +package c + +// static int D(int i) { return i; } +import "C" + +import "m/a" + +func Fn(i int) (int, int) { + return a.GoFn(i), int(C.D(C.int(i))) +} + +-- main.go -- +package main + +import "m/c" + +func main() { + println(c.Fn(0)) +} diff --git a/src/cmd/go/testdata/script/clean_cache_n.txt b/src/cmd/go/testdata/script/clean_cache_n.txt index 4497b36bc3d83e..72f9abf9ae1c82 100644 --- a/src/cmd/go/testdata/script/clean_cache_n.txt +++ b/src/cmd/go/testdata/script/clean_cache_n.txt @@ -15,6 +15,9 @@ exists $GOCACHE/00 go clean -cache ! exists $GOCACHE/00 +! go clean -cache . +stderr 'go: clean -cache cannot be used with package arguments' + -- main.go -- package main diff --git a/src/cmd/go/testdata/script/clean_testcache.txt b/src/cmd/go/testdata/script/clean_testcache.txt index b3f32fe696af75..3f98602c4ea6a6 100644 --- a/src/cmd/go/testdata/script/clean_testcache.txt +++ b/src/cmd/go/testdata/script/clean_testcache.txt @@ -8,6 +8,8 @@ go test x_test.go go clean -testcache go test x_test.go ! stdout 'cached' +! go clean -testcache ../x +stderr 'go: clean -testcache cannot be used with package arguments' # golang.org/issue/29100: 'go clean -testcache' should succeed # if the cache directory doesn't exist at all. diff --git a/src/cmd/go/testdata/script/embed.txt b/src/cmd/go/testdata/script/embed.txt index 04b17cd62b3859..5f7f6edd77e7f9 100644 --- a/src/cmd/go/testdata/script/embed.txt +++ b/src/cmd/go/testdata/script/embed.txt @@ -60,6 +60,18 @@ rm t/x.txt ! go build m/use stderr '^x.go:5:12: pattern [*]t: cannot embed directory t: contains no embeddable files$' +# all still ignores .git and symlinks +cp x.go3 x.go +! go build -x +stderr '^x.go:5:12: pattern all:t: cannot embed directory t: contains no embeddable files$' + +# all finds dot files and underscore files +cp x.txt t/.x.txt +go build -x +rm t/.x.txt +cp x.txt t/_x.txt +go build -x + -- x.go -- package p @@ -92,6 +104,14 @@ import "embed" //go:embed *t var X embed.FS +-- x.go3 -- +package p + +import "embed" + +//go:embed all:t +var X embed.FS + -- x.txt -- hello diff --git a/src/cmd/go/testdata/script/embed_brackets.txt b/src/cmd/go/testdata/script/embed_brackets.txt new file mode 100644 index 00000000000000..7093a8497e145f --- /dev/null +++ b/src/cmd/go/testdata/script/embed_brackets.txt @@ -0,0 +1,18 @@ +# issue 53314 +[windows] skip +cd [pkg] +go build + +-- [pkg]/go.mod -- +module m + +go 1.19 +-- [pkg]/x.go -- +package p + +import _ "embed" + +//go:embed t.txt +var S string + +-- [pkg]//t.txt -- diff --git a/src/cmd/go/testdata/script/env_unset.txt b/src/cmd/go/testdata/script/env_unset.txt index 4e0f249509873e..22bc845c37bf4e 100644 --- a/src/cmd/go/testdata/script/env_unset.txt +++ b/src/cmd/go/testdata/script/env_unset.txt @@ -12,13 +12,13 @@ stderr '^go(\.exe)?: unknown GOEXPERIMENT badexp$' go env -u GOEXPERIMENT ! go env -stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair bados/badarch$' ! go env -u GOOS -stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair \w+/badarch$' ! go env -u GOARCH -stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$' +stderr '^go: unsupported GOOS/GOARCH pair bados/\w+$' go env -u GOOS GOARCH diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt index b5e97391679cc5..132947c623ff2f 100644 --- a/src/cmd/go/testdata/script/env_write.txt +++ b/src/cmd/go/testdata/script/env_write.txt @@ -30,9 +30,9 @@ go env # checking errors ! go env -w -stderr 'go env -w: no KEY=VALUE arguments given' +stderr 'go: no KEY=VALUE arguments given' ! go env -u -stderr 'go env -u: no arguments given' +stderr 'go: ''go env -u'' requires an argument' # go env -w changes default setting env root= @@ -111,7 +111,7 @@ stderr 'GOPATH entry is relative; must be absolute path' # go env -w rejects invalid GOTMPDIR values ! go env -w GOTMPDIR=x -stderr 'go env -w: GOTMPDIR must be an absolute path' +stderr 'go: GOTMPDIR must be an absolute path' # go env -w should accept absolute GOTMPDIR value # and should not create it @@ -134,24 +134,24 @@ stdout ^$ go env -w CC=clang [!windows] ! go env -w CC=./clang [!windows] ! go env -w CC=bin/clang -[!windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[!windows] stderr 'go: CC entry is relative; must be absolute path' [windows] go env -w CC=$WORK\bin\clang [windows] ! go env -w CC=.\clang [windows] ! go env -w CC=bin\clang -[windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[windows] stderr 'go: CC entry is relative; must be absolute path' # go env -w rejects relative CXX values [!windows] go env -w CC=/usr/bin/cpp go env -w CXX=cpp [!windows] ! go env -w CXX=./cpp [!windows] ! go env -w CXX=bin/cpp -[!windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[!windows] stderr 'go: CXX entry is relative; must be absolute path' [windows] go env -w CXX=$WORK\bin\cpp [windows] ! go env -w CXX=.\cpp [windows] ! go env -w CXX=bin\cpp -[windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[windows] stderr 'go: CXX entry is relative; must be absolute path' # go env -w/-u checks validity of GOOS/ARCH combinations env GOOS= @@ -176,9 +176,9 @@ stderr 'unsupported GOOS/GOARCH.*windows/mips$' # go env -w should reject relative paths in GOMODCACHE environment. ! go env -w GOMODCACHE=~/test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "~/test"' ! go env -w GOMODCACHE=./test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "./test"' # go env -w checks validity of GOEXPERIMENT env GOEXPERIMENT= diff --git a/src/cmd/go/testdata/script/fmt_load_errors.txt b/src/cmd/go/testdata/script/fmt_load_errors.txt index 84bf41cfbafacd..e3a9034ede7426 100644 --- a/src/cmd/go/testdata/script/fmt_load_errors.txt +++ b/src/cmd/go/testdata/script/fmt_load_errors.txt @@ -7,16 +7,19 @@ stdout 'exclude[/\\]x\.go' stdout 'exclude[/\\]x_linux\.go' # Test edge cases with gofmt. -# Note that this execs GOROOT/bin/gofmt. -! exec gofmt does-not-exist +! exec $GOROOT/bin/gofmt does-not-exist -exec gofmt gofmt-dir/no-extension +exec $GOROOT/bin/gofmt gofmt-dir/no-extension stdout 'package x' -exec gofmt gofmt-dir +exec $GOROOT/bin/gofmt gofmt-dir ! stdout 'package x' +! exec $GOROOT/bin/gofmt empty.go nopackage.go +stderr -count=1 'empty\.go:1:1: expected .package., found .EOF.' +stderr -count=1 'nopackage\.go:1:1: expected .package., found not' + -- exclude/empty/x.txt -- -- exclude/ignore/_x.go -- package x @@ -30,3 +33,6 @@ package x package x -- gofmt-dir/no-extension -- package x +-- empty.go -- +-- nopackage.go -- +not the proper start to a Go file diff --git a/src/cmd/go/testdata/script/fsys_walk.txt b/src/cmd/go/testdata/script/fsys_walk.txt new file mode 100644 index 00000000000000..9d1a9451ff7a63 --- /dev/null +++ b/src/cmd/go/testdata/script/fsys_walk.txt @@ -0,0 +1,6 @@ +# Test that go list prefix... does not read directories not beginning with prefix. +env GODEBUG=gofsystrace=1 +go list m... +stderr mime +stderr mime[\\/]multipart +! stderr archive diff --git a/src/cmd/go/testdata/script/gccgo_link_c.txt b/src/cmd/go/testdata/script/gccgo_link_c.txt index 422adea93d07da..b9a4c70b7eecfc 100644 --- a/src/cmd/go/testdata/script/gccgo_link_c.txt +++ b/src/cmd/go/testdata/script/gccgo_link_c.txt @@ -2,15 +2,17 @@ # cmd/cgo: undefined reference when linking a C-library using gccgo [!cgo] skip -[!gccgo] skip +[!exec:gccgo] skip -go build -r -compiler gccgo cgoref +go build -n -compiler gccgo stderr 'gccgo.*\-L [^ ]*alibpath \-lalib' # make sure that Go-inline "#cgo LDFLAGS:" ("-L alibpath -lalib") passed to gccgo linking stage --- cgoref/cgoref.go -- +-- go.mod -- +module m +-- cgoref.go -- package main // #cgo LDFLAGS: -L alibpath -lalib // void f(void) {} import "C" -func main() { C.f() } \ No newline at end of file +func main() { C.f() } diff --git a/src/cmd/go/testdata/script/gccgo_m.txt b/src/cmd/go/testdata/script/gccgo_m.txt index b63ba46ced0f1e..beb9c50368e306 100644 --- a/src/cmd/go/testdata/script/gccgo_m.txt +++ b/src/cmd/go/testdata/script/gccgo_m.txt @@ -4,6 +4,7 @@ env GO111MODULE=off [short] skip +[cross] skip # gccgo can't necessarily cross-compile cd m go build diff --git a/src/cmd/go/testdata/script/gcflags_patterns.txt b/src/cmd/go/testdata/script/gcflags_patterns.txt index f23cecefd3aa30..0705277019c991 100644 --- a/src/cmd/go/testdata/script/gcflags_patterns.txt +++ b/src/cmd/go/testdata/script/gcflags_patterns.txt @@ -7,33 +7,37 @@ env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache # -gcflags=-e applies to named packages, not dependencies go build -n -v -gcflags=-e z1 z2 -stderr 'compile.* -e.* -p z1' -stderr 'compile.* -e.* -p z2' +stderr 'compile.* -p z1.* -e ' +stderr 'compile.* -p z2.* -e ' stderr 'compile.* -p y' -! stderr 'compile.* -e.* -p [^z]' +! stderr 'compile.* -p [^z].* -e ' # -gcflags can specify package=flags, and can be repeated; last match wins go build -n -v -gcflags=-e -gcflags=z1=-N z1 z2 -stderr 'compile.* -N.* -p z1' -! stderr 'compile.* -e.* -p z1' -! stderr 'compile.* -N.* -p z2' -stderr 'compile.* -e.* -p z2' +stderr 'compile.* -p z1.* -N ' +! stderr 'compile.* -p z1.* -e ' +! stderr 'compile.* -p z2.* -N ' +stderr 'compile.* -p z2.* -e ' stderr 'compile.* -p y' -! stderr 'compile.* -e.* -p [^z]' -! stderr 'compile.* -N.* -p [^z]' +! stderr 'compile.* -p [^z].* -e ' +! stderr 'compile.* -p [^z].* -N ' # -gcflags can have arbitrary spaces around the flags go build -n -v -gcflags=' z1 = -e ' z1 -stderr 'compile.* -e.* -p z1' +stderr 'compile.* -p z1.* -e ' # -gcflags='all=-e' should apply to all packages, even with go test go test -c -n -gcflags='all=-e' z1 -stderr 'compile.* -e.* -p z3 ' +stderr 'compile.* -p z3.* -e ' # this particular -gcflags argument made the compiler crash ! go build -gcflags=-d=ssa/ z1 stderr 'PhaseOptions usage' +# check for valid -ldflags parameter +! go build '-ldflags="-X main.X=Hello"' +stderr 'invalid value' + # -ldflags for implicit test package applies to test binary go test -c -n -gcflags=-N -ldflags=-X=x.y=z z1 stderr 'compile.* -N .*z_test.go' @@ -58,8 +62,7 @@ go build -n -ldflags=-X=math.pi=3 stderr 'link.* -X=math.pi=3' # -ldflags applies to current directory even if GOPATH is funny -[windows] cd $WORK/GoPath/src/my/cmd/prog -[darwin] cd $WORK/GoPath/src/my/cmd/prog +[!case-sensitive] cd $WORK/GoPath/src/my/cmd/prog go build -n -ldflags=-X=math.pi=3 stderr 'link.* -X=math.pi=3' diff --git a/src/cmd/go/testdata/script/generate.txt b/src/cmd/go/testdata/script/generate.txt index 73f5bbd57a91d3..58777c5865a3f1 100644 --- a/src/cmd/go/testdata/script/generate.txt +++ b/src/cmd/go/testdata/script/generate.txt @@ -17,11 +17,19 @@ stdout 'Now is the time for all good men' go generate './generate/substitution.go' stdout $GOARCH' substitution.go:7 pabc xyzp/substitution.go/123' -# Test go generate's run flag +# Test go generate's run and skip flags go generate -run y.s './generate/flag.go' stdout 'yes' # flag.go should select yes ! stdout 'no' # flag.go should not select no +go generate -skip th..sand './generate/flag.go' +stdout 'yes' # flag.go should select yes +! stdout 'no' # flag.go should not select no + +go generate -run . -skip th..sand './generate/flag.go' +stdout 'yes' # flag.go should select yes +! stdout 'no' # flag.go should not select no + # Test go generate provides the right "$GOPACKAGE" name in an x_test go generate './generate/env_test.go' stdout 'main_test' diff --git a/src/cmd/go/testdata/script/generate_goroot_PATH.txt b/src/cmd/go/testdata/script/generate_goroot_PATH.txt new file mode 100644 index 00000000000000..647cea3bf95302 --- /dev/null +++ b/src/cmd/go/testdata/script/generate_goroot_PATH.txt @@ -0,0 +1,38 @@ +# https://go.dev/issue/51473: to avoid the need for generators to rely on +# runtime.GOROOT, 'go generate' should run the test with its own GOROOT/bin +# at the beginning of $PATH. + +[short] skip + +[!plan9] env PATH= +[plan9] env path= +go generate . + +[!plan9] env PATH=$WORK${/}bin +[plan9] env path=$WORK${/}bin +go generate . + +-- go.mod -- +module example + +go 1.19 +-- main.go -- +//go:generate go run . + +package main + +import ( + "fmt" + "os" + "os/exec" +) + +func main() { + _, err := exec.LookPath("go") + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} +-- $WORK/bin/README.txt -- +This directory contains no executables. diff --git a/src/cmd/go/testdata/script/get_404_meta.txt b/src/cmd/go/testdata/script/get_404_meta.txt index ec4f8d32432179..29fc5421e12fb9 100644 --- a/src/cmd/go/testdata/script/get_404_meta.txt +++ b/src/cmd/go/testdata/script/get_404_meta.txt @@ -9,4 +9,10 @@ go get -d bazil.org/fuse/fs/fstestutil env GO111MODULE=on env GOPROXY=direct -go get -d bazil.org/fuse/fs/fstestutil +go get bazil.org/fuse/fs/fstestutil + + +-- go.mod -- +module m + +go 1.18 diff --git a/src/cmd/go/testdata/script/get_go_file.txt b/src/cmd/go/testdata/script/get_go_file.txt index bed87209876924..f6d0de4d06fe54 100644 --- a/src/cmd/go/testdata/script/get_go_file.txt +++ b/src/cmd/go/testdata/script/get_go_file.txt @@ -9,15 +9,15 @@ go get -d test # argument has .go suffix, is a file and exists ! go get -d test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get -d test_missing.go -stderr 'go get test_missing.go: arguments must be package or module paths' +stderr 'go: test_missing.go: arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get -d test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get -d test/test_missing.go @@ -27,19 +27,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get -d test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get -d test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get -d test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory diff --git a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt index 2517664dd02308..00bf32fc78effb 100644 --- a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt +++ b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt @@ -3,11 +3,11 @@ env GO111MODULE=off # GOPATH: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' # Modules: Set up env GO111MODULE=on # Modules: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' diff --git a/src/cmd/go/testdata/script/get_issue16471.txt b/src/cmd/go/testdata/script/get_issue16471.txt new file mode 100644 index 00000000000000..2a2225a444da97 --- /dev/null +++ b/src/cmd/go/testdata/script/get_issue16471.txt @@ -0,0 +1,22 @@ +[!net] skip +[!exec:git] skip + +env GO111MODULE=off + +cd rsc.io/go-get-issue-10952 + +exec git init +exec git add foo.go +exec git config user.name Gopher +exec git config user.email gopher@golang.org +exec git commit -a -m 'initial commit' +exec git remote add origin https://github.com/golang/go-get-issue-10952 + +exec git status + +! go get -x -u rsc.io/go-get-issue-10952 +stderr '^package rsc.io/go-get-issue-10952: rsc\.io/go-get-issue-10952 is a custom import path for https://github.com/rsc/go-get-issue-10952, but .* is checked out from https://github.com/golang/go-get-issue-10952$' + +-- rsc.io/go-get-issue-10952/foo.go -- +// Junk package to test go get. +package foo diff --git a/src/cmd/go/testdata/script/get_issue22125.txt b/src/cmd/go/testdata/script/get_issue22125.txt new file mode 100644 index 00000000000000..6fdbe81cd30458 --- /dev/null +++ b/src/cmd/go/testdata/script/get_issue22125.txt @@ -0,0 +1,14 @@ +# This test verifies a fix for a security issue; see https://go.dev/issue/22125. + +[!net] skip +[!exec:git] skip +[!exec:svn] skip + +env GO111MODULE=off + +cd $GOPATH +! go get -u vcs-test.golang.org/go/test1-svn-git +stderr 'src'${/}'vcs-test.* uses git, but parent .*src'${/}'vcs-test.* uses svn' + +[!case-sensitive] ! go get -u vcs-test.golang.org/go/test2-svn-git/test2main +[!case-sensitive] stderr 'src'${/}'vcs-test.* uses git, but parent .*src'${/}'vcs-test.* uses svn' diff --git a/src/cmd/go/testdata/script/go_version.txt b/src/cmd/go/testdata/script/go_version.txt new file mode 100644 index 00000000000000..1a787e1b18c1dd --- /dev/null +++ b/src/cmd/go/testdata/script/go_version.txt @@ -0,0 +1,9 @@ +# test that go version doesn't panic on non-go binaries +# See Issue #49181 + +[exec:/bin/true] cp /bin/true true +[exec:C:\windows\system32\help.exe] cp C:\windows\system32\help.exe help.exe + +go version -m . +! stdout . +! stderr . diff --git a/src/cmd/go/testdata/script/goflags.txt b/src/cmd/go/testdata/script/goflags.txt index 686d1138b8318e..f4872ffd356b5b 100644 --- a/src/cmd/go/testdata/script/goflags.txt +++ b/src/cmd/go/testdata/script/goflags.txt @@ -9,7 +9,7 @@ stdout '[\\/]runtime$' env GOFLAGS=-race OLDGOARCH=$GOARCH OLDGOOS=$GOOS GOARCH=386 GOOS=linux ! go list runtime -stderr 'race is only supported on' +stderr 'race is not supported on linux/386' env GOARCH=$OLDGOARCH GOOS=$OLDGOOS diff --git a/src/cmd/go/testdata/script/gopath_install.txt b/src/cmd/go/testdata/script/gopath_install.txt index 4b42fc593f9ddf..6c572eae619f06 100644 --- a/src/cmd/go/testdata/script/gopath_install.txt +++ b/src/cmd/go/testdata/script/gopath_install.txt @@ -26,7 +26,7 @@ cd .. env GOPATH= # reset to default ($HOME/go, which does not exist) env GOBIN= ! go install go-cmd-test/helloworld.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' # With $GOBIN set, should install there. env GOBIN=$WORK/bin1 diff --git a/src/cmd/go/testdata/script/gopath_local.txt b/src/cmd/go/testdata/script/gopath_local.txt index 7ee1f83471c631..54beaca5e8a9c2 100644 --- a/src/cmd/go/testdata/script/gopath_local.txt +++ b/src/cmd/go/testdata/script/gopath_local.txt @@ -22,7 +22,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/local/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' [windows] stop # Windows does not allow the ridiculous directory name we're about to use. @@ -58,7 +58,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/$BAD_DIR_NAME/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' -- testdata/local/easy.go -- package main diff --git a/src/cmd/go/testdata/script/govcs.txt b/src/cmd/go/testdata/script/govcs.txt index 4180d7da6ab159..46f1bd0da2ada6 100644 --- a/src/cmd/go/testdata/script/govcs.txt +++ b/src/cmd/go/testdata/script/govcs.txt @@ -5,64 +5,64 @@ env GOPROXY=direct # GOVCS stops go get env GOVCS='*:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOPRIVATE='github.com/google' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # public pattern works env GOPRIVATE='github.com/google' env GOVCS='public:all,private:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # private pattern works env GOPRIVATE='hubgit.com/google' env GOVCS='private:all,public:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # other patterns work (for more patterns, see TestGOVCS) env GOPRIVATE= env GOVCS='github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOVCS='github.com/google/go-cmp/inner:git,github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # bad patterns are reported (for more bad patterns, see TestGOVCSErrors) env GOVCS='git' ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' +stderr '^go: github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' env GOVCS=github.com:hg,github.com:git ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' +stderr '^go: github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' # bad GOVCS patterns do not stop commands that do not need to check VCS go list env GOPROXY=$proxy -go get -d rsc.io/quote # ok because used proxy +go get rsc.io/quote # ok because used proxy env GOPROXY=direct # svn is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.svn/hello -stderr '^go get rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' # fossil is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.fossil/hello -stderr '^go get rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' # bzr is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.bzr/hello -stderr '^go get rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' # git is OK by default env GOVCS= @@ -77,12 +77,12 @@ env GONOSUMDB='*' # git can be disallowed env GOVCS=public:hg ! go get rsc.io/nonexist.git/hello -stderr '^go get rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' # hg can be disallowed env GOVCS=public:git ! go get rsc.io/nonexist.hg/hello -stderr '^go get rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' # Repeat in GOPATH mode. Error texts slightly different. diff --git a/src/cmd/go/testdata/script/import_unix_tag.txt b/src/cmd/go/testdata/script/import_unix_tag.txt new file mode 100644 index 00000000000000..b88ca1e2ee4a74 --- /dev/null +++ b/src/cmd/go/testdata/script/import_unix_tag.txt @@ -0,0 +1,32 @@ +# Regression test for https://go.dev/issue/54712: the "unix" build constraint +# was not applied consistently during package loading. + +go list -x -f '{{if .Module}}{{.ImportPath}}{{end}}' -deps . +stdout 'example.com/version' + +-- go.mod -- +module example + +go 1.19 + +require example.com/version v1.1.0 +-- go.sum -- +example.com/version v1.1.0 h1:VdPnGmIF1NJrntStkxGrF3L/OfhaL567VzCjncGUgtM= +example.com/version v1.1.0/go.mod h1:S7K9BnT4o5wT4PCczXPfWVzpjD4ud4e7AJMQJEgiu2Q= +-- main_notunix.go -- +//go:build !(aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris) + +package main + +import _ "example.com/version" + +func main() {} + +-- main_unix.go -- +//go:build unix + +package main + +import _ "example.com/version" + +func main() {} diff --git a/src/cmd/go/testdata/script/index.txt b/src/cmd/go/testdata/script/index.txt new file mode 100644 index 00000000000000..6a2d13c8b5e686 --- /dev/null +++ b/src/cmd/go/testdata/script/index.txt @@ -0,0 +1,6 @@ +# Check that standard library packages are cached. +go list -json math # refresh cache +env GODEBUG=gofsystrace=1,gofsystracelog=fsys.log +go list -json math +! grep math/abs.go fsys.log +grep 'openIndexPackage .*[\\/]math$' fsys.log diff --git a/src/cmd/go/testdata/script/install_cmd_gobin.txt b/src/cmd/go/testdata/script/install_cmd_gobin.txt index 38fd66c0e8f8d1..049bf415b8d618 100644 --- a/src/cmd/go/testdata/script/install_cmd_gobin.txt +++ b/src/cmd/go/testdata/script/install_cmd_gobin.txt @@ -3,8 +3,8 @@ env GOBIN=gobin mkdir gobin go list -f '{{.Target}}' cmd/go -stdout $GOROOT[/\\]bin[/\\]go$GOEXE +stdout $GOROOT${/}bin${/}go$GOEXE # Check that tools are installed to $GOTOOLDIR, not $GOBIN. go list -f '{{.Target}}' cmd/compile -stdout $GOROOT[/\\]pkg[/\\]tool[/\\]${GOOS}_${GOARCH}[/\\]compile$GOEXE +stdout $GOROOT${/}pkg${/}tool${/}${GOOS}_${GOARCH}${/}compile$GOEXE diff --git a/src/cmd/go/testdata/script/install_msan_and_race_and_asan_require_cgo.txt b/src/cmd/go/testdata/script/install_msan_and_race_and_asan_require_cgo.txt new file mode 100644 index 00000000000000..d496eaa9cd9890 --- /dev/null +++ b/src/cmd/go/testdata/script/install_msan_and_race_and_asan_require_cgo.txt @@ -0,0 +1,20 @@ +# Tests Issue #21895 + +env CGO_ENABLED=0 + +[race] ! go install -race triv.go +[race] stderr '-race requires cgo' +[race] ! stderr '-msan' + +[msan] ! go install -msan triv.go +[msan] stderr '-msan requires cgo' +[msan] ! stderr '-race' + +[asan] ! go install -asan triv.go +[asan] stderr '(-asan: the version of $(go env CC) could not be parsed)|(-asan: C compiler is not gcc or clang)|(-asan is not supported with C compiler (\d+)\.(\d+))|(-asan requires cgo)' +[asan] ! stderr '-msan' + +-- triv.go -- +package main + +func main() {} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt b/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt deleted file mode 100644 index 7985cd2ab2728d..00000000000000 --- a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Tests Issue #21895 - -[!msan] [!race] skip 'skipping because both msan and the race detector are not supported' - -env CGO_ENABLED=0 - -[race] ! go install -race triv.go -[race] stderr '-race requires cgo' -[race] ! stderr '-msan' - -[msan] ! go install -msan triv.go -[msan] stderr '-msan requires cgo' -[msan] ! stderr '-race' - --- triv.go -- -package main - -func main() {} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/issue53586.txt b/src/cmd/go/testdata/script/issue53586.txt new file mode 100644 index 00000000000000..db405cd9e49375 --- /dev/null +++ b/src/cmd/go/testdata/script/issue53586.txt @@ -0,0 +1,18 @@ +[short] skip # sleeps to make mtime cacheable + +go mod init example + +cd subdir +go mod init example/subdir +sleep 2s # allow go.mod mtime to be cached + +go list -f '{{.Dir}}: {{.ImportPath}}' ./pkg +stdout $PWD${/}pkg': example/subdir/pkg$' + +rm go.mod # expose ../go.mod + +go list -f '{{.Dir}}: {{.ImportPath}}' ./pkg +stdout $PWD${/}pkg': example/subdir/pkg$' + +-- subdir/pkg/pkg.go -- +package pkg diff --git a/src/cmd/go/testdata/script/link_external_undef.txt b/src/cmd/go/testdata/script/link_external_undef.txt new file mode 100644 index 00000000000000..d86b3a374e919d --- /dev/null +++ b/src/cmd/go/testdata/script/link_external_undef.txt @@ -0,0 +1,48 @@ + +# Test case for issue 47993, in which the linker crashes +# on a bad input instead of issuing an error and exiting. + +# This test requires external linking, so use cgo as a proxy +[!cgo] skip + +! go build -ldflags='-linkmode=external' . +! stderr 'panic' +stderr '^.*unreachable sym in relocation.*' + +-- go.mod -- + +module issue47993 + +go 1.16 + +-- main.go -- + +package main + +type M struct { + b bool +} + +// Note the body-less func def here. This is what causes the problems. +func (m *M) run(fp func()) + +func doit(m *M) { + InAsm() + m.run(func() { + }) +} + +func main() { + m := &M{true} + doit(m) +} + +func InAsm() + +-- main.s -- + +// Add an assembly function so as to leave open the possibility +// that body-less functions in Go might be defined in assembly. + +// Currently we just need an empty file here. + diff --git a/src/cmd/go/testdata/script/link_syso_deps.txt b/src/cmd/go/testdata/script/link_syso_deps.txt new file mode 100644 index 00000000000000..c713304d9fdde1 --- /dev/null +++ b/src/cmd/go/testdata/script/link_syso_deps.txt @@ -0,0 +1,55 @@ +# Test that syso in deps is available to cgo. + +[!gc] skip 'requires syso support' +[!cgo] skip +[short] skip 'invokes system C compiler' + +# External linking is not supported on linux/ppc64. +# See: https://github.com/golang/go/issues/8912 +[linux] [ppc64] skip + +cc -c -o syso/x.syso syso/x.c +cc -c -o syso2/x.syso syso2/x.c +go build m/cgo + +-- go.mod -- +module m + +go 1.18 +-- cgo/x.go -- +package cgo + +// extern void f(void); +// extern void g(void); +import "C" + +func F() { + C.f() +} + +func G() { + C.g() +} + +-- cgo/x2.go -- +package cgo + +import _ "m/syso" + +-- syso/x.c -- +//go:build ignore + +void f() {} + +-- syso/x.go -- +package syso + +import _ "m/syso2" + +-- syso2/x.c -- +//go:build ignore + +void g() {} + +-- syso2/x.go -- +package syso2 diff --git a/src/cmd/go/testdata/script/link_syso_issue33139.txt b/src/cmd/go/testdata/script/link_syso_issue33139.txt index 8a8cb4aa8c20f0..36746e64993cb8 100644 --- a/src/cmd/go/testdata/script/link_syso_issue33139.txt +++ b/src/cmd/go/testdata/script/link_syso_issue33139.txt @@ -1,8 +1,10 @@ # Test that we can use the external linker with a host syso file that is # embedded in a package, that is referenced by a Go assembly function. # See issue 33139. + [!gc] skip [!cgo] skip +[short] skip 'invokes system C compiler' # External linking is not supported on linux/ppc64. # See: https://github.com/golang/go/issues/8912 diff --git a/src/cmd/go/testdata/script/list_all_gobuild.txt b/src/cmd/go/testdata/script/list_all_gobuild.txt new file mode 100644 index 00000000000000..e0a47398bbd92f --- /dev/null +++ b/src/cmd/go/testdata/script/list_all_gobuild.txt @@ -0,0 +1,41 @@ +# go list all should work with GOOS=linux because all packages build on Linux +env GOOS=linux +go list all + +# go list all should work with GOOS=darwin, but it used to fail because +# in the absence of //go:build support, p looked like it needed q +# (p_test.go was not properly excluded), and q was Linux-only. +# +# Also testing with r and s that +build lines keep working. +env GOOS=darwin +go list all + +-- go.mod -- +go 1.17 +module m + +-- p/p.go -- +package p + +-- p/p_test.go -- +//go:build linux + +package p + +import "m/q" + +-- q/q_linux.go -- +package q + +-- r/r.go -- +package r + +-- r/r_test.go -- +// +build linux + +package r + +import "m/s" + +-- s/s_linux.go -- +package s diff --git a/src/cmd/go/testdata/script/list_cgo_compiled_importmap.txt b/src/cmd/go/testdata/script/list_cgo_compiled_importmap.txt index 3d68ef3055972e..30effb104b49e9 100644 --- a/src/cmd/go/testdata/script/list_cgo_compiled_importmap.txt +++ b/src/cmd/go/testdata/script/list_cgo_compiled_importmap.txt @@ -12,7 +12,7 @@ env CGO_ENABLED=1 env GOFLAGS=-tags=netcgo # Force net to use cgo even on Windows. -# "runtime/cgo [runtime.test]" appears in the the test dependencies of "runtime", +# "runtime/cgo [runtime.test]" appears in the test dependencies of "runtime", # because "runtime/cgo" itself depends on "runtime" go list -deps -test -compiled -f '{{if eq .ImportPath "net [runtime.test]"}}{{printf "%q" .Imports}}{{end}}' runtime diff --git a/src/cmd/go/testdata/script/list_json_fields.txt b/src/cmd/go/testdata/script/list_json_fields.txt new file mode 100644 index 00000000000000..5ddbb7385e0d97 --- /dev/null +++ b/src/cmd/go/testdata/script/list_json_fields.txt @@ -0,0 +1,75 @@ +# Test using -json flag to specify specific fields. + +# Test -json produces "full" output by looking for multiple fields present. +go list -json . +stdout '"Name": "a"' +stdout '"Stale": true' +# Same thing for -json=true +go list -json=true . +stdout '"Name": "a"' +stdout '"Stale": true' + +# Test -json=false produces non-json output. +go list -json=false +cmp stdout want-non-json.txt + +# Test -json= keeps only that field. +go list -json=Name +cmp stdout want-json-name.txt + +# Test -json= with multiple fields. +go list -json=ImportPath,Name,GoFiles,Imports +cmp stdout want-json-multiple.txt + +# Test -json= with Deps outputs the Deps field. +go list -json=Deps +stdout '"Deps": \[' +stdout '"errors",' + +[!exec:git] skip + +# Test -json= without Stale skips computing buildinfo +cd repo +exec git init +# Control case: with -json=Stale cmd/go executes git status to compute buildinfo +go list -json=Stale -x +stderr 'git status' +# Test case: without -json=Stale cmd/go skips git status +go list -json=Name -x +! stderr 'git status' + +-- go.mod -- +module example.com/a + +go 1.18 +-- a.go -- +package a + +import "fmt" + +func F() { + fmt.Println("hey there") +} +-- want-non-json.txt -- +example.com/a +-- want-json-name.txt -- +{ + "Name": "a" +} +-- want-json-multiple.txt -- +{ + "ImportPath": "example.com/a", + "Name": "a", + "GoFiles": [ + "a.go" + ], + "Imports": [ + "fmt" + ] +} +-- repo/go.mod -- +module example.com/repo +-- repo/main.go -- +package main + +func main() {} diff --git a/src/cmd/go/testdata/script/list_legacy_mod.txt b/src/cmd/go/testdata/script/list_legacy_mod.txt new file mode 100644 index 00000000000000..ab901d7c341cee --- /dev/null +++ b/src/cmd/go/testdata/script/list_legacy_mod.txt @@ -0,0 +1,48 @@ +# In GOPATH mode, module legacy support does path rewriting very similar to vendoring. + +env GO111MODULE=off + +go list -f '{{range .Imports}}{{.}}{{"\n"}}{{end}}' old/p1 +stdout ^new/p1$ + +go list -f '{{range .Imports}}{{.}}{{"\n"}}{{end}}' new/p1 +stdout ^new/p2$ # not new/v2/p2 +! stdout ^new/v2 +stdout ^new/sub/x/v1/y$ # not new/sub/v2/x/v1/y +! stdout ^new/sub/v2 +stdout ^new/sub/inner/x # not new/sub/v2/inner/x + +go build old/p1 new/p1 + +-- new/go.mod -- +module "new/v2" +-- new/new.go -- +package new + +import _ "new/v2/p2" +-- new/p1/p1.go -- +package p1 + +import _ "old/p2" +import _ "new/v2" +import _ "new/v2/p2" +import _ "new/sub/v2/x/v1/y" // v2 is module, v1 is directory in module +import _ "new/sub/inner/x" // new/sub/inner/go.mod overrides new/sub/go.mod +-- new/p2/p2.go -- +package p2 +-- new/sub/go.mod -- +module new/sub/v2 +-- new/sub/inner/go.mod -- +module new/sub/inner +-- new/sub/inner/x/x.go -- +package x +-- new/sub/x/v1/y/y.go -- +package y +-- old/p1/p1.go -- +package p1 + +import _ "old/p2" +import _ "new/p1" +import _ "new" +-- old/p2/p2.go -- +package p2 diff --git a/src/cmd/go/testdata/script/list_load_err.txt b/src/cmd/go/testdata/script/list_load_err.txt index 0cfa7fbed2fe43..f1b9205f9904b7 100644 --- a/src/cmd/go/testdata/script/list_load_err.txt +++ b/src/cmd/go/testdata/script/list_load_err.txt @@ -1,6 +1,6 @@ # go list -e -deps should list imports from any file it can read, even if # other files in the same package cause go/build.Import to return an error. -# Verfifies golang.org/issue/38568 +# Verifies golang.org/issue/38568 go list -e -deps ./scan stdout m/want diff --git a/src/cmd/go/testdata/script/list_perm.txt b/src/cmd/go/testdata/script/list_perm.txt new file mode 100644 index 00000000000000..3b850ef3cc8074 --- /dev/null +++ b/src/cmd/go/testdata/script/list_perm.txt @@ -0,0 +1,83 @@ +env GO111MODULE=on + +# Establish baseline behavior, before mucking with file permissions. + +go list ./noread/... +stdout '^example.com/noread$' + +go list example.com/noread/... +stdout '^example.com/noread$' + +go list ./empty/... +stderr 'matched no packages' + +# Make the directory ./noread unreadable, and verify that 'go list' reports an +# explicit error for a pattern that should match it (rather than treating it as +# equivalent to an empty directory). + +[root] stop # Root typically ignores file permissions. +[windows] skip # Does not have Unix-style directory permissions. +[plan9] skip # Might not have Unix-style directory permissions. + +chmod 000 noread + +# Check explicit paths. + +! go list ./noread +! stdout '^example.com/noread$' +! stderr 'matched no packages' + +! go list example.com/noread +! stdout '^example.com/noread$' +! stderr 'matched no packages' + +# Check filesystem-relative patterns. + +! go list ./... +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern ./...: ' + +! go list ./noread/... +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern ./noread/...: ' + + +# Check module-prefix patterns. + +! go list example.com/... +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern example.com/...: ' + +! go list example.com/noread/... +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern example.com/noread/...: ' + + +[short] stop + +# Check global patterns, which should still +# fail due to errors in the local module. + +! go list all +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern all: ' + +! go list ... +! stdout '^example.com/noread$' +! stderr 'matched no packages' +stderr '^pattern ...: ' + + +-- go.mod -- +module example.com +go 1.15 +-- noread/noread.go -- +// Package noread exists, but will be made unreadable. +package noread +-- empty/README.txt -- +This directory intentionally left empty. diff --git a/src/cmd/go/testdata/script/list_permissions.txt b/src/cmd/go/testdata/script/list_permissions.txt deleted file mode 100644 index f65896ca14ee62..00000000000000 --- a/src/cmd/go/testdata/script/list_permissions.txt +++ /dev/null @@ -1,84 +0,0 @@ -env GO111MODULE=on - -# Establish baseline behavior, before mucking with file permissions. - -go list ./noread/... -stdout '^example.com/noread$' - -go list example.com/noread/... -stdout '^example.com/noread$' - -go list ./empty/... -stderr 'matched no packages' - -[root] stop # Root typically ignores file permissions. - -# Make the directory ./noread unreadable, and verify that 'go list' reports an -# explicit error for a pattern that should match it (rather than treating it as -# equivalent to an empty directory). - -[windows] skip # Does not have Unix-style directory permissions. -[plan9] skip # Might not have Unix-style directory permissions. - -chmod 000 noread - -# Check explicit paths. - -! go list ./noread -! stdout '^example.com/noread$' -! stderr 'matched no packages' - -! go list example.com/noread -! stdout '^example.com/noread$' -! stderr 'matched no packages' - -# Check filesystem-relative patterns. - -! go list ./... -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern ./...: ' - -! go list ./noread/... -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern ./noread/...: ' - - -# Check module-prefix patterns. - -! go list example.com/... -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern example.com/...: ' - -! go list example.com/noread/... -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern example.com/noread/...: ' - - -[short] stop - -# Check global patterns, which should still -# fail due to errors in the local module. - -! go list all -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern all: ' - -! go list ... -! stdout '^example.com/noread$' -! stderr 'matched no packages' -stderr '^pattern ...: ' - - --- go.mod -- -module example.com -go 1.15 --- noread/noread.go -- -// Package noread exists, but will be made unreadable. -package noread --- empty/README.txt -- -This directory intentionally left empty. diff --git a/src/cmd/go/testdata/script/list_replace_absolute_windows.txt b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt new file mode 100644 index 00000000000000..b3ff2a7c2d65cf --- /dev/null +++ b/src/cmd/go/testdata/script/list_replace_absolute_windows.txt @@ -0,0 +1,38 @@ +# Test a replacement with an absolute path (so the path isn't +# cleaned by having filepath.Abs called on it). This checks +# whether the modindex logic cleans the modroot path before using +# it. + +[!windows] skip +[short] skip + +go run print_go_mod.go # use this program to write a go.mod with an absolute path +cp stdout go.mod + +go list -modfile=go.mod all +-- print_go_mod.go -- +//go:build ignore +package main + +import ( + "fmt" + "os" +) + +func main() { + work := os.Getenv("WORK") +fmt.Printf(`module example.com/mod + +require b.com v0.0.0 + +replace b.com => %s\gopath\src/modb +`, work) +} +-- a.go -- +package a + +import _ "b.com/b" +-- modb/go.mod -- +module b.com +-- modb/b/b.go -- +package b diff --git a/src/cmd/go/testdata/script/list_reserved.txt b/src/cmd/go/testdata/script/list_reserved.txt new file mode 100644 index 00000000000000..b9c5361492be9c --- /dev/null +++ b/src/cmd/go/testdata/script/list_reserved.txt @@ -0,0 +1,7 @@ +# https://golang.org/issue/37641: the paths "example" and "test" are reserved +# for end users, and must never exist in the standard library. + +go list example/... test/... +stderr 'go: warning: "example/..." matched no packages$' +stderr 'go: warning: "test/..." matched no packages$' +! stdout . diff --git a/src/cmd/go/testdata/script/list_shadow.txt b/src/cmd/go/testdata/script/list_shadow.txt index 7b24d9367aede1..660508de9ff83c 100644 --- a/src/cmd/go/testdata/script/list_shadow.txt +++ b/src/cmd/go/testdata/script/list_shadow.txt @@ -15,7 +15,7 @@ stdout '^\(.*gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo\) \('$WORK # The error for go install should mention the conflicting directory. ! go install -n ./shadow/root2/src/foo -stderr 'go install: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' +stderr 'go: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' -- shadow/root1/src/foo/foo.go -- package foo diff --git a/src/cmd/go/testdata/script/list_swigcxx.txt b/src/cmd/go/testdata/script/list_swigcxx.txt index c6acd9ecdbabf4..4220487a286424 100644 --- a/src/cmd/go/testdata/script/list_swigcxx.txt +++ b/src/cmd/go/testdata/script/list_swigcxx.txt @@ -2,19 +2,22 @@ [!exec:swig] skip [!exec:g++] skip +[!cgo] skip # CompiledGoFiles should contain 4 files: # a.go -# a.swigcxx.go +# _cgo_import.go [gc only] # _cgo_gotypes.go # a.cgo1.go +# +# These names we see here, other than a.go, will be from the build cache, +# so we just count them. go list -f '{{.CompiledGoFiles}}' -compiled=true example/swig -# These names we see here, other than a.go, will be from the build cache, -# so we just count them. stdout a\.go -stdout -count=3 $GOCACHE +[gc] stdout -count=3 $GOCACHE +[gccgo] stdout -count=2 $GOCACHE -- go.mod -- module example diff --git a/src/cmd/go/testdata/script/list_test_err.txt b/src/cmd/go/testdata/script/list_test_err.txt index c6f1ecf4003909..25dbb969b01c06 100644 --- a/src/cmd/go/testdata/script/list_test_err.txt +++ b/src/cmd/go/testdata/script/list_test_err.txt @@ -44,6 +44,10 @@ stdout 'testdep_b ' stdout 'nameerr\.test "[^"]*wrong signature for TestBad' ! stderr 'wrong signature for TestBad' +# go list prints a useful error for generic test functions +! go list -test -deps genericerr +stderr 'wrong signature for TestGeneric, test functions cannot have type parameters' + # go list prints partial information with error if test has cyclic import ! go list -test -deps cycleerr stdout cycleerr @@ -106,6 +110,16 @@ import ( func TestBad(t *testing.B) {} +-- genericerr/genericerr.go -- +package genericerr + +-- genericerr/genericerr_test.go -- +package genericerr + +import "testing" + +func TestGeneric[T any](t *testing.T) {} + -- cycleerr/cycleerr_test.go -- package cycleerr diff --git a/src/cmd/go/testdata/script/mod_all.txt b/src/cmd/go/testdata/script/mod_all.txt index 090eeee22df263..b71a920870a9bb 100644 --- a/src/cmd/go/testdata/script/mod_all.txt +++ b/src/cmd/go/testdata/script/mod_all.txt @@ -202,9 +202,9 @@ go mod edit -go=1.17 u/go.mod go mod edit -go=1.17 w/go.mod go mod edit -go=1.17 x/go.mod go mod edit -go=1.17 -cp go.mod go.mod.orig +cmp go.mod go.mod.beforetidy go mod tidy -cmp go.mod go.mod.orig +cmp go.mod go.mod.aftertidy # With lazy loading, 'go list all' with neither -mod=vendor nor -test should # match -mod=vendor without -test in 1.15. @@ -315,7 +315,7 @@ go 1.15 require ( example.com/a v0.1.0 - example.com/b v0.1.0 + example.com/b v0.1.0 // indirect example.com/q v0.1.0 example.com/r v0.1.0 // indirect example.com/t v0.1.0 @@ -466,3 +466,66 @@ module example.com/x go 1.15 -- x/x.go -- package x +-- go.mod.beforetidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/b v0.1.0 // indirect + example.com/q v0.1.0 + example.com/r v0.1.0 // indirect + example.com/t v0.1.0 + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) +-- go.mod.aftertidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/q v0.1.0 + example.com/t v0.1.0 +) + +require ( + example.com/b v0.1.0 // indirect + example.com/r v0.1.0 // indirect + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) diff --git a/src/cmd/go/testdata/script/mod_bad_domain.txt b/src/cmd/go/testdata/script/mod_bad_domain.txt index 7a270d0f07cac7..afd6e5186c152a 100644 --- a/src/cmd/go/testdata/script/mod_bad_domain.txt +++ b/src/cmd/go/testdata/script/mod_bad_domain.txt @@ -2,7 +2,7 @@ env GO111MODULE=on # explicit get should report errors about bad names ! go get appengine -stderr '^go get: malformed module path "appengine": missing dot in first path element$' +stderr '^go: malformed module path "appengine": missing dot in first path element$' ! go get x/y.z stderr 'malformed module path "x/y.z": missing dot in first path element' @@ -24,10 +24,10 @@ stderr '^usenonexistent[/\\]x.go:2:8: no required module provides package nonexi # 'get -d' should be similarly definitive -go get -d ./useappengine # TODO(#41315): This should fail. +go get ./useappengine # TODO(#41315): This should fail. # stderr '^useappengine[/\\]x.go:2:8: cannot find package$' -! go get -d ./usenonexistent +! go get ./usenonexistent stderr '^x/usenonexistent imports\n\tnonexistent.rsc.io: cannot find module providing package nonexistent.rsc.io$' diff --git a/src/cmd/go/testdata/script/mod_build_info_err.txt b/src/cmd/go/testdata/script/mod_build_info_err.txt index cee055eabe9c26..5c3c309b0dc86e 100644 --- a/src/cmd/go/testdata/script/mod_build_info_err.txt +++ b/src/cmd/go/testdata/script/mod_build_info_err.txt @@ -11,7 +11,7 @@ stderr '^bad[/\\]bad.go:3:8: malformed import path "🐧.example.com/string": in # TODO(#41688): This should include a file and line, and report the reason for the error.. # (Today it includes only an import stack.) -! go get -d ./main +! go get ./main stderr '^m/main imports\n\tm/bad imports\n\t🐧.example.com/string: malformed import path "🐧.example.com/string": invalid char ''🐧''$' diff --git a/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt b/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt new file mode 100644 index 00000000000000..859eafcf84dff7 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt @@ -0,0 +1,52 @@ +# Regression test for issue #48557. +# Since builds in module mode do not support relative imports at all, the build +# ID for (and other contents of) a binary built with -trimpath in module mode +# should not depend on its working directory, even if the binary is specified as +# a list of relative source files. + +[short] skip # links and runs binaries + +env GOFLAGS=-trimpath +env GOCACHE=$WORK/gocache + + +# When we build a binary in module mode with -trimpath, the -D flag (for the +# "local import prefix") should not be passed to it. + +cd $WORK/tmp/foo +go build -x -o a.exe main.go +stderr ${/}compile$GOEXE.*' -nolocalimports' +! stderr ${/}compile$GOEXE.*' -D[ =]' + +go tool buildid a.exe +cp stdout ../foo-buildid.txt +go version a.exe +cp stdout ../foo-version.txt +cd .. + + +# On the second build — in a different directory but with -trimpath — the +# compiler should not be invoked, since the cache key should be identical. +# Only the linker and buildid tool should be needed. + +mkdir bar +cp foo/main.go bar/main.go +cd bar +go build -x -o a.exe main.go +! stderr ${/}compile$GOEXE + +go tool buildid a.exe +cp stdout ../bar-buildid.txt +go version a.exe +cp stdout ../bar-version.txt +cd .. + +cmp bar-buildid.txt foo-buildid.txt +cmp bar-version.txt foo-version.txt +cmp bar/a.exe foo/a.exe + + +-- $WORK/tmp/foo/main.go -- +package main + +func main() {} diff --git a/src/cmd/go/testdata/script/mod_build_versioned.txt b/src/cmd/go/testdata/script/mod_build_versioned.txt index d1d74de10c22b1..f55041834aefe8 100644 --- a/src/cmd/go/testdata/script/mod_build_versioned.txt +++ b/src/cmd/go/testdata/script/mod_build_versioned.txt @@ -1,7 +1,7 @@ env GO111MODULE=on [short] skip -go get -d rsc.io/fortune/v2 +go get rsc.io/fortune/v2 # The default executable name shouldn't be v2$GOEXE go build rsc.io/fortune/v2 diff --git a/src/cmd/go/testdata/script/mod_cache_dir.txt b/src/cmd/go/testdata/script/mod_cache_dir.txt index 7284ccf8bab735..4045928a9781b7 100644 --- a/src/cmd/go/testdata/script/mod_cache_dir.txt +++ b/src/cmd/go/testdata/script/mod_cache_dir.txt @@ -3,9 +3,9 @@ env GO111MODULE=on # Go should reject relative paths in GOMODCACHE environment. env GOMODCACHE="~/test" -! go get example.com/tools/cmd/hello +! go install example.com/tools/cmd/hello@latest stderr 'must be absolute path' env GOMODCACHE="./test" -! go get example.com/tools/cmd/hello +! go install example.com/tools/cmd/hello@latest stderr 'must be absolute path' diff --git a/src/cmd/go/testdata/script/mod_cache_rw.txt b/src/cmd/go/testdata/script/mod_cache_rw.txt index a5410764bc00f8..07755415d608a4 100644 --- a/src/cmd/go/testdata/script/mod_cache_rw.txt +++ b/src/cmd/go/testdata/script/mod_cache_rw.txt @@ -5,7 +5,7 @@ env GO111MODULE=on # golang.org/issue/31481: an explicit flag should make directories in the module # cache writable in order to work around the historical inability of 'rm -rf' to # forcibly remove files in unwritable directories. -go get -modcacherw -d rsc.io/quote@v1.5.2 +go get -modcacherw rsc.io/quote@v1.5.2 cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go # After adding an extraneous file, 'go mod verify' should fail. @@ -28,7 +28,7 @@ cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go # Windows does not respect FILE_ATTRIBUTE_READONLY on directories, according # to MSDN, so there we disable testing whether the directory itself is # unwritable. -go get -d rsc.io/quote@latest +go get rsc.io/quote@latest [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod [!windows] [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go ! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go diff --git a/src/cmd/go/testdata/script/mod_case.txt b/src/cmd/go/testdata/script/mod_case.txt index 4a4698600f7fb9..d3fb11dfa33216 100644 --- a/src/cmd/go/testdata/script/mod_case.txt +++ b/src/cmd/go/testdata/script/mod_case.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -go get -d +go get go list -m all stdout '^rsc.io/quote v1.5.2' stdout '^rsc.io/QUOTE v1.5.2' @@ -9,7 +9,7 @@ go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE stdout 'DEPS.*rsc.io/quote' stdout 'DIR.*!q!u!o!t!e' -go get -d rsc.io/QUOTE@v1.5.3-PRE +go get rsc.io/QUOTE@v1.5.3-PRE go list -m all stdout '^rsc.io/QUOTE v1.5.3-PRE' diff --git a/src/cmd/go/testdata/script/mod_case_cgo.txt b/src/cmd/go/testdata/script/mod_case_cgo.txt index f3d6aaa5abc77b..7c768a096395d9 100644 --- a/src/cmd/go/testdata/script/mod_case_cgo.txt +++ b/src/cmd/go/testdata/script/mod_case_cgo.txt @@ -2,7 +2,7 @@ env GO111MODULE=on -go get -d rsc.io/CGO +go get rsc.io/CGO [short] stop go build rsc.io/CGO diff --git a/src/cmd/go/testdata/script/mod_clean_cache.txt b/src/cmd/go/testdata/script/mod_clean_cache.txt index 01fbc381e043b2..2b8e820653b810 100644 --- a/src/cmd/go/testdata/script/mod_clean_cache.txt +++ b/src/cmd/go/testdata/script/mod_clean_cache.txt @@ -35,6 +35,9 @@ go clean -modcache ! stderr 'finding rsc.io' go mod edit -droprequire rsc.io/quote +! go clean -modcache m +stderr 'go: clean -modcache cannot be used with package arguments' + -- go.mod -- module m -- m.go -- diff --git a/src/cmd/go/testdata/script/mod_concurrent.txt b/src/cmd/go/testdata/script/mod_concurrent.txt index 8c215251587180..e2224d659b23a5 100644 --- a/src/cmd/go/testdata/script/mod_concurrent.txt +++ b/src/cmd/go/testdata/script/mod_concurrent.txt @@ -1,7 +1,7 @@ env GO111MODULE=on # Concurrent builds should succeed, even if they need to download modules. -go get -d ./x ./y +go get ./x ./y go build ./x & go build ./y wait diff --git a/src/cmd/go/testdata/script/mod_deprecate_message.txt b/src/cmd/go/testdata/script/mod_deprecate_message.txt index 567027935dc338..8e2771dc1f2250 100644 --- a/src/cmd/go/testdata/script/mod_deprecate_message.txt +++ b/src/cmd/go/testdata/script/mod_deprecate_message.txt @@ -1,25 +1,25 @@ # When there is a short single-line message, 'go get' should print it all. -go get -d short +go get short stderr '^go: module short is deprecated: short$' go list -m -u -f '{{.Deprecated}}' short stdout '^short$' # When there is a multi-line message, 'go get' should print the first line. -go get -d multiline +go get multiline stderr '^go: module multiline is deprecated: first line$' ! stderr 'second line' go list -m -u -f '{{.Deprecated}}' multiline stdout '^first line\nsecond line.$' # When there is a long message, 'go get' should print a placeholder. -go get -d long +go get long stderr '^go: module long is deprecated: \(message omitted: too long\)$' go list -m -u -f '{{.Deprecated}}' long stdout '^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$' # When a message contains unprintable chracters, 'go get' should say that # without printing the message. -go get -d unprintable +go get unprintable stderr '^go: module unprintable is deprecated: \(message omitted: contains non-printable characters\)$' go list -m -u -f '{{.Deprecated}}' unprintable stdout '^message contains ASCII BEL\x07$' diff --git a/src/cmd/go/testdata/script/mod_doc_path.txt b/src/cmd/go/testdata/script/mod_doc_path.txt new file mode 100644 index 00000000000000..57470a95c4c9ef --- /dev/null +++ b/src/cmd/go/testdata/script/mod_doc_path.txt @@ -0,0 +1,30 @@ +# cmd/doc should use GOROOT to locate the 'go' command, +# not use whatever is in $PATH. + +# Remove 'go' from $PATH. (It can still be located via $GOROOT/bin/go, and the +# test script's built-in 'go' command still knows where to find it.) +env PATH='' +[plan9] env path='' + +go doc p.X + +-- go.mod -- +module example + +go 1.19 + +require example.com/p v0.1.0 + +replace example.com/p => ./pfork +-- example.go -- +package example + +import _ "example.com/p" +-- pfork/go.mod -- +module example.com/p + +go 1.19 +-- pfork/p.go -- +package p + +const X = 42 diff --git a/src/cmd/go/testdata/script/mod_domain_root.txt b/src/cmd/go/testdata/script/mod_domain_root.txt index 14745b5812c682..c13029d534163e 100644 --- a/src/cmd/go/testdata/script/mod_domain_root.txt +++ b/src/cmd/go/testdata/script/mod_domain_root.txt @@ -2,7 +2,7 @@ # (example.com not example.com/something) env GO111MODULE=on -go get -d +go get -- go.mod -- module x diff --git a/src/cmd/go/testdata/script/mod_dot.txt b/src/cmd/go/testdata/script/mod_dot.txt index ca8d5c6cc2d62d..aa24986c72da31 100644 --- a/src/cmd/go/testdata/script/mod_dot.txt +++ b/src/cmd/go/testdata/script/mod_dot.txt @@ -1,3 +1,4 @@ +env GOWORK=off env GO111MODULE=on # golang.org/issue/32917 and golang.org/issue/28459: 'go build' and 'go test' @@ -5,11 +6,11 @@ env GO111MODULE=on # to resolve an external module. cd dir ! go get -stderr '^go get: no package in current directory$' +stderr '^go: no package to get in current directory$' ! go get . -stderr '^go get \.: no package in current directory$' +stderr '^go: .: no package to get in current directory$' ! go get ./subdir -stderr '^go get: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' +stderr '^go: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' ! go list ! stderr 'cannot find module providing package' stderr '^no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$' diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt index c2b72b2a02cab8..154e68338b6b3e 100644 --- a/src/cmd/go/testdata/script/mod_download.txt +++ b/src/cmd/go/testdata/script/mod_download.txt @@ -93,19 +93,19 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip # download reports errors encountered when locating modules ! go mod download bad/path -stderr '^go mod download: module bad/path: not a known dependency$' +stderr '^go: module bad/path: not a known dependency$' ! go mod download bad/path@latest -stderr '^go mod download: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' +stderr '^go: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' ! go mod download rsc.io/quote@v1.999.999 -stderr '^go mod download: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' +stderr '^go: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' ! go mod download -json bad/path stdout '^\t"Error": "module bad/path: not a known dependency"' # download main module produces a warning or error go mod download m -stderr '^go mod download: skipping argument m that resolves to the main module\n' +stderr '^go: skipping download of m that resolves to the main module\n' ! go mod download m@latest -stderr '^go mod download: m@latest: malformed module path "m": missing dot in first path element$' +stderr '^go: m@latest: malformed module path "m": missing dot in first path element$' # download without arguments updates go.mod and go.sum after loading the # build list, but does not save sums for downloaded zips. @@ -128,6 +128,50 @@ rm go.sum go mod download all cmp go.mod.update go.mod grep '^rsc.io/sampler v1.3.0 ' go.sum + +# https://golang.org/issue/44435: At go 1.17 or higher, 'go mod download' +# (without arguments) should only download the modules explicitly required in +# the go.mod file, not (presumed-irrelevant) transitive dependencies. +# +# (If the go.mod file is inconsistent, the version downloaded should be the +# selected version from the broader graph, but the go.mod file will also be +# updated to list the correct versions. If at some point we change 'go mod +# download' to stop updating for consistency, then it should fail if the +# requirements are inconsistent.) + +rm go.sum +cp go.mod.orig go.mod +go mod edit -go=1.17 +cp go.mod.update go.mod.go117 +go mod edit -go=1.17 go.mod.go117 + +go clean -modcache +go mod download +cmp go.mod go.mod.go117 + +go list -e -m all +stdout '^rsc.io/quote v1.5.2$' +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +stdout '^rsc.io/sampler v1.3.0$' +! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip +exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip +stdout '^golang\.org/x/text v0.0.0-20170915032832-14c0d48ead0c$' +! exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip +cmp go.mod go.mod.go117 + +# However, 'go mod download all' continues to download the selected version +# of every module reported by 'go list -m all'. + +cp go.mod.orig go.mod +go mod edit -go=1.17 +go clean -modcache +go mod download all +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip +exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip +exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip +cmp go.mod go.mod.go117 + cd .. # allow go mod download without go.mod diff --git a/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt b/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt new file mode 100644 index 00000000000000..3b19acc1b152a0 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt @@ -0,0 +1,28 @@ +env GO111MODULE=on + +[!net] skip +[!exec:git] skip + +env GOPROXY=direct +env HOME=$WORK/home/gopher + + +go env GOPROXY +stdout 'direct' + +exec git config --get log.decorate +stdout 'full' + +# Test that Git log with user's global config '~/gitconfig' has log.decorate=full +# go mod download has an error 'v1.x.y is not a tag' +# with go1.16.14: +# `go1.16.14 list -m vcs-test.golang.org/git/gitrepo1.git@v1.2.3` +# will output with error: +# go list -m: vcs-test.golang.org/git/gitrepo1.git@v1.2.3: invalid version: unknown revision v1.2.3 +# See golang/go#51312. +go list -m vcs-test.golang.org/git/gitrepo1.git@v1.2.3 +stdout 'vcs-test.golang.org/git/gitrepo1.git v1.2.3' + +-- $WORK/home/gopher/.gitconfig -- +[log] + decorate = full \ No newline at end of file diff --git a/src/cmd/go/testdata/script/mod_download_insecure_redirect.txt b/src/cmd/go/testdata/script/mod_download_insecure_redirect.txt new file mode 100644 index 00000000000000..46eb666686502c --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_insecure_redirect.txt @@ -0,0 +1,32 @@ +# golang.org/issue/29591: 'go get' was following plain-HTTP redirects even without -insecure (now replaced by GOINSECURE). + +[!net] skip +[!exec:git] skip + +env GO111MODULE=on +env GOPROXY=direct +env GOSUMDB=off + +! go mod download vcs-test.golang.org/insecure/go/insecure@latest +stderr 'redirected .* to insecure URL' + +# insecure host +env GOINSECURE=vcs-test.golang.org +go clean -modcache +go mod download vcs-test.golang.org/insecure/go/insecure@latest + +# insecure glob host +env GOINSECURE=*.golang.org +go clean -modcache +go mod download vcs-test.golang.org/insecure/go/insecure@latest + +# insecure multiple host +env GOINSECURE=somewhere-else.com,*.golang.org +go clean -modcache +go mod download vcs-test.golang.org/insecure/go/insecure@latest + +# different insecure host does not fetch +env GOINSECURE=somewhere-else.com +go clean -modcache +! go mod download vcs-test.golang.org/insecure/go/insecure@latest +stderr 'redirected .* to insecure URL' diff --git a/src/cmd/go/testdata/script/mod_download_issue51114.txt b/src/cmd/go/testdata/script/mod_download_issue51114.txt new file mode 100644 index 00000000000000..92479c6dd3cd0a --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_issue51114.txt @@ -0,0 +1,21 @@ +[short] skip +[!exec:git] skip +[!net] skip +[!linux] skip # Uses XDG_CONFIG_HOME + +env GIT_CONFIG_GLOBAL=$WORK/.gitconfig +env GOPROXY=direct + +! go mod download +stderr '^go: github\.com/golang/notexist/subdir@v0.1.0: reading github\.com/golang/notexist/subdir/go\.mod at revision subdir/v0\.1\.0: ' + +-- go.mod -- +module test + +go 1.18 + +require github.com/golang/notexist/subdir v0.1.0 + +-- $WORK/.gitconfig -- +[url "git@github.com:"] + insteadOf = https://github.com/ diff --git a/src/cmd/go/testdata/script/mod_download_partial.txt b/src/cmd/go/testdata/script/mod_download_partial.txt index 0aab60ddaf00f8..617b1fd8e363e3 100644 --- a/src/cmd/go/testdata/script/mod_download_partial.txt +++ b/src/cmd/go/testdata/script/mod_download_partial.txt @@ -1,5 +1,5 @@ # Download modules and populate go.sum. -go get -d -modcacherw +go get -modcacherw exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod # 'go mod verify' should fail if we delete a file. @@ -15,12 +15,13 @@ cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial go mod verify # 'go list' should not load packages from the directory. -# NOTE: the message "directory $dir outside available modules" is reported -# for directories not in the main module, active modules in the module cache, -# or local replacements. In this case, the directory is in the right place, -# but it's incomplete, so 'go list' acts as if it's not an active module. +# NOTE: the message "directory $dir outside main module or its selected dependencies" +# is reported for directories not in the main module, active modules in the +# module cache, or local replacements. In this case, the directory is in the +# right place, but it's incomplete, so 'go list' acts as if it's not an +# active module. ! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 -stderr 'outside available modules' +stderr 'outside main module or its selected dependencies' # 'go list -m' should not print the directory. go list -m -f '{{.Dir}}' rsc.io/quote diff --git a/src/cmd/go/testdata/script/mod_download_private_vcs.txt b/src/cmd/go/testdata/script/mod_download_private_vcs.txt new file mode 100644 index 00000000000000..da9fe0290b7a4d --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_private_vcs.txt @@ -0,0 +1,39 @@ +env GO111MODULE=on + +# Testing stderr for git ls-remote; turn off proxy. +[!net] skip +[!exec:git] skip +env GOPROXY=direct + +! go mod download github.com/golang/nonexist@latest +stderr 'Confirm the import path was entered correctly.' +stderr 'If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.' +! stdout . + +# Fetching a nonexistent commit should return an "unknown revision" +# error message. +! go mod download github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b +stderr '^go: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' +! stdout . + +! go mod download github.com/golang/nonexist@master +stderr '^Confirm the import path was entered correctly.$' +stderr '^If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.$' +! stderr 'unknown revision' +! stdout . + +[!exec:false] stop + +# Test that Git clone errors will be shown to the user instead of a generic +# "unknown revision" error. To do this we want to force git ls-remote to return +# an error we don't already have special handling for. See golang/go#42751. +env HOME=$WORK${/}home${/}gopher +env GIT_SSH_COMMAND=false +! go install github.com/golang/nonexist@master +stderr 'fatal: Could not read from remote repository.' +! stderr 'unknown revision' +! stdout . + +-- $WORK/home/gopher/.gitconfig -- +[url "git@github.com:"] + insteadOf = https://github.com/ diff --git a/src/cmd/go/testdata/script/mod_download_svn.txt b/src/cmd/go/testdata/script/mod_download_svn.txt new file mode 100644 index 00000000000000..79e00dc9706c9f --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_svn.txt @@ -0,0 +1,29 @@ +[!net] skip +[!exec:svn] skip + +# 'go mod download' will fall back to svn+ssh once svn fails over protocols like https. +# If vcs-test.golang.org isn't in the user's known_hosts file, this will result +# in an ssh prompt, which will stop 'go test' entirely +# +# Unfortunately, there isn't a way to globally disable host checking for ssh, +# without modifying the real system's or user's configs. Changing $HOME won't +# affect ssh either, as it ignores the environment variable entirely. +# +# However, a useful trick is pointing SVN_SSH to a program that doesn't exist, +# resulting in svn skipping ssh entirely. Alternatives like +# SVN_SSH="ssh -o StrictHostKeyChecking=no" didn't avoid the prompt. +env SVN_SSH="svn_do_not_use_ssh" + +env GO111MODULE=on +env GOPROXY=direct +env GOSUMDB=off + +# Attempting to get a module zip using svn should succeed. +go mod download vcs-test.golang.org/svn/hello.svn@000000000001 +exists $GOPATH/pkg/mod/cache/download/vcs-test.golang.org/svn/hello.svn/@v/v0.0.0-20170922011245-000000000001.zip + +# Attempting to get a nonexistent module using svn should fail with a +# reasonable message instead of a panic. +! go mod download vcs-test.golang.org/svn/nonexistent.svn@latest +! stderr panic +stderr 'go: module vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "latest"$' diff --git a/src/cmd/go/testdata/script/mod_download_too_many_redirects.txt b/src/cmd/go/testdata/script/mod_download_too_many_redirects.txt new file mode 100644 index 00000000000000..a6b5a590548729 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_download_too_many_redirects.txt @@ -0,0 +1,10 @@ +env GO111MODULE=on +env GOPROXYBASE=$GOPROXY +env GOPROXY=$GOPROXYBASE/redirect/11 +env GOSUMDB=off + +! go mod download rsc.io/quote@v1.2.0 +stderr 'stopped after 10 redirects' + +env GOPROXY=$GOPROXYBASE/redirect/9 +go mod download rsc.io/quote@v1.2.0 diff --git a/src/cmd/go/testdata/script/mod_e.txt b/src/cmd/go/testdata/script/mod_e.txt index 3a0d18dabc2c94..3cffaf6ef1c1cf 100644 --- a/src/cmd/go/testdata/script/mod_e.txt +++ b/src/cmd/go/testdata/script/mod_e.txt @@ -24,11 +24,11 @@ cmp go.mod.orig go.mod ! go mod vendor -stderr '^example.com/untidy imports\n\texample.net/directnotfound: cannot find module providing package example.net/directnotfound: module example.net/directnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy imports\n\texample.net/directnotfound: no required module provides package example.net/directnotfound; to add it:\n\tgo get example.net/directnotfound$' -stderr '^example.com/untidy imports\n\texample.net/m imports\n\texample.net/indirectnotfound: cannot find module providing package example.net/indirectnotfound: module example.net/indirectnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$' -stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: cannot find module providing package example.net/directtestnotfound: module example.net/directtestnotfound: reading http://.*: 404 Not Found$' +stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: no required module provides package example.net/directtestnotfound; to add it:\n\tgo get example.net/directtestnotfound$' ! stderr 'indirecttestnotfound' # Vendor prunes test dependencies. @@ -43,15 +43,22 @@ stderr -count=4 'cannot find module providing package' cmp go.mod.final go.mod -# 'go mod vendor -e' still logs the errors, but succeeds and updates go.mod. - +# 'go mod vendor -e' still logs the errors, but creates a vendor directory +# and exits with status 0. +# 'go mod vendor -e' does not update go.mod and will not vendor packages that +# would require changing go.mod, for example, by adding a requirement. cp go.mod.orig go.mod go mod vendor -e -stderr -count=3 'cannot find module providing package' -cmp go.mod.final go.mod +stderr -count=2 'no required module provides package' +stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$' exists vendor/modules.txt -exists vendor/example.net/m/m.go +! exists vendor/example.net +go mod edit -require example.net/m@v0.1.0 +go mod vendor -e +stderr -count=3 'no required module provides package' +exists vendor/modules.txt +exists vendor/example.net/m/m.go -- go.mod -- module example.com/untidy diff --git a/src/cmd/go/testdata/script/mod_edit.txt b/src/cmd/go/testdata/script/mod_edit.txt index 5aa5ca1ffc02c5..ebc032a73cf781 100644 --- a/src/cmd/go/testdata/script/mod_edit.txt +++ b/src/cmd/go/testdata/script/mod_edit.txt @@ -23,18 +23,18 @@ cmpenv go.mod $WORK/go.mod.edit2 # -exclude and -retract reject invalid versions. ! go mod edit -exclude=example.com/m@bad -stderr '^go mod: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -retract=bad -stderr '^go mod: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -exclude=example.com/m@v2.0.0 -stderr '^go mod: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' +stderr '^go: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' ! go mod edit -exclude=example.com/m/v2@v1.0.0 -stderr '^go mod: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' +stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' ! go mod edit -exclude=gopkg.in/example.v1@v2.0.0 -stderr '^go mod: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' +stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' cmpenv go.mod $WORK/go.mod.edit2 diff --git a/src/cmd/go/testdata/script/mod_edit_go.txt b/src/cmd/go/testdata/script/mod_edit_go.txt index 38321d071fb35e..7e9740fec43763 100644 --- a/src/cmd/go/testdata/script/mod_edit_go.txt +++ b/src/cmd/go/testdata/script/mod_edit_go.txt @@ -2,7 +2,7 @@ env GO111MODULE=on ! go build -stderr 'type aliases only supported as of' +stderr ' type aliases requires' go mod edit -go=1.9 grep 'go 1.9' go.mod go build @@ -11,7 +11,7 @@ go build # the cached 1.9 build. (https://golang.org/issue/37804) go mod edit -go=1.8 ! go build -stderr 'type aliases only supported as of' +stderr 'type aliases requires' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_fs_patterns.txt b/src/cmd/go/testdata/script/mod_fs_patterns.txt index a20fefd6d396d9..276d04e538e0b1 100644 --- a/src/cmd/go/testdata/script/mod_fs_patterns.txt +++ b/src/cmd/go/testdata/script/mod_fs_patterns.txt @@ -51,11 +51,11 @@ stdout '^at$' # a package path. cd ../badat/bad@ ! go list . -stderr 'directory . outside available modules' +stderr 'directory . outside main module or its selected dependencies' ! go list $PWD -stderr 'directory . outside available modules' +stderr 'directory . outside main module or its selected dependencies' ! go list $PWD/... -stderr 'directory . outside available modules' +stderr 'directory . outside main module or its selected dependencies' -- x/go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt index daed03b02c6cf4..92149933686a6f 100644 --- a/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt +++ b/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt @@ -4,14 +4,14 @@ cp go.mod go.mod.orig # If there is no sensible *package* meaning for 'm/p', it should refer # to *module* m/p. -go get -d m/p # @latest +go get m/p # @latest go list -m all stdout '^m/p v0.3.0 ' ! stdout '^m ' cp go.mod.orig go.mod -go get -d m/p@v0.1.0 +go get m/p@v0.1.0 go list -m all stdout '^m/p v0.1.0 ' ! stdout '^m ' @@ -22,7 +22,7 @@ stdout '^m/p v0.1.0 ' # (It only refers to *module* m/p if there is no such package at the # requested version.) -go get -d m/p@v0.2.0 +go get m/p@v0.2.0 go list -m all stdout '^m v0.2.0 ' stdout '^m/p v0.1.0 ' # unchanged from the previous case @@ -30,7 +30,7 @@ stdout '^m/p v0.1.0 ' # unchanged from the previous case # Repeating the above with module m/p already in the module graph does not # change its meaning. -go get -d m/p@v0.2.0 +go get m/p@v0.2.0 go list -m all stdout '^m v0.2.0 ' stdout '^m/p v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt index 33605f51a5b505..0af78bd4f252d7 100644 --- a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt +++ b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt @@ -8,14 +8,14 @@ cp go.mod go.mod.orig # # TODO(#27899): Should we automatically upgrade example.net/m to v0.2.0 # to resolve the conflict? -! go get -d example.net/m/p@v1.0.0 +! go get example.net/m/p@v1.0.0 stderr '^example.net/m/p: ambiguous import: found package example.net/m/p in multiple modules:\n\texample.net/m v0.1.0 \(.*[/\\]m1[/\\]p\)\n\texample.net/m/p v1.0.0 \(.*[/\\]p0\)\n\z' cmp go.mod go.mod.orig # Upgrading both modules simultaneously resolves the ambiguous upgrade. # Note that this command line mixes a module path (example.net/m) # and a package path (example.net/m/p) in the same command. -go get -d example.net/m@v0.2.0 example.net/m/p@v1.0.0 +go get example.net/m@v0.2.0 example.net/m/p@v1.0.0 go list -m all stdout '^example.net/m v0.2.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt index 0e7f93bccb5b87..1641196007b8e1 100644 --- a/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt +++ b/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt @@ -8,7 +8,7 @@ cp go.mod go.mod.orig # From a clean slate, 'go get' currently does the same thing as 'go mod tidy': # it resolves the package from the module with the longest matching prefix. -go get -d example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -21,7 +21,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous v0.1.0$' ! stdout '^example.net/ambiguous/nested ' @@ -30,7 +30,7 @@ stdout '^example.net/ambiguous v0.1.0$' # The user should be able to make the command unambiguous by explicitly # upgrading the conflicting module... -go get -d example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' stdout '^example.net/ambiguous v0.2.0$' @@ -41,7 +41,7 @@ stdout '^example.net/ambiguous v0.2.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none +go get example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none go list -m all ! stdout '^example.net/ambiguous/nested ' stdout '^example.net/ambiguous v0.1.0$' @@ -53,7 +53,7 @@ stdout '^example.net/ambiguous v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0 +go get example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -66,7 +66,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod -go get -d example.net/ambiguous/nested/pkg/...@v0.1.0 +go get example.net/ambiguous/nested/pkg/...@v0.1.0 go list -m all stdout '^example.net/ambiguous/nested v0.1.0$' ! stdout '^example.net/ambiguous ' @@ -75,7 +75,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$' cp go.mod.orig go.mod go mod edit -require=example.net/ambiguous@v0.1.0 -go get -d example.net/ambiguous/nested/pkg/...@v0.1.0 +go get example.net/ambiguous/nested/pkg/...@v0.1.0 go list -m all ! stdout '^example.net/ambiguous/nested ' stdout '^example.net/ambiguous v0.1.0$' diff --git a/src/cmd/go/testdata/script/mod_get_changes.txt b/src/cmd/go/testdata/script/mod_get_changes.txt index 3287b2a6095a1c..12a112b4d87af7 100644 --- a/src/cmd/go/testdata/script/mod_get_changes.txt +++ b/src/cmd/go/testdata/script/mod_get_changes.txt @@ -3,9 +3,9 @@ # for changed indirect dependencies. go list -m all ! stdout golang.org/x/text -go get -d rsc.io/quote@v1.5.2 -stderr '^go get: added rsc.io/quote v1.5.2$' -stderr '^go get: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' +go get rsc.io/quote@v1.5.2 +stderr '^go: added rsc.io/quote v1.5.2$' +stderr '^go: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' ! stderr '^go get.*golang.org/x/text' go list -m all stdout golang.org/x/text @@ -14,18 +14,18 @@ cmp go.mod go.mod.upgrade # When removing a requirement, 'go get' prints a message for the requiremnent # and for changed explicit dependencies. 'go get' does not print messages # for changed indirect dependencies. -go get -d rsc.io/sampler@none -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.3.0$' -stderr '^go get: removed rsc.io/sampler v1.3.0$' +go get rsc.io/sampler@none +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.3.0$' +stderr '^go: removed rsc.io/sampler v1.3.0$' ! stderr '^go get.*golang.org/x/text' cmp go.mod go.mod.downgrade # When removing or downgrading a requirement, 'go get' also prints a message # for explicit dependencies removed as a consequence. cp go.mod.usequote go.mod -go get -d rsc.io/quote@v1.5.1 -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.5.1$' -stderr '^go get: removed usequote v0.0.0$' +go get rsc.io/quote@v1.5.1 +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.5.1$' +stderr '^go: removed usequote v0.0.0$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_get_cmd.txt b/src/cmd/go/testdata/script/mod_get_cmd.txt deleted file mode 100644 index d31cee1444a6ad..00000000000000 --- a/src/cmd/go/testdata/script/mod_get_cmd.txt +++ /dev/null @@ -1,20 +0,0 @@ -env GO111MODULE=on -[short] skip - -# Test that when 'go get' is run from $GOBIN, it does not delete binaries -# after it installs them. Verifies golang.org/issue/32766. - -go get example.com/tools/cmd/hello - -# 'go get' should not delete the command when run from $GOPATH/bin -cd $GOPATH/bin -exists hello$GOEXE -go get example.com/tools/cmd/hello -exists hello$GOEXE - -# 'go get' should not delete the command when run from a different $GOBIN -mkdir $WORK/bin -cd $WORK/bin -env GOBIN=$WORK/bin -go get example.com/tools/cmd/hello -exists hello$GOEXE diff --git a/src/cmd/go/testdata/script/mod_get_commit.txt b/src/cmd/go/testdata/script/mod_get_commit.txt index 4649491a532a8c..f60eaab3a77e09 100644 --- a/src/cmd/go/testdata/script/mod_get_commit.txt +++ b/src/cmd/go/testdata/script/mod_get_commit.txt @@ -5,18 +5,18 @@ env GO111MODULE=on # golang.org/x/text/language@commit should resolve. # Because of -d, the compiler should not run. -go get -d -x golang.org/x/text/language@14c0d48 +go get -x golang.org/x/text/language@14c0d48 ! stderr 'compile|cp|gccgo .*language\.a$' # go get should skip build with no Go files in root -go get -d golang.org/x/text@14c0d48 +go get golang.org/x/text@14c0d48 # dropping -d, we should see a build. [short] skip env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache. -go get -x golang.org/x/text/language@14c0d48 +go build -x golang.org/x/text/language stderr 'compile|cp|gccgo .*language\.a$' # BUG: after the build, the package should not be stale, as 'go install' would @@ -24,19 +24,20 @@ stderr 'compile|cp|gccgo .*language\.a$' go list -f '{{.Stale}}' golang.org/x/text/language stdout ^true -# install after get should not run the compiler again. +# install after build should not run the compiler again. go install -x golang.org/x/text/language ! stderr 'compile|cp|gccgo .*language\.a$' -# even with -d, we should see an error for unknown packages. -! go get -d -x golang.org/x/text/foo@14c0d48 +# we should see an error for unknown packages. +! go get -x golang.org/x/text/foo@14c0d48 +stderr '^go: module golang.org/x/text@14c0d48 found \(v0.3.0\), but does not contain package golang.org/x/text/foo$' # get pseudo-version should record that version -go get -d rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 +go get rsc.io/quote@v0.0.0-20180214005840-23179ee8a569 grep 'rsc.io/quote v0.0.0-20180214005840-23179ee8a569' go.mod # but as commit should record as v1.5.1 -go get -d rsc.io/quote@23179ee8 +go get rsc.io/quote@23179ee8 grep 'rsc.io/quote v1.5.1' go.mod # go mod edit -require does not interpret commits @@ -44,7 +45,7 @@ go mod edit -require rsc.io/quote@23179ee grep 'rsc.io/quote 23179ee' go.mod # but other commands fix them -go mod graph +go list -m -mod=mod all grep 'rsc.io/quote v1.5.1' go.mod -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt index 63cd27a42d2bc7..03258f52966fc7 100644 --- a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt +++ b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt @@ -2,10 +2,10 @@ env GO111MODULE=on -# 'go get' outside a module with an executable prints a deprecation message. -go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' -stderr 'Use ''go install pkg@version'' instead.' +# 'go get' outside a module prints an error. +! go get example.com/cmd/a +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' cp go.mod.orig go.mod @@ -13,13 +13,16 @@ cp go.mod.orig go.mod # This will stop building in the future, but it's the command we want to use. go get rsc.io/quote ! stderr deprecated +! stderr 'no longer installs' cp go.mod.orig go.mod -# 'go get' inside a module with an executable prints a different -# deprecation message. +# 'go get' inside a module with an executable does not print a message. +# In 1.16 and 1.17, 'go get' did print a message in this case suggesting the +# use of -d. In 1.18, -d is a no-op, and we'd like to begin discouraging +# its use. go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' -stderr 'To adjust and download dependencies of the current module, use ''go get -d''' +! stderr deprecated +! stderr 'no longer installs' cp go.mod.orig go.mod # 'go get' should not print a warning for a main package inside the main module. diff --git a/src/cmd/go/testdata/script/mod_get_deprecated.txt b/src/cmd/go/testdata/script/mod_get_deprecated.txt index 7bdd7a58a89b5d..ec7bcfdb997e2e 100644 --- a/src/cmd/go/testdata/script/mod_get_deprecated.txt +++ b/src/cmd/go/testdata/script/mod_get_deprecated.txt @@ -1,31 +1,31 @@ # 'go get pkg' should not show a deprecation message for an unrelated module. -go get -d ./use/nothing +go get ./use/nothing ! stderr 'module.*is deprecated' # 'go get pkg' should show a deprecation message for the module providing pkg. -go get -d example.com/deprecated/a +go get example.com/deprecated/a stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' -go get -d example.com/deprecated/a@v1.0.0 +go get example.com/deprecated/a@v1.0.0 stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' # 'go get pkg' should show a deprecation message for a module providing # packages directly imported by pkg. -go get -d ./use/a +go get ./use/a stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$' # 'go get pkg' may show a deprecation message for an indirectly required module # if it provides a package named on the command line. -go get -d ./use/b +go get ./use/b ! stderr 'module.*is deprecated' -go get -d local/use +go get local/use ! stderr 'module.*is deprecated' -go get -d example.com/deprecated/b +go get example.com/deprecated/b stderr '^go: module example.com/deprecated/b is deprecated: in example.com/deprecated/b@v1.9.0$' # 'go get pkg' does not show a deprecation message for a module providing a # directly imported package if the module is no longer deprecated in its # latest version, even if the module is deprecated in its current version. -go get -d ./use/undeprecated +go get ./use/undeprecated ! stderr 'module.*is deprecated' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_direct.txt b/src/cmd/go/testdata/script/mod_get_direct.txt index 42ccbcd38a143b..856e05bc329328 100644 --- a/src/cmd/go/testdata/script/mod_get_direct.txt +++ b/src/cmd/go/testdata/script/mod_get_direct.txt @@ -10,7 +10,7 @@ env GO111MODULE=on env GOPROXY=direct env GOSUMDB=off -go list -m cloud.google.com/go@master +go list -m cloud.google.com/go@main ! stdout 'v0.0.0-' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt b/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt index efc38f77c85d37..0f5ba992da204e 100644 --- a/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt +++ b/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt @@ -18,7 +18,7 @@ cp go.mod go.mod.orig go mod tidy cmp go.mod.orig go.mod -go get -d example.com/d@v0.1.0 +go get example.com/d@v0.1.0 go list -m all stdout '^example.com/b v0.1.0 ' stdout '^example.com/c v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_downgrade.txt b/src/cmd/go/testdata/script/mod_get_downgrade.txt index c26c5e1c21013a..2eed56d9b71b89 100644 --- a/src/cmd/go/testdata/script/mod_get_downgrade.txt +++ b/src/cmd/go/testdata/script/mod_get_downgrade.txt @@ -3,25 +3,25 @@ env GO111MODULE=on # downgrade sampler should downgrade quote cp go.mod.orig go.mod -go get -d rsc.io/sampler@v1.0.0 +go get rsc.io/sampler@v1.0.0 go list -m all stdout 'rsc.io/quote v1.4.0' stdout 'rsc.io/sampler v1.0.0' # downgrade sampler away should downgrade quote further -go get -d rsc.io/sampler@none +go get rsc.io/sampler@none go list -m all stdout 'rsc.io/quote v1.3.0' # downgrade should report inconsistencies and not change go.mod -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go list -m all stdout 'rsc.io/quote v1.5.1' stdout 'rsc.io/sampler v1.3.0' -! go get -d rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none -stderr -count=1 '^go get:' -stderr '^go get: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' +! go get rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none +! stderr add|remove|upgrad|downgrad +stderr '^go: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' go list -m all stdout 'rsc.io/quote v1.5.1' @@ -29,7 +29,7 @@ stdout 'rsc.io/sampler v1.3.0' # go get -u args should limit upgrades cp go.mod.empty go.mod -go get -d -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0 +go get -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0 go list -m all stdout 'rsc.io/quote v1.4.0' stdout 'rsc.io/sampler v1.0.0' @@ -40,7 +40,7 @@ stdout 'rsc.io/sampler v1.0.0' cp go.mod.orig go.mod go list -m -versions example.com/latemigrate/v2 stdout v2.0.0 # proxy may serve incompatible versions -go get -d rsc.io/quote@none +go get rsc.io/quote@none go list -m all ! stdout 'example.com/latemigrate/v2' diff --git a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt index 5b768faeb1802c..582593b96cdfb6 100644 --- a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt +++ b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt @@ -4,15 +4,15 @@ cp go.mod go.mod.orig # not yet present in that module should report the version mismatch # rather than a "matched no packages" warning. -! go get -d example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/... -stderr '^go get: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' +! go get example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/... +stderr '^go: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' ! stderr 'matched no packages' cmp go.mod.orig go.mod # A wildcard pattern should match the pattern with that path. -go get -d example.net/pkgadded/...@v1.0.0 +go get example.net/pkgadded/...@v1.0.0 go list -m all stdout '^example.net/pkgadded v1.0.0' cp go.mod.orig go.mod @@ -22,12 +22,12 @@ cp go.mod.orig go.mod # and another argument constrains away the version that provides that # package, then 'go get' should fail with a useful error message. -! go get -d example.net/pkgadded@v1.0.0 . +! go get example.net/pkgadded@v1.0.0 . stderr '^example.com/m imports\n\texample.net/pkgadded/subpkg: cannot find module providing package example.net/pkgadded/subpkg$' ! stderr 'example.net/pkgadded v1\.2\.0' cmp go.mod.orig go.mod -go get -d example.net/pkgadded@v1.0.0 +go get example.net/pkgadded@v1.0.0 ! go list -deps -mod=readonly . stderr '^m.go:3:8: cannot find module providing package example\.net/pkgadded/subpkg: ' diff --git a/src/cmd/go/testdata/script/mod_get_downup_artifact.txt b/src/cmd/go/testdata/script/mod_get_downup_artifact.txt index c20583b22a1368..111a54f8f7370c 100644 --- a/src/cmd/go/testdata/script/mod_get_downup_artifact.txt +++ b/src/cmd/go/testdata/script/mod_get_downup_artifact.txt @@ -55,7 +55,7 @@ stdout '^example.com/d v0.1.0 ' # upgrades of module d and addition of module e, which are not relevant to # b@v0.1.0 and should not be added to the main module's dependencies. -go get -u -d example.com/a@latest example.com/c@v0.1.0 +go get -u example.com/a@latest example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_downup_indirect.txt b/src/cmd/go/testdata/script/mod_get_downup_indirect.txt index ced1dcd6b14e37..3a46a774ce7c45 100644 --- a/src/cmd/go/testdata/script/mod_get_downup_indirect.txt +++ b/src/cmd/go/testdata/script/mod_get_downup_indirect.txt @@ -25,7 +25,7 @@ cp go.mod go.mod.orig go mod tidy cmp go.mod.orig go.mod -go get -d example.com/d@v0.1.0 +go get example.com/d@v0.1.0 go list -m all ! stdout '^example.com/b ' ! stdout '^example.com/c ' diff --git a/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt b/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt index c49615cecb34d2..b678a177b521e3 100644 --- a/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt +++ b/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt @@ -28,7 +28,7 @@ cmp go.mod.orig go.mod # When we downgrade d.2 to d.1, no dependency on e should be added # because nothing else in the module or import graph requires it. -go get -d example.net/d@v0.1.0 +go get example.net/d@v0.1.0 go list -m all stdout '^example.net/b v0.2.1-0.20210219000000-000000000000 ' diff --git a/src/cmd/go/testdata/script/mod_get_errors.txt b/src/cmd/go/testdata/script/mod_get_errors.txt index 5c37058d1c1b95..7cb03ce2f1e6b5 100644 --- a/src/cmd/go/testdata/script/mod_get_errors.txt +++ b/src/cmd/go/testdata/script/mod_get_errors.txt @@ -1,35 +1,23 @@ cp go.mod go.mod.orig -# Both 'go get' and 'go get -d' should fail, without updating go.mod, -# if the transitive dependencies of the requested package (by default, -# the package in the current directory) cannot be resolved. +# 'go get' should fail, without updating go.mod, if the transitive dependencies +# of the requested package (by default, the package in the current directory) +# cannot be resolved. ! go get stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$' cmp go.mod.orig go.mod -! go get -d -stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$' -cmp go.mod.orig go.mod - cd importsyntax -# If 'go get' fails due to a compile error (such as a syntax error), -# it should not update the go.mod file. - -! go get -stderr '^..[/\\]badimport[/\\]syntaxerror[/\\]syntaxerror.go:1:1: expected ''package'', found pack$' # TODO: An import stack would be nice. -cmp ../go.mod.orig ../go.mod - - # A syntax error in a dependency prevents the compiler from needing that -# dependency's imports, so 'go get -d' should not report an error when those +# dependency's imports, so 'go get' should not report an error when those # imports cannot be resolved: it has all of the dependencies that the compiler # needs, and the user did not request to run the compiler. -go get -d +go get cmp ../go.mod.syntax-d ../go.mod diff --git a/src/cmd/go/testdata/script/mod_get_extra.txt b/src/cmd/go/testdata/script/mod_get_extra.txt index 7efa24e87b0d35..083e03678e48f1 100644 --- a/src/cmd/go/testdata/script/mod_get_extra.txt +++ b/src/cmd/go/testdata/script/mod_get_extra.txt @@ -4,7 +4,7 @@ cp go.mod go.mod.orig # determined by explicit queries to any version other than the explicit one. # Otherwise, 'go get -u' could introduce spurious dependencies. -go get -d -u example.net/a@v0.1.0 example.net/b@v0.1.0 +go get -u example.net/a@v0.1.0 example.net/b@v0.1.0 go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' @@ -16,7 +16,7 @@ stdout '^example.net/b v0.1.0 ' cp go.mod.orig go.mod -go get -d -u example.net/a@v0.1.0 example.net/b/...@v0.1.0 +go get -u example.net/a@v0.1.0 example.net/b/...@v0.1.0 go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_fallback.txt b/src/cmd/go/testdata/script/mod_get_fallback.txt index 9733fa366bea57..35722333d6a0e8 100644 --- a/src/cmd/go/testdata/script/mod_get_fallback.txt +++ b/src/cmd/go/testdata/script/mod_get_fallback.txt @@ -5,6 +5,11 @@ env GO111MODULE=on env GOPROXY=https://proxy.golang.org,direct env GOSUMDB=off -go get -x -v -d golang.org/x/tools/cmd/goimports +go get -x -v golang.org/x/tools/cmd/goimports stderr '# get https://proxy.golang.org/golang.org/x/tools/@v/list' ! stderr '# get https://golang.org' + +-- go.mod -- +module m + +go 1.18 diff --git a/src/cmd/go/testdata/script/mod_get_fossil.txt b/src/cmd/go/testdata/script/mod_get_fossil.txt index baad544557af88..c2d42f0f596f4a 100644 --- a/src/cmd/go/testdata/script/mod_get_fossil.txt +++ b/src/cmd/go/testdata/script/mod_get_fossil.txt @@ -18,11 +18,10 @@ env GOSUMDB=off env USER=fossiluser env FOSSIL_HOME=$WORK/home -# Attempting to get the latest version of a fossil repo. +# Attempt to get the latest version of a fossil repo. go get vcs-test.golang.org/fossil/hello.fossil ! stderr 'unexpected response from fossil info' grep 'vcs-test.golang.org/fossil/hello.fossil' go.mod -exists $GOPATH/bin/hello.fossil$GOEXE -- go.mod -- module x diff --git a/src/cmd/go/testdata/script/mod_get_go_file.txt b/src/cmd/go/testdata/script/mod_get_go_file.txt index 0c7b5dc11c581f..c81e491b947ecb 100644 --- a/src/cmd/go/testdata/script/mod_get_go_file.txt +++ b/src/cmd/go/testdata/script/mod_get_go_file.txt @@ -17,7 +17,7 @@ env GO111MODULE=on # argument has .go suffix, is a file and exists ! go get test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get test_missing.go @@ -25,7 +25,7 @@ stderr 'arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get test/test_missing.go @@ -35,19 +35,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory @@ -58,6 +58,11 @@ mkdir test/test_dir.go rm test/test_dir.go +-- go.mod -- +module m + +go 1.18 + -- test.go -- package main func main() {println("test")} diff --git a/src/cmd/go/testdata/script/mod_get_incompatible.txt b/src/cmd/go/testdata/script/mod_get_incompatible.txt index 8000ee61481a01..5a7d70637180af 100644 --- a/src/cmd/go/testdata/script/mod_get_incompatible.txt +++ b/src/cmd/go/testdata/script/mod_get_incompatible.txt @@ -1,15 +1,15 @@ env GO111MODULE=on -go get -d x +go get x go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' cp go.mod2 go.mod -go get -d rsc.io/breaker@7307b30 +go get rsc.io/breaker@7307b30 go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' -go get -d rsc.io/breaker@v2.0.0 +go get rsc.io/breaker@v2.0.0 go list -m all stdout 'rsc.io/breaker v2.0.0\+incompatible' diff --git a/src/cmd/go/testdata/script/mod_get_indirect.txt b/src/cmd/go/testdata/script/mod_get_indirect.txt index e1cc1ab4115be4..fa7edf22d7b997 100644 --- a/src/cmd/go/testdata/script/mod_get_indirect.txt +++ b/src/cmd/go/testdata/script/mod_get_indirect.txt @@ -27,7 +27,7 @@ grep 'golang.org/x/text v0.3.0 // indirect$' go.mod # indirect tag should be removed upon seeing direct import. cp $WORK/tmp/uselang.go x.go -go get -d +go get grep 'rsc.io/quote v1.5.2$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod diff --git a/src/cmd/go/testdata/script/mod_get_insecure_redirect.txt b/src/cmd/go/testdata/script/mod_get_insecure_redirect.txt deleted file mode 100644 index 2e128344955407..00000000000000 --- a/src/cmd/go/testdata/script/mod_get_insecure_redirect.txt +++ /dev/null @@ -1,32 +0,0 @@ -# golang.org/issue/29591: 'go get' was following plain-HTTP redirects even without -insecure (now replaced by GOINSECURE). - -[!net] skip -[!exec:git] skip - -env GO111MODULE=on -env GOPROXY=direct -env GOSUMDB=off - -! go get -d vcs-test.golang.org/insecure/go/insecure -stderr 'redirected .* to insecure URL' - -# insecure host -env GOINSECURE=vcs-test.golang.org -go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure - -# insecure glob host -env GOINSECURE=*.golang.org -go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure - -# insecure multiple host -env GOINSECURE=somewhere-else.com,*.golang.org -go clean -modcache -go get -d vcs-test.golang.org/insecure/go/insecure - -# different insecure host does not fetch -env GOINSECURE=somewhere-else.com -go clean -modcache -! go get -d vcs-test.golang.org/insecure/go/insecure -stderr 'redirected .* to insecure URL' diff --git a/src/cmd/go/testdata/script/mod_get_issue37438.txt b/src/cmd/go/testdata/script/mod_get_issue37438.txt index 38b2031d3ae4a0..9392e73a174bd6 100644 --- a/src/cmd/go/testdata/script/mod_get_issue37438.txt +++ b/src/cmd/go/testdata/script/mod_get_issue37438.txt @@ -6,7 +6,7 @@ # 'go get foo@requested' should resolve the requested version, # not error out on the (unrelated) latest one. -go get -d example.net/a/p@v0.2.0 +go get example.net/a/p@v0.2.0 -- go.mod -- module example diff --git a/src/cmd/go/testdata/script/mod_get_issue47979.txt b/src/cmd/go/testdata/script/mod_get_issue47979.txt new file mode 100644 index 00000000000000..848ee3aa09a9ad --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_issue47979.txt @@ -0,0 +1,117 @@ +# Regression test for https://golang.org/issue/47979: +# +# An argument to 'go get' that results in an upgrade to a different existing +# root should be allowed, and should not panic the 'go' command. + +cp go.mod go.mod.orig + + +# Transitive upgrades from upgraded roots should not prevent +# 'go get -u' from performing upgrades. + +cp go.mod.orig go.mod +go get -u . +cmp go.mod go.mod.want + + +# 'go get' of a specific version should allow upgrades of +# every dependency (transitively) required by that version, +# including dependencies that are pulled into the module +# graph by upgrading other root requirements +# (in this case, example.net/indirect). + +cp go.mod.orig go.mod +go get example.net/a@v0.2.0 +cmp go.mod go.mod.want + + +-- go.mod -- +module golang.org/issue47979 + +go 1.17 + +replace ( + example.net/a v0.1.0 => ./a1 + example.net/a v0.2.0 => ./a2 + example.net/indirect v0.1.0 => ./indirect1 + example.net/indirect v0.2.0 => ./indirect2 + example.net/other v0.1.0 => ./other + example.net/other v0.2.0 => ./other +) + +require ( + example.net/a v0.1.0 + example.net/other v0.1.0 +) + +require example.net/indirect v0.1.0 // indirect +-- go.mod.want -- +module golang.org/issue47979 + +go 1.17 + +replace ( + example.net/a v0.1.0 => ./a1 + example.net/a v0.2.0 => ./a2 + example.net/indirect v0.1.0 => ./indirect1 + example.net/indirect v0.2.0 => ./indirect2 + example.net/other v0.1.0 => ./other + example.net/other v0.2.0 => ./other +) + +require ( + example.net/a v0.2.0 + example.net/other v0.2.0 +) + +require example.net/indirect v0.2.0 // indirect +-- issue.go -- +package issue + +import _ "example.net/a" +-- useother/useother.go -- +package useother + +import _ "example.net/other" +-- a1/go.mod -- +module example.net/a + +go 1.17 + +require example.net/indirect v0.1.0 +-- a1/a.go -- +package a +-- a2/go.mod -- +module example.net/a + +go 1.17 + +require example.net/indirect v0.2.0 +-- a2/a.go -- +package a + +import "example.net/indirect" +-- indirect1/go.mod -- +module example.net/indirect + +go 1.17 + +require example.net/other v0.1.0 +-- indirect1/indirect.go -- +package indirect +-- indirect2/go.mod -- +module example.net/indirect + +go 1.17 + +require example.net/other v0.2.0 +-- indirect2/indirect.go -- +package indirect + +import "example.net/other" +-- other/go.mod -- +module example.net/other + +go 1.17 +-- other/other.go -- +package other diff --git a/src/cmd/go/testdata/script/mod_get_issue48511.txt b/src/cmd/go/testdata/script/mod_get_issue48511.txt new file mode 100644 index 00000000000000..0ba486d35bd6ce --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_issue48511.txt @@ -0,0 +1,68 @@ +# Regression test for https://golang.org/issue/48511: +# requirement minimization was accidentally replacing previous +# versions of the main module, causing dependencies to be +# spuriously dropping during requirement minimization and +# leading to an infinite loop. + +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig + +go get -u=patch ./... +cmp go.mod go.mod.want + +-- go.mod -- +module example.net/m + +go 1.16 + +replace ( + example.net/a v0.1.0 => ./a + example.net/b v0.1.0 => ./b + example.net/b v0.1.1 => ./b + example.net/m v0.1.0 => ./m1 +) + +require example.net/a v0.1.0 +-- go.mod.want -- +module example.net/m + +go 1.16 + +replace ( + example.net/a v0.1.0 => ./a + example.net/b v0.1.0 => ./b + example.net/b v0.1.1 => ./b + example.net/m v0.1.0 => ./m1 +) + +require ( + example.net/a v0.1.0 + example.net/b v0.1.1 // indirect +) +-- m.go -- +package m + +import "example.net/a" +-- m1/go.mod -- +module example.net/m + +go 1.16 + +require example.net/b v0.1.0 +-- a/go.mod -- +module example.net/a + +go 1.16 + +require example.net/m v0.1.0 +-- a/a.go -- +package a + +import "example.net/b" +-- b/go.mod -- +module example.net/b + +go 1.16 +-- b/b.go -- +package b diff --git a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt index 241a0c2f0dfa8e..00da0c316469f9 100644 --- a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt +++ b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt @@ -5,6 +5,6 @@ env GO111MODULE=on go mod init m -go get -d example.com/notags +go get example.com/notags go list -m all stdout '^example.com/notags v0.0.0-20190507143103-cc8cbe209b64$' diff --git a/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt b/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt new file mode 100644 index 00000000000000..3dae383de1e57b --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt @@ -0,0 +1,68 @@ +# Check that 'go get -u' will upgrade a dependency (direct or indirect) +# when the main module and the dependency are both lazy. +# Verifies #47768. + +# Check that go.mod is tidy, and an upgrade is available. +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig + +go list -m -u example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.0 \[v0.1.1\] => ./lazyupgrade@v0.1.0$' + +# 'go get -u' on a package that directly imports the dependency should upgrade. +go get -u ./usedirect +go list -m example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$' +cp go.mod.orig go.mod + +# 'go get -u' on a package that indirectly imports the dependency should upgrade. +go get -u ./useindirect +go list -m example.com/lazyupgrade +stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$' + +-- go.mod -- +module use + +go 1.17 + +require ( + direct v0.0.0 + example.com/lazyupgrade v0.1.0 +) + +replace ( + direct => ./direct + example.com/lazyupgrade v0.1.0 => ./lazyupgrade@v0.1.0 + example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1 +) +-- usedirect/usedirect.go -- +package use + +import _ "example.com/lazyupgrade" +-- useindirect/useindirect.go -- +package use + +import _ "direct" +-- direct/go.mod -- +module direct + +go 1.17 + +require example.com/lazyupgrade v0.1.0 +-- direct/direct.go -- +package direct + +import _ "example.com/lazyupgrade" +-- lazyupgrade@v0.1.0/go.mod -- +module example.com/lazyupgrade + +go 1.17 +-- lazyupgrade@v0.1.0/lazyupgrade.go -- +package lazyupgrade +-- lazyupgrade@v0.1.1/go.mod -- +module example.com/lazyupgrade + +go 1.17 +-- lazyupgrade@v0.1.1/lazyupgrade.go -- +package lazyupgrade diff --git a/src/cmd/go/testdata/script/mod_get_local.txt b/src/cmd/go/testdata/script/mod_get_local.txt index eb09da58b3e234..4c81d16a1fe072 100644 --- a/src/cmd/go/testdata/script/mod_get_local.txt +++ b/src/cmd/go/testdata/script/mod_get_local.txt @@ -7,7 +7,7 @@ cp go.mod go.mod.orig # 'go get -u' within the main module should work, even if it has a local-only name. cp go.mod.orig go.mod -go get -d -u ./... +go get -u ./... grep 'rsc.io/quote.*v1.5.2' go.mod grep 'golang.org/x/text.*v0.3.0' go.mod cp go.mod go.mod.implicitmod @@ -15,34 +15,34 @@ cp go.mod go.mod.implicitmod # 'go get -u local/...' should be equivalent to 'go get -u ./...' # (assuming no nested modules) cp go.mod.orig go.mod -go get -d -u local/... +go get -u local/... cmp go.mod go.mod.implicitmod # For the main module, @patch should be a no-op. cp go.mod.orig go.mod -go get -d -u local/...@patch +go get -u local/...@patch cmp go.mod go.mod.implicitmod -# 'go get -u -d' in the empty root of the main module should fail. -# 'go get -u -d .' should also fail. +# 'go get -u' in the empty root of the main module should fail. +# 'go get -u .' should also fail. cp go.mod.orig go.mod -! go get -u -d -! go get -u -d . +! go get -u +! go get -u . -# 'go get -u -d .' within a package in the main module updates the dependencies +# 'go get -u .' within a package in the main module updates the dependencies # of that package. cp go.mod.orig go.mod cd uselang -go get -u -d . +go get -u . cd .. grep 'rsc.io/quote.*v1.3.0' go.mod grep 'golang.org/x/text.*v0.3.0' go.mod cp go.mod go.mod.dotpkg -# 'go get -u -d' with an explicit package in the main module updates the +# 'go get -u' with an explicit package in the main module updates the # dependencies of that package. cp go.mod.orig go.mod -go get -u -d local/uselang +go get -u local/uselang cmp go.mod go.mod.dotpkg -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_main.txt b/src/cmd/go/testdata/script/mod_get_main.txt index 50b2fee9ae1bfd..cddd5f70826eb4 100644 --- a/src/cmd/go/testdata/script/mod_get_main.txt +++ b/src/cmd/go/testdata/script/mod_get_main.txt @@ -2,44 +2,44 @@ env GO111MODULE=on cp go.mod.orig go.mod # relative and absolute paths must be within the main module. -! go get -d .. -stderr '^go get: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d $WORK -stderr '^go get: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d ../... -stderr '^go get: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' -! go get -d $WORK/... -stderr '^go get: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get .. +stderr '^go: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get $WORK +stderr '^go: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get ../... +stderr '^go: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +! go get $WORK/... +stderr '^go: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' # @patch and @latest within the main module refer to the current version. # The main module won't be upgraded, but missing dependencies will be added. -go get -d rsc.io/x +go get rsc.io/x grep 'rsc.io/quote v1.5.2' go.mod -go get -d rsc.io/x@upgrade +go get rsc.io/x@upgrade grep 'rsc.io/quote v1.5.2' go.mod cp go.mod.orig go.mod -go get -d rsc.io/x@patch +go get rsc.io/x@patch grep 'rsc.io/quote v1.5.2' go.mod cp go.mod.orig go.mod # Upgrading a package pattern not contained in the main module should not # attempt to upgrade the main module. -go get -d rsc.io/quote/...@v1.5.1 +go get rsc.io/quote/...@v1.5.1 grep 'rsc.io/quote v1.5.1' go.mod # The main module cannot be updated to a specific version. -! go get -d rsc.io@v0.1.0 -stderr '^go get: can''t request version "v0.1.0" of the main module \(rsc.io\)$' +! go get rsc.io@v0.1.0 +stderr '^go: can''t request version "v0.1.0" of the main module \(rsc.io\)$' # A package in the main module can't be upgraded either. -! go get -d rsc.io/x@v0.1.0 -stderr '^go get: package rsc.io/x is in the main module, so can''t request version v0.1.0$' +! go get rsc.io/x@v0.1.0 +stderr '^go: package rsc.io/x is in the main module, so can''t request version v0.1.0$' # Nor can a pattern matching packages in the main module. -! go get -d rsc.io/x/...@latest -stderr '^go get: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' +! go get rsc.io/x/...@latest +stderr '^go: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' -- go.mod.orig -- module rsc.io diff --git a/src/cmd/go/testdata/script/mod_get_major.txt b/src/cmd/go/testdata/script/mod_get_major.txt index 367ede9ded45ba..2db13180bd7978 100644 --- a/src/cmd/go/testdata/script/mod_get_major.txt +++ b/src/cmd/go/testdata/script/mod_get_major.txt @@ -8,12 +8,12 @@ env GOSUMDB=off # golang.org/issue/34383: if a module path ends in a major-version suffix, # ensure that 'direct' mode can resolve the package to a module. -go get -d vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0 +go get vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0 go list -m vcs-test.golang.org/git/v3pkg.git/v3 stdout '^vcs-test.golang.org/git/v3pkg.git/v3 v3.0.0$' -go get -d vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0 +go get vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0 go list -m vcs-test.golang.org/git/empty-v2-without-v1.git/v2 stdout '^vcs-test.golang.org/git/empty-v2-without-v1.git/v2 v2.0.0$' diff --git a/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt b/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt index 789d42d24dbca7..5934251e4b8b74 100644 --- a/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt +++ b/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt @@ -13,7 +13,7 @@ env GOSUMDB=off cp go.sum.bug go.sum ! go build -n use stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$' -go get -d use +go get use cmp go.sum go.sum.tidy go build -n use @@ -22,7 +22,7 @@ cp go.sum.bug go.sum rm $WORK/gopath/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.ziphash ! go build -n use stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$' -go get -d use +go get use cmp go.sum go.sum.tidy go build -n use diff --git a/src/cmd/go/testdata/script/mod_get_moved.txt b/src/cmd/go/testdata/script/mod_get_moved.txt index 8430a737c40ca0..ba79c8263c06da 100644 --- a/src/cmd/go/testdata/script/mod_get_moved.txt +++ b/src/cmd/go/testdata/script/mod_get_moved.txt @@ -4,17 +4,17 @@ env GO111MODULE=on # A 'go get' that worked at a previous version should continue to work at that version, # even if the package was subsequently moved into a submodule. go mod init example.com/foo -go get -d example.com/split/subpkg@v1.0.0 +go get example.com/split/subpkg@v1.0.0 go list -m all stdout 'example.com/split v1.0.0' # A 'go get' that simultaneously upgrades away conflicting package defitions is not ambiguous. -go get -d example.com/split/subpkg@v1.1.0 +go get example.com/split/subpkg@v1.1.0 # A 'go get' without an upgrade should find the package. rm go.mod go mod init example.com/foo -go get -d example.com/split/subpkg +go get example.com/split/subpkg go list -m all stdout 'example.com/split/subpkg v1.1.0' @@ -23,18 +23,18 @@ stdout 'example.com/split/subpkg v1.1.0' # even if the package was subsequently moved into a parent module. rm go.mod go mod init example.com/foo -go get -d example.com/join/subpkg@v1.0.0 +go get example.com/join/subpkg@v1.0.0 go list -m all stdout 'example.com/join/subpkg v1.0.0' # A 'go get' that simultaneously upgrades away conflicting package definitions is not ambiguous. # (A wildcard pattern applies to both packages and modules, # because we define wildcard matching to apply after version resolution.) -go get -d example.com/join/subpkg/...@v1.1.0 +go get example.com/join/subpkg/...@v1.1.0 # A 'go get' without an upgrade should find the package. rm go.mod go mod init example.com/foo -go get -d example.com/join/subpkg@v1.1.0 +go get example.com/join/subpkg@v1.1.0 go list -m all stdout 'example.com/join v1.1.0' diff --git a/src/cmd/go/testdata/script/mod_get_newcycle.txt b/src/cmd/go/testdata/script/mod_get_newcycle.txt index f71620c1bcb41c..18dc6503617a56 100644 --- a/src/cmd/go/testdata/script/mod_get_newcycle.txt +++ b/src/cmd/go/testdata/script/mod_get_newcycle.txt @@ -11,4 +11,4 @@ go mod init m cmp stderr stderr-expected -- stderr-expected -- -go get: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 +go: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 diff --git a/src/cmd/go/testdata/script/mod_get_none.txt b/src/cmd/go/testdata/script/mod_get_none.txt index b358f05af36745..5aec209f59fe6f 100644 --- a/src/cmd/go/testdata/script/mod_get_none.txt +++ b/src/cmd/go/testdata/script/mod_get_none.txt @@ -3,10 +3,10 @@ env GO111MODULE=on go mod init example.com/foo # 'go get bar@none' should be a no-op if module bar is not active. -go get -d example.com/bar@none +go get example.com/bar@none go list -m all ! stdout example.com/bar -go get -d example.com/bar@none +go get example.com/bar@none go list -m all ! stdout example.com/bar diff --git a/src/cmd/go/testdata/script/mod_get_nopkgs.txt b/src/cmd/go/testdata/script/mod_get_nopkgs.txt index 078e71a041c470..14176a7dc8786d 100644 --- a/src/cmd/go/testdata/script/mod_get_nopkgs.txt +++ b/src/cmd/go/testdata/script/mod_get_nopkgs.txt @@ -6,17 +6,17 @@ cd subdir go get ./... stderr -count=1 'matched no packages' -go get -d ./... +go get ./... stderr -count=1 'matched no packages' # 'go get' on patterns that could conceivably match nested modules # should report a module resolution error. -go get -d example.net/emptysubdir/... # control case +go get example.net/emptysubdir/... # control case -! go get -d example.net/emptysubdir/subdir/... +! go get example.net/emptysubdir/subdir/... ! stderr 'matched no packages' -stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' +stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' # It doesn't make sense to 'go get' a path in the standard library, # since the standard library necessarily can't have unresolved imports. @@ -26,8 +26,8 @@ stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/empt # For that case, we emit a "malformed module path" error message, # which isn't ideal either. -! go get -d builtin/... # in GOROOT/src, but contains no packages -stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$' +! go get builtin/... # in GOROOT/src, but contains no packages +stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$' -- go.mod -- module example.net/emptysubdir diff --git a/src/cmd/go/testdata/script/mod_get_patch.txt b/src/cmd/go/testdata/script/mod_get_patch.txt index 053ef621471ecc..35cc276c5c3683 100644 --- a/src/cmd/go/testdata/script/mod_get_patch.txt +++ b/src/cmd/go/testdata/script/mod_get_patch.txt @@ -7,8 +7,8 @@ cp go.mod go.mod.orig # example.net/b@patch refers to the patch for the version of b that was selected # at the start of 'go get', not the version after applying other changes. -! go get -d example.net/a@v0.2.0 example.net/b@patch -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +! go get example.net/a@v0.2.0 example.net/b@patch +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -18,8 +18,8 @@ cmp go.mod go.mod.orig # # TODO(#42360): Reconsider the change in defaults. -! go get -d -u=patch example.net/a@v0.2.0 example.net/b -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +! go get -u=patch example.net/a@v0.2.0 example.net/b +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -27,7 +27,7 @@ cmp go.mod go.mod.orig # applying other version changes, not the versions that were selected at the start. # However, it should not patch versions determined by explicit arguments. -go get -d -u=patch example.net/a@v0.2.0 +go get -u=patch example.net/a@v0.2.0 go list -m all stdout '^example.net/a v0.2.0 ' stdout '^example.net/b v0.2.1 ' @@ -38,7 +38,7 @@ stdout '^example.net/b v0.2.1 ' cp go.mod.orig go.mod ! go get -u=patch all -stderr '^go get: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' +stderr '^go: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' cmp go.mod go.mod.orig diff --git a/src/cmd/go/testdata/script/mod_get_patchbound.txt b/src/cmd/go/testdata/script/mod_get_patchbound.txt index 4fd1ec53e16045..e4d3c491f45611 100644 --- a/src/cmd/go/testdata/script/mod_get_patchbound.txt +++ b/src/cmd/go/testdata/script/mod_get_patchbound.txt @@ -5,7 +5,7 @@ go list -m all stdout '^example.net/a v0.1.0 ' stdout '^example.net/b v0.1.0 ' -go get -d -u=patch example.net/a@v0.2.0 +go get -u=patch example.net/a@v0.2.0 go list -m all stdout '^example.net/a v0.2.0 ' stdout '^example.net/b v0.1.1 ' # not v0.1.2, which requires …/a v0.3.0. diff --git a/src/cmd/go/testdata/script/mod_get_patchcycle.txt b/src/cmd/go/testdata/script/mod_get_patchcycle.txt index d1db56f935ca2e..6600109d2dad9b 100644 --- a/src/cmd/go/testdata/script/mod_get_patchcycle.txt +++ b/src/cmd/go/testdata/script/mod_get_patchcycle.txt @@ -6,7 +6,7 @@ # (It used to print v0.1.1 but then silently upgrade to v0.2.0.) ! go get example.net/a@patch -stderr '^go get: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. +stderr '^go: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. -- go.mod -- module example diff --git a/src/cmd/go/testdata/script/mod_get_patchmod.txt b/src/cmd/go/testdata/script/mod_get_patchmod.txt index e39d13a0f4184e..28277310aa15e5 100644 --- a/src/cmd/go/testdata/script/mod_get_patchmod.txt +++ b/src/cmd/go/testdata/script/mod_get_patchmod.txt @@ -1,5 +1,5 @@ # example.net/pkgremoved@v0.1.0 refers to a package. -go get -d example.net/pkgremoved@v0.1.0 +go get example.net/pkgremoved@v0.1.0 go list example.net/pkgremoved stdout '^example.net/pkgremoved' @@ -15,8 +15,8 @@ cp go.mod go.mod.orig # be constrained to the latest patch of its originally-selected version (v0.1.0), # not upgraded to the latest patch of the new transitive dependency. -! go get -d example.net/pkgremoved@patch example.net/other@v0.1.0 -stderr '^go get: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' +! go get example.net/pkgremoved@patch example.net/other@v0.1.0 +stderr '^go: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' cmp go.mod.orig go.mod @@ -24,19 +24,19 @@ cmp go.mod.orig go.mod # Package to module ... -go get -d example.net/pkgremoved@v0.3.0 +go get example.net/pkgremoved@v0.3.0 go list example.net/pkgremoved stdout 'example.net/pkgremoved' -go get -d example.net/pkgremoved@patch +go get example.net/pkgremoved@patch ! go list example.net/pkgremoved # ... and module to package. -go get -d example.net/pkgremoved@v0.4.0 +go get example.net/pkgremoved@v0.4.0 ! go list example.net/pkgremoved -go get -d example.net/pkgremoved@patch +go get example.net/pkgremoved@patch go list example.net/pkgremoved stdout 'example.net/pkgremoved' diff --git a/src/cmd/go/testdata/script/mod_get_patterns.txt b/src/cmd/go/testdata/script/mod_get_patterns.txt index aee4374dc8a88a..891353fd4b9a0b 100644 --- a/src/cmd/go/testdata/script/mod_get_patterns.txt +++ b/src/cmd/go/testdata/script/mod_get_patterns.txt @@ -5,29 +5,29 @@ env GO111MODULE=on # in the build list, we assume the pattern matches a single module # whose path is a prefix of the part of the pattern before "...". cp go.mod.orig go.mod -go get -d rsc.io/quote/... +go get rsc.io/quote/... grep 'require rsc.io/quote' go.mod cp go.mod.orig go.mod -! go get -d rsc.io/quote/x... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' +! go get rsc.io/quote/x... +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' ! grep 'require rsc.io/quote' go.mod -! go get -d rsc.io/quote/x/... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' +! go get rsc.io/quote/x/... +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' ! grep 'require rsc.io/quote' go.mod # If a pattern matches no packages within a module, the module should not # be upgraded, even if the module path is a prefix of the pattern. cp go.mod.orig go.mod go mod edit -require example.com/nest@v1.0.0 -go get -d example.com/nest/sub/y... +go get example.com/nest/sub/y... grep 'example.com/nest/sub v1.0.0' go.mod grep 'example.com/nest v1.0.0' go.mod # However, if the pattern matches the module path itself, the module # should be upgraded even if it contains no matching packages. -go get -d example.com/n...t +go get example.com/n...t grep 'example.com/nest v1.1.0' go.mod grep 'example.com/nest/sub v1.0.0' go.mod diff --git a/src/cmd/go/testdata/script/mod_get_pkgtags.txt b/src/cmd/go/testdata/script/mod_get_pkgtags.txt index 0c79ec71b7b41d..7ad3c3c7c44a4c 100644 --- a/src/cmd/go/testdata/script/mod_get_pkgtags.txt +++ b/src/cmd/go/testdata/script/mod_get_pkgtags.txt @@ -12,10 +12,10 @@ stderr '^module example\.net/cmd provides package example\.net/cmd/tool and is r go mod edit -droprequire example.net/tools -# 'go get -d' makes a best effort to fetch those dependencies, but shouldn't +# 'go get' makes a best effort to fetch those dependencies, but shouldn't # error out if dependencies of tag-guarded files are missing. -go get -d example.net/tools@v0.1.0 +go get example.net/tools@v0.1.0 ! stderr 'no Go source files' ! go list example.net/tools @@ -48,27 +48,27 @@ stderr '^package example.net/tools: build constraints exclude all Go files in .* # 'go get' should fetch modules whose roots contain test-only packages, but # without the -t flag shouldn't error out if the test has missing dependencies. -go get -d example.net/testonly@v0.1.0 +go get example.net/testonly@v0.1.0 # With the -t flag, the test dependencies must resolve successfully. -! go get -d -t example.net/testonly@v0.1.0 +! go get -t example.net/testonly@v0.1.0 stderr '^example.net/testonly tested by\n\texample.net/testonly\.test imports\n\texample.net/missing: cannot find module providing package example.net/missing$' -# 'go get -d' should succeed for a module path that does not contain a package, +# 'go get' should succeed for a module path that does not contain a package, # but fail for a non-package subdirectory of a module. -! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +! go get example.net/missing/subdir@v0.1.0 +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' -go get -d example.net/missing@v0.1.0 +go get example.net/missing@v0.1.0 # Getting the subdirectory should continue to fail even if the corresponding # module is already present in the build list. -! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +! go get example.net/missing/subdir@v0.1.0 +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt b/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt index be3db42d1d9c1f..06e2fc68602918 100644 --- a/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt +++ b/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt @@ -8,7 +8,7 @@ cmp go.mod.orig go.mod grep '^example.com/incompatiblewithsub v2\.0\.0\+incompatible' go.sum ! grep '^example.com/incompatiblewithsub v1.0.0' go.sum -go get -d example.com/incompatiblewithsub/sub +go get example.com/incompatiblewithsub/sub cmp go.mod.orig go.mod ! grep '^example.com/incompatiblewithsub v1.0.0' go.sum diff --git a/src/cmd/go/testdata/script/mod_get_private_vcs.txt b/src/cmd/go/testdata/script/mod_get_private_vcs.txt deleted file mode 100644 index 75c776a7fa29c7..00000000000000 --- a/src/cmd/go/testdata/script/mod_get_private_vcs.txt +++ /dev/null @@ -1,43 +0,0 @@ -env GO111MODULE=on - -# Testing stderr for git ls-remote; turn off proxy. -[!net] skip -[!exec:git] skip -env GOPROXY=direct - -! go get github.com/golang/nonexist -stderr 'Confirm the import path was entered correctly.' -stderr 'If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.' -! stdout . - -# Fetching a nonexistent commit should return an "unknown revision" -# error message. -! go get github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b -stderr '^go get: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' -! stdout . - -! go get github.com/golang/nonexist@master -stderr '^Confirm the import path was entered correctly.$' -stderr '^If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.$' -! stderr 'unknown revision' -! stdout . - -[!linux] stop # Needs XDG_CONFIG_HOME. -[!exec:false] stop - -# Test that Git clone errors will be shown to the user instead of a generic -# "unknown revision" error. To do this we want to force git ls-remote to return -# an error we don't already have special handling for. See golang/go#42751. -# -# Set XDG_CONFIG_HOME to tell Git where to look for the git config file listed -# below, which turns on ssh. -env XDG_CONFIG_HOME=$TMPDIR -env GIT_SSH_COMMAND=false -! go install github.com/golang/nonexist@master -stderr 'fatal: Could not read from remote repository.' -! stderr 'unknown revision' -! stdout . - --- $TMPDIR/git/config -- -[url "git@github.com:"] - insteadOf = https://github.com/ diff --git a/src/cmd/go/testdata/script/mod_get_promote_implicit.txt b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt index 9eec2013210350..03f6810e354a0a 100644 --- a/src/cmd/go/testdata/script/mod_get_promote_implicit.txt +++ b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt @@ -12,7 +12,7 @@ stderr '^package m/use-indirect imports indirect-with-pkg from implicitly requir # NOTE: the hint recommends getting the imported package (tested below) since # it's more obvious and doesn't require -d. However, that adds an '// indirect' # comment on the requirement. -go get -d m/use-indirect +go get m/use-indirect cmp go.mod go.mod.use cp go.mod.orig go.mod @@ -21,7 +21,7 @@ cp go.mod.orig go.mod # know they're needed by the main module. See #43131 for the rationale. # The hint above recommends this because it's more obvious usage and doesn't # require the -d flag. -go get -d indirect-with-pkg indirect-without-pkg +go get indirect-with-pkg indirect-without-pkg cmp go.mod go.mod.indirect -- go.mod.orig -- diff --git a/src/cmd/go/testdata/script/mod_get_pseudo.txt b/src/cmd/go/testdata/script/mod_get_pseudo.txt index 582837a1665e28..b964ae448453b9 100644 --- a/src/cmd/go/testdata/script/mod_get_pseudo.txt +++ b/src/cmd/go/testdata/script/mod_get_pseudo.txt @@ -9,69 +9,69 @@ env GOSUMDB=off # We can resolve the @master branch without unshallowing the local repository # (even with older gits), so try that before we do anything else. # (This replicates https://golang.org/issue/26713 with git 2.7.4.) -go get -d github.com/rsc/legacytest@master +go get github.com/rsc/legacytest@master go list -m all stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' # get should include incompatible tags in "latest" calculation. go mod edit -droprequire github.com/rsc/legacytest -go get -d github.com/rsc/legacytest@latest +go get github.com/rsc/legacytest@latest go list go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.1-0.pseudo+incompatible -go get -d ...test@7303f77 +go get ...test@7303f77 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$' # v2.0.0+incompatible by tag+incompatible -go get -d ...test@v2.0.0+incompatible +go get ...test@v2.0.0+incompatible go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.0+incompatible by tag -go get -d ...test@v2.0.0 +go get ...test@v2.0.0 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v2.0.0+incompatible by hash (back on master) -go get -d ...test@d7ae1e4 +go get ...test@d7ae1e4 go list -m all stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$' # v1.2.1-0.pseudo -go get -d ...test@d2d4c3e +go get ...test@d2d4c3e go list -m all stdout '^github.com/rsc/legacytest v1\.2\.1-0\.\d{14}-d2d4c3ea6623$' # v1.2.0 -go get -d ...test@9f6f860 +go get ...test@9f6f860 go list -m all stdout '^github.com/rsc/legacytest v1\.2\.0$' # v1.1.0-pre.0.pseudo -go get -d ...test@fb3c628 +go get ...test@fb3c628 go list -m all stdout '^github.com/rsc/legacytest v1\.1\.0-pre\.0\.\d{14}-fb3c628075e3$' # v1.1.0-pre (no longer on master) -go get -d ...test@731e3b1 +go get ...test@731e3b1 go list -m all stdout '^github.com/rsc/legacytest v1\.1\.0-pre$' # v1.0.1-0.pseudo -go get -d ...test@fa4f5d6 +go get ...test@fa4f5d6 go list -m all stdout '^github.com/rsc/legacytest v1\.0\.1-0\.\d{14}-fa4f5d6a71c6$' # v1.0.0 -go get -d ...test@7fff7f3 +go get ...test@7fff7f3 go list -m all stdout '^github.com/rsc/legacytest v1\.0\.0$' # v0.0.0-pseudo -go get -d ...test@52853eb +go get ...test@52853eb go list -m all stdout '^github.com/rsc/legacytest v0\.0\.0-\d{14}-52853eb7b552$' diff --git a/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt b/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt index 0fbd041f8603fd..d085f4fa3c8e6b 100644 --- a/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt +++ b/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt @@ -17,7 +17,7 @@ env GOSUMDB=off # The pseudo-version hence sorts immediately after v0.2.2 rather # than v0.2.1, even though the v0.2.2 tag is not on master. -go get -d vcs-test.golang.org/git/tagtests.git@master +go get vcs-test.golang.org/git/tagtests.git@master go list -m all stdout '^vcs-test.golang.org/git/tagtests.git v0.2.3-0\.' diff --git a/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt b/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt index b78e6e644f7622..8e6cd907f18349 100644 --- a/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt +++ b/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt @@ -16,11 +16,11 @@ env GOSUMDB=off # # The pseudo-version is based on sub/v0.0.10, since v0.2.0 doesn't # contain the prefix. -go get -d vcs-test.golang.org/git/prefixtagtests.git/sub +go get vcs-test.golang.org/git/prefixtagtests.git/sub go list -m all stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.10$' -go get -d -u vcs-test.golang.org/git/prefixtagtests.git/sub@master +go get -u vcs-test.golang.org/git/prefixtagtests.git/sub@master go list -m all stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.11-0\.' diff --git a/src/cmd/go/testdata/script/mod_get_replaced.txt b/src/cmd/go/testdata/script/mod_get_replaced.txt index d97f3f1a401172..b1fc8b80563307 100644 --- a/src/cmd/go/testdata/script/mod_get_replaced.txt +++ b/src/cmd/go/testdata/script/mod_get_replaced.txt @@ -6,7 +6,7 @@ env oldGOPROXY=$GOPROXY # 'go get' should resolve it to the minimum valid pseudo-version. go mod edit -replace=example.com/x=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' @@ -15,11 +15,11 @@ stdout '^example.com/x v0.0.0-00010101000000-000000000000 ' go mod edit -replace=example.com/x@v0.1.0=./x go mod edit -replace=example.com/x@v0.2.0=./x -go get -d example.com/x +go get example.com/x go list -m example.com/x stdout '^example.com/x v0.2.0 ' -go get -d example.com/x@v1.3.1 +go get rsc.io/quote@>v1.3.1 go list -m rsc.io/quote stdout '^rsc.io/quote v1.4.0' @@ -81,15 +81,15 @@ cp go.mod.orig go.mod ! go list example stderr '^package example is not in GOROOT \(.*\)$' -! go get -d example -stderr '^go get: malformed module path "example": missing dot in first path element$' +! go get example +stderr '^go: malformed module path "example": missing dot in first path element$' go mod edit -replace example@v0.1.0=./example ! go list example stderr '^module example provides package example and is replaced but not required; to add it:\n\tgo get example@v0.1.0$' -go get -d example +go get example go list -m example stdout '^example v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_retract.txt b/src/cmd/go/testdata/script/mod_get_retract.txt index 560fa7bfb256ff..9757989666728e 100644 --- a/src/cmd/go/testdata/script/mod_get_retract.txt +++ b/src/cmd/go/testdata/script/mod_get_retract.txt @@ -1,7 +1,7 @@ # 'go get pkg' should not upgrade to a retracted version. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.1.0 -go get -d example.com/retract/self/prev +go get example.com/retract/self/prev go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' @@ -9,7 +9,7 @@ stdout '^example.com/retract/self/prev v1.1.0$' # version is available. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d example.com/retract/self/prev +go get example.com/retract/self/prev stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' stderr '^go: to switch to the latest unretracted version, run:\n\tgo get example.com/retract/self/prev@latest\n$' go list -m example.com/retract/self/prev @@ -18,14 +18,14 @@ stdout '^example.com/retract/self/prev v1.9.0$' # 'go get pkg@latest' should downgrade from a retracted version. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d example.com/retract/self/prev@latest +go get example.com/retract/self/prev@latest go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' # 'go get pkg@version' should update to a specific version, even if that # version is retracted. cp go.mod.orig go.mod -go get -d example.com/retract@v1.0.0-bad +go get example.com/retract@v1.0.0-bad stderr '^go: warning: example.com/retract@v1.0.0-bad: retracted by module author: bad$' go list -m example.com/retract stdout '^example.com/retract v1.0.0-bad$' @@ -34,16 +34,16 @@ stdout '^example.com/retract v1.0.0-bad$' # version is available. cp go.mod.orig go.mod go mod edit -require example.com/retract/self/prev@v1.9.0 -go get -d -u ./use +go get -u ./use stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.9.0$' # 'go get' should warn if a module needed to build named packages is retracted. # 'go get' should not warn about unrelated modules. -go get -d ./empty +go get ./empty ! stderr retracted -go get -d ./use +go get ./use stderr '^go: warning: example.com/retract/self/prev@v1.9.0: retracted by module author: self$' -- go.mod.orig -- diff --git a/src/cmd/go/testdata/script/mod_get_retract_ambiguous.txt b/src/cmd/go/testdata/script/mod_get_retract_ambiguous.txt index b49ba54982b643..4b4f5da03cdba6 100644 --- a/src/cmd/go/testdata/script/mod_get_retract_ambiguous.txt +++ b/src/cmd/go/testdata/script/mod_get_retract_ambiguous.txt @@ -1,4 +1,4 @@ -! go get -d example.com/retract/ambiguous/other +! go get example.com/retract/ambiguous/other stderr 'ambiguous import: found package example.com/retract/ambiguous/nested in multiple modules:' stderr '^go: warning: example.com/retract/ambiguous/nested@v1.9.0-bad: retracted by module author: nested modules are bad$' diff --git a/src/cmd/go/testdata/script/mod_get_split.txt b/src/cmd/go/testdata/script/mod_get_split.txt index f4e7661f9b2a03..0fb22c85d3943e 100644 --- a/src/cmd/go/testdata/script/mod_get_split.txt +++ b/src/cmd/go/testdata/script/mod_get_split.txt @@ -4,7 +4,7 @@ cp go.mod go.mod.orig # 'go get' on a package already provided by the build list should update # the module already in the build list, not fail with an ambiguous import error. -go get -d example.net/split/nested@patch +go get example.net/split/nested@patch go list -m all stdout '^example.net/split v0.2.1 ' ! stdout '^example.net/split/nested' @@ -13,7 +13,7 @@ stdout '^example.net/split v0.2.1 ' cp go.mod.orig go.mod -go get -d example.net/split/nested/...@patch +go get example.net/split/nested/...@patch go list -m all stdout '^example.net/split v0.2.1 ' ! stdout '^example.net/split/nested' @@ -32,7 +32,7 @@ stdout '^example.net/split v0.2.1 ' cp go.mod.orig go.mod -! go get -d example.net/split/nested@v0.1.0 +! go get example.net/split/nested@v0.1.0 stderr '^example.net/split/nested: ambiguous import: found package example.net/split/nested in multiple modules:\n\texample.net/split v0.2.0 \(.*split.2[/\\]nested\)\n\texample.net/split/nested v0.1.0 \(.*nested.1\)$' # A wildcard that matches packages in some module at its selected version @@ -54,22 +54,22 @@ stderr '^example.net/split/nested: ambiguous import: found package example.net/s # # TODO(#27899): Should we instead upgrade or downgrade to an arbirary version? -! go get -d example.net/split/nested/...@v0.1.0 -stderr '^go get: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' +! go get example.net/split/nested/...@v0.1.0 +stderr '^go: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' cmp go.mod go.mod.orig # If another argument resolves the ambiguity, we should be ok again. -go get -d example.net/split@none example.net/split/nested@v0.1.0 +go get example.net/split@none example.net/split/nested@v0.1.0 go list -m all ! stdout '^example.net/split ' stdout '^example.net/split/nested v0.1.0 ' cp go.mod.orig go.mod -go get -d example.net/split@v0.3.0 example.net/split/nested@v0.1.0 +go get example.net/split@v0.3.0 example.net/split/nested@v0.1.0 go list -m all stdout '^example.net/split v0.3.0 ' stdout '^example.net/split/nested v0.1.0 ' @@ -80,14 +80,14 @@ stdout '^example.net/split/nested v0.1.0 ' # to match the pattern if possible. cp go.mod.orig go.mod -go get -d example.net/split/nested@v0.0.0 +go get example.net/split/nested@v0.0.0 -go get -d example.net/...@v0.1.0 +go get example.net/...@v0.1.0 go list -m all stdout '^example.net/split v0.1.0 ' stdout '^example.net/split/nested v0.1.0 ' -go get -d example.net/... +go get example.net/... go list -m all stdout '^example.net/split v0.3.0 ' stdout '^example.net/split/nested v0.2.0 ' @@ -96,15 +96,15 @@ stdout '^example.net/split/nested v0.2.0 ' # @none applies to all matching module paths, # regardless of whether they contain any packages. -go get -d example.net/...@none +go get example.net/...@none go list -m all ! stdout '^example.net' # Starting from no dependencies, a wildcard can resolve to an empty module with # the same prefix even if it contains no packages. -go get -d example.net/...@none -go get -d example.net/split/...@v0.1.0 +go get example.net/...@none +go get example.net/split/...@v0.1.0 go list -m all stdout '^example.net/split v0.1.0 ' diff --git a/src/cmd/go/testdata/script/mod_get_sum_noroot.txt b/src/cmd/go/testdata/script/mod_get_sum_noroot.txt index 4f1cf032777038..0d9a840e779cc4 100644 --- a/src/cmd/go/testdata/script/mod_get_sum_noroot.txt +++ b/src/cmd/go/testdata/script/mod_get_sum_noroot.txt @@ -2,7 +2,7 @@ # it should add sums for the module's go.mod file and its content to go.sum. # Verifies golang.org/issue/41103. go mod init m -go get -d rsc.io/QUOTE +go get rsc.io/QUOTE grep '^rsc.io/QUOTE v1.5.2/go.mod ' go.sum grep '^rsc.io/QUOTE v1.5.2 ' go.sum diff --git a/src/cmd/go/testdata/script/mod_get_svn.txt b/src/cmd/go/testdata/script/mod_get_svn.txt deleted file mode 100644 index 3817fce9b61f87..00000000000000 --- a/src/cmd/go/testdata/script/mod_get_svn.txt +++ /dev/null @@ -1,36 +0,0 @@ -[!net] skip -[!exec:svn] skip - -# 'go get' will fall back to svn+ssh once svn fails over protocols like https. -# If vcs-test.golang.org isn't in the user's known_hosts file, this will result -# in an ssh prompt, which will stop 'go test' entirely -# -# Unfortunately, there isn't a way to globally disable host checking for ssh, -# without modifying the real system's or user's configs. Changing $HOME won't -# affect ssh either, as it ignores the environment variable entirely. -# -# However, a useful trick is pointing SVN_SSH to a program that doesn't exist, -# resulting in svn skipping ssh entirely. Alternatives like -# SVN_SSH="ssh -o StrictHostKeyChecking=no" didn't avoid the prompt. -env SVN_SSH="svn_do_not_use_ssh" - -env GO111MODULE=on -env GOPROXY=direct -env GOSUMDB=off - -# Attempting to get a module zip using svn should succeed. -go get vcs-test.golang.org/svn/hello.svn@000000000001 -exists $GOPATH/pkg/mod/cache/download/vcs-test.golang.org/svn/hello.svn/@v/v0.0.0-20170922011245-000000000001.zip -exists $GOPATH/bin/hello.svn$GOEXE - -# Attempting to get a nonexistent module using svn should fail with a -# reasonable message instead of a panic. -! go get -d vcs-test.golang.org/svn/nonexistent.svn -! stderr panic -stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"' - --- go.mod -- -module golang/go/issues/28943/main --- go.sum -- -vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001 h1:rZjvboXMfQICKXdhx/QHqJ2Y/AQsJVrXnwGqwcTxQiw= -vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001/go.mod h1:0memnh/BRLuxiK2zF4rvUgz6ts/fhhB28l3ULFWPusc= diff --git a/src/cmd/go/testdata/script/mod_get_tags.txt b/src/cmd/go/testdata/script/mod_get_tags.txt index e9869e3f0230b3..e4fb6c4326b46f 100644 --- a/src/cmd/go/testdata/script/mod_get_tags.txt +++ b/src/cmd/go/testdata/script/mod_get_tags.txt @@ -1,25 +1,14 @@ env GO111MODULE=on -[short] skip - # get should add modules needed to build packages, even if those # dependencies are in sources excluded by build tags. # All build tags are considered true except "ignore". go mod init m -go get -d . +go get . go list -m all stdout 'example.com/version v1.1.0' stdout 'rsc.io/quote v1.5.2' -[short] skip - -# Packages that are only imported in excluded files should not be built. -env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache. -go get -n -x . -stderr 'compile.* -p m ' -! stderr 'compile.* -p example.com/version ' -! stderr 'compile.* -p rsc.io/quote ' - -- empty.go -- package m diff --git a/src/cmd/go/testdata/script/mod_get_test.txt b/src/cmd/go/testdata/script/mod_get_test.txt index 23722bd4e4dcd7..0fa7cd98b98fb2 100644 --- a/src/cmd/go/testdata/script/mod_get_test.txt +++ b/src/cmd/go/testdata/script/mod_get_test.txt @@ -2,38 +2,38 @@ env GO111MODULE=on # By default, 'go get' should ignore tests cp go.mod.empty go.mod -go get -d m/a +go get m/a ! grep rsc.io/quote go.mod # 'go get -t' should consider test dependencies of the named package. cp go.mod.empty go.mod -go get -d -t m/a +go get -t m/a grep 'rsc.io/quote v1.5.2$' go.mod # 'go get -t' should not consider test dependencies of imported packages, # including packages imported from tests. cp go.mod.empty go.mod -go get -d -t m/b +go get -t m/b ! grep rsc.io/quote go.mod # 'go get -t -u' should update test dependencies of the named package. cp go.mod.empty go.mod go mod edit -require=rsc.io/quote@v1.5.1 -go get -d -t -u m/a +go get -t -u m/a grep 'rsc.io/quote v1.5.2$' go.mod # 'go get -t -u' should not add or update test dependencies # of imported packages, including packages imported from tests. cp go.mod.empty go.mod -go get -d -t -u m/b +go get -t -u m/b ! grep rsc.io/quote go.mod go mod edit -require=rsc.io/quote@v1.5.1 -go get -d -t -u m/b +go get -t -u m/b grep 'rsc.io/quote v1.5.1$' go.mod # 'go get all' should consider test dependencies with or without -t. cp go.mod.empty go.mod -go get -d all +go get all grep 'rsc.io/quote v1.5.2$' go.mod -- go.mod.empty -- diff --git a/src/cmd/go/testdata/script/mod_get_too_many_redirects.txt b/src/cmd/go/testdata/script/mod_get_too_many_redirects.txt deleted file mode 100644 index 9cbe0d279dea30..00000000000000 --- a/src/cmd/go/testdata/script/mod_get_too_many_redirects.txt +++ /dev/null @@ -1,10 +0,0 @@ -env GO111MODULE=on -env GOPROXYBASE=$GOPROXY -env GOPROXY=$GOPROXYBASE/redirect/11 -env GOSUMDB=off - -! go get -d rsc.io/quote@v1.2.0 -stderr 'stopped after 10 redirects' - -env GOPROXY=$GOPROXYBASE/redirect/9 -go get -d rsc.io/quote@v1.2.0 diff --git a/src/cmd/go/testdata/script/mod_get_trailing_slash.txt b/src/cmd/go/testdata/script/mod_get_trailing_slash.txt index c53669353794ad..7b469008baffd4 100644 --- a/src/cmd/go/testdata/script/mod_get_trailing_slash.txt +++ b/src/cmd/go/testdata/script/mod_get_trailing_slash.txt @@ -7,16 +7,9 @@ stdout ^example.com/dotgo.go$ go list example.com/dotgo.go/ stdout ^example.com/dotgo.go$ -# go get -d should succeed in either case, with or without a version. +# go get should succeed in either case, with or without a version. # Arguments are interpreted as packages or package patterns with versions, # not source files. -go get -d example.com/dotgo.go -go get -d example.com/dotgo.go/ -go get -d example.com/dotgo.go@v1.0.0 -go get -d example.com/dotgo.go/@v1.0.0 - -# go get (without -d) should also succeed in either case. -[short] skip go get example.com/dotgo.go go get example.com/dotgo.go/ go get example.com/dotgo.go@v1.0.0 diff --git a/src/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt b/src/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt index 0093c0eda0c827..a5651e934181a7 100644 --- a/src/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt +++ b/src/cmd/go/testdata/script/mod_get_update_unrelated_sum.txt @@ -18,7 +18,7 @@ cmp go.sum.orig go.sum # Upgrade a module. This also upgrades rsc.io/quote, and though we didn't load # a package from it, we had the sum for its old version, so we need the # sum for the new version, too. -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 grep '^rsc.io/quote v1.5.2 ' go.sum # The upgrade still breaks the build because the new version of quote imports @@ -34,7 +34,7 @@ cp go.sum.orig go.sum # We didn't need a sum for it before (even though we had one), so we won't # fetch a new sum. go mod edit -replace rsc.io/quote@v1.0.0=./dummy -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum cp go.mod.orig go.mod cp go.sum.orig go.sum @@ -43,7 +43,7 @@ cp go.sum.orig go.sum # Replace the new version with a directory before upgrading. # We can't get a sum for a directory. go mod edit -replace rsc.io/quote@v1.5.2=./dummy -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum cp go.mod.orig go.mod cp go.sum.orig go.sum @@ -52,7 +52,7 @@ cp go.sum.orig go.sum # Replace the new version with a different version. # We should get a sum for that version. go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1 -go get -d example.com/upgrade@v0.0.2 +go get example.com/upgrade@v0.0.2 ! grep '^rsc.io/quote v1.5.2 ' go.sum grep '^rsc.io/quote v1.5.1 ' go.sum cp go.mod.orig go.mod @@ -63,7 +63,7 @@ cp go.sum.orig go.sum # 'go get' should fail when fetching the zip. rm $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip env GOPROXY=off -! go get -d example.com/upgrade@v0.0.2 +! go get example.com/upgrade@v0.0.2 stderr '^go: upgraded rsc.io/quote v1.0.0 => v1.5.2: error finding sum for rsc.io/quote@v1.5.2: module lookup disabled by GOPROXY=off$' -- go.mod.orig -- diff --git a/src/cmd/go/testdata/script/mod_get_upgrade.txt b/src/cmd/go/testdata/script/mod_get_upgrade.txt index eeb6d6f6af6f41..51d5990ee179c7 100644 --- a/src/cmd/go/testdata/script/mod_get_upgrade.txt +++ b/src/cmd/go/testdata/script/mod_get_upgrade.txt @@ -1,35 +1,35 @@ env GO111MODULE=on -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go list -m all stdout 'rsc.io/quote v1.5.1' grep 'rsc.io/quote v1.5.1$' go.mod # get -u should update dependencies of the package in the current directory -go get -d -u +go get -u grep 'rsc.io/quote v1.5.2$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod # get -u rsc.io/sampler should update only sampler's dependencies cp go.mod-v1.5.1 go.mod -go get -d -u rsc.io/sampler +go get -u rsc.io/sampler grep 'rsc.io/quote v1.5.1$' go.mod grep 'golang.org/x/text [v0-9a-f\.-]+ // indirect' go.mod # move to a pseudo-version after any tags -go get -d rsc.io/quote@dd9747d +go get rsc.io/quote@dd9747d grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod # get -u should not jump off newer pseudo-version to earlier tag -go get -d -u +go get -u grep 'rsc.io/quote v0.0.0-20180628003336-dd9747d19b04' go.mod # move to earlier pseudo-version -go get -d rsc.io/quote@e7a685a342 +go get rsc.io/quote@e7a685a342 grep 'rsc.io/quote v0.0.0-20180214005133-e7a685a342c0' go.mod # get -u should jump off earlier pseudo-version to newer tag -go get -d -u +go get -u grep 'rsc.io/quote v1.5.2' go.mod -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt b/src/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt index f5f415aa3fae25..deff9358f060f4 100644 --- a/src/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt +++ b/src/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt @@ -10,52 +10,52 @@ env GO111MODULE=on # The v0.0.0 pseudo-version is chronologically newer. # Start at v0.1.1-0.20190429073117-b5426c86b553 -go get -d example.com/pseudoupgrade@b5426c8 +go get example.com/pseudoupgrade@b5426c8 go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get -u' should not downgrade to the (lower) tagged version. -go get -d -u +go get -u go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade@upgrade' should not downgrade. -go get -d example.com/pseudoupgrade@upgrade +go get example.com/pseudoupgrade@upgrade go list -m all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade' should not downgrade. # This is equivalent to 'get example.com/pseudoupgrade@upgrade'. -go get -d example.com/pseudoupgrade +go get example.com/pseudoupgrade go list -m all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' # 'get example.com/pseudoupgrade@latest' should downgrade. # @latest should not consider the current version. -go get -d example.com/pseudoupgrade@latest +go get example.com/pseudoupgrade@latest go list -m all stdout '^example.com/pseudoupgrade v0.1.0$' # We should observe the same behavior with the newer pseudo-version. -go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 +go get example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 # 'get -u' should not downgrade to the chronologically older tagged version. -go get -d -u +go get -u go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade@upgrade should not downgrade. -go get -d example.com/pseudoupgrade@upgrade +go get example.com/pseudoupgrade@upgrade go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade' should not downgrade. -go get -d example.com/pseudoupgrade +go get example.com/pseudoupgrade go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' # 'get example.com/pseudoupgrade@latest' should downgrade. -go get -d example.com/pseudoupgrade@latest +go get example.com/pseudoupgrade@latest go list -m -u all stdout '^example.com/pseudoupgrade v0.1.0$' diff --git a/src/cmd/go/testdata/script/mod_get_wild.txt b/src/cmd/go/testdata/script/mod_get_wild.txt index 78c645c6b9f3fe..06f9973e431932 100644 --- a/src/cmd/go/testdata/script/mod_get_wild.txt +++ b/src/cmd/go/testdata/script/mod_get_wild.txt @@ -11,15 +11,15 @@ stdout '^example.net/a v0.1.0 ' # already in the build list, and the wildcard in the first element prevents us # from attempting to resolve a new module whose path is a prefix of the pattern. -! go get -d -u=patch example.../b@upgrade -stderr '^go get: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' +! go get -u=patch example.../b@upgrade +stderr '^go: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' # Patching . causes a patch to example.net/a, which introduces a new match # for example.net/b/..., which is itself patched and causes another upgrade to # example.net/a, which is then patched again. -go get -d -u=patch . example.../b@upgrade +go get -u=patch . example.../b@upgrade go list -m all stdout '^example.net/a v0.2.1 ' # upgraded by dependency of b and -u=patch stdout '^example.net/b v0.2.0 ' # introduced by patch of a and upgraded by wildcard diff --git a/src/cmd/go/testdata/script/mod_getmode_vendor.txt b/src/cmd/go/testdata/script/mod_getmode_vendor.txt index d3df2078b07283..aaf526b2ab54be 100644 --- a/src/cmd/go/testdata/script/mod_getmode_vendor.txt +++ b/src/cmd/go/testdata/script/mod_getmode_vendor.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -go get -d rsc.io/quote@v1.5.1 +go get rsc.io/quote@v1.5.1 go mod vendor env GOPATH=$WORK/empty env GOPROXY=file:///nonexist @@ -11,20 +11,21 @@ stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$' stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$' ! go list -mod=vendor -m rsc.io/quote@latest -stderr 'go list -m: rsc.io/quote@latest: cannot query module due to -mod=vendor' +stderr 'go: rsc.io/quote@latest: cannot query module due to -mod=vendor' ! go get -mod=vendor -u stderr 'flag provided but not defined: -mod' # Since we don't have a complete module graph, 'go list -m' queries # that require the complete graph should fail with a useful error. ! go list -mod=vendor -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -mod=vendor -m ... -stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' -- go.mod -- module x +go 1.16 -- x.go -- package x import _ "rsc.io/quote" diff --git a/src/cmd/go/testdata/script/mod_getx.txt b/src/cmd/go/testdata/script/mod_getx.txt index ccb8d1375aa7dd..b3d06c17c85afa 100644 --- a/src/cmd/go/testdata/script/mod_getx.txt +++ b/src/cmd/go/testdata/script/mod_getx.txt @@ -8,7 +8,12 @@ env GOSUMDB=off # 'go get -x' should log URLs with an HTTP or HTTPS scheme. # A bug had caused us to log schemeless URLs instead. -go get -x -d golang.org/x/text@v0.1.0 +go get -x golang.org/x/text@v0.1.0 stderr '^# get https://golang.org/x/text\?go-get=1$' stderr '^# get https://golang.org/x/text\?go-get=1: 200 OK \([0-9.]+s\)$' ! stderr '^# get //.*' + +-- go.mod -- +module m + +go 1.18 diff --git a/src/cmd/go/testdata/script/mod_go_version_missing.txt b/src/cmd/go/testdata/script/mod_go_version_missing.txt index d704816729b8c3..2159a1e4c0ee5e 100644 --- a/src/cmd/go/testdata/script/mod_go_version_missing.txt +++ b/src/cmd/go/testdata/script/mod_go_version_missing.txt @@ -27,7 +27,7 @@ cmp go.mod go.mod.orig ! go list -mod=vendor all ! stderr '^go: inconsistent vendoring' -stderr 'cannot find package "\." in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$' +stderr 'cannot find package "vendor/example.com/badedit" in:\n\t.*[/\\]vendor[/\\]example.com[/\\]badedit$' # When we set -mod=mod, the go version should be updated immediately, # to the current version, converting the requirements from eager to lazy. diff --git a/src/cmd/go/testdata/script/mod_gomodcache.txt b/src/cmd/go/testdata/script/mod_gomodcache.txt index 74a3c79622f1f5..bafa5876241ece 100644 --- a/src/cmd/go/testdata/script/mod_gomodcache.txt +++ b/src/cmd/go/testdata/script/mod_gomodcache.txt @@ -5,7 +5,7 @@ env GO111MODULE=on env GOMODCACHE=$WORK/modcache go env GOMODCACHE stdout $WORK[/\\]modcache -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 exists $WORK/modcache/cache/download/rsc.io/quote/@v/v1.0.0.info grep '{"Version":"v1.0.0","Time":"2018-02-14T00:45:20Z"}' $WORK/modcache/cache/download/rsc.io/quote/@v/v1.0.0.info @@ -18,7 +18,7 @@ exists $WORK/modcache/cache/download/sumdb env GOMODCACHE= go env GOMODCACHE stdout $GOPATH[/\\]pkg[/\\]mod -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.0.0.info grep '{"Version":"v1.0.0","Time":"2018-02-14T00:45:20Z"}' $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.0.0.info @@ -31,11 +31,18 @@ env GOPATH= go env GOMODCACHE stdout $HOME[/\\]go[/\\]pkg[/\\]mod -# If GOMODCACHE isn't set and GOPATH starts with the path list separator, it's an error. +# If GOMODCACHE isn't set and GOPATH starts with the path list separator, +# GOMODCACHE is empty and any command that needs it errors out. env GOMODCACHE= env GOPATH=${:}$WORK/this/is/ignored -! go env GOMODCACHE -stderr 'missing \$GOPATH' + +go env GOMODCACHE +stdout '^$' +! stdout . +! stderr . + +! go mod download rsc.io/quote@v1.0.0 +stderr '^go: module cache not found: neither GOMODCACHE nor GOPATH is set$' # If GOMODCACHE isn't set and GOPATH has multiple elements only the first is used. env GOMODCACHE= diff --git a/src/cmd/go/testdata/script/mod_gonoproxy.txt b/src/cmd/go/testdata/script/mod_gonoproxy.txt index 204786969f5b25..d42d668f679eea 100644 --- a/src/cmd/go/testdata/script/mod_gonoproxy.txt +++ b/src/cmd/go/testdata/script/mod_gonoproxy.txt @@ -7,16 +7,16 @@ env dbname=localhost.localdev/sumdb # disagree with sumdb fails cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-wrong -! go get -d rsc.io/quote +! go get rsc.io/quote stderr 'SECURITY ERROR' # GONOSUMDB bypasses sumdb, for rsc.io/quote, rsc.io/sampler, golang.org/x/text env GONOSUMDB='*/quote,*/*mple*,golang.org/x' -go get -d rsc.io/quote +go get rsc.io/quote rm go.sum env GOPRIVATE='*/quote,*/*mple*,golang.org/x' env GONOPROXY=none # that is, proxy all despite GOPRIVATE -go get -d rsc.io/quote +go get rsc.io/quote # Download .info files needed for 'go list -m all' later. # TODO(#42723): either 'go list -m' should not read these files, @@ -26,27 +26,27 @@ stdout '^golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c$' # When GOPROXY is not empty but contains no entries, an error should be reported. env GOPROXY=',' -! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' +! go get golang.org/x/text +stderr '^go: golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' # When GOPROXY=off, fetching modules not matched by GONOPROXY fails. env GONOPROXY=*/fortune env GOPROXY=off -! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$' +! go get golang.org/x/text +stderr '^go: golang.org/x/text: module lookup disabled by GOPROXY=off$' # GONOPROXY bypasses proxy [!net] skip [!exec:git] skip env GOPRIVATE=none env GONOPROXY='*/fortune' -! go get -d rsc.io/fortune # does not exist in real world, only on test proxy +! go get rsc.io/fortune # does not exist in real world, only on test proxy stderr 'git ls-remote' env GOSUMDB= env GONOPROXY= env GOPRIVATE='*/x' -go get -d golang.org/x/text +go get golang.org/x/text go list -m all ! stdout 'text.*v0.0.0-2017' # should not have the version from the proxy diff --git a/src/cmd/go/testdata/script/mod_gopkg_unstable.txt b/src/cmd/go/testdata/script/mod_gopkg_unstable.txt index 5ad9106378f333..58bbc7651b3b53 100644 --- a/src/cmd/go/testdata/script/mod_gopkg_unstable.txt +++ b/src/cmd/go/testdata/script/mod_gopkg_unstable.txt @@ -1,7 +1,7 @@ env GO111MODULE=on cp go.mod.empty go.mod -go get -d gopkg.in/dummy.v2-unstable +go get gopkg.in/dummy.v2-unstable cp x.go.txt x.go cp go.mod.empty go.mod @@ -10,9 +10,11 @@ go list [!net] skip [!exec:git] skip +skip # TODO(#54503): redirect gopkg.in requests to a local server and re-enable. + env GOPROXY=direct env GOSUMDB=off -go get -d gopkg.in/macaroon-bakery.v2-unstable/bakery +go get gopkg.in/macaroon-bakery.v2-unstable/bakery go list -m all stdout 'gopkg.in/macaroon-bakery.v2-unstable v2.0.0-[0-9]+-[0-9a-f]+$' diff --git a/src/cmd/go/testdata/script/mod_import.txt b/src/cmd/go/testdata/script/mod_import.txt index 28358b5b0c5b56..07714e92c720d1 100644 --- a/src/cmd/go/testdata/script/mod_import.txt +++ b/src/cmd/go/testdata/script/mod_import.txt @@ -1,7 +1,7 @@ env GO111MODULE=on # latest rsc.io/quote should be v1.5.2 not v1.5.3-pre1 -go get -d +go get go list -m all stdout 'rsc.io/quote v1.5.2' diff --git a/src/cmd/go/testdata/script/mod_import_v1suffix.txt b/src/cmd/go/testdata/script/mod_import_v1suffix.txt index a4294504661454..75b3374bca0016 100644 --- a/src/cmd/go/testdata/script/mod_import_v1suffix.txt +++ b/src/cmd/go/testdata/script/mod_import_v1suffix.txt @@ -1,6 +1,6 @@ env GO111MODULE=on -! go get -d example.com/invalidpath/v1 +! go get example.com/invalidpath/v1 ! go install . -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_in_testdata_dir.txt b/src/cmd/go/testdata/script/mod_in_testdata_dir.txt index 66f79faa6d9b8d..866f7841b9fbb1 100644 --- a/src/cmd/go/testdata/script/mod_in_testdata_dir.txt +++ b/src/cmd/go/testdata/script/mod_in_testdata_dir.txt @@ -9,7 +9,7 @@ cd $WORK/testdata go mod init testdata.tld/foo # Getting a package within that module should resolve its dependencies. -go get -d +go get grep 'rsc.io/quote' go.mod # Tidying the module should preserve those dependencies. diff --git a/src/cmd/go/testdata/script/mod_init_invalid_major.txt b/src/cmd/go/testdata/script/mod_init_invalid_major.txt new file mode 100644 index 00000000000000..ae93e70d6307ff --- /dev/null +++ b/src/cmd/go/testdata/script/mod_init_invalid_major.txt @@ -0,0 +1,82 @@ +env GO111MODULE=on +env GOFLAGS=-mod=mod + +! go mod init example.com/user/repo/v0 +stderr '(?s)^go: invalid module path "example.com/user/repo/v0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v02 +stderr '(?s)^go: invalid module path "example.com/user/repo/v02": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v023 +stderr '(?s)^go: invalid module path "example.com/user/repo/v023": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v23$' + +! go mod init example.com/user/repo/v1 +stderr '(?s)^go: invalid module path "example.com/user/repo/v1": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v2.0 +stderr '(?s)^go: invalid module path "example.com/user/repo/v2.0": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v2.1.4 +stderr '(?s)^go: invalid module path "example.com/user/repo/v2.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v3.5 +stderr '(?s)^go: invalid module path "example.com/user/repo/v3.5": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v3$' + +! go mod init example.com/user/repo/v4.1.4 +stderr '(?s)^go: invalid module path "example.com/user/repo/v4.1.4": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v4$' + +! go mod init example.com/user/repo/v.2.3 +stderr '(?s)^go: invalid module path "example.com/user/repo/v.2.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v2$' + +! go mod init example.com/user/repo/v.5.3 +stderr '(?s)^go: invalid module path "example.com/user/repo/v.5.3": major version suffixes must be in the form of /vN and are only allowed for v2 or later(.*)go mod init example.com/user/repo/v5$' + +! go mod init gopkg.in/pkg +stderr '(?s)^go: invalid module path "gopkg.in/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/pkg.v1$' + +! go mod init gopkg.in/user/pkg +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v0 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v0": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg/v2 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg/v2": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$' + +! go mod init gopkg.in/user/pkg.v +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v0.1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v0.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v.1 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.1": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v01 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v01": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v1$' + +! go mod init gopkg.in/user/pkg.v.2.3 +stderr '(?s)^go: invalid module path "gopkg.in/user/pkg.v.2.3": module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN(.*)go mod init gopkg.in/user/pkg.v2$' + +# module paths with a trailing dot are rejected as invalid import paths +! go mod init example.com/user/repo/v2. +stderr '(?s)^go: malformed module path "example.com/user/repo/v2.": trailing dot in path element$' + +! go mod init example.com/user/repo/v2.. +stderr '(?s)^go: malformed module path "example.com/user/repo/v2..": trailing dot in path element$' + +! go mod init gopkg.in/user/pkg.v.2. +stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2.": trailing dot in path element$' + +! go mod init gopkg.in/user/pkg.v.2.. +stderr '(?s)^go: malformed module path "gopkg.in/user/pkg.v.2..": trailing dot in path element$' + +# module paths with spaces are also rejected +! go mod init 'foo bar' +stderr '(?s)^go: malformed module path "foo bar": invalid char '' ''$' + +! go mod init 'foo bar baz' +stderr '(?s)^go: malformed module path "foo bar baz": invalid char '' ''$' diff --git a/src/cmd/go/testdata/script/mod_install_pkg_version.txt b/src/cmd/go/testdata/script/mod_install_pkg_version.txt index fd02392af1b293..e3f59fc15289d9 100644 --- a/src/cmd/go/testdata/script/mod_install_pkg_version.txt +++ b/src/cmd/go/testdata/script/mod_install_pkg_version.txt @@ -16,7 +16,7 @@ env GO111MODULE=auto cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go install example.com/cmd/a@latest cmp go.mod go.mod.orig exists $GOPATH/bin/a$GOEXE @@ -70,7 +70,7 @@ go mod edit -require=rsc.io/fortune@v1.0.0 stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$' ! go install -mod=readonly ../../pkg/mod/rsc.io/fortune@v1.0.0 stderr '^missing go\.sum entry for module providing package rsc\.io/fortune; to add:\n\tgo mod download rsc\.io/fortune$' -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 go install -mod=readonly $GOPATH/pkg/mod/rsc.io/fortune@v1.0.0 exists $GOPATH/bin/fortune$GOEXE cd .. @@ -81,15 +81,15 @@ env GO111MODULE=auto # 'go install pkg@version' reports errors for meta packages, std packages, # and directories. ! go install std@v1.0.0 -stderr '^go install: std@v1.0.0: argument must be a package path, not a meta-package$' +stderr '^go: std@v1.0.0: argument must be a package path, not a meta-package$' ! go install fmt@v1.0.0 -stderr '^go install: fmt@v1.0.0: argument must not be a package in the standard library$' +stderr '^go: fmt@v1.0.0: argument must not be a package in the standard library$' ! go install example.com//cmd/a@v1.0.0 -stderr '^go install: example.com//cmd/a@v1.0.0: argument must be a clean package path$' +stderr '^go: example.com//cmd/a@v1.0.0: argument must be a clean package path$' ! go install example.com/cmd/a@v1.0.0 ./x@v1.0.0 -stderr '^go install: ./x@v1.0.0: argument must be a package path, not a relative path$' +stderr '^go: ./x@v1.0.0: argument must be a package path, not a relative path$' ! go install example.com/cmd/a@v1.0.0 $GOPATH/src/x@v1.0.0 -stderr '^go install: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' +stderr '^go: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' ! go install example.com/cmd/a@v1.0.0 cmd/...@v1.0.0 stderr '^package cmd/go not provided by module example.com/cmd@v1.0.0$' @@ -106,7 +106,7 @@ stdout '^example.com/cmd v1.0.0$' env GO111MODULE=auto ! go install example.com/cmd/a@v1.0.0 example.com/cmd/b@latest -stderr '^go install: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' +stderr '^go: example.com/cmd/b@latest: all arguments must refer to packages in the same module at the same version \(@v1.0.0\)$' # 'go install pkg@version' should report an error if the arguments are in @@ -125,7 +125,7 @@ stderr '^package example.com/cmd/err is not a main package$' mkdir tmp cd tmp go mod init m -go get -d example.com/cmd@v1.0.0 +go get example.com/cmd@v1.0.0 ! go build example.com/cmd/... stderr 'err[/\\]err.go:3:9: undefined: DoesNotCompile$' cd .. @@ -137,7 +137,7 @@ rm $GOPATH/bin # If a wildcard matches no packages, we should see a warning. ! go install example.com/cmd/nomatch...@v1.0.0 -stderr '^go install: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' +stderr '^go: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' go install example.com/cmd/a@v1.0.0 example.com/cmd/nomatch...@v1.0.0 stderr '^go: warning: "example.com/cmd/nomatch\.\.\." matched no packages$' @@ -159,7 +159,7 @@ cmp stderr exclude-err # 'go install pkg@version' should report an error if the module requires a # higher version of itself. ! go install example.com/cmd/a@v1.0.0-newerself -stderr '^go install: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' +stderr '^go: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' # 'go install pkg@version' will only match a retracted version if it's @@ -192,12 +192,12 @@ package main func main() {} -- replace-err -- -go install: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go install: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/src/cmd/go/testdata/script/mod_install_versioned.txt b/src/cmd/go/testdata/script/mod_install_versioned.txt index c6bce418b4fa70..627a9a81b0bead 100644 --- a/src/cmd/go/testdata/script/mod_install_versioned.txt +++ b/src/cmd/go/testdata/script/mod_install_versioned.txt @@ -1,11 +1,11 @@ env GO111MODULE=on -go get -d rsc.io/fortune +go get rsc.io/fortune go list -f '{{.Target}}' rsc.io/fortune ! stdout fortune@v1 stdout 'fortune(\.exe)?$' -go get -d rsc.io/fortune/v2 +go get rsc.io/fortune/v2 go list -f '{{.Target}}' rsc.io/fortune/v2 ! stdout v2 stdout 'fortune(\.exe)?$' diff --git a/src/cmd/go/testdata/script/mod_internal.txt b/src/cmd/go/testdata/script/mod_internal.txt index 687269d18f6f33..787b21f379c793 100644 --- a/src/cmd/go/testdata/script/mod_internal.txt +++ b/src/cmd/go/testdata/script/mod_internal.txt @@ -3,34 +3,34 @@ env GO111MODULE=on # golang.org/x/internal should be importable from other golang.org/x modules. go mod edit -module=golang.org/x/anything -go get -d . +go get . # ...and their tests... go test stdout PASS # ...but that should not leak into other modules. -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' # Internal packages in the standard library should not leak into modules. -go get -d ./fromstd +go get ./fromstd ! go build ./fromstd stderr 'use of internal package internal/testenv not allowed' # Dependencies should be able to use their own internal modules... go mod edit -module=golang.org/notx -go get -d ./throughdep +go get ./throughdep # ... but other modules should not, even if they have transitive dependencies. -go get -d . +go get . ! go build . stderr 'use of internal package golang.org/x/.* not allowed' # And transitive dependencies still should not leak. -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' @@ -38,17 +38,17 @@ stderr 'use of internal package golang.org/x/.* not allowed' # Replacing an internal module should keep it internal to the same paths. go mod edit -module=golang.org/notx go mod edit -replace golang.org/x/internal=./replace/golang.org/notx/internal -go get -d ./throughdep +go get ./throughdep -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' go mod edit -replace golang.org/x/internal=./vendor/golang.org/x/internal -go get -d ./throughdep +go get ./throughdep -go get -d ./baddep +go get ./baddep ! go build ./baddep stderr golang.org[/\\]notx[/\\]useinternal stderr 'use of internal package golang.org/x/.* not allowed' diff --git a/src/cmd/go/testdata/script/mod_invalid_path.txt b/src/cmd/go/testdata/script/mod_invalid_path.txt index 333a3ffa35cba1..667b76e340a5c4 100644 --- a/src/cmd/go/testdata/script/mod_invalid_path.txt +++ b/src/cmd/go/testdata/script/mod_invalid_path.txt @@ -29,9 +29,9 @@ stdout '^example.com/dotname/.dot$' go list ./use stdout '^example.com/dotname/use$' ! go list -m example.com/dotname/.dot@latest -stderr '^go list -m: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' -go get -d example.com/dotname/.dot -go get -d example.com/dotname/use +stderr '^go: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' +go get example.com/dotname/.dot +go get example.com/dotname/use go mod tidy -- mod/go.mod -- diff --git a/src/cmd/go/testdata/script/mod_invalid_path_dotname.txt b/src/cmd/go/testdata/script/mod_invalid_path_dotname.txt index 85934332d145db..484c208f0f7567 100644 --- a/src/cmd/go/testdata/script/mod_invalid_path_dotname.txt +++ b/src/cmd/go/testdata/script/mod_invalid_path_dotname.txt @@ -3,19 +3,19 @@ # 'go get' works with no version query. cp go.mod.empty go.mod -go get -d example.com/dotname/.dot +go get example.com/dotname/.dot go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' # 'go get' works with a version query. cp go.mod.empty go.mod -go get -d example.com/dotname/.dot@latest +go get example.com/dotname/.dot@latest go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' # 'go get' works on an importing package. cp go.mod.empty go.mod -go get -d . +go get . go list -m example.com/dotname stdout '^example.com/dotname v1.0.0$' diff --git a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt index 51dbf936888769..dd59eb1fedac68 100644 --- a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt +++ b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt @@ -4,16 +4,16 @@ # 'go list' accepts package paths with pluses. cp go.mod.orig go.mod -go get -d example.net/cmd +go get example.net/cmd go list example.net/cmd/x++ # 'go list -m' rejects module paths with pluses. ! go list -versions -m 'example.net/bad++' -stderr '^go list -m: malformed module path "example.net/bad\+\+": invalid char ''\+''$' +stderr '^go: malformed module path "example.net/bad\+\+": invalid char ''\+''$' # 'go get' accepts package paths with pluses. cp go.mod.orig go.mod -go get -d example.net/cmd/x++ +go get example.net/cmd/x++ go list -m example.net/cmd stdout '^example.net/cmd v0.0.0-00010101000000-000000000000 => ./cmd$' diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt index 6846a792a5df58..8385b08d95f105 100644 --- a/src/cmd/go/testdata/script/mod_invalid_version.txt +++ b/src/cmd/go/testdata/script/mod_invalid_version.txt @@ -19,7 +19,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' @@ -30,14 +30,14 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' # However, arguments to 'go get' can name packages above the root. cp go.mod.orig go.mod -go get -d golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c +go get golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c go list -m golang.org/x/text/... stdout 'golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' ! stdout 'golang.org/x/text/unicode' @@ -47,7 +47,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v2.1.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' cd .. ! go list -m golang.org/x/text stderr $WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' @@ -57,27 +57,27 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0 cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text -stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' +stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)' # A pseudo-version with more than 12 digits of SHA-1 prefix is invalid. cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text -stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' +stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)' # A pseudo-version that does not match the commit timestamp is invalid. cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' @@ -87,7 +87,7 @@ stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-v go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.1.1-0.20190915032832-14c0d48ead0c => golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' @@ -97,7 +97,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' @@ -109,7 +109,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' @@ -120,7 +120,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' @@ -130,7 +130,7 @@ stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-v go mod edit -replace golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.0.0-0.20170915032832-14c0d48ead0c => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' @@ -141,10 +141,10 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.999999.0 go mod edit -replace golang.org/x/text@v1.999999.0=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c cd outside -! go get -d golang.org/x/text@upgrade +! go get golang.org/x/text@upgrade stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999999.0: reading golang.org/x/text/go.mod at revision v1.999999.0: unknown revision v1.999999.0' cd .. -go get -d golang.org/x/text@upgrade +go get golang.org/x/text@upgrade go list -m golang.org/x/text stdout 'golang.org/x/text v1.999999.0 => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' @@ -153,7 +153,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' @@ -163,7 +163,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' @@ -173,7 +173,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c+incompatible cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' @@ -194,10 +194,10 @@ cp go.mod.orig go.mod go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible cd outside ! go list -m github.com/pierrec/lz4 -stderr 'go list -m: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr '^go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$' cd .. ! go list -m github.com/pierrec/lz4 -stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr '^go: github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$' # A +incompatible pseudo-version is valid for a revision of the module # that lacks a go.mod file. @@ -214,15 +214,15 @@ stdout 'github.com/pierrec/lz4 v2.0.4-0.20180826165652-dbe9298ce099\+incompatibl # to the equivalent +incompatible version, not a pseudo-version with a different # major version. cp go.mod.orig go.mod -go get -d github.com/pierrec/lz4@v2.0.5 +go get github.com/pierrec/lz4@v2.0.5 go list -m github.com/pierrec/lz4 stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible' # 'go get' for a mismatched major version with a go.mod file should error out, # not resolve to a pseudo-version with a different major version. cp go.mod.orig go.mod -! go get -d github.com/pierrec/lz4@v2.0.8 -stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2' +! go get github.com/pierrec/lz4@v2.0.8 +stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$' # An invalid +incompatible suffix for a canonical version should error out, # not resolve to a pseudo-version. @@ -233,10 +233,10 @@ cp go.mod.orig go.mod go mod edit -require github.com/pierrec/lz4@v2.0.8+incompatible cd outside ! go list -m github.com/pierrec/lz4 -stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$' cd .. ! go list -m github.com/pierrec/lz4 -stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr '^go: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$' -- go.mod.orig -- module example.com diff --git a/src/cmd/go/testdata/script/mod_issue35317.txt b/src/cmd/go/testdata/script/mod_issue35317.txt index b1852ab0319ebb..92416a54e474e0 100644 --- a/src/cmd/go/testdata/script/mod_issue35317.txt +++ b/src/cmd/go/testdata/script/mod_issue35317.txt @@ -5,4 +5,4 @@ env GO111MODULE=on [short] skip go mod init example.com -go get -d golang.org/x/text@v0.3.0 golang.org/x/internal@v0.1.0 golang.org/x/exp@none +go get golang.org/x/text@v0.3.0 golang.org/x/internal@v0.1.0 golang.org/x/exp@none diff --git a/src/cmd/go/testdata/script/mod_lazy_downgrade.txt b/src/cmd/go/testdata/script/mod_lazy_downgrade.txt index 2f815fef22ff63..eb69d2eb8f81e1 100644 --- a/src/cmd/go/testdata/script/mod_lazy_downgrade.txt +++ b/src/cmd/go/testdata/script/mod_lazy_downgrade.txt @@ -27,7 +27,7 @@ stdout '^example.com/c v0.2.0 ' # Downgrading c should also downgrade the b that requires it. -go get -d example.com/c@v0.1.0 +go get example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.2.0 ' @@ -35,7 +35,7 @@ stdout '^example.com/c v0.1.0 ' # Removing c entirely should also remove the a and b that require it. -go get -d example.com/c@none +go get example.com/c@none go list -m all ! stdout '^example.com/a ' ! stdout '^example.com/b ' @@ -53,7 +53,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.3.0 ' stdout '^example.com/c v0.2.0 ' -go get -d example.com/c@v0.1.0 +go get example.com/c@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.2.0 ' @@ -63,7 +63,7 @@ stdout '^example.com/c v0.1.0 ' # is still tracked, and it will still be downgraded away if we remove c. # ('go get' never makes a root into a non-root. Only 'go mod tidy' does that.) -go get -d example.com/c@none +go get example.com/c@none go list -m all ! stdout '^example.com/a ' ! stdout '^example.com/b ' @@ -84,7 +84,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.3.0 ' stdout '^example.com/c v0.2.0 ' -go get -d example.com/c@v0.1.0 example.com/b@v0.1.0 +go get example.com/c@v0.1.0 example.com/b@v0.1.0 go list -m all stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.1.0 ' @@ -96,7 +96,7 @@ stdout '^example.com/a v0.1.0 ' stdout '^example.com/b v0.1.0 ' ! stdout '^example.com/c ' -go get -d example.com/c@none +go get example.com/c@none go list -m all stdout '^example.com/a v0.1.0' stdout '^example.com/b v0.1.0' diff --git a/src/cmd/go/testdata/script/mod_lazy_import_allmod.txt b/src/cmd/go/testdata/script/mod_lazy_import_allmod.txt index 97718c4513b55b..60d4187b1178ae 100644 --- a/src/cmd/go/testdata/script/mod_lazy_import_allmod.txt +++ b/src/cmd/go/testdata/script/mod_lazy_import_allmod.txt @@ -66,7 +66,7 @@ stdout '^b v0.1.0 ' ! stdout '^c ' # After adding a new direct import of b/y, -# the existing verison of b should be promoted to a root, +# the existing version of b should be promoted to a root, # bringing the version of c required by b into the build list. cp m.go.new m.go diff --git a/src/cmd/go/testdata/script/mod_lazy_new_import.txt b/src/cmd/go/testdata/script/mod_lazy_new_import.txt index 4272a52de1c267..520d8459cc11d4 100644 --- a/src/cmd/go/testdata/script/mod_lazy_new_import.txt +++ b/src/cmd/go/testdata/script/mod_lazy_new_import.txt @@ -7,7 +7,7 @@ # \ # ---- a/y (new) ---- c # -# Where a/x and x/y are disjoint packages, but both contained in module a. +# Where a/x and a/y are disjoint packages, but both contained in module a. # # The module dependency graph initially looks like: # diff --git a/src/cmd/go/testdata/script/mod_list.txt b/src/cmd/go/testdata/script/mod_list.txt index 239c7caa4a2246..06316cc335ed67 100644 --- a/src/cmd/go/testdata/script/mod_list.txt +++ b/src/cmd/go/testdata/script/mod_list.txt @@ -39,8 +39,8 @@ stdout '^module nonexist: not a known dependency$' stdout '^module rsc.io/quote/buggy: not a known dependency$' ! go list -m nonexist rsc.io/quote/buggy -stderr '^go list -m: module nonexist: not a known dependency' -stderr '^go list -m: module rsc.io/quote/buggy: not a known dependency' +stderr '^go: module nonexist: not a known dependency' +stderr '^go: module rsc.io/quote/buggy: not a known dependency' # Module loader does not interfere with list -e (golang.org/issue/24149). go list -e -f '{{.Error.Err}}' database diff --git a/src/cmd/go/testdata/script/mod_list_command_line_arguments.txt b/src/cmd/go/testdata/script/mod_list_command_line_arguments.txt new file mode 100644 index 00000000000000..fd99ae84b2e570 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_list_command_line_arguments.txt @@ -0,0 +1,35 @@ +# The command-line-arguments package does not belong to a module... +cd a +go list -f '{{.Module}}' ../b/b.go +stdout '^$' + +# ... even if the arguments are sources from that module +go list -f '{{.Module}}' a.go +stdout '^$' + +[short] skip + +# check that the version of command-line-arguments doesn't include a module +go build -o a.exe a.go +go version -m a.exe +stdout '^\tpath\tcommand-line-arguments$' +stdout '^\tdep\ta\t\(devel\)\t$' +! stdout mod + +-- a/go.mod -- +module a +go 1.17 +-- a/a.go -- +package main + +import "a/dep" + +func main() { + dep.D() +} +-- a/dep/dep.go -- +package dep + +func D() {} +-- b/b.go -- +package b \ No newline at end of file diff --git a/src/cmd/go/testdata/script/mod_list_compiled_concurrent.txt b/src/cmd/go/testdata/script/mod_list_compiled_concurrent.txt index b08713dcfd793f..896bbab9fccf37 100644 --- a/src/cmd/go/testdata/script/mod_list_compiled_concurrent.txt +++ b/src/cmd/go/testdata/script/mod_list_compiled_concurrent.txt @@ -1,6 +1,7 @@ env GO111MODULE=on [short] skip +[!cgo] skip # Regression test for golang.org/issue/29667: # spurious 'failed to cache compiled Go files' errors. diff --git a/src/cmd/go/testdata/script/mod_list_deprecated.txt b/src/cmd/go/testdata/script/mod_list_deprecated.txt index f0ecbba2cea13d..ee985cccbf3a91 100644 --- a/src/cmd/go/testdata/script/mod_list_deprecated.txt +++ b/src/cmd/go/testdata/script/mod_list_deprecated.txt @@ -20,7 +20,7 @@ stdout '^in example.com/deprecated/a@v1.9.0$' # This works even if we use an old version that does not have the deprecation # message in its go.mod file. -go get -d example.com/deprecated/a@v1.0.0 +go get example.com/deprecated/a@v1.0.0 ! grep Deprecated: $WORK/gopath/pkg/mod/cache/download/example.com/deprecated/a/@v/v1.0.0.mod go list -m -u -f {{.Deprecated}} example.com/deprecated/a stdout '^in example.com/deprecated/a@v1.9.0$' diff --git a/src/cmd/go/testdata/script/mod_list_dir.txt b/src/cmd/go/testdata/script/mod_list_dir.txt index 1adab8f027d737..157d3b6a8a6672 100644 --- a/src/cmd/go/testdata/script/mod_list_dir.txt +++ b/src/cmd/go/testdata/script/mod_list_dir.txt @@ -3,7 +3,7 @@ # go list with path to directory should work # populate go.sum -go get -d +go get env GO111MODULE=off go list -f '{{.ImportPath}}' $GOROOT/src/math @@ -20,11 +20,11 @@ go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 stdout '^rsc.io/quote$' go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 stdout '^rsc.io/sampler$' -go get -d rsc.io/sampler@v1.3.1 +go get rsc.io/sampler@v1.3.1 go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.1 stdout '^rsc.io/sampler$' ! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/sampler@v1.3.0 -stderr 'outside available modules' +stderr 'outside main module or its selected dependencies' -- go.mod -- module x diff --git a/src/cmd/go/testdata/script/mod_list_direct.txt b/src/cmd/go/testdata/script/mod_list_direct.txt index 62a472f475e7f8..3aa1881554edce 100644 --- a/src/cmd/go/testdata/script/mod_list_direct.txt +++ b/src/cmd/go/testdata/script/mod_list_direct.txt @@ -10,7 +10,7 @@ env GOSUMDB=off # For a while, (*modfetch.codeRepo).Stat was not checking for a go.mod file, # which would produce a hard error at the subsequent call to GoMod. -go get -d +go get -v -- go.mod -- module example.com diff --git a/src/cmd/go/testdata/script/mod_list_odd_tags.txt b/src/cmd/go/testdata/script/mod_list_odd_tags.txt new file mode 100644 index 00000000000000..c1f40cdf3a312c --- /dev/null +++ b/src/cmd/go/testdata/script/mod_list_odd_tags.txt @@ -0,0 +1,13 @@ +[short] skip +[!exec:git] skip +[!net] skip + +env GOPROXY=direct + +go list -m vcs-test.golang.org/git/odd-tags.git@latest +stdout -count=1 '^.' +stdout '^vcs-test.golang.org/git/odd-tags.git v0.1.1-0.20220223184835-9d863d525bbf$' + +go list -m -versions vcs-test.golang.org/git/odd-tags.git +stdout -count=1 '^.' +stdout '^vcs-test.golang.org/git/odd-tags.git$' # No versions listed — the odd tags are filtered out. diff --git a/src/cmd/go/testdata/script/mod_list_replace_dir.txt b/src/cmd/go/testdata/script/mod_list_replace_dir.txt index f2f2d2b2bb23c4..b446543916f53b 100644 --- a/src/cmd/go/testdata/script/mod_list_replace_dir.txt +++ b/src/cmd/go/testdata/script/mod_list_replace_dir.txt @@ -3,13 +3,13 @@ # Verifies golang.org/issue/29548 # Populate go.sum and download dependencies. -go get -d +go get # Ensure v1.5.2 is also in the cache so we can list it. go mod download rsc.io/quote@v1.5.2 ! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2 -stderr '^directory ..[/\\]pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2 outside available modules$' +stderr '^directory ..[/\\]pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2 outside main module or its selected dependencies$' go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.1 stdout 'rsc.io/quote' diff --git a/src/cmd/go/testdata/script/mod_list_retract.txt b/src/cmd/go/testdata/script/mod_list_retract.txt index 4b133485152da0..b7147aa18241fd 100644 --- a/src/cmd/go/testdata/script/mod_list_retract.txt +++ b/src/cmd/go/testdata/script/mod_list_retract.txt @@ -101,7 +101,9 @@ module example.com/use go 1.15 require example.com/retract v1.0.0-bad - +-- go.sum -- +example.com/retract v1.0.0-bad h1:liAW69rbtjY67x2CcNzat668L/w+YGgNX3lhJsWIJis= +example.com/retract v1.0.0-bad/go.mod h1:0DvGGofJ9hr1q63cBrOY/jSY52OwhRGA0K47NE80I5Y= -- use.go -- package use diff --git a/src/cmd/go/testdata/script/mod_list_sums.txt b/src/cmd/go/testdata/script/mod_list_sums.txt index 86c528f82907b0..6c2f57c2b2d88d 100644 --- a/src/cmd/go/testdata/script/mod_list_sums.txt +++ b/src/cmd/go/testdata/script/mod_list_sums.txt @@ -29,4 +29,4 @@ stderr '^go: updates to go.sum needed, disabled by -mod=readonly$' # # TODO(#41297): This should not be an error either. ! go list -m -mod=readonly -versions rsc.io/sampler -stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' +stderr '^go: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' diff --git a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt index c6bbbb04ec7268..7eebe266dbb8f0 100644 --- a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt +++ b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt @@ -26,7 +26,7 @@ stdout '^example.com/nolatest v0.0.0$' # If proxy returns an invalid response, we should see an error. env GOPROXY=$testproxy/invalid ! go list -m -u example.com/nolatest -stderr '^go list -m: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' +stderr '^go: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt b/src/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt index b983bec73db903..8e51dfcd2aadf8 100644 --- a/src/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt +++ b/src/cmd/go/testdata/script/mod_list_upgrade_pseudo.txt @@ -12,11 +12,11 @@ env GO111MODULE=on # The latest pseudo-version is semantically higher than the latest tag. # 'list -u' should not suggest a lower version as an upgrade. -go get -d example.com/pseudoupgrade@b5426c8 +go get example.com/pseudoupgrade@b5426c8 go list -m -u all stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$' -go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 +go get example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534 go list -m -u all stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$' diff --git a/src/cmd/go/testdata/script/mod_load_badchain.txt b/src/cmd/go/testdata/script/mod_load_badchain.txt index eb464ab0d3b84e..be2a4bc1db283b 100644 --- a/src/cmd/go/testdata/script/mod_load_badchain.txt +++ b/src/cmd/go/testdata/script/mod_load_badchain.txt @@ -10,13 +10,13 @@ go mod download example.com/badchain/b@v1.1.0 go mod download example.com/badchain/c@v1.1.0 # Try to update example.com/badchain/a (and its dependencies). -! go get -d example.com/badchain/a +! go get example.com/badchain/a cmp stderr update-a-expected cmp go.mod go.mod.orig # Try to update the main module. This updates everything, including # modules that aren't direct requirements, so the error stack is shorter. -! go get -d -u ./... +! go get -u ./... cmp stderr update-main-expected cmp go.mod go.mod.orig @@ -69,17 +69,17 @@ import ( func Test(t *testing.T) {} -- update-main-expected -- -go get: example.com/badchain/c@v1.1.0: parsing go.mod: +go: example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- update-a-expected -- -go get: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- list-expected -- -go list -m: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c diff --git a/src/cmd/go/testdata/script/mod_load_badzip.txt b/src/cmd/go/testdata/script/mod_load_badzip.txt index 65374d2a6d1fdc..58160b4d442252 100644 --- a/src/cmd/go/testdata/script/mod_load_badzip.txt +++ b/src/cmd/go/testdata/script/mod_load_badzip.txt @@ -1,7 +1,7 @@ # Zip files with unexpected file names inside should be rejected. env GO111MODULE=on -! go get -d rsc.io/badzip +! go get rsc.io/badzip stderr 'zip for rsc.io/badzip@v1.0.0 has unexpected file rsc.io/badzip@v1.0.0.txt' ! grep rsc.io/badzip go.mod diff --git a/src/cmd/go/testdata/script/mod_missing_repo.txt b/src/cmd/go/testdata/script/mod_missing_repo.txt index 8dae85fa88d150..b91a8dbedacb52 100644 --- a/src/cmd/go/testdata/script/mod_missing_repo.txt +++ b/src/cmd/go/testdata/script/mod_missing_repo.txt @@ -9,7 +9,7 @@ env GO111MODULE=on env GOPROXY=direct env GOSUMDB=off -! go get -d vcs-test.golang.org/go/missingrepo/missingrepo-git +! go mod download vcs-test.golang.org/go/missingrepo/missingrepo-git@latest stderr 'vcs-test.golang.org/go/missingrepo/missingrepo-git: git ls-remote .*: exit status .*' -go get -d vcs-test.golang.org/go/missingrepo/missingrepo-git/notmissing +go mod download vcs-test.golang.org/go/missingrepo/missingrepo-git/notmissing@latest diff --git a/src/cmd/go/testdata/script/mod_multirepo.txt b/src/cmd/go/testdata/script/mod_multirepo.txt index 0f335a11f0fb3b..bbefb78d90a48e 100644 --- a/src/cmd/go/testdata/script/mod_multirepo.txt +++ b/src/cmd/go/testdata/script/mod_multirepo.txt @@ -7,7 +7,7 @@ go list -deps -f {{.Dir}} # v2 import should use a downloaded module # both without an explicit go.mod entry ... cp tmp/use_v2.go x.go -go get -d . +go get . go list -deps -f {{.Dir}} stdout 'pkg[\\/]mod[\\/]rsc.io[\\/]quote[\\/]v2@v2.0.1$' diff --git a/src/cmd/go/testdata/script/mod_no_gopath.txt b/src/cmd/go/testdata/script/mod_no_gopath.txt new file mode 100644 index 00000000000000..ed91f5d42e5c8a --- /dev/null +++ b/src/cmd/go/testdata/script/mod_no_gopath.txt @@ -0,0 +1,15 @@ +# https://golang.org/issue/43938: 'go build' should succeed +# if GOPATH and the variables needed for its default value +# are all unset but not relevant to the specific command. + +env HOME='' +env home='' +env GOPATH='' + +go list -deps main.go +stdout '^io$' + +-- main.go -- +package main + +import _ "io" diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt index 33341f7d4b3656..65808244e8cb4a 100644 --- a/src/cmd/go/testdata/script/mod_outside.txt +++ b/src/cmd/go/testdata/script/mod_outside.txt @@ -123,30 +123,30 @@ stderr '^go: go.mod file not found in current directory or any parent directory; stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' -# 'go get' without arguments implicitly operates on the main module, and thus -# should fail. +# 'go get' has no go.mod file to update outside a module and should fail. ! go get -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u ./needmod -stderr '^go: go.mod file not found in current directory or any parent directory; see ''go help modules''$' - -# 'go get -u all' upgrades the transitive import graph of the main module, -# which is empty. +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' ! go get -u all -stderr '^go get: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$' - -# 'go get' should check the proposed module graph for consistency, -# even though we won't write it anywhere. -! go get -d example.com/printversion@v1.0.0 example.com/version@none -stderr '^go get: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$' +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' +! go get example.com/printversion@v1.0.0 example.com/version@none +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' -# 'go get -d' should download and extract the source code needed to build the requested version. -rm -r $GOPATH/pkg/mod/example.com -go get -d example.com/printversion@v1.0.0 -exists $GOPATH/pkg/mod/example.com/printversion@v1.0.0 -exists $GOPATH/pkg/mod/example.com/version@v1.0.0 +# 'go get' should not download anything. +go clean -modcache +! go get example.com/printversion@v1.0.0 +stderr '^go: go.mod file not found in current directory or any parent directory.$' +stderr '^\t''go get'' is no longer supported outside a module.$' +! exists $GOPATH/pkg/mod/example.com/printversion@v1.0.0 +! exists $GOPATH/pkg/mod/example.com/version@v1.0.0 # 'go build' without arguments implicitly operates on the current directory, and should fail. @@ -196,14 +196,16 @@ exists $GOPATH/bin/printversion$GOEXE # 'go install' should fail if a package argument must be resolved to a module. ! go install example.com/printversion -stderr '^go install: version is required when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' +stderr '^go: ''go install'' requires a version when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' # 'go install' should fail if a source file imports a package that must be # resolved to a module. ! go install ./needmod/needmod.go stderr 'needmod[/\\]needmod.go:10:2: no required module provides package example.com/version: go.mod file not found in current directory or any parent directory; see ''go help modules''$' -# 'go install' should succeed with a package in GOROOT. +# 'go install' for a package in GOROOT should succeed. +# (It must be non-stale here so that the test does not write to GOROOT). +! stale cmd/addr2line go install cmd/addr2line ! stderr . @@ -223,35 +225,11 @@ go fmt needmod/needmod.go # The remainder of the test checks dependencies by linking and running binaries. -# 'go get' of a binary without a go.mod should install the requested version, -# resolving outside dependencies to the latest available versions. -go get example.com/printversion@v0.1.0 -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v0.1.0' -stdout 'using example.com/version v1.1.0' - -# 'go get' of a versioned binary should build and install the latest version -# using its minimal required modules, ignoring replacements and exclusions. -go get example.com/printversion -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v1.0.0' -stdout 'using example.com/version v1.0.0' - -# 'go get -u=patch' should patch dependencies before installing, -# again ignoring replacements and exclusions. -go get -u=patch example.com/printversion@v1.0.0 -exec ../bin/printversion -stdout 'path is example.com/printversion' -stdout 'main is example.com/printversion v1.0.0' -stdout 'using example.com/version v1.0.1' - # 'go run' should work with file arguments if they don't import anything # outside std. go run ./stdonly/stdonly.go stdout 'path is command-line-arguments$' -stdout 'main is command-line-arguments \(devel\)' +stdout 'main is $' # 'go generate' should work with file arguments. [exec:touch] go generate ./needmod/needmod.go diff --git a/src/cmd/go/testdata/script/mod_overlay.txt b/src/cmd/go/testdata/script/mod_overlay.txt index 86ab04bd3cfab4..da35be6a196ed6 100644 --- a/src/cmd/go/testdata/script/mod_overlay.txt +++ b/src/cmd/go/testdata/script/mod_overlay.txt @@ -20,7 +20,7 @@ go list -deps -overlay overlay.json . # Overlaid go.mod is not rewritten by 'go get'. cd $WORK/gopath/src/get-doesnt-add-dep cp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod -! go get -d -overlay overlay.json . +! go get -overlay overlay.json . stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' cmp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod @@ -30,17 +30,17 @@ cmp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod # the correct go.sum is used with the overlay, 'go get .' should # not report a security error. cd $WORK/gopath/src/overlay-sum-used -! go get -d . +! go get . stderr 'SECURITY ERROR' ! go mod verify stderr 'SECURITY ERROR' -go get -d -overlay overlay.json . +go get -overlay overlay.json . go mod verify -overlay overlay.json # Overlaid go.sum is not rewritten. # Copy an incomplete file to the overlay file, and expect an error # attempting to update the file cp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums -! go get -d -overlay overlay.json . +! go get -overlay overlay.json . stderr '^go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay$' cmp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums ! go mod tidy -overlay overlay.json @@ -55,7 +55,7 @@ cd $WORK/gopath/src/overlay-and-dash-modfile go list -modfile=alternate.mod -overlay overlay.json . stdout 'found.the/module' # Even with -modfile, overlaid files can't be opened for write. -! go get -modfile=alternate.mod -overlay overlay.json -d rsc.io/quote +! go get -modfile=alternate.mod -overlay overlay.json rsc.io/quote stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' # Carving out a module by adding an overlaid go.mod file @@ -77,11 +77,11 @@ go list -overlay overlay.json all ! stdout ^carve2$ stdout ^carve2/nomod$ # Editing go.mod file fails because overlay is read only -! go get -overlay overlay.json -d rsc.io/quote +! go get -overlay overlay.json rsc.io/quote stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$' ! grep rsc.io/quote $WORK/overlay/carve2-nomod-go.mod # Editing go.mod file succeeds because we use -modfile to redirect to same file -go get -overlay overlay.json -modfile $WORK/overlay/carve2-nomod-go.mod -d rsc.io/quote +go get -overlay overlay.json -modfile $WORK/overlay/carve2-nomod-go.mod rsc.io/quote grep rsc.io/quote $WORK/overlay/carve2-nomod-go.mod -- no-go-mod/file.go -- diff --git a/src/cmd/go/testdata/script/mod_perm.txt b/src/cmd/go/testdata/script/mod_perm.txt new file mode 100644 index 00000000000000..f5382eceaf67ca --- /dev/null +++ b/src/cmd/go/testdata/script/mod_perm.txt @@ -0,0 +1,23 @@ +# go list should work in ordinary conditions. +go list ./... +! stdout _data + +# skip in conditions where chmod 0 may not work. +# plan9 should be fine, but copied from list_perm.txt unchanged. +[root] skip +[windows] skip +[plan9] skip + +# go list should work with unreadable _data directory. +chmod 0 _data +go list ./... +! stdout _data + +-- go.mod -- +module m + +-- x.go -- +package m + +-- _data/x.go -- +package p diff --git a/src/cmd/go/testdata/script/mod_permissions.txt b/src/cmd/go/testdata/script/mod_permissions.txt index 2d32dcd10fd4c9..77e2508cb7f3b1 100644 --- a/src/cmd/go/testdata/script/mod_permissions.txt +++ b/src/cmd/go/testdata/script/mod_permissions.txt @@ -12,7 +12,7 @@ chmod 0640 go.mod chmod 0604 go.sum go mod edit -module=golang.org/issue/34634 -go get -d +go get cmp go.mod go.mod.want cmp go.sum go.sum.want diff --git a/src/cmd/go/testdata/script/mod_prefer_compatible.txt b/src/cmd/go/testdata/script/mod_prefer_compatible.txt index 1b408c3e9e925d..8e88997a3c3089 100644 --- a/src/cmd/go/testdata/script/mod_prefer_compatible.txt +++ b/src/cmd/go/testdata/script/mod_prefer_compatible.txt @@ -24,7 +24,7 @@ go list -m github.com/russross/blackfriday@upgrade stdout '^github.com/russross/blackfriday v1\.' ! go list -m github.com/russross/blackfriday@patch -stderr '^go list -m: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' +stderr '^go: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' # If we're fetching directly from version control, ignored +incompatible # versions should also be omitted by 'go list'. diff --git a/src/cmd/go/testdata/script/mod_proxy_invalid.txt b/src/cmd/go/testdata/script/mod_proxy_invalid.txt index 6427cc1527af37..63980b839e7743 100644 --- a/src/cmd/go/testdata/script/mod_proxy_invalid.txt +++ b/src/cmd/go/testdata/script/mod_proxy_invalid.txt @@ -2,7 +2,7 @@ env GO111MODULE=on env GOPROXY=$GOPROXY/invalid ! go list -m rsc.io/quote@latest -stderr '^go list -m: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' ! go list -m rsc.io/quote@1.5.2 -stderr '^go list -m: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' diff --git a/src/cmd/go/testdata/script/mod_proxy_list.txt b/src/cmd/go/testdata/script/mod_proxy_list.txt index 89129f4fe237d6..849cf2c4764065 100644 --- a/src/cmd/go/testdata/script/mod_proxy_list.txt +++ b/src/cmd/go/testdata/script/mod_proxy_list.txt @@ -3,34 +3,34 @@ env proxy=$GOPROXY # Proxy that can't serve should fail. env GOPROXY=$proxy/404 -! go get -d rsc.io/quote@v1.0.0 +! go get rsc.io/quote@v1.0.0 stderr '404 Not Found' # get should walk down the proxy list past 404 and 410 responses. env GOPROXY=$proxy/404,$proxy/410,$proxy -go get -d rsc.io/quote@v1.1.0 +go get rsc.io/quote@v1.1.0 # get should not walk past other 4xx errors if proxies are separated with ','. env GOPROXY=$proxy/403,$proxy -! go get -d rsc.io/quote@v1.2.0 +! go get rsc.io/quote@v1.2.0 stderr 'reading.*/403/rsc.io/.*: 403 Forbidden' # get should not walk past non-4xx errors if proxies are separated with ','. env GOPROXY=$proxy/500,$proxy -! go get -d rsc.io/quote@v1.3.0 +! go get rsc.io/quote@v1.3.0 stderr 'reading.*/500/rsc.io/.*: 500 Internal Server Error' # get should walk past other 4xx errors if proxies are separated with '|'. env GOPROXY=$proxy/403|https://0.0.0.0|$proxy -go get -d rsc.io/quote@v1.2.0 +go get rsc.io/quote@v1.2.0 # get should walk past non-4xx errors if proxies are separated with '|'. env GOPROXY=$proxy/500|https://0.0.0.0|$proxy -go get -d rsc.io/quote@v1.3.0 +go get rsc.io/quote@v1.3.0 # get should return the final error if that's all we have. env GOPROXY=$proxy/404,$proxy/410 -! go get -d rsc.io/quote@v1.4.0 +! go get rsc.io/quote@v1.4.0 stderr 'reading.*/410/rsc.io/.*: 410 Gone' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_query.txt b/src/cmd/go/testdata/script/mod_query.txt index a75f86ed7c5729..3758732504d0ca 100644 --- a/src/cmd/go/testdata/script/mod_query.txt +++ b/src/cmd/go/testdata/script/mod_query.txt @@ -25,7 +25,7 @@ go list -m rsc.io/quote@v1.5.3 -stderr 'go list -m: module rsc.io/quote: no matching versions for query ">v1.5.3"' +stderr 'go: module rsc.io/quote: no matching versions for query ">v1.5.3"' go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3 stdout 'no matching versions for query ">v1.5.3"' diff --git a/src/cmd/go/testdata/script/mod_query_empty.txt b/src/cmd/go/testdata/script/mod_query_empty.txt index f8b6e3e97edcff..1c39eae5744c31 100644 --- a/src/cmd/go/testdata/script/mod_query_empty.txt +++ b/src/cmd/go/testdata/script/mod_query_empty.txt @@ -7,14 +7,14 @@ go mod download example.com/join@v1.1.0 # reading that version should cause 'go get' to fail. env GOPROXY=file:///$WORK/badproxy cp go.mod.orig go.mod -! go get -d example.com/join/subpkg -stderr 'go get: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' +! go get example.com/join/subpkg +stderr 'go: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' # If @v/list is empty, the 'go' command should still try to resolve # other module paths. env GOPROXY=file:///$WORK/emptysub cp go.mod.orig go.mod -go get -d example.com/join/subpkg +go get example.com/join/subpkg go list -m example.com/join/... ! stdout 'example.com/join/subpkg' stdout 'example.com/join v1.1.0' @@ -23,7 +23,7 @@ stdout 'example.com/join v1.1.0' # that version is treated as nonexistent. env GOPROXY=file:///$WORK/notfound cp go.mod.orig go.mod -go get -d example.com/join/subpkg +go get example.com/join/subpkg go list -m example.com/join/... ! stdout 'example.com/join/subpkg' stdout 'example.com/join v1.1.0' @@ -39,8 +39,8 @@ stdout 'example.com/join v1.1.0' env GOPROXY=file:///$WORK/gatekeeper chmod 0000 $WORK/gatekeeper/example.com/join/subpkg/@latest cp go.mod.orig go.mod -! go get -d example.com/join/subpkg -stderr 'go get: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' +! go get example.com/join/subpkg +stderr 'go: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' -- go.mod.orig -- module example.com/othermodule diff --git a/src/cmd/go/testdata/script/mod_query_exclude.txt b/src/cmd/go/testdata/script/mod_query_exclude.txt index b0019694119bb1..f76b20c6d88306 100644 --- a/src/cmd/go/testdata/script/mod_query_exclude.txt +++ b/src/cmd/go/testdata/script/mod_query_exclude.txt @@ -18,17 +18,17 @@ stdout '^rsc.io/quote v1.5.1$' # get excluded version cp go.exclude.mod go.exclude.mod.orig -! go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.0 -stderr '^go get: rsc.io/quote@v1.5.0: excluded by go.mod$' +! go get -modfile=go.exclude.mod rsc.io/quote@v1.5.0 +stderr '^go: rsc.io/quote@v1.5.0: excluded by go.mod$' # get non-excluded version cp go.exclude.mod.orig go.exclude.mod -go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.1 +go get -modfile=go.exclude.mod rsc.io/quote@v1.5.1 stderr 'rsc.io/quote v1.5.1' # get query with excluded version cp go.exclude.mod.orig go.exclude.mod -go get -modfile=go.exclude.mod -d rsc.io/quote@>=v1.5 +go get -modfile=go.exclude.mod rsc.io/quote@>=v1.5 go list -modfile=go.exclude.mod -m ...quote stdout 'rsc.io/quote v1.5.[1-9]' diff --git a/src/cmd/go/testdata/script/mod_query_main.txt b/src/cmd/go/testdata/script/mod_query_main.txt index 39e5841a9cfa42..2a2fa42318aa1d 100644 --- a/src/cmd/go/testdata/script/mod_query_main.txt +++ b/src/cmd/go/testdata/script/mod_query_main.txt @@ -6,9 +6,9 @@ go mod download rsc.io/quote@latest # 'go mod download' will not download @upgrade or @patch, since they always # resolve to the main module. go mod download rsc.io/quote@upgrade -stderr '^go mod download: skipping argument rsc.io/quote@upgrade that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@upgrade that resolves to the main module$' go mod download rsc.io/quote@patch -stderr '^go mod download: skipping argument rsc.io/quote@patch that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@patch that resolves to the main module$' # 'go list -m' can show a version of the main module. go list -m rsc.io/quote@5d9f230b @@ -31,11 +31,11 @@ stdout '^rsc.io/quote$' # 'go get' will not attempt to upgrade the main module to any specific version. # See also: mod_get_main.txt. ! go get rsc.io/quote@5d9f230b -stderr '^go get: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@v1.5.2 -stderr '^go get: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@latest -stderr '^go get: can''t request version "latest" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "latest" of the main module \(rsc.io/quote\)$' -- go.mod -- module rsc.io/quote diff --git a/src/cmd/go/testdata/script/mod_readonly.txt b/src/cmd/go/testdata/script/mod_readonly.txt index d05ad2a3174f8e..9e950c38982120 100644 --- a/src/cmd/go/testdata/script/mod_readonly.txt +++ b/src/cmd/go/testdata/script/mod_readonly.txt @@ -19,7 +19,7 @@ cmp go.mod go.mod.empty env GOFLAGS=-mod=readonly # update go.mod - go get allowed -go get -d rsc.io/quote +go get rsc.io/quote grep rsc.io/quote go.mod # update go.mod - go mod tidy allowed @@ -41,7 +41,7 @@ go list -m all # -mod=readonly should reject inconsistent go.mod files # (ones that would be rewritten). -go get -d rsc.io/sampler@v1.2.0 +go get rsc.io/sampler@v1.2.0 go mod edit -require rsc.io/quote@v1.5.2 cp go.mod go.mod.inconsistent ! go list @@ -81,7 +81,7 @@ stderr '^x.go:2:8: no required module provides package rsc.io/quote; to add it:\ stderr '^x.go:2:8: no required module provides package rsc.io/quote; to add it:\n\tgo get rsc.io/quote$' # However, if we didn't see an import from the main module, we should suggest -# 'go get -d' instead, because we don't know whether 'go mod tidy' would add it. +# 'go get' instead, because we don't know whether 'go mod tidy' would add it. ! go list rsc.io/quote stderr '^no required module provides package rsc.io/quote; to add it:\n\tgo get rsc.io/quote$' diff --git a/src/cmd/go/testdata/script/mod_replace.txt b/src/cmd/go/testdata/script/mod_replace.txt index a0a367fb1db11c..26b15518d94fa3 100644 --- a/src/cmd/go/testdata/script/mod_replace.txt +++ b/src/cmd/go/testdata/script/mod_replace.txt @@ -42,7 +42,7 @@ stdout 'Concurrency is not parallelism.' # indicate the replacement module. cp go.mod.orig go.mod go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3 -! go get -d rsc.io/quote/v3/missing-package +! go get rsc.io/quote/v3/missing-package stderr 'module rsc.io/quote/v3@upgrade found \(v3.0.0, replaced by ./local/rsc.io/quote/v3\), but does not contain package' # The reported Dir and GoMod for a replaced module should be accurate. diff --git a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt index d24f37b7880920..df752d9716e538 100644 --- a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt +++ b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt @@ -35,7 +35,7 @@ go list -m gopkg.in/src-d/go-git.v4 # A mismatched gopkg.in path should not be able to replace a different major version. cd ../3-to-gomod-4 ! go list -m gopkg.in/src-d/go-git.v3 -stderr '^go list -m: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' +stderr '^go: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' -- 4-to-4/go.mod -- module golang.org/issue/34254 diff --git a/src/cmd/go/testdata/script/mod_replace_import.txt b/src/cmd/go/testdata/script/mod_replace_import.txt index 2add31f71c10d4..7bf3a86fed876f 100644 --- a/src/cmd/go/testdata/script/mod_replace_import.txt +++ b/src/cmd/go/testdata/script/mod_replace_import.txt @@ -6,7 +6,7 @@ cp go.mod go.mod.orig cmp go.mod go.mod.orig # 'go list' should resolve imports using replacements. -go get -d +go get go list all stdout 'example.com/a/b$' stdout 'example.com/x/v3$' diff --git a/src/cmd/go/testdata/script/mod_replace_readonly.txt b/src/cmd/go/testdata/script/mod_replace_readonly.txt index d950d78bd3c621..5c1226b15efd46 100644 --- a/src/cmd/go/testdata/script/mod_replace_readonly.txt +++ b/src/cmd/go/testdata/script/mod_replace_readonly.txt @@ -10,7 +10,7 @@ cp go.mod go.mod.orig go mod edit -replace rsc.io/quote=./quote ! go list rsc.io/quote stderr '^module rsc.io/quote provides package rsc.io/quote and is replaced but not required; to add it:\n\tgo get rsc.io/quote$' -go get -d rsc.io/quote +go get rsc.io/quote cmp go.mod go.mod.latest go list rsc.io/quote cp go.mod.orig go.mod @@ -19,7 +19,7 @@ cp go.mod.orig go.mod go mod edit -replace rsc.io/quote@v1.0.0-doesnotexist=./quote ! go list rsc.io/quote stderr '^module rsc.io/quote provides package rsc.io/quote and is replaced but not required; to add it:\n\tgo get rsc.io/quote@v1.0.0-doesnotexist$' -go get -d rsc.io/quote@v1.0.0-doesnotexist +go get rsc.io/quote@v1.0.0-doesnotexist cmp go.mod go.mod.specific go list rsc.io/quote cp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_retention.txt b/src/cmd/go/testdata/script/mod_retention.txt index 7a371b18068cd2..9d30026459a8af 100644 --- a/src/cmd/go/testdata/script/mod_retention.txt +++ b/src/cmd/go/testdata/script/mod_retention.txt @@ -39,12 +39,14 @@ go list -mod=mod all cmp go.mod go.mod.tidy # "// indirect" comments should be added if appropriate. +# TODO(#42504): add case for 'go list -mod=mod -tags=any all' when -tags=any +# is supported. Only a command that loads "all" without build constraints +# (except "ignore") has enough information to add "// indirect" comments. +# 'go mod tidy' and 'go mod vendor' are the only commands that do that, +# but 'go mod vendor' cannot write go.mod. cp go.mod.toodirect go.mod go list all cmp go.mod go.mod.toodirect -go mod vendor # loads everything, so adds "// indirect" comments. -cmp go.mod go.mod.tidy -rm -r vendor # Redundant requirements should be preserved... @@ -81,14 +83,14 @@ require ( package x import _ "rsc.io/quote" -- go.mod.crlf -- -module m - -go 1.14 - -require ( - rsc.io/quote v1.5.2 - rsc.io/testonly v1.0.0 // indirect -) +module m + +go 1.14 + +require ( + rsc.io/quote v1.5.2 + rsc.io/testonly v1.0.0 // indirect +) -- go.mod.unsorted -- module m @@ -139,10 +141,10 @@ module m go $goversion +require rsc.io/quote v1.5.2 + require ( - rsc.io/quote v1.5.2 + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect rsc.io/sampler v1.3.0 // indirect rsc.io/testonly v1.0.0 // indirect ) - -require golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect diff --git a/src/cmd/go/testdata/script/mod_retract.txt b/src/cmd/go/testdata/script/mod_retract.txt index 4f95ece8d7aaae..37aae4896d0c71 100644 --- a/src/cmd/go/testdata/script/mod_retract.txt +++ b/src/cmd/go/testdata/script/mod_retract.txt @@ -17,7 +17,7 @@ exists $GOPATH/pkg/mod/cache/download/example.com/retract/@v/v1.0.0-bad.mod # Importing a package from a module with a retracted latest version will # select the latest non-retracted version. -go get -d ./use_self_prev +go get ./use_self_prev go list -m example.com/retract/self/prev stdout '^example.com/retract/self/prev v1.1.0$' exists $GOPATH/pkg/mod/cache/download/example.com/retract/self/prev/@v/v1.9.0.mod diff --git a/src/cmd/go/testdata/script/mod_retract_fix_version.txt b/src/cmd/go/testdata/script/mod_retract_fix_version.txt index e45758b627016d..9ae49f53ab3797 100644 --- a/src/cmd/go/testdata/script/mod_retract_fix_version.txt +++ b/src/cmd/go/testdata/script/mod_retract_fix_version.txt @@ -15,7 +15,7 @@ cmp go.mod go.mod.want # If a retracted version doesn't match the module's major version suffx, # an error should be reported. ! go mod edit -retract=v3.0.1 -stderr '^go mod: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' +stderr '^go: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' cp go.mod.mismatch-v2 go.mod ! go list -m all stderr 'go.mod:3: retract rsc.io/quote/v2: version "v3.0.1" invalid: should be v2, not v3$' diff --git a/src/cmd/go/testdata/script/mod_retract_incompatible.txt b/src/cmd/go/testdata/script/mod_retract_incompatible.txt index 61538e8024445c..5d09532529239b 100644 --- a/src/cmd/go/testdata/script/mod_retract_incompatible.txt +++ b/src/cmd/go/testdata/script/mod_retract_incompatible.txt @@ -6,10 +6,10 @@ go mod init m # Request a +incompatible version retracted in v1.0.0. -go get -d example.com/retract/incompatible@v2.0.0+incompatible +go get example.com/retract/incompatible@v2.0.0+incompatible stderr '^go: warning: example.com/retract/incompatible@v2.0.0\+incompatible: retracted by module author$' # We should still see a warning if the +incompatible was previously in the # build list. -go get -d example.com/retract/incompatible@v2.0.0+incompatible +go get example.com/retract/incompatible@v2.0.0+incompatible stderr '^go: warning: example.com/retract/incompatible@v2.0.0\+incompatible: retracted by module author$' diff --git a/src/cmd/go/testdata/script/mod_retract_noupgrade.txt b/src/cmd/go/testdata/script/mod_retract_noupgrade.txt new file mode 100644 index 00000000000000..67de79f42d51a2 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_retract_noupgrade.txt @@ -0,0 +1,11 @@ +go list -m -u example.com/retract/noupgrade +stdout '^example.com/retract/noupgrade v1.0.0 \(retracted\)$' + +-- go.mod -- +module use + +go 1.19 + +require example.com/retract/noupgrade v1.0.0 +-- go.sum -- +example.com/retract/noupgrade v1.0.0/go.mod h1:q2/HnBejUQ83RcUo4stf2U++/Zr9R/Ky3BsodjKBkQ4= diff --git a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt index eb00e8405c0107..27c2b670658e21 100644 --- a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt +++ b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt @@ -24,12 +24,12 @@ stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-64c061 # A retracted version is a valid base. Retraction should not validate existing # pseudo-versions, nor should it turn invalid pseudo-versions valid. -go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b +go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b go list -m vcs-test.golang.org/git/retract-pseudo.git stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$' -! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371 -stderr '^go get: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' +! go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371 +stderr '^go: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' -- retract-pseudo.sh -- #!/bin/bash diff --git a/src/cmd/go/testdata/script/mod_retract_rationale.txt b/src/cmd/go/testdata/script/mod_retract_rationale.txt index 823c384e488ff6..92e9b7d6ea573c 100644 --- a/src/cmd/go/testdata/script/mod_retract_rationale.txt +++ b/src/cmd/go/testdata/script/mod_retract_rationale.txt @@ -1,5 +1,5 @@ # When there is no rationale, 'go get' should print a hard-coded message. -go get -d example.com/retract/rationale@v1.0.0-empty +go get example.com/retract/rationale@v1.0.0-empty stderr '^go: warning: example.com/retract/rationale@v1.0.0-empty: retracted by module author$' # 'go list' should print the same hard-coded message. @@ -8,7 +8,7 @@ stdout '^\[retracted by module author\]$' # When there is a multi-line message, 'go get' should print the first line. -go get -d example.com/retract/rationale@v1.0.0-multiline1 +go get example.com/retract/rationale@v1.0.0-multiline1 stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline1: retracted by module author: short description$' ! stderr 'detail' @@ -18,7 +18,7 @@ cmp stdout multiline # 'go get' output should be the same whether the retraction appears at top-level # or in a block. -go get -d example.com/retract/rationale@v1.0.0-multiline2 +go get example.com/retract/rationale@v1.0.0-multiline2 stderr '^go: warning: example.com/retract/rationale@v1.0.0-multiline2: retracted by module author: short description$' ! stderr 'detail' @@ -28,7 +28,7 @@ cmp stdout multiline # 'go get' should omit long messages. -go get -d example.com/retract/rationale@v1.0.0-long +go get example.com/retract/rationale@v1.0.0-long stderr '^go: warning: example.com/retract/rationale@v1.0.0-long: retracted by module author: \(message omitted: too long\)' # 'go list' should show the full message. @@ -37,7 +37,7 @@ stdout '^\[lo{500}ng\]$' # 'go get' should omit messages with unprintable characters. -go get -d example.com/retract/rationale@v1.0.0-unprintable +go get example.com/retract/rationale@v1.0.0-unprintable stderr '^go: warning: example.com/retract/rationale@v1.0.0-unprintable: retracted by module author: \(message omitted: contains non-printable characters\)' # 'go list' should show the full message. @@ -61,9 +61,9 @@ go list -m -retracted -f '{{range .Retracted}}{{.}},{{end}}' example.com/retract stdout '^single version,degenerate range,$' # 'go get' will only report the first retraction to avoid being too verbose. -go get -d example.com/retract/rationale@v1.0.0-order +go get example.com/retract/rationale@v1.0.0-order stderr '^go: warning: example.com/retract/rationale@v1.0.0-order: retracted by module author: degenerate range$' -go get -d example.com/retract/rationale@v1.0.1-order +go get example.com/retract/rationale@v1.0.1-order stderr '^go: warning: example.com/retract/rationale@v1.0.1-order: retracted by module author: single version$' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_retract_rename.txt b/src/cmd/go/testdata/script/mod_retract_rename.txt index f54742c5232253..38986f333f6474 100644 --- a/src/cmd/go/testdata/script/mod_retract_rename.txt +++ b/src/cmd/go/testdata/script/mod_retract_rename.txt @@ -1,5 +1,5 @@ # Populate go.sum. -go get -d +go get # 'go list -m -retracted' should load retractions, even if the version # containing retractions has a different module path. @@ -9,11 +9,11 @@ go list -m -retracted -f '{{with .Retracted}}retracted{{end}}' example.com/retra go list -m -u -f '{{with .Retracted}}retracted{{end}}' example.com/retract/rename # 'go get' should warn about the retracted version. -go get -d +go get stderr '^go: warning: example.com/retract/rename@v1.0.0-bad: retracted by module author: bad$' # We can't upgrade, since this latest version has a different module path. -! go get -d example.com/retract/rename +! go get example.com/retract/rename stderr 'module declares its path as: example.com/retract/newname' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_retract_replace.txt b/src/cmd/go/testdata/script/mod_retract_replace.txt index 9cd714739abf55..788968f420ec54 100644 --- a/src/cmd/go/testdata/script/mod_retract_replace.txt +++ b/src/cmd/go/testdata/script/mod_retract_replace.txt @@ -2,7 +2,7 @@ # obtain retractions from the replacement. # Populate go.sum. -go get -d +go get # The latest version, v1.9.0, is not available on the proxy. go list -m -retracted example.com/retract/missingmod diff --git a/src/cmd/go/testdata/script/mod_run_issue52331.txt b/src/cmd/go/testdata/script/mod_run_issue52331.txt new file mode 100644 index 00000000000000..917e8902118a67 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_run_issue52331.txt @@ -0,0 +1,35 @@ +# Regression test for https://go.dev/issue/52331: 'go run -mod=mod' +# failed to write go.mod and go.sum with the resolved dependencies. + +[short] skip + +! go run main.go +# stderr '^main\.go:6:2: no required module provides package example\.com/version; to add it:\n\tgo get example\.com/version\n\z' + +go run -mod=mod main.go +cmp go.mod go.mod.want +grep -count=1 '^example\.com/version v1.1.0 h1:' go.sum +grep -count=1 '^example\.com/version v1.1.0/go.mod h1:' go.sum + +-- go.mod -- +module example + +go 1.17 +-- go.mod.want -- +module example + +go 1.17 + +require example.com/version v1.1.0 // indirect +-- main.go -- +package main + +import ( + "fmt" + + "example.com/version" +) + +func main() { + fmt.Println(version.V) +} diff --git a/src/cmd/go/testdata/script/mod_run_nonmain.txt b/src/cmd/go/testdata/script/mod_run_nonmain.txt index 036755d2d1ac27..8435fc05b49636 100644 --- a/src/cmd/go/testdata/script/mod_run_nonmain.txt +++ b/src/cmd/go/testdata/script/mod_run_nonmain.txt @@ -7,7 +7,7 @@ stderr '^package example.net/nonmain is not a main package$' ! go run ./... stderr '^go: warning: "\./\.\.\." matched only non-main packages$' -stderr '^go run: no packages loaded from \./\.\.\.$' +stderr '^go: no packages loaded from \./\.\.\.$' -- go.mod -- module example.net/nonmain diff --git a/src/cmd/go/testdata/script/mod_run_pkg_version.txt b/src/cmd/go/testdata/script/mod_run_pkg_version.txt index e921fab5085404..c3a218d553d5ca 100644 --- a/src/cmd/go/testdata/script/mod_run_pkg_version.txt +++ b/src/cmd/go/testdata/script/mod_run_pkg_version.txt @@ -21,7 +21,7 @@ env GO111MODULE=on cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go run example.com/cmd/a@v1.0.0 stdout '^a@v1.0.0$' cmp go.mod go.mod.orig @@ -92,12 +92,12 @@ package main func main() {} -- replace-err -- -go run: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go run: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/src/cmd/go/testdata/script/mod_skip_write.txt b/src/cmd/go/testdata/script/mod_skip_write.txt new file mode 100644 index 00000000000000..9fdb6fc1212587 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_skip_write.txt @@ -0,0 +1,92 @@ +# Commands used to debug the module graph should not write go.mod or go.sum +# or report errors when those files need to be updated. + +# Everything's okay initially. +go list -m all + +# Downgrading sampler makes go.mod inconsistent, but 'go mod graph', +# 'go mod verify', and 'go mod why' still work. +cp go.mod go.mod.orig +go mod edit -require=rsc.io/sampler@v1.2.0 +cp go.mod go.mod.edit +! go list -m all +stderr 'updates to go.mod needed' + +go mod graph +cmp stdout graph.want +cmp go.mod go.mod.edit + +go mod verify +stdout '^all modules verified$' +cmp go.mod go.mod.edit + +go mod why rsc.io/sampler +cmp stdout why.want +cmp go.mod go.mod.edit + +go mod why -m rsc.io/sampler +cmp stdout why.want +cmp go.mod go.mod.edit + +cp go.mod.orig go.mod + +# Removing go.sum breaks other commands, but 'go mod graph' and +# 'go mod why' still work. +rm go.sum +! go list -m all +stderr 'missing go.sum entry' + +go mod graph +cmp stdout graph.want +! exists go.sum + +go mod verify +stdout '^all modules verified$' +! exists go.sum + +go mod why rsc.io/sampler +cmp stdout why.want +! exists go.sum + +go mod why -m rsc.io/sampler +cmp stdout why.want +! exists go.sum + +-- go.mod -- +module m + +go 1.18 + +require rsc.io/quote v1.5.2 + +require ( + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect + rsc.io/sampler v1.3.0 // indirect + rsc.io/testonly v1.0.0 // indirect +) +-- go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.2.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/testonly v1.0.0 h1:K/VWHdO+Jv7woUXG0GzVNx1czBXUt3Ib1deaMn+xk64= +rsc.io/testonly v1.0.0/go.mod h1:OqmGbIFOcF+XrFReLOGZ6BhMM7uMBiQwZsyNmh74SzY= +-- use.go -- +package use + +import _ "rsc.io/quote" +-- graph.want -- +m golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c +m rsc.io/quote@v1.5.2 +m rsc.io/sampler@v1.3.0 +m rsc.io/testonly@v1.0.0 +rsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0 +rsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c +-- why.want -- +# rsc.io/sampler +m +rsc.io/quote +rsc.io/sampler diff --git a/src/cmd/go/testdata/script/mod_sum_lookup.txt b/src/cmd/go/testdata/script/mod_sum_lookup.txt index e0219213800687..7513f7f49f352e 100644 --- a/src/cmd/go/testdata/script/mod_sum_lookup.txt +++ b/src/cmd/go/testdata/script/mod_sum_lookup.txt @@ -8,7 +8,7 @@ go list -e -mod=mod -tags=ignore ./noexist # When an import is resolved successfully, we should only save hashes for # the module that provides the package, not for other modules looked up. # Verifies golang.org/issue/31580. -go get -d ./exist +go get ./exist grep '^example.com/join v1.1.0 h1:' go.sum ! grep '^example.com/join/subpkg' go.sum cp go.sum go.list.sum diff --git a/src/cmd/go/testdata/script/mod_sum_readonly.txt b/src/cmd/go/testdata/script/mod_sum_readonly.txt index 113f13ea390852..57c5bbeefdf020 100644 --- a/src/cmd/go/testdata/script/mod_sum_readonly.txt +++ b/src/cmd/go/testdata/script/mod_sum_readonly.txt @@ -4,7 +4,7 @@ env GO111MODULE=on # When a sum is needed to load the build list, we get an error for the # specific module. The .mod file is not downloaded, and go.sum is not written. ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod ! exists go.sum @@ -12,7 +12,7 @@ stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo # we should see the same error. cp go.sum.h2only go.sum ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod cmp go.sum go.sum.h2only rm go.sum @@ -21,7 +21,7 @@ rm go.sum cp go.mod go.mod.orig go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1 ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.mod ! exists go.sum cp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_sum_replaced.txt b/src/cmd/go/testdata/script/mod_sum_replaced.txt index b03982d9cff442..6c322a00d607a9 100644 --- a/src/cmd/go/testdata/script/mod_sum_replaced.txt +++ b/src/cmd/go/testdata/script/mod_sum_replaced.txt @@ -1,7 +1,7 @@ env GO111MODULE=on -# After 'go get -d', the go.sum file should contain the sum for the module. -go get -d rsc.io/quote@v1.5.0 +# After 'go get', the go.sum file should contain the sum for the module. +go get rsc.io/quote@v1.5.0 grep 'rsc.io/quote v1.5.0' go.sum # If we replace the module and run 'go mod tidy', we should get a sum for the replacement. diff --git a/src/cmd/go/testdata/script/mod_sumdb.txt b/src/cmd/go/testdata/script/mod_sumdb.txt index fa3483c5cb1a51..d06db4ae69271a 100644 --- a/src/cmd/go/testdata/script/mod_sumdb.txt +++ b/src/cmd/go/testdata/script/mod_sumdb.txt @@ -8,13 +8,13 @@ env dbname=localhost.localdev/sumdb # (this also populates tiles on the sumdb server). cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-wrong -! go get -d rsc.io/quote -stderr 'go get: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' +! go get rsc.io/quote +stderr 'go: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' stderr 'downloaded: h1:3fEy' stderr 'localhost.localdev/sumdb: h1:wrong' stderr 'SECURITY ERROR\nThis download does NOT match the one reported by the checksum server.' -! go get -d rsc.io/sampler -! go get -d golang.org/x/text +! go get rsc.io/sampler +! go get golang.org/x/text go mod edit -require rsc.io/quote@v1.5.2 ! go mod tidy @@ -26,14 +26,14 @@ rm go.sum # switching to truthful sumdb detects timeline inconsistency cp go.mod.orig go.mod env GOSUMDB=$sumdb -! go get -d rsc.io/fortune +! go get rsc.io/fortune stderr 'SECURITY ERROR\ngo.sum database server misbehavior detected!' stderr 'proof of misbehavior:' # removing the cached wrong tree head and cached tiles clears the bad data rm $GOPATH/pkg/sumdb/$dbname/latest go clean -modcache -go get -d rsc.io/fortune +go get rsc.io/fortune -- go.mod.orig -- module m diff --git a/src/cmd/go/testdata/script/mod_sumdb_cache.txt b/src/cmd/go/testdata/script/mod_sumdb_cache.txt index 1b38475fb5e378..063fd20964fde2 100644 --- a/src/cmd/go/testdata/script/mod_sumdb_cache.txt +++ b/src/cmd/go/testdata/script/mod_sumdb_cache.txt @@ -7,40 +7,40 @@ env GOPROXY GONOPROXY GOSUMDB GONOSUMDB cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy/sumdb-503 -! go get -d rsc.io/quote +! go get rsc.io/quote stderr 503 # fetch through working proxy is OK cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy -go get -d rsc.io/quote +go get rsc.io/quote # repeated fetch works entirely from cache, does not consult sumdb cp go.mod.orig go.mod rm go.sum env GOPROXY=$proxy/sumdb-503 -go get -d rsc.io/quote +go get rsc.io/quote rm go.sum # fetch specific module can work without proxy, using cache or go.sum cp go.mod.orig go.mod rm go.sum env GOPROXY=off -go get -d rsc.io/quote@v1.5.2 # using cache +go get rsc.io/quote@v1.5.2 # using cache rm $GOPATH/pkg/mod/cache/download/sumdb/localhost.localdev/sumdb/lookup/rsc.io/quote@v1.5.2 -go get -d rsc.io/quote@v1.5.2 # using go.sum +go get rsc.io/quote@v1.5.2 # using go.sum # fetch fails once we lose access to both cache and go.sum rm go.sum env GOPROXY=$proxy/sumdb-504 -! go get -d rsc.io/quote@v1.5.2 +! go get rsc.io/quote@v1.5.2 stderr 504 # GOINSECURE does not bypass checksum lookup env GOINSECURE=rsc.io env GOPROXY=$proxy/sumdb-504 -! go get -d rsc.io/quote@v1.5.2 +! go get rsc.io/quote@v1.5.2 stderr 504 -- go.mod.orig -- diff --git a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt index 22fcbf3de84a71..a834c4800dcf5f 100644 --- a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt +++ b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt @@ -12,15 +12,15 @@ env GOPATH=$WORK/gopath1 # It comes from the sumweb package, which isn't yet producing structured errors. [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org -! go get -d golang.org/x/text@v0.3.2 -stderr '^go get: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' +! go get golang.org/x/text@v0.3.2 +stderr '^go: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' # If the proxy does not claim to support the database, # checksum verification should fall through to the next proxy, # and downloading should succeed. [windows] env GOPROXY=file:///$WORK/emptyproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/emptyproxy,https://proxy.golang.org -go get -d golang.org/x/text@v0.3.2 +go get golang.org/x/text@v0.3.2 # After a successful sumdb lookup, the lookup can be repeated # using the download cache as a proxy. @@ -29,7 +29,7 @@ cp supported $GOPATH/pkg/mod/cache/download/sumdb/sum.golang.org/supported [!windows] env GOPROXY=file://$WORK/gopath1/pkg/mod/cache/download,file://$WORK/sumproxy env GOPATH=$WORK/gopath2 rm go.sum -go get -d -x -v golang.org/x/text@v0.3.2 +go get -x -v golang.org/x/text@v0.3.2 # Once the checksum is present in the go.sum file, # an empty file-based sumdb can be used in conjunction with @@ -38,10 +38,10 @@ grep golang.org/x/text go.sum env GOPATH=$WORK/gopath3 [windows] env GOPROXY=file:///$WORK/sumproxy [!windows] env GOPROXY=file://$WORK/sumproxy -! go get -d golang.org/x/text@v0.3.2 +! go get golang.org/x/text@v0.3.2 [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org -go get -d golang.org/x/text@v0.3.2 +go get golang.org/x/text@v0.3.2 -- supported -- diff --git a/src/cmd/go/testdata/script/mod_sumdb_golang.txt b/src/cmd/go/testdata/script/mod_sumdb_golang.txt index becd88b52e7cff..a48a5ba1b048c0 100644 --- a/src/cmd/go/testdata/script/mod_sumdb_golang.txt +++ b/src/cmd/go/testdata/script/mod_sumdb_golang.txt @@ -16,7 +16,7 @@ stdout '^sum.golang.org$' env GOSUMDB=sum.golang.org env GOPROXY=direct -go get -d rsc.io/quote@v1.5.2 +go get rsc.io/quote@v1.5.2 cp go.sum saved.sum diff --git a/src/cmd/go/testdata/script/mod_sumdb_proxy.txt b/src/cmd/go/testdata/script/mod_sumdb_proxy.txt index 70b8e3fc4466ff..194c0c92e57051 100644 --- a/src/cmd/go/testdata/script/mod_sumdb_proxy.txt +++ b/src/cmd/go/testdata/script/mod_sumdb_proxy.txt @@ -5,14 +5,14 @@ env GOPROXY GONOPROXY GOSUMDB GONOSUMDB # basic fetch (through proxy) works cp go.mod.orig go.mod -go get -d rsc.io/fortune@v1.0.0 # note: must use test proxy, does not exist in real world +go get rsc.io/fortune@v1.0.0 # note: must use test proxy, does not exist in real world rm $GOPATH/pkg/mod/cache/download/sumdb # rm sumdb cache but NOT package download cache rm go.sum # can fetch by explicit URL cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-direct -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -21,7 +21,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=direct -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr 'verifying module: rsc.io/fortune@v1.0.0: .*: no such host localhost.localdev' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -30,7 +30,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-404 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr 'verifying.*localhost.localdev' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -39,7 +39,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-503 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr '503 Service Unavailable' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -48,7 +48,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-404,$proxy/sumdb-503 -! go get -d rsc.io/fortune@v1.0.0 +! go get rsc.io/fortune@v1.0.0 stderr '503 Service Unavailable' rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum @@ -57,7 +57,7 @@ rm go.sum cp go.mod.orig go.mod env GOSUMDB=$sumdb env GOPROXY=$proxy/sumdb-503|https://0.0.0.0|$proxy -go get -d rsc.io/fortune@v1.0.0 +go get rsc.io/fortune@v1.0.0 rm $GOPATH/pkg/mod/cache/download/sumdb rm go.sum diff --git a/src/cmd/go/testdata/script/mod_symlink.txt b/src/cmd/go/testdata/script/mod_symlink.txt index dbc23fb8f09847..0604e1a4c4f1c2 100644 --- a/src/cmd/go/testdata/script/mod_symlink.txt +++ b/src/cmd/go/testdata/script/mod_symlink.txt @@ -1,12 +1,12 @@ env GO111MODULE=on [!symlink] skip -# 'go get -d' should resolve modules of imported packages. -go get -d +# 'go get' should resolve modules of imported packages. +go get go list -deps -f '{{.Module}}' . stdout golang.org/x/text -go get -d ./subpkg +go get ./subpkg go list -deps -f '{{.Module}}' ./subpkg stdout golang.org/x/text diff --git a/src/cmd/go/testdata/script/mod_tidy_compat.txt b/src/cmd/go/testdata/script/mod_tidy_compat.txt index e6edef5ee3b78c..18b297da60e1a2 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat.txt @@ -20,7 +20,7 @@ env MODFMT='{{with .Module}}{{.Path}} {{.Version}}{{end}}' # + ---- example.net/lazy v0.1.0 ---- example.com/version v1.0.1 # # Go 1.17 avoids loading the go.mod file for example.com/version v1.0.1 -# (because it is lower than the verison explicitly required by m, +# (because it is lower than the version explicitly required by m, # and the module that requires it — m — specifies 'go 1.17'). # # That go.mod file happens not to affect the final 1.16 module graph anyway, @@ -50,7 +50,7 @@ cmp stdout m_all.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' +stderr '^go: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt index c544cb7413fcdc..a45de5ad8cdcb8 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt @@ -62,7 +62,7 @@ cmp stdout all-m.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' +stderr '^go: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' -- go.mod -- @@ -72,10 +72,9 @@ go 1.17 replace example.net/indirect v0.1.0 => ./indirect -require ( - example.net/ambiguous/nested v0.1.0 // indirect - example.net/indirect v0.1.0 -) +require example.net/indirect v0.1.0 + +require example.net/ambiguous/nested v0.1.0 // indirect -- all-m.txt -- example.com/m example.net/ambiguous v0.1.0 diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt index ea9e42e87e20fc..11313f144c3a34 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt @@ -97,10 +97,9 @@ replace ( example.net/requireincompatible v0.1.0 => ./requireincompatible ) -require ( - example.com/retract/incompatible v1.0.0 // indirect - example.net/lazy v0.1.0 -) +require example.net/lazy v0.1.0 + +require example.com/retract/incompatible v1.0.0 // indirect -- incompatible.go -- package incompatible diff --git a/src/cmd/go/testdata/script/mod_tidy_convergence.txt b/src/cmd/go/testdata/script/mod_tidy_convergence.txt index 09c46f764bf06f..be0a8e9b8c5914 100644 --- a/src/cmd/go/testdata/script/mod_tidy_convergence.txt +++ b/src/cmd/go/testdata/script/mod_tidy_convergence.txt @@ -79,7 +79,7 @@ cmp go.mod go.mod.tidye # succeed and remain stable. y.1 does not upgrade x, and can therefore be used # with it. -go get -d example.net/x@v0.1.0 example.net/y@v0.1.0 +go get example.net/x@v0.1.0 example.net/y@v0.1.0 go mod tidy cmp go.mod go.mod.postget @@ -96,7 +96,7 @@ cmp go.mod go.mod.tidye stderr '^go: found example\.net/y in example\.net/y v0.2.0$' stderr '^example\.net/m imports\n\texample\.net/x: package example\.net/x provided by example\.net/x at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$' -go get -d example.net/x@v0.1.0 example.net/y@v0.1.0 +go get example.net/x@v0.1.0 example.net/y@v0.1.0 go mod tidy cmp go.mod go.mod.postget-117 diff --git a/src/cmd/go/testdata/script/mod_tidy_convergence_loop.txt b/src/cmd/go/testdata/script/mod_tidy_convergence_loop.txt index 3c4d3244d5daa4..99599e551a1d6c 100644 --- a/src/cmd/go/testdata/script/mod_tidy_convergence_loop.txt +++ b/src/cmd/go/testdata/script/mod_tidy_convergence_loop.txt @@ -94,7 +94,7 @@ cmp go.mod go.mod.orig # packages simultaneously over-upgrades all of the dependencies, and 'go mod # tidy' treats "no package can be added" as a terminal state. -go get -d example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre +go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre go mod tidy -e cmp go.mod go.mod.postget go mod tidy -e @@ -154,7 +154,7 @@ cmp go.mod go.mod.117 # As in the eager case, for the lazy module the fully-upgraded dependency graph # becomes empty, and the empty graph is stable. -go get -d example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre +go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre go mod tidy -e cmp go.mod go.mod.postget go mod tidy -e diff --git a/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt new file mode 100644 index 00000000000000..8b508c7ea8a212 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt @@ -0,0 +1,58 @@ +# Verifies golang.org/issue/47738. + +# In this test, the user has rewritten their imports to use rsc.io/quote/v3, +# but their go.mod still requires rsc.io/quote@v1.5.2, and they indirectly +# require rsc.io/quote@v1.5.1 but don't import anything from it. +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2$' +! stdout 'rsc.io/quote/v3' +go list -e all +! stdout '^rsc.io/quote$' + +# 'go mod tidy' should preserve the requirement on rsc.io/quote but mark it +# indirect. This prevents a downgrade to v1.5.1, which could introduce +# an ambiguity. +go mod tidy +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2 indirect$' +stdout '^rsc.io/quote/v3@v3.0.0$' + +-- go.mod -- +module use + +go 1.16 + +require ( + old-indirect v0.0.0 + rsc.io/quote v1.5.2 +) + +replace old-indirect v0.0.0 => ./old-indirect +-- go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.1/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- use.go -- +package use + +import ( + _ "old-indirect/empty" + + _ "rsc.io/quote/v3" +) +-- old-indirect/empty/empty.go -- +package empty +-- old-indirect/go.mod -- +module old-indirect + +go 1.16 + +require rsc.io/quote v1.5.1 +-- old-indirect/go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/src/cmd/go/testdata/script/mod_tidy_duplicates.txt b/src/cmd/go/testdata/script/mod_tidy_duplicates.txt new file mode 100644 index 00000000000000..d454c8dc829b21 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_tidy_duplicates.txt @@ -0,0 +1,38 @@ +env GO111MODULE=on + +# Regression test for golang.org/issue/28456: +# 'go mod tidy' should not leave duplicate lines when re-writing the file. + +go mod tidy +cmp go.sum golden.sum + +-- go.mod -- +module use + +go 1.16 + +require rsc.io/quote v1.5.2 + +-- go.sum -- +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/testonly v1.0.0 h1:K/VWHdO+Jv7woUXG0GzVNx1czBXUt3Ib1deaMn+xk64= +rsc.io/testonly v1.0.0/go.mod h1:OqmGbIFOcF+XrFReLOGZ6BhMM7uMBiQwZsyNmh74SzY= +-- golden.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/testonly v1.0.0 h1:K/VWHdO+Jv7woUXG0GzVNx1czBXUt3Ib1deaMn+xk64= +rsc.io/testonly v1.0.0/go.mod h1:OqmGbIFOcF+XrFReLOGZ6BhMM7uMBiQwZsyNmh74SzY= +-- main.go -- +package use + +import _ "rsc.io/quote" diff --git a/src/cmd/go/testdata/script/mod_tidy_error.txt b/src/cmd/go/testdata/script/mod_tidy_error.txt index 395537b1a71779..51fc65fa7a8572 100644 --- a/src/cmd/go/testdata/script/mod_tidy_error.txt +++ b/src/cmd/go/testdata/script/mod_tidy_error.txt @@ -10,8 +10,8 @@ stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/non ! go mod vendor ! stderr 'package nonexist is not in GOROOT' -stderr '^issue27063 imports\n\tnonexist.example.com: cannot find module providing package nonexist.example.com' -stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/nonexist: cannot find module providing package other.example.com/nonexist' +stderr '^issue27063 imports\n\tnonexist.example.com: no required module provides package nonexist.example.com; to add it:\n\tgo get nonexist.example.com$' +stderr '^issue27063 imports\n\tissue27063/other imports\n\tother.example.com/nonexist: no required module provides package other.example.com/nonexist; to add it:\n\tgo get other.example.com/nonexist$' -- go.mod -- module issue27063 diff --git a/src/cmd/go/testdata/script/mod_tidy_replace.txt b/src/cmd/go/testdata/script/mod_tidy_replace.txt index 297f6a6a45c872..274f3bf5090f63 100644 --- a/src/cmd/go/testdata/script/mod_tidy_replace.txt +++ b/src/cmd/go/testdata/script/mod_tidy_replace.txt @@ -35,7 +35,7 @@ grep 'golang.org/x/text' go.mod # 'go get' and 'go mod tidy' should follow the requirements of the replacements, # not the originals, even if that results in a set of versions that are # misleading or redundant without those replacements. -go get -d rsc.io/sampler@v1.2.0 +go get rsc.io/sampler@v1.2.0 go mod tidy go list -m all stdout 'rsc.io/quote/v3 v3.0.0' diff --git a/src/cmd/go/testdata/script/mod_tidy_sum.txt b/src/cmd/go/testdata/script/mod_tidy_sum.txt index c583f560586970..5a15818543ef2c 100644 --- a/src/cmd/go/testdata/script/mod_tidy_sum.txt +++ b/src/cmd/go/testdata/script/mod_tidy_sum.txt @@ -1,12 +1,12 @@ env GO111MODULE=on # go.sum should list directly used modules and dependencies -go get -d rsc.io/quote@v1.5.2 +go get rsc.io/quote@v1.5.2 go mod tidy grep rsc.io/sampler go.sum # go.sum should not normally lose old entries -go get -d rsc.io/quote@v1.0.0 +go get rsc.io/quote@v1.0.0 grep 'rsc.io/quote v1.0.0' go.sum grep 'rsc.io/quote v1.5.2' go.sum grep rsc.io/sampler go.sum diff --git a/src/cmd/go/testdata/script/mod_tidy_temp.txt b/src/cmd/go/testdata/script/mod_tidy_temp.txt new file mode 100644 index 00000000000000..635a336bcaf0fe --- /dev/null +++ b/src/cmd/go/testdata/script/mod_tidy_temp.txt @@ -0,0 +1,26 @@ +# Regression test for https://go.dev/issue/51992 + +# 'go mod tidy' should error instead of throwing panic in the situation below. +# 1. /tmp/go.mod exists +# 2. run 'go mod tidy' in /tmp or in the child directory not having go.mod. + +[plan9] stop # Plan 9 has no $TMPDIR variable to set. + +env GOROOT=$TESTGO_GOROOT +env TMP=$WORK +env TMPDIR=$WORK +mkdir $WORK/child + +! go mod tidy +! stdout . +stderr 'go: go.mod file not found in current directory or any parent directory' + +cd $WORK/child +! go mod tidy +! stdout . +stderr 'go: go.mod file not found in current directory or any parent directory' + +-- $WORK/go.mod -- +module issue51992 + +go 1.18 diff --git a/src/cmd/go/testdata/script/mod_tidy_too_new.txt b/src/cmd/go/testdata/script/mod_tidy_too_new.txt index b9c53b510daeb0..8c34a997c956a5 100644 --- a/src/cmd/go/testdata/script/mod_tidy_too_new.txt +++ b/src/cmd/go/testdata/script/mod_tidy_too_new.txt @@ -9,7 +9,7 @@ cp go.mod go.mod.orig # would look like. ! go mod tidy -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.orig @@ -18,7 +18,7 @@ cmp go.mod go.mod.orig cp go.mod.orig go.mod go mod tidy -e -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.tidy diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch.txt b/src/cmd/go/testdata/script/mod_upgrade_patch.txt index 8b34f8bf27dfc6..79a9808ef03796 100644 --- a/src/cmd/go/testdata/script/mod_upgrade_patch.txt +++ b/src/cmd/go/testdata/script/mod_upgrade_patch.txt @@ -2,7 +2,7 @@ env GO111MODULE=on [short] skip # Initially, we are at v1.0.0 for all dependencies. -go get -d +go get cp go.mod go.mod.orig go list -m all stdout '^patch.example.com/direct v1.0.0' @@ -10,15 +10,15 @@ stdout '^patch.example.com/indirect v1.0.0' ! stdout '^patch.example.com/depofdirectpatch' # @patch should be rejected for modules not already in the build list. -! go get -d patch.example.com/depofdirectpatch@patch -stderr '^go get: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' +! go get patch.example.com/depofdirectpatch@patch +stderr '^go: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' cmp go.mod.orig go.mod # get -u=patch, with no arguments, should patch-update all dependencies # of the package in the current directory, pulling in transitive dependencies # and also patching those. cp go.mod.orig go.mod -go get -d -u=patch +go get -u=patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -26,7 +26,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # 'get all@patch' should patch the modules that provide packages in 'all'. cp go.mod.orig go.mod -go get -d all@patch +go get all@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -37,14 +37,14 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' cp go.mod.orig go.mod go mod edit -droprequire=patch.example.com/direct cp go.mod go.mod.dropped -! go get -d all@patch -stderr '^go get all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' +! go get all@patch +stderr '^go: all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' cmp go.mod.dropped go.mod # Requesting the direct dependency with -u=patch but without an explicit version # should patch-update it and its dependencies. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/direct +go get -u=patch patch.example.com/direct go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.1' @@ -52,7 +52,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # Requesting only the indirect dependency should not update the direct one. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/indirect +go get -u=patch patch.example.com/indirect go list -m all stdout '^patch.example.com/direct v1.0.0' stdout '^patch.example.com/indirect v1.0.1' @@ -61,7 +61,7 @@ stdout '^patch.example.com/indirect v1.0.1' # @patch should apply only to the specific module, # but the result must reflect its upgraded requirements. cp go.mod.orig go.mod -go get -d patch.example.com/direct@patch +go get patch.example.com/direct@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.0.0' @@ -69,7 +69,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # An explicit @patch should override a general -u. cp go.mod.orig go.mod -go get -d -u patch.example.com/direct@patch +go get -u patch.example.com/direct@patch go list -m all stdout '^patch.example.com/direct v1.0.1' stdout '^patch.example.com/indirect v1.1.0' @@ -77,7 +77,7 @@ stdout '^patch.example.com/depofdirectpatch v1.0.0' # An explicit @latest should override a general -u=patch. cp go.mod.orig go.mod -go get -d -u=patch patch.example.com/direct@latest +go get -u=patch patch.example.com/direct@latest go list -m all stdout '^patch.example.com/direct v1.1.0' stdout '^patch.example.com/indirect v1.0.1' @@ -86,16 +86,16 @@ stdout '^patch.example.com/indirect v1.0.1' # Standard library packages cannot be upgraded explicitly. cp go.mod.orig go.mod ! go get cmd/vet@patch -stderr 'go get: can''t request explicit version "patch" of standard library package cmd/vet$' +stderr 'go: can''t request explicit version "patch" of standard library package cmd/vet$' # However, standard-library packages without explicit versions are fine. -go get -d -u=patch -d cmd/go +go get -u=patch cmd/go # We can upgrade to a new version of a module with no root package. -go get -d example.com/noroot@v1.0.0 +go get example.com/noroot@v1.0.0 go list -m all stdout '^example.com/noroot v1.0.0$' -go get -d example.com/noroot@patch +go get example.com/noroot@patch go list -m all stdout '^example.com/noroot v1.0.1$' diff --git a/src/cmd/go/testdata/script/mod_vcs_missing.txt b/src/cmd/go/testdata/script/mod_vcs_missing.txt index f8be43cf4c6494..9e6e371927bef9 100644 --- a/src/cmd/go/testdata/script/mod_vcs_missing.txt +++ b/src/cmd/go/testdata/script/mod_vcs_missing.txt @@ -5,7 +5,7 @@ env GO111MODULE=on env GOPROXY=direct cd empty -! go get -d launchpad.net/gocheck +! go get launchpad.net/gocheck stderr '"bzr": executable file not found' cd .. diff --git a/src/cmd/go/testdata/script/mod_vendor.txt b/src/cmd/go/testdata/script/mod_vendor.txt index 2622916f614140..a2727ddf7f370e 100644 --- a/src/cmd/go/testdata/script/mod_vendor.txt +++ b/src/cmd/go/testdata/script/mod_vendor.txt @@ -40,15 +40,15 @@ stdout '^v1.0.0 $' # -mod=vendor should cause 'go list' flags that look up versions to fail. ! go list -mod=vendor -versions -m x -stderr '^go list -m: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' ! go list -mod=vendor -u -m x -stderr '^go list -m: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' # 'go list -mod=vendor -m' on a transitive dependency that does not # provide vendored packages should give a helpful error rather than # 'not a known dependency'. ! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright -stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go list -mod=mod' should report packages outside the import graph, # but 'go list -mod=vendor' should error out for them. @@ -82,6 +82,48 @@ exists vendor/mysite/myname/mypkg/LICENSE.txt ! exists vendor/x/x2 ! exists vendor/x/x2/LICENSE +# 'go mod vendor' should work with an alternative vendor directory if the -o flag is provided. +go mod vendor -v -o alternative-vendor-dir +exists alternative-vendor-dir/modules.txt +exists alternative-vendor-dir/a/foo/LICENSE + +# 'go mod vendor' should interpret paths relative to the current working directory when the -o flag is provided. +mkdir dir1 +mkdir dir2 + +cd dir1 +go mod vendor -v -o relative-vendor-dir + +go mod vendor -v -o ../dir2/relative-vendor-dir + +cd .. +exists dir1/relative-vendor-dir/modules.txt +exists dir1/relative-vendor-dir/a/foo/LICENSE +exists dir2/relative-vendor-dir/modules.txt +exists dir2/relative-vendor-dir/a/foo/LICENSE + +# 'go mod vendor' should fall back to the default 'vendor' directory when an empty argument is passed to the -o flag +# the same behavior should be exhibited both on the module root directory, as well as nested subdirectories + +go mod vendor -v -o '' +exists vendor/modules.txt + +env GOFLAGS=-o=foo +go mod vendor -v -o '' +exists vendor/modules.txt +env GOFLAGS='' + +mkdir -p nested/dir +cd nested/dir +go mod vendor -v -o '' +! exists vendor/ +exists ../../vendor/modules.txt +cd ../.. + +# 'go mod vendor' should work with absolute paths as well +go mod vendor -v -o $WORK/tmp/absolute-vendor-dir +exists $WORK/tmp/absolute-vendor-dir/modules.txt + [short] stop # 'go build' and 'go test' using vendored packages should succeed. diff --git a/src/cmd/go/testdata/script/mod_vendor_auto.txt b/src/cmd/go/testdata/script/mod_vendor_auto.txt index b0ea907206a449..3cace73a896410 100644 --- a/src/cmd/go/testdata/script/mod_vendor_auto.txt +++ b/src/cmd/go/testdata/script/mod_vendor_auto.txt @@ -17,10 +17,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$' stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # An explicit -mod=mod should force the vendor directory to be ignored. env GOFLAGS=-mod=mod @@ -105,10 +105,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' # ...but 'go list -m' should continue to fail, this time without # referring to a -mod default that the user didn't set. ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go mod init' should work if there is already a GOPATH-mode vendor directory @@ -177,7 +177,7 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' # 'go get' should update from the network or module cache, # even if a vendor directory is present. -go get -d example.com/version@v1.1.0 +go get example.com/version@v1.1.0 ! go list -f {{.Dir}} -tags tools all stderr '^go: inconsistent vendoring' diff --git a/src/cmd/go/testdata/script/mod_vendor_build.txt b/src/cmd/go/testdata/script/mod_vendor_build.txt index 3b8eec0119b3fd..4efda55e08f10f 100644 --- a/src/cmd/go/testdata/script/mod_vendor_build.txt +++ b/src/cmd/go/testdata/script/mod_vendor_build.txt @@ -10,7 +10,7 @@ stdout rsc.io/sampler ! grep 'rsc.io/sampler v1.3.0' go.mod # update to v1.3.1, now indirect in go.mod. -go get -d rsc.io/sampler@v1.3.1 +go get rsc.io/sampler@v1.3.1 grep 'rsc.io/sampler v1.3.1 // indirect' go.mod cp go.mod go.mod.good diff --git a/src/cmd/go/testdata/script/mod_vendor_embed.txt b/src/cmd/go/testdata/script/mod_vendor_embed.txt index be114159a1fbf7..b14fd9915646f3 100644 --- a/src/cmd/go/testdata/script/mod_vendor_embed.txt +++ b/src/cmd/go/testdata/script/mod_vendor_embed.txt @@ -6,11 +6,11 @@ cmp vendor/example.com/a/subdir/test/xtest/embed.txt a/subdir/test/xtest/embed.t cd broken_no_matching_files ! go mod vendor -stderr 'go mod vendor: pattern foo.txt: no matching files found' +stderr 'go: pattern foo.txt: no matching files found' cd ../broken_bad_pattern ! go mod vendor -stderr 'go mod vendor: pattern ../foo.txt: invalid pattern syntax' +stderr 'go: pattern ../foo.txt: invalid pattern syntax' # matchPotentialSourceFile prunes out tests and unbuilt code. # Make sure that they are vendored if they are embedded files. diff --git a/src/cmd/go/testdata/script/mod_vendor_goversion.txt b/src/cmd/go/testdata/script/mod_vendor_goversion.txt index aa4cb41171a519..9e3618a21848ff 100644 --- a/src/cmd/go/testdata/script/mod_vendor_goversion.txt +++ b/src/cmd/go/testdata/script/mod_vendor_goversion.txt @@ -3,7 +3,6 @@ [short] skip - # Control case: without a vendor directory, need117 builds and bad114 doesn't. go build example.net/need117 @@ -26,7 +25,8 @@ go mod vendor ! grep 1.17 vendor/modules.txt ! go build example.net/need117 -stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:18: .*\n\tconversion of slices to array pointers only supported as of -lang=go1\.17' +stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:1[89]:' +stderr 'conversion of slices to array pointers requires go1\.17 or later' ! grep 1.13 vendor/modules.txt go build example.net/bad114 diff --git a/src/cmd/go/testdata/script/mod_vendor_replace.txt b/src/cmd/go/testdata/script/mod_vendor_replace.txt index 0c1c1d22f5bff1..1820af62ad809b 100644 --- a/src/cmd/go/testdata/script/mod_vendor_replace.txt +++ b/src/cmd/go/testdata/script/mod_vendor_replace.txt @@ -36,7 +36,6 @@ module example.com/replace require rsc.io/quote/v3 v3.0.0 replace rsc.io/quote/v3 => ./local/not-rsc.io/quote/v3 - -- imports.go -- package replace @@ -64,3 +63,7 @@ require ( not-rsc.io/quote/v3 v3.0.0 ) replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0 +-- multiple-paths/go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote/v3 v3.0.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/src/cmd/go/testdata/script/mod_vendor_trimpath.txt b/src/cmd/go/testdata/script/mod_vendor_trimpath.txt index 5451aa773c8b5e..d9d9d9889741cd 100644 --- a/src/cmd/go/testdata/script/mod_vendor_trimpath.txt +++ b/src/cmd/go/testdata/script/mod_vendor_trimpath.txt @@ -29,8 +29,12 @@ stdout '^example.com/stack@v1.0.0/stack.go$' -- go.mod -- module example.com/main -require example.com/stack v1.0.0 +go 1.17 +require example.com/stack v1.0.0 +-- go.sum -- +example.com/stack v1.0.0 h1:IEDLeew5NytZ8vrgCF/QVem3H3SR3QMttdu9HfJvk9I= +example.com/stack v1.0.0/go.mod h1:7wFEbaV5e5O7wJ8aBdqQOR//UXppm/pwnwziMKViuI4= -- main.go -- package main diff --git a/src/cmd/go/testdata/script/mod_vendor_unused_only.txt b/src/cmd/go/testdata/script/mod_vendor_unused_only.txt index 839c6453cf825e..accd9f373deb0e 100644 --- a/src/cmd/go/testdata/script/mod_vendor_unused_only.txt +++ b/src/cmd/go/testdata/script/mod_vendor_unused_only.txt @@ -12,6 +12,8 @@ module example.com/m go 1.14 require example.com v1.0.0 // indirect +-- go.sum -- +example.com v1.0.0/go.mod h1:WRiieAqDBb1hVdDXLLdxNtCDWNfehn7FWyPC5Oz2vB4= -- go1.14-modules.txt -- # example.com v1.0.0 ## explicit diff --git a/src/cmd/go/testdata/script/mod_verify.txt b/src/cmd/go/testdata/script/mod_verify.txt index b5106659a9a9ba..018709e33b42e3 100644 --- a/src/cmd/go/testdata/script/mod_verify.txt +++ b/src/cmd/go/testdata/script/mod_verify.txt @@ -39,11 +39,6 @@ stderr 'go.mod: checksum mismatch' # go.sum should be created and updated automatically. rm go.sum -go mod graph -exists go.sum -grep '^rsc.io/quote v1.1.0/go.mod ' go.sum -! grep '^rsc.io/quote v1.1.0 ' go.sum - go mod tidy grep '^rsc.io/quote v1.1.0/go.mod ' go.sum grep '^rsc.io/quote v1.1.0 ' go.sum @@ -59,7 +54,7 @@ go mod verify # Packages below module root should not be mentioned in go.sum. rm go.sum go mod edit -droprequire rsc.io/quote -go get -d rsc.io/quote/buggy +go get rsc.io/quote/buggy grep '^rsc.io/quote v1.5.2/go.mod ' go.sum ! grep buggy go.sum diff --git a/src/cmd/go/testdata/script/modfile_flag.txt b/src/cmd/go/testdata/script/modfile_flag.txt index 0ad08808178316..398e523a9ceb65 100644 --- a/src/cmd/go/testdata/script/modfile_flag.txt +++ b/src/cmd/go/testdata/script/modfile_flag.txt @@ -24,6 +24,11 @@ stdout '^go.alt.mod$' go mod edit -require rsc.io/quote@v1.5.2 grep rsc.io/quote go.alt.mod +# 'go list -m' should add sums to the alternate go.sum. +go list -m -mod=mod all +grep '^rsc.io/quote v1.5.2/go.mod ' go.alt.sum +! grep '^rsc.io/quote v1.5.2 ' go.alt.sum + # other 'go mod' commands should work. 'go mod vendor' is tested later. go mod download rsc.io/quote go mod graph @@ -41,7 +46,7 @@ go list -mod=mod . grep rsc.io/quote go.alt.mod go build -n -mod=mod . go test -n -mod=mod . -go get -d rsc.io/quote +go get rsc.io/quote # 'go mod vendor' should work. @@ -73,7 +78,7 @@ cmp go.mod go.mod.orig cmp go.sum go.sum.orig -# If the altnernate mod file does not have a ".mod" suffix, an error +# If the alternate mod file does not have a ".mod" suffix, an error # should be reported. cp go.alt.mod goaltmod ! go mod tidy -modfile=goaltmod diff --git a/src/cmd/go/testdata/script/reuse_git.txt b/src/cmd/go/testdata/script/reuse_git.txt new file mode 100644 index 00000000000000..a5a0c8a9a0bd90 --- /dev/null +++ b/src/cmd/go/testdata/script/reuse_git.txt @@ -0,0 +1,425 @@ +[short] skip +[!exec:git] skip +[!net] skip + +env GO111MODULE=on +env GOPROXY=direct +env GOSUMDB=off + +# go mod download with the pseudo-version should invoke git but not have a TagSum or Ref. +go mod download -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20170922010558-fc3a09f3dc5c +stderr 'git fetch' +cp stdout hellopseudo.json +! stdout '"(Query|TagPrefix|TagSum|Ref)"' +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' +go clean -modcache + +# go mod download vcstest/hello should invoke git, print origin info +go mod download -x -json vcs-test.golang.org/git/hello.git@latest +stderr 'git fetch' +cp stdout hello.json +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +stdout '"Query": "latest"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +stdout '"Ref": "HEAD"' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' + +# pseudo-version again should not invoke git fetch (it has the version from the @latest query) +# but still be careful not to include a TagSum or a Ref, especially not Ref set to HEAD, +# which is easy to do when reusing the cached version from the @latest query. +go mod download -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20170922010558-fc3a09f3dc5c +! stderr 'git fetch' +cp stdout hellopseudo2.json +cmp hellopseudo.json hellopseudo2.json + +# go mod download vcstest/hello@hash needs to check TagSum to find pseudoversion base. +go mod download -x -json vcs-test.golang.org/git/hello.git@fc3a09f3dc5c +! stderr 'git fetch' +cp stdout hellohash.json +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"Query": "fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' + +# go mod download vcstest/hello/v9 should fail, still print origin info +! go mod download -x -json vcs-test.golang.org/git/hello.git/v9@latest +cp stdout hellov9.json +stdout '"Version": "latest"' +stdout '"Error":.*no matching versions' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +! stdout '"(Ref|Hash|RepoSum)":' + +# go mod download vcstest/hello/sub/v9 should also fail, print origin info with TagPrefix +! go mod download -x -json vcs-test.golang.org/git/hello.git/sub/v9@latest +cp stdout hellosubv9.json +stdout '"Version": "latest"' +stdout '"Error":.*no matching versions' +stdout '"TagPrefix": "sub/"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +! stdout '"(Ref|Hash|RepoSum)":' + +# go mod download vcstest/hello@nonexist should fail, still print origin info +! go mod download -x -json vcs-test.golang.org/git/hello.git@nonexist +cp stdout hellononexist.json +stdout '"Version": "nonexist"' +stdout '"Error":.*unknown revision nonexist' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' + +# go mod download vcstest/hello@1234567890123456789012345678901234567890 should fail, still print origin info +# (40 hex digits is assumed to be a full hash and is a slightly different code path from @nonexist) +! go mod download -x -json vcs-test.golang.org/git/hello.git@1234567890123456789012345678901234567890 +cp stdout hellononhash.json +stdout '"Version": "1234567890123456789012345678901234567890"' +stdout '"Error":.*unknown revision 1234567890123456789012345678901234567890' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' + +# go mod download vcstest/hello@v0.0.0-20220101120101-123456789abc should fail, still print origin info +# (non-existent pseudoversion) +! go mod download -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20220101120101-123456789abc +cp stdout hellononpseudo.json +stdout '"Version": "v0.0.0-20220101120101-123456789abc"' +stdout '"Error":.*unknown revision 123456789abc' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' + +# go mod download vcstest/tagtests should invoke git, print origin info +go mod download -x -json vcs-test.golang.org/git/tagtests.git@latest +stderr 'git fetch' +cp stdout tagtests.json +stdout '"Version": "v0.2.2"' +stdout '"Query": "latest"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' +stdout '"Ref": "refs/tags/v0.2.2"' +stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"' + +# go mod download vcstest/tagtests@v0.2.2 should print origin info, no TagSum needed +go mod download -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +cp stdout tagtestsv022.json +stdout '"Version": "v0.2.2"' +! stdout '"Query":' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +! stdout '"TagSum"' +stdout '"Ref": "refs/tags/v0.2.2"' +stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"' + +# go mod download vcstest/tagtests@master needs a TagSum again +go mod download -x -json vcs-test.golang.org/git/tagtests.git@master +cp stdout tagtestsmaster.json +stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"' +stdout '"Query": "master"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' +stdout '"Ref": "refs/heads/master"' +stdout '"Hash": "c7818c24fa2f3f714c67d0a6d3e411c85a518d1f"' + +# go mod download vcstest/prefixtagtests should invoke git, print origin info +go mod download -x -json vcs-test.golang.org/git/prefixtagtests.git/sub@latest +stderr 'git fetch' +cp stdout prefixtagtests.json +stdout '"Version": "v0.0.10"' +stdout '"Query": "latest"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/prefixtagtests"' +stdout '"Subdir": "sub"' +stdout '"TagPrefix": "sub/"' +stdout '"TagSum": "t1:YGSbWkJ8dn9ORAr[+]BlKHFK/2ZhXLb9hVuYfTZ9D8C7g="' +stdout '"Ref": "refs/tags/sub/v0.0.10"' +stdout '"Hash": "2b7c4692e12c109263cab51b416fcc835ddd7eae"' + +# go mod download of a bunch of these should fail (some are invalid) but write good JSON for later +! go mod download -json vcs-test.golang.org/git/hello.git@latest vcs-test.golang.org/git/hello.git/v9@latest vcs-test.golang.org/git/hello.git/sub/v9@latest vcs-test.golang.org/git/tagtests.git@latest vcs-test.golang.org/git/tagtests.git@v0.2.2 vcs-test.golang.org/git/tagtests.git@master +cp stdout all.json + +# clean the module cache, make sure that makes go mod download re-run git fetch, clean again +go clean -modcache +go mod download -x -json vcs-test.golang.org/git/hello.git@latest +stderr 'git fetch' +go clean -modcache + +# reuse go mod download vcstest/hello result +go mod download -reuse=hello.json -x -json vcs-test.golang.org/git/hello.git@latest +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +stdout '"Ref": "HEAD"' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' +! stdout '"Dir"' +! stdout '"Info"' +! stdout '"GoMod"' +! stdout '"Zip"' + +# reuse go mod download vcstest/hello pseudoversion result +go mod download -reuse=hellopseudo.json -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20170922010558-fc3a09f3dc5c +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +! stdout '"(Query|TagPrefix|TagSum|Ref)"' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello@hash +go mod download -reuse=hellohash.json -x -json vcs-test.golang.org/git/hello.git@fc3a09f3dc5c +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Query": "fc3a09f3dc5c"' +stdout '"Version": "v0.0.0-20170922010558-fc3a09f3dc5c"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/hello"' +! stdout '"(TagPrefix|Ref)"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +stdout '"Hash": "fc3a09f3dc5cfe0d7a743ea18f1f5226e68b3777"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello/v9 error result +! go mod download -reuse=hellov9.json -x -json vcs-test.golang.org/git/hello.git/v9@latest +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Error":.*no matching versions' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +! stdout '"(Ref|Hash)":' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello/sub/v9 error result +! go mod download -reuse=hellosubv9.json -x -json vcs-test.golang.org/git/hello.git/sub/v9@latest +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Error":.*no matching versions' +stdout '"TagPrefix": "sub/"' +stdout '"TagSum": "t1:47DEQpj8HBSa[+]/TImW[+]5JCeuQeRkm5NMpJWZG3hSuFU="' +! stdout '"(Ref|Hash)":' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello@nonexist +! go mod download -reuse=hellononexist.json -x -json vcs-test.golang.org/git/hello.git@nonexist +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "nonexist"' +stdout '"Error":.*unknown revision nonexist' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello@1234567890123456789012345678901234567890 +! go mod download -reuse=hellononhash.json -x -json vcs-test.golang.org/git/hello.git@1234567890123456789012345678901234567890 +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "1234567890123456789012345678901234567890"' +stdout '"Error":.*unknown revision 1234567890123456789012345678901234567890' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/hello@v0.0.0-20220101120101-123456789abc +! go mod download -reuse=hellononpseudo.json -x -json vcs-test.golang.org/git/hello.git@v0.0.0-20220101120101-123456789abc +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.0.0-20220101120101-123456789abc"' +stdout '"Error":.*unknown revision 123456789abc' +stdout '"RepoSum": "r1:c0/9JCZ25lxoBiK3[+]3BhACU4giH49flcJmBynJ[+]Jvmc="' +! stdout '"(TagPrefix|TagSum|Ref|Hash)"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/tagtests result +go mod download -reuse=tagtests.json -x -json vcs-test.golang.org/git/tagtests.git@latest +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.2.2"' +stdout '"Query": "latest"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' +stdout '"Ref": "refs/tags/v0.2.2"' +stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/tagtests@v0.2.2 result +go mod download -reuse=tagtestsv022.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.2.2"' +! stdout '"Query":' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +! stdout '"TagSum"' +stdout '"Ref": "refs/tags/v0.2.2"' +stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/tagtests@master result +go mod download -reuse=tagtestsmaster.json -x -json vcs-test.golang.org/git/tagtests.git@master +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"' +stdout '"Query": "master"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' +stdout '"Ref": "refs/heads/master"' +stdout '"Hash": "c7818c24fa2f3f714c67d0a6d3e411c85a518d1f"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse go mod download vcstest/tagtests@master result again with all.json +go mod download -reuse=all.json -x -json vcs-test.golang.org/git/tagtests.git@master +! stderr 'git fetch' +stdout '"Reuse": true' +stdout '"Version": "v0.2.3-0.20190509225625-c7818c24fa2f"' +stdout '"Query": "master"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"TagPrefix"' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' +stdout '"Ref": "refs/heads/master"' +stdout '"Hash": "c7818c24fa2f3f714c67d0a6d3e411c85a518d1f"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# go mod download vcstest/prefixtagtests result with json +go mod download -reuse=prefixtagtests.json -x -json vcs-test.golang.org/git/prefixtagtests.git/sub@latest +! stderr 'git fetch' +stdout '"Version": "v0.0.10"' +stdout '"Query": "latest"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/prefixtagtests"' +stdout '"Subdir": "sub"' +stdout '"TagPrefix": "sub/"' +stdout '"TagSum": "t1:YGSbWkJ8dn9ORAr[+]BlKHFK/2ZhXLb9hVuYfTZ9D8C7g="' +stdout '"Ref": "refs/tags/sub/v0.0.10"' +stdout '"Hash": "2b7c4692e12c109263cab51b416fcc835ddd7eae"' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse the bulk results with all.json +! go mod download -reuse=all.json -json vcs-test.golang.org/git/hello.git@latest vcs-test.golang.org/git/hello.git/v9@latest vcs-test.golang.org/git/hello.git/sub/v9@latest vcs-test.golang.org/git/tagtests.git@latest vcs-test.golang.org/git/tagtests.git@v0.2.2 vcs-test.golang.org/git/tagtests.git@master +! stderr 'git fetch' +stdout '"Reuse": true' +! stdout '"(Dir|Info|GoMod|Zip)"' + +# reuse attempt with stale hash should reinvoke git, not report reuse +go mod download -reuse=tagtestsv022badhash.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +stderr 'git fetch' +! stdout '"Reuse": true' +stdout '"Version": "v0.2.2"' +! stdout '"Query"' +stdout '"VCS": "git"' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +! stdout '"(TagPrefix|TagSum)"' +stdout '"Ref": "refs/tags/v0.2.2"' +stdout '"Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952"' +stdout '"Dir"' +stdout '"Info"' +stdout '"GoMod"' +stdout '"Zip"' + +# reuse with stale repo URL +go mod download -reuse=tagtestsv022badurl.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +! stdout '"Reuse": true' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' +stdout '"Dir"' +stdout '"Info"' +stdout '"GoMod"' +stdout '"Zip"' + +# reuse with stale VCS +go mod download -reuse=tagtestsv022badvcs.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +! stdout '"Reuse": true' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' + +# reuse with stale Dir +go mod download -reuse=tagtestsv022baddir.json -x -json vcs-test.golang.org/git/tagtests.git@v0.2.2 +! stdout '"Reuse": true' +stdout '"URL": "https://vcs-test.golang.org/git/tagtests"' + +# reuse with stale TagSum +go mod download -reuse=tagtestsbadtagsum.json -x -json vcs-test.golang.org/git/tagtests.git@latest +! stdout '"Reuse": true' +stdout '"TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo="' + +-- tagtestsv022badhash.json -- +{ + "Path": "vcs-test.golang.org/git/tagtests.git", + "Version": "v0.2.2", + "Origin": { + "VCS": "git", + "URL": "https://vcs-test.golang.org/git/tagtests", + "Ref": "refs/tags/v0.2.2", + "Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952XXX" + } +} + +-- tagtestsbadtagsum.json -- +{ + "Path": "vcs-test.golang.org/git/tagtests.git", + "Version": "v0.2.2", + "Query": "latest", + "Origin": { + "VCS": "git", + "URL": "https://vcs-test.golang.org/git/tagtests", + "TagSum": "t1:Dp7yRKDuE8WjG0429PN9hYWjqhy2te7P9Oki/sMEOGo=XXX", + "Ref": "refs/tags/v0.2.2", + "Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952" + }, + "Reuse": true +} + +-- tagtestsv022badvcs.json -- +{ + "Path": "vcs-test.golang.org/git/tagtests.git", + "Version": "v0.2.2", + "Origin": { + "VCS": "gitXXX", + "URL": "https://vcs-test.golang.org/git/tagtests", + "Ref": "refs/tags/v0.2.2", + "Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952" + } +} + +-- tagtestsv022baddir.json -- +{ + "Path": "vcs-test.golang.org/git/tagtests.git", + "Version": "v0.2.2", + "Origin": { + "VCS": "git", + "URL": "https://vcs-test.golang.org/git/tagtests", + "Subdir": "subdir", + "Ref": "refs/tags/v0.2.2", + "Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952" + } +} + +-- tagtestsv022badurl.json -- +{ + "Path": "vcs-test.golang.org/git/tagtests.git", + "Version": "v0.2.2", + "Origin": { + "VCS": "git", + "URL": "https://vcs-test.golang.org/git/tagtestsXXX", + "Ref": "refs/tags/v0.2.2", + "Hash": "59356c8cd18c5fe9a598167d98a6843e52d57952" + } +} diff --git a/src/cmd/go/testdata/script/run_dirs.txt b/src/cmd/go/testdata/script/run_dirs.txt index 538a6ac6f39cc8..bd5cfbe3fb272c 100644 --- a/src/cmd/go/testdata/script/run_dirs.txt +++ b/src/cmd/go/testdata/script/run_dirs.txt @@ -1,11 +1,21 @@ cd rundir ! go run x.go sub/sub.go -stderr 'named files must all be in one directory; have ./ and sub/' +stderr 'named files must all be in one directory; have . and sub' ! go run sub/sub.go x.go -stderr 'named files must all be in one directory; have sub/ and ./' +stderr 'named files must all be in one directory; have sub and .' + +cd ../ +go run rundir/foo.go ./rundir/bar.go +stderr 'hello world' -- rundir/sub/sub.go -- package main -- rundir/x.go -- package main +-- rundir/foo.go -- +package main +func main() { println(msg) } +-- rundir/bar.go -- +package main +const msg = "hello world" diff --git a/src/cmd/go/testdata/script/run_issue51125.txt b/src/cmd/go/testdata/script/run_issue51125.txt new file mode 100644 index 00000000000000..8fa4486ca4186d --- /dev/null +++ b/src/cmd/go/testdata/script/run_issue51125.txt @@ -0,0 +1,54 @@ +# Regression test for https://go.dev/issue/51125: +# Relative import paths (a holdover from GOPATH) were accidentally allowed in module mode. + +cd $WORK + +# Relative imports should not be allowed with a go.mod file. + +! go run driver.go +stderr '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$' + +go list -e -f '{{with .Error}}{{.}}{{end}}' -deps driver.go +stdout '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$' +! stderr . + + +# Relative imports should not be allowed in module mode even without a go.mod file. +rm go.mod + +! go run driver.go +stderr '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$' + +go list -e -f '{{with .Error}}{{.}}{{end}}' -deps driver.go +stdout '^driver.go:3:8: "./mypkg" is relative, but relative import paths are not supported in module mode$' +! stderr . + + +# In GOPATH mode, they're still allowed (but only outside of GOPATH/src). +env GO111MODULE=off + +[!short] go run driver.go + +go list -deps driver.go + + +-- $WORK/go.mod -- +module example + +go 1.17 +-- $WORK/driver.go -- +package main + +import "./mypkg" + +func main() { + mypkg.MyFunc() +} +-- $WORK/mypkg/code.go -- +package mypkg + +import "fmt" + +func MyFunc() { + fmt.Println("Hello, world!") +} diff --git a/src/cmd/go/testdata/script/run_wildcard.txt b/src/cmd/go/testdata/script/run_wildcard.txt index 72036d1d8dbdc7..3e7e7b7e42f306 100644 --- a/src/cmd/go/testdata/script/run_wildcard.txt +++ b/src/cmd/go/testdata/script/run_wildcard.txt @@ -4,4 +4,4 @@ env GO111MODULE=off # go run x/... should not panic when directory x doesn't exist. ! go run nonexistent/... -stderr '^go run: no packages loaded from nonexistent/...$' +stderr '^go: no packages loaded from nonexistent/...$' diff --git a/src/cmd/go/testdata/script/run_work_versioned.txt b/src/cmd/go/testdata/script/run_work_versioned.txt new file mode 100644 index 00000000000000..eb0f22d1c0780b --- /dev/null +++ b/src/cmd/go/testdata/script/run_work_versioned.txt @@ -0,0 +1,16 @@ +[short] skip +go run example.com/printversion@v0.1.0 +stdout '^main is example.com/printversion v0.1.0$' + +-- go.work -- +go 1.18 + +use ( + . +) +-- go.mod -- +module example + +go 1.18 + +require example.com/printversion v1.0.0 diff --git a/src/cmd/go/testdata/script/test2json_interrupt.txt b/src/cmd/go/testdata/script/test2json_interrupt.txt new file mode 100644 index 00000000000000..3e3a1c7b0d60c6 --- /dev/null +++ b/src/cmd/go/testdata/script/test2json_interrupt.txt @@ -0,0 +1,55 @@ +[short] skip 'links and runs a test binary' +[!fuzz] skip 'tests SIGINT behavior for interrupting fuzz tests' +[windows] skip 'windows does not support os.Interrupt' + +? go test -json -fuzz FuzzInterrupt -run '^$' -parallel 1 +stdout -count=1 '"Action":"pass","Package":"example","Test":"FuzzInterrupt"' +stdout -count=1 '"Action":"pass","Package":"example","Elapsed":' + +mkdir $WORK/fuzzcache +go test -c . -fuzz=. -o test2json_interrupt_obj +? go tool test2json -p example -t ./test2json_interrupt_obj -test.v -test.paniconexit0 -test.fuzzcachedir $WORK/fuzzcache -test.fuzz FuzzInterrupt -test.run '^$' -test.parallel 1 +stdout -count=1 '"Action":"pass","Package":"example","Test":"FuzzInterrupt"' +stdout -count=1 '"Action":"pass","Package":"example","Elapsed":' + +-- go.mod -- +module example +go 1.20 +-- example_test.go -- +package example_test + +import ( + "fmt" + "os" + "strconv" + "testing" + "strings" + "time" +) + +func FuzzInterrupt(f *testing.F) { + pids := os.Getenv("GO_TEST_INTERRUPT_PIDS") + if pids == "" { + // This is the main test process. + // Set the environment variable for fuzz workers. + pid := os.Getpid() + ppid := os.Getppid() + os.Setenv("GO_TEST_INTERRUPT_PIDS", fmt.Sprintf("%d,%d", ppid, pid)) + } + + f.Fuzz(func(t *testing.T, orig string) { + // Simulate a ctrl-C on the keyboard by sending SIGINT + // to the main test process and its parent. + for _, pid := range strings.Split(pids, ",") { + i, err := strconv.Atoi(pid) + if err != nil { + t.Fatal(err) + } + if p, err := os.FindProcess(i); err == nil { + p.Signal(os.Interrupt) + time.Sleep(10 * time.Millisecond) + pids = "" // Only interrupt once. + } + } + }) +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_benchmark_1x.txt b/src/cmd/go/testdata/script/test_benchmark_1x.txt new file mode 100644 index 00000000000000..b1d4c39c16f050 --- /dev/null +++ b/src/cmd/go/testdata/script/test_benchmark_1x.txt @@ -0,0 +1,37 @@ +# Test that -benchtime 1x only runs a total of 1 loop iteration. +# See golang.org/issue/32051. + +go test -run ^$ -bench . -benchtime 1x + +-- go.mod -- +module bench + +go 1.16 +-- x_test.go -- +package bench + +import ( + "fmt" + "os" + "testing" +) + +var called = false + +func TestMain(m *testing.M) { + m.Run() + if !called { + fmt.Println("benchmark never called") + os.Exit(1) + } +} + +func Benchmark(b *testing.B) { + if b.N > 1 { + b.Fatalf("called with b.N=%d; want b.N=1 only", b.N) + } + if called { + b.Fatal("called twice") + } + called = true +} diff --git a/src/cmd/go/testdata/script/test_build_failure.txt b/src/cmd/go/testdata/script/test_build_failure.txt index 8d13634c8c4321..e8c984f272c33b 100644 --- a/src/cmd/go/testdata/script/test_build_failure.txt +++ b/src/cmd/go/testdata/script/test_build_failure.txt @@ -3,7 +3,7 @@ ! go test -x coverbad ! stderr '[\\/]coverbad\.test( |$)' # 'go test' should not claim to have run the test. stderr 'undefined: g' -stderr 'undefined: j' +[cgo] stderr 'undefined: j' -- go.mod -- module coverbad diff --git a/src/cmd/go/testdata/script/test_buildvcs.txt b/src/cmd/go/testdata/script/test_buildvcs.txt new file mode 100644 index 00000000000000..965f76bf0d05df --- /dev/null +++ b/src/cmd/go/testdata/script/test_buildvcs.txt @@ -0,0 +1,104 @@ +# https://go.dev/issue/51723: 'go test' should not stamp VCS metadata +# in the build settings. (It isn't worth the latency hit, given that +# test binaries are almost never distributed to users.) + +[short] skip +[!exec:git] skip + +exec git init + +# The test binaries should not have VCS settings stamped by default. +# (The test itself verifies that.) +go test . ./testonly + +# However, setting -buildvcs explicitly should override that and +# stamp anyway (https://go.dev/issue/52648). +go test -buildvcs -c -o ./testonly.exe ./testonly +! exec ./testonly.exe +stdout 'unexpected VCS setting: vcs\.modified=true' + + +# Remove 'git' from $PATH. The test should still build. +# This ensures that we aren't loading VCS metadata that +# we subsequently throw away. +env PATH='' +env path='' + +# Compiling the test should not require the VCS tool. +go test -c -o $devnull . + + +# When listing a main package, in general we need its VCS metadata to determine +# the .Stale and .StaleReason fields. +! go list -buildvcs=true . +stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.' + +# Adding the -test flag should be strictly additive — it should not suppress the error. +! go list -buildvcs=true -test . +stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.' + +# Adding the suggested flag should suppress the error. +go list -test -buildvcs=false . +! stderr . + + +# Since the ./testonly package doesn't itself produce an actual binary, we shouldn't +# invoke a VCS tool to compute a build stamp by default when listing it. +go list ./testonly +! stderr . +go list -test ./testonly +! stderr . + +# Again, setting -buildvcs explicitly should force the use of the VCS tool. +! go list -buildvcs ./testonly +stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.' +! go list -buildvcs -test ./testonly +stderr '^go: missing Git command\. See https://golang\.org/s/gogetcmd\nerror obtaining VCS status: .*\n\tUse -buildvcs=false to disable VCS stamping.' + + +-- go.mod -- +module example + +go 1.18 +-- example.go -- +package main +-- example_test.go -- +package main + +import ( + "runtime/debug" + "strings" + "testing" +) + +func TestDetail(t *testing.T) { + bi, ok := debug.ReadBuildInfo() + if !ok { + t.Fatal("BuildInfo not present") + } + for _, s := range bi.Settings { + if strings.HasPrefix(s.Key, "vcs.") { + t.Fatalf("unexpected VCS setting: %s=%s", s.Key, s.Value) + } + } +} +-- testonly/main_test.go -- +package main + +import ( + "runtime/debug" + "strings" + "testing" +) + +func TestDetail(t *testing.T) { + bi, ok := debug.ReadBuildInfo() + if !ok { + t.Fatal("BuildInfo not present") + } + for _, s := range bi.Settings { + if strings.HasPrefix(s.Key, "vcs.") { + t.Fatalf("unexpected VCS setting: %s=%s", s.Key, s.Value) + } + } +} diff --git a/src/cmd/go/testdata/script/test_cache_inputs.txt b/src/cmd/go/testdata/script/test_cache_inputs.txt index d694a30994710e..3705c700d10bc9 100644 --- a/src/cmd/go/testdata/script/test_cache_inputs.txt +++ b/src/cmd/go/testdata/script/test_cache_inputs.txt @@ -108,6 +108,12 @@ go test testcache -run=Benchtime -bench=Benchtime -benchtime=1x go test testcache -run=Benchtime -bench=Benchtime -benchtime=1x ! stdout '\(cached\)' +# golang.org/issue/47355: that includes the `-failfast` argument. +go test testcache -run=TestOSArgs -failfast +! stdout '\(cached\)' +go test testcache -run=TestOSArgs -failfast +stdout '\(cached\)' + # Executables within GOROOT and GOPATH should affect caching, # even if the test does not stat them explicitly. diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt new file mode 100644 index 00000000000000..4e0f239a661af2 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt @@ -0,0 +1,41 @@ +# Run parallel chatty tests. Assert on CONT lines. This test makes sure that +# multiple parallel outputs have the appropriate CONT lines between them. +go test -parallel 3 chatty_parallel -v + +stdout '=== RUN TestInterruptor/interruption\n=== CONT TestLog\n chatty_parallel_test.go:28: this is the second TestLog log\n--- PASS: Test(Log|Interruptor) \([0-9.]{4}s\)' + +-- go.mod -- +module chatty_parallel + +go 1.18 +-- chatty_parallel_test.go -- +package chatty_parallel_test + +import ( + "testing" +) + +var ( + afterFirstLog = make(chan struct{}) + afterSubTest = make(chan struct{}) + afterSecondLog = make(chan struct{}) +) + +func TestInterruptor(t *testing.T) { + t.Parallel() + + <-afterFirstLog + t.Run("interruption", func (t *testing.T) {}) + close(afterSubTest) + <-afterSecondLog // Delay the "PASS: TestInterruptor" line until after "CONT TestLog". +} + +func TestLog(t *testing.T) { + t.Parallel() + + t.Logf("this is the first TestLog log") + close(afterFirstLog) + <-afterSubTest + t.Logf("this is the second TestLog log") + close(afterSecondLog) +} diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt deleted file mode 100644 index e651a7ed243f6b..00000000000000 --- a/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt +++ /dev/null @@ -1,39 +0,0 @@ -# Run parallel chatty tests. Assert on CONT lines. This test makes sure that -# multiple parallel outputs have the appropriate CONT lines between them. -go test -parallel 3 chatty_parallel_test.go -v - -stdout '--- PASS: TestFast \([0-9.]{4}s\)\n=== CONT TestSlow\n chatty_parallel_test.go:31: this is the second TestSlow log\n--- PASS: TestSlow \([0-9.]{4}s\)' - --- chatty_parallel_test.go -- -package chatty_parallel_test - -import ( - "testing" - "time" -) - -var ( - run = make(chan struct{}) - afterFirstLog = make(chan struct{}) - afterPass = make(chan struct{}) -) - -func TestFast(t *testing.T) { - t.Parallel() - - <-afterFirstLog - t.Cleanup(func() { - close(afterPass) - }) -} - -func TestSlow(t *testing.T) { - t.Parallel() - - t.Logf("this is the first TestSlow log") - close(afterFirstLog) - - <-afterPass - time.Sleep(100 * time.Millisecond) - t.Logf("this is the second TestSlow log") -} diff --git a/src/cmd/go/testdata/script/test_fail_newline.txt b/src/cmd/go/testdata/script/test_fail_newline.txt new file mode 100644 index 00000000000000..43cee565a19c1e --- /dev/null +++ b/src/cmd/go/testdata/script/test_fail_newline.txt @@ -0,0 +1,65 @@ +[short] skip + +# In package list mode, output is buffered. +# Check that a newline is printed after the buffer's contents. +cd fail +! go test . +! stderr . +stdout '^exitcode=1\n' +stdout '^FAIL\s+example/fail' + +# In local directory mode output is streamed, so we don't know +# whether the test printed anything at all, so we print the exit code +# (just in case it failed without emitting any output at all), +# and that happens to add the needed newline as well. +! go test +! stderr . +stdout '^exitcode=1exit status 1\n' +stdout '^FAIL\s+example/fail' + +# In package list mode, if the test passes the 'ok' message appears +# on its own line. +cd ../skip +go test -v . +! stderr . +stdout '^skipping\n' +stdout '^ok\s+example/skip' + +# If the output is streamed and the test passes, we can't tell whether it ended +# in a partial line, and don't want to emit any extra output in the +# overwhelmingly common case that it did not. +# (In theory we could hook the 'os' package to report whether output +# was emitted and whether it ended in a newline, but that seems too invasive.) +go test +! stderr . +stdout '^skippingok\s+example/skip' + + +-- go.mod -- +module example + +go 1.18 +-- fail/fail_test.go -- +package fail + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Stderr.WriteString("exitcode=1") + os.Exit(1) +} +-- skip/skip_test.go -- +package skip + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Stderr.WriteString("skipping") + os.Exit(0) +} diff --git a/src/cmd/go/testdata/script/test_flag.txt b/src/cmd/go/testdata/script/test_flag.txt index 0142b3f308f3b2..d168cfe6a8c882 100644 --- a/src/cmd/go/testdata/script/test_flag.txt +++ b/src/cmd/go/testdata/script/test_flag.txt @@ -9,13 +9,13 @@ go test -count=1 -custom -args -v=7 # However, it should be an error to use custom flags when -i or -c are used, # since we know for sure that no test binary will run at all. ! go test -i -custom -stderr '^go test: unknown flag -custom cannot be used with -i$' +stderr '^go: unknown flag -custom cannot be used with -i$' ! go test -c -custom -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' # The same should apply even if -c or -i come after a custom flag. ! go test -custom -c -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/test_fuzz.txt b/src/cmd/go/testdata/script/test_fuzz.txt new file mode 100644 index 00000000000000..3e048e00c55c39 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz.txt @@ -0,0 +1,498 @@ +[!fuzz] skip + +# Test that running a fuzz target that returns without failing or calling +# f.Fuzz fails and causes a non-zero exit status. +! go test noop_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that fuzzing a fuzz target that returns without failing or calling +# f.Fuzz fails and causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x noop_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that calling f.Error in a fuzz target causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x error_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that calling f.Fatal in a fuzz target causes a non-zero exit status. +! go test fatal_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that successful test exits cleanly. +go test success_fuzz_test.go +stdout ^ok +! stdout FAIL + +# Test that successful fuzzing exits cleanly. +go test -fuzz=Fuzz -fuzztime=1x success_fuzz_test.go +stdout ok +! stdout FAIL + +# Test that calling f.Fatal while fuzzing causes a non-zero exit status. +! go test -fuzz=Fuzz -fuzztime=1x fatal_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test error with seed corpus in f.Fuzz +! go test -run FuzzError fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'error here' + +[short] stop + +# Test that calling panic(nil) in a fuzz target causes a non-zero exit status. +! go test panic_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that skipped test exits cleanly. +go test skipped_fuzz_test.go +stdout ok +! stdout FAIL + +# Test that f.Fatal within f.Fuzz panics +! go test fatal_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'fatal here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Error within f.Fuzz panics +! go test error_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'error here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Fail within f.Fuzz panics +! go test fail_fuzz_fn_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'fuzz target' + +# Test that f.Skip within f.Fuzz panics +! go test skip_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'skip here' +stdout FAIL +stdout 'fuzz target' + +# Test that f.Skipped within f.Fuzz panics +! go test skipped_fuzz_fn_fuzz_test.go +! stdout ^ok +! stdout 'f.Skipped is' +stdout FAIL +stdout 'fuzz target' +stdout 't.Skipped is false' + +# Test that runtime.Goexit within the fuzz function is an error. +! go test goexit_fuzz_fn_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that a call to f.Fatal after the Fuzz func is executed. +! go test fatal_after_fuzz_func_fuzz_test.go +! stdout ok +stdout FAIL + +# Test that missing *T in f.Fuzz causes a non-zero exit status. +! go test incomplete_fuzz_call_fuzz_test.go +! stdout ^ok +stdout FAIL + +# Test that a panic in the Cleanup func is executed. +! go test cleanup_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'failed some precondition' + +# Test success with seed corpus in f.Fuzz +go test -run FuzzPass fuzz_add_test.go +stdout ok +! stdout FAIL +! stdout 'off by one error' + +# Test fatal with seed corpus in f.Fuzz +! go test -run FuzzFatal fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'fatal here' + +# Test panic with seed corpus in f.Fuzz +! go test -run FuzzPanic fuzz_add_test.go +! stdout ^ok +stdout FAIL +stdout 'off by one error' + +# Test panic(nil) with seed corpus in f.Fuzz +! go test -run FuzzNilPanic fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with unsupported seed corpus +! go test -run FuzzUnsupported fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with different number of args to f.Add +! go test -run FuzzAddDifferentNumber fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test panic with different type of args to f.Add +! go test -run FuzzAddDifferentType fuzz_add_test.go +! stdout ^ok +stdout FAIL + +# Test that the wrong type given with f.Add will fail. +! go test -run FuzzWrongType fuzz_add_test.go +! stdout ^ok +stdout '\[string int\], want \[\[\]uint8 int8\]' +stdout FAIL + +# Test fatal with testdata seed corpus +! go test -run FuzzFail corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL +stdout 'fatal here' + +# Test pass with testdata seed corpus +go test -run FuzzPass corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL +! stdout 'fatal here' + +# Test pass with testdata and f.Add seed corpus +go test -run FuzzPassString corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL + +# Fuzzing pass with testdata and f.Add seed corpus (skip running tests first) +go test -run=None -fuzz=FuzzPassString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x +stdout ok +! stdout FAIL + +# Fuzzing pass with testdata and f.Add seed corpus +go test -run=FuzzPassString -fuzz=FuzzPassString corpustesting/fuzz_testdata_corpus_test.go -fuzztime=10x +stdout ok +! stdout FAIL + +# Test panic with malformed seed corpus +! go test -run FuzzFail corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL + +# Test pass with file in other nested testdata directory +go test -run FuzzInNestedDir corpustesting/fuzz_testdata_corpus_test.go +stdout ok +! stdout FAIL +! stdout 'fatal here' + +# Test fails with file containing wrong type +! go test -run FuzzWrongType corpustesting/fuzz_testdata_corpus_test.go +! stdout ^ok +stdout FAIL + +-- noop_fuzz_test.go -- +package noop_fuzz + +import "testing" + +func Fuzz(f *testing.F) {} + +-- error_fuzz_test.go -- +package error_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Error("error in target") +} + +-- fatal_fuzz_test.go -- +package fatal_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fatal("fatal in target") +} + +-- panic_fuzz_test.go -- +package panic_fuzz + +import "testing" + +func FuzzPanic(f *testing.F) { + panic(nil) +} + +-- success_fuzz_test.go -- +package success_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} + +-- skipped_fuzz_test.go -- +package skipped_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Skip() +} + +-- fatal_fuzz_fn_fuzz_test.go -- +package fatal_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Fatal("fatal here") + }) +} + +-- error_fuzz_fn_fuzz_test.go -- +package error_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Error("error here") + }) +} + +-- fail_fuzz_fn_fuzz_test.go -- +package skip_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Fail() + }) +} + +-- skip_fuzz_fn_fuzz_test.go -- +package skip_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + f.Skip("skip here") + }) +} + +-- skipped_fuzz_fn_fuzz_test.go -- +package skipped_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + t.Logf("t.Skipped is %t\n", t.Skipped()) + t.Logf("f.Skipped is %t\n", f.Skipped()) + }) +} + +-- goexit_fuzz_fn_fuzz_test.go -- +package goexit_fuzz_fn_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + runtime.Goexit() + }) +} + +-- fatal_after_fuzz_func_fuzz_test.go -- +package fatal_after_fuzz_func_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + // no-op + }) + f.Fatal("this shouldn't be called") +} + +-- incomplete_fuzz_call_fuzz_test.go -- +package incomplete_fuzz_call_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func(b []byte) { + // this is missing *testing.T as the first param, so should panic + }) +} + +-- cleanup_fuzz_test.go -- +package cleanup_fuzz_test + +import "testing" + +func Fuzz(f *testing.F) { + f.Cleanup(func() { + panic("failed some precondition") + }) + f.Fuzz(func(t *testing.T, b []byte) { + // no-op + }) +} + +-- fuzz_add_test.go -- +package fuzz_add + +import "testing" + +func add(f *testing.F) { + f.Helper() + f.Add([]byte("123")) + f.Add([]byte("12345")) + f.Add([]byte("")) +} + +func FuzzPass(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == -1 { + t.Fatal("fatal here") // will not be executed + } + }) +} + +func FuzzError(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 3 { + t.Error("error here") + } + }) +} + +func FuzzFatal(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 0 { + t.Fatal("fatal here") + } + }) +} + +func FuzzPanic(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 5 { + panic("off by one error") + } + }) +} + +func FuzzNilPanic(f *testing.F) { + add(f) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 3 { + panic(nil) + } + }) +} + +func FuzzUnsupported(f *testing.F) { + m := make(map[string]bool) + f.Add(m) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzAddDifferentNumber(f *testing.F) { + f.Add([]byte("a")) + f.Add([]byte("a"), []byte("b")) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzAddDifferentType(f *testing.F) { + f.Add(false) + f.Add(1234) + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzWrongType(f *testing.F) { + f.Add("hello", 50) + f.Fuzz(func(*testing.T, []byte, int8) {}) +} + +-- corpustesting/fuzz_testdata_corpus_test.go -- +package fuzz_testdata_corpus + +import "testing" + +func fuzzFn(f *testing.F) { + f.Helper() + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) == "12345" { + t.Fatal("fatal here") + } + }) +} + +func FuzzFail(f *testing.F) { + fuzzFn(f) +} + +func FuzzPass(f *testing.F) { + fuzzFn(f) +} + +func FuzzPassString(f *testing.F) { + f.Add("some seed corpus") + f.Fuzz(func(*testing.T, string) {}) +} + +func FuzzPanic(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +func FuzzInNestedDir(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +func FuzzWrongType(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) {}) +} + +-- corpustesting/testdata/fuzz/FuzzFail/1 -- +go test fuzz v1 +[]byte("12345") +-- corpustesting/testdata/fuzz/FuzzPass/1 -- +go test fuzz v1 +[]byte("00000") +-- corpustesting/testdata/fuzz/FuzzPassString/1 -- +go test fuzz v1 +string("hello") +-- corpustesting/testdata/fuzz/FuzzPanic/1 -- +malformed +-- corpustesting/testdata/fuzz/FuzzInNestedDir/anotherdir/1 -- +go test fuzz v1 +[]byte("12345") +-- corpustesting/testdata/fuzz/FuzzWrongType/1 -- +go test fuzz v1 +int("00000") diff --git a/src/cmd/go/testdata/script/test_fuzz_cache.txt b/src/cmd/go/testdata/script/test_fuzz_cache.txt new file mode 100644 index 00000000000000..752ab3adecac65 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_cache.txt @@ -0,0 +1,97 @@ +[!fuzz-instrumented] skip + +[short] skip +env GOCACHE=$WORK/cache + +# Fuzz cache should not exist after a regular test run. +go test . +exists $GOCACHE +! exists $GOCACHE/fuzz + +# Fuzzing should write interesting values to the cache. +go test -fuzz=FuzzY -fuzztime=100x . +go run ./contains_files $GOCACHE/fuzz/example.com/y/FuzzY + +# 'go clean -cache' should not delete the fuzz cache. +go clean -cache +exists $GOCACHE/fuzz + +# 'go clean -fuzzcache' should delete the fuzz cache but not the build cache. +go build -x ./empty +stderr '(compile|gccgo)( |\.exe).*empty.go' +go clean -fuzzcache +! exists $GOCACHE/fuzz +go build -x ./empty +! stderr '(compile|gccgo)( |\.exe).*empty.go' + +# Fuzzing indicates that one new interesting value was found with an empty +# corpus, and the total size of the cache is now 1. +go clean -fuzzcache +go test -fuzz=FuzzEmpty -fuzztime=10000x . +stdout 'new interesting: 1' +stdout 'total: 1' + +# Fuzzing again with a small fuzztime does not find any other interesting +# values but still indicates that the cache size is 1. +go test -fuzz=FuzzEmpty -fuzztime=2x . +stdout 'new interesting: 0' +stdout 'total: 1' + +! go clean -fuzzcache example.com/y +stderr 'go: clean -fuzzcache cannot be used with package arguments' + +-- go.mod -- +module example.com/y + +go 1.16 +-- y_test.go -- +package y + +import ( + "io" + "testing" +) + +func FuzzEmpty(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} + +func FuzzY(f *testing.F) { + f.Add([]byte("y")) + f.Fuzz(func(t *testing.T, b []byte) { Y(io.Discard, b) }) +} +-- y.go -- +package y + +import ( + "bytes" + "io" +) + +func Y(w io.Writer, b []byte) { + if !bytes.Equal(b, []byte("y")) { + w.Write([]byte("not equal")) + } +} +-- empty/empty.go -- +package empty +-- contains_files/contains_files.go -- +package main + +import ( + "fmt" + "path/filepath" + "io/ioutil" + "os" +) + +func main() { + infos, err := ioutil.ReadDir(filepath.Clean(os.Args[1])) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if len(infos) == 0 { + os.Exit(1) + } +} diff --git a/src/cmd/go/testdata/script/test_fuzz_chatty.txt b/src/cmd/go/testdata/script/test_fuzz_chatty.txt new file mode 100644 index 00000000000000..d07fe50f95e71a --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_chatty.txt @@ -0,0 +1,102 @@ +[!fuzz] skip +[short] skip + +# Run chatty fuzz targets with an error. +! go test -v chatty_error_fuzz_test.go +! stdout '^ok' +stdout 'FAIL' +stdout 'error in target' + +# Run chatty fuzz targets with a fatal. +! go test -v chatty_fatal_fuzz_test.go +! stdout '^ok' +stdout 'FAIL' +stdout 'fatal in target' + +# Run chatty fuzz target with a panic +! go test -v chatty_panic_fuzz_test.go +! stdout ^ok +stdout FAIL +stdout 'this is bad' + +# Run skipped chatty fuzz targets. +go test -v chatty_skipped_fuzz_test.go +stdout ok +stdout SKIP +! stdout FAIL + +# Run successful chatty fuzz targets. +go test -v chatty_fuzz_test.go +stdout ok +stdout PASS +stdout 'all good here' +! stdout FAIL + +# Fuzz successful chatty fuzz target that includes a separate unit test. +go test -v chatty_with_test_fuzz_test.go -fuzz=Fuzz -fuzztime=1x +stdout ok +stdout PASS +! stdout FAIL +stdout -count=1 'all good here' +# Verify that the unit test is only run once. +stdout -count=1 'logged foo' + +-- chatty_error_fuzz_test.go -- +package chatty_error_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Error("error in target") +} + +-- chatty_fatal_fuzz_test.go -- +package chatty_fatal_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fatal("fatal in target") +} + +-- chatty_panic_fuzz_test.go -- +package chatty_panic_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + panic("this is bad") +} + +-- chatty_skipped_fuzz_test.go -- +package chatty_skipped_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Skip() +} + +-- chatty_fuzz_test.go -- +package chatty_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Log("all good here") + f.Fuzz(func(*testing.T, []byte) {}) +} + +-- chatty_with_test_fuzz_test.go -- +package chatty_with_test_fuzz + +import "testing" + +func TestFoo(t *testing.T) { + t.Log("logged foo") +} + +func Fuzz(f *testing.F) { + f.Log("all good here") + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_cleanup.txt b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt new file mode 100644 index 00000000000000..b65022bd74e7a0 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt @@ -0,0 +1,66 @@ +[!fuzz] skip +[short] skip + +# Cleanup should run after F.Skip. +go test -run=FuzzTargetSkip +stdout cleanup + +# Cleanup should run after F.Fatal. +! go test -run=FuzzTargetFatal +stdout cleanup + +# Cleanup should run after an unexpected runtime.Goexit. +! go test -run=FuzzTargetGoexit +stdout cleanup + +# Cleanup should run after panic. +! go test -run=FuzzTargetPanic +stdout cleanup + +# Cleanup should run in fuzz function on seed corpus. +go test -v -run=FuzzFunction +stdout '(?s)inner.*outer' + +# TODO(jayconrod): test cleanup while fuzzing. For now, the worker process's +# stdout and stderr is connected to the coordinator's, but it should eventually +# be connected to os.DevNull, so we wouldn't see t.Log output. + +-- go.mod -- +module cleanup + +go 1.15 +-- cleanup_test.go -- +package cleanup + +import ( + "runtime" + "testing" +) + +func FuzzTargetSkip(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + f.Skip() +} + +func FuzzTargetFatal(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + f.Fatal() +} + +func FuzzTargetGoexit(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + runtime.Goexit() +} + +func FuzzTargetPanic(f *testing.F) { + f.Cleanup(func() { f.Log("cleanup") }) + panic("oh no") +} + +func FuzzFunction(f *testing.F) { + f.Add([]byte{0}) + f.Cleanup(func() { f.Log("outer") }) + f.Fuzz(func(t *testing.T, b []byte) { + t.Cleanup(func() { t.Logf("inner") }) + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_cov.txt b/src/cmd/go/testdata/script/test_fuzz_cov.txt new file mode 100644 index 00000000000000..05b634889f335e --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_cov.txt @@ -0,0 +1,33 @@ +# Test that coverage instrumentation is working. Without the instrumentation +# it is _extremely_ unlikely that the fuzzer would produce this particular +# input in any reasonable amount of time. + +[short] skip +[!fuzz-instrumented] skip + +! go test -fuzz=FuzzCov +! stderr 'cov instrumentation working' + +-- go.mod -- +module test + +-- cov_test.go -- +package cov + +import "testing" + +func FuzzCov(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) == 8 && + b[0] == 'h' && + b[1] == 'e' && + b[2] == 'l' && + b[3] == 'l' && + b[4] == 'o' && + b[5] == ' ' && + b[6] == ':' && + b[7] == ')' { + panic("cov instrumentation working") + } + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_deadline.txt b/src/cmd/go/testdata/script/test_fuzz_deadline.txt new file mode 100644 index 00000000000000..5ba76a3d4fea9a --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_deadline.txt @@ -0,0 +1,35 @@ +[!fuzz] skip +[short] skip + +# The fuzz function should be able to detect whether -timeout +# was set with T.Deadline. Note there is no F.Deadline, and +# there is no timeout while fuzzing, even if -fuzztime is set. +go test -run=FuzzDeadline -wantdeadline=true # -timeout defaults to 10m +go test -run=FuzzDeadline -timeout=0 -wantdeadline=false +! go test -run=FuzzDeadline -timeout=1s -wantdeadline=false +go test -run=FuzzDeadline -timeout=1s -wantdeadline=true +go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=1s -wantdeadline=false +go test -fuzz=FuzzDeadline -timeout=0 -fuzztime=100x -wantdeadline=false + +-- go.mod -- +module fuzz + +go 1.16 +-- fuzz_deadline_test.go -- +package fuzz_test + +import ( + "flag" + "testing" +) + +var wantDeadline = flag.Bool("wantdeadline", false, "whether the test should have a deadline") + +func FuzzDeadline(f *testing.F) { + f.Add("run once") + f.Fuzz(func (t *testing.T, _ string) { + if _, hasDeadline := t.Deadline(); hasDeadline != *wantDeadline { + t.Fatalf("function got %v; want %v", hasDeadline, *wantDeadline) + } + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_dup_cache.txt b/src/cmd/go/testdata/script/test_fuzz_dup_cache.txt new file mode 100644 index 00000000000000..52d44a26ff8c82 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_dup_cache.txt @@ -0,0 +1,52 @@ +[!fuzz] skip +[short] skip + +# This test checks that cached corpus loading properly handles duplicate entries (this can +# happen when a f.Add value has a duplicate entry in the cached corpus.) Duplicate entries +# should be discarded, and the rest of the cache should be loaded as normal. + +env GOCACHE=$WORK/cache +env GODEBUG=fuzzdebug=1 + +mkdir -p $GOCACHE/fuzz/fuzztest/FuzzTarget +go run ./populate $GOCACHE/fuzz/fuzztest/FuzzTarget + +go test -fuzz=FuzzTarget -fuzztime=10x . +stdout 'entries: 5' + +-- go.mod -- +module fuzztest + +go 1.17 + +-- fuzz_test.go -- +package fuzz + +import "testing" + +func FuzzTarget(f *testing.F) { + f.Add(int(0)) + f.Fuzz(func(t *testing.T, _ int) {}) +} + +-- populate/main.go -- +package main + +import ( + "path/filepath" + "fmt" + "os" +) + +func main() { + for i := 0; i < 10; i++ { + b := byte(0) + if i > 5 { + b = byte(i) + } + tmpl := "go test fuzz v1\nint(%d)\n" + if err := os.WriteFile(filepath.Join(os.Args[1], fmt.Sprint(i)), []byte(fmt.Sprintf(tmpl, b)), 0777); err != nil { + panic(err) + } + } +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt new file mode 100644 index 00000000000000..56d94a4bcf58e6 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt @@ -0,0 +1,121 @@ +[!fuzz] skip +[short] skip + +# There are no seed values, so 'go test' should finish quickly. +go test + +# Fuzzing should exit 0 after fuzztime, even if timeout is short. +go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s + +# We should see the same behavior when invoking the test binary directly. +go test -c +exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache + +# Timeout should not cause inputs to be written as crashers. +! exists testdata/fuzz + +env GOCACHE=$WORK/tmp + +# When we use fuzztime with an "x" suffix, it runs a specific number of times. +# This fuzz function creates a file with a unique name ($pid.$count) on each +# run. We count the files to find the number of runs. +mkdir count +go test -fuzz=FuzzTestCount -fuzztime=1000x -fuzzminimizetime=1x +go run check_file_count.go count 1000 + +# When we use fuzzminimizetime with an "x" suffix, it runs a specific number of +# times while minimizing. This fuzz function creates a file with a unique name +# ($pid.$count) on each run once the first crash has been found. That means that +# there should be one file for each execution of the fuzz function during +# minimization, so we count these to determine how many times minimization was +# run. +mkdir minimizecount +! go test -fuzz=FuzzMinimizeCount -fuzzminimizetime=3x -parallel=1 +go run check_file_count.go minimizecount 3 + +-- go.mod -- +module fuzz + +go 1.16 +-- fuzz_fast_test.go -- +package fuzz_test + +import "testing" + +func FuzzFast(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} +-- fuzz_count_test.go -- +package fuzz + +import ( + "fmt" + "os" + "testing" +) + +func FuzzTestCount(f *testing.F) { + pid := os.Getpid() + n := 0 + f.Fuzz(func(t *testing.T, _ []byte) { + name := fmt.Sprintf("count/%v.%d", pid, n) + if err := os.WriteFile(name, nil, 0666); err != nil { + t.Fatal(err) + } + n++ + }) +} +-- fuzz_minimize_count_test.go -- +package fuzz + +import ( + "bytes" + "fmt" + "os" + "testing" +) + +func FuzzMinimizeCount(f *testing.F) { + pid := os.Getpid() + n := 0 + seed := bytes.Repeat([]byte("a"), 357) + f.Add(seed) + crashFound := false + f.Fuzz(func(t *testing.T, b []byte) { + if crashFound { + name := fmt.Sprintf("minimizecount/%v.%d", pid, n) + if err := os.WriteFile(name, nil, 0666); err != nil { + t.Fatal(err) + } + n++ + } + if !bytes.Equal(b, seed) { // this should happen right away + crashFound = true + t.Error("minimize this!") + } + }) +} +-- check_file_count.go -- +// +build ignore + +package main + +import ( + "fmt" + "os" + "strconv" +) + +func main() { + dir, err := os.ReadDir(os.Args[1]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + got := len(dir) + want, _ := strconv.Atoi(os.Args[2]) + if got != want { + fmt.Fprintf(os.Stderr, "got %d files; want %d\n", got, want) + os.Exit(1) + } +} diff --git a/src/cmd/go/testdata/script/test_fuzz_io_error.txt b/src/cmd/go/testdata/script/test_fuzz_io_error.txt new file mode 100644 index 00000000000000..1a0aa6427e89f6 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_io_error.txt @@ -0,0 +1,101 @@ +# Test that when the coordinator experiences an I/O error communicating +# with a worker, the coordinator stops the worker and reports the error. +# The coordinator should not record a crasher. +# +# We simulate an I/O error in the test by writing garbage to fuzz_out. +# This is unlikely, but possible. It's difficult to simulate interruptions +# due to ^C and EOF errors which are more common. We don't report those. +[short] skip +[!fuzz] skip + +# If the I/O error occurs before F.Fuzz is called, the coordinator should +# stop the worker and say that. +! go test -fuzz=FuzzClosePipeBefore -parallel=1 +stdout '\s*fuzzing process terminated without fuzzing:' +! stdout 'communicating with fuzzing process' +! exists testdata + +# If the I/O error occurs after F.Fuzz is called (unlikely), just exit. +# It's hard to distinguish this case from the worker being interrupted by ^C +# or exiting with status 0 (which it should do when interrupted by ^C). +! go test -fuzz=FuzzClosePipeAfter -parallel=1 +stdout '^\s*communicating with fuzzing process: invalid character ''!'' looking for beginning of value$' +! exists testdata + +-- go.mod -- +module test + +go 1.17 +-- io_error_test.go -- +package io_error + +import ( + "flag" + "testing" + "time" +) + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} + +func FuzzClosePipeBefore(f *testing.F) { + if isWorker() { + sendGarbageToCoordinator(f) + time.Sleep(3600 * time.Second) // pause until coordinator terminates the process + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzClosePipeAfter(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) { + if isWorker() { + sendGarbageToCoordinator(t) + time.Sleep(3600 * time.Second) // pause until coordinator terminates the process + } + }) +} +-- io_error_windows_test.go -- +package io_error + +import ( + "fmt" + "os" + "testing" +) + +func sendGarbageToCoordinator(tb testing.TB) { + v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES") + var fuzzInFD, fuzzOutFD uintptr + if _, err := fmt.Sscanf(v, "%x,%x", &fuzzInFD, &fuzzOutFD); err != nil { + tb.Fatalf("parsing GO_TEST_FUZZ_WORKER_HANDLES: %v", err) + } + f := os.NewFile(fuzzOutFD, "fuzz_out") + if _, err := f.Write([]byte("!!")); err != nil { + tb.Fatalf("writing fuzz_out: %v", err) + } +} +-- io_error_notwindows_test.go -- +// +build !windows + +package io_error + +import ( + "os" + "testing" +) + +func sendGarbageToCoordinator(tb testing.TB) { + f := os.NewFile(4, "fuzz_out") + if _, err := f.Write([]byte("!!")); err != nil { + tb.Fatalf("writing fuzz_out: %v", err) + } +} diff --git a/src/cmd/go/testdata/script/test_fuzz_match.txt b/src/cmd/go/testdata/script/test_fuzz_match.txt new file mode 100644 index 00000000000000..dbf987605f30fb --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_match.txt @@ -0,0 +1,38 @@ +[!fuzz] skip + +# Matches only fuzz targets to test. +go test standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches only for fuzzing. +go test -fuzz Fuzz -fuzztime 1x standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches none for fuzzing but will run the fuzz target as a test. +go test -fuzz ThisWillNotMatch -fuzztime 1x standalone_fuzz_test.go +! stdout '^ok.*no tests to run' +stdout '^ok' +stdout 'no fuzz tests to fuzz' + +[short] stop + +# Matches only fuzz targets to test with -run. +go test -run Fuzz standalone_fuzz_test.go +! stdout '^ok.*\[no tests to run\]' +stdout '^ok' + +# Matches no fuzz targets. +go test -run ThisWillNotMatch standalone_fuzz_test.go +stdout '^ok.*no tests to run' +! stdout 'no fuzz tests to fuzz' + +-- standalone_fuzz_test.go -- +package standalone_fuzz + +import "testing" + +func Fuzz(f *testing.F) { + f.Fuzz(func (*testing.T, []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize.txt b/src/cmd/go/testdata/script/test_fuzz_minimize.txt new file mode 100644 index 00000000000000..a6dc3f19534e8d --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_minimize.txt @@ -0,0 +1,203 @@ +[!fuzz] skip +[short] skip + +# We clean the fuzz cache during this test. Don't clean the user's cache. +env GOCACHE=$WORK/gocache + +# Test that fuzzminimizetime cannot be negative seconds +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms . +! stdout '^ok' +! stdout 'contains a non-zero byte' +stdout 'invalid duration' +stdout FAIL + +# Test that fuzzminimizetime cannot be negative times +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x . +! stdout '^ok' +! stdout 'contains a non-zero byte' +stdout 'invalid count' +stdout FAIL + +# Test that fuzzminimizetime can be zero seconds, and minimization is disabled +! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s . +! stdout '^ok' +! stdout 'minimizing' +stdout 'there was an Error' +stdout FAIL + +# Test that fuzzminimizetime can be zero times, and minimization is disabled +! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x . +! stdout '^ok' +! stdout 'minimizing' +stdout -count=1 'there was an Error' +stdout FAIL + +# Test that minimization is working for recoverable errors. +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x . +! stdout '^ok' +stdout 'got the minimum size!' +# The error message that was printed should be for the one written to testdata. +stdout 'contains a non-zero byte of length 50' +stdout FAIL + +# Check that the bytes written to testdata are of length 50 (the minimum size) +go run ./check_testdata FuzzMinimizerRecoverable 50 + +# Test that re-running the minimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . +rm testdata + +# Test that minimization is working for recoverable errors. Run it with -v this +# time to ensure the command line output still looks right. +! go test -v -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x . +! stdout '^ok' +stdout 'got the minimum size!' +# The error message that was printed should be for the one written to testdata. +stdout 'contains a non-zero byte of length 50' +stdout FAIL + +# Check that the bytes written to testdata are of length 50 (the minimum size) +go run ./check_testdata FuzzMinimizerRecoverable 50 + +# Test that re-running the minimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . +rm testdata + +# Test that minimization doesn't run for non-recoverable errors. +! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x . +! stdout '^ok' +! stdout 'minimizing' +stdout -count=1 '^\s+fuzzing process hung or terminated unexpectedly: exit status 99' +stdout FAIL + +# Check that re-running the value causes a crash. +! go test -run=FuzzMinimizerNonrecoverable . +rm testdata + +# Clear the fuzzing cache. There may already be minimized inputs that would +# interfere with the next stage of the test. +go clean -fuzzcache + +# Test that minimization can be cancelled by fuzzminimizetime and the latest +# crash will still be logged and written to testdata. +! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x . +! stdout '^ok' +stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerRecoverable[/\\]' +! stdout 'got the minimum size!' # it shouldn't have had enough time to minimize it +stdout FAIL + +# Test that re-running the unminimized value causes a crash. +! go test -run=FuzzMinimizerRecoverable . + +# TODO(jayconrod,katiehockman): add a test which verifies that the right bytes +# are written to testdata in the case of an interrupt during minimization. + +-- go.mod -- +module example.com/y + +go 1.16 +-- y_test.go -- +package y + +import ( + "os" + "testing" +) + +func FuzzMinimizeZeroDurationSet(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) > 5 { + t.Errorf("there was an Error") + } + }) +} + +func FuzzMinimizeZeroLimitSet(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) > 5 { + t.Errorf("there was an Error") + } + }) +} + +func FuzzMinimizerRecoverable(f *testing.F) { + f.Add(make([]byte, 100)) + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) < 50 { + // Make sure that b is large enough that it can be minimized + return + } + // Given the randomness of the mutations, this should allow the + // minimizer to trim down the value a bit. + for _, n := range b { + if n != 0 { + if len(b) == 50 { + t.Log("got the minimum size!") + } + t.Fatalf("contains a non-zero byte of length %d", len(b)) + } + } + }) +} + +func FuzzMinimizerNonrecoverable(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + os.Exit(99) + }) +} +-- empty/empty.go -- +package empty +-- check_testdata/check_testdata.go -- +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" +) + +func main() { + target := os.Args[1] + numBytes, err := strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Open the file in testdata (there should only be one) + dir := fmt.Sprintf("testdata/fuzz/%s", target) + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if len(files) != 1 { + fmt.Fprintf(os.Stderr, "expected one file, got %d", len(files)) + os.Exit(1) + } + got, err := ioutil.ReadFile(filepath.Join(dir, files[0].Name())) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + // Trim the newline at the end of the file + got = bytes.TrimSpace(got) + + // Make sure that there were exactly 100 bytes written to the corpus entry + prefix := []byte("[]byte(") + i := bytes.Index(got, prefix) + gotBytes := got[i+len(prefix) : len(got)-1] + s, err := strconv.Unquote(string(gotBytes)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if want, got := numBytes, len(s); want != got { + fmt.Fprintf(os.Stderr, "want %d bytes, got %d\n", want, got) + os.Exit(1) + } +} diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt new file mode 100644 index 00000000000000..1279f6e9ac02c7 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt @@ -0,0 +1,85 @@ +# Test that minimization doesn't use dirty coverage snapshots when it +# is unable to actually minimize the input. We do this by checking that +# a expected value appears in the cache. If a dirty coverage map is used +# (i.e. the coverage map generated during the last minimization step, +# rather than the map provided with the initial input) then this value +# is unlikely to appear in the cache, since the map generated during +# the last minimization step should not increase the coverage. + +[short] skip +[!fuzz-instrumented] skip + +env GOCACHE=$WORK/gocache +go test -fuzz=FuzzCovMin -fuzztime=500000x -test.fuzzcachedir=$GOCACHE/fuzz +go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin ab + +-- go.mod -- +module test + +-- covmin_test.go -- +package covmin + +import "testing" + +func FuzzCovMin(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) == 2 && data[0] == 'a' && data[1] == 'b' { + return + } + }) +} + +-- check_file/main.go -- +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" +) + +func checkFile(name, expected string) (bool, error) { + data, err := os.ReadFile(name) + if err != nil { + return false, err + } + for _, line := range bytes.Split(data, []byte("\n")) { + m := valRe.FindSubmatch(line) + if m == nil { + continue + } + fmt.Println(strconv.Unquote(string(m[1]))) + if s, err := strconv.Unquote(string(m[1])); err != nil { + return false, err + } else if s == expected { + return true, nil + } + } + return false, nil +} + +var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`) + +func main() { + dir, expected := os.Args[1], os.Args[2] + ents, err := os.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + for _, ent := range ents { + name := filepath.Join(dir, ent.Name()) + if good, err := checkFile(name, expected); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if good { + os.Exit(0) + } + } + fmt.Fprintln(os.Stderr, "input over minimized") + os.Exit(1) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt new file mode 100644 index 00000000000000..e61c4f9d04eb9d --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt @@ -0,0 +1,230 @@ +[short] skip +[!fuzz-instrumented] skip + +# Test that when an interesting value is discovered (one that expands coverage), +# the fuzzing engine minimizes it before writing it to the cache. +# +# The program below starts with a seed value of length 100, but more coverage +# will be found for any value other than the seed. We should end with a value +# in the cache of length 1 (the minimizer currently does not produce empty +# strings). check_cache.go confirms that. +# +# We would like to verify that ALL values in the cache were minimized to a +# length of 1, but this isn't always possible when new coverage is found in +# functions called by testing or internal/fuzz in the background. + +go test -c -fuzz=. # Build using shared build cache for speed. +env GOCACHE=$WORK/gocache +exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x +go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache + +# Test that minimization occurs for a crash that appears while minimizing a +# newly found interesting input. There must be only one worker for this test to +# be flaky like we want. +! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=XXX -test.fuzztime=10000x -test.parallel=1 +! stdout '^ok' +stdout -count=1 'got the minimum size!' +stdout -count=1 'bad input' +stdout FAIL +# Check that the input written to testdata will reproduce the error, and is the +# smallest possible. +go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1 + +# Test that a nonrecoverable error that occurs while minimizing an interesting +# input is reported correctly. +! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=XXX -test.fuzztime=10000x -test.parallel=1 +! stdout '^ok' +stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing' +stdout -count=1 'EOF' +stdout FAIL +# Check that the input written to testdata will reproduce the error. +go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1 + +-- go.mod -- +module fuzz + +go 1.17 +-- y.go -- +package fuzz + +import ( + "bytes" + "io" +) + +func Y(w io.Writer, s string) { + if !bytes.Equal([]byte(s), []byte("y")) { + w.Write([]byte("not equal")) + } +} +-- fuzz_test.go -- +package fuzz + +import ( + "bytes" + "os" + "testing" +) + +func FuzzMinimizerCrashInMinimization(f *testing.F) { + seed := bytes.Repeat([]byte{255}, 100) + f.Add(seed) + f.Fuzz(func(t *testing.T, b []byte) { + if bytes.Equal(seed, b) { + return + } + t.Error("bad input") + if len(b) == 1 { + t.Error("got the minimum size!") + } + }) +} + +var fuzzing bool + +func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) { + seed := bytes.Repeat([]byte{255}, 100) + f.Add(seed) + f.Fuzz(func(t *testing.T, b []byte) { + if bytes.Equal(seed, b) { + return + } else if len(b) == 1 { + os.Exit(1) + } + }) +} + +func FuzzMinCache(f *testing.F) { + seed := bytes.Repeat([]byte("a"), 20) + f.Add(seed) + f.Fuzz(func(t *testing.T, buf []byte) { + if bytes.Equal(buf, seed) { + return + } + }) +} +-- check_testdata/check_testdata.go -- +//go:build ignore +// +build ignore + +// check_testdata.go checks that the string written +// is not longer than the provided length. +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strconv" +) + +func main() { + wantLen, err := strconv.Atoi(os.Args[2]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + testName := os.Args[1] + dir := filepath.Join("testdata/fuzz", testName) + + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if len(files) == 0 { + fmt.Fprintf(os.Stderr, "expect at least one failure to be written to testdata\n") + os.Exit(1) + } + + for _, f := range files { + data, err := ioutil.ReadFile(filepath.Join(dir, f.Name())) + if err != nil { + panic(err) + } + var containsVal bool + for _, line := range bytes.Split(data, []byte("\n")) { + m := valRe.FindSubmatch(line) + if m == nil { + continue + } + containsVal = true + s, err := strconv.Unquote(string(m[1])) + if err != nil { + panic(err) + } + if len(s) != wantLen { + fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line) + os.Exit(1) + } + } + if !containsVal { + fmt.Fprintln(os.Stderr, "corpus file contained no values") + os.Exit(1) + } + } +} + +var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`) + +-- check_cache/check_cache.go -- +//go:build ignore +// +build ignore + +// check_cache.go checks that each file in the cached corpus has a []byte +// of length at most 1. This verifies that at least one cached input is minimized. +package main + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" +) + +func main() { + dir := os.Args[1] + ents, err := os.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + for _, ent := range ents { + name := filepath.Join(dir, ent.Name()) + if good, err := checkCacheFile(name); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if good { + os.Exit(0) + } + } + fmt.Fprintln(os.Stderr, "no cached inputs were minimized") + os.Exit(1) +} + +func checkCacheFile(name string) (good bool, err error) { + data, err := os.ReadFile(name) + if err != nil { + return false, err + } + for _, line := range bytes.Split(data, []byte("\n")) { + m := valRe.FindSubmatch(line) + if m == nil { + continue + } + if s, err := strconv.Unquote(string(m[1])); err != nil { + return false, err + } else if len(s) <= 1 { + return true, nil + } + } + return false, nil +} + +var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`) diff --git a/src/cmd/go/testdata/script/test_fuzz_modcache.txt b/src/cmd/go/testdata/script/test_fuzz_modcache.txt new file mode 100644 index 00000000000000..c0f18ea3c07319 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_modcache.txt @@ -0,0 +1,58 @@ +# This test demonstrates the fuzz corpus behavior for packages outside of the main module. +# (See https://golang.org/issue/48495.) + +[short] skip + +# Set -modcacherw so that the test behaves the same regardless of whether the +# module cache is writable. (For example, on some platforms it can always be +# written if the user is running as root.) At one point, a failing fuzz test +# in a writable module cache would corrupt module checksums in the cache. +env GOFLAGS=-modcacherw + + +# When the upstream module has no test corpus, running 'go test' should succeed, +# but 'go test -fuzz=.' should error out before running the test. +# (It should NOT corrupt the module cache by writing out new fuzz inputs, +# even if the cache is writable.) + +go get -t example.com/fuzzfail@v0.1.0 +go test example.com/fuzzfail + +! go test -fuzz=. example.com/fuzzfail +! stdout . +stderr '^cannot use -fuzz flag on package outside the main module$' + +go mod verify + + +# If the module does include a test corpus, 'go test' (without '-fuzz') should +# load that corpus and run the fuzz tests against it, but 'go test -fuzz=.' +# should continue to be rejected. + +go get -t example.com/fuzzfail@v0.2.0 + +! go test example.com/fuzzfail +stdout '^\s*fuzzfail_test\.go:7: oops:' + +! go test -fuzz=. example.com/fuzzfail +! stdout . +stderr '^cannot use -fuzz flag on package outside the main module$' + +go mod verify + + +# Packages in 'std' cannot be fuzzed when the corresponding GOROOT module is not +# the main module — either the failures would not be recorded or the behavior of +# the 'std' tests would change globally. + +! go test -fuzz . encoding/json +stderr '^cannot use -fuzz flag on package outside the main module$' + +! go test -fuzz . cmd/buildid +stderr '^cannot use -fuzz flag on package outside the main module$' + + +-- go.mod -- +module example.com/m + +go 1.18 diff --git a/src/cmd/go/testdata/script/test_fuzz_multiple.txt b/src/cmd/go/testdata/script/test_fuzz_multiple.txt new file mode 100644 index 00000000000000..1ec4985613fd36 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_multiple.txt @@ -0,0 +1,49 @@ +# This test checks that 'go test' prints a reasonable error when fuzzing is +# enabled, and multiple package or multiple fuzz targets match. +# TODO(#46312): support fuzzing multiple targets in multiple packages. + +[!fuzz] skip +[short] skip + +# With fuzzing disabled, multiple targets can be tested. +go test ./... + +# With fuzzing enabled, at most one package may be tested, +# even if only one package contains fuzz targets. +! go test -fuzz=. ./... +stderr '^cannot use -fuzz flag with multiple packages$' +! go test -fuzz=. ./zero ./one +stderr '^cannot use -fuzz flag with multiple packages$' +go test -fuzz=. -fuzztime=1x ./one + +# With fuzzing enabled, at most one target in the same package may match. +! go test -fuzz=. ./two +stdout '^testing: will not fuzz, -fuzz matches more than one fuzz test: \[FuzzOne FuzzTwo\]$' +go test -fuzz=FuzzTwo -fuzztime=1x ./two + +-- go.mod -- +module fuzz + +go 1.18 +-- zero/zero.go -- +package zero +-- one/one_test.go -- +package one + +import "testing" + +func FuzzOne(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} +-- two/two_test.go -- +package two + +import "testing" + +func FuzzOne(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzTwo(f *testing.F) { + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt new file mode 100644 index 00000000000000..d2ded27f856a79 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt @@ -0,0 +1,322 @@ +[!fuzz] skip + +# Tests that a crash caused by a mutator-discovered input writes the bad input +# to testdata, and fails+reports correctly. This tests the end-to-end behavior +# of the mutator finding a crash while fuzzing, adding it as a regression test +# to the seed corpus in testdata, and failing the next time the test is run. + +[short] skip + +# Running the seed corpus for all of the targets should pass the first +# time, since nothing in the seed corpus will cause a crash. +go test + +# Running the fuzzer should find a crashing input quickly. +! go test -fuzz=FuzzWithBug -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithBug[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzWithBug + +# Now, the failing bytes should have been added to the seed corpus for +# the target, and should fail when run without fuzzing. +! go test +stdout 'FuzzWithBug/[a-f0-9]{64}' +stdout 'this input caused a crash!' + +! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithNilPanic[/\\]' +stdout 'runtime.Goexit' +go run check_testdata.go FuzzWithNilPanic + +! go test -run=FuzzWithGoexit -fuzz=FuzzWithGoexit -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithGoexit[/\\]' +stdout 'runtime.Goexit' +go run check_testdata.go FuzzWithGoexit + +! go test -run=FuzzWithFail -fuzz=FuzzWithFail -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithFail[/\\]' +go run check_testdata.go FuzzWithFail + +! go test -run=FuzzWithLogFail -fuzz=FuzzWithLogFail -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithLogFail[/\\]' +stdout 'logged something' +go run check_testdata.go FuzzWithLogFail + +! go test -run=FuzzWithErrorf -fuzz=FuzzWithErrorf -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithErrorf[/\\]' +stdout 'errorf was called here' +go run check_testdata.go FuzzWithErrorf + +! go test -run=FuzzWithFatalf -fuzz=FuzzWithFatalf -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithFatalf[/\\]' +stdout 'fatalf was called here' +go run check_testdata.go FuzzWithFatalf + +! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithBadExit[/\\]' +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +go run check_testdata.go FuzzWithBadExit + +! go test -run=FuzzDeadlock -fuzz=FuzzDeadlock -fuzztime=100x -fuzzminimizetime=0x +stdout 'testdata[/\\]fuzz[/\\]FuzzDeadlock[/\\]' +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +go run check_testdata.go FuzzDeadlock + +# Running the fuzzer should find a crashing input quickly for fuzzing two types. +! go test -run=FuzzWithTwoTypes -fuzz=FuzzWithTwoTypes -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzWithTwoTypes[/\\]' +stdout 'these inputs caused a crash!' +go run check_testdata.go FuzzWithTwoTypes + +# Running the fuzzer should find a crashing input quickly for an integer. +! go test -run=FuzzInt -fuzz=FuzzInt -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzInt[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzInt + +! go test -run=FuzzUint -fuzz=FuzzUint -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzUint[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzUint + +# Running the fuzzer should find a crashing input quickly for a bool. +! go test -run=FuzzBool -fuzz=FuzzBool -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzBool[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzBool + +# Running the fuzzer should find a crashing input quickly for a float. +! go test -run=FuzzFloat -fuzz=FuzzFloat -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzFloat[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzFloat + +# Running the fuzzer should find a crashing input quickly for a byte. +! go test -run=FuzzByte -fuzz=FuzzByte -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzByte[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzByte + +# Running the fuzzer should find a crashing input quickly for a rune. +! go test -run=FuzzRune -fuzz=FuzzRune -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzRune[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzRune + +# Running the fuzzer should find a crashing input quickly for a string. +! go test -run=FuzzString -fuzz=FuzzString -fuzztime=100x -fuzzminimizetime=1000x +stdout 'testdata[/\\]fuzz[/\\]FuzzString[/\\]' +stdout 'this input caused a crash!' +go run check_testdata.go FuzzString + +-- go.mod -- +module m + +go 1.16 +-- fuzz_crash_test.go -- +package fuzz_crash + +import ( + "os" + "runtime" + "testing" +) + +func FuzzWithBug(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + panic("this input caused a crash!") + } + }) +} + +func FuzzWithNilPanic(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + panic(nil) + } + }) +} + +func FuzzWithGoexit(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + runtime.Goexit() + } + }) +} + +func FuzzWithFail(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Fail() + } + }) +} + +func FuzzWithLogFail(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Log("logged something") + t.Fail() + } + }) +} + +func FuzzWithErrorf(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Errorf("errorf was called here") + } + }) +} + +func FuzzWithFatalf(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + t.Fatalf("fatalf was called here") + } + }) +} + +func FuzzWithBadExit(f *testing.F) { + f.Add([]byte("aa")) + f.Fuzz(func(t *testing.T, b []byte) { + if string(b) != "aa" { + os.Exit(1) + } + }) +} + +func FuzzDeadlock(f *testing.F) { + f.Add(int(0)) + f.Fuzz(func(t *testing.T, n int) { + if n != 0 { + select {} + } + }) +} + +func FuzzWithTwoTypes(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b []byte) { + if len(a) > 0 && len(b) > 0 { + panic("these inputs caused a crash!") + } + }) +} + +func FuzzInt(f *testing.F) { + f.Add(0) + f.Fuzz(func(t *testing.T, a int) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzUint(f *testing.F) { + f.Add(uint(0)) + f.Fuzz(func(t *testing.T, a uint) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzBool(f *testing.F) { + f.Add(false) + f.Fuzz(func(t *testing.T, a bool) { + if a { + panic("this input caused a crash!") + } + }) +} + +func FuzzFloat(f *testing.F) { + f.Fuzz(func(t *testing.T, a float64) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzByte(f *testing.F) { + f.Add(byte(0)) + f.Fuzz(func(t *testing.T, a byte) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzRune(f *testing.F) { + f.Add(rune(0)) + f.Fuzz(func(t *testing.T, a rune) { + if a != 0 { + panic("this input caused a crash!") + } + }) +} + +func FuzzString(f *testing.F) { + f.Add("") + f.Fuzz(func(t *testing.T, a string) { + if a != "" { + panic("this input caused a crash!") + } + }) +} + +-- check_testdata.go -- +// +build ignore + +package main + +import ( + "bytes" + "crypto/sha256" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func main() { + target := os.Args[1] + dir := filepath.Join("testdata/fuzz", target) + + files, err := ioutil.ReadDir(dir) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if len(files) == 0 { + fmt.Fprintf(os.Stderr, "expect at least one new mutation to be written to testdata\n") + os.Exit(1) + } + + fname := files[0].Name() + contents, err := ioutil.ReadFile(filepath.Join(dir, fname)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if bytes.Equal(contents, []byte("aa")) { + fmt.Fprintf(os.Stderr, "newly written testdata entry was not mutated\n") + os.Exit(1) + } + // The hash of the bytes in the file should match the filename. + h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents))) + if !bytes.Equal([]byte(fname), h) { + fmt.Fprintf(os.Stderr, "hash of bytes %q does not match filename %q\n", h, fname) + os.Exit(1) + } +} diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt new file mode 100644 index 00000000000000..b5eab17349456c --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt @@ -0,0 +1,102 @@ +[!fuzz] skip + +# Check that if a worker does not call F.Fuzz or calls F.Fail first, +# 'go test' exits non-zero and no crasher is recorded. + +[short] skip + +! go test -fuzz=FuzzReturn +! exists testdata + +! go test -fuzz=FuzzSkip +! exists testdata + +! go test -fuzz=FuzzFail +! exists testdata + +! go test -fuzz=FuzzPanic +! exists testdata + +! go test -fuzz=FuzzNilPanic +! exists testdata + +! go test -fuzz=FuzzGoexit +! exists testdata + +! go test -fuzz=FuzzExit +! exists testdata + +-- go.mod -- +module m + +go 1.17 +-- fuzz_fail_test.go -- +package fuzz_fail + +import ( + "flag" + "os" + "runtime" + "testing" +) + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} + +func FuzzReturn(f *testing.F) { + if isWorker() { + return + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzSkip(f *testing.F) { + if isWorker() { + f.Skip() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzFail(f *testing.F) { + if isWorker() { + f.Fail() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzPanic(f *testing.F) { + if isWorker() { + panic("nope") + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzNilPanic(f *testing.F) { + if isWorker() { + panic(nil) + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzGoexit(f *testing.F) { + if isWorker() { + runtime.Goexit() + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzExit(f *testing.F) { + if isWorker() { + os.Exit(99) + } + f.Fuzz(func(*testing.T, []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator.txt b/src/cmd/go/testdata/script/test_fuzz_mutator.txt new file mode 100644 index 00000000000000..76b86488ad8ba5 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_mutator.txt @@ -0,0 +1,165 @@ +[!fuzz] skip + +# Test basic fuzzing mutator behavior. +# +# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value. +# Each fuzz function writes the input to a log file. The coordinator and worker +# use separate log files. check_logs.go verifies that the coordinator only +# tests seed values and the worker tests mutated values on the fuzz target. + +[short] skip + +go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz +go run check_logs.go fuzz fuzz.worker + +# TODO(b/181800488): remove -parallel=1, here and below. For now, when a +# crash is found, all workers keep running, wasting resources and reducing +# the number of executions available to the minimizer, increasing flakiness. + +# Test that the mutator is good enough to find several unique mutations. +! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go +! stdout '^ok' +stdout FAIL +stdout 'mutator found enough unique mutations' + +-- go.mod -- +module m + +go 1.16 +-- fuzz_test.go -- +package fuzz_test + +import ( + "flag" + "fmt" + "os" + "testing" +) + +var ( + logPath = flag.String("log", "", "path to log file") + logFile *os.File +) + +func TestMain(m *testing.M) { + flag.Parse() + var err error + logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if os.IsExist(err) { + *logPath += ".worker" + logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(m.Run()) +} + +func FuzzA(f *testing.F) { + f.Add([]byte("seed")) + f.Fuzz(func(t *testing.T, b []byte) { + fmt.Fprintf(logFile, "FuzzA %q\n", b) + }) +} + +func FuzzB(f *testing.F) { + f.Add([]byte("seed")) + f.Fuzz(func(t *testing.T, b []byte) { + fmt.Fprintf(logFile, "FuzzB %q\n", b) + }) +} + +-- check_logs.go -- +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "strings" +) + +func main() { + coordPath, workerPath := os.Args[1], os.Args[2] + + coordLog, err := os.Open(coordPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer coordLog.Close() + if err := checkCoordLog(coordLog); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + workerLog, err := os.Open(workerPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer workerLog.Close() + if err := checkWorkerLog(workerLog); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func checkCoordLog(r io.Reader) error { + b, err := io.ReadAll(r) + if err != nil { + return err + } + if string(bytes.TrimSpace(b)) != `FuzzB "seed"` { + return fmt.Errorf("coordinator: did not test FuzzB seed") + } + return nil +} + +func checkWorkerLog(r io.Reader) error { + scan := bufio.NewScanner(r) + var sawAMutant bool + for scan.Scan() { + line := scan.Text() + if !strings.HasPrefix(line, "FuzzA ") { + return fmt.Errorf("worker: tested something other than target: %s", line) + } + if strings.TrimPrefix(line, "FuzzA ") != `"seed"` { + sawAMutant = true + } + } + if err := scan.Err(); err != nil && err != bufio.ErrTooLong { + return err + } + if !sawAMutant { + return fmt.Errorf("worker: did not test any mutants") + } + return nil +} +-- mutator_test.go -- +package fuzz_test + +import ( + "testing" +) + +// TODO(katiehockman): re-work this test once we have a better fuzzing engine +// (ie. more mutations, and compiler instrumentation) +func FuzzMutator(f *testing.F) { + // TODO(katiehockman): simplify this once we can dedupe crashes (e.g. + // replace map with calls to panic, and simply count the number of crashes + // that were added to testdata) + crashes := make(map[string]bool) + // No seed corpus initiated + f.Fuzz(func(t *testing.T, b []byte) { + crashes[string(b)] = true + if len(crashes) >= 10 { + panic("mutator found enough unique mutations") + } + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt b/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt new file mode 100644 index 00000000000000..3764dcb9157a7f --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt @@ -0,0 +1,74 @@ +# TODO(jayconrod): support shared memory on more platforms. +[!darwin] [!linux] [!windows] skip + +# Verify that the fuzzing engine records the actual crashing input, even when +# a worker process terminates without communicating the crashing input back +# to the coordinator. + +[short] skip + +# Start fuzzing. The worker crashes after 100 iterations. +# The fuzz function writes the crashing input to "want" before exiting. +# The fuzzing engine reconstructs the crashing input and saves it to testdata. +! exists want +! go test -fuzz=. -parallel=1 -fuzztime=110x -fuzzminimizetime=10x -v +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' +stdout 'Failing input written to testdata' + +# Run the fuzz target without fuzzing. The fuzz function is called with the +# crashing input in testdata. The test passes if that input is identical to +# the one saved in "want". +exists want +go test -want=want + +-- go.mod -- +module fuzz + +go 1.17 +-- fuzz_test.go -- +package fuzz + +import ( + "bytes" + "flag" + "os" + "testing" +) + +var wantFlag = flag.String("want", "", "file containing previous crashing input") + +func FuzzRepeat(f *testing.F) { + i := 0 + f.Fuzz(func(t *testing.T, b []byte) { + i++ + if i == 100 { + f, err := os.OpenFile("want", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + // Couldn't create the file. Return without crashing, and try + // again. + i-- + t.Skip(err) + } + if _, err := f.Write(b); err != nil { + // We already created the file, so if we failed to write it + // there's not much we can do. The test will fail anyway, but + // at least make sure the error is logged to stdout. + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + os.Exit(1) // crash without communicating + } + + if *wantFlag != "" { + want, err := os.ReadFile(*wantFlag) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, b) { + t.Fatalf("inputs are not equal!\n got: %q\nwant:%q", b, want) + } + } + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt b/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt new file mode 100644 index 00000000000000..1051292fcb847e --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt @@ -0,0 +1,75 @@ +# NOTE: this test is skipped on Windows, since there's no concept of signals. +# When a process terminates another process, it provides an exit code. +[windows] skip +[!fuzz] skip +[short] skip + +# FuzzNonCrash sends itself a signal that does not appear to be a crash. +# We should not save a crasher. +! go test -fuzz=FuzzNonCrash +! exists testdata +! stdout unreachable +! stderr unreachable +stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: terminated' + +# FuzzKill sends itself a signal that cannot be caught by the worker process +# and does not appear to be a crash. +# We should not save a crasher. +! go test -fuzz=FuzzKill +! exists testdata +! stdout unreachable +! stderr unreachable +stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: killed' + +# FuzzCrash sends itself a signal that looks like a crash. +# We should save a crasher. +! go test -fuzz=FuzzCrash +exists testdata/fuzz/FuzzCrash +stdout '^\s+fuzzing process hung or terminated unexpectedly: exit status' + +-- go.mod -- +module test + +go 1.17 +-- fuzz_posix_test.go -- +// +build darwin freebsd linux + +package fuzz + +import ( + "syscall" + "testing" +) + +func FuzzNonCrash(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGTERM); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} + +func FuzzKill(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGKILL); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} + +func FuzzCrash(f *testing.F) { + f.Fuzz(func(*testing.T, bool) { + pid := syscall.Getpid() + if err := syscall.Kill(pid, syscall.SIGILL); err != nil { + panic(err) + } + // signal may not be received immediately. Wait for it. + select{} + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_parallel.txt b/src/cmd/go/testdata/script/test_fuzz_parallel.txt new file mode 100644 index 00000000000000..e6325208d008ad --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_parallel.txt @@ -0,0 +1,66 @@ +[!fuzz] skip +[short] skip + +# When running seed inputs, T.Parallel should let multiple inputs run in +# parallel. +go test -run=FuzzSeed + +# When fuzzing, T.Parallel should be safe to call, but it should have no effect. +# We just check that it doesn't hang, which would be the most obvious +# failure mode. +# TODO(jayconrod): check for the string "after T.Parallel". It's not printed +# by 'go test', so we can't distinguish that crasher from some other panic. +! go test -run=FuzzMutate -fuzz=FuzzMutate +exists testdata/fuzz/FuzzMutate + +# Testdata should now contain a corpus entry which will fail FuzzMutate. +# Run the test without fuzzing, setting -parallel to different values to make +# sure it fails, and doesn't hang. +! go test -run=FuzzMutate -parallel=1 +! go test -run=FuzzMutate -parallel=2 +! go test -run=FuzzMutate -parallel=4 + +-- go.mod -- +module fuzz_parallel + +go 1.17 +-- fuzz_parallel_test.go -- +package fuzz_parallel + +import ( + "sort" + "sync" + "testing" +) + +func FuzzSeed(f *testing.F) { + for _, v := range [][]byte{{'a'}, {'b'}, {'c'}} { + f.Add(v) + } + + var mu sync.Mutex + var before, after []byte + f.Cleanup(func() { + sort.Slice(after, func(i, j int) bool { return after[i] < after[j] }) + got := string(before) + string(after) + want := "abcabc" + if got != want { + f.Fatalf("got %q; want %q", got, want) + } + }) + + f.Fuzz(func(t *testing.T, b []byte) { + before = append(before, b...) + t.Parallel() + mu.Lock() + after = append(after, b...) + mu.Unlock() + }) +} + +func FuzzMutate(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) { + t.Parallel() + t.Error("after T.Parallel") + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt b/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt new file mode 100644 index 00000000000000..5434c723ad2244 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt @@ -0,0 +1,38 @@ +[!fuzz] skip + +! go test -fuzz=FuzzTrivial -coverprofile=prof +! stdout . +stderr '^cannot use -coverprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -blockprofile=prof +! stdout . +stderr '^cannot use -blockprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -cpuprofile=prof +! stdout . +stderr '^cannot use -cpuprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -memprofile=prof +! stdout . +stderr '^cannot use -memprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -mutexprofile=prof +! stdout . +stderr '^cannot use -mutexprofile flag with -fuzz flag$' + +! go test -fuzz=FuzzTrivial -trace=prof +! stdout . +stderr '^cannot use -trace flag with -fuzz flag$' + +-- go.mod -- +module example + +go 1.18 +-- fuzz_test.go -- +package example + +import "testing" + +func FuzzTrivial(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_return.txt b/src/cmd/go/testdata/script/test_fuzz_return.txt new file mode 100644 index 00000000000000..63275aad01d648 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_return.txt @@ -0,0 +1,19 @@ +[short] skip + +! go test . +stdout '^panic: testing: fuzz target must not return a value \[recovered\]$' + +-- go.mod -- +module test +go 1.18 +-- x_test.go -- +package test + +import "testing" + +func FuzzReturnErr(f *testing.F) { + f.Add("hello, validation!") + f.Fuzz(func(t *testing.T, in string) string { + return in + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_run.txt b/src/cmd/go/testdata/script/test_fuzz_run.txt new file mode 100644 index 00000000000000..99a4413d32210f --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_run.txt @@ -0,0 +1,143 @@ +[!fuzz] skip +[short] skip +env GOCACHE=$WORK/cache + +# Tests which verify the behavior and command line output when +# running a fuzz target as a unit test. + +# Tests without -run. + +! go test +stdout FAIL +stdout 'error here' + +! go test -v +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' + +# Tests where -run matches all seed corpora. + +! go test -run FuzzFoo/this +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -run /this +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -v -run FuzzFoo/this +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout 'no tests to run' + +! go test -v -run /this +stdout FAIL +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout 'no tests to run' + +# Tests where -run only matches one seed corpus which passes. + +go test -run FuzzFoo/thispasses +stdout ok +! stdout 'no tests to run' + +go test -run /thispasses +stdout ok +! stdout 'no tests to run' + +# Same tests in verbose mode +go test -v -run FuzzFoo/thispasses +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout '=== RUN FuzzFoo/thisfails' +! stdout 'no tests to run' + +go test -v -run /thispasses +stdout '=== RUN FuzzFoo/thispasses' +stdout '--- PASS: FuzzFoo/thispasses' +! stdout '=== RUN FuzzFoo/thisfails' +! stdout 'no tests to run' + +# Tests where -run only matches one seed corpus which fails. + +! go test -run FuzzFoo/thisfails +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -run /thisfails +stdout FAIL +stdout 'error here' +! stdout 'no tests to run' + +! go test -v -run FuzzFoo/thisfails +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +! stdout '=== RUN FuzzFoo/thispasses' +! stdout 'no tests to run' + +! go test -v -run /thisfails +stdout 'error here' +stdout '=== RUN FuzzFoo/thisfails' +stdout '--- FAIL: FuzzFoo/thisfails' +! stdout '=== RUN FuzzFoo/thispasses' +! stdout 'no tests to run' + +# Tests where -run doesn't match any seed corpora. + +go test -run FuzzFoo/nomatch +stdout ok + +go test -run /nomatch +stdout ok + +go test -v -run FuzzFoo/nomatch +stdout '=== RUN FuzzFoo' +stdout '--- PASS: FuzzFoo' +stdout ok +! stdout 'no tests to run' + +go test -v -run /nomatch +stdout '=== RUN FuzzFoo' +stdout '--- PASS: FuzzFoo' +stdout ok +! stdout 'no tests to run' + +-- go.mod -- +module example.com/x + +go 1.16 +-- x_test.go -- +package x + +import "testing" + +func FuzzFoo(f *testing.F) { + f.Add("this is fine") + f.Fuzz(func(t *testing.T, s string) { + if s == "fails" { + t.Error("error here") + } + }) +} +-- testdata/fuzz/FuzzFoo/thisfails -- +go test fuzz v1 +string("fails") +-- testdata/fuzz/FuzzFoo/thispasses -- +go test fuzz v1 +string("passes") diff --git a/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt b/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt new file mode 100644 index 00000000000000..3e3fbade2304f2 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt @@ -0,0 +1,203 @@ +[!fuzz-instrumented] skip +[short] skip +env GOCACHE=$WORK/cache + +# Test that fuzzing a target with a failure in f.Add prints the crash +# and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithAdd -run=FuzzWithAdd -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL + +# Test that fuzzing a target with a success in f.Add and a fuzztime of only +# 1 does not produce a crash. +go test -fuzz=FuzzWithGoodAdd -run=FuzzWithGoodAdd -fuzztime=1x +stdout ok +! stdout FAIL + +# Test that fuzzing a target with a failure in testdata/fuzz prints the crash +# and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithTestdata -run=FuzzWithTestdata -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout 'failure while testing seed corpus entry: FuzzWithTestdata/1' +stdout FAIL + +# Test that fuzzing a target with no seed corpus or cache finds a crash, prints +# it, and write it to testdata +! go test -fuzz=FuzzWithNoCache -run=FuzzWithNoCache -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithNoCache[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzWithCache +cp cache-file $GOCACHE/fuzz/example.com/x/FuzzWithCache/1 + +# Test that fuzzing a target with a failure in the cache prints the crash +# and writes this as a "new" crash to testdata/fuzz +! go test -fuzz=FuzzWithCache -run=FuzzWithCache -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithCache[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache +cp cache-file-bytes $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache/1 + +# Test that fuzzing a target with a failure in the cache minimizes it and writes +# the new crash to testdata/fuzz +! go test -fuzz=FuzzWithMinimizableCache -run=FuzzWithMinimizableCache -fuzztime=10000x +! stdout ^ok +stdout 'gathering baseline coverage' +stdout 'got the minimum size!' +stdout 'contains a non-zero byte of length 10' +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithMinimizableCache[/\\]' +stdout FAIL +# Make sure this crash didn't come from fuzzing +# (the log line that states fuzzing began shouldn't have printed) +! stdout 'execs' + +# Clear the fuzz cache and make sure it's gone +go clean -fuzzcache +! exists $GOCACHE/fuzz + +# The tests below should operate the exact same as the previous tests. If -fuzz +# is enabled, then whatever target is going to be fuzzed shouldn't be run by +# anything other than the workers. + +# Test that fuzzing a target (with -run=None set) with a failure in f.Add prints +# the crash and doesn't write anything to testdata/fuzz -fuzztime=1x +! go test -fuzz=FuzzWithAdd -run=None +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL + +# Test that fuzzing a target (with -run=None set) with a success in f.Add and a +# fuzztime of only 1 does not produce a crash. +go test -fuzz=FuzzWithGoodAdd -run=None -fuzztime=1x +stdout ok +! stdout FAIL + +# Test that fuzzing a target (with -run=None set) with a failure in +# testdata/fuzz prints the crash and doesn't write anything to testdata/fuzz +! go test -fuzz=FuzzWithTestdata -run=None -fuzztime=1x +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout FAIL + +# Write a crashing input to the cache +mkdir $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache +cp cache-file $GOCACHE/fuzz/example.com/x/FuzzRunNoneWithCache/1 + +# Test that fuzzing a target (with -run=None set) with a failure in the cache +# prints the crash and writes this as a "new" crash to testdata/fuzz +! go test -fuzz=FuzzRunNoneWithCache -run=None -fuzztime=1x +! stdout ^ok +stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzRunNoneWithCache[/\\]' +stdout FAIL + +# Clear the fuzz cache and make sure it's gone +go clean -fuzzcache +! exists $GOCACHE/fuzz + +# The tests below should operate the exact same way for the previous tests with +# a seed corpus (namely, they should still fail). However, the binary is built +# without instrumentation, so this should be a "testing only" run which executes +# the seed corpus before attempting to fuzz. + +go test -c +! exec ./x.test$GOEXE -test.fuzz=FuzzWithAdd -test.run=FuzzWithAdd -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithAdd[/\\]' +stdout FAIL +stderr warning + +go test -c +! exec ./x.test$GOEXE -test.fuzz=FuzzWithTestdata -test.run=FuzzWithTestdata -test.fuzztime=1x -test.fuzzcachedir=$WORK/cache +! stdout ^ok +! stdout 'Failing input written to testdata[/\\]fuzz[/\\]FuzzWithTestdata[/\\]' +stdout FAIL +stderr warning + +-- go.mod -- +module example.com/x + +go 1.16 +-- x_test.go -- +package x + +import "testing" + +func FuzzWithAdd(f *testing.F) { + f.Add(10) + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithGoodAdd(f *testing.F) { + f.Add(10) + f.Fuzz(func(t *testing.T, i int) { + if i != 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithTestdata(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithNoCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + t.Error("bad thing here") + }) +} + +func FuzzWithCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} + +func FuzzWithMinimizableCache(f *testing.F) { + f.Fuzz(func(t *testing.T, b []byte) { + if len(b) < 10 { + return + } + for _, n := range b { + if n != 0 { + if len(b) == 10 { + t.Log("got the minimum size!") + } + t.Fatalf("contains a non-zero byte of length %d", len(b)) + } + } + }) +} + +func FuzzRunNoneWithCache(f *testing.F) { + f.Fuzz(func(t *testing.T, i int) { + if i == 10 { + t.Error("bad thing here") + } + }) +} +-- testdata/fuzz/FuzzWithTestdata/1 -- +go test fuzz v1 +int(10) +-- cache-file -- +go test fuzz v1 +int(10) +-- cache-file-bytes -- +go test fuzz v1 +[]byte("11111111111111111111") diff --git a/src/cmd/go/testdata/script/test_fuzz_setenv.txt b/src/cmd/go/testdata/script/test_fuzz_setenv.txt new file mode 100644 index 00000000000000..2924569de11902 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_setenv.txt @@ -0,0 +1,45 @@ +[!fuzz] skip +[short] skip + +go test -fuzz=FuzzA -fuzztime=100x fuzz_setenv_test.go + +-- fuzz_setenv_test.go -- +package fuzz + +import ( + "flag" + "os" + "testing" +) + +func FuzzA(f *testing.F) { + if s := os.Getenv("TEST_FUZZ_SETENV_A"); isWorker() && s == "" { + f.Fatal("environment variable not set") + } else if !isWorker() && s != "" { + f.Fatal("environment variable already set") + } + f.Setenv("TEST_FUZZ_SETENV_A", "A") + if os.Getenv("TEST_FUZZ_SETENV_A") == "" { + f.Fatal("Setenv did not set environment variable") + } + f.Fuzz(func(*testing.T, []byte) {}) +} + +func FuzzB(f *testing.F) { + if os.Getenv("TEST_FUZZ_SETENV_A") != "" { + f.Fatal("environment variable not cleared after FuzzA") + } + f.Skip() +} + +func isWorker() bool { + f := flag.Lookup("test.fuzzworker") + if f == nil { + return false + } + get, ok := f.Value.(flag.Getter) + if !ok { + return false + } + return get.Get() == interface{}(true) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_test_race.txt b/src/cmd/go/testdata/script/test_fuzz_test_race.txt new file mode 100644 index 00000000000000..9d39cd684e7d19 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_test_race.txt @@ -0,0 +1,39 @@ +# Test that when both race detection and coverage instrumentation are enabled, +# and seed values are being executed, the race detector isn't mistakenly +# triggered. + +[short] skip +[!fuzz] skip +[!race] skip + +# Test with coverage instrumentation enabled (-fuzz) and race instrumentation +# but without actually fuzzing the target (by using a non-matching pattern) +go test -fuzz=xxx -race -v +! stderr 'race detected during execution of test' + +# Test with just race instrumentation enabled +go test -race -v +! stderr 'race detected during execution of test' + +# Test with coverage and race instrumentation enabled, and a matching fuzz +# pattern +go test -fuzz=FuzzRace -race -v -fuzztime=200x +! stderr 'race detected during execution of test' + +-- go.mod -- +module test + +-- race_test.go -- +package race + +import "testing" + +func FuzzRace(f *testing.F) { + for i := 0; i < 100; i++ { + f.Add(i) + } + + f.Fuzz(func(t *testing.T, i int) { + t.Parallel() + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_unsupported.txt b/src/cmd/go/testdata/script/test_fuzz_unsupported.txt new file mode 100644 index 00000000000000..1ed0b8a6f75368 --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_unsupported.txt @@ -0,0 +1,18 @@ +[fuzz] skip + +! go test -fuzz=. -fuzztime=1x +! stdout . +stderr '^-fuzz flag is not supported on '$GOOS'/'$GOARCH'$' + +-- go.mod -- +module example + +go 1.18 +-- fuzz_test.go -- +package example + +import "testing" + +func FuzzTrivial(f *testing.F) { + f.Fuzz(func(t *testing.T, _ []byte) {}) +} diff --git a/src/cmd/go/testdata/script/test_goroot_PATH.txt b/src/cmd/go/testdata/script/test_goroot_PATH.txt new file mode 100644 index 00000000000000..f49ec106ff56fc --- /dev/null +++ b/src/cmd/go/testdata/script/test_goroot_PATH.txt @@ -0,0 +1,41 @@ +# https://go.dev/issue/51473: to avoid the need for tests to rely on +# runtime.GOROOT, 'go test' should run the test with its own GOROOT/bin +# at the beginning of $PATH. + +[short] skip + +[!plan9] env PATH= +[plan9] env path= +go test . + +[!plan9] env PATH=$WORK${/}bin +[plan9] env path=$WORK${/}bin +go test . + +-- go.mod -- +module example + +go 1.19 +-- example_test.go -- +package example + +import ( + "os" + "os/exec" + "path/filepath" + "testing" +) + +func TestGoCommandExists(t *testing.T) { + got, err := exec.LookPath("go") + if err != nil { + t.Fatal(err) + } + + want := filepath.Join(os.Getenv("GOROOT"), "bin", "go" + os.Getenv("GOEXE")) + if got != want { + t.Fatalf(`exec.LookPath("go") = %q; want %q`, got, want) + } +} +-- $WORK/bin/README.txt -- +This directory contains no executables. diff --git a/src/cmd/go/testdata/script/test_issue45477.txt b/src/cmd/go/testdata/script/test_issue45477.txt new file mode 100644 index 00000000000000..f435b6a6f435d1 --- /dev/null +++ b/src/cmd/go/testdata/script/test_issue45477.txt @@ -0,0 +1,12 @@ +[short] skip # links and runs a test binary + +go test -v . + +-- go.mod -- +module example.com/pkg_test + +-- pkg.go -- +package pkg_test + +-- pkg_test.go -- +package pkg_test diff --git a/src/cmd/go/testdata/script/test_ppc64_linker_funcs.txt b/src/cmd/go/testdata/script/test_ppc64_linker_funcs.txt new file mode 100644 index 00000000000000..a33f9df724d159 --- /dev/null +++ b/src/cmd/go/testdata/script/test_ppc64_linker_funcs.txt @@ -0,0 +1,49 @@ +# Tests that the linker implements the PPC64 ELFv2 ABI +# register save and restore functions as defined in +# section 2.3.3.1 of the PPC64 ELFv2 ABI when linking +# external objects most likely compiled with gcc's +# -Os option. +# +# Verifies golang.org/issue/52366 for linux/ppc64le +[!linux] skip +[!gc] skip +[!cgo] skip +[!ppc64le] skip + +go build -ldflags='-linkmode=internal' +exec ./abitest +stdout success + +-- go.mod -- +module abitest + +-- abitest.go -- +package main + +/* +#cgo CFLAGS: -Os + +int foo_fpr() { + asm volatile("":::"fr31","fr30","fr29","fr28"); +} +int foo_gpr0() { + asm volatile("":::"r30","r29","r28"); +} +int foo_gpr1() { + asm volatile("":::"fr31", "fr30","fr29","fr28","r30","r29","r28"); +} +int foo_vr() { + asm volatile("":::"v31","v30","v29","v28"); +} +*/ +import "C" + +import "fmt" + +func main() { + C.foo_fpr() + C.foo_gpr0() + C.foo_gpr1() + C.foo_vr() + fmt.Println("success") +} diff --git a/src/cmd/go/testdata/script/test_race_install.txt b/src/cmd/go/testdata/script/test_race_install.txt index 8b1f343a325954..a1d47a7dd3c7b5 100644 --- a/src/cmd/go/testdata/script/test_race_install.txt +++ b/src/cmd/go/testdata/script/test_race_install.txt @@ -15,4 +15,4 @@ go 1.16 -- pkg/pkg.go -- package p -- stderr.txt -- -go test: -i flag is deprecated +go: -i flag is deprecated diff --git a/src/cmd/go/testdata/script/test_race_tag.txt b/src/cmd/go/testdata/script/test_race_tag.txt new file mode 100644 index 00000000000000..4b18ebc45496f8 --- /dev/null +++ b/src/cmd/go/testdata/script/test_race_tag.txt @@ -0,0 +1,29 @@ +# Tests Issue #54468 + +[short] skip 'links a test binary' +[!race] skip + +go mod tidy +go test -c -o=$devnull -race . + +! stderr 'cannot find package' + +-- go.mod -- +module testrace + +go 1.18 + +require rsc.io/sampler v1.0.0 +-- race_test.go -- +//go:build race + +package testrace + +import ( + "testing" + + _ "rsc.io/sampler" +) + +func TestRaceTag(t *testing.T) { +} diff --git a/src/cmd/go/testdata/script/test_relative_cmdline.txt b/src/cmd/go/testdata/script/test_relative_cmdline.txt index 2f9c80fe4d5d8d..96f7b872652b23 100644 --- a/src/cmd/go/testdata/script/test_relative_cmdline.txt +++ b/src/cmd/go/testdata/script/test_relative_cmdline.txt @@ -1,5 +1,7 @@ # Relative imports in command line package +env GO111MODULE=off + # Run tests outside GOPATH. env GOPATH=$WORK/tmp @@ -47,4 +49,4 @@ func TestF1(t *testing.T) { if F() != p2.F() { t.Fatal(F()) } -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/test_vet.txt b/src/cmd/go/testdata/script/test_vet.txt index 5af26b54f9141b..6151f912ae0db1 100644 --- a/src/cmd/go/testdata/script/test_vet.txt +++ b/src/cmd/go/testdata/script/test_vet.txt @@ -16,6 +16,22 @@ go test -vet=off p1.go ! stderr '[\\/]vet.*-shift' stdout '\[no test files\]' +# ensure all runs non-default vet +! go test -vet=all ./vetall/... +stderr 'using resp before checking for errors' + +# Test issue #47309 +! go test -vet=bools,xyz ./vetall/... +stderr '-vet argument must be a supported analyzer' + +# Test with a single analyzer +! go test -vet=httpresponse ./vetall/... +stderr 'using resp before checking for errors' + +# Test with a list of analyzers +go test -vet=atomic,bools,nilfunc ./vetall/... +stdout 'm/vetall.*\[no tests to run\]' + # Test issue #22890 go test m/vetcycle stdout 'm/vetcycle.*\[no test files\]' @@ -51,6 +67,21 @@ import "fmt" func F() { fmt.Printf("%d") // oops } +-- vetall/p.go -- +package p + +import "net/http" + +func F() { + resp, err := http.Head("example.com") + defer resp.Body.Close() + if err != nil { + panic(err) + } + // (defer statement belongs here) +} +-- vetall/p_test.go -- +package p -- vetcycle/p.go -- package p diff --git a/src/cmd/go/testdata/script/tooltags.txt b/src/cmd/go/testdata/script/tooltags.txt new file mode 100644 index 00000000000000..3076185bda728e --- /dev/null +++ b/src/cmd/go/testdata/script/tooltags.txt @@ -0,0 +1,55 @@ +env GOARCH=amd64 +env GOAMD64=v3 +go list -f '{{context.ToolTags}}' +stdout 'amd64.v1 amd64.v2 amd64.v3' + +env GOARCH=arm +env GOARM=6 +go list -f '{{context.ToolTags}}' +stdout 'arm.5 arm.6' + +env GOARCH=mips +env GOMIPS=hardfloat +go list -f '{{context.ToolTags}}' +stdout 'mips.hardfloat' + +env GOARCH=mips64 +env GOMIPS=hardfloat +go list -f '{{context.ToolTags}}' +stdout 'mips64.hardfloat' + +env GOARCH=ppc64 +env GOPPC64=power9 +go list -f '{{context.ToolTags}}' +stdout 'ppc64.power8 ppc64.power9' + +env GOARCH=ppc64 +env GOPPC64=power10 +go list -f '{{context.ToolTags}}' +stdout 'ppc64.power8 ppc64.power9 ppc64.power10' + +env GOARCH=ppc64le +env GOPPC64=power9 +go list -f '{{context.ToolTags}}' +stdout 'ppc64le.power8 ppc64le.power9' + +env GOARCH=ppc64le +env GOPPC64=power10 +go list -f '{{context.ToolTags}}' +stdout 'ppc64le.power8 ppc64le.power9 ppc64le.power10' + +env GOARCH=386 +env GO386=sse2 +go list -f '{{context.ToolTags}}' +stdout '386.sse2' + +env GOARCH=wasm +env GOWASM=satconv +go list -f '{{context.ToolTags}}' +stdout 'wasm.satconv' + +-- go.mod -- +module m + +-- p.go -- +package p diff --git a/src/cmd/go/testdata/script/vendor_complex.txt b/src/cmd/go/testdata/script/vendor_complex.txt index 9ca94e72c52749..290efdbd335590 100644 --- a/src/cmd/go/testdata/script/vendor_complex.txt +++ b/src/cmd/go/testdata/script/vendor_complex.txt @@ -2,7 +2,7 @@ env GO111MODULE=off # smoke test for complex build configuration go build -o complex.exe complex -[exec:gccgo] go build -compiler=gccgo -o complex.exe complex +[!cross] [exec:gccgo] go build -compiler=gccgo -o complex.exe complex -- complex/main.go -- package main diff --git a/src/cmd/go/testdata/script/vendor_list_issue11977.txt b/src/cmd/go/testdata/script/vendor_list_issue11977.txt index ce2e29f99a7902..cdab33c0892bff 100644 --- a/src/cmd/go/testdata/script/vendor_list_issue11977.txt +++ b/src/cmd/go/testdata/script/vendor_list_issue11977.txt @@ -2,7 +2,7 @@ [!exec:git] skip env GO111MODULE=off -go get -d github.com/rsc/go-get-issue-11864 +go get github.com/rsc/go-get-issue-11864 go list -f '{{join .TestImports "\n"}}' github.com/rsc/go-get-issue-11864/t stdout 'go-get-issue-11864/vendor/vendor.org/p' diff --git a/src/cmd/go/testdata/script/version.txt b/src/cmd/go/testdata/script/version.txt index 8615a4aac5978b..f7ead395c0cc06 100644 --- a/src/cmd/go/testdata/script/version.txt +++ b/src/cmd/go/testdata/script/version.txt @@ -2,7 +2,7 @@ go version stdout '^go version' -# Flags without files, or paths to misisng files, should error. +# Flags without files, or paths to missing files, should error. ! go version missing.exe ! go version -m stderr 'with arguments' @@ -16,11 +16,18 @@ stdout '^go version' env GOFLAGS= env GO111MODULE=on -# Skip the builds below if we are running in short mode. + +# Check that very basic version lookup succeeds. +go build empty.go +go version empty$GOEXE +[cgo] go build -ldflags=-linkmode=external empty.go +[cgo] go version empty$GOEXE + +# Skip the remaining builds if we are running in short mode. [short] skip # Check that 'go version' and 'go version -m' work on a binary built in module mode. -go get -d rsc.io/fortune +go get rsc.io/fortune go build -o fortune.exe rsc.io/fortune go version fortune.exe stdout '^fortune.exe: .+' @@ -28,6 +35,13 @@ go version -m fortune.exe stdout '^\tpath\trsc.io/fortune' stdout '^\tmod\trsc.io/fortune\tv1.0.0' +# Check the build info of a binary built from $GOROOT/src/cmd +go build -o test2json.exe cmd/test2json +go version -m test2json.exe +stdout '^test2json.exe: .+' +stdout '^\tpath\tcmd/test2json$' +! stdout 'mod' + # Repeat the test with -buildmode=pie. [!buildmode:pie] stop go build -buildmode=pie -o external.exe rsc.io/fortune @@ -50,3 +64,7 @@ stdout '^\tmod\trsc.io/fortune\tv1.0.0' -- go.mod -- module m + +-- empty.go -- +package main +func main(){} diff --git a/src/cmd/go/testdata/script/version_build_settings.txt b/src/cmd/go/testdata/script/version_build_settings.txt new file mode 100644 index 00000000000000..bfa7f5fbbe6d10 --- /dev/null +++ b/src/cmd/go/testdata/script/version_build_settings.txt @@ -0,0 +1,89 @@ +[short] skip + +# Compiler name is always added. +go build +go version -m m$GOEXE +stdout '^\tbuild\t-compiler=gc$' +stdout '^\tbuild\tGOOS=' +stdout '^\tbuild\tGOARCH=' +[amd64] stdout '^\tbuild\tGOAMD64=' +! stdout asmflags|gcflags|ldflags|gccgoflags + +# Toolchain flags are added if present. +# The raw flags are included, with package patterns if specified. +go build -asmflags=example.com/m=-D=FOO=bar +go version -m m$GOEXE +stdout '^\tbuild\t-asmflags=example\.com/m=-D=FOO=bar$' + +go build -gcflags=example.com/m=-N +go version -m m$GOEXE +stdout '^\tbuild\t-gcflags=example\.com/m=-N$' + +go build -ldflags=example.com/m=-w +go version -m m$GOEXE +stdout '^\tbuild\t-ldflags=example\.com/m=-w$' + +go build -trimpath +go version -m m$GOEXE +stdout '\tbuild\t-trimpath=true$' + +# gccgoflags are not added when gc is used, and vice versa. +# TODO: test gccgo. +go build -gccgoflags=all=UNUSED +go version -m m$GOEXE +! stdout gccgoflags + +# Build and tool tags are added but not release tags. +# "race" is included with build tags but not "cgo". +go build -tags=a,b +go version -m m$GOEXE +stdout '^\tbuild\t-tags=a,b$' +[race] go build -race +[race] go version -m m$GOEXE +[race] ! stdout '^\tbuild\t-tags=' +[race] stdout '^\tbuild\t-race=true$' + +# CGO flags are separate settings. +# CGO_ENABLED is always present. +# Other flags are added if CGO_ENABLED is true. +env CGO_ENABLED=0 +go build +go version -m m$GOEXE +stdout '^\tbuild\tCGO_ENABLED=0$' +! stdout CGO_CPPFLAGS|CGO_CFLAGS|CGO_CXXFLAGS|CGO_LDFLAGS + +[cgo] env CGO_ENABLED=1 +[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=1 +[cgo] env CGO_CFLAGS=-DFROM_CFLAGS=1 +[cgo] env CGO_CXXFLAGS=-DFROM_CXXFLAGS=1 +[cgo] env CGO_LDFLAGS=-L/extra/dir/does/not/exist +[cgo] go build '-ldflags=all=-linkmode=external -extldflags=-L/bonus/dir/does/not/exist' +[cgo] go version -m m$GOEXE +[cgo] stdout '^\tbuild\t-ldflags="all=-linkmode=external -extldflags=-L/bonus/dir/does/not/exist"$' +[cgo] stdout '^\tbuild\tCGO_ENABLED=1$' +[cgo] stdout '^\tbuild\tCGO_CPPFLAGS=-DFROM_CPPFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_CFLAGS=-DFROM_CFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_CXXFLAGS=-DFROM_CXXFLAGS=1$' +[cgo] stdout '^\tbuild\tCGO_LDFLAGS=-L/extra/dir/does/not/exist$' + +# https://go.dev/issue/52372: a cgo-enabled binary should not be stamped with +# CGO_ flags that contain paths. +[cgo] env CGO_ENABLED=1 +[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=1 +[cgo] env CGO_CFLAGS=-DFROM_CFLAGS=1 +[cgo] env CGO_CXXFLAGS=-DFROM_CXXFLAGS=1 +[cgo] env CGO_LDFLAGS=-L/extra/dir/does/not/exist +[cgo] go build -trimpath '-ldflags=all=-linkmode=external -extldflags=-L/bonus/dir/does/not/exist' +[cgo] go version -m m$GOEXE +[cgo] ! stdout '/extra/dir/does/not/exist' +[cgo] ! stdout '/bonus/dir/does/not/exist' +[cgo] stdout '^\tbuild\tCGO_ENABLED=1$' + +-- go.mod -- +module example.com/m + +go 1.18 +-- m.go -- +package main + +func main() {} diff --git a/src/cmd/go/testdata/script/version_buildvcs_bzr.txt b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt new file mode 100644 index 00000000000000..85db9bab6df402 --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt @@ -0,0 +1,107 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Bazaar specifics. +# The Git test covers common functionality. + +[!exec:bzr] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +env HOME=$WORK +cd repo/a +exec bzr whoami 'J.R. Gopher ' + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout bzrrevision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir .bzr +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/bzr +! exec bzr help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm .bzr + +# If there is an empty repository in a parent directory, only "modified" is tagged. +exec bzr init +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +! stdout vcs.revision +! stdout vcs.time +stdout '^\tbuild\tvcs.modified=true$' +cd .. + +# Revision and commit time are tagged for repositories with commits. +exec bzr add a README +exec bzr commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building an earlier commit should still build clean. +cp ../../outside/empty.txt ../NEWS +exec bzr add ../NEWS +exec bzr commit -m 'add NEWS' +exec bzr update -r1 +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=bzr$' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec bzr revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/bzr -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/bzr.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/src/cmd/go/testdata/script/version_buildvcs_fossil.txt b/src/cmd/go/testdata/script/version_buildvcs_fossil.txt new file mode 100644 index 00000000000000..720306868bc3c6 --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_fossil.txt @@ -0,0 +1,93 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Fossil specifics. +# The Git test covers common functionality. + +# "fossil" is the Fossil file server on Plan 9. +[plan9] skip +[!exec:fossil] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +env HOME=$WORK +env USER=gopher +[!windows] env fslckout=.fslckout +[windows] env fslckout=_FOSSIL_ +exec pwd +exec fossil init repo.fossil +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir $fslckout +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/fossil +! exec fossil help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm $fslckout + +# Revision and commit time are tagged for repositories with commits. +exec fossil open ../repo.fossil -f +exec fossil add a README +exec fossil commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as modified, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=fossil\n' +stdout '^\tbuild\tvcs.modified=true$' +exec fossil revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/fossil -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/fossil.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/src/cmd/go/testdata/script/version_buildvcs_git.txt b/src/cmd/go/testdata/script/version_buildvcs_git.txt new file mode 100644 index 00000000000000..44706870e22f22 --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_git.txt @@ -0,0 +1,164 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Git. Other tests focus on +# other VCS tools but may not cover common functionality. + +[!exec:git] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +# Also ensure that multiple errors are collected by "go list -e". +cd .. +mkdir .git +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/git +! exec git help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +go list -e -f '{{.ImportPath}}: {{.Error}}' ./... +stdout -count=1 '^example\.com/a: error obtaining VCS status' +stdout -count=1 '^example\.com/a/library: ' +stdout -count=1 '^example\.com/a/othermain: error obtaining VCS status' +cd .. +env PATH=$oldpath +rm .git + +# If there is an empty repository in a parent directory, only "uncommitted" is tagged. +exec git init +exec git config user.email gopher@golang.org +exec git config user.name 'J.R. Gopher' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs=git$' +stdout '^\tbuild\tvcs.modified=true$' +! stdout vcs.revision +! stdout vcs.time +rm $GOBIN/a$GOEXE + +# Revision and commit time are tagged for repositories with commits. +exec git add -A +exec git commit -m 'initial commit' +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec git checkout ../README +rm $GOBIN/a$GOEXE + +# If the build doesn't include any packages from the repository, +# there should be no VCS info. +go install example.com/cmd/a@v1.0.0 +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +rm $GOBIN/a$GOEXE + +go mod edit -require=example.com/c@v0.0.0 +go mod edit -replace=example.com/c@v0.0.0=../../outside/c +go install example.com/c +go version -m $GOBIN/c$GOEXE +! stdout vcs.revision +rm $GOBIN/c$GOEXE +exec git checkout go.mod + +# If the build depends on a package in the repository, but it's not in the +# main module, there should be no VCS info. +go mod edit -require=example.com/b@v0.0.0 +go mod edit -replace=example.com/b@v0.0.0=../b +go mod edit -require=example.com/d@v0.0.0 +go mod edit -replace=example.com/d@v0.0.0=../../outside/d +go install example.com/d +go version -m $GOBIN/d$GOEXE +! stdout vcs.revision +exec git checkout go.mod +rm $GOBIN/d$GOEXE + +# If we're loading multiple main packages, +# but they share the same VCS repository, +# we only need to execute VCS status commands once. +go list -x ./... +stdout -count=3 '^example.com' +stderr -count=1 '^git status' +stderr -count=1 '^git -c log.showsignature=false show' + +-- $WORK/fakebin/git -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/git.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- repo/a/library/f.go -- +package library +-- repo/a/othermain/f.go -- +package main + +func main() {} +-- repo/b/go.mod -- +module example.com/b + +go 1.18 +-- repo/b/b.go -- +package b +-- outside/empty.txt -- +-- outside/c/go.mod -- +module example.com/c + +go 1.18 +-- outside/c/main.go -- +package main + +func main() {} +-- outside/d/go.mod -- +module example.com/d + +go 1.18 + +require example.com/b v0.0.0 +-- outside/d/main.go -- +package main + +import _ "example.com/b" + +func main() {} diff --git a/src/cmd/go/testdata/script/version_buildvcs_git_gpg.txt b/src/cmd/go/testdata/script/version_buildvcs_git_gpg.txt new file mode 100644 index 00000000000000..dcf97d7c447f5b --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_git_gpg.txt @@ -0,0 +1,105 @@ +# This test checks that VCS information is stamped into Go binaries even when +# the current commit is signed and the use has configured git to display commit +# signatures. + +[!exec:git] skip +[!exec:gpg] skip +[short] skip +env GOBIN=$GOPATH/bin +env GNUPGHOME=$WORK/.gpupg +mkdir $GNUPGHOME +chmod 0700 $GNUPGHOME + +# Create GPG key +exec gpg --batch --passphrase '' --quick-generate-key gopher@golang.org +exec gpg --list-secret-keys --with-colons gopher@golang.org +cp stdout keyinfo.txt +go run extract_key_id.go keyinfo.txt +cp stdout keyid.txt + +# Initialize repo +cd repo/ +exec git init +exec git config user.email gopher@golang.org +exec git config user.name 'J.R. Gopher' +exec git config --add log.showSignature true +go run ../configure_signing_key.go ../keyid.txt + +# Create signed commit +cd a +exec git add -A +exec git commit -m 'initial commit' --gpg-sign +exec git log + +# Verify commit signature does not interfere with versioning +go install +go version -m $GOBIN/a +stdout '^\tbuild\tvcs\.revision=' +stdout '^\tbuild\tvcs\.time=' +stdout '^\tbuild\tvcs\.modified=false$' + +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} + +-- extract_key_id.go -- +package main + +import "fmt" +import "io/ioutil" +import "os" +import "strings" + +func main() { + err := run(os.Args[1]) + if err != nil { + panic(err) + } +} + +func run(keyInfoFilePath string) error { + contents, err := ioutil.ReadFile(keyInfoFilePath) + if err != nil { + return err + } + lines := strings.Split(string(contents), "\n") + for _, line := range lines { + fields := strings.Split(line, ":") + if fields[0] == "sec" { + fmt.Print(fields[4]) + return nil + } + } + return fmt.Errorf("key ID not found in: %s", keyInfoFilePath) +} + +-- configure_signing_key.go -- +package main + +import "io/ioutil" +import "os" +import "os/exec" + +func main() { + err := run(os.Args[1]) + if err != nil { + panic(err) + } +} + +func run(keyIdFilePath string) error { + keyId, err := ioutil.ReadFile(keyIdFilePath) + if err != nil { + return err + } + gitCmd := exec.Command("git", "config", "user.signingKey", string(keyId)) + return gitCmd.Run() +} diff --git a/src/cmd/go/testdata/script/version_buildvcs_hg.txt b/src/cmd/go/testdata/script/version_buildvcs_hg.txt new file mode 100644 index 00000000000000..fbbd886102e1bf --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_hg.txt @@ -0,0 +1,91 @@ +# This test checks that VCS information is stamped into Go binaries by default, +# controlled with -buildvcs. This test focuses on Mercurial specifics. +# The Git test covers common functionality. + +[!exec:hg] skip +[short] skip +env GOBIN=$WORK/gopath/bin +env oldpath=$PATH +cd repo/a + +# If there's no local repository, there's no VCS info. +go install +go version -m $GOBIN/a$GOEXE +! stdout hgrevision +rm $GOBIN/a$GOEXE + +# If there is a repository, but it can't be used for some reason, +# there should be an error. It should hint about -buildvcs=false. +cd .. +mkdir .hg +env PATH=$WORK${/}fakebin${:}$oldpath +chmod 0755 $WORK/fakebin/hg +! exec hg help +cd a +! go install +stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$' +rm $GOBIN/a$GOEXE +cd .. +env PATH=$oldpath +rm .hg + +# If there is an empty repository in a parent directory, only "uncommitted" is tagged. +exec hg init +cd a +go install +go version -m $GOBIN/a$GOEXE +! stdout vcs.revision +! stdout vcs.time +stdout '^\tbuild\tvcs.modified=true$' +cd .. + +# Revision and commit time are tagged for repositories with commits. +exec hg add a README +exec hg commit -m 'initial commit' +cd a +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.revision=' +stdout '^\tbuild\tvcs.time=' +stdout '^\tbuild\tvcs.modified=false$' +rm $GOBIN/a$GOEXE + +# Building with -buildvcs=false suppresses the info. +go install -buildvcs=false +go version -m $GOBIN/a$GOEXE +! stdout hgrevision +rm $GOBIN/a$GOEXE + +# An untracked file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt . +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +rm empty.txt +rm $GOBIN/a$GOEXE + +# An edited file is shown as uncommitted, even if it isn't part of the build. +cp ../../outside/empty.txt ../README +go install +go version -m $GOBIN/a$GOEXE +stdout '^\tbuild\tvcs.modified=true$' +exec hg revert ../README +rm $GOBIN/a$GOEXE + +-- $WORK/fakebin/hg -- +#!/bin/sh +exit 1 +-- $WORK/fakebin/hg.bat -- +exit 1 +-- repo/README -- +Far out in the uncharted backwaters of the unfashionable end of the western +spiral arm of the Galaxy lies a small, unregarded yellow sun. +-- repo/a/go.mod -- +module example.com/a + +go 1.18 +-- repo/a/a.go -- +package main + +func main() {} +-- outside/empty.txt -- diff --git a/src/cmd/go/testdata/script/version_buildvcs_nested.txt b/src/cmd/go/testdata/script/version_buildvcs_nested.txt new file mode 100644 index 00000000000000..a0c69f9c12acfd --- /dev/null +++ b/src/cmd/go/testdata/script/version_buildvcs_nested.txt @@ -0,0 +1,51 @@ +[!exec:git] skip +[!exec:hg] skip +[short] skip +env GOFLAGS='-n -buildvcs' + +# Create a root module in a root Git repository. +mkdir root +cd root +go mod init example.com/root +exec git init + +# Nesting repositories in parent directories are ignored, as the current +# directory main package, and containing main module are in the same repository. +# This is an error in GOPATH mode (to prevent VCS injection), but for modules, +# we assume users have control over repositories they've checked out. +mkdir hgsub +cd hgsub +exec hg init +cp ../../main.go main.go +! go build +stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*hgsub"$' +stderr '^\tUse -buildvcs=false to disable VCS stamping.$' +go build -buildvcs=false +go mod init example.com/root/hgsub +go build +cd .. + +# It's an error to build a package from a nested Git repository if the package +# is in a separate repository from the current directory or from the module +# root directory. +mkdir gitsub +cd gitsub +exec git init +exec git config user.name 'J.R.Gopher' +exec git config user.email 'gopher@golang.org' +cp ../../main.go main.go +! go build +stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*gitsub"$' +go build -buildvcs=false +go mod init example.com/root/gitsub +exec git commit --allow-empty -m empty # status commands fail without this +go build +rm go.mod +cd .. +! go build ./gitsub +stderr '^error obtaining VCS status: main package is in repository ".*gitsub" but current directory is in repository ".*root"$' +go build -buildvcs=false -o=gitsub${/} ./gitsub + +-- main.go -- +package main +func main() {} diff --git a/src/cmd/go/testdata/script/version_replace.txt b/src/cmd/go/testdata/script/version_replace.txt index ec98f4e3f3ac26..82b8504458be9a 100644 --- a/src/cmd/go/testdata/script/version_replace.txt +++ b/src/cmd/go/testdata/script/version_replace.txt @@ -1,7 +1,7 @@ [short] skip go mod download example.com/printversion@v0.1.0 example.com/printversion@v1.0.0 -go get -d example.com/printversion@v0.1.0 +go get example.com/printversion@v0.1.0 go install example.com/printversion go run example.com/printversion diff --git a/src/cmd/go/testdata/script/work.txt b/src/cmd/go/testdata/script/work.txt new file mode 100644 index 00000000000000..fa1558f9e690a1 --- /dev/null +++ b/src/cmd/go/testdata/script/work.txt @@ -0,0 +1,151 @@ +! go work init doesnotexist +stderr 'go: creating workspace file: no go.mod file exists in directory doesnotexist' +go env GOWORK +! stdout . + +go work init ./a ./b +cmpenv go.work go.work.want +go env GOWORK +stdout '^'$WORK'(\\|/)gopath(\\|/)src(\\|/)go.work$' + +! go run example.com/b +stderr 'a(\\|/)a.go:4:8: no required module provides package rsc.io/quote; to add it:\n\tcd '$WORK(\\|/)gopath(\\|/)src(\\|/)a'\n\tgo get rsc.io/quote' +cd a +go get rsc.io/quote +go env GOMOD # go env GOMOD reports the module in a single module context +stdout $GOPATH(\\|/)src(\\|/)a(\\|/)go.mod +cd .. +go run example.com/b +stdout 'Hello, world.' + +# And try from a different directory +cd c +go run example.com/b +stdout 'Hello, world.' +cd $GOPATH/src + +go list all # all includes both modules +stdout 'example.com/a' +stdout 'example.com/b' + +# -mod can only be set to readonly in workspace mode +go list -mod=readonly all +! go list -mod=mod all +stderr '^go: -mod may only be set to readonly when in workspace mode' +env GOWORK=off +go list -mod=mod all +env GOWORK= + +# Test that duplicates in the use list return an error +cp go.work go.work.backup +cp go.work.dup go.work +! go run example.com/b +stderr 'reading go.work: path .* appears multiple times in workspace' +cp go.work.backup go.work + +cp go.work.d go.work +go run example.com/d + +# Test that we don't run into "newRequirements called with unsorted roots" +# panic with unsorted main modules. +cp go.work.backwards go.work +go run example.com/d + +# Test that command-line-arguments work inside and outside modules. +# This exercises the code that determines which module command-line-arguments +# belongs to. +go list ./b/main.go +env GOWORK=off +go build -n -o foo foo.go +env GOWORK= +go build -n -o foo foo.go + +-- go.work.dup -- +go 1.18 + +use ( + a + b + ../src/a +) +-- go.work.want -- +go $goversion + +use ( + ./a + ./b +) +-- go.work.d -- +go 1.18 + +use ( + a + b + d +) +-- a/go.mod -- + +module example.com/a + +-- a/a.go -- +package a + +import "fmt" +import "rsc.io/quote" + +func HelloFromA() { + fmt.Println(quote.Hello()) +} + +-- b/go.mod -- + +module example.com/b + +-- b/main.go -- +package main + +import "example.com/a" + +func main() { + a.HelloFromA() +} +-- b/lib/hello.go -- +package lib + +import "example.com/a" + +func Hello() { + a.HelloFromA() +} + +-- c/README -- +Create this directory so we can cd to +it and make sure paths are interpreted +relative to the go.work, not the cwd. +-- d/go.mod -- +module example.com/d + +-- d/main.go -- +package main + +import "example.com/b/lib" + +func main() { + lib.Hello() +} + +-- go.work.backwards -- +go 1.18 + +use ( + d + b + a +) + +-- foo.go -- +package main +import "fmt" +func main() { + fmt.Println("Hello, World") +} diff --git a/src/cmd/go/testdata/script/work_build_no_modules.txt b/src/cmd/go/testdata/script/work_build_no_modules.txt new file mode 100644 index 00000000000000..c9859b437e29cf --- /dev/null +++ b/src/cmd/go/testdata/script/work_build_no_modules.txt @@ -0,0 +1,13 @@ +! go build . +stderr 'go: no modules were found in the current workspace; see ''go help work''' + +-- go.work -- +go 1.18 +-- go.mod -- +go 1.18 + +module foo +-- foo.go -- +package main + +func main() {} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_edit.txt b/src/cmd/go/testdata/script/work_edit.txt new file mode 100644 index 00000000000000..ad5de6286d288a --- /dev/null +++ b/src/cmd/go/testdata/script/work_edit.txt @@ -0,0 +1,162 @@ +# Test editing go.work files. + +go work init m +cmpenv go.work go.work.want_initial + +go work edit -use n +cmpenv go.work go.work.want_use_n + +go work edit -go 1.18 +cmp go.work go.work.want_go_118 + +go work edit -dropuse m +cmp go.work go.work.want_dropuse_m + +go work edit -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' +cmp go.work go.work.want_add_replaces + +go work edit -use n -use ../a -use /b -use c -use c +cmp go.work go.work.want_multiuse + +go work edit -dropuse /b -dropuse n +cmp go.work go.work.want_multidropuse + +go work edit -dropreplace='x.1@v1.4.0' +cmp go.work go.work.want_dropreplace + +go work edit -print -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0 +cmp stdout go.work.want_print + +go work edit -json -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0 +cmp stdout go.work.want_json + +env GOWORK=$GOPATH/src/unformatted +go work edit -print -fmt +cmp stdout formatted + +-- m/go.mod -- +module m + +go 1.18 +-- go.work.want_initial -- +go $goversion + +use ./m +-- go.work.want_use_n -- +go $goversion + +use ( + ./m + ./n +) +-- go.work.want_go_118 -- +go 1.18 + +use ( + ./m + ./n +) +-- go.work.want_dropuse_m -- +go 1.18 + +use ./n +-- go.work.want_add_replaces -- +go 1.18 + +use ./n + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_multiuse -- +go 1.18 + +use ( + ../a + ./c + ./n + /b +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_multidropuse -- +go 1.18 + +use ( + ../a + ./c +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) +-- go.work.want_dropreplace -- +go 1.18 + +use ( + ../a + ./c +) + +replace x.1 v1.3.0 => y.1 v1.4.0 +-- go.work.want_print -- +go 1.19 + +use ( + ../a + ./b +) + +replace x.1 v1.4.0 => ../z +-- go.work.want_json -- +{ + "Go": "1.19", + "Use": [ + { + "DiskPath": "../a" + }, + { + "DiskPath": "./b" + } + ], + "Replace": [ + { + "Old": { + "Path": "x.1", + "Version": "v1.4.0" + }, + "New": { + "Path": "../z" + } + } + ] +} +-- unformatted -- +go 1.18 + use ( + a + b + c + ) + replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z + ) +-- formatted -- +go 1.18 + +use ( + a + b + c +) + +replace ( + x.1 v1.3.0 => y.1 v1.4.0 + x.1 v1.4.0 => ../z +) diff --git a/src/cmd/go/testdata/script/work_env.txt b/src/cmd/go/testdata/script/work_env.txt new file mode 100644 index 00000000000000..511bb4e2cb62ea --- /dev/null +++ b/src/cmd/go/testdata/script/work_env.txt @@ -0,0 +1,28 @@ +go env GOWORK +stdout '^'$GOPATH'[\\/]src[\\/]go.work$' +go env +stdout '^(set )?GOWORK="?'$GOPATH'[\\/]src[\\/]go.work"?$' + +cd .. +go env GOWORK +! stdout . +go env +stdout 'GOWORK=("")?' + +cd src +go env GOWORK +stdout 'go.work' + +env GOWORK='off' +go env GOWORK +stdout 'off' + +! go env -w GOWORK=off +stderr '^go: GOWORK cannot be modified$' + +-- go.work -- +go 1.18 + +use a +-- a/go.mod -- +module example.com/a diff --git a/src/cmd/go/testdata/script/work_goproxy_off.txt b/src/cmd/go/testdata/script/work_goproxy_off.txt new file mode 100644 index 00000000000000..0a602e3d7bd88c --- /dev/null +++ b/src/cmd/go/testdata/script/work_goproxy_off.txt @@ -0,0 +1,59 @@ +go work init +go work use . ./sub + +# Verify that the go.mod files for both modules in the workspace are tidy, +# and add missing go.sum entries as needed. + +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig + +cd sub +cp go.mod go.mod.orig +go mod tidy +cmp go.mod go.mod.orig +cd .. + +go list -m all +stdout '^rsc\.io/quote v1\.5\.1$' +stdout '^rsc\.io/sampler v1\.3\.1$' + +# Now remove the module dependencies from the module cache. +# Because one module upgrades a transitive dependency needed by another, +# listing the modules in the workspace should error out. + +go clean -modcache +env GOPROXY=off +! go list -m all +stderr '^go: rsc.io/sampler@v1.3.0: module lookup disabled by GOPROXY=off$' + +-- example.go -- +package example + +import _ "rsc.io/sampler" +-- go.mod -- +module example + +go 1.19 + +require rsc.io/sampler v1.3.0 + +require ( + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect + rsc.io/testonly v1.0.0 // indirect +) +-- sub/go.mod -- +module example/sub + +go 1.19 + +require rsc.io/quote v1.5.1 + +require ( + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect + rsc.io/sampler v1.3.1 // indirect +) +-- sub/sub.go -- +package example + +import _ "rsc.io/quote" diff --git a/src/cmd/go/testdata/script/work_gowork.txt b/src/cmd/go/testdata/script/work_gowork.txt new file mode 100644 index 00000000000000..1cfbf0ca18fac3 --- /dev/null +++ b/src/cmd/go/testdata/script/work_gowork.txt @@ -0,0 +1,24 @@ +env GOWORK=stop.work +! go list a # require absolute path +! stderr panic +env GOWORK=doesnotexist +! go list a +! stderr panic + +env GOWORK=$GOPATH/src/stop.work +go list -n a +go build -n a +go test -n a + +-- stop.work -- +go 1.18 + +use ./a +-- a/a.go -- +package a +-- a/a_test.go -- +package a +-- a/go.mod -- +module a + +go 1.18 \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_init_gowork.txt b/src/cmd/go/testdata/script/work_init_gowork.txt new file mode 100644 index 00000000000000..55ac99b8c009dc --- /dev/null +++ b/src/cmd/go/testdata/script/work_init_gowork.txt @@ -0,0 +1,19 @@ +# Test that the GOWORK environment variable flag is used by go work init. + +! exists go.work +go work init +exists go.work + +env GOWORK=$GOPATH/src/foo/foo.work +! exists foo/foo.work +go work init +exists foo/foo.work + +env GOWORK= +cd foo/bar +! go work init +stderr 'already exists' + +# Create directories to make go.work files in. +-- foo/dummy.txt -- +-- foo/bar/dummy.txt -- diff --git a/src/cmd/go/testdata/script/work_init_path.txt b/src/cmd/go/testdata/script/work_init_path.txt new file mode 100644 index 00000000000000..e3977882a0a012 --- /dev/null +++ b/src/cmd/go/testdata/script/work_init_path.txt @@ -0,0 +1,17 @@ +# Regression test for https://go.dev/issue/51448. +# 'go work init . foo/bar' should produce a go.work file +# with the same paths as 'go work init; go work use -r .'. + +go work init . foo/bar +mv go.work go.work.init + +go work init +go work use -r . +cmp go.work go.work.init + +-- go.mod -- +module example +go 1.18 +-- foo/bar/go.mod -- +module example +go 1.18 diff --git a/src/cmd/go/testdata/script/work_install_submodule.txt b/src/cmd/go/testdata/script/work_install_submodule.txt new file mode 100644 index 00000000000000..3d1171736d9c7c --- /dev/null +++ b/src/cmd/go/testdata/script/work_install_submodule.txt @@ -0,0 +1,36 @@ +# This is a regression test for golang.org/issue/50036 +# Don't check sums for other modules in the workspace. + +cd m/sub +go install -n + +-- go.work -- +go 1.18 + +use ( + ./m + ./m/sub +) +-- m/go.mod -- +module example.com/m + +go 1.18 + +-- m/m.go -- +package m + +func M() {} +-- m/sub/go.mod -- +module example.com/m/sub + +go 1.18 + +require example.com/m v1.0.0 +-- m/sub/main.go -- +package main + +import "example.com/m" + +func main() { + m.M() +} diff --git a/src/cmd/go/testdata/script/work_issue51204.txt b/src/cmd/go/testdata/script/work_issue51204.txt new file mode 100644 index 00000000000000..d48300206074eb --- /dev/null +++ b/src/cmd/go/testdata/script/work_issue51204.txt @@ -0,0 +1,57 @@ +go work sync + +go list -f '{{.Dir}}' example.com/test +stdout '^'$PWD${/}test'$' + +-- go.work -- +go 1.18 + +use ( + ./test2 + ./test2/sub +) +-- test/go.mod -- +module example.com/test + +go 1.18 +-- test/file.go -- +package test + +func DoSomething() { +} +-- test2/go.mod -- +module example.com/test2 + +go 1.18 + +replace example.com/test => ../test + +require example.com/test v0.0.0-00010101000000-000000000000 +-- test2/file.go -- +package test2 + +import ( + "example.com/test" +) + +func DoSomething() { + test.DoSomething() +} +-- test2/sub/go.mod -- +module example.com/test2/sub + +go 1.18 + +replace example.com/test => ../../test + +require example.com/test v0.0.0 +-- test2/sub/file.go -- +package test2 + +import ( + "example.com/test" +) + +func DoSomething() { + test.DoSomething() +} diff --git a/src/cmd/go/testdata/script/work_issue54048.txt b/src/cmd/go/testdata/script/work_issue54048.txt new file mode 100644 index 00000000000000..ced3d9074a1592 --- /dev/null +++ b/src/cmd/go/testdata/script/work_issue54048.txt @@ -0,0 +1,19 @@ +! go list -m -json all +stderr 'go: module example.com/foo appears multiple times in workspace' + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) +-- a/go.mod -- +module example.com/foo + +go 1.18 + +-- b/go.mod -- +module example.com/foo + +go 1.18 diff --git a/src/cmd/go/testdata/script/work_module_not_in_go_work.txt b/src/cmd/go/testdata/script/work_module_not_in_go_work.txt new file mode 100644 index 00000000000000..5d3e64ce80748c --- /dev/null +++ b/src/cmd/go/testdata/script/work_module_not_in_go_work.txt @@ -0,0 +1,34 @@ +# This is a regression test for issue #49632. +# The Go command should mention go.work if the user +# tries to load a local package that's in a module +# that's not in go.work and can't be resolved. + +! go list ./... +stderr 'pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies' + +! go list ./a/c +stderr 'directory a[\\/]c is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using:\n\tgo work use a' + +! go install ./a/c +stderr 'directory a[\\/]c is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using:\n\tgo work use a' + +cd a/c +! go run . +stderr 'directory . is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using:\n\tgo work use \.\.' + +-- go.work -- +go 1.18 + +use ./b +-- a/go.mod -- +module example.com/a + +go 1.18 +-- a/a.go -- +package a +-- a/c/c.go -- +package main +-- b/go.mod -- +module example.com/b + +go 1.18 diff --git a/src/cmd/go/testdata/script/work_nowork.txt b/src/cmd/go/testdata/script/work_nowork.txt new file mode 100644 index 00000000000000..b4c9b1d9cf3dc9 --- /dev/null +++ b/src/cmd/go/testdata/script/work_nowork.txt @@ -0,0 +1,20 @@ +! go work use +stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$' + +! go work use . +stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$' + +! go work edit +stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$' + +! go work edit -go=1.18 +stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$' + +! go work sync +stderr '^go: no go\.work file found\n\t\(run ''go work init'' first or specify path using GOWORK environment variable\)$' + +-- go.mod -- +module example +go 1.18 +-- README.txt -- +There is no go.work file here. diff --git a/src/cmd/go/testdata/script/work_prune.txt b/src/cmd/go/testdata/script/work_prune.txt new file mode 100644 index 00000000000000..7e2ae4e6ce4ba3 --- /dev/null +++ b/src/cmd/go/testdata/script/work_prune.txt @@ -0,0 +1,104 @@ +# This test makes sure workspace mode's handling of the module graph +# is compatible with module pruning. The graph we load from either of +# the workspace modules should be the same, even if their graphs +# don't overlap. +# +# This is the module graph in the test: +# +# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 +# example.com/p -> example.com/q v1.0.0 +# +# If we didn't load the whole graph and didn't load the dependencies of b +# when loading p, we would end up loading q v1.0.0, rather than v1.1.0, +# which is selected by MVS. +# TODO(#48331): We currently load the wrong version of q. Fix this. + +go list -m -f '{{.Version}}' example.com/q +stdout '^v1.1.0$' + +-- go.work -- +go 1.18 + +use ( + ./a + ./p +) +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 + +replace example.com/b v1.0.0 => ../b +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/q v1.1.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- b/b.go -- +package b + +func B() { +} +-- b/b_test.go -- +package b + +import "example.com/q" + +func TestB() { + q.PrintVersion() +} +-- p/go.mod -- +module example.com/p + +go 1.18 + +require example.com/q v1.0.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- p/main.go -- +package main + +import "example.com/q" + +func main() { + q.PrintVersion() +} +-- q1_0_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_0_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.0.0") +} +-- q1_1_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_1_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.1.0") +} diff --git a/src/cmd/go/testdata/script/work_prune_all.txt b/src/cmd/go/testdata/script/work_prune_all.txt new file mode 100644 index 00000000000000..a7ad9c04aff3d4 --- /dev/null +++ b/src/cmd/go/testdata/script/work_prune_all.txt @@ -0,0 +1,176 @@ +# This test makes sure workspace mode's handling of the module graph +# is compatible with module pruning. The graph we load from either of +# the workspace modules should be the same, even if their graphs +# don't overlap. +# +# This is the module graph in the test: +# +# example.com/p -> example.com/q v1.0.0 +# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 -> example.com/w v1.0.0 -> example.com/x v1.0.0 -> example.com/y v1.0.0 +# |-> example.com/z v1.0.0 |-> example.com/z v1.1.0 +# |-> example.com/q v1.0.5 -> example.com/r v1.0.0 +# If we didn't load the whole graph and didn't load the dependencies of b +# when loading p, we would end up loading q v1.0.0, rather than v1.1.0, +# which is selected by MVS. + +go list -m all +stdout 'example.com/w v1.0.0' +stdout 'example.com/q v1.1.0' +stdout 'example.com/z v1.1.0' +stdout 'example.com/x v1.0.0' +! stdout 'example.com/r' +! stdout 'example.com/y' + +-- go.work -- +go 1.18 + +use ( + ./a + ./p +) + +replace example.com/b v1.0.0 => ./b +replace example.com/q v1.0.0 => ./q1_0_0 +replace example.com/q v1.0.5 => ./q1_0_5 +replace example.com/q v1.1.0 => ./q1_1_0 +replace example.com/r v1.0.0 => ./r +replace example.com/w v1.0.0 => ./w +replace example.com/x v1.0.0 => ./x +replace example.com/y v1.0.0 => ./y +replace example.com/z v1.0.0 => ./z1_0_0 +replace example.com/z v1.1.0 => ./z1_1_0 + +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 +require example.com/z v1.0.0 +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/q v1.1.0 +-- b/b.go -- +package b + +func B() { +} +-- p/go.mod -- +module example.com/p + +go 1.18 + +require example.com/q v1.0.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- p/main.go -- +package main + +import "example.com/q" + +func main() { + q.PrintVersion() +} +-- q1_0_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_0_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.0.0") +} +-- q1_0_5/go.mod -- +module example.com/q + +go 1.18 + +require example.com/r v1.0.0 +-- q1_0_5/q.go -- +package q + +import _ "example.com/r" +-- q1_1_0/go.mod -- +module example.com/q + +require example.com/w v1.0.0 +require example.com/z v1.1.0 + +go 1.18 +-- q1_1_0/q.go -- +package q + +import _ "example.com/w" +import _ "example.com/z" + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.1.0") +} +-- r/go.mod -- +module example.com/r + +go 1.18 + +require example.com/r v1.0.0 +-- r/r.go -- +package r +-- w/go.mod -- +module example.com/w + +go 1.18 + +require example.com/x v1.0.0 +-- w/w.go -- +package w +-- w/w_test.go -- +package w + +import _ "example.com/x" +-- x/go.mod -- +module example.com/x + +go 1.18 +-- x/x.go -- +package x +-- x/x_test.go -- +package x +import _ "example.com/y" +-- y/go.mod -- +module example.com/y + +go 1.18 +-- y/y.go -- +package y +-- z1_0_0/go.mod -- +module example.com/z + +go 1.18 + +require example.com/q v1.0.5 +-- z1_0_0/z.go -- +package z + +import _ "example.com/q" +-- z1_1_0/go.mod -- +module example.com/z + +go 1.18 +-- z1_1_0/z.go -- +package z diff --git a/src/cmd/go/testdata/script/work_regression_hang.txt b/src/cmd/go/testdata/script/work_regression_hang.txt new file mode 100644 index 00000000000000..a7661b68ad400c --- /dev/null +++ b/src/cmd/go/testdata/script/work_regression_hang.txt @@ -0,0 +1,71 @@ +# This test makes checks against a regression of a bug in the Go command +# where the module loader hung forever because all main module dependencies +# kept workspace pruning instead of adopting the pruning in their go.mod +# files, and the loader kept adding dependencies on the queue until they +# were either pruned or unpruned, never breaking a module dependency cycle. +# +# This is the module graph in the test: +# +# /-------------------------\ +# | | +# V | +# example.com/a -> example.com/b v1.0.0 -> example.com/c v1.1.0 + +go list -m -f '{{.Version}}' example.com/c + +-- go.work -- +go 1.16 + +use ( + ./a +) +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 + +replace example.com/b v1.0.0 => ../b +replace example.com/c v1.0.0 => ../c +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/c v1.0.0 +-- b/b.go -- +package b + +func B() { +} +-- b/cmd/main.go -- +package main + +import "example.com/c" + +func main() { + c.C() +} +-- c/go.mod -- +module example.com/c + +go 1.18 + +require example.com/b v1.0.0 +-- c/c.go -- +package c + +import "example.com/b" + +func C() { + b.B() +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_replace.txt b/src/cmd/go/testdata/script/work_replace.txt new file mode 100644 index 00000000000000..81268e50697eb7 --- /dev/null +++ b/src/cmd/go/testdata/script/work_replace.txt @@ -0,0 +1,55 @@ +# Support replace statement in go.work file + +# Replacement in go.work file, and none in go.mod file. +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep' + +# Wildcard replacement in go.work file overrides version replacement in go.mod +# file. +go list -m example.com/other +stdout 'example.com/other v1.0.0 => ./other2' + +-- go.work -- +use m + +replace example.com/dep => ./dep +replace example.com/other => ./other2 + +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +require example.com/other v1.0.0 + +replace example.com/other v1.0.0 => ./other +-- m/m.go -- +package m + +import "example.com/dep" +import "example.com/other" + +func F() { + dep.G() + other.H() +} +-- dep/go.mod -- +module example.com/dep +-- dep/dep.go -- +package dep + +func G() { +} +-- other/go.mod -- +module example.com/other +-- other/dep.go -- +package other + +func G() { +} +-- other2/go.mod -- +module example.com/other +-- other2/dep.go -- +package other + +func G() { +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_replace_conflict.txt b/src/cmd/go/testdata/script/work_replace_conflict.txt new file mode 100644 index 00000000000000..7b71b0fbd78cfa --- /dev/null +++ b/src/cmd/go/testdata/script/work_replace_conflict.txt @@ -0,0 +1,53 @@ +# Conflicting replaces in workspace modules returns error that suggests +# overriding it in the go.work file. + +! go list -m example.com/dep +stderr 'go: conflicting replacements for example.com/dep@v1.0.0:\n\t'$PWD${/}'dep1\n\t'$PWD${/}'dep2\nuse "go work edit -replace example.com/dep@v1.0.0=\[override\]" to resolve' +go work edit -replace example.com/dep@v1.0.0=./dep1 +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep1' + +-- foo -- +-- go.work -- +use m +use n +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +replace example.com/dep v1.0.0 => ../dep1 +-- m/m.go -- +package m + +import "example.com/dep" + +func F() { + dep.G() +} +-- n/go.mod -- +module example.com/n + +require example.com/dep v1.0.0 +replace example.com/dep v1.0.0 => ../dep2 +-- n/n.go -- +package n + +import "example.com/dep" + +func F() { + dep.G() +} +-- dep1/go.mod -- +module example.com/dep +-- dep1/dep.go -- +package dep + +func G() { +} +-- dep2/go.mod -- +module example.com/dep +-- dep2/dep.go -- +package dep + +func G() { +} diff --git a/src/cmd/go/testdata/script/work_replace_conflict_override.txt b/src/cmd/go/testdata/script/work_replace_conflict_override.txt new file mode 100644 index 00000000000000..c62084bee692ce --- /dev/null +++ b/src/cmd/go/testdata/script/work_replace_conflict_override.txt @@ -0,0 +1,57 @@ +# Conflicting workspace module replaces can be overridden by a replace in the +# go.work file. + +go list -m example.com/dep +stdout 'example.com/dep v1.0.0 => ./dep3' + +-- go.work -- +use m +use n +replace example.com/dep => ./dep3 +-- m/go.mod -- +module example.com/m + +require example.com/dep v1.0.0 +replace example.com/dep => ./dep1 +-- m/m.go -- +package m + +import "example.com/dep" + +func F() { + dep.G() +} +-- n/go.mod -- +module example.com/n + +require example.com/dep v1.0.0 +replace example.com/dep => ./dep2 +-- n/n.go -- +package n + +import "example.com/dep" + +func F() { + dep.G() +} +-- dep1/go.mod -- +module example.com/dep +-- dep1/dep.go -- +package dep + +func G() { +} +-- dep2/go.mod -- +module example.com/dep +-- dep2/dep.go -- +package dep + +func G() { +} +-- dep3/go.mod -- +module example.com/dep +-- dep3/dep.go -- +package dep + +func G() { +} diff --git a/src/cmd/go/testdata/script/work_replace_main_module.txt b/src/cmd/go/testdata/script/work_replace_main_module.txt new file mode 100644 index 00000000000000..b213764280ed21 --- /dev/null +++ b/src/cmd/go/testdata/script/work_replace_main_module.txt @@ -0,0 +1,45 @@ +# Ensure that replaces of the main module in workspace modules +# are ignored, and replaces in the go.work file are disallowed. +# This tests against an issue where requirements of the +# main module were being ignored because the main module +# was replaced in a transitive dependency with another +# version. + +go list example.com/dep + +cp replace_main_module.go.work go.work +! go list example.com/dep +stderr 'go: workspace module example.com/mainmoda is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.' + +-- replace_main_module.go.work -- +go 1.18 +use ( + ./mainmoda + ./mainmodb +) +replace example.com/mainmoda => ../mainmodareplacement +-- go.work -- +go 1.18 +use ( + ./mainmoda + ./mainmodb +) +-- mainmoda/go.mod -- +module example.com/mainmoda + +go 1.18 + +require example.com/dep v1.0.0 +replace example.com/dep => ../dep + +-- dep/go.mod -- +module example.com/dep +-- dep/dep.go -- +package dep +-- mainmodb/go.mod -- +module example.com/mainmodb +go 1.18 +replace example.com/mainmoda => ../mainmodareplacement +-- mainmodareplacement/go.mod -- +module example.com/mainmoda +go 1.18 \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_sum.txt b/src/cmd/go/testdata/script/work_sum.txt new file mode 100644 index 00000000000000..19dbb90507a57f --- /dev/null +++ b/src/cmd/go/testdata/script/work_sum.txt @@ -0,0 +1,34 @@ +# Test adding sums to go.work.sum when sum isn't in go.mod. + +go run . +cmp go.work.sum want.sum + +-- want.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +-- go.work -- +go 1.18 + +use . +-- go.mod -- +go 1.18 + +module example.com/hi + +require "rsc.io/quote" v1.5.2 +-- go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_sum_mismatch.txt b/src/cmd/go/testdata/script/work_sum_mismatch.txt new file mode 100644 index 00000000000000..ca5d71dc5e73c7 --- /dev/null +++ b/src/cmd/go/testdata/script/work_sum_mismatch.txt @@ -0,0 +1,61 @@ +# Test mismatched sums in go.sum files + +! go run ./a +cmpenv stderr want-error + +-- want-error -- +verifying rsc.io/sampler@v1.3.0/go.mod: checksum mismatch + downloaded: h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= + $WORK${/}gopath${/}src${/}a${/}go.sum: h1:U1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= + +SECURITY ERROR +This download does NOT match an earlier download recorded in go.sum. +The bits may have been replaced on the origin server, or an attacker may +have intercepted the download attempt. + +For more information, see 'go help module-auth'. +-- go.work -- +go 1.18 + +use ./a +use ./b +-- a/go.mod -- +go 1.18 + +module example.com/hi + +require "rsc.io/quote" v1.5.2 +-- a/go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:U1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- a/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} +-- b/go.mod -- +go 1.18 + +module example.com/hi2 + +require "rsc.io/quote" v1.5.2 +-- b/go.sum -- +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- b/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_sync.txt b/src/cmd/go/testdata/script/work_sync.txt new file mode 100644 index 00000000000000..69167d4cc14d04 --- /dev/null +++ b/src/cmd/go/testdata/script/work_sync.txt @@ -0,0 +1,119 @@ +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.0.0 + example.com/q v1.1.0 + example.com/r v1.0.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q + example.com/r => ../r +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 + example.com/q v1.1.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q + example.com/r => ../r +) +-- a/a.go -- +package a + +import ( + "example.com/p" + "example.com/q" +) + +func Foo() { + p.P() + q.Q() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/p v1.1.0 + example.com/q v1.0.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/p v1.1.0 + example.com/q v1.1.0 +) + +replace ( + example.com/p => ../p + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/p" + "example.com/q" +) + +func Foo() { + p.P() + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q +-- q/q.go -- +package q + +func Q() {} +-- r/go.mod -- +go 1.18 + +module example.com/r +-- r/q.go -- +package r + +func R() {} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt b/src/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt new file mode 100644 index 00000000000000..072323d15da8e5 --- /dev/null +++ b/src/cmd/go/testdata/script/work_sync_irrelevant_dependency.txt @@ -0,0 +1,119 @@ +# Test of go work sync in a workspace in which some dependency needed by `a` +# appears at a lower version in the build list of `b`, but is not needed at all +# by `b` (so it should not be upgraded within b). +# +# a -> p 1.1 +# b -> q 1.0 -(through a test dependency)-> p 1.0 +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 +) + +replace ( + example.com/p => ../p +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.1.0 +) + +replace ( + example.com/p => ../p +) +-- a/a.go -- +package a + +import ( + "example.com/p" +) + +func Foo() { + p.P() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.0.0 +) + +replace ( + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.0.0 +) + +replace ( + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/q" +) + +func Foo() { + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q + +require ( + example.com/p v1.0.0 +) + +replace ( + example.com/p => ../p +) +-- q/q.go -- +package q + +func Q() { +} +-- q/q_test.go -- +package q + +import example.com/p + +func TestQ(t *testing.T) { + p.P() +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_sync_missing_module.txt b/src/cmd/go/testdata/script/work_sync_missing_module.txt new file mode 100644 index 00000000000000..0018c733ee7e1d --- /dev/null +++ b/src/cmd/go/testdata/script/work_sync_missing_module.txt @@ -0,0 +1,12 @@ +# Ensure go work sync works without any modules in go.work. +go work sync + +# Ensure go work sync works even without a go.mod file. +rm go.mod +go work sync + +-- go.work -- +go 1.18 +-- go.mod -- +go 1.18 +module foo diff --git a/src/cmd/go/testdata/script/work_sync_relevant_dependency.txt b/src/cmd/go/testdata/script/work_sync_relevant_dependency.txt new file mode 100644 index 00000000000000..d7997027d9d74f --- /dev/null +++ b/src/cmd/go/testdata/script/work_sync_relevant_dependency.txt @@ -0,0 +1,106 @@ +# Test of go work sync in a workspace in which some dependency in the build +# list of 'b' (but not otherwise needed by `b`, so not seen when lazy loading +# occurs) actually is relevant to `a`. +# +# a -> p 1.0 +# b -> q 1.1 -> p 1.1 +go work sync +cmp a/go.mod a/want_go.mod +cmp b/go.mod b/want_go.mod + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) + +-- a/go.mod -- +go 1.18 + +module example.com/a + +require ( + example.com/p v1.0.0 +) + +replace ( + example.com/p => ../p +) +-- a/want_go.mod -- +go 1.18 + +module example.com/a + +require example.com/p v1.1.0 + +replace example.com/p => ../p +-- a/a.go -- +package a + +import ( + "example.com/p" +) + +func Foo() { + p.P() +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.1.0 +) + +replace ( + example.com/q => ../q +) +-- b/want_go.mod -- +go 1.18 + +module example.com/b + +require ( + example.com/q v1.1.0 +) + +replace ( + example.com/q => ../q +) +-- b/b.go -- +package b + +import ( + "example.com/q" +) + +func Foo() { + q.Q() +} +-- p/go.mod -- +go 1.18 + +module example.com/p +-- p/p.go -- +package p + +func P() {} +-- q/go.mod -- +go 1.18 + +module example.com/q + +require example.com/p v1.1.0 + +replace example.com/p => ../p +-- q/q.go -- +package q + +import example.com/p + +func Q() { + p.P() +} diff --git a/src/cmd/go/testdata/script/work_sync_sum.txt b/src/cmd/go/testdata/script/work_sync_sum.txt new file mode 100644 index 00000000000000..656fd31379ea48 --- /dev/null +++ b/src/cmd/go/testdata/script/work_sync_sum.txt @@ -0,0 +1,40 @@ +# Test that the sum file data state is properly reset between modules in +# go work sync so that the sum file that's written is correct. +# Exercises the fix to #50038. + +cp b/go.sum b/go.sum.want + +# As a sanity check, verify b/go.sum is tidy. +cd b +go mod tidy +cd .. +cmp b/go.sum b/go.sum.want + +# Run go work sync and verify it doesn't change b/go.sum. +go work sync +cmp b/go.sum b/go.sum.want + +-- b/go.sum -- +rsc.io/quote v1.0.0 h1:kQ3IZQzPTiDJxSZI98YaWgxFEhlNdYASHvh+MplbViw= +rsc.io/quote v1.0.0/go.mod h1:v83Ri/njykPcgJltBc/gEkJTmjTsNgtO1Y7vyIK1CQA= +-- go.work -- +go 1.18 +use ( + ./a + ./b +) +replace example.com/c => ./c +-- a/go.mod -- +module example.com/a +go 1.18 +require rsc.io/fortune v1.0.0 +-- a/a.go -- +package a +import "rsc.io/fortune" +-- b/go.mod -- +module example.com/b +go 1.18 +require rsc.io/quote v1.0.0 +-- b/b.go -- +package b +import _ "rsc.io/quote" diff --git a/src/cmd/go/testdata/script/work_use.txt b/src/cmd/go/testdata/script/work_use.txt new file mode 100644 index 00000000000000..12c8cecab74c7e --- /dev/null +++ b/src/cmd/go/testdata/script/work_use.txt @@ -0,0 +1,32 @@ +go work use -r foo +cmp go.work go.want_work_r + +go work use other +cmp go.work go.want_work_other +-- go.work -- +go 1.18 + +use ( + foo + foo/bar // doesn't exist +) +-- go.want_work_r -- +go 1.18 + +use ( + ./foo + ./foo/bar/baz +) +-- go.want_work_other -- +go 1.18 + +use ( + ./foo + ./foo/bar/baz + ./other +) +-- foo/go.mod -- +module foo +-- foo/bar/baz/go.mod -- +module baz +-- other/go.mod -- diff --git a/src/cmd/go/testdata/script/work_use_deleted.txt b/src/cmd/go/testdata/script/work_use_deleted.txt new file mode 100644 index 00000000000000..b379cbc09d9826 --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_deleted.txt @@ -0,0 +1,22 @@ +go work use -r . +cmp go.work go.work.want + +-- go.work -- +go 1.18 + +use ( + . + ./sub + ./sub/dir/deleted +) +-- go.work.want -- +go 1.18 + +use ./sub/dir +-- sub/README.txt -- +A go.mod file has been deleted from this directory. +In addition, the entire subdirectory sub/dir/deleted +has been deleted, along with sub/dir/deleted/go.mod. +-- sub/dir/go.mod -- +module example/sub/dir +go 1.18 diff --git a/src/cmd/go/testdata/script/work_use_dot.txt b/src/cmd/go/testdata/script/work_use_dot.txt new file mode 100644 index 00000000000000..8f210423ec2ad1 --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_dot.txt @@ -0,0 +1,49 @@ +cp go.work go.work.orig + +# If the current directory contains a go.mod file, +# 'go work use .' should add an entry for it. +cd bar/baz +go work use . +cmp ../../go.work ../../go.work.rel + +# If the current directory lacks a go.mod file, 'go work use .' +# should remove its entry. +mv go.mod go.mod.bak +go work use . +cmp ../../go.work ../../go.work.orig + +# If the path is absolute, it should remain absolute. +mv go.mod.bak go.mod +go work use $PWD +grep -count=1 '^use ' ../../go.work +grep '^use ["]?'$PWD'["]?$' ../../go.work + +# An absolute path should replace an entry for the corresponding relative path +# and vice-versa. +go work use . +cmp ../../go.work ../../go.work.rel +go work use $PWD +grep -count=1 '^use ' ../../go.work +grep '^use ["]?'$PWD'["]?$' ../../go.work + +# If both the absolute and relative paths are named, 'go work use' should error +# out: we don't know which one to use, and shouldn't add both because the +# resulting workspace would contain a duplicate module. +cp ../../go.work.orig ../../go.work +! go work use $PWD . +stderr '^go: already added "\./bar/baz" as "'$PWD'"$' +cmp ../../go.work ../../go.work.orig + + +-- go.mod -- +module example +go 1.18 +-- go.work -- +go 1.18 +-- go.work.rel -- +go 1.18 + +use ./bar/baz +-- bar/baz/go.mod -- +module example/bar/baz +go 1.18 diff --git a/src/cmd/go/testdata/script/work_use_issue50958.txt b/src/cmd/go/testdata/script/work_use_issue50958.txt new file mode 100644 index 00000000000000..7a25531f3d1cd2 --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_issue50958.txt @@ -0,0 +1,17 @@ +go work use -r . +cmp go.work go.work.want + +-- go.mod -- +module example +go 1.18 +-- go.work -- +go 1.18 + +use sub +-- go.work.want -- +go 1.18 + +use . +-- sub/README.txt -- +This directory no longer contains a go.mod file. + diff --git a/src/cmd/go/testdata/script/work_use_noargs.txt b/src/cmd/go/testdata/script/work_use_noargs.txt new file mode 100644 index 00000000000000..ca054344c638b8 --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_noargs.txt @@ -0,0 +1,11 @@ +# For now, 'go work use' requires arguments. +# (Eventually, we may may it implicitly behave like 'go work use .'. + +! go work use +stderr '^go: ''go work use'' requires one or more directory arguments' + +! go work use -r +stderr '^go: ''go work use'' requires one or more directory arguments' + +-- go.work -- +go 1.18 diff --git a/src/cmd/go/testdata/script/work_use_only_dirs.txt b/src/cmd/go/testdata/script/work_use_only_dirs.txt new file mode 100644 index 00000000000000..aa6dd78a6ae4cd --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_only_dirs.txt @@ -0,0 +1,17 @@ +! go work use foo bar baz + +stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]foo is not a directory' +stderr '^go: directory '$WORK'[/\\]gopath[/\\]src[/\\]baz does not exist' +cmp go.work go.work_want + +! go work use -r qux +stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]qux is not a directory' + +-- go.work -- +go 1.18 +-- go.work_want -- +go 1.18 +-- foo -- +-- qux -- +-- bar/go.mod -- +module bar \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_vet.txt b/src/cmd/go/testdata/script/work_vet.txt new file mode 100644 index 00000000000000..e258fc03946a82 --- /dev/null +++ b/src/cmd/go/testdata/script/work_vet.txt @@ -0,0 +1,19 @@ +! go vet ./a +stderr 'fmt.Println call has possible formatting directive' + +-- go.work -- +go 1.18 + +use ./a +-- a/go.mod -- +module example.com/a + +go 1.18 +-- a/a.go -- +package a + +import "fmt" + +func A() { + fmt.Println("%s") +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/work_why_download_graph.txt b/src/cmd/go/testdata/script/work_why_download_graph.txt new file mode 100644 index 00000000000000..8f1aeddf47106d --- /dev/null +++ b/src/cmd/go/testdata/script/work_why_download_graph.txt @@ -0,0 +1,65 @@ +# Test go mod download, why, and graph work in workspace mode. +# TODO(bcmills): clarify the interaction with #44435 + +go mod download rsc.io/quote +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.info +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.mod +grep '^rsc\.io/quote v1\.5\.2/go\.mod h1:' go.work.sum +grep '^rsc\.io/quote v1\.5\.2 h1:' go.work.sum + +go clean -modcache +rm go.work.sum +go mod download +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.info +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod +exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.info +! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.0.mod +grep '^rsc\.io/quote v1\.5\.2/go\.mod h1:' go.work.sum +grep '^rsc\.io/quote v1\.5\.2 h1:' go.work.sum + +go mod why rsc.io/quote +stdout '# rsc.io/quote\nexample.com/a\nrsc.io/quote' + +go mod graph +stdout 'example.com/a rsc.io/quote@v1.5.2\nexample.com/b example.com/c@v1.0.0\nrsc.io/quote@v1.5.2 rsc.io/sampler@v1.3.0\nrsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c' + +-- go.work -- +go 1.18 + +use ( + ./a + ./b +) +-- a/go.mod -- +go 1.18 + +module example.com/a + +require "rsc.io/quote" v1.5.2 +-- a/main.go -- +package main + +import ( + "fmt" + "rsc.io/quote" +) + +func main() { + fmt.Println(quote.Hello()) +} +-- b/go.mod -- +go 1.18 + +module example.com/b + +require example.com/c v1.0.0 +replace example.com/c => ../c +-- c/go.mod -- +go 1.18 + +module example.com/c + diff --git a/src/cmd/go/testdata/testterminal18153/terminal_test.go b/src/cmd/go/testdata/testterminal18153/terminal_test.go index 71493efe983741..34ee580c0e9970 100644 --- a/src/cmd/go/testdata/testterminal18153/terminal_test.go +++ b/src/cmd/go/testdata/testterminal18153/terminal_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build linux // +build linux // This test is run by src/cmd/dist/test.go (cmd_go_test_terminal), diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index e34066559415e7..8ac9c6a931711d 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -13,9 +13,11 @@ that directory, recursively. (Files starting with a period are ignored.) By default, gofmt prints the reformatted sources to standard output. Usage: + gofmt [flags] [path ...] The flags are: + -d Do not print reformatted sources to standard output. If a file's formatting is different than gofmt's, print diffs @@ -37,10 +39,10 @@ The flags are: the original file is restored from an automatic backup. Debugging support: + -cpuprofile filename Write cpu profile to the specified file. - The rewrite rule specified with the -r flag must be a string of the form: pattern -> replacement @@ -57,7 +59,7 @@ such a fragment, gofmt preserves leading indentation as well as leading and trailing spaces, so that individual sections of a Go program can be formatted by piping them through gofmt. -Examples +# Examples To check files for unnecessary parentheses: @@ -71,7 +73,7 @@ To convert the package tree from explicit slice upper bounds to implicit ones: gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src -The simplify command +# The simplify command When invoked with -s gofmt will make the following source transformations where possible. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index b3c120daab42c4..e464d64c98bac3 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "context" "flag" "fmt" "go/ast" @@ -13,6 +14,7 @@ import ( "go/printer" "go/scanner" "go/token" + "internal/diff" "io" "io/fs" "os" @@ -21,7 +23,7 @@ import ( "runtime/pprof" "strings" - "cmd/internal/diff" + "golang.org/x/sync/semaphore" ) var ( @@ -49,18 +51,21 @@ const ( printerNormalizeNumbers = 1 << 30 ) +// fdSem guards the number of concurrently-open file descriptors. +// +// For now, this is arbitrarily set to 200, based on the observation that many +// platforms default to a kernel limit of 256. Ideally, perhaps we should derive +// it from rlimit on platforms that support that system call. +// +// File descriptors opened from outside of this package are not tracked, +// so this limit may be approximate. +var fdSem = make(chan bool, 200) + var ( - fileSet = token.NewFileSet() // per process FileSet - exitCode = 0 - rewrite func(*ast.File) *ast.File + rewrite func(*token.FileSet, *ast.File) *ast.File parserMode parser.Mode ) -func report(err error) { - scanner.PrintError(os.Stderr, err) - exitCode = 2 -} - func usage() { fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n") flag.PrintDefaults() @@ -71,46 +76,179 @@ func initParserMode() { if *allErrors { parserMode |= parser.AllErrors } + // It's only -r that makes use of go/ast's object resolution, + // so avoid the unnecessary work if the flag isn't used. + if *rewriteRule == "" { + parserMode |= parser.SkipObjectResolution + } } func isGoFile(f fs.DirEntry) bool { // ignore non-Go files name := f.Name() - return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") + return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && !f.IsDir() } -// If in == nil, the source is the contents of the file with the given filename. -func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error { - var perm fs.FileMode = 0644 - if in == nil { - f, err := os.Open(filename) - if err != nil { - return err - } - defer f.Close() - fi, err := f.Stat() - if err != nil { - return err +// A sequencer performs concurrent tasks that may write output, but emits that +// output in a deterministic order. +type sequencer struct { + maxWeight int64 + sem *semaphore.Weighted // weighted by input bytes (an approximate proxy for memory overhead) + prev <-chan *reporterState // 1-buffered +} + +// newSequencer returns a sequencer that allows concurrent tasks up to maxWeight +// and writes tasks' output to out and err. +func newSequencer(maxWeight int64, out, err io.Writer) *sequencer { + sem := semaphore.NewWeighted(maxWeight) + prev := make(chan *reporterState, 1) + prev <- &reporterState{out: out, err: err} + return &sequencer{ + maxWeight: maxWeight, + sem: sem, + prev: prev, + } +} + +// exclusive is a weight that can be passed to a sequencer to cause +// a task to be executed without any other concurrent tasks. +const exclusive = -1 + +// Add blocks until the sequencer has enough weight to spare, then adds f as a +// task to be executed concurrently. +// +// If the weight is either negative or larger than the sequencer's maximum +// weight, Add blocks until all other tasks have completed, then the task +// executes exclusively (blocking all other calls to Add until it completes). +// +// f may run concurrently in a goroutine, but its output to the passed-in +// reporter will be sequential relative to the other tasks in the sequencer. +// +// If f invokes a method on the reporter, execution of that method may block +// until the previous task has finished. (To maximize concurrency, f should +// avoid invoking the reporter until it has finished any parallelizable work.) +// +// If f returns a non-nil error, that error will be reported after f's output +// (if any) and will cause a nonzero final exit code. +func (s *sequencer) Add(weight int64, f func(*reporter) error) { + if weight < 0 || weight > s.maxWeight { + weight = s.maxWeight + } + if err := s.sem.Acquire(context.TODO(), weight); err != nil { + // Change the task from "execute f" to "report err". + weight = 0 + f = func(*reporter) error { return err } + } + + r := &reporter{prev: s.prev} + next := make(chan *reporterState, 1) + s.prev = next + + // Start f in parallel: it can run until it invokes a method on r, at which + // point it will block until the previous task releases the output state. + go func() { + if err := f(r); err != nil { + r.Report(err) } - in = f - perm = fi.Mode().Perm() + next <- r.getState() // Release the next task. + s.sem.Release(weight) + }() +} + +// AddReport prints an error to s after the output of any previously-added +// tasks, causing the final exit code to be nonzero. +func (s *sequencer) AddReport(err error) { + s.Add(0, func(*reporter) error { return err }) +} + +// GetExitCode waits for all previously-added tasks to complete, then returns an +// exit code for the sequence suitable for passing to os.Exit. +func (s *sequencer) GetExitCode() int { + c := make(chan int, 1) + s.Add(0, func(r *reporter) error { + c <- r.ExitCode() + return nil + }) + return <-c +} + +// A reporter reports output, warnings, and errors. +type reporter struct { + prev <-chan *reporterState + state *reporterState +} + +// reporterState carries the state of a reporter instance. +// +// Only one reporter at a time may have access to a reporterState. +type reporterState struct { + out, err io.Writer + exitCode int +} + +// getState blocks until any prior reporters are finished with the reporter +// state, then returns the state for manipulation. +func (r *reporter) getState() *reporterState { + if r.state == nil { + r.state = <-r.prev } + return r.state +} - src, err := io.ReadAll(in) +// Warnf emits a warning message to the reporter's error stream, +// without changing its exit code. +func (r *reporter) Warnf(format string, args ...any) { + fmt.Fprintf(r.getState().err, format, args...) +} + +// Write emits a slice to the reporter's output stream. +// +// Any error is returned to the caller, and does not otherwise affect the +// reporter's exit code. +func (r *reporter) Write(p []byte) (int, error) { + return r.getState().out.Write(p) +} + +// Report emits a non-nil error to the reporter's error stream, +// changing its exit code to a nonzero value. +func (r *reporter) Report(err error) { + if err == nil { + panic("Report with nil error") + } + st := r.getState() + scanner.PrintError(st.err, err) + st.exitCode = 2 +} + +func (r *reporter) ExitCode() int { + return r.getState().exitCode +} + +// If info == nil, we are formatting stdin instead of a file. +// If in == nil, the source is the contents of the file with the given filename. +func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) error { + src, err := readFile(filename, info, in) if err != nil { return err } - file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin) + fileSet := token.NewFileSet() + fragmentOk := false + if info == nil { + // If we are formatting stdin, we accept a program fragment in lieu of a + // complete source file. + fragmentOk = true + } + file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, fragmentOk) if err != nil { return err } if rewrite != nil { if sourceAdj == nil { - file = rewrite(file) + file = rewrite(fileSet, file) } else { - fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n") + r.Warnf("warning: rewrite ignored for incomplete programs\n") } } @@ -128,15 +266,21 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error if !bytes.Equal(src, res) { // formatting has changed if *list { - fmt.Fprintln(out, filename) + fmt.Fprintln(r, filename) } if *write { + if info == nil { + panic("-w should not have been allowed with stdin") + } // make a temporary backup before overwriting original + perm := info.Mode().Perm() bakname, err := backupFile(filename+".", src, perm) if err != nil { return err } + fdSem <- true err = os.WriteFile(filename, res, perm) + <-fdSem if err != nil { os.Rename(bakname, filename) return err @@ -147,52 +291,114 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error } } if *doDiff { - data, err := diffWithReplaceTempFile(src, res, filename) - if err != nil { - return fmt.Errorf("computing diff: %s", err) - } - fmt.Fprintf(out, "diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename)) - out.Write(data) + newName := filepath.ToSlash(filename) + oldName := newName + ".orig" + r.Write(diff.Diff(oldName, src, newName, res)) } } if !*list && !*write && !*doDiff { - _, err = out.Write(res) + _, err = r.Write(res) } return err } -func visitFile(path string, f fs.DirEntry, err error) error { - if err != nil || !isGoFile(f) { - return err +// readFile reads the contents of filename, described by info. +// If in is non-nil, readFile reads directly from it. +// Otherwise, readFile opens and reads the file itself, +// with the number of concurrently-open files limited by fdSem. +func readFile(filename string, info fs.FileInfo, in io.Reader) ([]byte, error) { + if in == nil { + fdSem <- true + var err error + f, err := os.Open(filename) + if err != nil { + return nil, err + } + in = f + defer func() { + f.Close() + <-fdSem + }() } - if err := processFile(path, nil, os.Stdout, false); err != nil { - report(err) + + // Compute the file's size and read its contents with minimal allocations. + // + // If we have the FileInfo from filepath.WalkDir, use it to make + // a buffer of the right size and avoid ReadAll's reallocations. + // + // If the size is unknown (or bogus, or overflows an int), fall back to + // a size-independent ReadAll. + size := -1 + if info != nil && info.Mode().IsRegular() && int64(int(info.Size())) == info.Size() { + size = int(info.Size()) + } + if size+1 <= 0 { + // The file is not known to be regular, so we don't have a reliable size for it. + var err error + src, err := io.ReadAll(in) + if err != nil { + return nil, err + } + return src, nil + } + + // We try to read size+1 bytes so that we can detect modifications: if we + // read more than size bytes, then the file was modified concurrently. + // (If that happens, we could, say, append to src to finish the read, or + // proceed with a truncated buffer — but the fact that it changed at all + // indicates a possible race with someone editing the file, so we prefer to + // stop to avoid corrupting it.) + src := make([]byte, size+1) + n, err := io.ReadFull(in, src) + switch err { + case nil, io.EOF, io.ErrUnexpectedEOF: + // io.ReadFull returns io.EOF (for an empty file) or io.ErrUnexpectedEOF + // (for a non-empty file) if the file was changed unexpectedly. Continue + // with comparing file sizes in those cases. + default: + return nil, err + } + if n < size { + return nil, fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n) + } else if n > size { + return nil, fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src)) } - return nil + return src[:n], nil } func main() { + // Arbitrarily limit in-flight work to 2MiB times the number of threads. + // + // The actual overhead for the parse tree and output will depend on the + // specifics of the file, but this at least keeps the footprint of the process + // roughly proportional to GOMAXPROCS. + maxWeight := (2 << 20) * int64(runtime.GOMAXPROCS(0)) + s := newSequencer(maxWeight, os.Stdout, os.Stderr) + // call gofmtMain in a separate function // so that it can use defer and have them // run before the exit. - gofmtMain() - os.Exit(exitCode) + gofmtMain(s) + os.Exit(s.GetExitCode()) } -func gofmtMain() { +func gofmtMain(s *sequencer) { flag.Usage = usage flag.Parse() if *cpuprofile != "" { + fdSem <- true f, err := os.Create(*cpuprofile) if err != nil { - fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err) - exitCode = 2 + s.AddReport(fmt.Errorf("creating cpu profile: %s", err)) return } - defer f.Close() + defer func() { + f.Close() + <-fdSem + }() pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } @@ -203,69 +409,65 @@ func gofmtMain() { args := flag.Args() if len(args) == 0 { if *write { - fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input") - exitCode = 2 + s.AddReport(fmt.Errorf("error: cannot use -w with standard input")) return } - if err := processFile("", os.Stdin, os.Stdout, true); err != nil { - report(err) - } + s.Add(0, func(r *reporter) error { + return processFile("", nil, os.Stdin, r) + }) return } for _, arg := range args { switch info, err := os.Stat(arg); { case err != nil: - report(err) + s.AddReport(err) case !info.IsDir(): // Non-directory arguments are always formatted. - if err := processFile(arg, nil, os.Stdout, false); err != nil { - report(err) - } + arg := arg + s.Add(fileWeight(arg, info), func(r *reporter) error { + return processFile(arg, info, nil, r) + }) default: // Directories are walked, ignoring non-Go files. - if err := filepath.WalkDir(arg, visitFile); err != nil { - report(err) + err := filepath.WalkDir(arg, func(path string, f fs.DirEntry, err error) error { + if err != nil || !isGoFile(f) { + return err + } + info, err := f.Info() + if err != nil { + s.AddReport(err) + return nil + } + s.Add(fileWeight(path, info), func(r *reporter) error { + return processFile(path, info, nil, r) + }) + return nil + }) + if err != nil { + s.AddReport(err) } } } } -func diffWithReplaceTempFile(b1, b2 []byte, filename string) ([]byte, error) { - data, err := diff.Diff("gofmt", b1, b2) - if len(data) > 0 { - return replaceTempFilename(data, filename) - } - return data, err -} - -// replaceTempFilename replaces temporary filenames in diff with actual one. -// -// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500 -// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500 -// ... -// -> -// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500 -// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500 -// ... -func replaceTempFilename(diff []byte, filename string) ([]byte, error) { - bs := bytes.SplitN(diff, []byte{'\n'}, 3) - if len(bs) < 3 { - return nil, fmt.Errorf("got unexpected diff for %s", filename) +func fileWeight(path string, info fs.FileInfo) int64 { + if info == nil { + return exclusive } - // Preserve timestamps. - var t0, t1 []byte - if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 { - t0 = bs[0][i:] + if info.Mode().Type() == fs.ModeSymlink { + var err error + info, err = os.Stat(path) + if err != nil { + return exclusive + } } - if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 { - t1 = bs[1][i:] + if !info.Mode().IsRegular() { + // For non-regular files, FileInfo.Size is system-dependent and thus not a + // reliable indicator of weight. + return exclusive } - // Always print filepath with slash separator. - f := filepath.ToSlash(filename) - bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0)) - bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1)) - return bytes.Join(bs, []byte{'\n'}), nil + return info.Size() } const chmodSupported = runtime.GOOS != "windows" @@ -274,6 +476,9 @@ const chmodSupported = runtime.GOOS != "windows" // with 0 { + t.Logf("%q", errBuf.Bytes()) + } + if s.GetExitCode() != 0 { + t.Fail() } expected, err := os.ReadFile(out) @@ -116,11 +118,8 @@ func runTest(t *testing.T, in, out string) { t.Errorf("WARNING: -update did not rewrite input file %s", in) } - t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) - d, err := diffWithReplaceTempFile(expected, got, in) - if err == nil { - t.Errorf("%s", d) - } + t.Errorf("(gofmt %s) != %s (see %s.gofmt)\n%s", in, out, in, + diff.Diff("expected", expected, "got", got)) if err := os.WriteFile(in+".gofmt", got, 0666); err != nil { t.Error(err) } @@ -145,15 +144,18 @@ func TestRewrite(t *testing.T) { match = append(match, "gofmt.go", "gofmt_test.go") for _, in := range match { - out := in // for files where input and output are identical - if strings.HasSuffix(in, ".input") { - out = in[:len(in)-len(".input")] + ".golden" - } - runTest(t, in, out) - if in != out { - // Check idempotence. - runTest(t, out, out) - } + name := filepath.Base(in) + t.Run(name, func(t *testing.T) { + out := in // for files where input and output are identical + if strings.HasSuffix(in, ".input") { + out = in[:len(in)-len(".input")] + ".golden" + } + runTest(t, in, out) + if in != out && !t.Failed() { + // Check idempotence. + runTest(t, out, out) + } + }) } } @@ -191,69 +193,3 @@ func TestBackupFile(t *testing.T) { } t.Logf("Created: %s", name) } - -func TestDiff(t *testing.T) { - if _, err := exec.LookPath("diff"); err != nil { - t.Skipf("skip test on %s: diff command is required", runtime.GOOS) - } - in := []byte("first\nsecond\n") - out := []byte("first\nthird\n") - filename := "difftest.txt" - b, err := diffWithReplaceTempFile(in, out, filename) - if err != nil { - t.Fatal(err) - } - - if runtime.GOOS == "windows" { - b = bytes.ReplaceAll(b, []byte{'\r', '\n'}, []byte{'\n'}) - } - - bs := bytes.SplitN(b, []byte{'\n'}, 3) - line0, line1 := bs[0], bs[1] - - if prefix := "--- difftest.txt.orig"; !bytes.HasPrefix(line0, []byte(prefix)) { - t.Errorf("diff: first line should start with `%s`\ngot: %s", prefix, line0) - } - - if prefix := "+++ difftest.txt"; !bytes.HasPrefix(line1, []byte(prefix)) { - t.Errorf("diff: second line should start with `%s`\ngot: %s", prefix, line1) - } - - want := `@@ -1,2 +1,2 @@ - first --second -+third -` - - if got := string(bs[2]); got != want { - t.Errorf("diff: got:\n%s\nwant:\n%s", got, want) - } -} - -func TestReplaceTempFilename(t *testing.T) { - diff := []byte(`--- /tmp/tmpfile1 2017-02-08 00:53:26.175105619 +0900 -+++ /tmp/tmpfile2 2017-02-08 00:53:38.415151275 +0900 -@@ -1,2 +1,2 @@ - first --second -+third -`) - want := []byte(`--- path/to/file.go.orig 2017-02-08 00:53:26.175105619 +0900 -+++ path/to/file.go 2017-02-08 00:53:38.415151275 +0900 -@@ -1,2 +1,2 @@ - first --second -+third -`) - // Check path in diff output is always slash regardless of the - // os.PathSeparator (`/` or `\`). - sep := string(os.PathSeparator) - filename := strings.Join([]string{"path", "to", "file.go"}, sep) - got, err := replaceTempFilename(diff, filename) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(got, want) { - t.Errorf("os.PathSeparator='%s': replacedDiff:\ngot:\n%s\nwant:\n%s", sep, got, want) - } -} diff --git a/src/cmd/gofmt/gofmt_typeparams_test.go b/src/cmd/gofmt/gofmt_typeparams_test.go deleted file mode 100644 index 10641a77cb2f87..00000000000000 --- a/src/cmd/gofmt/gofmt_typeparams_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build typeparams -// +build typeparams - -package main - -func init() { - typeParamsEnabled = true -} diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index 4a821705f16d06..2ee5174b96f7c1 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -15,6 +15,7 @@ import ( "go/ast" "go/printer" "go/token" + "internal/testenv" "io" "io/fs" "os" @@ -113,7 +114,8 @@ func genFilenames(t *testing.T, filenames chan<- string) { t.Error(err) return nil } - if isGoFile(d) { + // don't descend into testdata directories + if isGoFile(d) && !strings.Contains(filepath.ToSlash(filename), "/testdata/") { filenames <- filename nfiles++ } @@ -130,7 +132,11 @@ func genFilenames(t *testing.T, filenames chan<- string) { } // otherwise, test all Go files under *root - filepath.WalkDir(*root, handleFile) + goroot := *root + if goroot == "" { + goroot = testenv.GOROOT(t) + } + filepath.WalkDir(goroot, handleFile) } func TestAll(t *testing.T) { diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index bab22e04cdac00..a98c6a0cd9b483 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -28,7 +28,9 @@ func initRewrite() { } pattern := parseExpr(f[0], "pattern") replace := parseExpr(f[1], "replacement") - rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) } + rewrite = func(fset *token.FileSet, p *ast.File) *ast.File { + return rewriteFile(fset, pattern, replace, p) + } } // parseExpr parses s as an expression. @@ -54,7 +56,7 @@ func dump(msg string, val reflect.Value) { */ // rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. -func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { +func rewriteFile(fileSet *token.FileSet, pattern, replace ast.Expr, p *ast.File) *ast.File { cmap := ast.NewCommentMap(fileSet, p, p.Comments) m := make(map[string]reflect.Value) pat := reflect.ValueOf(pattern) @@ -290,7 +292,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) } return v - case reflect.Ptr: + case reflect.Pointer: v := reflect.New(p.Type()).Elem() if elem := p.Elem(); elem.IsValid() { v.Set(subst(m, elem, pos).Addr()) diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go index 1a0e8174afa384..eb55daabc1db86 100644 --- a/src/cmd/gofmt/simplify.go +++ b/src/cmd/gofmt/simplify.go @@ -53,22 +53,26 @@ func (s simplifier) Visit(node ast.Node) ast.Visitor { // can be simplified to: s[a:] // if s is "simple enough" (for now we only accept identifiers) // - // Note: This may not be correct because len may have been redeclared in another - // file belonging to the same package. However, this is extremely unlikely - // and so far (April 2016, after years of supporting this rewrite feature) + // Note: This may not be correct because len may have been redeclared in + // the same package. However, this is extremely unlikely and so far + // (April 2022, after years of supporting this rewrite feature) // has never come up, so let's keep it working as is (see also #15153). + // + // Also note that this code used to use go/ast's object tracking, + // which was removed in exchange for go/parser.Mode.SkipObjectResolution. + // False positives are extremely unlikely as described above, + // and go/ast's object tracking is incomplete in any case. if n.Max != nil { // - 3-index slices always require the 2nd and 3rd index break } - if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { - // the array/slice object is a single, resolved identifier + if s, _ := n.X.(*ast.Ident); s != nil { + // the array/slice object is a single identifier if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() { // the high expression is a function call with a single argument - if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil { - // the function called is "len" and it is not locally defined; and - // because we don't have dot imports, it must be the predefined len() - if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj { + if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" { + // the function called is "len" + if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Name == s.Name { // the len argument is the array/slice object n.High = nil } diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden index 193dbacc727dd7..65de9cf19946d5 100644 --- a/src/cmd/gofmt/testdata/crlf.golden +++ b/src/cmd/gofmt/testdata/crlf.golden @@ -1,8 +1,8 @@ /* - Source containing CR/LF line endings. - The gofmt'ed output must only have LF - line endings. - Test case for issue 3961. +Source containing CR/LF line endings. +The gofmt'ed output must only have LF +line endings. +Test case for issue 3961. */ package main diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input index ae7e14dbf1386c..3cd4934caf2ef8 100644 --- a/src/cmd/gofmt/testdata/crlf.input +++ b/src/cmd/gofmt/testdata/crlf.input @@ -1,8 +1,8 @@ /* - Source containing CR/LF line endings. - The gofmt'ed output must only have LF - line endings. - Test case for issue 3961. +Source containing CR/LF line endings. +The gofmt'ed output must only have LF +line endings. +Test case for issue 3961. */ package main diff --git a/src/cmd/gofmt/testdata/tabs.golden b/src/cmd/gofmt/testdata/tabs.golden new file mode 100644 index 00000000000000..287678cfc9d777 --- /dev/null +++ b/src/cmd/gofmt/testdata/tabs.golden @@ -0,0 +1,33 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//gofmt + +package main + +var _ = []struct { + S string + Integer int +}{ + { + S: "Hello World", + Integer: 42, + }, + { + S: "\t", + Integer: 42, + }, + { + S: " ", // an actual + Integer: 42, + }, + { + S: ` `, // an actual + Integer: 42, + }, + { + S: "\u0009", + Integer: 42, + }, +} diff --git a/src/cmd/gofmt/testdata/tabs.input b/src/cmd/gofmt/testdata/tabs.input new file mode 100644 index 00000000000000..635be797c9868b --- /dev/null +++ b/src/cmd/gofmt/testdata/tabs.input @@ -0,0 +1,33 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//gofmt + +package main + +var _ = []struct{ + S string + Integer int +}{ + { + S: "Hello World", + Integer: 42, + }, + { + S: "\t", + Integer: 42, + }, + { + S: " ", // an actual + Integer: 42, + }, + { + S: ` `, // an actual + Integer: 42, + }, + { + S: "\u0009", + Integer: 42, + }, +} diff --git a/src/cmd/gofmt/testdata/typeparams.golden b/src/cmd/gofmt/testdata/typeparams.golden index 35f08d13792c70..d57a2ba59b03e6 100644 --- a/src/cmd/gofmt/testdata/typeparams.golden +++ b/src/cmd/gofmt/testdata/typeparams.golden @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//gofmt -G +//gofmt package typeparams @@ -21,7 +21,7 @@ func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} func f[P interface{}](x P) func f[P1, P2, P3 interface { m1(P1) - type P2, P3 + ~P2 | ~P3 }](x1 P1, x2 P2, x3 P3) struct{} func f[P any](T1[P], T2[P]) T3[P] diff --git a/src/cmd/gofmt/testdata/typeparams.input b/src/cmd/gofmt/testdata/typeparams.input index 7f3212c8e4a8d8..775cf9eb7bdc85 100644 --- a/src/cmd/gofmt/testdata/typeparams.input +++ b/src/cmd/gofmt/testdata/typeparams.input @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//gofmt -G +//gofmt package typeparams @@ -19,7 +19,7 @@ func f[P any](x P) func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} func f[P interface{}](x P) -func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{} +func f[P1, P2, P3 interface{ m1(P1); ~P2|~P3 }](x1 P1, x2 P2, x3 P3) struct{} func f[P any](T1[P], T2[P]) T3[P] func (x T[P]) m() diff --git a/src/cmd/gofmt/testdata/typeswitch.golden b/src/cmd/gofmt/testdata/typeswitch.golden index 2b1905edd3b4fc..3cf4dca7d4d6b8 100644 --- a/src/cmd/gofmt/testdata/typeswitch.golden +++ b/src/cmd/gofmt/testdata/typeswitch.golden @@ -1,17 +1,17 @@ /* - Parenthesized type switch expressions originally - accepted by gofmt must continue to be rewritten - into the correct unparenthesized form. - - Only type-switches that didn't declare a variable - in the type switch type assertion and which - contained only "expression-like" (named) types in their - cases were permitted to have their type assertion parenthesized - by go/parser (due to a weak predicate in the parser). All others - were rejected always, either with a syntax error in the - type switch header or in the case. - - See also issue 4470. +Parenthesized type switch expressions originally +accepted by gofmt must continue to be rewritten +into the correct unparenthesized form. + +Only type-switches that didn't declare a variable +in the type switch type assertion and which +contained only "expression-like" (named) types in their +cases were permitted to have their type assertion parenthesized +by go/parser (due to a weak predicate in the parser). All others +were rejected always, either with a syntax error in the +type switch header or in the case. + +See also issue 4470. */ package p diff --git a/src/cmd/gofmt/testdata/typeswitch.input b/src/cmd/gofmt/testdata/typeswitch.input index 8f8cba9b855abd..992a772d5210be 100644 --- a/src/cmd/gofmt/testdata/typeswitch.input +++ b/src/cmd/gofmt/testdata/typeswitch.input @@ -1,17 +1,17 @@ /* - Parenthesized type switch expressions originally - accepted by gofmt must continue to be rewritten - into the correct unparenthesized form. - - Only type-switches that didn't declare a variable - in the type switch type assertion and which - contained only "expression-like" (named) types in their - cases were permitted to have their type assertion parenthesized - by go/parser (due to a weak predicate in the parser). All others - were rejected always, either with a syntax error in the - type switch header or in the case. - - See also issue 4470. +Parenthesized type switch expressions originally +accepted by gofmt must continue to be rewritten +into the correct unparenthesized form. + +Only type-switches that didn't declare a variable +in the type switch type assertion and which +contained only "expression-like" (named) types in their +cases were permitted to have their type assertion parenthesized +by go/parser (due to a weak predicate in the parser). All others +were rejected always, either with a syntax error in the +type switch header or in the case. + +See also issue 4470. */ package p diff --git a/src/cmd/internal/archive/archive.go b/src/cmd/internal/archive/archive.go index da1f293243573e..d2c4f69ef5d6a0 100644 --- a/src/cmd/internal/archive/archive.go +++ b/src/cmd/internal/archive/archive.go @@ -124,9 +124,9 @@ type objReader struct { func (r *objReader) init(f *os.File) { r.a = &Archive{f, nil} - r.offset, _ = f.Seek(0, os.SEEK_CUR) - r.limit, _ = f.Seek(0, os.SEEK_END) - f.Seek(r.offset, os.SEEK_SET) + r.offset, _ = f.Seek(0, io.SeekCurrent) + r.limit, _ = f.Seek(0, io.SeekEnd) + f.Seek(r.offset, io.SeekStart) r.b = bio.NewReader(f) } @@ -227,7 +227,7 @@ func (r *objReader) skip(n int64) { r.readFull(r.tmp[:n]) } else { // Seek, giving up buffered data. - r.b.MustSeek(r.offset+n, os.SEEK_SET) + r.b.MustSeek(r.offset+n, io.SeekStart) r.offset += n } } @@ -435,7 +435,7 @@ func (r *objReader) parseObject(o *GoObj, size int64) error { // AddEntry adds an entry to the end of a, with the content from r. func (a *Archive) AddEntry(typ EntryType, name string, mtime int64, uid, gid int, mode os.FileMode, size int64, r io.Reader) { - off, err := a.f.Seek(0, os.SEEK_END) + off, err := a.f.Seek(0, io.SeekEnd) if err != nil { log.Fatal(err) } diff --git a/src/cmd/internal/archive/archive_test.go b/src/cmd/internal/archive/archive_test.go index c284a9cf0dcc53..bbaa72cbf860c5 100644 --- a/src/cmd/internal/archive/archive_test.go +++ b/src/cmd/internal/archive/archive_test.go @@ -18,32 +18,23 @@ import ( "os/exec" "path/filepath" "runtime" + "sync" "testing" "unicode/utf8" ) -var ( - buildDir string - go1obj string - go2obj string - goarchive string - cgoarchive string -) +var buildDir string func TestMain(m *testing.M) { if !testenv.HasGoBuild() { return } - if err := buildGoobj(); err != nil { - fmt.Println(err) - os.RemoveAll(buildDir) - os.Exit(1) - } - exit := m.Run() - os.RemoveAll(buildDir) + if buildDir != "" { + os.RemoveAll(buildDir) + } os.Exit(exit) } @@ -89,71 +80,91 @@ func copyFile(dst, src string) (err error) { return nil } -func buildGoobj() error { - var err error +var ( + buildOnce sync.Once + builtGoobjs goobjPaths + buildErr error +) - buildDir, err = ioutil.TempDir("", "TestGoobj") - if err != nil { - return err - } +type goobjPaths struct { + go1obj string + go2obj string + goarchive string + cgoarchive string +} - go1obj = filepath.Join(buildDir, "go1.o") - go2obj = filepath.Join(buildDir, "go2.o") - goarchive = filepath.Join(buildDir, "go.a") +func buildGoobj(t *testing.T) goobjPaths { + buildOnce.Do(func() { + buildErr = func() (err error) { + buildDir, err = ioutil.TempDir("", "TestGoobj") + if err != nil { + return err + } - gotool, err := testenv.GoTool() - if err != nil { - return err - } + go1obj := filepath.Join(buildDir, "go1.o") + go2obj := filepath.Join(buildDir, "go2.o") + goarchive := filepath.Join(buildDir, "go.a") + cgoarchive := "" - go1src := filepath.Join("testdata", "go1.go") - go2src := filepath.Join("testdata", "go2.go") + gotool, err := testenv.GoTool() + if err != nil { + return err + } - out, err := exec.Command(gotool, "tool", "compile", "-o", go1obj, go1src).CombinedOutput() - if err != nil { - return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out) - } - out, err = exec.Command(gotool, "tool", "compile", "-o", go2obj, go2src).CombinedOutput() - if err != nil { - return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out) - } - out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput() - if err != nil { - return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out) - } + go1src := filepath.Join("testdata", "go1.go") + go2src := filepath.Join("testdata", "go2.go") - if testenv.HasCGO() { - gopath := filepath.Join(buildDir, "gopath") - err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo")) - if err == nil { - err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666) - } - if err != nil { - return err - } - cmd := exec.Command(gotool, "install", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo") - cmd.Dir = filepath.Join(gopath, "src", "mycgo") - cmd.Env = append(os.Environ(), "GOPATH="+gopath) - out, err = cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("go install mycgo: %v\n%s", err, out) - } - pat := filepath.Join(gopath, "pkg", "*", "mycgo.a") - ms, err := filepath.Glob(pat) - if err != nil { - return err - } - if len(ms) == 0 { - return fmt.Errorf("cannot found paths for pattern %s", pat) - } - cgoarchive = ms[0] - } + out, err := exec.Command(gotool, "tool", "compile", "-p=p", "-o", go1obj, go1src).CombinedOutput() + if err != nil { + return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out) + } + out, err = exec.Command(gotool, "tool", "compile", "-p=p", "-o", go2obj, go2src).CombinedOutput() + if err != nil { + return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out) + } + out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput() + if err != nil { + return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out) + } - return nil + if testenv.HasCGO() { + cgoarchive = filepath.Join(buildDir, "mycgo.a") + gopath := filepath.Join(buildDir, "gopath") + err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo")) + if err == nil { + err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666) + } + if err != nil { + return err + } + cmd := exec.Command(gotool, "build", "-buildmode=archive", "-o", cgoarchive, "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo") + cmd.Dir = filepath.Join(gopath, "src", "mycgo") + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("go install mycgo: %v\n%s", err, out) + } + } + + builtGoobjs = goobjPaths{ + go1obj: go1obj, + go2obj: go2obj, + goarchive: goarchive, + cgoarchive: cgoarchive, + } + return nil + }() + }) + + if buildErr != nil { + t.Helper() + t.Fatal(buildErr) + } + return builtGoobjs } func TestParseGoobj(t *testing.T) { - path := go1obj + path := buildGoobj(t).go1obj f, err := os.Open(path) if err != nil { @@ -182,7 +193,7 @@ func TestParseGoobj(t *testing.T) { } func TestParseArchive(t *testing.T) { - path := goarchive + path := buildGoobj(t).goarchive f, err := os.Open(path) if err != nil { @@ -227,7 +238,7 @@ func TestParseArchive(t *testing.T) { func TestParseCGOArchive(t *testing.T) { testenv.MustHaveCGO(t) - path := cgoarchive + path := buildGoobj(t).cgoarchive f, err := os.Open(path) if err != nil { diff --git a/src/cmd/internal/bio/buf_mmap.go b/src/cmd/internal/bio/buf_mmap.go index b9755c7e50e8b4..89ae39f736abbd 100644 --- a/src/cmd/internal/bio/buf_mmap.go +++ b/src/cmd/internal/bio/buf_mmap.go @@ -18,12 +18,12 @@ import ( // because some operating systems place a limit on the number of // distinct mapped regions per process. As of this writing: // -// Darwin unlimited -// DragonFly 1000000 (vm.max_proc_mmap) -// FreeBSD unlimited -// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count? -// NetBSD unlimited -// OpenBSD unlimited +// Darwin unlimited +// DragonFly 1000000 (vm.max_proc_mmap) +// FreeBSD unlimited +// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count? +// NetBSD unlimited +// OpenBSD unlimited var mmapLimit int32 = 1<<31 - 1 func init() { diff --git a/src/cmd/internal/browser/browser.go b/src/cmd/internal/browser/browser.go index 577d31789f19af..6867c85d232040 100644 --- a/src/cmd/internal/browser/browser.go +++ b/src/cmd/internal/browser/browser.go @@ -6,8 +6,8 @@ package browser import ( - exec "internal/execabs" "os" + "os/exec" "runtime" "time" ) diff --git a/src/cmd/internal/buildid/buildid_test.go b/src/cmd/internal/buildid/buildid_test.go index e832f9987e582e..f04e328046c0a9 100644 --- a/src/cmd/internal/buildid/buildid_test.go +++ b/src/cmd/internal/buildid/buildid_test.go @@ -103,7 +103,7 @@ func TestFindAndHash(t *testing.T) { id[i] = byte(i) } numError := 0 - errorf := func(msg string, args ...interface{}) { + errorf := func(msg string, args ...any) { t.Errorf(msg, args...) if numError++; numError > 20 { t.Logf("stopping after too many errors") @@ -177,3 +177,11 @@ func TestExcludedReader(t *testing.T) { } } } + +func TestEmptyID(t *testing.T) { + r := strings.NewReader("aha!") + matches, hash, err := FindAndHash(r, "", 1000) + if matches != nil || hash != ([32]byte{}) || err == nil || !strings.Contains(err.Error(), "no id") { + t.Errorf("FindAndHash: want nil, [32]byte{}, no id specified, got %v, %v, %v", matches, hash, err) + } +} diff --git a/src/cmd/internal/buildid/rewrite.go b/src/cmd/internal/buildid/rewrite.go index a7928959c483da..becc0782424ea0 100644 --- a/src/cmd/internal/buildid/rewrite.go +++ b/src/cmd/internal/buildid/rewrite.go @@ -22,6 +22,9 @@ func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32 if bufSize == 0 { bufSize = 31 * 1024 // bufSize+little will likely fit in 32 kB } + if len(id) == 0 { + return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: no id specified") + } if len(id) > bufSize { return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: buffer too small") } @@ -148,7 +151,7 @@ func (r *excludedReader) Read(p []byte) (int, error) { return n, err } -func findMachoCodeSignature(r interface{}) (*macho.File, codesign.CodeSigCmd, bool) { +func findMachoCodeSignature(r any) (*macho.File, codesign.CodeSigCmd, bool) { ra, ok := r.(io.ReaderAt) if !ok { return nil, codesign.CodeSigCmd{}, false diff --git a/src/cmd/internal/codesign/codesign.go b/src/cmd/internal/codesign/codesign.go index 0517a10640a0bd..1116393b5c9612 100644 --- a/src/cmd/internal/codesign/codesign.go +++ b/src/cmd/internal/codesign/codesign.go @@ -11,10 +11,11 @@ package codesign import ( - "crypto/sha256" "debug/macho" "encoding/binary" "io" + + "cmd/internal/notsha256" ) // Code signature layout. @@ -190,7 +191,7 @@ func Size(codeSize int64, id string) int64 { nhashes := (codeSize + pageSize - 1) / pageSize idOff := int64(codeDirectorySize) hashOff := idOff + int64(len(id)+1) - cdirSz := hashOff + nhashes*sha256.Size + cdirSz := hashOff + nhashes*notsha256.Size return int64(superBlobSize+blobSize) + cdirSz } @@ -226,7 +227,7 @@ func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int identOffset: uint32(idOff), nCodeSlots: uint32(nhashes), codeLimit: uint32(codeSize), - hashSize: sha256.Size, + hashSize: notsha256.Size, hashType: CS_HASHTYPE_SHA256, pageSize: uint8(pageSizeBits), execSegBase: uint64(textOff), @@ -245,8 +246,12 @@ func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int outp = puts(outp, []byte(id+"\000")) // emit hashes + // NOTE(rsc): These must be SHA256, but for cgo bootstrap reasons + // we cannot import crypto/sha256 when GOEXPERIMENT=boringcrypto + // and the host is linux/amd64. So we use NOT-SHA256 + // and then apply a NOT ourselves to get SHA256. Sigh. var buf [pageSize]byte - h := sha256.New() + h := notsha256.New() p := 0 for p < int(codeSize) { n, err := io.ReadFull(data, buf[:]) @@ -263,6 +268,9 @@ func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int h.Reset() h.Write(buf[:n]) b := h.Sum(nil) + for i := range b { + b[i] ^= 0xFF // convert notsha256 to sha256 + } outp = puts(outp, b[:]) } } diff --git a/src/cmd/internal/diff/diff.go b/src/cmd/internal/diff/diff.go deleted file mode 100644 index 0ec2d7f8f95dce..00000000000000 --- a/src/cmd/internal/diff/diff.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package diff implements a Diff function that compare two inputs -// using the 'diff' tool. -package diff - -import ( - "bytes" - exec "internal/execabs" - "io/ioutil" - "os" - "runtime" -) - -// Returns diff of two arrays of bytes in diff tool format. -func Diff(prefix string, b1, b2 []byte) ([]byte, error) { - f1, err := writeTempFile(prefix, b1) - if err != nil { - return nil, err - } - defer os.Remove(f1) - - f2, err := writeTempFile(prefix, b2) - if err != nil { - return nil, err - } - defer os.Remove(f2) - - cmd := "diff" - if runtime.GOOS == "plan9" { - cmd = "/bin/ape/diff" - } - - data, err := exec.Command(cmd, "-u", f1, f2).CombinedOutput() - if len(data) > 0 { - // diff exits with a non-zero status when the files don't match. - // Ignore that failure as long as we get output. - err = nil - } - - // If we are on Windows and the diff is Cygwin diff, - // machines can get into a state where every Cygwin - // command works fine but prints a useless message like: - // - // Cygwin WARNING: - // Couldn't compute FAST_CWD pointer. This typically occurs if you're using - // an older Cygwin version on a newer Windows. Please update to the latest - // available Cygwin version from https://cygwin.com/. If the problem persists, - // please see https://cygwin.com/problems.html - // - // Skip over that message and just return the actual diff. - if len(data) > 0 && !bytes.HasPrefix(data, []byte("--- ")) { - i := bytes.Index(data, []byte("\n--- ")) - if i >= 0 && i < 80*10 && bytes.Contains(data[:i], []byte("://cygwin.com/")) { - data = data[i+1:] - } - } - - return data, err -} - -func writeTempFile(prefix string, data []byte) (string, error) { - file, err := ioutil.TempFile("", prefix) - if err != nil { - return "", err - } - _, err = file.Write(data) - if err1 := file.Close(); err == nil { - err = err1 - } - if err != nil { - os.Remove(file.Name()) - return "", err - } - return file.Name(), nil -} diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index ec441c2bcb687d..4821efa963edcd 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -12,7 +12,7 @@ import ( "errors" "fmt" "internal/buildcfg" - exec "internal/execabs" + "os/exec" "sort" "strconv" "strings" @@ -21,15 +21,15 @@ import ( ) // InfoPrefix is the prefix for all the symbols containing DWARF info entries. -const InfoPrefix = "go.info." +const InfoPrefix = "go:info." // ConstInfoPrefix is the prefix for all symbols containing DWARF info // entries that contain constants. -const ConstInfoPrefix = "go.constinfo." +const ConstInfoPrefix = "go:constinfo." // CUInfoPrefix is the prefix for symbols containing information to // populate the DWARF compilation unit info entries. -const CUInfoPrefix = "go.cuinfo." +const CUInfoPrefix = "go:cuinfo." // Used to form the symbol name assigned to the DWARF 'abstract subprogram" // info entry for a function @@ -50,6 +50,7 @@ type Var struct { Abbrev int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST] IsReturnValue bool IsInlFormal bool + DictIndex uint16 // index of the dictionary entry describing the type of this variable StackOffset int32 // This package can't use the ssa package, so it can't mention ssa.FuncDebug, // so indirect through a closure. @@ -97,6 +98,8 @@ type FnState struct { Scopes []Scope InlCalls InlCalls UseBASEntries bool + + dictIndexToOffset []int64 } func EnableLogging(doit bool) { @@ -315,6 +318,7 @@ const ( DW_AT_go_runtime_type = 0x2904 DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit + DW_AT_go_dict_index = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape DW_AT_internal_location = 253 // params and locals; not emitted ) @@ -325,8 +329,10 @@ const ( DW_ABRV_COMPUNIT DW_ABRV_COMPUNIT_TEXTLESS DW_ABRV_FUNCTION + DW_ABRV_WRAPPER DW_ABRV_FUNCTION_ABSTRACT DW_ABRV_FUNCTION_CONCRETE + DW_ABRV_WRAPPER_CONCRETE DW_ABRV_INLINED_SUBROUTINE DW_ABRV_INLINED_SUBROUTINE_RANGES DW_ABRV_VARIABLE @@ -360,6 +366,7 @@ const ( DW_ABRV_STRINGTYPE DW_ABRV_STRUCTTYPE DW_ABRV_TYPEDECL + DW_ABRV_DICT_INDEX DW_NABRV ) @@ -455,6 +462,19 @@ var abbrevs = [DW_NABRV]dwAbbrev{ }, }, + /* WRAPPER */ + { + DW_TAG_subprogram, + DW_CHILDREN_yes, + []dwAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_low_pc, DW_FORM_addr}, + {DW_AT_high_pc, DW_FORM_addr}, + {DW_AT_frame_base, DW_FORM_block1}, + {DW_AT_trampoline, DW_FORM_flag}, + }, + }, + /* FUNCTION_ABSTRACT */ { DW_TAG_subprogram, @@ -478,6 +498,19 @@ var abbrevs = [DW_NABRV]dwAbbrev{ }, }, + /* WRAPPER_CONCRETE */ + { + DW_TAG_subprogram, + DW_CHILDREN_yes, + []dwAttrForm{ + {DW_AT_abstract_origin, DW_FORM_ref_addr}, + {DW_AT_low_pc, DW_FORM_addr}, + {DW_AT_high_pc, DW_FORM_addr}, + {DW_AT_frame_base, DW_FORM_block1}, + {DW_AT_trampoline, DW_FORM_flag}, + }, + }, + /* INLINED_SUBROUTINE */ { DW_TAG_inlined_subroutine, @@ -854,6 +887,17 @@ var abbrevs = [DW_NABRV]dwAbbrev{ {DW_AT_type, DW_FORM_ref_addr}, }, }, + + /* DICT_INDEX */ + { + DW_TAG_typedef, + DW_CHILDREN_no, + []dwAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_dict_index, DW_FORM_udata}, + }, + }, } // GetAbbrev returns the contents of the .debug_abbrev section. @@ -1168,6 +1212,9 @@ func putPrunedScopes(ctxt Context, s *FnState, fnabbrev int) error { sort.Sort(byChildIndex(pruned.Vars)) scopes[k] = pruned } + + s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev) + var encbuf [20]byte if putscope(ctxt, s, scopes, 0, fnabbrev, encbuf[:0]) < int32(len(scopes)) { return errors.New("multiple toplevel scopes") @@ -1266,7 +1313,7 @@ func PutAbstractFunc(ctxt Context, s *FnState) error { // its corresponding 'abstract' DIE (containing location-independent // attributes such as name, type, etc). Inlined subroutine DIEs can // have other inlined subroutine DIEs as children. -func putInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error { +func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error { ic := s.InlCalls.Calls[callIdx] callee := ic.AbsFunSym @@ -1277,7 +1324,7 @@ func putInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error Uleb128put(ctxt, s.Info, int64(abbrev)) if logDwarf { - ctxt.Logf("putInlinedFunc(caller=%v,callee=%v,abbrev=%d)\n", callersym, callee, abbrev) + ctxt.Logf("putInlinedFunc(callee=%v,abbrev=%d)\n", callee, abbrev) } // Abstract origin. @@ -1312,8 +1359,7 @@ func putInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error // Children of this inline. for _, sib := range inlChildren(callIdx, &s.InlCalls) { - absfn := s.InlCalls.Calls[sib].AbsFunSym - err := putInlinedFunc(ctxt, s, absfn, sib) + err := putInlinedFunc(ctxt, s, sib) if err != nil { return err } @@ -1330,11 +1376,14 @@ func putInlinedFunc(ctxt Context, s *FnState, callersym Sym, callIdx int) error // for the function (which holds location-independent attributes such // as name, type), then the remainder of the attributes are specific // to this instance (location, frame base, etc). -func PutConcreteFunc(ctxt Context, s *FnState) error { +func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error { if logDwarf { ctxt.Logf("PutConcreteFunc(%v)\n", s.Info) } abbrev := DW_ABRV_FUNCTION_CONCRETE + if isWrapper { + abbrev = DW_ABRV_WRAPPER_CONCRETE + } Uleb128put(ctxt, s.Info, int64(abbrev)) // Abstract origin. @@ -1347,6 +1396,10 @@ func PutConcreteFunc(ctxt Context, s *FnState) error { // cfa / frame base putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa}) + if isWrapper { + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0) + } + // Scopes if err := putPrunedScopes(ctxt, s, abbrev); err != nil { return err @@ -1354,8 +1407,7 @@ func PutConcreteFunc(ctxt Context, s *FnState) error { // Inlined subroutines. for _, sib := range inlChildren(-1, &s.InlCalls) { - absfn := s.InlCalls.Calls[sib].AbsFunSym - err := putInlinedFunc(ctxt, s, absfn, sib) + err := putInlinedFunc(ctxt, s, sib) if err != nil { return err } @@ -1370,11 +1422,14 @@ func PutConcreteFunc(ctxt Context, s *FnState) error { // when its containing package was compiled (hence there is no need to // emit an abstract version for it to use as a base for inlined // routine records). -func PutDefaultFunc(ctxt Context, s *FnState) error { +func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error { if logDwarf { ctxt.Logf("PutDefaultFunc(%v)\n", s.Info) } abbrev := DW_ABRV_FUNCTION + if isWrapper { + abbrev = DW_ABRV_WRAPPER + } Uleb128put(ctxt, s.Info, int64(abbrev)) // Expand '"".' to import path. @@ -1387,13 +1442,16 @@ func PutDefaultFunc(ctxt Context, s *FnState) error { putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC) putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC) putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa}) - ctxt.AddFileRef(s.Info, s.Filesym) - - var ev int64 - if s.External { - ev = 1 + if isWrapper { + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0) + } else { + ctxt.AddFileRef(s.Info, s.Filesym) + var ev int64 + if s.External { + ev = 1 + } + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0) } - putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0) // Scopes if err := putPrunedScopes(ctxt, s, abbrev); err != nil { @@ -1402,8 +1460,7 @@ func PutDefaultFunc(ctxt Context, s *FnState) error { // Inlined subroutines. for _, sib := range inlChildren(-1, &s.InlCalls) { - absfn := s.InlCalls.Calls[sib].AbsFunSym - err := putInlinedFunc(ctxt, s, absfn, sib) + err := putInlinedFunc(ctxt, s, sib) if err != nil { return err } @@ -1413,6 +1470,47 @@ func PutDefaultFunc(ctxt Context, s *FnState) error { return nil } +// putparamtypes writes typedef DIEs for any parametric types that are used by this function. +func putparamtypes(ctxt Context, s *FnState, scopes []Scope, fnabbrev int) []int64 { + if fnabbrev == DW_ABRV_FUNCTION_CONCRETE { + return nil + } + + maxDictIndex := uint16(0) + + for i := range scopes { + for _, v := range scopes[i].Vars { + if v.DictIndex > maxDictIndex { + maxDictIndex = v.DictIndex + } + } + } + + if maxDictIndex == 0 { + return nil + } + + dictIndexToOffset := make([]int64, maxDictIndex) + + for i := range scopes { + for _, v := range scopes[i].Vars { + if v.DictIndex == 0 || dictIndexToOffset[v.DictIndex-1] != 0 { + continue + } + + dictIndexToOffset[v.DictIndex-1] = ctxt.CurrentOffset(s.Info) + + Uleb128put(ctxt, s.Info, int64(DW_ABRV_DICT_INDEX)) + n := fmt.Sprintf(".param%d", v.DictIndex-1) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DictIndex-1), nil) + } + } + + return dictIndexToOffset +} + func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev int, encbuf []byte) int32 { if logDwarf { @@ -1492,10 +1590,10 @@ func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) { // Determine whether to use a concrete variable or regular variable DIE. concrete := true switch fnabbrev { - case DW_ABRV_FUNCTION: + case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER: concrete = false break - case DW_ABRV_FUNCTION_CONCRETE: + case DW_ABRV_FUNCTION_CONCRETE, DW_ABRV_WRAPPER_CONCRETE: // If we're emitting a concrete subprogram DIE and the variable // in question is not part of the corresponding abstract function DIE, // then use the default (non-concrete) abbrev for this param. @@ -1586,7 +1684,12 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil) } putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) - putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 { + // If the type of this variable is parametric use the entry emitted by putparamtypes + putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info) + } else { + putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + } } if abbrevUsesLoclist(abbrev) { @@ -1620,8 +1723,10 @@ func (s byChildIndex) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // current extld. // AIX ld doesn't support DWARF with -bnoobjreorder with version // prior to 7.2.2. -func IsDWARFEnabledOnAIXLd(extld string) (bool, error) { - out, err := exec.Command(extld, "-Wl,-V").CombinedOutput() +func IsDWARFEnabledOnAIXLd(extld []string) (bool, error) { + name, args := extld[0], extld[1:] + args = append(args, "-Wl,-V") + out, err := exec.Command(name, args...).CombinedOutput() if err != nil { // The normal output should display ld version and // then fails because ".main" is not defined: diff --git a/src/cmd/internal/gcprog/gcprog.go b/src/cmd/internal/gcprog/gcprog.go index c8bf20646870d1..eeea53daf4a5e9 100644 --- a/src/cmd/internal/gcprog/gcprog.go +++ b/src/cmd/internal/gcprog/gcprog.go @@ -5,7 +5,7 @@ // Package gcprog implements an encoder for packed GC pointer bitmaps, // known as GC programs. // -// Program Format +// # Program Format // // The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object. // The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the @@ -20,7 +20,6 @@ // // The numbers n and c, when they follow a code, are encoded as varints // using the same encoding as encoding/binary's Uvarint. -// package gcprog import ( diff --git a/src/cmd/internal/goobj/builtin.go b/src/cmd/internal/goobj/builtin.go index e7d612aeb746f9..aa665fde99a6a2 100644 --- a/src/cmd/internal/goobj/builtin.go +++ b/src/cmd/internal/goobj/builtin.go @@ -4,6 +4,8 @@ package goobj +import "internal/buildcfg" + // Builtin (compiler-generated) function references appear // frequently. We assign special indices for them, so they // don't need to be referenced by name. @@ -27,7 +29,7 @@ func BuiltinIdx(name string, abi int) int { if !ok { return -1 } - if builtins[i].abi != abi { + if buildcfg.Experiment.RegabiWrappers && builtins[i].abi != abi { return -1 } return i diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go index 9f248137daabce..ae2e6cc0041689 100644 --- a/src/cmd/internal/goobj/builtinlist.go +++ b/src/cmd/internal/goobj/builtinlist.go @@ -33,6 +33,7 @@ var builtins = [...]struct { {"runtime.goPanicSlice3BU", 1}, {"runtime.goPanicSlice3C", 1}, {"runtime.goPanicSlice3CU", 1}, + {"runtime.goPanicSliceConvert", 1}, {"runtime.printbool", 1}, {"runtime.printfloat", 1}, {"runtime.printint", 1}, @@ -129,6 +130,8 @@ var builtins = [...]struct { {"runtime.makeslice64", 1}, {"runtime.makeslicecopy", 1}, {"runtime.growslice", 1}, + {"runtime.unsafeslice", 1}, + {"runtime.unsafeslice64", 1}, {"runtime.memmove", 1}, {"runtime.memclrNoHeapPointers", 1}, {"runtime.memclrHasPointers", 1}, @@ -192,6 +195,8 @@ var builtins = [...]struct { {"runtime.libfuzzerTraceConstCmp2", 1}, {"runtime.libfuzzerTraceConstCmp4", 1}, {"runtime.libfuzzerTraceConstCmp8", 1}, + {"runtime.libfuzzerHookStrCmp", 1}, + {"runtime.libfuzzerHookEqualFold", 1}, {"runtime.x86HasPOPCNT", 0}, {"runtime.x86HasSSE41", 0}, {"runtime.x86HasFMA", 0}, @@ -203,44 +208,46 @@ var builtins = [...]struct { {"runtime.newproc", 1}, {"runtime.panicoverflow", 1}, {"runtime.sigpanic", 1}, - {"runtime.gcWriteBarrier", 0}, + {"runtime.gcWriteBarrier", 1}, + {"runtime.duffzero", 1}, + {"runtime.duffcopy", 1}, {"runtime.morestack", 0}, {"runtime.morestackc", 0}, {"runtime.morestack_noctxt", 0}, - {"type.int8", 0}, - {"type.*int8", 0}, - {"type.uint8", 0}, - {"type.*uint8", 0}, - {"type.int16", 0}, - {"type.*int16", 0}, - {"type.uint16", 0}, - {"type.*uint16", 0}, - {"type.int32", 0}, - {"type.*int32", 0}, - {"type.uint32", 0}, - {"type.*uint32", 0}, - {"type.int64", 0}, - {"type.*int64", 0}, - {"type.uint64", 0}, - {"type.*uint64", 0}, - {"type.float32", 0}, - {"type.*float32", 0}, - {"type.float64", 0}, - {"type.*float64", 0}, - {"type.complex64", 0}, - {"type.*complex64", 0}, - {"type.complex128", 0}, - {"type.*complex128", 0}, - {"type.unsafe.Pointer", 0}, - {"type.*unsafe.Pointer", 0}, - {"type.uintptr", 0}, - {"type.*uintptr", 0}, - {"type.bool", 0}, - {"type.*bool", 0}, - {"type.string", 0}, - {"type.*string", 0}, - {"type.error", 0}, - {"type.*error", 0}, - {"type.func(error) string", 0}, - {"type.*func(error) string", 0}, + {"type:int8", 0}, + {"type:*int8", 0}, + {"type:uint8", 0}, + {"type:*uint8", 0}, + {"type:int16", 0}, + {"type:*int16", 0}, + {"type:uint16", 0}, + {"type:*uint16", 0}, + {"type:int32", 0}, + {"type:*int32", 0}, + {"type:uint32", 0}, + {"type:*uint32", 0}, + {"type:int64", 0}, + {"type:*int64", 0}, + {"type:uint64", 0}, + {"type:*uint64", 0}, + {"type:float32", 0}, + {"type:*float32", 0}, + {"type:float64", 0}, + {"type:*float64", 0}, + {"type:complex64", 0}, + {"type:*complex64", 0}, + {"type:complex128", 0}, + {"type:*complex128", 0}, + {"type:unsafe.Pointer", 0}, + {"type:*unsafe.Pointer", 0}, + {"type:uintptr", 0}, + {"type:*uintptr", 0}, + {"type:bool", 0}, + {"type:*bool", 0}, + {"type:string", 0}, + {"type:*string", 0}, + {"type:error", 0}, + {"type:*error", 0}, + {"type:func(error) string", 0}, + {"type:*func(error) string", 0}, } diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go index 6d33a10a51cda4..59cb957fa7df5c 100644 --- a/src/cmd/internal/goobj/funcinfo.go +++ b/src/cmd/internal/goobj/funcinfo.go @@ -16,23 +16,13 @@ type CUFileIndex uint32 // FuncInfo is serialized as a symbol (aux symbol). The symbol data is // the binary encoding of the struct below. -// -// TODO: make each pcdata a separate symbol? type FuncInfo struct { Args uint32 Locals uint32 FuncID objabi.FuncID FuncFlag objabi.FuncFlag - - Pcsp SymRef - Pcfile SymRef - Pcline SymRef - Pcinline SymRef - Pcdata []SymRef - Funcdataoff []uint32 - File []CUFileIndex - - InlTree []InlTreeNode + File []CUFileIndex + InlTree []InlTreeNode } func (a *FuncInfo) Write(w *bytes.Buffer) { @@ -44,10 +34,6 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { binary.LittleEndian.PutUint32(b[:], x) w.Write(b[:]) } - writeSymRef := func(s SymRef) { - writeUint32(s.PkgIdx) - writeUint32(s.SymIdx) - } writeUint32(a.Args) writeUint32(a.Locals) @@ -55,19 +41,7 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { writeUint8(uint8(a.FuncFlag)) writeUint8(0) // pad to uint32 boundary writeUint8(0) - writeSymRef(a.Pcsp) - writeSymRef(a.Pcfile) - writeSymRef(a.Pcline) - writeSymRef(a.Pcinline) - writeUint32(uint32(len(a.Pcdata))) - for _, sym := range a.Pcdata { - writeSymRef(sym) - } - writeUint32(uint32(len(a.Funcdataoff))) - for _, x := range a.Funcdataoff { - writeUint32(x) - } writeUint32(uint32(len(a.File))) for _, f := range a.File { writeUint32(uint32(f)) @@ -84,31 +58,19 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { // corresponding "off" field stores the byte offset of the start of // the items in question. type FuncInfoLengths struct { - NumPcdata uint32 - PcdataOff uint32 - NumFuncdataoff uint32 - FuncdataoffOff uint32 - NumFile uint32 - FileOff uint32 - NumInlTree uint32 - InlTreeOff uint32 - Initialized bool + NumFile uint32 + FileOff uint32 + NumInlTree uint32 + InlTreeOff uint32 + Initialized bool } func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths { var result FuncInfoLengths - // Offset to the number of pcdata values. This value is determined by counting - // the number of bytes until we write pcdata to the file. - const numpcdataOff = 44 - result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:]) - result.PcdataOff = numpcdataOff + 4 - - numfuncdataoffOff := result.PcdataOff + 8*result.NumPcdata - result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:]) - result.FuncdataoffOff = numfuncdataoffOff + 4 - - numfileOff := result.FuncdataoffOff + 4*result.NumFuncdataoff + // Offset to the number of the file table. This value is determined by counting + // the number of bytes until we write funcdataoff to the file. + const numfileOff = 12 result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:]) result.FileOff = numfileOff + 4 @@ -129,34 +91,6 @@ func (*FuncInfo) ReadFuncID(b []byte) objabi.FuncID { return objabi.FuncID(b[8]) func (*FuncInfo) ReadFuncFlag(b []byte) objabi.FuncFlag { return objabi.FuncFlag(b[9]) } -func (*FuncInfo) ReadPcsp(b []byte) SymRef { - return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])} -} - -func (*FuncInfo) ReadPcfile(b []byte) SymRef { - return SymRef{binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])} -} - -func (*FuncInfo) ReadPcline(b []byte) SymRef { - return SymRef{binary.LittleEndian.Uint32(b[28:]), binary.LittleEndian.Uint32(b[32:])} -} - -func (*FuncInfo) ReadPcinline(b []byte) SymRef { - return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])} -} - -func (*FuncInfo) ReadPcdata(b []byte) []SymRef { - syms := make([]SymRef, binary.LittleEndian.Uint32(b[44:])) - for i := range syms { - syms[i] = SymRef{binary.LittleEndian.Uint32(b[48+i*8:]), binary.LittleEndian.Uint32(b[52+i*8:])} - } - return syms -} - -func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 { - return int64(binary.LittleEndian.Uint32(b[funcdataofffoff+4*k:])) -} - func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) CUFileIndex { return CUFileIndex(binary.LittleEndian.Uint32(b[filesoff+4*k:])) } diff --git a/src/cmd/internal/goobj/mkbuiltin.go b/src/cmd/internal/goobj/mkbuiltin.go index 18b969586cceda..d9db63ad445011 100644 --- a/src/cmd/internal/goobj/mkbuiltin.go +++ b/src/cmd/internal/goobj/mkbuiltin.go @@ -105,7 +105,7 @@ func mkbuiltin(w io.Writer) { extras := append(fextras[:], enumerateBasicTypes()...) for _, b := range extras { prefix := "" - if !strings.HasPrefix(b.name, "type.") { + if !strings.HasPrefix(b.name, "type:") { prefix = pkg + "." } name := prefix + b.name @@ -130,8 +130,8 @@ func enumerateBasicTypes() []extra { "func(error) string"} result := []extra{} for _, n := range names { - result = append(result, extra{"type." + n, 0}) - result = append(result, extra{"type.*" + n, 0}) + result = append(result, extra{"type:" + n, 0}) + result = append(result, extra{"type:*" + n, 0}) } return result } @@ -151,7 +151,9 @@ var fextras = [...]extra{ {"sigpanic", 1}, // compiler backend inserted calls - {"gcWriteBarrier", 0}, // asm function, ABI0 + {"gcWriteBarrier", 1}, + {"duffzero", 1}, + {"duffcopy", 1}, // assembler backend inserted calls {"morestack", 0}, // asm function, ABI0 diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go index e2858bd57da0ca..39b86b0f8fef7b 100644 --- a/src/cmd/internal/goobj/objfile.go +++ b/src/cmd/internal/goobj/objfile.go @@ -19,21 +19,18 @@ package goobj import ( - "bytes" "cmd/internal/bio" - "crypto/sha1" "encoding/binary" "errors" "fmt" "internal/unsafeheader" - "io" "unsafe" ) // New object file format. // // Header struct { -// Magic [...]byte // "\x00go117ld" +// Magic [...]byte // "\x00go118ld" // Fingerprint [8]byte // Flags uint32 // Offsets [...]uint32 // byte offset of each block below @@ -100,7 +97,6 @@ import ( // } // // Data [...]byte -// Pcdata [...]byte // // // blocks only used by tools (objdump, nm) // @@ -181,6 +177,7 @@ const ( PkgIdxHashed // Hashed (content-addressable) symbols PkgIdxBuiltin // Predefined runtime symbols (ex: runtime.newobject) PkgIdxSelf // Symbols defined in the current package + PkgIdxSpecial = PkgIdxSelf // Indices above it has special meanings PkgIdxInvalid = 0 // The index of other referenced packages starts from 1. ) @@ -204,7 +201,6 @@ const ( BlkReloc BlkAux BlkData - BlkPcdata BlkRefName BlkEnd NBlk @@ -219,7 +215,7 @@ type Header struct { Offsets [NBlk]uint32 } -const Magic = "\x00go117ld" +const Magic = "\x00go118ld" func (h *Header) Write(w *Writer) { w.RawString(h.Magic) @@ -268,15 +264,16 @@ func (p *ImportedPkg) Write(w *Writer) { // Symbol definition. // // Serialized format: -// Sym struct { -// Name string -// ABI uint16 -// Type uint8 -// Flag uint8 -// Flag2 uint8 -// Siz uint32 -// Align uint32 -// } +// +// Sym struct { +// Name string +// ABI uint16 +// Type uint8 +// Flag uint8 +// Flag2 uint8 +// Siz uint32 +// Align uint32 +// } type Sym [SymSize]byte const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4 @@ -284,9 +281,10 @@ const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4 const SymABIstatic = ^uint16(0) const ( - ObjFlagShared = 1 << iota // this object is built with -shared - ObjFlagNeedNameExpansion // the linker needs to expand `"".` to package path in symbol names - ObjFlagFromAssembly // object is from asm src, not go + ObjFlagShared = 1 << iota // this object is built with -shared + _ // was ObjFlagNeedNameExpansion + ObjFlagFromAssembly // object is from asm src, not go + ObjFlagUnlinkable // unlinkable package (linker will emit an error) ) // Sym.Flag @@ -304,6 +302,7 @@ const ( const ( SymFlagUsedInIface = 1 << iota SymFlagItab + SymFlagDict ) // Returns the length of the name of the symbol. @@ -333,6 +332,7 @@ func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 } func (s *Sym) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 } func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 } func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 } +func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 } func (s *Sym) SetName(x string, w *Writer) { binary.LittleEndian.PutUint32(s[:], uint32(len(x))) @@ -357,6 +357,8 @@ type SymRef struct { SymIdx uint32 } +func (s SymRef) IsZero() bool { return s == SymRef{} } + // Hash64 type Hash64Type [Hash64Size]byte @@ -365,18 +367,19 @@ const Hash64Size = 8 // Hash type HashType [HashSize]byte -const HashSize = sha1.Size +const HashSize = 16 // truncated SHA256 // Relocation. // // Serialized format: -// Reloc struct { -// Off int32 -// Siz uint8 -// Type uint16 -// Add int64 -// Sym SymRef -// } +// +// Reloc struct { +// Off int32 +// Siz uint8 +// Type uint16 +// Add int64 +// Sym SymRef +// } type Reloc [RelocSize]byte const RelocSize = 4 + 1 + 2 + 8 + 8 @@ -414,10 +417,11 @@ func (r *Reloc) fromBytes(b []byte) { copy(r[:], b) } // Aux symbol info. // // Serialized format: -// Aux struct { -// Type uint8 -// Sym SymRef -// } +// +// Aux struct { +// Type uint8 +// Sym SymRef +// } type Aux [AuxSize]byte const AuxSize = 1 + 8 @@ -457,11 +461,12 @@ func (a *Aux) fromBytes(b []byte) { copy(a[:], b) } // Referenced symbol flags. // // Serialized format: -// RefFlags struct { -// Sym symRef -// Flag uint8 -// Flag2 uint8 -// } +// +// RefFlags struct { +// Sym symRef +// Flag uint8 +// Flag2 uint8 +// } type RefFlags [RefFlagsSize]byte const RefFlagsSize = 8 + 1 + 1 @@ -489,10 +494,11 @@ const huge = (1<<31 - 1) / RelocSize // Referenced symbol name. // // Serialized format: -// RefName struct { -// Sym symRef -// Name string -// } +// +// RefName struct { +// Sym symRef +// Name string +// } type RefName [RefNameSize]byte const RefNameSize = 8 + stringRefSize @@ -592,13 +598,12 @@ type Reader struct { b []byte // mmapped bytes, if not nil readonly bool // whether b is backed with read-only memory - rd io.ReaderAt start uint32 h Header // keep block offsets } func NewReaderFromBytes(b []byte, readonly bool) *Reader { - r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0} + r := &Reader{b: b, readonly: readonly, start: 0} err := r.h.Read(r) if err != nil { return nil @@ -867,6 +872,6 @@ func (r *Reader) Flags() uint32 { return r.h.Flags } -func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 } -func (r *Reader) NeedNameExpansion() bool { return r.Flags()&ObjFlagNeedNameExpansion != 0 } -func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 } +func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 } +func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 } +func (r *Reader) Unlinkable() bool { return r.Flags()&ObjFlagUnlinkable != 0 } diff --git a/src/cmd/internal/metadata/main.go b/src/cmd/internal/metadata/main.go new file mode 100644 index 00000000000000..7478eec1c9b516 --- /dev/null +++ b/src/cmd/internal/metadata/main.go @@ -0,0 +1,33 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Metadata prints basic system metadata to include in test logs. This is +// separate from cmd/dist so it does not need to build with the bootstrap +// toolchain. + +// This program is only used by cmd/dist. Add an "ignore" build tag so it +// is not installed. cmd/dist does "go run main.go" directly. + +//go:build ignore + +package main + +import ( + "cmd/internal/osinfo" + "fmt" + "internal/sysinfo" + "runtime" +) + +func main() { + fmt.Printf("# GOARCH: %s\n", runtime.GOARCH) + fmt.Printf("# CPU: %s\n", sysinfo.CPU.Name()) + + fmt.Printf("# GOOS: %s\n", runtime.GOOS) + ver, err := osinfo.Version() + if err != nil { + ver = fmt.Sprintf("UNKNOWN: error determining OS version: %v", err) + } + fmt.Printf("# OS Version: %s\n", ver) +} diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index 56c3b2585c7fdc..053cb8f548cc2f 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -15,7 +15,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "strings" "sync" "testing" @@ -56,7 +55,7 @@ func TestAllDependencies(t *testing.T) { // dependencies are vendored. If any imported package is missing, // 'go list -deps' will fail when attempting to load it. cmd := exec.Command(goBin, "list", "-mod=vendor", "-deps", "./...") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = m.Dir cmd.Stderr = new(strings.Builder) _, err := cmd.Output() @@ -70,7 +69,7 @@ func TestAllDependencies(t *testing.T) { // There is no vendor directory, so the module must have no dependencies. // Check that the list of active modules contains only the main module. cmd := exec.Command(goBin, "list", "-mod=readonly", "-m", "all") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = m.Dir cmd.Stderr = new(strings.Builder) out, err := cmd.Output() @@ -153,7 +152,7 @@ func TestAllDependencies(t *testing.T) { // module version specified in GOROOT/src/cmd/go.mod. bundleDir := t.TempDir() r := runner{ - Dir: filepath.Join(runtime.GOROOT(), "src/cmd"), + Dir: filepath.Join(testenv.GOROOT(t), "src/cmd"), Env: append(os.Environ(), modcacheEnv...), } r.run(t, goBin, "build", "-mod=readonly", "-o", bundleDir, "golang.org/x/tools/cmd/bundle") @@ -183,9 +182,9 @@ func TestAllDependencies(t *testing.T) { } }() - rel, err := filepath.Rel(runtime.GOROOT(), m.Dir) + rel, err := filepath.Rel(testenv.GOROOT(t), m.Dir) if err != nil { - t.Fatalf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), m.Dir, err) + t.Fatalf("filepath.Rel(%q, %q): %v", testenv.GOROOT(t), m.Dir, err) } r := runner{ Dir: filepath.Join(gorootCopyDir, rel), @@ -198,6 +197,7 @@ func TestAllDependencies(t *testing.T) { // Add GOROOTcopy/bin and bundleDir to front of PATH. "PATH="+filepath.Join(gorootCopyDir, "bin")+string(filepath.ListSeparator)+ bundleDir+string(filepath.ListSeparator)+os.Getenv("PATH"), + "GOWORK=off", ), } goBinCopy := filepath.Join(gorootCopyDir, "bin", "go") @@ -252,22 +252,22 @@ func packagePattern(modulePath string) string { func makeGOROOTCopy(t *testing.T) string { t.Helper() gorootCopyDir := t.TempDir() - err := filepath.Walk(runtime.GOROOT(), func(src string, info os.FileInfo, err error) error { + err := filepath.Walk(testenv.GOROOT(t), func(src string, info os.FileInfo, err error) error { if err != nil { return err } - if info.IsDir() && src == filepath.Join(runtime.GOROOT(), ".git") { + if info.IsDir() && src == filepath.Join(testenv.GOROOT(t), ".git") { return filepath.SkipDir } - rel, err := filepath.Rel(runtime.GOROOT(), src) + rel, err := filepath.Rel(testenv.GOROOT(t), src) if err != nil { - return fmt.Errorf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), src, err) + return fmt.Errorf("filepath.Rel(%q, %q): %v", testenv.GOROOT(t), src, err) } dst := filepath.Join(gorootCopyDir, rel) - if info.IsDir() && (src == filepath.Join(runtime.GOROOT(), "bin") || - src == filepath.Join(runtime.GOROOT(), "pkg")) { + if info.IsDir() && (src == filepath.Join(testenv.GOROOT(t), "bin") || + src == filepath.Join(testenv.GOROOT(t), "pkg")) { // If the OS supports symlinks, use them instead // of copying the bin and pkg directories. if err := os.Symlink(src, dst); err == nil { @@ -435,14 +435,14 @@ func findGorootModules(t *testing.T) []gorootModule { goBin := testenv.GoToolPath(t) goroot.once.Do(func() { - goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error { + goroot.err = filepath.WalkDir(testenv.GOROOT(t), func(path string, info fs.DirEntry, err error) error { if err != nil { return err } if info.IsDir() && (info.Name() == "vendor" || info.Name() == "testdata") { return filepath.SkipDir } - if info.IsDir() && path == filepath.Join(runtime.GOROOT(), "pkg") { + if info.IsDir() && path == filepath.Join(testenv.GOROOT(t), "pkg") { // GOROOT/pkg contains generated artifacts, not source code. // // In https://golang.org/issue/37929 it was observed to somehow contain @@ -464,7 +464,7 @@ func findGorootModules(t *testing.T) []gorootModule { // Use 'go list' to describe the module contained in this directory (but // not its dependencies). cmd := exec.Command(goBin, "list", "-json", "-m") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = dir cmd.Stderr = new(strings.Builder) out, err := cmd.Output() diff --git a/src/cmd/internal/notsha256/sha256.go b/src/cmd/internal/notsha256/sha256.go new file mode 100644 index 00000000000000..080b34497984c5 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256.go @@ -0,0 +1,141 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package notsha256 implements the NOTSHA256 algorithm, +// a hash defined as bitwise NOT of SHA256. +// It is used in situations where exact fidelity to SHA256 is unnecessary. +// In particular, it is used in the compiler toolchain, +// which cannot depend directly on cgo when GOEXPERIMENT=boringcrypto +// (and in that mode the real sha256 uses cgo). +package notsha256 + +import ( + "encoding/binary" + "hash" +) + +// The size of a checksum in bytes. +const Size = 32 + +// The blocksize in bytes. +const BlockSize = 64 + +const ( + chunk = 64 + init0 = 0x6A09E667 + init1 = 0xBB67AE85 + init2 = 0x3C6EF372 + init3 = 0xA54FF53A + init4 = 0x510E527F + init5 = 0x9B05688C + init6 = 0x1F83D9AB + init7 = 0x5BE0CD19 +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + h [8]uint32 + x [chunk]byte + nx int + len uint64 +} + +func (d *digest) Reset() { + d.h[0] = init0 + d.h[1] = init1 + d.h[2] = init2 + d.h[3] = init3 + d.h[4] = init4 + d.h[5] = init5 + d.h[6] = init6 + d.h[7] = init7 + d.nx = 0 + d.len = 0 +} + +// New returns a new hash.Hash computing the NOTSHA256 checksum. +// state of the hash. +func New() hash.Hash { + d := new(digest) + d.Reset() + return d +} + +func (d *digest) Size() int { + return Size +} + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { + nn = len(p) + d.len += uint64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx:], p) + d.nx += n + if d.nx == chunk { + block(d, d.x[:]) + d.nx = 0 + } + p = p[n:] + } + if len(p) >= chunk { + n := len(p) &^ (chunk - 1) + block(d, p[:n]) + p = p[n:] + } + if len(p) > 0 { + d.nx = copy(d.x[:], p) + } + return +} + +func (d *digest) Sum(in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + d0 := *d + hash := d0.checkSum() + return append(in, hash[:]...) +} + +func (d *digest) checkSum() [Size]byte { + len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + var tmp [64]byte + tmp[0] = 0x80 + if len%64 < 56 { + d.Write(tmp[0 : 56-len%64]) + } else { + d.Write(tmp[0 : 64+56-len%64]) + } + + // Length in bits. + len <<= 3 + binary.BigEndian.PutUint64(tmp[:], len) + d.Write(tmp[0:8]) + + if d.nx != 0 { + panic("d.nx != 0") + } + + var digest [Size]byte + + binary.BigEndian.PutUint32(digest[0:], d.h[0]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[4:], d.h[1]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[8:], d.h[2]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[12:], d.h[3]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[16:], d.h[4]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[20:], d.h[5]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[24:], d.h[6]^0xFFFFFFFF) + binary.BigEndian.PutUint32(digest[28:], d.h[7]^0xFFFFFFFF) + + return digest +} + +// Sum256 returns the SHA256 checksum of the data. +func Sum256(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() +} diff --git a/src/cmd/internal/notsha256/sha256_test.go b/src/cmd/internal/notsha256/sha256_test.go new file mode 100644 index 00000000000000..fa38e565068f3b --- /dev/null +++ b/src/cmd/internal/notsha256/sha256_test.go @@ -0,0 +1,175 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// SHA256 hash algorithm. See FIPS 180-2. + +package notsha256 + +import ( + "crypto/rand" + "fmt" + "io" + "strings" + "testing" +) + +type sha256Test struct { + out string + in string + unused string // marshal state, to keep table in sync with crypto/sha256 +} + +var golden = []sha256Test{ + {"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "", "sha\x03j\t\xe6g\xbbg\xae\x85 0 { + t.Errorf("allocs = %d, want 0", n) + } +} + +var bench = New() +var buf = make([]byte, 8192) + +func benchmarkSize(b *testing.B, size int) { + sum := make([]byte, bench.Size()) + b.Run("New", func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(int64(size)) + for i := 0; i < b.N; i++ { + bench.Reset() + bench.Write(buf[:size]) + bench.Sum(sum[:0]) + } + }) + b.Run("Sum256", func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(int64(size)) + for i := 0; i < b.N; i++ { + Sum256(buf[:size]) + } + }) +} + +func BenchmarkHash8Bytes(b *testing.B) { + benchmarkSize(b, 8) +} + +func BenchmarkHash1K(b *testing.B) { + benchmarkSize(b, 1024) +} + +func BenchmarkHash8K(b *testing.B) { + benchmarkSize(b, 8192) +} diff --git a/src/cmd/internal/notsha256/sha256block.go b/src/cmd/internal/notsha256/sha256block.go new file mode 100644 index 00000000000000..57cdf2efd55293 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block.go @@ -0,0 +1,128 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// SHA256 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package notsha256 + +import "math/bits" + +var _K = []uint32{ + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2, +} + +func blockGeneric(dig *digest, p []byte) { + var w [64]uint32 + h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] + for len(p) >= chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i := 0; i < 16; i++ { + j := i * 4 + w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) + } + for i := 16; i < 64; i++ { + v1 := w[i-2] + t1 := (bits.RotateLeft32(v1, -17)) ^ (bits.RotateLeft32(v1, -19)) ^ (v1 >> 10) + v2 := w[i-15] + t2 := (bits.RotateLeft32(v2, -7)) ^ (bits.RotateLeft32(v2, -18)) ^ (v2 >> 3) + w[i] = t1 + w[i-7] + t2 + w[i-16] + } + + a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7 + + for i := 0; i < 64; i++ { + t1 := h + ((bits.RotateLeft32(e, -6)) ^ (bits.RotateLeft32(e, -11)) ^ (bits.RotateLeft32(e, -25))) + ((e & f) ^ (^e & g)) + _K[i] + w[i] + + t2 := ((bits.RotateLeft32(a, -2)) ^ (bits.RotateLeft32(a, -13)) ^ (bits.RotateLeft32(a, -22))) + ((a & b) ^ (a & c) ^ (b & c)) + + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + h5 += f + h6 += g + h7 += h + + p = p[chunk:] + } + + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 +} diff --git a/src/cmd/internal/notsha256/sha256block_386.s b/src/cmd/internal/notsha256/sha256block_386.s new file mode 100644 index 00000000000000..f2ba7d7a9b7a5b --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_386.s @@ -0,0 +1,286 @@ +// 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. + +//go:build !purego +// +build !purego + +// SHA256 block routine. See sha256block.go for Go equivalent. +// +// The algorithm is detailed in FIPS 180-4: +// +// https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf +// +// Wt = Mt; for 0 <= t <= 15 +// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// +// a = H0 +// b = H1 +// c = H2 +// d = H3 +// e = H4 +// f = H5 +// g = H6 +// h = H7 +// +// for t = 0 to 63 { +// T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt +// T2 = BIGSIGMA0(a) + Maj(a,b,c) +// h = g +// g = f +// f = e +// e = d + T1 +// d = c +// c = b +// b = a +// a = T1 + T2 +// } +// +// H0 = a + H0 +// H1 = b + H1 +// H2 = c + H2 +// H3 = d + H3 +// H4 = e + H4 +// H5 = f + H5 +// H6 = g + H6 +// H7 = h + H7 + +// Wt = Mt; for 0 <= t <= 15 +#define MSGSCHEDULE0(index) \ + MOVL (index*4)(SI), AX; \ + BSWAPL AX; \ + MOVL AX, (index*4)(BP) + +// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x) +// SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x) +#define MSGSCHEDULE1(index) \ + MOVL ((index-2)*4)(BP), AX; \ + MOVL AX, CX; \ + RORL $17, AX; \ + MOVL CX, DX; \ + RORL $19, CX; \ + SHRL $10, DX; \ + MOVL ((index-15)*4)(BP), BX; \ + XORL CX, AX; \ + MOVL BX, CX; \ + XORL DX, AX; \ + RORL $7, BX; \ + MOVL CX, DX; \ + SHRL $3, DX; \ + RORL $18, CX; \ + ADDL ((index-7)*4)(BP), AX; \ + XORL CX, BX; \ + XORL DX, BX; \ + ADDL ((index-16)*4)(BP), BX; \ + ADDL BX, AX; \ + MOVL AX, ((index)*4)(BP) + +// Calculate T1 in AX - uses AX, BX, CX and DX registers. +// Wt is passed in AX. +// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt +// BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x) +// Ch(x, y, z) = (x AND y) XOR (NOT x AND z) +#define SHA256T1(const, e, f, g, h) \ + MOVL (h*4)(DI), BX; \ + ADDL AX, BX; \ + MOVL (e*4)(DI), AX; \ + ADDL $const, BX; \ + MOVL (e*4)(DI), CX; \ + RORL $6, AX; \ + MOVL (e*4)(DI), DX; \ + RORL $11, CX; \ + XORL CX, AX; \ + MOVL (e*4)(DI), CX; \ + RORL $25, DX; \ + ANDL (f*4)(DI), CX; \ + XORL AX, DX; \ + MOVL (e*4)(DI), AX; \ + NOTL AX; \ + ADDL DX, BX; \ + ANDL (g*4)(DI), AX; \ + XORL CX, AX; \ + ADDL BX, AX + +// Calculate T2 in BX - uses AX, BX, CX and DX registers. +// T2 = BIGSIGMA0(a) + Maj(a, b, c) +// BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x) +// Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) +#define SHA256T2(a, b, c) \ + MOVL (a*4)(DI), AX; \ + MOVL (c*4)(DI), BX; \ + RORL $2, AX; \ + MOVL (a*4)(DI), DX; \ + ANDL (b*4)(DI), BX; \ + RORL $13, DX; \ + MOVL (a*4)(DI), CX; \ + ANDL (c*4)(DI), CX; \ + XORL DX, AX; \ + XORL CX, BX; \ + MOVL (a*4)(DI), DX; \ + MOVL (b*4)(DI), CX; \ + RORL $22, DX; \ + ANDL (a*4)(DI), CX; \ + XORL CX, BX; \ + XORL DX, AX; \ + ADDL AX, BX + +// Calculate T1 and T2, then e = d + T1 and a = T1 + T2. +// The values for e and a are stored in d and h, ready for rotation. +#define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \ + SHA256T1(const, e, f, g, h); \ + MOVL AX, 292(SP); \ + SHA256T2(a, b, c); \ + MOVL 292(SP), AX; \ + ADDL AX, BX; \ + ADDL AX, (d*4)(DI); \ + MOVL BX, (h*4)(DI) + +#define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE0(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +#define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE1(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +TEXT ·block(SB),0,$296-16 + MOVL p_base+4(FP), SI + MOVL p_len+8(FP), DX + SHRL $6, DX + SHLL $6, DX + + LEAL (SI)(DX*1), DI + MOVL DI, 288(SP) + CMPL SI, DI + JEQ end + + LEAL 256(SP), DI // variables + + MOVL dig+0(FP), BP + MOVL (0*4)(BP), AX // a = H0 + MOVL AX, (0*4)(DI) + MOVL (1*4)(BP), BX // b = H1 + MOVL BX, (1*4)(DI) + MOVL (2*4)(BP), CX // c = H2 + MOVL CX, (2*4)(DI) + MOVL (3*4)(BP), DX // d = H3 + MOVL DX, (3*4)(DI) + MOVL (4*4)(BP), AX // e = H4 + MOVL AX, (4*4)(DI) + MOVL (5*4)(BP), BX // f = H5 + MOVL BX, (5*4)(DI) + MOVL (6*4)(BP), CX // g = H6 + MOVL CX, (6*4)(DI) + MOVL (7*4)(BP), DX // h = H7 + MOVL DX, (7*4)(DI) + +loop: + MOVL SP, BP // message schedule + + SHA256ROUND0(0, 0x428a2f98, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND0(1, 0x71374491, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND0(2, 0xb5c0fbcf, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND0(3, 0xe9b5dba5, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND0(4, 0x3956c25b, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND0(5, 0x59f111f1, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND0(6, 0x923f82a4, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND0(7, 0xab1c5ed5, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND0(8, 0xd807aa98, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND0(9, 0x12835b01, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND0(10, 0x243185be, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND0(11, 0x550c7dc3, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND0(12, 0x72be5d74, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND0(13, 0x80deb1fe, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND0(14, 0x9bdc06a7, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND0(15, 0xc19bf174, 1, 2, 3, 4, 5, 6, 7, 0) + + SHA256ROUND1(16, 0xe49b69c1, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(17, 0xefbe4786, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(18, 0x0fc19dc6, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(19, 0x240ca1cc, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(20, 0x2de92c6f, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(21, 0x4a7484aa, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(22, 0x5cb0a9dc, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(23, 0x76f988da, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(24, 0x983e5152, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(25, 0xa831c66d, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(26, 0xb00327c8, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(27, 0xbf597fc7, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(28, 0xc6e00bf3, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(29, 0xd5a79147, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(30, 0x06ca6351, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(31, 0x14292967, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(32, 0x27b70a85, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(33, 0x2e1b2138, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(34, 0x4d2c6dfc, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(35, 0x53380d13, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(36, 0x650a7354, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(37, 0x766a0abb, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(38, 0x81c2c92e, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(39, 0x92722c85, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(40, 0xa2bfe8a1, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(41, 0xa81a664b, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(42, 0xc24b8b70, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(43, 0xc76c51a3, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(44, 0xd192e819, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(45, 0xd6990624, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(46, 0xf40e3585, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(47, 0x106aa070, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(48, 0x19a4c116, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(49, 0x1e376c08, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(50, 0x2748774c, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(51, 0x34b0bcb5, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(52, 0x391c0cb3, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(53, 0x4ed8aa4a, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(54, 0x5b9cca4f, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(55, 0x682e6ff3, 1, 2, 3, 4, 5, 6, 7, 0) + SHA256ROUND1(56, 0x748f82ee, 0, 1, 2, 3, 4, 5, 6, 7) + SHA256ROUND1(57, 0x78a5636f, 7, 0, 1, 2, 3, 4, 5, 6) + SHA256ROUND1(58, 0x84c87814, 6, 7, 0, 1, 2, 3, 4, 5) + SHA256ROUND1(59, 0x8cc70208, 5, 6, 7, 0, 1, 2, 3, 4) + SHA256ROUND1(60, 0x90befffa, 4, 5, 6, 7, 0, 1, 2, 3) + SHA256ROUND1(61, 0xa4506ceb, 3, 4, 5, 6, 7, 0, 1, 2) + SHA256ROUND1(62, 0xbef9a3f7, 2, 3, 4, 5, 6, 7, 0, 1) + SHA256ROUND1(63, 0xc67178f2, 1, 2, 3, 4, 5, 6, 7, 0) + + MOVL dig+0(FP), BP + MOVL (0*4)(BP), AX // H0 = a + H0 + ADDL (0*4)(DI), AX + MOVL AX, (0*4)(DI) + MOVL AX, (0*4)(BP) + MOVL (1*4)(BP), BX // H1 = b + H1 + ADDL (1*4)(DI), BX + MOVL BX, (1*4)(DI) + MOVL BX, (1*4)(BP) + MOVL (2*4)(BP), CX // H2 = c + H2 + ADDL (2*4)(DI), CX + MOVL CX, (2*4)(DI) + MOVL CX, (2*4)(BP) + MOVL (3*4)(BP), DX // H3 = d + H3 + ADDL (3*4)(DI), DX + MOVL DX, (3*4)(DI) + MOVL DX, (3*4)(BP) + MOVL (4*4)(BP), AX // H4 = e + H4 + ADDL (4*4)(DI), AX + MOVL AX, (4*4)(DI) + MOVL AX, (4*4)(BP) + MOVL (5*4)(BP), BX // H5 = f + H5 + ADDL (5*4)(DI), BX + MOVL BX, (5*4)(DI) + MOVL BX, (5*4)(BP) + MOVL (6*4)(BP), CX // H6 = g + H6 + ADDL (6*4)(DI), CX + MOVL CX, (6*4)(DI) + MOVL CX, (6*4)(BP) + MOVL (7*4)(BP), DX // H7 = h + H7 + ADDL (7*4)(DI), DX + MOVL DX, (7*4)(DI) + MOVL DX, (7*4)(BP) + + ADDL $64, SI + CMPL SI, 288(SP) + JB loop + +end: + RET diff --git a/src/cmd/internal/notsha256/sha256block_amd64.go b/src/cmd/internal/notsha256/sha256block_amd64.go new file mode 100644 index 00000000000000..27b84a86b12979 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_amd64.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego +// +build !purego + +package notsha256 + +var useAVX2 = false diff --git a/src/cmd/internal/notsha256/sha256block_amd64.s b/src/cmd/internal/notsha256/sha256block_amd64.s new file mode 100644 index 00000000000000..36ea74451d15e7 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_amd64.s @@ -0,0 +1,427 @@ +// 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. + +//go:build !purego +// +build !purego + +#include "textflag.h" + +// SHA256 block routine. See sha256block.go for Go equivalent. +// +// The algorithm is detailed in FIPS 180-4: +// +// https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +// Wt = Mt; for 0 <= t <= 15 +// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// +// a = H0 +// b = H1 +// c = H2 +// d = H3 +// e = H4 +// f = H5 +// g = H6 +// h = H7 +// +// for t = 0 to 63 { +// T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt +// T2 = BIGSIGMA0(a) + Maj(a,b,c) +// h = g +// g = f +// f = e +// e = d + T1 +// d = c +// c = b +// b = a +// a = T1 + T2 +// } +// +// H0 = a + H0 +// H1 = b + H1 +// H2 = c + H2 +// H3 = d + H3 +// H4 = e + H4 +// H5 = f + H5 +// H6 = g + H6 +// H7 = h + H7 + +// Wt = Mt; for 0 <= t <= 15 +#define MSGSCHEDULE0(index) \ + MOVL (index*4)(SI), AX; \ + BSWAPL AX; \ + MOVL AX, (index*4)(BP) + +// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x) +// SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x) +#define MSGSCHEDULE1(index) \ + MOVL ((index-2)*4)(BP), AX; \ + MOVL AX, CX; \ + RORL $17, AX; \ + MOVL CX, DX; \ + RORL $19, CX; \ + SHRL $10, DX; \ + MOVL ((index-15)*4)(BP), BX; \ + XORL CX, AX; \ + MOVL BX, CX; \ + XORL DX, AX; \ + RORL $7, BX; \ + MOVL CX, DX; \ + SHRL $3, DX; \ + RORL $18, CX; \ + ADDL ((index-7)*4)(BP), AX; \ + XORL CX, BX; \ + XORL DX, BX; \ + ADDL ((index-16)*4)(BP), BX; \ + ADDL BX, AX; \ + MOVL AX, ((index)*4)(BP) + +// Calculate T1 in AX - uses AX, CX and DX registers. +// h is also used as an accumulator. Wt is passed in AX. +// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt +// BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x) +// Ch(x, y, z) = (x AND y) XOR (NOT x AND z) +#define SHA256T1(const, e, f, g, h) \ + ADDL AX, h; \ + MOVL e, AX; \ + ADDL $const, h; \ + MOVL e, CX; \ + RORL $6, AX; \ + MOVL e, DX; \ + RORL $11, CX; \ + XORL CX, AX; \ + MOVL e, CX; \ + RORL $25, DX; \ + ANDL f, CX; \ + XORL AX, DX; \ + MOVL e, AX; \ + NOTL AX; \ + ADDL DX, h; \ + ANDL g, AX; \ + XORL CX, AX; \ + ADDL h, AX + +// Calculate T2 in BX - uses BX, CX, DX and DI registers. +// T2 = BIGSIGMA0(a) + Maj(a, b, c) +// BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x) +// Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) +#define SHA256T2(a, b, c) \ + MOVL a, DI; \ + MOVL c, BX; \ + RORL $2, DI; \ + MOVL a, DX; \ + ANDL b, BX; \ + RORL $13, DX; \ + MOVL a, CX; \ + ANDL c, CX; \ + XORL DX, DI; \ + XORL CX, BX; \ + MOVL a, DX; \ + MOVL b, CX; \ + RORL $22, DX; \ + ANDL a, CX; \ + XORL CX, BX; \ + XORL DX, DI; \ + ADDL DI, BX + +// Calculate T1 and T2, then e = d + T1 and a = T1 + T2. +// The values for e and a are stored in d and h, ready for rotation. +#define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \ + SHA256T1(const, e, f, g, h); \ + SHA256T2(a, b, c); \ + MOVL BX, h; \ + ADDL AX, d; \ + ADDL AX, h + +#define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE0(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +#define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \ + MSGSCHEDULE1(index); \ + SHA256ROUND(index, const, a, b, c, d, e, f, g, h) + +TEXT ·block(SB), 0, $536-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 + +loop: + 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) + SHA256ROUND0(2, 0xb5c0fbcf, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND0(3, 0xe9b5dba5, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND0(4, 0x3956c25b, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND0(5, 0x59f111f1, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND0(6, 0x923f82a4, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND0(7, 0xab1c5ed5, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND0(8, 0xd807aa98, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND0(9, 0x12835b01, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND0(10, 0x243185be, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND0(11, 0x550c7dc3, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND0(12, 0x72be5d74, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND0(13, 0x80deb1fe, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND0(14, 0x9bdc06a7, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND0(15, 0xc19bf174, R9, R10, R11, R12, R13, R14, R15, R8) + + SHA256ROUND1(16, 0xe49b69c1, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(17, 0xefbe4786, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(18, 0x0fc19dc6, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(19, 0x240ca1cc, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(20, 0x2de92c6f, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(21, 0x4a7484aa, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(22, 0x5cb0a9dc, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(23, 0x76f988da, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(24, 0x983e5152, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(25, 0xa831c66d, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(26, 0xb00327c8, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(27, 0xbf597fc7, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(28, 0xc6e00bf3, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(29, 0xd5a79147, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(30, 0x06ca6351, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(31, 0x14292967, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(32, 0x27b70a85, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(33, 0x2e1b2138, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(34, 0x4d2c6dfc, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(35, 0x53380d13, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(36, 0x650a7354, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(37, 0x766a0abb, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(38, 0x81c2c92e, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(39, 0x92722c85, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(40, 0xa2bfe8a1, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(41, 0xa81a664b, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(42, 0xc24b8b70, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(43, 0xc76c51a3, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(44, 0xd192e819, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(45, 0xd6990624, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(46, 0xf40e3585, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(47, 0x106aa070, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(48, 0x19a4c116, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(49, 0x1e376c08, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(50, 0x2748774c, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(51, 0x34b0bcb5, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(52, 0x391c0cb3, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(53, 0x4ed8aa4a, R11, R12, R13, R14, R15, R8, R9, R10) + SHA256ROUND1(54, 0x5b9cca4f, R10, R11, R12, R13, R14, R15, R8, R9) + SHA256ROUND1(55, 0x682e6ff3, R9, R10, R11, R12, R13, R14, R15, R8) + SHA256ROUND1(56, 0x748f82ee, R8, R9, R10, R11, R12, R13, R14, R15) + SHA256ROUND1(57, 0x78a5636f, R15, R8, R9, R10, R11, R12, R13, R14) + SHA256ROUND1(58, 0x84c87814, R14, R15, R8, R9, R10, R11, R12, R13) + SHA256ROUND1(59, 0x8cc70208, R13, R14, R15, R8, R9, R10, R11, R12) + SHA256ROUND1(60, 0x90befffa, R12, R13, R14, R15, R8, R9, R10, R11) + SHA256ROUND1(61, 0xa4506ceb, R11, R12, R13, R14, R15, R8, R9, R10) + 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 + +end: + 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/cmd/internal/notsha256/sha256block_decl.go b/src/cmd/internal/notsha256/sha256block_decl.go new file mode 100644 index 00000000000000..631f1a4a1b046a --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_decl.go @@ -0,0 +1,13 @@ +// 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. + +//go:build !purego && (386 || amd64 || ppc64le || ppc64) +// +build !purego +// +build 386 amd64 ppc64le ppc64 + +package notsha256 + +//go:noescape + +func block(dig *digest, p []byte) diff --git a/src/cmd/internal/notsha256/sha256block_generic.go b/src/cmd/internal/notsha256/sha256block_generic.go new file mode 100644 index 00000000000000..2664722bc2f7a4 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_generic.go @@ -0,0 +1,12 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build purego || (!amd64 && !386 && !ppc64le && !ppc64) +// +build purego !amd64,!386,!ppc64le,!ppc64 + +package notsha256 + +func block(dig *digest, p []byte) { + blockGeneric(dig, p) +} diff --git a/src/cmd/internal/notsha256/sha256block_ppc64x.s b/src/cmd/internal/notsha256/sha256block_ppc64x.s new file mode 100644 index 00000000000000..e907d3b71beca7 --- /dev/null +++ b/src/cmd/internal/notsha256/sha256block_ppc64x.s @@ -0,0 +1,431 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// WARNING: this file is built by the bootstrap compiler, thus +// it must maintain compatibility with the oldest supported +// bootstrap toolchain. +// + +//go:build !purego && (ppc64 || ppc64le) +// +build !purego +// +build ppc64 ppc64le + +// Based on CRYPTOGAMS code with the following comment: +// # ==================================================================== +// # Written by Andy Polyakov for the OpenSSL +// # project. The module is, however, dual licensed under OpenSSL and +// # CRYPTOGAMS licenses depending on where you obtain it. For further +// # details see http://www.openssl.org/~appro/cryptogams/. +// # ==================================================================== + +#include "textflag.h" + +// SHA256 block routine. See sha256block.go for Go equivalent. +// +// The algorithm is detailed in FIPS 180-4: +// +// https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf +// +// Wt = Mt; for 0 <= t <= 15 +// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 +// +// a = H0 +// b = H1 +// c = H2 +// d = H3 +// e = H4 +// f = H5 +// g = H6 +// h = H7 +// +// for t = 0 to 63 { +// T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt +// T2 = BIGSIGMA0(a) + Maj(a,b,c) +// h = g +// g = f +// f = e +// e = d + T1 +// d = c +// c = b +// b = a +// a = T1 + T2 +// } +// +// H0 = a + H0 +// H1 = b + H1 +// H2 = c + H2 +// H3 = d + H3 +// H4 = e + H4 +// H5 = f + H5 +// H6 = g + H6 +// H7 = h + H7 + +#define CTX R3 +#define INP R4 +#define END R5 +#define TBL R6 +#define IDX R7 +#define LEN R9 +#define TEMP R12 + +#define HEX00 R0 +#define HEX10 R10 + +// V0-V7 are A-H +// V8-V23 are used for the message schedule +#define KI V24 +#define FUNC V25 +#define S0 V26 +#define S1 V27 +#define s0 V28 +#define s1 V29 +#define LEMASK V31 // Permutation control register for little endian + +// 4 copies of each Kt, to fill all 4 words of a vector register +DATA ·kcon+0x000(SB)/8, $0x428a2f98428a2f98 +DATA ·kcon+0x008(SB)/8, $0x428a2f98428a2f98 +DATA ·kcon+0x010(SB)/8, $0x7137449171374491 +DATA ·kcon+0x018(SB)/8, $0x7137449171374491 +DATA ·kcon+0x020(SB)/8, $0xb5c0fbcfb5c0fbcf +DATA ·kcon+0x028(SB)/8, $0xb5c0fbcfb5c0fbcf +DATA ·kcon+0x030(SB)/8, $0xe9b5dba5e9b5dba5 +DATA ·kcon+0x038(SB)/8, $0xe9b5dba5e9b5dba5 +DATA ·kcon+0x040(SB)/8, $0x3956c25b3956c25b +DATA ·kcon+0x048(SB)/8, $0x3956c25b3956c25b +DATA ·kcon+0x050(SB)/8, $0x59f111f159f111f1 +DATA ·kcon+0x058(SB)/8, $0x59f111f159f111f1 +DATA ·kcon+0x060(SB)/8, $0x923f82a4923f82a4 +DATA ·kcon+0x068(SB)/8, $0x923f82a4923f82a4 +DATA ·kcon+0x070(SB)/8, $0xab1c5ed5ab1c5ed5 +DATA ·kcon+0x078(SB)/8, $0xab1c5ed5ab1c5ed5 +DATA ·kcon+0x080(SB)/8, $0xd807aa98d807aa98 +DATA ·kcon+0x088(SB)/8, $0xd807aa98d807aa98 +DATA ·kcon+0x090(SB)/8, $0x12835b0112835b01 +DATA ·kcon+0x098(SB)/8, $0x12835b0112835b01 +DATA ·kcon+0x0A0(SB)/8, $0x243185be243185be +DATA ·kcon+0x0A8(SB)/8, $0x243185be243185be +DATA ·kcon+0x0B0(SB)/8, $0x550c7dc3550c7dc3 +DATA ·kcon+0x0B8(SB)/8, $0x550c7dc3550c7dc3 +DATA ·kcon+0x0C0(SB)/8, $0x72be5d7472be5d74 +DATA ·kcon+0x0C8(SB)/8, $0x72be5d7472be5d74 +DATA ·kcon+0x0D0(SB)/8, $0x80deb1fe80deb1fe +DATA ·kcon+0x0D8(SB)/8, $0x80deb1fe80deb1fe +DATA ·kcon+0x0E0(SB)/8, $0x9bdc06a79bdc06a7 +DATA ·kcon+0x0E8(SB)/8, $0x9bdc06a79bdc06a7 +DATA ·kcon+0x0F0(SB)/8, $0xc19bf174c19bf174 +DATA ·kcon+0x0F8(SB)/8, $0xc19bf174c19bf174 +DATA ·kcon+0x100(SB)/8, $0xe49b69c1e49b69c1 +DATA ·kcon+0x108(SB)/8, $0xe49b69c1e49b69c1 +DATA ·kcon+0x110(SB)/8, $0xefbe4786efbe4786 +DATA ·kcon+0x118(SB)/8, $0xefbe4786efbe4786 +DATA ·kcon+0x120(SB)/8, $0x0fc19dc60fc19dc6 +DATA ·kcon+0x128(SB)/8, $0x0fc19dc60fc19dc6 +DATA ·kcon+0x130(SB)/8, $0x240ca1cc240ca1cc +DATA ·kcon+0x138(SB)/8, $0x240ca1cc240ca1cc +DATA ·kcon+0x140(SB)/8, $0x2de92c6f2de92c6f +DATA ·kcon+0x148(SB)/8, $0x2de92c6f2de92c6f +DATA ·kcon+0x150(SB)/8, $0x4a7484aa4a7484aa +DATA ·kcon+0x158(SB)/8, $0x4a7484aa4a7484aa +DATA ·kcon+0x160(SB)/8, $0x5cb0a9dc5cb0a9dc +DATA ·kcon+0x168(SB)/8, $0x5cb0a9dc5cb0a9dc +DATA ·kcon+0x170(SB)/8, $0x76f988da76f988da +DATA ·kcon+0x178(SB)/8, $0x76f988da76f988da +DATA ·kcon+0x180(SB)/8, $0x983e5152983e5152 +DATA ·kcon+0x188(SB)/8, $0x983e5152983e5152 +DATA ·kcon+0x190(SB)/8, $0xa831c66da831c66d +DATA ·kcon+0x198(SB)/8, $0xa831c66da831c66d +DATA ·kcon+0x1A0(SB)/8, $0xb00327c8b00327c8 +DATA ·kcon+0x1A8(SB)/8, $0xb00327c8b00327c8 +DATA ·kcon+0x1B0(SB)/8, $0xbf597fc7bf597fc7 +DATA ·kcon+0x1B8(SB)/8, $0xbf597fc7bf597fc7 +DATA ·kcon+0x1C0(SB)/8, $0xc6e00bf3c6e00bf3 +DATA ·kcon+0x1C8(SB)/8, $0xc6e00bf3c6e00bf3 +DATA ·kcon+0x1D0(SB)/8, $0xd5a79147d5a79147 +DATA ·kcon+0x1D8(SB)/8, $0xd5a79147d5a79147 +DATA ·kcon+0x1E0(SB)/8, $0x06ca635106ca6351 +DATA ·kcon+0x1E8(SB)/8, $0x06ca635106ca6351 +DATA ·kcon+0x1F0(SB)/8, $0x1429296714292967 +DATA ·kcon+0x1F8(SB)/8, $0x1429296714292967 +DATA ·kcon+0x200(SB)/8, $0x27b70a8527b70a85 +DATA ·kcon+0x208(SB)/8, $0x27b70a8527b70a85 +DATA ·kcon+0x210(SB)/8, $0x2e1b21382e1b2138 +DATA ·kcon+0x218(SB)/8, $0x2e1b21382e1b2138 +DATA ·kcon+0x220(SB)/8, $0x4d2c6dfc4d2c6dfc +DATA ·kcon+0x228(SB)/8, $0x4d2c6dfc4d2c6dfc +DATA ·kcon+0x230(SB)/8, $0x53380d1353380d13 +DATA ·kcon+0x238(SB)/8, $0x53380d1353380d13 +DATA ·kcon+0x240(SB)/8, $0x650a7354650a7354 +DATA ·kcon+0x248(SB)/8, $0x650a7354650a7354 +DATA ·kcon+0x250(SB)/8, $0x766a0abb766a0abb +DATA ·kcon+0x258(SB)/8, $0x766a0abb766a0abb +DATA ·kcon+0x260(SB)/8, $0x81c2c92e81c2c92e +DATA ·kcon+0x268(SB)/8, $0x81c2c92e81c2c92e +DATA ·kcon+0x270(SB)/8, $0x92722c8592722c85 +DATA ·kcon+0x278(SB)/8, $0x92722c8592722c85 +DATA ·kcon+0x280(SB)/8, $0xa2bfe8a1a2bfe8a1 +DATA ·kcon+0x288(SB)/8, $0xa2bfe8a1a2bfe8a1 +DATA ·kcon+0x290(SB)/8, $0xa81a664ba81a664b +DATA ·kcon+0x298(SB)/8, $0xa81a664ba81a664b +DATA ·kcon+0x2A0(SB)/8, $0xc24b8b70c24b8b70 +DATA ·kcon+0x2A8(SB)/8, $0xc24b8b70c24b8b70 +DATA ·kcon+0x2B0(SB)/8, $0xc76c51a3c76c51a3 +DATA ·kcon+0x2B8(SB)/8, $0xc76c51a3c76c51a3 +DATA ·kcon+0x2C0(SB)/8, $0xd192e819d192e819 +DATA ·kcon+0x2C8(SB)/8, $0xd192e819d192e819 +DATA ·kcon+0x2D0(SB)/8, $0xd6990624d6990624 +DATA ·kcon+0x2D8(SB)/8, $0xd6990624d6990624 +DATA ·kcon+0x2E0(SB)/8, $0xf40e3585f40e3585 +DATA ·kcon+0x2E8(SB)/8, $0xf40e3585f40e3585 +DATA ·kcon+0x2F0(SB)/8, $0x106aa070106aa070 +DATA ·kcon+0x2F8(SB)/8, $0x106aa070106aa070 +DATA ·kcon+0x300(SB)/8, $0x19a4c11619a4c116 +DATA ·kcon+0x308(SB)/8, $0x19a4c11619a4c116 +DATA ·kcon+0x310(SB)/8, $0x1e376c081e376c08 +DATA ·kcon+0x318(SB)/8, $0x1e376c081e376c08 +DATA ·kcon+0x320(SB)/8, $0x2748774c2748774c +DATA ·kcon+0x328(SB)/8, $0x2748774c2748774c +DATA ·kcon+0x330(SB)/8, $0x34b0bcb534b0bcb5 +DATA ·kcon+0x338(SB)/8, $0x34b0bcb534b0bcb5 +DATA ·kcon+0x340(SB)/8, $0x391c0cb3391c0cb3 +DATA ·kcon+0x348(SB)/8, $0x391c0cb3391c0cb3 +DATA ·kcon+0x350(SB)/8, $0x4ed8aa4a4ed8aa4a +DATA ·kcon+0x358(SB)/8, $0x4ed8aa4a4ed8aa4a +DATA ·kcon+0x360(SB)/8, $0x5b9cca4f5b9cca4f +DATA ·kcon+0x368(SB)/8, $0x5b9cca4f5b9cca4f +DATA ·kcon+0x370(SB)/8, $0x682e6ff3682e6ff3 +DATA ·kcon+0x378(SB)/8, $0x682e6ff3682e6ff3 +DATA ·kcon+0x380(SB)/8, $0x748f82ee748f82ee +DATA ·kcon+0x388(SB)/8, $0x748f82ee748f82ee +DATA ·kcon+0x390(SB)/8, $0x78a5636f78a5636f +DATA ·kcon+0x398(SB)/8, $0x78a5636f78a5636f +DATA ·kcon+0x3A0(SB)/8, $0x84c8781484c87814 +DATA ·kcon+0x3A8(SB)/8, $0x84c8781484c87814 +DATA ·kcon+0x3B0(SB)/8, $0x8cc702088cc70208 +DATA ·kcon+0x3B8(SB)/8, $0x8cc702088cc70208 +DATA ·kcon+0x3C0(SB)/8, $0x90befffa90befffa +DATA ·kcon+0x3C8(SB)/8, $0x90befffa90befffa +DATA ·kcon+0x3D0(SB)/8, $0xa4506ceba4506ceb +DATA ·kcon+0x3D8(SB)/8, $0xa4506ceba4506ceb +DATA ·kcon+0x3E0(SB)/8, $0xbef9a3f7bef9a3f7 +DATA ·kcon+0x3E8(SB)/8, $0xbef9a3f7bef9a3f7 +DATA ·kcon+0x3F0(SB)/8, $0xc67178f2c67178f2 +DATA ·kcon+0x3F8(SB)/8, $0xc67178f2c67178f2 +DATA ·kcon+0x400(SB)/8, $0x0000000000000000 +DATA ·kcon+0x408(SB)/8, $0x0000000000000000 + +#ifdef GOARCH_ppc64le +DATA ·kcon+0x410(SB)/8, $0x1011121310111213 // permutation control vectors +DATA ·kcon+0x418(SB)/8, $0x1011121300010203 +DATA ·kcon+0x420(SB)/8, $0x1011121310111213 +DATA ·kcon+0x428(SB)/8, $0x0405060700010203 +DATA ·kcon+0x430(SB)/8, $0x1011121308090a0b +DATA ·kcon+0x438(SB)/8, $0x0405060700010203 +#else +DATA ·kcon+0x410(SB)/8, $0x1011121300010203 +DATA ·kcon+0x418(SB)/8, $0x1011121310111213 // permutation control vectors +DATA ·kcon+0x420(SB)/8, $0x0405060700010203 +DATA ·kcon+0x428(SB)/8, $0x1011121310111213 +DATA ·kcon+0x430(SB)/8, $0x0001020304050607 +DATA ·kcon+0x438(SB)/8, $0x08090a0b10111213 +#endif + +GLOBL ·kcon(SB), RODATA, $1088 + +#define SHA256ROUND0(a, b, c, d, e, f, g, h, xi) \ + VSEL g, f, e, FUNC; \ + VSHASIGMAW $15, e, $1, S1; \ + VADDUWM xi, h, h; \ + VSHASIGMAW $0, a, $1, S0; \ + VADDUWM FUNC, h, h; \ + VXOR b, a, FUNC; \ + VADDUWM S1, h, h; \ + VSEL b, c, FUNC, FUNC; \ + VADDUWM KI, g, g; \ + VADDUWM h, d, d; \ + VADDUWM FUNC, S0, S0; \ + LVX (TBL)(IDX), KI; \ + ADD $16, IDX; \ + VADDUWM S0, h, h + +#define SHA256ROUND1(a, b, c, d, e, f, g, h, xi, xj, xj_1, xj_9, xj_14) \ + VSHASIGMAW $0, xj_1, $0, s0; \ + VSEL g, f, e, FUNC; \ + VSHASIGMAW $15, e, $1, S1; \ + VADDUWM xi, h, h; \ + VSHASIGMAW $0, a, $1, S0; \ + VSHASIGMAW $15, xj_14, $0, s1; \ + VADDUWM FUNC, h, h; \ + VXOR b, a, FUNC; \ + VADDUWM xj_9, xj, xj; \ + VADDUWM S1, h, h; \ + VSEL b, c, FUNC, FUNC; \ + VADDUWM KI, g, g; \ + VADDUWM h, d, d; \ + VADDUWM FUNC, S0, S0; \ + VADDUWM s0, xj, xj; \ + LVX (TBL)(IDX), KI; \ + ADD $16, IDX; \ + VADDUWM S0, h, h; \ + VADDUWM s1, xj, xj + +#ifdef GOARCH_ppc64le +#define VPERMLE(va,vb,vc,vt) VPERM va, vb, vc, vt +#else +#define VPERMLE(va,vb,vc,vt) +#endif + +// func block(dig *digest, p []byte) +TEXT ·block(SB),0,$0-32 + MOVD dig+0(FP), CTX + MOVD p_base+8(FP), INP + MOVD p_len+16(FP), LEN + + SRD $6, LEN + SLD $6, LEN + ADD INP, LEN, END + + CMP INP, END + BEQ end + + MOVD $·kcon(SB), TBL + MOVWZ $0x10, HEX10 + MOVWZ $8, IDX + +#ifdef GOARCH_ppc64le + LVSL (IDX)(R0), LEMASK + VSPLTISB $0x0F, KI + VXOR KI, LEMASK, LEMASK +#endif + + LXVW4X (CTX)(HEX00), VS32 // v0 = vs32 + LXVW4X (CTX)(HEX10), VS36 // v4 = vs36 + + // unpack the input values into vector registers + VSLDOI $4, V0, V0, V1 + VSLDOI $8, V0, V0, V2 + VSLDOI $12, V0, V0, V3 + VSLDOI $4, V4, V4, V5 + VSLDOI $8, V4, V4, V6 + VSLDOI $12, V4, V4, V7 + +loop: + LVX (TBL)(HEX00), KI + MOVWZ $16, IDX + + LXVD2X (INP)(R0), VS40 // load v8 (=vs40) in advance + ADD $16, INP + + // Offload to VSR24-31 (aka FPR24-31) + XXLORQ VS32, VS32, VS24 + XXLORQ VS33, VS33, VS25 + XXLORQ VS34, VS34, VS26 + XXLORQ VS35, VS35, VS27 + XXLORQ VS36, VS36, VS28 + XXLORQ VS37, VS37, VS29 + XXLORQ VS38, VS38, VS30 + XXLORQ VS39, VS39, VS31 + + VADDUWM KI, V7, V7 // h+K[i] + LVX (TBL)(IDX), KI + ADD $16, IDX + + VPERMLE(V8, V8, LEMASK, V8) + SHA256ROUND0(V0, V1, V2, V3, V4, V5, V6, V7, V8) + VSLDOI $4, V8, V8, V9 + SHA256ROUND0(V7, V0, V1, V2, V3, V4, V5, V6, V9) + VSLDOI $4, V9, V9, V10 + SHA256ROUND0(V6, V7, V0, V1, V2, V3, V4, V5, V10) + LXVD2X (INP)(R0), VS44 // load v12 (=vs44) in advance + ADD $16, INP, INP + VSLDOI $4, V10, V10, V11 + SHA256ROUND0(V5, V6, V7, V0, V1, V2, V3, V4, V11) + VPERMLE(V12, V12, LEMASK, V12) + SHA256ROUND0(V4, V5, V6, V7, V0, V1, V2, V3, V12) + VSLDOI $4, V12, V12, V13 + SHA256ROUND0(V3, V4, V5, V6, V7, V0, V1, V2, V13) + VSLDOI $4, V13, V13, V14 + SHA256ROUND0(V2, V3, V4, V5, V6, V7, V0, V1, V14) + LXVD2X (INP)(R0), VS48 // load v16 (=vs48) in advance + ADD $16, INP, INP + VSLDOI $4, V14, V14, V15 + SHA256ROUND0(V1, V2, V3, V4, V5, V6, V7, V0, V15) + VPERMLE(V16, V16, LEMASK, V16) + SHA256ROUND0(V0, V1, V2, V3, V4, V5, V6, V7, V16) + VSLDOI $4, V16, V16, V17 + SHA256ROUND0(V7, V0, V1, V2, V3, V4, V5, V6, V17) + VSLDOI $4, V17, V17, V18 + SHA256ROUND0(V6, V7, V0, V1, V2, V3, V4, V5, V18) + VSLDOI $4, V18, V18, V19 + LXVD2X (INP)(R0), VS52 // load v20 (=vs52) in advance + ADD $16, INP, INP + SHA256ROUND0(V5, V6, V7, V0, V1, V2, V3, V4, V19) + VPERMLE(V20, V20, LEMASK, V20) + SHA256ROUND0(V4, V5, V6, V7, V0, V1, V2, V3, V20) + VSLDOI $4, V20, V20, V21 + SHA256ROUND0(V3, V4, V5, V6, V7, V0, V1, V2, V21) + VSLDOI $4, V21, V21, V22 + SHA256ROUND0(V2, V3, V4, V5, V6, V7, V0, V1, V22) + VSLDOI $4, V22, V22, V23 + SHA256ROUND1(V1, V2, V3, V4, V5, V6, V7, V0, V23, V8, V9, V17, V22) + + MOVWZ $3, TEMP + MOVWZ TEMP, CTR + +L16_xx: + SHA256ROUND1(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V18, V23) + SHA256ROUND1(V7, V0, V1, V2, V3, V4, V5, V6, V9, V10, V11, V19, V8) + SHA256ROUND1(V6, V7, V0, V1, V2, V3, V4, V5, V10, V11, V12, V20, V9) + SHA256ROUND1(V5, V6, V7, V0, V1, V2, V3, V4, V11, V12, V13, V21, V10) + SHA256ROUND1(V4, V5, V6, V7, V0, V1, V2, V3, V12, V13, V14, V22, V11) + SHA256ROUND1(V3, V4, V5, V6, V7, V0, V1, V2, V13, V14, V15, V23, V12) + SHA256ROUND1(V2, V3, V4, V5, V6, V7, V0, V1, V14, V15, V16, V8, V13) + SHA256ROUND1(V1, V2, V3, V4, V5, V6, V7, V0, V15, V16, V17, V9, V14) + SHA256ROUND1(V0, V1, V2, V3, V4, V5, V6, V7, V16, V17, V18, V10, V15) + SHA256ROUND1(V7, V0, V1, V2, V3, V4, V5, V6, V17, V18, V19, V11, V16) + SHA256ROUND1(V6, V7, V0, V1, V2, V3, V4, V5, V18, V19, V20, V12, V17) + SHA256ROUND1(V5, V6, V7, V0, V1, V2, V3, V4, V19, V20, V21, V13, V18) + SHA256ROUND1(V4, V5, V6, V7, V0, V1, V2, V3, V20, V21, V22, V14, V19) + SHA256ROUND1(V3, V4, V5, V6, V7, V0, V1, V2, V21, V22, V23, V15, V20) + SHA256ROUND1(V2, V3, V4, V5, V6, V7, V0, V1, V22, V23, V8, V16, V21) + SHA256ROUND1(V1, V2, V3, V4, V5, V6, V7, V0, V23, V8, V9, V17, V22) + + BC 0x10, 0, L16_xx // bdnz + + XXLORQ VS24, VS24, VS42 + + XXLORQ VS25, VS25, VS43 + VADDUWM V10, V0, V0 + XXLORQ VS26, VS26, VS44 + VADDUWM V11, V1, V1 + XXLORQ VS27, VS27, VS45 + VADDUWM V12, V2, V2 + XXLORQ VS28, VS28, VS46 + VADDUWM V13, V3, V3 + XXLORQ VS29, VS29, VS47 + VADDUWM V14, V4, V4 + XXLORQ VS30, VS30, VS48 + VADDUWM V15, V5, V5 + XXLORQ VS31, VS31, VS49 + VADDUWM V16, V6, V6 + VADDUWM V17, V7, V7 + + CMPU INP, END + BLT loop + + LVX (TBL)(IDX), V8 + ADD $16, IDX + VPERM V0, V1, KI, V0 + LVX (TBL)(IDX), V9 + VPERM V4, V5, KI, V4 + VPERM V0, V2, V8, V0 + VPERM V4, V6, V8, V4 + VPERM V0, V3, V9, V0 + VPERM V4, V7, V9, V4 + STXVD2X VS32, (CTX+HEX00) // v0 = vs32 + STXVD2X VS36, (CTX+HEX10) // v4 = vs36 + +end: + RET + diff --git a/src/cmd/internal/obj/addrtype_string.go b/src/cmd/internal/obj/addrtype_string.go index 71f0dd97a82484..e6277d39b01242 100644 --- a/src/cmd/internal/obj/addrtype_string.go +++ b/src/cmd/internal/obj/addrtype_string.go @@ -4,9 +4,30 @@ package obj import "strconv" -const _AddrType_name = "TYPE_NONETYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLIST" +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TYPE_NONE-0] + _ = x[TYPE_BRANCH-1] + _ = x[TYPE_TEXTSIZE-2] + _ = x[TYPE_MEM-3] + _ = x[TYPE_CONST-4] + _ = x[TYPE_FCONST-5] + _ = x[TYPE_SCONST-6] + _ = x[TYPE_REG-7] + _ = x[TYPE_ADDR-8] + _ = x[TYPE_SHIFT-9] + _ = x[TYPE_REGREG-10] + _ = x[TYPE_REGREG2-11] + _ = x[TYPE_INDIR-12] + _ = x[TYPE_REGLIST-13] + _ = x[TYPE_SPECIAL-14] +} + +const _AddrType_name = "TYPE_NONETYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLISTTYPE_SPECIAL" -var _AddrType_index = [...]uint8{0, 9, 20, 33, 41, 51, 62, 73, 81, 90, 100, 111, 123, 133, 145} +var _AddrType_index = [...]uint8{0, 9, 20, 33, 41, 51, 62, 73, 81, 90, 100, 111, 123, 133, 145, 157} func (i AddrType) String() string { if i >= AddrType(len(_AddrType_index)-1) { diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go index ccf5f9e7f8d959..7b1682776e5316 100644 --- a/src/cmd/internal/obj/arm/asm5.go +++ b/src/cmd/internal/obj/arm/asm5.go @@ -355,11 +355,10 @@ var oprange [ALAST & obj.AMask][]Optab var xcmp [C_GOK + 1][C_GOK + 1]bool var ( - deferreturn *obj.LSym - symdiv *obj.LSym - symdivu *obj.LSym - symmod *obj.LSym - symmodu *obj.LSym + symdiv *obj.LSym + symdivu *obj.LSym + symmod *obj.LSym + symmodu *obj.LSym ) // Note about encoding: Prog.scond holds the condition encoding, @@ -1219,8 +1218,6 @@ func buildop(ctxt *obj.Link) { return } - deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) - symdiv = ctxt.Lookup("runtime._div") symdivu = ctxt.Lookup("runtime._divu") symmod = ctxt.Lookup("runtime._mod") diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index 1454d8a7c92045..38aa11cde9845e 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -634,6 +634,61 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { + if c.ctxt.Flag_maymorestack != "" { + // Save LR and make room for REGCTXT. + const frameSize = 8 + // MOVW.W R14,$-8(SP) + p = obj.Appendp(p, c.newprog) + p.As = AMOVW + p.Scond |= C_WBIT + p.From.Type = obj.TYPE_REG + p.From.Reg = REGLINK + p.To.Type = obj.TYPE_MEM + p.To.Offset = -frameSize + p.To.Reg = REGSP + p.Spadj = frameSize + + // MOVW REGCTXT, 4(SP) + p = obj.Appendp(p, c.newprog) + p.As = AMOVW + p.From.Type = obj.TYPE_REG + p.From.Reg = REGCTXT + p.To.Type = obj.TYPE_MEM + p.To.Offset = 4 + p.To.Reg = REGSP + + // CALL maymorestack + p = obj.Appendp(p, c.newprog) + p.As = obj.ACALL + p.To.Type = obj.TYPE_BRANCH + // See ../x86/obj6.go + p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI()) + + // Restore REGCTXT and LR. + + // MOVW 4(SP), REGCTXT + p = obj.Appendp(p, c.newprog) + p.As = AMOVW + p.From.Type = obj.TYPE_MEM + p.From.Offset = 4 + p.From.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REGCTXT + + // MOVW.P 8(SP), R14 + p.As = AMOVW + p.Scond |= C_PBIT + p.From.Type = obj.TYPE_MEM + p.From.Offset = frameSize + p.From.Reg = REGSP + p.To.Type = obj.TYPE_REG + p.To.Reg = REGLINK + p.Spadj = -frameSize + } + + // Jump back to here after morestack returns. + startPred := p + // MOVW g_stackguard(g), R1 p = obj.Appendp(p, c.newprog) @@ -761,7 +816,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { b := obj.Appendp(pcdata, c.newprog) b.As = obj.AJMP b.To.Type = obj.TYPE_BRANCH - b.To.SetTarget(c.cursym.Func().Text.Link) + b.To.SetTarget(startPred.Link) b.Spadj = +framesize return end diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index bf75bb4a89156c..3bfc6759d80580 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -143,26 +143,6 @@ const ( REG_V30 REG_V31 - // The EQ in - // CSET EQ, R0 - // is encoded as TYPE_REG, even though it's not really a register. - COND_EQ - COND_NE - COND_HS - COND_LO - COND_MI - COND_PL - COND_VS - COND_VC - COND_HI - COND_LS - COND_GE - COND_LT - COND_GT - COND_LE - COND_AL - COND_NV - REG_RSP = REG_V31 + 32 // to differentiate ZR/SP, REG_RSP&0x1f = 31 ) @@ -197,28 +177,10 @@ const ( // a special register and the low bits select the register. // SYSREG_END is the last item in the automatically generated system register // declaration, and it is defined in the sysRegEnc.go file. +// Define the special register after REG_SPECIAL, the first value of it should be +// REG_{name} = SYSREG_END + iota. const ( REG_SPECIAL = obj.RBaseARM64 + 1<<12 - REG_DAIFSet = SYSREG_END + iota - REG_DAIFClr - REG_PLDL1KEEP - REG_PLDL1STRM - REG_PLDL2KEEP - REG_PLDL2STRM - REG_PLDL3KEEP - REG_PLDL3STRM - REG_PLIL1KEEP - REG_PLIL1STRM - REG_PLIL2KEEP - REG_PLIL2STRM - REG_PLIL3KEEP - REG_PLIL3STRM - REG_PSTL1KEEP - REG_PSTL1STRM - REG_PSTL2KEEP - REG_PSTL2STRM - REG_PSTL3KEEP - REG_PSTL3STRM ) // Register assignments: @@ -381,6 +343,7 @@ const ( // The more specific class needs to come earlier. C_NONE = iota C_REG // R0..R30 + C_ZREG // R0..R30, ZR C_RSP // R0..R30, RSP C_FREG // F0..F31 C_VREG // V0..V31 @@ -388,12 +351,13 @@ const ( C_SHIFT // Rn<<2 C_EXTREG // Rn.UXTB[<<3] C_SPR // REG_NZCV - C_COND // EQ, NE, etc + C_COND // condition code, EQ, NE, etc. + C_SPOP // special operand, PLDL1KEEP, VMALLE1IS, etc. C_ARNG // Vn. C_ELEM // Vn.[index] C_LIST // [V1, V2, V3] - C_ZCON // $0 or ZR + C_ZCON // $0 C_ABCON0 // could be C_ADDCON0 or C_BITCON C_ADDCON0 // 12-bit unsigned, unshifted C_ABCON // could be C_ADDCON or C_BITCON @@ -1046,6 +1010,7 @@ const ( AVBSL AVBIT AVTBL + AVTBX AVXAR AVZIP1 AVZIP2 @@ -1053,6 +1018,8 @@ const ( AVUADDW2 AVUADDW AVUSRA + AVTRN1 + AVTRN2 ALAST AB = obj.AJMP ABL = obj.ACALL @@ -1060,9 +1027,10 @@ const ( const ( // shift types - SHIFT_LL = 0 << 22 - SHIFT_LR = 1 << 22 - SHIFT_AR = 2 << 22 + SHIFT_LL = 0 << 22 + SHIFT_LR = 1 << 22 + SHIFT_AR = 2 << 22 + SHIFT_ROR = 3 << 22 ) // Arrangement for ARM64 SIMD instructions @@ -1082,3 +1050,164 @@ const ( ARNG_S ARNG_D ) + +//go:generate stringer -type SpecialOperand -trimprefix SPOP_ +type SpecialOperand int + +const ( + // PRFM + SPOP_PLDL1KEEP SpecialOperand = iota // must be the first one + SPOP_BEGIN SpecialOperand = iota - 1 // set as the lower bound + SPOP_PLDL1STRM + SPOP_PLDL2KEEP + SPOP_PLDL2STRM + SPOP_PLDL3KEEP + SPOP_PLDL3STRM + SPOP_PLIL1KEEP + SPOP_PLIL1STRM + SPOP_PLIL2KEEP + SPOP_PLIL2STRM + SPOP_PLIL3KEEP + SPOP_PLIL3STRM + SPOP_PSTL1KEEP + SPOP_PSTL1STRM + SPOP_PSTL2KEEP + SPOP_PSTL2STRM + SPOP_PSTL3KEEP + SPOP_PSTL3STRM + + // TLBI + SPOP_VMALLE1IS + SPOP_VAE1IS + SPOP_ASIDE1IS + SPOP_VAAE1IS + SPOP_VALE1IS + SPOP_VAALE1IS + SPOP_VMALLE1 + SPOP_VAE1 + SPOP_ASIDE1 + SPOP_VAAE1 + SPOP_VALE1 + SPOP_VAALE1 + SPOP_IPAS2E1IS + SPOP_IPAS2LE1IS + SPOP_ALLE2IS + SPOP_VAE2IS + SPOP_ALLE1IS + SPOP_VALE2IS + SPOP_VMALLS12E1IS + SPOP_IPAS2E1 + SPOP_IPAS2LE1 + SPOP_ALLE2 + SPOP_VAE2 + SPOP_ALLE1 + SPOP_VALE2 + SPOP_VMALLS12E1 + SPOP_ALLE3IS + SPOP_VAE3IS + SPOP_VALE3IS + SPOP_ALLE3 + SPOP_VAE3 + SPOP_VALE3 + SPOP_VMALLE1OS + SPOP_VAE1OS + SPOP_ASIDE1OS + SPOP_VAAE1OS + SPOP_VALE1OS + SPOP_VAALE1OS + SPOP_RVAE1IS + SPOP_RVAAE1IS + SPOP_RVALE1IS + SPOP_RVAALE1IS + SPOP_RVAE1OS + SPOP_RVAAE1OS + SPOP_RVALE1OS + SPOP_RVAALE1OS + SPOP_RVAE1 + SPOP_RVAAE1 + SPOP_RVALE1 + SPOP_RVAALE1 + SPOP_RIPAS2E1IS + SPOP_RIPAS2LE1IS + SPOP_ALLE2OS + SPOP_VAE2OS + SPOP_ALLE1OS + SPOP_VALE2OS + SPOP_VMALLS12E1OS + SPOP_RVAE2IS + SPOP_RVALE2IS + SPOP_IPAS2E1OS + SPOP_RIPAS2E1 + SPOP_RIPAS2E1OS + SPOP_IPAS2LE1OS + SPOP_RIPAS2LE1 + SPOP_RIPAS2LE1OS + SPOP_RVAE2OS + SPOP_RVALE2OS + SPOP_RVAE2 + SPOP_RVALE2 + SPOP_ALLE3OS + SPOP_VAE3OS + SPOP_VALE3OS + SPOP_RVAE3IS + SPOP_RVALE3IS + SPOP_RVAE3OS + SPOP_RVALE3OS + SPOP_RVAE3 + SPOP_RVALE3 + + // DC + SPOP_IVAC + SPOP_ISW + SPOP_CSW + SPOP_CISW + SPOP_ZVA + SPOP_CVAC + SPOP_CVAU + SPOP_CIVAC + SPOP_IGVAC + SPOP_IGSW + SPOP_IGDVAC + SPOP_IGDSW + SPOP_CGSW + SPOP_CGDSW + SPOP_CIGSW + SPOP_CIGDSW + SPOP_GVA + SPOP_GZVA + SPOP_CGVAC + SPOP_CGDVAC + SPOP_CGVAP + SPOP_CGDVAP + SPOP_CGVADP + SPOP_CGDVADP + SPOP_CIGVAC + SPOP_CIGDVAC + SPOP_CVAP + SPOP_CVADP + + // PSTATE fields + SPOP_DAIFSet + SPOP_DAIFClr + + // Condition code, EQ, NE, etc. Their relative order to EQ is matter. + SPOP_EQ + SPOP_NE + SPOP_HS + SPOP_LO + SPOP_MI + SPOP_PL + SPOP_VS + SPOP_VC + SPOP_HI + SPOP_LS + SPOP_GE + SPOP_LT + SPOP_GT + SPOP_LE + SPOP_AL + SPOP_NV + // Condition code end. + + SPOP_END +) diff --git a/src/cmd/internal/obj/arm64/anames.go b/src/cmd/internal/obj/arm64/anames.go index 9cc58716488ee0..03222f9c3773e5 100644 --- a/src/cmd/internal/obj/arm64/anames.go +++ b/src/cmd/internal/obj/arm64/anames.go @@ -530,6 +530,7 @@ var Anames = []string{ "VBSL", "VBIT", "VTBL", + "VTBX", "VXAR", "VZIP1", "VZIP2", @@ -537,5 +538,7 @@ var Anames = []string{ "VUADDW2", "VUADDW", "VUSRA", + "VTRN1", + "VTRN2", "LAST", } diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go index 2ecd8164b6b2cc..2f20dfe6442b29 100644 --- a/src/cmd/internal/obj/arm64/anames7.go +++ b/src/cmd/internal/obj/arm64/anames7.go @@ -8,6 +8,7 @@ package arm64 var cnames7 = []string{ "NONE", "REG", + "ZREG", "RSP", "FREG", "VREG", @@ -15,6 +16,7 @@ var cnames7 = []string{ "SHIFT", "EXTREG", "SPR", + "SPOP", "COND", "ARNG", "ELEM", diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index d99afa3d27606f..1e36985654dd3d 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -291,14 +291,14 @@ var optab = []Optab{ {obj.ATEXT, C_ADDR, C_NONE, C_NONE, C_TEXTSIZE, 0, 0, 0, 0, 0}, /* arithmetic operations */ - {AADD, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AADD, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AADC, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AADC, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {ANEG, C_REG, C_NONE, C_NONE, C_REG, 25, 4, 0, 0, 0}, - {ANEG, C_NONE, C_NONE, C_NONE, C_REG, 25, 4, 0, 0, 0}, - {ANGC, C_REG, C_NONE, C_NONE, C_REG, 17, 4, 0, 0, 0}, - {ACMP, C_REG, C_REG, C_NONE, C_NONE, 1, 4, 0, 0, 0}, + {AADD, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AADD, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AADC, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AADC, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {ANEG, C_ZREG, C_NONE, C_NONE, C_ZREG, 25, 4, 0, 0, 0}, + {ANEG, C_NONE, C_NONE, C_NONE, C_ZREG, 25, 4, 0, 0, 0}, + {ANGC, C_ZREG, C_NONE, C_NONE, C_ZREG, 17, 4, 0, 0, 0}, + {ACMP, C_ZREG, C_ZREG, C_NONE, C_NONE, 1, 4, 0, 0, 0}, {AADD, C_ADDCON, C_RSP, C_NONE, C_RSP, 2, 4, 0, 0, 0}, {AADD, C_ADDCON, C_NONE, C_NONE, C_RSP, 2, 4, 0, 0, 0}, {ACMP, C_ADDCON, C_RSP, C_NONE, C_NONE, 2, 4, 0, 0, 0}, @@ -316,32 +316,29 @@ var optab = []Optab{ {AADD, C_MOVCON3, C_NONE, C_NONE, C_RSP, 13, 16, 0, 0, 0}, {AADD, C_VCON, C_RSP, C_NONE, C_RSP, 13, 20, 0, 0, 0}, {AADD, C_VCON, C_NONE, C_NONE, C_RSP, 13, 20, 0, 0, 0}, - {ACMP, C_MOVCON2, C_REG, C_NONE, C_NONE, 13, 12, 0, 0, 0}, - {ACMP, C_MOVCON3, C_REG, C_NONE, C_NONE, 13, 16, 0, 0, 0}, - {ACMP, C_VCON, C_REG, C_NONE, C_NONE, 13, 20, 0, 0, 0}, - {AADD, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AADD, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AADD, C_SHIFT, C_RSP, C_NONE, C_RSP, 26, 4, 0, 0, 0}, - {AADD, C_SHIFT, C_NONE, C_NONE, C_RSP, 26, 4, 0, 0, 0}, - {AMVN, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {ACMP, C_SHIFT, C_REG, C_NONE, C_NONE, 3, 4, 0, 0, 0}, - {ACMP, C_SHIFT, C_RSP, C_NONE, C_NONE, 26, 4, 0, 0, 0}, - {ANEG, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AADD, C_REG, C_RSP, C_NONE, C_RSP, 27, 4, 0, 0, 0}, - {AADD, C_REG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0}, - {ACMP, C_REG, C_RSP, C_NONE, C_NONE, 27, 4, 0, 0, 0}, + {ACMP, C_MOVCON2, C_ZREG, C_NONE, C_NONE, 13, 12, 0, 0, 0}, + {ACMP, C_MOVCON3, C_ZREG, C_NONE, C_NONE, 13, 16, 0, 0, 0}, + {ACMP, C_VCON, C_ZREG, C_NONE, C_NONE, 13, 20, 0, 0, 0}, + {AADD, C_SHIFT, C_ZREG, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AADD, C_SHIFT, C_NONE, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AMVN, C_SHIFT, C_NONE, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {ACMP, C_SHIFT, C_ZREG, C_NONE, C_NONE, 3, 4, 0, 0, 0}, + {ANEG, C_SHIFT, C_NONE, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AADD, C_ZREG, C_RSP, C_NONE, C_RSP, 27, 4, 0, 0, 0}, + {AADD, C_ZREG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0}, + {ACMP, C_ZREG, C_RSP, C_NONE, C_NONE, 27, 4, 0, 0, 0}, {AADD, C_EXTREG, C_RSP, C_NONE, C_RSP, 27, 4, 0, 0, 0}, {AADD, C_EXTREG, C_NONE, C_NONE, C_RSP, 27, 4, 0, 0, 0}, {ACMP, C_EXTREG, C_RSP, C_NONE, C_NONE, 27, 4, 0, 0, 0}, - {AADD, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AADD, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AMUL, C_REG, C_REG, C_NONE, C_REG, 15, 4, 0, 0, 0}, - {AMUL, C_REG, C_NONE, C_NONE, C_REG, 15, 4, 0, 0, 0}, - {AMADD, C_REG, C_REG, C_REG, C_REG, 15, 4, 0, 0, 0}, - {AREM, C_REG, C_REG, C_NONE, C_REG, 16, 8, 0, 0, 0}, - {AREM, C_REG, C_NONE, C_NONE, C_REG, 16, 8, 0, 0, 0}, - {ASDIV, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {ASDIV, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, + {AADD, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AADD, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AMUL, C_ZREG, C_ZREG, C_NONE, C_ZREG, 15, 4, 0, 0, 0}, + {AMUL, C_ZREG, C_NONE, C_NONE, C_ZREG, 15, 4, 0, 0, 0}, + {AMADD, C_ZREG, C_ZREG, C_ZREG, C_ZREG, 15, 4, 0, 0, 0}, + {AREM, C_ZREG, C_ZREG, C_NONE, C_ZREG, 16, 8, 0, 0, 0}, + {AREM, C_ZREG, C_NONE, C_NONE, C_ZREG, 16, 8, 0, 0, 0}, + {ASDIV, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {ASDIV, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, {AFADDS, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0}, {AFADDS, C_FREG, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0}, @@ -355,67 +352,70 @@ var optab = []Optab{ {AVADDV, C_ARNG, C_NONE, C_NONE, C_VREG, 85, 4, 0, 0, 0}, /* logical operations */ - {AAND, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AAND, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AANDS, C_REG, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {AANDS, C_REG, C_NONE, C_NONE, C_REG, 1, 4, 0, 0, 0}, - {ATST, C_REG, C_REG, C_NONE, C_NONE, 1, 4, 0, 0, 0}, - {AAND, C_MBCON, C_REG, C_NONE, C_RSP, 53, 4, 0, 0, 0}, - {AAND, C_MBCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {AANDS, C_MBCON, C_REG, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {AANDS, C_MBCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {ATST, C_MBCON, C_REG, C_NONE, C_NONE, 53, 4, 0, 0, 0}, - {AAND, C_BITCON, C_REG, C_NONE, C_RSP, 53, 4, 0, 0, 0}, - {AAND, C_BITCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {AANDS, C_BITCON, C_REG, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {AANDS, C_BITCON, C_NONE, C_NONE, C_REG, 53, 4, 0, 0, 0}, - {ATST, C_BITCON, C_REG, C_NONE, C_NONE, 53, 4, 0, 0, 0}, - {AAND, C_MOVCON, C_REG, C_NONE, C_REG, 62, 8, 0, 0, 0}, - {AAND, C_MOVCON, C_NONE, C_NONE, C_REG, 62, 8, 0, 0, 0}, - {AANDS, C_MOVCON, C_REG, C_NONE, C_REG, 62, 8, 0, 0, 0}, - {AANDS, C_MOVCON, C_NONE, C_NONE, C_REG, 62, 8, 0, 0, 0}, - {ATST, C_MOVCON, C_REG, C_NONE, C_NONE, 62, 8, 0, 0, 0}, - {AAND, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0}, - {AAND, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0}, - {AAND, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0}, - {AAND, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0}, - {AAND, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0}, - {AAND, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0}, - {AANDS, C_MOVCON2, C_REG, C_NONE, C_REG, 28, 12, 0, 0, 0}, - {AANDS, C_MOVCON2, C_NONE, C_NONE, C_REG, 28, 12, 0, 0, 0}, - {AANDS, C_MOVCON3, C_REG, C_NONE, C_REG, 28, 16, 0, 0, 0}, - {AANDS, C_MOVCON3, C_NONE, C_NONE, C_REG, 28, 16, 0, 0, 0}, - {AANDS, C_VCON, C_REG, C_NONE, C_REG, 28, 20, 0, 0, 0}, - {AANDS, C_VCON, C_NONE, C_NONE, C_REG, 28, 20, 0, 0, 0}, - {ATST, C_MOVCON2, C_REG, C_NONE, C_NONE, 28, 12, 0, 0, 0}, - {ATST, C_MOVCON3, C_REG, C_NONE, C_NONE, 28, 16, 0, 0, 0}, - {ATST, C_VCON, C_REG, C_NONE, C_NONE, 28, 20, 0, 0, 0}, - {AAND, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AAND, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AANDS, C_SHIFT, C_REG, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {AANDS, C_SHIFT, C_NONE, C_NONE, C_REG, 3, 4, 0, 0, 0}, - {ATST, C_SHIFT, C_REG, C_NONE, C_NONE, 3, 4, 0, 0, 0}, + {AAND, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AAND, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AANDS, C_ZREG, C_ZREG, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {AANDS, C_ZREG, C_NONE, C_NONE, C_ZREG, 1, 4, 0, 0, 0}, + {ATST, C_ZREG, C_ZREG, C_NONE, C_NONE, 1, 4, 0, 0, 0}, + {AAND, C_MBCON, C_ZREG, C_NONE, C_RSP, 53, 4, 0, 0, 0}, + {AAND, C_MBCON, C_NONE, C_NONE, C_RSP, 53, 4, 0, 0, 0}, + {AANDS, C_MBCON, C_ZREG, C_NONE, C_ZREG, 53, 4, 0, 0, 0}, + {AANDS, C_MBCON, C_NONE, C_NONE, C_ZREG, 53, 4, 0, 0, 0}, + {ATST, C_MBCON, C_ZREG, C_NONE, C_NONE, 53, 4, 0, 0, 0}, + {AAND, C_BITCON, C_ZREG, C_NONE, C_RSP, 53, 4, 0, 0, 0}, + {AAND, C_BITCON, C_NONE, C_NONE, C_RSP, 53, 4, 0, 0, 0}, + {AANDS, C_BITCON, C_ZREG, C_NONE, C_ZREG, 53, 4, 0, 0, 0}, + {AANDS, C_BITCON, C_NONE, C_NONE, C_ZREG, 53, 4, 0, 0, 0}, + {ATST, C_BITCON, C_ZREG, C_NONE, C_NONE, 53, 4, 0, 0, 0}, + {AAND, C_MOVCON, C_ZREG, C_NONE, C_ZREG, 62, 8, 0, 0, 0}, + {AAND, C_MOVCON, C_NONE, C_NONE, C_ZREG, 62, 8, 0, 0, 0}, + {AANDS, C_MOVCON, C_ZREG, C_NONE, C_ZREG, 62, 8, 0, 0, 0}, + {AANDS, C_MOVCON, C_NONE, C_NONE, C_ZREG, 62, 8, 0, 0, 0}, + {ATST, C_MOVCON, C_ZREG, C_NONE, C_NONE, 62, 8, 0, 0, 0}, + {AAND, C_MOVCON2, C_ZREG, C_NONE, C_ZREG, 28, 12, 0, 0, 0}, + {AAND, C_MOVCON2, C_NONE, C_NONE, C_ZREG, 28, 12, 0, 0, 0}, + {AAND, C_MOVCON3, C_ZREG, C_NONE, C_ZREG, 28, 16, 0, 0, 0}, + {AAND, C_MOVCON3, C_NONE, C_NONE, C_ZREG, 28, 16, 0, 0, 0}, + {AAND, C_VCON, C_ZREG, C_NONE, C_ZREG, 28, 20, 0, 0, 0}, + {AAND, C_VCON, C_NONE, C_NONE, C_ZREG, 28, 20, 0, 0, 0}, + {AANDS, C_MOVCON2, C_ZREG, C_NONE, C_ZREG, 28, 12, 0, 0, 0}, + {AANDS, C_MOVCON2, C_NONE, C_NONE, C_ZREG, 28, 12, 0, 0, 0}, + {AANDS, C_MOVCON3, C_ZREG, C_NONE, C_ZREG, 28, 16, 0, 0, 0}, + {AANDS, C_MOVCON3, C_NONE, C_NONE, C_ZREG, 28, 16, 0, 0, 0}, + {AANDS, C_VCON, C_ZREG, C_NONE, C_ZREG, 28, 20, 0, 0, 0}, + {AANDS, C_VCON, C_NONE, C_NONE, C_ZREG, 28, 20, 0, 0, 0}, + {ATST, C_MOVCON2, C_ZREG, C_NONE, C_NONE, 28, 12, 0, 0, 0}, + {ATST, C_MOVCON3, C_ZREG, C_NONE, C_NONE, 28, 16, 0, 0, 0}, + {ATST, C_VCON, C_ZREG, C_NONE, C_NONE, 28, 20, 0, 0, 0}, + {AAND, C_SHIFT, C_ZREG, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AAND, C_SHIFT, C_NONE, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AANDS, C_SHIFT, C_ZREG, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {AANDS, C_SHIFT, C_NONE, C_NONE, C_ZREG, 3, 4, 0, 0, 0}, + {ATST, C_SHIFT, C_ZREG, C_NONE, C_NONE, 3, 4, 0, 0, 0}, {AMOVD, C_RSP, C_NONE, C_NONE, C_RSP, 24, 4, 0, 0, 0}, - {AMVN, C_REG, C_NONE, C_NONE, C_REG, 24, 4, 0, 0, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, /* also MOVHU */ - {AMOVW, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, /* also MOVWU */ + {AMOVD, C_ZREG, C_NONE, C_NONE, C_ZREG, 24, 4, 0, 0, 0}, + {AMVN, C_ZREG, C_NONE, C_NONE, C_ZREG, 24, 4, 0, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_ZREG, 45, 4, 0, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_ZREG, 45, 4, 0, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_ZREG, 45, 4, 0, 0, 0}, /* also MOVHU */ + {AMOVW, C_ZREG, C_NONE, C_NONE, C_ZREG, 45, 4, 0, 0, 0}, /* also MOVWU */ /* TODO: MVN C_SHIFT */ /* MOVs that become MOVK/MOVN/MOVZ/ADD/SUB/OR */ - {AMOVW, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0}, - {AMOVD, C_MOVCON, C_NONE, C_NONE, C_REG, 32, 4, 0, 0, 0}, + {AMOVW, C_MBCON, C_NONE, C_NONE, C_ZREG, 32, 4, 0, 0, 0}, + {AMOVD, C_MBCON, C_NONE, C_NONE, C_ZREG, 32, 4, 0, 0, 0}, + {AMOVW, C_MOVCON, C_NONE, C_NONE, C_ZREG, 32, 4, 0, 0, 0}, + {AMOVD, C_MOVCON, C_NONE, C_NONE, C_ZREG, 32, 4, 0, 0, 0}, {AMOVW, C_BITCON, C_NONE, C_NONE, C_RSP, 32, 4, 0, 0, 0}, {AMOVD, C_BITCON, C_NONE, C_NONE, C_RSP, 32, 4, 0, 0, 0}, - {AMOVW, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0}, - {AMOVD, C_MOVCON2, C_NONE, C_NONE, C_REG, 12, 8, 0, NOTUSETMP, 0}, - {AMOVD, C_MOVCON3, C_NONE, C_NONE, C_REG, 12, 12, 0, NOTUSETMP, 0}, - {AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, NOTUSETMP, 0}, + {AMOVW, C_MOVCON2, C_NONE, C_NONE, C_ZREG, 12, 8, 0, NOTUSETMP, 0}, + {AMOVD, C_MOVCON2, C_NONE, C_NONE, C_ZREG, 12, 8, 0, NOTUSETMP, 0}, + {AMOVD, C_MOVCON3, C_NONE, C_NONE, C_ZREG, 12, 12, 0, NOTUSETMP, 0}, + {AMOVD, C_VCON, C_NONE, C_NONE, C_ZREG, 12, 16, 0, NOTUSETMP, 0}, - {AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0}, + {AMOVK, C_VCON, C_NONE, C_NONE, C_ZREG, 33, 4, 0, 0, 0}, {AMOVD, C_AACON, C_NONE, C_NONE, C_RSP, 4, 4, REGFROM, 0, 0}, - {AMOVD, C_AACON2, C_NONE, C_NONE, C_RSP, 4, 8, REGFROM, 0, 0}, + {AMOVD, C_AACON2, C_NONE, C_NONE, C_RSP, 4, 8, REGFROM, NOTUSETMP, 0}, /* load long effective stack address (load int32 offset and add) */ {AMOVD, C_LACON, C_NONE, C_NONE, C_RSP, 34, 8, REGSP, LFROM, 0}, @@ -429,31 +429,31 @@ var optab = []Optab{ {AB, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, {ABL, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, {AB, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0}, - {ABL, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0}, - {ABL, C_REG, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0}, + {ABL, C_NONE, C_NONE, C_NONE, C_ZREG, 6, 4, 0, 0, 0}, + {ABL, C_ZREG, C_NONE, C_NONE, C_ZREG, 6, 4, 0, 0, 0}, {ABL, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0}, - {obj.ARET, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0}, + {obj.ARET, C_NONE, C_NONE, C_NONE, C_ZREG, 6, 4, 0, 0, 0}, {obj.ARET, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0}, {ABEQ, C_NONE, C_NONE, C_NONE, C_SBRA, 7, 4, 0, 0, 0}, - {ACBZ, C_REG, C_NONE, C_NONE, C_SBRA, 39, 4, 0, 0, 0}, - {ATBZ, C_VCON, C_REG, C_NONE, C_SBRA, 40, 4, 0, 0, 0}, + {ACBZ, C_ZREG, C_NONE, C_NONE, C_SBRA, 39, 4, 0, 0, 0}, + {ATBZ, C_VCON, C_ZREG, C_NONE, C_SBRA, 40, 4, 0, 0, 0}, {AERET, C_NONE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0}, // get a PC-relative address - {AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0}, - {AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0}, + {AADRP, C_SBRA, C_NONE, C_NONE, C_ZREG, 60, 4, 0, 0, 0}, + {AADR, C_SBRA, C_NONE, C_NONE, C_ZREG, 61, 4, 0, 0, 0}, {ACLREX, C_NONE, C_NONE, C_NONE, C_VCON, 38, 4, 0, 0, 0}, {ACLREX, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0, 0}, - {ABFM, C_VCON, C_REG, C_VCON, C_REG, 42, 4, 0, 0, 0}, - {ABFI, C_VCON, C_REG, C_VCON, C_REG, 43, 4, 0, 0, 0}, - {AEXTR, C_VCON, C_REG, C_REG, C_REG, 44, 4, 0, 0, 0}, - {ASXTB, C_REG, C_NONE, C_NONE, C_REG, 45, 4, 0, 0, 0}, - {ACLS, C_REG, C_NONE, C_NONE, C_REG, 46, 4, 0, 0, 0}, - {ALSL, C_VCON, C_REG, C_NONE, C_REG, 8, 4, 0, 0, 0}, - {ALSL, C_VCON, C_NONE, C_NONE, C_REG, 8, 4, 0, 0, 0}, - {ALSL, C_REG, C_NONE, C_NONE, C_REG, 9, 4, 0, 0, 0}, - {ALSL, C_REG, C_REG, C_NONE, C_REG, 9, 4, 0, 0, 0}, + {ABFM, C_VCON, C_ZREG, C_VCON, C_ZREG, 42, 4, 0, 0, 0}, + {ABFI, C_VCON, C_ZREG, C_VCON, C_ZREG, 43, 4, 0, 0, 0}, + {AEXTR, C_VCON, C_ZREG, C_ZREG, C_ZREG, 44, 4, 0, 0, 0}, + {ASXTB, C_ZREG, C_NONE, C_NONE, C_ZREG, 45, 4, 0, 0, 0}, + {ACLS, C_ZREG, C_NONE, C_NONE, C_ZREG, 46, 4, 0, 0, 0}, + {ALSL, C_VCON, C_ZREG, C_NONE, C_ZREG, 8, 4, 0, 0, 0}, + {ALSL, C_VCON, C_NONE, C_NONE, C_ZREG, 8, 4, 0, 0, 0}, + {ALSL, C_ZREG, C_NONE, C_NONE, C_ZREG, 9, 4, 0, 0, 0}, + {ALSL, C_ZREG, C_ZREG, C_NONE, C_ZREG, 9, 4, 0, 0, 0}, {ASVC, C_VCON, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0}, {ASVC, C_NONE, C_NONE, C_NONE, C_NONE, 10, 4, 0, 0, 0}, {ADWORD, C_NONE, C_NONE, C_NONE, C_VCON, 11, 8, 0, NOTUSETMP, 0}, @@ -463,21 +463,21 @@ var optab = []Optab{ {AWORD, C_NONE, C_NONE, C_NONE, C_LCON, 14, 4, 0, 0, 0}, {AWORD, C_NONE, C_NONE, C_NONE, C_LEXT, 14, 4, 0, 0, 0}, {AWORD, C_NONE, C_NONE, C_NONE, C_ADDR, 14, 4, 0, 0, 0}, - {AMOVW, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0}, - {AMOVD, C_VCONADDR, C_NONE, C_NONE, C_REG, 68, 8, 0, NOTUSETMP, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, - {AMOVB, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0}, - {AMOVBU, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0}, - {AMOVH, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0}, - {AMOVW, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0}, - {AMOVD, C_ADDR, C_NONE, C_NONE, C_REG, 65, 12, 0, 0, 0}, - {AMOVD, C_GOTADDR, C_NONE, C_NONE, C_REG, 71, 8, 0, 0, 0}, - {AMOVD, C_TLS_LE, C_NONE, C_NONE, C_REG, 69, 4, 0, 0, 0}, - {AMOVD, C_TLS_IE, C_NONE, C_NONE, C_REG, 70, 8, 0, 0, 0}, + {AMOVW, C_VCONADDR, C_NONE, C_NONE, C_ZREG, 68, 8, 0, NOTUSETMP, 0}, + {AMOVD, C_VCONADDR, C_NONE, C_NONE, C_ZREG, 68, 8, 0, NOTUSETMP, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, + {AMOVB, C_ADDR, C_NONE, C_NONE, C_ZREG, 65, 12, 0, 0, 0}, + {AMOVBU, C_ADDR, C_NONE, C_NONE, C_ZREG, 65, 12, 0, 0, 0}, + {AMOVH, C_ADDR, C_NONE, C_NONE, C_ZREG, 65, 12, 0, 0, 0}, + {AMOVW, C_ADDR, C_NONE, C_NONE, C_ZREG, 65, 12, 0, 0, 0}, + {AMOVD, C_ADDR, C_NONE, C_NONE, C_ZREG, 65, 12, 0, 0, 0}, + {AMOVD, C_GOTADDR, C_NONE, C_NONE, C_ZREG, 71, 8, 0, 0, 0}, + {AMOVD, C_TLS_LE, C_NONE, C_NONE, C_ZREG, 69, 4, 0, 0, 0}, + {AMOVD, C_TLS_IE, C_NONE, C_NONE, C_ZREG, 70, 8, 0, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_ADDR, 64, 12, 0, 0, 0}, {AFMOVS, C_ADDR, C_NONE, C_NONE, C_FREG, 65, 12, 0, 0, 0}, @@ -487,22 +487,22 @@ var optab = []Optab{ {AFMOVS, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0}, {AFMOVD, C_FCON, C_NONE, C_NONE, C_FREG, 55, 4, 0, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_FREG, 54, 4, 0, 0, 0}, - {AFMOVS, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, - {AFMOVS, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0}, - {AFMOVD, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, - {AFMOVD, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0}, - {AFCVTZSD, C_FREG, C_NONE, C_NONE, C_REG, 29, 4, 0, 0, 0}, - {ASCVTFD, C_REG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, + {AFMOVS, C_ZREG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, + {AFMOVS, C_FREG, C_NONE, C_NONE, C_ZREG, 29, 4, 0, 0, 0}, + {AFMOVD, C_ZREG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, + {AFMOVD, C_FREG, C_NONE, C_NONE, C_ZREG, 29, 4, 0, 0, 0}, + {AFCVTZSD, C_FREG, C_NONE, C_NONE, C_ZREG, 29, 4, 0, 0, 0}, + {ASCVTFD, C_ZREG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, {AFCVTSD, C_FREG, C_NONE, C_NONE, C_FREG, 29, 4, 0, 0, 0}, - {AVMOV, C_ELEM, C_NONE, C_NONE, C_REG, 73, 4, 0, 0, 0}, + {AVMOV, C_ELEM, C_NONE, C_NONE, C_ZREG, 73, 4, 0, 0, 0}, {AVMOV, C_ELEM, C_NONE, C_NONE, C_ELEM, 92, 4, 0, 0, 0}, {AVMOV, C_ELEM, C_NONE, C_NONE, C_VREG, 80, 4, 0, 0, 0}, - {AVMOV, C_REG, C_NONE, C_NONE, C_ARNG, 82, 4, 0, 0, 0}, - {AVMOV, C_REG, C_NONE, C_NONE, C_ELEM, 78, 4, 0, 0, 0}, + {AVMOV, C_ZREG, C_NONE, C_NONE, C_ARNG, 82, 4, 0, 0, 0}, + {AVMOV, C_ZREG, C_NONE, C_NONE, C_ELEM, 78, 4, 0, 0, 0}, {AVMOV, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0}, {AVDUP, C_ELEM, C_NONE, C_NONE, C_ARNG, 79, 4, 0, 0, 0}, {AVDUP, C_ELEM, C_NONE, C_NONE, C_VREG, 80, 4, 0, 0, 0}, - {AVDUP, C_REG, C_NONE, C_NONE, C_ARNG, 82, 4, 0, 0, 0}, + {AVDUP, C_ZREG, C_NONE, C_NONE, C_ARNG, 82, 4, 0, 0, 0}, {AVMOVI, C_ADDCON, C_NONE, C_NONE, C_ARNG, 86, 4, 0, 0, 0}, {AVFMLA, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0}, {AVEXT, C_VCON, C_ARNG, C_ARNG, C_ARNG, 94, 4, 0, 0, 0}, @@ -514,25 +514,25 @@ var optab = []Optab{ {AVUADDW, C_ARNG, C_ARNG, C_NONE, C_ARNG, 105, 4, 0, 0, 0}, /* conditional operations */ - {ACSEL, C_COND, C_REG, C_REG, C_REG, 18, 4, 0, 0, 0}, - {ACINC, C_COND, C_REG, C_NONE, C_REG, 18, 4, 0, 0, 0}, - {ACSET, C_COND, C_NONE, C_NONE, C_REG, 18, 4, 0, 0, 0}, + {ACSEL, C_COND, C_ZREG, C_ZREG, C_ZREG, 18, 4, 0, 0, 0}, + {ACINC, C_COND, C_ZREG, C_NONE, C_ZREG, 18, 4, 0, 0, 0}, + {ACSET, C_COND, C_NONE, C_NONE, C_ZREG, 18, 4, 0, 0, 0}, {AFCSELD, C_COND, C_FREG, C_FREG, C_FREG, 18, 4, 0, 0, 0}, - {ACCMN, C_COND, C_REG, C_REG, C_VCON, 19, 4, 0, 0, 0}, - {ACCMN, C_COND, C_REG, C_VCON, C_VCON, 19, 4, 0, 0, 0}, + {ACCMN, C_COND, C_ZREG, C_ZREG, C_VCON, 19, 4, 0, 0, 0}, + {ACCMN, C_COND, C_ZREG, C_VCON, C_VCON, 19, 4, 0, 0, 0}, {AFCCMPS, C_COND, C_FREG, C_FREG, C_VCON, 57, 4, 0, 0, 0}, /* scaled 12-bit unsigned displacement store */ - {AMOVB, C_REG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_UAUTO8K, 20, 4, REGSP, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_UOREG8K, 20, 4, 0, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_UAUTO16K, 20, 4, REGSP, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_UAUTO32K, 20, 4, REGSP, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_UOREG32K, 20, 4, 0, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_UAUTO4K, 20, 4, REGSP, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_UOREG4K, 20, 4, 0, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_UAUTO8K, 20, 4, REGSP, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_UOREG8K, 20, 4, 0, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_UAUTO16K, 20, 4, REGSP, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_UAUTO32K, 20, 4, REGSP, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_UOREG32K, 20, 4, 0, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_UAUTO16K, 20, 4, REGSP, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0}, @@ -542,16 +542,16 @@ var optab = []Optab{ {AFMOVQ, C_FREG, C_NONE, C_NONE, C_UOREG64K, 20, 4, 0, 0, 0}, /* unscaled 9-bit signed displacement store */ - {AMOVB, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, @@ -561,16 +561,16 @@ var optab = []Optab{ {AFMOVQ, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, /* scaled 12-bit unsigned displacement load */ - {AMOVB, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVB, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVBU, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVBU, C_UOREG4K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVH, C_UAUTO8K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVH, C_UOREG8K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVW, C_UAUTO16K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVW, C_UOREG16K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVD, C_UAUTO32K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVD, C_UOREG32K, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, + {AMOVB, C_UAUTO4K, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVB, C_UOREG4K, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVBU, C_UAUTO4K, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVBU, C_UOREG4K, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVH, C_UAUTO8K, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVH, C_UOREG8K, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVW, C_UAUTO16K, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVW, C_UOREG16K, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVD, C_UAUTO32K, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVD, C_UOREG32K, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, {AFMOVS, C_UAUTO16K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVS, C_UOREG16K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, @@ -580,16 +580,16 @@ var optab = []Optab{ {AFMOVQ, C_UOREG64K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, /* unscaled 9-bit signed displacement load */ - {AMOVB, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVB, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVBU, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVBU, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVH, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVH, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVW, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVW, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, - {AMOVD, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, - {AMOVD, C_NSOREG, C_NONE, C_NONE, C_REG, 21, 4, 0, 0, 0}, + {AMOVB, C_NSAUTO, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVB, C_NSOREG, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVBU, C_NSAUTO, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVBU, C_NSOREG, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVH, C_NSAUTO, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVH, C_NSOREG, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVW, C_NSAUTO, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVW, C_NSOREG, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, + {AMOVD, C_NSAUTO, C_NONE, C_NONE, C_ZREG, 21, 4, REGSP, 0, 0}, + {AMOVD, C_NSOREG, C_NONE, C_NONE, C_ZREG, 21, 4, 0, 0, 0}, {AFMOVS, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVS, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, @@ -599,16 +599,16 @@ var optab = []Optab{ {AFMOVQ, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, /* long displacement store */ - {AMOVB, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, @@ -618,16 +618,16 @@ var optab = []Optab{ {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, /* long displacement load */ - {AMOVB, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, - {AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, - {AMOVBU, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, - {AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, - {AMOVH, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, - {AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, - {AMOVW, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, - {AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, - {AMOVD, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, - {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 31, 8, 0, LFROM, 0}, + {AMOVB, C_LAUTO, C_NONE, C_NONE, C_ZREG, 31, 8, REGSP, LFROM, 0}, + {AMOVB, C_LOREG, C_NONE, C_NONE, C_ZREG, 31, 8, 0, LFROM, 0}, + {AMOVBU, C_LAUTO, C_NONE, C_NONE, C_ZREG, 31, 8, REGSP, LFROM, 0}, + {AMOVBU, C_LOREG, C_NONE, C_NONE, C_ZREG, 31, 8, 0, LFROM, 0}, + {AMOVH, C_LAUTO, C_NONE, C_NONE, C_ZREG, 31, 8, REGSP, LFROM, 0}, + {AMOVH, C_LOREG, C_NONE, C_NONE, C_ZREG, 31, 8, 0, LFROM, 0}, + {AMOVW, C_LAUTO, C_NONE, C_NONE, C_ZREG, 31, 8, REGSP, LFROM, 0}, + {AMOVW, C_LOREG, C_NONE, C_NONE, C_ZREG, 31, 8, 0, LFROM, 0}, + {AMOVD, C_LAUTO, C_NONE, C_NONE, C_ZREG, 31, 8, REGSP, LFROM, 0}, + {AMOVD, C_LOREG, C_NONE, C_NONE, C_ZREG, 31, 8, 0, LFROM, 0}, {AFMOVS, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0}, {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, @@ -637,68 +637,67 @@ var optab = []Optab{ {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, /* pre/post-indexed load (unscaled, signed 9-bit offset) */ - {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, - {AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, - {AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, - {AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, - {AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, + {AMOVD, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPOST}, + {AMOVW, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPOST}, + {AMOVH, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPOST}, + {AMOVB, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPOST}, + {AMOVBU, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPOST}, {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, - {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, - {AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, - {AMOVH, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, - {AMOVB, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, - {AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, + {AMOVD, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPRE}, + {AMOVW, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPRE}, + {AMOVH, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPRE}, + {AMOVB, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPRE}, + {AMOVBU, C_LOREG, C_NONE, C_NONE, C_ZREG, 22, 4, 0, 0, C_XPRE}, {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, /* pre/post-indexed store (unscaled, signed 9-bit offset) */ - {AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, - {AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, - {AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, - {AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, - {AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, - {AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, - {AMOVH, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, - {AMOVB, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, - {AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AMOVBU, C_ZREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, /* load with shifted or extended register offset */ - {AMOVD, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, - {AMOVW, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, - {AMOVH, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, - {AMOVB, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, - {AMOVBU, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, + {AMOVD, C_ROFF, C_NONE, C_NONE, C_ZREG, 98, 4, 0, 0, 0}, + {AMOVW, C_ROFF, C_NONE, C_NONE, C_ZREG, 98, 4, 0, 0, 0}, + {AMOVH, C_ROFF, C_NONE, C_NONE, C_ZREG, 98, 4, 0, 0, 0}, + {AMOVB, C_ROFF, C_NONE, C_NONE, C_ZREG, 98, 4, 0, 0, 0}, + {AMOVBU, C_ROFF, C_NONE, C_NONE, C_ZREG, 98, 4, 0, 0, 0}, {AFMOVS, C_ROFF, C_NONE, C_NONE, C_FREG, 98, 4, 0, 0, 0}, {AFMOVD, C_ROFF, C_NONE, C_NONE, C_FREG, 98, 4, 0, 0, 0}, /* store with extended register offset */ - {AMOVD, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, - {AMOVW, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, - {AMOVH, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, - {AMOVB, C_REG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, + {AMOVW, C_ZREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, + {AMOVH, C_ZREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, + {AMOVB, C_ZREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_ROFF, 99, 4, 0, 0, 0}, /* pre/post-indexed/signed-offset load/store register pair - (unscaled, signed 10-bit quad-aligned and long offset) */ + (unscaled, signed 10-bit quad-aligned and long offset). + The pre/post-indexed format only supports OREG cases because + the RSP and pseudo registers are not allowed to be modified + in this way. */ {AFLDPQ, C_NQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {AFLDPQ, C_NQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {AFLDPQ, C_NQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {AFLDPQ, C_PQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {AFLDPQ, C_PQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {AFLDPQ, C_PQAUTO_16, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {AFLDPQ, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {AFLDPQ, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {AFLDPQ, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, 0}, @@ -714,11 +713,7 @@ var optab = []Optab{ {AFLDPQ, C_ADDR, C_NONE, C_NONE, C_PAIR, 88, 12, 0, 0, 0}, {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_NQAUTO_16, 67, 4, REGSP, 0, 0}, - {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_NQAUTO_16, 67, 4, REGSP, 0, C_XPRE}, - {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_NQAUTO_16, 67, 4, REGSP, 0, C_XPOST}, {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_PQAUTO_16, 67, 4, REGSP, 0, 0}, - {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_PQAUTO_16, 67, 4, REGSP, 0, C_XPRE}, - {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_PQAUTO_16, 67, 4, REGSP, 0, C_XPOST}, {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, 0}, {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 8, REGSP, 0, 0}, {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, 0}, @@ -734,11 +729,7 @@ var optab = []Optab{ {AFSTPQ, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0}, {ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {ALDP, C_NPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {ALDP, C_PPAUTO, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {ALDP, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {ALDP, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {ALDP, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, 0}, @@ -754,11 +745,7 @@ var optab = []Optab{ {ALDP, C_ADDR, C_NONE, C_NONE, C_PAIR, 88, 12, 0, 0, 0}, {ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, 0}, - {ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, C_XPRE}, - {ASTP, C_PAIR, C_NONE, C_NONE, C_NPAUTO, 67, 4, REGSP, 0, C_XPOST}, {ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, 0}, - {ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, C_XPRE}, - {ASTP, C_PAIR, C_NONE, C_NONE, C_PPAUTO, 67, 4, REGSP, 0, C_XPOST}, {ASTP, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, 0}, {ASTP, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 8, REGSP, 0, 0}, {ASTP, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, 0}, @@ -775,11 +762,7 @@ var optab = []Optab{ // differ from LDP/STP for C_NSAUTO_4/C_PSAUTO_4/C_NSOREG_4/C_PSOREG_4 {ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {ALDPW, C_NSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0}, - {ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPRE}, - {ALDPW, C_PSAUTO_4, C_NONE, C_NONE, C_PAIR, 66, 4, REGSP, 0, C_XPOST}, {ALDPW, C_UAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {ALDPW, C_NAUTO4K, C_NONE, C_NONE, C_PAIR, 74, 8, REGSP, 0, 0}, {ALDPW, C_LAUTO, C_NONE, C_NONE, C_PAIR, 75, 12, REGSP, LFROM, 0}, @@ -795,11 +778,7 @@ var optab = []Optab{ {ALDPW, C_ADDR, C_NONE, C_NONE, C_PAIR, 88, 12, 0, 0, 0}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, 0}, - {ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, C_XPRE}, - {ASTPW, C_PAIR, C_NONE, C_NONE, C_NSAUTO_4, 67, 4, REGSP, 0, C_XPOST}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, 0}, - {ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, C_XPRE}, - {ASTPW, C_PAIR, C_NONE, C_NONE, C_PSAUTO_4, 67, 4, REGSP, 0, C_XPOST}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_UAUTO4K, 76, 8, REGSP, 0, 0}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_NAUTO4K, 76, 8, REGSP, 0, 0}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_LAUTO, 77, 12, REGSP, LTO, 0}, @@ -814,17 +793,17 @@ var optab = []Optab{ {ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, 0}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0}, - {ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG - {ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG + {ASWPD, C_ZREG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG + {ASWPD, C_ZREG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZOREG, 106, 4, 0, 0, 0}, // RegTo2=C_REGREG {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZAUTO, 106, 4, REGSP, 0, 0}, // RegTo2=C_REGREG - {ALDAR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, - {ALDXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, - {ALDAXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, + {ALDAR, C_ZOREG, C_NONE, C_NONE, C_ZREG, 58, 4, 0, 0, 0}, + {ALDXR, C_ZOREG, C_NONE, C_NONE, C_ZREG, 58, 4, 0, 0, 0}, + {ALDAXR, C_ZOREG, C_NONE, C_NONE, C_ZREG, 58, 4, 0, 0, 0}, {ALDXP, C_ZOREG, C_NONE, C_NONE, C_PAIR, 58, 4, 0, 0, 0}, - {ASTLR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_NONE - {ASTXR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG - {ASTLXR, C_REG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG + {ASTLR, C_ZREG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_NONE + {ASTXR, C_ZREG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG + {ASTLXR, C_ZREG, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, // RegTo2=C_REG {ASTXP, C_PAIR, C_NONE, C_NONE, C_ZOREG, 59, 4, 0, 0, 0}, /* VLD[1-4]/VST[1-4] */ @@ -854,24 +833,27 @@ var optab = []Optab{ {AVST1, C_ELEM, C_NONE, C_NONE, C_LOREG, 96, 4, 0, 0, 0}, /* special */ - {AMOVD, C_SPR, C_NONE, C_NONE, C_REG, 35, 4, 0, 0, 0}, - {AMRS, C_SPR, C_NONE, C_NONE, C_REG, 35, 4, 0, 0, 0}, - {AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0}, - {AMSR, C_REG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0}, + {AMOVD, C_SPR, C_NONE, C_NONE, C_ZREG, 35, 4, 0, 0, 0}, + {AMRS, C_SPR, C_NONE, C_NONE, C_ZREG, 35, 4, 0, 0, 0}, + {AMOVD, C_ZREG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0}, + {AMSR, C_ZREG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0}, {AMOVD, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0}, {AMSR, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0}, - {APRFM, C_UOREG32K, C_NONE, C_NONE, C_SPR, 91, 4, 0, 0, 0}, + {AMSR, C_VCON, C_NONE, C_NONE, C_SPOP, 37, 4, 0, 0, 0}, + {APRFM, C_UOREG32K, C_NONE, C_NONE, C_SPOP, 91, 4, 0, 0, 0}, {APRFM, C_UOREG32K, C_NONE, C_NONE, C_LCON, 91, 4, 0, 0, 0}, {ADMB, C_VCON, C_NONE, C_NONE, C_NONE, 51, 4, 0, 0, 0}, {AHINT, C_VCON, C_NONE, C_NONE, C_NONE, 52, 4, 0, 0, 0}, {ASYS, C_VCON, C_NONE, C_NONE, C_NONE, 50, 4, 0, 0, 0}, - {ASYS, C_VCON, C_REG, C_NONE, C_NONE, 50, 4, 0, 0, 0}, - {ASYSL, C_VCON, C_NONE, C_NONE, C_REG, 50, 4, 0, 0, 0}, + {ASYS, C_VCON, C_NONE, C_NONE, C_ZREG, 50, 4, 0, 0, 0}, + {ASYSL, C_VCON, C_NONE, C_NONE, C_ZREG, 50, 4, 0, 0, 0}, + {ATLBI, C_SPOP, C_NONE, C_NONE, C_NONE, 107, 4, 0, 0, 0}, + {ATLBI, C_SPOP, C_NONE, C_NONE, C_ZREG, 107, 4, 0, 0, 0}, /* encryption instructions */ {AAESD, C_VREG, C_NONE, C_NONE, C_VREG, 29, 4, 0, 0, 0}, // for compatibility with old code {AAESD, C_ARNG, C_NONE, C_NONE, C_ARNG, 29, 4, 0, 0, 0}, // recommend using the new one for better readability - {ASHA1C, C_VREG, C_REG, C_NONE, C_VREG, 1, 4, 0, 0, 0}, + {ASHA1C, C_VREG, C_ZREG, C_NONE, C_VREG, 1, 4, 0, 0, 0}, {ASHA1C, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0}, {ASHA1H, C_VREG, C_NONE, C_NONE, C_VREG, 29, 4, 0, 0, 0}, {ASHA1SU0, C_ARNG, C_ARNG, C_NONE, C_ARNG, 1, 4, 0, 0, 0}, @@ -886,7 +868,7 @@ var optab = []Optab{ {obj.AFUNCDATA, C_VCON, C_NONE, C_NONE, C_ADDR, 0, 0, 0, 0, 0}, {obj.ANOP, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, {obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689 - {obj.ANOP, C_REG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, + {obj.ANOP, C_ZREG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, {obj.ANOP, C_VREG, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, {obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL {obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, // same as AB/ABL @@ -895,40 +877,157 @@ var optab = []Optab{ {obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0}, } -/* - * valid pstate field values, and value to use in instruction - */ +// Valid pstate field values, and value to use in instruction. +// Doesn't include special registers. var pstatefield = []struct { - reg int16 + opd SpecialOperand enc uint32 }{ - {REG_SPSel, 0<<16 | 4<<12 | 5<<5}, - {REG_DAIFSet, 3<<16 | 4<<12 | 6<<5}, - {REG_DAIFClr, 3<<16 | 4<<12 | 7<<5}, + {SPOP_DAIFSet, 3<<16 | 4<<12 | 6<<5}, + {SPOP_DAIFClr, 3<<16 | 4<<12 | 7<<5}, } -var prfopfield = []struct { - reg int16 - enc uint32 +var prfopfield = map[SpecialOperand]uint32{ + SPOP_PLDL1KEEP: 0, + SPOP_PLDL1STRM: 1, + SPOP_PLDL2KEEP: 2, + SPOP_PLDL2STRM: 3, + SPOP_PLDL3KEEP: 4, + SPOP_PLDL3STRM: 5, + SPOP_PLIL1KEEP: 8, + SPOP_PLIL1STRM: 9, + SPOP_PLIL2KEEP: 10, + SPOP_PLIL2STRM: 11, + SPOP_PLIL3KEEP: 12, + SPOP_PLIL3STRM: 13, + SPOP_PSTL1KEEP: 16, + SPOP_PSTL1STRM: 17, + SPOP_PSTL2KEEP: 18, + SPOP_PSTL2STRM: 19, + SPOP_PSTL3KEEP: 20, + SPOP_PSTL3STRM: 21, +} + +// sysInstFields helps convert SYS alias instructions to SYS instructions. +// For example, the format of TLBI is: TLBI {, }. +// It's equivalent to: SYS #, C8, , #{, }. +// The field hasOperand2 indicates whether Xt is required. It helps to check +// some combinations that may be undefined, such as TLBI VMALLE1IS, R0. +var sysInstFields = map[SpecialOperand]struct { + op1 uint8 + cn uint8 + cm uint8 + op2 uint8 + hasOperand2 bool }{ - {REG_PLDL1KEEP, 0}, - {REG_PLDL1STRM, 1}, - {REG_PLDL2KEEP, 2}, - {REG_PLDL2STRM, 3}, - {REG_PLDL3KEEP, 4}, - {REG_PLDL3STRM, 5}, - {REG_PLIL1KEEP, 8}, - {REG_PLIL1STRM, 9}, - {REG_PLIL2KEEP, 10}, - {REG_PLIL2STRM, 11}, - {REG_PLIL3KEEP, 12}, - {REG_PLIL3STRM, 13}, - {REG_PSTL1KEEP, 16}, - {REG_PSTL1STRM, 17}, - {REG_PSTL2KEEP, 18}, - {REG_PSTL2STRM, 19}, - {REG_PSTL3KEEP, 20}, - {REG_PSTL3STRM, 21}, + // TLBI + SPOP_VMALLE1IS: {0, 8, 3, 0, false}, + SPOP_VAE1IS: {0, 8, 3, 1, true}, + SPOP_ASIDE1IS: {0, 8, 3, 2, true}, + SPOP_VAAE1IS: {0, 8, 3, 3, true}, + SPOP_VALE1IS: {0, 8, 3, 5, true}, + SPOP_VAALE1IS: {0, 8, 3, 7, true}, + SPOP_VMALLE1: {0, 8, 7, 0, false}, + SPOP_VAE1: {0, 8, 7, 1, true}, + SPOP_ASIDE1: {0, 8, 7, 2, true}, + SPOP_VAAE1: {0, 8, 7, 3, true}, + SPOP_VALE1: {0, 8, 7, 5, true}, + SPOP_VAALE1: {0, 8, 7, 7, true}, + SPOP_IPAS2E1IS: {4, 8, 0, 1, true}, + SPOP_IPAS2LE1IS: {4, 8, 0, 5, true}, + SPOP_ALLE2IS: {4, 8, 3, 0, false}, + SPOP_VAE2IS: {4, 8, 3, 1, true}, + SPOP_ALLE1IS: {4, 8, 3, 4, false}, + SPOP_VALE2IS: {4, 8, 3, 5, true}, + SPOP_VMALLS12E1IS: {4, 8, 3, 6, false}, + SPOP_IPAS2E1: {4, 8, 4, 1, true}, + SPOP_IPAS2LE1: {4, 8, 4, 5, true}, + SPOP_ALLE2: {4, 8, 7, 0, false}, + SPOP_VAE2: {4, 8, 7, 1, true}, + SPOP_ALLE1: {4, 8, 7, 4, false}, + SPOP_VALE2: {4, 8, 7, 5, true}, + SPOP_VMALLS12E1: {4, 8, 7, 6, false}, + SPOP_ALLE3IS: {6, 8, 3, 0, false}, + SPOP_VAE3IS: {6, 8, 3, 1, true}, + SPOP_VALE3IS: {6, 8, 3, 5, true}, + SPOP_ALLE3: {6, 8, 7, 0, false}, + SPOP_VAE3: {6, 8, 7, 1, true}, + SPOP_VALE3: {6, 8, 7, 5, true}, + SPOP_VMALLE1OS: {0, 8, 1, 0, false}, + SPOP_VAE1OS: {0, 8, 1, 1, true}, + SPOP_ASIDE1OS: {0, 8, 1, 2, true}, + SPOP_VAAE1OS: {0, 8, 1, 3, true}, + SPOP_VALE1OS: {0, 8, 1, 5, true}, + SPOP_VAALE1OS: {0, 8, 1, 7, true}, + SPOP_RVAE1IS: {0, 8, 2, 1, true}, + SPOP_RVAAE1IS: {0, 8, 2, 3, true}, + SPOP_RVALE1IS: {0, 8, 2, 5, true}, + SPOP_RVAALE1IS: {0, 8, 2, 7, true}, + SPOP_RVAE1OS: {0, 8, 5, 1, true}, + SPOP_RVAAE1OS: {0, 8, 5, 3, true}, + SPOP_RVALE1OS: {0, 8, 5, 5, true}, + SPOP_RVAALE1OS: {0, 8, 5, 7, true}, + SPOP_RVAE1: {0, 8, 6, 1, true}, + SPOP_RVAAE1: {0, 8, 6, 3, true}, + SPOP_RVALE1: {0, 8, 6, 5, true}, + SPOP_RVAALE1: {0, 8, 6, 7, true}, + SPOP_RIPAS2E1IS: {4, 8, 0, 2, true}, + SPOP_RIPAS2LE1IS: {4, 8, 0, 6, true}, + SPOP_ALLE2OS: {4, 8, 1, 0, false}, + SPOP_VAE2OS: {4, 8, 1, 1, true}, + SPOP_ALLE1OS: {4, 8, 1, 4, false}, + SPOP_VALE2OS: {4, 8, 1, 5, true}, + SPOP_VMALLS12E1OS: {4, 8, 1, 6, false}, + SPOP_RVAE2IS: {4, 8, 2, 1, true}, + SPOP_RVALE2IS: {4, 8, 2, 5, true}, + SPOP_IPAS2E1OS: {4, 8, 4, 0, true}, + SPOP_RIPAS2E1: {4, 8, 4, 2, true}, + SPOP_RIPAS2E1OS: {4, 8, 4, 3, true}, + SPOP_IPAS2LE1OS: {4, 8, 4, 4, true}, + SPOP_RIPAS2LE1: {4, 8, 4, 6, true}, + SPOP_RIPAS2LE1OS: {4, 8, 4, 7, true}, + SPOP_RVAE2OS: {4, 8, 5, 1, true}, + SPOP_RVALE2OS: {4, 8, 5, 5, true}, + SPOP_RVAE2: {4, 8, 6, 1, true}, + SPOP_RVALE2: {4, 8, 6, 5, true}, + SPOP_ALLE3OS: {6, 8, 1, 0, false}, + SPOP_VAE3OS: {6, 8, 1, 1, true}, + SPOP_VALE3OS: {6, 8, 1, 5, true}, + SPOP_RVAE3IS: {6, 8, 2, 1, true}, + SPOP_RVALE3IS: {6, 8, 2, 5, true}, + SPOP_RVAE3OS: {6, 8, 5, 1, true}, + SPOP_RVALE3OS: {6, 8, 5, 5, true}, + SPOP_RVAE3: {6, 8, 6, 1, true}, + SPOP_RVALE3: {6, 8, 6, 5, true}, + // DC + SPOP_IVAC: {0, 7, 6, 1, true}, + SPOP_ISW: {0, 7, 6, 2, true}, + SPOP_CSW: {0, 7, 10, 2, true}, + SPOP_CISW: {0, 7, 14, 2, true}, + SPOP_ZVA: {3, 7, 4, 1, true}, + SPOP_CVAC: {3, 7, 10, 1, true}, + SPOP_CVAU: {3, 7, 11, 1, true}, + SPOP_CIVAC: {3, 7, 14, 1, true}, + SPOP_IGVAC: {0, 7, 6, 3, true}, + SPOP_IGSW: {0, 7, 6, 4, true}, + SPOP_IGDVAC: {0, 7, 6, 5, true}, + SPOP_IGDSW: {0, 7, 6, 6, true}, + SPOP_CGSW: {0, 7, 10, 4, true}, + SPOP_CGDSW: {0, 7, 10, 6, true}, + SPOP_CIGSW: {0, 7, 14, 4, true}, + SPOP_CIGDSW: {0, 7, 14, 6, true}, + SPOP_GVA: {3, 7, 4, 3, true}, + SPOP_GZVA: {3, 7, 4, 4, true}, + SPOP_CGVAC: {3, 7, 10, 3, true}, + SPOP_CGDVAC: {3, 7, 10, 5, true}, + SPOP_CGVAP: {3, 7, 12, 3, true}, + SPOP_CGDVAP: {3, 7, 12, 5, true}, + SPOP_CGVADP: {3, 7, 13, 3, true}, + SPOP_CGDVADP: {3, 7, 13, 5, true}, + SPOP_CIGVAC: {3, 7, 14, 3, true}, + SPOP_CIGDVAC: {3, 7, 14, 5, true}, + SPOP_CVAP: {3, 7, 12, 1, true}, + SPOP_CVADP: {3, 7, 13, 1, true}, } // Used for padinng NOOP instruction @@ -1124,13 +1223,25 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable) + + // Now that we know byte offsets, we can generate jump table entries. + for _, jt := range cursym.Func().JumpTables { + for i, p := range jt.Targets { + // The ith jumptable entry points to the p.Pc'th + // byte in the function symbol s. + // TODO: try using relative PCs. + jt.Sym.WriteAddr(ctxt, int64(i)*8, 8, cursym, p.Pc) + } + } } // isUnsafePoint returns whether p is an unsafe point. func (c *ctxt7) isUnsafePoint(p *obj.Prog) bool { // If p explicitly uses REGTMP, it's unsafe to preempt, because the // preemption sequence clobbers REGTMP. - return p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP + return p.From.Reg == REGTMP || p.To.Reg == REGTMP || p.Reg == REGTMP || + p.From.Type == obj.TYPE_REGREG && p.From.Offset == REGTMP || + p.To.Type == obj.TYPE_REGREG && p.To.Offset == REGTMP } // isRestartable returns whether p is a multi-instruction sequence that, @@ -1203,7 +1314,7 @@ func (c *ctxt7) addpool128(p *obj.Prog, al, ah *obj.Addr) { q := c.newprog() q.As = ADWORD q.To.Type = obj.TYPE_CONST - q.To.Offset = al.Offset + q.To.Offset = al.Offset // q.Pc is lower than t.Pc, so al.Offset is stored in q. t := c.newprog() t.As = ADWORD @@ -1449,6 +1560,10 @@ func sequenceOfOnes(x uint64) bool { // N=0, S=11110x -- period=2 // R is the shift amount, low bits of S = n-1 func bitconEncode(x uint64, mode int) uint32 { + if mode == 32 { + x &= 0xffffffff + x = x<<32 | x + } var period uint32 // determine the period and sign-extend a unit to 64 bits switch { @@ -1693,20 +1808,19 @@ func rclass(r int16) int { case REG_R0 <= r && r <= REG_R30: // not 31 return C_REG case r == REGZERO: - return C_ZCON + return C_ZREG case REG_F0 <= r && r <= REG_F31: return C_FREG case REG_V0 <= r && r <= REG_V31: return C_VREG - case COND_EQ <= r && r <= COND_NV: - return C_COND case r == REGSP: return C_RSP case r >= REG_ARNG && r < REG_ELEM: return C_ARNG case r >= REG_ELEM && r < REG_ELEM_END: return C_ELEM - case r >= REG_UXTB && r < REG_SPECIAL: + case r >= REG_UXTB && r < REG_SPECIAL, + r >= REG_LSL && r < REG_ARNG: return C_EXTREG case r >= REG_SPECIAL: return C_SPR @@ -1718,17 +1832,24 @@ func rclass(r int16) int { // but saved in Offset which type is int64, con32class treats it as uint32 type and reclassifies it. func (c *ctxt7) con32class(a *obj.Addr) int { v := uint32(a.Offset) + // For 32-bit instruction with constant, rewrite + // the high 32-bit to be a repetition of the low + // 32-bit, so that the BITCON test can be shared + // for both 32-bit and 64-bit. 32-bit ops will + // zero the high 32-bit of the destination register + // anyway. + vbitcon := uint64(v)<<32 | uint64(v) if v == 0 { return C_ZCON } if isaddcon(int64(v)) { if v <= 0xFFF { - if isbitcon(uint64(a.Offset)) { + if isbitcon(vbitcon) { return C_ABCON0 } return C_ADDCON0 } - if isbitcon(uint64(a.Offset)) { + if isbitcon(vbitcon) { return C_ABCON } if movcon(int64(v)) >= 0 { @@ -1742,7 +1863,7 @@ func (c *ctxt7) con32class(a *obj.Addr) int { t := movcon(int64(v)) if t >= 0 { - if isbitcon(uint64(a.Offset)) { + if isbitcon(vbitcon) { return C_MBCON } return C_MOVCON @@ -1750,13 +1871,13 @@ func (c *ctxt7) con32class(a *obj.Addr) int { t = movcon(int64(^v)) if t >= 0 { - if isbitcon(uint64(a.Offset)) { + if isbitcon(vbitcon) { return C_MBCON } return C_MOVCON } - if isbitcon(uint64(a.Offset)) { + if isbitcon(vbitcon) { return C_BITCON } @@ -1974,8 +2095,14 @@ func (c *ctxt7) aclass(a *obj.Addr) int { case obj.TYPE_BRANCH: return C_SBRA - } + case obj.TYPE_SPECIAL: + opd := SpecialOperand(a.Offset) + if SPOP_EQ <= opd && opd <= SPOP_NV { + return C_COND + } + return C_SPOP + } return C_GOK } @@ -2074,8 +2201,8 @@ func cmp(a int, b int) bool { return true } - case C_REG: - if b == C_ZCON { + case C_ZREG: + if b == C_REG { return true } @@ -2089,13 +2216,18 @@ func cmp(a int, b int) bool { return true } + case C_MBCON: + if b == C_ABCON0 { + return true + } + case C_BITCON: if b == C_ABCON0 || b == C_ABCON || b == C_MBCON { return true } case C_MOVCON: - if b == C_MBCON || b == C_ZCON || b == C_ADDCON0 || b == C_AMCON { + if b == C_MBCON || b == C_ZCON || b == C_ADDCON0 || b == C_ABCON0 || b == C_AMCON { return true } @@ -2884,9 +3016,10 @@ func buildop(ctxt *obj.Link) { case ASYS: oprangeset(AAT, t) - oprangeset(ADC, t) oprangeset(AIC, t) - oprangeset(ATLBI, t) + + case ATLBI: + oprangeset(ADC, t) case ASYSL, AHINT: break @@ -3001,6 +3134,8 @@ func buildop(ctxt *obj.Link) { case AVZIP1: oprangeset(AVZIP2, t) + oprangeset(AVTRN1, t) + oprangeset(AVTRN2, t) case AVUXTL: oprangeset(AVUXTL2, t) @@ -3022,6 +3157,9 @@ func buildop(ctxt *obj.Link) { case AVUADDW: oprangeset(AVUADDW2, t) + case AVTBL: + oprangeset(AVTBX, t) + case ASHA1H, AVCNT, AVMOV, @@ -3030,7 +3168,6 @@ func buildop(ctxt *obj.Link) { AVST2, AVST3, AVST4, - AVTBL, AVDUP, AVMOVI, APRFM, @@ -3299,8 +3436,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } if int(o.size) == 8 { - o1 = c.oaddi(p, op, v&0xfff000, r, REGTMP) - o2 = c.oaddi(p, op, v&0x000fff, REGTMP, rt) + // NOTE: this case does not use REGTMP. If it ever does, + // remove the NOTUSETMP flag in optab. + o1 = c.oaddi(p, op, v&0xfff000, r, rt) + o2 = c.oaddi(p, op, v&0x000fff, rt, rt) break } @@ -3419,6 +3558,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o4 = os[3] case 13: /* addop $vcon, [R], R (64 bit literal); cmp $lcon,R -> addop $lcon,R, ZR */ + if p.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } if p.To.Reg == REG_RSP && isADDSop(p.As) { c.ctxt.Diag("illegal destination register: %v\n", p) } @@ -3535,12 +3677,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 18: /* csel cond,Rn,Rm,Rd; cinc/cinv/cneg cond,Rn,Rd; cset cond,Rd */ o1 = c.oprrr(p, p.As) - cond := int(p.From.Reg) - // AL and NV are not allowed for CINC/CINV/CNEG/CSET/CSETM instructions - if cond < COND_EQ || cond > COND_NV || (cond == COND_AL || cond == COND_NV) && p.From3Type() == obj.TYPE_NONE { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV || (cond == SPOP_AL || cond == SPOP_NV) && p.From3Type() == obj.TYPE_NONE { c.ctxt.Diag("invalid condition: %v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } r := int(p.Reg) @@ -3563,11 +3704,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 19: /* CCMN cond, (Rm|uimm5),Rn, uimm4 -> ccmn Rn,Rm,uimm4,cond */ nzcv := int(p.To.Offset) - cond := int(p.From.Reg) - if cond < COND_EQ || cond > COND_NV { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV { c.ctxt.Diag("invalid condition\n%v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } var rf int if p.GetFrom3().Type == obj.TYPE_REG { @@ -3675,52 +3816,18 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rt := int(p.To.Reg) o1 |= (uint32(rf&31) << 16) | (REGZERO & 31 << 5) | uint32(rt&31) - case 26: // op R<> 10) & 63 - shift := (p.From.Offset >> 22) & 3 - if shift != 0 { - c.ctxt.Diag("illegal combination: %v", p) - break - } - - if amount > 4 { - c.ctxt.Diag("the left shift amount out of range 0 to 4: %v", p) - break - } - rf := (p.From.Offset >> 16) & 31 - rt := int(p.To.Reg) - r := int(p.Reg) - if p.To.Type == obj.TYPE_NONE { - rt = REGZERO - } - if r == 0 { - r = rt - } - - o1 = c.opxrrr(p, p.As, false) - o1 |= uint32(rf)<<16 | uint32(amount&7)<<10 | (uint32(r&31) << 5) | uint32(rt&31) - case 27: /* op Rm<= REG_LSL && p.From.Reg < REG_ARNG) { amount := (p.From.Reg >> 5) & 7 if amount > 4 { c.ctxt.Diag("shift amount out of range 0 to 4: %v", p) } o1 = c.opxrrr(p, p.As, true) - o1 |= c.encRegShiftOrExt(&p.From, p.From.Reg) /* includes reg, op, etc */ + o1 |= c.encRegShiftOrExt(p, &p.From, p.From.Reg) /* includes reg, op, etc */ } else { o1 = c.opxrrr(p, p.As, false) o1 |= uint32(p.From.Reg&31) << 16 @@ -3736,6 +3843,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= (uint32(r&31) << 5) | uint32(rt&31) case 28: /* logop $vcon, [R], R (64 bit literal) */ + if p.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } o := uint32(0) num := uint8(0) cls := oclass(&p.From) @@ -3774,13 +3884,13 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 29: /* op Rn, Rd */ fc := c.aclass(&p.From) tc := c.aclass(&p.To) - if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZCON || tc == C_REG || tc == C_ZCON) { + if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZREG || tc == C_REG) { // FMOV Rx, Fy or FMOV Fy, Rx o1 = FPCVTI(0, 0, 0, 0, 6) if p.As == AFMOVD { o1 |= 1<<31 | 1<<22 // 64-bit } - if fc == C_REG || fc == C_ZCON { + if fc == C_REG || fc == C_ZREG { o1 |= 1 << 16 // FMOV Rx, Fy } } else { @@ -3883,6 +3993,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 = c.opirr(p, p.As) d := p.From.Offset + if d == 0 { + c.ctxt.Diag("zero shifts cannot be handled correctly: %v", p) + } s := movcon(d) if s < 0 || s >= 4 { c.ctxt.Diag("bad constant for MOVK: %#x\n%v", uint64(d), p) @@ -3890,7 +4003,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if (o1&S64) == 0 && s >= 2 { c.ctxt.Diag("illegal bit position\n%v", p) } - if ((d >> uint(s*16)) >> 16) != 0 { + if ((uint64(d) >> uint(s*16)) >> 16) != 0 { c.ctxt.Diag("requires uimm16\n%v", p) } rt := int(p.To.Reg) @@ -3956,10 +4069,16 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 = c.opirr(p, AMSR) o1 |= uint32((p.From.Offset & 0xF) << 8) /* Crm */ v := uint32(0) - for i := 0; i < len(pstatefield); i++ { - if pstatefield[i].reg == p.To.Reg { - v = pstatefield[i].enc - break + // PSTATEfield can be special registers and special operands. + if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SPSel { + v = 0<<16 | 4<<12 | 5<<5 + } else if p.To.Type == obj.TYPE_SPECIAL { + opd := SpecialOperand(p.To.Offset) + for _, pf := range pstatefield { + if pf.opd == opd { + v = pf.enc + break + } } } @@ -4170,8 +4289,6 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= uint32(p.From.Offset) if p.To.Type == obj.TYPE_REG { o1 |= uint32(p.To.Reg & 31) - } else if p.Reg != 0 { - o1 |= uint32(p.Reg & 31) } else { o1 |= 0x1F } @@ -4198,6 +4315,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if r == 0 { r = rt } + if r == REG_RSP { + c.ctxt.Diag("illegal source register: %v", p) + break + } mode := 64 v := uint64(p.From.Offset) switch p.As { @@ -4253,11 +4374,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 57: /* floating point conditional compare */ o1 = c.oprrr(p, p.As) - cond := int(p.From.Reg) - if cond < COND_EQ || cond > COND_NV { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV { c.ctxt.Diag("invalid condition\n%v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } nzcv := int(p.To.Offset) @@ -4362,6 +4483,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { /* reloc ops */ case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */ + if p.From.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } o1 = ADR(1, 0, REGTMP) o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31 rel := obj.Addrel(c.cursym) @@ -4593,6 +4717,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { // add Rtmp, R, Rtmp // ldp (Rtmp), (R1, R2) r := int(p.From.Reg) + if r == REGTMP { + c.ctxt.Diag("REGTMP used in large offset load: %v", p) + } if r == obj.REG_NONE { r = int(o.param) } @@ -4609,6 +4736,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 76: // add $O, R, Rtmp or sub $O, R, Rtmp // stp (R1, R2), (Rtmp) + if p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v", p) + } r := int(p.To.Reg) if r == obj.REG_NONE { r = int(o.param) @@ -4636,6 +4766,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { // add Rtmp, R, Rtmp // stp (R1, R2), (Rtmp) r := int(p.To.Reg) + if r == REGTMP || p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("REGTMP used in large offset store: %v", p) + } if r == obj.REG_NONE { r = int(o.param) } @@ -4941,6 +5074,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= (uint32(Q&1) << 30) | (uint32((r>>5)&7) << 16) | (uint32(r&0x1f) << 5) | uint32(rt&31) case 87: /* stp (r,r), addr(SB) -> adrp + add + stp */ + if p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v", p) + } o1 = ADR(1, 0, REGTMP) o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31 rel := obj.Addrel(c.cursym) @@ -4994,22 +5130,16 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 91: /* prfm imm(Rn), */ imm := uint32(p.From.Offset) r := p.From.Reg - v := uint32(0xff) + var v uint32 + var ok bool if p.To.Type == obj.TYPE_CONST { v = uint32(p.To.Offset) - if v > 31 { - c.ctxt.Diag("illegal prefetch operation\n%v", p) - } + ok = v <= 31 } else { - for i := 0; i < len(prfopfield); i++ { - if prfopfield[i].reg == p.To.Reg { - v = prfopfield[i].enc - break - } - } - if v == 0xff { - c.ctxt.Diag("illegal prefetch operation:\n%v", p) - } + v, ok = prfopfield[SpecialOperand(p.To.Offset)] + } + if !ok { + c.ctxt.Diag("illegal prefetch operation:\n%v", p) } o1 = c.opirr(p, p.As) @@ -5328,7 +5458,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { c.checkShiftAmount(p, &p.From) o1 = c.opldrr(p, p.As, true) - o1 |= c.encRegShiftOrExt(&p.From, p.From.Index) /* includes reg, op, etc */ + o1 |= c.encRegShiftOrExt(p, &p.From, p.From.Index) /* includes reg, op, etc */ } else { // (Rn)(Rm), no extension or shift. o1 = c.opldrr(p, p.As, false) @@ -5344,7 +5474,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { c.checkShiftAmount(p, &p.To) o1 = c.opstrr(p, p.As, true) - o1 |= c.encRegShiftOrExt(&p.To, p.To.Index) /* includes reg, op, etc */ + o1 |= c.encRegShiftOrExt(p, &p.To, p.To.Index) /* includes reg, op, etc */ } else { // (Rn)(Rm), no extension or shift. o1 = c.opstrr(p, p.As, false) @@ -5354,7 +5484,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rf := int(p.From.Reg) o1 |= uint32(rf & 31) - case 100: /* VTBL Vn., [Vt1., Vt2., ...], Vd. */ + case 100: /* VTBL/VTBX Vn., [Vt1., Vt2., ...], Vd. */ af := int((p.From.Reg >> 5) & 15) at := int((p.To.Reg >> 5) & 15) if af != at { @@ -5385,7 +5515,14 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { default: c.ctxt.Diag("invalid register numbers in ARM64 register list: %v", p) } - o1 = q<<30 | 0xe<<24 | len<<13 + var op uint32 + switch p.As { + case AVTBL: + op = 0 + case AVTBX: + op = 1 + } + o1 = q<<30 | 0xe<<24 | len<<13 | op<<12 o1 |= (uint32(rf&31) << 16) | uint32(offset&31)<<5 | uint32(rt&31) case 101: // VMOVQ $vcon1, $vcon2, Vd or VMOVD|VMOVS $vcon, Vd -> FMOVQ/FMOVD/FMOVS pool(PC), Vd: load from constant pool. @@ -5532,6 +5669,26 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { c.ctxt.Diag("illegal destination register: %v\n", p) } o1 |= enc | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31) + + case 107: /* tlbi, dc */ + op, ok := sysInstFields[SpecialOperand(p.From.Offset)] + if !ok || (p.As == ATLBI && op.cn != 8) || (p.As == ADC && op.cn != 7) { + c.ctxt.Diag("illegal argument: %v\n", p) + break + } + o1 = c.opirr(p, p.As) + if op.hasOperand2 { + if p.To.Reg == 0 { + c.ctxt.Diag("missing register at operand 2: %v\n", p) + } + o1 |= uint32(p.To.Reg & 0x1F) + } else { + if p.To.Reg != 0 || p.Reg != 0 { + c.ctxt.Diag("extraneous register at operand 2: %v\n", p) + } + o1 |= uint32(0x1F) + } + o1 |= uint32(SYSARG4(int(op.op1), int(op.cn), int(op.cm), int(op.op2))) } out[0] = o1 out[1] = o2 @@ -6199,6 +6356,12 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 { case AVUADDW, AVUADDW2: return 0x17<<25 | 1<<21 | 1<<12 + + case AVTRN1: + return 7<<25 | 5<<11 + + case AVTRN2: + return 7<<25 | 1<<14 | 5<<11 } c.ctxt.Diag("%v: bad rrr %d %v", p, a, a) @@ -6543,7 +6706,12 @@ func (c *ctxt7) opimm(p *obj.Prog, a obj.As) uint32 { func (c *ctxt7) brdist(p *obj.Prog, preshift int, flen int, shift int) int64 { v := int64(0) t := int64(0) - q := p.To.Target() + var q *obj.Prog + if p.To.Type == obj.TYPE_BRANCH { + q = p.To.Target() + } else if p.From.Type == obj.TYPE_BRANCH { // adr, adrp + q = p.From.Target() + } if q == nil { // TODO: don't use brdist for this case, as it isn't a branch. // (Calls from omovlit, and maybe adr/adrp opcodes as well.) @@ -7039,8 +7207,8 @@ func (c *ctxt7) omovlit(as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32 { // load a constant (MOVCON or BITCON) in a into rt func (c *ctxt7) omovconst(as obj.As, p *obj.Prog, a *obj.Addr, rt int) (o1 uint32) { - if cls := oclass(a); cls == C_BITCON || cls == C_ABCON || cls == C_ABCON0 { - // or $bitcon, REGZERO, rt + if cls := oclass(a); (cls == C_BITCON || cls == C_ABCON || cls == C_ABCON0) && rt != REGZERO { + // or $bitcon, REGZERO, rt. rt can't be ZR. mode := 64 var as1 obj.As switch as { @@ -7272,7 +7440,7 @@ func (c *ctxt7) opextr(p *obj.Prog, a obj.As, v int32, rn int, rm int, rt int) u return o } -/* genrate instruction encoding for ldp and stp series */ +/* generate instruction encoding for ldp and stp series */ func (c *ctxt7) opldpstp(p *obj.Prog, o *Optab, vo int32, rbase, rl, rh, ldp uint32) uint32 { wback := false if o.scond == C_XPOST || o.scond == C_XPRE { @@ -7411,7 +7579,7 @@ func roff(rm int16, o uint32, amount int16) uint32 { } // encRegShiftOrExt returns the encoding of shifted/extended register, Rx<> 5) & 7 rm = r & 31 @@ -7456,8 +7624,13 @@ func (c *ctxt7) encRegShiftOrExt(a *obj.Addr, r int16) uint32 { } else { return roff(rm, 7, num) } - case REG_LSL <= r && r < (REG_LSL+1<<8): - return roff(rm, 3, 6) + case REG_LSL <= r && r < REG_ARNG: + if a.Type == obj.TYPE_MEM { // (R1)(R2<<1) + return roff(rm, 3, 6) + } else if isADDWop(p.As) { + return roff(rm, 2, num) + } + return roff(rm, 3, num) default: c.ctxt.Diag("unsupported register extension type.") } diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.go b/src/cmd/internal/obj/arm64/asm_arm64_test.go index c6a00f5b941035..f468b6b0fe97ba 100644 --- a/src/cmd/internal/obj/arm64/asm_arm64_test.go +++ b/src/cmd/internal/obj/arm64/asm_arm64_test.go @@ -160,3 +160,14 @@ func TestVMOVQ(t *testing.T) { t.Errorf("TestVMOVQ got: a=0x%x, b=0x%x, want: a=0x7040201008040201, b=0x3040201008040201", a, b) } } + +func testmovk() uint64 + +// TestMOVK makes sure MOVK with a very large constant works. See issue 52261. +func TestMOVK(t *testing.T) { + x := testmovk() + want := uint64(40000 << 48) + if x != want { + t.Errorf("TestMOVK got %x want %x\n", x, want) + } +} diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.s b/src/cmd/internal/obj/arm64/asm_arm64_test.s index 9d337a4fd1a868..f85433c6e3ca30 100644 --- a/src/cmd/internal/obj/arm64/asm_arm64_test.s +++ b/src/cmd/internal/obj/arm64/asm_arm64_test.s @@ -12,3 +12,10 @@ TEXT ·testvmovq(SB), NOSPLIT, $0-16 MOVD R0, r1+0(FP) MOVD R1, r2+8(FP) RET + +// testmovk() uint64 +TEXT ·testmovk(SB), NOSPLIT, $0-8 + MOVD $0, R0 + MOVK $(40000<<48), R0 + MOVD R0, ret+0(FP) + RET diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go index efd4577f56bae0..c12f618e939107 100644 --- a/src/cmd/internal/obj/arm64/doc.go +++ b/src/cmd/internal/obj/arm64/doc.go @@ -6,24 +6,26 @@ Package arm64 implements an ARM64 assembler. Go assembly syntax is different from GNU ARM64 syntax, but we can still follow the general rules to map between them. -Instructions mnemonics mapping rules +# Instructions mnemonics mapping rules 1. Most instructions use width suffixes of instruction names to indicate operand width rather than using different register names. - Examples: - ADC R24, R14, R12 <=> adc x12, x24 - ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24 - FCMPS F2, F3 <=> fcmp s3, s2 - FCMPD F2, F3 <=> fcmp d3, d2 - FCVTDH F2, F3 <=> fcvt h3, d2 +Examples: + + ADC R24, R14, R12 <=> adc x12, x24 + ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24 + FCMPS F2, F3 <=> fcmp s3, s2 + FCMPD F2, F3 <=> fcmp d3, d2 + FCVTDH F2, F3 <=> fcvt h3, d2 2. Go uses .P and .W suffixes to indicate post-increment and pre-increment. - Examples: - MOVD.P -8(R10), R8 <=> ldr x8, [x10],#-8 - MOVB.W 16(R16), R10 <=> ldrsb x10, [x16,#16]! - MOVBU.W 16(R16), R10 <=> ldrb x10, [x16,#16]! +Examples: + + MOVD.P -8(R10), R8 <=> ldr x8, [x10],#-8 + MOVB.W 16(R16), R10 <=> ldrsb x10, [x16,#16]! + MOVBU.W 16(R16), R10 <=> ldrb x10, [x16,#16]! 3. Go uses a series of MOV instructions as load and store. @@ -39,12 +41,13 @@ ldrsh, sturh, strh => MOVH. 5. Go adds a V prefix for most floating-point and SIMD instructions, except cryptographic extension instructions and floating-point(scalar) instructions. - Examples: - VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h - VLD1.P (R6)(R11), [V31.D1] <=> ld1 {v31.1d}, [x6], x11 - VFMLA V29.S2, V20.S2, V14.S2 <=> fmla v14.2s, v20.2s, v29.2s - AESD V22.B16, V19.B16 <=> aesd v19.16b, v22.16b - SCVTFWS R3, F16 <=> scvtf s17, w6 +Examples: + + VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h + VLD1.P (R6)(R11), [V31.D1] <=> ld1 {v31.1d}, [x6], x11 + VFMLA V29.S2, V20.S2, V14.S2 <=> fmla v14.2s, v20.2s, v29.2s + AESD V22.B16, V19.B16 <=> aesd v19.16b, v22.16b + SCVTFWS R3, F16 <=> scvtf s17, w6 6. Align directive @@ -52,25 +55,28 @@ Go asm supports the PCALIGN directive, which indicates that the next instruction to a specified boundary by padding with NOOP instruction. The alignment value supported on arm64 must be a power of 2 and in the range of [8, 2048]. - Examples: - PCALIGN $16 - MOVD $2, R0 // This instruction is aligned with 16 bytes. - PCALIGN $1024 - MOVD $3, R1 // This instruction is aligned with 1024 bytes. +Examples: + + PCALIGN $16 + MOVD $2, R0 // This instruction is aligned with 16 bytes. + PCALIGN $1024 + MOVD $3, R1 // This instruction is aligned with 1024 bytes. PCALIGN also changes the function alignment. If a function has one or more PCALIGN directives, its address will be aligned to the same or coarser boundary, which is the maximum of all the alignment values. In the following example, the function Add is aligned with 128 bytes. - Examples: - TEXT ·Add(SB),$40-16 - MOVD $2, R0 - PCALIGN $32 - MOVD $4, R1 - PCALIGN $128 - MOVD $8, R2 - RET + +Examples: + + TEXT ·Add(SB),$40-16 + MOVD $2, R0 + PCALIGN $32 + MOVD $4, R1 + PCALIGN $128 + MOVD $8, R2 + RET On arm64, functions in Go are aligned to 16 bytes by default, we can also use PCALGIN to set the function alignment. The functions that need to be aligned are preferably using NOFRAME and NOSPLIT @@ -79,22 +85,38 @@ have the same alignment as the first hand-written instruction. In the following example, PCALIGN at the entry of the function Add will align its address to 2048 bytes. - Examples: - TEXT ·Add(SB),NOSPLIT|NOFRAME,$0 - PCALIGN $2048 - MOVD $1, R0 - MOVD $1, R1 - RET +Examples: + + TEXT ·Add(SB),NOSPLIT|NOFRAME,$0 + PCALIGN $2048 + MOVD $1, R0 + MOVD $1, R1 + RET 7. Move large constants to vector registers. Go asm uses VMOVQ/VMOVD/VMOVS to move 128-bit, 64-bit and 32-bit constants into vector registers, respectively. -And for a 128-bit interger, it take two 64-bit operands, for the high and low parts separately. +And for a 128-bit interger, it take two 64-bit operands, for the low and high parts separately. + +Examples: - Examples: - VMOVS $0x11223344, V0 - VMOVD $0x1122334455667788, V1 - VMOVQ $0x1122334455667788, $8877665544332211, V2 // V2=0x11223344556677888877665544332211 + VMOVS $0x11223344, V0 + VMOVD $0x1122334455667788, V1 + VMOVQ $0x1122334455667788, $0x99aabbccddeeff00, V2 // V2=0x99aabbccddeeff001122334455667788 + +8. Move an optionally-shifted 16-bit immediate value to a register. + +The instructions are MOVK(W), MOVZ(W) and MOVN(W), the assembly syntax is "op $(uimm16<". The +is the 16-bit unsigned immediate, in the range 0 to 65535; For the 32-bit variant, the is 0 or 16, for the +64-bit variant, the is 0, 16, 32 or 48. + +The current Go assembler does not accept zero shifts, such as "op $0, Rd" and "op $(0<<(16|32|48)), Rd" instructions. + +Examples: + + MOVK $(10<<32), R20 <=> movk x20, #10, lsl #32 + MOVZW $(20<<16), R8 <=> movz w8, #20, lsl #16 + MOVK $(0<<16), R10 will be reported as an error by the assembler. Special Cases. @@ -108,16 +130,16 @@ Special Cases. related to real ARM64 instruction. NOOP serves for the hardware nop instruction. NOOP is an alias of HINT $0. - Examples: - VMOV V13.B[1], R20 <=> mov x20, v13.b[1] - VMOV V13.H[1], R20 <=> mov w20, v13.h[1] - JMP (R3) <=> br x3 - CALL (R17) <=> blr x17 - LDAXRB (R19), R16 <=> ldaxrb w16, [x19] - NOOP <=> nop +Examples: + VMOV V13.B[1], R20 <=> mov x20, v13.b[1] + VMOV V13.H[1], R20 <=> mov w20, v13.h[1] + JMP (R3) <=> br x3 + CALL (R17) <=> blr x17 + LDAXRB (R19), R16 <=> ldaxrb w16, [x19] + NOOP <=> nop -Register mapping rules +# Register mapping rules 1. All basic register names are written as Rn. @@ -126,87 +148,96 @@ Register mapping rules 3. Bn, Hn, Dn, Sn and Qn instructions are written as Fn in floating-point instructions and as Vn in SIMD instructions. - -Argument mapping rules +# Argument mapping rules 1. The operands appear in left-to-right assignment order. Go reverses the arguments of most instructions. - Examples: - ADD R11.SXTB<<1, RSP, R25 <=> add x25, sp, w11, sxtb #1 - VADD V16, V19, V14 <=> add d14, d19, d16 +Examples: + + ADD R11.SXTB<<1, RSP, R25 <=> add x25, sp, w11, sxtb #1 + VADD V16, V19, V14 <=> add d14, d19, d16 Special Cases. (1) Argument order is the same as in the GNU ARM64 syntax: cbz, cbnz and some store instructions, such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1. - Examples: - MOVD R29, 384(R19) <=> str x29, [x19,#384] - MOVB.P R30, 30(R4) <=> strb w30, [x4],#30 - STLRH R21, (R19) <=> stlrh w21, [x19] +Examples: + + MOVD R29, 384(R19) <=> str x29, [x19,#384] + MOVB.P R30, 30(R4) <=> strb w30, [x4],#30 + STLRH R21, (R19) <=> stlrh w21, [x19] (2) MADD, MADDW, MSUB, MSUBW, SMADDL, SMSUBL, UMADDL, UMSUBL , , , - Examples: - MADD R2, R30, R22, R6 <=> madd x6, x22, x2, x30 - SMSUBL R10, R3, R17, R27 <=> smsubl x27, w17, w10, x3 +Examples: + + MADD R2, R30, R22, R6 <=> madd x6, x22, x2, x30 + SMSUBL R10, R3, R17, R27 <=> smsubl x27, w17, w10, x3 (3) FMADDD, FMADDS, FMSUBD, FMSUBS, FNMADDD, FNMADDS, FNMSUBD, FNMSUBS , , , - Examples: - FMADDD F30, F20, F3, F29 <=> fmadd d29, d3, d30, d20 - FNMSUBS F7, F25, F7, F22 <=> fnmsub s22, s7, s7, s25 +Examples: + + FMADDD F30, F20, F3, F29 <=> fmadd d29, d3, d30, d20 + FNMSUBS F7, F25, F7, F22 <=> fnmsub s22, s7, s7, s25 (4) BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX $, , $, - Examples: - BFIW $16, R20, $6, R0 <=> bfi w0, w20, #16, #6 - UBFIZ $34, R26, $5, R20 <=> ubfiz x20, x26, #34, #5 +Examples: + + BFIW $16, R20, $6, R0 <=> bfi w0, w20, #16, #6 + UBFIZ $34, R26, $5, R20 <=> ubfiz x20, x26, #34, #5 (5) FCCMPD, FCCMPS, FCCMPED, FCCMPES , Fm. Fn, $ - Examples: - FCCMPD AL, F8, F26, $0 <=> fccmp d26, d8, #0x0, al - FCCMPS VS, F29, F4, $4 <=> fccmp s4, s29, #0x4, vs - FCCMPED LE, F20, F5, $13 <=> fccmpe d5, d20, #0xd, le - FCCMPES NE, F26, F10, $0 <=> fccmpe s10, s26, #0x0, ne +Examples: + + FCCMPD AL, F8, F26, $0 <=> fccmp d26, d8, #0x0, al + FCCMPS VS, F29, F4, $4 <=> fccmp s4, s29, #0x4, vs + FCCMPED LE, F20, F5, $13 <=> fccmpe d5, d20, #0xd, le + FCCMPES NE, F26, F10, $0 <=> fccmpe s10, s26, #0x0, ne (6) CCMN, CCMNW, CCMP, CCMPW , , $, $ - Examples: - CCMP MI, R22, $12, $13 <=> ccmp x22, #0xc, #0xd, mi - CCMNW AL, R1, $11, $8 <=> ccmn w1, #0xb, #0x8, al +Examples: + + CCMP MI, R22, $12, $13 <=> ccmp x22, #0xc, #0xd, mi + CCMNW AL, R1, $11, $8 <=> ccmn w1, #0xb, #0x8, al (7) CCMN, CCMNW, CCMP, CCMPW , , , $ - Examples: - CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs - CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs +Examples: + + CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs + CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs (9) CSEL, CSELW, CSNEG, CSNEGW, CSINC, CSINCW , , , ; FCSELD, FCSELS , , , - Examples: - CSEL GT, R0, R19, R1 <=> csel x1, x0, x19, gt - CSNEGW GT, R7, R17, R8 <=> csneg w8, w7, w17, gt - FCSELD EQ, F15, F18, F16 <=> fcsel d16, d15, d18, eq +Examples: -(10) TBNZ, TBZ $, ,
        + {{template "script" .}} + + + diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go index f72314b1857ddb..1e9154c5f53369 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go @@ -79,7 +79,7 @@ type configMenuEntry struct { } // configMenu returns a list of items to add to a menu in the web UI. -func configMenu(fname string, url url.URL) []configMenuEntry { +func configMenu(fname string, u url.URL) []configMenuEntry { // Start with system configs. configs := []namedConfig{{Name: "Default", config: defaultConfig()}} if settings, err := readSettings(fname); err == nil { @@ -91,13 +91,15 @@ func configMenu(fname string, url url.URL) []configMenuEntry { result := make([]configMenuEntry, len(configs)) lastMatch := -1 for i, cfg := range configs { - dst, changed := cfg.config.makeURL(url) + dst, changed := cfg.config.makeURL(u) if !changed { lastMatch = i } + // Use a relative URL to work in presence of stripping/redirects in webui.go. + rel := &url.URL{RawQuery: dst.RawQuery, ForceQuery: true} result[i] = configMenuEntry{ Name: cfg.Name, - URL: dst.String(), + URL: rel.String(), UserConfig: (i != 0), } } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/tagroot.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/tagroot.go new file mode 100644 index 00000000000000..c43d5999821f3a --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/tagroot.go @@ -0,0 +1,129 @@ +package driver + +import ( + "strings" + + "github.com/google/pprof/internal/measurement" + "github.com/google/pprof/profile" +) + +// addLabelNodes adds pseudo stack frames "label:value" to each Sample with +// labels matching the supplied keys. +// +// rootKeys adds frames at the root of the callgraph (first key becomes new root). +// leafKeys adds frames at the leaf of the callgraph (last key becomes new leaf). +// +// Returns whether there were matches found for the label keys. +func addLabelNodes(p *profile.Profile, rootKeys, leafKeys []string, outputUnit string) (rootm, leafm bool) { + // Find where to insert the new locations and functions at the end of + // their ID spaces. + var maxLocID uint64 + var maxFunctionID uint64 + for _, loc := range p.Location { + if loc.ID > maxLocID { + maxLocID = loc.ID + } + } + for _, f := range p.Function { + if f.ID > maxFunctionID { + maxFunctionID = f.ID + } + } + nextLocID := maxLocID + 1 + nextFuncID := maxFunctionID + 1 + + // Intern the new locations and functions we are generating. + type locKey struct { + functionName, fileName string + } + locs := map[locKey]*profile.Location{} + + internLoc := func(locKey locKey) *profile.Location { + loc, found := locs[locKey] + if found { + return loc + } + + function := &profile.Function{ + ID: nextFuncID, + Name: locKey.functionName, + Filename: locKey.fileName, + } + nextFuncID++ + p.Function = append(p.Function, function) + + loc = &profile.Location{ + ID: nextLocID, + Line: []profile.Line{ + { + Function: function, + }, + }, + } + nextLocID++ + p.Location = append(p.Location, loc) + locs[locKey] = loc + return loc + } + + makeLabelLocs := func(s *profile.Sample, keys []string) ([]*profile.Location, bool) { + var locs []*profile.Location + var match bool + for i := range keys { + // Loop backwards, ensuring the first tag is closest to the root, + // and the last tag is closest to the leaves. + k := keys[len(keys)-1-i] + values := formatLabelValues(s, k, outputUnit) + if len(values) > 0 { + match = true + } + locKey := locKey{ + functionName: strings.Join(values, ","), + fileName: k, + } + loc := internLoc(locKey) + locs = append(locs, loc) + } + return locs, match + } + + for _, s := range p.Sample { + rootsToAdd, sampleMatchedRoot := makeLabelLocs(s, rootKeys) + if sampleMatchedRoot { + rootm = true + } + leavesToAdd, sampleMatchedLeaf := makeLabelLocs(s, leafKeys) + if sampleMatchedLeaf { + leafm = true + } + + var newLocs []*profile.Location + newLocs = append(newLocs, leavesToAdd...) + newLocs = append(newLocs, s.Location...) + newLocs = append(newLocs, rootsToAdd...) + s.Location = newLocs + } + return +} + +// formatLabelValues returns all the string and numeric labels in Sample, with +// the numeric labels formatted according to outputUnit. +func formatLabelValues(s *profile.Sample, k string, outputUnit string) []string { + var values []string + values = append(values, s.Label[k]...) + numLabels := s.NumLabel[k] + numUnits := s.NumUnit[k] + if len(numLabels) != len(numUnits) && len(numUnits) != 0 { + return values + } + for i, numLabel := range numLabels { + var value string + if len(numUnits) != 0 { + value = measurement.ScaledLabel(numLabel, numUnits[i], outputUnit) + } else { + value = measurement.ScaledLabel(numLabel, "", "") + } + values = append(values, value) + } + return values +} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go index b8e8b50b94d63b..94f32e3755fc9b 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go @@ -15,1390 +15,54 @@ package driver import ( + "embed" + "fmt" "html/template" + "os" - "github.com/google/pprof/third_party/d3" "github.com/google/pprof/third_party/d3flamegraph" ) +//go:embed html +var embeddedFiles embed.FS + // addTemplates adds a set of template definitions to templates. func addTemplates(templates *template.Template) { - template.Must(templates.Parse(`{{define "d3script"}}` + d3.JSSource + `{{end}}`)) - template.Must(templates.Parse(`{{define "d3flamegraphscript"}}` + d3flamegraph.JSSource + `{{end}}`)) - template.Must(templates.Parse(`{{define "d3flamegraphcss"}}` + d3flamegraph.CSSSource + `{{end}}`)) - template.Must(templates.Parse(` -{{define "css"}} - -{{end}} - -{{define "header"}} -
        -
        -

        pprof

        -
        - - - - {{$sampleLen := len .SampleTypes}} - {{if gt $sampleLen 1}} - - {{end}} - - - - - -
        - -
        - -
        - {{.Title}} -
        - {{range .Legend}}
        {{.}}
        {{end}} -
        -
        -
        - -
        - -
        -
        Save options as
        - - {{range .Configs}}{{if .UserConfig}} - - -
        - -
        -
        Delete config
        -
        - -
        - -
        {{range .Errors}}
        {{.}}
        {{end}}
        -{{end}} - -{{define "graph" -}} - - - - - {{.Title}} - {{template "css" .}} - - - {{template "header" .}} -
        - {{.HTMLBody}} -
        - {{template "script" .}} - - - -{{end}} - -{{define "script"}} - -{{end}} - -{{define "top" -}} - - - - - {{.Title}} - {{template "css" .}} - - - - {{template "header" .}} -
        - - - - - - - - - - - - - -
        FlatFlat%Sum%CumCum%NameInlined?
        -
        - {{template "script" .}} - - - -{{end}} - -{{define "sourcelisting" -}} - - - - - {{.Title}} - {{template "css" .}} - {{template "weblistcss" .}} - {{template "weblistjs" .}} - - - {{template "header" .}} -
        - {{.HTMLBody}} -
        - {{template "script" .}} - - - -{{end}} - -{{define "plaintext" -}} - - - - - {{.Title}} - {{template "css" .}} - - - {{template "header" .}} -
        -
        -      {{.TextBody}}
        -    
        -
        - {{template "script" .}} - - - -{{end}} - -{{define "flamegraph" -}} - - - - - {{.Title}} - {{template "css" .}} - - - - - {{template "header" .}} -
        -
        -
        -
        -
        -
        - {{template "script" .}} - - - - - - -{{end}} -`)) + // Load specified file. + loadFile := func(fname string) string { + data, err := embeddedFiles.ReadFile(fname) + if err != nil { + fmt.Fprintf(os.Stderr, "internal/driver: embedded file %q not found\n", + fname) + os.Exit(1) + } + return string(data) + } + loadCSS := func(fname string) string { + return `` + "\n" + } + loadJS := func(fname string) string { + return `` + "\n" + } + + // Define a named template with specified contents. + def := func(name, contents string) { + sub := template.New(name) + template.Must(sub.Parse(contents)) + template.Must(templates.AddParseTree(name, sub.Tree)) + } + + // Pre-packaged third-party files. + def("d3flamegraphscript", d3flamegraph.JSSource) + def("d3flamegraphcss", d3flamegraph.CSSSource) + + // Embeded files. + def("css", loadCSS("html/common.css")) + def("header", loadFile("html/header.html")) + def("graph", loadFile("html/graph.html")) + def("script", loadJS("html/common.js")) + def("top", loadFile("html/top.html")) + def("sourcelisting", loadFile("html/source.html")) + def("plaintext", loadFile("html/plaintext.html")) + def("flamegraph", loadFile("html/flamegraph.html")) } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go index 52dc68809c7765..0f3e8bf93c44fb 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go @@ -127,6 +127,11 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d "/flamegraph": http.HandlerFunc(ui.flamegraph), "/saveconfig": http.HandlerFunc(ui.saveConfig), "/deleteconfig": http.HandlerFunc(ui.deleteConfig), + "/download": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/vnd.google.protobuf+gzip") + w.Header().Set("Content-Disposition", "attachment;filename=profile.pb.gz") + p.Write(w) + }), }, } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go index 2638b2db2d9a95..718481b078856f 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go @@ -165,19 +165,61 @@ func GetBuildID(binary io.ReaderAt) ([]byte, error) { return nil, nil } -// GetBase determines the base address to subtract from virtual -// address to get symbol table address. For an executable, the base -// is 0. Otherwise, it's a shared library, and the base is the -// address where the mapping starts. The kernel is special, and may -// use the address of the _stext symbol as the mmap start. _stext -// offset can be obtained with `nm vmlinux | grep _stext` -func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) { +// kernelBase calculates the base for kernel mappings, which usually require +// special handling. For kernel mappings, tools (like perf) use the address of +// the kernel relocation symbol (_text or _stext) as the mmap start. Additionally, +// for obfuscation, ChromeOS profiles have the kernel image remapped to the 0-th page. +func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, bool) { const ( - pageSize = 4096 // PAGE_OFFSET for PowerPC64, see arch/powerpc/Kconfig in the kernel sources. pageOffsetPpc64 = 0xc000000000000000 + pageSize = 4096 ) + if loadSegment.Vaddr == start-offset { + return offset, true + } + if start == 0 && limit != 0 && stextOffset != nil { + // ChromeOS remaps its kernel to 0. Nothing else should come + // down this path. Empirical values: + // VADDR=0xffffffff80200000 + // stextOffset=0xffffffff80200198 + return start - *stextOffset, true + } + if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) { + // Some kernels look like: + // VADDR=0xffffffff80200000 + // stextOffset=0xffffffff80200198 + // Start=0xffffffff83200000 + // Limit=0xffffffff84200000 + // Offset=0 (0xc000000000000000 for PowerPC64) (== Start for ASLR kernel) + // So the base should be: + if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) { + // perf uses the address of _stext as start. Some tools may + // adjust for this before calling GetBase, in which case the page + // alignment should be different from that of stextOffset. + return start - *stextOffset, true + } + + return start - loadSegment.Vaddr, true + } + if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize { + // ChromeOS remaps its kernel to 0 + start%pageSize. Nothing + // else should come down this path. Empirical values: + // start=0x198 limit=0x2f9fffff offset=0 + // VADDR=0xffffffff81000000 + // stextOffset=0xffffffff81000198 + return start - *stextOffset, true + } + return 0, false +} + +// GetBase determines the base address to subtract from virtual +// address to get symbol table address. For an executable, the base +// is 0. Otherwise, it's a shared library, and the base is the +// address where the mapping starts. The kernel needs special handling. +func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) { + if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) { // Some tools may introduce a fake mapping that spans the entire // address space. Assume that the address has already been @@ -202,43 +244,15 @@ func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint6 // the 64-bit address space. return start - offset + loadSegment.Off - loadSegment.Vaddr, nil } - // Various kernel heuristics and cases follow. - if loadSegment.Vaddr == start-offset { - return offset, nil + // Various kernel heuristics and cases are handled separately. + if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match { + return base, nil } - if start == 0 && limit != 0 { - // ChromeOS remaps its kernel to 0. Nothing else should come - // down this path. Empirical values: - // VADDR=0xffffffff80200000 - // stextOffset=0xffffffff80200198 - if stextOffset != nil { - return -*stextOffset, nil - } - return -loadSegment.Vaddr, nil - } - if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) { - // Some kernels look like: - // VADDR=0xffffffff80200000 - // stextOffset=0xffffffff80200198 - // Start=0xffffffff83200000 - // Limit=0xffffffff84200000 - // Offset=0 (0xc000000000000000 for PowerPC64) (== Start for ASLR kernel) - // So the base should be: - if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) { - // perf uses the address of _stext as start. Some tools may - // adjust for this before calling GetBase, in which case the page - // alignment should be different from that of stextOffset. - return start - *stextOffset, nil - } - + // ChromeOS can remap its kernel to 0, and the caller might have not found + // the _stext symbol. Split this case from kernelBase() above, since we don't + // want to apply it to an ET_DYN user-mode executable. + if start == 0 && limit != 0 && stextOffset == nil { return start - loadSegment.Vaddr, nil - } else if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize { - // ChromeOS remaps its kernel to 0 + start%pageSize. Nothing - // else should come down this path. Empirical values: - // start=0x198 limit=0x2f9fffff offset=0 - // VADDR=0xffffffff81000000 - // stextOffset=0xffffffff81000198 - return start - *stextOffset, nil } return 0, fmt.Errorf("don't know how to handle EXEC segment: %v start=0x%x limit=0x%x offset=0x%x", *loadSegment, start, limit, offset) @@ -255,6 +269,11 @@ func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint6 if loadSegment == nil { return start - offset, nil } + // Kernels compiled as PIE can be ET_DYN as well. Use heuristic, similar to + // the ET_EXEC case above. + if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match { + return base, nil + } // The program header, if not nil, indicates the offset in the file where // the executable segment is located (loadSegment.Off), and the base virtual // address where the first byte of the segment is loaded @@ -284,10 +303,16 @@ func FindTextProgHeader(f *elf.File) *elf.ProgHeader { return nil } -// ProgramHeadersForMapping returns the loadable program segment headers that -// are fully contained in the runtime mapping with file offset pgoff and memory -// size memsz, and if the binary includes any loadable segments. -func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHeader, bool) { +// ProgramHeadersForMapping returns the program segment headers that overlap +// the runtime mapping with file offset mapOff and memory size mapSz. We skip +// over segments zero file size because their file offset values are unreliable. +// Even if overlapping, a segment is not selected if its aligned file offset is +// greater than the mapping file offset, or if the mapping includes the last +// page of the segment, but not the full segment and the mapping includes +// additional pages after the segment end. +// The function returns a slice of pointers to the headers in the input +// slice, which are valid only while phdrs is not modified or discarded. +func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint64) []*elf.ProgHeader { const ( // pageSize defines the virtual memory page size used by the loader. This // value is dependent on the memory management unit of the CPU. The page @@ -298,57 +323,61 @@ func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHead // specified in the ELF file header. pageSize = 4096 pageOffsetMask = pageSize - 1 - pageMask = ^uint64(pageOffsetMask) ) + mapLimit := mapOff + mapSz var headers []*elf.ProgHeader - hasLoadables := false - for _, p := range f.Progs { - // The segment must be fully included in the mapping. - if p.Type == elf.PT_LOAD && pgoff <= p.Off && p.Off+p.Memsz <= pgoff+memsz { - alignedOffset := uint64(0) + for i := range phdrs { + p := &phdrs[i] + // Skip over segments with zero file size. Their file offsets can have + // arbitrary values, see b/195427553. + if p.Filesz == 0 { + continue + } + segLimit := p.Off + p.Memsz + // The segment must overlap the mapping. + if p.Type == elf.PT_LOAD && mapOff < segLimit && p.Off < mapLimit { + // If the mapping offset is strictly less than the page aligned segment + // offset, then this mapping comes from a different segment, fixes + // b/179920361. + alignedSegOffset := uint64(0) if p.Off > (p.Vaddr & pageOffsetMask) { - alignedOffset = p.Off - (p.Vaddr & pageOffsetMask) + alignedSegOffset = p.Off - (p.Vaddr & pageOffsetMask) } - if alignedOffset <= pgoff { - headers = append(headers, &p.ProgHeader) + if mapOff < alignedSegOffset { + continue } + // If the mapping starts in the middle of the segment, it covers less than + // one page of the segment, and it extends at least one page past the + // segment, then this mapping comes from a different segment. + if mapOff > p.Off && (segLimit < mapOff+pageSize) && (mapLimit >= segLimit+pageSize) { + continue + } + headers = append(headers, p) } - if p.Type == elf.PT_LOAD { - hasLoadables = true - } - } - if len(headers) < 2 { - return headers, hasLoadables - } - - // If we have more than one matching segments, try a strict check on the - // segment memory size. We use a heuristic to compute the minimum mapping size - // required for a segment, assuming mappings are page aligned. - // The memory size based heuristic makes sense only if the mapping size is a - // multiple of page size. - if memsz%pageSize != 0 { - return headers, hasLoadables } + return headers +} - // Return all found headers if we cannot narrow the selection to a single - // program segment. +// HeaderForFileOffset attempts to identify a unique program header that +// includes the given file offset. It returns an error if it cannot identify a +// unique header. +func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) { var ph *elf.ProgHeader for _, h := range headers { - wantSize := (h.Vaddr+h.Memsz+pageSize-1)&pageMask - (h.Vaddr & pageMask) - if wantSize != memsz { - continue - } - if ph != nil { - // Found a second program header matching, so return all previously - // identified headers. - return headers, hasLoadables + if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz { + if ph != nil { + // Assuming no other bugs, this can only happen if we have two or + // more small program segments that fit on the same page, and a + // segment other than the last one includes uninitialized data, or + // if the debug binary used for symbolization is stripped of some + // sections, so segment file sizes are smaller than memory sizes. + return nil, fmt.Errorf("found second program header (%#v) that matches file offset %x, first program header is %#v. Is this a stripped binary, or does the first program segment contain uninitialized data?", *h, fileOffset, *ph) + } + ph = h } - ph = h } if ph == nil { - // No matching header for the strict check. Return all previously identified - // headers. - return headers, hasLoadables + return nil, fmt.Errorf("no program header matches file offset %x", fileOffset) } - return []*elf.ProgHeader{ph}, hasLoadables + return ph, nil } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go index 800867524848d4..09d40fd2c99017 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/graph/dotgraph.go @@ -126,7 +126,7 @@ func (b *builder) addLegend() { return } title := labels[0] - fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16`, title) + fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16`, escapeForDot(title)) fmt.Fprintf(b, ` label="%s\l"`, strings.Join(escapeAllForDot(labels), `\l`)) if b.config.LegendURL != "" { fmt.Fprintf(b, ` URL="%s" target="_blank"`, b.config.LegendURL) @@ -385,6 +385,9 @@ func multilinePrintableName(info *NodeInfo) string { infoCopy := *info infoCopy.Name = escapeForDot(ShortenFunctionName(infoCopy.Name)) infoCopy.Name = strings.Replace(infoCopy.Name, "::", `\n`, -1) + // Go type parameters are reported as "[...]" by Go pprof profiles. + // Keep this ellipsis rather than replacing with newlines below. + infoCopy.Name = strings.Replace(infoCopy.Name, "[...]", "[…]", -1) infoCopy.Name = strings.Replace(infoCopy.Name, ".", `\n`, -1) if infoCopy.File != "" { infoCopy.File = filepath.Base(infoCopy.File) @@ -485,7 +488,7 @@ func escapeAllForDot(in []string) []string { // escapeForDot escapes double quotes and backslashes, and replaces Graphviz's // "center" character (\n) with a left-justified character. -// See https://graphviz.org/doc/info/attrs.html#k:escString for more info. +// See https://graphviz.org/docs/attr-types/escString/ for more info. func escapeForDot(str string) string { return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(str, `\`, `\\`), `"`, `\"`), "\n", `\l`) } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/measurement/measurement.go b/src/cmd/vendor/github.com/google/pprof/internal/measurement/measurement.go index 53325740a3ed84..b5fcfbc3e463f9 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/measurement/measurement.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/measurement/measurement.go @@ -110,10 +110,15 @@ func compatibleValueTypes(v1, v2 *profile.ValueType) bool { return false } - return v1.Unit == v2.Unit || - (timeUnits.sniffUnit(v1.Unit) != nil && timeUnits.sniffUnit(v2.Unit) != nil) || - (memoryUnits.sniffUnit(v1.Unit) != nil && memoryUnits.sniffUnit(v2.Unit) != nil) || - (gcuUnits.sniffUnit(v1.Unit) != nil && gcuUnits.sniffUnit(v2.Unit) != nil) + if v1.Unit == v2.Unit { + return true + } + for _, ut := range unitTypes { + if ut.sniffUnit(v1.Unit) != nil && ut.sniffUnit(v2.Unit) != nil { + return true + } + } + return false } // Scale a measurement from an unit to a different unit and returns @@ -125,14 +130,10 @@ func Scale(value int64, fromUnit, toUnit string) (float64, string) { v, u := Scale(-value, fromUnit, toUnit) return -v, u } - if m, u, ok := memoryUnits.convertUnit(value, fromUnit, toUnit); ok { - return m, u - } - if t, u, ok := timeUnits.convertUnit(value, fromUnit, toUnit); ok { - return t, u - } - if g, u, ok := gcuUnits.convertUnit(value, fromUnit, toUnit); ok { - return g, u + for _, ut := range unitTypes { + if v, u, ok := ut.convertUnit(value, fromUnit, toUnit); ok { + return v, u + } } // Skip non-interesting units. switch toUnit { @@ -257,7 +258,7 @@ func (ut unitType) convertUnit(value int64, fromUnitStr, toUnitStr string) (floa return v / toUnit.factor, toUnit.canonicalName, true } -var memoryUnits = unitType{ +var unitTypes = []unitType{{ units: []unit{ {"B", []string{"b", "byte"}, 1}, {"kB", []string{"kb", "kbyte", "kilobyte"}, float64(1 << 10)}, @@ -267,9 +268,7 @@ var memoryUnits = unitType{ {"PB", []string{"pb", "pbyte", "petabyte"}, float64(1 << 50)}, }, defaultUnit: unit{"B", []string{"b", "byte"}, 1}, -} - -var timeUnits = unitType{ +}, { units: []unit{ {"ns", []string{"ns", "nanosecond"}, float64(time.Nanosecond)}, {"us", []string{"μs", "us", "microsecond"}, float64(time.Microsecond)}, @@ -278,9 +277,7 @@ var timeUnits = unitType{ {"hrs", []string{"hour", "hr"}, float64(time.Hour)}, }, defaultUnit: unit{"s", []string{}, float64(time.Second)}, -} - -var gcuUnits = unitType{ +}, { units: []unit{ {"n*GCU", []string{"nanogcu"}, 1e-9}, {"u*GCU", []string{"microgcu"}, 1e-6}, @@ -293,4 +290,4 @@ var gcuUnits = unitType{ {"P*GCU", []string{"petagcu"}, 1e15}, }, defaultUnit: unit{"GCU", []string{}, 1.0}, -} +}} diff --git a/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go b/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go index a57a0b20a96256..98eb1dd8179bb0 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go @@ -109,8 +109,10 @@ type MappingSources map[string][]struct { type ObjTool interface { // Open opens the named object file. If the object is a shared // library, start/limit/offset are the addresses where it is mapped - // into memory in the address space being inspected. - Open(file string, start, limit, offset uint64) (ObjFile, error) + // into memory in the address space being inspected. If the object + // is a linux kernel, relocationSymbol is the name of the symbol + // corresponding to the start address. + Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error) // Disasm disassembles the named object file, starting at // the start address and stopping at (before) the end address. diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go index 4a86554880132d..36ddf2e934efd6 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go @@ -432,6 +432,10 @@ func PrintAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFuncs int) e } } + if len(syms) == 0 { + return fmt.Errorf("no matches found for regexp: %s", o.Symbol) + } + // Correlate the symbols from the binary with the profile samples. for _, s := range syms { sns := symNodes[s] @@ -522,7 +526,7 @@ func symbolsFromBinaries(prof *profile.Profile, g *graph.Graph, rx *regexp.Regex } } - f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset) + f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol) if err != nil { fmt.Printf("%v\n", err) continue @@ -1054,6 +1058,7 @@ func printTree(w io.Writer, rpt *Report) error { var flatSum int64 rx := rpt.options.Symbol + matched := 0 for _, n := range g.Nodes { name, flat, cum := n.Info.PrintableName(), n.FlatValue(), n.CumValue() @@ -1061,6 +1066,7 @@ func printTree(w io.Writer, rpt *Report) error { if rx != nil && !rx.MatchString(name) { continue } + matched++ fmt.Fprintln(w, separator) // Print incoming edges. @@ -1098,6 +1104,9 @@ func printTree(w io.Writer, rpt *Report) error { if len(g.Nodes) > 0 { fmt.Fprintln(w, separator) } + if rx != nil && matched == 0 { + return fmt.Errorf("no matches found for regexp: %s", rx) + } return nil } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/source.go b/src/cmd/vendor/github.com/google/pprof/internal/report/source.go index 54245e5f9ea6a0..d8b43952656345 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/source.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/source.go @@ -58,6 +58,10 @@ func printSource(w io.Writer, rpt *Report) error { } functions.Sort(graph.NameOrder) + if len(functionNodes) == 0 { + return fmt.Errorf("no matches found for regexp: %s", o.Symbol) + } + sourcePath := o.SourcePath if sourcePath == "" { wd, err := os.Getwd() @@ -206,6 +210,9 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er sourcePath = wd } sp := newSourcePrinter(rpt, obj, sourcePath) + if len(sp.interest) == 0 { + return fmt.Errorf("no matches found for regexp: %s", rpt.options.Symbol) + } sp.print(w, maxFiles, rpt) sp.close() return nil @@ -299,7 +306,7 @@ func newSourcePrinter(rpt *Report, obj plugin.ObjTool, sourcePath string) *sourc continue } - // Seach in inlined stack for a match. + // Search in inlined stack for a match. matchFile := (loc.Mapping != nil && sp.sym.MatchString(loc.Mapping.File)) for j, line := range loc.Line { if (j == 0 && matchFile) || matches(line) { @@ -737,7 +744,7 @@ func (sp *sourcePrinter) objectFile(m *profile.Mapping) plugin.ObjFile { if object, ok := sp.objects[m.File]; ok { return object // May be nil if we detected an error earlier. } - object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset) + object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol) if err != nil { object = nil } diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go b/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go index 17c9f6eb947c51..851693f1d0e6d7 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go @@ -72,5 +72,4 @@ function pprof_toggle_asm(e) { const weblistPageClosing = ` - -` +` diff --git a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go index d741e7ad7fa7b6..d243b800a92fc8 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go @@ -205,49 +205,64 @@ func Demangle(prof *profile.Profile, force bool, demanglerMode string) { } } - var options []demangle.Option + options := demanglerModeToOptions(demanglerMode) + for _, fn := range prof.Function { + demangleSingleFunction(fn, options) + } +} + +func demanglerModeToOptions(demanglerMode string) []demangle.Option { switch demanglerMode { case "": // demangled, simplified: no parameters, no templates, no return type - options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams} + return []demangle.Option{demangle.NoParams, demangle.NoTemplateParams} case "templates": // demangled, simplified: no parameters, no return type - options = []demangle.Option{demangle.NoParams} + return []demangle.Option{demangle.NoParams} case "full": - options = []demangle.Option{demangle.NoClones} + return []demangle.Option{demangle.NoClones} case "none": // no demangling - return + return []demangle.Option{} } + panic(fmt.Sprintf("unknown demanglerMode %s", demanglerMode)) +} + +func demangleSingleFunction(fn *profile.Function, options []demangle.Option) { + if fn.Name != "" && fn.SystemName != fn.Name { + return // Already demangled. + } // Copy the options because they may be updated by the call. o := make([]demangle.Option, len(options)) - for _, fn := range prof.Function { - if fn.Name != "" && fn.SystemName != fn.Name { - continue // Already demangled. - } - copy(o, options) - if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName { - fn.Name = demangled - continue - } - // Could not demangle. Apply heuristics in case the name is - // already demangled. - name := fn.SystemName - if looksLikeDemangledCPlusPlus(name) { - if demanglerMode == "" || demanglerMode == "templates" { + copy(o, options) + if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName { + fn.Name = demangled + return + } + // Could not demangle. Apply heuristics in case the name is + // already demangled. + name := fn.SystemName + if looksLikeDemangledCPlusPlus(name) { + for _, o := range options { + switch o { + case demangle.NoParams: name = removeMatching(name, '(', ')') - } - if demanglerMode == "" { + case demangle.NoTemplateParams: name = removeMatching(name, '<', '>') } } - fn.Name = name } + fn.Name = name } // looksLikeDemangledCPlusPlus is a heuristic to decide if a name is // the result of demangling C++. If so, further heuristics will be // applied to simplify the name. func looksLikeDemangledCPlusPlus(demangled string) bool { - if strings.Contains(demangled, ".<") { // Skip java names of the form "class." + // Skip java names of the form "class.". + if strings.Contains(demangled, ".<") { + return false + } + // Skip Go names of the form "foo.(*Bar[...]).Method". + if strings.Contains(demangled, "]).") { return false } return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::") @@ -325,7 +340,10 @@ func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force b } name := filepath.Base(m.File) - f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset) + if m.BuildID != "" { + name += fmt.Sprintf(" (build ID %s)", m.BuildID) + } + f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol) if err != nil { ui.PrintErr("Local symbolization failed for ", name, ": ", err) missingBinaries = true diff --git a/src/cmd/vendor/github.com/google/pprof/profile/encode.go b/src/cmd/vendor/github.com/google/pprof/profile/encode.go index ab7f03ae2677b4..96aa271e54fb75 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/encode.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/encode.go @@ -17,6 +17,7 @@ package profile import ( "errors" "sort" + "strings" ) func (p *Profile) decoder() []decoder { @@ -252,6 +253,14 @@ func (p *Profile) postDecode() error { } else { mappings[m.ID] = m } + + // If this a main linux kernel mapping with a relocation symbol suffix + // ("[kernel.kallsyms]_text"), extract said suffix. + // It is fairly hacky to handle at this level, but the alternatives appear even worse. + if strings.HasPrefix(m.File, "[kernel.kallsyms]") { + m.KernelRelocationSymbol = strings.ReplaceAll(m.File, "[kernel.kallsyms]", "") + } + } functions := make(map[uint64]*Function, len(p.Function)) diff --git a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go index 0c8f3bb5b71f45..9ba9a77c924f82 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile.go @@ -295,11 +295,12 @@ func get64b(b []byte) (uint64, []byte) { // // The general format for profilez samples is a sequence of words in // binary format. The first words are a header with the following data: -// 1st word -- 0 -// 2nd word -- 3 -// 3rd word -- 0 if a c++ application, 1 if a java application. -// 4th word -- Sampling period (in microseconds). -// 5th word -- Padding. +// +// 1st word -- 0 +// 2nd word -- 3 +// 3rd word -- 0 if a c++ application, 1 if a java application. +// 4th word -- Sampling period (in microseconds). +// 5th word -- Padding. func parseCPU(b []byte) (*Profile, error) { var parse func([]byte) (uint64, []byte) var n1, n2, n3, n4, n5 uint64 @@ -403,15 +404,18 @@ func cleanupDuplicateLocations(p *Profile) { // // profilez samples are a repeated sequence of stack frames of the // form: -// 1st word -- The number of times this stack was encountered. -// 2nd word -- The size of the stack (StackSize). -// 3rd word -- The first address on the stack. -// ... -// StackSize + 2 -- The last address on the stack +// +// 1st word -- The number of times this stack was encountered. +// 2nd word -- The size of the stack (StackSize). +// 3rd word -- The first address on the stack. +// ... +// StackSize + 2 -- The last address on the stack +// // The last stack trace is of the form: -// 1st word -- 0 -// 2nd word -- 1 -// 3rd word -- 0 +// +// 1st word -- 0 +// 2nd word -- 1 +// 3rd word -- 0 // // Addresses from stack traces may point to the next instruction after // each call. Optionally adjust by -1 to land somewhere on the actual diff --git a/src/cmd/vendor/github.com/google/pprof/profile/merge.go b/src/cmd/vendor/github.com/google/pprof/profile/merge.go index 9978e7330e6e6f..6fcd11de19a539 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/merge.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/merge.go @@ -303,16 +303,17 @@ func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { return mi } m := &Mapping{ - ID: uint64(len(pm.p.Mapping) + 1), - Start: src.Start, - Limit: src.Limit, - Offset: src.Offset, - File: src.File, - BuildID: src.BuildID, - HasFunctions: src.HasFunctions, - HasFilenames: src.HasFilenames, - HasLineNumbers: src.HasLineNumbers, - HasInlineFrames: src.HasInlineFrames, + ID: uint64(len(pm.p.Mapping) + 1), + Start: src.Start, + Limit: src.Limit, + Offset: src.Offset, + File: src.File, + KernelRelocationSymbol: src.KernelRelocationSymbol, + BuildID: src.BuildID, + HasFunctions: src.HasFunctions, + HasFilenames: src.HasFilenames, + HasLineNumbers: src.HasLineNumbers, + HasInlineFrames: src.HasInlineFrames, } pm.p.Mapping = append(pm.p.Mapping, m) diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile.go b/src/cmd/vendor/github.com/google/pprof/profile/profile.go index 2590c8ddb42e25..5a3807f978eb6c 100644 --- a/src/cmd/vendor/github.com/google/pprof/profile/profile.go +++ b/src/cmd/vendor/github.com/google/pprof/profile/profile.go @@ -106,6 +106,15 @@ type Mapping struct { fileX int64 buildIDX int64 + + // Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File. + // For linux kernel mappings generated by some tools, correct symbolization depends + // on knowing which of the two possible relocation symbols was used for `Start`. + // This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext"). + // + // Note, this public field is not persisted in the proto. For the purposes of + // copying / merging / hashing profiles, it is considered subsumed by `File`. + KernelRelocationSymbol string } // Location corresponds to Profile.Location diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3/LICENSE b/src/cmd/vendor/github.com/google/pprof/third_party/d3/LICENSE deleted file mode 100644 index 1d9d875edb4697..00000000000000 --- a/src/cmd/vendor/github.com/google/pprof/third_party/d3/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2010-2017 Mike Bostock -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of contributors may be used to - endorse or promote products derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3/README.md b/src/cmd/vendor/github.com/google/pprof/third_party/d3/README.md deleted file mode 100644 index 53e6eb61eba7d2..00000000000000 --- a/src/cmd/vendor/github.com/google/pprof/third_party/d3/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# Building a customized D3.js bundle - -The D3.js version distributed with pprof is customized to only include the modules required by pprof. - -## Dependencies - -First, it's necessary to pull all bundle dependencies. We will use a JavaScript package manager, [npm](https://www.npmjs.com/), to accomplish that. npm dependencies are declared in a `package.json` file, so create one with the following configuration: - -```js -{ - "name": "d3-pprof", - "version": "1.0.0", - "description": "A d3.js bundle for pprof.", - "scripts": { - "prepare": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js" - }, - "license": "Apache-2.0", - "devDependencies": { - "d3-selection": "1.1.0", - "d3-hierarchy": "1.1.5", - "d3-scale": "1.0.6", - "d3-format": "1.2.0", - "d3-ease": "1.0.3", - "d3-array": "1.2.1", - "d3-collection": "1.0.4", - "d3-transition": "1.1.0", - "rollup": "0.51.8", - "rollup-plugin-node-resolve": "3", - "uglify-js": "3.1.10" - } -} -``` - -Besides the bundle dependencies, the `package.json` file also specifies a script called `prepare`, which will be executed to create the bundle after `Rollup` is installed. - -## Bundler - -The simplest way of creating a custom bundle is to use a bundler, such as [Rollup](https://rollupjs.org/) or [Webpack](https://webpack.js.org/). Rollup will be used in this example. - -First, create a `rollup.config.js` file, containing the configuration Rollup should use to build the bundle. - -```js -import node from "rollup-plugin-node-resolve"; - -export default { - input: "index.js", - output: { - format: "umd", - file: "d3.js" - }, - name: "d3", - plugins: [node()], - sourcemap: false -}; -``` - -Then create an `index.js` file containing all the functions that need to be exported in the bundle. - -```js -export { - select, - selection, - event, -} from "d3-selection"; - -export { - hierarchy, - partition, -} from "d3-hierarchy"; - -export { - scaleLinear, -} from "d3-scale"; - -export { - format, -} from "d3-format"; - -export { - easeCubic, -} from "d3-ease"; - -export { - ascending, -} from "d3-array"; - -export { - map, -} from "d3-collection"; - -export { - transition, -} from "d3-transition"; -``` - -## Building - -Once all files were created, execute the following commands to pull all dependencies and build the bundle. - -``` -% npm install -% npm run prepare -``` - -This will create two files, `d3.js` and `d3.min.js`, the custom D3.js bundle and its minified version respectively. - -# References - -## D3 Custom Bundle - -A demonstration of building a custom D3 4.0 bundle using ES2015 modules and Rollup. - -[bl.ocks.org/mbostock/bb09af4c39c79cffcde4](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4) - -## d3-pprof - -A repository containing all previously mentioned configuration files and the generated custom bundle. - -[github.com/spiermar/d3-pprof](https://github.com/spiermar/d3-pprof) \ No newline at end of file diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3/d3.go b/src/cmd/vendor/github.com/google/pprof/third_party/d3/d3.go deleted file mode 100644 index 7d217c71bf3803..00000000000000 --- a/src/cmd/vendor/github.com/google/pprof/third_party/d3/d3.go +++ /dev/null @@ -1,4675 +0,0 @@ -// D3.js is a JavaScript library for manipulating documents based on data. -// https://github.com/d3/d3 -// See LICENSE file for license details -// Custom build for pprof (https://github.com/spiermar/d3-pprof) - -package d3 - -// JSSource returns the d3.js file -const JSSource = ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.d3 = {}))); -}(this, (function (exports) { 'use strict'; - -var xhtml = "http://www.w3.org/1999/xhtml"; - -var namespaces = { - svg: "http://www.w3.org/2000/svg", - xhtml: xhtml, - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" -}; - -var namespace = function(name) { - var prefix = name += "", i = prefix.indexOf(":"); - if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); - return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; -}; - -function creatorInherit(name) { - return function() { - var document = this.ownerDocument, - uri = this.namespaceURI; - return uri === xhtml && document.documentElement.namespaceURI === xhtml - ? document.createElement(name) - : document.createElementNS(uri, name); - }; -} - -function creatorFixed(fullname) { - return function() { - return this.ownerDocument.createElementNS(fullname.space, fullname.local); - }; -} - -var creator = function(name) { - var fullname = namespace(name); - return (fullname.local - ? creatorFixed - : creatorInherit)(fullname); -}; - -var matcher = function(selector) { - return function() { - return this.matches(selector); - }; -}; - -if (typeof document !== "undefined") { - var element = document.documentElement; - if (!element.matches) { - var vendorMatches = element.webkitMatchesSelector - || element.msMatchesSelector - || element.mozMatchesSelector - || element.oMatchesSelector; - matcher = function(selector) { - return function() { - return vendorMatches.call(this, selector); - }; - }; - } -} - -var matcher$1 = matcher; - -var filterEvents = {}; - -exports.event = null; - -if (typeof document !== "undefined") { - var element$1 = document.documentElement; - if (!("onmouseenter" in element$1)) { - filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; - } -} - -function filterContextListener(listener, index, group) { - listener = contextListener(listener, index, group); - return function(event) { - var related = event.relatedTarget; - if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { - listener.call(this, event); - } - }; -} - -function contextListener(listener, index, group) { - return function(event1) { - var event0 = exports.event; // Events can be reentrant (e.g., focus). - exports.event = event1; - try { - listener.call(this, this.__data__, index, group); - } finally { - exports.event = event0; - } - }; -} - -function parseTypenames(typenames) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - return {type: t, name: name}; - }); -} - -function onRemove(typename) { - return function() { - var on = this.__on; - if (!on) return; - for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { - if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - } else { - on[++i] = o; - } - } - if (++i) on.length = i; - else delete this.__on; - }; -} - -function onAdd(typename, value, capture) { - var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; - return function(d, i, group) { - var on = this.__on, o, listener = wrap(value, i, group); - if (on) for (var j = 0, m = on.length; j < m; ++j) { - if ((o = on[j]).type === typename.type && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.capture); - this.addEventListener(o.type, o.listener = listener, o.capture = capture); - o.value = value; - return; - } - } - this.addEventListener(typename.type, listener, capture); - o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; - if (!on) this.__on = [o]; - else on.push(o); - }; -} - -var selection_on = function(typename, value, capture) { - var typenames = parseTypenames(typename + ""), i, n = typenames.length, t; - - if (arguments.length < 2) { - var on = this.node().__on; - if (on) for (var j = 0, m = on.length, o; j < m; ++j) { - for (i = 0, o = on[j]; i < n; ++i) { - if ((t = typenames[i]).type === o.type && t.name === o.name) { - return o.value; - } - } - } - return; - } - - on = value ? onAdd : onRemove; - if (capture == null) capture = false; - for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); - return this; -}; - -function none() {} - -var selector = function(selector) { - return selector == null ? none : function() { - return this.querySelector(selector); - }; -}; - -var selection_select = function(select) { - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - } - } - } - - return new Selection(subgroups, this._parents); -}; - -function empty() { - return []; -} - -var selectorAll = function(selector) { - return selector == null ? empty : function() { - return this.querySelectorAll(selector); - }; -}; - -var selection_selectAll = function(select) { - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - subgroups.push(select.call(node, node.__data__, i, group)); - parents.push(node); - } - } - } - - return new Selection(subgroups, parents); -}; - -var selection_filter = function(match) { - if (typeof match !== "function") match = matcher$1(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Selection(subgroups, this._parents); -}; - -var sparse = function(update) { - return new Array(update.length); -}; - -var selection_enter = function() { - return new Selection(this._enter || this._groups.map(sparse), this._parents); -}; - -function EnterNode(parent, datum) { - this.ownerDocument = parent.ownerDocument; - this.namespaceURI = parent.namespaceURI; - this._next = null; - this._parent = parent; - this.__data__ = datum; -} - -EnterNode.prototype = { - constructor: EnterNode, - appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, - insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, - querySelector: function(selector) { return this._parent.querySelector(selector); }, - querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } -}; - -var constant = function(x) { - return function() { - return x; - }; -}; - -var keyPrefix = "$"; // Protect against keys like “__proto__”. - -function bindIndex(parent, group, enter, update, exit, data) { - var i = 0, - node, - groupLength = group.length, - dataLength = data.length; - - // Put any non-null nodes that fit into update. - // Put any null nodes into enter. - // Put any remaining data into enter. - for (; i < dataLength; ++i) { - if (node = group[i]) { - node.__data__ = data[i]; - update[i] = node; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Put any non-null nodes that don’t fit into exit. - for (; i < groupLength; ++i) { - if (node = group[i]) { - exit[i] = node; - } - } -} - -function bindKey(parent, group, enter, update, exit, data, key) { - var i, - node, - nodeByKeyValue = {}, - groupLength = group.length, - dataLength = data.length, - keyValues = new Array(groupLength), - keyValue; - - // Compute the key for each node. - // If multiple nodes have the same key, the duplicates are added to exit. - for (i = 0; i < groupLength; ++i) { - if (node = group[i]) { - keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); - if (keyValue in nodeByKeyValue) { - exit[i] = node; - } else { - nodeByKeyValue[keyValue] = node; - } - } - } - - // Compute the key for each datum. - // If there a node associated with this key, join and add it to update. - // If there is not (or the key is a duplicate), add it to enter. - for (i = 0; i < dataLength; ++i) { - keyValue = keyPrefix + key.call(parent, data[i], i, data); - if (node = nodeByKeyValue[keyValue]) { - update[i] = node; - node.__data__ = data[i]; - nodeByKeyValue[keyValue] = null; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Add any remaining nodes that were not bound to data to exit. - for (i = 0; i < groupLength; ++i) { - if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { - exit[i] = node; - } - } -} - -var selection_data = function(value, key) { - if (!value) { - data = new Array(this.size()), j = -1; - this.each(function(d) { data[++j] = d; }); - return data; - } - - var bind = key ? bindKey : bindIndex, - parents = this._parents, - groups = this._groups; - - if (typeof value !== "function") value = constant(value); - - for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { - var parent = parents[j], - group = groups[j], - groupLength = group.length, - data = value.call(parent, parent && parent.__data__, j, parents), - dataLength = data.length, - enterGroup = enter[j] = new Array(dataLength), - updateGroup = update[j] = new Array(dataLength), - exitGroup = exit[j] = new Array(groupLength); - - bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); - - // Now connect the enter nodes to their following update node, such that - // appendChild can insert the materialized enter node before this node, - // rather than at the end of the parent node. - for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { - if (previous = enterGroup[i0]) { - if (i0 >= i1) i1 = i0 + 1; - while (!(next = updateGroup[i1]) && ++i1 < dataLength); - previous._next = next || null; - } - } - } - - update = new Selection(update, parents); - update._enter = enter; - update._exit = exit; - return update; -}; - -var selection_exit = function() { - return new Selection(this._exit || this._groups.map(sparse), this._parents); -}; - -var selection_merge = function(selection$$1) { - - for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Selection(merges, this._parents); -}; - -var selection_order = function() { - - for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { - for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - - return this; -}; - -var selection_sort = function(compare) { - if (!compare) compare = ascending; - - function compareNode(a, b) { - return a && b ? compare(a.__data__, b.__data__) : !a - !b; - } - - for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group[i]) { - sortgroup[i] = node; - } - } - sortgroup.sort(compareNode); - } - - return new Selection(sortgroups, this._parents).order(); -}; - -function ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -} - -var selection_call = function() { - var callback = arguments[0]; - arguments[0] = this; - callback.apply(null, arguments); - return this; -}; - -var selection_nodes = function() { - var nodes = new Array(this.size()), i = -1; - this.each(function() { nodes[++i] = this; }); - return nodes; -}; - -var selection_node = function() { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { - var node = group[i]; - if (node) return node; - } - } - - return null; -}; - -var selection_size = function() { - var size = 0; - this.each(function() { ++size; }); - return size; -}; - -var selection_empty = function() { - return !this.node(); -}; - -var selection_each = function(callback) { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { - if (node = group[i]) callback.call(node, node.__data__, i, group); - } - } - - return this; -}; - -function attrRemove(name) { - return function() { - this.removeAttribute(name); - }; -} - -function attrRemoveNS(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; -} - -function attrConstant(name, value) { - return function() { - this.setAttribute(name, value); - }; -} - -function attrConstantNS(fullname, value) { - return function() { - this.setAttributeNS(fullname.space, fullname.local, value); - }; -} - -function attrFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttribute(name); - else this.setAttribute(name, v); - }; -} - -function attrFunctionNS(fullname, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttributeNS(fullname.space, fullname.local); - else this.setAttributeNS(fullname.space, fullname.local, v); - }; -} - -var selection_attr = function(name, value) { - var fullname = namespace(name); - - if (arguments.length < 2) { - var node = this.node(); - return fullname.local - ? node.getAttributeNS(fullname.space, fullname.local) - : node.getAttribute(fullname); - } - - return this.each((value == null - ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" - ? (fullname.local ? attrFunctionNS : attrFunction) - : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); -}; - -var defaultView = function(node) { - return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node - || (node.document && node) // node is a Window - || node.defaultView; // node is a Document -}; - -function styleRemove(name) { - return function() { - this.style.removeProperty(name); - }; -} - -function styleConstant(name, value, priority) { - return function() { - this.style.setProperty(name, value, priority); - }; -} - -function styleFunction(name, value, priority) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.style.removeProperty(name); - else this.style.setProperty(name, v, priority); - }; -} - -var selection_style = function(name, value, priority) { - return arguments.length > 1 - ? this.each((value == null - ? styleRemove : typeof value === "function" - ? styleFunction - : styleConstant)(name, value, priority == null ? "" : priority)) - : styleValue(this.node(), name); -}; - -function styleValue(node, name) { - return node.style.getPropertyValue(name) - || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); -} - -function propertyRemove(name) { - return function() { - delete this[name]; - }; -} - -function propertyConstant(name, value) { - return function() { - this[name] = value; - }; -} - -function propertyFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) delete this[name]; - else this[name] = v; - }; -} - -var selection_property = function(name, value) { - return arguments.length > 1 - ? this.each((value == null - ? propertyRemove : typeof value === "function" - ? propertyFunction - : propertyConstant)(name, value)) - : this.node()[name]; -}; - -function classArray(string) { - return string.trim().split(/^|\s+/); -} - -function classList(node) { - return node.classList || new ClassList(node); -} - -function ClassList(node) { - this._node = node; - this._names = classArray(node.getAttribute("class") || ""); -} - -ClassList.prototype = { - add: function(name) { - var i = this._names.indexOf(name); - if (i < 0) { - this._names.push(name); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - remove: function(name) { - var i = this._names.indexOf(name); - if (i >= 0) { - this._names.splice(i, 1); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - contains: function(name) { - return this._names.indexOf(name) >= 0; - } -}; - -function classedAdd(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.add(names[i]); -} - -function classedRemove(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.remove(names[i]); -} - -function classedTrue(names) { - return function() { - classedAdd(this, names); - }; -} - -function classedFalse(names) { - return function() { - classedRemove(this, names); - }; -} - -function classedFunction(names, value) { - return function() { - (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); - }; -} - -var selection_classed = function(name, value) { - var names = classArray(name + ""); - - if (arguments.length < 2) { - var list = classList(this.node()), i = -1, n = names.length; - while (++i < n) if (!list.contains(names[i])) return false; - return true; - } - - return this.each((typeof value === "function" - ? classedFunction : value - ? classedTrue - : classedFalse)(names, value)); -}; - -function textRemove() { - this.textContent = ""; -} - -function textConstant(value) { - return function() { - this.textContent = value; - }; -} - -function textFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - }; -} - -var selection_text = function(value) { - return arguments.length - ? this.each(value == null - ? textRemove : (typeof value === "function" - ? textFunction - : textConstant)(value)) - : this.node().textContent; -}; - -function htmlRemove() { - this.innerHTML = ""; -} - -function htmlConstant(value) { - return function() { - this.innerHTML = value; - }; -} - -function htmlFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - }; -} - -var selection_html = function(value) { - return arguments.length - ? this.each(value == null - ? htmlRemove : (typeof value === "function" - ? htmlFunction - : htmlConstant)(value)) - : this.node().innerHTML; -}; - -function raise() { - if (this.nextSibling) this.parentNode.appendChild(this); -} - -var selection_raise = function() { - return this.each(raise); -}; - -function lower() { - if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); -} - -var selection_lower = function() { - return this.each(lower); -}; - -var selection_append = function(name) { - var create = typeof name === "function" ? name : creator(name); - return this.select(function() { - return this.appendChild(create.apply(this, arguments)); - }); -}; - -function constantNull() { - return null; -} - -var selection_insert = function(name, before) { - var create = typeof name === "function" ? name : creator(name), - select = before == null ? constantNull : typeof before === "function" ? before : selector(before); - return this.select(function() { - return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); - }); -}; - -function remove() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); -} - -var selection_remove = function() { - return this.each(remove); -}; - -var selection_datum = function(value) { - return arguments.length - ? this.property("__data__", value) - : this.node().__data__; -}; - -function dispatchEvent(node, type, params) { - var window = defaultView(node), - event = window.CustomEvent; - - if (typeof event === "function") { - event = new event(type, params); - } else { - event = window.document.createEvent("Event"); - if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; - else event.initEvent(type, false, false); - } - - node.dispatchEvent(event); -} - -function dispatchConstant(type, params) { - return function() { - return dispatchEvent(this, type, params); - }; -} - -function dispatchFunction(type, params) { - return function() { - return dispatchEvent(this, type, params.apply(this, arguments)); - }; -} - -var selection_dispatch = function(type, params) { - return this.each((typeof params === "function" - ? dispatchFunction - : dispatchConstant)(type, params)); -}; - -var root = [null]; - -function Selection(groups, parents) { - this._groups = groups; - this._parents = parents; -} - -function selection() { - return new Selection([[document.documentElement]], root); -} - -Selection.prototype = selection.prototype = { - constructor: Selection, - select: selection_select, - selectAll: selection_selectAll, - filter: selection_filter, - data: selection_data, - enter: selection_enter, - exit: selection_exit, - merge: selection_merge, - order: selection_order, - sort: selection_sort, - call: selection_call, - nodes: selection_nodes, - node: selection_node, - size: selection_size, - empty: selection_empty, - each: selection_each, - attr: selection_attr, - style: selection_style, - property: selection_property, - classed: selection_classed, - text: selection_text, - html: selection_html, - raise: selection_raise, - lower: selection_lower, - append: selection_append, - insert: selection_insert, - remove: selection_remove, - datum: selection_datum, - on: selection_on, - dispatch: selection_dispatch -}; - -var select = function(selector) { - return typeof selector === "string" - ? new Selection([[document.querySelector(selector)]], [document.documentElement]) - : new Selection([[selector]], root); -}; - -function count(node) { - var sum = 0, - children = node.children, - i = children && children.length; - if (!i) sum = 1; - else while (--i >= 0) sum += children[i].value; - node.value = sum; -} - -var node_count = function() { - return this.eachAfter(count); -}; - -var node_each = function(callback) { - var node = this, current, next = [node], children, i, n; - do { - current = next.reverse(), next = []; - while (node = current.pop()) { - callback(node), children = node.children; - if (children) for (i = 0, n = children.length; i < n; ++i) { - next.push(children[i]); - } - } - } while (next.length); - return this; -}; - -var node_eachBefore = function(callback) { - var node = this, nodes = [node], children, i; - while (node = nodes.pop()) { - callback(node), children = node.children; - if (children) for (i = children.length - 1; i >= 0; --i) { - nodes.push(children[i]); - } - } - return this; -}; - -var node_eachAfter = function(callback) { - var node = this, nodes = [node], next = [], children, i, n; - while (node = nodes.pop()) { - next.push(node), children = node.children; - if (children) for (i = 0, n = children.length; i < n; ++i) { - nodes.push(children[i]); - } - } - while (node = next.pop()) { - callback(node); - } - return this; -}; - -var node_sum = function(value) { - return this.eachAfter(function(node) { - var sum = +value(node.data) || 0, - children = node.children, - i = children && children.length; - while (--i >= 0) sum += children[i].value; - node.value = sum; - }); -}; - -var node_sort = function(compare) { - return this.eachBefore(function(node) { - if (node.children) { - node.children.sort(compare); - } - }); -}; - -var node_path = function(end) { - var start = this, - ancestor = leastCommonAncestor(start, end), - nodes = [start]; - while (start !== ancestor) { - start = start.parent; - nodes.push(start); - } - var k = nodes.length; - while (end !== ancestor) { - nodes.splice(k, 0, end); - end = end.parent; - } - return nodes; -}; - -function leastCommonAncestor(a, b) { - if (a === b) return a; - var aNodes = a.ancestors(), - bNodes = b.ancestors(), - c = null; - a = aNodes.pop(); - b = bNodes.pop(); - while (a === b) { - c = a; - a = aNodes.pop(); - b = bNodes.pop(); - } - return c; -} - -var node_ancestors = function() { - var node = this, nodes = [node]; - while (node = node.parent) { - nodes.push(node); - } - return nodes; -}; - -var node_descendants = function() { - var nodes = []; - this.each(function(node) { - nodes.push(node); - }); - return nodes; -}; - -var node_leaves = function() { - var leaves = []; - this.eachBefore(function(node) { - if (!node.children) { - leaves.push(node); - } - }); - return leaves; -}; - -var node_links = function() { - var root = this, links = []; - root.each(function(node) { - if (node !== root) { // Don’t include the root’s parent, if any. - links.push({source: node.parent, target: node}); - } - }); - return links; -}; - -function hierarchy(data, children) { - var root = new Node(data), - valued = +data.value && (root.value = data.value), - node, - nodes = [root], - child, - childs, - i, - n; - - if (children == null) children = defaultChildren; - - while (node = nodes.pop()) { - if (valued) node.value = +node.data.value; - if ((childs = children(node.data)) && (n = childs.length)) { - node.children = new Array(n); - for (i = n - 1; i >= 0; --i) { - nodes.push(child = node.children[i] = new Node(childs[i])); - child.parent = node; - child.depth = node.depth + 1; - } - } - } - - return root.eachBefore(computeHeight); -} - -function node_copy() { - return hierarchy(this).eachBefore(copyData); -} - -function defaultChildren(d) { - return d.children; -} - -function copyData(node) { - node.data = node.data.data; -} - -function computeHeight(node) { - var height = 0; - do node.height = height; - while ((node = node.parent) && (node.height < ++height)); -} - -function Node(data) { - this.data = data; - this.depth = - this.height = 0; - this.parent = null; -} - -Node.prototype = hierarchy.prototype = { - constructor: Node, - count: node_count, - each: node_each, - eachAfter: node_eachAfter, - eachBefore: node_eachBefore, - sum: node_sum, - sort: node_sort, - path: node_path, - ancestors: node_ancestors, - descendants: node_descendants, - leaves: node_leaves, - links: node_links, - copy: node_copy -}; - -var roundNode = function(node) { - node.x0 = Math.round(node.x0); - node.y0 = Math.round(node.y0); - node.x1 = Math.round(node.x1); - node.y1 = Math.round(node.y1); -}; - -var treemapDice = function(parent, x0, y0, x1, y1) { - var nodes = parent.children, - node, - i = -1, - n = nodes.length, - k = parent.value && (x1 - x0) / parent.value; - - while (++i < n) { - node = nodes[i], node.y0 = y0, node.y1 = y1; - node.x0 = x0, node.x1 = x0 += node.value * k; - } -}; - -var partition = function() { - var dx = 1, - dy = 1, - padding = 0, - round = false; - - function partition(root) { - var n = root.height + 1; - root.x0 = - root.y0 = padding; - root.x1 = dx; - root.y1 = dy / n; - root.eachBefore(positionNode(dy, n)); - if (round) root.eachBefore(roundNode); - return root; - } - - function positionNode(dy, n) { - return function(node) { - if (node.children) { - treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n); - } - var x0 = node.x0, - y0 = node.y0, - x1 = node.x1 - padding, - y1 = node.y1 - padding; - if (x1 < x0) x0 = x1 = (x0 + x1) / 2; - if (y1 < y0) y0 = y1 = (y0 + y1) / 2; - node.x0 = x0; - node.y0 = y0; - node.x1 = x1; - node.y1 = y1; - }; - } - - partition.round = function(x) { - return arguments.length ? (round = !!x, partition) : round; - }; - - partition.size = function(x) { - return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy]; - }; - - partition.padding = function(x) { - return arguments.length ? (padding = +x, partition) : padding; - }; - - return partition; -}; - -var ascending$1 = function(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; -}; - -var bisector = function(compare) { - if (compare.length === 1) compare = ascendingComparator(compare); - return { - left: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; - else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (lo == null) lo = 0; - if (hi == null) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; - else lo = mid + 1; - } - return lo; - } - }; -}; - -function ascendingComparator(f) { - return function(d, x) { - return ascending$1(f(d), x); - }; -} - -var ascendingBisect = bisector(ascending$1); -var bisectRight = ascendingBisect.right; - -var e10 = Math.sqrt(50); -var e5 = Math.sqrt(10); -var e2 = Math.sqrt(2); - -var ticks = function(start, stop, count) { - var reverse, - i = -1, - n, - ticks, - step; - - stop = +stop, start = +start, count = +count; - if (start === stop && count > 0) return [start]; - if (reverse = stop < start) n = start, start = stop, stop = n; - if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; - - if (step > 0) { - start = Math.ceil(start / step); - stop = Math.floor(stop / step); - ticks = new Array(n = Math.ceil(stop - start + 1)); - while (++i < n) ticks[i] = (start + i) * step; - } else { - start = Math.floor(start * step); - stop = Math.ceil(stop * step); - ticks = new Array(n = Math.ceil(start - stop + 1)); - while (++i < n) ticks[i] = (start - i) / step; - } - - if (reverse) ticks.reverse(); - - return ticks; -}; - -function tickIncrement(start, stop, count) { - var step = (stop - start) / Math.max(0, count), - power = Math.floor(Math.log(step) / Math.LN10), - error = step / Math.pow(10, power); - return power >= 0 - ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) - : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); -} - -function tickStep(start, stop, count) { - var step0 = Math.abs(stop - start) / Math.max(0, count), - step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), - error = step0 / step1; - if (error >= e10) step1 *= 10; - else if (error >= e5) step1 *= 5; - else if (error >= e2) step1 *= 2; - return stop < start ? -step1 : step1; -} - -var prefix = "$"; - -function Map() {} - -Map.prototype = map$1.prototype = { - constructor: Map, - has: function(key) { - return (prefix + key) in this; - }, - get: function(key) { - return this[prefix + key]; - }, - set: function(key, value) { - this[prefix + key] = value; - return this; - }, - remove: function(key) { - var property = prefix + key; - return property in this && delete this[property]; - }, - clear: function() { - for (var property in this) if (property[0] === prefix) delete this[property]; - }, - keys: function() { - var keys = []; - for (var property in this) if (property[0] === prefix) keys.push(property.slice(1)); - return keys; - }, - values: function() { - var values = []; - for (var property in this) if (property[0] === prefix) values.push(this[property]); - return values; - }, - entries: function() { - var entries = []; - for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]}); - return entries; - }, - size: function() { - var size = 0; - for (var property in this) if (property[0] === prefix) ++size; - return size; - }, - empty: function() { - for (var property in this) if (property[0] === prefix) return false; - return true; - }, - each: function(f) { - for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this); - } -}; - -function map$1(object, f) { - var map = new Map; - - // Copy constructor. - if (object instanceof Map) object.each(function(value, key) { map.set(key, value); }); - - // Index array by numeric index or specified key function. - else if (Array.isArray(object)) { - var i = -1, - n = object.length, - o; - - if (f == null) while (++i < n) map.set(i, object[i]); - else while (++i < n) map.set(f(o = object[i], i, object), o); - } - - // Convert object to map. - else if (object) for (var key in object) map.set(key, object[key]); - - return map; -} - -function Set() {} - -var proto = map$1.prototype; - -Set.prototype = set.prototype = { - constructor: Set, - has: proto.has, - add: function(value) { - value += ""; - this[prefix + value] = value; - return this; - }, - remove: proto.remove, - clear: proto.clear, - values: proto.keys, - size: proto.size, - empty: proto.empty, - each: proto.each -}; - -function set(object, f) { - var set = new Set; - - // Copy constructor. - if (object instanceof Set) object.each(function(value) { set.add(value); }); - - // Otherwise, assume it’s an array. - else if (object) { - var i = -1, n = object.length; - if (f == null) while (++i < n) set.add(object[i]); - else while (++i < n) set.add(f(object[i], i, object)); - } - - return set; -} - -var array$1 = Array.prototype; - -var map$3 = array$1.map; -var slice$2 = array$1.slice; - -var define = function(constructor, factory, prototype) { - constructor.prototype = factory.prototype = prototype; - prototype.constructor = constructor; -}; - -function extend(parent, definition) { - var prototype = Object.create(parent.prototype); - for (var key in definition) prototype[key] = definition[key]; - return prototype; -} - -function Color() {} - -var darker = 0.7; -var brighter = 1 / darker; - -var reI = "\\s*([+-]?\\d+)\\s*"; -var reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*"; -var reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*"; -var reHex3 = /^#([0-9a-f]{3})$/; -var reHex6 = /^#([0-9a-f]{6})$/; -var reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"); -var reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"); -var reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"); -var reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"); -var reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"); -var reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); - -var named = { - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - grey: 0x808080, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - rebeccapurple: 0x663399, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 -}; - -define(Color, color, { - displayable: function() { - return this.rgb().displayable(); - }, - toString: function() { - return this.rgb() + ""; - } -}); - -function color(format) { - var m; - format = (format + "").trim().toLowerCase(); - return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00 - : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000 - : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) - : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) - : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) - : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) - : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) - : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) - : named.hasOwnProperty(format) ? rgbn(named[format]) - : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) - : null; -} - -function rgbn(n) { - return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); -} - -function rgba(r, g, b, a) { - if (a <= 0) r = g = b = NaN; - return new Rgb(r, g, b, a); -} - -function rgbConvert(o) { - if (!(o instanceof Color)) o = color(o); - if (!o) return new Rgb; - o = o.rgb(); - return new Rgb(o.r, o.g, o.b, o.opacity); -} - -function rgb(r, g, b, opacity) { - return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); -} - -function Rgb(r, g, b, opacity) { - this.r = +r; - this.g = +g; - this.b = +b; - this.opacity = +opacity; -} - -define(Rgb, rgb, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - rgb: function() { - return this; - }, - displayable: function() { - return (0 <= this.r && this.r <= 255) - && (0 <= this.g && this.g <= 255) - && (0 <= this.b && this.b <= 255) - && (0 <= this.opacity && this.opacity <= 1); - }, - toString: function() { - var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); - return (a === 1 ? "rgb(" : "rgba(") - + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " - + Math.max(0, Math.min(255, Math.round(this.b) || 0)) - + (a === 1 ? ")" : ", " + a + ")"); - } -})); - -function hsla(h, s, l, a) { - if (a <= 0) h = s = l = NaN; - else if (l <= 0 || l >= 1) h = s = NaN; - else if (s <= 0) h = NaN; - return new Hsl(h, s, l, a); -} - -function hslConvert(o) { - if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Color)) o = color(o); - if (!o) return new Hsl; - if (o instanceof Hsl) return o; - o = o.rgb(); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - h = NaN, - s = max - min, - l = (max + min) / 2; - if (s) { - if (r === max) h = (g - b) / s + (g < b) * 6; - else if (g === max) h = (b - r) / s + 2; - else h = (r - g) / s + 4; - s /= l < 0.5 ? max + min : 2 - max - min; - h *= 60; - } else { - s = l > 0 && l < 1 ? 0 : h; - } - return new Hsl(h, s, l, o.opacity); -} - -function hsl(h, s, l, opacity) { - return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); -} - -function Hsl(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; -} - -define(Hsl, hsl, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - rgb: function() { - var h = this.h % 360 + (this.h < 0) * 360, - s = isNaN(h) || isNaN(this.s) ? 0 : this.s, - l = this.l, - m2 = l + (l < 0.5 ? l : 1 - l) * s, - m1 = 2 * l - m2; - return new Rgb( - hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), - hsl2rgb(h, m1, m2), - hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), - this.opacity - ); - }, - displayable: function() { - return (0 <= this.s && this.s <= 1 || isNaN(this.s)) - && (0 <= this.l && this.l <= 1) - && (0 <= this.opacity && this.opacity <= 1); - } -})); - -/* From FvD 13.37, CSS Color Module Level 3 */ -function hsl2rgb(h, m1, m2) { - return (h < 60 ? m1 + (m2 - m1) * h / 60 - : h < 180 ? m2 - : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 - : m1) * 255; -} - -var deg2rad = Math.PI / 180; -var rad2deg = 180 / Math.PI; - -var Kn = 18; -var Xn = 0.950470; -var Yn = 1; -var Zn = 1.088830; -var t0 = 4 / 29; -var t1 = 6 / 29; -var t2 = 3 * t1 * t1; -var t3 = t1 * t1 * t1; - -function labConvert(o) { - if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity); - if (o instanceof Hcl) { - var h = o.h * deg2rad; - return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity); - } - if (!(o instanceof Rgb)) o = rgbConvert(o); - var b = rgb2xyz(o.r), - a = rgb2xyz(o.g), - l = rgb2xyz(o.b), - x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), - y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn), - z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn); - return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity); -} - -function lab(l, a, b, opacity) { - return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity); -} - -function Lab(l, a, b, opacity) { - this.l = +l; - this.a = +a; - this.b = +b; - this.opacity = +opacity; -} - -define(Lab, lab, extend(Color, { - brighter: function(k) { - return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); - }, - darker: function(k) { - return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); - }, - rgb: function() { - var y = (this.l + 16) / 116, - x = isNaN(this.a) ? y : y + this.a / 500, - z = isNaN(this.b) ? y : y - this.b / 200; - y = Yn * lab2xyz(y); - x = Xn * lab2xyz(x); - z = Zn * lab2xyz(z); - return new Rgb( - xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB - xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), - xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z), - this.opacity - ); - } -})); - -function xyz2lab(t) { - return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; -} - -function lab2xyz(t) { - return t > t1 ? t * t * t : t2 * (t - t0); -} - -function xyz2rgb(x) { - return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); -} - -function rgb2xyz(x) { - return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); -} - -function hclConvert(o) { - if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity); - if (!(o instanceof Lab)) o = labConvert(o); - var h = Math.atan2(o.b, o.a) * rad2deg; - return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity); -} - -function hcl(h, c, l, opacity) { - return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity); -} - -function Hcl(h, c, l, opacity) { - this.h = +h; - this.c = +c; - this.l = +l; - this.opacity = +opacity; -} - -define(Hcl, hcl, extend(Color, { - brighter: function(k) { - return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity); - }, - darker: function(k) { - return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity); - }, - rgb: function() { - return labConvert(this).rgb(); - } -})); - -var A = -0.14861; -var B = +1.78277; -var C = -0.29227; -var D = -0.90649; -var E = +1.97294; -var ED = E * D; -var EB = E * B; -var BC_DA = B * C - D * A; - -function cubehelixConvert(o) { - if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Rgb)) o = rgbConvert(o); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB), - bl = b - l, - k = (E * (g - l) - C * bl) / D, - s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1 - h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN; - return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity); -} - -function cubehelix(h, s, l, opacity) { - return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity); -} - -function Cubehelix(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; -} - -define(Cubehelix, cubehelix, extend(Color, { - brighter: function(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Cubehelix(this.h, this.s, this.l * k, this.opacity); - }, - darker: function(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Cubehelix(this.h, this.s, this.l * k, this.opacity); - }, - rgb: function() { - var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad, - l = +this.l, - a = isNaN(this.s) ? 0 : this.s * l * (1 - l), - cosh = Math.cos(h), - sinh = Math.sin(h); - return new Rgb( - 255 * (l + a * (A * cosh + B * sinh)), - 255 * (l + a * (C * cosh + D * sinh)), - 255 * (l + a * (E * cosh)), - this.opacity - ); - } -})); - -var constant$3 = function(x) { - return function() { - return x; - }; -}; - -function linear$1(a, d) { - return function(t) { - return a + t * d; - }; -} - -function exponential(a, b, y) { - return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { - return Math.pow(a + t * b, y); - }; -} - -function hue(a, b) { - var d = b - a; - return d ? linear$1(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a); -} - -function gamma(y) { - return (y = +y) === 1 ? nogamma : function(a, b) { - return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a); - }; -} - -function nogamma(a, b) { - var d = b - a; - return d ? linear$1(a, d) : constant$3(isNaN(a) ? b : a); -} - -var interpolateRgb = (function rgbGamma(y) { - var color$$1 = gamma(y); - - function rgb$$1(start, end) { - var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r), - g = color$$1(start.g, end.g), - b = color$$1(start.b, end.b), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.r = r(t); - start.g = g(t); - start.b = b(t); - start.opacity = opacity(t); - return start + ""; - }; - } - - rgb$$1.gamma = rgbGamma; - - return rgb$$1; -})(1); - -var array$2 = function(a, b) { - var nb = b ? b.length : 0, - na = a ? Math.min(nb, a.length) : 0, - x = new Array(nb), - c = new Array(nb), - i; - - for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]); - for (; i < nb; ++i) c[i] = b[i]; - - return function(t) { - for (i = 0; i < na; ++i) c[i] = x[i](t); - return c; - }; -}; - -var date = function(a, b) { - var d = new Date; - return a = +a, b -= a, function(t) { - return d.setTime(a + b * t), d; - }; -}; - -var interpolateNumber = function(a, b) { - return a = +a, b -= a, function(t) { - return a + b * t; - }; -}; - -var object = function(a, b) { - var i = {}, - c = {}, - k; - - if (a === null || typeof a !== "object") a = {}; - if (b === null || typeof b !== "object") b = {}; - - for (k in b) { - if (k in a) { - i[k] = interpolateValue(a[k], b[k]); - } else { - c[k] = b[k]; - } - } - - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; -}; - -var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; -var reB = new RegExp(reA.source, "g"); - -function zero(b) { - return function() { - return b; - }; -} - -function one(b) { - return function(t) { - return b(t) + ""; - }; -} - -var interpolateString = function(a, b) { - var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b - am, // current match in a - bm, // current match in b - bs, // string preceding current number in b, if any - i = -1, // index in s - s = [], // string constants and placeholders - q = []; // number interpolators - - // Coerce inputs to strings. - a = a + "", b = b + ""; - - // Interpolate pairs of numbers in a & b. - while ((am = reA.exec(a)) - && (bm = reB.exec(b))) { - if ((bs = bm.index) > bi) { // a string precedes the next number in b - bs = b.slice(bi, bs); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match - if (s[i]) s[i] += bm; // coalesce with previous string - else s[++i] = bm; - } else { // interpolate non-matching numbers - s[++i] = null; - q.push({i: i, x: interpolateNumber(am, bm)}); - } - bi = reB.lastIndex; - } - - // Add remains of b. - if (bi < b.length) { - bs = b.slice(bi); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - - // Special optimization for only a single match. - // Otherwise, interpolate each of the numbers and rejoin the string. - return s.length < 2 ? (q[0] - ? one(q[0].x) - : zero(b)) - : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); -}; - -var interpolateValue = function(a, b) { - var t = typeof b, c; - return b == null || t === "boolean" ? constant$3(b) - : (t === "number" ? interpolateNumber - : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString) - : b instanceof color ? interpolateRgb - : b instanceof Date ? date - : Array.isArray(b) ? array$2 - : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object - : interpolateNumber)(a, b); -}; - -var interpolateRound = function(a, b) { - return a = +a, b -= a, function(t) { - return Math.round(a + b * t); - }; -}; - -var degrees = 180 / Math.PI; - -var identity$2 = { - translateX: 0, - translateY: 0, - rotate: 0, - skewX: 0, - scaleX: 1, - scaleY: 1 -}; - -var decompose = function(a, b, c, d, e, f) { - var scaleX, scaleY, skewX; - if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; - if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; - if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; - if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; - return { - translateX: e, - translateY: f, - rotate: Math.atan2(b, a) * degrees, - skewX: Math.atan(skewX) * degrees, - scaleX: scaleX, - scaleY: scaleY - }; -}; - -var cssNode; -var cssRoot; -var cssView; -var svgNode; - -function parseCss(value) { - if (value === "none") return identity$2; - if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; - cssNode.style.transform = value; - value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); - cssRoot.removeChild(cssNode); - value = value.slice(7, -1).split(","); - return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); -} - -function parseSvg(value) { - if (value == null) return identity$2; - if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); - svgNode.setAttribute("transform", value); - if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2; - value = value.matrix; - return decompose(value.a, value.b, value.c, value.d, value.e, value.f); -} - -function interpolateTransform(parse, pxComma, pxParen, degParen) { - - function pop(s) { - return s.length ? s.pop() + " " : ""; - } - - function translate(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push("translate(", null, pxComma, null, pxParen); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb || yb) { - s.push("translate(" + xb + pxComma + yb + pxParen); - } - } - - function rotate(a, b, s, q) { - if (a !== b) { - if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path - q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "rotate(" + b + degParen); - } - } - - function skewX(a, b, s, q) { - if (a !== b) { - q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "skewX(" + b + degParen); - } - } - - function scale(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push(pop(s) + "scale(", null, ",", null, ")"); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb !== 1 || yb !== 1) { - s.push(pop(s) + "scale(" + xb + "," + yb + ")"); - } - } - - return function(a, b) { - var s = [], // string constants and placeholders - q = []; // number interpolators - a = parse(a), b = parse(b); - translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); - rotate(a.rotate, b.rotate, s, q); - skewX(a.skewX, b.skewX, s, q); - scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); - a = b = null; // gc - return function(t) { - var i = -1, n = q.length, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - }; -} - -var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); -var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); - -var rho = Math.SQRT2; - -function cubehelix$1(hue$$1) { - return (function cubehelixGamma(y) { - y = +y; - - function cubehelix$$1(start, end) { - var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h), - s = nogamma(start.s, end.s), - l = nogamma(start.l, end.l), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.h = h(t); - start.s = s(t); - start.l = l(Math.pow(t, y)); - start.opacity = opacity(t); - return start + ""; - }; - } - - cubehelix$$1.gamma = cubehelixGamma; - - return cubehelix$$1; - })(1); -} - -cubehelix$1(hue); -var cubehelixLong = cubehelix$1(nogamma); - -var constant$4 = function(x) { - return function() { - return x; - }; -}; - -var number$1 = function(x) { - return +x; -}; - -var unit = [0, 1]; - -function deinterpolateLinear(a, b) { - return (b -= (a = +a)) - ? function(x) { return (x - a) / b; } - : constant$4(b); -} - -function deinterpolateClamp(deinterpolate) { - return function(a, b) { - var d = deinterpolate(a = +a, b = +b); - return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); }; - }; -} - -function reinterpolateClamp(reinterpolate) { - return function(a, b) { - var r = reinterpolate(a = +a, b = +b); - return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); }; - }; -} - -function bimap(domain, range, deinterpolate, reinterpolate) { - var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1]; - if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0); - else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1); - return function(x) { return r0(d0(x)); }; -} - -function polymap(domain, range, deinterpolate, reinterpolate) { - var j = Math.min(domain.length, range.length) - 1, - d = new Array(j), - r = new Array(j), - i = -1; - - // Reverse descending domains. - if (domain[j] < domain[0]) { - domain = domain.slice().reverse(); - range = range.slice().reverse(); - } - - while (++i < j) { - d[i] = deinterpolate(domain[i], domain[i + 1]); - r[i] = reinterpolate(range[i], range[i + 1]); - } - - return function(x) { - var i = bisectRight(domain, x, 1, j) - 1; - return r[i](d[i](x)); - }; -} - -function copy(source, target) { - return target - .domain(source.domain()) - .range(source.range()) - .interpolate(source.interpolate()) - .clamp(source.clamp()); -} - -// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. -// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b]. -function continuous(deinterpolate, reinterpolate) { - var domain = unit, - range = unit, - interpolate$$1 = interpolateValue, - clamp = false, - piecewise, - output, - input; - - function rescale() { - piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap; - output = input = null; - return scale; - } - - function scale(x) { - return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x); - } - - scale.invert = function(y) { - return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y); - }; - - scale.domain = function(_) { - return arguments.length ? (domain = map$3.call(_, number$1), rescale()) : domain.slice(); - }; - - scale.range = function(_) { - return arguments.length ? (range = slice$2.call(_), rescale()) : range.slice(); - }; - - scale.rangeRound = function(_) { - return range = slice$2.call(_), interpolate$$1 = interpolateRound, rescale(); - }; - - scale.clamp = function(_) { - return arguments.length ? (clamp = !!_, rescale()) : clamp; - }; - - scale.interpolate = function(_) { - return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1; - }; - - return rescale(); -} - -// Computes the decimal coefficient and exponent of the specified number x with -// significant digits p, where x is positive and p is in [1, 21] or undefined. -// For example, formatDecimal(1.23) returns ["123", 0]. -var formatDecimal = function(x, p) { - if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity - var i, coefficient = x.slice(0, i); - - // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ - // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). - return [ - coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, - +x.slice(i + 1) - ]; -}; - -var exponent = function(x) { - return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN; -}; - -var formatGroup = function(grouping, thousands) { - return function(value, width) { - var i = value.length, - t = [], - j = 0, - g = grouping[0], - length = 0; - - while (i > 0 && g > 0) { - if (length + g + 1 > width) g = Math.max(1, width - length); - t.push(value.substring(i -= g, i + g)); - if ((length += g + 1) > width) break; - g = grouping[j = (j + 1) % grouping.length]; - } - - return t.reverse().join(thousands); - }; -}; - -var formatNumerals = function(numerals) { - return function(value) { - return value.replace(/[0-9]/g, function(i) { - return numerals[+i]; - }); - }; -}; - -var formatDefault = function(x, p) { - x = x.toPrecision(p); - - out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) { - switch (x[i]) { - case ".": i0 = i1 = i; break; - case "0": if (i0 === 0) i0 = i; i1 = i; break; - case "e": break out; - default: if (i0 > 0) i0 = 0; break; - } - } - - return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x; -}; - -var prefixExponent; - -var formatPrefixAuto = function(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1], - i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, - n = coefficient.length; - return i === n ? coefficient - : i > n ? coefficient + new Array(i - n + 1).join("0") - : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) - : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! -}; - -var formatRounded = function(x, p) { - var d = formatDecimal(x, p); - if (!d) return x + ""; - var coefficient = d[0], - exponent = d[1]; - return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient - : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) - : coefficient + new Array(exponent - coefficient.length + 2).join("0"); -}; - -var formatTypes = { - "": formatDefault, - "%": function(x, p) { return (x * 100).toFixed(p); }, - "b": function(x) { return Math.round(x).toString(2); }, - "c": function(x) { return x + ""; }, - "d": function(x) { return Math.round(x).toString(10); }, - "e": function(x, p) { return x.toExponential(p); }, - "f": function(x, p) { return x.toFixed(p); }, - "g": function(x, p) { return x.toPrecision(p); }, - "o": function(x) { return Math.round(x).toString(8); }, - "p": function(x, p) { return formatRounded(x * 100, p); }, - "r": formatRounded, - "s": formatPrefixAuto, - "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, - "x": function(x) { return Math.round(x).toString(16); } -}; - -// [[fill]align][sign][symbol][0][width][,][.precision][type] -var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i; - -function formatSpecifier(specifier) { - return new FormatSpecifier(specifier); -} - -formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof - -function FormatSpecifier(specifier) { - if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); - - var match, - fill = match[1] || " ", - align = match[2] || ">", - sign = match[3] || "-", - symbol = match[4] || "", - zero = !!match[5], - width = match[6] && +match[6], - comma = !!match[7], - precision = match[8] && +match[8].slice(1), - type = match[9] || ""; - - // The "n" type is an alias for ",g". - if (type === "n") comma = true, type = "g"; - - // Map invalid types to the default format. - else if (!formatTypes[type]) type = ""; - - // If zero fill is specified, padding goes after sign and before digits. - if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; - - this.fill = fill; - this.align = align; - this.sign = sign; - this.symbol = symbol; - this.zero = zero; - this.width = width; - this.comma = comma; - this.precision = precision; - this.type = type; -} - -FormatSpecifier.prototype.toString = function() { - return this.fill - + this.align - + this.sign - + this.symbol - + (this.zero ? "0" : "") - + (this.width == null ? "" : Math.max(1, this.width | 0)) - + (this.comma ? "," : "") - + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0)) - + this.type; -}; - -var identity$3 = function(x) { - return x; -}; - -var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"]; - -var formatLocale = function(locale) { - var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3, - currency = locale.currency, - decimal = locale.decimal, - numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3, - percent = locale.percent || "%"; - - function newFormat(specifier) { - specifier = formatSpecifier(specifier); - - var fill = specifier.fill, - align = specifier.align, - sign = specifier.sign, - symbol = specifier.symbol, - zero = specifier.zero, - width = specifier.width, - comma = specifier.comma, - precision = specifier.precision, - type = specifier.type; - - // Compute the prefix and suffix. - // For SI-prefix, the suffix is lazily computed. - var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", - suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : ""; - - // What format function should we use? - // Is this an integer type? - // Can this type generate exponential notation? - var formatType = formatTypes[type], - maybeSuffix = !type || /[defgprs%]/.test(type); - - // Set the default precision if not specified, - // or clamp the specified precision to the supported range. - // For significant precision, it must be in [1, 21]. - // For fixed precision, it must be in [0, 20]. - precision = precision == null ? (type ? 6 : 12) - : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) - : Math.max(0, Math.min(20, precision)); - - function format(value) { - var valuePrefix = prefix, - valueSuffix = suffix, - i, n, c; - - if (type === "c") { - valueSuffix = formatType(value) + valueSuffix; - value = ""; - } else { - value = +value; - - // Perform the initial formatting. - var valueNegative = value < 0; - value = formatType(Math.abs(value), precision); - - // If a negative value rounds to zero during formatting, treat as positive. - if (valueNegative && +value === 0) valueNegative = false; - - // Compute the prefix and suffix. - valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - valueSuffix = valueSuffix + (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + (valueNegative && sign === "(" ? ")" : ""); - - // Break the formatted value into the integer “value” part that can be - // grouped, and fractional or exponential “suffix” part that is not. - if (maybeSuffix) { - i = -1, n = value.length; - while (++i < n) { - if (c = value.charCodeAt(i), 48 > c || c > 57) { - valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; - value = value.slice(0, i); - break; - } - } - } - } - - // If the fill character is not "0", grouping is applied before padding. - if (comma && !zero) value = group(value, Infinity); - - // Compute the padding. - var length = valuePrefix.length + value.length + valueSuffix.length, - padding = length < width ? new Array(width - length + 1).join(fill) : ""; - - // If the fill character is "0", grouping is applied after padding. - if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; - - // Reconstruct the final output based on the desired alignment. - switch (align) { - case "<": value = valuePrefix + value + valueSuffix + padding; break; - case "=": value = valuePrefix + padding + value + valueSuffix; break; - case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; - default: value = padding + valuePrefix + value + valueSuffix; break; - } - - return numerals(value); - } - - format.toString = function() { - return specifier + ""; - }; - - return format; - } - - function formatPrefix(specifier, value) { - var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), - e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, - k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3]; - return function(value) { - return f(k * value) + prefix; - }; - } - - return { - format: newFormat, - formatPrefix: formatPrefix - }; -}; - -var locale; - -var formatPrefix; - -defaultLocale({ - decimal: ".", - thousands: ",", - grouping: [3], - currency: ["$", ""] -}); - -function defaultLocale(definition) { - locale = formatLocale(definition); - exports.format = locale.format; - formatPrefix = locale.formatPrefix; - return locale; -} - -var precisionFixed = function(step) { - return Math.max(0, -exponent(Math.abs(step))); -}; - -var precisionPrefix = function(step, value) { - return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step))); -}; - -var precisionRound = function(step, max) { - step = Math.abs(step), max = Math.abs(max) - step; - return Math.max(0, exponent(max) - exponent(step)) + 1; -}; - -var tickFormat = function(domain, count, specifier) { - var start = domain[0], - stop = domain[domain.length - 1], - step = tickStep(start, stop, count == null ? 10 : count), - precision; - specifier = formatSpecifier(specifier == null ? ",f" : specifier); - switch (specifier.type) { - case "s": { - var value = Math.max(Math.abs(start), Math.abs(stop)); - if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; - return formatPrefix(specifier, value); - } - case "": - case "e": - case "g": - case "p": - case "r": { - if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); - break; - } - case "f": - case "%": { - if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; - break; - } - } - return exports.format(specifier); -}; - -function linearish(scale) { - var domain = scale.domain; - - scale.ticks = function(count) { - var d = domain(); - return ticks(d[0], d[d.length - 1], count == null ? 10 : count); - }; - - scale.tickFormat = function(count, specifier) { - return tickFormat(domain(), count, specifier); - }; - - scale.nice = function(count) { - if (count == null) count = 10; - - var d = domain(), - i0 = 0, - i1 = d.length - 1, - start = d[i0], - stop = d[i1], - step; - - if (stop < start) { - step = start, start = stop, stop = step; - step = i0, i0 = i1, i1 = step; - } - - step = tickIncrement(start, stop, count); - - if (step > 0) { - start = Math.floor(start / step) * step; - stop = Math.ceil(stop / step) * step; - step = tickIncrement(start, stop, count); - } else if (step < 0) { - start = Math.ceil(start * step) / step; - stop = Math.floor(stop * step) / step; - step = tickIncrement(start, stop, count); - } - - if (step > 0) { - d[i0] = Math.floor(start / step) * step; - d[i1] = Math.ceil(stop / step) * step; - domain(d); - } else if (step < 0) { - d[i0] = Math.ceil(start * step) / step; - d[i1] = Math.floor(stop * step) / step; - domain(d); - } - - return scale; - }; - - return scale; -} - -function linear() { - var scale = continuous(deinterpolateLinear, interpolateNumber); - - scale.copy = function() { - return copy(scale, linear()); - }; - - return linearish(scale); -} - -var t0$1 = new Date; -var t1$1 = new Date; - -function newInterval(floori, offseti, count, field) { - - function interval(date) { - return floori(date = new Date(+date)), date; - } - - interval.floor = interval; - - interval.ceil = function(date) { - return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; - }; - - interval.round = function(date) { - var d0 = interval(date), - d1 = interval.ceil(date); - return date - d0 < d1 - date ? d0 : d1; - }; - - interval.offset = function(date, step) { - return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; - }; - - interval.range = function(start, stop, step) { - var range = []; - start = interval.ceil(start); - step = step == null ? 1 : Math.floor(step); - if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date - do range.push(new Date(+start)); while (offseti(start, step), floori(start), start < stop) - return range; - }; - - interval.filter = function(test) { - return newInterval(function(date) { - if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); - }, function(date, step) { - if (date >= date) { - if (step < 0) while (++step <= 0) { - while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty - } else while (--step >= 0) { - while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty - } - } - }); - }; - - if (count) { - interval.count = function(start, end) { - t0$1.setTime(+start), t1$1.setTime(+end); - floori(t0$1), floori(t1$1); - return Math.floor(count(t0$1, t1$1)); - }; - - interval.every = function(step) { - step = Math.floor(step); - return !isFinite(step) || !(step > 0) ? null - : !(step > 1) ? interval - : interval.filter(field - ? function(d) { return field(d) % step === 0; } - : function(d) { return interval.count(0, d) % step === 0; }); - }; - } - - return interval; -} - -var millisecond = newInterval(function() { - // noop -}, function(date, step) { - date.setTime(+date + step); -}, function(start, end) { - return end - start; -}); - -// An optimized implementation for this simple case. -millisecond.every = function(k) { - k = Math.floor(k); - if (!isFinite(k) || !(k > 0)) return null; - if (!(k > 1)) return millisecond; - return newInterval(function(date) { - date.setTime(Math.floor(date / k) * k); - }, function(date, step) { - date.setTime(+date + step * k); - }, function(start, end) { - return (end - start) / k; - }); -}; - -var durationSecond$1 = 1e3; -var durationMinute$1 = 6e4; -var durationHour$1 = 36e5; -var durationDay$1 = 864e5; -var durationWeek$1 = 6048e5; - -var second = newInterval(function(date) { - date.setTime(Math.floor(date / durationSecond$1) * durationSecond$1); -}, function(date, step) { - date.setTime(+date + step * durationSecond$1); -}, function(start, end) { - return (end - start) / durationSecond$1; -}, function(date) { - return date.getUTCSeconds(); -}); - -var minute = newInterval(function(date) { - date.setTime(Math.floor(date / durationMinute$1) * durationMinute$1); -}, function(date, step) { - date.setTime(+date + step * durationMinute$1); -}, function(start, end) { - return (end - start) / durationMinute$1; -}, function(date) { - return date.getMinutes(); -}); - -var hour = newInterval(function(date) { - var offset = date.getTimezoneOffset() * durationMinute$1 % durationHour$1; - if (offset < 0) offset += durationHour$1; - date.setTime(Math.floor((+date - offset) / durationHour$1) * durationHour$1 + offset); -}, function(date, step) { - date.setTime(+date + step * durationHour$1); -}, function(start, end) { - return (end - start) / durationHour$1; -}, function(date) { - return date.getHours(); -}); - -var day = newInterval(function(date) { - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setDate(date.getDate() + step); -}, function(start, end) { - return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1; -}, function(date) { - return date.getDate() - 1; -}); - -function weekday(i) { - return newInterval(function(date) { - date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); - date.setHours(0, 0, 0, 0); - }, function(date, step) { - date.setDate(date.getDate() + step * 7); - }, function(start, end) { - return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationWeek$1; - }); -} - -var sunday = weekday(0); -var monday = weekday(1); -var tuesday = weekday(2); -var wednesday = weekday(3); -var thursday = weekday(4); -var friday = weekday(5); -var saturday = weekday(6); - -var month = newInterval(function(date) { - date.setDate(1); - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setMonth(date.getMonth() + step); -}, function(start, end) { - return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; -}, function(date) { - return date.getMonth(); -}); - -var year = newInterval(function(date) { - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); -}, function(date, step) { - date.setFullYear(date.getFullYear() + step); -}, function(start, end) { - return end.getFullYear() - start.getFullYear(); -}, function(date) { - return date.getFullYear(); -}); - -// An optimized implementation for this simple case. -year.every = function(k) { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { - date.setFullYear(Math.floor(date.getFullYear() / k) * k); - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); - }, function(date, step) { - date.setFullYear(date.getFullYear() + step * k); - }); -}; - -var utcMinute = newInterval(function(date) { - date.setUTCSeconds(0, 0); -}, function(date, step) { - date.setTime(+date + step * durationMinute$1); -}, function(start, end) { - return (end - start) / durationMinute$1; -}, function(date) { - return date.getUTCMinutes(); -}); - -var utcHour = newInterval(function(date) { - date.setUTCMinutes(0, 0, 0); -}, function(date, step) { - date.setTime(+date + step * durationHour$1); -}, function(start, end) { - return (end - start) / durationHour$1; -}, function(date) { - return date.getUTCHours(); -}); - -var utcDay = newInterval(function(date) { - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCDate(date.getUTCDate() + step); -}, function(start, end) { - return (end - start) / durationDay$1; -}, function(date) { - return date.getUTCDate() - 1; -}); - -function utcWeekday(i) { - return newInterval(function(date) { - date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); - date.setUTCHours(0, 0, 0, 0); - }, function(date, step) { - date.setUTCDate(date.getUTCDate() + step * 7); - }, function(start, end) { - return (end - start) / durationWeek$1; - }); -} - -var utcSunday = utcWeekday(0); -var utcMonday = utcWeekday(1); -var utcTuesday = utcWeekday(2); -var utcWednesday = utcWeekday(3); -var utcThursday = utcWeekday(4); -var utcFriday = utcWeekday(5); -var utcSaturday = utcWeekday(6); - -var utcMonth = newInterval(function(date) { - date.setUTCDate(1); - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCMonth(date.getUTCMonth() + step); -}, function(start, end) { - return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; -}, function(date) { - return date.getUTCMonth(); -}); - -var utcYear = newInterval(function(date) { - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); -}, function(date, step) { - date.setUTCFullYear(date.getUTCFullYear() + step); -}, function(start, end) { - return end.getUTCFullYear() - start.getUTCFullYear(); -}, function(date) { - return date.getUTCFullYear(); -}); - -// An optimized implementation for this simple case. -utcYear.every = function(k) { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { - date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); - }, function(date, step) { - date.setUTCFullYear(date.getUTCFullYear() + step * k); - }); -}; - -function localDate(d) { - if (0 <= d.y && d.y < 100) { - var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); - date.setFullYear(d.y); - return date; - } - return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); -} - -function utcDate(d) { - if (0 <= d.y && d.y < 100) { - var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); - date.setUTCFullYear(d.y); - return date; - } - return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); -} - -function newYear(y) { - return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0}; -} - -function formatLocale$1(locale) { - var locale_dateTime = locale.dateTime, - locale_date = locale.date, - locale_time = locale.time, - locale_periods = locale.periods, - locale_weekdays = locale.days, - locale_shortWeekdays = locale.shortDays, - locale_months = locale.months, - locale_shortMonths = locale.shortMonths; - - var periodRe = formatRe(locale_periods), - periodLookup = formatLookup(locale_periods), - weekdayRe = formatRe(locale_weekdays), - weekdayLookup = formatLookup(locale_weekdays), - shortWeekdayRe = formatRe(locale_shortWeekdays), - shortWeekdayLookup = formatLookup(locale_shortWeekdays), - monthRe = formatRe(locale_months), - monthLookup = formatLookup(locale_months), - shortMonthRe = formatRe(locale_shortMonths), - shortMonthLookup = formatLookup(locale_shortMonths); - - var formats = { - "a": formatShortWeekday, - "A": formatWeekday, - "b": formatShortMonth, - "B": formatMonth, - "c": null, - "d": formatDayOfMonth, - "e": formatDayOfMonth, - "f": formatMicroseconds, - "H": formatHour24, - "I": formatHour12, - "j": formatDayOfYear, - "L": formatMilliseconds, - "m": formatMonthNumber, - "M": formatMinutes, - "p": formatPeriod, - "Q": formatUnixTimestamp, - "s": formatUnixTimestampSeconds, - "S": formatSeconds, - "u": formatWeekdayNumberMonday, - "U": formatWeekNumberSunday, - "V": formatWeekNumberISO, - "w": formatWeekdayNumberSunday, - "W": formatWeekNumberMonday, - "x": null, - "X": null, - "y": formatYear, - "Y": formatFullYear, - "Z": formatZone, - "%": formatLiteralPercent - }; - - var utcFormats = { - "a": formatUTCShortWeekday, - "A": formatUTCWeekday, - "b": formatUTCShortMonth, - "B": formatUTCMonth, - "c": null, - "d": formatUTCDayOfMonth, - "e": formatUTCDayOfMonth, - "f": formatUTCMicroseconds, - "H": formatUTCHour24, - "I": formatUTCHour12, - "j": formatUTCDayOfYear, - "L": formatUTCMilliseconds, - "m": formatUTCMonthNumber, - "M": formatUTCMinutes, - "p": formatUTCPeriod, - "Q": formatUnixTimestamp, - "s": formatUnixTimestampSeconds, - "S": formatUTCSeconds, - "u": formatUTCWeekdayNumberMonday, - "U": formatUTCWeekNumberSunday, - "V": formatUTCWeekNumberISO, - "w": formatUTCWeekdayNumberSunday, - "W": formatUTCWeekNumberMonday, - "x": null, - "X": null, - "y": formatUTCYear, - "Y": formatUTCFullYear, - "Z": formatUTCZone, - "%": formatLiteralPercent - }; - - var parses = { - "a": parseShortWeekday, - "A": parseWeekday, - "b": parseShortMonth, - "B": parseMonth, - "c": parseLocaleDateTime, - "d": parseDayOfMonth, - "e": parseDayOfMonth, - "f": parseMicroseconds, - "H": parseHour24, - "I": parseHour24, - "j": parseDayOfYear, - "L": parseMilliseconds, - "m": parseMonthNumber, - "M": parseMinutes, - "p": parsePeriod, - "Q": parseUnixTimestamp, - "s": parseUnixTimestampSeconds, - "S": parseSeconds, - "u": parseWeekdayNumberMonday, - "U": parseWeekNumberSunday, - "V": parseWeekNumberISO, - "w": parseWeekdayNumberSunday, - "W": parseWeekNumberMonday, - "x": parseLocaleDate, - "X": parseLocaleTime, - "y": parseYear, - "Y": parseFullYear, - "Z": parseZone, - "%": parseLiteralPercent - }; - - // These recursive directive definitions must be deferred. - formats.x = newFormat(locale_date, formats); - formats.X = newFormat(locale_time, formats); - formats.c = newFormat(locale_dateTime, formats); - utcFormats.x = newFormat(locale_date, utcFormats); - utcFormats.X = newFormat(locale_time, utcFormats); - utcFormats.c = newFormat(locale_dateTime, utcFormats); - - function newFormat(specifier, formats) { - return function(date) { - var string = [], - i = -1, - j = 0, - n = specifier.length, - c, - pad, - format; - - if (!(date instanceof Date)) date = new Date(+date); - - while (++i < n) { - if (specifier.charCodeAt(i) === 37) { - string.push(specifier.slice(j, i)); - if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); - else pad = c === "e" ? " " : "0"; - if (format = formats[c]) c = format(date, pad); - string.push(c); - j = i + 1; - } - } - - string.push(specifier.slice(j, i)); - return string.join(""); - }; - } - - function newParse(specifier, newDate) { - return function(string) { - var d = newYear(1900), - i = parseSpecifier(d, specifier, string += "", 0), - week, day$$1; - if (i != string.length) return null; - - // If a UNIX timestamp is specified, return it. - if ("Q" in d) return new Date(d.Q); - - // The am-pm flag is 0 for AM, and 1 for PM. - if ("p" in d) d.H = d.H % 12 + d.p * 12; - - // Convert day-of-week and week-of-year to day-of-year. - if ("V" in d) { - if (d.V < 1 || d.V > 53) return null; - if (!("w" in d)) d.w = 1; - if ("Z" in d) { - week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay(); - week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week); - week = utcDay.offset(week, (d.V - 1) * 7); - d.y = week.getUTCFullYear(); - d.m = week.getUTCMonth(); - d.d = week.getUTCDate() + (d.w + 6) % 7; - } else { - week = newDate(newYear(d.y)), day$$1 = week.getDay(); - week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week); - week = day.offset(week, (d.V - 1) * 7); - d.y = week.getFullYear(); - d.m = week.getMonth(); - d.d = week.getDate() + (d.w + 6) % 7; - } - } else if ("W" in d || "U" in d) { - if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; - day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay(); - d.m = 0; - d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7; - } - - // If a time zone is specified, all fields are interpreted as UTC and then - // offset according to the specified time zone. - if ("Z" in d) { - d.H += d.Z / 100 | 0; - d.M += d.Z % 100; - return utcDate(d); - } - - // Otherwise, all fields are in local time. - return newDate(d); - }; - } - - function parseSpecifier(d, specifier, string, j) { - var i = 0, - n = specifier.length, - m = string.length, - c, - parse; - - while (i < n) { - if (j >= m) return -1; - c = specifier.charCodeAt(i++); - if (c === 37) { - c = specifier.charAt(i++); - parse = parses[c in pads ? specifier.charAt(i++) : c]; - if (!parse || ((j = parse(d, string, j)) < 0)) return -1; - } else if (c != string.charCodeAt(j++)) { - return -1; - } - } - - return j; - } - - function parsePeriod(d, string, i) { - var n = periodRe.exec(string.slice(i)); - return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseShortWeekday(d, string, i) { - var n = shortWeekdayRe.exec(string.slice(i)); - return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseWeekday(d, string, i) { - var n = weekdayRe.exec(string.slice(i)); - return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseShortMonth(d, string, i) { - var n = shortMonthRe.exec(string.slice(i)); - return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseMonth(d, string, i) { - var n = monthRe.exec(string.slice(i)); - return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; - } - - function parseLocaleDateTime(d, string, i) { - return parseSpecifier(d, locale_dateTime, string, i); - } - - function parseLocaleDate(d, string, i) { - return parseSpecifier(d, locale_date, string, i); - } - - function parseLocaleTime(d, string, i) { - return parseSpecifier(d, locale_time, string, i); - } - - function formatShortWeekday(d) { - return locale_shortWeekdays[d.getDay()]; - } - - function formatWeekday(d) { - return locale_weekdays[d.getDay()]; - } - - function formatShortMonth(d) { - return locale_shortMonths[d.getMonth()]; - } - - function formatMonth(d) { - return locale_months[d.getMonth()]; - } - - function formatPeriod(d) { - return locale_periods[+(d.getHours() >= 12)]; - } - - function formatUTCShortWeekday(d) { - return locale_shortWeekdays[d.getUTCDay()]; - } - - function formatUTCWeekday(d) { - return locale_weekdays[d.getUTCDay()]; - } - - function formatUTCShortMonth(d) { - return locale_shortMonths[d.getUTCMonth()]; - } - - function formatUTCMonth(d) { - return locale_months[d.getUTCMonth()]; - } - - function formatUTCPeriod(d) { - return locale_periods[+(d.getUTCHours() >= 12)]; - } - - return { - format: function(specifier) { - var f = newFormat(specifier += "", formats); - f.toString = function() { return specifier; }; - return f; - }, - parse: function(specifier) { - var p = newParse(specifier += "", localDate); - p.toString = function() { return specifier; }; - return p; - }, - utcFormat: function(specifier) { - var f = newFormat(specifier += "", utcFormats); - f.toString = function() { return specifier; }; - return f; - }, - utcParse: function(specifier) { - var p = newParse(specifier, utcDate); - p.toString = function() { return specifier; }; - return p; - } - }; -} - -var pads = {"-": "", "_": " ", "0": "0"}; -var numberRe = /^\s*\d+/; -var percentRe = /^%/; -var requoteRe = /[\\^$*+?|[\]().{}]/g; - -function pad(value, fill, width) { - var sign = value < 0 ? "-" : "", - string = (sign ? -value : value) + "", - length = string.length; - return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); -} - -function requote(s) { - return s.replace(requoteRe, "\\$&"); -} - -function formatRe(names) { - return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); -} - -function formatLookup(names) { - var map = {}, i = -1, n = names.length; - while (++i < n) map[names[i].toLowerCase()] = i; - return map; -} - -function parseWeekdayNumberSunday(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 1)); - return n ? (d.w = +n[0], i + n[0].length) : -1; -} - -function parseWeekdayNumberMonday(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 1)); - return n ? (d.u = +n[0], i + n[0].length) : -1; -} - -function parseWeekNumberSunday(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.U = +n[0], i + n[0].length) : -1; -} - -function parseWeekNumberISO(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.V = +n[0], i + n[0].length) : -1; -} - -function parseWeekNumberMonday(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.W = +n[0], i + n[0].length) : -1; -} - -function parseFullYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 4)); - return n ? (d.y = +n[0], i + n[0].length) : -1; -} - -function parseYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; -} - -function parseZone(d, string, i) { - var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); - return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; -} - -function parseMonthNumber(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.m = n[0] - 1, i + n[0].length) : -1; -} - -function parseDayOfMonth(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.d = +n[0], i + n[0].length) : -1; -} - -function parseDayOfYear(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 3)); - return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; -} - -function parseHour24(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.H = +n[0], i + n[0].length) : -1; -} - -function parseMinutes(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.M = +n[0], i + n[0].length) : -1; -} - -function parseSeconds(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 2)); - return n ? (d.S = +n[0], i + n[0].length) : -1; -} - -function parseMilliseconds(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 3)); - return n ? (d.L = +n[0], i + n[0].length) : -1; -} - -function parseMicroseconds(d, string, i) { - var n = numberRe.exec(string.slice(i, i + 6)); - return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1; -} - -function parseLiteralPercent(d, string, i) { - var n = percentRe.exec(string.slice(i, i + 1)); - return n ? i + n[0].length : -1; -} - -function parseUnixTimestamp(d, string, i) { - var n = numberRe.exec(string.slice(i)); - return n ? (d.Q = +n[0], i + n[0].length) : -1; -} - -function parseUnixTimestampSeconds(d, string, i) { - var n = numberRe.exec(string.slice(i)); - return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1; -} - -function formatDayOfMonth(d, p) { - return pad(d.getDate(), p, 2); -} - -function formatHour24(d, p) { - return pad(d.getHours(), p, 2); -} - -function formatHour12(d, p) { - return pad(d.getHours() % 12 || 12, p, 2); -} - -function formatDayOfYear(d, p) { - return pad(1 + day.count(year(d), d), p, 3); -} - -function formatMilliseconds(d, p) { - return pad(d.getMilliseconds(), p, 3); -} - -function formatMicroseconds(d, p) { - return formatMilliseconds(d, p) + "000"; -} - -function formatMonthNumber(d, p) { - return pad(d.getMonth() + 1, p, 2); -} - -function formatMinutes(d, p) { - return pad(d.getMinutes(), p, 2); -} - -function formatSeconds(d, p) { - return pad(d.getSeconds(), p, 2); -} - -function formatWeekdayNumberMonday(d) { - var day$$1 = d.getDay(); - return day$$1 === 0 ? 7 : day$$1; -} - -function formatWeekNumberSunday(d, p) { - return pad(sunday.count(year(d), d), p, 2); -} - -function formatWeekNumberISO(d, p) { - var day$$1 = d.getDay(); - d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d); - return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2); -} - -function formatWeekdayNumberSunday(d) { - return d.getDay(); -} - -function formatWeekNumberMonday(d, p) { - return pad(monday.count(year(d), d), p, 2); -} - -function formatYear(d, p) { - return pad(d.getFullYear() % 100, p, 2); -} - -function formatFullYear(d, p) { - return pad(d.getFullYear() % 10000, p, 4); -} - -function formatZone(d) { - var z = d.getTimezoneOffset(); - return (z > 0 ? "-" : (z *= -1, "+")) - + pad(z / 60 | 0, "0", 2) - + pad(z % 60, "0", 2); -} - -function formatUTCDayOfMonth(d, p) { - return pad(d.getUTCDate(), p, 2); -} - -function formatUTCHour24(d, p) { - return pad(d.getUTCHours(), p, 2); -} - -function formatUTCHour12(d, p) { - return pad(d.getUTCHours() % 12 || 12, p, 2); -} - -function formatUTCDayOfYear(d, p) { - return pad(1 + utcDay.count(utcYear(d), d), p, 3); -} - -function formatUTCMilliseconds(d, p) { - return pad(d.getUTCMilliseconds(), p, 3); -} - -function formatUTCMicroseconds(d, p) { - return formatUTCMilliseconds(d, p) + "000"; -} - -function formatUTCMonthNumber(d, p) { - return pad(d.getUTCMonth() + 1, p, 2); -} - -function formatUTCMinutes(d, p) { - return pad(d.getUTCMinutes(), p, 2); -} - -function formatUTCSeconds(d, p) { - return pad(d.getUTCSeconds(), p, 2); -} - -function formatUTCWeekdayNumberMonday(d) { - var dow = d.getUTCDay(); - return dow === 0 ? 7 : dow; -} - -function formatUTCWeekNumberSunday(d, p) { - return pad(utcSunday.count(utcYear(d), d), p, 2); -} - -function formatUTCWeekNumberISO(d, p) { - var day$$1 = d.getUTCDay(); - d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d); - return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); -} - -function formatUTCWeekdayNumberSunday(d) { - return d.getUTCDay(); -} - -function formatUTCWeekNumberMonday(d, p) { - return pad(utcMonday.count(utcYear(d), d), p, 2); -} - -function formatUTCYear(d, p) { - return pad(d.getUTCFullYear() % 100, p, 2); -} - -function formatUTCFullYear(d, p) { - return pad(d.getUTCFullYear() % 10000, p, 4); -} - -function formatUTCZone() { - return "+0000"; -} - -function formatLiteralPercent() { - return "%"; -} - -function formatUnixTimestamp(d) { - return +d; -} - -function formatUnixTimestampSeconds(d) { - return Math.floor(+d / 1000); -} - -var locale$1; -var timeFormat; -var timeParse; -var utcFormat; -var utcParse; - -defaultLocale$1({ - dateTime: "%x, %X", - date: "%-m/%-d/%Y", - time: "%-I:%M:%S %p", - periods: ["AM", "PM"], - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -}); - -function defaultLocale$1(definition) { - locale$1 = formatLocale$1(definition); - timeFormat = locale$1.format; - timeParse = locale$1.parse; - utcFormat = locale$1.utcFormat; - utcParse = locale$1.utcParse; - return locale$1; -} - -var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ"; - -function formatIsoNative(date) { - return date.toISOString(); -} - -var formatIso = Date.prototype.toISOString - ? formatIsoNative - : utcFormat(isoSpecifier); - -function parseIsoNative(string) { - var date = new Date(string); - return isNaN(date) ? null : date; -} - -var parseIso = +new Date("2000-01-01T00:00:00.000Z") - ? parseIsoNative - : utcParse(isoSpecifier); - -var colors = function(s) { - return s.match(/.{6}/g).map(function(x) { - return "#" + x; - }); -}; - -colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"); - -colors("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"); - -colors("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"); - -colors("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"); - -cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0)); - -var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); - -var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); - -var rainbow = cubehelix(); - -function ramp(range) { - var n = range.length; - return function(t) { - return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))]; - }; -} - -ramp(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")); - -var magma = ramp(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")); - -var inferno = ramp(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")); - -var plasma = ramp(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")); - -function cubicInOut(t) { - return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; -} - -var pi = Math.PI; - -var tau = 2 * Math.PI; - -var noop = {value: function() {}}; - -function dispatch() { - for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { - if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); - _[t] = []; - } - return new Dispatch(_); -} - -function Dispatch(_) { - this._ = _; -} - -function parseTypenames$1(typenames, types) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); - return {type: t, name: name}; - }); -} - -Dispatch.prototype = dispatch.prototype = { - constructor: Dispatch, - on: function(typename, callback) { - var _ = this._, - T = parseTypenames$1(typename + "", _), - t, - i = -1, - n = T.length; - - // If no callback was specified, return the callback of the given type and name. - if (arguments.length < 2) { - while (++i < n) if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) return t; - return; - } - - // If a type was specified, set the callback for the given type and name. - // Otherwise, if a null callback was specified, remove callbacks of the given name. - if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); - while (++i < n) { - if (t = (typename = T[i]).type) _[t] = set$3(_[t], typename.name, callback); - else if (callback == null) for (t in _) _[t] = set$3(_[t], typename.name, null); - } - - return this; - }, - copy: function() { - var copy = {}, _ = this._; - for (var t in _) copy[t] = _[t].slice(); - return new Dispatch(copy); - }, - call: function(type, that) { - if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - }, - apply: function(type, that, args) { - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - } -}; - -function get$1(type, name) { - for (var i = 0, n = type.length, c; i < n; ++i) { - if ((c = type[i]).name === name) { - return c.value; - } - } -} - -function set$3(type, name, callback) { - for (var i = 0, n = type.length; i < n; ++i) { - if (type[i].name === name) { - type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); - break; - } - } - if (callback != null) type.push({name: name, value: callback}); - return type; -} - -var frame = 0; -var timeout = 0; -var interval = 0; -var pokeDelay = 1000; -var taskHead; -var taskTail; -var clockLast = 0; -var clockNow = 0; -var clockSkew = 0; -var clock = typeof performance === "object" && performance.now ? performance : Date; -var setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; - -function now() { - return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); -} - -function clearNow() { - clockNow = 0; -} - -function Timer() { - this._call = - this._time = - this._next = null; -} - -Timer.prototype = timer.prototype = { - constructor: Timer, - restart: function(callback, delay, time) { - if (typeof callback !== "function") throw new TypeError("callback is not a function"); - time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); - if (!this._next && taskTail !== this) { - if (taskTail) taskTail._next = this; - else taskHead = this; - taskTail = this; - } - this._call = callback; - this._time = time; - sleep(); - }, - stop: function() { - if (this._call) { - this._call = null; - this._time = Infinity; - sleep(); - } - } -}; - -function timer(callback, delay, time) { - var t = new Timer; - t.restart(callback, delay, time); - return t; -} - -function timerFlush() { - now(); // Get the current time, if not already set. - ++frame; // Pretend we’ve set an alarm, if we haven’t already. - var t = taskHead, e; - while (t) { - if ((e = clockNow - t._time) >= 0) t._call.call(null, e); - t = t._next; - } - --frame; -} - -function wake() { - clockNow = (clockLast = clock.now()) + clockSkew; - frame = timeout = 0; - try { - timerFlush(); - } finally { - frame = 0; - nap(); - clockNow = 0; - } -} - -function poke() { - var now = clock.now(), delay = now - clockLast; - if (delay > pokeDelay) clockSkew -= delay, clockLast = now; -} - -function nap() { - var t0, t1 = taskHead, t2, time = Infinity; - while (t1) { - if (t1._call) { - if (time > t1._time) time = t1._time; - t0 = t1, t1 = t1._next; - } else { - t2 = t1._next, t1._next = null; - t1 = t0 ? t0._next = t2 : taskHead = t2; - } - } - taskTail = t0; - sleep(time); -} - -function sleep(time) { - if (frame) return; // Soonest alarm already set, or will be. - if (timeout) timeout = clearTimeout(timeout); - var delay = time - clockNow; // Strictly less than if we recomputed clockNow. - if (delay > 24) { - if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); - if (interval) interval = clearInterval(interval); - } else { - if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); - frame = 1, setFrame(wake); - } -} - -var timeout$1 = function(callback, delay, time) { - var t = new Timer; - delay = delay == null ? 0 : +delay; - t.restart(function(elapsed) { - t.stop(); - callback(elapsed + delay); - }, delay, time); - return t; -}; - -var emptyOn = dispatch("start", "end", "interrupt"); -var emptyTween = []; - -var CREATED = 0; -var SCHEDULED = 1; -var STARTING = 2; -var STARTED = 3; -var RUNNING = 4; -var ENDING = 5; -var ENDED = 6; - -var schedule = function(node, name, id, index, group, timing) { - var schedules = node.__transition; - if (!schedules) node.__transition = {}; - else if (id in schedules) return; - create(node, id, { - name: name, - index: index, // For context during callback. - group: group, // For context during callback. - on: emptyOn, - tween: emptyTween, - time: timing.time, - delay: timing.delay, - duration: timing.duration, - ease: timing.ease, - timer: null, - state: CREATED - }); -}; - -function init(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id]) || schedule.state > CREATED) throw new Error("too late"); - return schedule; -} - -function set$2(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id]) || schedule.state > STARTING) throw new Error("too late"); - return schedule; -} - -function get(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id])) throw new Error("too late"); - return schedule; -} - -function create(node, id, self) { - var schedules = node.__transition, - tween; - - // Initialize the self timer when the transition is created. - // Note the actual delay is not known until the first callback! - schedules[id] = self; - self.timer = timer(schedule, 0, self.time); - - function schedule(elapsed) { - self.state = SCHEDULED; - self.timer.restart(start, self.delay, self.time); - - // If the elapsed delay is less than our first sleep, start immediately. - if (self.delay <= elapsed) start(elapsed - self.delay); - } - - function start(elapsed) { - var i, j, n, o; - - // If the state is not SCHEDULED, then we previously errored on start. - if (self.state !== SCHEDULED) return stop(); - - for (i in schedules) { - o = schedules[i]; - if (o.name !== self.name) continue; - - // While this element already has a starting transition during this frame, - // defer starting an interrupting transition until that transition has a - // chance to tick (and possibly end); see d3/d3-transition#54! - if (o.state === STARTED) return timeout$1(start); - - // Interrupt the active transition, if any. - // Dispatch the interrupt event. - if (o.state === RUNNING) { - o.state = ENDED; - o.timer.stop(); - o.on.call("interrupt", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - - // Cancel any pre-empted transitions. No interrupt event is dispatched - // because the cancelled transitions never started. Note that this also - // removes this transition from the pending list! - else if (+i < id) { - o.state = ENDED; - o.timer.stop(); - delete schedules[i]; - } - } - - // Defer the first tick to end of the current frame; see d3/d3#1576. - // Note the transition may be canceled after start and before the first tick! - // Note this must be scheduled before the start event; see d3/d3-transition#16! - // Assuming this is successful, subsequent callbacks go straight to tick. - timeout$1(function() { - if (self.state === STARTED) { - self.state = RUNNING; - self.timer.restart(tick, self.delay, self.time); - tick(elapsed); - } - }); - - // Dispatch the start event. - // Note this must be done before the tween are initialized. - self.state = STARTING; - self.on.call("start", node, node.__data__, self.index, self.group); - if (self.state !== STARTING) return; // interrupted - self.state = STARTED; - - // Initialize the tween, deleting null tween. - tween = new Array(n = self.tween.length); - for (i = 0, j = -1; i < n; ++i) { - if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { - tween[++j] = o; - } - } - tween.length = j + 1; - } - - function tick(elapsed) { - var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), - i = -1, - n = tween.length; - - while (++i < n) { - tween[i].call(null, t); - } - - // Dispatch the end event. - if (self.state === ENDING) { - self.on.call("end", node, node.__data__, self.index, self.group); - stop(); - } - } - - function stop() { - self.state = ENDED; - self.timer.stop(); - delete schedules[id]; - for (var i in schedules) return; // eslint-disable-line no-unused-vars - delete node.__transition; - } -} - -var interrupt = function(node, name) { - var schedules = node.__transition, - schedule$$1, - active, - empty = true, - i; - - if (!schedules) return; - - name = name == null ? null : name + ""; - - for (i in schedules) { - if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; } - active = schedule$$1.state > STARTING && schedule$$1.state < ENDING; - schedule$$1.state = ENDED; - schedule$$1.timer.stop(); - if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group); - delete schedules[i]; - } - - if (empty) delete node.__transition; -}; - -var selection_interrupt = function(name) { - return this.each(function() { - interrupt(this, name); - }); -}; - -function tweenRemove(id, name) { - var tween0, tween1; - return function() { - var schedule$$1 = set$2(this, id), - tween = schedule$$1.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = tween0 = tween; - for (var i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1 = tween1.slice(); - tween1.splice(i, 1); - break; - } - } - } - - schedule$$1.tween = tween1; - }; -} - -function tweenFunction(id, name, value) { - var tween0, tween1; - if (typeof value !== "function") throw new Error; - return function() { - var schedule$$1 = set$2(this, id), - tween = schedule$$1.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = (tween0 = tween).slice(); - for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1[i] = t; - break; - } - } - if (i === n) tween1.push(t); - } - - schedule$$1.tween = tween1; - }; -} - -var transition_tween = function(name, value) { - var id = this._id; - - name += ""; - - if (arguments.length < 2) { - var tween = get(this.node(), id).tween; - for (var i = 0, n = tween.length, t; i < n; ++i) { - if ((t = tween[i]).name === name) { - return t.value; - } - } - return null; - } - - return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); -}; - -function tweenValue(transition, name, value) { - var id = transition._id; - - transition.each(function() { - var schedule$$1 = set$2(this, id); - (schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments); - }); - - return function(node) { - return get(node, id).value[name]; - }; -} - -var interpolate = function(a, b) { - var c; - return (typeof b === "number" ? interpolateNumber - : b instanceof color ? interpolateRgb - : (c = color(b)) ? (b = c, interpolateRgb) - : interpolateString)(a, b); -}; - -function attrRemove$1(name) { - return function() { - this.removeAttribute(name); - }; -} - -function attrRemoveNS$1(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; -} - -function attrConstant$1(name, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = this.getAttribute(name); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function attrConstantNS$1(fullname, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = this.getAttributeNS(fullname.space, fullname.local); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function attrFunction$1(name, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var value0, value1 = value(this); - if (value1 == null) return void this.removeAttribute(name); - value0 = this.getAttribute(name); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -function attrFunctionNS$1(fullname, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var value0, value1 = value(this); - if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); - value0 = this.getAttributeNS(fullname.space, fullname.local); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -var transition_attr = function(name, value) { - var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate; - return this.attrTween(name, typeof value === "function" - ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) - : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) - : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + "")); -}; - -function attrTweenNS(fullname, value) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.setAttributeNS(fullname.space, fullname.local, i(t)); - }; - } - tween._value = value; - return tween; -} - -function attrTween(name, value) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.setAttribute(name, i(t)); - }; - } - tween._value = value; - return tween; -} - -var transition_attrTween = function(name, value) { - var key = "attr." + name; - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - var fullname = namespace(name); - return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); -}; - -function delayFunction(id, value) { - return function() { - init(this, id).delay = +value.apply(this, arguments); - }; -} - -function delayConstant(id, value) { - return value = +value, function() { - init(this, id).delay = value; - }; -} - -var transition_delay = function(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? delayFunction - : delayConstant)(id, value)) - : get(this.node(), id).delay; -}; - -function durationFunction(id, value) { - return function() { - set$2(this, id).duration = +value.apply(this, arguments); - }; -} - -function durationConstant(id, value) { - return value = +value, function() { - set$2(this, id).duration = value; - }; -} - -var transition_duration = function(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? durationFunction - : durationConstant)(id, value)) - : get(this.node(), id).duration; -}; - -function easeConstant(id, value) { - if (typeof value !== "function") throw new Error; - return function() { - set$2(this, id).ease = value; - }; -} - -var transition_ease = function(value) { - var id = this._id; - - return arguments.length - ? this.each(easeConstant(id, value)) - : get(this.node(), id).ease; -}; - -var transition_filter = function(match) { - if (typeof match !== "function") match = matcher$1(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Transition(subgroups, this._parents, this._name, this._id); -}; - -var transition_merge = function(transition$$1) { - if (transition$$1._id !== this._id) throw new Error; - - for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Transition(merges, this._parents, this._name, this._id); -}; - -function start(name) { - return (name + "").trim().split(/^|\s+/).every(function(t) { - var i = t.indexOf("."); - if (i >= 0) t = t.slice(0, i); - return !t || t === "start"; - }); -} - -function onFunction(id, name, listener) { - var on0, on1, sit = start(name) ? init : set$2; - return function() { - var schedule$$1 = sit(this, id), - on = schedule$$1.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); - - schedule$$1.on = on1; - }; -} - -var transition_on = function(name, listener) { - var id = this._id; - - return arguments.length < 2 - ? get(this.node(), id).on.on(name) - : this.each(onFunction(id, name, listener)); -}; - -function removeFunction(id) { - return function() { - var parent = this.parentNode; - for (var i in this.__transition) if (+i !== id) return; - if (parent) parent.removeChild(this); - }; -} - -var transition_remove = function() { - return this.on("end.remove", removeFunction(this._id)); -}; - -var transition_select = function(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - schedule(subgroup[i], name, id, i, subgroup, get(node, id)); - } - } - } - - return new Transition(subgroups, this._parents, name, id); -}; - -var transition_selectAll = function(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - for (var children = select.call(node, node.__data__, i, group), child, inherit = get(node, id), k = 0, l = children.length; k < l; ++k) { - if (child = children[k]) { - schedule(child, name, id, k, children, inherit); - } - } - subgroups.push(children); - parents.push(node); - } - } - } - - return new Transition(subgroups, parents, name, id); -}; - -var Selection$1 = selection.prototype.constructor; - -var transition_selection = function() { - return new Selection$1(this._groups, this._parents); -}; - -function styleRemove$1(name, interpolate$$1) { - var value00, - value10, - interpolate0; - return function() { - var value0 = styleValue(this, name), - value1 = (this.style.removeProperty(name), styleValue(this, name)); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -function styleRemoveEnd(name) { - return function() { - this.style.removeProperty(name); - }; -} - -function styleConstant$1(name, interpolate$$1, value1) { - var value00, - interpolate0; - return function() { - var value0 = styleValue(this, name); - return value0 === value1 ? null - : value0 === value00 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value1); - }; -} - -function styleFunction$1(name, interpolate$$1, value) { - var value00, - value10, - interpolate0; - return function() { - var value0 = styleValue(this, name), - value1 = value(this); - if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name)); - return value0 === value1 ? null - : value0 === value00 && value1 === value10 ? interpolate0 - : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); - }; -} - -var transition_style = function(name, value, priority) { - var i = (name += "") === "transform" ? interpolateTransformCss : interpolate; - return value == null ? this - .styleTween(name, styleRemove$1(name, i)) - .on("end.style." + name, styleRemoveEnd(name)) - : this.styleTween(name, typeof value === "function" - ? styleFunction$1(name, i, tweenValue(this, "style." + name, value)) - : styleConstant$1(name, i, value + ""), priority); -}; - -function styleTween(name, value, priority) { - function tween() { - var node = this, i = value.apply(node, arguments); - return i && function(t) { - node.style.setProperty(name, i(t), priority); - }; - } - tween._value = value; - return tween; -} - -var transition_styleTween = function(name, value, priority) { - var key = "style." + (name += ""); - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); -}; - -function textConstant$1(value) { - return function() { - this.textContent = value; - }; -} - -function textFunction$1(value) { - return function() { - var value1 = value(this); - this.textContent = value1 == null ? "" : value1; - }; -} - -var transition_text = function(value) { - return this.tween("text", typeof value === "function" - ? textFunction$1(tweenValue(this, "text", value)) - : textConstant$1(value == null ? "" : value + "")); -}; - -var transition_transition = function() { - var name = this._name, - id0 = this._id, - id1 = newId(); - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - var inherit = get(node, id0); - schedule(node, name, id1, i, group, { - time: inherit.time + inherit.delay + inherit.duration, - delay: 0, - duration: inherit.duration, - ease: inherit.ease - }); - } - } - } - - return new Transition(groups, this._parents, name, id1); -}; - -var id = 0; - -function Transition(groups, parents, name, id) { - this._groups = groups; - this._parents = parents; - this._name = name; - this._id = id; -} - -function transition(name) { - return selection().transition(name); -} - -function newId() { - return ++id; -} - -var selection_prototype = selection.prototype; - -Transition.prototype = transition.prototype = { - constructor: Transition, - select: transition_select, - selectAll: transition_selectAll, - filter: transition_filter, - merge: transition_merge, - selection: transition_selection, - transition: transition_transition, - call: selection_prototype.call, - nodes: selection_prototype.nodes, - node: selection_prototype.node, - size: selection_prototype.size, - empty: selection_prototype.empty, - each: selection_prototype.each, - on: transition_on, - attr: transition_attr, - attrTween: transition_attrTween, - style: transition_style, - styleTween: transition_styleTween, - text: transition_text, - remove: transition_remove, - tween: transition_tween, - delay: transition_delay, - duration: transition_duration, - ease: transition_ease -}; - -var defaultTiming = { - time: null, // Set on use. - delay: 0, - duration: 250, - ease: cubicInOut -}; - -function inherit(node, id) { - var timing; - while (!(timing = node.__transition) || !(timing = timing[id])) { - if (!(node = node.parentNode)) { - return defaultTiming.time = now(), defaultTiming; - } - } - return timing; -} - -var selection_transition = function(name) { - var id, - timing; - - if (name instanceof Transition) { - id = name._id, name = name._name; - } else { - id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; - } - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - schedule(node, name, id, i, group, timing || inherit(node, id)); - } - } - } - - return new Transition(groups, this._parents, name, id); -}; - -selection.prototype.interrupt = selection_interrupt; -selection.prototype.transition = selection_transition; - -exports.select = select; -exports.selection = selection; -exports.hierarchy = hierarchy; -exports.partition = partition; -exports.scaleLinear = linear; -exports.easeCubic = cubicInOut; -exports.ascending = ascending$1; -exports.map = map$1; -exports.transition = transition; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); -` diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/LICENSE b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/D3_FLAME_GRAPH_LICENSE similarity index 100% rename from src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/LICENSE rename to src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/D3_FLAME_GRAPH_LICENSE diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/D3_LICENSE b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/D3_LICENSE new file mode 100644 index 00000000000000..b0145150fd35f3 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/D3_LICENSE @@ -0,0 +1,13 @@ +Copyright 2010-2021 Mike Bostock + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/README.md b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/README.md new file mode 100644 index 00000000000000..eb84b6800784ed --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/README.md @@ -0,0 +1,33 @@ +# Building a customized D3.js bundle + +The D3.js version distributed with pprof is customized to only include the +modules required by pprof. + +## Dependencies + +- Install [npm](https://www.npmjs.com). + +## Building + +- Run `update.sh` to: + - Download npm package dependencies (declared in `package.json` and `package-lock.json`) + - Create a d3.js bundle containing the JavScript of d3 and d3-flame-graph (by running `webpack`) + +This will `d3_flame_graph.go`, the minified custom D3.js bundle as Go source code. + +# References / Appendix + +## D3 Custom Bundle + +A demonstration of building a custom D3 4.0 bundle using ES2015 modules and Rollup. + +[bl.ocks.org/mbostock/bb09af4c39c79cffcde4](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4) + +## Old version of d3-pprof + +A previous version of d3-flame-graph bundled for pprof used Rollup instead of +Webpack. This has now been migrated directly into this directory. + +The repository configuring Rollup was here: + +[github.com/spiermar/d3-pprof](https://github.com/spiermar/d3-pprof) diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/d3_flame_graph.go b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/d3_flame_graph.go index 58a7fb4c40a8a9..7e279419951003 100644 --- a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/d3_flame_graph.go +++ b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/d3_flame_graph.go @@ -1,915 +1,17 @@ -// A D3.js plugin that produces flame graphs from hierarchical data. +// D3.js is a JavaScript library for manipulating documents based on data. +// https://github.com/d3/d3 +// See D3_LICENSE file for license details + +// d3-flame-graph is a D3.js plugin that produces flame graphs from hierarchical data. // https://github.com/spiermar/d3-flame-graph -// Version 2.0.0-alpha4 -// See LICENSE file for license details +// See D3_FLAME_GRAPH_LICENSE file for license details package d3flamegraph -// JSSource returns the d3-flamegraph.js file +// JSSource returns the d3 and d3-flame-graph JavaScript bundle const JSSource = ` -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3')) : - typeof define === 'function' && define.amd ? define(['exports', 'd3'], factory) : - (factory((global.d3 = global.d3 || {}),global.d3)); -}(this, (function (exports,d3) { 'use strict'; - -var d3__default = 'default' in d3 ? d3['default'] : d3; - -var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - - - - -function createCommonjsModule(fn, module) { - return module = { exports: {} }, fn(module, module.exports), module.exports; -} - -var d3Tip = createCommonjsModule(function (module) { -// d3.tip -// Copyright (c) 2013 Justin Palmer -// -// Tooltips for d3.js SVG visualizations - -(function (root, factory) { - if (typeof undefined === 'function' && undefined.amd) { - // AMD. Register as an anonymous module with d3 as a dependency. - undefined(['d3'], factory); - } else if ('object' === 'object' && module.exports) { - // CommonJS - var d3$$1 = d3__default; - module.exports = factory(d3$$1); - } else { - // Browser global. - root.d3.tip = factory(root.d3); - } -}(commonjsGlobal, function (d3$$1) { - - // Public - contructs a new tooltip - // - // Returns a tip - return function() { - var direction = d3_tip_direction, - offset = d3_tip_offset, - html = d3_tip_html, - node = initNode(), - svg = null, - point = null, - target = null; - - function tip(vis) { - svg = getSVGNode(vis); - point = svg.createSVGPoint(); - document.body.appendChild(node); - } - - // Public - show the tooltip on the screen - // - // Returns a tip - tip.show = function() { - var args = Array.prototype.slice.call(arguments); - if(args[args.length - 1] instanceof SVGElement) target = args.pop(); - - var content = html.apply(this, args), - poffset = offset.apply(this, args), - dir = direction.apply(this, args), - nodel = getNodeEl(), - i = directions.length, - coords, - scrollTop = document.documentElement.scrollTop || document.body.scrollTop, - scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; - - nodel.html(content) - .style('opacity', 1).style('pointer-events', 'all'); - - while(i--) nodel.classed(directions[i], false); - coords = direction_callbacks.get(dir).apply(this); - nodel.classed(dir, true) - .style('top', (coords.top + poffset[0]) + scrollTop + 'px') - .style('left', (coords.left + poffset[1]) + scrollLeft + 'px'); - - return tip; - }; - - // Public - hide the tooltip - // - // Returns a tip - tip.hide = function() { - var nodel = getNodeEl(); - nodel.style('opacity', 0).style('pointer-events', 'none'); - return tip - }; - - // Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value. - // - // n - name of the attribute - // v - value of the attribute - // - // Returns tip or attribute value - tip.attr = function(n, v) { - if (arguments.length < 2 && typeof n === 'string') { - return getNodeEl().attr(n) - } else { - var args = Array.prototype.slice.call(arguments); - d3$$1.selection.prototype.attr.apply(getNodeEl(), args); - } - - return tip - }; - - // Public: Proxy style calls to the d3 tip container. Sets or gets a style value. - // - // n - name of the property - // v - value of the property - // - // Returns tip or style property value - tip.style = function(n, v) { - if (arguments.length < 2 && typeof n === 'string') { - return getNodeEl().style(n) - } else { - var args = Array.prototype.slice.call(arguments); - d3$$1.selection.prototype.style.apply(getNodeEl(), args); - } - - return tip - }; - - // Public: Set or get the direction of the tooltip - // - // v - One of n(north), s(south), e(east), or w(west), nw(northwest), - // sw(southwest), ne(northeast) or se(southeast) - // - // Returns tip or direction - tip.direction = function(v) { - if (!arguments.length) return direction - direction = v == null ? v : functor(v); - - return tip - }; - - // Public: Sets or gets the offset of the tip - // - // v - Array of [x, y] offset - // - // Returns offset or - tip.offset = function(v) { - if (!arguments.length) return offset - offset = v == null ? v : functor(v); - - return tip - }; - - // Public: sets or gets the html value of the tooltip - // - // v - String value of the tip - // - // Returns html value or tip - tip.html = function(v) { - if (!arguments.length) return html - html = v == null ? v : functor(v); - - return tip - }; - - // Public: destroys the tooltip and removes it from the DOM - // - // Returns a tip - tip.destroy = function() { - if(node) { - getNodeEl().remove(); - node = null; - } - return tip; - }; - - function d3_tip_direction() { return 'n' } - function d3_tip_offset() { return [0, 0] } - function d3_tip_html() { return ' ' } - - var direction_callbacks = d3$$1.map({ - n: direction_n, - s: direction_s, - e: direction_e, - w: direction_w, - nw: direction_nw, - ne: direction_ne, - sw: direction_sw, - se: direction_se - }), - - directions = direction_callbacks.keys(); - - function direction_n() { - var bbox = getScreenBBox(); - return { - top: bbox.n.y - node.offsetHeight, - left: bbox.n.x - node.offsetWidth / 2 - } - } - - function direction_s() { - var bbox = getScreenBBox(); - return { - top: bbox.s.y, - left: bbox.s.x - node.offsetWidth / 2 - } - } - - function direction_e() { - var bbox = getScreenBBox(); - return { - top: bbox.e.y - node.offsetHeight / 2, - left: bbox.e.x - } - } - - function direction_w() { - var bbox = getScreenBBox(); - return { - top: bbox.w.y - node.offsetHeight / 2, - left: bbox.w.x - node.offsetWidth - } - } - - function direction_nw() { - var bbox = getScreenBBox(); - return { - top: bbox.nw.y - node.offsetHeight, - left: bbox.nw.x - node.offsetWidth - } - } - - function direction_ne() { - var bbox = getScreenBBox(); - return { - top: bbox.ne.y - node.offsetHeight, - left: bbox.ne.x - } - } - - function direction_sw() { - var bbox = getScreenBBox(); - return { - top: bbox.sw.y, - left: bbox.sw.x - node.offsetWidth - } - } - - function direction_se() { - var bbox = getScreenBBox(); - return { - top: bbox.se.y, - left: bbox.e.x - } - } - - function initNode() { - var node = d3$$1.select(document.createElement('div')); - node.style('position', 'absolute').style('top', 0).style('opacity', 0) - .style('pointer-events', 'none').style('box-sizing', 'border-box'); - - return node.node() - } - - function getSVGNode(el) { - el = el.node(); - if(el.tagName.toLowerCase() === 'svg') - return el - - return el.ownerSVGElement - } - - function getNodeEl() { - if(node === null) { - node = initNode(); - // re-add node to DOM - document.body.appendChild(node); - } - return d3$$1.select(node); - } - - // Private - gets the screen coordinates of a shape - // - // Given a shape on the screen, will return an SVGPoint for the directions - // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest), - // sw(southwest). - // - // +-+-+ - // | | - // + + - // | | - // +-+-+ - // - // Returns an Object {n, s, e, w, nw, sw, ne, se} - function getScreenBBox() { - var targetel = target || d3$$1.event.target; - - while ('undefined' === typeof targetel.getScreenCTM && 'undefined' === targetel.parentNode) { - targetel = targetel.parentNode; - } - - var bbox = {}, - matrix = targetel.getScreenCTM(), - tbbox = targetel.getBBox(), - width = tbbox.width, - height = tbbox.height, - x = tbbox.x, - y = tbbox.y; - - point.x = x; - point.y = y; - bbox.nw = point.matrixTransform(matrix); - point.x += width; - bbox.ne = point.matrixTransform(matrix); - point.y += height; - bbox.se = point.matrixTransform(matrix); - point.x -= width; - bbox.sw = point.matrixTransform(matrix); - point.y -= height / 2; - bbox.w = point.matrixTransform(matrix); - point.x += width; - bbox.e = point.matrixTransform(matrix); - point.x -= width / 2; - point.y -= height / 2; - bbox.n = point.matrixTransform(matrix); - point.y += height; - bbox.s = point.matrixTransform(matrix); - - return bbox - } - - // Private - replace D3JS 3.X d3.functor() function - function functor(v) { - return typeof v === "function" ? v : function() { - return v - } - } - - return tip - }; - -})); -}); - -var flamegraph = function () { - var w = 960; // graph width - var h = null; // graph height - var c = 18; // cell height - var selection = null; // selection - var tooltip = true; // enable tooltip - var title = ''; // graph title - var transitionDuration = 750; - var transitionEase = d3.easeCubic; // tooltip offset - var sort = false; - var inverted = false; // invert the graph direction - var clickHandler = null; - var minFrameSize = 0; - var details = null; - - var tip = d3Tip() - .direction('s') - .offset([8, 0]) - .attr('class', 'd3-flame-graph-tip') - .html(function (d) { return label(d) }); - - var svg; - - function name (d) { - return d.data.n || d.data.name - } - - function libtype (d) { - return d.data.l || d.data.libtype - } - - function children (d) { - return d.c || d.children - } - - function value (d) { - return d.v || d.value - } - - var label = function (d) { - return name(d) + ' (' + d3.format('.3f')(100 * (d.x1 - d.x0), 3) + '%, ' + value(d) + ' samples)' - }; - - function setDetails (t) { - if (details) { details.innerHTML = t; } - } - - var colorMapper = function (d) { - return d.highlight ? '#E600E6' : colorHash(name(d), libtype(d)) - }; - - function generateHash (name) { - // Return a vector (0.0->1.0) that is a hash of the input string. - // The hash is computed to favor early characters over later ones, so - // that strings with similar starts have similar vectors. Only the first - // 6 characters are considered. - const MAX_CHAR = 6; - - var hash = 0; - var maxHash = 0; - var weight = 1; - var mod = 10; - - if (name) { - for (var i = 0; i < name.length; i++) { - if (i > MAX_CHAR) { break } - hash += weight * (name.charCodeAt(i) % mod); - maxHash += weight * (mod - 1); - weight *= 0.70; - } - if (maxHash > 0) { hash = hash / maxHash; } - } - return hash - } - - function colorHash (name, libtype) { - // Return a color for the given name and library type. The library type - // selects the hue, and the name is hashed to a color in that hue. - - var r; - var g; - var b; - - // Select hue. Order is important. - var hue; - if (typeof libtype === 'undefined' || libtype === '') { - // default when libtype is not in use - hue = 'warm'; - } else { - hue = 'red'; - if (name.match(/::/)) { - hue = 'yellow'; - } - if (libtype === 'kernel') { - hue = 'orange'; - } else if (libtype === 'jit') { - hue = 'green'; - } else if (libtype === 'inlined') { - hue = 'aqua'; - } - } - - // calculate hash - var vector = 0; - if (name) { - var nameArr = name.split('` + "`" + `'); - if (nameArr.length > 1) { - name = nameArr[nameArr.length - 1]; // drop module name if present - } - name = name.split('(')[0]; // drop extra info - vector = generateHash(name); - } - - // calculate color - if (hue === 'red') { - r = 200 + Math.round(55 * vector); - g = 50 + Math.round(80 * vector); - b = g; - } else if (hue === 'orange') { - r = 190 + Math.round(65 * vector); - g = 90 + Math.round(65 * vector); - b = 0; - } else if (hue === 'yellow') { - r = 175 + Math.round(55 * vector); - g = r; - b = 50 + Math.round(20 * vector); - } else if (hue === 'green') { - r = 50 + Math.round(60 * vector); - g = 200 + Math.round(55 * vector); - b = r; - } else if (hue === 'aqua') { - r = 50 + Math.round(60 * vector); - g = 165 + Math.round(55 * vector); - b = g; - } else { - // original warm palette - r = 200 + Math.round(55 * vector); - g = 0 + Math.round(230 * (1 - vector)); - b = 0 + Math.round(55 * (1 - vector)); - } - - return 'rgb(' + r + ',' + g + ',' + b + ')' - } - - function hide (d) { - d.data.hide = true; - if (children(d)) { - children(d).forEach(hide); - } - } - - function show (d) { - d.data.fade = false; - d.data.hide = false; - if (children(d)) { - children(d).forEach(show); - } - } - - function getSiblings (d) { - var siblings = []; - if (d.parent) { - var me = d.parent.children.indexOf(d); - siblings = d.parent.children.slice(0); - siblings.splice(me, 1); - } - return siblings - } - - function hideSiblings (d) { - var siblings = getSiblings(d); - siblings.forEach(function (s) { - hide(s); - }); - if (d.parent) { - hideSiblings(d.parent); - } - } - function fadeAncestors (d) { - if (d.parent) { - d.parent.data.fade = true; - fadeAncestors(d.parent); - } - } - - // function getRoot (d) { - // if (d.parent) { - // return getRoot(d.parent) - // } - // return d - // } - - function zoom (d) { - tip.hide(d); - hideSiblings(d); - show(d); - fadeAncestors(d); - update(); - if (typeof clickHandler === 'function') { - clickHandler(d); - } - } - - function searchTree (d, term) { - var re = new RegExp(term); - var searchResults = []; - - function searchInner (d) { - var label = name(d); - - if (children(d)) { - children(d).forEach(function (child) { - searchInner(child); - }); - } - - if (label.match(re)) { - d.highlight = true; - searchResults.push(d); - } else { - d.highlight = false; - } - } - - searchInner(d); - return searchResults - } - - function clear (d) { - d.highlight = false; - if (children(d)) { - children(d).forEach(function (child) { - clear(child); - }); - } - } - - function doSort (a, b) { - if (typeof sort === 'function') { - return sort(a, b) - } else if (sort) { - return d3.ascending(name(a), name(b)) - } - } - - var p = d3.partition(); - - function filterNodes (root) { - var nodeList = root.descendants(); - if (minFrameSize > 0) { - var kx = w / (root.x1 - root.x0); - nodeList = nodeList.filter(function (el) { - return ((el.x1 - el.x0) * kx) > minFrameSize - }); - } - return nodeList - } - - function update () { - selection.each(function (root) { - var x = d3.scaleLinear().range([0, w]); - var y = d3.scaleLinear().range([0, c]); - - if (sort) root.sort(doSort); - root.sum(function (d) { - if (d.fade || d.hide) { - return 0 - } - // The node's self value is its total value minus all children. - var v = value(d); - if (children(d)) { - var c = children(d); - for (var i = 0; i < c.length; i++) { - v -= value(c[i]); - } - } - return v - }); - p(root); - - var kx = w / (root.x1 - root.x0); - function width (d) { return (d.x1 - d.x0) * kx } - - var descendants = filterNodes(root); - var g = d3.select(this).select('svg').selectAll('g').data(descendants, function (d) { return d.id }); - - g.transition() - .duration(transitionDuration) - .ease(transitionEase) - .attr('transform', function (d) { return 'translate(' + x(d.x0) + ',' + (inverted ? y(d.depth) : (h - y(d.depth) - c)) + ')' }); - - g.select('rect') - .attr('width', width); - - var node = g.enter() - .append('svg:g') - .attr('transform', function (d) { return 'translate(' + x(d.x0) + ',' + (inverted ? y(d.depth) : (h - y(d.depth) - c)) + ')' }); - - node.append('svg:rect') - .transition() - .delay(transitionDuration / 2) - .attr('width', width); - - if (!tooltip) { node.append('svg:title'); } - - node.append('foreignObject') - .append('xhtml:div'); - - // Now we have to re-select to see the new elements (why?). - g = d3.select(this).select('svg').selectAll('g').data(descendants, function (d) { return d.id }); - - g.attr('width', width) - .attr('height', function (d) { return c }) - .attr('name', function (d) { return name(d) }) - .attr('class', function (d) { return d.data.fade ? 'frame fade' : 'frame' }); - - g.select('rect') - .attr('height', function (d) { return c }) - .attr('fill', function (d) { return colorMapper(d) }); - - if (!tooltip) { - g.select('title') - .text(label); - } - - g.select('foreignObject') - .attr('width', width) - .attr('height', function (d) { return c }) - .select('div') - .attr('class', 'd3-flame-graph-label') - .style('display', function (d) { return (width(d) < 35) ? 'none' : 'block' }) - .transition() - .delay(transitionDuration) - .text(name); - - g.on('click', zoom); - - g.exit() - .remove(); - - g.on('mouseover', function (d) { - if (tooltip) tip.show(d, this); - setDetails(label(d)); - }).on('mouseout', function (d) { - if (tooltip) tip.hide(d); - setDetails(''); - }); - }); - } - - function merge (data, samples) { - samples.forEach(function (sample) { - var node = data.find(function (element) { - return (element.name === sample.name) - }); - - if (node) { - if (node.original) { - node.original += sample.value; - } else { - node.value += sample.value; - } - if (sample.children) { - if (!node.children) { - node.children = []; - } - merge(node.children, sample.children); - } - } else { - data.push(sample); - } - }); - } - - function s4 () { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1) - } - - function injectIds (node) { - node.id = s4() + '-' + s4() + '-' + '-' + s4() + '-' + s4(); - var children = node.c || node.children || []; - for (var i = 0; i < children.length; i++) { - injectIds(children[i]); - } - } - - function chart (s) { - var root = d3.hierarchy( - s.datum(), function (d) { return children(d) } - ); - injectIds(root); - selection = s.datum(root); - - if (!arguments.length) return chart - - if (!h) { - h = (root.height + 2) * c; - } - - selection.each(function (data) { - if (!svg) { - svg = d3.select(this) - .append('svg:svg') - .attr('width', w) - .attr('height', h) - .attr('class', 'partition d3-flame-graph') - .call(tip); - - svg.append('svg:text') - .attr('class', 'title') - .attr('text-anchor', 'middle') - .attr('y', '25') - .attr('x', w / 2) - .attr('fill', '#808080') - .text(title); - } - }); - - // first draw - update(); - } - - chart.height = function (_) { - if (!arguments.length) { return h } - h = _; - return chart - }; - - chart.width = function (_) { - if (!arguments.length) { return w } - w = _; - return chart - }; - - chart.cellHeight = function (_) { - if (!arguments.length) { return c } - c = _; - return chart - }; - - chart.tooltip = function (_) { - if (!arguments.length) { return tooltip } - if (typeof _ === 'function') { - tip = _; - } - tooltip = !!_; - return chart - }; - - chart.title = function (_) { - if (!arguments.length) { return title } - title = _; - return chart - }; - - chart.transitionDuration = function (_) { - if (!arguments.length) { return transitionDuration } - transitionDuration = _; - return chart - }; - - chart.transitionEase = function (_) { - if (!arguments.length) { return transitionEase } - transitionEase = _; - return chart - }; - - chart.sort = function (_) { - if (!arguments.length) { return sort } - sort = _; - return chart - }; - - chart.inverted = function (_) { - if (!arguments.length) { return inverted } - inverted = _; - return chart - }; - - chart.label = function (_) { - if (!arguments.length) { return label } - label = _; - return chart - }; - - chart.search = function (term) { - var searchResults = []; - selection.each(function (data) { - searchResults = searchTree(data, term); - update(); - }); - return searchResults - }; - - chart.clear = function () { - selection.each(function (data) { - clear(data); - update(); - }); - }; - - chart.zoomTo = function (d) { - zoom(d); - }; - - chart.resetZoom = function () { - selection.each(function (data) { - zoom(data); // zoom to root - }); - }; - - chart.onClick = function (_) { - if (!arguments.length) { - return clickHandler - } - clickHandler = _; - return chart - }; - - chart.merge = function (samples) { - var newRoot; // Need to re-create hierarchy after data changes. - selection.each(function (root) { - merge([root.data], [samples]); - newRoot = d3.hierarchy(root.data, function (d) { return children(d) }); - injectIds(newRoot); - }); - selection = selection.datum(newRoot); - update(); - }; - - chart.color = function (_) { - if (!arguments.length) { return colorMapper } - colorMapper = _; - return chart - }; - - chart.minFrameSize = function (_) { - if (!arguments.length) { return minFrameSize } - minFrameSize = _; - return chart - }; - - chart.details = function (_) { - if (!arguments.length) { return details } - details = _; - return chart - }; - - return chart -}; - -exports.flamegraph = flamegraph; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); +!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var e=n();for(var r in e)("object"==typeof exports?exports:t)[r]=e[r]}}(self,(function(){return(()=>{"use strict";var t={d:(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},o:(t,n)=>Object.prototype.hasOwnProperty.call(t,n),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},n={};function e(){}function r(t){return null==t?e:function(){return this.querySelector(t)}}function i(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function o(){return[]}function u(t){return function(n){return n.matches(t)}}t.r(n),t.d(n,{flamegraph:()=>ji,select:()=>pt});var a=Array.prototype.find;function l(){return this.firstElementChild}var s=Array.prototype.filter;function c(){return Array.from(this.children)}function f(t){return new Array(t.length)}function h(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function p(t){return function(){return t}}function d(t,n,e,r,i,o){for(var u,a=0,l=n.length,s=o.length;an?1:t>=n?0:NaN}h.prototype={constructor:h,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var _="http://www.w3.org/1999/xhtml";const w={svg:"http://www.w3.org/2000/svg",xhtml:_,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function b(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),w.hasOwnProperty(n)?{space:w[n],local:t}:t}function x(t){return function(){this.removeAttribute(t)}}function M(t){return function(){this.removeAttributeNS(t.space,t.local)}}function A(t,n){return function(){this.setAttribute(t,n)}}function N(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function E(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function k(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function S(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function C(t){return function(){this.style.removeProperty(t)}}function P(t,n,e){return function(){this.style.setProperty(t,n,e)}}function j(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function q(t,n){return t.style.getPropertyValue(n)||S(t).getComputedStyle(t,null).getPropertyValue(n)}function O(t){return function(){delete this[t]}}function L(t,n){return function(){this[t]=n}}function T(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function B(t){return t.trim().split(/^|\s+/)}function D(t){return t.classList||new H(t)}function H(t){this._node=t,this._names=B(t.getAttribute("class")||"")}function R(t,n){for(var e=D(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function ut(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var ft=[null];function ht(t,n){this._groups=t,this._parents=n}function pt(t){return"string"==typeof t?new ht([[document.querySelector(t)]],[document.documentElement]):new ht([[t]],ft)}function dt(){}function gt(t){return null==t?dt:function(){return this.querySelector(t)}}function vt(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function yt(){return[]}function mt(t){return null==t?yt:function(){return this.querySelectorAll(t)}}function _t(t){return function(){return this.matches(t)}}function wt(t){return function(n){return n.matches(t)}}ht.prototype=function(){return new ht([[document.documentElement]],ft)}.prototype={constructor:ht,select:function(t){"function"!=typeof t&&(t=r(t));for(var n=this._groups,e=n.length,i=new Array(e),o=0;o=E&&(E=N+1);!(A=b[E])&&++E<_;);M._next=A||null}}return(u=new ht(u,r))._enter=a,u._exit=l,u},enter:function(){return new ht(this._enter||this._groups.map(f),this._parents)},exit:function(){return new ht(this._exit||this._groups.map(f),this._parents)},join:function(t,n,e){var r=this.enter(),i=this,o=this.exit();return"function"==typeof t?(r=t(r))&&(r=r.selection()):r=r.append(t+""),null!=n&&(i=n(i))&&(i=i.selection()),null==e?o.remove():e(o),r&&i?r.merge(i).order():i},merge:function(t){for(var n=t.selection?t.selection():t,e=this._groups,r=n._groups,i=e.length,o=r.length,u=Math.min(i,o),a=new Array(i),l=0;l=0;)(r=i[o])&&(u&&4^r.compareDocumentPosition(u)&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=m);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?C:"function"==typeof n?j:P)(t,n,null==e?"":e)):q(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?O:"function"==typeof n?T:L)(t,n)):this.node()[t]},classed:function(t,n){var e=B(t+"");if(arguments.length<2){for(var r=D(this.node()),i=-1,o=e.length;++in?1:t>=n?0:NaN}Et.prototype={constructor:Et,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var Ot="http://www.w3.org/1999/xhtml";const Lt={svg:"http://www.w3.org/2000/svg",xhtml:Ot,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function Tt(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Lt.hasOwnProperty(n)?{space:Lt[n],local:t}:t}function Bt(t){return function(){this.removeAttribute(t)}}function Dt(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Ht(t,n){return function(){this.setAttribute(t,n)}}function Rt(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function Vt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function Xt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function zt(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function It(t){return function(){this.style.removeProperty(t)}}function $t(t,n,e){return function(){this.style.setProperty(t,n,e)}}function Ut(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function Yt(t,n){return t.style.getPropertyValue(n)||zt(t).getComputedStyle(t,null).getPropertyValue(n)}function Ft(t){return function(){delete this[t]}}function Zt(t,n){return function(){this[t]=n}}function Gt(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function Jt(t){return t.trim().split(/^|\s+/)}function Kt(t){return t.classList||new Qt(t)}function Qt(t){this._node=t,this._names=Jt(t.getAttribute("class")||"")}function Wt(t,n){for(var e=Kt(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function bn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var En=[null];function kn(t,n){this._groups=t,this._parents=n}function Sn(){return new kn([[document.documentElement]],En)}kn.prototype=Sn.prototype={constructor:kn,select:function(t){"function"!=typeof t&&(t=gt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=b&&(b=w+1);!(_=v[b])&&++b=0;)(r=i[o])&&(u&&4^r.compareDocumentPosition(u)&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=qt);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?It:"function"==typeof n?Ut:$t)(t,n,null==e?"":e)):Yt(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?Ft:"function"==typeof n?Gt:Zt)(t,n)):this.node()[t]},classed:function(t,n){var e=Jt(t+"");if(arguments.length<2){for(var r=Kt(this.node()),i=-1,o=e.length;++i1?r[0]+r.slice(2):r,+t.slice(e+1)]}function qn(t){return(t=jn(Math.abs(t)))?t[1]:NaN}var On,Ln=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Tn(t){if(!(n=Ln.exec(t)))throw new Error("invalid format: "+t);var n;return new Bn({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function Bn(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function Dn(t,n){var e=jn(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Tn.prototype=Bn.prototype,Bn.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const Hn={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>Dn(100*t,n),r:Dn,s:function(t,n){var e=jn(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(On=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+jn(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Rn(t){return t}var Vn,Xn,zn,In=Array.prototype.map,$n=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function Un(t,n){return null==t||null==n?NaN:tn?1:t>=n?0:NaN}function Yn(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function Fn(t){var n=0,e=t.children,r=e&&e.length;if(r)for(;--r>=0;)n+=e[r].value;else n=1;t.value=n}function Zn(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Jn)):void 0===n&&(n=Gn);for(var e,r,i,o,u,a=new Wn(t),l=[a];e=l.pop();)if((i=n(e.data))&&(u=(i=Array.from(i)).length))for(e.children=i,o=u-1;o>=0;--o)l.push(r=i[o]=new Wn(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(Qn)}function Gn(t){return t.children}function Jn(t){return Array.isArray(t)?t[1]:null}function Kn(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Qn(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Wn(t){this.data=t,this.depth=this.height=0,this.parent=null}Vn=function(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?Rn:(n=In.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],u=0,a=n[0],l=0;i>0&&a>0&&(l+a+1>r&&(a=Math.max(1,r-l)),o.push(t.substring(i-=a,i+a)),!((l+=a+1)>r));)a=n[u=(u+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",u=void 0===t.decimal?".":t.decimal+"",a=void 0===t.numerals?Rn:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(In.call(t.numerals,String)),l=void 0===t.percent?"%":t.percent+"",s=void 0===t.minus?"−":t.minus+"",c=void 0===t.nan?"NaN":t.nan+"";function f(t){var n=(t=Tn(t)).fill,e=t.align,f=t.sign,h=t.symbol,p=t.zero,d=t.width,g=t.comma,v=t.precision,y=t.trim,m=t.type;"n"===m?(g=!0,m="g"):Hn[m]||(void 0===v&&(v=12),y=!0,m="g"),(p||"0"===n&&"="===e)&&(p=!0,n="0",e="=");var _="$"===h?i:"#"===h&&/[boxX]/.test(m)?"0"+m.toLowerCase():"",w="$"===h?o:/[%p]/.test(m)?l:"",b=Hn[m],x=/[defgprs%]/.test(m);function M(t){var i,o,l,h=_,M=w;if("c"===m)M=b(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?c:b(Math.abs(t),v),y&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==f&&(A=!1),h=(A?"("===f?f:s:"-"===f||"("===f?"":f)+h,M=("s"===m?$n[8+On/3]:"")+M+(A&&"("===f?")":""),x)for(i=-1,o=t.length;++i(l=t.charCodeAt(i))||l>57){M=(46===l?u+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!p&&(t=r(t,1/0));var N=h.length+t.length+M.length,E=N>1)+h+t+M+E.slice(N);break;default:t=E+h+t+M}return a(t)}return v=void 0===v?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v)),M.toString=function(){return t+""},M}return{format:f,formatPrefix:function(t,n){var e=f(((t=Tn(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(qn(n)/3))),i=Math.pow(10,-r),o=$n[8+r/3];return function(t){return e(i*t)+o}}}}({thousands:",",grouping:[3],currency:["$",""]}),Xn=Vn.format,zn=Vn.formatPrefix,Wn.prototype=Zn.prototype={constructor:Wn,count:function(){return this.eachAfter(Fn)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,u=[o],a=[],l=-1;o=u.pop();)if(a.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Zn(this).eachBefore(Kn)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e=0?(o>=te?10:o>=ne?5:o>=ee?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=te?10:o>=ne?5:o>=ee?2:1)}function ie(t){let n=t,e=t,r=t;function i(t,n,i=0,o=t.length){if(i>>1;r(t[e],n)<0?i=e+1:o=e}while(it(n)-e,e=Un,r=(n,e)=>Un(t(n),e)),{left:i,center:function(t,e,r=0,o=t.length){const u=i(t,e,r,o-1);return u>r&&n(t[u-1],e)>-n(t[u],e)?u-1:u},right:function(t,n,i=0,o=t.length){if(i>>1;r(t[e],n)<=0?i=e+1:o=e}while(i>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?Se(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?Se(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=ye.exec(t))?new je(n[1],n[2],n[3],1):(n=me.exec(t))?new je(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=_e.exec(t))?Se(n[1],n[2],n[3],n[4]):(n=we.exec(t))?Se(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=be.exec(t))?Te(n[1],n[2]/100,n[3]/100,1):(n=xe.exec(t))?Te(n[1],n[2]/100,n[3]/100,n[4]):Me.hasOwnProperty(t)?ke(Me[t]):"transparent"===t?new je(NaN,NaN,NaN,0):null}function ke(t){return new je(t>>16&255,t>>8&255,255&t,1)}function Se(t,n,e,r){return r<=0&&(t=n=e=NaN),new je(t,n,e,r)}function Ce(t){return t instanceof ce||(t=Ee(t)),t?new je((t=t.rgb()).r,t.g,t.b,t.opacity):new je}function Pe(t,n,e,r){return 1===arguments.length?Ce(t):new je(t,n,e,null==r?1:r)}function je(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function qe(){return"#"+Le(this.r)+Le(this.g)+Le(this.b)}function Oe(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Le(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Te(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new De(t,n,e,r)}function Be(t){if(t instanceof De)return new De(t.h,t.s,t.l,t.opacity);if(t instanceof ce||(t=Ee(t)),!t)return new De;if(t instanceof De)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,l=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&l<1?0:u,new De(u,a,l,t.opacity)}function De(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function He(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Re(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Ve(t){return function(){return t}}function Xe(t,n){var e=n-t;return e?function(t,n){return function(e){return t+e*n}}(t,e):Ve(isNaN(t)?n:t)}le(ce,Ee,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:Ae,formatHex:Ae,formatHsl:function(){return Be(this).formatHsl()},formatRgb:Ne,toString:Ne}),le(je,Pe,se(ce,{brighter:function(t){return t=null==t?he:Math.pow(he,t),new je(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?fe:Math.pow(fe,t),new je(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:qe,formatHex:qe,formatRgb:Oe,toString:Oe})),le(De,(function(t,n,e,r){return 1===arguments.length?Be(t):new De(t,n,e,null==r?1:r)}),se(ce,{brighter:function(t){return t=null==t?he:Math.pow(he,t),new De(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?fe:Math.pow(fe,t),new De(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new je(He(t>=240?t-240:t+120,i,r),He(t,i,r),He(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const ze=function t(n){var e=function(t){return 1==(t=+t)?Xe:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Ve(isNaN(n)?e:n)}}(n);function r(t,n){var r=e((t=Pe(t)).r,(n=Pe(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),u=Xe(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return r.gamma=t,r}(1);function Ie(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ro&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,l.push({i:u,x:Ye(e,r)})),o=Ge.lastIndex;return on&&(e=t,t=n,n=e),s=function(e){return Math.max(t,Math.min(n,e))}),r=l>2?or:ir,i=o=null,f}function f(n){return null==n||isNaN(n=+n)?e:(i||(i=r(u.map(t),a,l)))(t(s(n)))}return f.invert=function(e){return s(n((o||(o=r(a,u.map(t),Ye)))(e)))},f.domain=function(t){return arguments.length?(u=Array.from(t,tr),c()):u.slice()},f.range=function(t){return arguments.length?(a=Array.from(t),c()):a.slice()},f.rangeRound=function(t){return a=Array.from(t),l=We,c()},f.clamp=function(t){return arguments.length?(s=!!t||er,c()):s!==er},f.interpolate=function(t){return arguments.length?(l=t,c()):l},f.unknown=function(t){return arguments.length?(e=t,f):e},function(e,r){return t=e,n=r,c()}}()(er,er)}function lr(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function sr(t){var n=t.domain;return t.ticks=function(t){var e=n();return function(t,n,e){var r,i,o,u,a=-1;if(e=+e,(t=+t)==(n=+n)&&e>0)return[t];if((r=n0){let e=Math.round(t/u),r=Math.round(n/u);for(e*un&&--r,o=new Array(i=r-e+1);++an&&--r,o=new Array(i=r-e+1);++a=te?i*=10:o>=ne?i*=5:o>=ee&&(i*=2),n0;){if((i=re(l,s,e))===r)return o[u]=l,o[a]=s,n(o);if(i>0)l=Math.floor(l/i)*i,s=Math.ceil(s/i)*i;else{if(!(i<0))break;l=Math.ceil(l*i)/i,s=Math.floor(s*i)/i}r=i}return t},t}function cr(){var t=ar();return t.copy=function(){return ur(t,cr())},lr.apply(t,arguments),sr(t)}function fr(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var hr={value:()=>{}};function pr(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function vr(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&n._call.call(null,t),n=n._next;--br}()}finally{br=0,function(){for(var t,n,e=_r,r=1/0;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:_r=n);wr=t,Tr(r)}(),Nr=0}}function Lr(){var t=kr.now(),n=t-Ar;n>1e3&&(Er-=n,Ar=t)}function Tr(t){br||(xr&&(xr=clearTimeout(xr)),t-Nr>24?(t<1/0&&(xr=setTimeout(Or,t-kr.now()-Er)),Mr&&(Mr=clearInterval(Mr))):(Mr||(Ar=kr.now(),Mr=setInterval(Lr,1e3)),br=1,Sr(Or)))}function Br(t,n,e){var r=new jr;return n=null==n?0:+n,r.restart((function(e){r.stop(),t(e+n)}),n,e),r}jr.prototype=qr.prototype={constructor:jr,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Cr():+e)+(null==n?0:+n),this._next||wr===this||(wr?wr._next=this:_r=this,wr=this),this._call=t,this._time=e,Tr()},stop:function(){this._call&&(this._call=null,this._time=1/0,Tr())}};var Dr=mr("start","end","cancel","interrupt"),Hr=[];function Rr(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(l){var s,c,f,h;if(1!==e.state)return a();for(s in i)if((h=i[s]).name===e.name){if(3===h.state)return Br(o);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[s]):+s0)throw new Error("too late; already scheduled");return e}function Xr(t,n){var e=zr(t,n);if(e.state>3)throw new Error("too late; already running");return e}function zr(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}var Ir,$r,Ur,Yr,Fr=180/Math.PI,Zr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Gr(t,n,e,r,i,o){var u,a,l;return(u=Math.sqrt(t*t+n*n))&&(t/=u,n/=u),(l=t*e+n*r)&&(e-=t*l,r-=n*l),(a=Math.sqrt(e*e+r*r))&&(e/=a,r/=a,l/=a),t*r180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Ye(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,l),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Ye(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,l),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:Ye(t,e)},{i:a-2,x:Ye(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,l),o=u=null,function(t){for(var n,e=-1,r=l.length;++e=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?Vr:Xr;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}var _i=Cn.prototype.constructor;function wi(t){return function(){this.style.removeProperty(t)}}function bi(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function xi(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&bi(t,o,e)),r}return o._value=n,o}function Mi(t){return function(n){this.textContent=t.call(this,n)}}function Ai(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&Mi(r)),n}return r._value=t,r}var Ni=0;function Ei(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function ki(){return++Ni}var Si=Cn.prototype;Ei.prototype=function(t){return Cn().transition(t)}.prototype={constructor:Ei,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=gt(t));for(var r=this._groups,i=r.length,o=new Array(i),u=0;u{p&&(p.textContent="search: "+n+" of "+e+" total samples ( "+Xn(".3f")(n/e*100,3)+"%)")},d()};const k=E;let S=(t,n,e=!1)=>{if(!n)return!1;let r=b(t);e&&(n=n.toLowerCase(),r=r.toLowerCase());const i=new RegExp(n);return void 0!==r&&r&&r.match(i)};const C=S;let P=function(t){p&&(t?p.textContent=t:"function"==typeof d?d():p.textContent="")};const j=P;let q=function(t){return b(t)+" ("+Xn(".3f")(100*(t.x1-t.x0),3)+"%, "+x(t)+" samples)"},O=function(t){return t.highlight?"#E600E6":function(t,n){let e=w||"warm";w||void 0===n||""===n||(e="red",void 0!==t&&t&&t.match(/::/)&&(e="yellow"),"kernel"===n?e="orange":"jit"===n?e="green":"inlined"===n&&(e="aqua"));const r=function(t){let n=0;if(t){const e=t.split("` + "`" + `");e.length>1&&(t=e[e.length-1]),n=function(t){let n=0,e=0,r=1;if(t){for(let i=0;i6);i++)n+=r*(t.charCodeAt(i)%10),e+=9*r,r*=.7;e>0&&(n/=e)}return n}(t=t.split("(")[0])}return n}(t);return function(t,n){let e,r,i;return"red"===t?(e=200+Math.round(55*n),r=50+Math.round(80*n),i=r):"orange"===t?(e=190+Math.round(65*n),r=90+Math.round(65*n),i=0):"yellow"===t?(e=175+Math.round(55*n),r=e,i=50+Math.round(20*n)):"green"===t?(e=50+Math.round(60*n),r=200+Math.round(55*n),i=e):"pastelgreen"===t?(e=163+Math.round(75*n),r=195+Math.round(49*n),i=72+Math.round(149*n)):"blue"===t?(e=91+Math.round(126*n),r=156+Math.round(76*n),i=221+Math.round(26*n)):"aqua"===t?(e=50+Math.round(60*n),r=165+Math.round(55*n),i=r):"cold"===t?(e=0+Math.round(55*(1-n)),r=0+Math.round(230*(1-n)),i=200+Math.round(55*n)):(e=200+Math.round(55*n),r=0+Math.round(230*(1-n)),i=0+Math.round(55*(1-n))),"rgb("+e+","+r+","+i+")"}(e,r)}(b(t),A(t))};const L=O;function T(t){t.data.fade=!1,t.data.hide=!1,t.children&&t.children.forEach(T)}function B(t){t.parent&&(t.parent.data.fade=!0,B(t.parent))}function D(t){if(i&&i.hide(),function(t){let n,e,r,i=t,o=i.parent;for(;o;){for(n=o.children,e=n.length;e--;)r=n[e],r!==i&&(r.data.hide=!0);i=o,o=i.parent}}(t),T(t),B(t),z(),y){const n=Pn(this).select("svg")._groups[0][0].parentNode.offsetTop,r=(window.innerHeight-n)/e,i=(t.height-r+10)*e;window.scrollTo({top:n+i,left:0,behavior:"smooth"})}"function"==typeof c&&c(t)}function H(t,n){if(t.id===n)return t;{const e=M(t);if(e)for(let t=0;t0){const r=t/(n.x1-n.x0);e=e.filter((function(t){return(t.x1-t.x0)*r>h}))}return e}(r),y=Pn(this).select("svg");y.attr("width",t);let _=y.selectAll("g").data(g,(function(t){return t.id}));if(!n||v){const t=Math.max.apply(null,g.map((function(t){return t.depth})));n=(t+3)*e,n{D(n)})),_.exit().remove(),_.on("mouseover",(function(t,n){i&&i.show(n,this),P(q(n)),"function"==typeof f&&f(n)})).on("mouseout",(function(){i&&i.hide(),P(null)}))}))}function I(t,n){n.forEach((function(n){const e=t.find((function(t){return t.name===n.name}));e?(e.value+=n.value,n.children&&(e.children||(e.children=[]),I(e.children,n.children))):t.push(n)}))}function $(t){let n,e,r,i,o,u,a,l;const s=[],c=[],f=[],h=!g;let p=t.data;for(p.hide?(t.value=0,e=t.children,e&&f.push(e)):(t.value=p.fade?0:x(p),s.push(t));n=s.pop();)if(e=n.children,e&&(o=e.length)){for(i=0;o--;)a=e[o],p=a.data,p.hide?(a.value=0,r=a.children,r&&f.push(r)):(p.fade?a.value=0:(l=x(p),a.value=l,i+=l),s.push(a));h&&n.value&&(n.value-=i),c.push(e)}for(o=c.length;o--;){for(e=c[o],i=0,u=e.length;u--;)i+=e[u].value;e[0].parent.value+=i}for(;f.length;)for(e=f.pop(),u=e.length;u--;)a=e[u],a.value=0,r=a.children,r&&f.push(r)}function U(){r.datum((t=>{if("Node"!==t.constructor.name){const n=Zn(t,M);return function(t){let n=0;!function(t,n){n(t);let e=t.children;if(e){const t=[e];let r,i,o;for(;t.length;)for(e=t.pop(),r=e.length;r--;)i=e[r],n(i),o=i.children,o&&t.push(o)}}(t,(function(t){t.id=n++}))}(n),$(n),n.originalValue=n.value,_&&n.eachAfter((t=>{let n=N(t);const e=t.children;let r=e&&e.length;for(;--r>=0;)n+=e[r].delta;t.delta=n})),n}}))}function Y(e){if(!arguments.length)return Y;r=e,U(),r.each((function(e){if(0===Pn(this).select("svg").size()){const e=Pn(this).append("svg:svg").attr("width",t).attr("class","partition d3-flame-graph");n&&(n(I([n.data],[t]),n.data))),U(),z(),Y):Y},Y.update=function(t){return r?(t&&(r.datum(t),U()),z(),Y):Y},Y.destroy=function(){return r?(i&&(i.hide(),"function"==typeof i.destroy&&i.destroy()),r.selectAll("svg").remove(),Y):Y},Y.setColorMapper=function(t){return arguments.length?(O=n=>{const e=L(n);return t(n,e)},Y):(O=L,Y)},Y.color=Y.setColorMapper,Y.setColorHue=function(t){return arguments.length?(w=t,Y):(w=null,Y)},Y.minFrameSize=function(t){return arguments.length?(h=t,Y):h},Y.setDetailsElement=function(t){return arguments.length?(p=t,Y):p},Y.details=Y.setDetailsElement,Y.selfValue=function(t){return arguments.length?(g=t,Y):g},Y.resetHeightOnZoom=function(t){return arguments.length?(v=t,Y):v},Y.scrollOnZoom=function(t){return arguments.length?(y=t,Y):y},Y.getName=function(t){return arguments.length?(b=t,Y):b},Y.getValue=function(t){return arguments.length?(x=t,Y):x},Y.getChildren=function(t){return arguments.length?(M=t,Y):M},Y.getLibtype=function(t){return arguments.length?(A=t,Y):A},Y.getDelta=function(t){return arguments.length?(N=t,Y):N},Y.setSearchHandler=function(t){return arguments.length?(E=t,Y):(E=k,Y)},Y.setDetailsHandler=function(t){return arguments.length?(P=t,Y):(P=j,Y)},Y.setSearchMatch=function(t){return arguments.length?(S=t,Y):(S=C,Y)},Y}return Cn.prototype.interrupt=function(t){return this.each((function(){!function(t,n){var e,r,i,o=t.__transition,u=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):u=!1;u&&delete t.__transition}}(this,t)}))},Cn.prototype.transition=function(t){var n,e;t instanceof Ei?(n=t._id,t=t._name):(n=ki(),(e=Ci).time=Cr(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,o=0;o d3_flame_graph.go +// D3.js is a JavaScript library for manipulating documents based on data. +// https://github.com/d3/d3 +// See D3_LICENSE file for license details + +// d3-flame-graph is a D3.js plugin that produces flame graphs from hierarchical data. +// https://github.com/spiermar/d3-flame-graph +// See D3_FLAME_GRAPH_LICENSE file for license details + +package d3flamegraph + +// JSSource returns the d3 and d3-flame-graph JavaScript bundle +const JSSource = \` + +$d3_js +\` + +// CSSSource returns the $D3FLAMEGRAPH_CSS file +const CSSSource = \` +$d3_css +\` + +EOF + gofmt -w d3_flame_graph.go +} + +get_licenses() { + cp node_modules/d3-selection/LICENSE D3_LICENSE + cp node_modules/d3-flame-graph/LICENSE D3_FLAME_GRAPH_LICENSE +} + +get_licenses +generate_d3_flame_graph_go diff --git a/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/webpack.config.js b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/webpack.config.js new file mode 100644 index 00000000000000..71239d9e9684a8 --- /dev/null +++ b/src/cmd/vendor/github.com/google/pprof/third_party/d3flamegraph/webpack.config.js @@ -0,0 +1,13 @@ +// Minimal webpack config to package a minified JS bundle (including +// dependencies) for execution in a ") + import "text/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") produces - Hello, ! + Hello, ! but the contextual autoescaping in html/template - import "html/template" - ... - t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) - err = t.ExecuteTemplate(out, "T", "") + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") produces safe, escaped HTML output - Hello, <script>alert('you have been pwned')</script>! + Hello, <script>alert('you have been pwned')</script>! - -Contexts +# Contexts This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing functions to each simple action pipeline, so given the excerpt - {{.}} + {{.}} At parse time each {{.}} is overwritten to add escaping functions as necessary. In this case it becomes - {{. | htmlescaper}} + {{. | htmlescaper}} where urlescaper, attrescaper, and htmlescaper are aliases for internal escaping functions. @@ -73,117 +72,113 @@ functions. For these internal escaping functions, if an action pipeline evaluates to a nil interface value, it is treated as though it were an empty string. -Namespaced and data- attributes +# Namespaced and data- attributes Attributes with a namespace are treated as if they had no namespace. Given the excerpt - + At parse time the attribute will be treated as if it were just "href". So at parse time the template becomes: - + Similarly to attributes with namespaces, attributes with a "data-" prefix are treated as if they had no "data-" prefix. So given - + At parse time this becomes - + If an attribute has both a namespace and a "data-" prefix, only the namespace will be removed when determining the context. For example - + This is handled as if "my:data-href" was just "data-href" and not "href" as it would be if the "data-" prefix were to be ignored too. Thus at parse time this becomes just - + As a special case, attributes with the namespace "xmlns" are always treated as containing URLs. Given the excerpts - - - + + + At parse time they become: - - - + + + -Errors +# Errors See the documentation of ErrorCode for details. - -A fuller picture +# A fuller picture The rest of this package comment may be skipped on first reading; it includes details necessary to understand escaping contexts and error messages. Most users will not need to understand these details. - -Contexts +# Contexts Assuming {{.}} is `O'Reilly: How are you?`, the table below shows how {{.}} appears when used in the context to the left. - Context {{.}} After - {{.}} O'Reilly: How are <i>you</i>? - O'Reilly: How are you? - O'Reilly: How are %3ci%3eyou%3c/i%3e? - O'Reilly%3a%20How%20are%3ci%3e...%3f - O\x27Reilly: How are \x3ci\x3eyou...? - "O\x27Reilly: How are \x3ci\x3eyou...?" - O\x27Reilly: How are \x3ci\x3eyou...\x3f + Context {{.}} After + {{.}} O'Reilly: How are <i>you</i>? + O'Reilly: How are you? + O'Reilly: How are %3ci%3eyou%3c/i%3e? + O'Reilly%3a%20How%20are%3ci%3e...%3f + O\x27Reilly: How are \x3ci\x3eyou...? + "O\x27Reilly: How are \x3ci\x3eyou...?" + O\x27Reilly: How are \x3ci\x3eyou...\x3f If used in an unsafe context, then the value might be filtered out: - Context {{.}} After - #ZgotmplZ + Context {{.}} After + #ZgotmplZ since "O'Reilly:" is not an allowed protocol like "http:". - If {{.}} is the innocuous word, `left`, then it can appear more widely, - Context {{.}} After - {{.}} left - left - left - left - left - left - left - left - left + Context {{.}} After + {{.}} left + left + left + left + left + left + left + left + left Non-string values can be used in JavaScript contexts. If {{.}} is - struct{A,B string}{ "foo", "bar" } + struct{A,B string}{ "foo", "bar" } in the escaped template - + then the template output is - + See package json to understand how non-string content is marshaled for embedding in JavaScript contexts. - -Typed Strings +# Typed Strings By default, this package assumes that all pipelines produce a plain text string. It adds escaping pipeline stages necessary to correctly and safely embed that @@ -197,24 +192,23 @@ exempted from escaping. The template - Hello, {{.}}! + Hello, {{.}}! can be invoked with - tmpl.Execute(out, template.HTML(`World`)) + tmpl.Execute(out, template.HTML(`World`)) to produce - Hello, World! + Hello, World! instead of the - Hello, <b>World<b>! + Hello, <b>World<b>! that would have been produced if {{.}} was a regular string. - -Security Model +# Security Model https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. diff --git a/src/html/template/error.go b/src/html/template/error.go index 0e527063ea62ae..5c51f772cbde36 100644 --- a/src/html/template/error.go +++ b/src/html/template/error.go @@ -32,14 +32,17 @@ type ErrorCode int // // Output: "ZgotmplZ" // Example: -// -// where {{.X}} evaluates to `javascript:...` +// +// +// where {{.X}} evaluates to `javascript:...` +// // Discussion: -// "ZgotmplZ" is a special value that indicates that unsafe content reached a -// CSS or URL context at runtime. The output of the example will be -// -// If the data comes from a trusted source, use content types to exempt it -// from filtering: URL(`javascript:...`). +// +// "ZgotmplZ" is a special value that indicates that unsafe content reached a +// CSS or URL context at runtime. The output of the example will be +// +// If the data comes from a trusted source, use content types to exempt it +// from filtering: URL(`javascript:...`). const ( // OK indicates the lack of an error. OK ErrorCode = iota @@ -228,6 +231,6 @@ func (e *Error) Error() string { // errorf creates an error given a format string f and args. // The template Name still needs to be supplied. -func errorf(k ErrorCode, node parse.Node, line int, f string, args ...interface{}) *Error { +func errorf(k ErrorCode, node parse.Node, line int, f string, args ...any) *Error { return &Error{k, node, "", line, fmt.Sprintf(f, args...)} } diff --git a/src/html/template/escape.go b/src/html/template/escape.go index 8739735cb7bc0c..54fbcdca333549 100644 --- a/src/html/template/escape.go +++ b/src/html/template/escape.go @@ -44,8 +44,8 @@ func escapeTemplate(tmpl *Template, node parse.Node, name string) error { } // evalArgs formats the list of arguments into a string. It is equivalent to -// fmt.Sprint(args...), except that it deferences all pointers. -func evalArgs(args ...interface{}) string { +// fmt.Sprint(args...), except that it dereferences all pointers. +func evalArgs(args ...any) string { // Optimization for simple common case of a single string argument. if len(args) == 1 { if s, ok := args[0].(string); ok { @@ -97,6 +97,15 @@ type escaper struct { actionNodeEdits map[*parse.ActionNode][]string templateNodeEdits map[*parse.TemplateNode]string textNodeEdits map[*parse.TextNode][]byte + // rangeContext holds context about the current range loop. + rangeContext *rangeContext +} + +// rangeContext holds information about the current range loop. +type rangeContext struct { + outer *rangeContext // outer loop + breaks []context // context at each break action + continues []context // context at each continue action } // makeEscaper creates a blank escaper for the given set. @@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper { map[*parse.ActionNode][]string{}, map[*parse.TemplateNode]string{}, map[*parse.TextNode][]byte{}, + nil, } } @@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context { switch n := n.(type) { case *parse.ActionNode: return e.escapeAction(c, n) + case *parse.BreakNode: + c.n = n + e.rangeContext.breaks = append(e.rangeContext.breaks, c) + return context{state: stateDead} case *parse.CommentNode: return c + case *parse.ContinueNode: + c.n = n + e.rangeContext.continues = append(e.rangeContext.breaks, c) + return context{state: stateDead} case *parse.IfNode: return e.escapeBranch(c, &n.BranchNode, "if") case *parse.ListNode: @@ -393,13 +411,19 @@ func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode { // nudge returns the context that would result from following empty string // transitions from the input context. // For example, parsing: -// `90% of the time. e.output[t.Name()] = c @@ -865,7 +940,7 @@ func HTMLEscapeString(s string) string { // HTMLEscaper returns the escaped HTML equivalent of the textual // representation of its arguments. -func HTMLEscaper(args ...interface{}) string { +func HTMLEscaper(args ...any) string { return template.HTMLEscaper(args...) } @@ -881,12 +956,12 @@ func JSEscapeString(s string) string { // JSEscaper returns the escaped JavaScript equivalent of the textual // representation of its arguments. -func JSEscaper(args ...interface{}) string { +func JSEscaper(args ...any) string { return template.JSEscaper(args...) } // URLQueryEscaper returns the escaped value of the textual representation of // its arguments in a form suitable for embedding in a URL query. -func URLQueryEscaper(args ...interface{}) string { +func URLQueryEscaper(args ...any) string { return template.URLQueryEscaper(args...) } diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index fbc84a75928b23..12add077c3c38d 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -35,8 +35,8 @@ func TestEscape(t *testing.T) { A, E []string B, M json.Marshaler N int - U interface{} // untyped nil - Z *int // typed nil + U any // untyped nil + Z *int // typed nil W HTML }{ F: false, @@ -688,7 +688,7 @@ func TestEscape(t *testing.T) { t.Errorf("%s: tree not set properly", test.name) continue } - b := new(bytes.Buffer) + b := new(strings.Builder) if err := tmpl.Execute(b, data); err != nil { t.Errorf("%s: template execution failed: %s", test.name, err) continue @@ -735,7 +735,7 @@ func TestEscapeMap(t *testing.T) { }, } { tmpl := Must(New("").Parse(test.input)) - b := new(bytes.Buffer) + b := new(strings.Builder) if err := tmpl.Execute(b, data); err != nil { t.Errorf("%s: template execution failed: %s", test.desc, err) continue @@ -858,7 +858,7 @@ func TestEscapeSet(t *testing.T) { // pred is a template function that returns the predecessor of a // natural number for testing recursive templates. - fns := FuncMap{"pred": func(a ...interface{}) (interface{}, error) { + fns := FuncMap{"pred": func(a ...any) (any, error) { if len(a) == 1 { if i, _ := a[0].(int); i > 0 { return i - 1, nil @@ -877,7 +877,7 @@ func TestEscapeSet(t *testing.T) { t.Errorf("error parsing %q: %v", source, err) continue } - var b bytes.Buffer + var b strings.Builder if err := tmpl.ExecuteTemplate(&b, "main", data); err != nil { t.Errorf("%q executing %v", err.Error(), tmpl.Lookup("main")) @@ -920,6 +920,22 @@ func TestErrors(t *testing.T) { "", "", }, + { + "{{range .Items}}{{end}}", + "", + }, + { + "{{range .Items}}{{continue}}{{end}}", + "", + }, + { + "{{range .Items}}{{break}}{{end}}", + "", + }, + { + "{{range .Items}}{{if .X}}{{break}}{{end}}{{end}}", + "", + }, // Error cases. { "{{if .Cond}}{{template "hello"}}{{end}}`)) Must(tmpl. Parse(`{{define "hello"}}Hello, {{"Ladies & Gentlemen!"}}{{end}}`)) - got := new(bytes.Buffer) + got := new(strings.Builder) var err error // Ensure that "hello" produces the same output when executed twice. want := "Hello, Ladies & Gentlemen!" @@ -1923,7 +1947,7 @@ func TestOrphanedTemplate(t *testing.T) { t1 := Must(New("foo").Parse(`link1`)) t2 := Must(t1.New("foo").Parse(`bar`)) - var b bytes.Buffer + var b strings.Builder const wantError = `template: "foo" is an incomplete or empty template` if err := t1.Execute(&b, "javascript:alert(1)"); err == nil { t.Fatal("expected error executing t1") @@ -1952,7 +1976,7 @@ func TestAliasedParseTreeDoesNotOverescape(t *testing.T) { if _, err := tpl.AddParseTree("bar", tpl.Tree); err != nil { t.Fatalf("AddParseTree error: %v", err) } - var b1, b2 bytes.Buffer + var b1, b2 strings.Builder if err := tpl.ExecuteTemplate(&b1, "foo", data); err != nil { t.Fatalf(`ExecuteTemplate failed for "foo": %v`, err) } diff --git a/src/html/template/example_test.go b/src/html/template/example_test.go index 6cf936f2709f33..605b25f41dbec6 100644 --- a/src/html/template/example_test.go +++ b/src/html/template/example_test.go @@ -98,7 +98,7 @@ func Example_autoescaping() { func Example_escape() { const s = `"Fran & Freddie's Diner" ` - v := []interface{}{`"Fran & Freddie's Diner"`, ' ', ``} + v := []any{`"Fran & Freddie's Diner"`, ' ', ``} fmt.Println(template.HTMLEscapeString(s)) template.HTMLEscape(os.Stdout, []byte(s)) diff --git a/src/html/template/exec_test.go b/src/html/template/exec_test.go index 888587335de112..1ec346fe814863 100644 --- a/src/html/template/exec_test.go +++ b/src/html/template/exec_test.go @@ -49,7 +49,7 @@ type T struct { MSI map[string]int MSIone map[string]int // one element, for deterministic output MSIEmpty map[string]int - MXI map[interface{}]int + MXI map[any]int MII map[int]int MI32S map[int32]string MI64S map[int64]string @@ -59,11 +59,11 @@ type T struct { MUI8S map[uint8]string SMSI []map[string]int // Empty interfaces; used to see if we can dig inside one. - Empty0 interface{} // nil - Empty1 interface{} - Empty2 interface{} - Empty3 interface{} - Empty4 interface{} + Empty0 any // nil + Empty1 any + Empty2 any + Empty3 any + Empty4 any // Non-empty interfaces. NonEmptyInterface I NonEmptyInterfacePtS *I @@ -141,7 +141,7 @@ var tVal = &T{ SB: []bool{true, false}, MSI: map[string]int{"one": 1, "two": 2, "three": 3}, MSIone: map[string]int{"one": 1}, - MXI: map[interface{}]int{"one": 1}, + MXI: map[any]int{"one": 1}, MII: map[int]int{1: 1}, MI32S: map[int32]string{1: "one", 2: "two"}, MI64S: map[int64]string{2: "i642", 3: "i643"}, @@ -212,7 +212,7 @@ func (t *T) Method2(a uint16, b string) string { return fmt.Sprintf("Method2: %d %s", a, b) } -func (t *T) Method3(v interface{}) string { +func (t *T) Method3(v any) string { return fmt.Sprintf("Method3: %v", v) } @@ -252,7 +252,7 @@ func (u *U) TrueFalse(b bool) string { return "" } -func typeOf(arg interface{}) string { +func typeOf(arg any) string { return fmt.Sprintf("%T", arg) } @@ -260,7 +260,7 @@ type execTest struct { name string input string output string - data interface{} + data any ok bool } @@ -393,7 +393,7 @@ var execTests = []execTest{ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true}, {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, - {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, + {"Interface Call", `{{stringer .S}}`, "foozle", map[string]any{"S": bytes.NewBufferString("foozle")}, true}, {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, {"call nil", "{{call nil}}", "", tVal, false}, @@ -567,6 +567,8 @@ var execTests = []execTest{ {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true}, + {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, @@ -738,7 +740,7 @@ func add(args ...int) int { return sum } -func echo(arg interface{}) interface{} { +func echo(arg any) any { return arg } @@ -757,12 +759,12 @@ func stringer(s fmt.Stringer) string { return s.String() } -func mapOfThree() interface{} { +func mapOfThree() any { return map[string]int{"three": 3} } func testExecute(execTests []execTest, template *Template, t *testing.T) { - b := new(bytes.Buffer) + b := new(strings.Builder) funcs := FuncMap{ "add": add, "count": count, @@ -854,7 +856,7 @@ func TestDelims(t *testing.T) { if err != nil { t.Fatalf("delim %q text %q parse err %s", left, text, err) } - var b = new(bytes.Buffer) + var b = new(strings.Builder) err = tmpl.Execute(b, value) if err != nil { t.Fatalf("delim %q exec err %s", left, err) @@ -995,7 +997,7 @@ func TestTree(t *testing.T) { if err != nil { t.Fatal("parse error:", err) } - var b bytes.Buffer + var b strings.Builder const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" // First by looking up the template. err = tmpl.Lookup("tree").Execute(&b, tree) @@ -1189,33 +1191,39 @@ var cmpTests = []cmpTest{ {"eq .Iface1 .Iface1", "true", true}, {"eq .Iface1 .Iface2", "false", true}, {"eq .Iface2 .Iface2", "true", true}, + {"eq .Map .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map nil", "true", true}, // Uncomparable types but nil is OK. + {"eq nil .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map .NonNilMap", "false", true}, // Uncomparable types but nil is OK. // Errors - {"eq `xy` 1", "", false}, // Different types. - {"eq 2 2.0", "", false}, // Different types. - {"lt true true", "", false}, // Unordered types. - {"lt 1+0i 1+0i", "", false}, // Unordered types. - {"eq .Ptr 1", "", false}, // Incompatible types. - {"eq .Ptr .NegOne", "", false}, // Incompatible types. - {"eq .Map .Map", "", false}, // Uncomparable types. - {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. + {"eq .Ptr 1", "", false}, // Incompatible types. + {"eq .Ptr .NegOne", "", false}, // Incompatible types. + {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq .NonNilMap .NonNilMap", "", false}, // Uncomparable types. } func TestComparison(t *testing.T) { - b := new(bytes.Buffer) + b := new(strings.Builder) var cmpStruct = struct { Uthree, Ufour uint NegOne, Three int Ptr, NilPtr *int + NonNilMap map[int]int Map map[int]int V1, V2 V Iface1, Iface2 fmt.Stringer }{ - Uthree: 3, - Ufour: 4, - NegOne: -1, - Three: 3, - Ptr: new(int), - Iface1: b, + Uthree: 3, + Ufour: 4, + NegOne: -1, + Three: 3, + Ptr: new(int), + NonNilMap: make(map[int]int), + Iface1: b, } for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) @@ -1247,7 +1255,7 @@ func TestMissingMapKey(t *testing.T) { if err != nil { t.Fatal(err) } - var b bytes.Buffer + var b strings.Builder // By default, just get "" // NOTE: not in html/template, get empty string err = tmpl.Execute(&b, data) if err != nil { @@ -1416,7 +1424,7 @@ func TestBlock(t *testing.T) { t.Fatal(err) } - var buf bytes.Buffer + var buf strings.Builder if err := tmpl.Execute(&buf, "hello"); err != nil { t.Fatal(err) } @@ -1436,7 +1444,7 @@ func TestBlock(t *testing.T) { func TestEvalFieldErrors(t *testing.T) { tests := []struct { name, src string - value interface{} + value any want string }{ { @@ -1522,7 +1530,7 @@ func TestAddrOfIndex(t *testing.T) { } for _, text := range texts { tmpl := Must(New("tmpl").Parse(text)) - var buf bytes.Buffer + var buf strings.Builder err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}})) if err != nil { t.Fatalf("%s: Execute: %v", text, err) @@ -1578,8 +1586,8 @@ func TestInterfaceValues(t *testing.T) { for _, tt := range tests { tmpl := Must(New("tmpl").Parse(tt.text)) - var buf bytes.Buffer - err := tmpl.Execute(&buf, map[string]interface{}{ + var buf strings.Builder + err := tmpl.Execute(&buf, map[string]any{ "PlusOne": func(n int) int { return n + 1 }, @@ -1608,7 +1616,7 @@ func TestInterfaceValues(t *testing.T) { // Check that panics during calls are recovered and returned as errors. func TestExecutePanicDuringCall(t *testing.T) { - funcs := map[string]interface{}{ + funcs := map[string]any{ "doPanic": func() string { panic("custom panic string") }, @@ -1616,7 +1624,7 @@ func TestExecutePanicDuringCall(t *testing.T) { tests := []struct { name string input string - data interface{} + data any wantErr string }{ { @@ -1673,7 +1681,7 @@ func TestIssue31810(t *testing.T) { t.Skip("broken in html/template") // A simple value with no arguments is fine. - var b bytes.Buffer + var b strings.Builder const text = "{{ (.) }}" tmpl, err := New("").Parse(text) if err != nil { @@ -1814,7 +1822,7 @@ func TestRecursiveExecuteViaMethod(t *testing.T) { func TestTemplateFuncsAfterClone(t *testing.T) { s := `{{ f . }}` want := "test" - orig := New("orig").Funcs(map[string]interface{}{ + orig := New("orig").Funcs(map[string]any{ "f": func(in string) string { return in }, diff --git a/src/html/template/html.go b/src/html/template/html.go index 356b8298ae36df..bcca0b51a0ef90 100644 --- a/src/html/template/html.go +++ b/src/html/template/html.go @@ -12,7 +12,7 @@ import ( ) // htmlNospaceEscaper escapes for inclusion in unquoted attribute values. -func htmlNospaceEscaper(args ...interface{}) string { +func htmlNospaceEscaper(args ...any) string { s, t := stringify(args...) if t == contentTypeHTML { return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false) @@ -21,7 +21,7 @@ func htmlNospaceEscaper(args ...interface{}) string { } // attrEscaper escapes for inclusion in quoted attribute values. -func attrEscaper(args ...interface{}) string { +func attrEscaper(args ...any) string { s, t := stringify(args...) if t == contentTypeHTML { return htmlReplacer(stripTags(s), htmlNormReplacementTable, true) @@ -30,7 +30,7 @@ func attrEscaper(args ...interface{}) string { } // rcdataEscaper escapes for inclusion in an RCDATA element body. -func rcdataEscaper(args ...interface{}) string { +func rcdataEscaper(args ...any) string { s, t := stringify(args...) if t == contentTypeHTML { return htmlReplacer(s, htmlNormReplacementTable, true) @@ -39,7 +39,7 @@ func rcdataEscaper(args ...interface{}) string { } // htmlEscaper escapes for inclusion in HTML text. -func htmlEscaper(args ...interface{}) string { +func htmlEscaper(args ...any) string { s, t := stringify(args...) if t == contentTypeHTML { return s @@ -84,10 +84,12 @@ var htmlNormReplacementTable = []string{ // @@ -174,7 +176,7 @@ func htmlReplacer(s string, replacementTable []string, badRunes bool) string { // stripTags takes a snippet of HTML and returns only the text content. // For example, `¡Hi! ` -> `¡Hi! `. func stripTags(html string) string { - var b bytes.Buffer + var b strings.Builder s, c, i, allText := []byte(html), context{}, 0, true // Using the transition funcs helps us avoid mangling // `
        ` or `I <3 Ponies!`. @@ -225,7 +227,7 @@ func stripTags(html string) string { // htmlNameFilter accepts valid parts of an HTML attribute or tag name or // a known-safe HTML attribute. -func htmlNameFilter(args ...interface{}) string { +func htmlNameFilter(args ...any) string { s, t := stringify(args...) if t == contentTypeHTMLAttr { return s @@ -260,6 +262,6 @@ func htmlNameFilter(args ...interface{}) string { // content interpolated into comments. // This approach is equally valid whether or not static comment content is // removed from the template. -func commentEscaper(args ...interface{}) string { +func commentEscaper(args ...any) string { return "" } diff --git a/src/html/template/js.go b/src/html/template/js.go index ea9c18346ba263..50523d00f167fd 100644 --- a/src/html/template/js.go +++ b/src/html/template/js.go @@ -122,7 +122,7 @@ var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem() // indirectToJSONMarshaler returns the value, after dereferencing as many times // as necessary to reach the base type (or nil) or an implementation of json.Marshal. -func indirectToJSONMarshaler(a interface{}) interface{} { +func indirectToJSONMarshaler(a any) any { // text/template now supports passing untyped nil as a func call // argument, so we must support it. Otherwise we'd panic below, as one // cannot call the Type or Interface methods on an invalid @@ -132,7 +132,7 @@ func indirectToJSONMarshaler(a interface{}) interface{} { } v := reflect.ValueOf(a) - for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() { + for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Pointer && !v.IsNil() { v = v.Elem() } return v.Interface() @@ -140,8 +140,8 @@ func indirectToJSONMarshaler(a interface{}) interface{} { // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has // neither side-effects nor free variables outside (NaN, Infinity). -func jsValEscaper(args ...interface{}) string { - var a interface{} +func jsValEscaper(args ...any) string { + var a any if len(args) == 1 { a = indirectToJSONMarshaler(args[0]) switch t := a.(type) { @@ -224,7 +224,7 @@ func jsValEscaper(args ...interface{}) string { // jsStrEscaper produces a string that can be included between quotes in // JavaScript source, in JavaScript embedded in an HTML5 ", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`}, {"", `"--\u003e"`}, @@ -158,7 +157,7 @@ func TestJSValEscaper(t *testing.T) { } // Make sure that escaping corner cases are not broken // by nesting. - a := []interface{}{test.x} + a := []any{test.x} want := "[" + strings.TrimSpace(test.js) + "]" if js := jsValEscaper(a); js != want { t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js) @@ -168,7 +167,7 @@ func TestJSValEscaper(t *testing.T) { func TestJSStrEscaper(t *testing.T) { tests := []struct { - x interface{} + x any esc string }{ {"", ``}, @@ -223,7 +222,7 @@ func TestJSStrEscaper(t *testing.T) { func TestJSRegexpEscaper(t *testing.T) { tests := []struct { - x interface{} + x any esc string }{ {"", `(?:)`}, @@ -278,7 +277,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) { tests := []struct { name string - escaper func(...interface{}) string + escaper func(...any) string escaped string }{ { @@ -321,7 +320,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) { // Escape it rune by rune to make sure that any // fast-path checking does not break escaping. - var buf bytes.Buffer + var buf strings.Builder for _, c := range input { buf.WriteString(test.escaper(string(c))) } diff --git a/src/html/template/multi_test.go b/src/html/template/multi_test.go index 6535ab6c0439cf..21050865cf7041 100644 --- a/src/html/template/multi_test.go +++ b/src/html/template/multi_test.go @@ -8,8 +8,8 @@ package template import ( "archive/zip" - "bytes" "os" + "strings" "testing" "text/template/parse" ) @@ -245,7 +245,7 @@ func TestEmptyTemplate(t *testing.T) { t.Fatal(err) } } - buf := &bytes.Buffer{} + buf := &strings.Builder{} if err := m.Execute(buf, c.in); err != nil { t.Error(i, err) continue @@ -280,7 +280,7 @@ func TestIssue19294(t *testing.T) { t.Fatal(err) } } - var buf bytes.Buffer + var buf strings.Builder res.Execute(&buf, 0) if buf.String() != "stylesheet" { t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") diff --git a/src/html/template/template.go b/src/html/template/template.go index 69312d36fdb361..30b64dff040872 100644 --- a/src/html/template/template.go +++ b/src/html/template/template.go @@ -64,6 +64,7 @@ func (t *Template) Templates() []*Template { // // missingkey: Control the behavior during execution if a map is // indexed with a key that is not present in the map. +// // "missingkey=default" or "missingkey=invalid" // The default behavior: Do nothing and continue execution. // If printed, the result of the index operation is the string @@ -72,7 +73,6 @@ func (t *Template) Templates() []*Template { // The operation returns the zero value for the map type's element. // "missingkey=error" // Execution stops immediately with an error. -// func (t *Template) Option(opt ...string) *Template { t.text.Option(opt...) return t @@ -117,7 +117,7 @@ func (t *Template) escape() error { // the output writer. // A template may be executed safely in parallel, although if parallel // executions share a Writer the output may be interleaved. -func (t *Template) Execute(wr io.Writer, data interface{}) error { +func (t *Template) Execute(wr io.Writer, data any) error { if err := t.escape(); err != nil { return err } @@ -131,7 +131,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) error { // the output writer. // A template may be executed safely in parallel, although if parallel // executions share a Writer the output may be interleaved. -func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error { tmpl, err := t.lookupAndEscapeTemplate(name) if err != nil { return err @@ -328,14 +328,7 @@ func (t *Template) Name() string { return t.text.Name() } -// FuncMap is the type of the map defining the mapping from names to -// functions. Each function must have either a single return value, or two -// return values of which the second has type error. In that case, if the -// second (error) argument evaluates to non-nil during execution, execution -// terminates and Execute returns that error. FuncMap has the same base type -// as FuncMap in "text/template", copied here so clients need not import -// "text/template". -type FuncMap map[string]interface{} +type FuncMap = template.FuncMap // Funcs adds the elements of the argument map to the template's function map. // It must be called before the template is parsed. @@ -368,6 +361,7 @@ func (t *Template) Lookup(name string) *Template { // Must is a helper that wraps a call to a function returning (*Template, error) // and panics if the error is non-nil. It is intended for use in variable initializations // such as +// // var t = template.Must(template.New("name").Parse("html")) func Must(t *Template, err error) *Template { if err != nil { @@ -486,7 +480,7 @@ func parseGlob(t *Template, pattern string) (*Template, error) { // IsTrue reports whether the value is 'true', in the sense of not the zero of its type, // and whether the value has a meaningful truth value. This is the definition of // truth used by if and other such actions. -func IsTrue(val interface{}) (truth, ok bool) { +func IsTrue(val any) (truth, ok bool) { return template.IsTrue(val) } diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go index 1f2c888bbea9d2..96d801348421b9 100644 --- a/src/html/template/template_test.go +++ b/src/html/template/template_test.go @@ -26,7 +26,7 @@ func TestTemplateClone(t *testing.T) { const want = "stuff" parsed := Must(clone.Parse(want)) - var buf bytes.Buffer + var buf strings.Builder err = parsed.Execute(&buf, nil) if err != nil { t.Fatal(err) @@ -206,8 +206,8 @@ func (c *testCase) mustNotParse(t *Template, text string) { } } -func (c *testCase) mustExecute(t *Template, val interface{}, want string) { - var buf bytes.Buffer +func (c *testCase) mustExecute(t *Template, val any, want string) { + var buf strings.Builder err := t.Execute(&buf, val) if err != nil { c.t.Fatalf("execute: %v", err) diff --git a/src/html/template/url.go b/src/html/template/url.go index 6f8185a4e90e69..7820561dc022da 100644 --- a/src/html/template/url.go +++ b/src/html/template/url.go @@ -5,7 +5,6 @@ package template import ( - "bytes" "fmt" "strings" ) @@ -19,20 +18,20 @@ import ( // // This filter conservatively assumes that all schemes other than the following // are unsafe: -// * http: Navigates to a new website, and may open a new window or tab. -// These side effects can be reversed by navigating back to the -// previous website, or closing the window or tab. No irreversible -// changes will take place without further user interaction with -// the new website. -// * https: Same as http. -// * mailto: Opens an email program and starts a new draft. This side effect -// is not irreversible until the user explicitly clicks send; it -// can be undone by closing the email program. +// - http: Navigates to a new website, and may open a new window or tab. +// These side effects can be reversed by navigating back to the +// previous website, or closing the window or tab. No irreversible +// changes will take place without further user interaction with +// the new website. +// - https: Same as http. +// - mailto: Opens an email program and starts a new draft. This side effect +// is not irreversible until the user explicitly clicks send; it +// can be undone by closing the email program. // // To allow URLs containing other schemes to bypass this filter, developers must // explicitly indicate that such a URL is expected and safe by encapsulating it // in a template.URL value. -func urlFilter(args ...interface{}) string { +func urlFilter(args ...any) string { s, t := stringify(args...) if t == contentTypeURL { return s @@ -46,9 +45,7 @@ func urlFilter(args ...interface{}) string { // isSafeURL is true if s is a relative URL or if URL has a protocol in // (http, https, mailto). func isSafeURL(s string) bool { - if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') { - - protocol := s[:i] + if protocol, _, ok := strings.Cut(s, ":"); ok && !strings.Contains(protocol, "/") { if !strings.EqualFold(protocol, "http") && !strings.EqualFold(protocol, "https") && !strings.EqualFold(protocol, "mailto") { return false } @@ -58,7 +55,7 @@ func isSafeURL(s string) bool { // urlEscaper produces an output that can be embedded in a URL query. // The output can be embedded in an HTML attribute without further escaping. -func urlEscaper(args ...interface{}) string { +func urlEscaper(args ...any) string { return urlProcessor(false, args...) } @@ -67,18 +64,18 @@ func urlEscaper(args ...interface{}) string { // The normalizer does not encode all HTML specials. Specifically, it does not // encode '&' so correct embedding in an HTML attribute requires escaping of // '&' to '&'. -func urlNormalizer(args ...interface{}) string { +func urlNormalizer(args ...any) string { return urlProcessor(true, args...) } // urlProcessor normalizes (when norm is true) or escapes its input to produce // a valid hierarchical or opaque URL part. -func urlProcessor(norm bool, args ...interface{}) string { +func urlProcessor(norm bool, args ...any) string { s, t := stringify(args...) if t == contentTypeURL { norm = true } - var b bytes.Buffer + var b strings.Builder if processURLOnto(s, norm, &b) { return b.String() } @@ -87,7 +84,7 @@ func urlProcessor(norm bool, args ...interface{}) string { // processURLOnto appends a normalized URL corresponding to its input to b // and reports whether the appended content differs from s. -func processURLOnto(s string, norm bool, b *bytes.Buffer) bool { +func processURLOnto(s string, norm bool, b *strings.Builder) bool { b.Grow(len(s) + 16) written := 0 // The byte loop below assumes that all URLs use UTF-8 as the @@ -143,7 +140,7 @@ func processURLOnto(s string, norm bool, b *bytes.Buffer) bool { // Filters and normalizes srcset values which are comma separated // URLs followed by metadata. -func srcsetFilterAndEscaper(args ...interface{}) string { +func srcsetFilterAndEscaper(args ...any) string { s, t := stringify(args...) switch t { case contentTypeSrcset: @@ -151,7 +148,7 @@ func srcsetFilterAndEscaper(args ...interface{}) string { case contentTypeURL: // Normalizing gets rid of all HTML whitespace // which separate the image URL from its metadata. - var b bytes.Buffer + var b strings.Builder if processURLOnto(s, true, &b) { s = b.String() } @@ -159,7 +156,7 @@ func srcsetFilterAndEscaper(args ...interface{}) string { return strings.ReplaceAll(s, ",", "%2c") } - var b bytes.Buffer + var b strings.Builder written := 0 for i := 0; i < len(s); i++ { if s[i] == ',' { @@ -185,7 +182,7 @@ func isHTMLSpaceOrASCIIAlnum(c byte) bool { return (c < 0x80) && 0 != (htmlSpaceAndASCIIAlnumBytes[c>>3]&(1< sp.Y { // If the source start point is higher than the destination start // point, then we compose the rows in bottom-up order instead of // top-down. Unlike the drawCopyOver function, we don't have to check // the x coordinates because the built-in copy function can handle // overlapping slices. - d0 += (dy - 1) * dst.Stride - s0 += (dy - 1) * src.Stride - ddelta = -dst.Stride - sdelta = -src.Stride + d0 = (dy - 1) * dstStride + s0 = (dy - 1) * srcStride + ddelta = -dstStride + sdelta = -srcStride } for ; dy > 0; dy-- { - copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n]) + copy(dstPix[d0:d0+bytesPerRow], srcPix[s0:s0+bytesPerRow]) d0 += ddelta s0 += sdelta } @@ -519,6 +642,156 @@ func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask } } +func drawGrayMaskOver(dst *image.RGBA, r image.Rectangle, src *image.Gray, sp image.Point, mask *image.Alpha, mp image.Point) { + x0, x1, dx := r.Min.X, r.Max.X, 1 + y0, y1, dy := r.Min.Y, r.Max.Y, 1 + if r.Overlaps(r.Add(sp.Sub(r.Min))) { + if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { + x0, x1, dx = x1-1, x0-1, -1 + y0, y1, dy = y1-1, y0-1, -1 + } + } + + sy := sp.Y + y0 - r.Min.Y + my := mp.Y + y0 - r.Min.Y + sx0 := sp.X + x0 - r.Min.X + mx0 := mp.X + x0 - r.Min.X + sx1 := sx0 + (x1 - x0) + i0 := dst.PixOffset(x0, y0) + di := dx * 4 + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + mi := mask.PixOffset(mx, my) + ma := uint32(mask.Pix[mi]) + ma |= ma << 8 + si := src.PixOffset(sx, sy) + sy := uint32(src.Pix[si]) + sy |= sy << 8 + sa := uint32(0xffff) + + d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + dr := uint32(d[0]) + dg := uint32(d[1]) + db := uint32(d[2]) + da := uint32(d[3]) + + // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. + // We work in 16-bit color, and so would normally do: + // dr |= dr << 8 + // and similarly for dg, db and da, but instead we multiply a + // (which is a 16-bit color, ranging in [0,65535]) by 0x101. + // This yields the same result, but is fewer arithmetic operations. + a := (m - (sa * ma / m)) * 0x101 + + d[0] = uint8((dr*a + sy*ma) / m >> 8) + d[1] = uint8((dg*a + sy*ma) / m >> 8) + d[2] = uint8((db*a + sy*ma) / m >> 8) + d[3] = uint8((da*a + sa*ma) / m >> 8) + } + i0 += dy * dst.Stride + } +} + +func drawRGBAMaskOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point, mask *image.Alpha, mp image.Point) { + x0, x1, dx := r.Min.X, r.Max.X, 1 + y0, y1, dy := r.Min.Y, r.Max.Y, 1 + if dst == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { + if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { + x0, x1, dx = x1-1, x0-1, -1 + y0, y1, dy = y1-1, y0-1, -1 + } + } + + sy := sp.Y + y0 - r.Min.Y + my := mp.Y + y0 - r.Min.Y + sx0 := sp.X + x0 - r.Min.X + mx0 := mp.X + x0 - r.Min.X + sx1 := sx0 + (x1 - x0) + i0 := dst.PixOffset(x0, y0) + di := dx * 4 + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + mi := mask.PixOffset(mx, my) + ma := uint32(mask.Pix[mi]) + ma |= ma << 8 + si := src.PixOffset(sx, sy) + sr := uint32(src.Pix[si+0]) + sg := uint32(src.Pix[si+1]) + sb := uint32(src.Pix[si+2]) + sa := uint32(src.Pix[si+3]) + sr |= sr << 8 + sg |= sg << 8 + sb |= sb << 8 + sa |= sa << 8 + d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + dr := uint32(d[0]) + dg := uint32(d[1]) + db := uint32(d[2]) + da := uint32(d[3]) + + // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. + // We work in 16-bit color, and so would normally do: + // dr |= dr << 8 + // and similarly for dg, db and da, but instead we multiply a + // (which is a 16-bit color, ranging in [0,65535]) by 0x101. + // This yields the same result, but is fewer arithmetic operations. + a := (m - (sa * ma / m)) * 0x101 + + d[0] = uint8((dr*a + sr*ma) / m >> 8) + d[1] = uint8((dg*a + sg*ma) / m >> 8) + d[2] = uint8((db*a + sb*ma) / m >> 8) + d[3] = uint8((da*a + sa*ma) / m >> 8) + } + i0 += dy * dst.Stride + } +} + +func drawRGBA64ImageMaskOver(dst *image.RGBA, r image.Rectangle, src image.RGBA64Image, sp image.Point, mask *image.Alpha, mp image.Point) { + x0, x1, dx := r.Min.X, r.Max.X, 1 + y0, y1, dy := r.Min.Y, r.Max.Y, 1 + if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { + if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { + x0, x1, dx = x1-1, x0-1, -1 + y0, y1, dy = y1-1, y0-1, -1 + } + } + + sy := sp.Y + y0 - r.Min.Y + my := mp.Y + y0 - r.Min.Y + sx0 := sp.X + x0 - r.Min.X + mx0 := mp.X + x0 - r.Min.X + sx1 := sx0 + (x1 - x0) + i0 := dst.PixOffset(x0, y0) + di := dx * 4 + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + mi := mask.PixOffset(mx, my) + ma := uint32(mask.Pix[mi]) + ma |= ma << 8 + srgba := src.RGBA64At(sx, sy) + d := dst.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + dr := uint32(d[0]) + dg := uint32(d[1]) + db := uint32(d[2]) + da := uint32(d[3]) + + // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. + // We work in 16-bit color, and so would normally do: + // dr |= dr << 8 + // and similarly for dg, db and da, but instead we multiply a + // (which is a 16-bit color, ranging in [0,65535]) by 0x101. + // This yields the same result, but is fewer arithmetic operations. + a := (m - (uint32(srgba.A) * ma / m)) * 0x101 + + d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8) + d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8) + d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8) + d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8) + } + i0 += dy * dst.Stride + } +} + func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 @@ -536,6 +809,89 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin sx1 := sx0 + (x1 - x0) i0 := dst.PixOffset(x0, y0) di := dx * 4 + + // Try the image.RGBA64Image interface, part of the standard library since + // Go 1.17. + // + // This optimization is similar to how FALLBACK1.17 optimizes FALLBACK1.0 + // in DrawMask, except here the concrete type of dst is known to be + // *image.RGBA. + if src0, _ := src.(image.RGBA64Image); src0 != nil { + if mask == nil { + if op == Over { + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + srgba := src0.RGBA64At(sx, sy) + d := dst.Pix[i : i+4 : i+4] + dr := uint32(d[0]) + dg := uint32(d[1]) + db := uint32(d[2]) + da := uint32(d[3]) + a := (m - uint32(srgba.A)) * 0x101 + d[0] = uint8((dr*a/m + uint32(srgba.R)) >> 8) + d[1] = uint8((dg*a/m + uint32(srgba.G)) >> 8) + d[2] = uint8((db*a/m + uint32(srgba.B)) >> 8) + d[3] = uint8((da*a/m + uint32(srgba.A)) >> 8) + } + i0 += dy * dst.Stride + } + } else { + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + srgba := src0.RGBA64At(sx, sy) + d := dst.Pix[i : i+4 : i+4] + d[0] = uint8(srgba.R >> 8) + d[1] = uint8(srgba.G >> 8) + d[2] = uint8(srgba.B >> 8) + d[3] = uint8(srgba.A >> 8) + } + i0 += dy * dst.Stride + } + } + return + + } else if mask0, _ := mask.(image.RGBA64Image); mask0 != nil { + if op == Over { + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + ma := uint32(mask0.RGBA64At(mx, my).A) + srgba := src0.RGBA64At(sx, sy) + d := dst.Pix[i : i+4 : i+4] + dr := uint32(d[0]) + dg := uint32(d[1]) + db := uint32(d[2]) + da := uint32(d[3]) + a := (m - (uint32(srgba.A) * ma / m)) * 0x101 + d[0] = uint8((dr*a + uint32(srgba.R)*ma) / m >> 8) + d[1] = uint8((dg*a + uint32(srgba.G)*ma) / m >> 8) + d[2] = uint8((db*a + uint32(srgba.B)*ma) / m >> 8) + d[3] = uint8((da*a + uint32(srgba.A)*ma) / m >> 8) + } + i0 += dy * dst.Stride + } + } else { + for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { + for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { + ma := uint32(mask0.RGBA64At(mx, my).A) + srgba := src0.RGBA64At(sx, sy) + d := dst.Pix[i : i+4 : i+4] + d[0] = uint8(uint32(srgba.R) * ma / m >> 8) + d[1] = uint8(uint32(srgba.G) * ma / m >> 8) + d[2] = uint8(uint32(srgba.B) * ma / m >> 8) + d[3] = uint8(uint32(srgba.A) * ma / m >> 8) + } + i0 += dy * dst.Stride + } + } + return + } + } + + // Use the image.Image interface, part of the standard library since Go + // 1.0. + // + // This is similar to FALLBACK1.0 in DrawMask, except here the concrete + // type of dst is known to be *image.RGBA. for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { ma := uint32(m) diff --git a/src/image/draw/draw_test.go b/src/image/draw/draw_test.go index 9c5a118400e095..a34d1c3e6e8ab9 100644 --- a/src/image/draw/draw_test.go +++ b/src/image/draw/draw_test.go @@ -13,6 +13,172 @@ import ( "testing/quick" ) +// slowestRGBA is a draw.Image like image.RGBA but it is a different type and +// therefore does not trigger the draw.go fastest code paths. +// +// Unlike slowerRGBA, it does not implement the draw.RGBA64Image interface. +type slowestRGBA struct { + Pix []uint8 + Stride int + Rect image.Rectangle +} + +func (p *slowestRGBA) ColorModel() color.Model { return color.RGBAModel } + +func (p *slowestRGBA) Bounds() image.Rectangle { return p.Rect } + +func (p *slowestRGBA) At(x, y int) color.Color { + return p.RGBA64At(x, y) +} + +func (p *slowestRGBA) RGBA64At(x, y int) color.RGBA64 { + if !(image.Point{x, y}.In(p.Rect)) { + return color.RGBA64{} + } + i := p.PixOffset(x, y) + s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + r := uint16(s[0]) + g := uint16(s[1]) + b := uint16(s[2]) + a := uint16(s[3]) + return color.RGBA64{ + (r << 8) | r, + (g << 8) | g, + (b << 8) | b, + (a << 8) | a, + } +} + +func (p *slowestRGBA) Set(x, y int, c color.Color) { + if !(image.Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + c1 := color.RGBAModel.Convert(c).(color.RGBA) + s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + s[0] = c1.R + s[1] = c1.G + s[2] = c1.B + s[3] = c1.A +} + +func (p *slowestRGBA) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 +} + +func convertToSlowestRGBA(m image.Image) *slowestRGBA { + if rgba, ok := m.(*image.RGBA); ok { + return &slowestRGBA{ + Pix: append([]byte(nil), rgba.Pix...), + Stride: rgba.Stride, + Rect: rgba.Rect, + } + } + rgba := image.NewRGBA(m.Bounds()) + Draw(rgba, rgba.Bounds(), m, m.Bounds().Min, Src) + return &slowestRGBA{ + Pix: rgba.Pix, + Stride: rgba.Stride, + Rect: rgba.Rect, + } +} + +func init() { + var p any = (*slowestRGBA)(nil) + if _, ok := p.(RGBA64Image); ok { + panic("slowestRGBA should not be an RGBA64Image") + } +} + +// slowerRGBA is a draw.Image like image.RGBA but it is a different type and +// therefore does not trigger the draw.go fastest code paths. +// +// Unlike slowestRGBA, it still implements the draw.RGBA64Image interface. +type slowerRGBA struct { + Pix []uint8 + Stride int + Rect image.Rectangle +} + +func (p *slowerRGBA) ColorModel() color.Model { return color.RGBAModel } + +func (p *slowerRGBA) Bounds() image.Rectangle { return p.Rect } + +func (p *slowerRGBA) At(x, y int) color.Color { + return p.RGBA64At(x, y) +} + +func (p *slowerRGBA) RGBA64At(x, y int) color.RGBA64 { + if !(image.Point{x, y}.In(p.Rect)) { + return color.RGBA64{} + } + i := p.PixOffset(x, y) + s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + r := uint16(s[0]) + g := uint16(s[1]) + b := uint16(s[2]) + a := uint16(s[3]) + return color.RGBA64{ + (r << 8) | r, + (g << 8) | g, + (b << 8) | b, + (a << 8) | a, + } +} + +func (p *slowerRGBA) Set(x, y int, c color.Color) { + if !(image.Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + c1 := color.RGBAModel.Convert(c).(color.RGBA) + s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + s[0] = c1.R + s[1] = c1.G + s[2] = c1.B + s[3] = c1.A +} + +func (p *slowerRGBA) SetRGBA64(x, y int, c color.RGBA64) { + if !(image.Point{x, y}.In(p.Rect)) { + return + } + i := p.PixOffset(x, y) + s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857 + s[0] = uint8(c.R >> 8) + s[1] = uint8(c.G >> 8) + s[2] = uint8(c.B >> 8) + s[3] = uint8(c.A >> 8) +} + +func (p *slowerRGBA) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 +} + +func convertToSlowerRGBA(m image.Image) *slowerRGBA { + if rgba, ok := m.(*image.RGBA); ok { + return &slowerRGBA{ + Pix: append([]byte(nil), rgba.Pix...), + Stride: rgba.Stride, + Rect: rgba.Rect, + } + } + rgba := image.NewRGBA(m.Bounds()) + Draw(rgba, rgba.Bounds(), m, m.Bounds().Min, Src) + return &slowerRGBA{ + Pix: rgba.Pix, + Stride: rgba.Stride, + Rect: rgba.Rect, + } +} + +func init() { + var p any = (*slowerRGBA)(nil) + if _, ok := p.(RGBA64Image); !ok { + panic("slowerRGBA should be an RGBA64Image") + } +} + func eq(c0, c1 color.Color) bool { r0, g0, b0, a0 := c0.RGBA() r1, g1, b1, a1 := c1.RGBA() @@ -178,6 +344,32 @@ var drawTests = []drawTest{ {"grayAlphaSrc", vgradGray(), fillAlpha(192), Src, color.RGBA{102, 102, 102, 192}}, {"grayNil", vgradGray(), nil, Over, color.RGBA{136, 136, 136, 255}}, {"grayNilSrc", vgradGray(), nil, Src, color.RGBA{136, 136, 136, 255}}, + // Same again, but with a slowerRGBA source. + {"graySlower", convertToSlowerRGBA(vgradGray()), fillAlpha(255), + Over, color.RGBA{136, 136, 136, 255}}, + {"graySrcSlower", convertToSlowerRGBA(vgradGray()), fillAlpha(255), + Src, color.RGBA{136, 136, 136, 255}}, + {"grayAlphaSlower", convertToSlowerRGBA(vgradGray()), fillAlpha(192), + Over, color.RGBA{136, 102, 102, 255}}, + {"grayAlphaSrcSlower", convertToSlowerRGBA(vgradGray()), fillAlpha(192), + Src, color.RGBA{102, 102, 102, 192}}, + {"grayNilSlower", convertToSlowerRGBA(vgradGray()), nil, + Over, color.RGBA{136, 136, 136, 255}}, + {"grayNilSrcSlower", convertToSlowerRGBA(vgradGray()), nil, + Src, color.RGBA{136, 136, 136, 255}}, + // Same again, but with a slowestRGBA source. + {"graySlowest", convertToSlowestRGBA(vgradGray()), fillAlpha(255), + Over, color.RGBA{136, 136, 136, 255}}, + {"graySrcSlowest", convertToSlowestRGBA(vgradGray()), fillAlpha(255), + Src, color.RGBA{136, 136, 136, 255}}, + {"grayAlphaSlowest", convertToSlowestRGBA(vgradGray()), fillAlpha(192), + Over, color.RGBA{136, 102, 102, 255}}, + {"grayAlphaSrcSlowest", convertToSlowestRGBA(vgradGray()), fillAlpha(192), + Src, color.RGBA{102, 102, 102, 192}}, + {"grayNilSlowest", convertToSlowestRGBA(vgradGray()), nil, + Over, color.RGBA{136, 136, 136, 255}}, + {"grayNilSrcSlowest", convertToSlowestRGBA(vgradGray()), nil, + Src, color.RGBA{136, 136, 136, 255}}, // Uniform mask (100%, 75%, nil) and variable CMYK source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. @@ -188,13 +380,32 @@ var drawTests = []drawTest{ {"cmykAlphaSrc", vgradMagenta(), fillAlpha(192), Src, color.RGBA{145, 67, 145, 192}}, {"cmykNil", vgradMagenta(), nil, Over, color.RGBA{192, 89, 192, 255}}, {"cmykNilSrc", vgradMagenta(), nil, Src, color.RGBA{192, 89, 192, 255}}, - // Variable mask and variable source. + // Variable mask and uniform source. // At (x, y) == (8, 8): // The destination pixel is {136, 0, 0, 255}. // The source pixel is {0, 0, 255, 255}. // The mask pixel's alpha is 102, or 40%. {"generic", fillBlue(255), vgradAlpha(192), Over, color.RGBA{81, 0, 102, 255}}, {"genericSrc", fillBlue(255), vgradAlpha(192), Src, color.RGBA{0, 0, 102, 102}}, + // Same again, but with a slowerRGBA mask. + {"genericSlower", fillBlue(255), convertToSlowerRGBA(vgradAlpha(192)), + Over, color.RGBA{81, 0, 102, 255}}, + {"genericSrcSlower", fillBlue(255), convertToSlowerRGBA(vgradAlpha(192)), + Src, color.RGBA{0, 0, 102, 102}}, + // Same again, but with a slowestRGBA mask. + {"genericSlowest", fillBlue(255), convertToSlowestRGBA(vgradAlpha(192)), + Over, color.RGBA{81, 0, 102, 255}}, + {"genericSrcSlowest", fillBlue(255), convertToSlowestRGBA(vgradAlpha(192)), + Src, color.RGBA{0, 0, 102, 102}}, + // Variable mask and variable source. + // At (x, y) == (8, 8): + // The destination pixel is {136, 0, 0, 255}. + // The source pixel is: + // - {0, 48, 0, 90}. + // - {136} in Gray-space, which is {136, 136, 136, 255} in RGBA-space. + // The mask pixel's alpha is 102, or 40%. + {"rgbaVariableMaskOver", vgradGreen(90), vgradAlpha(192), Over, color.RGBA{117, 19, 0, 255}}, + {"grayVariableMaskOver", vgradGray(), vgradAlpha(192), Over, color.RGBA{136, 54, 54, 255}}, } func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image { @@ -260,30 +471,45 @@ func TestDraw(t *testing.T) { for _, r := range rr { loop: for _, test := range drawTests { - dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image) - // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. - golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) - b := dst.Bounds() - if !b.Eq(golden.Bounds()) { - t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds()) - continue - } - // Draw the same combination onto the actual dst using the optimized DrawMask implementation. - DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) - if image.Pt(8, 8).In(r) { - // Check that the resultant pixel at (8, 8) matches what we expect - // (the expected value can be verified by hand). - if !eq(dst.At(8, 8), test.expected) { - t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected) + for i := 0; i < 3; i++ { + dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image) + // For i != 0, substitute a different-typed dst that will take + // us off the fastest code paths. We should still get the same + // result, in terms of final pixel RGBA values. + switch i { + case 1: + dst = convertToSlowerRGBA(dst) + case 2: + dst = convertToSlowestRGBA(dst) + } + + // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. + golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) + b := dst.Bounds() + if !b.Eq(golden.Bounds()) { + t.Errorf("draw %v %s on %T: bounds %v versus %v", + r, test.desc, dst, dst.Bounds(), golden.Bounds()) continue } - } - // Check that the resultant dst image matches the golden output. - for y := b.Min.Y; y < b.Max.Y; y++ { - for x := b.Min.X; x < b.Max.X; x++ { - if !eq(dst.At(x, y), golden.At(x, y)) { - t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y)) - continue loop + // Draw the same combination onto the actual dst using the optimized DrawMask implementation. + DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) + if image.Pt(8, 8).In(r) { + // Check that the resultant pixel at (8, 8) matches what we expect + // (the expected value can be verified by hand). + if !eq(dst.At(8, 8), test.expected) { + t.Errorf("draw %v %s on %T: at (8, 8) %v versus %v", + r, test.desc, dst, dst.At(8, 8), test.expected) + continue + } + } + // Check that the resultant dst image matches the golden output. + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(dst.At(x, y), golden.At(x, y)) { + t.Errorf("draw %v %s on %T: at (%d, %d), %v versus golden %v", + r, test.desc, dst, x, y, dst.At(x, y), golden.At(x, y)) + continue loop + } } } } @@ -396,6 +622,70 @@ func TestFill(t *testing.T) { } } +func TestDrawSrcNonpremultiplied(t *testing.T) { + var ( + opaqueGray = color.NRGBA{0x99, 0x99, 0x99, 0xff} + transparentBlue = color.NRGBA{0x00, 0x00, 0xff, 0x00} + transparentGreen = color.NRGBA{0x00, 0xff, 0x00, 0x00} + transparentRed = color.NRGBA{0xff, 0x00, 0x00, 0x00} + + opaqueGray64 = color.NRGBA64{0x9999, 0x9999, 0x9999, 0xffff} + transparentPurple64 = color.NRGBA64{0xfedc, 0x0000, 0x7654, 0x0000} + ) + + // dst and src are 1x3 images but the dr rectangle (and hence the overlap) + // is only 1x2. The Draw call should affect dst's pixels at (1, 10) and (2, + // 10) but the pixel at (0, 10) should be untouched. + // + // The src image is entirely transparent (and the Draw operator is Src) so + // the two touched pixels should be set to transparent colors. + // + // In general, Go's color.Color type (and specifically the Color.RGBA + // method) works in premultiplied alpha, where there's no difference + // between "transparent blue" and "transparent red". It's all "just + // transparent" and canonically "transparent black" (all zeroes). + // + // However, since the operator is Src (so the pixels are 'copied', not + // 'blended') and both dst and src images are *image.NRGBA (N stands for + // Non-premultiplied alpha which *does* distinguish "transparent blue" and + // "transparent red"), we prefer that this distinction carries through and + // dst's touched pixels should be transparent blue and transparent green, + // not just transparent black. + { + dst := image.NewNRGBA(image.Rect(0, 10, 3, 11)) + dst.SetNRGBA(0, 10, opaqueGray) + src := image.NewNRGBA(image.Rect(1, 20, 4, 21)) + src.SetNRGBA(1, 20, transparentBlue) + src.SetNRGBA(2, 20, transparentGreen) + src.SetNRGBA(3, 20, transparentRed) + + dr := image.Rect(1, 10, 3, 11) + Draw(dst, dr, src, image.Point{1, 20}, Src) + + if got, want := dst.At(0, 10), opaqueGray; got != want { + t.Errorf("At(0, 10):\ngot %#v\nwant %#v", got, want) + } + if got, want := dst.At(1, 10), transparentBlue; got != want { + t.Errorf("At(1, 10):\ngot %#v\nwant %#v", got, want) + } + if got, want := dst.At(2, 10), transparentGreen; got != want { + t.Errorf("At(2, 10):\ngot %#v\nwant %#v", got, want) + } + } + + // Check image.NRGBA64 (not image.NRGBA) similarly. + { + dst := image.NewNRGBA64(image.Rect(0, 0, 1, 1)) + dst.SetNRGBA64(0, 0, opaqueGray64) + src := image.NewNRGBA64(image.Rect(0, 0, 1, 1)) + src.SetNRGBA64(0, 0, transparentPurple64) + Draw(dst, dst.Bounds(), src, image.Point{0, 0}, Src) + if got, want := dst.At(0, 0), transparentPurple64; got != want { + t.Errorf("At(0, 0):\ngot %#v\nwant %#v", got, want) + } + } +} + // TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg // error diffusion of a uniform 50% gray source image with a black-and-white // palette is a checkerboard pattern. diff --git a/src/image/gif/fuzz_test.go b/src/image/gif/fuzz_test.go new file mode 100644 index 00000000000000..3ddf15d80f33e8 --- /dev/null +++ b/src/image/gif/fuzz_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gif + +import ( + "bytes" + "image" + "os" + "path/filepath" + "strings" + "testing" +) + +func FuzzDecode(f *testing.F) { + testdata, err := os.ReadDir("../testdata") + if err != nil { + f.Fatalf("failed to read testdata directory: %s", err) + } + for _, de := range testdata { + if de.IsDir() || !strings.HasSuffix(de.Name(), ".gif") { + continue + } + b, err := os.ReadFile(filepath.Join("../testdata", de.Name())) + if err != nil { + f.Fatalf("failed to read testdata: %s", err) + } + f.Add(b) + } + + f.Fuzz(func(t *testing.T, b []byte) { + cfg, _, err := image.DecodeConfig(bytes.NewReader(b)) + if err != nil { + return + } + if cfg.Width*cfg.Height > 1e6 { + return + } + img, typ, err := image.Decode(bytes.NewReader(b)) + if err != nil || typ != "gif" { + return + } + for q := 1; q <= 256; q++ { + var w bytes.Buffer + err := Encode(&w, img, &Options{NumColors: q}) + if err != nil { + t.Fatalf("failed to encode valid image: %s", err) + } + img1, err := Decode(&w) + if err != nil { + t.Fatalf("failed to decode roundtripped image: %s", err) + } + got := img1.Bounds() + want := img.Bounds() + if !got.Eq(want) { + t.Fatalf("roundtripped image bounds have changed, got: %v, want: %v", got, want) + } + } + }) +} diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go index 9e8268c86f3c01..0867b102956753 100644 --- a/src/image/gif/reader.go +++ b/src/image/gif/reader.go @@ -250,6 +250,10 @@ func (d *decoder) decode(r io.Reader, configOnly, keepAllFrames bool) error { return err } + if !keepAllFrames && len(d.image) == 1 { + return nil + } + case sTrailer: if len(d.image) == 0 { return fmt.Errorf("gif: missing image data") diff --git a/src/image/gif/reader_test.go b/src/image/gif/reader_test.go index 5eec5ecb4a6889..a7f943adeb76ea 100644 --- a/src/image/gif/reader_test.go +++ b/src/image/gif/reader_test.go @@ -379,7 +379,7 @@ func TestLoopCount(t *testing.T) { func TestUnexpectedEOF(t *testing.T) { for i := len(testGIF) - 1; i >= 0; i-- { - _, err := Decode(bytes.NewReader(testGIF[:i])) + _, err := DecodeAll(bytes.NewReader(testGIF[:i])) if err == errNotEnough { continue } diff --git a/src/image/gif/writer_test.go b/src/image/gif/writer_test.go index af0105c6bea382..8dd28908d75186 100644 --- a/src/image/gif/writer_test.go +++ b/src/image/gif/writer_test.go @@ -603,7 +603,7 @@ func TestEncodeWrappedImage(t *testing.T) { t.Fatalf("readImg: %v", err) } - // Case 1: Enocde a wrapped image.Image + // Case 1: Encode a wrapped image.Image buf := new(bytes.Buffer) w0 := offsetImage{m0, m0.Bounds()} err = Encode(buf, w0, nil) @@ -619,7 +619,7 @@ func TestEncodeWrappedImage(t *testing.T) { t.Fatalf("Wrapped: average delta is too high. expected: 0, got %d", avgDelta) } - // Case 2: Enocde a wrapped image.Image with offset + // Case 2: Encode a wrapped image.Image with offset b0 := image.Rectangle{ Min: image.Point{ X: 128, diff --git a/src/image/image.go b/src/image/image.go index 930d9ac6c7bacd..dfb70d4eaf6275 100644 --- a/src/image/image.go +++ b/src/image/image.go @@ -13,7 +13,9 @@ // image format requires the prior registration of a decoder function. // Registration is typically automatic as a side effect of initializing that // format's package so that, to decode a PNG image, it suffices to have +// // import _ "image/png" +// // in a program's main package. The _ means to import a package purely for its // initialization side effects. // diff --git a/src/image/internal/imageutil/gen.go b/src/image/internal/imageutil/gen.go index 38f41303fa747f..65e1e30b64f700 100644 --- a/src/image/internal/imageutil/gen.go +++ b/src/image/internal/imageutil/gen.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore package main diff --git a/src/image/jpeg/dct_test.go b/src/image/jpeg/dct_test.go index 845e758878934f..ed5b73d562cbdb 100644 --- a/src/image/jpeg/dct_test.go +++ b/src/image/jpeg/dct_test.go @@ -5,10 +5,10 @@ package jpeg import ( - "bytes" "fmt" "math" "math/rand" + "strings" "testing" ) @@ -181,7 +181,7 @@ func slowIDCT(b *block) { } func (b *block) String() string { - s := bytes.NewBuffer(nil) + s := &strings.Builder{} fmt.Fprintf(s, "{\n") for y := 0; y < 8; y++ { fmt.Fprintf(s, "\t") diff --git a/src/image/jpeg/fuzz_test.go b/src/image/jpeg/fuzz_test.go new file mode 100644 index 00000000000000..716f06f43cff24 --- /dev/null +++ b/src/image/jpeg/fuzz_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jpeg + +import ( + "bytes" + "image" + "os" + "path/filepath" + "strings" + "testing" +) + +func FuzzDecode(f *testing.F) { + testdata, err := os.ReadDir("../testdata") + if err != nil { + f.Fatalf("failed to read testdata directory: %s", err) + } + for _, de := range testdata { + if de.IsDir() || !strings.HasSuffix(de.Name(), ".jpeg") { + continue + } + b, err := os.ReadFile(filepath.Join("../testdata", de.Name())) + if err != nil { + f.Fatalf("failed to read testdata: %s", err) + } + f.Add(b) + } + + f.Fuzz(func(t *testing.T, b []byte) { + cfg, _, err := image.DecodeConfig(bytes.NewReader(b)) + if err != nil { + return + } + if cfg.Width*cfg.Height > 1e6 { + return + } + img, typ, err := image.Decode(bytes.NewReader(b)) + if err != nil || typ != "jpeg" { + return + } + for q := 1; q <= 100; q++ { + var w bytes.Buffer + err := Encode(&w, img, &Options{Quality: q}) + if err != nil { + t.Fatalf("failed to encode valid image: %s", err) + } + img1, err := Decode(&w) + if err != nil { + t.Fatalf("failed to decode roundtripped image: %s", err) + } + got := img1.Bounds() + want := img.Bounds() + if !got.Eq(want) { + t.Fatalf("roundtripped image bounds have changed, got: %s, want: %s", got, want) + } + } + }) +} diff --git a/src/image/jpeg/reader_test.go b/src/image/jpeg/reader_test.go index bf07fadede5390..02a2eb6509bc47 100644 --- a/src/image/jpeg/reader_test.go +++ b/src/image/jpeg/reader_test.go @@ -13,6 +13,7 @@ import ( "io" "math/rand" "os" + "runtime/debug" "strings" "testing" "time" @@ -176,7 +177,7 @@ func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) erro } func pixString(pix []byte, stride, x, y int) string { - s := bytes.NewBuffer(nil) + s := &strings.Builder{} for j := 0; j < 8; j++ { fmt.Fprintf(s, "\t") for i := 0; i < 8; i++ { @@ -247,18 +248,16 @@ func TestLargeImageWithShortData(t *testing.T) { "\x20\x36\x9f\x78\x64\x75\xe6\xab\x7d\xb2\xde\x29\x70\xd3\x20\x27" + "\xde\xaf\xa4\xf0\xca\x9f\x24\xa8\xdf\x46\xa8\x24\x84\x96\xe3\x77" + "\xf9\x2e\xe0\x0a\x62\x7f\xdf\xd9" - c := make(chan error, 1) - go func() { - _, err := Decode(strings.NewReader(input)) - c <- err - }() - select { - case err := <-c: - if err == nil { - t.Fatalf("got nil error, want non-nil") - } - case <-time.After(3 * time.Second): - t.Fatalf("timed out") + + timer := time.AfterFunc(30*time.Second, func() { + debug.SetTraceback("all") + panic("TestLargeImageWithShortData stuck in Decode") + }) + defer timer.Stop() + + _, err := Decode(strings.NewReader(input)) + if err == nil { + t.Fatalf("got nil error, want non-nil") } } diff --git a/src/image/jpeg/writer.go b/src/image/jpeg/writer.go index a600499004725f..0027f78294a259 100644 --- a/src/image/jpeg/writer.go +++ b/src/image/jpeg/writer.go @@ -481,25 +481,25 @@ func scale(dst *block, src *[4]block) { } // sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes: -// - the marker length "\x00\x08", -// - the number of components "\x01", -// - component 1 uses DC table 0 and AC table 0 "\x01\x00", -// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for -// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) -// should be 0x00, 0x3f, 0x00<<4 | 0x00. +// - the marker length "\x00\x08", +// - the number of components "\x01", +// - component 1 uses DC table 0 and AC table 0 "\x01\x00", +// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for +// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) +// should be 0x00, 0x3f, 0x00<<4 | 0x00. var sosHeaderY = []byte{ 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, } // sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes: -// - the marker length "\x00\x0c", -// - the number of components "\x03", -// - component 1 uses DC table 0 and AC table 0 "\x01\x00", -// - component 2 uses DC table 1 and AC table 1 "\x02\x11", -// - component 3 uses DC table 1 and AC table 1 "\x03\x11", -// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for -// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) -// should be 0x00, 0x3f, 0x00<<4 | 0x00. +// - the marker length "\x00\x0c", +// - the number of components "\x03", +// - component 1 uses DC table 0 and AC table 0 "\x01\x00", +// - component 2 uses DC table 1 and AC table 1 "\x02\x11", +// - component 3 uses DC table 1 and AC table 1 "\x03\x11", +// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for +// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al) +// should be 0x00, 0x3f, 0x00<<4 | 0x00. var sosHeaderYCbCr = []byte{ 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, diff --git a/src/image/jpeg/writer_test.go b/src/image/jpeg/writer_test.go index abd5e323339be1..341477044a3a00 100644 --- a/src/image/jpeg/writer_test.go +++ b/src/image/jpeg/writer_test.go @@ -13,6 +13,7 @@ import ( "io" "math/rand" "os" + "strings" "testing" ) @@ -82,7 +83,7 @@ func TestUnscaledQuant(t *testing.T) { } if bad { names := [nQuantIndex]string{"Luminance", "Chrominance"} - buf := &bytes.Buffer{} + buf := &strings.Builder{} for i, name := range names { fmt.Fprintf(buf, "// %s.\n{\n", name) for zig := 0; zig < blockSize; zig++ { diff --git a/src/image/png/fuzz.go b/src/image/png/fuzz.go index 6508533f4440b3..688b6c99361339 100644 --- a/src/image/png/fuzz.go +++ b/src/image/png/fuzz.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gofuzz -// +build gofuzz package png diff --git a/src/image/png/fuzz_test.go b/src/image/png/fuzz_test.go new file mode 100644 index 00000000000000..22b3ef082a458e --- /dev/null +++ b/src/image/png/fuzz_test.go @@ -0,0 +1,68 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package png + +import ( + "bytes" + "image" + "os" + "path/filepath" + "strings" + "testing" +) + +func FuzzDecode(f *testing.F) { + testdata, err := os.ReadDir("../testdata") + if err != nil { + f.Fatalf("failed to read testdata directory: %s", err) + } + for _, de := range testdata { + if de.IsDir() || !strings.HasSuffix(de.Name(), ".png") { + continue + } + b, err := os.ReadFile(filepath.Join("../testdata", de.Name())) + if err != nil { + f.Fatalf("failed to read testdata: %s", err) + } + f.Add(b) + } + + f.Fuzz(func(t *testing.T, b []byte) { + cfg, _, err := image.DecodeConfig(bytes.NewReader(b)) + if err != nil { + return + } + if cfg.Width*cfg.Height > 1e6 { + return + } + img, typ, err := image.Decode(bytes.NewReader(b)) + if err != nil || typ != "png" { + return + } + levels := []CompressionLevel{ + DefaultCompression, + NoCompression, + BestSpeed, + BestCompression, + } + for _, l := range levels { + var w bytes.Buffer + e := &Encoder{CompressionLevel: l} + err = e.Encode(&w, img) + if err != nil { + t.Fatalf("failed to encode valid image: %s", err) + } + img1, err := Decode(&w) + if err != nil { + t.Fatalf("failed to decode roundtripped image: %s", err) + } + got := img1.Bounds() + want := img.Bounds() + if !got.Eq(want) { + t.Fatalf("roundtripped image bounds have changed, got: %s, want: %s", got, want) + } + } + }) +} diff --git a/src/image/png/reader.go b/src/image/png/reader.go index 910520bd4b3430..3a717344c27998 100644 --- a/src/image/png/reader.go +++ b/src/image/png/reader.go @@ -51,6 +51,10 @@ func cbPaletted(cb int) bool { return cbP1 <= cb && cb <= cbP8 } +func cbTrueColor(cb int) bool { + return cb == cbTC8 || cb == cbTC16 +} + // Filter type, as per the PNG spec. const ( ftNone = 0 @@ -325,7 +329,9 @@ func (d *decoder) parsetRNS(length uint32) error { // Read presents one or more IDAT chunks as one continuous stream (minus the // intermediate chunk headers and footers). If the PNG data looked like: -// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2 +// +// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2 +// // then this reader presents xxxyy. For well-formed PNG data, the decoder state // immediately before the first Read call is that d.r is positioned between the // first IDAT and xxx, and the decoder state immediately after the last Read @@ -821,9 +827,17 @@ func (d *decoder) mergePassInto(dst image.Image, src image.Image, pass int) { dstPix, stride, rect = target.Pix, target.Stride, target.Rect bytesPerPixel = 8 case *image.Paletted: - srcPix = src.(*image.Paletted).Pix + source := src.(*image.Paletted) + srcPix = source.Pix dstPix, stride, rect = target.Pix, target.Stride, target.Rect bytesPerPixel = 1 + if len(target.Palette) < len(source.Palette) { + // readImagePass can return a paletted image whose implicit palette + // length (one more than the maximum Pix value) is larger than the + // explicit palette length (what's in the PLTE chunk). Make the + // same adjustment here. + target.Palette = source.Palette + } case *image.RGBA: srcPix = src.(*image.RGBA).Pix dstPix, stride, rect = target.Pix, target.Stride, target.Rect @@ -860,7 +874,7 @@ func (d *decoder) parseIEND(length uint32) error { return d.verifyChecksum() } -func (d *decoder) parseChunk() error { +func (d *decoder) parseChunk(configOnly bool) error { // Read the length and chunk type. if _, err := io.ReadFull(d.r, d.tmp[:8]); err != nil { return err @@ -888,6 +902,10 @@ func (d *decoder) parseChunk() error { if d.stage != dsSeenPLTE { return chunkOrderError } + } else if cbTrueColor(d.cb) { + if d.stage != dsSeenIHDR && d.stage != dsSeenPLTE { + return chunkOrderError + } } else if d.stage != dsSeenIHDR { return chunkOrderError } @@ -905,6 +923,9 @@ func (d *decoder) parseChunk() error { break } d.stage = dsSeenIDAT + if configOnly { + return nil + } return d.parseIDAT(length) case "IEND": if d.stage != dsSeenIDAT { @@ -964,7 +985,7 @@ func Decode(r io.Reader) (image.Image, error) { return nil, err } for d.stage != dsSeenIEND { - if err := d.parseChunk(); err != nil { + if err := d.parseChunk(false); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } @@ -987,21 +1008,26 @@ func DecodeConfig(r io.Reader) (image.Config, error) { } return image.Config{}, err } + for { - if err := d.parseChunk(); err != nil { + if err := d.parseChunk(true); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return image.Config{}, err } - paletted := cbPaletted(d.cb) - if d.stage == dsSeenIHDR && !paletted { - break - } - if d.stage == dsSeenPLTE && paletted { - break + + if cbPaletted(d.cb) { + if d.stage >= dsSeentRNS { + break + } + } else { + if d.stage >= dsSeenIHDR { + break + } } } + var cm color.Model switch d.cb { case cbG1, cbG2, cbG4, cbG8: diff --git a/src/image/png/reader_test.go b/src/image/png/reader_test.go index 39376852941033..ccf30b2c6edd98 100644 --- a/src/image/png/reader_test.go +++ b/src/image/png/reader_test.go @@ -783,6 +783,59 @@ func TestDimensionOverflow(t *testing.T) { } } +func TestDecodePalettedWithTransparency(t *testing.T) { + // These bytes come from https://go.dev/issue/54325 + // + // Per the PNG spec, a PLTE chunk contains 3 (not 4) bytes per palette + // entry: RGB (not RGBA). The alpha value comes from the optional tRNS + // chunk. Here, the PLTE chunk (0x50, 0x4c, 0x54, 0x45, etc) has 16 entries + // (0x30 = 48 bytes) and the tRNS chunk (0x74, 0x52, 0x4e, 0x53, etc) has 1 + // entry (0x01 = 1 byte) that sets the first palette entry's alpha to zero. + // + // Both Decode and DecodeConfig should pick up that the first palette + // entry's alpha is zero. + src := []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x04, 0x03, 0x00, 0x00, 0x00, 0x81, 0x54, 0x67, + 0xc7, 0x00, 0x00, 0x00, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0e, + 0x00, 0x23, 0x27, 0x7b, 0xb1, 0x2d, 0x0a, 0x49, 0x3f, 0x19, 0x78, 0x5f, 0xcd, 0xe4, 0x69, 0x69, + 0xe4, 0x71, 0x59, 0x53, 0x80, 0x11, 0x14, 0x8b, 0x00, 0xa9, 0x8d, 0x95, 0xcb, 0x99, 0x2f, 0x6b, + 0xd7, 0x29, 0x91, 0xd7, 0x7b, 0xba, 0xff, 0xe3, 0xd7, 0x13, 0xc6, 0xd3, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0xfd, 0x49, 0x44, + 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x00, 0x83, 0x55, 0x0c, 0x68, 0x60, 0x9d, 0x02, 0x9a, 0x80, + 0xde, 0x23, 0x74, 0x15, 0xef, 0x50, 0x94, 0x70, 0x2d, 0xd2, 0x7b, 0x87, 0xa2, 0x84, 0xeb, 0xee, + 0xbb, 0x77, 0x6f, 0x51, 0x94, 0xe8, 0xbd, 0x7d, 0xf7, 0xee, 0x12, 0xb2, 0x80, 0xd2, 0x3d, 0x54, + 0x01, 0x26, 0x10, 0x1f, 0x59, 0x40, 0x0f, 0xc8, 0xd7, 0x7e, 0x84, 0x70, 0x1c, 0xd7, 0xba, 0xb7, + 0x4a, 0xda, 0xda, 0x77, 0x11, 0xf6, 0xac, 0x5a, 0xa5, 0xf4, 0xf9, 0xbf, 0xfd, 0x3d, 0x24, 0x6b, + 0x98, 0x94, 0xf4, 0xff, 0x7f, 0x52, 0x42, 0x16, 0x30, 0x0e, 0xd9, 0xed, 0x6a, 0x8c, 0xec, 0x10, + 0x65, 0x53, 0x97, 0x60, 0x23, 0x64, 0x1d, 0x8a, 0x2e, 0xc6, 0x2e, 0x42, 0x08, 0x3d, 0x4c, 0xca, + 0x81, 0xc1, 0x82, 0xa6, 0xa2, 0x46, 0x08, 0x3d, 0x4a, 0xa1, 0x82, 0xc6, 0x82, 0xa1, 0x4a, 0x08, + 0x3d, 0xfa, 0xa6, 0x81, 0xa1, 0xa2, 0xc1, 0x9f, 0x10, 0x66, 0xd4, 0x2b, 0x87, 0x0a, 0x86, 0x1a, + 0x7d, 0x57, 0x80, 0x9b, 0x99, 0xaf, 0x62, 0x1a, 0x1a, 0xec, 0xf0, 0x0d, 0x66, 0x2a, 0x7b, 0x5a, + 0xba, 0xd2, 0x64, 0x63, 0x4b, 0xa6, 0xb2, 0xb4, 0x02, 0xa8, 0x12, 0xb5, 0x24, 0xa5, 0x99, 0x2e, + 0x33, 0x95, 0xd4, 0x92, 0x10, 0xee, 0xd0, 0x59, 0xb9, 0x6a, 0xd6, 0x21, 0x24, 0xb7, 0x33, 0x9d, + 0x01, 0x01, 0x64, 0xbf, 0xac, 0x59, 0xb2, 0xca, 0xeb, 0x14, 0x92, 0x80, 0xd6, 0x9a, 0x53, 0x4a, + 0x6b, 0x4e, 0x2d, 0x42, 0x52, 0xa1, 0x73, 0x28, 0x54, 0xe7, 0x90, 0x6a, 0x00, 0x92, 0x92, 0x45, + 0xa1, 0x40, 0x84, 0x2c, 0xe0, 0xc4, 0xa0, 0xb2, 0x28, 0x14, 0xc1, 0x67, 0xe9, 0x50, 0x60, 0x60, + 0xea, 0x70, 0x40, 0x12, 0x00, 0x79, 0x54, 0x09, 0x22, 0x00, 0x00, 0x30, 0xf3, 0x52, 0x87, 0xc6, + 0xe4, 0xbd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + } + + cfg, err := DecodeConfig(bytes.NewReader(src)) + if err != nil { + t.Fatalf("DecodeConfig: %v", err) + } else if _, _, _, alpha := cfg.ColorModel.(color.Palette)[0].RGBA(); alpha != 0 { + t.Errorf("DecodeConfig: got %d, want 0", alpha) + } + + img, err := Decode(bytes.NewReader(src)) + if err != nil { + t.Fatalf("Decode: %v", err) + } else if _, _, _, alpha := img.ColorModel().(color.Palette)[0].RGBA(); alpha != 0 { + t.Errorf("Decode: got %d, want 0", alpha) + } +} + func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) { data, err := os.ReadFile(filename) if err != nil { diff --git a/src/image/ycbcr.go b/src/image/ycbcr.go index 328b90d1525655..78f5ebe1d8161f 100644 --- a/src/image/ycbcr.go +++ b/src/image/ycbcr.go @@ -45,6 +45,7 @@ func (s YCbCrSubsampleRatio) String() string { // that map to separate chroma samples. // It is not an absolute requirement, but YStride and len(Y) are typically // multiples of 8, and: +// // For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1. // For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2. // For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4. diff --git a/src/index/suffixarray/gen.go b/src/index/suffixarray/gen.go index 3bc9b1e2aea432..d6eb32e2aeefa3 100644 --- a/src/index/suffixarray/gen.go +++ b/src/index/suffixarray/gen.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Gen generates sais2.go by duplicating functions in sais.go // using different input types. diff --git a/src/index/suffixarray/sais.go b/src/index/suffixarray/sais.go index b4496d29882fab..74c52356171981 100644 --- a/src/index/suffixarray/sais.go +++ b/src/index/suffixarray/sais.go @@ -656,7 +656,7 @@ func recurse_32(sa, oldTmp []int32, numLMS, maxID int) { dst, saTmp, text := sa[:numLMS], sa[numLMS:len(sa)-numLMS], sa[len(sa)-numLMS:] // Set up temporary space for recursive call. - // We must pass sais_32 a tmp buffer wiith at least maxID entries. + // We must pass sais_32 a tmp buffer with at least maxID entries. // // The subproblem is guaranteed to have length at most len(sa)/2, // so that sa can hold both the subproblem and its suffix array. diff --git a/src/index/suffixarray/sais2.go b/src/index/suffixarray/sais2.go index f1247028c6cf02..32b89728011ffd 100644 --- a/src/index/suffixarray/sais2.go +++ b/src/index/suffixarray/sais2.go @@ -1194,7 +1194,7 @@ func recurse_64(sa, oldTmp []int64, numLMS, maxID int) { dst, saTmp, text := sa[:numLMS], sa[numLMS:len(sa)-numLMS], sa[len(sa)-numLMS:] // Set up temporary space for recursive call. - // We must pass sais_64 a tmp buffer wiith at least maxID entries. + // We must pass sais_64 a tmp buffer with at least maxID entries. // // The subproblem is guaranteed to have length at most len(sa)/2, // so that sa can hold both the subproblem and its suffix array. diff --git a/src/index/suffixarray/suffixarray.go b/src/index/suffixarray/suffixarray.go index 9c169e7aca84e4..7fca0fd7ba9977 100644 --- a/src/index/suffixarray/suffixarray.go +++ b/src/index/suffixarray/suffixarray.go @@ -13,7 +13,6 @@ // // lookup byte slice s // offsets1 := index.Lookup(s, -1) // the list of all indices where s occurs in data // offsets2 := index.Lookup(s, 3) // the list of at most 3 indices where s occurs in data -// package suffixarray import ( @@ -230,7 +229,6 @@ func (x *Index) Write(w io.Writer) error { // Bytes returns the data over which the index was created. // It must not be modified. -// func (x *Index) Bytes() []byte { return x.data } @@ -255,7 +253,6 @@ func (x *Index) lookupAll(s []byte) ints { // The result is nil if s is empty, s is not found, or n == 0. // Lookup time is O(log(N)*len(s) + len(result)) where N is the // size of the indexed data. -// func (x *Index) Lookup(s []byte, n int) (result []int) { if len(s) > 0 && n != 0 { matches := x.lookupAll(s) @@ -286,7 +283,6 @@ func (x *Index) Lookup(s []byte, n int) (result []int) { // in successive order. Otherwise, at most n matches are returned and // they may not be successive. The result is nil if there are no matches, // or if n == 0. -// func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) { // a non-empty literal prefix is used to determine possible // match start indices with Lookup diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go index aaff9cece35720..11acac346fbbc6 100644 --- a/src/internal/abi/abi.go +++ b/src/internal/abi/abi.go @@ -4,7 +4,10 @@ package abi -import "unsafe" +import ( + "internal/goarch" + "unsafe" +) // RegArgs is a struct that has space for each argument // and return value register on the current architecture. @@ -16,6 +19,14 @@ import "unsafe" // when it may not be safe to keep them only in the integer // register space otherwise. type RegArgs struct { + // Values in these slots should be precisely the bit-by-bit + // representation of how they would appear in a register. + // + // This means that on big endian arches, integer values should + // be in the top bits of the slot. Floats are usually just + // directly represented, but some architectures treat narrow + // width floating point values specially (e.g. they're promoted + // first, or they need to be NaN-boxed). Ints [IntArgRegs]uintptr // untyped integer registers Floats [FloatArgRegs]uint64 // untyped float registers @@ -33,6 +44,44 @@ type RegArgs struct { ReturnIsPtr IntArgRegBitmap } +func (r *RegArgs) Dump() { + print("Ints:") + for _, x := range r.Ints { + print(" ", x) + } + println() + print("Floats:") + for _, x := range r.Floats { + print(" ", x) + } + println() + print("Ptrs:") + for _, x := range r.Ptrs { + print(" ", x) + } + println() +} + +// IntRegArgAddr returns a pointer inside of r.Ints[reg] that is appropriately +// offset for an argument of size argSize. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +// +// This method is a helper for dealing with the endianness of different CPU +// architectures, since sub-word-sized arguments in big endian architectures +// need to be "aligned" to the upper edge of the register to be interpreted +// by the CPU correctly. +func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer { + if argSize > goarch.PtrSize || argSize == 0 || argSize&(argSize-1) != 0 { + panic("invalid argSize") + } + offset := uintptr(0) + if goarch.BigEndian { + offset = goarch.PtrSize - argSize + } + return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset) +} + // IntArgRegBitmap is a bitmap large enough to hold one bit per // integer argument/return register. type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8 @@ -65,7 +114,7 @@ func (b *IntArgRegBitmap) Get(i int) bool { // compile-time error. // // Implemented as a compile intrinsic. -func FuncPCABI0(f interface{}) uintptr +func FuncPCABI0(f any) uintptr // FuncPCABIInternal returns the entry PC of the function f. If f is a // direct reference of a function, it must be defined as ABIInternal. @@ -74,4 +123,4 @@ func FuncPCABI0(f interface{}) uintptr // the behavior is undefined. // // Implemented as a compile intrinsic. -func FuncPCABIInternal(f interface{}) uintptr +func FuncPCABIInternal(f any) uintptr diff --git a/src/internal/abi/abi_amd64.go b/src/internal/abi/abi_amd64.go index aff71f6a58a955..d3c567822311b1 100644 --- a/src/internal/abi/abi_amd64.go +++ b/src/internal/abi/abi_amd64.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build goexperiment.regabireflect -// +build goexperiment.regabireflect - package abi const ( diff --git a/src/internal/abi/abi_arm64.go b/src/internal/abi/abi_arm64.go new file mode 100644 index 00000000000000..4dc51431bfb80d --- /dev/null +++ b/src/internal/abi/abi_arm64.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // See abi_generic.go. + + // R0 - R15. + IntArgRegs = 16 + + // F0 - F15. + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/src/internal/abi/abi_generic.go b/src/internal/abi/abi_generic.go index 69400f930fb12b..d5803e70d2b310 100644 --- a/src/internal/abi/abi_generic.go +++ b/src/internal/abi/abi_generic.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !goexperiment.regabireflect -// +build !goexperiment.regabireflect +//go:build !goexperiment.regabiargs && !amd64 && !arm64 && !ppc64 && !ppc64le package abi diff --git a/src/internal/abi/abi_ppc64x.go b/src/internal/abi/abi_ppc64x.go new file mode 100644 index 00000000000000..73416d74d64a21 --- /dev/null +++ b/src/internal/abi/abi_ppc64x.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +package abi + +const ( + // See abi_generic.go. + + // R3 - R10, R14 - R17. + IntArgRegs = 12 + + // F1 - F12. + FloatArgRegs = 12 + + EffectiveFloatRegSize = 8 +) diff --git a/src/internal/abi/abi_riscv64.go b/src/internal/abi/abi_riscv64.go new file mode 100644 index 00000000000000..165682057733a4 --- /dev/null +++ b/src/internal/abi/abi_riscv64.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.regabiargs + +package abi + +const ( + // See abi_generic.go. + + // X8 - X23 + IntArgRegs = 16 + + // F8 - F23. + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/src/internal/abi/abi_test.go b/src/internal/abi/abi_test.go index 5a3b6b616d37ef..51d26f69aecae9 100644 --- a/src/internal/abi/abi_test.go +++ b/src/internal/abi/abi_test.go @@ -50,7 +50,7 @@ func TestFuncPCCompileError(t *testing.T) { } // compile go code. - cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-symabis", symabi, "-o", obj, goSrc) + cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-p=p", "-symabis", symabi, "-o", obj, goSrc) out, err = cmd.CombinedOutput() if err == nil { t.Fatalf("go tool compile did not fail") diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go index 9fe7f211fb3024..e8553e85008231 100644 --- a/src/internal/buildcfg/cfg.go +++ b/src/internal/buildcfg/cfg.go @@ -15,21 +15,22 @@ import ( "fmt" "os" "path/filepath" + "runtime" "strings" ) var ( - defaultGOROOT string // set by linker - - GOROOT = envOr("GOROOT", defaultGOROOT) + GOROOT = runtime.GOROOT() // cached for efficiency GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) GO386 = envOr("GO386", defaultGO386) + GOAMD64 = goamd64() GOARM = goarm() GOMIPS = gomips() GOMIPS64 = gomips64() GOPPC64 = goppc64() GOWASM = gowasm() + ToolTags = toolTags() GO_LDSO = defaultGO_LDSO Version = version ) @@ -52,6 +53,21 @@ func envOr(key, value string) string { return value } +func goamd64() int { + switch v := envOr("GOAMD64", defaultGOAMD64); v { + case "v1": + return 1 + case "v2": + return 2 + case "v3": + return 3 + case "v4": + return 4 + } + Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4") + return int(defaultGOAMD64[len("v")] - '0') +} + func goarm() int { def := defaultGOARM if GOOS == "android" && GOARCH == "arm" { @@ -94,14 +110,16 @@ func goppc64() int { return 8 case "power9": return 9 + case "power10": + return 10 } - Error = fmt.Errorf("invalid GOPPC64: must be power8, power9") + Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10") return int(defaultGOPPC64[len("power")] - '0') } type gowasmFeatures struct { - SignExt bool SatConv bool + SignExt bool } func (f gowasmFeatures) String() string { @@ -134,3 +152,61 @@ func gowasm() (f gowasmFeatures) { func Getgoextlinkenabled() string { return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) } + +func toolTags() []string { + tags := experimentTags() + tags = append(tags, gogoarchTags()...) + return tags +} + +func experimentTags() []string { + var list []string + // For each experiment that has been enabled in the toolchain, define a + // build tag with the same name but prefixed by "goexperiment." which can be + // used for compiling alternative files for the experiment. This allows + // changes for the experiment, like extra struct fields in the runtime, + // without affecting the base non-experiment code at all. + for _, exp := range Experiment.Enabled() { + list = append(list, "goexperiment."+exp) + } + return list +} + +func gogoarchTags() []string { + switch GOARCH { + case "386": + return []string{GOARCH + "." + GO386} + case "amd64": + var list []string + for i := 1; i <= GOAMD64; i++ { + list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i)) + } + return list + case "arm": + var list []string + for i := 5; i <= GOARM; i++ { + list = append(list, fmt.Sprintf("%s.%d", GOARCH, i)) + } + return list + case "mips", "mipsle": + return []string{GOARCH + "." + GOMIPS} + case "mips64", "mips64le": + return []string{GOARCH + "." + GOMIPS64} + case "ppc64", "ppc64le": + var list []string + for i := 8; i <= GOPPC64; i++ { + list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i)) + } + return list + case "wasm": + var list []string + if GOWASM.SatConv { + list = append(list, GOARCH+".satconv") + } + if GOWASM.SignExt { + list = append(list, GOARCH+".signext") + } + return list + } + return nil +} diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go new file mode 100644 index 00000000000000..0123593317d431 --- /dev/null +++ b/src/internal/buildcfg/cfg_test.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package buildcfg + +import ( + "os" + "testing" +) + +func TestConfigFlags(t *testing.T) { + os.Setenv("GOAMD64", "v1") + if goamd64() != 1 { + t.Errorf("Wrong parsing of GOAMD64=v1") + } + os.Setenv("GOAMD64", "v4") + if goamd64() != 4 { + t.Errorf("Wrong parsing of GOAMD64=v4") + } + Error = nil + os.Setenv("GOAMD64", "1") + if goamd64(); Error == nil { + t.Errorf("Wrong parsing of GOAMD64=1") + } +} diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index 9a60253aab9b18..a5955d3947a751 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -12,6 +12,13 @@ import ( "internal/goexperiment" ) +// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline +// (platform-default) experiment configuration. +type ExperimentFlags struct { + goexperiment.Flags + baseline goexperiment.Flags +} + // Experiment contains the toolchain experiments enabled for the // current build. // @@ -21,14 +28,17 @@ import ( // experimentBaseline specifies the experiment flags that are enabled by // default in the current toolchain. This is, in effect, the "control" // configuration and any variation from this is an experiment. -var Experiment, experimentBaseline = func() (goexperiment.Flags, goexperiment.Flags) { - flags, baseline, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) +var Experiment ExperimentFlags = func() ExperimentFlags { + flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) if err != nil { Error = err + return ExperimentFlags{} } - return flags, baseline + return *flags }() +// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string. +// It is not guaranteed to be canonical. const DefaultGOEXPERIMENT = defaultGOEXPERIMENT // FramePointerEnabled enables the use of platform conventions for @@ -45,19 +55,31 @@ var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64" // flag sets. // // TODO(mdempsky): Move to internal/goexperiment. -func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment.Flags, err error) { - regabiSupported := goarch == "amd64" && (goos == "android" || goos == "linux" || goos == "darwin" || goos == "windows") +func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + // regabiSupported is set to true on platforms where register ABI is + // supported and enabled by default. + // regabiAlwaysOn is set to true on platforms where register ABI is + // always on. + var regabiSupported, regabiAlwaysOn bool + switch goarch { + case "amd64", "arm64", "ppc64le", "ppc64": + regabiAlwaysOn = true + regabiSupported = true + case "riscv64": + regabiSupported = true + } - baseline = goexperiment.Flags{ + baseline := goexperiment.Flags{ RegabiWrappers: regabiSupported, - RegabiG: regabiSupported, - RegabiReflect: regabiSupported, - RegabiDefer: regabiSupported, RegabiArgs: regabiSupported, + Unified: true, } // Start with the statically enabled set of experiments. - flags = baseline + flags := &ExperimentFlags{ + Flags: baseline, + baseline: baseline, + } // Pick up any changes to the baseline configuration from the // GOEXPERIMENT environment. This can be set at make.bash time @@ -65,7 +87,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment if goexp != "" { // Create a map of known experiment names. names := make(map[string]func(bool)) - rv := reflect.ValueOf(&flags).Elem() + rv := reflect.ValueOf(&flags.Flags).Elem() rt := rv.Type() for i := 0; i < rt.NumField(); i++ { field := rv.Field(i) @@ -78,9 +100,6 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment // do the right thing. names["regabi"] = func(v bool) { flags.RegabiWrappers = v - flags.RegabiG = v - flags.RegabiReflect = v - flags.RegabiDefer = v flags.RegabiArgs = v } @@ -93,7 +112,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment // GOEXPERIMENT=none disables all experiment flags. // This is used by cmd/dist, which doesn't know how // to build with any experiment flags. - flags = goexperiment.Flags{} + flags.Flags = goexperiment.Flags{} continue } val := true @@ -102,29 +121,32 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (flags, baseline goexperiment } set, ok := names[f] if !ok { - err = fmt.Errorf("unknown GOEXPERIMENT %s", f) - return + return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f) } set(val) } } - // regabi is only supported on amd64. - if goarch != "amd64" { + if regabiAlwaysOn { + flags.RegabiWrappers = true + flags.RegabiArgs = true + } + // regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le. + if !regabiSupported { flags.RegabiWrappers = false - flags.RegabiG = false - flags.RegabiReflect = false - flags.RegabiDefer = false flags.RegabiArgs = false } // Check regabi dependencies. - if flags.RegabiG && !flags.RegabiWrappers { - err = fmt.Errorf("GOEXPERIMENT regabig requires regabiwrappers") - } - if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) { - err = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer") + if flags.RegabiArgs && !flags.RegabiWrappers { + return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers") } - return + return flags, nil +} + +// String returns the canonical GOEXPERIMENT string to enable this experiment +// configuration. (Experiments in the same state as in the baseline are elided.) +func (exp *ExperimentFlags) String() string { + return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",") } // expList returns the list of lower-cased experiment names for @@ -157,33 +179,14 @@ func expList(exp, base *goexperiment.Flags, all bool) []string { return list } -// GOEXPERIMENT is a comma-separated list of enabled or disabled -// experiments that differ from the baseline experiment configuration. -// GOEXPERIMENT is exactly what a user would set on the command line -// to get the set of enabled experiments. -func GOEXPERIMENT() string { - return strings.Join(expList(&Experiment, &experimentBaseline, false), ",") -} - -// EnabledExperiments returns a list of enabled experiments, as +// Enabled returns a list of enabled experiments, as // lower-cased experiment names. -func EnabledExperiments() []string { - return expList(&Experiment, nil, false) +func (exp *ExperimentFlags) Enabled() []string { + return expList(&exp.Flags, nil, false) } -// AllExperiments returns a list of all experiment settings. +// All returns a list of all experiment settings. // Disabled experiments appear in the list prefixed by "no". -func AllExperiments() []string { - return expList(&Experiment, nil, true) -} - -// UpdateExperiments updates the Experiment global based on a new GOARCH value. -// This is only required for cmd/go, which can change GOARCH after -// program startup due to use of "go env -w". -func UpdateExperiments(goos, goarch, goexperiment string) { - var err error - Experiment, experimentBaseline, err = ParseGOEXPERIMENT(goos, goarch, goexperiment) - if err != nil { - Error = err - } +func (exp *ExperimentFlags) All() []string { + return expList(&exp.Flags, nil, true) } diff --git a/src/internal/bytealg/bytealg.go b/src/internal/bytealg/bytealg.go index 6b2b540acc8846..ebebce75fe484a 100644 --- a/src/internal/bytealg/bytealg.go +++ b/src/internal/bytealg/bytealg.go @@ -11,7 +11,6 @@ import ( // Offsets into internal/cpu records for use in assembly. const ( - offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) offsetX86HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) offsetX86HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) diff --git a/src/internal/bytealg/compare_386.s b/src/internal/bytealg/compare_386.s index 0981983d20f313..27b660ccf7c526 100644 --- a/src/internal/bytealg/compare_386.s +++ b/src/internal/bytealg/compare_386.s @@ -36,8 +36,9 @@ TEXT cmpbody<>(SB),NOSPLIT,$0-0 JEQ allsame CMPL BP, $4 JB small - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE mediumloop +#ifdef GO386_softfloat + JMP mediumloop +#endif largeloop: CMPL BP, $16 JB mediumloop diff --git a/src/internal/bytealg/compare_amd64.s b/src/internal/bytealg/compare_amd64.s index 8295acb03a184d..fdd015f560b79f 100644 --- a/src/internal/bytealg/compare_amd64.s +++ b/src/internal/bytealg/compare_amd64.s @@ -3,10 +3,10 @@ // license that can be found in the LICENSE file. #include "go_asm.h" +#include "asm_amd64.h" #include "textflag.h" TEXT ·Compare(SB),NOSPLIT,$0-56 -#ifdef GOEXPERIMENT_regabiargs // AX = a_base (want in SI) // BX = a_len (want in BX) // CX = a_cap (unused) @@ -15,17 +15,9 @@ TEXT ·Compare(SB),NOSPLIT,$0-56 // R8 = b_cap (unused) MOVQ SI, DX MOVQ AX, SI -#else - MOVQ a_base+0(FP), SI - MOVQ a_len+8(FP), BX - MOVQ b_base+24(FP), DI - MOVQ b_len+32(FP), DX - LEAQ ret+48(FP), R9 -#endif JMP cmpbody<>(SB) TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 -#ifdef GOEXPERIMENT_regabiargs // AX = a_base (want in SI) // BX = a_len (want in BX) // CX = b_base (want in DI) @@ -33,13 +25,6 @@ TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 MOVQ AX, SI MOVQ DI, DX MOVQ CX, DI -#else - MOVQ a_base+0(FP), SI - MOVQ a_len+8(FP), BX - MOVQ b_base+16(FP), DI - MOVQ b_len+24(FP), DX - LEAQ ret+32(FP), R9 -#endif JMP cmpbody<>(SB) // input: @@ -47,12 +32,8 @@ TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 // DI = b // BX = alen // DX = blen -#ifndef GOEXPERIMENT_regabiargs -// R9 = address of output word (stores -1/0/1 here) -#else // output: // AX = output (-1/0/1) -#endif TEXT cmpbody<>(SB),NOSPLIT,$0-0 CMPQ SI, DI JEQ allsame @@ -64,9 +45,13 @@ TEXT cmpbody<>(SB),NOSPLIT,$0-0 CMPQ R8, $63 JBE loop +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JEQ big_loop_avx2 JMP big_loop +#else + JMP big_loop_avx2 +#endif loop: CMPQ R8, $16 JBE _0through16 @@ -100,9 +85,6 @@ diff16: CMPB CX, (DI)(BX*1) SETHI AX LEAQ -1(AX*2), AX // convert 1/0 to +1/-1 -#ifndef GOEXPERIMENT_regabiargs - MOVQ AX, (R9) -#endif RET // 0 through 16 bytes left, alen>=8, blen>=8 @@ -128,9 +110,6 @@ diff8: SHRQ CX, AX // move a's bit to bottom ANDQ $1, AX // mask bit LEAQ -1(AX*2), AX // 1/0 => +1/-1 -#ifndef GOEXPERIMENT_regabiargs - MOVQ AX, (R9) -#endif RET // 0-7 bytes in common @@ -169,9 +148,6 @@ di_finish: SHRQ CX, SI // move a's bit to bottom ANDQ $1, SI // mask bit LEAQ -1(SI*2), AX // 1/0 => +1/-1 -#ifndef GOEXPERIMENT_regabiargs - MOVQ AX, (R9) -#endif RET allsame: @@ -181,12 +157,10 @@ allsame: SETGT AX // 1 if alen > blen SETEQ CX // 1 if alen == blen LEAQ -1(CX)(AX*2), AX // 1,0,-1 result -#ifndef GOEXPERIMENT_regabiargs - MOVQ AX, (R9) -#endif RET // this works for >= 64 bytes of data. +#ifndef hasAVX2 big_loop: MOVOU (SI), X0 MOVOU (DI), X1 @@ -222,6 +196,7 @@ big_loop: CMPQ R8, $64 JBE loop JMP big_loop +#endif // Compare 64-bytes per loop iteration. // Loop is unrolled and uses AVX2. diff --git a/src/internal/bytealg/compare_arm64.s b/src/internal/bytealg/compare_arm64.s index 56d56f241eb869..cc02c464e8b274 100644 --- a/src/internal/bytealg/compare_arm64.s +++ b/src/internal/bytealg/compare_arm64.s @@ -5,65 +5,67 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 - MOVD a_base+0(FP), R2 - MOVD a_len+8(FP), R0 - MOVD b_base+24(FP), R3 - MOVD b_len+32(FP), R1 - MOVD $ret+48(FP), R7 +TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 + // R0 = a_base (want in R0) + // R1 = a_len (want in R1) + // R2 = a_cap (unused) + // R3 = b_base (want in R2) + // R4 = b_len (want in R3) + // R5 = b_cap (unused) + MOVD R3, R2 + MOVD R4, R3 B cmpbody<>(SB) -TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 - MOVD a_base+0(FP), R2 - MOVD a_len+8(FP), R0 - MOVD b_base+16(FP), R3 - MOVD b_len+24(FP), R1 - MOVD $ret+32(FP), R7 +TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 + // R0 = a_base + // R1 = a_len + // R2 = b_base + // R3 = b_len B cmpbody<>(SB) // On entry: -// R0 is the length of a -// R1 is the length of b -// R2 points to the start of a -// R3 points to the start of b -// R7 points to return value (-1/0/1 will be written here) +// R0 points to the start of a +// R1 is the length of a +// R2 points to the start of b +// R3 is the length of b // // On exit: +// R0 is the result // R4, R5, R6, R8, R9 and R10 are clobbered TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0 - CMP R2, R3 + CMP R0, R2 BEQ samebytes // same starting pointers; compare lengths - CMP R0, R1 - CSEL LT, R1, R0, R6 // R6 is min(R0, R1) + CMP R1, R3 + CSEL LT, R3, R1, R6 // R6 is min(R1, R3) CBZ R6, samebytes BIC $0xf, R6, R10 CBZ R10, small // length < 16 - ADD R2, R10 // end of chunk16 + ADD R0, R10 // end of chunk16 // length >= 16 chunk16_loop: - LDP.P 16(R2), (R4, R8) - LDP.P 16(R3), (R5, R9) + LDP.P 16(R0), (R4, R8) + LDP.P 16(R2), (R5, R9) CMP R4, R5 BNE cmp CMP R8, R9 BNE cmpnext - CMP R10, R2 + CMP R10, R0 BNE chunk16_loop AND $0xf, R6, R6 CBZ R6, samebytes SUBS $8, R6 BLT tail // the length of tail > 8 bytes - MOVD.P 8(R2), R4 - MOVD.P 8(R3), R5 + MOVD.P 8(R0), R4 + MOVD.P 8(R2), R5 CMP R4, R5 BNE cmp SUB $8, R6 // compare last 8 bytes tail: - MOVD (R2)(R6), R4 - MOVD (R3)(R6), R5 + MOVD (R0)(R6), R4 + MOVD (R2)(R6), R5 CMP R4, R5 BEQ samebytes cmp: @@ -71,52 +73,50 @@ cmp: REV R5, R5 CMP R4, R5 ret: - MOVD $1, R4 - CNEG HI, R4, R4 - MOVD R4, (R7) + MOVD $1, R0 + CNEG HI, R0, R0 RET small: TBZ $3, R6, lt_8 - MOVD (R2), R4 - MOVD (R3), R5 + MOVD (R0), R4 + MOVD (R2), R5 CMP R4, R5 BNE cmp SUBS $8, R6 BEQ samebytes + ADD $8, R0 ADD $8, R2 - ADD $8, R3 SUB $8, R6 B tail lt_8: TBZ $2, R6, lt_4 - MOVWU (R2), R4 - MOVWU (R3), R5 + MOVWU (R0), R4 + MOVWU (R2), R5 CMPW R4, R5 BNE cmp SUBS $4, R6 BEQ samebytes + ADD $4, R0 ADD $4, R2 - ADD $4, R3 lt_4: TBZ $1, R6, lt_2 - MOVHU (R2), R4 - MOVHU (R3), R5 + MOVHU (R0), R4 + MOVHU (R2), R5 CMPW R4, R5 BNE cmp + ADD $2, R0 ADD $2, R2 - ADD $2, R3 lt_2: TBZ $0, R6, samebytes one: - MOVBU (R2), R4 - MOVBU (R3), R5 + MOVBU (R0), R4 + MOVBU (R2), R5 CMPW R4, R5 BNE ret samebytes: - CMP R1, R0 - CSET NE, R4 - CNEG LO, R4, R4 - MOVD R4, (R7) + CMP R3, R1 + CSET NE, R0 + CNEG LO, R0, R0 RET cmpnext: REV R8, R4 diff --git a/src/internal/bytealg/compare_generic.go b/src/internal/bytealg/compare_generic.go index 0690d0cf31c51e..b04e2750611bf0 100644 --- a/src/internal/bytealg/compare_generic.go +++ b/src/internal/bytealg/compare_generic.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !ppc64 && !ppc64le && !mips && !mipsle && !wasm && !mips64 && !mips64le -// +build !386,!amd64,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!wasm,!mips64,!mips64le +//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !loong64 && !ppc64 && !ppc64le && !mips && !mipsle && !wasm && !mips64 && !mips64le && !riscv64 package bytealg diff --git a/src/internal/bytealg/compare_loong64.s b/src/internal/bytealg/compare_loong64.s new file mode 100644 index 00000000000000..54c2daba69bd80 --- /dev/null +++ b/src/internal/bytealg/compare_loong64.s @@ -0,0 +1,86 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "textflag.h" + +TEXT ·Compare(SB),NOSPLIT,$0-56 + MOVV a_base+0(FP), R6 + MOVV b_base+24(FP), R7 + MOVV a_len+8(FP), R4 + MOVV b_len+32(FP), R5 + MOVV $ret+48(FP), R13 + JMP cmpbody<>(SB) + +TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 + MOVV a_base+0(FP), R6 + MOVV b_base+16(FP), R7 + MOVV a_len+8(FP), R4 + MOVV b_len+24(FP), R5 + MOVV $ret+32(FP), R13 + JMP cmpbody<>(SB) + +// On entry: +// R4 length of a +// R5 length of b +// R6 points to the start of a +// R7 points to the start of b +// R13 points to the return value (-1/0/1) +TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0 + BEQ R6, R7, samebytes // same start of a and b + + SGTU R4, R5, R9 + BNE R0, R9, r2_lt_r1 + MOVV R4, R14 + JMP entry +r2_lt_r1: + MOVV R5, R14 // R14 is min(R4, R5) +entry: + ADDV R6, R14, R12 // R6 start of a, R14 end of a + BEQ R6, R12, samebytes // length is 0 + + SRLV $4, R14 // R14 is number of chunks + BEQ R0, R14, byte_loop + + // make sure both a and b are aligned. + OR R6, R7, R15 + AND $7, R15 + BNE R0, R15, byte_loop + +chunk16_loop: + BEQ R0, R14, byte_loop + MOVV (R6), R8 + MOVV (R7), R9 + BNE R8, R9, byte_loop + MOVV 8(R6), R16 + MOVV 8(R7), R17 + ADDV $16, R6 + ADDV $16, R7 + SUBVU $1, R14 + BEQ R16, R17, chunk16_loop + SUBV $8, R6 + SUBV $8, R7 + +byte_loop: + BEQ R6, R12, samebytes + MOVBU (R6), R8 + ADDVU $1, R6 + MOVBU (R7), R9 + ADDVU $1, R7 + BEQ R8, R9, byte_loop + +byte_cmp: + SGTU R8, R9, R12 // R12 = 1 if (R8 > R9) + BNE R0, R12, ret + MOVV $-1, R12 + JMP ret + +samebytes: + SGTU R4, R5, R8 + SGTU R5, R4, R9 + SUBV R9, R8, R12 + +ret: + MOVV R12, (R13) + RET diff --git a/src/internal/bytealg/compare_mips64x.s b/src/internal/bytealg/compare_mips64x.s index b472e510bce7a5..117a9ef631fd2b 100644 --- a/src/internal/bytealg/compare_mips64x.s +++ b/src/internal/bytealg/compare_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/compare_mipsx.s b/src/internal/bytealg/compare_mipsx.s index dcc4916e567141..857ac1338906f4 100644 --- a/src/internal/bytealg/compare_mipsx.s +++ b/src/internal/bytealg/compare_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/compare_native.go b/src/internal/bytealg/compare_native.go index baa188ff7af553..34964e281c637c 100644 --- a/src/internal/bytealg/compare_native.go +++ b/src/internal/bytealg/compare_native.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build 386 || amd64 || s390x || arm || arm64 || ppc64 || ppc64le || mips || mipsle || wasm || mips64 || mips64le -// +build 386 amd64 s390x arm arm64 ppc64 ppc64le mips mipsle wasm mips64 mips64le +//go:build 386 || amd64 || s390x || arm || arm64 || loong64 || ppc64 || ppc64le || mips || mipsle || wasm || mips64 || mips64le || riscv64 package bytealg diff --git a/src/internal/bytealg/compare_ppc64x.s b/src/internal/bytealg/compare_ppc64x.s index 83444fa826008b..cbe0525af55d40 100644 --- a/src/internal/bytealg/compare_ppc64x.s +++ b/src/internal/bytealg/compare_ppc64x.s @@ -3,276 +3,500 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "textflag.h" -TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 - MOVD a_base+0(FP), R5 - MOVD b_base+24(FP), R6 - MOVD a_len+8(FP), R3 - CMP R5,R6,CR7 - MOVD b_len+32(FP), R4 - MOVD $ret+48(FP), R7 +TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 + // incoming: + // R3 a addr -> R5 + // R4 a len -> R3 + // R5 a cap unused + // R6 b addr -> R6 + // R7 b len -> R4 + // R8 b cap unused + MOVD R3, R5 + MOVD R4, R3 + MOVD R7, R4 + CMP R5,R6,CR7 CMP R3,R4,CR6 BEQ CR7,equal - -#ifdef GOARCH_ppc64le - BR cmpbodyLE<>(SB) -#else - BR cmpbodyBE<>(SB) -#endif - + MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16 + CMP R16,$1 + BNE power8 + BR cmpbodyp9<>(SB) +power8: + BR cmpbody<>(SB) equal: BEQ CR6,done MOVD $1, R8 BGT CR6,greater NEG R8 - greater: - MOVD R8, (R7) + MOVD R8, R3 RET - done: - MOVD $0, (R7) + MOVD $0, R3 RET -TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 - MOVD a_base+0(FP), R5 - MOVD b_base+16(FP), R6 - MOVD a_len+8(FP), R3 - CMP R5,R6,CR7 - MOVD b_len+24(FP), R4 - MOVD $ret+32(FP), R7 +TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 + // incoming: + // R3 a addr -> R5 + // R4 a len -> R3 + // R5 b addr -> R6 + // R6 b len -> R4 + MOVD R6, R7 + MOVD R5, R6 + MOVD R3, R5 + MOVD R4, R3 + MOVD R7, R4 + CMP R5,R6,CR7 CMP R3,R4,CR6 BEQ CR7,equal - -#ifdef GOARCH_ppc64le - BR cmpbodyLE<>(SB) -#else - BR cmpbodyBE<>(SB) -#endif - + MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16 + CMP R16,$1 + BNE power8 + BR cmpbodyp9<>(SB) +power8: + BR cmpbody<>(SB) equal: BEQ CR6,done MOVD $1, R8 BGT CR6,greater NEG R8 - greater: - MOVD R8, (R7) + MOVD R8, R3 RET done: - MOVD $0, (R7) + MOVD $0, R3 RET -// Do an efficient memcmp for ppc64le +#ifdef GOARCH_ppc64le +DATA byteswap<>+0(SB)/8, $0x0706050403020100 +DATA byteswap<>+8(SB)/8, $0x0f0e0d0c0b0a0908 +GLOBL byteswap<>+0(SB), RODATA, $16 +#define SWAP V21 +#endif + +// Do an efficient memcmp for ppc64le/ppc64/POWER8 // R3 = a len // R4 = b len // R5 = a addr // R6 = b addr -// R7 = addr of return value -TEXT cmpbodyLE<>(SB),NOSPLIT|NOFRAME,$0-0 +// On exit: +// R3 = return value +TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0 MOVD R3,R8 // set up length CMP R3,R4,CR2 // unequal? - BC 12,8,setuplen // BLT CR2 + BLT CR2,setuplen // BLT CR2 MOVD R4,R8 // use R4 for comparison len setuplen: - MOVD R8,CTR // set up loop counter - CMP R8,$8 // only optimize >=8 - BLT simplecheck - DCBT (R5) // cache hint - DCBT (R6) CMP R8,$32 // optimize >= 32 MOVD R8,R9 - BLT setup8a // 8 byte moves only -setup32a: - SRADCC $5,R8,R9 // number of 32 byte chunks - MOVD R9,CTR - - // Special processing for 32 bytes or longer. - // Loading this way is faster and correct as long as the - // doublewords being compared are equal. Once they - // are found unequal, reload them in proper byte order - // to determine greater or less than. -loop32a: - MOVD 0(R5),R9 // doublewords to compare - MOVD 0(R6),R10 // get 4 doublewords - MOVD 8(R5),R14 - MOVD 8(R6),R15 - CMPU R9,R10 // bytes equal? - MOVD $0,R16 // set up for cmpne - BNE cmpne // further compare for LT or GT - MOVD 16(R5),R9 // get next pair of doublewords - MOVD 16(R6),R10 - CMPU R14,R15 // bytes match? - MOVD $8,R16 // set up for cmpne - BNE cmpne // further compare for LT or GT - MOVD 24(R5),R14 // get next pair of doublewords - MOVD 24(R6),R15 - CMPU R9,R10 // bytes match? - MOVD $16,R16 // set up for cmpne - BNE cmpne // further compare for LT or GT - MOVD $-8,R16 // for cmpne, R5,R6 already inc by 32 - ADD $32,R5 // bump up to next 32 - ADD $32,R6 - CMPU R14,R15 // bytes match? - BC 8,2,loop32a // br ctr and cr - BNE cmpne + BLT setup8a // optimize < 32 + MOVD $16,R10 // set offsets to load into vectors + CMP R8,$64 + BLT cmp32 // process size 32-63 + + DCBT (R5) // optimize >= 64 + DCBT (R6) // cache hint + MOVD $32,R11 // set offsets to load into vector + MOVD $48,R12 // set offsets to load into vector + +loop64a:// process size 64 and greater + LXVD2X (R5)(R0),V3 // load bytes of A at offset 0 into vector + LXVD2X (R6)(R0),V4 // load bytes of B at offset 0 into vector + VCMPEQUDCC V3,V4,V1 + BGE CR6,different // jump out if its different + + LXVD2X (R5)(R10),V3 // load bytes of A at offset 16 into vector + LXVD2X (R6)(R10),V4 // load bytes of B at offset 16 into vector + + VCMPEQUDCC V3,V4,V1 + BGE CR6,different + + LXVD2X (R5)(R11),V3 // load bytes of A at offset 32 into vector + LXVD2X (R6)(R11),V4 // load bytes of B at offset 32 into vector + + VCMPEQUDCC V3,V4,V1 + BGE CR6,different + + LXVD2X (R5)(R12),V3 // load bytes of A at offset 64 into vector + LXVD2X (R6)(R12),V4 // load bytes of B at offset 64 into vector + + VCMPEQUDCC V3,V4,V1 + BGE CR6,different + + ADD $-64,R9,R9 // reduce remaining size by 64 + ADD $64,R5,R5 // increment to next 64 bytes of A + ADD $64,R6,R6 // increment to next 64 bytes of B + CMPU R9,$64 + BGE loop64a // loop back to loop64a only if there are >= 64 bytes remaining + + CMPU R9,$32 + BGE cmp32 // loop to cmp32 if there are 32-64 bytes remaining + CMPU R9,$0 + BNE rem // loop to rem if the remainder is not 0 + + BEQ CR2,equal // remainder is zero, jump to equal if len(A)==len(B) + BLT CR2,less // jump to less if len(A)+00(SB), R16 + LXVD2X (R16)(R0),SWAP // Set up swap string + + VPERM V3,V3,SWAP,V3 + VPERM V4,V4,SWAP,V4 +#endif + MFVSRD VS35,R16 // move upper doublwords of A and B into GPR for comparison + MFVSRD VS36,R10 + + CMPU R16,R10 + BEQ lower + BGT greater + MOVD $-1,R3 // return value if A < B + RET +lower: + VSLDOI $8,V3,V3,V3 // move lower doublwords of A and B into GPR for comparison + MFVSRD VS35,R16 + VSLDOI $8,V4,V4,V4 + MFVSRD VS36,R10 + + CMPU R16,R10 + BGT greater + MOVD $-1,R3 // return value if A < B + RET setup8a: - SRADCC $3,R9,R9 // get the 8 byte count + SRADCC $3,R8,R9 // get the 8 byte count BEQ leftover // shifted value is 0 + CMPU R8,$8 // optimize 8byte move + BEQ size8 + CMPU R8,$16 + BEQ size16 MOVD R9,CTR // loop count for doublewords loop8: - MOVDBR (R5+R0),R9 // doublewords to compare +#ifdef GOARCH_ppc64le + MOVDBR (R5+R0),R16 // doublewords to compare MOVDBR (R6+R0),R10 // LE compare order +#else + MOVD (R5+R0),R16 // doublewords to compare + MOVD (R6+R0),R10 // BE compare order +#endif ADD $8,R5 ADD $8,R6 - CMPU R9,R10 // match? + CMPU R16,R10 // match? BC 8,2,loop8 // bt ctr <> 0 && cr BGT greater BLT less leftover: ANDCC $7,R8,R9 // check for leftover bytes - MOVD R9,CTR // save the ctr - BNE simple // leftover bytes - BC 12,10,equal // test CR2 for length comparison - BC 12,8,less - BR greater + BEQ zeroremainder simplecheck: - CMP R8,$0 // remaining compare length 0 - BNE simple // do simple compare - BC 12,10,equal // test CR2 for length comparison - BC 12,8,less // 1st len < 2nd len, result less - BR greater // 1st len > 2nd len must be greater -simple: - MOVBZ 0(R5), R9 // get byte from 1st operand - ADD $1,R5 - MOVBZ 0(R6), R10 // get byte from 2nd operand - ADD $1,R6 - CMPU R9, R10 - BC 8,2,simple // bc ctr <> 0 && cr - BGT greater // 1st > 2nd - BLT less // 1st < 2nd - BC 12,10,equal // test CR2 for length comparison - BC 12,9,greater // 2nd len > 1st len - BR less // must be less -cmpne: // only here is not equal - MOVDBR (R5+R16),R8 // reload in reverse order - MOVDBR (R6+R16),R9 - CMPU R8,R9 // compare correct endianness - BGT greater // here only if NE -less: - MOVD $-1,R3 - MOVD R3,(R7) // return value if A < B + MOVD R0,R14 + CMP R9,$4 // process 4 bytes + BLT halfword +#ifdef GOARCH_ppc64le + MOVWBR (R5)(R14),R10 + MOVWBR (R6)(R14),R11 +#else + MOVWZ (R5)(R14),R10 + MOVWZ (R6)(R14),R11 +#endif + CMPU R10,R11 + BGT greater + BLT less + ADD $-4,R9 + ADD $4,R14 + PCALIGN $16 + +halfword: + CMP R9,$2 // process 2 bytes + BLT byte +#ifdef GOARCH_ppc64le + MOVHBR (R5)(R14),R10 + MOVHBR (R6)(R14),R11 +#else + MOVHZ (R5)(R14),R10 + MOVHZ (R6)(R14),R11 +#endif + CMPU R10,R11 + BGT greater + BLT less + ADD $-2,R9 + ADD $2,R14 + PCALIGN $16 +byte: + CMP R9,$0 // process 1 byte + BEQ skip + MOVBZ (R5)(R14),R10 + MOVBZ (R6)(R14),R11 + CMPU R10,R11 + BGT greater + BLT less + PCALIGN $16 +skip: + BEQ CR2,equal + BGT CR2,greater + +less: MOVD $-1,R3 // return value if A < B RET +size16: + LXVD2X (R5)(R0),V3 // load bytes of A at offset 0 into vector + LXVD2X (R6)(R0),V4 // load bytes of B at offset 0 into vector + VCMPEQUDCC V3,V4,V1 + BGE CR6,different +zeroremainder: + BEQ CR2,equal // remainder is zero, jump to equal if len(A)==len(B) + BLT CR2,less // jump to less if len(A) 1st len + BLT CR2,less // 2nd len < 1st len equal: - MOVD $0,(R7) // return value if A == B + MOVD $0, R3 // return value if A == B RET greater: - MOVD $1,R3 - MOVD R3,(R7) // return value if A > B + MOVD $1,R3 // return value if A > B RET -// Do an efficient memcmp for ppc64 (BE) +// Do an efficient memcmp for ppc64le/ppc64/POWER9 // R3 = a len // R4 = b len // R5 = a addr // R6 = b addr -// R7 = addr of return value -TEXT cmpbodyBE<>(SB),NOSPLIT|NOFRAME,$0-0 +// On exit: +// R3 = return value +TEXT cmpbodyp9<>(SB),NOSPLIT|NOFRAME,$0-0 MOVD R3,R8 // set up length CMP R3,R4,CR2 // unequal? - BC 12,8,setuplen // BLT CR2 + BLT CR2,setuplen // BLT CR2 MOVD R4,R8 // use R4 for comparison len setuplen: - MOVD R8,CTR // set up loop counter - CMP R8,$8 // only optimize >=8 - BLT simplecheck - DCBT (R5) // cache hint - DCBT (R6) - CMP R8,$32 // optimize >= 32 + CMP R8,$16 // optimize for size<16 MOVD R8,R9 - BLT setup8a // 8 byte moves only - -setup32a: - SRADCC $5,R8,R9 // number of 32 byte chunks - MOVD R9,CTR -loop32a: - MOVD 0(R5),R9 // doublewords to compare - MOVD 0(R6),R10 // get 4 doublewords - MOVD 8(R5),R14 - MOVD 8(R6),R15 - CMPU R9,R10 // bytes equal? - BLT less // found to be less - BGT greater // found to be greater - MOVD 16(R5),R9 // get next pair of doublewords - MOVD 16(R6),R10 - CMPU R14,R15 // bytes match? - BLT less // found less - BGT greater // found greater - MOVD 24(R5),R14 // get next pair of doublewords - MOVD 24(R6),R15 - CMPU R9,R10 // bytes match? - BLT less // found to be less - BGT greater // found to be greater - ADD $32,R5 // bump up to next 32 - ADD $32,R6 - CMPU R14,R15 // bytes match? - BC 8,2,loop32a // br ctr and cr - BLT less // with BE, byte ordering is - BGT greater // good for compare - ANDCC $24,R8,R9 // Any 8 byte chunks? - BEQ leftover // and result is 0 -setup8a: - SRADCC $3,R9,R9 // get the 8 byte count - BEQ leftover // shifted value is 0 - MOVD R9,CTR // loop count for doublewords -loop8: - MOVD (R5),R9 - MOVD (R6),R10 - ADD $8,R5 - ADD $8,R6 - CMPU R9,R10 // match? - BC 8,2,loop8 // bt ctr <> 0 && cr + BLT simplecheck + MOVD $16,R10 // set offsets to load into vectors + CMP R8,$32 // optimize for size 16-31 + BLT cmp16 + CMP R8,$64 + BLT cmp32 // optimize for size 32-63 + DCBT (R5) // optimize for size>=64 + DCBT (R6) // cache hint + + MOVD $32,R11 // set offsets to load into vector + MOVD $48,R12 // set offsets to load into vector + +loop64a:// process size 64 and greater + LXVB16X (R0)(R5),V3 // load bytes of A at offset 0 into vector + LXVB16X (R0)(R6),V4 // load bytes of B at offset 0 into vector + VCMPNEBCC V3,V4,V1 // record comparison into V1 + BNE CR6,different // jump out if its different + + LXVB16X (R10)(R5),V3 // load bytes of A at offset 16 into vector + LXVB16X (R10)(R6),V4 // load bytes of B at offset 16 into vector + VCMPNEBCC V3,V4,V1 + BNE CR6,different + + LXVB16X (R11)(R5),V3 // load bytes of A at offset 32 into vector + LXVB16X (R11)(R6),V4 // load bytes of B at offset 32 into vector + VCMPNEBCC V3,V4,V1 + BNE CR6,different + + LXVB16X (R12)(R5),V3 // load bytes of A at offset 48 into vector + LXVB16X (R12)(R6),V4 // load bytes of B at offset 48 into vector + VCMPNEBCC V3,V4,V1 + BNE CR6,different + + ADD $-64,R9,R9 // reduce remaining size by 64 + ADD $64,R5,R5 // increment to next 64 bytes of A + ADD $64,R6,R6 // increment to next 64 bytes of B + CMPU R9,$64 + BGE loop64a // loop back to loop64a only if there are >= 64 bytes remaining + + CMPU R9,$32 + BGE cmp32 // loop to cmp32 if there are 32-64 bytes remaining + CMPU R9,$16 + BGE cmp16 // loop to cmp16 if there are 16-31 bytes left + CMPU R9,$0 + BNE simplecheck // loop to simplecheck for remaining bytes + + BEQ CR2,equal // remainder is zero, jump to equal if len(A)==len(B) + BLT CR2,less // jump to less if len(A) B + RET +cmp16: + ANDCC $16,R9,R31 + BEQ tail + + LXVB16X (R0)(R5),V3 // load bytes of A at offset 16 into vector + LXVB16X (R0)(R6),V4 // load bytes of B at offset 16 into vector + VCMPEQUDCC V3,V4,V1 + BGE CR6,different + + ADD $16,R5 + ADD $16,R6 +tail: + ANDCC $15,R9 // Load the last 16 bytes (we know there are at least 32b) + BEQ end + + ADD R9,R5 + ADD R9,R6 + MOVD $-16,R10 + + LXVB16X (R10)(R5),V3 // load bytes of A at offset 16 into vector + LXVB16X (R10)(R6),V4 // load bytes of B at offset 16 into vector + VCMPEQUDCC V3,V4,V1 + BGE CR6,different +end: + BEQ CR2,equal // remainder is zero, jump to equal if len(A)==len(B) + BLT CR2,less // jump to less if BLT CR2 that is, len(A) 0 && cr - BGT greater // 1st > 2nd - BLT less // 1st < 2nd - BC 12,10,equal // test CR2 for length comparison - BC 12,9,greater // 2nd len > 1st len + MOVD $0,R14 // process 8 bytes + CMP R9,$8 + BLT word +#ifdef GOARCH_ppc64le + MOVDBR (R5+R14),R10 + MOVDBR (R6+R14),R11 +#else + MOVD (R5+R14),R10 + MOVD (R6+R14),R11 +#endif + CMPU R10,R11 + BGT greater + BLT less + ADD $8,R14 + ADD $-8,R9 + PCALIGN $16 +word: + CMP R9,$4 // process 4 bytes + BLT halfword +#ifdef GOARCH_ppc64le + MOVWBR (R5+R14),R10 + MOVWBR (R6+R14),R11 +#else + MOVWZ (R5+R14),R10 + MOVWZ (R6+R14),R11 +#endif + CMPU R10,R11 + BGT greater + BLT less + ADD $4,R14 + ADD $-4,R9 + PCALIGN $16 +halfword: + CMP R9,$2 // process 2 bytes + BLT byte +#ifdef GOARCH_ppc64le + MOVHBR (R5+R14),R10 + MOVHBR (R6+R14),R11 +#else + MOVHZ (R5+R14),R10 + MOVHZ (R6+R14),R11 +#endif + CMPU R10,R11 + BGT greater + BLT less + ADD $2,R14 + ADD $-2,R9 + PCALIGN $16 +byte: + CMP R9,$0 // process 1 byte + BEQ skip + MOVBZ (R5+R14),R10 + MOVBZ (R6+R14),R11 + CMPU R10,R11 + BGT greater + BLT less + PCALIGN $16 +skip: + BEQ CR2,equal + BGT CR2,greater less: - MOVD $-1,R3 - MOVD R3,(R7) // return value if A < B + MOVD $-1,R3 // return value if A < B RET equal: - MOVD $0,(R7) // return value if A == B - RET -greater: - MOVD $1,R3 - MOVD R3,(R7) // return value if A > B + MOVD $0, R3 // return value if A == B RET diff --git a/src/internal/bytealg/compare_riscv64.s b/src/internal/bytealg/compare_riscv64.s new file mode 100644 index 00000000000000..7d2f8d6d0b810c --- /dev/null +++ b/src/internal/bytealg/compare_riscv64.s @@ -0,0 +1,205 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "textflag.h" + +TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 +#ifndef GOEXPERIMENT_regabiargs + MOV a_base+0(FP), X10 + MOV a_len+8(FP), X11 + MOV b_base+24(FP), X12 + MOV b_len+32(FP), X13 + MOV $ret+48(FP), X14 +#else + // X10 = a_base + // X11 = a_len + // X12 = a_cap (unused) + // X13 = b_base (want in X12) + // X14 = b_len (want in X13) + // X15 = b_cap (unused) + MOV X13, X12 + MOV X14, X13 +#endif + JMP compare<>(SB) + +TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 +#ifndef GOEXPERIMENT_regabiargs + MOV a_base+0(FP), X10 + MOV a_len+8(FP), X11 + MOV b_base+16(FP), X12 + MOV b_len+24(FP), X13 + MOV $ret+32(FP), X14 +#endif + // X10 = a_base + // X11 = a_len + // X12 = b_base + // X13 = b_len + JMP compare<>(SB) + +// On entry: +// X10 points to start of a +// X11 length of a +// X12 points to start of b +// X13 length of b +// for non-regabi X14 points to the address to store the return value (-1/0/1) +// for regabi the return value in X10 +TEXT compare<>(SB),NOSPLIT|NOFRAME,$0 + BEQ X10, X12, cmp_len + + MOV X11, X5 + BGE X13, X5, use_a_len // X5 = min(len(a), len(b)) + MOV X13, X5 +use_a_len: + BEQZ X5, cmp_len + + MOV $32, X6 + BLT X5, X6, loop4_check + + // Check alignment - if alignment differs we have to do one byte at a time. + AND $3, X10, X7 + AND $3, X12, X8 + BNE X7, X8, loop4_check + BEQZ X7, loop32_check + + // Check one byte at a time until we reach 8 byte alignment. + SUB X7, X5, X5 +align: + ADD $-1, X7 + MOVBU 0(X10), X8 + MOVBU 0(X12), X9 + BNE X8, X9, cmp + ADD $1, X10 + ADD $1, X12 + BNEZ X7, align + +loop32_check: + MOV $32, X7 + BLT X5, X7, loop16_check +loop32: + MOV 0(X10), X15 + MOV 0(X12), X16 + MOV 8(X10), X17 + MOV 8(X12), X18 + BEQ X15, X16, loop32a + JMP cmp8a +loop32a: + BEQ X17, X18, loop32b + JMP cmp8b +loop32b: + MOV 16(X10), X15 + MOV 16(X12), X16 + MOV 24(X10), X17 + MOV 24(X12), X18 + BEQ X15, X16, loop32c + JMP cmp8a +loop32c: + BEQ X17, X18, loop32d + JMP cmp8b +loop32d: + ADD $32, X10 + ADD $32, X12 + ADD $-32, X5 + BGE X5, X7, loop32 + BEQZ X5, cmp_len + +loop16_check: + MOV $16, X6 + BLT X5, X6, loop4_check +loop16: + MOV 0(X10), X15 + MOV 0(X12), X16 + MOV 8(X10), X17 + MOV 8(X12), X18 + BEQ X15, X16, loop16a + JMP cmp8a +loop16a: + BEQ X17, X18, loop16b + JMP cmp8b +loop16b: + ADD $16, X10 + ADD $16, X12 + ADD $-16, X5 + BGE X5, X6, loop16 + BEQZ X5, cmp_len + +loop4_check: + MOV $4, X6 + BLT X5, X6, loop1 +loop4: + MOVBU 0(X10), X8 + MOVBU 0(X12), X9 + MOVBU 1(X10), X15 + MOVBU 1(X12), X16 + BEQ X8, X9, loop4a + SLTU X9, X8, X5 + SLTU X8, X9, X6 + JMP cmp_ret +loop4a: + BEQ X15, X16, loop4b + SLTU X16, X15, X5 + SLTU X15, X16, X6 + JMP cmp_ret +loop4b: + MOVBU 2(X10), X21 + MOVBU 2(X12), X22 + MOVBU 3(X10), X23 + MOVBU 3(X12), X24 + BEQ X21, X22, loop4c + SLTU X22, X21, X5 + SLTU X21, X22, X6 + JMP cmp_ret +loop4c: + BEQ X23, X24, loop4d + SLTU X24, X23, X5 + SLTU X23, X24, X6 + JMP cmp_ret +loop4d: + ADD $4, X10 + ADD $4, X12 + ADD $-4, X5 + BGE X5, X6, loop4 + +loop1: + BEQZ X5, cmp_len + MOVBU 0(X10), X8 + MOVBU 0(X12), X9 + BNE X8, X9, cmp + ADD $1, X10 + ADD $1, X12 + ADD $-1, X5 + JMP loop1 + + // Compare 8 bytes of memory in X15/X16 that are known to differ. +cmp8a: + MOV $0xff, X19 +cmp8a_loop: + AND X15, X19, X8 + AND X16, X19, X9 + BNE X8, X9, cmp + SLLI $8, X19 + JMP cmp8a_loop + + // Compare 8 bytes of memory in X17/X18 that are known to differ. +cmp8b: + MOV $0xff, X19 +cmp8b_loop: + AND X17, X19, X8 + AND X18, X19, X9 + BNE X8, X9, cmp + SLLI $8, X19 + JMP cmp8b_loop + +cmp_len: + MOV X11, X8 + MOV X13, X9 +cmp: + SLTU X9, X8, X5 + SLTU X8, X9, X6 +cmp_ret: + SUB X5, X6, X10 +#ifndef GOEXPERIMENT_regabiargs + MOV X10, (X14) +#endif + RET diff --git a/src/internal/bytealg/count_amd64.s b/src/internal/bytealg/count_amd64.s index fa864c4c76631d..efb17f84b776a0 100644 --- a/src/internal/bytealg/count_amd64.s +++ b/src/internal/bytealg/count_amd64.s @@ -3,12 +3,15 @@ // license that can be found in the LICENSE file. #include "go_asm.h" +#include "asm_amd64.h" #include "textflag.h" TEXT ·Count(SB),NOSPLIT,$0-40 +#ifndef hasPOPCNT CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 JEQ 2(PC) JMP ·countGeneric(SB) +#endif MOVQ b_base+0(FP), SI MOVQ b_len+8(FP), BX MOVB c+24(FP), AL @@ -16,9 +19,11 @@ TEXT ·Count(SB),NOSPLIT,$0-40 JMP countbody<>(SB) TEXT ·CountString(SB),NOSPLIT,$0-32 +#ifndef hasPOPCNT CMPB internal∕cpu·X86+const_offsetX86HasPOPCNT(SB), $1 JEQ 2(PC) JMP ·countGenericString(SB) +#endif MOVQ s_base+0(FP), SI MOVQ s_len+8(FP), BX MOVB c+16(FP), AL @@ -151,8 +156,10 @@ endofpage: RET avx2: +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JNE sse +#endif MOVD AX, X0 LEAQ -32(SI)(BX*1), R11 VPBROADCASTB X0, Y1 diff --git a/src/internal/bytealg/count_generic.go b/src/internal/bytealg/count_generic.go index 1891d29b24cb4a..932a7c584c1ae6 100644 --- a/src/internal/bytealg/count_generic.go +++ b/src/internal/bytealg/count_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 && !arm && !arm64 && !ppc64le && !ppc64 && !riscv64 && !s390x -// +build !amd64,!arm,!arm64,!ppc64le,!ppc64,!riscv64,!s390x package bytealg diff --git a/src/internal/bytealg/count_native.go b/src/internal/bytealg/count_native.go index a19a6f8223fe9e..90189c9fe051bd 100644 --- a/src/internal/bytealg/count_native.go +++ b/src/internal/bytealg/count_native.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || arm || arm64 || ppc64le || ppc64 || riscv64 || s390x -// +build amd64 arm arm64 ppc64le ppc64 riscv64 s390x package bytealg diff --git a/src/internal/bytealg/count_ppc64x.s b/src/internal/bytealg/count_ppc64x.s index 94163cbd8a398d..2d2490b0246a4e 100644 --- a/src/internal/bytealg/count_ppc64x.s +++ b/src/internal/bytealg/count_ppc64x.s @@ -3,29 +3,27 @@ // license that can be found in the LICENSE file. //go:build ppc64le || ppc64 -// +build ppc64le ppc64 #include "go_asm.h" #include "textflag.h" -TEXT ·Count(SB), NOSPLIT|NOFRAME, $0-40 - MOVD b_base+0(FP), R3 // R3 = byte array pointer - MOVD b_len+8(FP), R4 // R4 = length - MOVBZ c+24(FP), R5 // R5 = byte - MOVD $ret+32(FP), R14 // R14 = &ret +TEXT ·Count(SB),NOSPLIT|NOFRAME,$0-40 + // R3 = byte array pointer + // R4 = length + MOVBZ R6, R5 // R5 = byte BR countbytebody<>(SB) -TEXT ·CountString(SB), NOSPLIT|NOFRAME, $0-32 - MOVD s_base+0(FP), R3 // R3 = string - MOVD s_len+8(FP), R4 // R4 = length - MOVBZ c+16(FP), R5 // R5 = byte - MOVD $ret+24(FP), R14 // R14 = &ret +TEXT ·CountString(SB), NOSPLIT|NOFRAME, $0-32 + // R3 = byte array pointer + // R4 = length + MOVBZ R5, R5 // R5 = byte BR countbytebody<>(SB) // R3: addr of string // R4: len of string // R5: byte to count -// R14: addr for return value +// On exit: +// R3: return value // endianness shouldn't matter since we are just counting and order // is irrelevant TEXT countbytebody<>(SB), NOSPLIT|NOFRAME, $0-0 @@ -94,5 +92,5 @@ next2: BR small done: - MOVD R18, (R14) // return count + MOVD R18, R3 // return count RET diff --git a/src/internal/bytealg/count_riscv64.s b/src/internal/bytealg/count_riscv64.s index 3f4eb23286e039..a15d07d768917e 100644 --- a/src/internal/bytealg/count_riscv64.s +++ b/src/internal/bytealg/count_riscv64.s @@ -5,40 +5,62 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·Count(SB),NOSPLIT,$0-40 - MOV b_base+0(FP), A1 - MOV b_len+8(FP), A2 - MOVBU c+24(FP), A3 // byte to count - MOV ZERO, A4 // count - ADD A1, A2 // end +TEXT ·Count(SB),NOSPLIT,$0-40 +#ifndef GOEXPERIMENT_regabiargs + MOV b_base+0(FP), X10 + MOV b_len+8(FP), X11 + MOVBU c+24(FP), X12 // byte to count +#else + // X10 = b_base + // X11 = b_len + // X12 = b_cap (unused) + // X13 = byte to count (want in X12) + AND $0xff, X13, X12 +#endif + MOV ZERO, X14 // count + ADD X10, X11 // end loop: - BEQ A1, A2, done - MOVBU (A1), A5 - ADD $1, A1 - BNE A3, A5, loop - ADD $1, A4 + BEQ X10, X11, done + MOVBU (X10), X15 + ADD $1, X10 + BNE X12, X15, loop + ADD $1, X14 JMP loop done: - MOV A4, ret+32(FP) +#ifndef GOEXPERIMENT_regabiargs + MOV X14, ret+32(FP) +#else + MOV X14, X10 +#endif RET -TEXT ·CountString(SB),NOSPLIT,$0-32 - MOV s_base+0(FP), A1 - MOV s_len+8(FP), A2 - MOVBU c+16(FP), A3 // byte to count - MOV ZERO, A4 // count - ADD A1, A2 // end +TEXT ·CountString(SB),NOSPLIT,$0-32 +#ifndef GOEXPERIMENT_regabiargs + MOV s_base+0(FP), X10 + MOV s_len+8(FP), X11 + MOVBU c+16(FP), X12 // byte to count +#endif + // X10 = s_base + // X11 = s_len + // X12 = byte to count + AND $0xff, X12 + MOV ZERO, X14 // count + ADD X10, X11 // end loop: - BEQ A1, A2, done - MOVBU (A1), A5 - ADD $1, A1 - BNE A3, A5, loop - ADD $1, A4 + BEQ X10, X11, done + MOVBU (X10), X15 + ADD $1, X10 + BNE X12, X15, loop + ADD $1, X14 JMP loop done: - MOV A4, ret+24(FP) +#ifndef GOEXPERIMENT_regabiargs + MOV X14, ret+24(FP) +#else + MOV X14, X10 +#endif RET diff --git a/src/internal/bytealg/equal_386.s b/src/internal/bytealg/equal_386.s index 87233635a927d3..58b3cbe3d07194 100644 --- a/src/internal/bytealg/equal_386.s +++ b/src/internal/bytealg/equal_386.s @@ -43,8 +43,9 @@ TEXT memeqbody<>(SB),NOSPLIT,$0-0 hugeloop: CMPL BX, $64 JB bigloop - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE bigloop +#ifdef GO386_softfloat + JMP bigloop +#endif MOVOU (SI), X0 MOVOU (DI), X1 MOVOU 16(SI), X2 diff --git a/src/internal/bytealg/equal_amd64.s b/src/internal/bytealg/equal_amd64.s index 6f12d2a1690380..d178a3377938d1 100644 --- a/src/internal/bytealg/equal_amd64.s +++ b/src/internal/bytealg/equal_amd64.s @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. #include "go_asm.h" +#include "asm_amd64.h" #include "textflag.h" // memequal(a, b unsafe.Pointer, size uintptr) bool TEXT runtime·memequal(SB),NOSPLIT,$0-25 -#ifdef GOEXPERIMENT_regabiargs // AX = a (want in SI) // BX = b (want in DI) // CX = size (want in BX) @@ -20,22 +20,9 @@ neq: MOVQ BX, DI MOVQ CX, BX JMP memeqbody<>(SB) -#else - MOVQ a+0(FP), SI - MOVQ b+8(FP), DI - CMPQ SI, DI - JEQ eq - MOVQ size+16(FP), BX - LEAQ ret+24(FP), AX - JMP memeqbody<>(SB) -eq: - MOVB $1, ret+24(FP) - RET -#endif // memequal_varlen(a, b unsafe.Pointer) bool TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17 -#ifdef GOEXPERIMENT_regabiargs // AX = a (want in SI) // BX = b (want in DI) // 8(DX) = size (want in BX) @@ -48,34 +35,19 @@ neq: MOVQ BX, DI MOVQ 8(DX), BX // compiler stores size at offset 8 in the closure JMP memeqbody<>(SB) -#else - MOVQ a+0(FP), SI - MOVQ b+8(FP), DI - CMPQ SI, DI - JEQ eq - MOVQ 8(DX), BX // compiler stores size at offset 8 in the closure - LEAQ ret+16(FP), AX - JMP memeqbody<>(SB) -eq: - MOVB $1, ret+16(FP) - RET -#endif // Input: // a in SI // b in DI // count in BX -#ifndef GOEXPERIMENT_regabiargs -// address of result byte in AX -#else // Output: // result in AX -#endif TEXT memeqbody<>(SB),NOSPLIT,$0-0 CMPQ BX, $8 JB small CMPQ BX, $64 JB bigloop +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JE hugeloop_avx2 @@ -104,12 +76,9 @@ hugeloop: SUBQ $64, BX CMPL DX, $0xffff JEQ hugeloop -#ifdef GOEXPERIMENT_regabiargs XORQ AX, AX // return 0 -#else - MOVB $0, (AX) -#endif RET +#endif // 64 bytes at a time using ymm registers hugeloop_avx2: @@ -129,11 +98,7 @@ hugeloop_avx2: CMPL DX, $0xffffffff JEQ hugeloop_avx2 VZEROUPPER -#ifdef GOEXPERIMENT_regabiargs XORQ AX, AX // return 0 -#else - MOVB $0, (AX) -#endif RET bigloop_avx2: @@ -150,11 +115,7 @@ bigloop: SUBQ $8, BX CMPQ CX, DX JEQ bigloop -#ifdef GOEXPERIMENT_regabiargs XORQ AX, AX // return 0 -#else - MOVB $0, (AX) -#endif RET // remaining 0-8 bytes @@ -162,11 +123,7 @@ leftover: MOVQ -8(SI)(BX*1), CX MOVQ -8(DI)(BX*1), DX CMPQ CX, DX -#ifdef GOEXPERIMENT_regabiargs SETEQ AX -#else - SETEQ (AX) -#endif RET small: @@ -201,10 +158,5 @@ di_finish: SUBQ SI, DI SHLQ CX, DI equal: -#ifdef GOEXPERIMENT_regabiargs SETEQ AX -#else - SETEQ (AX) -#endif RET - diff --git a/src/internal/bytealg/equal_arm64.s b/src/internal/bytealg/equal_arm64.s index 01aa7b7b7aa8ad..d3aabba5871eed 100644 --- a/src/internal/bytealg/equal_arm64.s +++ b/src/internal/bytealg/equal_arm64.s @@ -6,58 +6,44 @@ #include "textflag.h" // memequal(a, b unsafe.Pointer, size uintptr) bool -TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 - MOVD size+16(FP), R1 +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 // short path to handle 0-byte case - CBZ R1, equal - MOVD a+0(FP), R0 - MOVD b+8(FP), R2 - MOVD $ret+24(FP), R8 + CBZ R2, equal B memeqbody<>(SB) equal: MOVD $1, R0 - MOVB R0, ret+24(FP) RET // memequal_varlen(a, b unsafe.Pointer) bool -TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 - MOVD a+0(FP), R3 - MOVD b+8(FP), R4 - CMP R3, R4 +TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-17 + CMP R0, R1 BEQ eq - MOVD 8(R26), R5 // compiler stores size at offset 8 in the closure - CBZ R5, eq - MOVD R3, 8(RSP) - MOVD R4, 16(RSP) - MOVD R5, 24(RSP) - BL runtime·memequal(SB) - MOVBU 32(RSP), R3 - MOVB R3, ret+16(FP) - RET + MOVD 8(R26), R2 // compiler stores size at offset 8 in the closure + CBZ R2, eq + B memeqbody<>(SB) eq: - MOVD $1, R3 - MOVB R3, ret+16(FP) + MOVD $1, R0 RET // input: // R0: pointer a -// R1: data len -// R2: pointer b -// R8: address to put result +// R1: pointer b +// R2: data len +// at return: result in R0 TEXT memeqbody<>(SB),NOSPLIT,$0 - CMP $1, R1 + CMP $1, R2 // handle 1-byte special case for better performance BEQ one - CMP $16, R1 + CMP $16, R2 // handle specially if length < 16 BLO tail - BIC $0x3f, R1, R3 + BIC $0x3f, R2, R3 CBZ R3, chunk16 // work with 64-byte chunks ADD R3, R0, R6 // end of chunks chunk64_loop: VLD1.P (R0), [V0.D2, V1.D2, V2.D2, V3.D2] - VLD1.P (R2), [V4.D2, V5.D2, V6.D2, V7.D2] + VLD1.P (R1), [V4.D2, V5.D2, V6.D2, V7.D2] VCMEQ V0.D2, V4.D2, V8.D2 VCMEQ V1.D2, V5.D2, V9.D2 VCMEQ V2.D2, V6.D2, V10.D2 @@ -71,66 +57,65 @@ chunk64_loop: CBZ R4, not_equal CBZ R5, not_equal BNE chunk64_loop - AND $0x3f, R1, R1 - CBZ R1, equal + AND $0x3f, R2, R2 + CBZ R2, equal chunk16: // work with 16-byte chunks - BIC $0xf, R1, R3 + BIC $0xf, R2, R3 CBZ R3, tail ADD R3, R0, R6 // end of chunks chunk16_loop: LDP.P 16(R0), (R4, R5) - LDP.P 16(R2), (R7, R9) + LDP.P 16(R1), (R7, R9) EOR R4, R7 CBNZ R7, not_equal EOR R5, R9 CBNZ R9, not_equal CMP R0, R6 BNE chunk16_loop - AND $0xf, R1, R1 - CBZ R1, equal + AND $0xf, R2, R2 + CBZ R2, equal tail: // special compare of tail with length < 16 - TBZ $3, R1, lt_8 + TBZ $3, R2, lt_8 MOVD (R0), R4 - MOVD (R2), R5 + MOVD (R1), R5 EOR R4, R5 CBNZ R5, not_equal - SUB $8, R1, R6 // offset of the last 8 bytes + SUB $8, R2, R6 // offset of the last 8 bytes MOVD (R0)(R6), R4 - MOVD (R2)(R6), R5 + MOVD (R1)(R6), R5 EOR R4, R5 CBNZ R5, not_equal B equal lt_8: - TBZ $2, R1, lt_4 + TBZ $2, R2, lt_4 MOVWU (R0), R4 - MOVWU (R2), R5 + MOVWU (R1), R5 EOR R4, R5 CBNZ R5, not_equal - SUB $4, R1, R6 // offset of the last 4 bytes + SUB $4, R2, R6 // offset of the last 4 bytes MOVWU (R0)(R6), R4 - MOVWU (R2)(R6), R5 + MOVWU (R1)(R6), R5 EOR R4, R5 CBNZ R5, not_equal B equal lt_4: - TBZ $1, R1, lt_2 + TBZ $1, R2, lt_2 MOVHU.P 2(R0), R4 - MOVHU.P 2(R2), R5 + MOVHU.P 2(R1), R5 CMP R4, R5 BNE not_equal lt_2: - TBZ $0, R1, equal + TBZ $0, R2, equal one: MOVBU (R0), R4 - MOVBU (R2), R5 + MOVBU (R1), R5 CMP R4, R5 BNE not_equal equal: MOVD $1, R0 - MOVB R0, (R8) RET not_equal: - MOVB ZR, (R8) + MOVB ZR, R0 RET diff --git a/src/internal/bytealg/equal_loong64.s b/src/internal/bytealg/equal_loong64.s new file mode 100644 index 00000000000000..dcdde89b25c4d0 --- /dev/null +++ b/src/internal/bytealg/equal_loong64.s @@ -0,0 +1,52 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "textflag.h" + +#define REGCTXT R29 + +// memequal(a, b unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 + MOVV a+0(FP), R4 + MOVV b+8(FP), R5 + BEQ R4, R5, eq + MOVV size+16(FP), R6 + ADDV R4, R6, R7 +loop: + BNE R4, R7, test + MOVV $1, R4 + MOVB R4, ret+24(FP) + RET +test: + MOVBU (R4), R9 + ADDV $1, R4 + MOVBU (R5), R10 + ADDV $1, R5 + BEQ R9, R10, loop + + MOVB R0, ret+24(FP) + RET +eq: + MOVV $1, R4 + MOVB R4, ret+24(FP) + RET + +// memequal_varlen(a, b unsafe.Pointer) bool +TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 + MOVV a+0(FP), R4 + MOVV b+8(FP), R5 + BEQ R4, R5, eq + MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure + MOVV R4, 8(R3) + MOVV R5, 16(R3) + MOVV R6, 24(R3) + JAL runtime·memequal(SB) + MOVBU 32(R3), R4 + MOVB R4, ret+16(FP) + RET +eq: + MOVV $1, R4 + MOVB R4, ret+16(FP) + RET diff --git a/src/internal/bytealg/equal_mips64x.s b/src/internal/bytealg/equal_mips64x.s index c2f7d3997edb36..d92f225e8d29d1 100644 --- a/src/internal/bytealg/equal_mips64x.s +++ b/src/internal/bytealg/equal_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/equal_mipsx.s b/src/internal/bytealg/equal_mipsx.s index 11e5549e45a1fa..4c46dd4fce42d0 100644 --- a/src/internal/bytealg/equal_mipsx.s +++ b/src/internal/bytealg/equal_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/equal_ppc64x.s b/src/internal/bytealg/equal_ppc64x.s index 5f0fea521b24a7..f2c7cc10f04fbc 100644 --- a/src/internal/bytealg/equal_ppc64x.s +++ b/src/internal/bytealg/equal_ppc64x.s @@ -3,101 +3,203 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "textflag.h" -// memequal(a, b unsafe.Pointer, size uintptr) bool -TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 - MOVD a+0(FP), R3 - MOVD b+8(FP), R4 - MOVD size+16(FP), R5 - MOVD $ret+24(FP), R10 +// 4K (smallest case) page size offset mask for PPC64. +#define PAGE_OFFSET 4095 + +// TODO: At writing, ISEL and BC do not support CR bit type arguments, +// define them here for readability. +#define CR0LT 4*0+0 +#define CR0EQ 4*0+2 +#define CR1LT 4*1+0 +#define CR6LT 4*6+0 + +// Likewise, the BC opcode is hard to read, and no extended +// mnemonics are offered for these forms. +#define BGELR_CR6 BC 4, CR6LT, (LR) +#define BEQLR BC 12, CR0EQ, (LR) +// memequal(a, b unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 + // R3 = a + // R4 = b + // R5 = size BR memeqbody<>(SB) // memequal_varlen(a, b unsafe.Pointer) bool -TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 - MOVD a+0(FP), R3 - MOVD b+8(FP), R4 +TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 + // R3 = a + // R4 = b CMP R3, R4 BEQ eq MOVD 8(R11), R5 // compiler stores size at offset 8 in the closure - MOVD $ret+16(FP), R10 BR memeqbody<>(SB) eq: MOVD $1, R3 - MOVB R3, ret+16(FP) RET // Do an efficient memequal for ppc64 // R3 = s1 // R4 = s2 // R5 = len -// R10 = addr of return value (byte) +// On exit: +// R3 = return value TEXT memeqbody<>(SB),NOSPLIT|NOFRAME,$0-0 - MOVD R5,CTR - CMP R5,$8 // only optimize >=8 - BLT simplecheck - DCBT (R3) // cache hint - DCBT (R4) - CMP R5,$32 // optimize >= 32 - MOVD R5,R6 // needed if setup8a branch - BLT setup8a // 8 byte moves only -setup32a: // 8 byte aligned, >= 32 bytes - SRADCC $5,R5,R6 // number of 32 byte chunks to compare - MOVD R6,CTR - MOVD $16,R14 // index for VSX loads and stores -loop32a: - LXVD2X (R3+R0), VS32 // VS32 = V0 - LXVD2X (R4+R0), VS33 // VS33 = V1 + MOVD R3, R8 // Move s1 into R8 + ADD R5, R3, R9 // &s1[len(s1)] + ADD R5, R4, R10 // &s2[len(s2)] + MOVD $1, R11 + CMP R5, $16 // Use GPR checks for check for len <= 16 + BLE check0_16 + MOVD $0, R3 // Assume no-match in case BGELR CR6 returns + CMP R5, $32 // Use overlapping VSX loads for len <= 32 + BLE check17_32 // Do a pair of overlapping VSR compares + CMP R5, $64 + BLE check33_64 // Hybrid check + overlap compare. + +setup64: + SRD $6, R5, R6 // number of 64 byte chunks to compare + MOVD R6, CTR + MOVD $16, R14 // index for VSX loads and stores + MOVD $32, R15 + MOVD $48, R16 + ANDCC $0x3F, R5, R5 // len%64==0? + + PCALIGN $32 +loop64: + LXVD2X (R8+R0), V0 + LXVD2X (R4+R0), V1 VCMPEQUBCC V0, V1, V2 // compare, setting CR6 - BGE CR6, noteq - LXVD2X (R3+R14), VS32 - LXVD2X (R4+R14), VS33 - VCMPEQUBCC V0, V1, V2 - BGE CR6, noteq - ADD $32,R3 // bump up to next 32 - ADD $32,R4 - BC 16, 0, loop32a // br ctr and cr - ANDCC $24,R5,R6 // Any 8 byte chunks? - BEQ leftover // and result is 0 -setup8a: - SRADCC $3,R6,R6 // get the 8 byte count - BEQ leftover // shifted value is 0 - MOVD R6,CTR -loop8: - MOVD 0(R3),R6 // doublewords to compare - ADD $8,R3 - MOVD 0(R4),R7 - ADD $8,R4 - CMP R6,R7 // match? - BC 8,2,loop8 // bt ctr <> 0 && cr - BNE noteq -leftover: - ANDCC $7,R5,R6 // check for leftover bytes - BEQ equal - MOVD R6,CTR - BR simple -simplecheck: - CMP R5,$0 - BEQ equal -simple: - MOVBZ 0(R3), R6 - ADD $1,R3 - MOVBZ 0(R4), R7 - ADD $1,R4 - CMP R6, R7 - BNE noteq - BC 8,2,simple - BNE noteq - BR equal -noteq: - MOVB $0, (R10) + BGELR_CR6 + LXVD2X (R8+R14), V0 + LXVD2X (R4+R14), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + LXVD2X (R8+R15), V0 + LXVD2X (R4+R15), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + LXVD2X (R8+R16), V0 + LXVD2X (R4+R16), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + ADD $64,R8 // bump up to next 64 + ADD $64,R4 + BDNZ loop64 + + ISEL $CR0EQ, R11, R3, R3 // If no tail, return 1, otherwise R3 remains 0. + BEQLR // return if no tail. + + ADD $-64, R9, R8 + ADD $-64, R10, R4 + LXVD2X (R8+R0), V0 + LXVD2X (R4+R0), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + LXVD2X (R8+R14), V0 + LXVD2X (R4+R14), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + LXVD2X (R8+R15), V0 + LXVD2X (R4+R15), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + LXVD2X (R8+R16), V0 + LXVD2X (R4+R16), V1 + VCMPEQUBCC V0, V1, V2 + ISEL $CR6LT, R11, R0, R3 RET -equal: - MOVD $1, R3 - MOVB R3, (R10) + +check33_64: + // Bytes 0-15 + LXVD2X (R8+R0), V0 + LXVD2X (R4+R0), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + ADD $16, R8 + ADD $16, R4 + + // Bytes 16-31 + LXVD2X (R8+R0), V0 + LXVD2X (R4+R0), V1 + VCMPEQUBCC V0, V1, V2 + BGELR_CR6 + + // A little tricky, but point R4,R8 to &sx[len-32], + // and reuse check17_32 to check the next 1-31 bytes (with some overlap) + ADD $-32, R9, R8 + ADD $-32, R10, R4 + // Fallthrough + +check17_32: + LXVD2X (R8+R0), V0 + LXVD2X (R4+R0), V1 + VCMPEQUBCC V0, V1, V2 + ISEL $CR6LT, R11, R0, R5 + + // Load sX[len(sX)-16:len(sX)] and compare. + ADD $-16, R9 + ADD $-16, R10 + LXVD2X (R9+R0), V0 + LXVD2X (R10+R0), V1 + VCMPEQUBCC V0, V1, V2 + ISEL $CR6LT, R5, R0, R3 + RET + +check0_16: + CMP R5, $8 + BLT check0_7 + // Load sX[0:7] and compare. + MOVD (R8), R6 + MOVD (R4), R7 + CMP R6, R7 + ISEL $CR0EQ, R11, R0, R5 + // Load sX[len(sX)-8:len(sX)] and compare. + MOVD -8(R9), R6 + MOVD -8(R10), R7 + CMP R6, R7 + ISEL $CR0EQ, R5, R0, R3 RET +check0_7: + CMP R5,$0 + MOVD $1, R3 + BEQLR // return if len == 0 + + // Check < 8B loads with a single compare, but select the load address + // such that it cannot cross a page boundary. Load a few bytes from the + // lower address if that does not cross the lower page. Or, load a few + // extra bytes from the higher addresses. And align those values + // consistently in register as either address may have differing + // alignment requirements. + ANDCC $PAGE_OFFSET, R8, R6 // &sX & PAGE_OFFSET + ANDCC $PAGE_OFFSET, R4, R9 + SUBC R5, $8, R12 // 8-len + SLD $3, R12, R14 // (8-len)*8 + CMPU R6, R12, CR1 // Enough bytes lower in the page to load lower? + CMPU R9, R12, CR0 + SUB R12, R8, R6 // compute lower load address + SUB R12, R4, R9 + ISEL $CR1LT, R8, R6, R8 // R8 = R6 < 0 ? R8 (&s1) : R6 (&s1 - (8-len)) + ISEL $CR0LT, R4, R9, R4 // Similar for s2 + MOVD (R8), R15 + MOVD (R4), R16 + SLD R14, R15, R7 + SLD R14, R16, R17 + SRD R14, R7, R7 // Clear the upper (8-len) bytes (with 2 shifts) + SRD R14, R17, R17 + SRD R14, R15, R6 // Clear the lower (8-len) bytes + SRD R14, R16, R9 +#ifdef GOARCH_ppc64le + ISEL $CR1LT, R7, R6, R8 // Choose the correct len bytes to compare based on alignment + ISEL $CR0LT, R17, R9, R4 +#else + ISEL $CR1LT, R6, R7, R8 + ISEL $CR0LT, R9, R17, R4 +#endif + CMP R4, R8 + ISEL $CR0EQ, R11, R0, R3 + RET diff --git a/src/internal/bytealg/equal_riscv64.s b/src/internal/bytealg/equal_riscv64.s index 22cb4fa97d8685..77202d60759208 100644 --- a/src/internal/bytealg/equal_riscv64.s +++ b/src/internal/bytealg/equal_riscv64.s @@ -5,45 +5,140 @@ #include "go_asm.h" #include "textflag.h" -#define CTXT S4 +#define CTXT S10 // func memequal(a, b unsafe.Pointer, size uintptr) bool -TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 - MOV a+0(FP), A1 - MOV b+8(FP), A2 - BEQ A1, A2, eq - MOV size+16(FP), A3 - ADD A1, A3, A4 -loop: - BEQ A1, A4, eq - - MOVBU (A1), A6 - ADD $1, A1 - MOVBU (A2), A7 - ADD $1, A2 - BEQ A6, A7, loop - - MOVB ZERO, ret+24(FP) - RET -eq: - MOV $1, A1 - MOVB A1, ret+24(FP) - RET +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 +#ifndef GOEXPERIMENT_regabiargs + MOV a+0(FP), X10 + MOV b+8(FP), X11 + MOV size+16(FP), X12 + MOV $ret+24(FP), X13 +#endif + // X10 = a_base + // X11 = b_base + // X12 = size + JMP memequal<>(SB) // func memequal_varlen(a, b unsafe.Pointer) bool -TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 - MOV a+0(FP), A1 - MOV b+8(FP), A2 - BEQ A1, A2, eq - MOV 8(CTXT), A3 // compiler stores size at offset 8 in the closure - MOV A1, 8(X2) - MOV A2, 16(X2) - MOV A3, 24(X2) - CALL runtime·memequal(SB) - MOVBU 32(X2), A1 - MOVB A1, ret+16(FP) +TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 + MOV 8(CTXT), X12 // compiler stores size at offset 8 in the closure +#ifndef GOEXPERIMENT_regabiargs + MOV a+0(FP), X10 + MOV b+8(FP), X11 + MOV $ret+16(FP), X13 +#endif + // X10 = a_base + // X11 = b_base + JMP memequal<>(SB) + +// On entry X10 and X11 contain pointers, X12 contains length. +// For non-regabi X13 contains address for return value. +// For regabi return value in X10. +TEXT memequal<>(SB),NOSPLIT|NOFRAME,$0 + BEQ X10, X11, eq + + MOV $32, X23 + BLT X12, X23, loop4_check + + // Check alignment - if alignment differs we have to do one byte at a time. + AND $3, X10, X9 + AND $3, X11, X19 + BNE X9, X19, loop4_check + BEQZ X9, loop32_check + + // Check one byte at a time until we reach 8 byte alignment. + SUB X9, X12, X12 +align: + ADD $-1, X9 + MOVBU 0(X10), X19 + MOVBU 0(X11), X20 + BNE X19, X20, not_eq + ADD $1, X10 + ADD $1, X11 + BNEZ X9, align + +loop32_check: + MOV $32, X9 + BLT X12, X9, loop16_check +loop32: + MOV 0(X10), X19 + MOV 0(X11), X20 + MOV 8(X10), X21 + MOV 8(X11), X22 + BNE X19, X20, not_eq + BNE X21, X22, not_eq + MOV 16(X10), X14 + MOV 16(X11), X15 + MOV 24(X10), X16 + MOV 24(X11), X17 + BNE X14, X15, not_eq + BNE X16, X17, not_eq + ADD $32, X10 + ADD $32, X11 + ADD $-32, X12 + BGE X12, X9, loop32 + BEQZ X12, eq + +loop16_check: + MOV $16, X23 + BLT X12, X23, loop4_check +loop16: + MOV 0(X10), X19 + MOV 0(X11), X20 + MOV 8(X10), X21 + MOV 8(X11), X22 + BNE X19, X20, not_eq + BNE X21, X22, not_eq + ADD $16, X10 + ADD $16, X11 + ADD $-16, X12 + BGE X12, X23, loop16 + BEQZ X12, eq + +loop4_check: + MOV $4, X23 + BLT X12, X23, loop1 +loop4: + MOVBU 0(X10), X19 + MOVBU 0(X11), X20 + MOVBU 1(X10), X21 + MOVBU 1(X11), X22 + BNE X19, X20, not_eq + BNE X21, X22, not_eq + MOVBU 2(X10), X14 + MOVBU 2(X11), X15 + MOVBU 3(X10), X16 + MOVBU 3(X11), X17 + BNE X14, X15, not_eq + BNE X16, X17, not_eq + ADD $4, X10 + ADD $4, X11 + ADD $-4, X12 + BGE X12, X23, loop4 + +loop1: + BEQZ X12, eq + MOVBU 0(X10), X19 + MOVBU 0(X11), X20 + BNE X19, X20, not_eq + ADD $1, X10 + ADD $1, X11 + ADD $-1, X12 + JMP loop1 + +not_eq: +#ifndef GOEXPERIMENT_regabiargs + MOVB ZERO, (X13) +#else + MOVB ZERO, X10 +#endif RET eq: - MOV $1, A1 - MOVB A1, ret+16(FP) +#ifndef GOEXPERIMENT_regabiargs + MOV $1, X10 + MOVB X10, (X13) +#else + MOV $1, X10 +#endif RET diff --git a/src/internal/bytealg/index_amd64.s b/src/internal/bytealg/index_amd64.s index 6193b572393a58..04314917b89184 100644 --- a/src/internal/bytealg/index_amd64.s +++ b/src/internal/bytealg/index_amd64.s @@ -233,8 +233,10 @@ success_avx2: VZEROUPPER JMP success sse42: +#ifndef hasSSE42 CMPB internal∕cpu·X86+const_offsetX86HasSSE42(SB), $1 JNE no_sse42 +#endif CMPQ AX, $12 // PCMPESTRI is slower than normal compare, // so using it makes sense only if we advance 4+ bytes per compare diff --git a/src/internal/bytealg/index_generic.go b/src/internal/bytealg/index_generic.go index 0a6eb90d2d9297..a59e32938e76ec 100644 --- a/src/internal/bytealg/index_generic.go +++ b/src/internal/bytealg/index_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 && !arm64 && !s390x && !ppc64le && !ppc64 -// +build !amd64,!arm64,!s390x,!ppc64le,!ppc64 package bytealg diff --git a/src/internal/bytealg/index_native.go b/src/internal/bytealg/index_native.go index 9547a5d8e2aa39..6e4a2f39e4317f 100644 --- a/src/internal/bytealg/index_native.go +++ b/src/internal/bytealg/index_native.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || arm64 || s390x || ppc64le || ppc64 -// +build amd64 arm64 s390x ppc64le ppc64 package bytealg diff --git a/src/internal/bytealg/index_ppc64x.go b/src/internal/bytealg/index_ppc64x.go index c9b2b5a59f836d..ab3cbe5e96e6ba 100644 --- a/src/internal/bytealg/index_ppc64x.go +++ b/src/internal/bytealg/index_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (aix || linux) && (ppc64 || ppc64le) -// +build aix linux -// +build ppc64 ppc64le package bytealg diff --git a/src/internal/bytealg/index_ppc64x.s b/src/internal/bytealg/index_ppc64x.s index 3ed94421256115..38442ce27cec99 100644 --- a/src/internal/bytealg/index_ppc64x.s +++ b/src/internal/bytealg/index_ppc64x.s @@ -17,12 +17,12 @@ // NOTE: There is a power9 implementation that // improves performance by 10-15% on little -// endian for some of the benchmarks, but -// work is still needed for a big endian +// endian for some of the benchmarks. +// Unrolled index2to16 loop by 4 on ppc64le/power9 +// Work is still needed for a big endian // implementation on power9. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "textflag.h" @@ -46,35 +46,32 @@ DATA byteswap<>+8(SB)/8, $0x0f0e0d0c0b0a0908 GLOBL byteswap<>+0(SB), RODATA, $16 -TEXT ·Index(SB), NOSPLIT|NOFRAME, $0-56 - MOVD a_base+0(FP), R3 // R3 = byte array pointer - MOVD a_len+8(FP), R4 // R4 = length - MOVD b_base+24(FP), R5 // R5 = separator pointer - MOVD b_len+32(FP), R6 // R6 = separator length - MOVD $ret+48(FP), R14 // R14 = &ret +TEXT ·Index(SB),NOSPLIT|NOFRAME,$0-56 + // R3 = byte array pointer + // R4 = length + MOVD R6, R5 // R5 = separator pointer + MOVD R7, R6 // R6 = separator length #ifdef GOARCH_ppc64le MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R7 CMP R7, $1 BNE power8 BR indexbodyp9<>(SB) - #endif power8: BR indexbody<>(SB) -TEXT ·IndexString(SB), NOSPLIT|NOFRAME, $0-40 - MOVD a_base+0(FP), R3 // R3 = string - MOVD a_len+8(FP), R4 // R4 = length - MOVD b_base+16(FP), R5 // R5 = separator pointer - MOVD b_len+24(FP), R6 // R6 = separator length - MOVD $ret+32(FP), R14 // R14 = &ret +TEXT ·IndexString(SB),NOSPLIT|NOFRAME,$0-40 + // R3 = string + // R4 = length + // R5 = separator pointer + // R6 = separator length #ifdef GOARCH_ppc64le MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R7 CMP R7, $1 BNE power8 - BR indexbody<>(SB) + BR indexbodyp9<>(SB) #endif power8: @@ -101,16 +98,6 @@ power8: #define LASTSTR R27 #define ONES V20 #define SWAP V21 -#define V0_ VS32 -#define V1_ VS33 -#define V2_ VS34 -#define V3_ VS35 -#define V4_ VS36 -#define V5_ VS37 -#define V6_ VS38 -#define V7_ VS39 -#define V8_ VS40 -#define V9_ VS41 #define SWAP_ VS53 TEXT indexbody<>(SB), NOSPLIT|NOFRAME, $0 CMP R6, R4 // Compare lengths @@ -133,7 +120,7 @@ TEXT indexbody<>(SB), NOSPLIT|NOFRAME, $0 BGE CR4, loadge16 // Load for len(sep) >= 16 SUB R6, R16, R9 // 16-len of sep SLD $3, R9 // Set up for VSLO - MTVSRD R9, V9_ // Set up for VSLO + MTVSRD R9, V9 // Set up for VSLO VSLDOI $8, V9, V9, V9 // Set up for VSLO VSLO ONES, V9, SEPMASK // Mask for separator len(sep) < 16 @@ -141,12 +128,12 @@ loadge16: ANDCC $15, R5, R9 // Find byte offset of sep ADD R9, R6, R10 // Add sep len CMP R10, $16 // Check if sep len+offset > 16 - BGE sepcross16 // Sep crosses 16 byte boundary + BGT sepcross16 // Sep crosses 16 byte boundary RLDICR $0, R5, $59, R8 // Adjust addr to 16 byte container - VLOADSWAP(R8, R0, V0, V0_)// Load 16 bytes @R8 into V0 + VLOADSWAP(R8, R0, V0, V0) // Load 16 bytes @R8 into V0 SLD $3, R9 // Set up shift count for VSLO - MTVSRD R9, V8_ // Set up shift count for VSLO + MTVSRD R9, V8 // Set up shift count for VSLO VSLDOI $8, V8, V8, V8 VSLO V0, V8, V0 // Shift by start byte @@ -154,7 +141,7 @@ loadge16: BR index2plus sepcross16: - VLOADSWAP(R5, R0, V0, V0_) // Load 16 bytes @R5 into V0 + VLOADSWAP(R5, R0, V0, V0) // Load 16 bytes @R5 into V0 VAND V0, SEPMASK, V0 // mask out separator BLE CR4, index2to16 @@ -183,10 +170,10 @@ index2plus: // that value when found. Loop as // long as len(string) > 16 index2loop2: - VLOADSWAP(R7, R19, V3, V3_) // Load 16 bytes @R7+1 into V3 + VLOADSWAP(R7, R19, V3, V3) // Load 16 bytes @R7+1 into V3 index2loop: - VLOADSWAP(R7, R0, V2, V2_) // Load 16 bytes @R7 into V2 + VLOADSWAP(R7, R0, V2, V2) // Load 16 bytes @R7 into V2 VCMPEQUH V1, V2, V5 // Search for sep VCMPEQUH V1, V3, V6 // Search for sep offset by 1 VSEL V6, V5, V31, V7 // merge even and odd indices @@ -205,7 +192,7 @@ index2loop: BLT index2loop2 // If < last, continue loop CMP R7, LASTBYTE // Compare addr+16 against last byte BLT index2to16 // If < 16 handle specially - VLOADSWAP(R7, R0, V3, V3_) // Load 16 bytes @R7 into V3 + VLOADSWAP(R7, R0, V3, V3) // Load 16 bytes @R7 into V3 VSLDOI $1, V3, V10, V3 // Shift left by 1 byte BR index2loop @@ -238,11 +225,11 @@ index3plus: index3loop2: MOVD $2, R21 // Set up index for 2 VSPLTISB $0, V10 // Clear V10 - VLOADSWAP(R7, R21, V3, V3_)// Load 16 bytes @R7+2 into V3 + VLOADSWAP(R7, R21, V3, V3)// Load 16 bytes @R7+2 into V3 VSLDOI $14, V3, V10, V3 // Left justify next 2 bytes index3loop: - VLOADSWAP(R7, R0, V2, V2_) // Load with correct order + VLOADSWAP(R7, R0, V2, V2) // Load with correct order VSLDOI $1, V2, V3, V4 // string[1:17] VSLDOI $2, V2, V3, V9 // string[2:18] VCMPEQUH V1, V2, V5 // compare hw even indices @@ -298,12 +285,12 @@ index4plus: VSPLTW $0, V0, V1 // Splat 1st word of separator index4loop: - VLOADSWAP(R7, R0, V2, V2_) // Load 16 bytes @R7 into V2 + VLOADSWAP(R7, R0, V2, V2) // Load 16 bytes @R7 into V2 next4: VSPLTISB $0, V10 // Clear MOVD $3, R9 // Number of bytes beyond 16 - VLOADSWAP(R7, R9, V3, V3_) // Load 16 bytes @R7+3 into V3 + VLOADSWAP(R7, R9, V3, V3) // Load 16 bytes @R7+3 into V3 VSLDOI $13, V3, V10, V3 // Shift left last 3 bytes VSLDOI $1, V2, V3, V4 // V4=(V2:V3)<<1 VSLDOI $2, V2, V3, V9 // V9=(V2:V3)<<2 @@ -348,7 +335,7 @@ index2to16: // At least 16 bytes of string left // Mask the number of bytes in sep index2to16loop: - VLOADSWAP(R7, R0, V1, V1_) // Load 16 bytes @R7 into V1 + VLOADSWAP(R7, R0, V1, V1) // Load 16 bytes @R7 into V1 compare: VAND V1, SEPMASK, V2 // Mask out sep size @@ -370,14 +357,14 @@ index2to16tail: ADD R10, R9, R11 // offset + len CMP R11, $16 // >= 16? BLE short // Does not cross 16 bytes - VLOADSWAP(R7, R0, V1, V1_)// Load 16 bytes @R7 into V1 + VLOADSWAP(R7, R0, V1, V1) // Load 16 bytes @R7 into V1 BR index2to16next // Continue on short: RLDICR $0, R7, $59, R9 // Adjust addr to 16 byte container - VLOADSWAP(R9, R0, V1, V1_)// Load 16 bytes @R9 into V1 + VLOADSWAP(R9, R0, V1, V1)// Load 16 bytes @R9 into V1 SLD $3, R10 // Set up shift - MTVSRD R10, V8_ // Set up shift + MTVSRD R10, V8 // Set up shift VSLDOI $8, V8, V8, V8 VSLO V1, V8, V1 // Shift by start byte VSPLTISB $0, V25 // Clear for later use @@ -397,17 +384,17 @@ index17plus: BGT index33plus SUB $16, R6, R9 // Extra > 16 SLD $56, R9, R10 // Shift to use in VSLO - MTVSRD R10, V9_ // Set up for VSLO - VLOADSWAP(R5, R9, V1, V1_)// Load 16 bytes @R5+R9 into V1 + MTVSRD R10, V9 // Set up for VSLO + VLOADSWAP(R5, R9, V1, V1)// Load 16 bytes @R5+R9 into V1 VSLO V1, V9, V1 // Shift left VSPLTISB $0xff, V7 // Splat 1s VSPLTISB $0, V27 // Splat 0 index17to32loop: - VLOADSWAP(R7, R0, V2, V2_) // Load 16 bytes @R7 into V2 + VLOADSWAP(R7, R0, V2, V2) // Load 16 bytes @R7 into V2 next17: - VLOADSWAP(R7, R9, V3, V3_) // Load 16 bytes @R7+R9 into V3 + VLOADSWAP(R7, R9, V3, V3) // Load 16 bytes @R7+R9 into V3 VSLO V3, V9, V3 // Shift left VCMPEQUB V0, V2, V4 // Compare first 16 bytes VCMPEQUB V1, V3, V5 // Compare extra over 16 bytes @@ -420,8 +407,7 @@ next17: BR index17to32loop // Continue notfound: - MOVD $-1, R8 // Return -1 if not found - MOVD R8, (R14) + MOVD $-1, R3 // Return -1 if not found RET index33plus: @@ -432,12 +418,12 @@ foundR25: SRD $3, R25 // Convert from bits to bytes ADD R25, R7 // Add to current string address SUB R3, R7 // Subtract from start of string - MOVD R7, (R14) // Return byte where found + MOVD R7, R3 // Return byte where found RET found: SUB R3, R7 // Return byte where found - MOVD R7, (R14) + MOVD R7, R3 RET TEXT indexbodyp9<>(SB), NOSPLIT|NOFRAME, $0 @@ -459,7 +445,7 @@ TEXT indexbodyp9<>(SB), NOSPLIT|NOFRAME, $0 BGE CR4, loadge16 // Load for len(sep) >= 16 SUB R6, R16, R9 // 16-len of sep SLD $3, R9 // Set up for VSLO - MTVSRD R9, V9_ // Set up for VSLO + MTVSRD R9, V9 // Set up for VSLO VSLDOI $8, V9, V9, V9 // Set up for VSLO VSLO ONES, V9, SEPMASK // Mask for separator len(sep) < 16 @@ -467,12 +453,12 @@ loadge16: ANDCC $15, R5, R9 // Find byte offset of sep ADD R9, R6, R10 // Add sep len CMP R10, $16 // Check if sep len+offset > 16 - BGE sepcross16 // Sep crosses 16 byte boundary + BGT sepcross16 // Sep crosses 16 byte boundary RLDICR $0, R5, $59, R8 // Adjust addr to 16 byte container - LXVB16X (R8)(R0), V0_ // Load 16 bytes @R8 into V0 + LXVB16X (R8)(R0), V0 // Load 16 bytes @R8 into V0 SLD $3, R9 // Set up shift count for VSLO - MTVSRD R9, V8_ // Set up shift count for VSLO + MTVSRD R9, V8 // Set up shift count for VSLO VSLDOI $8, V8, V8, V8 VSLO V0, V8, V0 // Shift by start byte @@ -480,7 +466,7 @@ loadge16: BR index2plus sepcross16: - LXVB16X (R5)(R0), V0_ // Load 16 bytes @R5 into V0 + LXVB16X (R5)(R0), V0 // Load 16 bytes @R5 into V0 VAND V0, SEPMASK, V0 // mask out separator BLE CR4, index2to16 @@ -509,10 +495,10 @@ index2plus: // that value when found. Loop as // long as len(string) > 16 index2loop2: - LXVB16X (R7)(R19), V3_ // Load 16 bytes @R7+1 into V3 + LXVB16X (R7)(R19), V3 // Load 16 bytes @R7+1 into V3 index2loop: - LXVB16X (R7)(R0), V2_ // Load 16 bytes @R7 into V2 + LXVB16X (R7)(R0), V2 // Load 16 bytes @R7 into V2 VCMPEQUH V1, V2, V5 // Search for sep VCMPEQUH V1, V3, V6 // Search for sep offset by 1 VSEL V6, V5, V31, V7 // merge even and odd indices @@ -531,7 +517,7 @@ index2loop: BLT index2loop2 // If < last, continue loop CMP R7, LASTBYTE // Compare addr+16 against last byte BLT index2to16 // If < 16 handle specially - LXVB16X (R7)(R0), V3_ // Load 16 bytes @R7 into V3 + LXVB16X (R7)(R0), V3 // Load 16 bytes @R7 into V3 VSLDOI $1, V3, V10, V3 // Shift left by 1 byte BR index2loop @@ -564,11 +550,11 @@ index3plus: index3loop2: MOVD $2, R21 // Set up index for 2 VSPLTISB $0, V10 // Clear V10 - LXVB16X (R7)(R21), V3_ // Load 16 bytes @R7+2 into V3 + LXVB16X (R7)(R21), V3 // Load 16 bytes @R7+2 into V3 VSLDOI $14, V3, V10, V3 // Left justify next 2 bytes index3loop: - LXVB16X (R7)(R0), V2_ // Load 16 bytes @R7 + LXVB16X (R7)(R0), V2 // Load 16 bytes @R7 VSLDOI $1, V2, V3, V4 // string[1:17] VSLDOI $2, V2, V3, V9 // string[2:18] VCMPEQUH V1, V2, V5 // compare hw even indices @@ -609,7 +595,6 @@ index4plus: ADD $20, R7, R9 // Check string size to load CMP R9, LASTBYTE // Verify string length BGE index2to16 // If not large enough, process remaining - MOVD $2, R15 // Set up index // Set up masks for use with VSEL MOVD $0xff, R21 // Set up mask 0xff000000ff000000... @@ -624,12 +609,12 @@ index4plus: VSPLTW $0, V0, V1 // Splat 1st word of separator index4loop: - LXVB16X (R7)(R0), V2_ // Load 16 bytes @R7 into V2 + LXVB16X (R7)(R0), V2 // Load 16 bytes @R7 into V2 next4: VSPLTISB $0, V10 // Clear MOVD $3, R9 // Number of bytes beyond 16 - LXVB16X (R7)(R9), V3_ // Load 16 bytes @R7 into V2 + LXVB16X (R7)(R9), V3 // Load 16 bytes @R7 into V2 VSLDOI $13, V3, V10, V3 // Shift left last 3 bytes VSLDOI $1, V2, V3, V4 // V4=(V2:V3)<<1 VSLDOI $2, V2, V3, V9 // V9=(V2:V3)<<2 @@ -667,46 +652,91 @@ index2to16: CMP R7, LASTSTR // Compare last start byte BGT notfound // last takes len(sep) into account - ADD $16, R7, R9 // Check for last byte of string + ADD $19, R7, R9 // To check 4 indices per iteration, need at least 16+3 bytes CMP R9, LASTBYTE BGT index2to16tail // At least 16 bytes of string left // Mask the number of bytes in sep -index2to16loop: - LXVB16X (R7)(R0), V1_ // Load 16 bytes @R7 into V1 + VSPLTISB $0, V10 // Clear + MOVD $3, R17 // Number of bytes beyond 16 -compare: - VAND V1, SEPMASK, V2 // Mask out sep size - VCMPEQUBCC V0, V2, V3 // Compare masked string - BLT CR6, found // All equal - ADD $1, R7 // Update ptr to next byte +index2to16loop: + LXVB16X (R7)(R0), V1 // Load next 16 bytes of string into V1 from R7 + LXVB16X (R7)(R17), V5 // Load next 16 bytes of string into V5 from R7+3 + + VSLDOI $13, V5, V10, V2 // Shift left last 3 bytes + VSLDOI $1, V1, V2, V3 // V3=(V1:V2)<<1 + VSLDOI $2, V1, V2, V4 // V4=(V1:V2)<<2 + VAND V1, SEPMASK, V8 // Mask out sep size 0th index + VAND V3, SEPMASK, V9 // Mask out sep size 1st index + VAND V4, SEPMASK, V11 // Mask out sep size 2nd index + VAND V5, SEPMASK, V12 // Mask out sep size 3rd index + VCMPEQUBCC V0, V8, V8 // compare masked string + BLT CR6, found // All equal while comparing 0th index + VCMPEQUBCC V0, V9, V9 // compare masked string + BLT CR6, found2 // All equal while comparing 1st index + VCMPEQUBCC V0, V11, V11 // compare masked string + BLT CR6, found3 // All equal while comparing 2nd index + VCMPEQUBCC V0, V12, V12 // compare masked string + BLT CR6, found4 // All equal while comparing 3rd index + + ADD $4, R7 // Update ptr to next 4 bytes CMP R7, LASTSTR // Still less than last start byte BGT notfound // Not found - ADD $16, R7, R9 // Verify remaining bytes - CMP R9, LASTBYTE // At least 16 - BLT index2to16loop // Try again + ADD $19, R7, R9 // Verify remaining bytes + CMP R9, LASTBYTE // length of string at least 19 + BLE index2to16loop // Try again, else do post processing and jump to index2to16next - // Less than 16 bytes remaining in string - // Separator >= 2 + // <19 bytes left, post process the remaining string index2to16tail: - ADD R3, R4, R9 // End of string - SUB R7, R9, R9 // Number of bytes left - ANDCC $15, R7, R10 // 16 byte offset - ADD R10, R9, R11 // offset + len - CMP R11, $16 // >= 16? - BLE short // Does not cross 16 bytes - LXVB16X (R7)(R0), V1_ // Load 16 bytes @R7 into V1 - BR index2to16next // Continue on + ADD R3, R4, R9 // End of string + SUB R7, R9, R9 // Number of bytes left + ANDCC $15, R7, R10 // 16 byte offset + ADD R10, R9, R11 // offset + len + CMP R11, $16 // >= 16? + BLE short // Does not cross 16 bytes + LXVB16X (R7)(R0), V1 // Load 16 bytes @R7 into V1 + CMP R9, $16 // Post-processing of unrolled loop + BLE index2to16next // continue to index2to16next if <= 16 bytes + SUB R16, R9, R10 // R9 should be 18 or 17 hence R10 is 1 or 2 + LXVB16X (R7)(R10), V9 + CMP R10, $1 // string length is 17, compare 1 more byte + BNE extra2 // string length is 18, compare 2 more bytes + VSLDOI $15, V9, V10, V25 + VAND V1, SEPMASK, V2 // Just compare size of sep + VCMPEQUBCC V0, V2, V3 // Compare sep and partial string + BLT CR6, found // Found + ADD $1, R7 // Not found, try next partial string + CMP R7, LASTSTR // Check for end of string + BGT notfound // If at end, then not found + VSLDOI $1, V1, V25, V1 // Shift string left by 1 byte + BR index2to16next // go to remainder loop +extra2: + VSLDOI $14, V9, V10, V25 + VAND V1, SEPMASK, V2 // Just compare size of sep + VCMPEQUBCC V0, V2, V3 // Compare sep and partial string + BLT CR6, found // Found + ADD $1, R7 // Not found, try next partial string + CMP R7, LASTSTR // Check for end of string + BGT notfound // If at end, then not found + VSLDOI $1, V1, V25, V1 // Shift string left by 1 byte + VAND V1, SEPMASK, V2 // Just compare size of sep + VCMPEQUBCC V0, V2, V3 // Compare sep and partial string + BLT CR6, found // Found + ADD $1, R7 // Not found, try next partial string + CMP R7, LASTSTR // Check for end of string + BGT notfound // If at end, then not found + VSLDOI $1, V1, V25, V1 // Shift string left by 1 byte + BR index2to16next // Check the remaining partial string in index2to16next short: - RLDICR $0, R7, $59, R9 // Adjust addr to 16 byte container - LXVB16X (R9)(R0), V1_ // Load 16 bytes @R9 into V1 - SLD $3, R10 // Set up shift - MTVSRD R10, V8_ // Set up shift + RLDICR $0, R7, $59, R9 // Adjust addr to 16 byte container + LXVB16X (R9)(R0), V1 // Load 16 bytes @R9 into V1 + SLD $3, R10 // Set up shift + MTVSRD R10, V8 // Set up shift VSLDOI $8, V8, V8, V8 - VSLO V1, V8, V1 // Shift by start byte - VSPLTISB $0, V25 // Clear for later use + VSLO V1, V8, V1 // Shift by start byte index2to16next: VAND V1, SEPMASK, V2 // Just compare size of sep @@ -715,7 +745,7 @@ index2to16next: ADD $1, R7 // Not found, try next partial string CMP R7, LASTSTR // Check for end of string BGT notfound // If at end, then not found - VSLDOI $1, V1, V25, V1 // Shift string left by 1 byte + VSLDOI $1, V1, V10, V1 // Shift string left by 1 byte BR index2to16next // Check the next partial string index17plus: @@ -723,17 +753,17 @@ index17plus: BGT index33plus SUB $16, R6, R9 // Extra > 16 SLD $56, R9, R10 // Shift to use in VSLO - MTVSRD R10, V9_ // Set up for VSLO - LXVB16X (R5)(R9), V1_ // Load 16 bytes @R5+R9 into V1 + MTVSRD R10, V9 // Set up for VSLO + LXVB16X (R5)(R9), V1 // Load 16 bytes @R5+R9 into V1 VSLO V1, V9, V1 // Shift left VSPLTISB $0xff, V7 // Splat 1s VSPLTISB $0, V27 // Splat 0 index17to32loop: - LXVB16X (R7)(R0), V2_ // Load 16 bytes @R7 into V2 + LXVB16X (R7)(R0), V2 // Load 16 bytes @R7 into V2 next17: - LXVB16X (R7)(R9), V3_ // Load 16 bytes @R7+R9 into V3 + LXVB16X (R7)(R9), V3 // Load 16 bytes @R7+R9 into V3 VSLO V3, V9, V3 // Shift left VCMPEQUB V0, V2, V4 // Compare first 16 bytes VCMPEQUB V1, V3, V5 // Compare extra over 16 bytes @@ -746,8 +776,7 @@ next17: BR index17to32loop // Continue notfound: - MOVD $-1, R8 // Return -1 if not found - MOVD R8, (R14) + MOVD $-1, R3 // Return -1 if not found RET index33plus: @@ -758,11 +787,15 @@ foundR25: SRD $3, R25 // Convert from bits to bytes ADD R25, R7 // Add to current string address SUB R3, R7 // Subtract from start of string - MOVD R7, (R14) // Return byte where found + MOVD R7, R3 // Return byte where found RET - -found: +found4: + ADD $1, R7 // found from unrolled loop at index 3 +found3: + ADD $1, R7 // found from unrolled loop at index 2 +found2: + ADD $1, R7 // found from unrolled loop at index 1 +found: // found at index 0 SUB R3, R7 // Return byte where found - MOVD R7, (R14) + MOVD R7, R3 RET - diff --git a/src/internal/bytealg/indexbyte_amd64.s b/src/internal/bytealg/indexbyte_amd64.s index f78093c539013e..1ca70e39e23a79 100644 --- a/src/internal/bytealg/indexbyte_amd64.s +++ b/src/internal/bytealg/indexbyte_amd64.s @@ -115,8 +115,10 @@ endofpage: RET avx2: +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JNE sse +#endif MOVD AX, X0 LEAQ -32(SI)(BX*1), R11 VPBROADCASTB X0, Y1 diff --git a/src/internal/bytealg/indexbyte_generic.go b/src/internal/bytealg/indexbyte_generic.go index 6ef639fafdc992..b89d34ff231aec 100644 --- a/src/internal/bytealg/indexbyte_generic.go +++ b/src/internal/bytealg/indexbyte_generic.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !ppc64 && !ppc64le && !mips && !mipsle && !mips64 && !mips64le && !riscv64 && !wasm -// +build !386,!amd64,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!mips64,!mips64le,!riscv64,!wasm +//go:build !386 && !amd64 && !s390x && !arm && !arm64 && !loong64 && !ppc64 && !ppc64le && !mips && !mipsle && !mips64 && !mips64le && !riscv64 && !wasm package bytealg diff --git a/src/internal/bytealg/indexbyte_loong64.s b/src/internal/bytealg/indexbyte_loong64.s new file mode 100644 index 00000000000000..baa9c86be283d7 --- /dev/null +++ b/src/internal/bytealg/indexbyte_loong64.s @@ -0,0 +1,52 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "textflag.h" + +TEXT ·IndexByte(SB),NOSPLIT,$0-40 + MOVV b_base+0(FP), R4 + MOVV b_len+8(FP), R5 + MOVBU c+24(FP), R6 // byte to find + MOVV R4, R7 // store base for later + ADDV R4, R5 // end + ADDV $-1, R4 + +loop: + ADDV $1, R4 + BEQ R4, R5, notfound + MOVBU (R4), R8 + BNE R6, R8, loop + + SUBV R7, R4 // remove base + MOVV R4, ret+32(FP) + RET + +notfound: + MOVV $-1, R4 + MOVV R4, ret+32(FP) + RET + +TEXT ·IndexByteString(SB),NOSPLIT,$0-32 + MOVV s_base+0(FP), R4 + MOVV s_len+8(FP), R5 + MOVBU c+16(FP), R6 // byte to find + MOVV R4, R7 // store base for later + ADDV R4, R5 // end + ADDV $-1, R4 + +loop: + ADDV $1, R4 + BEQ R4, R5, notfound + MOVBU (R4), R8 + BNE R6, R8, loop + + SUBV R7, R4 // remove base + MOVV R4, ret+24(FP) + RET + +notfound: + MOVV $-1, R4 + MOVV R4, ret+24(FP) + RET diff --git a/src/internal/bytealg/indexbyte_mips64x.s b/src/internal/bytealg/indexbyte_mips64x.s index 0f377f5a4c9e33..5689f84b47a54a 100644 --- a/src/internal/bytealg/indexbyte_mips64x.s +++ b/src/internal/bytealg/indexbyte_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/indexbyte_mipsx.s b/src/internal/bytealg/indexbyte_mipsx.s index bed015bbd6b5af..1c2b104d3c9efd 100644 --- a/src/internal/bytealg/indexbyte_mipsx.s +++ b/src/internal/bytealg/indexbyte_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "textflag.h" diff --git a/src/internal/bytealg/indexbyte_native.go b/src/internal/bytealg/indexbyte_native.go index 965f38fe52761c..c5bb2df5ea1e68 100644 --- a/src/internal/bytealg/indexbyte_native.go +++ b/src/internal/bytealg/indexbyte_native.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build 386 || amd64 || s390x || arm || arm64 || ppc64 || ppc64le || mips || mipsle || mips64 || mips64le || riscv64 || wasm -// +build 386 amd64 s390x arm arm64 ppc64 ppc64le mips mipsle mips64 mips64le riscv64 wasm +//go:build 386 || amd64 || s390x || arm || arm64 || loong64 || ppc64 || ppc64le || mips || mipsle || mips64 || mips64le || riscv64 || wasm package bytealg diff --git a/src/internal/bytealg/indexbyte_ppc64x.s b/src/internal/bytealg/indexbyte_ppc64x.s index 8e13c5a56e3486..1a6e852d67258f 100644 --- a/src/internal/bytealg/indexbyte_ppc64x.s +++ b/src/internal/bytealg/indexbyte_ppc64x.s @@ -3,36 +3,40 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "textflag.h" -TEXT ·IndexByte(SB),NOSPLIT|NOFRAME,$0-40 - MOVD b_base+0(FP), R3 // R3 = byte array pointer - MOVD b_len+8(FP), R4 // R4 = length - MOVBZ c+24(FP), R5 // R5 = byte - MOVD $ret+32(FP), R14 // R14 = &ret +TEXT ·IndexByte(SB),NOSPLIT|NOFRAME,$0-40 + // R3 = byte array pointer + // R4 = length + MOVD R6, R5 // R5 = byte + MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16 BR indexbytebody<>(SB) -TEXT ·IndexByteString(SB),NOSPLIT|NOFRAME,$0-32 - MOVD s_base+0(FP), R3 // R3 = string - MOVD s_len+8(FP), R4 // R4 = length - MOVBZ c+16(FP), R5 // R5 = byte - MOVD $ret+24(FP), R14 // R14 = &ret +TEXT ·IndexByteString(SB),NOSPLIT|NOFRAME,$0-32 + // R3 = string + // R4 = length + // R5 = byte + MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16 BR indexbytebody<>(SB) +// R3 = addr of string +// R4 = len of string +// R5 = byte to find +// R16 = 1 if running on a POWER9 system, 0 otherwise +// On exit: +// R3 = return value TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0 MOVD R3,R17 // Save base address for calculating the index later. RLDICR $0,R3,$60,R8 // Align address to doubleword boundary in R8. RLDIMI $8,R5,$48,R5 // Replicating the byte across the register. ADD R4,R3,R7 // Last acceptable address in R7. - DCBT (R8) // Prepare cache line. RLDIMI $16,R5,$32,R5 CMPU R4,$32 // Check if it's a small string (≤32 bytes). Those will be processed differently. MOVD $-1,R9 - WORD $0x54661EB8 // Calculate padding in R6 (rlwinm r6,r3,3,26,28). + RLWNM $3,R3,$26,$28,R6 // shift amount for mask (r3&0x7)*8 RLDIMI $32,R5,$0,R5 MOVD R7,R10 // Save last acceptable address in R10 for later. ADD $-1,R7,R7 @@ -41,8 +45,77 @@ TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0 #else SRD R6,R9,R9 // Same for Big Endian #endif - BLE small_string // Jump to the small string case if it's ≤32 bytes. - + BLT small_string // Jump to the small string case if it's <32 bytes. + CMP R16,$1 // optimize for power8 v power9 + BNE power8 + VSPLTISB $3,V10 // Use V10 as control for VBPERMQ + MTVRD R5,V1 + LVSL (R0+R0),V11 // set up the permute vector such that V10 has {0x78, .., 0x8, 0x0} + VSLB V11,V10,V10 // to extract the first bit of match result into GPR + VSPLTB $7,V1,V1 // Replicate byte across V1 + CMP R4,$64 + MOVD $16,R11 + MOVD R3,R8 + BLT cmp32 + MOVD $32,R12 + MOVD $48,R6 + +loop64: + LXVB16X (R0)(R8),V2 // scan 64 bytes at a time + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat0 // match found at R8, jump out + + LXVB16X (R8)(R11),V2 + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat1 // match found at R8+16 bytes, jump out + + LXVB16X (R8)(R12),V2 + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat2 // match found at R8+32 bytes, jump out + + LXVB16X (R8)(R6),V2 + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat3 // match found at R8+48 bytes, jump out + ADD $64,R8 + ADD $-64,R4 + CMP R4,$64 // >=64 bytes left to scan? + BGE loop64 + CMP R4,$32 + BLT rem // jump to rem if there are < 32 bytes left +cmp32: + LXVB16X (R0)(R8),V2 // 32-63 bytes left + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat0 // match found at R8 + + LXVB16X (R11)(R8),V2 + VCMPEQUBCC V2,V1,V6 + BNE CR6,foundat1 // match found at R8+16 + + ADD $32,R8 + ADD $-32,R4 +rem: + RLDICR $0,R8,$60,R8 // align address to reuse code for tail end processing + BR small_string + +foundat3: + ADD $16,R8 +foundat2: + ADD $16,R8 +foundat1: + ADD $16,R8 +foundat0: + // Compress the result into a single doubleword and + // move it to a GPR for the final calculation. + VBPERMQ V6,V10,V6 + MFVRD V6,R3 + // count leading zeroes upto the match that ends up in low 16 bits + // in both endian modes, compute index by subtracting the number by 16 + CNTLZW R3,R11 + ADD $-16,R11 + ADD R8,R11,R3 // Calculate byte address + SUB R17,R3 + RET +power8: // If we are 64-byte aligned, branch to qw_align just to get the auxiliary values // in V0, V1 and V10, then branch to the preloop. ANDCC $63,R3,R11 @@ -52,7 +125,6 @@ TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0 MOVD 0(R8),R12 // Load one doubleword from the aligned address in R8. CMPB R12,R5,R3 // Check for a match. AND R9,R3,R3 // Mask bytes below s_base - RLDICL $0,R7,$61,R6 // length-1 RLDICR $0,R7,$60,R7 // Last doubleword in R7 CMPU R3,$0,CR7 // If we have a match, jump to the final computation BNE CR7,done @@ -185,8 +257,7 @@ tail: BNE CR6,found_qw_align notfound: - MOVD $-1,R3 - MOVD R3,(R14) + MOVD $-1, R3 RET found: @@ -228,8 +299,7 @@ found: ADD R8,R11,R3 // Calculate byte address return: - SUB R17,R3 - MOVD R3,(R14) + SUB R17, R3 RET found_qw_align: @@ -252,8 +322,13 @@ found_qw_align: CMPU R11,R4 BLT return BR notfound + PCALIGN $16 done: + ADD $-1,R10,R6 + // Offset of last index for the final + // doubleword comparison + RLDICL $0,R6,$61,R6 // At this point, R3 has 0xFF in the same position as the byte we are // looking for in the doubleword. Use that to calculate the exact index // of the byte. @@ -273,6 +348,7 @@ done: BR notfound small_string: + // process string of length < 32 bytes // We unroll this loop for better performance. CMPU R4,$0 // Check for length=0 BEQ notfound @@ -281,7 +357,6 @@ small_string: CMPB R12,R5,R3 // Check for a match. AND R9,R3,R3 // Mask bytes below s_base. CMPU R3,$0,CR7 // If we have a match, jump to the final computation. - RLDICL $0,R7,$61,R6 // length-1 RLDICR $0,R7,$60,R7 // Last doubleword in R7. CMPU R8,R7 BNE CR7,done diff --git a/src/internal/bytealg/indexbyte_riscv64.s b/src/internal/bytealg/indexbyte_riscv64.s index 156c3036997e81..a20396592b9a4f 100644 --- a/src/internal/bytealg/indexbyte_riscv64.s +++ b/src/internal/bytealg/indexbyte_riscv64.s @@ -5,48 +5,69 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·IndexByte(SB),NOSPLIT,$0-40 - MOV b_base+0(FP), A1 - MOV b_len+8(FP), A2 - MOVBU c+24(FP), A3 // byte to find - MOV A1, A4 // store base for later - ADD A1, A2 // end - ADD $-1, A1 +TEXT ·IndexByte(SB),NOSPLIT,$0-40 +#ifndef GOEXPERIMENT_regabiargs + MOV b_base+0(FP), X10 + MOV b_len+8(FP), X11 + MOVBU c+24(FP), X13 // byte to find +#endif + // X10 = b_base + // X11 = b_len + // X12 = b_cap (unused) + // X13 = byte to find + AND $0xff, X13 + MOV X10, X12 // store base for later + ADD X10, X11 // end + ADD $-1, X10 loop: - ADD $1, A1 - BEQ A1, A2, notfound - MOVBU (A1), A5 - BNE A3, A5, loop + ADD $1, X10 + BEQ X10, X11, notfound + MOVBU (X10), X14 + BNE X13, X14, loop - SUB A4, A1 // remove base - MOV A1, ret+32(FP) + SUB X12, X10 // remove base +#ifndef GOEXPERIMENT_regabiargs + MOV X10, ret+32(FP) +#endif RET notfound: - MOV $-1, A1 - MOV A1, ret+32(FP) + MOV $-1, X10 +#ifndef GOEXPERIMENT_regabiargs + MOV X10, ret+32(FP) +#endif RET -TEXT ·IndexByteString(SB),NOSPLIT,$0-32 - MOV s_base+0(FP), A1 - MOV s_len+8(FP), A2 - MOVBU c+16(FP), A3 // byte to find - MOV A1, A4 // store base for later - ADD A1, A2 // end - ADD $-1, A1 +TEXT ·IndexByteString(SB),NOSPLIT,$0-32 +#ifndef GOEXPERIMENT_regabiargs + MOV s_base+0(FP), X10 + MOV s_len+8(FP), X11 + MOVBU c+16(FP), X12 // byte to find +#endif + // X10 = b_base + // X11 = b_len + // X12 = byte to find + AND $0xff, X12 + MOV X10, X13 // store base for later + ADD X10, X11 // end + ADD $-1, X10 loop: - ADD $1, A1 - BEQ A1, A2, notfound - MOVBU (A1), A5 - BNE A3, A5, loop + ADD $1, X10 + BEQ X10, X11, notfound + MOVBU (X10), X14 + BNE X12, X14, loop - SUB A4, A1 // remove base - MOV A1, ret+24(FP) + SUB X13, X10 // remove base +#ifndef GOEXPERIMENT_regabiargs + MOV X10, ret+24(FP) +#endif RET notfound: - MOV $-1, A1 - MOV A1, ret+24(FP) + MOV $-1, X10 +#ifndef GOEXPERIMENT_regabiargs + MOV X10, ret+24(FP) +#endif RET diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go index 815994b679ac3c..78664d7a96ddab 100644 --- a/src/internal/cfg/cfg.go +++ b/src/internal/cfg/cfg.go @@ -33,6 +33,7 @@ const KnownEnv = ` GCCGO GO111MODULE GO386 + GOAMD64 GOARCH GOARM GOBIN @@ -61,6 +62,7 @@ const KnownEnv = ` GOTOOLDIR GOVCS GOWASM + GOWORK GO_EXTLINK_ENABLED PKG_CONFIG ` diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go index dab5d068ef36e4..2d3fae12ae4dc3 100644 --- a/src/internal/cpu/cpu.go +++ b/src/internal/cpu/cpu.go @@ -36,7 +36,8 @@ var X86 struct { HasOSXSAVE bool HasPCLMULQDQ bool HasPOPCNT bool - HasSSE2 bool + HasRDTSCP bool + HasSHA bool HasSSE3 bool HasSSSE3 bool HasSSE41 bool @@ -61,6 +62,7 @@ var ARM64 struct { HasPMULL bool HasSHA1 bool HasSHA2 bool + HasSHA512 bool HasCRC32 bool HasATOMICS bool HasCPUID bool @@ -81,12 +83,13 @@ var MIPS64X struct { // those as well. The minimum processor requirement is POWER8 (ISA 2.07). // The struct is padded to avoid false sharing. var PPC64 struct { - _ CacheLinePad - HasDARN bool // Hardware random number generator (requires kernel enablement) - HasSCV bool // Syscall vectored (requires kernel enablement) - IsPOWER8 bool // ISA v2.07 (POWER8) - IsPOWER9 bool // ISA v3.00 (POWER9) - _ CacheLinePad + _ CacheLinePad + HasDARN bool // Hardware random number generator (requires kernel enablement) + HasSCV bool // Syscall vectored (requires kernel enablement) + IsPOWER8 bool // ISA v2.07 (POWER8) + IsPOWER9 bool // ISA v3.00 (POWER9) + IsPOWER10 bool // ISA v3.1 (POWER10) + _ CacheLinePad } var S390X struct { @@ -136,7 +139,6 @@ type option struct { Feature *bool Specified bool // whether feature value was specified in GODEBUG Enable bool // whether feature should be enabled - Required bool // whether feature is mandatory and can not be disabled } // processOptions enables or disables CPU feature values based on the parsed env string. @@ -179,7 +181,7 @@ field: if key == "all" { for i := range options { options[i].Specified = true - options[i].Enable = enable || options[i].Required + options[i].Enable = enable } continue field } @@ -205,11 +207,6 @@ field: continue } - if !o.Enable && o.Required { - print("GODEBUG: can not disable \"", o.Name, "\", required CPU feature\n") - continue - } - *o.Feature = o.Enable } } diff --git a/src/internal/cpu/cpu_386.go b/src/internal/cpu/cpu_386.go deleted file mode 100644 index 561c81f8083518..00000000000000 --- a/src/internal/cpu/cpu_386.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cpu - -const GOARCH = "386" diff --git a/src/internal/cpu/cpu_amd64.go b/src/internal/cpu/cpu_amd64.go deleted file mode 100644 index 9b0015362d53fb..00000000000000 --- a/src/internal/cpu/cpu_amd64.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cpu - -const GOARCH = "amd64" diff --git a/src/internal/cpu/cpu_arm64.go b/src/internal/cpu/cpu_arm64.go index f64d9e4dd3100a..18ec636112ed7f 100644 --- a/src/internal/cpu/cpu_arm64.go +++ b/src/internal/cpu/cpu_arm64.go @@ -4,7 +4,10 @@ package cpu -const CacheLinePadSize = 64 +// CacheLinePadSize is used to prevent false sharing of cache lines. +// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size. +// It doesn't cost much and is much more future-proof. +const CacheLinePadSize = 128 func doinit() { options = []option{ @@ -12,6 +15,7 @@ func doinit() { {Name: "pmull", Feature: &ARM64.HasPMULL}, {Name: "sha1", Feature: &ARM64.HasSHA1}, {Name: "sha2", Feature: &ARM64.HasSHA2}, + {Name: "sha512", Feature: &ARM64.HasSHA512}, {Name: "crc32", Feature: &ARM64.HasCRC32}, {Name: "atomics", Feature: &ARM64.HasATOMICS}, {Name: "cpuid", Feature: &ARM64.HasCPUID}, diff --git a/src/internal/cpu/cpu_arm64_android.go b/src/internal/cpu/cpu_arm64_android.go index ac6eee54b21138..fbdf7baca2f14d 100644 --- a/src/internal/cpu/cpu_arm64_android.go +++ b/src/internal/cpu/cpu_arm64_android.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 -// +build arm64 package cpu diff --git a/src/internal/cpu/cpu_arm64_darwin.go b/src/internal/cpu/cpu_arm64_darwin.go index ce1b250a189137..60beadddbb1d60 100644 --- a/src/internal/cpu/cpu_arm64_darwin.go +++ b/src/internal/cpu/cpu_arm64_darwin.go @@ -3,13 +3,13 @@ // license that can be found in the LICENSE file. //go:build arm64 && darwin && !ios -// +build arm64,darwin,!ios package cpu func osInit() { ARM64.HasATOMICS = sysctlEnabled([]byte("hw.optional.armv8_1_atomics\x00")) ARM64.HasCRC32 = sysctlEnabled([]byte("hw.optional.armv8_crc32\x00")) + ARM64.HasSHA512 = sysctlEnabled([]byte("hw.optional.armv8_2_sha512\x00")) // There are no hw.optional sysctl values for the below features on Mac OS 11.0 // to detect their supported state dynamically. Assume the CPU features that diff --git a/src/internal/cpu/cpu_arm64_freebsd.go b/src/internal/cpu/cpu_arm64_freebsd.go index 8c481370da6577..c25e021c68f1c1 100644 --- a/src/internal/cpu/cpu_arm64_freebsd.go +++ b/src/internal/cpu/cpu_arm64_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 -// +build arm64 package cpu diff --git a/src/internal/cpu/cpu_arm64_hwcap.go b/src/internal/cpu/cpu_arm64_hwcap.go index 8ac04fd8f975b8..0baa39f9cf79b0 100644 --- a/src/internal/cpu/cpu_arm64_hwcap.go +++ b/src/internal/cpu/cpu_arm64_hwcap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && linux -// +build arm64,linux package cpu diff --git a/src/internal/cpu/cpu_arm64_linux.go b/src/internal/cpu/cpu_arm64_linux.go index c3a3f9a8e9d64d..d746bdb063ed96 100644 --- a/src/internal/cpu/cpu_arm64_linux.go +++ b/src/internal/cpu/cpu_arm64_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && linux && !android -// +build arm64,linux,!android package cpu diff --git a/src/internal/cpu/cpu_arm64_openbsd.go b/src/internal/cpu/cpu_arm64_openbsd.go new file mode 100644 index 00000000000000..2b284ebd03f0b9 --- /dev/null +++ b/src/internal/cpu/cpu_arm64_openbsd.go @@ -0,0 +1,60 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build arm64 + +package cpu + +const ( + // From OpenBSD's sys/sysctl.h. + _CTL_MACHDEP = 7 + + // From OpenBSD's machine/cpu.h. + _CPU_ID_AA64ISAR0 = 2 + _CPU_ID_AA64ISAR1 = 3 +) + +func extractBits(data uint64, start, end uint) uint { + return (uint)(data>>start) & ((1 << (end - start + 1)) - 1) +} + +//go:noescape +func sysctlUint64(mib []uint32) (uint64, bool) + +func osInit() { + // Get ID_AA64ISAR0 from sysctl. + isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0}) + if !ok { + return + } + + // ID_AA64ISAR0_EL1 + switch extractBits(isar0, 4, 7) { + case 1: + ARM64.HasAES = true + case 2: + ARM64.HasAES = true + ARM64.HasPMULL = true + } + + switch extractBits(isar0, 8, 11) { + case 1: + ARM64.HasSHA1 = true + } + + switch extractBits(isar0, 12, 15) { + case 1, 2: + ARM64.HasSHA2 = true + } + + switch extractBits(isar0, 16, 19) { + case 1: + ARM64.HasCRC32 = true + } + + switch extractBits(isar0, 20, 23) { + case 2: + ARM64.HasATOMICS = true + } +} diff --git a/src/internal/cpu/cpu_arm64_other.go b/src/internal/cpu/cpu_arm64_other.go index e8b5d529a4bbd6..44592cfcede8e1 100644 --- a/src/internal/cpu/cpu_arm64_other.go +++ b/src/internal/cpu/cpu_arm64_other.go @@ -2,12 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build arm64 && !linux && !freebsd && !android && (!darwin || ios) -// +build arm64 -// +build !linux -// +build !freebsd -// +build !android -// +build !darwin ios +//go:build arm64 && !linux && !freebsd && !android && (!darwin || ios) && !openbsd package cpu diff --git a/src/internal/cpu/cpu_loong64.go b/src/internal/cpu/cpu_loong64.go new file mode 100644 index 00000000000000..1c90c24fe318d9 --- /dev/null +++ b/src/internal/cpu/cpu_loong64.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package cpu + +// CacheLinePadSize is used to prevent false sharing of cache lines. +// We choose 64 because Loongson 3A5000 the L1 Dcache is 4-way 256-line 64-byte-per-line. +const CacheLinePadSize = 64 + +func doinit() {} diff --git a/src/internal/cpu/cpu_mips64x.go b/src/internal/cpu/cpu_mips64x.go index d2f9d4499ba375..c452ffd8b30216 100644 --- a/src/internal/cpu/cpu_mips64x.go +++ b/src/internal/cpu/cpu_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package cpu diff --git a/src/internal/cpu/cpu_no_name.go b/src/internal/cpu/cpu_no_name.go index 8d563b536c3364..2adfa1b70994f3 100644 --- a/src/internal/cpu/cpu_no_name.go +++ b/src/internal/cpu/cpu_no_name.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !386 && !amd64 -// +build !386,!amd64 +//go:build !386 && !amd64 && !ppc64 && !ppc64le package cpu diff --git a/src/internal/cpu/cpu_ppc64x.go b/src/internal/cpu/cpu_ppc64x.go index 2e7fd3ebb9f75f..c4a08fe1bd9504 100644 --- a/src/internal/cpu/cpu_ppc64x.go +++ b/src/internal/cpu/cpu_ppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package cpu @@ -22,3 +21,15 @@ func doinit() { func isSet(hwc uint, value uint) bool { return hwc&value != 0 } + +func Name() string { + switch { + case PPC64.IsPOWER10: + return "POWER10" + case PPC64.IsPOWER9: + return "POWER9" + case PPC64.IsPOWER8: + return "POWER8" + } + return "" +} diff --git a/src/internal/cpu/cpu_ppc64x_aix.go b/src/internal/cpu/cpu_ppc64x_aix.go index 3d17a9c7304a56..f05ed6fad8a5e9 100644 --- a/src/internal/cpu/cpu_ppc64x_aix.go +++ b/src/internal/cpu/cpu_ppc64x_aix.go @@ -3,19 +3,22 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package cpu const ( // getsystemcfg constants - _SC_IMPL = 2 - _IMPL_POWER9 = 0x20000 + _SC_IMPL = 2 + _IMPL_POWER8 = 0x10000 + _IMPL_POWER9 = 0x20000 + _IMPL_POWER10 = 0x40000 ) func osinit() { impl := getsystemcfg(_SC_IMPL) + PPC64.IsPOWER8 = isSet(impl, _IMPL_POWER8) PPC64.IsPOWER9 = isSet(impl, _IMPL_POWER9) + PPC64.IsPOWER10 = isSet(impl, _IMPL_POWER10) } // getsystemcfg is defined in runtime/os2_aix.go diff --git a/src/internal/cpu/cpu_ppc64x_linux.go b/src/internal/cpu/cpu_ppc64x_linux.go index b7c73451118b5a..9df82ca8a50bb9 100644 --- a/src/internal/cpu/cpu_ppc64x_linux.go +++ b/src/internal/cpu/cpu_ppc64x_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package cpu @@ -16,7 +15,9 @@ var HWCap2 uint // HWCAP bits. These are exposed by Linux. const ( // ISA Level + hwcap2_ARCH_2_07 = 0x80000000 hwcap2_ARCH_3_00 = 0x00800000 + hwcap2_ARCH_3_1 = 0x00040000 // CPU features hwcap2_DARN = 0x00200000 @@ -24,7 +25,9 @@ const ( ) func osinit() { + PPC64.IsPOWER8 = isSet(HWCap2, hwcap2_ARCH_2_07) PPC64.IsPOWER9 = isSet(HWCap2, hwcap2_ARCH_3_00) + PPC64.IsPOWER10 = isSet(HWCap2, hwcap2_ARCH_3_1) PPC64.HasDARN = isSet(HWCap2, hwcap2_DARN) PPC64.HasSCV = isSet(HWCap2, hwcap2_SCV) } diff --git a/src/internal/cpu/cpu_test.go b/src/internal/cpu/cpu_test.go index 2de7365732d748..c95cd517266013 100644 --- a/src/internal/cpu/cpu_test.go +++ b/src/internal/cpu/cpu_test.go @@ -6,38 +6,20 @@ package cpu_test import ( . "internal/cpu" + "internal/godebug" "internal/testenv" "os" "os/exec" - "runtime" - "strings" "testing" ) -func TestMinimalFeatures(t *testing.T) { - // TODO: maybe do MustSupportFeatureDectection(t) ? - if runtime.GOARCH == "arm64" { - switch runtime.GOOS { - case "linux", "android", "darwin": - default: - t.Skipf("%s/%s is not supported", runtime.GOOS, runtime.GOARCH) - } - } - - for _, o := range Options { - if o.Required && !*o.Feature { - t.Errorf("%v expected true, got false", o.Name) - } - } -} - func MustHaveDebugOptionsSupport(t *testing.T) { if !DebugOptions { t.Skipf("skipping test: cpu feature options not supported by OS") } } -func MustSupportFeatureDectection(t *testing.T) { +func MustSupportFeatureDetection(t *testing.T) { // TODO: add platforms that do not have CPU feature detection support. } @@ -52,30 +34,26 @@ func runDebugOptionsTest(t *testing.T, test string, options string) { cmd.Env = append(cmd.Env, env) output, err := cmd.CombinedOutput() - lines := strings.Fields(string(output)) - lastline := lines[len(lines)-1] - - got := strings.TrimSpace(lastline) - want := "PASS" - if err != nil || got != want { - t.Fatalf("%s with %s: want %s, got %v", test, env, want, got) + if err != nil { + t.Fatalf("%s with %s: run failed: %v output:\n%s\n", + test, env, err, string(output)) } } func TestDisableAllCapabilities(t *testing.T) { - MustSupportFeatureDectection(t) + MustSupportFeatureDetection(t) runDebugOptionsTest(t, "TestAllCapabilitiesDisabled", "cpu.all=off") } func TestAllCapabilitiesDisabled(t *testing.T) { MustHaveDebugOptionsSupport(t) - if os.Getenv("GODEBUG") != "cpu.all=off" { + if godebug.Get("cpu.all") != "off" { t.Skipf("skipping test: GODEBUG=cpu.all=off not set") } for _, o := range Options { - want := o.Required + want := false if got := *o.Feature; got != want { t.Errorf("%v: expected %v, got %v", o.Name, want, got) } diff --git a/src/internal/cpu/cpu_x86.go b/src/internal/cpu/cpu_x86.go index fd1217a05d7cab..96b8ef92b560df 100644 --- a/src/internal/cpu/cpu_x86.go +++ b/src/internal/cpu/cpu_x86.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 -// +build 386 amd64 package cpu @@ -15,6 +14,9 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) // xgetbv with ecx = 0 is implemented in cpu_x86.s. func xgetbv() (eax, edx uint32) +// getGOAMD64level is implemented in cpu_x86.s. Returns number in [1,4]. +func getGOAMD64level() int32 + const ( // edx bits cpuid_SSE2 = 1 << 26 @@ -37,6 +39,10 @@ const ( cpuid_BMI2 = 1 << 8 cpuid_ERMS = 1 << 9 cpuid_ADX = 1 << 19 + cpuid_SHA = 1 << 29 + + // edx bits for CPUID 0x80000001 + cpuid_RDTSCP = 1 << 27 ) var maxExtendedFunctionInformation uint32 @@ -45,21 +51,31 @@ func doinit() { options = []option{ {Name: "adx", Feature: &X86.HasADX}, {Name: "aes", Feature: &X86.HasAES}, - {Name: "avx", Feature: &X86.HasAVX}, - {Name: "avx2", Feature: &X86.HasAVX2}, - {Name: "bmi1", Feature: &X86.HasBMI1}, - {Name: "bmi2", Feature: &X86.HasBMI2}, {Name: "erms", Feature: &X86.HasERMS}, - {Name: "fma", Feature: &X86.HasFMA}, {Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ}, - {Name: "popcnt", Feature: &X86.HasPOPCNT}, - {Name: "sse3", Feature: &X86.HasSSE3}, - {Name: "sse41", Feature: &X86.HasSSE41}, - {Name: "sse42", Feature: &X86.HasSSE42}, - {Name: "ssse3", Feature: &X86.HasSSSE3}, - - // These capabilities should always be enabled on amd64: - {Name: "sse2", Feature: &X86.HasSSE2, Required: GOARCH == "amd64"}, + {Name: "rdtscp", Feature: &X86.HasRDTSCP}, + {Name: "sha", Feature: &X86.HasSHA}, + } + level := getGOAMD64level() + if level < 2 { + // These options are required at level 2. At lower levels + // they can be turned off. + options = append(options, + option{Name: "popcnt", Feature: &X86.HasPOPCNT}, + option{Name: "sse3", Feature: &X86.HasSSE3}, + option{Name: "sse41", Feature: &X86.HasSSE41}, + option{Name: "sse42", Feature: &X86.HasSSE42}, + option{Name: "ssse3", Feature: &X86.HasSSSE3}) + } + if level < 3 { + // These options are required at level 3. At lower levels + // they can be turned off. + options = append(options, + option{Name: "avx", Feature: &X86.HasAVX}, + option{Name: "avx2", Feature: &X86.HasAVX2}, + option{Name: "bmi1", Feature: &X86.HasBMI1}, + option{Name: "bmi2", Feature: &X86.HasBMI2}, + option{Name: "fma", Feature: &X86.HasFMA}) } maxID, _, _, _ := cpuid(0, 0) @@ -70,8 +86,7 @@ func doinit() { maxExtendedFunctionInformation, _, _, _ = cpuid(0x80000000, 0) - _, _, ecx1, edx1 := cpuid(1, 0) - X86.HasSSE2 = isSet(edx1, cpuid_SSE2) + _, _, ecx1, _ := cpuid(1, 0) X86.HasSSE3 = isSet(ecx1, cpuid_SSE3) X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ) @@ -112,6 +127,17 @@ func doinit() { X86.HasBMI2 = isSet(ebx7, cpuid_BMI2) X86.HasERMS = isSet(ebx7, cpuid_ERMS) X86.HasADX = isSet(ebx7, cpuid_ADX) + X86.HasSHA = isSet(ebx7, cpuid_SHA) + + var maxExtendedInformation uint32 + maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0) + + if maxExtendedInformation < 0x80000001 { + return + } + + _, _, _, edxExt1 := cpuid(0x80000001, 0) + X86.HasRDTSCP = isSet(edxExt1, cpuid_RDTSCP) } func isSet(hwc uint32, value uint32) bool { diff --git a/src/internal/cpu/cpu_x86.s b/src/internal/cpu/cpu_x86.s index 0df5da1cc74d45..2ee8eca248a614 100644 --- a/src/internal/cpu/cpu_x86.s +++ b/src/internal/cpu/cpu_x86.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 -// +build 386 amd64 #include "textflag.h" @@ -25,3 +24,20 @@ TEXT ·xgetbv(SB),NOSPLIT,$0-8 MOVL AX, eax+0(FP) MOVL DX, edx+4(FP) RET + +// func getGOAMD64level() int32 +TEXT ·getGOAMD64level(SB),NOSPLIT,$0-4 +#ifdef GOAMD64_v4 + MOVL $4, ret+0(FP) +#else +#ifdef GOAMD64_v3 + MOVL $3, ret+0(FP) +#else +#ifdef GOAMD64_v2 + MOVL $2, ret+0(FP) +#else + MOVL $1, ret+0(FP) +#endif +#endif +#endif + RET diff --git a/src/internal/cpu/cpu_x86_test.go b/src/internal/cpu/cpu_x86_test.go index e3e16cc1612e05..43d6b211ea4cf5 100644 --- a/src/internal/cpu/cpu_x86_test.go +++ b/src/internal/cpu/cpu_x86_test.go @@ -3,14 +3,12 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 -// +build 386 amd64 package cpu_test import ( . "internal/cpu" - "os" - "runtime" + "internal/godebug" "testing" ) @@ -20,31 +18,17 @@ func TestX86ifAVX2hasAVX(t *testing.T) { } } -func TestDisableSSE2(t *testing.T) { - runDebugOptionsTest(t, "TestSSE2DebugOption", "cpu.sse2=off") -} - -func TestSSE2DebugOption(t *testing.T) { - MustHaveDebugOptionsSupport(t) - - if os.Getenv("GODEBUG") != "cpu.sse2=off" { - t.Skipf("skipping test: GODEBUG=cpu.sse2=off not set") - } - - want := runtime.GOARCH != "386" // SSE2 can only be disabled on 386. - if got := X86.HasSSE2; got != want { - t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got) - } -} - func TestDisableSSE3(t *testing.T) { + if GetGOAMD64level() > 1 { + t.Skip("skipping test: can't run on GOAMD64>v1 machines") + } runDebugOptionsTest(t, "TestSSE3DebugOption", "cpu.sse3=off") } func TestSSE3DebugOption(t *testing.T) { MustHaveDebugOptionsSupport(t) - if os.Getenv("GODEBUG") != "cpu.sse3=off" { + if godebug.Get("cpu.sse3") != "off" { t.Skipf("skipping test: GODEBUG=cpu.sse3=off not set") } diff --git a/src/internal/cpu/export_x86_test.go b/src/internal/cpu/export_x86_test.go new file mode 100644 index 00000000000000..a12b6f27234a8e --- /dev/null +++ b/src/internal/cpu/export_x86_test.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build 386 || amd64 + +package cpu + +var ( + GetGOAMD64level = getGOAMD64level +) diff --git a/src/internal/dag/alg.go b/src/internal/dag/alg.go new file mode 100644 index 00000000000000..88002797c02a04 --- /dev/null +++ b/src/internal/dag/alg.go @@ -0,0 +1,63 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dag + +// Transpose reverses all edges in g. +func (g *Graph) Transpose() { + old := g.edges + + g.edges = make(map[string]map[string]bool) + for _, n := range g.Nodes { + g.edges[n] = make(map[string]bool) + } + + for from, tos := range old { + for to := range tos { + g.edges[to][from] = true + } + } +} + +// Topo returns a topological sort of g. This function is deterministic. +func (g *Graph) Topo() []string { + topo := make([]string, 0, len(g.Nodes)) + marks := make(map[string]bool) + + var visit func(n string) + visit = func(n string) { + if marks[n] { + return + } + for _, to := range g.Edges(n) { + visit(to) + } + marks[n] = true + topo = append(topo, n) + } + for _, root := range g.Nodes { + visit(root) + } + for i, j := 0, len(topo)-1; i < j; i, j = i+1, j-1 { + topo[i], topo[j] = topo[j], topo[i] + } + return topo +} + +// TransitiveReduction removes edges from g that are transitively +// reachable. g must be transitively closed. +func (g *Graph) TransitiveReduction() { + // For i -> j -> k, if i -> k exists, delete it. + for _, i := range g.Nodes { + for _, j := range g.Nodes { + if g.HasEdge(i, j) { + for _, k := range g.Nodes { + if g.HasEdge(j, k) { + g.DelEdge(i, k) + } + } + } + } + } +} diff --git a/src/internal/dag/alg_test.go b/src/internal/dag/alg_test.go new file mode 100644 index 00000000000000..e5ea8b6ab669b0 --- /dev/null +++ b/src/internal/dag/alg_test.go @@ -0,0 +1,46 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dag + +import ( + "reflect" + "strings" + "testing" +) + +func TestTranspose(t *testing.T) { + g := mustParse(t, diamond) + g.Transpose() + wantEdges(t, g, "a->b a->c a->d b->d c->d") +} + +func TestTopo(t *testing.T) { + g := mustParse(t, diamond) + got := g.Topo() + // "d" is the root, so it's first. + // + // "c" and "b" could be in either order, but Topo is + // deterministic in reverse node definition order. + // + // "a" is a leaf. + wantNodes := strings.Fields("d c b a") + if !reflect.DeepEqual(wantNodes, got) { + t.Fatalf("want topo sort %v, got %v", wantNodes, got) + } +} + +func TestTransitiveReduction(t *testing.T) { + t.Run("diamond", func(t *testing.T) { + g := mustParse(t, diamond) + g.TransitiveReduction() + wantEdges(t, g, "b->a c->a d->b d->c") + }) + t.Run("chain", func(t *testing.T) { + const chain = `NONE < a < b < c < d; a, d < e;` + g := mustParse(t, chain) + g.TransitiveReduction() + wantEdges(t, g, "e->d d->c c->b b->a") + }) +} diff --git a/src/internal/dag/parse.go b/src/internal/dag/parse.go new file mode 100644 index 00000000000000..9d5b918b1147cc --- /dev/null +++ b/src/internal/dag/parse.go @@ -0,0 +1,314 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package dag implements a language for expressing directed acyclic +// graphs. +// +// The general syntax of a rule is: +// +// a, b < c, d; +// +// which means c and d come after a and b in the partial order +// (that is, there are edges from c and d to a and b), +// but doesn't provide a relative order between a vs b or c vs d. +// +// The rules can chain together, as in: +// +// e < f, g < h; +// +// which is equivalent to +// +// e < f, g; +// f, g < h; +// +// Except for the special bottom element "NONE", each name +// must appear exactly once on the right-hand side of any rule. +// That rule serves as the definition of the allowed successor +// for that name. The definition must appear before any uses +// of the name on the left-hand side of a rule. (That is, the +// rules themselves must be ordered according to the partial +// order, for easier reading by people.) +// +// Negative assertions double-check the partial order: +// +// i !< j +// +// means that it must NOT be the case that i < j. +// Negative assertions may appear anywhere in the rules, +// even before i and j have been defined. +// +// Comments begin with #. +package dag + +import ( + "fmt" + "sort" + "strings" +) + +type Graph struct { + Nodes []string + byLabel map[string]int + edges map[string]map[string]bool +} + +func newGraph() *Graph { + return &Graph{byLabel: map[string]int{}, edges: map[string]map[string]bool{}} +} + +func (g *Graph) addNode(label string) bool { + if _, ok := g.byLabel[label]; ok { + return false + } + g.byLabel[label] = len(g.Nodes) + g.Nodes = append(g.Nodes, label) + g.edges[label] = map[string]bool{} + return true +} + +func (g *Graph) AddEdge(from, to string) { + g.edges[from][to] = true +} + +func (g *Graph) DelEdge(from, to string) { + delete(g.edges[from], to) +} + +func (g *Graph) HasEdge(from, to string) bool { + return g.edges[from] != nil && g.edges[from][to] +} + +func (g *Graph) Edges(from string) []string { + edges := make([]string, 0, 16) + for k := range g.edges[from] { + edges = append(edges, k) + } + sort.Slice(edges, func(i, j int) bool { return g.byLabel[edges[i]] < g.byLabel[edges[j]] }) + return edges +} + +// Parse parses the DAG language and returns the transitive closure of +// the described graph. In the returned graph, there is an edge from "b" +// to "a" if b < a (or a > b) in the partial order. +func Parse(dag string) (*Graph, error) { + g := newGraph() + disallowed := []rule{} + + rules, err := parseRules(dag) + if err != nil { + return nil, err + } + + // TODO: Add line numbers to errors. + var errors []string + errorf := func(format string, a ...any) { + errors = append(errors, fmt.Sprintf(format, a...)) + } + for _, r := range rules { + if r.op == "!<" { + disallowed = append(disallowed, r) + continue + } + for _, def := range r.def { + if def == "NONE" { + errorf("NONE cannot be a predecessor") + continue + } + if !g.addNode(def) { + errorf("multiple definitions for %s", def) + } + for _, less := range r.less { + if less == "NONE" { + continue + } + if _, ok := g.byLabel[less]; !ok { + errorf("use of %s before its definition", less) + } else { + g.AddEdge(def, less) + } + } + } + } + + // Check for missing definition. + for _, tos := range g.edges { + for to := range tos { + if g.edges[to] == nil { + errorf("missing definition for %s", to) + } + } + } + + // Complete transitive closure. + for _, k := range g.Nodes { + for _, i := range g.Nodes { + for _, j := range g.Nodes { + if i != k && k != j && g.HasEdge(i, k) && g.HasEdge(k, j) { + if i == j { + // Can only happen along with a "use of X before deps" error above, + // but this error is more specific - it makes clear that reordering the + // rules will not be enough to fix the problem. + errorf("graph cycle: %s < %s < %s", j, k, i) + } + g.AddEdge(i, j) + } + } + } + } + + // Check negative assertions against completed allowed graph. + for _, bad := range disallowed { + for _, less := range bad.less { + for _, def := range bad.def { + if g.HasEdge(def, less) { + errorf("graph edge assertion failed: %s !< %s", less, def) + } + } + } + } + + if len(errors) > 0 { + return nil, fmt.Errorf("%s", strings.Join(errors, "\n")) + } + + return g, nil +} + +// A rule is a line in the DAG language where "less < def" or "less !< def". +type rule struct { + less []string + op string // Either "<" or "!<" + def []string +} + +type syntaxError string + +func (e syntaxError) Error() string { + return string(e) +} + +// parseRules parses the rules of a DAG. +func parseRules(rules string) (out []rule, err error) { + defer func() { + e := recover() + switch e := e.(type) { + case nil: + return + case syntaxError: + err = e + default: + panic(e) + } + }() + p := &rulesParser{lineno: 1, text: rules} + + var prev []string + var op string + for { + list, tok := p.nextList() + if tok == "" { + if prev == nil { + break + } + p.syntaxError("unexpected EOF") + } + if prev != nil { + out = append(out, rule{prev, op, list}) + } + prev = list + if tok == ";" { + prev = nil + op = "" + continue + } + if tok != "<" && tok != "!<" { + p.syntaxError("missing <") + } + op = tok + } + + return out, err +} + +// A rulesParser parses the depsRules syntax described above. +type rulesParser struct { + lineno int + lastWord string + text string +} + +// syntaxError reports a parsing error. +func (p *rulesParser) syntaxError(msg string) { + panic(syntaxError(fmt.Sprintf("parsing graph: line %d: syntax error: %s near %s", p.lineno, msg, p.lastWord))) +} + +// nextList parses and returns a comma-separated list of names. +func (p *rulesParser) nextList() (list []string, token string) { + for { + tok := p.nextToken() + switch tok { + case "": + if len(list) == 0 { + return nil, "" + } + fallthrough + case ",", "<", "!<", ";": + p.syntaxError("bad list syntax") + } + list = append(list, tok) + + tok = p.nextToken() + if tok != "," { + return list, tok + } + } +} + +// nextToken returns the next token in the deps rules, +// one of ";" "," "<" "!<" or a name. +func (p *rulesParser) nextToken() string { + for { + if p.text == "" { + return "" + } + switch p.text[0] { + case ';', ',', '<': + t := p.text[:1] + p.text = p.text[1:] + return t + + case '!': + if len(p.text) < 2 || p.text[1] != '<' { + p.syntaxError("unexpected token !") + } + p.text = p.text[2:] + return "!<" + + case '#': + i := strings.Index(p.text, "\n") + if i < 0 { + i = len(p.text) + } + p.text = p.text[i:] + continue + + case '\n': + p.lineno++ + fallthrough + case ' ', '\t': + p.text = p.text[1:] + continue + + default: + i := strings.IndexAny(p.text, "!;,<#\n \t") + if i < 0 { + i = len(p.text) + } + t := p.text[:i] + p.text = p.text[i:] + p.lastWord = t + return t + } + } +} diff --git a/src/internal/dag/parse_test.go b/src/internal/dag/parse_test.go new file mode 100644 index 00000000000000..b2520c365946ab --- /dev/null +++ b/src/internal/dag/parse_test.go @@ -0,0 +1,61 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dag + +import ( + "reflect" + "strings" + "testing" +) + +const diamond = ` +NONE < a < b, c < d; +` + +func mustParse(t *testing.T, dag string) *Graph { + t.Helper() + g, err := Parse(dag) + if err != nil { + t.Fatal(err) + } + return g +} + +func wantEdges(t *testing.T, g *Graph, edges string) { + t.Helper() + + wantEdges := strings.Fields(edges) + wantEdgeMap := make(map[string]bool) + for _, e := range wantEdges { + wantEdgeMap[e] = true + } + + for _, n1 := range g.Nodes { + for _, n2 := range g.Nodes { + got := g.HasEdge(n1, n2) + want := wantEdgeMap[n1+"->"+n2] + if got && want { + t.Logf("%s->%s", n1, n2) + } else if got && !want { + t.Errorf("%s->%s present but not expected", n1, n2) + } else if want && !got { + t.Errorf("%s->%s missing but expected", n1, n2) + } + } + } +} + +func TestParse(t *testing.T) { + // Basic smoke test for graph parsing. + g := mustParse(t, diamond) + + wantNodes := strings.Fields("a b c d") + if !reflect.DeepEqual(wantNodes, g.Nodes) { + t.Fatalf("want nodes %v, got %v", wantNodes, g.Nodes) + } + + // Parse returns the transitive closure, so it adds d->a. + wantEdges(t, g, "b->a c->a d->a d->b d->c") +} diff --git a/src/internal/diff/diff.go b/src/internal/diff/diff.go new file mode 100644 index 00000000000000..47b285671454ed --- /dev/null +++ b/src/internal/diff/diff.go @@ -0,0 +1,261 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diff + +import ( + "bytes" + "fmt" + "sort" + "strings" +) + +// A pair is a pair of values tracked for both the x and y side of a diff. +// It is typically a pair of line indexes. +type pair struct{ x, y int } + +// Diff returns an anchored diff of the two texts old and new +// in the “unified diff” format. If old and new are identical, +// Diff returns a nil slice (no output). +// +// Unix diff implementations typically look for a diff with +// the smallest number of lines inserted and removed, +// which can in the worst case take time quadratic in the +// number of lines in the texts. As a result, many implementations +// either can be made to run for a long time or cut off the search +// after a predetermined amount of work. +// +// In contrast, this implementation looks for a diff with the +// smallest number of “unique” lines inserted and removed, +// where unique means a line that appears just once in both old and new. +// We call this an “anchored diff” because the unique lines anchor +// the chosen matching regions. An anchored diff is usually clearer +// than a standard diff, because the algorithm does not try to +// reuse unrelated blank lines or closing braces. +// The algorithm also guarantees to run in O(n log n) time +// instead of the standard O(n²) time. +// +// Some systems call this approach a “patience diff,” named for +// the “patience sorting” algorithm, itself named for a solitaire card game. +// We avoid that name for two reasons. First, the name has been used +// for a few different variants of the algorithm, so it is imprecise. +// Second, the name is frequently interpreted as meaning that you have +// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm, +// when in fact the algorithm is faster than the standard one. +func Diff(oldName string, old []byte, newName string, new []byte) []byte { + if bytes.Equal(old, new) { + return nil + } + x := lines(old) + y := lines(new) + + // Print diff header. + var out bytes.Buffer + fmt.Fprintf(&out, "diff %s %s\n", oldName, newName) + fmt.Fprintf(&out, "--- %s\n", oldName) + fmt.Fprintf(&out, "+++ %s\n", newName) + + // Loop over matches to consider, + // expanding each match to include surrounding lines, + // and then printing diff chunks. + // To avoid setup/teardown cases outside the loop, + // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair + // in the sequence of matches. + var ( + done pair // printed up to x[:done.x] and y[:done.y] + chunk pair // start lines of current chunk + count pair // number of lines from each side in current chunk + ctext []string // lines for current chunk + ) + for _, m := range tgs(x, y) { + if m.x < done.x { + // Already handled scanning forward from earlier match. + continue + } + + // Expand matching lines as far possible, + // establishing that x[start.x:end.x] == y[start.y:end.y]. + // Note that on the first (or last) iteration we may (or definitey do) + // have an empty match: start.x==end.x and start.y==end.y. + start := m + for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { + start.x-- + start.y-- + } + end := m + for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] { + end.x++ + end.y++ + } + + // Emit the mismatched lines before start into this chunk. + // (No effect on first sentinel iteration, when start = {0,0}.) + for _, s := range x[done.x:start.x] { + ctext = append(ctext, "-"+s) + count.x++ + } + for _, s := range y[done.y:start.y] { + ctext = append(ctext, "+"+s) + count.y++ + } + + // If we're not at EOF and have too few common lines, + // the chunk includes all the common lines and continues. + const C = 3 // number of context lines + if (end.x < len(x) || end.y < len(y)) && + (end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) { + for _, s := range x[start.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + continue + } + + // End chunk with common lines for context. + if len(ctext) > 0 { + n := end.x - start.x + if n > C { + n = C + } + for _, s := range x[start.x : start.x+n] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = pair{start.x + n, start.y + n} + + // Format and emit chunk. + // Convert line numbers to 1-indexed. + // Special case: empty file shows up as 0,0 not 1,0. + if count.x > 0 { + chunk.x++ + } + if count.y > 0 { + chunk.y++ + } + fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y) + for _, s := range ctext { + out.WriteString(s) + } + count.x = 0 + count.y = 0 + ctext = ctext[:0] + } + + // If we reached EOF, we're done. + if end.x >= len(x) && end.y >= len(y) { + break + } + + // Otherwise start a new chunk. + chunk = pair{end.x - C, end.y - C} + for _, s := range x[chunk.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + } + + return out.Bytes() +} + +// lines returns the lines in the file x, including newlines. +// If the file does not end in a newline, one is supplied +// along with a warning about the missing newline. +func lines(x []byte) []string { + l := strings.SplitAfter(string(x), "\n") + if l[len(l)-1] == "" { + l = l[:len(l)-1] + } else { + // Treat last line as having a message about the missing newline attached, + // using the same text as BSD/GNU diff (including the leading backslash). + l[len(l)-1] += "\n\\ No newline at end of file\n" + } + return l +} + +// tgs returns the pairs of indexes of the longest common subsequence +// of unique lines in x and y, where a unique line is one that appears +// once in x and once in y. +// +// The longest common subsequence algorithm is as described in +// Thomas G. Szymanski, “A Special Case of the Maximal Common +// Subsequence Problem,” Princeton TR #170 (January 1975), +// available at https://research.swtch.com/tgs170.pdf. +func tgs(x, y []string) []pair { + // Count the number of times each string appears in a and b. + // We only care about 0, 1, many, counted as 0, -1, -2 + // for the x side and 0, -4, -8 for the y side. + // Using negative numbers now lets us distinguish positive line numbers later. + m := make(map[string]int) + for _, s := range x { + if c := m[s]; c > -2 { + m[s] = c - 1 + } + } + for _, s := range y { + if c := m[s]; c > -8 { + m[s] = c - 4 + } + } + + // Now unique strings can be identified by m[s] = -1+-4. + // + // Gather the indexes of those strings in x and y, building: + // xi[i] = increasing indexes of unique strings in x. + // yi[i] = increasing indexes of unique strings in y. + // inv[i] = index j such that x[xi[i]] = y[yi[j]]. + var xi, yi, inv []int + for i, s := range y { + if m[s] == -1+-4 { + m[s] = len(yi) + yi = append(yi, i) + } + } + for i, s := range x { + if j, ok := m[s]; ok && j >= 0 { + xi = append(xi, i) + inv = append(inv, j) + } + } + + // Apply Algorithm A from Szymanski's paper. + // In those terms, A = J = inv and B = [0, n). + // We add sentinel pairs {0,0}, and {len(x),len(y)} + // to the returned sequence, to help the processing loop. + J := inv + n := len(xi) + T := make([]int, n) + L := make([]int, n) + for i := range T { + T[i] = n + 1 + } + for i := 0; i < n; i++ { + k := sort.Search(n, func(k int) bool { + return T[k] >= J[i] + }) + T[k] = J[i] + L[i] = k + 1 + } + k := 0 + for _, v := range L { + if k < v { + k = v + } + } + seq := make([]pair, 2+k) + seq[1+k] = pair{len(x), len(y)} // sentinel at end + lastj := n + for i := n - 1; i >= 0; i-- { + if L[i] == k && J[i] < lastj { + seq[k] = pair{xi[i], yi[J[i]]} + k-- + } + } + seq[0] = pair{0, 0} // sentinel at start + return seq +} diff --git a/src/internal/diff/diff_test.go b/src/internal/diff/diff_test.go new file mode 100644 index 00000000000000..37281c529b9128 --- /dev/null +++ b/src/internal/diff/diff_test.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diff + +import ( + "bytes" + "internal/txtar" + "path/filepath" + "testing" +) + +func clean(text []byte) []byte { + text = bytes.ReplaceAll(text, []byte("$\n"), []byte("\n")) + text = bytes.TrimSuffix(text, []byte("^D\n")) + return text +} + +func Test(t *testing.T) { + files, _ := filepath.Glob("testdata/*.txt") + if len(files) == 0 { + t.Fatalf("no testdata") + } + + for _, file := range files { + t.Run(filepath.Base(file), func(t *testing.T) { + a, err := txtar.ParseFile(file) + if err != nil { + t.Fatal(err) + } + if len(a.Files) != 3 || a.Files[2].Name != "diff" { + t.Fatalf("%s: want three files, third named \"diff\"", file) + } + diffs := Diff(a.Files[0].Name, clean(a.Files[0].Data), a.Files[1].Name, clean(a.Files[1].Data)) + want := clean(a.Files[2].Data) + if !bytes.Equal(diffs, want) { + t.Fatalf("%s: have:\n%s\nwant:\n%s\n%s", file, + diffs, want, Diff("have", diffs, "want", want)) + } + }) + } +} diff --git a/src/internal/diff/testdata/allnew.txt b/src/internal/diff/testdata/allnew.txt new file mode 100644 index 00000000000000..887564927ab233 --- /dev/null +++ b/src/internal/diff/testdata/allnew.txt @@ -0,0 +1,13 @@ +-- old -- +-- new -- +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -0,0 +1,3 @@ ++a ++b ++c diff --git a/src/internal/diff/testdata/allold.txt b/src/internal/diff/testdata/allold.txt new file mode 100644 index 00000000000000..bcc9ac0ee05be6 --- /dev/null +++ b/src/internal/diff/testdata/allold.txt @@ -0,0 +1,13 @@ +-- old -- +a +b +c +-- new -- +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +0,0 @@ +-a +-b +-c diff --git a/src/internal/diff/testdata/basic.txt b/src/internal/diff/testdata/basic.txt new file mode 100644 index 00000000000000..d2565b5d6ed3be --- /dev/null +++ b/src/internal/diff/testdata/basic.txt @@ -0,0 +1,35 @@ +Example from Hunt and McIlroy, “An Algorithm for Differential File Comparison.” +https://www.cs.dartmouth.edu/~doug/diff.pdf + +-- old -- +a +b +c +d +e +f +g +-- new -- +w +a +b +x +y +z +e +-- diff -- +diff old new +--- old ++++ new +@@ -1,7 +1,7 @@ ++w + a + b +-c +-d ++x ++y ++z + e +-f +-g diff --git a/src/internal/diff/testdata/dups.txt b/src/internal/diff/testdata/dups.txt new file mode 100644 index 00000000000000..d10524d0d81529 --- /dev/null +++ b/src/internal/diff/testdata/dups.txt @@ -0,0 +1,40 @@ +-- old -- +a + +b + +c + +d + +e + +f +-- new -- +a + +B + +C + +d + +e + +f +-- diff -- +diff old new +--- old ++++ new +@@ -1,8 +1,8 @@ + a + $ +-b +- +-c ++B ++ ++C + $ + d + $ diff --git a/src/internal/diff/testdata/end.txt b/src/internal/diff/testdata/end.txt new file mode 100644 index 00000000000000..158637c135b4d1 --- /dev/null +++ b/src/internal/diff/testdata/end.txt @@ -0,0 +1,38 @@ +-- old -- +1 +2 +3 +4 +5 +6 +7 +eight +nine +ten +eleven +-- new -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +-- diff -- +diff old new +--- old ++++ new +@@ -5,7 +5,6 @@ + 5 + 6 + 7 +-eight +-nine +-ten +-eleven ++8 ++9 ++10 diff --git a/src/internal/diff/testdata/eof.txt b/src/internal/diff/testdata/eof.txt new file mode 100644 index 00000000000000..5dc145c4de47cd --- /dev/null +++ b/src/internal/diff/testdata/eof.txt @@ -0,0 +1,9 @@ +-- old -- +a +b +c^D +-- new -- +a +b +c^D +-- diff -- diff --git a/src/internal/diff/testdata/eof1.txt b/src/internal/diff/testdata/eof1.txt new file mode 100644 index 00000000000000..1ebf621e92163d --- /dev/null +++ b/src/internal/diff/testdata/eof1.txt @@ -0,0 +1,18 @@ +-- old -- +a +b +c +-- new -- +a +b +c^D +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +1,3 @@ + a + b +-c ++c +\ No newline at end of file diff --git a/src/internal/diff/testdata/eof2.txt b/src/internal/diff/testdata/eof2.txt new file mode 100644 index 00000000000000..047705e6865baa --- /dev/null +++ b/src/internal/diff/testdata/eof2.txt @@ -0,0 +1,18 @@ +-- old -- +a +b +c^D +-- new -- +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -1,3 +1,3 @@ + a + b +-c +\ No newline at end of file ++c diff --git a/src/internal/diff/testdata/long.txt b/src/internal/diff/testdata/long.txt new file mode 100644 index 00000000000000..3fc99f71d51865 --- /dev/null +++ b/src/internal/diff/testdata/long.txt @@ -0,0 +1,62 @@ +-- old -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +14½ +15 +16 +17 +18 +19 +20 +-- new -- +1 +2 +3 +4 +5 +6 +8 +9 +10 +11 +12 +13 +14 +17 +18 +19 +20 +-- diff -- +diff old new +--- old ++++ new +@@ -4,7 +4,6 @@ + 4 + 5 + 6 +-7 + 8 + 9 + 10 +@@ -12,9 +11,6 @@ + 12 + 13 + 14 +-14½ +-15 +-16 + 17 + 18 + 19 diff --git a/src/internal/diff/testdata/same.txt b/src/internal/diff/testdata/same.txt new file mode 100644 index 00000000000000..86b1100d810311 --- /dev/null +++ b/src/internal/diff/testdata/same.txt @@ -0,0 +1,5 @@ +-- old -- +hello world +-- new -- +hello world +-- diff -- diff --git a/src/internal/diff/testdata/start.txt b/src/internal/diff/testdata/start.txt new file mode 100644 index 00000000000000..217b2fdc9f8db8 --- /dev/null +++ b/src/internal/diff/testdata/start.txt @@ -0,0 +1,34 @@ +-- old -- +e +pi +4 +5 +6 +7 +8 +9 +10 +-- new -- +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +-- diff -- +diff old new +--- old ++++ new +@@ -1,5 +1,6 @@ +-e +-pi ++1 ++2 ++3 + 4 + 5 + 6 diff --git a/src/internal/diff/testdata/triv.txt b/src/internal/diff/testdata/triv.txt new file mode 100644 index 00000000000000..ab5759fcb2c7d6 --- /dev/null +++ b/src/internal/diff/testdata/triv.txt @@ -0,0 +1,40 @@ +Another example from Hunt and McIlroy, +“An Algorithm for Differential File Comparison.” +https://www.cs.dartmouth.edu/~doug/diff.pdf + +Anchored diff gives up on finding anything, +since there are no unique lines. + +-- old -- +a +b +c +a +b +b +a +-- new -- +c +a +b +a +b +c +-- diff -- +diff old new +--- old ++++ new +@@ -1,7 +1,6 @@ +-a +-b +-c +-a +-b +-b +-a ++c ++a ++b ++a ++b ++c diff --git a/src/internal/execabs/execabs.go b/src/internal/execabs/execabs.go deleted file mode 100644 index 9a05d971dadb9e..00000000000000 --- a/src/internal/execabs/execabs.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package execabs is a drop-in replacement for os/exec -// that requires PATH lookups to find absolute paths. -// That is, execabs.Command("cmd") runs the same PATH lookup -// as exec.Command("cmd"), but if the result is a path -// which is relative, the Run and Start methods will report -// an error instead of running the executable. -package execabs - -import ( - "context" - "fmt" - "os/exec" - "path/filepath" - "reflect" - "unsafe" -) - -var ErrNotFound = exec.ErrNotFound - -type ( - Cmd = exec.Cmd - Error = exec.Error - ExitError = exec.ExitError -) - -func relError(file, path string) error { - return fmt.Errorf("%s resolves to executable relative to current directory (.%c%s)", file, filepath.Separator, path) -} - -func LookPath(file string) (string, error) { - path, err := exec.LookPath(file) - if err != nil { - return "", err - } - if filepath.Base(file) == file && !filepath.IsAbs(path) { - return "", relError(file, path) - } - return path, nil -} - -func fixCmd(name string, cmd *exec.Cmd) { - if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) { - // exec.Command was called with a bare binary name and - // exec.LookPath returned a path which is not absolute. - // Set cmd.lookPathErr and clear cmd.Path so that it - // cannot be run. - lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer())) - if *lookPathErr == nil { - *lookPathErr = relError(name, cmd.Path) - } - cmd.Path = "" - } -} - -func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd { - cmd := exec.CommandContext(ctx, name, arg...) - fixCmd(name, cmd) - return cmd - -} - -func Command(name string, arg ...string) *exec.Cmd { - cmd := exec.Command(name, arg...) - fixCmd(name, cmd) - return cmd -} diff --git a/src/internal/execabs/execabs_test.go b/src/internal/execabs/execabs_test.go deleted file mode 100644 index 97a3f39b4a24bb..00000000000000 --- a/src/internal/execabs/execabs_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package execabs - -import ( - "context" - "fmt" - "internal/testenv" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" -) - -func TestFixCmd(t *testing.T) { - cmd := &exec.Cmd{Path: "hello"} - fixCmd("hello", cmd) - if cmd.Path != "" { - t.Error("fixCmd didn't clear cmd.Path") - } - expectedErr := fmt.Sprintf("hello resolves to executable relative to current directory (.%chello)", filepath.Separator) - if err := cmd.Run(); err == nil { - t.Fatal("Command.Run didn't fail") - } else if err.Error() != expectedErr { - t.Fatalf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error()) - } -} - -func TestCommand(t *testing.T) { - testenv.MustHaveExec(t) - - for _, cmd := range []func(string) *Cmd{ - func(s string) *Cmd { return Command(s) }, - func(s string) *Cmd { return CommandContext(context.Background(), s) }, - } { - tmpDir := t.TempDir() - executable := "execabs-test" - if runtime.GOOS == "windows" { - executable += ".exe" - } - if err := os.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil { - t.Fatalf("os.WriteFile failed: %s", err) - } - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("os.Getwd failed: %s", err) - } - defer os.Chdir(cwd) - if err = os.Chdir(tmpDir); err != nil { - t.Fatalf("os.Chdir failed: %s", err) - } - if runtime.GOOS != "windows" { - // add "." to PATH so that exec.LookPath looks in the current directory on - // non-windows platforms as well - origPath := os.Getenv("PATH") - defer os.Setenv("PATH", origPath) - os.Setenv("PATH", fmt.Sprintf(".:%s", origPath)) - } - expectedErr := fmt.Sprintf("execabs-test resolves to executable relative to current directory (.%c%s)", filepath.Separator, executable) - if err = cmd("execabs-test").Run(); err == nil { - t.Fatalf("Command.Run didn't fail when exec.LookPath returned a relative path") - } else if err.Error() != expectedErr { - t.Errorf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error()) - } - } -} - -func TestLookPath(t *testing.T) { - testenv.MustHaveExec(t) - - tmpDir := t.TempDir() - executable := "execabs-test" - if runtime.GOOS == "windows" { - executable += ".exe" - } - if err := os.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil { - t.Fatalf("os.WriteFile failed: %s", err) - } - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("os.Getwd failed: %s", err) - } - defer os.Chdir(cwd) - if err = os.Chdir(tmpDir); err != nil { - t.Fatalf("os.Chdir failed: %s", err) - } - if runtime.GOOS != "windows" { - // add "." to PATH so that exec.LookPath looks in the current directory on - // non-windows platforms as well - origPath := os.Getenv("PATH") - defer os.Setenv("PATH", origPath) - os.Setenv("PATH", fmt.Sprintf(".:%s", origPath)) - } - expectedErr := fmt.Sprintf("execabs-test resolves to executable relative to current directory (.%c%s)", filepath.Separator, executable) - if _, err := LookPath("execabs-test"); err == nil { - t.Fatalf("LookPath didn't fail when finding a non-relative path") - } else if err.Error() != expectedErr { - t.Errorf("LookPath returned unexpected error: want %q, got %q", expectedErr, err.Error()) - } -} diff --git a/src/internal/fmtsort/sort.go b/src/internal/fmtsort/sort.go index 7127ba6ac3d970..278a89bd75d423 100644 --- a/src/internal/fmtsort/sort.go +++ b/src/internal/fmtsort/sort.go @@ -36,19 +36,18 @@ func (o *SortedMap) Swap(i, j int) { // // The ordering rules are more general than with Go's < operator: // -// - when applicable, nil compares low -// - ints, floats, and strings order by < -// - NaN compares less than non-NaN floats -// - bool compares false before true -// - complex compares real, then imag -// - pointers compare by machine address -// - channel values compare by machine address -// - structs compare each field in turn -// - arrays compare each element in turn. -// Otherwise identical arrays compare by length. -// - interface values compare first by reflect.Type describing the concrete type -// and then by concrete value as described in the previous rules. -// +// - when applicable, nil compares low +// - ints, floats, and strings order by < +// - NaN compares less than non-NaN floats +// - bool compares false before true +// - complex compares real, then imag +// - pointers compare by machine address +// - channel values compare by machine address +// - structs compare each field in turn +// - arrays compare each element in turn. +// Otherwise identical arrays compare by length. +// - interface values compare first by reflect.Type describing the concrete type +// and then by concrete value as described in the previous rules. func Sort(mapValue reflect.Value) *SortedMap { if mapValue.Type().Kind() != reflect.Map { return nil @@ -130,7 +129,7 @@ func compare(aVal, bVal reflect.Value) int { default: return -1 } - case reflect.Ptr, reflect.UnsafePointer: + case reflect.Pointer, reflect.UnsafePointer: a, b := aVal.Pointer(), bVal.Pointer() switch { case a < b: diff --git a/src/internal/fmtsort/sort_test.go b/src/internal/fmtsort/sort_test.go index 5c4db1c5faa950..11befca6f1921f 100644 --- a/src/internal/fmtsort/sort_test.go +++ b/src/internal/fmtsort/sort_test.go @@ -9,6 +9,7 @@ import ( "internal/fmtsort" "math" "reflect" + "sort" "strings" "testing" "unsafe" @@ -37,12 +38,12 @@ var compareTests = [][]reflect.Value{ ct(reflect.TypeOf(chans[0]), chans[0], chans[1], chans[2]), ct(reflect.TypeOf(toy{}), toy{0, 1}, toy{0, 2}, toy{1, -1}, toy{1, 1}), ct(reflect.TypeOf([2]int{}), [2]int{1, 1}, [2]int{1, 2}, [2]int{2, 0}), - ct(reflect.TypeOf(interface{}(interface{}(0))), iFace, 1, 2, 3), + ct(reflect.TypeOf(any(any(0))), iFace, 1, 2, 3), } -var iFace interface{} +var iFace any -func ct(typ reflect.Type, args ...interface{}) []reflect.Value { +func ct(typ reflect.Type, args ...any) []reflect.Value { value := make([]reflect.Value, len(args)) for i, v := range args { x := reflect.ValueOf(v) @@ -83,8 +84,8 @@ func TestCompare(t *testing.T) { } type sortTest struct { - data interface{} // Always a map. - print string // Printed result using our custom printer. + data any // Always a map. + print string // Printed result using our custom printer. } var sortTests = []sortTest{ @@ -134,7 +135,7 @@ var sortTests = []sortTest{ }, } -func sprint(data interface{}) string { +func sprint(data any) string { om := fmtsort.Sort(reflect.ValueOf(data)) if om == nil { return "nil" @@ -188,9 +189,19 @@ func sprintKey(key reflect.Value) string { var ( ints [3]int - chans = [3]chan int{make(chan int), make(chan int), make(chan int)} + chans = makeChans() ) +func makeChans() []chan int { + cs := []chan int{make(chan int), make(chan int), make(chan int)} + // Order channels by address. See issue #49431. + // TODO: pin these pointers once pinning is available (#46787). + sort.Slice(cs, func(i, j int) bool { + return uintptr(reflect.ValueOf(cs[i]).UnsafePointer()) < uintptr(reflect.ValueOf(cs[j]).UnsafePointer()) + }) + return cs +} + func pointerMap() map[*int]string { m := make(map[*int]string) for i := 2; i >= 0; i-- { @@ -233,7 +244,7 @@ func TestInterface(t *testing.T) { // A map containing multiple concrete types should be sorted by type, // then value. However, the relative ordering of types is unspecified, // so test this by checking the presence of sorted subgroups. - m := map[interface{}]string{ + m := map[any]string{ [2]int{1, 0}: "", [2]int{0, 1}: "", true: "", diff --git a/src/internal/fuzz/counters_supported.go b/src/internal/fuzz/counters_supported.go new file mode 100644 index 00000000000000..79e27d27e1af28 --- /dev/null +++ b/src/internal/fuzz/counters_supported.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (darwin || linux || windows || freebsd) && (amd64 || arm64) + +package fuzz + +import ( + "unsafe" +) + +// coverage returns a []byte containing unique 8-bit counters for each edge of +// the instrumented source code. This coverage data will only be generated if +// `-d=libfuzzer` is set at build time. This can be used to understand the code +// coverage of a test execution. +func coverage() []byte { + addr := unsafe.Pointer(&_counters) + size := uintptr(unsafe.Pointer(&_ecounters)) - uintptr(addr) + return unsafe.Slice((*byte)(addr), int(size)) +} diff --git a/src/internal/fuzz/counters_unsupported.go b/src/internal/fuzz/counters_unsupported.go new file mode 100644 index 00000000000000..bf281570681fb5 --- /dev/null +++ b/src/internal/fuzz/counters_unsupported.go @@ -0,0 +1,24 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO: expand the set of supported platforms, with testing. Nothing about +// the instrumentation is OS specific, but only amd64 and arm64 are +// supported in the runtime. See src/runtime/libfuzzer*. +// +// If you update this constraint, also update cmd/internal/sys.FuzzInstrumeted. +// +//go:build !((darwin || linux || windows || freebsd) && (amd64 || arm64)) + +package fuzz + +// TODO(#48504): re-enable on platforms where instrumentation works. +// In theory, we shouldn't need this file at all: if the binary was built +// without coverage, then _counters and _ecounters should have the same address. +// However, this caused an init failure on aix/ppc64, so it's disabled here. + +// coverage returns a []byte containing unique 8-bit counters for each edge of +// the instrumented source code. This coverage data will only be generated if +// `-d=libfuzzer` is set at build time. This can be used to understand the code +// coverage of a test execution. +func coverage() []byte { return nil } diff --git a/src/internal/fuzz/coverage.go b/src/internal/fuzz/coverage.go new file mode 100644 index 00000000000000..88f98a16b2e299 --- /dev/null +++ b/src/internal/fuzz/coverage.go @@ -0,0 +1,107 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "fmt" + "math/bits" +) + +// ResetCovereage sets all of the counters for each edge of the instrumented +// source code to 0. +func ResetCoverage() { + cov := coverage() + for i := range cov { + cov[i] = 0 + } +} + +// SnapshotCoverage copies the current counter values into coverageSnapshot, +// preserving them for later inspection. SnapshotCoverage also rounds each +// counter down to the nearest power of two. This lets the coordinator store +// multiple values for each counter by OR'ing them together. +func SnapshotCoverage() { + cov := coverage() + for i, b := range cov { + b |= b >> 1 + b |= b >> 2 + b |= b >> 4 + b -= b >> 1 + coverageSnapshot[i] = b + } +} + +// diffCoverage returns a set of bits set in snapshot but not in base. +// If there are no new bits set, diffCoverage returns nil. +func diffCoverage(base, snapshot []byte) []byte { + if len(base) != len(snapshot) { + panic(fmt.Sprintf("the number of coverage bits changed: before=%d, after=%d", len(base), len(snapshot))) + } + found := false + for i := range snapshot { + if snapshot[i]&^base[i] != 0 { + found = true + break + } + } + if !found { + return nil + } + diff := make([]byte, len(snapshot)) + for i := range diff { + diff[i] = snapshot[i] &^ base[i] + } + return diff +} + +// countNewCoverageBits returns the number of bits set in snapshot that are not +// set in base. +func countNewCoverageBits(base, snapshot []byte) int { + n := 0 + for i := range snapshot { + n += bits.OnesCount8(snapshot[i] &^ base[i]) + } + return n +} + +// isCoverageSubset returns true if all the base coverage bits are set in +// snapshot +func isCoverageSubset(base, snapshot []byte) bool { + for i, v := range base { + if v&snapshot[i] != v { + return false + } + } + return true +} + +// hasCoverageBit returns true if snapshot has at least one bit set that is +// also set in base. +func hasCoverageBit(base, snapshot []byte) bool { + for i := range snapshot { + if snapshot[i]&base[i] != 0 { + return true + } + } + return false +} + +func countBits(cov []byte) int { + n := 0 + for _, c := range cov { + n += bits.OnesCount8(c) + } + return n +} + +var ( + coverageEnabled = len(coverage()) > 0 + coverageSnapshot = make([]byte, len(coverage())) + + // _counters and _ecounters mark the start and end, respectively, of where + // the 8-bit coverage counters reside in memory. They're known to cmd/link, + // which specially assigns their addresses for this purpose. + _counters, _ecounters [0]byte +) diff --git a/src/internal/fuzz/encoding.go b/src/internal/fuzz/encoding.go new file mode 100644 index 00000000000000..c2eed7045e7b39 --- /dev/null +++ b/src/internal/fuzz/encoding.go @@ -0,0 +1,361 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "math" + "strconv" + "strings" + "unicode/utf8" +) + +// encVersion1 will be the first line of a file with version 1 encoding. +var encVersion1 = "go test fuzz v1" + +// marshalCorpusFile encodes an arbitrary number of arguments into the file format for the +// corpus. +func marshalCorpusFile(vals ...any) []byte { + if len(vals) == 0 { + panic("must have at least one value to marshal") + } + b := bytes.NewBuffer([]byte(encVersion1 + "\n")) + // TODO(katiehockman): keep uint8 and int32 encoding where applicable, + // instead of changing to byte and rune respectively. + for _, val := range vals { + switch t := val.(type) { + case int, int8, int16, int64, uint, uint16, uint32, uint64, bool: + fmt.Fprintf(b, "%T(%v)\n", t, t) + case float32: + if math.IsNaN(float64(t)) && math.Float32bits(t) != math.Float32bits(float32(math.NaN())) { + // We encode unusual NaNs as hex values, because that is how users are + // likely to encounter them in literature about floating-point encoding. + // This allows us to reproduce fuzz failures that depend on the specific + // NaN representation (for float32 there are about 2^24 possibilities!), + // not just the fact that the value is *a* NaN. + // + // Note that the specific value of float32(math.NaN()) can vary based on + // whether the architecture represents signaling NaNs using a low bit + // (as is common) or a high bit (as commonly implemented on MIPS + // hardware before around 2012). We believe that the increase in clarity + // from identifying "NaN" with math.NaN() is worth the slight ambiguity + // from a platform-dependent value. + fmt.Fprintf(b, "math.Float32frombits(0x%x)\n", math.Float32bits(t)) + } else { + // We encode all other values — including the NaN value that is + // bitwise-identical to float32(math.Nan()) — using the default + // formatting, which is equivalent to strconv.FormatFloat with format + // 'g' and can be parsed by strconv.ParseFloat. + // + // For an ordinary floating-point number this format includes + // sufficiently many digits to reconstruct the exact value. For positive + // or negative infinity it is the string "+Inf" or "-Inf". For positive + // or negative zero it is "0" or "-0". For NaN, it is the string "NaN". + fmt.Fprintf(b, "%T(%v)\n", t, t) + } + case float64: + if math.IsNaN(t) && math.Float64bits(t) != math.Float64bits(math.NaN()) { + fmt.Fprintf(b, "math.Float64frombits(0x%x)\n", math.Float64bits(t)) + } else { + fmt.Fprintf(b, "%T(%v)\n", t, t) + } + case string: + fmt.Fprintf(b, "string(%q)\n", t) + case rune: // int32 + // Although rune and int32 are represented by the same type, only a subset + // of valid int32 values can be expressed as rune literals. Notably, + // negative numbers, surrogate halves, and values above unicode.MaxRune + // have no quoted representation. + // + // fmt with "%q" (and the corresponding functions in the strconv package) + // would quote out-of-range values to the Unicode replacement character + // instead of the original value (see https://go.dev/issue/51526), so + // they must be treated as int32 instead. + // + // We arbitrarily draw the line at UTF-8 validity, which biases toward the + // "rune" interpretation. (However, we accept either format as input.) + if utf8.ValidRune(t) { + fmt.Fprintf(b, "rune(%q)\n", t) + } else { + fmt.Fprintf(b, "int32(%v)\n", t) + } + case byte: // uint8 + // For bytes, we arbitrarily prefer the character interpretation. + // (Every byte has a valid character encoding.) + fmt.Fprintf(b, "byte(%q)\n", t) + case []byte: // []uint8 + fmt.Fprintf(b, "[]byte(%q)\n", t) + default: + panic(fmt.Sprintf("unsupported type: %T", t)) + } + } + return b.Bytes() +} + +// unmarshalCorpusFile decodes corpus bytes into their respective values. +func unmarshalCorpusFile(b []byte) ([]any, error) { + if len(b) == 0 { + return nil, fmt.Errorf("cannot unmarshal empty string") + } + lines := bytes.Split(b, []byte("\n")) + if len(lines) < 2 { + return nil, fmt.Errorf("must include version and at least one value") + } + version := strings.TrimSuffix(string(lines[0]), "\r") + if version != encVersion1 { + return nil, fmt.Errorf("unknown encoding version: %s", version) + } + var vals []any + for _, line := range lines[1:] { + line = bytes.TrimSpace(line) + if len(line) == 0 { + continue + } + v, err := parseCorpusValue(line) + if err != nil { + return nil, fmt.Errorf("malformed line %q: %v", line, err) + } + vals = append(vals, v) + } + return vals, nil +} + +func parseCorpusValue(line []byte) (any, error) { + fs := token.NewFileSet() + expr, err := parser.ParseExprFrom(fs, "(test)", line, 0) + if err != nil { + return nil, err + } + call, ok := expr.(*ast.CallExpr) + if !ok { + return nil, fmt.Errorf("expected call expression") + } + if len(call.Args) != 1 { + return nil, fmt.Errorf("expected call expression with 1 argument; got %d", len(call.Args)) + } + arg := call.Args[0] + + if arrayType, ok := call.Fun.(*ast.ArrayType); ok { + if arrayType.Len != nil { + return nil, fmt.Errorf("expected []byte or primitive type") + } + elt, ok := arrayType.Elt.(*ast.Ident) + if !ok || elt.Name != "byte" { + return nil, fmt.Errorf("expected []byte") + } + lit, ok := arg.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return nil, fmt.Errorf("string literal required for type []byte") + } + s, err := strconv.Unquote(lit.Value) + if err != nil { + return nil, err + } + return []byte(s), nil + } + + var idType *ast.Ident + if selector, ok := call.Fun.(*ast.SelectorExpr); ok { + xIdent, ok := selector.X.(*ast.Ident) + if !ok || xIdent.Name != "math" { + return nil, fmt.Errorf("invalid selector type") + } + switch selector.Sel.Name { + case "Float64frombits": + idType = &ast.Ident{Name: "float64-bits"} + case "Float32frombits": + idType = &ast.Ident{Name: "float32-bits"} + default: + return nil, fmt.Errorf("invalid selector type") + } + } else { + idType, ok = call.Fun.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("expected []byte or primitive type") + } + if idType.Name == "bool" { + id, ok := arg.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("malformed bool") + } + if id.Name == "true" { + return true, nil + } else if id.Name == "false" { + return false, nil + } else { + return nil, fmt.Errorf("true or false required for type bool") + } + } + } + + var ( + val string + kind token.Token + ) + if op, ok := arg.(*ast.UnaryExpr); ok { + switch lit := op.X.(type) { + case *ast.BasicLit: + if op.Op != token.SUB { + return nil, fmt.Errorf("unsupported operation on int/float: %v", op.Op) + } + // Special case for negative numbers. + val = op.Op.String() + lit.Value // e.g. "-" + "124" + kind = lit.Kind + case *ast.Ident: + if lit.Name != "Inf" { + return nil, fmt.Errorf("expected operation on int or float type") + } + if op.Op == token.SUB { + val = "-Inf" + } else { + val = "+Inf" + } + kind = token.FLOAT + default: + return nil, fmt.Errorf("expected operation on int or float type") + } + } else { + switch lit := arg.(type) { + case *ast.BasicLit: + val, kind = lit.Value, lit.Kind + case *ast.Ident: + if lit.Name != "NaN" { + return nil, fmt.Errorf("literal value required for primitive type") + } + val, kind = "NaN", token.FLOAT + default: + return nil, fmt.Errorf("literal value required for primitive type") + } + } + + switch typ := idType.Name; typ { + case "string": + if kind != token.STRING { + return nil, fmt.Errorf("string literal value required for type string") + } + return strconv.Unquote(val) + case "byte", "rune": + if kind == token.INT { + switch typ { + case "rune": + return parseInt(val, typ) + case "byte": + return parseUint(val, typ) + } + } + if kind != token.CHAR { + return nil, fmt.Errorf("character literal required for byte/rune types") + } + n := len(val) + if n < 2 { + return nil, fmt.Errorf("malformed character literal, missing single quotes") + } + code, _, _, err := strconv.UnquoteChar(val[1:n-1], '\'') + if err != nil { + return nil, err + } + if typ == "rune" { + return code, nil + } + if code >= 256 { + return nil, fmt.Errorf("can only encode single byte to a byte type") + } + return byte(code), nil + case "int", "int8", "int16", "int32", "int64": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for int types") + } + return parseInt(val, typ) + case "uint", "uint8", "uint16", "uint32", "uint64": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for uint types") + } + return parseUint(val, typ) + case "float32": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("float or integer literal required for float32 type") + } + v, err := strconv.ParseFloat(val, 32) + return float32(v), err + case "float64": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("float or integer literal required for float64 type") + } + return strconv.ParseFloat(val, 64) + case "float32-bits": + if kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float32frombits type") + } + bits, err := parseUint(val, "uint32") + if err != nil { + return nil, err + } + return math.Float32frombits(bits.(uint32)), nil + case "float64-bits": + if kind != token.FLOAT && kind != token.INT { + return nil, fmt.Errorf("integer literal required for math.Float64frombits type") + } + bits, err := parseUint(val, "uint64") + if err != nil { + return nil, err + } + return math.Float64frombits(bits.(uint64)), nil + default: + return nil, fmt.Errorf("expected []byte or primitive type") + } +} + +// parseInt returns an integer of value val and type typ. +func parseInt(val, typ string) (any, error) { + switch typ { + case "int": + // The int type may be either 32 or 64 bits. If 32, the fuzz tests in the + // corpus may include 64-bit values produced by fuzzing runs on 64-bit + // architectures. When running those tests, we implicitly wrap the values to + // fit in a regular int. (The test case is still “interesting”, even if the + // specific values of its inputs are platform-dependent.) + i, err := strconv.ParseInt(val, 0, 64) + return int(i), err + case "int8": + i, err := strconv.ParseInt(val, 0, 8) + return int8(i), err + case "int16": + i, err := strconv.ParseInt(val, 0, 16) + return int16(i), err + case "int32", "rune": + i, err := strconv.ParseInt(val, 0, 32) + return int32(i), err + case "int64": + return strconv.ParseInt(val, 0, 64) + default: + panic("unreachable") + } +} + +// parseInt returns an unsigned integer of value val and type typ. +func parseUint(val, typ string) (any, error) { + switch typ { + case "uint": + i, err := strconv.ParseUint(val, 0, 64) + return uint(i), err + case "uint8", "byte": + i, err := strconv.ParseUint(val, 0, 8) + return uint8(i), err + case "uint16": + i, err := strconv.ParseUint(val, 0, 16) + return uint16(i), err + case "uint32": + i, err := strconv.ParseUint(val, 0, 32) + return uint32(i), err + case "uint64": + return strconv.ParseUint(val, 0, 64) + default: + panic("unreachable") + } +} diff --git a/src/internal/fuzz/encoding_test.go b/src/internal/fuzz/encoding_test.go new file mode 100644 index 00000000000000..6f6173d7e0122c --- /dev/null +++ b/src/internal/fuzz/encoding_test.go @@ -0,0 +1,409 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "math" + "strconv" + "testing" + "unicode" +) + +func TestUnmarshalMarshal(t *testing.T) { + var tests = []struct { + desc string + in string + reject bool + want string // if different from in + }{ + { + desc: "missing version", + in: "int(1234)", + reject: true, + }, + { + desc: "malformed string", + in: `go test fuzz v1 +string("a"bcad")`, + reject: true, + }, + { + desc: "empty value", + in: `go test fuzz v1 +int()`, + reject: true, + }, + { + desc: "negative uint", + in: `go test fuzz v1 +uint(-32)`, + reject: true, + }, + { + desc: "int8 too large", + in: `go test fuzz v1 +int8(1234456)`, + reject: true, + }, + { + desc: "multiplication in int value", + in: `go test fuzz v1 +int(20*5)`, + reject: true, + }, + { + desc: "double negation", + in: `go test fuzz v1 +int(--5)`, + reject: true, + }, + { + desc: "malformed bool", + in: `go test fuzz v1 +bool(0)`, + reject: true, + }, + { + desc: "malformed byte", + in: `go test fuzz v1 +byte('aa)`, + reject: true, + }, + { + desc: "byte out of range", + in: `go test fuzz v1 +byte('☃')`, + reject: true, + }, + { + desc: "extra newline", + in: `go test fuzz v1 +string("has extra newline") +`, + want: `go test fuzz v1 +string("has extra newline")`, + }, + { + desc: "trailing spaces", + in: `go test fuzz v1 +string("extra") +[]byte("spacing") + `, + want: `go test fuzz v1 +string("extra") +[]byte("spacing")`, + }, + { + desc: "float types", + in: `go test fuzz v1 +float64(0) +float32(0)`, + }, + { + desc: "various types", + in: `go test fuzz v1 +int(-23) +int8(-2) +int64(2342425) +uint(1) +uint16(234) +uint32(352342) +uint64(123) +rune('œ') +byte('K') +byte('ÿ') +[]byte("hello¿") +[]byte("a") +bool(true) +string("hello\\xbd\\xb2=\\xbc ⌘") +float64(-12.5) +float32(2.5)`, + }, + { + desc: "float edge cases", + // The two IEEE 754 bit patterns used for the math.Float{64,32}frombits + // encodings are non-math.NAN quiet-NaN values. Since they are not equal + // to math.NaN(), they should be re-encoded to their bit patterns. They + // are, respectively: + // * math.Float64bits(math.NaN())+1 + // * math.Float32bits(float32(math.NaN()))+1 + in: `go test fuzz v1 +float32(-0) +float64(-0) +float32(+Inf) +float32(-Inf) +float32(NaN) +float64(+Inf) +float64(-Inf) +float64(NaN) +math.Float64frombits(0x7ff8000000000002) +math.Float32frombits(0x7fc00001)`, + }, + { + desc: "int variations", + // Although we arbitrarily choose default integer bases (0 or 16), we may + // want to change those arbitrary choices in the future and should not + // break the parser. Verify that integers in the opposite bases still + // parse correctly. + in: `go test fuzz v1 +int(0x0) +int32(0x41) +int64(0xfffffffff) +uint32(0xcafef00d) +uint64(0xffffffffffffffff) +uint8(0b0000000) +byte(0x0) +byte('\000') +byte('\u0000') +byte('\'') +math.Float64frombits(9221120237041090562) +math.Float32frombits(2143289345)`, + want: `go test fuzz v1 +int(0) +rune('A') +int64(68719476735) +uint32(3405705229) +uint64(18446744073709551615) +byte('\x00') +byte('\x00') +byte('\x00') +byte('\x00') +byte('\'') +math.Float64frombits(0x7ff8000000000002) +math.Float32frombits(0x7fc00001)`, + }, + { + desc: "rune validation", + in: `go test fuzz v1 +rune(0) +rune(0x41) +rune(-1) +rune(0xfffd) +rune(0xd800) +rune(0x10ffff) +rune(0x110000) +`, + want: `go test fuzz v1 +rune('\x00') +rune('A') +int32(-1) +rune('�') +int32(55296) +rune('\U0010ffff') +int32(1114112)`, + }, + { + desc: "int overflow", + in: `go test fuzz v1 +int(0x7fffffffffffffff) +uint(0xffffffffffffffff)`, + want: func() string { + switch strconv.IntSize { + case 32: + return `go test fuzz v1 +int(-1) +uint(4294967295)` + case 64: + return `go test fuzz v1 +int(9223372036854775807) +uint(18446744073709551615)` + default: + panic("unreachable") + } + }(), + }, + { + desc: "windows new line", + in: "go test fuzz v1\r\nint(0)\r\n", + want: "go test fuzz v1\nint(0)", + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + vals, err := unmarshalCorpusFile([]byte(test.in)) + if test.reject { + if err == nil { + t.Fatalf("unmarshal unexpected success") + } + return + } + if err != nil { + t.Fatalf("unmarshal unexpected error: %v", err) + } + newB := marshalCorpusFile(vals...) + if err != nil { + t.Fatalf("marshal unexpected error: %v", err) + } + if newB[len(newB)-1] != '\n' { + t.Error("didn't write final newline to corpus file") + } + + want := test.want + if want == "" { + want = test.in + } + want += "\n" + got := string(newB) + if got != want { + t.Errorf("unexpected marshaled value\ngot:\n%s\nwant:\n%s", got, want) + } + }) + } +} + +// BenchmarkMarshalCorpusFile measures the time it takes to serialize byte +// slices of various sizes to a corpus file. The slice contains a repeating +// sequence of bytes 0-255 to mix escaped and non-escaped characters. +func BenchmarkMarshalCorpusFile(b *testing.B) { + buf := make([]byte, 1024*1024) + for i := 0; i < len(buf); i++ { + buf[i] = byte(i) + } + + for sz := 1; sz <= len(buf); sz <<= 1 { + sz := sz + b.Run(strconv.Itoa(sz), func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.SetBytes(int64(sz)) + marshalCorpusFile(buf[:sz]) + } + }) + } +} + +// BenchmarkUnmarshalCorpusfile measures the time it takes to deserialize +// files encoding byte slices of various sizes. The slice contains a repeating +// sequence of bytes 0-255 to mix escaped and non-escaped characters. +func BenchmarkUnmarshalCorpusFile(b *testing.B) { + buf := make([]byte, 1024*1024) + for i := 0; i < len(buf); i++ { + buf[i] = byte(i) + } + + for sz := 1; sz <= len(buf); sz <<= 1 { + sz := sz + data := marshalCorpusFile(buf[:sz]) + b.Run(strconv.Itoa(sz), func(b *testing.B) { + for i := 0; i < b.N; i++ { + b.SetBytes(int64(sz)) + unmarshalCorpusFile(data) + } + }) + } +} + +func TestByteRoundTrip(t *testing.T) { + for x := 0; x < 256; x++ { + b1 := byte(x) + buf := marshalCorpusFile(b1) + vs, err := unmarshalCorpusFile(buf) + if err != nil { + t.Fatal(err) + } + b2 := vs[0].(byte) + if b2 != b1 { + t.Fatalf("unmarshaled %v, want %v:\n%s", b2, b1, buf) + } + } +} + +func TestInt8RoundTrip(t *testing.T) { + for x := -128; x < 128; x++ { + i1 := int8(x) + buf := marshalCorpusFile(i1) + vs, err := unmarshalCorpusFile(buf) + if err != nil { + t.Fatal(err) + } + i2 := vs[0].(int8) + if i2 != i1 { + t.Fatalf("unmarshaled %v, want %v:\n%s", i2, i1, buf) + } + } +} + +func FuzzFloat64RoundTrip(f *testing.F) { + f.Add(math.Float64bits(0)) + f.Add(math.Float64bits(math.Copysign(0, -1))) + f.Add(math.Float64bits(math.MaxFloat64)) + f.Add(math.Float64bits(math.SmallestNonzeroFloat64)) + f.Add(math.Float64bits(math.NaN())) + f.Add(uint64(0x7FF0000000000001)) // signaling NaN + f.Add(math.Float64bits(math.Inf(1))) + f.Add(math.Float64bits(math.Inf(-1))) + + f.Fuzz(func(t *testing.T, u1 uint64) { + x1 := math.Float64frombits(u1) + + b := marshalCorpusFile(x1) + t.Logf("marshaled math.Float64frombits(0x%x):\n%s", u1, b) + + xs, err := unmarshalCorpusFile(b) + if err != nil { + t.Fatal(err) + } + if len(xs) != 1 { + t.Fatalf("unmarshaled %d values", len(xs)) + } + x2 := xs[0].(float64) + u2 := math.Float64bits(x2) + if u2 != u1 { + t.Errorf("unmarshaled %v (bits 0x%x)", x2, u2) + } + }) +} + +func FuzzRuneRoundTrip(f *testing.F) { + f.Add(rune(-1)) + f.Add(rune(0xd800)) + f.Add(rune(0xdfff)) + f.Add(rune(unicode.ReplacementChar)) + f.Add(rune(unicode.MaxASCII)) + f.Add(rune(unicode.MaxLatin1)) + f.Add(rune(unicode.MaxRune)) + f.Add(rune(unicode.MaxRune + 1)) + f.Add(rune(-0x80000000)) + f.Add(rune(0x7fffffff)) + + f.Fuzz(func(t *testing.T, r1 rune) { + b := marshalCorpusFile(r1) + t.Logf("marshaled rune(0x%x):\n%s", r1, b) + + rs, err := unmarshalCorpusFile(b) + if err != nil { + t.Fatal(err) + } + if len(rs) != 1 { + t.Fatalf("unmarshaled %d values", len(rs)) + } + r2 := rs[0].(rune) + if r2 != r1 { + t.Errorf("unmarshaled rune(0x%x)", r2) + } + }) +} + +func FuzzStringRoundTrip(f *testing.F) { + f.Add("") + f.Add("\x00") + f.Add(string([]rune{unicode.ReplacementChar})) + + f.Fuzz(func(t *testing.T, s1 string) { + b := marshalCorpusFile(s1) + t.Logf("marshaled %q:\n%s", s1, b) + + rs, err := unmarshalCorpusFile(b) + if err != nil { + t.Fatal(err) + } + if len(rs) != 1 { + t.Fatalf("unmarshaled %d values", len(rs)) + } + s2 := rs[0].(string) + if s2 != s1 { + t.Errorf("unmarshaled %q", s2) + } + }) +} diff --git a/src/internal/fuzz/fuzz.go b/src/internal/fuzz/fuzz.go new file mode 100644 index 00000000000000..3ccf74745f67a1 --- /dev/null +++ b/src/internal/fuzz/fuzz.go @@ -0,0 +1,1091 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fuzz provides common fuzzing functionality for tests built with +// "go test" and for programs that use fuzzing functionality in the testing +// package. +package fuzz + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + "internal/godebug" + "io" + "io/ioutil" + "math/bits" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "sync" + "time" +) + +// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing. +// The zero value is valid for each field unless specified otherwise. +type CoordinateFuzzingOpts struct { + // Log is a writer for logging progress messages and warnings. + // If nil, io.Discard will be used instead. + Log io.Writer + + // Timeout is the amount of wall clock time to spend fuzzing after the corpus + // has loaded. If zero, there will be no time limit. + Timeout time.Duration + + // Limit is the number of random values to generate and test. If zero, + // there will be no limit on the number of generated values. + Limit int64 + + // MinimizeTimeout is the amount of wall clock time to spend minimizing + // after discovering a crasher. If zero, there will be no time limit. If + // MinimizeTimeout and MinimizeLimit are both zero, then minimization will + // be disabled. + MinimizeTimeout time.Duration + + // MinimizeLimit is the maximum number of calls to the fuzz function to be + // made while minimizing after finding a crash. If zero, there will be no + // limit. Calls to the fuzz function made when minimizing also count toward + // Limit. If MinimizeTimeout and MinimizeLimit are both zero, then + // minimization will be disabled. + MinimizeLimit int64 + + // parallel is the number of worker processes to run in parallel. If zero, + // CoordinateFuzzing will run GOMAXPROCS workers. + Parallel int + + // Seed is a list of seed values added by the fuzz target with testing.F.Add + // and in testdata. + Seed []CorpusEntry + + // Types is the list of types which make up a corpus entry. + // Types must be set and must match values in Seed. + Types []reflect.Type + + // CorpusDir is a directory where files containing values that crash the + // code being tested may be written. CorpusDir must be set. + CorpusDir string + + // CacheDir is a directory containing additional "interesting" values. + // The fuzzer may derive new values from these, and may write new values here. + CacheDir string +} + +// CoordinateFuzzing creates several worker processes and communicates with +// them to test random inputs that could trigger crashes and expose bugs. +// The worker processes run the same binary in the same directory with the +// same environment variables as the coordinator process. Workers also run +// with the same arguments as the coordinator, except with the -test.fuzzworker +// flag prepended to the argument list. +// +// If a crash occurs, the function will return an error containing information +// about the crash, which can be reported to the user. +func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) { + if err := ctx.Err(); err != nil { + return err + } + if opts.Log == nil { + opts.Log = io.Discard + } + if opts.Parallel == 0 { + opts.Parallel = runtime.GOMAXPROCS(0) + } + if opts.Limit > 0 && int64(opts.Parallel) > opts.Limit { + // Don't start more workers than we need. + opts.Parallel = int(opts.Limit) + } + + c, err := newCoordinator(opts) + if err != nil { + return err + } + + if opts.Timeout > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, opts.Timeout) + defer cancel() + } + + // fuzzCtx is used to stop workers, for example, after finding a crasher. + fuzzCtx, cancelWorkers := context.WithCancel(ctx) + defer cancelWorkers() + doneC := ctx.Done() + + // stop is called when a worker encounters a fatal error. + var fuzzErr error + stopping := false + stop := func(err error) { + if err == fuzzCtx.Err() || isInterruptError(err) { + // Suppress cancellation errors and terminations due to SIGINT. + // The messages are not helpful since either the user triggered the error + // (with ^C) or another more helpful message will be printed (a crasher). + err = nil + } + if err != nil && (fuzzErr == nil || fuzzErr == ctx.Err()) { + fuzzErr = err + } + if stopping { + return + } + stopping = true + cancelWorkers() + doneC = nil + } + + // Ensure that any crash we find is written to the corpus, even if an error + // or interruption occurs while minimizing it. + crashWritten := false + defer func() { + if c.crashMinimizing == nil || crashWritten { + return + } + werr := writeToCorpus(&c.crashMinimizing.entry, opts.CorpusDir) + if werr != nil { + err = fmt.Errorf("%w\n%v", err, werr) + return + } + if err == nil { + err = &crashError{ + path: c.crashMinimizing.entry.Path, + err: errors.New(c.crashMinimizing.crasherMsg), + } + } + }() + + // Start workers. + // TODO(jayconrod): do we want to support fuzzing different binaries? + dir := "" // same as self + binPath := os.Args[0] + args := append([]string{"-test.fuzzworker"}, os.Args[1:]...) + env := os.Environ() // same as self + + errC := make(chan error) + workers := make([]*worker, opts.Parallel) + for i := range workers { + var err error + workers[i], err = newWorker(c, dir, binPath, args, env) + if err != nil { + return err + } + } + for i := range workers { + w := workers[i] + go func() { + err := w.coordinate(fuzzCtx) + if fuzzCtx.Err() != nil || isInterruptError(err) { + err = nil + } + cleanErr := w.cleanup() + if err == nil { + err = cleanErr + } + errC <- err + }() + } + + // Main event loop. + // Do not return until all workers have terminated. We avoid a deadlock by + // receiving messages from workers even after ctx is cancelled. + activeWorkers := len(workers) + statTicker := time.NewTicker(3 * time.Second) + defer statTicker.Stop() + defer c.logStats() + + c.logStats() + for { + var inputC chan fuzzInput + input, ok := c.peekInput() + if ok && c.crashMinimizing == nil && !stopping { + inputC = c.inputC + } + + var minimizeC chan fuzzMinimizeInput + minimizeInput, ok := c.peekMinimizeInput() + if ok && !stopping { + minimizeC = c.minimizeC + } + + select { + case <-doneC: + // Interrupted, cancelled, or timed out. + // stop sets doneC to nil so we don't busy wait here. + stop(ctx.Err()) + + case err := <-errC: + // A worker terminated, possibly after encountering a fatal error. + stop(err) + activeWorkers-- + if activeWorkers == 0 { + return fuzzErr + } + + case result := <-c.resultC: + // Received response from worker. + if stopping { + break + } + c.updateStats(result) + + if result.crasherMsg != "" { + if c.warmupRun() && result.entry.IsSeed { + target := filepath.Base(c.opts.CorpusDir) + fmt.Fprintf(c.opts.Log, "failure while testing seed corpus entry: %s/%s\n", target, testName(result.entry.Parent)) + stop(errors.New(result.crasherMsg)) + break + } + if c.canMinimize() && result.canMinimize { + if c.crashMinimizing != nil { + // This crash is not minimized, and another crash is being minimized. + // Ignore this one and wait for the other one to finish. + break + } + // Found a crasher but haven't yet attempted to minimize it. + // Send it back to a worker for minimization. Disable inputC so + // other workers don't continue fuzzing. + c.crashMinimizing = &result + fmt.Fprintf(c.opts.Log, "fuzz: minimizing %d-byte failing input file\n", len(result.entry.Data)) + c.queueForMinimization(result, nil) + } else if !crashWritten { + // Found a crasher that's either minimized or not minimizable. + // Write to corpus and stop. + err := writeToCorpus(&result.entry, opts.CorpusDir) + if err == nil { + crashWritten = true + err = &crashError{ + path: result.entry.Path, + err: errors.New(result.crasherMsg), + } + } + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG new crasher, elapsed: %s, id: %s, parent: %s, gen: %d, size: %d, exec time: %s\n", + c.elapsed(), + result.entry.Path, + result.entry.Parent, + result.entry.Generation, + len(result.entry.Data), + result.entryDuration, + ) + } + stop(err) + } + } else if result.coverageData != nil { + if c.warmupRun() { + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG processed an initial input, elapsed: %s, id: %s, new bits: %d, size: %d, exec time: %s\n", + c.elapsed(), + result.entry.Parent, + countBits(diffCoverage(c.coverageMask, result.coverageData)), + len(result.entry.Data), + result.entryDuration, + ) + } + c.updateCoverage(result.coverageData) + c.warmupInputLeft-- + if c.warmupInputLeft == 0 { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel) + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG finished processing input corpus, elapsed: %s, entries: %d, initial coverage bits: %d\n", + c.elapsed(), + len(c.corpus.entries), + countBits(c.coverageMask), + ) + } + } + } else if keepCoverage := diffCoverage(c.coverageMask, result.coverageData); keepCoverage != nil { + // Found a value that expanded coverage. + // It's not a crasher, but we may want to add it to the on-disk + // corpus and prioritize it for future fuzzing. + // TODO(jayconrod, katiehockman): Prioritize fuzzing these + // values which expanded coverage, perhaps based on the + // number of new edges that this result expanded. + // TODO(jayconrod, katiehockman): Don't write a value that's already + // in the corpus. + if c.canMinimize() && result.canMinimize && c.crashMinimizing == nil { + // Send back to workers to find a smaller value that preserves + // at least one new coverage bit. + c.queueForMinimization(result, keepCoverage) + } else { + // Update the coordinator's coverage mask and save the value. + inputSize := len(result.entry.Data) + entryNew, err := c.addCorpusEntries(true, result.entry) + if err != nil { + stop(err) + break + } + if !entryNew { + continue + } + c.updateCoverage(keepCoverage) + c.inputQueue.enqueue(result.entry) + c.interestingCount++ + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG new interesting input, elapsed: %s, id: %s, parent: %s, gen: %d, new bits: %d, total bits: %d, size: %d, exec time: %s\n", + c.elapsed(), + result.entry.Path, + result.entry.Parent, + result.entry.Generation, + countBits(keepCoverage), + countBits(c.coverageMask), + inputSize, + result.entryDuration, + ) + } + } + } else { + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG worker reported interesting input that doesn't expand coverage, elapsed: %s, id: %s, parent: %s, canMinimize: %t\n", + c.elapsed(), + result.entry.Path, + result.entry.Parent, + result.canMinimize, + ) + } + } + } else if c.warmupRun() { + // No error or coverage data was reported for this input during + // warmup, so continue processing results. + c.warmupInputLeft-- + if c.warmupInputLeft == 0 { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed, now fuzzing with %d workers\n", c.elapsed(), c.warmupInputCount, c.warmupInputCount, c.opts.Parallel) + if shouldPrintDebugInfo() { + fmt.Fprintf( + c.opts.Log, + "DEBUG finished testing-only phase, elapsed: %s, entries: %d\n", + time.Since(c.startTime), + len(c.corpus.entries), + ) + } + } + } + + // Once the result has been processed, stop the worker if we + // have reached the fuzzing limit. + if c.opts.Limit > 0 && c.count >= c.opts.Limit { + stop(nil) + } + + case inputC <- input: + // Sent the next input to a worker. + c.sentInput(input) + + case minimizeC <- minimizeInput: + // Sent the next input for minimization to a worker. + c.sentMinimizeInput(minimizeInput) + + case <-statTicker.C: + c.logStats() + } + } + + // TODO(jayconrod,katiehockman): if a crasher can't be written to the corpus, + // write to the cache instead. +} + +// crashError wraps a crasher written to the seed corpus. It saves the name +// of the file where the input causing the crasher was saved. The testing +// framework uses this to report a command to re-run that specific input. +type crashError struct { + path string + err error +} + +func (e *crashError) Error() string { + return e.err.Error() +} + +func (e *crashError) Unwrap() error { + return e.err +} + +func (e *crashError) CrashPath() string { + return e.path +} + +type corpus struct { + entries []CorpusEntry + hashes map[[sha256.Size]byte]bool +} + +// addCorpusEntries adds entries to the corpus, and optionally writes the entries +// to the cache directory. If an entry is already in the corpus it is skipped. If +// all of the entries are unique, addCorpusEntries returns true and a nil error, +// if at least one of the entries was a duplicate, it returns false and a nil error. +func (c *coordinator) addCorpusEntries(addToCache bool, entries ...CorpusEntry) (bool, error) { + noDupes := true + for _, e := range entries { + data, err := corpusEntryData(e) + if err != nil { + return false, err + } + h := sha256.Sum256(data) + if c.corpus.hashes[h] { + noDupes = false + continue + } + if addToCache { + if err := writeToCorpus(&e, c.opts.CacheDir); err != nil { + return false, err + } + // For entries written to disk, we don't hold onto the bytes, + // since the corpus would consume a significant amount of + // memory. + e.Data = nil + } + c.corpus.hashes[h] = true + c.corpus.entries = append(c.corpus.entries, e) + } + return noDupes, nil +} + +// CorpusEntry represents an individual input for fuzzing. +// +// We must use an equivalent type in the testing and testing/internal/testdeps +// packages, but testing can't import this package directly, and we don't want +// to export this type from testing. Instead, we use the same struct type and +// use a type alias (not a defined type) for convenience. +type CorpusEntry = struct { + Parent string + + // Path is the path of the corpus file, if the entry was loaded from disk. + // For other entries, including seed values provided by f.Add, Path is the + // name of the test, e.g. seed#0 or its hash. + Path string + + // Data is the raw input data. Data should only be populated for seed + // values. For on-disk corpus files, Data will be nil, as it will be loaded + // from disk using Path. + Data []byte + + // Values is the unmarshaled values from a corpus file. + Values []any + + Generation int + + // IsSeed indicates whether this entry is part of the seed corpus. + IsSeed bool +} + +// corpusEntryData returns the raw input bytes, either from the data struct +// field, or from disk. +func corpusEntryData(ce CorpusEntry) ([]byte, error) { + if ce.Data != nil { + return ce.Data, nil + } + + return os.ReadFile(ce.Path) +} + +type fuzzInput struct { + // entry is the value to test initially. The worker will randomly mutate + // values from this starting point. + entry CorpusEntry + + // timeout is the time to spend fuzzing variations of this input, + // not including starting or cleaning up. + timeout time.Duration + + // limit is the maximum number of calls to the fuzz function the worker may + // make. The worker may make fewer calls, for example, if it finds an + // error early. If limit is zero, there is no limit on calls to the + // fuzz function. + limit int64 + + // warmup indicates whether this is a warmup input before fuzzing begins. If + // true, the input should not be fuzzed. + warmup bool + + // coverageData reflects the coordinator's current coverageMask. + coverageData []byte +} + +type fuzzResult struct { + // entry is an interesting value or a crasher. + entry CorpusEntry + + // crasherMsg is an error message from a crash. It's "" if no crash was found. + crasherMsg string + + // canMinimize is true if the worker should attempt to minimize this result. + // It may be false because an attempt has already been made. + canMinimize bool + + // coverageData is set if the worker found new coverage. + coverageData []byte + + // limit is the number of values the coordinator asked the worker + // to test. 0 if there was no limit. + limit int64 + + // count is the number of values the worker actually tested. + count int64 + + // totalDuration is the time the worker spent testing inputs. + totalDuration time.Duration + + // entryDuration is the time the worker spent execution an interesting result + entryDuration time.Duration +} + +type fuzzMinimizeInput struct { + // entry is an interesting value or crasher to minimize. + entry CorpusEntry + + // crasherMsg is an error message from a crash. It's "" if no crash was found. + // If set, the worker will attempt to find a smaller input that also produces + // an error, though not necessarily the same error. + crasherMsg string + + // limit is the maximum number of calls to the fuzz function the worker may + // make. The worker may make fewer calls, for example, if it can't reproduce + // an error. If limit is zero, there is no limit on calls to the fuzz function. + limit int64 + + // timeout is the time to spend minimizing this input. + // A zero timeout means no limit. + timeout time.Duration + + // keepCoverage is a set of coverage bits that entry found that were not in + // the coordinator's combined set. When minimizing, the worker should find an + // input that preserves at least one of these bits. keepCoverage is nil for + // crashing inputs. + keepCoverage []byte +} + +// coordinator holds channels that workers can use to communicate with +// the coordinator. +type coordinator struct { + opts CoordinateFuzzingOpts + + // startTime is the time we started the workers after loading the corpus. + // Used for logging. + startTime time.Time + + // inputC is sent values to fuzz by the coordinator. Any worker may receive + // values from this channel. Workers send results to resultC. + inputC chan fuzzInput + + // minimizeC is sent values to minimize by the coordinator. Any worker may + // receive values from this channel. Workers send results to resultC. + minimizeC chan fuzzMinimizeInput + + // resultC is sent results of fuzzing by workers. The coordinator + // receives these. Multiple types of messages are allowed. + resultC chan fuzzResult + + // count is the number of values fuzzed so far. + count int64 + + // countLastLog is the number of values fuzzed when the output was last + // logged. + countLastLog int64 + + // timeLastLog is the time at which the output was last logged. + timeLastLog time.Time + + // interestingCount is the number of unique interesting values which have + // been found this execution. + interestingCount int + + // warmupInputCount is the count of all entries in the corpus which will + // need to be received from workers to run once during warmup, but not fuzz. + // This could be for coverage data, or only for the purposes of verifying + // that the seed corpus doesn't have any crashers. See warmupRun. + warmupInputCount int + + // warmupInputLeft is the number of entries in the corpus which still need + // to be received from workers to run once during warmup, but not fuzz. + // See warmupInputLeft. + warmupInputLeft int + + // duration is the time spent fuzzing inside workers, not counting time + // starting up or tearing down. + duration time.Duration + + // countWaiting is the number of fuzzing executions the coordinator is + // waiting on workers to complete. + countWaiting int64 + + // corpus is a set of interesting values, including the seed corpus and + // generated values that workers reported as interesting. + corpus corpus + + // minimizationAllowed is true if one or more of the types of fuzz + // function's parameters can be minimized. + minimizationAllowed bool + + // inputQueue is a queue of inputs that workers should try fuzzing. This is + // initially populated from the seed corpus and cached inputs. More inputs + // may be added as new coverage is discovered. + inputQueue queue + + // minimizeQueue is a queue of inputs that caused errors or exposed new + // coverage. Workers should attempt to find smaller inputs that do the + // same thing. + minimizeQueue queue + + // crashMinimizing is the crash that is currently being minimized. + crashMinimizing *fuzzResult + + // coverageMask aggregates coverage that was found for all inputs in the + // corpus. Each byte represents a single basic execution block. Each set bit + // within the byte indicates that an input has triggered that block at least + // 1 << n times, where n is the position of the bit in the byte. For example, a + // value of 12 indicates that separate inputs have triggered this block + // between 4-7 times and 8-15 times. + coverageMask []byte +} + +func newCoordinator(opts CoordinateFuzzingOpts) (*coordinator, error) { + // Make sure all of the seed corpus has marshalled data. + for i := range opts.Seed { + if opts.Seed[i].Data == nil && opts.Seed[i].Values != nil { + opts.Seed[i].Data = marshalCorpusFile(opts.Seed[i].Values...) + } + } + c := &coordinator{ + opts: opts, + startTime: time.Now(), + inputC: make(chan fuzzInput), + minimizeC: make(chan fuzzMinimizeInput), + resultC: make(chan fuzzResult), + timeLastLog: time.Now(), + corpus: corpus{hashes: make(map[[sha256.Size]byte]bool)}, + } + if err := c.readCache(); err != nil { + return nil, err + } + if opts.MinimizeLimit > 0 || opts.MinimizeTimeout > 0 { + for _, t := range opts.Types { + if isMinimizable(t) { + c.minimizationAllowed = true + break + } + } + } + + covSize := len(coverage()) + if covSize == 0 { + fmt.Fprintf(c.opts.Log, "warning: the test binary was not built with coverage instrumentation, so fuzzing will run without coverage guidance and may be inefficient\n") + // Even though a coverage-only run won't occur, we should still run all + // of the seed corpus to make sure there are no existing failures before + // we start fuzzing. + c.warmupInputCount = len(c.opts.Seed) + for _, e := range c.opts.Seed { + c.inputQueue.enqueue(e) + } + } else { + c.warmupInputCount = len(c.corpus.entries) + for _, e := range c.corpus.entries { + c.inputQueue.enqueue(e) + } + // Set c.coverageMask to a clean []byte full of zeros. + c.coverageMask = make([]byte, covSize) + } + c.warmupInputLeft = c.warmupInputCount + + if len(c.corpus.entries) == 0 { + fmt.Fprintf(c.opts.Log, "warning: starting with empty corpus\n") + var vals []any + for _, t := range opts.Types { + vals = append(vals, zeroValue(t)) + } + data := marshalCorpusFile(vals...) + h := sha256.Sum256(data) + name := fmt.Sprintf("%x", h[:4]) + c.addCorpusEntries(false, CorpusEntry{Path: name, Data: data}) + } + + return c, nil +} + +func (c *coordinator) updateStats(result fuzzResult) { + c.count += result.count + c.countWaiting -= result.limit + c.duration += result.totalDuration +} + +func (c *coordinator) logStats() { + now := time.Now() + if c.warmupRun() { + runSoFar := c.warmupInputCount - c.warmupInputLeft + if coverageEnabled { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, gathering baseline coverage: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount) + } else { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, testing seed corpus: %d/%d completed\n", c.elapsed(), runSoFar, c.warmupInputCount) + } + } else if c.crashMinimizing != nil { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, minimizing\n", c.elapsed()) + } else { + rate := float64(c.count-c.countLastLog) / now.Sub(c.timeLastLog).Seconds() + if coverageEnabled { + total := c.warmupInputCount + c.interestingCount + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec), new interesting: %d (total: %d)\n", c.elapsed(), c.count, rate, c.interestingCount, total) + } else { + fmt.Fprintf(c.opts.Log, "fuzz: elapsed: %s, execs: %d (%.0f/sec)\n", c.elapsed(), c.count, rate) + } + } + c.countLastLog = c.count + c.timeLastLog = now +} + +// peekInput returns the next value that should be sent to workers. +// If the number of executions is limited, the returned value includes +// a limit for one worker. If there are no executions left, peekInput returns +// a zero value and false. +// +// peekInput doesn't actually remove the input from the queue. The caller +// must call sentInput after sending the input. +// +// If the input queue is empty and the coverage/testing-only run has completed, +// queue refills it from the corpus. +func (c *coordinator) peekInput() (fuzzInput, bool) { + if c.opts.Limit > 0 && c.count+c.countWaiting >= c.opts.Limit { + // Already making the maximum number of calls to the fuzz function. + // Don't send more inputs right now. + return fuzzInput{}, false + } + if c.inputQueue.len == 0 { + if c.warmupRun() { + // Wait for coverage/testing-only run to finish before sending more + // inputs. + return fuzzInput{}, false + } + c.refillInputQueue() + } + + entry, ok := c.inputQueue.peek() + if !ok { + panic("input queue empty after refill") + } + input := fuzzInput{ + entry: entry.(CorpusEntry), + timeout: workerFuzzDuration, + warmup: c.warmupRun(), + } + if c.coverageMask != nil { + input.coverageData = make([]byte, len(c.coverageMask)) + copy(input.coverageData, c.coverageMask) + } + if input.warmup { + // No fuzzing will occur, but it should count toward the limit set by + // -fuzztime. + input.limit = 1 + return input, true + } + + if c.opts.Limit > 0 { + input.limit = c.opts.Limit / int64(c.opts.Parallel) + if c.opts.Limit%int64(c.opts.Parallel) > 0 { + input.limit++ + } + remaining := c.opts.Limit - c.count - c.countWaiting + if input.limit > remaining { + input.limit = remaining + } + } + return input, true +} + +// sentInput updates internal counters after an input is sent to c.inputC. +func (c *coordinator) sentInput(input fuzzInput) { + c.inputQueue.dequeue() + c.countWaiting += input.limit +} + +// refillInputQueue refills the input queue from the corpus after it becomes +// empty. +func (c *coordinator) refillInputQueue() { + for _, e := range c.corpus.entries { + c.inputQueue.enqueue(e) + } +} + +// queueForMinimization creates a fuzzMinimizeInput from result and adds it +// to the minimization queue to be sent to workers. +func (c *coordinator) queueForMinimization(result fuzzResult, keepCoverage []byte) { + if result.crasherMsg != "" { + c.minimizeQueue.clear() + } + + input := fuzzMinimizeInput{ + entry: result.entry, + crasherMsg: result.crasherMsg, + keepCoverage: keepCoverage, + } + c.minimizeQueue.enqueue(input) +} + +// peekMinimizeInput returns the next input that should be sent to workers for +// minimization. +func (c *coordinator) peekMinimizeInput() (fuzzMinimizeInput, bool) { + if !c.canMinimize() { + // Already making the maximum number of calls to the fuzz function. + // Don't send more inputs right now. + return fuzzMinimizeInput{}, false + } + v, ok := c.minimizeQueue.peek() + if !ok { + return fuzzMinimizeInput{}, false + } + input := v.(fuzzMinimizeInput) + + if c.opts.MinimizeTimeout > 0 { + input.timeout = c.opts.MinimizeTimeout + } + if c.opts.MinimizeLimit > 0 { + input.limit = c.opts.MinimizeLimit + } else if c.opts.Limit > 0 { + if input.crasherMsg != "" { + input.limit = c.opts.Limit + } else { + input.limit = c.opts.Limit / int64(c.opts.Parallel) + if c.opts.Limit%int64(c.opts.Parallel) > 0 { + input.limit++ + } + } + } + if c.opts.Limit > 0 { + remaining := c.opts.Limit - c.count - c.countWaiting + if input.limit > remaining { + input.limit = remaining + } + } + return input, true +} + +// sentMinimizeInput removes an input from the minimization queue after it's +// sent to minimizeC. +func (c *coordinator) sentMinimizeInput(input fuzzMinimizeInput) { + c.minimizeQueue.dequeue() + c.countWaiting += input.limit +} + +// warmupRun returns true while the coordinator is running inputs without +// mutating them as a warmup before fuzzing. This could be to gather baseline +// coverage data for entries in the corpus, or to test all of the seed corpus +// for errors before fuzzing begins. +// +// The coordinator doesn't store coverage data in the cache with each input +// because that data would be invalid when counter offsets in the test binary +// change. +// +// When gathering coverage, the coordinator sends each entry to a worker to +// gather coverage for that entry only, without fuzzing or minimizing. This +// phase ends when all workers have finished, and the coordinator has a combined +// coverage map. +func (c *coordinator) warmupRun() bool { + return c.warmupInputLeft > 0 +} + +// updateCoverage sets bits in c.coverageMask that are set in newCoverage. +// updateCoverage returns the number of newly set bits. See the comment on +// coverageMask for the format. +func (c *coordinator) updateCoverage(newCoverage []byte) int { + if len(newCoverage) != len(c.coverageMask) { + panic(fmt.Sprintf("number of coverage counters changed at runtime: %d, expected %d", len(newCoverage), len(c.coverageMask))) + } + newBitCount := 0 + for i := range newCoverage { + diff := newCoverage[i] &^ c.coverageMask[i] + newBitCount += bits.OnesCount8(diff) + c.coverageMask[i] |= newCoverage[i] + } + return newBitCount +} + +// canMinimize returns whether the coordinator should attempt to find smaller +// inputs that reproduce a crash or new coverage. +func (c *coordinator) canMinimize() bool { + return c.minimizationAllowed && + (c.opts.Limit == 0 || c.count+c.countWaiting < c.opts.Limit) +} + +func (c *coordinator) elapsed() time.Duration { + return time.Since(c.startTime).Round(1 * time.Second) +} + +// readCache creates a combined corpus from seed values and values in the cache +// (in GOCACHE/fuzz). +// +// TODO(fuzzing): need a mechanism that can remove values that +// aren't useful anymore, for example, because they have the wrong type. +func (c *coordinator) readCache() error { + if _, err := c.addCorpusEntries(false, c.opts.Seed...); err != nil { + return err + } + entries, err := ReadCorpus(c.opts.CacheDir, c.opts.Types) + if err != nil { + if _, ok := err.(*MalformedCorpusError); !ok { + // It's okay if some files in the cache directory are malformed and + // are not included in the corpus, but fail if it's an I/O error. + return err + } + // TODO(jayconrod,katiehockman): consider printing some kind of warning + // indicating the number of files which were skipped because they are + // malformed. + } + if _, err := c.addCorpusEntries(false, entries...); err != nil { + return err + } + return nil +} + +// MalformedCorpusError is an error found while reading the corpus from the +// filesystem. All of the errors are stored in the errs list. The testing +// framework uses this to report malformed files in testdata. +type MalformedCorpusError struct { + errs []error +} + +func (e *MalformedCorpusError) Error() string { + var msgs []string + for _, s := range e.errs { + msgs = append(msgs, s.Error()) + } + return strings.Join(msgs, "\n") +} + +// ReadCorpus reads the corpus from the provided dir. The returned corpus +// entries are guaranteed to match the given types. Any malformed files will +// be saved in a MalformedCorpusError and returned, along with the most recent +// error. +func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) { + files, err := ioutil.ReadDir(dir) + if os.IsNotExist(err) { + return nil, nil // No corpus to read + } else if err != nil { + return nil, fmt.Errorf("reading seed corpus from testdata: %v", err) + } + var corpus []CorpusEntry + var errs []error + for _, file := range files { + // TODO(jayconrod,katiehockman): determine when a file is a fuzzing input + // based on its name. We should only read files created by writeToCorpus. + // If we read ALL files, we won't be able to change the file format by + // changing the extension. We also won't be able to add files like + // README.txt explaining why the directory exists. + if file.IsDir() { + continue + } + filename := filepath.Join(dir, file.Name()) + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read corpus file: %v", err) + } + var vals []any + vals, err = readCorpusData(data, types) + if err != nil { + errs = append(errs, fmt.Errorf("%q: %v", filename, err)) + continue + } + corpus = append(corpus, CorpusEntry{Path: filename, Values: vals}) + } + if len(errs) > 0 { + return corpus, &MalformedCorpusError{errs: errs} + } + return corpus, nil +} + +func readCorpusData(data []byte, types []reflect.Type) ([]any, error) { + vals, err := unmarshalCorpusFile(data) + if err != nil { + return nil, fmt.Errorf("unmarshal: %v", err) + } + if err = CheckCorpus(vals, types); err != nil { + return nil, err + } + return vals, nil +} + +// CheckCorpus verifies that the types in vals match the expected types +// provided. +func CheckCorpus(vals []any, types []reflect.Type) error { + if len(vals) != len(types) { + return fmt.Errorf("wrong number of values in corpus entry: %d, want %d", len(vals), len(types)) + } + valsT := make([]reflect.Type, len(vals)) + for valsI, v := range vals { + valsT[valsI] = reflect.TypeOf(v) + } + for i := range types { + if valsT[i] != types[i] { + return fmt.Errorf("mismatched types in corpus entry: %v, want %v", valsT, types) + } + } + return nil +} + +// writeToCorpus atomically writes the given bytes to a new file in testdata. If +// the directory does not exist, it will create one. If the file already exists, +// writeToCorpus will not rewrite it. writeToCorpus sets entry.Path to the new +// file that was just written or an error if it failed. +func writeToCorpus(entry *CorpusEntry, dir string) (err error) { + sum := fmt.Sprintf("%x", sha256.Sum256(entry.Data)) + entry.Path = filepath.Join(dir, sum) + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + if err := ioutil.WriteFile(entry.Path, entry.Data, 0666); err != nil { + os.Remove(entry.Path) // remove partially written file + return err + } + return nil +} + +func testName(path string) string { + return filepath.Base(path) +} + +func zeroValue(t reflect.Type) any { + for _, v := range zeroVals { + if reflect.TypeOf(v) == t { + return v + } + } + panic(fmt.Sprintf("unsupported type: %v", t)) +} + +var zeroVals []any = []any{ + []byte(""), + string(""), + false, + byte(0), + rune(0), + float32(0), + float64(0), + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), +} + +var ( + debugInfo bool + debugInfoOnce sync.Once +) + +func shouldPrintDebugInfo() bool { + debugInfoOnce.Do(func() { + debugInfo = godebug.Get("fuzzdebug") == "1" + }) + return debugInfo +} diff --git a/src/internal/fuzz/mem.go b/src/internal/fuzz/mem.go new file mode 100644 index 00000000000000..a5c3b02242e380 --- /dev/null +++ b/src/internal/fuzz/mem.go @@ -0,0 +1,140 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "fmt" + "io/ioutil" + "os" + "unsafe" +) + +// sharedMem manages access to a region of virtual memory mapped from a file, +// shared between multiple processes. The region includes space for a header and +// a value of variable length. +// +// When fuzzing, the coordinator creates a sharedMem from a temporary file for +// each worker. This buffer is used to pass values to fuzz between processes. +// Care must be taken to manage access to shared memory across processes; +// sharedMem provides no synchronization on its own. See workerComm for an +// explanation. +type sharedMem struct { + // f is the file mapped into memory. + f *os.File + + // region is the mapped region of virtual memory for f. The content of f may + // be read or written through this slice. + region []byte + + // removeOnClose is true if the file should be deleted by Close. + removeOnClose bool + + // sys contains OS-specific information. + sys sharedMemSys +} + +// sharedMemHeader stores metadata in shared memory. +type sharedMemHeader struct { + // count is the number of times the worker has called the fuzz function. + // May be reset by coordinator. + count int64 + + // valueLen is the number of bytes in region which should be read. + valueLen int + + // randState and randInc hold the state of a pseudo-random number generator. + randState, randInc uint64 + + // rawInMem is true if the region holds raw bytes, which occurs during + // minimization. If true after the worker fails during minimization, this + // indicates that an unrecoverable error occurred, and the region can be + // used to retrieve the raw bytes that caused the error. + rawInMem bool +} + +// sharedMemSize returns the size needed for a shared memory buffer that can +// contain values of the given size. +func sharedMemSize(valueSize int) int { + // TODO(jayconrod): set a reasonable maximum size per platform. + return int(unsafe.Sizeof(sharedMemHeader{})) + valueSize +} + +// sharedMemTempFile creates a new temporary file of the given size, then maps +// it into memory. The file will be removed when the Close method is called. +func sharedMemTempFile(size int) (m *sharedMem, err error) { + // Create a temporary file. + f, err := ioutil.TempFile("", "fuzz-*") + if err != nil { + return nil, err + } + defer func() { + if err != nil { + f.Close() + os.Remove(f.Name()) + } + }() + + // Resize it to the correct size. + totalSize := sharedMemSize(size) + if err := f.Truncate(int64(totalSize)); err != nil { + return nil, err + } + + // Map the file into memory. + removeOnClose := true + return sharedMemMapFile(f, totalSize, removeOnClose) +} + +// header returns a pointer to metadata within the shared memory region. +func (m *sharedMem) header() *sharedMemHeader { + return (*sharedMemHeader)(unsafe.Pointer(&m.region[0])) +} + +// valueRef returns the value currently stored in shared memory. The returned +// slice points to shared memory; it is not a copy. +func (m *sharedMem) valueRef() []byte { + length := m.header().valueLen + valueOffset := int(unsafe.Sizeof(sharedMemHeader{})) + return m.region[valueOffset : valueOffset+length] +} + +// valueCopy returns a copy of the value stored in shared memory. +func (m *sharedMem) valueCopy() []byte { + ref := m.valueRef() + b := make([]byte, len(ref)) + copy(b, ref) + return b +} + +// setValue copies the data in b into the shared memory buffer and sets +// the length. len(b) must be less than or equal to the capacity of the buffer +// (as returned by cap(m.value())). +func (m *sharedMem) setValue(b []byte) { + v := m.valueRef() + if len(b) > cap(v) { + panic(fmt.Sprintf("value length %d larger than shared memory capacity %d", len(b), cap(v))) + } + m.header().valueLen = len(b) + copy(v[:cap(v)], b) +} + +// setValueLen sets the length of the shared memory buffer returned by valueRef +// to n, which may be at most the cap of that slice. +// +// Note that we can only store the length in the shared memory header. The full +// slice header contains a pointer, which is likely only valid for one process, +// since each process can map shared memory at a different virtual address. +func (m *sharedMem) setValueLen(n int) { + v := m.valueRef() + if n > cap(v) { + panic(fmt.Sprintf("length %d larger than shared memory capacity %d", n, cap(v))) + } + m.header().valueLen = n +} + +// TODO(jayconrod): add method to resize the buffer. We'll need that when the +// mutator can increase input length. Only the coordinator will be able to +// do it, since we'll need to send a message to the worker telling it to +// remap the file. diff --git a/src/internal/fuzz/minimize.go b/src/internal/fuzz/minimize.go new file mode 100644 index 00000000000000..0e410fb86a3ff8 --- /dev/null +++ b/src/internal/fuzz/minimize.go @@ -0,0 +1,95 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "reflect" +) + +func isMinimizable(t reflect.Type) bool { + return t == reflect.TypeOf("") || t == reflect.TypeOf([]byte(nil)) +} + +func minimizeBytes(v []byte, try func([]byte) bool, shouldStop func() bool) { + tmp := make([]byte, len(v)) + // If minimization was successful at any point during minimizeBytes, + // then the vals slice in (*workerServer).minimizeInput will point to + // tmp. Since tmp is altered while making new candidates, we need to + // make sure that it is equal to the correct value, v, before exiting + // this function. + defer copy(tmp, v) + + // First, try to cut the tail. + for n := 1024; n != 0; n /= 2 { + for len(v) > n { + if shouldStop() { + return + } + candidate := v[:len(v)-n] + if !try(candidate) { + break + } + // Set v to the new value to continue iterating. + v = candidate + } + } + + // Then, try to remove each individual byte. + for i := 0; i < len(v)-1; i++ { + if shouldStop() { + return + } + candidate := tmp[:len(v)-1] + copy(candidate[:i], v[:i]) + copy(candidate[i:], v[i+1:]) + if !try(candidate) { + continue + } + // Update v to delete the value at index i. + copy(v[i:], v[i+1:]) + v = v[:len(candidate)] + // v[i] is now different, so decrement i to redo this iteration + // of the loop with the new value. + i-- + } + + // Then, try to remove each possible subset of bytes. + for i := 0; i < len(v)-1; i++ { + copy(tmp, v[:i]) + for j := len(v); j > i+1; j-- { + if shouldStop() { + return + } + candidate := tmp[:len(v)-j+i] + copy(candidate[i:], v[j:]) + if !try(candidate) { + continue + } + // Update v and reset the loop with the new length. + copy(v[i:], v[j:]) + v = v[:len(candidate)] + j = len(v) + } + } + + // Then, try to make it more simplified and human-readable by trying to replace each + // byte with a printable character. + printableChars := []byte("012789ABCXYZabcxyz !\"#$%&'()*+,.") + for i, b := range v { + if shouldStop() { + return + } + + for _, pc := range printableChars { + v[i] = pc + if try(v) { + // Successful. Move on to the next byte in v. + break + } + // Unsuccessful. Revert v[i] back to original value. + v[i] = b + } + } +} diff --git a/src/internal/fuzz/minimize_test.go b/src/internal/fuzz/minimize_test.go new file mode 100644 index 00000000000000..2db26338967e27 --- /dev/null +++ b/src/internal/fuzz/minimize_test.go @@ -0,0 +1,182 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || linux || windows + +package fuzz + +import ( + "bytes" + "context" + "errors" + "fmt" + "reflect" + "testing" + "time" + "unicode" + "unicode/utf8" +) + +func TestMinimizeInput(t *testing.T) { + type testcase struct { + name string + fn func(CorpusEntry) error + input []any + expected []any + } + cases := []testcase{ + { + name: "ones_byte", + fn: func(e CorpusEntry) error { + b := e.Values[0].([]byte) + ones := 0 + for _, v := range b { + if v == 1 { + ones++ + } + } + if ones == 3 { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{[]byte{0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + expected: []any{[]byte{1, 1, 1}}, + }, + { + name: "single_bytes", + fn: func(e CorpusEntry) error { + b := e.Values[0].([]byte) + if len(b) < 2 { + return nil + } + if len(b) == 2 && b[0] == 1 && b[1] == 2 { + return nil + } + return fmt.Errorf("bad %v", e.Values[0]) + }, + input: []any{[]byte{1, 2, 3, 4, 5}}, + expected: []any{[]byte("00")}, + }, + { + name: "set_of_bytes", + fn: func(e CorpusEntry) error { + b := e.Values[0].([]byte) + if len(b) < 3 { + return nil + } + if bytes.Equal(b, []byte{0, 1, 2, 3, 4, 5}) || bytes.Equal(b, []byte{0, 4, 5}) { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{[]byte{0, 1, 2, 3, 4, 5}}, + expected: []any{[]byte{0, 4, 5}}, + }, + { + name: "non_ascii_bytes", + fn: func(e CorpusEntry) error { + b := e.Values[0].([]byte) + if len(b) == 3 { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{[]byte("ท")}, // ท is 3 bytes + expected: []any{[]byte("000")}, + }, + { + name: "ones_string", + fn: func(e CorpusEntry) error { + b := e.Values[0].(string) + ones := 0 + for _, v := range b { + if v == '1' { + ones++ + } + } + if ones == 3 { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{"001010001000000000000000000"}, + expected: []any{"111"}, + }, + { + name: "string_length", + fn: func(e CorpusEntry) error { + b := e.Values[0].(string) + if len(b) == 5 { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{"zzzzz"}, + expected: []any{"00000"}, + }, + { + name: "string_with_letter", + fn: func(e CorpusEntry) error { + b := e.Values[0].(string) + r, _ := utf8.DecodeRune([]byte(b)) + if unicode.IsLetter(r) { + return fmt.Errorf("bad %v", e.Values[0]) + } + return nil + }, + input: []any{"ZZZZZ"}, + expected: []any{"A"}, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ws := &workerServer{ + fuzzFn: func(e CorpusEntry) (time.Duration, error) { + return time.Second, tc.fn(e) + }, + } + mem := &sharedMem{region: make([]byte, 100)} // big enough to hold value and header + vals := tc.input + success, err := ws.minimizeInput(context.Background(), vals, mem, minimizeArgs{}) + if !success { + t.Errorf("minimizeInput did not succeed") + } + if err == nil { + t.Fatal("minimizeInput didn't provide an error") + } + if expected := fmt.Sprintf("bad %v", tc.expected[0]); err.Error() != expected { + t.Errorf("unexpected error: got %q, want %q", err, expected) + } + if !reflect.DeepEqual(vals, tc.expected) { + t.Errorf("unexpected results: got %v, want %v", vals, tc.expected) + } + }) + } +} + +// TestMinimizeFlaky checks that if we're minimizing an interesting +// input and a flaky failure occurs, that minimization was not indicated +// to be successful, and the error isn't returned (since it's flaky). +func TestMinimizeFlaky(t *testing.T) { + ws := &workerServer{fuzzFn: func(e CorpusEntry) (time.Duration, error) { + return time.Second, errors.New("ohno") + }} + mem := &sharedMem{region: make([]byte, 100)} // big enough to hold value and header + vals := []any{[]byte(nil)} + args := minimizeArgs{KeepCoverage: make([]byte, len(coverageSnapshot))} + success, err := ws.minimizeInput(context.Background(), vals, mem, args) + if success { + t.Error("unexpected success") + } + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if count := mem.header().count; count != 1 { + t.Errorf("count: got %d, want 1", count) + } +} diff --git a/src/internal/fuzz/mutator.go b/src/internal/fuzz/mutator.go new file mode 100644 index 00000000000000..bb960660ae86d7 --- /dev/null +++ b/src/internal/fuzz/mutator.go @@ -0,0 +1,300 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "encoding/binary" + "fmt" + "math" + "unsafe" +) + +type mutator struct { + r mutatorRand + scratch []byte // scratch slice to avoid additional allocations +} + +func newMutator() *mutator { + return &mutator{r: newPcgRand()} +} + +func (m *mutator) rand(n int) int { + return m.r.intn(n) +} + +func (m *mutator) randByteOrder() binary.ByteOrder { + if m.r.bool() { + return binary.LittleEndian + } + return binary.BigEndian +} + +// chooseLen chooses length of range mutation in range [1,n]. It gives +// preference to shorter ranges. +func (m *mutator) chooseLen(n int) int { + switch x := m.rand(100); { + case x < 90: + return m.rand(min(8, n)) + 1 + case x < 99: + return m.rand(min(32, n)) + 1 + default: + return m.rand(n) + 1 + } +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// mutate performs several mutations on the provided values. +func (m *mutator) mutate(vals []any, maxBytes int) { + // TODO(katiehockman): pull some of these functions into helper methods and + // test that each case is working as expected. + // TODO(katiehockman): perform more types of mutations for []byte. + + // maxPerVal will represent the maximum number of bytes that each value be + // allowed after mutating, giving an equal amount of capacity to each line. + // Allow a little wiggle room for the encoding. + maxPerVal := maxBytes/len(vals) - 100 + + // Pick a random value to mutate. + // TODO: consider mutating more than one value at a time. + i := m.rand(len(vals)) + switch v := vals[i].(type) { + case int: + vals[i] = int(m.mutateInt(int64(v), maxInt)) + case int8: + vals[i] = int8(m.mutateInt(int64(v), math.MaxInt8)) + case int16: + vals[i] = int16(m.mutateInt(int64(v), math.MaxInt16)) + case int64: + vals[i] = m.mutateInt(v, maxInt) + case uint: + vals[i] = uint(m.mutateUInt(uint64(v), maxUint)) + case uint16: + vals[i] = uint16(m.mutateUInt(uint64(v), math.MaxUint16)) + case uint32: + vals[i] = uint32(m.mutateUInt(uint64(v), math.MaxUint32)) + case uint64: + vals[i] = m.mutateUInt(uint64(v), maxUint) + case float32: + vals[i] = float32(m.mutateFloat(float64(v), math.MaxFloat32)) + case float64: + vals[i] = m.mutateFloat(v, math.MaxFloat64) + case bool: + if m.rand(2) == 1 { + vals[i] = !v // 50% chance of flipping the bool + } + case rune: // int32 + vals[i] = rune(m.mutateInt(int64(v), math.MaxInt32)) + case byte: // uint8 + vals[i] = byte(m.mutateUInt(uint64(v), math.MaxUint8)) + case string: + if len(v) > maxPerVal { + panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) + } + if cap(m.scratch) < maxPerVal { + m.scratch = append(make([]byte, 0, maxPerVal), v...) + } else { + m.scratch = m.scratch[:len(v)] + copy(m.scratch, v) + } + m.mutateBytes(&m.scratch) + vals[i] = string(m.scratch) + case []byte: + if len(v) > maxPerVal { + panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) + } + if cap(m.scratch) < maxPerVal { + m.scratch = append(make([]byte, 0, maxPerVal), v...) + } else { + m.scratch = m.scratch[:len(v)] + copy(m.scratch, v) + } + m.mutateBytes(&m.scratch) + vals[i] = m.scratch + default: + panic(fmt.Sprintf("type not supported for mutating: %T", vals[i])) + } +} + +func (m *mutator) mutateInt(v, maxValue int64) int64 { + var max int64 + for { + max = 100 + switch m.rand(2) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + v += int64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= -maxValue { + continue + } + if v < 0 && maxValue+v < max { + // Don't let v drop below -maxValue + max = maxValue + v + } + v -= int64(1 + m.rand(int(max))) + return v + } + } +} + +func (m *mutator) mutateUInt(v, maxValue uint64) uint64 { + var max uint64 + for { + max = 100 + switch m.rand(2) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + + v += uint64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= 0 { + continue + } + if v < max { + // Don't let v drop below 0 + max = v + } + v -= uint64(1 + m.rand(int(max))) + return v + } + } +} + +func (m *mutator) mutateFloat(v, maxValue float64) float64 { + var max float64 + for { + switch m.rand(4) { + case 0: + // Add a random number + if v >= maxValue { + continue + } + max = 100 + if v > 0 && maxValue-v < max { + // Don't let v exceed maxValue + max = maxValue - v + } + v += float64(1 + m.rand(int(max))) + return v + case 1: + // Subtract a random number + if v <= -maxValue { + continue + } + max = 100 + if v < 0 && maxValue+v < max { + // Don't let v drop below -maxValue + max = maxValue + v + } + v -= float64(1 + m.rand(int(max))) + return v + case 2: + // Multiply by a random number + absV := math.Abs(v) + if v == 0 || absV >= maxValue { + continue + } + max = 10 + if maxValue/absV < max { + // Don't let v go beyond the minimum or maximum value + max = maxValue / absV + } + v *= float64(1 + m.rand(int(max))) + return v + case 3: + // Divide by a random number + if v == 0 { + continue + } + v /= float64(1 + m.rand(10)) + return v + } + } +} + +type byteSliceMutator func(*mutator, []byte) []byte + +var byteSliceMutators = []byteSliceMutator{ + byteSliceRemoveBytes, + byteSliceInsertRandomBytes, + byteSliceDuplicateBytes, + byteSliceOverwriteBytes, + byteSliceBitFlip, + byteSliceXORByte, + byteSliceSwapByte, + byteSliceArithmeticUint8, + byteSliceArithmeticUint16, + byteSliceArithmeticUint32, + byteSliceArithmeticUint64, + byteSliceOverwriteInterestingUint8, + byteSliceOverwriteInterestingUint16, + byteSliceOverwriteInterestingUint32, + byteSliceInsertConstantBytes, + byteSliceOverwriteConstantBytes, + byteSliceShuffleBytes, + byteSliceSwapBytes, +} + +func (m *mutator) mutateBytes(ptrB *[]byte) { + b := *ptrB + defer func() { + if unsafe.SliceData(*ptrB) != unsafe.SliceData(b) { + panic("data moved to new address") + } + *ptrB = b + }() + + for { + mut := byteSliceMutators[m.rand(len(byteSliceMutators))] + if mutated := mut(m, b); mutated != nil { + b = mutated + return + } + } +} + +var ( + interesting8 = []int8{-128, -1, 0, 1, 16, 32, 64, 100, 127} + interesting16 = []int16{-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767} + interesting32 = []int32{-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647} +) + +const ( + maxUint = uint64(^uint(0)) + maxInt = int64(maxUint >> 1) +) + +func init() { + for _, v := range interesting8 { + interesting16 = append(interesting16, int16(v)) + } + for _, v := range interesting16 { + interesting32 = append(interesting32, int32(v)) + } +} diff --git a/src/internal/fuzz/mutator_test.go b/src/internal/fuzz/mutator_test.go new file mode 100644 index 00000000000000..cea7e2e3be8c33 --- /dev/null +++ b/src/internal/fuzz/mutator_test.go @@ -0,0 +1,117 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "bytes" + "fmt" + "os" + "strconv" + "testing" +) + +func BenchmarkMutatorBytes(b *testing.B) { + origEnv := os.Getenv("GODEBUG") + defer func() { os.Setenv("GODEBUG", origEnv) }() + os.Setenv("GODEBUG", fmt.Sprintf("%s,fuzzseed=123", origEnv)) + m := newMutator() + + for _, size := range []int{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + } { + b.Run(strconv.Itoa(size), func(b *testing.B) { + buf := make([]byte, size) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // resize buffer to the correct shape and reset the PCG + buf = buf[0:size] + m.r = newPcgRand() + m.mutate([]any{buf}, workerSharedMemSize) + } + }) + } +} + +func BenchmarkMutatorString(b *testing.B) { + origEnv := os.Getenv("GODEBUG") + defer func() { os.Setenv("GODEBUG", origEnv) }() + os.Setenv("GODEBUG", fmt.Sprintf("%s,fuzzseed=123", origEnv)) + m := newMutator() + + for _, size := range []int{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + } { + b.Run(strconv.Itoa(size), func(b *testing.B) { + buf := make([]byte, size) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // resize buffer to the correct shape and reset the PCG + buf = buf[0:size] + m.r = newPcgRand() + m.mutate([]any{string(buf)}, workerSharedMemSize) + } + }) + } +} + +func BenchmarkMutatorAllBasicTypes(b *testing.B) { + origEnv := os.Getenv("GODEBUG") + defer func() { os.Setenv("GODEBUG", origEnv) }() + os.Setenv("GODEBUG", fmt.Sprintf("%s,fuzzseed=123", origEnv)) + m := newMutator() + + types := []any{ + []byte(""), + string(""), + false, + float32(0), + float64(0), + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + } + + for _, t := range types { + b.Run(fmt.Sprintf("%T", t), func(b *testing.B) { + for i := 0; i < b.N; i++ { + m.r = newPcgRand() + m.mutate([]any{t}, workerSharedMemSize) + } + }) + } +} + +func TestStringImmutability(t *testing.T) { + v := []any{"hello"} + m := newMutator() + m.mutate(v, 1024) + original := v[0].(string) + originalCopy := make([]byte, len(original)) + copy(originalCopy, []byte(original)) + for i := 0; i < 25; i++ { + m.mutate(v, 1024) + } + if !bytes.Equal([]byte(original), originalCopy) { + t.Fatalf("string was mutated: got %x, want %x", []byte(original), originalCopy) + } +} diff --git a/src/internal/fuzz/mutators_byteslice.go b/src/internal/fuzz/mutators_byteslice.go new file mode 100644 index 00000000000000..d9dab1df9f4e85 --- /dev/null +++ b/src/internal/fuzz/mutators_byteslice.go @@ -0,0 +1,313 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +// byteSliceRemoveBytes removes a random chunk of bytes from b. +func byteSliceRemoveBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + pos0 := m.rand(len(b)) + pos1 := pos0 + m.chooseLen(len(b)-pos0) + copy(b[pos0:], b[pos1:]) + b = b[:len(b)-(pos1-pos0)] + return b +} + +// byteSliceInsertRandomBytes inserts a chunk of random bytes into b at a random +// position. +func byteSliceInsertRandomBytes(m *mutator, b []byte) []byte { + pos := m.rand(len(b) + 1) + n := m.chooseLen(1024) + if len(b)+n >= cap(b) { + return nil + } + b = b[:len(b)+n] + copy(b[pos+n:], b[pos:]) + for i := 0; i < n; i++ { + b[pos+i] = byte(m.rand(256)) + } + return b +} + +// byteSliceDuplicateBytes duplicates a chunk of bytes in b and inserts it into +// a random position. +func byteSliceDuplicateBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + n := m.chooseLen(len(b) - src) + // Use the end of the slice as scratch space to avoid doing an + // allocation. If the slice is too small abort and try something + // else. + if len(b)+(n*2) >= cap(b) { + return nil + } + end := len(b) + // Increase the size of b to fit the duplicated block as well as + // some extra working space + b = b[:end+(n*2)] + // Copy the block of bytes we want to duplicate to the end of the + // slice + copy(b[end+n:], b[src:src+n]) + // Shift the bytes after the splice point n positions to the right + // to make room for the new block + copy(b[dst+n:end+n], b[dst:end]) + // Insert the duplicate block into the splice point + copy(b[dst:], b[end+n:]) + b = b[:end+n] + return b +} + +// byteSliceOverwriteBytes overwrites a chunk of b with another chunk of b. +func byteSliceOverwriteBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + n := m.chooseLen(len(b) - src - 1) + copy(b[dst:], b[src:src+n]) + return b +} + +// byteSliceBitFlip flips a random bit in a random byte in b. +func byteSliceBitFlip(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + b[pos] ^= 1 << uint(m.rand(8)) + return b +} + +// byteSliceXORByte XORs a random byte in b with a random value. +func byteSliceXORByte(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + // In order to avoid a no-op (where the random value matches + // the existing value), use XOR instead of just setting to + // the random value. + b[pos] ^= byte(1 + m.rand(255)) + return b +} + +// byteSliceSwapByte swaps two random bytes in b. +func byteSliceSwapByte(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + b[src], b[dst] = b[dst], b[src] + return b +} + +// byteSliceArithmeticUint8 adds/subtracts from a random byte in b. +func byteSliceArithmeticUint8(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + v := byte(m.rand(35) + 1) + if m.r.bool() { + b[pos] += v + } else { + b[pos] -= v + } + return b +} + +// byteSliceArithmeticUint16 adds/subtracts from a random uint16 in b. +func byteSliceArithmeticUint16(m *mutator, b []byte) []byte { + if len(b) < 2 { + return nil + } + v := uint16(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 1) + enc := m.randByteOrder() + enc.PutUint16(b[pos:], enc.Uint16(b[pos:])+v) + return b +} + +// byteSliceArithmeticUint32 adds/subtracts from a random uint32 in b. +func byteSliceArithmeticUint32(m *mutator, b []byte) []byte { + if len(b) < 4 { + return nil + } + v := uint32(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 3) + enc := m.randByteOrder() + enc.PutUint32(b[pos:], enc.Uint32(b[pos:])+v) + return b +} + +// byteSliceArithmeticUint64 adds/subtracts from a random uint64 in b. +func byteSliceArithmeticUint64(m *mutator, b []byte) []byte { + if len(b) < 8 { + return nil + } + v := uint64(m.rand(35) + 1) + if m.r.bool() { + v = 0 - v + } + pos := m.rand(len(b) - 7) + enc := m.randByteOrder() + enc.PutUint64(b[pos:], enc.Uint64(b[pos:])+v) + return b +} + +// byteSliceOverwriteInterestingUint8 overwrites a random byte in b with an interesting +// value. +func byteSliceOverwriteInterestingUint8(m *mutator, b []byte) []byte { + if len(b) == 0 { + return nil + } + pos := m.rand(len(b)) + b[pos] = byte(interesting8[m.rand(len(interesting8))]) + return b +} + +// byteSliceOverwriteInterestingUint16 overwrites a random uint16 in b with an interesting +// value. +func byteSliceOverwriteInterestingUint16(m *mutator, b []byte) []byte { + if len(b) < 2 { + return nil + } + pos := m.rand(len(b) - 1) + v := uint16(interesting16[m.rand(len(interesting16))]) + m.randByteOrder().PutUint16(b[pos:], v) + return b +} + +// byteSliceOverwriteInterestingUint32 overwrites a random uint16 in b with an interesting +// value. +func byteSliceOverwriteInterestingUint32(m *mutator, b []byte) []byte { + if len(b) < 4 { + return nil + } + pos := m.rand(len(b) - 3) + v := uint32(interesting32[m.rand(len(interesting32))]) + m.randByteOrder().PutUint32(b[pos:], v) + return b +} + +// byteSliceInsertConstantBytes inserts a chunk of constant bytes into a random position in b. +func byteSliceInsertConstantBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + // TODO(rolandshoemaker,katiehockman): 4096 was mainly picked + // randomly. We may want to either pick a much larger value + // (AFL uses 32768, paired with a similar impl to chooseLen + // which biases towards smaller lengths that grow over time), + // or set the max based on characteristics of the corpus + // (libFuzzer sets a min/max based on the min/max size of + // entries in the corpus and then picks uniformly from + // that range). + n := m.chooseLen(4096) + if len(b)+n >= cap(b) { + return nil + } + b = b[:len(b)+n] + copy(b[dst+n:], b[dst:]) + rb := byte(m.rand(256)) + for i := dst; i < dst+n; i++ { + b[i] = rb + } + return b +} + +// byteSliceOverwriteConstantBytes overwrites a chunk of b with constant bytes. +func byteSliceOverwriteConstantBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + n := m.chooseLen(len(b) - dst) + rb := byte(m.rand(256)) + for i := dst; i < dst+n; i++ { + b[i] = rb + } + return b +} + +// byteSliceShuffleBytes shuffles a chunk of bytes in b. +func byteSliceShuffleBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + dst := m.rand(len(b)) + n := m.chooseLen(len(b) - dst) + if n <= 2 { + return nil + } + // Start at the end of the range, and iterate backwards + // to dst, swapping each element with another element in + // dst:dst+n (Fisher-Yates shuffle). + for i := n - 1; i > 0; i-- { + j := m.rand(i + 1) + b[dst+i], b[dst+j] = b[dst+j], b[dst+i] + } + return b +} + +// byteSliceSwapBytes swaps two chunks of bytes in b. +func byteSliceSwapBytes(m *mutator, b []byte) []byte { + if len(b) <= 1 { + return nil + } + src := m.rand(len(b)) + dst := m.rand(len(b)) + for dst == src { + dst = m.rand(len(b)) + } + // Choose the random length as len(b) - max(src, dst) + // so that we don't attempt to swap a chunk that extends + // beyond the end of the slice + max := dst + if src > max { + max = src + } + n := m.chooseLen(len(b) - max - 1) + // Check that neither chunk intersect, so that we don't end up + // duplicating parts of the input, rather than swapping them + if src > dst && dst+n >= src || dst > src && src+n >= dst { + return nil + } + // Use the end of the slice as scratch space to avoid doing an + // allocation. If the slice is too small abort and try something + // else. + if len(b)+n >= cap(b) { + return nil + } + end := len(b) + b = b[:end+n] + copy(b[end:], b[dst:dst+n]) + copy(b[dst:], b[src:src+n]) + copy(b[src:], b[end:]) + b = b[:end] + return b +} diff --git a/src/internal/fuzz/mutators_byteslice_test.go b/src/internal/fuzz/mutators_byteslice_test.go new file mode 100644 index 00000000000000..78869678818724 --- /dev/null +++ b/src/internal/fuzz/mutators_byteslice_test.go @@ -0,0 +1,186 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "bytes" + "testing" +) + +type mockRand struct { + values []int + counter int + b bool +} + +func (mr *mockRand) uint32() uint32 { + c := mr.values[mr.counter] + mr.counter++ + return uint32(c) +} + +func (mr *mockRand) intn(n int) int { + c := mr.values[mr.counter] + mr.counter++ + return c % n +} + +func (mr *mockRand) uint32n(n uint32) uint32 { + c := mr.values[mr.counter] + mr.counter++ + return uint32(c) % n +} + +func (mr *mockRand) exp2() int { + c := mr.values[mr.counter] + mr.counter++ + return c +} + +func (mr *mockRand) bool() bool { + b := mr.b + mr.b = !mr.b + return b +} + +func (mr *mockRand) save(*uint64, *uint64) { + panic("unimplemented") +} + +func (mr *mockRand) restore(uint64, uint64) { + panic("unimplemented") +} + +func TestByteSliceMutators(t *testing.T) { + for _, tc := range []struct { + name string + mutator func(*mutator, []byte) []byte + randVals []int + input []byte + expected []byte + }{ + { + name: "byteSliceRemoveBytes", + mutator: byteSliceRemoveBytes, + input: []byte{1, 2, 3, 4}, + expected: []byte{4}, + }, + { + name: "byteSliceInsertRandomBytes", + mutator: byteSliceInsertRandomBytes, + input: make([]byte, 4, 8), + expected: []byte{3, 4, 5, 0, 0, 0, 0}, + }, + { + name: "byteSliceDuplicateBytes", + mutator: byteSliceDuplicateBytes, + input: append(make([]byte, 0, 13), []byte{1, 2, 3, 4}...), + expected: []byte{1, 1, 2, 3, 4, 2, 3, 4}, + }, + { + name: "byteSliceOverwriteBytes", + mutator: byteSliceOverwriteBytes, + input: []byte{1, 2, 3, 4}, + expected: []byte{1, 1, 3, 4}, + }, + { + name: "byteSliceBitFlip", + mutator: byteSliceBitFlip, + input: []byte{1, 2, 3, 4}, + expected: []byte{3, 2, 3, 4}, + }, + { + name: "byteSliceXORByte", + mutator: byteSliceXORByte, + input: []byte{1, 2, 3, 4}, + expected: []byte{3, 2, 3, 4}, + }, + { + name: "byteSliceSwapByte", + mutator: byteSliceSwapByte, + input: []byte{1, 2, 3, 4}, + expected: []byte{2, 1, 3, 4}, + }, + { + name: "byteSliceArithmeticUint8", + mutator: byteSliceArithmeticUint8, + input: []byte{1, 2, 3, 4}, + expected: []byte{255, 2, 3, 4}, + }, + { + name: "byteSliceArithmeticUint16", + mutator: byteSliceArithmeticUint16, + input: []byte{1, 2, 3, 4}, + expected: []byte{1, 3, 3, 4}, + }, + { + name: "byteSliceArithmeticUint32", + mutator: byteSliceArithmeticUint32, + input: []byte{1, 2, 3, 4}, + expected: []byte{2, 2, 3, 4}, + }, + { + name: "byteSliceArithmeticUint64", + mutator: byteSliceArithmeticUint64, + input: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + expected: []byte{2, 2, 3, 4, 5, 6, 7, 8}, + }, + { + name: "byteSliceOverwriteInterestingUint8", + mutator: byteSliceOverwriteInterestingUint8, + input: []byte{1, 2, 3, 4}, + expected: []byte{255, 2, 3, 4}, + }, + { + name: "byteSliceOverwriteInterestingUint16", + mutator: byteSliceOverwriteInterestingUint16, + input: []byte{1, 2, 3, 4}, + expected: []byte{255, 127, 3, 4}, + }, + { + name: "byteSliceOverwriteInterestingUint32", + mutator: byteSliceOverwriteInterestingUint32, + input: []byte{1, 2, 3, 4}, + expected: []byte{250, 0, 0, 250}, + }, + { + name: "byteSliceInsertConstantBytes", + mutator: byteSliceInsertConstantBytes, + input: append(make([]byte, 0, 8), []byte{1, 2, 3, 4}...), + expected: []byte{3, 3, 3, 1, 2, 3, 4}, + }, + { + name: "byteSliceOverwriteConstantBytes", + mutator: byteSliceOverwriteConstantBytes, + input: []byte{1, 2, 3, 4}, + expected: []byte{3, 3, 3, 4}, + }, + { + name: "byteSliceShuffleBytes", + mutator: byteSliceShuffleBytes, + input: []byte{1, 2, 3, 4}, + expected: []byte{2, 3, 1, 4}, + }, + { + name: "byteSliceSwapBytes", + mutator: byteSliceSwapBytes, + randVals: []int{0, 2, 0, 2}, + input: append(make([]byte, 0, 9), []byte{1, 2, 3, 4}...), + expected: []byte{3, 2, 1, 4}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + r := &mockRand{values: []int{0, 1, 2, 3, 4, 5}} + if tc.randVals != nil { + r.values = tc.randVals + } + m := &mutator{r: r} + b := tc.mutator(m, tc.input) + if !bytes.Equal(b, tc.expected) { + t.Errorf("got %x, want %x", b, tc.expected) + } + }) + } +} diff --git a/src/internal/fuzz/pcg.go b/src/internal/fuzz/pcg.go new file mode 100644 index 00000000000000..c9ea0afcf8c328 --- /dev/null +++ b/src/internal/fuzz/pcg.go @@ -0,0 +1,145 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "math/bits" + "os" + "strconv" + "strings" + "sync/atomic" + "time" +) + +type mutatorRand interface { + uint32() uint32 + intn(int) int + uint32n(uint32) uint32 + exp2() int + bool() bool + + save(randState, randInc *uint64) + restore(randState, randInc uint64) +} + +// The functions in pcg implement a 32 bit PRNG with a 64 bit period: pcg xsh rr +// 64 32. See https://www.pcg-random.org/ for more information. This +// implementation is geared specifically towards the needs of fuzzing: Simple +// creation and use, no reproducibility, no concurrency safety, just the +// necessary methods, optimized for speed. + +var globalInc uint64 // PCG stream + +const multiplier uint64 = 6364136223846793005 + +// pcgRand is a PRNG. It should not be copied or shared. No Rand methods are +// concurrency safe. +type pcgRand struct { + noCopy noCopy // help avoid mistakes: ask vet to ensure that we don't make a copy + state uint64 + inc uint64 +} + +func godebugSeed() *int { + debug := strings.Split(os.Getenv("GODEBUG"), ",") + for _, f := range debug { + if strings.HasPrefix(f, "fuzzseed=") { + seed, err := strconv.Atoi(strings.TrimPrefix(f, "fuzzseed=")) + if err != nil { + panic("malformed fuzzseed") + } + return &seed + } + } + return nil +} + +// newPcgRand generates a new, seeded Rand, ready for use. +func newPcgRand() *pcgRand { + r := new(pcgRand) + now := uint64(time.Now().UnixNano()) + if seed := godebugSeed(); seed != nil { + now = uint64(*seed) + } + inc := atomic.AddUint64(&globalInc, 1) + r.state = now + r.inc = (inc << 1) | 1 + r.step() + r.state += now + r.step() + return r +} + +func (r *pcgRand) step() { + r.state *= multiplier + r.state += r.inc +} + +func (r *pcgRand) save(randState, randInc *uint64) { + *randState = r.state + *randInc = r.inc +} + +func (r *pcgRand) restore(randState, randInc uint64) { + r.state = randState + r.inc = randInc +} + +// uint32 returns a pseudo-random uint32. +func (r *pcgRand) uint32() uint32 { + x := r.state + r.step() + return bits.RotateLeft32(uint32(((x>>18)^x)>>27), -int(x>>59)) +} + +// intn returns a pseudo-random number in [0, n). +// n must fit in a uint32. +func (r *pcgRand) intn(n int) int { + if int(uint32(n)) != n { + panic("large Intn") + } + return int(r.uint32n(uint32(n))) +} + +// uint32n returns a pseudo-random number in [0, n). +// +// For implementation details, see: +// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction +// https://lemire.me/blog/2016/06/30/fast-random-shuffling +func (r *pcgRand) uint32n(n uint32) uint32 { + v := r.uint32() + prod := uint64(v) * uint64(n) + low := uint32(prod) + if low < n { + thresh := uint32(-int32(n)) % n + for low < thresh { + v = r.uint32() + prod = uint64(v) * uint64(n) + low = uint32(prod) + } + } + return uint32(prod >> 32) +} + +// exp2 generates n with probability 1/2^(n+1). +func (r *pcgRand) exp2() int { + return bits.TrailingZeros32(r.uint32()) +} + +// bool generates a random bool. +func (r *pcgRand) bool() bool { + return r.uint32()&1 == 0 +} + +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) lock() {} +func (*noCopy) unlock() {} diff --git a/src/internal/fuzz/queue.go b/src/internal/fuzz/queue.go new file mode 100644 index 00000000000000..42a8379541f881 --- /dev/null +++ b/src/internal/fuzz/queue.go @@ -0,0 +1,71 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +// queue holds a growable sequence of inputs for fuzzing and minimization. +// +// For now, this is a simple ring buffer +// (https://en.wikipedia.org/wiki/Circular_buffer). +// +// TODO(golang.org/issue/46224): use a priotization algorithm based on input +// size, previous duration, coverage, and any other metrics that seem useful. +type queue struct { + // elems holds a ring buffer. + // The queue is empty when begin = end. + // The queue is full (until grow is called) when end = begin + N - 1 (mod N) + // where N = cap(elems). + elems []any + head, len int +} + +func (q *queue) cap() int { + return len(q.elems) +} + +func (q *queue) grow() { + oldCap := q.cap() + newCap := oldCap * 2 + if newCap == 0 { + newCap = 8 + } + newElems := make([]any, newCap) + oldLen := q.len + for i := 0; i < oldLen; i++ { + newElems[i] = q.elems[(q.head+i)%oldCap] + } + q.elems = newElems + q.head = 0 +} + +func (q *queue) enqueue(e any) { + if q.len+1 > q.cap() { + q.grow() + } + i := (q.head + q.len) % q.cap() + q.elems[i] = e + q.len++ +} + +func (q *queue) dequeue() (any, bool) { + if q.len == 0 { + return nil, false + } + e := q.elems[q.head] + q.elems[q.head] = nil + q.head = (q.head + 1) % q.cap() + q.len-- + return e, true +} + +func (q *queue) peek() (any, bool) { + if q.len == 0 { + return nil, false + } + return q.elems[q.head], true +} + +func (q *queue) clear() { + *q = queue{} +} diff --git a/src/internal/fuzz/queue_test.go b/src/internal/fuzz/queue_test.go new file mode 100644 index 00000000000000..3b179afb573ce7 --- /dev/null +++ b/src/internal/fuzz/queue_test.go @@ -0,0 +1,58 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import "testing" + +func TestQueue(t *testing.T) { + // Zero valued queue should have 0 length and capacity. + var q queue + if n := q.len; n != 0 { + t.Fatalf("empty queue has len %d; want 0", n) + } + if n := q.cap(); n != 0 { + t.Fatalf("empty queue has cap %d; want 0", n) + } + + // As we add elements, len should grow. + N := 32 + for i := 0; i < N; i++ { + q.enqueue(i) + if n := q.len; n != i+1 { + t.Fatalf("after adding %d elements, queue has len %d", i, n) + } + if v, ok := q.peek(); !ok { + t.Fatalf("couldn't peek after adding %d elements", i) + } else if v.(int) != 0 { + t.Fatalf("after adding %d elements, peek is %d; want 0", i, v) + } + } + + // As we remove and add elements, len should shrink and grow. + // We should also remove elements in the same order they were added. + want := 0 + for _, r := range []int{1, 2, 3, 5, 8, 13, 21} { + s := make([]int, 0, r) + for i := 0; i < r; i++ { + if got, ok := q.dequeue(); !ok { + t.Fatalf("after removing %d of %d elements, could not dequeue", i+1, r) + } else if got != want { + t.Fatalf("after removing %d of %d elements, got %d; want %d", i+1, r, got, want) + } else { + s = append(s, got.(int)) + } + want = (want + 1) % N + if n := q.len; n != N-i-1 { + t.Fatalf("after removing %d of %d elements, len is %d; want %d", i+1, r, n, N-i-1) + } + } + for i, v := range s { + q.enqueue(v) + if n := q.len; n != N-r+i+1 { + t.Fatalf("after adding back %d of %d elements, len is %d; want %d", i+1, r, n, n-r+i+1) + } + } + } +} diff --git a/src/internal/fuzz/sys_posix.go b/src/internal/fuzz/sys_posix.go new file mode 100644 index 00000000000000..fec6054f671cc8 --- /dev/null +++ b/src/internal/fuzz/sys_posix.go @@ -0,0 +1,130 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || linux + +package fuzz + +import ( + "fmt" + "os" + "os/exec" + "syscall" +) + +type sharedMemSys struct{} + +func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) { + prot := syscall.PROT_READ | syscall.PROT_WRITE + flags := syscall.MAP_FILE | syscall.MAP_SHARED + region, err := syscall.Mmap(int(f.Fd()), 0, size, prot, flags) + if err != nil { + return nil, err + } + + return &sharedMem{f: f, region: region, removeOnClose: removeOnClose}, nil +} + +// Close unmaps the shared memory and closes the temporary file. If this +// sharedMem was created with sharedMemTempFile, Close also removes the file. +func (m *sharedMem) Close() error { + // Attempt all operations, even if we get an error for an earlier operation. + // os.File.Close may fail due to I/O errors, but we still want to delete + // the temporary file. + var errs []error + errs = append(errs, + syscall.Munmap(m.region), + m.f.Close()) + if m.removeOnClose { + errs = append(errs, os.Remove(m.f.Name())) + } + for _, err := range errs { + if err != nil { + return err + } + } + return nil +} + +// setWorkerComm configures communication channels on the cmd that will +// run a worker process. +func setWorkerComm(cmd *exec.Cmd, comm workerComm) { + mem := <-comm.memMu + memFile := mem.f + comm.memMu <- mem + cmd.ExtraFiles = []*os.File{comm.fuzzIn, comm.fuzzOut, memFile} +} + +// getWorkerComm returns communication channels in the worker process. +func getWorkerComm() (comm workerComm, err error) { + fuzzIn := os.NewFile(3, "fuzz_in") + fuzzOut := os.NewFile(4, "fuzz_out") + memFile := os.NewFile(5, "fuzz_mem") + fi, err := memFile.Stat() + if err != nil { + return workerComm{}, err + } + size := int(fi.Size()) + if int64(size) != fi.Size() { + return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size") + } + removeOnClose := false + mem, err := sharedMemMapFile(memFile, size, removeOnClose) + if err != nil { + return workerComm{}, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil +} + +// isInterruptError returns whether an error was returned by a process that +// was terminated by an interrupt signal (SIGINT). +func isInterruptError(err error) bool { + exitErr, ok := err.(*exec.ExitError) + if !ok || exitErr.ExitCode() >= 0 { + return false + } + status := exitErr.Sys().(syscall.WaitStatus) + return status.Signal() == syscall.SIGINT +} + +// terminationSignal checks if err is an exec.ExitError with a signal status. +// If it is, terminationSignal returns the signal and true. +// If not, -1 and false. +func terminationSignal(err error) (os.Signal, bool) { + exitErr, ok := err.(*exec.ExitError) + if !ok || exitErr.ExitCode() >= 0 { + return syscall.Signal(-1), false + } + status := exitErr.Sys().(syscall.WaitStatus) + return status.Signal(), status.Signaled() +} + +// isCrashSignal returns whether a signal was likely to have been caused by an +// error in the program that received it, triggered by a fuzz input. For +// example, SIGSEGV would be received after a nil pointer dereference. +// Other signals like SIGKILL or SIGHUP are more likely to have been sent by +// another process, and we shouldn't record a crasher if the worker process +// receives one of these. +// +// Note that Go installs its own signal handlers on startup, so some of these +// signals may only be received if signal handlers are changed. For example, +// SIGSEGV is normally transformed into a panic that causes the process to exit +// with status 2 if not recovered, which we handle as a crash. +func isCrashSignal(signal os.Signal) bool { + switch signal { + case + syscall.SIGILL, // illegal instruction + syscall.SIGTRAP, // breakpoint + syscall.SIGABRT, // abort() called + syscall.SIGBUS, // invalid memory access (e.g., misaligned address) + syscall.SIGFPE, // math error, e.g., integer divide by zero + syscall.SIGSEGV, // invalid memory access (e.g., write to read-only) + syscall.SIGPIPE: // sent data to closed pipe or socket + return true + default: + return false + } +} diff --git a/src/internal/fuzz/sys_unimplemented.go b/src/internal/fuzz/sys_unimplemented.go new file mode 100644 index 00000000000000..f84dae6a61fddb --- /dev/null +++ b/src/internal/fuzz/sys_unimplemented.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// If you update this constraint, also update cmd/internal/sys.FuzzSupported. +// +//go:build !darwin && !freebsd && !linux && !windows + +package fuzz + +import ( + "os" + "os/exec" +) + +type sharedMemSys struct{} + +func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) { + panic("not implemented") +} + +func (m *sharedMem) Close() error { + panic("not implemented") +} + +func setWorkerComm(cmd *exec.Cmd, comm workerComm) { + panic("not implemented") +} + +func getWorkerComm() (comm workerComm, err error) { + panic("not implemented") +} + +func isInterruptError(err error) bool { + panic("not implemented") +} + +func terminationSignal(err error) (os.Signal, bool) { + panic("not implemented") +} + +func isCrashSignal(signal os.Signal) bool { + panic("not implemented") +} diff --git a/src/internal/fuzz/sys_windows.go b/src/internal/fuzz/sys_windows.go new file mode 100644 index 00000000000000..aa85be7e1c64d7 --- /dev/null +++ b/src/internal/fuzz/sys_windows.go @@ -0,0 +1,147 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "fmt" + "os" + "os/exec" + "syscall" + "unsafe" +) + +type sharedMemSys struct { + mapObj syscall.Handle +} + +func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (mem *sharedMem, err error) { + defer func() { + if err != nil { + err = fmt.Errorf("mapping temporary file %s: %w", f.Name(), err) + } + }() + + // Create a file mapping object. The object itself is not shared. + mapObj, err := syscall.CreateFileMapping( + syscall.Handle(f.Fd()), // fhandle + nil, // sa + syscall.PAGE_READWRITE, // prot + 0, // maxSizeHigh + 0, // maxSizeLow + nil, // name + ) + if err != nil { + return nil, err + } + + // Create a view from the file mapping object. + access := uint32(syscall.FILE_MAP_READ | syscall.FILE_MAP_WRITE) + addr, err := syscall.MapViewOfFile( + mapObj, // handle + access, // access + 0, // offsetHigh + 0, // offsetLow + uintptr(size), // length + ) + if err != nil { + syscall.CloseHandle(mapObj) + return nil, err + } + + region := unsafe.Slice((*byte)(unsafe.Pointer(addr)), size) + return &sharedMem{ + f: f, + region: region, + removeOnClose: removeOnClose, + sys: sharedMemSys{mapObj: mapObj}, + }, nil +} + +// Close unmaps the shared memory and closes the temporary file. If this +// sharedMem was created with sharedMemTempFile, Close also removes the file. +func (m *sharedMem) Close() error { + // Attempt all operations, even if we get an error for an earlier operation. + // os.File.Close may fail due to I/O errors, but we still want to delete + // the temporary file. + var errs []error + errs = append(errs, + syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&m.region[0]))), + syscall.CloseHandle(m.sys.mapObj), + m.f.Close()) + if m.removeOnClose { + errs = append(errs, os.Remove(m.f.Name())) + } + for _, err := range errs { + if err != nil { + return err + } + } + return nil +} + +// setWorkerComm configures communication channels on the cmd that will +// run a worker process. +func setWorkerComm(cmd *exec.Cmd, comm workerComm) { + mem := <-comm.memMu + memName := mem.f.Name() + comm.memMu <- mem + syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) + syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1) + cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%q", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), memName)) + cmd.SysProcAttr = &syscall.SysProcAttr{AdditionalInheritedHandles: []syscall.Handle{syscall.Handle(comm.fuzzIn.Fd()), syscall.Handle(comm.fuzzOut.Fd())}} +} + +// getWorkerComm returns communication channels in the worker process. +func getWorkerComm() (comm workerComm, err error) { + v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES") + if v == "" { + return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set") + } + var fuzzInFD, fuzzOutFD uintptr + var memName string + if _, err := fmt.Sscanf(v, "%x,%x,%q", &fuzzInFD, &fuzzOutFD, &memName); err != nil { + return workerComm{}, fmt.Errorf("parsing GO_TEST_FUZZ_WORKER_HANDLES=%s: %v", v, err) + } + + fuzzIn := os.NewFile(fuzzInFD, "fuzz_in") + fuzzOut := os.NewFile(fuzzOutFD, "fuzz_out") + tmpFile, err := os.OpenFile(memName, os.O_RDWR, 0) + if err != nil { + return workerComm{}, fmt.Errorf("worker opening temp file: %w", err) + } + fi, err := tmpFile.Stat() + if err != nil { + return workerComm{}, fmt.Errorf("worker checking temp file size: %w", err) + } + size := int(fi.Size()) + if int64(size) != fi.Size() { + return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size") + } + removeOnClose := false + mem, err := sharedMemMapFile(tmpFile, size, removeOnClose) + if err != nil { + return workerComm{}, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + + return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil +} + +func isInterruptError(err error) bool { + // On Windows, we can't tell whether the process was interrupted by the error + // returned by Wait. It looks like an ExitError with status 1. + return false +} + +// terminationSignal returns -1 and false because Windows doesn't have signals. +func terminationSignal(err error) (os.Signal, bool) { + return syscall.Signal(-1), false +} + +// isCrashSignal is not implemented because Windows doesn't have signals. +func isCrashSignal(signal os.Signal) bool { + panic("not implemented: no signals on windows") +} diff --git a/src/internal/fuzz/trace.go b/src/internal/fuzz/trace.go new file mode 100644 index 00000000000000..5e3ccccfadbb0b --- /dev/null +++ b/src/internal/fuzz/trace.go @@ -0,0 +1,35 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !libfuzzer + +package fuzz + +import _ "unsafe" // for go:linkname + +//go:linkname libfuzzerTraceCmp1 runtime.libfuzzerTraceCmp1 +//go:linkname libfuzzerTraceCmp2 runtime.libfuzzerTraceCmp2 +//go:linkname libfuzzerTraceCmp4 runtime.libfuzzerTraceCmp4 +//go:linkname libfuzzerTraceCmp8 runtime.libfuzzerTraceCmp8 + +//go:linkname libfuzzerTraceConstCmp1 runtime.libfuzzerTraceConstCmp1 +//go:linkname libfuzzerTraceConstCmp2 runtime.libfuzzerTraceConstCmp2 +//go:linkname libfuzzerTraceConstCmp4 runtime.libfuzzerTraceConstCmp4 +//go:linkname libfuzzerTraceConstCmp8 runtime.libfuzzerTraceConstCmp8 + +//go:linkname libfuzzerHookStrCmp runtime.libfuzzerHookStrCmp +//go:linkname libfuzzerHookEqualFold runtime.libfuzzerHookEqualFold + +func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) {} +func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) {} +func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) {} +func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) {} + +func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) {} +func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) {} +func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) {} +func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) {} + +func libfuzzerHookStrCmp(arg0, arg1 string, fakePC int) {} +func libfuzzerHookEqualFold(arg0, arg1 string, fakePC int) {} diff --git a/src/internal/fuzz/worker.go b/src/internal/fuzz/worker.go new file mode 100644 index 00000000000000..6e4c4e2d0fbbb1 --- /dev/null +++ b/src/internal/fuzz/worker.go @@ -0,0 +1,1185 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "reflect" + "runtime" + "sync" + "time" +) + +const ( + // workerFuzzDuration is the amount of time a worker can spend testing random + // variations of an input given by the coordinator. + workerFuzzDuration = 100 * time.Millisecond + + // workerTimeoutDuration is the amount of time a worker can go without + // responding to the coordinator before being stopped. + workerTimeoutDuration = 1 * time.Second + + // workerExitCode is used as an exit code by fuzz worker processes after an internal error. + // This distinguishes internal errors from uncontrolled panics and other crashes. + // Keep in sync with internal/fuzz.workerExitCode. + workerExitCode = 70 + + // workerSharedMemSize is the maximum size of the shared memory file used to + // communicate with workers. This limits the size of fuzz inputs. + workerSharedMemSize = 100 << 20 // 100 MB +) + +// worker manages a worker process running a test binary. The worker object +// exists only in the coordinator (the process started by 'go test -fuzz'). +// workerClient is used by the coordinator to send RPCs to the worker process, +// which handles them with workerServer. +type worker struct { + dir string // working directory, same as package directory + binPath string // path to test executable + args []string // arguments for test executable + env []string // environment for test executable + + coordinator *coordinator + + memMu chan *sharedMem // mutex guarding shared memory with worker; persists across processes. + + cmd *exec.Cmd // current worker process + client *workerClient // used to communicate with worker process + waitErr error // last error returned by wait, set before termC is closed. + interrupted bool // true after stop interrupts a running worker. + termC chan struct{} // closed by wait when worker process terminates +} + +func newWorker(c *coordinator, dir, binPath string, args, env []string) (*worker, error) { + mem, err := sharedMemTempFile(workerSharedMemSize) + if err != nil { + return nil, err + } + memMu := make(chan *sharedMem, 1) + memMu <- mem + return &worker{ + dir: dir, + binPath: binPath, + args: args, + env: env[:len(env):len(env)], // copy on append to ensure workers don't overwrite each other. + coordinator: c, + memMu: memMu, + }, nil +} + +// cleanup releases persistent resources associated with the worker. +func (w *worker) cleanup() error { + mem := <-w.memMu + if mem == nil { + return nil + } + close(w.memMu) + return mem.Close() +} + +// coordinate runs the test binary to perform fuzzing. +// +// coordinate loops until ctx is cancelled or a fatal error is encountered. +// If a test process terminates unexpectedly while fuzzing, coordinate will +// attempt to restart and continue unless the termination can be attributed +// to an interruption (from a timer or the user). +// +// While looping, coordinate receives inputs from the coordinator, passes +// those inputs to the worker process, then passes the results back to +// the coordinator. +func (w *worker) coordinate(ctx context.Context) error { + // Main event loop. + for { + // Start or restart the worker if it's not running. + if !w.isRunning() { + if err := w.startAndPing(ctx); err != nil { + return err + } + } + + select { + case <-ctx.Done(): + // Worker was told to stop. + err := w.stop() + if err != nil && !w.interrupted && !isInterruptError(err) { + return err + } + return ctx.Err() + + case <-w.termC: + // Worker process terminated unexpectedly while waiting for input. + err := w.stop() + if w.interrupted { + panic("worker interrupted after unexpected termination") + } + if err == nil || isInterruptError(err) { + // Worker stopped, either by exiting with status 0 or after being + // interrupted with a signal that was not sent by the coordinator. + // + // When the user presses ^C, on POSIX platforms, SIGINT is delivered to + // all processes in the group concurrently, and the worker may see it + // before the coordinator. The worker should exit 0 gracefully (in + // theory). + // + // This condition is probably intended by the user, so suppress + // the error. + return nil + } + if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == workerExitCode { + // Worker exited with a code indicating F.Fuzz was not called correctly, + // for example, F.Fail was called first. + return fmt.Errorf("fuzzing process exited unexpectedly due to an internal failure: %w", err) + } + // Worker exited non-zero or was terminated by a non-interrupt + // signal (for example, SIGSEGV) while fuzzing. + return fmt.Errorf("fuzzing process hung or terminated unexpectedly: %w", err) + // TODO(jayconrod,katiehockman): if -keepfuzzing, restart worker. + + case input := <-w.coordinator.inputC: + // Received input from coordinator. + args := fuzzArgs{ + Limit: input.limit, + Timeout: input.timeout, + Warmup: input.warmup, + CoverageData: input.coverageData, + } + entry, resp, isInternalError, err := w.client.fuzz(ctx, input.entry, args) + canMinimize := true + if err != nil { + // Error communicating with worker. + w.stop() + if ctx.Err() != nil { + // Timeout or interruption. + return ctx.Err() + } + if w.interrupted { + // Communication error before we stopped the worker. + // Report an error, but don't record a crasher. + return fmt.Errorf("communicating with fuzzing process: %v", err) + } + if sig, ok := terminationSignal(w.waitErr); ok && !isCrashSignal(sig) { + // Worker terminated by a signal that probably wasn't caused by a + // specific input to the fuzz function. For example, on Linux, + // the kernel (OOM killer) may send SIGKILL to a process using a lot + // of memory. Or the shell might send SIGHUP when the terminal + // is closed. Don't record a crasher. + return fmt.Errorf("fuzzing process terminated by unexpected signal; no crash will be recorded: %v", w.waitErr) + } + if isInternalError { + // An internal error occurred which shouldn't be considered + // a crash. + return err + } + // Unexpected termination. Set error message and fall through. + // We'll restart the worker on the next iteration. + // Don't attempt to minimize this since it crashed the worker. + resp.Err = fmt.Sprintf("fuzzing process hung or terminated unexpectedly: %v", w.waitErr) + canMinimize = false + } + result := fuzzResult{ + limit: input.limit, + count: resp.Count, + totalDuration: resp.TotalDuration, + entryDuration: resp.InterestingDuration, + entry: entry, + crasherMsg: resp.Err, + coverageData: resp.CoverageData, + canMinimize: canMinimize, + } + w.coordinator.resultC <- result + + case input := <-w.coordinator.minimizeC: + // Received input to minimize from coordinator. + result, err := w.minimize(ctx, input) + if err != nil { + // Error minimizing. Send back the original input. If it didn't cause + // an error before, report it as causing an error now. + // TODO: double-check this is handled correctly when + // implementing -keepfuzzing. + result = fuzzResult{ + entry: input.entry, + crasherMsg: input.crasherMsg, + canMinimize: false, + limit: input.limit, + } + if result.crasherMsg == "" { + result.crasherMsg = err.Error() + } + } + w.coordinator.resultC <- result + } + } +} + +// minimize tells a worker process to attempt to find a smaller value that +// either causes an error (if we started minimizing because we found an input +// that causes an error) or preserves new coverage (if we started minimizing +// because we found an input that expands coverage). +func (w *worker) minimize(ctx context.Context, input fuzzMinimizeInput) (min fuzzResult, err error) { + if w.coordinator.opts.MinimizeTimeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, w.coordinator.opts.MinimizeTimeout) + defer cancel() + } + + args := minimizeArgs{ + Limit: input.limit, + Timeout: input.timeout, + KeepCoverage: input.keepCoverage, + } + entry, resp, err := w.client.minimize(ctx, input.entry, args) + if err != nil { + // Error communicating with worker. + w.stop() + if ctx.Err() != nil || w.interrupted || isInterruptError(w.waitErr) { + // Worker was interrupted, possibly by the user pressing ^C. + // Normally, workers can handle interrupts and timeouts gracefully and + // will return without error. An error here indicates the worker + // may not have been in a good state, but the error won't be meaningful + // to the user. Just return the original crasher without logging anything. + return fuzzResult{ + entry: input.entry, + crasherMsg: input.crasherMsg, + coverageData: input.keepCoverage, + canMinimize: false, + limit: input.limit, + }, nil + } + return fuzzResult{ + entry: entry, + crasherMsg: fmt.Sprintf("fuzzing process hung or terminated unexpectedly while minimizing: %v", err), + canMinimize: false, + limit: input.limit, + count: resp.Count, + totalDuration: resp.Duration, + }, nil + } + + if input.crasherMsg != "" && resp.Err == "" { + return fuzzResult{}, fmt.Errorf("attempted to minimize a crash but could not reproduce") + } + + return fuzzResult{ + entry: entry, + crasherMsg: resp.Err, + coverageData: resp.CoverageData, + canMinimize: false, + limit: input.limit, + count: resp.Count, + totalDuration: resp.Duration, + }, nil +} + +func (w *worker) isRunning() bool { + return w.cmd != nil +} + +// startAndPing starts the worker process and sends it a message to make sure it +// can communicate. +// +// startAndPing returns an error if any part of this didn't work, including if +// the context is expired or the worker process was interrupted before it +// responded. Errors that happen after start but before the ping response +// likely indicate that the worker did not call F.Fuzz or called F.Fail first. +// We don't record crashers for these errors. +func (w *worker) startAndPing(ctx context.Context) error { + if ctx.Err() != nil { + return ctx.Err() + } + if err := w.start(); err != nil { + return err + } + if err := w.client.ping(ctx); err != nil { + w.stop() + if ctx.Err() != nil { + return ctx.Err() + } + if isInterruptError(err) { + // User may have pressed ^C before worker responded. + return err + } + // TODO: record and return stderr. + return fmt.Errorf("fuzzing process terminated without fuzzing: %w", err) + } + return nil +} + +// start runs a new worker process. +// +// If the process couldn't be started, start returns an error. Start won't +// return later termination errors from the process if they occur. +// +// If the process starts successfully, start returns nil. stop must be called +// once later to clean up, even if the process terminates on its own. +// +// When the process terminates, w.waitErr is set to the error (if any), and +// w.termC is closed. +func (w *worker) start() (err error) { + if w.isRunning() { + panic("worker already started") + } + w.waitErr = nil + w.interrupted = false + w.termC = nil + + cmd := exec.Command(w.binPath, w.args...) + cmd.Dir = w.dir + cmd.Env = w.env[:len(w.env):len(w.env)] // copy on append to ensure workers don't overwrite each other. + + // Create the "fuzz_in" and "fuzz_out" pipes so we can communicate with + // the worker. We don't use stdin and stdout, since the test binary may + // do something else with those. + // + // Each pipe has a reader and a writer. The coordinator writes to fuzzInW + // and reads from fuzzOutR. The worker inherits fuzzInR and fuzzOutW. + // The coordinator closes fuzzInR and fuzzOutW after starting the worker, + // since we have no further need of them. + fuzzInR, fuzzInW, err := os.Pipe() + if err != nil { + return err + } + defer fuzzInR.Close() + fuzzOutR, fuzzOutW, err := os.Pipe() + if err != nil { + fuzzInW.Close() + return err + } + defer fuzzOutW.Close() + setWorkerComm(cmd, workerComm{fuzzIn: fuzzInR, fuzzOut: fuzzOutW, memMu: w.memMu}) + + // Start the worker process. + if err := cmd.Start(); err != nil { + fuzzInW.Close() + fuzzOutR.Close() + return err + } + + // Worker started successfully. + // After this, w.client owns fuzzInW and fuzzOutR, so w.client.Close must be + // called later by stop. + w.cmd = cmd + w.termC = make(chan struct{}) + comm := workerComm{fuzzIn: fuzzInW, fuzzOut: fuzzOutR, memMu: w.memMu} + m := newMutator() + w.client = newWorkerClient(comm, m) + + go func() { + w.waitErr = w.cmd.Wait() + close(w.termC) + }() + + return nil +} + +// stop tells the worker process to exit by closing w.client, then blocks until +// it terminates. If the worker doesn't terminate after a short time, stop +// signals it with os.Interrupt (where supported), then os.Kill. +// +// stop returns the error the process terminated with, if any (same as +// w.waitErr). +// +// stop must be called at least once after start returns successfully, even if +// the worker process terminates unexpectedly. +func (w *worker) stop() error { + if w.termC == nil { + panic("worker was not started successfully") + } + select { + case <-w.termC: + // Worker already terminated. + if w.client == nil { + // stop already called. + return w.waitErr + } + // Possible unexpected termination. + w.client.Close() + w.cmd = nil + w.client = nil + return w.waitErr + default: + // Worker still running. + } + + // Tell the worker to stop by closing fuzz_in. It won't actually stop until it + // finishes with earlier calls. + closeC := make(chan struct{}) + go func() { + w.client.Close() + close(closeC) + }() + + sig := os.Interrupt + if runtime.GOOS == "windows" { + // Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on + // Windows; using it with os.Process.Signal will return an error.” + // Fall back to Kill instead. + sig = os.Kill + } + + t := time.NewTimer(workerTimeoutDuration) + for { + select { + case <-w.termC: + // Worker terminated. + t.Stop() + <-closeC + w.cmd = nil + w.client = nil + return w.waitErr + + case <-t.C: + // Timer fired before worker terminated. + w.interrupted = true + switch sig { + case os.Interrupt: + // Try to stop the worker with SIGINT and wait a little longer. + w.cmd.Process.Signal(sig) + sig = os.Kill + t.Reset(workerTimeoutDuration) + + case os.Kill: + // Try to stop the worker with SIGKILL and keep waiting. + w.cmd.Process.Signal(sig) + sig = nil + t.Reset(workerTimeoutDuration) + + case nil: + // Still waiting. Print a message to let the user know why. + fmt.Fprintf(w.coordinator.opts.Log, "waiting for fuzzing process to terminate...\n") + } + } + } +} + +// RunFuzzWorker is called in a worker process to communicate with the +// coordinator process in order to fuzz random inputs. RunFuzzWorker loops +// until the coordinator tells it to stop. +// +// fn is a wrapper on the fuzz function. It may return an error to indicate +// a given input "crashed". The coordinator will also record a crasher if +// the function times out or terminates the process. +// +// RunFuzzWorker returns an error if it could not communicate with the +// coordinator process. +func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error { + comm, err := getWorkerComm() + if err != nil { + return err + } + srv := &workerServer{ + workerComm: comm, + fuzzFn: func(e CorpusEntry) (time.Duration, error) { + timer := time.AfterFunc(10*time.Second, func() { + panic("deadlocked!") // this error message won't be printed + }) + defer timer.Stop() + start := time.Now() + err := fn(e) + return time.Since(start), err + }, + m: newMutator(), + } + return srv.serve(ctx) +} + +// call is serialized and sent from the coordinator on fuzz_in. It acts as +// a minimalist RPC mechanism. Exactly one of its fields must be set to indicate +// which method to call. +type call struct { + Ping *pingArgs + Fuzz *fuzzArgs + Minimize *minimizeArgs +} + +// minimizeArgs contains arguments to workerServer.minimize. The value to +// minimize is already in shared memory. +type minimizeArgs struct { + // Timeout is the time to spend minimizing. This may include time to start up, + // especially if the input causes the worker process to terminated, requiring + // repeated restarts. + Timeout time.Duration + + // Limit is the maximum number of values to test, without spending more time + // than Duration. 0 indicates no limit. + Limit int64 + + // KeepCoverage is a set of coverage counters the worker should attempt to + // keep in minimized values. When provided, the worker will reject inputs that + // don't cause at least one of these bits to be set. + KeepCoverage []byte + + // Index is the index of the fuzz target parameter to be minimized. + Index int +} + +// minimizeResponse contains results from workerServer.minimize. +type minimizeResponse struct { + // WroteToMem is true if the worker found a smaller input and wrote it to + // shared memory. If minimizeArgs.KeepCoverage was set, the minimized input + // preserved at least one coverage bit and did not cause an error. + // Otherwise, the minimized input caused some error, recorded in Err. + WroteToMem bool + + // Err is the error string caused by the value in shared memory, if any. + Err string + + // CoverageData is the set of coverage bits activated by the minimized value + // in shared memory. When set, it contains at least one bit from KeepCoverage. + // CoverageData will be nil if Err is set or if minimization failed. + CoverageData []byte + + // Duration is the time spent minimizing, not including starting or cleaning up. + Duration time.Duration + + // Count is the number of values tested. + Count int64 +} + +// fuzzArgs contains arguments to workerServer.fuzz. The value to fuzz is +// passed in shared memory. +type fuzzArgs struct { + // Timeout is the time to spend fuzzing, not including starting or + // cleaning up. + Timeout time.Duration + + // Limit is the maximum number of values to test, without spending more time + // than Duration. 0 indicates no limit. + Limit int64 + + // Warmup indicates whether this is part of a warmup run, meaning that + // fuzzing should not occur. If coverageEnabled is true, then coverage data + // should be reported. + Warmup bool + + // CoverageData is the coverage data. If set, the worker should update its + // local coverage data prior to fuzzing. + CoverageData []byte +} + +// fuzzResponse contains results from workerServer.fuzz. +type fuzzResponse struct { + // Duration is the time spent fuzzing, not including starting or cleaning up. + TotalDuration time.Duration + InterestingDuration time.Duration + + // Count is the number of values tested. + Count int64 + + // CoverageData is set if the value in shared memory expands coverage + // and therefore may be interesting to the coordinator. + CoverageData []byte + + // Err is the error string caused by the value in shared memory, which is + // non-empty if the value in shared memory caused a crash. + Err string + + // InternalErr is the error string caused by an internal error in the + // worker. This shouldn't be considered a crasher. + InternalErr string +} + +// pingArgs contains arguments to workerServer.ping. +type pingArgs struct{} + +// pingResponse contains results from workerServer.ping. +type pingResponse struct{} + +// workerComm holds pipes and shared memory used for communication +// between the coordinator process (client) and a worker process (server). +// These values are unique to each worker; they are shared only with the +// coordinator, not with other workers. +// +// Access to shared memory is synchronized implicitly over the RPC protocol +// implemented in workerServer and workerClient. During a call, the client +// (worker) has exclusive access to shared memory; at other times, the server +// (coordinator) has exclusive access. +type workerComm struct { + fuzzIn, fuzzOut *os.File + memMu chan *sharedMem // mutex guarding shared memory +} + +// workerServer is a minimalist RPC server, run by fuzz worker processes. +// It allows the coordinator process (using workerClient) to call methods in a +// worker process. This system allows the coordinator to run multiple worker +// processes in parallel and to collect inputs that caused crashes from shared +// memory after a worker process terminates unexpectedly. +type workerServer struct { + workerComm + m *mutator + + // coverageMask is the local coverage data for the worker. It is + // periodically updated to reflect the data in the coordinator when new + // coverage is found. + coverageMask []byte + + // fuzzFn runs the worker's fuzz target on the given input and returns an + // error if it finds a crasher (the process may also exit or crash), and the + // time it took to run the input. It sets a deadline of 10 seconds, at which + // point it will panic with the assumption that the process is hanging or + // deadlocked. + fuzzFn func(CorpusEntry) (time.Duration, error) +} + +// serve reads serialized RPC messages on fuzzIn. When serve receives a message, +// it calls the corresponding method, then sends the serialized result back +// on fuzzOut. +// +// serve handles RPC calls synchronously; it will not attempt to read a message +// until the previous call has finished. +// +// serve returns errors that occurred when communicating over pipes. serve +// does not return errors from method calls; those are passed through serialized +// responses. +func (ws *workerServer) serve(ctx context.Context) error { + enc := json.NewEncoder(ws.fuzzOut) + dec := json.NewDecoder(&contextReader{ctx: ctx, r: ws.fuzzIn}) + for { + var c call + if err := dec.Decode(&c); err != nil { + if err == io.EOF || err == ctx.Err() { + return nil + } else { + return err + } + } + + var resp any + switch { + case c.Fuzz != nil: + resp = ws.fuzz(ctx, *c.Fuzz) + case c.Minimize != nil: + resp = ws.minimize(ctx, *c.Minimize) + case c.Ping != nil: + resp = ws.ping(ctx, *c.Ping) + default: + return errors.New("no arguments provided for any call") + } + + if err := enc.Encode(resp); err != nil { + return err + } + } +} + +// chainedMutations is how many mutations are applied before the worker +// resets the input to it's original state. +// NOTE: this number was picked without much thought. It is low enough that +// it seems to create a significant diversity in mutated inputs. We may want +// to consider looking into this more closely once we have a proper performance +// testing framework. Another option is to randomly pick the number of chained +// mutations on each invocation of the workerServer.fuzz method (this appears to +// be what libFuzzer does, although there seems to be no documentation which +// explains why this choice was made.) +const chainedMutations = 5 + +// fuzz runs the test function on random variations of the input value in shared +// memory for a limited duration or number of iterations. +// +// fuzz returns early if it finds an input that crashes the fuzz function (with +// fuzzResponse.Err set) or an input that expands coverage (with +// fuzzResponse.InterestingDuration set). +// +// fuzz does not modify the input in shared memory. Instead, it saves the +// initial PRNG state in shared memory and increments a counter in shared +// memory before each call to the test function. The caller may reconstruct +// the crashing input with this information, since the PRNG is deterministic. +func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) (resp fuzzResponse) { + if args.CoverageData != nil { + if ws.coverageMask != nil && len(args.CoverageData) != len(ws.coverageMask) { + resp.InternalErr = fmt.Sprintf("unexpected size for CoverageData: got %d, expected %d", len(args.CoverageData), len(ws.coverageMask)) + return resp + } + ws.coverageMask = args.CoverageData + } + start := time.Now() + defer func() { resp.TotalDuration = time.Since(start) }() + + if args.Timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, args.Timeout) + defer cancel() + } + mem := <-ws.memMu + ws.m.r.save(&mem.header().randState, &mem.header().randInc) + defer func() { + resp.Count = mem.header().count + ws.memMu <- mem + }() + if args.Limit > 0 && mem.header().count >= args.Limit { + resp.InternalErr = fmt.Sprintf("mem.header().count %d already exceeds args.Limit %d", mem.header().count, args.Limit) + return resp + } + + originalVals, err := unmarshalCorpusFile(mem.valueCopy()) + if err != nil { + resp.InternalErr = err.Error() + return resp + } + vals := make([]any, len(originalVals)) + copy(vals, originalVals) + + shouldStop := func() bool { + return args.Limit > 0 && mem.header().count >= args.Limit + } + fuzzOnce := func(entry CorpusEntry) (dur time.Duration, cov []byte, errMsg string) { + mem.header().count++ + var err error + dur, err = ws.fuzzFn(entry) + if err != nil { + errMsg = err.Error() + if errMsg == "" { + errMsg = "fuzz function failed with no input" + } + return dur, nil, errMsg + } + if ws.coverageMask != nil && countNewCoverageBits(ws.coverageMask, coverageSnapshot) > 0 { + return dur, coverageSnapshot, "" + } + return dur, nil, "" + } + + if args.Warmup { + dur, _, errMsg := fuzzOnce(CorpusEntry{Values: vals}) + if errMsg != "" { + resp.Err = errMsg + return resp + } + resp.InterestingDuration = dur + if coverageEnabled { + resp.CoverageData = coverageSnapshot + } + return resp + } + + for { + select { + case <-ctx.Done(): + return resp + default: + if mem.header().count%chainedMutations == 0 { + copy(vals, originalVals) + ws.m.r.save(&mem.header().randState, &mem.header().randInc) + } + ws.m.mutate(vals, cap(mem.valueRef())) + + entry := CorpusEntry{Values: vals} + dur, cov, errMsg := fuzzOnce(entry) + if errMsg != "" { + resp.Err = errMsg + return resp + } + if cov != nil { + resp.CoverageData = cov + resp.InterestingDuration = dur + return resp + } + if shouldStop() { + return resp + } + } + } +} + +func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp minimizeResponse) { + start := time.Now() + defer func() { resp.Duration = time.Now().Sub(start) }() + mem := <-ws.memMu + defer func() { ws.memMu <- mem }() + vals, err := unmarshalCorpusFile(mem.valueCopy()) + if err != nil { + panic(err) + } + inpHash := sha256.Sum256(mem.valueCopy()) + if args.Timeout != 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, args.Timeout) + defer cancel() + } + + // Minimize the values in vals, then write to shared memory. We only write + // to shared memory after completing minimization. + success, err := ws.minimizeInput(ctx, vals, mem, args) + if success { + writeToMem(vals, mem) + outHash := sha256.Sum256(mem.valueCopy()) + mem.header().rawInMem = false + resp.WroteToMem = true + if err != nil { + resp.Err = err.Error() + } else { + // If the values didn't change during minimization then coverageSnapshot is likely + // a dirty snapshot which represents the very last step of minimization, not the + // coverage for the initial input. In that case just return the coverage we were + // given initially, since it more accurately represents the coverage map for the + // input we are returning. + if outHash != inpHash { + resp.CoverageData = coverageSnapshot + } else { + resp.CoverageData = args.KeepCoverage + } + } + } + return resp +} + +// minimizeInput applies a series of minimizing transformations on the provided +// vals, ensuring that each minimization still causes an error, or keeps +// coverage, in fuzzFn. It uses the context to determine how long to run, +// stopping once closed. It returns a bool indicating whether minimization was +// successful and an error if one was found. +func (ws *workerServer) minimizeInput(ctx context.Context, vals []any, mem *sharedMem, args minimizeArgs) (success bool, retErr error) { + keepCoverage := args.KeepCoverage + memBytes := mem.valueRef() + bPtr := &memBytes + count := &mem.header().count + shouldStop := func() bool { + return ctx.Err() != nil || + (args.Limit > 0 && *count >= args.Limit) + } + if shouldStop() { + return false, nil + } + + // Check that the original value preserves coverage or causes an error. + // If not, then whatever caused us to think the value was interesting may + // have been a flake, and we can't minimize it. + *count++ + _, retErr = ws.fuzzFn(CorpusEntry{Values: vals}) + if keepCoverage != nil { + if !hasCoverageBit(keepCoverage, coverageSnapshot) || retErr != nil { + return false, nil + } + } else if retErr == nil { + return false, nil + } + mem.header().rawInMem = true + + // tryMinimized runs the fuzz function with candidate replacing the value + // at index valI. tryMinimized returns whether the input with candidate is + // interesting for the same reason as the original input: it returns + // an error if one was expected, or it preserves coverage. + tryMinimized := func(candidate []byte) bool { + prev := vals[args.Index] + switch prev.(type) { + case []byte: + vals[args.Index] = candidate + case string: + vals[args.Index] = string(candidate) + default: + panic("impossible") + } + copy(*bPtr, candidate) + *bPtr = (*bPtr)[:len(candidate)] + mem.setValueLen(len(candidate)) + *count++ + _, err := ws.fuzzFn(CorpusEntry{Values: vals}) + if err != nil { + retErr = err + if keepCoverage != nil { + // Now that we've found a crash, that's more important than any + // minimization of interesting inputs that was being done. Clear out + // keepCoverage to only minimize the crash going forward. + keepCoverage = nil + } + return true + } + // Minimization should preserve coverage bits. + if keepCoverage != nil && isCoverageSubset(keepCoverage, coverageSnapshot) { + return true + } + vals[args.Index] = prev + return false + } + switch v := vals[args.Index].(type) { + case string: + minimizeBytes([]byte(v), tryMinimized, shouldStop) + case []byte: + minimizeBytes(v, tryMinimized, shouldStop) + default: + panic("impossible") + } + return true, retErr +} + +func writeToMem(vals []any, mem *sharedMem) { + b := marshalCorpusFile(vals...) + mem.setValue(b) +} + +// ping does nothing. The coordinator calls this method to ensure the worker +// has called F.Fuzz and can communicate. +func (ws *workerServer) ping(ctx context.Context, args pingArgs) pingResponse { + return pingResponse{} +} + +// workerClient is a minimalist RPC client. The coordinator process uses a +// workerClient to call methods in each worker process (handled by +// workerServer). +type workerClient struct { + workerComm + m *mutator + + // mu is the mutex protecting the workerComm.fuzzIn pipe. This must be + // locked before making calls to the workerServer. It prevents + // workerClient.Close from closing fuzzIn while workerClient methods are + // writing to it concurrently, and prevents multiple callers from writing to + // fuzzIn concurrently. + mu sync.Mutex +} + +func newWorkerClient(comm workerComm, m *mutator) *workerClient { + return &workerClient{workerComm: comm, m: m} +} + +// Close shuts down the connection to the RPC server (the worker process) by +// closing fuzz_in. Close drains fuzz_out (avoiding a SIGPIPE in the worker), +// and closes it after the worker process closes the other end. +func (wc *workerClient) Close() error { + wc.mu.Lock() + defer wc.mu.Unlock() + + // Close fuzzIn. This signals to the server that there are no more calls, + // and it should exit. + if err := wc.fuzzIn.Close(); err != nil { + wc.fuzzOut.Close() + return err + } + + // Drain fuzzOut and close it. When the server exits, the kernel will close + // its end of fuzzOut, and we'll get EOF. + if _, err := io.Copy(ioutil.Discard, wc.fuzzOut); err != nil { + wc.fuzzOut.Close() + return err + } + return wc.fuzzOut.Close() +} + +// errSharedMemClosed is returned by workerClient methods that cannot access +// shared memory because it was closed and unmapped by another goroutine. That +// can happen when worker.cleanup is called in the worker goroutine while a +// workerClient.fuzz call runs concurrently. +// +// This error should not be reported. It indicates the operation was +// interrupted. +var errSharedMemClosed = errors.New("internal error: shared memory was closed and unmapped") + +// minimize tells the worker to call the minimize method. See +// workerServer.minimize. +func (wc *workerClient) minimize(ctx context.Context, entryIn CorpusEntry, args minimizeArgs) (entryOut CorpusEntry, resp minimizeResponse, retErr error) { + wc.mu.Lock() + defer wc.mu.Unlock() + + mem, ok := <-wc.memMu + if !ok { + return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed + } + mem.header().count = 0 + inp, err := corpusEntryData(entryIn) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, err + } + mem.setValue(inp) + defer func() { wc.memMu <- mem }() + entryOut = entryIn + entryOut.Values, err = unmarshalCorpusFile(inp) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling provided value: %v", err) + } + for i, v := range entryOut.Values { + if !isMinimizable(reflect.TypeOf(v)) { + continue + } + + wc.memMu <- mem + args.Index = i + c := call{Minimize: &args} + callErr := wc.callLocked(ctx, c, &resp) + mem, ok = <-wc.memMu + if !ok { + return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed + } + + if callErr != nil { + retErr = callErr + if !mem.header().rawInMem { + // An unrecoverable error occurred before minimization began. + return entryIn, minimizeResponse{}, retErr + } + // An unrecoverable error occurred during minimization. mem now + // holds the raw, unmarshalled bytes of entryIn.Values[i] that + // caused the error. + switch entryOut.Values[i].(type) { + case string: + entryOut.Values[i] = string(mem.valueCopy()) + case []byte: + entryOut.Values[i] = mem.valueCopy() + default: + panic("impossible") + } + entryOut.Data = marshalCorpusFile(entryOut.Values...) + // Stop minimizing; another unrecoverable error is likely to occur. + break + } + + if resp.WroteToMem { + // Minimization succeeded, and mem holds the marshaled data. + entryOut.Data = mem.valueCopy() + entryOut.Values, err = unmarshalCorpusFile(entryOut.Data) + if err != nil { + return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling minimized value: %v", err) + } + } + + // Prepare for next iteration of the loop. + if args.Timeout != 0 { + args.Timeout -= resp.Duration + if args.Timeout <= 0 { + break + } + } + if args.Limit != 0 { + args.Limit -= mem.header().count + if args.Limit <= 0 { + break + } + } + } + resp.Count = mem.header().count + h := sha256.Sum256(entryOut.Data) + entryOut.Path = fmt.Sprintf("%x", h[:4]) + return entryOut, resp, retErr +} + +// fuzz tells the worker to call the fuzz method. See workerServer.fuzz. +func (wc *workerClient) fuzz(ctx context.Context, entryIn CorpusEntry, args fuzzArgs) (entryOut CorpusEntry, resp fuzzResponse, isInternalError bool, err error) { + wc.mu.Lock() + defer wc.mu.Unlock() + + mem, ok := <-wc.memMu + if !ok { + return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed + } + mem.header().count = 0 + inp, err := corpusEntryData(entryIn) + if err != nil { + return CorpusEntry{}, fuzzResponse{}, true, err + } + mem.setValue(inp) + wc.memMu <- mem + + c := call{Fuzz: &args} + callErr := wc.callLocked(ctx, c, &resp) + if resp.InternalErr != "" { + return CorpusEntry{}, fuzzResponse{}, true, errors.New(resp.InternalErr) + } + mem, ok = <-wc.memMu + if !ok { + return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed + } + defer func() { wc.memMu <- mem }() + resp.Count = mem.header().count + + if !bytes.Equal(inp, mem.valueRef()) { + return CorpusEntry{}, fuzzResponse{}, true, errors.New("workerServer.fuzz modified input") + } + needEntryOut := callErr != nil || resp.Err != "" || + (!args.Warmup && resp.CoverageData != nil) + if needEntryOut { + valuesOut, err := unmarshalCorpusFile(inp) + if err != nil { + return CorpusEntry{}, fuzzResponse{}, true, fmt.Errorf("unmarshaling fuzz input value after call: %v", err) + } + wc.m.r.restore(mem.header().randState, mem.header().randInc) + if !args.Warmup { + // Only mutate the valuesOut if fuzzing actually occurred. + numMutations := ((resp.Count - 1) % chainedMutations) + 1 + for i := int64(0); i < numMutations; i++ { + wc.m.mutate(valuesOut, cap(mem.valueRef())) + } + } + dataOut := marshalCorpusFile(valuesOut...) + + h := sha256.Sum256(dataOut) + name := fmt.Sprintf("%x", h[:4]) + entryOut = CorpusEntry{ + Parent: entryIn.Path, + Path: name, + Data: dataOut, + Generation: entryIn.Generation + 1, + } + if args.Warmup { + // The bytes weren't mutated, so if entryIn was a seed corpus value, + // then entryOut is too. + entryOut.IsSeed = entryIn.IsSeed + } + } + + return entryOut, resp, false, callErr +} + +// ping tells the worker to call the ping method. See workerServer.ping. +func (wc *workerClient) ping(ctx context.Context) error { + wc.mu.Lock() + defer wc.mu.Unlock() + c := call{Ping: &pingArgs{}} + var resp pingResponse + return wc.callLocked(ctx, c, &resp) +} + +// callLocked sends an RPC from the coordinator to the worker process and waits +// for the response. The callLocked may be cancelled with ctx. +func (wc *workerClient) callLocked(ctx context.Context, c call, resp any) (err error) { + enc := json.NewEncoder(wc.fuzzIn) + dec := json.NewDecoder(&contextReader{ctx: ctx, r: wc.fuzzOut}) + if err := enc.Encode(c); err != nil { + return err + } + return dec.Decode(resp) +} + +// contextReader wraps a Reader with a Context. If the context is cancelled +// while the underlying reader is blocked, Read returns immediately. +// +// This is useful for reading from a pipe. Closing a pipe file descriptor does +// not unblock pending Reads on that file descriptor. All copies of the pipe's +// other file descriptor (the write end) must be closed in all processes that +// inherit it. This is difficult to do correctly in the situation we care about +// (process group termination). +type contextReader struct { + ctx context.Context + r io.Reader +} + +func (cr *contextReader) Read(b []byte) (int, error) { + if ctxErr := cr.ctx.Err(); ctxErr != nil { + return 0, ctxErr + } + done := make(chan struct{}) + + // This goroutine may stay blocked after Read returns because the underlying + // read is blocked. + var n int + var err error + go func() { + n, err = cr.r.Read(b) + close(done) + }() + + select { + case <-cr.ctx.Done(): + return 0, cr.ctx.Err() + case <-done: + return n, err + } +} diff --git a/src/internal/fuzz/worker_test.go b/src/internal/fuzz/worker_test.go new file mode 100644 index 00000000000000..d0b21da7838643 --- /dev/null +++ b/src/internal/fuzz/worker_test.go @@ -0,0 +1,206 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fuzz + +import ( + "context" + "errors" + "flag" + "fmt" + "internal/race" + "io" + "os" + "os/signal" + "reflect" + "strconv" + "testing" + "time" +) + +var benchmarkWorkerFlag = flag.Bool("benchmarkworker", false, "") + +func TestMain(m *testing.M) { + flag.Parse() + if *benchmarkWorkerFlag { + runBenchmarkWorker() + return + } + os.Exit(m.Run()) +} + +func BenchmarkWorkerFuzzOverhead(b *testing.B) { + if race.Enabled { + b.Skip("TODO(48504): fix and re-enable") + } + origEnv := os.Getenv("GODEBUG") + defer func() { os.Setenv("GODEBUG", origEnv) }() + os.Setenv("GODEBUG", fmt.Sprintf("%s,fuzzseed=123", origEnv)) + + ws := &workerServer{ + fuzzFn: func(_ CorpusEntry) (time.Duration, error) { return time.Second, nil }, + workerComm: workerComm{memMu: make(chan *sharedMem, 1)}, + } + + mem, err := sharedMemTempFile(workerSharedMemSize) + if err != nil { + b.Fatalf("failed to create temporary shared memory file: %s", err) + } + defer func() { + if err := mem.Close(); err != nil { + b.Error(err) + } + }() + + initialVal := []any{make([]byte, 32)} + encodedVals := marshalCorpusFile(initialVal...) + mem.setValue(encodedVals) + + ws.memMu <- mem + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ws.m = newMutator() + mem.setValue(encodedVals) + mem.header().count = 0 + + ws.fuzz(context.Background(), fuzzArgs{Limit: 1}) + } +} + +// BenchmarkWorkerPing acts as the coordinator and measures the time it takes +// a worker to respond to N pings. This is a rough measure of our RPC latency. +func BenchmarkWorkerPing(b *testing.B) { + if race.Enabled { + b.Skip("TODO(48504): fix and re-enable") + } + b.SetParallelism(1) + w := newWorkerForTest(b) + for i := 0; i < b.N; i++ { + if err := w.client.ping(context.Background()); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkWorkerFuzz acts as the coordinator and measures the time it takes +// a worker to mutate a given input and call a trivial fuzz function N times. +func BenchmarkWorkerFuzz(b *testing.B) { + if race.Enabled { + b.Skip("TODO(48504): fix and re-enable") + } + b.SetParallelism(1) + w := newWorkerForTest(b) + entry := CorpusEntry{Values: []any{[]byte(nil)}} + entry.Data = marshalCorpusFile(entry.Values...) + for i := int64(0); i < int64(b.N); { + args := fuzzArgs{ + Limit: int64(b.N) - i, + Timeout: workerFuzzDuration, + } + _, resp, _, err := w.client.fuzz(context.Background(), entry, args) + if err != nil { + b.Fatal(err) + } + if resp.Err != "" { + b.Fatal(resp.Err) + } + if resp.Count == 0 { + b.Fatal("worker did not make progress") + } + i += resp.Count + } +} + +// newWorkerForTest creates and starts a worker process for testing or +// benchmarking. The worker process calls RunFuzzWorker, which responds to +// RPC messages until it's stopped. The process is stopped and cleaned up +// automatically when the test is done. +func newWorkerForTest(tb testing.TB) *worker { + tb.Helper() + c, err := newCoordinator(CoordinateFuzzingOpts{ + Types: []reflect.Type{reflect.TypeOf([]byte(nil))}, + Log: io.Discard, + }) + if err != nil { + tb.Fatal(err) + } + dir := "" // same as self + binPath := os.Args[0] // same as self + args := append(os.Args[1:], "-benchmarkworker") + env := os.Environ() // same as self + w, err := newWorker(c, dir, binPath, args, env) + if err != nil { + tb.Fatal(err) + } + tb.Cleanup(func() { + if err := w.cleanup(); err != nil { + tb.Error(err) + } + }) + if err := w.startAndPing(context.Background()); err != nil { + tb.Fatal(err) + } + tb.Cleanup(func() { + if err := w.stop(); err != nil { + tb.Error(err) + } + }) + return w +} + +func runBenchmarkWorker() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + fn := func(CorpusEntry) error { return nil } + if err := RunFuzzWorker(ctx, fn); err != nil && err != ctx.Err() { + panic(err) + } +} + +func BenchmarkWorkerMinimize(b *testing.B) { + if race.Enabled { + b.Skip("TODO(48504): fix and re-enable") + } + + ws := &workerServer{ + workerComm: workerComm{memMu: make(chan *sharedMem, 1)}, + } + + mem, err := sharedMemTempFile(workerSharedMemSize) + if err != nil { + b.Fatalf("failed to create temporary shared memory file: %s", err) + } + defer func() { + if err := mem.Close(); err != nil { + b.Error(err) + } + }() + ws.memMu <- mem + + bytes := make([]byte, 1024) + ctx := context.Background() + for sz := 1; sz <= len(bytes); sz <<= 1 { + sz := sz + input := []any{bytes[:sz]} + encodedVals := marshalCorpusFile(input...) + mem = <-ws.memMu + mem.setValue(encodedVals) + ws.memMu <- mem + b.Run(strconv.Itoa(sz), func(b *testing.B) { + i := 0 + ws.fuzzFn = func(_ CorpusEntry) (time.Duration, error) { + if i == 0 { + i++ + return time.Second, errors.New("initial failure for deflake") + } + return time.Second, nil + } + for i := 0; i < b.N; i++ { + b.SetBytes(int64(sz)) + ws.minimize(ctx, minimizeArgs{}) + } + }) + } +} diff --git a/src/internal/goarch/gengoarch.go b/src/internal/goarch/gengoarch.go new file mode 100644 index 00000000000000..0b0be5cd157da4 --- /dev/null +++ b/src/internal/goarch/gengoarch.go @@ -0,0 +1,60 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "strings" +) + +var goarches []string + +func main() { + data, err := os.ReadFile("../../go/build/syslist.go") + if err != nil { + log.Fatal(err) + } + const goarchPrefix = `var knownArch = map[string]bool{` + inGOARCH := false + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, goarchPrefix) { + inGOARCH = true + } else if inGOARCH && strings.HasPrefix(line, "}") { + break + } else if inGOARCH { + goarch := strings.Fields(line)[0] + goarch = strings.TrimPrefix(goarch, `"`) + goarch = strings.TrimSuffix(goarch, `":`) + goarches = append(goarches, goarch) + } + } + + for _, target := range goarches { + if target == "amd64p32" { + continue + } + var buf bytes.Buffer + fmt.Fprintf(&buf, "// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT.\n\n") + fmt.Fprintf(&buf, "//go:build %s\n\n", target) // must explicitly include target for bootstrapping purposes + fmt.Fprintf(&buf, "package goarch\n\n") + fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) + for _, goarch := range goarches { + value := 0 + if goarch == target { + value = 1 + } + fmt.Fprintf(&buf, "const Is%s = %d\n", strings.Title(goarch), value) + } + err := os.WriteFile("zgoarch_"+target+".go", buf.Bytes(), 0666) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/src/internal/goarch/goarch.go b/src/internal/goarch/goarch.go new file mode 100644 index 00000000000000..3dda62fadc21f3 --- /dev/null +++ b/src/internal/goarch/goarch.go @@ -0,0 +1,60 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// package goarch contains GOARCH-specific constants. +package goarch + +// The next line makes 'go generate' write the zgoarch*.go files with +// per-arch information, including constants named $GOARCH for every +// GOARCH. The constant is 1 on the current system, 0 otherwise; multiplying +// by them is useful for defining GOARCH-specific constants. +// +//go:generate go run gengoarch.go + +type ArchFamilyType int + +const ( + AMD64 ArchFamilyType = iota + ARM + ARM64 + I386 + LOONG64 + MIPS + MIPS64 + PPC64 + RISCV64 + S390X + WASM +) + +// PtrSize is the size of a pointer in bytes - unsafe.Sizeof(uintptr(0)) but as an ideal constant. +// It is also the size of the machine's native word size (that is, 4 on 32-bit systems, 8 on 64-bit). +const PtrSize = 4 << (^uintptr(0) >> 63) + +// ArchFamily is the architecture family (AMD64, ARM, ...) +const ArchFamily ArchFamilyType = _ArchFamily + +// BigEndian reports whether the architecture is big-endian. +const BigEndian = IsArmbe|IsArm64be|IsMips|IsMips64|IsPpc|IsPpc64|IsS390|IsS390x|IsSparc|IsSparc64 == 1 + +// DefaultPhysPageSize is the default physical page size. +const DefaultPhysPageSize = _DefaultPhysPageSize + +// PCQuantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). +// The various PC tables record PC deltas pre-divided by PCQuantum. +const PCQuantum = _PCQuantum + +// Int64Align is the required alignment for a 64-bit integer (4 on 32-bit systems, 8 on 64-bit). +const Int64Align = PtrSize + +// MinFrameSize is the size of the system-reserved words at the bottom +// of a frame (just above the architectural stack pointer). +// It is zero on x86 and PtrSize on most non-x86 (LR-based) systems. +// On PowerPC it is larger, to cover three more reserved words: +// the compiler word, the link editor word, and the TOC save word. +const MinFrameSize = _MinFrameSize + +// StackAlign is the required alignment of the SP register. +// The stack must be at least word aligned, but some architectures require more. +const StackAlign = _StackAlign diff --git a/src/internal/goarch/goarch_386.go b/src/internal/goarch/goarch_386.go new file mode 100644 index 00000000000000..c6214217fcf339 --- /dev/null +++ b/src/internal/goarch/goarch_386.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = I386 + _DefaultPhysPageSize = 4096 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_amd64.go b/src/internal/goarch/goarch_amd64.go new file mode 100644 index 00000000000000..911e3e72421720 --- /dev/null +++ b/src/internal/goarch/goarch_amd64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = AMD64 + _DefaultPhysPageSize = 4096 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_arm.go b/src/internal/goarch/goarch_arm.go new file mode 100644 index 00000000000000..a6591713c82040 --- /dev/null +++ b/src/internal/goarch/goarch_arm.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = ARM + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_arm64.go b/src/internal/goarch/goarch_arm64.go new file mode 100644 index 00000000000000..85d0b476391321 --- /dev/null +++ b/src/internal/goarch/goarch_arm64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = ARM64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = 16 +) diff --git a/src/internal/goarch/goarch_loong64.go b/src/internal/goarch/goarch_loong64.go new file mode 100644 index 00000000000000..dae1f4da315b2d --- /dev/null +++ b/src/internal/goarch/goarch_loong64.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package goarch + +const ( + _ArchFamily = LOONG64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_mips.go b/src/internal/goarch/goarch_mips.go new file mode 100644 index 00000000000000..59f3995e2a54fd --- /dev/null +++ b/src/internal/goarch/goarch_mips.go @@ -0,0 +1,13 @@ +// 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 goarch + +const ( + _ArchFamily = MIPS + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_mips64.go b/src/internal/goarch/goarch_mips64.go new file mode 100644 index 00000000000000..9e4f82797d410a --- /dev/null +++ b/src/internal/goarch/goarch_mips64.go @@ -0,0 +1,13 @@ +// 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 goarch + +const ( + _ArchFamily = MIPS64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_mips64le.go b/src/internal/goarch/goarch_mips64le.go new file mode 100644 index 00000000000000..9e4f82797d410a --- /dev/null +++ b/src/internal/goarch/goarch_mips64le.go @@ -0,0 +1,13 @@ +// 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 goarch + +const ( + _ArchFamily = MIPS64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_mipsle.go b/src/internal/goarch/goarch_mipsle.go new file mode 100644 index 00000000000000..3e6642bb86399b --- /dev/null +++ b/src/internal/goarch/goarch_mipsle.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = MIPS + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_ppc64.go b/src/internal/goarch/goarch_ppc64.go new file mode 100644 index 00000000000000..60cc846e6a380a --- /dev/null +++ b/src/internal/goarch/goarch_ppc64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = PPC64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 32 + _StackAlign = 16 +) diff --git a/src/internal/goarch/goarch_ppc64le.go b/src/internal/goarch/goarch_ppc64le.go new file mode 100644 index 00000000000000..60cc846e6a380a --- /dev/null +++ b/src/internal/goarch/goarch_ppc64le.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = PPC64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 32 + _StackAlign = 16 +) diff --git a/src/internal/goarch/goarch_riscv64.go b/src/internal/goarch/goarch_riscv64.go new file mode 100644 index 00000000000000..3b6da1e02fe9b8 --- /dev/null +++ b/src/internal/goarch/goarch_riscv64.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = RISCV64 + _DefaultPhysPageSize = 4096 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_s390x.go b/src/internal/goarch/goarch_s390x.go new file mode 100644 index 00000000000000..20c5705581e79c --- /dev/null +++ b/src/internal/goarch/goarch_s390x.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = S390X + _DefaultPhysPageSize = 4096 + _PCQuantum = 2 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/goarch_wasm.go b/src/internal/goarch/goarch_wasm.go new file mode 100644 index 00000000000000..98618d6980edd7 --- /dev/null +++ b/src/internal/goarch/goarch_wasm.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = WASM + _DefaultPhysPageSize = 65536 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/src/internal/goarch/zgoarch_386.go b/src/internal/goarch/zgoarch_386.go new file mode 100644 index 00000000000000..4a9b0e67c454a7 --- /dev/null +++ b/src/internal/goarch/zgoarch_386.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build 386 + +package goarch + +const GOARCH = `386` + +const Is386 = 1 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_amd64.go b/src/internal/goarch/zgoarch_amd64.go new file mode 100644 index 00000000000000..7926392b77510c --- /dev/null +++ b/src/internal/goarch/zgoarch_amd64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build amd64 + +package goarch + +const GOARCH = `amd64` + +const Is386 = 0 +const IsAmd64 = 1 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_arm.go b/src/internal/goarch/zgoarch_arm.go new file mode 100644 index 00000000000000..6c03b8b060c1b4 --- /dev/null +++ b/src/internal/goarch/zgoarch_arm.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm + +package goarch + +const GOARCH = `arm` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 1 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_arm64.go b/src/internal/goarch/zgoarch_arm64.go new file mode 100644 index 00000000000000..ad342d79c96bb2 --- /dev/null +++ b/src/internal/goarch/zgoarch_arm64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm64 + +package goarch + +const GOARCH = `arm64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 1 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_arm64be.go b/src/internal/goarch/zgoarch_arm64be.go new file mode 100644 index 00000000000000..0f260030908eca --- /dev/null +++ b/src/internal/goarch/zgoarch_arm64be.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm64be + +package goarch + +const GOARCH = `arm64be` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 1 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_armbe.go b/src/internal/goarch/zgoarch_armbe.go new file mode 100644 index 00000000000000..6092fee7516e38 --- /dev/null +++ b/src/internal/goarch/zgoarch_armbe.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build armbe + +package goarch + +const GOARCH = `armbe` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 1 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_loong64.go b/src/internal/goarch/zgoarch_loong64.go new file mode 100644 index 00000000000000..21c67e1176838e --- /dev/null +++ b/src/internal/goarch/zgoarch_loong64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build loong64 + +package goarch + +const GOARCH = `loong64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 1 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mips.go b/src/internal/goarch/zgoarch_mips.go new file mode 100644 index 00000000000000..0db19746556adf --- /dev/null +++ b/src/internal/goarch/zgoarch_mips.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips + +package goarch + +const GOARCH = `mips` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 1 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mips64.go b/src/internal/goarch/zgoarch_mips64.go new file mode 100644 index 00000000000000..738806f0aef0af --- /dev/null +++ b/src/internal/goarch/zgoarch_mips64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64 + +package goarch + +const GOARCH = `mips64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 1 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mips64le.go b/src/internal/goarch/zgoarch_mips64le.go new file mode 100644 index 00000000000000..8de5beb8819729 --- /dev/null +++ b/src/internal/goarch/zgoarch_mips64le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64le + +package goarch + +const GOARCH = `mips64le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 1 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mips64p32.go b/src/internal/goarch/zgoarch_mips64p32.go new file mode 100644 index 00000000000000..ea461bed70c26c --- /dev/null +++ b/src/internal/goarch/zgoarch_mips64p32.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64p32 + +package goarch + +const GOARCH = `mips64p32` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 1 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mips64p32le.go b/src/internal/goarch/zgoarch_mips64p32le.go new file mode 100644 index 00000000000000..15473ce6c7a047 --- /dev/null +++ b/src/internal/goarch/zgoarch_mips64p32le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64p32le + +package goarch + +const GOARCH = `mips64p32le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 1 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_mipsle.go b/src/internal/goarch/zgoarch_mipsle.go new file mode 100644 index 00000000000000..4955142e876539 --- /dev/null +++ b/src/internal/goarch/zgoarch_mipsle.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mipsle + +package goarch + +const GOARCH = `mipsle` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 1 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_ppc.go b/src/internal/goarch/zgoarch_ppc.go new file mode 100644 index 00000000000000..ec01763b3c3ca5 --- /dev/null +++ b/src/internal/goarch/zgoarch_ppc.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc + +package goarch + +const GOARCH = `ppc` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 1 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_ppc64.go b/src/internal/goarch/zgoarch_ppc64.go new file mode 100644 index 00000000000000..39be3925c8e919 --- /dev/null +++ b/src/internal/goarch/zgoarch_ppc64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc64 + +package goarch + +const GOARCH = `ppc64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 1 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_ppc64le.go b/src/internal/goarch/zgoarch_ppc64le.go new file mode 100644 index 00000000000000..5f959e0e0245ef --- /dev/null +++ b/src/internal/goarch/zgoarch_ppc64le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc64le + +package goarch + +const GOARCH = `ppc64le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 1 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_riscv.go b/src/internal/goarch/zgoarch_riscv.go new file mode 100644 index 00000000000000..8d81a14dd9beac --- /dev/null +++ b/src/internal/goarch/zgoarch_riscv.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build riscv + +package goarch + +const GOARCH = `riscv` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 1 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_riscv64.go b/src/internal/goarch/zgoarch_riscv64.go new file mode 100644 index 00000000000000..1df989c2a696ed --- /dev/null +++ b/src/internal/goarch/zgoarch_riscv64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build riscv64 + +package goarch + +const GOARCH = `riscv64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 1 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_s390.go b/src/internal/goarch/zgoarch_s390.go new file mode 100644 index 00000000000000..56815b9f435567 --- /dev/null +++ b/src/internal/goarch/zgoarch_s390.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build s390 + +package goarch + +const GOARCH = `s390` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 1 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_s390x.go b/src/internal/goarch/zgoarch_s390x.go new file mode 100644 index 00000000000000..e61e9bd5938a8b --- /dev/null +++ b/src/internal/goarch/zgoarch_s390x.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build s390x + +package goarch + +const GOARCH = `s390x` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 1 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_sparc.go b/src/internal/goarch/zgoarch_sparc.go new file mode 100644 index 00000000000000..ee5b746566ddd2 --- /dev/null +++ b/src/internal/goarch/zgoarch_sparc.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build sparc + +package goarch + +const GOARCH = `sparc` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 1 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_sparc64.go b/src/internal/goarch/zgoarch_sparc64.go new file mode 100644 index 00000000000000..519aaa10c13bb5 --- /dev/null +++ b/src/internal/goarch/zgoarch_sparc64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build sparc64 + +package goarch + +const GOARCH = `sparc64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 1 +const IsWasm = 0 diff --git a/src/internal/goarch/zgoarch_wasm.go b/src/internal/goarch/zgoarch_wasm.go new file mode 100644 index 00000000000000..25567a1b6481f6 --- /dev/null +++ b/src/internal/goarch/zgoarch_wasm.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build wasm + +package goarch + +const GOARCH = `wasm` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 1 diff --git a/src/internal/godebug/godebug.go b/src/internal/godebug/godebug.go new file mode 100644 index 00000000000000..ac434e5fd83649 --- /dev/null +++ b/src/internal/godebug/godebug.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package godebug parses the GODEBUG environment variable. +package godebug + +import "os" + +// Get returns the value for the provided GODEBUG key. +func Get(key string) string { + return get(os.Getenv("GODEBUG"), key) +} + +// get returns the value part of key=value in s (a GODEBUG value). +func get(s, key string) string { + for i := 0; i < len(s)-len(key)-1; i++ { + if i > 0 && s[i-1] != ',' { + continue + } + afterKey := s[i+len(key):] + if afterKey[0] != '=' || s[i:i+len(key)] != key { + continue + } + val := afterKey[1:] + for i, b := range val { + if b == ',' { + return val[:i] + } + } + return val + } + return "" +} diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go new file mode 100644 index 00000000000000..41b9117b73bcaf --- /dev/null +++ b/src/internal/godebug/godebug_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package godebug + +import "testing" + +func TestGet(t *testing.T) { + tests := []struct { + godebug string + key string + want string + }{ + {"", "", ""}, + {"", "foo", ""}, + {"foo=bar", "foo", "bar"}, + {"foo=bar,after=x", "foo", "bar"}, + {"before=x,foo=bar,after=x", "foo", "bar"}, + {"before=x,foo=bar", "foo", "bar"}, + {",,,foo=bar,,,", "foo", "bar"}, + {"foodecoy=wrong,foo=bar", "foo", "bar"}, + {"foo=", "foo", ""}, + {"foo", "foo", ""}, + {",foo", "foo", ""}, + {"foo=bar,baz", "loooooooong", ""}, + } + for _, tt := range tests { + got := get(tt.godebug, tt.key) + if got != tt.want { + t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.key, got, tt.want) + } + } +} diff --git a/src/internal/goexperiment/exp_boringcrypto_off.go b/src/internal/goexperiment/exp_boringcrypto_off.go new file mode 100644 index 00000000000000..020c75bd532c51 --- /dev/null +++ b/src/internal/goexperiment/exp_boringcrypto_off.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.boringcrypto +// +build !goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = false +const BoringCryptoInt = 0 diff --git a/src/internal/goexperiment/exp_boringcrypto_on.go b/src/internal/goexperiment/exp_boringcrypto_on.go new file mode 100644 index 00000000000000..1454329a463f5f --- /dev/null +++ b/src/internal/goexperiment/exp_boringcrypto_on.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.boringcrypto +// +build goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = true +const BoringCryptoInt = 1 diff --git a/src/internal/goexperiment/exp_heapminimum512kib_off.go b/src/internal/goexperiment/exp_heapminimum512kib_off.go new file mode 100644 index 00000000000000..09da431b407113 --- /dev/null +++ b/src/internal/goexperiment/exp_heapminimum512kib_off.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.heapminimum512kib +// +build !goexperiment.heapminimum512kib + +package goexperiment + +const HeapMinimum512KiB = false +const HeapMinimum512KiBInt = 0 diff --git a/src/internal/goexperiment/exp_heapminimum512kib_on.go b/src/internal/goexperiment/exp_heapminimum512kib_on.go new file mode 100644 index 00000000000000..bab684b5e680a5 --- /dev/null +++ b/src/internal/goexperiment/exp_heapminimum512kib_on.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.heapminimum512kib +// +build goexperiment.heapminimum512kib + +package goexperiment + +const HeapMinimum512KiB = true +const HeapMinimum512KiBInt = 1 diff --git a/src/internal/goexperiment/exp_regabi_off.go b/src/internal/goexperiment/exp_regabi_off.go deleted file mode 100644 index 5d8823843d9484..00000000000000 --- a/src/internal/goexperiment/exp_regabi_off.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.regabi -// +build !goexperiment.regabi - -package goexperiment - -const Regabi = false -const RegabiInt = 0 diff --git a/src/internal/goexperiment/exp_regabi_on.go b/src/internal/goexperiment/exp_regabi_on.go deleted file mode 100644 index c08d58e9b2aa25..00000000000000 --- a/src/internal/goexperiment/exp_regabi_on.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.regabi -// +build goexperiment.regabi - -package goexperiment - -const Regabi = true -const RegabiInt = 1 diff --git a/src/internal/goexperiment/exp_regabidefer_off.go b/src/internal/goexperiment/exp_regabidefer_off.go deleted file mode 100644 index b47c0c2cf55129..00000000000000 --- a/src/internal/goexperiment/exp_regabidefer_off.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.regabidefer -// +build !goexperiment.regabidefer - -package goexperiment - -const RegabiDefer = false -const RegabiDeferInt = 0 diff --git a/src/internal/goexperiment/exp_regabidefer_on.go b/src/internal/goexperiment/exp_regabidefer_on.go deleted file mode 100644 index bbf2f6c69be4d6..00000000000000 --- a/src/internal/goexperiment/exp_regabidefer_on.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.regabidefer -// +build goexperiment.regabidefer - -package goexperiment - -const RegabiDefer = true -const RegabiDeferInt = 1 diff --git a/src/internal/goexperiment/exp_regabig_off.go b/src/internal/goexperiment/exp_regabig_off.go deleted file mode 100644 index 1b37d451868021..00000000000000 --- a/src/internal/goexperiment/exp_regabig_off.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.regabig -// +build !goexperiment.regabig - -package goexperiment - -const RegabiG = false -const RegabiGInt = 0 diff --git a/src/internal/goexperiment/exp_regabig_on.go b/src/internal/goexperiment/exp_regabig_on.go deleted file mode 100644 index 7e5b162e0b2ae2..00000000000000 --- a/src/internal/goexperiment/exp_regabig_on.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.regabig -// +build goexperiment.regabig - -package goexperiment - -const RegabiG = true -const RegabiGInt = 1 diff --git a/src/internal/goexperiment/exp_regabireflect_off.go b/src/internal/goexperiment/exp_regabireflect_off.go deleted file mode 100644 index 515f4a53e6ced9..00000000000000 --- a/src/internal/goexperiment/exp_regabireflect_off.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.regabireflect -// +build !goexperiment.regabireflect - -package goexperiment - -const RegabiReflect = false -const RegabiReflectInt = 0 diff --git a/src/internal/goexperiment/exp_regabireflect_on.go b/src/internal/goexperiment/exp_regabireflect_on.go deleted file mode 100644 index e8a3e9c06ac739..00000000000000 --- a/src/internal/goexperiment/exp_regabireflect_on.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.regabireflect -// +build goexperiment.regabireflect - -package goexperiment - -const RegabiReflect = true -const RegabiReflectInt = 1 diff --git a/src/internal/goexperiment/exp_unified_off.go b/src/internal/goexperiment/exp_unified_off.go new file mode 100644 index 00000000000000..4c16fd85624c60 --- /dev/null +++ b/src/internal/goexperiment/exp_unified_off.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.unified +// +build !goexperiment.unified + +package goexperiment + +const Unified = false +const UnifiedInt = 0 diff --git a/src/internal/goexperiment/exp_unified_on.go b/src/internal/goexperiment/exp_unified_on.go new file mode 100644 index 00000000000000..2b17ba3e79b4bb --- /dev/null +++ b/src/internal/goexperiment/exp_unified_on.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.unified +// +build goexperiment.unified + +package goexperiment + +const Unified = true +const UnifiedInt = 1 diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index cd4c178818495f..20d9c2da5d9605 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -58,6 +58,11 @@ type Flags struct { FieldTrack bool PreemptibleLoops bool StaticLockRanking bool + BoringCrypto bool + + // Unified enables the compiler's unified IR construction + // experiment. + Unified bool // Regabi is split into several sub-experiments that can be // enabled individually. Not all combinations work. @@ -68,26 +73,17 @@ type Flags struct { // ABI0 and ABIInternal functions. Without this, the ABIs are // assumed to be identical so cross-ABI calls are direct. RegabiWrappers bool - // RegabiG enables dedicated G and zero registers in - // ABIInternal. - // - // Requires wrappers because it makes the ABIs incompatible. - RegabiG bool - // RegabiReflect enables the register-passing paths in - // reflection calls. This is also gated by intArgRegs in - // reflect and runtime (which are disabled by default) so it - // can be used in targeted tests. - RegabiReflect bool - // RegabiDefer enables desugaring defer and go calls - // into argument-less closures. - RegabiDefer bool // RegabiArgs enables register arguments/results in all // compiled Go functions. // - // Requires wrappers (to do ABI translation), g (because - // runtime assembly that's been ported to ABIInternal uses the - // G register), reflect (so reflection calls use registers), - // and defer (because the runtime doesn't support passing - // register arguments to defer/go). + // Requires wrappers (to do ABI translation), and reflect (so + // reflection calls use registers). RegabiArgs bool + + // HeapMinimum512KiB reduces the minimum heap size to 512 KiB. + // + // This was originally reduced as part of PacerRedesign, but + // has been broken out to its own experiment that is disabled + // by default. + HeapMinimum512KiB bool } diff --git a/src/internal/goos/gengoos.go b/src/internal/goos/gengoos.go new file mode 100644 index 00000000000000..37d9706d1e877c --- /dev/null +++ b/src/internal/goos/gengoos.go @@ -0,0 +1,71 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "strings" +) + +var gooses []string + +func main() { + data, err := os.ReadFile("../../go/build/syslist.go") + if err != nil { + log.Fatal(err) + } + const goosPrefix = `var knownOS = map[string]bool{` + inGOOS := false + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, goosPrefix) { + inGOOS = true + } else if inGOOS && strings.HasPrefix(line, "}") { + break + } else if inGOOS { + goos := strings.Fields(line)[0] + goos = strings.TrimPrefix(goos, `"`) + goos = strings.TrimSuffix(goos, `":`) + gooses = append(gooses, goos) + } + } + + for _, target := range gooses { + if target == "nacl" { + continue + } + var tags []string + if target == "linux" { + tags = append(tags, "!android") // must explicitly exclude android for linux + } + if target == "solaris" { + tags = append(tags, "!illumos") // must explicitly exclude illumos for solaris + } + if target == "darwin" { + tags = append(tags, "!ios") // must explicitly exclude ios for darwin + } + tags = append(tags, target) // must explicitly include target for bootstrapping purposes + var buf bytes.Buffer + fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") + fmt.Fprintf(&buf, "//go:build %s\n\n", strings.Join(tags, " && ")) + fmt.Fprintf(&buf, "package goos\n\n") + fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target) + for _, goos := range gooses { + value := 0 + if goos == target { + value = 1 + } + fmt.Fprintf(&buf, "const Is%s = %d\n", strings.Title(goos), value) + } + err := os.WriteFile("zgoos_"+target+".go", buf.Bytes(), 0666) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/src/internal/goos/goos.go b/src/internal/goos/goos.go new file mode 100644 index 00000000000000..02dc9688cb2107 --- /dev/null +++ b/src/internal/goos/goos.go @@ -0,0 +1,13 @@ +// 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 goos contains GOOS-specific constants. +package goos + +// The next line makes 'go generate' write the zgoos*.go files with +// per-OS information, including constants named Is$GOOS for every +// known GOOS. The constant is 1 on the current system, 0 otherwise; +// multiplying by them is useful for defining GOOS-specific constants. +// +//go:generate go run gengoos.go diff --git a/src/internal/goos/zgoos_aix.go b/src/internal/goos/zgoos_aix.go new file mode 100644 index 00000000000000..ff861550c4851a --- /dev/null +++ b/src/internal/goos/zgoos_aix.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build aix + +package goos + +const GOOS = `aix` + +const IsAix = 1 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_android.go b/src/internal/goos/zgoos_android.go new file mode 100644 index 00000000000000..e8aaa1242842d1 --- /dev/null +++ b/src/internal/goos/zgoos_android.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build android + +package goos + +const GOOS = `android` + +const IsAix = 0 +const IsAndroid = 1 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_darwin.go b/src/internal/goos/zgoos_darwin.go new file mode 100644 index 00000000000000..decdd496425c7f --- /dev/null +++ b/src/internal/goos/zgoos_darwin.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build !ios && darwin + +package goos + +const GOOS = `darwin` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 1 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_dragonfly.go b/src/internal/goos/zgoos_dragonfly.go new file mode 100644 index 00000000000000..2224baa2301831 --- /dev/null +++ b/src/internal/goos/zgoos_dragonfly.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build dragonfly + +package goos + +const GOOS = `dragonfly` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 1 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_freebsd.go b/src/internal/goos/zgoos_freebsd.go new file mode 100644 index 00000000000000..3ee5bf998e21c3 --- /dev/null +++ b/src/internal/goos/zgoos_freebsd.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build freebsd + +package goos + +const GOOS = `freebsd` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 1 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_hurd.go b/src/internal/goos/zgoos_hurd.go new file mode 100644 index 00000000000000..8a3d34304d8f7f --- /dev/null +++ b/src/internal/goos/zgoos_hurd.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build hurd + +package goos + +const GOOS = `hurd` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 1 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_illumos.go b/src/internal/goos/zgoos_illumos.go new file mode 100644 index 00000000000000..fc1b9a9e22fcc7 --- /dev/null +++ b/src/internal/goos/zgoos_illumos.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build illumos + +package goos + +const GOOS = `illumos` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 1 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_ios.go b/src/internal/goos/zgoos_ios.go new file mode 100644 index 00000000000000..746e769ef766a0 --- /dev/null +++ b/src/internal/goos/zgoos_ios.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build ios + +package goos + +const GOOS = `ios` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 1 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_js.go b/src/internal/goos/zgoos_js.go new file mode 100644 index 00000000000000..6cf2a5d9e27133 --- /dev/null +++ b/src/internal/goos/zgoos_js.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build js + +package goos + +const GOOS = `js` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 1 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_linux.go b/src/internal/goos/zgoos_linux.go new file mode 100644 index 00000000000000..cb9d6e8afaa905 --- /dev/null +++ b/src/internal/goos/zgoos_linux.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build !android && linux + +package goos + +const GOOS = `linux` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 1 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_netbsd.go b/src/internal/goos/zgoos_netbsd.go new file mode 100644 index 00000000000000..8285928d350471 --- /dev/null +++ b/src/internal/goos/zgoos_netbsd.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build netbsd + +package goos + +const GOOS = `netbsd` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 1 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_openbsd.go b/src/internal/goos/zgoos_openbsd.go new file mode 100644 index 00000000000000..3f739a4a2f728c --- /dev/null +++ b/src/internal/goos/zgoos_openbsd.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build openbsd + +package goos + +const GOOS = `openbsd` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 1 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_plan9.go b/src/internal/goos/zgoos_plan9.go new file mode 100644 index 00000000000000..d4c1c651f7bf88 --- /dev/null +++ b/src/internal/goos/zgoos_plan9.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build plan9 + +package goos + +const GOOS = `plan9` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 1 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_solaris.go b/src/internal/goos/zgoos_solaris.go new file mode 100644 index 00000000000000..69e3285ab62046 --- /dev/null +++ b/src/internal/goos/zgoos_solaris.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build !illumos && solaris + +package goos + +const GOOS = `solaris` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 1 +const IsWindows = 0 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_windows.go b/src/internal/goos/zgoos_windows.go new file mode 100644 index 00000000000000..16158be78b97e8 --- /dev/null +++ b/src/internal/goos/zgoos_windows.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build windows + +package goos + +const GOOS = `windows` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 1 +const IsZos = 0 diff --git a/src/internal/goos/zgoos_zos.go b/src/internal/goos/zgoos_zos.go new file mode 100644 index 00000000000000..fb6165c7a12ec8 --- /dev/null +++ b/src/internal/goos/zgoos_zos.go @@ -0,0 +1,25 @@ +// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. + +//go:build zos + +package goos + +const GOOS = `zos` + +const IsAix = 0 +const IsAndroid = 0 +const IsDarwin = 0 +const IsDragonfly = 0 +const IsFreebsd = 0 +const IsHurd = 0 +const IsIllumos = 0 +const IsIos = 0 +const IsJs = 0 +const IsLinux = 0 +const IsNacl = 0 +const IsNetbsd = 0 +const IsOpenbsd = 0 +const IsPlan9 = 0 +const IsSolaris = 0 +const IsWindows = 0 +const IsZos = 1 diff --git a/src/internal/goroot/gc.go b/src/internal/goroot/gc.go index 2338b78f3aa987..79403d29fc02e5 100644 --- a/src/internal/goroot/gc.go +++ b/src/internal/goroot/gc.go @@ -3,13 +3,12 @@ // license that can be found in the LICENSE file. //go:build gc -// +build gc package goroot import ( - exec "internal/execabs" "os" + "os/exec" "path/filepath" "strings" "sync" diff --git a/src/internal/goroot/gccgo.go b/src/internal/goroot/gccgo.go index b1041da11d8b7c..62841222a7efdb 100644 --- a/src/internal/goroot/gccgo.go +++ b/src/internal/goroot/gccgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gccgo -// +build gccgo package goroot diff --git a/src/internal/goversion/goversion.go b/src/internal/goversion/goversion.go index 4cc15688c0b705..e9ecf8e64319f3 100644 --- a/src/internal/goversion/goversion.go +++ b/src/internal/goversion/goversion.go @@ -9,4 +9,4 @@ package goversion // // It should be updated at the start of each development cycle to be // the version of the next Go 1.x release. See golang.org/issue/40705. -const Version = 17 +const Version = 20 diff --git a/src/internal/intern/intern.go b/src/internal/intern/intern.go new file mode 100644 index 00000000000000..c7639b4668736a --- /dev/null +++ b/src/internal/intern/intern.go @@ -0,0 +1,179 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package intern lets you make smaller comparable values by boxing +// a larger comparable value (such as a 16 byte string header) down +// into a globally unique 8 byte pointer. +// +// The globally unique pointers are garbage collected with weak +// references and finalizers. This package hides that. +package intern + +import ( + "internal/godebug" + "runtime" + "sync" + "unsafe" +) + +// A Value pointer is the handle to an underlying comparable value. +// See func Get for how Value pointers may be used. +type Value struct { + _ [0]func() // prevent people from accidentally using value type as comparable + cmpVal any + // resurrected is guarded by mu (for all instances of Value). + // It is set true whenever v is synthesized from a uintptr. + resurrected bool +} + +// Get returns the comparable value passed to the Get func +// that returned v. +func (v *Value) Get() any { return v.cmpVal } + +// key is a key in our global value map. +// It contains type-specialized fields to avoid allocations +// when converting common types to empty interfaces. +type key struct { + s string + cmpVal any + // isString reports whether key contains a string. + // Without it, the zero value of key is ambiguous. + isString bool +} + +// keyFor returns a key to use with cmpVal. +func keyFor(cmpVal any) key { + if s, ok := cmpVal.(string); ok { + return key{s: s, isString: true} + } + return key{cmpVal: cmpVal} +} + +// Value returns a *Value built from k. +func (k key) Value() *Value { + if k.isString { + return &Value{cmpVal: k.s} + } + return &Value{cmpVal: k.cmpVal} +} + +var ( + // mu guards valMap, a weakref map of *Value by underlying value. + // It also guards the resurrected field of all *Values. + mu sync.Mutex + valMap = map[key]uintptr{} // to uintptr(*Value) + valSafe = safeMap() // non-nil in safe+leaky mode +) + +// safeMap returns a non-nil map if we're in safe-but-leaky mode, +// as controlled by GODEBUG=intern=leaky +func safeMap() map[key]*Value { + if godebug.Get("intern") == "leaky" { + return map[key]*Value{} + } + return nil +} + +// Get returns a pointer representing the comparable value cmpVal. +// +// The returned pointer will be the same for Get(v) and Get(v2) +// if and only if v == v2, and can be used as a map key. +func Get(cmpVal any) *Value { + return get(keyFor(cmpVal)) +} + +// GetByString is identical to Get, except that it is specialized for strings. +// This avoids an allocation from putting a string into an interface{} +// to pass as an argument to Get. +func GetByString(s string) *Value { + return get(key{s: s, isString: true}) +} + +// We play unsafe games that violate Go's rules (and assume a non-moving +// collector). So we quiet Go here. +// See the comment below Get for more implementation details. +// +//go:nocheckptr +func get(k key) *Value { + mu.Lock() + defer mu.Unlock() + + var v *Value + if valSafe != nil { + v = valSafe[k] + } else if addr, ok := valMap[k]; ok { + v = (*Value)(unsafe.Pointer(addr)) + v.resurrected = true + } + if v != nil { + return v + } + v = k.Value() + if valSafe != nil { + valSafe[k] = v + } else { + // SetFinalizer before uintptr conversion (theoretical concern; + // see https://github.com/go4org/intern/issues/13) + runtime.SetFinalizer(v, finalize) + valMap[k] = uintptr(unsafe.Pointer(v)) + } + return v +} + +func finalize(v *Value) { + mu.Lock() + defer mu.Unlock() + if v.resurrected { + // We lost the race. Somebody resurrected it while we + // were about to finalize it. Try again next round. + v.resurrected = false + runtime.SetFinalizer(v, finalize) + return + } + delete(valMap, keyFor(v.cmpVal)) +} + +// Interning is simple if you don't require that unused values be +// garbage collectable. But we do require that; we don't want to be +// DOS vector. We do this by using a uintptr to hide the pointer from +// the garbage collector, and using a finalizer to eliminate the +// pointer when no other code is using it. +// +// The obvious implementation of this is to use a +// map[interface{}]uintptr-of-*interface{}, and set up a finalizer to +// delete from the map. Unfortunately, this is racy. Because pointers +// are being created in violation of Go's unsafety rules, it's +// possible to create a pointer to a value concurrently with the GC +// concluding that the value can be collected. There are other races +// that break the equality invariant as well, but the use-after-free +// will cause a runtime crash. +// +// To make this work, the finalizer needs to know that no references +// have been unsafely created since the finalizer was set up. To do +// this, values carry a "resurrected" sentinel, which gets set +// whenever a pointer is unsafely created. If the finalizer encounters +// the sentinel, it clears the sentinel and delays collection for one +// additional GC cycle, by re-installing itself as finalizer. This +// ensures that the unsafely created pointer is visible to the GC, and +// will correctly prevent collection. +// +// This technique does mean that interned values that get reused take +// at least 3 GC cycles to fully collect (1 to clear the sentinel, 1 +// to clean up the unsafe map, 1 to be actually deleted). +// +// @ianlancetaylor commented in +// https://github.com/golang/go/issues/41303#issuecomment-717401656 +// that it is possible to implement weak references in terms of +// finalizers without unsafe. Unfortunately, the approach he outlined +// does not work here, for two reasons. First, there is no way to +// construct a strong pointer out of a weak pointer; our map stores +// weak pointers, but we must return strong pointers to callers. +// Second, and more fundamentally, we must return not just _a_ strong +// pointer to callers, but _the same_ strong pointer to callers. In +// order to return _the same_ strong pointer to callers, we must track +// it, which is exactly what we cannot do with strong pointers. +// +// See https://github.com/inetaf/netaddr/issues/53 for more +// discussion, and https://github.com/go4org/intern/issues/2 for an +// illustration of the subtleties at play. diff --git a/src/internal/intern/intern_test.go b/src/internal/intern/intern_test.go new file mode 100644 index 00000000000000..d1e409ef95718d --- /dev/null +++ b/src/internal/intern/intern_test.go @@ -0,0 +1,199 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package intern + +import ( + "fmt" + "runtime" + "testing" +) + +func TestBasics(t *testing.T) { + clearMap() + foo := Get("foo") + bar := Get("bar") + empty := Get("") + nilEface := Get(nil) + i := Get(0x7777777) + foo2 := Get("foo") + bar2 := Get("bar") + empty2 := Get("") + nilEface2 := Get(nil) + i2 := Get(0x7777777) + foo3 := GetByString("foo") + empty3 := GetByString("") + + if foo.Get() != foo2.Get() { + t.Error("foo/foo2 values differ") + } + if foo.Get() != foo3.Get() { + t.Error("foo/foo3 values differ") + } + if foo.Get() != "foo" { + t.Error("foo.Get not foo") + } + if foo != foo2 { + t.Error("foo/foo2 pointers differ") + } + if foo != foo3 { + t.Error("foo/foo3 pointers differ") + } + + if bar.Get() != bar2.Get() { + t.Error("bar values differ") + } + if bar.Get() != "bar" { + t.Error("bar.Get not bar") + } + if bar != bar2 { + t.Error("bar pointers differ") + } + + if i.Get() != i.Get() { + t.Error("i values differ") + } + if i.Get() != 0x7777777 { + t.Error("i.Get not 0x7777777") + } + if i != i2 { + t.Error("i pointers differ") + } + + if empty.Get() != empty2.Get() { + t.Error("empty/empty2 values differ") + } + if empty.Get() != empty.Get() { + t.Error("empty/empty3 values differ") + } + if empty.Get() != "" { + t.Error("empty.Get not empty string") + } + if empty != empty2 { + t.Error("empty/empty2 pointers differ") + } + if empty != empty3 { + t.Error("empty/empty3 pointers differ") + } + + if nilEface.Get() != nilEface2.Get() { + t.Error("nilEface values differ") + } + if nilEface.Get() != nil { + t.Error("nilEface.Get not nil") + } + if nilEface != nilEface2 { + t.Error("nilEface pointers differ") + } + + if n := mapLen(); n != 5 { + t.Errorf("map len = %d; want 4", n) + } + + wantEmpty(t) +} + +func wantEmpty(t testing.TB) { + t.Helper() + const gcTries = 5000 + for try := 0; try < gcTries; try++ { + runtime.GC() + n := mapLen() + if n == 0 { + break + } + if try == gcTries-1 { + t.Errorf("map len = %d after (%d GC tries); want 0, contents: %v", n, gcTries, mapKeys()) + } + } +} + +func TestStress(t *testing.T) { + iters := 10000 + if testing.Short() { + iters = 1000 + } + var sink []byte + for i := 0; i < iters; i++ { + _ = Get("foo") + sink = make([]byte, 1<<20) + } + _ = sink +} + +func BenchmarkStress(b *testing.B) { + done := make(chan struct{}) + defer close(done) + go func() { + for { + select { + case <-done: + return + default: + } + runtime.GC() + } + }() + + clearMap() + v1 := Get("foo") + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v2 := Get("foo") + if v1 != v2 { + b.Fatal("wrong value") + } + // And also a key we don't retain: + _ = Get("bar") + } + }) + runtime.GC() + wantEmpty(b) +} + +func mapLen() int { + mu.Lock() + defer mu.Unlock() + return len(valMap) +} + +func mapKeys() (keys []string) { + mu.Lock() + defer mu.Unlock() + for k := range valMap { + keys = append(keys, fmt.Sprint(k)) + } + return keys +} + +func clearMap() { + mu.Lock() + defer mu.Unlock() + for k := range valMap { + delete(valMap, k) + } +} + +var ( + globalString = "not a constant" + sink string +) + +func TestGetByStringAllocs(t *testing.T) { + allocs := int(testing.AllocsPerRun(100, func() { + GetByString(globalString) + })) + if allocs != 0 { + t.Errorf("GetString allocated %d objects, want 0", allocs) + } +} + +func BenchmarkGetByString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + v := GetByString(globalString) + sink = v.Get().(string) + } +} diff --git a/src/internal/lazytemplate/lazytemplate.go b/src/internal/lazytemplate/lazytemplate.go index c83eaeaf3ec6eb..8eeed5a527ac6a 100644 --- a/src/internal/lazytemplate/lazytemplate.go +++ b/src/internal/lazytemplate/lazytemplate.go @@ -33,7 +33,7 @@ func (r *Template) build() { r.name, r.text = "", "" } -func (r *Template) Execute(w io.Writer, data interface{}) error { +func (r *Template) Execute(w io.Writer, data any) error { return r.tp().Execute(w, data) } diff --git a/src/internal/nettrace/nettrace.go b/src/internal/nettrace/nettrace.go index de3254df589f0e..0a2bf925e9a7f2 100644 --- a/src/internal/nettrace/nettrace.go +++ b/src/internal/nettrace/nettrace.go @@ -16,7 +16,8 @@ type TraceKey struct{} // specify an alternate resolver func. // It is not exposed to outsider users. (But see issue 12503) // The value should be the same type as lookupIP: -// func lookupIP(ctx context.Context, host string) ([]IPAddr, error) +// +// func lookupIP(ctx context.Context, host string) ([]IPAddr, error) type LookupIPAltResolverKey struct{} // Trace contains a set of hooks for tracing events within @@ -27,10 +28,10 @@ type Trace struct { DNSStart func(name string) // DNSDone is called after a DNS lookup completes (or fails). - // The coalesced parameter is whether singleflight de-dupped + // The coalesced parameter is whether singleflight de-duped // the call. The addrs are of type net.IPAddr but can't // actually be for circular dependency reasons. - DNSDone func(netIPs []interface{}, coalesced bool, err error) + DNSDone func(netIPs []any, coalesced bool, err error) // ConnectStart is called before a Dial, excluding Dials made // during DNS lookups. In the case of DualStack (Happy Eyeballs) diff --git a/src/internal/pkgbits/codes.go b/src/internal/pkgbits/codes.go new file mode 100644 index 00000000000000..f0cabde96eba92 --- /dev/null +++ b/src/internal/pkgbits/codes.go @@ -0,0 +1,77 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +// A Code is an enum value that can be encoded into bitstreams. +// +// Code types are preferable for enum types, because they allow +// Decoder to detect desyncs. +type Code interface { + // Marker returns the SyncMarker for the Code's dynamic type. + Marker() SyncMarker + + // Value returns the Code's ordinal value. + Value() int +} + +// A CodeVal distinguishes among go/constant.Value encodings. +type CodeVal int + +func (c CodeVal) Marker() SyncMarker { return SyncVal } +func (c CodeVal) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ValBool CodeVal = iota + ValString + ValInt64 + ValBigInt + ValBigRat + ValBigFloat +) + +// A CodeType distinguishes among go/types.Type encodings. +type CodeType int + +func (c CodeType) Marker() SyncMarker { return SyncType } +func (c CodeType) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + TypeBasic CodeType = iota + TypeNamed + TypePointer + TypeSlice + TypeArray + TypeChan + TypeMap + TypeSignature + TypeStruct + TypeInterface + TypeUnion + TypeTypeParam +) + +// A CodeObj distinguishes among go/types.Object encodings. +type CodeObj int + +func (c CodeObj) Marker() SyncMarker { return SyncCodeObj } +func (c CodeObj) Value() int { return int(c) } + +// Note: These values are public and cannot be changed without +// updating the go/types importers. + +const ( + ObjAlias CodeObj = iota + ObjConst + ObjType + ObjFunc + ObjVar + ObjStub +) diff --git a/src/internal/pkgbits/decoder.go b/src/internal/pkgbits/decoder.go new file mode 100644 index 00000000000000..357e328a3bae49 --- /dev/null +++ b/src/internal/pkgbits/decoder.go @@ -0,0 +1,432 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "encoding/binary" + "fmt" + "go/constant" + "go/token" + "io" + "math/big" + "os" + "runtime" + "strings" +) + +// A PkgDecoder provides methods for decoding a package's Unified IR +// export data. +type PkgDecoder struct { + // version is the file format version. + version uint32 + + // sync indicates whether the file uses sync markers. + sync bool + + // pkgPath is the package path for the package to be decoded. + // + // TODO(mdempsky): Remove; unneeded since CL 391014. + pkgPath string + + // elemData is the full data payload of the encoded package. + // Elements are densely and contiguously packed together. + // + // The last 8 bytes of elemData are the package fingerprint. + elemData string + + // elemEnds stores the byte-offset end positions of element + // bitstreams within elemData. + // + // For example, element I's bitstream data starts at elemEnds[I-1] + // (or 0, if I==0) and ends at elemEnds[I]. + // + // Note: elemEnds is indexed by absolute indices, not + // section-relative indices. + elemEnds []uint32 + + // elemEndsEnds stores the index-offset end positions of relocation + // sections within elemEnds. + // + // For example, section K's end positions start at elemEndsEnds[K-1] + // (or 0, if K==0) and end at elemEndsEnds[K]. + elemEndsEnds [numRelocs]uint32 +} + +// PkgPath returns the package path for the package +// +// TODO(mdempsky): Remove; unneeded since CL 391014. +func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } + +// SyncMarkers reports whether pr uses sync markers. +func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } + +// NewPkgDecoder returns a PkgDecoder initialized to read the Unified +// IR export data from input. pkgPath is the package path for the +// compilation unit that produced the export data. +// +// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014. +func NewPkgDecoder(pkgPath, input string) PkgDecoder { + pr := PkgDecoder{ + pkgPath: pkgPath, + } + + // TODO(mdempsky): Implement direct indexing of input string to + // avoid copying the position information. + + r := strings.NewReader(input) + + assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + + switch pr.version { + default: + panic(fmt.Errorf("unsupported version: %v", pr.version)) + case 0: + // no flags + case 1: + var flags uint32 + assert(binary.Read(r, binary.LittleEndian, &flags) == nil) + pr.sync = flags&flagSyncMarkers != 0 + } + + assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) + + pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) + assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) + + pos, err := r.Seek(0, io.SeekCurrent) + assert(err == nil) + + pr.elemData = input[pos:] + assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) + + return pr +} + +// NumElems returns the number of elements in section k. +func (pr *PkgDecoder) NumElems(k RelocKind) int { + count := int(pr.elemEndsEnds[k]) + if k > 0 { + count -= int(pr.elemEndsEnds[k-1]) + } + return count +} + +// TotalElems returns the total number of elements across all sections. +func (pr *PkgDecoder) TotalElems() int { + return len(pr.elemEnds) +} + +// Fingerprint returns the package fingerprint. +func (pr *PkgDecoder) Fingerprint() [8]byte { + var fp [8]byte + copy(fp[:], pr.elemData[len(pr.elemData)-8:]) + return fp +} + +// AbsIdx returns the absolute index for the given (section, index) +// pair. +func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { + absIdx := int(idx) + if k > 0 { + absIdx += int(pr.elemEndsEnds[k-1]) + } + if absIdx >= int(pr.elemEndsEnds[k]) { + errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + } + return absIdx +} + +// DataIdx returns the raw element bitstream for the given (section, +// index) pair. +func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string { + absIdx := pr.AbsIdx(k, idx) + + var start uint32 + if absIdx > 0 { + start = pr.elemEnds[absIdx-1] + } + end := pr.elemEnds[absIdx] + + return pr.elemData[start:end] +} + +// StringIdx returns the string value for the given string index. +func (pr *PkgDecoder) StringIdx(idx Index) string { + return pr.DataIdx(RelocString, idx) +} + +// NewDecoder returns a Decoder for the given (section, index) pair, +// and decodes the given SyncMarker from the element bitstream. +func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder { + r := pr.NewDecoderRaw(k, idx) + r.Sync(marker) + return r +} + +// NewDecoderRaw returns a Decoder for the given (section, index) pair. +// +// Most callers should use NewDecoder instead. +func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { + r := Decoder{ + common: pr, + k: k, + Idx: idx, + } + + r.Data.Reset(pr.DataIdx(k, idx)) + r.Sync(SyncRelocs) + r.Relocs = make([]RelocEnt, r.Len()) + for i := range r.Relocs { + r.Sync(SyncReloc) + r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())} + } + + return r +} + +// A Decoder provides methods for decoding an individual element's +// bitstream data. +type Decoder struct { + common *PkgDecoder + + Relocs []RelocEnt + Data strings.Reader + + k RelocKind + Idx Index +} + +func (r *Decoder) checkErr(err error) { + if err != nil { + errorf("unexpected decoding error: %w", err) + } +} + +func (r *Decoder) rawUvarint() uint64 { + x, err := binary.ReadUvarint(&r.Data) + r.checkErr(err) + return x +} + +func (r *Decoder) rawVarint() int64 { + ux := r.rawUvarint() + + // Zig-zag decode. + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x +} + +func (r *Decoder) rawReloc(k RelocKind, idx int) Index { + e := r.Relocs[idx] + assert(e.Kind == k) + return e.Idx +} + +// Sync decodes a sync marker from the element bitstream and asserts +// that it matches the expected marker. +// +// If EnableSync is false, then Sync is a no-op. +func (r *Decoder) Sync(mWant SyncMarker) { + if !r.common.sync { + return + } + + pos, _ := r.Data.Seek(0, io.SeekCurrent) + mHave := SyncMarker(r.rawUvarint()) + writerPCs := make([]int, r.rawUvarint()) + for i := range writerPCs { + writerPCs[i] = int(r.rawUvarint()) + } + + if mHave == mWant { + return + } + + // There's some tension here between printing: + // + // (1) full file paths that tools can recognize (e.g., so emacs + // hyperlinks the "file:line" text for easy navigation), or + // + // (2) short file paths that are easier for humans to read (e.g., by + // omitting redundant or irrelevant details, so it's easier to + // focus on the useful bits that remain). + // + // The current formatting favors the former, as it seems more + // helpful in practice. But perhaps the formatting could be improved + // to better address both concerns. For example, use relative file + // paths if they would be shorter, or rewrite file paths to contain + // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how + // to reliably expand that again. + + fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos) + + fmt.Printf("\nfound %v, written at:\n", mHave) + if len(writerPCs) == 0 { + fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) + } + for _, pc := range writerPCs { + fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc))) + } + + fmt.Printf("\nexpected %v, reading at:\n", mWant) + var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size? + n := runtime.Callers(2, readerPCs[:]) + for _, pc := range fmtFrames(readerPCs[:n]...) { + fmt.Printf("\t%s\n", pc) + } + + // We already printed a stack trace for the reader, so now we can + // simply exit. Printing a second one with panic or base.Fatalf + // would just be noise. + os.Exit(1) +} + +// Bool decodes and returns a bool value from the element bitstream. +func (r *Decoder) Bool() bool { + r.Sync(SyncBool) + x, err := r.Data.ReadByte() + r.checkErr(err) + assert(x < 2) + return x != 0 +} + +// Int64 decodes and returns an int64 value from the element bitstream. +func (r *Decoder) Int64() int64 { + r.Sync(SyncInt64) + return r.rawVarint() +} + +// Int64 decodes and returns a uint64 value from the element bitstream. +func (r *Decoder) Uint64() uint64 { + r.Sync(SyncUint64) + return r.rawUvarint() +} + +// Len decodes and returns a non-negative int value from the element bitstream. +func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v } + +// Int decodes and returns an int value from the element bitstream. +func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v } + +// Uint decodes and returns a uint value from the element bitstream. +func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v } + +// Code decodes a Code value from the element bitstream and returns +// its ordinal value. It's the caller's responsibility to convert the +// result to an appropriate Code type. +// +// TODO(mdempsky): Ideally this method would have signature "Code[T +// Code] T" instead, but we don't allow generic methods and the +// compiler can't depend on generics yet anyway. +func (r *Decoder) Code(mark SyncMarker) int { + r.Sync(mark) + return r.Len() +} + +// Reloc decodes a relocation of expected section k from the element +// bitstream and returns an index to the referenced element. +func (r *Decoder) Reloc(k RelocKind) Index { + r.Sync(SyncUseReloc) + return r.rawReloc(k, r.Len()) +} + +// String decodes and returns a string value from the element +// bitstream. +func (r *Decoder) String() string { + r.Sync(SyncString) + return r.common.StringIdx(r.Reloc(RelocString)) +} + +// Strings decodes and returns a variable-length slice of strings from +// the element bitstream. +func (r *Decoder) Strings() []string { + res := make([]string, r.Len()) + for i := range res { + res[i] = r.String() + } + return res +} + +// Value decodes and returns a constant.Value from the element +// bitstream. +func (r *Decoder) Value() constant.Value { + r.Sync(SyncValue) + isComplex := r.Bool() + val := r.scalar() + if isComplex { + val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) + } + return val +} + +func (r *Decoder) scalar() constant.Value { + switch tag := CodeVal(r.Code(SyncVal)); tag { + default: + panic(fmt.Errorf("unexpected scalar tag: %v", tag)) + + case ValBool: + return constant.MakeBool(r.Bool()) + case ValString: + return constant.MakeString(r.String()) + case ValInt64: + return constant.MakeInt64(r.Int64()) + case ValBigInt: + return constant.Make(r.bigInt()) + case ValBigRat: + num := r.bigInt() + denom := r.bigInt() + return constant.Make(new(big.Rat).SetFrac(num, denom)) + case ValBigFloat: + return constant.Make(r.bigFloat()) + } +} + +func (r *Decoder) bigInt() *big.Int { + v := new(big.Int).SetBytes([]byte(r.String())) + if r.Bool() { + v.Neg(v) + } + return v +} + +func (r *Decoder) bigFloat() *big.Float { + v := new(big.Float).SetPrec(512) + assert(v.UnmarshalText([]byte(r.String())) == nil) + return v +} + +// @@@ Helpers + +// TODO(mdempsky): These should probably be removed. I think they're a +// smell that the export data format is not yet quite right. + +// PeekPkgPath returns the package path for the specified package +// index. +func (pr *PkgDecoder) PeekPkgPath(idx Index) string { + r := pr.NewDecoder(RelocPkg, idx, SyncPkgDef) + path := r.String() + if path == "" { + path = pr.pkgPath + } + return path +} + +// PeekObj returns the package path, object name, and CodeObj for the +// specified object index. +func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { + r := pr.NewDecoder(RelocName, idx, SyncObject1) + r.Sync(SyncSym) + r.Sync(SyncPkg) + path := pr.PeekPkgPath(r.Reloc(RelocPkg)) + name := r.String() + assert(name != "") + + tag := CodeObj(r.Code(SyncCodeObj)) + + return path, name, tag +} diff --git a/src/internal/pkgbits/doc.go b/src/internal/pkgbits/doc.go new file mode 100644 index 00000000000000..4862e390493532 --- /dev/null +++ b/src/internal/pkgbits/doc.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkgbits implements low-level coding abstractions for +// Unified IR's export data format. +// +// At a low-level, a package is a collection of bitstream elements. +// Each element has a "kind" and a dense, non-negative index. +// Elements can be randomly accessed given their kind and index. +// +// Individual elements are sequences of variable-length values (e.g., +// integers, booleans, strings, go/constant values, cross-references +// to other elements). Package pkgbits provides APIs for encoding and +// decoding these low-level values, but the details of mapping +// higher-level Go constructs into elements is left to higher-level +// abstractions. +// +// Elements may cross-reference each other with "relocations." For +// example, an element representing a pointer type has a relocation +// referring to the element type. +// +// Go constructs may be composed as a constellation of multiple +// elements. For example, a declared function may have one element to +// describe the object (e.g., its name, type, position), and a +// separate element to describe its function body. This allows readers +// some flexibility in efficiently seeking or re-reading data (e.g., +// inlining requires re-reading the function body for each inlined +// call, without needing to re-read the object-level details). +package pkgbits diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go new file mode 100644 index 00000000000000..70a2cbae5106c8 --- /dev/null +++ b/src/internal/pkgbits/encoder.go @@ -0,0 +1,394 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "bytes" + "crypto/md5" + "encoding/binary" + "go/constant" + "io" + "math/big" + "runtime" + "strings" +) + +// currentVersion is the current version number. +// +// - v0: initial prototype +// +// - v1: adds the flags uint32 word +// +// TODO(mdempsky): For the next version bump: +// - remove the legacy "has init" bool from the public root +// - remove obj's "derived func instance" bool +const currentVersion uint32 = 1 + +// A PkgEncoder provides methods for encoding a package's Unified IR +// export data. +type PkgEncoder struct { + // elems holds the bitstream for previously encoded elements. + elems [numRelocs][]string + + // stringsIdx maps previously encoded strings to their index within + // the RelocString section, to allow deduplication. That is, + // elems[RelocString][stringsIdx[s]] == s (if present). + stringsIdx map[string]Index + + // syncFrames is the number of frames to write at each sync + // marker. A negative value means sync markers are omitted. + syncFrames int +} + +// SyncMarkers reports whether pw uses sync markers. +func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } + +// NewPkgEncoder returns an initialized PkgEncoder. +// +// syncFrames is the number of caller frames that should be serialized +// at Sync points. Serializing additional frames results in larger +// export data files, but can help diagnosing desync errors in +// higher-level Unified IR reader/writer code. If syncFrames is +// negative, then sync markers are omitted entirely. +func NewPkgEncoder(syncFrames int) PkgEncoder { + return PkgEncoder{ + stringsIdx: make(map[string]Index), + syncFrames: syncFrames, + } +} + +// DumpTo writes the package's encoded data to out0 and returns the +// package fingerprint. +func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { + h := md5.New() + out := io.MultiWriter(out0, h) + + writeUint32 := func(x uint32) { + assert(binary.Write(out, binary.LittleEndian, x) == nil) + } + + writeUint32(currentVersion) + + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) + + // Write elemEndsEnds. + var sum uint32 + for _, elems := range &pw.elems { + sum += uint32(len(elems)) + writeUint32(sum) + } + + // Write elemEnds. + sum = 0 + for _, elems := range &pw.elems { + for _, elem := range elems { + sum += uint32(len(elem)) + writeUint32(sum) + } + } + + // Write elemData. + for _, elems := range &pw.elems { + for _, elem := range elems { + _, err := io.WriteString(out, elem) + assert(err == nil) + } + } + + // Write fingerprint. + copy(fingerprint[:], h.Sum(nil)) + _, err := out0.Write(fingerprint[:]) + assert(err == nil) + + return +} + +// StringIdx adds a string value to the strings section, if not +// already present, and returns its index. +func (pw *PkgEncoder) StringIdx(s string) Index { + if idx, ok := pw.stringsIdx[s]; ok { + assert(pw.elems[RelocString][idx] == s) + return idx + } + + idx := Index(len(pw.elems[RelocString])) + pw.elems[RelocString] = append(pw.elems[RelocString], s) + pw.stringsIdx[s] = idx + return idx +} + +// NewEncoder returns an Encoder for a new element within the given +// section, and encodes the given SyncMarker as the start of the +// element bitstream. +func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder { + e := pw.NewEncoderRaw(k) + e.Sync(marker) + return e +} + +// NewEncoderRaw returns an Encoder for a new element within the given +// section. +// +// Most callers should use NewEncoder instead. +func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder { + idx := Index(len(pw.elems[k])) + pw.elems[k] = append(pw.elems[k], "") // placeholder + + return Encoder{ + p: pw, + k: k, + Idx: idx, + } +} + +// An Encoder provides methods for encoding an individual element's +// bitstream data. +type Encoder struct { + p *PkgEncoder + + Relocs []RelocEnt + RelocMap map[RelocEnt]uint32 + Data bytes.Buffer // accumulated element bitstream data + + encodingRelocHeader bool + + k RelocKind + Idx Index // index within relocation section +} + +// Flush finalizes the element's bitstream and returns its Index. +func (w *Encoder) Flush() Index { + var sb strings.Builder + + // Backup the data so we write the relocations at the front. + var tmp bytes.Buffer + io.Copy(&tmp, &w.Data) + + // TODO(mdempsky): Consider writing these out separately so they're + // easier to strip, along with function bodies, so that we can prune + // down to just the data that's relevant to go/types. + if w.encodingRelocHeader { + panic("encodingRelocHeader already true; recursive flush?") + } + w.encodingRelocHeader = true + w.Sync(SyncRelocs) + w.Len(len(w.Relocs)) + for _, rEnt := range w.Relocs { + w.Sync(SyncReloc) + w.Len(int(rEnt.Kind)) + w.Len(int(rEnt.Idx)) + } + + io.Copy(&sb, &w.Data) + io.Copy(&sb, &tmp) + w.p.elems[w.k][w.Idx] = sb.String() + + return w.Idx +} + +func (w *Encoder) checkErr(err error) { + if err != nil { + errorf("unexpected encoding error: %v", err) + } +} + +func (w *Encoder) rawUvarint(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + _, err := w.Data.Write(buf[:n]) + w.checkErr(err) +} + +func (w *Encoder) rawVarint(x int64) { + // Zig-zag encode. + ux := uint64(x) << 1 + if x < 0 { + ux = ^ux + } + + w.rawUvarint(ux) +} + +func (w *Encoder) rawReloc(r RelocKind, idx Index) int { + e := RelocEnt{r, idx} + if w.RelocMap != nil { + if i, ok := w.RelocMap[e]; ok { + return int(i) + } + } else { + w.RelocMap = make(map[RelocEnt]uint32) + } + + i := len(w.Relocs) + w.RelocMap[e] = uint32(i) + w.Relocs = append(w.Relocs, e) + return i +} + +func (w *Encoder) Sync(m SyncMarker) { + if !w.p.SyncMarkers() { + return + } + + // Writing out stack frame string references requires working + // relocations, but writing out the relocations themselves involves + // sync markers. To prevent infinite recursion, we simply trim the + // stack frame for sync markers within the relocation header. + var frames []string + if !w.encodingRelocHeader && w.p.syncFrames > 0 { + pcs := make([]uintptr, w.p.syncFrames) + n := runtime.Callers(2, pcs) + frames = fmtFrames(pcs[:n]...) + } + + // TODO(mdempsky): Save space by writing out stack frames as a + // linked list so we can share common stack frames. + w.rawUvarint(uint64(m)) + w.rawUvarint(uint64(len(frames))) + for _, frame := range frames { + w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame)))) + } +} + +// Bool encodes and writes a bool value into the element bitstream, +// and then returns the bool value. +// +// For simple, 2-alternative encodings, the idiomatic way to call Bool +// is something like: +// +// if w.Bool(x != 0) { +// // alternative #1 +// } else { +// // alternative #2 +// } +// +// For multi-alternative encodings, use Code instead. +func (w *Encoder) Bool(b bool) bool { + w.Sync(SyncBool) + var x byte + if b { + x = 1 + } + err := w.Data.WriteByte(x) + w.checkErr(err) + return b +} + +// Int64 encodes and writes an int64 value into the element bitstream. +func (w *Encoder) Int64(x int64) { + w.Sync(SyncInt64) + w.rawVarint(x) +} + +// Uint64 encodes and writes a uint64 value into the element bitstream. +func (w *Encoder) Uint64(x uint64) { + w.Sync(SyncUint64) + w.rawUvarint(x) +} + +// Len encodes and writes a non-negative int value into the element bitstream. +func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) } + +// Int encodes and writes an int value into the element bitstream. +func (w *Encoder) Int(x int) { w.Int64(int64(x)) } + +// Len encodes and writes a uint value into the element bitstream. +func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) } + +// Reloc encodes and writes a relocation for the given (section, +// index) pair into the element bitstream. +// +// Note: Only the index is formally written into the element +// bitstream, so bitstream decoders must know from context which +// section an encoded relocation refers to. +func (w *Encoder) Reloc(r RelocKind, idx Index) { + w.Sync(SyncUseReloc) + w.Len(w.rawReloc(r, idx)) +} + +// Code encodes and writes a Code value into the element bitstream. +func (w *Encoder) Code(c Code) { + w.Sync(c.Marker()) + w.Len(c.Value()) +} + +// String encodes and writes a string value into the element +// bitstream. +// +// Internally, strings are deduplicated by adding them to the strings +// section (if not already present), and then writing a relocation +// into the element bitstream. +func (w *Encoder) String(s string) { + w.StringRef(w.p.StringIdx(s)) +} + +// StringRef writes a reference to the given index, which must be a +// previously encoded string value. +func (w *Encoder) StringRef(idx Index) { + w.Sync(SyncString) + w.Reloc(RelocString, idx) +} + +// Strings encodes and writes a variable-length slice of strings into +// the element bitstream. +func (w *Encoder) Strings(ss []string) { + w.Len(len(ss)) + for _, s := range ss { + w.String(s) + } +} + +// Value encodes and writes a constant.Value into the element +// bitstream. +func (w *Encoder) Value(val constant.Value) { + w.Sync(SyncValue) + if w.Bool(val.Kind() == constant.Complex) { + w.scalar(constant.Real(val)) + w.scalar(constant.Imag(val)) + } else { + w.scalar(val) + } +} + +func (w *Encoder) scalar(val constant.Value) { + switch v := constant.Val(val).(type) { + default: + errorf("unhandled %v (%v)", val, val.Kind()) + case bool: + w.Code(ValBool) + w.Bool(v) + case string: + w.Code(ValString) + w.String(v) + case int64: + w.Code(ValInt64) + w.Int64(v) + case *big.Int: + w.Code(ValBigInt) + w.bigInt(v) + case *big.Rat: + w.Code(ValBigRat) + w.bigInt(v.Num()) + w.bigInt(v.Denom()) + case *big.Float: + w.Code(ValBigFloat) + w.bigFloat(v) + } +} + +func (w *Encoder) bigInt(v *big.Int) { + b := v.Bytes() + w.String(string(b)) // TODO: More efficient encoding. + w.Bool(v.Sign() < 0) +} + +func (w *Encoder) bigFloat(v *big.Float) { + b := v.Append(nil, 'p', -1) + w.String(string(b)) // TODO: More efficient encoding. +} diff --git a/src/internal/pkgbits/flags.go b/src/internal/pkgbits/flags.go new file mode 100644 index 00000000000000..654222745facd2 --- /dev/null +++ b/src/internal/pkgbits/flags.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +const ( + flagSyncMarkers = 1 << iota // file format contains sync markers +) diff --git a/src/internal/pkgbits/reloc.go b/src/internal/pkgbits/reloc.go new file mode 100644 index 00000000000000..fcdfb97ca99261 --- /dev/null +++ b/src/internal/pkgbits/reloc.go @@ -0,0 +1,42 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +// A RelocKind indicates a particular section within a unified IR export. +type RelocKind int32 + +// An Index represents a bitstream element index within a particular +// section. +type Index int32 + +// A relocEnt (relocation entry) is an entry in an element's local +// reference table. +// +// TODO(mdempsky): Rename this too. +type RelocEnt struct { + Kind RelocKind + Idx Index +} + +// Reserved indices within the meta relocation section. +const ( + PublicRootIdx Index = 0 + PrivateRootIdx Index = 1 +) + +const ( + RelocString RelocKind = iota + RelocMeta + RelocPosBase + RelocPkg + RelocName + RelocType + RelocObj + RelocObjExt + RelocObjDict + RelocBody + + numRelocs = iota +) diff --git a/src/internal/pkgbits/support.go b/src/internal/pkgbits/support.go new file mode 100644 index 00000000000000..f7579dfdc4ed96 --- /dev/null +++ b/src/internal/pkgbits/support.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import "fmt" + +func assert(b bool) { + if !b { + panic("assertion failed") + } +} + +func errorf(format string, args ...any) { + panic(fmt.Errorf(format, args...)) +} diff --git a/src/internal/pkgbits/sync.go b/src/internal/pkgbits/sync.go new file mode 100644 index 00000000000000..1520b73afb9e96 --- /dev/null +++ b/src/internal/pkgbits/sync.go @@ -0,0 +1,136 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgbits + +import ( + "fmt" + "runtime" + "strings" +) + +// fmtFrames formats a backtrace for reporting reader/writer desyncs. +func fmtFrames(pcs ...uintptr) []string { + res := make([]string, 0, len(pcs)) + walkFrames(pcs, func(file string, line int, name string, offset uintptr) { + // Trim package from function name. It's just redundant noise. + name = strings.TrimPrefix(name, "cmd/compile/internal/noder.") + + res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset)) + }) + return res +} + +type frameVisitor func(file string, line int, name string, offset uintptr) + +// walkFrames calls visit for each call frame represented by pcs. +// +// pcs should be a slice of PCs, as returned by runtime.Callers. +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} + +// SyncMarker is an enum type that represents markers that may be +// written to export data to ensure the reader and writer stay +// synchronized. +type SyncMarker int + +//go:generate stringer -type=SyncMarker -trimprefix=Sync + +const ( + _ SyncMarker = iota + + // Public markers (known to go/types importers). + + // Low-level coding markers. + SyncEOF + SyncBool + SyncInt64 + SyncUint64 + SyncString + SyncValue + SyncVal + SyncRelocs + SyncReloc + SyncUseReloc + + // Higher-level object and type markers. + SyncPublic + SyncPos + SyncPosBase + SyncObject + SyncObject1 + SyncPkg + SyncPkgDef + SyncMethod + SyncType + SyncTypeIdx + SyncTypeParamNames + SyncSignature + SyncParams + SyncParam + SyncCodeObj + SyncSym + SyncLocalIdent + SyncSelector + + // Private markers (only known to cmd/compile). + SyncPrivate + + SyncFuncExt + SyncVarExt + SyncTypeExt + SyncPragma + + SyncExprList + SyncExprs + SyncExpr + SyncExprType + SyncAssign + SyncOp + SyncFuncLit + SyncCompLit + + SyncDecl + SyncFuncBody + SyncOpenScope + SyncCloseScope + SyncCloseAnotherScope + SyncDeclNames + SyncDeclName + + SyncStmts + SyncBlockStmt + SyncIfStmt + SyncForStmt + SyncSwitchStmt + SyncRangeStmt + SyncCaseClause + SyncCommClause + SyncSelectStmt + SyncDecls + SyncLabeledStmt + SyncUseObjLocal + SyncAddLocal + SyncLinkname + SyncStmt1 + SyncStmtsEnd + SyncLabel + SyncOptLabel + + SyncMultiExpr + SyncRType + SyncConvRTTI +) diff --git a/src/internal/pkgbits/syncmarker_string.go b/src/internal/pkgbits/syncmarker_string.go new file mode 100644 index 00000000000000..4a5b0ca5f2ffcb --- /dev/null +++ b/src/internal/pkgbits/syncmarker_string.go @@ -0,0 +1,89 @@ +// Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT. + +package pkgbits + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SyncEOF-1] + _ = x[SyncBool-2] + _ = x[SyncInt64-3] + _ = x[SyncUint64-4] + _ = x[SyncString-5] + _ = x[SyncValue-6] + _ = x[SyncVal-7] + _ = x[SyncRelocs-8] + _ = x[SyncReloc-9] + _ = x[SyncUseReloc-10] + _ = x[SyncPublic-11] + _ = x[SyncPos-12] + _ = x[SyncPosBase-13] + _ = x[SyncObject-14] + _ = x[SyncObject1-15] + _ = x[SyncPkg-16] + _ = x[SyncPkgDef-17] + _ = x[SyncMethod-18] + _ = x[SyncType-19] + _ = x[SyncTypeIdx-20] + _ = x[SyncTypeParamNames-21] + _ = x[SyncSignature-22] + _ = x[SyncParams-23] + _ = x[SyncParam-24] + _ = x[SyncCodeObj-25] + _ = x[SyncSym-26] + _ = x[SyncLocalIdent-27] + _ = x[SyncSelector-28] + _ = x[SyncPrivate-29] + _ = x[SyncFuncExt-30] + _ = x[SyncVarExt-31] + _ = x[SyncTypeExt-32] + _ = x[SyncPragma-33] + _ = x[SyncExprList-34] + _ = x[SyncExprs-35] + _ = x[SyncExpr-36] + _ = x[SyncExprType-37] + _ = x[SyncAssign-38] + _ = x[SyncOp-39] + _ = x[SyncFuncLit-40] + _ = x[SyncCompLit-41] + _ = x[SyncDecl-42] + _ = x[SyncFuncBody-43] + _ = x[SyncOpenScope-44] + _ = x[SyncCloseScope-45] + _ = x[SyncCloseAnotherScope-46] + _ = x[SyncDeclNames-47] + _ = x[SyncDeclName-48] + _ = x[SyncStmts-49] + _ = x[SyncBlockStmt-50] + _ = x[SyncIfStmt-51] + _ = x[SyncForStmt-52] + _ = x[SyncSwitchStmt-53] + _ = x[SyncRangeStmt-54] + _ = x[SyncCaseClause-55] + _ = x[SyncCommClause-56] + _ = x[SyncSelectStmt-57] + _ = x[SyncDecls-58] + _ = x[SyncLabeledStmt-59] + _ = x[SyncUseObjLocal-60] + _ = x[SyncAddLocal-61] + _ = x[SyncLinkname-62] + _ = x[SyncStmt1-63] + _ = x[SyncStmtsEnd-64] + _ = x[SyncLabel-65] + _ = x[SyncOptLabel-66] +} + +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" + +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} + +func (i SyncMarker) String() string { + i -= 1 + if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) { + return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]] +} diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go index 5b9e5d402068ee..66408e459040b5 100644 --- a/src/internal/poll/copy_file_range_linux.go +++ b/src/internal/poll/copy_file_range_linux.go @@ -6,66 +6,34 @@ package poll import ( "internal/syscall/unix" - "sync/atomic" + "sync" "syscall" ) -var copyFileRangeSupported int32 = -1 // accessed atomically +var ( + kernelVersion53Once sync.Once + kernelVersion53 bool +) const maxCopyFileRangeRound = 1 << 30 -func kernelVersion() (major int, minor int) { - var uname syscall.Utsname - if err := syscall.Uname(&uname); err != nil { - return - } - - rl := uname.Release - var values [2]int - vi := 0 - value := 0 - for _, c := range rl { - if '0' <= c && c <= '9' { - value = (value * 10) + int(c-'0') - } else { - // Note that we're assuming N.N.N here. If we see anything else we are likely to - // mis-parse it. - values[vi] = value - vi++ - if vi >= len(values) { - break - } - value = 0 - } - } - switch vi { - case 0: - return 0, 0 - case 1: - return values[0], 0 - case 2: - return values[0], values[1] - } - return -} - // CopyFileRange copies at most remain bytes of data from src to dst, using // the copy_file_range system call. dst and src must refer to regular files. func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) { - if supported := atomic.LoadInt32(©FileRangeSupported); supported == 0 { - return 0, false, nil - } else if supported == -1 { - major, minor := kernelVersion() + kernelVersion53Once.Do(func() { + major, minor := unix.KernelVersion() + // copy_file_range(2) is broken in various ways on kernels older than 5.3, + // see issue #42400 and + // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS if major > 5 || (major == 5 && minor >= 3) { - atomic.StoreInt32(©FileRangeSupported, 1) - } else { - // copy_file_range(2) is broken in various ways on kernels older than 5.3, - // see issue #42400 and - // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS - atomic.StoreInt32(©FileRangeSupported, 0) - return 0, false, nil + kernelVersion53 = true } + }) + + if !kernelVersion53 { + return 0, false, nil } + for remain > 0 { max := remain if max > maxCopyFileRangeRound { @@ -73,26 +41,18 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err } n, err := copyFileRange(dst, src, int(max)) switch err { - case syscall.ENOSYS: - // copy_file_range(2) was introduced in Linux 4.5. - // Go supports Linux >= 2.6.33, so the system call - // may not be present. - // - // If we see ENOSYS, we have certainly not transferred - // any data, so we can tell the caller that we - // couldn't handle the transfer and let them fall - // back to more generic code. - // - // Seeing ENOSYS also means that we will not try to - // use copy_file_range(2) again. - atomic.StoreInt32(©FileRangeSupported, 0) - return 0, false, nil case syscall.EXDEV, syscall.EINVAL, syscall.EIO, syscall.EOPNOTSUPP, syscall.EPERM: // Prior to Linux 5.3, it was not possible to - // copy_file_range across file systems. Similarly to - // the ENOSYS case above, if we see EXDEV, we have - // not transferred any data, and we can let the caller - // fall back to generic code. + // copy_file_range across file systems. An attempt + // to do this will result in a EXDEV error. + // + // Even though we have checked the kernel version and blocked + // the attempts to copy_file_range(2) when the kernel version + // is older than 5.3, but until now the latest kernel (5.19.x) + // may still return EXDEV error in certain cases. + // + // If we see EXDEV, we have not transferred any data, + // and we can let the caller fall back to generic code. // // As for EINVAL, that is what we see if, for example, // dst or src refer to a pipe rather than a regular diff --git a/src/internal/poll/errno_unix.go b/src/internal/poll/errno_unix.go index 55c548824f8bdd..8eed93a31cb737 100644 --- a/src/internal/poll/errno_unix.go +++ b/src/internal/poll/errno_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package poll diff --git a/src/internal/poll/errno_windows.go b/src/internal/poll/errno_windows.go index c55f5f0df5057d..3679aa8c4c795b 100644 --- a/src/internal/poll/errno_windows.go +++ b/src/internal/poll/errno_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package poll diff --git a/src/internal/poll/error_stub_test.go b/src/internal/poll/error_stub_test.go index bcc25dd666a7b2..48e095254d3863 100644 --- a/src/internal/poll/error_stub_test.go +++ b/src/internal/poll/error_stub_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package poll_test diff --git a/src/internal/poll/export_posix_test.go b/src/internal/poll/export_posix_test.go index f59c1f6dfaba53..3415ab38398179 100644 --- a/src/internal/poll/export_posix_test.go +++ b/src/internal/poll/export_posix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows // Export guts for testing on posix. // Since testing imports os and os imports internal/poll, diff --git a/src/internal/poll/fcntl_js.go b/src/internal/poll/fcntl_js.go index 7bf0ddc792f642..0f42ef61a572be 100644 --- a/src/internal/poll/fcntl_js.go +++ b/src/internal/poll/fcntl_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package poll diff --git a/src/internal/poll/fcntl_libc.go b/src/internal/poll/fcntl_libc.go index cc609e48ad7af9..529b8e123a8a74 100644 --- a/src/internal/poll/fcntl_libc.go +++ b/src/internal/poll/fcntl_libc.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || solaris -// +build aix darwin solaris +//go:build aix || darwin || (openbsd && !mips64) || solaris package poll import _ "unsafe" // for go:linkname // Implemented in the syscall package. +// //go:linkname fcntl syscall.fcntl func fcntl(fd int, cmd int, arg int) (int, error) diff --git a/src/internal/poll/fcntl_syscall.go b/src/internal/poll/fcntl_syscall.go index 8db5b6650492ee..bbfc8a8be50bc7 100644 --- a/src/internal/poll/fcntl_syscall.go +++ b/src/internal/poll/fcntl_syscall.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) package poll diff --git a/src/internal/poll/fd.go b/src/internal/poll/fd.go index 69a90054d3575f..ef61d0cb3ffaa0 100644 --- a/src/internal/poll/fd.go +++ b/src/internal/poll/fd.go @@ -74,6 +74,7 @@ func consume(v *[][]byte, n int64) { return } n -= ln0 + (*v)[0] = nil *v = (*v)[1:] } } diff --git a/src/internal/poll/fd_fsync_posix.go b/src/internal/poll/fd_fsync_posix.go index 651a5ecd8b2d4f..6f17019e73d8f2 100644 --- a/src/internal/poll/fd_fsync_posix.go +++ b/src/internal/poll/fd_fsync_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix dragonfly freebsd js,wasm linux netbsd openbsd solaris package poll diff --git a/src/internal/poll/fd_opendir_darwin.go b/src/internal/poll/fd_opendir_darwin.go index 8eb770c35818aa..3ae2dc8448215e 100644 --- a/src/internal/poll/fd_opendir_darwin.go +++ b/src/internal/poll/fd_opendir_darwin.go @@ -34,5 +34,6 @@ func (fd *FD) OpenDir() (uintptr, string, error) { } // Implemented in syscall/syscall_darwin.go. +// //go:linkname fdopendir syscall.fdopendir func fdopendir(fd int) (dir uintptr, err error) diff --git a/src/internal/poll/fd_plan9.go b/src/internal/poll/fd_plan9.go index 0b5b9375333c20..0fdf4f6d80f6a8 100644 --- a/src/internal/poll/fd_plan9.go +++ b/src/internal/poll/fd_plan9.go @@ -12,12 +12,6 @@ import ( "time" ) -type atomicBool int32 - -func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } -func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } -func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } - type FD struct { // Lock sysfd and serialize access to Read and Write methods. fdmu fdMutex @@ -31,8 +25,8 @@ type FD struct { waio *asyncIO rtimer *time.Timer wtimer *time.Timer - rtimedout atomicBool // set true when read deadline has been reached - wtimedout atomicBool // set true when write deadline has been reached + rtimedout atomic.Bool // set true when read deadline has been reached + wtimedout atomic.Bool // set true when write deadline has been reached // Whether this is a normal file. // On Plan 9 we do not use this package for ordinary files, @@ -70,7 +64,7 @@ func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) { return 0, nil } fd.rmu.Lock() - if fd.rtimedout.isSet() { + if fd.rtimedout.Load() { fd.rmu.Unlock() return 0, ErrDeadlineExceeded } @@ -94,7 +88,7 @@ func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) { } defer fd.writeUnlock() fd.wmu.Lock() - if fd.wtimedout.isSet() { + if fd.wtimedout.Load() { fd.wmu.Unlock() return 0, ErrDeadlineExceeded } @@ -128,12 +122,12 @@ func setDeadlineImpl(fd *FD, t time.Time, mode int) error { if mode == 'r' || mode == 'r'+'w' { fd.rmu.Lock() defer fd.rmu.Unlock() - fd.rtimedout.setFalse() + fd.rtimedout.Store(false) } if mode == 'w' || mode == 'r'+'w' { fd.wmu.Lock() defer fd.wmu.Unlock() - fd.wtimedout.setFalse() + fd.wtimedout.Store(false) } if t.IsZero() || d < 0 { // Stop timer @@ -154,7 +148,7 @@ func setDeadlineImpl(fd *FD, t time.Time, mode int) error { if mode == 'r' || mode == 'r'+'w' { fd.rtimer = time.AfterFunc(d, func() { fd.rmu.Lock() - fd.rtimedout.setTrue() + fd.rtimedout.Store(true) if fd.raio != nil { fd.raio.Cancel() } @@ -164,7 +158,7 @@ func setDeadlineImpl(fd *FD, t time.Time, mode int) error { if mode == 'w' || mode == 'r'+'w' { fd.wtimer = time.AfterFunc(d, func() { fd.wmu.Lock() - fd.wtimedout.setTrue() + fd.wtimedout.Store(true) if fd.waio != nil { fd.waio.Cancel() } @@ -175,13 +169,13 @@ func setDeadlineImpl(fd *FD, t time.Time, mode int) error { if !t.IsZero() && d < 0 { // Interrupt current I/O operation if mode == 'r' || mode == 'r'+'w' { - fd.rtimedout.setTrue() + fd.rtimedout.Store(true) if fd.raio != nil { fd.raio.Cancel() } } if mode == 'w' || mode == 'r'+'w' { - fd.wtimedout.setTrue() + fd.wtimedout.Store(true) if fd.waio != nil { fd.waio.Cancel() } diff --git a/src/internal/poll/fd_poll_js.go b/src/internal/poll/fd_poll_js.go index 760e24802e3fbd..84bfcae633d929 100644 --- a/src/internal/poll/fd_poll_js.go +++ b/src/internal/poll/fd_poll_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package poll diff --git a/src/internal/poll/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go index b072af00ea569e..4d3cc784059611 100644 --- a/src/internal/poll/fd_poll_runtime.go +++ b/src/internal/poll/fd_poll_runtime.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || windows || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd windows solaris +//go:build unix || windows package poll @@ -16,6 +15,7 @@ import ( ) // runtimeNano returns the current value of the runtime clock in nanoseconds. +// //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go index 487f3285ee9895..778fe1e5c1aa75 100644 --- a/src/internal/poll/fd_posix.go +++ b/src/internal/poll/fd_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package poll diff --git a/src/internal/poll/fd_posix_test.go b/src/internal/poll/fd_posix_test.go index 1dcf51d41946aa..b97e46595a5cc1 100644 --- a/src/internal/poll/fd_posix_test.go +++ b/src/internal/poll/fd_posix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package poll_test diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 3b17cd22b03b8f..2786064d9f32dc 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package poll import ( + "internal/syscall/unix" "io" "sync/atomic" "syscall" @@ -230,6 +230,60 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) { } } +// ReadFromInet4 wraps the recvfrom network call for IPv4. +func (fd *FD) ReadFromInet4(p []byte, from *syscall.SockaddrInet4) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, err + } + for { + n, err := unix.RecvfromInet4(fd.Sysfd, p, 0, from) + if err != nil { + if err == syscall.EINTR { + continue + } + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, err + } +} + +// ReadFromInet6 wraps the recvfrom network call for IPv6. +func (fd *FD) ReadFromInet6(p []byte, from *syscall.SockaddrInet6) (int, error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, err + } + for { + n, err := unix.RecvfromInet6(fd.Sysfd, p, 0, from) + if err != nil { + if err == syscall.EINTR { + continue + } + n = 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, err + } +} + // ReadMsg wraps the recvmsg network call. func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.Sockaddr, error) { if err := fd.readLock(); err != nil { @@ -257,6 +311,60 @@ func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.S } } +// ReadMsgInet4 is ReadMsg, but specialized for syscall.SockaddrInet4. +func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, 0, 0, err + } + for { + n, oobn, sysflags, err := unix.RecvmsgInet4(fd.Sysfd, p, oob, flags, sa4) + if err != nil { + if err == syscall.EINTR { + continue + } + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, oobn, sysflags, err + } +} + +// ReadMsgInet6 is ReadMsg, but specialized for syscall.SockaddrInet6. +func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + if err := fd.pd.prepareRead(fd.isFile); err != nil { + return 0, 0, 0, err + } + for { + n, oobn, sysflags, err := unix.RecvmsgInet6(fd.Sysfd, p, oob, flags, sa6) + if err != nil { + if err == syscall.EINTR { + continue + } + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + return n, oobn, sysflags, err + } +} + // Write implements io.Writer. func (fd *FD) Write(p []byte) (int, error) { if err := fd.writeLock(); err != nil { @@ -327,6 +435,58 @@ func (fd *FD) Pwrite(p []byte, off int64) (int, error) { } } +// WriteToInet4 wraps the sendto network call for IPv4 addresses. +func (fd *FD) WriteToInet4(p []byte, sa *syscall.SockaddrInet4) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + for { + err := unix.SendtoInet4(fd.Sysfd, p, 0, sa) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return 0, err + } + return len(p), nil + } +} + +// WriteToInet6 wraps the sendto network call for IPv6 addresses. +func (fd *FD) WriteToInet6(p []byte, sa *syscall.SockaddrInet6) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, err + } + for { + err := unix.SendtoInet6(fd.Sysfd, p, 0, sa) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return 0, err + } + return len(p), nil + } +} + // WriteTo wraps the sendto network call. func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (int, error) { if err := fd.writeLock(); err != nil { @@ -379,6 +539,58 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err } } +// WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4. +func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, 0, err + } + for { + n, err := unix.SendmsgNInet4(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return n, 0, err + } + return n, len(oob), err + } +} + +// WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6. +func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) { + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + if err := fd.pd.prepareWrite(fd.isFile); err != nil { + return 0, 0, err + } + for { + n, err := unix.SendmsgNInet6(fd.Sysfd, p, oob, sa, 0) + if err == syscall.EINTR { + continue + } + if err == syscall.EAGAIN && fd.pd.pollable() { + if err = fd.pd.waitWrite(fd.isFile); err == nil { + continue + } + } + if err != nil { + return n, 0, err + } + return n, len(oob), err + } +} + // Accept wraps the accept network call. func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { if err := fd.readLock(); err != nil { diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index 4a5169527c42b0..1af2011f94d63e 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -500,8 +500,7 @@ func (fd *FD) readConsole(b []byte) (int, error) { } } } - n := utf8.EncodeRune(buf[len(buf):cap(buf)], r) - buf = buf[:len(buf)+n] + buf = utf8.AppendRune(buf, r) } fd.readbyte = buf fd.readbyteOffset = 0 @@ -593,6 +592,64 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) { return n, sa, nil } +// ReadFromInet4 wraps the recvfrom network call for IPv4. +func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { + if len(buf) == 0 { + return 0, nil + } + if len(buf) > maxRW { + buf = buf[:maxRW] + } + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, err + } + rawToSockaddrInet4(o.rsa, sa4) + return n, err +} + +// ReadFromInet6 wraps the recvfrom network call for IPv6. +func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { + if len(buf) == 0 { + return 0, nil + } + if len(buf) > maxRW { + buf = buf[:maxRW] + } + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) + err = fd.eofError(n, err) + if err != nil { + return n, err + } + rawToSockaddrInet6(o.rsa, sa6) + return n, err +} + // Write implements io.Writer. func (fd *FD) Write(buf []byte) (int, error) { if err := fd.writeLock(); err != nil { @@ -791,6 +848,80 @@ func (fd *FD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { return ntotal, nil } +// WriteToInet4 is WriteTo, specialized for syscall.SockaddrInet4. +func (fd *FD) WriteToInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + if len(buf) == 0 { + // handle zero-byte payload + o := &fd.wop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) + }) + return n, err + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + o := &fd.wop + o.InitBuf(b) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet4(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa4, &o.o, nil) + }) + ntotal += int(n) + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + +// WriteToInet6 is WriteTo, specialized for syscall.SockaddrInet6. +func (fd *FD) WriteToInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + + if len(buf) == 0 { + // handle zero-byte payload + o := &fd.wop + o.InitBuf(buf) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) + }) + return n, err + } + + ntotal := 0 + for len(buf) > 0 { + b := buf + if len(b) > maxRW { + b = b[:maxRW] + } + o := &fd.wop + o.InitBuf(b) + n, err := execIO(o, func(o *operation) error { + return windows.WSASendtoInet6(o.fd.Sysfd, &o.buf, 1, &o.qty, 0, sa6, &o.o, nil) + }) + ntotal += int(n) + if err != nil { + return ntotal, err + } + buf = buf[n:] + } + return ntotal, nil +} + // Call ConnectEx. This doesn't need any locking, since it is only // called when the descriptor is first created. This is here rather // than in the net package so that it can use fd.wop. @@ -984,31 +1115,54 @@ func (fd *FD) RawWrite(f func(uintptr) bool) error { return syscall.EWINDOWS } -func sockaddrToRaw(sa syscall.Sockaddr) (unsafe.Pointer, int32, error) { +func sockaddrInet4ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) int32 { + *rsa = syscall.RawSockaddrAny{} + raw := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) + raw.Family = syscall.AF_INET + p := (*[2]byte)(unsafe.Pointer(&raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + raw.Addr = sa.Addr + return int32(unsafe.Sizeof(*raw)) +} + +func sockaddrInet6ToRaw(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) int32 { + *rsa = syscall.RawSockaddrAny{} + raw := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) + raw.Family = syscall.AF_INET6 + p := (*[2]byte)(unsafe.Pointer(&raw.Port)) + p[0] = byte(sa.Port >> 8) + p[1] = byte(sa.Port) + raw.Scope_id = sa.ZoneId + raw.Addr = sa.Addr + return int32(unsafe.Sizeof(*raw)) +} + +func rawToSockaddrInet4(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet4) { + pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.Addr = pp.Addr +} + +func rawToSockaddrInet6(rsa *syscall.RawSockaddrAny, sa *syscall.SockaddrInet6) { + pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) + sa.ZoneId = pp.Scope_id + sa.Addr = pp.Addr +} + +func sockaddrToRaw(rsa *syscall.RawSockaddrAny, sa syscall.Sockaddr) (int32, error) { switch sa := sa.(type) { case *syscall.SockaddrInet4: - var raw syscall.RawSockaddrInet4 - raw.Family = syscall.AF_INET - p := (*[2]byte)(unsafe.Pointer(&raw.Port)) - p[0] = byte(sa.Port >> 8) - p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - raw.Addr[i] = sa.Addr[i] - } - return unsafe.Pointer(&raw), int32(unsafe.Sizeof(raw)), nil + sz := sockaddrInet4ToRaw(rsa, sa) + return sz, nil case *syscall.SockaddrInet6: - var raw syscall.RawSockaddrInet6 - raw.Family = syscall.AF_INET6 - p := (*[2]byte)(unsafe.Pointer(&raw.Port)) - p[0] = byte(sa.Port >> 8) - p[1] = byte(sa.Port) - raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - raw.Addr[i] = sa.Addr[i] - } - return unsafe.Pointer(&raw), int32(unsafe.Sizeof(raw)), nil + sz := sockaddrInet6ToRaw(rsa, sa) + return sz, nil default: - return nil, 0, syscall.EWINDOWS + return 0, syscall.EWINDOWS } } @@ -1025,7 +1179,9 @@ func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.S o := &fd.rop o.InitMsg(p, oob) - o.rsa = new(syscall.RawSockaddrAny) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) o.msg.Flags = uint32(flags) @@ -1040,6 +1196,64 @@ func (fd *FD) ReadMsg(p []byte, oob []byte, flags int) (int, int, int, syscall.S return n, int(o.msg.Control.Len), int(o.msg.Flags), sa, err } +// ReadMsgInet4 is ReadMsg, but specialized to return a syscall.SockaddrInet4. +func (fd *FD) ReadMsgInet4(p []byte, oob []byte, flags int, sa4 *syscall.SockaddrInet4) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + + if len(p) > maxRW { + p = p[:maxRW] + } + + o := &fd.rop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) + o.msg.Flags = uint32(flags) + n, err := execIO(o, func(o *operation) error { + return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) + }) + err = fd.eofError(n, err) + if err == nil { + rawToSockaddrInet4(o.rsa, sa4) + } + return n, int(o.msg.Control.Len), int(o.msg.Flags), err +} + +// ReadMsgInet6 is ReadMsg, but specialized to return a syscall.SockaddrInet6. +func (fd *FD) ReadMsgInet6(p []byte, oob []byte, flags int, sa6 *syscall.SockaddrInet6) (int, int, int, error) { + if err := fd.readLock(); err != nil { + return 0, 0, 0, err + } + defer fd.readUnlock() + + if len(p) > maxRW { + p = p[:maxRW] + } + + o := &fd.rop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa)) + o.msg.Flags = uint32(flags) + n, err := execIO(o, func(o *operation) error { + return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil) + }) + err = fd.eofError(n, err) + if err == nil { + rawToSockaddrInet6(o.rsa, sa6) + } + return n, int(o.msg.Control.Len), int(o.msg.Flags), err +} + // WriteMsg wraps the WSASendMsg network call. func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) { if len(p) > maxRW { @@ -1054,11 +1268,14 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err o := &fd.wop o.InitMsg(p, oob) if sa != nil { - rsa, len, err := sockaddrToRaw(sa) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len, err := sockaddrToRaw(o.rsa, sa) if err != nil { return 0, 0, err } - o.msg.Name = (syscall.Pointer)(rsa) + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) o.msg.Namelen = len } n, err := execIO(o, func(o *operation) error { @@ -1066,3 +1283,53 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err }) return n, int(o.msg.Control.Len), err } + +// WriteMsgInet4 is WriteMsg specialized for syscall.SockaddrInet4. +func (fd *FD) WriteMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (int, int, error) { + if len(p) > maxRW { + return 0, 0, errors.New("packet is too large (only 1GB is allowed)") + } + + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len := sockaddrInet4ToRaw(o.rsa, sa) + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = len + n, err := execIO(o, func(o *operation) error { + return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) + }) + return n, int(o.msg.Control.Len), err +} + +// WriteMsgInet6 is WriteMsg specialized for syscall.SockaddrInet6. +func (fd *FD) WriteMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (int, int, error) { + if len(p) > maxRW { + return 0, 0, errors.New("packet is too large (only 1GB is allowed)") + } + + if err := fd.writeLock(); err != nil { + return 0, 0, err + } + defer fd.writeUnlock() + + o := &fd.wop + o.InitMsg(p, oob) + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + len := sockaddrInet6ToRaw(o.rsa, sa) + o.msg.Name = (syscall.Pointer)(unsafe.Pointer(o.rsa)) + o.msg.Namelen = len + n, err := execIO(o, func(o *operation) error { + return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil) + }) + return n, int(o.msg.Control.Len), err +} diff --git a/src/internal/poll/fd_writev_darwin.go b/src/internal/poll/fd_writev_darwin.go index 805fa2ccd9ae79..b5b8998df8cae1 100644 --- a/src/internal/poll/fd_writev_darwin.go +++ b/src/internal/poll/fd_writev_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package poll @@ -13,5 +12,6 @@ import ( ) // Implemented in syscall/syscall_darwin.go. +// //go:linkname writev syscall.writev func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) diff --git a/src/internal/poll/fd_writev_illumos.go b/src/internal/poll/fd_writev_illumos.go deleted file mode 100644 index a0b11ed5ae972c..00000000000000 --- a/src/internal/poll/fd_writev_illumos.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build illumos -// +build illumos - -package poll - -import ( - "internal/syscall/unix" - "syscall" -) - -func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) { - return unix.Writev(fd, iovecs) -} diff --git a/src/internal/poll/fd_writev_solaris.go b/src/internal/poll/fd_writev_solaris.go new file mode 100644 index 00000000000000..d20f20114e1616 --- /dev/null +++ b/src/internal/poll/fd_writev_solaris.go @@ -0,0 +1,14 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package poll + +import ( + "internal/syscall/unix" + "syscall" +) + +func writev(fd int, iovecs []syscall.Iovec) (uintptr, error) { + return unix.Writev(fd, iovecs) +} diff --git a/src/internal/poll/fd_writev_unix.go b/src/internal/poll/fd_writev_unix.go index 87f284a56a1b21..aa96d104c89bf1 100644 --- a/src/internal/poll/fd_writev_unix.go +++ b/src/internal/poll/fd_writev_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd package poll diff --git a/src/internal/poll/hook_cloexec.go b/src/internal/poll/hook_cloexec.go index d519f602c1e985..5b3cdcec283cb2 100644 --- a/src/internal/poll/hook_cloexec.go +++ b/src/internal/poll/hook_cloexec.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build dragonfly freebsd illumos linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris package poll diff --git a/src/internal/poll/hook_unix.go b/src/internal/poll/hook_unix.go index c88d65cdc83de5..1a5035675d8c78 100644 --- a/src/internal/poll/hook_unix.go +++ b/src/internal/poll/hook_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package poll diff --git a/src/internal/poll/iovec_illumos.go b/src/internal/poll/iovec_illumos.go deleted file mode 100644 index f4058b298f35c5..00000000000000 --- a/src/internal/poll/iovec_illumos.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build illumos -// +build illumos - -package poll - -import ( - "syscall" - "unsafe" -) - -func newIovecWithBase(base *byte) syscall.Iovec { - return syscall.Iovec{Base: (*int8)(unsafe.Pointer(base))} -} diff --git a/src/internal/poll/iovec_solaris.go b/src/internal/poll/iovec_solaris.go new file mode 100644 index 00000000000000..e68f833d2d5e02 --- /dev/null +++ b/src/internal/poll/iovec_solaris.go @@ -0,0 +1,14 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package poll + +import ( + "syscall" + "unsafe" +) + +func newIovecWithBase(base *byte) syscall.Iovec { + return syscall.Iovec{Base: (*int8)(unsafe.Pointer(base))} +} diff --git a/src/internal/poll/iovec_unix.go b/src/internal/poll/iovec_unix.go index 6fd5d86630b6aa..c1500840ac243e 100644 --- a/src/internal/poll/iovec_unix.go +++ b/src/internal/poll/iovec_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd package poll diff --git a/src/internal/poll/sendfile_bsd.go b/src/internal/poll/sendfile_bsd.go index 3ba30a2154cd54..89315a8c67352e 100644 --- a/src/internal/poll/sendfile_bsd.go +++ b/src/internal/poll/sendfile_bsd.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd -// +build dragonfly freebsd +//go:build darwin || dragonfly || freebsd package poll diff --git a/src/internal/poll/sendfile_solaris.go b/src/internal/poll/sendfile_solaris.go index 0a884307bb15bf..7ae18f4b1a661c 100644 --- a/src/internal/poll/sendfile_solaris.go +++ b/src/internal/poll/sendfile_solaris.go @@ -7,6 +7,7 @@ package poll import "syscall" // Not strictly needed, but very helpful for debugging, see issue #10221. +// //go:cgo_import_dynamic _ _ "libsendfile.so" //go:cgo_import_dynamic _ _ "libsocket.so" diff --git a/src/internal/poll/sock_cloexec.go b/src/internal/poll/sock_cloexec.go index b3038290b92168..4fb9f004bbfe4a 100644 --- a/src/internal/poll/sock_cloexec.go +++ b/src/internal/poll/sock_cloexec.go @@ -5,8 +5,7 @@ // This file implements accept for platforms that provide a fast path for // setting SetNonblock and CloseOnExec. -//go:build dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build dragonfly freebsd illumos linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris package poll @@ -16,36 +15,8 @@ import "syscall" // descriptor as nonblocking and close-on-exec. func accept(s int) (int, syscall.Sockaddr, string, error) { ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC) - // On Linux the accept4 system call was introduced in 2.6.28 - // kernel and on FreeBSD it was introduced in 10 kernel. If we - // get an ENOSYS error on both Linux and FreeBSD, or EINVAL - // error on Linux, fall back to using accept. - switch err { - case nil: - return ns, sa, "", nil - default: // errors other than the ones listed - return -1, sa, "accept4", err - case syscall.ENOSYS: // syscall missing - case syscall.EINVAL: // some Linux use this instead of ENOSYS - case syscall.EACCES: // some Linux use this instead of ENOSYS - case syscall.EFAULT: // some Linux use this instead of ENOSYS - } - - // See ../syscall/exec_unix.go for description of ForkLock. - // It is probably okay to hold the lock across syscall.Accept - // because we have put fd.sysfd into non-blocking mode. - // However, a call to the File method will put it back into - // blocking mode. We can't take that risk, so no use of ForkLock here. - ns, sa, err = AcceptFunc(s) - if err == nil { - syscall.CloseOnExec(ns) - } if err != nil { - return -1, nil, "accept", err - } - if err = syscall.SetNonblock(ns, true); err != nil { - CloseFunc(ns) - return -1, nil, "setnonblock", err + return -1, sa, "accept4", err } return ns, sa, "", nil } diff --git a/src/internal/poll/sockopt.go b/src/internal/poll/sockopt.go index 4f2e2fb455dc9b..a7c9d115b4ace6 100644 --- a/src/internal/poll/sockopt.go +++ b/src/internal/poll/sockopt.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package poll diff --git a/src/internal/poll/sockopt_unix.go b/src/internal/poll/sockopt_unix.go index 4fb9600deedff1..9cba44da9d1398 100644 --- a/src/internal/poll/sockopt_unix.go +++ b/src/internal/poll/sockopt_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package poll diff --git a/src/internal/poll/sockoptip.go b/src/internal/poll/sockoptip.go index d86c4c1f81ad4c..41955e1fda0403 100644 --- a/src/internal/poll/sockoptip.go +++ b/src/internal/poll/sockoptip.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package poll diff --git a/src/internal/poll/splice_linux.go b/src/internal/poll/splice_linux.go index 8062d98fae945c..96cbe4a3122704 100644 --- a/src/internal/poll/splice_linux.go +++ b/src/internal/poll/splice_linux.go @@ -5,10 +5,8 @@ package poll import ( - "internal/syscall/unix" "runtime" "sync" - "sync/atomic" "syscall" "unsafe" ) @@ -19,7 +17,9 @@ const ( // maxSpliceSize is the maximum amount of data Splice asks // the kernel to move in a single call to splice(2). - maxSpliceSize = 4 << 20 + // We use 1MB as Splice writes data through a pipe, and 1MB is the default maximum pipe buffer size, + // which is determined by /proc/sys/fs/pipe-max-size. + maxSpliceSize = 1 << 20 ) // Splice transfers at most remain bytes of data from src to dst, using the @@ -154,18 +154,26 @@ func splice(out int, in int, max int, flags int) (int, error) { return int(n), err } -type splicePipe struct { +type splicePipeFields struct { rfd int wfd int data int } +type splicePipe struct { + splicePipeFields + + // We want to use a finalizer, so ensure that the size is + // large enough to not use the tiny allocator. + _ [24 - unsafe.Sizeof(splicePipeFields{})%24]byte +} + // splicePipePool caches pipes to avoid high-frequency construction and destruction of pipe buffers. // The garbage collector will free all pipes in the sync.Pool periodically, thus we need to set up // a finalizer for each pipe to close its file descriptors before the actual GC. var splicePipePool = sync.Pool{New: newPoolPipe} -func newPoolPipe() interface{} { +func newPoolPipe() any { // Discard the error which occurred during the creation of pipe buffer, // redirecting the data transmission to the conventional way utilizing read() + write() as a fallback. p := newPipe() @@ -199,40 +207,21 @@ func putPipe(p *splicePipe) { splicePipePool.Put(p) } -var disableSplice unsafe.Pointer - // newPipe sets up a pipe for a splice operation. -func newPipe() (sp *splicePipe) { - p := (*bool)(atomic.LoadPointer(&disableSplice)) - if p != nil && *p { - return nil - } - +func newPipe() *splicePipe { var fds [2]int - // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it - // might not be implemented. Falling back to pipe is possible, but prior to - // 2.6.29 splice returns -EAGAIN instead of 0 when the connection is - // closed. - const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK - if err := syscall.Pipe2(fds[:], flags); err != nil { + if err := syscall.Pipe2(fds[:], syscall.O_CLOEXEC|syscall.O_NONBLOCK); err != nil { return nil } - sp = &splicePipe{rfd: fds[0], wfd: fds[1]} - - if p == nil { - p = new(bool) - defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p)) - - // F_GETPIPE_SZ was added in 2.6.35, which does not have the -EAGAIN bug. - if _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 { - *p = true - destroyPipe(sp) - return nil - } - } + // Splice will loop writing maxSpliceSize bytes from the source to the pipe, + // and then write those bytes from the pipe to the destination. + // Set the pipe buffer size to maxSpliceSize to optimize that. + // Ignore errors here, as a smaller buffer size will work, + // although it will require more system calls. + fcntl(fds[0], syscall.F_SETPIPE_SZ, maxSpliceSize) - return + return &splicePipe{splicePipeFields: splicePipeFields{rfd: fds[0], wfd: fds[1]}} } // destroyPipe destroys a pipe. diff --git a/src/internal/poll/splice_linux_test.go b/src/internal/poll/splice_linux_test.go index 280468c7e748d8..29bcaab4140f7c 100644 --- a/src/internal/poll/splice_linux_test.go +++ b/src/internal/poll/splice_linux_test.go @@ -6,40 +6,48 @@ package poll_test import ( "internal/poll" - "internal/syscall/unix" "runtime" - "syscall" + "sync" + "sync/atomic" "testing" "time" ) -// checkPipes returns true if all pipes are closed properly, false otherwise. -func checkPipes(fds []int) bool { - for _, fd := range fds { - // Check if each pipe fd has been closed. - _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0) - if errno == 0 { - return false +var closeHook atomic.Value // func(fd int) + +func init() { + closeFunc := poll.CloseFunc + poll.CloseFunc = func(fd int) (err error) { + if v := closeHook.Load(); v != nil { + if hook := v.(func(int)); hook != nil { + hook(fd) + } } + return closeFunc(fd) } - return true } func TestSplicePipePool(t *testing.T) { const N = 64 var ( - p *poll.SplicePipe - ps []*poll.SplicePipe - fds []int - err error + p *poll.SplicePipe + ps []*poll.SplicePipe + allFDs []int + pendingFDs sync.Map // fd → struct{}{} + err error ) + + closeHook.Store(func(fd int) { pendingFDs.Delete(fd) }) + t.Cleanup(func() { closeHook.Store((func(int))(nil)) }) + for i := 0; i < N; i++ { p, _, err = poll.GetPipe() if err != nil { - t.Skip("failed to create pipe, skip this test") + t.Skipf("failed to create pipe due to error(%v), skip this test", err) } _, pwfd := poll.GetPipeFds(p) - fds = append(fds, pwfd) + allFDs = append(allFDs, pwfd) + pendingFDs.Store(pwfd, struct{}{}) ps = append(ps, p) } for _, p = range ps { @@ -62,12 +70,21 @@ func TestSplicePipePool(t *testing.T) { for { runtime.GC() time.Sleep(10 * time.Millisecond) - if checkPipes(fds) { + + // Detect whether all pipes are closed properly. + var leakedFDs []int + pendingFDs.Range(func(k, v any) bool { + leakedFDs = append(leakedFDs, k.(int)) + return true + }) + if len(leakedFDs) == 0 { break } + select { case <-expiredTime.C: - t.Fatal("at least one pipe is still open") + t.Logf("all descriptors: %v", allFDs) + t.Fatalf("leaked descriptors: %v", leakedFDs) default: } } diff --git a/src/internal/poll/strconv.go b/src/internal/poll/strconv.go index c98332d3dac637..2b052fa1747f3a 100644 --- a/src/internal/poll/strconv.go +++ b/src/internal/poll/strconv.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package poll diff --git a/src/internal/poll/sys_cloexec.go b/src/internal/poll/sys_cloexec.go index 7e6d422d621cda..7cd80019f4064a 100644 --- a/src/internal/poll/sys_cloexec.go +++ b/src/internal/poll/sys_cloexec.go @@ -5,8 +5,7 @@ // This file implements accept for platforms that do not provide a fast path for // setting SetNonblock and CloseOnExec. -//go:build aix || darwin || (js && wasm) || (solaris && !illumos) -// +build aix darwin js,wasm solaris,!illumos +//go:build aix || darwin || (js && wasm) package poll diff --git a/src/internal/poll/writev.go b/src/internal/poll/writev.go index 824de7569e8a2c..4086c705fdfa2b 100644 --- a/src/internal/poll/writev.go +++ b/src/internal/poll/writev.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build darwin dragonfly freebsd illumos linux netbsd openbsd +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris package poll import ( "io" + "runtime" "syscall" ) @@ -30,6 +30,10 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) { // 1024 and this seems conservative enough for now. Darwin's // UIO_MAXIOV also seems to be 1024. maxVec := 1024 + if runtime.GOOS == "solaris" { + // IOV_MAX is set to XOPEN_IOV_MAX on Solaris. + maxVec = 16 + } var n int64 var err error diff --git a/src/internal/profile/encode.go b/src/internal/profile/encode.go index af319330d9ad12..77d77f1dfb5c34 100644 --- a/src/internal/profile/encode.go +++ b/src/internal/profile/encode.go @@ -175,7 +175,7 @@ var profileDecoder = []decoder{ if err != nil { return err } - if *&m.(*Profile).stringTable[0] != "" { + if m.(*Profile).stringTable[0] != "" { return errors.New("string_table[0] must be ''") } return nil diff --git a/src/internal/profile/graph.go b/src/internal/profile/graph.go new file mode 100644 index 00000000000000..4fcd08172cb00e --- /dev/null +++ b/src/internal/profile/graph.go @@ -0,0 +1,1173 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package graph collects a set of samples into a directed graph. + +// Original file location: https://github.com/google/pprof/tree/main/internal/graph/graph.go +package profile + +import ( + "fmt" + "math" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" +) + +var ( + // Removes package name and method arguments for Java method names. + // See tests for examples. + javaRegExp = regexp.MustCompile(`^(?:[a-z]\w*\.)*([A-Z][\w\$]*\.(?:|[a-z][\w\$]*(?:\$\d+)?))(?:(?:\()|$)`) + // Removes package name and method arguments for Go function names. + // See tests for examples. + goRegExp = regexp.MustCompile(`^(?:[\w\-\.]+\/)+(.+)`) + // Removes potential module versions in a package path. + goVerRegExp = regexp.MustCompile(`^(.*?)/v(?:[2-9]|[1-9][0-9]+)([./].*)$`) + // Strips C++ namespace prefix from a C++ function / method name. + // NOTE: Make sure to keep the template parameters in the name. Normally, + // template parameters are stripped from the C++ names but when + // -symbolize=demangle=templates flag is used, they will not be. + // See tests for examples. + cppRegExp = regexp.MustCompile(`^(?:[_a-zA-Z]\w*::)+(_*[A-Z]\w*::~?[_a-zA-Z]\w*(?:<.*>)?)`) + cppAnonymousPrefixRegExp = regexp.MustCompile(`^\(anonymous namespace\)::`) +) + +// This was obtained from https://github.com/google/pprof/blob/main/internal/graph/dotgraph.go. +const maxNodelets = 4 // Number of nodelets for labels (both numeric and non) + +// Options encodes the options for constructing a graph +type Options struct { + SampleValue func(s []int64) int64 // Function to compute the value of a sample + SampleMeanDivisor func(s []int64) int64 // Function to compute the divisor for mean graphs, or nil + FormatTag func(int64, string) string // Function to format a sample tag value into a string + ObjNames bool // Always preserve obj filename + OrigFnNames bool // Preserve original (eg mangled) function names + + CallTree bool // Build a tree instead of a graph + DropNegative bool // Drop nodes with overall negative values + + KeptNodes NodeSet // If non-nil, only use nodes in this set +} + +// Nodes is an ordered collection of graph nodes. +type Nodes []*Node + +// Node is an entry on a profiling report. It represents a unique +// program location. +type Node struct { + // Info describes the source location associated to this node. + Info NodeInfo + + // Function represents the function that this node belongs to. On + // graphs with sub-function resolution (eg line number or + // addresses), two nodes in a NodeMap that are part of the same + // function have the same value of Node.Function. If the Node + // represents the whole function, it points back to itself. + Function *Node + + // Values associated to this node. Flat is exclusive to this node, + // Cum includes all descendents. + Flat, FlatDiv, Cum, CumDiv int64 + + // In and out Contains the nodes immediately reaching or reached by + // this node. + In, Out EdgeMap + + // LabelTags provide additional information about subsets of a sample. + LabelTags TagMap + + // NumericTags provide additional values for subsets of a sample. + // Numeric tags are optionally associated to a label tag. The key + // for NumericTags is the name of the LabelTag they are associated + // to, or "" for numeric tags not associated to a label tag. + NumericTags map[string]TagMap +} + +// Graph summarizes a performance profile into a format that is +// suitable for visualization. +type Graph struct { + Nodes Nodes +} + +// FlatValue returns the exclusive value for this node, computing the +// mean if a divisor is available. +func (n *Node) FlatValue() int64 { + if n.FlatDiv == 0 { + return n.Flat + } + return n.Flat / n.FlatDiv +} + +// CumValue returns the inclusive value for this node, computing the +// mean if a divisor is available. +func (n *Node) CumValue() int64 { + if n.CumDiv == 0 { + return n.Cum + } + return n.Cum / n.CumDiv +} + +// AddToEdge increases the weight of an edge between two nodes. If +// there isn't such an edge one is created. +func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) { + n.AddToEdgeDiv(to, 0, v, residual, inline) +} + +// AddToEdgeDiv increases the weight of an edge between two nodes. If +// there isn't such an edge one is created. +func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bool) { + if n.Out[to] != to.In[n] { + panic(fmt.Errorf("asymmetric edges %v %v", *n, *to)) + } + + if e := n.Out[to]; e != nil { + e.WeightDiv += dv + e.Weight += v + if residual { + e.Residual = true + } + if !inline { + e.Inline = false + } + return + } + + info := &Edge{Src: n, Dest: to, WeightDiv: dv, Weight: v, Residual: residual, Inline: inline} + n.Out[to] = info + to.In[n] = info +} + +// NodeInfo contains the attributes for a node. +type NodeInfo struct { + Name string + OrigName string + Address uint64 + File string + StartLine, Lineno int + Objfile string +} + +// PrintableName calls the Node's Formatter function with a single space separator. +func (i *NodeInfo) PrintableName() string { + return strings.Join(i.NameComponents(), " ") +} + +// NameComponents returns the components of the printable name to be used for a node. +func (i *NodeInfo) NameComponents() []string { + var name []string + if i.Address != 0 { + name = append(name, fmt.Sprintf("%016x", i.Address)) + } + if fun := i.Name; fun != "" { + name = append(name, fun) + } + + switch { + case i.Lineno != 0: + // User requested line numbers, provide what we have. + name = append(name, fmt.Sprintf("%s:%d", i.File, i.Lineno)) + case i.File != "": + // User requested file name, provide it. + name = append(name, i.File) + case i.Name != "": + // User requested function name. It was already included. + case i.Objfile != "": + // Only binary name is available + name = append(name, "["+filepath.Base(i.Objfile)+"]") + default: + // Do not leave it empty if there is no information at all. + name = append(name, "") + } + return name +} + +// NodeMap maps from a node info struct to a node. It is used to merge +// report entries with the same info. +type NodeMap map[NodeInfo]*Node + +// NodeSet is a collection of node info structs. +type NodeSet map[NodeInfo]bool + +// NodePtrSet is a collection of nodes. Trimming a graph or tree requires a set +// of objects which uniquely identify the nodes to keep. In a graph, NodeInfo +// works as a unique identifier; however, in a tree multiple nodes may share +// identical NodeInfos. A *Node does uniquely identify a node so we can use that +// instead. Though a *Node also uniquely identifies a node in a graph, +// currently, during trimming, graphs are rebuilt from scratch using only the +// NodeSet, so there would not be the required context of the initial graph to +// allow for the use of *Node. +type NodePtrSet map[*Node]bool + +// FindOrInsertNode takes the info for a node and either returns a matching node +// from the node map if one exists, or adds one to the map if one does not. +// If kept is non-nil, nodes are only added if they can be located on it. +func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node { + if kept != nil { + if _, ok := kept[info]; !ok { + return nil + } + } + + if n, ok := nm[info]; ok { + return n + } + + n := &Node{ + Info: info, + In: make(EdgeMap), + Out: make(EdgeMap), + LabelTags: make(TagMap), + NumericTags: make(map[string]TagMap), + } + nm[info] = n + if info.Address == 0 && info.Lineno == 0 { + // This node represents the whole function, so point Function + // back to itself. + n.Function = n + return n + } + // Find a node that represents the whole function. + info.Address = 0 + info.Lineno = 0 + n.Function = nm.FindOrInsertNode(info, nil) + return n +} + +// EdgeMap is used to represent the incoming/outgoing edges from a node. +type EdgeMap map[*Node]*Edge + +// Edge contains any attributes to be represented about edges in a graph. +type Edge struct { + Src, Dest *Node + // The summary weight of the edge + Weight, WeightDiv int64 + + // residual edges connect nodes that were connected through a + // separate node, which has been removed from the report. + Residual bool + // An inline edge represents a call that was inlined into the caller. + Inline bool +} + +// WeightValue returns the weight value for this edge, normalizing if a +// divisor is available. +func (e *Edge) WeightValue() int64 { + if e.WeightDiv == 0 { + return e.Weight + } + return e.Weight / e.WeightDiv +} + +// Tag represent sample annotations +type Tag struct { + Name string + Unit string // Describe the value, "" for non-numeric tags + Value int64 + Flat, FlatDiv int64 + Cum, CumDiv int64 +} + +// FlatValue returns the exclusive value for this tag, computing the +// mean if a divisor is available. +func (t *Tag) FlatValue() int64 { + if t.FlatDiv == 0 { + return t.Flat + } + return t.Flat / t.FlatDiv +} + +// CumValue returns the inclusive value for this tag, computing the +// mean if a divisor is available. +func (t *Tag) CumValue() int64 { + if t.CumDiv == 0 { + return t.Cum + } + return t.Cum / t.CumDiv +} + +// TagMap is a collection of tags, classified by their name. +type TagMap map[string]*Tag + +// SortTags sorts a slice of tags based on their weight. +func SortTags(t []*Tag, flat bool) []*Tag { + ts := tags{t, flat} + sort.Sort(ts) + return ts.t +} + +// New summarizes performance data from a profile into a graph. +func New(prof *Profile, o *Options) *Graph { + if o.CallTree { + return newTree(prof, o) + } + g, _ := newGraph(prof, o) + return g +} + +// newGraph computes a graph from a profile. It returns the graph, and +// a map from the profile location indices to the corresponding graph +// nodes. +func newGraph(prof *Profile, o *Options) (*Graph, map[uint64]Nodes) { + nodes, locationMap := CreateNodes(prof, o) + seenNode := make(map[*Node]bool) + seenEdge := make(map[nodePair]bool) + for _, sample := range prof.Sample { + var w, dw int64 + w = o.SampleValue(sample.Value) + if o.SampleMeanDivisor != nil { + dw = o.SampleMeanDivisor(sample.Value) + } + if dw == 0 && w == 0 { + continue + } + for k := range seenNode { + delete(seenNode, k) + } + for k := range seenEdge { + delete(seenEdge, k) + } + var parent *Node + // A residual edge goes over one or more nodes that were not kept. + residual := false + + labels := joinLabels(sample) + // Group the sample frames, based on a global map. + for i := len(sample.Location) - 1; i >= 0; i-- { + l := sample.Location[i] + locNodes := locationMap[l.ID] + for ni := len(locNodes) - 1; ni >= 0; ni-- { + n := locNodes[ni] + if n == nil { + residual = true + continue + } + // Add cum weight to all nodes in stack, avoiding double counting. + if _, ok := seenNode[n]; !ok { + seenNode[n] = true + n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false) + } + // Update edge weights for all edges in stack, avoiding double counting. + if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent { + seenEdge[nodePair{n, parent}] = true + parent.AddToEdgeDiv(n, dw, w, residual, ni != len(locNodes)-1) + } + parent = n + residual = false + } + } + if parent != nil && !residual { + // Add flat weight to leaf node. + parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true) + } + } + + return selectNodesForGraph(nodes, o.DropNegative), locationMap +} + +func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph { + // Collect nodes into a graph. + gNodes := make(Nodes, 0, len(nodes)) + for _, n := range nodes { + if n == nil { + continue + } + if n.Cum == 0 && n.Flat == 0 { + continue + } + if dropNegative && isNegative(n) { + continue + } + gNodes = append(gNodes, n) + } + return &Graph{gNodes} +} + +type nodePair struct { + src, dest *Node +} + +func newTree(prof *Profile, o *Options) (g *Graph) { + parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample)) + for _, sample := range prof.Sample { + var w, dw int64 + w = o.SampleValue(sample.Value) + if o.SampleMeanDivisor != nil { + dw = o.SampleMeanDivisor(sample.Value) + } + if dw == 0 && w == 0 { + continue + } + var parent *Node + labels := joinLabels(sample) + // Group the sample frames, based on a per-node map. + for i := len(sample.Location) - 1; i >= 0; i-- { + l := sample.Location[i] + lines := l.Line + if len(lines) == 0 { + lines = []Line{{}} // Create empty line to include location info. + } + for lidx := len(lines) - 1; lidx >= 0; lidx-- { + nodeMap := parentNodeMap[parent] + if nodeMap == nil { + nodeMap = make(NodeMap) + parentNodeMap[parent] = nodeMap + } + n := nodeMap.findOrInsertLine(l, lines[lidx], o) + if n == nil { + continue + } + n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false) + if parent != nil { + parent.AddToEdgeDiv(n, dw, w, false, lidx != len(lines)-1) + } + parent = n + } + } + if parent != nil { + parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true) + } + } + + nodes := make(Nodes, len(prof.Location)) + for _, nm := range parentNodeMap { + nodes = append(nodes, nm.nodes()...) + } + return selectNodesForGraph(nodes, o.DropNegative) +} + +// ShortenFunctionName returns a shortened version of a function's name. +func ShortenFunctionName(f string) string { + f = cppAnonymousPrefixRegExp.ReplaceAllString(f, "") + f = goVerRegExp.ReplaceAllString(f, `${1}${2}`) + for _, re := range []*regexp.Regexp{goRegExp, javaRegExp, cppRegExp} { + if matches := re.FindStringSubmatch(f); len(matches) >= 2 { + return strings.Join(matches[1:], "") + } + } + return f +} + +// TrimTree trims a Graph in forest form, keeping only the nodes in kept. This +// will not work correctly if even a single node has multiple parents. +func (g *Graph) TrimTree(kept NodePtrSet) { + // Creates a new list of nodes + oldNodes := g.Nodes + g.Nodes = make(Nodes, 0, len(kept)) + + for _, cur := range oldNodes { + // A node may not have multiple parents + if len(cur.In) > 1 { + panic("TrimTree only works on trees") + } + + // If a node should be kept, add it to the new list of nodes + if _, ok := kept[cur]; ok { + g.Nodes = append(g.Nodes, cur) + continue + } + + // If a node has no parents, then delete all of the in edges of its + // children to make them each roots of their own trees. + if len(cur.In) == 0 { + for _, outEdge := range cur.Out { + delete(outEdge.Dest.In, cur) + } + continue + } + + // Get the parent. This works since at this point cur.In must contain only + // one element. + if len(cur.In) != 1 { + panic("Get parent assertion failed. cur.In expected to be of length 1.") + } + var parent *Node + for _, edge := range cur.In { + parent = edge.Src + } + + parentEdgeInline := parent.Out[cur].Inline + + // Remove the edge from the parent to this node + delete(parent.Out, cur) + + // Reconfigure every edge from the current node to now begin at the parent. + for _, outEdge := range cur.Out { + child := outEdge.Dest + + delete(child.In, cur) + child.In[parent] = outEdge + parent.Out[child] = outEdge + + outEdge.Src = parent + outEdge.Residual = true + // If the edge from the parent to the current node and the edge from the + // current node to the child are both inline, then this resulting residual + // edge should also be inline + outEdge.Inline = parentEdgeInline && outEdge.Inline + } + } + g.RemoveRedundantEdges() +} + +func joinLabels(s *Sample) string { + if len(s.Label) == 0 { + return "" + } + + var labels []string + for key, vals := range s.Label { + for _, v := range vals { + labels = append(labels, key+":"+v) + } + } + sort.Strings(labels) + return strings.Join(labels, `\n`) +} + +// isNegative returns true if the node is considered as "negative" for the +// purposes of drop_negative. +func isNegative(n *Node) bool { + switch { + case n.Flat < 0: + return true + case n.Flat == 0 && n.Cum < 0: + return true + default: + return false + } +} + +// CreateNodes creates graph nodes for all locations in a profile. It +// returns set of all nodes, plus a mapping of each location to the +// set of corresponding nodes (one per location.Line). +func CreateNodes(prof *Profile, o *Options) (Nodes, map[uint64]Nodes) { + locations := make(map[uint64]Nodes, len(prof.Location)) + nm := make(NodeMap, len(prof.Location)) + for _, l := range prof.Location { + lines := l.Line + if len(lines) == 0 { + lines = []Line{{}} // Create empty line to include location info. + } + nodes := make(Nodes, len(lines)) + for ln := range lines { + nodes[ln] = nm.findOrInsertLine(l, lines[ln], o) + } + locations[l.ID] = nodes + } + return nm.nodes(), locations +} + +func (nm NodeMap) nodes() Nodes { + nodes := make(Nodes, 0, len(nm)) + for _, n := range nm { + nodes = append(nodes, n) + } + return nodes +} + +func (nm NodeMap) findOrInsertLine(l *Location, li Line, o *Options) *Node { + var objfile string + if m := l.Mapping; m != nil && m.File != "" { + objfile = m.File + } + + if ni := nodeInfo(l, li, objfile, o); ni != nil { + return nm.FindOrInsertNode(*ni, o.KeptNodes) + } + return nil +} + +func nodeInfo(l *Location, line Line, objfile string, o *Options) *NodeInfo { + if line.Function == nil { + return &NodeInfo{Address: l.Address, Objfile: objfile} + } + ni := &NodeInfo{ + Address: l.Address, + Lineno: int(line.Line), + Name: line.Function.Name, + } + if fname := line.Function.Filename; fname != "" { + ni.File = filepath.Clean(fname) + } + if o.OrigFnNames { + ni.OrigName = line.Function.SystemName + } + if o.ObjNames || (ni.Name == "" && ni.OrigName == "") { + ni.Objfile = objfile + ni.StartLine = int(line.Function.StartLine) + } + return ni +} + +type tags struct { + t []*Tag + flat bool +} + +func (t tags) Len() int { return len(t.t) } +func (t tags) Swap(i, j int) { t.t[i], t.t[j] = t.t[j], t.t[i] } +func (t tags) Less(i, j int) bool { + if !t.flat { + if t.t[i].Cum != t.t[j].Cum { + return abs64(t.t[i].Cum) > abs64(t.t[j].Cum) + } + } + if t.t[i].Flat != t.t[j].Flat { + return abs64(t.t[i].Flat) > abs64(t.t[j].Flat) + } + return t.t[i].Name < t.t[j].Name +} + +// Sum adds the flat and cum values of a set of nodes. +func (ns Nodes) Sum() (flat int64, cum int64) { + for _, n := range ns { + flat += n.Flat + cum += n.Cum + } + return +} + +func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, numUnit map[string][]string, format func(int64, string) string, flat bool) { + // Update sample value + if flat { + n.FlatDiv += dw + n.Flat += w + } else { + n.CumDiv += dw + n.Cum += w + } + + // Add string tags + if labels != "" { + t := n.LabelTags.findOrAddTag(labels, "", 0) + if flat { + t.FlatDiv += dw + t.Flat += w + } else { + t.CumDiv += dw + t.Cum += w + } + } + + numericTags := n.NumericTags[labels] + if numericTags == nil { + numericTags = TagMap{} + n.NumericTags[labels] = numericTags + } + // Add numeric tags + if format == nil { + format = defaultLabelFormat + } + for k, nvals := range numLabel { + units := numUnit[k] + for i, v := range nvals { + var t *Tag + if len(units) > 0 { + t = numericTags.findOrAddTag(format(v, units[i]), units[i], v) + } else { + t = numericTags.findOrAddTag(format(v, k), k, v) + } + if flat { + t.FlatDiv += dw + t.Flat += w + } else { + t.CumDiv += dw + t.Cum += w + } + } + } +} + +func defaultLabelFormat(v int64, key string) string { + return strconv.FormatInt(v, 10) +} + +func (m TagMap) findOrAddTag(label, unit string, value int64) *Tag { + l := m[label] + if l == nil { + l = &Tag{ + Name: label, + Unit: unit, + Value: value, + } + m[label] = l + } + return l +} + +// String returns a text representation of a graph, for debugging purposes. +func (g *Graph) String() string { + var s []string + + nodeIndex := make(map[*Node]int, len(g.Nodes)) + + for i, n := range g.Nodes { + nodeIndex[n] = i + 1 + } + + for i, n := range g.Nodes { + name := n.Info.PrintableName() + var in, out []int + + for _, from := range n.In { + in = append(in, nodeIndex[from.Src]) + } + for _, to := range n.Out { + out = append(out, nodeIndex[to.Dest]) + } + s = append(s, fmt.Sprintf("%d: %s[flat=%d cum=%d] %x -> %v ", i+1, name, n.Flat, n.Cum, in, out)) + } + return strings.Join(s, "\n") +} + +// DiscardLowFrequencyNodes returns a set of the nodes at or over a +// specific cum value cutoff. +func (g *Graph) DiscardLowFrequencyNodes(nodeCutoff int64) NodeSet { + return makeNodeSet(g.Nodes, nodeCutoff) +} + +// DiscardLowFrequencyNodePtrs returns a NodePtrSet of nodes at or over a +// specific cum value cutoff. +func (g *Graph) DiscardLowFrequencyNodePtrs(nodeCutoff int64) NodePtrSet { + cutNodes := getNodesAboveCumCutoff(g.Nodes, nodeCutoff) + kept := make(NodePtrSet, len(cutNodes)) + for _, n := range cutNodes { + kept[n] = true + } + return kept +} + +func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet { + cutNodes := getNodesAboveCumCutoff(nodes, nodeCutoff) + kept := make(NodeSet, len(cutNodes)) + for _, n := range cutNodes { + kept[n.Info] = true + } + return kept +} + +// getNodesAboveCumCutoff returns all the nodes which have a Cum value greater +// than or equal to cutoff. +func getNodesAboveCumCutoff(nodes Nodes, nodeCutoff int64) Nodes { + cutoffNodes := make(Nodes, 0, len(nodes)) + for _, n := range nodes { + if abs64(n.Cum) < nodeCutoff { + continue + } + cutoffNodes = append(cutoffNodes, n) + } + return cutoffNodes +} + +// TrimLowFrequencyTags removes tags that have less than +// the specified weight. +func (g *Graph) TrimLowFrequencyTags(tagCutoff int64) { + // Remove nodes with value <= total*nodeFraction + for _, n := range g.Nodes { + n.LabelTags = trimLowFreqTags(n.LabelTags, tagCutoff) + for s, nt := range n.NumericTags { + n.NumericTags[s] = trimLowFreqTags(nt, tagCutoff) + } + } +} + +func trimLowFreqTags(tags TagMap, minValue int64) TagMap { + kept := TagMap{} + for s, t := range tags { + if abs64(t.Flat) >= minValue || abs64(t.Cum) >= minValue { + kept[s] = t + } + } + return kept +} + +// TrimLowFrequencyEdges removes edges that have less than +// the specified weight. Returns the number of edges removed +func (g *Graph) TrimLowFrequencyEdges(edgeCutoff int64) int { + var droppedEdges int + for _, n := range g.Nodes { + for src, e := range n.In { + if abs64(e.Weight) < edgeCutoff { + delete(n.In, src) + delete(src.Out, n) + droppedEdges++ + } + } + } + return droppedEdges +} + +// SortNodes sorts the nodes in a graph based on a specific heuristic. +func (g *Graph) SortNodes(cum bool, visualMode bool) { + // Sort nodes based on requested mode + switch { + case visualMode: + // Specialized sort to produce a more visually-interesting graph + g.Nodes.Sort(EntropyOrder) + case cum: + g.Nodes.Sort(CumNameOrder) + default: + g.Nodes.Sort(FlatNameOrder) + } +} + +// SelectTopNodePtrs returns a set of the top maxNodes *Node in a graph. +func (g *Graph) SelectTopNodePtrs(maxNodes int, visualMode bool) NodePtrSet { + set := make(NodePtrSet) + for _, node := range g.selectTopNodes(maxNodes, visualMode) { + set[node] = true + } + return set +} + +// SelectTopNodes returns a set of the top maxNodes nodes in a graph. +func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet { + return makeNodeSet(g.selectTopNodes(maxNodes, visualMode), 0) +} + +// selectTopNodes returns a slice of the top maxNodes nodes in a graph. +func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes { + if maxNodes > 0 { + if visualMode { + var count int + // If generating a visual graph, count tags as nodes. Update + // maxNodes to account for them. + for i, n := range g.Nodes { + tags := countTags(n) + if tags > maxNodelets { + tags = maxNodelets + } + if count += tags + 1; count >= maxNodes { + maxNodes = i + 1 + break + } + } + } + } + if maxNodes > len(g.Nodes) { + maxNodes = len(g.Nodes) + } + return g.Nodes[:maxNodes] +} + +// countTags counts the tags with flat count. This underestimates the +// number of tags being displayed, but in practice is close enough. +func countTags(n *Node) int { + count := 0 + for _, e := range n.LabelTags { + if e.Flat != 0 { + count++ + } + } + for _, t := range n.NumericTags { + for _, e := range t { + if e.Flat != 0 { + count++ + } + } + } + return count +} + +// RemoveRedundantEdges removes residual edges if the destination can +// be reached through another path. This is done to simplify the graph +// while preserving connectivity. +func (g *Graph) RemoveRedundantEdges() { + // Walk the nodes and outgoing edges in reverse order to prefer + // removing edges with the lowest weight. + for i := len(g.Nodes); i > 0; i-- { + n := g.Nodes[i-1] + in := n.In.Sort() + for j := len(in); j > 0; j-- { + e := in[j-1] + if !e.Residual { + // Do not remove edges heavier than a non-residual edge, to + // avoid potential confusion. + break + } + if isRedundantEdge(e) { + delete(e.Src.Out, e.Dest) + delete(e.Dest.In, e.Src) + } + } + } +} + +// isRedundantEdge determines if there is a path that allows e.Src +// to reach e.Dest after removing e. +func isRedundantEdge(e *Edge) bool { + src, n := e.Src, e.Dest + seen := map[*Node]bool{n: true} + queue := Nodes{n} + for len(queue) > 0 { + n := queue[0] + queue = queue[1:] + for _, ie := range n.In { + if e == ie || seen[ie.Src] { + continue + } + if ie.Src == src { + return true + } + seen[ie.Src] = true + queue = append(queue, ie.Src) + } + } + return false +} + +// nodeSorter is a mechanism used to allow a report to be sorted +// in different ways. +type nodeSorter struct { + rs Nodes + less func(l, r *Node) bool +} + +func (s nodeSorter) Len() int { return len(s.rs) } +func (s nodeSorter) Swap(i, j int) { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] } +func (s nodeSorter) Less(i, j int) bool { return s.less(s.rs[i], s.rs[j]) } + +// Sort reorders a slice of nodes based on the specified ordering +// criteria. The result is sorted in decreasing order for (absolute) +// numeric quantities, alphabetically for text, and increasing for +// addresses. +func (ns Nodes) Sort(o NodeOrder) error { + var s nodeSorter + + switch o { + case FlatNameOrder: + s = nodeSorter{ns, + func(l, r *Node) bool { + if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv { + return iv > jv + } + if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv { + return iv < jv + } + if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv { + return iv > jv + } + return compareNodes(l, r) + }, + } + case FlatCumNameOrder: + s = nodeSorter{ns, + func(l, r *Node) bool { + if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv { + return iv > jv + } + if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv { + return iv > jv + } + if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv { + return iv < jv + } + return compareNodes(l, r) + }, + } + case NameOrder: + s = nodeSorter{ns, + func(l, r *Node) bool { + if iv, jv := l.Info.Name, r.Info.Name; iv != jv { + return iv < jv + } + return compareNodes(l, r) + }, + } + case FileOrder: + s = nodeSorter{ns, + func(l, r *Node) bool { + if iv, jv := l.Info.File, r.Info.File; iv != jv { + return iv < jv + } + if iv, jv := l.Info.StartLine, r.Info.StartLine; iv != jv { + return iv < jv + } + return compareNodes(l, r) + }, + } + case AddressOrder: + s = nodeSorter{ns, + func(l, r *Node) bool { + if iv, jv := l.Info.Address, r.Info.Address; iv != jv { + return iv < jv + } + return compareNodes(l, r) + }, + } + case CumNameOrder, EntropyOrder: + // Hold scoring for score-based ordering + var score map[*Node]int64 + scoreOrder := func(l, r *Node) bool { + if iv, jv := abs64(score[l]), abs64(score[r]); iv != jv { + return iv > jv + } + if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv { + return iv < jv + } + if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv { + return iv > jv + } + return compareNodes(l, r) + } + + switch o { + case CumNameOrder: + score = make(map[*Node]int64, len(ns)) + for _, n := range ns { + score[n] = n.Cum + } + s = nodeSorter{ns, scoreOrder} + case EntropyOrder: + score = make(map[*Node]int64, len(ns)) + for _, n := range ns { + score[n] = entropyScore(n) + } + s = nodeSorter{ns, scoreOrder} + } + default: + return fmt.Errorf("report: unrecognized sort ordering: %d", o) + } + sort.Sort(s) + return nil +} + +// compareNodes compares two nodes to provide a deterministic ordering +// between them. Two nodes cannot have the same Node.Info value. +func compareNodes(l, r *Node) bool { + return fmt.Sprint(l.Info) < fmt.Sprint(r.Info) +} + +// entropyScore computes a score for a node representing how important +// it is to include this node on a graph visualization. It is used to +// sort the nodes and select which ones to display if we have more +// nodes than desired in the graph. This number is computed by looking +// at the flat and cum weights of the node and the incoming/outgoing +// edges. The fundamental idea is to penalize nodes that have a simple +// fallthrough from their incoming to the outgoing edge. +func entropyScore(n *Node) int64 { + score := float64(0) + + if len(n.In) == 0 { + score++ // Favor entry nodes + } else { + score += edgeEntropyScore(n, n.In, 0) + } + + if len(n.Out) == 0 { + score++ // Favor leaf nodes + } else { + score += edgeEntropyScore(n, n.Out, n.Flat) + } + + return int64(score*float64(n.Cum)) + n.Flat +} + +// edgeEntropyScore computes the entropy value for a set of edges +// coming in or out of a node. Entropy (as defined in information +// theory) refers to the amount of information encoded by the set of +// edges. A set of edges that have a more interesting distribution of +// samples gets a higher score. +func edgeEntropyScore(n *Node, edges EdgeMap, self int64) float64 { + score := float64(0) + total := self + for _, e := range edges { + if e.Weight > 0 { + total += abs64(e.Weight) + } + } + if total != 0 { + for _, e := range edges { + frac := float64(abs64(e.Weight)) / float64(total) + score += -frac * math.Log2(frac) + } + if self > 0 { + frac := float64(abs64(self)) / float64(total) + score += -frac * math.Log2(frac) + } + } + return score +} + +// NodeOrder sets the ordering for a Sort operation +type NodeOrder int + +// Sorting options for node sort. +const ( + FlatNameOrder NodeOrder = iota + FlatCumNameOrder + CumNameOrder + NameOrder + FileOrder + AddressOrder + EntropyOrder +) + +// Sort returns a slice of the edges in the map, in a consistent +// order. The sort order is first based on the edge weight +// (higher-to-lower) and then by the node names to avoid flakiness. +func (e EdgeMap) Sort() []*Edge { + el := make(edgeList, 0, len(e)) + for _, w := range e { + el = append(el, w) + } + + sort.Sort(el) + return el +} + +// Sum returns the total weight for a set of nodes. +func (e EdgeMap) Sum() int64 { + var ret int64 + for _, edge := range e { + ret += edge.Weight + } + return ret +} + +type edgeList []*Edge + +func (el edgeList) Len() int { + return len(el) +} + +func (el edgeList) Less(i, j int) bool { + if el[i].Weight != el[j].Weight { + return abs64(el[i].Weight) > abs64(el[j].Weight) + } + + from1 := el[i].Src.Info.PrintableName() + from2 := el[j].Src.Info.PrintableName() + if from1 != from2 { + return from1 < from2 + } + + to1 := el[i].Dest.Info.PrintableName() + to2 := el[j].Dest.Info.PrintableName() + + return to1 < to2 +} + +func (el edgeList) Swap(i, j int) { + el[i], el[j] = el[j], el[i] +} + +func abs64(i int64) int64 { + if i < 0 { + return -i + } + return i +} diff --git a/src/internal/profile/legacy_java_profile.go b/src/internal/profile/legacy_java_profile.go new file mode 100644 index 00000000000000..4f9f2a7c97870c --- /dev/null +++ b/src/internal/profile/legacy_java_profile.go @@ -0,0 +1,317 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements parsers to convert java legacy profiles into +// the profile.proto format. + +// Original file location: https://github.com/google/pprof/blob/main/profile/legacy_java_profile.go + +package profile + +import ( + "bytes" + "fmt" + "io" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +var ( + attributeRx = regexp.MustCompile(`([\w ]+)=([\w ]+)`) + javaSampleRx = regexp.MustCompile(` *(\d+) +(\d+) +@ +([ x0-9a-f]*)`) + javaLocationRx = regexp.MustCompile(`^\s*0x([[:xdigit:]]+)\s+(.*)\s*$`) + javaLocationFileLineRx = regexp.MustCompile(`^(.*)\s+\((.+):(-?[[:digit:]]+)\)$`) + javaLocationPathRx = regexp.MustCompile(`^(.*)\s+\((.*)\)$`) +) + +// javaCPUProfile returns a new Profile from profilez data. +// b is the profile bytes after the header, period is the profiling +// period, and parse is a function to parse 8-byte chunks from the +// profile in its native endianness. +func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { + p := &Profile{ + Period: period * 1000, + PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, + SampleType: []*ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}}, + } + var err error + var locs map[uint64]*Location + if b, locs, err = parseCPUSamples(b, parse, false, p); err != nil { + return nil, err + } + + if err = parseJavaLocations(b, locs, p); err != nil { + return nil, err + } + + // Strip out addresses for better merge. + if err = p.Aggregate(true, true, true, true, false); err != nil { + return nil, err + } + + return p, nil +} + +// parseJavaProfile returns a new profile from heapz or contentionz +// data. b is the profile bytes after the header. +func parseJavaProfile(b []byte) (*Profile, error) { + h := bytes.SplitAfterN(b, []byte("\n"), 2) + if len(h) < 2 { + return nil, errUnrecognized + } + + p := &Profile{ + PeriodType: &ValueType{}, + } + header := string(bytes.TrimSpace(h[0])) + + var err error + var pType string + switch header { + case "--- heapz 1 ---": + pType = "heap" + case "--- contentionz 1 ---": + pType = "contention" + default: + return nil, errUnrecognized + } + + if b, err = parseJavaHeader(pType, h[1], p); err != nil { + return nil, err + } + var locs map[uint64]*Location + if b, locs, err = parseJavaSamples(pType, b, p); err != nil { + return nil, err + } + if err = parseJavaLocations(b, locs, p); err != nil { + return nil, err + } + + // Strip out addresses for better merge. + if err = p.Aggregate(true, true, true, true, false); err != nil { + return nil, err + } + + return p, nil +} + +// parseJavaHeader parses the attribute section on a java profile and +// populates a profile. Returns the remainder of the buffer after all +// attributes. +func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) { + nextNewLine := bytes.IndexByte(b, byte('\n')) + for nextNewLine != -1 { + line := string(bytes.TrimSpace(b[0:nextNewLine])) + if line != "" { + h := attributeRx.FindStringSubmatch(line) + if h == nil { + // Not a valid attribute, exit. + return b, nil + } + + attribute, value := strings.TrimSpace(h[1]), strings.TrimSpace(h[2]) + var err error + switch pType + "/" + attribute { + case "heap/format", "cpu/format", "contention/format": + if value != "java" { + return nil, errUnrecognized + } + case "heap/resolution": + p.SampleType = []*ValueType{ + {Type: "inuse_objects", Unit: "count"}, + {Type: "inuse_space", Unit: value}, + } + case "contention/resolution": + p.SampleType = []*ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: value}, + } + case "contention/sampling period": + p.PeriodType = &ValueType{ + Type: "contentions", Unit: "count", + } + if p.Period, err = strconv.ParseInt(value, 0, 64); err != nil { + return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) + } + case "contention/ms since reset": + millis, err := strconv.ParseInt(value, 0, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) + } + p.DurationNanos = millis * 1000 * 1000 + default: + return nil, errUnrecognized + } + } + // Grab next line. + b = b[nextNewLine+1:] + nextNewLine = bytes.IndexByte(b, byte('\n')) + } + return b, nil +} + +// parseJavaSamples parses the samples from a java profile and +// populates the Samples in a profile. Returns the remainder of the +// buffer after the samples. +func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*Location, error) { + nextNewLine := bytes.IndexByte(b, byte('\n')) + locs := make(map[uint64]*Location) + for nextNewLine != -1 { + line := string(bytes.TrimSpace(b[0:nextNewLine])) + if line != "" { + sample := javaSampleRx.FindStringSubmatch(line) + if sample == nil { + // Not a valid sample, exit. + return b, locs, nil + } + + // Java profiles have data/fields inverted compared to other + // profile types. + var err error + value1, value2, value3 := sample[2], sample[1], sample[3] + addrs := parseHexAddresses(value3) + if err != nil { + return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + var sloc []*Location + for _, addr := range addrs { + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + s := &Sample{ + Value: make([]int64, 2), + Location: sloc, + } + + if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil { + return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) + } + if s.Value[1], err = strconv.ParseInt(value2, 0, 64); err != nil { + return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) + } + + switch pType { + case "heap": + const javaHeapzSamplingRate = 524288 // 512K + if s.Value[0] == 0 { + return nil, nil, fmt.Errorf("parsing sample %s: second value must be non-zero", line) + } + s.NumLabel = map[string][]int64{"bytes": {s.Value[1] / s.Value[0]}} + s.Value[0], s.Value[1] = scaleHeapSample(s.Value[0], s.Value[1], javaHeapzSamplingRate) + case "contention": + if period := p.Period; period != 0 { + s.Value[0] = s.Value[0] * p.Period + s.Value[1] = s.Value[1] * p.Period + } + } + p.Sample = append(p.Sample, s) + } + // Grab next line. + b = b[nextNewLine+1:] + nextNewLine = bytes.IndexByte(b, byte('\n')) + } + return b, locs, nil +} + +// parseJavaLocations parses the location information in a java +// profile and populates the Locations in a profile. It uses the +// location addresses from the profile as both the ID of each +// location. +func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile) error { + r := bytes.NewBuffer(b) + fns := make(map[string]*Function) + for { + line, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + return err + } + if line == "" { + break + } + } + + if line = strings.TrimSpace(line); line == "" { + continue + } + + jloc := javaLocationRx.FindStringSubmatch(line) + if len(jloc) != 3 { + continue + } + addr, err := strconv.ParseUint(jloc[1], 16, 64) + if err != nil { + return fmt.Errorf("parsing sample %s: %v", line, err) + } + loc := locs[addr] + if loc == nil { + // Unused/unseen + continue + } + var lineFunc, lineFile string + var lineNo int64 + + if fileLine := javaLocationFileLineRx.FindStringSubmatch(jloc[2]); len(fileLine) == 4 { + // Found a line of the form: "function (file:line)" + lineFunc, lineFile = fileLine[1], fileLine[2] + if n, err := strconv.ParseInt(fileLine[3], 10, 64); err == nil && n > 0 { + lineNo = n + } + } else if filePath := javaLocationPathRx.FindStringSubmatch(jloc[2]); len(filePath) == 3 { + // If there's not a file:line, it's a shared library path. + // The path isn't interesting, so just give the .so. + lineFunc, lineFile = filePath[1], filepath.Base(filePath[2]) + } else if strings.Contains(jloc[2], "generated stub/JIT") { + lineFunc = "STUB" + } else { + // Treat whole line as the function name. This is used by the + // java agent for internal states such as "GC" or "VM". + lineFunc = jloc[2] + } + fn := fns[lineFunc] + + if fn == nil { + fn = &Function{ + Name: lineFunc, + SystemName: lineFunc, + Filename: lineFile, + } + fns[lineFunc] = fn + p.Function = append(p.Function, fn) + } + loc.Line = []Line{ + { + Function: fn, + Line: lineNo, + }, + } + loc.Address = 0 + } + + p.remapLocationIDs() + p.remapFunctionIDs() + p.remapMappingIDs() + + return nil +} diff --git a/src/internal/profile/legacy_profile.go b/src/internal/profile/legacy_profile.go index d69f8deee7cb6a..0ac350a8884aa7 100644 --- a/src/internal/profile/legacy_profile.go +++ b/src/internal/profile/legacy_profile.go @@ -192,14 +192,6 @@ func (p *Profile) remapMappingIDs() { } } - // Subtract the offset from the start of the main mapping if it - // ends up at a recognizable start address. - const expectedStart = 0x400000 - if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { - m.Start = expectedStart - m.Offset = 0 - } - for _, l := range p.Location { if a := l.Address; a != 0 { for _, m := range p.Mapping { @@ -335,11 +327,12 @@ func addTracebackSample(l []*Location, s []string, p *Profile) { // // The general format for profilez samples is a sequence of words in // binary format. The first words are a header with the following data: -// 1st word -- 0 -// 2nd word -- 3 -// 3rd word -- 0 if a c++ application, 1 if a java application. -// 4th word -- Sampling period (in microseconds). -// 5th word -- Padding. +// +// 1st word -- 0 +// 2nd word -- 3 +// 3rd word -- 0 if a c++ application, 1 if a java application. +// 4th word -- Sampling period (in microseconds). +// 5th word -- Padding. func parseCPU(b []byte) (*Profile, error) { var parse func([]byte) (uint64, []byte) var n1, n2, n3, n4, n5 uint64 @@ -410,15 +403,18 @@ func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) ( // // profilez samples are a repeated sequence of stack frames of the // form: -// 1st word -- The number of times this stack was encountered. -// 2nd word -- The size of the stack (StackSize). -// 3rd word -- The first address on the stack. -// ... -// StackSize + 2 -- The last address on the stack +// +// 1st word -- The number of times this stack was encountered. +// 2nd word -- The size of the stack (StackSize). +// 3rd word -- The first address on the stack. +// ... +// StackSize + 2 -- The last address on the stack +// // The last stack trace is of the form: -// 1st word -- 0 -// 2nd word -- 1 -// 3rd word -- 0 +// +// 1st word -- 0 +// 2nd word -- 1 +// 3rd word -- 0 // // Addresses from stack traces may point to the next instruction after // each call. Optionally adjust by -1 to land somewhere on the actual @@ -750,11 +746,11 @@ func parseCppContention(r *bytes.Buffer) (*Profile, error) { break } - attr := strings.SplitN(l, delimiter, 2) - if len(attr) != 2 { + key, val, ok := strings.Cut(l, delimiter) + if !ok { break } - key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) + key, val = strings.TrimSpace(key), strings.TrimSpace(val) var err error switch key { case "cycles/second": @@ -1050,8 +1046,8 @@ func (p *Profile) ParseMemoryMap(rd io.Reader) error { if err == errUnrecognized { // Recognize assignments of the form: attr=value, and replace // $attr with value on subsequent mappings. - if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 { - attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) + if attr, value, ok := strings.Cut(l, delimiter); ok { + attrs = append(attrs, "$"+strings.TrimSpace(attr), strings.TrimSpace(value)) r = strings.NewReplacer(attrs...) } // Ignore any unrecognized entries diff --git a/src/internal/reflectlite/all_test.go b/src/internal/reflectlite/all_test.go index e15f364fcd33f3..bb3cad470c2f28 100644 --- a/src/internal/reflectlite/all_test.go +++ b/src/internal/reflectlite/all_test.go @@ -32,7 +32,7 @@ type T struct { } type pair struct { - i interface{} + i any s string } @@ -421,7 +421,7 @@ func TestAll(t *testing.T) { func TestInterfaceValue(t *testing.T) { var inter struct { - E interface{} + E any } inter.E = 123.456 v1 := ValueOf(&inter) @@ -437,7 +437,7 @@ func TestInterfaceValue(t *testing.T) { } func TestFunctionValue(t *testing.T) { - var x interface{} = func() {} + var x any = func() {} v := ValueOf(x) if fmt.Sprint(ToInterface(v)) != fmt.Sprint(x) { t.Fatalf("TestFunction returned wrong pointer") @@ -496,7 +496,7 @@ type Basic struct { type NotBasic Basic type DeepEqualTest struct { - a, b interface{} + a, b any eq bool } @@ -510,7 +510,7 @@ var ( type self struct{} type Loop *Loop -type Loopy interface{} +type Loopy any var loop1, loop2 Loop var loopy1, loopy2 Loopy @@ -578,7 +578,7 @@ var typeOfTests = []DeepEqualTest{ {int32(1), int64(1), false}, {0.5, "hello", false}, {[]int{1, 2, 3}, [3]int{1, 2, 3}, false}, - {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false}, + {&[3]any{1, 2, 4}, &[3]any{1, 2, "s"}, false}, {Basic{1, 0.5}, NotBasic{1, 0.5}, false}, {map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false}, @@ -606,14 +606,14 @@ func TestTypeOf(t *testing.T) { } } -func Nil(a interface{}, t *testing.T) { +func Nil(a any, t *testing.T) { n := Field(ValueOf(a), 0) if !n.IsNil() { t.Errorf("%v should be nil", a) } } -func NotNil(a interface{}, t *testing.T) { +func NotNil(a any, t *testing.T) { n := Field(ValueOf(a), 0) if n.IsNil() { t.Errorf("value of type %v should not be nil", TypeString(ValueOf(a).Type())) @@ -623,9 +623,9 @@ func NotNil(a interface{}, t *testing.T) { func TestIsNil(t *testing.T) { // These implement IsNil. // Wrap in extra struct to hide interface type. - doNil := []interface{}{ + doNil := []any{ struct{ x *int }{}, - struct{ x interface{} }{}, + struct{ x any }{}, struct{ x map[string]int }{}, struct{ x func() bool }{}, struct{ x chan int }{}, @@ -668,7 +668,7 @@ func TestIsNil(t *testing.T) { NotNil(mi, t) var ii struct { - x interface{} + x any } Nil(ii, t) ii.x = 2 @@ -770,7 +770,7 @@ func TestImportPath(t *testing.T) { {TypeOf([]byte(nil)), ""}, {TypeOf([]rune(nil)), ""}, {TypeOf(string("")), ""}, - {TypeOf((*interface{})(nil)).Elem(), ""}, + {TypeOf((*any)(nil)).Elem(), ""}, {TypeOf((*byte)(nil)), ""}, {TypeOf((*rune)(nil)), ""}, {TypeOf((*int64)(nil)), ""}, @@ -805,7 +805,7 @@ func noAlloc(t *testing.T, n int, f func(int)) { func TestAllocations(t *testing.T) { noAlloc(t, 100, func(j int) { - var i interface{} + var i any var v Value // We can uncomment this when compiler escape analysis @@ -939,7 +939,7 @@ func TestBigZero(t *testing.T) { func TestInvalid(t *testing.T) { // Used to have inconsistency between IsValid() and Kind() != Invalid. - type T struct{ v interface{} } + type T struct{ v any } v := Field(ValueOf(T{}), 0) if v.IsValid() != true || v.Kind() != Interface { @@ -954,10 +954,13 @@ func TestInvalid(t *testing.T) { type TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678 int type nameTest struct { - v interface{} + v any want string } +type A struct{} +type B[T any] struct{} + var nameTests = []nameTest{ {(*int32)(nil), "int32"}, {(*D1)(nil), "D1"}, @@ -966,11 +969,13 @@ var nameTests = []nameTest{ {(*func() D1)(nil), ""}, {(*<-chan D1)(nil), ""}, {(*chan<- D1)(nil), ""}, - {(*interface{})(nil), ""}, + {(*any)(nil), ""}, {(*interface { F() })(nil), ""}, {(*TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678)(nil), "TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678"}, + {(*B[A])(nil), "B[internal/reflectlite_test.A]"}, + {(*B[B[A]])(nil), "B[internal/reflectlite_test.B[internal/reflectlite_test.A]]"}, } func TestNames(t *testing.T) { diff --git a/src/internal/reflectlite/export_test.go b/src/internal/reflectlite/export_test.go index 354ea9dbd07325..e9a928bdc6a9ab 100644 --- a/src/internal/reflectlite/export_test.go +++ b/src/internal/reflectlite/export_test.go @@ -36,7 +36,7 @@ func Field(v Value, i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } @@ -78,10 +78,12 @@ func Zero(typ Type) Value { // ToInterface returns v's current value as an interface{}. // It is equivalent to: +// // var i interface{} = (v's underlying value) +// // It panics if the Value was obtained by accessing // unexported struct fields. -func ToInterface(v Value) (i interface{}) { +func ToInterface(v Value) (i any) { return valueInterface(v) } diff --git a/src/internal/reflectlite/set_test.go b/src/internal/reflectlite/set_test.go index a610499d084274..ca7ea9b0bc39d9 100644 --- a/src/internal/reflectlite/set_test.go +++ b/src/internal/reflectlite/set_test.go @@ -26,8 +26,8 @@ func TestImplicitSetConversion(t *testing.T) { } var implementsTests = []struct { - x interface{} - t interface{} + x any + t any b bool }{ {new(*bytes.Buffer), new(io.Reader), true}, @@ -73,8 +73,8 @@ func TestImplements(t *testing.T) { } var assignableTests = []struct { - x interface{} - t interface{} + x any + t any b bool }{ {new(chan int), new(<-chan int), true}, @@ -82,13 +82,13 @@ var assignableTests = []struct { {new(*int), new(IntPtr), true}, {new(IntPtr), new(*int), true}, {new(IntPtr), new(IntPtr1), false}, - {new(Ch), new(<-chan interface{}), true}, + {new(Ch), new(<-chan any), true}, // test runs implementsTests too } type IntPtr *int type IntPtr1 *int -type Ch <-chan interface{} +type Ch <-chan any func TestAssignableTo(t *testing.T) { for i, tt := range append(assignableTests, implementsTests...) { diff --git a/src/internal/reflectlite/swapper.go b/src/internal/reflectlite/swapper.go index 6330ab2d34668d..fc402bb38a423c 100644 --- a/src/internal/reflectlite/swapper.go +++ b/src/internal/reflectlite/swapper.go @@ -5,6 +5,7 @@ package reflectlite import ( + "internal/goarch" "internal/unsafeheader" "unsafe" ) @@ -13,7 +14,7 @@ import ( // slice. // // Swapper panics if the provided interface is not a slice. -func Swapper(slice interface{}) func(i, j int) { +func Swapper(slice any) func(i, j int) { v := ValueOf(slice) if v.Kind() != Slice { panic(&ValueError{Method: "Swapper", Kind: v.Kind()}) @@ -36,7 +37,7 @@ func Swapper(slice interface{}) func(i, j int) { // Some common & small cases, without using memmove: if hasPtr { - if size == ptrSize { + if size == goarch.PtrSize { ps := *(*[]unsafe.Pointer)(v.ptr) return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] } } diff --git a/src/internal/reflectlite/tostring_test.go b/src/internal/reflectlite/tostring_test.go index a1e5dae09d8663..966b0bd8499203 100644 --- a/src/internal/reflectlite/tostring_test.go +++ b/src/internal/reflectlite/tostring_test.go @@ -44,7 +44,7 @@ func valueToStringImpl(val reflect.Value) string { } else { return "false" } - case reflect.Ptr: + case reflect.Pointer: v := val str = typ.String() + "(" if v.IsNil() { diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go index b1899b0191f3f9..21e3c1278d0423 100644 --- a/src/internal/reflectlite/type.go +++ b/src/internal/reflectlite/type.go @@ -100,17 +100,20 @@ const ( Func Interface Map - Ptr + Pointer Slice String Struct UnsafePointer ) +const Ptr = Pointer + // tflag is used by an rtype to signal what extra type information is // available in the memory directly following the rtype value. // // tflag values must be kept in sync with copies in: +// // cmd/compile/internal/reflectdata/reflect.go // cmd/link/internal/ld/decodesym.go // runtime/type.go @@ -266,17 +269,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + name name // name is always non-empty + typ *rtype // type of field + offset uintptr // byte offset of field } func (f *structField) embedded() bool { - return f.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -296,7 +295,7 @@ type structType struct { // // The next two bytes are the data length: // -// l := uint16(data[1])<<8 | uint16(data[2]) +// l := uint16(data[1])<<8 | uint16(data[2]) // // Bytes [3:3+l] are the string data. // @@ -325,6 +324,10 @@ func (n name) hasTag() bool { return (*n.bytes)&(1<<1) != 0 } +func (n name) embedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + // readVarint parses a varint as encoded by encoding/binary. // It returns the number of encoded bytes and the encoded value. func (n name) readVarint(off int) (int, int) { @@ -575,7 +578,14 @@ func (t *rtype) Name() string { } s := t.String() i := len(s) - 1 - for i >= 0 && s[i] != '.' { + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } i-- } return s[i+1:] @@ -705,7 +715,7 @@ func (t *interfaceType) NumMethod() int { return len(t.methods) } // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. -func TypeOf(i interface{}) Type { +func TypeOf(i any) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } @@ -937,7 +947,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go index 0365eeeabf634f..b9bca3ab44db6a 100644 --- a/src/internal/reflectlite/value.go +++ b/src/internal/reflectlite/value.go @@ -5,13 +5,12 @@ package reflectlite import ( + "internal/goarch" "internal/unsafeheader" "runtime" "unsafe" ) -const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const - // Value is the reflection interface to a Go value. // // Not all methods apply to all kinds of values. Restrictions, @@ -88,9 +87,9 @@ func (f flag) ro() flag { } // pointer returns the underlying pointer represented by v. -// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer +// v.Kind() must be Pointer, Map, Chan, Func, or UnsafePointer func (v Value) pointer() unsafe.Pointer { - if v.typ.size != ptrSize || !v.typ.pointers() { + if v.typ.size != goarch.PtrSize || !v.typ.pointers() { panic("can't call pointer on a non-pointer Value") } if v.flag&flagIndir != 0 { @@ -100,9 +99,9 @@ func (v Value) pointer() unsafe.Pointer { } // packEface converts v to the empty interface. -func packEface(v Value) interface{} { +func packEface(v Value) any { t := v.typ - var i interface{} + var i any e := (*emptyInterface)(unsafe.Pointer(&i)) // First, fill in the data portion of the interface. switch { @@ -137,7 +136,7 @@ func packEface(v Value) interface{} { } // unpackEface converts the empty interface i to a Value. -func unpackEface(i interface{}) Value { +func unpackEface(i any) Value { e := (*emptyInterface)(unsafe.Pointer(&i)) // NOTE: don't read e.word until we know whether it is really a pointer or not. t := e.typ @@ -221,17 +220,17 @@ func (v Value) CanSet() bool { // Elem returns the value that the interface v contains // or that the pointer v points to. -// It panics if v's Kind is not Interface or Ptr. +// It panics if v's Kind is not Interface or Pointer. // It returns the zero Value if v is nil. func (v Value) Elem() Value { k := v.kind() switch k { case Interface: - var eface interface{} + var eface any if v.typ.NumMethod() == 0 { - eface = *(*interface{})(v.ptr) + eface = *(*any)(v.ptr) } else { - eface = (interface{})(*(*interface { + eface = (any)(*(*interface { M() })(v.ptr)) } @@ -240,7 +239,7 @@ func (v Value) Elem() Value { x.flag |= v.flag.ro() } return x - case Ptr: + case Pointer: ptr := v.ptr if v.flag&flagIndir != 0 { ptr = *(*unsafe.Pointer)(ptr) @@ -258,7 +257,7 @@ func (v Value) Elem() Value { panic(&ValueError{"reflectlite.Value.Elem", v.kind()}) } -func valueInterface(v Value) interface{} { +func valueInterface(v Value) any { if v.flag == 0 { panic(&ValueError{"reflectlite.Value.Interface", 0}) } @@ -268,7 +267,7 @@ func valueInterface(v Value) interface{} { // Empty interface has one layout, all interfaces with // methods have a second layout. if v.numMethod() == 0 { - return *(*interface{})(v.ptr) + return *(*any)(v.ptr) } return *(*interface { M() @@ -289,7 +288,7 @@ func valueInterface(v Value) interface{} { func (v Value) IsNil() bool { k := v.kind() switch k { - case Chan, Func, Map, Ptr, UnsafePointer: + case Chan, Func, Map, Pointer, UnsafePointer: // if v.flag&flagMethod != 0 { // return false // } @@ -392,7 +391,7 @@ func unsafe_New(*rtype) unsafe.Pointer // ValueOf returns a new Value initialized to the concrete value // stored in the interface i. ValueOf(nil) returns the zero Value. -func ValueOf(i interface{}) Value { +func ValueOf(i any) Value { if i == nil { return Value{} } @@ -434,7 +433,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value } x := valueInterface(v) if dst.NumMethod() == 0 { - *(*interface{})(target) = x + *(*any)(target) = x } else { ifaceE2I(dst, x, target) } @@ -456,16 +455,17 @@ func arrayAt(p unsafe.Pointer, i int, eltSize uintptr, whySafe string) unsafe.Po return add(p, uintptr(i)*eltSize, "i < len") } -func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) +func ifaceE2I(t *rtype, src any, dst unsafe.Pointer) // typedmemmove copies a value of type t to dst from src. +// //go:noescape func typedmemmove(t *rtype, dst, src unsafe.Pointer) // Dummy annotation marking that the value x escapes, // for use in cases where the reflect code is so clever that // the compiler cannot follow. -func escapes(x interface{}) { +func escapes(x any) { if dummy.b { dummy.x = x } @@ -473,5 +473,5 @@ func escapes(x interface{}) { var dummy struct { b bool - x interface{} + x any } diff --git a/src/internal/saferio/io.go b/src/internal/saferio/io.go new file mode 100644 index 00000000000000..8fb27b0be3f4ec --- /dev/null +++ b/src/internal/saferio/io.go @@ -0,0 +1,127 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package saferio provides I/O functions that avoid allocating large +// amounts of memory unnecessarily. This is intended for packages that +// read data from an [io.Reader] where the size is part of the input +// data but the input may be corrupt, or may be provided by an +// untrustworthy attacker. +package saferio + +import ( + "io" + "reflect" +) + +// chunk is an arbitrary limit on how much memory we are willing +// to allocate without concern. +const chunk = 10 << 20 // 10M + +// ReadData reads n bytes from the input stream, but avoids allocating +// all n bytes if n is large. This avoids crashing the program by +// allocating all n bytes in cases where n is incorrect. +// +// The error is io.EOF only if no bytes were read. +// If an io.EOF happens after reading some but not all the bytes, +// ReadData returns io.ErrUnexpectedEOF. +func ReadData(r io.Reader, n uint64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := io.ReadFull(r, buf) + if err != nil { + return nil, err + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := io.ReadFull(r, buf1[:next]) + if err != nil { + if len(buf) > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + } + return buf, nil +} + +// ReadDataAt reads n bytes from the input stream at off, but avoids +// allocating all n bytes if n is large. This avoids crashing the program +// by allocating all n bytes in cases where n is incorrect. +func ReadDataAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := r.ReadAt(buf, off) + if err != nil { + // io.SectionReader can return EOF for n == 0, + // but for our purposes that is a success. + if err != io.EOF || n > 0 { + return nil, err + } + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := r.ReadAt(buf1[:next], off) + if err != nil { + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + off += int64(next) + } + return buf, nil +} + +// SliceCap returns the capacity to use when allocating a slice. +// After the slice is allocated with the capacity, it should be +// built using append. This will avoid allocating too much memory +// if the capacity is large and incorrect. +// +// A negative result means that the value is always too big. +// +// The element type is described by passing a value of that type. +// This would ideally use generics, but this code is built with +// the bootstrap compiler which need not support generics. +func SliceCap(v any, c uint64) int { + if int64(c) < 0 || c != uint64(int(c)) { + return -1 + } + size := reflect.TypeOf(v).Size() + if uintptr(c)*size > chunk { + c = uint64(chunk / size) + if c == 0 { + c = 1 + } + } + return int(c) +} diff --git a/src/internal/saferio/io_test.go b/src/internal/saferio/io_test.go new file mode 100644 index 00000000000000..1a7d3e1840bcab --- /dev/null +++ b/src/internal/saferio/io_test.go @@ -0,0 +1,129 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package saferio + +import ( + "bytes" + "io" + "testing" +) + +func TestReadData(t *testing.T) { + const count = 100 + input := bytes.Repeat([]byte{'a'}, count) + + t.Run("small", func(t *testing.T) { + got, err := ReadData(bytes.NewReader(input), count) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, input) { + t.Errorf("got %v, want %v", got, input) + } + }) + + t.Run("large", func(t *testing.T) { + _, err := ReadData(bytes.NewReader(input), 10<<30) + if err == nil { + t.Error("large read succeeded unexpectedly") + } + }) + + t.Run("maxint", func(t *testing.T) { + _, err := ReadData(bytes.NewReader(input), 1<<62) + if err == nil { + t.Error("large read succeeded unexpectedly") + } + }) + + t.Run("small-EOF", func(t *testing.T) { + _, err := ReadData(bytes.NewReader(nil), chunk-1) + if err != io.EOF { + t.Errorf("ReadData = %v, want io.EOF", err) + } + }) + + t.Run("large-EOF", func(t *testing.T) { + _, err := ReadData(bytes.NewReader(nil), chunk+1) + if err != io.EOF { + t.Errorf("ReadData = %v, want io.EOF", err) + } + }) + + t.Run("large-UnexpectedEOF", func(t *testing.T) { + _, err := ReadData(bytes.NewReader(make([]byte, chunk)), chunk+1) + if err != io.ErrUnexpectedEOF { + t.Errorf("ReadData = %v, want io.ErrUnexpectedEOF", err) + } + }) +} + +func TestReadDataAt(t *testing.T) { + const count = 100 + input := bytes.Repeat([]byte{'a'}, count) + + t.Run("small", func(t *testing.T) { + got, err := ReadDataAt(bytes.NewReader(input), count, 0) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, input) { + t.Errorf("got %v, want %v", got, input) + } + }) + + t.Run("large", func(t *testing.T) { + _, err := ReadDataAt(bytes.NewReader(input), 10<<30, 0) + if err == nil { + t.Error("large read succeeded unexpectedly") + } + }) + + t.Run("maxint", func(t *testing.T) { + _, err := ReadDataAt(bytes.NewReader(input), 1<<62, 0) + if err == nil { + t.Error("large read succeeded unexpectedly") + } + }) + + t.Run("SectionReader", func(t *testing.T) { + // Reading 0 bytes from an io.SectionReader at the end + // of the section will return EOF, but ReadDataAt + // should succeed and return 0 bytes. + sr := io.NewSectionReader(bytes.NewReader(input), 0, 0) + got, err := ReadDataAt(sr, 0, 0) + if err != nil { + t.Fatal(err) + } + if len(got) > 0 { + t.Errorf("got %d bytes, expected 0", len(got)) + } + }) +} + +func TestSliceCap(t *testing.T) { + t.Run("small", func(t *testing.T) { + c := SliceCap(0, 10) + if c != 10 { + t.Errorf("got capacity %d, want %d", c, 10) + } + }) + + t.Run("large", func(t *testing.T) { + c := SliceCap(byte(0), 1<<30) + if c < 0 { + t.Error("SliceCap failed unexpectedly") + } else if c == 1<<30 { + t.Errorf("got capacity %d which is too high", c) + } + }) + + t.Run("maxint", func(t *testing.T) { + c := SliceCap(byte(0), 1<<63) + if c >= 0 { + t.Errorf("SliceCap returned %d, expected failure", c) + } + }) +} diff --git a/src/internal/singleflight/singleflight.go b/src/internal/singleflight/singleflight.go index b2d82e26c29c6b..19d5a94a0bba2a 100644 --- a/src/internal/singleflight/singleflight.go +++ b/src/internal/singleflight/singleflight.go @@ -14,7 +14,7 @@ type call struct { // These fields are written once before the WaitGroup is done // and are only read after the WaitGroup is done. - val interface{} + val any err error // These fields are read and written with the singleflight @@ -34,7 +34,7 @@ type Group struct { // Result holds the results of Do, so they can be passed // on a channel. type Result struct { - Val interface{} + Val any Err error Shared bool } @@ -44,7 +44,7 @@ type Result struct { // time. If a duplicate comes in, the duplicate caller waits for the // original to complete and receives the same results. // The return value shared indicates whether v was given to multiple callers. -func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { +func (g *Group) Do(key string, fn func() (any, error)) (v any, err error, shared bool) { g.mu.Lock() if g.m == nil { g.m = make(map[string]*call) @@ -65,10 +65,8 @@ func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, e } // DoChan is like Do but returns a channel that will receive the -// results when they are ready. The second result is true if the function -// will eventually be called, false if it will not (because there is -// a pending request with this key). -func (g *Group) DoChan(key string, fn func() (interface{}, error)) (<-chan Result, bool) { +// results when they are ready. +func (g *Group) DoChan(key string, fn func() (any, error)) <-chan Result { ch := make(chan Result, 1) g.mu.Lock() if g.m == nil { @@ -78,7 +76,7 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) (<-chan Resul c.dups++ c.chans = append(c.chans, ch) g.mu.Unlock() - return ch, false + return ch } c := &call{chans: []chan<- Result{ch}} c.wg.Add(1) @@ -87,11 +85,11 @@ func (g *Group) DoChan(key string, fn func() (interface{}, error)) (<-chan Resul go g.doCall(c, key, fn) - return ch, true + return ch } // doCall handles the single call for a key. -func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { +func (g *Group) doCall(c *call, key string, fn func() (any, error)) { c.val, c.err = fn() c.wg.Done() diff --git a/src/internal/singleflight/singleflight_test.go b/src/internal/singleflight/singleflight_test.go index 6404a1775a0de9..99713c9e149c08 100644 --- a/src/internal/singleflight/singleflight_test.go +++ b/src/internal/singleflight/singleflight_test.go @@ -15,7 +15,7 @@ import ( func TestDo(t *testing.T) { var g Group - v, err, _ := g.Do("key", func() (interface{}, error) { + v, err, _ := g.Do("key", func() (any, error) { return "bar", nil }) if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want { @@ -29,7 +29,7 @@ func TestDo(t *testing.T) { func TestDoErr(t *testing.T) { var g Group someErr := errors.New("some error") - v, err, _ := g.Do("key", func() (interface{}, error) { + v, err, _ := g.Do("key", func() (any, error) { return nil, someErr }) if err != someErr { @@ -44,9 +44,9 @@ func TestDoDupSuppress(t *testing.T) { var g Group var wg1, wg2 sync.WaitGroup c := make(chan string, 1) - var calls int32 - fn := func() (interface{}, error) { - if atomic.AddInt32(&calls, 1) == 1 { + var calls atomic.Int32 + fn := func() (any, error) { + if calls.Add(1) == 1 { // First invocation. wg1.Done() } @@ -81,7 +81,7 @@ func TestDoDupSuppress(t *testing.T) { // least reached the line before the Do. c <- "bar" wg2.Wait() - if got := atomic.LoadInt32(&calls); got <= 0 || got >= n { + if got := calls.Load(); got <= 0 || got >= n { t.Errorf("number of calls = %d; want over 0 and less than %d", got, n) } } diff --git a/src/internal/syscall/execenv/execenv_default.go b/src/internal/syscall/execenv/execenv_default.go index 73289f1cd05a89..335647c63813cd 100644 --- a/src/internal/syscall/execenv/execenv_default.go +++ b/src/internal/syscall/execenv/execenv_default.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package execenv diff --git a/src/internal/syscall/execenv/execenv_windows.go b/src/internal/syscall/execenv/execenv_windows.go index 6c0654943e6906..a8aa1a644eeffa 100644 --- a/src/internal/syscall/execenv/execenv_windows.go +++ b/src/internal/syscall/execenv/execenv_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package execenv diff --git a/src/internal/syscall/unix/at.go b/src/internal/syscall/unix/at.go index 9b08864f7f580d..876ca9ff578b94 100644 --- a/src/internal/syscall/unix/at.go +++ b/src/internal/syscall/unix/at.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux || openbsd || netbsd || dragonfly -// +build linux openbsd netbsd dragonfly +//go:build dragonfly || linux || netbsd || (openbsd && mips64) package unix @@ -41,19 +40,3 @@ func Openat(dirfd int, path string, flags int, perm uint32) (int, error) { return int(fd), nil } - -func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { - var p *byte - p, err := syscall.BytePtrFromString(path) - if err != nil { - return err - } - - _, _, errno := syscall.Syscall6(fstatatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if errno != 0 { - return errno - } - - return nil - -} diff --git a/src/internal/syscall/unix/at_darwin.go b/src/internal/syscall/unix/at_darwin.go deleted file mode 100644 index a88a27e0c6c527..00000000000000 --- a/src/internal/syscall/unix/at_darwin.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package unix - -import ( - "syscall" - _ "unsafe" // for linkname -) - -func Unlinkat(dirfd int, path string, flags int) error { - return unlinkat(dirfd, path, flags) -} - -func Openat(dirfd int, path string, flags int, perm uint32) (int, error) { - return openat(dirfd, path, flags, perm) -} - -func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { - return fstatat(dirfd, path, stat, flags) -} - -//go:linkname unlinkat syscall.unlinkat -func unlinkat(dirfd int, path string, flags int) error - -//go:linkname openat syscall.openat -func openat(dirfd int, path string, flags int, perm uint32) (int, error) - -//go:linkname fstatat syscall.fstatat -func fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error diff --git a/src/internal/syscall/unix/at_fstatat.go b/src/internal/syscall/unix/at_fstatat.go new file mode 100644 index 00000000000000..8f25fe9f64edb0 --- /dev/null +++ b/src/internal/syscall/unix/at_fstatat.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || (linux && !loong64) || netbsd || (openbsd && mips64) + +package unix + +import ( + "syscall" + "unsafe" +) + +func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { + var p *byte + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + + _, _, errno := syscall.Syscall6(fstatatTrap, uintptr(dirfd), uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if errno != 0 { + return errno + } + + return nil + +} diff --git a/src/internal/syscall/unix/at_libc.go b/src/internal/syscall/unix/at_libc.go index 4cc351ea278d88..f48d3791e370c8 100644 --- a/src/internal/syscall/unix/at_libc.go +++ b/src/internal/syscall/unix/at_libc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || solaris -// +build aix solaris package unix diff --git a/src/internal/syscall/unix/at_libc2.go b/src/internal/syscall/unix/at_libc2.go new file mode 100644 index 00000000000000..93d0cf4443f1f3 --- /dev/null +++ b/src/internal/syscall/unix/at_libc2.go @@ -0,0 +1,33 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || (openbsd && !mips64) + +package unix + +import ( + "syscall" + _ "unsafe" // for linkname +) + +func Unlinkat(dirfd int, path string, flags int) error { + return unlinkat(dirfd, path, flags) +} + +func Openat(dirfd int, path string, flags int, perm uint32) (int, error) { + return openat(dirfd, path, flags, perm) +} + +func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { + return fstatat(dirfd, path, stat, flags) +} + +//go:linkname unlinkat syscall.unlinkat +func unlinkat(dirfd int, path string, flags int) error + +//go:linkname openat syscall.openat +func openat(dirfd int, path string, flags int, perm uint32) (int, error) + +//go:linkname fstatat syscall.fstatat +func fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error diff --git a/src/internal/syscall/unix/at_statx.go b/src/internal/syscall/unix/at_statx.go new file mode 100644 index 00000000000000..230d697b8c4cdf --- /dev/null +++ b/src/internal/syscall/unix/at_statx.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 + +package unix + +import ( + "syscall" +) + +func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { + return syscall.Fstatat(dirfd, path, stat, flags) +} diff --git a/src/internal/syscall/unix/at_sysnum_fstatat64_linux.go b/src/internal/syscall/unix/at_sysnum_fstatat64_linux.go index 050d401bc0ef6a..445b0c38546ac0 100644 --- a/src/internal/syscall/unix/at_sysnum_fstatat64_linux.go +++ b/src/internal/syscall/unix/at_sysnum_fstatat64_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm || mips || mipsle || 386 -// +build arm mips mipsle 386 package unix diff --git a/src/internal/syscall/unix/at_sysnum_fstatat_linux.go b/src/internal/syscall/unix/at_sysnum_fstatat_linux.go index e53a2d1b75fb99..73a3da5bff2112 100644 --- a/src/internal/syscall/unix/at_sysnum_fstatat_linux.go +++ b/src/internal/syscall/unix/at_sysnum_fstatat_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 || riscv64 -// +build arm64 riscv64 package unix diff --git a/src/internal/syscall/unix/at_sysnum_linux.go b/src/internal/syscall/unix/at_sysnum_linux.go index fa7cd75d42653a..b9b8495e32ac3c 100644 --- a/src/internal/syscall/unix/at_sysnum_linux.go +++ b/src/internal/syscall/unix/at_sysnum_linux.go @@ -9,5 +9,9 @@ import "syscall" const unlinkatTrap uintptr = syscall.SYS_UNLINKAT const openatTrap uintptr = syscall.SYS_OPENAT -const AT_REMOVEDIR = 0x200 -const AT_SYMLINK_NOFOLLOW = 0x100 +const ( + AT_EACCESS = 0x200 + AT_FDCWD = -0x64 + AT_REMOVEDIR = 0x200 + AT_SYMLINK_NOFOLLOW = 0x100 +) diff --git a/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go b/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go index 4cb4a5976ba737..76edf675227f9b 100644 --- a/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go +++ b/src/internal/syscall/unix/at_sysnum_newfstatat_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || mips64 || mips64le || ppc64 || ppc64le || s390x -// +build amd64 mips64 mips64le ppc64 ppc64le s390x package unix diff --git a/src/internal/syscall/unix/constants.go b/src/internal/syscall/unix/constants.go new file mode 100644 index 00000000000000..e3245897055cce --- /dev/null +++ b/src/internal/syscall/unix/constants.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package unix + +const ( + R_OK = 0x4 + W_OK = 0x2 + X_OK = 0x1 +) diff --git a/src/internal/syscall/unix/eaccess_linux.go b/src/internal/syscall/unix/eaccess_linux.go new file mode 100644 index 00000000000000..5695a5e4ce7cdf --- /dev/null +++ b/src/internal/syscall/unix/eaccess_linux.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import "syscall" + +func Eaccess(path string, mode uint32) error { + return syscall.Faccessat(AT_FDCWD, path, mode, AT_EACCESS) +} diff --git a/src/internal/syscall/unix/eaccess_other.go b/src/internal/syscall/unix/eaccess_other.go new file mode 100644 index 00000000000000..23be11829755bb --- /dev/null +++ b/src/internal/syscall/unix/eaccess_other.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix && !linux + +package unix + +import "syscall" + +func Eaccess(path string, mode uint32) error { + return syscall.ENOSYS +} diff --git a/src/internal/syscall/unix/fcntl_linux_32bit.go b/src/internal/syscall/unix/fcntl_linux_32bit.go index 46a4f6b030d46d..7b39ee72bc494d 100644 --- a/src/internal/syscall/unix/fcntl_linux_32bit.go +++ b/src/internal/syscall/unix/fcntl_linux_32bit.go @@ -6,7 +6,6 @@ // If you change the build tags here, see syscall/flock_linux_32bit.go. //go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) -// +build linux,386 linux,arm linux,mips linux,mipsle package unix diff --git a/src/internal/syscall/unix/getentropy_darwin.go b/src/internal/syscall/unix/getentropy_darwin.go index e1a410a4541446..7bab1f27b0c896 100644 --- a/src/internal/syscall/unix/getentropy_darwin.go +++ b/src/internal/syscall/unix/getentropy_darwin.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin && !ios + package unix import ( diff --git a/src/internal/syscall/unix/getentropy_openbsd.go b/src/internal/syscall/unix/getentropy_openbsd.go index d5caa8095a68bf..ad0914da903b98 100644 --- a/src/internal/syscall/unix/getentropy_openbsd.go +++ b/src/internal/syscall/unix/getentropy_openbsd.go @@ -1,25 +1,17 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package unix +//go:build openbsd && !mips64 -import ( - "syscall" - "unsafe" -) +package unix -// getentropy(2)'s syscall number, from /usr/src/sys/kern/syscalls.master -const entropyTrap uintptr = 7 +import _ "unsafe" // for linkname // GetEntropy calls the OpenBSD getentropy system call. func GetEntropy(p []byte) error { - _, _, errno := syscall.Syscall(entropyTrap, - uintptr(unsafe.Pointer(&p[0])), - uintptr(len(p)), - 0) - if errno != 0 { - return errno - } - return nil + return getentropy(p) } + +//go:linkname getentropy syscall.getentropy +func getentropy(p []byte) error diff --git a/src/internal/syscall/unix/getentropy_openbsd_mips64.go b/src/internal/syscall/unix/getentropy_openbsd_mips64.go new file mode 100644 index 00000000000000..d5caa8095a68bf --- /dev/null +++ b/src/internal/syscall/unix/getentropy_openbsd_mips64.go @@ -0,0 +1,25 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "syscall" + "unsafe" +) + +// getentropy(2)'s syscall number, from /usr/src/sys/kern/syscalls.master +const entropyTrap uintptr = 7 + +// GetEntropy calls the OpenBSD getentropy system call. +func GetEntropy(p []byte) error { + _, _, errno := syscall.Syscall(entropyTrap, + uintptr(unsafe.Pointer(&p[0])), + uintptr(len(p)), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/getrandom.go b/src/internal/syscall/unix/getrandom.go index d2c58c0f6fa459..a6659331e46d87 100644 --- a/src/internal/syscall/unix/getrandom.go +++ b/src/internal/syscall/unix/getrandom.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux -// +build dragonfly freebsd linux package unix diff --git a/src/internal/syscall/unix/ioctl_aix.go b/src/internal/syscall/unix/ioctl_aix.go index 19d56c36a13156..d361533b5c4a43 100644 --- a/src/internal/syscall/unix/ioctl_aix.go +++ b/src/internal/syscall/unix/ioctl_aix.go @@ -16,7 +16,7 @@ var libc_ioctl uintptr // Implemented in syscall/syscall_aix.go. func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) -func Ioctl(fd int, cmd int, args uintptr) (err error) { +func Ioctl(fd int, cmd int, args unsafe.Pointer) (err error) { _, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_ioctl)), 3, uintptr(fd), uintptr(cmd), uintptr(args), 0, 0, 0) if e1 != 0 { err = e1 diff --git a/src/internal/syscall/unix/kernel_version_linux.go b/src/internal/syscall/unix/kernel_version_linux.go new file mode 100644 index 00000000000000..71e8aa4c57164c --- /dev/null +++ b/src/internal/syscall/unix/kernel_version_linux.go @@ -0,0 +1,42 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "syscall" +) + +// KernelVersion returns major and minor kernel version numbers, parsed from +// the syscall.Uname's Release field, or 0, 0 if the version can't be obtained +// or parsed. +// +// Currently only implemented for Linux. +func KernelVersion() (major, minor int) { + var uname syscall.Utsname + if err := syscall.Uname(&uname); err != nil { + return + } + + var ( + values [2]int + value, vi int + ) + for _, c := range uname.Release { + if '0' <= c && c <= '9' { + value = (value * 10) + int(c-'0') + } else { + // Note that we're assuming N.N.N here. + // If we see anything else, we are likely to mis-parse it. + values[vi] = value + vi++ + if vi >= len(values) { + break + } + value = 0 + } + } + + return values[0], values[1] +} diff --git a/src/internal/syscall/unix/kernel_version_other.go b/src/internal/syscall/unix/kernel_version_other.go new file mode 100644 index 00000000000000..00af9f2ba01841 --- /dev/null +++ b/src/internal/syscall/unix/kernel_version_other.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux + +package unix + +func KernelVersion() (major int, minor int) { + return 0, 0 +} diff --git a/src/internal/syscall/unix/net.go b/src/internal/syscall/unix/net.go new file mode 100644 index 00000000000000..5618d40ae0f793 --- /dev/null +++ b/src/internal/syscall/unix/net.go @@ -0,0 +1,44 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package unix + +import ( + "syscall" + _ "unsafe" +) + +//go:linkname RecvfromInet4 syscall.recvfromInet4 +//go:noescape +func RecvfromInet4(fd int, p []byte, flags int, from *syscall.SockaddrInet4) (int, error) + +//go:linkname RecvfromInet6 syscall.recvfromInet6 +//go:noescape +func RecvfromInet6(fd int, p []byte, flags int, from *syscall.SockaddrInet6) (n int, err error) + +//go:linkname SendtoInet4 syscall.sendtoInet4 +//go:noescape +func SendtoInet4(fd int, p []byte, flags int, to *syscall.SockaddrInet4) (err error) + +//go:linkname SendtoInet6 syscall.sendtoInet6 +//go:noescape +func SendtoInet6(fd int, p []byte, flags int, to *syscall.SockaddrInet6) (err error) + +//go:linkname SendmsgNInet4 syscall.sendmsgNInet4 +//go:noescape +func SendmsgNInet4(fd int, p, oob []byte, to *syscall.SockaddrInet4, flags int) (n int, err error) + +//go:linkname SendmsgNInet6 syscall.sendmsgNInet6 +//go:noescape +func SendmsgNInet6(fd int, p, oob []byte, to *syscall.SockaddrInet6, flags int) (n int, err error) + +//go:linkname RecvmsgInet4 syscall.recvmsgInet4 +//go:noescape +func RecvmsgInet4(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet4) (n, oobn int, recvflags int, err error) + +//go:linkname RecvmsgInet6 syscall.recvmsgInet6 +//go:noescape +func RecvmsgInet6(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet6) (n, oobn int, recvflags int, err error) diff --git a/src/internal/syscall/unix/net_js.go b/src/internal/syscall/unix/net_js.go new file mode 100644 index 00000000000000..622fc8eb14aa03 --- /dev/null +++ b/src/internal/syscall/unix/net_js.go @@ -0,0 +1,44 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build js + +package unix + +import ( + "syscall" + _ "unsafe" +) + +func RecvfromInet4(fd int, p []byte, flags int, from *syscall.SockaddrInet4) (int, error) { + return 0, syscall.ENOSYS +} + +func RecvfromInet6(fd int, p []byte, flags int, from *syscall.SockaddrInet6) (n int, err error) { + return 0, syscall.ENOSYS +} + +func SendtoInet4(fd int, p []byte, flags int, to *syscall.SockaddrInet4) (err error) { + return syscall.ENOSYS +} + +func SendtoInet6(fd int, p []byte, flags int, to *syscall.SockaddrInet6) (err error) { + return syscall.ENOSYS +} + +func SendmsgNInet4(fd int, p, oob []byte, to *syscall.SockaddrInet4, flags int) (n int, err error) { + return 0, syscall.ENOSYS +} + +func SendmsgNInet6(fd int, p, oob []byte, to *syscall.SockaddrInet6, flags int) (n int, err error) { + return 0, syscall.ENOSYS +} + +func RecvmsgInet4(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet4) (n, oobn int, recvflags int, err error) { + return 0, 0, 0, syscall.ENOSYS +} + +func RecvmsgInet6(fd int, p, oob []byte, flags int, from *syscall.SockaddrInet6) (n, oobn int, recvflags int, err error) { + return 0, 0, 0, syscall.ENOSYS +} diff --git a/src/internal/syscall/unix/nonblocking.go b/src/internal/syscall/unix/nonblocking.go index a22986cb8a402b..a0becd1e01e04d 100644 --- a/src/internal/syscall/unix/nonblocking.go +++ b/src/internal/syscall/unix/nonblocking.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) package unix diff --git a/src/internal/syscall/unix/nonblocking_js.go b/src/internal/syscall/unix/nonblocking_js.go index a5a5080d46153a..8ed40f3f9104ad 100644 --- a/src/internal/syscall/unix/nonblocking_js.go +++ b/src/internal/syscall/unix/nonblocking_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package unix diff --git a/src/internal/syscall/unix/nonblocking_libc.go b/src/internal/syscall/unix/nonblocking_libc.go index d9565efcb6af46..bff668496236e6 100644 --- a/src/internal/syscall/unix/nonblocking_libc.go +++ b/src/internal/syscall/unix/nonblocking_libc.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || solaris -// +build aix darwin solaris +//go:build aix || darwin || (openbsd && !mips64) || solaris package unix @@ -21,5 +20,6 @@ func IsNonblock(fd int) (nonblocking bool, err error) { } // Implemented in the syscall package. +// //go:linkname fcntl syscall.fcntl func fcntl(fd int, cmd int, arg int) (int, error) diff --git a/src/internal/syscall/unix/pipe2_illumos.go b/src/internal/syscall/unix/pipe2_illumos.go deleted file mode 100644 index b0aac895808deb..00000000000000 --- a/src/internal/syscall/unix/pipe2_illumos.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build illumos -// +build illumos - -package unix - -import ( - "syscall" - "unsafe" -) - -//go:cgo_import_dynamic libc_pipe2 pipe2 "libc.so" - -//go:linkname procpipe2 libc_pipe2 - -var procpipe2 uintptr - -type _C_int int32 - -func Pipe2(p []int, flags int) error { - if len(p) != 2 { - return syscall.EINVAL - } - var pp [2]_C_int - _, _, errno := syscall6(uintptr(unsafe.Pointer(&procpipe2)), 2, uintptr(unsafe.Pointer(&pp)), uintptr(flags), 0, 0, 0, 0) - if errno != 0 { - return errno - } - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return nil -} diff --git a/src/internal/syscall/unix/sysnum_linux_generic.go b/src/internal/syscall/unix/sysnum_linux_generic.go index a76025454cad46..8c132c6bf5071a 100644 --- a/src/internal/syscall/unix/sysnum_linux_generic.go +++ b/src/internal/syscall/unix/sysnum_linux_generic.go @@ -2,15 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && (arm64 || riscv64) -// +build linux -// +build arm64 riscv64 +//go:build linux && (arm64 || loong64 || riscv64) package unix // This file is named "generic" because at a certain point Linux started // standardizing on system call numbers across architectures. So far this -// means only arm64 and riscv64 use the standard numbers. +// means only arm64 loong64 and riscv64 use the standard numbers. const ( getrandomTrap uintptr = 278 diff --git a/src/internal/syscall/unix/sysnum_linux_mips64x.go b/src/internal/syscall/unix/sysnum_linux_mips64x.go index f353d4d73e48f6..bca526d2b905bf 100644 --- a/src/internal/syscall/unix/sysnum_linux_mips64x.go +++ b/src/internal/syscall/unix/sysnum_linux_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package unix diff --git a/src/internal/syscall/unix/sysnum_linux_mipsx.go b/src/internal/syscall/unix/sysnum_linux_mipsx.go index 4ed471532a2a41..c86195e49698a5 100644 --- a/src/internal/syscall/unix/sysnum_linux_mipsx.go +++ b/src/internal/syscall/unix/sysnum_linux_mipsx.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle package unix diff --git a/src/internal/syscall/unix/sysnum_linux_ppc64x.go b/src/internal/syscall/unix/sysnum_linux_ppc64x.go index b484ffef80b97b..a4dcf2bc9d71f5 100644 --- a/src/internal/syscall/unix/sysnum_linux_ppc64x.go +++ b/src/internal/syscall/unix/sysnum_linux_ppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package unix diff --git a/src/internal/syscall/unix/writev_illumos.go b/src/internal/syscall/unix/writev_illumos.go deleted file mode 100644 index f60949f662b58a..00000000000000 --- a/src/internal/syscall/unix/writev_illumos.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build illumos -// +build illumos - -package unix - -import ( - "syscall" - "unsafe" -) - -//go:cgo_import_dynamic libc_writev writev "libc.so" - -//go:linkname procwritev libc_writev - -var procwritev uintptr - -func Writev(fd int, iovs []syscall.Iovec) (uintptr, error) { - var p *syscall.Iovec - if len(iovs) > 0 { - p = &iovs[0] - } - n, _, errno := syscall6(uintptr(unsafe.Pointer(&procwritev)), 3, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(len(iovs)), 0, 0, 0) - if errno != 0 { - return 0, errno - } - return n, nil -} diff --git a/src/internal/syscall/unix/writev_solaris.go b/src/internal/syscall/unix/writev_solaris.go new file mode 100644 index 00000000000000..d4895eef9e7408 --- /dev/null +++ b/src/internal/syscall/unix/writev_solaris.go @@ -0,0 +1,28 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "syscall" + "unsafe" +) + +//go:cgo_import_dynamic libc_writev writev "libc.so" + +//go:linkname procwritev libc_writev + +var procwritev uintptr + +func Writev(fd int, iovs []syscall.Iovec) (uintptr, error) { + var p *syscall.Iovec + if len(iovs) > 0 { + p = &iovs[0] + } + n, _, errno := syscall6(uintptr(unsafe.Pointer(&procwritev)), 3, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(len(iovs)), 0, 0, 0) + if errno != 0 { + return 0, errno + } + return n, nil +} diff --git a/src/internal/syscall/windows/exec_windows_test.go b/src/internal/syscall/windows/exec_windows_test.go index 283d7cea94bfa0..3311da54741646 100644 --- a/src/internal/syscall/windows/exec_windows_test.go +++ b/src/internal/syscall/windows/exec_windows_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. -// +build windows +//go:build windows package windows_test diff --git a/src/internal/syscall/windows/memory_windows.go b/src/internal/syscall/windows/memory_windows.go new file mode 100644 index 00000000000000..8fb34cf34924bc --- /dev/null +++ b/src/internal/syscall/windows/memory_windows.go @@ -0,0 +1,24 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package windows + +type MemoryBasicInformation struct { + // A pointer to the base address of the region of pages. + BaseAddress uintptr + // A pointer to the base address of a range of pages allocated by the VirtualAlloc function. + // The page pointed to by the BaseAddress member is contained within this allocation range. + AllocationBase uintptr + // The memory protection option when the region was initially allocated + AllocationProtect uint32 + PartitionId uint16 + // The size of the region beginning at the base address in which all pages have identical attributes, in bytes. + RegionSize uintptr + // The state of the pages in the region. + State uint32 + // The access protection of the pages in the region. + Protect uint32 + // The type of pages in the region. + Type uint32 +} diff --git a/src/internal/syscall/windows/mksyscall.go b/src/internal/syscall/windows/mksyscall.go index 599f07601bd4d5..81f08c627e6496 100644 --- a/src/internal/syscall/windows/mksyscall.go +++ b/src/internal/syscall/windows/mksyscall.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 generate +//go:build generate package windows diff --git a/src/internal/syscall/windows/net_windows.go b/src/internal/syscall/windows/net_windows.go new file mode 100644 index 00000000000000..3d3df7161c1ce2 --- /dev/null +++ b/src/internal/syscall/windows/net_windows.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package windows + +import ( + "syscall" + _ "unsafe" +) + +//go:linkname WSASendtoInet4 syscall.wsaSendtoInet4 +//go:noescape +func WSASendtoInet4(s syscall.Handle, bufs *syscall.WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *syscall.SockaddrInet4, overlapped *syscall.Overlapped, croutine *byte) (err error) + +//go:linkname WSASendtoInet6 syscall.wsaSendtoInet6 +//go:noescape +func WSASendtoInet6(s syscall.Handle, bufs *syscall.WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *syscall.SockaddrInet6, overlapped *syscall.Overlapped, croutine *byte) (err error) diff --git a/src/internal/syscall/windows/registry/export_test.go b/src/internal/syscall/windows/registry/export_test.go index 8badf6fdcf1f9a..7f1ac70e8fa01c 100644 --- a/src/internal/syscall/windows/registry/export_test.go +++ b/src/internal/syscall/windows/registry/export_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. -// +build windows +//go:build windows package registry diff --git a/src/internal/syscall/windows/registry/key.go b/src/internal/syscall/windows/registry/key.go index 612c48f084060f..ce6397f1e29c7d 100644 --- a/src/internal/syscall/windows/registry/key.go +++ b/src/internal/syscall/windows/registry/key.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 windows +//go:build windows // Package registry provides access to the Windows registry. // @@ -22,10 +22,12 @@ // // NOTE: This package is a copy of golang.org/x/sys/windows/registry // with KeyInfo.ModTime removed to prevent dependency cycles. -// package registry -import "syscall" +import ( + "runtime" + "syscall" +) const ( // Registry key security and access rights. @@ -88,6 +90,12 @@ func OpenKey(k Key, path string, access uint32) (Key, error) { // ReadSubKeyNames returns the names of subkeys of key k. func (k Key) ReadSubKeyNames() ([]string, error) { + // RegEnumKeyEx must be called repeatedly and to completion. + // During this time, this goroutine cannot migrate away from + // its current thread. See #49320. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + names := make([]string, 0) // Registry key size limit is 255 bytes and described there: // https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx diff --git a/src/internal/syscall/windows/registry/mksyscall.go b/src/internal/syscall/windows/registry/mksyscall.go index 320abf7fc69eca..0e0b4210d58a0d 100644 --- a/src/internal/syscall/windows/registry/mksyscall.go +++ b/src/internal/syscall/windows/registry/mksyscall.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 generate +//go:build generate package registry diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go index 57971629006a7a..278b0b491178d5 100644 --- a/src/internal/syscall/windows/registry/registry_test.go +++ b/src/internal/syscall/windows/registry/registry_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. -// +build windows +//go:build windows package registry_test @@ -118,7 +118,7 @@ func equalStringSlice(a, b []string) bool { type ValueTest struct { Type uint32 Name string - Value interface{} + Value any WillFail bool } diff --git a/src/internal/syscall/windows/registry/syscall.go b/src/internal/syscall/windows/registry/syscall.go index a6525dac5d91ac..cb315adadedf72 100644 --- a/src/internal/syscall/windows/registry/syscall.go +++ b/src/internal/syscall/windows/registry/syscall.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 windows +//go:build windows package registry diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go index dc3930a6bc2eda..025574015f69e8 100644 --- a/src/internal/syscall/windows/registry/value.go +++ b/src/internal/syscall/windows/registry/value.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 windows +//go:build windows package registry diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index f8965d0bab175d..39ff25fcb7bb15 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -5,7 +5,6 @@ package windows import ( - "internal/unsafeheader" "sync" "syscall" "unicode/utf16" @@ -26,16 +25,13 @@ func UTF16PtrToString(p *uint16) string { n++ } // Turn *uint16 into []uint16. - var s []uint16 - hdr := (*unsafeheader.Slice)(unsafe.Pointer(&s)) - hdr.Data = unsafe.Pointer(p) - hdr.Cap = n - hdr.Len = n + s := unsafe.Slice((*uint16)(unsafe.Pointer(p)), n) // Decode []uint16 into string. return string(utf16.Decode(s)) } const ( + ERROR_BAD_LENGTH syscall.Errno = 24 ERROR_SHARING_VIOLATION syscall.Errno = 32 ERROR_LOCK_VIOLATION syscall.Errno = 33 ERROR_NOT_SUPPORTED syscall.Errno = 50 @@ -154,6 +150,33 @@ const ( //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW //sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle +//sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery + +const ( + // flags for CreateToolhelp32Snapshot + TH32CS_SNAPMODULE = 0x08 + TH32CS_SNAPMODULE32 = 0x10 +) + +const MAX_MODULE_NAME32 = 255 + +type ModuleEntry32 struct { + Size uint32 + ModuleID uint32 + ProcessID uint32 + GlblcntUsage uint32 + ProccntUsage uint32 + ModBaseAddr uintptr + ModBaseSize uint32 + ModuleHandle syscall.Handle + Module [MAX_MODULE_NAME32 + 1]uint16 + ExePath [syscall.MAX_PATH]uint16 +} + +const SizeofModuleEntry32 = unsafe.Sizeof(ModuleEntry32{}) + +//sys Module32First(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW +//sys Module32Next(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32NextW const ( WSA_FLAG_OVERLAPPED = 0x01 diff --git a/src/internal/syscall/windows/sysdll/sysdll.go b/src/internal/syscall/windows/sysdll/sysdll.go index c587c19c77d45a..e79fd19edc0ebb 100644 --- a/src/internal/syscall/windows/sysdll/sysdll.go +++ b/src/internal/syscall/windows/sysdll/sysdll.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 windows +//go:build windows // Package sysdll is an internal leaf package that records and reports // which Windows DLL names are used by Go itself. These DLLs are then diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index aaad4a5b94b7e5..afd64e318e516d 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -62,10 +62,13 @@ var ( procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW") procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") procLockFileEx = modkernel32.NewProc("LockFileEx") + procModule32FirstW = modkernel32.NewProc("Module32FirstW") + procModule32NextW = modkernel32.NewProc("Module32NextW") procMoveFileExW = modkernel32.NewProc("MoveFileExW") procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") + procVirtualQuery = modkernel32.NewProc("VirtualQuery") procNetShareAdd = modnetapi32.NewProc("NetShareAdd") procNetShareDel = modnetapi32.NewProc("NetShareDel") procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") @@ -224,6 +227,22 @@ func LockFileEx(file syscall.Handle, flags uint32, reserved uint32, bytesLow uin return } +func Module32First(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procModule32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func Module32Next(snapshot syscall.Handle, moduleEntry *ModuleEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procModule32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(moduleEntry)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags)) if r1 == 0 { @@ -257,6 +276,14 @@ func UnlockFileEx(file syscall.Handle, reserved uint32, bytesLow uint32, bytesHi return } +func VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procVirtualQuery.Addr(), 3, uintptr(address), uintptr(unsafe.Pointer(buffer)), uintptr(length)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) { r0, _, _ := syscall.Syscall6(procNetShareAdd.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(parmErr)), 0, 0) if r0 != 0 { diff --git a/src/internal/testenv/noopt.go b/src/internal/testenv/noopt.go new file mode 100644 index 00000000000000..ae2a3d011a07c4 --- /dev/null +++ b/src/internal/testenv/noopt.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build noopt + +package testenv + +// OptimizationOff reports whether optimization is disabled. +func OptimizationOff() bool { + return true +} diff --git a/src/internal/testenv/opt.go b/src/internal/testenv/opt.go new file mode 100644 index 00000000000000..1bb96f73a12944 --- /dev/null +++ b/src/internal/testenv/opt.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !noopt + +package testenv + +// OptimizationOff reports whether optimization is disabled. +func OptimizationOff() bool { + return false +} diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index c902b1404f246a..7b435fd002ab44 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -11,8 +11,10 @@ package testenv import ( + "bytes" "errors" "flag" + "fmt" "internal/cfg" "os" "os/exec" @@ -22,6 +24,7 @@ import ( "strings" "sync" "testing" + "time" ) // Builder reports the name of the builder running this test @@ -32,7 +35,7 @@ func Builder() string { return os.Getenv("GO_BUILDER_NAME") } -// HasGoBuild reports whether the current system can build programs with ``go build'' +// HasGoBuild reports whether the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. func HasGoBuild() bool { if os.Getenv("GO_GCFLAGS") != "" { @@ -49,7 +52,7 @@ func HasGoBuild() bool { return true } -// MustHaveGoBuild checks that the current system can build programs with ``go build'' +// MustHaveGoBuild checks that the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. // If not, MustHaveGoBuild calls t.Skip with an explanation. func MustHaveGoBuild(t testing.TB) { @@ -61,13 +64,13 @@ func MustHaveGoBuild(t testing.TB) { } } -// HasGoRun reports whether the current system can run programs with ``go run.'' +// HasGoRun reports whether the current system can run programs with “go run.” func HasGoRun() bool { // For now, having go run and having go build are the same. return HasGoBuild() } -// MustHaveGoRun checks that the current system can run programs with ``go run.'' +// MustHaveGoRun checks that the current system can run programs with “go run.” // If not, MustHaveGoRun calls t.Skip with an explanation. func MustHaveGoRun(t testing.TB) { if !HasGoRun() { @@ -94,6 +97,100 @@ func GoToolPath(t testing.TB) string { return path } +var ( + gorootOnce sync.Once + gorootPath string + gorootErr error +) + +func findGOROOT() (string, error) { + gorootOnce.Do(func() { + gorootPath = runtime.GOROOT() + if gorootPath != "" { + // If runtime.GOROOT() is non-empty, assume that it is valid. + // + // (It might not be: for example, the user may have explicitly set GOROOT + // to the wrong directory, or explicitly set GOROOT_FINAL but not GOROOT + // and hasn't moved the tree to GOROOT_FINAL yet. But those cases are + // rare, and if that happens the user can fix what they broke.) + return + } + + // runtime.GOROOT doesn't know where GOROOT is (perhaps because the test + // binary was built with -trimpath, or perhaps because GOROOT_FINAL was set + // without GOROOT and the tree hasn't been moved there yet). + // + // Since this is internal/testenv, we can cheat and assume that the caller + // is a test of some package in a subdirectory of GOROOT/src. ('go test' + // runs the test in the directory containing the packaged under test.) That + // means that if we start walking up the tree, we should eventually find + // GOROOT/src/go.mod, and we can report the parent directory of that. + + cwd, err := os.Getwd() + if err != nil { + gorootErr = fmt.Errorf("finding GOROOT: %w", err) + return + } + + dir := cwd + for { + parent := filepath.Dir(dir) + if parent == dir { + // dir is either "." or only a volume name. + gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory") + return + } + + if base := filepath.Base(dir); base != "src" { + dir = parent + continue // dir cannot be GOROOT/src if it doesn't end in "src". + } + + b, err := os.ReadFile(filepath.Join(dir, "go.mod")) + if err != nil { + if os.IsNotExist(err) { + dir = parent + continue + } + gorootErr = fmt.Errorf("finding GOROOT: %w", err) + return + } + goMod := string(b) + + for goMod != "" { + var line string + line, goMod, _ = strings.Cut(goMod, "\n") + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" { + // Found "module std", which is the module declaration in GOROOT/src! + gorootPath = parent + return + } + } + } + }) + + return gorootPath, gorootErr +} + +// GOROOT reports the path to the directory containing the root of the Go +// project source tree. This is normally equivalent to runtime.GOROOT, but +// works even if the test binary was built with -trimpath. +// +// If GOROOT cannot be found, GOROOT skips t if t is non-nil, +// or panics otherwise. +func GOROOT(t testing.TB) string { + path, err := findGOROOT() + if err != nil { + if t == nil { + panic(err) + } + t.Helper() + t.Skip(err) + } + return path +} + // GoTool reports the path to the Go tool. func GoTool() (string, error) { if !HasGoBuild() { @@ -103,7 +200,11 @@ func GoTool() (string, error) { if runtime.GOOS == "windows" { exeSuffix = ".exe" } - path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) + goroot, err := findGOROOT() + if err != nil { + return "", fmt.Errorf("cannot find go tool: %w", err) + } + path := filepath.Join(goroot, "bin", "go"+exeSuffix) if _, err := os.Stat(path); err == nil { return path, nil } @@ -306,3 +407,67 @@ func SkipIfShortAndSlow(t testing.TB) { t.Skipf("skipping test in -short mode on %s", runtime.GOARCH) } } + +// SkipIfOptimizationOff skips t if optimization is disabled. +func SkipIfOptimizationOff(t testing.TB) { + if OptimizationOff() { + t.Helper() + t.Skip("skipping test with optimization disabled") + } +} + +// RunWithTimeout runs cmd and returns its combined output. If the +// subprocess exits with a non-zero status, it will log that status +// and return a non-nil error, but this is not considered fatal. +func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) { + args := cmd.Args + if args == nil { + args = []string{cmd.Path} + } + + var b bytes.Buffer + cmd.Stdout = &b + cmd.Stderr = &b + if err := cmd.Start(); err != nil { + t.Fatalf("starting %s: %v", args, err) + } + + // If the process doesn't complete within 1 minute, + // assume it is hanging and kill it to get a stack trace. + p := cmd.Process + done := make(chan bool) + go func() { + scale := 1 + // This GOARCH/GOOS test is copied from cmd/dist/test.go. + // TODO(iant): Have cmd/dist update the environment variable. + if runtime.GOARCH == "arm" || runtime.GOOS == "windows" { + scale = 2 + } + if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { + if sc, err := strconv.Atoi(s); err == nil { + scale = sc + } + } + + select { + case <-done: + case <-time.After(time.Duration(scale) * time.Minute): + p.Signal(Sigquit) + // If SIGQUIT doesn't do it after a little + // while, kill the process. + select { + case <-done: + case <-time.After(time.Duration(scale) * 30 * time.Second): + p.Signal(os.Kill) + } + } + }() + + err := cmd.Wait() + if err != nil { + t.Logf("%s exit status: %v", args, err) + } + close(done) + + return b.Bytes(), err +} diff --git a/src/internal/testenv/testenv_cgo.go b/src/internal/testenv/testenv_cgo.go index 02f08f57c717c5..7426a29c1a6efc 100644 --- a/src/internal/testenv/testenv_cgo.go +++ b/src/internal/testenv/testenv_cgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo -// +build cgo package testenv diff --git a/src/internal/testenv/testenv_notunix.go b/src/internal/testenv/testenv_notunix.go new file mode 100644 index 00000000000000..180206bc9b701c --- /dev/null +++ b/src/internal/testenv/testenv_notunix.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows || plan9 || (js && wasm) + +package testenv + +import "os" + +// Sigquit is the signal to send to kill a hanging subprocess. +// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill. +var Sigquit = os.Kill diff --git a/src/internal/testenv/testenv_notwin.go b/src/internal/testenv/testenv_notwin.go index 846ec9385647ef..81171fd193ffba 100644 --- a/src/internal/testenv/testenv_notwin.go +++ b/src/internal/testenv/testenv_notwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package testenv diff --git a/src/internal/testenv/testenv_test.go b/src/internal/testenv/testenv_test.go new file mode 100644 index 00000000000000..ebc27f159ad80c --- /dev/null +++ b/src/internal/testenv/testenv_test.go @@ -0,0 +1,53 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testenv_test + +import ( + "internal/testenv" + "os" + "path/filepath" + "runtime" + "testing" +) + +func TestGoToolLocation(t *testing.T) { + testenv.MustHaveGoBuild(t) + + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + + // Tests are defined to run within their package source directory, + // and this package's source directory is $GOROOT/src/internal/testenv. + // The 'go' command is installed at $GOROOT/bin/go, so if the environment + // is correct then testenv.GoTool() should be identical to ../../../bin/go. + + relWant := "../../../bin/go" + exeSuffix + absWant, err := filepath.Abs(relWant) + if err != nil { + t.Fatal(err) + } + + wantInfo, err := os.Stat(absWant) + if err != nil { + t.Fatal(err) + } + t.Logf("found go tool at %q (%q)", relWant, absWant) + + goTool, err := testenv.GoTool() + if err != nil { + t.Fatalf("testenv.GoTool(): %v", err) + } + t.Logf("testenv.GoTool() = %q", goTool) + + gotInfo, err := os.Stat(goTool) + if err != nil { + t.Fatal(err) + } + if !os.SameFile(wantInfo, gotInfo) { + t.Fatalf("%q is not the same file as %q", absWant, goTool) + } +} diff --git a/src/internal/testenv/testenv_unix.go b/src/internal/testenv/testenv_unix.go new file mode 100644 index 00000000000000..a97e88da2f670b --- /dev/null +++ b/src/internal/testenv/testenv_unix.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package testenv + +import "syscall" + +// Sigquit is the signal to send to kill a hanging subprocess. +// Send SIGQUIT to get a stack trace. +var Sigquit = syscall.SIGQUIT diff --git a/src/internal/trace/gc.go b/src/internal/trace/gc.go index cc19fdf8912d29..c1bc86234094b0 100644 --- a/src/internal/trace/gc.go +++ b/src/internal/trace/gc.go @@ -352,11 +352,11 @@ func (h bandUtilHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } -func (h *bandUtilHeap) Push(x interface{}) { +func (h *bandUtilHeap) Push(x any) { *h = append(*h, x.(bandUtil)) } -func (h *bandUtilHeap) Pop() interface{} { +func (h *bandUtilHeap) Pop() any { x := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] return x @@ -386,11 +386,11 @@ func (h utilHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } -func (h *utilHeap) Push(x interface{}) { +func (h *utilHeap) Push(x any) { *h = append(*h, x.(UtilWindow)) } -func (h *utilHeap) Pop() interface{} { +func (h *utilHeap) Pop() any { x := (*h)[len(*h)-1] *h = (*h)[:len(*h)-1] return x diff --git a/src/internal/trace/goroutines.go b/src/internal/trace/goroutines.go index a5fda489bea798..796bc8b03c8f5b 100644 --- a/src/internal/trace/goroutines.go +++ b/src/internal/trace/goroutines.go @@ -4,7 +4,10 @@ package trace -import "sort" +import ( + "sort" + "strings" +) // GDesc contains statistics and execution details of a single goroutine. type GDesc struct { @@ -126,10 +129,17 @@ func (g *GDesc) finalize(lastTs, activeGCStartTime int64, trigger *Event) { finalStat := g.snapshotStat(lastTs, activeGCStartTime) g.GExecutionStat = finalStat - for _, s := range g.activeRegions { - s.End = trigger - s.GExecutionStat = finalStat.sub(s.GExecutionStat) - g.Regions = append(g.Regions, s) + + // System goroutines are never part of regions, even though they + // "inherit" a task due to creation (EvGoCreate) from within a region. + // This may happen e.g. if the first GC is triggered within a region, + // starting the GC worker goroutines. + if !IsSystemGoroutine(g.Name) { + for _, s := range g.activeRegions { + s.End = trigger + s.GExecutionStat = finalStat.sub(s.GExecutionStat) + g.Regions = append(g.Regions, s) + } } *(g.gdesc) = gdesc{} } @@ -158,10 +168,13 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { case EvGoCreate: g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)} g.blockSchedTime = ev.Ts - // When a goroutine is newly created, inherit the - // task of the active region. For ease handling of - // this case, we create a fake region description with - // the task id. + // When a goroutine is newly created, inherit the task + // of the active region. For ease handling of this + // case, we create a fake region description with the + // task id. This isn't strictly necessary as this + // goroutine may not be associated with the task, but + // it can be convenient to see all children created + // during a region. if creatorG := gs[ev.G]; creatorG != nil && len(creatorG.gdesc.activeRegions) > 0 { regions := creatorG.gdesc.activeRegions s := regions[len(regions)-1] @@ -336,3 +349,9 @@ func RelatedGoroutines(events []*Event, goid uint64) map[uint64]bool { gmap[0] = true // for GC events return gmap } + +func IsSystemGoroutine(entryFn string) bool { + // This mimics runtime.isSystemGoroutine as closely as + // possible. + return entryFn != "runtime.main" && strings.HasPrefix(entryFn, "runtime.") +} diff --git a/src/internal/trace/mkcanned.bash b/src/internal/trace/mkcanned.bash index b365b909c8cf5e..879cf1c500fce0 100755 --- a/src/internal/trace/mkcanned.bash +++ b/src/internal/trace/mkcanned.bash @@ -13,8 +13,8 @@ if [ $# != 1 ]; then exit 1 fi -go test -run ClientServerParallel4 -trace "testdata/http_$1_good" net/http -go test -run 'TraceStress$|TraceStressStartStop$|TestUserTaskSpan$' runtime/trace -savetraces +go test -run '^$' -bench ClientServerParallel4 -benchtime 10x -trace "testdata/http_$1_good" net/http +go test -run 'TraceStress$|TraceStressStartStop$|TestUserTaskRegion$' runtime/trace -savetraces mv ../../runtime/trace/TestTraceStress.trace "testdata/stress_$1_good" mv ../../runtime/trace/TestTraceStressStartStop.trace "testdata/stress_start_stop_$1_good" -mv ../../runtime/trace/TestUserTaskSpan.trace "testdata/user_task_span_$1_good" +mv ../../runtime/trace/TestUserTaskRegion.trace "testdata/user_task_region_$1_good" diff --git a/src/internal/trace/order.go b/src/internal/trace/order.go index 36ed58d675aa7a..07a6e13ffef985 100644 --- a/src/internal/trace/order.go +++ b/src/internal/trace/order.go @@ -52,6 +52,12 @@ const ( // incorrect (condition observed on some machines). func order1007(m map[int][]*Event) (events []*Event, err error) { pending := 0 + // The ordering of CPU profile sample events in the data stream is based on + // when each run of the signal handler was able to acquire the spinlock, + // with original timestamps corresponding to when ReadTrace pulled the data + // off of the profBuf queue. Re-sort them by the timestamp we captured + // inside the signal handler. + sort.Stable(eventList(m[ProfileP])) var batches []*eventBatch for _, v := range m { pending += len(v) diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index 254f20137b4ee7..b091a85f6afdb2 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -75,6 +75,7 @@ const ( NetpollP // depicts network unblocks SyscallP // depicts returns from syscalls GCP // depicts GC state + ProfileP // depicts recording of CPU profile samples ) // ParseResult is the result of Parse. @@ -150,9 +151,9 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri return } switch ver { - case 1005, 1007, 1008, 1009, 1010, 1011: - // Note: When adding a new version, add canned traces - // from the old version to the test suite using mkcanned.bash. + case 1005, 1007, 1008, 1009, 1010, 1011, 1019: + // Note: When adding a new version, confirm that canned traces from the + // old version are part of the test suite. Add them using mkcanned.bash. break default: err = fmt.Errorf("unsupported trace file version %v.%v (update Go toolchain) %v", ver/1000, ver%1000, ver) @@ -448,8 +449,27 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even case EvUserLog: // e.Args 0: taskID, 1:keyID, 2: stackID e.SArgs = []string{strings[e.Args[1]], raw.sargs[0]} + case EvCPUSample: + e.Ts = int64(e.Args[0]) + e.P = int(e.Args[1]) + e.G = e.Args[2] + e.Args[0] = 0 + } + switch raw.typ { + default: + batches[lastP] = append(batches[lastP], e) + case EvCPUSample: + // Most events are written out by the active P at the exact + // moment they describe. CPU profile samples are different + // because they're written to the tracing log after some delay, + // by a separate worker goroutine, into a separate buffer. + // + // We keep these in their own batch until all of the batches are + // merged in timestamp order. We also (right before the merge) + // re-sort these events by the timestamp captured in the + // profiling signal handler. + batches[ProfileP] = append(batches[ProfileP], e) } - batches[lastP] = append(batches[lastP], e) } } if len(batches) == 0 { @@ -953,7 +973,7 @@ func PrintEvent(ev *Event) { func (ev *Event) String() string { desc := EventDescriptions[ev.Type] - w := new(bytes.Buffer) + w := new(strings.Builder) fmt.Fprintf(w, "%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off) for i, a := range desc.Args { fmt.Fprintf(w, " %v=%v", a, ev.Args[i]) @@ -1058,7 +1078,8 @@ const ( EvUserTaskEnd = 46 // end of task [timestamp, internal task id, stack] EvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), stack, name string] EvUserLog = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] - EvCount = 49 + EvCPUSample = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] + EvCount = 50 ) var EventDescriptions = [EvCount]struct { @@ -1117,4 +1138,5 @@ var EventDescriptions = [EvCount]struct { EvUserTaskEnd: {"UserTaskEnd", 1011, true, []string{"taskid"}, nil}, EvUserRegion: {"UserRegion", 1011, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, EvUserLog: {"UserLog", 1011, true, []string{"id", "keyid"}, []string{"category", "message"}}, + EvCPUSample: {"CPUSample", 1019, true, []string{"ts", "p", "g"}, nil}, } diff --git a/src/internal/trace/testdata/http_1_19_good b/src/internal/trace/testdata/http_1_19_good new file mode 100644 index 00000000000000..c1d519e7d5da8d Binary files /dev/null and b/src/internal/trace/testdata/http_1_19_good differ diff --git a/src/internal/trace/testdata/stress_1_19_good b/src/internal/trace/testdata/stress_1_19_good new file mode 100644 index 00000000000000..13f59268e669e9 Binary files /dev/null and b/src/internal/trace/testdata/stress_1_19_good differ diff --git a/src/internal/trace/testdata/stress_start_stop_1_19_good b/src/internal/trace/testdata/stress_start_stop_1_19_good new file mode 100644 index 00000000000000..92d92789c4f256 Binary files /dev/null and b/src/internal/trace/testdata/stress_start_stop_1_19_good differ diff --git a/src/internal/trace/testdata/user_task_span_1_11_good b/src/internal/trace/testdata/user_task_region_1_11_good similarity index 100% rename from src/internal/trace/testdata/user_task_span_1_11_good rename to src/internal/trace/testdata/user_task_region_1_11_good diff --git a/src/internal/trace/testdata/user_task_region_1_19_good b/src/internal/trace/testdata/user_task_region_1_19_good new file mode 100644 index 00000000000000..1daa3b25a6c64d Binary files /dev/null and b/src/internal/trace/testdata/user_task_region_1_19_good differ diff --git a/src/cmd/go/internal/txtar/archive.go b/src/internal/txtar/archive.go similarity index 92% rename from src/cmd/go/internal/txtar/archive.go rename to src/internal/txtar/archive.go index 17966848771b3c..81b31454512a11 100644 --- a/src/cmd/go/internal/txtar/archive.go +++ b/src/internal/txtar/archive.go @@ -6,15 +6,15 @@ // // The goals for the format are: // -// - be trivial enough to create and edit by hand. -// - be able to store trees of text files describing go command test cases. -// - diff nicely in git history and code reviews. +// - be trivial enough to create and edit by hand. +// - be able to store trees of text files describing go command test cases. +// - diff nicely in git history and code reviews. // // Non-goals include being a completely general archive format, // storing binary data, storing file modes, storing special files like // symbolic links, and so on. // -// Txtar format +// # Txtar format // // A txtar archive is zero or more comment lines and then a sequence of file entries. // Each file entry begins with a file marker line of the form "-- FILENAME --" @@ -34,7 +34,7 @@ package txtar import ( "bytes" "fmt" - "os" + "io/ioutil" "strings" ) @@ -66,7 +66,7 @@ func Format(a *Archive) []byte { // ParseFile parses the named file as an archive. func ParseFile(file string) (*Archive, error) { - data, err := os.ReadFile(file) + data, err := ioutil.ReadFile(file) if err != nil { return nil, err } @@ -121,7 +121,7 @@ func isMarker(data []byte) (name string, after []byte) { if i := bytes.IndexByte(data, '\n'); i >= 0 { data, after = data[:i], data[i+1:] } - if !bytes.HasSuffix(data, markerEnd) { + if !(bytes.HasSuffix(data, markerEnd) && len(data) >= len(marker)+len(markerEnd)) { return "", nil } return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after diff --git a/src/cmd/compile/internal/types2/testdata/check/blank.src b/src/internal/types/testdata/check/blank.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/blank.src rename to src/internal/types/testdata/check/blank.go diff --git a/src/internal/types/testdata/check/builtins0.go b/src/internal/types/testdata/check/builtins0.go new file mode 100644 index 00000000000000..f4932a8309eef7 --- /dev/null +++ b/src/internal/types/testdata/check/builtins0.go @@ -0,0 +1,956 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// builtin calls + +package builtins + +import "unsafe" + +func f0() {} + +func append1() { + var b byte + var x int + var s []byte + _ = append() // ERROR not enough arguments + _ = append("foo" /* ERROR must be a slice */ ) + _ = append(nil /* ERROR must be a slice */ , s) + _ = append(x /* ERROR must be a slice */ , s) + _ = append(s) + _ = append(s, nil...) + append /* ERROR not used */ (s) + + _ = append(s, b) + _ = append(s, x /* ERROR cannot use x */ ) + _ = append(s, s /* ERROR cannot use s */ ) + _ = append(s...) /* ERROR not enough arguments */ + _ = append(s, b, s /* ERROR too many arguments */ ...) + _ = append(s, 1, 2, 3) + _ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6) + _ = append(s, 1, 2 /* ERROR too many arguments */, s...) + _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) + + type S []byte + type T string + var t T + _ = append(s, "foo" /* ERROR cannot use .* in argument to append */ ) + _ = append(s, "foo"...) + _ = append(S(s), "foo" /* ERROR cannot use .* in argument to append */ ) + _ = append(S(s), "foo"...) + _ = append(s, t /* ERROR cannot use t */ ) + _ = append(s, t...) + _ = append(s, T("foo")...) + _ = append(S(s), t /* ERROR cannot use t */ ) + _ = append(S(s), t...) + _ = append(S(s), T("foo")...) + _ = append([]string{}, t /* ERROR cannot use t */ , "foo") + _ = append([]T{}, t, "foo") +} + +// from the spec +func append2() { + s0 := []int{0, 0} + s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2} + s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7} + s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} + s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} + + var t []interface{} + t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} + + var b []byte + b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' } + + _ = s4 +} + +func append3() { + f1 := func() (s []int) { return } + f2 := func() (s []int, x int) { return } + f3 := func() (s []int, x, y int) { return } + f5 := func() (s []interface{}, x int, y float32, z string, b bool) { return } + ff := func() (int, float32) { return 0, 0 } + _ = append(f0 /* ERROR used as value */ ()) + _ = append(f1()) + _ = append(f2()) + _ = append(f3()) + _ = append(f5()) + _ = append(ff /* ERROR must be a slice */ ()) // TODO(gri) better error message +} + +func cap1() { + var a [10]bool + var p *[20]int + var c chan string + _ = cap() // ERROR not enough arguments + _ = cap(1, 2) // ERROR too many arguments + _ = cap(42 /* ERROR invalid */) + const _3 = cap(a) + assert(_3 == 10) + const _4 = cap(p) + assert(_4 == 20) + _ = cap(c) + cap /* ERROR not used */ (c) + + // issue 4744 + type T struct{ a [10]int } + const _ = cap(((*T)(nil)).a) + + var s [][]byte + _ = cap(s) + _ = cap(s... /* ERROR invalid use of \.\.\. */ ) +} + +func cap2() { + f1a := func() (a [10]int) { return } + f1s := func() (s []int) { return } + f2 := func() (s []int, x int) { return } + _ = cap(f0 /* ERROR used as value */ ()) + _ = cap(f1a()) + _ = cap(f1s()) + _ = cap(f2()) // ERROR too many arguments +} + +// test cases for issue 7387 +func cap3() { + var f = func() int { return 0 } + var x = f() + const ( + _ = cap([4]int{}) + _ = cap([4]int{x}) + _ = cap /* ERROR not constant */ ([4]int{f()}) + _ = cap /* ERROR not constant */ ([4]int{cap([]int{})}) + _ = cap([4]int{cap([4]int{})}) + ) + var y float64 + var z complex128 + const ( + _ = cap([4]float64{}) + _ = cap([4]float64{y}) + _ = cap([4]float64{real(2i)}) + _ = cap /* ERROR not constant */ ([4]float64{real(z)}) + ) + var ch chan [10]int + const ( + _ = cap /* ERROR not constant */ (<-ch) + _ = cap /* ERROR not constant */ ([4]int{(<-ch)[0]}) + ) +} + +func close1() { + var c chan int + var r <-chan int + close() // ERROR not enough arguments + close(1, 2) // ERROR too many arguments + close(42 /* ERROR cannot close non-channel */) + close(r /* ERROR receive-only channel */) + close(c) + _ = close /* ERROR used as value */ (c) + + var s []chan int + close(s... /* ERROR invalid use of \.\.\. */ ) +} + +func close2() { + f1 := func() (ch chan int) { return } + f2 := func() (ch chan int, x int) { return } + close(f0 /* ERROR used as value */ ()) + close(f1()) + close(f2()) // ERROR too many arguments +} + +func complex1() { + var i32 int32 + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + _ = complex() // ERROR not enough arguments + _ = complex(1) // ERROR not enough arguments + _ = complex(true /* ERROR mismatched types */ , 0) + _ = complex(i32 /* ERROR expected floating-point */ , 0) + _ = complex("foo" /* ERROR mismatched types */ , 0) + _ = complex(c64 /* ERROR expected floating-point */ , 0) + _ = complex(0 /* ERROR mismatched types */ , true) + _ = complex(0 /* ERROR expected floating-point */ , i32) + _ = complex(0 /* ERROR mismatched types */ , "foo") + _ = complex(0 /* ERROR expected floating-point */ , c64) + _ = complex(f32, f32) + _ = complex(f32, 1) + _ = complex(f32, 1.0) + _ = complex(f32, 'a') + _ = complex(f64, f64) + _ = complex(f64, 1) + _ = complex(f64, 1.0) + _ = complex(f64, 'a') + _ = complex(f32 /* ERROR mismatched types */ , f64) + _ = complex(f64 /* ERROR mismatched types */ , f32) + _ = complex(1, 1) + _ = complex(1, 1.1) + _ = complex(1, 'a') + complex /* ERROR not used */ (1, 2) + + var _ complex64 = complex(f32, f32) + var _ complex64 = complex /* ERROR cannot use .* in variable declaration */ (f64, f64) + + var _ complex128 = complex /* ERROR cannot use .* in variable declaration */ (f32, f32) + var _ complex128 = complex(f64, f64) + + // untyped constants + const _ int = complex(1, 0) + const _ float32 = complex(1, 0) + const _ complex64 = complex(1, 0) + const _ complex128 = complex(1, 0) + const _ = complex(0i, 0i) + const _ = complex(0i, 0) + const _ int = 1.0 + complex(1, 0i) + + const _ int = complex /* ERROR int */ (1.1, 0) + const _ float32 = complex /* ERROR float32 */ (1, 2) + + // untyped values + var s uint + _ = complex(1 /* ERROR integer */ <X, T2->X + ) + + var t T3 + _ = t.X /* ERROR "ambiguous selector t.X" */ +} + +func _() { + type ( + T1 struct { X int } + T2 struct { T1 } + T3 struct { T1 } + T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X + ) + + var t T4 + _ = t.X /* ERROR "ambiguous selector t.X" */ +} + +func issue4355() { + type ( + T1 struct {X int} + T2 struct {T1} + T3 struct {T2} + T4 struct {T2} + T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X + ) + + var t T5 + _ = t.X /* ERROR "ambiguous selector t.X" */ +} + +func _() { + type State int + type A struct{ State } + type B struct{ fmt.State } + type T struct{ A; B } + + var t T + _ = t.State /* ERROR "ambiguous selector t.State" */ +} + +// Embedded fields can be predeclared types. + +func _() { + type T0 struct{ + int + float32 + f int + } + var x T0 + _ = x.int + _ = x.float32 + _ = x.f + + type T1 struct{ + T0 + } + var y T1 + _ = y.int + _ = y.float32 + _ = y.f +} + +// Restrictions on embedded field types. + +func _() { + type I1 interface{} + type I2 interface{} + type P1 *int + type P2 *int + type UP unsafe.Pointer + + type T1 struct { + I1 + * /* ERROR "cannot be a pointer to an interface" */ I2 + * /* ERROR "cannot be a pointer to an interface" */ error + P1 /* ERROR "cannot be a pointer" */ + * /* ERROR "cannot be a pointer" */ P2 + } + + // unsafe.Pointers are treated like regular pointers when embedded + type T2 struct { + unsafe /* ERROR "cannot be unsafe.Pointer" */ .Pointer + */* ERROR "cannot be unsafe.Pointer" */ /* ERROR "Pointer redeclared" */ unsafe.Pointer + UP /* ERROR "cannot be unsafe.Pointer" */ + * /* ERROR "cannot be unsafe.Pointer" */ /* ERROR "UP redeclared" */ UP + } +} + +// Named types that are pointers. + +type S struct{ x int } +func (*S) m() {} +type P *S + +func _() { + var s *S + _ = s.x + _ = s.m + + var p P + _ = p.x + _ = p.m /* ERROR "no field or method" */ + _ = P.m /* ERROR "no field or method" */ +} + +// Borrowed from the FieldByName test cases in reflect/all_test.go. + +type D1 struct { + d int +} +type D2 struct { + d int +} + +type S0 struct { + A, B, C int + D1 + D2 +} + +type S1 struct { + B int + S0 +} + +type S2 struct { + A int + *S1 +} + +type S1x struct { + S1 +} + +type S1y struct { + S1 +} + +type S3 struct { + S1x + S2 + D, E int + *S1y +} + +type S4 struct { + *S4 + A int +} + +// The X in S6 and S7 annihilate, but they also block the X in S8.S9. +type S5 struct { + S6 + S7 + S8 +} + +type S6 struct { + X int +} + +type S7 S6 + +type S8 struct { + S9 +} + +type S9 struct { + X int + Y int +} + +// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. +type S10 struct { + S11 + S12 + S13 +} + +type S11 struct { + S6 +} + +type S12 struct { + S6 +} + +type S13 struct { + S8 +} + +func _() { + _ = struct{}{}.Foo /* ERROR "no field or method" */ + _ = S0{}.A + _ = S0{}.D /* ERROR "no field or method" */ + _ = S1{}.A + _ = S1{}.B + _ = S1{}.S0 + _ = S1{}.C + _ = S2{}.A + _ = S2{}.S1 + _ = S2{}.B + _ = S2{}.C + _ = S2{}.D /* ERROR "no field or method" */ + _ = S3{}.S1 /* ERROR "ambiguous selector S3{}.S1" */ + _ = S3{}.A + _ = S3{}.B /* ERROR "ambiguous selector" S3{}.B */ + _ = S3{}.D + _ = S3{}.E + _ = S4{}.A + _ = S4{}.B /* ERROR "no field or method" */ + _ = S5{}.X /* ERROR "ambiguous selector S5{}.X" */ + _ = S5{}.Y + _ = S10{}.X /* ERROR "ambiguous selector S10{}.X" */ + _ = S10{}.Y +} + +// Borrowed from the FieldByName benchmark in reflect/all_test.go. + +type R0 struct { + *R1 + *R2 + *R3 + *R4 +} + +type R1 struct { + *R5 + *R6 + *R7 + *R8 +} + +type R2 R1 +type R3 R1 +type R4 R1 + +type R5 struct { + *R9 + *R10 + *R11 + *R12 +} + +type R6 R5 +type R7 R5 +type R8 R5 + +type R9 struct { + *R13 + *R14 + *R15 + *R16 +} + +type R10 R9 +type R11 R9 +type R12 R9 + +type R13 struct { + *R17 + *R18 + *R19 + *R20 +} + +type R14 R13 +type R15 R13 +type R16 R13 + +type R17 struct { + *R21 + *R22 + *R23 + *R24 +} + +type R18 R17 +type R19 R17 +type R20 R17 + +type R21 struct { + X int +} + +type R22 R21 +type R23 R21 +type R24 R21 + +var _ = R0{}.X /* ERROR "ambiguous selector R0{}.X" */ \ No newline at end of file diff --git a/src/internal/types/testdata/check/decls4.go b/src/internal/types/testdata/check/decls4.go new file mode 100644 index 00000000000000..2ce180fbbbfa0e --- /dev/null +++ b/src/internal/types/testdata/check/decls4.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. + +// type aliases + +package decls4 + +type ( + T0 [10]int + T1 []byte + T2 struct { + x int + } + T3 interface{ + m() T2 + } + T4 func(int, T0) chan T2 +) + +type ( + Ai = int + A0 = T0 + A1 = T1 + A2 = T2 + A3 = T3 + A4 = T4 + + A10 = [10]int + A11 = []byte + A12 = struct { + x int + } + A13 = interface{ + m() A2 + } + A14 = func(int, A0) chan A2 +) + +// check assignment compatibility due to equality of types +var ( + xi_ int + ai Ai = xi_ + + x0 T0 + a0 A0 = x0 + + x1 T1 + a1 A1 = x1 + + x2 T2 + a2 A2 = x2 + + x3 T3 + a3 A3 = x3 + + x4 T4 + a4 A4 = x4 +) + +// alias receiver types +func (Ai /* ERROR "cannot define new methods on non-local type int" */) m1() {} +func (T0) m1() {} +func (A0) m1 /* ERROR already declared */ () {} +func (A0) m2 () {} +func (A3 /* ERROR invalid receiver */ ) m1 () {} +func (A10 /* ERROR invalid receiver */ ) m1() {} + +// x0 has methods m1, m2 declared via receiver type names T0 and A0 +var _ interface{ m1(); m2() } = x0 + +// alias receiver types (test case for issue #23042) +type T struct{} + +var ( + _ = T.m + _ = T{}.m + _ interface{m()} = T{} +) + +var ( + _ = T.n + _ = T{}.n + _ interface{m(); n()} = T{} +) + +type U = T +func (U) m() {} + +// alias receiver types (long type declaration chains) +type ( + V0 = V1 + V1 = (V2) + V2 = ((V3)) + V3 = T +) + +func (V0) m /* ERROR already declared */ () {} +func (V1) n() {} + +// alias receiver types (invalid due to cycles) +type ( + W0 /* ERROR illegal cycle */ = W1 + W1 = (W2) + W2 = ((W0)) +) + +func (W0) m() {} // no error expected (due to above cycle error) +func (W1) n() {} + +// alias receiver types (invalid due to builtin underlying type) +type ( + B0 = B1 + B1 = B2 + B2 = int +) + +func (B0 /* ERROR cannot define new methods on non-local type int */ ) m() {} +func (B1 /* ERROR cannot define new methods on non-local type int */ ) n() {} + +// cycles +type ( + C2 /* ERROR illegal cycle */ = C2 + C3 /* ERROR illegal cycle */ = C4 + C4 = C3 + C5 struct { + f *C6 + } + C6 = C5 + C7 /* ERROR illegal cycle */ struct { + f C8 + } + C8 = C7 +) + +// embedded fields +var ( + s0 struct { T0 } + s1 struct { A0 } = s0 /* ERROR cannot use */ // embedded field names are different +) + +// embedding and lookup of fields and methods +func _(s struct{A0}) { s.A0 = x0 } + +type eX struct{xf int} + +func (eX) xm() + +type eY = struct{eX} // field/method set of eY includes xf, xm + +type eZ = *struct{eX} // field/method set of eZ includes xf, xm + +type eA struct { + eX // eX contributes xf, xm to eA +} + +type eA2 struct { + *eX // *eX contributes xf, xm to eA +} + +type eB struct { + eY // eY contributes xf, xm to eB +} + +type eB2 struct { + *eY // *eY contributes xf, xm to eB +} + +type eC struct { + eZ // eZ contributes xf, xm to eC +} + +var ( + _ = eA{}.xf + _ = eA{}.xm + _ = eA2{}.xf + _ = eA2{}.xm + _ = eB{}.xf + _ = eB{}.xm + _ = eB2{}.xf + _ = eB2{}.xm + _ = eC{}.xf + _ = eC{}.xm +) + +// ambiguous selectors due to embedding via type aliases +type eD struct { + eY + eZ +} + +var ( + _ = eD{}.xf /* ERROR ambiguous selector eD{}.xf */ + _ = eD{}.xm /* ERROR ambiguous selector eD{}.xm */ +) + +var ( + _ interface{ xm() } = eD /* ERROR missing method xm */ {} +) \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/decls5.src b/src/internal/types/testdata/check/decls5.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/decls5.src rename to src/internal/types/testdata/check/decls5.go diff --git a/src/internal/types/testdata/check/errors.go b/src/internal/types/testdata/check/errors.go new file mode 100644 index 00000000000000..7cdc5fb5fff311 --- /dev/null +++ b/src/internal/types/testdata/check/errors.go @@ -0,0 +1,66 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errors + +// Testing precise operand formatting in error messages +// (matching messages are regular expressions, hence the \'s). +func f(x int, m map[string]int) { + // no values + _ = f /* ERROR f\(0, m\) \(no value\) used as value */ (0, m) + + // built-ins + _ = println // ERROR println \(built-in\) must be called + + // types + _ = complex128 // ERROR complex128 \(type\) is not an expression + + // constants + const c1 = 991 + const c2 float32 = 0.5 + const c3 = "foo" + 0 // ERROR 0 \(untyped int constant\) is not used + 0.5 // ERROR 0.5 \(untyped float constant\) is not used + "foo" // ERROR "foo" \(untyped string constant\) is not used + c1 // ERROR c1 \(untyped int constant 991\) is not used + c2 // ERROR c2 \(constant 0.5 of type float32\) is not used + c1 /* ERROR c1 \+ c2 \(constant 991.5 of type float32\) is not used */ + c2 + c3 // ERROR c3 \(untyped string constant "foo"\) is not used + + // variables + x // ERROR x \(variable of type int\) is not used + + // values + nil // ERROR nil is not used + ( /* ERROR \(\*int\)\(nil\) \(value of type \*int\) is not used */ *int)(nil) + x /* ERROR x != x \(untyped bool value\) is not used */ != x + x /* ERROR x \+ x \(value of type int\) is not used */ + x + + // value, ok's + const s = "foo" + m /* ERROR m\[s\] \(map index expression of type int\) is not used */ [s] +} + +// Valid ERROR comments can have a variety of forms. +func _() { + 0 /* ERROR "0 .* is not used" */ + 0 /* ERROR 0 .* is not used */ + 0 // ERROR "0 .* is not used" + 0 // ERROR 0 .* is not used +} + +// Don't report spurious errors as a consequence of earlier errors. +// Add more tests as needed. +func _() { + if err := foo /* ERROR undeclared */ (); err != nil /* no error here */ {} +} + +// Use unqualified names for package-local objects. +type T struct{} +var _ int = T /* ERROR value of type T */ {} // use T in error message rather then errors.T + +// Don't report errors containing "invalid type" (issue #24182). +func _(x *missing /* ERROR undeclared name: missing */ ) { + x.m() // there shouldn't be an error here referring to *invalid type +} diff --git a/src/internal/types/testdata/check/expr0.go b/src/internal/types/testdata/check/expr0.go new file mode 100644 index 00000000000000..19923777df1d47 --- /dev/null +++ b/src/internal/types/testdata/check/expr0.go @@ -0,0 +1,187 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// unary expressions + +package expr0 + +type mybool bool + +var ( + // bool + b0 = true + b1 bool = b0 + b2 = !true + b3 = !b1 + b4 bool = !true + b5 bool = !b4 + b6 = +b0 /* ERROR "not defined" */ + b7 = -b0 /* ERROR "not defined" */ + b8 = ^b0 /* ERROR "not defined" */ + b9 = *b0 /* ERROR "cannot indirect" */ + b10 = &true /* ERROR "cannot take address" */ + b11 = &b0 + b12 = <-b0 /* ERROR "cannot receive" */ + b13 = & & /* ERROR "cannot take address" */ b0 + + // byte + _ = byte(0) + _ = byte(- /* ERROR "cannot convert" */ 1) + _ = - /* ERROR "-byte\(1\) \(constant -1 of type byte\) overflows byte" */ byte(1) // test for issue 11367 + _ = byte /* ERROR "overflows byte" */ (0) - byte(1) + + // int + i0 = 1 + i1 int = i0 + i2 = +1 + i3 = +i0 + i4 int = +1 + i5 int = +i4 + i6 = -1 + i7 = -i0 + i8 int = -1 + i9 int = -i4 + i10 = !i0 /* ERROR "not defined" */ + i11 = ^1 + i12 = ^i0 + i13 int = ^1 + i14 int = ^i4 + i15 = *i0 /* ERROR "cannot indirect" */ + i16 = &i0 + i17 = *i16 + i18 = <-i16 /* ERROR "cannot receive" */ + + // uint + u0 = uint(1) + u1 uint = u0 + u2 = +1 + u3 = +u0 + u4 uint = +1 + u5 uint = +u4 + u6 = -1 + u7 = -u0 + u8 uint = - /* ERROR "overflows" */ 1 + u9 uint = -u4 + u10 = !u0 /* ERROR "not defined" */ + u11 = ^1 + u12 = ^i0 + u13 uint = ^ /* ERROR "overflows" */ 1 + u14 uint = ^u4 + u15 = *u0 /* ERROR "cannot indirect" */ + u16 = &u0 + u17 = *u16 + u18 = <-u16 /* ERROR "cannot receive" */ + u19 = ^uint(0) + + // float64 + f0 = float64(1) + f1 float64 = f0 + f2 = +1 + f3 = +f0 + f4 float64 = +1 + f5 float64 = +f4 + f6 = -1 + f7 = -f0 + f8 float64 = -1 + f9 float64 = -f4 + f10 = !f0 /* ERROR "not defined" */ + f11 = ^1 + f12 = ^i0 + f13 float64 = ^1 + f14 float64 = ^f4 /* ERROR "not defined" */ + f15 = *f0 /* ERROR "cannot indirect" */ + f16 = &f0 + f17 = *u16 + f18 = <-u16 /* ERROR "cannot receive" */ + + // complex128 + c0 = complex128(1) + c1 complex128 = c0 + c2 = +1 + c3 = +c0 + c4 complex128 = +1 + c5 complex128 = +c4 + c6 = -1 + c7 = -c0 + c8 complex128 = -1 + c9 complex128 = -c4 + c10 = !c0 /* ERROR "not defined" */ + c11 = ^1 + c12 = ^i0 + c13 complex128 = ^1 + c14 complex128 = ^c4 /* ERROR "not defined" */ + c15 = *c0 /* ERROR "cannot indirect" */ + c16 = &c0 + c17 = *u16 + c18 = <-u16 /* ERROR "cannot receive" */ + + // string + s0 = "foo" + s1 = +"foo" /* ERROR "not defined" */ + s2 = -s0 /* ERROR "not defined" */ + s3 = !s0 /* ERROR "not defined" */ + s4 = ^s0 /* ERROR "not defined" */ + s5 = *s4 + s6 = &s4 + s7 = *s6 + s8 = <-s7 + + // channel + ch chan int + rc <-chan float64 + sc chan <- string + ch0 = +ch /* ERROR "not defined" */ + ch1 = -ch /* ERROR "not defined" */ + ch2 = !ch /* ERROR "not defined" */ + ch3 = ^ch /* ERROR "not defined" */ + ch4 = *ch /* ERROR "cannot indirect" */ + ch5 = &ch + ch6 = *ch5 + ch7 = <-ch + ch8 = <-rc + ch9 = <-sc /* ERROR "cannot receive" */ + ch10, ok = <-ch + // ok is of type bool + ch11, myok = <-ch + _ mybool = myok /* ERROR "cannot use .* in variable declaration" */ +) + +// address of composite literals +type T struct{x, y int} + +func f() T { return T{} } + +var ( + _ = &T{1, 2} + _ = &[...]int{} + _ = &[]int{} + _ = &[]int{} + _ = &map[string]T{} + _ = &(T{1, 2}) + _ = &((((T{1, 2})))) + _ = &f /* ERROR "cannot take address" */ () +) + +// recursive pointer types +type P *P + +var ( + p1 P = new(P) + p2 P = *p1 + p3 P = &p2 +) + +func g() (a, b int) { return } + +func _() { + _ = -g /* ERROR 2-valued g */ () + _ = <-g /* ERROR 2-valued g */ () +} + +// ~ is accepted as unary operator only permitted in interface type elements +var ( + _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ 0 + _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ "foo" + _ = ~ /* ERROR cannot use ~ outside of interface or type constraint */ i0 +) diff --git a/src/internal/types/testdata/check/expr1.go b/src/internal/types/testdata/check/expr1.go new file mode 100644 index 00000000000000..42b95fbb37bec2 --- /dev/null +++ b/src/internal/types/testdata/check/expr1.go @@ -0,0 +1,127 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// binary expressions + +package expr1 + +type mybool bool + +func _(x, y bool, z mybool) { + x = x || y + x = x || true + x = x || false + x = x && y + x = x && true + x = x && false + + z = z /* ERROR mismatched types */ || y + z = z || true + z = z || false + z = z /* ERROR mismatched types */ && y + z = z && true + z = z && false +} + +type myint int + +func _(x, y int, z myint) { + x = x + 1 + x = x + 1.0 + x = x + 1.1 // ERROR truncated to int + x = x + y + x = x - y + x = x * y + x = x / y + x = x % y + x = x << y + x = x >> y + + z = z + 1 + z = z + 1.0 + z = z + 1.1 // ERROR truncated to int + z = z /* ERROR mismatched types */ + y + z = z /* ERROR mismatched types */ - y + z = z /* ERROR mismatched types */ * y + z = z /* ERROR mismatched types */ / y + z = z /* ERROR mismatched types */ % y + z = z << y + z = z >> y +} + +type myuint uint + +func _(x, y uint, z myuint) { + x = x + 1 + x = x + - /* ERROR overflows uint */ 1 + x = x + 1.0 + x = x + 1.1 // ERROR truncated to uint + x = x + y + x = x - y + x = x * y + x = x / y + x = x % y + x = x << y + x = x >> y + + z = z + 1 + z = x + - /* ERROR overflows uint */ 1 + z = z + 1.0 + z = z + 1.1 // ERROR truncated to uint + z = z /* ERROR mismatched types */ + y + z = z /* ERROR mismatched types */ - y + z = z /* ERROR mismatched types */ * y + z = z /* ERROR mismatched types */ / y + z = z /* ERROR mismatched types */ % y + z = z << y + z = z >> y +} + +type myfloat64 float64 + +func _(x, y float64, z myfloat64) { + x = x + 1 + x = x + -1 + x = x + 1.0 + x = x + 1.1 + x = x + y + x = x - y + x = x * y + x = x / y + x = x /* ERROR not defined */ % y + x = x /* ERROR operand x .* must be integer */ << y + x = x /* ERROR operand x .* must be integer */ >> y + + z = z + 1 + z = z + -1 + z = z + 1.0 + z = z + 1.1 + z = z /* ERROR mismatched types */ + y + z = z /* ERROR mismatched types */ - y + z = z /* ERROR mismatched types */ * y + z = z /* ERROR mismatched types */ / y + z = z /* ERROR mismatched types */ % y + z = z /* ERROR operand z .* must be integer */ << y + z = z /* ERROR operand z .* must be integer */ >> y +} + +type mystring string + +func _(x, y string, z mystring) { + x = x + "foo" + x = x /* ERROR not defined */ - "foo" + x = x /* ERROR mismatched types string and untyped int */ + 1 + x = x + y + x = x /* ERROR not defined */ - y + x = x /* ERROR mismatched types string and untyped int */* 10 +} + +func f() (a, b int) { return } + +func _(x int) { + _ = f /* ERROR 2-valued f */ () + 1 + _ = x + f /* ERROR 2-valued f */ () + _ = f /* ERROR 2-valued f */ () + f + _ = f /* ERROR 2-valued f */ () + f /* ERROR 2-valued f */ () +} diff --git a/src/internal/types/testdata/check/expr2.go b/src/internal/types/testdata/check/expr2.go new file mode 100644 index 00000000000000..6133dbb42bfeda --- /dev/null +++ b/src/internal/types/testdata/check/expr2.go @@ -0,0 +1,260 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// comparisons + +package expr2 + +func _bool() { + const t = true == true + const f = true == false + _ = t /* ERROR cannot compare */ < f + _ = 0 /* ERROR mismatched types untyped int and untyped bool */ == t + var b bool + var x, y float32 + b = x < y + _ = b + _ = struct{b bool}{x < y} +} + +// corner cases +var ( + v0 = nil == nil // ERROR operator == not defined on untyped nil +) + +func arrays() { + // basics + var a, b [10]int + _ = a == b + _ = a != b + _ = a /* ERROR < not defined */ < b + _ = a /* ERROR cannot compare.*mismatched types */ == nil + + type C [10]int + var c C + _ = a == c + + type D [10]int + var d D + _ = c /* ERROR mismatched types */ == d + + var e [10]func() int + _ = e /* ERROR \[10\]func\(\) int cannot be compared */ == e +} + +func structs() { + // basics + var s, t struct { + x int + a [10]float32 + _ bool + } + _ = s == t + _ = s != t + _ = s /* ERROR < not defined */ < t + _ = s /* ERROR cannot compare.*mismatched types */ == nil + + type S struct { + x int + a [10]float32 + _ bool + } + type T struct { + x int + a [10]float32 + _ bool + } + var ss S + var tt T + _ = s == ss + _ = ss /* ERROR mismatched types */ == tt + + var u struct { + x int + a [10]map[string]int + } + _ = u /* ERROR cannot compare */ == u +} + +func pointers() { + // nil + _ = nil == nil // ERROR operator == not defined on untyped nil + _ = nil != nil // ERROR operator != not defined on untyped nil + _ = nil /* ERROR < not defined */ < nil + _ = nil /* ERROR <= not defined */ <= nil + _ = nil /* ERROR > not defined */ > nil + _ = nil /* ERROR >= not defined */ >= nil + + // basics + var p, q *int + _ = p == q + _ = p != q + + _ = p == nil + _ = p != nil + _ = nil == q + _ = nil != q + + _ = p /* ERROR < not defined */ < q + _ = p /* ERROR <= not defined */ <= q + _ = p /* ERROR > not defined */ > q + _ = p /* ERROR >= not defined */ >= q + + // various element types + type ( + S1 struct{} + S2 struct{} + P1 *S1 + P2 *S2 + ) + var ( + ps1 *S1 + ps2 *S2 + p1 P1 + p2 P2 + ) + _ = ps1 == ps1 + _ = ps1 /* ERROR mismatched types */ == ps2 + _ = ps2 /* ERROR mismatched types */ == ps1 + + _ = p1 == p1 + _ = p1 /* ERROR mismatched types */ == p2 + + _ = p1 == ps1 +} + +func channels() { + // basics + var c, d chan int + _ = c == d + _ = c != d + _ = c == nil + _ = c /* ERROR < not defined */ < d + + // various element types (named types) + type ( + C1 chan int + C1r <-chan int + C1s chan<- int + C2 chan float32 + ) + var ( + c1 C1 + c1r C1r + c1s C1s + c1a chan int + c2 C2 + ) + _ = c1 == c1 + _ = c1 /* ERROR mismatched types */ == c1r + _ = c1 /* ERROR mismatched types */ == c1s + _ = c1r /* ERROR mismatched types */ == c1s + _ = c1 == c1a + _ = c1a == c1 + _ = c1 /* ERROR mismatched types */ == c2 + _ = c1a /* ERROR mismatched types */ == c2 + + // various element types (unnamed types) + var ( + d1 chan int + d1r <-chan int + d1s chan<- int + d1a chan<- int + d2 chan float32 + ) + _ = d1 == d1 + _ = d1 == d1r + _ = d1 == d1s + _ = d1r /* ERROR mismatched types */ == d1s + _ = d1 == d1a + _ = d1a == d1 + _ = d1 /* ERROR mismatched types */ == d2 + _ = d1a /* ERROR mismatched types */ == d2 +} + +// for interfaces test +type S1 struct{} +type S11 struct{} +type S2 struct{} +func (*S1) m() int +func (*S11) m() int +func (*S11) n() +func (*S2) m() float32 + +func interfaces() { + // basics + var i, j interface{ m() int } + _ = i == j + _ = i != j + _ = i == nil + _ = i /* ERROR < not defined */ < j + + // various interfaces + var ii interface { m() int; n() } + var k interface { m() float32 } + _ = i == ii + _ = i /* ERROR mismatched types */ == k + + // interfaces vs values + var s1 S1 + var s11 S11 + var s2 S2 + + _ = i == 0 /* ERROR cannot convert */ + _ = i /* ERROR mismatched types */ == s1 + _ = i == &s1 + _ = i == &s11 + + _ = i /* ERROR mismatched types */ == s2 + _ = i /* ERROR mismatched types */ == &s2 + + // issue #28164 + // testcase from issue + _ = interface{}(nil) == [ /* ERROR slice can only be compared to nil */ ]int(nil) + + // related cases + var e interface{} + var s []int + var x int + _ = e == s // ERROR slice can only be compared to nil + _ = s /* ERROR slice can only be compared to nil */ == e + _ = e /* ERROR operator < not defined on interface */ < x + _ = x < e // ERROR operator < not defined on interface +} + +func slices() { + // basics + var s []int + _ = s == nil + _ = s != nil + _ = s /* ERROR < not defined */ < nil + + // slices are not otherwise comparable + _ = s /* ERROR slice can only be compared to nil */ == s + _ = s /* ERROR < not defined */ < s +} + +func maps() { + // basics + var m map[string]int + _ = m == nil + _ = m != nil + _ = m /* ERROR < not defined */ < nil + + // maps are not otherwise comparable + _ = m /* ERROR map can only be compared to nil */ == m + _ = m /* ERROR < not defined */ < m +} + +func funcs() { + // basics + var f func(int) float32 + _ = f == nil + _ = f != nil + _ = f /* ERROR < not defined */ < nil + + // funcs are not otherwise comparable + _ = f /* ERROR func can only be compared to nil */ == f + _ = f /* ERROR < not defined */ < f +} diff --git a/src/internal/types/testdata/check/expr3.go b/src/internal/types/testdata/check/expr3.go new file mode 100644 index 00000000000000..ba6c7dd31434cf --- /dev/null +++ b/src/internal/types/testdata/check/expr3.go @@ -0,0 +1,564 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package expr3 + +import "time" + +func indexes() { + _ = 1 /* ERROR "cannot index" */ [0] + _ = indexes /* ERROR "cannot index" */ [0] + _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2] + + var a [10]int + _ = a[true /* ERROR "cannot convert" */ ] + _ = a["foo" /* ERROR "cannot convert" */ ] + _ = a[1.1 /* ERROR "truncated" */ ] + _ = a[1.0] + _ = a[- /* ERROR "negative" */ 1] + _ = a[- /* ERROR "negative" */ 1 :] + _ = a[: - /* ERROR "negative" */ 1] + _ = a[: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] + _ = a[0: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] + _ = a[0: /* ERROR "middle index required" */ :10] + _ = a[:10:10] + + var a0 int + a0 = a[0] + _ = a0 + var a1 int32 + a1 = a /* ERROR "cannot use .* in assignment" */ [1] + _ = a1 + + _ = a[9] + _ = a[10 /* ERROR "index .* out of bounds" */ ] + _ = a[1 /* ERROR "overflows" */ <<100] + _ = a[1<< /* ERROR "constant shift overflow" */ 1000] // no out-of-bounds follow-on error + _ = a[10:] + _ = a[:10] + _ = a[10:10] + _ = a[11 /* ERROR "index .* out of bounds" */ :] + _ = a[: 11 /* ERROR "index .* out of bounds" */ ] + _ = a[: 1 /* ERROR "overflows" */ <<100] + _ = a[:10:10] + _ = a[:11 /* ERROR "index .* out of bounds" */ :10] + _ = a[:10:11 /* ERROR "index .* out of bounds" */ ] + _ = a[10:0 /* ERROR "invalid slice indices" */ :10] + _ = a[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = a[10:0 /* ERROR "invalid slice indices" */:0] + _ = &a /* ERROR "cannot take address" */ [:10] + + pa := &a + _ = pa[9] + _ = pa[10 /* ERROR "index .* out of bounds" */ ] + _ = pa[1 /* ERROR "overflows" */ <<100] + _ = pa[10:] + _ = pa[:10] + _ = pa[10:10] + _ = pa[11 /* ERROR "index .* out of bounds" */ :] + _ = pa[: 11 /* ERROR "index .* out of bounds" */ ] + _ = pa[: 1 /* ERROR "overflows" */ <<100] + _ = pa[:10:10] + _ = pa[:11 /* ERROR "index .* out of bounds" */ :10] + _ = pa[:10:11 /* ERROR "index .* out of bounds" */ ] + _ = pa[10:0 /* ERROR "invalid slice indices" */ :10] + _ = pa[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = pa[10:0 /* ERROR "invalid slice indices" */ :0] + _ = &pa /* ERROR "cannot take address" */ [:10] + + var b [0]int + _ = b[0 /* ERROR "index .* out of bounds" */ ] + _ = b[:] + _ = b[0:] + _ = b[:0] + _ = b[0:0] + _ = b[0:0:0] + _ = b[1 /* ERROR "index .* out of bounds" */ :0:0] + + var s []int + _ = s[- /* ERROR "negative" */ 1] + _ = s[- /* ERROR "negative" */ 1 :] + _ = s[: - /* ERROR "negative" */ 1] + _ = s[0] + _ = s[1:2] + _ = s[2:1 /* ERROR "invalid slice indices" */ ] + _ = s[2:] + _ = s[: 1 /* ERROR "overflows" */ <<100] + _ = s[1 /* ERROR "overflows" */ <<100 :] + _ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100] + _ = s[: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ] + _ = s[:10:10] + _ = s[10:0 /* ERROR "invalid slice indices" */ :10] + _ = s[0:10:0 /* ERROR "invalid slice indices" */ ] + _ = s[10:0 /* ERROR "invalid slice indices" */ :0] + _ = &s /* ERROR "cannot take address" */ [:10] + + var m map[string]int + _ = m[0 /* ERROR "cannot use .* in map index" */ ] + _ = m /* ERROR "cannot slice" */ ["foo" : "bar"] + _ = m["foo"] + // ok is of type bool + type mybool bool + var ok mybool + _, ok = m["bar"] + _ = ok + _ = m/* ERROR "mismatched types int and untyped string" */[0 /* ERROR "cannot use 0" */ ] + "foo" + + var t string + _ = t[- /* ERROR "negative" */ 1] + _ = t[- /* ERROR "negative" */ 1 :] + _ = t[: - /* ERROR "negative" */ 1] + _ = t[1:2:3 /* ERROR "3-index slice of string" */ ] + _ = "foo"[1:2:3 /* ERROR "3-index slice of string" */ ] + var t0 byte + t0 = t[0] + _ = t0 + var t1 rune + t1 = t /* ERROR "cannot use .* in assignment" */ [2] + _ = t1 + _ = ("foo" + "bar")[5] + _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ] + + const c = "foo" + _ = c[- /* ERROR "negative" */ 1] + _ = c[- /* ERROR "negative" */ 1 :] + _ = c[: - /* ERROR "negative" */ 1] + var c0 byte + c0 = c[0] + _ = c0 + var c2 float32 + c2 = c /* ERROR "cannot use .* in assignment" */ [2] + _ = c[3 /* ERROR "index .* out of bounds" */ ] + _ = ""[0 /* ERROR "index .* out of bounds" */ ] + _ = c2 + + _ = s[1<<30] // no compile-time error here + + // issue 4913 + type mystring string + var ss string + var ms mystring + var i, j int + ss = "foo"[1:2] + ss = "foo"[i:j] + ms = "foo" /* ERROR "cannot use .* in assignment" */ [1:2] + ms = "foo" /* ERROR "cannot use .* in assignment" */ [i:j] + _, _ = ss, ms +} + +type T struct { + x int + y func() +} + +func (*T) m() {} + +func method_expressions() { + _ = T.a /* ERROR "no field or method" */ + _ = T.x /* ERROR "has no method" */ + _ = T.m /* ERROR "cannot call pointer method m on T" */ + _ = (*T).m + + var f func(*T) = T.m /* ERROR "cannot call pointer method m on T" */ + var g func(*T) = (*T).m + _, _ = f, g + + _ = T.y /* ERROR "has no method" */ + _ = (*T).y /* ERROR "has no method" */ +} + +func struct_literals() { + type T0 struct { + a, b, c int + } + + type T1 struct { + T0 + a, b int + u float64 + s string + } + + // keyed elements + _ = T1{} + _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ } + _ = T1{aa /* ERROR "unknown field" */ : 0} + _ = T1{1 /* ERROR "invalid field name" */ : 0} + _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10} + _ = T1{a: "foo" /* ERROR "cannot use .* in struct literal" */ } + _ = T1{c /* ERROR "unknown field" */ : 0} + _ = T1{T0: { /* ERROR "missing type" */ }} // struct literal element type may not be elided + _ = T1{T0: T0{}} + _ = T1{T0 /* ERROR "invalid field name" */ .a: 0} + + // unkeyed elements + _ = T0{1, 2, 3} + _ = T0{1, b /* ERROR "mixture" */ : 2, 3} + _ = T0{1, 2} /* ERROR "too few values" */ + _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } + _ = T0{1, "foo" /* ERROR "cannot use .* in struct literal" */, 3.4 /* ERROR "cannot use .*\(truncated\)" */} + + // invalid type + type P *struct{ + x int + } + _ = P /* ERROR "invalid composite literal type" */ {} + + // unexported fields + _ = time.Time{} + _ = time.Time{sec /* ERROR "unknown field" */ : 0} + _ = time.Time{ + 0 /* ERROR implicit assignment to unexported field wall in time.Time literal */, + 0 /* ERROR implicit assignment */ , + nil /* ERROR implicit assignment */ , + } +} + +func array_literals() { + type A0 [0]int + _ = A0{} + _ = A0{0 /* ERROR "index .* out of bounds" */} + _ = A0{0 /* ERROR "index .* out of bounds" */ : 0} + + type A1 [10]int + _ = A1{} + _ = A1{0, 1, 2} + _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ } + _ = A1{- /* ERROR "negative" */ 1: 0} + _ = A1{8: 8, 9} + _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ } + _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + _ = A1{5: 5, 6, 7, 3: 3, 4} + _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } + _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10} + _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } + _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} + _ = A1{2.0} + _ = A1{2.1 /* ERROR "truncated" */ } + _ = A1{"foo" /* ERROR "cannot use .* in array or slice literal" */ } + + // indices must be integer constants + i := 1 + const f = 2.1 + const s = "foo" + _ = A1{i /* ERROR "index i must be integer constant" */ : 0} + _ = A1{f /* ERROR "truncated" */ : 0} + _ = A1{s /* ERROR "cannot convert" */ : 0} + + a0 := [...]int{} + assert(len(a0) == 0) + + a1 := [...]int{0, 1, 2} + assert(len(a1) == 3) + var a13 [3]int + var a14 [4]int + a13 = a1 + a14 = a1 /* ERROR "cannot use .* in assignment" */ + _, _ = a13, a14 + + a2 := [...]int{- /* ERROR "negative" */ 1: 0} + _ = a2 + + a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + assert(len(a3) == 5) // somewhat arbitrary + + a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14} + assert(len(a4) == 1024) + + // composite literal element types may be elided + type T []int + _ = [10]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} + a6 := [...]T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} + assert(len(a6) == 8) + + // recursively so + _ = [10][10]T{{}, [10]T{{}}, {{1, 2, 3}}} + + // from the spec + type Point struct { x, y float32 } + _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}} + _ = [...]Point{{1.5, -3.5}, {0, 0}} + _ = [][]int{[]int{1, 2, 3}, []int{4, 5}} + _ = [][]int{{1, 2, 3}, {4, 5}} + _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}} + _ = [...]*Point{{1.5, -3.5}, {0, 0}} +} + +func slice_literals() { + type S0 []int + _ = S0{} + _ = S0{0, 1, 2} + _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + _ = S0{- /* ERROR "negative" */ 1: 0} + _ = S0{8: 8, 9} + _ = S0{8: 8, 9, 10} + _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + _ = S0{5: 5, 6, 7, 3: 3, 4} + _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } + _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10} + _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "overflows" */ <<100: 4, 5 /* ERROR "duplicate index" */ } + _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4} + _ = S0{2.0} + _ = S0{2.1 /* ERROR "truncated" */ } + _ = S0{"foo" /* ERROR "cannot use .* in array or slice literal" */ } + + // indices must be resolved correctly + const index1 = 1 + _ = S0{index1: 1} + _ = S0{index2: 2} + _ = S0{index3 /* ERROR "undeclared name" */ : 3} + + // indices must be integer constants + i := 1 + const f = 2.1 + const s = "foo" + _ = S0{i /* ERROR "index i must be integer constant" */ : 0} + _ = S0{f /* ERROR "truncated" */ : 0} + _ = S0{s /* ERROR "cannot convert" */ : 0} + + // composite literal element types may be elided + type T []int + _ = []T{T{}, {}, 5: T{1, 2, 3}, 7: {1, 2, 3}} + _ = [][]int{{1, 2, 3}, {4, 5}} + + // recursively so + _ = [][]T{{}, []T{{}}, {{1, 2, 3}}} + + // issue 17954 + type T0 *struct { s string } + _ = []T0{{}} + _ = []T0{{"foo"}} + + type T1 *struct{ int } + _ = []T1{} + _ = []T1{{0}, {1}, {2}} + + type T2 T1 + _ = []T2{} + _ = []T2{{0}, {1}, {2}} + + _ = map[T0]T2{} + _ = map[T0]T2{{}: {}} +} + +const index2 int = 2 + +type N int +func (N) f() {} + +func map_literals() { + type M0 map[string]int + type M1 map[bool]int + type M2 map[*int]int + + _ = M0{} + _ = M0{1 /* ERROR "missing key" */ } + _ = M0{1 /* ERROR "cannot use .* in map literal" */ : 2} + _ = M0{"foo": "bar" /* ERROR "cannot use .* in map literal" */ } + _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } + + _ = map[interface{}]int{2: 1, 2 /* ERROR "duplicate key" */ : 1} + _ = map[interface{}]int{int(2): 1, int16(2): 1} + _ = map[interface{}]int{int16(2): 1, int16 /* ERROR "duplicate key" */ (2): 1} + + type S string + + _ = map[interface{}]int{"a": 1, "a" /* ERROR "duplicate key" */ : 1} + _ = map[interface{}]int{"a": 1, S("a"): 1} + _ = map[interface{}]int{S("a"): 1, S /* ERROR "duplicate key" */ ("a"): 1} + _ = map[interface{}]int{1.0: 1, 1.0 /* ERROR "duplicate key" */: 1} + _ = map[interface{}]int{int64(-1): 1, int64 /* ERROR "duplicate key" */ (-1) : 1} + _ = map[interface{}]int{^uint64(0): 1, ^ /* ERROR "duplicate key" */ uint64(0): 1} + _ = map[interface{}]int{complex(1,2): 1, complex /* ERROR "duplicate key" */ (1,2) : 1} + + type I interface { + f() + } + + _ = map[I]int{N(0): 1, N(2): 1} + _ = map[I]int{N(2): 1, N /* ERROR "duplicate key" */ (2): 1} + + // map keys must be resolved correctly + key1 := "foo" + _ = M0{key1: 1} + _ = M0{key2: 2} + _ = M0{key3 /* ERROR "undeclared name" */ : 2} + + var value int + _ = M1{true: 1, false: 0} + _ = M2{nil: 0, &value: 1} + + // composite literal element types may be elided + type T [2]int + _ = map[int]T{0: T{3, 4}, 1: {5, 6}} + + // recursively so + _ = map[int][]T{0: {}, 1: {{}, T{1, 2}}} + + // composite literal key types may be elided + _ = map[T]int{T{3, 4}: 0, {5, 6}: 1} + + // recursively so + _ = map[[2]T]int{{}: 0, {{}}: 1, [2]T{{}}: 2, {T{1, 2}}: 3} + + // composite literal element and key types may be elided + _ = map[T]T{{}: {}, {1, 2}: T{3, 4}, T{4, 5}: {}} + _ = map[T]M0{{} : {}, T{1, 2}: M0{"foo": 0}, {1, 3}: {"foo": 1}} + + // recursively so + _ = map[[2]T][]T{{}: {}, {{}}: {{}, T{1, 2}}, [2]T{{}}: nil, {T{1, 2}}: {{}, {}}} + + // from the spec + type Point struct { x, y float32 } + _ = map[string]Point{"orig": {0, 0}} + _ = map[*Point]string{{0, 0}: "orig"} + + // issue 17954 + type T0 *struct{ s string } + type T1 *struct{ int } + type T2 T1 + + _ = map[T0]T2{} + _ = map[T0]T2{{}: {}} +} + +var key2 string = "bar" + +type I interface { + m() +} + +type I2 interface { + m(int) +} + +type T1 struct{} +type T2 struct{} + +func (T2) m(int) {} + +type mybool bool + +func type_asserts() { + var x int + _ = x /* ERROR "not an interface" */ .(int) + + var e interface{} + var ok bool + x, ok = e.(int) + _ = ok + + // ok value is of type bool + var myok mybool + _, myok = e.(int) + _ = myok + + var t I + _ = t /* ERROR "use of .* outside type switch" */ .(type) + _ = t /* ERROR "m has pointer receiver" */ .(T) + _ = t.(*T) + _ = t /* ERROR "missing method m" */ .(T1) + _ = t /* ERROR "wrong type for method m" */ .(T2) + _ = t /* STRICT "wrong type for method m" */ .(I2) // only an error in strict mode (issue 8561) + + // e doesn't statically have an m, but may have one dynamically. + _ = e.(I2) +} + +func f0() {} +func f1(x int) {} +func f2(u float32, s string) {} +func fs(s []byte) {} +func fv(x ...int) {} +func fi(x ... interface{}) {} +func (T) fm(x ...int) + +func g0() {} +func g1() int { return 0} +func g2() (u float32, s string) { return } +func gs() []byte { return nil } + +func _calls() { + var x int + var y float32 + var s []int + + f0() + _ = f0 /* ERROR "used as value" */ () + f0(g0 /* ERROR "too many arguments" */ ) + + f1(0) + f1(x) + f1(10.0) + f1() /* ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)" */ + f1(x, y /* ERROR "too many arguments in call to f1\n\thave \(int, float32\)\n\twant \(int\)" */ ) + f1(s /* ERROR "cannot use .* in argument" */ ) + f1(x ... /* ERROR "cannot use ..." */ ) + f1(g0 /* ERROR "used as value" */ ()) + f1(g1()) + f1(g2 /* ERROR "too many arguments in call to f1\n\thave \(float32, string\)\n\twant \(int\)" */ ()) + + f2() /* ERROR "not enough arguments in call to f2\n\thave \(\)\n\twant \(float32, string\)" */ + f2(3.14) /* ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(float32, string\)" */ + f2(3.14, "foo") + f2(x /* ERROR "cannot use .* in argument" */ , "foo") + f2(g0 /* ERROR "used as value" */ ()) + f2(g1()) /* ERROR "not enough arguments in call to f2\n\thave \(int\)\n\twant \(float32, string\)" */ + f2(g2()) + + fs() /* ERROR "not enough arguments" */ + fs(g0 /* ERROR "used as value" */ ()) + fs(g1 /* ERROR "cannot use .* in argument" */ ()) + fs(g2 /* ERROR "too many arguments" */ ()) + fs(gs()) + + fv() + fv(1, 2.0, x) + fv(s /* ERROR "cannot use .* in argument" */ ) + fv(s...) + fv(x /* ERROR "cannot use" */ ...) + fv(1, s /* ERROR "too many arguments" */ ...) + fv(gs /* ERROR "cannot use .* in argument" */ ()) + fv(gs /* ERROR "cannot use .* in argument" */ ()...) + + var t T + t.fm() + t.fm(1, 2.0, x) + t.fm(s /* ERROR "cannot use .* in argument" */ ) + t.fm(g1()) + t.fm(1, s /* ERROR "too many arguments" */ ...) + t.fm(gs /* ERROR "cannot use .* in argument" */ ()) + t.fm(gs /* ERROR "cannot use .* in argument" */ ()...) + + T.fm(t, ) + T.fm(t, 1, 2.0, x) + T.fm(t, s /* ERROR "cannot use .* in argument" */ ) + T.fm(t, g1()) + T.fm(t, 1, s /* ERROR "too many arguments" */ ...) + T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()) + T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...) + + var i interface{ fm(x ...int) } = t + i.fm() + i.fm(1, 2.0, x) + i.fm(s /* ERROR "cannot use .* in argument" */ ) + i.fm(g1()) + i.fm(1, s /* ERROR "too many arguments" */ ...) + i.fm(gs /* ERROR "cannot use .* in argument" */ ()) + i.fm(gs /* ERROR "cannot use .* in argument" */ ()...) + + fi() + fi(1, 2.0, x, 3.14, "foo") + fi(g2()) + fi(0, g2) + fi(0, g2 /* ERROR "2-valued g2" */ ()) +} + +func issue6344() { + type T []interface{} + var x T + fi(x...) // ... applies also to named slices +} diff --git a/src/internal/types/testdata/check/funcinference.go b/src/internal/types/testdata/check/funcinference.go new file mode 100644 index 00000000000000..fedf1991dd8091 --- /dev/null +++ b/src/internal/types/testdata/check/funcinference.go @@ -0,0 +1,104 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package funcInference + +import "strconv" + +type any interface{} + +func f0[A any, B interface{*C}, C interface{*D}, D interface{*A}](A, B, C, D) {} +func _() { + f := f0[string] + f("a", nil, nil, nil) + f0("a", nil, nil, nil) +} + +func f1[A any, B interface{*A}](A, B) {} +func _() { + f := f1[int] + f(int(0), new(int)) + f1(int(0), new(int)) +} + +func f2[A any, B interface{[]A}](A, B) {} +func _() { + f := f2[byte] + f(byte(0), []byte{}) + f2(byte(0), []byte{}) +} + +// Embedding stand-alone type parameters is not permitted for now. Disabled. +// func f3[A any, B interface{~C}, C interface{~*A}](A, B, C) +// func _() { +// f := f3[int] +// var x int +// f(x, &x, &x) +// f3(x, &x, &x) +// } + +func f4[A any, B interface{[]C}, C interface{*A}](A, B, C) {} +func _() { + f := f4[int] + var x int + f(x, []*int{}, &x) + f4(x, []*int{}, &x) +} + +func f5[A interface{struct{b B; c C}}, B any, C interface{*B}](x B) A { panic(0) } +func _() { + x := f5(1.2) + var _ float64 = x.b + var _ float64 = *x.c +} + +func f6[A any, B interface{~struct{f []A}}](B) A { panic(0) } +func _() { + x := f6(struct{f []string}{}) + var _ string = x +} + +func f7[A interface{*B}, B interface{~*A}]() {} + +// More realistic examples + +func Double[S interface{ ~[]E }, E interface{ ~int | ~int8 | ~int16 | ~int32 | ~int64 }](s S) S { + r := make(S, len(s)) + for i, v := range s { + r[i] = v + v + } + return r +} + +type MySlice []int + +var _ = Double(MySlice{1}) + +// From the draft design. + +type Setter[B any] interface { + Set(string) + *B +} + +func FromStrings[T interface{}, PT Setter[T]](s []string) []T { + result := make([]T, len(s)) + for i, v := range s { + // The type of &result[i] is *T which is in the type set + // of Setter, so we can convert it to PT. + p := PT(&result[i]) + // PT has a Set method. + p.Set(v) + } + return result +} + +type Settable int + +func (p *Settable) Set(s string) { + i, _ := strconv.Atoi(s) // real code should not ignore the error + *p = Settable(i) +} + +var _ = FromStrings[Settable]([]string{"1", "2"}) diff --git a/src/internal/types/testdata/check/go1_12.go b/src/internal/types/testdata/check/go1_12.go new file mode 100644 index 00000000000000..56c6d5a4c9b4bc --- /dev/null +++ b/src/internal/types/testdata/check/go1_12.go @@ -0,0 +1,36 @@ +// -lang=go1.12 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check Go language version-specific errors. + +package p + +// numeric literals +const ( + _ = 1_000 // ERROR "underscores in numeric literals requires go1.13 or later" + _ = 0b111 // ERROR "binary literals requires go1.13 or later" + _ = 0o567 // ERROR "0o/0O-style octal literals requires go1.13 or later" + _ = 0xabc // ok + _ = 0x0p1 // ERROR "hexadecimal floating-point literals requires go1.13 or later" + + _ = 0B111 // ERROR "binary" + _ = 0O567 // ERROR "octal" + _ = 0Xabc // ok + _ = 0X0P1 // ERROR "hexadecimal floating-point" + + _ = 1_000i // ERROR "underscores" + _ = 0b111i // ERROR "binary" + _ = 0o567i // ERROR "octal" + _ = 0xabci // ERROR "hexadecimal floating-point" + _ = 0x0p1i // ERROR "hexadecimal floating-point" +) + +// signed shift counts +var ( + s int + _ = 1 << s // ERROR "invalid operation: signed shift count s \(variable of type int\) requires go1.13 or later" + _ = 1 >> s // ERROR "signed shift count" +) diff --git a/src/internal/types/testdata/check/go1_13.go b/src/internal/types/testdata/check/go1_13.go new file mode 100644 index 00000000000000..cc7861d6161002 --- /dev/null +++ b/src/internal/types/testdata/check/go1_13.go @@ -0,0 +1,23 @@ +// -lang=go1.13 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check Go language version-specific errors. + +package p + +// interface embedding + +type I interface { m() } + +type _ interface { + m() + I // ERROR "duplicate method m" +} + +type _ interface { + I + I // ERROR "duplicate method m" +} diff --git a/src/internal/types/testdata/check/go1_16.go b/src/internal/types/testdata/check/go1_16.go new file mode 100644 index 00000000000000..81b529044c5064 --- /dev/null +++ b/src/internal/types/testdata/check/go1_16.go @@ -0,0 +1,15 @@ +// -lang=go1.16 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check Go language version-specific errors. + +package p + +type Slice []byte +type Array [8]byte + +var s Slice +var p = (*Array)(s /* ERROR requires go1.17 or later */ ) diff --git a/src/internal/types/testdata/check/go1_19.go b/src/internal/types/testdata/check/go1_19.go new file mode 100644 index 00000000000000..f899d93733c0a4 --- /dev/null +++ b/src/internal/types/testdata/check/go1_19.go @@ -0,0 +1,15 @@ +// -lang=go1.19 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check Go language version-specific errors. + +package p + +type Slice []byte +type Array [8]byte + +var s Slice +var p = (Array)(s /* ERROR requires go1.20 or later */) diff --git a/src/internal/types/testdata/check/go1_8.go b/src/internal/types/testdata/check/go1_8.go new file mode 100644 index 00000000000000..99f2fd4eb27b65 --- /dev/null +++ b/src/internal/types/testdata/check/go1_8.go @@ -0,0 +1,12 @@ +// -lang=go1.8 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check Go language version-specific errors. + +package p + +// type alias declarations +type any = /* ERROR type aliases requires go1.9 or later */ interface{} diff --git a/src/cmd/compile/internal/types2/testdata/check/gotos.src b/src/internal/types/testdata/check/gotos.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/gotos.src rename to src/internal/types/testdata/check/gotos.go diff --git a/src/internal/types/testdata/check/importC.go b/src/internal/types/testdata/check/importC.go new file mode 100644 index 00000000000000..807802199fda5c --- /dev/null +++ b/src/internal/types/testdata/check/importC.go @@ -0,0 +1,56 @@ +// -fakeImportC + +// 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 importC + +import "C" +import _ /* ERROR cannot rename import "C" */ "C" +import foo /* ERROR cannot rename import "C" */ "C" +import . /* ERROR cannot rename import "C" */ "C" + +// Test cases extracted from issue #22090. + +import "unsafe" + +const _ C.int = 0xff // no error due to invalid constant type + +type T struct { + Name string + Ordinal int +} + +func _(args []T) { + var s string + for i, v := range args { + cname := C.CString(v.Name) + args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s, cname)) // no error due to i not being "used" + C.free(unsafe.Pointer(cname)) + } +} + +type CType C.Type + +const _ CType = C.X // no error due to invalid constant type +const _ = C.X + +// Test cases extracted from issue #23712. + +func _() { + var a [C.ArrayLength]byte + _ = a[0] // no index out of bounds error here +} + +// Additional tests to verify fix for #23712. + +func _() { + var a [C.ArrayLength1]byte + _ = 1 / len(a) // no division by zero error here and below + _ = 1 / cap(a) + _ = uint(unsafe.Sizeof(a)) // must not be negative + + var b [C.ArrayLength2]byte + a = b // should be valid +} diff --git a/src/cmd/compile/internal/types2/testdata/check/importdecl0/importdecl0a.src b/src/internal/types/testdata/check/importdecl0/importdecl0a.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/importdecl0/importdecl0a.src rename to src/internal/types/testdata/check/importdecl0/importdecl0a.go diff --git a/src/cmd/compile/internal/types2/testdata/check/importdecl0/importdecl0b.src b/src/internal/types/testdata/check/importdecl0/importdecl0b.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/importdecl0/importdecl0b.src rename to src/internal/types/testdata/check/importdecl0/importdecl0b.go diff --git a/src/cmd/compile/internal/types2/testdata/check/importdecl1/importdecl1a.src b/src/internal/types/testdata/check/importdecl1/importdecl1a.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/importdecl1/importdecl1a.src rename to src/internal/types/testdata/check/importdecl1/importdecl1a.go diff --git a/src/cmd/compile/internal/types2/testdata/check/importdecl1/importdecl1b.src b/src/internal/types/testdata/check/importdecl1/importdecl1b.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/importdecl1/importdecl1b.src rename to src/internal/types/testdata/check/importdecl1/importdecl1b.go diff --git a/src/cmd/compile/internal/types2/testdata/check/init0.src b/src/internal/types/testdata/check/init0.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/init0.src rename to src/internal/types/testdata/check/init0.go diff --git a/src/cmd/compile/internal/types2/testdata/check/init1.src b/src/internal/types/testdata/check/init1.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/init1.src rename to src/internal/types/testdata/check/init1.go diff --git a/src/cmd/compile/internal/types2/testdata/check/init2.src b/src/internal/types/testdata/check/init2.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/init2.src rename to src/internal/types/testdata/check/init2.go diff --git a/src/cmd/compile/internal/types2/testdata/check/issue25008/issue25008a.src b/src/internal/types/testdata/check/issue25008/issue25008a.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/issue25008/issue25008a.src rename to src/internal/types/testdata/check/issue25008/issue25008a.go diff --git a/src/cmd/compile/internal/types2/testdata/check/issue25008/issue25008b.src b/src/internal/types/testdata/check/issue25008/issue25008b.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/check/issue25008/issue25008b.src rename to src/internal/types/testdata/check/issue25008/issue25008b.go diff --git a/src/internal/types/testdata/check/issues0.go b/src/internal/types/testdata/check/issues0.go new file mode 100644 index 00000000000000..95cfa2a9103df3 --- /dev/null +++ b/src/internal/types/testdata/check/issues0.go @@ -0,0 +1,373 @@ +// -lang=go1.17 + +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p // don't permit non-interface elements in interfaces + +import ( + "fmt" + syn "regexp/syntax" + t1 "text/template" + t2 "html/template" +) + +func issue7035() { + type T struct{ X int } + _ = func() { + fmt.Println() // must refer to imported fmt rather than the fmt below + } + fmt := new(T) + _ = fmt.X +} + +func issue8066() { + const ( + _ = float32(340282356779733661637539395458142568447) + _ = float32(340282356779733661637539395458142568448 /* ERROR cannot convert */ ) + ) +} + +// Check that a missing identifier doesn't lead to a spurious error cascade. +func issue8799a() { + x, ok := missing /* ERROR undeclared */ () + _ = !ok + _ = x +} + +func issue8799b(x int, ok bool) { + x, ok = missing /* ERROR undeclared */ () + _ = !ok + _ = x +} + +func issue9182() { + type Point C /* ERROR undeclared */ .Point + // no error for composite literal based on unknown type + _ = Point{x: 1, y: 2} +} + +func f0() (a []int) { return } +func f1() (a []int, b int) { return } +func f2() (a, b []int) { return } + +func append_([]int, ...int) {} + +func issue9473(a []int, b ...int) { + // variadic builtin function + _ = append(f0()) + _ = append(f0(), f0()...) + _ = append(f1()) + _ = append(f2 /* ERROR cannot use .* in argument */ ()) + _ = append(f2()... /* ERROR cannot use ... */ ) + _ = append(f0(), f1 /* ERROR 2-valued f1 */ ()) + _ = append(f0(), f2 /* ERROR 2-valued f2 */ ()) + _ = append(f0(), f1 /* ERROR 2-valued f1 */ ()...) + _ = append(f0(), f2 /* ERROR 2-valued f2 */ ()...) + + // variadic user-defined function + append_(f0()) + append_(f0(), f0()...) + append_(f1()) + append_(f2 /* ERROR cannot use .* in argument */ ()) + append_(f2()... /* ERROR cannot use ... */ ) + append_(f0(), f1 /* ERROR 2-valued f1 */ ()) + append_(f0(), f2 /* ERROR 2-valued f2 */ ()) + append_(f0(), f1 /* ERROR 2-valued f1 */ ()...) + append_(f0(), f2 /* ERROR 2-valued f2 */ ()...) +} + +// Check that embedding a non-interface type in an interface results in a good error message. +func issue10979() { + type _ interface { + int /* ERROR non-interface type int */ + } + type T struct{} + type _ interface { + T /* ERROR non-interface type T */ + } + type _ interface { + nosuchtype /* ERROR undeclared name: nosuchtype */ + } + type _ interface { + fmt.Nosuchtype /* ERROR Nosuchtype not declared by package fmt */ + } + type _ interface { + nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype + } + type I interface { + I.m /* ERROR no field or method m */ + m() + } +} + +// issue11347 +// These should not crash. +var a1, b1 /* ERROR cycle */ , c1 /* ERROR cycle */ b1 = 0 > 0<<""[""[c1]]>c1 +var a2, b2 /* ERROR cycle */ = 0 /* ERROR cannot initialize */ /* ERROR cannot initialize */ > 0<<""[b2] +var a3, b3 /* ERROR cycle */ = int /* ERROR cannot initialize */ /* ERROR cannot initialize */ (1<<""[b3]) + +// issue10260 +// Check that error messages explain reason for interface assignment failures. +type ( + I0 interface{} + I1 interface{ foo() } + I2 interface{ foo(x int) } + T0 struct{} + T1 struct{} + T2 struct{} +) + +func (*T1) foo() {} +func (*T2) foo(x int) {} + +func issue10260() { + var ( + i0 I0 + i1 I1 + i2 I2 + t0 *T0 + t1 *T1 + t2 *T2 + ) + + var x I1 + x = T1 /* ERROR cannot use T1{} .* as I1 value in assignment: T1 does not implement I1 \(method foo has pointer receiver\) */ {} + _ = x /* ERROR impossible type assertion: x\.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ .(T1) + + T1{}.foo /* ERROR cannot call pointer method foo on T1 */ () + x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ () + + _ = i2 /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ .(*T1) + + i1 = i0 /* ERROR cannot use i0 .* as I1 value in assignment: I0 does not implement I1 \(missing method foo\) */ + i1 = t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ + i1 = i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ + i1 = t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ + i2 = i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ + i2 = t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ + + _ = func() I1 { return i0 /* ERROR cannot use i0 .* as I1 value in return statement: I0 does not implement I1 \(missing method foo\) */ } + _ = func() I1 { return t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ } + _ = func() I1 { return i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ } + _ = func() I1 { return t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ } + _ = func() I2 { return i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ } + _ = func() I2 { return t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ } + + // a few more - less exhaustive now + + f := func(I1, I2){} + f(i0 /* ERROR missing method foo */ , i1 /* ERROR wrong type for method foo */ ) + + _ = [...]I1{i0 /* ERROR cannot use i0 .* as I1 value in array or slice literal: I0 does not implement I1 \(missing method foo\) */ } + _ = [...]I1{i2 /* ERROR cannot use i2 .* as I1 value in array or slice literal: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ } + _ = []I1{i0 /* ERROR missing method foo */ } + _ = []I1{i2 /* ERROR wrong type for method foo */ } + _ = map[int]I1{0: i0 /* ERROR missing method foo */ } + _ = map[int]I1{0: i2 /* ERROR wrong type for method foo */ } + + make(chan I1) <- i0 /* ERROR missing method foo */ + make(chan I1) <- i2 /* ERROR wrong type for method foo */ +} + +// Check that constants representable as integers are in integer form +// before being used in operations that are only defined on integers. +func issue14229() { + // from the issue + const _ = int64(-1<<63) % 1e6 + + // related + const ( + a int = 3 + b = 4.0 + _ = a / b + _ = a % b + _ = b / a + _ = 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 +} + +// Test that we don't get "declared but not used" +// errors in the context of invalid/C objects. +func issue20358() { + var F C /* ERROR "undeclared" */ .F + var A C /* ERROR "undeclared" */ .A + var S C /* ERROR "undeclared" */ .S + type T C /* ERROR "undeclared" */ .T + type P C /* ERROR "undeclared" */ .P + + // these variables must be "used" even though + // the LHS expressions/types below in which + // context they are used are unknown/invalid + var f, a, s1, s2, s3, t, p int + + _ = F(f) + _ = A[a] + _ = S[s1:s2:s3] + _ = T{t} + _ = P{f: p} +} + +// Test that we don't declare lhs variables in short variable +// declarations before we type-check function literals on the +// rhs. +func issue24026() { + f := func() int { f(0) /* must refer to outer f */; return 0 } + _ = f + + _ = func() { + f := func() { _ = f() /* must refer to outer f */ } + _ = f + } + + // b and c must not be visible inside function literal + a := 0 + a, b, c := func() (int, int, int) { + return a, b /* ERROR undeclared */ , c /* ERROR undeclared */ + }() + _, _ = b, c +} + +func f(int) {} // for issue24026 + +// Test that we don't report a "missing return statement" error +// (due to incorrect context when type-checking interfaces). +func issue24140(x interface{}) int { + switch x.(type) { + case interface{}: + return 0 + default: + panic(0) + } +} + +// Test that we don't crash when the 'if' condition is missing. +func issue25438() { + if { /* ERROR missing condition */ } + if x := 0; /* ERROR missing condition */ { _ = x } + if + { /* ERROR missing condition */ } +} + +// Test that we can embed alias type names in interfaces. +type issue25301 interface { + E +} + +type E = interface { + m() +} + +// Test case from issue. +// cmd/compile reports a cycle as well. +type issue25301b /* ERROR cycle */ = interface { + m() interface{ issue25301b } +} + +type issue25301c interface { + notE // ERROR non-interface type struct\{\} +} + +type notE = struct{} + +// Test that method declarations don't introduce artificial cycles +// (issue #26124). +const CC TT = 1 +type TT int +func (TT) MM() [CC]TT + +// Reduced test case from issue #26124. +const preloadLimit LNumber = 128 +type LNumber float64 +func (LNumber) assertFunction() *LFunction +type LFunction struct { + GFunction LGFunction +} +type LGFunction func(*LState) +type LState struct { + reg *registry +} +type registry struct { + alloc *allocator +} +type allocator struct { + _ [int(preloadLimit)]int +} + +// Test that we don't crash when type-checking composite literals +// containing errors in the type. +var issue27346 = [][n /* ERROR undeclared */ ]int{ + 0: {}, +} + +var issue22467 = map[int][... /* ERROR invalid use of ... */ ]int{0: {}} + +// Test that invalid use of ... in parameter lists is recognized +// (issue #28281). +func issue28281a(int, int, ...int) +func issue28281b(a, b int, c ...int) +func issue28281c(a, b, c ... /* ERROR can only use ... with final parameter */ int) +func issue28281d(... /* ERROR can only use ... with final parameter */ int, int) +func issue28281e(a, b, c ... /* ERROR can only use ... with final parameter */ int, d int) +func issue28281f(... /* ERROR can only use ... with final parameter */ int, ... /* ERROR can only use ... with final parameter */ int, int) +func (... /* ERROR can only use ... with final parameter */ TT) f() +func issue28281g() (... /* ERROR can only use ... with final parameter */ TT) + +// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output +func issue26234a(f *syn.Prog) { + // The error message below should refer to the actual package name (syntax) + // not the local package name (syn). + f.foo /* ERROR f\.foo undefined \(type \*syntax\.Prog has no field or method foo\) */ +} + +type T struct { + x int + E1 + E2 +} + +type E1 struct{ f int } +type E2 struct{ f int } + +func issue26234b(x T) { + _ = x.f /* ERROR ambiguous selector x.f */ +} + +func issue26234c() { + T.x /* ERROR T.x undefined \(type T has no method x\) */ () +} + +func issue35895() { + // T is defined in this package, don't qualify its name with the package name. + var _ T = 0 // ERROR cannot use 0 \(untyped int constant\) as T + + // There is only one package with name syntax imported, only use the (global) package name in error messages. + var _ *syn.Prog = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.Prog + + // Because both t1 and t2 have the same global package name (template), + // qualify packages with full path name in this case. + var _ t1.Template = t2 /* ERROR cannot use .* \(value of type .html/template.\.Template\) as .text/template.\.Template */ .Template{} +} + +func issue42989(s uint) { + var m map[int]string + delete(m, 1< 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Returns true if this is a new key, false if already present. +func (m *Map[K, V]) Insert(key K, val V) bool { + pn := m.find(key) + if *pn != nil { + (*pn).val = val + return false + } + *pn = &node[K, V]{key: key, val: val} + return true +} + +// Find returns the value associated with a key, or zero if not present. +// The found result reports whether the key was found. +func (m *Map[K, V]) Find(key K) (V, bool) { + pn := m.find(key) + if *pn == nil { + var zero V // see the discussion of zero values, above + return zero, false + } + return (*pn).val, true +} + +// keyValue is a pair of key and value used when iterating. +type keyValue[K, V any] struct { + key K + val V +} + +// InOrder returns an iterator that does an in-order traversal of the map. +func (m *Map[K, V]) InOrder() *Iterator[K, V] { + sender, receiver := chans_Ranger[keyValue[K, V]]() + var f func(*node[K, V]) bool + f = func(n *node[K, V]) bool { + if n == nil { + return true + } + // Stop sending values if sender.Send returns false, + // meaning that nothing is listening at the receiver end. + return f(n.left) && + sender.Send(keyValue[K, V]{n.key, n.val}) && + f(n.right) + } + go func() { + f(m.root) + sender.Close() + }() + return &Iterator[K, V]{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator[K, V any] struct { + r *chans_Receiver[keyValue[K, V]] +} + +// Next returns the next key and value pair, and a boolean indicating +// whether they are valid or whether we have reached the end. +func (it *Iterator[K, V]) Next() (K, V, bool) { + keyval, ok := it.r.Next() + if !ok { + var zerok K + var zerov V + return zerok, zerov, false + } + return keyval.key, keyval.val, true +} + +// chans + +func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) { panic(0) } + +// A sender is used to send values to a Receiver. +type chans_Sender[T any] struct { + values chan<- T + done <-chan bool +} + +func (s *chans_Sender[T]) Send(v T) bool { + select { + case s.values <- v: + return true + case <-s.done: + return false + } +} + +func (s *chans_Sender[T]) Close() { + close(s.values) +} + +type chans_Receiver[T any] struct { + values <-chan T + done chan<- bool +} + +func (r *chans_Receiver[T]) Next() (T, bool) { + v, ok := <-r.values + return v, ok +} diff --git a/src/internal/types/testdata/check/methodsets.go b/src/internal/types/testdata/check/methodsets.go new file mode 100644 index 00000000000000..b0eb14cf50f94a --- /dev/null +++ b/src/internal/types/testdata/check/methodsets.go @@ -0,0 +1,214 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package methodsets + +type T0 struct {} + +func (T0) v0() {} +func (*T0) p0() {} + +type T1 struct {} // like T0 with different method names + +func (T1) v1() {} +func (*T1) p1() {} + +type T2 interface { + v2() + p2() +} + +type T3 struct { + T0 + *T1 + T2 +} + +// Method expressions +func _() { + var ( + _ func(T0) = T0.v0 + _ = T0.p0 /* ERROR "cannot call pointer method p0 on T0" */ + + _ func (*T0) = (*T0).v0 + _ func (*T0) = (*T0).p0 + + // T1 is like T0 + + _ func(T2) = T2.v2 + _ func(T2) = T2.p2 + + _ func(T3) = T3.v0 + _ func(T3) = T3.p0 /* ERROR "cannot call pointer method p0 on T3" */ + _ func(T3) = T3.v1 + _ func(T3) = T3.p1 + _ func(T3) = T3.v2 + _ func(T3) = T3.p2 + + _ func(*T3) = (*T3).v0 + _ func(*T3) = (*T3).p0 + _ func(*T3) = (*T3).v1 + _ func(*T3) = (*T3).p1 + _ func(*T3) = (*T3).v2 + _ func(*T3) = (*T3).p2 + ) +} + +// Method values with addressable receivers +func _() { + var ( + v0 T0 + _ func() = v0.v0 + _ func() = v0.p0 + ) + + var ( + p0 *T0 + _ func() = p0.v0 + _ func() = p0.p0 + ) + + // T1 is like T0 + + var ( + v2 T2 + _ func() = v2.v2 + _ func() = v2.p2 + ) + + var ( + v4 T3 + _ func() = v4.v0 + _ func() = v4.p0 + _ func() = v4.v1 + _ func() = v4.p1 + _ func() = v4.v2 + _ func() = v4.p2 + ) + + var ( + p4 *T3 + _ func() = p4.v0 + _ func() = p4.p0 + _ func() = p4.v1 + _ func() = p4.p1 + _ func() = p4.v2 + _ func() = p4.p2 + ) +} + +// Method calls with addressable receivers +func _() { + var v0 T0 + v0.v0() + v0.p0() + + var p0 *T0 + p0.v0() + p0.p0() + + // T1 is like T0 + + var v2 T2 + v2.v2() + v2.p2() + + var v4 T3 + v4.v0() + v4.p0() + v4.v1() + v4.p1() + v4.v2() + v4.p2() + + var p4 *T3 + p4.v0() + p4.p0() + p4.v1() + p4.p1() + p4.v2() + p4.p2() +} + +// Method values with value receivers +func _() { + var ( + _ func() = T0{}.v0 + _ func() = T0{}.p0 /* ERROR "cannot call pointer method p0 on T0" */ + + _ func() = (&T0{}).v0 + _ func() = (&T0{}).p0 + + // T1 is like T0 + + // no values for T2 + + _ func() = T3{}.v0 + _ func() = T3{}.p0 /* ERROR "cannot call pointer method p0 on T3" */ + _ func() = T3{}.v1 + _ func() = T3{}.p1 + _ func() = T3{}.v2 + _ func() = T3{}.p2 + + _ func() = (&T3{}).v0 + _ func() = (&T3{}).p0 + _ func() = (&T3{}).v1 + _ func() = (&T3{}).p1 + _ func() = (&T3{}).v2 + _ func() = (&T3{}).p2 + ) +} + +// Method calls with value receivers +func _() { + T0{}.v0() + T0{}.p0 /* ERROR "cannot call pointer method p0 on T0" */ () + + (&T0{}).v0() + (&T0{}).p0() + + // T1 is like T0 + + // no values for T2 + + T3{}.v0() + T3{}.p0 /* ERROR "cannot call pointer method p0 on T3" */ () + T3{}.v1() + T3{}.p1() + T3{}.v2() + T3{}.p2() + + (&T3{}).v0() + (&T3{}).p0() + (&T3{}).v1() + (&T3{}).p1() + (&T3{}).v2() + (&T3{}).p2() +} + +// *T has no methods if T is an interface type +func issue5918() { + var ( + err error + _ = err.Error() + _ func() string = err.Error + _ func(error) string = error.Error + + perr = &err + _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ () + _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ + _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */ + ) + + type T *interface{ m() int } + var ( + x T + _ = (*x).m() + _ = (*x).m + + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ () + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ + _ = T.m /* ERROR "type T is pointer to interface, not interface" */ + ) +} diff --git a/src/internal/types/testdata/check/shifts.go b/src/internal/types/testdata/check/shifts.go new file mode 100644 index 00000000000000..5cd0182d529498 --- /dev/null +++ b/src/internal/types/testdata/check/shifts.go @@ -0,0 +1,399 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package shifts + +func shifts0() { + // basic constant shifts + const ( + s = 10 + _ = 0<<0 + _ = 1<> s) + _, _, _ = u, v, x +} + +func shifts4() { + // shifts in comparisons w/ untyped operands + var s uint + + _ = 1<> 1.1 /* ERROR "truncated to uint" */ // example from issue 11325 + _ = 0 >> 1.1 /* ERROR "truncated to uint" */ + _ = 0 << 1.1 /* ERROR "truncated to uint" */ + _ = 0 >> 1. + _ = 1 >> 1.1 /* ERROR "truncated to uint" */ + _ = 1 >> 1. + _ = 1. >> 1 + _ = 1. >> 1. + _ = 1.1 /* ERROR "must be integer" */ >> 1 +} + +func issue11594() { + var _ = complex64 /* ERROR "must be integer" */ (1) << 2 // example from issue 11594 + _ = float32 /* ERROR "must be integer" */ (0) << 1 + _ = float64 /* ERROR "must be integer" */ (0) >> 2 + _ = complex64 /* ERROR "must be integer" */ (0) << 3 + _ = complex64 /* ERROR "must be integer" */ (0) >> 4 +} + +func issue21727() { + var s uint + var a = make([]int, 1< 0 { + return l[0], true + } + return +} + +// A test case for instantiating types with other types (extracted from map.go2) + +type Pair[K any] struct { + key K +} + +type Receiver[T any] struct { + values T +} + +type Iterator[K any] struct { + r Receiver[Pair[K]] +} + +func Values [T any] (r Receiver[T]) T { + return r.values +} + +func (it Iterator[K]) Next() K { + return Values[Pair[K]](it.r).key +} + +// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) + +type NumericAbs[T any] interface { + Abs() T +} + +func AbsDifference[T NumericAbs[T]](x T) { panic(0) } + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type OrderedAbs[T any] T +// +// func (a OrderedAbs[T]) Abs() OrderedAbs[T] +// +// func OrderedAbsDifference[T any](x T) { +// AbsDifference(OrderedAbs[T](x)) +// } + +// same code, reduced to essence + +func g[P interface{ m() P }](x P) { panic(0) } + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type T4[P any] P +// +// func (_ T4[P]) m() T4[P] +// +// func _[Q any](x Q) { +// g(T4[Q](x)) +// } + +// Another test case that caused problems in the past + +type T5[_ interface { a() }, _ interface{}] struct{} + +type A[P any] struct{ x P } + +func (_ A[P]) a() {} + +var _ T5[A[int], int] + +// Invoking methods with parameterized receiver types uses +// type inference to determine the actual type arguments matching +// the receiver type parameters from the actual receiver argument. +// Go does implicit address-taking and dereferenciation depending +// on the actual receiver and the method's receiver type. To make +// type inference work, the type-checker matches "pointer-ness" +// of the actual receiver and the method's receiver type. +// The following code tests this mechanism. + +type R1[A any] struct{} +func (_ R1[A]) vm() +func (_ *R1[A]) pm() + +func _[T any](r R1[T], p *R1[T]) { + r.vm() + r.pm() + p.vm() + p.pm() +} + +type R2[A, B any] struct{} +func (_ R2[A, B]) vm() +func (_ *R2[A, B]) pm() + +func _[T any](r R2[T, int], p *R2[string, T]) { + r.vm() + r.pm() + p.vm() + p.pm() +} + +// It is ok to have multiple embedded unions. +type _ interface { + m0() + ~int | ~string | ~bool + ~float32 | ~float64 + m1() + m2() + ~complex64 | ~complex128 + ~rune +} + +// Type sets may contain each type at most once. +type _ interface { + ~int|~ /* ERROR overlapping terms ~int */ int + ~int|int /* ERROR overlapping terms int */ + int|int /* ERROR overlapping terms int */ +} + +type _ interface { + ~struct{f int} | ~struct{g int} | ~ /* ERROR overlapping terms */ struct{f int} +} + +// Interface term lists can contain any type, incl. *Named types. +// Verify that we use the underlying type(s) of the type(s) in the +// term list when determining if an operation is permitted. + +type MyInt int +func add1[T interface{MyInt}](x T) T { + return x + 1 +} + +type MyString string +func double[T interface{MyInt|MyString}](x T) T { + return x + x +} + +// Embedding of interfaces with term lists leads to interfaces +// with term lists that are the intersection of the embedded +// term lists. + +type E0 interface { + ~int | ~bool | ~string +} + +type E1 interface { + ~int | ~float64 | ~string +} + +type E2 interface { + ~float64 +} + +type I0 interface { + E0 +} + +func f0[T I0]() {} +var _ = f0[int] +var _ = f0[bool] +var _ = f0[string] +var _ = f0[float64 /* ERROR does not implement I0 */ ] + +type I01 interface { + E0 + E1 +} + +func f01[T I01]() {} +var _ = f01[int] +var _ = f01[bool /* ERROR does not implement I0 */ ] +var _ = f01[string] +var _ = f01[float64 /* ERROR does not implement I0 */ ] + +type I012 interface { + E0 + E1 + E2 +} + +func f012[T I012]() {} +var _ = f012[int /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[bool /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[string /* ERROR cannot implement I012.*empty type set */ ] +var _ = f012[float64 /* ERROR cannot implement I012.*empty type set */ ] + +type I12 interface { + E1 + E2 +} + +func f12[T I12]() {} +var _ = f12[int /* ERROR does not implement I12 */ ] +var _ = f12[bool /* ERROR does not implement I12 */ ] +var _ = f12[string /* ERROR does not implement I12 */ ] +var _ = f12[float64] + +type I0_ interface { + E0 + ~int +} + +func f0_[T I0_]() {} +var _ = f0_[int] +var _ = f0_[bool /* ERROR does not implement I0_ */ ] +var _ = f0_[string /* ERROR does not implement I0_ */ ] +var _ = f0_[float64 /* ERROR does not implement I0_ */ ] + +// Using a function instance as a type is an error. +var _ f0 // ERROR not a type +var _ f0 /* ERROR not a type */ [int] + +// Empty type sets can only be satisfied by empty type sets. +type none interface { + // force an empty type set + int + string +} + +func ff[T none]() {} +func gg[T any]() {} +func hh[T ~int]() {} + +func _[T none]() { + _ = ff[int /* ERROR cannot implement none \(empty type set\) */ ] + _ = ff[T] // pathological but ok because T's type set is empty, too + _ = gg[int] + _ = gg[T] + _ = hh[int] + _ = hh[T] +} diff --git a/src/internal/types/testdata/check/typeinstcycles.go b/src/internal/types/testdata/check/typeinstcycles.go new file mode 100644 index 00000000000000..74fe19195a8e5e --- /dev/null +++ b/src/internal/types/testdata/check/typeinstcycles.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +func F1[T any](_ [unsafe.Sizeof(F1[int])]T) (res T) { return } +func F2[T any](_ T) (res [unsafe.Sizeof(F2[string])]int) { return } +func F3[T any](_ [unsafe.Sizeof(F1[string])]int) {} diff --git a/src/internal/types/testdata/check/typeparams.go b/src/internal/types/testdata/check/typeparams.go new file mode 100644 index 00000000000000..766500c6b99ef7 --- /dev/null +++ b/src/internal/types/testdata/check/typeparams.go @@ -0,0 +1,508 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// import "io" // for type assertion tests + +var _ any // ok to use any anywhere +func _[_ any, _ interface{any}](any) { + var _ any +} + +func identity[T any](x T) T { return x } + +func _[_ any](x int) int { panic(0) } +func _[T any](T /* ERROR redeclared */ T)() {} +func _[T, T /* ERROR redeclared */ any]() {} + +// Constraints (incl. any) may be parenthesized. +func _[_ (any)]() {} +func _[_ (interface{})]() {} + +func reverse[T any](list []T) []T { + rlist := make([]T, len(list)) + i := len(list) + for _, x := range list { + i-- + rlist[i] = x + } + return rlist +} + +var _ = reverse /* ERROR cannot use generic function reverse */ +var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3}) +var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3}) +var f = reverse[chan int] +var _ = f(0 /* ERROR cannot use 0 .* as \[\]chan int */ ) + +func swap[A, B any](a A, b B) (B, A) { return b, a } + +var _ = swap /* ERROR single value is expected */ [int, float32](1, 2) +var f32, i = swap[int, float32](swap[float32, int](1, 2)) +var _ float32 = f32 +var _ int = i + +func swapswap[A, B any](a A, b B) (A, B) { + return swap[B, A](b, a) +} + +type F[A, B any] func(A, B) (B, A) + +func min[T interface{ ~int }](x, y T) T { + if x < y { + return x + } + return y +} + +func _[T interface{~int | ~float32}](x, y T) bool { return x < y } +func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y } +func _[T interface{~int | ~float32 | ~bool}](x, y T) bool { return x /* ERROR cannot compare */ < y } + +func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y } +func _[T C2[T]](x, y T) bool { return x < y } + +type C1[T any] interface{} +type C2[T any] interface{ ~int | ~float32 } + +func new[T any]() *T { + var x T + return &x +} + +var _ = new /* ERROR cannot use generic function new */ +var _ *int = new[int]() + +func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) {} // w/o constraint we don't know if T is comparable + +func f1[T1 any](struct{T1 /* ERROR cannot be a .* type parameter */ }) int { panic(0) } +var _ = f1[int](struct{T1}{}) +type T1 = int + +func f2[t1 any](struct{t1 /* ERROR cannot be a .* type parameter */ ; x float32}) int { panic(0) } +var _ = f2[t1](struct{t1; x float32}{}) +type t1 = int + + +func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int { panic(0) } + +var _ = f3[int, rune, bool](1, struct{x rune}{}, nil) + +// indexing + +func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } +func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } +func _[T interface{ ~string }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types +func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] } +func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } +func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } + +// indexing with various combinations of map types in type sets (see issue #42616) +func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types +func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] } +func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted +func _[T interface{ ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] } +func _[T interface{ ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types +func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types + +// indexing with various combinations of array and other types in type sets +func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } +func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } +func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] } + +// indexing with strings and non-variable arrays (assignment not permitted) +func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 } +func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 } +func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 } +func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 } +func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 } +func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 } + +// slicing + +func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] } +func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j:k] } +func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j:k] } +func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] } + +type myByte1 []byte +type myByte2 []byte +func _[T interface{ []byte | myByte1 | myByte2 }] (x T, i, j, k int) { var _ T = x[i:j:k] } +func _[T interface{ []byte | myByte1 | []int }] (x T, i, j, k int) { var _ T = x /* ERROR no core type */ [i:j:k] } + +func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j] } +func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j:k /* ERROR 3-index slice of string */ ] } +func _[T interface{ []byte | myByte1 | []int | string }] (x T, i, j, k int) { var _ T = x /* ERROR no core type */ [i:j] } + +// len/cap built-ins + +func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) } +func _[T interface{ ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string }](x T) { _ = len(x) } +func _[T interface{ ~[10]int }](x T) { _ = len(x) } +func _[T interface{ ~[]byte }](x T) { _ = len(x) } +func _[T interface{ ~map[int]int }](x T) { _ = len(x) } +func _[T interface{ ~chan int }](x T) { _ = len(x) } +func _[T interface{ ~string | ~[]byte | ~chan int }](x T) { _ = len(x) } + +func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~[10]int }](x T) { _ = cap(x) } +func _[T interface{ ~[]byte }](x T) { _ = cap(x) } +func _[T interface{ ~map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } +func _[T interface{ ~chan int }](x T) { _ = cap(x) } +func _[T interface{ ~[]byte | ~chan int }](x T) { _ = cap(x) } + +// range iteration + +func _[T interface{}](x T) { + for range x /* ERROR cannot range */ {} +} + +type myString string + +func _[ + B1 interface{ string }, + B2 interface{ string | myString }, + + C1 interface{ chan int }, + C2 interface{ chan int | <-chan int }, + C3 interface{ chan<- int }, + + S1 interface{ []int }, + S2 interface{ []int | [10]int }, + + A1 interface{ [10]int }, + A2 interface{ [10]int | []int }, + + P1 interface{ *[10]int }, + P2 interface{ *[10]int | *[]int }, + + M1 interface{ map[string]int }, + M2 interface{ map[string]int | map[string]string }, +]() { + var b0 string + for range b0 {} + for _ = range b0 {} + for _, _ = range b0 {} + + var b1 B1 + for range b1 {} + for _ = range b1 {} + for _, _ = range b1 {} + + var b2 B2 + for range b2 {} + + var c0 chan int + for range c0 {} + for _ = range c0 {} + for _, _ /* ERROR permits only one iteration variable */ = range c0 {} + + var c1 C1 + for range c1 {} + for _ = range c1 {} + for _, _ /* ERROR permits only one iteration variable */ = range c1 {} + + var c2 C2 + for range c2 {} + + var c3 C3 + for range c3 /* ERROR receive from send-only channel */ {} + + var s0 []int + for range s0 {} + for _ = range s0 {} + for _, _ = range s0 {} + + var s1 S1 + for range s1 {} + for _ = range s1 {} + for _, _ = range s1 {} + + var s2 S2 + for range s2 /* ERROR cannot range over s2.*no core type */ {} + + var a0 []int + for range a0 {} + for _ = range a0 {} + for _, _ = range a0 {} + + var a1 A1 + for range a1 {} + for _ = range a1 {} + for _, _ = range a1 {} + + var a2 A2 + for range a2 /* ERROR cannot range over a2.*no core type */ {} + + var p0 *[10]int + for range p0 {} + for _ = range p0 {} + for _, _ = range p0 {} + + var p1 P1 + for range p1 {} + for _ = range p1 {} + for _, _ = range p1 {} + + var p2 P2 + for range p2 /* ERROR cannot range over p2.*no core type */ {} + + var m0 map[string]int + for range m0 {} + for _ = range m0 {} + for _, _ = range m0 {} + + var m1 M1 + for range m1 {} + for _ = range m1 {} + for _, _ = range m1 {} + + var m2 M2 + for range m2 /* ERROR cannot range over m2.*no core type */ {} +} + +// type inference checks + +var _ = new /* ERROR cannot infer T */ () + +func f4[A, B, C any](A, B) C { panic(0) } + +var _ = f4 /* ERROR cannot infer C */ (1, 2) +var _ = f4[int, float32, complex128](1, 2) + +func f5[A, B, C any](A, []*B, struct{f []C}) int { panic(0) } + +var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{}) +var _ = f5 /* ERROR cannot infer */ (0, nil, struct{f []complex128}{}) +var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{}) + +func f6[A any](A, []A) int { panic(0) } + +var _ = f6(0, nil) + +func f6nil[A any](A) int { panic(0) } + +var _ = f6nil /* ERROR cannot infer */ (nil) + +// type inference with variadic functions + +func f7[T any](...T) T { panic(0) } + +var _ int = f7 /* ERROR cannot infer T */ () +var _ int = f7(1) +var _ int = f7(1, 2) +var _ int = f7([]int{}...) +var _ int = f7 /* ERROR cannot use */ ([]float64{}...) +var _ float64 = f7([]float64{}...) +var _ = f7[float64](1, 2.3) +var _ = f7(float64(1), 2.3) +var _ = f7(1, 2.3 /* ERROR does not match */ ) +var _ = f7(1.2, 3 /* ERROR does not match */ ) + +func f8[A, B any](A, B, ...B) int { panic(0) } + +var _ = f8(1) /* ERROR not enough arguments */ +var _ = f8(1, 2.3) +var _ = f8(1, 2.3, 3.4, 4.5) +var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) +var _ = f8[int, float64](1, 2.3, 3.4, 4) + +var _ = f8[int, float64](0, 0, nil...) // test case for #18268 + +// init functions cannot have type parameters + +func init() {} +func init[_ /* ERROR func init must have no type parameters */ any]() {} +func init[P /* ERROR func init must have no type parameters */ any]() {} + +type T struct {} + +func (T) m1() {} +func (T) m2[ /* ERROR method must have no type parameters */ _ any]() {} +func (T) m3[ /* ERROR method must have no type parameters */ P any]() {} + +// type inference across parameterized types + +type S1[P any] struct { f P } + +func f9[P any](x S1[P]) {} + +func _() { + f9[int](S1[int]{42}) + f9(S1[int]{42}) +} + +type S2[A, B, C any] struct{} + +func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) {} + +func _[P any]() { + f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{}) + f10(S2[int, int, string]{}, S2[int, float32, bool]{}) + f10(S2[P, int, P]{}, S2[P, float32, bool]{}) +} + +// corner case for type inference +// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) + +func f11[T any]() {} + +func _() { + f11[int]() +} + +// the previous example was extracted from + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// func f12[T interface{m() T}]() {} +// +// type A[T any] T +// +// func (a A[T]) m() A[T] +// +// func _[T any]() { +// f12[A[T]]() +// } + +// method expressions + +func (_ S1[P]) m() + +func _() { + m := S1[int].m + m(struct { f int }{42}) +} + +func _[T any] (x T) { + m := S1[T].m + m(S1[T]{x}) +} + +type I1[A any] interface { + m1(A) +} + +var _ I1[int] = r1[int]{} + +type r1[T any] struct{} + +func (_ r1[T]) m1(T) + +type I2[A, B any] interface { + m1(A) + m2(A) B +} + +var _ I2[int, float32] = R2[int, float32]{} + +type R2[P, Q any] struct{} + +func (_ R2[X, Y]) m1(X) +func (_ R2[X, Y]) m2(X) Y + +// type assertions and type switches over generic types +// NOTE: These are currently disabled because it's unclear what the correct +// approach is, and one can always work around by assigning the variable to +// an interface first. + +// // ReadByte1 corresponds to the ReadByte example in the draft design. +// func ReadByte1[T io.Reader](r T) (byte, error) { +// if br, ok := r.(io.ByteReader); ok { +// return br.ReadByte() +// } +// var b [1]byte +// _, err := r.Read(b[:]) +// return b[0], err +// } +// +// // ReadBytes2 is like ReadByte1 but uses a type switch instead. +// func ReadByte2[T io.Reader](r T) (byte, error) { +// switch br := r.(type) { +// case io.ByteReader: +// return br.ReadByte() +// } +// var b [1]byte +// _, err := r.Read(b[:]) +// return b[0], err +// } +// +// // type assertions and type switches over generic types are strict +// type I3 interface { +// m(int) +// } +// +// type I4 interface { +// m() int // different signature from I3.m +// } +// +// func _[T I3](x I3, p T) { +// // type assertions and type switches over interfaces are not strict +// _ = x.(I4) +// switch x.(type) { +// case I4: +// } +// +// // type assertions and type switches over generic types are strict +// _ = p /* ERROR cannot have dynamic type I4 */.(I4) +// switch p.(type) { +// case I4 /* ERROR cannot have dynamic type I4 */ : +// } +// } + +// type assertions and type switches over generic types lead to errors for now + +func _[T any](x T) { + _ = x /* ERROR cannot use type assertion */ .(int) + switch x /* ERROR cannot use type switch */ .(type) { + } + + // work-around + var t interface{} = x + _ = t.(int) + switch t.(type) { + } +} + +func _[T interface{~int}](x T) { + _ = x /* ERROR cannot use type assertion */ .(int) + switch x /* ERROR cannot use type switch */ .(type) { + } + + // work-around + var t interface{} = x + _ = t.(int) + switch t.(type) { + } +} + +// error messages related to type bounds mention those bounds +type C[P any] interface{} + +func _[P C[P]] (x P) { + x.m /* ERROR x.m undefined */ () +} + +type I interface {} + +func _[P I] (x P) { + x.m /* ERROR type P has no field or method m */ () +} + +func _[P interface{}] (x P) { + x.m /* ERROR type P has no field or method m */ () +} + +func _[P any] (x P) { + x.m /* ERROR type P has no field or method m */ () +} diff --git a/src/internal/types/testdata/check/unions.go b/src/internal/types/testdata/check/unions.go new file mode 100644 index 00000000000000..bcd7de66448168 --- /dev/null +++ b/src/internal/types/testdata/check/unions.go @@ -0,0 +1,66 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check that overlong unions don't bog down type checking. +// Disallow them for now. + +package p + +type t int + +type ( + t00 t; t01 t; t02 t; t03 t; t04 t; t05 t; t06 t; t07 t; t08 t; t09 t + t10 t; t11 t; t12 t; t13 t; t14 t; t15 t; t16 t; t17 t; t18 t; t19 t + t20 t; t21 t; t22 t; t23 t; t24 t; t25 t; t26 t; t27 t; t28 t; t29 t + t30 t; t31 t; t32 t; t33 t; t34 t; t35 t; t36 t; t37 t; t38 t; t39 t + t40 t; t41 t; t42 t; t43 t; t44 t; t45 t; t46 t; t47 t; t48 t; t49 t + t50 t; t51 t; t52 t; t53 t; t54 t; t55 t; t56 t; t57 t; t58 t; t59 t + t60 t; t61 t; t62 t; t63 t; t64 t; t65 t; t66 t; t67 t; t68 t; t69 t + t70 t; t71 t; t72 t; t73 t; t74 t; t75 t; t76 t; t77 t; t78 t; t79 t + t80 t; t81 t; t82 t; t83 t; t84 t; t85 t; t86 t; t87 t; t88 t; t89 t + t90 t; t91 t; t92 t; t93 t; t94 t; t95 t; t96 t; t97 t; t98 t; t99 t +) + +type u99 interface { + t00|t01|t02|t03|t04|t05|t06|t07|t08|t09| + t10|t11|t12|t13|t14|t15|t16|t17|t18|t19| + t20|t21|t22|t23|t24|t25|t26|t27|t28|t29| + t30|t31|t32|t33|t34|t35|t36|t37|t38|t39| + t40|t41|t42|t43|t44|t45|t46|t47|t48|t49| + t50|t51|t52|t53|t54|t55|t56|t57|t58|t59| + t60|t61|t62|t63|t64|t65|t66|t67|t68|t69| + t70|t71|t72|t73|t74|t75|t76|t77|t78|t79| + t80|t81|t82|t83|t84|t85|t86|t87|t88|t89| + t90|t91|t92|t93|t94|t95|t96|t97|t98 +} + +type u100a interface { + u99|float32 +} + +type u100b interface { + u99|float64 +} + +type u101 interface { + t00|t01|t02|t03|t04|t05|t06|t07|t08|t09| + t10|t11|t12|t13|t14|t15|t16|t17|t18|t19| + t20|t21|t22|t23|t24|t25|t26|t27|t28|t29| + t30|t31|t32|t33|t34|t35|t36|t37|t38|t39| + t40|t41|t42|t43|t44|t45|t46|t47|t48|t49| + t50|t51|t52|t53|t54|t55|t56|t57|t58|t59| + t60|t61|t62|t63|t64|t65|t66|t67|t68|t69| + t70|t71|t72|t73|t74|t75|t76|t77|t78|t79| + t80|t81|t82|t83|t84|t85|t86|t87|t88|t89| + t90|t91|t92|t93|t94|t95|t96|t97|t98|t99| + int // ERROR cannot handle more than 100 union terms +} + +type u102 interface { + int /* ERROR cannot handle more than 100 union terms */ |string|u100a +} + +type u200 interface { + u100a /* ERROR cannot handle more than 100 union terms */ |u100b +} diff --git a/src/internal/types/testdata/check/vardecl.go b/src/internal/types/testdata/check/vardecl.go new file mode 100644 index 00000000000000..11591af385a986 --- /dev/null +++ b/src/internal/types/testdata/check/vardecl.go @@ -0,0 +1,215 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package vardecl + +// Prerequisites. +import "math" +func f() {} +func g() (x, y int) { return } +var m map[string]int + +// Var decls must have a type or an initializer. +var _ int +var _, _ int + +var _; /* ERROR "expected type" */ +var _, _; /* ERROR "expected type" */ +var _, _, _; /* ERROR "expected type" */ + +// The initializer must be an expression. +var _ = int /* ERROR "not an expression" */ +var _ = f /* ERROR "used as value" */ () + +// Identifier and expression arity must match. +var _, _ = 1, 2 +var _ = 1, 2 /* ERROR "extra init expr 2" */ +var _, _ = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ +var _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 + +var _ = g /* ERROR "2-valued g" */ () +var _, _ = g() +var _, _, _ = g /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ () + +var _ = m["foo"] +var _, _ = m["foo"] +var _, _, _ = m /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ ["foo"] + +var _, _ int = 1, 2 +var _ int = 1, 2 /* ERROR "extra init expr 2" */ +var _, _ int = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ +var _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 + +var ( + _, _ = 1, 2 + _ = 1, 2 /* ERROR "extra init expr 2" */ + _, _ = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ + _, _, _ /* ERROR "missing init expr for _" */ = 1, 2 + + _ = g /* ERROR "2-valued g" */ () + _, _ = g() + _, _, _ = g /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ () + + _ = m["foo"] + _, _ = m["foo"] + _, _, _ = m /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ ["foo"] + + _, _ int = 1, 2 + _ int = 1, 2 /* ERROR "extra init expr 2" */ + _, _ int = 1 /* ERROR "cannot initialize [0-9]+ variables with [0-9]+ values" */ + _, _, _ /* ERROR "missing init expr for _" */ int = 1, 2 +) + +// Variables declared in function bodies must be 'used'. +type T struct{} +func (r T) _(a, b, c int) (u, v, w int) { + var x1 /* ERROR "declared but not used" */ int + var x2 /* ERROR "declared but not used" */ int + x1 = 1 + (x2) = 2 + + y1 /* ERROR "declared but not used" */ := 1 + y2 /* ERROR "declared but not used" */ := 2 + y1 = 1 + (y1) = 2 + + { + var x1 /* ERROR "declared but not used" */ int + var x2 /* ERROR "declared but not used" */ int + x1 = 1 + (x2) = 2 + + y1 /* ERROR "declared but not used" */ := 1 + y2 /* ERROR "declared but not used" */ := 2 + y1 = 1 + (y1) = 2 + } + + if x /* ERROR "declared but not used" */ := 0; a < b {} + + switch x /* ERROR "declared but not used" */, y := 0, 1; a { + case 0: + _ = y + case 1: + x /* ERROR "declared but not used" */ := 0 + } + + var t interface{} + switch t /* ERROR "declared but not used" */ := t.(type) {} + + switch t /* ERROR "declared but not used" */ := t.(type) { + case int: + } + + switch t /* ERROR "declared but not used" */ := t.(type) { + case int: + case float32, complex64: + t = nil + } + + switch t := t.(type) { + case int: + case float32, complex64: + _ = t + } + + switch t := t.(type) { + case int: + case float32: + case string: + _ = func() string { + return t + } + } + + switch t := t; t /* ERROR "declared but not used" */ := t.(type) {} + + var z1 /* ERROR "declared but not used" */ int + var z2 int + _ = func(a, b, c int) (u, v, w int) { + z1 = a + (z1) = b + a = z2 + return + } + + var s []int + var i /* ERROR "declared but not used" */ , j int + for i, j = range s { + _ = j + } + + for i, j /* ERROR "declared but not used" */ := range s { + _ = func() int { + return i + } + } + return +} + +// Unused variables in function literals must lead to only one error (issue #22524). +func _() { + _ = func() { + var x /* ERROR declared but not used */ int + } +} + +// Invalid variable declarations must not lead to "declared but not used errors". +// TODO(gri) enable these tests once go/types follows types2 logic for declared but not used variables +// func _() { +// var a x // DISABLED_ERROR undeclared name: x +// var b = x // DISABLED_ERROR undeclared name: x +// var c int = x // DISABLED_ERROR undeclared name: x +// var d, e, f x /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ +// var g, h, i = x, x, x /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ +// var j, k, l float32 = x, x, x /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ /* DISABLED_ERROR x */ +// // but no "declared but not used" errors +// } + +// Invalid (unused) expressions must not lead to spurious "declared but not used errors". +func _() { + var a, b, c int + var x, y int + x, y = a /* ERROR cannot assign [0-9]+ values to [0-9]+ variables */ , b, c + _ = x + _ = y +} + +func _() { + var x int + return x /* ERROR too many return values */ + return math /* ERROR too many return values */ .Sin(0) +} + +func _() int { + var x, y int + return x, y /* ERROR too many return values */ +} + +// Short variable declarations must declare at least one new non-blank variable. +func _() { + _ := /* ERROR no new variables */ 0 + _, a := 0, 1 + _, a := /* ERROR no new variables */ 0, 1 + _, a, b := 0, 1, 2 + _, _, _ := /* ERROR no new variables */ 0, 1, 2 + + _ = a + _ = b +} + +// Test case for variables depending on function literals (see also #22992). +var A /* ERROR initialization cycle */ = func() int { return A }() + +func _() { + // The function literal below must not see a. + var a = func() int { return a /* ERROR "undeclared name" */ }() + var _ = func() int { return a }() + + // The function literal below must not see x, y, or z. + var x, y, z = 0, 1, func() int { return x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ }() + _, _, _ = x, y, z +} + +// TODO(gri) consolidate other var decl checks in this file \ No newline at end of file diff --git a/src/internal/types/testdata/examples/constraints.go b/src/internal/types/testdata/examples/constraints.go new file mode 100644 index 00000000000000..5b144893cef373 --- /dev/null +++ b/src/internal/types/testdata/examples/constraints.go @@ -0,0 +1,80 @@ +// Copyright 2021 The Go 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 shows some examples of generic constraint interfaces. + +package p + +type MyInt int + +type ( + // Arbitrary types may be embedded like interfaces. + _ interface{int} + _ interface{~int} + + // Types may be combined into a union. + union interface{int|~string} + + // Union terms must describe disjoint (non-overlapping) type sets. + _ interface{int|int /* ERROR overlapping terms int */ } + _ interface{int|~ /* ERROR overlapping terms ~int */ int } + _ interface{~int|~ /* ERROR overlapping terms ~int */ int } + _ interface{~int|MyInt /* ERROR overlapping terms p.MyInt and ~int */ } + _ interface{int|any} + _ interface{int|~string|union} + _ interface{int|~string|interface{int}} + _ interface{union|int} // interfaces (here: union) are ignored when checking for overlap + _ interface{union|union} // ditto + + // For now we do not permit interfaces with methods in unions. + _ interface{~ /* ERROR invalid use of ~ */ any} + _ interface{int|interface /* ERROR cannot use .* in union */ { m() }} +) + +type ( + // Tilde is not permitted on defined types or interfaces. + foo int + bar any + _ interface{foo} + _ interface{~ /* ERROR invalid use of ~ */ foo } + _ interface{~ /* ERROR invalid use of ~ */ bar } +) + +// Stand-alone type parameters are not permitted as elements or terms in unions. +type ( + _[T interface{ *T } ] struct{} // ok + _[T interface{ int | *T } ] struct{} // ok + _[T interface{ T /* ERROR term cannot be a type parameter */ } ] struct{} + _[T interface{ ~T /* ERROR type in term ~T cannot be a type parameter */ } ] struct{} + _[T interface{ int|T /* ERROR term cannot be a type parameter */ }] struct{} +) + +// Multiple embedded union elements are intersected. The order in which they +// appear in the interface doesn't matter since intersection is a symmetric +// operation. + +type myInt1 int +type myInt2 int + +func _[T interface{ myInt1|myInt2; ~int }]() T { return T(0) } +func _[T interface{ ~int; myInt1|myInt2 }]() T { return T(0) } + +// Here the intersections are empty - there's no type that's in the type set of T. +func _[T interface{ myInt1|myInt2; int }]() T { return T(0 /* ERROR cannot convert */ ) } +func _[T interface{ int; myInt1|myInt2 }]() T { return T(0 /* ERROR cannot convert */ ) } + +// Union elements may be interfaces as long as they don't define +// any methods or embed comparable. + +type ( + Integer interface{ ~int|~int8|~int16|~int32|~int64 } + Unsigned interface{ ~uint|~uint8|~uint16|~uint32|~uint64 } + Floats interface{ ~float32|~float64 } + Complex interface{ ~complex64|~complex128 } + Number interface{ Integer|Unsigned|Floats|Complex } + Ordered interface{ Integer|Unsigned|Floats|~string } + + _ interface{ Number | error /* ERROR cannot use error in union */ } + _ interface{ Ordered | comparable /* ERROR cannot use comparable in union */ } +) diff --git a/src/internal/types/testdata/examples/functions.go b/src/internal/types/testdata/examples/functions.go new file mode 100644 index 00000000000000..244c9dd2283998 --- /dev/null +++ b/src/internal/types/testdata/examples/functions.go @@ -0,0 +1,219 @@ +// Copyright 2019 The Go 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 shows some examples of type-parameterized functions. + +package p + +// Reverse is a generic function that takes a []T argument and +// reverses that slice in place. +func Reverse[T any](list []T) { + i := 0 + j := len(list)-1 + for i < j { + list[i], list[j] = list[j], list[i] + i++ + j-- + } +} + +func _() { + // Reverse can be called with an explicit type argument. + Reverse[int](nil) + Reverse[string]([]string{"foo", "bar"}) + Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) + + // Since the type parameter is used for an incoming argument, + // it can be inferred from the provided argument's type. + Reverse([]string{"foo", "bar"}) + Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}}) + + // But the incoming argument must have a type, even if it's a + // default type. An untyped nil won't work. + // Reverse(nil) // this won't type-check + + // A typed nil will work, though. + Reverse([]int(nil)) +} + +// Certain functions, such as the built-in `new` could be written using +// type parameters. +func new[T any]() *T { + var x T + return &x +} + +// When calling our own `new`, we need to pass the type parameter +// explicitly since there is no (value) argument from which the +// result type could be inferred. We don't try to infer the +// result type from the assignment to keep things simple and +// easy to understand. +var _ = new[int]() +var _ *float64 = new[float64]() // the result type is indeed *float64 + +// A function may have multiple type parameters, of course. +func foo[A, B, C any](a A, b []B, c *C) B { + // do something here + return b[0] +} + +// As before, we can pass type parameters explicitly. +var s = foo[int, string, float64](1, []string{"first"}, new[float64]()) + +// Or we can use type inference. +var _ float64 = foo(42, []float64{1.0}, &s) + +// Type inference works in a straight-forward manner even +// for variadic functions. +func variadic[A, B any](A, B, ...B) int { panic(0) } + +// var _ = variadic(1) // ERROR not enough arguments +var _ = variadic(1, 2.3) +var _ = variadic(1, 2.3, 3.4, 4.5) +var _ = variadic[int, float64](1, 2.3, 3.4, 4) + +// Type inference also works in recursive function calls where +// the inferred type is the type parameter of the caller. +func f1[T any](x T) { + f1(x) +} + +func f2a[T any](x, y T) { + f2a(x, y) +} + +func f2b[T any](x, y T) { + f2b(y, x) +} + +func g2a[P, Q any](x P, y Q) { + g2a(x, y) +} + +func g2b[P, Q any](x P, y Q) { + g2b(y, x) +} + +// Here's an example of a recursive function call with variadic +// arguments and type inference inferring the type parameter of +// the caller (i.e., itself). +func max[T interface{ ~int }](x ...T) T { + var x0 T + if len(x) > 0 { + x0 = x[0] + } + if len(x) > 1 { + x1 := max(x[1:]...) + if x1 > x0 { + return x1 + } + } + return x0 +} + +// When inferring channel types, the channel direction is ignored +// for the purpose of type inference. Once the type has been in- +// fered, the usual parameter passing rules are applied. +// Thus even if a type can be inferred successfully, the function +// call may not be valid. + +func fboth[T any](chan T) {} +func frecv[T any](<-chan T) {} +func fsend[T any](chan<- T) {} + +func _() { + var both chan int + var recv <-chan int + var send chan<-int + + fboth(both) + fboth(recv /* ERROR cannot use */ ) + fboth(send /* ERROR cannot use */ ) + + frecv(both) + frecv(recv) + frecv(send /* ERROR cannot use */ ) + + fsend(both) + fsend(recv /* ERROR cannot use */) + fsend(send) +} + +func ffboth[T any](func(chan T)) {} +func ffrecv[T any](func(<-chan T)) {} +func ffsend[T any](func(chan<- T)) {} + +func _() { + var both func(chan int) + var recv func(<-chan int) + var send func(chan<- int) + + ffboth(both) + ffboth(recv /* ERROR cannot use */ ) + ffboth(send /* ERROR cannot use */ ) + + ffrecv(both /* ERROR cannot use */ ) + ffrecv(recv) + ffrecv(send /* ERROR cannot use */ ) + + ffsend(both /* ERROR cannot use */ ) + ffsend(recv /* ERROR cannot use */ ) + ffsend(send) +} + +// When inferring elements of unnamed composite parameter types, +// if the arguments are defined types, use their underlying types. +// Even though the matching types are not exactly structurally the +// same (one is a type literal, the other a named type), because +// assignment is permitted, parameter passing is permitted as well, +// so type inference should be able to handle these cases well. + +func g1[T any]([]T) {} +func g2[T any]([]T, T) {} +func g3[T any](*T, ...T) {} + +func _() { + type intSlize []int + g1([]int{}) + g1(intSlize{}) + g2(nil, 0) + + type myString string + var s1 string + g3(nil, "1", myString("2"), "3") + g3(& /* ERROR does not match */ s1, "1", myString("2"), "3") + _ = s1 + + type myStruct struct{x int} + var s2 myStruct + g3(nil, struct{x int}{}, myStruct{}) + g3(&s2, struct{x int}{}, myStruct{}) + g3(nil, myStruct{}, struct{x int}{}) + g3(&s2, myStruct{}, struct{x int}{}) +} + +// Here's a realistic example. + +func append[T any](s []T, t ...T) []T { panic(0) } + +func _() { + var f func() + type Funcs []func() + var funcs Funcs + _ = append(funcs, f) +} + +// Generic type declarations cannot have empty type parameter lists +// (that would indicate a slice type). Thus, generic functions cannot +// have empty type parameter lists, either. This is a syntax error. + +func h[] /* ERROR empty type parameter list */ () {} + +func _() { + h /* ERROR cannot index */ [] /* ERROR operand */ () +} + +// Parameterized functions must have a function body. + +func _ /* ERROR missing function body */ [P any]() diff --git a/src/internal/types/testdata/examples/inference.go b/src/internal/types/testdata/examples/inference.go new file mode 100644 index 00000000000000..23a3d81f3deb20 --- /dev/null +++ b/src/internal/types/testdata/examples/inference.go @@ -0,0 +1,116 @@ +// Copyright 2021 The Go 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 shows some examples of type inference. + +package p + +type Ordered interface { + ~int | ~float64 | ~string +} + +func min[T Ordered](x, y T) T { panic(0) } + +func _() { + // min can be called with explicit instantiation. + _ = min[int](1, 2) + + // Alternatively, the type argument can be inferred from + // one of the arguments. Untyped arguments will be considered + // last. + var x int + _ = min(x, x) + _ = min(x, 1) + _ = min(x, 1.0) + _ = min(1, 2) + _ = min(1, 2.3 /* ERROR default type float64 .* does not match */) + + var y float64 + _ = min(1, y) + _ = min(1.2, y) + _ = min(1.2, 3.4) + _ = min(1.2, 3 /* ERROR default type int .* does not match */) + + var s string + _ = min(s, "foo") + _ = min("foo", "bar") +} + +func mixed[T1, T2, T3 any](T1, T2, T3) {} + +func _() { + // mixed can be called with explicit instantiation. + mixed[int, string, bool](0, "", false) + + // Alternatively, partial type arguments may be provided + // (from left to right), and the other may be inferred. + mixed[int, string](0, "", false) + mixed[int](0, "", false) + mixed(0, "", false) + + // Provided type arguments always take precedence over + // inferred types. + mixed[int, string](1.1 /* ERROR cannot use 1.1 */, "", false) +} + +func related1[Slice interface{ ~[]Elem }, Elem any](s Slice, e Elem) {} + +func _() { + // related1 can be called with explicit instantiation. + var si []int + related1[[]int, int](si, 0) + + // Alternatively, the 2nd type argument can be inferred + // from the first one through constraint type inference. + var ss []string + _ = related1[[]string] + related1[[]string](ss, "foo") + + // A type argument inferred from another explicitly provided + // type argument overrides whatever value argument type is given. + related1[[]string](ss, 0 /* ERROR cannot use 0 */) + + // A type argument may be inferred from a value argument + // and then help infer another type argument via constraint + // type inference. + related1(si, 0) + related1(si, "foo" /* ERROR cannot use "foo" */) +} + +func related2[Elem any, Slice interface{ []Elem }](e Elem, s Slice) {} + +func _() { + // related2 can be called with explicit instantiation. + var si []int + related2[int, []int](0, si) + + // Alternatively, the 2nd type argument can be inferred + // from the first one through constraint type inference. + var ss []string + _ = related2[string] + related2[string]("foo", ss) + + // A type argument may be inferred from a value argument + // and then help infer another type argument via constraint + // type inference. Untyped arguments are always considered + // last. + related2(1.2, []float64{}) + related2(1.0, []int{}) + related2 /* ERROR does not implement */ (float64(1.0), []int{}) // TODO(gri) fix error position +} + +type List[P any] []P + +func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil } + +func _() { + // related3 can be instantiated explicitly + related3[int, []int]() + related3[byte, List[byte]]() + + // The 2nd type argument cannot be inferred from the first + // one because there's two possible choices: []Elem and + // List[Elem]. + related3 /* ERROR cannot infer Slice */ [int]() +} diff --git a/src/internal/types/testdata/examples/methods.go b/src/internal/types/testdata/examples/methods.go new file mode 100644 index 00000000000000..a46f789d602ce5 --- /dev/null +++ b/src/internal/types/testdata/examples/methods.go @@ -0,0 +1,112 @@ +// Copyright 2019 The Go 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 shows some examples of methods on type-parameterized types. + +package p + +// Parameterized types may have methods. +type T1[A any] struct{ a A } + +// When declaring a method for a parameterized type, the "instantiated" +// receiver type acts as an implicit declaration of the type parameters +// for the receiver type. In the example below, method m1 on type T1 has +// the receiver type T1[A] which declares the type parameter A for use +// with this method. That is, within the method m1, A stands for the +// actual type argument provided to an instantiated T1. +func (t T1[A]) m1() A { return t.a } + +// For instance, if T1 is instantiated with the type int, the type +// parameter A in m1 assumes that type (int) as well and we can write +// code like this: +var x T1[int] +var _ int = x.m1() + +// Because the type parameter provided to a parameterized receiver type +// is declared through that receiver declaration, it must be an identifier. +// It cannot possibly be some other type because the receiver type is not +// instantiated with concrete types, it is standing for the parameterized +// receiver type. +func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {} + +// Note that using what looks like a predeclared identifier, say int, +// as type parameter in this situation is deceptive and considered bad +// style. In m3 below, int is the name of the local receiver type parameter +// and it shadows the predeclared identifier int which then cannot be used +// anymore as expected. +// This is no different from locally re-declaring a predeclared identifier +// and usually should be avoided. There are some notable exceptions; e.g., +// sometimes it makes sense to use the identifier "copy" which happens to +// also be the name of a predeclared built-in function. +func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot use 42 .* as int */ } + +// The names of the type parameters used in a parameterized receiver +// type don't have to match the type parameter names in the declaration +// of the type used for the receiver. In our example, even though T1 is +// declared with type parameter named A, methods using that receiver type +// are free to use their own name for that type parameter. That is, the +// name of type parameters is always local to the declaration where they +// are introduced. In our example we can write a method m2 and use the +// name X instead of A for the type parameter w/o any difference. +func (t T1[X]) m4() X { return t.a } + +// If the receiver type is parameterized, type parameters must always be +// provided: this simply follows from the general rule that a parameterized +// type must be instantiated before it can be used. A method receiver +// declaration using a parameterized receiver type is no exception. It is +// simply that such receiver type expressions perform two tasks simultaneously: +// they declare the (local) type parameters and then use them to instantiate +// the receiver type. Forgetting to provide a type parameter leads to an error. +func (t T1 /* ERROR generic type .* without instantiation */ ) m5() {} + +// However, sometimes we don't need the type parameter, and thus it is +// inconvenient to have to choose a name. Since the receiver type expression +// serves as a declaration for its type parameters, we are free to choose the +// blank identifier: +func (t T1[_]) m6() {} + +// Naturally, these rules apply to any number of type parameters on the receiver +// type. Here are some more complex examples. +type T2[A, B, C any] struct { + a A + b B + c C +} + +// Naming of the type parameters is local and has no semantic impact: +func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c } +func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c } +func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c } + +// Type parameters may be left blank if they are not needed: +func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c } +func (t T2[_, _, X]) m5() X { return t.c } +func (t T2[_, _, _]) m6() {} + +// As usual, blank names may be used for any object which we don't care about +// using later. For instance, we may write an unnamed method with a receiver +// that cannot be accessed: +func (_ T2[_, _, _]) _() int { return 42 } + +// Because a receiver parameter list is simply a parameter list, we can +// leave the receiver argument away for receiver types. +type T0 struct{} +func (T0) _() {} +func (T1[A]) _() {} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // A generic receiver type may constrain its type parameter such +// // that it must be a pointer type. Such receiver types are not +// // permitted. +// type T3a[P interface{ ~int | ~string | ~float64 }] P +// +// func (T3a[_]) m() {} // this is ok +// +// type T3b[P interface{ ~unsafe.Pointer }] P +// +// func (T3b /* ERROR invalid receiver */ [_]) m() {} +// +// type T3c[P interface{ *int | *string }] P +// +// func (T3c /* ERROR invalid receiver */ [_]) m() {} diff --git a/src/internal/types/testdata/examples/operations.go b/src/internal/types/testdata/examples/operations.go new file mode 100644 index 00000000000000..18e4d6080c59f0 --- /dev/null +++ b/src/internal/types/testdata/examples/operations.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Go Authors. 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 + +// indirection + +func _[P any](p P) { + _ = *p // ERROR cannot indirect p +} + +func _[P interface{ int }](p P) { + _ = *p // ERROR cannot indirect p +} + +func _[P interface{ *int }](p P) { + _ = *p +} + +func _[P interface{ *int | *string }](p P) { + _ = *p // ERROR must have identical base types +} + +type intPtr *int + +func _[P interface{ *int | intPtr } ](p P) { + var _ int = *p +} diff --git a/src/internal/types/testdata/examples/types.go b/src/internal/types/testdata/examples/types.go new file mode 100644 index 00000000000000..052d168fc6fcc9 --- /dev/null +++ b/src/internal/types/testdata/examples/types.go @@ -0,0 +1,315 @@ +// Copyright 2019 The Go 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 shows some examples of generic types. + +package p + +// List is just what it says - a slice of E elements. +type List[E any] []E + +// A generic (parameterized) type must always be instantiated +// before it can be used to designate the type of a variable +// (including a struct field, or function parameter); though +// for the latter cases, the provided type may be another type +// parameter. So: +var _ List[byte] = []byte{} + +// A generic binary tree might be declared as follows. +type Tree[E any] struct { + left, right *Tree[E] + payload E +} + +// A simple instantiation of Tree: +var root1 Tree[int] + +// The actual type parameter provided may be a generic type itself: +var root2 Tree[List[int]] + +// A couple of more complex examples. +// We don't need extra parentheses around the element type of the slices on +// the right (unlike when we use ()'s rather than []'s for type parameters). +var _ List[List[int]] = []List[int]{} +var _ List[List[List[Tree[int]]]] = []List[List[Tree[int]]]{} + +// Type parameters act like type aliases when used in generic types +// in the sense that we can "emulate" a specific type instantiation +// with type aliases. +type T1[P any] struct { + f P +} + +type T2[P any] struct { + f struct { + g P + } +} + +var x1 T1[struct{ g int }] +var x2 T2[int] + +func _() { + // This assignment is invalid because the types of x1, x2 are T1(...) + // and T2(...) respectively, which are two different defined types. + x1 = x2 // ERROR assignment + + // This assignment is valid because the types of x1.f and x2.f are + // both struct { g int }; the type parameters act like type aliases + // and their actual names don't come into play here. + x1.f = x2.f +} + +// We can verify this behavior using type aliases instead: +type T1a struct { + f A1 +} +type A1 = struct { g int } + +type T2a struct { + f struct { + g A2 + } +} +type A2 = int + +var x1a T1a +var x2a T2a + +func _() { + x1a = x2a // ERROR assignment + x1a.f = x2a.f +} + +// Another interesting corner case are generic types that don't use +// their type arguments. For instance: +type T[P any] struct{} + +var xint T[int] +var xbool T[bool] + +// Are these two variables of the same type? After all, their underlying +// types are identical. We consider them to be different because each type +// instantiation creates a new named type, in this case T and T +// even if their underlying types are identical. This is sensible because +// we might still have methods that have different signatures or behave +// differently depending on the type arguments, and thus we can't possibly +// consider such types identical. Consequently: +func _() { + xint = xbool // ERROR assignment +} + +// Generic types cannot be used without instantiation. +var _ T // ERROR cannot use generic type T +var _ = T /* ERROR cannot use generic type T */ (0) + +// In type context, generic (parameterized) types cannot be parenthesized before +// being instantiated. See also NOTES entry from 12/4/2019. +var _ (T /* ERROR cannot use generic type T */ )[ /* ERROR unexpected \[|expected ';' */ int] + +// All types may be parameterized, including interfaces. +type I1[T any] interface{ + m1(T) +} + +// There is no such thing as a variadic generic type. +type _[T ... /* ERROR invalid use of ... */ any] struct{} + +// Generic interfaces may be embedded as one would expect. +type I2 interface { + I1(int) // method! + I1[string] // embedded I1 +} + +func _() { + var x I2 + x.I1(0) + x.m1("foo") +} + +type I0 interface { + m0() +} + +type I3 interface { + I0 + I1[bool] + m(string) +} + +func _() { + var x I3 + x.m0() + x.m1(true) + x.m("foo") +} + +type _ struct { + ( /* ERROR cannot parenthesize */ int8) + ( /* ERROR cannot parenthesize */ *int16) + *( /* ERROR cannot parenthesize */ int32) + List[int] + + int8 /* ERROR int8 redeclared */ + * /* ERROR int16 redeclared */ int16 + List /* ERROR List redeclared */ [int] +} + +// Issue #45639: We don't allow this anymore. Keep this code +// in case we decide to revisit this decision. +// +// It's possible to declare local types whose underlying types +// are type parameters. As with ordinary type definitions, the +// types underlying properties are "inherited" but the methods +// are not. +// func _[T interface{ m(); ~int }]() { +// type L T +// var x L +// +// // m is not defined on L (it is not "inherited" from +// // its underlying type). +// x.m /* ERROR x.m undefined */ () +// +// // But the properties of T, such that as that it supports +// // the operations of the types given by its type bound, +// // are also the properties of L. +// x++ +// _ = x - x +// +// // On the other hand, if we define a local alias for T, +// // that alias stands for T as expected. +// type A = T +// var y A +// y.m() +// _ = y < 0 +// } + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // It is not permitted to declare a local type whose underlying +// // type is a type parameter not declared by that type declaration. +// func _[T any]() { +// type _ T // ERROR cannot use function type parameter T as RHS in type declaration +// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration +// } + +// As a special case, an explicit type argument may be omitted +// from a type parameter bound if the type bound expects exactly +// one type argument. In that case, the type argument is the +// respective type parameter to which the type bound applies. +// Note: We may not permit this syntactic sugar at first. +// Note: This is now disabled. All examples below are adjusted. +type Adder[T any] interface { + Add(T) T +} + +// We don't need to explicitly instantiate the Adder bound +// if we have exactly one type parameter. +func Sum[T Adder[T]](list []T) T { + var sum T + for _, x := range list { + sum = sum.Add(x) + } + return sum +} + +// Valid and invalid variations. +type B0 any +type B1[_ any] any +type B2[_, _ any] any + +func _[T1 B0]() {} +func _[T1 B1[T1]]() {} +func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {} + +func _[T1, T2 B0]() {} +func _[T1 B1[T1], T2 B1[T2]]() {} +func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {} + +func _[T1 B0, T2 B1[T2]]() {} // here B1 applies to T2 + +// When the type argument is left away, the type bound is +// instantiated for each type parameter with that type +// parameter. +// Note: We may not permit this syntactic sugar at first. +func _[A Adder[A], B Adder[B], C Adder[A]]() { + var a A // A's type bound is Adder[A] + a = a.Add(a) + var b B // B's type bound is Adder[B] + b = b.Add(b) + var c C // C's type bound is Adder[A] + a = c.Add(a) +} + +// The type of variables (incl. parameters and return values) cannot +// be an interface with type constraints or be/embed comparable. +type I interface { + ~int +} + +var ( + _ interface /* ERROR contains type constraints */ {~int} + _ I /* ERROR contains type constraints */ +) + +func _(I /* ERROR contains type constraints */ ) +func _(x, y, z I /* ERROR contains type constraints */ ) +func _() I /* ERROR contains type constraints */ + +func _() { + var _ I /* ERROR contains type constraints */ +} + +type C interface { + comparable +} + +var _ comparable /* ERROR comparable */ +var _ C /* ERROR comparable */ + +func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ ) + +func _() { + var _ comparable /* ERROR comparable */ + var _ C /* ERROR comparable */ +} + +// Type parameters are never const types, i.e., it's +// not possible to declare a constant of type parameter type. +// (If a type set contains just a single const type, we could +// allow it, but such type sets don't make much sense in the +// first place.) +func _[T interface{~int|~float64}]() { + // not valid + const _ = T /* ERROR not constant */ (0) + const _ T /* ERROR invalid constant type T */ = 1 + + // valid + var _ = T(0) + var _ T = 1 + _ = T(0) +} + +// It is possible to create composite literals of type parameter +// type as long as it's possible to create a composite literal +// of the core type of the type parameter's constraint. +func _[P interface{ ~[]int }]() P { + return P{} + return P{1, 2, 3} +} + +func _[P interface{ ~[]E }, E interface{ map[string]P } ]() P { + x := P{} + return P{{}} + return P{E{}} + return P{E{"foo": x}} + return P{{"foo": x}, {}} +} + +// This is a degenerate case with a singleton type set, but we can create +// composite literals even if the core type is a defined type. +type MyInts []int + +func _[P MyInts]() P { + return P{} +} diff --git a/src/internal/types/testdata/examples/typesets.go b/src/internal/types/testdata/examples/typesets.go new file mode 100644 index 00000000000000..a50beb9745328b --- /dev/null +++ b/src/internal/types/testdata/examples/typesets.go @@ -0,0 +1,59 @@ +// Copyright 2021 The Go 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 shows some examples of constraint literals with elided interfaces. +// These examples are permitted if proposal issue #48424 is accepted. + +package p + +// Constraint type sets of the form T, ~T, or A|B may omit the interface. +type ( + _[T int] struct{} + _[T ~int] struct{} + _[T int | string] struct{} + _[T ~int | ~string] struct{} +) + +func min[T int | string](x, y T) T { + if x < y { + return x + } + return y +} + +func lookup[M ~map[K]V, K comparable, V any](m M, k K) V { + return m[k] +} + +func deref[P ~*E, E any](p P) E { + return *p +} + +func _() int { + p := new(int) + return deref(p) +} + +func addrOfCopy[V any, P *V](v V) P { + return &v +} + +func _() *int { + return addrOfCopy(0) +} + +// A type parameter may not be embedded in an interface; +// so it can also not be used as a constraint. +func _[A any, B A /* ERROR cannot use a type parameter as constraint */]() {} +func _[A any, B, C A /* ERROR cannot use a type parameter as constraint */]() {} + +// Error messages refer to the type constraint as it appears in the source. +// (No implicit interface should be exposed.) +func _[T string](x T) T { + return x /* ERROR constrained by string */ * x +} + +func _[T int | string](x T) T { + return x /* ERROR constrained by int|string */ * x +} diff --git a/src/go/types/testdata/fixedbugs/issue20583.src b/src/internal/types/testdata/fixedbugs/issue20583.go similarity index 100% rename from src/go/types/testdata/fixedbugs/issue20583.src rename to src/internal/types/testdata/fixedbugs/issue20583.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue23203a.src b/src/internal/types/testdata/fixedbugs/issue23203a.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue23203a.src rename to src/internal/types/testdata/fixedbugs/issue23203a.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue23203b.src b/src/internal/types/testdata/fixedbugs/issue23203b.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue23203b.src rename to src/internal/types/testdata/fixedbugs/issue23203b.go diff --git a/src/internal/types/testdata/fixedbugs/issue25838.go b/src/internal/types/testdata/fixedbugs/issue25838.go new file mode 100644 index 00000000000000..adbd138f165918 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue25838.go @@ -0,0 +1,26 @@ +// Copyright 2022 The Go Authors. 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 + +// examples from the issue + +type ( + e = f + f = g + g = []h + h i + i = j + j = e +) + +type ( + e1 = []h1 + h1 e1 +) + +type ( + P = *T + T P +) diff --git a/src/go/types/testdata/fixedbugs/issue26390.src b/src/internal/types/testdata/fixedbugs/issue26390.go similarity index 100% rename from src/go/types/testdata/fixedbugs/issue26390.src rename to src/internal/types/testdata/fixedbugs/issue26390.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue28251.src b/src/internal/types/testdata/fixedbugs/issue28251.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue28251.src rename to src/internal/types/testdata/fixedbugs/issue28251.go diff --git a/src/internal/types/testdata/fixedbugs/issue39634.go b/src/internal/types/testdata/fixedbugs/issue39634.go new file mode 100644 index 00000000000000..9df72f990ee9c2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39634.go @@ -0,0 +1,90 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Examples adjusted to match new [T any] syntax for type parameters. +// Also, previously permitted empty type parameter lists and instantiations +// are now syntax errors. + +package p + +// crash 1 +type nt1[_ any]interface{g /* ERROR undeclared name */ } +type ph1[e nt1[e],g(d /* ERROR undeclared name */ )]s /* ERROR undeclared name */ +func(*ph1[e,e /* ERROR redeclared */ ])h(d /* ERROR undeclared name */ ) + +// crash 2 +// Disabled: empty []'s are now syntax errors. This example leads to too many follow-on errors. +// type Numeric2 interface{t2 /* ERROR not a type */ } +// func t2[T Numeric2](s[]T){0 /* ERROR not a type */ []{s /* ERROR cannot index */ [0][0]}} + +// crash 3 +type t3 *interface{ t3.p /* ERROR no field or method p */ } + +// crash 4 +type Numeric4 interface{t4 /* ERROR not a type */ } +func t4[T Numeric4](s[]T){if( /* ERROR non-boolean */ 0){*s /* ERROR cannot indirect */ [0]}} + +// crash 7 +type foo7 interface { bar() } +type x7[A any] struct{ foo7 } +func main7() { var _ foo7 = x7[int]{} } + +// crash 8 +type foo8[A any] interface { ~A /* ERROR cannot be a type parameter */ } +func bar8[A foo8[A]](a A) {} + +// crash 9 +type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] } +func _() { var _ = new(foo9[int]) } + +// crash 12 +var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len /* ERROR must be called */ /* ERROR must be called */ )]c /* ERROR undeclared */ /* ERROR undeclared */ + +// crash 15 +func y15() { var a /* ERROR declared but not used */ interface{ p() } = G15[string]{} } +type G15[X any] s /* ERROR undeclared name */ +func (G15 /* ERROR generic type .* without instantiation */ ) p() + +// crash 16 +type Foo16[T any] r16 /* ERROR not a type */ +func r16[T any]() Foo16[Foo16[T]] { panic(0) } + +// crash 17 +type Y17 interface{ c() } +type Z17 interface { + c() Y17 + Y17 /* ERROR duplicate method */ +} +func F17[T Z17](T) {} + +// crash 18 +type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ]) + +// crash 19 +type Z19 [][[]Z19{}[0][0]]c19 /* ERROR undeclared */ + +// crash 20 +type Z20 /* ERROR illegal cycle */ interface{ Z20 } +func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) } + +// crash 21 +type Z21 /* ERROR illegal cycle */ interface{ Z21 } +func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) } + +// crash 24 +type T24[P any] P // ERROR cannot use a type parameter as RHS in type declaration +func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() } + +// crash 25 +type T25[A any] int +func (t T25[A]) m1() {} +var x T25 /* ERROR without instantiation */ .m1 + +// crash 26 +type T26 = interface{ F26[ /* ERROR interface method must have no type parameters */ Z any]() } +func F26[Z any]() T26 { return F26[] /* ERROR operand */ } + +// crash 27 +func e27[T any]() interface{ x27 /* ERROR not a type */ } { panic(0) } +func x27() { e27 /* ERROR cannot infer T */ () } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39664.go2 b/src/internal/types/testdata/fixedbugs/issue39664.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue39664.go2 rename to src/internal/types/testdata/fixedbugs/issue39664.go diff --git a/src/internal/types/testdata/fixedbugs/issue39680.go b/src/internal/types/testdata/fixedbugs/issue39680.go new file mode 100644 index 00000000000000..e56bc3547582d1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39680.go @@ -0,0 +1,31 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Embedding stand-alone type parameters is not permitted for now. Disabled. + +/* +import "fmt" + +// Minimal test case. +func _[T interface{~T}](x T) T{ + return x +} + +// Test case from issue. +type constr[T any] interface { + ~T +} + +func Print[T constr[T]](s []T) { + for _, v := range s { + fmt.Print(v) + } +} + +func f() { + Print([]string{"Hello, ", "playground\n"}) +} +*/ diff --git a/src/internal/types/testdata/fixedbugs/issue39693.go b/src/internal/types/testdata/fixedbugs/issue39693.go new file mode 100644 index 00000000000000..496754d972b0e1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39693.go @@ -0,0 +1,23 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Number1 interface { + // embedding non-interface types is permitted + int + float64 +} + +func Add1[T Number1](a, b T) T { + return a /* ERROR not defined */ + b +} + +type Number2 interface { + int | float64 +} + +func Add2[T Number2](a, b T) T { + return a + b +} diff --git a/src/internal/types/testdata/fixedbugs/issue39699.go b/src/internal/types/testdata/fixedbugs/issue39699.go new file mode 100644 index 00000000000000..72f83997c2460a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39699.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T0 interface{ +} + +type T1 interface{ + ~int +} + +type T2 interface{ + comparable +} + +type T3 interface { + T0 + T1 + T2 +} + +func _() { + _ = T0(0) + _ = T1 /* ERROR cannot use interface T1 in conversion */ (1) + _ = T2 /* ERROR cannot use interface T2 in conversion */ (2) + _ = T3 /* ERROR cannot use interface T3 in conversion */ (3) +} diff --git a/src/internal/types/testdata/fixedbugs/issue39711.go b/src/internal/types/testdata/fixedbugs/issue39711.go new file mode 100644 index 00000000000000..d85fa03fc433bc --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39711.go @@ -0,0 +1,13 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Do not report a duplicate type error for this term list. +// (Check types after interfaces have been completed.) +type _ interface { + // TODO(rfindley) Once we have full type sets we can enable this again. + // Fow now we don't permit interfaces in term lists. + // type interface{ Error() string }, interface{ String() string } +} diff --git a/src/internal/types/testdata/fixedbugs/issue39723.go b/src/internal/types/testdata/fixedbugs/issue39723.go new file mode 100644 index 00000000000000..00885238e69c9a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39723.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// A constraint must be an interface; it cannot +// be a type parameter, for instance. +func _[A interface{ ~int }, B A /* ERROR cannot use a type parameter as constraint */ ]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue39725.go b/src/internal/types/testdata/fixedbugs/issue39725.go new file mode 100644 index 00000000000000..62dc45a59601ee --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39725.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) {} +func _() { + f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{}) +} + +// simplified test case from issue +func f2[T any](_ []T, _ func(T)) {} +func _() { + f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {}) +} diff --git a/src/internal/types/testdata/fixedbugs/issue39754.go b/src/internal/types/testdata/fixedbugs/issue39754.go new file mode 100644 index 00000000000000..9edd239d7d97c2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39754.go @@ -0,0 +1,21 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Optional[T any] struct {} + +func (_ Optional[T]) Val() (T, bool) + +type Box[T any] interface { + Val() (T, bool) +} + +func f[V interface{}, A, B Box[V]]() {} + +func _() { + f[int, Optional[int], Optional[int]]() + _ = f[int, Optional[int], Optional /* ERROR does not implement Box */ [string]] + _ = f[int, Optional[int], Optional /* ERROR Optional.* does not implement Box.* */ [string]] +} diff --git a/src/internal/types/testdata/fixedbugs/issue39755.go b/src/internal/types/testdata/fixedbugs/issue39755.go new file mode 100644 index 00000000000000..257b73a2fbeff1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39755.go @@ -0,0 +1,23 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _[T interface{~map[string]int}](x T) { + _ = x == nil +} + +// simplified test case from issue + +type PathParamsConstraint interface { + ~map[string]string | ~[]struct{key, value string} +} + +type PathParams[T PathParamsConstraint] struct { + t T +} + +func (pp *PathParams[T]) IsNil() bool { + return pp.t == nil // this must succeed +} diff --git a/src/internal/types/testdata/fixedbugs/issue39768.go b/src/internal/types/testdata/fixedbugs/issue39768.go new file mode 100644 index 00000000000000..696d9d9beef9d2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39768.go @@ -0,0 +1,21 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type T[P any] P +// type A = T // ERROR cannot use generic type +// var x A[int] +// var _ A +// +// type B = T[int] +// var y B = x +// var _ B /* ERROR not a generic type */ [int] + +// test case from issue + +type Vector[T any] []T +type VectorAlias = Vector // ERROR cannot use generic type +var v Vector[int] diff --git a/src/internal/types/testdata/fixedbugs/issue39938.go b/src/internal/types/testdata/fixedbugs/issue39938.go new file mode 100644 index 00000000000000..6bc92848495646 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39938.go @@ -0,0 +1,54 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// All but E2 and E5 provide an "indirection" and break infinite expansion of a type. +type E0[P any] []P +type E1[P any] *P +type E2[P any] struct{ _ P } +type E3[P any] struct{ _ *P } +type E5[P any] struct{ _ [10]P } + +type T0 struct { + _ E0[T0] +} + +type T0_ struct { + E0[T0_] +} + +type T1 struct { + _ E1[T1] +} + +type T2 /* ERROR illegal cycle */ struct { + _ E2[T2] +} + +type T3 struct { + _ E3[T3] +} + +type T4 /* ERROR illegal cycle */ [10]E5[T4] + +type T5 struct { + _ E0[E2[T5]] +} + +type T6 struct { + _ E0[E2[E0[E1[E2[[10]T6]]]]] +} + +type T7 struct { + _ E0[[10]E2[E0[E2[E2[T7]]]]] +} + +type T8 struct { + _ E0[[]E2[E0[E2[E2[T8]]]]] +} + +type T9 /* ERROR illegal cycle */ [10]E2[E5[E2[T9]]] + +type T10 [10]E2[E5[E2[func(T10)]]] diff --git a/src/internal/types/testdata/fixedbugs/issue39948.go b/src/internal/types/testdata/fixedbugs/issue39948.go new file mode 100644 index 00000000000000..c893cc049e44d6 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39948.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[P any] interface{ + P // ERROR term cannot be a type parameter +} diff --git a/src/internal/types/testdata/fixedbugs/issue39976.go b/src/internal/types/testdata/fixedbugs/issue39976.go new file mode 100644 index 00000000000000..d703da90a2176a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue39976.go @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type policy[K, V any] interface{} +type LRU[K, V any] struct{} + +func NewCache[K, V any](p policy[K, V]) {} + +func _() { + var lru LRU[int, string] + NewCache[int, string](&lru) + NewCache(& /* ERROR does not match policy\[K, V\] \(cannot infer K and V\) */ lru) +} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39982.go2 b/src/internal/types/testdata/fixedbugs/issue39982.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue39982.go2 rename to src/internal/types/testdata/fixedbugs/issue39982.go diff --git a/src/internal/types/testdata/fixedbugs/issue40038.go b/src/internal/types/testdata/fixedbugs/issue40038.go new file mode 100644 index 00000000000000..5f81fcbfaa7236 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40038.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type A[T any] int + +func (A[T]) m(A[T]) + +func f[P interface{m(P)}]() {} + +func _() { + _ = f[A[int]] +} diff --git a/src/internal/types/testdata/fixedbugs/issue40056.go b/src/internal/types/testdata/fixedbugs/issue40056.go new file mode 100644 index 00000000000000..66130c0a558eba --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40056.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _() { + NewS /* ERROR cannot infer T */ ().M() +} + +type S struct {} + +func NewS[T any]() *S { panic(0) } + +func (_ *S /* ERROR S is not a generic type */ [T]) M() diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40057.go2 b/src/internal/types/testdata/fixedbugs/issue40057.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue40057.go2 rename to src/internal/types/testdata/fixedbugs/issue40057.go diff --git a/src/internal/types/testdata/fixedbugs/issue40301.go b/src/internal/types/testdata/fixedbugs/issue40301.go new file mode 100644 index 00000000000000..c78f9a1fa04089 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40301.go @@ -0,0 +1,12 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +func _[T any](x T) { + _ = unsafe.Alignof(x) + _ = unsafe.Sizeof(x) +} diff --git a/src/internal/types/testdata/fixedbugs/issue40350.go b/src/internal/types/testdata/fixedbugs/issue40350.go new file mode 100644 index 00000000000000..7ffd551c2e178f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40350.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type number interface { + ~float64 | ~int | ~int32 + float64 | ~int32 +} + +func f[T number]() {} + +func _() { + _ = f[int /* ERROR int does not implement number \(int missing in float64 | ~int32\)*/] +} diff --git a/src/internal/types/testdata/fixedbugs/issue40684.go b/src/internal/types/testdata/fixedbugs/issue40684.go new file mode 100644 index 00000000000000..63a058d039d934 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40684.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[_ any] int + +func f[_ any]() {} +func g[_, _ any]() {} + +func _() { + _ = f[T /* ERROR without instantiation */ ] + _ = g[T /* ERROR without instantiation */ , T /* ERROR without instantiation */ ] +} diff --git a/src/internal/types/testdata/fixedbugs/issue40789.go b/src/internal/types/testdata/fixedbugs/issue40789.go new file mode 100644 index 00000000000000..9eea4ad60a6647 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue40789.go @@ -0,0 +1,37 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func main() { + m := map[string]int{ + "a": 6, + "b": 7, + } + fmt.Println(copyMap[map[string]int, string, int](m)) +} + +type Map[K comparable, V any] interface { + map[K] V +} + +func copyMap[M Map[K, V], K comparable, V any](m M) M { + m1 := make(M) + for k, v := range m { + m1[k] = v + } + return m1 +} + +// simpler test case from the same issue + +type A[X comparable] interface { + []X +} + +func f[B A[X], X comparable]() B { + return nil +} diff --git a/src/internal/types/testdata/fixedbugs/issue41124.go b/src/internal/types/testdata/fixedbugs/issue41124.go new file mode 100644 index 00000000000000..4550dd732c149c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue41124.go @@ -0,0 +1,91 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Test case from issue. + +type Nat /* ERROR cycle */ interface { + Zero|Succ +} + +type Zero struct{} +type Succ struct{ + Nat // Nat contains type constraints but is invalid, so no error +} + +// Struct tests. + +type I1 interface { + comparable +} + +type I2 interface { + ~int +} + +type I3 interface { + I1 + I2 +} + +type _ struct { + f I1 // ERROR interface is .* comparable +} + +type _ struct { + comparable // ERROR interface is .* comparable +} + +type _ struct{ + I1 // ERROR interface is .* comparable +} + +type _ struct{ + I2 // ERROR interface contains type constraints +} + +type _ struct{ + I3 // ERROR interface contains type constraints +} + +// General composite types. + +type ( + _ [10]I1 // ERROR interface is .* comparable + _ [10]I2 // ERROR interface contains type constraints + + _ []I1 // ERROR interface is .* comparable + _ []I2 // ERROR interface contains type constraints + + _ *I3 // ERROR interface contains type constraints + _ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints + _ chan I3 // ERROR interface contains type constraints + _ func(I1 /* ERROR interface is .* comparable */ ) + _ func() I2 // ERROR interface contains type constraints +) + +// Other cases. + +var _ = [...]I3 /* ERROR interface contains type constraints */ {} + +func _(x interface{}) { + _ = x.(I3 /* ERROR interface contains type constraints */ ) +} + +type T1[_ any] struct{} +type T3[_, _, _ any] struct{} +var _ T1[I2 /* ERROR interface contains type constraints */ ] +var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32] + +func f1[_ any]() int { panic(0) } +var _ = f1[I2 /* ERROR interface contains type constraints */ ]() +func f3[_, _, _ any]() int { panic(0) } +var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]() + +func _(x interface{}) { + switch x.(type) { + case I2 /* ERROR interface contains type constraints */ : + } +} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42695.src b/src/internal/types/testdata/fixedbugs/issue42695.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue42695.src rename to src/internal/types/testdata/fixedbugs/issue42695.go diff --git a/src/internal/types/testdata/fixedbugs/issue42758.go b/src/internal/types/testdata/fixedbugs/issue42758.go new file mode 100644 index 00000000000000..6d75b106d4873d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue42758.go @@ -0,0 +1,33 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _[T any](x interface{}){ + switch x.(type) { + case T: // ok to use a type parameter + case int: + } + + switch x.(type) { + case T: + case T /* ERROR duplicate case */ : + } +} + +type constraint interface { + ~int +} + +func _[T constraint](x interface{}){ + switch x.(type) { + case T: // ok to use a type parameter even if type set contains int + case int: + } +} + +func _(x constraint /* ERROR contains type constraints */ ) { + switch x.(type) { // no need to report another error + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue42881.go b/src/internal/types/testdata/fixedbugs/issue42881.go new file mode 100644 index 00000000000000..7122d1c787ae49 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue42881.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + T1 interface{ comparable } + T2 interface{ int } +) + +var ( + _ comparable // ERROR cannot use type comparable outside a type constraint: interface is \(or embeds\) comparable + _ T1 // ERROR cannot use type T1 outside a type constraint: interface is \(or embeds\) comparable + _ T2 // ERROR cannot use type T2 outside a type constraint: interface contains type constraints +) diff --git a/src/internal/types/testdata/fixedbugs/issue42987.go b/src/internal/types/testdata/fixedbugs/issue42987.go new file mode 100644 index 00000000000000..f58c63f8a3621b --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue42987.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check that there is only one error (no follow-on errors). + +package p +var _ = [ ... /* ERROR invalid use of \[...\] array */ ]byte("foo") \ No newline at end of file diff --git a/src/internal/types/testdata/fixedbugs/issue43056.go b/src/internal/types/testdata/fixedbugs/issue43056.go new file mode 100644 index 00000000000000..8ff4e7f9b4d8f7 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43056.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Go Authors. 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 + +// simplified example +func f[T ~func(T)](a, b T) {} + +type F func(F) + +func _() { + var i F + var j func(F) + + f(i, j) + f(j, i) +} + +// example from issue +func g[T interface{ Equal(T) bool }](a, b T) {} + +type I interface{ Equal(I) bool } + +func _() { + var i I + var j interface{ Equal(I) bool } + + g(i, j) + g(j, i) +} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43087.src b/src/internal/types/testdata/fixedbugs/issue43087.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue43087.src rename to src/internal/types/testdata/fixedbugs/issue43087.go diff --git a/src/internal/types/testdata/fixedbugs/issue43109.go b/src/internal/types/testdata/fixedbugs/issue43109.go new file mode 100644 index 00000000000000..a4533c9bf7c36d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43109.go @@ -0,0 +1,10 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Ensure there is no "imported but not used" error +// if a package wasn't imported in the first place. + +package p + +import . "/foo" // ERROR could not import \/foo diff --git a/src/internal/types/testdata/fixedbugs/issue43110.go b/src/internal/types/testdata/fixedbugs/issue43110.go new file mode 100644 index 00000000000000..8d5c983fd507aa --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43110.go @@ -0,0 +1,43 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type P *struct{} + +func _() { + // want an error even if the switch is empty + var a struct{ _ func() } + switch a /* ERROR cannot switch on a */ { + } + + switch a /* ERROR cannot switch on a */ { + case a: // no follow-on error here + } + + // this is ok because f can be compared to nil + var f func() + switch f { + } + + switch f { + case nil: + } + + switch (func())(nil) { + case nil: + } + + switch (func())(nil) { + case f /* ERROR invalid case f in switch on .* \(func can only be compared to nil\) */ : + } + + switch nil /* ERROR use of untyped nil in switch expression */ { + } + + // this is ok + switch P(nil) { + case P(nil): + } +} diff --git a/src/go/types/testdata/fixedbugs/issue43124.src b/src/internal/types/testdata/fixedbugs/issue43124.go similarity index 100% rename from src/go/types/testdata/fixedbugs/issue43124.src rename to src/internal/types/testdata/fixedbugs/issue43124.go diff --git a/src/go/types/testdata/fixedbugs/issue43125.src b/src/internal/types/testdata/fixedbugs/issue43125.go similarity index 100% rename from src/go/types/testdata/fixedbugs/issue43125.src rename to src/internal/types/testdata/fixedbugs/issue43125.go diff --git a/src/internal/types/testdata/fixedbugs/issue43190.go b/src/internal/types/testdata/fixedbugs/issue43190.go new file mode 100644 index 00000000000000..d1b46b59f24058 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43190.go @@ -0,0 +1,31 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The errors below are produced by the parser, but we check +// them here for consistency with the types2 tests. + +package p + +import ; // ERROR missing import path +import "" // ERROR invalid import path \(empty string\) +import +var /* ERROR missing import path */ _ int +import .; // ERROR missing import path +import 'x' // ERROR import path must be a string +var _ int +import /* ERROR imports must appear before other declarations */ _ "math" + +// Don't repeat previous error for each immediately following import ... +import () +import (.) // ERROR missing import path +import ( + "fmt" + . +) // ERROR missing import path + +// ... but remind with error again if we start a new import section after +// other declarations +var _ = fmt.Println +import /* ERROR imports must appear before other declarations */ _ "math" +import _ "math" diff --git a/src/internal/types/testdata/fixedbugs/issue43527.go b/src/internal/types/testdata/fixedbugs/issue43527.go new file mode 100644 index 00000000000000..2955c261f954ee --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43527.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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 + +const L = 10 + +type ( + _ [L]struct{} + _ [A /* ERROR undeclared name A for array length */ ]struct{} + _ [B /* ERROR invalid array length B */ ]struct{} + _[A any] struct{} + + B int +) diff --git a/src/internal/types/testdata/fixedbugs/issue43671.go b/src/internal/types/testdata/fixedbugs/issue43671.go new file mode 100644 index 00000000000000..3c78f85aa4e794 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue43671.go @@ -0,0 +1,58 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type C0 interface{ int } +type C1 interface{ chan int } +type C2 interface{ chan int | <-chan int } +type C3 interface{ chan int | chan float32 } +type C4 interface{ chan int | chan<- int } +type C5[T any] interface{ ~chan T | <-chan T } + +func _[T any](ch T) { + <-ch // ERROR cannot receive from ch .* no core type +} + +func _[T C0](ch T) { + <-ch // ERROR cannot receive from non-channel ch +} + +func _[T C1](ch T) { + <-ch +} + +func _[T C2](ch T) { + <-ch +} + +func _[T C3](ch T) { + <-ch // ERROR cannot receive from ch .* no core type +} + +func _[T C4](ch T) { + <-ch // ERROR cannot receive from send-only channel +} + +func _[T C5[X], X any](ch T, x X) { + x = <-ch +} + +// test case from issue, slightly modified +type RecvChan[T any] interface { + ~chan T | ~<-chan T +} + +func _[T any, C RecvChan[T]](ch C) T { + return <-ch +} + +func f[T any, C interface{ chan T }](ch C) T { + return <-ch +} + +func _(ch chan int) { + var x int = f(ch) // test constraint type inference for this case + _ = x +} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue44688.go2 b/src/internal/types/testdata/fixedbugs/issue44688.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue44688.go2 rename to src/internal/types/testdata/fixedbugs/issue44688.go diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue44799.go2 b/src/internal/types/testdata/fixedbugs/issue44799.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue44799.go2 rename to src/internal/types/testdata/fixedbugs/issue44799.go diff --git a/src/internal/types/testdata/fixedbugs/issue45114.go b/src/internal/types/testdata/fixedbugs/issue45114.go new file mode 100644 index 00000000000000..009366010e1e67 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45114.go @@ -0,0 +1,8 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var s uint +var _ = string(1 /* ERROR shifted operand 1 .* must be integer */ << s) diff --git a/src/internal/types/testdata/fixedbugs/issue45548.go b/src/internal/types/testdata/fixedbugs/issue45548.go new file mode 100644 index 00000000000000..01c9672745a600 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45548.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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[F interface{*Q}, G interface{*R}, Q, R any](q Q, r R) {} + +func _() { + f[*float64, *int](1, 2) + f[*float64](1, 2) + f(1, 2) +} diff --git a/src/internal/types/testdata/fixedbugs/issue45550.go b/src/internal/types/testdata/fixedbugs/issue45550.go new file mode 100644 index 00000000000000..3eeaca0957a9bd --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45550.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Builder /* ERROR illegal cycle */ [T interface{ struct{ Builder[T] } }] struct{} +type myBuilder struct { + Builder[myBuilder] +} diff --git a/src/internal/types/testdata/fixedbugs/issue45635.go b/src/internal/types/testdata/fixedbugs/issue45635.go new file mode 100644 index 00000000000000..fc50797b17ca76 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45635.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + some /* ERROR "undeclared name" */ [int, int]() +} + +type N[T any] struct{} + +var _ N [] // ERROR expected type argument list + +type I interface { + ~[]int +} + +func _[T I](i, j int) { + var m map[int]int + _ = m[i, j /* ERROR "more than one index" */ ] + + var a [3]int + _ = a[i, j /* ERROR "more than one index" */ ] + + var s []int + _ = s[i, j /* ERROR "more than one index" */ ] + + var t T + _ = t[i, j /* ERROR "more than one index" */ ] +} diff --git a/src/internal/types/testdata/fixedbugs/issue45639.go b/src/internal/types/testdata/fixedbugs/issue45639.go new file mode 100644 index 00000000000000..80148fe4819862 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45639.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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 + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // It is not permitted to declare a local type whose underlying +// // type is a type parameters not declared by that type declaration. +// func _[T any]() { +// type _ T // ERROR cannot use function type parameter T as RHS in type declaration +// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration +// } diff --git a/src/internal/types/testdata/fixedbugs/issue45920.go b/src/internal/types/testdata/fixedbugs/issue45920.go new file mode 100644 index 00000000000000..d67dfc0f9d1786 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45920.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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 f1[T any, C chan T | <-chan T](ch C) {} + +func _(ch chan int) { f1(ch) } +func _(ch <-chan int) { f1(ch) } +func _(ch chan<- int) { f1 /* ERROR chan<- int does not implement chan int \| <-chan int */ (ch) } + +func f2[T any, C chan T | chan<- T](ch C) {} + +func _(ch chan int) { f2(ch) } +func _(ch <-chan int) { f2 /* ERROR <-chan int does not implement chan int \| chan<- int */ (ch) } +func _(ch chan<- int) { f2(ch) } diff --git a/src/internal/types/testdata/fixedbugs/issue45985.go b/src/internal/types/testdata/fixedbugs/issue45985.go new file mode 100644 index 00000000000000..9a0f5e36970058 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue45985.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue45985 + +func app[S interface{ ~[]T }, T any](s S, e T) S { + return append(s, e) +} + +func _() { + _ = app/* ERROR "S does not match" */[int] +} diff --git a/src/internal/types/testdata/fixedbugs/issue46090.go b/src/internal/types/testdata/fixedbugs/issue46090.go new file mode 100644 index 00000000000000..07f0101acb1ff9 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46090.go @@ -0,0 +1,11 @@ +// -lang=go1.17 + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The predeclared type comparable is not visible before Go 1.18. + +package p + +type _ comparable // ERROR predeclared comparable diff --git a/src/internal/types/testdata/fixedbugs/issue46275.go b/src/internal/types/testdata/fixedbugs/issue46275.go new file mode 100644 index 00000000000000..0862d5bb5a0ef6 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46275.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue46275 + +type N[T any] struct { + *N[T] + t T +} + +func (n *N[T]) Elem() T { + return n.t +} + +type I interface { + Elem() string +} + +func _() { + var n1 *N[string] + var _ I = n1 + type NS N[string] + var n2 *NS + var _ I = n2 +} diff --git a/src/go/types/testdata/fixedbugs/issue46403.src b/src/internal/types/testdata/fixedbugs/issue46403.go similarity index 100% rename from src/go/types/testdata/fixedbugs/issue46403.src rename to src/internal/types/testdata/fixedbugs/issue46403.go diff --git a/src/internal/types/testdata/fixedbugs/issue46404.go b/src/internal/types/testdata/fixedbugs/issue46404.go new file mode 100644 index 00000000000000..e3c93f66a85938 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46404.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue46404 + +// TODO(gri) re-enable this test with matching errors +// between go/types and types2 +// Check that we don't type check t[_] as an instantiation. +// type t [t /* type parameters must be named */ /* not a generic type */ [_]]_ // cannot use diff --git a/src/internal/types/testdata/fixedbugs/issue46461.go b/src/internal/types/testdata/fixedbugs/issue46461.go new file mode 100644 index 00000000000000..4432402a300e9a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46461.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. 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 + +// test case 1 +type T /* ERROR illegal cycle */ [U interface{ M() T[U] }] int + +type X int + +func (X) M() T[X] { return 0 } + +// test case 2 +type A /* ERROR illegal cycle */ [T interface{ A[T] }] interface{} + +// test case 3 +type A2 /* ERROR illegal cycle */ [U interface{ A2[U] }] interface{ M() A2[U] } + +type I interface{ A2[I]; M() A2[I] } diff --git a/src/internal/types/testdata/fixedbugs/issue46583.go b/src/internal/types/testdata/fixedbugs/issue46583.go new file mode 100644 index 00000000000000..da1f1ffbbaf6c2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue46583.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T1 struct{} +func (t T1) m(int) {} +var f1 func(T1) + +type T2 struct{} +func (t T2) m(x int) {} +var f2 func(T2) + +type T3 struct{} +func (T3) m(int) {} +var f3 func(T3) + +type T4 struct{} +func (T4) m(x int) {} +var f4 func(T4) + +func _() { + f1 = T1 /* ERROR func\(T1, int\) */ .m + f2 = T2 /* ERROR func\(t T2, x int\) */ .m + f3 = T3 /* ERROR func\(T3, int\) */ .m + f4 = T4 /* ERROR func\(_ T4, x int\) */ .m +} diff --git a/src/internal/types/testdata/fixedbugs/issue47031.go b/src/internal/types/testdata/fixedbugs/issue47031.go new file mode 100644 index 00000000000000..b184f9b5b7dfbf --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47031.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Mer interface { M() } + +func F[T Mer](p *T) { + p.M /* ERROR p\.M undefined */ () +} + +type MyMer int + +func (MyMer) M() {} + +func _() { + F(new(MyMer)) + F[Mer](nil) +} diff --git a/src/internal/types/testdata/fixedbugs/issue47115.go b/src/internal/types/testdata/fixedbugs/issue47115.go new file mode 100644 index 00000000000000..a0bfe38de8b050 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47115.go @@ -0,0 +1,40 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type C0 interface{ int } +type C1 interface{ chan int } +type C2 interface{ chan int | <-chan int } +type C3 interface{ chan int | chan float32 } +type C4 interface{ chan int | chan<- int } +type C5[T any] interface{ ~chan T | chan<- T } + +func _[T any](ch T) { + ch <- /* ERROR cannot send to ch .* no core type */ 0 +} + +func _[T C0](ch T) { + ch <- /* ERROR cannot send to non-channel */ 0 +} + +func _[T C1](ch T) { + ch <- 0 +} + +func _[T C2](ch T) { + ch <-/* ERROR cannot send to receive-only channel */ 0 +} + +func _[T C3](ch T) { + ch <- /* ERROR cannot send to ch .* no core type */ 0 +} + +func _[T C4](ch T) { + ch <- 0 +} + +func _[T C5[X], X any](ch T, x X) { + ch <- x +} diff --git a/src/internal/types/testdata/fixedbugs/issue47127.go b/src/internal/types/testdata/fixedbugs/issue47127.go new file mode 100644 index 00000000000000..bb4b487eb2c6f0 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47127.go @@ -0,0 +1,37 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Embedding of stand-alone type parameters is not permitted. + +package p + +type ( + _[P any] interface{ *P | []P | chan P | map[string]P } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } +) + +func _[P any]() { + type ( + _[P any] interface{ *P | []P | chan P | map[string]P } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } + + _ interface{ *P | []P | chan P | map[string]P } + _ interface{ P /* ERROR term cannot be a type parameter */ } + _ interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _ interface{ int | P /* ERROR term cannot be a type parameter */ } + _ interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } + ) +} + +func _[P any, Q interface{ *P | []P | chan P | map[string]P }]() {} +func _[P any, Q interface{ P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue47411.go b/src/internal/types/testdata/fixedbugs/issue47411.go new file mode 100644 index 00000000000000..12303072edf97d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47411.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. 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[_ comparable]() {} +func g[_ interface{interface{comparable; ~int|~string}}]() {} + +func _[P comparable, + Q interface{ comparable; ~int|~string }, + R any, // not comparable + S interface{ comparable; ~func() }, // not comparable +]() { + _ = f[int] + _ = f[P] + _ = f[Q] + _ = f[func /* ERROR does not implement comparable */ ()] + _ = f[R /* ERROR R does not implement comparable */ ] + + _ = g[int] + _ = g[P /* ERROR P does not implement interface{interface{comparable; ~int \| ~string} */ ] + _ = g[Q] + _ = g[func /* ERROR func\(\) does not implement interface{interface{comparable; ~int \| ~string}} */ ()] + _ = g[R /* ERROR R does not implement interface{interface{comparable; ~int \| ~string} */ ] +} diff --git a/src/internal/types/testdata/fixedbugs/issue47747.go b/src/internal/types/testdata/fixedbugs/issue47747.go new file mode 100644 index 00000000000000..6f09fc2f57bae1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47747.go @@ -0,0 +1,71 @@ +// Copyright 2021 The Go Authors. 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 + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type T1[P any] P +// +// func (T1[_]) m() {} +// +// func _[P any](x *T1[P]) { +// // x.m exists because x is of type *T1 where T1 is a defined type +// // (even though under(T1) is a type parameter) +// x.m() +// } + + +func _[P interface{ m() }](x P) { + x.m() + // (&x).m doesn't exist because &x is of type *P + // and pointers to type parameters don't have methods + (&x).m /* ERROR type \*P is pointer to type parameter, not type parameter */ () +} + + +type T2 interface{ m() } + +func _(x *T2) { + // x.m doesn't exists because x is of type *T2 + // and pointers to interfaces don't have methods + x.m /* ERROR type \*T2 is pointer to interface, not interface */() +} + +// Test case 1 from issue + +type Fooer1[t any] interface { + Foo(Barer[t]) +} +type Barer[t any] interface { + Bar(t) +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type Foo1[t any] t +// type Bar[t any] t +// +// func (l Foo1[t]) Foo(v Barer[t]) { v.Bar(t(l)) } +// func (b *Bar[t]) Bar(l t) { *b = Bar[t](l) } +// +// func _[t any](f Fooer1[t]) t { +// var b Bar[t] +// f.Foo(&b) +// return t(b) +// } + +// Test case 2 from issue + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type Fooer2[t any] interface { +// Foo() +// } +// +// type Foo2[t any] t +// +// func (f *Foo2[t]) Foo() {} +// +// func _[t any](v t) { +// var f = Foo2[t](v) +// _ = Fooer2[t](&f) +// } diff --git a/src/internal/types/testdata/fixedbugs/issue47796.go b/src/internal/types/testdata/fixedbugs/issue47796.go new file mode 100644 index 00000000000000..6667ba4fec0855 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47796.go @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. 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 + +// parameterized types with self-recursive constraints +type ( + T1 /* ERROR illegal cycle */ [P T1[P]] interface{} + T2 /* ERROR illegal cycle */ [P, Q T2[P, Q]] interface{} + T3[P T2[P, Q], Q interface{ ~string }] interface{} + + T4a /* ERROR illegal cycle */ [P T4a[P]] interface{ ~int } + T4b /* ERROR illegal cycle */ [P T4b[int]] interface{ ~int } + T4c /* ERROR illegal cycle */ [P T4c[string]] interface{ ~int } + + // mutually recursive constraints + T5 /* ERROR illegal cycle */ [P T6[P]] interface{ int } + T6[P T5[P]] interface{ int } +) + +// verify that constraints are checked as expected +var ( + _ T1[int] + _ T2[int, string] + _ T3[int, string] +) + +// test case from issue + +type Eq /* ERROR illegal cycle */ [a Eq[a]] interface { + Equal(that a) bool +} diff --git a/src/internal/types/testdata/fixedbugs/issue47818.go b/src/internal/types/testdata/fixedbugs/issue47818.go new file mode 100644 index 00000000000000..e9b0adbce98960 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47818.go @@ -0,0 +1,61 @@ +// -lang=go1.17 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parser accepts type parameters but the type checker +// needs to report any operations that are not permitted +// before Go 1.18. + +package p + +type T[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR predeclared any requires go1\.18 or later */] struct{} + +// for init (and main, but we're not in package main) we should only get one error +func init[P /* ERROR func init must have no type parameters */ any /* ERROR predeclared any requires go1\.18 or later */]() { +} +func main[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR predeclared any requires go1\.18 or later */]() { +} + +func f[P /* ERROR type parameter requires go1\.18 or later */ any /* ERROR predeclared any requires go1\.18 or later */](x P) { + var _ T[ /* ERROR type instantiation requires go1\.18 or later */ int] + var _ (T[ /* ERROR type instantiation requires go1\.18 or later */ int]) + _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int]{} + _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int](struct{}{}) +} + +func (T[ /* ERROR type instantiation requires go1\.18 or later */ P]) g(x int) { + f[ /* ERROR function instantiation requires go1\.18 or later */ int](0) // explicit instantiation + (f[ /* ERROR function instantiation requires go1\.18 or later */ int])(0) // parentheses (different code path) + f( /* ERROR implicit function instantiation requires go1\.18 or later */ x) // implicit instantiation +} + +type C1 interface { + comparable // ERROR predeclared comparable requires go1\.18 or later +} + +type C2 interface { + comparable // ERROR predeclared comparable requires go1\.18 or later + int // ERROR embedding non-interface type int requires go1\.18 or later + ~ /* ERROR embedding interface element ~int requires go1\.18 or later */ int + int /* ERROR embedding interface element int \| ~string requires go1\.18 or later */ | ~string +} + +type _ interface { + // errors for these were reported with their declaration + C1 + C2 +} + +type ( + _ comparable // ERROR predeclared comparable requires go1\.18 or later + // errors for these were reported with their declaration + _ C1 + _ C2 + + _ = comparable // ERROR predeclared comparable requires go1\.18 or later + // errors for these were reported with their declaration + _ = C1 + _ = C2 +) diff --git a/src/internal/types/testdata/fixedbugs/issue47887.go b/src/internal/types/testdata/fixedbugs/issue47887.go new file mode 100644 index 00000000000000..4c4fc2fda8f8a3 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47887.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Fooer[t any] interface { + foo(Barer[t]) +} +type Barer[t any] interface { + bar(Bazer[t]) +} +type Bazer[t any] interface { + Fooer[t] + baz(t) +} + +type Int int + +func (n Int) baz(int) {} +func (n Int) foo(b Barer[int]) { b.bar(n) } + +type F[t any] interface { f(G[t]) } +type G[t any] interface { g(H[t]) } +type H[t any] interface { F[t] } + +type T struct{} +func (n T) f(b G[T]) { b.g(n) } diff --git a/src/internal/types/testdata/fixedbugs/issue47968.go b/src/internal/types/testdata/fixedbugs/issue47968.go new file mode 100644 index 00000000000000..3dd303957ca816 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue47968.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[P any] struct{} + +func (T[P]) m1() + +type A1 = T // ERROR cannot use generic type + +func (A1[P]) m2() {} + +type A2 = T[int] + +func (A2 /* ERROR cannot define new methods on instantiated type T\[int\] */) m3() {} +func (_ /* ERROR cannot define new methods on instantiated type T\[int\] */ A2) m4() {} + +func (T[int]) m5() {} // int is the type parameter name, not an instantiation +func (T[* /* ERROR must be an identifier */ int]) m6() {} // syntax error diff --git a/src/internal/types/testdata/fixedbugs/issue48008.go b/src/internal/types/testdata/fixedbugs/issue48008.go new file mode 100644 index 00000000000000..6c14c78e4c9cfc --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48008.go @@ -0,0 +1,60 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[P any] struct{} + +func _(x interface{}) { + switch x.(type) { + case nil: + case int: + + case T[int]: + case []T[int]: + case [10]T[int]: + case struct{T[int]}: + case *T[int]: + case func(T[int]): + case interface{m(T[int])}: + case map[T[int]] string: + case chan T[int]: + + case T /* ERROR cannot use generic type T\[P any\] without instantiation */ : + case []T /* ERROR cannot use generic type */ : + case [10]T /* ERROR cannot use generic type */ : + case struct{T /* ERROR cannot use generic type */ }: + case *T /* ERROR cannot use generic type */ : + case func(T /* ERROR cannot use generic type */ ): + case interface{m(T /* ERROR cannot use generic type */ )}: + case map[T /* ERROR cannot use generic type */ ] string: + case chan T /* ERROR cannot use generic type */ : + + case T /* ERROR cannot use generic type */ , *T /* ERROR cannot use generic type */ : + } +} + +// Make sure a parenthesized nil is ok. + +func _(x interface{}) { + switch x.(type) { + case ((nil)), int: + } +} + +// Make sure we look for the predeclared nil. + +func _(x interface{}) { + type nil int + switch x.(type) { + case nil: // ok - this is the type nil + } +} + +func _(x interface{}) { + var nil int + switch x.(type) { + case nil /* ERROR not a type */ : // not ok - this is the variable nil + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue48018.go b/src/internal/types/testdata/fixedbugs/issue48018.go new file mode 100644 index 00000000000000..e6ccc6b9be7035 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48018.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. 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 Box[A any] struct { + value A +} + +func Nest[A /* ERROR instantiation cycle */ any](b Box[A], n int) interface{} { + if n == 0 { + return b + } + return Nest(Box[Box[A]]{b}, n-1) +} + +func main() { + Nest(Box[int]{0}, 10) +} diff --git a/src/internal/types/testdata/fixedbugs/issue48048.go b/src/internal/types/testdata/fixedbugs/issue48048.go new file mode 100644 index 00000000000000..f4013306215ec3 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48048.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[P any] struct{} + +func (T[_]) A() {} + +var _ = (T[int]).A +var _ = (*T[int]).A + +var _ = (T /* ERROR cannot use generic type */).A +var _ = (*T /* ERROR cannot use generic type */).A diff --git a/src/internal/types/testdata/fixedbugs/issue48082.go b/src/internal/types/testdata/fixedbugs/issue48082.go new file mode 100644 index 00000000000000..5395154978ebe1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48082.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue48082 + +import "init" /* ERROR init must be a func */ /* ERROR could not import init */ diff --git a/src/internal/types/testdata/fixedbugs/issue48083.go b/src/internal/types/testdata/fixedbugs/issue48083.go new file mode 100644 index 00000000000000..3dae51415df0a2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48083.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[P any] struct{} + +type _ interface{ int | T /* ERROR cannot use generic type */ } \ No newline at end of file diff --git a/src/internal/types/testdata/fixedbugs/issue48136.go b/src/internal/types/testdata/fixedbugs/issue48136.go new file mode 100644 index 00000000000000..b87f84ae642d68 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48136.go @@ -0,0 +1,36 @@ +// Copyright 2021 The Go Authors. 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 f1[P interface{ *P }]() {} +func f2[P interface{ func(P) }]() {} +func f3[P, Q interface{ func(Q) P }]() {} +func f4[P interface{ *Q }, Q interface{ func(P) }]() {} +func f5[P interface{ func(P) }]() {} +func f6[P interface { *Tree[P] }, Q any ]() {} + +func _() { + f1 /* ERROR cannot infer P */ () + f2 /* ERROR cannot infer P */ () + f3 /* ERROR cannot infer P */ () + f4 /* ERROR cannot infer P */ () + f5 /* ERROR cannot infer P */ () + f6 /* ERROR cannot infer P */ () +} + +type Tree[P any] struct { + left, right *Tree[P] + data P +} + +// test case from issue + +func foo[Src interface { func() Src }]() Src { + return foo[Src] +} + +func _() { + foo /* ERROR cannot infer Src */ () +} diff --git a/src/internal/types/testdata/fixedbugs/issue48234.go b/src/internal/types/testdata/fixedbugs/issue48234.go new file mode 100644 index 00000000000000..e069930c42d99c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48234.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var _ = interface{ + m() + m /* ERROR "duplicate method" */ () +}(nil) diff --git a/src/internal/types/testdata/fixedbugs/issue48312.go b/src/internal/types/testdata/fixedbugs/issue48312.go new file mode 100644 index 00000000000000..2fdb7cad947113 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48312.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T interface{ m() } +type P *T + +func _(p *T) { + p.m /* ERROR type \*T is pointer to interface, not interface */ () +} + +func _(p P) { + p.m /* ERROR type P is pointer to interface, not interface */ () +} + +func _[P T](p *P) { + p.m /* ERROR type \*P is pointer to type parameter, not type parameter */ () +} diff --git a/src/internal/types/testdata/fixedbugs/issue48472.go b/src/internal/types/testdata/fixedbugs/issue48472.go new file mode 100644 index 00000000000000..2d908f4c8b56c7 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48472.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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 g() { + var s string + var i int + _ = s /* ERROR invalid operation: s \+ i \(mismatched types string and int\) */ + i +} + +func f(i int) int { + i /* ERROR invalid operation: i \+= "1" \(mismatched types int and untyped string\) */ += "1" + return i +} diff --git a/src/internal/types/testdata/fixedbugs/issue48529.go b/src/internal/types/testdata/fixedbugs/issue48529.go new file mode 100644 index 00000000000000..a3653fa19c0961 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48529.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T /* ERROR illegal cycle */ [U interface{ M() T[U, int] }] int + +type X int + +func (X) M() T[X] { return 0 } diff --git a/src/internal/types/testdata/fixedbugs/issue48582.go b/src/internal/types/testdata/fixedbugs/issue48582.go new file mode 100644 index 00000000000000..c12091be7902f4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48582.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type N /* ERROR cycle */ interface { + int | N +} + +type A /* ERROR cycle */ interface { + int | B +} + +type B interface { + int | A +} + +type S /* ERROR cycle */ struct { + I // ERROR interface contains type constraints +} + +type I interface { + int | S +} + +type P interface { + *P // ERROR interface contains type constraints +} diff --git a/src/internal/types/testdata/fixedbugs/issue48619.go b/src/internal/types/testdata/fixedbugs/issue48619.go new file mode 100644 index 00000000000000..72eea1ef596204 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48619.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. 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[P any](a, _ P) { + var x int + // TODO(gri) these error messages, while correct, could be better + f(a, x /* ERROR type int of x does not match inferred type P for P */) + f(x, a /* ERROR type P of a does not match inferred type int for P */) +} + +func g[P any](a, b P) { + g(a, b) + g(&a, &b) + g([]P{}, []P{}) + + // work-around: provide type argument explicitly + g[*P](&a, &b) + g[[]P]([]P{}, []P{}) +} diff --git a/src/internal/types/testdata/fixedbugs/issue48656.go b/src/internal/types/testdata/fixedbugs/issue48656.go new file mode 100644 index 00000000000000..0f60f47120a96b --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48656.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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[P *Q, Q any](P, Q) { + _ = f[P] +} + +func f2[P /* ERROR instantiation cycle */ *Q, Q any](P, Q) { + _ = f2[*P] +} diff --git a/src/internal/types/testdata/fixedbugs/issue48695.go b/src/internal/types/testdata/fixedbugs/issue48695.go new file mode 100644 index 00000000000000..9f4a76851d5dad --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48695.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. 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 g[P ~func(T) P, T any](P) {} + +func _() { + type F func(int) F + var f F + g(f) + _ = g[F] +} diff --git a/src/internal/types/testdata/fixedbugs/issue48703.go b/src/internal/types/testdata/fixedbugs/issue48703.go new file mode 100644 index 00000000000000..8a32c1ecf22a3c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48703.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +// The actual example from the issue. +type List[P any] struct{} + +func (_ List[P]) m() (_ List[List[P]]) { return } + +// Other types of recursion through methods. +type R[P any] int + +func (*R[R /* ERROR must be an identifier */ [int]]) m0() {} +func (R[P]) m1(R[R[P]]) {} +func (R[P]) m2(R[*P]) {} +func (R[P]) m3([unsafe.Sizeof(new(R[P]))]int) {} +func (R[P]) m4([unsafe.Sizeof(new(R[R[P]]))]int) {} + +// Mutual recursion +type M[P any] int + +func (R[P]) m5(M[M[P]]) {} +func (M[P]) m(R[R[P]]) {} diff --git a/src/internal/types/testdata/fixedbugs/issue48712.go b/src/internal/types/testdata/fixedbugs/issue48712.go new file mode 100644 index 00000000000000..63ce7bc5103442 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48712.go @@ -0,0 +1,41 @@ +// Copyright 2022 The Go Authors. 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 _[P comparable](x, y P) { + _ = x == x + _ = x == y + _ = y == x + _ = y == y + + _ = x /* ERROR type parameter P is not comparable with < */ < y +} + +func _[P comparable](x P, y any) { + _ = x == x + _ = x == y + _ = y == x + _ = y == y + + _ = x /* ERROR type parameter P is not comparable with < */ < y +} + +func _[P any](x, y P) { + _ = x /* ERROR incomparable types in type set */ == x + _ = x /* ERROR incomparable types in type set */ == y + _ = y /* ERROR incomparable types in type set */ == x + _ = y /* ERROR incomparable types in type set */ == y + + _ = x /* ERROR type parameter P is not comparable with < */ < y +} + +func _[P any](x P, y any) { + _ = x /* ERROR incomparable types in type set */ == x + _ = x /* ERROR incomparable types in type set */ == y + _ = y == x // ERROR incomparable types in type set + _ = y == y + + _ = x /* ERROR type parameter P is not comparable with < */ < y +} diff --git a/src/internal/types/testdata/fixedbugs/issue48819.go b/src/internal/types/testdata/fixedbugs/issue48819.go new file mode 100644 index 00000000000000..9262110ea09e71 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48819.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +type T /* ERROR illegal cycle in declaration of T */ struct { + T +} + +func _(t T) { + _ = unsafe.Sizeof(t) // should not go into infinite recursion here +} diff --git a/src/internal/types/testdata/fixedbugs/issue48827.go b/src/internal/types/testdata/fixedbugs/issue48827.go new file mode 100644 index 00000000000000..aa1d12aaf5c638 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48827.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type G[P any] int + +type ( + _ G[int] + _ G[G /* ERROR "cannot use.*without instantiation" */] + _ bool /* ERROR "invalid operation: bool\[int\] \(bool is not a generic type\)" */ [int] + _ bool /* ERROR "invalid operation: bool\[G\] \(bool is not a generic type\)" */[G] +) + +// The example from the issue. +func _() { + _ = &([10]bool /* ERROR "invalid operation.*bool is not a generic type" */ [1 /* ERROR "expected type" */ ]{}) +} diff --git a/src/internal/types/testdata/fixedbugs/issue48951.go b/src/internal/types/testdata/fixedbugs/issue48951.go new file mode 100644 index 00000000000000..a9365281ee8abd --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48951.go @@ -0,0 +1,21 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + A1[P any] [10]A1 /* ERROR illegal cycle */ [P] + A2[P any] [10]A2 /* ERROR illegal cycle */ [*P] + A3[P any] [10]*A3[P] + + L1[P any] []L1[P] + + S1[P any] struct{ f S1 /* ERROR illegal cycle */ [P] } + S2[P any] struct{ f S2 /* ERROR illegal cycle */ [*P] } // like example in issue + S3[P any] struct{ f *S3[P] } + + I1[P any] interface{ I1 /* ERROR illegal cycle */ [P] } + I2[P any] interface{ I2 /* ERROR illegal cycle */ [*P] } + I3[P any] interface{ *I3 /* ERROR interface contains type constraints */ [P] } +) diff --git a/src/internal/types/testdata/fixedbugs/issue48962.go b/src/internal/types/testdata/fixedbugs/issue48962.go new file mode 100644 index 00000000000000..4270da1c733741 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48962.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T0[P any] struct { + f P +} + +type T1 /* ERROR illegal cycle */ struct { + _ T0[T1] +} diff --git a/src/internal/types/testdata/fixedbugs/issue48974.go b/src/internal/types/testdata/fixedbugs/issue48974.go new file mode 100644 index 00000000000000..d8ff7c8cf46d1b --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue48974.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Fooer interface { + Foo() +} + +type Fooable[F /* ERROR instantiation cycle */ Fooer] struct { + ptr F +} + +func (f *Fooable[F]) Adapter() *Fooable[*FooerImpl[F]] { + return &Fooable[*FooerImpl[F]]{&FooerImpl[F]{}} +} + +type FooerImpl[F Fooer] struct { +} + +func (fi *FooerImpl[F]) Foo() {} diff --git a/src/internal/types/testdata/fixedbugs/issue49003.go b/src/internal/types/testdata/fixedbugs/issue49003.go new file mode 100644 index 00000000000000..ece1a27bb927f4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49003.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. 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(s string) int { + for range s { + } +} // ERROR missing return diff --git a/src/internal/types/testdata/fixedbugs/issue49005.go b/src/internal/types/testdata/fixedbugs/issue49005.go new file mode 100644 index 00000000000000..7083dc9eef569b --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49005.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T1 interface{ M() } + +func F1() T1 + +var _ = F1().(*X1 /* ERROR undeclared name: X1 */) + +func _() { + switch F1().(type) { + case *X1 /* ERROR undeclared name: X1 */ : + } +} + +type T2 interface{ M() } + +func F2() T2 + +var _ = F2 /* ERROR impossible type assertion: F2\(\)\.\(\*X2\)\n\t\*X2 does not implement T2 \(missing method M\) */ ().(*X2) + +type X2 struct{} + +func _() { + switch F2().(type) { + case * /* ERROR impossible type switch case: \*X2\n\tF2\(\) \(value of type T2\) cannot have dynamic type \*X2 \(missing method M\) */ X2: + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue49043.go b/src/internal/types/testdata/fixedbugs/issue49043.go new file mode 100644 index 00000000000000..a360457d9ffd2d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49043.go @@ -0,0 +1,24 @@ +// Copyright 2021 The Go Authors. 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 + +// The example from the issue. +type ( + N[P any] M /* ERROR illegal cycle */ [P] + M[P any] N /* ERROR illegal cycle */ [P] +) + +// A slightly more complicated case. +type ( + A[P any] B /* ERROR illegal cycle */ [P] + B[P any] C[P] + C[P any] A[P] +) + +// Confusing but valid (note that `type T *T` is valid). +type ( + N1[P any] *M1[P] + M1[P any] *N1[P] +) diff --git a/src/internal/types/testdata/fixedbugs/issue49112.go b/src/internal/types/testdata/fixedbugs/issue49112.go new file mode 100644 index 00000000000000..61b757ccb2f631 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49112.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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[P int](P) {} + +func _() { + _ = f[int] + _ = f[[ /* ERROR \[\]int does not implement int */ ]int] + + f(0) + f/* ERROR \[\]int does not implement int */ ([]int{}) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49179.go b/src/internal/types/testdata/fixedbugs/issue49179.go new file mode 100644 index 00000000000000..8890e92f510f53 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49179.go @@ -0,0 +1,37 @@ +// Copyright 2021 The Go Authors. 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 f1[P int | string]() {} +func f2[P ~int | string | float64]() {} +func f3[P int](x P) {} + +type myInt int +type myFloat float64 + +func _() { + _ = f1[int] + _ = f1[myInt /* ERROR possibly missing ~ for int in constraint int \| string */] + _ = f2[myInt] + _ = f2[myFloat /* ERROR possibly missing ~ for float64 in constraint ~int \| string \| float64 */] + var x myInt + f3 /* ERROR myInt does not implement int \(possibly missing ~ for int in constraint int\) */ (x) +} + +// test case from the issue + +type SliceConstraint[T any] interface { + []T +} + +func Map[S SliceConstraint[E], E any](s S, f func(E) E) S { + return s +} + +type MySlice []int + +func f(s MySlice) { + Map[MySlice /* ERROR MySlice does not implement SliceConstraint\[int\] \(possibly missing ~ for \[\]int in constraint SliceConstraint\[int\]\) */, int](s, nil) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49242.go b/src/internal/types/testdata/fixedbugs/issue49242.go new file mode 100644 index 00000000000000..524a0cbae364ad --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49242.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Go Authors. 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 _[P int](x P) int { + return x // ERROR cannot use x .* as int value in return statement +} + +func _[P int]() int { + return P /* ERROR cannot use P\(1\) .* as int value in return statement */ (1) +} + +func _[P int](x int) P { + return x // ERROR cannot use x .* as P value in return statement +} + +func _[P, Q any](x P) Q { + return x // ERROR cannot use x .* as Q value in return statement +} + +// test case from issue +func F[G interface{ uint }]() int { + f := func(uint) int { return 0 } + return f(G /* ERROR cannot use G\(1\) .* as uint value in argument to f */ (1)) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49247.go b/src/internal/types/testdata/fixedbugs/issue49247.go new file mode 100644 index 00000000000000..3f25e0ee35526a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49247.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +func Add1024[T integer](s []T) { + for i, v := range s { + s[i] = v + 1024 // ERROR cannot convert 1024 \(untyped int constant\) to T + } +} + +func f[T interface{ int8 }]() { + println(T(1024 /* ERROR cannot convert 1024 \(untyped int value\) to T */)) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49276.go b/src/internal/types/testdata/fixedbugs/issue49276.go new file mode 100644 index 00000000000000..8839087b506712 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49276.go @@ -0,0 +1,46 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +type S /* ERROR illegal cycle in declaration of S */ struct { + _ [unsafe.Sizeof(s)]byte +} + +var s S + +// Since f is a pointer, this case could be valid. +// But it's pathological and not worth the expense. +type T struct { + f *[unsafe.Sizeof(T /* ERROR illegal cycle in type declaration */ {})]int +} + +// a mutually recursive case using unsafe.Sizeof +type ( + A1 struct { + _ [unsafe.Sizeof(B1{})]int + } + + B1 struct { + _ [unsafe.Sizeof(A1 /* ERROR illegal cycle in type declaration */ {})]int + } +) + +// a mutually recursive case using len +type ( + A2 struct { + f [len(B2{}.f)]int + } + + B2 struct { + f [len(A2 /* ERROR illegal cycle in type declaration */ {}.f)]int + } +) + +// test case from issue +type a struct { + _ [42 - unsafe.Sizeof(a /* ERROR illegal cycle in type declaration */ {})]byte +} diff --git a/src/internal/types/testdata/fixedbugs/issue49296.go b/src/internal/types/testdata/fixedbugs/issue49296.go new file mode 100644 index 00000000000000..eaa8e4dc7d8ad6 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49296.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. 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 _[ + T0 any, + T1 []int, + T2 ~float64 | ~complex128 | chan int, +]() { + _ = T0(nil /* ERROR cannot convert nil to T0 */ ) + _ = T1(1 /* ERROR cannot convert 1 .* to T1 */ ) + _ = T2(2 /* ERROR cannot convert 2 .* to T2 */ ) +} + +// test case from issue +func f[T interface{[]int}]() { + _ = T(1 /* ERROR cannot convert */ ) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49439.go b/src/internal/types/testdata/fixedbugs/issue49439.go new file mode 100644 index 00000000000000..6cc838b3b30104 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49439.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +type T0 /* ERROR illegal cycle */ [P T0[P]] struct{} + +type T1 /* ERROR illegal cycle */ [P T2[P]] struct{} +type T2[P T1[P]] struct{} + +type T3 /* ERROR illegal cycle */ [P interface{ ~struct{ f T3[int] } }] struct{} + +// valid cycle in M +type N[P M[P]] struct{} +type M[Q any] struct { F *M[Q] } + +// "crazy" case +type TC[P [unsafe.Sizeof(func() { + type T [P [unsafe.Sizeof(func(){})]byte] struct{} +})]byte] struct{} + +// test case from issue +type X /* ERROR illegal cycle */ [T any, PT X[T]] interface{} diff --git a/src/internal/types/testdata/fixedbugs/issue49482.go b/src/internal/types/testdata/fixedbugs/issue49482.go new file mode 100644 index 00000000000000..d5c52dc2884665 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49482.go @@ -0,0 +1,26 @@ +// Copyright 2022 The Go Authors. 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 + +// The following is OK, per the special handling for type literals discussed in issue #49482. +type _[P *struct{}] struct{} +type _[P *int,] int +type _[P (*int),] int + +const P = 2 // declare P to avoid noisy 'undeclared name' errors below. + +// The following parse as invalid array types due to parsing ambiguitiues. +type _ [P *int /* ERROR "int \(type\) is not an expression" */ ]int +type _ [P /* ERROR non-function P */ (*int)]int + +// Adding a trailing comma or an enclosing interface resolves the ambiguity. +type _[P *int,] int +type _[P (*int),] int +type _[P interface{*int}] int +type _[P interface{(*int)}] int + +// The following parse correctly as valid generic types. +type _[P *struct{} | int] struct{} +type _[P *struct{} | ~int] struct{} diff --git a/src/internal/types/testdata/fixedbugs/issue49541.go b/src/internal/types/testdata/fixedbugs/issue49541.go new file mode 100644 index 00000000000000..c8499c1b61d2ab --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49541.go @@ -0,0 +1,45 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type S[A, B any] struct { + f int +} + +func (S[A, B]) m() {} + +// TODO(gri): with type-type inference enabled we should only report one error +// below. See issue #50588. + +func _[A any](s S /* ERROR got 1 arguments but 2 type parameters */ [A]) { + // we should see no follow-on errors below + s.f = 1 + s.m() +} + +// another test case from the issue + +func _() { + X(Interface[*F /* ERROR got 1 arguments but 2 type parameters */ [string]](Impl{})) +} + +func X[Q Qer](fs Interface[Q]) { +} + +type Impl struct{} + +func (Impl) M() {} + +type Interface[Q Qer] interface { + M() +} + +type Qer interface { + Q() +} + +type F[A, B any] struct{} + +func (f *F[A, B]) Q() {} diff --git a/src/internal/types/testdata/fixedbugs/issue49579.go b/src/internal/types/testdata/fixedbugs/issue49579.go new file mode 100644 index 00000000000000..ee2d94ab8921fc --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49579.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type I[F any] interface { + Q(*F) +} + +func G[F any]() I[any] { + return g /* ERROR cannot use g\[F\]{} .* as I\[any\] value in return statement: g\[F\] does not implement I\[any\] \(method Q has pointer receiver\) */ [F]{} +} + +type g[F any] struct{} + +func (*g[F]) Q(*any) {} diff --git a/src/internal/types/testdata/fixedbugs/issue49592.go b/src/internal/types/testdata/fixedbugs/issue49592.go new file mode 100644 index 00000000000000..846deaa89aac62 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49592.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _() { + var x *interface{} + var y interface{} + _ = x == y +} diff --git a/src/internal/types/testdata/fixedbugs/issue49602.go b/src/internal/types/testdata/fixedbugs/issue49602.go new file mode 100644 index 00000000000000..208501fafd303e --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49602.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type M interface { + m() +} + +type C interface { + comparable +} + +type _ interface { + int | M // ERROR cannot use p\.M in union \(p\.M contains methods\) + int | comparable // ERROR cannot use comparable in union + int | C // ERROR cannot use p\.C in union \(p\.C embeds comparable\) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49705.go b/src/internal/types/testdata/fixedbugs/issue49705.go new file mode 100644 index 00000000000000..5b5fba2a1dc661 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49705.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +func shl[I Integer](n int) I { + return 1 << n +} diff --git a/src/internal/types/testdata/fixedbugs/issue49735.go b/src/internal/types/testdata/fixedbugs/issue49735.go new file mode 100644 index 00000000000000..50870226e47e7d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49735.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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 _[P1 any, P2 ~byte](s1 P1, s2 P2) { + _ = append(nil /* ERROR first argument to append must be a slice; have untyped nil */ , 0) + _ = append(s1 /* ERROR s1 .* has no core type */ , 0) + _ = append(s2 /* ERROR s2 .* has core type byte */ , 0) +} diff --git a/src/internal/types/testdata/fixedbugs/issue49739.go b/src/internal/types/testdata/fixedbugs/issue49739.go new file mode 100644 index 00000000000000..46b1e71a3b95cf --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49739.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Verify that we get an empty type set (not just an error) +// when using an invalid ~A. + +package p + +type A int +type C interface { + ~ /* ERROR invalid use of ~ */ A +} + +func f[_ C]() {} +func g[_ interface{ C }]() {} +func h[_ C | int]() {} + +func _() { + _ = f[int /* ERROR cannot implement C \(empty type set\) */] + _ = g[int /* ERROR cannot implement interface{C} \(empty type set\) */] + _ = h[int] +} diff --git a/src/internal/types/testdata/fixedbugs/issue49864.go b/src/internal/types/testdata/fixedbugs/issue49864.go new file mode 100644 index 00000000000000..0437e74a643fab --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue49864.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 _[P ~int, Q any](p P) { + _ = Q(p /* ERROR cannot convert */ ) +} diff --git a/src/internal/types/testdata/fixedbugs/issue50259.go b/src/internal/types/testdata/fixedbugs/issue50259.go new file mode 100644 index 00000000000000..6df8c64524324c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50259.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var x T[B] + +type T[_ any] struct{} +type A T[B] +type B = T[A] + +// test case from issue + +var v Box[Step] +type Box[T any] struct{} +type Step = Box[StepBox] +type StepBox Box[Step] diff --git a/src/internal/types/testdata/fixedbugs/issue50276.go b/src/internal/types/testdata/fixedbugs/issue50276.go new file mode 100644 index 00000000000000..97e477e6fa3210 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50276.go @@ -0,0 +1,39 @@ +// Copyright 2022 The Go Authors. 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 + +// simplified test case + +type transform[T any] struct{} +type pair[S any] struct {} + +var _ transform[step] + +type box transform[step] +type step = pair[box] + +// test case from issue + +type Transform[T any] struct{ hold T } +type Pair[S, T any] struct { + First S + Second T +} + +var first Transform[Step] + +// This line doesn't use the Step alias, and it compiles fine if you uncomment it. +var second Transform[Pair[Box, interface{}]] + +type Box *Transform[Step] + +// This line is the same as the `first` line, but it comes after the Box declaration and +// does not break the compile. +var third Transform[Step] + +type Step = Pair[Box, interface{}] + +// This line also does not break the compile +var fourth Transform[Step] diff --git a/src/internal/types/testdata/fixedbugs/issue50281.go b/src/internal/types/testdata/fixedbugs/issue50281.go new file mode 100644 index 00000000000000..f333e81a7054f8 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50281.go @@ -0,0 +1,26 @@ +// Copyright 2022 The Go Authors. 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 _[S string | []byte](s S) { + var buf []byte + _ = append(buf, s...) +} + +func _[S ~string | ~[]byte](s S) { + var buf []byte + _ = append(buf, s...) +} + +// test case from issue + +type byteseq interface { + string | []byte +} + +// This should allow to eliminate the two functions above. +func AppendByteString[source byteseq](buf []byte, s source) []byte { + return append(buf, s[1:6]...) +} diff --git a/src/internal/types/testdata/fixedbugs/issue50321.go b/src/internal/types/testdata/fixedbugs/issue50321.go new file mode 100644 index 00000000000000..199e66eb6ca721 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50321.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. 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 Ln[A A /* ERROR cannot use a type parameter as constraint */ ](p A) { +} diff --git a/src/internal/types/testdata/fixedbugs/issue50372.go b/src/internal/types/testdata/fixedbugs/issue50372.go new file mode 100644 index 00000000000000..4c9b65a72f5563 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50372.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Go Authors. 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 _(s []int) { + var i, j, k, l int + _, _, _, _ = i, j, k, l + + for range s {} + for i = range s {} + for i, j = range s {} + for i, j, k /* ERROR range clause permits at most two iteration variables|at most 2 expressions */ = range s {} + for i, j, k, l /* ERROR range clause permits at most two iteration variables|at most 2 expressions */ = range s {} +} + +func _(s chan int) { + var i, j, k, l int + _, _, _, _ = i, j, k, l + + for range s {} + for i = range s {} + for i, j /* ERROR range over .* permits only one iteration variable */ = range s {} + for i, j, k /* ERROR range over .* permits only one iteration variable|at most 2 expressions */ = range s {} + for i, j, k, l /* ERROR range over .* permits only one iteration variable|at most 2 expressions */ = range s {} +} diff --git a/src/internal/types/testdata/fixedbugs/issue50417.go b/src/internal/types/testdata/fixedbugs/issue50417.go new file mode 100644 index 00000000000000..2caef1b9863e65 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50417.go @@ -0,0 +1,68 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. + +package p + +type Sf struct { + f int +} + +func f0[P Sf](p P) { + _ = p.f // ERROR p\.f undefined + p.f /* ERROR p\.f undefined */ = 0 +} + +func f0t[P ~struct{f int}](p P) { + _ = p.f // ERROR p\.f undefined + p.f /* ERROR p\.f undefined */ = 0 +} + +var _ = f0[Sf] +var _ = f0t[Sf] + +var _ = f0[Sm /* ERROR does not implement */ ] +var _ = f0t[Sm /* ERROR does not implement */ ] + +func f1[P interface{ Sf; m() }](p P) { + _ = p.f // ERROR p\.f undefined + p.f /* ERROR p\.f undefined */ = 0 + p.m() +} + +var _ = f1[Sf /* ERROR missing method m */ ] +var _ = f1[Sm /* ERROR does not implement */ ] + +type Sm struct {} + +func (Sm) m() {} + +type Sfm struct { + f int +} + +func (Sfm) m() {} + +func f2[P interface{ Sfm; m() }](p P) { + _ = p.f // ERROR p\.f undefined + p.f /* ERROR p\.f undefined */ = 0 + p.m() +} + +var _ = f2[Sfm] + +// special case: core type is a named pointer type + +type PSfm *Sfm + +func f3[P interface{ PSfm }](p P) { + _ = p.f // ERROR p\.f undefined + p.f /* ERROR p\.f undefined */ = 0 + p.m /* ERROR type P has no field or method m */ () +} + +var _ = f3[PSfm] diff --git a/src/internal/types/testdata/fixedbugs/issue50426.go b/src/internal/types/testdata/fixedbugs/issue50426.go new file mode 100644 index 00000000000000..17ec0ce529688b --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50426.go @@ -0,0 +1,44 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type A1 [2]uint64 +type A2 [2]uint64 + +func (a A1) m() A1 { return a } +func (a A2) m() A2 { return a } + +func f[B any, T interface { + A1 | A2 + m() T +}](v T) { +} + +func _() { + var v A2 + // Use function type inference to infer type A2 for T. + // Don't use constraint type inference before function + // type inference for typed arguments, otherwise it would + // infer type [2]uint64 for T which doesn't have method m + // (was the bug). + f[int](v) +} + +// Keep using constraint type inference before function type +// inference for untyped arguments so we infer type float64 +// for E below, and not int (which would not work). +func g[S ~[]E, E any](S, E) {} + +func _() { + var s []float64 + g[[]float64](s, 0) +} + +// Keep using constraint type inference after function +// type inference for untyped arguments so we infer +// missing type arguments for which we only have the +// untyped arguments as starting point. +func h[E any, R []E](v E) R { return R{v} } +func _() []int { return h(0) } diff --git a/src/internal/types/testdata/fixedbugs/issue50427.go b/src/internal/types/testdata/fixedbugs/issue50427.go new file mode 100644 index 00000000000000..d89d63e3087d96 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50427.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. 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 + +// The parser no longer parses type parameters for methods. +// In the past, type checking the code below led to a crash (#50427). + +type T interface{ m[ /* ERROR "must have no type parameters" */ P any]() } + +func _(t T) { + var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = t /* ERROR "does not implement" */ +} + +type S struct{} + +func (S) m[ /* ERROR "must have no type parameters" */ P any]() {} + +func _(s S) { + var _ interface{ m[ /* ERROR "must have no type parameters" */ P any](); n() } = s /* ERROR "does not implement" */ + +} diff --git a/src/internal/types/testdata/fixedbugs/issue50450.go b/src/internal/types/testdata/fixedbugs/issue50450.go new file mode 100644 index 00000000000000..bae31115786055 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50450.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type S struct{} + +func f[P S]() {} + +var _ = f[S] diff --git a/src/internal/types/testdata/fixedbugs/issue50516.go b/src/internal/types/testdata/fixedbugs/issue50516.go new file mode 100644 index 00000000000000..f73015e2be8510 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50516.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. 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 _[P struct{ f int }](x P) { + _ = x.g // ERROR type P has no field or method g +} + +func _[P struct{ f int } | struct{ g int }](x P) { + _ = x.g // ERROR type P has no field or method g +} diff --git a/src/internal/types/testdata/fixedbugs/issue50646.go b/src/internal/types/testdata/fixedbugs/issue50646.go new file mode 100644 index 00000000000000..3bdba1113a3b47 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50646.go @@ -0,0 +1,26 @@ +// Copyright 2022 The Go Authors. 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 f1[_ comparable]() {} +func f2[_ interface{ comparable }]() {} + +type T interface{ m() } + +func _[P comparable, Q ~int, R any]() { + _ = f1[int] + _ = f1[T /* ERROR T does not implement comparable */ ] + _ = f1[any /* ERROR any does not implement comparable */ ] + _ = f1[P] + _ = f1[Q] + _ = f1[R /* ERROR R does not implement comparable */] + + _ = f2[int] + _ = f2[T /* ERROR T does not implement comparable */ ] + _ = f2[any /* ERROR any does not implement comparable */ ] + _ = f2[P] + _ = f2[Q] + _ = f2[R /* ERROR R does not implement comparable */] +} diff --git a/src/internal/types/testdata/fixedbugs/issue50729.go b/src/internal/types/testdata/fixedbugs/issue50729.go new file mode 100644 index 00000000000000..fe19fdfa688463 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50729.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. 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 + +// version 1 +var x1 T1[B1] + +type T1[_ any] struct{} +type A1 T1[B1] +type B1 = T1[A1] + +// version 2 +type T2[_ any] struct{} +type A2 T2[B2] +type B2 = T2[A2] + +var x2 T2[B2] diff --git a/src/internal/types/testdata/fixedbugs/issue50755.go b/src/internal/types/testdata/fixedbugs/issue50755.go new file mode 100644 index 00000000000000..afc7b2414cb37c --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50755.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Go Authors. 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 + +// The core type of M2 unifies with the type of m1 +// during function argument type inference. +// M2's constraint is unnamed. +func f1[K1 comparable, E1 any](m1 map[K1]E1) {} + +func f2[M2 map[string]int](m2 M2) { + f1(m2) +} + +// The core type of M3 unifies with the type of m1 +// during function argument type inference. +// M3's constraint is named. +type Map3 map[string]int + +func f3[M3 Map3](m3 M3) { + f1(m3) +} + +// The core type of M5 unifies with the core type of M4 +// during constraint type inference. +func f4[M4 map[K4]int, K4 comparable](m4 M4) {} + +func f5[M5 map[K5]int, K5 comparable](m5 M5) { + f4(m5) +} + +// test case from issue + +func Copy[MC ~map[KC]VC, KC comparable, VC any](dst, src MC) { + for k, v := range src { + dst[k] = v + } +} + +func Merge[MM ~map[KM]VM, KM comparable, VM any](ms ...MM) MM { + result := MM{} + for _, m := range ms { + Copy(result, m) + } + return result +} diff --git a/src/internal/types/testdata/fixedbugs/issue50779.go b/src/internal/types/testdata/fixedbugs/issue50779.go new file mode 100644 index 00000000000000..fe68c28bbadbe8 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50779.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type AC interface { + C +} + +type ST []int + +type R[S any, P any] struct{} + +type SR = R[SS, ST] + +type SS interface { + NSR(any) *SR // ERROR invalid use of type alias SR in recursive type +} + +type C interface { + NSR(any) *SR +} diff --git a/src/internal/types/testdata/fixedbugs/issue50782.go b/src/internal/types/testdata/fixedbugs/issue50782.go new file mode 100644 index 00000000000000..fd1ab11b8cf120 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50782.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. + +package p + +// The first example from the issue. +type Numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// numericAbs matches numeric types with an Abs method. +type numericAbs[T Numeric] interface { + ~struct{ Value T } + Abs() T +} + +// AbsDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T { + // Field accesses are not permitted for now. Keep an error so + // we can find and fix this code once the situation changes. + return a.Value // ERROR a\.Value undefined + // TODO: The error below should probably be positioned on the '-'. + // d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value + // return d.Abs() +} + +// The second example from the issue. +type T[P int] struct{ f P } + +func _[P T[P /* ERROR "P does not implement int" */ ]]() {} + +// Additional tests +func _[P T[T /* ERROR "T\[P\] does not implement int" */ [P /* ERROR "P does not implement int" */ ]]]() {} +func _[P T[Q /* ERROR "Q does not implement int" */ ], Q T[P /* ERROR "P does not implement int" */ ]]() {} +func _[P T[Q], Q int]() {} + +type C[P comparable] struct{ f P } +func _[P C[C[P]]]() {} +func _[P C[C /* ERROR "C\[Q\] does not implement comparable" */ [Q /* ERROR "Q does not implement comparable" */]], Q func()]() {} +func _[P [10]C[P]]() {} +func _[P struct{ f C[C[P]]}]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue50816.go b/src/internal/types/testdata/fixedbugs/issue50816.go new file mode 100644 index 00000000000000..e7e31d91922ee4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50816.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkg + +type I interface { + Foo() +} + +type T1 struct{} + +func (T1) foo() {} + +type T2 struct{} + +func (T2) foo() string { return "" } + +func _() { + var i I + _ = i /* ERROR impossible type assertion: i\.\(T1\)\n\tT1 does not implement I \(missing method Foo\)\n\t\thave foo\(\)\n\t\twant Foo\(\) */ .(T1) + _ = i /* ERROR impossible type assertion: i\.\(T2\)\n\tT2 does not implement I \(missing method Foo\)\n\t\thave foo\(\) string\n\t\twant Foo\(\) */ .(T2) +} diff --git a/src/internal/types/testdata/fixedbugs/issue50833.go b/src/internal/types/testdata/fixedbugs/issue50833.go new file mode 100644 index 00000000000000..e912e4d67dfe8a --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50833.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + S struct{ f int } + PS *S +) + +func a() []*S { return []*S{{f: 1}} } +func b() []PS { return []PS{{f: 1}} } + +func c[P *S]() []P { return []P{{f: 1}} } +func d[P PS]() []P { return []P{{f: 1}} } diff --git a/src/internal/types/testdata/fixedbugs/issue50912.go b/src/internal/types/testdata/fixedbugs/issue50912.go new file mode 100644 index 00000000000000..f161925049539f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50912.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. 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 Real[P ~complex128](x P) { + _ = real(x /* ERROR not supported */ ) +} + +func Imag[P ~complex128](x P) { + _ = imag(x /* ERROR not supported */ ) +} + +func Complex[P ~float64](x P) { + _ = complex(x /* ERROR not supported */ , 0) + _ = complex(0 /* ERROR not supported */ , x) + _ = complex(x /* ERROR not supported */ , x) +} diff --git a/src/internal/types/testdata/fixedbugs/issue50918.go b/src/internal/types/testdata/fixedbugs/issue50918.go new file mode 100644 index 00000000000000..41604b8bad9cd1 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50918.go @@ -0,0 +1,21 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type thing1 struct { + things []string +} + +type thing2 struct { + things []thing1 +} + +func _() { + var a1, b1 thing1 + _ = a1 /* ERROR struct containing \[\]string cannot be compared */ == b1 + + var a2, b2 thing2 + _ = a2 /* ERROR struct containing \[\]thing1 cannot be compared */ == b2 +} diff --git a/src/internal/types/testdata/fixedbugs/issue50929.go b/src/internal/types/testdata/fixedbugs/issue50929.go new file mode 100644 index 00000000000000..3629ecf1045c47 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50929.go @@ -0,0 +1,68 @@ +// Copyright 2022 The Go 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 is tested when running "go test -run Manual" +// without source arguments. Use for one-off debugging. + +package p + +import "fmt" + +type F[A, B any] int + +func G[A, B any](F[A, B]) { +} + +func _() { + // TODO(gri) only report one error below (issue #50932) + var x F /* ERROR got 1 arguments but 2 type parameters */ [int] + G(x /* ERROR does not match */) +} + +// test case from issue +// (lots of errors but doesn't crash anymore) + +type RC[G any, RG any] interface { + ~[]RG +} + +type RG[G any] struct{} + +type RSC[G any] []*RG[G] + +type M[Rc RC[G, RG], G any, RG any] struct { + Fn func(Rc) +} + +type NFn[Rc RC[G, RG], G any, RG any] func(Rc) + +func NC[Rc RC[G, RG], G any, RG any](nFn NFn[Rc, G, RG]) { + var empty Rc + nFn(empty) +} + +func NSG[G any](c RSC[G]) { + fmt.Println(c) +} + +func MMD[Rc RC /* ERROR got 1 arguments */ [RG], RG any, G any]() M /* ERROR got 2 arguments */ [Rc, RG] { + + var nFn NFn /* ERROR got 2 arguments */ [Rc, RG] + + var empty Rc + switch any(empty).(type) { + case BC /* ERROR undeclared name: BC */ : + + case RSC[G]: + nFn = NSG /* ERROR cannot use NSG\[G\] */ [G] + } + + return M /* ERROR got 2 arguments */ [Rc, RG]{ + Fn: func(rc Rc) { + NC(nFn /* ERROR does not match */ ) + }, + } + + return M /* ERROR got 2 arguments */ [Rc, RG]{} +} diff --git a/src/internal/types/testdata/fixedbugs/issue50965.go b/src/internal/types/testdata/fixedbugs/issue50965.go new file mode 100644 index 00000000000000..bf2dcc93d02e6f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50965.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. 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 _(x int, c string) { + switch x { + case c /* ERROR invalid case c in switch on x \(mismatched types string and int\) */ : + } +} + +func _(x, c []int) { + switch x { + case c /* ERROR invalid case c in switch on x \(slice can only be compared to nil\) */ : + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue51048.go b/src/internal/types/testdata/fixedbugs/issue51048.go new file mode 100644 index 00000000000000..58308370ea54b7 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51048.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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 _[P int]() { + _ = f[P] +} + +func f[T int]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue51145.go b/src/internal/types/testdata/fixedbugs/issue51145.go new file mode 100644 index 00000000000000..b84391df19793d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51145.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "fmt" + +type ( + _ [fmt /* ERROR invalid array length fmt */ ]int + _ [float64 /* ERROR invalid array length float64 */ ]int + _ [f /* ERROR invalid array length f */ ]int + _ [nil /* ERROR invalid array length nil */ ]int +) + +func f() + +var _ fmt.Stringer // use fmt diff --git a/src/internal/types/testdata/fixedbugs/issue51158.go b/src/internal/types/testdata/fixedbugs/issue51158.go new file mode 100644 index 00000000000000..3edc50538206ed --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51158.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Type checking the following code should not cause an infinite recursion. +func f[M map[K]int, K comparable](m M) { + f(m) +} + +// Equivalent code using mutual recursion. +func f1[M map[K]int, K comparable](m M) { + f2(m) +} +func f2[M map[K]int, K comparable](m M) { + f1(m) +} diff --git a/src/internal/types/testdata/fixedbugs/issue51229.go b/src/internal/types/testdata/fixedbugs/issue51229.go new file mode 100644 index 00000000000000..808b6471f6eb5f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51229.go @@ -0,0 +1,164 @@ +// Copyright 2022 The Go Authors. 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 + +// Constraint type inference should be independent of the +// ordering of the type parameter declarations. Try all +// permutations in the test case below. +// Permutations produced by https://go.dev/play/p/PHcZNGJTEBZ. + +func f00[S1 ~[]E1, S2 ~[]E2, E1 ~byte, E2 ~byte](S1, S2) {} +func f01[S2 ~[]E2, S1 ~[]E1, E1 ~byte, E2 ~byte](S1, S2) {} +func f02[E1 ~byte, S1 ~[]E1, S2 ~[]E2, E2 ~byte](S1, S2) {} +func f03[S1 ~[]E1, E1 ~byte, S2 ~[]E2, E2 ~byte](S1, S2) {} +func f04[S2 ~[]E2, E1 ~byte, S1 ~[]E1, E2 ~byte](S1, S2) {} +func f05[E1 ~byte, S2 ~[]E2, S1 ~[]E1, E2 ~byte](S1, S2) {} +func f06[E2 ~byte, S2 ~[]E2, S1 ~[]E1, E1 ~byte](S1, S2) {} +func f07[S2 ~[]E2, E2 ~byte, S1 ~[]E1, E1 ~byte](S1, S2) {} +func f08[S1 ~[]E1, E2 ~byte, S2 ~[]E2, E1 ~byte](S1, S2) {} +func f09[E2 ~byte, S1 ~[]E1, S2 ~[]E2, E1 ~byte](S1, S2) {} +func f10[S2 ~[]E2, S1 ~[]E1, E2 ~byte, E1 ~byte](S1, S2) {} +func f11[S1 ~[]E1, S2 ~[]E2, E2 ~byte, E1 ~byte](S1, S2) {} +func f12[S1 ~[]E1, E1 ~byte, E2 ~byte, S2 ~[]E2](S1, S2) {} +func f13[E1 ~byte, S1 ~[]E1, E2 ~byte, S2 ~[]E2](S1, S2) {} +func f14[E2 ~byte, S1 ~[]E1, E1 ~byte, S2 ~[]E2](S1, S2) {} +func f15[S1 ~[]E1, E2 ~byte, E1 ~byte, S2 ~[]E2](S1, S2) {} +func f16[E1 ~byte, E2 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {} +func f17[E2 ~byte, E1 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {} +func f18[E2 ~byte, E1 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {} +func f19[E1 ~byte, E2 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {} +func f20[S2 ~[]E2, E2 ~byte, E1 ~byte, S1 ~[]E1](S1, S2) {} +func f21[E2 ~byte, S2 ~[]E2, E1 ~byte, S1 ~[]E1](S1, S2) {} +func f22[E1 ~byte, S2 ~[]E2, E2 ~byte, S1 ~[]E1](S1, S2) {} +func f23[S2 ~[]E2, E1 ~byte, E2 ~byte, S1 ~[]E1](S1, S2) {} + +type myByte byte + +func _(a []byte, b []myByte) { + f00(a, b) + f01(a, b) + f02(a, b) + f03(a, b) + f04(a, b) + f05(a, b) + f06(a, b) + f07(a, b) + f08(a, b) + f09(a, b) + f10(a, b) + f11(a, b) + f12(a, b) + f13(a, b) + f14(a, b) + f15(a, b) + f16(a, b) + f17(a, b) + f18(a, b) + f19(a, b) + f20(a, b) + f21(a, b) + f22(a, b) + f23(a, b) +} + +// Constraint type inference may have to iterate. +// Again, the order of the type parameters shouldn't matter. + +func g0[S ~[]E, M ~map[string]S, E any](m M) {} +func g1[M ~map[string]S, S ~[]E, E any](m M) {} +func g2[E any, S ~[]E, M ~map[string]S](m M) {} +func g3[S ~[]E, E any, M ~map[string]S](m M) {} +func g4[M ~map[string]S, E any, S ~[]E](m M) {} +func g5[E any, M ~map[string]S, S ~[]E](m M) {} + +func _(m map[string][]byte) { + g0(m) + g1(m) + g2(m) + g3(m) + g4(m) + g5(m) +} + +// Worst-case scenario. +// There are 10 unknown type parameters. In each iteration of +// constraint type inference we infer one more, from right to left. +// Each iteration looks repeatedly at all 11 type parameters, +// requiring a total of 10*11 = 110 iterations with the current +// implementation. Pathological case. + +func h[K any, J ~*K, I ~*J, H ~*I, G ~*H, F ~*G, E ~*F, D ~*E, C ~*D, B ~*C, A ~*B](x A) {} + +func _(x **********int) { + h(x) +} + +// Examples with channel constraints and tilde. + +func ch1[P chan<- int]() (_ P) { return } // core(P) == chan<- int (single type, no tilde) +func ch2[P ~chan int]() { return } // core(P) == ~chan<- int (tilde) +func ch3[P chan E, E any](E) { return } // core(P) == chan<- E (single type, no tilde) +func ch4[P chan E | ~chan<- E, E any](E) { return } // core(P) == ~chan<- E (tilde) +func ch5[P chan int | chan<- int]() { return } // core(P) == chan<- int (not a single type) + +func _() { + // P can be inferred as there's a single specific type and no tilde. + var _ chan int = ch1 /* ERROR cannot use ch1.*value of type chan<- int */ () + var _ chan<- int = ch1() + + // P cannot be inferred as there's a tilde. + ch2 /* ERROR cannot infer P */ () + type myChan chan int + ch2[myChan]() + + // P can be inferred as there's a single specific type and no tilde. + var e int + ch3(e) + + // P cannot be inferred as there's more than one specific type and a tilde. + ch4 /* ERROR cannot infer P */ (e) + _ = ch4[chan int] + + // P cannot be inferred as there's more than one specific type. + ch5 /* ERROR cannot infer P */ () + ch5[chan<- int]() +} + +// test case from issue + +func equal[M1 ~map[K1]V1, M2 ~map[K2]V2, K1, K2 ~uint32, V1, V2 ~string](m1 M1, m2 M2) bool { + if len(m1) != len(m2) { + return false + } + for k, v1 := range m1 { + if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 { + return false + } + } + return true +} + +func equalFixed[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool { + if len(m1) != len(m2) { + return false + } + for k, v1 := range m1 { + if v2, ok := m2[K2(k)]; !ok || v1 != V1(v2) { + return false + } + } + return true +} + +type ( + someNumericID uint32 + someStringID string +) + +func _() { + foo := map[uint32]string{10: "bar"} + bar := map[someNumericID]someStringID{10: "bar"} + equal(foo, bar) +} diff --git a/src/internal/types/testdata/fixedbugs/issue51232.go b/src/internal/types/testdata/fixedbugs/issue51232.go new file mode 100644 index 00000000000000..3fa6a05732ac83 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51232.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type RC[RG any] interface { + ~[]RG +} + +type Fn[RCT RC[RG], RG any] func(RCT) + +type F[RCT RC[RG], RG any] interface { + Fn() Fn /* ERROR got 1 arguments */ [RCT] +} + +type concreteF[RCT RC[RG], RG any] struct { + makeFn func() Fn /* ERROR got 1 arguments */ [RCT] +} + +func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] { + return c.makeFn() +} + +func NewConcrete[RCT RC[RG], RG any](Rc RCT) F /* ERROR got 1 arguments */ [RCT] { + // TODO(rfindley): eliminate the duplicate error below. + return & /* ERROR cannot use .* as F\[RCT\] */ concreteF /* ERROR got 1 arguments */ [RCT]{ + makeFn: nil, + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue51233.go b/src/internal/types/testdata/fixedbugs/issue51233.go new file mode 100644 index 00000000000000..9c15028c91d23e --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51233.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. 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 + +// As of issue #51527, type-type inference has been disabled. + +type RC[RG any] interface { + ~[]RG +} + +type Fn[RCT RC[RG], RG any] func(RCT) + +type FFn[RCT RC[RG], RG any] func() Fn /* ERROR got 1 arguments */ [RCT] + +type F[RCT RC[RG], RG any] interface { + Fn() Fn /* ERROR got 1 arguments */ [RCT] +} + +type concreteF[RCT RC[RG], RG any] struct { + makeFn FFn /* ERROR got 1 arguments */ [RCT] +} + +func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] { + return c.makeFn() +} diff --git a/src/internal/types/testdata/fixedbugs/issue51257.go b/src/internal/types/testdata/fixedbugs/issue51257.go new file mode 100644 index 00000000000000..8a3eb3278de9b4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51257.go @@ -0,0 +1,46 @@ +// Copyright 2022 The Go Authors. 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[_ comparable]() {} + +type S1 struct{ x int } +type S2 struct{ x any } +type S3 struct{ x [10]interface{ m() } } + +func _[P1 comparable, P2 S2]() { + _ = f[S1] + _ = f[S2 /* ERROR S2 does not implement comparable */ ] + _ = f[S3 /* ERROR S3 does not implement comparable */ ] + + type L1 struct { x P1 } + type L2 struct { x P2 } + _ = f[L1] + _ = f[L2 /* ERROR L2 does not implement comparable */ ] +} + + +// example from issue + +type Set[T comparable] map[T]struct{} + +func NewSetFromSlice[T comparable](items []T) *Set[T] { + s := Set[T]{} + + for _, item := range items { + s[item] = struct{}{} + } + + return &s +} + +type T struct{ x any } + +func main() { + NewSetFromSlice /* ERROR T does not implement comparable */ ([]T{ + {"foo"}, + {5}, + }) +} diff --git a/src/internal/types/testdata/fixedbugs/issue51335.go b/src/internal/types/testdata/fixedbugs/issue51335.go new file mode 100644 index 00000000000000..0b5a1af0825d55 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51335.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type S1 struct{} +type S2 struct{} + +func _[P *S1|*S2]() { + _= []P{{ /* ERROR invalid composite literal element type P: no core type */ }} +} + +func _[P *S1|S1]() { + _= []P{{ /* ERROR invalid composite literal element type P: no core type */ }} +} diff --git a/src/internal/types/testdata/fixedbugs/issue51339.go b/src/internal/types/testdata/fixedbugs/issue51339.go new file mode 100644 index 00000000000000..38f86109e36400 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51339.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go 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 is tested when running "go test -run Manual" +// without source arguments. Use for one-off debugging. + +package p + +type T[P any, B *P] struct{} + +func (T /* ERROR cannot use generic type */ ) m0() {} + +// TODO(rfindley): eliminate the duplicate errors here. +func (/* ERROR got 1 type parameter, but receiver base type declares 2 */ T /* ERROR got 1 arguments but 2 type parameters */ [_]) m1() {} +func (T[_, _]) m2() {} +// TODO(gri) this error is unfortunate (issue #51343) +func (T /* ERROR got 3 arguments but 2 type parameters */ [_, _, _]) m3() {} diff --git a/src/internal/types/testdata/fixedbugs/issue51360.go b/src/internal/types/testdata/fixedbugs/issue51360.go new file mode 100644 index 00000000000000..fe3de04dbfd346 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51360.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. 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 _() { + len.Println /* ERROR cannot select on len */ + len.Println /* ERROR cannot select on len */ () + _ = len.Println /* ERROR cannot select on len */ + _ = len /* ERROR cannot index len */ [0] + _ = *len /* ERROR cannot indirect len */ +} diff --git a/src/internal/types/testdata/fixedbugs/issue51376.go b/src/internal/types/testdata/fixedbugs/issue51376.go new file mode 100644 index 00000000000000..d51607b7abb5fc --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51376.go @@ -0,0 +1,24 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Map map[string]int + +func f[M ~map[K]V, K comparable, V any](M) {} +func g[M map[K]V, K comparable, V any](M) {} + +func _[M1 ~map[K]V, M2 map[K]V, K comparable, V any]() { + var m1 M1 + f(m1) + g /* ERROR M1 does not implement map\[K\]V */ (m1) // M1 has tilde + + var m2 M2 + f(m2) + g(m2) // M1 does not have tilde + + var m3 Map + f(m3) + g /* ERROR Map does not implement map\[string\]int */ (m3) // M in g does not have tilde +} diff --git a/src/internal/types/testdata/fixedbugs/issue51386.go b/src/internal/types/testdata/fixedbugs/issue51386.go new file mode 100644 index 00000000000000..ef6223927a21f4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51386.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type myString string + +func _[P ~string | ~[]byte | ~[]rune]() { + _ = P("") + const s myString = "" + _ = P(s) +} + +func _[P myString]() { + _ = P("") +} diff --git a/src/internal/types/testdata/fixedbugs/issue51437.go b/src/internal/types/testdata/fixedbugs/issue51437.go new file mode 100644 index 00000000000000..376261516eec92 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51437.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T struct{} + +func (T) m() []int { return nil } + +func f(x T) { + for _, x := range func() []int { + return x.m() // x declared in parameter list of f + }() { + _ = x // x declared by range clause + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue51472.go b/src/internal/types/testdata/fixedbugs/issue51472.go new file mode 100644 index 00000000000000..ecdc9547fe2236 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51472.go @@ -0,0 +1,54 @@ +// Copyright 2022 The Go Authors. 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 _[T comparable](x T) { + _ = x == x +} + +func _[T interface{interface{comparable}}](x T) { + _ = x == x +} + +func _[T interface{comparable; interface{comparable}}](x T) { + _ = x == x +} + +func _[T interface{comparable; ~int}](x T) { + _ = x == x +} + +func _[T interface{comparable; ~[]byte}](x T) { + _ = x /* ERROR cannot compare */ == x +} + +// TODO(gri) The error message here should be better. See issue #51525. +func _[T interface{comparable; ~int; ~string}](x T) { + _ = x /* ERROR cannot compare */ == x +} + +// TODO(gri) The error message here should be better. See issue #51525. +func _[T interface{~int; ~string}](x T) { + _ = x /* ERROR cannot compare */ == x +} + +func _[T interface{comparable; interface{~int}; interface{int|float64}}](x T) { + _ = x == x +} + +func _[T interface{interface{comparable; ~int}; interface{~float64; comparable; m()}}](x T) { + _ = x /* ERROR cannot compare */ == x +} + +// test case from issue + +func f[T interface{comparable; []byte|string}](x T) { + _ = x == x +} + +func _(s []byte) { + f /* ERROR \[\]byte does not implement interface{comparable; \[\]byte \| string} */ (s) + _ = f[[ /* ERROR does not implement */ ]byte] +} diff --git a/src/internal/types/testdata/fixedbugs/issue51509.go b/src/internal/types/testdata/fixedbugs/issue51509.go new file mode 100644 index 00000000000000..5ae47176d07e2f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51509.go @@ -0,0 +1,7 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T /* ERROR illegal cycle */ T.x diff --git a/src/internal/types/testdata/fixedbugs/issue51525.go b/src/internal/types/testdata/fixedbugs/issue51525.go new file mode 100644 index 00000000000000..af1d1e6063d0a0 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51525.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. 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 _[T interface { + int + string +}](x T) { + _ = x /* ERROR empty type set */ == x +} + +func _[T interface{ int | []byte }](x T) { + _ = x /* ERROR incomparable types in type set */ == x +} diff --git a/src/internal/types/testdata/fixedbugs/issue51533.go b/src/internal/types/testdata/fixedbugs/issue51533.go new file mode 100644 index 00000000000000..bf46f755f9bc08 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51533.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. 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 _(x any) { + switch x { + case 0: + fallthrough // ERROR fallthrough statement out of place + _ = x + default: + } + + switch x.(type) { + case int: + fallthrough // ERROR cannot fallthrough in type switch + default: + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue51578.go b/src/internal/types/testdata/fixedbugs/issue51578.go new file mode 100644 index 00000000000000..5c204bae2097f4 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51578.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var _ = (*interface /* ERROR interface contains type constraints */ {int})(nil) + +// abbreviated test case from issue + +type TypeSet interface{ int | string } + +func _() { + f((*TypeSet /* ERROR interface contains type constraints */)(nil)) +} + +func f(any) {} \ No newline at end of file diff --git a/src/internal/types/testdata/fixedbugs/issue51593.go b/src/internal/types/testdata/fixedbugs/issue51593.go new file mode 100644 index 00000000000000..e06c39fac0829d --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51593.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. 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[P interface{ m(R) }, R any]() {} + +type T = interface { m(int) } + +func _() { + _ = f /* ERROR cannot infer R */ [T] // don't crash in type inference +} diff --git a/src/internal/types/testdata/fixedbugs/issue51607.go b/src/internal/types/testdata/fixedbugs/issue51607.go new file mode 100644 index 00000000000000..d8df143627e357 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51607.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. 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 + +// Interface types must be ignored during overlap test. + +type ( + T1 interface{int} + T2 interface{~int} + T3 interface{T1 | bool | string} + T4 interface{T2 | ~bool | ~string} +) + +type ( + // overlap errors for non-interface terms + // (like the interface terms, but explicitly inlined) + _ interface{int | int /* ERROR overlapping terms int and int */ } + _ interface{int | ~ /* ERROR overlapping terms ~int and int */ int} + _ interface{~int | int /* ERROR overlapping terms int and ~int */ } + _ interface{~int | ~ /* ERROR overlapping terms ~int and ~int */ int} + + _ interface{T1 | bool | string | T1 | bool /* ERROR overlapping terms bool and bool */ | string /* ERROR overlapping terms string and string */ } + _ interface{T1 | bool | string | T2 | ~ /* ERROR overlapping terms ~bool and bool */ bool | ~ /* ERROR overlapping terms ~string and string */ string} + + // no errors for interface terms + _ interface{T1 | T1} + _ interface{T1 | T2} + _ interface{T2 | T1} + _ interface{T2 | T2} + + _ interface{T3 | T3 | int} + _ interface{T3 | T4 | bool } + _ interface{T4 | T3 | string } + _ interface{T4 | T4 | float64 } +) + +func _[_ T1 | bool | string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T1 | bool | string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} +func _[_ T2 | ~bool | ~string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T2 | ~bool | ~string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} + +func _[_ T3 | T3 | int]() {} +func _[_ T3 | T4 | bool]() {} +func _[_ T4 | T3 | string]() {} +func _[_ T4 | T4 | float64]() {} + +// test cases from issue + +type _ interface { + interface {bool | int} | interface {bool | string} +} + +type _ interface { + interface {bool | int} ; interface {bool | string} +} + +type _ interface { + interface {bool; int} ; interface {bool; string} +} + +type _ interface { + interface {bool; int} | interface {bool; string} +} \ No newline at end of file diff --git a/src/internal/types/testdata/fixedbugs/issue51610.go b/src/internal/types/testdata/fixedbugs/issue51610.go new file mode 100644 index 00000000000000..d10c7885035de3 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51610.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. 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 _[P int | float64 | complex128]() { + _ = map[P]int{1: 1, 1.0 /* ERROR duplicate key 1 */ : 2, 1 /* ERROR duplicate key \(1 \+ 0i\) */ + 0i: 3} +} diff --git a/src/internal/types/testdata/fixedbugs/issue51616.go b/src/internal/types/testdata/fixedbugs/issue51616.go new file mode 100644 index 00000000000000..e0efc9e620ece2 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51616.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + C[T any] interface{~int; M() T} + + _ C[bool] + _ comparable + _ interface {~[]byte | ~string} + + // Alias type declarations may refer to "constraint" types + // like ordinary type declarations. + _ = C[bool] + _ = comparable + _ = interface {~[]byte | ~string} +) diff --git a/src/internal/types/testdata/fixedbugs/issue51658.go b/src/internal/types/testdata/fixedbugs/issue51658.go new file mode 100644 index 00000000000000..f32051caecb0e6 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51658.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go 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 test checks syntax errors which differ between +// go/parser and the syntax package. +// TODO: consolidate eventually + +package p + +type F { // ERROR expected type|type declaration + float64 +} // ERROR expected declaration|non-declaration statement + +func _[T F | int](x T) { + _ = x == 0 // don't crash when recording type of 0 +} + +// test case from issue + +type FloatType { // ERROR expected type|type declaration + float32 | float64 +} // ERROR expected declaration|non-declaration statement + +type IntegerType interface { + int8 | int16 | int32 | int64 | int | + uint8 | uint16 | uint32 | uint64 | uint +} + +type ComplexType interface { + complex64 | complex128 +} + +type Number interface { + FloatType | IntegerType | ComplexType +} + +func GetDefaultNumber[T Number](value, defaultValue T) T { + if value == 0 { + return defaultValue + } + return value +} diff --git a/src/internal/types/testdata/fixedbugs/issue51877.go b/src/internal/types/testdata/fixedbugs/issue51877.go new file mode 100644 index 00000000000000..06f054b257bdb7 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue51877.go @@ -0,0 +1,18 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type S struct { + f1 int + f2 bool +} + +var ( + _ = S{0} /* ERROR too few values in S{…} */ + _ = struct{ f1, f2 int }{0} /* ERROR too few values in struct{f1 int; f2 int}{…} */ + + _ = S{0, true, "foo" /* ERROR too many values in S{…} */} + _ = struct{ f1, f2 int }{0, 1, 2 /* ERROR too many values in struct{f1 int; f2 int}{…} */} +) diff --git a/src/internal/types/testdata/fixedbugs/issue52031.go b/src/internal/types/testdata/fixedbugs/issue52031.go new file mode 100644 index 00000000000000..448a550b250ca7 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue52031.go @@ -0,0 +1,33 @@ +// -lang=go1.12 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type resultFlags uint + +// Example from #52031. +// +// The following shifts should not produce errors on Go < 1.13, as their +// untyped constant operands are representable by type uint. +const ( + _ resultFlags = (1 << iota) / 2 + + reportEqual + reportUnequal + reportByIgnore + reportByMethod + reportByFunc + reportByCycle +) + +// Invalid cases. +var x int = 1 +var _ = (8 << x /* ERROR "signed shift count .* requires go1.13 or later" */) + +const _ = (1 << 1.2 /* ERROR "truncated to uint" */) + +var y float64 +var _ = (1 << y /* ERROR "must be integer" */) diff --git a/src/internal/types/testdata/fixedbugs/issue52401.go b/src/internal/types/testdata/fixedbugs/issue52401.go new file mode 100644 index 00000000000000..c7efd8c71807a0 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue52401.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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 _() { + const x = 0 + x /* ERROR cannot assign to x */ += 1 + x /* ERROR cannot assign to x */ ++ +} diff --git a/src/internal/types/testdata/fixedbugs/issue52529.go b/src/internal/types/testdata/fixedbugs/issue52529.go new file mode 100644 index 00000000000000..de7b2964b0ce1f --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue52529.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Foo[P any] struct { + _ *Bar[P] +} + +type Bar[Q any] Foo[Q] + +func (v *Bar[R]) M() { + _ = (*Foo[R])(v) +} diff --git a/src/internal/types/testdata/fixedbugs/issue52698.go b/src/internal/types/testdata/fixedbugs/issue52698.go new file mode 100644 index 00000000000000..d1b06a210da1cd --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue52698.go @@ -0,0 +1,62 @@ +// Copyright 2022 The Go Authors. 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 + +// correctness check: ensure that cycles through generic instantiations are detected +type T[P any] struct { + _ P +} + +type S /* ERROR illegal cycle */ struct { + _ T[S] +} + +// simplified test 1 + +var _ A1[A1[string]] + +type A1[P any] struct { + _ B1[P] +} + +type B1[P any] struct { + _ P +} + +// simplified test 2 +var _ B2[A2] + +type A2 struct { + _ B2[string] +} + +type B2[P any] struct { + _ C2[P] +} + +type C2[P any] struct { + _ P +} + +// test case from issue +type T23 interface { + ~struct { + Field0 T13[T15] + } +} + +type T1[P1 interface { +}] struct { + Field2 P1 +} + +type T13[P2 interface { +}] struct { + Field2 T1[P2] +} + +type T15 struct { + Field0 T13[string] +} diff --git a/src/internal/types/testdata/fixedbugs/issue52915.go b/src/internal/types/testdata/fixedbugs/issue52915.go new file mode 100644 index 00000000000000..2c38e5bccaf5aa --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue52915.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +type T[P any] struct { + T /* ERROR illegal cycle */ [P] +} + +func _[P any]() { + _ = unsafe.Sizeof(T[int]{}) + _ = unsafe.Sizeof(struct{ T[int] }{}) + + _ = unsafe.Sizeof(T[P]{}) + _ = unsafe.Sizeof(struct{ T[P] }{}) +} + +// TODO(gri) This is a follow-on error due to T[int] being invalid. +// We should try to avoid it. +const _ = unsafe /* ERROR not constant */ .Sizeof(T[int]{}) diff --git a/src/internal/types/testdata/fixedbugs/issue54280.go b/src/internal/types/testdata/fixedbugs/issue54280.go new file mode 100644 index 00000000000000..e83e1a140a93c6 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue54280.go @@ -0,0 +1,7 @@ +// Copyright 2022 The Go Authors. 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 + +const C = 912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912 // ERROR constant overflow diff --git a/src/internal/types/testdata/fixedbugs/issue54405.go b/src/internal/types/testdata/fixedbugs/issue54405.go new file mode 100644 index 00000000000000..e89d5e1b80a894 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue54405.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go 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 we don't see spurious errors for == +// for values with invalid types due to prior errors. + +package p + +var x struct { + f *NotAType /* ERROR undeclared name */ +} +var _ = x.f == nil // no error expected here + +var y *NotAType /* ERROR undeclared name */ +var _ = y == nil // no error expected here diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue6977.src b/src/internal/types/testdata/fixedbugs/issue6977.go similarity index 100% rename from src/cmd/compile/internal/types2/testdata/fixedbugs/issue6977.src rename to src/internal/types/testdata/fixedbugs/issue6977.go diff --git a/src/internal/types/testdata/spec/assignability.go b/src/internal/types/testdata/spec/assignability.go new file mode 100644 index 00000000000000..0ab8eb3e93df2d --- /dev/null +++ b/src/internal/types/testdata/spec/assignability.go @@ -0,0 +1,264 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package assignability + +// See the end of this package for the declarations +// of the types and variables used in these tests. + +// "x's type is identical to T" +func _[TP any](X TP) { + b = b + a = a + l = l + s = s + p = p + f = f + i = i + m = m + c = c + d = d + + B = B + A = A + L = L + S = S + P = P + F = F + I = I + M = M + C = C + D = D + X = X +} + +// "x's type V and T have identical underlying types +// and at least one of V or T is not a named type." +// (here a named type is a type with a name) +func _[TP1, TP2 Interface](X1 TP1, X2 TP2) { + b = B // ERROR cannot use B .* as int value + a = A + l = L + s = S + p = P + f = F + i = I + m = M + c = C + d = D + + B = b // ERROR cannot use b .* as Basic value + A = a + L = l + S = s + P = p + F = f + I = i + M = m + C = c + D = d + X1 = i // ERROR cannot use i .* as TP1 value + X1 = X2 // ERROR cannot use X2 .* as TP1 value +} + +// "T is an interface type and x implements T and T is not a type parameter" +func _[TP Interface](X TP) { + i = d // ERROR missing method m + i = D + i = X + X = i // ERROR cannot use i .* as TP value +} + +// "x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type" +// (here a named type is a type with a name) +type ( + _SendChan = chan<- int + _RecvChan = <-chan int + + SendChan _SendChan + RecvChan _RecvChan +) + +func _[ + _CC ~_Chan, + _SC ~_SendChan, + _RC ~_RecvChan, + + CC Chan, + SC SendChan, + RC RecvChan, +]() { + var ( + _ _SendChan = c + _ _RecvChan = c + _ _Chan = c + + _ _SendChan = C + _ _RecvChan = C + _ _Chan = C + + _ SendChan = c + _ RecvChan = c + _ Chan = c + + _ SendChan = C // ERROR cannot use C .* as SendChan value + _ RecvChan = C // ERROR cannot use C .* as RecvChan value + _ Chan = C + _ Chan = make /* ERROR cannot use make\(chan Basic\) .* as Chan value */ (chan Basic) + ) + + var ( + _ _CC = C // ERROR cannot use C .* as _CC value + _ _SC = C // ERROR cannot use C .* as _SC value + _ _RC = C // ERROR cannot use C .* as _RC value + + _ CC = _CC /* ERROR cannot use _CC\(nil\) .* as CC value */ (nil) + _ SC = _CC /* ERROR cannot use _CC\(nil\) .* as SC value */ (nil) + _ RC = _CC /* ERROR cannot use _CC\(nil\) .* as RC value */ (nil) + + _ CC = C // ERROR cannot use C .* as CC value + _ SC = C // ERROR cannot use C .* as SC value + _ RC = C // ERROR cannot use C .* as RC value + ) +} + +// "x's type V is not a named type and T is a type parameter, and x is assignable to each specific type in T's type set." +func _[ + TP0 any, + TP1 ~_Chan, + TP2 ~chan int | ~chan byte, +]() { + var ( + _ TP0 = c // ERROR cannot use c .* as TP0 value + _ TP0 = C // ERROR cannot use C .* as TP0 value + _ TP1 = c + _ TP1 = C // ERROR cannot use C .* as TP1 value + _ TP2 = c // ERROR .* cannot assign chan int to chan byte + ) +} + +// "x's type V is a type parameter and T is not a named type, and values x' of each specific type in V's type set are assignable to T." +func _[ + TP0 Interface, + TP1 ~_Chan, + TP2 ~chan int | ~chan byte, +](X0 TP0, X1 TP1, X2 TP2) { + i = X0 + I = X0 + c = X1 + C = X1 // ERROR cannot use X1 .* as Chan value + c = X2 // ERROR .* cannot assign chan byte \(in TP2\) to chan int +} + +// "x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type" +func _[TP Interface](X TP) { + b = nil // ERROR cannot use nil + a = nil // ERROR cannot use nil + l = nil + s = nil // ERROR cannot use nil + p = nil + f = nil + i = nil + m = nil + c = nil + d = nil // ERROR cannot use nil + + B = nil // ERROR cannot use nil + A = nil // ERROR cannot use nil + L = nil + S = nil // ERROR cannot use nil + P = nil + F = nil + I = nil + M = nil + C = nil + D = nil // ERROR cannot use nil + X = nil // ERROR cannot use nil +} + +// "x is an untyped constant representable by a value of type T" +func _[ + Int8 ~int8, + Int16 ~int16, + Int32 ~int32, + Int64 ~int64, + Int8_16 ~int8 | ~int16, +]( + i8 Int8, + i16 Int16, + i32 Int32, + i64 Int64, + i8_16 Int8_16, +) { + b = 42 + b = 42.0 + // etc. + + i8 = -1 << 7 + i8 = 1<<7 - 1 + i16 = -1 << 15 + i16 = 1<<15 - 1 + i32 = -1 << 31 + i32 = 1<<31 - 1 + i64 = -1 << 63 + i64 = 1<<63 - 1 + + i8_16 = -1 << 7 + i8_16 = 1<<7 - 1 + i8_16 = - /* ERROR cannot use .* as Int8_16 */ 1 << 15 + i8_16 = 1 /* ERROR cannot use .* as Int8_16 */ <<15 - 1 +} + +// proto-types for tests + +type ( + _Basic = int + _Array = [10]int + _Slice = []int + _Struct = struct{ f int } + _Pointer = *int + _Func = func(x int) string + _Interface = interface{ m() int } + _Map = map[string]int + _Chan = chan int + + Basic _Basic + Array _Array + Slice _Slice + Struct _Struct + Pointer _Pointer + Func _Func + Interface _Interface + Map _Map + Chan _Chan + Defined _Struct +) + +func (Defined) m() int + +// proto-variables for tests + +var ( + b _Basic + a _Array + l _Slice + s _Struct + p _Pointer + f _Func + i _Interface + m _Map + c _Chan + d _Struct + + B Basic + A Array + L Slice + S Struct + P Pointer + F Func + I Interface + M Map + C Chan + D Defined +) diff --git a/src/internal/types/testdata/spec/comparisons.go b/src/internal/types/testdata/spec/comparisons.go new file mode 100644 index 00000000000000..2a7598a5815d0f --- /dev/null +++ b/src/internal/types/testdata/spec/comparisons.go @@ -0,0 +1,120 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comparisons + +type ( + B int // basic type representative + A [10]func() + L []byte + S struct{ f []byte } + P *S + F func() + I interface{} + M map[string]int + C chan int +) + +var ( + b B + a A + l L + s S + p P + f F + i I + m M + c C +) + +func _() { + _ = nil == nil // ERROR operator == not defined on untyped nil + _ = b == b + _ = a /* ERROR \[10\]func\(\) cannot be compared */ == a + _ = l /* ERROR slice can only be compared to nil */ == l + _ = s /* ERROR struct containing \[\]byte cannot be compared */ == s + _ = p == p + _ = f /* ERROR func can only be compared to nil */ == f + _ = i == i + _ = m /* ERROR map can only be compared to nil */ == m + _ = c == c + + _ = b /* ERROR mismatched types */ == nil + _ = a /* ERROR mismatched types */ == nil + _ = l == nil + _ = s /* ERROR mismatched types */ == nil + _ = p == nil + _ = f == nil + _ = i == nil + _ = m == nil + _ = c == nil + + _ = nil /* ERROR operator < not defined on untyped nil */ < nil + _ = b < b + _ = a /* ERROR operator < not defined on array */ < a + _ = l /* ERROR operator < not defined on slice */ < l + _ = s /* ERROR operator < not defined on struct */ < s + _ = p /* ERROR operator < not defined on pointer */ < p + _ = f /* ERROR operator < not defined on func */ < f + _ = i /* ERROR operator < not defined on interface */ < i + _ = m /* ERROR operator < not defined on map */ < m + _ = c /* ERROR operator < not defined on chan */ < c +} + +func _[ + B int, + A [10]func(), + L []byte, + S struct{ f []byte }, + P *S, + F func(), + I interface{}, + J comparable, + M map[string]int, + C chan int, +]( + b B, + a A, + l L, + s S, + p P, + f F, + i I, + j J, + m M, + c C, +) { + _ = b == b + _ = a /* ERROR incomparable types in type set */ == a + _ = l /* ERROR incomparable types in type set */ == l + _ = s /* ERROR incomparable types in type set */ == s + _ = p == p + _ = f /* ERROR incomparable types in type set */ == f + _ = i /* ERROR incomparable types in type set */ == i + _ = j == j + _ = m /* ERROR incomparable types in type set */ == m + _ = c == c + + _ = b /* ERROR mismatched types */ == nil + _ = a /* ERROR mismatched types */ == nil + _ = l == nil + _ = s /* ERROR mismatched types */ == nil + _ = p == nil + _ = f == nil + _ = i /* ERROR mismatched types */ == nil + _ = j /* ERROR mismatched types */ == nil + _ = m == nil + _ = c == nil + + _ = b < b + _ = a /* ERROR type parameter A is not comparable with < */ < a + _ = l /* ERROR type parameter L is not comparable with < */ < l + _ = s /* ERROR type parameter S is not comparable with < */ < s + _ = p /* ERROR type parameter P is not comparable with < */ < p + _ = f /* ERROR type parameter F is not comparable with < */ < f + _ = i /* ERROR type parameter I is not comparable with < */ < i + _ = j /* ERROR type parameter J is not comparable with < */ < j + _ = m /* ERROR type parameter M is not comparable with < */ < m + _ = c /* ERROR type parameter C is not comparable with < */ < c +} diff --git a/src/internal/types/testdata/spec/conversions.go b/src/internal/types/testdata/spec/conversions.go new file mode 100644 index 00000000000000..e8fa4c5300b283 --- /dev/null +++ b/src/internal/types/testdata/spec/conversions.go @@ -0,0 +1,208 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package conversions + +import "unsafe" + +// constant conversions + +func _[T ~byte]() T { return 255 } +func _[T ~byte]() T { return 256 /* ERROR cannot use 256 .* as T value */ } + +func _[T ~byte]() { + const _ = T /* ERROR T\(0\) .* is not constant */ (0) + var _ T = 255 + var _ T = 256 // ERROR cannot use 256 .* as T value +} + +func _[T ~string]() T { return T('a') } +func _[T ~int | ~string]() T { return T('a') } +func _[T ~byte | ~int | ~string]() T { return T(256 /* ERROR cannot convert 256 .* to T */) } + +// implicit conversions never convert to string +func _[T ~string]() { + var _ string = 0 // ERROR cannot use .* as string value + var _ T = 0 // ERROR cannot use .* as T value +} + +// failing const conversions of constants to type parameters report a cause +func _[ + T1 any, + T2 interface{ m() }, + T3 ~int | ~float64 | ~bool, + T4 ~int | ~string, +]() { + _ = T1(0 /* ERROR cannot convert 0 .* to T1\n\tT1 does not contain specific types */) + _ = T2(1 /* ERROR cannot convert 1 .* to T2\n\tT2 does not contain specific types */) + _ = T3(2 /* ERROR cannot convert 2 .* to T3\n\tcannot convert 2 .* to bool \(in T3\) */) + _ = T4(3.14 /* ERROR cannot convert 3.14 .* to T4\n\tcannot convert 3.14 .* to int \(in T4\) */) +} + +// "x is assignable to T" +// - tested via assignability tests + +// "x's type and T have identical underlying types if tags are ignored" + +func _[X ~int, T ~int](x X) T { return T(x) } +func _[X struct { + f int "foo" +}, T struct { + f int "bar" +}](x X) T { + return T(x) +} + +type Foo struct { + f int "foo" +} +type Bar struct { + f int "bar" +} +type Far struct{ f float64 } + +func _[X Foo, T Bar](x X) T { return T(x) } +func _[X Foo | Bar, T Bar](x X) T { return T(x) } +func _[X Foo, T Foo | Bar](x X) T { return T(x) } +func _[X Foo, T Far](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by Foo\) to T\n\tcannot convert Foo \(in X\) to Far \(in T\) */) +} + +// "x's type and T are unnamed pointer types and their pointer base types +// have identical underlying types if tags are ignored" + +func _[X ~*Foo, T ~*Bar](x X) T { return T(x) } +func _[X ~*Foo | ~*Bar, T ~*Bar](x X) T { return T(x) } +func _[X ~*Foo, T ~*Foo | ~*Bar](x X) T { return T(x) } +func _[X ~*Foo, T ~*Far](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by ~\*Foo\) to T\n\tcannot convert \*Foo \(in X\) to \*Far \(in T\) */) +} + +// Verify that the defined types in constraints are considered for the rule above. + +type ( + B int + C int + X0 *B + T0 *C +) + +func _(x X0) T0 { return T0(x /* ERROR cannot convert */) } // non-generic reference +func _[X X0, T T0](x X) T { return T(x /* ERROR cannot convert */) } +func _[T T0](x X0) T { return T(x /* ERROR cannot convert */) } +func _[X X0](x X) T0 { return T0(x /* ERROR cannot convert */) } + +// "x's type and T are both integer or floating point types" + +func _[X Integer, T Integer](x X) T { return T(x) } +func _[X Unsigned, T Integer](x X) T { return T(x) } +func _[X Float, T Integer](x X) T { return T(x) } + +func _[X Integer, T Unsigned](x X) T { return T(x) } +func _[X Unsigned, T Unsigned](x X) T { return T(x) } +func _[X Float, T Unsigned](x X) T { return T(x) } + +func _[X Integer, T Float](x X) T { return T(x) } +func _[X Unsigned, T Float](x X) T { return T(x) } +func _[X Float, T Float](x X) T { return T(x) } + +func _[X, T Integer | Unsigned | Float](x X) T { return T(x) } +func _[X, T Integer | ~string](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by Integer \| ~string\) to T\n\tcannot convert string \(in X\) to int \(in T\) */) +} + +// "x's type and T are both complex types" + +func _[X, T Complex](x X) T { return T(x) } +func _[X, T Float | Complex](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by Float \| Complex\) to T\n\tcannot convert float32 \(in X\) to complex64 \(in T\) */) +} + +// "x is an integer or a slice of bytes or runes and T is a string type" + +type myInt int +type myString string + +func _[T ~string](x int) T { return T(x) } +func _[T ~string](x myInt) T { return T(x) } +func _[X Integer](x X) string { return string(x) } +func _[X Integer](x X) myString { return myString(x) } +func _[X Integer](x X) *string { + return (*string)(x /* ERROR cannot convert x \(variable of type X constrained by Integer\) to \*string\n\tcannot convert int \(in X\) to \*string */) +} + +func _[T ~string](x []byte) T { return T(x) } +func _[T ~string](x []rune) T { return T(x) } +func _[X ~[]byte, T ~string](x X) T { return T(x) } +func _[X ~[]rune, T ~string](x X) T { return T(x) } +func _[X Integer | ~[]byte | ~[]rune, T ~string](x X) T { return T(x) } +func _[X Integer | ~[]byte | ~[]rune, T ~*string](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by Integer \| ~\[\]byte \| ~\[\]rune\) to T\n\tcannot convert int \(in X\) to \*string \(in T\) */) +} + +// "x is a string and T is a slice of bytes or runes" + +func _[T ~[]byte](x string) T { return T(x) } +func _[T ~[]rune](x string) T { return T(x) } +func _[T ~[]rune](x *string) T { + return T(x /* ERROR cannot convert x \(variable of type \*string\) to T\n\tcannot convert \*string to \[\]rune \(in T\) */) +} + +func _[X ~string, T ~[]byte](x X) T { return T(x) } +func _[X ~string, T ~[]rune](x X) T { return T(x) } +func _[X ~string, T ~[]byte | ~[]rune](x X) T { return T(x) } +func _[X ~*string, T ~[]byte | ~[]rune](x X) T { + return T(x /* ERROR cannot convert x \(variable of type X constrained by ~\*string\) to T\n\tcannot convert \*string \(in X\) to \[\]byte \(in T\) */) +} + +// package unsafe: +// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" + +type myUintptr uintptr + +func _[X ~uintptr](x X) unsafe.Pointer { return unsafe.Pointer(x) } +func _[T unsafe.Pointer](x myUintptr) T { return T(x) } +func _[T unsafe.Pointer](x int64) T { + return T(x /* ERROR cannot convert x \(variable of type int64\) to T\n\tcannot convert int64 to unsafe\.Pointer \(in T\) */) +} + +// "and vice versa" + +func _[T ~uintptr](x unsafe.Pointer) T { return T(x) } +func _[X unsafe.Pointer](x X) uintptr { return uintptr(x) } +func _[X unsafe.Pointer](x X) myUintptr { return myUintptr(x) } +func _[X unsafe.Pointer](x X) int64 { + return int64(x /* ERROR cannot convert x \(variable of type X constrained by unsafe\.Pointer\) to int64\n\tcannot convert unsafe\.Pointer \(in X\) to int64 */) +} + +// "x is a slice, T is an array or pointer-to-array type, +// and the slice and array types have identical element types." + +func _[X ~[]E, T ~[10]E, E any](x X) T { return T(x) } +func _[X ~[]E, T ~*[10]E, E any](x X) T { return T(x) } + +// ---------------------------------------------------------------------------- +// The following declarations can be replaced by the exported types of the +// constraints package once all builders support importing interfaces with +// type constraints. + +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Integer interface { + Signed | Unsigned +} + +type Float interface { + ~float32 | ~float64 +} + +type Complex interface { + ~complex64 | ~complex128 +} diff --git a/src/internal/unsafeheader/unsafeheader_test.go b/src/internal/unsafeheader/unsafeheader_test.go index 6fb7cca888fa54..f3d1a9bb68882f 100644 --- a/src/internal/unsafeheader/unsafeheader_test.go +++ b/src/internal/unsafeheader/unsafeheader_test.go @@ -25,7 +25,7 @@ func TestTypeMatchesReflectType(t *testing.T) { }) } -func testHeaderMatchesReflect(t *testing.T, header, reflectHeader interface{}) { +func testHeaderMatchesReflect(t *testing.T, header, reflectHeader any) { h := reflect.TypeOf(header) rh := reflect.TypeOf(reflectHeader) diff --git a/src/internal/xcoff/ar.go b/src/internal/xcoff/ar.go index 0fb410f7dd71dc..2b432d5e1033a3 100644 --- a/src/internal/xcoff/ar.go +++ b/src/internal/xcoff/ar.go @@ -123,7 +123,7 @@ func NewArchive(r io.ReaderAt) (*Archive, error) { } var fhdr bigarFileHeader - if _, err := sr.Seek(0, os.SEEK_SET); err != nil { + if _, err := sr.Seek(0, io.SeekStart); err != nil { return nil, err } if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil { @@ -151,7 +151,7 @@ func NewArchive(r io.ReaderAt) (*Archive, error) { // The member header is normally 2 bytes larger. But it's easier // to read the name if the header is read without _ar_nam. // However, AIAFMAG must be read afterward. - if _, err := sr.Seek(off, os.SEEK_SET); err != nil { + if _, err := sr.Seek(off, io.SeekStart); err != nil { return nil, err } @@ -183,7 +183,7 @@ func NewArchive(r io.ReaderAt) (*Archive, error) { fileoff := off + AR_HSZ_BIG + namlen if fileoff&1 != 0 { fileoff++ - if _, err := sr.Seek(1, os.SEEK_CUR); err != nil { + if _, err := sr.Seek(1, io.SeekCurrent); err != nil { return nil, err } } diff --git a/src/internal/xcoff/file.go b/src/internal/xcoff/file.go index 05e4fd555ccac6..e859de932a9623 100644 --- a/src/internal/xcoff/file.go +++ b/src/internal/xcoff/file.go @@ -9,6 +9,7 @@ import ( "debug/dwarf" "encoding/binary" "fmt" + "internal/saferio" "io" "os" "strings" @@ -167,7 +168,7 @@ func NewFile(r io.ReaderAt) (*File, error) { f.TargetMachine = magic // Read XCOFF file header - if _, err := sr.Seek(0, os.SEEK_SET); err != nil { + if _, err := sr.Seek(0, io.SeekStart); err != nil { return nil, err } var nscns uint16 @@ -204,7 +205,7 @@ func NewFile(r io.ReaderAt) (*File, error) { // Read string table (located right after symbol table). offset := symptr + uint64(nsyms)*SYMESZ - if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil { + if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { return nil, err } // The first 4 bytes contain the length (in bytes). @@ -213,17 +214,15 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, err } if l > 4 { - if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil { - return nil, err - } - f.StringTable = make([]byte, l) - if _, err := io.ReadFull(sr, f.StringTable); err != nil { + st, err := saferio.ReadDataAt(sr, uint64(l), int64(offset)) + if err != nil { return nil, err } + f.StringTable = st } // Read section headers - if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), os.SEEK_SET); err != nil { + if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), io.SeekStart); err != nil { return nil, err } f.Sections = make([]*Section, nscns) @@ -269,7 +268,7 @@ func NewFile(r io.ReaderAt) (*File, error) { var idxToSym = make(map[int]*Symbol) // Read symbol table - if _, err := sr.Seek(int64(symptr), os.SEEK_SET); err != nil { + if _, err := sr.Seek(int64(symptr), io.SeekStart); err != nil { return nil, err } f.Symbols = make([]*Symbol, 0) @@ -284,6 +283,9 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, err } numaux = int(se.Nnumaux) + if numaux < 0 { + return nil, fmt.Errorf("malformed symbol table, invalid number of aux symbols") + } sym.SectionNumber = int(se.Nscnum) sym.StorageClass = int(se.Nsclass) sym.Value = uint64(se.Nvalue) @@ -304,6 +306,9 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, err } numaux = int(se.Nnumaux) + if numaux < 0 { + return nil, fmt.Errorf("malformed symbol table, invalid number of aux symbols") + } sym.SectionNumber = int(se.Nscnum) sym.StorageClass = int(se.Nsclass) sym.Value = se.Nvalue @@ -355,7 +360,7 @@ func NewFile(r io.ReaderAt) (*File, error) { // Read csect auxiliary entry (by convention, it is the last). if !needAuxFcn { - if _, err := sr.Seek(int64(numaux-1)*SYMESZ, os.SEEK_CUR); err != nil { + if _, err := sr.Seek(int64(numaux-1)*SYMESZ, io.SeekCurrent); err != nil { return nil, err } } @@ -382,7 +387,7 @@ func NewFile(r io.ReaderAt) (*File, error) { f.Symbols = append(f.Symbols, sym) skip: i += numaux // Skip auxiliary entries - if _, err := sr.Seek(int64(numaux)*SYMESZ, os.SEEK_CUR); err != nil { + if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil { return nil, err } } @@ -397,7 +402,7 @@ func NewFile(r io.ReaderAt) (*File, error) { if sect.Relptr == 0 { continue } - if _, err := sr.Seek(int64(sect.Relptr), os.SEEK_SET); err != nil { + if _, err := sr.Seek(int64(sect.Relptr), io.SeekStart); err != nil { return nil, err } for i := uint32(0); i < sect.Nreloc; i++ { @@ -508,7 +513,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { // Library name pattern is either path/base/member or base/member func (f *File) readImportIDs(s *Section) ([]string, error) { // Read loader header - if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil { + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { return nil, err } var istlen uint32 @@ -534,7 +539,7 @@ func (f *File) readImportIDs(s *Section) ([]string, error) { } // Read loader import file ID table - if _, err := s.sr.Seek(int64(impoff), os.SEEK_SET); err != nil { + if _, err := s.sr.Seek(int64(impoff), io.SeekStart); err != nil { return nil, err } table := make([]byte, istlen) @@ -577,7 +582,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { return nil, nil } // Read loader header - if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil { + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { return nil, err } var stlen uint32 @@ -606,7 +611,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { } // Read loader section string table - if _, err := s.sr.Seek(int64(stoff), os.SEEK_SET); err != nil { + if _, err := s.sr.Seek(int64(stoff), io.SeekStart); err != nil { return nil, err } st := make([]byte, stlen) @@ -621,7 +626,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { } // Read loader symbol table - if _, err := s.sr.Seek(int64(symoff), os.SEEK_SET); err != nil { + if _, err := s.sr.Seek(int64(symoff), io.SeekStart); err != nil { return nil, err } all := make([]ImportedSymbol, 0) diff --git a/src/io/example_test.go b/src/io/example_test.go index a18df9feff6965..818020e9dec6dd 100644 --- a/src/io/example_test.go +++ b/src/io/example_test.go @@ -5,7 +5,6 @@ package io_test import ( - "bytes" "fmt" "io" "log" @@ -142,7 +141,9 @@ func ExampleTeeReader() { r = io.TeeReader(r, os.Stdout) // Everything read from r will be copied to stdout. - io.ReadAll(r) + if _, err := io.ReadAll(r); err != nil { + log.Fatal(err) + } // Output: // some io.Reader stream to be read @@ -160,6 +161,21 @@ func ExampleSectionReader() { // io.Reader stream } +func ExampleSectionReader_Read() { + r := strings.NewReader("some io.Reader stream to be read\n") + s := io.NewSectionReader(r, 5, 17) + + buf := make([]byte, 9) + if _, err := s.Read(buf); err != nil { + log.Fatal(err) + } + + fmt.Printf("%s\n", buf) + + // Output: + // io.Reader +} + func ExampleSectionReader_ReadAt() { r := strings.NewReader("some io.Reader stream to be read\n") s := io.NewSectionReader(r, 5, 17) @@ -191,6 +207,16 @@ func ExampleSectionReader_Seek() { // stream } +func ExampleSectionReader_Size() { + r := strings.NewReader("some io.Reader stream to be read\n") + s := io.NewSectionReader(r, 5, 17) + + fmt.Println(s.Size()) + + // Output: + // 17 +} + func ExampleSeeker_Seek() { r := strings.NewReader("some io.Reader stream to be read\n") @@ -212,7 +238,7 @@ func ExampleSeeker_Seek() { func ExampleMultiWriter() { r := strings.NewReader("some io.Reader stream to be read\n") - var buf1, buf2 bytes.Buffer + var buf1, buf2 strings.Builder w := io.MultiWriter(&buf1, &buf2) if _, err := io.Copy(w, r); err != nil { diff --git a/src/io/export_test.go b/src/io/export_test.go index fa3e8e76f61ebc..06853f975f577f 100644 --- a/src/io/export_test.go +++ b/src/io/export_test.go @@ -6,3 +6,5 @@ package io // exported for test var ErrInvalidWrite = errInvalidWrite +var ErrWhence = errWhence +var ErrOffset = errOffset diff --git a/src/io/fs/fs.go b/src/io/fs/fs.go index e603afadb0b1b1..4ce4d1a5282ff3 100644 --- a/src/io/fs/fs.go +++ b/src/io/fs/fs.go @@ -120,6 +120,7 @@ type ReadDirFile interface { // In this case, if ReadDir returns an empty slice, it will return // a non-nil error explaining why. // At the end of a directory, the error is io.EOF. + // (ReadDir must return io.EOF itself, not an error wrapping io.EOF.) // // If n <= 0, ReadDir returns all the DirEntry values from the directory // in a single slice. In this case, if ReadDir succeeds (reads all the way @@ -153,7 +154,7 @@ type FileInfo interface { Mode() FileMode // file mode bits ModTime() time.Time // modification time IsDir() bool // abbreviation for Mode().IsDir() - Sys() interface{} // underlying data source (can return nil) + Sys() any // underlying data source (can return nil) } // A FileMode represents a file's mode and permission bits. diff --git a/src/io/fs/glob.go b/src/io/fs/glob.go index 45d9cb61b9632a..0e529cd05d139e 100644 --- a/src/io/fs/glob.go +++ b/src/io/fs/glob.go @@ -31,6 +31,16 @@ type GlobFS interface { // Otherwise, Glob uses ReadDir to traverse the directory tree // and look for matches for the pattern. func Glob(fsys FS, pattern string) (matches []string, err error) { + return globWithLimit(fsys, pattern, 0) +} + +func globWithLimit(fsys FS, pattern string, depth int) (matches []string, err error) { + // This limit is added to prevent stack exhaustion issues. See + // CVE-2022-30630. + const pathSeparatorsLimit = 10000 + if depth > pathSeparatorsLimit { + return nil, path.ErrBadPattern + } if fsys, ok := fsys.(GlobFS); ok { return fsys.Glob(pattern) } @@ -59,9 +69,9 @@ func Glob(fsys FS, pattern string) (matches []string, err error) { } var m []string - m, err = Glob(fsys, dir) + m, err = globWithLimit(fsys, dir, depth+1) if err != nil { - return + return nil, err } for _, d := range m { matches, err = glob(fsys, d, file, matches) diff --git a/src/io/fs/glob_test.go b/src/io/fs/glob_test.go index f19bebed77f6c7..d052eab371366f 100644 --- a/src/io/fs/glob_test.go +++ b/src/io/fs/glob_test.go @@ -8,6 +8,7 @@ import ( . "io/fs" "os" "path" + "strings" "testing" ) @@ -55,6 +56,15 @@ func TestGlobError(t *testing.T) { } } +func TestCVE202230630(t *testing.T) { + // Prior to CVE-2022-30630, a stack exhaustion would occur given a large + // number of separators. There is now a limit of 10,000. + _, err := Glob(os.DirFS("."), "/*"+strings.Repeat("/", 10001)) + if err != path.ErrBadPattern { + t.Fatalf("Glob returned err=%v, want %v", err, path.ErrBadPattern) + } +} + // contains reports whether vector contains the string s. func contains(vector []string, s string) bool { for _, elem := range vector { diff --git a/src/io/fs/walk.go b/src/io/fs/walk.go index 534876bad34205..cff26104f047ca 100644 --- a/src/io/fs/walk.go +++ b/src/io/fs/walk.go @@ -14,6 +14,11 @@ import ( // as an error by any function. var SkipDir = errors.New("skip this directory") +// SkipAll is used as a return value from WalkDirFuncs to indicate that +// all remaining files and directories are to be skipped. It is not returned +// as an error by any function. +var SkipAll = errors.New("skip everything and stop the walk") + // WalkDirFunc is the type of the function called by WalkDir to visit // each file or directory. // @@ -27,8 +32,10 @@ var SkipDir = errors.New("skip this directory") // The error result returned by the function controls how WalkDir // continues. If the function returns the special value SkipDir, WalkDir // skips the current directory (path if d.IsDir() is true, otherwise -// path's parent directory). Otherwise, if the function returns a non-nil -// error, WalkDir stops entirely and returns that error. +// path's parent directory). If the function returns the special value +// SkipAll, WalkDir skips all remaining files and directories. Otherwise, +// if the function returns a non-nil error, WalkDir stops entirely and +// returns that error. // // The err argument reports an error related to path, signaling that // WalkDir will not walk into that directory. The function can decide how @@ -47,18 +54,18 @@ var SkipDir = errors.New("skip this directory") // ReadDir. In this second case, the function is called twice with the // path of the directory: the first call is before the directory read is // attempted and has err set to nil, giving the function a chance to -// return SkipDir and avoid the ReadDir entirely. The second call is -// after a failed ReadDir and reports the error from ReadDir. +// return SkipDir or SkipAll and avoid the ReadDir entirely. The second call +// is after a failed ReadDir and reports the error from ReadDir. // (If ReadDir succeeds, there is no second call.) // // The differences between WalkDirFunc compared to filepath.WalkFunc are: // // - The second argument has type fs.DirEntry instead of fs.FileInfo. // - The function is called before reading a directory, to allow SkipDir -// to bypass the directory read entirely. +// or SkipAll to bypass the directory read entirely or skip all remaining +// files and directories respectively. // - If a directory read fails, the function is called a second time // for that directory to report the error. -// type WalkDirFunc func(path string, d DirEntry, err error) error // walkDir recursively descends path, calling walkDirFn. @@ -76,6 +83,9 @@ func walkDir(fsys FS, name string, d DirEntry, walkDirFn WalkDirFunc) error { // Second call, to report ReadDir error. err = walkDirFn(name, d, err) if err != nil { + if err == SkipDir && d.IsDir() { + err = nil + } return err } } @@ -111,7 +121,7 @@ func WalkDir(fsys FS, root string, fn WalkDirFunc) error { } else { err = walkDir(fsys, root, &statDirEntry{info}, fn) } - if err == SkipDir { + if err == SkipDir || err == SkipAll { return nil } return err diff --git a/src/io/fs/walk_test.go b/src/io/fs/walk_test.go index 5e127e71cd96da..04358beb240da6 100644 --- a/src/io/fs/walk_test.go +++ b/src/io/fs/walk_test.go @@ -8,6 +8,8 @@ import ( . "io/fs" "os" pathpkg "path" + "path/filepath" + "reflect" "testing" "testing/fstest" ) @@ -122,3 +124,34 @@ func TestWalkDir(t *testing.T) { } checkMarks(t, true) } + +func TestIssue51617(t *testing.T) { + dir := t.TempDir() + for _, sub := range []string{"a", filepath.Join("a", "bad"), filepath.Join("a", "next")} { + if err := os.Mkdir(filepath.Join(dir, sub), 0755); err != nil { + t.Fatal(err) + } + } + bad := filepath.Join(dir, "a", "bad") + if err := os.Chmod(bad, 0); err != nil { + t.Fatal(err) + } + defer os.Chmod(bad, 0700) // avoid errors on cleanup + var saw []string + err := WalkDir(os.DirFS(dir), ".", func(path string, d DirEntry, err error) error { + if err != nil { + return filepath.SkipDir + } + if d.IsDir() { + saw = append(saw, path) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + want := []string{".", "a", "a/bad", "a/next"} + if !reflect.DeepEqual(saw, want) { + t.Errorf("got directories %v, want %v", saw, want) + } +} diff --git a/src/io/io.go b/src/io/io.go index 2724321ed9d420..630ab73b56f4f1 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -47,7 +47,7 @@ var EOF = errors.New("EOF") // middle of reading a fixed-size block or data structure. var ErrUnexpectedEOF = errors.New("unexpected EOF") -// ErrNoProgress is returned by some clients of an Reader when +// ErrNoProgress is returned by some clients of a Reader when // many calls to Read have failed to return any data or error, // usually the sign of a broken Reader implementation. var ErrNoProgress = errors.New("multiple Read calls return no data or error") @@ -111,13 +111,15 @@ type Closer interface { // interpreted according to whence: // SeekStart means relative to the start of the file, // SeekCurrent means relative to the current offset, and -// SeekEnd means relative to the end. +// SeekEnd means relative to the end +// (for example, offset = -2 specifies the penultimate byte of the file). // Seek returns the new offset relative to the start of the -// file and an error, if any. +// file or an error, if any. // // Seeking to an offset before the start of the file is an error. -// Seeking to any positive offset is legal, but the behavior of subsequent -// I/O operations on the underlying object is implementation-dependent. +// Seeking to any positive offset may be allowed, but if the new offset exceeds +// the size of the underlying object the behavior of subsequent I/O operations +// is implementation-dependent. type Seeker interface { Seek(offset int64, whence int) (int64, error) } @@ -261,10 +263,11 @@ type ByteReader interface { // ByteScanner is the interface that adds the UnreadByte method to the // basic ReadByte method. // -// UnreadByte causes the next call to ReadByte to return the same byte -// as the previous call to ReadByte. -// It may be an error to call UnreadByte twice without an intervening -// call to ReadByte. +// UnreadByte causes the next call to ReadByte to return the last byte read. +// If the last operation was not a successful call to ReadByte, UnreadByte may +// return an error, unread the last byte read (or the byte prior to the +// last-unread byte), or (in implementations that support the Seeker interface) +// seek to one byte before the current offset. type ByteScanner interface { ByteReader UnreadByte() error @@ -277,7 +280,7 @@ type ByteWriter interface { // RuneReader is the interface that wraps the ReadRune method. // -// ReadRune reads a single UTF-8 encoded Unicode character +// ReadRune reads a single encoded Unicode character // and returns the rune and its size in bytes. If no character is // available, err will be set. type RuneReader interface { @@ -287,10 +290,11 @@ type RuneReader interface { // RuneScanner is the interface that adds the UnreadRune method to the // basic ReadRune method. // -// UnreadRune causes the next call to ReadRune to return the same rune -// as the previous call to ReadRune. -// It may be an error to call UnreadRune twice without an intervening -// call to ReadRune. +// UnreadRune causes the next call to ReadRune to return the last rune read. +// If the last operation was not a successful call to ReadRune, UnreadRune may +// return an error, unread the last rune read (or the rune prior to the +// last-unread rune), or (in implementations that support the Seeker interface) +// seek to the start of the rune before the current offset. type RuneScanner interface { RuneReader UnreadRune() error @@ -478,7 +482,16 @@ func (l *LimitedReader) Read(p []byte) (n int, err error) { // NewSectionReader returns a SectionReader that reads from r // starting at offset off and stops with EOF after n bytes. func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader { - return &SectionReader{r, off, off, off + n} + var remaining int64 + const maxint64 = 1<<63 - 1 + if off <= maxint64-n { + remaining = n + off + } else { + // Overflow, with no way to return error. + // Assume we can read up to an offset of 1<<63 - 1. + remaining = maxint64 + } + return &SectionReader{r, off, off, remaining} } // SectionReader implements Read, Seek, and ReadAt on a section @@ -542,6 +555,46 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) { // Size returns the size of the section in bytes. func (s *SectionReader) Size() int64 { return s.limit - s.base } +// An OffsetWriter maps writes at offset base to offset base+off in the underlying writer. +type OffsetWriter struct { + w WriterAt + base int64 // the original offset + off int64 // the current offset +} + +// NewOffsetWriter returns an OffsetWriter that writes to w +// starting at offset off. +func NewOffsetWriter(w WriterAt, off int64) *OffsetWriter { + return &OffsetWriter{w, off, off} +} + +func (o *OffsetWriter) Write(p []byte) (n int, err error) { + n, err = o.w.WriteAt(p, o.off) + o.off += int64(n) + return +} + +func (o *OffsetWriter) WriteAt(p []byte, off int64) (n int, err error) { + off += o.base + return o.w.WriteAt(p, off) +} + +func (o *OffsetWriter) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case SeekStart: + offset += o.base + case SeekCurrent: + offset += o.off + } + if offset < o.base { + return 0, errOffset + } + o.off = offset + return offset - o.base, nil +} + // TeeReader returns a Reader that writes to w what it reads from r. // All reads from r performed through it are matched with // corresponding writes to w. There is no internal buffering - @@ -585,7 +638,7 @@ func (discard) WriteString(s string) (int, error) { } var blackHolePool = sync.Pool{ - New: func() interface{} { + New: func() any { b := make([]byte, 8192) return &b }, @@ -609,7 +662,12 @@ func (discard) ReadFrom(r Reader) (n int64, err error) { // NopCloser returns a ReadCloser with a no-op Close method wrapping // the provided Reader r. +// If r implements WriterTo, the returned ReadCloser will implement WriterTo +// by forwarding calls to r. func NopCloser(r Reader) ReadCloser { + if _, ok := r.(WriterTo); ok { + return nopCloserWriterTo{r} + } return nopCloser{r} } @@ -619,6 +677,16 @@ type nopCloser struct { func (nopCloser) Close() error { return nil } +type nopCloserWriterTo struct { + Reader +} + +func (nopCloserWriterTo) Close() error { return nil } + +func (c nopCloserWriterTo) WriteTo(w Writer) (n int64, err error) { + return c.Reader.(WriterTo).WriteTo(w) +} + // ReadAll reads from r until an error or EOF and returns the data it read. // A successful call returns err == nil, not err == EOF. Because ReadAll is // defined to read from src until EOF, it does not treat an EOF from Read diff --git a/src/io/io_test.go b/src/io/io_test.go index 5b355e8c55b169..35db15c3ba4fba 100644 --- a/src/io/io_test.go +++ b/src/io/io_test.go @@ -9,7 +9,10 @@ import ( "errors" "fmt" . "io" + "os" "strings" + "sync" + "sync/atomic" "testing" ) @@ -430,6 +433,20 @@ func TestSectionReader_Size(t *testing.T) { } } +func TestSectionReader_Max(t *testing.T) { + r := strings.NewReader("abcdef") + const maxint64 = 1<<63 - 1 + sr := NewSectionReader(r, 3, maxint64) + n, err := sr.Read(make([]byte, 3)) + if n != 3 || err != nil { + t.Errorf("Read = %v %v, want 3, nil", n, err) + } + n, err = sr.Read(make([]byte, 3)) + if n != 0 || err != EOF { + t.Errorf("Read = %v, %v, want 0, EOF", n, err) + } +} + // largeWriter returns an invalid count that is larger than the number // of bytes provided (issue 39978). type largeWriter struct { @@ -457,3 +474,198 @@ func TestCopyLargeWriter(t *testing.T) { t.Errorf("Copy error: got %v, want %v", err, want) } } + +func TestNopCloserWriterToForwarding(t *testing.T) { + for _, tc := range [...]struct { + Name string + r Reader + }{ + {"not a WriterTo", Reader(nil)}, + {"a WriterTo", struct { + Reader + WriterTo + }{}}, + } { + nc := NopCloser(tc.r) + + _, expected := tc.r.(WriterTo) + _, got := nc.(WriterTo) + if expected != got { + t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected) + } + } +} + +func TestOffsetWriter_Seek(t *testing.T) { + tmpfilename := "TestOffsetWriter_Seek" + tmpfile, err := os.CreateTemp(t.TempDir(), tmpfilename) + if err != nil || tmpfile == nil { + t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err) + } + defer tmpfile.Close() + w := NewOffsetWriter(tmpfile, 0) + + // Should throw error errWhence if whence is not valid + t.Run("errWhence", func(t *testing.T) { + for _, whence := range []int{-3, -2, -1, 3, 4, 5} { + var offset int64 = 0 + gotOff, gotErr := w.Seek(offset, whence) + if gotOff != 0 || gotErr != ErrWhence { + t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", + whence, offset, gotOff, gotErr, 0, ErrWhence) + } + } + }) + + // Should throw error errOffset if offset is negative + t.Run("errOffset", func(t *testing.T) { + for _, whence := range []int{SeekStart, SeekCurrent} { + for offset := int64(-3); offset < 0; offset++ { + gotOff, gotErr := w.Seek(offset, whence) + if gotOff != 0 || gotErr != ErrOffset { + t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", + whence, offset, gotOff, gotErr, 0, ErrOffset) + } + } + } + }) + + // Normal tests + t.Run("normal", func(t *testing.T) { + tests := []struct { + offset int64 + whence int + returnOff int64 + }{ + // keep in order + {whence: SeekStart, offset: 1, returnOff: 1}, + {whence: SeekStart, offset: 2, returnOff: 2}, + {whence: SeekStart, offset: 3, returnOff: 3}, + {whence: SeekCurrent, offset: 1, returnOff: 4}, + {whence: SeekCurrent, offset: 2, returnOff: 6}, + {whence: SeekCurrent, offset: 3, returnOff: 9}, + } + for idx, tt := range tests { + gotOff, gotErr := w.Seek(tt.offset, tt.whence) + if gotOff != tt.returnOff || gotErr != nil { + t.Errorf("%d:: For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, )", + idx+1, tt.whence, tt.offset, gotOff, gotErr, tt.returnOff) + } + } + }) +} + +func TestOffsetWriter_WriteAt(t *testing.T) { + const content = "0123456789ABCDEF" + contentSize := int64(len(content)) + tmpdir, err := os.MkdirTemp(t.TempDir(), "TestOffsetWriter_WriteAt") + if err != nil { + t.Fatal(err) + } + + work := func(off, at int64) { + position := fmt.Sprintf("off_%d_at_%d", off, at) + tmpfile, err := os.CreateTemp(tmpdir, position) + if err != nil || tmpfile == nil { + t.Fatalf("CreateTemp(%s) failed: %v", position, err) + } + defer tmpfile.Close() + + var writeN int64 + var wg sync.WaitGroup + // Concurrent writes, one byte at a time + for step, value := range []byte(content) { + wg.Add(1) + go func(wg *sync.WaitGroup, tmpfile *os.File, value byte, off, at int64, step int) { + defer wg.Done() + + w := NewOffsetWriter(tmpfile, off) + n, e := w.WriteAt([]byte{value}, at+int64(step)) + if e != nil { + t.Errorf("WriteAt failed. off: %d, at: %d, step: %d\n error: %v", off, at, step, e) + } + atomic.AddInt64(&writeN, int64(n)) + }(&wg, tmpfile, value, off, at, step) + } + wg.Wait() + + // Read one more byte to reach EOF + buf := make([]byte, contentSize+1) + readN, err := tmpfile.ReadAt(buf, off+at) + if err != EOF { + t.Fatalf("ReadAt failed: %v", err) + } + readContent := string(buf[:contentSize]) + if writeN != int64(readN) || writeN != contentSize || readContent != content { + t.Fatalf("%s:: WriteAt(%s, %d) error. \ngot n: %v, content: %s \nexpected n: %v, content: %v", + position, content, at, readN, readContent, contentSize, content) + } + } + for off := int64(0); off < 2; off++ { + for at := int64(0); at < 2; at++ { + work(off, at) + } + } +} + +func TestOffsetWriter_Write(t *testing.T) { + const content = "0123456789ABCDEF" + contentSize := len(content) + tmpdir := t.TempDir() + + makeOffsetWriter := func(name string) (*OffsetWriter, *os.File) { + tmpfilename := "TestOffsetWriter_Write_" + name + tmpfile, err := os.CreateTemp(tmpdir, tmpfilename) + if err != nil || tmpfile == nil { + t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err) + } + return NewOffsetWriter(tmpfile, 0), tmpfile + } + checkContent := func(name string, f *os.File) { + // Read one more byte to reach EOF + buf := make([]byte, contentSize+1) + readN, err := f.ReadAt(buf, 0) + if err != EOF { + t.Fatalf("ReadAt failed, err: %v", err) + } + readContent := string(buf[:contentSize]) + if readN != contentSize || readContent != content { + t.Fatalf("%s error. \ngot n: %v, content: %s \nexpected n: %v, content: %v", + name, readN, readContent, contentSize, content) + } + } + + var name string + name = "Write" + t.Run(name, func(t *testing.T) { + // Write directly (off: 0, at: 0) + // Write content to file + w, f := makeOffsetWriter(name) + defer f.Close() + for _, value := range []byte(content) { + n, err := w.Write([]byte{value}) + if err != nil { + t.Fatalf("Write failed, n: %d, err: %v", n, err) + } + } + checkContent(name, f) + + // Copy -> Write + // Copy file f to file f2 + name = "Copy" + w2, f2 := makeOffsetWriter(name) + defer f2.Close() + Copy(w2, f) + checkContent(name, f2) + }) + + // Copy -> WriteTo -> Write + // Note: strings.Reader implements the io.WriterTo interface. + name = "Write_Of_Copy_WriteTo" + t.Run(name, func(t *testing.T) { + w, f := makeOffsetWriter(name) + defer f.Close() + Copy(w, strings.NewReader(content)) + checkContent(name, f) + }) +} diff --git a/src/io/ioutil/ioutil.go b/src/io/ioutil/ioutil.go index 45682b89c993f0..6a1d69172ce624 100644 --- a/src/io/ioutil/ioutil.go +++ b/src/io/ioutil/ioutil.go @@ -4,7 +4,7 @@ // Package ioutil implements some I/O utility functions. // -// As of Go 1.16, the same functionality is now provided +// Deprecated: As of Go 1.16, the same functionality is now provided // by package io or package os, and those implementations // should be preferred in new code. // See the specific function documentation for details. @@ -22,7 +22,7 @@ import ( // defined to read from src until EOF, it does not treat an EOF from Read // as an error to be reported. // -// As of Go 1.16, this function simply calls io.ReadAll. +// Deprecated: As of Go 1.16, this function simply calls io.ReadAll. func ReadAll(r io.Reader) ([]byte, error) { return io.ReadAll(r) } @@ -32,7 +32,7 @@ func ReadAll(r io.Reader) ([]byte, error) { // reads the whole file, it does not treat an EOF from Read as an error // to be reported. // -// As of Go 1.16, this function simply calls os.ReadFile. +// Deprecated: As of Go 1.16, this function simply calls os.ReadFile. func ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) } @@ -41,7 +41,7 @@ func ReadFile(filename string) ([]byte, error) { // If the file does not exist, WriteFile creates it with permissions perm // (before umask); otherwise WriteFile truncates it before writing, without changing permissions. // -// As of Go 1.16, this function simply calls os.WriteFile. +// Deprecated: As of Go 1.16, this function simply calls os.WriteFile. func WriteFile(filename string, data []byte, perm fs.FileMode) error { return os.WriteFile(filename, data, perm) } @@ -51,10 +51,21 @@ func WriteFile(filename string, data []byte, perm fs.FileMode) error { // sorted by filename. If an error occurs reading the directory, // ReadDir returns no directory entries along with the error. // -// As of Go 1.16, os.ReadDir is a more efficient and correct choice: +// Deprecated: As of Go 1.16, os.ReadDir is a more efficient and correct choice: // it returns a list of fs.DirEntry instead of fs.FileInfo, // and it returns partial results in the case of an error // midway through reading a directory. +// +// If you must continue obtaining a list of fs.FileInfo, you still can: +// +// entries, err := os.ReadDir(dirname) +// if err != nil { ... } +// infos := make([]fs.FileInfo, 0, len(entries)) +// for _, entry := range entries { +// info, err := entry.Info() +// if err != nil { ... } +// infos = append(infos, info) +// } func ReadDir(dirname string) ([]fs.FileInfo, error) { f, err := os.Open(dirname) if err != nil { @@ -72,7 +83,7 @@ func ReadDir(dirname string) ([]fs.FileInfo, error) { // NopCloser returns a ReadCloser with a no-op Close method wrapping // the provided Reader r. // -// As of Go 1.16, this function simply calls io.NopCloser. +// Deprecated: As of Go 1.16, this function simply calls io.NopCloser. func NopCloser(r io.Reader) io.ReadCloser { return io.NopCloser(r) } @@ -80,5 +91,5 @@ func NopCloser(r io.Reader) io.ReadCloser { // Discard is an io.Writer on which all Write calls succeed // without doing anything. // -// As of Go 1.16, this value is simply io.Discard. +// Deprecated: As of Go 1.16, this value is simply io.Discard. var Discard io.Writer = io.Discard diff --git a/src/io/ioutil/tempfile.go b/src/io/ioutil/tempfile.go index c43db2c080d69e..0561ad5a274e75 100644 --- a/src/io/ioutil/tempfile.go +++ b/src/io/ioutil/tempfile.go @@ -20,7 +20,7 @@ import ( // to find the pathname of the file. It is the caller's responsibility // to remove the file when no longer needed. // -// As of Go 1.17, this function simply calls os.CreateTemp. +// Deprecated: As of Go 1.17, this function simply calls os.CreateTemp. func TempFile(dir, pattern string) (f *os.File, err error) { return os.CreateTemp(dir, pattern) } @@ -35,7 +35,7 @@ func TempFile(dir, pattern string) (f *os.File, err error) { // will not choose the same directory. It is the caller's responsibility // to remove the directory when no longer needed. // -// As of Go 1.17, this function simply calls os.MkdirTemp. +// Deprecated: As of Go 1.17, this function simply calls os.MkdirTemp. func TempDir(dir, pattern string) (name string, err error) { return os.MkdirTemp(dir, pattern) } diff --git a/src/io/ioutil/tempfile_test.go b/src/io/ioutil/tempfile_test.go index 5cef18c33b0c1e..818fcdadf8a87b 100644 --- a/src/io/ioutil/tempfile_test.go +++ b/src/io/ioutil/tempfile_test.go @@ -155,7 +155,7 @@ func TestTempDir_BadDir(t *testing.T) { badDir := filepath.Join(dir, "not-exist") _, err = TempDir(badDir, "foo") if pe, ok := err.(*fs.PathError); !ok || !os.IsNotExist(err) || pe.Path != badDir { - t.Errorf("TempDir error = %#v; want PathError for path %q satisifying os.IsNotExist", err, badDir) + t.Errorf("TempDir error = %#v; want PathError for path %q satisfying os.IsNotExist", err, badDir) } } diff --git a/src/io/multi.go b/src/io/multi.go index 24ee71e4ca65b1..07a9afffda3827 100644 --- a/src/io/multi.go +++ b/src/io/multi.go @@ -41,6 +41,31 @@ func (mr *multiReader) Read(p []byte) (n int, err error) { return 0, EOF } +func (mr *multiReader) WriteTo(w Writer) (sum int64, err error) { + return mr.writeToWithBuffer(w, make([]byte, 1024*32)) +} + +func (mr *multiReader) writeToWithBuffer(w Writer, buf []byte) (sum int64, err error) { + for i, r := range mr.readers { + var n int64 + if subMr, ok := r.(*multiReader); ok { // reuse buffer with nested multiReaders + n, err = subMr.writeToWithBuffer(w, buf) + } else { + n, err = copyBuffer(w, r, buf) + } + sum += n + if err != nil { + mr.readers = mr.readers[i:] // permit resume / retry after error + return sum, err + } + mr.readers[i] = nil // permit early GC + } + mr.readers = nil + return sum, nil +} + +var _ WriterTo = (*multiReader)(nil) + // MultiReader returns a Reader that's the logical concatenation of // the provided input readers. They're read sequentially. Once all // inputs have returned EOF, Read will return EOF. If any of the readers diff --git a/src/io/multi_test.go b/src/io/multi_test.go index 909b6d8be28392..7a24a8afc5a419 100644 --- a/src/io/multi_test.go +++ b/src/io/multi_test.go @@ -63,6 +63,31 @@ func TestMultiReader(t *testing.T) { }) } +func TestMultiReaderAsWriterTo(t *testing.T) { + mr := MultiReader( + strings.NewReader("foo "), + MultiReader( // Tickle the buffer reusing codepath + strings.NewReader(""), + strings.NewReader("bar"), + ), + ) + mrAsWriterTo, ok := mr.(WriterTo) + if !ok { + t.Fatalf("expected cast to WriterTo to succeed") + } + sink := &strings.Builder{} + n, err := mrAsWriterTo.WriteTo(sink) + if err != nil { + t.Fatalf("expected no error; got %v", err) + } + if n != 7 { + t.Errorf("expected read 7 bytes; got %d", n) + } + if result := sink.String(); result != "foo bar" { + t.Errorf(`expected "foo bar"; got %q`, result) + } +} + func TestMultiWriter(t *testing.T) { sink := new(bytes.Buffer) // Hide bytes.Buffer's WriteString method: @@ -141,7 +166,7 @@ func testMultiWriter(t *testing.T, sink interface { } } -// writerFunc is an Writer implemented by the underlying func. +// writerFunc is a Writer implemented by the underlying func. type writerFunc func(p []byte) (int, error) func (f writerFunc) Write(p []byte) (int, error) { @@ -203,7 +228,7 @@ func TestMultiReaderCopy(t *testing.T) { // Test that MultiWriter copies the input slice and is insulated from future modification. func TestMultiWriterCopy(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder slice := []Writer{&buf} w := MultiWriter(slice...) slice[0] = nil @@ -216,7 +241,7 @@ func TestMultiWriterCopy(t *testing.T) { } } -// readerFunc is an Reader implemented by the underlying func. +// readerFunc is a Reader implemented by the underlying func. type readerFunc func(p []byte) (int, error) func (f readerFunc) Read(p []byte) (int, error) { diff --git a/src/io/pipe.go b/src/io/pipe.go index b5343bb6b73b50..2724e3f7abe4fd 100644 --- a/src/io/pipe.go +++ b/src/io/pipe.go @@ -47,7 +47,7 @@ type pipe struct { werr onceError } -func (p *pipe) Read(b []byte) (n int, err error) { +func (p *pipe) read(b []byte) (n int, err error) { select { case <-p.done: return 0, p.readCloseError() @@ -64,15 +64,7 @@ func (p *pipe) Read(b []byte) (n int, err error) { } } -func (p *pipe) readCloseError() error { - rerr := p.rerr.Load() - if werr := p.werr.Load(); rerr == nil && werr != nil { - return werr - } - return ErrClosedPipe -} - -func (p *pipe) CloseRead(err error) error { +func (p *pipe) closeRead(err error) error { if err == nil { err = ErrClosedPipe } @@ -81,7 +73,7 @@ func (p *pipe) CloseRead(err error) error { return nil } -func (p *pipe) Write(b []byte) (n int, err error) { +func (p *pipe) write(b []byte) (n int, err error) { select { case <-p.done: return 0, p.writeCloseError() @@ -103,15 +95,7 @@ func (p *pipe) Write(b []byte) (n int, err error) { return n, nil } -func (p *pipe) writeCloseError() error { - werr := p.werr.Load() - if rerr := p.rerr.Load(); werr == nil && rerr != nil { - return rerr - } - return ErrClosedPipe -} - -func (p *pipe) CloseWrite(err error) error { +func (p *pipe) closeWrite(err error) error { if err == nil { err = EOF } @@ -120,6 +104,24 @@ func (p *pipe) CloseWrite(err error) error { return nil } +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return ErrClosedPipe +} + // A PipeReader is the read half of a pipe. type PipeReader struct { p *pipe @@ -131,7 +133,7 @@ type PipeReader struct { // If the write end is closed with an error, that error is // returned as err; otherwise err is EOF. func (r *PipeReader) Read(data []byte) (n int, err error) { - return r.p.Read(data) + return r.p.read(data) } // Close closes the reader; subsequent writes to the @@ -146,7 +148,7 @@ func (r *PipeReader) Close() error { // CloseWithError never overwrites the previous error if it exists // and always returns nil. func (r *PipeReader) CloseWithError(err error) error { - return r.p.CloseRead(err) + return r.p.closeRead(err) } // A PipeWriter is the write half of a pipe. @@ -160,7 +162,7 @@ type PipeWriter struct { // If the read end is closed with an error, that err is // returned as err; otherwise err is ErrClosedPipe. func (w *PipeWriter) Write(data []byte) (n int, err error) { - return w.p.Write(data) + return w.p.write(data) } // Close closes the writer; subsequent reads from the @@ -176,7 +178,7 @@ func (w *PipeWriter) Close() error { // CloseWithError never overwrites the previous error if it exists // and always returns nil. func (w *PipeWriter) CloseWithError(err error) error { - return w.p.CloseWrite(err) + return w.p.closeWrite(err) } // Pipe creates a synchronous in-memory pipe. diff --git a/src/log/log.go b/src/log/log.go index b77af29032927b..566535f25c1852 100644 --- a/src/log/log.go +++ b/src/log/log.go @@ -20,6 +20,7 @@ import ( "os" "runtime" "sync" + "sync/atomic" "time" ) @@ -31,8 +32,11 @@ import ( // The prefix is followed by a colon only when Llongfile or Lshortfile // is specified. // For example, flags Ldate | Ltime (or LstdFlags) produce, +// // 2009/01/23 01:23:23 message +// // while flags Ldate | Ltime | Lmicroseconds | Llongfile produce, +// // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 @@ -50,11 +54,12 @@ const ( // the Writer's Write method. A Logger can be used simultaneously from // multiple goroutines; it guarantees to serialize access to the Writer. type Logger struct { - mu sync.Mutex // ensures atomic writes; protects the following fields - prefix string // prefix on each line to identify the logger (but see Lmsgprefix) - flag int // properties - out io.Writer // destination for output - buf []byte // for accumulating text to write + mu sync.Mutex // ensures atomic writes; protects the following fields + prefix string // prefix on each line to identify the logger (but see Lmsgprefix) + flag int // properties + out io.Writer // destination for output + buf []byte // for accumulating text to write + isDiscard atomic.Bool // whether out == io.Discard } // New creates a new Logger. The out variable sets the @@ -63,7 +68,11 @@ type Logger struct { // after the log header if the Lmsgprefix flag is provided. // The flag argument defines the logging properties. func New(out io.Writer, prefix string, flag int) *Logger { - return &Logger{out: out, prefix: prefix, flag: flag} + l := &Logger{out: out, prefix: prefix, flag: flag} + if out == io.Discard { + l.isDiscard.Store(true) + } + return l } // SetOutput sets the output destination for the logger. @@ -71,6 +80,7 @@ func (l *Logger) SetOutput(w io.Writer) { l.mu.Lock() defer l.mu.Unlock() l.out = w + l.isDiscard.Store(w == io.Discard) } var std = New(os.Stderr, "", LstdFlags) @@ -96,10 +106,10 @@ func itoa(buf *[]byte, i int, wid int) { } // formatHeader writes log header to buf in following order: -// * l.prefix (if it's not blank and Lmsgprefix is unset), -// * date and/or time (if corresponding flags are provided), -// * file and line number (if corresponding flags are provided), -// * l.prefix (if it's not blank and Lmsgprefix is set). +// - l.prefix (if it's not blank and Lmsgprefix is unset), +// - date and/or time (if corresponding flags are provided), +// - file and line number (if corresponding flags are provided), +// - l.prefix (if it's not blank and Lmsgprefix is set). func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) { if l.flag&Lmsgprefix == 0 { *buf = append(*buf, l.prefix...) @@ -187,52 +197,65 @@ func (l *Logger) Output(calldepth int, s string) error { // Printf calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Printf. -func (l *Logger) Printf(format string, v ...interface{}) { +func (l *Logger) Printf(format string, v ...any) { + if l.isDiscard.Load() { + return + } l.Output(2, fmt.Sprintf(format, v...)) } // Print calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Print. -func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) } +func (l *Logger) Print(v ...any) { + if l.isDiscard.Load() { + return + } + l.Output(2, fmt.Sprint(v...)) +} // Println calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Println. -func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) } +func (l *Logger) Println(v ...any) { + if l.isDiscard.Load() { + return + } + l.Output(2, fmt.Sprintln(v...)) +} // Fatal is equivalent to l.Print() followed by a call to os.Exit(1). -func (l *Logger) Fatal(v ...interface{}) { +func (l *Logger) Fatal(v ...any) { l.Output(2, fmt.Sprint(v...)) os.Exit(1) } // Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). -func (l *Logger) Fatalf(format string, v ...interface{}) { +func (l *Logger) Fatalf(format string, v ...any) { l.Output(2, fmt.Sprintf(format, v...)) os.Exit(1) } // Fatalln is equivalent to l.Println() followed by a call to os.Exit(1). -func (l *Logger) Fatalln(v ...interface{}) { +func (l *Logger) Fatalln(v ...any) { l.Output(2, fmt.Sprintln(v...)) os.Exit(1) } // Panic is equivalent to l.Print() followed by a call to panic(). -func (l *Logger) Panic(v ...interface{}) { +func (l *Logger) Panic(v ...any) { s := fmt.Sprint(v...) l.Output(2, s) panic(s) } // Panicf is equivalent to l.Printf() followed by a call to panic(). -func (l *Logger) Panicf(format string, v ...interface{}) { +func (l *Logger) Panicf(format string, v ...any) { s := fmt.Sprintf(format, v...) l.Output(2, s) panic(s) } // Panicln is equivalent to l.Println() followed by a call to panic(). -func (l *Logger) Panicln(v ...interface{}) { +func (l *Logger) Panicln(v ...any) { s := fmt.Sprintln(v...) l.Output(2, s) panic(s) @@ -277,9 +300,7 @@ func (l *Logger) Writer() io.Writer { // SetOutput sets the output destination for the standard logger. func SetOutput(w io.Writer) { - std.mu.Lock() - defer std.mu.Unlock() - std.out = w + std.SetOutput(w) } // Flags returns the output flags for the standard logger. @@ -313,56 +334,65 @@ func Writer() io.Writer { // Print calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Print. -func Print(v ...interface{}) { +func Print(v ...any) { + if std.isDiscard.Load() { + return + } std.Output(2, fmt.Sprint(v...)) } // Printf calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Printf. -func Printf(format string, v ...interface{}) { +func Printf(format string, v ...any) { + if std.isDiscard.Load() { + return + } std.Output(2, fmt.Sprintf(format, v...)) } // Println calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Println. -func Println(v ...interface{}) { +func Println(v ...any) { + if std.isDiscard.Load() { + return + } std.Output(2, fmt.Sprintln(v...)) } // Fatal is equivalent to Print() followed by a call to os.Exit(1). -func Fatal(v ...interface{}) { +func Fatal(v ...any) { std.Output(2, fmt.Sprint(v...)) os.Exit(1) } // Fatalf is equivalent to Printf() followed by a call to os.Exit(1). -func Fatalf(format string, v ...interface{}) { +func Fatalf(format string, v ...any) { std.Output(2, fmt.Sprintf(format, v...)) os.Exit(1) } // Fatalln is equivalent to Println() followed by a call to os.Exit(1). -func Fatalln(v ...interface{}) { +func Fatalln(v ...any) { std.Output(2, fmt.Sprintln(v...)) os.Exit(1) } // Panic is equivalent to Print() followed by a call to panic(). -func Panic(v ...interface{}) { +func Panic(v ...any) { s := fmt.Sprint(v...) std.Output(2, s) panic(s) } // Panicf is equivalent to Printf() followed by a call to panic(). -func Panicf(format string, v ...interface{}) { +func Panicf(format string, v ...any) { s := fmt.Sprintf(format, v...) std.Output(2, s) panic(s) } // Panicln is equivalent to Println() followed by a call to panic(). -func Panicln(v ...interface{}) { +func Panicln(v ...any) { s := fmt.Sprintln(v...) std.Output(2, s) panic(s) diff --git a/src/log/log_test.go b/src/log/log_test.go index 5be8e822586f5b..f2ef165accc976 100644 --- a/src/log/log_test.go +++ b/src/log/log_test.go @@ -9,6 +9,7 @@ package log import ( "bytes" "fmt" + "io" "os" "regexp" "strings" @@ -20,7 +21,7 @@ const ( Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]` Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]` Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]` - Rline = `(60|62):` // must update if the calls to l.Printf / l.Print below move + Rline = `(61|63):` // must update if the calls to l.Printf / l.Print below move Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline ) @@ -52,7 +53,7 @@ var tests = []tester{ // Test using Println("hello", 23, "world") or using Printf("hello %d world", 23) func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) { - buf := new(bytes.Buffer) + buf := new(strings.Builder) SetOutput(buf) SetFlags(flag) SetPrefix(prefix) @@ -89,7 +90,7 @@ func TestAll(t *testing.T) { func TestOutput(t *testing.T) { const testString = "test" - var b bytes.Buffer + var b strings.Builder l := New(&b, "", 0) l.Println(testString) if expect := testString + "\n"; b.String() != expect { @@ -142,7 +143,7 @@ func TestFlagAndPrefixSetting(t *testing.T) { } func TestUTCFlag(t *testing.T) { - var b bytes.Buffer + var b strings.Builder l := New(&b, "Test:", LstdFlags) l.SetFlags(Ldate | Ltime | LUTC) // Verify a log message looks right in the right time zone. Quantize to the second only. @@ -166,7 +167,7 @@ func TestUTCFlag(t *testing.T) { } func TestEmptyPrintCreatesLine(t *testing.T) { - var b bytes.Buffer + var b strings.Builder l := New(&b, "Header:", LstdFlags) l.Print() l.Println("non-empty") @@ -179,6 +180,17 @@ func TestEmptyPrintCreatesLine(t *testing.T) { } } +func TestDiscard(t *testing.T) { + l := New(io.Discard, "", 0) + s := strings.Repeat("a", 102400) + c := testing.AllocsPerRun(100, func() { l.Printf("%s", s) }) + // One allocation for slice passed to Printf, + // but none for formatting of long string. + if c > 1 { + t.Errorf("got %v allocs, want at most 1", c) + } +} + func BenchmarkItoa(b *testing.B) { dst := make([]byte, 0, 64) for i := 0; i < b.N; i++ { diff --git a/src/log/syslog/doc.go b/src/log/syslog/doc.go index bd12bea581f52f..9a33eeb5d57645 100644 --- a/src/log/syslog/doc.go +++ b/src/log/syslog/doc.go @@ -13,7 +13,7 @@ // The syslog package is frozen and is not accepting new features. // Some external packages provide more functionality. See: // -// https://godoc.org/?q=syslog +// https://godoc.org/?q=syslog package syslog // BUG(brainman): This package is not implemented on Windows. As the diff --git a/src/log/syslog/example_test.go b/src/log/syslog/example_test.go index 993976569e2423..4df29c0af0bfe2 100644 --- a/src/log/syslog/example_test.go +++ b/src/log/syslog/example_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package syslog_test diff --git a/src/log/syslog/syslog.go b/src/log/syslog/syslog.go index 6abd4ad124863d..8c6ba72135298c 100644 --- a/src/log/syslog/syslog.go +++ b/src/log/syslog/syslog.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package syslog diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go index 62c62508452a06..de1681d6532328 100644 --- a/src/log/syslog/syslog_test.go +++ b/src/log/syslog/syslog_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 && !js -// +build !windows,!plan9,!js package syslog @@ -11,9 +10,9 @@ import ( "bufio" "fmt" "io" - "log" "net" "os" + "path/filepath" "runtime" "sync" "testing" @@ -82,28 +81,36 @@ func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) { } } -func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) { +func startServer(t *testing.T, n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) { if n == "udp" || n == "tcp" { la = "127.0.0.1:0" } else { - // unix and unixgram: choose an address if none given + // unix and unixgram: choose an address if none given. if la == "" { - // use os.CreateTemp to get a name that is unique - f, err := os.CreateTemp("", "syslogtest") + // The address must be short to fit in the sun_path field of the + // sockaddr_un passed to the underlying system calls, so we use + // os.MkdirTemp instead of t.TempDir: t.TempDir generally includes all or + // part of the test name in the directory, which can be much more verbose + // and risks running up against the limit. + dir, err := os.MkdirTemp("", "") if err != nil { - log.Fatal("TempFile: ", err) + t.Fatal(err) } - f.Close() - la = f.Name() + t.Cleanup(func() { + if err := os.RemoveAll(dir); err != nil { + t.Errorf("failed to remove socket temp directory: %v", err) + } + }) + la = filepath.Join(dir, "sock") } - os.Remove(la) } wg = new(sync.WaitGroup) if n == "udp" || n == "unixgram" { l, e := net.ListenPacket(n, la) if e != nil { - log.Fatalf("startServer failed: %v", e) + t.Helper() + t.Fatalf("startServer failed: %v", e) } addr = l.LocalAddr().String() sock = l @@ -115,7 +122,8 @@ func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, } else { l, e := net.Listen(n, la) if e != nil { - log.Fatalf("startServer failed: %v", e) + t.Helper() + t.Fatalf("startServer failed: %v", e) } addr = l.Addr().String() sock = l @@ -130,32 +138,35 @@ func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, func TestWithSimulated(t *testing.T) { t.Parallel() + msg := "Test 123" - var transport []string - for _, n := range []string{"unix", "unixgram", "udp", "tcp"} { - if testableNetwork(n) { - transport = append(transport, n) + for _, tr := range []string{"unix", "unixgram", "udp", "tcp"} { + if !testableNetwork(tr) { + continue } - } - for _, tr := range transport { - done := make(chan string) - addr, sock, srvWG := startServer(tr, "", done) - defer srvWG.Wait() - defer sock.Close() - if tr == "unix" || tr == "unixgram" { - defer os.Remove(addr) - } - s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test") - if err != nil { - t.Fatalf("Dial() failed: %v", err) - } - err = s.Info(msg) - if err != nil { - t.Fatalf("log failed: %v", err) - } - check(t, msg, <-done, tr) - s.Close() + tr := tr + t.Run(tr, func(t *testing.T) { + t.Parallel() + + done := make(chan string) + addr, sock, srvWG := startServer(t, tr, "", done) + defer srvWG.Wait() + defer sock.Close() + if tr == "unix" || tr == "unixgram" { + defer os.Remove(addr) + } + s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test") + if err != nil { + t.Fatalf("Dial() failed: %v", err) + } + err = s.Info(msg) + if err != nil { + t.Fatalf("log failed: %v", err) + } + check(t, msg, <-done, tr) + s.Close() + }) } } @@ -166,7 +177,7 @@ func TestFlap(t *testing.T) { } done := make(chan string) - addr, sock, srvWG := startServer(net, "", done) + addr, sock, srvWG := startServer(t, net, "", done) defer srvWG.Wait() defer os.Remove(addr) defer sock.Close() @@ -183,7 +194,10 @@ func TestFlap(t *testing.T) { check(t, msg, <-done, net) // restart the server - _, sock2, srvWG2 := startServer(net, addr, done) + if err := os.Remove(addr); err != nil { + t.Fatal(err) + } + _, sock2, srvWG2 := startServer(t, net, addr, done) defer srvWG2.Wait() defer sock2.Close() @@ -283,6 +297,7 @@ func check(t *testing.T, in, out, transport string) { func TestWrite(t *testing.T) { t.Parallel() + tests := []struct { pri Priority pre string @@ -300,7 +315,7 @@ func TestWrite(t *testing.T) { } else { for _, test := range tests { done := make(chan string) - addr, sock, srvWG := startServer("udp", "", done) + addr, sock, srvWG := startServer(t, "udp", "", done) defer srvWG.Wait() defer sock.Close() l, err := Dial("udp", addr, test.pri, test.pre) @@ -324,7 +339,7 @@ func TestWrite(t *testing.T) { } func TestConcurrentWrite(t *testing.T) { - addr, sock, srvWG := startServer("udp", "", make(chan string, 1)) + addr, sock, srvWG := startServer(t, "udp", "", make(chan string, 1)) defer srvWG.Wait() defer sock.Close() w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?") @@ -360,7 +375,7 @@ func TestConcurrentReconnect(t *testing.T) { } } done := make(chan string, N*M) - addr, sock, srvWG := startServer(net, "", done) + addr, sock, srvWG := startServer(t, net, "", done) if net == "unix" { defer os.Remove(addr) } diff --git a/src/log/syslog/syslog_unix.go b/src/log/syslog/syslog_unix.go index 2e45f0764fd9e4..f9cdcdc273493d 100644 --- a/src/log/syslog/syslog_unix.go +++ b/src/log/syslog/syslog_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package syslog diff --git a/src/make.bash b/src/make.bash index 7986125a06ffc6..4616502cec18ce 100755 --- a/src/make.bash +++ b/src/make.bash @@ -41,12 +41,20 @@ # Default is "gcc". Also supported: "clang". # # CC_FOR_TARGET: Command line to run to compile C code for GOARCH. -# This is used by cgo. Default is CC. +# This is used by cgo. Default is CC. +# +# CC_FOR_${GOOS}_${GOARCH}: Command line to run to compile C code for specified ${GOOS} and ${GOARCH}. +# (for example, CC_FOR_linux_arm) +# If this is not set, the build will use CC_FOR_TARGET if appropriate, or CC. # # CXX_FOR_TARGET: Command line to run to compile C++ code for GOARCH. # This is used by cgo. Default is CXX, or, if that is not set, # "g++" or "clang++". # +# CXX_FOR_${GOOS}_${GOARCH}: Command line to run to compile C++ code for specified ${GOOS} and ${GOARCH}. +# (for example, CXX_FOR_linux_arm) +# If this is not set, the build will use CXX_FOR_TARGET if appropriate, or CXX. +# # FC: Command line to run to compile Fortran code for GOARCH. # This is used by cgo. Default is "gfortran". # @@ -59,17 +67,15 @@ # timing information to this file. Useful for profiling where the # time goes when these scripts run. # -# GOROOT_BOOTSTRAP: A working Go tree >= Go 1.4 for bootstrap. +# GOROOT_BOOTSTRAP: A working Go tree >= Go 1.17 for bootstrap. # If $GOROOT_BOOTSTRAP/bin/go is missing, $(go env GOROOT) is -# tried for all "go" in $PATH. $HOME/go1.4 by default. +# tried for all "go" in $PATH. By default, one of $HOME/go1.17, +# $HOME/sdk/go1.17, or $HOME/go1.4, whichever exists, in that order. +# We still check $HOME/go1.4 to allow for build scripts that still hard-code +# that name even though they put newer Go toolchains there. set -e -export GOENV=off -unset GOBIN # Issue 14340 -unset GOFLAGS -unset GO111MODULE - if [ ! -f run.bash ]; then echo 'make.bash must be run from $GOROOT/src' 1>&2 exit 1 @@ -130,15 +136,6 @@ if [ "$(uname -s)" = "GNU/kFreeBSD" ]; then export CGO_ENABLED=0 fi -# Test which linker/loader our system is using, if GO_LDSO is not set. -if [ -z "$GO_LDSO" ] && type readelf >/dev/null 2>&1; then - if echo "int main() { return 0; }" | ${CC:-cc} -o ./test-musl-ldso -x c - >/dev/null 2>&1; then - LDSO=$(readelf -l ./test-musl-ldso | grep 'interpreter:' | sed -e 's/^.*interpreter: \(.*\)[]]/\1/') >/dev/null 2>&1 - [ -z "$LDSO" ] || export GO_LDSO="$LDSO" - rm -f ./test-musl-ldso - fi -fi - # Clean old generated file that will cause problems in the build. rm -f ./runtime/runtime_defs.go @@ -152,36 +149,50 @@ if [ "$1" = "-v" ]; then shift fi -export GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4} +goroot_bootstrap_set=${GOROOT_BOOTSTRAP+"true"} +if [ -z "$GOROOT_BOOTSTRAP" ]; then + GOROOT_BOOTSTRAP="$HOME/go1.4" + for d in sdk/go1.17 go1.17; do + if [ -d "$HOME/$d" ]; then + GOROOT_BOOTSTRAP="$HOME/$d" + fi + done +fi +export GOROOT_BOOTSTRAP + export GOROOT="$(cd .. && pwd)" IFS=$'\n'; for go_exe in $(type -ap go); do if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then goroot=$(GOROOT='' GOOS='' GOARCH='' "$go_exe" env GOROOT) if [ "$goroot" != "$GOROOT" ]; then + if [ "$goroot_bootstrap_set" = "true" ]; then + printf 'WARNING: %s does not exist, found %s from env\n' "$GOROOT_BOOTSTRAP/bin/go" "$go_exe" >&2 + printf 'WARNING: set %s as GOROOT_BOOTSTRAP\n' "$goroot" >&2 + fi GOROOT_BOOTSTRAP=$goroot fi fi done; unset IFS if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2 - echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2 + echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.17." >&2 exit 1 fi # Get the exact bootstrap toolchain version to help with debugging. # We clear GOOS and GOARCH to avoid an ominous but harmless warning if # the bootstrap doesn't support them. -GOROOT_BOOTSTRAP_VERSION=$(GOOS= GOARCH= $GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //') +GOROOT_BOOTSTRAP_VERSION=$(GOOS= GOARCH= GOEXPERIMENT= $GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //') echo "Building Go cmd/dist using $GOROOT_BOOTSTRAP. ($GOROOT_BOOTSTRAP_VERSION)" if $verbose; then echo cmd/dist fi if [ "$GOROOT_BOOTSTRAP" = "$GOROOT" ]; then echo "ERROR: \$GOROOT_BOOTSTRAP must not be set to \$GOROOT" >&2 - echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2 + echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.17." >&2 exit 1 fi rm -f cmd/dist/dist -GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist +GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off GOEXPERIMENT="" GOENV=off GOFLAGS="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist # -e doesn't propagate out of eval, so check success by hand. eval $(./cmd/dist/dist env -p || echo FAIL=true) @@ -205,7 +216,7 @@ fi # Run dist bootstrap to complete make.bash. # Bootstrap installs a proper cmd/dist, built with the new toolchain. -# Throw ours, built with Go 1.4, away after bootstrap. +# Throw ours, built with the bootstrap toolchain, away after bootstrap. ./cmd/dist/dist bootstrap -a $vflag $GO_DISTFLAGS "$@" rm -f ./cmd/dist/dist diff --git a/src/make.bat b/src/make.bat index 8f2825b09a935f..f956dc2064fe58 100644 --- a/src/make.bat +++ b/src/make.bat @@ -46,10 +46,7 @@ if x%4==x--no-local goto nolocal setlocal :nolocal -set GOENV=off set GOBUILDFAIL=0 -set GOFLAGS= -set GO111MODULE= if exist make.bat goto ok echo Must run make.bat from Go src directory. @@ -83,6 +80,8 @@ for /f "tokens=*" %%g in ('where go 2^>nul') do ( ) ) ) +if "x%GOROOT_BOOTSTRAP%"=="x" if exist "%HOMEDRIVE%%HOMEPATH%\go1.17" set GOROOT_BOOTSTRAP=%HOMEDRIVE%%HOMEPATH%\go1.17 +if "x%GOROOT_BOOTSTRAP%"=="x" if exist "%HOMEDRIVE%%HOMEPATH%\sdk\go1.17" set GOROOT_BOOTSTRAP=%HOMEDRIVE%%HOMEPATH%\sdk\go1.17 if "x%GOROOT_BOOTSTRAP%"=="x" set GOROOT_BOOTSTRAP=%HOMEDRIVE%%HOMEPATH%\Go1.4 :bootstrapset @@ -90,20 +89,25 @@ if not exist "%GOROOT_BOOTSTRAP%\bin\go.exe" goto bootstrapfail set GOROOT=%GOROOT_TEMP% set GOROOT_TEMP= -echo Building Go cmd/dist using %GOROOT_BOOTSTRAP% -if x%vflag==x-v echo cmd/dist setlocal -set GOROOT=%GOROOT_BOOTSTRAP% set GOOS= set GOARCH= +set GOEXPERIMENT= +for /f "tokens=*" %%g IN ('"%GOROOT_BOOTSTRAP%\bin\go" version') do (set GOROOT_BOOTSTRAP_VERSION=%%g) +set GOROOT_BOOTSTRAP_VERSION=%GOROOT_BOOTSTRAP_VERSION:go version =% +echo Building Go cmd/dist using %GOROOT_BOOTSTRAP%. (%GOROOT_BOOTSTRAP_VERSION%) +if x%vflag==x-v echo cmd/dist +set GOROOT=%GOROOT_BOOTSTRAP% set GOBIN= set GO111MODULE=off +set GOENV=off +set GOFLAGS= "%GOROOT_BOOTSTRAP%\bin\go.exe" build -o cmd\dist\dist.exe .\cmd\dist endlocal if errorlevel 1 goto fail .\cmd\dist\dist.exe env -w -p >env.bat if errorlevel 1 goto fail -call env.bat +call .\env.bat del env.bat if x%vflag==x-v echo. @@ -124,7 +128,7 @@ if x%4==x--no-banner set bootstrapflags=%bootstrapflags% --no-banner :: Run dist bootstrap to complete make.bash. :: Bootstrap installs a proper cmd/dist, built with the new toolchain. -:: Throw ours, built with Go 1.4, away after bootstrap. +:: Throw ours, built with the bootstrap toolchain, away after bootstrap. .\cmd\dist\dist.exe bootstrap -a %vflag% %bootstrapflags% if errorlevel 1 goto fail del .\cmd\dist\dist.exe @@ -143,7 +147,7 @@ goto end :bootstrapfail echo ERROR: Cannot find %GOROOT_BOOTSTRAP%\bin\go.exe -echo Set GOROOT_BOOTSTRAP to a working Go tree ^>= Go 1.4. +echo Set GOROOT_BOOTSTRAP to a working Go tree ^>= Go 1.17. :fail set GOBUILDFAIL=1 diff --git a/src/make.rc b/src/make.rc index 7bdc7dea1c1eef..04f309b4c534b9 100755 --- a/src/make.rc +++ b/src/make.rc @@ -47,17 +47,24 @@ if(~ $1 -v) { shift } -GOENV=off -GOFLAGS=() -GO111MODULE=() GOROOT = `{cd .. && pwd} -if(! ~ $#GOROOT_BOOTSTRAP 1) +goroot_bootstrap_set = 'true' +if(! ~ $#GOROOT_BOOTSTRAP 1){ + goroot_bootstrap_set = 'false' GOROOT_BOOTSTRAP = $home/go1.4 + for(d in sdk/go1.17 go1.17) + if(test -d $home/$d) + GOROOT_BOOTSTRAP = $home/$d +} for(p in $path){ if(! test -x $GOROOT_BOOTSTRAP/bin/go){ if(go_exe = `{path=$p whatis go}){ goroot = `{GOROOT='' $go_exe env GOROOT} if(! ~ $goroot $GOROOT){ + if(~ $goroot_bootstrap_set 'true'){ + echo 'WARNING: '$GOROOT_BOOTSTRAP'/bin/go does not exist, found '$go_exe' from env' >[1=2] + echo 'WARNING: set '$goroot' as GOROOT_BOOTSTRAP' >[1=2] + } GOROOT_BOOTSTRAP = $goroot } } @@ -65,19 +72,23 @@ for(p in $path){ } if(! test -x $GOROOT_BOOTSTRAP/bin/go){ echo 'ERROR: Cannot find '$GOROOT_BOOTSTRAP'/bin/go.' >[1=2] - echo 'Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.' >[1=2] + echo 'Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.17.' >[1=2] exit bootstrap } if(~ $GOROOT_BOOTSTRAP $GOROOT){ echo 'ERROR: $GOROOT_BOOTSTRAP must not be set to $GOROOT' >[1=2] - echo 'Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.' >[1=2] + echo 'Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.17.' >[1=2] exit bootstrap } -echo 'Building Go cmd/dist using '^$GOROOT_BOOTSTRAP +# Get the exact bootstrap toolchain version to help with debugging. +# We clear GOOS and GOARCH to avoid an ominous but harmless warning if +# the bootstrap doesn't support them. +GOROOT_BOOTSTRAP_VERSION=`{GOOS='' GOARCH='' GOEXPERIMENT='' $GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //'} +echo 'Building Go cmd/dist using '$GOROOT_BOOTSTRAP'. ('$"GOROOT_BOOTSTRAP_VERSION')' if(~ $#vflag 1) echo cmd/dist -GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GO111MODULE=off $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist +GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GOEXPERIMENT='' GO111MODULE=off GOENV=off GOFLAGS='' $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist eval `{./cmd/dist/dist env -9} if(~ $#vflag 1) @@ -94,7 +105,7 @@ if(~ $1 --dist-tool){ # Run dist bootstrap to complete make.bash. # Bootstrap installs a proper cmd/dist, built with the new toolchain. -# Throw ours, built with Go 1.4, away after bootstrap. +# Throw ours, built with the bootstrap toolchain, away after bootstrap. ./cmd/dist/dist bootstrap -a $vflag $* rm -f ./cmd/dist/dist diff --git a/src/math/abs.go b/src/math/abs.go index df83add69561d7..08be14548dd778 100644 --- a/src/math/abs.go +++ b/src/math/abs.go @@ -7,6 +7,7 @@ package math // Abs returns the absolute value of x. // // Special cases are: +// // Abs(±Inf) = +Inf // Abs(NaN) = NaN func Abs(x float64) float64 { diff --git a/src/math/acosh.go b/src/math/acosh.go index f74e0b62fbaa69..a85d003d3eabe7 100644 --- a/src/math/acosh.go +++ b/src/math/acosh.go @@ -36,6 +36,7 @@ package math // Acosh returns the inverse hyperbolic cosine of x. // // Special cases are: +// // Acosh(+Inf) = +Inf // Acosh(x) = NaN if x < 1 // Acosh(NaN) = NaN diff --git a/src/math/all_test.go b/src/math/all_test.go index 55c805e199ed9c..8d5e0ad4391c1e 100644 --- a/src/math/all_test.go +++ b/src/math/all_test.go @@ -1631,8 +1631,8 @@ var vfpowSC = [][2]float64{ {-1, Inf(-1)}, {-1, Inf(1)}, {-1, NaN()}, - {-1 / 2, Inf(-1)}, - {-1 / 2, Inf(1)}, + {-0.5, Inf(-1)}, + {-0.5, Inf(1)}, {Copysign(0, -1), Inf(-1)}, {Copysign(0, -1), -Pi}, {Copysign(0, -1), -0.5}, @@ -1652,8 +1652,8 @@ var vfpowSC = [][2]float64{ {0, Inf(1)}, {0, NaN()}, - {1 / 2, Inf(-1)}, - {1 / 2, Inf(1)}, + {0.5, Inf(-1)}, + {0.5, Inf(1)}, {1, Inf(-1)}, {1, Inf(1)}, {1, NaN()}, @@ -1681,8 +1681,8 @@ var vfpowSC = [][2]float64{ {2, float64(1 << 32)}, {2, -float64(1 << 32)}, {-2, float64(1<<32 + 1)}, - {1 / 2, float64(1 << 45)}, - {1 / 2, -float64(1 << 45)}, + {0.5, float64(1 << 45)}, + {0.5, -float64(1 << 45)}, {Nextafter(1, 2), float64(1 << 63)}, {Nextafter(1, -2), float64(1 << 63)}, {Nextafter(-1, 2), float64(1 << 63)}, @@ -3175,7 +3175,7 @@ func TestTrigReduce(t *testing.T) { // https://golang.org/issue/201 type floatTest struct { - val interface{} + val any name string str string } diff --git a/src/math/asin.go b/src/math/asin.go index 989a74155be289..8e1b2ab4916ed1 100644 --- a/src/math/asin.go +++ b/src/math/asin.go @@ -14,6 +14,7 @@ package math // Asin returns the arcsine, in radians, of x. // // Special cases are: +// // Asin(±0) = ±0 // Asin(x) = NaN if x < -1 or x > 1 func Asin(x float64) float64 { @@ -52,6 +53,7 @@ func asin(x float64) float64 { // Acos returns the arccosine, in radians, of x. // // Special case is: +// // Acos(x) = NaN if x < -1 or x > 1 func Acos(x float64) float64 { if haveArchAcos { diff --git a/src/math/asinh.go b/src/math/asinh.go index 6dcb241c1fefa1..6f6e9e460828d0 100644 --- a/src/math/asinh.go +++ b/src/math/asinh.go @@ -33,6 +33,7 @@ package math // Asinh returns the inverse hyperbolic sine of x. // // Special cases are: +// // Asinh(±0) = ±0 // Asinh(±Inf) = ±Inf // Asinh(NaN) = NaN diff --git a/src/math/atan.go b/src/math/atan.go index 69af8601614d44..e722e99757fd20 100644 --- a/src/math/atan.go +++ b/src/math/atan.go @@ -90,8 +90,9 @@ func satan(x float64) float64 { // Atan returns the arctangent, in radians, of x. // // Special cases are: -// Atan(±0) = ±0 -// Atan(±Inf) = ±Pi/2 +// +// Atan(±0) = ±0 +// Atan(±Inf) = ±Pi/2 func Atan(x float64) float64 { if haveArchAtan { return archAtan(x) diff --git a/src/math/atan2.go b/src/math/atan2.go index 11d7e81acdf93b..c324ed0a1578a2 100644 --- a/src/math/atan2.go +++ b/src/math/atan2.go @@ -9,6 +9,7 @@ package math // of the return value. // // Special cases are (in order): +// // Atan2(y, NaN) = NaN // Atan2(NaN, x) = NaN // Atan2(+0, x>=0) = +0 diff --git a/src/math/atanh.go b/src/math/atanh.go index fe8bd6d8a43357..9d594625a5c04a 100644 --- a/src/math/atanh.go +++ b/src/math/atanh.go @@ -39,6 +39,7 @@ package math // Atanh returns the inverse hyperbolic tangent of x. // // Special cases are: +// // Atanh(1) = +Inf // Atanh(±0) = ±0 // Atanh(-1) = -Inf diff --git a/src/math/big/alias_test.go b/src/math/big/alias_test.go new file mode 100644 index 00000000000000..36c37fb0656e1d --- /dev/null +++ b/src/math/big/alias_test.go @@ -0,0 +1,312 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package big_test + +import ( + cryptorand "crypto/rand" + "math/big" + "math/rand" + "reflect" + "testing" + "testing/quick" +) + +func equal(z, x *big.Int) bool { + return z.Cmp(x) == 0 +} + +type bigInt struct { + *big.Int +} + +func generatePositiveInt(rand *rand.Rand, size int) *big.Int { + n := big.NewInt(1) + n.Lsh(n, uint(rand.Intn(size*8))) + n.Rand(rand, n) + return n +} + +func (bigInt) Generate(rand *rand.Rand, size int) reflect.Value { + n := generatePositiveInt(rand, size) + if rand.Intn(4) == 0 { + n.Neg(n) + } + return reflect.ValueOf(bigInt{n}) +} + +type notZeroInt struct { + *big.Int +} + +func (notZeroInt) Generate(rand *rand.Rand, size int) reflect.Value { + n := generatePositiveInt(rand, size) + if rand.Intn(4) == 0 { + n.Neg(n) + } + if n.Sign() == 0 { + n.SetInt64(1) + } + return reflect.ValueOf(notZeroInt{n}) +} + +type positiveInt struct { + *big.Int +} + +func (positiveInt) Generate(rand *rand.Rand, size int) reflect.Value { + n := generatePositiveInt(rand, size) + return reflect.ValueOf(positiveInt{n}) +} + +type prime struct { + *big.Int +} + +func (prime) Generate(r *rand.Rand, size int) reflect.Value { + n, err := cryptorand.Prime(r, r.Intn(size*8-2)+2) + if err != nil { + panic(err) + } + return reflect.ValueOf(prime{n}) +} + +type zeroOrOne struct { + uint +} + +func (zeroOrOne) Generate(rand *rand.Rand, size int) reflect.Value { + return reflect.ValueOf(zeroOrOne{uint(rand.Intn(2))}) +} + +type smallUint struct { + uint +} + +func (smallUint) Generate(rand *rand.Rand, size int) reflect.Value { + return reflect.ValueOf(smallUint{uint(rand.Intn(1024))}) +} + +// checkAliasingOneArg checks if f returns a correct result when v and x alias. +// +// f is a function that takes x as an argument, doesn't modify it, sets v to the +// result, and returns v. It is the function signature of unbound methods like +// +// func (v *big.Int) m(x *big.Int) *big.Int +// +// v and x are two random Int values. v is randomized even if it will be +// overwritten to test for improper buffer reuse. +func checkAliasingOneArg(t *testing.T, f func(v, x *big.Int) *big.Int, v, x *big.Int) bool { + x1, v1 := new(big.Int).Set(x), new(big.Int).Set(x) + + // Calculate a reference f(x) without aliasing. + if out := f(v, x); out != v { + return false + } + + // Test aliasing the argument and the receiver. + if out := f(v1, v1); out != v1 || !equal(v1, v) { + t.Logf("f(v, x) != f(x, x)") + return false + } + + // Ensure the arguments was not modified. + return equal(x, x1) +} + +// checkAliasingTwoArgs checks if f returns a correct result when any +// combination of v, x and y alias. +// +// f is a function that takes x and y as arguments, doesn't modify them, sets v +// to the result, and returns v. It is the function signature of unbound methods +// like +// +// func (v *big.Int) m(x, y *big.Int) *big.Int +// +// v, x and y are random Int values. v is randomized even if it will be +// overwritten to test for improper buffer reuse. +func checkAliasingTwoArgs(t *testing.T, f func(v, x, y *big.Int) *big.Int, v, x, y *big.Int) bool { + x1, y1, v1 := new(big.Int).Set(x), new(big.Int).Set(y), new(big.Int).Set(v) + + // Calculate a reference f(x, y) without aliasing. + if out := f(v, x, y); out == nil { + // Certain functions like ModInverse return nil for certain inputs. + // Check that receiver and arguments were unchanged and move on. + return equal(x, x1) && equal(y, y1) && equal(v, v1) + } else if out != v { + return false + } + + // Test aliasing the first argument and the receiver. + v1.Set(x) + if out := f(v1, v1, y); out != v1 || !equal(v1, v) { + t.Logf("f(v, x, y) != f(x, x, y)") + return false + } + // Test aliasing the second argument and the receiver. + v1.Set(y) + if out := f(v1, x, v1); out != v1 || !equal(v1, v) { + t.Logf("f(v, x, y) != f(y, x, y)") + return false + } + + // Calculate a reference f(y, y) without aliasing. + // We use y because it's the one that commonly has restrictions + // like being prime or non-zero. + v1.Set(v) + y2 := new(big.Int).Set(y) + if out := f(v, y, y2); out == nil { + return equal(y, y1) && equal(y2, y1) && equal(v, v1) + } else if out != v { + return false + } + + // Test aliasing the two arguments. + if out := f(v1, y, y); out != v1 || !equal(v1, v) { + t.Logf("f(v, y1, y2) != f(v, y, y)") + return false + } + // Test aliasing the two arguments and the receiver. + v1.Set(y) + if out := f(v1, v1, v1); out != v1 || !equal(v1, v) { + t.Logf("f(v, y1, y2) != f(y, y, y)") + return false + } + + // Ensure the arguments were not modified. + return equal(x, x1) && equal(y, y1) +} + +func TestAliasing(t *testing.T) { + for name, f := range map[string]interface{}{ + "Abs": func(v, x bigInt) bool { + return checkAliasingOneArg(t, (*big.Int).Abs, v.Int, x.Int) + }, + "Add": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Add, v.Int, x.Int, y.Int) + }, + "And": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).And, v.Int, x.Int, y.Int) + }, + "AndNot": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).AndNot, v.Int, x.Int, y.Int) + }, + "Div": func(v, x bigInt, y notZeroInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Div, v.Int, x.Int, y.Int) + }, + "Exp-XY": func(v, x, y bigInt, z notZeroInt) bool { + return checkAliasingTwoArgs(t, func(v, x, y *big.Int) *big.Int { + return v.Exp(x, y, z.Int) + }, v.Int, x.Int, y.Int) + }, + "Exp-XZ": func(v, x, y bigInt, z notZeroInt) bool { + return checkAliasingTwoArgs(t, func(v, x, z *big.Int) *big.Int { + return v.Exp(x, y.Int, z) + }, v.Int, x.Int, z.Int) + }, + "Exp-YZ": func(v, x, y bigInt, z notZeroInt) bool { + return checkAliasingTwoArgs(t, func(v, y, z *big.Int) *big.Int { + return v.Exp(x.Int, y, z) + }, v.Int, y.Int, z.Int) + }, + "GCD": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, func(v, x, y *big.Int) *big.Int { + return v.GCD(nil, nil, x, y) + }, v.Int, x.Int, y.Int) + }, + "GCD-X": func(v, x, y bigInt) bool { + a, b := new(big.Int), new(big.Int) + return checkAliasingTwoArgs(t, func(v, x, y *big.Int) *big.Int { + a.GCD(v, b, x, y) + return v + }, v.Int, x.Int, y.Int) + }, + "GCD-Y": func(v, x, y bigInt) bool { + a, b := new(big.Int), new(big.Int) + return checkAliasingTwoArgs(t, func(v, x, y *big.Int) *big.Int { + a.GCD(b, v, x, y) + return v + }, v.Int, x.Int, y.Int) + }, + "Lsh": func(v, x bigInt, n smallUint) bool { + return checkAliasingOneArg(t, func(v, x *big.Int) *big.Int { + return v.Lsh(x, n.uint) + }, v.Int, x.Int) + }, + "Mod": func(v, x bigInt, y notZeroInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Mod, v.Int, x.Int, y.Int) + }, + "ModInverse": func(v, x bigInt, y notZeroInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).ModInverse, v.Int, x.Int, y.Int) + }, + "ModSqrt": func(v, x bigInt, p prime) bool { + return checkAliasingTwoArgs(t, (*big.Int).ModSqrt, v.Int, x.Int, p.Int) + }, + "Mul": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Mul, v.Int, x.Int, y.Int) + }, + "Neg": func(v, x bigInt) bool { + return checkAliasingOneArg(t, (*big.Int).Neg, v.Int, x.Int) + }, + "Not": func(v, x bigInt) bool { + return checkAliasingOneArg(t, (*big.Int).Not, v.Int, x.Int) + }, + "Or": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Or, v.Int, x.Int, y.Int) + }, + "Quo": func(v, x bigInt, y notZeroInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Quo, v.Int, x.Int, y.Int) + }, + "Rand": func(v, x bigInt, seed int64) bool { + return checkAliasingOneArg(t, func(v, x *big.Int) *big.Int { + rnd := rand.New(rand.NewSource(seed)) + return v.Rand(rnd, x) + }, v.Int, x.Int) + }, + "Rem": func(v, x bigInt, y notZeroInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Rem, v.Int, x.Int, y.Int) + }, + "Rsh": func(v, x bigInt, n smallUint) bool { + return checkAliasingOneArg(t, func(v, x *big.Int) *big.Int { + return v.Rsh(x, n.uint) + }, v.Int, x.Int) + }, + "Set": func(v, x bigInt) bool { + return checkAliasingOneArg(t, (*big.Int).Set, v.Int, x.Int) + }, + "SetBit": func(v, x bigInt, i smallUint, b zeroOrOne) bool { + return checkAliasingOneArg(t, func(v, x *big.Int) *big.Int { + return v.SetBit(x, int(i.uint), b.uint) + }, v.Int, x.Int) + }, + "Sqrt": func(v bigInt, x positiveInt) bool { + return checkAliasingOneArg(t, (*big.Int).Sqrt, v.Int, x.Int) + }, + "Sub": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Sub, v.Int, x.Int, y.Int) + }, + "Xor": func(v, x, y bigInt) bool { + return checkAliasingTwoArgs(t, (*big.Int).Xor, v.Int, x.Int, y.Int) + }, + } { + t.Run(name, func(t *testing.T) { + scale := 1.0 + switch name { + case "ModInverse", "GCD-Y", "GCD-X": + scale /= 5 + case "Rand": + scale /= 10 + case "Exp-XZ", "Exp-XY", "Exp-YZ": + scale /= 50 + case "ModSqrt": + scale /= 500 + } + if err := quick.Check(f, &quick.Config{ + MaxCountScale: scale, + }); err != nil { + t.Error(err) + } + }) + } +} diff --git a/src/math/big/arith.go b/src/math/big/arith.go index 8f55c195d4bf26..06e63e2574f31d 100644 --- a/src/math/big/arith.go +++ b/src/math/big/arith.go @@ -41,7 +41,7 @@ const ( // These operations are used by the vector operations below. // z1<<_W + z0 = x*y -func mulWW_g(x, y Word) (z1, z0 Word) { +func mulWW(x, y Word) (z1, z0 Word) { hi, lo := bits.Mul(uint(x), uint(y)) return Word(hi), Word(lo) } diff --git a/src/math/big/arith_386.s b/src/math/big/arith_386.s index acf2b06665a342..8cf4665f29cd68 100644 --- a/src/math/big/arith_386.s +++ b/src/math/big/arith_386.s @@ -10,15 +10,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),NOSPLIT,$0 - MOVL x+0(FP), AX - MULL y+4(FP) - MOVL DX, z1+8(FP) - MOVL AX, z0+12(FP) - RET - - // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 MOVL z+0(FP), DI diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s index 59be952200f3c4..b1e914c2bdc0d7 100644 --- a/src/math/big/arith_amd64.s +++ b/src/math/big/arith_amd64.s @@ -10,16 +10,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),NOSPLIT,$0 - MOVQ x+0(FP), AX - MULQ y+8(FP) - MOVQ DX, z1+16(FP) - MOVQ AX, z0+24(FP) - RET - - - // The carry bit is saved with SBBQ Rx, Rx: if the carry was set, Rx is -1, otherwise it is 0. // It is restored with ADDQ Rx, Rx: if Rx was -1 the carry is set, otherwise it is cleared. // This is faster than using rotate instructions. @@ -379,7 +369,7 @@ E5: CMPQ BX, R11 // i < n // func addMulVVW(z, x []Word, y Word) (c Word) TEXT ·addMulVVW(SB),NOSPLIT,$0 - CMPB ·support_adx(SB), $1 + CMPB ·support_adx(SB), $1 JEQ adx MOVQ z+0(FP), R10 MOVQ x+24(FP), R8 diff --git a/src/math/big/arith_arm.s b/src/math/big/arith_arm.s index f2872d80a1ad05..10054bde474327 100644 --- a/src/math/big/arith_arm.s +++ b/src/math/big/arith_arm.s @@ -271,14 +271,3 @@ E9: MOVW R4, c+28(FP) RET - - - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),NOSPLIT,$0 - MOVW x+0(FP), R1 - MOVW y+4(FP), R2 - MULLU R1, R2, (R4, R3) - MOVW R4, z1+8(FP) - MOVW R3, z0+12(FP) - RET diff --git a/src/math/big/arith_arm64.s b/src/math/big/arith_arm64.s index 7bfe08e7b7c3de..addf2d64a1886a 100644 --- a/src/math/big/arith_arm64.s +++ b/src/math/big/arith_arm64.s @@ -13,17 +13,6 @@ // TODO: Consider re-implementing using Advanced SIMD // once the assembler supports those instructions. -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),NOSPLIT,$0 - MOVD x+0(FP), R0 - MOVD y+8(FP), R1 - MUL R0, R1, R2 - UMULH R0, R1, R3 - MOVD R3, z1+16(FP) - MOVD R2, z0+24(FP) - RET - - // func addVV(z, x, y []Word) (c Word) TEXT ·addVV(SB),NOSPLIT,$0 MOVD z_len+8(FP), R0 diff --git a/src/math/big/arith_decl.go b/src/math/big/arith_decl.go index eea3d6b32561b7..9b254f22130fca 100644 --- a/src/math/big/arith_decl.go +++ b/src/math/big/arith_decl.go @@ -8,12 +8,27 @@ package big // implemented in arith_$GOARCH.s -func mulWW(x, y Word) (z1, z0 Word) + +//go:noescape func addVV(z, x, y []Word) (c Word) + +//go:noescape func subVV(z, x, y []Word) (c Word) + +//go:noescape func addVW(z, x []Word, y Word) (c Word) + +//go:noescape func subVW(z, x []Word, y Word) (c Word) + +//go:noescape func shlVU(z, x []Word, s uint) (c Word) + +//go:noescape func shrVU(z, x []Word, s uint) (c Word) + +//go:noescape func mulAddVWW(z, x []Word, y, r Word) (c Word) + +//go:noescape func addMulVVW(z, x []Word, y Word) (c Word) diff --git a/src/math/big/arith_decl_pure.go b/src/math/big/arith_decl_pure.go index 059f6f1325fa53..75f3ed29486ee7 100644 --- a/src/math/big/arith_decl_pure.go +++ b/src/math/big/arith_decl_pure.go @@ -7,10 +7,6 @@ package big -func mulWW(x, y Word) (z1, z0 Word) { - return mulWW_g(x, y) -} - func addVV(z, x, y []Word) (c Word) { return addVV_g(z, x, y) } diff --git a/src/math/big/arith_loong64.s b/src/math/big/arith_loong64.s new file mode 100644 index 00000000000000..0ae30319672823 --- /dev/null +++ b/src/math/big/arith_loong64.s @@ -0,0 +1,34 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,loong64 + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·addVV(SB),NOSPLIT,$0 + JMP ·addVV_g(SB) + +TEXT ·subVV(SB),NOSPLIT,$0 + JMP ·subVV_g(SB) + +TEXT ·addVW(SB),NOSPLIT,$0 + JMP ·addVW_g(SB) + +TEXT ·subVW(SB),NOSPLIT,$0 + JMP ·subVW_g(SB) + +TEXT ·shlVU(SB),NOSPLIT,$0 + JMP ·shlVU_g(SB) + +TEXT ·shrVU(SB),NOSPLIT,$0 + JMP ·shrVU_g(SB) + +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + JMP ·mulAddVWW_g(SB) + +TEXT ·addMulVVW(SB),NOSPLIT,$0 + JMP ·addMulVVW_g(SB) diff --git a/src/math/big/arith_mips64x.s b/src/math/big/arith_mips64x.s index 4b5c502440f5f8..3ee6e27c823100 100644 --- a/src/math/big/arith_mips64x.s +++ b/src/math/big/arith_mips64x.s @@ -11,9 +11,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -TEXT ·mulWW(SB),NOSPLIT,$0 - JMP ·mulWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) diff --git a/src/math/big/arith_mipsx.s b/src/math/big/arith_mipsx.s index e72e6d6377e87f..b1d32821afd40d 100644 --- a/src/math/big/arith_mipsx.s +++ b/src/math/big/arith_mipsx.s @@ -11,9 +11,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -TEXT ·mulWW(SB),NOSPLIT,$0 - JMP ·mulWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) diff --git a/src/math/big/arith_ppc64x.s b/src/math/big/arith_ppc64x.s index 68c62864946129..a83696a0cbf370 100644 --- a/src/math/big/arith_ppc64x.s +++ b/src/math/big/arith_ppc64x.s @@ -11,16 +11,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB), NOSPLIT, $0 - MOVD x+0(FP), R4 - MOVD y+8(FP), R5 - MULHDU R4, R5, R6 - MULLD R4, R5, R7 - MOVD R6, z1+16(FP) - MOVD R7, z0+24(FP) - RET - // func addVV(z, y, y []Word) (c Word) // z[i] = x[i] + y[i] for all i, carrying TEXT ·addVV(SB), NOSPLIT, $0 @@ -346,11 +336,161 @@ done: MOVD R4, c+56(FP) RET +//func shlVU(z, x []Word, s uint) (c Word) TEXT ·shlVU(SB), NOSPLIT, $0 - BR ·shlVU_g(SB) + MOVD z+0(FP), R3 + MOVD x+24(FP), R6 + MOVD s+48(FP), R9 + MOVD z_len+8(FP), R4 + MOVD x_len+32(FP), R7 + CMP R9, R0 // s==0 copy(z,x) + BEQ zeroshift + CMP R4, R0 // len(z)==0 return + BEQ done + + ADD $-1, R4, R5 // len(z)-1 + SUBC R9, $64, R4 // ŝ=_W-s, we skip & by _W-1 as the caller ensures s < _W(64) + SLD $3, R5, R7 + ADD R6, R7, R15 // save starting address &x[len(z)-1] + ADD R3, R7, R16 // save starting address &z[len(z)-1] + MOVD (R6)(R7), R14 + SRD R4, R14, R7 // compute x[len(z)-1]>>ŝ into R7 + CMP R5, R0 // iterate from i=len(z)-1 to 0 + BEQ loopexit // Already at end? + MOVD 0(R15),R10 // x[i] +shloop: + SLD R9, R10, R10 // x[i]<>ŝ + OR R11, R10, R10 + MOVD R10, 0(R16) // z[i-1]=x[i]<>ŝ + MOVD R14, R10 // reuse x[i-1] for next iteration + ADD $-8, R16 // i-- + CMP R15, R6 // &x[i-1]>&x[0]? + BGT shloop +loopexit: + MOVD 0(R6), R4 + SLD R9, R4, R4 + MOVD R4, 0(R3) // z[0]=x[0]<>ŝ into c + RET + +zeroshift: + CMP R6, R0 // x is null, nothing to copy + BEQ done + CMP R6, R3 // if x is same as z, nothing to copy + BEQ done + CMP R7, R4 + ISEL $0, R7, R4, R7 // Take the lower bound of lengths of x,z + SLD $3, R7, R7 + SUB R6, R3, R11 // dest - src + CMPU R11, R7, CR2 // < len? + BLT CR2, backward // there is overlap, copy backwards + MOVD $0, R14 + // shlVU processes backwards, but added a forward copy option + // since its faster on POWER +repeat: + MOVD (R6)(R14), R15 // Copy 8 bytes at a time + MOVD R15, (R3)(R14) + ADD $8, R14 + CMP R14, R7 // More 8 bytes left? + BLT repeat + BR done +backward: + ADD $-8,R7, R14 +repeatback: + MOVD (R6)(R14), R15 // copy x into z backwards + MOVD R15, (R3)(R14) // copy 8 bytes at a time + SUB $8, R14 + CMP R14, $-8 // More 8 bytes left? + BGT repeatback + +done: + MOVD R0, c+56(FP) // c=0 + RET +//func shrVU(z, x []Word, s uint) (c Word) TEXT ·shrVU(SB), NOSPLIT, $0 - BR ·shrVU_g(SB) + MOVD z+0(FP), R3 + MOVD x+24(FP), R6 + MOVD s+48(FP), R9 + MOVD z_len+8(FP), R4 + MOVD x_len+32(FP), R7 + + CMP R9, R0 // s==0, copy(z,x) + BEQ zeroshift + CMP R4, R0 // len(z)==0 return + BEQ done + SUBC R9, $64, R5 // ŝ=_W-s, we skip & by _W-1 as the caller ensures s < _W(64) + + MOVD 0(R6), R7 + SLD R5, R7, R7 // compute x[0]<<ŝ + MOVD $1, R8 // iterate from i=1 to i=3, else jump to scalar loop + CMP R4, $3 + BLT scalar + MTVSRD R9, VS38 // s + VSPLTB $7, V6, V4 + MTVSRD R5, VS39 // ŝ + VSPLTB $7, V7, V2 + ADD $-2, R4, R16 + PCALIGN $16 +loopback: + ADD $-1, R8, R10 + SLD $3, R10 + LXVD2X (R6)(R10), VS32 // load x[i-1], x[i] + SLD $3, R8, R12 + LXVD2X (R6)(R12), VS33 // load x[i], x[i+1] + + VSRD V0, V4, V3 // x[i-1]>>s, x[i]>>s + VSLD V1, V2, V5 // x[i]<<ŝ, x[i+1]<<ŝ + VOR V3, V5, V5 // Or(|) the two registers together + STXVD2X VS37, (R3)(R10) // store into z[i-1] and z[i] + ADD $2, R8 // Done processing 2 entries, i and i+1 + CMP R8, R16 // Are there at least a couple of more entries left? + BLE loopback + CMP R8, R4 // Are we at the last element? + BEQ loopexit +scalar: + ADD $-1, R8, R10 + SLD $3, R10 + MOVD (R6)(R10),R11 + SRD R9, R11, R11 // x[len(z)-2] >> s + SLD $3, R8, R12 + MOVD (R6)(R12), R12 + SLD R5, R12, R12 // x[len(z)-1]<<ŝ + OR R12, R11, R11 // x[len(z)-2]>>s | x[len(z)-1]<<ŝ + MOVD R11, (R3)(R10) // z[len(z)-2]=x[len(z)-2]>>s | x[len(z)-1]<<ŝ +loopexit: + ADD $-1, R4 + SLD $3, R4 + MOVD (R6)(R4), R5 + SRD R9, R5, R5 // x[len(z)-1]>>s + MOVD R5, (R3)(R4) // z[len(z)-1]=x[len(z)-1]>>s + MOVD R7, c+56(FP) // store pre-computed x[0]<<ŝ into c + RET + +zeroshift: + CMP R6, R0 // x is null, nothing to copy + BEQ done + CMP R6, R3 // if x is same as z, nothing to copy + BEQ done + CMP R7, R4 + ISEL $0, R7, R4, R7 // Take the lower bounds of lengths of x, z + SLD $3, R7, R7 + MOVD $0, R14 +repeat: + MOVD (R6)(R14), R15 // copy 8 bytes at a time + MOVD R15, (R3)(R14) // shrVU processes bytes only forwards + ADD $8, R14 + CMP R14, R7 // More 8 bytes left? + BLT repeat +done: + MOVD R0, c+56(FP) + RET // func mulAddVWW(z, x []Word, y, r Word) (c Word) TEXT ·mulAddVWW(SB), NOSPLIT, $0 diff --git a/src/math/big/arith_riscv64.s b/src/math/big/arith_riscv64.s index 2e950ddd0f3732..cb9ac182927b6e 100644 --- a/src/math/big/arith_riscv64.s +++ b/src/math/big/arith_riscv64.s @@ -10,17 +10,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),NOSPLIT,$0 - MOV x+0(FP), X5 - MOV y+8(FP), X6 - MULHU X5, X6, X7 - MUL X5, X6, X8 - MOV X7, z1+16(FP) - MOV X8, z0+24(FP) - RET - - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s index ad822f76b39d1d..aa6590e20a53da 100644 --- a/src/math/big/arith_s390x.s +++ b/src/math/big/arith_s390x.s @@ -10,15 +10,6 @@ // This file provides fast assembly versions for the elementary // arithmetic operations on vectors implemented in arith.go. -TEXT ·mulWW(SB), NOSPLIT, $0 - MOVD x+0(FP), R3 - MOVD y+8(FP), R4 - MULHDU R3, R4 - MOVD R10, z1+16(FP) - MOVD R11, z0+24(FP) - RET - - // DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2, r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 // func addVV(z, x, y []Word) (c Word) diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go index 7b3427f83415ba..e530dd9750601b 100644 --- a/src/math/big/arith_test.go +++ b/src/math/big/arith_test.go @@ -510,7 +510,7 @@ func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { } // TODO(gri) mulAddVWW and divWVW are symmetric operations but -// their signature is not symmetric. Try to unify. +// their signature is not symmetric. Try to unify. type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) type argWVW struct { @@ -558,7 +558,7 @@ var mulWWTests = []struct { func TestMulWW(t *testing.T) { for i, test := range mulWWTests { - q, r := mulWW_g(test.x, test.y) + q, r := mulWW(test.x, test.y) if q != test.q || r != test.r { t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) } diff --git a/src/math/big/arith_wasm.s b/src/math/big/arith_wasm.s index e8605f1e15356d..93eb16d21d8c7e 100644 --- a/src/math/big/arith_wasm.s +++ b/src/math/big/arith_wasm.s @@ -7,9 +7,6 @@ #include "textflag.h" -TEXT ·mulWW(SB),NOSPLIT,$0 - JMP ·mulWW_g(SB) - TEXT ·addVV(SB),NOSPLIT,$0 JMP ·addVV_g(SB) diff --git a/src/math/big/example_rat_test.go b/src/math/big/example_rat_test.go index a97117001c0b5e..dc67430980172b 100644 --- a/src/math/big/example_rat_test.go +++ b/src/math/big/example_rat_test.go @@ -10,10 +10,13 @@ import ( ) // Use the classic continued fraction for e -// e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...] +// +// e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...] +// // i.e., for the nth term, use -// 1 if n mod 3 != 1 -// (n-1)/3 * 2 if n mod 3 == 1 +// +// 1 if n mod 3 != 1 +// (n-1)/3 * 2 if n mod 3 == 1 func recur(n, lim int64) *big.Rat { term := new(big.Rat) if n%3 != 1 { diff --git a/src/math/big/float.go b/src/math/big/float.go index 42050e2c39d76e..84666d817bb569 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -21,7 +21,7 @@ const debugFloat = false // enable for debugging // A nonzero finite Float represents a multi-precision floating point number // -// sign × mantissa × 2**exponent +// sign × mantissa × 2**exponent // // with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp. // A Float may also be zero (+0, -0) or infinite (+Inf, -Inf). @@ -236,7 +236,6 @@ func (x *Float) Acc() Accuracy { // -1 if x < 0 // 0 if x is ±0 // +1 if x > 0 -// func (x *Float) Sign() int { if debugFloat { x.validate() @@ -304,7 +303,9 @@ func (z *Float) setExpAndRound(exp int64, sbit uint) { // SetMantExp sets z to mant × 2**exp and returns z. // The result z has the same precision and rounding mode // as mant. SetMantExp is an inverse of MantExp but does -// not require 0.5 <= |mant| < 1.0. Specifically: +// not require 0.5 <= |mant| < 1.0. Specifically, for a +// given x of type *Float, SetMantExp relates to MantExp +// as follows: // // mant := new(Float) // new(Float).SetMantExp(mant, x.MantExp(mant)).Cmp(x) == 0 @@ -1667,10 +1668,9 @@ func (z *Float) Quo(x, y *Float) *Float { // Cmp compares x and y and returns: // -// -1 if x < y -// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) -// +1 if x > y -// +// -1 if x < y +// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) +// +1 if x > y func (x *Float) Cmp(y *Float) int { if debugFloat { x.validate() @@ -1705,7 +1705,6 @@ func (x *Float) Cmp(y *Float) int { // 0 if x == 0 (signed or unsigned) // +1 if 0 < x < +Inf // +2 if x == +Inf -// func (x *Float) ord() int { var m int switch x.form { diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 57b7df3936f936..3bb51c7dea8622 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -153,7 +153,6 @@ func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) { // for p, q := uint64(0), uint64(1); p < q; p, q = q, q*5 { // fmt.Println(q) // } -// var pow5tab = [...]uint64{ 1, 5, @@ -216,7 +215,7 @@ func (z *Float) pow5(n uint64) *Float { // point number with a mantissa in the given conversion base (the exponent // is always a decimal number), or a string representing an infinite value. // -// For base 0, an underscore character ``_'' may appear between a base +// For base 0, an underscore character “_” may appear between a base // prefix and an adjacent digit, and between successive digits; such // underscores do not change the value of the number, or the returned // digit count. Incorrect placement of underscores is reported as an @@ -230,22 +229,22 @@ func (z *Float) pow5(n uint64) *Float { // If z's precision is 0, it is changed to 64 before rounding takes effect. // The number must be of the form: // -// number = [ sign ] ( float | "inf" | "Inf" ) . -// sign = "+" | "-" . -// float = ( mantissa | prefix pmantissa ) [ exponent ] . -// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] . -// mantissa = digits "." [ digits ] | digits | "." digits . -// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits . -// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits . -// digits = digit { [ "_" ] digit } . -// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// number = [ sign ] ( float | "inf" | "Inf" ) . +// sign = "+" | "-" . +// float = ( mantissa | prefix pmantissa ) [ exponent ] . +// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] . +// mantissa = digits "." [ digits ] | digits | "." digits . +// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits . +// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits . +// digits = digit { [ "_" ] digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . // // The base argument must be 0, 2, 8, 10, or 16. Providing an invalid base // argument will lead to a run-time panic. // // For base 0, the number prefix determines the actual base: A prefix of -// ``0b'' or ``0B'' selects base 2, ``0o'' or ``0O'' selects base 8, and -// ``0x'' or ``0X'' selects base 16. Otherwise, the actual base is 10 and +// “0b” or “0B” selects base 2, “0o” or “0O” selects base 8, and +// “0x” or “0X” selects base 16. Otherwise, the actual base is 10 and // no prefix is accepted. The octal prefix "0" is not supported (a leading // "0" is simply considered a "0"). // @@ -257,7 +256,6 @@ func (z *Float) pow5(n uint64) *Float { // // The returned *Float f is nil and the value of z is valid but not // defined if an error is reported. -// func (z *Float) Parse(s string, base int) (f *Float, b int, err error) { // scan doesn't handle ±Inf if len(s) == 3 && (s == "Inf" || s == "inf") { diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index 3aa68341436fef..a1cc38a4596c5b 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -576,7 +576,7 @@ func TestFloatText(t *testing.T) { func TestFloatFormat(t *testing.T) { for _, test := range []struct { format string - value interface{} // float32, float64, or string (== 512bit *Float) + value any // float32, float64, or string (== 512bit *Float) want string }{ // from fmt/fmt_test.go diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go index d1c1dab069178f..990e085abe8d05 100644 --- a/src/math/big/floatmarsh.go +++ b/src/math/big/floatmarsh.go @@ -8,6 +8,7 @@ package big import ( "encoding/binary" + "errors" "fmt" ) @@ -67,6 +68,9 @@ func (z *Float) GobDecode(buf []byte) error { *z = Float{} return nil } + if len(buf) < 6 { + return errors.New("Float.GobDecode: buffer too small") + } if buf[0] != floatGobVersion { return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0]) @@ -83,6 +87,9 @@ func (z *Float) GobDecode(buf []byte) error { z.prec = binary.BigEndian.Uint32(buf[2:]) if z.form == finite { + if len(buf) < 10 { + return errors.New("Float.GobDecode: buffer too small for finite form float") + } z.exp = int32(binary.BigEndian.Uint32(buf[6:])) z.mant = z.mant.setBytes(buf[10:]) } diff --git a/src/math/big/floatmarsh_test.go b/src/math/big/floatmarsh_test.go index c056d78b8008cd..401f45a51fe7e0 100644 --- a/src/math/big/floatmarsh_test.go +++ b/src/math/big/floatmarsh_test.go @@ -137,3 +137,15 @@ func TestFloatJSONEncoding(t *testing.T) { } } } + +func TestFloatGobDecodeShortBuffer(t *testing.T) { + for _, tc := range [][]byte{ + []byte{0x1, 0x0, 0x0, 0x0}, + []byte{0x1, 0xfa, 0x0, 0x0, 0x0, 0x0}, + } { + err := NewFloat(0).GobDecode(tc) + if err == nil { + t.Error("expected GobDecode to return error for malformed input") + } + } +} diff --git a/src/math/big/int.go b/src/math/big/int.go index 7647346486c4cb..ca4c3561e629fc 100644 --- a/src/math/big/int.go +++ b/src/math/big/int.go @@ -34,7 +34,6 @@ var intOne = &Int{false, natOne} // -1 if x < 0 // 0 if x == 0 // +1 if x > 0 -// func (x *Int) Sign() int { if len(x.abs) == 0 { return 0 @@ -66,7 +65,20 @@ func (z *Int) SetUint64(x uint64) *Int { // NewInt allocates and returns a new Int set to x. func NewInt(x int64) *Int { - return new(Int).SetInt64(x) + // This code is arranged to be inlineable and produce + // zero allocations when inlined. See issue 29951. + u := uint64(x) + if x < 0 { + u = -u + } + var abs []Word + if x == 0 { + } else if _W == 32 && u>>32 != 0 { + abs = []Word{Word(u), Word(u >> 32)} + } else { + abs = []Word{Word(u)} + } + return &Int{neg: x < 0, abs: abs} } // Set sets z to x and returns z. @@ -232,9 +244,8 @@ func (z *Int) Rem(x, y *Int) *Int { // q = x/y with the result truncated to zero // r = x - y*q // -// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) +// (See Daan Leijen, “Division and Modulus for Computer Scientists”.) // See DivMod for Euclidean division and modulus (unlike Go). -// func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign @@ -287,12 +298,11 @@ func (z *Int) Mod(x, y *Int) *Int { // q = x div y such that // m = x - y*q with 0 <= m < |y| // -// (See Raymond T. Boute, ``The Euclidean definition of the functions -// div and mod''. ACM Transactions on Programming Languages and +// (See Raymond T. Boute, “The Euclidean definition of the functions +// div and mod”. ACM Transactions on Programming Languages and // Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. // ACM press.) // See QuoRem for T-division and modulus (like Go). -// func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { y0 := y // save y if z == y || alias(z.abs, y.abs) { @@ -313,10 +323,9 @@ func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { // Cmp compares x and y and returns: // -// -1 if x < y -// 0 if x == y -// +1 if x > y -// +// -1 if x < y +// 0 if x == y +// +1 if x > y func (x *Int) Cmp(y *Int) (r int) { // x cmp y == x cmp y // x cmp (-y) == x @@ -340,10 +349,9 @@ func (x *Int) Cmp(y *Int) (r int) { // CmpAbs compares the absolute values of x and y and returns: // -// -1 if |x| < |y| -// 0 if |x| == |y| -// +1 if |x| > |y| -// +// -1 if |x| < |y| +// 0 if |x| == |y| +// +1 if |x| > |y| func (x *Int) CmpAbs(y *Int) int { return x.abs.cmp(y.abs) } @@ -405,8 +413,8 @@ func (x *Int) IsUint64() bool { // // The base argument must be 0 or a value between 2 and MaxBase. // For base 0, the number prefix determines the actual base: A prefix of -// ``0b'' or ``0B'' selects base 2, ``0'', ``0o'' or ``0O'' selects base 8, -// and ``0x'' or ``0X'' selects base 16. Otherwise, the selected base is 10 +// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8, +// and “0x” or “0X” selects base 16. Otherwise, the selected base is 10 // and no prefix is accepted. // // For bases <= 36, lower and upper case letters are considered the same: @@ -414,13 +422,12 @@ func (x *Int) IsUint64() bool { // For bases > 36, the upper case letters 'A' to 'Z' represent the digit // values 36 to 61. // -// For base 0, an underscore character ``_'' may appear between a base +// For base 0, an underscore character “_” may appear between a base // prefix and an adjacent digit, and between successive digits; such // underscores do not change the value of the number. // Incorrect placement of underscores is reported as an error if there // are no other errors. If base != 0, underscores are not recognized // and act like any other character that is not a valid digit. -// func (z *Int) SetString(s string, base int) (*Int, bool) { return z.setFromScanner(strings.NewReader(s), base) } @@ -503,6 +510,9 @@ func (z *Int) Exp(x, y, m *Int) *Int { var mWords nat if m != nil { + if z == m || alias(z.abs, m.abs) { + m = new(Int).Set(m) + } mWords = m.abs // m.abs may be nil for m == 0 } @@ -562,8 +572,10 @@ func (z *Int) GCD(x, y, a, b *Int) *Int { // lehmerSimulate attempts to simulate several Euclidean update steps // using the leading digits of A and B. It returns u0, u1, v0, v1 // such that A and B can be updated as: -// A = u0*A + v0*B -// B = u1*A + v1*B +// +// A = u0*A + v0*B +// B = u1*A + v1*B +// // Requirements: A >= B and len(B.abs) >= 2 // Since we are calculating with full words to avoid overflow, // we use 'even' to track the sign of the cosequences. @@ -614,8 +626,10 @@ func lehmerSimulate(A, B *Int) (u0, u1, v0, v1 Word, even bool) { } // lehmerUpdate updates the inputs A and B such that: -// A = u0*A + v0*B -// B = u1*A + v1*B +// +// A = u0*A + v0*B +// B = u1*A + v1*B +// // where the signs of u0, u1, v0, v1 are given by even // For even == true: u0, v1 >= 0 && u1, v0 <= 0 // For even == false: u0, v1 <= 0 && u1, v0 >= 0 @@ -792,11 +806,13 @@ func (z *Int) lehmerGCD(x, y, a, b *Int) *Int { // As this uses the math/rand package, it must not be used for // security-sensitive work. Use crypto/rand.Int instead. func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { - z.neg = false + // z.neg is not modified before the if check, because z and n might alias. if n.neg || len(n.abs) == 0 { + z.neg = false z.abs = nil return z } + z.neg = false z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) return z } @@ -804,7 +820,7 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { // ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ // and returns z. If g and n are not relatively prime, g has no multiplicative // inverse in the ring ℤ/nℤ. In this case, z is unchanged and the return value -// is nil. +// is nil. If n == 0, a division-by-zero run-time panic occurs. func (z *Int) ModInverse(g, n *Int) *Int { // GCD expects parameters a and b to be > 0. if n.neg { @@ -837,7 +853,7 @@ func (z *Int) ModInverse(g, n *Int) *Int { // The y argument must be an odd integer. func Jacobi(x, y *Int) int { if len(y.abs) == 0 || y.abs[0]&1 == 0 { - panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y)) + panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y.String())) } // We use the formulation described in chapter 2, section 2.4, @@ -889,9 +905,11 @@ func Jacobi(x, y *Int) int { } // modSqrt3Mod4 uses the identity -// (a^((p+1)/4))^2 mod p -// == u^(p+1) mod p -// == u^2 mod p +// +// (a^((p+1)/4))^2 mod p +// == u^(p+1) mod p +// == u^2 mod p +// // to calculate the square root of any quadratic residue mod p quickly for 3 // mod 4 primes. func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int { @@ -902,9 +920,11 @@ func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int { } // modSqrt5Mod8 uses Atkin's observation that 2 is not a square mod p -// alpha == (2*a)^((p-5)/8) mod p -// beta == 2*a*alpha^2 mod p is a square root of -1 -// b == a*alpha*(beta-1) mod p is a square root of a +// +// alpha == (2*a)^((p-5)/8) mod p +// beta == 2*a*alpha^2 mod p is a square root of -1 +// b == a*alpha*(beta-1) mod p is a square root of a +// // to calculate the square root of any quadratic residue mod p quickly for 5 // mod 8 primes. func (z *Int) modSqrt5Mod8Prime(x, p *Int) *Int { @@ -977,7 +997,7 @@ func (z *Int) modSqrtTonelliShanks(x, p *Int) *Int { // ModSqrt sets z to a square root of x mod p if such a square root exists, and // returns z. The modulus p must be an odd prime. If x is not a square mod p, // ModSqrt leaves z unchanged and returns nil. This function panics if p is -// not an odd integer. +// not an odd integer, its behavior is undefined if p is odd but not prime. func (z *Int) ModSqrt(x, p *Int) *Int { switch Jacobi(x, p) { case -1: diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go index 3c8557323a0324..75831e5215bee1 100644 --- a/src/math/big/int_test.go +++ b/src/math/big/int_test.go @@ -8,6 +8,8 @@ import ( "bytes" "encoding/hex" "fmt" + "internal/testenv" + "math" "math/rand" "strconv" "strings" @@ -92,7 +94,7 @@ func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { t.Errorf("%s%v is not normalized", msg, z) } if (&z).Cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) + t.Errorf("%v %s %v\n\tgot z = %v; want %v", a.x, msg, a.y, &z, a.z) } } @@ -1894,3 +1896,27 @@ func TestFillBytes(t *testing.T) { }) } } + +func TestNewIntMinInt64(t *testing.T) { + // Test for uint64 cast in NewInt. + want := int64(math.MinInt64) + if got := NewInt(want).Int64(); got != want { + t.Fatalf("wanted %d, got %d", want, got) + } +} + +func TestNewIntAllocs(t *testing.T) { + testenv.SkipIfOptimizationOff(t) + for _, n := range []int64{0, 7, -7, 1 << 30, -1 << 30, 1 << 50, -1 << 50} { + x := NewInt(3) + got := testing.AllocsPerRun(100, func() { + // NewInt should inline, and all its allocations + // can happen on the stack. Passing the result of NewInt + // to Add should not cause any of those allocations to escape. + x.Add(x, NewInt(n)) + }) + if got != 0 { + t.Errorf("x.Add(x, NewInt(%d)), wanted 0 allocations, got %f", n, got) + } + } +} diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go index 05672841056ddb..a3a4023caa6974 100644 --- a/src/math/big/intconv.go +++ b/src/math/big/intconv.go @@ -63,7 +63,6 @@ var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter // specification of minimum digits precision, output field // width, space or zero padding, and '-' for left or right // justification. -// func (x *Int) Format(s fmt.State, ch rune) { // determine base var base int @@ -175,10 +174,9 @@ func (x *Int) Format(s fmt.State, ch rune) { // // The base argument must be 0 or a value from 2 through MaxBase. If the base // is 0, the string prefix determines the actual conversion base. A prefix of -// ``0b'' or ``0B'' selects base 2; a ``0'', ``0o'', or ``0O'' prefix selects -// base 8, and a ``0x'' or ``0X'' prefix selects base 16. Otherwise the selected +// “0b” or “0B” selects base 2; a “0”, “0o”, or “0O” prefix selects +// base 8, and a “0x” or “0X” prefix selects base 16. Otherwise the selected // base is 10. -// func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { // determine sign neg, err := scanSign(r) diff --git a/src/math/big/intmarsh.go b/src/math/big/intmarsh.go index c1422e27107206..ce429ffc11e71d 100644 --- a/src/math/big/intmarsh.go +++ b/src/math/big/intmarsh.go @@ -67,7 +67,10 @@ func (z *Int) UnmarshalText(text []byte) error { // MarshalJSON implements the json.Marshaler interface. func (x *Int) MarshalJSON() ([]byte, error) { - return x.MarshalText() + if x == nil { + return []byte("null"), nil + } + return x.abs.itoa(x.neg, 10), nil } // UnmarshalJSON implements the json.Unmarshaler interface. diff --git a/src/math/big/intmarsh_test.go b/src/math/big/intmarsh_test.go index f82956ceaf22ea..8e7d29f9ddc719 100644 --- a/src/math/big/intmarsh_test.go +++ b/src/math/big/intmarsh_test.go @@ -97,6 +97,19 @@ func TestIntJSONEncoding(t *testing.T) { } } +func TestIntJSONEncodingNil(t *testing.T) { + var x *Int + b, err := x.MarshalJSON() + if err != nil { + t.Fatalf("marshaling of nil failed: %s", err) + } + got := string(b) + want := "null" + if got != want { + t.Fatalf("marshaling of nil failed: got %s want %s", got, want) + } +} + func TestIntXMLEncoding(t *testing.T) { for _, test := range encodingTests { for _, sign := range []string{"", "+", "-"} { diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 140c619c8ca711..5cc42b80dc772a 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -22,7 +22,7 @@ import ( // An unsigned integer x of the form // -// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] +// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] // // with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, // with the digits x[i] as the slice elements. @@ -31,7 +31,6 @@ import ( // During arithmetic operations, denormalized values may occur but are // always normalized before returning the final result. The normalized // representation of 0 is the empty or nil slice (length = 0). -// type nat []Word var ( @@ -341,7 +340,7 @@ func karatsuba(z, x, y nat) { karatsuba(p, xd, yd) // save original z2:z0 - // (ok to use upper half of z since we're done recursing) + // (ok to use upper half of z since we're done recurring) r := z[n*4:] copy(r, z[:n*2]) @@ -363,10 +362,11 @@ func karatsuba(z, x, y nat) { } // alias reports whether x and y share the same base array. +// // Note: alias assumes that the capacity of underlying arrays -// is never changed for nat values; i.e. that there are -// no 3-operand slice expressions in this code (or worse, -// reflect-based operations to the same effect). +// is never changed for nat values; i.e. that there are +// no 3-operand slice expressions in this code (or worse, +// reflect-based operations to the same effect). func alias(x, y nat) bool { return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] } diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go index 42d1cccf6f8cf0..21fdab53fd46cc 100644 --- a/src/math/big/natconv.go +++ b/src/math/big/natconv.go @@ -66,7 +66,7 @@ var ( // scan returns the corresponding natural number res, the actual base b, // a digit count, and a read or syntax error err, if any. // -// For base 0, an underscore character ``_'' may appear between a base +// For base 0, an underscore character “_” may appear between a base // prefix and an adjacent digit, and between successive digits; such // underscores do not change the value of the number, or the returned // digit count. Incorrect placement of underscores is reported as an @@ -74,12 +74,12 @@ var ( // not recognized and thus terminate scanning like any other character // that is not a valid radix point or digit. // -// number = mantissa | prefix pmantissa . -// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] . -// mantissa = digits "." [ digits ] | digits | "." digits . -// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits . -// digits = digit { [ "_" ] digit } . -// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// number = mantissa | prefix pmantissa . +// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] . +// mantissa = digits "." [ digits ] | digits | "." digits . +// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits . +// digits = digit { [ "_" ] digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . // // Unless fracOk is set, the base argument must be 0 or a value between // 2 and MaxBase. If fracOk is set, the base argument must be one of @@ -87,8 +87,8 @@ var ( // time panic. // // For base 0, the number prefix determines the actual base: A prefix of -// ``0b'' or ``0B'' selects base 2, ``0o'' or ``0O'' selects base 8, and -// ``0x'' or ``0X'' selects base 16. If fracOk is false, a ``0'' prefix +// “0b” or “0B” selects base 2, “0o” or “0O” selects base 8, and +// “0x” or “0X” selects base 16. If fracOk is false, a “0” prefix // (immediately followed by digits) selects base 8 as well. Otherwise, // the selected base is 10 and no prefix is accepted. // @@ -105,7 +105,6 @@ var ( // parsed. A digit count <= 0 indicates the presence of a period (if fracOk // is set, only), and -count is the number of fractional digits found. // In this case, the actual value of the scanned number is res * b**count. -// func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { // reject invalid bases baseOk := base == 0 || @@ -366,7 +365,6 @@ func (x nat) itoa(neg bool, base int) []byte { // range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and // ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for // specific hardware. -// func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []divisor) { // split larger blocks recursively if table != nil { @@ -436,8 +434,9 @@ func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []diviso // Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion) // Benchmark and configure leafSize using: go test -bench="Leaf" -// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) -// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU +// +// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) +// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU var leafSize int = 8 // number of Word-size binary values treat as a monolithic block type divisor struct { diff --git a/src/math/big/prime.go b/src/math/big/prime.go index d9a5f1ec968384..a06378956a6bfb 100644 --- a/src/math/big/prime.go +++ b/src/math/big/prime.go @@ -141,7 +141,7 @@ NextRandom: // // Jacobsen, "Pseudoprime Statistics, Tables, and Data", http://ntheory.org/pseudoprimes.html. // -// Nicely, "The Baillie-PSW Primality Test", http://www.trnicely.net/misc/bpsw.html. +// Nicely, "The Baillie-PSW Primality Test", https://web.archive.org/web/20191121062007/http://www.trnicely.net/misc/bpsw.html. // (Note that Nicely's definition of the "extra strong" test gives the wrong Jacobi condition, // as pointed out by Jacobsen.) // diff --git a/src/math/big/rat.go b/src/math/big/rat.go index d35cd4cbd10c6f..700a6432659c40 100644 --- a/src/math/big/rat.go +++ b/src/math/big/rat.go @@ -392,7 +392,6 @@ func (z *Rat) Inv(x *Rat) *Rat { // -1 if x < 0 // 0 if x == 0 // +1 if x > 0 -// func (x *Rat) Sign() int { return x.a.Sign() } @@ -418,7 +417,7 @@ func (x *Rat) Num() *Int { // If the result is a reference to x's denominator it // may change if a new value is assigned to x, and vice versa. func (x *Rat) Denom() *Int { - x.b.neg = false // the result is always >= 0 + // Note that x.b.neg is guaranteed false. if len(x.b.abs) == 0 { // Note: If this proves problematic, we could // panic instead and require the Rat to @@ -479,10 +478,9 @@ func (z *Int) scaleDenom(x *Int, f nat) { // Cmp compares x and y and returns: // -// -1 if x < y -// 0 if x == y -// +1 if x > y -// +// -1 if x < y +// 0 if x == y +// +1 if x > y func (x *Rat) Cmp(y *Rat) int { var a, b Int a.scaleDenom(&x.a, y.b.abs) diff --git a/src/math/big/rat_test.go b/src/math/big/rat_test.go index 02569c1b16a33f..d98c89b3578a7a 100644 --- a/src/math/big/rat_test.go +++ b/src/math/big/rat_test.go @@ -726,3 +726,21 @@ func TestIssue34919(t *testing.T) { } } } + +func TestDenomRace(t *testing.T) { + x := NewRat(1, 2) + const N = 3 + c := make(chan bool, N) + for i := 0; i < N; i++ { + go func() { + // Denom (also used by Float.SetRat) used to mutate x unnecessarily, + // provoking race reports when run in the race detector. + x.Denom() + new(Float).SetRat(x) + c <- true + }() + } + for i := 0; i < N; i++ { + <-c + } +} diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go index ac3c8bd11f8a86..794a51d007a70a 100644 --- a/src/math/big/ratconv.go +++ b/src/math/big/ratconv.go @@ -41,16 +41,16 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error { // success. s can be given as a (possibly signed) fraction "a/b", or as a // floating-point number optionally followed by an exponent. // If a fraction is provided, both the dividend and the divisor may be a -// decimal integer or independently use a prefix of ``0b'', ``0'' or ``0o'', -// or ``0x'' (or their upper-case variants) to denote a binary, octal, or +// decimal integer or independently use a prefix of “0b”, “0” or “0o”, +// or “0x” (or their upper-case variants) to denote a binary, octal, or // hexadecimal integer, respectively. The divisor may not be signed. // If a floating-point number is provided, it may be in decimal form or -// use any of the same prefixes as above but for ``0'' to denote a non-decimal -// mantissa. A leading ``0'' is considered a decimal leading 0; it does not +// use any of the same prefixes as above but for “0” to denote a non-decimal +// mantissa. A leading “0” is considered a decimal leading 0; it does not // indicate octal representation in this case. -// An optional base-10 ``e'' or base-2 ``p'' (or their upper-case variants) +// An optional base-10 “e” or base-2 “p” (or their upper-case variants) // exponent may be provided as well, except for hexadecimal floats which -// only accept an (optional) ``p'' exponent (because an ``e'' or ``E'' cannot +// only accept an (optional) “p” exponent (because an “e” or “E” cannot // be distinguished from a mantissa digit). If the exponent's absolute value // is too large, the operation may fail. // The entire string, not just a prefix, must be valid for success. If the @@ -113,7 +113,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { // special-case 0 (see also issue #16176) if len(z.a.abs) == 0 { - return z, true + return z.norm(), true } // len(z.a.abs) > 0 @@ -169,6 +169,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) { n := exp5 if n < 0 { n = -n + if n < 0 { + // This can occur if -n overflows. -(-1 << 63) would become + // -1 << 63, which is still negative. + return nil, false + } } if n > 1e6 { return nil, false // avoid excessively large exponents @@ -200,10 +205,10 @@ func (z *Rat) SetString(s string) (*Rat, bool) { } // scanExponent scans the longest possible prefix of r representing a base 10 -// (``e'', ``E'') or a base 2 (``p'', ``P'') exponent, if any. It returns the +// (“e”, “E”) or a base 2 (“p”, “P”) exponent, if any. It returns the // exponent, the exponent base (10 or 2), or a read or syntax error, if any. // -// If sepOk is set, an underscore character ``_'' may appear between successive +// If sepOk is set, an underscore character “_” may appear between successive // exponent digits; such underscores do not change the value of the exponent. // Incorrect placement of underscores is reported as an error if there are no // other errors. If sepOk is not set, underscores are not recognized and thus diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go index 15d206cb386ab4..45a35608f4b874 100644 --- a/src/math/big/ratconv_test.go +++ b/src/math/big/ratconv_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "math" + "reflect" "strconv" "strings" "testing" @@ -104,6 +105,7 @@ var setStringTests = []StringTest{ {in: "4/3/"}, {in: "4/3."}, {in: "4/"}, + {in: "13e-9223372036854775808"}, // CVE-2022-23772 // valid {"0", "0", true}, @@ -204,6 +206,14 @@ func TestRatSetString(t *testing.T) { } } +func TestRatSetStringZero(t *testing.T) { + got, _ := new(Rat).SetString("0") + want := new(Rat).SetInt64(0) + if !reflect.DeepEqual(got, want) { + t.Errorf("got %#+v, want %#+v", got, want) + } +} + func TestRatScan(t *testing.T) { var buf bytes.Buffer for i, test := range setStringTests { diff --git a/src/math/big/ratmarsh.go b/src/math/big/ratmarsh.go index fbc7b6002d9509..56102e845b779c 100644 --- a/src/math/big/ratmarsh.go +++ b/src/math/big/ratmarsh.go @@ -45,12 +45,18 @@ func (z *Rat) GobDecode(buf []byte) error { *z = Rat{} return nil } + if len(buf) < 5 { + return errors.New("Rat.GobDecode: buffer too small") + } b := buf[0] if b>>1 != ratGobVersion { return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) } const j = 1 + 4 i := j + binary.BigEndian.Uint32(buf[j-4:j]) + if len(buf) < int(i) { + return errors.New("Rat.GobDecode: buffer too small") + } z.a.neg = b&1 != 0 z.a.abs = z.a.abs.setBytes(buf[j:i]) z.b.abs = z.b.abs.setBytes(buf[i:]) diff --git a/src/math/big/ratmarsh_test.go b/src/math/big/ratmarsh_test.go index 351d109f8d849a..55a9878bb871b1 100644 --- a/src/math/big/ratmarsh_test.go +++ b/src/math/big/ratmarsh_test.go @@ -123,3 +123,15 @@ func TestRatXMLEncoding(t *testing.T) { } } } + +func TestRatGobDecodeShortBuffer(t *testing.T) { + for _, tc := range [][]byte{ + []byte{0x2}, + []byte{0x2, 0x0, 0x0, 0x0, 0xff}, + } { + err := NewRat(1, 2).GobDecode(tc) + if err == nil { + t.Error("expected GobDecode to return error for malformed input") + } + } +} diff --git a/src/math/big/sqrt.go b/src/math/big/sqrt.go index 0d50164557c5a2..b4b03743f4da71 100644 --- a/src/math/big/sqrt.go +++ b/src/math/big/sqrt.go @@ -82,7 +82,9 @@ func (z *Float) Sqrt(x *Float) *Float { } // Compute √x (to z.prec precision) by solving -// 1/t² - x = 0 +// +// 1/t² - x = 0 +// // for t (using Newton's method), and then inverting. func (z *Float) sqrtInverse(x *Float) { // let diff --git a/src/math/bits.go b/src/math/bits.go index 77bcdbe1ce649a..c5cb93b15945d4 100644 --- a/src/math/bits.go +++ b/src/math/bits.go @@ -27,10 +27,10 @@ func Inf(sign int) float64 { return Float64frombits(v) } -// NaN returns an IEEE 754 ``not-a-number'' value. +// NaN returns an IEEE 754 “not-a-number” value. func NaN() float64 { return Float64frombits(uvnan) } -// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value. +// IsNaN reports whether f is an IEEE 754 “not-a-number” value. func IsNaN(f float64) (is bool) { // IEEE 754 says that only NaNs satisfy f != f. // To avoid the floating-point hardware, could use: diff --git a/src/math/bits/example_math_test.go b/src/math/bits/example_math_test.go new file mode 100644 index 00000000000000..4bb466f85cd8f0 --- /dev/null +++ b/src/math/bits/example_math_test.go @@ -0,0 +1,202 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits_test + +import ( + "fmt" + "math/bits" +) + +func ExampleAdd32() { + // First number is 33<<32 + 12 + n1 := []uint32{33, 12} + // Second number is 21<<32 + 23 + n2 := []uint32{21, 23} + // Add them together without producing carry. + d1, carry := bits.Add32(n1[1], n2[1], 0) + d0, _ := bits.Add32(n1[0], n2[0], carry) + nsum := []uint32{d0, d1} + fmt.Printf("%v + %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + + // First number is 1<<32 + 2147483648 + n1 = []uint32{1, 0x80000000} + // Second number is 1<<32 + 2147483648 + n2 = []uint32{1, 0x80000000} + // Add them together producing carry. + d1, carry = bits.Add32(n1[1], n2[1], 0) + d0, _ = bits.Add32(n1[0], n2[0], carry) + nsum = []uint32{d0, d1} + fmt.Printf("%v + %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + // Output: + // [33 12] + [21 23] = [54 35] (carry bit was 0) + // [1 2147483648] + [1 2147483648] = [3 0] (carry bit was 1) +} + +func ExampleAdd64() { + // First number is 33<<64 + 12 + n1 := []uint64{33, 12} + // Second number is 21<<64 + 23 + n2 := []uint64{21, 23} + // Add them together without producing carry. + d1, carry := bits.Add64(n1[1], n2[1], 0) + d0, _ := bits.Add64(n1[0], n2[0], carry) + nsum := []uint64{d0, d1} + fmt.Printf("%v + %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + + // First number is 1<<64 + 9223372036854775808 + n1 = []uint64{1, 0x8000000000000000} + // Second number is 1<<64 + 9223372036854775808 + n2 = []uint64{1, 0x8000000000000000} + // Add them together producing carry. + d1, carry = bits.Add64(n1[1], n2[1], 0) + d0, _ = bits.Add64(n1[0], n2[0], carry) + nsum = []uint64{d0, d1} + fmt.Printf("%v + %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + // Output: + // [33 12] + [21 23] = [54 35] (carry bit was 0) + // [1 9223372036854775808] + [1 9223372036854775808] = [3 0] (carry bit was 1) +} + +func ExampleSub32() { + // First number is 33<<32 + 23 + n1 := []uint32{33, 23} + // Second number is 21<<32 + 12 + n2 := []uint32{21, 12} + // Sub them together without producing carry. + d1, carry := bits.Sub32(n1[1], n2[1], 0) + d0, _ := bits.Sub32(n1[0], n2[0], carry) + nsum := []uint32{d0, d1} + fmt.Printf("%v - %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + + // First number is 3<<32 + 2147483647 + n1 = []uint32{3, 0x7fffffff} + // Second number is 1<<32 + 2147483648 + n2 = []uint32{1, 0x80000000} + // Sub them together producing carry. + d1, carry = bits.Sub32(n1[1], n2[1], 0) + d0, _ = bits.Sub32(n1[0], n2[0], carry) + nsum = []uint32{d0, d1} + fmt.Printf("%v - %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + // Output: + // [33 23] - [21 12] = [12 11] (carry bit was 0) + // [3 2147483647] - [1 2147483648] = [1 4294967295] (carry bit was 1) +} + +func ExampleSub64() { + // First number is 33<<64 + 23 + n1 := []uint64{33, 23} + // Second number is 21<<64 + 12 + n2 := []uint64{21, 12} + // Sub them together without producing carry. + d1, carry := bits.Sub64(n1[1], n2[1], 0) + d0, _ := bits.Sub64(n1[0], n2[0], carry) + nsum := []uint64{d0, d1} + fmt.Printf("%v - %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + + // First number is 3<<64 + 9223372036854775807 + n1 = []uint64{3, 0x7fffffffffffffff} + // Second number is 1<<64 + 9223372036854775808 + n2 = []uint64{1, 0x8000000000000000} + // Sub them together producing carry. + d1, carry = bits.Sub64(n1[1], n2[1], 0) + d0, _ = bits.Sub64(n1[0], n2[0], carry) + nsum = []uint64{d0, d1} + fmt.Printf("%v - %v = %v (carry bit was %v)\n", n1, n2, nsum, carry) + // Output: + // [33 23] - [21 12] = [12 11] (carry bit was 0) + // [3 9223372036854775807] - [1 9223372036854775808] = [1 18446744073709551615] (carry bit was 1) +} + +func ExampleMul32() { + // First number is 0<<32 + 12 + n1 := []uint32{0, 12} + // Second number is 0<<32 + 12 + n2 := []uint32{0, 12} + // Multiply them together without producing overflow. + hi, lo := bits.Mul32(n1[1], n2[1]) + nsum := []uint32{hi, lo} + fmt.Printf("%v * %v = %v\n", n1[1], n2[1], nsum) + + // First number is 0<<32 + 2147483648 + n1 = []uint32{0, 0x80000000} + // Second number is 0<<32 + 2 + n2 = []uint32{0, 2} + // Multiply them together producing overflow. + hi, lo = bits.Mul32(n1[1], n2[1]) + nsum = []uint32{hi, lo} + fmt.Printf("%v * %v = %v\n", n1[1], n2[1], nsum) + // Output: + // 12 * 12 = [0 144] + // 2147483648 * 2 = [1 0] +} + +func ExampleMul64() { + // First number is 0<<64 + 12 + n1 := []uint64{0, 12} + // Second number is 0<<64 + 12 + n2 := []uint64{0, 12} + // Multiply them together without producing overflow. + hi, lo := bits.Mul64(n1[1], n2[1]) + nsum := []uint64{hi, lo} + fmt.Printf("%v * %v = %v\n", n1[1], n2[1], nsum) + + // First number is 0<<64 + 9223372036854775808 + n1 = []uint64{0, 0x8000000000000000} + // Second number is 0<<64 + 2 + n2 = []uint64{0, 2} + // Multiply them together producing overflow. + hi, lo = bits.Mul64(n1[1], n2[1]) + nsum = []uint64{hi, lo} + fmt.Printf("%v * %v = %v\n", n1[1], n2[1], nsum) + // Output: + // 12 * 12 = [0 144] + // 9223372036854775808 * 2 = [1 0] +} + +func ExampleDiv32() { + // First number is 0<<32 + 6 + n1 := []uint32{0, 6} + // Second number is 0<<32 + 3 + n2 := []uint32{0, 3} + // Divide them together. + quo, rem := bits.Div32(n1[0], n1[1], n2[1]) + nsum := []uint32{quo, rem} + fmt.Printf("[%v %v] / %v = %v\n", n1[0], n1[1], n2[1], nsum) + + // First number is 2<<32 + 2147483648 + n1 = []uint32{2, 0x80000000} + // Second number is 0<<32 + 2147483648 + n2 = []uint32{0, 0x80000000} + // Divide them together. + quo, rem = bits.Div32(n1[0], n1[1], n2[1]) + nsum = []uint32{quo, rem} + fmt.Printf("[%v %v] / %v = %v\n", n1[0], n1[1], n2[1], nsum) + // Output: + // [0 6] / 3 = [2 0] + // [2 2147483648] / 2147483648 = [5 0] +} + +func ExampleDiv64() { + // First number is 0<<64 + 6 + n1 := []uint64{0, 6} + // Second number is 0<<64 + 3 + n2 := []uint64{0, 3} + // Divide them together. + quo, rem := bits.Div64(n1[0], n1[1], n2[1]) + nsum := []uint64{quo, rem} + fmt.Printf("[%v %v] / %v = %v\n", n1[0], n1[1], n2[1], nsum) + + // First number is 2<<64 + 9223372036854775808 + n1 = []uint64{2, 0x8000000000000000} + // Second number is 0<<64 + 9223372036854775808 + n2 = []uint64{0, 0x8000000000000000} + // Divide them together. + quo, rem = bits.Div64(n1[0], n1[1], n2[1]) + nsum = []uint64{quo, rem} + fmt.Printf("[%v %v] / %v = %v\n", n1[0], n1[1], n2[1], nsum) + // Output: + // [0 6] / 3 = [2 0] + // [2 9223372036854775808] / 9223372036854775808 = [5 0] +} diff --git a/src/math/bits/make_examples.go b/src/math/bits/make_examples.go index ac4004df414903..92e9aabfb52738 100644 --- a/src/math/bits/make_examples.go +++ b/src/math/bits/make_examples.go @@ -37,44 +37,44 @@ func main() { for _, e := range []struct { name string in int - out [4]interface{} - out2 [4]interface{} + out [4]any + out2 [4]any }{ { name: "LeadingZeros", in: 1, - out: [4]interface{}{bits.LeadingZeros8(1), bits.LeadingZeros16(1), bits.LeadingZeros32(1), bits.LeadingZeros64(1)}, + out: [4]any{bits.LeadingZeros8(1), bits.LeadingZeros16(1), bits.LeadingZeros32(1), bits.LeadingZeros64(1)}, }, { name: "TrailingZeros", in: 14, - out: [4]interface{}{bits.TrailingZeros8(14), bits.TrailingZeros16(14), bits.TrailingZeros32(14), bits.TrailingZeros64(14)}, + out: [4]any{bits.TrailingZeros8(14), bits.TrailingZeros16(14), bits.TrailingZeros32(14), bits.TrailingZeros64(14)}, }, { name: "OnesCount", in: 14, - out: [4]interface{}{bits.OnesCount8(14), bits.OnesCount16(14), bits.OnesCount32(14), bits.OnesCount64(14)}, + out: [4]any{bits.OnesCount8(14), bits.OnesCount16(14), bits.OnesCount32(14), bits.OnesCount64(14)}, }, { name: "RotateLeft", in: 15, - out: [4]interface{}{bits.RotateLeft8(15, 2), bits.RotateLeft16(15, 2), bits.RotateLeft32(15, 2), bits.RotateLeft64(15, 2)}, - out2: [4]interface{}{bits.RotateLeft8(15, -2), bits.RotateLeft16(15, -2), bits.RotateLeft32(15, -2), bits.RotateLeft64(15, -2)}, + out: [4]any{bits.RotateLeft8(15, 2), bits.RotateLeft16(15, 2), bits.RotateLeft32(15, 2), bits.RotateLeft64(15, 2)}, + out2: [4]any{bits.RotateLeft8(15, -2), bits.RotateLeft16(15, -2), bits.RotateLeft32(15, -2), bits.RotateLeft64(15, -2)}, }, { name: "Reverse", in: 19, - out: [4]interface{}{bits.Reverse8(19), bits.Reverse16(19), bits.Reverse32(19), bits.Reverse64(19)}, + out: [4]any{bits.Reverse8(19), bits.Reverse16(19), bits.Reverse32(19), bits.Reverse64(19)}, }, { name: "ReverseBytes", in: 15, - out: [4]interface{}{nil, bits.ReverseBytes16(15), bits.ReverseBytes32(15), bits.ReverseBytes64(15)}, + out: [4]any{nil, bits.ReverseBytes16(15), bits.ReverseBytes32(15), bits.ReverseBytes64(15)}, }, { name: "Len", in: 8, - out: [4]interface{}{bits.Len8(8), bits.Len16(8), bits.Len32(8), bits.Len64(8)}, + out: [4]any{bits.Len8(8), bits.Len16(8), bits.Len32(8), bits.Len64(8)}, }, } { for i, size := range []int{8, 16, 32, 64} { diff --git a/src/math/cbrt.go b/src/math/cbrt.go index 45c8ecb3a8cf25..e5e9548cb1f394 100644 --- a/src/math/cbrt.go +++ b/src/math/cbrt.go @@ -19,6 +19,7 @@ package math // Cbrt returns the cube root of x. // // Special cases are: +// // Cbrt(±0) = ±0 // Cbrt(±Inf) = ±Inf // Cbrt(NaN) = NaN diff --git a/src/math/cmplx/huge_test.go b/src/math/cmplx/huge_test.go index 78b42316de59d9..e794cf281abe6d 100644 --- a/src/math/cmplx/huge_test.go +++ b/src/math/cmplx/huge_test.go @@ -6,7 +6,6 @@ // accurate for huge arguments. //go:build !s390x -// +build !s390x package cmplx diff --git a/src/math/cmplx/isnan.go b/src/math/cmplx/isnan.go index d3382c05eefd81..fed442cb483672 100644 --- a/src/math/cmplx/isnan.go +++ b/src/math/cmplx/isnan.go @@ -18,7 +18,7 @@ func IsNaN(x complex128) bool { return false } -// NaN returns a complex ``not-a-number'' value. +// NaN returns a complex “not-a-number” value. func NaN() complex128 { nan := math.NaN() return complex(nan, nan) diff --git a/src/math/cmplx/pow.go b/src/math/cmplx/pow.go index 5a405f8e9607f0..666bba28c5b599 100644 --- a/src/math/cmplx/pow.go +++ b/src/math/cmplx/pow.go @@ -44,6 +44,7 @@ import "math" // Pow returns x**y, the base-x exponential of y. // For generalized compatibility with math.Pow: +// // Pow(0, ±0) returns 1+0i // Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i. func Pow(x, y complex128) complex128 { diff --git a/src/math/copysign.go b/src/math/copysign.go index 719c64b9eba796..3a30afb41367e7 100644 --- a/src/math/copysign.go +++ b/src/math/copysign.go @@ -4,9 +4,9 @@ package math -// Copysign returns a value with the magnitude -// of x and the sign of y. -func Copysign(x, y float64) float64 { - const sign = 1 << 63 - return Float64frombits(Float64bits(x)&^sign | Float64bits(y)&sign) +// Copysign returns a value with the magnitude of f +// and the sign of sign. +func Copysign(f, sign float64) float64 { + const signBit = 1 << 63 + return Float64frombits(Float64bits(f)&^signBit | Float64bits(sign)&signBit) } diff --git a/src/math/dim.go b/src/math/dim.go index 6a857bbe41fc0c..6a286cdc75a46f 100644 --- a/src/math/dim.go +++ b/src/math/dim.go @@ -7,6 +7,7 @@ package math // Dim returns the maximum of x-y or 0. // // Special cases are: +// // Dim(+Inf, +Inf) = NaN // Dim(-Inf, -Inf) = NaN // Dim(x, NaN) = Dim(NaN, x) = NaN @@ -28,6 +29,7 @@ func Dim(x, y float64) float64 { // Max returns the larger of x or y. // // Special cases are: +// // Max(x, +Inf) = Max(+Inf, x) = +Inf // Max(x, NaN) = Max(NaN, x) = NaN // Max(+0, ±0) = Max(±0, +0) = +0 @@ -61,6 +63,7 @@ func max(x, y float64) float64 { // Min returns the smaller of x or y. // // Special cases are: +// // Min(x, -Inf) = Min(-Inf, x) = -Inf // Min(x, NaN) = Min(NaN, x) = NaN // Min(-0, ±0) = Min(±0, -0) = -0 diff --git a/src/math/dim_asm.go b/src/math/dim_asm.go index 9ba742a407a27b..f4adbd0ae5e11c 100644 --- a/src/math/dim_asm.go +++ b/src/math/dim_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || arm64 || riscv64 || s390x -// +build amd64 arm64 riscv64 s390x package math diff --git a/src/math/dim_noasm.go b/src/math/dim_noasm.go index ea46577d8ded7f..5b9e06fed33d03 100644 --- a/src/math/dim_noasm.go +++ b/src/math/dim_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 && !arm64 && !riscv64 && !s390x -// +build !amd64,!arm64,!riscv64,!s390x package math diff --git a/src/math/erf.go b/src/math/erf.go index 4d6fe472f1af94..ba00c7d03edc11 100644 --- a/src/math/erf.go +++ b/src/math/erf.go @@ -182,6 +182,7 @@ const ( // Erf returns the error function of x. // // Special cases are: +// // Erf(+Inf) = 1 // Erf(-Inf) = -1 // Erf(NaN) = NaN @@ -266,6 +267,7 @@ func erf(x float64) float64 { // Erfc returns the complementary error function of x. // // Special cases are: +// // Erfc(+Inf) = 0 // Erfc(-Inf) = 2 // Erfc(NaN) = NaN diff --git a/src/math/erfinv.go b/src/math/erfinv.go index ee423d33e42725..eed0feb42ddeec 100644 --- a/src/math/erfinv.go +++ b/src/math/erfinv.go @@ -69,6 +69,7 @@ const ( // Erfinv returns the inverse error function of x. // // Special cases are: +// // Erfinv(1) = +Inf // Erfinv(-1) = -Inf // Erfinv(x) = NaN if x < -1 or x > 1 @@ -118,6 +119,7 @@ func Erfinv(x float64) float64 { // Erfcinv returns the inverse of Erfc(x). // // Special cases are: +// // Erfcinv(0) = +Inf // Erfcinv(2) = -Inf // Erfcinv(x) = NaN if x < 0 or x > 2 diff --git a/src/math/example_test.go b/src/math/example_test.go index 9fc196796796c9..a26d8cbe97d8b4 100644 --- a/src/math/example_test.go +++ b/src/math/example_test.go @@ -162,6 +162,11 @@ func ExampleLog10() { // Output: 2.0 } +func ExampleRemainder() { + fmt.Printf("%.1f", math.Remainder(100, 30)) + // Output: 10.0 +} + func ExampleMod() { c := math.Mod(7, 4) fmt.Printf("%.1f", c) diff --git a/src/math/exp.go b/src/math/exp.go index d05eb91fb004d4..760795f46feef5 100644 --- a/src/math/exp.go +++ b/src/math/exp.go @@ -7,8 +7,10 @@ package math // Exp returns e**x, the base-e exponential of x. // // Special cases are: +// // Exp(+Inf) = +Inf // Exp(NaN) = NaN +// // Very large values overflow to 0 or +Inf. // Very small values underflow to 1. func Exp(x float64) float64 { diff --git a/src/math/exp2_asm.go b/src/math/exp2_asm.go index 76d258f1252644..c26b2c3fab67c7 100644 --- a/src/math/exp2_asm.go +++ b/src/math/exp2_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 -// +build arm64 package math diff --git a/src/math/exp2_noasm.go b/src/math/exp2_noasm.go index 1b0ac87ebf0ed4..c2b409329f1e1f 100644 --- a/src/math/exp2_noasm.go +++ b/src/math/exp2_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !arm64 -// +build !arm64 package math diff --git a/src/math/exp_amd64.go b/src/math/exp_amd64.go index 654ccce4810350..0f701b1d6d8da1 100644 --- a/src/math/exp_amd64.go +++ b/src/math/exp_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 -// +build amd64 package math diff --git a/src/math/exp_asm.go b/src/math/exp_asm.go index a1673ea1ddbdac..424442845bb07e 100644 --- a/src/math/exp_asm.go +++ b/src/math/exp_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || arm64 || s390x -// +build amd64 arm64 s390x package math diff --git a/src/math/exp_noasm.go b/src/math/exp_noasm.go index b757e6e23033c7..bd3f02412a28a2 100644 --- a/src/math/exp_noasm.go +++ b/src/math/exp_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 && !arm64 && !s390x -// +build !amd64,!arm64,!s390x package math diff --git a/src/math/expm1.go b/src/math/expm1.go index 66d3421661ea10..ff1c82f524143b 100644 --- a/src/math/expm1.go +++ b/src/math/expm1.go @@ -117,9 +117,11 @@ package math // It is more accurate than Exp(x) - 1 when x is near zero. // // Special cases are: +// // Expm1(+Inf) = +Inf // Expm1(-Inf) = -1 // Expm1(NaN) = NaN +// // Very large values overflow to -1 or +Inf. func Expm1(x float64) float64 { if haveArchExpm1 { diff --git a/src/math/floor.go b/src/math/floor.go index 7913a900e37c87..cb5856424b4e76 100644 --- a/src/math/floor.go +++ b/src/math/floor.go @@ -7,6 +7,7 @@ package math // Floor returns the greatest integer value less than or equal to x. // // Special cases are: +// // Floor(±0) = ±0 // Floor(±Inf) = ±Inf // Floor(NaN) = NaN @@ -35,6 +36,7 @@ func floor(x float64) float64 { // Ceil returns the least integer value greater than or equal to x. // // Special cases are: +// // Ceil(±0) = ±0 // Ceil(±Inf) = ±Inf // Ceil(NaN) = NaN @@ -52,6 +54,7 @@ func ceil(x float64) float64 { // Trunc returns the integer value of x. // // Special cases are: +// // Trunc(±0) = ±0 // Trunc(±Inf) = ±Inf // Trunc(NaN) = NaN @@ -73,6 +76,7 @@ func trunc(x float64) float64 { // Round returns the nearest integer, rounding half away from zero. // // Special cases are: +// // Round(±0) = ±0 // Round(±Inf) = ±Inf // Round(NaN) = NaN @@ -110,6 +114,7 @@ func Round(x float64) float64 { // RoundToEven returns the nearest integer, rounding ties to even. // // Special cases are: +// // RoundToEven(±0) = ±0 // RoundToEven(±Inf) = ±Inf // RoundToEven(NaN) = NaN diff --git a/src/math/floor_asm.go b/src/math/floor_asm.go index 1265e5171e9b74..fb419d6da2f6bf 100644 --- a/src/math/floor_asm.go +++ b/src/math/floor_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 || arm64 || ppc64 || ppc64le || s390x || wasm -// +build 386 amd64 arm64 ppc64 ppc64le s390x wasm package math diff --git a/src/math/floor_noasm.go b/src/math/floor_noasm.go index 821af2102007c0..5641c7ea0a49ca 100644 --- a/src/math/floor_noasm.go +++ b/src/math/floor_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !386 && !amd64 && !arm64 && !ppc64 && !ppc64le && !s390x && !wasm -// +build !386,!amd64,!arm64,!ppc64,!ppc64le,!s390x,!wasm package math diff --git a/src/math/frexp.go b/src/math/frexp.go index 3c8a909ed0cf4d..e194947e646af4 100644 --- a/src/math/frexp.go +++ b/src/math/frexp.go @@ -10,6 +10,7 @@ package math // with the absolute value of frac in the interval [½, 1). // // Special cases are: +// // Frexp(±0) = ±0, 0 // Frexp(±Inf) = ±Inf, 0 // Frexp(NaN) = NaN, 0 diff --git a/src/math/gamma.go b/src/math/gamma.go index cc9e869496b114..86c67232580154 100644 --- a/src/math/gamma.go +++ b/src/math/gamma.go @@ -121,6 +121,7 @@ func stirling(x float64) (float64, float64) { // Gamma returns the Gamma function of x. // // Special cases are: +// // Gamma(+Inf) = +Inf // Gamma(+0) = +Inf // Gamma(-0) = -Inf diff --git a/src/math/huge_test.go b/src/math/huge_test.go index ec81a4a31da396..bc28c6ff693a55 100644 --- a/src/math/huge_test.go +++ b/src/math/huge_test.go @@ -6,7 +6,6 @@ // accurate for huge arguments. //go:build !s390x -// +build !s390x package math_test diff --git a/src/math/hypot.go b/src/math/hypot.go index 12af17766d144d..4e79de0e9bc3e6 100644 --- a/src/math/hypot.go +++ b/src/math/hypot.go @@ -12,6 +12,7 @@ package math // unnecessary overflow and underflow. // // Special cases are: +// // Hypot(±Inf, q) = +Inf // Hypot(p, ±Inf) = +Inf // Hypot(NaN, q) = NaN diff --git a/src/math/hypot_asm.go b/src/math/hypot_asm.go index 9435af41cf8a9a..852691037f6f4b 100644 --- a/src/math/hypot_asm.go +++ b/src/math/hypot_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || amd64 -// +build 386 amd64 package math diff --git a/src/math/hypot_noasm.go b/src/math/hypot_noasm.go index bc41b136e56e27..8b64812a1c197e 100644 --- a/src/math/hypot_noasm.go +++ b/src/math/hypot_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !386 && !amd64 -// +build !386,!amd64 package math diff --git a/src/math/j0.go b/src/math/j0.go index cb5f07bca63f54..a311e18d62d4d3 100644 --- a/src/math/j0.go +++ b/src/math/j0.go @@ -70,6 +70,7 @@ package math // J0 returns the order-zero Bessel function of the first kind. // // Special cases are: +// // J0(±Inf) = 0 // J0(0) = 1 // J0(NaN) = NaN @@ -147,6 +148,7 @@ func J0(x float64) float64 { // Y0 returns the order-zero Bessel function of the second kind. // // Special cases are: +// // Y0(+Inf) = 0 // Y0(0) = -Inf // Y0(x < 0) = NaN diff --git a/src/math/j1.go b/src/math/j1.go index 7c7d279730dc63..cc19e75b95002a 100644 --- a/src/math/j1.go +++ b/src/math/j1.go @@ -69,6 +69,7 @@ package math // J1 returns the order-one Bessel function of the first kind. // // Special cases are: +// // J1(±Inf) = 0 // J1(NaN) = NaN func J1(x float64) float64 { @@ -147,6 +148,7 @@ func J1(x float64) float64 { // Y1 returns the order-one Bessel function of the second kind. // // Special cases are: +// // Y1(+Inf) = 0 // Y1(0) = -Inf // Y1(x < 0) = NaN diff --git a/src/math/jn.go b/src/math/jn.go index b1aca8ff6be747..3491692a96ca7f 100644 --- a/src/math/jn.go +++ b/src/math/jn.go @@ -48,6 +48,7 @@ package math // Jn returns the order-n Bessel function of the first kind. // // Special cases are: +// // Jn(n, ±Inf) = 0 // Jn(n, NaN) = NaN func Jn(n int, x float64) float64 { @@ -225,6 +226,7 @@ func Jn(n int, x float64) float64 { // Yn returns the order-n Bessel function of the second kind. // // Special cases are: +// // Yn(n, +Inf) = 0 // Yn(n ≥ 0, 0) = -Inf // Yn(n < 0, 0) = +Inf if n is odd, -Inf if n is even diff --git a/src/math/ldexp.go b/src/math/ldexp.go index 55c82f1e842030..df365c0b1ac3ab 100644 --- a/src/math/ldexp.go +++ b/src/math/ldexp.go @@ -8,6 +8,7 @@ package math // It returns frac × 2**exp. // // Special cases are: +// // Ldexp(±0, exp) = ±0 // Ldexp(±Inf, exp) = ±Inf // Ldexp(NaN, exp) = NaN diff --git a/src/math/lgamma.go b/src/math/lgamma.go index 7af5871744bc00..4058ad6631eb2c 100644 --- a/src/math/lgamma.go +++ b/src/math/lgamma.go @@ -166,6 +166,7 @@ var _lgamW = [...]float64{ // Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x). // // Special cases are: +// // Lgamma(+Inf) = +Inf // Lgamma(0) = +Inf // Lgamma(-integer) = +Inf diff --git a/src/math/log.go b/src/math/log.go index 1b3e306adfcb18..695a545e7f00ec 100644 --- a/src/math/log.go +++ b/src/math/log.go @@ -73,6 +73,7 @@ package math // Log returns the natural logarithm of x. // // Special cases are: +// // Log(+Inf) = +Inf // Log(0) = -Inf // Log(x < 0) = NaN diff --git a/src/math/log1p.go b/src/math/log1p.go index c117f7245db842..3a7b3854a81038 100644 --- a/src/math/log1p.go +++ b/src/math/log1p.go @@ -87,6 +87,7 @@ package math // It is more accurate than Log(1 + x) when x is near zero. // // Special cases are: +// // Log1p(+Inf) = +Inf // Log1p(±0) = ±0 // Log1p(-1) = -Inf diff --git a/src/math/log_asm.go b/src/math/log_asm.go index 4d3b7ee065a6ce..848cce13b289b5 100644 --- a/src/math/log_asm.go +++ b/src/math/log_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 || s390x -// +build amd64 s390x package math diff --git a/src/math/log_stub.go b/src/math/log_stub.go index e1697164364e09..d35992bf37ab69 100644 --- a/src/math/log_stub.go +++ b/src/math/log_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 && !s390x -// +build !amd64,!s390x package math diff --git a/src/math/logb.go b/src/math/logb.go index f2769d4fd75445..04ba3e968e7306 100644 --- a/src/math/logb.go +++ b/src/math/logb.go @@ -7,6 +7,7 @@ package math // Logb returns the binary exponent of x. // // Special cases are: +// // Logb(±Inf) = +Inf // Logb(0) = -Inf // Logb(NaN) = NaN @@ -26,6 +27,7 @@ func Logb(x float64) float64 { // Ilogb returns the binary exponent of x as an integer. // // Special cases are: +// // Ilogb(±Inf) = MaxInt32 // Ilogb(0) = MinInt32 // Ilogb(NaN) = MaxInt32 diff --git a/src/math/mod.go b/src/math/mod.go index 6bc5f288325472..6f24250cfb4612 100644 --- a/src/math/mod.go +++ b/src/math/mod.go @@ -13,6 +13,7 @@ package math // sign agrees with that of x. // // Special cases are: +// // Mod(±Inf, y) = NaN // Mod(NaN, y) = NaN // Mod(x, 0) = NaN diff --git a/src/math/modf.go b/src/math/modf.go index bf08dc65568b7f..613a75fc9a6864 100644 --- a/src/math/modf.go +++ b/src/math/modf.go @@ -8,6 +8,7 @@ package math // that sum to f. Both values have the same sign as f. // // Special cases are: +// // Modf(±Inf) = ±Inf, NaN // Modf(NaN) = NaN, NaN func Modf(f float64) (int float64, frac float64) { diff --git a/src/math/modf_asm.go b/src/math/modf_asm.go index ce431a9d8d41a9..c63be6cf361a5f 100644 --- a/src/math/modf_asm.go +++ b/src/math/modf_asm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 || ppc64 || ppc64le -// +build arm64 ppc64 ppc64le package math diff --git a/src/math/modf_noasm.go b/src/math/modf_noasm.go index 9607a0894b25a1..55c6a7f6e20693 100644 --- a/src/math/modf_noasm.go +++ b/src/math/modf_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !arm64 && !ppc64 && !ppc64le -// +build !arm64,!ppc64,!ppc64le package math diff --git a/src/math/nextafter.go b/src/math/nextafter.go index 9088e4d248a9cf..ec18d542d9c037 100644 --- a/src/math/nextafter.go +++ b/src/math/nextafter.go @@ -7,6 +7,7 @@ package math // Nextafter32 returns the next representable float32 value after x towards y. // // Special cases are: +// // Nextafter32(x, x) = x // Nextafter32(NaN, y) = NaN // Nextafter32(x, NaN) = NaN @@ -29,6 +30,7 @@ func Nextafter32(x, y float32) (r float32) { // Nextafter returns the next representable float64 value after x towards y. // // Special cases are: +// // Nextafter(x, x) = x // Nextafter(NaN, y) = NaN // Nextafter(x, NaN) = NaN diff --git a/src/math/pow.go b/src/math/pow.go index e45a044ae1cc02..3af8c8b649b9b8 100644 --- a/src/math/pow.go +++ b/src/math/pow.go @@ -15,6 +15,7 @@ func isOddInt(x float64) bool { // Pow returns x**y, the base-x exponential of y. // // Special cases are (in order): +// // Pow(x, ±0) = 1 for any x // Pow(1, y) = 1 for any y // Pow(x, 1) = x for any x diff --git a/src/math/pow10.go b/src/math/pow10.go index 1234e208850ac5..c31ad8dbc778fe 100644 --- a/src/math/pow10.go +++ b/src/math/pow10.go @@ -25,6 +25,7 @@ var pow10negtab32 = [...]float64{ // Pow10 returns 10**n, the base-10 exponential of n. // // Special cases are: +// // Pow10(n) = 0 for n < -323 // Pow10(n) = +Inf for n > 308 func Pow10(n int) float64 { diff --git a/src/math/rand/example_test.go b/src/math/rand/example_test.go index 41076135558cbb..f691e39d648fd2 100644 --- a/src/math/rand/example_test.go +++ b/src/math/rand/example_test.go @@ -57,7 +57,7 @@ func Example_rand() { // The tabwriter here helps us generate aligned output. w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) defer w.Flush() - show := func(name string, v1, v2, v3 interface{}) { + show := func(name string, v1, v2, v3 any) { fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", name, v1, v2, v3) } diff --git a/src/math/rand/exp.go b/src/math/rand/exp.go index 5a8d946c0cccd2..c1162c19b68809 100644 --- a/src/math/rand/exp.go +++ b/src/math/rand/exp.go @@ -26,8 +26,7 @@ const ( // To produce a distribution with a different rate parameter, // callers can adjust the output using: // -// sample = ExpFloat64() / desiredRateParameter -// +// sample = ExpFloat64() / desiredRateParameter func (r *Rand) ExpFloat64() float64 { for { j := r.Uint32() diff --git a/src/math/rand/gen_cooked.go b/src/math/rand/gen_cooked.go index 7950e09fd7c3f2..782bb6671d2e93 100644 --- a/src/math/rand/gen_cooked.go +++ b/src/math/rand/gen_cooked.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // This program computes the value of rngCooked in rng.go, // which is used for seeding all instances of rand.Source. diff --git a/src/math/rand/normal.go b/src/math/rand/normal.go index 2c5a7aa99b8917..6654479a0052cb 100644 --- a/src/math/rand/normal.go +++ b/src/math/rand/normal.go @@ -33,8 +33,7 @@ func absInt32(i int32) uint32 { // To produce a different normal distribution, callers can // adjust the output using: // -// sample = NormFloat64() * desiredStdDev + desiredMean -// +// sample = NormFloat64() * desiredStdDev + desiredMean func (r *Rand) NormFloat64() float64 { for { j := int32(r.Uint32()) // Possibly negative diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go index 13f20ca5efb282..bcf2f9c950526e 100644 --- a/src/math/rand/rand.go +++ b/src/math/rand/rand.go @@ -40,7 +40,12 @@ type Source64 interface { // NewSource returns a new pseudo-random Source seeded with the given value. // Unlike the default Source used by top-level functions, this source is not // safe for concurrent use by multiple goroutines. +// The returned Source implements Source64. func NewSource(seed int64) Source { + return newSource64(seed) +} + +func newSource64(seed int64) Source64 { var rng rngSource rng.Seed(seed) return &rng @@ -365,8 +370,7 @@ func Read(p []byte) (n int, err error) { return globalRand.Read(p) } // To produce a different normal distribution, callers can // adjust the output using: // -// sample = NormFloat64() * desiredStdDev + desiredMean -// +// sample = NormFloat64() * desiredStdDev + desiredMean func NormFloat64() float64 { return globalRand.NormFloat64() } // ExpFloat64 returns an exponentially distributed float64 in the range @@ -375,8 +379,7 @@ func NormFloat64() float64 { return globalRand.NormFloat64() } // To produce a distribution with a different rate parameter, // callers can adjust the output using: // -// sample = ExpFloat64() / desiredRateParameter -// +// sample = ExpFloat64() / desiredRateParameter func ExpFloat64() float64 { return globalRand.ExpFloat64() } type lockedSource struct { diff --git a/src/math/rand/regress_test.go b/src/math/rand/regress_test.go index 1f30be85d1ac0f..813098ec9c5a7b 100644 --- a/src/math/rand/regress_test.go +++ b/src/math/rand/regress_test.go @@ -46,7 +46,7 @@ func TestRegress(t *testing.T) { var args []reflect.Value var argstr string if mt.NumIn() == 1 { - var x interface{} + var x any switch mt.In(0).Kind() { default: t.Fatalf("unexpected argument type for r.%s", m.Name) @@ -83,7 +83,7 @@ func TestRegress(t *testing.T) { args = append(args, reflect.ValueOf(x)) } - var out interface{} + var out any out = mv.Call(args)[0].Interface() if m.Name == "Int" || m.Name == "Intn" { out = int64(out.(int)) @@ -120,7 +120,7 @@ func TestRegress(t *testing.T) { } } -var regressGolden = []interface{}{ +var regressGolden = []any{ float64(4.668112973579268), // ExpFloat64() float64(0.1601593871172866), // ExpFloat64() float64(3.0465834105636), // ExpFloat64() diff --git a/src/math/remainder.go b/src/math/remainder.go index bf8bfd5553a7ac..8e99345c59b797 100644 --- a/src/math/remainder.go +++ b/src/math/remainder.go @@ -29,6 +29,7 @@ package math // Remainder returns the IEEE 754 floating-point remainder of x/y. // // Special cases are: +// // Remainder(±Inf, y) = NaN // Remainder(NaN, y) = NaN // Remainder(x, 0) = NaN diff --git a/src/math/sin.go b/src/math/sin.go index d95bb548e8da00..4793d7e7cd63da 100644 --- a/src/math/sin.go +++ b/src/math/sin.go @@ -112,6 +112,7 @@ var _cos = [...]float64{ // Cos returns the cosine of the radian argument x. // // Special cases are: +// // Cos(±Inf) = NaN // Cos(NaN) = NaN func Cos(x float64) float64 { @@ -177,6 +178,7 @@ func cos(x float64) float64 { // Sin returns the sine of the radian argument x. // // Special cases are: +// // Sin(±0) = ±0 // Sin(±Inf) = NaN // Sin(NaN) = NaN diff --git a/src/math/sincos.go b/src/math/sincos.go index 5c5726f6898a73..e3fb96094fa75f 100644 --- a/src/math/sincos.go +++ b/src/math/sincos.go @@ -9,6 +9,7 @@ package math // Sincos returns Sin(x), Cos(x). // // Special cases are: +// // Sincos(±0) = ±0, 1 // Sincos(±Inf) = NaN, NaN // Sincos(NaN) = NaN, NaN diff --git a/src/math/sinh.go b/src/math/sinh.go index 9fe9b4e17a15a2..78b3c299d66893 100644 --- a/src/math/sinh.go +++ b/src/math/sinh.go @@ -19,6 +19,7 @@ package math // Sinh returns the hyperbolic sine of x. // // Special cases are: +// // Sinh(±0) = ±0 // Sinh(±Inf) = ±Inf // Sinh(NaN) = NaN @@ -71,6 +72,7 @@ func sinh(x float64) float64 { // Cosh returns the hyperbolic cosine of x. // // Special cases are: +// // Cosh(±0) = 1 // Cosh(±Inf) = +Inf // Cosh(NaN) = NaN diff --git a/src/math/sinh_s390x.s b/src/math/sinh_s390x.s index 73701f24f12fd8..d684968a3a87e6 100644 --- a/src/math/sinh_s390x.s +++ b/src/math/sinh_s390x.s @@ -56,11 +56,11 @@ GLOBL sinhe9<>+0(SB), RODATA, $8 TEXT ·sinhAsm(SB),NOSPLIT,$0-16 FMOVD x+0(FP), F0 - //specail case Sinh(±0) = ±0 + //special case Sinh(±0) = ±0 FMOVD $(0.0), F1 FCMPU F0, F1 BEQ sinhIsZero - //specail case Sinh(±Inf = ±Inf + //special case Sinh(±Inf) = ±Inf FMOVD $1.797693134862315708145274237317043567981e+308, F1 FCMPU F1, F0 BLEU sinhIsInf diff --git a/src/math/sqrt.go b/src/math/sqrt.go index 903d57d5e0d067..54929ebcaf74c3 100644 --- a/src/math/sqrt.go +++ b/src/math/sqrt.go @@ -85,20 +85,16 @@ package math // Sqrt returns the square root of x. // // Special cases are: +// // Sqrt(+Inf) = +Inf // Sqrt(±0) = ±0 // Sqrt(x < 0) = NaN // Sqrt(NaN) = NaN func Sqrt(x float64) float64 { - if haveArchSqrt { - return archSqrt(x) - } return sqrt(x) } -// Note: Sqrt is implemented in assembly on some systems. -// Others have assembly stubs that jump to func sqrt below. -// On systems where Sqrt is a single instruction, the compiler +// Note: On systems where Sqrt is a single instruction, the compiler // may turn a direct call into a direct use of that instruction instead. func sqrt(x float64) float64 { diff --git a/src/math/sqrt_386.s b/src/math/sqrt_386.s deleted file mode 100644 index 90aec13b8dbdd7..00000000000000 --- a/src/math/sqrt_386.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - FMOVD x+0(FP),F0 - FSQRT - FMOVDP F0,ret+8(FP) - RET diff --git a/src/math/sqrt_amd64.s b/src/math/sqrt_amd64.s deleted file mode 100644 index c3b110e7c093b5..00000000000000 --- a/src/math/sqrt_amd64.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB), NOSPLIT, $0 - XORPS X0, X0 // break dependency - SQRTSD x+0(FP), X0 - MOVSD X0, ret+8(FP) - RET diff --git a/src/math/sqrt_arm.s b/src/math/sqrt_arm.s deleted file mode 100644 index 64792ecaf7e254..00000000000000 --- a/src/math/sqrt_arm.s +++ /dev/null @@ -1,20 +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. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - MOVB runtime·goarm(SB), R11 - CMP $5, R11 - BEQ arm5 - MOVD x+0(FP),F0 - SQRTD F0,F0 - MOVD F0,ret+8(FP) - RET -arm5: - // Tail call to Go implementation. - // Can't use JMP, as in softfloat mode SQRTD is rewritten - // to a CALL, which makes this function have a frame. - RET ·sqrt(SB) diff --git a/src/math/sqrt_arm64.s b/src/math/sqrt_arm64.s deleted file mode 100644 index 36ba41ab4a1098..00000000000000 --- a/src/math/sqrt_arm64.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - FMOVD x+0(FP), F0 - FSQRTD F0, F0 - FMOVD F0, ret+8(FP) - RET diff --git a/src/math/sqrt_asm.go b/src/math/sqrt_asm.go deleted file mode 100644 index b9102568ed51cd..00000000000000 --- a/src/math/sqrt_asm.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build 386 || amd64 || arm64 || arm || mips || mipsle || ppc64 || ppc64le || s390x || riscv64 || wasm -// +build 386 amd64 arm64 arm mips mipsle ppc64 ppc64le s390x riscv64 wasm - -package math - -const haveArchSqrt = true - -func archSqrt(x float64) float64 diff --git a/src/math/sqrt_mipsx.s b/src/math/sqrt_mipsx.s deleted file mode 100644 index 291d4af39cf5b4..00000000000000 --- a/src/math/sqrt_mipsx.s +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build mips || mipsle -// +build mips mipsle - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 -#ifdef GOMIPS_softfloat - JMP ·sqrt(SB) -#else - MOVD x+0(FP), F0 - SQRTD F0, F0 - MOVD F0, ret+8(FP) -#endif - RET diff --git a/src/math/sqrt_noasm.go b/src/math/sqrt_noasm.go deleted file mode 100644 index 7b546b7e8cb2ca..00000000000000 --- a/src/math/sqrt_noasm.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !386 && !amd64 && !arm64 && !arm && !mips && !mipsle && !ppc64 && !ppc64le && !s390x && !riscv64 && !wasm -// +build !386,!amd64,!arm64,!arm,!mips,!mipsle,!ppc64,!ppc64le,!s390x,!riscv64,!wasm - -package math - -const haveArchSqrt = false - -func archSqrt(x float64) float64 { - panic("not implemented") -} diff --git a/src/math/sqrt_ppc64x.s b/src/math/sqrt_ppc64x.s deleted file mode 100644 index c929da2159a24d..00000000000000 --- a/src/math/sqrt_ppc64x.s +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ppc64 || ppc64le -// +build ppc64 ppc64le - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - FMOVD x+0(FP), F0 - FSQRT F0, F0 - FMOVD F0, ret+8(FP) - RET diff --git a/src/math/sqrt_riscv64.s b/src/math/sqrt_riscv64.s deleted file mode 100644 index 0dbdbc99ed66a9..00000000000000 --- a/src/math/sqrt_riscv64.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - MOVD x+0(FP), F0 - FSQRTD F0, F0 - MOVD F0, ret+8(FP) - RET diff --git a/src/math/sqrt_s390x.s b/src/math/sqrt_s390x.s deleted file mode 100644 index fa31f753624460..00000000000000 --- a/src/math/sqrt_s390x.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// func archSqrt(x float64) float64 -TEXT ·archSqrt(SB),NOSPLIT,$0 - FMOVD x+0(FP), F1 - FSQRT F1, F1 - FMOVD F1, ret+8(FP) - RET diff --git a/src/math/sqrt_wasm.s b/src/math/sqrt_wasm.s deleted file mode 100644 index fa6799ddc62400..00000000000000 --- a/src/math/sqrt_wasm.s +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2018 The Go 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 ·archSqrt(SB),NOSPLIT,$0 - Get SP - F64Load x+0(FP) - F64Sqrt - F64Store ret+8(FP) - RET diff --git a/src/math/stubs.go b/src/math/stubs.go index e1345eb841dd25..c4350d4b8758e1 100644 --- a/src/math/stubs.go +++ b/src/math/stubs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !s390x -// +build !s390x // This is a large group of functions that most architectures don't // implement in assembly. diff --git a/src/math/tan.go b/src/math/tan.go index a25417f527dd4a..8f6e71e82b2e13 100644 --- a/src/math/tan.go +++ b/src/math/tan.go @@ -67,15 +67,16 @@ var _tanP = [...]float64{ } var _tanQ = [...]float64{ 1.00000000000000000000e0, - 1.36812963470692954678e4, //0x40cab8a5eeb36572 - -1.32089234440210967447e6, //0xc13427bc582abc96 - 2.50083801823357915839e7, //0x4177d98fc2ead8ef - -5.38695755929454629881e7, //0xc189afe03cbe5a31 + 1.36812963470692954678e4, // 0x40cab8a5eeb36572 + -1.32089234440210967447e6, // 0xc13427bc582abc96 + 2.50083801823357915839e7, // 0x4177d98fc2ead8ef + -5.38695755929454629881e7, // 0xc189afe03cbe5a31 } // Tan returns the tangent of the radian argument x. // // Special cases are: +// // Tan(±0) = ±0 // Tan(±Inf) = NaN // Tan(NaN) = NaN diff --git a/src/math/tan_s390x.s b/src/math/tan_s390x.s index b6e2295874e6ee..82267608b97ea6 100644 --- a/src/math/tan_s390x.s +++ b/src/math/tan_s390x.s @@ -38,7 +38,7 @@ GLOBL ·tanxadd<> + 0(SB), RODATA, $8 TEXT ·tanAsm(SB), NOSPLIT, $0-16 FMOVD x+0(FP), F0 - //specail case Tan(±0) = ±0 + //special case Tan(±0) = ±0 FMOVD $(0.0), F1 FCMPU F0, F1 BEQ atanIsZero diff --git a/src/math/tanh.go b/src/math/tanh.go index a825678424648d..94ebc3b6515d52 100644 --- a/src/math/tanh.go +++ b/src/math/tanh.go @@ -68,6 +68,7 @@ var tanhQ = [...]float64{ // Tanh returns the hyperbolic tangent of x. // // Special cases are: +// // Tanh(±0) = ±0 // Tanh(±Inf) = ±1 // Tanh(NaN) = NaN diff --git a/src/math/trig_reduce.go b/src/math/trig_reduce.go index 5cdf4fa01379e4..5ecdd8375e3295 100644 --- a/src/math/trig_reduce.go +++ b/src/math/trig_reduce.go @@ -14,7 +14,9 @@ import ( // where y is given by y = floor(x * (4 / Pi)) and C is the leading partial // terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30 // and 32 trailing zero bits, y should have less than 30 significant bits. +// // y < 1<<30 -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4 +// // So, conservatively we can take x < 1<<29. // Above this threshold Payne-Hanek range reduction must be used. const reduceThreshold = 1 << 29 diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go index 58f60daec4312c..e6b470b1fb0ef8 100644 --- a/src/mime/encodedword.go +++ b/src/mime/encodedword.go @@ -203,35 +203,25 @@ func (d *WordDecoder) Decode(word string) (string, error) { } word = word[2 : len(word)-2] - // split delimits the first 2 fields - split := strings.IndexByte(word, '?') - - // split word "UTF-8?q?ascii" into "UTF-8", 'q', and "ascii" - charset := word[:split] - if len(charset) == 0 { - return "", errInvalidWord - } - if len(word) < split+3 { + // split word "UTF-8?q?text" into "UTF-8", 'q', and "text" + charset, text, _ := strings.Cut(word, "?") + if charset == "" { return "", errInvalidWord } - encoding := word[split+1] - // the field after split must only be one byte - if word[split+2] != '?' { + encoding, text, _ := strings.Cut(text, "?") + if len(encoding) != 1 { return "", errInvalidWord } - text := word[split+3:] - content, err := decode(encoding, text) + content, err := decode(encoding[0], text) if err != nil { return "", err } var buf strings.Builder - if err := d.convert(&buf, charset, content); err != nil { return "", err } - return buf.String(), nil } diff --git a/src/mime/mediatype.go b/src/mime/mediatype.go index 56ceb488533fc9..bc8d417e62893e 100644 --- a/src/mime/mediatype.go +++ b/src/mime/mediatype.go @@ -19,13 +19,12 @@ import ( // FormatMediaType returns the empty string. func FormatMediaType(t string, param map[string]string) string { var b strings.Builder - if slash := strings.IndexByte(t, '/'); slash == -1 { + if major, sub, ok := strings.Cut(t, "/"); !ok { if !isToken(t) { return "" } b.WriteString(strings.ToLower(t)) } else { - major, sub := t[:slash], t[slash+1:] if !isToken(major) || !isToken(sub) { return "" } @@ -138,11 +137,8 @@ var ErrInvalidMediaParameter = errors.New("mime: invalid media parameter") // The returned map, params, maps from the lowercase // attribute to the attribute value with its case preserved. func ParseMediaType(v string) (mediatype string, params map[string]string, err error) { - i := strings.Index(v, ";") - if i == -1 { - i = len(v) - } - mediatype = strings.TrimSpace(strings.ToLower(v[0:i])) + base, _, _ := strings.Cut(v, ";") + mediatype = strings.TrimSpace(strings.ToLower(base)) err = checkMediaTypeDisposition(mediatype) if err != nil { @@ -156,7 +152,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e // Lazily initialized. var continuation map[string]map[string]string - v = v[i:] + v = v[len(base):] for len(v) > 0 { v = strings.TrimLeftFunc(v, unicode.IsSpace) if len(v) == 0 { @@ -167,15 +163,14 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e if strings.TrimSpace(rest) == ";" { // Ignore trailing semicolons. // Not an error. - return + break } // Parse error. return mediatype, nil, ErrInvalidMediaParameter } pmap := params - if idx := strings.Index(key, "*"); idx != -1 { - baseName := key[:idx] + if baseName, _, ok := strings.Cut(key, "*"); ok { if continuation == nil { continuation = make(map[string]map[string]string) } @@ -185,8 +180,8 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e pmap = continuation[baseName] } } - if _, exists := pmap[key]; exists { - // Duplicate parameter name is bogus. + if v, exists := pmap[key]; exists && v != value { + // Duplicate parameter names are incorrect, but we allow them if they are equal. return "", nil, errors.New("mime: duplicate parameter name") } pmap[key] = value diff --git a/src/mime/mediatype_test.go b/src/mime/mediatype_test.go index e91ff38d68b489..1458cdb6e2dd82 100644 --- a/src/mime/mediatype_test.go +++ b/src/mime/mediatype_test.go @@ -42,7 +42,7 @@ func TestConsumeValue(t *testing.T) { {`"My \" value"end`, "My \" value", "end"}, {`"\" rest`, "", `"\" rest`}, {`"C:\dev\go\robots.txt"`, `C:\dev\go\robots.txt`, ""}, - {`"C:\新建文件件\中文第二次测试.mp4"`, `C:\新建文件件\中文第二次测试.mp4`, ""}, + {`"C:\新建文件夹\中文第二次测试.mp4"`, `C:\新建文件夹\中文第二次测试.mp4`, ""}, } for _, test := range tests { value, rest := consumeValue(test[0]) @@ -394,9 +394,23 @@ func TestParseMediaType(t *testing.T) { // Empty string used to be mishandled. {`foo; bar=""`, "foo", m("bar", "")}, - // Microsoft browers in intranet mode do not think they need to escape \ in file name. + // Microsoft browsers in intranet mode do not think they need to escape \ in file name. {`form-data; name="file"; filename="C:\dev\go\robots.txt"`, "form-data", m("name", "file", "filename", `C:\dev\go\robots.txt`)}, - {`form-data; name="file"; filename="C:\新建文件件\中文第二次测试.mp4"`, "form-data", m("name", "file", "filename", `C:\新建文件件\中文第二次测试.mp4`)}, + {`form-data; name="file"; filename="C:\新建文件夹\中文第二次测试.mp4"`, "form-data", m("name", "file", "filename", `C:\新建文件夹\中文第二次测试.mp4`)}, + + // issue #46323 (https://github.com/golang/go/issues/46323) + { + // example from rfc2231-p.3 (https://datatracker.ietf.org/doc/html/rfc2231) + `message/external-body; access-type=URL; + URL*0="ftp://"; + URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar";`, // <-- trailing semicolon + `message/external-body`, + m("access-type", "URL", "url", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"), + }, + + // Issue #48866: duplicate parameters containing equal values should be allowed + {`text; charset=utf-8; charset=utf-8; format=fixed`, "text", m("charset", "utf-8", "format", "fixed")}, + {`text; charset=utf-8; format=flowed; charset=utf-8`, "text", m("charset", "utf-8", "format", "flowed")}, } for _, test := range tests { mt, params, err := ParseMediaType(test.in) diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go index e3a3a3eae8e156..8a4eabcee038a2 100644 --- a/src/mime/multipart/formdata_test.go +++ b/src/mime/multipart/formdata_test.go @@ -5,7 +5,6 @@ package multipart import ( - "bytes" "io" "math" "os" @@ -92,7 +91,7 @@ func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File { if err != nil { t.Fatal("opening file:", err) } - b := new(bytes.Buffer) + b := new(strings.Builder) _, err = io.Copy(b, f) if err != nil { t.Fatal("copying contents:", err) diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go index 81bf722d4ee5fd..aa05ac8f9c8a5f 100644 --- a/src/mime/multipart/multipart.go +++ b/src/mime/multipart/multipart.go @@ -149,11 +149,11 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) { return bp, nil } -func (bp *Part) populateHeaders() error { - r := textproto.NewReader(bp.mr.bufReader) +func (p *Part) populateHeaders() error { + r := textproto.NewReader(p.mr.bufReader) header, err := r.ReadMIMEHeader() if err == nil { - bp.Header = header + p.Header = header } return err } @@ -264,7 +264,8 @@ func scanUntilBoundary(buf, dashBoundary, nlDashBoundary []byte, total int64, re // and the caller has verified already that bytes.HasPrefix(buf, prefix) is true. // // matchAfterPrefix returns +1 if the buffer does match the boundary, -// meaning the prefix is followed by a dash, space, tab, cr, nl, or end of input. +// meaning the prefix is followed by a double dash, space, tab, cr, nl, +// or end of input. // It returns -1 if the buffer definitely does NOT match the boundary, // meaning the prefix is followed by some other character. // For example, "--foobar" does not match "--foo". @@ -278,9 +279,25 @@ func matchAfterPrefix(buf, prefix []byte, readErr error) int { return 0 } c := buf[len(prefix)] - if c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '-' { + + if c == ' ' || c == '\t' || c == '\r' || c == '\n' { return +1 } + + // Try to detect boundaryDash + if c == '-' { + if len(buf) == len(prefix)+1 { + if readErr != nil { + // Prefix + "-" does not match + return -1 + } + return 0 + } + if buf[len(prefix)+1] == '-' { + return +1 + } + } + return -1 } @@ -386,41 +403,42 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) { // isFinalBoundary reports whether line is the final boundary line // indicating that all parts are over. // It matches `^--boundary--[ \t]*(\r\n)?$` -func (mr *Reader) isFinalBoundary(line []byte) bool { - if !bytes.HasPrefix(line, mr.dashBoundaryDash) { +func (r *Reader) isFinalBoundary(line []byte) bool { + if !bytes.HasPrefix(line, r.dashBoundaryDash) { return false } - rest := line[len(mr.dashBoundaryDash):] + rest := line[len(r.dashBoundaryDash):] rest = skipLWSPChar(rest) - return len(rest) == 0 || bytes.Equal(rest, mr.nl) + return len(rest) == 0 || bytes.Equal(rest, r.nl) } -func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { +func (r *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { // https://tools.ietf.org/html/rfc2046#section-5.1 // The boundary delimiter line is then defined as a line // consisting entirely of two hyphen characters ("-", // decimal value 45) followed by the boundary parameter // value from the Content-Type header field, optional linear // whitespace, and a terminating CRLF. - if !bytes.HasPrefix(line, mr.dashBoundary) { + if !bytes.HasPrefix(line, r.dashBoundary) { return false } - rest := line[len(mr.dashBoundary):] + rest := line[len(r.dashBoundary):] rest = skipLWSPChar(rest) // On the first part, see our lines are ending in \n instead of \r\n // and switch into that mode if so. This is a violation of the spec, // but occurs in practice. - if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { - mr.nl = mr.nl[1:] - mr.nlDashBoundary = mr.nlDashBoundary[1:] + if r.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { + r.nl = r.nl[1:] + r.nlDashBoundary = r.nlDashBoundary[1:] } - return bytes.Equal(rest, mr.nl) + return bytes.Equal(rest, r.nl) } // skipLWSPChar returns b with leading spaces and tabs removed. // RFC 822 defines: -// LWSP-char = SPACE / HTAB +// +// LWSP-char = SPACE / HTAB func skipLWSPChar(b []byte) []byte { for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') { b = b[1:] diff --git a/src/mime/multipart/multipart_test.go b/src/mime/multipart/multipart_test.go index 741d2304ed45b3..e0cb768c6967a5 100644 --- a/src/mime/multipart/multipart_test.go +++ b/src/mime/multipart/multipart_test.go @@ -126,7 +126,7 @@ func TestMultipartSlowInput(t *testing.T) { func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) { t.Parallel() reader := NewReader(r, "MyBoundary") - buf := new(bytes.Buffer) + buf := new(strings.Builder) // Part1 part, err := reader.NextPart() @@ -291,24 +291,34 @@ func TestLineLimit(t *testing.T) { } func TestMultipartTruncated(t *testing.T) { - testBody := ` + for _, body := range []string{ + ` This is a multi-part message. This line is ignored. --MyBoundary foo-bar: baz Oh no, premature EOF! -` - body := strings.ReplaceAll(testBody, "\n", "\r\n") - bodyReader := strings.NewReader(body) - r := NewReader(bodyReader, "MyBoundary") +`, + ` +This is a multi-part message. This line is ignored. +--MyBoundary +foo-bar: baz - part, err := r.NextPart() - if err != nil { - t.Fatalf("didn't get a part") - } - _, err = io.Copy(io.Discard, part) - if err != io.ErrUnexpectedEOF { - t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err) +Oh no, premature EOF! +--MyBoundary-`, + } { + body = strings.ReplaceAll(body, "\n", "\r\n") + bodyReader := strings.NewReader(body) + r := NewReader(bodyReader, "MyBoundary") + + part, err := r.NextPart() + if err != nil { + t.Fatalf("didn't get a part") + } + _, err = io.Copy(io.Discard, part) + if err != io.ErrUnexpectedEOF { + t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err) + } } } @@ -406,7 +416,7 @@ func TestLineContinuation(t *testing.T) { if err != nil { t.Fatalf("didn't get a part") } - var buf bytes.Buffer + var buf strings.Builder n, err := io.Copy(&buf, part) if err != nil { t.Errorf("error reading part: %v\nread so far: %q", err, buf.String()) @@ -436,7 +446,7 @@ func testQuotedPrintableEncoding(t *testing.T, cte string) { if te, ok := part.Header["Content-Transfer-Encoding"]; ok { t.Errorf("unexpected Content-Transfer-Encoding of %q", te) } - var buf bytes.Buffer + var buf strings.Builder _, err = io.Copy(&buf, part) if err != nil { t.Error(err) @@ -474,7 +484,7 @@ Content-Transfer-Encoding: quoted-printable if _, ok := part.Header["Content-Transfer-Encoding"]; !ok { t.Errorf("missing Content-Transfer-Encoding") } - var buf bytes.Buffer + var buf strings.Builder _, err = io.Copy(&buf, part) if err != nil { t.Error(err) @@ -751,6 +761,7 @@ html things }, }, }, + // Issue 12662: Check that we don't consume the leading \r if the peekBuffer // ends in '\r\n--separator-' { @@ -767,6 +778,7 @@ Content-Type: application/octet-stream }, }, }, + // Issue 12662: Same test as above with \r\n at the end { name: "peek buffer boundary condition", @@ -782,6 +794,7 @@ Content-Type: application/octet-stream }, }, }, + // Issue 12662v2: We want to make sure that for short buffers that end with // '\r\n--separator-' we always consume at least one (valid) symbol from the // peekBuffer @@ -799,6 +812,7 @@ Content-Type: application/octet-stream }, }, }, + // Context: https://github.com/camlistore/camlistore/issues/642 // If the file contents in the form happens to have a size such as: // size = peekBufferSize - (len("\n--") + len(boundary) + len("\r") + 1), (modulo peekBufferSize) @@ -832,6 +846,52 @@ val }, }, + // Issue 46042; a nested multipart uses the outer separator followed by + // a dash. + { + name: "nested separator prefix is outer separator followed by a dash", + sep: "foo", + in: strings.Replace(`--foo +Content-Type: multipart/alternative; boundary="foo-bar" + +--foo-bar + +Body +--foo-bar + +Body2 +--foo-bar-- +--foo--`, "\n", "\r\n", -1), + want: []headerBody{ + {textproto.MIMEHeader{"Content-Type": {`multipart/alternative; boundary="foo-bar"`}}, + strings.Replace(`--foo-bar + +Body +--foo-bar + +Body2 +--foo-bar--`, "\n", "\r\n", -1), + }, + }, + }, + + // A nested boundary cannot be the outer separator followed by double dash. + { + name: "nested separator prefix is outer separator followed by double dash", + sep: "foo", + in: strings.Replace(`--foo +Content-Type: multipart/alternative; boundary="foo--" + +--foo-- + +Body + +--foo--`, "\n", "\r\n", -1), + want: []headerBody{ + {textproto.MIMEHeader{"Content-Type": {`multipart/alternative; boundary="foo--"`}}, ""}, + }, + }, + roundTripParseTest(), } @@ -933,7 +993,7 @@ func roundTripParseTest() parseTest { formData("foo", "bar"), }, } - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(&buf) for _, p := range t.want { pw, err := w.CreatePart(p.header) diff --git a/src/mime/multipart/writer_test.go b/src/mime/multipart/writer_test.go index cfc0f09f37f210..9e0f1314c98350 100644 --- a/src/mime/multipart/writer_test.go +++ b/src/mime/multipart/writer_test.go @@ -98,7 +98,7 @@ func TestWriterSetBoundary(t *testing.T) { {"(boundary)", true}, } for i, tt := range tests { - var b bytes.Buffer + var b strings.Builder w := NewWriter(&b) err := w.SetBoundary(tt.b) got := err == nil @@ -145,7 +145,7 @@ func TestWriterBoundaryGoroutines(t *testing.T) { } func TestSortedHeader(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(&buf) if err := w.SetBoundary("MIMEBOUNDARY"); err != nil { t.Fatalf("Error setting mime boundary: %v", err) diff --git a/src/mime/quotedprintable/reader_test.go b/src/mime/quotedprintable/reader_test.go index 48a7ff64953be9..0af1e5f0f75455 100644 --- a/src/mime/quotedprintable/reader_test.go +++ b/src/mime/quotedprintable/reader_test.go @@ -6,7 +6,6 @@ package quotedprintable import ( "bufio" - "bytes" "errors" "flag" "fmt" @@ -22,7 +21,7 @@ import ( func TestReader(t *testing.T) { tests := []struct { in, want string - err interface{} + err any }{ {in: "", want: ""}, {in: "foo bar", want: "foo bar"}, @@ -69,7 +68,7 @@ func TestReader(t *testing.T) { want: "accept UTF-8 right quotation mark: ’"}, } for _, tt := range tests { - var buf bytes.Buffer + var buf strings.Builder _, err := io.Copy(&buf, NewReader(strings.NewReader(tt.in))) if got := buf.String(); got != tt.want { t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want) @@ -114,7 +113,7 @@ func TestExhaustive(t *testing.T) { } } - var buf bytes.Buffer + var buf strings.Builder res := make(map[string]int) n := 6 if testing.Short() { @@ -160,7 +159,7 @@ func TestExhaustive(t *testing.T) { if err != nil { panic(err) } - qpres := make(chan interface{}, 2) + qpres := make(chan any, 2) go func() { br := bufio.NewReader(stderr) s, _ := br.ReadString('\n') diff --git a/src/mime/quotedprintable/writer_test.go b/src/mime/quotedprintable/writer_test.go index 42de0f3d6e8f2c..07411fe269eba2 100644 --- a/src/mime/quotedprintable/writer_test.go +++ b/src/mime/quotedprintable/writer_test.go @@ -91,7 +91,7 @@ func testWriter(t *testing.T, binary bool) { } for _, tt := range tests { - buf := new(bytes.Buffer) + buf := new(strings.Builder) w := NewWriter(buf) want := tt.want diff --git a/src/mime/testdata/test.types.globs2 b/src/mime/testdata/test.types.globs2 index cb5b7899b0d5f5..4606d98f13a53a 100644 --- a/src/mime/testdata/test.types.globs2 +++ b/src/mime/testdata/test.types.globs2 @@ -6,4 +6,9 @@ # mime package test for globs2 50:document/test:*.t3 50:example/test:*.t4 +50:text/plain:*,v +50:application/x-trash:*~ 30:example/do-not-use:*.t4 +10:example/glob-question-mark:*.foo?ar +10:example/glob-asterisk:*.foo*r +10:example/glob-range:*.foo[1-3] diff --git a/src/mime/type.go b/src/mime/type.go index 26424339af8d03..465ecf0d599ccb 100644 --- a/src/mime/type.go +++ b/src/mime/type.go @@ -23,7 +23,7 @@ var ( ) func clearSyncMap(m *sync.Map) { - m.Range(func(k, _ interface{}) bool { + m.Range(func(k, _ any) bool { m.Delete(k) return true }) @@ -99,11 +99,11 @@ func initMime() { // system's MIME-info database or mime.types file(s) if available under one or // more of these names: // -// /usr/local/share/mime/globs2 -// /usr/share/mime/globs2 -// /etc/mime.types -// /etc/apache2/mime.types -// /etc/apache/mime.types +// /usr/local/share/mime/globs2 +// /usr/share/mime/globs2 +// /etc/mime.types +// /etc/apache2/mime.types +// /etc/apache/mime.types // // On Windows, MIME types are extracted from the registry. // diff --git a/src/mime/type_test.go b/src/mime/type_test.go index f10e6343f9c459..d8368e8846f627 100644 --- a/src/mime/type_test.go +++ b/src/mime/type_test.go @@ -14,7 +14,10 @@ import ( func setMimeInit(fn func()) (cleanup func()) { once = sync.Once{} testInitMime = fn - return func() { testInitMime = nil } + return func() { + testInitMime = nil + once = sync.Once{} + } } func clearMimeTypes() { diff --git a/src/mime/type_unix.go b/src/mime/type_unix.go index f954bc8a1fba68..649d9001e3be04 100644 --- a/src/mime/type_unix.go +++ b/src/mime/type_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package mime @@ -41,15 +40,27 @@ func loadMimeGlobsFile(filename string) error { scanner := bufio.NewScanner(f) for scanner.Scan() { - // Each line should be of format: weight:mimetype:*.ext + // Each line should be of format: weight:mimetype:glob[:morefields...] fields := strings.Split(scanner.Text(), ":") - if len(fields) < 3 || len(fields[0]) < 1 || len(fields[2]) < 2 { + if len(fields) < 3 || len(fields[0]) < 1 || len(fields[2]) < 3 { continue - } else if fields[0][0] == '#' || fields[2][0] != '*' { + } else if fields[0][0] == '#' || fields[2][0] != '*' || fields[2][1] != '.' { continue } extension := fields[2][1:] + if strings.ContainsAny(extension, "?*[") { + // Not a bare extension, but a glob. Ignore for now: + // - we do not have an implementation for this glob + // syntax (translation to path/filepath.Match could + // be possible) + // - support for globs with weight ordering would have + // performance impact to all lookups to support the + // rarely seen glob entries + // - trying to match glob metacharacters literally is + // not useful + continue + } if _, ok := mimeTypes.Load(extension); ok { // We've already seen this extension. // The file is in weight order, so we keep diff --git a/src/mime/type_unix_test.go b/src/mime/type_unix_test.go index 6e2988225c35ce..7b8db79d272a98 100644 --- a/src/mime/type_unix_test.go +++ b/src/mime/type_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package mime @@ -12,6 +11,7 @@ import ( ) func initMimeUnixTest(t *testing.T) { + once.Do(initMime) err := loadMimeGlobsFile("testdata/test.types.globs2") if err != nil { t.Fatal(err) @@ -23,11 +23,16 @@ func initMimeUnixTest(t *testing.T) { func TestTypeByExtensionUNIX(t *testing.T) { initMimeUnixTest(t) typeTests := map[string]string{ - ".T1": "application/test", - ".t2": "text/test; charset=utf-8", - ".t3": "document/test", - ".t4": "example/test", - ".png": "image/png", + ".T1": "application/test", + ".t2": "text/test; charset=utf-8", + ".t3": "document/test", + ".t4": "example/test", + ".png": "image/png", + ",v": "", + "~": "", + ".foo?ar": "", + ".foo*r": "", + ".foo[1-3]": "", } for ext, want := range typeTests { diff --git a/src/mime/type_windows.go b/src/mime/type_windows.go index cee9c9db041b2b..93802141c532d0 100644 --- a/src/mime/type_windows.go +++ b/src/mime/type_windows.go @@ -30,6 +30,17 @@ func initMimeWindows() { if err != nil { continue } + + // There is a long-standing problem on Windows: the + // registry sometimes records that the ".js" extension + // should be "text/plain". See issue #32350. While + // normally local configuration should override + // defaults, this problem is common enough that we + // handle it here by ignoring that registry setting. + if name == ".js" && (v == "text/plain" || v == "text/plain; charset=utf-8") { + continue + } + setExtensionType(name, v) } } diff --git a/src/net/addrselect.go b/src/net/addrselect.go index ae93c595afafe6..b76183a34c42fe 100644 --- a/src/net/addrselect.go +++ b/src/net/addrselect.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris - // Minimal RFC 6724 address selection. package net -import "sort" +import ( + "net/netip" + "sort" +) func sortByRFC6724(addrs []IPAddr) { if len(addrs) < 2 { @@ -18,14 +18,15 @@ func sortByRFC6724(addrs []IPAddr) { sortByRFC6724withSrcs(addrs, srcAddrs(addrs)) } -func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) { +func sortByRFC6724withSrcs(addrs []IPAddr, srcs []netip.Addr) { if len(addrs) != len(srcs) { panic("internal error") } addrAttr := make([]ipAttr, len(addrs)) srcAttr := make([]ipAttr, len(srcs)) for i, v := range addrs { - addrAttr[i] = ipAttrOf(v.IP) + addrAttrIP, _ := netip.AddrFromSlice(v.IP) + addrAttr[i] = ipAttrOf(addrAttrIP) srcAttr[i] = ipAttrOf(srcs[i]) } sort.Stable(&byRFC6724{ @@ -39,8 +40,8 @@ func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) { // srcsAddrs tries to UDP-connect to each address to see if it has a // route. (This doesn't send any packets). The destination port // number is irrelevant. -func srcAddrs(addrs []IPAddr) []IP { - srcs := make([]IP, len(addrs)) +func srcAddrs(addrs []IPAddr) []netip.Addr { + srcs := make([]netip.Addr, len(addrs)) dst := UDPAddr{Port: 9} for i := range addrs { dst.IP = addrs[i].IP @@ -48,7 +49,7 @@ func srcAddrs(addrs []IPAddr) []IP { c, err := DialUDP("udp", nil, &dst) if err == nil { if src, ok := c.LocalAddr().(*UDPAddr); ok { - srcs[i] = src.IP + srcs[i], _ = netip.AddrFromSlice(src.IP) } c.Close() } @@ -62,8 +63,8 @@ type ipAttr struct { Label uint8 } -func ipAttrOf(ip IP) ipAttr { - if ip == nil { +func ipAttrOf(ip netip.Addr) ipAttr { + if !ip.IsValid() { return ipAttr{} } match := rfc6724policyTable.Classify(ip) @@ -77,7 +78,7 @@ func ipAttrOf(ip IP) ipAttr { type byRFC6724 struct { addrs []IPAddr // addrs to sort addrAttr []ipAttr - srcs []IP // or nil if unreachable + srcs []netip.Addr // or not valid addr if unreachable srcAttr []ipAttr } @@ -111,13 +112,13 @@ func (s *byRFC6724) Less(i, j int) bool { // If DB is known to be unreachable or if Source(DB) is undefined, then // prefer DA. Similarly, if DA is known to be unreachable or if // Source(DA) is undefined, then prefer DB. - if SourceDA == nil && SourceDB == nil { + if !SourceDA.IsValid() && !SourceDB.IsValid() { return false // "equal" } - if SourceDB == nil { + if !SourceDB.IsValid() { return preferDA } - if SourceDA == nil { + if !SourceDA.IsValid() { return preferDB } @@ -187,7 +188,7 @@ func (s *byRFC6724) Less(i, j int) bool { return preferDB } - // Rule 9: Use longest matching prefix. + // Rule 9: Use the longest matching prefix. // When DA and DB belong to the same address family (both are IPv6 or // both are IPv4 [but see below]): If CommonPrefixLen(Source(DA), DA) > // CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if @@ -215,7 +216,7 @@ func (s *byRFC6724) Less(i, j int) bool { } type policyTableEntry struct { - Prefix *IPNet + Prefix netip.Prefix Precedence uint8 Label uint8 } @@ -223,90 +224,75 @@ type policyTableEntry struct { type policyTable []policyTableEntry // RFC 6724 section 2.1. +// Items are sorted by the size of their Prefix.Mask.Size, var rfc6724policyTable = policyTable{ { - Prefix: mustCIDR("::1/128"), + // "::1/128" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128), Precedence: 50, Label: 0, }, { - Prefix: mustCIDR("::/0"), - Precedence: 40, - Label: 1, - }, - { + // "::ffff:0:0/96" // IPv4-compatible, etc. - Prefix: mustCIDR("::ffff:0:0/96"), + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96), Precedence: 35, Label: 4, }, { - // 6to4 - Prefix: mustCIDR("2002::/16"), - Precedence: 30, - Label: 2, + // "::/96" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96), + Precedence: 1, + Label: 3, }, { + // "2001::/32" // Teredo - Prefix: mustCIDR("2001::/32"), + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32), Precedence: 5, Label: 5, }, { - Prefix: mustCIDR("fc00::/7"), - Precedence: 3, - Label: 13, + // "2002::/16" + // 6to4 + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16), + Precedence: 30, + Label: 2, }, { - Prefix: mustCIDR("::/96"), + // "3ffe::/16" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16), Precedence: 1, - Label: 3, + Label: 12, }, { - Prefix: mustCIDR("fec0::/10"), + // "fec0::/10" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10), Precedence: 1, Label: 11, }, { - Prefix: mustCIDR("3ffe::/16"), - Precedence: 1, - Label: 12, + // "fc00::/7" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7), + Precedence: 3, + Label: 13, + }, + { + // "::/0" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0), + Precedence: 40, + Label: 1, }, -} - -func init() { - sort.Sort(sort.Reverse(byMaskLength(rfc6724policyTable))) -} - -// byMaskLength sorts policyTableEntry by the size of their Prefix.Mask.Size, -// from smallest mask, to largest. -type byMaskLength []policyTableEntry - -func (s byMaskLength) Len() int { return len(s) } -func (s byMaskLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byMaskLength) Less(i, j int) bool { - isize, _ := s[i].Prefix.Mask.Size() - jsize, _ := s[j].Prefix.Mask.Size() - return isize < jsize -} - -// mustCIDR calls ParseCIDR and panics on any error, or if the network -// is not IPv6. -func mustCIDR(s string) *IPNet { - ip, ipNet, err := ParseCIDR(s) - if err != nil { - panic(err.Error()) - } - if len(ip) != IPv6len { - panic("unexpected IP length") - } - return ipNet } // Classify returns the policyTableEntry of the entry with the longest // matching prefix that contains ip. // The table t must be sorted from largest mask size to smallest. -func (t policyTable) Classify(ip IP) policyTableEntry { +func (t policyTable) Classify(ip netip.Addr) policyTableEntry { + // Prefix.Contains() will not match an IPv6 prefix for an IPv4 address. + if ip.Is4() { + ip = netip.AddrFrom16(ip.As16()) + } for _, ent := range t { if ent.Prefix.Contains(ip) { return ent @@ -327,17 +313,18 @@ const ( scopeGlobal scope = 0xe ) -func classifyScope(ip IP) scope { +func classifyScope(ip netip.Addr) scope { if ip.IsLoopback() || ip.IsLinkLocalUnicast() { return scopeLinkLocal } - ipv6 := len(ip) == IPv6len && ip.To4() == nil + ipv6 := ip.Is6() && !ip.Is4In6() + ipv6AsBytes := ip.As16() if ipv6 && ip.IsMulticast() { - return scope(ip[1] & 0xf) + return scope(ipv6AsBytes[1] & 0xf) } // Site-local addresses are defined in RFC 3513 section 2.5.6 // (and deprecated in RFC 3879). - if ipv6 && ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 { + if ipv6 && ipv6AsBytes[0] == 0xfe && ipv6AsBytes[1]&0xc0 == 0xc0 { return scopeSiteLocal } return scopeGlobal @@ -353,30 +340,28 @@ func classifyScope(ip IP) scope { // If a and b are different IP versions, 0 is returned. // // See https://tools.ietf.org/html/rfc6724#section-2.2 -func commonPrefixLen(a, b IP) (cpl int) { - if a4 := a.To4(); a4 != nil { - a = a4 - } +func commonPrefixLen(a netip.Addr, b IP) (cpl int) { if b4 := b.To4(); b4 != nil { b = b4 } - if len(a) != len(b) { + aAsSlice := a.AsSlice() + if len(aAsSlice) != len(b) { return 0 } // If IPv6, only up to the prefix (first 64 bits) - if len(a) > 8 { - a = a[:8] + if len(aAsSlice) > 8 { + aAsSlice = aAsSlice[:8] b = b[:8] } - for len(a) > 0 { - if a[0] == b[0] { + for len(aAsSlice) > 0 { + if aAsSlice[0] == b[0] { cpl += 8 - a = a[1:] + aAsSlice = aAsSlice[1:] b = b[1:] continue } bits := 8 - ab, bb := a[0], b[0] + ab, bb := aAsSlice[0], b[0] for { ab >>= 1 bb >>= 1 diff --git a/src/net/addrselect_test.go b/src/net/addrselect_test.go index dc13917018b23d..7e8134d754447b 100644 --- a/src/net/addrselect_test.go +++ b/src/net/addrselect_test.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net import ( + "net/netip" "reflect" "testing" ) @@ -15,7 +15,7 @@ import ( func TestSortByRFC6724(t *testing.T) { tests := []struct { in []IPAddr - srcs []IP + srcs []netip.Addr want []IPAddr reverse bool // also test it starting backwards }{ @@ -27,9 +27,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("198.51.100.121")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("169.254.13.78"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("169.254.13.78"), }, want: []IPAddr{ {IP: ParseIP("2001:db8:1::1")}, @@ -44,9 +44,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("198.51.100.121")}, }, - srcs: []IP{ - ParseIP("fe80::1"), - ParseIP("198.51.100.117"), + srcs: []netip.Addr{ + netip.MustParseAddr("fe80::1"), + netip.MustParseAddr("198.51.100.117"), }, want: []IPAddr{ {IP: ParseIP("198.51.100.121")}, @@ -61,9 +61,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("10.1.2.3")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("10.1.2.4"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("10.1.2.4"), }, want: []IPAddr{ {IP: ParseIP("2001:db8:1::1")}, @@ -78,9 +78,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("fe80::1")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("fe80::2"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("fe80::2"), }, want: []IPAddr{ {IP: ParseIP("fe80::1")}, @@ -100,13 +100,13 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("23.23.134.56")}, {IP: ParseIP("23.21.50.150")}, }, - srcs: []IP{ - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), + srcs: []netip.Addr{ + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), }, want: []IPAddr{ {IP: ParseIP("54.83.193.112")}, @@ -122,7 +122,7 @@ func TestSortByRFC6724(t *testing.T) { for i, tt := range tests { inCopy := make([]IPAddr, len(tt.in)) copy(inCopy, tt.in) - srcCopy := make([]IP, len(tt.in)) + srcCopy := make([]netip.Addr, len(tt.in)) copy(srcCopy, tt.srcs) sortByRFC6724withSrcs(inCopy, srcCopy) if !reflect.DeepEqual(inCopy, tt.want) { @@ -146,39 +146,100 @@ func TestSortByRFC6724(t *testing.T) { } +func TestRFC6724PolicyTableOrder(t *testing.T) { + for i := 0; i < len(rfc6724policyTable)-1; i++ { + if !(rfc6724policyTable[i].Prefix.Bits() >= rfc6724policyTable[i+1].Prefix.Bits()) { + t.Errorf("rfc6724policyTable item number %d sorted in wrong order = %d bits, next item = %d bits;", i, rfc6724policyTable[i].Prefix.Bits(), rfc6724policyTable[i+1].Prefix.Bits()) + } + } +} + +func TestRFC6724PolicyTableContent(t *testing.T) { + expectedRfc6724policyTable := policyTable{ + { + Prefix: netip.MustParsePrefix("::1/128"), + Precedence: 50, + Label: 0, + }, + { + Prefix: netip.MustParsePrefix("::ffff:0:0/96"), + Precedence: 35, + Label: 4, + }, + { + Prefix: netip.MustParsePrefix("::/96"), + Precedence: 1, + Label: 3, + }, + { + Prefix: netip.MustParsePrefix("2001::/32"), + Precedence: 5, + Label: 5, + }, + { + Prefix: netip.MustParsePrefix("2002::/16"), + Precedence: 30, + Label: 2, + }, + { + Prefix: netip.MustParsePrefix("3ffe::/16"), + Precedence: 1, + Label: 12, + }, + { + Prefix: netip.MustParsePrefix("fec0::/10"), + Precedence: 1, + Label: 11, + }, + { + Prefix: netip.MustParsePrefix("fc00::/7"), + Precedence: 3, + Label: 13, + }, + { + Prefix: netip.MustParsePrefix("::/0"), + Precedence: 40, + Label: 1, + }, + } + if !reflect.DeepEqual(rfc6724policyTable, expectedRfc6724policyTable) { + t.Errorf("rfc6724policyTable has wrong contend = %v; want %v", rfc6724policyTable, expectedRfc6724policyTable) + } +} + func TestRFC6724PolicyTableClassify(t *testing.T) { tests := []struct { - ip IP + ip netip.Addr want policyTableEntry }{ { - ip: ParseIP("127.0.0.1"), + ip: netip.MustParseAddr("127.0.0.1"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::ffff:0:0"), Mask: CIDRMask(96, 128)}, + Prefix: netip.MustParsePrefix("::ffff:0:0/96"), Precedence: 35, Label: 4, }, }, { - ip: ParseIP("2601:645:8002:a500:986f:1db8:c836:bd65"), + ip: netip.MustParseAddr("2601:645:8002:a500:986f:1db8:c836:bd65"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::"), Mask: CIDRMask(0, 128)}, + Prefix: netip.MustParsePrefix("::/0"), Precedence: 40, Label: 1, }, }, { - ip: ParseIP("::1"), + ip: netip.MustParseAddr("::1"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::1"), Mask: CIDRMask(128, 128)}, + Prefix: netip.MustParsePrefix("::1/128"), Precedence: 50, Label: 0, }, }, { - ip: ParseIP("2002::ab12"), + ip: netip.MustParseAddr("2002::ab12"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("2002::"), Mask: CIDRMask(16, 128)}, + Prefix: netip.MustParsePrefix("2002::/16"), Precedence: 30, Label: 2, }, @@ -194,24 +255,24 @@ func TestRFC6724PolicyTableClassify(t *testing.T) { func TestRFC6724ClassifyScope(t *testing.T) { tests := []struct { - ip IP + ip netip.Addr want scope }{ - {ParseIP("127.0.0.1"), scopeLinkLocal}, // rfc6724#section-3.2 - {ParseIP("::1"), scopeLinkLocal}, // rfc4007#section-4 - {ParseIP("169.254.1.2"), scopeLinkLocal}, // rfc6724#section-3.2 - {ParseIP("fec0::1"), scopeSiteLocal}, - {ParseIP("8.8.8.8"), scopeGlobal}, - - {ParseIP("ff02::"), scopeLinkLocal}, // IPv6 multicast - {ParseIP("ff05::"), scopeSiteLocal}, // IPv6 multicast - {ParseIP("ff04::"), scopeAdminLocal}, // IPv6 multicast - {ParseIP("ff0e::"), scopeGlobal}, // IPv6 multicast - - {IPv4(0xe0, 0, 0, 0), scopeGlobal}, // IPv4 link-local multicast as 16 bytes - {IPv4(0xe0, 2, 2, 2), scopeGlobal}, // IPv4 global multicast as 16 bytes - {IPv4(0xe0, 0, 0, 0).To4(), scopeGlobal}, // IPv4 link-local multicast as 4 bytes - {IPv4(0xe0, 2, 2, 2).To4(), scopeGlobal}, // IPv4 global multicast as 4 bytes + {netip.MustParseAddr("127.0.0.1"), scopeLinkLocal}, // rfc6724#section-3.2 + {netip.MustParseAddr("::1"), scopeLinkLocal}, // rfc4007#section-4 + {netip.MustParseAddr("169.254.1.2"), scopeLinkLocal}, // rfc6724#section-3.2 + {netip.MustParseAddr("fec0::1"), scopeSiteLocal}, + {netip.MustParseAddr("8.8.8.8"), scopeGlobal}, + + {netip.MustParseAddr("ff02::"), scopeLinkLocal}, // IPv6 multicast + {netip.MustParseAddr("ff05::"), scopeSiteLocal}, // IPv6 multicast + {netip.MustParseAddr("ff04::"), scopeAdminLocal}, // IPv6 multicast + {netip.MustParseAddr("ff0e::"), scopeGlobal}, // IPv6 multicast + + {netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 0, 0, 0}), scopeGlobal}, // IPv4 link-local multicast as 16 bytes + {netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 2, 2, 2}), scopeGlobal}, // IPv4 global multicast as 16 bytes + {netip.AddrFrom4([4]byte{0xe0, 0, 0, 0}), scopeGlobal}, // IPv4 link-local multicast as 4 bytes + {netip.AddrFrom4([4]byte{0xe0, 2, 2, 2}), scopeGlobal}, // IPv4 global multicast as 4 bytes } for i, tt := range tests { got := classifyScope(tt.ip) @@ -223,22 +284,23 @@ func TestRFC6724ClassifyScope(t *testing.T) { func TestRFC6724CommonPrefixLength(t *testing.T) { tests := []struct { - a, b IP + a netip.Addr + b IP want int }{ - {ParseIP("fe80::1"), ParseIP("fe80::2"), 64}, - {ParseIP("fe81::1"), ParseIP("fe80::2"), 15}, - {ParseIP("127.0.0.1"), ParseIP("fe80::1"), 0}, // diff size - {IPv4(1, 2, 3, 4), IP{1, 2, 3, 4}, 32}, - {IP{1, 2, 255, 255}, IP{1, 2, 0, 0}, 16}, - {IP{1, 2, 127, 255}, IP{1, 2, 0, 0}, 17}, - {IP{1, 2, 63, 255}, IP{1, 2, 0, 0}, 18}, - {IP{1, 2, 31, 255}, IP{1, 2, 0, 0}, 19}, - {IP{1, 2, 15, 255}, IP{1, 2, 0, 0}, 20}, - {IP{1, 2, 7, 255}, IP{1, 2, 0, 0}, 21}, - {IP{1, 2, 3, 255}, IP{1, 2, 0, 0}, 22}, - {IP{1, 2, 1, 255}, IP{1, 2, 0, 0}, 23}, - {IP{1, 2, 0, 255}, IP{1, 2, 0, 0}, 24}, + {netip.MustParseAddr("fe80::1"), ParseIP("fe80::2"), 64}, + {netip.MustParseAddr("fe81::1"), ParseIP("fe80::2"), 15}, + {netip.MustParseAddr("127.0.0.1"), ParseIP("fe80::1"), 0}, // diff size + {netip.AddrFrom4([4]byte{1, 2, 3, 4}), IP{1, 2, 3, 4}, 32}, + {netip.AddrFrom4([4]byte{1, 2, 255, 255}), IP{1, 2, 0, 0}, 16}, + {netip.AddrFrom4([4]byte{1, 2, 127, 255}), IP{1, 2, 0, 0}, 17}, + {netip.AddrFrom4([4]byte{1, 2, 63, 255}), IP{1, 2, 0, 0}, 18}, + {netip.AddrFrom4([4]byte{1, 2, 31, 255}), IP{1, 2, 0, 0}, 19}, + {netip.AddrFrom4([4]byte{1, 2, 15, 255}), IP{1, 2, 0, 0}, 20}, + {netip.AddrFrom4([4]byte{1, 2, 7, 255}), IP{1, 2, 0, 0}, 21}, + {netip.AddrFrom4([4]byte{1, 2, 3, 255}), IP{1, 2, 0, 0}, 22}, + {netip.AddrFrom4([4]byte{1, 2, 1, 255}), IP{1, 2, 0, 0}, 23}, + {netip.AddrFrom4([4]byte{1, 2, 0, 255}), IP{1, 2, 0, 0}, 24}, } for i, tt := range tests { got := commonPrefixLen(tt.a, tt.b) diff --git a/src/net/cgo_aix.go b/src/net/cgo_aix.go index a94405ecc04969..f3478148b4f60e 100644 --- a/src/net/cgo_aix.go +++ b/src/net/cgo_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/cgo_android.go b/src/net/cgo_android.go index 4b1a2e3e1d44bd..5ab8b5fedea13a 100644 --- a/src/net/cgo_android.go +++ b/src/net/cgo_android.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/cgo_bsd.go b/src/net/cgo_bsd.go index 23be72140b694b..1456289b0691ba 100644 --- a/src/net/cgo_bsd.go +++ b/src/net/cgo_bsd.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo && (darwin || dragonfly || freebsd) -// +build cgo -// +build !netgo -// +build darwin dragonfly freebsd package net diff --git a/src/net/cgo_linux.go b/src/net/cgo_linux.go index 1bd6be93f7891c..de6e87f17622df 100644 --- a/src/net/cgo_linux.go +++ b/src/net/cgo_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !android && cgo && !netgo -// +build !android,cgo,!netgo package net diff --git a/src/net/cgo_netbsd.go b/src/net/cgo_netbsd.go index 3714793a52f3c5..03392e8ff34c59 100644 --- a/src/net/cgo_netbsd.go +++ b/src/net/cgo_netbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/cgo_openbsd.go b/src/net/cgo_openbsd.go index 3714793a52f3c5..03392e8ff34c59 100644 --- a/src/net/cgo_openbsd.go +++ b/src/net/cgo_openbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/cgo_resnew.go b/src/net/cgo_resnew.go index 154405270f1728..fa6e68770ce35f 100644 --- a/src/net/cgo_resnew.go +++ b/src/net/cgo_resnew.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo && (darwin || (linux && !android) || netbsd || solaris) -// +build cgo -// +build !netgo -// +build darwin linux,!android netbsd solaris package net diff --git a/src/net/cgo_resold.go b/src/net/cgo_resold.go index c4aab33eaa0570..37c75527f95c0d 100644 --- a/src/net/cgo_resold.go +++ b/src/net/cgo_resold.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo && (android || freebsd || dragonfly || openbsd) -// +build cgo -// +build !netgo -// +build android freebsd dragonfly openbsd package net diff --git a/src/net/cgo_socknew.go b/src/net/cgo_socknew.go index f9cfad9909cce8..fbb9e10f340196 100644 --- a/src/net/cgo_socknew.go +++ b/src/net/cgo_socknew.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo && (android || linux || solaris) -// +build cgo -// +build !netgo -// +build android linux solaris package net diff --git a/src/net/cgo_sockold.go b/src/net/cgo_sockold.go index 22c67252f55cec..4d9869de04fabb 100644 --- a/src/net/cgo_sockold.go +++ b/src/net/cgo_sockold.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || netbsd || openbsd) -// +build cgo -// +build !netgo -// +build aix darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/cgo_solaris.go b/src/net/cgo_solaris.go index a84f5b0d34c147..cde9c957fee53c 100644 --- a/src/net/cgo_solaris.go +++ b/src/net/cgo_solaris.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/cgo_stub.go b/src/net/cgo_stub.go index 039e4be88b908a..298d829f6fa6db 100644 --- a/src/net/cgo_stub.go +++ b/src/net/cgo_stub.go @@ -3,14 +3,11 @@ // license that can be found in the LICENSE file. //go:build !cgo || netgo -// +build !cgo netgo package net import "context" -func init() { netGo = true } - type addrinfoErrno int func (eai addrinfoErrno) Error() string { return "" } diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go index 2ea86e074fb943..71d90560ac921f 100644 --- a/src/net/cgo_unix.go +++ b/src/net/cgo_unix.go @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) -// +build cgo -// +build !netgo -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build cgo && !netgo && unix package net @@ -253,12 +250,12 @@ func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, // These are roughly enough for the following: // -// Source Encoding Maximum length of single name entry -// Unicast DNS ASCII or <=253 + a NUL terminator -// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator -// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator -// the same as unicast DNS ASCII <=253 + a NUL terminator -// Local database various depends on implementation +// Source Encoding Maximum length of single name entry +// Unicast DNS ASCII or <=253 + a NUL terminator +// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator +// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator +// the same as unicast DNS ASCII <=253 + a NUL terminator +// Local database various depends on implementation const ( nameinfoLen = 64 maxNameinfoLen = 4096 @@ -323,7 +320,7 @@ func cgoLookupAddrPTR(addr string, sa *C.struct_sockaddr, salen C.socklen_t) (na break } } - return []string{absDomainName(b)}, nil + return []string{absDomainName(string(b))}, nil } func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *C.struct_sockaddr, salen C.socklen_t) { diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go index 1f3d9ea207ae93..af9f9dc3f2c8f2 100644 --- a/src/net/cgo_unix_test.go +++ b/src/net/cgo_unix_test.go @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) -// +build cgo -// +build !netgo -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build cgo && !netgo && unix package net diff --git a/src/net/cgo_windows.go b/src/net/cgo_windows.go index 1fd1f29787c860..6bb6cbbb2fdcfa 100644 --- a/src/net/cgo_windows.go +++ b/src/net/cgo_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !netgo -// +build cgo,!netgo package net diff --git a/src/net/conf.go b/src/net/conf.go index 6b9569cd92f7f7..b08bbc7d7a1c81 100644 --- a/src/net/conf.go +++ b/src/net/conf.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build !js package net import ( "internal/bytealg" + "internal/godebug" "os" "runtime" "sync" @@ -21,7 +21,7 @@ type conf struct { forceCgoLookupHost bool netGo bool // go DNS resolution forced - netCgo bool // cgo DNS resolution forced + netCgo bool // non-go DNS resolution forced (cgo, or win32) // machine has an /etc/mdns.allow file hasMDNSAllow bool @@ -49,9 +49,23 @@ func initConfVal() { confVal.dnsDebugLevel = debugLevel confVal.netGo = netGo || dnsMode == "go" confVal.netCgo = netCgo || dnsMode == "cgo" + if !confVal.netGo && !confVal.netCgo && (runtime.GOOS == "windows" || runtime.GOOS == "plan9") { + // Neither of these platforms actually use cgo. + // + // The meaning of "cgo" mode in the net package is + // really "the native OS way", which for libc meant + // cgo on the original platforms that motivated + // PreferGo support before Windows and Plan9 got support, + // at which time the GODEBUG=netdns=go and GODEBUG=netdns=cgo + // names were already kinda locked in. + confVal.netCgo = true + } if confVal.dnsDebugLevel > 0 { defer func() { + if confVal.dnsDebugLevel > 1 { + println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo) + } switch { case confVal.netGo: if netGo { @@ -75,6 +89,10 @@ func initConfVal() { return } + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return + } + // If any environment-specified resolver options are specified, // force cgo. Note that LOCALDOMAIN can change behavior merely // by being specified with the empty string. @@ -129,7 +147,19 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde } fallbackOrder := hostLookupCgo if c.netGo || r.preferGo() { - fallbackOrder = hostLookupFilesDNS + switch c.goos { + case "windows": + // TODO(bradfitz): implement files-based + // lookup on Windows too? I guess /etc/hosts + // kinda exists on Windows. But for now, only + // do DNS. + fallbackOrder = hostLookupDNS + default: + fallbackOrder = hostLookupFilesDNS + } + } + if c.goos == "windows" || c.goos == "plan9" { + return fallbackOrder } if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { return fallbackOrder @@ -278,16 +308,18 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde // goDebugNetDNS parses the value of the GODEBUG "netdns" value. // The netdns value can be of the form: -// 1 // debug level 1 -// 2 // debug level 2 -// cgo // use cgo for DNS lookups -// go // use go for DNS lookups -// cgo+1 // use cgo for DNS lookups + debug level 1 -// 1+cgo // same -// cgo+2 // same, but debug level 2 +// +// 1 // debug level 1 +// 2 // debug level 2 +// cgo // use cgo for DNS lookups +// go // use go for DNS lookups +// cgo+1 // use cgo for DNS lookups + debug level 1 +// 1+cgo // same +// cgo+2 // same, but debug level 2 +// // etc. func goDebugNetDNS() (dnsMode string, debugLevel int) { - goDebug := goDebugString("netdns") + goDebug := godebug.Get("netdns") parsePart := func(s string) { if s == "" { return diff --git a/src/net/conf_netcgo.go b/src/net/conf_netcgo.go index 8f387ebc03e5cf..82d1bb643e19f0 100644 --- a/src/net/conf_netcgo.go +++ b/src/net/conf_netcgo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build netcgo -// +build netcgo package net diff --git a/src/net/conf_test.go b/src/net/conf_test.go index b1f2c55ea50909..5ae70550860197 100644 --- a/src/net/conf_test.go +++ b/src/net/conf_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/conn_test.go b/src/net/conn_test.go index 45e271c264267a..d168dda08e19e4 100644 --- a/src/net/conn_test.go +++ b/src/net/conn_test.go @@ -6,7 +6,6 @@ // tag. //go:build !js -// +build !js package net @@ -18,7 +17,7 @@ import ( // someTimeout is used just to test that net.Conn implementations // don't explode when their SetFooDeadline methods are called. // It isn't actually used for testing timeouts. -const someTimeout = 10 * time.Second +const someTimeout = 1 * time.Hour func TestConnAndListener(t *testing.T) { for i, network := range []string{"tcp", "unix", "unixpacket"} { @@ -27,10 +26,7 @@ func TestConnAndListener(t *testing.T) { continue } - ls, err := newLocalServer(network) - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, network) defer ls.teardown() ch := make(chan error, 1) handler := func(ls *localServer, ln Listener) { ls.transponder(ln, ch) } diff --git a/src/net/dial.go b/src/net/dial.go index 486ced0f2a99c2..c5383425666eb5 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -114,6 +114,7 @@ func minNonzeroTime(a, b time.Time) time.Time { // - now+Timeout // - d.Deadline // - the context's deadline +// // Or zero, if none of Timeout, Deadline, or context's deadline is set. func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Time) { if d.Timeout != 0 { // including negative, for historical reasons @@ -289,6 +290,7 @@ func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string // Dial will try each IP address in order until one succeeds. // // Examples: +// // Dial("tcp", "golang.org:http") // Dial("tcp", "192.0.2.1:http") // Dial("tcp", "198.51.100.1:80") @@ -304,6 +306,7 @@ func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string // behaves with a non-well known protocol number such as "0" or "255". // // Examples: +// // Dial("ip4:1", "192.0.2.1") // Dial("ip6:ipv6-icmp", "2001:db8::1") // Dial("ip6:58", "fe80::1%lo0") @@ -338,6 +341,7 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) { type sysDialer struct { Dialer network, address string + testHookDialTCP func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) } // Dial connects to the address on the named network. @@ -421,12 +425,7 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn primaries = addrs } - var c Conn - if len(fallbacks) > 0 { - c, err = sd.dialParallel(ctx, primaries, fallbacks) - } else { - c, err = sd.dialSerial(ctx, primaries) - } + c, err := sd.dialParallel(ctx, primaries, fallbacks) if err != nil { return nil, err } diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 723038c7a22a44..1256867da8db77 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -3,13 +3,14 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net import ( "bufio" "context" + "errors" + "fmt" "internal/testenv" "io" "os" @@ -60,10 +61,7 @@ func TestProhibitionaryDialArg(t *testing.T) { } func TestDialLocal(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() _, port, err := SplitHostPort(ln.Addr().String()) if err != nil { @@ -179,31 +177,9 @@ func dialClosedPort(t *testing.T) (dialLatency time.Duration) { } func TestDialParallel(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - - if !supportsIPv4() || !supportsIPv6() { - t.Skip("both IPv4 and IPv6 are required") - } - - closedPortDelay := dialClosedPort(t) - const instant time.Duration = 0 const fallbackDelay = 200 * time.Millisecond - // Some cases will run quickly when "connection refused" is fast, - // or trigger the fallbackDelay on Windows. This value holds the - // lesser of the two delays. - var closedPortOrFallbackDelay time.Duration - if closedPortDelay < fallbackDelay { - closedPortOrFallbackDelay = closedPortDelay - } else { - closedPortOrFallbackDelay = fallbackDelay - } - - origTestHookDialTCP := testHookDialTCP - defer func() { testHookDialTCP = origTestHookDialTCP }() - testHookDialTCP = slowDialTCP - nCopies := func(s string, n int) []string { out := make([]string, n) for i := 0; i < n; i++ { @@ -227,31 +203,21 @@ func TestDialParallel(t *testing.T) { // Primary is slow; fallback should kick in. {[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay}, // Skip a "connection refused" in the primary thread. - {[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, closedPortDelay}, - {[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, closedPortDelay}, + {[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, instant}, + {[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, instant}, // Skip a "connection refused" in the fallback thread. - {[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay + closedPortDelay}, + {[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay}, // Primary refused, fallback without delay. - {[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, closedPortOrFallbackDelay}, - {[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, closedPortOrFallbackDelay}, + {[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, instant}, + {[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, instant}, // Everything is refused. - {[]string{"127.0.0.1"}, []string{}, "tcp4", false, closedPortDelay}, + {[]string{"127.0.0.1"}, []string{}, "tcp4", false, instant}, // Nothing to do; fail instantly. {[]string{}, []string{}, "", false, instant}, // Connecting to tons of addresses should not trip the deadline. {nCopies("::1", 1000), []string{}, "", true, instant}, } - handler := func(dss *dualStackServer, ln Listener) { - for { - c, err := ln.Accept() - if err != nil { - return - } - c.Close() - } - } - // Convert a list of IP strings into TCPAddrs. makeAddrs := func(ips []string, port string) addrList { var out addrList @@ -266,76 +232,73 @@ func TestDialParallel(t *testing.T) { } for i, tt := range testCases { - dss, err := newDualStackServer() - if err != nil { - t.Fatal(err) - } - defer dss.teardown() - if err := dss.buildup(handler); err != nil { - t.Fatal(err) - } - if tt.teardownNetwork != "" { - // Destroy one of the listening sockets, creating an unreachable port. - dss.teardownNetwork(tt.teardownNetwork) - } + i, tt := i, tt + t.Run(fmt.Sprint(i), func(t *testing.T) { + dialTCP := func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) { + n := "tcp6" + if raddr.IP.To4() != nil { + n = "tcp4" + } + if n == tt.teardownNetwork { + return nil, errors.New("unreachable") + } + if r := raddr.IP.String(); r == slowDst4 || r == slowDst6 { + <-ctx.Done() + return nil, ctx.Err() + } + return &TCPConn{}, nil + } - primaries := makeAddrs(tt.primaries, dss.port) - fallbacks := makeAddrs(tt.fallbacks, dss.port) - d := Dialer{ - FallbackDelay: fallbackDelay, - } - startTime := time.Now() - sd := &sysDialer{ - Dialer: d, - network: "tcp", - address: "?", - } - c, err := sd.dialParallel(context.Background(), primaries, fallbacks) - elapsed := time.Since(startTime) + primaries := makeAddrs(tt.primaries, "80") + fallbacks := makeAddrs(tt.fallbacks, "80") + d := Dialer{ + FallbackDelay: fallbackDelay, + } + const forever = 60 * time.Minute + if tt.expectElapsed == instant { + d.FallbackDelay = forever + } + startTime := time.Now() + sd := &sysDialer{ + Dialer: d, + network: "tcp", + address: "?", + testHookDialTCP: dialTCP, + } + c, err := sd.dialParallel(context.Background(), primaries, fallbacks) + elapsed := time.Since(startTime) - if c != nil { - c.Close() - } + if c != nil { + c.Close() + } - if tt.expectOk && err != nil { - t.Errorf("#%d: got %v; want nil", i, err) - } else if !tt.expectOk && err == nil { - t.Errorf("#%d: got nil; want non-nil", i) - } + if tt.expectOk && err != nil { + t.Errorf("#%d: got %v; want nil", i, err) + } else if !tt.expectOk && err == nil { + t.Errorf("#%d: got nil; want non-nil", i) + } - // We used to always use 95 milliseconds as the slop, - // but that was flaky on Windows. See issue 35616. - slop := 95 * time.Millisecond - if fifth := tt.expectElapsed / 5; fifth > slop { - slop = fifth - } - expectElapsedMin := tt.expectElapsed - slop - expectElapsedMax := tt.expectElapsed + slop - if elapsed < expectElapsedMin { - t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin) - } else if elapsed > expectElapsedMax { - t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax) - } + if elapsed < tt.expectElapsed || elapsed >= forever { + t.Errorf("#%d: got %v; want >= %v, < forever", i, elapsed, tt.expectElapsed) + } - // Repeat each case, ensuring that it can be canceled quickly. - ctx, cancel := context.WithCancel(context.Background()) - var wg sync.WaitGroup - wg.Add(1) - go func() { - time.Sleep(5 * time.Millisecond) - cancel() - wg.Done() - }() - startTime = time.Now() - c, err = sd.dialParallel(ctx, primaries, fallbacks) - if c != nil { - c.Close() - } - elapsed = time.Now().Sub(startTime) - if elapsed > 100*time.Millisecond { - t.Errorf("#%d (cancel): got %v; want <= 100ms", i, elapsed) - } - wg.Wait() + // Repeat each case, ensuring that it can be canceled. + ctx, cancel := context.WithCancel(context.Background()) + var wg sync.WaitGroup + wg.Add(1) + go func() { + time.Sleep(5 * time.Millisecond) + cancel() + wg.Done() + }() + // Ignore errors, since all we care about is that the + // call can be canceled. + c, _ = sd.dialParallel(ctx, primaries, fallbacks) + if c != nil { + c.Close() + } + wg.Wait() + }) } } @@ -433,14 +396,25 @@ func TestDialParallelSpuriousConnection(t *testing.T) { readDeadline = time.Now().Add(5 * time.Second) } - var wg sync.WaitGroup - wg.Add(2) + var closed sync.WaitGroup + closed.Add(2) handler := func(dss *dualStackServer, ln Listener) { // Accept one connection per address. c, err := ln.Accept() if err != nil { t.Fatal(err) } + + // Workaround for https://go.dev/issue/37795. + // On arm64 macOS (current as of macOS 12.4), + // reading from a socket at the same time as the client + // is closing it occasionally hangs for 60 seconds before + // returning ECONNRESET. Sleep for a bit to give the + // socket time to close before trying to read from it. + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + time.Sleep(10 * time.Millisecond) + } + // The client should close itself, without sending data. c.SetReadDeadline(readDeadline) var b [1]byte @@ -448,7 +422,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) { t.Errorf("got %v; want %v", err, io.EOF) } c.Close() - wg.Done() + closed.Done() } dss, err := newDualStackServer() if err != nil { @@ -461,12 +435,16 @@ func TestDialParallelSpuriousConnection(t *testing.T) { const fallbackDelay = 100 * time.Millisecond + var dialing sync.WaitGroup + dialing.Add(2) origTestHookDialTCP := testHookDialTCP defer func() { testHookDialTCP = origTestHookDialTCP }() testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) { - // Sleep long enough for Happy Eyeballs to kick in, and inhibit cancellation. + // Wait until Happy Eyeballs kicks in and both connections are dialing, + // and inhibit cancellation. // This forces dialParallel to juggle two successful connections. - time.Sleep(fallbackDelay * 2) + dialing.Done() + dialing.Wait() // Now ignore the provided context (which will be canceled) and use a // different one to make sure this completes with a valid connection, @@ -500,7 +478,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) { c.Close() // The server should've seen both connections. - wg.Wait() + closed.Wait() } func TestDialerPartialDeadline(t *testing.T) { @@ -538,6 +516,9 @@ func TestDialerPartialDeadline(t *testing.T) { } } +// isEADDRINUSE reports whether err is syscall.EADDRINUSE. +var isEADDRINUSE = func(err error) bool { return false } + func TestDialerLocalAddr(t *testing.T) { if !supportsIPv4() || !supportsIPv6() { t.Skip("both IPv4 and IPv6 are required") @@ -593,7 +574,9 @@ func TestDialerLocalAddr(t *testing.T) { {"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, } + issue34264Index := -1 if supportsIPv4map() { + issue34264Index = len(tests) tests = append(tests, test{ "tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil, }) @@ -615,20 +598,16 @@ func TestDialerLocalAddr(t *testing.T) { c.Close() } } - var err error var lss [2]*localServer for i, network := range []string{"tcp4", "tcp6"} { - lss[i], err = newLocalServer(network) - if err != nil { - t.Fatal(err) - } + lss[i] = newLocalServer(t, network) defer lss[i].teardown() if err := lss[i].buildup(handler); err != nil { t.Fatal(err) } } - for _, tt := range tests { + for i, tt := range tests { d := &Dialer{LocalAddr: tt.laddr} var addr string ip := ParseIP(tt.raddr) @@ -640,7 +619,15 @@ func TestDialerLocalAddr(t *testing.T) { } c, err := d.Dial(tt.network, addr) if err == nil && tt.error != nil || err != nil && tt.error == nil { - t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error) + if i == issue34264Index && runtime.GOOS == "freebsd" && isEADDRINUSE(err) { + // https://golang.org/issue/34264: FreeBSD through at least version 12.2 + // has been observed to fail with EADDRINUSE when dialing from an IPv6 + // local address to an IPv4 remote address. + t.Logf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error) + t.Logf("(spurious EADDRINUSE ignored on freebsd: see https://golang.org/issue/34264)") + } else { + t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error) + } } if err != nil { if perr := parseDialError(err); perr != nil { @@ -713,10 +700,7 @@ func TestDialerKeepAlive(t *testing.T) { c.Close() } } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -751,6 +735,12 @@ func TestDialerKeepAlive(t *testing.T) { func TestDialCancel(t *testing.T) { mustHaveExternalNetwork(t) + if strings.HasPrefix(testenv.Builder(), "darwin-arm64") { + // The darwin-arm64 machines run in an environment that's not + // compatible with this test. + t.Skipf("builder %q gives no route to host for 198.18.0.0", testenv.Builder()) + } + blackholeIPPort := JoinHostPort(slowDst4, "1234") if !supportsIPv4() { blackholeIPPort = JoinHostPort(slowDst6, "1234") @@ -814,10 +804,7 @@ func TestCancelAfterDial(t *testing.T) { t.Skip("avoiding time.Sleep") } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") var wg sync.WaitGroup wg.Add(1) @@ -920,11 +907,7 @@ func TestDialerControl(t *testing.T) { if !testableNetwork(network) { continue } - ln, err := newLocalListener(network) - if err != nil { - t.Error(err) - continue - } + ln := newLocalListener(t, network) defer ln.Close() d := Dialer{Control: controlOnConnSetup} c, err := d.Dial(network, ln.Addr().String()) @@ -940,11 +923,7 @@ func TestDialerControl(t *testing.T) { if !testableNetwork(network) { continue } - c1, err := newLocalPacketListener(network) - if err != nil { - t.Error(err) - continue - } + c1 := newLocalPacketListener(t, network) if network == "unixgram" { defer os.Remove(c1.LocalAddr().String()) } @@ -980,10 +959,7 @@ func (contextWithNonZeroDeadline) Deadline() (time.Time, bool) { } func TestDialWithNonZeroDeadline(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() _, port, err := SplitHostPort(ln.Addr().String()) if err != nil { diff --git a/src/net/dial_unix_test.go b/src/net/dial_unix_test.go index 108b973099715c..d0df0b71eaeea5 100644 --- a/src/net/dial_unix_test.go +++ b/src/net/dial_unix_test.go @@ -2,18 +2,24 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net import ( "context" + "errors" "syscall" "testing" "time" ) +func init() { + isEADDRINUSE = func(err error) bool { + return errors.Is(err, syscall.EADDRINUSE) + } +} + // Issue 16523 func TestDialContextCancelRace(t *testing.T) { oldConnectFunc := connectFunc @@ -25,10 +31,7 @@ func TestDialContextCancelRace(t *testing.T) { testHookCanceledDial = oldTestHookCanceledDial }() - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") listenerDone := make(chan struct{}) go func() { defer close(listenerDone) diff --git a/src/net/dnsclient.go b/src/net/dnsclient.go index 1bbe39650bbac2..b609dbd468f698 100644 --- a/src/net/dnsclient.go +++ b/src/net/dnsclient.go @@ -5,6 +5,7 @@ package net import ( + "internal/bytealg" "internal/itoa" "sort" @@ -12,13 +13,10 @@ import ( ) // provided by runtime -func fastrand() uint32 +func fastrandu() uint func randInt() int { - x, y := fastrand(), fastrand() // 32-bit halves - u := uint(x)<<31 ^ uint(int32(y)) // full uint, even on 64-bit systems; avoid 32-bit shift on 32-bit systems - i := int(u >> 1) // clear sign bit, even on 32-bit systems - return i + return int(fastrandu() >> 1) // clear sign bit } func randIntn(n int) int { @@ -75,6 +73,11 @@ func equalASCIIName(x, y dnsmessage.Name) bool { // (currently restricted to hostname-compatible "preferred name" LDH labels and // SRV-like "underscore labels"; see golang.org/issue/12421). func isDomainName(s string) bool { + // The root domain name is valid. See golang.org/issue/45715. + if s == "." { + return true + } + // See RFC 1035, RFC 3696. // Presentation format has dots before every label except the first, and the // terminal empty label is optional here because we assume fully-qualified @@ -136,18 +139,11 @@ func isDomainName(s string) bool { // It's hard to tell so we settle on the heuristic that names without dots // (like "localhost" or "myhost") do not get trailing dots, but any other // names do. -func absDomainName(b []byte) string { - hasDots := false - for _, x := range b { - if x == '.' { - hasDots = true - break - } - } - if hasDots && b[len(b)-1] != '.' { - b = append(b, '.') +func absDomainName(s string) string { + if bytealg.IndexByteString(s, '.') != -1 && s[len(s)-1] != '.' { + s += "." } - return string(b) + return s } // An SRV represents a single DNS SRV record. diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 50e9bb0f20dd70..2bf01b314cdeea 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build !js // DNS client: see RFC 1035. // Has to be linked into package net for Dial. @@ -21,6 +20,7 @@ import ( "internal/itoa" "io" "os" + "runtime" "sync" "time" @@ -31,6 +31,10 @@ const ( // to be used as a useTCP parameter to exchange useTCPOnly = true useUDPOrTCP = false + + // Maximum DNS packet size. + // Value taken from https://dnsflagday.net/2020/. + maxDNSPacketSize = 1232 ) var ( @@ -50,19 +54,34 @@ var ( func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) { id = uint16(randInt()) b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true}) - b.EnableCompression() if err := b.StartQuestions(); err != nil { return 0, nil, nil, err } if err := b.Question(q); err != nil { return 0, nil, nil, err } + + // Accept packets up to maxDNSPacketSize. RFC 6891. + if err := b.StartAdditionals(); err != nil { + return 0, nil, nil, err + } + var rh dnsmessage.ResourceHeader + if err := rh.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false); err != nil { + return 0, nil, nil, err + } + if err := b.OPTResource(rh, dnsmessage.OPTResource{}); err != nil { + return 0, nil, nil, err + } + tcpReq, err = b.Finish() + if err != nil { + return 0, nil, nil, err + } udpReq = tcpReq[2:] l := len(tcpReq) - 2 tcpReq[0] = byte(l >> 8) tcpReq[1] = byte(l) - return id, udpReq, tcpReq, err + return id, udpReq, tcpReq, nil } func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool { @@ -83,7 +102,7 @@ func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) return dnsmessage.Parser{}, dnsmessage.Header{}, err } - b = make([]byte, 512) // see RFC 1035 + b = make([]byte, maxDNSPacketSize) for { n, err := c.Read(b) if err != nil { @@ -362,12 +381,21 @@ func (conf *resolverConfig) tryUpdate(name string) { } conf.lastChecked = now - var mtime time.Time - if fi, err := os.Stat(name); err == nil { - mtime = fi.ModTime() - } - if mtime.Equal(conf.dnsConfig.mtime) { - return + switch runtime.GOOS { + case "windows": + // There's no file on disk, so don't bother checking + // and failing. + // + // The Windows implementation of dnsReadConfig (called + // below) ignores the name. + default: + var mtime time.Time + if fi, err := os.Stat(name); err == nil { + mtime = fi.ModTime() + } + if mtime.Equal(conf.dnsConfig.mtime) { + return + } } dnsConf := dnsReadConfig(name) @@ -453,7 +481,7 @@ func (conf *dnsConfig) nameList(name string) []string { // Check name length (see isDomainName). l := len(name) rooted := l > 0 && name[l-1] == '.' - if l > 254 || l == 254 && rooted { + if l > 254 || l == 254 && !rooted { return nil } @@ -641,7 +669,7 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name strin // We asked for recursion, so it should have included all the // answers we need in this one packet. // - // Further, RFC 1035 section 4.3.1 says that "the recursive + // Further, RFC 1034 section 4.3.1 says that "the recursive // response to a query will be... The answer to the query, // possibly preface by one or more CNAME RRs that specify // aliases encountered on the way to an answer." diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index 350ad5def797bc..17798e434b146f 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net @@ -882,7 +881,7 @@ func (f *fakeDNSPacketConn) Close() error { func TestIgnoreDNSForgeries(t *testing.T) { c, s := Pipe() go func() { - b := make([]byte, 512) + b := make([]byte, maxDNSPacketSize) n, err := s.Read(b) if err != nil { t.Error(err) @@ -2121,3 +2120,227 @@ func TestNullMX(t *testing.T) { t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0]) } } + +func TestRootNS(t *testing.T) { + // See https://golang.org/issue/45715. + fake := fakeDNSServer{ + rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, + }, + Questions: q.Questions, + Answers: []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeNS, + Class: dnsmessage.ClassINET, + }, + Body: &dnsmessage.NSResource{ + NS: dnsmessage.MustNewName("i.root-servers.net."), + }, + }, + }, + } + return r, nil + }, + } + r := Resolver{PreferGo: true, Dial: fake.DialContext} + rrset, err := r.LookupNS(context.Background(), ".") + if err != nil { + t.Fatalf("LookupNS: %v", err) + } + if want := []*NS{&NS{Host: "i.root-servers.net."}}; !reflect.DeepEqual(rrset, want) { + records := []string{} + for _, rr := range rrset { + records = append(records, fmt.Sprintf("%v", rr)) + } + t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0]) + } +} + +// Test that we advertise support for a larger DNS packet size. +// This isn't a great test as it just tests the dnsmessage package +// against itself. +func TestDNSPacketSize(t *testing.T) { + fake := fakeDNSServer{ + rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + if len(q.Additionals) == 0 { + t.Error("missing EDNS record") + } else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok { + t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0]) + } else if len(opt.Options) != 0 { + t.Errorf("found %d Options, expected none", len(opt.Options)) + } else { + got := int(q.Additionals[0].Header.Class) + t.Logf("EDNS packet size == %d", got) + if got != maxDNSPacketSize { + t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize) + } + } + + // Hand back a dummy answer to verify that + // LookupIPAddr completes. + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, + }, + Questions: q.Questions, + } + if q.Questions[0].Type == dnsmessage.TypeA { + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, + }, + }, + } + } + return r, nil + }, + } + + r := &Resolver{PreferGo: true, Dial: fake.DialContext} + if _, err := r.LookupIPAddr(context.Background(), "go.dev"); err != nil { + t.Errorf("lookup failed: %v", err) + } +} + +func TestLongDNSNames(t *testing.T) { + const longDNSsuffix = ".go.dev." + const longDNSsuffixNoEndingDot = ".go.dev" + + var longDNSPrefix = strings.Repeat("verylongdomainlabel.", 20) + + var longDNSNamesTests = []struct { + req string + fail bool + }{ + {req: longDNSPrefix[:255-len(longDNSsuffix)] + longDNSsuffix, fail: true}, + {req: longDNSPrefix[:254-len(longDNSsuffix)] + longDNSsuffix}, + {req: longDNSPrefix[:253-len(longDNSsuffix)] + longDNSsuffix}, + + {req: longDNSPrefix[:253-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot}, + {req: longDNSPrefix[:254-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot, fail: true}, + } + + fake := fakeDNSServer{ + rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, + }, + Questions: q.Questions, + Answers: []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: q.Questions[0].Type, + Class: dnsmessage.ClassINET, + }, + }, + }, + } + + switch q.Questions[0].Type { + case dnsmessage.TypeA: + r.Answers[0].Body = &dnsmessage.AResource{A: TestAddr} + case dnsmessage.TypeAAAA: + r.Answers[0].Body = &dnsmessage.AAAAResource{AAAA: TestAddr6} + case dnsmessage.TypeTXT: + r.Answers[0].Body = &dnsmessage.TXTResource{TXT: []string{"."}} + case dnsmessage.TypeMX: + r.Answers[0].Body = &dnsmessage.MXResource{ + MX: dnsmessage.MustNewName("go.dev."), + } + case dnsmessage.TypeNS: + r.Answers[0].Body = &dnsmessage.NSResource{ + NS: dnsmessage.MustNewName("go.dev."), + } + case dnsmessage.TypeSRV: + r.Answers[0].Body = &dnsmessage.SRVResource{ + Target: dnsmessage.MustNewName("go.dev."), + } + default: + panic("unknown dnsmessage type") + } + + return r, nil + }, + } + + r := &Resolver{PreferGo: true, Dial: fake.DialContext} + + methodTests := []string{"CNAME", "Host", "IP", "IPAddr", "MX", "NS", "NetIP", "SRV", "TXT"} + query := func(t string, req string) error { + switch t { + case "CNAME": + _, err := r.LookupCNAME(context.Background(), req) + return err + case "Host": + _, err := r.LookupHost(context.Background(), req) + return err + case "IP": + _, err := r.LookupIP(context.Background(), "ip", req) + return err + case "IPAddr": + _, err := r.LookupIPAddr(context.Background(), req) + return err + case "MX": + _, err := r.LookupMX(context.Background(), req) + return err + case "NS": + _, err := r.LookupNS(context.Background(), req) + return err + case "NetIP": + _, err := r.LookupNetIP(context.Background(), "ip", req) + return err + case "SRV": + const service = "service" + const proto = "proto" + req = req[len(service)+len(proto)+4:] + _, _, err := r.LookupSRV(context.Background(), service, proto, req) + return err + case "TXT": + _, err := r.LookupTXT(context.Background(), req) + return err + } + panic("unknown query method") + } + + for i, v := range longDNSNamesTests { + for _, testName := range methodTests { + err := query(testName, v.req) + if v.fail { + if err == nil { + t.Errorf("%v: Lookup%v: unexpected success", i, testName) + break + } + + expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: v.req, IsNotFound: true} + var dnsErr *DNSError + errors.As(err, &dnsErr) + if dnsErr == nil || *dnsErr != expectedErr { + t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err) + } + break + } + if err != nil { + t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err) + } + } + } +} diff --git a/src/net/dnsconfig.go b/src/net/dnsconfig.go new file mode 100644 index 00000000000000..091b5483013f5a --- /dev/null +++ b/src/net/dnsconfig.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "os" + "sync/atomic" + "time" +) + +var ( + defaultNS = []string{"127.0.0.1:53", "[::1]:53"} + getHostname = os.Hostname // variable for testing +) + +type dnsConfig struct { + servers []string // server addresses (in host:port form) to use + search []string // rooted suffixes to append to local name + ndots int // number of dots in name to trigger absolute lookup + timeout time.Duration // wait before giving up on a query, including retries + attempts int // lost packets before giving up on server + rotate bool // round robin among servers + unknownOpt bool // anything unknown was encountered + lookup []string // OpenBSD top-level database "lookup" order + err error // any error that occurs during open of resolv.conf + mtime time.Time // time of resolv.conf modification + soffset uint32 // used by serverOffset + singleRequest bool // use sequential A and AAAA queries instead of parallel queries + useTCP bool // force usage of TCP for DNS resolutions +} + +// serverOffset returns an offset that can be used to determine +// indices of servers in c.servers when making queries. +// When the rotate option is enabled, this offset increases. +// Otherwise it is always 0. +func (c *dnsConfig) serverOffset() uint32 { + if c.rotate { + return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start + } + return 0 +} diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index db9213a13fd3e9..65098f68275e4b 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build !js && !windows // Read system DNS config from /etc/resolv.conf @@ -11,32 +10,9 @@ package net import ( "internal/bytealg" - "os" - "sync/atomic" "time" ) -var ( - defaultNS = []string{"127.0.0.1:53", "[::1]:53"} - getHostname = os.Hostname // variable for testing -) - -type dnsConfig struct { - servers []string // server addresses (in host:port form) to use - search []string // rooted suffixes to append to local name - ndots int // number of dots in name to trigger absolute lookup - timeout time.Duration // wait before giving up on a query, including retries - attempts int // lost packets before giving up on server - rotate bool // round robin among servers - unknownOpt bool // anything unknown was encountered - lookup []string // OpenBSD top-level database "lookup" order - err error // any error that occurs during open of resolv.conf - mtime time.Time // time of resolv.conf modification - soffset uint32 // used by serverOffset - singleRequest bool // use sequential A and AAAA queries instead of parallel queries - useTCP bool // force usage of TCP for DNS resolutions -} - // See resolv.conf(5) on a Linux machine. func dnsReadConfig(filename string) *dnsConfig { conf := &dnsConfig{ @@ -88,9 +64,13 @@ func dnsReadConfig(filename string) *dnsConfig { } case "search": // set search path to given servers - conf.search = make([]string, len(f)-1) - for i := 0; i < len(conf.search); i++ { - conf.search[i] = ensureRooted(f[i+1]) + conf.search = make([]string, 0, len(f)-1) + for i := 1; i < len(f); i++ { + name := ensureRooted(f[i]) + if name == "." { + continue + } + conf.search = append(conf.search, name) } case "options": // magic options @@ -133,6 +113,9 @@ func dnsReadConfig(filename string) *dnsConfig { // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports // https://man.openbsd.org/resolv.conf.5 conf.useTCP = true + case s == "edns0": + // We use EDNS by default. + // Ignore this option. default: conf.unknownOpt = true } @@ -157,17 +140,6 @@ func dnsReadConfig(filename string) *dnsConfig { return conf } -// serverOffset returns an offset that can be used to determine -// indices of servers in c.servers when making queries. -// When the rotate option is enabled, this offset increases. -// Otherwise it is always 0. -func (c *dnsConfig) serverOffset() uint32 { - if c.rotate { - return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start - } - return 0 -} - func dnsDefaultSearch() []string { hn, err := getHostname() if err != nil { diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go index 0e2317c46912f4..9be751f2e3e7c1 100644 --- a/src/net/dnsconfig_unix_test.go +++ b/src/net/dnsconfig_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net @@ -53,6 +52,16 @@ var dnsReadConfigTests = []struct { attempts: 2, }, }, + { + name: "testdata/search-single-dot-resolv.conf", + want: &dnsConfig{ + servers: []string{"8.8.8.8:53"}, + search: []string{}, + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + }, + }, { name: "testdata/empty-resolv.conf", want: &dnsConfig{ @@ -167,6 +176,9 @@ func TestDNSReadConfig(t *testing.T) { getHostname = func() (string, error) { return "host.domain.local", nil } for _, tt := range dnsReadConfigTests { + if len(tt.want.search) == 0 { + tt.want.search = append(tt.want.search, dnsDefaultSearch()...) + } conf := dnsReadConfig(tt.name) if conf.err != nil { t.Fatal(conf.err) diff --git a/src/net/dnsconfig_windows.go b/src/net/dnsconfig_windows.go new file mode 100644 index 00000000000000..5d640da1d740c9 --- /dev/null +++ b/src/net/dnsconfig_windows.go @@ -0,0 +1,58 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "syscall" + "time" +) + +func dnsReadConfig(ignoredFilename string) (conf *dnsConfig) { + conf = &dnsConfig{ + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + } + defer func() { + if len(conf.servers) == 0 { + conf.servers = defaultNS + } + }() + aas, err := adapterAddresses() + if err != nil { + return + } + // TODO(bradfitz): this just collects all the DNS servers on all + // the interfaces in some random order. It should order it by + // default route, or only use the default route(s) instead. + // In practice, however, it mostly works. + for _, aa := range aas { + for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next { + sa, err := dns.Address.Sockaddr.Sockaddr() + if err != nil { + continue + } + var ip IP + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + ip = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) + case *syscall.SockaddrInet6: + ip = make(IP, IPv6len) + copy(ip, sa.Addr[:]) + if ip[0] == 0xfe && ip[1] == 0xc0 { + // Ignore these fec0/10 ones. Windows seems to + // populate them as defaults on its misc rando + // interfaces. + continue + } + default: + // Unexpected type. + continue + } + conf.servers = append(conf.servers, JoinHostPort(ip.String(), "53")) + } + } + return conf +} diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go index d851bf75667d3a..28b7c680feb16a 100644 --- a/src/net/dnsname_test.go +++ b/src/net/dnsname_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net diff --git a/src/net/error_plan9_test.go b/src/net/error_plan9_test.go index d7c7f1487fb143..1270af19e54d5b 100644 --- a/src/net/error_plan9_test.go +++ b/src/net/error_plan9_test.go @@ -17,3 +17,7 @@ func isPlatformError(err error) bool { _, ok := err.(syscall.ErrorString) return ok } + +func isENOBUFS(err error) bool { + return false // ENOBUFS is Unix-specific +} diff --git a/src/net/error_posix.go b/src/net/error_posix.go index 50eb66fc61740e..8fc7d0bb7384e4 100644 --- a/src/net/error_posix.go +++ b/src/net/error_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/error_posix_test.go b/src/net/error_posix_test.go index ea52a45ee89db6..081176f771aebf 100644 --- a/src/net/error_posix_test.go +++ b/src/net/error_posix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package net diff --git a/src/net/error_test.go b/src/net/error_test.go index c304390819a915..4467dc11b20965 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -554,10 +553,7 @@ third: } func TestCloseError(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { @@ -665,10 +661,7 @@ func TestAcceptError(t *testing.T) { c.Close() } } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") if err := ls.buildup(handler); err != nil { ls.teardown() t.Fatal(err) @@ -774,10 +767,7 @@ func TestFileError(t *testing.T) { t.Error("should fail") } - ln, err = newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln = newLocalListener(t, "tcp") for i := 0; i < 3; i++ { f, err := ln.(*TCPListener).File() @@ -805,3 +795,12 @@ func parseLookupPortError(nestedErr error) error { } return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr) } + +func TestContextError(t *testing.T) { + if !errors.Is(errCanceled, context.Canceled) { + t.Error("errCanceled is not context.Canceled") + } + if !errors.Is(errTimeout, context.DeadlineExceeded) { + t.Error("errTimeout is not context.DeadlineExceeded") + } +} diff --git a/src/net/error_unix.go b/src/net/error_unix.go index d0b5e2ce96905e..1f9b6eb78ce870 100644 --- a/src/net/error_unix.go +++ b/src/net/error_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || js || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js linux netbsd openbsd solaris +//go:build unix || js package net diff --git a/src/net/error_unix_test.go b/src/net/error_unix_test.go index 533a45e648f118..291a7234f21555 100644 --- a/src/net/error_unix_test.go +++ b/src/net/error_unix_test.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build !plan9 && !windows -// +build !plan9,!windows package net import ( + "errors" "os" "syscall" ) @@ -33,3 +33,7 @@ func samePlatformError(err, want error) bool { } return err == want } + +func isENOBUFS(err error) bool { + return errors.Is(err, syscall.ENOBUFS) +} diff --git a/src/net/error_windows_test.go b/src/net/error_windows_test.go index 834a9de4411a01..25825f96f80753 100644 --- a/src/net/error_windows_test.go +++ b/src/net/error_windows_test.go @@ -4,7 +4,10 @@ package net -import "syscall" +import ( + "errors" + "syscall" +) var ( errTimedout = syscall.ETIMEDOUT @@ -17,3 +20,10 @@ func isPlatformError(err error) bool { _, ok := err.(syscall.Errno) return ok } + +func isENOBUFS(err error) bool { + // syscall.ENOBUFS is a completely made-up value on Windows: we don't expect + // a real system call to ever actually return it. However, since it is already + // defined in the syscall package we may as well check for it. + return errors.Is(err, syscall.ENOBUFS) +} diff --git a/src/net/example_test.go b/src/net/example_test.go index 72c7183c13469a..2c045d73a24bcb 100644 --- a/src/net/example_test.go +++ b/src/net/example_test.go @@ -124,6 +124,176 @@ func ExampleIP_DefaultMask() { // ffffff00 } +func ExampleIP_Equal() { + ipv4DNS := net.ParseIP("8.8.8.8") + ipv4Lo := net.ParseIP("127.0.0.1") + ipv6DNS := net.ParseIP("0:0:0:0:0:FFFF:0808:0808") + + fmt.Println(ipv4DNS.Equal(ipv4DNS)) + fmt.Println(ipv4DNS.Equal(ipv4Lo)) + fmt.Println(ipv4DNS.Equal(ipv6DNS)) + + // Output: + // true + // false + // true +} + +func ExampleIP_IsGlobalUnicast() { + ipv6Global := net.ParseIP("2000::") + ipv6UniqLocal := net.ParseIP("2000::") + ipv6Multi := net.ParseIP("FF00::") + + ipv4Private := net.ParseIP("10.255.0.0") + ipv4Public := net.ParseIP("8.8.8.8") + ipv4Broadcast := net.ParseIP("255.255.255.255") + + fmt.Println(ipv6Global.IsGlobalUnicast()) + fmt.Println(ipv6UniqLocal.IsGlobalUnicast()) + fmt.Println(ipv6Multi.IsGlobalUnicast()) + + fmt.Println(ipv4Private.IsGlobalUnicast()) + fmt.Println(ipv4Public.IsGlobalUnicast()) + fmt.Println(ipv4Broadcast.IsGlobalUnicast()) + + // Output: + // true + // true + // false + // true + // true + // false +} + +func ExampleIP_IsInterfaceLocalMulticast() { + ipv6InterfaceLocalMulti := net.ParseIP("ff01::1") + ipv6Global := net.ParseIP("2000::") + ipv4 := net.ParseIP("255.0.0.0") + + fmt.Println(ipv6InterfaceLocalMulti.IsInterfaceLocalMulticast()) + fmt.Println(ipv6Global.IsInterfaceLocalMulticast()) + fmt.Println(ipv4.IsInterfaceLocalMulticast()) + + // Output: + // true + // false + // false +} + +func ExampleIP_IsLinkLocalMulticast() { + ipv6LinkLocalMulti := net.ParseIP("ff02::2") + ipv6LinkLocalUni := net.ParseIP("fe80::") + ipv4LinkLocalMulti := net.ParseIP("224.0.0.0") + ipv4LinkLocalUni := net.ParseIP("169.254.0.0") + + fmt.Println(ipv6LinkLocalMulti.IsLinkLocalMulticast()) + fmt.Println(ipv6LinkLocalUni.IsLinkLocalMulticast()) + fmt.Println(ipv4LinkLocalMulti.IsLinkLocalMulticast()) + fmt.Println(ipv4LinkLocalUni.IsLinkLocalMulticast()) + + // Output: + // true + // false + // true + // false +} + +func ExampleIP_IsLinkLocalUnicast() { + ipv6LinkLocalUni := net.ParseIP("fe80::") + ipv6Global := net.ParseIP("2000::") + ipv4LinkLocalUni := net.ParseIP("169.254.0.0") + ipv4LinkLocalMulti := net.ParseIP("224.0.0.0") + + fmt.Println(ipv6LinkLocalUni.IsLinkLocalUnicast()) + fmt.Println(ipv6Global.IsLinkLocalUnicast()) + fmt.Println(ipv4LinkLocalUni.IsLinkLocalUnicast()) + fmt.Println(ipv4LinkLocalMulti.IsLinkLocalUnicast()) + + // Output: + // true + // false + // true + // false +} + +func ExampleIP_IsLoopback() { + ipv6Lo := net.ParseIP("::1") + ipv6 := net.ParseIP("ff02::1") + ipv4Lo := net.ParseIP("127.0.0.0") + ipv4 := net.ParseIP("128.0.0.0") + + fmt.Println(ipv6Lo.IsLoopback()) + fmt.Println(ipv6.IsLoopback()) + fmt.Println(ipv4Lo.IsLoopback()) + fmt.Println(ipv4.IsLoopback()) + + // Output: + // true + // false + // true + // false +} + +func ExampleIP_IsMulticast() { + ipv6Multi := net.ParseIP("FF00::") + ipv6LinkLocalMulti := net.ParseIP("ff02::1") + ipv6Lo := net.ParseIP("::1") + ipv4Multi := net.ParseIP("239.0.0.0") + ipv4LinkLocalMulti := net.ParseIP("224.0.0.0") + ipv4Lo := net.ParseIP("127.0.0.0") + + fmt.Println(ipv6Multi.IsMulticast()) + fmt.Println(ipv6LinkLocalMulti.IsMulticast()) + fmt.Println(ipv6Lo.IsMulticast()) + fmt.Println(ipv4Multi.IsMulticast()) + fmt.Println(ipv4LinkLocalMulti.IsMulticast()) + fmt.Println(ipv4Lo.IsMulticast()) + + // Output: + // true + // true + // false + // true + // true + // false +} + +func ExampleIP_IsPrivate() { + ipv6Private := net.ParseIP("fc00::") + ipv6Public := net.ParseIP("fe00::") + ipv4Private := net.ParseIP("10.255.0.0") + ipv4Public := net.ParseIP("11.0.0.0") + + fmt.Println(ipv6Private.IsPrivate()) + fmt.Println(ipv6Public.IsPrivate()) + fmt.Println(ipv4Private.IsPrivate()) + fmt.Println(ipv4Public.IsPrivate()) + + // Output: + // true + // false + // true + // false +} + +func ExampleIP_IsUnspecified() { + ipv6Unspecified := net.ParseIP("::") + ipv6Specified := net.ParseIP("fe00::") + ipv4Unspecified := net.ParseIP("0.0.0.0") + ipv4Specified := net.ParseIP("8.8.8.8") + + fmt.Println(ipv6Unspecified.IsUnspecified()) + fmt.Println(ipv6Specified.IsUnspecified()) + fmt.Println(ipv4Unspecified.IsUnspecified()) + fmt.Println(ipv4Specified.IsUnspecified()) + + // Output: + // true + // false + // true + // false +} + func ExampleIP_Mask() { ipv4Addr := net.ParseIP("192.0.2.1") // This mask corresponds to a /24 subnet for IPv4. @@ -140,6 +310,42 @@ func ExampleIP_Mask() { // 2001:db8:: } +func ExampleIP_String() { + ipv6 := net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + ipv4 := net.IPv4(10, 255, 0, 0) + + fmt.Println(ipv6.String()) + fmt.Println(ipv4.String()) + + // Output: + // fc00:: + // 10.255.0.0 +} + +func ExampleIP_To16() { + ipv6 := net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + ipv4 := net.IPv4(10, 255, 0, 0) + + fmt.Println(ipv6.To16()) + fmt.Println(ipv4.To16()) + + // Output: + // fc00:: + // 10.255.0.0 +} + +func ExampleIP_to4() { + ipv6 := net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + ipv4 := net.IPv4(10, 255, 0, 0) + + fmt.Println(ipv6.To4()) + fmt.Println(ipv4.To4()) + + // Output: + // + // 10.255.0.0 +} + func ExampleCIDRMask() { // This mask corresponds to a /31 subnet for IPv4. fmt.Println(net.CIDRMask(31, 32)) diff --git a/src/net/external_test.go b/src/net/external_test.go index b8753cc092bf1b..3a97011fe8570b 100644 --- a/src/net/external_test.go +++ b/src/net/external_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net diff --git a/src/net/fcntl_libc_test.go b/src/net/fcntl_libc_test.go index 0320d63a863213..5858865cf0a366 100644 --- a/src/net/fcntl_libc_test.go +++ b/src/net/fcntl_libc_test.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || solaris -// +build aix darwin solaris +//go:build aix || darwin || (openbsd && !mips64) || solaris package net import _ "unsafe" // for go:linkname // Implemented in the syscall package. +// //go:linkname fcntl syscall.fcntl func fcntl(fd int, cmd int, arg int) (int, error) diff --git a/src/net/fcntl_syscall_test.go b/src/net/fcntl_syscall_test.go index 0f04bb4ed65add..b9ac1d3effd664 100644 --- a/src/net/fcntl_syscall_test.go +++ b/src/net/fcntl_syscall_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) package net diff --git a/src/net/fd_posix.go b/src/net/fd_posix.go index 4703ff33a10c71..ffb9bcf8b9e8ca 100644 --- a/src/net/fd_posix.go +++ b/src/net/fd_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package net @@ -63,6 +62,17 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { runtime.KeepAlive(fd) return n, sa, wrapSyscallError(readFromSyscallName, err) } +func (fd *netFD) readFromInet4(p []byte, from *syscall.SockaddrInet4) (n int, err error) { + n, err = fd.pfd.ReadFromInet4(p, from) + runtime.KeepAlive(fd) + return n, wrapSyscallError(readFromSyscallName, err) +} + +func (fd *netFD) readFromInet6(p []byte, from *syscall.SockaddrInet6) (n int, err error) { + n, err = fd.pfd.ReadFromInet6(p, from) + runtime.KeepAlive(fd) + return n, wrapSyscallError(readFromSyscallName, err) +} func (fd *netFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int, sa syscall.Sockaddr, err error) { n, oobn, retflags, sa, err = fd.pfd.ReadMsg(p, oob, flags) @@ -70,6 +80,18 @@ func (fd *netFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int return n, oobn, retflags, sa, wrapSyscallError(readMsgSyscallName, err) } +func (fd *netFD) readMsgInet4(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet4) (n, oobn, retflags int, err error) { + n, oobn, retflags, err = fd.pfd.ReadMsgInet4(p, oob, flags, sa) + runtime.KeepAlive(fd) + return n, oobn, retflags, wrapSyscallError(readMsgSyscallName, err) +} + +func (fd *netFD) readMsgInet6(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet6) (n, oobn, retflags int, err error) { + n, oobn, retflags, err = fd.pfd.ReadMsgInet6(p, oob, flags, sa) + runtime.KeepAlive(fd) + return n, oobn, retflags, wrapSyscallError(readMsgSyscallName, err) +} + func (fd *netFD) Write(p []byte) (nn int, err error) { nn, err = fd.pfd.Write(p) runtime.KeepAlive(fd) @@ -82,12 +104,36 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { return n, wrapSyscallError(writeToSyscallName, err) } +func (fd *netFD) writeToInet4(p []byte, sa *syscall.SockaddrInet4) (n int, err error) { + n, err = fd.pfd.WriteToInet4(p, sa) + runtime.KeepAlive(fd) + return n, wrapSyscallError(writeToSyscallName, err) +} + +func (fd *netFD) writeToInet6(p []byte, sa *syscall.SockaddrInet6) (n int, err error) { + n, err = fd.pfd.WriteToInet6(p, sa) + runtime.KeepAlive(fd) + return n, wrapSyscallError(writeToSyscallName, err) +} + func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { n, oobn, err = fd.pfd.WriteMsg(p, oob, sa) runtime.KeepAlive(fd) return n, oobn, wrapSyscallError(writeMsgSyscallName, err) } +func (fd *netFD) writeMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (n int, oobn int, err error) { + n, oobn, err = fd.pfd.WriteMsgInet4(p, oob, sa) + runtime.KeepAlive(fd) + return n, oobn, wrapSyscallError(writeMsgSyscallName, err) +} + +func (fd *netFD) writeMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (n int, oobn int, err error) { + n, oobn, err = fd.pfd.WriteMsgInet6(p, oob, sa) + runtime.KeepAlive(fd) + return n, oobn, wrapSyscallError(writeMsgSyscallName, err) +} + func (fd *netFD) SetDeadline(t time.Time) error { return fd.pfd.SetDeadline(t) } diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index a7bbdd26b45840..a400c6075e67a2 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net @@ -92,12 +91,12 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc } // Start the "interrupter" goroutine, if this context might be canceled. - // (The background context cannot) // // The interrupter goroutine waits for the context to be done and // interrupts the dial (by altering the fd's write deadline, which // wakes up waitWrite). - if ctx != context.Background() { + ctxDone := ctx.Done() + if ctxDone != nil { // Wait for the interrupter goroutine to exit before returning // from connect. done := make(chan struct{}) @@ -117,7 +116,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc }() go func() { select { - case <-ctx.Done(): + case <-ctxDone: // Force the runtime's poller to immediately give up // waiting for writability, unblocking waitWrite // below. @@ -141,7 +140,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc // details. if err := fd.pfd.WaitWrite(); err != nil { select { - case <-ctx.Done(): + case <-ctxDone: return nil, mapErr(ctx.Err()) default: } diff --git a/src/net/file_stub.go b/src/net/file_stub.go index 9f988fe8993617..91df926a578914 100644 --- a/src/net/file_stub.go +++ b/src/net/file_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net diff --git a/src/net/file_test.go b/src/net/file_test.go index a70ef1b312ff12..ea2a218dfbc562 100644 --- a/src/net/file_test.go +++ b/src/net/file_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -45,10 +44,7 @@ func TestFileConn(t *testing.T) { var network, address string switch tt.network { case "udp": - c, err := newLocalPacketListener(tt.network) - if err != nil { - t.Fatal(err) - } + c := newLocalPacketListener(t, tt.network) defer c.Close() network = c.LocalAddr().Network() address = c.LocalAddr().String() @@ -62,10 +58,7 @@ func TestFileConn(t *testing.T) { var b [1]byte c.Read(b[:]) } - ls, err := newLocalServer(tt.network) - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, tt.network) defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -149,17 +142,17 @@ func TestFileListener(t *testing.T) { continue } - ln1, err := newLocalListener(tt.network) - if err != nil { - t.Fatal(err) - } + ln1 := newLocalListener(t, tt.network) switch tt.network { case "unix", "unixpacket": defer os.Remove(ln1.Addr().String()) } addr := ln1.Addr() - var f *os.File + var ( + f *os.File + err error + ) switch ln1 := ln1.(type) { case *TCPListener: f, err = ln1.File() @@ -241,17 +234,17 @@ func TestFilePacketConn(t *testing.T) { continue } - c1, err := newLocalPacketListener(tt.network) - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, tt.network) switch tt.network { case "unixgram": defer os.Remove(c1.LocalAddr().String()) } addr := c1.LocalAddr() - var f *os.File + var ( + f *os.File + err error + ) switch c1 := c1.(type) { case *UDPConn: f, err = c1.File() @@ -315,10 +308,7 @@ func TestFileCloseRace(t *testing.T) { c.Read(b[:]) } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) diff --git a/src/net/file_unix.go b/src/net/file_unix.go index 4520d4b839ed60..0df67db501211e 100644 --- a/src/net/file_unix.go +++ b/src/net/file_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net diff --git a/src/net/hook_unix.go b/src/net/hook_unix.go index b9153d1947f98c..fa82c7e52ba67b 100644 --- a/src/net/hook_unix.go +++ b/src/net/hook_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package net diff --git a/src/net/hosts.go b/src/net/hosts.go index 5c560f3756ed1a..e604031920b1fe 100644 --- a/src/net/hosts.go +++ b/src/net/hosts.go @@ -82,10 +82,10 @@ func readHosts() { continue } for i := 1; i < len(f); i++ { - name := absDomainName([]byte(f[i])) + name := absDomainName(f[i]) h := []byte(f[i]) lowerASCIIBytes(h) - key := absDomainName(h) + key := absDomainName(string(h)) hs[key] = append(hs[key], addr) is[addr] = append(is[addr], name) } @@ -106,11 +106,12 @@ func lookupStaticHost(host string) []string { defer hosts.Unlock() readHosts() if len(hosts.byName) != 0 { - // TODO(jbd,bradfitz): avoid this alloc if host is already all lowercase? - // or linear scan the byName map if it's small enough? - lowerHost := []byte(host) - lowerASCIIBytes(lowerHost) - if ips, ok := hosts.byName[absDomainName(lowerHost)]; ok { + if hasUpperCase(host) { + lowerHost := []byte(host) + lowerASCIIBytes(lowerHost) + host = string(lowerHost) + } + if ips, ok := hosts.byName[absDomainName(host)]; ok { ipsCp := make([]string, len(ips)) copy(ipsCp, ips) return ipsCp diff --git a/src/net/hosts_test.go b/src/net/hosts_test.go index 19c43999f9291c..72919140e9c79f 100644 --- a/src/net/hosts_test.go +++ b/src/net/hosts_test.go @@ -70,7 +70,7 @@ func TestLookupStaticHost(t *testing.T) { } func testStaticHost(t *testing.T, hostsPath string, ent staticHostEntry) { - ins := []string{ent.in, absDomainName([]byte(ent.in)), strings.ToLower(ent.in), strings.ToUpper(ent.in)} + ins := []string{ent.in, absDomainName(ent.in), strings.ToLower(ent.in), strings.ToUpper(ent.in)} for _, in := range ins { addrs := lookupStaticHost(in) if !reflect.DeepEqual(addrs, ent.out) { @@ -141,7 +141,7 @@ func TestLookupStaticAddr(t *testing.T) { func testStaticAddr(t *testing.T, hostsPath string, ent staticHostEntry) { hosts := lookupStaticAddr(ent.in) for i := range ent.out { - ent.out[i] = absDomainName([]byte(ent.out[i])) + ent.out[i] = absDomainName(ent.out[i]) } if !reflect.DeepEqual(hosts, ent.out) { t.Errorf("%s, lookupStaticAddr(%s) = %v; want %v", hostsPath, ent.in, hosts, ent.out) diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go index 0114da377b5647..bdb35a64e50b97 100644 --- a/src/net/http/cgi/child.go +++ b/src/net/http/cgi/child.go @@ -39,8 +39,8 @@ func Request() (*http.Request, error) { func envMap(env []string) map[string]string { m := make(map[string]string) for _, kv := range env { - if idx := strings.Index(kv, "="); idx != -1 { - m[kv[:idx]] = kv[idx+1:] + if k, v, ok := strings.Cut(kv, "="); ok { + m[k] = v } } return m diff --git a/src/net/http/cgi/host.go b/src/net/http/cgi/host.go index eff67caf4e6efb..349dda15acfb30 100644 --- a/src/net/http/cgi/host.go +++ b/src/net/http/cgi/host.go @@ -90,10 +90,11 @@ func (h *Handler) stderr() io.Writer { // removeLeadingDuplicates remove leading duplicate in environments. // It's possible to override environment like following. -// cgi.Handler{ -// ... -// Env: []string{"SCRIPT_FILENAME=foo.php"}, -// } +// +// cgi.Handler{ +// ... +// Env: []string{"SCRIPT_FILENAME=foo.php"}, +// } func removeLeadingDuplicates(env []string) (ret []string) { for i, e := range env { found := false @@ -137,7 +138,6 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { env := []string{ "SERVER_SOFTWARE=go", - "SERVER_NAME=" + req.Host, "SERVER_PROTOCOL=HTTP/1.1", "HTTP_HOST=" + req.Host, "GATEWAY_INTERFACE=CGI/1.1", @@ -157,6 +157,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { env = append(env, "REMOTE_ADDR="+req.RemoteAddr, "REMOTE_HOST="+req.RemoteAddr) } + if hostDomain, _, err := net.SplitHostPort(req.Host); err == nil { + env = append(env, "SERVER_NAME="+hostDomain) + } else { + env = append(env, "SERVER_NAME="+req.Host) + } + if req.TLS != nil { env = append(env, "HTTPS=on") } @@ -273,12 +279,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { break } headerLines++ - parts := strings.SplitN(string(line), ":", 2) - if len(parts) < 2 { + header, val, ok := strings.Cut(string(line), ":") + if !ok { h.printf("cgi: bogus header line: %s", string(line)) continue } - header, val := parts[0], parts[1] if !httpguts.ValidHeaderFieldName(header) { h.printf("cgi: invalid header name: %q", header) continue @@ -351,7 +356,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } -func (h *Handler) printf(format string, v ...interface{}) { +func (h *Handler) printf(format string, v ...any) { if h.Logger != nil { h.Logger.Printf(format, v...) } else { diff --git a/src/net/http/cgi/host_test.go b/src/net/http/cgi/host_test.go index fb869a67282d60..860e9b3e8f1b8f 100644 --- a/src/net/http/cgi/host_test.go +++ b/src/net/http/cgi/host_test.go @@ -8,7 +8,6 @@ package cgi import ( "bufio" - "bytes" "fmt" "io" "net" @@ -62,12 +61,12 @@ readlines: } linesRead++ trimmedLine := strings.TrimRight(line, "\r\n") - split := strings.SplitN(trimmedLine, "=", 2) - if len(split) != 2 { - t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v", - len(split), linesRead, line, m) + k, v, ok := strings.Cut(trimmedLine, "=") + if !ok { + t.Fatalf("Unexpected response from invalid line number %v: %q; existing map=%v", + linesRead, line, m) } - m[split[0]] = split[1] + m[k] = v } for key, expected := range expectedMap { @@ -114,7 +113,7 @@ func TestCGIBasicGet(t *testing.T) { "param-a": "b", "param-foo": "bar", "env-GATEWAY_INTERFACE": "CGI/1.1", - "env-HTTP_HOST": "example.com", + "env-HTTP_HOST": "example.com:80", "env-PATH_INFO": "", "env-QUERY_STRING": "foo=bar&a=b", "env-REMOTE_ADDR": "1.2.3.4", @@ -128,7 +127,7 @@ func TestCGIBasicGet(t *testing.T) { "env-SERVER_PORT": "80", "env-SERVER_SOFTWARE": "go", } - replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) + replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com:80\n\n", expectedMap) if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected { t.Errorf("got a Content-Type of %q; expected %q", got, expected) @@ -540,7 +539,7 @@ func TestEnvOverride(t *testing.T) { func TestHandlerStderr(t *testing.T) { check(t) - var stderr bytes.Buffer + var stderr strings.Builder h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", diff --git a/src/net/http/cgi/plan9_test.go b/src/net/http/cgi/plan9_test.go index f998bac6ea654c..b7ace3f81c94d5 100644 --- a/src/net/http/cgi/plan9_test.go +++ b/src/net/http/cgi/plan9_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package cgi diff --git a/src/net/http/cgi/posix_test.go b/src/net/http/cgi/posix_test.go index bc58ea94cc93b7..49b9470d4ae2a4 100644 --- a/src/net/http/cgi/posix_test.go +++ b/src/net/http/cgi/posix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package cgi diff --git a/src/net/http/client.go b/src/net/http/client.go index 4d380c65db9836..f57417ea10ff7a 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -23,6 +23,7 @@ import ( "sort" "strings" "sync" + "sync/atomic" "time" ) @@ -54,7 +55,6 @@ import ( // with the expectation that the Jar will insert those mutated cookies // with the updated values (assuming the origin matches). // If Jar is nil, the initial cookies are forwarded without change. -// type Client struct { // Transport specifies the mechanism by which individual // HTTP requests are made. @@ -392,7 +392,7 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi } timer := time.NewTimer(time.Until(deadline)) - var timedOut atomicBool + var timedOut atomic.Bool go func() { select { @@ -400,14 +400,14 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi doCancel() timer.Stop() case <-timer.C: - timedOut.setTrue() + timedOut.Store(true) doCancel() case <-stopTimerCh: timer.Stop() } }() - return stopTimer, timedOut.isSet + return stopTimer, timedOut.Load } // See 2 (end of page 4) https://www.ietf.org/rfc/rfc2617.txt @@ -424,11 +424,11 @@ func basicAuth(username, password string) string { // the following redirect codes, Get follows the redirect, up to a // maximum of 10 redirects: // -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) // // An error is returned if there were too many redirects or if there // was an HTTP protocol error. A non-2xx response doesn't cause an @@ -453,11 +453,11 @@ func Get(url string) (resp *Response, err error) { // following redirect codes, Get follows the redirect after calling the // Client's CheckRedirect function: // -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) // // An error is returned if the Client's CheckRedirect function fails // or if there was an HTTP protocol error. A non-2xx response doesn't @@ -519,17 +519,6 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect shouldRedirect = true includeBody = true - // Treat 307 and 308 specially, since they're new in - // Go 1.8, and they also require re-sending the request body. - if resp.Header.Get("Location") == "" { - // 308s have been observed in the wild being served - // without Location headers. Since Go 1.7 and earlier - // didn't follow these codes, just stop here instead - // of returning an error. - // See Issue 17773. - shouldRedirect = false - break - } if ireq.GetBody == nil && ireq.outgoingLength() != 0 { // We had a request body, and 307/308 require // re-sending it, but GetBody is not defined. So just @@ -641,8 +630,10 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { if len(reqs) > 0 { loc := resp.Header.Get("Location") if loc == "" { - resp.closeBody() - return nil, uerr(fmt.Errorf("%d response missing Location header", resp.StatusCode)) + // While most 3xx responses include a Location, it is not + // required and 3xx responses without a Location have been + // observed in the wild. See issues #17773 and #49281. + return resp, nil } u, err := req.URL.Parse(loc) if err != nil { @@ -900,13 +891,13 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro // the following redirect codes, Head follows the redirect, up to a // maximum of 10 redirects: // -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) // -// Head is a wrapper around DefaultClient.Head +// Head is a wrapper around DefaultClient.Head. // // To make a request with a specified context.Context, use NewRequestWithContext // and DefaultClient.Do. @@ -918,11 +909,11 @@ func Head(url string) (resp *Response, err error) { // following redirect codes, Head follows the redirect after calling the // Client's CheckRedirect function: // -// 301 (Moved Permanently) -// 302 (Found) -// 303 (See Other) -// 307 (Temporary Redirect) -// 308 (Permanent Redirect) +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) // // To make a request with a specified context.Context, use NewRequestWithContext // and Client.Do. @@ -951,9 +942,9 @@ func (c *Client) CloseIdleConnections() { } // cancelTimerBody is an io.ReadCloser that wraps rc with two features: -// 1) On Read error or close, the stop func is called. -// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and -// marked as net.Error that hit its timeout. +// 1. On Read error or close, the stop func is called. +// 2. On Read failure, if reqDidTimeout is true, the error is wrapped and +// marked as net.Error that hit its timeout. type cancelTimerBody struct { stop func() // stops the time.Timer waiting to cancel the request rc io.ReadCloser @@ -965,7 +956,6 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) { if err == nil { return n, nil } - b.stop() if err == io.EOF { return n, err } diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 01d605c35194f8..44b532ae1f3a3b 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -13,6 +13,7 @@ import ( "encoding/base64" "errors" "fmt" + "internal/testenv" "io" "log" "net" @@ -21,6 +22,7 @@ import ( "net/http/httptest" "net/url" "reflect" + "runtime" "strconv" "strings" "sync" @@ -431,11 +433,10 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa if v := urlQuery.Get("code"); v != "" { location := ts.URL if final := urlQuery.Get("next"); final != "" { - splits := strings.Split(final, ",") - first, rest := splits[0], splits[1:] + first, rest, _ := strings.Cut(final, ",") location = fmt.Sprintf("%s?code=%s", location, first) - if len(rest) > 0 { - location = fmt.Sprintf("%s&next=%s", location, strings.Join(rest, ",")) + if rest != "" { + location = fmt.Sprintf("%s&next=%s", location, rest) } } code, _ := strconv.Atoi(v) @@ -530,27 +531,31 @@ func TestClientRedirectUseResponse(t *testing.T) { } } -// Issue 17773: don't follow a 308 (or 307) if the response doesn't +// Issues 17773 and 49281: don't follow a 3xx if the response doesn't // have a Location header. -func TestClientRedirect308NoLocation(t *testing.T) { - setParallel(t) - defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - w.Header().Set("Foo", "Bar") - w.WriteHeader(308) - })) - defer ts.Close() - c := ts.Client() - res, err := c.Get(ts.URL) - if err != nil { - t.Fatal(err) - } - res.Body.Close() - if res.StatusCode != 308 { - t.Errorf("status = %d; want %d", res.StatusCode, 308) - } - if got := res.Header.Get("Foo"); got != "Bar" { - t.Errorf("Foo header = %q; want Bar", got) +func TestClientRedirectNoLocation(t *testing.T) { + for _, code := range []int{301, 308} { + t.Run(fmt.Sprint(code), func(t *testing.T) { + setParallel(t) + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Foo", "Bar") + w.WriteHeader(code) + })) + defer ts.Close() + c := ts.Client() + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if res.StatusCode != code { + t.Errorf("status = %d; want %d", res.StatusCode, code) + } + if got := res.Header.Get("Foo"); got != "Bar" { + t.Errorf("Foo header = %q; want Bar", got) + } + }) } } @@ -746,7 +751,7 @@ func (j *RecordingJar) Cookies(u *url.URL) []*Cookie { return nil } -func (j *RecordingJar) logf(format string, args ...interface{}) { +func (j *RecordingJar) logf(format string, args ...any) { j.mu.Lock() defer j.mu.Unlock() fmt.Fprintf(&j.log, format, args...) @@ -1206,64 +1211,83 @@ func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) } func testClientTimeout(t *testing.T, h2 bool) { setParallel(t) defer afterTest(t) - testDone := make(chan struct{}) // closed in defer below - sawRoot := make(chan bool, 1) - sawSlow := make(chan bool, 1) + var ( + mu sync.Mutex + nonce string // a unique per-request string + sawSlowNonce bool // true if the handler saw /slow?nonce= + ) cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + _ = r.ParseForm() if r.URL.Path == "/" { - sawRoot <- true - Redirect(w, r, "/slow", StatusFound) + Redirect(w, r, "/slow?nonce="+r.Form.Get("nonce"), StatusFound) return } if r.URL.Path == "/slow" { - sawSlow <- true + mu.Lock() + if r.Form.Get("nonce") == nonce { + sawSlowNonce = true + } else { + t.Logf("mismatched nonce: received %s, want %s", r.Form.Get("nonce"), nonce) + } + mu.Unlock() + w.Write([]byte("Hello")) w.(Flusher).Flush() - <-testDone + <-r.Context().Done() return } })) defer cst.close() - defer close(testDone) // before cst.close, to unblock /slow handler - - // 200ms should be long enough to get a normal request (the / - // handler), but not so long that it makes the test slow. - const timeout = 200 * time.Millisecond - cst.c.Timeout = timeout - res, err := cst.c.Get(cst.ts.URL) - if err != nil { - if strings.Contains(err.Error(), "Client.Timeout") { - t.Skipf("host too slow to get fast resource in %v", timeout) + // Try to trigger a timeout after reading part of the response body. + // The initial timeout is emprically usually long enough on a decently fast + // machine, but if we undershoot we'll retry with exponentially longer + // timeouts until the test either passes or times out completely. + // This keeps the test reasonably fast in the typical case but allows it to + // also eventually succeed on arbitrarily slow machines. + timeout := 10 * time.Millisecond + nextNonce := 0 + for ; ; timeout *= 2 { + if timeout <= 0 { + // The only way we can feasibly hit this while the test is running is if + // the request fails without actually waiting for the timeout to occur. + t.Fatalf("timeout overflow") + } + if deadline, ok := t.Deadline(); ok && !time.Now().Add(timeout).Before(deadline) { + t.Fatalf("failed to produce expected timeout before test deadline") + } + t.Logf("attempting test with timeout %v", timeout) + cst.c.Timeout = timeout + + mu.Lock() + nonce = fmt.Sprint(nextNonce) + nextNonce++ + sawSlowNonce = false + mu.Unlock() + res, err := cst.c.Get(cst.ts.URL + "/?nonce=" + nonce) + if err != nil { + if strings.Contains(err.Error(), "Client.Timeout") { + // Timed out before handler could respond. + t.Logf("timeout before response received") + continue + } + if runtime.GOOS == "windows" && strings.HasPrefix(runtime.GOARCH, "arm") { + testenv.SkipFlaky(t, 43120) + } + t.Fatal(err) } - t.Fatal(err) - } - - select { - case <-sawRoot: - // good. - default: - t.Fatal("handler never got / request") - } - select { - case <-sawSlow: - // good. - default: - t.Fatal("handler never got /slow request") - } + mu.Lock() + ok := sawSlowNonce + mu.Unlock() + if !ok { + t.Fatal("handler never got /slow request, but client returned response") + } - errc := make(chan error, 1) - go func() { - _, err := io.ReadAll(res.Body) - errc <- err + _, err = io.ReadAll(res.Body) res.Body.Close() - }() - const failTime = 5 * time.Second - select { - case err := <-errc: if err == nil { t.Fatal("expected error from ReadAll") } @@ -1274,10 +1298,13 @@ func testClientTimeout(t *testing.T, h2 bool) { t.Errorf("net.Error.Timeout = false; want true") } if got := ne.Error(); !strings.Contains(got, "(Client.Timeout") { + if runtime.GOOS == "windows" && strings.HasPrefix(runtime.GOARCH, "arm") { + testenv.SkipFlaky(t, 43120) + } t.Errorf("error string = %q; missing timeout substring", got) } - case <-time.After(failTime): - t.Errorf("timeout after %v waiting for timeout of %v", failTime, timeout) + + break } } @@ -1319,6 +1346,9 @@ func testClientTimeout_Headers(t *testing.T, h2 bool) { t.Error("net.Error.Timeout = false; want true") } if got := ne.Error(); !strings.Contains(got, "Client.Timeout exceeded") { + if runtime.GOOS == "windows" && strings.HasPrefix(runtime.GOARCH, "arm") { + testenv.SkipFlaky(t, 43120) + } t.Errorf("error string = %q; missing timeout substring", got) } } @@ -1353,6 +1383,33 @@ func TestClientTimeoutCancel(t *testing.T) { } } +func TestClientTimeoutDoesNotExpire_h1(t *testing.T) { testClientTimeoutDoesNotExpire(t, h1Mode) } +func TestClientTimeoutDoesNotExpire_h2(t *testing.T) { testClientTimeoutDoesNotExpire(t, h2Mode) } + +// Issue 49366: if Client.Timeout is set but not hit, no error should be returned. +func testClientTimeoutDoesNotExpire(t *testing.T, h2 bool) { + setParallel(t) + defer afterTest(t) + + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + w.Write([]byte("body")) + })) + defer cst.close() + + cst.c.Timeout = 1 * time.Hour + req, _ := NewRequest("GET", cst.ts.URL, nil) + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + if _, err = io.Copy(io.Discard, res.Body); err != nil { + t.Fatalf("io.Copy(io.Discard, res.Body) = %v, want nil", err) + } + if err = res.Body.Close(); err != nil { + t.Fatalf("res.Body.Close() = %v, want nil", err) + } +} + func TestClientRedirectEatsBody_h1(t *testing.T) { testClientRedirectEatsBody(t, h1Mode) } func TestClientRedirectEatsBody_h2(t *testing.T) { testClientRedirectEatsBody(t, h2Mode) } func testClientRedirectEatsBody(t *testing.T, h2 bool) { @@ -2082,3 +2139,47 @@ func (b *issue40382Body) Close() error { } return nil } + +func TestProbeZeroLengthBody(t *testing.T) { + setParallel(t) + defer afterTest(t) + reqc := make(chan struct{}) + cst := newClientServerTest(t, false, HandlerFunc(func(w ResponseWriter, r *Request) { + close(reqc) + if _, err := io.Copy(w, r.Body); err != nil { + t.Errorf("error copying request body: %v", err) + } + })) + defer cst.close() + + bodyr, bodyw := io.Pipe() + var gotBody string + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + req, _ := NewRequest("GET", cst.ts.URL, bodyr) + res, err := cst.c.Do(req) + b, err := io.ReadAll(res.Body) + if err != nil { + t.Error(err) + } + gotBody = string(b) + }() + + select { + case <-reqc: + // Request should be sent after trying to probe the request body for 200ms. + case <-time.After(60 * time.Second): + t.Errorf("request not sent after 60s") + } + + // Write the request body and wait for the request to complete. + const content = "body" + bodyw.Write([]byte(content)) + bodyw.Close() + wg.Wait() + if gotBody != content { + t.Fatalf("server got body %q, want %q", gotBody, content) + } +} diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go index 5e227181acb05c..b472ca4b7860a3 100644 --- a/src/net/http/clientserver_test.go +++ b/src/net/http/clientserver_test.go @@ -9,6 +9,7 @@ package http_test import ( "bytes" "compress/gzip" + "context" "crypto/rand" "crypto/sha1" "crypto/tls" @@ -19,7 +20,9 @@ import ( "net" . "net/http" "net/http/httptest" + "net/http/httptrace" "net/http/httputil" + "net/textproto" "net/url" "os" "reflect" @@ -81,7 +84,7 @@ func optWithServerLog(lg *log.Logger) func(*httptest.Server) { } } -func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{}) *clientServerTest { +func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...any) *clientServerTest { if h2 { CondSkipHTTP2(t) } @@ -189,7 +192,7 @@ type h12Compare struct { ReqFunc reqFunc // optional CheckResponse func(proto string, res *Response) // optional EarlyCheckResponse func(proto string, res *Response) // optional; pre-normalize - Opts []interface{} + Opts []any } func (tt h12Compare) reqFunc() reqFunc { @@ -441,7 +444,7 @@ func TestH12_AutoGzip(t *testing.T) { func TestH12_AutoGzip_Disabled(t *testing.T) { h12Compare{ - Opts: []interface{}{ + Opts: []any{ func(tr *Transport) { tr.DisableCompression = true }, }, Handler: func(w ResponseWriter, r *Request) { @@ -1168,7 +1171,7 @@ func TestInterruptWithPanic_ErrAbortHandler_h1(t *testing.T) { func TestInterruptWithPanic_ErrAbortHandler_h2(t *testing.T) { testInterruptWithPanic(t, h2Mode, ErrAbortHandler) } -func testInterruptWithPanic(t *testing.T, h2 bool, panicValue interface{}) { +func testInterruptWithPanic(t *testing.T, h2 bool, panicValue any) { setParallel(t) const msg = "hello" defer afterTest(t) @@ -1518,7 +1521,7 @@ func TestBidiStreamReverseProxy(t *testing.T) { })) defer proxy.close() - bodyRes := make(chan interface{}, 1) // error or hash.Hash + bodyRes := make(chan any, 1) // error or hash.Hash pr, pw := io.Pipe() req, _ := NewRequest("PUT", proxy.ts.URL, pr) const size = 4 << 20 @@ -1582,3 +1585,126 @@ func TestH12_WebSocketUpgrade(t *testing.T) { }, }.run(t) } + +func TestIdentityTransferEncoding_h1(t *testing.T) { testIdentityTransferEncoding(t, h1Mode) } +func TestIdentityTransferEncoding_h2(t *testing.T) { testIdentityTransferEncoding(t, h2Mode) } + +func testIdentityTransferEncoding(t *testing.T, h2 bool) { + setParallel(t) + defer afterTest(t) + + const body = "body" + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + gotBody, _ := io.ReadAll(r.Body) + if got, want := string(gotBody), body; got != want { + t.Errorf("got request body = %q; want %q", got, want) + } + w.Header().Set("Transfer-Encoding", "identity") + w.WriteHeader(StatusOK) + w.(Flusher).Flush() + io.WriteString(w, body) + })) + defer cst.close() + req, _ := NewRequest("GET", cst.ts.URL, strings.NewReader(body)) + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + gotBody, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if got, want := string(gotBody), body; got != want { + t.Errorf("got response body = %q; want %q", got, want) + } +} + +func TestEarlyHintsRequest_h1(t *testing.T) { testEarlyHintsRequest(t, h1Mode) } +func TestEarlyHintsRequest_h2(t *testing.T) { testEarlyHintsRequest(t, h2Mode) } +func testEarlyHintsRequest(t *testing.T, h2 bool) { + defer afterTest(t) + + var wg sync.WaitGroup + wg.Add(1) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + h := w.Header() + + h.Add("Content-Length", "123") // must be ignored + h.Add("Link", "; rel=preload; as=style") + h.Add("Link", "; rel=preload; as=script") + w.WriteHeader(StatusEarlyHints) + + wg.Wait() + + h.Add("Link", "; rel=preload; as=script") + w.WriteHeader(StatusEarlyHints) + + w.Write([]byte("Hello")) + })) + defer cst.close() + + checkLinkHeaders := func(t *testing.T, expected, got []string) { + t.Helper() + + if len(expected) != len(got) { + t.Errorf("got %d expected %d", len(got), len(expected)) + } + + for i := range expected { + if expected[i] != got[i] { + t.Errorf("got %q expected %q", got[i], expected[i]) + } + } + } + + checkExcludedHeaders := func(t *testing.T, header textproto.MIMEHeader) { + t.Helper() + + for _, h := range []string{"Content-Length", "Transfer-Encoding"} { + if v, ok := header[h]; ok { + t.Errorf("%s is %q; must not be sent", h, v) + } + } + } + + var respCounter uint8 + trace := &httptrace.ClientTrace{ + Got1xxResponse: func(code int, header textproto.MIMEHeader) error { + switch respCounter { + case 0: + checkLinkHeaders(t, []string{"; rel=preload; as=style", "; rel=preload; as=script"}, header["Link"]) + checkExcludedHeaders(t, header) + + wg.Done() + case 1: + checkLinkHeaders(t, []string{"; rel=preload; as=style", "; rel=preload; as=script", "; rel=preload; as=script"}, header["Link"]) + checkExcludedHeaders(t, header) + + default: + t.Error("Unexpected 1xx response") + } + + respCounter++ + + return nil + }, + } + req, _ := NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), "GET", cst.ts.URL, nil) + + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + checkLinkHeaders(t, []string{"; rel=preload; as=style", "; rel=preload; as=script", "; rel=preload; as=script"}, res.Header["Link"]) + if cl := res.Header.Get("Content-Length"); cl != "123" { + t.Errorf("Content-Length is %q; want 123", cl) + } + + body, _ := io.ReadAll(res.Body) + if string(body) != "Hello" { + t.Errorf("Read body %q; want Hello", body) + } +} diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index ca2c1c2506694c..b7b0455ee1653e 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -5,6 +5,8 @@ package http import ( + "errors" + "fmt" "log" "net" "net/http/internal/ascii" @@ -67,15 +69,15 @@ func readSetCookies(h Header) []*Cookie { continue } parts[0] = textproto.TrimString(parts[0]) - j := strings.Index(parts[0], "=") - if j < 0 { + name, value, ok := strings.Cut(parts[0], "=") + if !ok { continue } - name, value := parts[0][:j], parts[0][j+1:] + name = textproto.TrimString(name) if !isCookieNameValid(name) { continue } - value, ok := parseCookieValue(value, true) + value, ok = parseCookieValue(value, true) if !ok { continue } @@ -90,10 +92,7 @@ func readSetCookies(h Header) []*Cookie { continue } - attr, val := parts[i], "" - if j := strings.Index(attr, "="); j >= 0 { - attr, val = attr[:j], attr[j+1:] - } + attr, val, _ := strings.Cut(parts[i], "=") lowerAttr, isASCII := ascii.ToLower(attr) if !isASCII { continue @@ -240,6 +239,37 @@ func (c *Cookie) String() string { return b.String() } +// Valid reports whether the cookie is valid. +func (c *Cookie) Valid() error { + if c == nil { + return errors.New("http: nil Cookie") + } + if !isCookieNameValid(c.Name) { + return errors.New("http: invalid Cookie.Name") + } + if !c.Expires.IsZero() && !validCookieExpires(c.Expires) { + return errors.New("http: invalid Cookie.Expires") + } + for i := 0; i < len(c.Value); i++ { + if !validCookieValueByte(c.Value[i]) { + return fmt.Errorf("http: invalid byte %q in Cookie.Value", c.Value[i]) + } + } + if len(c.Path) > 0 { + for i := 0; i < len(c.Path); i++ { + if !validCookiePathByte(c.Path[i]) { + return fmt.Errorf("http: invalid byte %q in Cookie.Path", c.Path[i]) + } + } + } + if len(c.Domain) > 0 { + if !validCookieDomain(c.Domain) { + return errors.New("http: invalid Cookie.Domain") + } + } + return nil +} + // readCookies parses all "Cookie" values from the header h and // returns the successfully parsed Cookies. // @@ -256,19 +286,13 @@ func readCookies(h Header, filter string) []*Cookie { var part string for len(line) > 0 { // continue since we have rest - if splitIndex := strings.Index(line, ";"); splitIndex > 0 { - part, line = line[:splitIndex], line[splitIndex+1:] - } else { - part, line = line, "" - } + part, line, _ = strings.Cut(line, ";") part = textproto.TrimString(part) - if len(part) == 0 { + if part == "" { continue } - name, val := part, "" - if j := strings.Index(part, "="); j >= 0 { - name, val = name[:j], name[j+1:] - } + name, val, _ := strings.Cut(part, "=") + name = textproto.TrimString(name) if !isCookieNameValid(name) { continue } @@ -365,11 +389,13 @@ func sanitizeCookieName(n string) string { // sanitizeCookieValue produces a suitable cookie-value from v. // https://tools.ietf.org/html/rfc6265#section-4.1.1 -// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) -// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E -// ; US-ASCII characters excluding CTLs, -// ; whitespace DQUOTE, comma, semicolon, -// ; and backslash +// +// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E +// ; US-ASCII characters excluding CTLs, +// ; whitespace DQUOTE, comma, semicolon, +// ; and backslash +// // We loosen this as spaces and commas are common in cookie values // but we produce a quoted cookie-value if and only if v contains // commas or spaces. @@ -379,7 +405,7 @@ func sanitizeCookieValue(v string) string { if len(v) == 0 { return v } - if strings.IndexByte(v, ' ') >= 0 || strings.IndexByte(v, ',') >= 0 { + if strings.ContainsAny(v, " ,") { return `"` + v + `"` } return v diff --git a/src/net/http/cookie_test.go b/src/net/http/cookie_test.go index 959713a0dcf0a0..e5bd46a744fba5 100644 --- a/src/net/http/cookie_test.go +++ b/src/net/http/cookie_test.go @@ -5,7 +5,6 @@ package http import ( - "bytes" "encoding/json" "fmt" "log" @@ -151,7 +150,7 @@ var writeSetCookiesTests = []struct { func TestWriteSetCookies(t *testing.T) { defer log.SetOutput(os.Stderr) - var logbuf bytes.Buffer + var logbuf strings.Builder log.SetOutput(&logbuf) for i, tt := range writeSetCookiesTests { @@ -352,6 +351,12 @@ var readSetCookiesTests = []struct { Header{"Set-Cookie": {`special-8=","`}}, []*Cookie{{Name: "special-8", Value: ",", Raw: `special-8=","`}}, }, + // Make sure we can properly read back the Set-Cookie headers + // for names containing spaces: + { + Header{"Set-Cookie": {`special-9 =","`}}, + []*Cookie{{Name: "special-9", Value: ",", Raw: `special-9 =","`}}, + }, // TODO(bradfitz): users have reported seeing this in the // wild, but do browsers handle it? RFC 6265 just says "don't @@ -360,7 +365,7 @@ var readSetCookiesTests = []struct { // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}}, } -func toJSON(v interface{}) string { +func toJSON(v any) string { b, err := json.Marshal(v) if err != nil { return fmt.Sprintf("%#v", v) @@ -476,7 +481,7 @@ func TestSetCookieDoubleQuotes(t *testing.T) { func TestCookieSanitizeValue(t *testing.T) { defer log.SetOutput(os.Stderr) - var logbuf bytes.Buffer + var logbuf strings.Builder log.SetOutput(&logbuf) tests := []struct { @@ -508,7 +513,7 @@ func TestCookieSanitizeValue(t *testing.T) { func TestCookieSanitizePath(t *testing.T) { defer log.SetOutput(os.Stderr) - var logbuf bytes.Buffer + var logbuf strings.Builder log.SetOutput(&logbuf) tests := []struct { @@ -529,6 +534,34 @@ func TestCookieSanitizePath(t *testing.T) { } } +func TestCookieValid(t *testing.T) { + tests := []struct { + cookie *Cookie + valid bool + }{ + {nil, false}, + {&Cookie{Name: ""}, false}, + {&Cookie{Name: "invalid-value", Value: "foo\"bar"}, false}, + {&Cookie{Name: "invalid-path", Path: "/foo;bar/"}, false}, + {&Cookie{Name: "invalid-domain", Domain: "example.com:80"}, false}, + {&Cookie{Name: "invalid-expiry", Value: "", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, false}, + {&Cookie{Name: "valid-empty"}, true}, + {&Cookie{Name: "valid-expires", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0)}, true}, + {&Cookie{Name: "valid-max-age", Value: "foo", Path: "/bar", Domain: "example.com", MaxAge: 60}, true}, + {&Cookie{Name: "valid-all-fields", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0), MaxAge: 0}, true}, + } + + for _, tt := range tests { + err := tt.cookie.Valid() + if err != nil && tt.valid { + t.Errorf("%#v.Valid() returned error %v; want nil", tt.cookie, err) + } + if err == nil && !tt.valid { + t.Errorf("%#v.Valid() returned nil; want error", tt.cookie) + } + } +} + func BenchmarkCookieString(b *testing.B) { const wantCookieString = `cookie-9=i3e01nf61b6t23bvfmplnanol3; Path=/restricted/; Domain=example.com; Expires=Tue, 10 Nov 2009 23:00:00 GMT; Max-Age=3600` c := &Cookie{ diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go index e6583da7fe67df..097c93a137e8a5 100644 --- a/src/net/http/cookiejar/jar.go +++ b/src/net/http/cookiejar/jar.go @@ -19,9 +19,9 @@ import ( ) // PublicSuffixList provides the public suffix of a domain. For example: -// - the public suffix of "example.com" is "com", -// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and -// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us". +// - the public suffix of "example.com" is "com", +// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and +// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us". // // Implementations of PublicSuffixList must be safe for concurrent use by // multiple goroutines. @@ -121,7 +121,9 @@ func (e *entry) shouldSend(https bool, host, path string) bool { return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure) } -// domainMatch implements "domain-match" of RFC 6265 section 5.1.3. +// domainMatch checks whether e's Domain allows sending e back to host. +// It differs from "domain-match" of RFC 6265 section 5.1.3 because we treat +// a cookie with an IP address in the Domain always as a host cookie. func (e *entry) domainMatch(host string) bool { if e.Domain == host { return true @@ -303,10 +305,8 @@ func canonicalHost(host string) (string, error) { return "", err } } - if strings.HasSuffix(host, ".") { - // Strip trailing dot from fully qualified domain names. - host = host[:len(host)-1] - } + // Strip trailing dot from fully qualified domain names. + host = strings.TrimSuffix(host, ".") encoded, err := toASCII(host) if err != nil { return "", err @@ -457,10 +457,36 @@ func (j *Jar) domainAndType(host, domain string) (string, bool, error) { } if isIP(host) { - // According to RFC 6265 domain-matching includes not being - // an IP address. - // TODO: This might be relaxed as in common browsers. - return "", false, errNoHostname + // RFC 6265 is not super clear here, a sensible interpretation + // is that cookies with an IP address in the domain-attribute + // are allowed. + + // RFC 6265 section 5.2.3 mandates to strip an optional leading + // dot in the domain-attribute before processing the cookie. + // + // Most browsers don't do that for IP addresses, only curl + // version 7.54) and IE (version 11) do not reject a + // Set-Cookie: a=1; domain=.127.0.0.1 + // This leading dot is optional and serves only as hint for + // humans to indicate that a cookie with "domain=.bbc.co.uk" + // would be sent to every subdomain of bbc.co.uk. + // It just doesn't make sense on IP addresses. + // The other processing and validation steps in RFC 6265 just + // collaps to: + if host != domain { + return "", false, errIllegalDomain + } + + // According to RFC 6265 such cookies should be treated as + // domain cookies. + // As there are no subdomains of an IP address the treatment + // according to RFC 6265 would be exactly the same as that of + // a host-only cookie. Contemporary browsers (and curl) do + // allows such cookies but treat them as host-only cookies. + // So do we as it just doesn't make sense to label them as + // domain cookies when there is no domain; the whole notion of + // domain cookies requires a domain name to be well defined. + return host, true, nil } // From here on: If the cookie is valid, it is a domain cookie (with diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go index 47fb1abdaafa4a..13d994aa393478 100644 --- a/src/net/http/cookiejar/jar_test.go +++ b/src/net/http/cookiejar/jar_test.go @@ -20,8 +20,9 @@ var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC) // testPSL implements PublicSuffixList with just two rules: "co.uk" // and the default rule "*". // The implementation has two intentional bugs: -// PublicSuffix("www.buggy.psl") == "xy" -// PublicSuffix("www2.buggy.psl") == "com" +// +// PublicSuffix("www.buggy.psl") == "xy" +// PublicSuffix("www2.buggy.psl") == "com" type testPSL struct{} func (testPSL) String() string { @@ -305,8 +306,8 @@ var domainAndTypeTests = [...]struct { {"foo.sso.example.com", "sso.example.com", "sso.example.com", false, nil}, {"bar.co.uk", "bar.co.uk", "bar.co.uk", false, nil}, {"foo.bar.co.uk", ".bar.co.uk", "bar.co.uk", false, nil}, - {"127.0.0.1", "127.0.0.1", "", false, errNoHostname}, - {"2001:4860:0:2001::68", "2001:4860:0:2001::68", "2001:4860:0:2001::68", false, errNoHostname}, + {"127.0.0.1", "127.0.0.1", "127.0.0.1", true, nil}, + {"2001:4860:0:2001::68", "2001:4860:0:2001::68", "2001:4860:0:2001::68", true, nil}, {"www.example.com", ".", "", false, errMalformedDomain}, {"www.example.com", "..", "", false, errMalformedDomain}, {"www.example.com", "other.com", "", false, errIllegalDomain}, @@ -327,7 +328,7 @@ func TestDomainAndType(t *testing.T) { for _, tc := range domainAndTypeTests { domain, hostOnly, err := jar.domainAndType(tc.host, tc.domain) if err != tc.wantErr { - t.Errorf("%q/%q: got %q error, want %q", + t.Errorf("%q/%q: got %q error, want %v", tc.host, tc.domain, err, tc.wantErr) continue } @@ -358,13 +359,13 @@ func mustParseURL(s string) *url.URL { } // jarTest encapsulates the following actions on a jar: -// 1. Perform SetCookies with fromURL and the cookies from setCookies. -// (Done at time tNow + 0 ms.) -// 2. Check that the entries in the jar matches content. -// (Done at time tNow + 1001 ms.) -// 3. For each query in tests: Check that Cookies with toURL yields the -// cookies in want. -// (Query n done at tNow + (n+2)*1001 ms.) +// 1. Perform SetCookies with fromURL and the cookies from setCookies. +// (Done at time tNow + 0 ms.) +// 2. Check that the entries in the jar matches content. +// (Done at time tNow + 1001 ms.) +// 3. For each query in tests: Check that Cookies with toURL yields the +// cookies in want. +// (Query n done at tNow + (n+2)*1001 ms.) type jarTest struct { description string // The description of what this test is supposed to test fromURL string // The full URL of the request from which Set-Cookie headers where received @@ -592,6 +593,21 @@ var basicsTests = [...]jarTest{ "a=1", []query{{"http://192.168.0.10", "a=1"}}, }, + { + "Domain cookies on IP.", + "http://192.168.0.10", + []string{ + "a=1; domain=192.168.0.10", // allowed + "b=2; domain=172.31.9.9", // rejected, can't set cookie for other IP + "c=3; domain=.192.168.0.10", // rejected like in most browsers + }, + "a=1", + []query{ + {"http://192.168.0.10", "a=1"}, + {"http://172.31.9.9", ""}, + {"http://www.fancy.192.168.0.10", ""}, + }, + }, { "Port is ignored #1.", "http://www.host.test/", @@ -926,10 +942,17 @@ var chromiumBasicsTests = [...]jarTest{ { "TestIpAddress #3.", "http://1.2.3.4/foo", - []string{"a=1; domain=1.2.3.4"}, + []string{"a=1; domain=1.2.3.3"}, "", []query{{"http://1.2.3.4/foo", ""}}, }, + { + "TestIpAddress #4.", + "http://1.2.3.4/foo", + []string{"a=1; domain=1.2.3.4"}, + "a=1", + []query{{"http://1.2.3.4/foo", "a=1"}}, + }, { "TestNonDottedAndTLD #2.", "http://com./index.html", diff --git a/src/net/http/doc.go b/src/net/http/doc.go index ae9b708c695bfd..67c4246c6080f6 100644 --- a/src/net/http/doc.go +++ b/src/net/http/doc.go @@ -102,6 +102,5 @@ directly and use its ConfigureTransport and/or ConfigureServer functions. Manually configuring HTTP/2 via the golang.org/x/net/http2 package takes precedence over the net/http package's built-in HTTP/2 support. - */ package http diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index 096a6d382a8afa..205ca83f402609 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -88,12 +88,7 @@ func SetPendingDialHooks(before, after func()) { func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn } -func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-ch - cancel() - }() +func NewTestTimeoutHandler(handler Handler, ctx context.Context) Handler { return &timeoutHandler{ handler: handler, testContext: ctx, @@ -311,3 +306,12 @@ func ExportCloseTransportConnsAbruptly(tr *Transport) { } tr.idleMu.Unlock() } + +// ResponseWriterConnForTesting returns w's underlying connection, if w +// is a regular *response ResponseWriter. +func ResponseWriterConnForTesting(w ResponseWriter) (c net.Conn, ok bool) { + if r, ok := w.(*response); ok { + return r.conn.rwc, true + } + return nil, false +} diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go index 5888783620874c..7a344ff31dc464 100644 --- a/src/net/http/fcgi/fcgi_test.go +++ b/src/net/http/fcgi/fcgi_test.go @@ -401,16 +401,16 @@ func TestResponseWriterSniffsContentType(t *testing.T) { } } -type signallingNopCloser struct { +type signalingNopCloser struct { io.Reader closed chan bool } -func (*signallingNopCloser) Write(buf []byte) (int, error) { +func (*signalingNopCloser) Write(buf []byte) (int, error) { return len(buf), nil } -func (rc *signallingNopCloser) Close() error { +func (rc *signalingNopCloser) Close() error { close(rc.closed) return nil } @@ -429,7 +429,7 @@ func TestSlowRequest(t *testing.T) { } }(pw) - rc := &signallingNopCloser{pr, make(chan bool)} + rc := &signalingNopCloser{pr, make(chan bool)} handlerDone := make(chan bool) c := newChild(rc, http.HandlerFunc(func( diff --git a/src/net/http/filetransport.go b/src/net/http/filetransport.go index 32126d7ec0f6c3..94684b07a13959 100644 --- a/src/net/http/filetransport.go +++ b/src/net/http/filetransport.go @@ -22,11 +22,11 @@ type fileTransport struct { // The typical use case for NewFileTransport is to register the "file" // protocol with a Transport, as in: // -// t := &http.Transport{} -// t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) -// c := &http.Client{Transport: t} -// res, err := c.Get("file:///etc/passwd") -// ... +// t := &http.Transport{} +// t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) +// c := &http.Client{Transport: t} +// res, err := c.Get("file:///etc/passwd") +// ... func NewFileTransport(fs FileSystem) RoundTripper { return fileTransport{fileHandler{fs}} } diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 57e731e481ad49..cf80018b5e1a84 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -42,20 +42,20 @@ import ( // An empty Dir is treated as ".". type Dir string -// mapDirOpenError maps the provided non-nil error from opening name +// mapOpenError maps the provided non-nil error from opening name // to a possibly better non-nil error. In particular, it turns OS-specific errors -// about opening files in non-directories into fs.ErrNotExist. See Issue 18984. -func mapDirOpenError(originalErr error, name string) error { +// about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552. +func mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error { if errors.Is(originalErr, fs.ErrNotExist) || errors.Is(originalErr, fs.ErrPermission) { return originalErr } - parts := strings.Split(name, string(filepath.Separator)) + parts := strings.Split(name, string(sep)) for i := range parts { if parts[i] == "" { continue } - fi, err := os.Stat(strings.Join(parts[:i+1], string(filepath.Separator))) + fi, err := stat(strings.Join(parts[:i+1], string(sep))) if err != nil { return originalErr } @@ -79,7 +79,7 @@ func (d Dir) Open(name string) (File, error) { fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) f, err := os.Open(fullName) if err != nil { - return nil, mapDirOpenError(err, fullName) + return nil, mapOpenError(err, fullName, filepath.Separator, os.Stat) } return f, nil } @@ -541,6 +541,7 @@ func writeNotModified(w ResponseWriter) { h := w.Header() delete(h, "Content-Type") delete(h, "Content-Length") + delete(h, "Content-Encoding") if h.Get("Etag") != "" { delete(h, "Last-Modified") } @@ -759,7 +760,9 @@ func (f ioFS) Open(name string) (File, error) { } file, err := f.fsys.Open(name) if err != nil { - return nil, err + return nil, mapOpenError(err, name, '/', func(path string) (fs.FileInfo, error) { + return fs.Stat(f.fsys, path) + }) } return ioFile{file}, nil } @@ -815,6 +818,7 @@ func (f ioFile) Readdir(count int) ([]fs.FileInfo, error) { // FS converts fsys to a FileSystem implementation, // for use with FileServer and NewFileTransport. +// The files provided by fsys must implement io.Seeker. func FS(fsys fs.FS) FileSystem { return ioFS{fsys} } @@ -829,23 +833,32 @@ func FS(fsys fs.FS) FileSystem { // To use the operating system's file system implementation, // use http.Dir: // -// http.Handle("/", http.FileServer(http.Dir("/tmp"))) +// http.Handle("/", http.FileServer(http.Dir("/tmp"))) // // To use an fs.FS implementation, use http.FS to convert it: // // http.Handle("/", http.FileServer(http.FS(fsys))) -// func FileServer(root FileSystem) Handler { return &fileHandler{root} } func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { - upath := r.URL.Path - if !strings.HasPrefix(upath, "/") { - upath = "/" + upath - r.URL.Path = upath + const options = MethodOptions + ", " + MethodGet + ", " + MethodHead + + switch r.Method { + case MethodGet, MethodHead: + if !strings.HasPrefix(r.URL.Path, "/") { + r.URL.Path = "/" + r.URL.Path + } + serveFile(w, r, f.root, path.Clean(r.URL.Path), true) + + case MethodOptions: + w.Header().Set("Allow", options) + + default: + w.Header().Set("Allow", options) + Error(w, "read-only", StatusMethodNotAllowed) } - serveFile(w, r, f.root, path.Clean(upath), true) } // httpRange specifies the byte range to be sent to the client. @@ -881,11 +894,11 @@ func parseRange(s string, size int64) ([]httpRange, error) { if ra == "" { continue } - i := strings.Index(ra, "-") - if i < 0 { + start, end, ok := strings.Cut(ra, "-") + if !ok { return nil, errors.New("invalid range") } - start, end := textproto.TrimString(ra[:i]), textproto.TrimString(ra[i+1:]) + start, end = textproto.TrimString(start), textproto.TrimString(end) var r httpRange if start == "" { // If no start is specified, end specifies the diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go index b42ade1e8ac4f4..71fc064367d774 100644 --- a/src/net/http/fs_test.go +++ b/src/net/http/fs_test.go @@ -24,6 +24,7 @@ import ( "reflect" "regexp" "runtime" + "sort" "strings" "testing" "time" @@ -404,6 +405,47 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) { } } +func TestFileServerMethodOptions(t *testing.T) { + defer afterTest(t) + const want = "GET, HEAD, OPTIONS" + ts := httptest.NewServer(FileServer(Dir("."))) + defer ts.Close() + + tests := []struct { + method string + wantStatus int + }{ + {MethodOptions, StatusOK}, + + {MethodDelete, StatusMethodNotAllowed}, + {MethodPut, StatusMethodNotAllowed}, + {MethodPost, StatusMethodNotAllowed}, + } + + for _, test := range tests { + req, err := NewRequest(test.method, ts.URL, nil) + if err != nil { + t.Fatal(err) + } + res, err := ts.Client().Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + if res.StatusCode != test.wantStatus { + t.Errorf("%s got status %q, want code %d", test.method, res.Status, test.wantStatus) + } + + a := strings.Split(res.Header.Get("Allow"), ", ") + sort.Strings(a) + got := strings.Join(a, ", ") + if got != want { + t.Errorf("%s got Allow header %q, want %q", test.method, got, want) + } + } +} + func TestDirJoin(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping test on windows") @@ -564,6 +606,60 @@ func testServeFileWithContentEncoding(t *testing.T, h2 bool) { } } +// Tests that ServeFile does not generate representation metadata when +// file has not been modified, as per RFC 7232 section 4.1. +func TestServeFileNotModified_h1(t *testing.T) { testServeFileNotModified(t, h1Mode) } +func TestServeFileNotModified_h2(t *testing.T) { testServeFileNotModified(t, h2Mode) } +func testServeFileNotModified(t *testing.T, h2 bool) { + defer afterTest(t) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Encoding", "foo") + w.Header().Set("Etag", `"123"`) + ServeFile(w, r, "testdata/file") + + // Because the testdata is so small, it would fit in + // both the h1 and h2 Server's write buffers. For h1, + // sendfile is used, though, forcing a header flush at + // the io.Copy. http2 doesn't do a header flush so + // buffers all 11 bytes and then adds its own + // Content-Length. To prevent the Server's + // Content-Length and test ServeFile only, flush here. + w.(Flusher).Flush() + })) + defer cst.close() + req, err := NewRequest("GET", cst.ts.URL, nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("If-None-Match", `"123"`) + resp, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + b, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + t.Fatal("reading Body:", err) + } + if len(b) != 0 { + t.Errorf("non-empty body") + } + if g, e := resp.StatusCode, StatusNotModified; g != e { + t.Errorf("status mismatch: got %d, want %d", g, e) + } + // HTTP1 transport sets ContentLength to 0. + if g, e1, e2 := resp.ContentLength, int64(-1), int64(0); g != e1 && g != e2 { + t.Errorf("Content-Length mismatch: got %d, want %d or %d", g, e1, e2) + } + if resp.Header.Get("Content-Type") != "" { + t.Errorf("Content-Type present, but it should not be") + } + if resp.Header.Get("Content-Encoding") != "" { + t.Errorf("Content-Encoding present, but it should not be") + } +} + func TestServeIndexHtml(t *testing.T) { defer afterTest(t) @@ -658,7 +754,7 @@ type fakeFileInfo struct { } func (f *fakeFileInfo) Name() string { return f.basename } -func (f *fakeFileInfo) Sys() interface{} { return nil } +func (f *fakeFileInfo) Sys() any { return nil } func (f *fakeFileInfo) ModTime() time.Time { return f.modtime } func (f *fakeFileInfo) IsDir() bool { return f.dir } func (f *fakeFileInfo) Size() int64 { return int64(len(f.contents)) } @@ -1177,7 +1273,7 @@ func TestLinuxSendfile(t *testing.T) { } defer os.Remove(filepath) - var buf bytes.Buffer + var buf strings.Builder child := exec.Command("strace", "-f", "-q", os.Args[0], "-test.run=TestLinuxSendfileChild") child.ExtraFiles = append(child.ExtraFiles, lnf) child.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) @@ -1244,10 +1340,19 @@ func TestLinuxSendfileChild(*testing.T) { } } -// Issue 18984: tests that requests for paths beyond files return not-found errors +// Issues 18984, 49552: tests that requests for paths beyond files return not-found errors func TestFileServerNotDirError(t *testing.T) { defer afterTest(t) - ts := httptest.NewServer(FileServer(Dir("testdata"))) + t.Run("Dir", func(t *testing.T) { + testFileServerNotDirError(t, func(path string) FileSystem { return Dir(path) }) + }) + t.Run("FS", func(t *testing.T) { + testFileServerNotDirError(t, func(path string) FileSystem { return FS(os.DirFS(path)) }) + }) +} + +func testFileServerNotDirError(t *testing.T, newfs func(string) FileSystem) { + ts := httptest.NewServer(FileServer(newfs("testdata"))) defer ts.Close() res, err := Get(ts.URL + "/index.html/not-a-file") @@ -1259,9 +1364,9 @@ func TestFileServerNotDirError(t *testing.T) { t.Errorf("StatusCode = %v; want 404", res.StatusCode) } - test := func(name string, dir Dir) { + test := func(name string, fsys FileSystem) { t.Run(name, func(t *testing.T) { - _, err = dir.Open("/index.html/not-a-file") + _, err = fsys.Open("/index.html/not-a-file") if err == nil { t.Fatal("err == nil; want != nil") } @@ -1270,7 +1375,7 @@ func TestFileServerNotDirError(t *testing.T) { errors.Is(err, fs.ErrNotExist)) } - _, err = dir.Open("/index.html/not-a-dir/not-a-file") + _, err = fsys.Open("/index.html/not-a-dir/not-a-file") if err == nil { t.Fatal("err == nil; want != nil") } @@ -1286,8 +1391,8 @@ func TestFileServerNotDirError(t *testing.T) { t.Fatal("get abs path:", err) } - test("RelativePath", Dir("testdata")) - test("AbsolutePath", Dir(absPath)) + test("RelativePath", newfs("testdata")) + test("AbsolutePath", newfs(absPath)) } func TestFileServerCleanPath(t *testing.T) { diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index a948ff3eed4af6..4882fd0d8c5cc6 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -30,7 +30,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "math" mathrand "math/rand" @@ -53,6 +52,10 @@ import ( "golang.org/x/net/idna" ) +// The HTTP protocols are defined in terms of ASCII, not Unicode. This file +// contains helper functions which may use Unicode-aware functions which would +// otherwise be unsafe and could introduce vulnerabilities if used improperly. + // asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t // are equal, ASCII-case-insensitively. func http2asciiEqualFold(s, t string) bool { @@ -733,6 +736,12 @@ func http2isBadCipher(cipher uint16) bool { // ClientConnPool manages a pool of HTTP/2 client connections. type http2ClientConnPool interface { + // GetClientConn returns a specific HTTP/2 connection (usually + // a TLS-TCP connection) to an HTTP/2 server. On success, the + // returned ClientConn accounts for the upcoming RoundTrip + // call, so the caller should not omit it. If the caller needs + // to, ClientConn.RoundTrip can be called with a bogus + // new(http.Request) to release the stream reservation. GetClientConn(req *Request, addr string) (*http2ClientConn, error) MarkDead(*http2ClientConn) } @@ -759,7 +768,7 @@ type http2clientConnPool struct { conns map[string][]*http2ClientConn // key is host:port dialing map[string]*http2dialCall // currently in-flight dials keys map[*http2ClientConn][]string - addConnCalls map[string]*http2addConnCall // in-flight addConnIfNeede calls + addConnCalls map[string]*http2addConnCall // in-flight addConnIfNeeded calls } func (p *http2clientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) { @@ -771,28 +780,8 @@ const ( http2noDialOnMiss = false ) -// shouldTraceGetConn reports whether getClientConn should call any -// ClientTrace.GetConn hook associated with the http.Request. -// -// This complexity is needed to avoid double calls of the GetConn hook -// during the back-and-forth between net/http and x/net/http2 (when the -// net/http.Transport is upgraded to also speak http2), as well as support -// the case where x/net/http2 is being used directly. -func (p *http2clientConnPool) shouldTraceGetConn(st http2clientConnIdleState) bool { - // If our Transport wasn't made via ConfigureTransport, always - // trace the GetConn hook if provided, because that means the - // http2 package is being used directly and it's the one - // dialing, as opposed to net/http. - if _, ok := p.t.ConnPool.(http2noDialClientConnPool); !ok { - return true - } - // Otherwise, only use the GetConn hook if this connection has - // been used previously for other requests. For fresh - // connections, the net/http package does the dialing. - return !st.freshConn -} - func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMiss bool) (*http2ClientConn, error) { + // TODO(dneil): Dial a new connection when t.DisableKeepAlives is set? if http2isConnectionCloseRequest(req) && dialOnMiss { // It gets its own connection. http2traceGetConn(req, addr) @@ -806,10 +795,14 @@ func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMis for { p.mu.Lock() for _, cc := range p.conns[addr] { - if st := cc.idleState(); st.canTakeNewRequest { - if p.shouldTraceGetConn(st) { + if cc.ReserveNewRequest() { + // When a connection is presented to us by the net/http package, + // the GetConn hook has already been called. + // Don't call it a second time here. + if !cc.getConnCalled { http2traceGetConn(req, addr) } + cc.getConnCalled = false p.mu.Unlock() return cc, nil } @@ -825,7 +818,13 @@ func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMis if http2shouldRetryDial(call, req) { continue } - return call.res, call.err + cc, err := call.res, call.err + if err != nil { + return nil, err + } + if cc.ReserveNewRequest() { + return cc, nil + } } } @@ -860,7 +859,6 @@ func (p *http2clientConnPool) getStartDialLocked(ctx context.Context, addr strin func (c *http2dialCall) dial(ctx context.Context, addr string) { const singleUse = false // shared conn c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse) - close(c.done) c.p.mu.Lock() delete(c.p.dialing, addr) @@ -868,6 +866,8 @@ func (c *http2dialCall) dial(ctx context.Context, addr string) { c.p.addConnLocked(addr, c.res) } c.p.mu.Unlock() + + close(c.done) } // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't @@ -922,6 +922,7 @@ func (c *http2addConnCall) run(t *http2Transport, key string, tc *tls.Conn) { if err != nil { c.err = err } else { + cc.getConnCalled = true // already called by the net/http package p.addConnLocked(key, cc) } delete(p.addConnCalls, key) @@ -1208,6 +1209,13 @@ func (e http2ErrCode) String() string { return fmt.Sprintf("unknown error code 0x%x", uint32(e)) } +func (e http2ErrCode) stringToken() string { + if s, ok := http2errCodeName[e]; ok { + return s + } + return fmt.Sprintf("ERR_UNKNOWN_%d", uint32(e)) +} + // ConnectionError is an error that results in the termination of the // entire connection. type http2ConnectionError http2ErrCode @@ -1224,6 +1232,11 @@ type http2StreamError struct { Cause error // optional additional detail } +// errFromPeer is a sentinel error value for StreamError.Cause to +// indicate that the StreamError was sent from the peer over the wire +// and wasn't locally generated in the Transport. +var http2errFromPeer = errors.New("received from peer") + func http2streamError(id uint32, code http2ErrCode) http2StreamError { return http2StreamError{StreamID: id, Code: code} } @@ -1281,7 +1294,7 @@ func (e http2headerFieldNameError) Error() string { type http2headerFieldValueError string func (e http2headerFieldValueError) Error() string { - return fmt.Sprintf("invalid header field value %q", string(e)) + return fmt.Sprintf("invalid header field value for %q", string(e)) } var ( @@ -1339,7 +1352,7 @@ const http2frameHeaderLen = 9 var http2padZeros = make([]byte, 255) // zeros for padding // A FrameType is a registered frame type as defined in -// http://http2.github.io/http2-spec/#rfc.section.11.2 +// https://httpwg.org/specs/rfc7540.html#rfc.section.11.2 type http2FrameType uint8 const ( @@ -1438,7 +1451,7 @@ var http2flagName = map[http2FrameType]map[http2Flags]string{ // a frameParser parses a frame given its FrameHeader and payload // bytes. The length of payload will always equal fh.Length (which // might be 0). -type http2frameParser func(fc *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) +type http2frameParser func(fc *http2frameCache, fh http2FrameHeader, countError func(string), payload []byte) (http2Frame, error) var http2frameParsers = map[http2FrameType]http2frameParser{ http2FrameData: http2parseDataFrame, @@ -1462,7 +1475,7 @@ func http2typeFrameParser(t http2FrameType) http2frameParser { // A FrameHeader is the 9 byte header of all HTTP/2 frames. // -// See http://http2.github.io/http2-spec/#FrameHeader +// See https://httpwg.org/specs/rfc7540.html#FrameHeader type http2FrameHeader struct { valid bool // caller can access []byte fields in the Frame @@ -1583,6 +1596,11 @@ type http2Framer struct { lastFrame http2Frame errDetail error + // countError is a non-nil func that's called on a frame parse + // error with some unique error path token. It's initialized + // from Transport.CountError or Server.CountError. + countError func(errToken string) + // lastHeaderStream is non-zero if the last frame was an // unfinished HEADERS/CONTINUATION. lastHeaderStream uint32 @@ -1745,6 +1763,7 @@ func http2NewFramer(w io.Writer, r io.Reader) *http2Framer { fr := &http2Framer{ w: w, r: r, + countError: func(string) {}, logReads: http2logFrameReads, logWrites: http2logFrameWrites, debugReadLoggerf: log.Printf, @@ -1819,7 +1838,7 @@ func (fr *http2Framer) ReadFrame() (http2Frame, error) { if _, err := io.ReadFull(fr.r, payload); err != nil { return nil, err } - f, err := http2typeFrameParser(fh.Type)(fr.frameCache, fh, payload) + f, err := http2typeFrameParser(fh.Type)(fr.frameCache, fh, fr.countError, payload) if err != nil { if ce, ok := err.(http2connError); ok { return nil, fr.connError(ce.Code, ce.Reason) @@ -1888,7 +1907,7 @@ func (fr *http2Framer) checkFrameOrder(f http2Frame) error { // A DataFrame conveys arbitrary, variable-length sequences of octets // associated with a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.1 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.1 type http2DataFrame struct { http2FrameHeader data []byte @@ -1907,13 +1926,14 @@ func (f *http2DataFrame) Data() []byte { return f.data } -func http2parseDataFrame(fc *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { +func http2parseDataFrame(fc *http2frameCache, fh http2FrameHeader, countError func(string), payload []byte) (http2Frame, error) { if fh.StreamID == 0 { // DATA frames MUST be associated with a stream. If a // DATA frame is received whose stream identifier // field is 0x0, the recipient MUST respond with a // connection error (Section 5.4.1) of type // PROTOCOL_ERROR. + countError("frame_data_stream_0") return nil, http2connError{http2ErrCodeProtocol, "DATA frame with stream ID 0"} } f := fc.getDataFrame() @@ -1924,6 +1944,7 @@ func http2parseDataFrame(fc *http2frameCache, fh http2FrameHeader, payload []byt var err error payload, padSize, err = http2readByte(payload) if err != nil { + countError("frame_data_pad_byte_short") return nil, err } } @@ -1932,6 +1953,7 @@ func http2parseDataFrame(fc *http2frameCache, fh http2FrameHeader, payload []byt // length of the frame payload, the recipient MUST // treat this as a connection error. // Filed: https://github.com/http2/http2-spec/issues/610 + countError("frame_data_pad_too_big") return nil, http2connError{http2ErrCodeProtocol, "pad size larger than data payload"} } f.data = payload[:len(payload)-int(padSize)] @@ -2008,13 +2030,13 @@ func (f *http2Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad // endpoints communicate, such as preferences and constraints on peer // behavior. // -// See http://http2.github.io/http2-spec/#SETTINGS +// See https://httpwg.org/specs/rfc7540.html#SETTINGS type http2SettingsFrame struct { http2FrameHeader p []byte } -func http2parseSettingsFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseSettingsFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { if fh.Flags.Has(http2FlagSettingsAck) && fh.Length > 0 { // When this (ACK 0x1) bit is set, the payload of the // SETTINGS frame MUST be empty. Receipt of a @@ -2022,6 +2044,7 @@ func http2parseSettingsFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) // field value other than 0 MUST be treated as a // connection error (Section 5.4.1) of type // FRAME_SIZE_ERROR. + countError("frame_settings_ack_with_length") return nil, http2ConnectionError(http2ErrCodeFrameSize) } if fh.StreamID != 0 { @@ -2032,14 +2055,17 @@ func http2parseSettingsFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) // field is anything other than 0x0, the endpoint MUST // respond with a connection error (Section 5.4.1) of // type PROTOCOL_ERROR. + countError("frame_settings_has_stream") return nil, http2ConnectionError(http2ErrCodeProtocol) } if len(p)%6 != 0 { + countError("frame_settings_mod_6") // Expecting even number of 6 byte settings. return nil, http2ConnectionError(http2ErrCodeFrameSize) } f := &http2SettingsFrame{http2FrameHeader: fh, p: p} if v, ok := f.Value(http2SettingInitialWindowSize); ok && v > (1<<31)-1 { + countError("frame_settings_window_size_too_big") // Values above the maximum flow control window size of 2^31 - 1 MUST // be treated as a connection error (Section 5.4.1) of type // FLOW_CONTROL_ERROR. @@ -2143,7 +2169,7 @@ func (f *http2Framer) WriteSettingsAck() error { // A PingFrame is a mechanism for measuring a minimal round trip time // from the sender, as well as determining whether an idle connection // is still functional. -// See http://http2.github.io/http2-spec/#rfc.section.6.7 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.7 type http2PingFrame struct { http2FrameHeader Data [8]byte @@ -2151,11 +2177,13 @@ type http2PingFrame struct { func (f *http2PingFrame) IsAck() bool { return f.Flags.Has(http2FlagPingAck) } -func http2parsePingFrame(_ *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { +func http2parsePingFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), payload []byte) (http2Frame, error) { if len(payload) != 8 { + countError("frame_ping_length") return nil, http2ConnectionError(http2ErrCodeFrameSize) } if fh.StreamID != 0 { + countError("frame_ping_has_stream") return nil, http2ConnectionError(http2ErrCodeProtocol) } f := &http2PingFrame{http2FrameHeader: fh} @@ -2174,7 +2202,7 @@ func (f *http2Framer) WritePing(ack bool, data [8]byte) error { } // A GoAwayFrame informs the remote peer to stop creating streams on this connection. -// See http://http2.github.io/http2-spec/#rfc.section.6.8 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.8 type http2GoAwayFrame struct { http2FrameHeader LastStreamID uint32 @@ -2191,11 +2219,13 @@ func (f *http2GoAwayFrame) DebugData() []byte { return f.debugData } -func http2parseGoAwayFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseGoAwayFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { if fh.StreamID != 0 { + countError("frame_goaway_has_stream") return nil, http2ConnectionError(http2ErrCodeProtocol) } if len(p) < 8 { + countError("frame_goaway_short") return nil, http2ConnectionError(http2ErrCodeFrameSize) } return &http2GoAwayFrame{ @@ -2231,19 +2261,20 @@ func (f *http2UnknownFrame) Payload() []byte { return f.p } -func http2parseUnknownFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseUnknownFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { return &http2UnknownFrame{fh, p}, nil } // A WindowUpdateFrame is used to implement flow control. -// See http://http2.github.io/http2-spec/#rfc.section.6.9 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.9 type http2WindowUpdateFrame struct { http2FrameHeader Increment uint32 // never read with high bit set } -func http2parseWindowUpdateFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseWindowUpdateFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { if len(p) != 4 { + countError("frame_windowupdate_bad_len") return nil, http2ConnectionError(http2ErrCodeFrameSize) } inc := binary.BigEndian.Uint32(p[:4]) & 0x7fffffff // mask off high reserved bit @@ -2255,8 +2286,10 @@ func http2parseWindowUpdateFrame(_ *http2frameCache, fh http2FrameHeader, p []by // control window MUST be treated as a connection // error (Section 5.4.1). if fh.StreamID == 0 { + countError("frame_windowupdate_zero_inc_conn") return nil, http2ConnectionError(http2ErrCodeProtocol) } + countError("frame_windowupdate_zero_inc_stream") return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) } return &http2WindowUpdateFrame{ @@ -2307,7 +2340,7 @@ func (f *http2HeadersFrame) HasPriority() bool { return f.http2FrameHeader.Flags.Has(http2FlagHeadersPriority) } -func http2parseHeadersFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ http2Frame, err error) { +func http2parseHeadersFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (_ http2Frame, err error) { hf := &http2HeadersFrame{ http2FrameHeader: fh, } @@ -2316,11 +2349,13 @@ func http2parseHeadersFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) ( // is received whose stream identifier field is 0x0, the recipient MUST // respond with a connection error (Section 5.4.1) of type // PROTOCOL_ERROR. + countError("frame_headers_zero_stream") return nil, http2connError{http2ErrCodeProtocol, "HEADERS frame with stream ID 0"} } var padLength uint8 if fh.Flags.Has(http2FlagHeadersPadded) { if p, padLength, err = http2readByte(p); err != nil { + countError("frame_headers_pad_short") return } } @@ -2328,16 +2363,19 @@ func http2parseHeadersFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) ( var v uint32 p, v, err = http2readUint32(p) if err != nil { + countError("frame_headers_prio_short") return nil, err } hf.Priority.StreamDep = v & 0x7fffffff hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set p, hf.Priority.Weight, err = http2readByte(p) if err != nil { + countError("frame_headers_prio_weight_short") return nil, err } } - if len(p)-int(padLength) <= 0 { + if len(p)-int(padLength) < 0 { + countError("frame_headers_pad_too_big") return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol) } hf.headerFragBuf = p[:len(p)-int(padLength)] @@ -2417,7 +2455,7 @@ func (f *http2Framer) WriteHeaders(p http2HeadersFrameParam) error { } // A PriorityFrame specifies the sender-advised priority of a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.3 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.3 type http2PriorityFrame struct { http2FrameHeader http2PriorityParam @@ -2444,11 +2482,13 @@ func (p http2PriorityParam) IsZero() bool { return p == http2PriorityParam{} } -func http2parsePriorityFrame(_ *http2frameCache, fh http2FrameHeader, payload []byte) (http2Frame, error) { +func http2parsePriorityFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), payload []byte) (http2Frame, error) { if fh.StreamID == 0 { + countError("frame_priority_zero_stream") return nil, http2connError{http2ErrCodeProtocol, "PRIORITY frame with stream ID 0"} } if len(payload) != 5 { + countError("frame_priority_bad_length") return nil, http2connError{http2ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))} } v := binary.BigEndian.Uint32(payload[:4]) @@ -2485,17 +2525,19 @@ func (f *http2Framer) WritePriority(streamID uint32, p http2PriorityParam) error } // A RSTStreamFrame allows for abnormal termination of a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.4 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4 type http2RSTStreamFrame struct { http2FrameHeader ErrCode http2ErrCode } -func http2parseRSTStreamFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseRSTStreamFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { if len(p) != 4 { + countError("frame_rststream_bad_len") return nil, http2ConnectionError(http2ErrCodeFrameSize) } if fh.StreamID == 0 { + countError("frame_rststream_zero_stream") return nil, http2ConnectionError(http2ErrCodeProtocol) } return &http2RSTStreamFrame{fh, http2ErrCode(binary.BigEndian.Uint32(p[:4]))}, nil @@ -2515,14 +2557,15 @@ func (f *http2Framer) WriteRSTStream(streamID uint32, code http2ErrCode) error { } // A ContinuationFrame is used to continue a sequence of header block fragments. -// See http://http2.github.io/http2-spec/#rfc.section.6.10 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.10 type http2ContinuationFrame struct { http2FrameHeader headerFragBuf []byte } -func http2parseContinuationFrame(_ *http2frameCache, fh http2FrameHeader, p []byte) (http2Frame, error) { +func http2parseContinuationFrame(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (http2Frame, error) { if fh.StreamID == 0 { + countError("frame_continuation_zero_stream") return nil, http2connError{http2ErrCodeProtocol, "CONTINUATION frame with stream ID 0"} } return &http2ContinuationFrame{fh, p}, nil @@ -2555,7 +2598,7 @@ func (f *http2Framer) WriteContinuation(streamID uint32, endHeaders bool, header } // A PushPromiseFrame is used to initiate a server stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.6 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.6 type http2PushPromiseFrame struct { http2FrameHeader PromiseID uint32 @@ -2571,7 +2614,7 @@ func (f *http2PushPromiseFrame) HeadersEnded() bool { return f.http2FrameHeader.Flags.Has(http2FlagPushPromiseEndHeaders) } -func http2parsePushPromise(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ http2Frame, err error) { +func http2parsePushPromise(_ *http2frameCache, fh http2FrameHeader, countError func(string), p []byte) (_ http2Frame, err error) { pp := &http2PushPromiseFrame{ http2FrameHeader: fh, } @@ -2582,6 +2625,7 @@ func http2parsePushPromise(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ // with. If the stream identifier field specifies the value // 0x0, a recipient MUST respond with a connection error // (Section 5.4.1) of type PROTOCOL_ERROR. + countError("frame_pushpromise_zero_stream") return nil, http2ConnectionError(http2ErrCodeProtocol) } // The PUSH_PROMISE frame includes optional padding. @@ -2589,18 +2633,21 @@ func http2parsePushPromise(_ *http2frameCache, fh http2FrameHeader, p []byte) (_ var padLength uint8 if fh.Flags.Has(http2FlagPushPromisePadded) { if p, padLength, err = http2readByte(p); err != nil { + countError("frame_pushpromise_pad_short") return } } p, pp.PromiseID, err = http2readUint32(p) if err != nil { + countError("frame_pushpromise_promiseid_short") return } pp.PromiseID = pp.PromiseID & (1<<31 - 1) if int(padLength) > len(p) { // like the DATA frame, error out if padding is longer than the body. + countError("frame_pushpromise_pad_too_big") return nil, http2ConnectionError(http2ErrCodeProtocol) } pp.headerFragBuf = p[:len(p)-int(padLength)] @@ -2817,7 +2864,8 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) } if !httpguts.ValidHeaderFieldValue(hf.Value) { - invalid = http2headerFieldValueError(hf.Value) + // Don't include the value in the error, because it may be sensitive. + invalid = http2headerFieldValueError(hf.Name) } isPseudo := strings.HasPrefix(hf.Name, ":") if isPseudo { @@ -2963,6 +3011,10 @@ func (t *http2Transport) dialTLSWithContext(ctx context.Context, network, addr s return tlsCn, nil } +func http2tlsUnderlyingConn(tc *tls.Conn) net.Conn { + return tc.NetConn() +} + var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" type http2goroutineLock uint64 @@ -3217,14 +3269,14 @@ const ( http2ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // SETTINGS_MAX_FRAME_SIZE default - // http://http2.github.io/http2-spec/#rfc.section.6.5.2 + // https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2 http2initialMaxFrameSize = 16384 // NextProtoTLS is the NPN/ALPN protocol negotiated during // HTTP/2's TLS setup. http2NextProtoTLS = "h2" - // http://http2.github.io/http2-spec/#SettingValues + // https://httpwg.org/specs/rfc7540.html#SettingValues http2initialHeaderTableSize = 4096 http2initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size @@ -3273,7 +3325,7 @@ func (st http2streamState) String() string { // Setting is a setting parameter: which setting it is, and its value. type http2Setting struct { // ID is which setting is being set. - // See http://http2.github.io/http2-spec/#SettingValues + // See https://httpwg.org/specs/rfc7540.html#SettingFormat ID http2SettingID // Val is the value. @@ -3305,7 +3357,7 @@ func (s http2Setting) Valid() error { } // A SettingID is an HTTP/2 setting as defined in -// http://http2.github.io/http2-spec/#iana-settings +// https://httpwg.org/specs/rfc7540.html#iana-settings type http2SettingID uint16 const ( @@ -3337,10 +3389,11 @@ func (s http2SettingID) String() string { // name (key). See httpguts.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. " +// +// "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 http2validWireHeaderFieldName(v string) bool { if len(v) == 0 { return false @@ -3531,8 +3584,8 @@ func (s *http2sorter) SortStrings(ss []string) { // validPseudoPath reports whether v is a valid :path pseudo-header // value. It must be either: // -// *) a non-empty string starting with '/' -// *) the string '*', for OPTIONS requests. +// - a non-empty string starting with '/' +// - the string '*', for OPTIONS requests. // // For now this is only used a quick check for deciding when to clean // up Opaque URLs before sending requests from the Transport. @@ -3570,6 +3623,17 @@ type http2pipeBuffer interface { io.Reader } +// setBuffer initializes the pipe buffer. +// It has no effect if the pipe is already closed. +func (p *http2pipe) setBuffer(b http2pipeBuffer) { + p.mu.Lock() + defer p.mu.Unlock() + if p.err != nil || p.breakErr != nil { + return + } + p.b = b +} + func (p *http2pipe) Len() int { p.mu.Lock() defer p.mu.Unlock() @@ -3786,6 +3850,12 @@ type http2Server struct { // If nil, a default scheduler is chosen. NewWriteScheduler func() http2WriteScheduler + // CountError, if non-nil, is called on HTTP/2 server errors. + // It's intended to increment a metric for monitoring, such + // as an expvar or Prometheus metric. + // The errType consists of only ASCII word characters. + CountError func(errType string) + // Internal state. This is a pointer (rather than embedded directly) // so that we don't embed a Mutex in this struct, which will make the // struct non-copyable, which might break some callers. @@ -3915,16 +3985,12 @@ func http2ConfigureServer(s *Server, conf *http2Server) error { s.TLSConfig.PreferServerCipherSuites = true - haveNPN := false - for _, p := range s.TLSConfig.NextProtos { - if p == http2NextProtoTLS { - haveNPN = true - break - } - } - if !haveNPN { + if !http2strSliceContains(s.TLSConfig.NextProtos, http2NextProtoTLS) { s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, http2NextProtoTLS) } + if !http2strSliceContains(s.TLSConfig.NextProtos, "http/1.1") { + s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "http/1.1") + } if s.TLSNextProto == nil { s.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){} @@ -3969,6 +4035,20 @@ type http2ServeConnOpts struct { // requests. If nil, BaseConfig.Handler is used. If BaseConfig // or BaseConfig.Handler is nil, http.DefaultServeMux is used. Handler Handler + + // UpgradeRequest is an initial request received on a connection + // undergoing an h2c upgrade. The request body must have been + // completely read from the connection before calling ServeConn, + // and the 101 Switching Protocols response written. + UpgradeRequest *Request + + // Settings is the decoded contents of the HTTP2-Settings header + // in an h2c upgrade request. + Settings []byte + + // SawClientPreface is set if the HTTP/2 connection preface + // has already been read from the connection. + SawClientPreface bool } func (o *http2ServeConnOpts) context() context.Context { @@ -4037,6 +4117,7 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { headerTableSize: http2initialHeaderTableSize, serveG: http2newGoroutineLock(), pushEnabled: true, + sawClientPreface: opts.SawClientPreface, } s.state.registerConn(sc) @@ -4054,7 +4135,7 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { if s.NewWriteScheduler != nil { sc.writeSched = s.NewWriteScheduler() } else { - sc.writeSched = http2NewRandomWriteScheduler() + sc.writeSched = http2NewPriorityWriteScheduler(nil) } // These start at the RFC-specified defaults. If there is a higher @@ -4065,6 +4146,9 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) fr := http2NewFramer(sc.bw, c) + if s.CountError != nil { + fr.countError = s.CountError + } fr.ReadMetaHeaders = hpack.NewDecoder(http2initialHeaderTableSize, nil) fr.MaxHeaderListSize = sc.maxHeaderListSize() fr.SetMaxReadFrameSize(s.maxReadFrameSize()) @@ -4116,9 +4200,27 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) { } } + if opts.Settings != nil { + fr := &http2SettingsFrame{ + http2FrameHeader: http2FrameHeader{valid: true}, + p: opts.Settings, + } + if err := fr.ForeachSetting(sc.processSetting); err != nil { + sc.rejectConn(http2ErrCodeProtocol, "invalid settings") + return + } + opts.Settings = nil + } + if hook := http2testHookGetServerConn; hook != nil { hook(sc) } + + if opts.UpgradeRequest != nil { + sc.upgradeRequest(opts.UpgradeRequest) + opts.UpgradeRequest = nil + } + sc.serve() } @@ -4163,6 +4265,7 @@ type http2serverConn struct { // Everything following is owned by the serve loop; use serveG.check(): serveG http2goroutineLock // used to verify funcs are on serve() pushEnabled bool + sawClientPreface bool // preface has already been read, used in h2c upgrade sawFirstSettings bool // got the initial SETTINGS frame after the preface needToSendSettingsAck bool unackedSettings int // how many SETTINGS have we sent without ACKs? @@ -4373,7 +4476,15 @@ func (sc *http2serverConn) canonicalHeader(v string) string { sc.canonHeader = make(map[string]string) } cv = CanonicalHeaderKey(v) - sc.canonHeader[v] = cv + // maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of + // entries in the canonHeader cache. This should be larger than the number + // of unique, uncommon header keys likely to be sent by the peer, while not + // so high as to permit unreasonable memory usage if the peer sends an unbounded + // number of unique header keys. + const maxCachedCanonicalHeaders = 32 + if len(sc.canonHeader) < maxCachedCanonicalHeaders { + sc.canonHeader[v] = cv + } return cv } @@ -4479,7 +4590,7 @@ func (sc *http2serverConn) serve() { }) sc.unackedSettings++ - // Each connection starts with intialWindowSize inflow tokens. + // Each connection starts with initialWindowSize inflow tokens. // If a higher value is configured, we add more tokens. if diff := sc.srv.initialConnRecvWindowSize() - http2initialWindowSize; diff > 0 { sc.sendWindowUpdate(nil, int(diff)) @@ -4519,6 +4630,15 @@ func (sc *http2serverConn) serve() { case res := <-sc.wroteFrameCh: sc.wroteFrame(res) case res := <-sc.readFrameCh: + // Process any written frames before reading new frames from the client since a + // written frame could have triggered a new stream to be started. + if sc.writingFrameAsync { + select { + case wroteRes := <-sc.wroteFrameCh: + sc.wroteFrame(wroteRes) + default: + } + } if !sc.processFrameFromReader(res) { return } @@ -4613,6 +4733,9 @@ var http2errPrefaceTimeout = errors.New("timeout waiting for client preface") // returns errPrefaceTimeout on timeout, or an error if the greeting // is invalid. func (sc *http2serverConn) readPreface() error { + if sc.sawClientPreface { + return nil + } errc := make(chan error, 1) go func() { // Read the client preface @@ -4973,6 +5096,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() { func (sc *http2serverConn) goAway(code http2ErrCode) { sc.serveG.check() if sc.inGoAway { + if sc.goAwayCode == http2ErrCodeNo { + sc.goAwayCode = code + } return } sc.inGoAway = true @@ -5055,7 +5181,7 @@ func (sc *http2serverConn) processFrame(f http2Frame) error { // First frame received must be SETTINGS. if !sc.sawFirstSettings { if _, ok := f.(*http2SettingsFrame); !ok { - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("first_settings", http2ConnectionError(http2ErrCodeProtocol)) } sc.sawFirstSettings = true } @@ -5080,7 +5206,7 @@ func (sc *http2serverConn) processFrame(f http2Frame) error { case *http2PushPromiseFrame: // A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE // frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("push_promise", http2ConnectionError(http2ErrCodeProtocol)) default: sc.vlogf("http2: server ignoring frame: %v", f.Header()) return nil @@ -5100,7 +5226,7 @@ func (sc *http2serverConn) processPing(f *http2PingFrame) error { // identifier field value other than 0x0, the recipient MUST // respond with a connection error (Section 5.4.1) of type // PROTOCOL_ERROR." - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("ping_on_stream", http2ConnectionError(http2ErrCodeProtocol)) } if sc.inGoAway && sc.goAwayCode != http2ErrCodeNo { return nil @@ -5119,7 +5245,7 @@ func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error // or PRIORITY on a stream in this state MUST be // treated as a connection error (Section 5.4.1) of // type PROTOCOL_ERROR." - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("stream_idle", http2ConnectionError(http2ErrCodeProtocol)) } if st == nil { // "WINDOW_UPDATE can be sent by a peer that has sent a @@ -5130,7 +5256,7 @@ func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error return nil } if !st.flow.add(int32(f.Increment)) { - return http2streamError(f.StreamID, http2ErrCodeFlowControl) + return sc.countError("bad_flow", http2streamError(f.StreamID, http2ErrCodeFlowControl)) } default: // connection-level flow control if !sc.flow.add(int32(f.Increment)) { @@ -5151,7 +5277,7 @@ func (sc *http2serverConn) processResetStream(f *http2RSTStreamFrame) error { // identifying an idle stream is received, the // recipient MUST treat this as a connection error // (Section 5.4.1) of type PROTOCOL_ERROR. - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("reset_idle_stream", http2ConnectionError(http2ErrCodeProtocol)) } if st != nil { st.cancelCtx() @@ -5203,7 +5329,7 @@ func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error { // Why is the peer ACKing settings we never sent? // The spec doesn't mention this case, but // hang up on them anyway. - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("ack_mystery", http2ConnectionError(http2ErrCodeProtocol)) } return nil } @@ -5211,7 +5337,7 @@ func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error { // This isn't actually in the spec, but hang up on // suspiciously large settings frames or those with // duplicate entries. - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("settings_big_or_dups", http2ConnectionError(http2ErrCodeProtocol)) } if err := f.ForeachSetting(sc.processSetting); err != nil { return err @@ -5278,7 +5404,7 @@ func (sc *http2serverConn) processSettingInitialWindowSize(val uint32) error { // control window to exceed the maximum size as a // connection error (Section 5.4.1) of type // FLOW_CONTROL_ERROR." - return http2ConnectionError(http2ErrCodeFlowControl) + return sc.countError("setting_win_size", http2ConnectionError(http2ErrCodeFlowControl)) } } return nil @@ -5311,7 +5437,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // or PRIORITY on a stream in this state MUST be // treated as a connection error (Section 5.4.1) of // type PROTOCOL_ERROR." - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("data_on_idle", http2ConnectionError(http2ErrCodeProtocol)) } // "If a DATA frame is received whose stream is not in "open" @@ -5328,7 +5454,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // and return any flow control bytes since we're not going // to consume them. if sc.inflow.available() < int32(f.Length) { - return http2streamError(id, http2ErrCodeFlowControl) + return sc.countError("data_flow", http2streamError(id, http2ErrCodeFlowControl)) } // Deduct the flow control from inflow, since we're // going to immediately add it back in @@ -5341,7 +5467,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // Already have a stream error in flight. Don't send another. return nil } - return http2streamError(id, http2ErrCodeStreamClosed) + return sc.countError("closed", http2streamError(id, http2ErrCodeStreamClosed)) } if st.body == nil { panic("internal error: should have a body in this state") @@ -5349,16 +5475,22 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { // Sender sending more than they'd declared? if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { + if sc.inflow.available() < int32(f.Length) { + return sc.countError("data_flow", http2streamError(id, http2ErrCodeFlowControl)) + } + sc.inflow.take(int32(f.Length)) + sc.sendWindowUpdate(nil, int(f.Length)) // conn-level + st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) // RFC 7540, sec 8.1.2.6: A request or response is also malformed if the // value of a content-length header field does not equal the sum of the // DATA frame payload lengths that form the body. - return http2streamError(id, http2ErrCodeProtocol) + return sc.countError("send_too_much", http2streamError(id, http2ErrCodeProtocol)) } if f.Length > 0 { // Check whether the client has flow control quota. if st.inflow.available() < int32(f.Length) { - return http2streamError(id, http2ErrCodeFlowControl) + return sc.countError("flow_on_data_length", http2streamError(id, http2ErrCodeFlowControl)) } st.inflow.take(int32(f.Length)) @@ -5366,7 +5498,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error { wrote, err := st.body.Write(data) if err != nil { sc.sendWindowUpdate(nil, int(f.Length)-wrote) - return http2streamError(id, http2ErrCodeStreamClosed) + return sc.countError("body_write_err", http2streamError(id, http2ErrCodeStreamClosed)) } if wrote != len(data) { panic("internal error: bad Writer") @@ -5452,7 +5584,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { // stream identifier MUST respond with a connection error // (Section 5.4.1) of type PROTOCOL_ERROR. if id%2 != 1 { - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("headers_even", http2ConnectionError(http2ErrCodeProtocol)) } // A HEADERS frame can be used to create a new stream or // send a trailer for an open one. If we already have a stream @@ -5469,7 +5601,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { // this state, it MUST respond with a stream error (Section 5.4.2) of // type STREAM_CLOSED. if st.state == http2stateHalfClosedRemote { - return http2streamError(id, http2ErrCodeStreamClosed) + return sc.countError("headers_half_closed", http2streamError(id, http2ErrCodeStreamClosed)) } return st.processTrailerHeaders(f) } @@ -5480,7 +5612,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { // receives an unexpected stream identifier MUST respond with // a connection error (Section 5.4.1) of type PROTOCOL_ERROR. if id <= sc.maxClientStreamID { - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("stream_went_down", http2ConnectionError(http2ErrCodeProtocol)) } sc.maxClientStreamID = id @@ -5497,14 +5629,14 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { if sc.curClientStreams+1 > sc.advMaxStreams { if sc.unackedSettings == 0 { // They should know better. - return http2streamError(id, http2ErrCodeProtocol) + return sc.countError("over_max_streams", http2streamError(id, http2ErrCodeProtocol)) } // Assume it's a network race, where they just haven't // received our last SETTINGS update. But actually // this can't happen yet, because we don't yet provide // a way for users to adjust server parameters at // runtime. - return http2streamError(id, http2ErrCodeRefusedStream) + return sc.countError("over_max_streams_race", http2streamError(id, http2ErrCodeRefusedStream)) } initialState := http2stateOpen @@ -5514,7 +5646,7 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { st := sc.newStream(id, 0, initialState) if f.HasPriority() { - if err := http2checkPriority(f.StreamID, f.Priority); err != nil { + if err := sc.checkPriority(f.StreamID, f.Priority); err != nil { return err } sc.writeSched.AdjustStream(st.id, f.Priority) @@ -5554,19 +5686,39 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error { return nil } +func (sc *http2serverConn) upgradeRequest(req *Request) { + sc.serveG.check() + id := uint32(1) + sc.maxClientStreamID = id + st := sc.newStream(id, 0, http2stateHalfClosedRemote) + st.reqTrailer = req.Trailer + if st.reqTrailer != nil { + st.trailer = make(Header) + } + rw := sc.newResponseWriter(st, req) + + // Disable any read deadline set by the net/http package + // prior to the upgrade. + if sc.hs.ReadTimeout != 0 { + sc.conn.SetReadDeadline(time.Time{}) + } + + go sc.runHandler(rw, req, sc.handler.ServeHTTP) +} + func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { sc := st.sc sc.serveG.check() if st.gotTrailerHeader { - return http2ConnectionError(http2ErrCodeProtocol) + return sc.countError("dup_trailers", http2ConnectionError(http2ErrCodeProtocol)) } st.gotTrailerHeader = true if !f.StreamEnded() { - return http2streamError(st.id, http2ErrCodeProtocol) + return sc.countError("trailers_not_ended", http2streamError(st.id, http2ErrCodeProtocol)) } if len(f.PseudoFields()) > 0 { - return http2streamError(st.id, http2ErrCodeProtocol) + return sc.countError("trailers_pseudo", http2streamError(st.id, http2ErrCodeProtocol)) } if st.trailer != nil { for _, hf := range f.RegularFields() { @@ -5575,7 +5727,7 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { // TODO: send more details to the peer somehow. But http2 has // no way to send debug data at a stream level. Discuss with // HTTP folk. - return http2streamError(st.id, http2ErrCodeProtocol) + return sc.countError("trailers_bogus", http2streamError(st.id, http2ErrCodeProtocol)) } st.trailer[key] = append(st.trailer[key], hf.Value) } @@ -5584,13 +5736,13 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error { return nil } -func http2checkPriority(streamID uint32, p http2PriorityParam) error { +func (sc *http2serverConn) checkPriority(streamID uint32, p http2PriorityParam) error { if streamID == p.StreamDep { // Section 5.3.1: "A stream cannot depend on itself. An endpoint MUST treat // this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR." // Section 5.3.3 says that a stream can depend on one of its dependencies, // so it's only self-dependencies that are forbidden. - return http2streamError(streamID, http2ErrCodeProtocol) + return sc.countError("priority", http2streamError(streamID, http2ErrCodeProtocol)) } return nil } @@ -5599,7 +5751,7 @@ func (sc *http2serverConn) processPriority(f *http2PriorityFrame) error { if sc.inGoAway { return nil } - if err := http2checkPriority(f.StreamID, f.http2PriorityParam); err != nil { + if err := sc.checkPriority(f.StreamID, f.http2PriorityParam); err != nil { return err } sc.writeSched.AdjustStream(f.StreamID, f.http2PriorityParam) @@ -5656,7 +5808,7 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead isConnect := rp.method == "CONNECT" if isConnect { if rp.path != "" || rp.scheme != "" || rp.authority == "" { - return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) + return nil, nil, sc.countError("bad_connect", http2streamError(f.StreamID, http2ErrCodeProtocol)) } } else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") { // See 8.1.2.6 Malformed Requests and Responses: @@ -5669,13 +5821,13 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead // "All HTTP/2 requests MUST include exactly one valid // value for the :method, :scheme, and :path // pseudo-header fields" - return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) + return nil, nil, sc.countError("bad_path_method", http2streamError(f.StreamID, http2ErrCodeProtocol)) } bodyOpen := !f.StreamEnded() if rp.method == "HEAD" && bodyOpen { // HEAD requests can't have bodies - return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol) + return nil, nil, sc.countError("head_body", http2streamError(f.StreamID, http2ErrCodeProtocol)) } rp.header = make(Header) @@ -5758,7 +5910,7 @@ func (sc *http2serverConn) newWriterAndRequestNoBody(st *http2stream, rp http2re var err error url_, err = url.ParseRequestURI(rp.path) if err != nil { - return nil, nil, http2streamError(st.id, http2ErrCodeProtocol) + return nil, nil, sc.countError("bad_path", http2streamError(st.id, http2ErrCodeProtocol)) } requestURI = rp.path } @@ -5784,6 +5936,11 @@ func (sc *http2serverConn) newWriterAndRequestNoBody(st *http2stream, rp http2re } req = req.WithContext(st.ctx) + rw := sc.newResponseWriter(st, req) + return rw, req, nil +} + +func (sc *http2serverConn) newResponseWriter(st *http2stream, req *Request) *http2responseWriter { rws := http2responseWriterStatePool.Get().(*http2responseWriterState) bwSave := rws.bw *rws = http2responseWriterState{} // zero all the fields @@ -5792,10 +5949,7 @@ func (sc *http2serverConn) newWriterAndRequestNoBody(st *http2stream, rp http2re rws.bw.Reset(http2chunkWriter{rws}) rws.stream = st rws.req = req - rws.body = body - - rw := &http2responseWriter{rws: rws} - return rw, req, nil + return &http2responseWriter{rws: rws} } // Run on its own goroutine. @@ -5803,6 +5957,9 @@ func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, han didPanic := true defer func() { rw.rws.stream.cancelCtx() + if req.MultipartForm != nil { + req.MultipartForm.RemoveAll() + } if didPanic { e := recover() sc.writeFrameFromHandler(http2FrameWriteRequest{ @@ -5955,17 +6112,18 @@ type http2requestBody struct { _ http2incomparable stream *http2stream conn *http2serverConn - closed bool // for use by Close only + closeOnce sync.Once // for use by Close only sawEOF bool // for use by Read only pipe *http2pipe // non-nil if we have a HTTP entity message body needsContinue bool // need to send a 100-continue } func (b *http2requestBody) Close() error { - if b.pipe != nil && !b.closed { - b.pipe.BreakWithError(http2errClosedBody) - } - b.closed = true + b.closeOnce.Do(func() { + if b.pipe != nil { + b.pipe.BreakWithError(http2errClosedBody) + } + }) return nil } @@ -6009,7 +6167,6 @@ type http2responseWriterState struct { // immutable within a request: stream *http2stream req *Request - body *http2requestBody // to close at end of request, if DATA frames didn't conn *http2serverConn // TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc @@ -6185,8 +6342,9 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { // prior to the headers being written. If the set of trailers is fixed // or known before the header is written, the normal Go trailers mechanism // is preferred: -// https://golang.org/pkg/net/http/#ResponseWriter -// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers +// +// https://golang.org/pkg/net/http/#ResponseWriter +// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers const http2TrailerPrefix = "Trailer:" // promoteUndeclaredTrailers permits http.Handlers to set trailers @@ -6282,8 +6440,7 @@ func http2checkWriteHeaderCode(code int) { // Issue 22880: require valid WriteHeader status codes. // For now we only enforce that it's three digits. // In the future we might block things over 599 (600 and above aren't defined - // at http://httpwg.org/specs/rfc7231.html#status.codes) - // and we might block under 200 (once we have more mature 1xx support). + // at http://httpwg.org/specs/rfc7231.html#status.codes). // But for now any three digits. // // We used to send "HTTP/1.1 000 0" on the wire in responses but there's @@ -6304,13 +6461,41 @@ func (w *http2responseWriter) WriteHeader(code int) { } func (rws *http2responseWriterState) writeHeader(code int) { - if !rws.wroteHeader { - http2checkWriteHeaderCode(code) - rws.wroteHeader = true - rws.status = code - if len(rws.handlerHeader) > 0 { - rws.snapHeader = http2cloneHeader(rws.handlerHeader) + if rws.wroteHeader { + return + } + + http2checkWriteHeaderCode(code) + + // Handle informational headers + if code >= 100 && code <= 199 { + // Per RFC 8297 we must not clear the current header map + h := rws.handlerHeader + + _, cl := h["Content-Length"] + _, te := h["Transfer-Encoding"] + if cl || te { + h = h.Clone() + h.Del("Content-Length") + h.Del("Transfer-Encoding") } + + if rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ + streamID: rws.stream.id, + httpResCode: code, + h: h, + endStream: rws.handlerDone && !rws.hasTrailers(), + }) != nil { + rws.dirty = true + } + + return + } + + rws.wroteHeader = true + rws.status = code + if len(rws.handlerHeader) > 0 { + rws.snapHeader = http2cloneHeader(rws.handlerHeader) } } @@ -6642,6 +6827,34 @@ func http2h1ServerKeepAlivesDisabled(hs *Server) bool { return false } +func (sc *http2serverConn) countError(name string, err error) error { + if sc == nil || sc.srv == nil { + return err + } + f := sc.srv.CountError + if f == nil { + return err + } + var typ string + var code http2ErrCode + switch e := err.(type) { + case http2ConnectionError: + typ = "conn" + code = http2ErrCode(e) + case http2StreamError: + typ = "stream" + code = http2ErrCode(e.Code) + default: + return err + } + codeStr := http2errCodeName[code] + if codeStr == "" { + codeStr = strconv.Itoa(int(code)) + } + f(fmt.Sprintf("%s_%s_%s", typ, codeStr, name)) + return err +} + const ( // transportDefaultConnFlow is how many connection-level flow control // tokens we give the server at start-up, past the default 64k. @@ -6657,6 +6870,15 @@ const ( http2transportDefaultStreamMinRefresh = 4 << 10 http2defaultUserAgent = "Go-http-client/2.0" + + // initialMaxConcurrentStreams is a connections maxConcurrentStreams until + // it's received servers initial SETTINGS frame, which corresponds with the + // spec's minimum recommended value. + http2initialMaxConcurrentStreams = 100 + + // defaultMaxConcurrentStreams is a connections default maxConcurrentStreams + // if the server doesn't include one in its initial SETTINGS frame. + http2defaultMaxConcurrentStreams = 1000 ) // Transport is an HTTP/2 Transport. @@ -6664,13 +6886,23 @@ const ( // A Transport internally caches connections to servers. It is safe // for concurrent use by multiple goroutines. type http2Transport struct { - // DialTLS specifies an optional dial function for creating - // TLS connections for requests. + // DialTLSContext specifies an optional dial function with context for + // creating TLS connections for requests. // - // If DialTLS is nil, tls.Dial is used. + // If DialTLSContext and DialTLS is nil, tls.Dial is used. // // If the returned net.Conn has a ConnectionState method like tls.Conn, // it will be used to set http.Response.TLS. + DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) + + // DialTLS specifies an optional dial function for creating + // TLS connections for requests. + // + // If DialTLSContext and DialTLS is nil, tls.Dial is used. + // + // Deprecated: Use DialTLSContext instead, which allows the transport + // to cancel dials as soon as they are no longer needed. + // If both are set, DialTLSContext takes priority. DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error) // TLSClientConfig specifies the TLS configuration to use with @@ -6727,6 +6959,17 @@ type http2Transport struct { // Defaults to 15s. PingTimeout time.Duration + // WriteByteTimeout is the timeout after which the connection will be + // closed no data can be written to it. The timeout begins when data is + // available to write, and is extended whenever any bytes are written. + WriteByteTimeout time.Duration + + // CountError, if non-nil, is called on HTTP/2 transport errors. + // It's intended to increment a metric for monitoring, such + // as an expvar or Prometheus metric. + // The errType consists of only ASCII word characters. + CountError func(errType string) + // t1, if non-nil, is the standard library Transport using // this transport. Its settings are used (but not its // RoundTrip method, etc). @@ -6833,11 +7076,12 @@ func (t *http2Transport) initConnPool() { // ClientConn is the state of a single HTTP/2 client connection to an // HTTP/2 server. type http2ClientConn struct { - t *http2Transport - tconn net.Conn // usually *tls.Conn, except specialized impls - tlsState *tls.ConnectionState // nil only for specialized impls - reused uint32 // whether conn is being reused; atomic - singleUse bool // whether being used for a single http.Request + t *http2Transport + tconn net.Conn // usually *tls.Conn, except specialized impls + tlsState *tls.ConnectionState // nil only for specialized impls + reused uint32 // whether conn is being reused; atomic + singleUse bool // whether being used for a single http.Request + getConnCalled bool // used by clientConnPool // readLoop goroutine fields: readerDone chan struct{} // closed on error @@ -6850,87 +7094,94 @@ type http2ClientConn struct { cond *sync.Cond // hold mu; broadcast on flow/closed changes flow http2flow // our conn-level flow control quota (cs.flow is per stream) inflow http2flow // peer's conn-level flow control + doNotReuse bool // whether conn is marked to not be reused for any future requests closing bool closed bool + seenSettings bool // true if we've seen a settings frame, false otherwise wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back goAway *http2GoAwayFrame // if non-nil, the GoAwayFrame we received goAwayDebug string // goAway frame's debug data, retained as a string streams map[uint32]*http2clientStream // client-initiated + streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip nextStreamID uint32 pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams pings map[[8]byte]chan struct{} // in flight ping data to notification channel - bw *bufio.Writer br *bufio.Reader - fr *http2Framer lastActive time.Time lastIdle time.Time // time last idle - // Settings from peer: (also guarded by mu) + // Settings from peer: (also guarded by wmu) maxFrameSize uint32 maxConcurrentStreams uint32 peerMaxHeaderListSize uint64 initialWindowSize uint32 - hbuf bytes.Buffer // HPACK encoder writes into this - henc *hpack.Encoder - freeBuf [][]byte + // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. + // Write to reqHeaderMu to lock it, read from it to unlock. + // Lock reqmu BEFORE mu or wmu. + reqHeaderMu chan struct{} - wmu sync.Mutex // held while writing; acquire AFTER mu if holding both - werr error // first write error that has occurred + // wmu is held while writing. + // Acquire BEFORE mu when holding both, to avoid blocking mu on network writes. + // Only acquire both at the same time when changing peer settings. + wmu sync.Mutex + bw *bufio.Writer + fr *http2Framer + werr error // first write error that has occurred + hbuf bytes.Buffer // HPACK encoder writes into this + henc *hpack.Encoder } // clientStream is the state for a single HTTP/2 stream. One of these // is created for each Transport.RoundTrip call. type http2clientStream struct { - cc *http2ClientConn - req *Request + cc *http2ClientConn + + // Fields of Request that we may access even after the response body is closed. + ctx context.Context + reqCancel <-chan struct{} + trace *httptrace.ClientTrace // or nil ID uint32 - resc chan http2resAndError bufPipe http2pipe // buffered pipe with the flow-controlled response payload - startedWrite bool // started request body write; guarded by cc.mu requestedGzip bool - on100 func() // optional code to run if get a 100 continue response + isHead bool + + abortOnce sync.Once + abort chan struct{} // closed to signal stream should end immediately + abortErr error // set if abort is closed + + peerClosed chan struct{} // closed when the peer sends an END_STREAM flag + donec chan struct{} // closed after the stream is in the closed state + on100 chan struct{} // buffered; written to if a 100 is received + + respHeaderRecv chan struct{} // closed when headers are received + res *Response // set if respHeaderRecv is closed flow http2flow // guarded by cc.mu inflow http2flow // guarded by cc.mu bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read readErr error // sticky read error; owned by transportResponseBody.Read - stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu - didReset bool // whether we sent a RST_STREAM to the server; guarded by cc.mu - peerReset chan struct{} // closed on peer reset - resetErr error // populated before peerReset is closed + reqBody io.ReadCloser + reqBodyContentLength int64 // -1 means unknown + reqBodyClosed bool // body has been closed; guarded by cc.mu - done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu + // owned by writeRequest: + sentEndStream bool // sent an END_STREAM flag to the peer + sentHeaders bool // owned by clientConnReadLoop: firstByte bool // got the first response byte pastHeaders bool // got first MetaHeadersFrame (actual headers) pastTrailers bool // got optional second MetaHeadersFrame (trailers) num1xx uint8 // number of 1xx responses seen + readClosed bool // peer sent an END_STREAM flag + readAborted bool // read loop reset the stream trailer Header // accumulated trailers resTrailer *Header // client's Response.Trailer } -// awaitRequestCancel waits for the user to cancel a request or for the done -// channel to be signaled. A non-nil error is returned only if the request was -// canceled. -func http2awaitRequestCancel(req *Request, done <-chan struct{}) error { - ctx := req.Context() - if req.Cancel == nil && ctx.Done() == nil { - return nil - } - select { - case <-req.Cancel: - return http2errRequestCanceled - case <-ctx.Done(): - return ctx.Err() - case <-done: - return nil - } -} - var http2got1xxFuncForTests func(int, textproto.MIMEHeader) error // get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func, @@ -6942,73 +7193,65 @@ func (cs *http2clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) e return http2traceGot1xxResponseFunc(cs.trace) } -// awaitRequestCancel waits for the user to cancel a 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). If the request is canceled, then cs will be canceled and closed. -func (cs *http2clientStream) awaitRequestCancel(req *Request) { - if err := http2awaitRequestCancel(req, cs.done); err != nil { - cs.cancelStream() - cs.bufPipe.CloseWithError(err) - } +func (cs *http2clientStream) abortStream(err error) { + cs.cc.mu.Lock() + defer cs.cc.mu.Unlock() + cs.abortStreamLocked(err) } -func (cs *http2clientStream) cancelStream() { - cc := cs.cc - cc.mu.Lock() - didReset := cs.didReset - cs.didReset = true - cc.mu.Unlock() - - if !didReset { - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) - cc.forgetStreamID(cs.ID) +func (cs *http2clientStream) abortStreamLocked(err error) { + cs.abortOnce.Do(func() { + cs.abortErr = err + close(cs.abort) + }) + if cs.reqBody != nil && !cs.reqBodyClosed { + cs.reqBody.Close() + cs.reqBodyClosed = true } -} - -// 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 + // TODO(dneil): Clean up tests where cs.cc.cond is nil. + if cs.cc.cond != nil { + // Wake up writeRequestBody if it is waiting on flow control. + cs.cc.cond.Broadcast() } } -func (cs *http2clientStream) getStartedWrite() bool { +func (cs *http2clientStream) abortRequestBodyWrite() { cc := cs.cc cc.mu.Lock() defer cc.mu.Unlock() - return cs.startedWrite -} - -func (cs *http2clientStream) abortRequestBodyWrite(err error) { - if err == nil { - panic("nil error") + if cs.reqBody != nil && !cs.reqBodyClosed { + cs.reqBody.Close() + cs.reqBodyClosed = true + cc.cond.Broadcast() } - cc := cs.cc - cc.mu.Lock() - cs.stopReqBody = err - cc.cond.Broadcast() - cc.mu.Unlock() } type http2stickyErrWriter struct { - w io.Writer - err *error + conn net.Conn + timeout time.Duration + err *error } func (sew http2stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } - n, err = sew.w.Write(p) - *sew.err = err - return + for { + if sew.timeout != 0 { + sew.conn.SetWriteDeadline(time.Now().Add(sew.timeout)) + } + nn, err := sew.conn.Write(p[n:]) + n += nn + if n < len(p) && nn > 0 && errors.Is(err, os.ErrDeadlineExceeded) { + // Keep extending the deadline so long as we're making progress. + continue + } + if sew.timeout != 0 { + sew.conn.SetWriteDeadline(time.Time{}) + } + *sew.err = err + return n, err + } } // noCachedConnError is the concrete type of ErrNoCachedConn, which @@ -7082,20 +7325,22 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res } reused := !atomic.CompareAndSwapUint32(&cc.reused, 0, 1) http2traceGotConn(req, cc, reused) - res, gotErrAfterReqBodyWrite, err := cc.roundTrip(req) + res, err := cc.RoundTrip(req) if err != nil && retry <= 6 { - if req, err = http2shouldRetryRequest(req, err, gotErrAfterReqBodyWrite); err == nil { + if req, err = http2shouldRetryRequest(req, err); err == nil { // After the first retry, do exponential backoff with 10% jitter. if retry == 0 { + t.vlogf("RoundTrip retrying after failure: %v", err) continue } backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) select { case <-time.After(time.Second * time.Duration(backoff)): + t.vlogf("RoundTrip retrying after failure: %v", err) continue case <-req.Context().Done(): - return nil, req.Context().Err() + err = req.Context().Err() } } } @@ -7126,7 +7371,7 @@ var ( // response headers. It is always called with a non-nil error. // It returns either a request to retry (either the same request, or a // modified clone), or an error if the request can't be replayed. -func http2shouldRetryRequest(req *Request, err error, afterBodyWrite bool) (*Request, error) { +func http2shouldRetryRequest(req *Request, err error) (*Request, error) { if !http2canRetryError(err) { return nil, err } @@ -7139,7 +7384,6 @@ func http2shouldRetryRequest(req *Request, err error, afterBodyWrite bool) (*Req // If the request body can be reset back to its original // state via the optional req.GetBody, do that. if req.GetBody != nil { - // TODO: consider a req.Body.Close here? or audit that all caller paths do? body, err := req.GetBody() if err != nil { return nil, err @@ -7151,10 +7395,8 @@ func http2shouldRetryRequest(req *Request, err error, afterBodyWrite bool) (*Req // The Request.Body can't reset back to the beginning, but we // don't seem to have started to read from it yet, so reuse - // the request directly. The "afterBodyWrite" means the - // bodyWrite process has started, which becomes true before - // the first Read. - if !afterBodyWrite { + // the request directly. + if err == http2errClientConnUnusable { return req, nil } @@ -7166,6 +7408,10 @@ func http2canRetryError(err error) bool { return true } if se, ok := err.(http2StreamError); ok { + if se.Code == http2ErrCodeProtocol && se.Cause == http2errFromPeer { + // See golang/go#47635, golang/go#42777 + return true + } return se.Code == http2ErrCodeRefusedStream } return false @@ -7176,7 +7422,7 @@ func (t *http2Transport) dialClientConn(ctx context.Context, addr string, single if err != nil { return nil, err } - tconn, err := t.dialTLS(ctx)("tcp", addr, t.newTLSConfig(host)) + tconn, err := t.dialTLS(ctx, "tcp", addr, t.newTLSConfig(host)) if err != nil { return nil, err } @@ -7197,24 +7443,25 @@ func (t *http2Transport) newTLSConfig(host string) *tls.Config { return cfg } -func (t *http2Transport) dialTLS(ctx context.Context) func(string, string, *tls.Config) (net.Conn, error) { - if t.DialTLS != nil { - return t.DialTLS +func (t *http2Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) { + if t.DialTLSContext != nil { + return t.DialTLSContext(ctx, network, addr, tlsCfg) + } else if t.DialTLS != nil { + return t.DialTLS(network, addr, tlsCfg) } - return func(network, addr string, cfg *tls.Config) (net.Conn, error) { - tlsCn, err := t.dialTLSWithContext(ctx, network, addr, cfg) - if err != nil { - return nil, err - } - state := tlsCn.ConnectionState() - if p := state.NegotiatedProtocol; p != http2NextProtoTLS { - return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, http2NextProtoTLS) - } - if !state.NegotiatedProtocolIsMutual { - return nil, errors.New("http2: could not negotiate protocol mutually") - } - return tlsCn, nil + + tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg) + if err != nil { + return nil, err + } + state := tlsCn.ConnectionState() + if p := state.NegotiatedProtocol; p != http2NextProtoTLS { + return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, http2NextProtoTLS) } + if !state.NegotiatedProtocolIsMutual { + return nil, errors.New("http2: could not negotiate protocol mutually") + } + return tlsCn, nil } // disableKeepAlives reports whether connections should be closed as @@ -7240,14 +7487,15 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client tconn: c, readerDone: make(chan struct{}), nextStreamID: 1, - maxFrameSize: 16 << 10, // spec default - initialWindowSize: 65535, // spec default - maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. - peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. + maxFrameSize: 16 << 10, // spec default + initialWindowSize: 65535, // spec default + maxConcurrentStreams: http2initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. + peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. streams: make(map[uint32]*http2clientStream), singleUse: singleUse, wantSettingsAck: true, pings: make(map[[8]byte]chan struct{}), + reqHeaderMu: make(chan struct{}, 1), } if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d @@ -7262,9 +7510,16 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. - cc.bw = bufio.NewWriter(http2stickyErrWriter{c, &cc.werr}) + cc.bw = bufio.NewWriter(http2stickyErrWriter{ + conn: c, + timeout: t.WriteByteTimeout, + err: &cc.werr, + }) cc.br = bufio.NewReader(c) cc.fr = http2NewFramer(cc.bw, cc.br) + if t.CountError != nil { + cc.fr.countError = t.CountError + } cc.fr.ReadMetaHeaders = hpack.NewDecoder(http2initialHeaderTableSize, nil) cc.fr.MaxHeaderListSize = t.maxHeaderListSize() @@ -7309,14 +7564,23 @@ func (cc *http2ClientConn) healthCheck() { // trigger the healthCheck again if there is no frame received. ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) defer cancel() + cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) if err != nil { + cc.vlogf("http2: Transport health check failure: %v", err) cc.closeForLostPing() - cc.t.connPool().MarkDead(cc) - return + } else { + cc.vlogf("http2: Transport health check success") } } +// SetDoNotReuse marks cc as not reusable for future HTTP requests. +func (cc *http2ClientConn) SetDoNotReuse() { + cc.mu.Lock() + defer cc.mu.Unlock() + cc.doNotReuse = true +} + func (cc *http2ClientConn) setGoAway(f *http2GoAwayFrame) { cc.mu.Lock() defer cc.mu.Unlock() @@ -7334,27 +7598,94 @@ func (cc *http2ClientConn) setGoAway(f *http2GoAwayFrame) { last := f.LastStreamID for streamID, cs := range cc.streams { if streamID > last { - select { - case cs.resc <- http2resAndError{err: http2errClientConnGotGoAway}: - default: - } + cs.abortStreamLocked(http2errClientConnGotGoAway) } } } // CanTakeNewRequest reports whether the connection can take a new request, // meaning it has not been closed or received or sent a GOAWAY. +// +// If the caller is going to immediately make a new request on this +// connection, use ReserveNewRequest instead. func (cc *http2ClientConn) CanTakeNewRequest() bool { cc.mu.Lock() defer cc.mu.Unlock() return cc.canTakeNewRequestLocked() } +// ReserveNewRequest is like CanTakeNewRequest but also reserves a +// concurrent stream in cc. The reservation is decremented on the +// next call to RoundTrip. +func (cc *http2ClientConn) ReserveNewRequest() bool { + cc.mu.Lock() + defer cc.mu.Unlock() + if st := cc.idleStateLocked(); !st.canTakeNewRequest { + return false + } + cc.streamsReserved++ + return true +} + +// ClientConnState describes the state of a ClientConn. +type http2ClientConnState struct { + // Closed is whether the connection is closed. + Closed bool + + // Closing is whether the connection is in the process of + // closing. It may be closing due to shutdown, being a + // single-use connection, being marked as DoNotReuse, or + // having received a GOAWAY frame. + Closing bool + + // StreamsActive is how many streams are active. + StreamsActive int + + // StreamsReserved is how many streams have been reserved via + // ClientConn.ReserveNewRequest. + StreamsReserved int + + // StreamsPending is how many requests have been sent in excess + // of the peer's advertised MaxConcurrentStreams setting and + // are waiting for other streams to complete. + StreamsPending int + + // MaxConcurrentStreams is how many concurrent streams the + // peer advertised as acceptable. Zero means no SETTINGS + // frame has been received yet. + MaxConcurrentStreams uint32 + + // LastIdle, if non-zero, is when the connection last + // transitioned to idle state. + LastIdle time.Time +} + +// State returns a snapshot of cc's state. +func (cc *http2ClientConn) State() http2ClientConnState { + cc.wmu.Lock() + maxConcurrent := cc.maxConcurrentStreams + if !cc.seenSettings { + maxConcurrent = 0 + } + cc.wmu.Unlock() + + cc.mu.Lock() + defer cc.mu.Unlock() + return http2ClientConnState{ + Closed: cc.closed, + Closing: cc.closing || cc.singleUse || cc.doNotReuse || cc.goAway != nil, + StreamsActive: len(cc.streams), + StreamsReserved: cc.streamsReserved, + StreamsPending: cc.pendingRequests, + LastIdle: cc.lastIdle, + MaxConcurrentStreams: maxConcurrent, + } +} + // clientConnIdleState describes the suitability of a client // connection to initiate a new RoundTrip request. type http2clientConnIdleState struct { canTakeNewRequest bool - freshConn bool // whether it's unused by any previous request } func (cc *http2ClientConn) idleState() http2clientConnIdleState { @@ -7375,13 +7706,13 @@ func (cc *http2ClientConn) idleStateLocked() (st http2clientConnIdleState) { // writing it. maxConcurrentOkay = true } else { - maxConcurrentOkay = int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) + maxConcurrentOkay = int64(len(cc.streams)+cc.streamsReserved+1) <= int64(cc.maxConcurrentStreams) } st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && + !cc.doNotReuse && int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && !cc.tooIdleLocked() - st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest return } @@ -7410,9 +7741,27 @@ func (cc *http2ClientConn) onIdleTimeout() { cc.closeIfIdle() } +func (cc *http2ClientConn) closeConn() error { + t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn) + defer t.Stop() + return cc.tconn.Close() +} + +// A tls.Conn.Close can hang for a long time if the peer is unresponsive. +// Try to shut it down more aggressively. +func (cc *http2ClientConn) forceCloseConn() { + tc, ok := cc.tconn.(*tls.Conn) + if !ok { + return + } + if nc := http2tlsUnderlyingConn(tc); nc != nil { + nc.Close() + } +} + func (cc *http2ClientConn) closeIfIdle() { cc.mu.Lock() - if len(cc.streams) > 0 { + if len(cc.streams) > 0 || cc.streamsReserved > 0 { cc.mu.Unlock() return } @@ -7424,18 +7773,24 @@ func (cc *http2ClientConn) closeIfIdle() { if http2VerboseLogs { cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2) } - cc.tconn.Close() + cc.closeConn() +} + +func (cc *http2ClientConn) isDoNotReuseAndIdle() bool { + cc.mu.Lock() + defer cc.mu.Unlock() + return cc.doNotReuse && len(cc.streams) == 0 } var http2shutdownEnterWaitStateHook = func() {} -// Shutdown gracefully close the client connection, waiting for running streams to complete. +// Shutdown gracefully closes the client connection, waiting for running streams to complete. func (cc *http2ClientConn) Shutdown(ctx context.Context) error { if err := cc.sendGoAway(); err != nil { return err } // Wait for all in-flight streams to complete or connection to close - done := make(chan error, 1) + done := make(chan struct{}) cancelled := false // guarded by cc.mu go func() { cc.mu.Lock() @@ -7443,7 +7798,7 @@ func (cc *http2ClientConn) Shutdown(ctx context.Context) error { for { if len(cc.streams) == 0 || cc.closed { cc.closed = true - done <- cc.tconn.Close() + close(done) break } if cancelled { @@ -7454,8 +7809,8 @@ func (cc *http2ClientConn) Shutdown(ctx context.Context) error { }() http2shutdownEnterWaitStateHook() select { - case err := <-done: - return err + case <-done: + return cc.closeConn() case <-ctx.Done(): cc.mu.Lock() // Free the goroutine above @@ -7468,15 +7823,18 @@ func (cc *http2ClientConn) Shutdown(ctx context.Context) error { func (cc *http2ClientConn) sendGoAway() error { cc.mu.Lock() - defer cc.mu.Unlock() - cc.wmu.Lock() - defer cc.wmu.Unlock() - if cc.closing { + closing := cc.closing + cc.closing = true + maxStreamID := cc.nextStreamID + cc.mu.Unlock() + if closing { // GOAWAY sent already return nil } + + cc.wmu.Lock() + defer cc.wmu.Unlock() // Send a graceful shutdown frame to server - maxStreamID := cc.nextStreamID if err := cc.fr.WriteGoAway(maxStreamID, http2ErrCodeNo, nil); err != nil { return err } @@ -7484,7 +7842,6 @@ func (cc *http2ClientConn) sendGoAway() error { return err } // Prevent new requests - cc.closing = true return nil } @@ -7492,18 +7849,13 @@ func (cc *http2ClientConn) sendGoAway() error { // err is sent to streams. func (cc *http2ClientConn) closeForError(err error) error { cc.mu.Lock() - defer cc.cond.Broadcast() - defer cc.mu.Unlock() - for id, cs := range cc.streams { - select { - case cs.resc <- http2resAndError{err: err}: - default: - } - cs.bufPipe.CloseWithError(err) - delete(cc.streams, id) - } cc.closed = true - return cc.tconn.Close() + for _, cs := range cc.streams { + cs.abortStreamLocked(err) + } + cc.cond.Broadcast() + cc.mu.Unlock() + return cc.closeConn() } // Close closes the client connection immediately. @@ -7517,47 +7869,10 @@ func (cc *http2ClientConn) Close() error { // closes the client connection immediately. In-flight requests are interrupted. func (cc *http2ClientConn) closeForLostPing() error { err := errors.New("http2: client connection lost") - return cc.closeForError(err) -} - -const http2maxAllocFrameSize = 512 << 10 - -// frameBuffer returns a scratch buffer suitable for writing DATA frames. -// They're capped at the min of the peer's max frame size or 512KB -// (kinda arbitrarily), but definitely capped so we don't allocate 4GB -// bufers. -func (cc *http2ClientConn) frameScratchBuffer() []byte { - cc.mu.Lock() - size := cc.maxFrameSize - if size > http2maxAllocFrameSize { - size = http2maxAllocFrameSize - } - for i, buf := range cc.freeBuf { - if len(buf) >= int(size) { - cc.freeBuf[i] = nil - cc.mu.Unlock() - return buf[:size] - } + if f := cc.t.CountError; f != nil { + f("conn_close_lost_ping") } - cc.mu.Unlock() - return make([]byte, size) -} - -func (cc *http2ClientConn) putFrameScratchBuffer(buf []byte) { - cc.mu.Lock() - defer cc.mu.Unlock() - const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate. - if len(cc.freeBuf) < maxBufs { - cc.freeBuf = append(cc.freeBuf, buf) - return - } - for i, old := range cc.freeBuf { - if old == nil { - cc.freeBuf[i] = buf - return - } - } - // forget about it. + return cc.closeForError(err) } // errRequestCanceled is a copy of net/http's errRequestCanceled because it's not @@ -7621,41 +7936,158 @@ func http2actualContentLength(req *Request) int64 { return -1 } +func (cc *http2ClientConn) decrStreamReservations() { + cc.mu.Lock() + defer cc.mu.Unlock() + cc.decrStreamReservationsLocked() +} + +func (cc *http2ClientConn) decrStreamReservationsLocked() { + if cc.streamsReserved > 0 { + cc.streamsReserved-- + } +} + func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { - resp, _, err := cc.roundTrip(req) - return resp, err + ctx := req.Context() + cs := &http2clientStream{ + cc: cc, + ctx: ctx, + reqCancel: req.Cancel, + isHead: req.Method == "HEAD", + reqBody: req.Body, + reqBodyContentLength: http2actualContentLength(req), + trace: httptrace.ContextClientTrace(ctx), + peerClosed: make(chan struct{}), + abort: make(chan struct{}), + respHeaderRecv: make(chan struct{}), + donec: make(chan struct{}), + } + go cs.doRequest(req) + + waitDone := func() error { + select { + case <-cs.donec: + return nil + case <-ctx.Done(): + return ctx.Err() + case <-cs.reqCancel: + return http2errRequestCanceled + } + } + + handleResponseHeaders := func() (*Response, error) { + res := cs.res + if res.StatusCode > 299 { + // On error or status code 3xx, 4xx, 5xx, etc abort any + // ongoing write, assuming that the server doesn't care + // about our request body. If the server replied with 1xx or + // 2xx, however, then assume the server DOES potentially + // want our body (e.g. full-duplex streaming: + // golang.org/issue/13444). If it turns out the server + // doesn't, they'll RST_STREAM us soon enough. This is a + // heuristic to avoid adding knobs to Transport. Hopefully + // we can keep it. + cs.abortRequestBodyWrite() + } + res.Request = req + res.TLS = cc.tlsState + if res.Body == http2noBody && http2actualContentLength(req) == 0 { + // If there isn't a request or response body still being + // written, then wait for the stream to be closed before + // RoundTrip returns. + if err := waitDone(); err != nil { + return nil, err + } + } + return res, nil + } + + for { + select { + case <-cs.respHeaderRecv: + return handleResponseHeaders() + case <-cs.abort: + select { + case <-cs.respHeaderRecv: + // If both cs.respHeaderRecv and cs.abort are signaling, + // pick respHeaderRecv. The server probably wrote the + // response and immediately reset the stream. + // golang.org/issue/49645 + return handleResponseHeaders() + default: + waitDone() + return nil, cs.abortErr + } + case <-ctx.Done(): + err := ctx.Err() + cs.abortStream(err) + return nil, err + case <-cs.reqCancel: + cs.abortStream(http2errRequestCanceled) + return nil, http2errRequestCanceled + } + } } -func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterReqBodyWrite bool, err error) { +// doRequest runs for the duration of the request lifetime. +// +// It sends the request and performs post-request cleanup (closing Request.Body, etc.). +func (cs *http2clientStream) doRequest(req *Request) { + err := cs.writeRequest(req) + cs.cleanupWriteRequest(err) +} + +// writeRequest sends a request. +// +// It returns nil after the request is written, the response read, +// and the request stream is half-closed by the peer. +// +// It returns non-nil if the request ends otherwise. +// If the returned error is StreamError, the error Code may be used in resetting the stream. +func (cs *http2clientStream) writeRequest(req *Request) (err error) { + cc := cs.cc + ctx := cs.ctx + if err := http2checkConnHeaders(req); err != nil { - return nil, false, err - } - if cc.idleTimer != nil { - cc.idleTimer.Stop() + return err } - trailers, err := http2commaSeparatedTrailers(req) - if err != nil { - return nil, false, err + // Acquire the new-request lock by writing to reqHeaderMu. + // This lock guards the critical section covering allocating a new stream ID + // (requires mu) and creating the stream (requires wmu). + if cc.reqHeaderMu == nil { + panic("RoundTrip on uninitialized ClientConn") // for tests + } + select { + case cc.reqHeaderMu <- struct{}{}: + case <-cs.reqCancel: + return http2errRequestCanceled + case <-ctx.Done(): + return ctx.Err() } - hasTrailers := trailers != "" cc.mu.Lock() - if err := cc.awaitOpenSlotForRequest(req); err != nil { + if cc.idleTimer != nil { + cc.idleTimer.Stop() + } + cc.decrStreamReservationsLocked() + if err := cc.awaitOpenSlotForStreamLocked(cs); err != nil { cc.mu.Unlock() - return nil, false, err + <-cc.reqHeaderMu + return err } - - body := req.Body - contentLen := http2actualContentLength(req) - hasBody := contentLen != 0 + cc.addStreamLocked(cs) // assigns stream ID + if http2isConnectionCloseRequest(req) { + cc.doNotReuse = true + } + cc.mu.Unlock() // 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.isHead { // Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: https://zlib.net/zlib_faq.html#faq39 @@ -7668,195 +8100,224 @@ func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterRe // We don't request gzip if the request is for a range, since // auto-decoding a portion of a gzipped document will just fail // anyway. See https://golang.org/issue/8923 - requestedGzip = true + cs.requestedGzip = true } - // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is - // sent by writeRequestBody below, along with any Trailers, - // again in form HEADERS{1}, CONTINUATION{0,}) - hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen) - if err != nil { - cc.mu.Unlock() - return nil, false, err + continueTimeout := cc.t.expectContinueTimeout() + if continueTimeout != 0 { + if !httpguts.HeaderValuesContainsToken(req.Header["Expect"], "100-continue") { + continueTimeout = 0 + } else { + cs.on100 = make(chan struct{}, 1) + } } - cs := cc.newStream() - cs.req = req - cs.trace = httptrace.ContextClientTrace(req.Context()) - cs.requestedGzip = requestedGzip - bodyWriter := cc.t.getBodyWriterState(cs, body) - cs.on100 = bodyWriter.on100 + // Past this point (where we send request headers), it is possible for + // RoundTrip to return successfully. Since the RoundTrip contract permits + // the caller to "mutate or reuse" the Request after closing the Response's Body, + // we must take care when referencing the Request from here on. + err = cs.encodeAndWriteHeaders(req) + <-cc.reqHeaderMu + if err != nil { + return err + } - defer func() { - cc.wmu.Lock() - werr := cc.werr - cc.wmu.Unlock() - if werr != nil { - cc.Close() + hasBody := cs.reqBodyContentLength != 0 + if !hasBody { + cs.sentEndStream = true + } else { + if continueTimeout != 0 { + http2traceWait100Continue(cs.trace) + timer := time.NewTimer(continueTimeout) + select { + case <-timer.C: + err = nil + case <-cs.on100: + err = nil + case <-cs.abort: + err = cs.abortErr + case <-ctx.Done(): + err = ctx.Err() + case <-cs.reqCancel: + err = http2errRequestCanceled + } + timer.Stop() + if err != nil { + http2traceWroteRequest(cs.trace, err) + return err + } } - }() - cc.wmu.Lock() - endStream := !hasBody && !hasTrailers - werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) - cc.wmu.Unlock() - http2traceWroteHeaders(cs.trace) - cc.mu.Unlock() - - if werr != nil { - if hasBody { - req.Body.Close() // per RoundTripper contract - bodyWriter.cancel() + if err = cs.writeRequestBody(req); err != nil { + if err != http2errStopReqBodyWrite { + http2traceWroteRequest(cs.trace, err) + return err + } + } else { + cs.sentEndStream = true } - cc.forgetStreamID(cs.ID) - // Don't bother sending a RST_STREAM (our write already failed; - // no need to keep writing) - http2traceWroteRequest(cs.trace, werr) - return nil, false, werr } + http2traceWroteRequest(cs.trace, err) + var respHeaderTimer <-chan time.Time - if hasBody { - bodyWriter.scheduleBodyWrite() - } else { - http2traceWroteRequest(cs.trace, nil) - if d := cc.responseHeaderTimeout(); d != 0 { - timer := time.NewTimer(d) - defer timer.Stop() - respHeaderTimer = timer.C + var respHeaderRecv chan struct{} + if d := cc.responseHeaderTimeout(); d != 0 { + timer := time.NewTimer(d) + defer timer.Stop() + respHeaderTimer = timer.C + respHeaderRecv = cs.respHeaderRecv + } + // Wait until the peer half-closes its end of the stream, + // or until the request is aborted (via context, error, or otherwise), + // whichever comes first. + for { + select { + case <-cs.peerClosed: + return nil + case <-respHeaderTimer: + return http2errTimeout + case <-respHeaderRecv: + respHeaderRecv = nil + respHeaderTimer = nil // keep waiting for END_STREAM + case <-cs.abort: + return cs.abortErr + case <-ctx.Done(): + return ctx.Err() + case <-cs.reqCancel: + return http2errRequestCanceled } } +} - readLoopResCh := cs.resc - bodyWritten := false - ctx := req.Context() +func (cs *http2clientStream) encodeAndWriteHeaders(req *Request) error { + cc := cs.cc + ctx := cs.ctx - handleReadLoopResponse := func(re http2resAndError) (*Response, bool, error) { - res := re.res - if re.err != nil || res.StatusCode > 299 { - // On error or status code 3xx, 4xx, 5xx, etc abort any - // ongoing write, assuming that the server doesn't care - // about our request body. If the server replied with 1xx or - // 2xx, however, then assume the server DOES potentially - // want our body (e.g. full-duplex streaming: - // golang.org/issue/13444). If it turns out the server - // doesn't, they'll RST_STREAM us soon enough. This is a - // heuristic to avoid adding knobs to Transport. Hopefully - // we can keep it. - bodyWriter.cancel() - cs.abortRequestBodyWrite(http2errStopReqBodyWrite) - if hasBody && !bodyWritten { - <-bodyWriter.resc - } - } - if re.err != nil { - cc.forgetStreamID(cs.ID) - return nil, cs.getStartedWrite(), re.err - } - res.Request = req - res.TLS = cc.tlsState - return res, false, nil + cc.wmu.Lock() + defer cc.wmu.Unlock() + + // If the request was canceled while waiting for cc.mu, just quit. + select { + case <-cs.abort: + return cs.abortErr + case <-ctx.Done(): + return ctx.Err() + case <-cs.reqCancel: + return http2errRequestCanceled + default: } - for { + // Encode headers. + // + // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is + // sent by writeRequestBody below, along with any Trailers, + // again in form HEADERS{1}, CONTINUATION{0,}) + trailers, err := http2commaSeparatedTrailers(req) + if err != nil { + return err + } + hasTrailers := trailers != "" + contentLen := http2actualContentLength(req) + hasBody := contentLen != 0 + hdrs, err := cc.encodeHeaders(req, cs.requestedGzip, trailers, contentLen) + if err != nil { + return err + } + + // Write the request. + endStream := !hasBody && !hasTrailers + cs.sentHeaders = true + err = cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) + http2traceWroteHeaders(cs.trace) + return err +} + +// cleanupWriteRequest performs post-request tasks. +// +// If err (the result of writeRequest) is non-nil and the stream is not closed, +// cleanupWriteRequest will send a reset to the peer. +func (cs *http2clientStream) cleanupWriteRequest(err error) { + cc := cs.cc + + if cs.ID == 0 { + // We were canceled before creating the stream, so return our reservation. + cc.decrStreamReservations() + } + + // TODO: write h12Compare test showing whether + // Request.Body is closed by the Transport, + // and in multiple cases: server replies <=299 and >299 + // while still writing request body + cc.mu.Lock() + bodyClosed := cs.reqBodyClosed + cs.reqBodyClosed = true + cc.mu.Unlock() + if !bodyClosed && cs.reqBody != nil { + cs.reqBody.Close() + } + + if err != nil && cs.sentEndStream { + // If the connection is closed immediately after the response is read, + // we may be aborted before finishing up here. If the stream was closed + // cleanly on both sides, there is no error. select { - case re := <-readLoopResCh: - return handleReadLoopResponse(re) - case <-respHeaderTimer: - if !hasBody || bodyWritten { - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) - } else { - bodyWriter.cancel() - cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) - <-bodyWriter.resc - } - cc.forgetStreamID(cs.ID) - return nil, cs.getStartedWrite(), http2errTimeout - case <-ctx.Done(): - if !hasBody || bodyWritten { - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) - } else { - bodyWriter.cancel() - cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) - <-bodyWriter.resc - } - cc.forgetStreamID(cs.ID) - return nil, cs.getStartedWrite(), ctx.Err() - case <-req.Cancel: - if !hasBody || bodyWritten { - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) + case <-cs.peerClosed: + err = nil + default: + } + } + if err != nil { + cs.abortStream(err) // possibly redundant, but harmless + if cs.sentHeaders { + if se, ok := err.(http2StreamError); ok { + if se.Cause != http2errFromPeer { + cc.writeStreamReset(cs.ID, se.Code, err) + } } else { - bodyWriter.cancel() - cs.abortRequestBodyWrite(http2errStopReqBodyWriteAndCancel) - <-bodyWriter.resc - } - cc.forgetStreamID(cs.ID) - return nil, cs.getStartedWrite(), http2errRequestCanceled - case <-cs.peerReset: - // processResetStream already removed the - // stream from the streams map; no need for - // forgetStreamID. - return nil, cs.getStartedWrite(), cs.resetErr - case err := <-bodyWriter.resc: - bodyWritten = true - // Prefer the read loop's response, if available. Issue 16102. - select { - case re := <-readLoopResCh: - return handleReadLoopResponse(re) - default: - } - if err != nil { - cc.forgetStreamID(cs.ID) - return nil, cs.getStartedWrite(), err - } - if d := cc.responseHeaderTimeout(); d != 0 { - timer := time.NewTimer(d) - defer timer.Stop() - respHeaderTimer = timer.C + cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) } } + cs.bufPipe.CloseWithError(err) // no-op if already closed + } else { + if cs.sentHeaders && !cs.sentEndStream { + cc.writeStreamReset(cs.ID, http2ErrCodeNo, nil) + } + cs.bufPipe.CloseWithError(http2errRequestCanceled) + } + if cs.ID != 0 { + cc.forgetStreamID(cs.ID) } + + cc.wmu.Lock() + werr := cc.werr + cc.wmu.Unlock() + if werr != nil { + cc.Close() + } + + close(cs.donec) } -// awaitOpenSlotForRequest waits until len(streams) < maxConcurrentStreams. +// awaitOpenSlotForStream waits until len(streams) < maxConcurrentStreams. // Must hold cc.mu. -func (cc *http2ClientConn) awaitOpenSlotForRequest(req *Request) error { - var waitingForConn chan struct{} - var waitingForConnErr error // guarded by cc.mu +func (cc *http2ClientConn) awaitOpenSlotForStreamLocked(cs *http2clientStream) error { for { cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { - if waitingForConn != nil { - close(waitingForConn) - } return http2errClientConnUnusable } cc.lastIdle = time.Time{} - if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) { - if waitingForConn != nil { - close(waitingForConn) - } + if int64(len(cc.streams)) < int64(cc.maxConcurrentStreams) { return nil } - // Unfortunately, we cannot wait on a condition variable and channel at - // the same time, so instead, we spin up a goroutine to check if the - // request is canceled while we wait for a slot to open in the connection. - if waitingForConn == nil { - waitingForConn = make(chan struct{}) - go func() { - if err := http2awaitRequestCancel(req, waitingForConn); err != nil { - cc.mu.Lock() - waitingForConnErr = err - cc.cond.Broadcast() - cc.mu.Unlock() - } - }() - } cc.pendingRequests++ cc.cond.Wait() cc.pendingRequests-- - if waitingForConnErr != nil { - return waitingForConnErr + select { + case <-cs.abort: + return cs.abortErr + default: } } } @@ -7883,10 +8344,6 @@ func (cc *http2ClientConn) writeHeaders(streamID uint32, endStream bool, maxFram cc.fr.WriteContinuation(streamID, endHeaders, chunk) } } - // TODO(bradfitz): this Flush could potentially block (as - // could the WriteHeaders call(s) above), which means they - // wouldn't respond to Request.Cancel being readable. That's - // rare, but this should probably be in a goroutine. cc.bw.Flush() return cc.werr } @@ -7902,32 +8359,59 @@ var ( http2errReqBodyTooLong = errors.New("http2: request body larger than specified content length") ) -func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) { +// frameScratchBufferLen returns the length of a buffer to use for +// outgoing request bodies to read/write to/from. +// +// It returns max(1, min(peer's advertised max frame size, +// Request.ContentLength+1, 512KB)). +func (cs *http2clientStream) frameScratchBufferLen(maxFrameSize int) int { + const max = 512 << 10 + n := int64(maxFrameSize) + if n > max { + n = max + } + if cl := cs.reqBodyContentLength; cl != -1 && cl+1 < n { + // Add an extra byte past the declared content-length to + // give the caller's Request.Body io.Reader a chance to + // give us more bytes than they declared, so we can catch it + // early. + n = cl + 1 + } + if n < 1 { + return 1 + } + return int(n) // doesn't truncate; max is 512K +} + +var http2bufPool sync.Pool // of *[]byte + +func (cs *http2clientStream) writeRequestBody(req *Request) (err error) { cc := cs.cc + body := cs.reqBody sentEnd := false // whether we sent the final DATA frame w/ END_STREAM - buf := cc.frameScratchBuffer() - defer cc.putFrameScratchBuffer(buf) - defer func() { - http2traceWroteRequest(cs.trace, err) - // TODO: write h12Compare test showing whether - // Request.Body is closed by the Transport, - // and in multiple cases: server replies <=299 and >299 - // while still writing request body - cerr := bodyCloser.Close() - if err == nil { - err = cerr - } - }() - - req := cs.req hasTrailers := req.Trailer != nil - remainLen := http2actualContentLength(req) + remainLen := cs.reqBodyContentLength hasContentLen := remainLen != -1 + cc.mu.Lock() + maxFrameSize := int(cc.maxFrameSize) + cc.mu.Unlock() + + // Scratch buffer for reading into & writing from. + scratchLen := cs.frameScratchBufferLen(maxFrameSize) + var buf []byte + if bp, ok := http2bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen { + defer http2bufPool.Put(bp) + buf = *bp + } else { + buf = make([]byte, scratchLen) + defer http2bufPool.Put(&buf) + } + var sawEOF bool for !sawEOF { - n, err := body.Read(buf[:len(buf)-1]) + n, err := body.Read(buf[:len(buf)]) if hasContentLen { remainLen -= int64(n) if remainLen == 0 && err == nil { @@ -7938,35 +8422,36 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos // to send the END_STREAM bit early, double-check that we're actually // at EOF. Subsequent reads should return (0, EOF) at this point. // If either value is different, we return an error in one of two ways below. + var scratch [1]byte var n1 int - n1, err = body.Read(buf[n:]) + n1, err = body.Read(scratch[:]) remainLen -= int64(n1) } if remainLen < 0 { err = http2errReqBodyTooLong - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) return err } } - if err == io.EOF { - sawEOF = true - err = nil - } else if err != nil { - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, err) - return err + if err != nil { + cc.mu.Lock() + bodyClosed := cs.reqBodyClosed + cc.mu.Unlock() + switch { + case bodyClosed: + return http2errStopReqBodyWrite + case err == io.EOF: + sawEOF = true + err = nil + default: + return err + } } remain := buf[:n] for len(remain) > 0 && err == nil { var allowed int32 allowed, err = cs.awaitFlowControl(len(remain)) - switch { - case err == http2errStopReqBodyWrite: - return err - case err == http2errStopReqBodyWriteAndCancel: - cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil) - return err - case err != nil: + if err != nil { return err } cc.wmu.Lock() @@ -7997,24 +8482,26 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos return nil } - var trls []byte - if hasTrailers { - cc.mu.Lock() - trls, err = cc.encodeTrailers(req) - cc.mu.Unlock() - if err != nil { - cc.writeStreamReset(cs.ID, http2ErrCodeInternal, err) - cc.forgetStreamID(cs.ID) - return err - } - } - + // Since the RoundTrip contract permits the caller to "mutate or reuse" + // a request after the Response's Body is closed, verify that this hasn't + // happened before accessing the trailers. cc.mu.Lock() - maxFrameSize := int(cc.maxFrameSize) + trailer := req.Trailer + err = cs.abortErr cc.mu.Unlock() + if err != nil { + return err + } cc.wmu.Lock() defer cc.wmu.Unlock() + var trls []byte + if len(trailer) > 0 { + trls, err = cc.encodeTrailers(trailer) + if err != nil { + return err + } + } // Two ways to send END_STREAM: either with trailers, or // with an empty DATA frame. @@ -8035,17 +8522,24 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos // if the stream is dead. func (cs *http2clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) { cc := cs.cc + ctx := cs.ctx cc.mu.Lock() defer cc.mu.Unlock() for { if cc.closed { return 0, http2errClientConnClosed } - if cs.stopReqBody != nil { - return 0, cs.stopReqBody + if cs.reqBodyClosed { + return 0, http2errStopReqBodyWrite } - if err := cs.checkResetOrDone(); err != nil { - return 0, err + select { + case <-cs.abort: + return 0, cs.abortErr + case <-ctx.Done(): + return 0, ctx.Err() + case <-cs.reqCancel: + return 0, http2errRequestCanceled + default: } if a := cs.flow.available(); a > 0 { take := a @@ -8063,9 +8557,14 @@ func (cs *http2clientStream) awaitFlowControl(maxBytes int) (taken int32, err er } } -// requires cc.mu be held. +var http2errNilRequestURL = errors.New("http2: Request.URI is nil") + +// requires cc.wmu be held. func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { cc.hbuf.Reset() + if req.URL == nil { + return nil, http2errNilRequestURL + } host := req.Host if host == "" { @@ -8101,7 +8600,8 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { - return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) + // Don't include the value in the error, because it may be sensitive. + return nil, fmt.Errorf("invalid HTTP header value for header %q", k) } } } @@ -8251,12 +8751,12 @@ func http2shouldSendReqContentLength(method string, contentLength int64) bool { } } -// requires cc.mu be held. -func (cc *http2ClientConn) encodeTrailers(req *Request) ([]byte, error) { +// requires cc.wmu be held. +func (cc *http2ClientConn) encodeTrailers(trailer Header) ([]byte, error) { cc.hbuf.Reset() hlSize := uint64(0) - for k, vv := range req.Trailer { + for k, vv := range trailer { for _, v := range vv { hf := hpack.HeaderField{Name: k, Value: v} hlSize += uint64(hf.Size()) @@ -8266,7 +8766,7 @@ func (cc *http2ClientConn) encodeTrailers(req *Request) ([]byte, error) { return nil, http2errRequestHeaderListSize } - for k, vv := range req.Trailer { + for k, vv := range trailer { lowKey, ascii := http2asciiToLower(k) if !ascii { // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header @@ -8296,51 +8796,51 @@ type http2resAndError struct { } // requires cc.mu be held. -func (cc *http2ClientConn) newStream() *http2clientStream { - cs := &http2clientStream{ - cc: cc, - ID: cc.nextStreamID, - resc: make(chan http2resAndError, 1), - peerReset: make(chan struct{}), - done: make(chan struct{}), - } +func (cc *http2ClientConn) addStreamLocked(cs *http2clientStream) { cs.flow.add(int32(cc.initialWindowSize)) cs.flow.setConnFlow(&cc.flow) cs.inflow.add(http2transportDefaultStreamFlow) cs.inflow.setConnFlow(&cc.inflow) + cs.ID = cc.nextStreamID cc.nextStreamID += 2 cc.streams[cs.ID] = cs - return cs + if cs.ID == 0 { + panic("assigned stream ID 0") + } } func (cc *http2ClientConn) forgetStreamID(id uint32) { - cc.streamByID(id, true) -} - -func (cc *http2ClientConn) streamByID(id uint32, andRemove bool) *http2clientStream { cc.mu.Lock() - defer cc.mu.Unlock() - cs := cc.streams[id] - if andRemove && cs != nil && !cc.closed { - cc.lastActive = time.Now() - delete(cc.streams, id) - if len(cc.streams) == 0 && cc.idleTimer != nil { - cc.idleTimer.Reset(cc.idleTimeout) - cc.lastIdle = time.Now() - } - close(cs.done) - // Wake up checkResetOrDone via clientStream.awaitFlowControl and - // wake up RoundTrip if there is a pending request. - cc.cond.Broadcast() + slen := len(cc.streams) + delete(cc.streams, id) + if len(cc.streams) != slen-1 { + panic("forgetting unknown stream id") + } + cc.lastActive = time.Now() + if len(cc.streams) == 0 && cc.idleTimer != nil { + cc.idleTimer.Reset(cc.idleTimeout) + cc.lastIdle = time.Now() + } + // Wake up writeRequestBody via clientStream.awaitFlowControl and + // wake up RoundTrip if there is a pending request. + cc.cond.Broadcast() + + closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() + if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { + if http2VerboseLogs { + cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2) + } + cc.closed = true + defer cc.closeConn() } - return cs + + cc.mu.Unlock() } // clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. type http2clientConnReadLoop struct { - _ http2incomparable - cc *http2ClientConn - closeWhenIdle bool + _ http2incomparable + cc *http2ClientConn } // readLoop runs in its own goroutine and reads and dispatches frames. @@ -8378,8 +8878,8 @@ func http2isEOFOrNetReadError(err error) bool { func (rl *http2clientConnReadLoop) cleanup() { cc := rl.cc - defer cc.tconn.Close() - defer cc.t.connPool().MarkDead(cc) + cc.t.connPool().MarkDead(cc) + defer cc.closeConn() defer close(cc.readerDone) if cc.idleTimer != nil { @@ -8400,23 +8900,49 @@ func (rl *http2clientConnReadLoop) cleanup() { } else if err == io.EOF { err = io.ErrUnexpectedEOF } + cc.closed = true for _, cs := range cc.streams { - cs.bufPipe.CloseWithError(err) // no-op if already closed select { - case cs.resc <- http2resAndError{err: err}: + case <-cs.peerClosed: + // The server closed the stream before closing the conn, + // so no need to interrupt it. default: + cs.abortStreamLocked(err) } - close(cs.done) } - cc.closed = true cc.cond.Broadcast() cc.mu.Unlock() } +// countReadFrameError calls Transport.CountError with a string +// representing err. +func (cc *http2ClientConn) countReadFrameError(err error) { + f := cc.t.CountError + if f == nil || err == nil { + return + } + if ce, ok := err.(http2ConnectionError); ok { + errCode := http2ErrCode(ce) + f(fmt.Sprintf("read_frame_conn_error_%s", errCode.stringToken())) + return + } + if errors.Is(err, io.EOF) { + f("read_frame_eof") + return + } + if errors.Is(err, io.ErrUnexpectedEOF) { + f("read_frame_unexpected_eof") + return + } + if errors.Is(err, http2ErrFrameTooLarge) { + f("read_frame_too_large") + return + } + f("read_frame_other") +} + func (rl *http2clientConnReadLoop) run() error { cc := rl.cc - rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse - gotReply := false // ever saw a HEADERS reply gotSettings := false readIdleTimeout := cc.t.ReadIdleTimeout var t *time.Timer @@ -8433,9 +8959,7 @@ func (rl *http2clientConnReadLoop) run() error { cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(http2StreamError); ok { - if cs := cc.streamByID(se.StreamID, false); cs != nil { - cs.cc.writeStreamReset(cs.ID, se.Code, err) - cs.cc.forgetStreamID(cs.ID) + if cs := rl.streamByID(se.StreamID); cs != nil { if se.Cause == nil { se.Cause = cc.fr.errDetail } @@ -8443,6 +8967,7 @@ func (rl *http2clientConnReadLoop) run() error { } continue } else if err != nil { + cc.countReadFrameError(err) return err } if http2VerboseLogs { @@ -8455,22 +8980,16 @@ func (rl *http2clientConnReadLoop) run() error { } gotSettings = true } - maybeIdle := false // whether frame might transition us to idle switch f := f.(type) { case *http2MetaHeadersFrame: err = rl.processHeaders(f) - maybeIdle = true - gotReply = true case *http2DataFrame: err = rl.processData(f) - maybeIdle = true case *http2GoAwayFrame: err = rl.processGoAway(f) - maybeIdle = true case *http2RSTStreamFrame: err = rl.processResetStream(f) - maybeIdle = true case *http2SettingsFrame: err = rl.processSettings(f) case *http2PushPromiseFrame: @@ -8488,38 +9007,24 @@ func (rl *http2clientConnReadLoop) run() error { } return err } - if rl.closeWhenIdle && gotReply && maybeIdle { - cc.closeIfIdle() - } } } func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) error { - cc := rl.cc - cs := cc.streamByID(f.StreamID, false) + cs := rl.streamByID(f.StreamID) if cs == nil { // We'd get here if we canceled a request while the // server had its response still in flight. So if this // was just something we canceled, ignore it. return nil } - if f.StreamEnded() { - // Issue 20521: If the stream has ended, streamByID() causes - // clientStream.done to be closed, which causes the request's bodyWriter - // to be closed with an errStreamClosed, which may be received by - // clientConn.RoundTrip before the result of processing these headers. - // Deferring stream closure allows the header processing to occur first. - // clientConn.RoundTrip may still receive the bodyWriter error first, but - // the fix for issue 16102 prioritises any response. - // - // Issue 22413: If there is no request body, we should close the - // stream before writing to cs.resc so that the stream is closed - // immediately once RoundTrip returns. - if cs.req.Body != nil { - defer cc.forgetStreamID(f.StreamID) - } else { - cc.forgetStreamID(f.StreamID) - } + if cs.readClosed { + rl.endStreamError(cs, http2StreamError{ + StreamID: f.StreamID, + Code: http2ErrCodeProtocol, + Cause: errors.New("protocol error: headers after END_STREAM"), + }) + return nil } if !cs.firstByte { if cs.trace != nil { @@ -8543,9 +9048,11 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) erro return err } // Any other error type is a stream error. - cs.cc.writeStreamReset(f.StreamID, http2ErrCodeProtocol, err) - cc.forgetStreamID(cs.ID) - cs.resc <- http2resAndError{err: err} + rl.endStreamError(cs, http2StreamError{ + StreamID: f.StreamID, + Code: http2ErrCodeProtocol, + Cause: err, + }) return nil // return nil from process* funcs to keep conn alive } if res == nil { @@ -8553,7 +9060,11 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2MetaHeadersFrame) erro return nil } cs.resTrailer = &res.Trailer - cs.resc <- http2resAndError{res: res} + cs.res = res + close(cs.respHeaderRecv) + if f.StreamEnded() { + rl.endStream(cs) + } return nil } @@ -8615,6 +9126,9 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http } if statusCode >= 100 && statusCode <= 199 { + if f.StreamEnded() { + return nil, errors.New("1xx informational response with END_STREAM flag") + } cs.num1xx++ const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http if cs.num1xx > max1xxResponses { @@ -8627,42 +9141,49 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http } if statusCode == 100 { http2traceGot100Continue(cs.trace) - if cs.on100 != nil { - cs.on100() // forces any write delay timer to fire + select { + case cs.on100 <- struct{}{}: + default: } } cs.pastHeaders = false // do it all again return nil, nil } - streamEnded := f.StreamEnded() - isHead := cs.req.Method == "HEAD" - if !streamEnded || isHead { - res.ContentLength = -1 - if clens := res.Header["Content-Length"]; len(clens) == 1 { - if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil { - res.ContentLength = int64(cl) - } else { - // TODO: care? unlike http/1, it won't mess up our framing, so it's - // more safe smuggling-wise to ignore. - } - } else if len(clens) > 1 { + res.ContentLength = -1 + if clens := res.Header["Content-Length"]; len(clens) == 1 { + if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil { + res.ContentLength = int64(cl) + } else { // TODO: care? unlike http/1, it won't mess up our framing, so it's // more safe smuggling-wise to ignore. } + } else if len(clens) > 1 { + // TODO: care? unlike http/1, it won't mess up our framing, so it's + // more safe smuggling-wise to ignore. + } else if f.StreamEnded() && !cs.isHead { + res.ContentLength = 0 } - if streamEnded || isHead { + if cs.isHead { res.Body = http2noBody return res, nil } - cs.bufPipe = http2pipe{b: &http2dataBuffer{expected: res.ContentLength}} + if f.StreamEnded() { + if res.ContentLength > 0 { + res.Body = http2missingBody{} + } else { + res.Body = http2noBody + } + return res, nil + } + + cs.bufPipe.setBuffer(&http2dataBuffer{expected: res.ContentLength}) cs.bytesRemain = res.ContentLength res.Body = http2transportResponseBody{cs} - go cs.awaitRequestCancel(cs.req) - if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { + if cs.requestedGzip && http2asciiEqualFold(res.Header.Get("Content-Encoding"), "gzip") { res.Header.Del("Content-Encoding") res.Header.Del("Content-Length") res.ContentLength = -1 @@ -8701,8 +9222,7 @@ func (rl *http2clientConnReadLoop) processTrailers(cs *http2clientStream, f *htt } // transportResponseBody is the concrete type of Transport.RoundTrip's -// Response.Body. It is an io.ReadCloser. On Read, it reads from cs.body. -// On Close it sends RST_STREAM if EOF wasn't already seen. +// Response.Body. It is an io.ReadCloser. type http2transportResponseBody struct { cs *http2clientStream } @@ -8720,7 +9240,7 @@ func (b http2transportResponseBody) Read(p []byte) (n int, err error) { n = int(cs.bytesRemain) if err == nil { err = errors.New("net/http: server replied with more than declared Content-Length; truncated") - cc.writeStreamReset(cs.ID, http2ErrCodeProtocol, err) + cs.abortStream(err) } cs.readErr = err return int(cs.bytesRemain), err @@ -8738,8 +9258,6 @@ func (b http2transportResponseBody) Read(p []byte) (n int, err error) { } cc.mu.Lock() - defer cc.mu.Unlock() - var connAdd, streamAdd int32 // Check the conn-level first, before the stream-level. if v := cc.inflow.available(); v < http2transportDefaultConnFlow/2 { @@ -8756,6 +9274,8 @@ func (b http2transportResponseBody) Read(p []byte) (n int, err error) { cs.inflow.add(streamAdd) } } + cc.mu.Unlock() + if connAdd != 0 || streamAdd != 0 { cc.wmu.Lock() defer cc.wmu.Unlock() @@ -8776,34 +9296,45 @@ func (b http2transportResponseBody) Close() error { cs := b.cs cc := cs.cc - serverSentStreamEnd := cs.bufPipe.Err() == io.EOF unread := cs.bufPipe.Len() - - if unread > 0 || !serverSentStreamEnd { + if unread > 0 { cc.mu.Lock() - cc.wmu.Lock() - if !serverSentStreamEnd { - cc.fr.WriteRSTStream(cs.ID, http2ErrCodeCancel) - cs.didReset = true - } // Return connection-level flow control. if unread > 0 { cc.inflow.add(int32(unread)) + } + cc.mu.Unlock() + + // TODO(dneil): Acquiring this mutex can block indefinitely. + // Move flow control return to a goroutine? + cc.wmu.Lock() + // Return connection-level flow control. + if unread > 0 { cc.fr.WriteWindowUpdate(0, uint32(unread)) } cc.bw.Flush() cc.wmu.Unlock() - cc.mu.Unlock() } cs.bufPipe.BreakWithError(http2errClosedResponseBody) - cc.forgetStreamID(cs.ID) + cs.abortStream(http2errClosedResponseBody) + + select { + case <-cs.donec: + case <-cs.ctx.Done(): + // See golang/go#49366: The net/http package can cancel the + // request context after the response body is fully read. + // Don't treat this as an error. + return nil + case <-cs.reqCancel: + return http2errRequestCanceled + } return nil } func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { cc := rl.cc - cs := cc.streamByID(f.StreamID, f.StreamEnded()) + cs := rl.streamByID(f.StreamID) data := f.Data() if cs == nil { cc.mu.Lock() @@ -8832,6 +9363,14 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { } return nil } + if cs.readClosed { + cc.logf("protocol error: received DATA after END_STREAM") + rl.endStreamError(cs, http2StreamError{ + StreamID: f.StreamID, + Code: http2ErrCodeProtocol, + }) + return nil + } if !cs.firstByte { cc.logf("protocol error: received DATA before a HEADERS frame") rl.endStreamError(cs, http2StreamError{ @@ -8841,7 +9380,7 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { return nil } if f.Length > 0 { - if cs.req.Method == "HEAD" && len(data) > 0 { + if cs.isHead && len(data) > 0 { cc.logf("protocol error: received DATA on a HEAD request") rl.endStreamError(cs, http2StreamError{ StreamID: f.StreamID, @@ -8863,30 +9402,39 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { if pad := int(f.Length) - len(data); pad > 0 { refund += pad } - // Return len(data) now if the stream is already closed, - // since data will never be read. - didReset := cs.didReset - if didReset { - refund += len(data) + + didReset := false + var err error + if len(data) > 0 { + if _, err = cs.bufPipe.Write(data); err != nil { + // Return len(data) now if the stream is already closed, + // since data will never be read. + didReset = true + refund += len(data) + } } + if refund > 0 { cc.inflow.add(int32(refund)) + if !didReset { + cs.inflow.add(int32(refund)) + } + } + cc.mu.Unlock() + + if refund > 0 { cc.wmu.Lock() cc.fr.WriteWindowUpdate(0, uint32(refund)) if !didReset { - cs.inflow.add(int32(refund)) cc.fr.WriteWindowUpdate(cs.ID, uint32(refund)) } cc.bw.Flush() cc.wmu.Unlock() } - cc.mu.Unlock() - if len(data) > 0 && !didReset { - if _, err := cs.bufPipe.Write(data); err != nil { - rl.endStreamError(cs, err) - return err - } + if err != nil { + rl.endStreamError(cs, err) + return nil } } @@ -8899,24 +9447,32 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error { func (rl *http2clientConnReadLoop) endStream(cs *http2clientStream) { // TODO: check that any declared content-length matches, like // server.go's (*stream).endStream method. - rl.endStreamError(cs, nil) + if !cs.readClosed { + cs.readClosed = true + // Close cs.bufPipe and cs.peerClosed with cc.mu held to avoid a + // race condition: The caller can read io.EOF from Response.Body + // and close the body before we close cs.peerClosed, causing + // cleanupWriteRequest to send a RST_STREAM. + rl.cc.mu.Lock() + defer rl.cc.mu.Unlock() + cs.bufPipe.closeWithErrorAndCode(io.EOF, cs.copyTrailers) + close(cs.peerClosed) + } } func (rl *http2clientConnReadLoop) endStreamError(cs *http2clientStream, err error) { - var code func() - if err == nil { - err = io.EOF - code = cs.copyTrailers - } - if http2isConnectionCloseRequest(cs.req) { - rl.closeWhenIdle = true - } - cs.bufPipe.closeWithErrorAndCode(err, code) + cs.readAborted = true + cs.abortStream(err) +} - select { - case cs.resc <- http2resAndError{err: err}: - default: +func (rl *http2clientConnReadLoop) streamByID(id uint32) *http2clientStream { + rl.cc.mu.Lock() + defer rl.cc.mu.Unlock() + cs := rl.cc.streams[id] + if cs != nil && !cs.readAborted { + return cs } + return nil } func (cs *http2clientStream) copyTrailers() { @@ -8935,12 +9491,33 @@ func (rl *http2clientConnReadLoop) processGoAway(f *http2GoAwayFrame) error { if f.ErrCode != 0 { // TODO: deal with GOAWAY more. particularly the error code cc.vlogf("transport got GOAWAY with error code = %v", f.ErrCode) + if fn := cc.t.CountError; fn != nil { + fn("recv_goaway_" + f.ErrCode.stringToken()) + } + } cc.setGoAway(f) return nil } func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error { + cc := rl.cc + // Locking both mu and wmu here allows frame encoding to read settings with only wmu held. + // Acquiring wmu when f.IsAck() is unnecessary, but convenient and mostly harmless. + cc.wmu.Lock() + defer cc.wmu.Unlock() + + if err := rl.processSettingsNoWrite(f); err != nil { + return err + } + if !f.IsAck() { + cc.fr.WriteSettingsAck() + cc.bw.Flush() + } + return nil +} + +func (rl *http2clientConnReadLoop) processSettingsNoWrite(f *http2SettingsFrame) error { cc := rl.cc cc.mu.Lock() defer cc.mu.Unlock() @@ -8953,12 +9530,14 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error return http2ConnectionError(http2ErrCodeProtocol) } + var seenMaxConcurrentStreams bool err := f.ForeachSetting(func(s http2Setting) error { switch s.ID { case http2SettingMaxFrameSize: cc.maxFrameSize = s.Val case http2SettingMaxConcurrentStreams: cc.maxConcurrentStreams = s.Val + seenMaxConcurrentStreams = true case http2SettingMaxHeaderListSize: cc.peerMaxHeaderListSize = uint64(s.Val) case http2SettingInitialWindowSize: @@ -8990,17 +9569,23 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error return err } - cc.wmu.Lock() - defer cc.wmu.Unlock() + if !cc.seenSettings { + if !seenMaxConcurrentStreams { + // This was the servers initial SETTINGS frame and it + // didn't contain a MAX_CONCURRENT_STREAMS field so + // increase the number of concurrent streams this + // connection can establish to our default. + cc.maxConcurrentStreams = http2defaultMaxConcurrentStreams + } + cc.seenSettings = true + } - cc.fr.WriteSettingsAck() - cc.bw.Flush() - return cc.werr + return nil } func (rl *http2clientConnReadLoop) processWindowUpdate(f *http2WindowUpdateFrame) error { cc := rl.cc - cs := cc.streamByID(f.StreamID, false) + cs := rl.streamByID(f.StreamID) if f.StreamID != 0 && cs == nil { return nil } @@ -9020,24 +9605,22 @@ func (rl *http2clientConnReadLoop) processWindowUpdate(f *http2WindowUpdateFrame } func (rl *http2clientConnReadLoop) processResetStream(f *http2RSTStreamFrame) error { - cs := rl.cc.streamByID(f.StreamID, true) + cs := rl.streamByID(f.StreamID) if cs == nil { - // TODO: return error if server tries to RST_STEAM an idle stream + // TODO: return error if server tries to RST_STREAM an idle stream return nil } - select { - case <-cs.peerReset: - // Already reset. - // This is the only goroutine - // which closes this, so there - // isn't a race. - default: - err := http2streamError(cs.ID, f.ErrCode) - cs.resetErr = err - close(cs.peerReset) - cs.bufPipe.CloseWithError(err) - cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl + serr := http2streamError(cs.ID, f.ErrCode) + serr.Cause = http2errFromPeer + if f.ErrCode == http2ErrCodeProtocol { + rl.cc.SetDoNotReuse() } + if fn := cs.cc.t.CountError; fn != nil { + fn("recv_rststream_" + f.ErrCode.stringToken()) + } + cs.abortStream(serr) + + cs.bufPipe.CloseWithError(serr) return nil } @@ -9059,19 +9642,24 @@ func (cc *http2ClientConn) Ping(ctx context.Context) error { } cc.mu.Unlock() } - cc.wmu.Lock() - if err := cc.fr.WritePing(false, p); err != nil { - cc.wmu.Unlock() - return err - } - if err := cc.bw.Flush(); err != nil { - cc.wmu.Unlock() - return err - } - cc.wmu.Unlock() + errc := make(chan error, 1) + go func() { + cc.wmu.Lock() + defer cc.wmu.Unlock() + if err := cc.fr.WritePing(false, p); err != nil { + errc <- err + return + } + if err := cc.bw.Flush(); err != nil { + errc <- err + return + } + }() select { case <-c: return nil + case err := <-errc: + return err case <-ctx.Done(): return ctx.Err() case <-cc.readerDone: @@ -9146,7 +9734,19 @@ func (t *http2Transport) logf(format string, args ...interface{}) { log.Printf(format, args...) } -var http2noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil)) +var http2noBody io.ReadCloser = http2noBodyReader{} + +type http2noBodyReader struct{} + +func (http2noBodyReader) Close() error { return nil } + +func (http2noBodyReader) Read([]byte) (int, error) { return 0, io.EOF } + +type http2missingBody struct{} + +func (http2missingBody) Close() error { return nil } + +func (http2missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF } func http2strSliceContains(ss []string, s string) bool { for _, v := range ss { @@ -9194,87 +9794,6 @@ 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() { - cs.cc.mu.Lock() - cs.startedWrite = true - cs.cc.mu.Unlock() - resc <- cs.writeRequestBody(body, cs.req.Body) - } - s.delay = t.expectContinueTimeout() - if s.delay == 0 || - !httpguts.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 { - if s.timer.Stop() { - s.resc <- nil - } - } -} - -func (s http2bodyWriterState) on100() { - if s.timer == nil { - // If we didn't do a delayed write, ignore the server's - // bogus 100 continue response. - 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 { - // We're not doing a delayed write (see - // getBodyWriterState), so just start the writing - // goroutine immediately. - go s.fn() - return - } - http2traceWait100Continue(s.cs.trace) - if s.timer.Stop() { - s.timer.Reset(s.delay) - } -} - // isConnectionCloseRequest reports whether req should use its own // connection for a single request and then close the connection. func http2isConnectionCloseRequest(req *Request) bool { @@ -9751,7 +10270,8 @@ type http2WriteScheduler interface { // Pop dequeues the next frame to write. Returns false if no frames can // be written. Frames with a given wr.StreamID() are Pop'd in the same - // order they are Push'd. No frames should be discarded except by CloseStream. + // order they are Push'd, except RST_STREAM frames. No frames should be + // discarded except by CloseStream. Pop() (wr http2FrameWriteRequest, ok bool) } @@ -9771,6 +10291,7 @@ type http2FrameWriteRequest struct { // stream is the stream on which this frame will be written. // nil for non-stream frames like PING and SETTINGS. + // nil for RST_STREAM streams, which use the StreamError.StreamID field instead. stream *http2stream // done, if non-nil, must be a buffered channel with space for @@ -10343,16 +10864,15 @@ func (ws *http2priorityWriteScheduler) AdjustStream(streamID uint32, priority ht func (ws *http2priorityWriteScheduler) Push(wr http2FrameWriteRequest) { var n *http2priorityNode - if id := wr.StreamID(); id == 0 { + if wr.isControl() { n = &ws.root } else { + id := wr.StreamID() n = ws.nodes[id] if n == nil { // id is an idle or closed stream. wr should not be a HEADERS or - // DATA frame. However, wr can be a RST_STREAM. In this case, we - // push wr onto the root, rather than creating a new priorityNode, - // since RST_STREAM is tiny and the stream's priority is unknown - // anyway. See issue #17919. + // DATA frame. In other case, we push wr onto the root, rather + // than creating a new priorityNode. if wr.DataSize() > 0 { panic("add DATA on non-open stream") } @@ -10450,11 +10970,11 @@ func (ws *http2randomWriteScheduler) AdjustStream(streamID uint32, priority http } func (ws *http2randomWriteScheduler) Push(wr http2FrameWriteRequest) { - id := wr.StreamID() - if id == 0 { + if wr.isControl() { ws.zero.push(wr) return } + id := wr.StreamID() q, ok := ws.sq[id] if !ok { q = ws.queuePool.get() @@ -10464,7 +10984,7 @@ func (ws *http2randomWriteScheduler) Push(wr http2FrameWriteRequest) { } func (ws *http2randomWriteScheduler) Pop() (http2FrameWriteRequest, bool) { - // Control frames first. + // Control and RST_STREAM frames first. if !ws.zero.empty() { return ws.zero.shift(), true } diff --git a/src/net/http/h2_error.go b/src/net/http/h2_error.go new file mode 100644 index 00000000000000..0391d31e5b428f --- /dev/null +++ b/src/net/http/h2_error.go @@ -0,0 +1,38 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !nethttpomithttp2 +// +build !nethttpomithttp2 + +package http + +import ( + "reflect" +) + +func (e http2StreamError) As(target any) bool { + dst := reflect.ValueOf(target).Elem() + dstType := dst.Type() + if dstType.Kind() != reflect.Struct { + return false + } + src := reflect.ValueOf(e) + srcType := src.Type() + numField := srcType.NumField() + if dstType.NumField() != numField { + return false + } + for i := 0; i < numField; i++ { + sf := srcType.Field(i) + df := dstType.Field(i) + if sf.Name != df.Name || !sf.Type.ConvertibleTo(df.Type) { + return false + } + } + for i := 0; i < numField; i++ { + df := dst.Field(i) + df.Set(src.Field(i).Convert(df.Type())) + } + return true +} diff --git a/src/net/http/h2_error_test.go b/src/net/http/h2_error_test.go new file mode 100644 index 00000000000000..0d85e2f36c3faf --- /dev/null +++ b/src/net/http/h2_error_test.go @@ -0,0 +1,44 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !nethttpomithttp2 +// +build !nethttpomithttp2 + +package http + +import ( + "errors" + "fmt" + "testing" +) + +type externalStreamErrorCode uint32 + +type externalStreamError struct { + StreamID uint32 + Code externalStreamErrorCode + Cause error +} + +func (e externalStreamError) Error() string { + return fmt.Sprintf("ID %v, code %v", e.StreamID, e.Code) +} + +func TestStreamError(t *testing.T) { + var target externalStreamError + streamErr := http2streamError(42, http2ErrCodeProtocol) + ok := errors.As(streamErr, &target) + if !ok { + t.Fatalf("errors.As failed") + } + if target.StreamID != streamErr.StreamID { + t.Errorf("got StreamID %v, expected %v", target.StreamID, streamErr.StreamID) + } + if target.Cause != streamErr.Cause { + t.Errorf("got Cause %v, expected %v", target.Cause, streamErr.Cause) + } + if uint32(target.Code) != uint32(streamErr.Code) { + t.Errorf("got Code %v, expected %v", target.Code, streamErr.Code) + } +} diff --git a/src/net/http/header.go b/src/net/http/header.go index 4c72dcb2c88d25..e0b342c63cbd94 100644 --- a/src/net/http/header.go +++ b/src/net/http/header.go @@ -13,6 +13,8 @@ import ( "strings" "sync" "time" + + "golang.org/x/net/http/httpguts" ) // A Header represents the key-value pairs in an HTTP header. @@ -41,7 +43,8 @@ func (h Header) Set(key, value string) { // Get gets the first value associated with the given key. If // there are no values associated with the key, Get returns "". // It is case insensitive; textproto.CanonicalMIMEHeaderKey is -// used to canonicalize the provided key. To use non-canonical keys, +// used to canonicalize the provided key. Get assumes that all +// keys are stored in canonical form. To use non-canonical keys, // access the map directly. func (h Header) Get(key string) string { return textproto.MIMEHeader(h).Get(key) @@ -101,6 +104,12 @@ func (h Header) Clone() Header { sv := make([]string, nv) // shared backing array for headers' values h2 := make(Header, len(h)) for k, vv := range h { + if vv == nil { + // Preserve nil values. ReverseProxy distinguishes + // between nil and zero-length header values. + h2[k] = nil + continue + } n := copy(sv, vv) h2[k] = sv[:n:n] sv = sv[n:] @@ -155,7 +164,7 @@ func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kv func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key } var headerSorterPool = sync.Pool{ - New: func() interface{} { return new(headerSorter) }, + New: func() any { return new(headerSorter) }, } // sortedKeyValues returns h's keys sorted in the returned kvs @@ -192,6 +201,13 @@ func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptra kvs, sorter := h.sortedKeyValues(exclude) var formattedVals []string for _, kv := range kvs { + if !httpguts.ValidHeaderFieldName(kv.key) { + // This could be an error. In the common case of + // writing response headers, however, we have no good + // way to provide the error back to the server + // handler, so just drop invalid headers instead. + continue + } for _, v := range kv.values { v = headerNewlineToSpace.Replace(v) v = textproto.TrimString(v) diff --git a/src/net/http/header_test.go b/src/net/http/header_test.go index 47893629194b6a..e98cc5c760b2b0 100644 --- a/src/net/http/header_test.go +++ b/src/net/http/header_test.go @@ -9,6 +9,7 @@ import ( "internal/race" "reflect" "runtime" + "strings" "testing" "time" ) @@ -89,10 +90,23 @@ var headerWriteTests = []struct { "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" + "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n", }, + // Tests invalid characters in headers. + { + Header{ + "Content-Type": {"text/html; charset=UTF-8"}, + "NewlineInValue": {"1\r\nBar: 2"}, + "NewlineInKey\r\n": {"1"}, + "Colon:InKey": {"1"}, + "Evil: 1\r\nSmuggledValue": {"1"}, + }, + nil, + "Content-Type: text/html; charset=UTF-8\r\n" + + "NewlineInValue: 1 Bar: 2\r\n", + }, } func TestHeaderWrite(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder for i, test := range headerWriteTests { test.h.WriteSubset(&buf, test.exclude) if buf.String() != test.expected { @@ -235,6 +249,11 @@ func TestCloneOrMakeHeader(t *testing.T) { in: Header{"foo": {"bar"}}, want: Header{"foo": {"bar"}}, }, + { + name: "nil value", + in: Header{"foo": nil}, + want: Header{"foo": nil}, + }, } for _, tt := range tests { diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go index 1b712ef2b0a8e8..1c1d8801558ed7 100644 --- a/src/net/http/httptest/recorder.go +++ b/src/net/http/httptest/recorder.go @@ -207,18 +207,20 @@ func (rw *ResponseRecorder) Result() *http.Response { if trailers, ok := rw.snapHeader["Trailer"]; ok { res.Trailer = make(http.Header, len(trailers)) for _, k := range trailers { - k = http.CanonicalHeaderKey(k) - if !httpguts.ValidTrailerHeader(k) { - // Ignore since forbidden by RFC 7230, section 4.1.2. - continue + for _, k := range strings.Split(k, ",") { + k = http.CanonicalHeaderKey(textproto.TrimString(k)) + if !httpguts.ValidTrailerHeader(k) { + // Ignore since forbidden by RFC 7230, section 4.1.2. + continue + } + vv, ok := rw.HeaderMap[k] + if !ok { + continue + } + vv2 := make([]string, len(vv)) + copy(vv2, vv) + res.Trailer[k] = vv2 } - vv, ok := rw.HeaderMap[k] - if !ok { - continue - } - vv2 := make([]string, len(vv)) - copy(vv2, vv) - res.Trailer[k] = vv2 } } for k, vv := range rw.HeaderMap { diff --git a/src/net/http/httptest/recorder_test.go b/src/net/http/httptest/recorder_test.go index 8cb32dd7403683..4782eced43e6ce 100644 --- a/src/net/http/httptest/recorder_test.go +++ b/src/net/http/httptest/recorder_test.go @@ -220,8 +220,7 @@ func TestRecorder(t *testing.T) { "Trailer headers are correctly recorded", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Non-Trailer", "correct") - w.Header().Set("Trailer", "Trailer-A") - w.Header().Add("Trailer", "Trailer-B") + w.Header().Set("Trailer", "Trailer-A, Trailer-B") w.Header().Add("Trailer", "Trailer-C") io.WriteString(w, "") w.Header().Set("Non-Trailer", "incorrect") diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go index 4f85ff55d89397..f254a494d162d9 100644 --- a/src/net/http/httptest/server.go +++ b/src/net/http/httptest/server.go @@ -76,7 +76,9 @@ func newLocalListener() net.Listener { // When debugging a particular http server-based test, // this flag lets you run +// // go test -run=BrokenTest -httptest.serve=127.0.0.1:8000 +// // to start the broken server so you can interact with it manually. // We only register this flag if it looks like the caller knows about it // and is trying to use it as we don't want to pollute flags and this @@ -317,21 +319,17 @@ func (s *Server) wrap() { s.mu.Lock() defer s.mu.Unlock() - // Keep Close from returning until the user's ConnState hook - // (if any) finishes. Without this, the call to forgetConn - // below might send the count to 0 before we run the hook. - s.wg.Add(1) - defer s.wg.Done() - switch cs { case http.StateNew: - s.wg.Add(1) if _, exists := s.conns[c]; exists { panic("invalid state transition") } if s.conns == nil { s.conns = make(map[net.Conn]http.ConnState) } + // Add c to the set of tracked conns and increment it to the + // waitgroup. + s.wg.Add(1) s.conns[c] = cs if s.closed { // Probably just a socket-late-binding dial from @@ -358,7 +356,14 @@ func (s *Server) wrap() { s.closeConn(c) } case http.StateHijacked, http.StateClosed: - s.forgetConn(c) + // Remove c from the set of tracked conns and decrement it from the + // waitgroup, unless it was previously removed. + if _, ok := s.conns[c]; ok { + delete(s.conns, c) + // Keep Close from returning until the user's ConnState hook + // (if any) finishes. + defer s.wg.Done() + } } if oldHook != nil { oldHook(c, cs) @@ -378,13 +383,3 @@ func (s *Server) closeConnChan(c net.Conn, done chan<- struct{}) { done <- struct{}{} } } - -// forgetConn removes c from the set of tracked conns and decrements it from the -// waitgroup, unless it was previously removed. -// s.mu must be held. -func (s *Server) forgetConn(c net.Conn) { - if _, ok := s.conns[c]; ok { - delete(s.conns, c) - s.wg.Done() - } -} diff --git a/src/net/http/httptest/server_test.go b/src/net/http/httptest/server_test.go index 39568b358c2610..5313f65456c350 100644 --- a/src/net/http/httptest/server_test.go +++ b/src/net/http/httptest/server_test.go @@ -9,6 +9,7 @@ import ( "io" "net" "net/http" + "sync" "testing" ) @@ -203,6 +204,59 @@ func TestServerZeroValueClose(t *testing.T) { ts.Close() // tests that it doesn't panic } +// Issue 51799: test hijacking a connection and then closing it +// concurrently with closing the server. +func TestCloseHijackedConnection(t *testing.T) { + hijacked := make(chan net.Conn) + ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer close(hijacked) + hj, ok := w.(http.Hijacker) + if !ok { + t.Fatal("failed to hijack") + } + c, _, err := hj.Hijack() + if err != nil { + t.Fatal(err) + } + hijacked <- c + })) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + req, err := http.NewRequest("GET", ts.URL, nil) + if err != nil { + t.Log(err) + } + // Use a client not associated with the Server. + var c http.Client + resp, err := c.Do(req) + if err != nil { + t.Log(err) + return + } + resp.Body.Close() + }() + + wg.Add(1) + conn := <-hijacked + go func(conn net.Conn) { + defer wg.Done() + // Close the connection and then inform the Server that + // we closed it. + conn.Close() + ts.Config.ConnState(conn, http.StateClosed) + }(conn) + + wg.Add(1) + go func() { + defer wg.Done() + ts.Close() + }() + wg.Wait() +} + func TestTLSServerWithHTTP2(t *testing.T) { modes := []struct { name string diff --git a/src/net/http/httptrace/trace.go b/src/net/http/httptrace/trace.go index 5777c91747c200..6af30f78d1f4b9 100644 --- a/src/net/http/httptrace/trace.go +++ b/src/net/http/httptrace/trace.go @@ -50,7 +50,7 @@ func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context { } } if trace.DNSDone != nil { - nt.DNSDone = func(netIPs []interface{}, coalesced bool, err error) { + nt.DNSDone = func(netIPs []any, coalesced bool, err error) { addrs := make([]net.IPAddr, len(netIPs)) for i, ip := range netIPs { addrs[i] = ip.(net.IPAddr) diff --git a/src/net/http/httptrace/trace_test.go b/src/net/http/httptrace/trace_test.go index bb57ada8531322..6efa1f79401b95 100644 --- a/src/net/http/httptrace/trace_test.go +++ b/src/net/http/httptrace/trace_test.go @@ -5,13 +5,13 @@ package httptrace import ( - "bytes" "context" + "strings" "testing" ) func TestWithClientTrace(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder connectStart := func(b byte) func(network, addr string) { return func(network, addr string) { buf.WriteByte(b) @@ -37,7 +37,7 @@ func TestWithClientTrace(t *testing.T) { } func TestCompose(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder var testNum int connectStart := func(b byte) func(network, addr string) { diff --git a/src/net/http/httputil/dump.go b/src/net/http/httputil/dump.go index 2948f27e5d5c58..6f5fa0d01d0d81 100644 --- a/src/net/http/httputil/dump.go +++ b/src/net/http/httputil/dump.go @@ -258,9 +258,6 @@ func DumpRequest(req *http.Request, body bool) ([]byte, error) { if len(req.TransferEncoding) > 0 { fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ",")) } - if req.Close { - fmt.Fprintf(&b, "Connection: close\r\n") - } err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump) if err != nil { @@ -292,7 +289,7 @@ func DumpRequest(req *http.Request, body bool) ([]byte, error) { // can detect that the lack of body was intentional. var errNoBody = errors.New("sentinel error value") -// failureToReadBody is a io.ReadCloser that just returns errNoBody on +// failureToReadBody is an io.ReadCloser that just returns errNoBody on // Read. It's swapped in when we don't actually want to consume // the body, but need a non-nil one, and want to distinguish the // error from reading the dummy body. diff --git a/src/net/http/httputil/dump_test.go b/src/net/http/httputil/dump_test.go index 366cc8239ac755..c20c054865fa32 100644 --- a/src/net/http/httputil/dump_test.go +++ b/src/net/http/httputil/dump_test.go @@ -31,7 +31,7 @@ type dumpTest struct { Req *http.Request GetReq func() *http.Request - Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body + Body any // optional []byte or func() io.ReadCloser to populate Req.Body WantDump string WantDumpOut string @@ -236,6 +236,19 @@ var dumpTests = []dumpTest{ "Transfer-Encoding: chunked\r\n" + "Accept-Encoding: gzip\r\n\r\n", }, + + // Issue 54616: request with Connection header doesn't result in duplicate header. + { + GetReq: func() *http.Request { + return mustReadRequest("GET / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: close\r\n\r\n") + }, + NoBody: true, + WantDump: "GET / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "Connection: close\r\n\r\n", + }, } func TestDumpRequest(t *testing.T) { @@ -510,7 +523,7 @@ func TestDumpRequestOutIssue38352(t *testing.T) { select { case <-out: case <-time.After(timeout): - b := &bytes.Buffer{} + b := &strings.Builder{} fmt.Fprintf(b, "deadlock detected on iteration %d after %s with delay: %v\n", i, timeout, delay) pprof.Lookup("goroutine").WriteTo(b, 1) t.Fatal(b.String()) diff --git a/src/net/http/httputil/example_test.go b/src/net/http/httputil/example_test.go index b77a243ca3a44c..6c107f839001b1 100644 --- a/src/net/http/httputil/example_test.go +++ b/src/net/http/httputil/example_test.go @@ -103,7 +103,12 @@ func ExampleReverseProxy() { if err != nil { log.Fatal(err) } - frontendProxy := httptest.NewServer(httputil.NewSingleHostReverseProxy(rpURL)) + frontendProxy := httptest.NewServer(&httputil.ReverseProxy{ + Rewrite: func(r *httputil.ProxyRequest) { + r.SetXForwarded() + r.SetURL(rpURL) + }, + }) defer frontendProxy.Close() resp, err := http.Get(frontendProxy.URL) diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 8b63368386f43b..9ab7367eb2a9e4 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -8,9 +8,11 @@ package httputil import ( "context" + "errors" "fmt" "io" "log" + "mime" "net" "net/http" "net/http/internal/ascii" @@ -23,29 +25,118 @@ import ( "golang.org/x/net/http/httpguts" ) -// ReverseProxy is an HTTP Handler that takes an incoming request and -// sends it to another server, proxying the response back to the -// client. +// A ProxyRequest contains a request to be rewritten by a ReverseProxy. +type ProxyRequest struct { + // In is the request received by the proxy. + // The Rewrite function must not modify In. + In *http.Request + + // Out is the request which will be sent by the proxy. + // The Rewrite function may modify or replace this request. + // Hop-by-hop headers are removed from this request + // before Rewrite is called. + Out *http.Request +} + +// SetURL routes the outbound request to the scheme, host, and base path +// provided in target. If the target's path is "/base" and the incoming +// request was for "/dir", the target request will be for "/base/dir". +// +// SetURL rewrites the outbound Host header to match the target's host. +// To preserve the inbound request's Host header (the default behavior +// of NewSingleHostReverseProxy): +// +// rewriteFunc := func(r *httputil.ProxyRequest) { +// r.SetURL(url) +// r.Out.Host = r.In.Host +// } +func (r *ProxyRequest) SetURL(target *url.URL) { + rewriteRequestURL(r.Out, target) + r.Out.Host = "" +} + +// SetXForwarded sets the X-Forwarded-For, X-Forwarded-Host, and +// X-Forwarded-Proto headers of the outbound request. // -// ReverseProxy by default sets the client IP as the value of the -// X-Forwarded-For header. +// - The X-Forwarded-For header is set to the client IP address. +// - The X-Forwarded-Host header is set to the host name requested +// by the client. +// - The X-Forwarded-Proto header is set to "http" or "https", depending +// on whether the inbound request was made on a TLS-enabled connection. // -// If an X-Forwarded-For header already exists, the client IP is -// appended to the existing values. As a special case, if the header -// exists in the Request.Header map but has a nil value (such as when -// set by the Director func), the X-Forwarded-For header is -// not modified. +// If the outbound request contains an existing X-Forwarded-For header, +// SetXForwarded appends the client IP address to it. To append to the +// inbound request's X-Forwarded-For header (the default behavior of +// ReverseProxy when using a Director function), copy the header +// from the inbound request before calling SetXForwarded: // -// To prevent IP spoofing, be sure to delete any pre-existing -// X-Forwarded-For header coming from the client or -// an untrusted proxy. +// rewriteFunc := func(r *httputil.ProxyRequest) { +// r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"] +// r.SetXForwarded() +// } +func (r *ProxyRequest) SetXForwarded() { + clientIP, _, err := net.SplitHostPort(r.In.RemoteAddr) + if err == nil { + prior := r.Out.Header["X-Forwarded-For"] + if len(prior) > 0 { + clientIP = strings.Join(prior, ", ") + ", " + clientIP + } + r.Out.Header.Set("X-Forwarded-For", clientIP) + } else { + r.Out.Header.Del("X-Forwarded-For") + } + r.Out.Header.Set("X-Forwarded-Host", r.In.Host) + if r.In.TLS == nil { + r.Out.Header.Set("X-Forwarded-Proto", "http") + } else { + r.Out.Header.Set("X-Forwarded-Proto", "https") + } +} + +// ReverseProxy is an HTTP Handler that takes an incoming request and +// sends it to another server, proxying the response back to the +// client. type ReverseProxy struct { - // Director must be a function which modifies + // Rewrite must be a function which modifies + // the request into a new request to be sent + // using Transport. Its response is then copied + // back to the original client unmodified. + // Rewrite must not access the provided ProxyRequest + // or its contents after returning. + // + // The Forwarded, X-Forwarded, X-Forwarded-Host, + // and X-Forwarded-Proto headers are removed from the + // outbound request before Rewrite is called. See also + // the ProxyRequest.SetXForwarded method. + // + // At most one of Rewrite or Director may be set. + Rewrite func(*ProxyRequest) + + // Director is a function which modifies // the request into a new request to be sent // using Transport. Its response is then copied // back to the original client unmodified. // Director must not access the provided Request // after returning. + // + // By default, the X-Forwarded-For, X-Forwarded-Host, and + // X-Forwarded-Proto headers of the ourgoing request are + // set as by the ProxyRequest.SetXForwarded function. + // + // If an X-Forwarded-For header already exists, the client IP is + // appended to the existing values. To prevent IP spoofing, be + // sure to delete any pre-existing X-Forwarded-For header + // coming from the client or an untrusted proxy. + // + // If a header exists in the Request.Header map but has a nil value + // (such as when set by the Director func), it is not modified. + // + // Hop-by-hop headers are removed from the request after + // Director returns, which can remove headers added by + // Director. Use a Rewrite function instead to ensure + // modifications to the request are preserved. + // + // At most one of Rewrite or Director may be set. Director func(*http.Request) // The transport used to perform proxy requests. @@ -137,28 +228,41 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) { // URLs to the scheme, host, and base path provided in target. If the // target's path is "/base" and the incoming request was for "/dir", // the target request will be for /base/dir. +// // NewSingleHostReverseProxy does not rewrite the Host header. -// To rewrite Host headers, use ReverseProxy directly with a custom -// Director policy. +// +// To customize the ReverseProxy behavior beyond what +// NewSingleHostReverseProxy provides, use ReverseProxy directly +// with a Rewrite function. The ProxyRequest SetURL method +// may be used to route the outbound request. (Note that SetURL, +// unlike NewSingleHostReverseProxy, rewrites the Host header +// of the outbound request by default.) +// +// proxy := &ReverseProxy{ +// Rewrite: func(r *ProxyRequest) { +// r.SetURL(target) +// r.Out.Host = r.In.Host // if desired +// } +// } func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { - targetQuery := target.RawQuery director := func(req *http.Request) { - req.URL.Scheme = target.Scheme - req.URL.Host = target.Host - req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) - if targetQuery == "" || req.URL.RawQuery == "" { - req.URL.RawQuery = targetQuery + req.URL.RawQuery - } 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", "") - } + rewriteRequestURL(req, target) } return &ReverseProxy{Director: director} } +func rewriteRequestURL(req *http.Request, target *url.URL) { + targetQuery := target.RawQuery + req.URL.Scheme = target.Scheme + req.URL.Host = target.Host + req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) + if targetQuery == "" || req.URL.RawQuery == "" { + req.URL.RawQuery = targetQuery + req.URL.RawQuery + } else { + req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery + } +} + func copyHeader(dst, src http.Header) { for k, vv := range src { for _, v := range vv { @@ -217,7 +321,18 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } ctx := req.Context() - if cn, ok := rw.(http.CloseNotifier); ok { + if ctx.Done() != nil { + // CloseNotifier predates context.Context, and has been + // entirely superseded by it. If the request contains + // a Context that carries a cancellation signal, don't + // bother spinning up a goroutine to watch the CloseNotify + // channel (if any). + // + // If the request Context has a nil Done channel (which + // means it is either context.Background, or a custom + // Context implementation with no cancellation signal), + // then consult the CloseNotifier if available. + } else if cn, ok := rw.(http.CloseNotifier); ok { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) defer cancel() @@ -248,7 +363,14 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { outreq.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate } - p.Director(outreq) + if (p.Director != nil) == (p.Rewrite != nil) { + p.getErrorHandler()(rw, req, errors.New("ReverseProxy must have exactly one of Director or Rewrite set")) + return + } + + if p.Director != nil { + p.Director(outreq) + } outreq.Close = false reqUpType := upgradeType(outreq.Header) @@ -256,20 +378,13 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { p.getErrorHandler()(rw, req, fmt.Errorf("client tried to switch to invalid protocol %q", reqUpType)) return } - removeConnectionHeaders(outreq.Header) - - // Remove hop-by-hop headers to the backend. Especially - // important is "Connection" because we want a persistent - // connection, regardless of what the client sent to us. - for _, h := range hopHeaders { - outreq.Header.Del(h) - } + removeHopByHopHeaders(outreq.Header) // Issue 21096: tell backend applications that care about trailer support // that we support trailers. (We do, but we don't go out of our way to // advertise that unless the incoming client request thought it was worth // mentioning.) Note that we look at req.Header, not outreq.Header, since - // the latter has passed through removeConnectionHeaders. + // the latter has passed through removeHopByHopHeaders. if httpguts.HeaderValuesContainsToken(req.Header["Te"], "trailers") { outreq.Header.Set("Te", "trailers") } @@ -281,20 +396,59 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { outreq.Header.Set("Upgrade", reqUpType) } - if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { - // If we aren't the first proxy retain prior - // X-Forwarded-For information as a comma+space - // separated list and fold multiple headers into one. - prior, ok := outreq.Header["X-Forwarded-For"] - omit := ok && prior == nil // Issue 38079: nil now means don't populate the header - if len(prior) > 0 { - clientIP = strings.Join(prior, ", ") + ", " + clientIP + if p.Rewrite != nil { + // Strip client-provided forwarding headers. + // The Rewrite func may use SetXForwarded to set new values + // for these or copy the previous values from the inbound request. + outreq.Header.Del("Forwarded") + outreq.Header.Del("X-Forwarded-For") + outreq.Header.Del("X-Forwarded-Host") + outreq.Header.Del("X-Forwarded-Proto") + + pr := &ProxyRequest{ + In: req, + Out: outreq, + } + p.Rewrite(pr) + outreq = pr.Out + } else { + if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { + // If we aren't the first proxy retain prior + // X-Forwarded-For information as a comma+space + // separated list and fold multiple headers into one. + prior, ok := outreq.Header["X-Forwarded-For"] + omit := ok && prior == nil // Issue 38079: nil now means don't populate the header + if len(prior) > 0 { + clientIP = strings.Join(prior, ", ") + ", " + clientIP + } + if !omit { + outreq.Header.Set("X-Forwarded-For", clientIP) + } } - if !omit { - outreq.Header.Set("X-Forwarded-For", clientIP) + if prior, ok := outreq.Header["X-Forwarded-Host"]; !(ok && prior == nil) { + outreq.Header.Set("X-Forwarded-Host", req.Host) + } + if prior, ok := outreq.Header["X-Forwarded-Proto"]; !(ok && prior == nil) { + if req.TLS == nil { + outreq.Header.Set("X-Forwarded-Proto", "http") + } else { + outreq.Header.Set("X-Forwarded-Proto", "https") + } } } + if _, ok := outreq.Header["User-Agent"]; !ok { + // If the outbound request doesn't have a User-Agent header set, + // don't send the default Go HTTP client User-Agent. + outreq.Header.Set("User-Agent", "") + } + + if _, ok := outreq.Header["User-Agent"]; !ok { + // If the outbound request doesn't have a User-Agent header set, + // don't send the default Go HTTP client User-Agent. + outreq.Header.Set("User-Agent", "") + } + res, err := transport.RoundTrip(outreq) if err != nil { p.getErrorHandler()(rw, outreq, err) @@ -310,11 +464,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { return } - removeConnectionHeaders(res.Header) - - for _, h := range hopHeaders { - res.Header.Del(h) - } + removeHopByHopHeaders(res.Header) if !p.modifyResponse(rw, res, outreq) { return @@ -393,9 +543,9 @@ func shouldPanicOnCopyError(req *http.Request) bool { return false } -// removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h. -// See RFC 7230, section 6.1 -func removeConnectionHeaders(h http.Header) { +// removeHopByHopHeaders removes hop-by-hop headers. +func removeHopByHopHeaders(h http.Header) { + // RFC 7230, section 6.1: Remove headers listed in the "Connection" header. for _, f := range h["Connection"] { for _, sf := range strings.Split(f, ",") { if sf = textproto.TrimString(sf); sf != "" { @@ -403,6 +553,12 @@ func removeConnectionHeaders(h http.Header) { } } } + // RFC 2616, section 13.5.1: Remove a set of known hop-by-hop headers. + // This behavior is superseded by the RFC 7230 Connection header, but + // preserve it for backwards compatibility. + for _, f := range hopHeaders { + h.Del(f) + } } // flushInterval returns the p.FlushInterval value, conditionally @@ -412,7 +568,7 @@ func (p *ReverseProxy) flushInterval(res *http.Response) time.Duration { // For Server-Sent Events responses, flush immediately. // The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream - if resCT == "text/event-stream" { + if baseCT, _, _ := mime.ParseMediaType(resCT); baseCT == "text/event-stream" { return -1 // negative means immediately } @@ -483,7 +639,7 @@ func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int } } -func (p *ReverseProxy) logf(format string, args ...interface{}) { +func (p *ReverseProxy) logf(format string, args ...any) { if p.ErrorLog != nil { p.ErrorLog.Printf(format, args...) } else { @@ -610,7 +766,6 @@ func (p *ReverseProxy) handleUpgradeResponse(rw http.ResponseWriter, req *http.R go spc.copyToBackend(errc) go spc.copyFromBackend(errc) <-errc - return } // switchProtocolCopier exists so goroutines proxying data back and diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index 4b6ad77a29466f..549bc67b879c46 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -50,6 +50,12 @@ func TestReverseProxy(t *testing.T) { if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } + if r.Header.Get("X-Forwarded-Host") == "" { + t.Errorf("didn't get X-Forwarded-Host header") + } + if r.Header.Get("X-Forwarded-Proto") == "" { + t.Errorf("didn't get X-Forwarded-Proto header") + } if c := r.Header.Get("Connection"); c != "" { t.Errorf("handler got Connection header value %q", c) } @@ -299,6 +305,7 @@ func TestXForwardedFor(t *testing.T) { const prevForwardedFor = "client ip" const backendResponse = "I am the backend" const backendStatus = 404 + const host = "some-name" backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") @@ -306,6 +313,12 @@ func TestXForwardedFor(t *testing.T) { if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { t.Errorf("X-Forwarded-For didn't contain prior data") } + if got, want := r.Header.Get("X-Forwarded-Host"), host; got != want { + t.Errorf("X-Forwarded-Host = %q, want %q", got, want) + } + if got, want := r.Header.Get("X-Forwarded-Proto"), "http"; got != want { + t.Errorf("X-Forwarded-Proto = %q, want %q", got, want) + } w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) @@ -319,7 +332,7 @@ func TestXForwardedFor(t *testing.T) { defer frontend.Close() getReq, _ := http.NewRequest("GET", frontend.URL, nil) - getReq.Host = "some-name" + getReq.Host = host getReq.Header.Set("Connection", "close") getReq.Header.Set("X-Forwarded-For", prevForwardedFor) getReq.Close = true @@ -336,11 +349,36 @@ func TestXForwardedFor(t *testing.T) { } } +func TestXForwardedProtoTLS(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("X-Forwarded-Proto"), "https"; got != want { + t.Errorf("X-Forwarded-Proto = %q, want %q", got, want) + } + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := NewSingleHostReverseProxy(backendURL) + frontend := httptest.NewTLSServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Host = "some-host" + _, err = frontend.Client().Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } +} + // Issue 38079: don't append to X-Forwarded-For if it's present but nil func TestXForwardedFor_Omit(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if v := r.Header.Get("X-Forwarded-For"); v != "" { - t.Errorf("got X-Forwarded-For header: %q", v) + for _, h := range []string{"X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto"} { + if v := r.Header.Get(h); v != "" { + t.Errorf("got %v header: %q", h, v) + } } w.Write([]byte("hi")) })) @@ -356,6 +394,8 @@ func TestXForwardedFor_Omit(t *testing.T) { oldDirector := proxyHandler.Director proxyHandler.Director = func(r *http.Request) { r.Header["X-Forwarded-For"] = nil + r.Header["X-Forwarded-Host"] = nil + r.Header["X-Forwarded-Proto"] = nil oldDirector(r) } @@ -369,6 +409,46 @@ func TestXForwardedFor_Omit(t *testing.T) { res.Body.Close() } +func TestReverseProxyRewriteStripsForwarded(t *testing.T) { + headers := []string{ + "Forwarded", + "X-Forwarded-For", + "X-Forwarded-Host", + "X-Forwarded-Proto", + } + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for _, h := range headers { + if v := r.Header.Get(h); v != "" { + t.Errorf("got %v header: %q", h, v) + } + } + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.SetURL(backendURL) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Host = "some-name" + getReq.Close = true + for _, h := range headers { + getReq.Header.Set(h, "x") + } + res, err := frontend.Client().Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + res.Body.Close() +} + var proxyQueryTests = []struct { baseSuffix string // suffix to add to backend URL reqSuffix string // suffix to add to frontend's request URL @@ -574,46 +654,38 @@ func TestNilBody(t *testing.T) { // Issue 15524 func TestUserAgentHeader(t *testing.T) { - const explicitUA = "explicit UA" + var gotUA string 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) - } + gotUA = r.Header.Get("User-Agent") })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } - proxyHandler := NewSingleHostReverseProxy(backendURL) + + proxyHandler := new(ReverseProxy) proxyHandler.ErrorLog = log.New(io.Discard, "", 0) // quiet for tests + proxyHandler.Director = func(req *http.Request) { + req.URL = backendURL + } frontend := httptest.NewServer(proxyHandler) defer frontend.Close() frontendClient := frontend.Client() - getReq, _ := http.NewRequest("GET", frontend.URL, nil) - getReq.Header.Set("User-Agent", explicitUA) - getReq.Close = true - res, err := frontendClient.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 = frontendClient.Do(getReq) - if err != nil { - t.Fatalf("Get: %v", err) + for _, sentUA := range []string{"explicit UA", ""} { + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + getReq.Header.Set("User-Agent", sentUA) + getReq.Close = true + res, err := frontendClient.Do(getReq) + if err != nil { + t.Fatalf("Get: %v", err) + } + res.Body.Close() + if got, want := gotUA, sentUA; got != want { + t.Errorf("got forwarded User-Agent %q, want %q", got, want) + } } - res.Body.Close() } type bufferPool struct { @@ -1029,13 +1101,16 @@ func TestClonesRequestHeaders(t *testing.T) { } rp.ServeHTTP(httptest.NewRecorder(), req) - if req.Header.Get("From-Director") == "1" { - t.Error("Director header mutation modified caller's request") - } - if req.Header.Get("X-Forwarded-For") != "" { - t.Error("X-Forward-For header mutation modified caller's request") + for _, h := range []string{ + "From-Director", + "X-Forwarded-For", + "X-Forwarded-Host", + "X-Forwarded-Proto", + } { + if req.Header.Get(h) != "" { + t.Errorf("%v header mutation modified caller's request", h) + } } - } type roundTripperFunc func(req *http.Request) (*http.Response, error) @@ -1048,7 +1123,7 @@ func TestModifyResponseClosesBody(t *testing.T) { req, _ := http.NewRequest("GET", "http://foo.tld/", nil) req.RemoteAddr = "1.2.3.4:56789" closeCheck := new(checkCloser) - logBuf := new(bytes.Buffer) + logBuf := new(strings.Builder) outErr := errors.New("ModifyResponse error") rp := &ReverseProxy{ Director: func(req *http.Request) {}, @@ -1194,6 +1269,26 @@ func TestSelectFlushInterval(t *testing.T) { p: &ReverseProxy{FlushInterval: 0}, want: -1, }, + { + name: "server-sent events with media-type parameters overrides non-zero", + res: &http.Response{ + Header: http.Header{ + "Content-Type": {"text/event-stream;charset=utf-8"}, + }, + }, + p: &ReverseProxy{FlushInterval: 123}, + want: -1, + }, + { + name: "server-sent events with media-type parameters overrides zero", + res: &http.Response{ + Header: http.Header{ + "Content-Type": {"text/event-stream;charset=utf-8"}, + }, + }, + p: &ReverseProxy{FlushInterval: 0}, + want: -1, + }, { name: "Content-Length: -1, overrides non-zero", res: &http.Response{ @@ -1468,6 +1563,40 @@ func TestUnannouncedTrailer(t *testing.T) { } +func TestSetURL(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(r.Host)) + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.SetURL(backendURL) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + frontendClient := frontend.Client() + + res, err := frontendClient.Get(frontend.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatalf("Reading body: %v", err) + } + + if got, want := string(body), backendURL.Host; got != want { + t.Errorf("backend got Host %q, want %q", got, want) + } +} + func TestSingleJoinSlash(t *testing.T) { tests := []struct { slasha string @@ -1517,3 +1646,28 @@ func TestJoinURLPath(t *testing.T) { } } } + +func TestReverseProxyRewriteReplacesOut(t *testing.T) { + const content = "response_content" + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(content)) + })) + defer backend.Close() + proxyHandler := &ReverseProxy{ + Rewrite: func(r *ProxyRequest) { + r.Out, _ = http.NewRequest("GET", backend.URL, nil) + }, + } + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + res, err := frontend.Client().Get(frontend.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + defer res.Body.Close() + body, _ := io.ReadAll(res.Body) + if got, want := string(body), content; got != want { + t.Errorf("got response %q, want %q", got, want) + } +} diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go index f06e5725f34779..5a174415dc4014 100644 --- a/src/net/http/internal/chunked.go +++ b/src/net/http/internal/chunked.go @@ -81,6 +81,11 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { cr.err = errors.New("malformed chunked encoding") break } + } else { + if cr.err == io.EOF { + cr.err = io.ErrUnexpectedEOF + } + break } cr.checkEnd = false } @@ -109,6 +114,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { // bytes to verify they are "\r\n". if cr.n == 0 && cr.err == nil { cr.checkEnd = true + } else if cr.err == io.EOF { + cr.err = io.ErrUnexpectedEOF } } return n, cr.err @@ -152,21 +159,21 @@ func isASCIISpace(b byte) bool { return b == ' ' || b == '\t' || b == '\n' || b == '\r' } +var semi = []byte(";") + // removeChunkExtension removes any chunk-extension from p. // For example, -// "0" => "0" -// "0;token" => "0" -// "0;token=val" => "0" -// `0;token="quoted string"` => "0" +// +// "0" => "0" +// "0;token" => "0" +// "0;token=val" => "0" +// `0;token="quoted string"` => "0" func removeChunkExtension(p []byte) ([]byte, error) { - semi := bytes.IndexByte(p, ';') - if semi == -1 { - return p, nil - } + p, _, _ = bytes.Cut(p, semi) // TODO: care about exact syntax of chunk extensions? We're // ignoring and stripping them anyway. For now just never // return an error. - return p[:semi], nil + return p, nil } // NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go index 08152ed1e24d67..5e29a786dd61e4 100644 --- a/src/net/http/internal/chunked_test.go +++ b/src/net/http/internal/chunked_test.go @@ -11,6 +11,7 @@ import ( "io" "strings" "testing" + "testing/iotest" ) func TestChunk(t *testing.T) { @@ -211,3 +212,30 @@ func TestChunkReadPartial(t *testing.T) { } } + +// Issue 48861: ChunkedReader should report incomplete chunks +func TestIncompleteChunk(t *testing.T) { + const valid = "4\r\nabcd\r\n" + "5\r\nabc\r\n\r\n" + "0\r\n" + + for i := 0; i < len(valid); i++ { + incomplete := valid[:i] + r := NewChunkedReader(strings.NewReader(incomplete)) + if _, err := io.ReadAll(r); err != io.ErrUnexpectedEOF { + t.Errorf("expected io.ErrUnexpectedEOF for %q, got %v", incomplete, err) + } + } + + r := NewChunkedReader(strings.NewReader(valid)) + if _, err := io.ReadAll(r); err != nil { + t.Errorf("unexpected error for %q: %v", valid, err) + } +} + +func TestChunkEndReadError(t *testing.T) { + readErr := fmt.Errorf("chunk end read error") + + r := NewChunkedReader(io.MultiReader(strings.NewReader("4\r\nabcd"), iotest.ErrReader(readErr))) + if _, err := io.ReadAll(r); err != readErr { + t.Errorf("expected %v, got %v", readErr, err) + } +} diff --git a/src/net/http/internal/testcert/testcert.go b/src/net/http/internal/testcert/testcert.go index 5f94704ef590d6..d510e791d617cb 100644 --- a/src/net/http/internal/testcert/testcert.go +++ b/src/net/http/internal/testcert/testcert.go @@ -10,37 +10,56 @@ import "strings" // LocalhostCert is a PEM-encoded TLS cert with SAN IPs // "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT. // generated from src/crypto/tls: -// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h +// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var LocalhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS +MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw -MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB -iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4 -iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul -rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO -BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw -AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA -AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9 -tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs -h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM -fblo6RBxUQ== +MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r +bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U +aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P +YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk +POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu +h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE +AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv +bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI +5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv +cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 ++tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B +grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK +5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ +WkBKOclmOV2xlTVuPw== -----END CERTIFICATE-----`) // LocalhostKey is the private key for LocalhostCert. var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY----- -MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9 -SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB -l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB -AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet -3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb -uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H -qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp -jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY -fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U -fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU -y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX -qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo -f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA== +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi +4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS +gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW +URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX +AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy +VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK +x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk +lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL +dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89 +EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq +XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki +6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O +3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s +uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ +Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ +w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo ++bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP +OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA +brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv +m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y +LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN +/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN +s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ +Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0 +xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/ +ZboOWVe3icTy64BT3OQhmg== -----END RSA TESTING KEY-----`)) func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") } diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go index 6564627998f53e..27872b4e7a40f1 100644 --- a/src/net/http/main_test.go +++ b/src/net/http/main_test.go @@ -31,11 +31,8 @@ func interestingGoroutines() (gs []string) { buf := make([]byte, 2<<20) buf = buf[:runtime.Stack(buf, true)] for _, g := range strings.Split(string(buf), "\n\n") { - sl := strings.SplitN(g, "\n", 2) - if len(sl) != 2 { - continue - } - stack := strings.TrimSpace(sl[1]) + _, stack, _ := strings.Cut(g, "\n") + stack = strings.TrimSpace(stack) if stack == "" || strings.Contains(stack, "testing.(*M).before.func1") || strings.Contains(stack, "os/signal.signal_recv") || @@ -46,7 +43,7 @@ func interestingGoroutines() (gs []string) { // These only show up with GOTRACEBACK=2; Issue 5005 (comment 28) strings.Contains(stack, "runtime.goexit") || strings.Contains(stack, "created by runtime.gc") || - strings.Contains(stack, "net/http_test.interestingGoroutines") || + strings.Contains(stack, "interestingGoroutines") || strings.Contains(stack, "runtime.MHeap_Scavenger") { continue } diff --git a/src/net/http/omithttp2.go b/src/net/http/omithttp2.go index 79599d006aaf94..3316f55c6dcf5c 100644 --- a/src/net/http/omithttp2.go +++ b/src/net/http/omithttp2.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build nethttpomithttp2 -// +build nethttpomithttp2 package http @@ -27,7 +26,7 @@ const http2NextProtoTLS = "h2" type http2Transport struct { MaxHeaderListSize uint32 - ConnPool interface{} + ConnPool any } func (*http2Transport) RoundTrip(*Request) (*Response, error) { panic(noHTTP2) } @@ -57,9 +56,9 @@ type http2Server struct { NewWriteScheduler func() http2WriteScheduler } -type http2WriteScheduler interface{} +type http2WriteScheduler any -func http2NewPriorityWriteScheduler(interface{}) http2WriteScheduler { panic(noHTTP2) } +func http2NewPriorityWriteScheduler(any) http2WriteScheduler { panic(noHTTP2) } func http2ConfigureServer(s *Server, conf *http2Server) error { panic(noHTTP2) } diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go index 888ea35c9a6c52..bba522768f6684 100644 --- a/src/net/http/pprof/pprof.go +++ b/src/net/http/pprof/pprof.go @@ -10,20 +10,26 @@ // The handled paths all begin with /debug/pprof/. // // To use pprof, link this package into your program: +// // import _ "net/http/pprof" // // If your application is not already running an http server, you // need to start one. Add "net/http" and "log" to your imports and // the following code to your main function: // -// go func() { -// log.Println(http.ListenAndServe("localhost:6060", nil)) -// }() +// go func() { +// log.Println(http.ListenAndServe("localhost:6060", nil)) +// }() // +// By default, all the profiles listed in [runtime/pprof.Profile] are +// available (via [Handler]), in addition to the [Cmdline], [Profile], [Symbol], +// and [Trace] profiles defined in this package. // If you are not using DefaultServeMux, you will have to register handlers // with the mux you are using. // -// Then use the pprof tool to look at the heap profile: +// # Usage examples +// +// Use the pprof tool to look at the heap profile: // // go tool pprof http://localhost:6060/debug/pprof/heap // @@ -44,7 +50,7 @@ // The package also exports a handler that serves execution trace data // for the "go tool trace" command. To collect a 5-second execution trace: // -// wget -O trace.out http://localhost:6060/debug/pprof/trace?seconds=5 +// curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5 // go tool trace trace.out // // To view all available profiles, open http://localhost:6060/debug/pprof/ @@ -53,7 +59,6 @@ // For a study of the facility in action, visit // // https://blog.golang.org/2011/06/profiling-go-programs.html -// package pprof import ( @@ -222,6 +227,7 @@ func Symbol(w http.ResponseWriter, r *http.Request) { } // Handler returns an HTTP handler that serves the named profile. +// Available profiles can be found in [runtime/pprof.Profile]. func Handler(name string) http.Handler { return handler(name) } @@ -345,7 +351,7 @@ var profileDescriptions = map[string]string{ "allocs": "A sampling of all past memory allocations", "block": "Stack traces that led to blocking on synchronization primitives", "cmdline": "The command line invocation of the current program", - "goroutine": "Stack traces of all current goroutines", + "goroutine": "Stack traces of all current goroutines. Use debug=2 as a query parameter to export in the same format as an unrecovered panic.", "heap": "A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.", "mutex": "Stack traces of holders of contended mutexes", "profile": "CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.", @@ -417,7 +423,9 @@ func indexTmplExecute(w io.Writer, profiles []profileEntry) error { -/debug/pprof/
        +/debug/pprof/ +
        +

        Set debug=1 as a query parameter to export in legacy text format


        Types of profiles available: diff --git a/src/net/http/pprof/pprof_test.go b/src/net/http/pprof/pprof_test.go index 84757e401aaa0c..f82ad45bf6fecc 100644 --- a/src/net/http/pprof/pprof_test.go +++ b/src/net/http/pprof/pprof_test.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "internal/profile" + "internal/testenv" "io" "net/http" "net/http/httptest" @@ -152,6 +153,10 @@ func mutexHog(duration time.Duration, hogger func(mu1, mu2 *sync.Mutex, start ti } func TestDeltaProfile(t *testing.T) { + if strings.HasPrefix(runtime.GOARCH, "arm") { + testenv.SkipFlaky(t, 50218) + } + rate := runtime.SetMutexProfileFraction(1) defer func() { runtime.SetMutexProfileFraction(rate) diff --git a/src/net/http/readrequest_test.go b/src/net/http/readrequest_test.go index 1950f4907ad703..a6a12a0ddde674 100644 --- a/src/net/http/readrequest_test.go +++ b/src/net/http/readrequest_test.go @@ -416,7 +416,7 @@ func TestReadRequest(t *testing.T) { req.Body = nil testName := fmt.Sprintf("Test %d (%q)", i, tt.Raw) diff(t, testName, req, tt.Req) - var bout bytes.Buffer + var bout strings.Builder if rbody != nil { _, err := io.Copy(&bout, rbody) if err != nil { diff --git a/src/net/http/request.go b/src/net/http/request.go index 09cb0c7f564f74..924ca1b390bf08 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -317,14 +317,14 @@ type Request struct { Response *Response // ctx is either the client or server context. It should only - // be modified via copying the whole Request using WithContext. + // be modified via copying the whole Request using Clone or WithContext. // It is unexported to prevent people from using Context wrong // and mutating the contexts held by callers of the same request. ctx context.Context } // Context returns the request's context. To change the context, use -// WithContext. +// Clone or WithContext. // // The returned context is always non-nil; it defaults to the // background context. @@ -349,9 +349,7 @@ func (r *Request) Context() context.Context { // sending the request, and reading the response headers and body. // // To create a new request with a context, use NewRequestWithContext. -// To change the context of a request, such as an incoming request you -// want to modify before sending back out, use Request.Clone. Between -// those two uses, it's rare to need WithContext. +// To make a deep copy of a request with a new context, use Request.Clone. func (r *Request) WithContext(ctx context.Context) *Request { if ctx == nil { panic("nil context") @@ -359,7 +357,6 @@ func (r *Request) WithContext(ctx context.Context) *Request { r2 := new(Request) *r2 = *r r2.ctx = ctx - r2.URL = cloneURL(r.URL) // legacy behavior; TODO: try to remove. Issue 23544 return r2 } @@ -419,6 +416,9 @@ var ErrNoCookie = errors.New("http: named cookie not present") // If multiple cookies match the given name, only one cookie will // be returned. func (r *Request) Cookie(name string) (*Cookie, error) { + if name == "" { + return nil, ErrNoCookie + } for _, c := range readCookies(r.Header, name) { return c, nil } @@ -480,6 +480,9 @@ func (r *Request) multipartReader(allowMixed bool) (*multipart.Reader, error) { if v == "" { return nil, ErrNotMultipart } + if r.Body == nil { + return nil, errors.New("missing form body") + } d, params, err := mime.ParseMediaType(v) if err != nil || !(d == "multipart/form-data" || allowMixed && d == "multipart/mixed") { return nil, ErrNotMultipart @@ -513,6 +516,7 @@ const defaultUserAgent = "Go-http-client/1.1" // Write writes an HTTP/1.1 request, which is the header and body, in wire format. // This method consults the following fields of the request: +// // Host // URL // Method (defaults to "GET") @@ -736,9 +740,11 @@ func idnaASCII(v string) (string, error) { // into Punycode form, if necessary. // // Ideally we'd clean the Host header according to the spec: -// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]") -// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host) -// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host) +// +// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]") +// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host) +// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host) +// // But practically, what we are trying to avoid is the situation in // issue 11206, where a malformed Host header used in the proxy context // would create a bad request. So it is enough to just truncate at the @@ -779,11 +785,10 @@ func removeZone(host string) string { return host[:j] + host[i:] } -// ParseHTTPVersion parses an HTTP version string. +// ParseHTTPVersion parses an HTTP version string according to RFC 7230, section 2.6. // "HTTP/1.0" returns (1, 0, true). Note that strings without // a minor version, such as "HTTP/2", are not valid. func ParseHTTPVersion(vers string) (major, minor int, ok bool) { - const Big = 1000000 // arbitrary upper bound switch vers { case "HTTP/1.1": return 1, 1, true @@ -793,19 +798,21 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) { if !strings.HasPrefix(vers, "HTTP/") { return 0, 0, false } - dot := strings.Index(vers, ".") - if dot < 0 { + if len(vers) != len("HTTP/X.Y") { return 0, 0, false } - major, err := strconv.Atoi(vers[5:dot]) - if err != nil || major < 0 || major > Big { + if vers[6] != '.' { return 0, 0, false } - minor, err = strconv.Atoi(vers[dot+1:]) - if err != nil || minor < 0 || minor > Big { + maj, err := strconv.ParseUint(vers[5:6], 10, 0) + if err != nil { + return 0, 0, false + } + min, err := strconv.ParseUint(vers[7:8], 10, 0) + if err != nil { return 0, 0, false } - return major, minor, true + return int(maj), int(min), true } func validMethod(method string) bool { @@ -939,7 +946,7 @@ func NewRequestWithContext(ctx context.Context, method, url string, body io.Read func (r *Request) BasicAuth() (username, password string, ok bool) { auth := r.Header.Get("Authorization") if auth == "" { - return + return "", "", false } return parseBasicAuth(auth) } @@ -950,42 +957,43 @@ func parseBasicAuth(auth string) (username, password string, ok bool) { const prefix = "Basic " // Case insensitive prefix match. See Issue 22736. if len(auth) < len(prefix) || !ascii.EqualFold(auth[:len(prefix)], prefix) { - return + return "", "", false } c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) if err != nil { - return + return "", "", false } cs := string(c) - s := strings.IndexByte(cs, ':') - if s < 0 { - return + username, password, ok = strings.Cut(cs, ":") + if !ok { + return "", "", false } - return cs[:s], cs[s+1:], true + return username, password, true } // SetBasicAuth sets the request's Authorization header to use HTTP // Basic Authentication with the provided username and password. // // With HTTP Basic Authentication the provided username and password -// are not encrypted. +// are not encrypted. It should generally only be used in an HTTPS +// request. // -// Some protocols may impose additional requirements on pre-escaping the -// username and password. For instance, when used with OAuth2, both arguments -// must be URL encoded first with url.QueryEscape. +// The username may not contain a colon. Some protocols may impose +// additional requirements on pre-escaping the username and +// password. For instance, when used with OAuth2, both arguments must +// be URL encoded first with url.QueryEscape. func (r *Request) SetBasicAuth(username, password string) { r.Header.Set("Authorization", "Basic "+basicAuth(username, password)) } // parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. func parseRequestLine(line string) (method, requestURI, proto string, ok bool) { - s1 := strings.Index(line, " ") - s2 := strings.Index(line[s1+1:], " ") - if s1 < 0 || s2 < 0 { - return + method, rest, ok1 := strings.Cut(line, " ") + requestURI, proto, ok2 := strings.Cut(rest, " ") + if !ok1 || !ok2 { + return "", "", "", false } - s2 += s1 + 1 - return line[:s1], line[s1+1 : s2], line[s2+1:], true + return method, requestURI, proto, true } var textprotoReaderPool sync.Pool @@ -1119,21 +1127,34 @@ func readRequest(b *bufio.Reader) (req *Request, err error) { // MaxBytesReader is similar to io.LimitReader but is intended for // limiting the size of incoming request bodies. In contrast to // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a -// non-EOF error for a Read beyond the limit, and closes the -// underlying reader when its Close method is called. +// non-nil error of type *MaxBytesError for a Read beyond the limit, +// and closes the underlying reader when its Close method is called. // // MaxBytesReader prevents clients from accidentally or maliciously -// sending a large request and wasting server resources. +// sending a large request and wasting server resources. If possible, +// it tells the ResponseWriter to close the connection after the limit +// has been reached. func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { if n < 0 { // Treat negative limits as equivalent to 0. n = 0 } - return &maxBytesReader{w: w, r: r, n: n} + return &maxBytesReader{w: w, r: r, i: n, n: n} +} + +// MaxBytesError is returned by MaxBytesReader when its read limit is exceeded. +type MaxBytesError struct { + Limit int64 +} + +func (e *MaxBytesError) Error() string { + // Due to Hyrum's law, this text cannot be changed. + return "http: request body too large" } type maxBytesReader struct { w ResponseWriter r io.ReadCloser // underlying reader + i int64 // max bytes initially, for MaxBytesError n int64 // max bytes remaining err error // sticky error } @@ -1148,7 +1169,8 @@ func (l *maxBytesReader) Read(p []byte) (n int, err error) { // 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 { + // 0 < len(p) < 2^63 + if int64(len(p))-1 > l.n { p = p[:l.n+1] } n, err = l.r.Read(p) @@ -1175,7 +1197,7 @@ func (l *maxBytesReader) Read(p []byte) (n int, err error) { if res, ok := l.w.(requestTooLarger); ok { res.requestTooLarge() } - l.err = errors.New("http: request body too large") + l.err = &MaxBytesError{l.i} return n, l.err } diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go index 4e0c4ba207f8be..2f348284de977d 100644 --- a/src/net/http/request_test.go +++ b/src/net/http/request_test.go @@ -639,10 +639,10 @@ var parseHTTPVersionTests = []struct { major, minor int ok bool }{ + {"HTTP/0.0", 0, 0, true}, {"HTTP/0.9", 0, 9, true}, {"HTTP/1.0", 1, 0, true}, {"HTTP/1.1", 1, 1, true}, - {"HTTP/3.14", 3, 14, true}, {"HTTP", 0, 0, false}, {"HTTP/one.one", 0, 0, false}, @@ -651,6 +651,12 @@ var parseHTTPVersionTests = []struct { {"HTTP/0,-1", 0, 0, false}, {"HTTP/", 0, 0, false}, {"HTTP/1,1", 0, 0, false}, + {"HTTP/+1.1", 0, 0, false}, + {"HTTP/1.+1", 0, 0, false}, + {"HTTP/0000000001.1", 0, 0, false}, + {"HTTP/1.0000000001", 0, 0, false}, + {"HTTP/3.14", 0, 0, false}, + {"HTTP/12.3", 0, 0, false}, } func TestParseHTTPVersion(t *testing.T) { @@ -809,7 +815,7 @@ func TestStarRequest(t *testing.T) { clientReq := *req clientReq.Body = nil - var out bytes.Buffer + var out strings.Builder if err := clientReq.Write(&out); err != nil { t.Fatal(err) } @@ -817,7 +823,7 @@ func TestStarRequest(t *testing.T) { if strings.Contains(out.String(), "chunked") { t.Error("wrote chunked request; want no body") } - back, err := ReadRequest(bufio.NewReader(bytes.NewReader(out.Bytes()))) + back, err := ReadRequest(bufio.NewReader(strings.NewReader(out.String()))) if err != nil { t.Fatal(err) } @@ -829,7 +835,7 @@ func TestStarRequest(t *testing.T) { t.Errorf("Original request doesn't match Request read back.") t.Logf("Original: %#v", req) t.Logf("Original.URL: %#v", req.URL) - t.Logf("Wrote: %s", out.Bytes()) + t.Logf("Wrote: %s", out.String()) t.Logf("Read back (doesn't match Original): %#v", back) } } @@ -976,6 +982,12 @@ func TestMaxBytesReaderDifferentLimits(t *testing.T) { wantN: len(testStr), wantErr: false, }, + 10: { /* Issue 54408 */ + limit: int64(1<<63 - 1), + lenP: len(testStr), + wantN: len(testStr), + wantErr: false, + }, } for i, tt := range tests { rc := MaxBytesReader(nil, io.NopCloser(strings.NewReader(testStr)), tt.limit) @@ -992,23 +1004,15 @@ func TestMaxBytesReaderDifferentLimits(t *testing.T) { } } -func TestWithContextDeepCopiesURL(t *testing.T) { +func TestWithContextNilURL(t *testing.T) { req, err := NewRequest("POST", "https://golang.org/", nil) if err != nil { t.Fatal(err) } - reqCopy := req.WithContext(context.Background()) - reqCopy.URL.Scheme = "http" - - firstURL, secondURL := req.URL.String(), reqCopy.URL.String() - if firstURL == secondURL { - t.Errorf("unexpected change to original request's URL") - } - - // And also check we don't crash on nil (Issue 20601) + // Issue 20601 req.URL = nil - reqCopy = req.WithContext(context.Background()) + reqCopy := req.WithContext(context.Background()) if reqCopy.URL != nil { t.Error("expected nil URL in cloned request") } @@ -1168,7 +1172,7 @@ func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectCo if fh.Filename != expectFilename { t.Errorf("filename = %q, want %q", fh.Filename, expectFilename) } - var b bytes.Buffer + var b strings.Builder _, err = io.Copy(&b, f) if err != nil { t.Fatal("copying contents:", err) @@ -1179,6 +1183,47 @@ func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectCo return f } +// Issue 53181: verify Request.Cookie return the correct Cookie. +// Return ErrNoCookie instead of the first cookie when name is "". +func TestRequestCookie(t *testing.T) { + for _, tt := range []struct { + name string + value string + expectedErr error + }{ + { + name: "foo", + value: "bar", + expectedErr: nil, + }, + { + name: "", + expectedErr: ErrNoCookie, + }, + } { + req, err := NewRequest("GET", "http://example.com/", nil) + if err != nil { + t.Fatal(err) + } + req.AddCookie(&Cookie{Name: tt.name, Value: tt.value}) + c, err := req.Cookie(tt.name) + if err != tt.expectedErr { + t.Errorf("got %v, want %v", err, tt.expectedErr) + } + + // skip if error occured. + if err != nil { + continue + } + if c.Value != tt.value { + t.Errorf("got %v, want %v", c.Value, tt.value) + } + if c.Name != tt.name { + t.Errorf("got %s, want %v", tt.name, c.Name) + } + } +} + const ( fileaContents = "This is a test file." filebContents = "Another test file." diff --git a/src/net/http/requestwrite_test.go b/src/net/http/requestwrite_test.go index 1157bdfff940a9..380ae9dec3244d 100644 --- a/src/net/http/requestwrite_test.go +++ b/src/net/http/requestwrite_test.go @@ -20,7 +20,7 @@ import ( type reqWriteTest struct { Req Request - Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body + Body any // optional []byte or func() io.ReadCloser to populate Req.Body // Any of these three may be empty to skip that test. WantWrite string // Request.Write @@ -629,7 +629,7 @@ func TestRequestWrite(t *testing.T) { tt.Req.Header = make(Header) } - var braw bytes.Buffer + var braw strings.Builder err := tt.Req.Write(&braw) if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.WantError); g != e { t.Errorf("writing #%d, err = %q, want %q", i, g, e) @@ -649,7 +649,7 @@ func TestRequestWrite(t *testing.T) { if tt.WantProxy != "" { setBody() - var praw bytes.Buffer + var praw strings.Builder err = tt.Req.WriteProxy(&praw) if err != nil { t.Errorf("WriteProxy #%d: %s", i, err) @@ -815,7 +815,7 @@ func TestRequestWriteClosesBody(t *testing.T) { if err != nil { t.Fatal(err) } - buf := new(bytes.Buffer) + buf := new(strings.Builder) if err := req.Write(buf); err != nil { t.Error(err) } diff --git a/src/net/http/response.go b/src/net/http/response.go index b8985da3c80fc4..755c6965575fd2 100644 --- a/src/net/http/response.go +++ b/src/net/http/response.go @@ -165,16 +165,14 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { } return nil, err } - if i := strings.IndexByte(line, ' '); i == -1 { + proto, status, ok := strings.Cut(line, " ") + if !ok { return nil, badStringError("malformed HTTP response", line) - } else { - resp.Proto = line[:i] - resp.Status = strings.TrimLeft(line[i+1:], " ") - } - statusCode := resp.Status - if i := strings.IndexByte(resp.Status, ' '); i != -1 { - statusCode = resp.Status[:i] } + resp.Proto = proto + resp.Status = strings.TrimLeft(status, " ") + + statusCode, _, _ := strings.Cut(resp.Status, " ") if len(statusCode) != 3 { return nil, badStringError("malformed HTTP status code", statusCode) } @@ -182,7 +180,6 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { if err != nil || resp.StatusCode < 0 { return nil, badStringError("malformed HTTP status code", statusCode) } - var ok bool if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok { return nil, badStringError("malformed HTTP version", resp.Proto) } @@ -208,8 +205,11 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { } // RFC 7234, section 5.4: Should treat +// // Pragma: no-cache +// // like +// // Cache-Control: no-cache func fixPragmaCacheControl(header Header) { if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { @@ -231,24 +231,23 @@ func (r *Response) ProtoAtLeast(major, minor int) bool { // // This method consults the following fields of the response r: // -// StatusCode -// ProtoMajor -// ProtoMinor -// Request.Method -// TransferEncoding -// Trailer -// Body -// ContentLength -// Header, values for non-canonical keys will have unpredictable behavior +// StatusCode +// ProtoMajor +// ProtoMinor +// Request.Method +// TransferEncoding +// Trailer +// Body +// ContentLength +// Header, values for non-canonical keys will have unpredictable behavior // // The Response Body is closed after it is sent. func (r *Response) Write(w io.Writer) error { // Status line text := r.Status if text == "" { - var ok bool - text, ok = statusText[r.StatusCode] - if !ok { + text = StatusText(r.StatusCode) + if text == "" { text = "status code " + strconv.Itoa(r.StatusCode) } } else { diff --git a/src/net/http/response_test.go b/src/net/http/response_test.go index 8eef65474e4c85..19fb48f23ce194 100644 --- a/src/net/http/response_test.go +++ b/src/net/http/response_test.go @@ -596,7 +596,7 @@ func TestReadResponse(t *testing.T) { rbody := resp.Body resp.Body = nil diff(t, fmt.Sprintf("#%d Response", i), resp, &tt.Resp) - var bout bytes.Buffer + var bout strings.Builder if rbody != nil { _, err = io.Copy(&bout, rbody) if err != nil { @@ -646,8 +646,8 @@ type readerAndCloser struct { func TestReadResponseCloseInMiddle(t *testing.T) { t.Parallel() for _, test := range readResponseCloseInMiddleTests { - fatalf := func(format string, args ...interface{}) { - args = append([]interface{}{test.chunked, test.compressed}, args...) + fatalf := func(format string, args ...any) { + args = append([]any{test.chunked, test.compressed}, args...) t.Fatalf("on test chunked=%v, compressed=%v: "+format, args...) } checkErr := func(err error, msg string) { @@ -732,7 +732,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) { } } -func diff(t *testing.T, prefix string, have, want interface{}) { +func diff(t *testing.T, prefix string, have, want any) { t.Helper() hv := reflect.ValueOf(have).Elem() wv := reflect.ValueOf(want).Elem() @@ -809,7 +809,7 @@ func TestResponseStatusStutter(t *testing.T) { ProtoMajor: 1, ProtoMinor: 3, } - var buf bytes.Buffer + var buf strings.Builder r.Write(&buf) if strings.Contains(buf.String(), "123 123") { t.Errorf("stutter in status: %s", buf.String()) @@ -829,7 +829,7 @@ func TestResponseContentLengthShortBody(t *testing.T) { if res.ContentLength != 123 { t.Fatalf("Content-Length = %d; want 123", res.ContentLength) } - var buf bytes.Buffer + var buf strings.Builder n, err := io.Copy(&buf, res.Body) if n != int64(len(shortBody)) { t.Errorf("Copied %d bytes; want %d, len(%q)", n, len(shortBody), shortBody) @@ -849,10 +849,10 @@ func TestReadResponseErrors(t *testing.T) { type testCase struct { name string // optional, defaults to in in string - wantErr interface{} // nil, err value, or string substring + wantErr any // nil, err value, or string substring } - status := func(s string, wantErr interface{}) testCase { + status := func(s string, wantErr any) testCase { if wantErr == true { wantErr = "malformed HTTP status code" } @@ -863,7 +863,7 @@ func TestReadResponseErrors(t *testing.T) { } } - version := func(s string, wantErr interface{}) testCase { + version := func(s string, wantErr any) testCase { if wantErr == true { wantErr = "malformed HTTP version" } @@ -874,7 +874,7 @@ func TestReadResponseErrors(t *testing.T) { } } - contentLength := func(status, body string, wantErr interface{}) testCase { + contentLength := func(status, body string, wantErr any) testCase { return testCase{ name: fmt.Sprintf("status %q %q", status, body), in: fmt.Sprintf("HTTP/1.1 %s\r\n%s", status, body), @@ -947,7 +947,7 @@ func TestReadResponseErrors(t *testing.T) { // wantErr can be nil, an error value to match exactly, or type string to // match a substring. -func matchErr(err error, wantErr interface{}) error { +func matchErr(err error, wantErr any) error { if err == nil { if wantErr == nil { return nil @@ -972,19 +972,6 @@ func matchErr(err error, wantErr interface{}) error { return fmt.Errorf("%v; want %v", err, wantErr) } -func TestNeedsSniff(t *testing.T) { - // needsSniff returns true with an empty response. - r := &response{} - if got, want := r.needsSniff(), true; got != want { - t.Errorf("needsSniff = %t; want %t", got, want) - } - // needsSniff returns false when Content-Type = nil. - r.handlerHeader = Header{"Content-Type": nil} - if got, want := r.needsSniff(), false; got != want { - t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want) - } -} - // A response should only write out single Connection: close header. Tests #19499. func TestResponseWritesOnlySingleConnectionClose(t *testing.T) { const connectionCloseHeader = "Connection: close" @@ -1002,7 +989,7 @@ func TestResponseWritesOnlySingleConnectionClose(t *testing.T) { t.Fatalf("ReadResponse failed %v", err) } - var buf2 bytes.Buffer + var buf2 strings.Builder if err = res.Write(&buf2); err != nil { t.Fatalf("Write failed %v", err) } diff --git a/src/net/http/responsewrite_test.go b/src/net/http/responsewrite_test.go index 1cc87b942ed5b9..226ad7225b2d71 100644 --- a/src/net/http/responsewrite_test.go +++ b/src/net/http/responsewrite_test.go @@ -5,7 +5,6 @@ package http import ( - "bytes" "io" "strings" "testing" @@ -276,7 +275,7 @@ func TestResponseWrite(t *testing.T) { for i := range respWriteTests { tt := &respWriteTests[i] - var braw bytes.Buffer + var braw strings.Builder err := tt.Resp.Write(&braw) if err != nil { t.Errorf("error writing #%d: %s", i, err) diff --git a/src/net/http/roundtrip.go b/src/net/http/roundtrip.go index eef7c79293e643..c4c5d3b6ebb3dd 100644 --- a/src/net/http/roundtrip.go +++ b/src/net/http/roundtrip.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js || !wasm -// +build !js !wasm package http diff --git a/src/net/http/roundtrip_js.go b/src/net/http/roundtrip_js.go index 74c83a9172cab2..01c0600ba55500 100644 --- a/src/net/http/roundtrip_js.go +++ b/src/net/http/roundtrip_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package http @@ -41,11 +40,19 @@ const jsFetchCreds = "js.fetch:credentials" // Reference: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters const jsFetchRedirect = "js.fetch:redirect" -var useFakeNetwork = js.Global().Get("fetch").IsUndefined() +// jsFetchMissing will be true if the Fetch API is not present in +// the browser globals. +var jsFetchMissing = js.Global().Get("fetch").IsUndefined() // RoundTrip implements the RoundTripper interface using the WHATWG Fetch API. func (t *Transport) RoundTrip(req *Request) (*Response, error) { - if useFakeNetwork { + // The Transport has a documented contract that states that if the DialContext or + // DialTLSContext functions are set, they will be used to set up the connections. + // If they aren't set then the documented contract is to use Dial or DialTLS, even + // though they are deprecated. Therefore, if any of these are set, we should obey + // the contract and dial using the regular round-trip instead. Otherwise, we'll try + // to fall back on the Fetch API, unless it's not available. + if t.Dial != nil || t.DialContext != nil || t.DialTLS != nil || t.DialTLSContext != nil || jsFetchMissing { return t.roundTrip(req) } @@ -111,7 +118,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { errCh = make(chan error, 1) success, failure js.Func ) - success = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + success = js.FuncOf(func(this js.Value, args []js.Value) any { success.Release() failure.Release() @@ -131,8 +138,24 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { } contentLength := int64(0) - if cl, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil { + clHeader := header.Get("Content-Length") + switch { + case clHeader != "": + cl, err := strconv.ParseInt(clHeader, 10, 64) + if err != nil { + errCh <- fmt.Errorf("net/http: ill-formed Content-Length header: %v", err) + return nil + } + if cl < 0 { + // Content-Length values less than 0 are invalid. + // See: https://datatracker.ietf.org/doc/html/rfc2616/#section-14.13 + errCh <- fmt.Errorf("net/http: invalid Content-Length header: %q", clHeader) + return nil + } contentLength = cl + default: + // If the response length is not declared, set it to -1. + contentLength = -1 } b := result.Get("body") @@ -159,7 +182,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { return nil }) - failure = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + failure = js.FuncOf(func(this js.Value, args []js.Value) any { success.Release() failure.Release() errCh <- fmt.Errorf("net/http: fetch() failed: %s", args[0].Get("message").String()) @@ -200,7 +223,7 @@ func (r *streamReader) Read(p []byte) (n int, err error) { bCh = make(chan []byte, 1) errCh = make(chan error, 1) ) - success := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + success := js.FuncOf(func(this js.Value, args []js.Value) any { result := args[0] if result.Get("done").Bool() { errCh <- io.EOF @@ -212,7 +235,7 @@ func (r *streamReader) Read(p []byte) (n int, err error) { return nil }) defer success.Release() - failure := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + failure := js.FuncOf(func(this js.Value, args []js.Value) any { // Assumes it's a TypeError. See // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError // for more information on this type. See @@ -266,7 +289,7 @@ func (r *arrayReader) Read(p []byte) (n int, err error) { bCh = make(chan []byte, 1) errCh = make(chan error, 1) ) - success := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + success := js.FuncOf(func(this js.Value, args []js.Value) any { // Wrap the input ArrayBuffer with a Uint8Array uint8arrayWrapper := uint8Array.New(args[0]) value := make([]byte, uint8arrayWrapper.Get("byteLength").Int()) @@ -275,7 +298,7 @@ func (r *arrayReader) Read(p []byte) (n int, err error) { return nil }) defer success.Release() - failure := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + failure := js.FuncOf(func(this js.Value, args []js.Value) any { // Assumes it's a TypeError. See // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError // for more information on this type. diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 6394da3bb7cf60..21f23c652f75fa 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -20,9 +20,11 @@ import ( "io" "log" "math/rand" + "mime/multipart" "net" . "net/http" "net/http/httptest" + "net/http/httptrace" "net/http/httputil" "net/http/internal" "net/http/internal/testcert" @@ -146,7 +148,7 @@ func newHandlerTest(h Handler) handlerTest { func (ht *handlerTest) rawResponse(req string) string { reqb := reqBytes(req) - var output bytes.Buffer + var output strings.Builder conn := &rwTestConn{ Reader: bytes.NewReader(reqb), Writer: &output, @@ -2146,7 +2148,7 @@ func TestInvalidTrailerClosesConnection(t *testing.T) { // Read and Write. type slowTestConn struct { // over multiple calls to Read, time.Durations are slept, strings are read. - script []interface{} + script []any closec chan bool mu sync.Mutex // guards rd/wd @@ -2238,7 +2240,7 @@ func TestRequestBodyTimeoutClosesConnection(t *testing.T) { defer afterTest(t) for _, handler := range testHandlerBodyConsumers { conn := &slowTestConn{ - script: []interface{}{ + script: []any{ "POST /public HTTP/1.1\r\n" + "Host: test\r\n" + "Content-Length: 10000\r\n" + @@ -2273,6 +2275,18 @@ func TestRequestBodyTimeoutClosesConnection(t *testing.T) { } } +// cancelableTimeoutContext overwrites the error message to DeadlineExceeded +type cancelableTimeoutContext struct { + context.Context +} + +func (c cancelableTimeoutContext) Err() error { + if c.Context.Err() != nil { + return context.DeadlineExceeded + } + return nil +} + func TestTimeoutHandler_h1(t *testing.T) { testTimeoutHandler(t, h1Mode) } func TestTimeoutHandler_h2(t *testing.T) { testTimeoutHandler(t, h2Mode) } func testTimeoutHandler(t *testing.T, h2 bool) { @@ -2285,8 +2299,9 @@ func testTimeoutHandler(t *testing.T, h2 bool) { _, werr := w.Write([]byte("hi")) writeErrors <- werr }) - timeout := make(chan time.Time, 1) // write to this to force timeouts - cst := newClientServerTest(t, h2, NewTestTimeoutHandler(sayHi, timeout)) + ctx, cancel := context.WithCancel(context.Background()) + h := NewTestTimeoutHandler(sayHi, cancelableTimeoutContext{ctx}) + cst := newClientServerTest(t, h2, h) defer cst.close() // Succeed without timing out: @@ -2307,7 +2322,8 @@ func testTimeoutHandler(t *testing.T, h2 bool) { } // Times out: - timeout <- time.Time{} + cancel() + res, err = cst.c.Get(cst.ts.URL) if err != nil { t.Error(err) @@ -2428,8 +2444,9 @@ func TestTimeoutHandlerRaceHeaderTimeout(t *testing.T) { _, werr := w.Write([]byte("hi")) writeErrors <- werr }) - timeout := make(chan time.Time, 1) // write to this to force timeouts - cst := newClientServerTest(t, h1Mode, NewTestTimeoutHandler(sayHi, timeout)) + ctx, cancel := context.WithCancel(context.Background()) + h := NewTestTimeoutHandler(sayHi, cancelableTimeoutContext{ctx}) + cst := newClientServerTest(t, h1Mode, h) defer cst.close() // Succeed without timing out: @@ -2450,7 +2467,8 @@ func TestTimeoutHandlerRaceHeaderTimeout(t *testing.T) { } // Times out: - timeout <- time.Time{} + cancel() + res, err = cst.c.Get(cst.ts.URL) if err != nil { t.Error(err) @@ -2500,6 +2518,47 @@ func TestTimeoutHandlerStartTimerWhenServing(t *testing.T) { } } +func TestTimeoutHandlerContextCanceled(t *testing.T) { + setParallel(t) + defer afterTest(t) + writeErrors := make(chan error, 1) + sayHi := HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Type", "text/plain") + var err error + // The request context has already been canceled, but + // retry the write for a while to give the timeout handler + // a chance to notice. + for i := 0; i < 100; i++ { + _, err = w.Write([]byte("a")) + if err != nil { + break + } + time.Sleep(1 * time.Millisecond) + } + writeErrors <- err + }) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + h := NewTestTimeoutHandler(sayHi, ctx) + cst := newClientServerTest(t, h1Mode, h) + defer cst.close() + + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Error(err) + } + if g, e := res.StatusCode, StatusServiceUnavailable; g != e { + t.Errorf("got res.StatusCode %d; expected %d", g, e) + } + body, _ := io.ReadAll(res.Body) + if g, e := string(body), ""; g != e { + t.Errorf("got body %q; expected %q", g, e) + } + if g, e := <-writeErrors, context.Canceled; g != e { + t.Errorf("got unexpected Write in handler: %v, want %g", g, e) + } +} + // https://golang.org/issue/15948 func TestTimeoutHandlerEmptyResponse(t *testing.T) { setParallel(t) @@ -2708,7 +2767,7 @@ func TestHandlerPanicWithHijack(t *testing.T) { testHandlerPanic(t, true, h1Mode, nil, "intentional death for testing") } -func testHandlerPanic(t *testing.T, withHijack, h2 bool, wrapper func(Handler) Handler, panicValue interface{}) { +func testHandlerPanic(t *testing.T, withHijack, h2 bool, wrapper func(Handler) Handler, panicValue any) { defer afterTest(t) // Unlike the other tests that set the log output to io.Discard // to quiet the output, this test uses a pipe. The pipe serves three @@ -2977,6 +3036,13 @@ func testRequestBodyLimit(t *testing.T, h2 bool) { if n != limit { t.Errorf("io.Copy = %d, want %d", n, limit) } + mbErr, ok := err.(*MaxBytesError) + if !ok { + t.Errorf("expected MaxBytesError, got %T", err) + } + if mbErr.Limit != limit { + t.Errorf("MaxBytesError.Limit = %d, want %d", mbErr.Limit, limit) + } })) defer cst.close() @@ -3017,22 +3083,14 @@ func TestClientWriteShutdown(t *testing.T) { if err != nil { t.Fatalf("CloseWrite: %v", err) } - donec := make(chan bool) - go func() { - defer close(donec) - bs, err := io.ReadAll(conn) - if err != nil { - t.Errorf("ReadAll: %v", err) - } - got := string(bs) - if got != "" { - t.Errorf("read %q from server; want nothing", got) - } - }() - select { - case <-donec: - case <-time.After(10 * time.Second): - t.Fatalf("timeout") + + bs, err := io.ReadAll(conn) + if err != nil { + t.Errorf("ReadAll: %v", err) + } + got := string(bs) + if got != "" { + t.Errorf("read %q from server; want nothing", got) } } @@ -3434,6 +3492,37 @@ func TestOptions(t *testing.T) { } } +func TestOptionsHandler(t *testing.T) { + rc := make(chan *Request, 1) + + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) { + rc <- r + })) + ts.Config.DisableGeneralOptionsHandler = true + ts.Start() + defer ts.Close() + + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + _, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n")) + if err != nil { + t.Fatal(err) + } + + select { + case got := <-rc: + if got.Method != "OPTIONS" || got.RequestURI != "*" { + t.Errorf("Expected OPTIONS * request, got %v", got) + } + case <-time.After(5 * time.Second): + t.Error("timeout") + } +} + // Tests regarding the ordering of Write, WriteHeader, Header, and // Flush calls. In Go 1.0, rw.WriteHeader immediately flushed the // (*response).header to the wire. In Go 1.1, the actual wire flush is @@ -3653,7 +3742,7 @@ func TestAcceptMaxFds(t *testing.T) { func TestWriteAfterHijack(t *testing.T) { req := reqBytes("GET / HTTP/1.1\nHost: golang.org") - var buf bytes.Buffer + var buf strings.Builder wrotec := make(chan bool, 1) conn := &rwTestConn{ Reader: bytes.NewReader(req), @@ -3816,7 +3905,7 @@ func testServerReaderFromOrder(t *testing.T, h2 bool) { // Issue 6157, Issue 6685 func TestCodesPreventingContentTypeAndBody(t *testing.T) { - for _, code := range []int{StatusNotModified, StatusNoContent, StatusContinue} { + for _, code := range []int{StatusNotModified, StatusNoContent} { ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { if r.URL.Path == "/header" { w.Header().Set("Content-Length", "123") @@ -3884,7 +3973,7 @@ func testTransportAndServerSharedBodyRace(t *testing.T, h2 bool) { // this test fails, it hangs. This helps debugging and I've // added this enough times "temporarily". It now gets added // full time. - errorf := func(format string, args ...interface{}) { + errorf := func(format string, args ...any) { v := fmt.Sprintf(format, args...) println(v) t.Error(v) @@ -3893,10 +3982,10 @@ func testTransportAndServerSharedBodyRace(t *testing.T, h2 bool) { unblockBackend := make(chan bool) backend := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) { gone := rw.(CloseNotifier).CloseNotify() - didCopy := make(chan interface{}) + didCopy := make(chan any) go func() { n, err := io.CopyN(rw, req.Body, bodySize) - didCopy <- []interface{}{n, err} + didCopy <- []any{n, err} }() isGone := false Loop: @@ -4455,7 +4544,7 @@ func TestNoContentLengthIfTransferEncoding(t *testing.T) { t.Fatal(err) } bs := bufio.NewScanner(c) - var got bytes.Buffer + var got strings.Builder for bs.Scan() { if strings.TrimSpace(bs.Text()) == "" { break @@ -4544,7 +4633,7 @@ GET /should-be-ignored HTTP/1.1 Host: foo `) - var buf bytes.Buffer + var buf strings.Builder conn := &rwTestConn{ Reader: bytes.NewReader(req), Writer: &buf, @@ -4827,11 +4916,7 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) { handlerDone := make(chan struct{}) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { close(inHandler) - select { - case <-r.Context().Done(): - case <-time.After(3 * time.Second): - t.Errorf("timeout waiting for context to be done") - } + <-r.Context().Done() close(handlerDone) })) defer ts.Close() @@ -4841,18 +4926,9 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) { } defer c.Close() io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n") - select { - case <-inHandler: - case <-time.After(3 * time.Second): - t.Fatalf("timeout waiting to see ServeHTTP get called") - } + <-inHandler c.Close() // this should trigger the context being done - - select { - case <-handlerDone: - case <-time.After(4 * time.Second): - t.Fatalf("timeout waiting to see ServeHTTP exit") - } + <-handlerDone } func TestServerContext_ServerContextKey_h1(t *testing.T) { @@ -4888,7 +4964,7 @@ func TestServerContext_LocalAddrContextKey_h2(t *testing.T) { func testServerContext_LocalAddrContextKey(t *testing.T, h2 bool) { setParallel(t) defer afterTest(t) - ch := make(chan interface{}, 1) + ch := make(chan any, 1) cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { ch <- r.Context().Value(LocalAddrContextKey) })) @@ -5027,10 +5103,11 @@ func benchmarkClientServerParallel(b *testing.B, parallelism int, useTLS bool) { // The client code runs in a subprocess. // // For use like: -// $ go test -c -// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof -// $ go tool pprof http.test http.prof -// (pprof) web +// +// $ go test -c +// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof +// $ go tool pprof http.test http.prof +// (pprof) web func BenchmarkServer(b *testing.B) { b.ReportAllocs() // Child process mode; @@ -5689,22 +5766,37 @@ func testServerKeepAlivesEnabled(t *testing.T, h2 bool) { } // Not parallel: messes with global variable. (http2goAwayTimeout) defer afterTest(t) - cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { - fmt.Fprintf(w, "%v", r.RemoteAddr) - })) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {})) defer cst.close() srv := cst.ts.Config srv.SetKeepAlivesEnabled(false) - a := cst.getURL(cst.ts.URL) - if !waitCondition(2*time.Second, 10*time.Millisecond, srv.ExportAllConnsIdle) { - t.Fatalf("test server has active conns") - } - b := cst.getURL(cst.ts.URL) - if a == b { - t.Errorf("got same connection between first and second requests") - } - if !waitCondition(2*time.Second, 10*time.Millisecond, srv.ExportAllConnsIdle) { - t.Fatalf("test server has active conns") + for try := 0; try < 2; try++ { + if !waitCondition(2*time.Second, 10*time.Millisecond, srv.ExportAllConnsIdle) { + t.Fatalf("request %v: test server has active conns", try) + } + conns := 0 + var info httptrace.GotConnInfo + ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ + GotConn: func(v httptrace.GotConnInfo) { + conns++ + info = v + }, + }) + req, err := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil) + if err != nil { + t.Fatal(err) + } + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if conns != 1 { + t.Fatalf("request %v: got %v conns, want 1", try, conns) + } + if info.Reused || info.WasIdle { + t.Fatalf("request %v: Reused=%v (want false), WasIdle=%v (want false)", try, info.Reused, info.WasIdle) + } } } @@ -5751,6 +5843,58 @@ func TestServerCancelsReadTimeoutWhenIdle(t *testing.T) { }) } +// Issue 54784: test that the Server's ReadHeaderTimeout only starts once the +// beginning of a request has been received, rather than including time the +// connection spent idle. +func TestServerCancelsReadHeaderTimeoutWhenIdle(t *testing.T) { + setParallel(t) + defer afterTest(t) + runTimeSensitiveTest(t, []time.Duration{ + 10 * time.Millisecond, + 50 * time.Millisecond, + 250 * time.Millisecond, + time.Second, + 2 * time.Second, + }, func(t *testing.T, timeout time.Duration) error { + ts := httptest.NewUnstartedServer(serve(200)) + ts.Config.ReadHeaderTimeout = timeout + ts.Config.IdleTimeout = 0 // disable idle timeout + ts.Start() + defer ts.Close() + + // rather than using an http.Client, create a single connection, so that + // we can ensure this connection is not closed. + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("dial failed: %v", err) + } + br := bufio.NewReader(conn) + defer conn.Close() + + if _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil { + t.Fatalf("writing first request failed: %v", err) + } + + if _, err := ReadResponse(br, nil); err != nil { + t.Fatalf("first response (before timeout) failed: %v", err) + } + + // wait for longer than the server's ReadHeaderTimeout, and then send + // another request + time.Sleep(timeout + 10*time.Millisecond) + + if _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: e.com\r\n\r\n")); err != nil { + t.Fatalf("writing second request failed: %v", err) + } + + if _, err := ReadResponse(br, nil); err != nil { + t.Fatalf("second response (after timeout) failed: %v", err) + } + + return nil + }) +} + // runTimeSensitiveTest runs test with the provided durations until one passes. // If they all fail, t.Fatal is called with the last one's duration and error value. func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t *testing.T, d time.Duration) error) { @@ -5933,11 +6077,7 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) { t.Fatal(err) } - select { - case <-done: - case <-time.After(2 * time.Second): - t.Error("timeout") - } + <-done } // Issue 18319: test that the Server validates the request method. @@ -6189,6 +6329,7 @@ func TestUnsupportedTransferEncodingsReturn501(t *testing.T) { "fugazi", "foo-bar", "unknown", + "\rchunked", } for _, badTE := range unsupportedTEs { @@ -6232,7 +6373,7 @@ func testContentEncodingNoSniffing(t *testing.T, h2 bool) { // setting contentEncoding as an interface instead of a string // directly, so as to differentiate between 3 states: // unset, empty string "" and set string "foo/bar". - contentEncoding interface{} + contentEncoding any wantContentType string } @@ -6370,7 +6511,7 @@ func TestTimeoutHandlerSuperfluousLogs(t *testing.T) { exitHandler <- true } - logBuf := new(bytes.Buffer) + logBuf := new(strings.Builder) srvLog := log.New(logBuf, "", 0) // When expecting to timeout, we'll keep the duration short. dur := 20 * time.Millisecond @@ -6490,7 +6631,7 @@ func TestDisableKeepAliveUpgrade(t *testing.T) { rwc, ok := resp.Body.(io.ReadWriteCloser) if !ok { - t.Fatalf("Response.Body is not a io.ReadWriteCloser: %T", resp.Body) + t.Fatalf("Response.Body is not an io.ReadWriteCloser: %T", resp.Body) } _, err = rwc.Write([]byte("hello")) @@ -6580,7 +6721,7 @@ func testQuerySemicolon(t *testing.T, query string, wantX string, allowSemicolon } ts := httptest.NewUnstartedServer(h) - logBuf := &bytes.Buffer{} + logBuf := &strings.Builder{} ts.Config.ErrorLog = log.New(logBuf, "", 0) ts.Start() defer ts.Close() @@ -6609,3 +6750,160 @@ func testQuerySemicolon(t *testing.T, query string, wantX string, allowSemicolon } } } + +func TestMaxBytesHandler(t *testing.T) { + setParallel(t) + defer afterTest(t) + + for _, maxSize := range []int64{100, 1_000, 1_000_000} { + for _, requestSize := range []int64{100, 1_000, 1_000_000} { + t.Run(fmt.Sprintf("max size %d request size %d", maxSize, requestSize), + func(t *testing.T) { + testMaxBytesHandler(t, maxSize, requestSize) + }) + } + } +} + +func testMaxBytesHandler(t *testing.T, maxSize, requestSize int64) { + var ( + handlerN int64 + handlerErr error + ) + echo := HandlerFunc(func(w ResponseWriter, r *Request) { + var buf bytes.Buffer + handlerN, handlerErr = io.Copy(&buf, r.Body) + io.Copy(w, &buf) + }) + + ts := httptest.NewServer(MaxBytesHandler(echo, maxSize)) + defer ts.Close() + + c := ts.Client() + var buf strings.Builder + body := strings.NewReader(strings.Repeat("a", int(requestSize))) + res, err := c.Post(ts.URL, "text/plain", body) + if err != nil { + t.Errorf("unexpected connection error: %v", err) + } else { + _, err = io.Copy(&buf, res.Body) + res.Body.Close() + if err != nil { + t.Errorf("unexpected read error: %v", err) + } + } + if handlerN > maxSize { + t.Errorf("expected max request body %d; got %d", maxSize, handlerN) + } + if requestSize > maxSize && handlerErr == nil { + t.Error("expected error on handler side; got nil") + } + if requestSize <= maxSize { + if handlerErr != nil { + t.Errorf("%d expected nil error on handler side; got %v", requestSize, handlerErr) + } + if handlerN != requestSize { + t.Errorf("expected request of size %d; got %d", requestSize, handlerN) + } + } + if buf.Len() != int(handlerN) { + t.Errorf("expected echo of size %d; got %d", handlerN, buf.Len()) + } +} + +func TestEarlyHints(t *testing.T) { + ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { + h := w.Header() + h.Add("Link", "; rel=preload; as=style") + h.Add("Link", "; rel=preload; as=script") + w.WriteHeader(StatusEarlyHints) + + h.Add("Link", "; rel=preload; as=script") + w.WriteHeader(StatusEarlyHints) + + w.Write([]byte("stuff")) + })) + + got := ht.rawResponse("GET / HTTP/1.1\nHost: golang.org") + expected := "HTTP/1.1 103 Early Hints\r\nLink: ; rel=preload; as=style\r\nLink: ; rel=preload; as=script\r\n\r\nHTTP/1.1 103 Early Hints\r\nLink: ; rel=preload; as=style\r\nLink: ; rel=preload; as=script\r\nLink: ; rel=preload; as=script\r\n\r\nHTTP/1.1 200 OK\r\nLink: ; rel=preload; as=style\r\nLink: ; rel=preload; as=script\r\nLink: ; rel=preload; as=script\r\nDate: " // dynamic content expected + if !strings.Contains(got, expected) { + t.Errorf("unexpected response; got %q; should start by %q", got, expected) + } +} +func TestProcessing(t *testing.T) { + ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { + w.WriteHeader(StatusProcessing) + w.Write([]byte("stuff")) + })) + + got := ht.rawResponse("GET / HTTP/1.1\nHost: golang.org") + expected := "HTTP/1.1 102 Processing\r\n\r\nHTTP/1.1 200 OK\r\nDate: " // dynamic content expected + if !strings.Contains(got, expected) { + t.Errorf("unexpected response; got %q; should start by %q", got, expected) + } +} + +func TestParseFormCleanup_h1(t *testing.T) { testParseFormCleanup(t, h1Mode) } +func TestParseFormCleanup_h2(t *testing.T) { + t.Skip("https://go.dev/issue/20253") + testParseFormCleanup(t, h2Mode) +} + +func testParseFormCleanup(t *testing.T, h2 bool) { + const maxMemory = 1024 + const key = "file" + + if runtime.GOOS == "windows" { + // Windows sometimes refuses to remove a file that was just closed. + t.Skip("https://go.dev/issue/25965") + } + + setParallel(t) + defer afterTest(t) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + r.ParseMultipartForm(maxMemory) + f, _, err := r.FormFile(key) + if err != nil { + t.Errorf("r.FormFile(%q) = %v", key, err) + return + } + of, ok := f.(*os.File) + if !ok { + t.Errorf("r.FormFile(%q) returned type %T, want *os.File", key, f) + return + } + w.Write([]byte(of.Name())) + })) + defer cst.close() + + fBuf := new(bytes.Buffer) + mw := multipart.NewWriter(fBuf) + mf, err := mw.CreateFormFile(key, "myfile.txt") + if err != nil { + t.Fatal(err) + } + if _, err := mf.Write(bytes.Repeat([]byte("A"), maxMemory*2)); err != nil { + t.Fatal(err) + } + if err := mw.Close(); err != nil { + t.Fatal(err) + } + req, err := NewRequest("POST", cst.ts.URL, fBuf) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", mw.FormDataContentType()) + res, err := cst.c.Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + fname, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + cst.close() + if _, err := os.Stat(string(fname)); !errors.Is(err, os.ErrNotExist) { + t.Errorf("file %q exists after HTTP handler returned", string(fname)) + } +} diff --git a/src/net/http/server.go b/src/net/http/server.go index 5b113cff970dc1..3d427e5ae4bab6 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -13,6 +13,7 @@ import ( "crypto/tls" "errors" "fmt" + "internal/godebug" "io" "log" "math/rand" @@ -20,7 +21,6 @@ import ( "net/textproto" "net/url" urlpkg "net/url" - "os" "path" "runtime" "sort" @@ -98,8 +98,8 @@ type ResponseWriter interface { // Handlers can set HTTP trailers. // // Changing the header map after a call to WriteHeader (or - // Write) has no effect unless the modified headers are - // trailers. + // Write) has no effect unless the HTTP status code was of the + // 1xx class or the modified headers are trailers. // // There are two ways to set Trailers. The preferred way is to // predeclare in the headers which trailers you will later @@ -144,13 +144,18 @@ type ResponseWriter interface { // If WriteHeader is not called explicitly, the first call to Write // will trigger an implicit WriteHeader(http.StatusOK). // Thus explicit calls to WriteHeader are mainly used to - // send error codes. + // send error codes or 1xx informational responses. // // The provided code must be a valid HTTP 1xx-5xx status code. - // Only one header may be written. Go does not currently - // support sending user-defined 1xx informational headers, - // with the exception of 100-continue response header that the - // Server sends automatically when the Request.Body is read. + // Any number of 1xx headers may be written, followed by at most + // one 2xx-5xx header. 1xx headers are sent immediately, but 2xx-5xx + // headers may be buffered. Use the Flusher interface to send + // buffered data. The header map is cleared when 2xx-5xx headers are + // sent, but not with 1xx headers. + // + // The server will automatically send a 100 (Continue) header + // on the first read from the request body if the request has + // an "Expect: 100-continue" header. WriteHeader(statusCode int) } @@ -288,9 +293,9 @@ type conn struct { // on this connection, if any. lastMethod string - curReq atomic.Value // of *response (which has a Request in it) + curReq atomic.Pointer[response] // (which has a Request in it) - curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState)) + curState atomic.Uint64 // packed (unixtime<<8|uint8(ConnState)) // mu guards hijackedv mu sync.Mutex @@ -420,19 +425,19 @@ type response struct { req *Request // request for this response reqBody io.ReadCloser cancelCtx context.CancelFunc // when ServeHTTP exits - wroteHeader bool // reply header has been (logically) written + wroteHeader bool // a non-1xx header has been (logically) written wroteContinue bool // 100 Continue response was written wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" wantsClose bool // HTTP request has Connection "close" - // canWriteContinue is a boolean value accessed as an atomic int32 - // that says whether or not a 100 Continue header can be written - // to the connection. + // canWriteContinue is an atomic boolean that says whether or + // not a 100 Continue header can be written to the + // connection. // writeContinueMu must be held while writing the header. - // These two fields together synchronize the body reader - // (the expectContinueReader, which wants to write 100 Continue) + // These two fields together synchronize the body reader (the + // expectContinueReader, which wants to write 100 Continue) // against the main writer. - canWriteContinue atomicBool + canWriteContinue atomic.Bool writeContinueMu sync.Mutex w *bufio.Writer // buffers output in chunks to chunkWriter @@ -470,7 +475,7 @@ type response struct { // written. trailers []string - handlerDone atomicBool // set true when the handler exits + handlerDone atomic.Bool // set true when the handler exits // Buffers for Date, Content-Length, and status code dateBuf [len(TimeFormat)]byte @@ -494,8 +499,9 @@ type response struct { // prior to the headers being written. If the set of trailers is fixed // or known before the header is written, the normal Go trailers mechanism // is preferred: -// https://golang.org/pkg/net/http/#ResponseWriter -// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers +// +// https://pkg.go.dev/net/http#ResponseWriter +// https://pkg.go.dev/net/http#example-ResponseWriter-Trailers const TrailerPrefix = "Trailer:" // finalTrailers is called after the Handler exits and returns a non-nil @@ -521,12 +527,6 @@ func (w *response) finalTrailers() Header { return t } -type atomicBool int32 - -func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } -func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } -func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } - // declareTrailer is called for each Trailer header when the // response header is written. It notes that a header will need to be // written in the trailers at the end of the response. @@ -549,12 +549,6 @@ func (w *response) requestTooLarge() { } } -// needsSniff reports whether a Content-Type still needs to be sniffed. -func (w *response) needsSniff() bool { - _, haveType := w.handlerHeader["Content-Type"] - return !w.cw.wroteHeader && !haveType && w.written < sniffLen -} - // writerOnly hides an io.Writer value's optional ReadFrom method // from io.Copy. type writerOnly struct { @@ -743,7 +737,7 @@ func (cr *connReader) handleReadError(_ error) { // may be called from multiple goroutines. func (cr *connReader) closeNotify() { - res, _ := cr.conn.curReq.Load().(*response) + res := cr.conn.curReq.Load() if res != nil && atomic.CompareAndSwapInt32(&res.didCloseNotify, 0, 1) { res.closeNotifyCh <- true } @@ -798,7 +792,7 @@ var ( ) var copyBufPool = sync.Pool{ - New: func() interface{} { + New: func() any { b := make([]byte, 32*1024) return &b }, @@ -865,39 +859,61 @@ func (srv *Server) initialReadLimitSize() int64 { return int64(srv.maxHeaderBytes()) + 4096 // bufio slop } +// tlsHandshakeTimeout returns the time limit permitted for the TLS +// handshake, or zero for unlimited. +// +// It returns the minimum of any positive ReadHeaderTimeout, +// ReadTimeout, or WriteTimeout. +func (srv *Server) tlsHandshakeTimeout() time.Duration { + var ret time.Duration + for _, v := range [...]time.Duration{ + srv.ReadHeaderTimeout, + srv.ReadTimeout, + srv.WriteTimeout, + } { + if v <= 0 { + continue + } + if ret == 0 || v < ret { + ret = v + } + } + return ret +} + // wrapper around io.ReadCloser which on first read, sends an // HTTP/1.1 100 Continue header type expectContinueReader struct { resp *response readCloser io.ReadCloser - closed atomicBool - sawEOF atomicBool + closed atomic.Bool + sawEOF atomic.Bool } func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { - if ecr.closed.isSet() { + if ecr.closed.Load() { return 0, ErrBodyReadAfterClose } w := ecr.resp - if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() { + if !w.wroteContinue && w.canWriteContinue.Load() && !w.conn.hijacked() { w.wroteContinue = true w.writeContinueMu.Lock() - if w.canWriteContinue.isSet() { + if w.canWriteContinue.Load() { w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") w.conn.bufw.Flush() - w.canWriteContinue.setFalse() + w.canWriteContinue.Store(false) } w.writeContinueMu.Unlock() } n, err = ecr.readCloser.Read(p) if err == io.EOF { - ecr.sawEOF.setTrue() + ecr.sawEOF.Store(true) } return } func (ecr *expectContinueReader) Close() error { - ecr.closed.setTrue() + ecr.closed.Store(true) return ecr.readCloser.Close() } @@ -1077,8 +1093,7 @@ func checkWriteHeaderCode(code int) { // Issue 22880: require valid WriteHeader status codes. // For now we only enforce that it's three digits. // In the future we might block things over 599 (600 and above aren't defined - // at https://httpwg.org/specs/rfc7231.html#status.codes) - // and we might block under 200 (once we have more mature 1xx support). + // at https://httpwg.org/specs/rfc7231.html#status.codes). // But for now any three digits. // // We used to send "HTTP/1.1 000 0" on the wire in responses but there's @@ -1121,6 +1136,26 @@ func (w *response) WriteHeader(code int) { return } checkWriteHeaderCode(code) + + // Handle informational headers + if code >= 100 && code <= 199 { + // Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read() + if code == 100 && w.canWriteContinue.Load() { + w.writeContinueMu.Lock() + w.canWriteContinue.Store(false) + w.writeContinueMu.Unlock() + } + + writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:]) + + // Per RFC 8297 we must not clear the current header map + w.handlerHeader.WriteSubset(w.conn.bufw, excludedHeadersNoBody) + w.conn.bufw.Write(crlf) + w.conn.bufw.Flush() + + return + } + w.wroteHeader = true w.status = code @@ -1265,7 +1300,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // send a Content-Length header. // Further, we don't send an automatic Content-Length if they // set a Transfer-Encoding, because they're generally incompatible. - if w.handlerDone.isSet() && !trailers && !hasTE && bodyAllowedForStatus(w.status) && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) { + if w.handlerDone.Load() && !trailers && !hasTE && bodyAllowedForStatus(w.status) && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) { w.contentLength = int64(len(p)) setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10) } @@ -1307,7 +1342,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // because we don't know if the next bytes on the wire will be // the body-following-the-timer or the subsequent request. // See Issue 11549. - if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() { + if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.Load() { w.closeAfterReply = true } @@ -1404,11 +1439,11 @@ func (cw *chunkWriter) writeHeader(p []byte) { hasCL = false } - if w.req.Method == "HEAD" || !bodyAllowedForStatus(code) { - // do nothing - } else if code == StatusNoContent { + if w.req.Method == "HEAD" || !bodyAllowedForStatus(code) || code == StatusNoContent { + // Response has no body. delHeader("Transfer-Encoding") } else if hasCL { + // Content-Length has been provided, so no chunking is to be done. delHeader("Transfer-Encoding") } else if w.req.ProtoAtLeast(1, 1) { // HTTP/1.1 or greater: Transfer-Encoding has been set to identity, and no @@ -1419,6 +1454,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { if hasTE && te == "identity" { cw.chunking = false w.closeAfterReply = true + delHeader("Transfer-Encoding") } else { // HTTP/1.1 or greater: use chunked transfer encoding // to avoid closing the connection at EOF. @@ -1492,7 +1528,7 @@ func writeStatusLine(bw *bufio.Writer, is11 bool, code int, scratch []byte) { } else { bw.WriteString("HTTP/1.0 ") } - if text, ok := statusText[code]; ok { + if text := StatusText(code); text != "" { bw.Write(strconv.AppendInt(scratch[:0], int64(code), 10)) bw.WriteByte(' ') bw.WriteString(text) @@ -1528,14 +1564,14 @@ func (w *response) bodyAllowed() bool { // // The Writers are wired together like: // -// 1. *response (the ResponseWriter) -> -// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes -> -// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) -// and which writes the chunk headers, if needed -> -// 4. conn.bufw, a *bufio.Writer of default (4kB) bytes, writing to -> -// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write -// and populates c.werr with it if so, but otherwise writes to -> -// 6. the rwc, the net.Conn. +// 1. *response (the ResponseWriter) -> +// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes -> +// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) +// and which writes the chunk headers, if needed -> +// 4. conn.bufw, a *bufio.Writer of default (4kB) bytes, writing to -> +// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write +// and populates c.werr with it if so, but otherwise writes to -> +// 6. the rwc, the net.Conn. // // TODO(bradfitz): short-circuit some of the buffering when the // initial header contains both a Content-Type and Content-Length. @@ -1564,13 +1600,13 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er return 0, ErrHijacked } - if w.canWriteContinue.isSet() { + if w.canWriteContinue.Load() { // Body reader wants to write 100 Continue but hasn't yet. // Tell it not to. The store must be done while holding the lock // because the lock makes sure that there is not an active write // this very moment. w.writeContinueMu.Lock() - w.canWriteContinue.setFalse() + w.canWriteContinue.Store(false) w.writeContinueMu.Unlock() } @@ -1596,7 +1632,7 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er } func (w *response) finishRequest() { - w.handlerDone.setTrue() + w.handlerDone.Store(true) if !w.wroteHeader { w.WriteHeader(StatusOK) @@ -1698,7 +1734,7 @@ type closeWriter interface { var _ closeWriter = (*net.TCPConn)(nil) // closeWrite flushes any outstanding data and sends a FIN packet (if -// client is connected via TCP), signalling that we're done. We then +// client is connected via TCP), signaling that we're done. We then // pause for a bit, hoping the client processes it before any // subsequent RST. // @@ -1739,7 +1775,7 @@ func (c *conn) setState(nc net.Conn, state ConnState, runHook bool) { panic("internal error") } packedState := uint64(time.Now().Unix()<<8) | uint64(state) - atomic.StoreUint64(&c.curState.atomic, packedState) + c.curState.Store(packedState) if !runHook { return } @@ -1749,7 +1785,7 @@ func (c *conn) setState(nc net.Conn, state ConnState, runHook bool) { } func (c *conn) getState() (state ConnState, unixSec int64) { - packedState := atomic.LoadUint64(&c.curState.atomic) + packedState := c.curState.Load() return ConnState(packedState & 0xff), int64(packedState >> 8) } @@ -1794,6 +1830,7 @@ func isCommonNetReadError(err error) bool { func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) + var inFlightResponse *response defer func() { if err := recover(); err != nil && err != ErrAbortHandler { const size = 64 << 10 @@ -1801,18 +1838,25 @@ func (c *conn) serve(ctx context.Context) { buf = buf[:runtime.Stack(buf, false)] c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } + if inFlightResponse != nil { + inFlightResponse.cancelCtx() + } if !c.hijacked() { + if inFlightResponse != nil { + inFlightResponse.conn.r.abortPendingRead() + inFlightResponse.reqBody.Close() + } c.close() c.setState(c.rwc, StateClosed, runHooks) } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { - if d := c.server.ReadTimeout; d > 0 { - c.rwc.SetReadDeadline(time.Now().Add(d)) - } - if d := c.server.WriteTimeout; d > 0 { - c.rwc.SetWriteDeadline(time.Now().Add(d)) + tlsTO := c.server.tlsHandshakeTimeout() + if tlsTO > 0 { + dl := time.Now().Add(tlsTO) + c.rwc.SetReadDeadline(dl) + c.rwc.SetWriteDeadline(dl) } if err := tlsConn.HandshakeContext(ctx); err != nil { // If the handshake failed due to the client not speaking @@ -1826,6 +1870,11 @@ func (c *conn) serve(ctx context.Context) { c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err) return } + // Restore Conn-level deadlines. + if tlsTO > 0 { + c.rwc.SetReadDeadline(time.Time{}) + c.rwc.SetWriteDeadline(time.Time{}) + } c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() if proto := c.tlsState.NegotiatedProtocol; validNextProto(proto) { @@ -1904,7 +1953,7 @@ func (c *conn) serve(ctx context.Context) { if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} - w.canWriteContinue.setTrue() + w.canWriteContinue.Store(true) } } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() @@ -1926,7 +1975,9 @@ func (c *conn) serve(ctx context.Context) { // in parallel even if their responses need to be serialized. // But we're not going to implement HTTP pipelining because it // was never deployed in the wild and the answer is HTTP/2. + inFlightResponse = w serverHandler{c.server}.ServeHTTP(w, w.req) + inFlightResponse = nil w.cancelCtx() if c.hijacked() { return @@ -1939,7 +1990,7 @@ func (c *conn) serve(ctx context.Context) { return } c.setState(c.rwc, StateIdle, runHooks) - c.curReq.Store((*response)(nil)) + c.curReq.Store(nil) if !w.conn.server.doKeepAlives() { // We're in shutdown mode. We might've replied @@ -1951,10 +2002,18 @@ func (c *conn) serve(ctx context.Context) { if d := c.server.idleTimeout(); d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) - if _, err := c.bufr.Peek(4); err != nil { - return - } + } else { + c.rwc.SetReadDeadline(time.Time{}) } + + // Wait for the connection to become readable again before trying to + // read the next request. This prevents a ReadHeaderTimeout or + // ReadTimeout from starting until the first bytes of the next request + // have been received. + if _, err := c.bufr.Peek(4); err != nil { + return + } + c.rwc.SetReadDeadline(time.Time{}) } } @@ -1980,7 +2039,7 @@ func (w *response) sendExpectationFailed() { // Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter // and a Hijacker. func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { - if w.handlerDone.isSet() { + if w.handlerDone.Load() { panic("net/http: Hijack called after ServeHTTP finished") } if w.wroteHeader { @@ -2002,7 +2061,7 @@ func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { } func (w *response) CloseNotify() <-chan bool { - if w.handlerDone.isSet() { + if w.handlerDone.Load() { panic("net/http: CloseNotify called after ServeHTTP finished") } return w.closeNotifyCh @@ -2063,7 +2122,7 @@ func Error(w ResponseWriter, error string, code int) { func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } // NotFoundHandler returns a simple request handler -// that replies to each request with a ``404 page not found'' reply. +// that replies to each request with a “404 page not found” reply. func NotFoundHandler() Handler { return HandlerFunc(NotFound) } // StripPrefix returns a handler that serves HTTP requests by removing the @@ -2153,7 +2212,7 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) { // Shouldn't send the body for POST or HEAD; that leaves GET. if !hadCT && r.Method == "GET" { - body := "" + statusText[code] + ".\n" + body := "" + StatusText(code) + ".\n" fmt.Fprintln(w, body) } } @@ -2272,7 +2331,7 @@ func cleanPath(p string) string { // stripHostPort returns h without any trailing ":". func stripHostPort(h string) string { // If no port on host, return unchanged - if strings.IndexByte(h, ':') == -1 { + if !strings.Contains(h, ":") { return h } host, _, err := net.SplitHostPort(h) @@ -2356,7 +2415,7 @@ func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool { // the pattern that will match after following the redirect. // // If there is no registered handler that applies to the request, -// Handler returns a ``page not found'' handler and an empty pattern. +// Handler returns a “page not found” handler and an empty pattern. func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. @@ -2533,6 +2592,10 @@ type Server struct { Handler Handler // handler to invoke, http.DefaultServeMux if nil + // DisableGeneralOptionsHandler, if true, passes "OPTIONS *" requests to the Handler, + // otherwise responds with 200 OK and Content-Length: 0. + DisableGeneralOptionsHandler bool + // TLSConfig optionally provides a TLS configuration for use // by ServeTLS and ListenAndServeTLS. Note that this value is // cloned by ServeTLS and ListenAndServeTLS, so it's not @@ -2616,7 +2679,7 @@ type Server struct { // value. ConnContext func(ctx context.Context, c net.Conn) context.Context - inShutdown atomicBool // true when server is in shutdown + inShutdown atomic.Bool // true when server is in shutdown disableKeepAlives int32 // accessed atomically. nextProtoOnce sync.Once // guards setupHTTP2_* init @@ -2625,33 +2688,9 @@ type Server struct { mu sync.Mutex listeners map[*net.Listener]struct{} activeConn map[*conn]struct{} - doneChan chan struct{} onShutdown []func() -} -func (s *Server) getDoneChan() <-chan struct{} { - s.mu.Lock() - defer s.mu.Unlock() - return s.getDoneChanLocked() -} - -func (s *Server) getDoneChanLocked() chan struct{} { - if s.doneChan == nil { - s.doneChan = make(chan struct{}) - } - return s.doneChan -} - -func (s *Server) closeDoneChanLocked() { - ch := s.getDoneChanLocked() - select { - case <-ch: - // Already closed. Don't close again. - default: - // Safe to close here. We're the only closer, guarded - // by s.mu. - close(ch) - } + listenerGroup sync.WaitGroup } // Close immediately closes all active net.Listeners and any @@ -2664,11 +2703,19 @@ func (s *Server) closeDoneChanLocked() { // Close returns any error returned from closing the Server's // underlying Listener(s). func (srv *Server) Close() error { - srv.inShutdown.setTrue() + srv.inShutdown.Store(true) srv.mu.Lock() defer srv.mu.Unlock() - srv.closeDoneChanLocked() err := srv.closeListenersLocked() + + // Unlock srv.mu while waiting for listenerGroup. + // The group Add and Done calls are made with srv.mu held, + // to avoid adding a new listener in the window between + // us setting inShutdown above and waiting here. + srv.mu.Unlock() + srv.listenerGroup.Wait() + srv.mu.Lock() + for c := range srv.activeConn { c.rwc.Close() delete(srv.activeConn, c) @@ -2706,15 +2753,15 @@ const shutdownPollIntervalMax = 500 * time.Millisecond // Once Shutdown has been called on a server, it may not be reused; // future calls to methods such as Serve will return ErrServerClosed. func (srv *Server) Shutdown(ctx context.Context) error { - srv.inShutdown.setTrue() + srv.inShutdown.Store(true) srv.mu.Lock() lnerr := srv.closeListenersLocked() - srv.closeDoneChanLocked() for _, f := range srv.onShutdown { go f() } srv.mu.Unlock() + srv.listenerGroup.Wait() pollIntervalBase := time.Millisecond nextPollInterval := func() time.Duration { @@ -2731,7 +2778,7 @@ func (srv *Server) Shutdown(ctx context.Context) error { timer := time.NewTimer(nextPollInterval()) defer timer.Stop() for { - if srv.closeIdleConns() && srv.numListeners() == 0 { + if srv.closeIdleConns() { return lnerr } select { @@ -2754,12 +2801,6 @@ func (srv *Server) RegisterOnShutdown(f func()) { srv.mu.Unlock() } -func (s *Server) numListeners() int { - s.mu.Lock() - defer s.mu.Unlock() - return len(s.listeners) -} - // closeIdleConns closes all idle connections and reports whether the // server is quiescent. func (s *Server) closeIdleConns() bool { @@ -2859,7 +2900,7 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { if handler == nil { handler = DefaultServeMux } - if req.RequestURI == "*" && req.Method == "OPTIONS" { + if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } @@ -3000,10 +3041,8 @@ func (srv *Server) Serve(l net.Listener) error { for { rw, err := l.Accept() if err != nil { - select { - case <-srv.getDoneChan(): + if srv.shuttingDown() { return ErrServerClosed - default: } if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { @@ -3094,8 +3133,10 @@ func (s *Server) trackListener(ln *net.Listener, add bool) bool { return false } s.listeners[ln] = struct{}{} + s.listenerGroup.Add(1) } else { delete(s.listeners, ln) + s.listenerGroup.Done() } return true } @@ -3132,7 +3173,7 @@ func (s *Server) doKeepAlives() bool { } func (s *Server) shuttingDown() bool { - return s.inShutdown.isSet() + return s.inShutdown.Load() } // SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled. @@ -3152,7 +3193,7 @@ func (srv *Server) SetKeepAlivesEnabled(v bool) { // TODO: Issue 26303: close HTTP/2 conns as soon as they become idle. } -func (s *Server) logf(format string, args ...interface{}) { +func (s *Server) logf(format string, args ...any) { if s.ErrorLog != nil { s.ErrorLog.Printf(format, args...) } else { @@ -3163,7 +3204,7 @@ func (s *Server) logf(format string, args ...interface{}) { // logf prints to the ErrorLog of the *Server associated with request r // via ServerContextKey. If there's no associated server, or if ErrorLog // is nil, logging is done via the log package's standard logger. -func logf(r *Request, format string, args ...interface{}) { +func logf(r *Request, format string, args ...any) { s, _ := r.Context().Value(ServerContextKey).(*Server) if s != nil && s.ErrorLog != nil { s.ErrorLog.Printf(format, args...) @@ -3259,7 +3300,7 @@ func (srv *Server) onceSetNextProtoDefaults_Serve() { // configured otherwise. (by setting srv.TLSNextProto non-nil) // It must only be called via srv.nextProtoOnce (use srv.setupHTTP2_*). func (srv *Server) onceSetNextProtoDefaults() { - if omitBundledHTTP2 || strings.Contains(os.Getenv("GODEBUG"), "http2server=0") { + if omitBundledHTTP2 || godebug.Get("http2server") == "0" { return } // Enable HTTP/2 by default if the user hasn't otherwise @@ -3326,7 +3367,7 @@ func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { h: make(Header), req: r, } - panicChan := make(chan interface{}, 1) + panicChan := make(chan any, 1) go func() { defer func() { if p := recover(); p != nil { @@ -3354,9 +3395,15 @@ func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { case <-ctx.Done(): tw.mu.Lock() defer tw.mu.Unlock() - w.WriteHeader(StatusServiceUnavailable) - io.WriteString(w, h.errorBody()) - tw.timedOut = true + switch err := ctx.Err(); err { + case context.DeadlineExceeded: + w.WriteHeader(StatusServiceUnavailable) + io.WriteString(w, h.errorBody()) + tw.err = ErrHandlerTimeout + default: + w.WriteHeader(StatusServiceUnavailable) + tw.err = err + } } } @@ -3367,7 +3414,7 @@ type timeoutWriter struct { req *Request mu sync.Mutex - timedOut bool + err error wroteHeader bool code int } @@ -3387,8 +3434,8 @@ func (tw *timeoutWriter) Header() Header { return tw.h } func (tw *timeoutWriter) Write(p []byte) (int, error) { tw.mu.Lock() defer tw.mu.Unlock() - if tw.timedOut { - return 0, ErrHandlerTimeout + if tw.err != nil { + return 0, tw.err } if !tw.wroteHeader { tw.writeHeaderLocked(StatusOK) @@ -3400,7 +3447,7 @@ func (tw *timeoutWriter) writeHeaderLocked(code int) { checkWriteHeaderCode(code) switch { - case tw.timedOut: + case tw.err != nil: return case tw.wroteHeader: if tw.req != nil { @@ -3567,3 +3614,12 @@ func tlsRecordHeaderLooksLikeHTTP(hdr [5]byte) bool { } return false } + +// MaxBytesHandler returns a Handler that runs h with its ResponseWriter and Request.Body wrapped by a MaxBytesReader. +func MaxBytesHandler(h Handler, n int64) Handler { + return HandlerFunc(func(w ResponseWriter, r *Request) { + r2 := *r + r2.Body = MaxBytesReader(w, r.Body, n) + h.ServeHTTP(w, &r2) + }) +} diff --git a/src/net/http/server_test.go b/src/net/http/server_test.go index 0132f3ba5fbd77..d17c5c1e7ef5e6 100644 --- a/src/net/http/server_test.go +++ b/src/net/http/server_test.go @@ -9,8 +9,61 @@ package http import ( "fmt" "testing" + "time" ) +func TestServerTLSHandshakeTimeout(t *testing.T) { + tests := []struct { + s *Server + want time.Duration + }{ + { + s: &Server{}, + want: 0, + }, + { + s: &Server{ + ReadTimeout: -1, + }, + want: 0, + }, + { + s: &Server{ + ReadTimeout: 5 * time.Second, + }, + want: 5 * time.Second, + }, + { + s: &Server{ + ReadTimeout: 5 * time.Second, + WriteTimeout: -1, + }, + want: 5 * time.Second, + }, + { + s: &Server{ + ReadTimeout: 5 * time.Second, + WriteTimeout: 4 * time.Second, + }, + want: 4 * time.Second, + }, + { + s: &Server{ + ReadTimeout: 5 * time.Second, + ReadHeaderTimeout: 2 * time.Second, + WriteTimeout: 4 * time.Second, + }, + want: 2 * time.Second, + }, + } + for i, tt := range tests { + got := tt.s.tlsHandshakeTimeout() + if got != tt.want { + t.Errorf("%d. got %v; want %v", i, got, tt.want) + } + } +} + func BenchmarkServerMatch(b *testing.B) { fn := func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "OK") diff --git a/src/net/http/sniff.go b/src/net/http/sniff.go index 67a7151b0cc1fb..ac18ab979d06e5 100644 --- a/src/net/http/sniff.go +++ b/src/net/http/sniff.go @@ -128,11 +128,6 @@ var sniffSignatures = []sniffSig{ // Audio and Video types // Enforce the pattern match ordering as prescribed in // https://mimesniff.spec.whatwg.org/#matching-an-audio-or-video-type-pattern - &maskedSig{ - mask: []byte("\xFF\xFF\xFF\xFF"), - pat: []byte(".snd"), - ct: "audio/basic", - }, &maskedSig{ mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"), pat: []byte("FORM\x00\x00\x00\x00AIFF"), diff --git a/src/net/http/status.go b/src/net/http/status.go index 286315f6395a8e..cd90877ef05fad 100644 --- a/src/net/http/status.go +++ b/src/net/http/status.go @@ -7,68 +7,68 @@ package http // HTTP status codes as registered with IANA. // See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml const ( - StatusContinue = 100 // RFC 7231, 6.2.1 - StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 + StatusContinue = 100 // RFC 9110, 15.2.1 + StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2 StatusProcessing = 102 // RFC 2518, 10.1 StatusEarlyHints = 103 // RFC 8297 - 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 + StatusOK = 200 // RFC 9110, 15.3.1 + StatusCreated = 201 // RFC 9110, 15.3.2 + StatusAccepted = 202 // RFC 9110, 15.3.3 + StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4 + StatusNoContent = 204 // RFC 9110, 15.3.5 + StatusResetContent = 205 // RFC 9110, 15.3.6 + StatusPartialContent = 206 // RFC 9110, 15.3.7 StatusMultiStatus = 207 // RFC 4918, 11.1 StatusAlreadyReported = 208 // RFC 5842, 7.1 StatusIMUsed = 226 // RFC 3229, 10.4.1 - 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 + StatusMultipleChoices = 300 // RFC 9110, 15.4.1 + StatusMovedPermanently = 301 // RFC 9110, 15.4.2 + StatusFound = 302 // RFC 9110, 15.4.3 + StatusSeeOther = 303 // RFC 9110, 15.4.4 + StatusNotModified = 304 // RFC 9110, 15.4.5 + StatusUseProxy = 305 // RFC 9110, 15.4.6 + _ = 306 // RFC 9110, 15.4.7 (Unused) + StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8 + StatusPermanentRedirect = 308 // RFC 9110, 15.4.9 - 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 - StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - StatusUnprocessableEntity = 422 // RFC 4918, 11.2 + StatusBadRequest = 400 // RFC 9110, 15.5.1 + StatusUnauthorized = 401 // RFC 9110, 15.5.2 + StatusPaymentRequired = 402 // RFC 9110, 15.5.3 + StatusForbidden = 403 // RFC 9110, 15.5.4 + StatusNotFound = 404 // RFC 9110, 15.5.5 + StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6 + StatusNotAcceptable = 406 // RFC 9110, 15.5.7 + StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8 + StatusRequestTimeout = 408 // RFC 9110, 15.5.9 + StatusConflict = 409 // RFC 9110, 15.5.10 + StatusGone = 410 // RFC 9110, 15.5.11 + StatusLengthRequired = 411 // RFC 9110, 15.5.12 + StatusPreconditionFailed = 412 // RFC 9110, 15.5.13 + StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14 + StatusRequestURITooLong = 414 // RFC 9110, 15.5.15 + StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16 + StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 + StatusExpectationFailed = 417 // RFC 9110, 15.5.18 + StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused) + StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20 + StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21 StatusLocked = 423 // RFC 4918, 11.3 StatusFailedDependency = 424 // RFC 4918, 11.4 StatusTooEarly = 425 // RFC 8470, 5.2. - StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 + StatusUpgradeRequired = 426 // RFC 9110, 15.5.22 StatusPreconditionRequired = 428 // RFC 6585, 3 StatusTooManyRequests = 429 // RFC 6585, 4 StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - 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 + StatusInternalServerError = 500 // RFC 9110, 15.6.1 + StatusNotImplemented = 501 // RFC 9110, 15.6.2 + StatusBadGateway = 502 // RFC 9110, 15.6.3 + StatusServiceUnavailable = 503 // RFC 9110, 15.6.4 + StatusGatewayTimeout = 504 // RFC 9110, 15.6.5 + StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6 StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 StatusInsufficientStorage = 507 // RFC 4918, 11.5 StatusLoopDetected = 508 // RFC 5842, 7.2 @@ -76,77 +76,135 @@ const ( StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 ) -var statusText = map[int]string{ - StatusContinue: "Continue", - StatusSwitchingProtocols: "Switching Protocols", - StatusProcessing: "Processing", - StatusEarlyHints: "Early Hints", - - StatusOK: "OK", - StatusCreated: "Created", - StatusAccepted: "Accepted", - StatusNonAuthoritativeInfo: "Non-Authoritative Information", - StatusNoContent: "No Content", - StatusResetContent: "Reset Content", - StatusPartialContent: "Partial Content", - StatusMultiStatus: "Multi-Status", - StatusAlreadyReported: "Already Reported", - StatusIMUsed: "IM Used", - - StatusMultipleChoices: "Multiple Choices", - StatusMovedPermanently: "Moved Permanently", - StatusFound: "Found", - StatusSeeOther: "See Other", - StatusNotModified: "Not Modified", - StatusUseProxy: "Use Proxy", - StatusTemporaryRedirect: "Temporary Redirect", - StatusPermanentRedirect: "Permanent Redirect", - - StatusBadRequest: "Bad Request", - StatusUnauthorized: "Unauthorized", - StatusPaymentRequired: "Payment Required", - StatusForbidden: "Forbidden", - StatusNotFound: "Not Found", - StatusMethodNotAllowed: "Method Not Allowed", - StatusNotAcceptable: "Not Acceptable", - StatusProxyAuthRequired: "Proxy Authentication Required", - StatusRequestTimeout: "Request Timeout", - StatusConflict: "Conflict", - StatusGone: "Gone", - StatusLengthRequired: "Length Required", - StatusPreconditionFailed: "Precondition Failed", - StatusRequestEntityTooLarge: "Request Entity Too Large", - StatusRequestURITooLong: "Request URI Too Long", - StatusUnsupportedMediaType: "Unsupported Media Type", - StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", - StatusExpectationFailed: "Expectation Failed", - StatusTeapot: "I'm a teapot", - StatusMisdirectedRequest: "Misdirected Request", - StatusUnprocessableEntity: "Unprocessable Entity", - StatusLocked: "Locked", - StatusFailedDependency: "Failed Dependency", - StatusTooEarly: "Too Early", - StatusUpgradeRequired: "Upgrade Required", - StatusPreconditionRequired: "Precondition Required", - StatusTooManyRequests: "Too Many Requests", - StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large", - StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons", - - StatusInternalServerError: "Internal Server Error", - StatusNotImplemented: "Not Implemented", - StatusBadGateway: "Bad Gateway", - 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", -} - // StatusText returns a text for the HTTP status code. It returns the empty // string if the code is unknown. func StatusText(code int) string { - return statusText[code] + switch code { + case StatusContinue: + return "Continue" + case StatusSwitchingProtocols: + return "Switching Protocols" + case StatusProcessing: + return "Processing" + case StatusEarlyHints: + return "Early Hints" + case StatusOK: + return "OK" + case StatusCreated: + return "Created" + case StatusAccepted: + return "Accepted" + case StatusNonAuthoritativeInfo: + return "Non-Authoritative Information" + case StatusNoContent: + return "No Content" + case StatusResetContent: + return "Reset Content" + case StatusPartialContent: + return "Partial Content" + case StatusMultiStatus: + return "Multi-Status" + case StatusAlreadyReported: + return "Already Reported" + case StatusIMUsed: + return "IM Used" + case StatusMultipleChoices: + return "Multiple Choices" + case StatusMovedPermanently: + return "Moved Permanently" + case StatusFound: + return "Found" + case StatusSeeOther: + return "See Other" + case StatusNotModified: + return "Not Modified" + case StatusUseProxy: + return "Use Proxy" + case StatusTemporaryRedirect: + return "Temporary Redirect" + case StatusPermanentRedirect: + return "Permanent Redirect" + case StatusBadRequest: + return "Bad Request" + case StatusUnauthorized: + return "Unauthorized" + case StatusPaymentRequired: + return "Payment Required" + case StatusForbidden: + return "Forbidden" + case StatusNotFound: + return "Not Found" + case StatusMethodNotAllowed: + return "Method Not Allowed" + case StatusNotAcceptable: + return "Not Acceptable" + case StatusProxyAuthRequired: + return "Proxy Authentication Required" + case StatusRequestTimeout: + return "Request Timeout" + case StatusConflict: + return "Conflict" + case StatusGone: + return "Gone" + case StatusLengthRequired: + return "Length Required" + case StatusPreconditionFailed: + return "Precondition Failed" + case StatusRequestEntityTooLarge: + return "Request Entity Too Large" + case StatusRequestURITooLong: + return "Request URI Too Long" + case StatusUnsupportedMediaType: + return "Unsupported Media Type" + case StatusRequestedRangeNotSatisfiable: + return "Requested Range Not Satisfiable" + case StatusExpectationFailed: + return "Expectation Failed" + case StatusTeapot: + return "I'm a teapot" + case StatusMisdirectedRequest: + return "Misdirected Request" + case StatusUnprocessableEntity: + return "Unprocessable Entity" + case StatusLocked: + return "Locked" + case StatusFailedDependency: + return "Failed Dependency" + case StatusTooEarly: + return "Too Early" + case StatusUpgradeRequired: + return "Upgrade Required" + case StatusPreconditionRequired: + return "Precondition Required" + case StatusTooManyRequests: + return "Too Many Requests" + case StatusRequestHeaderFieldsTooLarge: + return "Request Header Fields Too Large" + case StatusUnavailableForLegalReasons: + return "Unavailable For Legal Reasons" + case StatusInternalServerError: + return "Internal Server Error" + case StatusNotImplemented: + return "Not Implemented" + case StatusBadGateway: + return "Bad Gateway" + case StatusServiceUnavailable: + return "Service Unavailable" + case StatusGatewayTimeout: + return "Gateway Timeout" + case StatusHTTPVersionNotSupported: + return "HTTP Version Not Supported" + case StatusVariantAlsoNegotiates: + return "Variant Also Negotiates" + case StatusInsufficientStorage: + return "Insufficient Storage" + case StatusLoopDetected: + return "Loop Detected" + case StatusNotExtended: + return "Not Extended" + case StatusNetworkAuthenticationRequired: + return "Network Authentication Required" + default: + return "" + } } diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go index 85c2e5a360d686..4583c6b453d085 100644 --- a/src/net/http/transfer.go +++ b/src/net/http/transfer.go @@ -73,7 +73,7 @@ type transferWriter struct { ByteReadCh chan readResult // non-nil if probeRequestBody called } -func newTransferWriter(r interface{}) (t *transferWriter, err error) { +func newTransferWriter(r any) (t *transferWriter, err error) { t = &transferWriter{} // Extract relevant fields @@ -196,10 +196,11 @@ func (t *transferWriter) shouldSendChunkedRequestBody() bool { // headers before the pipe is fed data), we need to be careful and bound how // long we wait for it. This delay will only affect users if all the following // are true: -// * the request body blocks -// * the content length is not set (or set to -1) -// * the method doesn't usually have a body (GET, HEAD, DELETE, ...) -// * there is no transfer-encoding=chunked already set. +// - the request body blocks +// - the content length is not set (or set to -1) +// - the method doesn't usually have a body (GET, HEAD, DELETE, ...) +// - there is no transfer-encoding=chunked already set. +// // In other words, this delay will not normally affect anybody, and there // are workarounds if it does. func (t *transferWriter) probeRequestBody() { @@ -212,6 +213,7 @@ func (t *transferWriter) probeRequestBody() { rres.b = buf[0] } t.ByteReadCh <- rres + close(t.ByteReadCh) }(t.Body) timer := time.NewTimer(200 * time.Millisecond) select { @@ -420,8 +422,8 @@ func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err // // This function is only intended for use in writeBody. func (t *transferWriter) unwrapBody() io.Reader { - if reflect.TypeOf(t.Body) == nopCloserType { - return reflect.ValueOf(t.Body).Field(0).Interface().(io.Reader) + if r, ok := unwrapNopCloser(t.Body); ok { + return r } if r, ok := t.Body.(*readTrackingBody); ok { r.didRead = true @@ -466,6 +468,7 @@ func bodyAllowedForStatus(status int) bool { var ( suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"} suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"} + excludedHeadersNoBody = map[string]bool{"Content-Length": true, "Transfer-Encoding": true} ) func suppressedHeaders(status int) []string { @@ -480,7 +483,7 @@ func suppressedHeaders(status int) []string { } // msg is *Request or *Response. -func readTransfer(msg interface{}, r *bufio.Reader) (err error) { +func readTransfer(msg any, r *bufio.Reader) (err error) { t := &transferReader{RequestMethod: "GET"} // Unify input @@ -639,7 +642,7 @@ func (t *transferReader) parseTransferEncoding() error { if len(raw) != 1 { return &unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", raw)} } - if !ascii.EqualFold(textproto.TrimString(raw[0]), "chunked") { + if !ascii.EqualFold(raw[0], "chunked") { return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])} } @@ -808,7 +811,7 @@ func fixTrailer(header Header, chunked bool) (Header, error) { // and then reads the trailer if necessary. type body struct { src io.Reader - hdr interface{} // non-nil (Response or Request) value means read trailer + hdr any // non-nil (Response or Request) value means read trailer r *bufio.Reader // underlying wire-format reader for the trailer closing bool // is the connection to be closed after reading body? doEarlyClose bool // whether Close should stop early @@ -1029,7 +1032,7 @@ func (b *body) registerOnHitEOF(fn func()) { b.onHitEOF = fn } -// bodyLocked is a io.Reader reading from a *body when its mutex is +// bodyLocked is an io.Reader reading from a *body when its mutex is // already held. type bodyLocked struct { b *body @@ -1072,10 +1075,28 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) { if n == 1 { p[0] = rres.b } + if err == nil { + err = io.EOF + } return } var nopCloserType = reflect.TypeOf(io.NopCloser(nil)) +var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct { + io.Reader + io.WriterTo +}{})) + +// unwrapNopCloser return the underlying reader and true if r is a NopCloser +// else it return false +func unwrapNopCloser(r io.Reader) (underlyingReader io.Reader, isNopCloser bool) { + switch reflect.TypeOf(r) { + case nopCloserType, nopCloserWriterToType: + return reflect.ValueOf(r).Field(0).Interface().(io.Reader), true + default: + return nil, false + } +} // isKnownInMemoryReader reports whether r is a type known to not // block on Read. Its caller uses this as an optional optimization to @@ -1085,8 +1106,8 @@ func isKnownInMemoryReader(r io.Reader) bool { case *bytes.Reader, *bytes.Buffer, *strings.Reader: return true } - if reflect.TypeOf(r) == nopCloserType { - return isKnownInMemoryReader(reflect.ValueOf(r).Field(0).Interface().(io.Reader)) + if r, ok := unwrapNopCloser(r); ok { + return isKnownInMemoryReader(r) } if r, ok := r.(*readTrackingBody); ok { return isKnownInMemoryReader(r.ReadCloser) diff --git a/src/net/http/transfer_test.go b/src/net/http/transfer_test.go index f0c28b26299a8d..5e0df896d80c81 100644 --- a/src/net/http/transfer_test.go +++ b/src/net/http/transfer_test.go @@ -267,7 +267,7 @@ func TestTransferWriterWriteBodyReaderTypes(t *testing.T) { } if tc.expectedReader != actualReader { - t.Fatalf("got reader %T want %T", actualReader, tc.expectedReader) + t.Fatalf("got reader %s want %s", actualReader, tc.expectedReader) } } diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 309194e8e52c86..1854daba8a71a8 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -17,6 +17,7 @@ import ( "crypto/tls" "errors" "fmt" + "internal/godebug" "io" "log" "net" @@ -24,7 +25,6 @@ import ( "net/http/internal/ascii" "net/textproto" "net/url" - "os" "reflect" "strings" "sync" @@ -42,10 +42,10 @@ import ( // $no_proxy) environment variables. var DefaultTransport RoundTripper = &Transport{ Proxy: ProxyFromEnvironment, - DialContext: (&net.Dialer{ + DialContext: defaultTransportDialContext(&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, - }).DialContext, + }), ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, @@ -360,7 +360,7 @@ func (t *Transport) hasCustomTLSDialer() bool { // It must be called via t.nextProtoOnce.Do. func (t *Transport) onceSetNextProtoDefaults() { t.tlsNextProtoWasNil = (t.TLSNextProto == nil) - if strings.Contains(os.Getenv("GODEBUG"), "http2client=0") { + if godebug.Get("http2client") == "0" { return } @@ -422,8 +422,8 @@ func (t *Transport) onceSetNextProtoDefaults() { // ProxyFromEnvironment returns the URL of the proxy to use for a // given request, as indicated by the environment variables // HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions -// thereof). HTTPS_PROXY takes precedence over HTTP_PROXY for https -// requests. +// thereof). Requests use the proxy from the environment variable +// matching their scheme, unless excluded by NO_PROXY. // // The environment values may be either a complete URL or a // "host[:port]", in which case the "http" scheme is assumed. @@ -525,7 +525,8 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { req.closeBody() - return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k) + // Don't include the value in the error, because it may be sensitive. + return nil, fmt.Errorf("net/http: invalid header field value for %q", k) } } } @@ -606,6 +607,9 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { } else if !pconn.shouldRetryRequest(req, err) { // Issue 16465: return underlying net.Conn.Read error from peek, // as we've historically done. + if e, ok := err.(nothingWrittenError); ok { + err = e.error + } if e, ok := err.(transportReadFromServerError); ok { err = e.err } @@ -1715,12 +1719,12 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *pers return nil, err } if resp.StatusCode != 200 { - f := strings.SplitN(resp.Status, " ", 2) + _, text, ok := strings.Cut(resp.Status, " ") conn.Close() - if len(f) < 2 { + if !ok { return nil, errors.New("unknown status code") } - return nil, errors.New(f[1]) + return nil, errors.New(text) } } @@ -1792,7 +1796,6 @@ var _ io.ReaderFrom = (*persistConnWriter)(nil) // socks5://proxy.com|https|foo.com socks5 to proxy, then https to foo.com // https://proxy.com|https|foo.com https to proxy, then CONNECT to foo.com // https://proxy.com|http https to proxy, http to anywhere after that -// type connectMethod struct { _ incomparable proxyURL *url.URL // nil for no proxy, else full proxy URL @@ -2032,6 +2035,9 @@ func (pc *persistConn) mapRoundTripError(req *transportRequest, startBytesWritte } if _, ok := err.(transportReadFromServerError); ok { + if pc.nwrite == startBytesWritten { + return nothingWrittenError{err} + } // Don't decorate return err } @@ -2481,7 +2487,7 @@ type requestAndChan struct { callerGone <-chan struct{} // closed when roundTrip caller has returned } -// A writeRequest is sent by the readLoop's goroutine to the +// A writeRequest is sent by the caller's goroutine to the // writeLoop's goroutine to write a request while the read loop // concurrently waits on both the write response and the server's // reply. @@ -2668,8 +2674,8 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err // a t.Logf func. See export_test.go's Request.WithT method. type tLogKey struct{} -func (tr *transportRequest) logf(format string, args ...interface{}) { - if logf, ok := tr.Request.Context().Value(tLogKey{}).(func(string, ...interface{})); ok { +func (tr *transportRequest) logf(format string, args ...any) { + if logf, ok := tr.Request.Context().Value(tLogKey{}).(func(string, ...any)); ok { logf(time.Now().Format(time.RFC3339Nano)+": "+format, args...) } } diff --git a/src/net/http/transport_default_js.go b/src/net/http/transport_default_js.go new file mode 100644 index 00000000000000..c07d35ef861ebc --- /dev/null +++ b/src/net/http/transport_default_js.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build js && wasm +// +build js,wasm + +package http + +import ( + "context" + "net" +) + +func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { + return nil +} diff --git a/src/net/http/transport_default_other.go b/src/net/http/transport_default_other.go new file mode 100644 index 00000000000000..8a2f1cc42bbcf5 --- /dev/null +++ b/src/net/http/transport_default_other.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !(js && wasm) +// +build !js !wasm + +package http + +import ( + "context" + "net" +) + +func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { + return dialer.DialContext +} diff --git a/src/net/http/transport_internal_test.go b/src/net/http/transport_internal_test.go index 1cce27235dfdc3..2ed637e9f036ca 100644 --- a/src/net/http/transport_internal_test.go +++ b/src/net/http/transport_internal_test.go @@ -52,8 +52,8 @@ func TestTransportPersistConnReadLoopEOF(t *testing.T) { conn.Close() // simulate the server hanging up on the client _, err = pc.roundTrip(treq) - if !isTransportReadFromServerError(err) && err != errServerClosedIdle { - t.Errorf("roundTrip = %#v, %v; want errServerClosedIdle or transportReadFromServerError", err, err) + if !isNothingWrittenError(err) && !isTransportReadFromServerError(err) && err != errServerClosedIdle { + t.Errorf("roundTrip = %#v, %v; want errServerClosedIdle, transportReadFromServerError, or nothingWrittenError", err, err) } <-pc.closech @@ -63,6 +63,11 @@ func TestTransportPersistConnReadLoopEOF(t *testing.T) { } } +func isNothingWrittenError(err error) bool { + _, ok := err.(nothingWrittenError) + return ok +} + func isTransportReadFromServerError(err error) bool { _, ok := err.(transportReadFromServerError) return ok diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index eeaa49264450de..e1f2a24f46cbf3 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -48,7 +48,7 @@ import ( ) // TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close -// and then verify that the final 2 responses get errors back. +// and then verify that the final 2 responses get errors back. // hostPortHandler writes back the client's "host:port". var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) { @@ -57,6 +57,12 @@ var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) { } w.Header().Set("X-Saw-Close", fmt.Sprint(r.Close)) w.Write([]byte(r.RemoteAddr)) + + // Include the address of the net.Conn in addition to the RemoteAddr, + // in case kernels reuse source ports quickly (see Issue 52450) + if c, ok := ResponseWriterConnForTesting(w); ok { + fmt.Fprintf(w, ", %T %p", c, c) + } }) // testCloseConn is a net.Conn tracked by a testConnSet. @@ -240,6 +246,12 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) { connSet.check(t) } +// TestTransportConnectionCloseOnRequest tests that the Transport's doesn't reuse +// an underlying TCP connection after making an http.Request with Request.Close set. +// +// It tests the behavior by making an HTTP request to a server which +// describes the source source connection it got (remote port number + +// address of its net.Conn). func TestTransportConnectionCloseOnRequest(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(hostPortHandler) @@ -250,7 +262,7 @@ func TestTransportConnectionCloseOnRequest(t *testing.T) { c := ts.Client() tr := c.Transport.(*Transport) tr.Dial = testDial - for _, connectionClose := range []bool{false, true} { + for _, reqClose := range []bool{false, true} { fetch := func(n int) string { req := new(Request) var err error @@ -262,29 +274,37 @@ func TestTransportConnectionCloseOnRequest(t *testing.T) { req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 - req.Close = connectionClose + req.Close = reqClose res, err := c.Do(req) if err != nil { - t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err) + t.Fatalf("error in Request.Close=%v, req #%d, Do: %v", reqClose, n, err) } - if got, want := res.Header.Get("X-Saw-Close"), fmt.Sprint(connectionClose); got != want { - t.Errorf("For connectionClose = %v; handler's X-Saw-Close was %v; want %v", - connectionClose, got, !connectionClose) + if got, want := res.Header.Get("X-Saw-Close"), fmt.Sprint(reqClose); got != want { + t.Errorf("for Request.Close = %v; handler's X-Saw-Close was %v; want %v", + reqClose, got, !reqClose) } body, err := io.ReadAll(res.Body) if err != nil { - t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err) + t.Fatalf("for Request.Close=%v, on request %v/2: ReadAll: %v", reqClose, n, err) } return string(body) } body1 := fetch(1) body2 := fetch(2) - bodiesDiffer := body1 != body2 - if bodiesDiffer != connectionClose { - t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", - connectionClose, bodiesDiffer, body1, body2) + + got := 1 + if body1 != body2 { + got++ + } + want := 1 + if reqClose { + want = 2 + } + if got != want { + t.Errorf("for Request.Close=%v: server saw %v unique connections, wanted %v\n\nbodies were: %q and %q", + reqClose, got, want, body1, body2) } tr.CloseIdleConnections() @@ -776,7 +796,7 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) { c := ts.Client() fetch := func(n, retries int) string { - condFatalf := func(format string, arg ...interface{}) { + condFatalf := func(format string, arg ...any) { if retries <= 0 { t.Fatalf(format, arg...) } @@ -2099,17 +2119,21 @@ func TestTransportConcurrency(t *testing.T) { for req := range reqs { res, err := c.Get(ts.URL + "/?echo=" + req) if err != nil { - t.Errorf("error on req %s: %v", req, err) + if runtime.GOOS == "netbsd" && strings.HasSuffix(err.Error(), ": connection reset by peer") { + // https://go.dev/issue/52168: this test was observed to fail with + // ECONNRESET errors in Dial on various netbsd builders. + t.Logf("error on req %s: %v", req, err) + t.Logf("(see https://go.dev/issue/52168)") + } else { + t.Errorf("error on req %s: %v", req, err) + } wg.Done() continue } all, err := io.ReadAll(res.Body) if err != nil { t.Errorf("read error on req %s: %v", req, err) - wg.Done() - continue - } - if string(all) != req { + } else if string(all) != req { t.Errorf("body of req %s = %q; want %q", req, all, req) } res.Body.Close() @@ -2420,7 +2444,7 @@ func TestTransportCancelRequestInDial(t *testing.T) { if testing.Short() { t.Skip("skipping test in -short mode") } - var logbuf bytes.Buffer + var logbuf strings.Builder eventLog := log.New(&logbuf, "", 0) unblockDial := make(chan bool) @@ -2907,7 +2931,7 @@ func TestTransportIgnore1xxResponses(t *testing.T) { defer cst.close() cst.tr.DisableKeepAlives = true // prevent log spam; our test server is hanging up anyway - var got bytes.Buffer + var got strings.Builder req, _ := NewRequest("GET", cst.ts.URL, nil) req = req.WithContext(httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ @@ -2925,7 +2949,7 @@ func TestTransportIgnore1xxResponses(t *testing.T) { res.Write(&got) want := "1xx: code=123, header=map[Foo:[bar]]\nHTTP/1.1 200 OK\r\nContent-Length: 5\r\nBar: baz\r\n\r\nHello" if got.String() != want { - t.Errorf(" got: %q\nwant: %q\n", got.Bytes(), want) + t.Errorf(" got: %q\nwant: %q\n", got.String(), want) } } @@ -2991,7 +3015,7 @@ type proxyFromEnvTest struct { } func (t proxyFromEnvTest) String() string { - var buf bytes.Buffer + var buf strings.Builder space := func() { if buf.Len() > 0 { buf.WriteByte(' ') @@ -3435,6 +3459,7 @@ func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) } // - we reused a keep-alive connection // - we haven't yet received any header data // - either we wrote no bytes to the server, or the request is idempotent +// // This automatically prevents an infinite resend loop because we'll run out of // the cached keep-alive connections eventually. func TestRetryRequestsOnError(t *testing.T) { @@ -3512,9 +3537,9 @@ func TestRetryRequestsOnError(t *testing.T) { var ( mu sync.Mutex - logbuf bytes.Buffer + logbuf strings.Builder ) - logf := func(format string, args ...interface{}) { + logf := func(format string, args ...any) { mu.Lock() defer mu.Unlock() fmt.Fprintf(&logbuf, format, args...) @@ -4490,8 +4515,8 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { cst.tr.ExpectContinueTimeout = 1 * time.Second var mu sync.Mutex // guards buf - var buf bytes.Buffer - logf := func(format string, args ...interface{}) { + var buf strings.Builder + logf := func(format string, args ...any) { mu.Lock() defer mu.Unlock() fmt.Fprintf(&buf, format, args...) @@ -4649,8 +4674,8 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) { func TestTransportEventTraceTLSVerify(t *testing.T) { var mu sync.Mutex - var buf bytes.Buffer - logf := func(format string, args ...interface{}) { + var buf strings.Builder + logf := func(format string, args ...any) { mu.Lock() defer mu.Unlock() fmt.Fprintf(&buf, format, args...) @@ -4735,8 +4760,8 @@ func TestTransportEventTraceRealDNS(t *testing.T) { c := &Client{Transport: tr} var mu sync.Mutex // guards buf - var buf bytes.Buffer - logf := func(format string, args ...interface{}) { + var buf strings.Builder + logf := func(format string, args ...any) { mu.Lock() defer mu.Unlock() fmt.Fprintf(&buf, format, args...) @@ -5953,7 +5978,7 @@ func TestTransportIgnores408(t *testing.T) { // Not parallel. Relies on mutating the log package's global Output. defer log.SetOutput(log.Writer()) - var logout bytes.Buffer + var logout strings.Builder log.SetOutput(&logout) defer afterTest(t) @@ -6060,14 +6085,14 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { Method: " ", URL: u, }, - wantErr: "invalid method", + wantErr: `invalid method " "`, }, { name: "nil URL", req: &Request{ Method: "GET", }, - wantErr: "nil Request.URL", + wantErr: `nil Request.URL`, }, { name: "invalid header key", @@ -6076,7 +6101,7 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { Header: Header{"💡": {"emoji"}}, URL: u, }, - wantErr: "invalid header field name", + wantErr: `invalid header field name "💡"`, }, { name: "invalid header value", @@ -6085,7 +6110,7 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { Header: Header{"key": {"\x19"}}, URL: u, }, - wantErr: "invalid header field value", + wantErr: `invalid header field value for "key"`, }, { name: "non HTTP(s) scheme", @@ -6093,7 +6118,7 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { Method: "POST", URL: &url.URL{Scheme: "faux"}, }, - wantErr: "unsupported protocol scheme", + wantErr: `unsupported protocol scheme "faux"`, }, { name: "no Host in URL", @@ -6101,7 +6126,7 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { Method: "POST", URL: &url.URL{Scheme: "http"}, }, - wantErr: "no Host", + wantErr: `no Host in request URL`, }, } @@ -6117,8 +6142,8 @@ func TestTransportClosesBodyOnInvalidRequests(t *testing.T) { if !bc { t.Fatal("Expected body to have been closed") } - if g, w := err.Error(), tt.wantErr; !strings.Contains(g, w) { - t.Fatalf("Error mismatch\n\t%q\ndoes not contain\n\t%q", g, w) + if g, w := err.Error(), tt.wantErr; !strings.HasSuffix(g, w) { + t.Fatalf("Error mismatch: %q does not end with %q", g, w) } }) } @@ -6505,10 +6530,39 @@ func TestCancelRequestWhenSharingConnection(t *testing.T) { r2c := <-reqc cancel() - // Give the cancelation a moment to take effect, and then unblock the first request. + // Give the cancellation a moment to take effect, and then unblock the first request. time.Sleep(1 * time.Millisecond) close(idlec) close(r2c) wg.Wait() } + +func TestHandlerAbortRacesBodyRead(t *testing.T) { + setParallel(t) + defer afterTest(t) + + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + go io.Copy(io.Discard, req.Body) + panic(ErrAbortHandler) + })) + defer ts.Close() + + var wg sync.WaitGroup + for i := 0; i < 2; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 10; j++ { + const reqLen = 6 * 1024 * 1024 + req, _ := NewRequest("POST", ts.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen}) + req.ContentLength = reqLen + resp, _ := ts.Client().Transport.RoundTrip(req) + if resp != nil { + resp.Body.Close() + } + } + }() + } + wg.Wait() +} diff --git a/src/net/http/triv.go b/src/net/http/triv.go index 4dc62407c6d5ec..4c2160bbc30055 100644 --- a/src/net/http/triv.go +++ b/src/net/http/triv.go @@ -3,12 +3,10 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore package main import ( - "bytes" "expvar" "flag" "fmt" @@ -18,6 +16,7 @@ import ( "os" "os/exec" "strconv" + "strings" "sync" ) @@ -50,7 +49,7 @@ func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "GET": ctr.n++ case "POST": - buf := new(bytes.Buffer) + var buf strings.Builder io.Copy(buf, req.Body) body := buf.String() if n, err := strconv.Atoi(body); err != nil { @@ -119,7 +118,7 @@ func Logger(w http.ResponseWriter, req *http.Request) { http.Error(w, "oops", http.StatusNotFound) } -var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") +var webroot = flag.String("root", "", "web root directory") func main() { flag.Parse() @@ -129,11 +128,13 @@ func main() { expvar.Publish("counter", ctr) http.Handle("/counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) - http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) + if *webroot != "" { + http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) + } http.Handle("/chan", ChanCreate()) http.HandleFunc("/flags", FlagServer) http.HandleFunc("/args", ArgServer) http.HandleFunc("/go/hello", HelloServer) http.HandleFunc("/date", DateServer) - log.Fatal(http.ListenAndServe(":12345", nil)) + log.Fatal(http.ListenAndServe("localhost:12345", nil)) } diff --git a/src/net/interface.go b/src/net/interface.go index 0e5d3202c911ec..e1c9a2e2fff2e6 100644 --- a/src/net/interface.go +++ b/src/net/interface.go @@ -39,11 +39,12 @@ type Interface struct { type Flags uint const ( - FlagUp Flags = 1 << iota // interface is up + FlagUp Flags = 1 << iota // interface is administratively up FlagBroadcast // interface supports broadcast access capability FlagLoopback // interface is a loopback interface FlagPointToPoint // interface belongs to a point-to-point link FlagMulticast // interface supports multicast access capability + FlagRunning // interface is in running state ) var flagNames = []string{ @@ -52,6 +53,7 @@ var flagNames = []string{ "loopback", "pointtopoint", "multicast", + "running", } func (f Flags) String() string { diff --git a/src/net/interface_aix.go b/src/net/interface_aix.go index 49f78c2abbcbfa..f2e967b1e8804d 100644 --- a/src/net/interface_aix.go +++ b/src/net/interface_aix.go @@ -78,7 +78,7 @@ func interfaceTable(ifindex int) ([]Interface, error) { // Retrieve MTU ifr := &ifreq{} copy(ifr.Name[:], ifi.Name) - err = unix.Ioctl(sock, syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(ifr))) + err = unix.Ioctl(sock, syscall.SIOCGIFMTU, unsafe.Pointer(ifr)) if err != nil { return nil, err } @@ -101,6 +101,9 @@ func linkFlags(rawFlags int32) Flags { if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp } + if rawFlags&syscall.IFF_RUNNING != 0 { + f |= FlagRunning + } if rawFlags&syscall.IFF_BROADCAST != 0 { f |= FlagBroadcast } diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go index 7578b1a16a91a9..9b2b42addbb609 100644 --- a/src/net/interface_bsd.go +++ b/src/net/interface_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package net @@ -60,6 +59,9 @@ func linkFlags(rawFlags int) Flags { if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp } + if rawFlags&syscall.IFF_RUNNING != 0 { + f |= FlagRunning + } if rawFlags&syscall.IFF_BROADCAST != 0 { f |= FlagBroadcast } diff --git a/src/net/interface_bsd_test.go b/src/net/interface_bsd_test.go index 8d0d9c367198d0..ce59962f6108f4 100644 --- a/src/net/interface_bsd_test.go +++ b/src/net/interface_bsd_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/interface_bsdvar.go b/src/net/interface_bsdvar.go index 6230e0bfeec1d5..e9bea3d3798a06 100644 --- a/src/net/interface_bsdvar.go +++ b/src/net/interface_bsdvar.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || netbsd || openbsd -// +build dragonfly netbsd openbsd package net diff --git a/src/net/interface_freebsd.go b/src/net/interface_freebsd.go index 2b51fcb632a3f7..8536bd3cf6482b 100644 --- a/src/net/interface_freebsd.go +++ b/src/net/interface_freebsd.go @@ -11,16 +11,11 @@ import ( ) func interfaceMessages(ifindex int) ([]route.Message, error) { - typ := route.RIBType(syscall.NET_RT_IFLISTL) - rib, err := route.FetchRIB(syscall.AF_UNSPEC, typ, ifindex) + rib, err := route.FetchRIB(syscall.AF_UNSPEC, route.RIBTypeInterface, 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 nil, err } - return route.ParseRIB(typ, rib) + return route.ParseRIB(route.RIBTypeInterface, rib) } // interfaceMulticastAddrTable returns addresses for a specific diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go index 441ab2f8805a1f..9112ecc854c74c 100644 --- a/src/net/interface_linux.go +++ b/src/net/interface_linux.go @@ -99,6 +99,9 @@ func linkFlags(rawFlags uint32) Flags { if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp } + if rawFlags&syscall.IFF_RUNNING != 0 { + f |= FlagRunning + } if rawFlags&syscall.IFF_BROADCAST != 0 { f |= FlagBroadcast } diff --git a/src/net/interface_plan9.go b/src/net/interface_plan9.go index 957975c2655492..92b2eed2591bca 100644 --- a/src/net/interface_plan9.go +++ b/src/net/interface_plan9.go @@ -95,9 +95,9 @@ func readInterface(i int) (*Interface, error) { } } - ifc.Flags = FlagUp | FlagBroadcast | FlagMulticast + ifc.Flags = FlagUp | FlagRunning | FlagBroadcast | FlagMulticast } else { - ifc.Flags = FlagUp | FlagMulticast | FlagLoopback + ifc.Flags = FlagUp | FlagRunning | FlagMulticast | FlagLoopback } return ifc, nil diff --git a/src/net/interface_solaris.go b/src/net/interface_solaris.go index f8d1571b900311..32f503f45b29ba 100644 --- a/src/net/interface_solaris.go +++ b/src/net/interface_solaris.go @@ -37,6 +37,9 @@ func linkFlags(rawFlags int) Flags { if rawFlags&syscall.IFF_UP != 0 { f |= FlagUp } + if rawFlags&syscall.IFF_RUNNING != 0 { + f |= FlagRunning + } if rawFlags&syscall.IFF_BROADCAST != 0 { f |= FlagBroadcast } diff --git a/src/net/interface_stub.go b/src/net/interface_stub.go index efe67c24d3cde8..2d4475f63eeba3 100644 --- a/src/net/interface_stub.go +++ b/src/net/interface_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net diff --git a/src/net/interface_test.go b/src/net/interface_test.go index 754db36e3c4430..f6c98684181efb 100644 --- a/src/net/interface_test.go +++ b/src/net/interface_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net diff --git a/src/net/interface_unix_test.go b/src/net/interface_unix_test.go index 0d69fa5da4127d..92ec13a909ca72 100644 --- a/src/net/interface_unix_test.go +++ b/src/net/interface_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd package net diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go index 30e90b83c19d3b..22a13128499bcf 100644 --- a/src/net/interface_windows.go +++ b/src/net/interface_windows.go @@ -62,6 +62,7 @@ func interfaceTable(ifindex int) ([]Interface, error) { } if aa.OperStatus == windows.IfOperStatusUp { ifi.Flags |= FlagUp + ifi.Flags |= FlagRunning } // For now we need to infer link-layer service // capabilities from media types. diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go index 8af85d382eb093..c7c8d16d4c9a7e 100644 --- a/src/net/internal/socktest/main_test.go +++ b/src/net/internal/socktest/main_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 -// +build !js,!plan9 package socktest_test diff --git a/src/net/internal/socktest/main_unix_test.go b/src/net/internal/socktest/main_unix_test.go index 6aa8875b66cf6c..7d21f6f99f6baa 100644 --- a/src/net/internal/socktest/main_unix_test.go +++ b/src/net/internal/socktest/main_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package socktest_test diff --git a/src/net/internal/socktest/switch_posix.go b/src/net/internal/socktest/switch_posix.go index cda74e86399869..fcad4ceae31e8e 100644 --- a/src/net/internal/socktest/switch_posix.go +++ b/src/net/internal/socktest/switch_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package socktest diff --git a/src/net/internal/socktest/switch_stub.go b/src/net/internal/socktest/switch_stub.go index 5aa2ecec08fd31..8a2fc353f05816 100644 --- a/src/net/internal/socktest/switch_stub.go +++ b/src/net/internal/socktest/switch_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package socktest diff --git a/src/net/internal/socktest/switch_unix.go b/src/net/internal/socktest/switch_unix.go index bfe9d4dbd36229..f2e95d68c17f4c 100644 --- a/src/net/internal/socktest/switch_unix.go +++ b/src/net/internal/socktest/switch_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package socktest diff --git a/src/net/internal/socktest/sys_cloexec.go b/src/net/internal/socktest/sys_cloexec.go index c7f8331c2e5276..d57f44d9ee363c 100644 --- a/src/net/internal/socktest/sys_cloexec.go +++ b/src/net/internal/socktest/sys_cloexec.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build dragonfly freebsd illumos linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris package socktest diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go index e7cc45922e09d1..e1040d3087a944 100644 --- a/src/net/internal/socktest/sys_unix.go +++ b/src/net/internal/socktest/sys_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package socktest diff --git a/src/net/ip.go b/src/net/ip.go index 38e1aa2247f5c3..54c52881cf4d4f 100644 --- a/src/net/ip.go +++ b/src/net/ip.go @@ -308,7 +308,7 @@ func ubtoa(dst []byte, start int, v byte) int { // 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:db8::1"), if ip is a valid IPv6 address +// - IPv6 conforming to RFC 5952 ("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 @@ -545,6 +545,9 @@ func (n *IPNet) Network() string { return "ip+net" } // character and a mask expressed as hexadecimal form with no // punctuation like "198.51.100.0/c000ff00". func (n *IPNet) String() string { + if n == nil { + return "" + } nn, m := networkNumberAndMask(n) if nn == nil || m == nil { return "" diff --git a/src/net/ip_test.go b/src/net/ip_test.go index 5bbda6024dc3d9..8f1590cfd5cd19 100644 --- a/src/net/ip_test.go +++ b/src/net/ip_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -408,6 +407,7 @@ var ipNetStringTests = []struct { {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"}, {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, + {nil, ""}, } func TestIPNetString(t *testing.T) { @@ -719,7 +719,7 @@ var ipAddrScopeTests = []struct { {IP.IsPrivate, IP{0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false}, } -func name(f interface{}) string { +func name(f any) string { return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() } diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go index b94eec0e182b3d..64112b08dd4028 100644 --- a/src/net/iprawsock_posix.go +++ b/src/net/iprawsock_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/iprawsock_test.go b/src/net/iprawsock_test.go index a96448ee6c6568..ca5ab480c0eca9 100644 --- a/src/net/iprawsock_test.go +++ b/src/net/iprawsock_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go index c51c2274015dcf..7bb66f2d6cce3d 100644 --- a/src/net/ipsock_posix.go +++ b/src/net/ipsock_posix.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net import ( "context" "internal/poll" + "net/netip" "runtime" "syscall" ) @@ -78,29 +78,29 @@ func (p *ipStackCapabilities) probe() { // address family, both AF_INET and AF_INET6, and a wildcard address // like the following: // -// - A listen for a wildcard communication domain, "tcp" or -// "udp", with a wildcard address: If the platform supports -// both IPv6 and IPv4-mapped IPv6 communication capabilities, -// or does not support IPv4, we use a dual stack, AF_INET6 and -// IPV6_V6ONLY=0, wildcard address listen. The dual stack -// wildcard address listen may fall back to an IPv6-only, -// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen. -// Otherwise we prefer an IPv4-only, AF_INET, wildcard address -// listen. +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with a wildcard address: If the platform supports +// both IPv6 and IPv4-mapped IPv6 communication capabilities, +// or does not support IPv4, we use a dual stack, AF_INET6 and +// IPV6_V6ONLY=0, wildcard address listen. The dual stack +// wildcard address listen may fall back to an IPv6-only, +// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen. +// Otherwise we prefer an IPv4-only, AF_INET, wildcard address +// listen. // -// - A listen for a wildcard communication domain, "tcp" or -// "udp", with an IPv4 wildcard address: same as above. +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with an IPv4 wildcard address: same as above. // -// - A listen for a wildcard communication domain, "tcp" or -// "udp", with an IPv6 wildcard address: same as above. +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with an IPv6 wildcard address: same as above. // -// - A listen for an IPv4 communication domain, "tcp4" or "udp4", -// with an IPv4 wildcard address: We use an IPv4-only, AF_INET, -// wildcard address listen. +// - A listen for an IPv4 communication domain, "tcp4" or "udp4", +// with an IPv4 wildcard address: We use an IPv4-only, AF_INET, +// wildcard address listen. // -// - A listen for an IPv6 communication domain, "tcp6" or "udp6", -// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6 -// and IPV6_V6ONLY=1, wildcard address listen. +// - A listen for an IPv6 communication domain, "tcp6" or "udp6", +// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6 +// and IPV6_V6ONLY=1, wildcard address listen. // // Otherwise guess: If the addresses are IPv4 then returns AF_INET, // or else returns AF_INET6. It also returns a boolean value what @@ -142,42 +142,91 @@ func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, soty return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlFn) } +func ipToSockaddrInet4(ip IP, port int) (syscall.SockaddrInet4, error) { + if len(ip) == 0 { + ip = IPv4zero + } + ip4 := ip.To4() + if ip4 == nil { + return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: ip.String()} + } + sa := syscall.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return sa, nil +} + +func ipToSockaddrInet6(ip IP, port int, zone string) (syscall.SockaddrInet6, error) { + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow a listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(IPv4zero) { + ip = IPv6zero + } + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { + return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: ip.String()} + } + sa := syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))} + copy(sa.Addr[:], ip6) + return sa, nil +} + func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { switch family { case syscall.AF_INET: - if len(ip) == 0 { - ip = IPv4zero - } - ip4 := ip.To4() - if ip4 == nil { - return nil, &AddrError{Err: "non-IPv4 address", Addr: ip.String()} + sa, err := ipToSockaddrInet4(ip, port) + if err != nil { + return nil, err } - sa := &syscall.SockaddrInet4{Port: port} - copy(sa.Addr[:], ip4) - return sa, nil + return &sa, nil case syscall.AF_INET6: - // In general, an IP wildcard address, which is either - // "0.0.0.0" or "::", means the entire IP addressing - // space. For some historical reason, it is used to - // specify "any available address" on some operations - // of IP node. - // - // When the IP node supports IPv4-mapped IPv6 address, - // we allow a listener to listen to the wildcard - // address of both IP addressing spaces by specifying - // IPv6 wildcard address. - if len(ip) == 0 || ip.Equal(IPv4zero) { - ip = IPv6zero - } - // We accept any IPv6 address including IPv4-mapped - // IPv6 address. - ip6 := ip.To16() - if ip6 == nil { - return nil, &AddrError{Err: "non-IPv6 address", Addr: ip.String()} + sa, err := ipToSockaddrInet6(ip, port, zone) + if err != nil { + return nil, err } - sa := &syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))} - copy(sa.Addr[:], ip6) - return sa, nil + return &sa, nil } return nil, &AddrError{Err: "invalid address family", Addr: ip.String()} } + +func addrPortToSockaddrInet4(ap netip.AddrPort) (syscall.SockaddrInet4, error) { + // ipToSockaddrInet4 has special handling here for zero length slices. + // We do not, because netip has no concept of a generic zero IP address. + addr := ap.Addr() + if !addr.Is4() { + return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: addr.String()} + } + sa := syscall.SockaddrInet4{ + Addr: addr.As4(), + Port: int(ap.Port()), + } + return sa, nil +} + +func addrPortToSockaddrInet6(ap netip.AddrPort) (syscall.SockaddrInet6, error) { + // ipToSockaddrInet6 has special handling here for zero length slices. + // We do not, because netip has no concept of a generic zero IP address. + // + // addr is allowed to be an IPv4 address, because As16 will convert it + // to an IPv4-mapped IPv6 address. + // The error message is kept consistent with ipToSockaddrInet6. + addr := ap.Addr() + if !addr.IsValid() { + return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: addr.String()} + } + sa := syscall.SockaddrInet6{ + Addr: addr.As16(), + Port: int(ap.Port()), + ZoneId: uint32(zoneCache.index(addr.Zone())), + } + return sa, nil +} diff --git a/src/net/listen_test.go b/src/net/listen_test.go index b1dce29ac209a9..df3cadfa1a9ca6 100644 --- a/src/net/listen_test.go +++ b/src/net/listen_test.go @@ -3,12 +3,10 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 -// +build !js,!plan9 package net import ( - "context" "fmt" "internal/testenv" "os" @@ -380,7 +378,7 @@ func differentWildcardAddr(i, j string) bool { return true } -func checkFirstListener(network string, ln interface{}) error { +func checkFirstListener(network string, ln any) error { switch network { case "tcp": fd := ln.(*TCPListener).fd @@ -535,8 +533,6 @@ func TestIPv4MulticastListener(t *testing.T) { switch runtime.GOOS { case "android", "plan9": t.Skipf("not supported on %s", runtime.GOOS) - case "solaris", "illumos": - t.Skipf("not supported on solaris or illumos, see golang.org/issue/7399") } if !supportsIPv4() { t.Skip("IPv4 is not supported") @@ -610,8 +606,6 @@ func TestIPv6MulticastListener(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("not supported on %s", runtime.GOOS) - case "solaris", "illumos": - t.Skipf("not supported on solaris or illumos, see issue 7399") } if !supportsIPv6() { t.Skip("IPv6 is not supported") @@ -702,10 +696,7 @@ func multicastRIBContains(ip IP) (bool, error) { // Issue 21856. func TestClosingListener(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") addr := ln.Addr() go func() { @@ -743,19 +734,7 @@ func TestListenConfigControl(t *testing.T) { if !testableNetwork(network) { continue } - ln, err := newLocalListener(network) - if err != nil { - t.Error(err) - continue - } - address := ln.Addr().String() - ln.Close() - lc := ListenConfig{Control: controlOnConnSetup} - ln, err = lc.Listen(context.Background(), network, address) - if err != nil { - t.Error(err) - continue - } + ln := newLocalListener(t, network, &ListenConfig{Control: controlOnConnSetup}) ln.Close() } }) @@ -764,26 +743,8 @@ func TestListenConfigControl(t *testing.T) { if !testableNetwork(network) { continue } - c, err := newLocalPacketListener(network) - if err != nil { - t.Error(err) - continue - } - address := c.LocalAddr().String() + c := newLocalPacketListener(t, network, &ListenConfig{Control: controlOnConnSetup}) c.Close() - if network == "unixgram" { - os.Remove(address) - } - lc := ListenConfig{Control: controlOnConnSetup} - c, err = lc.ListenPacket(context.Background(), network, address) - if err != nil { - t.Error(err) - continue - } - c.Close() - if network == "unixgram" { - os.Remove(address) - } } }) } diff --git a/src/net/lookup.go b/src/net/lookup.go index d350ef7fc03b0b..969c902b1d44b7 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -8,7 +8,10 @@ import ( "context" "internal/nettrace" "internal/singleflight" + "net/netip" "sync" + + "golang.org/x/net/dns/dnsmessage" ) // protocols contains minimal mappings between internet protocol @@ -221,10 +224,15 @@ func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, er default: return nil, UnknownNetworkError(network) } + + if host == "" { + return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true} + } addrs, err := r.internetAddrList(ctx, afnet, host) if err != nil { return nil, err } + ips := make([]IP, 0, len(addrs)) for _, addr := range addrs { ips = append(ips, addr.(*IPAddr).IP) @@ -232,6 +240,28 @@ func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, er return ips, nil } +// LookupNetIP looks up host using the local resolver. +// It returns a slice of that host's IP addresses of the type specified by +// network. +// The network must be one of "ip", "ip4" or "ip6". +func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) { + // TODO(bradfitz): make this efficient, making the internal net package + // type throughout be netip.Addr and only converting to the net.IP slice + // version at the edge. But for now (2021-10-20), this is a wrapper around + // the old way. + ips, err := r.LookupIP(ctx, network, host) + if err != nil { + return nil, err + } + ret := make([]netip.Addr, 0, len(ips)) + for _, ip := range ips { + if a, ok := netip.AddrFromSlice(ip); ok { + ret = append(ret, a) + } + } + return ret, nil +} + // onlyValuesCtx is a context that uses an underlying context // for value lookup if the underlying context hasn't yet expired. type onlyValuesCtx struct { @@ -242,7 +272,7 @@ type onlyValuesCtx struct { var _ context.Context = (*onlyValuesCtx)(nil) // Value performs a lookup if the original context hasn't expired. -func (ovc *onlyValuesCtx) Value(key interface{}) interface{} { +func (ovc *onlyValuesCtx) Value(key any) any { select { case <-ovc.lookupValues.Done(): return nil @@ -263,7 +293,7 @@ func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context { // It returns a slice of that host's IPv4 and IPv6 addresses. func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) { // Make sure that no matter what we do later, host=="" is rejected. - // parseIP, for example, does accept empty strings. + // parseIPZone, for example, does accept empty strings. if host == "" { return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true} } @@ -291,14 +321,15 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP lookupKey := network + "\000" + host dnsWaitGroup.Add(1) - ch, called := r.getLookupGroup().DoChan(lookupKey, func() (interface{}, error) { - defer dnsWaitGroup.Done() + ch := r.getLookupGroup().DoChan(lookupKey, func() (any, error) { return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host) }) - if !called { + + dnsWaitGroupDone := func(ch <-chan singleflight.Result, cancelFn context.CancelFunc) { + <-ch dnsWaitGroup.Done() + cancelFn() } - select { case <-ctx.Done(): // Our context was canceled. If we are the only @@ -310,30 +341,50 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP // See issues 8602, 20703, 22724. if r.getLookupGroup().ForgetUnshared(lookupKey) { lookupGroupCancel() + go dnsWaitGroupDone(ch, func() {}) } else { - go func() { - <-ch - lookupGroupCancel() - }() + go dnsWaitGroupDone(ch, lookupGroupCancel) + } + ctxErr := ctx.Err() + err := &DNSError{ + Err: mapErr(ctxErr).Error(), + Name: host, + IsTimeout: ctxErr == context.DeadlineExceeded, } - err := mapErr(ctx.Err()) if trace != nil && trace.DNSDone != nil { trace.DNSDone(nil, false, err) } return nil, err case r := <-ch: + dnsWaitGroup.Done() lookupGroupCancel() + err := r.Err + if err != nil { + if _, ok := err.(*DNSError); !ok { + isTimeout := false + if err == context.DeadlineExceeded { + isTimeout = true + } else if terr, ok := err.(timeout); ok { + isTimeout = terr.Timeout() + } + err = &DNSError{ + Err: err.Error(), + Name: host, + IsTimeout: isTimeout, + } + } + } if trace != nil && trace.DNSDone != nil { addrs, _ := r.Val.([]IPAddr) - trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err) + trace.DNSDone(ipAddrsEface(addrs), r.Shared, err) } - return lookupIPReturn(r.Val, r.Err, r.Shared) + return lookupIPReturn(r.Val, err, r.Shared) } } // lookupIPReturn turns the return values from singleflight.Do into // the return values from LookupIP. -func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) { +func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) { if err != nil { return nil, err } @@ -347,8 +398,8 @@ func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error } // ipAddrsEface returns an empty interface slice of addrs. -func ipAddrsEface(addrs []IPAddr) []interface{} { - s := make([]interface{}, len(addrs)) +func ipAddrsEface(addrs []IPAddr) []any { + s := make([]any, len(addrs)) for i, v := range addrs { s[i] = v } @@ -442,7 +493,7 @@ func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) // The returned service names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { return DefaultResolver.LookupSRV(context.Background(), service, proto, name) } @@ -460,7 +511,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err // The returned service names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { cname, addrs, err := r.lookupSRV(ctx, service, proto, name) if err != nil { @@ -490,7 +541,7 @@ func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) ( // The returned mail server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. // // LookupMX uses context.Background internally; to specify the context, use // Resolver.LookupMX. @@ -503,7 +554,7 @@ func LookupMX(name string) ([]*MX, error) { // The returned mail server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { records, err := r.lookupMX(ctx, name) if err != nil { @@ -514,9 +565,7 @@ func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { if mx == nil { continue } - // Bypass the hostname validity check for targets which contain only a dot, - // as this is used to represent a 'Null' MX record. - if mx.Host != "." && !isDomainName(mx.Host) { + if !isDomainName(mx.Host) { continue } filteredMX = append(filteredMX, mx) @@ -532,7 +581,7 @@ func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { // The returned name server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. // // LookupNS uses context.Background internally; to specify the context, use // Resolver.LookupNS. @@ -545,7 +594,7 @@ func LookupNS(name string) ([]*NS, error) { // The returned name server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { records, err := r.lookupNS(ctx, name) if err != nil { @@ -585,7 +634,7 @@ func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) // // The returned names are validated to be properly formatted presentation-format // domain names. If the response contains invalid names, those records are filtered -// out and an error will be returned alongside the the remaining results, if any. +// out and an error will be returned alongside the remaining results, if any. // // When using the host C library resolver, at most one result will be // returned. To bypass the host resolver, use a custom Resolver. @@ -601,7 +650,7 @@ func LookupAddr(addr string) (names []string, err error) { // // The returned names are validated to be properly formatted presentation-format // domain names. If the response contains invalid names, those records are filtered -// out and an error will be returned alongside the the remaining results, if any. +// out and an error will be returned alongside the remaining results, if any. func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) { names, err := r.lookupAddr(ctx, addr) if err != nil { @@ -620,6 +669,230 @@ func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error } // errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup... -// method recieves DNS records which contain invalid DNS names. This may be returned alongside +// method receives DNS records which contain invalid DNS names. This may be returned alongside // results which have had the malformed records filtered out. var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names" + +// dial makes a new connection to the provided server (which must be +// an IP address) with the provided network type, using either r.Dial +// (if both r and r.Dial are non-nil) or else Dialer.DialContext. +func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) { + // Calling Dial here is scary -- we have to be sure not to + // dial a name that will require a DNS lookup, or Dial will + // call back here to translate it. The DNS config parser has + // already checked that all the cfg.servers are IP + // addresses, which Dial will use without a DNS lookup. + var c Conn + var err error + if r != nil && r.Dial != nil { + c, err = r.Dial(ctx, network, server) + } else { + var d Dialer + c, err = d.DialContext(ctx, network, server) + } + if err != nil { + return nil, mapErr(err) + } + return c, nil +} + +// goLookupSRV returns the SRV records for a target name, built either +// from its component service ("sip"), protocol ("tcp"), and name +// ("example.com."), or from name directly (if service and proto are +// both empty). +// +// In either case, the returned target name ("_sip._tcp.example.com.") +// is also returned on success. +// +// The records are sorted by weight. +func (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) { + if service == "" && proto == "" { + target = name + } else { + target = "_" + service + "._" + proto + "." + name + } + p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) + if err != nil { + return "", nil, err + } + var cname dnsmessage.Name + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeSRV { + if err := p.SkipAnswer(); err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + if cname.Length == 0 && h.Name.Length != 0 { + cname = h.Name + } + srv, err := p.SRVResource() + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) + } + byPriorityWeight(srvs).sort() + return cname.String(), srvs, nil +} + +// goLookupMX returns the MX records for name. +func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) + if err != nil { + return nil, err + } + var mxs []*MX + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeMX { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + mx, err := p.MXResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) + + } + byPref(mxs).sort() + return mxs, nil +} + +// goLookupNS returns the NS records for name. +func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) + if err != nil { + return nil, err + } + var nss []*NS + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeNS { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + ns, err := p.NSResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + nss = append(nss, &NS{Host: ns.NS.String()}) + } + return nss, nil +} + +// goLookupTXT returns the TXT records from name. +func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) + if err != nil { + return nil, err + } + var txts []string + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeTXT { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + txt, err := p.TXTResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + // Multiple strings in one TXT record need to be + // concatenated without separator to be consistent + // with previous Go resolver. + n := 0 + for _, s := range txt.TXT { + n += len(s) + } + txtJoin := make([]byte, 0, n) + for _, s := range txt.TXT { + txtJoin = append(txtJoin, s...) + } + if len(txts) == 0 { + txts = make([]string, 0, 1) + } + txts = append(txts, string(txtJoin)) + } + return txts, nil +} diff --git a/src/net/lookup_fake.go b/src/net/lookup_fake.go index f4fcaed5cfa1c7..c27eae4ba5097e 100644 --- a/src/net/lookup_fake.go +++ b/src/net/lookup_fake.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go index 75c18b33ac06f0..445b1294e352dd 100644 --- a/src/net/lookup_plan9.go +++ b/src/net/lookup_plan9.go @@ -179,7 +179,27 @@ loop: return } -func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) { +// preferGoOverPlan9 reports whether the resolver should use the +// "PreferGo" implementation rather than asking plan9 services +// for the answers. +func (r *Resolver) preferGoOverPlan9() bool { + conf := systemConf() + order := conf.hostLookupOrder(r, "") // name is unused + + // TODO(bradfitz): for now we only permit use of the PreferGo + // implementation when there's a non-nil Resolver with a + // non-nil Dialer. This is a sign that they the code is trying + // to use their DNS-speaking net.Conn (such as an in-memory + // DNS cache) and they don't want to actually hit the network. + // Once we add support for looking the default DNS servers + // from plan9, though, then we can relax this. + return order != hostLookupCgo && r != nil && r.Dial != nil +} + +func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { + if r.preferGoOverPlan9() { + return r.goLookupIP(ctx, network, host) + } lits, err := r.lookupHost(ctx, host) if err != nil { return @@ -223,7 +243,10 @@ func (*Resolver) lookupPort(ctx context.Context, network, service string) (port return 0, unknownPortError } -func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { +func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupCNAME(ctx, name) + } lines, err := queryDNS(ctx, name, "cname") if err != nil { if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") { @@ -240,7 +263,10 @@ func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, er return "", errors.New("bad response from ndb/dns") } -func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { +func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { + if r.preferGoOverPlan9() { + return r.goLookupSRV(ctx, service, proto, name) + } var target string if service == "" && proto == "" { target = name @@ -262,14 +288,17 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cn if !(portOk && priorityOk && weightOk) { continue } - addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)}) - cname = absDomainName([]byte(f[0])) + addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)}) + cname = absDomainName(f[0]) } byPriorityWeight(addrs).sort() return } -func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { +func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { + if r.preferGoOverPlan9() { + return r.goLookupMX(ctx, name) + } lines, err := queryDNS(ctx, name, "mx") if err != nil { return @@ -280,14 +309,17 @@ func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error continue } if pref, _, ok := dtoi(f[2]); ok { - mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)}) + mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)}) } } byPref(mx).sort() return } -func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { +func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { + if r.preferGoOverPlan9() { + return r.goLookupNS(ctx, name) + } lines, err := queryDNS(ctx, name, "ns") if err != nil { return @@ -297,12 +329,15 @@ func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error if len(f) < 3 { continue } - ns = append(ns, &NS{absDomainName([]byte(f[2]))}) + ns = append(ns, &NS{absDomainName(f[2])}) } return } -func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { +func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupTXT(ctx, name) + } lines, err := queryDNS(ctx, name, "txt") if err != nil { return @@ -315,7 +350,10 @@ func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err return } -func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { +func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupPTR(ctx, addr) + } arpa, err := reverseaddr(addr) if err != nil { return @@ -329,7 +367,7 @@ func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, er if len(f) < 3 { continue } - name = append(name, absDomainName([]byte(f[2]))) + name = append(name, absDomainName(f[2])) } return } diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go index 3faaf007106b7c..ed9f93f3fe8990 100644 --- a/src/net/lookup_test.go +++ b/src/net/lookup_test.go @@ -3,12 +3,10 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net import ( - "bytes" "context" "fmt" "internal/testenv" @@ -353,6 +351,7 @@ var lookupCNAMETests = []struct { func TestLookupCNAME(t *testing.T) { mustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -391,6 +390,7 @@ var lookupGoogleHostTests = []struct { func TestLookupGoogleHost(t *testing.T) { mustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -443,6 +443,7 @@ var lookupGoogleIPTests = []struct { func TestLookupGoogleIP(t *testing.T) { mustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -633,6 +634,7 @@ func TestLookupDotsWithRemoteSource(t *testing.T) { testenv.SkipFlaky(t, 27992) } mustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) if !supportsIPv4() || !*testIPv4 { t.Skip("IPv4 is required") @@ -657,7 +659,6 @@ 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 { @@ -670,7 +671,6 @@ func testDots(t *testing.T, mode string) { cname, err := LookupCNAME("www.mit.edu") 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) @@ -678,7 +678,6 @@ func testDots(t *testing.T, mode string) { 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 { @@ -691,7 +690,6 @@ 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 { @@ -704,7 +702,6 @@ 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 !hasSuffixFold(cname, ".google.com.") { @@ -720,7 +717,7 @@ func testDots(t *testing.T, mode string) { } func mxString(mxs []*MX) string { - var buf bytes.Buffer + var buf strings.Builder sep := "" fmt.Fprintf(&buf, "[") for _, mx := range mxs { @@ -732,7 +729,7 @@ func mxString(mxs []*MX) string { } func nsString(nss []*NS) string { - var buf bytes.Buffer + var buf strings.Builder sep := "" fmt.Fprintf(&buf, "[") for _, ns := range nss { @@ -744,7 +741,7 @@ func nsString(nss []*NS) string { } func srvString(srvs []*SRV) string { - var buf bytes.Buffer + var buf strings.Builder sep := "" fmt.Fprintf(&buf, "[") for _, srv := range srvs { @@ -885,21 +882,66 @@ func TestLookupNonLDH(t *testing.T) { func TestLookupContextCancel(t *testing.T) { mustHaveExternalNetwork(t) - defer dnsWaitGroup.Wait() + testenv.SkipFlakyNet(t) - ctx, ctxCancel := context.WithCancel(context.Background()) - ctxCancel() - _, err := DefaultResolver.LookupIPAddr(ctx, "google.com") - if err != errCanceled { - testenv.SkipFlakyNet(t) - t.Fatal(err) + origTestHookLookupIP := testHookLookupIP + defer func() { + dnsWaitGroup.Wait() + testHookLookupIP = origTestHookLookupIP + }() + + lookupCtx, cancelLookup := context.WithCancel(context.Background()) + unblockLookup := make(chan struct{}) + + // Set testHookLookupIP to start a new, concurrent call to LookupIPAddr + // and cancel the original one, then block until the canceled call has returned + // (ensuring that it has performed any synchronous cleanup). + testHookLookupIP = func( + ctx context.Context, + fn func(context.Context, string, string) ([]IPAddr, error), + network string, + host string, + ) ([]IPAddr, error) { + select { + case <-unblockLookup: + default: + // Start a concurrent LookupIPAddr for the same host while the caller is + // still blocked, and sleep a little to give it time to be deduplicated + // before we cancel (and unblock) the caller. + // (If the timing doesn't quite work out, we'll end up testing sequential + // calls instead of concurrent ones, but the test should still pass.) + t.Logf("starting concurrent LookupIPAddr") + dnsWaitGroup.Add(1) + go func() { + defer dnsWaitGroup.Done() + _, err := DefaultResolver.LookupIPAddr(context.Background(), host) + if err != nil { + t.Error(err) + } + }() + time.Sleep(1 * time.Millisecond) + } + + cancelLookup() + <-unblockLookup + // If the concurrent lookup above is deduplicated to this one + // (as we expect to happen most of the time), it is important + // that the original call does not cancel the shared Context. + // (See https://go.dev/issue/22724.) Explicitly check for + // cancellation now, just in case fn itself doesn't notice it. + if err := ctx.Err(); err != nil { + t.Logf("testHookLookupIP canceled") + return nil, err + } + t.Logf("testHookLookupIP performing lookup") + return fn(ctx, network, host) } - ctx = context.Background() - _, err = DefaultResolver.LookupIPAddr(ctx, "google.com") - if err != nil { - testenv.SkipFlakyNet(t) - t.Fatal(err) + + _, err := DefaultResolver.LookupIPAddr(lookupCtx, "google.com") + if dnsErr, ok := err.(*DNSError); !ok || dnsErr.Err != errCanceled.Error() { + t.Errorf("unexpected error from canceled, blocked LookupIPAddr: %v", err) } + close(unblockLookup) } // Issue 24330: treat the nil *Resolver like a zero value. Verify nothing @@ -926,6 +968,9 @@ func TestNilResolverLookup(t *testing.T) { // canceled lookups (see golang.org/issue/24178 for details). func TestLookupHostCancel(t *testing.T) { mustHaveExternalNetwork(t) + testenv.SkipFlakyNet(t) + t.Parallel() // Executes 600ms worth of sequential sleeps. + const ( google = "www.google.com" invalidDomain = "invalid.invalid" // RFC 2606 reserves .invalid @@ -944,9 +989,15 @@ func TestLookupHostCancel(t *testing.T) { if err == nil { t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr) } - if !strings.Contains(err.Error(), "canceled") { - t.Fatalf("LookupHost(%q): failed with unexpected error: %v", invalidDomain, err) - } + + // Don't verify what the actual error is. + // We know that it must be non-nil because the domain is invalid, + // but we don't have any guarantee that LookupHost actually bothers + // to check for cancellation on the fast path. + // (For example, it could use a local cache to avoid blocking entirely.) + + // The lookup may deduplicate in-flight requests, so give it time to settle + // in between. time.Sleep(time.Millisecond * 1) } @@ -1050,7 +1101,7 @@ func TestLookupIPAddrPreservesContextValues(t *testing.T) { defer func() { testHookLookupIP = origTestHookLookupIP }() keyValues := []struct { - key, value interface{} + key, value any }{ {"key-1", 12}, {384, "value2"}, @@ -1156,6 +1207,17 @@ func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) { wg.Wait() } +// Issue 53995: Resolver.LookupIP should return error for empty host name. +func TestResolverLookupIPWithEmptyHost(t *testing.T) { + _, err := DefaultResolver.LookupIP(context.Background(), "ip", "") + if err == nil { + t.Fatal("DefaultResolver.LookupIP for empty host success, want no host error") + } + if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) { + t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost) + } +} + func TestWithUnexpiredValuesPreserved(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -1267,3 +1329,71 @@ func TestResolverLookupIP(t *testing.T) { }) } } + +// A context timeout should still return a DNSError. +func TestDNSTimeout(t *testing.T) { + origTestHookLookupIP := testHookLookupIP + defer func() { testHookLookupIP = origTestHookLookupIP }() + defer dnsWaitGroup.Wait() + + timeoutHookGo := make(chan bool, 1) + timeoutHook := func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { + <-timeoutHookGo + return nil, context.DeadlineExceeded + } + testHookLookupIP = timeoutHook + + checkErr := func(err error) { + t.Helper() + if err == nil { + t.Error("expected an error") + } else if dnserr, ok := err.(*DNSError); !ok { + t.Errorf("got error type %T, want %T", err, (*DNSError)(nil)) + } else if !dnserr.IsTimeout { + t.Errorf("got error %#v, want IsTimeout == true", dnserr) + } else if isTimeout := dnserr.Timeout(); !isTimeout { + t.Errorf("got err.Timeout() == %t, want true", isTimeout) + } + } + + // Single lookup. + timeoutHookGo <- true + _, err := LookupIP("golang.org") + checkErr(err) + + // Double lookup. + var err1, err2 error + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + _, err1 = LookupIP("golang1.org") + }() + go func() { + defer wg.Done() + _, err2 = LookupIP("golang1.org") + }() + close(timeoutHookGo) + wg.Wait() + checkErr(err1) + checkErr(err2) + + // Double lookup with context. + timeoutHookGo = make(chan bool) + ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) + wg.Add(2) + go func() { + defer wg.Done() + _, err1 = DefaultResolver.LookupIPAddr(ctx, "golang2.org") + }() + go func() { + defer wg.Done() + _, err2 = DefaultResolver.LookupIPAddr(ctx, "golang2.org") + }() + time.Sleep(10 * time.Nanosecond) + close(timeoutHookGo) + wg.Wait() + checkErr(err1) + checkErr(err2) + cancel() +} diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index 8030e3d99e1e13..4b885e938a06fe 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net @@ -12,8 +11,6 @@ import ( "internal/bytealg" "sync" "syscall" - - "golang.org/x/net/dns/dnsmessage" ) var onceReadProtocols sync.Once @@ -56,26 +53,6 @@ func lookupProtocol(_ context.Context, name string) (int, error) { return lookupProtocolMap(name) } -func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) { - // Calling Dial here is scary -- we have to be sure not to - // dial a name that will require a DNS lookup, or Dial will - // call back here to translate it. The DNS config parser has - // already checked that all the cfg.servers are IP - // addresses, which Dial will use without a DNS lookup. - var c Conn - var err error - if r != nil && r.Dial != nil { - c, err = r.Dial(ctx, network, server) - } else { - var d Dialer - c, err = d.DialContext(ctx, network, server) - } - if err != nil { - return nil, mapErr(err) - } - return c, nil -} - func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { order := systemConf().hostLookupOrder(r, host) if !r.preferGo() && order == hostLookupCgo { @@ -130,194 +107,19 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) } func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { - var target string - if service == "" && proto == "" { - target = name - } else { - target = "_" + service + "._" + proto + "." + name - } - p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) - if err != nil { - return "", nil, err - } - var srvs []*SRV - var cname dnsmessage.Name - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeSRV { - if err := p.SkipAnswer(); err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - if cname.Length == 0 && h.Name.Length != 0 { - cname = h.Name - } - srv, err := p.SRVResource() - if err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) - } - byPriorityWeight(srvs).sort() - return cname.String(), srvs, nil + return r.goLookupSRV(ctx, service, proto, name) } func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) - if err != nil { - return nil, err - } - var mxs []*MX - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeMX { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - mx, err := p.MXResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) - - } - byPref(mxs).sort() - return mxs, nil + return r.goLookupMX(ctx, name) } func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) - if err != nil { - return nil, err - } - var nss []*NS - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeNS { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - ns, err := p.NSResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - nss = append(nss, &NS{Host: ns.NS.String()}) - } - return nss, nil + return r.goLookupNS(ctx, name) } func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) - if err != nil { - return nil, err - } - var txts []string - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeTXT { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - txt, err := p.TXTResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - // Multiple strings in one TXT record need to be - // concatenated without separator to be consistent - // with previous Go resolver. - n := 0 - for _, s := range txt.TXT { - n += len(s) - } - txtJoin := make([]byte, 0, n) - for _, s := range txt.TXT { - txtJoin = append(txtJoin, s...) - } - if len(txts) == 0 { - txts = make([]string, 0, 1) - } - txts = append(txts, string(txtJoin)) - } - return txts, nil + return r.goLookupTXT(ctx, name) } func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go index bb34a081336930..9ff39c74a4e208 100644 --- a/src/net/lookup_windows.go +++ b/src/net/lookup_windows.go @@ -82,7 +82,19 @@ func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error return addrs, nil } +// preferGoOverWindows reports whether the resolver should use the +// pure Go implementation rather than making win32 calls to ask the +// kernel for its answer. +func (r *Resolver) preferGoOverWindows() bool { + conf := systemConf() + order := conf.hostLookupOrder(r, "") // name is unused + return order != hostLookupCgo +} + func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) { + if r.preferGoOverWindows() { + return r.goLookupIP(ctx, network, name) + } // TODO(bradfitz,brainman): use ctx more. See TODO below. var family int32 = syscall.AF_UNSPEC @@ -169,7 +181,7 @@ func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr } func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { - if r.preferGo() { + if r.preferGoOverWindows() { return lookupPortMap(network, service) } @@ -217,28 +229,34 @@ func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service} } -func (*Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { +func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { + if r.preferGoOverWindows() { + return r.goLookupCNAME(ctx, name) + } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() - var r *syscall.DNSRecord - e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil) // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS { // if there are no aliases, the canonical name is the input name - return absDomainName([]byte(name)), nil + return absDomainName(name), nil } if e != nil { return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) - resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r) + resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), rec) cname := windows.UTF16PtrToString(resolved) - return absDomainName([]byte(cname)), nil + return absDomainName(cname), nil } -func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { +func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { + if r.preferGoOverWindows() { + return r.goLookupSRV(ctx, service, proto, name) + } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() @@ -248,74 +266,83 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (st } else { target = "_" + service + "._" + proto + "." + name } - var r *syscall.DNSRecord - e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &rec, nil) if e != nil { return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) srvs := make([]*SRV, 0, 10) - for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) { + for _, p := range validRecs(rec, syscall.DNS_TYPE_SRV, target) { v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0])) - srvs = append(srvs, &SRV{absDomainName([]byte(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:]))), v.Port, v.Priority, v.Weight}) + srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight}) } byPriorityWeight(srvs).sort() - return absDomainName([]byte(target)), srvs, nil + return absDomainName(target), srvs, nil } -func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { +func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { + if r.preferGoOverWindows() { + return r.goLookupMX(ctx, name) + } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() - var r *syscall.DNSRecord - e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil) if e != nil { return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) mxs := make([]*MX, 0, 10) - for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) { + for _, p := range validRecs(rec, syscall.DNS_TYPE_MX, name) { v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0])) - mxs = append(mxs, &MX{absDomainName([]byte(windows.UTF16PtrToString(v.NameExchange))), v.Preference}) + mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference}) } byPref(mxs).sort() return mxs, nil } -func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { +func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { + if r.preferGoOverWindows() { + return r.goLookupNS(ctx, name) + } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() - var r *syscall.DNSRecord - e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil) if e != nil { return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) nss := make([]*NS, 0, 10) - for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) { + for _, p := range validRecs(rec, syscall.DNS_TYPE_NS, name) { v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) - nss = append(nss, &NS{absDomainName([]byte(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:])))}) + nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))}) } return nss, nil } -func (*Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { +func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { + if r.preferGoOverWindows() { + return r.goLookupTXT(ctx, name) + } // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() - var r *syscall.DNSRecord - e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil) if e != nil { return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) txts := make([]string, 0, 10) - for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) { + for _, p := range validRecs(rec, syscall.DNS_TYPE_TEXT, name) { d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0])) s := "" for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] { @@ -326,7 +353,11 @@ func (*Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { return txts, nil } -func (*Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { +func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { + if r.preferGoOverWindows() { + return r.goLookupPTR(ctx, addr) + } + // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. acquireThread() defer releaseThread() @@ -334,17 +365,17 @@ func (*Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) if err != nil { return nil, err } - var r *syscall.DNSRecord - e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil) + var rec *syscall.DNSRecord + e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &rec, nil) if e != nil { return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr} } - defer syscall.DnsRecordListFree(r, 1) + defer syscall.DnsRecordListFree(rec, 1) ptrs := make([]string, 0, 10) - for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) { + for _, p := range validRecs(rec, syscall.DNS_TYPE_PTR, arpa) { v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) - ptrs = append(ptrs, absDomainName([]byte(windows.UTF16PtrToString(v.Host)))) + ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host))) } return ptrs, nil } diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go index aa95501d023930..c618a05bb44e44 100644 --- a/src/net/lookup_windows_test.go +++ b/src/net/lookup_windows_test.go @@ -5,7 +5,7 @@ package net import ( - "bytes" + "context" "encoding/json" "errors" "fmt" @@ -15,115 +15,129 @@ import ( "regexp" "sort" "strings" + "syscall" "testing" ) var nslookupTestServers = []string{"mail.golang.com", "gmail.com"} var lookupTestIPs = []string{"8.8.8.8", "1.1.1.1"} -func toJson(v interface{}) string { +func toJson(v any) string { data, _ := json.Marshal(v) return string(data) } +func testLookup(t *testing.T, fn func(*testing.T, *Resolver, string)) { + for _, def := range []bool{true, false} { + def := def + for _, server := range nslookupTestServers { + server := server + var name string + if def { + name = "default/" + } else { + name = "go/" + } + t.Run(name+server, func(t *testing.T) { + t.Parallel() + r := DefaultResolver + if !def { + r = &Resolver{PreferGo: true} + } + fn(t, r, server) + }) + } + } +} + func TestNSLookupMX(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - mx, err := LookupMX(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + mx, err := r.LookupMX(context.Background(), server) if err != nil { - t.Error(err) - continue + t.Fatal(err) } if len(mx) == 0 { - t.Errorf("no results") - continue + t.Fatal("no results") } expected, err := nslookupMX(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Sort(byPrefAndHost(expected)) sort.Sort(byPrefAndHost(mx)) if !reflect.DeepEqual(expected, mx) { t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx)) } - } + }) } func TestNSLookupCNAME(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - cname, err := LookupCNAME(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + cname, err := r.LookupCNAME(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if cname == "" { - t.Errorf("no result %s", server) + t.Fatalf("no result %s", server) } expected, err := nslookupCNAME(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } if expected != cname { t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname) } - } + }) } func TestNSLookupNS(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - ns, err := LookupNS(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + ns, err := r.LookupNS(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if len(ns) == 0 { - t.Errorf("no results") - continue + t.Fatal("no results") } expected, err := nslookupNS(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Sort(byHost(expected)) sort.Sort(byHost(ns)) if !reflect.DeepEqual(expected, ns) { t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns) } - } + }) } func TestNSLookupTXT(t *testing.T) { testenv.MustHaveExternalNetwork(t) - for _, server := range nslookupTestServers { - txt, err := LookupTXT(server) + testLookup(t, func(t *testing.T, r *Resolver, server string) { + txt, err := r.LookupTXT(context.Background(), server) if err != nil { - t.Errorf("failed %s: %s", server, err) - continue + t.Fatalf("failed %s: %s", server, err) } if len(txt) == 0 { - t.Errorf("no results") - continue + t.Fatalf("no results") } expected, err := nslookupTXT(server) if err != nil { - t.Logf("skipping failed nslookup %s test: %s", server, err) - continue + t.Skipf("skipping failed nslookup %s test: %s", server, err) } sort.Strings(expected) sort.Strings(txt) if !reflect.DeepEqual(expected, txt) { t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt)) } - } + }) } func TestLookupLocalPTR(t *testing.T) { @@ -142,7 +156,7 @@ func TestLookupLocalPTR(t *testing.T) { } expected, err := lookupPTR(addr.String()) if err != nil { - t.Logf("skipping failed lookup %s test: %s", addr.String(), err) + t.Skipf("skipping failed lookup %s test: %s", addr.String(), err) } sort.Strings(expected) sort.Strings(names) @@ -157,6 +171,14 @@ func TestLookupPTR(t *testing.T) { for _, addr := range lookupTestIPs { names, err := LookupAddr(addr) if err != nil { + // The DNSError type stores the error as a string, so it cannot wrap the + // original error code and we cannot check for it here. However, we can at + // least use its error string to identify the correct localized text for + // the error to skip. + var DNS_ERROR_RCODE_SERVER_FAILURE syscall.Errno = 9002 + if strings.HasSuffix(err.Error(), DNS_ERROR_RCODE_SERVER_FAILURE.Error()) { + testenv.SkipFlaky(t, 38111) + } t.Errorf("failed %s: %s", addr, err) } if len(names) == 0 { @@ -165,6 +187,7 @@ func TestLookupPTR(t *testing.T) { expected, err := lookupPTR(addr) if err != nil { t.Logf("skipping failed lookup %s test: %s", addr, err) + continue } sort.Strings(expected) sort.Strings(names) @@ -192,8 +215,8 @@ func (s byHost) Less(i, j int) bool { return s[i].Host < s[j].Host } func (s byHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func nslookup(qtype, name string) (string, error) { - var out bytes.Buffer - var err bytes.Buffer + var out strings.Builder + var err strings.Builder cmd := exec.Command("nslookup", "-querytype="+qtype, name) cmd.Stdout = &out cmd.Stderr = &err @@ -220,14 +243,14 @@ func nslookupMX(name string) (mx []*MX, err error) { rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`) for _, ans := range rx.FindAllStringSubmatch(r, -1) { pref, _, _ := dtoi(ans[2]) - mx = append(mx, &MX{absDomainName([]byte(ans[3])), uint16(pref)}) + mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)}) } // windows nslookup syntax // gmail.com MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`) for _, ans := range rx.FindAllStringSubmatch(r, -1) { pref, _, _ := dtoi(ans[2]) - mx = append(mx, &MX{absDomainName([]byte(ans[3])), uint16(pref)}) + mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)}) } return } @@ -241,7 +264,7 @@ func nslookupNS(name string) (ns []*NS, err error) { // golang.org nameserver = ns1.google.com. rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+nameserver\s*=\s*([a-z0-9.\-]+)$`) for _, ans := range rx.FindAllStringSubmatch(r, -1) { - ns = append(ns, &NS{absDomainName([]byte(ans[2]))}) + ns = append(ns, &NS{absDomainName(ans[2])}) } return } @@ -258,7 +281,7 @@ func nslookupCNAME(name string) (cname string, err error) { for _, ans := range rx.FindAllStringSubmatch(r, -1) { last = ans[2] } - return absDomainName([]byte(last)), nil + return absDomainName(last), nil } func nslookupTXT(name string) (txt []string, err error) { @@ -299,7 +322,7 @@ func lookupPTR(name string) (ptr []string, err error) { ptr = make([]string, 0, 10) rx := regexp.MustCompile(`(?m)^Pinging\s+([a-zA-Z0-9.\-]+)\s+\[.*$`) for _, ans := range rx.FindAllStringSubmatch(r, -1) { - ptr = append(ptr, absDomainName([]byte(ans[1]))) + ptr = append(ptr, absDomainName(ans[1])) } return } diff --git a/src/net/mac.go b/src/net/mac.go index 373ac3d7e2018e..53d5b2dbf596b4 100644 --- a/src/net/mac.go +++ b/src/net/mac.go @@ -26,6 +26,7 @@ func (a HardwareAddr) String() string { // ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, EUI-64, or a 20-octet // IP over InfiniBand link-layer address using one of the following formats: +// // 00:00:5e:00:53:01 // 02:00:5e:10:00:00:00:01 // 00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01 diff --git a/src/net/mail/message.go b/src/net/mail/message.go index 47bbf6ca9771bd..5de47eb45b6990 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -8,12 +8,12 @@ Package mail implements parsing of mail messages. For the most part, this package follows the syntax as specified by RFC 5322 and extended by RFC 6532. Notable divergences: - * Obsolete address formats are not parsed, including addresses with - embedded route information. - * The full range of spacing (the CFWS syntax element) is not supported, - such as breaking addresses across lines. - * No unicode normalization is performed. - * The special characters ()[]:;@\, are allowed to appear unquoted in names. + - Obsolete address formats are not parsed, including addresses with + embedded route information. + - The full range of spacing (the CFWS syntax element) is not supported, + such as breaking addresses across lines. + - No unicode normalization is performed. + - The special characters ()[]:;@\, are allowed to appear unquoted in names. */ package mail @@ -35,7 +35,7 @@ var debug = debugT(false) type debugT bool -func (d debugT) Printf(format string, args ...interface{}) { +func (d debugT) Printf(format string, args ...any) { if d { log.Printf(format, args...) } @@ -79,7 +79,7 @@ func buildDateLayouts() { years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT seconds := [...]string{":05", ""} // second // "-0700 (MST)" is not in RFC 5322, but is common. - zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ... + zones := [...]string{"-0700", "MST", "UT"} // zone = (("+" / "-") 4DIGIT) / "UT" / "GMT" / ... for _, dow := range dows { for _, day := range days { @@ -100,7 +100,7 @@ func ParseDate(date string) (time.Time, error) { dateLayoutsBuildOnce.Do(buildDateLayouts) // CR and LF must match and are tolerated anywhere in the date field. date = strings.ReplaceAll(date, "\r\n", "") - if strings.Index(date, "\r") != -1 { + if strings.Contains(date, "\r") { return time.Time{}, errors.New("mail: header has a CR without LF") } // Re-using some addrParser methods which support obsolete text, i.e. non-printable ASCII @@ -745,17 +745,41 @@ func (p *addrParser) consumeComment() (string, bool) { } func (p *addrParser) decodeRFC2047Word(s string) (word string, isEncoded bool, err error) { - if p.dec != nil { - word, err = p.dec.Decode(s) - } else { - word, err = rfc2047Decoder.Decode(s) + dec := p.dec + if dec == nil { + dec = &rfc2047Decoder + } + + // Substitute our own CharsetReader function so that we can tell + // whether an error from the Decode method was due to the + // CharsetReader (meaning the charset is invalid). + // We used to look for the charsetError type in the error result, + // but that behaves badly with CharsetReaders other than the + // one in rfc2047Decoder. + adec := *dec + charsetReaderError := false + adec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) { + if dec.CharsetReader == nil { + charsetReaderError = true + return nil, charsetError(charset) + } + r, err := dec.CharsetReader(charset, input) + if err != nil { + charsetReaderError = true + } + return r, err } - + word, err = adec.Decode(s) if err == nil { return word, true, nil } - if _, ok := err.(charsetError); ok { + // If the error came from the character set reader + // (meaning the character set itself is invalid + // but the decoding worked fine until then), + // return the original text and the error, + // with isEncoded=true. + if charsetReaderError { return s, true, err } @@ -805,18 +829,18 @@ func isQtext(r rune) bool { // quoteString renders a string as an RFC 5322 quoted-string. func quoteString(s string) string { - var buf strings.Builder - buf.WriteByte('"') + var b strings.Builder + b.WriteByte('"') for _, r := range s { if isQtext(r) || isWSP(r) { - buf.WriteRune(r) + b.WriteRune(r) } else if isVchar(r) { - buf.WriteByte('\\') - buf.WriteRune(r) + b.WriteByte('\\') + b.WriteRune(r) } } - buf.WriteByte('"') - return buf.String() + b.WriteByte('"') + return b.String() } // isVchar reports whether r is an RFC 5322 VCHAR character. diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go index 80a17b2853ae45..61e50ccfd5c844 100644 --- a/src/net/mail/message_test.go +++ b/src/net/mail/message_test.go @@ -265,11 +265,21 @@ func TestDateParsingCFWS(t *testing.T) { { "Tue, 26 May 2020 14:04:40 UT", time.Date(2020, 05, 26, 14, 04, 40, 0, time.UTC), - false, + true, }, { "Thu, 21 May 2020 14:04:40 UT", time.Date(2020, 05, 21, 14, 04, 40, 0, time.UTC), + true, + }, + { + "Tue, 26 May 2020 14:04:40 XT", + time.Date(2020, 05, 26, 14, 04, 40, 0, time.UTC), + false, + }, + { + "Thu, 21 May 2020 14:04:40 XT", + time.Date(2020, 05, 21, 14, 04, 40, 0, time.UTC), false, }, { @@ -344,6 +354,17 @@ func TestAddressParsingError(t *testing.T) { t.Errorf(`mail.ParseAddress(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err) } } + + t.Run("CustomWordDecoder", func(t *testing.T) { + p := &AddressParser{WordDecoder: &mime.WordDecoder{}} + for i, tc := range mustErrTestCases { + _, err := p.Parse(tc.text) + if err == nil || !strings.Contains(err.Error(), tc.wantErrText) { + t.Errorf(`p.Parse(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err) + } + } + }) + } func TestAddressParsing(t *testing.T) { diff --git a/src/net/main_cloexec_test.go b/src/net/main_cloexec_test.go index 742be2fcd81eef..6ea99ad6469cc9 100644 --- a/src/net/main_cloexec_test.go +++ b/src/net/main_cloexec_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build dragonfly freebsd illumos linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris package net diff --git a/src/net/main_conf_test.go b/src/net/main_conf_test.go index 645b267b780908..41b78eda1d8219 100644 --- a/src/net/main_conf_test.go +++ b/src/net/main_conf_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package net diff --git a/src/net/main_noconf_test.go b/src/net/main_noconf_test.go index bcea630cd3c4cc..ab050fac2b4ec0 100644 --- a/src/net/main_noconf_test.go +++ b/src/net/main_noconf_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (js && wasm) || plan9 || windows -// +build js,wasm plan9 windows package net diff --git a/src/net/main_posix_test.go b/src/net/main_posix_test.go index c9ab25a4ad572e..8899aa9c94ec72 100644 --- a/src/net/main_posix_test.go +++ b/src/net/main_posix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 -// +build !js,!plan9 package net @@ -18,9 +17,9 @@ func enableSocketConnect() { } func disableSocketConnect(network string) { - ss := strings.Split(network, ":") + net, _, _ := strings.Cut(network, ":") sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) { - switch ss[0] { + switch net { case "tcp4": if so.Cookie.Family() == syscall.AF_INET && so.Cookie.Type() == syscall.SOCK_STREAM { return nil, syscall.EHOSTUNREACH diff --git a/src/net/main_test.go b/src/net/main_test.go index dc17d3fbb84d15..1ee8c2efe717b4 100644 --- a/src/net/main_test.go +++ b/src/net/main_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -174,11 +173,8 @@ func runningGoroutines() []string { b := make([]byte, 2<<20) b = b[:runtime.Stack(b, true)] for _, s := range strings.Split(string(b), "\n\n") { - ss := strings.SplitN(s, "\n", 2) - if len(ss) != 2 { - continue - } - stack := strings.TrimSpace(ss[1]) + _, stack, _ := strings.Cut(s, "\n") + stack = strings.TrimSpace(stack) if !strings.Contains(stack, "created by net") { continue } diff --git a/src/net/main_unix_test.go b/src/net/main_unix_test.go index c8cff2d305aa99..e7a5b4fe9ad4ef 100644 --- a/src/net/main_unix_test.go +++ b/src/net/main_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go index b50a1e59a1a923..61c17530c27ef2 100644 --- a/src/net/mockserver_test.go +++ b/src/net/mockserver_test.go @@ -3,54 +3,86 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net import ( + "context" "errors" "fmt" "os" + "path/filepath" "sync" "testing" "time" ) -// testUnixAddr uses os.CreateTemp to get a name that is unique. -func testUnixAddr() string { - f, err := os.CreateTemp("", "go-nettest") +// testUnixAddr uses os.MkdirTemp to get a name that is unique. +func testUnixAddr(t testing.TB) string { + // Pass an empty pattern to get a directory name that is as short as possible. + // If we end up with a name longer than the sun_path field in the sockaddr_un + // struct, we won't be able to make the syscall to open the socket. + d, err := os.MkdirTemp("", "") if err != nil { - panic(err) + t.Fatal(err) } - addr := f.Name() - f.Close() - os.Remove(addr) - return addr + t.Cleanup(func() { + if err := os.RemoveAll(d); err != nil { + t.Error(err) + } + }) + return filepath.Join(d, "sock") } -func newLocalListener(network string) (Listener, error) { +func newLocalListener(t testing.TB, network string, lcOpt ...*ListenConfig) Listener { + var lc *ListenConfig + switch len(lcOpt) { + case 0: + lc = new(ListenConfig) + case 1: + lc = lcOpt[0] + default: + t.Helper() + t.Fatal("too many ListenConfigs passed to newLocalListener: want 0 or 1") + } + + listen := func(net, addr string) Listener { + ln, err := lc.Listen(context.Background(), net, addr) + if err != nil { + t.Helper() + t.Fatal(err) + } + return ln + } + switch network { case "tcp": if supportsIPv4() { + if !supportsIPv6() { + return listen("tcp4", "127.0.0.1:0") + } if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil { - return ln, nil + return ln } } if supportsIPv6() { - return Listen("tcp6", "[::1]:0") + return listen("tcp6", "[::1]:0") } case "tcp4": if supportsIPv4() { - return Listen("tcp4", "127.0.0.1:0") + return listen("tcp4", "127.0.0.1:0") } case "tcp6": if supportsIPv6() { - return Listen("tcp6", "[::1]:0") + return listen("tcp6", "[::1]:0") } case "unix", "unixpacket": - return Listen(network, testUnixAddr()) + return listen(network, testUnixAddr(t)) } - return nil, fmt.Errorf("%s is not supported", network) + + t.Helper() + t.Fatalf("%s is not supported", network) + return nil } func newDualStackListener() (lns []*TCPListener, err error) { @@ -121,12 +153,10 @@ func (ls *localServer) teardown() error { return nil } -func newLocalServer(network string) (*localServer, error) { - ln, err := newLocalListener(network) - if err != nil { - return nil, err - } - return &localServer{Listener: ln, done: make(chan bool)}, nil +func newLocalServer(t testing.TB, network string) *localServer { + t.Helper() + ln := newLocalListener(t, network) + return &localServer{Listener: ln, done: make(chan bool)} } type streamListener struct { @@ -135,8 +165,8 @@ type streamListener struct { done chan bool // signal that indicates server stopped } -func (sl *streamListener) newLocalServer() (*localServer, error) { - return &localServer{Listener: sl.Listener, done: make(chan bool)}, nil +func (sl *streamListener) newLocalServer() *localServer { + return &localServer{Listener: sl.Listener, done: make(chan bool)} } type dualStackServer struct { @@ -288,75 +318,50 @@ func transceiver(c Conn, wb []byte, ch chan<- error) { } } -func timeoutReceiver(c Conn, d, min, max time.Duration, ch chan<- error) { - var err error - defer func() { ch <- err }() - - t0 := time.Now() - if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { - return +func newLocalPacketListener(t testing.TB, network string, lcOpt ...*ListenConfig) PacketConn { + var lc *ListenConfig + switch len(lcOpt) { + case 0: + lc = new(ListenConfig) + case 1: + lc = lcOpt[0] + default: + t.Helper() + t.Fatal("too many ListenConfigs passed to newLocalListener: want 0 or 1") } - b := make([]byte, 256) - var n int - n, err = c.Read(b) - t1 := time.Now() - if n != 0 || err == nil || !err.(Error).Timeout() { - err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) - return - } - if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { - err = fmt.Errorf("Read took %s; expected %s", dt, d) - return - } -} - -func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) { - var err error - defer func() { ch <- err }() - t0 := time.Now() - if err = c.SetWriteDeadline(time.Now().Add(d)); err != nil { - return - } - var n int - for { - n, err = c.Write([]byte("TIMEOUT TRANSMITTER")) + listenPacket := func(net, addr string) PacketConn { + c, err := lc.ListenPacket(context.Background(), net, addr) if err != nil { - break + t.Helper() + t.Fatal(err) } + return c } - t1 := time.Now() - if err == nil || !err.(Error).Timeout() { - err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err) - return - } - if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { - err = fmt.Errorf("Write took %s; expected %s", dt, d) - return - } -} -func newLocalPacketListener(network string) (PacketConn, error) { switch network { case "udp": if supportsIPv4() { - return ListenPacket("udp4", "127.0.0.1:0") + return listenPacket("udp4", "127.0.0.1:0") } if supportsIPv6() { - return ListenPacket("udp6", "[::1]:0") + return listenPacket("udp6", "[::1]:0") } case "udp4": if supportsIPv4() { - return ListenPacket("udp4", "127.0.0.1:0") + return listenPacket("udp4", "127.0.0.1:0") } case "udp6": if supportsIPv6() { - return ListenPacket("udp6", "[::1]:0") + return listenPacket("udp6", "[::1]:0") } case "unixgram": - return ListenPacket(network, testUnixAddr()) + return listenPacket(network, testUnixAddr(t)) } - return nil, fmt.Errorf("%s is not supported", network) + + t.Helper() + t.Fatalf("%s is not supported", network) + return nil } func newDualStackPacketListener() (cs []*UDPConn, err error) { @@ -421,20 +426,18 @@ func (ls *localPacketServer) teardown() error { return nil } -func newLocalPacketServer(network string) (*localPacketServer, error) { - c, err := newLocalPacketListener(network) - if err != nil { - return nil, err - } - return &localPacketServer{PacketConn: c, done: make(chan bool)}, nil +func newLocalPacketServer(t testing.TB, network string) *localPacketServer { + t.Helper() + c := newLocalPacketListener(t, network) + return &localPacketServer{PacketConn: c, done: make(chan bool)} } type packetListener struct { PacketConn } -func (pl *packetListener) newLocalServer() (*localPacketServer, error) { - return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)}, nil +func (pl *packetListener) newLocalServer() *localPacketServer { + return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)} } func packetTransponder(c PacketConn, ch chan<- error) { @@ -505,25 +508,3 @@ func packetTransceiver(c PacketConn, wb []byte, dst Addr, ch chan<- error) { ch <- fmt.Errorf("read %d; want %d", n, len(wb)) } } - -func timeoutPacketReceiver(c PacketConn, d, min, max time.Duration, ch chan<- error) { - var err error - defer func() { ch <- err }() - - t0 := time.Now() - if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { - return - } - b := make([]byte, 256) - var n int - n, _, err = c.ReadFrom(b) - t1 := time.Now() - if n != 0 || err == nil || !err.(Error).Timeout() { - err = fmt.Errorf("ReadFrom did not return (0, timeout): (%d, %v)", n, err) - return - } - if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { - err = fmt.Errorf("ReadFrom took %s; expected %s", dt, d) - return - } -} diff --git a/src/net/net.go b/src/net/net.go index a7c65fff79526d..ff56c31c563437 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -36,7 +36,7 @@ The Listen function creates servers: go handleConnection(conn) } -Name Resolution +# Name Resolution The method for resolving domain names, whether indirectly with functions like Dial or directly with functions like LookupHost and LookupAddr, varies by operating system. @@ -61,7 +61,7 @@ The resolver decision can be overridden by setting the netdns value of the GODEBUG environment variable (see package runtime) to go or cgo, as in: export GODEBUG=netdns=go # force pure Go resolver - export GODEBUG=netdns=cgo # force cgo resolver + export GODEBUG=netdns=cgo # force native resolver (cgo, win32) The decision can also be forced while building the Go source tree by setting the netgo or netcgo build tag. @@ -73,8 +73,8 @@ join the two settings by a plus sign, as in GODEBUG=netdns=go+1. On Plan 9, the resolver always accesses /net/cs and /net/dns. -On Windows, the resolver always uses C library functions, such as GetAddrInfo and DnsQuery. - +On Windows, in Go 1.18.x and earlier, the resolver always used C +library functions, such as GetAddrInfo and DnsQuery. */ package net @@ -125,10 +125,10 @@ type Conn interface { // Any blocked Read or Write operations will be unblocked and return errors. Close() error - // LocalAddr returns the local network address. + // LocalAddr returns the local network address, if known. LocalAddr() Addr - // RemoteAddr returns the remote network address. + // RemoteAddr returns the remote network address, if known. RemoteAddr() Addr // SetDeadline sets the read and write deadlines associated @@ -328,7 +328,7 @@ type PacketConn interface { // Any blocked ReadFrom or WriteTo operations will be unblocked and return errors. Close() error - // LocalAddr returns the local network address. + // LocalAddr returns the local network address, if known. LocalAddr() Addr // SetDeadline sets the read and write deadlines associated @@ -396,8 +396,12 @@ type Listener interface { // An Error represents a network error. type Error interface { error - Timeout() bool // Is the error a timeout? - Temporary() bool // Is the error temporary? + Timeout() bool // Is the error a timeout? + + // Deprecated: Temporary errors are not well-defined. + // Most "temporary" errors are timeouts, and the few exceptions are surprising. + // Do not use this method. + Temporary() bool } // Various errors contained in OpError. @@ -409,15 +413,20 @@ var ( errMissingAddress = errors.New("missing address") // For both read and write operations. - errCanceled = errors.New("operation was canceled") + errCanceled = canceledError{} ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection") ) +// canceledError lets us return the same error string we have always +// returned, while still being Is context.Canceled. +type canceledError struct{} + +func (canceledError) Error() string { return "operation was canceled" } + +func (canceledError) Is(err error) bool { return err == context.Canceled } + // mapErr maps from the context errors to the historical internal net // error values. -// -// TODO(bradfitz): get rid of this after adjusting tests and making -// context.DeadlineExceeded implement net.Error? func mapErr(err error) error { switch err { case context.Canceled: @@ -576,10 +585,14 @@ func (e InvalidAddrError) Temporary() bool { return false } // errTimeout exists to return the historical "i/o timeout" string // for context.DeadlineExceeded. See mapErr. // It is also used when Dialer.Deadline is exceeded. +// error.Is(errTimeout, context.DeadlineExceeded) returns true. // // TODO(iant): We could consider changing this to os.ErrDeadlineExceeded -// in the future, but note that that would conflict with the TODO -// at mapErr that suggests changing it to context.DeadlineExceeded. +// in the future, if we make +// +// errors.Is(os.ErrDeadlineExceeded, context.DeadlineExceeded) +// +// return true. var errTimeout error = &timeoutError{} type timeoutError struct{} @@ -588,6 +601,10 @@ func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } +func (e *timeoutError) Is(err error) bool { + return err == context.DeadlineExceeded +} + // DNSConfigError represents an error reading the machine's DNS configuration. // (No longer used; kept for compatibility.) type DNSConfigError struct { @@ -699,6 +716,12 @@ var ( _ io.Reader = (*Buffers)(nil) ) +// WriteTo writes contents of the buffers to w. +// +// WriteTo implements io.WriterTo for Buffers. +// +// WriteTo modifies the slice v as well as v[i] for 0 <= i < len(v), +// but does not modify v[i][j] for any i, j. func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) { if wv, ok := w.(buffersWriter); ok { return wv.writeBuffers(v) @@ -715,6 +738,12 @@ func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) { return n, nil } +// Read from the buffers. +// +// Read implements io.Reader for Buffers. +// +// Read modifies the slice v as well as v[i] for 0 <= i < len(v), +// but does not modify v[i][j] for any i, j. func (v *Buffers) Read(p []byte) (n int, err error) { for len(p) > 0 && len(*v) > 0 { n0 := copy(p, (*v)[0]) diff --git a/src/net/net_fake.go b/src/net/net_fake.go index 74fc1da6fd80af..6d07d6297a4c56 100644 --- a/src/net/net_fake.go +++ b/src/net/net_fake.go @@ -5,7 +5,6 @@ // Fake networking for js/wasm. It is intended to allow tests of other package to pass. //go:build js && wasm -// +build js,wasm package net @@ -17,6 +16,8 @@ import ( "sync" "syscall" "time" + + "golang.org/x/net/dns/dnsmessage" ) var listenersMu sync.Mutex @@ -266,16 +267,48 @@ func sysSocket(family, sotype, proto int) (int, error) { func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { return 0, nil, syscall.ENOSYS + +} +func (fd *netFD) readFromInet4(p []byte, sa *syscall.SockaddrInet4) (n int, err error) { + return 0, syscall.ENOSYS +} + +func (fd *netFD) readFromInet6(p []byte, sa *syscall.SockaddrInet6) (n int, err error) { + return 0, syscall.ENOSYS } func (fd *netFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int, sa syscall.Sockaddr, err error) { return 0, 0, 0, nil, syscall.ENOSYS } +func (fd *netFD) readMsgInet4(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet4) (n, oobn, retflags int, err error) { + return 0, 0, 0, syscall.ENOSYS +} + +func (fd *netFD) readMsgInet6(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet6) (n, oobn, retflags int, err error) { + return 0, 0, 0, syscall.ENOSYS +} + +func (fd *netFD) writeMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (n int, oobn int, err error) { + return 0, 0, syscall.ENOSYS +} + +func (fd *netFD) writeMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (n int, oobn int, err error) { + return 0, 0, syscall.ENOSYS +} + func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { return 0, syscall.ENOSYS } +func (fd *netFD) writeToInet4(p []byte, sa *syscall.SockaddrInet4) (n int, err error) { + return 0, syscall.ENOSYS +} + +func (fd *netFD) writeToInet6(p []byte, sa *syscall.SockaddrInet6) (n int, err error) { + return 0, syscall.ENOSYS +} + func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { return 0, 0, syscall.ENOSYS } @@ -283,3 +316,7 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob func (fd *netFD) dup() (f *os.File, err error) { return nil, syscall.ENOSYS } + +func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) { + panic("unreachable") +} diff --git a/src/net/net_test.go b/src/net/net_test.go index 6e7be4db23c1e2..05c058a46c1ac2 100644 --- a/src/net/net_test.go +++ b/src/net/net_test.go @@ -3,14 +3,12 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net import ( "errors" "fmt" - "internal/testenv" "io" "net/internal/socktest" "os" @@ -34,10 +32,7 @@ func TestCloseRead(t *testing.T) { } t.Parallel() - ln, err := newLocalListener(network) - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, network) switch network { case "unix", "unixpacket": defer os.Remove(ln.Addr().String()) @@ -102,6 +97,17 @@ func TestCloseWrite(t *testing.T) { t.Error(err) return } + + // Workaround for https://go.dev/issue/49352. + // On arm64 macOS (current as of macOS 12.4), + // reading from a socket at the same time as the client + // is closing it occasionally hangs for 60 seconds before + // returning ECONNRESET. Sleep for a bit to give the + // socket time to close before trying to read from it. + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + time.Sleep(10 * time.Millisecond) + } + if !deadline.IsZero() { c.SetDeadline(deadline) } @@ -133,10 +139,7 @@ func TestCloseWrite(t *testing.T) { } } - ls, err := newLocalServer(network) - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, network) defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -190,10 +193,7 @@ func TestConnClose(t *testing.T) { } t.Parallel() - ln, err := newLocalListener(network) - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, network) switch network { case "unix", "unixpacket": defer os.Remove(ln.Addr().String()) @@ -235,16 +235,12 @@ func TestListenerClose(t *testing.T) { } t.Parallel() - ln, err := newLocalListener(network) - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, network) switch network { case "unix", "unixpacket": defer os.Remove(ln.Addr().String()) } - dst := ln.Addr().String() if err := ln.Close(); err != nil { if perr := parseCloseError(err, false); perr != nil { t.Error(perr) @@ -257,28 +253,12 @@ func TestListenerClose(t *testing.T) { t.Fatal("should fail") } - if network == "tcp" { - // We will have two TCP FSMs inside the - // kernel here. There's no guarantee that a - // signal comes from the far end FSM will be - // delivered immediately to the near end FSM, - // especially on the platforms that allow - // multiple consumer threads to pull pending - // established connections at the same time by - // enabling SO_REUSEPORT option such as Linux, - // DragonFly BSD. So we need to give some time - // quantum to the kernel. - // - // Note that net.inet.tcp.reuseport_ext=1 by - // default on DragonFly BSD. - time.Sleep(time.Millisecond) - - cc, err := Dial("tcp", dst) - if err == nil { - t.Error("Dial to closed TCP listener succeeded.") - cc.Close() - } - } + // Note: we cannot ensure that a subsequent Dial does not succeed, because + // we do not in general have any guarantee that ln.Addr is not immediately + // reused. (TCP sockets enter a TIME_WAIT state when closed, but that only + // applies to existing connections for the port — it does not prevent the + // port itself from being used for entirely new connections in the + // meantime.) }) } } @@ -293,10 +273,7 @@ func TestPacketConnClose(t *testing.T) { } t.Parallel() - c, err := newLocalPacketListener(network) - if err != nil { - t.Fatal(err) - } + c := newLocalPacketListener(t, network) switch network { case "unixgram": defer os.Remove(c.LocalAddr().String()) @@ -321,18 +298,17 @@ func TestPacketConnClose(t *testing.T) { func TestListenCloseListen(t *testing.T) { const maxTries = 10 for tries := 0; tries < maxTries; tries++ { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") addr := ln.Addr().String() + // TODO: This is racy. The selected address could be reused in between this + // Close and the subsequent Listen. if err := ln.Close(); err != nil { if perr := parseCloseError(err, false); perr != nil { t.Error(perr) } t.Fatal(err) } - ln, err = Listen("tcp", addr) + ln, err := Listen("tcp", addr) if err == nil { // Success. (This test didn't always make it here earlier.) ln.Close() @@ -378,10 +354,7 @@ func TestAcceptIgnoreAbortedConnRequest(t *testing.T) { } c.Close() } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -408,10 +381,7 @@ func TestZeroByteRead(t *testing.T) { } t.Parallel() - ln, err := newLocalListener(network) - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, network) connc := make(chan Conn, 1) go func() { defer ln.Close() @@ -460,10 +430,8 @@ func TestZeroByteRead(t *testing.T) { // runs peer1 and peer2 concurrently. withTCPConnPair returns when // both have completed. func withTCPConnPair(t *testing.T, peer1, peer2 func(c *TCPConn) error) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + t.Helper() + ln := newLocalListener(t, "tcp") defer ln.Close() errc := make(chan error, 2) go func() { @@ -558,35 +526,50 @@ func TestCloseUnblocksRead(t *testing.T) { // Issue 24808: verify that ECONNRESET is not temporary for read. func TestNotTemporaryRead(t *testing.T) { - if runtime.GOOS == "freebsd" { - testenv.SkipFlaky(t, 25289) - } - if runtime.GOOS == "aix" { - testenv.SkipFlaky(t, 29685) - } t.Parallel() - server := func(cs *TCPConn) error { - cs.SetLinger(0) - // Give the client time to get stuck in a Read. - time.Sleep(50 * time.Millisecond) + + ln := newLocalListener(t, "tcp") + serverDone := make(chan struct{}) + dialed := make(chan struct{}) + go func() { + defer close(serverDone) + + cs, err := ln.Accept() + if err != nil { + return + } + <-dialed + cs.(*TCPConn).SetLinger(0) cs.Close() - return nil + }() + defer func() { + ln.Close() + <-serverDone + }() + + ss, err := Dial("tcp", ln.Addr().String()) + close(dialed) + if err != nil { + t.Fatal(err) } - client := func(ss *TCPConn) error { - _, err := ss.Read([]byte{0}) - if err == nil { - return errors.New("Read succeeded unexpectedly") - } else if err == io.EOF { - // This happens on Plan 9. - return nil - } else if ne, ok := err.(Error); !ok { - return fmt.Errorf("unexpected error %v", err) - } else if ne.Temporary() { - return fmt.Errorf("unexpected temporary error %v", err) + defer ss.Close() + + _, err = ss.Read([]byte{0}) + if err == nil { + t.Fatal("Read succeeded unexpectedly") + } else if err == io.EOF { + // This happens on Plan 9, but for some reason (prior to CL 385314) it was + // accepted everywhere else too. + if runtime.GOOS == "plan9" { + return } - return nil + t.Fatal("Read unexpectedly returned io.EOF after socket was abruptly closed") + } + if ne, ok := err.(Error); !ok { + t.Errorf("Read error does not implement net.Error: %v", err) + } else if ne.Temporary() { + t.Errorf("Read error is unexpectedly temporary: %v", err) } - withTCPConnPair(t, client, server) } // The various errors should implement the Error interface. diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index 2a563a078c392b..947dda56f28abf 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "fmt" + "internal/testenv" "io" "os" "os/exec" @@ -205,6 +206,13 @@ func runCmd(args ...string) ([]byte, error) { } func checkNetsh(t *testing.T) { + if testenv.Builder() == "windows-arm64-10" { + // netsh was observed to sometimes hang on this builder. + // We have not observed failures on windows-arm64-11, so for the + // moment we are leaving the test enabled elsewhere on the theory + // that it may have been a platform bug fixed in Windows 11. + testenv.SkipFlaky(t, 52082) + } out, err := runCmd("netsh", "help") if err != nil { t.Fatal(err) diff --git a/src/net/netgo.go b/src/net/netgo.go new file mode 100644 index 00000000000000..75baa880354c5c --- /dev/null +++ b/src/net/netgo.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Default netGo to true if the netgo build tag is being used, or the +// C library DNS routines are not available. Note that the C library +// routines are always available on Windows. + +//go:build netgo || (!cgo && !windows) + +package net + +func init() { netGo = true } diff --git a/src/net/netgo_unix_test.go b/src/net/netgo_unix_test.go index 5551e47de7ff7d..019772aa6aaab4 100644 --- a/src/net/netgo_unix_test.go +++ b/src/net/netgo_unix_test.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!cgo || netgo) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) -// +build !cgo netgo -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/netip/export_test.go b/src/net/netip/export_test.go new file mode 100644 index 00000000000000..59971fa2e4e519 --- /dev/null +++ b/src/net/netip/export_test.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip + +import "internal/intern" + +var ( + Z0 = z0 + Z4 = z4 + Z6noz = z6noz +) + +type Uint128 = uint128 + +func Mk128(hi, lo uint64) Uint128 { + return uint128{hi, lo} +} + +func MkAddr(u Uint128, z *intern.Value) Addr { + return Addr{u, z} +} + +func IPv4(a, b, c, d uint8) Addr { return AddrFrom4([4]byte{a, b, c, d}) } + +var TestAppendToMarshal = testAppendToMarshal + +func (a Addr) IsZero() bool { return a.isZero() } +func (p Prefix) IsZero() bool { return p.isZero() } diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go new file mode 100644 index 00000000000000..c9fc6c98e993ef --- /dev/null +++ b/src/net/netip/fuzz_test.go @@ -0,0 +1,351 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip_test + +import ( + "bytes" + "encoding" + "fmt" + "net" + . "net/netip" + "reflect" + "strings" + "testing" +) + +var corpus = []string{ + // Basic zero IPv4 address. + "0.0.0.0", + // Basic non-zero IPv4 address. + "192.168.140.255", + // IPv4 address in windows-style "print all the digits" form. + "010.000.015.001", + // IPv4 address with a silly amount of leading zeros. + "000001.00000002.00000003.000000004", + // 4-in-6 with octet with leading zero + "::ffff:1.2.03.4", + // Basic zero IPv6 address. + "::", + // Localhost IPv6. + "::1", + // Fully expanded IPv6 address. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", + // IPv6 with elided fields in the middle. + "fd7a:115c::626b:430b", + // IPv6 with elided fields at the end. + "fd7a:115c:a1e0:ab12:4843:cd96::", + // IPv6 with single elided field at the end. + "fd7a:115c:a1e0:ab12:4843:cd96:626b::", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:0", + // IPv6 with single elided field in the middle. + "fd7a:115c:a1e0::4843:cd96:626b:430b", + "fd7a:115c:a1e0:0:4843:cd96:626b:430b", + // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6) + "::ffff:192.168.140.255", + "::ffff:192.168.140.255", + // IPv6 with a zone specifier. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", + // IPv6 with dotted decimal and zone specifier. + "1:2::ffff:192.168.140.255%eth1", + "1:2::ffff:c0a8:8cff%eth1", + // IPv6 with capital letters. + "FD9E:1A04:F01D::1", + "fd9e:1a04:f01d::1", + // Empty string. + "", + // Garbage non-IP. + "bad", + // Single number. Some parsers accept this as an IPv4 address in + // big-endian uint32 form, but we don't. + "1234", + // IPv4 with a zone specifier. + "1.2.3.4%eth0", + // IPv4 field must have at least one digit. + ".1.2.3", + "1.2.3.", + "1..2.3", + // IPv4 address too long. + "1.2.3.4.5", + // IPv4 in dotted octal form. + "0300.0250.0214.0377", + // IPv4 in dotted hex form. + "0xc0.0xa8.0x8c.0xff", + // IPv4 in class B form. + "192.168.12345", + // IPv4 in class B form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.0.1", + // IPv4 in class A form. + "192.1234567", + // IPv4 in class A form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.1", + // IPv4 field has value >255. + "192.168.300.1", + // IPv4 with too many fields. + "192.168.0.1.5.6", + // IPv6 with not enough fields. + "1:2:3:4:5:6:7", + // IPv6 with too many fields. + "1:2:3:4:5:6:7:8:9", + // IPv6 with 8 fields and a :: expander. + "1:2:3:4::5:6:7:8", + // IPv6 with a field bigger than 2b. + "fe801::1", + // IPv6 with non-hex values in field. + "fe80:tail:scal:e::", + // IPv6 with a zone delimiter but no zone. + "fe80::1%", + // IPv6 with a zone specifier of zero. + "::ffff:0:0%0", + // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4. + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4. + "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 with invalid embedded IPv4. + "::ffff:192.168.140.bad", + // IPv6 with multiple ellipsis ::. + "fe80::1::1", + // IPv6 with invalid non hex/colon character. + "fe80:1?:1", + // IPv6 with truncated bytes after single colon. + "fe80:", + // AddrPort strings. + "1.2.3.4:51820", + "[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", + "[::ffff:c000:0280]:65535", + "[::ffff:c000:0280%eth0]:1", + // Prefix strings. + "1.2.3.4/24", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118", + "::ffff:c000:0280/96", + "::ffff:c000:0280%eth0/37", +} + +func FuzzParse(f *testing.F) { + for _, seed := range corpus { + f.Add(seed) + } + + f.Fuzz(func(t *testing.T, s string) { + ip, _ := ParseAddr(s) + checkStringParseRoundTrip(t, ip, ParseAddr) + checkEncoding(t, ip) + + // Check that we match the net's IP parser, modulo zones. + if !strings.Contains(s, "%") { + stdip := net.ParseIP(s) + if !ip.IsValid() != (stdip == nil) { + t.Errorf("ParseAddr zero != net.ParseIP nil: ip=%q stdip=%q", ip, stdip) + } + + if ip.IsValid() && !ip.Is4In6() { + buf, err := ip.MarshalText() + if err != nil { + t.Fatal(err) + } + buf2, err := stdip.MarshalText() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf, buf2) { + t.Errorf("Addr.MarshalText() != net.IP.MarshalText(): ip=%q stdip=%q", ip, stdip) + } + if ip.String() != stdip.String() { + t.Errorf("Addr.String() != net.IP.String(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsGlobalUnicast() != stdip.IsGlobalUnicast() { + t.Errorf("Addr.IsGlobalUnicast() != net.IP.IsGlobalUnicast(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsInterfaceLocalMulticast() != stdip.IsInterfaceLocalMulticast() { + t.Errorf("Addr.IsInterfaceLocalMulticast() != net.IP.IsInterfaceLocalMulticast(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsLinkLocalMulticast() != stdip.IsLinkLocalMulticast() { + t.Errorf("Addr.IsLinkLocalMulticast() != net.IP.IsLinkLocalMulticast(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsLinkLocalUnicast() != stdip.IsLinkLocalUnicast() { + t.Errorf("Addr.IsLinkLocalUnicast() != net.IP.IsLinkLocalUnicast(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsLoopback() != stdip.IsLoopback() { + t.Errorf("Addr.IsLoopback() != net.IP.IsLoopback(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsMulticast() != stdip.IsMulticast() { + t.Errorf("Addr.IsMulticast() != net.IP.IsMulticast(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsPrivate() != stdip.IsPrivate() { + t.Errorf("Addr.IsPrivate() != net.IP.IsPrivate(): ip=%q stdip=%q", ip, stdip) + } + if ip.IsUnspecified() != stdip.IsUnspecified() { + t.Errorf("Addr.IsUnspecified() != net.IP.IsUnspecified(): ip=%q stdip=%q", ip, stdip) + } + } + } + + // Check that .Next().Prev() and .Prev().Next() preserve the IP. + if ip.IsValid() && ip.Next().IsValid() && ip.Next().Prev() != ip { + t.Errorf(".Next.Prev did not round trip: ip=%q .next=%q .next.prev=%q", ip, ip.Next(), ip.Next().Prev()) + } + if ip.IsValid() && ip.Prev().IsValid() && ip.Prev().Next() != ip { + t.Errorf(".Prev.Next did not round trip: ip=%q .prev=%q .prev.next=%q", ip, ip.Prev(), ip.Prev().Next()) + } + + port, err := ParseAddrPort(s) + if err == nil { + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + } + port = AddrPortFrom(ip, 80) + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + + ipp, err := ParsePrefix(s) + if err == nil { + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + } + ipp = PrefixFrom(ip, 8) + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + }) +} + +// checkTextMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) + err = y.UnmarshalText(buf) + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Fatalf("(%T).UnmarshalText(%q) = %v", y, buf, err) + } + e := reflect.ValueOf(y).Elem().Interface() + if !reflect.DeepEqual(x, e) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("MarshalText/UnmarshalText failed to round trip: %#v != %#v", x, e) + } + buf2, err := y.(encoding.TextMarshaler).MarshalText() + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalText a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalText() = %q", y, buf2) + t.Fatalf("second MarshalText differs from first: %q != %q", buf, buf2) + } +} + +// checkBinaryMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { + buf, err := x.MarshalBinary() + if err != nil { + t.Fatal(err) + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) + err = y.UnmarshalBinary(buf) + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Fatalf("(%T).UnmarshalBinary(%q) = %v", y, buf, err) + } + e := reflect.ValueOf(y).Elem().Interface() + if !reflect.DeepEqual(x, e) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %#v != %#v", x, e) + } + buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalBinary a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalBinary() = %q", y, buf2) + t.Fatalf("second MarshalBinary differs from first: %q != %q", buf, buf2) + } +} + +func checkTextMarshalMatchesString(t *testing.T, x netipType) { + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + str := x.String() + if string(buf) != str { + t.Fatalf("%v: MarshalText = %q, String = %q", x, buf, str) + } +} + +type appendMarshaler interface { + encoding.TextMarshaler + AppendTo([]byte) []byte +} + +// checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo. +func checkTextMarshalMatchesAppendTo(t *testing.T, x appendMarshaler) { + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + + buf2 := make([]byte, 0, len(buf)) + buf2 = x.AppendTo(buf2) + if !bytes.Equal(buf, buf2) { + t.Fatalf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2) + } +} + +type netipType interface { + encoding.BinaryMarshaler + encoding.TextMarshaler + fmt.Stringer + IsValid() bool +} + +type netipTypeCmp interface { + comparable + netipType +} + +// checkStringParseRoundTrip checks that x's String method and the provided parse function can round trip correctly. +func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(string) (P, error)) { + if !x.IsValid() { + // Ignore invalid values. + return + } + + s := x.String() + y, err := parse(s) + if err != nil { + t.Fatalf("s=%q err=%v", s, err) + } + if x != y { + t.Fatalf("%T round trip identity failure: s=%q x=%#v y=%#v", x, s, x, y) + } + s2 := y.String() + if s != s2 { + t.Fatalf("%T String round trip identity failure: s=%#v s2=%#v", x, s, s2) + } +} + +func checkEncoding(t *testing.T, x netipType) { + if x.IsValid() { + checkTextMarshaler(t, x) + checkBinaryMarshaler(t, x) + checkTextMarshalMatchesString(t, x) + } + + if am, ok := x.(appendMarshaler); ok { + checkTextMarshalMatchesAppendTo(t, am) + } +} diff --git a/src/net/netip/inlining_test.go b/src/net/netip/inlining_test.go new file mode 100644 index 00000000000000..52991bee8c23b3 --- /dev/null +++ b/src/net/netip/inlining_test.go @@ -0,0 +1,105 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip + +import ( + "internal/testenv" + "os/exec" + "regexp" + "runtime" + "strings" + "testing" +) + +func TestInlining(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + out, err := exec.Command( + testenv.GoToolPath(t), + "build", + "--gcflags=-m", + "net/netip").CombinedOutput() + if err != nil { + t.Fatalf("go build: %v, %s", err, out) + } + got := map[string]bool{} + regexp.MustCompile(` can inline (\S+)`).ReplaceAllFunc(out, func(match []byte) []byte { + got[strings.TrimPrefix(string(match), " can inline ")] = true + return nil + }) + wantInlinable := []string{ + "(*uint128).halves", + "Addr.BitLen", + "Addr.hasZone", + "Addr.Is4", + "Addr.Is4In6", + "Addr.Is6", + "Addr.IsLoopback", + "Addr.IsMulticast", + "Addr.IsInterfaceLocalMulticast", + "Addr.IsValid", + "Addr.IsUnspecified", + "Addr.Less", + "Addr.lessOrEq", + "Addr.Unmap", + "Addr.Zone", + "Addr.v4", + "Addr.v6", + "Addr.v6u16", + "Addr.withoutZone", + "AddrPortFrom", + "AddrPort.Addr", + "AddrPort.Port", + "AddrPort.IsValid", + "Prefix.IsSingleIP", + "Prefix.Masked", + "Prefix.IsValid", + "PrefixFrom", + "Prefix.Addr", + "Prefix.Bits", + "AddrFrom4", + "IPv6LinkLocalAllNodes", + "IPv6Unspecified", + "MustParseAddr", + "MustParseAddrPort", + "MustParsePrefix", + "appendDecimal", + "appendHex", + "uint128.addOne", + "uint128.and", + "uint128.bitsClearedFrom", + "uint128.bitsSetFrom", + "uint128.isZero", + "uint128.not", + "uint128.or", + "uint128.subOne", + "uint128.xor", + } + switch runtime.GOARCH { + case "amd64", "arm64": + // These don't inline on 32-bit. + wantInlinable = append(wantInlinable, + "u64CommonPrefixLen", + "uint128.commonPrefixLen", + "Addr.Next", + "Addr.Prev", + ) + } + + for _, want := range wantInlinable { + if !got[want] { + t.Errorf("%q is no longer inlinable", want) + continue + } + delete(got, want) + } + for sym := range got { + if strings.Contains(sym, ".func") { + continue + } + t.Logf("not in expected set, but also inlinable: %q", sym) + + } +} diff --git a/src/net/netip/leaf_alts.go b/src/net/netip/leaf_alts.go new file mode 100644 index 00000000000000..70513abfd920ce --- /dev/null +++ b/src/net/netip/leaf_alts.go @@ -0,0 +1,54 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stuff that exists in std, but we can't use due to being a dependency +// of net, for go/build deps_test policy reasons. + +package netip + +func stringsLastIndexByte(s string, b byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == b { + return i + } + } + return -1 +} + +func beUint64(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 +} + +func bePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +func bePutUint32(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 leUint16(b []byte) uint16 { + _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 + return uint16(b[0]) | uint16(b[1])<<8 +} + +func lePutUint16(b []byte, v uint16) { + _ = b[1] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) +} diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go new file mode 100644 index 00000000000000..b5d55acdb36d15 --- /dev/null +++ b/src/net/netip/netip.go @@ -0,0 +1,1504 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package netip defines an IP address type that's a small value type. +// Building on that Addr type, the package also defines AddrPort (an +// IP address and a port), and Prefix (an IP address and a bit length +// prefix). +// +// Compared to the net.IP type, this package's Addr type takes less +// memory, is immutable, and is comparable (supports == and being a +// map key). +package netip + +import ( + "errors" + "math" + "strconv" + + "internal/bytealg" + "internal/intern" + "internal/itoa" +) + +// Sizes: (64-bit) +// net.IP: 24 byte slice header + {4, 16} = 28 to 40 bytes +// net.IPAddr: 40 byte slice header + {4, 16} = 44 to 56 bytes + zone length +// netip.Addr: 24 bytes (zone is per-name singleton, shared across all users) + +// Addr represents an IPv4 or IPv6 address (with or without a scoped +// addressing zone), similar to net.IP or net.IPAddr. +// +// Unlike net.IP or net.IPAddr, Addr is a comparable value +// type (it supports == and can be a map key) and is immutable. +// +// The zero Addr is not a valid IP address. +// Addr{} is distinct from both 0.0.0.0 and ::. +type Addr struct { + // addr is the hi and lo bits of an IPv6 address. If z==z4, + // hi and lo contain the IPv4-mapped IPv6 address. + // + // hi and lo are constructed by interpreting a 16-byte IPv6 + // address as a big-endian 128-bit number. The most significant + // bits of that number go into hi, the rest into lo. + // + // For example, 0011:2233:4455:6677:8899:aabb:ccdd:eeff is stored as: + // addr.hi = 0x0011223344556677 + // addr.lo = 0x8899aabbccddeeff + // + // We store IPs like this, rather than as [16]byte, because it + // turns most operations on IPs into arithmetic and bit-twiddling + // operations on 64-bit registers, which is much faster than + // bytewise processing. + addr uint128 + + // z is a combination of the address family and the IPv6 zone. + // + // nil means invalid IP address (for a zero Addr). + // z4 means an IPv4 address. + // z6noz means an IPv6 address without a zone. + // + // Otherwise it's the interned zone name string. + z *intern.Value +} + +// z0, z4, and z6noz are sentinel Addr.z values. +// See the Addr type's field docs. +var ( + z0 = (*intern.Value)(nil) + z4 = new(intern.Value) + z6noz = new(intern.Value) +) + +// IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast +// address ff02::1. +func IPv6LinkLocalAllNodes() Addr { return AddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) } + +// IPv6Unspecified returns the IPv6 unspecified address "::". +func IPv6Unspecified() Addr { return Addr{z: z6noz} } + +// IPv4Unspecified returns the IPv4 unspecified address "0.0.0.0". +func IPv4Unspecified() Addr { return AddrFrom4([4]byte{}) } + +// AddrFrom4 returns the address of the IPv4 address given by the bytes in addr. +func AddrFrom4(addr [4]byte) Addr { + return Addr{ + addr: uint128{0, 0xffff00000000 | uint64(addr[0])<<24 | uint64(addr[1])<<16 | uint64(addr[2])<<8 | uint64(addr[3])}, + z: z4, + } +} + +// AddrFrom16 returns the IPv6 address given by the bytes in addr. +// An IPv4-mapped IPv6 address is left as an IPv6 address. +// (Use Unmap to convert them if needed.) +func AddrFrom16(addr [16]byte) Addr { + return Addr{ + addr: uint128{ + beUint64(addr[:8]), + beUint64(addr[8:]), + }, + z: z6noz, + } +} + +// ipv6Slice is like IPv6Raw, but operates on a 16-byte slice. Assumes +// slice is 16 bytes, caller must enforce this. +func ipv6Slice(addr []byte) Addr { + return Addr{ + addr: uint128{ + beUint64(addr[:8]), + beUint64(addr[8:]), + }, + z: z6noz, + } +} + +// ParseAddr parses s as an IP address, returning the result. The string +// s can be in dotted decimal ("192.0.2.1"), IPv6 ("2001:db8::68"), +// or IPv6 with a scoped addressing zone ("fe80::1cc0:3e8c:119f:c2e1%ens18"). +func ParseAddr(s string) (Addr, error) { + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return parseIPv4(s) + case ':': + return parseIPv6(s) + case '%': + // Assume that this was trying to be an IPv6 address with + // a zone specifier, but the address is missing. + return Addr{}, parseAddrError{in: s, msg: "missing IPv6 address"} + } + } + return Addr{}, parseAddrError{in: s, msg: "unable to parse IP"} +} + +// MustParseAddr calls ParseAddr(s) and panics on error. +// It is intended for use in tests with hard-coded strings. +func MustParseAddr(s string) Addr { + ip, err := ParseAddr(s) + if err != nil { + panic(err) + } + return ip +} + +type parseAddrError struct { + in string // the string given to ParseAddr + msg string // an explanation of the parse failure + at string // optionally, the unparsed portion of in at which the error occurred. +} + +func (err parseAddrError) Error() string { + q := strconv.Quote + if err.at != "" { + return "ParseAddr(" + q(err.in) + "): " + err.msg + " (at " + q(err.at) + ")" + } + return "ParseAddr(" + q(err.in) + "): " + err.msg +} + +// parseIPv4 parses s as an IPv4 address (in form "192.168.0.1"). +func parseIPv4(s string) (ip Addr, err error) { + var fields [4]uint8 + var val, pos int + var digLen int // number of digits in current octet + for i := 0; i < len(s); i++ { + if s[i] >= '0' && s[i] <= '9' { + if digLen == 1 && val == 0 { + return Addr{}, parseAddrError{in: s, msg: "IPv4 field has octet with leading zero"} + } + val = val*10 + int(s[i]) - '0' + digLen++ + if val > 255 { + return Addr{}, parseAddrError{in: s, msg: "IPv4 field has value >255"} + } + } else if s[i] == '.' { + // .1.2.3 + // 1.2.3. + // 1..2.3 + if i == 0 || i == len(s)-1 || s[i-1] == '.' { + return Addr{}, parseAddrError{in: s, msg: "IPv4 field must have at least one digit", at: s[i:]} + } + // 1.2.3.4.5 + if pos == 3 { + return Addr{}, parseAddrError{in: s, msg: "IPv4 address too long"} + } + fields[pos] = uint8(val) + pos++ + val = 0 + digLen = 0 + } else { + return Addr{}, parseAddrError{in: s, msg: "unexpected character", at: s[i:]} + } + } + if pos < 3 { + return Addr{}, parseAddrError{in: s, msg: "IPv4 address too short"} + } + fields[3] = uint8(val) + return AddrFrom4(fields), nil +} + +// parseIPv6 parses s as an IPv6 address (in form "2001:db8::68"). +func parseIPv6(in string) (Addr, error) { + s := in + + // Split off the zone right from the start. Yes it's a second scan + // of the string, but trying to handle it inline makes a bunch of + // other inner loop conditionals more expensive, and it ends up + // being slower. + zone := "" + i := bytealg.IndexByteString(s, '%') + if i != -1 { + s, zone = s[:i], s[i+1:] + if zone == "" { + // Not allowed to have an empty zone if explicitly specified. + return Addr{}, parseAddrError{in: in, msg: "zone must be a non-empty string"} + } + } + + var ip [16]byte + ellipsis := -1 // position of ellipsis in ip + + // Might have leading ellipsis + if len(s) >= 2 && s[0] == ':' && s[1] == ':' { + ellipsis = 0 + s = s[2:] + // Might be only ellipsis + if len(s) == 0 { + return IPv6Unspecified().WithZone(zone), nil + } + } + + // Loop, parsing hex numbers followed by colon. + i = 0 + for i < 16 { + // Hex number. Similar to parseIPv4, inlining the hex number + // parsing yields a significant performance increase. + off := 0 + acc := uint32(0) + for ; off < len(s); off++ { + c := s[off] + if c >= '0' && c <= '9' { + acc = (acc << 4) + uint32(c-'0') + } else if c >= 'a' && c <= 'f' { + acc = (acc << 4) + uint32(c-'a'+10) + } else if c >= 'A' && c <= 'F' { + acc = (acc << 4) + uint32(c-'A'+10) + } else { + break + } + if acc > math.MaxUint16 { + // Overflow, fail. + return Addr{}, parseAddrError{in: in, msg: "IPv6 field has value >=2^16", at: s} + } + } + if off == 0 { + // No digits found, fail. + return Addr{}, parseAddrError{in: in, msg: "each colon-separated field must have at least one digit", at: s} + } + + // If followed by dot, might be in trailing IPv4. + if off < len(s) && s[off] == '.' { + if ellipsis < 0 && i != 12 { + // Not the right place. + return Addr{}, parseAddrError{in: in, msg: "embedded IPv4 address must replace the final 2 fields of the address", at: s} + } + if i+4 > 16 { + // Not enough room. + return Addr{}, parseAddrError{in: in, msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: s} + } + // TODO: could make this a bit faster by having a helper + // that parses to a [4]byte, and have both parseIPv4 and + // parseIPv6 use it. + ip4, err := parseIPv4(s) + if err != nil { + return Addr{}, parseAddrError{in: in, msg: err.Error(), at: s} + } + ip[i] = ip4.v4(0) + ip[i+1] = ip4.v4(1) + ip[i+2] = ip4.v4(2) + ip[i+3] = ip4.v4(3) + s = "" + i += 4 + break + } + + // Save this 16-bit chunk. + ip[i] = byte(acc >> 8) + ip[i+1] = byte(acc) + i += 2 + + // Stop at end of string. + s = s[off:] + if len(s) == 0 { + break + } + + // Otherwise must be followed by colon and more. + if s[0] != ':' { + return Addr{}, parseAddrError{in: in, msg: "unexpected character, want colon", at: s} + } else if len(s) == 1 { + return Addr{}, parseAddrError{in: in, msg: "colon must be followed by more characters", at: s} + } + s = s[1:] + + // Look for ellipsis. + if s[0] == ':' { + if ellipsis >= 0 { // already have one + return Addr{}, parseAddrError{in: in, msg: "multiple :: in address", at: s} + } + ellipsis = i + s = s[1:] + if len(s) == 0 { // can be at end + break + } + } + } + + // Must have used entire string. + if len(s) != 0 { + return Addr{}, parseAddrError{in: in, msg: "trailing garbage after address", at: s} + } + + // If didn't parse enough, expand ellipsis. + if i < 16 { + if ellipsis < 0 { + return Addr{}, parseAddrError{in: in, msg: "address string too short"} + } + n := 16 - i + for j := i - 1; j >= ellipsis; j-- { + ip[j+n] = ip[j] + } + for j := ellipsis + n - 1; j >= ellipsis; j-- { + ip[j] = 0 + } + } else if ellipsis >= 0 { + // Ellipsis must represent at least one 0 group. + return Addr{}, parseAddrError{in: in, msg: "the :: must expand to at least one field of zeros"} + } + return AddrFrom16(ip).WithZone(zone), nil +} + +// AddrFromSlice parses the 4- or 16-byte byte slice as an IPv4 or IPv6 address. +// Note that a net.IP can be passed directly as the []byte argument. +// If slice's length is not 4 or 16, AddrFromSlice returns Addr{}, false. +func AddrFromSlice(slice []byte) (ip Addr, ok bool) { + switch len(slice) { + case 4: + return AddrFrom4(*(*[4]byte)(slice)), true + case 16: + return ipv6Slice(slice), true + } + return Addr{}, false +} + +// v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns +// unspecified garbage. +func (ip Addr) v4(i uint8) uint8 { + return uint8(ip.addr.lo >> ((3 - i) * 8)) +} + +// v6 returns the i'th byte of ip. If ip is an IPv4 address, this +// accesses the IPv4-mapped IPv6 address form of the IP. +func (ip Addr) v6(i uint8) uint8 { + return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8)) +} + +// v6u16 returns the i'th 16-bit word of ip. If ip is an IPv4 address, +// this accesses the IPv4-mapped IPv6 address form of the IP. +func (ip Addr) v6u16(i uint8) uint16 { + return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16)) +} + +// isZero reports whether ip is the zero value of the IP type. +// The zero value is not a valid IP address of any type. +// +// Note that "0.0.0.0" and "::" are not the zero value. Use IsUnspecified to +// check for these values instead. +func (ip Addr) isZero() bool { + // Faster than comparing ip == Addr{}, but effectively equivalent, + // as there's no way to make an IP with a nil z from this package. + return ip.z == z0 +} + +// IsValid reports whether the Addr is an initialized address (not the zero Addr). +// +// Note that "0.0.0.0" and "::" are both valid values. +func (ip Addr) IsValid() bool { return ip.z != z0 } + +// BitLen returns the number of bits in the IP address: +// 128 for IPv6, 32 for IPv4, and 0 for the zero Addr. +// +// Note that IPv4-mapped IPv6 addresses are considered IPv6 addresses +// and therefore have bit length 128. +func (ip Addr) BitLen() int { + switch ip.z { + case z0: + return 0 + case z4: + return 32 + } + return 128 +} + +// Zone returns ip's IPv6 scoped addressing zone, if any. +func (ip Addr) Zone() string { + if ip.z == nil { + return "" + } + zone, _ := ip.z.Get().(string) + return zone +} + +// Compare returns an integer comparing two IPs. +// The result will be 0 if ip == ip2, -1 if ip < ip2, and +1 if ip > ip2. +// The definition of "less than" is the same as the Less method. +func (ip Addr) Compare(ip2 Addr) int { + f1, f2 := ip.BitLen(), ip2.BitLen() + if f1 < f2 { + return -1 + } + if f1 > f2 { + return 1 + } + hi1, hi2 := ip.addr.hi, ip2.addr.hi + if hi1 < hi2 { + return -1 + } + if hi1 > hi2 { + return 1 + } + lo1, lo2 := ip.addr.lo, ip2.addr.lo + if lo1 < lo2 { + return -1 + } + if lo1 > lo2 { + return 1 + } + if ip.Is6() { + za, zb := ip.Zone(), ip2.Zone() + if za < zb { + return -1 + } + if za > zb { + return 1 + } + } + return 0 +} + +// Less reports whether ip sorts before ip2. +// IP addresses sort first by length, then their address. +// IPv6 addresses with zones sort just after the same address without a zone. +func (ip Addr) Less(ip2 Addr) bool { return ip.Compare(ip2) == -1 } + +func (ip Addr) lessOrEq(ip2 Addr) bool { return ip.Compare(ip2) <= 0 } + +// Is4 reports whether ip is an IPv4 address. +// +// It returns false for IPv4-mapped IPv6 addresses. See Addr.Unmap. +func (ip Addr) Is4() bool { + return ip.z == z4 +} + +// Is4In6 reports whether ip is an IPv4-mapped IPv6 address. +func (ip Addr) Is4In6() bool { + return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff +} + +// Is6 reports whether ip is an IPv6 address, including IPv4-mapped +// IPv6 addresses. +func (ip Addr) Is6() bool { + return ip.z != z0 && ip.z != z4 +} + +// Unmap returns ip with any IPv4-mapped IPv6 address prefix removed. +// +// That is, if ip is an IPv6 address wrapping an IPv4 address, it +// returns the wrapped IPv4 address. Otherwise it returns ip unmodified. +func (ip Addr) Unmap() Addr { + if ip.Is4In6() { + ip.z = z4 + } + return ip +} + +// WithZone returns an IP that's the same as ip but with the provided +// zone. If zone is empty, the zone is removed. If ip is an IPv4 +// address, WithZone is a no-op and returns ip unchanged. +func (ip Addr) WithZone(zone string) Addr { + if !ip.Is6() { + return ip + } + if zone == "" { + ip.z = z6noz + return ip + } + ip.z = intern.GetByString(zone) + return ip +} + +// withoutZone unconditionally strips the zone from ip. +// It's similar to WithZone, but small enough to be inlinable. +func (ip Addr) withoutZone() Addr { + if !ip.Is6() { + return ip + } + ip.z = z6noz + return ip +} + +// hasZone reports whether ip has an IPv6 zone. +func (ip Addr) hasZone() bool { + return ip.z != z0 && ip.z != z4 && ip.z != z6noz +} + +// IsLinkLocalUnicast reports whether ip is a link-local unicast address. +func (ip Addr) IsLinkLocalUnicast() bool { + // Dynamic Configuration of IPv4 Link-Local Addresses + // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1 + if ip.Is4() { + return ip.v4(0) == 169 && ip.v4(1) == 254 + } + // IP Version 6 Addressing Architecture (2.4 Address Type Identification) + // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 + if ip.Is6() { + return ip.v6u16(0)&0xffc0 == 0xfe80 + } + return false // zero value +} + +// IsLoopback reports whether ip is a loopback address. +func (ip Addr) IsLoopback() bool { + // Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing) + // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3 + if ip.Is4() { + return ip.v4(0) == 127 + } + // IP Version 6 Addressing Architecture (2.4 Address Type Identification) + // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 + if ip.Is6() { + return ip.addr.hi == 0 && ip.addr.lo == 1 + } + return false // zero value +} + +// IsMulticast reports whether ip is a multicast address. +func (ip Addr) IsMulticast() bool { + // Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES) + // https://datatracker.ietf.org/doc/html/rfc1112#section-4 + if ip.Is4() { + return ip.v4(0)&0xf0 == 0xe0 + } + // IP Version 6 Addressing Architecture (2.4 Address Type Identification) + // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 + if ip.Is6() { + return ip.addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff + } + return false // zero value +} + +// IsInterfaceLocalMulticast reports whether ip is an IPv6 interface-local +// multicast address. +func (ip Addr) IsInterfaceLocalMulticast() bool { + // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) + // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 + if ip.Is6() { + return ip.v6u16(0)&0xff0f == 0xff01 + } + return false // zero value +} + +// IsLinkLocalMulticast reports whether ip is a link-local multicast address. +func (ip Addr) IsLinkLocalMulticast() bool { + // IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24)) + // https://datatracker.ietf.org/doc/html/rfc5771#section-4 + if ip.Is4() { + return ip.v4(0) == 224 && ip.v4(1) == 0 && ip.v4(2) == 0 + } + // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) + // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 + if ip.Is6() { + return ip.v6u16(0)&0xff0f == 0xff02 + } + return false // zero value +} + +// IsGlobalUnicast reports whether ip is a global unicast address. +// +// It returns true for IPv6 addresses which fall outside of the current +// IANA-allocated 2000::/3 global unicast space, with the exception of the +// link-local address space. It also returns true even if ip is in the IPv4 +// private address space or IPv6 unique local address space. +// It returns false for the zero Addr. +// +// For reference, see RFC 1122, RFC 4291, and RFC 4632. +func (ip Addr) IsGlobalUnicast() bool { + if ip.z == z0 { + // Invalid or zero-value. + return false + } + + // Match package net's IsGlobalUnicast logic. Notably private IPv4 addresses + // and ULA IPv6 addresses are still considered "global unicast". + if ip.Is4() && (ip == IPv4Unspecified() || ip == AddrFrom4([4]byte{255, 255, 255, 255})) { + return false + } + + return ip != IPv6Unspecified() && + !ip.IsLoopback() && + !ip.IsMulticast() && + !ip.IsLinkLocalUnicast() +} + +// IsPrivate reports whether ip is a private address, according to RFC 1918 +// (IPv4 addresses) and RFC 4193 (IPv6 addresses). That is, it reports whether +// ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the +// same as net.IP.IsPrivate. +func (ip Addr) IsPrivate() bool { + // Match the stdlib's IsPrivate logic. + if ip.Is4() { + // RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as + // private IPv4 address subnets. + return ip.v4(0) == 10 || + (ip.v4(0) == 172 && ip.v4(1)&0xf0 == 16) || + (ip.v4(0) == 192 && ip.v4(1) == 168) + } + + if ip.Is6() { + // RFC 4193 allocates fc00::/7 as the unique local unicast IPv6 address + // subnet. + return ip.v6(0)&0xfe == 0xfc + } + + return false // zero value +} + +// IsUnspecified reports whether ip is an unspecified address, either the IPv4 +// address "0.0.0.0" or the IPv6 address "::". +// +// Note that the zero Addr is not an unspecified address. +func (ip Addr) IsUnspecified() bool { + return ip == IPv4Unspecified() || ip == IPv6Unspecified() +} + +// Prefix keeps only the top b bits of IP, producing a Prefix +// of the specified length. +// If ip is a zero Addr, Prefix always returns a zero Prefix and a nil error. +// Otherwise, if bits is less than zero or greater than ip.BitLen(), +// Prefix returns an error. +func (ip Addr) Prefix(b int) (Prefix, error) { + if b < 0 { + return Prefix{}, errors.New("negative Prefix bits") + } + effectiveBits := b + switch ip.z { + case z0: + return Prefix{}, nil + case z4: + if b > 32 { + return Prefix{}, errors.New("prefix length " + itoa.Itoa(b) + " too large for IPv4") + } + effectiveBits += 96 + default: + if b > 128 { + return Prefix{}, errors.New("prefix length " + itoa.Itoa(b) + " too large for IPv6") + } + } + ip.addr = ip.addr.and(mask6(effectiveBits)) + return PrefixFrom(ip, b), nil +} + +const ( + netIPv4len = 4 + netIPv6len = 16 +) + +// As16 returns the IP address in its 16-byte representation. +// IPv4 addresses are returned as IPv4-mapped IPv6 addresses. +// IPv6 addresses with zones are returned without their zone (use the +// Zone method to get it). +// The ip zero value returns all zeroes. +func (ip Addr) As16() (a16 [16]byte) { + bePutUint64(a16[:8], ip.addr.hi) + bePutUint64(a16[8:], ip.addr.lo) + return a16 +} + +// As4 returns an IPv4 or IPv4-in-IPv6 address in its 4-byte representation. +// If ip is the zero Addr or an IPv6 address, As4 panics. +// Note that 0.0.0.0 is not the zero Addr. +func (ip Addr) As4() (a4 [4]byte) { + if ip.z == z4 || ip.Is4In6() { + bePutUint32(a4[:], uint32(ip.addr.lo)) + return a4 + } + if ip.z == z0 { + panic("As4 called on IP zero value") + } + panic("As4 called on IPv6 address") +} + +// AsSlice returns an IPv4 or IPv6 address in its respective 4-byte or 16-byte representation. +func (ip Addr) AsSlice() []byte { + switch ip.z { + case z0: + return nil + case z4: + var ret [4]byte + bePutUint32(ret[:], uint32(ip.addr.lo)) + return ret[:] + default: + var ret [16]byte + bePutUint64(ret[:8], ip.addr.hi) + bePutUint64(ret[8:], ip.addr.lo) + return ret[:] + } +} + +// Next returns the address following ip. +// If there is none, it returns the zero Addr. +func (ip Addr) Next() Addr { + ip.addr = ip.addr.addOne() + if ip.Is4() { + if uint32(ip.addr.lo) == 0 { + // Overflowed. + return Addr{} + } + } else { + if ip.addr.isZero() { + // Overflowed + return Addr{} + } + } + return ip +} + +// Prev returns the IP before ip. +// If there is none, it returns the IP zero value. +func (ip Addr) Prev() Addr { + if ip.Is4() { + if uint32(ip.addr.lo) == 0 { + return Addr{} + } + } else if ip.addr.isZero() { + return Addr{} + } + ip.addr = ip.addr.subOne() + return ip +} + +// String returns the string form of the IP address ip. +// It returns one of 5 forms: +// +// - "invalid IP", if ip is the zero Addr +// - IPv4 dotted decimal ("192.0.2.1") +// - IPv6 ("2001:db8::1") +// - "::ffff:1.2.3.4" (if Is4In6) +// - IPv6 with zone ("fe80:db8::1%eth0") +// +// Note that unlike package net's IP.String method, +// IPv4-mapped IPv6 addresses format with a "::ffff:" +// prefix before the dotted quad. +func (ip Addr) String() string { + switch ip.z { + case z0: + return "invalid IP" + case z4: + return ip.string4() + default: + if ip.Is4In6() { + if z := ip.Zone(); z != "" { + return "::ffff:" + ip.Unmap().string4() + "%" + z + } else { + return "::ffff:" + ip.Unmap().string4() + } + } + return ip.string6() + } +} + +// AppendTo appends a text encoding of ip, +// as generated by MarshalText, +// to b and returns the extended buffer. +func (ip Addr) AppendTo(b []byte) []byte { + switch ip.z { + case z0: + return b + case z4: + return ip.appendTo4(b) + default: + if ip.Is4In6() { + b = append(b, "::ffff:"...) + b = ip.Unmap().appendTo4(b) + if z := ip.Zone(); z != "" { + b = append(b, '%') + b = append(b, z...) + } + return b + } + return ip.appendTo6(b) + } +} + +// digits is a string of the hex digits from 0 to f. It's used in +// appendDecimal and appendHex to format IP addresses. +const digits = "0123456789abcdef" + +// appendDecimal appends the decimal string representation of x to b. +func appendDecimal(b []byte, x uint8) []byte { + // Using this function rather than strconv.AppendUint makes IPv4 + // string building 2x faster. + + if x >= 100 { + b = append(b, digits[x/100]) + } + if x >= 10 { + b = append(b, digits[x/10%10]) + } + return append(b, digits[x%10]) +} + +// appendHex appends the hex string representation of x to b. +func appendHex(b []byte, x uint16) []byte { + // Using this function rather than strconv.AppendUint makes IPv6 + // string building 2x faster. + + if x >= 0x1000 { + b = append(b, digits[x>>12]) + } + if x >= 0x100 { + b = append(b, digits[x>>8&0xf]) + } + if x >= 0x10 { + b = append(b, digits[x>>4&0xf]) + } + return append(b, digits[x&0xf]) +} + +// appendHexPad appends the fully padded hex string representation of x to b. +func appendHexPad(b []byte, x uint16) []byte { + return append(b, digits[x>>12], digits[x>>8&0xf], digits[x>>4&0xf], digits[x&0xf]) +} + +func (ip Addr) string4() string { + const max = len("255.255.255.255") + ret := make([]byte, 0, max) + ret = ip.appendTo4(ret) + return string(ret) +} + +func (ip Addr) appendTo4(ret []byte) []byte { + ret = appendDecimal(ret, ip.v4(0)) + ret = append(ret, '.') + ret = appendDecimal(ret, ip.v4(1)) + ret = append(ret, '.') + ret = appendDecimal(ret, ip.v4(2)) + ret = append(ret, '.') + ret = appendDecimal(ret, ip.v4(3)) + return ret +} + +// string6 formats ip in IPv6 textual representation. It follows the +// guidelines in section 4 of RFC 5952 +// (https://tools.ietf.org/html/rfc5952#section-4): no unnecessary +// zeros, use :: to elide the longest run of zeros, and don't use :: +// to compact a single zero field. +func (ip Addr) string6() string { + // Use a zone with a "plausibly long" name, so that most zone-ful + // IP addresses won't require additional allocation. + // + // The compiler does a cool optimization here, where ret ends up + // stack-allocated and so the only allocation this function does + // is to construct the returned string. As such, it's okay to be a + // bit greedy here, size-wise. + const max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") + ret := make([]byte, 0, max) + ret = ip.appendTo6(ret) + return string(ret) +} + +func (ip Addr) appendTo6(ret []byte) []byte { + zeroStart, zeroEnd := uint8(255), uint8(255) + for i := uint8(0); i < 8; i++ { + j := i + for j < 8 && ip.v6u16(j) == 0 { + j++ + } + if l := j - i; l >= 2 && l > zeroEnd-zeroStart { + zeroStart, zeroEnd = i, j + } + } + + for i := uint8(0); i < 8; i++ { + if i == zeroStart { + ret = append(ret, ':', ':') + i = zeroEnd + if i >= 8 { + break + } + } else if i > 0 { + ret = append(ret, ':') + } + + ret = appendHex(ret, ip.v6u16(i)) + } + + if ip.z != z6noz { + ret = append(ret, '%') + ret = append(ret, ip.Zone()...) + } + return ret +} + +// StringExpanded is like String but IPv6 addresses are expanded with leading +// zeroes and no "::" compression. For example, "2001:db8::1" becomes +// "2001:0db8:0000:0000:0000:0000:0000:0001". +func (ip Addr) StringExpanded() string { + switch ip.z { + case z0, z4: + return ip.String() + } + + const size = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") + ret := make([]byte, 0, size) + for i := uint8(0); i < 8; i++ { + if i > 0 { + ret = append(ret, ':') + } + + ret = appendHexPad(ret, ip.v6u16(i)) + } + + if ip.z != z6noz { + // The addition of a zone will cause a second allocation, but when there + // is no zone the ret slice will be stack allocated. + ret = append(ret, '%') + ret = append(ret, ip.Zone()...) + } + return string(ret) +} + +// MarshalText implements the encoding.TextMarshaler interface, +// The encoding is the same as returned by String, with one exception: +// If ip is the zero Addr, the encoding is the empty string. +func (ip Addr) MarshalText() ([]byte, error) { + switch ip.z { + case z0: + return []byte(""), nil + case z4: + max := len("255.255.255.255") + b := make([]byte, 0, max) + return ip.appendTo4(b), nil + default: + max := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") + b := make([]byte, 0, max) + if ip.Is4In6() { + b = append(b, "::ffff:"...) + b = ip.Unmap().appendTo4(b) + if z := ip.Zone(); z != "" { + b = append(b, '%') + b = append(b, z...) + } + return b, nil + } + return ip.appendTo6(b), nil + } + +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The IP address is expected in a form accepted by ParseAddr. +// +// If text is empty, UnmarshalText sets *ip to the zero Addr and +// returns no error. +func (ip *Addr) UnmarshalText(text []byte) error { + if len(text) == 0 { + *ip = Addr{} + return nil + } + var err error + *ip, err = ParseAddr(string(text)) + return err +} + +func (ip Addr) marshalBinaryWithTrailingBytes(trailingBytes int) []byte { + var b []byte + switch ip.z { + case z0: + b = make([]byte, trailingBytes) + case z4: + b = make([]byte, 4+trailingBytes) + bePutUint32(b, uint32(ip.addr.lo)) + default: + z := ip.Zone() + b = make([]byte, 16+len(z)+trailingBytes) + bePutUint64(b[:8], ip.addr.hi) + bePutUint64(b[8:], ip.addr.lo) + copy(b[16:], z) + } + return b +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +// It returns a zero-length slice for the zero Addr, +// the 4-byte form for an IPv4 address, +// and the 16-byte form with zone appended for an IPv6 address. +func (ip Addr) MarshalBinary() ([]byte, error) { + return ip.marshalBinaryWithTrailingBytes(0), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It expects data in the form generated by MarshalBinary. +func (ip *Addr) UnmarshalBinary(b []byte) error { + n := len(b) + switch { + case n == 0: + *ip = Addr{} + return nil + case n == 4: + *ip = AddrFrom4(*(*[4]byte)(b)) + return nil + case n == 16: + *ip = ipv6Slice(b) + return nil + case n > 16: + *ip = ipv6Slice(b[:16]).WithZone(string(b[16:])) + return nil + } + return errors.New("unexpected slice size") +} + +// AddrPort is an IP and a port number. +type AddrPort struct { + ip Addr + port uint16 +} + +// AddrPortFrom returns an AddrPort with the provided IP and port. +// It does not allocate. +func AddrPortFrom(ip Addr, port uint16) AddrPort { return AddrPort{ip: ip, port: port} } + +// Addr returns p's IP address. +func (p AddrPort) Addr() Addr { return p.ip } + +// Port returns p's port. +func (p AddrPort) Port() uint16 { return p.port } + +// splitAddrPort splits s into an IP address string and a port +// string. It splits strings shaped like "foo:bar" or "[foo]:bar", +// without further validating the substrings. v6 indicates whether the +// ip string should parse as an IPv6 address or an IPv4 address, in +// order for s to be a valid ip:port string. +func splitAddrPort(s string) (ip, port string, v6 bool, err error) { + i := stringsLastIndexByte(s, ':') + if i == -1 { + return "", "", false, errors.New("not an ip:port") + } + + ip, port = s[:i], s[i+1:] + if len(ip) == 0 { + return "", "", false, errors.New("no IP") + } + if len(port) == 0 { + return "", "", false, errors.New("no port") + } + if ip[0] == '[' { + if len(ip) < 2 || ip[len(ip)-1] != ']' { + return "", "", false, errors.New("missing ]") + } + ip = ip[1 : len(ip)-1] + v6 = true + } + + return ip, port, v6, nil +} + +// ParseAddrPort parses s as an AddrPort. +// +// It doesn't do any name resolution: both the address and the port +// must be numeric. +func ParseAddrPort(s string) (AddrPort, error) { + var ipp AddrPort + ip, port, v6, err := splitAddrPort(s) + if err != nil { + return ipp, err + } + port16, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return ipp, errors.New("invalid port " + strconv.Quote(port) + " parsing " + strconv.Quote(s)) + } + ipp.port = uint16(port16) + ipp.ip, err = ParseAddr(ip) + if err != nil { + return AddrPort{}, err + } + if v6 && ipp.ip.Is4() { + return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote(s) + ", square brackets can only be used with IPv6 addresses") + } else if !v6 && ipp.ip.Is6() { + return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote(s) + ", IPv6 addresses must be surrounded by square brackets") + } + return ipp, nil +} + +// MustParseAddrPort calls ParseAddrPort(s) and panics on error. +// It is intended for use in tests with hard-coded strings. +func MustParseAddrPort(s string) AddrPort { + ip, err := ParseAddrPort(s) + if err != nil { + panic(err) + } + return ip +} + +// isZero reports whether p is the zero AddrPort. +func (p AddrPort) isZero() bool { return p == AddrPort{} } + +// IsValid reports whether p.Addr() is valid. +// All ports are valid, including zero. +func (p AddrPort) IsValid() bool { return p.ip.IsValid() } + +func (p AddrPort) String() string { + switch p.ip.z { + case z0: + return "invalid AddrPort" + case z4: + a := p.ip.As4() + buf := make([]byte, 0, 21) + for i := range a { + buf = strconv.AppendUint(buf, uint64(a[i]), 10) + buf = append(buf, "...:"[i]) + } + buf = strconv.AppendUint(buf, uint64(p.port), 10) + return string(buf) + default: + // TODO: this could be more efficient allocation-wise: + return joinHostPort(p.ip.String(), itoa.Itoa(int(p.port))) + } +} + +func joinHostPort(host, port string) string { + // We assume that host is a literal IPv6 address if host has + // colons. + if bytealg.IndexByteString(host, ':') >= 0 { + return "[" + host + "]:" + port + } + return host + ":" + port +} + +// AppendTo appends a text encoding of p, +// as generated by MarshalText, +// to b and returns the extended buffer. +func (p AddrPort) AppendTo(b []byte) []byte { + switch p.ip.z { + case z0: + return b + case z4: + b = p.ip.appendTo4(b) + default: + if p.ip.Is4In6() { + b = append(b, "[::ffff:"...) + b = p.ip.Unmap().appendTo4(b) + if z := p.ip.Zone(); z != "" { + b = append(b, '%') + b = append(b, z...) + } + } else { + b = append(b, '[') + b = p.ip.appendTo6(b) + } + b = append(b, ']') + } + b = append(b, ':') + b = strconv.AppendUint(b, uint64(p.port), 10) + return b +} + +// MarshalText implements the encoding.TextMarshaler interface. The +// encoding is the same as returned by String, with one exception: if +// p.Addr() is the zero Addr, the encoding is the empty string. +func (p AddrPort) MarshalText() ([]byte, error) { + var max int + switch p.ip.z { + case z0: + case z4: + max = len("255.255.255.255:65535") + default: + max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") + } + b := make([]byte, 0, max) + b = p.AppendTo(b) + return b, nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler +// interface. The AddrPort is expected in a form +// generated by MarshalText or accepted by ParseAddrPort. +func (p *AddrPort) UnmarshalText(text []byte) error { + if len(text) == 0 { + *p = AddrPort{} + return nil + } + var err error + *p, err = ParseAddrPort(string(text)) + return err +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +// It returns Addr.MarshalBinary with an additional two bytes appended +// containing the port in little-endian. +func (p AddrPort) MarshalBinary() ([]byte, error) { + b := p.Addr().marshalBinaryWithTrailingBytes(2) + lePutUint16(b[len(b)-2:], p.Port()) + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It expects data in the form generated by MarshalBinary. +func (p *AddrPort) UnmarshalBinary(b []byte) error { + if len(b) < 2 { + return errors.New("unexpected slice size") + } + var addr Addr + err := addr.UnmarshalBinary(b[:len(b)-2]) + if err != nil { + return err + } + *p = AddrPortFrom(addr, leUint16(b[len(b)-2:])) + return nil +} + +// Prefix is an IP address prefix (CIDR) representing an IP network. +// +// The first Bits() of Addr() are specified. The remaining bits match any address. +// The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6. +type Prefix struct { + ip Addr + + // bits is logically a uint8 (storing [0,128]) but also + // encodes an "invalid" bit, currently represented by the + // invalidPrefixBits sentinel value. It could be packed into + // the uint8 more with more complicated expressions in the + // accessors, but the extra byte (in padding anyway) doesn't + // hurt and simplifies code below. + bits int16 +} + +// invalidPrefixBits is the Prefix.bits value used when PrefixFrom is +// outside the range of a uint8. It's returned as the int -1 in the +// public API. +const invalidPrefixBits = -1 + +// PrefixFrom returns a Prefix with the provided IP address and bit +// prefix length. +// +// It does not allocate. Unlike Addr.Prefix, PrefixFrom does not mask +// off the host bits of ip. +// +// If bits is less than zero or greater than ip.BitLen, Prefix.Bits +// will return an invalid value -1. +func PrefixFrom(ip Addr, bits int) Prefix { + if bits < 0 || bits > ip.BitLen() { + bits = invalidPrefixBits + } + b16 := int16(bits) + return Prefix{ + ip: ip.withoutZone(), + bits: b16, + } +} + +// Addr returns p's IP address. +func (p Prefix) Addr() Addr { return p.ip } + +// Bits returns p's prefix length. +// +// It reports -1 if invalid. +func (p Prefix) Bits() int { return int(p.bits) } + +// IsValid reports whether p.Bits() has a valid range for p.Addr(). +// If p.Addr() is the zero Addr, IsValid returns false. +// Note that if p is the zero Prefix, then p.IsValid() == false. +func (p Prefix) IsValid() bool { return !p.ip.isZero() && p.bits >= 0 && int(p.bits) <= p.ip.BitLen() } + +func (p Prefix) isZero() bool { return p == Prefix{} } + +// IsSingleIP reports whether p contains exactly one IP. +func (p Prefix) IsSingleIP() bool { return p.bits != 0 && int(p.bits) == p.ip.BitLen() } + +// ParsePrefix parses s as an IP address prefix. +// The string can be in the form "192.168.1.0/24" or "2001:db8::/32", +// the CIDR notation defined in RFC 4632 and RFC 4291. +// IPv6 zones are not permitted in prefixes, and an error will be returned if a +// zone is present. +// +// Note that masked address bits are not zeroed. Use Masked for that. +func ParsePrefix(s string) (Prefix, error) { + i := stringsLastIndexByte(s, '/') + if i < 0 { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): no '/'") + } + ip, err := ParseAddr(s[:i]) + if err != nil { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): " + err.Error()) + } + // IPv6 zones are not allowed: https://go.dev/issue/51899 + if ip.Is6() && ip.z != z6noz { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): IPv6 zones cannot be present in a prefix") + } + + bitsStr := s[i+1:] + bits, err := strconv.Atoi(bitsStr) + if err != nil { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): bad bits after slash: " + strconv.Quote(bitsStr)) + } + maxBits := 32 + if ip.Is6() { + maxBits = 128 + } + if bits < 0 || bits > maxBits { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): prefix length out of range") + } + return PrefixFrom(ip, bits), nil +} + +// MustParsePrefix calls ParsePrefix(s) and panics on error. +// It is intended for use in tests with hard-coded strings. +func MustParsePrefix(s string) Prefix { + ip, err := ParsePrefix(s) + if err != nil { + panic(err) + } + return ip +} + +// Masked returns p in its canonical form, with all but the high +// p.Bits() bits of p.Addr() masked off. +// +// If p is zero or otherwise invalid, Masked returns the zero Prefix. +func (p Prefix) Masked() Prefix { + if m, err := p.ip.Prefix(int(p.bits)); err == nil { + return m + } + return Prefix{} +} + +// Contains reports whether the network p includes ip. +// +// An IPv4 address will not match an IPv6 prefix. +// An IPv4-mapped IPv6 address will not match an IPv4 prefix. +// A zero-value IP will not match any prefix. +// If ip has an IPv6 zone, Contains returns false, +// because Prefixes strip zones. +func (p Prefix) Contains(ip Addr) bool { + if !p.IsValid() || ip.hasZone() { + return false + } + if f1, f2 := p.ip.BitLen(), ip.BitLen(); f1 == 0 || f2 == 0 || f1 != f2 { + return false + } + if ip.Is4() { + // xor the IP addresses together; mismatched bits are now ones. + // Shift away the number of bits we don't care about. + // Shifts in Go are more efficient if the compiler can prove + // that the shift amount is smaller than the width of the shifted type (64 here). + // We know that p.bits is in the range 0..32 because p is Valid; + // the compiler doesn't know that, so mask with 63 to help it. + // Now truncate to 32 bits, because this is IPv4. + // If all the bits we care about are equal, the result will be zero. + return uint32((ip.addr.lo^p.ip.addr.lo)>>((32-p.bits)&63)) == 0 + } else { + // xor the IP addresses together. + // Mask away the bits we don't care about. + // If all the bits we care about are equal, the result will be zero. + return ip.addr.xor(p.ip.addr).and(mask6(int(p.bits))).isZero() + } +} + +// Overlaps reports whether p and o contain any IP addresses in common. +// +// If p and o are of different address families or either have a zero +// IP, it reports false. Like the Contains method, a prefix with an +// IPv4-mapped IPv6 address is still treated as an IPv6 mask. +func (p Prefix) Overlaps(o Prefix) bool { + if !p.IsValid() || !o.IsValid() { + return false + } + if p == o { + return true + } + if p.ip.Is4() != o.ip.Is4() { + return false + } + var minBits int16 + if p.bits < o.bits { + minBits = p.bits + } else { + minBits = o.bits + } + if minBits == 0 { + return true + } + // One of these Prefix calls might look redundant, but we don't require + // that p and o values are normalized (via Prefix.Masked) first, + // so the Prefix call on the one that's already minBits serves to zero + // out any remaining bits in IP. + var err error + if p, err = p.ip.Prefix(int(minBits)); err != nil { + return false + } + if o, err = o.ip.Prefix(int(minBits)); err != nil { + return false + } + return p.ip == o.ip +} + +// AppendTo appends a text encoding of p, +// as generated by MarshalText, +// to b and returns the extended buffer. +func (p Prefix) AppendTo(b []byte) []byte { + if p.isZero() { + return b + } + if !p.IsValid() { + return append(b, "invalid Prefix"...) + } + + // p.ip is non-nil, because p is valid. + if p.ip.z == z4 { + b = p.ip.appendTo4(b) + } else { + if p.ip.Is4In6() { + b = append(b, "::ffff:"...) + b = p.ip.Unmap().appendTo4(b) + } else { + b = p.ip.appendTo6(b) + } + } + + b = append(b, '/') + b = appendDecimal(b, uint8(p.bits)) + return b +} + +// MarshalText implements the encoding.TextMarshaler interface, +// The encoding is the same as returned by String, with one exception: +// If p is the zero value, the encoding is the empty string. +func (p Prefix) MarshalText() ([]byte, error) { + var max int + switch p.ip.z { + case z0: + case z4: + max = len("255.255.255.255/32") + default: + max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128") + } + b := make([]byte, 0, max) + b = p.AppendTo(b) + return b, nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The IP address is expected in a form accepted by ParsePrefix +// or generated by MarshalText. +func (p *Prefix) UnmarshalText(text []byte) error { + if len(text) == 0 { + *p = Prefix{} + return nil + } + var err error + *p, err = ParsePrefix(string(text)) + return err +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +// It returns Addr.MarshalBinary with an additional byte appended +// containing the prefix bits. +func (p Prefix) MarshalBinary() ([]byte, error) { + b := p.Addr().withoutZone().marshalBinaryWithTrailingBytes(1) + b[len(b)-1] = uint8(p.Bits()) + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It expects data in the form generated by MarshalBinary. +func (p *Prefix) UnmarshalBinary(b []byte) error { + if len(b) < 1 { + return errors.New("unexpected slice size") + } + var addr Addr + err := addr.UnmarshalBinary(b[:len(b)-1]) + if err != nil { + return err + } + *p = PrefixFrom(addr, int(b[len(b)-1])) + return nil +} + +// String returns the CIDR notation of p: "/". +func (p Prefix) String() string { + if !p.IsValid() { + return "invalid Prefix" + } + return p.ip.String() + "/" + itoa.Itoa(int(p.bits)) +} diff --git a/src/net/netip/netip_pkg_test.go b/src/net/netip/netip_pkg_test.go new file mode 100644 index 00000000000000..677f523e6d9667 --- /dev/null +++ b/src/net/netip/netip_pkg_test.go @@ -0,0 +1,359 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip + +import ( + "bytes" + "encoding" + "encoding/json" + "strings" + "testing" +) + +var ( + mustPrefix = MustParsePrefix + mustIP = MustParseAddr +) + +func TestPrefixValid(t *testing.T) { + v4 := MustParseAddr("1.2.3.4") + v6 := MustParseAddr("::1") + tests := []struct { + ipp Prefix + want bool + }{ + {Prefix{v4, -2}, false}, + {Prefix{v4, -1}, false}, + {Prefix{v4, 0}, true}, + {Prefix{v4, 32}, true}, + {Prefix{v4, 33}, false}, + + {Prefix{v6, -2}, false}, + {Prefix{v6, -1}, false}, + {Prefix{v6, 0}, true}, + {Prefix{v6, 32}, true}, + {Prefix{v6, 128}, true}, + {Prefix{v6, 129}, false}, + + {Prefix{Addr{}, -2}, false}, + {Prefix{Addr{}, -1}, false}, + {Prefix{Addr{}, 0}, false}, + {Prefix{Addr{}, 32}, false}, + {Prefix{Addr{}, 128}, false}, + } + for _, tt := range tests { + got := tt.ipp.IsValid() + if got != tt.want { + t.Errorf("(%v).IsValid() = %v want %v", tt.ipp, got, tt.want) + } + } +} + +var nextPrevTests = []struct { + ip Addr + next Addr + prev Addr +}{ + {mustIP("10.0.0.1"), mustIP("10.0.0.2"), mustIP("10.0.0.0")}, + {mustIP("10.0.0.255"), mustIP("10.0.1.0"), mustIP("10.0.0.254")}, + {mustIP("127.0.0.1"), mustIP("127.0.0.2"), mustIP("127.0.0.0")}, + {mustIP("254.255.255.255"), mustIP("255.0.0.0"), mustIP("254.255.255.254")}, + {mustIP("255.255.255.255"), Addr{}, mustIP("255.255.255.254")}, + {mustIP("0.0.0.0"), mustIP("0.0.0.1"), Addr{}}, + {mustIP("::"), mustIP("::1"), Addr{}}, + {mustIP("::%x"), mustIP("::1%x"), Addr{}}, + {mustIP("::1"), mustIP("::2"), mustIP("::")}, + {mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), Addr{}, mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")}, +} + +func TestIPNextPrev(t *testing.T) { + doNextPrev(t) + + for _, ip := range []Addr{ + mustIP("0.0.0.0"), + mustIP("::"), + } { + got := ip.Prev() + if !got.isZero() { + t.Errorf("IP(%v).Prev = %v; want zero", ip, got) + } + } + + var allFF [16]byte + for i := range allFF { + allFF[i] = 0xff + } + + for _, ip := range []Addr{ + mustIP("255.255.255.255"), + AddrFrom16(allFF), + } { + got := ip.Next() + if !got.isZero() { + t.Errorf("IP(%v).Next = %v; want zero", ip, got) + } + } +} + +func BenchmarkIPNextPrev(b *testing.B) { + for i := 0; i < b.N; i++ { + doNextPrev(b) + } +} + +func doNextPrev(t testing.TB) { + for _, tt := range nextPrevTests { + gnext, gprev := tt.ip.Next(), tt.ip.Prev() + if gnext != tt.next { + t.Errorf("IP(%v).Next = %v; want %v", tt.ip, gnext, tt.next) + } + if gprev != tt.prev { + t.Errorf("IP(%v).Prev = %v; want %v", tt.ip, gprev, tt.prev) + } + if !tt.ip.Next().isZero() && tt.ip.Next().Prev() != tt.ip { + t.Errorf("IP(%v).Next.Prev = %v; want %v", tt.ip, tt.ip.Next().Prev(), tt.ip) + } + if !tt.ip.Prev().isZero() && tt.ip.Prev().Next() != tt.ip { + t.Errorf("IP(%v).Prev.Next = %v; want %v", tt.ip, tt.ip.Prev().Next(), tt.ip) + } + } +} + +func TestIPBitLen(t *testing.T) { + tests := []struct { + ip Addr + want int + }{ + {Addr{}, 0}, + {mustIP("0.0.0.0"), 32}, + {mustIP("10.0.0.1"), 32}, + {mustIP("::"), 128}, + {mustIP("fed0::1"), 128}, + {mustIP("::ffff:10.0.0.1"), 128}, + } + for _, tt := range tests { + got := tt.ip.BitLen() + if got != tt.want { + t.Errorf("BitLen(%v) = %d; want %d", tt.ip, got, tt.want) + } + } +} + +func TestPrefixContains(t *testing.T) { + tests := []struct { + ipp Prefix + ip Addr + want bool + }{ + {mustPrefix("9.8.7.6/0"), mustIP("9.8.7.6"), true}, + {mustPrefix("9.8.7.6/16"), mustIP("9.8.7.6"), true}, + {mustPrefix("9.8.7.6/16"), mustIP("9.8.6.4"), true}, + {mustPrefix("9.8.7.6/16"), mustIP("9.9.7.6"), false}, + {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.6"), true}, + {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, + {mustPrefix("9.8.7.6/32"), mustIP("9.8.7.7"), false}, + {mustPrefix("::1/0"), mustIP("::1"), true}, + {mustPrefix("::1/0"), mustIP("::2"), true}, + {mustPrefix("::1/127"), mustIP("::1"), true}, + {mustPrefix("::1/127"), mustIP("::2"), false}, + {mustPrefix("::1/128"), mustIP("::1"), true}, + {mustPrefix("::1/127"), mustIP("::2"), false}, + // Zones ignored: https://go.dev/issue/51899 + {Prefix{mustIP("1.2.3.4").WithZone("a"), 32}, mustIP("1.2.3.4"), true}, + {Prefix{mustIP("::1").WithZone("a"), 128}, mustIP("::1"), true}, + // invalid IP + {mustPrefix("::1/0"), Addr{}, false}, + {mustPrefix("1.2.3.4/0"), Addr{}, false}, + // invalid Prefix + {Prefix{mustIP("::1"), 129}, mustIP("::1"), false}, + {Prefix{mustIP("1.2.3.4"), 33}, mustIP("1.2.3.4"), false}, + {Prefix{Addr{}, 0}, mustIP("1.2.3.4"), false}, + {Prefix{Addr{}, 32}, mustIP("1.2.3.4"), false}, + {Prefix{Addr{}, 128}, mustIP("::1"), false}, + // wrong IP family + {mustPrefix("::1/0"), mustIP("1.2.3.4"), false}, + {mustPrefix("1.2.3.4/0"), mustIP("::1"), false}, + } + for _, tt := range tests { + got := tt.ipp.Contains(tt.ip) + if got != tt.want { + t.Errorf("(%v).Contains(%v) = %v want %v", tt.ipp, tt.ip, got, tt.want) + } + } +} + +func TestParseIPError(t *testing.T) { + tests := []struct { + ip string + errstr string + }{ + { + ip: "localhost", + }, + { + ip: "500.0.0.1", + errstr: "field has value >255", + }, + { + ip: "::gggg%eth0", + errstr: "must have at least one digit", + }, + { + ip: "fe80::1cc0:3e8c:119f:c2e1%", + errstr: "zone must be a non-empty string", + }, + { + ip: "%eth0", + errstr: "missing IPv6 address", + }, + } + for _, test := range tests { + t.Run(test.ip, func(t *testing.T) { + _, err := ParseAddr(test.ip) + if err == nil { + t.Fatal("no error") + } + if _, ok := err.(parseAddrError); !ok { + t.Errorf("error type is %T, want parseIPError", err) + } + if test.errstr == "" { + test.errstr = "unable to parse IP" + } + if got := err.Error(); !strings.Contains(got, test.errstr) { + t.Errorf("error is missing substring %q: %s", test.errstr, got) + } + }) + } +} + +func TestParseAddrPort(t *testing.T) { + tests := []struct { + in string + want AddrPort + wantErr bool + }{ + {in: "1.2.3.4:1234", want: AddrPort{mustIP("1.2.3.4"), 1234}}, + {in: "1.1.1.1:123456", wantErr: true}, + {in: "1.1.1.1:-123", wantErr: true}, + {in: "[::1]:1234", want: AddrPort{mustIP("::1"), 1234}}, + {in: "[1.2.3.4]:1234", wantErr: true}, + {in: "fe80::1:1234", wantErr: true}, + {in: ":0", wantErr: true}, // if we need to parse this form, there should be a separate function that explicitly allows it + } + for _, test := range tests { + t.Run(test.in, func(t *testing.T) { + got, err := ParseAddrPort(test.in) + if err != nil { + if test.wantErr { + return + } + t.Fatal(err) + } + if got != test.want { + t.Errorf("got %v; want %v", got, test.want) + } + if got.String() != test.in { + t.Errorf("String = %q; want %q", got.String(), test.in) + } + }) + + t.Run(test.in+"/AppendTo", func(t *testing.T) { + got, err := ParseAddrPort(test.in) + if err == nil { + testAppendToMarshal(t, got) + } + }) + + // TextMarshal and TextUnmarshal mostly behave like + // ParseAddrPort and String. Divergent behavior are handled in + // TestAddrPortMarshalUnmarshal. + t.Run(test.in+"/Marshal", func(t *testing.T) { + var got AddrPort + jsin := `"` + test.in + `"` + err := json.Unmarshal([]byte(jsin), &got) + if err != nil { + if test.wantErr { + return + } + t.Fatal(err) + } + if got != test.want { + t.Errorf("got %v; want %v", got, test.want) + } + gotb, err := json.Marshal(got) + if err != nil { + t.Fatal(err) + } + if string(gotb) != jsin { + t.Errorf("Marshal = %q; want %q", string(gotb), jsin) + } + }) + } +} + +func TestAddrPortMarshalUnmarshal(t *testing.T) { + tests := []struct { + in string + want AddrPort + }{ + {"", AddrPort{}}, + } + + for _, test := range tests { + t.Run(test.in, func(t *testing.T) { + orig := `"` + test.in + `"` + + var ipp AddrPort + if err := json.Unmarshal([]byte(orig), &ipp); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + + ippb, err := json.Marshal(ipp) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + back := string(ippb) + if orig != back { + t.Errorf("Marshal = %q; want %q", back, orig) + } + + testAppendToMarshal(t, ipp) + }) + } +} + +type appendMarshaler interface { + encoding.TextMarshaler + AppendTo([]byte) []byte +} + +// testAppendToMarshal tests that x's AppendTo and MarshalText methods yield the same results. +// x's MarshalText method must not return an error. +func testAppendToMarshal(t *testing.T, x appendMarshaler) { + t.Helper() + m, err := x.MarshalText() + if err != nil { + t.Fatalf("(%v).MarshalText: %v", x, err) + } + a := make([]byte, 0, len(m)) + a = x.AppendTo(a) + if !bytes.Equal(m, a) { + t.Errorf("(%v).MarshalText = %q, (%v).AppendTo = %q", x, m, x, a) + } +} + +func TestIPv6Accessor(t *testing.T) { + var a [16]byte + for i := range a { + a[i] = uint8(i) + 1 + } + ip := AddrFrom16(a) + for i := range a { + if got, want := ip.v6(uint8(i)), uint8(i)+1; got != want { + t.Errorf("v6(%v) = %v; want %v", i, got, want) + } + } +} diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go new file mode 100644 index 00000000000000..b0915bd8e9a8b0 --- /dev/null +++ b/src/net/netip/netip_test.go @@ -0,0 +1,1986 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip_test + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "internal/intern" + "internal/testenv" + "net" + . "net/netip" + "reflect" + "sort" + "strings" + "testing" +) + +var long = flag.Bool("long", false, "run long tests") + +type uint128 = Uint128 + +var ( + mustPrefix = MustParsePrefix + mustIP = MustParseAddr + mustIPPort = MustParseAddrPort +) + +func TestParseAddr(t *testing.T) { + var validIPs = []struct { + in string + ip Addr // output of ParseAddr() + str string // output of String(). If "", use in. + wantErr string + }{ + // Basic zero IPv4 address. + { + in: "0.0.0.0", + ip: MkAddr(Mk128(0, 0xffff00000000), Z4), + }, + // Basic non-zero IPv4 address. + { + in: "192.168.140.255", + ip: MkAddr(Mk128(0, 0xffffc0a88cff), Z4), + }, + // IPv4 address in windows-style "print all the digits" form. + { + in: "010.000.015.001", + wantErr: `ParseAddr("010.000.015.001"): IPv4 field has octet with leading zero`, + }, + // IPv4 address with a silly amount of leading zeros. + { + in: "000001.00000002.00000003.000000004", + wantErr: `ParseAddr("000001.00000002.00000003.000000004"): IPv4 field has octet with leading zero`, + }, + // 4-in-6 with octet with leading zero + { + in: "::ffff:1.2.03.4", + wantErr: `ParseAddr("::ffff:1.2.03.4"): ParseAddr("1.2.03.4"): IPv4 field has octet with leading zero (at "1.2.03.4")`, + }, + // Basic zero IPv6 address. + { + in: "::", + ip: MkAddr(Mk128(0, 0), Z6noz), + }, + // Localhost IPv6. + { + in: "::1", + ip: MkAddr(Mk128(0, 1), Z6noz), + }, + // Fully expanded IPv6 address. + { + in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", + ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), Z6noz), + }, + // IPv6 with elided fields in the middle. + { + in: "fd7a:115c::626b:430b", + ip: MkAddr(Mk128(0xfd7a115c00000000, 0x00000000626b430b), Z6noz), + }, + // IPv6 with elided fields at the end. + { + in: "fd7a:115c:a1e0:ab12:4843:cd96::", + ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd9600000000), Z6noz), + }, + // IPv6 with single elided field at the end. + { + in: "fd7a:115c:a1e0:ab12:4843:cd96:626b::", + ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b0000), Z6noz), + str: "fd7a:115c:a1e0:ab12:4843:cd96:626b:0", + }, + // IPv6 with single elided field in the middle. + { + in: "fd7a:115c:a1e0::4843:cd96:626b:430b", + ip: MkAddr(Mk128(0xfd7a115ca1e00000, 0x4843cd96626b430b), Z6noz), + str: "fd7a:115c:a1e0:0:4843:cd96:626b:430b", + }, + // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6) + { + in: "::ffff:192.168.140.255", + ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), Z6noz), + str: "::ffff:192.168.140.255", + }, + // IPv6 with a zone specifier. + { + in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", + ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), intern.Get("eth0")), + }, + // IPv6 with dotted decimal and zone specifier. + { + in: "1:2::ffff:192.168.140.255%eth1", + ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), intern.Get("eth1")), + str: "1:2::ffff:c0a8:8cff%eth1", + }, + // 4-in-6 with zone + { + in: "::ffff:192.168.140.255%eth1", + ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), intern.Get("eth1")), + str: "::ffff:192.168.140.255%eth1", + }, + // IPv6 with capital letters. + { + in: "FD9E:1A04:F01D::1", + ip: MkAddr(Mk128(0xfd9e1a04f01d0000, 0x1), Z6noz), + str: "fd9e:1a04:f01d::1", + }, + } + + for _, test := range validIPs { + t.Run(test.in, func(t *testing.T) { + got, err := ParseAddr(test.in) + if err != nil { + if err.Error() == test.wantErr { + return + } + t.Fatal(err) + } + if test.wantErr != "" { + t.Fatalf("wanted error %q; got none", test.wantErr) + } + if got != test.ip { + t.Errorf("got %#v, want %#v", got, test.ip) + } + + // Check that ParseAddr is a pure function. + got2, err := ParseAddr(test.in) + if err != nil { + t.Fatal(err) + } + if got != got2 { + t.Errorf("ParseAddr(%q) got 2 different results: %#v, %#v", test.in, got, got2) + } + + // Check that ParseAddr(ip.String()) is the identity function. + s := got.String() + got3, err := ParseAddr(s) + if err != nil { + t.Fatal(err) + } + if got != got3 { + t.Errorf("ParseAddr(%q) != ParseAddr(ParseIP(%q).String()). Got %#v, want %#v", test.in, test.in, got3, got) + } + + // Check that the slow-but-readable parser produces the same result. + slow, err := parseIPSlow(test.in) + if err != nil { + t.Fatal(err) + } + if got != slow { + t.Errorf("ParseAddr(%q) = %#v, parseIPSlow(%q) = %#v", test.in, got, test.in, slow) + } + + // Check that the parsed IP formats as expected. + s = got.String() + wants := test.str + if wants == "" { + wants = test.in + } + if s != wants { + t.Errorf("ParseAddr(%q).String() got %q, want %q", test.in, s, wants) + } + + // Check that AppendTo matches MarshalText. + TestAppendToMarshal(t, got) + + // Check that MarshalText/UnmarshalText work similarly to + // ParseAddr/String (see TestIPMarshalUnmarshal for + // marshal-specific behavior that's not common with + // ParseAddr/String). + js := `"` + test.in + `"` + var jsgot Addr + if err := json.Unmarshal([]byte(js), &jsgot); err != nil { + t.Fatal(err) + } + if jsgot != got { + t.Errorf("json.Unmarshal(%q) = %#v, want %#v", test.in, jsgot, got) + } + jsb, err := json.Marshal(jsgot) + if err != nil { + t.Fatal(err) + } + jswant := `"` + wants + `"` + jsback := string(jsb) + if jsback != jswant { + t.Errorf("Marshal(Unmarshal(%q)) = %s, want %s", test.in, jsback, jswant) + } + }) + } + + var invalidIPs = []string{ + // Empty string + "", + // Garbage non-IP + "bad", + // Single number. Some parsers accept this as an IPv4 address in + // big-endian uint32 form, but we don't. + "1234", + // IPv4 with a zone specifier + "1.2.3.4%eth0", + // IPv4 field must have at least one digit + ".1.2.3", + "1.2.3.", + "1..2.3", + // IPv4 address too long + "1.2.3.4.5", + // IPv4 in dotted octal form + "0300.0250.0214.0377", + // IPv4 in dotted hex form + "0xc0.0xa8.0x8c.0xff", + // IPv4 in class B form + "192.168.12345", + // IPv4 in class B form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.0.1", + // IPv4 in class A form + "192.1234567", + // IPv4 in class A form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.1", + // IPv4 field has value >255 + "192.168.300.1", + // IPv4 with too many fields + "192.168.0.1.5.6", + // IPv6 with not enough fields + "1:2:3:4:5:6:7", + // IPv6 with too many fields + "1:2:3:4:5:6:7:8:9", + // IPv6 with 8 fields and a :: expander + "1:2:3:4::5:6:7:8", + // IPv6 with a field bigger than 2b + "fe801::1", + // IPv6 with non-hex values in field + "fe80:tail:scal:e::", + // IPv6 with a zone delimiter but no zone. + "fe80::1%", + // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4. + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4. + "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 with invalid embedded IPv4. + "::ffff:192.168.140.bad", + // IPv6 with multiple ellipsis ::. + "fe80::1::1", + // IPv6 with invalid non hex/colon character. + "fe80:1?:1", + // IPv6 with truncated bytes after single colon. + "fe80:", + } + + for _, s := range invalidIPs { + t.Run(s, func(t *testing.T) { + got, err := ParseAddr(s) + if err == nil { + t.Errorf("ParseAddr(%q) = %#v, want error", s, got) + } + + slow, err := parseIPSlow(s) + if err == nil { + t.Errorf("parseIPSlow(%q) = %#v, want error", s, slow) + } + + std := net.ParseIP(s) + if std != nil { + t.Errorf("net.ParseIP(%q) = %#v, want error", s, std) + } + + if s == "" { + // Don't test unmarshaling of "" here, do it in + // IPMarshalUnmarshal. + return + } + var jsgot Addr + js := []byte(`"` + s + `"`) + if err := json.Unmarshal(js, &jsgot); err == nil { + t.Errorf("json.Unmarshal(%q) = %#v, want error", s, jsgot) + } + }) + } +} + +func TestIPv4Constructors(t *testing.T) { + if AddrFrom4([4]byte{1, 2, 3, 4}) != MustParseAddr("1.2.3.4") { + t.Errorf("don't match") + } +} + +func TestAddrMarshalUnmarshalBinary(t *testing.T) { + tests := []struct { + ip string + wantSize int + }{ + {"", 0}, // zero IP + {"1.2.3.4", 4}, + {"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", 16}, + {"::ffff:c000:0280", 16}, + {"::ffff:c000:0280%eth0", 20}, + } + for _, tc := range tests { + var ip Addr + if len(tc.ip) > 0 { + ip = mustIP(tc.ip) + } + b, err := ip.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if len(b) != tc.wantSize { + t.Fatalf("%q encoded to size %d; want %d", tc.ip, len(b), tc.wantSize) + } + var ip2 Addr + if err := ip2.UnmarshalBinary(b); err != nil { + t.Fatal(err) + } + if ip != ip2 { + t.Fatalf("got %v; want %v", ip2, ip) + } + } + + // Cannot unmarshal from unexpected IP length. + for _, n := range []int{3, 5} { + var ip2 Addr + if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil { + t.Fatalf("unmarshaled from unexpected IP length %d", n) + } + } +} + +func TestAddrPortMarshalTextString(t *testing.T) { + tests := []struct { + in AddrPort + want string + }{ + {mustIPPort("1.2.3.4:80"), "1.2.3.4:80"}, + {mustIPPort("[1::CAFE]:80"), "[1::cafe]:80"}, + {mustIPPort("[1::CAFE%en0]:80"), "[1::cafe%en0]:80"}, + {mustIPPort("[::FFFF:192.168.140.255]:80"), "[::ffff:192.168.140.255]:80"}, + {mustIPPort("[::FFFF:192.168.140.255%en0]:80"), "[::ffff:192.168.140.255%en0]:80"}, + } + for i, tt := range tests { + if got := tt.in.String(); got != tt.want { + t.Errorf("%d. for (%v, %v) String = %q; want %q", i, tt.in.Addr(), tt.in.Port(), got, tt.want) + } + mt, err := tt.in.MarshalText() + if err != nil { + t.Errorf("%d. for (%v, %v) MarshalText error: %v", i, tt.in.Addr(), tt.in.Port(), err) + continue + } + if string(mt) != tt.want { + t.Errorf("%d. for (%v, %v) MarshalText = %q; want %q", i, tt.in.Addr(), tt.in.Port(), mt, tt.want) + } + } +} + +func TestAddrPortMarshalUnmarshalBinary(t *testing.T) { + tests := []struct { + ipport string + wantSize int + }{ + {"1.2.3.4:51820", 4 + 2}, + {"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2}, + {"[::ffff:c000:0280]:65535", 16 + 2}, + {"[::ffff:c000:0280%eth0]:1", 20 + 2}, + } + for _, tc := range tests { + var ipport AddrPort + if len(tc.ipport) > 0 { + ipport = mustIPPort(tc.ipport) + } + b, err := ipport.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if len(b) != tc.wantSize { + t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize) + } + var ipport2 AddrPort + if err := ipport2.UnmarshalBinary(b); err != nil { + t.Fatal(err) + } + if ipport != ipport2 { + t.Fatalf("got %v; want %v", ipport2, ipport) + } + } + + // Cannot unmarshal from unexpected lengths. + for _, n := range []int{3, 7} { + var ipport2 AddrPort + if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil { + t.Fatalf("unmarshaled from unexpected length %d", n) + } + } +} + +func TestPrefixMarshalTextString(t *testing.T) { + tests := []struct { + in Prefix + want string + }{ + {mustPrefix("1.2.3.4/24"), "1.2.3.4/24"}, + {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"}, + {mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"}, + {mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"}, + {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped + } + for i, tt := range tests { + if got := tt.in.String(); got != tt.want { + t.Errorf("%d. for %v String = %q; want %q", i, tt.in, got, tt.want) + } + mt, err := tt.in.MarshalText() + if err != nil { + t.Errorf("%d. for %v MarshalText error: %v", i, tt.in, err) + continue + } + if string(mt) != tt.want { + t.Errorf("%d. for %v MarshalText = %q; want %q", i, tt.in, mt, tt.want) + } + } +} + +func TestPrefixMarshalUnmarshalBinary(t *testing.T) { + type testCase struct { + prefix Prefix + wantSize int + } + tests := []testCase{ + {mustPrefix("1.2.3.4/24"), 4 + 1}, + {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1}, + {mustPrefix("::ffff:c000:0280/96"), 16 + 1}, + {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped + } + tests = append(tests, + testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize}, + testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize}) + for _, tc := range tests { + prefix := tc.prefix + b, err := prefix.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if len(b) != tc.wantSize { + t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize) + } + var prefix2 Prefix + if err := prefix2.UnmarshalBinary(b); err != nil { + t.Fatal(err) + } + if prefix != prefix2 { + t.Fatalf("got %v; want %v", prefix2, prefix) + } + } + + // Cannot unmarshal from unexpected lengths. + for _, n := range []int{3, 6} { + var prefix2 Prefix + if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil { + t.Fatalf("unmarshaled from unexpected length %d", n) + } + } +} + +func TestAddrMarshalUnmarshal(t *testing.T) { + // This only tests the cases where Marshal/Unmarshal diverges from + // the behavior of ParseAddr/String. For the rest of the test cases, + // see TestParseAddr above. + orig := `""` + var ip Addr + if err := json.Unmarshal([]byte(orig), &ip); err != nil { + t.Fatalf("Unmarshal(%q) got error %v", orig, err) + } + if ip != (Addr{}) { + t.Errorf("Unmarshal(%q) is not the zero Addr", orig) + } + + jsb, err := json.Marshal(ip) + if err != nil { + t.Fatalf("Marshal(%v) got error %v", ip, err) + } + back := string(jsb) + if back != orig { + t.Errorf("Marshal(Unmarshal(%q)) got %q, want %q", orig, back, orig) + } +} + +func TestAddrFrom16(t *testing.T) { + tests := []struct { + name string + in [16]byte + want Addr + }{ + { + name: "v6-raw", + in: [...]byte{15: 1}, + want: MkAddr(Mk128(0, 1), Z6noz), + }, + { + name: "v4-raw", + in: [...]byte{10: 0xff, 11: 0xff, 12: 1, 13: 2, 14: 3, 15: 4}, + want: MkAddr(Mk128(0, 0xffff01020304), Z6noz), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := AddrFrom16(tt.in) + if got != tt.want { + t.Errorf("got %#v; want %#v", got, tt.want) + } + }) + } +} + +func TestIPProperties(t *testing.T) { + var ( + nilIP Addr + + unicast4 = mustIP("192.0.2.1") + unicast6 = mustIP("2001:db8::1") + unicastZone6 = mustIP("2001:db8::1%eth0") + unicast6Unassigned = mustIP("4000::1") // not in 2000::/3. + + multicast4 = mustIP("224.0.0.1") + multicast6 = mustIP("ff02::1") + multicastZone6 = mustIP("ff02::1%eth0") + + llu4 = mustIP("169.254.0.1") + llu6 = mustIP("fe80::1") + llu6Last = mustIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff") + lluZone6 = mustIP("fe80::1%eth0") + + loopback4 = mustIP("127.0.0.1") + loopback6 = mustIP("::1") + + ilm6 = mustIP("ff01::1") + ilmZone6 = mustIP("ff01::1%eth0") + + private4a = mustIP("10.0.0.1") + private4b = mustIP("172.16.0.1") + private4c = mustIP("192.168.1.1") + private6 = mustIP("fd00::1") + + unspecified4 = AddrFrom4([4]byte{}) + unspecified6 = IPv6Unspecified() + ) + + tests := []struct { + name string + ip Addr + globalUnicast bool + interfaceLocalMulticast bool + linkLocalMulticast bool + linkLocalUnicast bool + loopback bool + multicast bool + private bool + unspecified bool + }{ + { + name: "nil", + ip: nilIP, + }, + { + name: "unicast v4Addr", + ip: unicast4, + globalUnicast: true, + }, + { + name: "unicast v6Addr", + ip: unicast6, + globalUnicast: true, + }, + { + name: "unicast v6AddrZone", + ip: unicastZone6, + globalUnicast: true, + }, + { + name: "unicast v6Addr unassigned", + ip: unicast6Unassigned, + globalUnicast: true, + }, + { + name: "multicast v4Addr", + ip: multicast4, + linkLocalMulticast: true, + multicast: true, + }, + { + name: "multicast v6Addr", + ip: multicast6, + linkLocalMulticast: true, + multicast: true, + }, + { + name: "multicast v6AddrZone", + ip: multicastZone6, + linkLocalMulticast: true, + multicast: true, + }, + { + name: "link-local unicast v4Addr", + ip: llu4, + linkLocalUnicast: true, + }, + { + name: "link-local unicast v6Addr", + ip: llu6, + linkLocalUnicast: true, + }, + { + name: "link-local unicast v6Addr upper bound", + ip: llu6Last, + linkLocalUnicast: true, + }, + { + name: "link-local unicast v6AddrZone", + ip: lluZone6, + linkLocalUnicast: true, + }, + { + name: "loopback v4Addr", + ip: loopback4, + loopback: true, + }, + { + name: "loopback v6Addr", + ip: loopback6, + loopback: true, + }, + { + name: "interface-local multicast v6Addr", + ip: ilm6, + interfaceLocalMulticast: true, + multicast: true, + }, + { + name: "interface-local multicast v6AddrZone", + ip: ilmZone6, + interfaceLocalMulticast: true, + multicast: true, + }, + { + name: "private v4Addr 10/8", + ip: private4a, + globalUnicast: true, + private: true, + }, + { + name: "private v4Addr 172.16/12", + ip: private4b, + globalUnicast: true, + private: true, + }, + { + name: "private v4Addr 192.168/16", + ip: private4c, + globalUnicast: true, + private: true, + }, + { + name: "private v6Addr", + ip: private6, + globalUnicast: true, + private: true, + }, + { + name: "unspecified v4Addr", + ip: unspecified4, + unspecified: true, + }, + { + name: "unspecified v6Addr", + ip: unspecified6, + unspecified: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gu := tt.ip.IsGlobalUnicast() + if gu != tt.globalUnicast { + t.Errorf("IsGlobalUnicast(%v) = %v; want %v", tt.ip, gu, tt.globalUnicast) + } + + ilm := tt.ip.IsInterfaceLocalMulticast() + if ilm != tt.interfaceLocalMulticast { + t.Errorf("IsInterfaceLocalMulticast(%v) = %v; want %v", tt.ip, ilm, tt.interfaceLocalMulticast) + } + + llu := tt.ip.IsLinkLocalUnicast() + if llu != tt.linkLocalUnicast { + t.Errorf("IsLinkLocalUnicast(%v) = %v; want %v", tt.ip, llu, tt.linkLocalUnicast) + } + + llm := tt.ip.IsLinkLocalMulticast() + if llm != tt.linkLocalMulticast { + t.Errorf("IsLinkLocalMulticast(%v) = %v; want %v", tt.ip, llm, tt.linkLocalMulticast) + } + + lo := tt.ip.IsLoopback() + if lo != tt.loopback { + t.Errorf("IsLoopback(%v) = %v; want %v", tt.ip, lo, tt.loopback) + } + + multicast := tt.ip.IsMulticast() + if multicast != tt.multicast { + t.Errorf("IsMulticast(%v) = %v; want %v", tt.ip, multicast, tt.multicast) + } + + private := tt.ip.IsPrivate() + if private != tt.private { + t.Errorf("IsPrivate(%v) = %v; want %v", tt.ip, private, tt.private) + } + + unspecified := tt.ip.IsUnspecified() + if unspecified != tt.unspecified { + t.Errorf("IsUnspecified(%v) = %v; want %v", tt.ip, unspecified, tt.unspecified) + } + }) + } +} + +func TestAddrWellKnown(t *testing.T) { + tests := []struct { + name string + ip Addr + std net.IP + }{ + { + name: "IPv6 link-local all nodes", + ip: IPv6LinkLocalAllNodes(), + std: net.IPv6linklocalallnodes, + }, + { + name: "IPv6 unspecified", + ip: IPv6Unspecified(), + std: net.IPv6unspecified, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + want := tt.std.String() + got := tt.ip.String() + + if got != want { + t.Fatalf("got %s, want %s", got, want) + } + }) + } +} + +func TestLessCompare(t *testing.T) { + tests := []struct { + a, b Addr + want bool + }{ + {Addr{}, Addr{}, false}, + {Addr{}, mustIP("1.2.3.4"), true}, + {mustIP("1.2.3.4"), Addr{}, false}, + + {mustIP("1.2.3.4"), mustIP("0102:0304::0"), true}, + {mustIP("0102:0304::0"), mustIP("1.2.3.4"), false}, + {mustIP("1.2.3.4"), mustIP("1.2.3.4"), false}, + + {mustIP("::1"), mustIP("::2"), true}, + {mustIP("::1"), mustIP("::1%foo"), true}, + {mustIP("::1%foo"), mustIP("::2"), true}, + {mustIP("::2"), mustIP("::3"), true}, + + {mustIP("::"), mustIP("0.0.0.0"), false}, + {mustIP("0.0.0.0"), mustIP("::"), true}, + + {mustIP("::1%a"), mustIP("::1%b"), true}, + {mustIP("::1%a"), mustIP("::1%a"), false}, + {mustIP("::1%b"), mustIP("::1%a"), false}, + } + for _, tt := range tests { + got := tt.a.Less(tt.b) + if got != tt.want { + t.Errorf("Less(%q, %q) = %v; want %v", tt.a, tt.b, got, tt.want) + } + cmp := tt.a.Compare(tt.b) + if got && cmp != -1 { + t.Errorf("Less(%q, %q) = true, but Compare = %v (not -1)", tt.a, tt.b, cmp) + } + if cmp < -1 || cmp > 1 { + t.Errorf("bogus Compare return value %v", cmp) + } + if cmp == 0 && tt.a != tt.b { + t.Errorf("Compare(%q, %q) = 0; but not equal", tt.a, tt.b) + } + if cmp == 1 && !tt.b.Less(tt.a) { + t.Errorf("Compare(%q, %q) = 1; but b.Less(a) isn't true", tt.a, tt.b) + } + + // Also check inverse. + if got == tt.want && got { + got2 := tt.b.Less(tt.a) + if got2 { + t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a) + } + } + } + + // And just sort. + values := []Addr{ + mustIP("::1"), + mustIP("::2"), + Addr{}, + mustIP("1.2.3.4"), + mustIP("8.8.8.8"), + mustIP("::1%foo"), + } + sort.Slice(values, func(i, j int) bool { return values[i].Less(values[j]) }) + got := fmt.Sprintf("%s", values) + want := `[invalid IP 1.2.3.4 8.8.8.8 ::1 ::1%foo ::2]` + if got != want { + t.Errorf("unexpected sort\n got: %s\nwant: %s\n", got, want) + } +} + +func TestIPStringExpanded(t *testing.T) { + tests := []struct { + ip Addr + s string + }{ + { + ip: Addr{}, + s: "invalid IP", + }, + { + ip: mustIP("192.0.2.1"), + s: "192.0.2.1", + }, + { + ip: mustIP("::ffff:192.0.2.1"), + s: "0000:0000:0000:0000:0000:ffff:c000:0201", + }, + { + ip: mustIP("2001:db8::1"), + s: "2001:0db8:0000:0000:0000:0000:0000:0001", + }, + { + ip: mustIP("2001:db8::1%eth0"), + s: "2001:0db8:0000:0000:0000:0000:0000:0001%eth0", + }, + } + + for _, tt := range tests { + t.Run(tt.ip.String(), func(t *testing.T) { + want := tt.s + got := tt.ip.StringExpanded() + + if got != want { + t.Fatalf("got %s, want %s", got, want) + } + }) + } +} + +func TestPrefixMasking(t *testing.T) { + type subtest struct { + ip Addr + bits uint8 + p Prefix + ok bool + } + + // makeIPv6 produces a set of IPv6 subtests with an optional zone identifier. + makeIPv6 := func(zone string) []subtest { + if zone != "" { + zone = "%" + zone + } + + return []subtest{ + { + ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)), + bits: 255, + }, + { + ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)), + bits: 32, + p: mustPrefix("2001:db8::/32"), + ok: true, + }, + { + ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)), + bits: 96, + p: mustPrefix("fe80::dead:beef:0:0/96"), + ok: true, + }, + { + ip: mustIP(fmt.Sprintf("aaaa::%s", zone)), + bits: 4, + p: mustPrefix("a000::/4"), + ok: true, + }, + { + ip: mustIP(fmt.Sprintf("::%s", zone)), + bits: 63, + p: mustPrefix("::/63"), + ok: true, + }, + } + } + + tests := []struct { + family string + subtests []subtest + }{ + { + family: "nil", + subtests: []subtest{ + { + bits: 255, + ok: true, + }, + { + bits: 16, + ok: true, + }, + }, + }, + { + family: "IPv4", + subtests: []subtest{ + { + ip: mustIP("192.0.2.0"), + bits: 255, + }, + { + ip: mustIP("192.0.2.0"), + bits: 16, + p: mustPrefix("192.0.0.0/16"), + ok: true, + }, + { + ip: mustIP("255.255.255.255"), + bits: 20, + p: mustPrefix("255.255.240.0/20"), + ok: true, + }, + { + // Partially masking one byte that contains both + // 1s and 0s on either side of the mask limit. + ip: mustIP("100.98.156.66"), + bits: 10, + p: mustPrefix("100.64.0.0/10"), + ok: true, + }, + }, + }, + { + family: "IPv6", + subtests: makeIPv6(""), + }, + { + family: "IPv6 zone", + subtests: makeIPv6("eth0"), + }, + } + + for _, tt := range tests { + t.Run(tt.family, func(t *testing.T) { + for _, st := range tt.subtests { + t.Run(st.p.String(), func(t *testing.T) { + // Ensure st.ip is not mutated. + orig := st.ip.String() + + p, err := st.ip.Prefix(int(st.bits)) + if st.ok && err != nil { + t.Fatalf("failed to produce prefix: %v", err) + } + if !st.ok && err == nil { + t.Fatal("expected an error, but none occurred") + } + if err != nil { + t.Logf("err: %v", err) + return + } + + if !reflect.DeepEqual(p, st.p) { + t.Errorf("prefix = %q, want %q", p, st.p) + } + + if got := st.ip.String(); got != orig { + t.Errorf("IP was mutated: %q, want %q", got, orig) + } + }) + } + }) + } +} + +func TestPrefixMarshalUnmarshal(t *testing.T) { + tests := []string{ + "", + "1.2.3.4/32", + "0.0.0.0/0", + "::/0", + "::1/128", + "2001:db8::/32", + } + + for _, s := range tests { + t.Run(s, func(t *testing.T) { + // Ensure that JSON (and by extension, text) marshaling is + // sane by entering quoted input. + orig := `"` + s + `"` + + var p Prefix + if err := json.Unmarshal([]byte(orig), &p); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + + pb, err := json.Marshal(p) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + + back := string(pb) + if orig != back { + t.Errorf("Marshal = %q; want %q", back, orig) + } + }) + } +} + +func TestPrefixUnmarshalTextNonZero(t *testing.T) { + ip := mustPrefix("fe80::/64") + if err := ip.UnmarshalText([]byte("xxx")); err == nil { + t.Fatal("unmarshaled into non-empty Prefix") + } +} + +func TestIs4AndIs6(t *testing.T) { + tests := []struct { + ip Addr + is4 bool + is6 bool + }{ + {Addr{}, false, false}, + {mustIP("1.2.3.4"), true, false}, + {mustIP("127.0.0.2"), true, false}, + {mustIP("::1"), false, true}, + {mustIP("::ffff:192.0.2.128"), false, true}, + {mustIP("::fffe:c000:0280"), false, true}, + {mustIP("::1%eth0"), false, true}, + } + for _, tt := range tests { + got4 := tt.ip.Is4() + if got4 != tt.is4 { + t.Errorf("Is4(%q) = %v; want %v", tt.ip, got4, tt.is4) + } + + got6 := tt.ip.Is6() + if got6 != tt.is6 { + t.Errorf("Is6(%q) = %v; want %v", tt.ip, got6, tt.is6) + } + } +} + +func TestIs4In6(t *testing.T) { + tests := []struct { + ip Addr + want bool + wantUnmap Addr + }{ + {Addr{}, false, Addr{}}, + {mustIP("::ffff:c000:0280"), true, mustIP("192.0.2.128")}, + {mustIP("::ffff:192.0.2.128"), true, mustIP("192.0.2.128")}, + {mustIP("::ffff:192.0.2.128%eth0"), true, mustIP("192.0.2.128")}, + {mustIP("::fffe:c000:0280"), false, mustIP("::fffe:c000:0280")}, + {mustIP("::ffff:127.1.2.3"), true, mustIP("127.1.2.3")}, + {mustIP("::ffff:7f01:0203"), true, mustIP("127.1.2.3")}, + {mustIP("0:0:0:0:0000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")}, + {mustIP("0:0:0:0:000000:ffff:127.1.2.3"), true, mustIP("127.1.2.3")}, + {mustIP("0:0:0:0::ffff:127.1.2.3"), true, mustIP("127.1.2.3")}, + {mustIP("::1"), false, mustIP("::1")}, + {mustIP("1.2.3.4"), false, mustIP("1.2.3.4")}, + } + for _, tt := range tests { + got := tt.ip.Is4In6() + if got != tt.want { + t.Errorf("Is4In6(%q) = %v; want %v", tt.ip, got, tt.want) + } + u := tt.ip.Unmap() + if u != tt.wantUnmap { + t.Errorf("Unmap(%q) = %v; want %v", tt.ip, u, tt.wantUnmap) + } + } +} + +func TestPrefixMasked(t *testing.T) { + tests := []struct { + prefix Prefix + masked Prefix + }{ + { + prefix: mustPrefix("192.168.0.255/24"), + masked: mustPrefix("192.168.0.0/24"), + }, + { + prefix: mustPrefix("2100::/3"), + masked: mustPrefix("2000::/3"), + }, + { + prefix: PrefixFrom(mustIP("2000::"), 129), + masked: Prefix{}, + }, + { + prefix: PrefixFrom(mustIP("1.2.3.4"), 33), + masked: Prefix{}, + }, + } + for _, test := range tests { + t.Run(test.prefix.String(), func(t *testing.T) { + got := test.prefix.Masked() + if got != test.masked { + t.Errorf("Masked=%s, want %s", got, test.masked) + } + }) + } +} + +func TestPrefix(t *testing.T) { + tests := []struct { + prefix string + ip Addr + bits int + str string + contains []Addr + notContains []Addr + }{ + { + prefix: "192.168.0.0/24", + ip: mustIP("192.168.0.0"), + bits: 24, + contains: mustIPs("192.168.0.1", "192.168.0.55"), + notContains: mustIPs("192.168.1.1", "1.1.1.1"), + }, + { + prefix: "192.168.1.1/32", + ip: mustIP("192.168.1.1"), + bits: 32, + contains: mustIPs("192.168.1.1"), + notContains: mustIPs("192.168.1.2"), + }, + { + prefix: "100.64.0.0/10", // CGNAT range; prefix not multiple of 8 + ip: mustIP("100.64.0.0"), + bits: 10, + contains: mustIPs("100.64.0.0", "100.64.0.1", "100.81.251.94", "100.100.100.100", "100.127.255.254", "100.127.255.255"), + notContains: mustIPs("100.63.255.255", "100.128.0.0"), + }, + { + prefix: "2001:db8::/96", + ip: mustIP("2001:db8::"), + bits: 96, + contains: mustIPs("2001:db8::aaaa:bbbb", "2001:db8::1"), + notContains: mustIPs("2001:db8::1:aaaa:bbbb", "2001:db9::"), + }, + { + prefix: "0.0.0.0/0", + ip: mustIP("0.0.0.0"), + bits: 0, + contains: mustIPs("192.168.0.1", "1.1.1.1"), + notContains: append(mustIPs("2001:db8::1"), Addr{}), + }, + { + prefix: "::/0", + ip: mustIP("::"), + bits: 0, + contains: mustIPs("::1", "2001:db8::1"), + notContains: mustIPs("192.0.2.1"), + }, + { + prefix: "2000::/3", + ip: mustIP("2000::"), + bits: 3, + contains: mustIPs("2001:db8::1"), + notContains: mustIPs("fe80::1"), + }, + } + for _, test := range tests { + t.Run(test.prefix, func(t *testing.T) { + prefix, err := ParsePrefix(test.prefix) + if err != nil { + t.Fatal(err) + } + if prefix.Addr() != test.ip { + t.Errorf("IP=%s, want %s", prefix.Addr(), test.ip) + } + if prefix.Bits() != test.bits { + t.Errorf("bits=%d, want %d", prefix.Bits(), test.bits) + } + for _, ip := range test.contains { + if !prefix.Contains(ip) { + t.Errorf("does not contain %s", ip) + } + } + for _, ip := range test.notContains { + if prefix.Contains(ip) { + t.Errorf("contains %s", ip) + } + } + want := test.str + if want == "" { + want = test.prefix + } + if got := prefix.String(); got != want { + t.Errorf("prefix.String()=%q, want %q", got, want) + } + + TestAppendToMarshal(t, prefix) + }) + } +} + +func TestPrefixFromInvalidBits(t *testing.T) { + v4 := MustParseAddr("1.2.3.4") + v6 := MustParseAddr("66::66") + tests := []struct { + ip Addr + in, want int + }{ + {v4, 0, 0}, + {v6, 0, 0}, + {v4, 1, 1}, + {v4, 33, -1}, + {v6, 33, 33}, + {v6, 127, 127}, + {v6, 128, 128}, + {v4, 254, -1}, + {v4, 255, -1}, + {v4, -1, -1}, + {v6, -1, -1}, + {v4, -5, -1}, + {v6, -5, -1}, + } + for _, tt := range tests { + p := PrefixFrom(tt.ip, tt.in) + if got := p.Bits(); got != tt.want { + t.Errorf("for (%v, %v), Bits out = %v; want %v", tt.ip, tt.in, got, tt.want) + } + } +} + +func TestParsePrefixAllocs(t *testing.T) { + tests := []struct { + ip string + slash string + }{ + {"192.168.1.0", "/24"}, + {"aaaa:bbbb:cccc::", "/24"}, + } + for _, test := range tests { + prefix := test.ip + test.slash + t.Run(prefix, func(t *testing.T) { + ipAllocs := int(testing.AllocsPerRun(5, func() { + ParseAddr(test.ip) + })) + prefixAllocs := int(testing.AllocsPerRun(5, func() { + ParsePrefix(prefix) + })) + if got := prefixAllocs - ipAllocs; got != 0 { + t.Errorf("allocs=%d, want 0", got) + } + }) + } +} + +func TestParsePrefixError(t *testing.T) { + tests := []struct { + prefix string + errstr string + }{ + { + prefix: "192.168.0.0", + errstr: "no '/'", + }, + { + prefix: "1.257.1.1/24", + errstr: "value >255", + }, + { + prefix: "1.1.1.0/q", + errstr: "bad bits", + }, + { + prefix: "1.1.1.0/-1", + errstr: "out of range", + }, + { + prefix: "1.1.1.0/33", + errstr: "out of range", + }, + { + prefix: "2001::/129", + errstr: "out of range", + }, + // Zones are not allowed: https://go.dev/issue/51899 + { + prefix: "1.1.1.0%a/24", + errstr: "unexpected character", + }, + { + prefix: "2001:db8::%a/32", + errstr: "zones cannot be present", + }, + } + for _, test := range tests { + t.Run(test.prefix, func(t *testing.T) { + _, err := ParsePrefix(test.prefix) + if err == nil { + t.Fatal("no error") + } + if got := err.Error(); !strings.Contains(got, test.errstr) { + t.Errorf("error is missing substring %q: %s", test.errstr, got) + } + }) + } +} + +func TestPrefixIsSingleIP(t *testing.T) { + tests := []struct { + ipp Prefix + want bool + }{ + {ipp: mustPrefix("127.0.0.1/32"), want: true}, + {ipp: mustPrefix("127.0.0.1/31"), want: false}, + {ipp: mustPrefix("127.0.0.1/0"), want: false}, + {ipp: mustPrefix("::1/128"), want: true}, + {ipp: mustPrefix("::1/127"), want: false}, + {ipp: mustPrefix("::1/0"), want: false}, + {ipp: Prefix{}, want: false}, + } + for _, tt := range tests { + got := tt.ipp.IsSingleIP() + if got != tt.want { + t.Errorf("IsSingleIP(%v) = %v want %v", tt.ipp, got, tt.want) + } + } +} + +func mustIPs(strs ...string) []Addr { + var res []Addr + for _, s := range strs { + res = append(res, mustIP(s)) + } + return res +} + +func BenchmarkBinaryMarshalRoundTrip(b *testing.B) { + b.ReportAllocs() + tests := []struct { + name string + ip string + }{ + {"ipv4", "1.2.3.4"}, + {"ipv6", "2001:db8::1"}, + {"ipv6+zone", "2001:db8::1%eth0"}, + } + for _, tc := range tests { + b.Run(tc.name, func(b *testing.B) { + ip := mustIP(tc.ip) + for i := 0; i < b.N; i++ { + bt, err := ip.MarshalBinary() + if err != nil { + b.Fatal(err) + } + var ip2 Addr + if err := ip2.UnmarshalBinary(bt); err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkStdIPv4(b *testing.B) { + b.ReportAllocs() + ips := []net.IP{} + for i := 0; i < b.N; i++ { + ip := net.IPv4(8, 8, 8, 8) + ips = ips[:0] + for i := 0; i < 100; i++ { + ips = append(ips, ip) + } + } +} + +func BenchmarkIPv4(b *testing.B) { + b.ReportAllocs() + ips := []Addr{} + for i := 0; i < b.N; i++ { + ip := IPv4(8, 8, 8, 8) + ips = ips[:0] + for i := 0; i < 100; i++ { + ips = append(ips, ip) + } + } +} + +// ip4i was one of the possible representations of IP that came up in +// discussions, inlining IPv4 addresses, but having an "overflow" +// interface for IPv6 or IPv6 + zone. This is here for benchmarking. +type ip4i struct { + ip4 [4]byte + flags1 byte + flags2 byte + flags3 byte + flags4 byte + ipv6 any +} + +func newip4i_v4(a, b, c, d byte) ip4i { + return ip4i{ip4: [4]byte{a, b, c, d}} +} + +// BenchmarkIPv4_inline benchmarks the candidate representation, ip4i. +func BenchmarkIPv4_inline(b *testing.B) { + b.ReportAllocs() + ips := []ip4i{} + for i := 0; i < b.N; i++ { + ip := newip4i_v4(8, 8, 8, 8) + ips = ips[:0] + for i := 0; i < 100; i++ { + ips = append(ips, ip) + } + } +} + +func BenchmarkStdIPv6(b *testing.B) { + b.ReportAllocs() + ips := []net.IP{} + for i := 0; i < b.N; i++ { + ip := net.ParseIP("2001:db8::1") + ips = ips[:0] + for i := 0; i < 100; i++ { + ips = append(ips, ip) + } + } +} + +func BenchmarkIPv6(b *testing.B) { + b.ReportAllocs() + ips := []Addr{} + for i := 0; i < b.N; i++ { + ip := mustIP("2001:db8::1") + ips = ips[:0] + for i := 0; i < 100; i++ { + ips = append(ips, ip) + } + } +} + +func BenchmarkIPv4Contains(b *testing.B) { + b.ReportAllocs() + prefix := PrefixFrom(IPv4(192, 168, 1, 0), 24) + ip := IPv4(192, 168, 1, 1) + for i := 0; i < b.N; i++ { + prefix.Contains(ip) + } +} + +func BenchmarkIPv6Contains(b *testing.B) { + b.ReportAllocs() + prefix := MustParsePrefix("::1/128") + ip := MustParseAddr("::1") + for i := 0; i < b.N; i++ { + prefix.Contains(ip) + } +} + +var parseBenchInputs = []struct { + name string + ip string +}{ + {"v4", "192.168.1.1"}, + {"v6", "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b"}, + {"v6_ellipsis", "fd7a:115c::626b:430b"}, + {"v6_v4", "::ffff:192.168.140.255"}, + {"v6_zone", "1:2::ffff:192.168.140.255%eth1"}, +} + +func BenchmarkParseAddr(b *testing.B) { + sinkInternValue = intern.Get("eth1") // Pin to not benchmark the intern package + for _, test := range parseBenchInputs { + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkIP, _ = ParseAddr(test.ip) + } + }) + } +} + +func BenchmarkStdParseIP(b *testing.B) { + for _, test := range parseBenchInputs { + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkStdIP = net.ParseIP(test.ip) + } + }) + } +} + +func BenchmarkIPString(b *testing.B) { + for _, test := range parseBenchInputs { + ip := MustParseAddr(test.ip) + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkString = ip.String() + } + }) + } +} + +func BenchmarkIPStringExpanded(b *testing.B) { + for _, test := range parseBenchInputs { + ip := MustParseAddr(test.ip) + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkString = ip.StringExpanded() + } + }) + } +} + +func BenchmarkIPMarshalText(b *testing.B) { + b.ReportAllocs() + ip := MustParseAddr("66.55.44.33") + for i := 0; i < b.N; i++ { + sinkBytes, _ = ip.MarshalText() + } +} + +func BenchmarkAddrPortString(b *testing.B) { + for _, test := range parseBenchInputs { + ip := MustParseAddr(test.ip) + ipp := AddrPortFrom(ip, 60000) + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkString = ipp.String() + } + }) + } +} + +func BenchmarkAddrPortMarshalText(b *testing.B) { + for _, test := range parseBenchInputs { + ip := MustParseAddr(test.ip) + ipp := AddrPortFrom(ip, 60000) + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sinkBytes, _ = ipp.MarshalText() + } + }) + } +} + +func BenchmarkPrefixMasking(b *testing.B) { + tests := []struct { + name string + ip Addr + bits int + }{ + { + name: "IPv4 /32", + ip: IPv4(192, 0, 2, 0), + bits: 32, + }, + { + name: "IPv4 /17", + ip: IPv4(192, 0, 2, 0), + bits: 17, + }, + { + name: "IPv4 /0", + ip: IPv4(192, 0, 2, 0), + bits: 0, + }, + { + name: "IPv6 /128", + ip: mustIP("2001:db8::1"), + bits: 128, + }, + { + name: "IPv6 /65", + ip: mustIP("2001:db8::1"), + bits: 65, + }, + { + name: "IPv6 /0", + ip: mustIP("2001:db8::1"), + bits: 0, + }, + { + name: "IPv6 zone /128", + ip: mustIP("2001:db8::1%eth0"), + bits: 128, + }, + { + name: "IPv6 zone /65", + ip: mustIP("2001:db8::1%eth0"), + bits: 65, + }, + { + name: "IPv6 zone /0", + ip: mustIP("2001:db8::1%eth0"), + bits: 0, + }, + } + + for _, tt := range tests { + b.Run(tt.name, func(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + sinkPrefix, _ = tt.ip.Prefix(tt.bits) + } + }) + } +} + +func BenchmarkPrefixMarshalText(b *testing.B) { + b.ReportAllocs() + ipp := MustParsePrefix("66.55.44.33/22") + for i := 0; i < b.N; i++ { + sinkBytes, _ = ipp.MarshalText() + } +} + +func BenchmarkParseAddrPort(b *testing.B) { + for _, test := range parseBenchInputs { + var ipp string + if strings.HasPrefix(test.name, "v6") { + ipp = fmt.Sprintf("[%s]:1234", test.ip) + } else { + ipp = fmt.Sprintf("%s:1234", test.ip) + } + b.Run(test.name, func(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + sinkAddrPort, _ = ParseAddrPort(ipp) + } + }) + } +} + +func TestAs4(t *testing.T) { + tests := []struct { + ip Addr + want [4]byte + wantPanic bool + }{ + { + ip: mustIP("1.2.3.4"), + want: [4]byte{1, 2, 3, 4}, + }, + { + ip: AddrFrom16(mustIP("1.2.3.4").As16()), // IPv4-in-IPv6 + want: [4]byte{1, 2, 3, 4}, + }, + { + ip: mustIP("0.0.0.0"), + want: [4]byte{0, 0, 0, 0}, + }, + { + ip: Addr{}, + wantPanic: true, + }, + { + ip: mustIP("::1"), + wantPanic: true, + }, + } + as4 := func(ip Addr) (v [4]byte, gotPanic bool) { + defer func() { + if recover() != nil { + gotPanic = true + return + } + }() + v = ip.As4() + return + } + for i, tt := range tests { + got, gotPanic := as4(tt.ip) + if gotPanic != tt.wantPanic { + t.Errorf("%d. panic on %v = %v; want %v", i, tt.ip, gotPanic, tt.wantPanic) + continue + } + if got != tt.want { + t.Errorf("%d. %v = %v; want %v", i, tt.ip, got, tt.want) + } + } +} + +func TestPrefixOverlaps(t *testing.T) { + pfx := mustPrefix + tests := []struct { + a, b Prefix + want bool + }{ + {Prefix{}, pfx("1.2.0.0/16"), false}, // first zero + {pfx("1.2.0.0/16"), Prefix{}, false}, // second zero + {pfx("::0/3"), pfx("0.0.0.0/3"), false}, // different families + + {pfx("1.2.0.0/16"), pfx("1.2.0.0/16"), true}, // equal + + {pfx("1.2.0.0/16"), pfx("1.2.3.0/24"), true}, + {pfx("1.2.3.0/24"), pfx("1.2.0.0/16"), true}, + + {pfx("1.2.0.0/16"), pfx("1.2.3.0/32"), true}, + {pfx("1.2.3.0/32"), pfx("1.2.0.0/16"), true}, + + // Match /0 either order + {pfx("1.2.3.0/32"), pfx("0.0.0.0/0"), true}, + {pfx("0.0.0.0/0"), pfx("1.2.3.0/32"), true}, + + {pfx("1.2.3.0/32"), pfx("5.5.5.5/0"), true}, // normalization not required; /0 means true + + // IPv6 overlapping + {pfx("5::1/128"), pfx("5::0/8"), true}, + {pfx("5::0/8"), pfx("5::1/128"), true}, + + // IPv6 not overlapping + {pfx("1::1/128"), pfx("2::2/128"), false}, + {pfx("0100::0/8"), pfx("::1/128"), false}, + + // IPv4-mapped IPv6 addresses should not overlap with IPv4. + {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false}, + + // Invalid prefixes + {PrefixFrom(mustIP("1.2.3.4"), 33), pfx("1.2.3.0/24"), false}, + {PrefixFrom(mustIP("2000::"), 129), pfx("2000::/64"), false}, + } + for i, tt := range tests { + if got := tt.a.Overlaps(tt.b); got != tt.want { + t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.a, tt.b, got, tt.want) + } + // Overlaps is commutative + if got := tt.b.Overlaps(tt.a); got != tt.want { + t.Errorf("%d. (%v).Overlaps(%v) = %v; want %v", i, tt.b, tt.a, got, tt.want) + } + } +} + +// Sink variables are here to force the compiler to not elide +// seemingly useless work in benchmarks and allocation tests. If you +// were to just `_ = foo()` within a test function, the compiler could +// correctly deduce that foo() does nothing and doesn't need to be +// called. By writing results to a global variable, we hide that fact +// from the compiler and force it to keep the code under test. +var ( + sinkIP Addr + sinkStdIP net.IP + sinkAddrPort AddrPort + sinkPrefix Prefix + sinkPrefixSlice []Prefix + sinkInternValue *intern.Value + sinkIP16 [16]byte + sinkIP4 [4]byte + sinkBool bool + sinkString string + sinkBytes []byte + sinkUDPAddr = &net.UDPAddr{IP: make(net.IP, 0, 16)} +) + +func TestNoAllocs(t *testing.T) { + // Wrappers that panic on error, to prove that our alloc-free + // methods are returning successfully. + panicIP := func(ip Addr, err error) Addr { + if err != nil { + panic(err) + } + return ip + } + panicPfx := func(pfx Prefix, err error) Prefix { + if err != nil { + panic(err) + } + return pfx + } + panicIPP := func(ipp AddrPort, err error) AddrPort { + if err != nil { + panic(err) + } + return ipp + } + test := func(name string, f func()) { + t.Run(name, func(t *testing.T) { + n := testing.AllocsPerRun(1000, f) + if n != 0 { + t.Fatalf("allocs = %d; want 0", int(n)) + } + }) + } + + // IP constructors + test("IPv4", func() { sinkIP = IPv4(1, 2, 3, 4) }) + test("AddrFrom4", func() { sinkIP = AddrFrom4([4]byte{1, 2, 3, 4}) }) + test("AddrFrom16", func() { sinkIP = AddrFrom16([16]byte{}) }) + test("ParseAddr/4", func() { sinkIP = panicIP(ParseAddr("1.2.3.4")) }) + test("ParseAddr/6", func() { sinkIP = panicIP(ParseAddr("::1")) }) + test("MustParseAddr", func() { sinkIP = MustParseAddr("1.2.3.4") }) + test("IPv6LinkLocalAllNodes", func() { sinkIP = IPv6LinkLocalAllNodes() }) + test("IPv6Unspecified", func() { sinkIP = IPv6Unspecified() }) + + // IP methods + test("IP.IsZero", func() { sinkBool = MustParseAddr("1.2.3.4").IsZero() }) + test("IP.BitLen", func() { sinkBool = MustParseAddr("1.2.3.4").BitLen() == 8 }) + test("IP.Zone/4", func() { sinkBool = MustParseAddr("1.2.3.4").Zone() == "" }) + test("IP.Zone/6", func() { sinkBool = MustParseAddr("fe80::1").Zone() == "" }) + test("IP.Zone/6zone", func() { sinkBool = MustParseAddr("fe80::1%zone").Zone() == "" }) + test("IP.Compare", func() { + a := MustParseAddr("1.2.3.4") + b := MustParseAddr("2.3.4.5") + sinkBool = a.Compare(b) == 0 + }) + test("IP.Less", func() { + a := MustParseAddr("1.2.3.4") + b := MustParseAddr("2.3.4.5") + sinkBool = a.Less(b) + }) + test("IP.Is4", func() { sinkBool = MustParseAddr("1.2.3.4").Is4() }) + test("IP.Is6", func() { sinkBool = MustParseAddr("fe80::1").Is6() }) + test("IP.Is4In6", func() { sinkBool = MustParseAddr("fe80::1").Is4In6() }) + test("IP.Unmap", func() { sinkIP = MustParseAddr("ffff::2.3.4.5").Unmap() }) + test("IP.WithZone", func() { sinkIP = MustParseAddr("fe80::1").WithZone("") }) + test("IP.IsGlobalUnicast", func() { sinkBool = MustParseAddr("2001:db8::1").IsGlobalUnicast() }) + test("IP.IsInterfaceLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsInterfaceLocalMulticast() }) + test("IP.IsLinkLocalMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalMulticast() }) + test("IP.IsLinkLocalUnicast", func() { sinkBool = MustParseAddr("fe80::1").IsLinkLocalUnicast() }) + test("IP.IsLoopback", func() { sinkBool = MustParseAddr("fe80::1").IsLoopback() }) + test("IP.IsMulticast", func() { sinkBool = MustParseAddr("fe80::1").IsMulticast() }) + test("IP.IsPrivate", func() { sinkBool = MustParseAddr("fd00::1").IsPrivate() }) + test("IP.IsUnspecified", func() { sinkBool = IPv6Unspecified().IsUnspecified() }) + test("IP.Prefix/4", func() { sinkPrefix = panicPfx(MustParseAddr("1.2.3.4").Prefix(20)) }) + test("IP.Prefix/6", func() { sinkPrefix = panicPfx(MustParseAddr("fe80::1").Prefix(64)) }) + test("IP.As16", func() { sinkIP16 = MustParseAddr("1.2.3.4").As16() }) + test("IP.As4", func() { sinkIP4 = MustParseAddr("1.2.3.4").As4() }) + test("IP.Next", func() { sinkIP = MustParseAddr("1.2.3.4").Next() }) + test("IP.Prev", func() { sinkIP = MustParseAddr("1.2.3.4").Prev() }) + + // AddrPort constructors + test("AddrPortFrom", func() { sinkAddrPort = AddrPortFrom(IPv4(1, 2, 3, 4), 22) }) + test("ParseAddrPort", func() { sinkAddrPort = panicIPP(ParseAddrPort("[::1]:1234")) }) + test("MustParseAddrPort", func() { sinkAddrPort = MustParseAddrPort("[::1]:1234") }) + + // Prefix constructors + test("PrefixFrom", func() { sinkPrefix = PrefixFrom(IPv4(1, 2, 3, 4), 32) }) + test("ParsePrefix/4", func() { sinkPrefix = panicPfx(ParsePrefix("1.2.3.4/20")) }) + test("ParsePrefix/6", func() { sinkPrefix = panicPfx(ParsePrefix("fe80::1/64")) }) + test("MustParsePrefix", func() { sinkPrefix = MustParsePrefix("1.2.3.4/20") }) + + // Prefix methods + test("Prefix.Contains", func() { sinkBool = MustParsePrefix("1.2.3.0/24").Contains(MustParseAddr("1.2.3.4")) }) + test("Prefix.Overlaps", func() { + a, b := MustParsePrefix("1.2.3.0/24"), MustParsePrefix("1.2.0.0/16") + sinkBool = a.Overlaps(b) + }) + test("Prefix.IsZero", func() { sinkBool = MustParsePrefix("1.2.0.0/16").IsZero() }) + test("Prefix.IsSingleIP", func() { sinkBool = MustParsePrefix("1.2.3.4/32").IsSingleIP() }) + test("IPPRefix.Masked", func() { sinkPrefix = MustParsePrefix("1.2.3.4/16").Masked() }) +} + +func TestAddrStringAllocs(t *testing.T) { + tests := []struct { + name string + ip Addr + wantAllocs int + }{ + {"zero", Addr{}, 0}, + {"ipv4", MustParseAddr("192.168.1.1"), 1}, + {"ipv6", MustParseAddr("2001:db8::1"), 1}, + {"ipv6+zone", MustParseAddr("2001:db8::1%eth0"), 1}, + {"ipv4-in-ipv6", MustParseAddr("::ffff:192.168.1.1"), 1}, + {"ipv4-in-ipv6+zone", MustParseAddr("::ffff:192.168.1.1%eth0"), 1}, + } + optimizationOff := testenv.OptimizationOff() + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if optimizationOff && strings.HasPrefix(tc.name, "ipv4-in-ipv6") { + // Optimizations are required to remove some allocs. + t.Skipf("skipping on %v", testenv.Builder()) + } + allocs := int(testing.AllocsPerRun(1000, func() { + sinkString = tc.ip.String() + })) + if allocs != tc.wantAllocs { + t.Errorf("allocs=%d, want %d", allocs, tc.wantAllocs) + } + }) + } +} + +func TestPrefixString(t *testing.T) { + tests := []struct { + ipp Prefix + want string + }{ + {Prefix{}, "invalid Prefix"}, + {PrefixFrom(Addr{}, 8), "invalid Prefix"}, + {PrefixFrom(MustParseAddr("1.2.3.4"), 88), "invalid Prefix"}, + } + + for _, tt := range tests { + if got := tt.ipp.String(); got != tt.want { + t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want) + } + } +} + +func TestInvalidAddrPortString(t *testing.T) { + tests := []struct { + ipp AddrPort + want string + }{ + {AddrPort{}, "invalid AddrPort"}, + {AddrPortFrom(Addr{}, 80), "invalid AddrPort"}, + } + + for _, tt := range tests { + if got := tt.ipp.String(); got != tt.want { + t.Errorf("(%#v).String() = %q want %q", tt.ipp, got, tt.want) + } + } +} + +func TestAsSlice(t *testing.T) { + tests := []struct { + in Addr + want []byte + }{ + {in: Addr{}, want: nil}, + {in: mustIP("1.2.3.4"), want: []byte{1, 2, 3, 4}}, + {in: mustIP("ffff::1"), want: []byte{0xff, 0xff, 15: 1}}, + } + + for _, test := range tests { + got := test.in.AsSlice() + if !bytes.Equal(got, test.want) { + t.Errorf("%v.AsSlice() = %v want %v", test.in, got, test.want) + } + } +} + +var sink16 [16]byte + +func BenchmarkAs16(b *testing.B) { + addr := MustParseAddr("1::10") + for i := 0; i < b.N; i++ { + sink16 = addr.As16() + } +} diff --git a/src/net/netip/slow_test.go b/src/net/netip/slow_test.go new file mode 100644 index 00000000000000..d7c802516416da --- /dev/null +++ b/src/net/netip/slow_test.go @@ -0,0 +1,190 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip_test + +import ( + "fmt" + . "net/netip" + "strconv" + "strings" +) + +// zeros is a slice of eight stringified zeros. It's used in +// parseIPSlow to construct slices of specific amounts of zero fields, +// from 1 to 8. +var zeros = []string{"0", "0", "0", "0", "0", "0", "0", "0"} + +// parseIPSlow is like ParseIP, but aims for readability above +// speed. It's the reference implementation for correctness checking +// and against which we measure optimized parsers. +// +// parseIPSlow understands the following forms of IP addresses: +// - Regular IPv4: 1.2.3.4 +// - IPv4 with many leading zeros: 0000001.0000002.0000003.0000004 +// - Regular IPv6: 1111:2222:3333:4444:5555:6666:7777:8888 +// - IPv6 with many leading zeros: 00000001:0000002:0000003:0000004:0000005:0000006:0000007:0000008 +// - IPv6 with zero blocks elided: 1111:2222::7777:8888 +// - IPv6 with trailing 32 bits expressed as IPv4: 1111:2222:3333:4444:5555:6666:77.77.88.88 +// +// It does not process the following IP address forms, which have been +// varyingly accepted by some programs due to an under-specification +// of the shapes of IPv4 addresses: +// +// - IPv4 as a single 32-bit uint: 4660 (same as "1.2.3.4") +// - IPv4 with octal numbers: 0300.0250.0.01 (same as "192.168.0.1") +// - IPv4 with hex numbers: 0xc0.0xa8.0x0.0x1 (same as "192.168.0.1") +// - IPv4 in "class-B style": 1.2.52 (same as "1.2.3.4") +// - IPv4 in "class-A style": 1.564 (same as "1.2.3.4") +func parseIPSlow(s string) (Addr, error) { + // Identify and strip out the zone, if any. There should be 0 or 1 + // '%' in the string. + var zone string + fs := strings.Split(s, "%") + switch len(fs) { + case 1: + // No zone, that's fine. + case 2: + s, zone = fs[0], fs[1] + if zone == "" { + return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): no zone after zone specifier", s) + } + default: + return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): too many zone specifiers", s) // TODO: less specific? + } + + // IPv4 by itself is easy to do in a helper. + if strings.Count(s, ":") == 0 { + if zone != "" { + return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): IPv4 addresses cannot have a zone", s) + } + return parseIPv4Slow(s) + } + + normal, err := normalizeIPv6Slow(s) + if err != nil { + return Addr{}, err + } + + // At this point, we've normalized the address back into 8 hex + // fields of 16 bits each. Parse that. + fs = strings.Split(normal, ":") + if len(fs) != 8 { + return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): wrong size address", s) + } + var ret [16]byte + for i, f := range fs { + a, b, err := parseWord(f) + if err != nil { + return Addr{}, err + } + ret[i*2] = a + ret[i*2+1] = b + } + + return AddrFrom16(ret).WithZone(zone), nil +} + +// normalizeIPv6Slow expands s, which is assumed to be an IPv6 +// address, to its canonical text form. +// +// The canonical form of an IPv6 address is 8 colon-separated fields, +// where each field should be a hex value from 0 to ffff. This +// function does not verify the contents of each field. +// +// This function performs two transformations: +// - The last 32 bits of an IPv6 address may be represented in +// IPv4-style dotted quad form, as in 1:2:3:4:5:6:7.8.9.10. That +// address is transformed to its hex equivalent, +// e.g. 1:2:3:4:5:6:708:90a. +// - An address may contain one "::", which expands into as many +// 16-bit blocks of zeros as needed to make the address its correct +// full size. For example, fe80::1:2 expands to fe80:0:0:0:0:0:1:2. +// +// Both short forms may be present in a single address, +// e.g. fe80::1.2.3.4. +func normalizeIPv6Slow(orig string) (string, error) { + s := orig + + // Find and convert an IPv4 address in the final field, if any. + i := strings.LastIndex(s, ":") + if i == -1 { + return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig) + } + if strings.Contains(s[i+1:], ".") { + ip, err := parseIPv4Slow(s[i+1:]) + if err != nil { + return "", err + } + a4 := ip.As4() + s = fmt.Sprintf("%s:%02x%02x:%02x%02x", s[:i], a4[0], a4[1], a4[2], a4[3]) + } + + // Find and expand a ::, if any. + fs := strings.Split(s, "::") + switch len(fs) { + case 1: + // No ::, nothing to do. + case 2: + lhs, rhs := fs[0], fs[1] + // Found a ::, figure out how many zero blocks need to be + // inserted. + nblocks := strings.Count(lhs, ":") + strings.Count(rhs, ":") + if lhs != "" { + nblocks++ + } + if rhs != "" { + nblocks++ + } + if nblocks > 7 { + return "", fmt.Errorf("netaddr.ParseIP(%q): address too long", orig) + } + fs = nil + // Either side of the :: can be empty. We don't want empty + // fields to feature in the final normalized address. + if lhs != "" { + fs = append(fs, lhs) + } + fs = append(fs, zeros[:8-nblocks]...) + if rhs != "" { + fs = append(fs, rhs) + } + s = strings.Join(fs, ":") + default: + // Too many :: + return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig) + } + + return s, nil +} + +// parseIPv4Slow parses and returns an IPv4 address in dotted quad +// form, e.g. "192.168.0.1". It is slow but easy to read, and the +// reference implementation against which we compare faster +// implementations for correctness. +func parseIPv4Slow(s string) (Addr, error) { + fs := strings.Split(s, ".") + if len(fs) != 4 { + return Addr{}, fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", s) + } + var ret [4]byte + for i := range ret { + val, err := strconv.ParseUint(fs[i], 10, 8) + if err != nil { + return Addr{}, err + } + ret[i] = uint8(val) + } + return AddrFrom4([4]byte{ret[0], ret[1], ret[2], ret[3]}), nil +} + +// parseWord converts a 16-bit hex string into its corresponding +// two-byte value. +func parseWord(s string) (byte, byte, error) { + ret, err := strconv.ParseUint(s, 16, 16) + if err != nil { + return 0, 0, err + } + return uint8(ret >> 8), uint8(ret), nil +} diff --git a/src/net/netip/uint128.go b/src/net/netip/uint128.go new file mode 100644 index 00000000000000..738939d7de165e --- /dev/null +++ b/src/net/netip/uint128.go @@ -0,0 +1,92 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip + +import "math/bits" + +// uint128 represents a uint128 using two uint64s. +// +// When the methods below mention a bit number, bit 0 is the most +// significant bit (in hi) and bit 127 is the lowest (lo&1). +type uint128 struct { + hi uint64 + lo uint64 +} + +// mask6 returns a uint128 bitmask with the topmost n bits of a +// 128-bit number. +func mask6(n int) uint128 { + return uint128{^(^uint64(0) >> n), ^uint64(0) << (128 - n)} +} + +// isZero reports whether u == 0. +// +// It's faster than u == (uint128{}) because the compiler (as of Go +// 1.15/1.16b1) doesn't do this trick and instead inserts a branch in +// its eq alg's generated code. +func (u uint128) isZero() bool { return u.hi|u.lo == 0 } + +// and returns the bitwise AND of u and m (u&m). +func (u uint128) and(m uint128) uint128 { + return uint128{u.hi & m.hi, u.lo & m.lo} +} + +// xor returns the bitwise XOR of u and m (u^m). +func (u uint128) xor(m uint128) uint128 { + return uint128{u.hi ^ m.hi, u.lo ^ m.lo} +} + +// or returns the bitwise OR of u and m (u|m). +func (u uint128) or(m uint128) uint128 { + return uint128{u.hi | m.hi, u.lo | m.lo} +} + +// not returns the bitwise NOT of u. +func (u uint128) not() uint128 { + return uint128{^u.hi, ^u.lo} +} + +// subOne returns u - 1. +func (u uint128) subOne() uint128 { + lo, borrow := bits.Sub64(u.lo, 1, 0) + return uint128{u.hi - borrow, lo} +} + +// addOne returns u + 1. +func (u uint128) addOne() uint128 { + lo, carry := bits.Add64(u.lo, 1, 0) + return uint128{u.hi + carry, lo} +} + +func u64CommonPrefixLen(a, b uint64) uint8 { + return uint8(bits.LeadingZeros64(a ^ b)) +} + +func (u uint128) commonPrefixLen(v uint128) (n uint8) { + if n = u64CommonPrefixLen(u.hi, v.hi); n == 64 { + n += u64CommonPrefixLen(u.lo, v.lo) + } + return +} + +// halves returns the two uint64 halves of the uint128. +// +// Logically, think of it as returning two uint64s. +// It only returns pointers for inlining reasons on 32-bit platforms. +func (u *uint128) halves() [2]*uint64 { + return [2]*uint64{&u.hi, &u.lo} +} + +// bitsSetFrom returns a copy of u with the given bit +// and all subsequent ones set. +func (u uint128) bitsSetFrom(bit uint8) uint128 { + return u.or(mask6(int(bit)).not()) +} + +// bitsClearedFrom returns a copy of u with the given bit +// and all subsequent ones cleared. +func (u uint128) bitsClearedFrom(bit uint8) uint128 { + return u.and(mask6(int(bit))) +} diff --git a/src/net/netip/uint128_test.go b/src/net/netip/uint128_test.go new file mode 100644 index 00000000000000..dd1ae0ec798813 --- /dev/null +++ b/src/net/netip/uint128_test.go @@ -0,0 +1,89 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package netip + +import ( + "testing" +) + +func TestUint128AddSub(t *testing.T) { + const add1 = 1 + const sub1 = -1 + tests := []struct { + in uint128 + op int // +1 or -1 to add vs subtract + want uint128 + }{ + {uint128{0, 0}, add1, uint128{0, 1}}, + {uint128{0, 1}, add1, uint128{0, 2}}, + {uint128{1, 0}, add1, uint128{1, 1}}, + {uint128{0, ^uint64(0)}, add1, uint128{1, 0}}, + {uint128{^uint64(0), ^uint64(0)}, add1, uint128{0, 0}}, + + {uint128{0, 0}, sub1, uint128{^uint64(0), ^uint64(0)}}, + {uint128{0, 1}, sub1, uint128{0, 0}}, + {uint128{0, 2}, sub1, uint128{0, 1}}, + {uint128{1, 0}, sub1, uint128{0, ^uint64(0)}}, + {uint128{1, 1}, sub1, uint128{1, 0}}, + } + for _, tt := range tests { + var got uint128 + switch tt.op { + case add1: + got = tt.in.addOne() + case sub1: + got = tt.in.subOne() + default: + panic("bogus op") + } + if got != tt.want { + t.Errorf("%v add %d = %v; want %v", tt.in, tt.op, got, tt.want) + } + } +} + +func TestBitsSetFrom(t *testing.T) { + tests := []struct { + bit uint8 + want uint128 + }{ + {0, uint128{^uint64(0), ^uint64(0)}}, + {1, uint128{^uint64(0) >> 1, ^uint64(0)}}, + {63, uint128{1, ^uint64(0)}}, + {64, uint128{0, ^uint64(0)}}, + {65, uint128{0, ^uint64(0) >> 1}}, + {127, uint128{0, 1}}, + {128, uint128{0, 0}}, + } + for _, tt := range tests { + var zero uint128 + got := zero.bitsSetFrom(tt.bit) + if got != tt.want { + t.Errorf("0.bitsSetFrom(%d) = %064b want %064b", tt.bit, got, tt.want) + } + } +} + +func TestBitsClearedFrom(t *testing.T) { + tests := []struct { + bit uint8 + want uint128 + }{ + {0, uint128{0, 0}}, + {1, uint128{1 << 63, 0}}, + {63, uint128{^uint64(0) &^ 1, 0}}, + {64, uint128{^uint64(0), 0}}, + {65, uint128{^uint64(0), 1 << 63}}, + {127, uint128{^uint64(0), ^uint64(0) &^ 1}}, + {128, uint128{^uint64(0), ^uint64(0)}}, + } + for _, tt := range tests { + ones := uint128{^uint64(0), ^uint64(0)} + got := ones.bitsClearedFrom(tt.bit) + if got != tt.want { + t.Errorf("ones.bitsClearedFrom(%d) = %064b want %064b", tt.bit, got, tt.want) + } + } +} diff --git a/src/net/nss.go b/src/net/nss.go index 85177cab9b92da..c4c608fb618647 100644 --- a/src/net/nss.go +++ b/src/net/nss.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris - package net import ( diff --git a/src/net/nss_test.go b/src/net/nss_test.go index 4b73886c5119c4..c9ccc60cb78722 100644 --- a/src/net/nss_test.go +++ b/src/net/nss_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/packetconn_test.go b/src/net/packetconn_test.go index aeb9845fa77b8f..fa160df5f50a1d 100644 --- a/src/net/packetconn_test.go +++ b/src/net/packetconn_test.go @@ -6,14 +6,12 @@ // tag. //go:build !js -// +build !js package net import ( "os" "testing" - "time" ) // The full stack test cases for IPConn have been moved to the @@ -29,16 +27,16 @@ func packetConnTestData(t *testing.T, network string) ([]byte, func()) { return []byte("PACKETCONN TEST"), nil } -var packetConnTests = []struct { - net string - addr1 string - addr2 string -}{ - {"udp", "127.0.0.1:0", "127.0.0.1:0"}, - {"unixgram", testUnixAddr(), testUnixAddr()}, -} - func TestPacketConn(t *testing.T) { + var packetConnTests = []struct { + net string + addr1 string + addr2 string + }{ + {"udp", "127.0.0.1:0", "127.0.0.1:0"}, + {"unixgram", testUnixAddr(t), testUnixAddr(t)}, + } + closer := func(c PacketConn, net, addr1, addr2 string) { c.Close() switch net { @@ -61,9 +59,6 @@ func TestPacketConn(t *testing.T) { } defer closer(c1, tt.net, tt.addr1, tt.addr2) c1.LocalAddr() - c1.SetDeadline(time.Now().Add(500 * time.Millisecond)) - c1.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - c1.SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) c2, err := ListenPacket(tt.net, tt.addr2) if err != nil { @@ -71,9 +66,6 @@ func TestPacketConn(t *testing.T) { } defer closer(c2, tt.net, tt.addr1, tt.addr2) c2.LocalAddr() - c2.SetDeadline(time.Now().Add(500 * time.Millisecond)) - c2.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - c2.SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) rb2 := make([]byte, 128) if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil { @@ -93,6 +85,15 @@ func TestPacketConn(t *testing.T) { } func TestConnAndPacketConn(t *testing.T) { + var packetConnTests = []struct { + net string + addr1 string + addr2 string + }{ + {"udp", "127.0.0.1:0", "127.0.0.1:0"}, + {"unixgram", testUnixAddr(t), testUnixAddr(t)}, + } + closer := func(c PacketConn, net, addr1, addr2 string) { c.Close() switch net { @@ -116,9 +117,6 @@ func TestConnAndPacketConn(t *testing.T) { } defer closer(c1, tt.net, tt.addr1, tt.addr2) c1.LocalAddr() - c1.SetDeadline(time.Now().Add(500 * time.Millisecond)) - c1.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - c1.SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) c2, err := Dial(tt.net, c1.LocalAddr().String()) if err != nil { @@ -127,9 +125,6 @@ func TestConnAndPacketConn(t *testing.T) { defer c2.Close() c2.LocalAddr() c2.RemoteAddr() - c2.SetDeadline(time.Now().Add(500 * time.Millisecond)) - c2.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - c2.SetWriteDeadline(time.Now().Add(500 * time.Millisecond)) if _, err := c2.Write(wb); err != nil { t.Fatal(err) diff --git a/src/net/parse.go b/src/net/parse.go index 6c230ab63fa0c7..ee2890fe2ce5b3 100644 --- a/src/net/parse.go +++ b/src/net/parse.go @@ -208,6 +208,16 @@ func last(s string, b byte) int { return i } +// hasUpperCase tells whether the given string contains at least one upper-case. +func hasUpperCase(s string) bool { + for i := range s { + if 'A' <= s[i] && s[i] <= 'Z' { + return true + } + } + return false +} + // lowerASCIIBytes makes x ASCII lowercase in-place. func lowerASCIIBytes(x []byte) { for i, b := range x { @@ -331,26 +341,3 @@ func readFull(r io.Reader) (all []byte, err error) { } } } - -// goDebugString returns the value of the named GODEBUG key. -// GODEBUG is of the form "key=val,key2=val2" -func goDebugString(key string) string { - s := os.Getenv("GODEBUG") - for i := 0; i < len(s)-len(key)-1; i++ { - if i > 0 && s[i-1] != ',' { - continue - } - afterKey := s[i+len(key):] - if afterKey[0] != '=' || s[i:i+len(key)] != key { - continue - } - val := afterKey[1:] - for i, b := range val { - if b == ',' { - return val[:i] - } - } - return val - } - return "" -} diff --git a/src/net/parse_test.go b/src/net/parse_test.go index c5f8bfd198ce4f..97716d769a9b49 100644 --- a/src/net/parse_test.go +++ b/src/net/parse_test.go @@ -51,33 +51,6 @@ func TestReadLine(t *testing.T) { } } -func TestGoDebugString(t *testing.T) { - defer os.Setenv("GODEBUG", os.Getenv("GODEBUG")) - tests := []struct { - godebug string - key string - want string - }{ - {"", "foo", ""}, - {"foo=", "foo", ""}, - {"foo=bar", "foo", "bar"}, - {"foo=bar,", "foo", "bar"}, - {"foo,foo=bar,", "foo", "bar"}, - {"foo1=bar,foo=bar,", "foo", "bar"}, - {"foo=bar,foo=bar,", "foo", "bar"}, - {"foo=", "foo", ""}, - {"foo", "foo", ""}, - {",foo", "foo", ""}, - {"foo=bar,baz", "loooooooong", ""}, - } - for _, tt := range tests { - os.Setenv("GODEBUG", tt.godebug) - if got := goDebugString(tt.key); got != tt.want { - t.Errorf("for %q, goDebugString(%q) = %q; want %q", tt.godebug, tt.key, got, tt.want) - } - } -} - func TestDtoi(t *testing.T) { for _, tt := range []struct { in string diff --git a/src/net/platform_test.go b/src/net/platform_test.go index 2da23dedcea674..c522ba282915b0 100644 --- a/src/net/platform_test.go +++ b/src/net/platform_test.go @@ -34,8 +34,8 @@ func init() { // testableNetwork reports whether network is testable on the current // platform configuration. func testableNetwork(network string) bool { - ss := strings.Split(network, ":") - switch ss[0] { + net, _, _ := strings.Cut(network, ":") + switch net { case "ip+nopriv": case "ip", "ip4", "ip6": switch runtime.GOOS { @@ -68,7 +68,7 @@ func testableNetwork(network string) bool { } } } - switch ss[0] { + switch net { case "tcp4", "udp4", "ip4": if !supportsIPv4() { return false @@ -88,7 +88,7 @@ func iOS() bool { // testableAddress reports whether address of network is testable on // the current platform configuration. func testableAddress(network, address string) bool { - switch ss := strings.Split(network, ":"); ss[0] { + switch net, _, _ := strings.Cut(network, ":"); net { case "unix", "unixgram", "unixpacket": // Abstract unix domain sockets, a Linux-ism. if address[0] == '@' && runtime.GOOS != "linux" { @@ -107,7 +107,7 @@ func testableListenArgs(network, address, client string) bool { var err error var addr Addr - switch ss := strings.Split(network, ":"); ss[0] { + switch net, _, _ := strings.Cut(network, ":"); net { case "tcp", "tcp4", "tcp6": addr, err = ResolveTCPAddr("tcp", address) case "udp", "udp4", "udp6": @@ -173,7 +173,7 @@ func testableListenArgs(network, address, client string) bool { return true } -func condFatalf(t *testing.T, network string, format string, args ...interface{}) { +func condFatalf(t *testing.T, network string, format string, args ...any) { t.Helper() // A few APIs like File and Read/WriteMsg{UDP,IP} are not // fully implemented yet on Plan 9 and Windows. diff --git a/src/net/port_unix.go b/src/net/port_unix.go index a9a96a2323b80d..b05b588f17dd3c 100644 --- a/src/net/port_unix.go +++ b/src/net/port_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) // Read system port mappings from /etc/services diff --git a/src/net/protoconn_test.go b/src/net/protoconn_test.go index fc9b3862563e9a..e4198a3a051a6a 100644 --- a/src/net/protoconn_test.go +++ b/src/net/protoconn_test.go @@ -6,7 +6,6 @@ // tag. //go:build !js -// +build !js package net @@ -74,10 +73,7 @@ func TestTCPConnSpecificMethods(t *testing.T) { } ch := make(chan error, 1) handler := func(ls *localServer, ln Listener) { ls.transponder(ls.Listener, ch) } - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -208,7 +204,7 @@ func TestUnixListenerSpecificMethods(t *testing.T) { t.Skip("unix test") } - addr := testUnixAddr() + addr := testUnixAddr(t) la, err := ResolveUnixAddr("unix", addr) if err != nil { t.Fatal(err) @@ -249,7 +245,7 @@ func TestUnixConnSpecificMethods(t *testing.T) { t.Skip("unixgram test") } - addr1, addr2, addr3 := testUnixAddr(), testUnixAddr(), testUnixAddr() + addr1, addr2, addr3 := testUnixAddr(t), testUnixAddr(t), testUnixAddr(t) a1, err := ResolveUnixAddr("unixgram", addr1) if err != nil { diff --git a/src/net/rawconn_stub_test.go b/src/net/rawconn_stub_test.go index 975aa8d9569162..ff3d829893d06e 100644 --- a/src/net/rawconn_stub_test.go +++ b/src/net/rawconn_stub_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (js && wasm) || plan9 -// +build js,wasm plan9 package net diff --git a/src/net/rawconn_test.go b/src/net/rawconn_test.go index 3ef7af33b7330e..d1ef79d7157a3b 100644 --- a/src/net/rawconn_test.go +++ b/src/net/rawconn_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -65,10 +64,7 @@ func TestRawConnReadWrite(t *testing.T) { return } } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -103,10 +99,7 @@ func TestRawConnReadWrite(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -181,10 +174,7 @@ func TestRawConnControl(t *testing.T) { } t.Run("TCP", func(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() cc1, err := ln.(*TCPListener).SyscallConn() diff --git a/src/net/rawconn_unix_test.go b/src/net/rawconn_unix_test.go index 75bbab8b27eeb4..f11119ed9ec0da 100644 --- a/src/net/rawconn_unix_test.go +++ b/src/net/rawconn_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net diff --git a/src/net/resolverdialfunc_test.go b/src/net/resolverdialfunc_test.go new file mode 100644 index 00000000000000..034c636eb6937c --- /dev/null +++ b/src/net/resolverdialfunc_test.go @@ -0,0 +1,328 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !js + +// Test that Resolver.Dial can be a func returning an in-memory net.Conn +// speaking DNS. + +package net + +import ( + "bytes" + "context" + "errors" + "fmt" + "reflect" + "sort" + "testing" + "time" + + "golang.org/x/net/dns/dnsmessage" +) + +func TestResolverDialFunc(t *testing.T) { + r := &Resolver{ + PreferGo: true, + Dial: newResolverDialFunc(&resolverDialHandler{ + StartDial: func(network, address string) error { + t.Logf("StartDial(%q, %q) ...", network, address) + return nil + }, + Question: func(h dnsmessage.Header, q dnsmessage.Question) { + t.Logf("Header: %+v for %q (type=%v, class=%v)", h, + q.Name.String(), q.Type, q.Class) + }, + // TODO: add test without HandleA* hooks specified at all, that Go + // doesn't issue retries; map to something terminal. + HandleA: func(w AWriter, name string) error { + w.AddIP([4]byte{1, 2, 3, 4}) + w.AddIP([4]byte{5, 6, 7, 8}) + return nil + }, + HandleAAAA: func(w AAAAWriter, name string) error { + w.AddIP([16]byte{1: 1, 15: 15}) + w.AddIP([16]byte{2: 2, 14: 14}) + return nil + }, + HandleSRV: func(w SRVWriter, name string) error { + w.AddSRV(1, 2, 80, "foo.bar.") + w.AddSRV(2, 3, 81, "bar.baz.") + return nil + }, + }), + } + ctx := context.Background() + const fakeDomain = "something-that-is-a-not-a-real-domain.fake-tld." + + t.Run("LookupIP", func(t *testing.T) { + ips, err := r.LookupIP(ctx, "ip", fakeDomain) + if err != nil { + t.Fatal(err) + } + if got, want := sortedIPStrings(ips), []string{"0:200::e00", "1.2.3.4", "1::f", "5.6.7.8"}; !reflect.DeepEqual(got, want) { + t.Errorf("LookupIP wrong.\n got: %q\nwant: %q\n", got, want) + } + }) + + t.Run("LookupSRV", func(t *testing.T) { + _, got, err := r.LookupSRV(ctx, "some-service", "tcp", fakeDomain) + if err != nil { + t.Fatal(err) + } + want := []*SRV{ + { + Target: "foo.bar.", + Port: 80, + Priority: 1, + Weight: 2, + }, + { + Target: "bar.baz.", + Port: 81, + Priority: 2, + Weight: 3, + }, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("wrong result. got:") + for _, r := range got { + t.Logf(" - %+v", r) + } + } + }) +} + +func sortedIPStrings(ips []IP) []string { + ret := make([]string, len(ips)) + for i, ip := range ips { + ret[i] = ip.String() + } + sort.Strings(ret) + return ret +} + +func newResolverDialFunc(h *resolverDialHandler) func(ctx context.Context, network, address string) (Conn, error) { + return func(ctx context.Context, network, address string) (Conn, error) { + a := &resolverFuncConn{ + h: h, + network: network, + address: address, + ttl: 10, // 10 second default if unset + } + if h.StartDial != nil { + if err := h.StartDial(network, address); err != nil { + return nil, err + } + } + return a, nil + } +} + +type resolverDialHandler struct { + // StartDial, if non-nil, is called when Go first calls Resolver.Dial. + // Any error returned aborts the dial and is returned unwrapped. + StartDial func(network, address string) error + + Question func(dnsmessage.Header, dnsmessage.Question) + + // err may be ErrNotExist or ErrRefused; others map to SERVFAIL (RCode2). + // A nil error means success. + HandleA func(w AWriter, name string) error + HandleAAAA func(w AAAAWriter, name string) error + HandleSRV func(w SRVWriter, name string) error +} + +type ResponseWriter struct{ a *resolverFuncConn } + +func (w ResponseWriter) header() dnsmessage.ResourceHeader { + q := w.a.q + return dnsmessage.ResourceHeader{ + Name: q.Name, + Type: q.Type, + Class: q.Class, + TTL: w.a.ttl, + } +} + +// SetTTL sets the TTL for subsequent written resources. +// Once a resource has been written, SetTTL calls are no-ops. +// That is, it can only be called at most once, before anything +// else is written. +func (w ResponseWriter) SetTTL(seconds uint32) { + // ... intention is last one wins and mutates all previously + // written records too, but that's a little annoying. + // But it's also annoying if the requirement is it needs to be set + // last. + // And it's also annoying if it's possible for users to set + // different TTLs per Answer. + if w.a.wrote { + return + } + w.a.ttl = seconds + +} + +type AWriter struct{ ResponseWriter } + +func (w AWriter) AddIP(v4 [4]byte) { + w.a.wrote = true + err := w.a.builder.AResource(w.header(), dnsmessage.AResource{A: v4}) + if err != nil { + panic(err) + } +} + +type AAAAWriter struct{ ResponseWriter } + +func (w AAAAWriter) AddIP(v6 [16]byte) { + w.a.wrote = true + err := w.a.builder.AAAAResource(w.header(), dnsmessage.AAAAResource{AAAA: v6}) + if err != nil { + panic(err) + } +} + +type SRVWriter struct{ ResponseWriter } + +// AddSRV adds a SRV record. The target name must end in a period and +// be 63 bytes or fewer. +func (w SRVWriter) AddSRV(priority, weight, port uint16, target string) error { + targetName, err := dnsmessage.NewName(target) + if err != nil { + return err + } + w.a.wrote = true + err = w.a.builder.SRVResource(w.header(), dnsmessage.SRVResource{ + Priority: priority, + Weight: weight, + Port: port, + Target: targetName, + }) + if err != nil { + panic(err) // internal fault, not user + } + return nil +} + +var ( + ErrNotExist = errors.New("name does not exist") // maps to RCode3, NXDOMAIN + ErrRefused = errors.New("refused") // maps to RCode5, REFUSED +) + +type resolverFuncConn struct { + h *resolverDialHandler + ctx context.Context + network string + address string + builder *dnsmessage.Builder + q dnsmessage.Question + ttl uint32 + wrote bool + + rbuf bytes.Buffer +} + +func (*resolverFuncConn) Close() error { return nil } +func (*resolverFuncConn) LocalAddr() Addr { return someaddr{} } +func (*resolverFuncConn) RemoteAddr() Addr { return someaddr{} } +func (*resolverFuncConn) SetDeadline(t time.Time) error { return nil } +func (*resolverFuncConn) SetReadDeadline(t time.Time) error { return nil } +func (*resolverFuncConn) SetWriteDeadline(t time.Time) error { return nil } + +func (a *resolverFuncConn) Read(p []byte) (n int, err error) { + return a.rbuf.Read(p) +} + +func (a *resolverFuncConn) Write(packet []byte) (n int, err error) { + if len(packet) < 2 { + return 0, fmt.Errorf("short write of %d bytes; want 2+", len(packet)) + } + reqLen := int(packet[0])<<8 | int(packet[1]) + req := packet[2:] + if len(req) != reqLen { + return 0, fmt.Errorf("packet declared length %d doesn't match body length %d", reqLen, len(req)) + } + + var parser dnsmessage.Parser + h, err := parser.Start(req) + if err != nil { + // TODO: hook + return 0, err + } + q, err := parser.Question() + hadQ := (err == nil) + if err == nil && a.h.Question != nil { + a.h.Question(h, q) + } + if err != nil && err != dnsmessage.ErrSectionDone { + return 0, err + } + + resh := h + resh.Response = true + resh.Authoritative = true + if hadQ { + resh.RCode = dnsmessage.RCodeSuccess + } else { + resh.RCode = dnsmessage.RCodeNotImplemented + } + a.rbuf.Grow(514) + a.rbuf.WriteByte('X') // reserved header for beu16 length + a.rbuf.WriteByte('Y') // reserved header for beu16 length + builder := dnsmessage.NewBuilder(a.rbuf.Bytes(), resh) + a.builder = &builder + if hadQ { + a.q = q + a.builder.StartQuestions() + err := a.builder.Question(q) + if err != nil { + return 0, fmt.Errorf("Question: %w", err) + } + a.builder.StartAnswers() + switch q.Type { + case dnsmessage.TypeA: + if a.h.HandleA != nil { + resh.RCode = mapRCode(a.h.HandleA(AWriter{ResponseWriter{a}}, q.Name.String())) + } + case dnsmessage.TypeAAAA: + if a.h.HandleAAAA != nil { + resh.RCode = mapRCode(a.h.HandleAAAA(AAAAWriter{ResponseWriter{a}}, q.Name.String())) + } + case dnsmessage.TypeSRV: + if a.h.HandleSRV != nil { + resh.RCode = mapRCode(a.h.HandleSRV(SRVWriter{ResponseWriter{a}}, q.Name.String())) + } + } + } + tcpRes, err := builder.Finish() + if err != nil { + return 0, fmt.Errorf("Finish: %w", err) + } + + n = len(tcpRes) - 2 + tcpRes[0] = byte(n >> 8) + tcpRes[1] = byte(n) + a.rbuf.Write(tcpRes[2:]) + + return len(packet), nil +} + +type someaddr struct{} + +func (someaddr) Network() string { return "unused" } +func (someaddr) String() string { return "unused-someaddr" } + +func mapRCode(err error) dnsmessage.RCode { + switch err { + case nil: + return dnsmessage.RCodeSuccess + case ErrNotExist: + return dnsmessage.RCodeNameError + case ErrRefused: + return dnsmessage.RCodeRefused + default: + return dnsmessage.RCodeServerFailure + } +} diff --git a/src/net/rpc/client.go b/src/net/rpc/client.go index 60bb2cc99f99bf..42d13519b1907e 100644 --- a/src/net/rpc/client.go +++ b/src/net/rpc/client.go @@ -27,11 +27,11 @@ var ErrShutdown = errors.New("connection is shut down") // Call represents an active RPC. type Call struct { - ServiceMethod string // The name of the service and method to call. - Args interface{} // The argument to the function (*struct). - Reply interface{} // The reply from the function (*struct). - Error error // After completion, the error status. - Done chan *Call // Receives *Call when Go is complete. + ServiceMethod string // The name of the service and method to call. + Args any // The argument to the function (*struct). + Reply any // The reply from the function (*struct). + Error error // After completion, the error status. + Done chan *Call // Receives *Call when Go is complete. } // Client represents an RPC Client. @@ -61,9 +61,9 @@ type Client struct { // discarded. // See NewClient's comment for information about concurrent access. type ClientCodec interface { - WriteRequest(*Request, interface{}) error + WriteRequest(*Request, any) error ReadResponseHeader(*Response) error - ReadResponseBody(interface{}) error + ReadResponseBody(any) error Close() error } @@ -214,7 +214,7 @@ type gobClientCodec struct { encBuf *bufio.Writer } -func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err error) { +func (c *gobClientCodec) WriteRequest(r *Request, body any) (err error) { if err = c.enc.Encode(r); err != nil { return } @@ -228,7 +228,7 @@ func (c *gobClientCodec) ReadResponseHeader(r *Response) error { return c.dec.Decode(r) } -func (c *gobClientCodec) ReadResponseBody(body interface{}) error { +func (c *gobClientCodec) ReadResponseBody(body any) error { return c.dec.Decode(body) } @@ -295,7 +295,7 @@ func (client *Client) Close() error { // the invocation. The done channel will signal when the call is complete by returning // the same Call object. If done is nil, Go will allocate a new channel. // If non-nil, done must be buffered or Go will deliberately crash. -func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { +func (client *Client) Go(serviceMethod string, args any, reply any, done chan *Call) *Call { call := new(Call) call.ServiceMethod = serviceMethod call.Args = args @@ -317,7 +317,7 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface } // Call invokes the named function, waits for it to complete, and returns its error status. -func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error { +func (client *Client) Call(serviceMethod string, args any, reply any) error { call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done return call.Error } diff --git a/src/net/rpc/client_test.go b/src/net/rpc/client_test.go index 03225e3d0104b5..ffc12faedae9c9 100644 --- a/src/net/rpc/client_test.go +++ b/src/net/rpc/client_test.go @@ -17,8 +17,8 @@ type shutdownCodec struct { closed bool } -func (c *shutdownCodec) WriteRequest(*Request, interface{}) error { return nil } -func (c *shutdownCodec) ReadResponseBody(interface{}) error { return nil } +func (c *shutdownCodec) WriteRequest(*Request, any) error { return nil } +func (c *shutdownCodec) ReadResponseBody(any) error { return nil } func (c *shutdownCodec) ReadResponseHeader(*Response) error { c.responded <- 1 return errors.New("shutdownCodec ReadResponseHeader") @@ -57,8 +57,8 @@ func TestGobError(t *testing.T) { if err == nil { t.Fatal("no error") } - if !strings.Contains(err.(error).Error(), "reading body EOF") { - t.Fatal("expected `reading body EOF', got", err) + if !strings.Contains(err.(error).Error(), "reading body unexpected EOF") { + t.Fatal("expected `reading body unexpected EOF', got", err) } }() Register(new(S)) diff --git a/src/net/rpc/debug.go b/src/net/rpc/debug.go index a1d799ff19a142..9e499fd984de65 100644 --- a/src/net/rpc/debug.go +++ b/src/net/rpc/debug.go @@ -72,7 +72,7 @@ type debugHTTP struct { func (server debugHTTP) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Build a sorted version of the data. var services serviceArray - server.serviceMap.Range(func(snamei, svci interface{}) bool { + server.serviceMap.Range(func(snamei, svci any) bool { svc := svci.(*service) ds := debugService{svc, snamei.(string), make(methodArray, 0, len(svc.method))} for mname, method := range svc.method { diff --git a/src/net/rpc/jsonrpc/all_test.go b/src/net/rpc/jsonrpc/all_test.go index 667f839f580224..e2ccdfc6fb8e49 100644 --- a/src/net/rpc/jsonrpc/all_test.go +++ b/src/net/rpc/jsonrpc/all_test.go @@ -5,7 +5,6 @@ package jsonrpc import ( - "bytes" "encoding/json" "errors" "fmt" @@ -28,9 +27,9 @@ type Reply struct { type Arith int type ArithAddResp struct { - Id interface{} `json:"id"` - Result Reply `json:"result"` - Error interface{} `json:"error"` + Id any `json:"id"` + Result Reply `json:"result"` + Error any `json:"error"` } func (t *Arith) Add(args *Args, reply *Reply) error { @@ -262,7 +261,7 @@ func TestMalformedOutput(t *testing.T) { } func TestServerErrorHasNullResult(t *testing.T) { - var out bytes.Buffer + var out strings.Builder sc := NewServerCodec(struct { io.Reader io.Writer diff --git a/src/net/rpc/jsonrpc/client.go b/src/net/rpc/jsonrpc/client.go index e6359bed5985cf..c473017d26d956 100644 --- a/src/net/rpc/jsonrpc/client.go +++ b/src/net/rpc/jsonrpc/client.go @@ -44,12 +44,12 @@ func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec { } type clientRequest struct { - Method string `json:"method"` - Params [1]interface{} `json:"params"` - Id uint64 `json:"id"` + Method string `json:"method"` + Params [1]any `json:"params"` + Id uint64 `json:"id"` } -func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error { +func (c *clientCodec) WriteRequest(r *rpc.Request, param any) error { c.mutex.Lock() c.pending[r.Seq] = r.ServiceMethod c.mutex.Unlock() @@ -62,7 +62,7 @@ func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error { type clientResponse struct { Id uint64 `json:"id"` Result *json.RawMessage `json:"result"` - Error interface{} `json:"error"` + Error any `json:"error"` } func (r *clientResponse) reset() { @@ -97,7 +97,7 @@ func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error { return nil } -func (c *clientCodec) ReadResponseBody(x interface{}) error { +func (c *clientCodec) ReadResponseBody(x any) error { if x == nil { return nil } diff --git a/src/net/rpc/jsonrpc/server.go b/src/net/rpc/jsonrpc/server.go index 40e4e6f2aa5c18..3ee4ddfef2ac2a 100644 --- a/src/net/rpc/jsonrpc/server.go +++ b/src/net/rpc/jsonrpc/server.go @@ -57,8 +57,8 @@ func (r *serverRequest) reset() { type serverResponse struct { Id *json.RawMessage `json:"id"` - Result interface{} `json:"result"` - Error interface{} `json:"error"` + Result any `json:"result"` + Error any `json:"error"` } func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error { @@ -81,7 +81,7 @@ func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error { return nil } -func (c *serverCodec) ReadRequestBody(x interface{}) error { +func (c *serverCodec) ReadRequestBody(x any) error { if x == nil { return nil } @@ -92,14 +92,14 @@ func (c *serverCodec) ReadRequestBody(x interface{}) error { // RPC params is struct. // Unmarshal into array containing struct for now. // Should think about making RPC more general. - var params [1]interface{} + var params [1]any params[0] = x return json.Unmarshal(*c.req.Params, ¶ms) } var null = json.RawMessage([]byte("null")) -func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error { +func (c *serverCodec) WriteResponse(r *rpc.Response, x any) error { c.mutex.Lock() b, ok := c.pending[r.Seq] if !ok { diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go index 074c5b9b0d7a5e..109ebba5413e5c 100644 --- a/src/net/rpc/server.go +++ b/src/net/rpc/server.go @@ -3,126 +3,126 @@ // license that can be found in the LICENSE file. /* - Package rpc provides access to the exported methods of an object across a - network or other I/O connection. A server registers an object, making it visible - as a service with the name of the type of the object. After registration, exported - methods of the object will be accessible remotely. A server may register multiple - objects (services) of different types but it is an error to register multiple - objects of the same type. +Package rpc provides access to the exported methods of an object across a +network or other I/O connection. A server registers an object, making it visible +as a service with the name of the type of the object. After registration, exported +methods of the object will be accessible remotely. A server may register multiple +objects (services) of different types but it is an error to register multiple +objects of the same type. - Only methods that satisfy these criteria will be made available for remote access; - other methods will be ignored: +Only methods that satisfy these criteria will be made available for remote access; +other methods will be ignored: - - the method's type is exported. - - the method is exported. - - the method has two arguments, both exported (or builtin) types. - - the method's second argument is a pointer. - - the method has return type error. + - the method's type is exported. + - the method is exported. + - the method has two arguments, both exported (or builtin) types. + - the method's second argument is a pointer. + - the method has return type error. - In effect, the method must look schematically like +In effect, the method must look schematically like - func (t *T) MethodName(argType T1, replyType *T2) error + func (t *T) MethodName(argType T1, replyType *T2) error - where T1 and T2 can be marshaled by encoding/gob. - These requirements apply even if a different codec is used. - (In the future, these requirements may soften for custom codecs.) +where T1 and T2 can be marshaled by encoding/gob. +These requirements apply even if a different codec is used. +(In the future, these requirements may soften for custom codecs.) - The method's first argument represents the arguments provided by the caller; the - second argument represents the result parameters to be returned to the caller. - The method's return value, if non-nil, is passed back as a string that the client - sees as if created by errors.New. If an error is returned, the reply parameter - will not be sent back to the client. +The method's first argument represents the arguments provided by the caller; the +second argument represents the result parameters to be returned to the caller. +The method's return value, if non-nil, is passed back as a string that the client +sees as if created by errors.New. If an error is returned, the reply parameter +will not be sent back to the client. - The server may handle requests on a single connection by calling ServeConn. More - typically it will create a network listener and call Accept or, for an HTTP - listener, HandleHTTP and http.Serve. +The server may handle requests on a single connection by calling ServeConn. More +typically it will create a network listener and call Accept or, for an HTTP +listener, HandleHTTP and http.Serve. - A client wishing to use the service establishes a connection and then invokes - NewClient on the connection. The convenience function Dial (DialHTTP) performs - both steps for a raw network connection (an HTTP connection). The resulting - Client object has two methods, Call and Go, that specify the service and method to - call, a pointer containing the arguments, and a pointer to receive the result - parameters. +A client wishing to use the service establishes a connection and then invokes +NewClient on the connection. The convenience function Dial (DialHTTP) performs +both steps for a raw network connection (an HTTP connection). The resulting +Client object has two methods, Call and Go, that specify the service and method to +call, a pointer containing the arguments, and a pointer to receive the result +parameters. - The Call method waits for the remote call to complete while the Go method - launches the call asynchronously and signals completion using the Call - structure's Done channel. +The Call method waits for the remote call to complete while the Go method +launches the call asynchronously and signals completion using the Call +structure's Done channel. - Unless an explicit codec is set up, package encoding/gob is used to - transport the data. +Unless an explicit codec is set up, package encoding/gob is used to +transport the data. - Here is a simple example. A server wishes to export an object of type Arith: +Here is a simple example. A server wishes to export an object of type Arith: - package server + package server - import "errors" + import "errors" - type Args struct { - A, B int - } + type Args struct { + A, B int + } - type Quotient struct { - Quo, Rem int - } + type Quotient struct { + Quo, Rem int + } - type Arith int + type Arith int - func (t *Arith) Multiply(args *Args, reply *int) error { - *reply = args.A * args.B - return nil - } + func (t *Arith) Multiply(args *Args, reply *int) error { + *reply = args.A * args.B + return nil + } - func (t *Arith) Divide(args *Args, quo *Quotient) error { - if args.B == 0 { - return errors.New("divide by zero") - } - quo.Quo = args.A / args.B - quo.Rem = args.A % args.B - return nil + func (t *Arith) Divide(args *Args, quo *Quotient) error { + if args.B == 0 { + return errors.New("divide by zero") } + quo.Quo = args.A / args.B + quo.Rem = args.A % args.B + return nil + } - The server calls (for HTTP service): +The server calls (for HTTP service): - arith := new(Arith) - rpc.Register(arith) - rpc.HandleHTTP() - l, e := net.Listen("tcp", ":1234") - if e != nil { - log.Fatal("listen error:", e) - } - go http.Serve(l, nil) + arith := new(Arith) + rpc.Register(arith) + rpc.HandleHTTP() + l, e := net.Listen("tcp", ":1234") + if e != nil { + log.Fatal("listen error:", e) + } + go http.Serve(l, nil) - At this point, clients can see a service "Arith" with methods "Arith.Multiply" and - "Arith.Divide". To invoke one, a client first dials the server: +At this point, clients can see a service "Arith" with methods "Arith.Multiply" and +"Arith.Divide". To invoke one, a client first dials the server: - client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") - if err != nil { - log.Fatal("dialing:", err) - } + client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") + if err != nil { + log.Fatal("dialing:", err) + } - Then it can make a remote call: +Then it can make a remote call: - // Synchronous call - args := &server.Args{7,8} - var reply int - err = client.Call("Arith.Multiply", args, &reply) - if err != nil { - log.Fatal("arith error:", err) - } - fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply) + // Synchronous call + args := &server.Args{7,8} + var reply int + err = client.Call("Arith.Multiply", args, &reply) + if err != nil { + log.Fatal("arith error:", err) + } + fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply) - or +or - // Asynchronous call - quotient := new(Quotient) - divCall := client.Go("Arith.Divide", args, quotient, nil) - replyCall := <-divCall.Done // will be equal to divCall - // check errors, print, etc. + // Asynchronous call + quotient := new(Quotient) + divCall := client.Go("Arith.Divide", args, quotient, nil) + replyCall := <-divCall.Done // will be equal to divCall + // check errors, print, etc. - A server implementation will often provide a simple, type-safe wrapper for the - client. +A server implementation will often provide a simple, type-safe wrapper for the +client. - The net/rpc package is frozen and is not accepting new features. +The net/rpc package is frozen and is not accepting new features. */ package rpc @@ -203,7 +203,7 @@ var DefaultServer = NewServer() // Is this type exported or a builtin? func isExportedOrBuiltinType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { + for t.Kind() == reflect.Pointer { t = t.Elem() } // PkgPath will be non-empty even for an exported type, @@ -213,38 +213,43 @@ func isExportedOrBuiltinType(t reflect.Type) bool { // Register publishes in the server the set of methods of the // receiver value that satisfy the following conditions: -// - exported method of exported type -// - two arguments, both of exported type -// - the second argument is a pointer -// - one return value, of type error +// - exported method of exported type +// - two arguments, both of exported type +// - the second argument is a pointer +// - one return value, of type error +// // It returns an error if the receiver is not an exported type or has // no suitable methods. It also logs the error using package log. // The client accesses each method using a string of the form "Type.Method", // where Type is the receiver's concrete type. -func (server *Server) Register(rcvr interface{}) error { +func (server *Server) Register(rcvr any) error { return server.register(rcvr, "", false) } // RegisterName is like Register but uses the provided name for the type // instead of the receiver's concrete type. -func (server *Server) RegisterName(name string, rcvr interface{}) error { +func (server *Server) RegisterName(name string, rcvr any) error { return server.register(rcvr, name, true) } -func (server *Server) register(rcvr interface{}, name string, useName bool) error { +// logRegisterError specifies whether to log problems during method registration. +// To debug registration, recompile the package with this set to true. +const logRegisterError = false + +func (server *Server) register(rcvr any, name string, useName bool) error { s := new(service) s.typ = reflect.TypeOf(rcvr) s.rcvr = reflect.ValueOf(rcvr) - sname := reflect.Indirect(s.rcvr).Type().Name() - if useName { - sname = name + sname := name + if !useName { + sname = reflect.Indirect(s.rcvr).Type().Name() } if sname == "" { s := "rpc.Register: no service name for type " + s.typ.String() log.Print(s) return errors.New(s) } - if !token.IsExported(sname) && !useName { + if !useName && !token.IsExported(sname) { s := "rpc.Register: type " + sname + " is not exported" log.Print(s) return errors.New(s) @@ -252,13 +257,13 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro s.name = sname // Install the methods - s.method = suitableMethods(s.typ, true) + s.method = suitableMethods(s.typ, logRegisterError) if len(s.method) == 0 { str := "" // To help the user, see if a pointer receiver would work. - method := suitableMethods(reflect.PtrTo(s.typ), false) + method := suitableMethods(reflect.PointerTo(s.typ), false) if len(method) != 0 { str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)" } else { @@ -274,9 +279,9 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro return nil } -// suitableMethods returns suitable Rpc methods of typ, it will report -// error using log if reportErr is true. -func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { +// suitableMethods returns suitable Rpc methods of typ. It will log +// errors if logErr is true. +func suitableMethods(typ reflect.Type, logErr bool) map[string]*methodType { methods := make(map[string]*methodType) for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m) @@ -288,7 +293,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { } // Method needs three ins: receiver, *args, *reply. if mtype.NumIn() != 3 { - if reportErr { + if logErr { log.Printf("rpc.Register: method %q has %d input parameters; needs exactly three\n", mname, mtype.NumIn()) } continue @@ -296,36 +301,36 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { // First arg need not be a pointer. argType := mtype.In(1) if !isExportedOrBuiltinType(argType) { - if reportErr { + if logErr { log.Printf("rpc.Register: argument type of method %q is not exported: %q\n", mname, argType) } continue } // Second arg must be a pointer. replyType := mtype.In(2) - if replyType.Kind() != reflect.Ptr { - if reportErr { + if replyType.Kind() != reflect.Pointer { + if logErr { log.Printf("rpc.Register: reply type of method %q is not a pointer: %q\n", mname, replyType) } continue } // Reply type must be exported. if !isExportedOrBuiltinType(replyType) { - if reportErr { + if logErr { log.Printf("rpc.Register: reply type of method %q is not exported: %q\n", mname, replyType) } continue } // Method needs one out. if mtype.NumOut() != 1 { - if reportErr { + if logErr { log.Printf("rpc.Register: method %q has %d output parameters; needs exactly one\n", mname, mtype.NumOut()) } continue } // The return type of the method must be error. if returnType := mtype.Out(0); returnType != typeOfError { - if reportErr { + if logErr { log.Printf("rpc.Register: return type of method %q is %q, must be error\n", mname, returnType) } continue @@ -340,7 +345,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { // contains an error when it is used. var invalidRequest = struct{}{} -func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply interface{}, codec ServerCodec, errmsg string) { +func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply any, codec ServerCodec, errmsg string) { resp := server.getResponse() // Encode the response header resp.ServiceMethod = req.ServiceMethod @@ -397,11 +402,11 @@ func (c *gobServerCodec) ReadRequestHeader(r *Request) error { return c.dec.Decode(r) } -func (c *gobServerCodec) ReadRequestBody(body interface{}) error { +func (c *gobServerCodec) ReadRequestBody(body any) error { return c.dec.Decode(body) } -func (c *gobServerCodec) WriteResponse(r *Response, body interface{}) (err error) { +func (c *gobServerCodec) WriteResponse(r *Response, body any) (err error) { if err = c.enc.Encode(r); err != nil { if c.encBuf.Flush() == nil { // Gob couldn't encode the header. Should not happen, so if it does, @@ -552,7 +557,7 @@ func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *m // Decode the argument value. argIsValue := false // if true, need to indirect before calling. - if mtype.ArgType.Kind() == reflect.Ptr { + if mtype.ArgType.Kind() == reflect.Pointer { argv = reflect.New(mtype.ArgType.Elem()) } else { argv = reflect.New(mtype.ArgType) @@ -632,11 +637,11 @@ func (server *Server) Accept(lis net.Listener) { } // Register publishes the receiver's methods in the DefaultServer. -func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) } +func Register(rcvr any) error { return DefaultServer.Register(rcvr) } // RegisterName is like Register but uses the provided name for the type // instead of the receiver's concrete type. -func RegisterName(name string, rcvr interface{}) error { +func RegisterName(name string, rcvr any) error { return DefaultServer.RegisterName(name, rcvr) } @@ -650,8 +655,8 @@ func RegisterName(name string, rcvr interface{}) error { // See NewClient's comment for information about concurrent access. type ServerCodec interface { ReadRequestHeader(*Request) error - ReadRequestBody(interface{}) error - WriteResponse(*Response, interface{}) error + ReadRequestBody(any) error + WriteResponse(*Response, any) error // Close can be called multiple times and must be idempotent. Close() error diff --git a/src/net/rpc/server_test.go b/src/net/rpc/server_test.go index e5d7fe0c8f55e7..dc5f5decc770b3 100644 --- a/src/net/rpc/server_test.go +++ b/src/net/rpc/server_test.go @@ -427,7 +427,7 @@ func (codec *CodecEmulator) ReadRequestHeader(req *Request) error { return nil } -func (codec *CodecEmulator) ReadRequestBody(argv interface{}) error { +func (codec *CodecEmulator) ReadRequestBody(argv any) error { if codec.args == nil { return io.ErrUnexpectedEOF } @@ -435,7 +435,7 @@ func (codec *CodecEmulator) ReadRequestBody(argv interface{}) error { return nil } -func (codec *CodecEmulator) WriteResponse(resp *Response, reply interface{}) error { +func (codec *CodecEmulator) WriteResponse(resp *Response, reply any) error { if resp.Error != "" { codec.err = errors.New(resp.Error) } else { @@ -521,7 +521,7 @@ func TestRegistrationError(t *testing.T) { type WriteFailCodec int -func (WriteFailCodec) WriteRequest(*Request, interface{}) error { +func (WriteFailCodec) WriteRequest(*Request, any) error { // the panic caused by this error used to not unlock a lock. return errors.New("fail") } @@ -530,7 +530,7 @@ func (WriteFailCodec) ReadResponseHeader(*Response) error { select {} } -func (WriteFailCodec) ReadResponseBody(interface{}) error { +func (WriteFailCodec) ReadResponseBody(any) error { select {} } diff --git a/src/net/sendfile_linux_test.go b/src/net/sendfile_linux_test.go new file mode 100644 index 00000000000000..8cd6acca17ced4 --- /dev/null +++ b/src/net/sendfile_linux_test.go @@ -0,0 +1,77 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux +// +build linux + +package net + +import ( + "io" + "os" + "strconv" + "testing" +) + +func BenchmarkSendFile(b *testing.B) { + for i := 0; i <= 10; i++ { + size := 1 << (i + 10) + bench := sendFileBench{chunkSize: size} + b.Run(strconv.Itoa(size), bench.benchSendFile) + } +} + +type sendFileBench struct { + chunkSize int +} + +func (bench sendFileBench) benchSendFile(b *testing.B) { + fileSize := b.N * bench.chunkSize + f := createTempFile(b, fileSize) + fileName := f.Name() + defer os.Remove(fileName) + defer f.Close() + + client, server := spliceTestSocketPair(b, "tcp") + defer server.Close() + + cleanUp, err := startSpliceClient(client, "r", bench.chunkSize, fileSize) + if err != nil { + b.Fatal(err) + } + defer cleanUp() + + b.ReportAllocs() + b.SetBytes(int64(bench.chunkSize)) + b.ResetTimer() + + // Data go from file to socket via sendfile(2). + sent, err := io.Copy(server, f) + if err != nil { + b.Fatalf("failed to copy data with sendfile, error: %v", err) + } + if sent != int64(fileSize) { + b.Fatalf("bytes sent mismatch\n\texpect: %d\n\tgot: %d", fileSize, sent) + } +} + +func createTempFile(b *testing.B, size int) *os.File { + f, err := os.CreateTemp("", "linux-sendfile-test") + if err != nil { + b.Fatalf("failed to create temporary file: %v", err) + } + + data := make([]byte, size) + if _, err := f.Write(data); err != nil { + b.Fatalf("failed to create and feed the file: %v", err) + } + if err := f.Sync(); err != nil { + b.Fatalf("failed to save the file: %v", err) + } + if _, err := f.Seek(0, io.SeekStart); err != nil { + b.Fatalf("failed to rewind the file: %v", err) + } + + return f +} diff --git a/src/net/sendfile_stub.go b/src/net/sendfile_stub.go index 5753bc02898742..7428da3127b10b 100644 --- a/src/net/sendfile_stub.go +++ b/src/net/sendfile_stub.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || (js && wasm) || netbsd || openbsd -// +build aix darwin js,wasm netbsd openbsd +//go:build aix || (js && wasm) || netbsd || openbsd || ios package net diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go index 54e51fa0abc66e..969c022b00eb6f 100644 --- a/src/net/sendfile_test.go +++ b/src/net/sendfile_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -28,10 +27,7 @@ const ( ) func TestSendfile(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() errc := make(chan error, 1) @@ -98,10 +94,7 @@ func TestSendfile(t *testing.T) { } func TestSendfileParts(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() errc := make(chan error, 1) @@ -156,10 +149,7 @@ func TestSendfileParts(t *testing.T) { } func TestSendfileSeeked(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() const seekTo = 65 << 10 @@ -185,7 +175,7 @@ func TestSendfileSeeked(t *testing.T) { return } defer f.Close() - if _, err := f.Seek(seekTo, os.SEEK_SET); err != nil { + if _, err := f.Seek(seekTo, io.SeekStart); err != nil { errc <- err return } @@ -226,10 +216,7 @@ func TestSendfilePipe(t *testing.T) { t.Parallel() - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() r, w, err := os.Pipe() @@ -318,10 +305,7 @@ func TestSendfilePipe(t *testing.T) { // Issue 43822: tests that returns EOF when conn write timeout. func TestSendfileOnWriteTimeoutExceeded(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() errc := make(chan error, 1) diff --git a/src/net/sendfile_unix_alt.go b/src/net/sendfile_unix_alt.go index 54667d672f87b4..b86771721ee2d7 100644 --- a/src/net/sendfile_unix_alt.go +++ b/src/net/sendfile_unix_alt.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || solaris -// +build dragonfly freebsd solaris +//go:build (darwin && !ios) || dragonfly || freebsd || solaris package net @@ -21,7 +20,7 @@ import ( // // if handled == false, sendFile performed no work. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - // FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. + // Darwin, FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. // If you pass in more bytes than the file contains, it will // loop back to the beginning ad nauseam until it's sent // exactly the number of bytes told to. As such, we need to @@ -49,7 +48,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { remain = fi.Size() } - // The other quirk with FreeBSD/DragonFly/Solaris's sendfile + // The other quirk with Darwin/FreeBSD/DragonFly/Solaris's sendfile // implementation is that it doesn't use the current position // of the file -- if you pass it offset 0, it starts from // offset 0. There's no way to tell it "start from current diff --git a/src/net/server_test.go b/src/net/server_test.go index 7cbf1522988715..6796d7993e9c5b 100644 --- a/src/net/server_test.go +++ b/src/net/server_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -78,10 +77,7 @@ func TestTCPServer(t *testing.T) { } }() for i := 0; i < N; i++ { - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() lss = append(lss, ls) tpchs = append(tpchs, make(chan error, 1)) } @@ -126,19 +122,19 @@ func TestTCPServer(t *testing.T) { } } -var unixAndUnixpacketServerTests = []struct { - network, address string -}{ - {"unix", testUnixAddr()}, - {"unix", "@nettest/go/unix"}, - - {"unixpacket", testUnixAddr()}, - {"unixpacket", "@nettest/go/unixpacket"}, -} - // TestUnixAndUnixpacketServer tests concurrent accept-read-write // servers func TestUnixAndUnixpacketServer(t *testing.T) { + var unixAndUnixpacketServerTests = []struct { + network, address string + }{ + {"unix", testUnixAddr(t)}, + {"unix", "@nettest/go/unix"}, + + {"unixpacket", testUnixAddr(t)}, + {"unixpacket", "@nettest/go/unixpacket"}, + } + const N = 3 for i, tt := range unixAndUnixpacketServerTests { @@ -163,10 +159,7 @@ func TestUnixAndUnixpacketServer(t *testing.T) { } }() for i := 0; i < N; i++ { - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() lss = append(lss, ls) tpchs = append(tpchs, make(chan error, 1)) } @@ -188,7 +181,11 @@ func TestUnixAndUnixpacketServer(t *testing.T) { } t.Fatal(err) } - defer os.Remove(c.LocalAddr().String()) + + if addr := c.LocalAddr(); addr != nil { + t.Logf("connected %s->%s", addr, lss[i].Listener.Addr()) + } + defer c.Close() trchs = append(trchs, make(chan error, 1)) go transceiver(c, []byte("UNIX AND UNIXPACKET SERVER TEST"), trchs[i]) @@ -267,10 +264,7 @@ func TestUDPServer(t *testing.T) { t.Fatal(err) } - ls, err := (&packetListener{PacketConn: c1}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&packetListener{PacketConn: c1}).newLocalServer() defer ls.teardown() tpch := make(chan error, 1) handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) } @@ -319,18 +313,18 @@ func TestUDPServer(t *testing.T) { } } -var unixgramServerTests = []struct { - saddr string // server endpoint - caddr string // client endpoint - dial bool // test with Dial -}{ - {saddr: testUnixAddr(), caddr: testUnixAddr()}, - {saddr: testUnixAddr(), caddr: testUnixAddr(), dial: true}, - - {saddr: "@nettest/go/unixgram/server", caddr: "@nettest/go/unixgram/client"}, -} - func TestUnixgramServer(t *testing.T) { + var unixgramServerTests = []struct { + saddr string // server endpoint + caddr string // client endpoint + dial bool // test with Dial + }{ + {saddr: testUnixAddr(t), caddr: testUnixAddr(t)}, + {saddr: testUnixAddr(t), caddr: testUnixAddr(t), dial: true}, + + {saddr: "@nettest/go/unixgram/server", caddr: "@nettest/go/unixgram/client"}, + } + for i, tt := range unixgramServerTests { if !testableListenArgs("unixgram", tt.saddr, "") { t.Logf("skipping %s test", "unixgram "+tt.saddr+"<-"+tt.caddr) @@ -345,10 +339,7 @@ func TestUnixgramServer(t *testing.T) { t.Fatal(err) } - ls, err := (&packetListener{PacketConn: c1}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&packetListener{PacketConn: c1}).newLocalServer() defer ls.teardown() tpch := make(chan error, 1) handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, tpch) } diff --git a/src/net/smtp/auth.go b/src/net/smtp/auth.go index fd1a472f930dd4..7a32ef6a2e615b 100644 --- a/src/net/smtp/auth.go +++ b/src/net/smtp/auth.go @@ -16,8 +16,7 @@ type Auth interface { // Start begins an authentication with a server. // It returns the name of the authentication protocol // and optionally data to include in the initial AUTH message - // sent to the server. It can return proto == "" to indicate - // that the authentication should be skipped. + // sent to the server. // If it returns a non-nil error, the SMTP client aborts // the authentication attempt and closes the connection. Start(server *ServerInfo) (proto string, toServer []byte, err error) diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go index 1a6864a0f2436c..3bd2061b0c907c 100644 --- a/src/net/smtp/smtp.go +++ b/src/net/smtp/smtp.go @@ -4,15 +4,17 @@ // Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321. // It also implements the following extensions: +// // 8BITMIME RFC 1652 // AUTH RFC 2554 // STARTTLS RFC 3207 +// // Additional extensions may be handled by clients. // // The smtp package is frozen and is not accepting new features. // Some external packages provide more functionality. See: // -// https://godoc.org/?q=smtp +// https://godoc.org/?q=smtp package smtp import ( @@ -105,7 +107,7 @@ func (c *Client) Hello(localName string) error { } // cmd is a convenience function that sends a command and returns the response -func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) { +func (c *Client) cmd(expectCode int, format string, args ...any) (int, string, error) { id, err := c.Text.Cmd(format, args...) if err != nil { return 0, "", err @@ -136,12 +138,8 @@ func (c *Client) ehlo() error { if len(extList) > 1 { extList = extList[1:] for _, line := range extList { - args := strings.SplitN(line, " ", 2) - if len(args) > 1 { - ext[args[0]] = args[1] - } else { - ext[args[0]] = "" - } + k, v, _ := strings.Cut(line, " ") + ext[k] = v } } if mechs, ok := ext["AUTH"]; ok { diff --git a/src/net/smtp/smtp_test.go b/src/net/smtp/smtp_test.go index 55219372d278c7..dba074436b6097 100644 --- a/src/net/smtp/smtp_test.go +++ b/src/net/smtp/smtp_test.go @@ -113,7 +113,7 @@ func TestAuthPlain(t *testing.T) { func TestClientAuthTrimSpace(t *testing.T) { server := "220 hello world\r\n" + "200 some more" - var wrote bytes.Buffer + var wrote strings.Builder var fake faker fake.ReadWriter = struct { io.Reader @@ -164,7 +164,7 @@ func TestBasic(t *testing.T) { server := strings.Join(strings.Split(basicServer, "\n"), "\r\n") client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) @@ -505,7 +505,7 @@ func TestNewClient(t *testing.T) { server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n") client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) out := func() string { bcmdbuf.Flush() @@ -550,7 +550,7 @@ func TestNewClient2(t *testing.T) { server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n") client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) @@ -643,7 +643,7 @@ func TestHello(t *testing.T) { for i := 0; i < len(helloServer); i++ { server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n") client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) @@ -749,7 +749,7 @@ var helloClient = []string{ func TestSendMail(t *testing.T) { server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n") client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { @@ -906,7 +906,7 @@ SendMail is working for me. func TestAuthFailed(t *testing.T) { server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n") client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n") - var cmdbuf bytes.Buffer + var cmdbuf strings.Builder bcmdbuf := bufio.NewWriter(&cmdbuf) var fake faker fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) @@ -948,7 +948,7 @@ QUIT ` func TestTLSClient(t *testing.T) { - if (runtime.GOOS == "freebsd" && runtime.GOARCH == "amd64") || runtime.GOOS == "js" { + if runtime.GOOS == "freebsd" || runtime.GOOS == "js" { testenv.SkipFlaky(t, 19229) } ln := newLocalListener(t) @@ -1104,8 +1104,9 @@ func sendMail(hostPort string) error { } // localhostCert is a PEM-encoded TLS cert generated from src/crypto/tls: -// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \ -// --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h +// +// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \ +// --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var localhostCert = []byte(` -----BEGIN CERTIFICATE----- MIICFDCCAX2gAwIBAgIRAK0xjnaPuNDSreeXb+z+0u4wDQYJKoZIhvcNAQELBQAw diff --git a/src/net/sock_bsd.go b/src/net/sock_bsd.go index 4c883ada78f91e..27daf722b53466 100644 --- a/src/net/sock_bsd.go +++ b/src/net/sock_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/sock_cloexec.go b/src/net/sock_cloexec.go index 6861c4bf635dcf..3f1cc9827aa363 100644 --- a/src/net/sock_cloexec.go +++ b/src/net/sock_cloexec.go @@ -5,13 +5,11 @@ // This file implements sysSocket for platforms that provide a fast path for // setting SetNonblock and CloseOnExec. -//go:build dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build dragonfly freebsd illumos linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris package net import ( - "internal/poll" "os" "syscall" ) @@ -20,32 +18,8 @@ import ( // descriptor as nonblocking and close-on-exec. func sysSocket(family, sotype, proto int) (int, error) { s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto) - // On Linux the SOCK_NONBLOCK and SOCK_CLOEXEC flags were - // introduced in 2.6.27 kernel and on FreeBSD both flags were - // introduced in 10 kernel. If we get an EINVAL error on Linux - // or EPROTONOSUPPORT error on FreeBSD, fall back to using - // socket without them. - switch err { - case nil: - return s, nil - default: - return -1, os.NewSyscallError("socket", err) - case syscall.EPROTONOSUPPORT, syscall.EINVAL: - } - - // See ../syscall/exec_unix.go for description of ForkLock. - syscall.ForkLock.RLock() - s, err = socketFunc(family, sotype, proto) - if err == nil { - syscall.CloseOnExec(s) - } - syscall.ForkLock.RUnlock() if err != nil { return -1, os.NewSyscallError("socket", err) } - if err = syscall.SetNonblock(s, true); err != nil { - poll.CloseFunc(s) - return -1, os.NewSyscallError("setnonblock", err) - } return s, nil } diff --git a/src/net/sock_linux.go b/src/net/sock_linux.go index 9f62ed3deed37c..cffe9a236fc7f1 100644 --- a/src/net/sock_linux.go +++ b/src/net/sock_linux.go @@ -4,53 +4,21 @@ package net -import "syscall" - -func kernelVersion() (major int, minor int) { - var uname syscall.Utsname - if err := syscall.Uname(&uname); err != nil { - return - } - - rl := uname.Release - var values [2]int - vi := 0 - value := 0 - for _, c := range rl { - if c >= '0' && c <= '9' { - value = (value * 10) + int(c-'0') - } else { - // Note that we're assuming N.N.N here. If we see anything else we are likely to - // mis-parse it. - values[vi] = value - vi++ - if vi >= len(values) { - break - } - value = 0 - } - } - switch vi { - case 0: - return 0, 0 - case 1: - return values[0], 0 - case 2: - return values[0], values[1] - } - return -} +import ( + "internal/syscall/unix" + "syscall" +) // Linux stores the backlog as: // -// - uint16 in kernel version < 4.1, -// - uint32 in kernel version >= 4.1 +// - uint16 in kernel version < 4.1, +// - uint32 in kernel version >= 4.1 // // Truncate number to avoid wrapping. // // See issue 5030 and 41470. func maxAckBacklog(n int) int { - major, minor := kernelVersion() + major, minor := unix.KernelVersion() size := 16 if major > 4 || (major == 4 && minor >= 1) { size = 32 diff --git a/src/net/sock_linux_test.go b/src/net/sock_linux_test.go index 5df02935c3ce69..11303cfff1e646 100644 --- a/src/net/sock_linux_test.go +++ b/src/net/sock_linux_test.go @@ -5,12 +5,13 @@ package net import ( + "internal/syscall/unix" "testing" ) func TestMaxAckBacklog(t *testing.T) { n := 196602 - major, minor := kernelVersion() + major, minor := unix.KernelVersion() backlog := maxAckBacklog(n) expected := 1<<16 - 1 if major > 4 || (major == 4 && minor >= 1) { diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go index 9b1e7880aed449..4431c3a6b3d422 100644 --- a/src/net/sock_posix.go +++ b/src/net/sock_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package net diff --git a/src/net/sock_stub.go b/src/net/sock_stub.go index d804bfaacce3a0..4b73e575fe3a3f 100644 --- a/src/net/sock_stub.go +++ b/src/net/sock_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || (js && wasm) || solaris -// +build aix js,wasm solaris package net diff --git a/src/net/sockaddr_posix.go b/src/net/sockaddr_posix.go index 9d77cb569b938e..76c3233b29845c 100644 --- a/src/net/sockaddr_posix.go +++ b/src/net/sockaddr_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/sockopt_bsd.go b/src/net/sockopt_bsd.go index ee1f98b834cf37..ff998119808e48 100644 --- a/src/net/sockopt_bsd.go +++ b/src/net/sockopt_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/src/net/sockopt_posix.go b/src/net/sockopt_posix.go index 50b9bfa0a78ea5..32e8fcd5055040 100644 --- a/src/net/sockopt_posix.go +++ b/src/net/sockopt_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package net diff --git a/src/net/sockopt_stub.go b/src/net/sockopt_stub.go index 99b5277ed0a874..98e23714d9a267 100644 --- a/src/net/sockopt_stub.go +++ b/src/net/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net diff --git a/src/net/sockoptip_bsdvar.go b/src/net/sockoptip_bsdvar.go index 56022fd1a59e37..3e9ba1ee7867e7 100644 --- a/src/net/sockoptip_bsdvar.go +++ b/src/net/sockoptip_bsdvar.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd netbsd openbsd solaris package net diff --git a/src/net/sockoptip_posix.go b/src/net/sockoptip_posix.go index a2143aec2cba67..572ea455c09586 100644 --- a/src/net/sockoptip_posix.go +++ b/src/net/sockoptip_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package net diff --git a/src/net/sockoptip_stub.go b/src/net/sockoptip_stub.go index 4175922cec5f6f..2c993eb71953f6 100644 --- a/src/net/sockoptip_stub.go +++ b/src/net/sockoptip_stub.go @@ -3,38 +3,31 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net import "syscall" func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } func setIPv6MulticastLoopback(fd *netFD, v bool) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { - // See golang.org/issue/7399. return syscall.ENOPROTOOPT } diff --git a/src/net/splice_stub.go b/src/net/splice_stub.go index ce2e9046a9440f..3cdadb11c5615f 100644 --- a/src/net/splice_stub.go +++ b/src/net/splice_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package net diff --git a/src/net/splice_test.go b/src/net/splice_test.go index be13cc924d04f5..fa14c95eb71533 100644 --- a/src/net/splice_test.go +++ b/src/net/splice_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package net @@ -47,20 +46,14 @@ type spliceTestCase struct { } func (tc spliceTestCase) test(t *testing.T) { - clientUp, serverUp, err := spliceTestSocketPair(tc.upNet) - if err != nil { - t.Fatal(err) - } + clientUp, serverUp := spliceTestSocketPair(t, tc.upNet) defer serverUp.Close() cleanup, err := startSpliceClient(clientUp, "w", tc.chunkSize, tc.totalSize) if err != nil { t.Fatal(err) } defer cleanup() - clientDown, serverDown, err := spliceTestSocketPair(tc.downNet) - if err != nil { - t.Fatal(err) - } + clientDown, serverDown := spliceTestSocketPair(t, tc.downNet) defer serverDown.Close() cleanup, err = startSpliceClient(clientDown, "r", tc.chunkSize, tc.totalSize) if err != nil { @@ -104,15 +97,9 @@ func (tc spliceTestCase) test(t *testing.T) { } func testSpliceReaderAtEOF(t *testing.T, upNet, downNet string) { - clientUp, serverUp, err := spliceTestSocketPair(upNet) - if err != nil { - t.Fatal(err) - } + clientUp, serverUp := spliceTestSocketPair(t, upNet) defer clientUp.Close() - clientDown, serverDown, err := spliceTestSocketPair(downNet) - if err != nil { - t.Fatal(err) - } + clientDown, serverDown := spliceTestSocketPair(t, downNet) defer clientDown.Close() serverUp.Close() @@ -141,7 +128,7 @@ func testSpliceReaderAtEOF(t *testing.T, upNet, downNet string) { }() buf := make([]byte, 3) - _, err = io.ReadFull(clientDown, buf) + _, err := io.ReadFull(clientDown, buf) if err != nil { t.Errorf("clientDown: %v", err) } @@ -151,15 +138,9 @@ func testSpliceReaderAtEOF(t *testing.T, upNet, downNet string) { } func testSpliceIssue25985(t *testing.T, upNet, downNet string) { - front, err := newLocalListener(upNet) - if err != nil { - t.Fatal(err) - } + front := newLocalListener(t, upNet) defer front.Close() - back, err := newLocalListener(downNet) - if err != nil { - t.Fatal(err) - } + back := newLocalListener(t, downNet) defer back.Close() var wg sync.WaitGroup @@ -211,16 +192,10 @@ func testSpliceIssue25985(t *testing.T, upNet, downNet string) { } func testSpliceNoUnixpacket(t *testing.T) { - clientUp, serverUp, err := spliceTestSocketPair("unixpacket") - if err != nil { - t.Fatal(err) - } + clientUp, serverUp := spliceTestSocketPair(t, "unixpacket") defer clientUp.Close() defer serverUp.Close() - clientDown, serverDown, err := spliceTestSocketPair("tcp") - if err != nil { - t.Fatal(err) - } + clientDown, serverDown := spliceTestSocketPair(t, "tcp") defer clientDown.Close() defer serverDown.Close() // If splice called poll.Splice here, we'd get err == syscall.EINVAL @@ -238,7 +213,7 @@ func testSpliceNoUnixpacket(t *testing.T) { } func testSpliceNoUnixgram(t *testing.T) { - addr, err := ResolveUnixAddr("unixgram", testUnixAddr()) + addr, err := ResolveUnixAddr("unixgram", testUnixAddr(t)) if err != nil { t.Fatal(err) } @@ -248,10 +223,7 @@ func testSpliceNoUnixgram(t *testing.T) { t.Fatal(err) } defer up.Close() - clientDown, serverDown, err := spliceTestSocketPair("tcp") - if err != nil { - t.Fatal(err) - } + clientDown, serverDown := spliceTestSocketPair(t, "tcp") defer clientDown.Close() defer serverDown.Close() // Analogous to testSpliceNoUnixpacket. @@ -285,10 +257,7 @@ func (tc spliceTestCase) bench(b *testing.B) { // To benchmark the genericReadFrom code path, set this to false. useSplice := true - clientUp, serverUp, err := spliceTestSocketPair(tc.upNet) - if err != nil { - b.Fatal(err) - } + clientUp, serverUp := spliceTestSocketPair(b, tc.upNet) defer serverUp.Close() cleanup, err := startSpliceClient(clientUp, "w", tc.chunkSize, tc.chunkSize*b.N) @@ -297,10 +266,7 @@ func (tc spliceTestCase) bench(b *testing.B) { } defer cleanup() - clientDown, serverDown, err := spliceTestSocketPair(tc.downNet) - if err != nil { - b.Fatal(err) - } + clientDown, serverDown := spliceTestSocketPair(b, tc.downNet) defer serverDown.Close() cleanup, err = startSpliceClient(clientDown, "r", tc.chunkSize, tc.chunkSize*b.N) @@ -328,11 +294,9 @@ func (tc spliceTestCase) bench(b *testing.B) { } } -func spliceTestSocketPair(net string) (client, server Conn, err error) { - ln, err := newLocalListener(net) - if err != nil { - return nil, nil, err - } +func spliceTestSocketPair(t testing.TB, net string) (client, server Conn) { + t.Helper() + ln := newLocalListener(t, net) defer ln.Close() var cerr, serr error acceptDone := make(chan struct{}) @@ -346,15 +310,15 @@ func spliceTestSocketPair(net string) (client, server Conn, err error) { if server != nil { server.Close() } - return nil, nil, cerr + t.Fatal(cerr) } if serr != nil { if client != nil { client.Close() } - return nil, nil, serr + t.Fatal(serr) } - return client, server, nil + return client, server } func startSpliceClient(conn Conn, op string, chunkSize, totalSize int) (func(), error) { diff --git a/src/net/sys_cloexec.go b/src/net/sys_cloexec.go index a32483e2df94e4..6e61d40c192531 100644 --- a/src/net/sys_cloexec.go +++ b/src/net/sys_cloexec.go @@ -5,8 +5,7 @@ // This file implements sysSocket for platforms that do not provide a fast path // for setting SetNonblock and CloseOnExec. -//go:build aix || darwin || (solaris && !illumos) -// +build aix darwin solaris,!illumos +//go:build aix || darwin package net diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index 19a90143f34861..6bad0e8f8bb93b 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -8,6 +8,7 @@ import ( "context" "internal/itoa" "io" + "net/netip" "os" "syscall" "time" @@ -23,6 +24,20 @@ type TCPAddr struct { Zone string // IPv6 scoped addressing zone } +// AddrPort returns the TCPAddr a as a netip.AddrPort. +// +// If a.Port does not fit in a uint16, it's silently truncated. +// +// If a is nil, a zero value is returned. +func (a *TCPAddr) AddrPort() netip.AddrPort { + if a == nil { + return netip.AddrPort{} + } + na, _ := netip.AddrFromSlice(a.IP) + na = na.WithZone(a.Zone) + return netip.AddrPortFrom(na, uint16(a.Port)) +} + // Network returns the address's network name, "tcp". func (a *TCPAddr) Network() string { return "tcp" } @@ -81,6 +96,17 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) { return addrs.forResolve(network, address).(*TCPAddr), nil } +// TCPAddrFromAddrPort returns addr as a TCPAddr. If addr.IsValid() is false, +// then the returned TCPAddr will contain a nil IP field, indicating an +// address family-agnostic unspecified address. +func TCPAddrFromAddrPort(addr netip.AddrPort) *TCPAddr { + return &TCPAddr{ + IP: addr.Addr().AsSlice(), + Zone: addr.Addr().Zone(), + Port: int(addr.Port()), + } +} + // TCPConn is an implementation of the Conn interface for TCP network // connections. type TCPConn struct { diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go index 768d03b06cefa0..435335e92e8edb 100644 --- a/src/net/tcpsock_plan9.go +++ b/src/net/tcpsock_plan9.go @@ -15,8 +15,11 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) { } func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { - if testHookDialTCP != nil { - return testHookDialTCP(ctx, sd.network, laddr, raddr) + if h := sd.testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) + } + if h := testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) } return sd.doDialTCP(ctx, laddr, raddr) } diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go index 7c4523c5eea8ec..1c91170c500915 100644 --- a/src/net/tcpsock_posix.go +++ b/src/net/tcpsock_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net @@ -56,8 +55,11 @@ func (c *TCPConn) readFrom(r io.Reader) (int64, error) { } func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { - if testHookDialTCP != nil { - return testHookDialTCP(ctx, sd.network, laddr, raddr) + if h := sd.testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) + } + if h := testHookDialTCP; h != nil { + return h(ctx, sd.network, laddr, raddr) } return sd.doDialTCP(ctx, laddr, raddr) } diff --git a/src/net/tcpsock_test.go b/src/net/tcpsock_test.go index 9c9f1eae93d832..ae65788a73dc5e 100644 --- a/src/net/tcpsock_test.go +++ b/src/net/tcpsock_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -388,10 +387,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) { t.Log(err) continue } - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() defer ls.teardown() ch := make(chan error, 1) handler := func(ls *localServer, ln Listener) { ls.transponder(ln, ch) } @@ -627,10 +623,7 @@ func TestTCPSelfConnect(t *testing.T) { t.Skip("known-broken test on windows") } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") var d Dialer c, err := d.Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { @@ -677,10 +670,7 @@ func TestTCPBig(t *testing.T) { for _, writev := range []bool{false, true} { t.Run(fmt.Sprintf("writev=%v", writev), func(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() x := int(1 << 30) @@ -724,10 +714,7 @@ func TestTCPBig(t *testing.T) { } func TestCopyPipeIntoTCP(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() errc := make(chan error, 1) @@ -795,10 +782,7 @@ func TestCopyPipeIntoTCP(t *testing.T) { } func BenchmarkSetReadDeadline(b *testing.B) { - ln, err := newLocalListener("tcp") - if err != nil { - b.Fatal(err) - } + ln := newLocalListener(b, "tcp") defer ln.Close() var serv Conn done := make(chan error) diff --git a/src/net/tcpsock_unix_test.go b/src/net/tcpsock_unix_test.go index 41bd229132d912..b14670bc6728be 100644 --- a/src/net/tcpsock_unix_test.go +++ b/src/net/tcpsock_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package net @@ -23,10 +22,7 @@ func TestTCPSpuriousConnSetupCompletion(t *testing.T) { t.Skip("skipping in short mode") } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") var wg sync.WaitGroup wg.Add(1) go func(ln Listener) { diff --git a/src/net/tcpsockopt_posix.go b/src/net/tcpsockopt_posix.go index d08832adc04c98..d708f048758976 100644 --- a/src/net/tcpsockopt_posix.go +++ b/src/net/tcpsockopt_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows +//go:build unix || windows package net diff --git a/src/net/tcpsockopt_stub.go b/src/net/tcpsockopt_stub.go index 028d5fd29cff8e..0fe91829c0e05a 100644 --- a/src/net/tcpsockopt_stub.go +++ b/src/net/tcpsockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package net diff --git a/src/net/tcpsockopt_unix.go b/src/net/tcpsockopt_unix.go index a945889e00e86f..bdcdc402398394 100644 --- a/src/net/tcpsockopt_unix.go +++ b/src/net/tcpsockopt_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || freebsd || linux || netbsd -// +build aix freebsd linux netbsd package net diff --git a/src/net/testdata/search-single-dot-resolv.conf b/src/net/testdata/search-single-dot-resolv.conf new file mode 100644 index 00000000000000..934cd3e97c0e73 --- /dev/null +++ b/src/net/testdata/search-single-dot-resolv.conf @@ -0,0 +1,5 @@ +# /etc/resolv.conf + +domain localdomain +search . +nameserver 8.8.8.8 diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index 5c3084f8a7c9fd..1f7afc57665cda 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -28,7 +28,6 @@ type Reader struct { // should be reading from an io.LimitReader or similar Reader to bound // the size of responses. func NewReader(r *bufio.Reader) *Reader { - commonHeaderOnce.Do(initCommonHeader) return &Reader{R: r} } @@ -88,7 +87,6 @@ func (r *Reader) readLineSlice() ([]byte, error) { // and the second will return "Line 2". // // Empty lines are never continued. -// func (r *Reader) ReadContinuedLine() (string, error) { line, err := r.readContinuedLineSlice(noValidation) return string(line), err @@ -217,9 +215,12 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa } // ReadCodeLine reads a response code line of the form +// // code message +// // where code is a three-digit status code and the message // extends to the rest of the line. An example of such a line is: +// // 220 plan9.bell-labs.com ESMTP // // If the prefix of the status does not match the digits in expectCode, @@ -230,7 +231,6 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa // If the response is multi-line, ReadCodeLine returns an error. // // An expectCode <= 0 disables the check of the status code. -// func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) if err == nil && continued { @@ -254,10 +254,10 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for // details of another form of response accepted: // -// code-message line 1 -// message line 2 -// ... -// code message line n +// code-message line 1 +// message line 2 +// ... +// code message line n // // If the prefix of the status does not match the digits in expectCode, // ReadResponse returns with err set to &Error{code, message}. @@ -265,7 +265,6 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // the status is not in the range [310,319]. // // An expectCode <= 0 disables the check of the status code. -// func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) multi := continued @@ -460,6 +459,8 @@ func (r *Reader) ReadDotLines() ([]string, error) { return v, err } +var colon = []byte(":") + // ReadMIMEHeader reads a MIME-style header from r. // The header is a sequence of possibly continued Key: Value lines // ending in a blank line. @@ -479,7 +480,6 @@ func (r *Reader) ReadDotLines() ([]string, error) { // "My-Key": {"Value 1", "Value 2"}, // "Long-Key": {"Even Longer Value"}, // } -// func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { // Avoid lots of small slice allocations later by allocating one // large one ahead of time which we'll cut up into smaller @@ -508,11 +508,11 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { } // Key ends at first colon. - i := bytes.IndexByte(kv, ':') - if i < 0 { + k, v, ok := bytes.Cut(kv, colon) + if !ok { return m, ProtocolError("malformed MIME header line: " + string(kv)) } - key := canonicalMIMEHeaderKey(kv[:i]) + key := canonicalMIMEHeaderKey(k) // As per RFC 7230 field-name is a token, tokens consist of one or more chars. // We could return a ProtocolError here, but better to be liberal in what we @@ -522,11 +522,7 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { } // Skip initial spaces in value. - i++ // skip colon - for i < len(kv) && (kv[i] == ' ' || kv[i] == '\t') { - i++ - } - value := string(kv[i:]) + value := strings.TrimLeft(string(v), " \t") vv := m[key] if vv == nil && len(strs) > 0 { @@ -561,6 +557,8 @@ func mustHaveFieldNameColon(line []byte) error { return nil } +var nl = []byte("\n") + // upcomingHeaderNewlines returns an approximation of the number of newlines // that will be in this header. If it gets confused, it returns 0. func (r *Reader) upcomingHeaderNewlines() (n int) { @@ -571,17 +569,7 @@ func (r *Reader) upcomingHeaderNewlines() (n int) { return } peek, _ := r.R.Peek(s) - for len(peek) > 0 { - i := bytes.IndexByte(peek, '\n') - if i < 3 { - // Not present (-1) or found within the next few bytes, - // implying we're at the end ("\r\n\r\n" or "\n\n") - return - } - n++ - peek = peek[i+1:] - } - return + return bytes.Count(peek, nl) } // CanonicalMIMEHeaderKey returns the canonical format of the @@ -593,8 +581,6 @@ func (r *Reader) upcomingHeaderNewlines() (n int) { // If s contains a space or invalid header field bytes, it is // returned without modifications. func CanonicalMIMEHeaderKey(s string) string { - commonHeaderOnce.Do(initCommonHeader) - // Quick check for canonical encoding. upper := true for i := 0; i < len(s); i++ { @@ -617,11 +603,12 @@ const toLower = 'a' - 'A' // validHeaderFieldByte reports whether b is a valid byte in a header // field name. RFC 7230 says: -// header-field = field-name ":" OWS field-value OWS -// field-name = token -// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / -// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA -// token = 1*tchar +// +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// token = 1*tchar func validHeaderFieldByte(b byte) bool { return int(b) < len(isTokenTable) && isTokenTable[b] } @@ -656,6 +643,7 @@ func canonicalMIMEHeaderKey(a []byte) string { a[i] = c upper = c == '-' // for next time } + commonHeaderOnce.Do(initCommonHeader) // The compiler recognizes m[string(byteSlice)] as a special // case, so a copy of a's bytes into a new string does not // happen in this map lookup: diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go index 3124d438fa57d4..d11d40f1cf10b3 100644 --- a/src/net/textproto/reader_test.go +++ b/src/net/textproto/reader_test.go @@ -8,8 +8,10 @@ import ( "bufio" "bytes" "io" + "net" "reflect" "strings" + "sync" "testing" ) @@ -324,6 +326,33 @@ func TestCommonHeaders(t *testing.T) { } } +func TestIssue46363(t *testing.T) { + // Regression test for data race reported in issue 46363: + // ReadMIMEHeader reads commonHeader before commonHeader has been initialized. + // Run this test with the race detector enabled to catch the reported data race. + + // Reset commonHeaderOnce, so that commonHeader will have to be initialized + commonHeaderOnce = sync.Once{} + commonHeader = nil + + // Test for data race by calling ReadMIMEHeader and CanonicalMIMEHeaderKey concurrently + + // Send MIME header over net.Conn + r, w := net.Pipe() + go func() { + // ReadMIMEHeader calls canonicalMIMEHeaderKey, which reads from commonHeader + NewConn(r).ReadMIMEHeader() + }() + w.Write([]byte("A: 1\r\nB: 2\r\nC: 3\r\n\r\n")) + + // CanonicalMIMEHeaderKey calls commonHeaderOnce.Do(initCommonHeader) which initializes commonHeader + CanonicalMIMEHeaderKey("a") + + if commonHeader == nil { + t.Fatal("CanonicalMIMEHeaderKey should initialize commonHeader") + } +} + var clientHeaders = strings.Replace(`Host: golang.org Connection: keep-alive Cache-Control: max-age=0 diff --git a/src/net/textproto/textproto.go b/src/net/textproto/textproto.go index 8fd781e7775745..70038d58886a32 100644 --- a/src/net/textproto/textproto.go +++ b/src/net/textproto/textproto.go @@ -22,7 +22,6 @@ // // Conn, a convenient packaging of Reader, Writer, and Pipeline for use // with a single network connection. -// package textproto import ( @@ -110,8 +109,7 @@ func Dial(network, addr string) (*Conn, error) { // return nil, err // } // return c.ReadCodeLine(250) -// -func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) { +func (c *Conn) Cmd(format string, args ...any) (id uint, err error) { id = c.Next() c.StartRequest(id) err = c.PrintfLine(format, args...) diff --git a/src/net/textproto/writer.go b/src/net/textproto/writer.go index 33c146c0220404..2ece3f511b9864 100644 --- a/src/net/textproto/writer.go +++ b/src/net/textproto/writer.go @@ -26,7 +26,7 @@ var crnl = []byte{'\r', '\n'} var dotcrnl = []byte{'.', '\r', '\n'} // PrintfLine writes the formatted output followed by \r\n. -func (w *Writer) PrintfLine(format string, args ...interface{}) error { +func (w *Writer) PrintfLine(format string, args ...any) error { w.closeDot() fmt.Fprintf(w.W, format, args...) w.W.Write(crnl) diff --git a/src/net/textproto/writer_test.go b/src/net/textproto/writer_test.go index 2afef11b5eeaea..8f11b107888702 100644 --- a/src/net/textproto/writer_test.go +++ b/src/net/textproto/writer_test.go @@ -6,12 +6,12 @@ package textproto import ( "bufio" - "bytes" + "strings" "testing" ) func TestPrintfLine(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(bufio.NewWriter(&buf)) err := w.PrintfLine("foo %d", 123) if s := buf.String(); s != "foo 123\r\n" || err != nil { @@ -20,7 +20,7 @@ func TestPrintfLine(t *testing.T) { } func TestDotWriter(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(bufio.NewWriter(&buf)) d := w.DotWriter() n, err := d.Write([]byte("abc\n.def\n..ghi\n.jkl\n.")) @@ -35,7 +35,7 @@ func TestDotWriter(t *testing.T) { } func TestDotWriterCloseEmptyWrite(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(bufio.NewWriter(&buf)) d := w.DotWriter() n, err := d.Write([]byte{}) @@ -50,7 +50,7 @@ func TestDotWriterCloseEmptyWrite(t *testing.T) { } func TestDotWriterCloseNoWrite(t *testing.T) { - var buf bytes.Buffer + var buf strings.Builder w := NewWriter(bufio.NewWriter(&buf)) d := w.DotWriter() d.Close() diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index e1cf1467c36f00..52ddf8c4298707 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -93,53 +92,35 @@ func TestDialTimeout(t *testing.T) { } } -var dialTimeoutMaxDurationTests = []struct { - timeout time.Duration - delta time.Duration // for deadline -}{ - // Large timeouts that will overflow an int64 unix nanos. - {1<<63 - 1, 0}, - {0, 1<<63 - 1}, -} - func TestDialTimeoutMaxDuration(t *testing.T) { - if runtime.GOOS == "openbsd" { - testenv.SkipFlaky(t, 15157) - } - - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } - defer ln.Close() + ln := newLocalListener(t, "tcp") + defer func() { + if err := ln.Close(); err != nil { + t.Error(err) + } + }() - for i, tt := range dialTimeoutMaxDurationTests { - ch := make(chan error) - max := time.NewTimer(250 * time.Millisecond) - defer max.Stop() - go func() { + for _, tt := range []struct { + timeout time.Duration + delta time.Duration // for deadline + }{ + // Large timeouts that will overflow an int64 unix nanos. + {1<<63 - 1, 0}, + {0, 1<<63 - 1}, + } { + t.Run(fmt.Sprintf("timeout=%s/delta=%s", tt.timeout, tt.delta), func(t *testing.T) { d := Dialer{Timeout: tt.timeout} if tt.delta != 0 { d.Deadline = time.Now().Add(tt.delta) } c, err := d.Dial(ln.Addr().Network(), ln.Addr().String()) - if err == nil { - c.Close() - } - ch <- err - }() - - select { - case <-max.C: - t.Fatalf("#%d: Dial didn't return in an expected time", i) - case err := <-ch: - if perr := parseDialError(err); perr != nil { - t.Error(perr) - } if err != nil { - t.Errorf("#%d: %v", i, err) + t.Fatal(err) } - } + if err := c.Close(); err != nil { + t.Error(err) + } + }) } } @@ -163,10 +144,7 @@ func TestAcceptTimeout(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() var wg sync.WaitGroup @@ -219,10 +197,7 @@ func TestAcceptTimeoutMustReturn(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() max := time.NewTimer(time.Second) @@ -265,14 +240,10 @@ func TestAcceptTimeoutMustNotReturn(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() - max := time.NewTimer(100 * time.Millisecond) - defer max.Stop() + maxch := make(chan *time.Timer) ch := make(chan error) go func() { if err := ln.(*TCPListener).SetDeadline(time.Now().Add(-5 * time.Second)); err != nil { @@ -281,10 +252,14 @@ func TestAcceptTimeoutMustNotReturn(t *testing.T) { if err := ln.(*TCPListener).SetDeadline(noDeadline); err != nil { t.Error(err) } + maxch <- time.NewTimer(100 * time.Millisecond) _, err := ln.Accept() ch <- err }() + max := <-maxch + defer max.Stop() + select { case err := <-ch: if perr := parseAcceptError(err); perr != nil { @@ -308,6 +283,7 @@ var readTimeoutTests = []struct { {50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}}, } +// There is a very similar copy of this in os/timeout_test.go. func TestReadTimeout(t *testing.T) { handler := func(ls *localServer, ln Listener) { c, err := ln.Accept() @@ -318,10 +294,7 @@ func TestReadTimeout(t *testing.T) { c.Write([]byte("READ TIMEOUT TEST")) defer c.Close() } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -362,6 +335,7 @@ func TestReadTimeout(t *testing.T) { } } +// There is a very similar copy of this in os/timeout_test.go. func TestReadTimeoutMustNotReturn(t *testing.T) { t.Parallel() @@ -370,10 +344,7 @@ func TestReadTimeoutMustNotReturn(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -382,8 +353,7 @@ func TestReadTimeoutMustNotReturn(t *testing.T) { } defer c.Close() - max := time.NewTimer(100 * time.Millisecond) - defer max.Stop() + maxch := make(chan *time.Timer) ch := make(chan error) go func() { if err := c.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil { @@ -395,11 +365,15 @@ func TestReadTimeoutMustNotReturn(t *testing.T) { if err := c.SetReadDeadline(noDeadline); err != nil { t.Error(err) } + maxch <- time.NewTimer(100 * time.Millisecond) var b [1]byte _, err := c.Read(b[:]) ch <- err }() + max := <-maxch + defer max.Stop() + select { case err := <-ch: if perr := parseReadError(err); perr != nil { @@ -437,10 +411,7 @@ func TestReadFromTimeout(t *testing.T) { c.WriteTo([]byte("READFROM TIMEOUT TEST"), dst) } } - ls, err := newLocalPacketServer("udp") - if err != nil { - t.Fatal(err) - } + ls := newLocalPacketServer(t, "udp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -497,13 +468,11 @@ var writeTimeoutTests = []struct { {10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}}, } +// There is a very similar copy of this in os/timeout_test.go. func TestWriteTimeout(t *testing.T) { t.Parallel() - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() for i, tt := range writeTimeoutTests { @@ -540,6 +509,7 @@ func TestWriteTimeout(t *testing.T) { } } +// There is a very similar copy of this in os/timeout_test.go. func TestWriteTimeoutMustNotReturn(t *testing.T) { t.Parallel() @@ -548,10 +518,7 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -560,8 +527,7 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { } defer c.Close() - max := time.NewTimer(100 * time.Millisecond) - defer max.Stop() + maxch := make(chan *time.Timer) ch := make(chan error) go func() { if err := c.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil { @@ -573,6 +539,7 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { if err := c.SetWriteDeadline(noDeadline); err != nil { t.Error(err) } + maxch <- time.NewTimer(100 * time.Millisecond) var b [1]byte for { if _, err := c.Write(b[:]); err != nil { @@ -582,6 +549,9 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { } }() + max := <-maxch + defer max.Stop() + select { case err := <-ch: if perr := parseWriteError(err); perr != nil { @@ -600,24 +570,10 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { } } -var writeToTimeoutTests = []struct { - timeout time.Duration - xerrs [2]error // expected errors in transition -}{ - // Tests that write deadlines work, even if there's buffer - // space available to write. - {-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}}, - - {10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}}, -} - func TestWriteToTimeout(t *testing.T) { t.Parallel() - c1, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, "udp") defer c1.Close() host, _, err := SplitHostPort(c1.LocalAddr().String()) @@ -625,47 +581,117 @@ func TestWriteToTimeout(t *testing.T) { t.Fatal(err) } - for i, tt := range writeToTimeoutTests { - c2, err := ListenPacket(c1.LocalAddr().Network(), JoinHostPort(host, "0")) - if err != nil { - t.Fatal(err) - } - defer c2.Close() + timeouts := []time.Duration{ + -5 * time.Second, + 10 * time.Millisecond, + } - if err := c2.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil { - t.Fatalf("#%d: %v", i, err) - } - for j, xerr := range tt.xerrs { - for { + for _, timeout := range timeouts { + t.Run(fmt.Sprint(timeout), func(t *testing.T) { + c2, err := ListenPacket(c1.LocalAddr().Network(), JoinHostPort(host, "0")) + if err != nil { + t.Fatal(err) + } + defer c2.Close() + + if err := c2.SetWriteDeadline(time.Now().Add(timeout)); err != nil { + t.Fatalf("SetWriteDeadline: %v", err) + } + backoff := 1 * time.Millisecond + nDeadlineExceeded := 0 + for j := 0; nDeadlineExceeded < 2; j++ { n, err := c2.WriteTo([]byte("WRITETO TIMEOUT TEST"), c1.LocalAddr()) - if xerr != nil { - if perr := parseWriteError(err); perr != nil { - t.Errorf("#%d/%d: %v", i, j, perr) - } - if !isDeadlineExceeded(err) { - t.Fatalf("#%d/%d: %v", i, j, err) - } + t.Logf("#%d: WriteTo: %d, %v", j, n, err) + if err == nil && timeout >= 0 && nDeadlineExceeded == 0 { + // If the timeout is nonnegative, some number of WriteTo calls may + // succeed before the timeout takes effect. + t.Logf("WriteTo succeeded; sleeping %v", timeout/3) + time.Sleep(timeout / 3) + continue } - if err == nil { - time.Sleep(tt.timeout / 3) + if isENOBUFS(err) { + t.Logf("WriteTo: %v", err) + // We're looking for a deadline exceeded error, but if the kernel's + // network buffers are saturated we may see ENOBUFS instead (see + // https://go.dev/issue/49930). Give it some time to unsaturate. + time.Sleep(backoff) + backoff *= 2 continue } + if perr := parseWriteError(err); perr != nil { + t.Errorf("failed to parse error: %v", perr) + } + if !isDeadlineExceeded(err) { + t.Errorf("error is not 'deadline exceeded'") + } if n != 0 { - t.Fatalf("#%d/%d: wrote %d; want 0", i, j, n) + t.Errorf("unexpectedly wrote %d bytes", n) } - break + if !t.Failed() { + t.Logf("WriteTo timed out as expected") + } + nDeadlineExceeded++ } - } + }) } } -func TestReadTimeoutFluctuation(t *testing.T) { - t.Parallel() +const ( + // minDynamicTimeout is the minimum timeout to attempt for + // tests that automatically increase timeouts until success. + // + // Lower values may allow tests to succeed more quickly if the value is close + // to the true minimum, but may require more iterations (and waste more time + // and CPU power on failed attempts) if the timeout is too low. + minDynamicTimeout = 1 * time.Millisecond + + // maxDynamicTimeout is the maximum timeout to attempt for + // tests that automatically increase timeouts until succeess. + // + // This should be a strict upper bound on the latency required to hit a + // timeout accurately, even on a slow or heavily-loaded machine. If a test + // would increase the timeout beyond this value, the test fails. + maxDynamicTimeout = 4 * time.Second +) - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) +// timeoutUpperBound returns the maximum time that we expect a timeout of +// duration d to take to return the caller. +func timeoutUpperBound(d time.Duration) time.Duration { + switch runtime.GOOS { + case "openbsd", "netbsd": + // NetBSD and OpenBSD seem to be unable to reliably hit deadlines even when + // the absolute durations are long. + // In https://build.golang.org/log/c34f8685d020b98377dd4988cd38f0c5bd72267e, + // we observed that an openbsd-amd64-68 builder took 4.090948779s for a + // 2.983020682s timeout (37.1% overhead). + // (See https://go.dev/issue/50189 for further detail.) + // Give them lots of slop to compensate. + return d * 3 / 2 + } + // Other platforms seem to hit their deadlines more reliably, + // at least when they are long enough to cover scheduling jitter. + return d * 11 / 10 +} + +// nextTimeout returns the next timeout to try after an operation took the given +// actual duration with a timeout shorter than that duration. +func nextTimeout(actual time.Duration) (next time.Duration, ok bool) { + if actual >= maxDynamicTimeout { + return maxDynamicTimeout, false } + // Since the previous attempt took actual, we can't expect to beat that + // duration by any significant margin. Try the next attempt with an arbitrary + // factor above that, so that our growth curve is at least exponential. + next = actual * 5 / 4 + if next > maxDynamicTimeout { + return maxDynamicTimeout, true + } + return next, true +} + +// There is a very similar copy of this in os/timeout_test.go. +func TestReadTimeoutFluctuation(t *testing.T) { + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -674,31 +700,55 @@ func TestReadTimeoutFluctuation(t *testing.T) { } defer c.Close() - max := time.NewTimer(time.Second) - defer max.Stop() - ch := make(chan error) - go timeoutReceiver(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) + d := minDynamicTimeout + b := make([]byte, 256) + for { + t.Logf("SetReadDeadline(+%v)", d) + t0 := time.Now() + deadline := t0.Add(d) + if err = c.SetReadDeadline(deadline); err != nil { + t.Fatalf("SetReadDeadline(%v): %v", deadline, err) + } + var n int + n, err = c.Read(b) + t1 := time.Now() - select { - case <-max.C: - t.Fatal("Read took over 1s; expected 0.1s") - case err := <-ch: + if n != 0 || err == nil || !err.(Error).Timeout() { + t.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) + } if perr := parseReadError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { - t.Fatal(err) + t.Errorf("Read error is not DeadlineExceeded: %v", err) } + + actual := t1.Sub(t0) + if t1.Before(deadline) { + t.Errorf("Read took %s; expected at least %s", actual, d) + } + if t.Failed() { + return + } + if want := timeoutUpperBound(d); actual > want { + next, ok := nextTimeout(actual) + if !ok { + t.Fatalf("Read took %s; expected at most %v", actual, want) + } + // Maybe this machine is too slow to reliably schedule goroutines within + // the requested duration. Increase the timeout and try again. + t.Logf("Read took %s (expected %s); trying with longer timeout", actual, d) + d = next + continue + } + + break } } +// There is a very similar copy of this in os/timeout_test.go. func TestReadFromTimeoutFluctuation(t *testing.T) { - t.Parallel() - - c1, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, "udp") defer c1.Close() c2, err := Dial(c1.LocalAddr().Network(), c1.LocalAddr().String()) @@ -707,36 +757,59 @@ func TestReadFromTimeoutFluctuation(t *testing.T) { } defer c2.Close() - max := time.NewTimer(time.Second) - defer max.Stop() - ch := make(chan error) - go timeoutPacketReceiver(c2.(PacketConn), 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) + d := minDynamicTimeout + b := make([]byte, 256) + for { + t.Logf("SetReadDeadline(+%v)", d) + t0 := time.Now() + deadline := t0.Add(d) + if err = c2.SetReadDeadline(deadline); err != nil { + t.Fatalf("SetReadDeadline(%v): %v", deadline, err) + } + var n int + n, _, err = c2.(PacketConn).ReadFrom(b) + t1 := time.Now() - select { - case <-max.C: - t.Fatal("ReadFrom took over 1s; expected 0.1s") - case err := <-ch: + if n != 0 || err == nil || !err.(Error).Timeout() { + t.Errorf("ReadFrom did not return (0, timeout): (%d, %v)", n, err) + } if perr := parseReadError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { - t.Fatal(err) + t.Errorf("ReadFrom error is not DeadlineExceeded: %v", err) + } + + actual := t1.Sub(t0) + if t1.Before(deadline) { + t.Errorf("ReadFrom took %s; expected at least %s", actual, d) + } + if t.Failed() { + return } + if want := timeoutUpperBound(d); actual > want { + next, ok := nextTimeout(actual) + if !ok { + t.Fatalf("ReadFrom took %s; expected at most %s", actual, want) + } + // Maybe this machine is too slow to reliably schedule goroutines within + // the requested duration. Increase the timeout and try again. + t.Logf("ReadFrom took %s (expected %s); trying with longer timeout", actual, d) + d = next + continue + } + + break } } func TestWriteTimeoutFluctuation(t *testing.T) { - t.Parallel() - switch runtime.GOOS { case "plan9": t.Skipf("not supported on %s", runtime.GOOS) } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -745,33 +818,77 @@ func TestWriteTimeoutFluctuation(t *testing.T) { } defer c.Close() - d := time.Second - if iOS() { - d = 3 * time.Second // see golang.org/issue/10775 - } - max := time.NewTimer(d) - defer max.Stop() - ch := make(chan error) - go timeoutTransmitter(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) + d := minDynamicTimeout + for { + t.Logf("SetWriteDeadline(+%v)", d) + t0 := time.Now() + deadline := t0.Add(d) + if err = c.SetWriteDeadline(deadline); err != nil { + t.Fatalf("SetWriteDeadline(%v): %v", deadline, err) + } + var n int64 + for { + var dn int + dn, err = c.Write([]byte("TIMEOUT TRANSMITTER")) + n += int64(dn) + if err != nil { + break + } + } + t1 := time.Now() - select { - case <-max.C: - t.Fatalf("Write took over %v; expected 0.1s", d) - case err := <-ch: + if err == nil || !err.(Error).Timeout() { + t.Fatalf("Write did not return (any, timeout): (%d, %v)", n, err) + } if perr := parseWriteError(err); perr != nil { t.Error(perr) } if !isDeadlineExceeded(err) { - t.Fatal(err) + t.Errorf("Write error is not DeadlineExceeded: %v", err) + } + + actual := t1.Sub(t0) + if t1.Before(deadline) { + t.Errorf("Write took %s; expected at least %s", actual, d) + } + if t.Failed() { + return } + if want := timeoutUpperBound(d); actual > want { + if n > 0 { + // SetWriteDeadline specifies a time “after which I/O operations fail + // instead of blocking”. However, the kernel's send buffer is not yet + // full, we may be able to write some arbitrary (but finite) number of + // bytes to it without blocking. + t.Logf("Wrote %d bytes into send buffer; retrying until buffer is full", n) + if d <= maxDynamicTimeout/2 { + // We don't know how long the actual write loop would have taken if + // the buffer were full, so just guess and double the duration so that + // the next attempt can make twice as much progress toward filling it. + d *= 2 + } + } else if next, ok := nextTimeout(actual); !ok { + t.Fatalf("Write took %s; expected at most %s", actual, want) + } else { + // Maybe this machine is too slow to reliably schedule goroutines within + // the requested duration. Increase the timeout and try again. + t.Logf("Write took %s (expected %s); trying with longer timeout", actual, d) + d = next + } + continue + } + + break } } +// There is a very similar copy of this in os/timeout_test.go. func TestVariousDeadlines(t *testing.T) { t.Parallel() testVariousDeadlines(t) } +// There is a very similar copy of this in os/timeout_test.go. func TestVariousDeadlines1Proc(t *testing.T) { // Cannot use t.Parallel - modifies global GOMAXPROCS. if testing.Short() { @@ -781,6 +898,7 @@ func TestVariousDeadlines1Proc(t *testing.T) { testVariousDeadlines(t) } +// There is a very similar copy of this in os/timeout_test.go. func TestVariousDeadlines4Proc(t *testing.T) { // Cannot use t.Parallel - modifies global GOMAXPROCS. if testing.Short() { @@ -819,10 +937,7 @@ func testVariousDeadlines(t *testing.T) { c.Close() } } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -860,35 +975,23 @@ func testVariousDeadlines(t *testing.T) { name := fmt.Sprintf("%v %d/%d", timeout, run, numRuns) t.Log(name) - tooSlow := time.NewTimer(5 * time.Second) - defer tooSlow.Stop() - c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String()) if err != nil { t.Fatal(err) } - ch := make(chan result, 1) - go func() { - t0 := time.Now() - if err := c.SetDeadline(t0.Add(timeout)); err != nil { - t.Error(err) - } - n, err := io.Copy(io.Discard, c) - dt := time.Since(t0) - c.Close() - ch <- result{n, err, dt} - }() + t0 := time.Now() + if err := c.SetDeadline(t0.Add(timeout)); err != nil { + t.Error(err) + } + n, err := io.Copy(io.Discard, c) + dt := time.Since(t0) + c.Close() - select { - case res := <-ch: - if nerr, ok := res.err.(Error); ok && nerr.Timeout() { - t.Logf("%v: good timeout after %v; %d bytes", name, res.d, res.n) - } else { - t.Fatalf("%v: Copy = %d, %v; want timeout", name, res.n, res.err) - } - case <-tooSlow.C: - t.Fatalf("%v: client stuck in Dial+Copy", name) + if nerr, ok := err.(Error); ok && nerr.Timeout() { + t.Logf("%v: good timeout after %v; %d bytes", name, dt, n) + } else { + t.Fatalf("%v: Copy = %d, %v; want timeout", name, n, err) } } } @@ -954,10 +1057,7 @@ func TestReadWriteProlongedTimeout(t *testing.T) { }() wg.Wait() } - ls, err := newLocalServer("tcp") - if err != nil { - t.Fatal(err) - } + ls := newLocalServer(t, "tcp") defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -976,6 +1076,7 @@ func TestReadWriteProlongedTimeout(t *testing.T) { } } +// There is a very similar copy of this in os/timeout_test.go. func TestReadWriteDeadlineRace(t *testing.T) { t.Parallel() @@ -984,10 +1085,7 @@ func TestReadWriteDeadlineRace(t *testing.T) { N = 50 } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() c, err := Dial(ln.Addr().Network(), ln.Addr().String()) @@ -1037,10 +1135,7 @@ func TestReadWriteDeadlineRace(t *testing.T) { // Issue 35367. func TestConcurrentSetDeadline(t *testing.T) { - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() const goroutines = 8 @@ -1049,6 +1144,7 @@ func TestConcurrentSetDeadline(t *testing.T) { var c [conns]Conn for i := 0; i < conns; i++ { + var err error c[i], err = Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { t.Fatal(err) diff --git a/src/net/udpsock.go b/src/net/udpsock.go index 70f2ce226aa7b1..e30624dea5a74e 100644 --- a/src/net/udpsock.go +++ b/src/net/udpsock.go @@ -7,6 +7,7 @@ package net import ( "context" "internal/itoa" + "net/netip" "syscall" ) @@ -26,6 +27,20 @@ type UDPAddr struct { Zone string // IPv6 scoped addressing zone } +// AddrPort returns the UDPAddr a as a netip.AddrPort. +// +// If a.Port does not fit in a uint16, it's silently truncated. +// +// If a is nil, a zero value is returned. +func (a *UDPAddr) AddrPort() netip.AddrPort { + if a == nil { + return netip.AddrPort{} + } + na, _ := netip.AddrFromSlice(a.IP) + na = na.WithZone(a.Zone) + return netip.AddrPortFrom(na, uint16(a.Port)) +} + // Network returns the address's network name, "udp". func (a *UDPAddr) Network() string { return "udp" } @@ -84,6 +99,24 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) { return addrs.forResolve(network, address).(*UDPAddr), nil } +// UDPAddrFromAddrPort returns addr as a UDPAddr. If addr.IsValid() is false, +// then the returned UDPAddr will contain a nil IP field, indicating an +// address family-agnostic unspecified address. +func UDPAddrFromAddrPort(addr netip.AddrPort) *UDPAddr { + return &UDPAddr{ + IP: addr.Addr().AsSlice(), + Zone: addr.Addr().Zone(), + Port: int(addr.Port()), + } +} + +// An addrPortUDPAddr is a netip.AddrPort-based UDP address that satisfies the Addr interface. +type addrPortUDPAddr struct { + netip.AddrPort +} + +func (addrPortUDPAddr) Network() string { return "udp" } + // UDPConn is the implementation of the Conn and PacketConn interfaces // for UDP network connections. type UDPConn struct { @@ -130,6 +163,22 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { return n, addr, err } +// ReadFromUDPAddrPort acts like ReadFrom but returns a netip.AddrPort. +// +// If c is bound to an unspecified address, the returned +// netip.AddrPort's address might be an IPv4-mapped IPv6 address. +// Use netip.Addr.Unmap to get the address without the IPv6 prefix. +func (c *UDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) { + if !c.ok() { + return 0, netip.AddrPort{}, syscall.EINVAL + } + n, addr, err = c.readFromAddrPort(b) + if err != nil { + err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + return n, addr, err +} + // ReadMsgUDP reads a message from c, copying the payload into b and // the associated out-of-band data into oob. It returns the number of // bytes copied into b, the number of bytes copied into oob, the flags @@ -138,8 +187,18 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { // The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be // used to manipulate IP-level socket options in oob. func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { + var ap netip.AddrPort + n, oobn, flags, ap, err = c.ReadMsgUDPAddrPort(b, oob) + if ap.IsValid() { + addr = UDPAddrFromAddrPort(ap) + } + return +} + +// ReadMsgUDPAddrPort is like ReadMsgUDP but returns an netip.AddrPort instead of a UDPAddr. +func (c *UDPConn) ReadMsgUDPAddrPort(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) { if !c.ok() { - return 0, 0, 0, nil, syscall.EINVAL + return 0, 0, 0, netip.AddrPort{}, syscall.EINVAL } n, oobn, flags, addr, err = c.readMsg(b, oob) if err != nil { @@ -160,6 +219,18 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { return n, err } +// WriteToUDPAddrPort acts like WriteTo but takes a netip.AddrPort. +func (c *UDPConn) WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + n, err := c.writeToAddrPort(b, addr) + if err != nil { + err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addrPortUDPAddr{addr}, Err: err} + } + return n, err +} + // WriteTo implements the PacketConn WriteTo method. func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { @@ -195,6 +266,18 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er return } +// WriteMsgUDPAddrPort is like WriteMsgUDP but takes a netip.AddrPort instead of a UDPAddr. +func (c *UDPConn) WriteMsgUDPAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) { + if !c.ok() { + return 0, 0, syscall.EINVAL + } + n, oobn, err = c.writeMsgAddrPort(b, oob, addr) + if err != nil { + err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addrPortUDPAddr{addr}, Err: err} + } + return +} + func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } // DialUDP acts like Dial for UDP networks. diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go index 1df293d1db9352..732a3b07eec0b4 100644 --- a/src/net/udpsock_plan9.go +++ b/src/net/udpsock_plan9.go @@ -7,6 +7,7 @@ package net import ( "context" "errors" + "net/netip" "os" "syscall" ) @@ -28,8 +29,27 @@ func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) { return n, addr, nil } -func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { - return 0, 0, 0, nil, syscall.EPLAN9 +func (c *UDPConn) readFromAddrPort(b []byte) (int, netip.AddrPort, error) { + // TODO: optimize. The equivalent code on posix is alloc-free. + buf := make([]byte, udpHeaderSize+len(b)) + m, err := c.fd.Read(buf) + if err != nil { + return 0, netip.AddrPort{}, err + } + if m < udpHeaderSize { + return 0, netip.AddrPort{}, errors.New("short read reading UDP header") + } + buf = buf[:m] + + h, buf := unmarshalUDPHeader(buf) + n := copy(b, buf) + ip, _ := netip.AddrFromSlice(h.raddr) + addr := netip.AddrPortFrom(ip, h.rport) + return n, addr, nil +} + +func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) { + return 0, 0, 0, netip.AddrPort{}, syscall.EPLAN9 } func (c *UDPConn) writeTo(b []byte, addr *UDPAddr) (int, error) { @@ -52,10 +72,18 @@ func (c *UDPConn) writeTo(b []byte, addr *UDPAddr) (int, error) { return len(b), nil } +func (c *UDPConn) writeToAddrPort(b []byte, addr netip.AddrPort) (int, error) { + return c.writeTo(b, UDPAddrFromAddrPort(addr)) // TODO: optimize instead of allocating +} + func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { return 0, 0, syscall.EPLAN9 } +func (c *UDPConn) writeMsgAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) { + return 0, 0, syscall.EPLAN9 +} + func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) { fd, err := dialPlan9(ctx, sd.network, laddr, raddr) if err != nil { diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go index 96fb373ce770ca..5b021d24ae6020 100644 --- a/src/net/udpsock_posix.go +++ b/src/net/udpsock_posix.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net import ( "context" + "net/netip" "syscall" ) @@ -44,27 +44,68 @@ func (a *UDPAddr) toLocal(net string) sockaddr { } func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) { - n, sa, err := c.fd.readFrom(b) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - *addr = UDPAddr{IP: sa.Addr[0:], Port: sa.Port} - case *syscall.SockaddrInet6: - *addr = UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneCache.name(int(sa.ZoneId))} - default: + var n int + var err error + switch c.fd.family { + case syscall.AF_INET: + var from syscall.SockaddrInet4 + n, err = c.fd.readFromInet4(b, &from) + if err == nil { + ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes + *addr = UDPAddr{IP: ip[:], Port: from.Port} + } + case syscall.AF_INET6: + var from syscall.SockaddrInet6 + n, err = c.fd.readFromInet6(b, &from) + if err == nil { + ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes + *addr = UDPAddr{IP: ip[:], Port: from.Port, Zone: zoneCache.name(int(from.ZoneId))} + } + } + if err != nil { // No sockaddr, so don't return UDPAddr. addr = nil } return n, addr, err } -func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) { - var sa syscall.Sockaddr - n, oobn, flags, sa, err = c.fd.readMsg(b, oob, 0) - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} - case *syscall.SockaddrInet6: - addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneCache.name(int(sa.ZoneId))} +func (c *UDPConn) readFromAddrPort(b []byte) (n int, addr netip.AddrPort, err error) { + var ip netip.Addr + var port int + switch c.fd.family { + case syscall.AF_INET: + var from syscall.SockaddrInet4 + n, err = c.fd.readFromInet4(b, &from) + if err == nil { + ip = netip.AddrFrom4(from.Addr) + port = from.Port + } + case syscall.AF_INET6: + var from syscall.SockaddrInet6 + n, err = c.fd.readFromInet6(b, &from) + if err == nil { + ip = netip.AddrFrom16(from.Addr).WithZone(zoneCache.name(int(from.ZoneId))) + port = from.Port + } + } + if err == nil { + addr = netip.AddrPortFrom(ip, uint16(port)) + } + return n, addr, err +} + +func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) { + switch c.fd.family { + case syscall.AF_INET: + var sa syscall.SockaddrInet4 + n, oobn, flags, err = c.fd.readMsgInet4(b, oob, 0, &sa) + ip := netip.AddrFrom4(sa.Addr) + addr = netip.AddrPortFrom(ip, uint16(sa.Port)) + case syscall.AF_INET6: + var sa syscall.SockaddrInet6 + n, oobn, flags, err = c.fd.readMsgInet6(b, oob, 0, &sa) + ip := netip.AddrFrom16(sa.Addr).WithZone(zoneCache.name(int(sa.ZoneId))) + addr = netip.AddrPortFrom(ip, uint16(sa.Port)) } return } @@ -76,11 +117,49 @@ func (c *UDPConn) writeTo(b []byte, addr *UDPAddr) (int, error) { if addr == nil { return 0, errMissingAddress } - sa, err := addr.sockaddr(c.fd.family) - if err != nil { - return 0, err + + switch c.fd.family { + case syscall.AF_INET: + sa, err := ipToSockaddrInet4(addr.IP, addr.Port) + if err != nil { + return 0, err + } + return c.fd.writeToInet4(b, &sa) + case syscall.AF_INET6: + sa, err := ipToSockaddrInet6(addr.IP, addr.Port, addr.Zone) + if err != nil { + return 0, err + } + return c.fd.writeToInet6(b, &sa) + default: + return 0, &AddrError{Err: "invalid address family", Addr: addr.IP.String()} + } +} + +func (c *UDPConn) writeToAddrPort(b []byte, addr netip.AddrPort) (int, error) { + if c.fd.isConnected { + return 0, ErrWriteToConnected + } + if !addr.IsValid() { + return 0, errMissingAddress + } + + switch c.fd.family { + case syscall.AF_INET: + sa, err := addrPortToSockaddrInet4(addr) + if err != nil { + return 0, err + } + return c.fd.writeToInet4(b, &sa) + case syscall.AF_INET6: + sa, err := addrPortToSockaddrInet6(addr) + if err != nil { + return 0, err + } + return c.fd.writeToInet6(b, &sa) + default: + return 0, &AddrError{Err: "invalid address family", Addr: addr.Addr().String()} } - return c.fd.writeTo(b, sa) } func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { @@ -97,6 +176,32 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error return c.fd.writeMsg(b, oob, sa) } +func (c *UDPConn) writeMsgAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) { + if c.fd.isConnected && addr.IsValid() { + return 0, 0, ErrWriteToConnected + } + if !c.fd.isConnected && !addr.IsValid() { + return 0, 0, errMissingAddress + } + + switch c.fd.family { + case syscall.AF_INET: + sa, err := addrPortToSockaddrInet4(addr) + if err != nil { + return 0, 0, err + } + return c.fd.writeMsgInet4(b, oob, &sa) + case syscall.AF_INET6: + sa, err := addrPortToSockaddrInet6(addr) + if err != nil { + return 0, 0, err + } + return c.fd.writeMsgInet6(b, oob, &sa) + default: + return 0, 0, &AddrError{Err: "invalid address family", Addr: addr.Addr().String()} + } +} + func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) { fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial", sd.Dialer.Control) if err != nil { diff --git a/src/net/udpsock_test.go b/src/net/udpsock_test.go index 0e8c3511c36e45..0ed2ff98c1a2c8 100644 --- a/src/net/udpsock_test.go +++ b/src/net/udpsock_test.go @@ -3,13 +3,13 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net import ( "errors" "internal/testenv" + "net/netip" "os" "reflect" "runtime" @@ -286,10 +286,7 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) { t.Log(err) continue } - ls, err := (&packetListener{PacketConn: c1}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&packetListener{PacketConn: c1}).newLocalServer() defer ls.teardown() ch := make(chan error, 1) handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) } @@ -334,10 +331,7 @@ func TestUDPZeroBytePayload(t *testing.T) { testenv.SkipFlaky(t, 29225) } - c, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } + c := newLocalPacketListener(t, "udp") defer c.Close() for _, genericRead := range []bool{false, true} { @@ -370,10 +364,7 @@ func TestUDPZeroByteBuffer(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - c, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } + c := newLocalPacketListener(t, "udp") defer c.Close() b := []byte("UDP ZERO BYTE BUFFER TEST") @@ -407,10 +398,7 @@ func TestUDPReadSizeError(t *testing.T) { t.Skipf("not supported on %s", runtime.GOOS) } - c1, err := newLocalPacketListener("udp") - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, "udp") defer c1.Close() c2, err := Dial("udp", c1.LocalAddr().String()) @@ -428,19 +416,14 @@ func TestUDPReadSizeError(t *testing.T) { if n != len(b1) { t.Errorf("got %d; want %d", n, len(b1)) } - c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) b2 := make([]byte, len(b1)-1) if genericRead { n, err = c1.(Conn).Read(b2) } else { n, _, err = c1.ReadFrom(b2) } - switch err { - case nil: // ReadFrom succeeds - default: // Read may timeout, it depends on the platform - if nerr, ok := err.(Error); (!ok || !nerr.Timeout()) && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZE - t.Fatal(err) - } + if err != nil && runtime.GOOS != "windows" { // Windows returns WSAEMSGSIZE + t.Fatal(err) } if n != len(b1)-1 { t.Fatalf("got %d; want %d", n, len(b1)-1) @@ -475,11 +458,95 @@ func TestUDPReadTimeout(t *testing.T) { } } +func TestAllocs(t *testing.T) { + switch runtime.GOOS { + case "plan9": + // Plan9 wasn't optimized. + t.Skipf("skipping on %v", runtime.GOOS) + } + // Optimizations are required to remove the allocs. + testenv.SkipIfOptimizationOff(t) + + conn, err := ListenUDP("udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + addr := conn.LocalAddr() + addrPort := addr.(*UDPAddr).AddrPort() + buf := make([]byte, 8) + + allocs := testing.AllocsPerRun(1000, func() { + _, _, err := conn.WriteMsgUDPAddrPort(buf, nil, addrPort) + if err != nil { + t.Fatal(err) + } + _, _, _, _, err = conn.ReadMsgUDPAddrPort(buf, nil) + if err != nil { + t.Fatal(err) + } + }) + if got := int(allocs); got != 0 { + t.Errorf("WriteMsgUDPAddrPort/ReadMsgUDPAddrPort allocated %d objects", got) + } + + allocs = testing.AllocsPerRun(1000, func() { + _, err := conn.WriteToUDPAddrPort(buf, addrPort) + if err != nil { + t.Fatal(err) + } + _, _, err = conn.ReadFromUDPAddrPort(buf) + if err != nil { + t.Fatal(err) + } + }) + if got := int(allocs); got != 0 { + t.Errorf("WriteToUDPAddrPort/ReadFromUDPAddrPort allocated %d objects", got) + } + + allocs = testing.AllocsPerRun(1000, func() { + _, err := conn.WriteTo(buf, addr) + if err != nil { + t.Fatal(err) + } + _, _, err = conn.ReadFromUDP(buf) + if err != nil { + t.Fatal(err) + } + }) + if got := int(allocs); got != 1 { + t.Errorf("WriteTo/ReadFromUDP allocated %d objects", got) + } +} + +func BenchmarkReadWriteMsgUDPAddrPort(b *testing.B) { + conn, err := ListenUDP("udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}) + if err != nil { + b.Fatal(err) + } + defer conn.Close() + addr := conn.LocalAddr().(*UDPAddr).AddrPort() + buf := make([]byte, 8) + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, _, err := conn.WriteMsgUDPAddrPort(buf, nil, addr) + if err != nil { + b.Fatal(err) + } + _, _, _, _, err = conn.ReadMsgUDPAddrPort(buf, nil) + if err != nil { + b.Fatal(err) + } + } +} + func BenchmarkWriteToReadFromUDP(b *testing.B) { conn, err := ListenUDP("udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}) if err != nil { b.Fatal(err) } + defer conn.Close() addr := conn.LocalAddr() buf := make([]byte, 8) b.ResetTimer() @@ -495,3 +562,105 @@ func BenchmarkWriteToReadFromUDP(b *testing.B) { } } } + +func BenchmarkWriteToReadFromUDPAddrPort(b *testing.B) { + conn, err := ListenUDP("udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}) + if err != nil { + b.Fatal(err) + } + defer conn.Close() + addr := conn.LocalAddr().(*UDPAddr).AddrPort() + buf := make([]byte, 8) + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _, err := conn.WriteToUDPAddrPort(buf, addr) + if err != nil { + b.Fatal(err) + } + _, _, err = conn.ReadFromUDPAddrPort(buf) + if err != nil { + b.Fatal(err) + } + } +} + +func TestUDPIPVersionReadMsg(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping on %v", runtime.GOOS) + } + conn, err := ListenUDP("udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + daddr := conn.LocalAddr().(*UDPAddr).AddrPort() + buf := make([]byte, 8) + _, err = conn.WriteToUDPAddrPort(buf, daddr) + if err != nil { + t.Fatal(err) + } + _, _, _, saddr, err := conn.ReadMsgUDPAddrPort(buf, nil) + if err != nil { + t.Fatal(err) + } + if !saddr.Addr().Is4() { + t.Error("returned AddrPort is not IPv4") + } + _, err = conn.WriteToUDPAddrPort(buf, daddr) + if err != nil { + t.Fatal(err) + } + _, _, _, soldaddr, err := conn.ReadMsgUDP(buf, nil) + if err != nil { + t.Fatal(err) + } + if len(soldaddr.IP) != 4 { + t.Error("returned UDPAddr is not IPv4") + } +} + +// TestIPv6WriteMsgUDPAddrPortTargetAddrIPVersion verifies that +// WriteMsgUDPAddrPort accepts IPv4, IPv4-mapped IPv6, and IPv6 target addresses +// on a UDPConn listening on "::". +func TestIPv6WriteMsgUDPAddrPortTargetAddrIPVersion(t *testing.T) { + if !supportsIPv6() { + t.Skip("IPv6 is not supported") + } + + switch runtime.GOOS { + case "dragonfly", "openbsd": + // DragonflyBSD's IPv6 sockets are always IPv6-only, according to the man page: + // https://www.dragonflybsd.org/cgi/web-man?command=ip6 (search for IPV6_V6ONLY). + // OpenBSD's IPv6 sockets are always IPv6-only, according to the man page: + // https://man.openbsd.org/ip6#IPV6_V6ONLY + t.Skipf("skipping on %v", runtime.GOOS) + } + + conn, err := ListenUDP("udp", nil) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + daddr4 := netip.AddrPortFrom(netip.MustParseAddr("127.0.0.1"), 12345) + daddr4in6 := netip.AddrPortFrom(netip.MustParseAddr("::ffff:127.0.0.1"), 12345) + daddr6 := netip.AddrPortFrom(netip.MustParseAddr("::1"), 12345) + buf := make([]byte, 8) + + _, _, err = conn.WriteMsgUDPAddrPort(buf, nil, daddr4) + if err != nil { + t.Fatal(err) + } + + _, _, err = conn.WriteMsgUDPAddrPort(buf, nil, daddr4in6) + if err != nil { + t.Fatal(err) + } + + _, _, err = conn.WriteMsgUDPAddrPort(buf, nil, daddr6) + if err != nil { + t.Fatal(err) + } +} diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go index 99a89c827b8abb..b244dbdbbd08f2 100644 --- a/src/net/unixsock_posix.go +++ b/src/net/unixsock_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/unixsock_readmsg_cloexec.go b/src/net/unixsock_readmsg_cloexec.go index 716484cc6ce957..fa4fd7d9331f42 100644 --- a/src/net/unixsock_readmsg_cloexec.go +++ b/src/net/unixsock_readmsg_cloexec.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || solaris -// +build aix darwin freebsd solaris package net diff --git a/src/net/unixsock_readmsg_cmsg_cloexec.go b/src/net/unixsock_readmsg_cmsg_cloexec.go index bb851b89c05bd3..6b0de875ad5c3f 100644 --- a/src/net/unixsock_readmsg_cmsg_cloexec.go +++ b/src/net/unixsock_readmsg_cmsg_cloexec.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || linux || netbsd || openbsd -// +build dragonfly linux netbsd openbsd package net diff --git a/src/net/unixsock_readmsg_other.go b/src/net/unixsock_readmsg_other.go index 329076183aece9..b3d19fe73dda28 100644 --- a/src/net/unixsock_readmsg_other.go +++ b/src/net/unixsock_readmsg_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (js && wasm) || windows -// +build js,wasm windows package net diff --git a/src/net/unixsock_readmsg_test.go b/src/net/unixsock_readmsg_test.go index a4d2fca69cefde..414f626644b6b3 100644 --- a/src/net/unixsock_readmsg_test.go +++ b/src/net/unixsock_readmsg_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package net diff --git a/src/net/unixsock_test.go b/src/net/unixsock_test.go index 71092e88fb600f..2fc9580cafb68f 100644 --- a/src/net/unixsock_test.go +++ b/src/net/unixsock_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package net @@ -26,7 +25,7 @@ func TestReadUnixgramWithUnnamedSocket(t *testing.T) { testenv.SkipFlaky(t, 15157) } - addr := testUnixAddr() + addr := testUnixAddr(t) la, err := ResolveUnixAddr("unixgram", addr) if err != nil { t.Fatal(err) @@ -77,10 +76,7 @@ func TestUnixgramZeroBytePayload(t *testing.T) { t.Skip("unixgram test") } - c1, err := newLocalPacketListener("unixgram") - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, "unixgram") defer os.Remove(c1.LocalAddr().String()) defer c1.Close() @@ -127,10 +123,7 @@ func TestUnixgramZeroByteBuffer(t *testing.T) { // issue 4352: Recvfrom failed with "address family not // supported by protocol family" if zero-length buffer provided - c1, err := newLocalPacketListener("unixgram") - if err != nil { - t.Fatal(err) - } + c1 := newLocalPacketListener(t, "unixgram") defer os.Remove(c1.LocalAddr().String()) defer c1.Close() @@ -175,7 +168,7 @@ func TestUnixgramWrite(t *testing.T) { t.Skip("unixgram test") } - addr := testUnixAddr() + addr := testUnixAddr(t) laddr, err := ResolveUnixAddr("unixgram", addr) if err != nil { t.Fatal(err) @@ -220,7 +213,7 @@ func testUnixgramWriteConn(t *testing.T, raddr *UnixAddr) { } func testUnixgramWritePacketConn(t *testing.T, raddr *UnixAddr) { - addr := testUnixAddr() + addr := testUnixAddr(t) c, err := ListenPacket("unixgram", addr) if err != nil { t.Fatal(err) @@ -249,9 +242,9 @@ func TestUnixConnLocalAndRemoteNames(t *testing.T) { } handler := func(ls *localServer, ln Listener) {} - for _, laddr := range []string{"", testUnixAddr()} { + for _, laddr := range []string{"", testUnixAddr(t)} { laddr := laddr - taddr := testUnixAddr() + taddr := testUnixAddr(t) ta, err := ResolveUnixAddr("unix", taddr) if err != nil { t.Fatal(err) @@ -260,10 +253,7 @@ func TestUnixConnLocalAndRemoteNames(t *testing.T) { if err != nil { t.Fatal(err) } - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) @@ -311,9 +301,9 @@ func TestUnixgramConnLocalAndRemoteNames(t *testing.T) { t.Skip("unixgram test") } - for _, laddr := range []string{"", testUnixAddr()} { + for _, laddr := range []string{"", testUnixAddr(t)} { laddr := laddr - taddr := testUnixAddr() + taddr := testUnixAddr(t) ta, err := ResolveUnixAddr("unixgram", taddr) if err != nil { t.Fatal(err) @@ -369,7 +359,7 @@ func TestUnixUnlink(t *testing.T) { if !testableNetwork("unix") { t.Skip("unix test") } - name := testUnixAddr() + name := testUnixAddr(t) listen := func(t *testing.T) *UnixListener { l, err := Listen("unix", name) diff --git a/src/net/unixsock_windows_test.go b/src/net/unixsock_windows_test.go index 29244f64710337..d541d89f78c1e8 100644 --- a/src/net/unixsock_windows_test.go +++ b/src/net/unixsock_windows_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package net @@ -46,9 +45,9 @@ func TestUnixConnLocalWindows(t *testing.T) { } handler := func(ls *localServer, ln Listener) {} - for _, laddr := range []string{"", testUnixAddr()} { + for _, laddr := range []string{"", testUnixAddr(t)} { laddr := laddr - taddr := testUnixAddr() + taddr := testUnixAddr(t) ta, err := ResolveUnixAddr("unix", taddr) if err != nil { t.Fatal(err) @@ -57,10 +56,7 @@ func TestUnixConnLocalWindows(t *testing.T) { if err != nil { t.Fatal(err) } - ls, err := (&streamListener{Listener: ln}).newLocalServer() - if err != nil { - t.Fatal(err) - } + ls := (&streamListener{Listener: ln}).newLocalServer() defer ls.teardown() if err := ls.buildup(handler); err != nil { t.Fatal(err) diff --git a/src/net/url/example_test.go b/src/net/url/example_test.go index 476132a1c93aa0..a1913508f72fce 100644 --- a/src/net/url/example_test.go +++ b/src/net/url/example_test.go @@ -12,6 +12,46 @@ import ( "strings" ) +func ExamplePathEscape() { + path := url.PathEscape("my/cool+blog&about,stuff") + fmt.Println(path) + + // Output: + // my%2Fcool+blog&about%2Cstuff +} + +func ExamplePathUnescape() { + escapedPath := "my%2Fcool+blog&about%2Cstuff" + path, err := url.PathUnescape(escapedPath) + if err != nil { + log.Fatal(err) + } + fmt.Println(path) + + // Output: + // my/cool+blog&about,stuff +} + +func ExampleQueryEscape() { + query := url.QueryEscape("my/cool+blog&about,stuff") + fmt.Println(query) + + // Output: + // my%2Fcool%2Bblog%26about%2Cstuff +} + +func ExampleQueryUnescape() { + escapedQuery := "my%2Fcool%2Bblog%26about%2Cstuff" + query, err := url.QueryUnescape(escapedQuery) + if err != nil { + log.Fatal(err) + } + fmt.Println(query) + + // Output: + // my/cool+blog&about,stuff +} + func ExampleValues() { v := url.Values{} v.Set("name", "Ava") @@ -28,6 +68,84 @@ func ExampleValues() { // [Jess Sarah Zoe] } +func ExampleValues_Add() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew") + v.Add("cat sounds", "mau") + fmt.Println(v["cat sounds"]) + + // Output: + // [meow mew mau] +} + +func ExampleValues_Del() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew") + v.Add("cat sounds", "mau") + fmt.Println(v["cat sounds"]) + + v.Del("cat sounds") + fmt.Println(v["cat sounds"]) + + // Output: + // [meow mew mau] + // [] +} + +func ExampleValues_Encode() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew/") + v.Add("cat sounds", "mau$") + fmt.Println(v.Encode()) + + // Output: + // cat+sounds=meow&cat+sounds=mew%2F&cat+sounds=mau%24 +} + +func ExampleValues_Get() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew") + v.Add("cat sounds", "mau") + fmt.Printf("%q\n", v.Get("cat sounds")) + fmt.Printf("%q\n", v.Get("dog sounds")) + + // Output: + // "meow" + // "" +} + +func ExampleValues_Has() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew") + v.Add("cat sounds", "mau") + fmt.Println(v.Has("cat sounds")) + fmt.Println(v.Has("dog sounds")) + + // Output: + // true + // false +} + +func ExampleValues_Set() { + v := url.Values{} + v.Add("cat sounds", "meow") + v.Add("cat sounds", "mew") + v.Add("cat sounds", "mau") + fmt.Println(v["cat sounds"]) + + v.Set("cat sounds", "meow") + fmt.Println(v["cat sounds"]) + + // Output: + // [meow mew mau] + // [meow] +} + func ExampleURL() { u, err := url.Parse("http://bing.com/search?q=dotnet") if err != nil { @@ -247,7 +365,7 @@ func ExampleURL_RequestURI() { // Output: /path?foo=bar } -func toJSON(m interface{}) string { +func toJSON(m any) string { js, err := json.Marshal(m) if err != nil { log.Fatal(err) diff --git a/src/net/url/url.go b/src/net/url/url.go index 20de0f6f5178a1..d530a50d40d19f 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -13,6 +13,7 @@ package url import ( "errors" "fmt" + "path" "sort" "strconv" "strings" @@ -350,11 +351,14 @@ func escape(s string, mode encoding) string { // Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/. // A consequence is that it is impossible to tell which slashes in the Path were // slashes in the raw URL and which were %2f. This distinction is rarely important, -// but when it is, the code should use RawPath, an optional field which only gets -// set if the default encoding is different from Path. +// but when it is, the code should use the EscapedPath method, which preserves +// the original encoding of Path. // -// URL's String method uses the EscapedPath method to obtain the path. See the -// EscapedPath method for more details. +// The RawPath field is an optional field which is only set when the default +// encoding of Path is different from the escaped path. See the EscapedPath method +// for more details. +// +// URL's String method uses the EscapedPath method to obtain the path. type URL struct { Scheme string Opaque string // encoded opaque data @@ -362,6 +366,7 @@ type URL struct { Host string // host or host:port Path string // path (relative paths may omit leading slash) RawPath string // encoded path hint (see EscapedPath method) + OmitHost bool // do not emit empty host (authority) ForceQuery bool // append a query ('?') even if RawQuery is empty RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' @@ -379,9 +384,9 @@ func User(username string) *Userinfo { // // This functionality should only be used with legacy web sites. // RFC 2396 warns that interpreting Userinfo this way -// ``is NOT RECOMMENDED, because the passing of authentication +// “is NOT RECOMMENDED, because the passing of authentication // information in clear text (such as URI) has proven to be a -// security risk in almost every case where it has been used.'' +// security risk in almost every case where it has been used.” func UserPassword(username, password string) *Userinfo { return &Userinfo{username, password, true} } @@ -426,7 +431,7 @@ func (u *Userinfo) String() string { } // Maybe rawURL is of the form scheme:path. -// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) +// (Scheme must be [a-zA-Z][a-zA-Z0-9+.-]*) // If so, return scheme, path; else return "", rawURL. func getScheme(rawURL string) (scheme, path string, err error) { for i := 0; i < len(rawURL); i++ { @@ -452,20 +457,6 @@ func getScheme(rawURL string) (scheme, path string, err error) { return "", rawURL, nil } -// split slices s into two substrings separated by the first occurrence of -// sep. If cutc is true then sep is excluded from the second substring. -// If sep does not occur in s then s and the empty string is returned. -func split(s string, sep byte, cutc bool) (string, string) { - i := strings.IndexByte(s, sep) - if i < 0 { - return s, "" - } - if cutc { - return s[:i], s[i+1:] - } - return s[:i], s[i:] -} - // Parse parses a raw url into a URL structure. // // The url may be relative (a path, without a host) or absolute @@ -474,7 +465,7 @@ func split(s string, sep byte, cutc bool) (string, string) { // error, due to parsing ambiguities. func Parse(rawURL string) (*URL, error) { // Cut off #frag - u, frag := split(rawURL, '#', true) + u, frag, _ := strings.Cut(rawURL, "#") url, err := parse(u, false) if err != nil { return nil, &Error{"parse", u, err} @@ -534,7 +525,7 @@ func parse(rawURL string, viaRequest bool) (*URL, error) { url.ForceQuery = true rest = rest[:len(rest)-1] } else { - rest, url.RawQuery = split(rest, '?', true) + rest, url.RawQuery, _ = strings.Cut(rest, "?") } if !strings.HasPrefix(rest, "/") { @@ -553,9 +544,7 @@ func parse(rawURL string, viaRequest bool) (*URL, error) { // RFC 3986, §3.3: // In addition, a URI reference (Section 4.1) may be a relative-path reference, // in which case the first path segment cannot contain a colon (":") character. - colon := strings.Index(rest, ":") - slash := strings.Index(rest, "/") - if colon >= 0 && (slash < 0 || colon < slash) { + if segment, _, _ := strings.Cut(rest, "/"); strings.Contains(segment, ":") { // First path segment has colon. Not allowed in relative URL. return nil, errors.New("first path segment in URL cannot contain colon") } @@ -563,12 +552,20 @@ func parse(rawURL string, viaRequest bool) (*URL, error) { if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") { var authority string - authority, rest = split(rest[2:], '/', false) + authority, rest = rest[2:], "" + if i := strings.Index(authority, "/"); i >= 0 { + authority, rest = authority[:i], authority[i:] + } url.User, url.Host, err = parseAuthority(authority) if err != nil { return nil, err } + } else if url.Scheme != "" && strings.HasPrefix(rest, "/") { + // OmitHost is set to true when rawURL has an empty host (authority). + // See golang.org/issue/46059. + url.OmitHost = true } + // Set Path and, optionally, RawPath. // RawPath is a hint of the encoding of Path. We don't want to set it if // the default escaping of Path is equivalent, to help make sure that people @@ -602,7 +599,7 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { } user = User(userinfo) } else { - username, password := split(userinfo, ':', true) + username, password, _ := strings.Cut(userinfo, ":") if username, err = unescape(username, encodeUserPassword); err != nil { return nil, "", err } @@ -799,15 +796,15 @@ func validOptionalPort(port string) bool { // To obtain the path, String uses u.EscapedPath(). // // In the second form, the following rules apply: -// - if u.Scheme is empty, scheme: is omitted. -// - if u.User is nil, userinfo@ is omitted. -// - if u.Host is empty, host/ is omitted. -// - if u.Scheme and u.Host are empty and u.User is nil, -// the entire scheme://userinfo@host/ is omitted. -// - if u.Host is non-empty and u.Path begins with a /, -// the form host/path does not add its own /. -// - if u.RawQuery is empty, ?query is omitted. -// - if u.Fragment is empty, #fragment is omitted. +// - if u.Scheme is empty, scheme: is omitted. +// - if u.User is nil, userinfo@ is omitted. +// - if u.Host is empty, host/ is omitted. +// - if u.Scheme and u.Host are empty and u.User is nil, +// the entire scheme://userinfo@host/ is omitted. +// - if u.Host is non-empty and u.Path begins with a /, +// the form host/path does not add its own /. +// - if u.RawQuery is empty, ?query is omitted. +// - if u.Fragment is empty, #fragment is omitted. func (u *URL) String() string { var buf strings.Builder if u.Scheme != "" { @@ -818,15 +815,19 @@ func (u *URL) String() string { buf.WriteString(u.Opaque) } else { if u.Scheme != "" || u.Host != "" || u.User != nil { - if u.Host != "" || u.Path != "" || u.User != nil { - buf.WriteString("//") - } - if ui := u.User; ui != nil { - buf.WriteString(ui.String()) - buf.WriteByte('@') - } - if h := u.Host; h != "" { - buf.WriteString(escape(h, encodeHost)) + if u.OmitHost && u.Host == "" && u.User == nil { + // omit empty host + } else { + if u.Host != "" || u.Path != "" || u.User != nil { + buf.WriteString("//") + } + if ui := u.User; ui != nil { + buf.WriteString(ui.String()) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(escape(h, encodeHost)) + } } } path := u.EscapedPath() @@ -840,7 +841,7 @@ func (u *URL) String() string { // it would be mistaken for a scheme name. Such a segment must be // preceded by a dot-segment (e.g., "./this:that") to make a relative- // path reference. - if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 { + if segment, _, _ := strings.Cut(path, "/"); strings.Contains(segment, ":") { buf.WriteString("./") } } @@ -933,12 +934,8 @@ func ParseQuery(query string) (Values, error) { func parseQuery(m Values, query string) (err error) { for query != "" { - key := query - if i := strings.IndexAny(key, "&"); i >= 0 { - key, query = key[:i], key[i+1:] - } else { - query = "" - } + var key string + key, query, _ = strings.Cut(query, "&") if strings.Contains(key, ";") { err = fmt.Errorf("invalid semicolon separator in query") continue @@ -946,10 +943,7 @@ func parseQuery(m Values, query string) (err error) { if key == "" { continue } - value := "" - if i := strings.Index(key, "="); i >= 0 { - key, value = key[:i], key[i+1:] - } + key, value, _ := strings.Cut(key, "=") key, err1 := QueryUnescape(key) if err1 != nil { if err == nil { @@ -969,7 +963,7 @@ func parseQuery(m Values, query string) (err error) { return err } -// Encode encodes the values into ``URL encoded'' form +// Encode encodes the values into “URL encoded” form // ("bar=baz&foo=quux") sorted by key. func (v Values) Encode() string { if v == nil { @@ -1013,22 +1007,16 @@ func resolvePath(base, ref string) string { } var ( - last string elem string - i int dst strings.Builder ) first := true remaining := full // We want to return a leading '/', so write it now. dst.WriteByte('/') - for i >= 0 { - i = strings.IndexByte(remaining, '/') - if i < 0 { - last, elem, remaining = remaining, remaining, "" - } else { - elem, remaining = remaining[:i], remaining[i+1:] - } + found := true + for found { + elem, remaining, found = strings.Cut(remaining, "/") if elem == "." { first = false // drop @@ -1056,7 +1044,7 @@ func resolvePath(base, ref string) string { } } - if last == "." || last == ".." { + if elem == "." || elem == ".." { dst.WriteByte('/') } @@ -1109,7 +1097,7 @@ func (u *URL) ResolveReference(ref *URL) *URL { url.Path = "" return &url } - if ref.Path == "" && ref.RawQuery == "" { + if ref.Path == "" && !ref.ForceQuery && ref.RawQuery == "" { url.RawQuery = u.RawQuery if ref.Fragment == "" { url.Fragment = u.Fragment @@ -1202,12 +1190,37 @@ func (u *URL) UnmarshalBinary(text []byte) error { return nil } +// JoinPath returns a new URL with the provided path elements joined to +// any existing path and the resulting path cleaned of any ./ or ../ elements. +// Any sequences of multiple / characters will be reduced to a single /. +func (u *URL) JoinPath(elem ...string) *URL { + elem = append([]string{u.EscapedPath()}, elem...) + var p string + if !strings.HasPrefix(elem[0], "/") { + // Return a relative path if u is relative, + // but ensure that it contains no ../ elements. + elem[0] = "/" + elem[0] + p = path.Join(elem...)[1:] + } else { + p = path.Join(elem...) + } + // path.Join will remove any trailing slashes. + // Preserve at least one. + if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") { + p += "/" + } + url := *u + url.setPath(p) + return &url +} + // validUserinfo reports whether s is a valid userinfo string per RFC 3986 // Section 3.2.1: -// userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) -// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" -// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" -// / "*" / "+" / "," / ";" / "=" +// +// userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) +// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" +// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" +// / "*" / "+" / "," / ";" / "=" // // It doesn't validate pct-encoded. The caller does that via func unescape. func validUserinfo(s string) bool { @@ -1242,3 +1255,14 @@ func stringContainsCTLByte(s string) bool { } return false } + +// JoinPath returns a URL string with the provided path elements joined to +// the existing path of base and the resulting path cleaned of any ./ or ../ elements. +func JoinPath(base string, elem ...string) (result string, err error) { + url, err := Parse(base) + if err != nil { + return + } + result = url.JoinPath(elem...).String() + return +} diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 63c8e695af7650..577cf631c835e1 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -163,14 +163,15 @@ var urltests = []URLTest{ }, "http:%2f%2fwww.google.com/?q=go+language", }, - // non-authority with path + // non-authority with path; see golang.org/issue/46059 { "mailto:/webmaster@golang.org", &URL{ - Scheme: "mailto", - Path: "/webmaster@golang.org", + Scheme: "mailto", + Path: "/webmaster@golang.org", + OmitHost: true, }, - "mailto:///webmaster@golang.org", // unfortunate compromise + "", }, // non-authority { @@ -618,15 +619,15 @@ var urltests = []URLTest{ // more useful string for debugging than fmt's struct printer func ufmt(u *URL) string { - var user, pass interface{} + var user, pass any if u.User != nil { user = u.User.Username() if p, ok := u.User.Password(); ok { pass = p } } - return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q, rawfrag=%q, forcequery=%v", - u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.RawFragment, u.ForceQuery) + return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q, rawfrag=%q, forcequery=%v, omithost=%t", + u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.RawFragment, u.ForceQuery, u.OmitHost) } func BenchmarkString(b *testing.B) { @@ -1172,7 +1173,7 @@ var resolveReferenceTests = []struct { {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"}, // Remove any dot-segments prior to forming the target URI. - // http://tools.ietf.org/html/rfc3986#section-5.2.4 + // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"}, // Triple dot isn't special @@ -1192,7 +1193,7 @@ var resolveReferenceTests = []struct { {"http://foo.com/foo%2dbar/", "./baz-quux", "http://foo.com/foo%2dbar/baz-quux"}, // RFC 3986: Normal Examples - // http://tools.ietf.org/html/rfc3986#section-5.4.1 + // https://datatracker.ietf.org/doc/html/rfc3986#section-5.4.1 {"http://a/b/c/d;p?q", "g:h", "g:h"}, {"http://a/b/c/d;p?q", "g", "http://a/b/c/g"}, {"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"}, @@ -1218,7 +1219,7 @@ var resolveReferenceTests = []struct { {"http://a/b/c/d;p?q", "../../g", "http://a/g"}, // RFC 3986: Abnormal Examples - // http://tools.ietf.org/html/rfc3986#section-5.4.2 + // https://datatracker.ietf.org/doc/html/rfc3986#section-5.4.2 {"http://a/b/c/d;p?q", "../../../g", "http://a/g"}, {"http://a/b/c/d;p?q", "../../../../g", "http://a/g"}, {"http://a/b/c/d;p?q", "/./g", "http://a/g"}, @@ -1244,6 +1245,9 @@ var resolveReferenceTests = []struct { {"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"}, {"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"}, {"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"}, + + // Empty path and query but with ForceQuery (issue 46033). + {"https://a/b/c/d;p?q#s", "?", "https://a/b/c/d;p?"}, } func TestResolveReference(t *testing.T) { @@ -2060,11 +2064,148 @@ func BenchmarkPathUnescape(b *testing.B) { } } -var sink string - -func BenchmarkSplit(b *testing.B) { - url := "http://www.google.com/?q=go+language#foo%26bar" - for i := 0; i < b.N; i++ { - sink, sink = split(url, '#', true) +func TestJoinPath(t *testing.T) { + tests := []struct { + base string + elem []string + out string + }{ + { + base: "https://go.googlesource.com", + elem: []string{"go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com/a/b/c", + elem: []string{"../../../go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com/", + elem: []string{"../go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com", + elem: []string{"../go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com", + elem: []string{"../go", "../../go", "../../../go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com/../go", + elem: nil, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com/", + elem: []string{"./go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com//", + elem: []string{"/go"}, + out: "https://go.googlesource.com/go", + }, + { + base: "https://go.googlesource.com//", + elem: []string{"/go", "a", "b", "c"}, + out: "https://go.googlesource.com/go/a/b/c", + }, + { + base: "http://[fe80::1%en0]:8080/", + elem: []string{"/go"}, + }, + { + base: "https://go.googlesource.com", + elem: []string{"go/"}, + out: "https://go.googlesource.com/go/", + }, + { + base: "https://go.googlesource.com", + elem: []string{"go//"}, + out: "https://go.googlesource.com/go/", + }, + { + base: "https://go.googlesource.com", + elem: nil, + out: "https://go.googlesource.com/", + }, + { + base: "https://go.googlesource.com/", + elem: nil, + out: "https://go.googlesource.com/", + }, + { + base: "https://go.googlesource.com/a%2fb", + elem: []string{"c"}, + out: "https://go.googlesource.com/a%2fb/c", + }, + { + base: "https://go.googlesource.com/a%2fb", + elem: []string{"c%2fd"}, + out: "https://go.googlesource.com/a%2fb/c%2fd", + }, + { + base: "https://go.googlesource.com/a/b", + elem: []string{"/go"}, + out: "https://go.googlesource.com/a/b/go", + }, + { + base: "/", + elem: nil, + out: "/", + }, + { + base: "a", + elem: nil, + out: "a", + }, + { + base: "a", + elem: []string{"b"}, + out: "a/b", + }, + { + base: "a", + elem: []string{"../b"}, + out: "b", + }, + { + base: "a", + elem: []string{"../../b"}, + out: "b", + }, + { + base: "", + elem: []string{"a"}, + out: "a", + }, + { + base: "", + elem: []string{"../a"}, + out: "a", + }, + } + for _, tt := range tests { + wantErr := "nil" + if tt.out == "" { + wantErr = "non-nil error" + } + if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err == nil) != (tt.out != "") { + t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) + } + var out string + u, err := Parse(tt.base) + if err == nil { + u = u.JoinPath(tt.elem...) + out = u.String() + } + if out != tt.out || (err == nil) != (tt.out != "") { + t.Errorf("Parse(%q).JoinPath(%q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) + } } } diff --git a/src/net/write_unix_test.go b/src/net/write_unix_test.go index f79f2d0865d559..23e8befa92dc9e 100644 --- a/src/net/write_unix_test.go +++ b/src/net/write_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net diff --git a/src/net/writev_test.go b/src/net/writev_test.go index bf40ca2023ac9e..81b14774f9535d 100644 --- a/src/net/writev_test.go +++ b/src/net/writev_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package net @@ -154,7 +153,7 @@ func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) { var wantSum int switch runtime.GOOS { - case "android", "darwin", "ios", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd": + case "android", "darwin", "ios", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd", "solaris": var wantMinCalls int wantSum = want.Len() v := chunks @@ -187,10 +186,7 @@ func TestWritevError(t *testing.T) { t.Skipf("skipping the test: windows does not have problem sending large chunks of data") } - ln, err := newLocalListener("tcp") - if err != nil { - t.Fatal(err) - } + ln := newLocalListener(t, "tcp") defer ln.Close() ch := make(chan Conn, 1) diff --git a/src/net/writev_unix.go b/src/net/writev_unix.go index a0fedc2f99061d..3318fc5f6f9b04 100644 --- a/src/net/writev_unix.go +++ b/src/net/writev_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd -// +build darwin dragonfly freebsd illumos linux netbsd openbsd +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris package net diff --git a/src/os/dir_unix.go b/src/os/dir_unix.go index 5589c9c68256b0..9b3871a3e866ee 100644 --- a/src/os/dir_unix.go +++ b/src/os/dir_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix dragonfly freebsd js,wasm linux netbsd openbsd solaris package os @@ -28,7 +27,7 @@ const ( ) var dirBufPool = sync.Pool{ - New: func() interface{} { + New: func() any { // The buffer must be at least a block long. buf := make([]byte, blockSize) return &buf diff --git a/src/os/endian_big.go b/src/os/endian_big.go index 0529dccd6f2143..0375e533726ef4 100644 --- a/src/os/endian_big.go +++ b/src/os/endian_big.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build ppc64 || s390x || mips || mips64 -// +build ppc64 s390x mips mips64 package os diff --git a/src/os/endian_little.go b/src/os/endian_little.go index 6be6020f534229..a7cf1cdda8e47e 100644 --- a/src/os/endian_little.go +++ b/src/os/endian_little.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -//go:build 386 || amd64 || arm || arm64 || ppc64le || mips64le || mipsle || riscv64 || wasm -// +build 386 amd64 arm arm64 ppc64le mips64le mipsle riscv64 wasm +//go:build 386 || amd64 || arm || arm64 || loong64 || ppc64le || mips64le || mipsle || riscv64 || wasm package os diff --git a/src/os/env_test.go b/src/os/env_test.go index 4b860157b4c242..f8d56ef8e022fd 100644 --- a/src/os/env_test.go +++ b/src/os/env_test.go @@ -66,7 +66,7 @@ func TestExpand(t *testing.T) { } } -var global interface{} +var global any func BenchmarkExpand(b *testing.B) { b.Run("noop", func(b *testing.B) { @@ -166,3 +166,39 @@ func TestLookupEnv(t *testing.T) { t.Errorf("smallpox release failed; world remains safe but LookupEnv is broken") } } + +// On Windows, Environ was observed to report keys with a single leading "=". +// Check that they are properly reported by LookupEnv and can be set by SetEnv. +// See https://golang.org/issue/49886. +func TestEnvironConsistency(t *testing.T) { + for _, kv := range Environ() { + i := strings.Index(kv, "=") + if i == 0 { + // We observe in practice keys with a single leading "=" on Windows. + // TODO(#49886): Should we consume only the first leading "=" as part + // of the key, or parse through arbitrarily many of them until a non-=, + // or try each possible key/value boundary until LookupEnv succeeds? + i = strings.Index(kv[1:], "=") + 1 + } + if i < 0 { + t.Errorf("Environ entry missing '=': %q", kv) + } + + k := kv[:i] + v := kv[i+1:] + v2, ok := LookupEnv(k) + if ok && v == v2 { + t.Logf("LookupEnv(%q) = %q, %t", k, v2, ok) + } else { + t.Errorf("Environ contains %q, but LookupEnv(%q) = %q, %t", kv, k, v2, ok) + } + + // Since k=v is already present in the environment, + // setting it should be a no-op. + if err := Setenv(k, v); err == nil { + t.Logf("Setenv(%q, %q)", k, v) + } else { + t.Errorf("Environ contains %q, but SetEnv(%q, %q) = %q", kv, k, v, err) + } + } +} diff --git a/src/os/env_unix_test.go b/src/os/env_unix_test.go index d45e1deb833897..4609fc3b94d404 100644 --- a/src/os/env_unix_test.go +++ b/src/os/env_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package os_test diff --git a/src/os/error.go b/src/os/error.go index fe8f2a84466697..9827446e65c060 100644 --- a/src/os/error.go +++ b/src/os/error.go @@ -5,7 +5,6 @@ package os import ( - "internal/oserror" "internal/poll" "io/fs" ) @@ -28,7 +27,6 @@ var ( ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout" ) -func errClosed() error { return oserror.ErrClosed } func errNoDeadline() error { return poll.ErrNoDeadline } // errDeadlineExceeded returns the value for os.ErrDeadlineExceeded. diff --git a/src/os/error_errno.go b/src/os/error_errno.go index 580e915b73c3db..c8140461a4dd9f 100644 --- a/src/os/error_errno.go +++ b/src/os/error_errno.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package os diff --git a/src/os/error_posix.go b/src/os/error_posix.go index 268b3a923ab1ab..5ca2e60e5b4f88 100644 --- a/src/os/error_posix.go +++ b/src/os/error_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package os diff --git a/src/os/error_unix_test.go b/src/os/error_unix_test.go index e45282a0fdb477..1c694fe5f1669c 100644 --- a/src/os/error_unix_test.go +++ b/src/os/error_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os_test diff --git a/src/os/error_windows_test.go b/src/os/error_windows_test.go index aa0c14b7d46e36..86c8a985bb3359 100644 --- a/src/os/error_windows_test.go +++ b/src/os/error_windows_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package os_test diff --git a/src/os/example_test.go b/src/os/example_test.go index e8554b0b122e75..53e3c5227ba648 100644 --- a/src/os/example_test.go +++ b/src/os/example_test.go @@ -241,3 +241,25 @@ func ExampleWriteFile() { log.Fatal(err) } } + +func ExampleMkdir() { + err := os.Mkdir("testdir", 0750) + if err != nil && !os.IsExist(err) { + log.Fatal(err) + } + err = os.WriteFile("testdir/testfile.txt", []byte("Hello, Gophers!"), 0660) + if err != nil { + log.Fatal(err) + } +} + +func ExampleMkdirAll() { + err := os.MkdirAll("test/subdir", 0750) + if err != nil && !os.IsExist(err) { + log.Fatal(err) + } + err = os.WriteFile("test/subdir/testfile.txt", []byte("Hello, Gophers!"), 0660) + if err != nil { + log.Fatal(err) + } +} diff --git a/src/os/exec.go b/src/os/exec.go index bc75d4dd66c910..9eb3166ecb764e 100644 --- a/src/os/exec.go +++ b/src/os/exec.go @@ -149,6 +149,8 @@ func (p *ProcessState) SystemTime() time.Duration { } // Exited reports whether the program has exited. +// On Unix systems this reports true if the program exited due to calling exit, +// but false if the program terminated due to a signal. func (p *ProcessState) Exited() bool { return p.exited() } @@ -162,7 +164,7 @@ func (p *ProcessState) Success() bool { // Sys returns system-dependent exit information about // the process. Convert it to the appropriate underlying // type, such as syscall.WaitStatus on Unix, to access its contents. -func (p *ProcessState) Sys() interface{} { +func (p *ProcessState) Sys() any { return p.sys() } @@ -171,6 +173,6 @@ func (p *ProcessState) Sys() interface{} { // type, such as *syscall.Rusage on Unix, to access its contents. // (On Unix, *syscall.Rusage matches struct rusage as defined in the // getrusage(2) manual page.) -func (p *ProcessState) SysUsage() interface{} { +func (p *ProcessState) SysUsage() any { return p.sysUsage() } diff --git a/src/os/exec/dot_test.go b/src/os/exec/dot_test.go new file mode 100644 index 00000000000000..306f98cbaaa475 --- /dev/null +++ b/src/os/exec/dot_test.go @@ -0,0 +1,191 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package exec_test + +import ( + "errors" + "internal/testenv" + "os" + . "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var pathVar string = func() string { + if runtime.GOOS == "plan9" { + return "path" + } + return "PATH" +}() + +func TestLookPath(t *testing.T) { + testenv.MustHaveExec(t) + + tmpDir := filepath.Join(t.TempDir(), "testdir") + if err := os.Mkdir(tmpDir, 0777); err != nil { + t.Fatal(err) + } + + executable := "execabs-test" + if runtime.GOOS == "windows" { + executable += ".exe" + } + if err := os.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0777); err != nil { + t.Fatal(err) + } + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer func() { + if err := os.Chdir(cwd); err != nil { + panic(err) + } + }() + if err = os.Chdir(tmpDir); err != nil { + t.Fatal(err) + } + t.Setenv("PWD", tmpDir) + t.Logf(". is %#q", tmpDir) + + origPath := os.Getenv(pathVar) + + // Add "." to PATH so that exec.LookPath looks in the current directory on all systems. + // And try to trick it with "../testdir" too. + for _, errdot := range []string{"1", "0"} { + t.Run("GODEBUG=execerrdot="+errdot, func(t *testing.T) { + t.Setenv("GODEBUG", "execerrdot="+errdot) + for _, dir := range []string{".", "../testdir"} { + t.Run(pathVar+"="+dir, func(t *testing.T) { + t.Setenv(pathVar, dir+string(filepath.ListSeparator)+origPath) + good := dir + "/execabs-test" + if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) { + t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good) + } + if runtime.GOOS == "windows" { + good = dir + `\execabs-test` + if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) { + t.Fatalf(`LookPath(%#q) = %#q, %v, want "%s...", nil`, good, found, err, good) + } + } + + _, err := LookPath("execabs-test") + if errdot == "1" { + if err == nil { + t.Fatalf("LookPath didn't fail when finding a non-relative path") + } else if !errors.Is(err, ErrDot) { + t.Fatalf("LookPath returned unexpected error: want Is ErrDot, got %q", err) + } + } else { + if err != nil { + t.Fatalf("LookPath failed unexpectedly: %v", err) + } + } + + cmd := Command("execabs-test") + if errdot == "1" { + if cmd.Err == nil { + t.Fatalf("Command didn't fail when finding a non-relative path") + } else if !errors.Is(cmd.Err, ErrDot) { + t.Fatalf("Command returned unexpected error: want Is ErrDot, got %q", cmd.Err) + } + cmd.Err = nil + } else { + if cmd.Err != nil { + t.Fatalf("Command failed unexpectedly: %v", err) + } + } + + // Clearing cmd.Err should let the execution proceed, + // and it should fail because it's not a valid binary. + if err := cmd.Run(); err == nil { + t.Fatalf("Run did not fail: expected exec error") + } else if errors.Is(err, ErrDot) { + t.Fatalf("Run returned unexpected error ErrDot: want error like ENOEXEC: %q", err) + } + }) + } + }) + } + + // Test the behavior when the first entry in PATH is an absolute name for the + // current directory. + // + // On Windows, "." may or may not be implicitly included before the explicit + // %PATH%, depending on the process environment; + // see https://go.dev/issue/4394. + // + // If the relative entry from "." resolves to the same executable as what + // would be resolved from an absolute entry in %PATH% alone, LookPath should + // return the absolute version of the path instead of ErrDot. + // (See https://go.dev/issue/53536.) + // + // If PATH does not implicitly include "." (such as on Unix platforms, or on + // Windows configured with NoDefaultCurrentDirectoryInExePath), then this + // lookup should succeed regardless of the behavior for ".", so it may be + // useful to run as a control case even on those platforms. + t.Run(pathVar+"=$PWD", func(t *testing.T) { + t.Setenv(pathVar, tmpDir+string(filepath.ListSeparator)+origPath) + good := filepath.Join(tmpDir, "execabs-test") + if found, err := LookPath(good); err != nil || !strings.HasPrefix(found, good) { + t.Fatalf(`LookPath(%#q) = %#q, %v, want \"%s...\", nil`, good, found, err, good) + } + + if found, err := LookPath("execabs-test"); err != nil || !strings.HasPrefix(found, good) { + t.Fatalf(`LookPath(%#q) = %#q, %v, want \"%s...\", nil`, "execabs-test", found, err, good) + } + + cmd := Command("execabs-test") + if cmd.Err != nil { + t.Fatalf("Command(%#q).Err = %v; want nil", "execabs-test", cmd.Err) + } + }) + + t.Run(pathVar+"=$OTHER", func(t *testing.T) { + // Control case: if the lookup returns ErrDot when PATH is empty, then we + // know that PATH implicitly includes ".". If it does not, then we don't + // expect to see ErrDot at all in this test (because the path will be + // unambiguously absolute). + wantErrDot := false + t.Setenv(pathVar, "") + if found, err := LookPath("execabs-test"); errors.Is(err, ErrDot) { + wantErrDot = true + } else if err == nil { + t.Fatalf(`with PATH='', LookPath(%#q) = %#q; want non-nil error`, "execabs-test", found) + } + + // Set PATH to include an explicit directory that contains a completely + // independent executable that happens to have the same name as an + // executable in ".". If "." is included implicitly, looking up the + // (unqualified) executable name will return ErrDot; otherwise, the + // executable in "." should have no effect and the lookup should + // unambiguously resolve to the directory in PATH. + + dir := t.TempDir() + executable := "execabs-test" + if runtime.GOOS == "windows" { + executable += ".exe" + } + if err := os.WriteFile(filepath.Join(dir, executable), []byte{1, 2, 3}, 0777); err != nil { + t.Fatal(err) + } + t.Setenv(pathVar, dir+string(filepath.ListSeparator)+origPath) + + found, err := LookPath("execabs-test") + if wantErrDot { + wantFound := filepath.Join(".", executable) + if found != wantFound || !errors.Is(err, ErrDot) { + t.Fatalf(`LookPath(%#q) = %#q, %v, want %#q, Is ErrDot`, "execabs-test", found, err, wantFound) + } + } else { + wantFound := filepath.Join(dir, executable) + if found != wantFound || err != nil { + t.Fatalf(`LookPath(%#q) = %#q, %v, want %#q, nil`, "execabs-test", found, err, wantFound) + } + } + }) +} diff --git a/src/os/exec/env_test.go b/src/os/exec/env_test.go index b5ac398c274db4..112f1e654a104b 100644 --- a/src/os/exec/env_test.go +++ b/src/os/exec/env_test.go @@ -18,17 +18,29 @@ func TestDedupEnv(t *testing.T) { { noCase: true, in: []string{"k1=v1", "k2=v2", "K1=v3"}, - want: []string{"K1=v3", "k2=v2"}, + want: []string{"k2=v2", "K1=v3"}, }, { noCase: false, in: []string{"k1=v1", "K1=V2", "k1=v3"}, - want: []string{"k1=v3", "K1=V2"}, + want: []string{"K1=V2", "k1=v3"}, }, { in: []string{"=a", "=b", "foo", "bar"}, want: []string{"=b", "foo", "bar"}, }, + { + // #49886: preserve weird Windows keys with leading "=" signs. + noCase: true, + in: []string{`=C:=C:\golang`, `=D:=D:\tmp`, `=D:=D:\`}, + want: []string{`=C:=C:\golang`, `=D:=D:\`}, + }, + { + // #52436: preserve invalid key-value entries (for now). + // (Maybe filter them out or error out on them at some point.) + in: []string{"dodgy", "entries"}, + want: []string{"dodgy", "entries"}, + }, } for _, tt := range tests { got := dedupEnvCase(tt.noCase, tt.in) diff --git a/src/os/exec/example_test.go b/src/os/exec/example_test.go index a66890be69fe43..150f5cff48fd70 100644 --- a/src/os/exec/example_test.go +++ b/src/os/exec/example_test.go @@ -5,7 +5,6 @@ package exec_test import ( - "bytes" "context" "encoding/json" "fmt" @@ -28,7 +27,7 @@ func ExampleLookPath() { func ExampleCommand() { cmd := exec.Command("tr", "a-z", "A-Z") cmd.Stdin = strings.NewReader("some input") - var out bytes.Buffer + var out strings.Builder cmd.Stdout = &out err := cmd.Run() if err != nil { @@ -144,6 +143,21 @@ func ExampleCmd_CombinedOutput() { fmt.Printf("%s\n", stdoutStderr) } +func ExampleCmd_Environ() { + cmd := exec.Command("pwd") + + // Set Dir before calling cmd.Environ so that it will include an + // updated PWD variable (on platforms where that is used). + cmd.Dir = ".." + cmd.Env = append(cmd.Environ(), "POSIXLY_CORRECT=1") + + out, err := cmd.Output() + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s\n", out) +} + func ExampleCommandContext() { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 0c495755116f52..737aaab6a7dc3a 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -18,6 +18,76 @@ // Note that the examples in this package assume a Unix system. // They may not run on Windows, and they do not run in the Go Playground // used by golang.org and godoc.org. +// +// # Executables in the current directory +// +// The functions Command and LookPath look for a program +// in the directories listed in the current path, following the +// conventions of the host operating system. +// Operating systems have for decades included the current +// directory in this search, sometimes implicitly and sometimes +// configured explicitly that way by default. +// Modern practice is that including the current directory +// is usually unexpected and often leads to security problems. +// +// To avoid those security problems, as of Go 1.19, this package will not resolve a program +// using an implicit or explicit path entry relative to the current directory. +// That is, if you run exec.LookPath("go"), it will not successfully return +// ./go on Unix nor .\go.exe on Windows, no matter how the path is configured. +// Instead, if the usual path algorithms would result in that answer, +// these functions return an error err satisfying errors.Is(err, ErrDot). +// +// For example, consider these two program snippets: +// +// path, err := exec.LookPath("prog") +// if err != nil { +// log.Fatal(err) +// } +// use(path) +// +// and +// +// cmd := exec.Command("prog") +// if err := cmd.Run(); err != nil { +// log.Fatal(err) +// } +// +// These will not find and run ./prog or .\prog.exe, +// no matter how the current path is configured. +// +// Code that always wants to run a program from the current directory +// can be rewritten to say "./prog" instead of "prog". +// +// Code that insists on including results from relative path entries +// can instead override the error using an errors.Is check: +// +// path, err := exec.LookPath("prog") +// if errors.Is(err, exec.ErrDot) { +// err = nil +// } +// if err != nil { +// log.Fatal(err) +// } +// use(path) +// +// and +// +// cmd := exec.Command("prog") +// if errors.Is(cmd.Err, exec.ErrDot) { +// cmd.Err = nil +// } +// if err := cmd.Run(); err != nil { +// log.Fatal(err) +// } +// +// Setting the environment variable GODEBUG=execerrdot=0 +// disables generation of ErrDot entirely, temporarily restoring the pre-Go 1.19 +// behavior for programs that are unable to apply more targeted fixes. +// A future version of Go may remove support for this variable. +// +// Before adding such overrides, make sure you understand the +// security implications of doing so. +// See https://go.dev/blog/path-security for more information. package exec import ( @@ -50,6 +120,20 @@ func (e *Error) Error() string { func (e *Error) Unwrap() error { return e.Err } +// wrappedError wraps an error without relying on fmt.Errorf. +type wrappedError struct { + prefix string + err error +} + +func (w wrappedError) Error() string { + return w.prefix + ": " + w.err.Error() +} + +func (w wrappedError) Unwrap() error { + return w.err +} + // Cmd represents an external command being prepared or run. // // A Cmd cannot be reused after calling its Run, Output or CombinedOutput @@ -134,14 +218,32 @@ type Cmd struct { ProcessState *os.ProcessState ctx context.Context // nil means none - lookPathErr error // LookPath error, if any. - finished bool // when Wait was called + Err error // LookPath error, if any. childFiles []*os.File closeAfterStart []io.Closer closeAfterWait []io.Closer goroutine []func() error - errch chan error // one send per goroutine - waitDone chan struct{} + goroutineErrs <-chan error // one receive per goroutine + ctxErr <-chan error // if non nil, receives the error from watchCtx exactly once + + // For a security release long ago, we created x/sys/execabs, + // which manipulated the unexported lookPathErr error field + // in this struct. For Go 1.19 we exported the field as Err error, + // above, but we have to keep lookPathErr around for use by + // old programs building against new toolchains. + // The String and Start methods look for an error in lookPathErr + // in preference to Err, to preserve the errors that execabs sets. + // + // In general we don't guarantee misuse of reflect like this, + // but the misuse of reflect was by us, the best of various bad + // options to fix the security problem, and people depend on + // those old copies of execabs continuing to work. + // The result is that we have to leave this variable around for the + // rest of time, a compatibility scar. + // + // See https://go.dev/blog/path-security + // and https://go.dev/issue/43724 for more context. + lookPathErr error } // Command returns the Cmd struct to execute the named program with @@ -172,11 +274,16 @@ func Command(name string, arg ...string) *Cmd { Args: append([]string{name}, arg...), } if filepath.Base(name) == name { - if lp, err := LookPath(name); err != nil { - cmd.lookPathErr = err - } else { + lp, err := LookPath(name) + if lp != "" { + // Update cmd.Path even if err is non-nil. + // If err is ErrDot (especially on Windows), lp may include a resolved + // extension (like .exe or .bat) that should be preserved. cmd.Path = lp } + if err != nil { + cmd.Err = err + } } return cmd } @@ -200,7 +307,7 @@ func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { // In particular, it is not suitable for use as input to a shell. // The output of String may vary across Go releases. func (c *Cmd) String() string { - if c.lookPathErr != nil { + if c.Err != nil || c.lookPathErr != nil { // failed to resolve path; report the original requested path (plus args) return strings.Join(c.Args, " ") } @@ -216,20 +323,13 @@ func (c *Cmd) String() string { // interfaceEqual protects against panics from doing equality tests on // two interfaces with non-comparable underlying types. -func interfaceEqual(a, b interface{}) bool { +func interfaceEqual(a, b any) bool { defer func() { recover() }() return a == b } -func (c *Cmd) envv() ([]string, error) { - if c.Env != nil { - return c.Env, nil - } - return execenv.Default(c.SysProcAttr) -} - func (c *Cmd) argv() []string { if len(c.Args) > 0 { return c.Args @@ -237,10 +337,6 @@ func (c *Cmd) argv() []string { return []string{c.Path} } -// skipStdinCopyError optionally specifies a function which reports -// whether the provided stdin copy error should be ignored. -var skipStdinCopyError func(error) bool - func (c *Cmd) stdin() (f *os.File, err error) { if c.Stdin == nil { f, err = os.Open(os.DevNull) @@ -264,7 +360,7 @@ func (c *Cmd) stdin() (f *os.File, err error) { c.closeAfterWait = append(c.closeAfterWait, pw) c.goroutine = append(c.goroutine, func() error { _, err := io.Copy(pw, c.Stdin) - if skip := skipStdinCopyError; skip != nil && skip(err) { + if skipStdinCopyError(err) { err = nil } if err1 := pw.Close(); err == nil { @@ -346,7 +442,7 @@ func (c *Cmd) Run() error { // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. func lookExtensions(path, dir string) (string, error) { if filepath.Base(path) == path { - path = filepath.Join(".", path) + path = "." + string(filepath.Separator) + path } if dir == "" { return LookPath(path) @@ -371,13 +467,19 @@ func lookExtensions(path, dir string) (string, error) { // // If Start returns successfully, the c.Process field will be set. // -// The Wait method will return the exit code and release associated resources -// once the command exits. +// After a successful call to Start the Wait method must be called in +// order to release associated system resources. func (c *Cmd) Start() error { - if c.lookPathErr != nil { + if c.Path == "" && c.Err == nil && c.lookPathErr == nil { + c.Err = errors.New("exec: no command") + } + if c.Err != nil || c.lookPathErr != nil { c.closeDescriptors(c.closeAfterStart) c.closeDescriptors(c.closeAfterWait) - return c.lookPathErr + if c.lookPathErr != nil { + return c.lookPathErr + } + return c.Err } if runtime.GOOS == "windows" { lp, err := lookExtensions(c.Path, c.Dir) @@ -414,7 +516,7 @@ func (c *Cmd) Start() error { } c.childFiles = append(c.childFiles, c.ExtraFiles...) - envv, err := c.envv() + env, err := c.environ() if err != nil { return err } @@ -422,7 +524,7 @@ func (c *Cmd) Start() error { c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ Dir: c.Dir, Files: c.childFiles, - Env: addCriticalEnv(dedupEnv(envv)), + Env: env, Sys: c.SysProcAttr, }) if err != nil { @@ -433,26 +535,18 @@ func (c *Cmd) Start() error { c.closeDescriptors(c.closeAfterStart) - // Don't allocate the channel unless there are goroutines to fire. + // Don't allocate the goroutineErrs channel unless there are goroutines to fire. if len(c.goroutine) > 0 { - c.errch = make(chan error, len(c.goroutine)) + errc := make(chan error, len(c.goroutine)) + c.goroutineErrs = errc for _, fn := range c.goroutine { go func(fn func() error) { - c.errch <- fn() + errc <- fn() }(fn) } } - if c.ctx != nil { - c.waitDone = make(chan struct{}) - go func() { - select { - case <-c.ctx.Done(): - c.Process.Kill() - case <-c.waitDone: - } - }() - } + c.ctxErr = c.watchCtx() return nil } @@ -499,33 +593,79 @@ func (c *Cmd) Wait() error { if c.Process == nil { return errors.New("exec: not started") } - if c.finished { + if c.ProcessState != nil { return errors.New("exec: Wait was already called") } - c.finished = true - state, err := c.Process.Wait() - if c.waitDone != nil { - close(c.waitDone) + if err == nil && !state.Success() { + err = &ExitError{ProcessState: state} } c.ProcessState = state + // Wait for the pipe-copying goroutines to complete. var copyError error for range c.goroutine { - if err := <-c.errch; err != nil && copyError == nil { + if err := <-c.goroutineErrs; err != nil && copyError == nil { copyError = err } } + c.goroutine = nil // Allow the goroutines' closures to be GC'd. + + if c.ctxErr != nil { + interruptErr := <-c.ctxErr + // If c.Process.Wait returned an error, prefer that. + // Otherwise, report any error from the interrupt goroutine. + if interruptErr != nil && err == nil { + err = interruptErr + } + } + // Report errors from the copying goroutines only if the program otherwise + // exited normally on its own. Otherwise, the copying error may be due to the + // abnormal termination. + if err == nil { + err = copyError + } c.closeDescriptors(c.closeAfterWait) + c.closeAfterWait = nil - if err != nil { - return err - } else if !state.Success() { - return &ExitError{ProcessState: state} + return err +} + +// watchCtx conditionally starts a goroutine that waits until either c.ctx is +// done or c.Process.Wait has completed (called from Wait). +// If c.ctx is done first, the goroutine terminates c.Process. +// +// If a goroutine was started, watchCtx returns a channel on which its result +// must be received. +func (c *Cmd) watchCtx() <-chan error { + if c.ctx == nil { + return nil } - return copyError + errc := make(chan error) + go func() { + select { + case errc <- nil: + return + case <-c.ctx.Done(): + } + + var err error + if killErr := c.Process.Kill(); killErr == nil { + // We appear to have successfully delivered a kill signal, so any + // program behavior from this point may be due to ctx. + err = c.ctx.Err() + } else if !errors.Is(killErr, os.ErrProcessDone) { + err = wrappedError{ + prefix: "exec: error sending signal to Cmd", + err: killErr, + } + } + errc <- err + }() + + return errc } // Output runs the command and returns its standard output. @@ -735,6 +875,54 @@ func minInt(a, b int) int { return b } +// environ returns a best-effort copy of the environment in which the command +// would be run as it is currently configured. If an error occurs in computing +// the environment, it is returned alongside the best-effort copy. +func (c *Cmd) environ() ([]string, error) { + var err error + + env := c.Env + if env == nil { + env, err = execenv.Default(c.SysProcAttr) + if err != nil { + env = os.Environ() + // Note that the non-nil err is preserved despite env being overridden. + } + + if c.Dir != "" { + switch runtime.GOOS { + case "windows", "plan9": + // Windows and Plan 9 do not use the PWD variable, so we don't need to + // keep it accurate. + default: + // On POSIX platforms, PWD represents “an absolute pathname of the + // current working directory.” Since we are changing the working + // directory for the command, we should also update PWD to reflect that. + // + // Unfortunately, we didn't always do that, so (as proposed in + // https://go.dev/issue/50599) to avoid unintended collateral damage we + // only implicitly update PWD when Env is nil. That way, we're much + // less likely to override an intentional change to the variable. + if pwd, absErr := filepath.Abs(c.Dir); absErr == nil { + env = append(env, "PWD="+pwd) + } else if err == nil { + err = absErr + } + } + } + } + + return addCriticalEnv(dedupEnv(env)), err +} + +// Environ returns a copy of the environment in which the command would be run +// as it is currently configured. +func (c *Cmd) Environ() []string { + // Intentionally ignore errors: environ returns a best-effort environment no matter what. + env, _ := c.environ() + return env +} + // dedupEnv returns a copy of env with any duplicates removed, in favor of // later values. // Items not of the normal environment "key=value" form are preserved unchanged. @@ -745,25 +933,47 @@ func dedupEnv(env []string) []string { // dedupEnvCase is dedupEnv with a case option for testing. // If caseInsensitive is true, the case of keys is ignored. func dedupEnvCase(caseInsensitive bool, env []string) []string { + // Construct the output in reverse order, to preserve the + // last occurrence of each key. out := make([]string, 0, len(env)) - saw := make(map[string]int, len(env)) // key => index into out - for _, kv := range env { - eq := strings.Index(kv, "=") - if eq < 0 { - out = append(out, kv) + saw := make(map[string]bool, len(env)) + for n := len(env); n > 0; n-- { + kv := env[n-1] + + i := strings.Index(kv, "=") + if i == 0 { + // We observe in practice keys with a single leading "=" on Windows. + // TODO(#49886): Should we consume only the first leading "=" as part + // of the key, or parse through arbitrarily many of them until a non-"="? + i = strings.Index(kv[1:], "=") + 1 + } + if i < 0 { + if kv != "" { + // The entry is not of the form "key=value" (as it is required to be). + // Leave it as-is for now. + // TODO(#52436): should we strip or reject these bogus entries? + out = append(out, kv) + } continue } - k := kv[:eq] + k := kv[:i] if caseInsensitive { k = strings.ToLower(k) } - if dupIdx, isDup := saw[k]; isDup { - out[dupIdx] = kv + if saw[k] { continue } - saw[k] = len(out) + + saw[k] = true out = append(out, kv) } + + // Now reverse the slice to restore the original order. + for i := 0; i < len(out)/2; i++ { + j := len(out) - i - 1 + out[i], out[j] = out[j], out[i] + } + return out } @@ -775,11 +985,10 @@ func addCriticalEnv(env []string) []string { return env } for _, kv := range env { - eq := strings.Index(kv, "=") - if eq < 0 { + k, _, ok := strings.Cut(kv, "=") + if !ok { continue } - k := kv[:eq] if strings.EqualFold(k, "SYSTEMROOT") { // We already have it. return env @@ -787,3 +996,12 @@ func addCriticalEnv(env []string) []string { } return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT")) } + +// ErrDot indicates that a path lookup resolved to an executable +// in the current directory due to ‘.’ being in the path, either +// implicitly or explicitly. See the package documentation for details. +// +// Note that functions in this package do not return ErrDot directly. +// Code should use errors.Is(err, ErrDot), not err == ErrDot, +// to test whether a returned error err is due to this condition. +var ErrDot = errors.New("cannot run executable found relative to current directory") diff --git a/src/os/exec/exec_linux_test.go b/src/os/exec/exec_linux_test.go index 3cfa30ee72477e..b9f6b7b7672124 100644 --- a/src/os/exec/exec_linux_test.go +++ b/src/os/exec/exec_linux_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && cgo -// +build linux,cgo // On systems that use glibc, calling malloc can create a new arena, // and creating a new arena can read /sys/devices/system/cpu/online. @@ -23,7 +22,7 @@ import ( ) func init() { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + if os.Getenv("GO_EXEC_TEST_PID") == "" { return } diff --git a/src/os/exec/exec_plan9.go b/src/os/exec/exec_plan9.go index 21ac7b765f1df0..8920bec1f5eada 100644 --- a/src/os/exec/exec_plan9.go +++ b/src/os/exec/exec_plan9.go @@ -6,14 +6,14 @@ package exec import "io/fs" -func init() { - skipStdinCopyError = func(err error) bool { - // Ignore hungup errors copying to stdin if the program - // completed successfully otherwise. - // See Issue 35753. - pe, ok := err.(*fs.PathError) - return ok && - pe.Op == "write" && pe.Path == "|1" && - pe.Err.Error() == "i/o on hungup channel" - } +// skipStdinCopyError optionally specifies a function which reports +// whether the provided stdin copy error should be ignored. +func skipStdinCopyError(err error) bool { + // Ignore hungup errors copying to stdin if the program + // completed successfully otherwise. + // See Issue 35753. + pe, ok := err.(*fs.PathError) + return ok && + pe.Op == "write" && pe.Path == "|1" && + pe.Err.Error() == "i/o on hungup channel" } diff --git a/src/os/exec/exec_posix_test.go b/src/os/exec/exec_posix_test.go index 7b2c0c0c111b35..f0401377e8e9c0 100644 --- a/src/os/exec/exec_posix_test.go +++ b/src/os/exec/exec_posix_test.go @@ -2,22 +2,51 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package exec_test import ( + "fmt" + "internal/testenv" + "os" "os/user" + "path/filepath" + "reflect" "runtime" "strconv" + "strings" "syscall" "testing" "time" ) +func init() { + registerHelperCommand("pwd", cmdPwd) + registerHelperCommand("sleep", cmdSleep) +} + +func cmdPwd(...string) { + pwd, err := os.Getwd() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println(pwd) +} + +func cmdSleep(args ...string) { + n, err := strconv.Atoi(args[0]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + time.Sleep(time.Duration(n) * time.Second) +} + func TestCredentialNoSetGroups(t *testing.T) { if runtime.GOOS == "android" { + maySkipHelperCommand("echo") t.Skip("unsupported on Android") } @@ -56,7 +85,7 @@ func TestCredentialNoSetGroups(t *testing.T) { func TestWaitid(t *testing.T) { t.Parallel() - cmd := helperCommand(t, "sleep") + cmd := helperCommand(t, "sleep", "3") if err := cmd.Start(); err != nil { t.Fatal(err) } @@ -87,3 +116,139 @@ func TestWaitid(t *testing.T) { <-ch } + +// https://go.dev/issue/50599: if Env is not set explicitly, setting Dir should +// implicitly update PWD to the correct path, and Environ should list the +// updated value. +func TestImplicitPWD(t *testing.T) { + t.Parallel() + + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + cases := []struct { + name string + dir string + want string + }{ + {"empty", "", cwd}, + {"dot", ".", cwd}, + {"dotdot", "..", filepath.Dir(cwd)}, + {"PWD", cwd, cwd}, + {"PWDdotdot", cwd + string(filepath.Separator) + "..", filepath.Dir(cwd)}, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + cmd := helperCommand(t, "pwd") + if cmd.Env != nil { + t.Fatalf("test requires helperCommand not to set Env field") + } + cmd.Dir = tc.dir + + var pwds []string + for _, kv := range cmd.Environ() { + if strings.HasPrefix(kv, "PWD=") { + pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) + } + } + + wantPWDs := []string{tc.want} + if tc.dir == "" { + if _, ok := os.LookupEnv("PWD"); !ok { + wantPWDs = nil + } + } + if !reflect.DeepEqual(pwds, wantPWDs) { + t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) + } + + cmd.Stderr = new(strings.Builder) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%v:\n%s", err, cmd.Stderr) + } + got := strings.Trim(string(out), "\r\n") + t.Logf("in\n\t%s\n`pwd` reported\n\t%s", tc.dir, got) + if got != tc.want { + t.Errorf("want\n\t%s", tc.want) + } + }) + } +} + +// However, if cmd.Env is set explicitly, setting Dir should not override it. +// (This checks that the implementation for https://go.dev/issue/50599 doesn't +// break existing users who may have explicitly mismatched the PWD variable.) +func TestExplicitPWD(t *testing.T) { + maySkipHelperCommand("pwd") + testenv.MustHaveSymlink(t) + + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + link := filepath.Join(t.TempDir(), "link") + if err := os.Symlink(cwd, link); err != nil { + t.Fatal(err) + } + + // Now link is another equally-valid name for cwd. If we set Dir to one and + // PWD to the other, the subprocess should report the PWD version. + cases := []struct { + name string + dir string + pwd string + }{ + {name: "original PWD", pwd: cwd}, + {name: "link PWD", pwd: link}, + {name: "in link with original PWD", dir: link, pwd: cwd}, + {name: "in dir with link PWD", dir: cwd, pwd: link}, + // Ideally we would also like to test what happens if we set PWD to + // something totally bogus (or the empty string), but then we would have no + // idea what output the subprocess should actually produce: cwd itself may + // contain symlinks preserved from the PWD value in the test's environment. + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + cmd := helperCommand(t, "pwd") + // This is intentionally opposite to the usual order of setting cmd.Dir + // and then calling cmd.Environ. Here, we *want* PWD not to match cmd.Dir, + // so we don't care whether cmd.Dir is reflected in cmd.Environ. + cmd.Env = append(cmd.Environ(), "PWD="+tc.pwd) + cmd.Dir = tc.dir + + var pwds []string + for _, kv := range cmd.Environ() { + if strings.HasPrefix(kv, "PWD=") { + pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) + } + } + + wantPWDs := []string{tc.pwd} + if !reflect.DeepEqual(pwds, wantPWDs) { + t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) + } + + cmd.Stderr = new(strings.Builder) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%v:\n%s", err, cmd.Stderr) + } + got := strings.Trim(string(out), "\r\n") + t.Logf("in\n\t%s\nwith PWD=%s\nsubprocess os.Getwd() reported\n\t%s", tc.dir, tc.pwd, got) + if got != tc.pwd { + t.Errorf("want\n\t%s", tc.pwd) + } + }) + } +} diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index d854e0de843d69..52001bf9e30ab4 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -11,6 +11,7 @@ import ( "bufio" "bytes" "context" + "flag" "fmt" "internal/poll" "internal/testenv" @@ -21,25 +22,23 @@ import ( "net/http/httptest" "os" "os/exec" + "os/exec/internal/fdtest" "path/filepath" + "reflect" "runtime" "strconv" "strings" + "sync" "testing" "time" ) -// haveUnexpectedFDs is set at init time to report whether any -// file descriptors were open at program start. +// haveUnexpectedFDs is set at init time to report whether any file descriptors +// were open at program start. var haveUnexpectedFDs bool -// unfinalizedFiles holds files that should not be finalized, -// because that would close the associated file descriptor, -// which we don't want to do. -var unfinalizedFiles []*os.File - func init() { - if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + if os.Getenv("GO_EXEC_TEST_PID") != "" { return } if runtime.GOOS == "windows" { @@ -49,41 +48,250 @@ func init() { if poll.IsPollDescriptor(fd) { continue } - // We have no good portable way to check whether an FD is open. - // We use NewFile to create a *os.File, which lets us - // know whether it is open, but then we have to cope with - // the finalizer on the *os.File. - f := os.NewFile(fd, "") - if _, err := f.Stat(); err != nil { - // Close the file to clear the finalizer. - // We expect the Close to fail. - f.Close() - } else { - fmt.Printf("fd %d open at test start\n", fd) + + if fdtest.Exists(fd) { haveUnexpectedFDs = true - // Use a global variable to avoid running - // the finalizer, which would close the FD. - unfinalizedFiles = append(unfinalizedFiles, f) + return } } } -func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) { +// TestMain allows the test binary to impersonate many other binaries, +// some of which may manipulate os.Stdin, os.Stdout, and/or os.Stderr +// (and thus cannot run as an ordinary Test function, since the testing +// package monkey-patches those variables before running tests). +func TestMain(m *testing.M) { + flag.Parse() + + pid := os.Getpid() + if os.Getenv("GO_EXEC_TEST_PID") == "" { + os.Setenv("GO_EXEC_TEST_PID", strconv.Itoa(pid)) + + code := m.Run() + if code == 0 && flag.Lookup("test.run").Value.String() == "" && flag.Lookup("test.list").Value.String() == "" { + for cmd := range helperCommands { + if _, ok := helperCommandUsed.Load(cmd); !ok { + fmt.Fprintf(os.Stderr, "helper command unused: %q\n", cmd) + code = 1 + } + } + } + os.Exit(code) + } + + args := flag.Args() + if len(args) == 0 { + fmt.Fprintf(os.Stderr, "No command\n") + os.Exit(2) + } + + cmd, args := args[0], args[1:] + f, ok := helperCommands[cmd] + if !ok { + fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) + os.Exit(2) + } + f(args...) + os.Exit(0) +} + +// registerHelperCommand registers a command that the test process can impersonate. +// A command should be registered in the same source file in which it is used. +// If all tests are run and pass, all registered commands must be used. +// (This prevents stale commands from accreting if tests are removed or +// refactored over time.) +func registerHelperCommand(name string, f func(...string)) { + if helperCommands[name] != nil { + panic("duplicate command registered: " + name) + } + helperCommands[name] = f +} + +// maySkipHelperCommand records that the test that uses the named helper command +// was invoked, but may call Skip on the test before actually calling +// helperCommand. +func maySkipHelperCommand(name string) { + helperCommandUsed.Store(name, true) +} + +// helperCommand returns an exec.Cmd that will run the named helper command. +func helperCommand(t *testing.T, name string, args ...string) *exec.Cmd { + t.Helper() + return helperCommandContext(t, nil, name, args...) +} + +// helperCommandContext is like helperCommand, but also accepts a Context under +// which to run the command. +func helperCommandContext(t *testing.T, ctx context.Context, name string, args ...string) (cmd *exec.Cmd) { + helperCommandUsed.LoadOrStore(name, true) + + t.Helper() testenv.MustHaveExec(t) - cs := []string{"-test.run=TestHelperProcess", "--"} - cs = append(cs, s...) + cs := append([]string{name}, args...) if ctx != nil { - cmd = exec.CommandContext(ctx, os.Args[0], cs...) + cmd = exec.CommandContext(ctx, exePath(t), cs...) } else { - cmd = exec.Command(os.Args[0], cs...) + cmd = exec.Command(exePath(t), cs...) } - cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") return cmd } -func helperCommand(t *testing.T, s ...string) *exec.Cmd { - return helperCommandContext(t, nil, s...) +// exePath returns the path to the running executable. +func exePath(t testing.TB) string { + exeOnce.Do(func() { + // Use os.Executable instead of os.Args[0] in case the caller modifies + // cmd.Dir: if the test binary is invoked like "./exec.test", it should + // not fail spuriously. + exeOnce.path, exeOnce.err = os.Executable() + }) + + if exeOnce.err != nil { + if t == nil { + panic(exeOnce.err) + } + t.Fatal(exeOnce.err) + } + + return exeOnce.path +} + +var exeOnce struct { + path string + err error + sync.Once +} + +var helperCommandUsed sync.Map + +var helperCommands = map[string]func(...string){ + "echo": cmdEcho, + "echoenv": cmdEchoEnv, + "cat": cmdCat, + "pipetest": cmdPipeTest, + "stdinClose": cmdStdinClose, + "exit": cmdExit, + "describefiles": cmdDescribeFiles, + "extraFilesAndPipes": cmdExtraFilesAndPipes, + "stderrfail": cmdStderrFail, + "yes": cmdYes, +} + +func cmdEcho(args ...string) { + iargs := []any{} + for _, s := range args { + iargs = append(iargs, s) + } + fmt.Println(iargs...) +} + +func cmdEchoEnv(args ...string) { + for _, s := range args { + fmt.Println(os.Getenv(s)) + } +} + +func cmdCat(args ...string) { + if len(args) == 0 { + io.Copy(os.Stdout, os.Stdin) + return + } + exit := 0 + for _, fn := range args { + f, err := os.Open(fn) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + exit = 2 + } else { + defer f.Close() + io.Copy(os.Stdout, f) + } + } + os.Exit(exit) +} + +func cmdPipeTest(...string) { + bufr := bufio.NewReader(os.Stdin) + for { + line, _, err := bufr.ReadLine() + if err == io.EOF { + break + } else if err != nil { + os.Exit(1) + } + if bytes.HasPrefix(line, []byte("O:")) { + os.Stdout.Write(line) + os.Stdout.Write([]byte{'\n'}) + } else if bytes.HasPrefix(line, []byte("E:")) { + os.Stderr.Write(line) + os.Stderr.Write([]byte{'\n'}) + } else { + os.Exit(1) + } + } +} + +func cmdStdinClose(...string) { + b, err := io.ReadAll(os.Stdin) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + if s := string(b); s != stdinCloseTestString { + fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString) + os.Exit(1) + } +} + +func cmdExit(args ...string) { + n, _ := strconv.Atoi(args[0]) + os.Exit(n) +} + +func cmdDescribeFiles(args ...string) { + f := os.NewFile(3, fmt.Sprintf("fd3")) + ln, err := net.FileListener(f) + if err == nil { + fmt.Printf("fd3: listener %s\n", ln.Addr()) + ln.Close() + } +} + +func cmdExtraFilesAndPipes(args ...string) { + n, _ := strconv.Atoi(args[0]) + pipes := make([]*os.File, n) + for i := 0; i < n; i++ { + pipes[i] = os.NewFile(uintptr(3+i), strconv.Itoa(i)) + } + response := "" + for i, r := range pipes { + buf := make([]byte, 10) + n, err := r.Read(buf) + if err != nil { + fmt.Fprintf(os.Stderr, "Child: read error: %v on pipe %d\n", err, i) + os.Exit(1) + } + response = response + string(buf[:n]) + } + fmt.Fprintf(os.Stderr, "child: %s", response) +} + +func cmdStderrFail(...string) { + fmt.Fprintf(os.Stderr, "some stderr text\n") + os.Exit(1) +} + +func cmdYes(args ...string) { + if len(args) == 0 { + args = []string{"y"} + } + s := strings.Join(args, " ") + "\n" + for { + _, err := os.Stdout.WriteString(s) + if err != nil { + os.Exit(1) + } + } } func TestEcho(t *testing.T) { @@ -97,7 +305,7 @@ func TestEcho(t *testing.T) { } func TestCommandRelativeName(t *testing.T) { - testenv.MustHaveExec(t) + cmd := helperCommand(t, "echo", "foo") // Run our own binary as a relative path // (e.g. "_test/exec.test") our parent directory. @@ -112,9 +320,8 @@ func TestCommandRelativeName(t *testing.T) { t.Skipf("skipping; unexpected shallow dir of %q", dir) } - cmd := exec.Command(filepath.Join(dirBase, base), "-test.run=TestHelperProcess", "--", "echo", "foo") + cmd.Path = filepath.Join(dirBase, base) cmd.Dir = parentDir - cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} out, err := cmd.Output() if err != nil { @@ -166,16 +373,14 @@ func TestCatGoodAndBadFile(t *testing.T) { if _, ok := err.(*exec.ExitError); !ok { t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err) } - s := string(bs) - sp := strings.SplitN(s, "\n", 2) - if len(sp) != 2 { - t.Fatalf("expected two lines from cat; got %q", s) + errLine, body, ok := strings.Cut(string(bs), "\n") + if !ok { + t.Fatalf("expected two lines from cat; got %q", bs) } - errLine, body := sp[0], sp[1] if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") { t.Errorf("expected stderr to complain about file; got %q", errLine) } - if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") { + if !strings.Contains(body, "func TestCatGoodAndBadFile(t *testing.T)") { t.Errorf("expected test code; got %q (len %d)", body, len(body)) } } @@ -379,50 +584,21 @@ func TestStdinCloseRace(t *testing.T) { // Issue 5071 func TestPipeLookPathLeak(t *testing.T) { - // If we are reading from /proc/self/fd we (should) get an exact result. - tolerance := 0 - - // Reading /proc/self/fd is more reliable than calling lsof, so try that - // first. - numOpenFDs := func() (int, []byte, error) { - fds, err := os.ReadDir("/proc/self/fd") - if err != nil { - return 0, nil, err - } - return len(fds), nil, nil + if runtime.GOOS == "windows" { + t.Skip("we don't currently suppore counting open handles on windows") } - want, before, err := numOpenFDs() - if err != nil { - // We encountered a problem reading /proc/self/fd (we might be on - // a platform that doesn't have it). Fall back onto lsof. - t.Logf("using lsof because: %v", err) - numOpenFDs = func() (int, []byte, error) { - // Android's stock lsof does not obey the -p option, - // so extra filtering is needed. - // https://golang.org/issue/10206 - if runtime.GOOS == "android" { - // numOpenFDsAndroid handles errors itself and - // might skip or fail the test. - n, lsof := numOpenFDsAndroid(t) - return n, lsof, nil - } - lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output() - return bytes.Count(lsof, []byte("\n")), lsof, err - } - // lsof may see file descriptors associated with the fork itself, - // so we allow some extra margin if we have to use it. - // https://golang.org/issue/19243 - tolerance = 5 - - // Retry reading the number of open file descriptors. - want, before, err = numOpenFDs() - if err != nil { - t.Log(err) - t.Skipf("skipping test; error finding or running lsof") + openFDs := func() []uintptr { + var fds []uintptr + for i := uintptr(0); i < 100; i++ { + if fdtest.Exists(i) { + fds = append(fds, i) + } } + return fds } + want := openFDs() for i := 0; i < 6; i++ { cmd := exec.Command("something-that-does-not-exist-executable") cmd.StdoutPipe() @@ -432,62 +608,14 @@ func TestPipeLookPathLeak(t *testing.T) { t.Fatal("unexpected success") } } - got, after, err := numOpenFDs() - if err != nil { - // numOpenFDs has already succeeded once, it should work here. - t.Errorf("unexpected failure: %v", err) - } - if got-want > tolerance { - t.Errorf("number of open file descriptors changed: got %v, want %v", got, want) - if before != nil { - t.Errorf("before:\n%v\n", before) - } - if after != nil { - t.Errorf("after:\n%v\n", after) - } - } -} - -func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) { - raw, err := exec.Command("lsof").Output() - if err != nil { - t.Skip("skipping test; error finding or running lsof") - } - - // First find the PID column index by parsing the first line, and - // select lines containing pid in the column. - pid := []byte(strconv.Itoa(os.Getpid())) - pidCol := -1 - - s := bufio.NewScanner(bytes.NewReader(raw)) - for s.Scan() { - line := s.Bytes() - fields := bytes.Fields(line) - if pidCol < 0 { - for i, v := range fields { - if bytes.Equal(v, []byte("PID")) { - pidCol = i - break - } - } - lsof = append(lsof, line...) - continue - } - if bytes.Equal(fields[pidCol], pid) { - lsof = append(lsof, '\n') - lsof = append(lsof, line...) - } - } - if pidCol < 0 { - t.Fatal("error processing lsof output: unexpected header format") + got := openFDs() + if !reflect.DeepEqual(got, want) { + t.Errorf("set of open file descriptors changed: got %v, want %v", got, want) } - if err := s.Err(); err != nil { - t.Fatalf("error processing lsof output: %v", err) - } - return bytes.Count(lsof, []byte("\n")), lsof } func TestExtraFilesFDShuffle(t *testing.T) { + maySkipHelperCommand("extraFilesAndPipes") testenv.SkipFlaky(t, 5780) switch runtime.GOOS { case "windows": @@ -560,25 +688,15 @@ func TestExtraFilesFDShuffle(t *testing.T) { if err != nil { t.Fatalf("Run: %v", err) } - ch := make(chan string, 1) - go func(ch chan string) { - buf := make([]byte, 512) - n, err := stderr.Read(buf) - if err != nil { - t.Errorf("Read: %s", err) - ch <- err.Error() - } else { - ch <- string(buf[:n]) - } - close(ch) - }(ch) - select { - case m := <-ch: - if m != expected { + + buf := make([]byte, 512) + n, err := stderr.Read(buf) + if err != nil { + t.Errorf("Read: %s", err) + } else { + if m := string(buf[:n]); m != expected { t.Errorf("Read: '%s' not '%s'", m, expected) } - case <-time.After(5 * time.Second): - t.Errorf("Read timedout") } c.Wait() } @@ -686,7 +804,7 @@ func TestExtraFiles(t *testing.T) { } c = exec.CommandContext(ctx, exe) - var stdout, stderr bytes.Buffer + var stdout, stderr strings.Builder c.Stdout = &stdout c.Stderr = &stderr c.ExtraFiles = []*os.File{tf} @@ -704,7 +822,7 @@ func TestExtraFiles(t *testing.T) { } err = c.Run() if err != nil { - t.Fatalf("Run: %v\n--- stdout:\n%s--- stderr:\n%s", err, stdout.Bytes(), stderr.Bytes()) + t.Fatalf("Run: %v\n--- stdout:\n%s--- stderr:\n%s", err, stdout.String(), stderr.String()) } if stdout.String() != text { t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text) @@ -713,6 +831,7 @@ func TestExtraFiles(t *testing.T) { func TestExtraFilesRace(t *testing.T) { if runtime.GOOS == "windows" { + maySkipHelperCommand("describefiles") t.Skip("no operating system support; skipping") } listen := func() net.Listener { @@ -770,167 +889,6 @@ func TestExtraFilesRace(t *testing.T) { } } -// TestHelperProcess isn't a real test. It's used as a helper process -// for TestParameterRun. -func TestHelperProcess(*testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - defer os.Exit(0) - - args := os.Args - for len(args) > 0 { - if args[0] == "--" { - args = args[1:] - break - } - args = args[1:] - } - if len(args) == 0 { - fmt.Fprintf(os.Stderr, "No command\n") - os.Exit(2) - } - - cmd, args := args[0], args[1:] - switch cmd { - case "echo": - iargs := []interface{}{} - for _, s := range args { - iargs = append(iargs, s) - } - fmt.Println(iargs...) - case "echoenv": - for _, s := range args { - fmt.Println(os.Getenv(s)) - } - os.Exit(0) - case "cat": - if len(args) == 0 { - io.Copy(os.Stdout, os.Stdin) - return - } - exit := 0 - for _, fn := range args { - f, err := os.Open(fn) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - exit = 2 - } else { - defer f.Close() - io.Copy(os.Stdout, f) - } - } - os.Exit(exit) - case "pipetest": - bufr := bufio.NewReader(os.Stdin) - for { - line, _, err := bufr.ReadLine() - if err == io.EOF { - break - } else if err != nil { - os.Exit(1) - } - if bytes.HasPrefix(line, []byte("O:")) { - os.Stdout.Write(line) - os.Stdout.Write([]byte{'\n'}) - } else if bytes.HasPrefix(line, []byte("E:")) { - os.Stderr.Write(line) - os.Stderr.Write([]byte{'\n'}) - } else { - os.Exit(1) - } - } - case "stdinClose": - b, err := io.ReadAll(os.Stdin) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - if s := string(b); s != stdinCloseTestString { - fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString) - os.Exit(1) - } - os.Exit(0) - case "exit": - n, _ := strconv.Atoi(args[0]) - os.Exit(n) - case "describefiles": - f := os.NewFile(3, fmt.Sprintf("fd3")) - ln, err := net.FileListener(f) - if err == nil { - fmt.Printf("fd3: listener %s\n", ln.Addr()) - ln.Close() - } - os.Exit(0) - case "extraFilesAndPipes": - n, _ := strconv.Atoi(args[0]) - pipes := make([]*os.File, n) - for i := 0; i < n; i++ { - pipes[i] = os.NewFile(uintptr(3+i), strconv.Itoa(i)) - } - response := "" - for i, r := range pipes { - ch := make(chan string, 1) - go func(c chan string) { - buf := make([]byte, 10) - n, err := r.Read(buf) - if err != nil { - fmt.Fprintf(os.Stderr, "Child: read error: %v on pipe %d\n", err, i) - os.Exit(1) - } - c <- string(buf[:n]) - close(c) - }(ch) - select { - case m := <-ch: - response = response + m - case <-time.After(5 * time.Second): - fmt.Fprintf(os.Stderr, "Child: Timeout reading from pipe: %d\n", i) - os.Exit(1) - } - } - fmt.Fprintf(os.Stderr, "child: %s", response) - os.Exit(0) - case "exec": - cmd := exec.Command(args[1]) - cmd.Dir = args[0] - output, err := cmd.CombinedOutput() - if err != nil { - fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output)) - os.Exit(1) - } - fmt.Printf("%s", string(output)) - os.Exit(0) - case "lookpath": - p, err := exec.LookPath(args[0]) - if err != nil { - fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err) - os.Exit(1) - } - fmt.Print(p) - os.Exit(0) - case "stderrfail": - fmt.Fprintf(os.Stderr, "some stderr text\n") - os.Exit(1) - case "sleep": - time.Sleep(3 * time.Second) - os.Exit(0) - case "pipehandle": - handle, _ := strconv.ParseUint(args[0], 16, 64) - pipe := os.NewFile(uintptr(handle), "") - _, err := fmt.Fprint(pipe, args[1]) - if err != nil { - fmt.Fprintf(os.Stderr, "writing to pipe failed: %v\n", err) - os.Exit(1) - } - pipe.Close() - os.Exit(0) - default: - fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd) - os.Exit(2) - } -} - type delayedInfiniteReader struct{} func (delayedInfiniteReader) Read(b []byte) (int, error) { @@ -943,12 +901,10 @@ func (delayedInfiniteReader) Read(b []byte) (int, error) { // Issue 9173: ignore stdin pipe writes if the program completes successfully. func TestIgnorePipeErrorOnSuccess(t *testing.T) { - testenv.MustHaveExec(t) - testWith := func(r io.Reader) func(*testing.T) { return func(t *testing.T) { cmd := helperCommand(t, "echo", "foo") - var out bytes.Buffer + var out strings.Builder cmd.Stdin = r cmd.Stdout = &out if err := cmd.Run(); err != nil { @@ -970,32 +926,15 @@ func (w *badWriter) Write(data []byte) (int, error) { } func TestClosePipeOnCopyError(t *testing.T) { - testenv.MustHaveExec(t) - - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { - t.Skipf("skipping test on %s - no yes command", runtime.GOOS) - } - cmd := exec.Command("yes") + cmd := helperCommand(t, "yes") cmd.Stdout = new(badWriter) - c := make(chan int, 1) - go func() { - err := cmd.Run() - if err == nil { - t.Errorf("yes completed successfully") - } - c <- 1 - }() - select { - case <-c: - // ok - case <-time.After(5 * time.Second): - t.Fatalf("yes got stuck writing to bad writer") + err := cmd.Run() + if err == nil { + t.Errorf("yes unexpectedly completed successfully") } } func TestOutputStderrCapture(t *testing.T) { - testenv.MustHaveExec(t) - cmd := helperCommand(t, "stderrfail") _, err := cmd.Output() ee, ok := err.(*exec.ExitError) @@ -1032,22 +971,23 @@ func TestContext(t *testing.T) { if n != len(buf) || err != nil || string(buf) != "O:hi\n" { t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n]) } - waitErr := make(chan error, 1) - go func() { - waitErr <- c.Wait() - }() - cancel() - select { - case err := <-waitErr: - if err == nil { - t.Fatal("expected Wait failure") - } - case <-time.After(3 * time.Second): - t.Fatal("timeout waiting for child process death") + go cancel() + + if err := c.Wait(); err == nil { + t.Fatal("expected Wait failure") } } func TestContextCancel(t *testing.T) { + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" { + maySkipHelperCommand("cat") + testenv.SkipFlaky(t, 42061) + } + + // To reduce noise in the final goroutine dump, + // let other parallel tests complete if possible. + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) defer cancel() c := helperCommandContext(t, ctx, "cat") @@ -1072,14 +1012,25 @@ func TestContextCancel(t *testing.T) { // Calling cancel should have killed the process, so writes // should now fail. Give the process a little while to die. start := time.Now() + delay := 1 * time.Millisecond for { if _, err := io.WriteString(stdin, "echo"); err != nil { break } + if time.Since(start) > time.Minute { - t.Fatal("canceling context did not stop program") + // Panic instead of calling t.Fatal so that we get a goroutine dump. + // We want to know exactly what the os/exec goroutines got stuck on. + panic("canceling context did not stop program") + } + + // Back off exponentially (up to 1-second sleeps) to give the OS time to + // terminate the process. + delay *= 2 + if delay > 1*time.Second { + delay = 1 * time.Second } - time.Sleep(time.Millisecond) + time.Sleep(delay) } if err := c.Wait(); err == nil { @@ -1091,10 +1042,8 @@ func TestContextCancel(t *testing.T) { // test that environment variables are de-duped. func TestDedupEnvEcho(t *testing.T) { - testenv.MustHaveExec(t) - cmd := helperCommand(t, "echoenv", "FOO") - cmd.Env = append(cmd.Env, "FOO=bad", "FOO=good") + cmd.Env = append(cmd.Environ(), "FOO=bad", "FOO=good") out, err := cmd.CombinedOutput() if err != nil { t.Fatal(err) @@ -1138,21 +1087,10 @@ func TestStringPathNotResolved(t *testing.T) { } } -// start a child process without the user code explicitly starting -// with a copy of the parent's. (The Windows SYSTEMROOT issue: Issue -// 25210) -func TestChildCriticalEnv(t *testing.T) { - testenv.MustHaveExec(t) - if runtime.GOOS != "windows" { - t.Skip("only testing on Windows") - } - cmd := helperCommand(t, "echoenv", "SYSTEMROOT") - cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - if strings.TrimSpace(string(out)) == "" { - t.Error("no SYSTEMROOT found") +func TestNoPath(t *testing.T) { + err := new(exec.Cmd).Start() + want := "exec: no command" + if err == nil || err.Error() != want { + t.Errorf("new(Cmd).Start() = %v, want %q", err, want) } } diff --git a/src/os/exec/exec_unix.go b/src/os/exec/exec_unix.go index 467c069e1ca48c..3ed672a7440fbe 100644 --- a/src/os/exec/exec_unix.go +++ b/src/os/exec/exec_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 && !windows -// +build !plan9,!windows package exec @@ -12,14 +11,14 @@ import ( "syscall" ) -func init() { - skipStdinCopyError = func(err error) bool { - // Ignore EPIPE errors copying to stdin if the program - // completed successfully otherwise. - // See Issue 9173. - pe, ok := err.(*fs.PathError) - return ok && - pe.Op == "write" && pe.Path == "|1" && - pe.Err == syscall.EPIPE - } +// skipStdinCopyError optionally specifies a function which reports +// whether the provided stdin copy error should be ignored. +func skipStdinCopyError(err error) bool { + // Ignore EPIPE errors copying to stdin if the program + // completed successfully otherwise. + // See Issue 9173. + pe, ok := err.(*fs.PathError) + return ok && + pe.Op == "write" && pe.Path == "|1" && + pe.Err == syscall.EPIPE } diff --git a/src/os/exec/exec_windows.go b/src/os/exec/exec_windows.go index bb937f8aed7552..e7a2ee6c9db6a7 100644 --- a/src/os/exec/exec_windows.go +++ b/src/os/exec/exec_windows.go @@ -9,15 +9,15 @@ import ( "syscall" ) -func init() { - skipStdinCopyError = func(err error) bool { - // Ignore ERROR_BROKEN_PIPE and ERROR_NO_DATA errors copying - // to stdin if the program completed successfully otherwise. - // See Issue 20445. - const _ERROR_NO_DATA = syscall.Errno(0xe8) - pe, ok := err.(*fs.PathError) - return ok && - pe.Op == "write" && pe.Path == "|1" && - (pe.Err == syscall.ERROR_BROKEN_PIPE || pe.Err == _ERROR_NO_DATA) - } +// skipStdinCopyError optionally specifies a function which reports +// whether the provided stdin copy error should be ignored. +func skipStdinCopyError(err error) bool { + // Ignore ERROR_BROKEN_PIPE and ERROR_NO_DATA errors copying + // to stdin if the program completed successfully otherwise. + // See Issue 20445. + const _ERROR_NO_DATA = syscall.Errno(0xe8) + pe, ok := err.(*fs.PathError) + return ok && + pe.Op == "write" && pe.Path == "|1" && + (pe.Err == syscall.ERROR_BROKEN_PIPE || pe.Err == _ERROR_NO_DATA) } diff --git a/src/os/exec/exec_windows_test.go b/src/os/exec/exec_windows_test.go index fbccffec0e9c97..35ae0b0b8af7ca 100644 --- a/src/os/exec/exec_windows_test.go +++ b/src/os/exec/exec_windows_test.go @@ -3,18 +3,35 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package exec_test import ( + "fmt" "io" "os" + "os/exec" "strconv" + "strings" "syscall" "testing" ) +func init() { + registerHelperCommand("pipehandle", cmdPipeHandle) +} + +func cmdPipeHandle(args ...string) { + handle, _ := strconv.ParseUint(args[0], 16, 64) + pipe := os.NewFile(uintptr(handle), "") + _, err := fmt.Fprint(pipe, args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "writing to pipe failed: %v\n", err) + os.Exit(1) + } + pipe.Close() +} + func TestPipePassing(t *testing.T) { r, w, err := os.Pipe() if err != nil { @@ -41,3 +58,41 @@ func TestPipePassing(t *testing.T) { t.Error(err) } } + +func TestNoInheritHandles(t *testing.T) { + cmd := exec.Command("cmd", "/c exit 88") + cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true} + err := cmd.Run() + exitError, ok := err.(*exec.ExitError) + if !ok { + t.Fatalf("got error %v; want ExitError", err) + } + if exitError.ExitCode() != 88 { + t.Fatalf("got exit code %d; want 88", exitError.ExitCode()) + } +} + +// start a child process without the user code explicitly starting +// with a copy of the parent's SYSTEMROOT. +// (See issue 25210.) +func TestChildCriticalEnv(t *testing.T) { + cmd := helperCommand(t, "echoenv", "SYSTEMROOT") + + // Explicitly remove SYSTEMROOT from the command's environment. + var env []string + for _, kv := range cmd.Environ() { + k, _, ok := strings.Cut(kv, "=") + if !ok || !strings.EqualFold(k, "SYSTEMROOT") { + env = append(env, kv) + } + } + cmd.Env = env + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + if strings.TrimSpace(string(out)) == "" { + t.Error("no SYSTEMROOT found") + } +} diff --git a/src/os/exec/internal/fdtest/exists_js.go b/src/os/exec/internal/fdtest/exists_js.go new file mode 100644 index 00000000000000..a7ce33c74f4259 --- /dev/null +++ b/src/os/exec/internal/fdtest/exists_js.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build js + +package fdtest + +import ( + "syscall" +) + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var s syscall.Stat_t + err := syscall.Fstat(int(fd), &s) + return err != syscall.EBADF +} diff --git a/src/os/exec/internal/fdtest/exists_plan9.go b/src/os/exec/internal/fdtest/exists_plan9.go new file mode 100644 index 00000000000000..8886e0602715eb --- /dev/null +++ b/src/os/exec/internal/fdtest/exists_plan9.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build plan9 + +package fdtest + +import ( + "syscall" +) + +const errBadFd = syscall.ErrorString("fd out of range or not open") + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var buf [1]byte + _, err := syscall.Fstat(int(fd), buf[:]) + return err != errBadFd +} diff --git a/src/os/exec/internal/fdtest/exists_test.go b/src/os/exec/internal/fdtest/exists_test.go new file mode 100644 index 00000000000000..a02dddf7f7de5e --- /dev/null +++ b/src/os/exec/internal/fdtest/exists_test.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fdtest + +import ( + "os" + "runtime" + "testing" +) + +func TestExists(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Exists not implemented for windows") + } + + if !Exists(os.Stdout.Fd()) { + t.Errorf("Exists(%d) got false want true", os.Stdout.Fd()) + } +} diff --git a/src/os/exec/internal/fdtest/exists_unix.go b/src/os/exec/internal/fdtest/exists_unix.go new file mode 100644 index 00000000000000..265cb69822043b --- /dev/null +++ b/src/os/exec/internal/fdtest/exists_unix.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +// Package fdtest provides test helpers for working with file descriptors across exec. +package fdtest + +import ( + "syscall" +) + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var s syscall.Stat_t + err := syscall.Fstat(int(fd), &s) + return err != syscall.EBADF +} diff --git a/src/os/exec/internal/fdtest/exists_windows.go b/src/os/exec/internal/fdtest/exists_windows.go new file mode 100644 index 00000000000000..72b8ccfd23c47e --- /dev/null +++ b/src/os/exec/internal/fdtest/exists_windows.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows + +package fdtest + +// Exists is not implemented on windows and panics. +func Exists(fd uintptr) bool { + panic("unimplemented") +} diff --git a/src/os/exec/lp_js.go b/src/os/exec/lp_js.go index 4eac25fe6f4021..54ddc4d5b483bc 100644 --- a/src/os/exec/lp_js.go +++ b/src/os/exec/lp_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package exec diff --git a/src/os/exec/lp_linux_test.go b/src/os/exec/lp_linux_test.go new file mode 100644 index 00000000000000..5d77427d89be6b --- /dev/null +++ b/src/os/exec/lp_linux_test.go @@ -0,0 +1,67 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package exec + +import ( + "internal/syscall/unix" + "os" + "path/filepath" + "syscall" + "testing" +) + +func TestFindExecutableVsNoexec(t *testing.T) { + // This test case relies on faccessat2(2) syscall, which appeared in Linux v5.8. + if major, minor := unix.KernelVersion(); major < 5 || (major == 5 && minor < 8) { + t.Skip("requires Linux kernel v5.8 with faccessat2(2) syscall") + } + + tmp := t.TempDir() + + // Create a tmpfs mount. + err := syscall.Mount("tmpfs", tmp, "tmpfs", 0, "") + if err != nil { + // Usually this means lack of CAP_SYS_ADMIN, but there might be + // other reasons, expecially in restricted test environments. + t.Skipf("requires ability to mount tmpfs (%v)", err) + } + t.Cleanup(func() { + if err := syscall.Unmount(tmp, 0); err != nil { + t.Error(err) + } + }) + + // Create an executable. + path := filepath.Join(tmp, "program") + err = os.WriteFile(path, []byte("#!/bin/sh\necho 123\n"), 0o755) + if err != nil { + t.Fatal(err) + } + + // Check that it works as expected. + err = findExecutable(path) + if err != nil { + t.Fatalf("findExecutable: got %v, want nil", err) + } + + if err := Command(path).Run(); err != nil { + t.Fatalf("exec: got %v, want nil", err) + } + + // Remount with noexec flag. + err = syscall.Mount("", tmp, "", syscall.MS_REMOUNT|syscall.MS_NOEXEC, "") + if err != nil { + t.Fatalf("remount %s with noexec failed: %v", tmp, err) + } + + if err := Command(path).Run(); err == nil { + t.Fatal("exec on noexec filesystem: got nil, want error") + } + + err = findExecutable(path) + if err == nil { + t.Fatalf("findExecutable: got nil, want error") + } +} diff --git a/src/os/exec/lp_plan9.go b/src/os/exec/lp_plan9.go index e8826a5083b726..092684f03a236b 100644 --- a/src/os/exec/lp_plan9.go +++ b/src/os/exec/lp_plan9.go @@ -6,6 +6,7 @@ package exec import ( "errors" + "internal/godebug" "io/fs" "os" "path/filepath" @@ -30,7 +31,11 @@ func findExecutable(file string) error { // directories named by the path environment variable. // If file begins with "/", "#", "./", or "../", it is tried // directly and the path is not consulted. -// The result may be an absolute path or a path relative to the current directory. +// On success, the result is an absolute path. +// +// In older versions of Go, LookPath could return a path relative to the current directory. +// As of Go 1.19, LookPath will instead return that path along with an error satisfying +// errors.Is(err, ErrDot). See the package documentation for more details. func LookPath(file string) (string, error) { // skip the path lookup for these prefixes skip := []string{"/", "#", "./", "../"} @@ -49,6 +54,9 @@ func LookPath(file string) (string, error) { for _, dir := range filepath.SplitList(path) { path := filepath.Join(dir, file) if err := findExecutable(path); err == nil { + if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" { + return path, &Error{file, ErrDot} + } return path, nil } } diff --git a/src/os/exec/lp_unix.go b/src/os/exec/lp_unix.go index d1d246accf18b4..af68c2f268a842 100644 --- a/src/os/exec/lp_unix.go +++ b/src/os/exec/lp_unix.go @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package exec import ( "errors" + "internal/godebug" + "internal/syscall/unix" "io/fs" "os" "path/filepath" "strings" + "syscall" ) // ErrNotFound is the error resulting if a path search failed to find an executable file. @@ -23,7 +25,18 @@ func findExecutable(file string) error { if err != nil { return err } - if m := d.Mode(); !m.IsDir() && m&0111 != 0 { + m := d.Mode() + if m.IsDir() { + return syscall.EISDIR + } + err = unix.Eaccess(file, unix.X_OK) + // ENOSYS means Eaccess is not available or not implemented. + // EPERM can be returned by Linux containers employing seccomp. + // In both cases, fall back to checking the permission bits. + if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) { + return err + } + if m&0111 != 0 { return nil } return fs.ErrPermission @@ -32,7 +45,11 @@ func findExecutable(file string) error { // LookPath searches for an executable named file in the // directories named by the PATH environment variable. // If file contains a slash, it is tried directly and the PATH is not consulted. -// The result may be an absolute path or a path relative to the current directory. +// Otherwise, on success, the result is an absolute path. +// +// In older versions of Go, LookPath could return a path relative to the current directory. +// As of Go 1.19, LookPath will instead return that path along with an error satisfying +// errors.Is(err, ErrDot). See the package documentation for more details. func LookPath(file string) (string, error) { // NOTE(rsc): I wish we could use the Plan 9 behavior here // (only bypass the path if file begins with / or ./ or ../) @@ -53,6 +70,9 @@ func LookPath(file string) (string, error) { } path := filepath.Join(dir, file) if err := findExecutable(path); err == nil { + if !filepath.IsAbs(path) && godebug.Get("execerrdot") != "0" { + return path, &Error{file, ErrDot} + } return path, nil } } diff --git a/src/os/exec/lp_unix_test.go b/src/os/exec/lp_unix_test.go index 9fded0eb0af37c..ebeb5bb3ecd8e8 100644 --- a/src/os/exec/lp_unix_test.go +++ b/src/os/exec/lp_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package exec diff --git a/src/os/exec/lp_windows.go b/src/os/exec/lp_windows.go index e7a2cdf142a02d..ec45db7459bc56 100644 --- a/src/os/exec/lp_windows.go +++ b/src/os/exec/lp_windows.go @@ -6,10 +6,12 @@ package exec import ( "errors" + "internal/godebug" "io/fs" "os" "path/filepath" "strings" + "syscall" ) // ErrNotFound is the error resulting if a path search failed to find an executable file. @@ -53,10 +55,14 @@ func findExecutable(file string, exts []string) (string, error) { // LookPath searches for an executable named file in the // directories named by the PATH environment variable. -// If file contains a slash, it is tried directly and the PATH is not consulted. // LookPath also uses PATHEXT environment variable to match // a suitable candidate. -// The result may be an absolute path or a path relative to the current directory. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// Otherwise, on success, the result is an absolute path. +// +// In older versions of Go, LookPath could return a path relative to the current directory. +// As of Go 1.19, LookPath will instead return that path along with an error satisfying +// errors.Is(err, ErrDot). See the package documentation for more details. func LookPath(file string) (string, error) { var exts []string x := os.Getenv(`PATHEXT`) @@ -75,20 +81,62 @@ func LookPath(file string) (string, error) { } if strings.ContainsAny(file, `:\/`) { - if f, err := findExecutable(file, exts); err == nil { + f, err := findExecutable(file, exts) + if err == nil { return f, nil - } else { - return "", &Error{file, err} } + return "", &Error{file, err} } - if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { - return f, nil + + // On Windows, creating the NoDefaultCurrentDirectoryInExePath + // environment variable (with any value or no value!) signals that + // path lookups should skip the current directory. + // In theory we are supposed to call NeedCurrentDirectoryForExePathW + // "as the registry location of this environment variable can change" + // but that seems exceedingly unlikely: it would break all users who + // have configured their environment this way! + // https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw + // See also go.dev/issue/43947. + var ( + dotf string + dotErr error + ) + if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found { + if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { + if godebug.Get("execerrdot") == "0" { + return f, nil + } + dotf, dotErr = f, &Error{file, ErrDot} + } } + path := os.Getenv("path") for _, dir := range filepath.SplitList(path) { if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { + if dotErr != nil { + // https://go.dev/issue/53536: if we resolved a relative path implicitly, + // and it is the same executable that would be resolved from the explicit %PATH%, + // prefer the explicit name for the executable (and, likely, no error) instead + // of the equivalent implicit name with ErrDot. + // + // Otherwise, return the ErrDot for the implicit path as soon as we find + // out that the explicit one doesn't match. + dotfi, dotfiErr := os.Lstat(dotf) + fi, fiErr := os.Lstat(f) + if dotfiErr != nil || fiErr != nil || !os.SameFile(dotfi, fi) { + return dotf, dotErr + } + } + + if !filepath.IsAbs(f) && godebug.Get("execerrdot") != "0" { + return f, &Error{file, ErrDot} + } return f, nil } } + + if dotErr != nil { + return dotf, dotErr + } return "", &Error{file, ErrNotFound} } diff --git a/src/os/exec/lp_windows_test.go b/src/os/exec/lp_windows_test.go index bbf6a9b7f13b5e..1f609fffd0ddf8 100644 --- a/src/os/exec/lp_windows_test.go +++ b/src/os/exec/lp_windows_test.go @@ -8,6 +8,7 @@ package exec_test import ( + "errors" "fmt" "internal/testenv" "io" @@ -19,6 +20,34 @@ import ( "testing" ) +func init() { + registerHelperCommand("exec", cmdExec) + registerHelperCommand("lookpath", cmdLookPath) +} + +func cmdLookPath(args ...string) { + p, err := exec.LookPath(args[0]) + if err != nil { + fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err) + os.Exit(1) + } + fmt.Print(p) +} + +func cmdExec(args ...string) { + cmd := exec.Command(args[1]) + cmd.Dir = args[0] + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output)) + os.Exit(1) + } + fmt.Printf("%s", string(output)) +} + func installExe(t *testing.T, dest, src string) { fsrc, err := os.Open(src) if err != nil { @@ -66,10 +95,10 @@ type lookPathTest struct { fails bool // test is expected to fail } -func (test lookPathTest) runProg(t *testing.T, env []string, args ...string) (string, error) { - cmd := exec.Command(args[0], args[1:]...) +func (test lookPathTest) runProg(t *testing.T, env []string, cmd *exec.Cmd) (string, error) { cmd.Env = env cmd.Dir = test.rootDir + args := append([]string(nil), cmd.Args...) args[0] = filepath.Base(args[0]) cmdText := fmt.Sprintf("%q command", strings.Join(args, " ")) out, err := cmd.CombinedOutput() @@ -135,10 +164,9 @@ func (test lookPathTest) run(t *testing.T, tmpdir, printpathExe string) { // Run "cmd.exe /c test.searchFor" with new environment and // work directory set. All candidates are copies of printpath.exe. // These will output their program paths when run. - should, errCmd := test.runProg(t, env, "cmd", "/c", test.searchFor) + should, errCmd := test.runProg(t, env, exec.Command("cmd", "/c", test.searchFor)) // Run the lookpath program with new environment and work directory set. - env = append(env, "GO_WANT_HELPER_PROCESS=1") - have, errLP := test.runProg(t, env, os.Args[0], "-test.run=TestHelperProcess", "--", "lookpath", test.searchFor) + have, errLP := test.runProg(t, env, helperCommand(t, "lookpath", test.searchFor)) // Compare results. if errCmd == nil && errLP == nil { // both succeeded @@ -305,7 +333,7 @@ var lookPathTests = []lookPathTest{ }, } -func TestLookPath(t *testing.T) { +func TestLookPathWindows(t *testing.T) { tmp := t.TempDir() printpathExe := buildPrintPathExe(t, tmp) @@ -346,30 +374,26 @@ func (test commandTest) isSuccess(rootDir, output string, err error) error { return nil } -func (test commandTest) runOne(rootDir string, env []string, dir, arg0 string) error { - cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess", "--", "exec", dir, arg0) +func (test commandTest) runOne(t *testing.T, rootDir string, env []string, dir, arg0 string) { + cmd := helperCommand(t, "exec", dir, arg0) cmd.Dir = rootDir cmd.Env = env output, err := cmd.CombinedOutput() err = test.isSuccess(rootDir, string(output), err) if (err != nil) != test.fails { if test.fails { - return fmt.Errorf("test=%+v: succeeded, but expected to fail", test) + t.Errorf("test=%+v: succeeded, but expected to fail", test) + } else { + t.Error(err) } - return err } - return nil } func (test commandTest) run(t *testing.T, rootDir, printpathExe string) { createFiles(t, rootDir, test.files, printpathExe) PATHEXT := `.COM;.EXE;.BAT` env := createEnv(rootDir, test.PATH, PATHEXT) - env = append(env, "GO_WANT_HELPER_PROCESS=1") - err := test.runOne(rootDir, env, test.dir, test.arg0) - if err != nil { - t.Error(err) - } + test.runOne(t, rootDir, env, test.dir, test.arg0) } var commandTests = []commandTest{ diff --git a/src/os/exec/read3.go b/src/os/exec/read3.go index a8c71831d8806e..8327d73e514bea 100644 --- a/src/os/exec/read3.go +++ b/src/os/exec/read3.go @@ -3,11 +3,10 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // This is a test program that verifies that it can read from // descriptor 3 and that no other descriptors are open. -// This is not done via TestHelperProcess and GO_WANT_HELPER_PROCESS +// This is not done via TestHelperProcess and GO_EXEC_TEST_PID // because we want to ensure that this program does not use cgo, // because C libraries can open file descriptors behind our backs // and confuse the test. See issue 25628. @@ -19,12 +18,15 @@ import ( "io" "os" "os/exec" + "os/exec/internal/fdtest" "runtime" "strings" ) func main() { fd3 := os.NewFile(3, "fd3") + defer fd3.Close() + bs, err := io.ReadAll(fd3) if err != nil { fmt.Printf("ReadAll from fd 3: %v\n", err) @@ -38,65 +40,52 @@ func main() { // descriptor from parent == 3 // All descriptors 4 and up should be available, // except for any used by the network poller. - var files []*os.File - for wantfd := uintptr(4); wantfd <= 100; wantfd++ { - if poll.IsPollDescriptor(wantfd) { + for fd := uintptr(4); fd <= 100; fd++ { + if poll.IsPollDescriptor(fd) { continue } - f, err := os.Open(os.Args[0]) - if err != nil { - fmt.Printf("error opening file with expected fd %d: %v", wantfd, err) - os.Exit(1) + + if !fdtest.Exists(fd) { + continue } - if got := f.Fd(); got != wantfd { - fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd) - fdfile := fmt.Sprintf("/proc/self/fd/%d", wantfd) - link, err := os.Readlink(fdfile) - fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err) - var args []string - switch runtime.GOOS { - case "plan9": - args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} - case "aix", "solaris", "illumos": - args = []string{fmt.Sprint(os.Getpid())} - default: - args = []string{"-p", fmt.Sprint(os.Getpid())} - } - // Determine which command to use to display open files. - ofcmd := "lsof" - switch runtime.GOOS { - case "dragonfly", "freebsd", "netbsd", "openbsd": - ofcmd = "fstat" - case "plan9": - ofcmd = "/bin/cat" - case "aix": - ofcmd = "procfiles" - case "solaris", "illumos": - ofcmd = "pfiles" - } + fmt.Printf("leaked parent file. fdtest.Exists(%d) got true want false\n", fd) + + fdfile := fmt.Sprintf("/proc/self/fd/%d", fd) + link, err := os.Readlink(fdfile) + fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err) - cmd := exec.Command(ofcmd, args...) - out, err := cmd.CombinedOutput() - if err != nil { - fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err) - } - fmt.Printf("%s", out) - os.Exit(1) + var args []string + switch runtime.GOOS { + case "plan9": + args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} + case "aix", "solaris", "illumos": + args = []string{fmt.Sprint(os.Getpid())} + default: + args = []string{"-p", fmt.Sprint(os.Getpid())} } - files = append(files, f) - } - for _, f := range files { - f.Close() - } + // Determine which command to use to display open files. + ofcmd := "lsof" + switch runtime.GOOS { + case "dragonfly", "freebsd", "netbsd", "openbsd": + ofcmd = "fstat" + case "plan9": + ofcmd = "/bin/cat" + case "aix": + ofcmd = "procfiles" + case "solaris", "illumos": + ofcmd = "pfiles" + } - // Referring to fd3 here ensures that it is not - // garbage collected, and therefore closed, while - // executing the wantfd loop above. It doesn't matter - // what we do with fd3 as long as we refer to it; - // closing it is the easy choice. - fd3.Close() + cmd := exec.Command(ofcmd, args...) + out, err := cmd.CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err) + } + fmt.Printf("%s", out) + os.Exit(1) + } os.Stdout.Write(bs) } diff --git a/src/os/exec_plan9.go b/src/os/exec_plan9.go index cc84f976696290..69714ff79830d9 100644 --- a/src/os/exec_plan9.go +++ b/src/os/exec_plan9.go @@ -115,11 +115,11 @@ func (p *ProcessState) success() bool { return p.status.ExitStatus() == 0 } -func (p *ProcessState) sys() interface{} { +func (p *ProcessState) sys() any { return p.status } -func (p *ProcessState) sysUsage() interface{} { +func (p *ProcessState) sysUsage() any { return p.status } diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go index e8736f7c54eaab..e1e7d53a27af06 100644 --- a/src/os/exec_posix.go +++ b/src/os/exec_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package os @@ -88,11 +87,11 @@ func (p *ProcessState) success() bool { return p.status.ExitStatus() == 0 } -func (p *ProcessState) sys() interface{} { +func (p *ProcessState) sys() any { return p.status } -func (p *ProcessState) sysUsage() interface{} { +func (p *ProcessState) sysUsage() any { return p.rusage } diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go index d1bbeb752987c5..90a4a61222b6b8 100644 --- a/src/os/exec_unix.go +++ b/src/os/exec_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/exec_unix_test.go b/src/os/exec_unix_test.go index f14b3519fbe759..332ffe904125c7 100644 --- a/src/os/exec_unix_test.go +++ b/src/os/exec_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package os_test diff --git a/src/os/executable_path.go b/src/os/executable_path.go index 625430ecfca1e5..d6161bcb08bbec 100644 --- a/src/os/executable_path.go +++ b/src/os/executable_path.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || openbsd -// +build aix openbsd package os diff --git a/src/os/executable_plan9.go b/src/os/executable_plan9.go index ad7a4410dcdd03..8d8c83260f59ca 100644 --- a/src/os/executable_plan9.go +++ b/src/os/executable_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package os diff --git a/src/os/executable_procfs.go b/src/os/executable_procfs.go index 76ba0e6d085e5b..18348eab912aa0 100644 --- a/src/os/executable_procfs.go +++ b/src/os/executable_procfs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux || netbsd || (js && wasm) -// +build linux netbsd js,wasm package os diff --git a/src/os/executable_sysctl.go b/src/os/executable_sysctl.go index 039448b5573a92..3c2aeacf7da53f 100644 --- a/src/os/executable_sysctl.go +++ b/src/os/executable_sysctl.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || dragonfly -// +build freebsd dragonfly package os diff --git a/src/os/export_unix_test.go b/src/os/export_unix_test.go index 10f8312831792c..49c8dae0365c8f 100644 --- a/src/os/export_unix_test.go +++ b/src/os/export_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/fifo_test.go b/src/os/fifo_test.go index 007ed2912933bb..de70927961314c 100644 --- a/src/os/fifo_test.go +++ b/src/os/fifo_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd package os_test diff --git a/src/os/file.go b/src/os/file.go index e717f171e72580..e2eef8ec5d30dc 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -37,14 +37,12 @@ // Note: The maximum number of concurrent operations on a File may be limited by // the OS or the system. The number should be high, but exceeding it may degrade // performance or cause other issues. -// package os import ( "errors" "internal/poll" "internal/testlog" - "internal/unsafeheader" "io" "io/fs" "runtime" @@ -109,7 +107,7 @@ func (e *LinkError) Unwrap() error { return e.Err } -// Read reads up to len(b) bytes from the File. +// Read reads up to len(b) bytes from the File and stores them in b. // It returns the number of bytes read and any error encountered. // At end of file, Read returns 0, io.EOF. func (f *File) Read(b []byte) (n int, err error) { @@ -166,7 +164,7 @@ type onlyWriter struct { io.Writer } -// Write writes len(b) bytes to the File. +// Write writes len(b) bytes from b to the File. // It returns the number of bytes written and an error, if any. // Write returns a non-nil error when n != len(b). func (f *File) Write(b []byte) (n int, err error) { @@ -248,11 +246,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) { // WriteString is like Write, but writes the contents of string s rather than // a slice of bytes. func (f *File) WriteString(s string) (n int, err error) { - var b []byte - hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b)) - hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data - hdr.Cap = len(s) - hdr.Len = len(s) + b := unsafe.Slice(unsafe.StringData(s), len(s)) return f.Write(b) } @@ -621,8 +615,12 @@ func isWindowsNulName(name string) bool { // operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the // same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside // the /prefix tree, then using DirFS does not stop the access any more than using -// os.Open does. DirFS is therefore not a general substitute for a chroot-style security -// mechanism when the directory tree contains arbitrary content. +// os.Open does. Additionally, the root of the fs.FS returned for a relative path, +// DirFS("prefix"), will be affected by later calls to Chdir. DirFS is therefore not +// a general substitute for a chroot-style security mechanism when the directory tree +// contains arbitrary content. +// +// The result implements fs.StatFS. func DirFS(dir string) fs.FS { return dirFS(dir) } diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index 604aea607bcf30..93eb233e004a79 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -66,7 +66,7 @@ type dirInfo struct { func epipecheck(file *File, e error) { } -// DevNull is the name of the operating system's ``null device.'' +// DevNull is the name of the operating system's “null device.” // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". const DevNull = "/dev/null" @@ -139,7 +139,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { // Close closes the File, rendering it unusable for I/O. // On files that support SetDeadline, any pending I/O operations will -// be canceled and return immediately with an error. +// be canceled and return immediately with an ErrClosed error. // Close will return an error if it has already been called. func (f *File) Close() error { if err := f.checkValid("close"); err != nil { diff --git a/src/os/file_posix.go b/src/os/file_posix.go index 211f2a1798aba3..c6d18ffeb60ed3 100644 --- a/src/os/file_posix.go +++ b/src/os/file_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package os @@ -17,7 +16,7 @@ func sigpipe() // implemented in package runtime // Close closes the File, rendering it unusable for I/O. // On files that support SetDeadline, any pending I/O operations will -// be canceled and return immediately with an error. +// be canceled and return immediately with an ErrClosed error. // Close will return an error if it has already been called. func (f *File) Close() error { if f == nil { diff --git a/src/os/file_unix.go b/src/os/file_unix.go index a2531b9656278c..1833c26531602b 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os @@ -169,18 +168,28 @@ func newFile(fd uintptr, name string, kind newFileKind) *File { } } - if err := f.pfd.Init("file", pollable); err != nil { - // An error here indicates a failure to register - // with the netpoll system. That can happen for - // a file descriptor that is not supported by - // epoll/kqueue; for example, disk files on - // Linux systems. We assume that any real error - // will show up in later I/O. - } else if pollable { - // We successfully registered with netpoll, so put - // the file into nonblocking mode. - if err := syscall.SetNonblock(fdi, true); err == nil { + clearNonBlock := false + if pollable { + if kind == kindNonBlock { f.nonblock = true + } else if err := syscall.SetNonblock(fdi, true); err == nil { + f.nonblock = true + clearNonBlock = true + } else { + pollable = false + } + } + + // An error here indicates a failure to register + // with the netpoll system. That can happen for + // a file descriptor that is not supported by + // epoll/kqueue; for example, disk files on + // Linux systems. We assume that any real error + // will show up in later I/O. + // We do restore the blocking behavior if it was set by us. + if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock { + if err := syscall.SetNonblock(fdi, false); err == nil { + f.nonblock = false } } @@ -197,7 +206,7 @@ func epipecheck(file *File, e error) { } } -// DevNull is the name of the operating system's ``null device.'' +// DevNull is the name of the operating system's “null device.” // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". const DevNull = "/dev/null" diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 378e2b1c7d69c7..db5c27dd30f41f 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -95,7 +95,7 @@ type dirInfo struct { func epipecheck(file *File, e error) { } -// DevNull is the name of the operating system's ``null device.'' +// DevNull is the name of the operating system's “null device.” // On Unix-like systems, it is "/dev/null"; on Windows, "NUL". const DevNull = "NUL" @@ -401,10 +401,11 @@ func openSymlink(path string) (syscall.Handle, error) { // normaliseLinkPath converts absolute paths returned by // DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, ...) // into paths acceptable by all Windows APIs. -// For example, it coverts -// \??\C:\foo\bar into C:\foo\bar -// \??\UNC\foo\bar into \\foo\bar -// \??\Volume{abc}\ into C:\ +// For example, it converts +// +// \??\C:\foo\bar into C:\foo\bar +// \??\UNC\foo\bar into \\foo\bar +// \??\Volume{abc}\ into C:\ func normaliseLinkPath(path string) (string, error) { if len(path) < 4 || path[:4] != `\??\` { // unexpected path, return it as is diff --git a/src/os/os_test.go b/src/os/os_test.go index 506f1fb0ee6071..4c64afaef06081 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -5,7 +5,6 @@ package os_test import ( - "bytes" "errors" "flag" "fmt" @@ -28,6 +27,16 @@ import ( "time" ) +func TestMain(m *testing.M) { + if Getenv("GO_OS_TEST_DRAIN_STDIN") == "1" { + os.Stdout.Close() + io.Copy(io.Discard, os.Stdin) + Exit(0) + } + + Exit(m.Run()) +} + var dot = []string{ "dir_unix.go", "env.go", @@ -54,34 +63,31 @@ var sysdir = func() *sysDir { "libpowermanager.so", }, } - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - wd, err := syscall.Getwd() - if err != nil { - wd = err.Error() - } - sd := &sysDir{ - filepath.Join(wd, "..", ".."), - []string{ - "ResourceRules.plist", - "Info.plist", - }, - } - found := true - for _, f := range sd.files { - path := filepath.Join(sd.name, f) - if _, err := Stat(path); err != nil { - found = false - break - } - } - if found { - return sd + case "ios": + wd, err := syscall.Getwd() + if err != nil { + wd = err.Error() + } + sd := &sysDir{ + filepath.Join(wd, "..", ".."), + []string{ + "ResourceRules.plist", + "Info.plist", + }, + } + found := true + for _, f := range sd.files { + path := filepath.Join(sd.name, f) + if _, err := Stat(path); err != nil { + found = false + break } - // In a self-hosted iOS build the above files might - // not exist. Look for system files instead below. } + if found { + return sd + } + // In a self-hosted iOS build the above files might + // not exist. Look for system files instead below. case "windows": return &sysDir{ Getenv("SystemRoot") + "\\system32\\drivers\\etc", @@ -115,20 +121,16 @@ func size(name string, t *testing.T) int64 { if err != nil { t.Fatal("open failed:", err) } - defer file.Close() - var buf [100]byte - len := 0 - for { - n, e := file.Read(buf[0:]) - len += n - if e == io.EOF { - break - } - if e != nil { - t.Fatal("read failed:", e) + defer func() { + if err := file.Close(); err != nil { + t.Error(err) } + }() + n, err := io.Copy(io.Discard, file) + if err != nil { + t.Fatal(err) } - return int64(len) + return n } func equal(name1, name2 string) (r bool) { @@ -144,13 +146,8 @@ func equal(name1, name2 string) (r bool) { // localTmp returns a local temporary directory not on NFS. func localTmp() string { switch runtime.GOOS { - case "android", "windows": + case "android", "ios", "windows": return TempDir() - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - return TempDir() - } } return "/tmp" } @@ -225,6 +222,29 @@ func TestStatError(t *testing.T) { } } +func TestStatSymlinkLoop(t *testing.T) { + testenv.MustHaveSymlink(t) + + defer chtmpdir(t)() + + err := os.Symlink("x", "y") + if err != nil { + t.Fatal(err) + } + defer os.Remove("y") + + err = os.Symlink("y", "x") + if err != nil { + t.Fatal(err) + } + defer os.Remove("x") + + _, err = os.Stat("x") + if _, ok := err.(*fs.PathError); !ok { + t.Errorf("expected *PathError, got %T: %v\n", err, err) + } +} + func TestFstat(t *testing.T) { path := sfdir + "/" + sfname file, err1 := Open(path) @@ -294,12 +314,8 @@ func TestReadClosed(t *testing.T) { _, err = file.Read(b) e, ok := err.(*PathError) - if !ok { - t.Fatalf("Read: %T(%v), want PathError", e, e) - } - - if e.Err != ErrClosed { - t.Errorf("Read: %v, want PathError(ErrClosed)", e) + if !ok || e.Err != ErrClosed { + t.Fatalf("Read: got %T(%v), want %T(%v)", err, err, e, ErrClosed) } } @@ -574,15 +590,12 @@ func TestReaddirnamesOneAtATime(t *testing.T) { switch runtime.GOOS { case "android": dir = "/system/bin" - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - wd, err := Getwd() - if err != nil { - t.Fatal(err) - } - dir = wd + case "ios": + wd, err := Getwd() + if err != nil { + t.Fatal(err) } + dir = wd case "plan9": dir = "/bin" case "windows": @@ -1149,7 +1162,7 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { } w.Close() - var b bytes.Buffer + var b strings.Builder io.Copy(&b, r) output := b.String() @@ -1403,22 +1416,19 @@ func TestChdirAndGetwd(t *testing.T) { dirs = []string{"/system/bin"} case "plan9": dirs = []string{"/", "/usr"} - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - dirs = nil - for _, d := range []string{"d1", "d2"} { - dir, err := os.MkdirTemp("", d) - if err != nil { - t.Fatalf("TempDir: %v", err) - } - // Expand symlinks so path equality tests work. - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatalf("EvalSymlinks: %v", err) - } - dirs = append(dirs, dir) + case "ios": + dirs = nil + for _, d := range []string{"d1", "d2"} { + dir, err := os.MkdirTemp("", d) + if err != nil { + t.Fatalf("TempDir: %v", err) + } + // Expand symlinks so path equality tests work. + dir, err = filepath.EvalSymlinks(dir) + if err != nil { + t.Fatalf("EvalSymlinks: %v", err) } + dirs = append(dirs, dir) } } oldwd := Getenv("PWD") @@ -1684,21 +1694,26 @@ func runBinHostname(t *testing.T) string { t.Fatal(err) } defer r.Close() - const path = "/bin/hostname" + + path, err := osexec.LookPath("hostname") + if err != nil { + if errors.Is(err, osexec.ErrNotFound) { + t.Skip("skipping test; test requires hostname but it does not exist") + } + t.Fatal(err) + } + argv := []string{"hostname"} if runtime.GOOS == "aix" { argv = []string{"hostname", "-s"} } p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}}) if err != nil { - if _, err := Stat(path); IsNotExist(err) { - t.Skipf("skipping test; test requires %s but it does not exist", path) - } t.Fatal(err) } w.Close() - var b bytes.Buffer + var b strings.Builder io.Copy(&b, r) _, err = p.Wait() if err != nil { @@ -1721,7 +1736,7 @@ func runBinHostname(t *testing.T) string { func testWindowsHostname(t *testing.T, hostname string) { cmd := osexec.Command("hostname") - out, err := cmd.CombinedOutput() + out, err := cmd.Output() if err != nil { t.Fatalf("Failed to execute hostname command: %v %s", err, out) } @@ -1761,8 +1776,8 @@ func TestHostname(t *testing.T) { // and the /bin/hostname only returns the first component want := runBinHostname(t) if hostname != want { - i := strings.Index(hostname, ".") - if i < 0 || hostname[0:i] != want { + host, _, ok := strings.Cut(hostname, ".") + if !ok || host != want { t.Errorf("Hostname() = %q, want %q", hostname, want) } } @@ -2277,9 +2292,18 @@ func testKillProcess(t *testing.T, processKiller func(p *Process)) { testenv.MustHaveExec(t) t.Parallel() - // Re-exec the test binary itself to emulate "sleep 1". - cmd := osexec.Command(Args[0], "-test.run", "TestSleep") - err := cmd.Start() + // Re-exec the test binary to start a process that hangs until stdin is closed. + cmd := osexec.Command(Args[0]) + cmd.Env = append(os.Environ(), "GO_OS_TEST_DRAIN_STDIN=1") + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + stdin, err := cmd.StdinPipe() + if err != nil { + t.Fatal(err) + } + err = cmd.Start() if err != nil { t.Fatalf("Failed to start test process: %v", err) } @@ -2288,19 +2312,14 @@ func testKillProcess(t *testing.T, processKiller func(p *Process)) { if err := cmd.Wait(); err == nil { t.Errorf("Test process succeeded, but expected to fail") } + stdin.Close() // Keep stdin alive until the process has finished dying. }() - time.Sleep(100 * time.Millisecond) - processKiller(cmd.Process) -} + // Wait for the process to be started. + // (It will close its stdout when it reaches TestMain.) + io.Copy(io.Discard, stdout) -// TestSleep emulates "sleep 1". It is a helper for testKillProcess, so we -// don't have to rely on an external "sleep" command being available. -func TestSleep(t *testing.T) { - if testing.Short() { - t.Skip("Skipping in short mode") - } - time.Sleep(time.Second) + processKiller(cmd.Process) } func TestKillStartProcess(t *testing.T) { @@ -2410,6 +2429,9 @@ func TestRemoveAllRace(t *testing.T) { // like it does on Unix. t.Skip("skipping on windows") } + if runtime.GOOS == "dragonfly" { + testenv.SkipFlaky(t, 52301) + } n := runtime.GOMAXPROCS(16) defer runtime.GOMAXPROCS(n) @@ -2438,8 +2460,6 @@ func TestRemoveAllRace(t *testing.T) { // Test that reading from a pipe doesn't use up a thread. func TestPipeThreads(t *testing.T) { switch runtime.GOOS { - case "freebsd": - t.Skip("skipping on FreeBSD; issue 19093") case "illumos", "solaris": t.Skip("skipping on Solaris and illumos; issue 19111") case "windows": @@ -2519,9 +2539,9 @@ func testDoubleCloseError(t *testing.T, path string) { if err := file.Close(); err == nil { t.Error("second Close did not fail") } else if pe, ok := err.(*PathError); !ok { - t.Errorf("second Close returned unexpected error type %T; expected fs.PathError", pe) + t.Errorf("second Close: got %T, want %T", err, pe) } else if pe.Err != ErrClosed { - t.Errorf("second Close returned %q, wanted %q", err, ErrClosed) + t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed) } else { t.Logf("second close returned expected error %q", err) } diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go index 9b4c0ab290bc07..c3c703f860b6fd 100644 --- a/src/os/os_unix_test.go +++ b/src/os/os_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os_test diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 195c30f50efd8b..41a066dcbcadff 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -643,29 +643,6 @@ func TestDeleteReadOnly(t *testing.T) { } } -func TestStatSymlinkLoop(t *testing.T) { - testenv.MustHaveSymlink(t) - - defer chtmpdir(t)() - - err := os.Symlink("x", "y") - if err != nil { - t.Fatal(err) - } - defer os.Remove("y") - - err = os.Symlink("y", "x") - if err != nil { - t.Fatal(err) - } - defer os.Remove("x") - - _, err = os.Stat("x") - if _, ok := err.(*fs.PathError); !ok { - t.Errorf("expected *PathError, got %T: %v\n", err, err) - } -} - func TestReadStdin(t *testing.T) { old := poll.ReadConsole defer func() { diff --git a/src/os/path_test.go b/src/os/path_test.go index 4535abbc527b04..59f72834859e2f 100644 --- a/src/os/path_test.go +++ b/src/os/path_test.go @@ -96,13 +96,8 @@ func TestMkdirAllWithSymlink(t *testing.T) { func TestMkdirAllAtSlash(t *testing.T) { switch runtime.GOOS { - case "android", "plan9", "windows": + case "android", "ios", "plan9", "windows": t.Skipf("skipping on %s", runtime.GOOS) - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - t.Skipf("skipping on darwin/arm64, mkdir returns EPERM") - } } RemoveAll("/_go_os_test") const dir = "/_go_os_test/dir" diff --git a/src/os/path_unix.go b/src/os/path_unix.go index db383594920e97..3c6310a4dfdacf 100644 --- a/src/os/path_unix.go +++ b/src/os/path_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/path_windows.go b/src/os/path_windows.go index a96245f358c99c..3356908a3609ec 100644 --- a/src/os/path_windows.go +++ b/src/os/path_windows.go @@ -11,7 +11,7 @@ const ( // IsPathSeparator reports whether c is a directory separator character. func IsPathSeparator(c uint8) bool { - // NOTE: Windows accept / as path separator. + // NOTE: Windows accepts / as path separator. return c == '\\' || c == '/' } diff --git a/src/os/pipe2_bsd.go b/src/os/pipe2_bsd.go deleted file mode 100644 index bf6d081db5b83c..00000000000000 --- a/src/os/pipe2_bsd.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build dragonfly || freebsd || netbsd || openbsd -// +build dragonfly freebsd netbsd openbsd - -package os - -import "syscall" - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an error, if any. -func Pipe() (r *File, w *File, err error) { - var p [2]int - - e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) - if e != nil { - return nil, nil, NewSyscallError("pipe", e) - } - - return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil -} diff --git a/src/os/pipe2_illumos.go b/src/os/pipe2_illumos.go deleted file mode 100644 index 71b8cb8e25be57..00000000000000 --- a/src/os/pipe2_illumos.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build illumos -// +build illumos - -package os - -import ( - "internal/syscall/unix" - "syscall" -) - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an error, if any. -func Pipe() (r *File, w *File, err error) { - var p [2]int - - e := unix.Pipe2(p[0:], syscall.O_CLOEXEC) - if e != nil { - return nil, nil, NewSyscallError("pipe", e) - } - - return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil -} diff --git a/src/os/pipe2_unix.go b/src/os/pipe2_unix.go new file mode 100644 index 00000000000000..1e2e8ccb67cb78 --- /dev/null +++ b/src/os/pipe2_unix.go @@ -0,0 +1,22 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) + if e != nil { + return nil, nil, NewSyscallError("pipe2", e) + } + + return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil +} diff --git a/src/os/pipe_bsd.go b/src/os/pipe_bsd.go deleted file mode 100644 index 097b32e7eb33cf..00000000000000 --- a/src/os/pipe_bsd.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build aix || darwin || (js && wasm) || (solaris && !illumos) -// +build aix darwin js,wasm solaris,!illumos - -package os - -import "syscall" - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an error, if any. -func Pipe() (r *File, w *File, err error) { - var p [2]int - - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e := syscall.Pipe(p[0:]) - if e != nil { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - - return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil -} diff --git a/src/os/pipe_linux.go b/src/os/pipe_linux.go deleted file mode 100644 index acd7b88e1d45d7..00000000000000 --- a/src/os/pipe_linux.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package os - -import "syscall" - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an error, if any. -func Pipe() (r *File, w *File, err error) { - var p [2]int - - e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) - // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it - // might not be implemented. - if e == syscall.ENOSYS { - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e = syscall.Pipe(p[0:]) - if e != nil { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - } else if e != nil { - return nil, nil, NewSyscallError("pipe2", e) - } - - return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil -} diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go index 41a1e9c78aa365..26565853e1ff9d 100644 --- a/src/os/pipe_test.go +++ b/src/os/pipe_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // Test broken pipes on Unix systems. +// //go:build !plan9 && !js -// +build !plan9,!js package os_test @@ -151,11 +151,6 @@ func TestStdPipeHelper(t *testing.T) { } func testClosedPipeRace(t *testing.T, read bool) { - switch runtime.GOOS { - case "freebsd": - t.Skip("FreeBSD does not use the poller; issue 19093") - } - limit := 1 if !read { // Get the amount we have to write to overload a pipe diff --git a/src/os/pipe_unix.go b/src/os/pipe_unix.go new file mode 100644 index 00000000000000..710f77670e2763 --- /dev/null +++ b/src/os/pipe_unix.go @@ -0,0 +1,28 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || (js && wasm) + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if e != nil { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil +} diff --git a/src/os/rawconn.go b/src/os/rawconn.go index ffc598b0618987..14a495d9c0a59d 100644 --- a/src/os/rawconn.go +++ b/src/os/rawconn.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package os diff --git a/src/os/rawconn_test.go b/src/os/rawconn_test.go index 8aebaf87a60760..62b99f87843020 100644 --- a/src/os/rawconn_test.go +++ b/src/os/rawconn_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // Test use of raw connections. +// //go:build !plan9 && !js -// +build !plan9,!js package os_test diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go index cb6a59abdb2cf5..982a2b6330c5f8 100644 --- a/src/os/readfrom_linux_test.go +++ b/src/os/readfrom_linux_test.go @@ -13,6 +13,7 @@ import ( . "os" "path/filepath" "strconv" + "strings" "syscall" "testing" "time" @@ -74,6 +75,56 @@ func TestCopyFileRange(t *testing.T) { mustSeekStart(t, dst2) mustContainData(t, dst2, data) // through traditional means }) + t.Run("CopyFileItself", func(t *testing.T) { + hook := hookCopyFileRange(t) + + f, err := os.CreateTemp("", "file-readfrom-itself-test") + if err != nil { + t.Fatalf("failed to create tmp file: %v", err) + } + t.Cleanup(func() { + f.Close() + os.Remove(f.Name()) + }) + + data := []byte("hello world!") + if _, err := f.Write(data); err != nil { + t.Fatalf("failed to create and feed the file: %v", err) + } + + if err := f.Sync(); err != nil { + t.Fatalf("failed to save the file: %v", err) + } + + // Rewind it. + if _, err := f.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to rewind the file: %v", err) + } + + // Read data from the file itself. + if _, err := io.Copy(f, f); err != nil { + t.Fatalf("failed to read from the file: %v", err) + } + + if !hook.called || hook.written != 0 || hook.handled || hook.err != nil { + t.Fatalf("poll.CopyFileRange should be called and return the EINVAL error, but got hook.called=%t, hook.err=%v", hook.called, hook.err) + } + + // Rewind it. + if _, err := f.Seek(0, io.SeekStart); err != nil { + t.Fatalf("failed to rewind the file: %v", err) + } + + data2, err := io.ReadAll(f) + if err != nil { + t.Fatalf("failed to read from the file: %v", err) + } + + // It should wind up a double of the original data. + if strings.Repeat(string(data), 2) != string(data2) { + t.Fatalf("data mismatch: %s != %s", string(data), string(data2)) + } + }) t.Run("NotRegular", func(t *testing.T) { t.Run("BothPipes", func(t *testing.T) { hook := hookCopyFileRange(t) @@ -344,6 +395,10 @@ type copyFileRangeHook struct { srcfd int remain int64 + written int64 + handled bool + err error + original func(dst, src *poll.FD, remain int64) (int64, bool, error) } @@ -354,7 +409,8 @@ func (h *copyFileRangeHook) install() { h.dstfd = dst.Sysfd h.srcfd = src.Sysfd h.remain = remain - return h.original(dst, src, remain) + h.written, h.handled, h.err = h.original(dst, src, remain) + return h.written, h.handled, h.err } } diff --git a/src/os/readfrom_stub.go b/src/os/readfrom_stub.go index 826760f3df65cc..8b7d5fb8f9e35c 100644 --- a/src/os/readfrom_stub.go +++ b/src/os/readfrom_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package os diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go index d04540bc63eb2c..8b46152a9e71cb 100644 --- a/src/os/removeall_at.go +++ b/src/os/removeall_at.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package os diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go index 853e0eddfc8920..2b8a7727f4f3e1 100644 --- a/src/os/removeall_noat.go +++ b/src/os/removeall_noat.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris +//go:build !unix package os diff --git a/src/os/rlimit.go b/src/os/rlimit.go new file mode 100644 index 00000000000000..a89414d098bbd1 --- /dev/null +++ b/src/os/rlimit.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package os + +import "syscall" + +// Some systems set an artificially low soft limit on open file count, for compatibility +// with code that uses select and its hard-coded maximum file descriptor +// (limited by the size of fd_set). +// +// Go does not use select, so it should not be subject to these limits. +// On some systems the limit is 256, which is very easy to run into, +// even in simple programs like gofmt when they parallelize walking +// a file tree. +// +// After a long discussion on go.dev/issue/46279, we decided the +// best approach was for Go to raise the limit unconditionally for itself, +// and then leave old software to set the limit back as needed. +// Code that really wants Go to leave the limit alone can set the hard limit, +// which Go of course has no choice but to respect. +func init() { + var lim syscall.Rlimit + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max { + lim.Cur = lim.Max + adjustFileLimit(&lim) + syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim) + } +} diff --git a/src/os/rlimit_darwin.go b/src/os/rlimit_darwin.go new file mode 100644 index 00000000000000..b28982a83a193d --- /dev/null +++ b/src/os/rlimit_darwin.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin + +package os + +import "syscall" + +// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go. +func adjustFileLimit(lim *syscall.Rlimit) { + // On older macOS, setrlimit(RLIMIT_NOFILE, lim) with lim.Cur = infinity fails. + // Set to the value of kern.maxfilesperproc instead. + n, err := syscall.SysctlUint32("kern.maxfilesperproc") + if err != nil { + return + } + if lim.Cur > uint64(n) { + lim.Cur = uint64(n) + } +} diff --git a/src/os/rlimit_stub.go b/src/os/rlimit_stub.go new file mode 100644 index 00000000000000..cbe28400c5b47d --- /dev/null +++ b/src/os/rlimit_stub.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package os + +import "syscall" + +// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go. +func adjustFileLimit(lim *syscall.Rlimit) {} diff --git a/src/os/rlimit_test.go b/src/os/rlimit_test.go new file mode 100644 index 00000000000000..c02e36f3f76a68 --- /dev/null +++ b/src/os/rlimit_test.go @@ -0,0 +1,40 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os_test + +import ( + . "os" + "runtime" + "testing" +) + +func TestOpenFileLimit(t *testing.T) { + // For open file count, + // macOS sets the default soft limit to 256 and no hard limit. + // CentOS and Fedora set the default soft limit to 1024, + // with hard limits of 4096 and 524288, respectively. + // Check that we can open 1200 files, which proves + // that the rlimit is being raised appropriately on those systems. + fileCount := 1200 + + // OpenBSD has a default soft limit of 512 and hard limit of 1024. + if runtime.GOOS == "openbsd" { + fileCount = 768 + } + + var files []*File + for i := 0; i < fileCount; i++ { + f, err := Open("rlimit.go") + if err != nil { + t.Error(err) + break + } + files = append(files, f) + } + + for _, f := range files { + f.Close() + } +} diff --git a/src/os/signal/doc.go b/src/os/signal/doc.go index 7af61d2d81746f..ab262edc58a2e4 100644 --- a/src/os/signal/doc.go +++ b/src/os/signal/doc.go @@ -8,7 +8,7 @@ Package signal implements access to incoming signals. Signals are primarily used on Unix-like systems. For the use of this package on Windows and Plan 9, see below. -Types of signals +# Types of signals The signals SIGKILL and SIGSTOP may not be caught by a program, and therefore cannot be affected by this package. @@ -33,7 +33,7 @@ by default is ^\ (Control-Backslash). In general you can cause a program to simply exit by pressing ^C, and you can cause it to exit with a stack dump by pressing ^\. -Default behavior of signals in Go programs +# Default behavior of signals in Go programs By default, a synchronous signal is converted into a run-time panic. A SIGHUP, SIGINT, or SIGTERM signal causes the program to exit. A @@ -55,7 +55,7 @@ and, on Linux, signals 32 (SIGCANCEL) and 33 (SIGSETXID) started by os.Exec, or by the os/exec package, will inherit the modified signal mask. -Changing the behavior of signals in Go programs +# Changing the behavior of signals in Go programs The functions in this package allow a program to change the way Go programs handle signals. @@ -88,7 +88,7 @@ for a blocked signal, it will be unblocked. If, later, Reset is called for that signal, or Stop is called on all channels passed to Notify for that signal, the signal will once again be blocked. -SIGPIPE +# SIGPIPE When a Go program writes to a broken pipe, the kernel will raise a SIGPIPE signal. @@ -109,7 +109,7 @@ This means that, by default, command line programs will behave like typical Unix command line programs, while other programs will not crash with SIGPIPE when writing to a closed network connection. -Go programs that use cgo or SWIG +# Go programs that use cgo or SWIG In a Go program that includes non-Go code, typically C/C++ code accessed using cgo or SWIG, Go's startup code normally runs first. It @@ -164,7 +164,7 @@ signal, and raises it again, to invoke any non-Go handler or default system handler. If the program does not exit, the Go handler then reinstalls itself and continues execution of the program. -Non-Go programs that call Go code +# Non-Go programs that call Go code When Go code is built with options like -buildmode=c-shared, it will be run as part of an existing non-Go program. The non-Go code may @@ -201,7 +201,7 @@ non-Go thread, it will act as described above, except that if there is an existing non-Go signal handler, that handler will be installed before raising the signal. -Windows +# Windows On Windows a ^C (Control-C) or ^BREAK (Control-Break) normally cause the program to exit. If Notify is called for os.Interrupt, ^C or ^BREAK @@ -217,11 +217,10 @@ CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT is received - the process will still get terminated unless it exits. But receiving syscall.SIGTERM will give the process an opportunity to clean up before termination. -Plan 9 +# Plan 9 On Plan 9, signals have type syscall.Note, which is a string. Calling Notify with a syscall.Note will cause that value to be sent on the channel when that string is posted as a note. - */ package signal diff --git a/src/os/signal/example_unix_test.go b/src/os/signal/example_unix_test.go index 3f7795b8cf9558..b7047ac45cdf2c 100644 --- a/src/os/signal/example_unix_test.go +++ b/src/os/signal/example_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package signal_test diff --git a/src/os/signal/internal/pty/pty.go b/src/os/signal/internal/pty/pty.go index 8d2eac71035f22..537febba553c69 100644 --- a/src/os/signal/internal/pty/pty.go +++ b/src/os/signal/internal/pty/pty.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (aix || darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd) && cgo -// +build aix darwin dragonfly freebsd linux,!android netbsd openbsd -// +build cgo // Package pty is a simple pseudo-terminal package for Unix systems, // implemented by calling C functions via cgo. diff --git a/src/os/signal/signal_cgo_test.go b/src/os/signal/signal_cgo_test.go index e1e4509e2a723b..67bad66e0b2fa2 100644 --- a/src/os/signal/signal_cgo_test.go +++ b/src/os/signal/signal_cgo_test.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd) && cgo -// +build darwin dragonfly freebsd linux,!android netbsd openbsd -// +build cgo // Note that this test does not work on Solaris: issue #22849. // Don't run the test on Android because at least some versions of the diff --git a/src/os/signal/signal_linux_test.go b/src/os/signal/signal_linux_test.go index 7abe1ec5a0a595..f70f108442bb07 100644 --- a/src/os/signal/signal_linux_test.go +++ b/src/os/signal/signal_linux_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package signal diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go index 649854b746c149..fec6db72a933df 100644 --- a/src/os/signal/signal_test.go +++ b/src/os/signal/signal_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package signal @@ -48,11 +47,8 @@ func init() { // instead need a test-skip and upstream bug filed against the Solaris // kernel). // - // This constant is chosen so as to make the test as generous as possible - // while still reliably completing within 3 minutes in non-short mode. - // // See https://golang.org/issue/33174. - settleTime = 11 * time.Second + settleTime = 5 * time.Second } else if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "ppc64") { // Older linux kernels seem to have some hiccups delivering the signal // in a timely manner on ppc64 and ppc64le. When running on a @@ -137,6 +133,9 @@ func TestSignal(t *testing.T) { // Using 10 is arbitrary. c1 := make(chan os.Signal, 10) Notify(c1) + // Stop relaying the SIGURG signals. See #49724 + Reset(syscall.SIGURG) + defer Stop(c1) // Send this process a SIGWINCH t.Logf("sigwinch...") @@ -711,7 +710,7 @@ func TestNotifyContextNotifications(t *testing.T) { } wg.Wait() <-ctx.Done() - fmt.Print("received SIGINT") + fmt.Println("received SIGINT") // Sleep to give time to simultaneous signals to reach the process. // These signals must be ignored given stop() is not called on this code. // We want to guarantee a SIGINT doesn't cause a premature termination of the program. @@ -728,11 +727,17 @@ func TestNotifyContextNotifications(t *testing.T) { {"multiple", 10}, } for _, tc := range testCases { + tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + var subTimeout time.Duration if deadline, ok := t.Deadline(); ok { - subTimeout := time.Until(deadline) - subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess. + timeout := time.Until(deadline) + if timeout < 2*settleTime { + t.Fatalf("starting test with less than %v remaining", 2*settleTime) + } + subTimeout = timeout - (timeout / 10) // Leave 10% headroom for cleaning up subprocess. } args := []string{ @@ -748,7 +753,7 @@ func TestNotifyContextNotifications(t *testing.T) { if err != nil { t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out) } - if want := []byte("received SIGINT"); !bytes.Contains(out, want) { + if want := []byte("received SIGINT\n"); !bytes.Contains(out, want) { t.Errorf("got %q, wanted %q", out, want) } }) diff --git a/src/os/signal/signal_unix.go b/src/os/signal/signal_unix.go index 9e241c43acef2c..772175a9223009 100644 --- a/src/os/signal/signal_unix.go +++ b/src/os/signal/signal_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package signal diff --git a/src/os/stat_darwin.go b/src/os/stat_darwin.go index 74214cefa446ea..b92ffd4a0a6ff4 100644 --- a/src/os/stat_darwin.go +++ b/src/os/stat_darwin.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtimespec) + fs.modTime = time.Unix(fs.sys.Mtimespec.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK, syscall.S_IFWHT: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(int64(ts.Sec), int64(ts.Nsec)) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atimespec.Unix()) } diff --git a/src/os/stat_dragonfly.go b/src/os/stat_dragonfly.go index 217bc6726d22cc..316c26c7cab7af 100644 --- a/src/os/stat_dragonfly.go +++ b/src/os/stat_dragonfly.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtim) + fs.modTime = time.Unix(fs.sys.Mtim.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(ts.Sec, ts.Nsec) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atim.Unix()) } diff --git a/src/os/stat_freebsd.go b/src/os/stat_freebsd.go index bab4ffa7984cf9..919ee44dd6b503 100644 --- a/src/os/stat_freebsd.go +++ b/src/os/stat_freebsd.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtimespec) + fs.modTime = time.Unix(fs.sys.Mtimespec.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(int64(ts.Sec), int64(ts.Nsec)) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atimespec.Unix()) } diff --git a/src/os/stat_js.go b/src/os/stat_js.go index 3badf5ba575b24..a137172e66de6f 100644 --- a/src/os/stat_js.go +++ b/src/os/stat_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package os @@ -15,7 +14,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtime, fs.sys.MtimeNsec) + fs.modTime = time.Unix(fs.sys.Mtime, fs.sys.MtimeNsec) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -44,12 +43,8 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(sec, nsec int64) time.Time { - return time.Unix(sec, nsec) -} - // For testing. func atime(fi FileInfo) time.Time { st := fi.Sys().(*syscall.Stat_t) - return timespecToTime(st.Atime, st.AtimeNsec) + return time.Unix(st.Atime, st.AtimeNsec) } diff --git a/src/os/stat_linux.go b/src/os/stat_linux.go index d36afa9ffd5e74..316c26c7cab7af 100644 --- a/src/os/stat_linux.go +++ b/src/os/stat_linux.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtim) + fs.modTime = time.Unix(fs.sys.Mtim.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(int64(ts.Sec), int64(ts.Nsec)) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atim.Unix()) } diff --git a/src/os/stat_netbsd.go b/src/os/stat_netbsd.go index 11ebcacab85f17..919ee44dd6b503 100644 --- a/src/os/stat_netbsd.go +++ b/src/os/stat_netbsd.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtimespec) + fs.modTime = time.Unix(fs.sys.Mtimespec.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(ts.Sec, int64(ts.Nsec)) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atimespec.Unix()) } diff --git a/src/os/stat_openbsd.go b/src/os/stat_openbsd.go index 9df2d7f7737efc..316c26c7cab7af 100644 --- a/src/os/stat_openbsd.go +++ b/src/os/stat_openbsd.go @@ -12,7 +12,7 @@ import ( func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtim) + fs.modTime = time.Unix(fs.sys.Mtim.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -41,11 +41,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(ts.Sec, int64(ts.Nsec)) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atim.Unix()) } diff --git a/src/os/stat_plan9.go b/src/os/stat_plan9.go index 57ae6fb0bbdf67..e20accf191321c 100644 --- a/src/os/stat_plan9.go +++ b/src/os/stat_plan9.go @@ -43,7 +43,7 @@ func fileInfoFromStat(d *syscall.Dir) *fileStat { } // arg is an open *File or a path string. -func dirstat(arg interface{}) (*syscall.Dir, error) { +func dirstat(arg any) (*syscall.Dir, error) { var name string var err error diff --git a/src/os/stat_solaris.go b/src/os/stat_solaris.go index 217bc6726d22cc..4e00ecb075f3a5 100644 --- a/src/os/stat_solaris.go +++ b/src/os/stat_solaris.go @@ -9,10 +9,18 @@ import ( "time" ) +// These constants aren't in the syscall package, which is frozen. +// Values taken from golang.org/x/sys/unix. +const ( + _S_IFNAM = 0x5000 + _S_IFDOOR = 0xd000 + _S_IFPORT = 0xe000 +) + func fillFileStatFromSys(fs *fileStat, name string) { fs.name = basename(name) fs.size = fs.sys.Size - fs.modTime = timespecToTime(fs.sys.Mtim) + fs.modTime = time.Unix(fs.sys.Mtim.Unix()) fs.mode = FileMode(fs.sys.Mode & 0777) switch fs.sys.Mode & syscall.S_IFMT { case syscall.S_IFBLK: @@ -29,6 +37,8 @@ func fillFileStatFromSys(fs *fileStat, name string) { // nothing to do case syscall.S_IFSOCK: fs.mode |= ModeSocket + case _S_IFNAM, _S_IFDOOR, _S_IFPORT: + fs.mode |= ModeIrregular } if fs.sys.Mode&syscall.S_ISGID != 0 { fs.mode |= ModeSetgid @@ -41,11 +51,7 @@ func fillFileStatFromSys(fs *fileStat, name string) { } } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(ts.Sec, ts.Nsec) -} - // For testing. func atime(fi FileInfo) time.Time { - return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) + return time.Unix(fi.Sys().(*syscall.Stat_t).Atim.Unix()) } diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go index 8c17805f71fc30..437afc02b48d8c 100644 --- a/src/os/stat_unix.go +++ b/src/os/stat_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/sticky_bsd.go b/src/os/sticky_bsd.go index ab23d8111d3b4e..e71daf7c7494ab 100644 --- a/src/os/sticky_bsd.go +++ b/src/os/sticky_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm netbsd openbsd solaris package os diff --git a/src/os/sticky_notbsd.go b/src/os/sticky_notbsd.go index 9979b43e8e69d0..9a87fbde92c2ba 100644 --- a/src/os/sticky_notbsd.go +++ b/src/os/sticky_notbsd.go @@ -3,14 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && (!js || !wasm) && !netbsd && !openbsd && !solaris -// +build !aix -// +build !darwin -// +build !dragonfly -// +build !freebsd -// +build !js !wasm -// +build !netbsd -// +build !openbsd -// +build !solaris package os diff --git a/src/os/sys_bsd.go b/src/os/sys_bsd.go index 1e245eb53a2d94..e272c245717cbb 100644 --- a/src/os/sys_bsd.go +++ b/src/os/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd -// +build darwin dragonfly freebsd js,wasm netbsd openbsd package os diff --git a/src/os/sys_js.go b/src/os/sys_js.go index 4d6a64e8ebeaec..4fd0e2d7c7dd12 100644 --- a/src/os/sys_js.go +++ b/src/os/sys_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package os diff --git a/src/os/sys_unix.go b/src/os/sys_unix.go index e316eaf06c4f76..79005c2cbd2041 100644 --- a/src/os/sys_unix.go +++ b/src/os/sys_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package os diff --git a/src/os/tempfile.go b/src/os/tempfile.go index 5b681fcebfea17..3be3d13dfb9c7f 100644 --- a/src/os/tempfile.go +++ b/src/os/tempfile.go @@ -46,7 +46,7 @@ func CreateTemp(dir, pattern string) (*File, error) { if try++; try < 10000 { continue } - return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist} + return nil, &PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: ErrExist} } return f, err } diff --git a/src/os/tempfile_test.go b/src/os/tempfile_test.go index e71a2444c97484..e5b74bc21f93c1 100644 --- a/src/os/tempfile_test.go +++ b/src/os/tempfile_test.go @@ -151,7 +151,7 @@ func TestMkdirTempBadDir(t *testing.T) { badDir := filepath.Join(dir, "not-exist") _, err = MkdirTemp(badDir, "foo") if pe, ok := err.(*fs.PathError); !ok || !IsNotExist(err) || pe.Path != badDir { - t.Errorf("TempDir error = %#v; want PathError for path %q satisifying IsNotExist", err, badDir) + t.Errorf("TempDir error = %#v; want PathError for path %q satisfying IsNotExist", err, badDir) } } diff --git a/src/os/timeout_test.go b/src/os/timeout_test.go index 6d65e420f044ef..ff0d77a413f4fa 100644 --- a/src/os/timeout_test.go +++ b/src/os/timeout_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package os_test @@ -59,6 +58,7 @@ var readTimeoutTests = []struct { {50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}}, } +// There is a very similar copy of this in net/timeout_test.go. func TestReadTimeout(t *testing.T) { t.Parallel() @@ -99,6 +99,7 @@ func TestReadTimeout(t *testing.T) { } } +// There is a very similar copy of this in net/timeout_test.go. func TestReadTimeoutMustNotReturn(t *testing.T) { t.Parallel() @@ -150,6 +151,7 @@ var writeTimeoutTests = []struct { {10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}}, } +// There is a very similar copy of this in net/timeout_test.go. func TestWriteTimeout(t *testing.T) { t.Parallel() @@ -187,6 +189,7 @@ func TestWriteTimeout(t *testing.T) { } } +// There is a very similar copy of this in net/timeout_test.go. func TestWriteTimeoutMustNotReturn(t *testing.T) { t.Parallel() @@ -231,28 +234,60 @@ func TestWriteTimeoutMustNotReturn(t *testing.T) { } } -func timeoutReader(r *os.File, d, min, max time.Duration, ch chan<- error) { - var err error - defer func() { ch <- err }() +const ( + // minDynamicTimeout is the minimum timeout to attempt for + // tests that automatically increase timeouts until success. + // + // Lower values may allow tests to succeed more quickly if the value is close + // to the true minimum, but may require more iterations (and waste more time + // and CPU power on failed attempts) if the timeout is too low. + minDynamicTimeout = 1 * time.Millisecond + + // maxDynamicTimeout is the maximum timeout to attempt for + // tests that automatically increase timeouts until succeess. + // + // This should be a strict upper bound on the latency required to hit a + // timeout accurately, even on a slow or heavily-loaded machine. If a test + // would increase the timeout beyond this value, the test fails. + maxDynamicTimeout = 4 * time.Second +) - t0 := time.Now() - if err = r.SetReadDeadline(time.Now().Add(d)); err != nil { - return - } - b := make([]byte, 256) - var n int - n, err = r.Read(b) - t1 := time.Now() - if n != 0 || err == nil || !isDeadlineExceeded(err) { - err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) - return +// timeoutUpperBound returns the maximum time that we expect a timeout of +// duration d to take to return the caller. +func timeoutUpperBound(d time.Duration) time.Duration { + switch runtime.GOOS { + case "openbsd", "netbsd": + // NetBSD and OpenBSD seem to be unable to reliably hit deadlines even when + // the absolute durations are long. + // In https://build.golang.org/log/c34f8685d020b98377dd4988cd38f0c5bd72267e, + // we observed that an openbsd-amd64-68 builder took 4.090948779s for a + // 2.983020682s timeout (37.1% overhead). + // (See https://go.dev/issue/50189 for further detail.) + // Give them lots of slop to compensate. + return d * 3 / 2 + } + // Other platforms seem to hit their deadlines more reliably, + // at least when they are long enough to cover scheduling jitter. + return d * 11 / 10 +} + +// nextTimeout returns the next timeout to try after an operation took the given +// actual duration with a timeout shorter than that duration. +func nextTimeout(actual time.Duration) (next time.Duration, ok bool) { + if actual >= maxDynamicTimeout { + return maxDynamicTimeout, false } - if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { - err = fmt.Errorf("Read took %s; expected %s", dt, d) - return + // Since the previous attempt took actual, we can't expect to beat that + // duration by any significant margin. Try the next attempt with an arbitrary + // factor above that, so that our growth curve is at least exponential. + next = actual * 5 / 4 + if next > maxDynamicTimeout { + return maxDynamicTimeout, true } + return next, true } +// There is a very similar copy of this in net/timeout_test.go. func TestReadTimeoutFluctuation(t *testing.T) { t.Parallel() @@ -263,47 +298,47 @@ func TestReadTimeoutFluctuation(t *testing.T) { defer r.Close() defer w.Close() - max := time.NewTimer(time.Second) - defer max.Stop() - ch := make(chan error) - go timeoutReader(r, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) - - select { - case <-max.C: - t.Fatal("Read took over 1s; expected 0.1s") - case err := <-ch: - if !isDeadlineExceeded(err) { - t.Fatal(err) + d := minDynamicTimeout + b := make([]byte, 256) + for { + t.Logf("SetReadDeadline(+%v)", d) + t0 := time.Now() + deadline := t0.Add(d) + if err = r.SetReadDeadline(deadline); err != nil { + t.Fatalf("SetReadDeadline(%v): %v", deadline, err) } - } -} + var n int + n, err = r.Read(b) + t1 := time.Now() -func timeoutWriter(w *os.File, d, min, max time.Duration, ch chan<- error) { - var err error - defer func() { ch <- err }() + if n != 0 || err == nil || !isDeadlineExceeded(err) { + t.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) + } - t0 := time.Now() - if err = w.SetWriteDeadline(time.Now().Add(d)); err != nil { - return - } - var n int - for { - n, err = w.Write([]byte("TIMEOUT WRITER")) - if err != nil { - break + actual := t1.Sub(t0) + if t1.Before(deadline) { + t.Errorf("Read took %s; expected at least %s", actual, d) } - } - t1 := time.Now() - if err == nil || !isDeadlineExceeded(err) { - err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err) - return - } - if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { - err = fmt.Errorf("Write took %s; expected %s", dt, d) - return + if t.Failed() { + return + } + if want := timeoutUpperBound(d); actual > want { + next, ok := nextTimeout(actual) + if !ok { + t.Fatalf("Read took %s; expected at most %v", actual, want) + } + // Maybe this machine is too slow to reliably schedule goroutines within + // the requested duration. Increase the timeout and try again. + t.Logf("Read took %s (expected %s); trying with longer timeout", actual, d) + d = next + continue + } + + break } } +// There is a very similar copy of this in net/timeout_test.go. func TestWriteTimeoutFluctuation(t *testing.T) { t.Parallel() @@ -314,27 +349,71 @@ func TestWriteTimeoutFluctuation(t *testing.T) { defer r.Close() defer w.Close() - d := time.Second - max := time.NewTimer(d) - defer max.Stop() - ch := make(chan error) - go timeoutWriter(w, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) + d := minDynamicTimeout + for { + t.Logf("SetWriteDeadline(+%v)", d) + t0 := time.Now() + deadline := t0.Add(d) + if err = w.SetWriteDeadline(deadline); err != nil { + t.Fatalf("SetWriteDeadline(%v): %v", deadline, err) + } + var n int64 + for { + var dn int + dn, err = w.Write([]byte("TIMEOUT TRANSMITTER")) + n += int64(dn) + if err != nil { + break + } + } + t1 := time.Now() - select { - case <-max.C: - t.Fatalf("Write took over %v; expected 0.1s", d) - case err := <-ch: - if !isDeadlineExceeded(err) { - t.Fatal(err) + if err == nil || !isDeadlineExceeded(err) { + t.Fatalf("Write did not return (any, timeout): (%d, %v)", n, err) + } + + actual := t1.Sub(t0) + if t1.Before(deadline) { + t.Errorf("Write took %s; expected at least %s", actual, d) } + if t.Failed() { + return + } + if want := timeoutUpperBound(d); actual > want { + if n > 0 { + // SetWriteDeadline specifies a time “after which I/O operations fail + // instead of blocking”. However, the kernel's send buffer is not yet + // full, we may be able to write some arbitrary (but finite) number of + // bytes to it without blocking. + t.Logf("Wrote %d bytes into send buffer; retrying until buffer is full", n) + if d <= maxDynamicTimeout/2 { + // We don't know how long the actual write loop would have taken if + // the buffer were full, so just guess and double the duration so that + // the next attempt can make twice as much progress toward filling it. + d *= 2 + } + } else if next, ok := nextTimeout(actual); !ok { + t.Fatalf("Write took %s; expected at most %s", actual, want) + } else { + // Maybe this machine is too slow to reliably schedule goroutines within + // the requested duration. Increase the timeout and try again. + t.Logf("Write took %s (expected %s); trying with longer timeout", actual, d) + d = next + } + continue + } + + break } } +// There is a very similar copy of this in net/timeout_test.go. func TestVariousDeadlines(t *testing.T) { t.Parallel() testVariousDeadlines(t) } +// There is a very similar copy of this in net/timeout_test.go. func TestVariousDeadlines1Proc(t *testing.T) { // Cannot use t.Parallel - modifies global GOMAXPROCS. if testing.Short() { @@ -344,6 +423,7 @@ func TestVariousDeadlines1Proc(t *testing.T) { testVariousDeadlines(t) } +// There is a very similar copy of this in net/timeout_test.go. func TestVariousDeadlines4Proc(t *testing.T) { // Cannot use t.Parallel - modifies global GOMAXPROCS. if testing.Short() { @@ -455,6 +535,7 @@ func testVariousDeadlines(t *testing.T) { } } +// There is a very similar copy of this in net/timeout_test.go. func TestReadWriteDeadlineRace(t *testing.T) { t.Parallel() diff --git a/src/os/types_plan9.go b/src/os/types_plan9.go index 125da661b79de9..ccf4fd932e7a53 100644 --- a/src/os/types_plan9.go +++ b/src/os/types_plan9.go @@ -15,13 +15,13 @@ type fileStat struct { size int64 mode FileMode modTime time.Time - sys interface{} + sys any } func (fs *fileStat) Size() int64 { return fs.size } func (fs *fileStat) Mode() FileMode { return fs.mode } func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) Sys() interface{} { return fs.sys } +func (fs *fileStat) Sys() any { return fs.sys } func sameFile(fs1, fs2 *fileStat) bool { a := fs1.sys.(*syscall.Dir) diff --git a/src/os/types_unix.go b/src/os/types_unix.go index e9b8b8ba3ac7af..1b90a5a14157a9 100644 --- a/src/os/types_unix.go +++ b/src/os/types_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package os @@ -24,7 +23,7 @@ type fileStat struct { func (fs *fileStat) Size() int64 { return fs.size } func (fs *fileStat) Mode() FileMode { return fs.mode } func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) Sys() interface{} { return &fs.sys } +func (fs *fileStat) Sys() any { return &fs.sys } func sameFile(fs1, fs2 *fileStat) bool { return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino diff --git a/src/os/types_windows.go b/src/os/types_windows.go index 59bf5ca38143dc..5443dfedc8ca21 100644 --- a/src/os/types_windows.go +++ b/src/os/types_windows.go @@ -138,7 +138,7 @@ func (fs *fileStat) ModTime() time.Time { } // Sys returns syscall.Win32FileAttributeData for file fs. -func (fs *fileStat) Sys() interface{} { +func (fs *fileStat) Sys() any { return &syscall.Win32FileAttributeData{ FileAttributes: fs.FileAttributes, CreationTime: fs.CreationTime, diff --git a/src/os/user/cgo_listgroups_unix.go b/src/os/user/cgo_listgroups_unix.go new file mode 100644 index 00000000000000..0d937da3345dcc --- /dev/null +++ b/src/os/user/cgo_listgroups_unix.go @@ -0,0 +1,48 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (dragonfly || darwin || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo + +package user + +import ( + "fmt" + "strconv" + "unsafe" +) + +/* +#include +#include +*/ +import "C" + +const maxGroups = 2048 + +func listGroups(u *User) ([]string, error) { + ug, err := strconv.Atoi(u.Gid) + if err != nil { + return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) + } + userGID := C.gid_t(ug) + nameC := make([]byte, len(u.Username)+1) + copy(nameC, u.Username) + + n := C.int(256) + gidsC := make([]C.gid_t, n) + rv := getGroupList((*C.char)(unsafe.Pointer(&nameC[0])), userGID, &gidsC[0], &n) + if rv == -1 { + // Mac is the only Unix that does not set n properly when rv == -1, so + // we need to use different logic for Mac vs. the other OS's. + if err := groupRetry(u.Username, nameC, userGID, &gidsC, &n); err != nil { + return nil, err + } + } + gidsC = gidsC[:n] + gids := make([]string, 0, n) + for _, g := range gidsC[:n] { + gids = append(gids, strconv.Itoa(int(g))) + } + return gids, nil +} diff --git a/src/os/user/cgo_lookup_unix.go b/src/os/user/cgo_lookup_unix.go index abc9e9ce6d617d..4f8577bbc95d8c 100644 --- a/src/os/user/cgo_lookup_unix.go +++ b/src/os/user/cgo_lookup_unix.go @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo -// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build cgo -// +build !osusergo +//go:build unix && !android && cgo && !osusergo package user @@ -125,9 +122,7 @@ func buildUser(pwd *C.struct_passwd) *User { // say: "It is expected to be a comma separated list of // personal data where the first item is the full name of the // user." - if i := strings.Index(u.Name, ","); i >= 0 { - u.Name = u.Name[:i] - } + u.Name, _, _ = strings.Cut(u.Name, ",") return u } diff --git a/src/os/user/cgo_unix_test.go b/src/os/user/cgo_unix_test.go index 9ec32b3a788969..6d16aa20b30ec8 100644 --- a/src/os/user/cgo_unix_test.go +++ b/src/os/user/cgo_unix_test.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo -// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build cgo -// +build !osusergo package user diff --git a/src/os/user/getgrouplist_darwin.go b/src/os/user/getgrouplist_darwin.go index 23d12e3247624e..db6fb87e23fb99 100644 --- a/src/os/user/getgrouplist_darwin.go +++ b/src/os/user/getgrouplist_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build cgo && !osusergo -// +build cgo,!osusergo package user diff --git a/src/os/user/getgrouplist_unix.go b/src/os/user/getgrouplist_unix.go index fd66961ccf7166..104c2243df618c 100644 --- a/src/os/user/getgrouplist_unix.go +++ b/src/os/user/getgrouplist_unix.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build (dragonfly || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo -// +build dragonfly freebsd !android,linux netbsd openbsd solaris,!illumos -// +build cgo -// +build !osusergo package user diff --git a/src/os/user/listgroups_aix.go b/src/os/user/listgroups_aix.go deleted file mode 100644 index d2fdfdc6b1e3b7..00000000000000 --- a/src/os/user/listgroups_aix.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build cgo && !osusergo -// +build cgo,!osusergo - -package user - -import "fmt" - -func listGroups(u *User) ([]string, error) { - return nil, fmt.Errorf("user: list groups for %s: not supported on AIX", u.Username) -} diff --git a/src/os/user/listgroups_illumos.go b/src/os/user/listgroups_illumos.go deleted file mode 100644 index d25e0339b99593..00000000000000 --- a/src/os/user/listgroups_illumos.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build cgo && !osusergo -// +build cgo,!osusergo - -// Even though this file requires no C, it is used to provide a -// listGroup stub because all the other illumos calls work. Otherwise, -// this stub will conflict with the lookup_stubs.go fallback. - -package user - -import "fmt" - -func listGroups(u *User) ([]string, error) { - return nil, fmt.Errorf("user: list groups for %s: not supported on illumos", u.Username) -} diff --git a/src/os/user/listgroups_stub.go b/src/os/user/listgroups_stub.go new file mode 100644 index 00000000000000..4cf808b65decc0 --- /dev/null +++ b/src/os/user/listgroups_stub.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build android || (js && !wasm) + +package user + +import ( + "errors" +) + +func init() { + groupListImplemented = false +} + +func listGroups(*User) ([]string, error) { + return nil, errors.New("user: list groups not implemented") +} diff --git a/src/os/user/listgroups_unix.go b/src/os/user/listgroups_unix.go index 38aa7653b054f6..fa2df4931c152e 100644 --- a/src/os/user/listgroups_unix.go +++ b/src/os/user/listgroups_unix.go @@ -1,51 +1,113 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (dragonfly || darwin || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo -// +build dragonfly darwin freebsd !android,linux netbsd openbsd solaris,!illumos -// +build cgo -// +build !osusergo +//go:build ((darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo)) || aix || illumos package user import ( + "bufio" + "bytes" + "errors" "fmt" + "io" + "os" "strconv" - "unsafe" ) -/* -#include -#include -*/ -import "C" +const groupFile = "/etc/group" -const maxGroups = 2048 +var colon = []byte{':'} -func listGroups(u *User) ([]string, error) { - ug, err := strconv.Atoi(u.Gid) +func listGroupsFromReader(u *User, r io.Reader) ([]string, error) { + if u.Username == "" { + return nil, errors.New("user: list groups: empty username") + } + primaryGid, err := strconv.Atoi(u.Gid) if err != nil { return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) } - userGID := C.gid_t(ug) - nameC := make([]byte, len(u.Username)+1) - copy(nameC, u.Username) - - n := C.int(256) - gidsC := make([]C.gid_t, n) - rv := getGroupList((*C.char)(unsafe.Pointer(&nameC[0])), userGID, &gidsC[0], &n) - if rv == -1 { - // Mac is the only Unix that does not set n properly when rv == -1, so - // we need to use different logic for Mac vs. the other OS's. - if err := groupRetry(u.Username, nameC, userGID, &gidsC, &n); err != nil { - return nil, err + + userCommas := []byte("," + u.Username + ",") // ,john, + userFirst := userCommas[1:] // john, + userLast := userCommas[:len(userCommas)-1] // ,john + userOnly := userCommas[1 : len(userCommas)-1] // john + + // Add primary Gid first. + groups := []string{u.Gid} + + rd := bufio.NewReader(r) + done := false + for !done { + line, err := rd.ReadBytes('\n') + if err != nil { + if err == io.EOF { + done = true + } else { + return groups, err + } + } + + // Look for username in the list of users. If user is found, + // append the GID to the groups slice. + + // There's no spec for /etc/passwd or /etc/group, but we try to follow + // the same rules as the glibc parser, which allows comments and blank + // space at the beginning of a line. + line = bytes.TrimSpace(line) + if len(line) == 0 || line[0] == '#' || + // If you search for a gid in a row where the group + // name (the first field) starts with "+" or "-", + // glibc fails to find the record, and so should we. + line[0] == '+' || line[0] == '-' { + continue + } + + // Format of /etc/group is + // groupname:password:GID:user_list + // for example + // wheel:x:10:john,paul,jack + // tcpdump:x:72: + listIdx := bytes.LastIndexByte(line, ':') + if listIdx == -1 || listIdx == len(line)-1 { + // No commas, or empty group list. + continue + } + if bytes.Count(line[:listIdx], colon) != 2 { + // Incorrect number of colons. + continue } + list := line[listIdx+1:] + // Check the list for user without splitting or copying. + if !(bytes.Equal(list, userOnly) || bytes.HasPrefix(list, userFirst) || bytes.HasSuffix(list, userLast) || bytes.Contains(list, userCommas)) { + continue + } + + // groupname:password:GID + parts := bytes.Split(line[:listIdx], colon) + if len(parts) != 3 || len(parts[0]) == 0 { + continue + } + gid := string(parts[2]) + // Make sure it's numeric and not the same as primary GID. + numGid, err := strconv.Atoi(gid) + if err != nil || numGid == primaryGid { + continue + } + + groups = append(groups, gid) } - gidsC = gidsC[:n] - gids := make([]string, 0, n) - for _, g := range gidsC[:n] { - gids = append(gids, strconv.Itoa(int(g))) + + return groups, nil +} + +func listGroups(u *User) ([]string, error) { + f, err := os.Open(groupFile) + if err != nil { + return nil, err } - return gids, nil + defer f.Close() + + return listGroupsFromReader(u, f) } diff --git a/src/os/user/listgroups_unix_test.go b/src/os/user/listgroups_unix_test.go new file mode 100644 index 00000000000000..a9f79ec6bb06ea --- /dev/null +++ b/src/os/user/listgroups_unix_test.go @@ -0,0 +1,107 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ((darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo)) || aix || illumos + +package user + +import ( + "fmt" + "sort" + "strings" + "testing" +) + +var testGroupFile = `# See the opendirectoryd(8) man page for additional +# information about Open Directory. +## +nobody:*:-2: +nogroup:*:-1: +wheel:*:0:root +emptyid:*::root +invalidgid:*:notanumber:root ++plussign:*:20:root +-minussign:*:21:root +# Next line is invalid (empty group name) +:*:22:root + +daemon:*:1:root + indented:*:7:root +# comment:*:4:found + # comment:*:4:found +kmem:*:2:root +manymembers:x:777:jill,jody,john,jack,jov,user777 +` + largeGroup() + +func largeGroup() (res string) { + var b strings.Builder + b.WriteString("largegroup:x:1000:user1") + for i := 2; i <= 7500; i++ { + fmt.Fprintf(&b, ",user%d", i) + } + return b.String() +} + +var listGroupsTests = []struct { + // input + in string + user string + gid string + // output + gids []string + err bool +}{ + {in: testGroupFile, user: "root", gid: "0", gids: []string{"0", "1", "2", "7"}}, + {in: testGroupFile, user: "jill", gid: "33", gids: []string{"33", "777"}}, + {in: testGroupFile, user: "jody", gid: "34", gids: []string{"34", "777"}}, + {in: testGroupFile, user: "john", gid: "35", gids: []string{"35", "777"}}, + {in: testGroupFile, user: "jov", gid: "37", gids: []string{"37", "777"}}, + {in: testGroupFile, user: "user777", gid: "7", gids: []string{"7", "777", "1000"}}, + {in: testGroupFile, user: "user1111", gid: "1111", gids: []string{"1111", "1000"}}, + {in: testGroupFile, user: "user1000", gid: "1000", gids: []string{"1000"}}, + {in: testGroupFile, user: "user7500", gid: "7500", gids: []string{"1000", "7500"}}, + {in: testGroupFile, user: "no-such-user", gid: "2345", gids: []string{"2345"}}, + {in: "", user: "no-such-user", gid: "2345", gids: []string{"2345"}}, + // Error cases. + {in: "", user: "", gid: "2345", err: true}, + {in: "", user: "joanna", gid: "bad", err: true}, +} + +func TestListGroups(t *testing.T) { + for _, tc := range listGroupsTests { + u := &User{Username: tc.user, Gid: tc.gid} + got, err := listGroupsFromReader(u, strings.NewReader(tc.in)) + if tc.err { + if err == nil { + t.Errorf("listGroups(%q): got nil; want error", tc.user) + } + continue // no more checks + } + if err != nil { + t.Errorf("listGroups(%q): got %v error, want nil", tc.user, err) + continue // no more checks + } + checkSameIDs(t, got, tc.gids) + } +} + +func checkSameIDs(t *testing.T, got, want []string) { + t.Helper() + if len(got) != len(want) { + t.Errorf("ID list mismatch: got %v; want %v", got, want) + return + } + sort.Strings(got) + sort.Strings(want) + mismatch := -1 + for i, g := range want { + if got[i] != g { + mismatch = i + break + } + } + if mismatch != -1 { + t.Errorf("ID list mismatch (at index %d): got %v; want %v", mismatch, got, want) + } +} diff --git a/src/os/user/lookup_android.go b/src/os/user/lookup_android.go index 151aab49c203d7..0ae31fd8189bf2 100644 --- a/src/os/user/lookup_android.go +++ b/src/os/user/lookup_android.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build android -// +build android package user diff --git a/src/os/user/lookup_plan9.go b/src/os/user/lookup_plan9.go index 33ae3a6adf416c..07939363e739c6 100644 --- a/src/os/user/lookup_plan9.go +++ b/src/os/user/lookup_plan9.go @@ -18,7 +18,9 @@ const ( ) func init() { + userImplemented = false groupImplemented = false + groupListImplemented = false } func current() (*User, error) { diff --git a/src/os/user/lookup_stubs.go b/src/os/user/lookup_stubs.go index c975a11964c11c..ce1617d2507bdf 100644 --- a/src/os/user/lookup_stubs.go +++ b/src/os/user/lookup_stubs.go @@ -3,22 +3,16 @@ // license that can be found in the LICENSE file. //go:build (!cgo && !windows && !plan9) || android || (osusergo && !windows && !plan9) -// +build !cgo,!windows,!plan9 android osusergo,!windows,!plan9 package user import ( - "errors" "fmt" "os" "runtime" "strconv" ) -func init() { - groupImplemented = false -} - func current() (*User, error) { uid := currentUID() // $USER and /etc/passwd may disagree; prefer the latter if we can get it. @@ -64,13 +58,6 @@ func current() (*User, error) { return u, fmt.Errorf("user: Current requires cgo or %s set in environment", missing) } -func listGroups(*User) ([]string, error) { - if runtime.GOOS == "android" || runtime.GOOS == "aix" { - return nil, fmt.Errorf("user: GroupIds not implemented on %s", runtime.GOOS) - } - return nil, errors.New("user: GroupIds requires cgo") -} - func currentUID() string { if id := os.Getuid(); id >= 0 { return strconv.Itoa(id) diff --git a/src/os/user/lookup_unix.go b/src/os/user/lookup_unix.go index 97c611fad42fdf..ed06e73fbc3367 100644 --- a/src/os/user/lookup_unix.go +++ b/src/os/user/lookup_unix.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo) -// +build aix darwin dragonfly freebsd js,wasm !android,linux netbsd openbsd solaris -// +build !cgo osusergo +//go:build ((unix && !android) || (js && wasm)) && (!cgo || osusergo) package user @@ -18,17 +16,10 @@ import ( "strings" ) -const groupFile = "/etc/group" const userFile = "/etc/passwd" -var colon = []byte{':'} - -func init() { - groupImplemented = false -} - // lineFunc returns a value, an error, or (nil, nil) to skip the row. -type lineFunc func(line []byte) (v interface{}, err error) +type lineFunc func(line []byte) (v any, err error) // readColonFile parses r as an /etc/group or /etc/passwd style file, running // fn for each row. readColonFile returns a value, an error, or (nil, nil) if @@ -36,7 +27,7 @@ type lineFunc func(line []byte) (v interface{}, err error) // // readCols is the minimum number of colon-separated fields that will be passed // to fn; in a long line additional fields may be silently discarded. -func readColonFile(r io.Reader, fn lineFunc, readCols int) (v interface{}, err error) { +func readColonFile(r io.Reader, fn lineFunc, readCols int) (v any, err error) { rd := bufio.NewReader(r) // Read the file line-by-line. @@ -107,7 +98,7 @@ func matchGroupIndexValue(value string, idx int) lineFunc { leadColon = ":" } substr := []byte(leadColon + value + ":") - return func(line []byte) (v interface{}, err error) { + return func(line []byte) (v any, err error) { if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 3 { return } @@ -154,7 +145,7 @@ func matchUserIndexValue(value string, idx int) lineFunc { leadColon = ":" } substr := []byte(leadColon + value + ":") - return func(line []byte) (v interface{}, err error) { + return func(line []byte) (v any, err error) { if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 6 { return } @@ -181,9 +172,7 @@ func matchUserIndexValue(value string, idx int) lineFunc { // say: "It is expected to be a comma separated list of // personal data where the first item is the full name of the // user." - if i := strings.Index(u.Name, ","); i >= 0 { - u.Name = u.Name[:i] - } + u.Name, _, _ = strings.Cut(u.Name, ",") return u, nil } } diff --git a/src/os/user/lookup_unix_test.go b/src/os/user/lookup_unix_test.go index 060cfe186f5ced..399a03fc3c1b2c 100644 --- a/src/os/user/lookup_unix_test.go +++ b/src/os/user/lookup_unix_test.go @@ -2,37 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && !cgo -// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build !cgo +//go:build unix && !android && !cgo package user import ( - "fmt" "reflect" "strings" "testing" ) -var testGroupFile = `# See the opendirectoryd(8) man page for additional -# information about Open Directory. -## -nobody:*:-2: -nogroup:*:-1: -wheel:*:0:root -emptyid:*::root -invalidgid:*:notanumber:root -+plussign:*:20:root --minussign:*:21:root - -daemon:*:1:root - indented:*:7: -# comment:*:4:found - # comment:*:4:found -kmem:*:2:root -` + largeGroup() - var groupTests = []struct { in string name string @@ -51,19 +30,10 @@ var groupTests = []struct { {testGroupFile, "indented", "7"}, {testGroupFile, "# comment", ""}, {testGroupFile, "largegroup", "1000"}, + {testGroupFile, "manymembers", "777"}, {"", "emptyfile", ""}, } -// Generate a proper "largegroup" entry for testGroupFile string -func largeGroup() (res string) { - var b strings.Builder - b.WriteString("largegroup:x:1000:user1") - for i := 2; i <= 7500; i++ { - fmt.Fprintf(&b, ",user%d", i) - } - return b.String() -} - func TestFindGroupName(t *testing.T) { for _, tt := range groupTests { got, err := findGroupName(tt.name, strings.NewReader(tt.in)) diff --git a/src/os/user/user.go b/src/os/user/user.go index c1b8101c8629cb..0307d2ad6a1271 100644 --- a/src/os/user/user.go +++ b/src/os/user/user.go @@ -6,11 +6,13 @@ Package user allows user account lookups by name or id. For most Unix systems, this package has two internal implementations of -resolving user and group ids to names. One is written in pure Go and -parses /etc/passwd and /etc/group. The other is cgo-based and relies on -the standard C library (libc) routines such as getpwuid_r and getgrnam_r. +resolving user and group ids to names, and listing supplementary group IDs. +One is written in pure Go and parses /etc/passwd and /etc/group. The other +is cgo-based and relies on the standard C library (libc) routines such as +getpwuid_r, getgrnam_r, and getgrouplist. -When cgo is available, cgo-based (libc-backed) code is used by default. +When cgo is available, and the required routines are implemented in libc +for a particular platform, cgo-based (libc-backed) code is used. This can be overridden by using osusergo build tag, which enforces the pure Go implementation. */ @@ -20,9 +22,12 @@ import ( "strconv" ) +// These may be set to false in init() for a particular platform and/or +// build flags to let the tests know to skip tests of some features. var ( - userImplemented = true // set to false by lookup_stubs.go's init - groupImplemented = true // set to false by lookup_stubs.go's init + userImplemented = true + groupImplemented = true + groupListImplemented = true ) // User represents a user account. diff --git a/src/os/user/user_test.go b/src/os/user/user_test.go index 49920317bed93b..80251749a7d670 100644 --- a/src/os/user/user_test.go +++ b/src/os/user/user_test.go @@ -5,7 +5,6 @@ package user import ( - "runtime" "testing" ) @@ -56,10 +55,6 @@ func compare(t *testing.T, want, got *User) { func TestLookup(t *testing.T) { checkUser(t) - if runtime.GOOS == "plan9" { - t.Skipf("Lookup not implemented on %q", runtime.GOOS) - } - want, err := Current() if err != nil { t.Fatalf("Current: %v", err) @@ -77,10 +72,6 @@ func TestLookup(t *testing.T) { func TestLookupId(t *testing.T) { checkUser(t) - if runtime.GOOS == "plan9" { - t.Skipf("LookupId not implemented on %q", runtime.GOOS) - } - want, err := Current() if err != nil { t.Fatalf("Current: %v", err) @@ -127,14 +118,15 @@ func TestLookupGroup(t *testing.T) { } } -func TestGroupIds(t *testing.T) { - checkGroup(t) - if runtime.GOOS == "aix" { - t.Skip("skipping GroupIds, see golang.org/issue/30563") - } - if runtime.GOOS == "illumos" { - t.Skip("skipping GroupIds, see golang.org/issue/14709") +func checkGroupList(t *testing.T) { + t.Helper() + if !groupListImplemented { + t.Skip("user: group list not implemented; skipping test") } +} + +func TestGroupIds(t *testing.T) { + checkGroupList(t) user, err := Current() if err != nil { t.Fatalf("Current(): %v", err) diff --git a/src/os/wait_unimp.go b/src/os/wait_unimp.go index 9bb85da80262b0..721b9f9f7e3323 100644 --- a/src/os/wait_unimp.go +++ b/src/os/wait_unimp.go @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || (js && wasm) || openbsd || solaris -// +build aix darwin js,wasm openbsd solaris +// aix, darwin, js/wasm, openbsd and solaris don't implement +// waitid/wait6. netbsd implements wait6, but that is causing test +// failures, see issue #48789. + +//go:build aix || darwin || (js && wasm) || netbsd || openbsd || solaris package os @@ -11,7 +14,9 @@ package os // succeed immediately, and reports whether it has done so. // It does not actually call p.Wait. // This version is used on systems that do not implement waitid, -// or where we have not implemented it yet. +// or where we have not implemented it yet. Note that this is racy: +// a call to Process.Signal can in an extremely unlikely case send a +// signal to the wrong process, see issue #13987. func (p *Process) blockUntilWaitable() (bool, error) { return false, nil } diff --git a/src/os/wait_wait6.go b/src/os/wait_wait6.go index 45b370a802e8ae..d395dac40b491b 100644 --- a/src/os/wait_wait6.go +++ b/src/os/wait_wait6.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || netbsd -// +build dragonfly freebsd netbsd +//go:build dragonfly || freebsd package os diff --git a/src/os/wait_waitid.go b/src/os/wait_waitid.go index 44962c8534e086..c0503b209c082c 100644 --- a/src/os/wait_waitid.go +++ b/src/os/wait_waitid.go @@ -6,7 +6,6 @@ // waitid returns if the process is stopped, even when using WEXITED. //go:build linux -// +build linux package os diff --git a/src/path/filepath/example_unix_test.go b/src/path/filepath/example_unix_test.go index 4ce10095e67360..b364cf0608de2b 100644 --- a/src/path/filepath/example_unix_test.go +++ b/src/path/filepath/example_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package filepath_test diff --git a/src/path/filepath/example_unix_walk_test.go b/src/path/filepath/example_unix_walk_test.go index d72efcebe61305..86146dba5be6cf 100644 --- a/src/path/filepath/example_unix_walk_test.go +++ b/src/path/filepath/example_unix_walk_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package filepath_test diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index c77a26952a657f..b5cc4b8cf3fc32 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -40,7 +40,6 @@ var ErrBadPattern = errors.New("syntax error in pattern") // // On Windows, escaping is disabled. Instead, '\\' is treated as // path separator. -// func Match(pattern, name string) (matched bool, err error) { Pattern: for len(pattern) > 0 { @@ -241,6 +240,16 @@ func getEsc(chunk string) (r rune, nchunk string, err error) { // The only possible returned error is ErrBadPattern, when pattern // is malformed. func Glob(pattern string) (matches []string, err error) { + return globWithLimit(pattern, 0) +} + +func globWithLimit(pattern string, depth int) (matches []string, err error) { + // This limit is used prevent stack exhaustion issues. See CVE-2022-30632. + const pathSeparatorsLimit = 10000 + if depth == pathSeparatorsLimit { + return nil, ErrBadPattern + } + // Check pattern is well-formed. if _, err := Match(pattern, ""); err != nil { return nil, err @@ -270,7 +279,7 @@ func Glob(pattern string) (matches []string, err error) { } var m []string - m, err = Glob(dir) + m, err = globWithLimit(dir, depth+1) if err != nil { return } diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go index 375c41a7e9d5d0..d6282596fedbb9 100644 --- a/src/path/filepath/match_test.go +++ b/src/path/filepath/match_test.go @@ -155,6 +155,16 @@ func TestGlob(t *testing.T) { } } +func TestCVE202230632(t *testing.T) { + // Prior to CVE-2022-30632, this would cause a stack exhaustion given a + // large number of separators (more than 4,000,000). There is now a limit + // of 10,000. + _, err := Glob("/*" + strings.Repeat("/", 10001)) + if err != ErrBadPattern { + t.Fatalf("Glob returned err=%v, want ErrBadPattern", err) + } +} + func TestGlobError(t *testing.T) { bad := []string{`[]`, `nonexist/[]`} for _, pattern := range bad { diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go index b56534deadecfe..c86b0c0ff819b0 100644 --- a/src/path/filepath/path.go +++ b/src/path/filepath/path.go @@ -67,13 +67,13 @@ const ( // by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: // -// 1. Replace multiple Separator elements with a single one. -// 2. Eliminate each . path name element (the current directory). -// 3. Eliminate each inner .. path name element (the parent directory) -// along with the non-.. element that precedes it. -// 4. Eliminate .. elements that begin a rooted path: -// that is, replace "/.." by "/" at the beginning of a path, -// assuming Separator is '/'. +// 1. Replace multiple Separator elements with a single one. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path, +// assuming Separator is '/'. // // The returned path ends in a slash only if it represents a root directory, // such as "/" on Unix or `C:\` on Windows. @@ -83,8 +83,8 @@ const ( // If the result of this process is an empty string, Clean // returns the string ".". // -// See also Rob Pike, ``Lexical File Names in Plan 9 or -// Getting Dot-Dot Right,'' +// See also Rob Pike, “Lexical File Names in Plan 9 or +// Getting Dot-Dot Right,” // https://9p.io/sys/doc/lexnames.html func Clean(path string) string { originalPath := path @@ -117,9 +117,21 @@ func Clean(path string) string { case os.IsPathSeparator(path[r]): // empty path element r++ - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): + case path[r] == '.' && r+1 == n: // . element r++ + case path[r] == '.' && os.IsPathSeparator(path[r+1]): + // ./ element + r++ + + for r < len(path) && os.IsPathSeparator(path[r]) { + r++ + } + if out.w == 0 && volumeNameLen(path[r:]) > 0 { + // When joining prefix "." and an absolute path on Windows, + // the prefix should not be removed. + out.append('.') + } case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): // .. element: remove to last separator r += 2 @@ -340,6 +352,11 @@ func Rel(basepath, targpath string) (string, error) { // as an error by any function. var SkipDir error = fs.SkipDir +// SkipAll is used as a return value from WalkFuncs to indicate that +// all remaining files and directories are to be skipped. It is not returned +// as an error by any function. +var SkipAll error = fs.SkipAll + // WalkFunc is the type of the function called by Walk to visit each // file or directory. // @@ -358,8 +375,9 @@ var SkipDir error = fs.SkipDir // The error result returned by the function controls how Walk continues. // If the function returns the special value SkipDir, Walk skips the // current directory (path if info.IsDir() is true, otherwise path's -// parent directory). Otherwise, if the function returns a non-nil error, -// Walk stops entirely and returns that error. +// parent directory). If the function returns the special value SkipAll, +// Walk skips all remaining files and directories. Otherwise, if the function +// returns a non-nil error, Walk stops entirely and returns that error. // // The err argument reports an error related to path, signaling that Walk // will not walk into that directory. The function can decide how to @@ -396,6 +414,9 @@ func walkDir(path string, d fs.DirEntry, walkDirFn fs.WalkDirFunc) error { // Second call, to report ReadDir error. err = walkDirFn(path, d, err) if err != nil { + if err == SkipDir && d.IsDir() { + err = nil + } return err } } @@ -426,7 +447,7 @@ func walk(path string, info fs.FileInfo, walkFn WalkFunc) error { if err != nil || err1 != nil { // The caller's behavior is controlled by the return value, which is decided // by walkFn. walkFn may ignore err and return nil. - // If walkFn returns SkipDir, it will be handled by the caller. + // If walkFn returns SkipDir or SkipAll, it will be handled by the caller. // So walk should return whatever walkFn returns. return err1 } @@ -468,7 +489,7 @@ func WalkDir(root string, fn fs.WalkDirFunc) error { } else { err = walkDir(root, &statDirEntry{info}, fn) } - if err == SkipDir { + if err == SkipDir || err == SkipAll { return nil } return err @@ -504,7 +525,7 @@ func Walk(root string, fn WalkFunc) error { } else { err = walk(root, info, fn) } - if err == SkipDir { + if err == SkipDir || err == SkipAll { return nil } return err diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index bc5509b49cf1ab..9bdc58ea358028 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -93,6 +93,9 @@ var wincleantests = []PathTest{ {`//host/share/foo/../baz`, `\\host\share\baz`}, {`\\a\b\..\c`, `\\a\b\c`}, {`\\a\b`, `\\a\b`}, + {`.\c:`, `.\c:`}, + {`.\c:\foo`, `.\c:\foo`}, + {`.\c:foo`, `.\c:foo`}, } func TestClean(t *testing.T) { @@ -635,6 +638,64 @@ func TestWalkSkipDirOnFile(t *testing.T) { }) } +func TestWalkSkipAllOnFile(t *testing.T) { + td := t.TempDir() + + if err := os.MkdirAll(filepath.Join(td, "dir", "subdir"), 0755); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Join(td, "dir2"), 0755); err != nil { + t.Fatal(err) + } + + touch(t, filepath.Join(td, "dir", "foo1")) + touch(t, filepath.Join(td, "dir", "foo2")) + touch(t, filepath.Join(td, "dir", "subdir", "foo3")) + touch(t, filepath.Join(td, "dir", "foo4")) + touch(t, filepath.Join(td, "dir2", "bar")) + touch(t, filepath.Join(td, "last")) + + remainingWereSkipped := true + walker := func(path string) error { + if strings.HasSuffix(path, "foo2") { + return filepath.SkipAll + } + + if strings.HasSuffix(path, "foo3") || + strings.HasSuffix(path, "foo4") || + strings.HasSuffix(path, "bar") || + strings.HasSuffix(path, "last") { + remainingWereSkipped = false + } + return nil + } + + walkFn := func(path string, _ fs.FileInfo, _ error) error { return walker(path) } + walkDirFn := func(path string, _ fs.DirEntry, _ error) error { return walker(path) } + + check := func(t *testing.T, walk func(root string) error, root string) { + t.Helper() + remainingWereSkipped = true + if err := walk(root); err != nil { + t.Fatal(err) + } + if !remainingWereSkipped { + t.Errorf("SkipAll on file foo2 did not block processing of remaining files and directories") + } + } + + t.Run("Walk", func(t *testing.T) { + Walk := func(_ string) error { return filepath.Walk(td, walkFn) } + check(t, Walk, td) + check(t, Walk, filepath.Join(td, "dir")) + }) + t.Run("WalkDir", func(t *testing.T) { + WalkDir := func(_ string) error { return filepath.WalkDir(td, walkDirFn) } + check(t, WalkDir, td) + check(t, WalkDir, filepath.Join(td, "dir")) + }) +} + func TestWalkFileError(t *testing.T) { td := t.TempDir() @@ -791,6 +852,8 @@ var winisabstests = []IsAbsTest{ {`c:a\b`, false}, {`c:\a\b`, true}, {`c:/a/b`, true}, + {`\\host\share`, true}, + {`\\host\share\`, true}, {`\\host\share\foo`, true}, {`//host/share/foo/bar`, true}, } @@ -1327,7 +1390,7 @@ func TestBug3486(t *testing.T) { // https://golang.org/issue/3486 if runtime.GOOS == "ios" { t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) } - root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") + root, err := filepath.EvalSymlinks(testenv.GOROOT(t) + "/test") if err != nil { t.Fatal(err) } @@ -1524,3 +1587,38 @@ func TestEvalSymlinksAboveRootChdir(t *testing.T) { t.Logf("EvalSymlinks(%q) = %q", check, resolved) } } + +func TestIssue51617(t *testing.T) { + dir := t.TempDir() + for _, sub := range []string{"a", filepath.Join("a", "bad"), filepath.Join("a", "next")} { + if err := os.Mkdir(filepath.Join(dir, sub), 0755); err != nil { + t.Fatal(err) + } + } + bad := filepath.Join(dir, "a", "bad") + if err := os.Chmod(bad, 0); err != nil { + t.Fatal(err) + } + defer os.Chmod(bad, 0700) // avoid errors on cleanup + var saw []string + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return filepath.SkipDir + } + if d.IsDir() { + rel, err := filepath.Rel(dir, path) + if err != nil { + t.Fatal(err) + } + saw = append(saw, rel) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + want := []string{".", "a", filepath.Join("a", "bad"), filepath.Join("a", "next")} + if !reflect.DeepEqual(saw, want) { + t.Errorf("got directories %v, want %v", saw, want) + } +} diff --git a/src/path/filepath/path_unix.go b/src/path/filepath/path_unix.go index d4b6f967a3f785..93fdfdd8a049d5 100644 --- a/src/path/filepath/path_unix.go +++ b/src/path/filepath/path_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package filepath diff --git a/src/path/filepath/path_windows.go b/src/path/filepath/path_windows.go index 445c868e414604..b4d8ac33010e75 100644 --- a/src/path/filepath/path_windows.go +++ b/src/path/filepath/path_windows.go @@ -45,6 +45,10 @@ func IsAbs(path string) (b bool) { if l == 0 { return false } + // If the volume name starts with a double slash, this is a UNC path. + if isSlash(path[0]) && isSlash(path[1]) { + return true + } path = path[l:] if path == "" { return false diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go index 76a459ac96b39d..9e6c0ec81d901c 100644 --- a/src/path/filepath/path_windows_test.go +++ b/src/path/filepath/path_windows_test.go @@ -197,13 +197,17 @@ func TestEvalSymlinksCanonicalNames(t *testing.T) { // (where c: is vol parameter) to discover "8dot3 name creation state". // The state is combination of 2 flags. The global flag controls if it // is per volume or global setting: -// 0 - Enable 8dot3 name creation on all volumes on the system -// 1 - Disable 8dot3 name creation on all volumes on the system -// 2 - Set 8dot3 name creation on a per volume basis -// 3 - Disable 8dot3 name creation on all volumes except the system volume +// +// 0 - Enable 8dot3 name creation on all volumes on the system +// 1 - Disable 8dot3 name creation on all volumes on the system +// 2 - Set 8dot3 name creation on a per volume basis +// 3 - Disable 8dot3 name creation on all volumes except the system volume +// // If global flag is set to 2, then per-volume flag needs to be examined: -// 0 - Enable 8dot3 name creation on this volume -// 1 - Disable 8dot3 name creation on this volume +// +// 0 - Enable 8dot3 name creation on this volume +// 1 - Disable 8dot3 name creation on this volume +// // checkVolume8dot3Setting verifies that "8dot3 name creation" flags // are set to 2 and 0, if enabled parameter is true, or 2 and 1, if enabled // is false. Otherwise checkVolume8dot3Setting returns error. @@ -530,3 +534,29 @@ func TestNTNamespaceSymlink(t *testing.T) { t.Errorf(`EvalSymlinks(%q): got %q, want %q`, filelink, got, want) } } + +func TestIssue52476(t *testing.T) { + tests := []struct { + lhs, rhs string + want string + }{ + {`..\.`, `C:`, `..\C:`}, + {`..`, `C:`, `..\C:`}, + {`.`, `:`, `:`}, + {`.`, `C:`, `.\C:`}, + {`.`, `C:/a/b/../c`, `.\C:\a\c`}, + {`.`, `\C:`, `.\C:`}, + {`C:\`, `.`, `C:\`}, + {`C:\`, `C:\`, `C:\C:`}, + {`C`, `:`, `C\:`}, + {`\.`, `C:`, `\C:`}, + {`\`, `C:`, `\C:`}, + } + + for _, test := range tests { + got := filepath.Join(test.lhs, test.rhs) + if got != test.want { + t.Errorf(`Join(%q, %q): got %q, want %q`, test.lhs, test.rhs, got, test.want) + } + } +} diff --git a/src/path/filepath/symlink_plan9.go b/src/path/filepath/symlink_plan9.go new file mode 100644 index 00000000000000..820d150d976e19 --- /dev/null +++ b/src/path/filepath/symlink_plan9.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filepath + +import ( + "os" + "strings" + "syscall" +) + +func evalSymlinks(path string) (string, error) { + // Plan 9 doesn't have symbolic links, so no need for substitutions. + if len(path) > 0 { + // Check validity of path + _, err := os.Lstat(path) + if err != nil { + // Return the same error value as on other operating systems + if strings.HasSuffix(err.Error(), "not a directory") { + err = syscall.ENOTDIR + } + return "", err + } + } + return Clean(path), nil +} diff --git a/src/path/filepath/symlink_unix.go b/src/path/filepath/symlink_unix.go index 657945a81a3361..f8980d5ad3886e 100644 --- a/src/path/filepath/symlink_unix.go +++ b/src/path/filepath/symlink_unix.go @@ -1,5 +1,4 @@ -//go:build !windows -// +build !windows +//go:build !windows && !plan9 package filepath diff --git a/src/path/filepath/symlink_windows.go b/src/path/filepath/symlink_windows.go index d72279e2bbc1a4..9a436d59780d92 100644 --- a/src/path/filepath/symlink_windows.go +++ b/src/path/filepath/symlink_windows.go @@ -49,11 +49,12 @@ func baseIsDotDot(path string) bool { // toNorm returns the normalized path that is guaranteed to be unique. // It should accept the following formats: -// * UNC paths (e.g \\server\share\foo\bar) -// * absolute paths (e.g C:\foo\bar) -// * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.) -// * relative paths begin with '\' (e.g \foo\bar) -// * relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .) +// - UNC paths (e.g \\server\share\foo\bar) +// - absolute paths (e.g C:\foo\bar) +// - relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.) +// - relative paths begin with '\' (e.g \foo\bar) +// - relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .) +// // The returned normalized path will be in the same form (of 5 listed above) as the input path. // If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B). // The normBase parameter should be equal to the normBase func, except for in tests. See docs on the normBase func. diff --git a/src/path/match.go b/src/path/match.go index 918624c60e80a3..673bbc7ff65996 100644 --- a/src/path/match.go +++ b/src/path/match.go @@ -34,7 +34,6 @@ var ErrBadPattern = errors.New("syntax error in pattern") // Match requires pattern to match all of name, not just a substring. // The only possible returned error is ErrBadPattern, when pattern // is malformed. -// func Match(pattern, name string) (matched bool, err error) { Pattern: for len(pattern) > 0 { diff --git a/src/path/path.go b/src/path/path.go index f1f3499f63e00c..547b9debce14c5 100644 --- a/src/path/path.go +++ b/src/path/path.go @@ -52,20 +52,20 @@ func (b *lazybuf) string() string { // by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: // -// 1. Replace multiple slashes with a single slash. -// 2. Eliminate each . path name element (the current directory). -// 3. Eliminate each inner .. path name element (the parent directory) -// along with the non-.. element that precedes it. -// 4. Eliminate .. elements that begin a rooted path: -// that is, replace "/.." by "/" at the beginning of a path. +// 1. Replace multiple slashes with a single slash. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path. // // The returned path ends in a slash only if it is the root "/". // // If the result of this process is an empty string, Clean // returns the string ".". // -// See also Rob Pike, ``Lexical File Names in Plan 9 or -// Getting Dot-Dot Right,'' +// See also Rob Pike, “Lexical File Names in Plan 9 or +// Getting Dot-Dot Right,” // https://9p.io/sys/doc/lexnames.html func Clean(path string) string { if path == "" { diff --git a/src/pgo/inline/inline_hot.go b/src/pgo/inline/inline_hot.go new file mode 100644 index 00000000000000..3da2f40611f257 --- /dev/null +++ b/src/pgo/inline/inline_hot.go @@ -0,0 +1,81 @@ +package main + +import ( + "time" +) + +type BS struct { + length uint + s []uint64 +} + +const wSize = uint(64) +const lWSize = uint(6) + +func D(i uint) int { + return int((i + (wSize - 1)) >> lWSize) +} + +func N(length uint) (bs *BS) { + bs = &BS{ + length, + make([]uint64, D(length)), + } + + return bs +} + +func (b *BS) S(i uint) *BS { + b.s[i>>lWSize] |= 1 << (i & (wSize - 1)) + return b +} + +var jn = [...]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +func T(v uint64) uint { + return uint(jn[((v&-v)*0x03f79d71b4ca8b09)>>58]) +} + +func (b *BS) NS(i uint) (uint, bool) { + x := int(i >> lWSize) + if x >= len(b.s) { + return 0, false + } + w := b.s[x] + w = w >> (i & (wSize - 1)) + if w != 0 { + return i + T(w), true + } + x = x + 1 + for x < len(b.s) { + if b.s[x] != 0 { + return uint(x)*wSize + T(b.s[x]), true + } + x = x + 1 + + } + return 0, false +} + +func A() { + s := N(100000) + for i := 0; i < 1000; i += 30 { + s.S(uint(i)) + } + for j := 0; j < 1000; j++ { + c := uint(0) + for i, e := s.NS(0); e; i, e = s.NS(i + 1) { + c++ + } + } +} + +func main() { + time.Sleep(time.Second) + A() +} diff --git a/src/pgo/inline/inline_hot_test.go b/src/pgo/inline/inline_hot_test.go new file mode 100644 index 00000000000000..f906ee7a4d9a7c --- /dev/null +++ b/src/pgo/inline/inline_hot_test.go @@ -0,0 +1,42 @@ +package main + +import "testing" + +func BenchmarkA(b *testing.B) { + benchmarkB(b) +} +func benchmarkB(b *testing.B) { + + for i := 0; true; { + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + } +} diff --git a/src/plugin/plugin.go b/src/plugin/plugin.go index 4a524bfa3f02eb..b2a0fbe3ea2f18 100644 --- a/src/plugin/plugin.go +++ b/src/plugin/plugin.go @@ -22,7 +22,7 @@ type Plugin struct { pluginpath string err string // set if plugin failed to load loaded chan struct{} // closed when loaded - syms map[string]interface{} + syms map[string]any } // Open opens a Go plugin. @@ -69,4 +69,4 @@ func (p *Plugin) Lookup(symName string) (Symbol, error) { // } // *v.(*int) = 7 // f.(func())() // prints "Hello, number 7" -type Symbol interface{} +type Symbol any diff --git a/src/plugin/plugin_dlopen.go b/src/plugin/plugin_dlopen.go index aa85d4831ce849..6ba0f780657a48 100644 --- a/src/plugin/plugin_dlopen.go +++ b/src/plugin/plugin_dlopen.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && cgo) || (darwin && cgo) || (freebsd && cgo) -// +build linux,cgo darwin,cgo freebsd,cgo package plugin @@ -103,7 +102,7 @@ func open(name string) (*Plugin, error) { } // Fill out the value of each plugin symbol. - updatedSyms := map[string]interface{}{} + updatedSyms := map[string]any{} for symName, sym := range syms { isFunc := symName[0] == '.' if isFunc { @@ -148,8 +147,9 @@ var ( ) // lastmoduleinit is defined in package runtime -func lastmoduleinit() (pluginpath string, syms map[string]interface{}, errstr string) +func lastmoduleinit() (pluginpath string, syms map[string]any, errstr string) // doInit is defined in package runtime +// //go:linkname doInit runtime.doInit func doInit(t unsafe.Pointer) // t should be a *runtime.initTask diff --git a/src/plugin/plugin_stubs.go b/src/plugin/plugin_stubs.go index 67855bed4ba82a..2e9492e7c657e6 100644 --- a/src/plugin/plugin_stubs.go +++ b/src/plugin/plugin_stubs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!linux && !freebsd && !darwin) || !cgo -// +build !linux,!freebsd,!darwin !cgo package plugin diff --git a/src/plugin/plugin_test.go b/src/plugin/plugin_test.go index 4ce912132c911f..557987cfa6c94b 100644 --- a/src/plugin/plugin_test.go +++ b/src/plugin/plugin_test.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux || (linux && !arm64) -// +build !linux linux,!arm64 - package plugin_test import ( diff --git a/src/race.bash b/src/race.bash index 81fb4be60646b7..f1a168bfbb7313 100755 --- a/src/race.bash +++ b/src/race.bash @@ -9,39 +9,21 @@ set -e function usage { - echo 'race detector is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, openbsd/amd64, darwin/amd64, and darwin/arm64' 1>&2 + echo 'race detector is only supported on linux/amd64, linux/ppc64le, linux/arm64, linux/s390x, freebsd/amd64, netbsd/amd64, openbsd/amd64, darwin/amd64, and darwin/arm64' 1>&2 exit 1 } -case $(uname) in -"Darwin") - if [ $(uname -m) != "x86_64" ] && [ $(uname -m) != "arm64" ]; then - usage - fi - ;; -"Linux") - if [ $(uname -m) != "x86_64" ] && [ $(uname -m) != "ppc64le" ] && [ $(uname -m) != "aarch64" ]; then - usage - fi - ;; -"FreeBSD") - if [ $(uname -m) != "amd64" ]; then - usage - fi - ;; -"NetBSD") - if [ $(uname -m) != "amd64" ]; then - usage - fi - ;; -"OpenBSD") - if [ $(uname -m) != "amd64" ]; then - usage - fi - ;; -*) - usage - ;; +case $(uname -s -m) in + "Darwin x86_64") ;; + "Darwin arm64") ;; + "Linux x86_64") ;; + "Linux ppc64le") ;; + "Linux aarch64") ;; + "Linux s390x") ;; + "FreeBSD amd64") ;; + "NetBSD amd64") ;; + "OpenBSD amd64") ;; + *) usage ;; esac if [ ! -f make.bash ]; then diff --git a/src/race.bat b/src/race.bat index 8f0355612cb3f3..d395e19f9741dd 100644 --- a/src/race.bat +++ b/src/race.bat @@ -16,11 +16,11 @@ goto end :ok set GOROOT=%CD%\.. -call make.bat --dist-tool >NUL +call .\make.bat --dist-tool >NUL if errorlevel 1 goto fail .\cmd\dist\dist.exe env -w -p >env.bat if errorlevel 1 goto fail -call env.bat +call .\env.bat del env.bat if %GOHOSTARCH% == amd64 goto continue @@ -28,7 +28,7 @@ echo Race detector is only supported on windows/amd64. goto fail :continue -call make.bat --no-banner --no-local +call .\make.bat --no-banner --no-local if %GOBUILDFAIL%==1 goto end echo # go install -race std go install -race std diff --git a/src/reflect/abi.go b/src/reflect/abi.go index 17b79a8394fad5..32cb314188704c 100644 --- a/src/reflect/abi.go +++ b/src/reflect/abi.go @@ -6,7 +6,7 @@ package reflect import ( "internal/abi" - "internal/goexperiment" + "internal/goarch" "unsafe" ) @@ -29,9 +29,9 @@ import ( // commented out there should be the actual values once // we're ready to use the register ABI everywhere. var ( - intArgRegs = abi.IntArgRegs * goexperiment.RegabiArgsInt - floatArgRegs = abi.FloatArgRegs * goexperiment.RegabiArgsInt - floatRegSize = uintptr(abi.EffectiveFloatRegSize * goexperiment.RegabiArgsInt) + intArgRegs = abi.IntArgRegs + floatArgRegs = abi.FloatArgRegs + floatRegSize = uintptr(abi.EffectiveFloatRegSize) ) // abiStep represents an ABI "instruction." Each instruction @@ -167,7 +167,7 @@ func (a *abiSeq) addRcvr(rcvr *rtype) (*abiStep, bool) { a.valueStart = append(a.valueStart, len(a.steps)) var ok, ptr bool if ifaceIndir(rcvr) || rcvr.pointers() { - ok = a.assignIntN(0, ptrSize, 1, 0b1) + ok = a.assignIntN(0, goarch.PtrSize, 1, 0b1) ptr = true } else { // TODO(mknyszek): Is this case even possible? @@ -176,11 +176,11 @@ func (a *abiSeq) addRcvr(rcvr *rtype) (*abiStep, bool) { // in the reflect package which only conditionally added // a pointer bit to the reflect.(Value).Call stack frame's // GC bitmap. - ok = a.assignIntN(0, ptrSize, 1, 0b0) + ok = a.assignIntN(0, goarch.PtrSize, 1, 0b0) ptr = false } if !ok { - a.stackAssign(ptrSize, ptrSize) + a.stackAssign(goarch.PtrSize, goarch.PtrSize) return &a.steps[len(a.steps)-1], ptr } return nil, ptr @@ -197,12 +197,12 @@ func (a *abiSeq) addRcvr(rcvr *rtype) (*abiStep, bool) { // complete register-assignment algorithm for the Go ABI. func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool { switch t.Kind() { - case UnsafePointer, Ptr, Chan, Map, Func: + case UnsafePointer, Pointer, Chan, Map, Func: return a.assignIntN(offset, t.size, 1, 0b1) case Bool, Int, Uint, Int8, Uint8, Int16, Uint16, Int32, Uint32, Uintptr: return a.assignIntN(offset, t.size, 1, 0b0) case Int64, Uint64: - switch ptrSize { + switch goarch.PtrSize { case 4: return a.assignIntN(offset, 4, 2, 0b0) case 8: @@ -215,11 +215,11 @@ func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool { case Complex128: return a.assignFloatN(offset, 8, 2) case String: - return a.assignIntN(offset, ptrSize, 2, 0b01) + return a.assignIntN(offset, goarch.PtrSize, 2, 0b01) case Interface: - return a.assignIntN(offset, ptrSize, 2, 0b10) + return a.assignIntN(offset, goarch.PtrSize, 2, 0b10) case Slice: - return a.assignIntN(offset, ptrSize, 3, 0b001) + return a.assignIntN(offset, goarch.PtrSize, 3, 0b001) case Array: tt := (*arrayType)(unsafe.Pointer(t)) switch tt.len { @@ -237,7 +237,7 @@ func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool { st := (*structType)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !a.regAssign(f.typ, offset+f.offset()) { + if !a.regAssign(f.typ, offset+f.offset) { return false } } @@ -262,7 +262,7 @@ func (a *abiSeq) assignIntN(offset, size uintptr, n int, ptrMap uint8) bool { if n > 8 || n < 0 { panic("invalid n") } - if ptrMap != 0 && size != ptrSize { + if ptrMap != 0 && size != goarch.PtrSize { panic("non-empty pointer map passed for non-pointer-size values") } if a.iregs+n > intArgRegs { @@ -413,7 +413,7 @@ func newAbiDesc(t *funcType, rcvr *rtype) abiDesc { stackPtrs.append(0) } } else { - spill += ptrSize + spill += goarch.PtrSize } } for i, arg := range t.in() { @@ -430,12 +430,12 @@ func newAbiDesc(t *funcType, rcvr *rtype) abiDesc { } } } - spill = align(spill, ptrSize) + spill = align(spill, goarch.PtrSize) // From the input parameters alone, we now know // the stackCallArgsSize and retOffset. stackCallArgsSize := in.stackBytes - retOffset := align(in.stackBytes, ptrSize) + retOffset := align(in.stackBytes, goarch.PtrSize) // Compute the stack frame pointer bitmap and register // pointer bitmap for return values. @@ -466,3 +466,45 @@ func newAbiDesc(t *funcType, rcvr *rtype) abiDesc { out.stackBytes -= retOffset return abiDesc{in, out, stackCallArgsSize, retOffset, spill, stackPtrs, inRegPtrs, outRegPtrs} } + +// intFromReg loads an argSize sized integer from reg and places it at to. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +func intFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) { + memmove(to, r.IntRegArgAddr(reg, argSize), argSize) +} + +// intToReg loads an argSize sized integer and stores it into reg. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +func intToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) { + memmove(r.IntRegArgAddr(reg, argSize), from, argSize) +} + +// floatFromReg loads a float value from its register representation in r. +// +// argSize must be 4 or 8. +func floatFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) { + switch argSize { + case 4: + *(*float32)(to) = archFloat32FromReg(r.Floats[reg]) + case 8: + *(*float64)(to) = *(*float64)(unsafe.Pointer(&r.Floats[reg])) + default: + panic("bad argSize") + } +} + +// floatToReg stores a float value in its register representation in r. +// +// argSize must be either 4 or 8. +func floatToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) { + switch argSize { + case 4: + r.Floats[reg] = archFloat32ToReg(*(*float32)(from)) + case 8: + r.Floats[reg] = *(*uint64)(from) + default: + panic("bad argSize") + } +} diff --git a/src/reflect/abi_test.go b/src/reflect/abi_test.go index 5a0130f7b452b9..9d93472779012b 100644 --- a/src/reflect/abi_test.go +++ b/src/reflect/abi_test.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build goexperiment.regabireflect -// +build goexperiment.regabireflect +//go:build goexperiment.regabiargs package reflect_test import ( "internal/abi" + "math" "math/rand" "reflect" "runtime" @@ -33,7 +33,7 @@ func TestMethodValueCallABI(t *testing.T) { // for us, so there isn't a whole lot to do. Let's just // make sure that we can pass register and stack arguments // through. The exact combination is not super important. - makeMethodValue := func(method string) (*StructWithMethods, interface{}) { + makeMethodValue := func(method string) (*StructWithMethods, any) { s := new(StructWithMethods) v := reflect.ValueOf(s).MethodByName(method) return s, v.Interface() @@ -256,7 +256,7 @@ func TestReflectMakeFuncCallABI(t *testing.T) { }) } -var abiCallTestCases = []interface{}{ +var abiCallTestCases = []any{ passNone, passInt, passInt8, @@ -545,13 +545,14 @@ func passEmptyStruct(a int, b struct{}, c float64) (int, struct{}, float64) { // This test case forces a large argument to the stack followed by more // in-register arguments. +// //go:registerparams //go:noinline func passStruct10AndSmall(a Struct10, b byte, c uint) (Struct10, byte, uint) { return a, b, c } -var abiMakeFuncTestCases = []interface{}{ +var abiMakeFuncTestCases = []any{ callArgsNone, callArgsInt, callArgsInt8, @@ -962,3 +963,27 @@ func genValue(t *testing.T, typ reflect.Type, r *rand.Rand) reflect.Value { } return v } + +func TestSignalingNaNArgument(t *testing.T) { + v := reflect.ValueOf(func(x float32) { + // make sure x is a signaling NaN. + u := math.Float32bits(x) + if u != snan { + t.Fatalf("signaling NaN not correct: %x\n", u) + } + }) + v.Call([]reflect.Value{reflect.ValueOf(math.Float32frombits(snan))}) +} + +func TestSignalingNaNReturn(t *testing.T) { + v := reflect.ValueOf(func() float32 { + return math.Float32frombits(snan) + }) + var x float32 + reflect.ValueOf(&x).Elem().Set(v.Call(nil)[0]) + // make sure x is a signaling NaN. + u := math.Float32bits(x) + if u != snan { + t.Fatalf("signaling NaN not correct: %x\n", u) + } +} diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index eac27e886f21d6..e07180cd2f8848 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -10,6 +10,8 @@ import ( "flag" "fmt" "go/token" + "internal/goarch" + "internal/testenv" "io" "math" "math/rand" @@ -28,7 +30,7 @@ import ( "unsafe" ) -var sink interface{} +var sink any func TestBool(t *testing.T) { v := ValueOf(true) @@ -45,8 +47,10 @@ type T struct { d *int } +var _ = T{} == T{} // tests depend on T being comparable + type pair struct { - i interface{} + i any s string } @@ -335,6 +339,122 @@ func TestSetValue(t *testing.T) { } } +func TestMapIterSet(t *testing.T) { + m := make(map[string]any, len(valueTests)) + for _, tt := range valueTests { + m[tt.s] = tt.i + } + v := ValueOf(m) + + k := New(v.Type().Key()).Elem() + e := New(v.Type().Elem()).Elem() + + iter := v.MapRange() + for iter.Next() { + k.SetIterKey(iter) + e.SetIterValue(iter) + want := m[k.String()] + got := e.Interface() + if got != want { + t.Errorf("%q: want (%T) %v, got (%T) %v", k.String(), want, want, got, got) + } + if setkey, key := valueToString(k), valueToString(iter.Key()); setkey != key { + t.Errorf("MapIter.Key() = %q, MapIter.SetKey() = %q", key, setkey) + } + if setval, val := valueToString(e), valueToString(iter.Value()); setval != val { + t.Errorf("MapIter.Value() = %q, MapIter.SetValue() = %q", val, setval) + } + } + + if testenv.OptimizationOff() { + return // no inlining with the noopt builder + } + + got := int(testing.AllocsPerRun(10, func() { + iter := v.MapRange() + for iter.Next() { + k.SetIterKey(iter) + e.SetIterValue(iter) + } + })) + // Calling MapRange should not allocate even though it returns a *MapIter. + // The function is inlineable, so if the local usage does not escape + // the *MapIter, it can remain stack allocated. + want := 0 + if got != want { + t.Errorf("wanted %d alloc, got %d", want, got) + } +} + +func TestCanIntUintFloatComplex(t *testing.T) { + type integer int + type uinteger uint + type float float64 + type complex complex128 + + var ops = [...]string{"CanInt", "CanUint", "CanFloat", "CanComplex"} + + var testCases = []struct { + i any + want [4]bool + }{ + // signed integer + {132, [...]bool{true, false, false, false}}, + {int8(8), [...]bool{true, false, false, false}}, + {int16(16), [...]bool{true, false, false, false}}, + {int32(32), [...]bool{true, false, false, false}}, + {int64(64), [...]bool{true, false, false, false}}, + // unsigned integer + {uint(132), [...]bool{false, true, false, false}}, + {uint8(8), [...]bool{false, true, false, false}}, + {uint16(16), [...]bool{false, true, false, false}}, + {uint32(32), [...]bool{false, true, false, false}}, + {uint64(64), [...]bool{false, true, false, false}}, + {uintptr(0xABCD), [...]bool{false, true, false, false}}, + // floating-point + {float32(256.25), [...]bool{false, false, true, false}}, + {float64(512.125), [...]bool{false, false, true, false}}, + // complex + {complex64(532.125 + 10i), [...]bool{false, false, false, true}}, + {complex128(564.25 + 1i), [...]bool{false, false, false, true}}, + // underlying + {integer(-132), [...]bool{true, false, false, false}}, + {uinteger(132), [...]bool{false, true, false, false}}, + {float(256.25), [...]bool{false, false, true, false}}, + {complex(532.125 + 10i), [...]bool{false, false, false, true}}, + // not-acceptable + {"hello world", [...]bool{false, false, false, false}}, + {new(int), [...]bool{false, false, false, false}}, + {new(uint), [...]bool{false, false, false, false}}, + {new(float64), [...]bool{false, false, false, false}}, + {new(complex64), [...]bool{false, false, false, false}}, + {new([5]int), [...]bool{false, false, false, false}}, + {new(integer), [...]bool{false, false, false, false}}, + {new(map[int]int), [...]bool{false, false, false, false}}, + {new(chan<- int), [...]bool{false, false, false, false}}, + {new(func(a int8)), [...]bool{false, false, false, false}}, + {new(struct{ i int }), [...]bool{false, false, false, false}}, + } + + for i, tc := range testCases { + v := ValueOf(tc.i) + got := [...]bool{v.CanInt(), v.CanUint(), v.CanFloat(), v.CanComplex()} + + for j := range tc.want { + if got[j] != tc.want[j] { + t.Errorf( + "#%d: v.%s() returned %t for type %T, want %t", + i, + ops[j], + got[j], + tc.i, + tc.want[j], + ) + } + } + } +} + func TestCanSetField(t *testing.T) { type embed struct{ x, X int } type Embed struct{ x, X int } @@ -436,7 +556,7 @@ func TestCanSetField(t *testing.T) { for _, tc := range tt.cases { f := tt.val for _, i := range tc.index { - if f.Kind() == Ptr { + if f.Kind() == Pointer { f = f.Elem() } if i == -1 { @@ -581,7 +701,7 @@ func TestAll(t *testing.T) { func TestInterfaceGet(t *testing.T) { var inter struct { - E interface{} + E any } inter.E = 123.456 v1 := ValueOf(&inter) @@ -594,7 +714,7 @@ func TestInterfaceGet(t *testing.T) { func TestInterfaceValue(t *testing.T) { var inter struct { - E interface{} + E any } inter.E = 123.456 v1 := ValueOf(&inter) @@ -610,7 +730,7 @@ func TestInterfaceValue(t *testing.T) { } func TestFunctionValue(t *testing.T) { - var x interface{} = func() {} + var x any = func() {} v := ValueOf(x) if fmt.Sprint(v.Interface()) != fmt.Sprint(x) { t.Fatalf("TestFunction returned wrong pointer") @@ -810,7 +930,7 @@ type Basic struct { type NotBasic Basic type DeepEqualTest struct { - a, b interface{} + a, b any eq bool } @@ -824,11 +944,11 @@ var ( type self struct{} type Loop *Loop -type Loopy interface{} +type Loopy any var loop1, loop2 Loop var loopy1, loopy2 Loopy -var cycleMap1, cycleMap2, cycleMap3 map[string]interface{} +var cycleMap1, cycleMap2, cycleMap3 map[string]any type structWithSelfPtr struct { p *structWithSelfPtr @@ -842,11 +962,11 @@ func init() { loopy1 = &loopy2 loopy2 = &loopy1 - cycleMap1 = map[string]interface{}{} + cycleMap1 = map[string]any{} cycleMap1["cycle"] = cycleMap1 - cycleMap2 = map[string]interface{}{} + cycleMap2 = map[string]any{} cycleMap2["cycle"] = cycleMap2 - cycleMap3 = map[string]interface{}{} + cycleMap3 = map[string]any{} cycleMap3["different"] = cycleMap3 } @@ -864,6 +984,9 @@ var deepEqualTests = []DeepEqualTest{ {error(nil), error(nil), true}, {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, {fn1, fn2, true}, + {[]byte{1, 2, 3}, []byte{1, 2, 3}, true}, + {[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true}, + {MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true}, // Inequalities {1, 2, false}, @@ -884,6 +1007,9 @@ var deepEqualTests = []DeepEqualTest{ {fn1, fn3, false}, {fn3, fn3, false}, {[][]int{{1}}, [][]int{{2}}, false}, + {&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false}, + + // Fun with floating point. {math.NaN(), math.NaN(), false}, {&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false}, {&[1]float64{math.NaN()}, self{}, true}, @@ -891,7 +1017,6 @@ var deepEqualTests = []DeepEqualTest{ {[]float64{math.NaN()}, self{}, true}, {map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, {map[float64]float64{math.NaN(): 1}, self{}, true}, - {&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false}, // Nil vs empty: not the same. {[]int{}, []int(nil), false}, @@ -906,9 +1031,12 @@ var deepEqualTests = []DeepEqualTest{ {int32(1), int64(1), false}, {0.5, "hello", false}, {[]int{1, 2, 3}, [3]int{1, 2, 3}, false}, - {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false}, + {&[3]any{1, 2, 4}, &[3]any{1, 2, "s"}, false}, {Basic{1, 0.5}, NotBasic{1, 0.5}, false}, {map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false}, + {[]byte{1, 2, 3}, []MyByte{1, 2, 3}, false}, + {[]MyByte{1, 2, 3}, MyBytes{1, 2, 3}, false}, + {[]byte{1, 2, 3}, MyBytes{1, 2, 3}, false}, // Possible loops. {&loop1, &loop1, true}, @@ -1008,7 +1136,83 @@ func TestDeepEqualUnexportedMap(t *testing.T) { } } -func check2ndField(x interface{}, offs uintptr, t *testing.T) { +var deepEqualPerfTests = []struct { + x, y any +}{ + {x: int8(99), y: int8(99)}, + {x: []int8{99}, y: []int8{99}}, + {x: int16(99), y: int16(99)}, + {x: []int16{99}, y: []int16{99}}, + {x: int32(99), y: int32(99)}, + {x: []int32{99}, y: []int32{99}}, + {x: int64(99), y: int64(99)}, + {x: []int64{99}, y: []int64{99}}, + {x: int(999999), y: int(999999)}, + {x: []int{999999}, y: []int{999999}}, + + {x: uint8(99), y: uint8(99)}, + {x: []uint8{99}, y: []uint8{99}}, + {x: uint16(99), y: uint16(99)}, + {x: []uint16{99}, y: []uint16{99}}, + {x: uint32(99), y: uint32(99)}, + {x: []uint32{99}, y: []uint32{99}}, + {x: uint64(99), y: uint64(99)}, + {x: []uint64{99}, y: []uint64{99}}, + {x: uint(999999), y: uint(999999)}, + {x: []uint{999999}, y: []uint{999999}}, + {x: uintptr(999999), y: uintptr(999999)}, + {x: []uintptr{999999}, y: []uintptr{999999}}, + + {x: float32(1.414), y: float32(1.414)}, + {x: []float32{1.414}, y: []float32{1.414}}, + {x: float64(1.414), y: float64(1.414)}, + {x: []float64{1.414}, y: []float64{1.414}}, + + {x: complex64(1.414), y: complex64(1.414)}, + {x: []complex64{1.414}, y: []complex64{1.414}}, + {x: complex128(1.414), y: complex128(1.414)}, + {x: []complex128{1.414}, y: []complex128{1.414}}, + + {x: true, y: true}, + {x: []bool{true}, y: []bool{true}}, + + {x: "abcdef", y: "abcdef"}, + {x: []string{"abcdef"}, y: []string{"abcdef"}}, + + {x: []byte("abcdef"), y: []byte("abcdef")}, + {x: [][]byte{[]byte("abcdef")}, y: [][]byte{[]byte("abcdef")}}, + + {x: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}, y: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, + {x: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, y: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}}, +} + +func TestDeepEqualAllocs(t *testing.T) { + for _, tt := range deepEqualPerfTests { + t.Run(ValueOf(tt.x).Type().String(), func(t *testing.T) { + got := testing.AllocsPerRun(100, func() { + if !DeepEqual(tt.x, tt.y) { + t.Errorf("DeepEqual(%v, %v)=false", tt.x, tt.y) + } + }) + if int(got) != 0 { + t.Errorf("DeepEqual(%v, %v) allocated %d times", tt.x, tt.y, int(got)) + } + }) + } +} + +func BenchmarkDeepEqual(b *testing.B) { + for _, bb := range deepEqualPerfTests { + b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sink = DeepEqual(bb.x, bb.y) + } + }) + } +} + +func check2ndField(x any, offs uintptr, t *testing.T) { s := ValueOf(x) f := s.Type().Field(1) if f.Offset != offs { @@ -1041,14 +1245,14 @@ func TestAlignment(t *testing.T) { check2ndField(x1, uintptr(unsafe.Pointer(&x1.f))-uintptr(unsafe.Pointer(&x1)), t) } -func Nil(a interface{}, t *testing.T) { +func Nil(a any, t *testing.T) { n := ValueOf(a).Field(0) if !n.IsNil() { t.Errorf("%v should be nil", a) } } -func NotNil(a interface{}, t *testing.T) { +func NotNil(a any, t *testing.T) { n := ValueOf(a).Field(0) if n.IsNil() { t.Errorf("value of type %v should not be nil", ValueOf(a).Type().String()) @@ -1058,9 +1262,9 @@ func NotNil(a interface{}, t *testing.T) { func TestIsNil(t *testing.T) { // These implement IsNil. // Wrap in extra struct to hide interface type. - doNil := []interface{}{ + doNil := []any{ struct{ x *int }{}, - struct{ x interface{} }{}, + struct{ x any }{}, struct{ x map[string]int }{}, struct{ x func() bool }{}, struct{ x chan int }{}, @@ -1103,7 +1307,7 @@ func TestIsNil(t *testing.T) { NotNil(mi, t) var ii struct { - x interface{} + x any } Nil(ii, t) ii.x = 2 @@ -1119,7 +1323,7 @@ func TestIsNil(t *testing.T) { func TestIsZero(t *testing.T) { for i, tt := range []struct { - x interface{} + x any want bool }{ // Booleans @@ -1162,9 +1366,16 @@ func TestIsZero(t *testing.T) { {uintptr(128), false}, // Array {Zero(TypeOf([5]string{})).Interface(), true}, - {[5]string{"", "", "", "", ""}, true}, - {[5]string{}, true}, - {[5]string{"", "", "", "a", ""}, false}, + {[5]string{}, true}, // comparable array + {[5]string{"", "", "", "a", ""}, false}, // comparable array + {[1]*int{}, true}, // direct pointer array + {[1]*int{new(int)}, false}, // direct pointer array + {[3][]int{}, true}, // incomparable array + {[3][]int{{1}}, false}, // incomparable array + {[1 << 12]byte{}, true}, + {[1 << 12]byte{1}, false}, + {[3]Value{}, true}, + {[3]Value{{}, ValueOf(0), {}}, false}, // Chan {(chan string)(nil), true}, {make(chan string), false}, @@ -1179,7 +1390,7 @@ func TestIsZero(t *testing.T) { {(map[string]string)(nil), true}, {map[string]string{}, false}, {make(map[string]string), false}, - // Ptr + // Pointer {(*func())(nil), true}, {(*int)(nil), true}, {new(int), false}, @@ -1191,8 +1402,14 @@ func TestIsZero(t *testing.T) { {"", true}, {"not-zero", false}, // Structs - {T{}, true}, - {T{123, 456.75, "hello", &_i}, false}, + {T{}, true}, // comparable struct + {T{123, 456.75, "hello", &_i}, false}, // comparable struct + {struct{ p *int }{}, true}, // direct pointer struct + {struct{ p *int }{new(int)}, false}, // direct pointer struct + {struct{ s []int }{}, true}, // incomparable struct + {struct{ s []int }{[]int{1}}, false}, // incomparable struct + {struct{ Value }{}, true}, + {struct{ Value }{ValueOf(0)}, false}, // UnsafePointer {(unsafe.Pointer)(nil), true}, {(unsafe.Pointer)(new(int)), false}, @@ -1212,6 +1429,13 @@ func TestIsZero(t *testing.T) { if !Zero(TypeOf(tt.x)).IsZero() { t.Errorf("%d: IsZero(Zero(TypeOf((%s)(%+v)))) is false", i, x.Kind(), tt.x) } + + p := New(x.Type()).Elem() + p.Set(x) + p.SetZero() + if !p.IsZero() { + t.Errorf("%d: IsZero((%s)(%+v)) is true after SetZero", i, p.Kind(), tt.x) + } } func() { @@ -1224,6 +1448,65 @@ func TestIsZero(t *testing.T) { }() } +func BenchmarkIsZero(b *testing.B) { + source := ValueOf(struct { + ArrayComparable [4]T + ArrayIncomparable [4]_Complex + StructComparable T + StructIncomparable _Complex + }{}) + + for i := 0; i < source.NumField(); i++ { + name := source.Type().Field(i).Name + value := source.Field(i) + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + sink = value.IsZero() + } + }) + } +} + +func BenchmarkSetZero(b *testing.B) { + source := ValueOf(new(struct { + Bool bool + Int int64 + Uint uint64 + Float float64 + Complex complex128 + Array [4]Value + Chan chan Value + Func func() Value + Interface interface{ String() string } + Map map[string]Value + Pointer *Value + Slice []Value + String string + Struct Value + })).Elem() + + for i := 0; i < source.NumField(); i++ { + name := source.Type().Field(i).Name + value := source.Field(i) + zero := Zero(value.Type()) + b.Run(name+"/Direct", func(b *testing.B) { + for i := 0; i < b.N; i++ { + value.SetZero() + } + }) + b.Run(name+"/CachedZero", func(b *testing.B) { + for i := 0; i < b.N; i++ { + value.Set(zero) + } + }) + b.Run(name+"/NewZero", func(b *testing.B) { + for i := 0; i < b.N; i++ { + value.Set(Zero(value.Type())) + } + }) + } +} + func TestInterfaceExtraction(t *testing.T) { var s struct { W io.Writer @@ -1231,7 +1514,7 @@ func TestInterfaceExtraction(t *testing.T) { s.W = os.Stdout v := Indirect(ValueOf(&s)).Field(0).Interface() - if v != s.W.(interface{}) { + if v != s.W.(any) { t.Error("Interface() on interface: ", v, s.W) } } @@ -1302,6 +1585,10 @@ func TestMap(t *testing.T) { if m != nil { t.Errorf("mv.Set(nil) failed") } + + type S string + shouldPanic("not assignable", func() { mv.MapIndex(ValueOf(S("key"))) }) + shouldPanic("not assignable", func() { mv.SetMapIndex(ValueOf(S("key")), ValueOf(0)) }) } func TestNilMap(t *testing.T) { @@ -1670,11 +1957,9 @@ func TestSelect(t *testing.T) { recvStr = fmt.Sprintf(", received %v, %v", recv.Interface(), recvOK) } t.Fatalf("%s\nselected #%d incorrectly%s", fmtSelect(info), i, recvStr) - continue } if cas.panic { t.Fatalf("%s\nselected #%d incorrectly (case should panic)", fmtSelect(info), i) - continue } if cases[i].Dir == SelectRecv { @@ -1780,7 +2065,7 @@ func selectWatcher() { // runSelect runs a single select test. // It returns the values returned by Select but also returns // a panic value if the Select panics. -func runSelect(cases []SelectCase, info []caseInfo) (chosen int, recv Value, recvOK bool, panicErr interface{}) { +func runSelect(cases []SelectCase, info []caseInfo) (chosen int, recv Value, recvOK bool, panicErr any) { defer func() { panicErr = recover() @@ -1800,7 +2085,7 @@ func runSelect(cases []SelectCase, info []caseInfo) (chosen int, recv Value, rec // fmtSelect formats the information about a single select test. func fmtSelect(info []caseInfo) string { - var buf bytes.Buffer + var buf strings.Builder fmt.Fprintf(&buf, "\nselect {\n") for i, cas := range info { fmt.Fprintf(&buf, "%d: %s", i, cas.desc) @@ -2317,6 +2602,11 @@ func TestMethodValue(t *testing.T) { p := Point{3, 4} var i int64 + // Check that method value have the same underlying code pointers. + if p1, p2 := ValueOf(Point{1, 1}).Method(1), ValueOf(Point{2, 2}).Method(1); p1.Pointer() != p2.Pointer() { + t.Errorf("methodValueCall mismatched: %v - %v", p1, p2) + } + // Curried method of value. tfunc := TypeOf((func(int) int)(nil)) v := ValueOf(p).Method(1) @@ -2566,7 +2856,7 @@ func TestMethod5(t *testing.T) { var TinterType = TypeOf(new(Tinter)).Elem() - CheckI := func(name string, i interface{}, inc int) { + CheckI := func(name string, i any, inc int) { v := ValueOf(i) CheckV(name, v, inc) CheckV("(i="+name+")", v.Convert(TinterType), inc) @@ -2615,7 +2905,7 @@ func TestInterfaceSet(t *testing.T) { p := &Point{3, 4} var s struct { - I interface{} + I any P interface { Dist(int) int } @@ -2657,7 +2947,7 @@ func TestAnonymousFields(t *testing.T) { } type FTest struct { - s interface{} + s any name string index []int value int @@ -2888,7 +3178,7 @@ func TestImportPath(t *testing.T) { {TypeOf([]byte(nil)), ""}, {TypeOf([]rune(nil)), ""}, {TypeOf(string("")), ""}, - {TypeOf((*interface{})(nil)).Elem(), ""}, + {TypeOf((*any)(nil)).Elem(), ""}, {TypeOf((*byte)(nil)), ""}, {TypeOf((*rune)(nil)), ""}, {TypeOf((*int64)(nil)), ""}, @@ -3024,11 +3314,11 @@ 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() { + if typ.NumMethod() != 1 || typ.Method(0).Func.UnsafePointer() != ValueOf((*outer).M).UnsafePointer() { 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()) + t.Errorf("\t%d: %s %p\n", i, m.Name, m.Func.UnsafePointer()) } } } @@ -3067,11 +3357,11 @@ func (i *InnerInt) M() int { func TestEmbeddedMethods(t *testing.T) { typ := TypeOf((*OuterInt)(nil)) - if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*OuterInt).M).Pointer() { + if typ.NumMethod() != 1 || typ.Method(0).Func.UnsafePointer() != ValueOf((*OuterInt).M).UnsafePointer() { t.Errorf("Wrong method table for OuterInt: (m=%p)", (*OuterInt).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()) + t.Errorf("\t%d: %s %p\n", i, m.Name, m.Func.UnsafePointer()) } } @@ -3091,7 +3381,7 @@ func TestEmbeddedMethods(t *testing.T) { } } -type FuncDDD func(...interface{}) error +type FuncDDD func(...any) error func (f FuncDDD) M() {} @@ -3114,22 +3404,22 @@ func TestPtrTo(t *testing.T) { typ := TypeOf(z) for i = 0; i < 100; i++ { - typ = PtrTo(typ) + typ = PointerTo(typ) } for i = 0; i < 100; i++ { typ = typ.Elem() } if typ != TypeOf(z) { - t.Errorf("after 100 PtrTo and Elem, have %s, want %s", typ, TypeOf(z)) + t.Errorf("after 100 PointerTo and Elem, have %s, want %s", typ, TypeOf(z)) } } func TestPtrToGC(t *testing.T) { type T *uintptr tt := TypeOf(T(nil)) - pt := PtrTo(tt) + pt := PointerTo(tt) const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := New(pt) p := new(*uintptr) @@ -3161,11 +3451,11 @@ func BenchmarkPtrTo(b *testing.B) { } b.ResetTimer() - // Now benchmark calling PtrTo on it: we'll have to hit the ptrMap cache on + // Now benchmark calling PointerTo on it: we'll have to hit the ptrMap cache on // every call. b.RunParallel(func(pb *testing.PB) { for pb.Next() { - PtrTo(t) + PointerTo(t) } }) } @@ -3251,7 +3541,7 @@ func noAlloc(t *testing.T, n int, f func(int)) { func TestAllocations(t *testing.T) { noAlloc(t, 100, func(j int) { - var i interface{} + var i any var v Value // We can uncomment this when compiler escape analysis @@ -3329,11 +3619,11 @@ func TestSlice(t *testing.T) { rv := ValueOf(&xs).Elem() rv = rv.Slice(3, 4) - ptr2 := rv.Pointer() + ptr2 := rv.UnsafePointer() rv = rv.Slice(5, 5) - ptr3 := rv.Pointer() + ptr3 := rv.UnsafePointer() if ptr3 != ptr2 { - t.Errorf("xs.Slice(3,4).Slice3(5,5).Pointer() = %#x, want %#x", ptr3, ptr2) + t.Errorf("xs.Slice(3,4).Slice3(5,5).UnsafePointer() = %p, want %p", ptr3, ptr2) } } @@ -3376,11 +3666,11 @@ func TestSlice3(t *testing.T) { rv = ValueOf(&xs).Elem() rv = rv.Slice3(3, 5, 7) - ptr2 := rv.Pointer() + ptr2 := rv.UnsafePointer() rv = rv.Slice3(4, 4, 4) - ptr3 := rv.Pointer() + ptr3 := rv.UnsafePointer() if ptr3 != ptr2 { - t.Errorf("xs.Slice3(3,5,7).Slice3(4,4,4).Pointer() = %#x, want %#x", ptr3, ptr2) + t.Errorf("xs.Slice3(3,5,7).Slice3(4,4,4).UnsafePointer() = %p, want %p", ptr3, ptr2) } } @@ -3415,7 +3705,7 @@ func TestSetLenCap(t *testing.T) { } func TestVariadic(t *testing.T) { - var b bytes.Buffer + var b strings.Builder V := ValueOf b.Reset() @@ -3425,7 +3715,7 @@ func TestVariadic(t *testing.T) { } b.Reset() - V(fmt.Fprintf).CallSlice([]Value{V(&b), V("%s, %d world"), V([]interface{}{"hello", 42})}) + V(fmt.Fprintf).CallSlice([]Value{V(&b), V("%s, %d world"), V([]any{"hello", 42})}) if b.String() != "hello, 42 world" { t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world") } @@ -3483,8 +3773,11 @@ func TestTagGet(t *testing.T) { } func TestBytes(t *testing.T) { - type B []byte - x := B{1, 2, 3, 4} + shouldPanic("on int Value", func() { ValueOf(0).Bytes() }) + shouldPanic("of non-byte slice", func() { ValueOf([]string{}).Bytes() }) + + type S []byte + x := S{1, 2, 3, 4} y := ValueOf(x).Bytes() if !bytes.Equal(x, y) { t.Fatalf("ValueOf(%v).Bytes() = %v", x, y) @@ -3492,6 +3785,28 @@ func TestBytes(t *testing.T) { if &x[0] != &y[0] { t.Errorf("ValueOf(%p).Bytes() = %p", &x[0], &y[0]) } + + type A [4]byte + a := A{1, 2, 3, 4} + shouldPanic("unaddressable", func() { ValueOf(a).Bytes() }) + shouldPanic("on ptr Value", func() { ValueOf(&a).Bytes() }) + b := ValueOf(&a).Elem().Bytes() + if !bytes.Equal(a[:], y) { + t.Fatalf("ValueOf(%v).Bytes() = %v", a, b) + } + if &a[0] != &b[0] { + t.Errorf("ValueOf(%p).Bytes() = %p", &a[0], &b[0]) + } + + // Per issue #24746, it was decided that Bytes can be called on byte slices + // that normally cannot be converted from per Go language semantics. + type B byte + type SB []B + type AB [4]B + ValueOf([]B{1, 2, 3, 4}).Bytes() // should not panic + ValueOf(new([4]B)).Elem().Bytes() // should not panic + ValueOf(SB{1, 2, 3, 4}).Bytes() // should not panic + ValueOf(new(AB)).Elem().Bytes() // should not panic } func TestSetBytes(t *testing.T) { @@ -3741,6 +4056,51 @@ func TestCallPanic(t *testing.T) { badCall(func() { call(v.Field(7).Field(1).Elem().Method(0)) }) // .namedT2.t0.W } +func TestValuePanic(t *testing.T) { + vo := ValueOf + shouldPanic("reflect.Value.Addr of unaddressable value", func() { vo(0).Addr() }) + shouldPanic("call of reflect.Value.Bool on float64 Value", func() { vo(0.0).Bool() }) + shouldPanic("call of reflect.Value.Bytes on string Value", func() { vo("").Bytes() }) + shouldPanic("call of reflect.Value.Call on bool Value", func() { vo(true).Call(nil) }) + shouldPanic("call of reflect.Value.CallSlice on int Value", func() { vo(0).CallSlice(nil) }) + shouldPanic("call of reflect.Value.Close on string Value", func() { vo("").Close() }) + shouldPanic("call of reflect.Value.Complex on float64 Value", func() { vo(0.0).Complex() }) + shouldPanic("call of reflect.Value.Elem on bool Value", func() { vo(false).Elem() }) + shouldPanic("call of reflect.Value.Field on int Value", func() { vo(0).Field(0) }) + shouldPanic("call of reflect.Value.Float on string Value", func() { vo("").Float() }) + shouldPanic("call of reflect.Value.Index on float64 Value", func() { vo(0.0).Index(0) }) + shouldPanic("call of reflect.Value.Int on bool Value", func() { vo(false).Int() }) + shouldPanic("call of reflect.Value.IsNil on int Value", func() { vo(0).IsNil() }) + shouldPanic("call of reflect.Value.Len on bool Value", func() { vo(false).Len() }) + shouldPanic("call of reflect.Value.MapIndex on float64 Value", func() { vo(0.0).MapIndex(vo(0.0)) }) + shouldPanic("call of reflect.Value.MapKeys on string Value", func() { vo("").MapKeys() }) + shouldPanic("call of reflect.Value.MapRange on int Value", func() { vo(0).MapRange() }) + shouldPanic("call of reflect.Value.Method on zero Value", func() { vo(nil).Method(0) }) + shouldPanic("call of reflect.Value.NumField on string Value", func() { vo("").NumField() }) + shouldPanic("call of reflect.Value.NumMethod on zero Value", func() { vo(nil).NumMethod() }) + shouldPanic("call of reflect.Value.OverflowComplex on float64 Value", func() { vo(float64(0)).OverflowComplex(0) }) + shouldPanic("call of reflect.Value.OverflowFloat on int64 Value", func() { vo(int64(0)).OverflowFloat(0) }) + shouldPanic("call of reflect.Value.OverflowInt on uint64 Value", func() { vo(uint64(0)).OverflowInt(0) }) + shouldPanic("call of reflect.Value.OverflowUint on complex64 Value", func() { vo(complex64(0)).OverflowUint(0) }) + shouldPanic("call of reflect.Value.Recv on string Value", func() { vo("").Recv() }) + shouldPanic("call of reflect.Value.Send on bool Value", func() { vo(true).Send(vo(true)) }) + shouldPanic("value of type string is not assignable to type bool", func() { vo(new(bool)).Elem().Set(vo("")) }) + shouldPanic("call of reflect.Value.SetBool on string Value", func() { vo(new(string)).Elem().SetBool(false) }) + shouldPanic("reflect.Value.SetBytes using unaddressable value", func() { vo("").SetBytes(nil) }) + shouldPanic("call of reflect.Value.SetCap on string Value", func() { vo(new(string)).Elem().SetCap(0) }) + shouldPanic("call of reflect.Value.SetComplex on string Value", func() { vo(new(string)).Elem().SetComplex(0) }) + shouldPanic("call of reflect.Value.SetFloat on string Value", func() { vo(new(string)).Elem().SetFloat(0) }) + shouldPanic("call of reflect.Value.SetInt on string Value", func() { vo(new(string)).Elem().SetInt(0) }) + shouldPanic("call of reflect.Value.SetLen on string Value", func() { vo(new(string)).Elem().SetLen(0) }) + shouldPanic("call of reflect.Value.SetString on int Value", func() { vo(new(int)).Elem().SetString("") }) + shouldPanic("reflect.Value.SetUint using unaddressable value", func() { vo(0.0).SetUint(0) }) + shouldPanic("call of reflect.Value.Slice on bool Value", func() { vo(true).Slice(1, 2) }) + shouldPanic("call of reflect.Value.Slice3 on int Value", func() { vo(0).Slice3(1, 2, 3) }) + shouldPanic("call of reflect.Value.TryRecv on bool Value", func() { vo(true).TryRecv() }) + shouldPanic("call of reflect.Value.TrySend on string Value", func() { vo("").TrySend(vo("")) }) + shouldPanic("call of reflect.Value.Uint on float64 Value", func() { vo(0.0).Uint() }) +} + func shouldPanic(expect string, f func()) { defer func() { r := recover() @@ -3768,7 +4128,7 @@ func shouldPanic(expect string, f func()) { f() } -func isNonNil(x interface{}) { +func isNonNil(x any) { if x == nil { panic("nil interface") } @@ -3794,7 +4154,7 @@ func TestAlias(t *testing.T) { var V = ValueOf -func EmptyInterfaceV(x interface{}) Value { +func EmptyInterfaceV(x any) Value { return ValueOf(&x).Elem() } @@ -4235,7 +4595,7 @@ var convertTests = []struct { {V((map[uint]bool)(nil)), V((map[uint]bool)(nil))}, {V([]uint(nil)), V([]uint(nil))}, {V([]int(nil)), V([]int(nil))}, - {V(new(interface{})), V(new(interface{}))}, + {V(new(any)), V(new(any))}, {V(new(io.Reader)), V(new(io.Reader))}, {V(new(io.Writer)), V(new(io.Writer))}, @@ -4387,8 +4747,17 @@ func TestConvertPanic(t *testing.T) { var gFloat32 float32 +const snan uint32 = 0x7f800001 + func TestConvertNaNs(t *testing.T) { - const snan uint32 = 0x7f800001 + // Test to see if a store followed by a load of a signaling NaN + // maintains the signaling bit. (This used to fail on the 387 port.) + gFloat32 = math.Float32frombits(snan) + runtime.Gosched() // make sure we don't optimize the store/load away + if got := math.Float32bits(gFloat32); got != snan { + t.Errorf("store/load of sNaN not faithful, got %x want %x", got, snan) + } + // Test reflect's conversion between float32s. See issue 36400. type myFloat32 float32 x := V(myFloat32(math.Float32frombits(snan))) y := x.Convert(TypeOf(float32(0))) @@ -4425,7 +4794,7 @@ var comparableTests = []struct { {TypeOf(NonComparableStruct{}), false}, {TypeOf([10]map[string]int{}), false}, {TypeOf([10]string{}), true}, - {TypeOf(new(interface{})).Elem(), true}, + {TypeOf(new(any)).Elem(), true}, } func TestComparable(t *testing.T) { @@ -4475,7 +4844,7 @@ func TestOverflow(t *testing.T) { } } -func checkSameType(t *testing.T, x Type, y interface{}) { +func checkSameType(t *testing.T, x Type, y any) { if x != TypeOf(y) || TypeOf(Zero(x).Interface()) != TypeOf(y) { t.Errorf("did not find preexisting type for %s (vs %s)", TypeOf(x), TypeOf(y)) } @@ -4485,73 +4854,73 @@ func TestArrayOf(t *testing.T) { // check construction and use of type not in binary tests := []struct { n int - value func(i int) interface{} + value func(i int) any comparable bool want string }{ { n: 0, - value: func(i int) interface{} { type Tint int; return Tint(i) }, + value: func(i int) any { type Tint int; return Tint(i) }, comparable: true, want: "[]", }, { n: 10, - value: func(i int) interface{} { type Tint int; return Tint(i) }, + value: func(i int) any { type Tint int; return Tint(i) }, comparable: true, want: "[0 1 2 3 4 5 6 7 8 9]", }, { n: 10, - value: func(i int) interface{} { type Tfloat float64; return Tfloat(i) }, + value: func(i int) any { type Tfloat float64; return Tfloat(i) }, comparable: true, want: "[0 1 2 3 4 5 6 7 8 9]", }, { n: 10, - value: func(i int) interface{} { type Tstring string; return Tstring(strconv.Itoa(i)) }, + value: func(i int) any { type Tstring string; return Tstring(strconv.Itoa(i)) }, comparable: true, want: "[0 1 2 3 4 5 6 7 8 9]", }, { n: 10, - value: func(i int) interface{} { type Tstruct struct{ V int }; return Tstruct{i} }, + value: func(i int) any { type Tstruct struct{ V int }; return Tstruct{i} }, comparable: true, want: "[{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}]", }, { n: 10, - value: func(i int) interface{} { type Tint int; return []Tint{Tint(i)} }, + value: func(i int) any { type Tint int; return []Tint{Tint(i)} }, comparable: false, want: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]", }, { n: 10, - value: func(i int) interface{} { type Tint int; return [1]Tint{Tint(i)} }, + value: func(i int) any { type Tint int; return [1]Tint{Tint(i)} }, comparable: true, want: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]", }, { n: 10, - value: func(i int) interface{} { type Tstruct struct{ V [1]int }; return Tstruct{[1]int{i}} }, + value: func(i int) any { type Tstruct struct{ V [1]int }; return Tstruct{[1]int{i}} }, comparable: true, want: "[{[0]} {[1]} {[2]} {[3]} {[4]} {[5]} {[6]} {[7]} {[8]} {[9]}]", }, { n: 10, - value: func(i int) interface{} { type Tstruct struct{ V []int }; return Tstruct{[]int{i}} }, + value: func(i int) any { type Tstruct struct{ V []int }; return Tstruct{[]int{i}} }, comparable: false, want: "[{[0]} {[1]} {[2]} {[3]} {[4]} {[5]} {[6]} {[7]} {[8]} {[9]}]", }, { n: 10, - value: func(i int) interface{} { type TstructUV struct{ U, V int }; return TstructUV{i, i} }, + value: func(i int) any { type TstructUV struct{ U, V int }; return TstructUV{i, i} }, comparable: true, want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]", }, { n: 10, - value: func(i int) interface{} { + value: func(i int) any { type TstructUV struct { U int V float64 @@ -4612,7 +4981,7 @@ func TestArrayOfGC(t *testing.T) { type T *uintptr tt := TypeOf(T(nil)) const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := New(ArrayOf(n, tt)).Elem() for j := 0; j < v.Len(); j++ { @@ -4688,7 +5057,7 @@ func TestArrayOfDirectIface(t *testing.T) { v1 := ValueOf(&i1).Elem() p1 := v1.InterfaceData()[1] - i2 := Zero(ArrayOf(1, PtrTo(TypeOf(int8(0))))).Interface() + i2 := Zero(ArrayOf(1, PointerTo(TypeOf(int8(0))))).Interface() v2 := ValueOf(&i2).Elem() p2 := v2.InterfaceData()[1] @@ -4706,7 +5075,7 @@ func TestArrayOfDirectIface(t *testing.T) { v1 := ValueOf(&i1).Elem() p1 := v1.InterfaceData()[1] - i2 := Zero(ArrayOf(0, PtrTo(TypeOf(int8(0))))).Interface() + i2 := Zero(ArrayOf(0, PointerTo(TypeOf(int8(0))))).Interface() v2 := ValueOf(&i2).Elem() p2 := v2.InterfaceData()[1] @@ -4776,7 +5145,7 @@ func TestSliceOfGC(t *testing.T) { tt := TypeOf(T(nil)) st := SliceOf(tt) const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := MakeSlice(st, n, n) for j := 0; j < v.Len(); j++ { @@ -4966,7 +5335,7 @@ func TestStructOf(t *testing.T) { checkSameType(t, StructOf(fields[2:3]), struct{ Y uint64 }{}) // gccgo used to fail this test. - type structFieldType interface{} + type structFieldType any checkSameType(t, StructOf([]StructField{ { @@ -5142,7 +5511,7 @@ func TestStructOfGC(t *testing.T) { st := StructOf(fields) const n = 10000 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := New(st).Elem() for j := 0; j < v.NumField(); j++ { @@ -5407,8 +5776,8 @@ func TestStructOfWithInterface(t *testing.T) { }, { name: "StructI", - typ: PtrTo(TypeOf(StructI(want))), - val: ValueOf(func() interface{} { + typ: PointerTo(TypeOf(StructI(want))), + val: ValueOf(func() any { v := StructI(want) return &v }()), @@ -5416,8 +5785,8 @@ func TestStructOfWithInterface(t *testing.T) { }, { name: "StructIPtr", - typ: PtrTo(TypeOf(StructIPtr(want))), - val: ValueOf(func() interface{} { + typ: PointerTo(TypeOf(StructIPtr(want))), + val: ValueOf(func() any { v := StructIPtr(want) return &v }()), @@ -5505,7 +5874,7 @@ func TestStructOfWithInterface(t *testing.T) { fields := []StructField{{ Name: "StructIPtr", Anonymous: true, - Type: PtrTo(TypeOf(StructIPtr(want))), + Type: PointerTo(TypeOf(StructIPtr(want))), }} rt := StructOf(fields) rv := New(rt).Elem() @@ -5519,7 +5888,7 @@ func TestStructOfWithInterface(t *testing.T) { fields = []StructField{{ Name: "SettableStruct", Anonymous: true, - Type: PtrTo(TypeOf(SettableStruct{})), + Type: PointerTo(TypeOf(SettableStruct{})), }} rt = StructOf(fields) rv = New(rt).Elem() @@ -5535,7 +5904,7 @@ func TestStructOfWithInterface(t *testing.T) { { Name: "SettableStruct", Anonymous: true, - Type: PtrTo(TypeOf(SettableStruct{})), + Type: PointerTo(TypeOf(SettableStruct{})), }, { Name: "EmptyStruct", @@ -5601,6 +5970,87 @@ func TestStructOfDifferentPkgPath(t *testing.T) { }) } +func TestStructOfTooLarge(t *testing.T) { + t1 := TypeOf(byte(0)) + t2 := TypeOf(int16(0)) + t4 := TypeOf(int32(0)) + t0 := ArrayOf(0, t1) + + // 2^64-3 sized type (or 2^32-3 on 32-bit archs) + bigType := StructOf([]StructField{ + {Name: "F1", Type: ArrayOf(int(^uintptr(0)>>1), t1)}, + {Name: "F2", Type: ArrayOf(int(^uintptr(0)>>1-1), t1)}, + }) + + type test struct { + shouldPanic bool + fields []StructField + } + + tests := [...]test{ + { + shouldPanic: false, // 2^64-1, ok + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(2, t1)}, + }, + }, + { + shouldPanic: true, // overflow in total size + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(3, t1)}, + }, + }, + { + shouldPanic: true, // overflow while aligning F2 + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: t4}, + }, + }, + { + shouldPanic: true, // overflow while adding trailing byte for zero-sized fields + fields: []StructField{ + {Name: "F1", Type: bigType}, + {Name: "F2", Type: ArrayOf(2, t1)}, + {Name: "F3", Type: t0}, + }, + }, + { + shouldPanic: true, // overflow while aligning total size + fields: []StructField{ + {Name: "F1", Type: t2}, + {Name: "F2", Type: bigType}, + }, + }, + } + + for i, tt := range tests { + func() { + defer func() { + err := recover() + if !tt.shouldPanic { + if err != nil { + t.Errorf("test %d should not panic, got %s", i, err) + } + return + } + if err == nil { + t.Errorf("test %d expected to panic", i) + return + } + s := fmt.Sprintf("%s", err) + if s != "reflect.StructOf: struct size would exceed virtual address space" { + t.Errorf("test %d wrong panic message: %s", i, s) + return + } + }() + _ = StructOf(tt.fields) + }() + } +} + func TestChanOf(t *testing.T) { // check construction and use of type not in binary type T string @@ -5679,7 +6129,7 @@ func TestChanOfGC(t *testing.T) { // so we have to save pointers to channels in x; the pointer code will // use the gc info in the newly constructed chan type. const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := MakeChan(ct, n) for j := 0; j < n; j++ { @@ -5737,7 +6187,7 @@ func TestMapOfGCKeys(t *testing.T) { // so we have to save pointers to maps in x; the pointer code will // use the gc info in the newly constructed map type. const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := MakeMap(mt) for j := 0; j < n; j++ { @@ -5775,7 +6225,7 @@ func TestMapOfGCValues(t *testing.T) { // so we have to save pointers to maps in x; the pointer code will // use the gc info in the newly constructed map type. const n = 100 - var x []interface{} + var x []any for i := 0; i < n; i++ { v := MakeMap(mt) for j := 0; j < n; j++ { @@ -5843,7 +6293,7 @@ func TestFuncOf(t *testing.T) { testCases := []struct { in, out []Type variadic bool - want interface{} + want any }{ {in: []Type{TypeOf(T1(0))}, want: (func(T1))(nil)}, {in: []Type{TypeOf(int(0))}, want: (func(int))(nil)}, @@ -5859,6 +6309,13 @@ func TestFuncOf(t *testing.T) { FuncOf([]Type{TypeOf(1), TypeOf(""), SliceOf(TypeOf(false))}, nil, true) shouldPanic("must be slice", func() { FuncOf([]Type{TypeOf(0), TypeOf(""), TypeOf(false)}, nil, true) }) shouldPanic("must be slice", func() { FuncOf(nil, nil, true) }) + + //testcase for #54669 + var in []Type + for i := 0; i < 51; i++ { + in = append(in, TypeOf(1)) + } + FuncOf(in, nil, false) } type B1 struct { @@ -6058,7 +6515,6 @@ func TestAllocsInterfaceSmall(t *testing.T) { // [false false false false] // ... // [true true true true] -// type exhaustive struct { r *rand.Rand pos int @@ -6270,6 +6726,29 @@ func TestCallMethodJump(t *testing.T) { *CallGC = false } +func TestCallArgLive(t *testing.T) { + type T struct{ X, Y *string } // pointerful aggregate + + F := func(t T) { *t.X = "ok" } + + // In reflect.Value.Call, trigger a garbage collection in reflect.call + // between marshaling argument and the actual call. + *CallGC = true + + x := new(string) + runtime.SetFinalizer(x, func(p *string) { + if *p != "ok" { + t.Errorf("x dead prematurely") + } + }) + v := T{x, nil} + + ValueOf(F).Call([]Value{ValueOf(v)}) + + // Stop garbage collecting during reflect.call. + *CallGC = false +} + func TestMakeFuncStackCopy(t *testing.T) { target := func(in []Value) []Value { runtime.GC() @@ -6313,7 +6792,7 @@ func TestValueString(t *testing.T) { func TestInvalid(t *testing.T) { // Used to have inconsistency between IsValid() and Kind() != Invalid. - type T struct{ v interface{} } + type T struct{ v any } v := ValueOf(T{}).Field(0) if v.IsValid() != true || v.Kind() != Interface { @@ -6331,7 +6810,7 @@ func TestLargeGCProg(t *testing.T) { fv.Call([]Value{ValueOf([256]*byte{})}) } -func fieldIndexRecover(t Type, i int) (recovered interface{}) { +func fieldIndexRecover(t Type, i int) (recovered any) { defer func() { recovered = recover() }() @@ -6466,10 +6945,10 @@ func clobber() { func TestFuncLayout(t *testing.T) { align := func(x uintptr) uintptr { - return (x + PtrSize - 1) &^ (PtrSize - 1) + return (x + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) } var r []byte - if PtrSize == 4 { + if goarch.PtrSize == 4 { r = []byte{0, 0, 0, 1} } else { r = []byte{0, 0, 1} @@ -6490,56 +6969,56 @@ func TestFuncLayout(t *testing.T) { tests := []test{ { typ: ValueOf(func(a, b string) string { return "" }).Type(), - size: 6 * PtrSize, - argsize: 4 * PtrSize, - retOffset: 4 * PtrSize, + size: 6 * goarch.PtrSize, + argsize: 4 * goarch.PtrSize, + retOffset: 4 * goarch.PtrSize, stack: []byte{1, 0, 1, 0, 1}, gc: []byte{1, 0, 1, 0, 1}, }, { typ: ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(), - size: align(align(3*4) + PtrSize + 2), - argsize: align(3*4) + PtrSize + 2, - retOffset: align(align(3*4) + PtrSize + 2), + size: align(align(3*4) + goarch.PtrSize + 2), + argsize: align(3*4) + goarch.PtrSize + 2, + retOffset: align(align(3*4) + goarch.PtrSize + 2), stack: r, gc: r, }, { - typ: ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(), - size: 4 * PtrSize, - argsize: 4 * PtrSize, - retOffset: 4 * PtrSize, + typ: ValueOf(func(a map[int]int, b uintptr, c any) {}).Type(), + size: 4 * goarch.PtrSize, + argsize: 4 * goarch.PtrSize, + retOffset: 4 * goarch.PtrSize, stack: []byte{1, 0, 1, 1}, gc: []byte{1, 0, 1, 1}, }, { typ: ValueOf(func(a S) {}).Type(), - size: 4 * PtrSize, - argsize: 4 * PtrSize, - retOffset: 4 * PtrSize, + size: 4 * goarch.PtrSize, + argsize: 4 * goarch.PtrSize, + retOffset: 4 * goarch.PtrSize, stack: []byte{0, 0, 1, 1}, gc: []byte{0, 0, 1, 1}, }, { rcvr: ValueOf((*byte)(nil)).Type(), typ: ValueOf(func(a uintptr, b *int) {}).Type(), - size: 3 * PtrSize, - argsize: 3 * PtrSize, - retOffset: 3 * PtrSize, + size: 3 * goarch.PtrSize, + argsize: 3 * goarch.PtrSize, + retOffset: 3 * goarch.PtrSize, stack: []byte{1, 0, 1}, gc: []byte{1, 0, 1}, }, { typ: ValueOf(func(a uintptr) {}).Type(), - size: PtrSize, - argsize: PtrSize, - retOffset: PtrSize, + size: goarch.PtrSize, + argsize: goarch.PtrSize, + retOffset: goarch.PtrSize, stack: []byte{}, gc: []byte{}, }, { typ: ValueOf(func() uintptr { return 0 }).Type(), - size: PtrSize, + size: goarch.PtrSize, argsize: 0, retOffset: 0, stack: []byte{}, @@ -6548,9 +7027,9 @@ func TestFuncLayout(t *testing.T) { { rcvr: ValueOf(uintptr(0)).Type(), typ: ValueOf(func(a uintptr) {}).Type(), - size: 2 * PtrSize, - argsize: 2 * PtrSize, - retOffset: 2 * PtrSize, + size: 2 * goarch.PtrSize, + argsize: 2 * goarch.PtrSize, + retOffset: 2 * goarch.PtrSize, stack: []byte{1}, gc: []byte{1}, // Note: this one is tricky, as the receiver is not a pointer. But we @@ -6596,8 +7075,21 @@ func TestFuncLayout(t *testing.T) { } } +// trimBitmap removes trailing 0 elements from b and returns the result. +func trimBitmap(b []byte) []byte { + for len(b) > 0 && b[len(b)-1] == 0 { + b = b[:len(b)-1] + } + return b +} + func verifyGCBits(t *testing.T, typ Type, bits []byte) { heapBits := GCBits(New(typ).Interface()) + + // Trim scalars at the end, as bits might end in zero, + // e.g. with rep(2, lit(1, 0)). + bits = trimBitmap(bits) + if !bytes.Equal(heapBits, bits) { _, _, line, _ := runtime.Caller(1) t.Errorf("line %d: heapBits incorrect for %v\nhave %v\nwant %v", line, typ, heapBits, bits) @@ -6610,16 +7102,14 @@ func verifyGCBitsSlice(t *testing.T, typ Type, cap int, bits []byte) { // repeat a bitmap for a small array or executing a repeat in // a GC program. val := MakeSlice(typ, 0, cap) - data := NewAt(ArrayOf(cap, typ), unsafe.Pointer(val.Pointer())) + data := NewAt(ArrayOf(cap, typ), val.UnsafePointer()) heapBits := GCBits(data.Interface()) // Repeat the bitmap for the slice size, trimming scalars in // the last element. - bits = rep(cap, bits) - for len(bits) > 0 && bits[len(bits)-1] == 0 { - bits = bits[:len(bits)-1] - } + bits = trimBitmap(rep(cap, bits)) if !bytes.Equal(heapBits, bits) { - t.Errorf("heapBits incorrect for make(%v, 0, %v)\nhave %v\nwant %v", typ, cap, heapBits, bits) + _, _, line, _ := runtime.Caller(1) + t.Errorf("line %d: heapBits incorrect for make(%v, 0, %v)\nhave %v\nwant %v", line, typ, cap, heapBits, bits) } } @@ -6751,14 +7241,14 @@ func TestGCBits(t *testing.T) { verifyGCBits(t, MapOf(ArrayOf(10000, Tscalarptr), Tscalar), lit(1)) verifyGCBits(t, TypeOf((*[10000]Xscalar)(nil)), lit(1)) - verifyGCBits(t, PtrTo(ArrayOf(10000, Tscalar)), lit(1)) + verifyGCBits(t, PointerTo(ArrayOf(10000, Tscalar)), lit(1)) verifyGCBits(t, TypeOf(([][10000]Xscalar)(nil)), lit(1)) verifyGCBits(t, SliceOf(ArrayOf(10000, Tscalar)), lit(1)) - hdr := make([]byte, 8/PtrSize) + hdr := make([]byte, 8/goarch.PtrSize) - verifyMapBucket := func(t *testing.T, k, e Type, m interface{}, want []byte) { + verifyMapBucket := func(t *testing.T, k, e Type, m any, want []byte) { verifyGCBits(t, MapBucketOf(k, e), want) verifyGCBits(t, CachedBucketOf(TypeOf(m)), want) } @@ -6772,7 +7262,7 @@ func TestGCBits(t *testing.T) { join(hdr, rep(8, lit(0, 1)), rep(8, lit(1)), lit(1))) verifyMapBucket(t, Tint64, Tptr, map[int64]Xptr(nil), - join(hdr, rep(8, rep(8/PtrSize, lit(0))), rep(8, lit(1)), lit(1))) + join(hdr, rep(8, rep(8/goarch.PtrSize, lit(0))), rep(8, lit(1)), lit(1))) verifyMapBucket(t, Tscalar, Tscalar, map[Xscalar]Xscalar(nil), @@ -6782,20 +7272,20 @@ func TestGCBits(t *testing.T) { map[[2]Xscalarptr][3]Xptrscalar(nil), join(hdr, rep(8*2, lit(0, 1)), rep(8*3, lit(1, 0)), lit(1))) verifyMapBucket(t, - ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar), - map[[64 / PtrSize]Xscalarptr][64 / PtrSize]Xptrscalar(nil), - join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8*64/PtrSize, lit(1, 0)), lit(1))) + ArrayOf(64/goarch.PtrSize, Tscalarptr), ArrayOf(64/goarch.PtrSize, Tptrscalar), + map[[64 / goarch.PtrSize]Xscalarptr][64 / goarch.PtrSize]Xptrscalar(nil), + join(hdr, rep(8*64/goarch.PtrSize, lit(0, 1)), rep(8*64/goarch.PtrSize, lit(1, 0)), lit(1))) verifyMapBucket(t, - ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar), - map[[64/PtrSize + 1]Xscalarptr][64 / PtrSize]Xptrscalar(nil), - join(hdr, rep(8, lit(1)), rep(8*64/PtrSize, lit(1, 0)), lit(1))) + ArrayOf(64/goarch.PtrSize+1, Tscalarptr), ArrayOf(64/goarch.PtrSize, Tptrscalar), + map[[64/goarch.PtrSize + 1]Xscalarptr][64 / goarch.PtrSize]Xptrscalar(nil), + join(hdr, rep(8, lit(1)), rep(8*64/goarch.PtrSize, lit(1, 0)), lit(1))) verifyMapBucket(t, - ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar), - map[[64 / PtrSize]Xscalarptr][64/PtrSize + 1]Xptrscalar(nil), - join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8, lit(1)), lit(1))) + ArrayOf(64/goarch.PtrSize, Tscalarptr), ArrayOf(64/goarch.PtrSize+1, Tptrscalar), + map[[64 / goarch.PtrSize]Xscalarptr][64/goarch.PtrSize + 1]Xptrscalar(nil), + join(hdr, rep(8*64/goarch.PtrSize, lit(0, 1)), rep(8, lit(1)), lit(1))) verifyMapBucket(t, - ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar), - map[[64/PtrSize + 1]Xscalarptr][64/PtrSize + 1]Xptrscalar(nil), + ArrayOf(64/goarch.PtrSize+1, Tscalarptr), ArrayOf(64/goarch.PtrSize+1, Tptrscalar), + map[[64/goarch.PtrSize + 1]Xscalarptr][64/goarch.PtrSize + 1]Xptrscalar(nil), join(hdr, rep(8, lit(1)), rep(8, lit(1)), lit(1))) } @@ -6820,7 +7310,7 @@ func TestTypeOfTypeOf(t *testing.T) { check("ChanOf", ChanOf(BothDir, TypeOf(T{}))) check("FuncOf", FuncOf([]Type{TypeOf(T{})}, nil, false)) check("MapOf", MapOf(TypeOf(T{}), TypeOf(T{}))) - check("PtrTo", PtrTo(TypeOf(T{}))) + check("PtrTo", PointerTo(TypeOf(T{}))) check("SliceOf", SliceOf(TypeOf(T{}))) } @@ -6887,7 +7377,7 @@ func TestChanAlloc(t *testing.T) { type TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678 int type nameTest struct { - v interface{} + v any want string } @@ -6899,7 +7389,7 @@ var nameTests = []nameTest{ {(*func() D1)(nil), ""}, {(*<-chan D1)(nil), ""}, {(*chan<- D1)(nil), ""}, - {(*interface{})(nil), ""}, + {(*any)(nil), ""}, {(*interface { F() })(nil), ""}, @@ -6925,7 +7415,7 @@ func TestExported(t *testing.T) { type p3 p type exportTest struct { - v interface{} + v any want bool } exportTests := []exportTest{ @@ -7000,6 +7490,61 @@ func BenchmarkNew(b *testing.B) { }) } +func BenchmarkMap(b *testing.B) { + type V *int + type S string + value := ValueOf((V)(nil)) + stringKeys := []string{} + mapOfStrings := map[string]V{} + uint64Keys := []uint64{} + mapOfUint64s := map[uint64]V{} + userStringKeys := []S{} + mapOfUserStrings := map[S]V{} + for i := 0; i < 100; i++ { + stringKey := fmt.Sprintf("key%d", i) + stringKeys = append(stringKeys, stringKey) + mapOfStrings[stringKey] = nil + + uint64Key := uint64(i) + uint64Keys = append(uint64Keys, uint64Key) + mapOfUint64s[uint64Key] = nil + + userStringKey := S(fmt.Sprintf("key%d", i)) + userStringKeys = append(userStringKeys, userStringKey) + mapOfUserStrings[userStringKey] = nil + } + + tests := []struct { + label string + m, keys, value Value + }{ + {"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value}, + {"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value}, + {"UserStringKeys", ValueOf(mapOfUserStrings), ValueOf(userStringKeys), value}, + } + + for _, tt := range tests { + b.Run(tt.label, func(b *testing.B) { + b.Run("MapIndex", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for j := tt.keys.Len() - 1; j >= 0; j-- { + tt.m.MapIndex(tt.keys.Index(j)) + } + } + }) + b.Run("SetMapIndex", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for j := tt.keys.Len() - 1; j >= 0; j-- { + tt.m.SetMapIndex(tt.keys.Index(j), tt.value) + } + } + }) + }) + } +} + func TestSwapper(t *testing.T) { type I int var a, b, c I @@ -7013,9 +7558,9 @@ func TestSwapper(t *testing.T) { type S string tests := []struct { - in interface{} + in any i, j int - want interface{} + want any }{ { in: []int{1, 20, 300}, @@ -7182,6 +7727,72 @@ func TestMapIterNilMap(t *testing.T) { } } +func TestMapIterReset(t *testing.T) { + iter := new(MapIter) + + // Use of zero iterator should panic. + func() { + defer func() { recover() }() + iter.Next() + t.Error("Next did not panic") + }() + + // Reset to new Map should work. + m := map[string]int{"one": 1, "two": 2, "three": 3} + iter.Reset(ValueOf(m)) + if got, want := iterateToString(iter), `[one: 1, three: 3, two: 2]`; got != want { + t.Errorf("iterator returned %s (after sorting), want %s", got, want) + } + + // Reset to Zero value should work, but iterating over it should panic. + iter.Reset(Value{}) + func() { + defer func() { recover() }() + iter.Next() + t.Error("Next did not panic") + }() + + // Reset to a different Map with different types should work. + m2 := map[int]string{1: "one", 2: "two", 3: "three"} + iter.Reset(ValueOf(m2)) + if got, want := iterateToString(iter), `[1: one, 2: two, 3: three]`; got != want { + t.Errorf("iterator returned %s (after sorting), want %s", got, want) + } + + // Check that Reset, Next, and SetKey/SetValue play nicely together. + m3 := map[uint64]uint64{ + 1 << 0: 1 << 1, + 1 << 1: 1 << 2, + 1 << 2: 1 << 3, + } + kv := New(TypeOf(uint64(0))).Elem() + for i := 0; i < 5; i++ { + var seenk, seenv uint64 + iter.Reset(ValueOf(m3)) + for iter.Next() { + kv.SetIterKey(iter) + seenk ^= kv.Uint() + kv.SetIterValue(iter) + seenv ^= kv.Uint() + } + if seenk != 0b111 { + t.Errorf("iteration yielded keys %b, want %b", seenk, 0b111) + } + if seenv != 0b1110 { + t.Errorf("iteration yielded values %b, want %b", seenv, 0b1110) + } + } + + // Reset should not allocate. + n := int(testing.AllocsPerRun(10, func() { + iter.Reset(ValueOf(m2)) + iter.Reset(Value{}) + })) + if n > 0 { + t.Errorf("MapIter.Reset allocated %d times", n) + } +} + func TestMapIterSafety(t *testing.T) { // Using a zero MapIter causes a panic, but not a crash. func() { @@ -7247,6 +7858,16 @@ func TestMapIterNext(t *testing.T) { } } +func BenchmarkMapIterNext(b *testing.B) { + m := ValueOf(map[string]int{"a": 0, "b": 1, "c": 2, "d": 3}) + it := m.MapRange() + for i := 0; i < b.N; i++ { + for it.Next() { + } + it.Reset(m) + } +} + func TestMapIterDelete0(t *testing.T) { // Delete all elements before first iteration. m := map[string]int{"one": 1, "two": 2, "three": 3} @@ -7295,4 +7916,653 @@ func TestConvertibleTo(t *testing.T) { if t1.ConvertibleTo(t2) { t.Fatalf("(%s).ConvertibleTo(%s) = true, want false", t1, t2) } + + t3 := ValueOf([]example1.MyStruct{}).Type() + t4 := ValueOf([]example2.MyStruct{}).Type() + + if t3.ConvertibleTo(t4) { + t.Fatalf("(%s).ConvertibleTo(%s) = true, want false", t3, t4) + } +} + +func TestSetIter(t *testing.T) { + data := map[string]int{ + "foo": 1, + "bar": 2, + "baz": 3, + } + + m := ValueOf(data) + i := m.MapRange() + k := New(TypeOf("")).Elem() + v := New(TypeOf(0)).Elem() + shouldPanic("Value.SetIterKey called before Next", func() { + k.SetIterKey(i) + }) + shouldPanic("Value.SetIterValue called before Next", func() { + v.SetIterValue(i) + }) + data2 := map[string]int{} + for i.Next() { + k.SetIterKey(i) + v.SetIterValue(i) + data2[k.Interface().(string)] = v.Interface().(int) + } + if !DeepEqual(data, data2) { + t.Errorf("maps not equal, got %v want %v", data2, data) + } + shouldPanic("Value.SetIterKey called on exhausted iterator", func() { + k.SetIterKey(i) + }) + shouldPanic("Value.SetIterValue called on exhausted iterator", func() { + v.SetIterValue(i) + }) + + i.Reset(m) + i.Next() + shouldPanic("Value.SetIterKey using unaddressable value", func() { + ValueOf("").SetIterKey(i) + }) + shouldPanic("Value.SetIterValue using unaddressable value", func() { + ValueOf(0).SetIterValue(i) + }) + shouldPanic("value of type string is not assignable to type int", func() { + New(TypeOf(0)).Elem().SetIterKey(i) + }) + shouldPanic("value of type int is not assignable to type string", func() { + New(TypeOf("")).Elem().SetIterValue(i) + }) + + // Make sure assignment conversion works. + var x any + y := ValueOf(&x).Elem() + y.SetIterKey(i) + if _, ok := data[x.(string)]; !ok { + t.Errorf("got key %s which is not in map", x) + } + y.SetIterValue(i) + if x.(int) < 1 || x.(int) > 3 { + t.Errorf("got value %d which is not in map", x) + } + + // Try some key/value types which are direct interfaces. + a := 88 + b := 99 + pp := map[*int]*int{ + &a: &b, + } + i = ValueOf(pp).MapRange() + i.Next() + y.SetIterKey(i) + if got := *y.Interface().(*int); got != a { + t.Errorf("pointer incorrect: got %d want %d", got, a) + } + y.SetIterValue(i) + if got := *y.Interface().(*int); got != b { + t.Errorf("pointer incorrect: got %d want %d", got, b) + } + + // Make sure we panic assigning from an unexported field. + m = ValueOf(struct{ m map[string]int }{data}).Field(0) + for iter := m.MapRange(); iter.Next(); { + shouldPanic("using value obtained using unexported field", func() { + k.SetIterKey(iter) + }) + shouldPanic("using value obtained using unexported field", func() { + v.SetIterValue(iter) + }) + } +} + +func TestMethodCallValueCodePtr(t *testing.T) { + m := ValueOf(Point{}).Method(1) + want := MethodValueCallCodePtr() + if got := uintptr(m.UnsafePointer()); got != want { + t.Errorf("methodValueCall code pointer mismatched, want: %v, got: %v", want, got) + } + if got := m.Pointer(); got != want { + t.Errorf("methodValueCall code pointer mismatched, want: %v, got: %v", want, got) + } +} + +type A struct{} +type B[T any] struct{} + +func TestIssue50208(t *testing.T) { + want1 := "B[reflect_test.A]" + if got := TypeOf(new(B[A])).Elem().Name(); got != want1 { + t.Errorf("name of type parameter mismatched, want:%s, got:%s", want1, got) + } + want2 := "B[reflect_test.B[reflect_test.A]]" + if got := TypeOf(new(B[B[A]])).Elem().Name(); got != want2 { + t.Errorf("name of type parameter mismatched, want:%s, got:%s", want2, got) + } +} + +func TestNegativeKindString(t *testing.T) { + x := -1 + s := Kind(x).String() + want := "kind-1" + if s != want { + t.Fatalf("Kind(-1).String() = %q, want %q", s, want) + } +} + +type ( + namedBool bool + namedBytes []byte +) + +var sourceAll = struct { + Bool Value + String Value + Bytes Value + NamedBytes Value + BytesArray Value + SliceAny Value + MapStringAny Value +}{ + Bool: ValueOf(new(bool)).Elem(), + String: ValueOf(new(string)).Elem(), + Bytes: ValueOf(new([]byte)).Elem(), + NamedBytes: ValueOf(new(namedBytes)).Elem(), + BytesArray: ValueOf(new([32]byte)).Elem(), + SliceAny: ValueOf(new([]any)).Elem(), + MapStringAny: ValueOf(new(map[string]any)).Elem(), +} + +var sinkAll struct { + RawBool bool + RawString string + RawBytes []byte + RawInt int +} + +func BenchmarkBool(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawBool = sourceAll.Bool.Bool() + } +} + +func BenchmarkString(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawString = sourceAll.String.String() + } +} + +func BenchmarkBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawBytes = sourceAll.Bytes.Bytes() + } +} + +func BenchmarkNamedBytes(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawBytes = sourceAll.NamedBytes.Bytes() + } +} + +func BenchmarkBytesArray(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawBytes = sourceAll.BytesArray.Bytes() + } +} + +func BenchmarkSliceLen(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawInt = sourceAll.SliceAny.Len() + } +} + +func BenchmarkMapLen(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawInt = sourceAll.MapStringAny.Len() + } +} + +func BenchmarkStringLen(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawInt = sourceAll.String.Len() + } +} + +func BenchmarkArrayLen(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawInt = sourceAll.BytesArray.Len() + } +} + +func BenchmarkSliceCap(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkAll.RawInt = sourceAll.SliceAny.Cap() + } +} + +func TestValue_Cap(t *testing.T) { + a := &[3]int{1, 2, 3} + v := ValueOf(a) + if v.Cap() != cap(a) { + t.Errorf("Cap = %d want %d", v.Cap(), cap(a)) + } + + a = nil + v = ValueOf(a) + if v.Cap() != cap(a) { + t.Errorf("Cap = %d want %d", v.Cap(), cap(a)) + } + + getError := func(f func()) (errorStr string) { + defer func() { + e := recover() + if str, ok := e.(string); ok { + errorStr = str + } + }() + f() + return + } + e := getError(func() { + var ptr *int + ValueOf(ptr).Cap() + }) + wantStr := "reflect: call of reflect.Value.Cap on ptr to non-array Value" + if e != wantStr { + t.Errorf("error is %q, want %q", e, wantStr) + } +} + +func TestValue_Len(t *testing.T) { + a := &[3]int{1, 2, 3} + v := ValueOf(a) + if v.Len() != len(a) { + t.Errorf("Len = %d want %d", v.Len(), len(a)) + } + + a = nil + v = ValueOf(a) + if v.Len() != len(a) { + t.Errorf("Len = %d want %d", v.Len(), len(a)) + } + + getError := func(f func()) (errorStr string) { + defer func() { + e := recover() + if str, ok := e.(string); ok { + errorStr = str + } + }() + f() + return + } + e := getError(func() { + var ptr *int + ValueOf(ptr).Len() + }) + wantStr := "reflect: call of reflect.Value.Len on ptr to non-array Value" + if e != wantStr { + t.Errorf("error is %q, want %q", e, wantStr) + } +} + +func TestValue_Comparable(t *testing.T) { + var a int + var s []int + var i interface{} = a + var iSlice interface{} = s + var iArrayFalse interface{} = [2]interface{}{1, map[int]int{}} + var iArrayTrue interface{} = [2]interface{}{1, struct{ I interface{} }{1}} + var testcases = []struct { + value Value + comparable bool + deref bool + }{ + { + ValueOf(32), + true, + false, + }, + { + ValueOf(int8(1)), + true, + false, + }, + { + ValueOf(int16(1)), + true, + false, + }, + { + ValueOf(int32(1)), + true, + false, + }, + { + ValueOf(int64(1)), + true, + false, + }, + { + ValueOf(uint8(1)), + true, + false, + }, + { + ValueOf(uint16(1)), + true, + false, + }, + { + ValueOf(uint32(1)), + true, + false, + }, + { + ValueOf(uint64(1)), + true, + false, + }, + { + ValueOf(float32(1)), + true, + false, + }, + { + ValueOf(float64(1)), + true, + false, + }, + { + ValueOf(complex(float32(1), float32(1))), + true, + false, + }, + { + ValueOf(complex(float64(1), float64(1))), + true, + false, + }, + { + ValueOf("abc"), + true, + false, + }, + { + ValueOf(true), + true, + false, + }, + { + ValueOf(map[int]int{}), + false, + false, + }, + { + ValueOf([]int{}), + false, + false, + }, + { + Value{}, + false, + false, + }, + { + ValueOf(&a), + true, + false, + }, + { + ValueOf(&s), + true, + false, + }, + { + ValueOf(&i), + true, + true, + }, + { + ValueOf(&iSlice), + false, + true, + }, + { + ValueOf([2]int{}), + true, + false, + }, + { + ValueOf([2]map[int]int{}), + false, + false, + }, + { + ValueOf([0]func(){}), + false, + false, + }, + { + ValueOf([2]struct{ I interface{} }{{1}, {1}}), + true, + false, + }, + { + ValueOf([2]struct{ I interface{} }{{[]int{}}, {1}}), + false, + false, + }, + { + ValueOf([2]interface{}{1, struct{ I int }{1}}), + true, + false, + }, + { + ValueOf([2]interface{}{[1]interface{}{map[int]int{}}, struct{ I int }{1}}), + false, + false, + }, + { + ValueOf(&iArrayFalse), + false, + true, + }, + { + ValueOf(&iArrayTrue), + true, + true, + }, + } + + for _, cas := range testcases { + v := cas.value + if cas.deref { + v = v.Elem() + } + got := v.Comparable() + if got != cas.comparable { + t.Errorf("%T.Comparable = %t, want %t", v, got, cas.comparable) + } + } +} + +type ValueEqualTest struct { + v, u any + eq bool + vDeref, uDeref bool +} + +var equalI interface{} = 1 +var equalSlice interface{} = []int{1} +var nilInterface interface{} +var mapInterface interface{} = map[int]int{} + +var valueEqualTests = []ValueEqualTest{ + { + Value{}, Value{}, + true, + false, false, + }, + { + true, true, + true, + false, false, + }, + { + 1, 1, + true, + false, false, + }, + { + int8(1), int8(1), + true, + false, false, + }, + { + int16(1), int16(1), + true, + false, false, + }, + { + int32(1), int32(1), + true, + false, false, + }, + { + int64(1), int64(1), + true, + false, false, + }, + { + uint(1), uint(1), + true, + false, false, + }, + { + uint8(1), uint8(1), + true, + false, false, + }, + { + uint16(1), uint16(1), + true, + false, false, + }, + { + uint32(1), uint32(1), + true, + false, false, + }, + { + uint64(1), uint64(1), + true, + false, false, + }, + { + float32(1), float32(1), + true, + false, false, + }, + { + float64(1), float64(1), + true, + false, false, + }, + { + complex(1, 1), complex(1, 1), + true, + false, false, + }, + { + complex128(1 + 1i), complex128(1 + 1i), + true, + false, false, + }, + { + func() {}, nil, + false, + false, false, + }, + { + &equalI, 1, + true, + true, false, + }, + { + &equalSlice, []int{1}, + false, + true, false, + }, + { + map[int]int{}, map[int]int{}, + false, + false, false, + }, + { + (chan int)(nil), nil, + false, + false, false, + }, + { + (chan int)(nil), (chan int)(nil), + true, + false, false, + }, + { + &equalI, &equalI, + true, + false, false, + }, + { + struct{ i int }{1}, struct{ i int }{1}, + true, + false, false, + }, + { + struct{ i int }{1}, struct{ i int }{2}, + false, + false, false, + }, + { + &nilInterface, &nilInterface, + true, + true, true, + }, + { + 1, ValueOf(struct{ i int }{1}).Field(0), + true, + false, false, + }, + { + &mapInterface, &mapInterface, + false, + true, true, + }, +} + +func TestValue_Equal(t *testing.T) { + for _, test := range valueEqualTests { + var v, u Value + if vv, ok := test.v.(Value); ok { + v = vv + } else { + v = ValueOf(test.v) + } + + if uu, ok := test.u.(Value); ok { + u = uu + } else { + u = ValueOf(test.u) + } + if test.vDeref { + v = v.Elem() + } + + if test.uDeref { + u = u.Elem() + } + + if r := v.Equal(u); r != test.eq { + t.Errorf("%s == %s got %t, want %t", v.Type(), u.Type(), r, test.eq) + } + } } diff --git a/src/reflect/asm_amd64.s b/src/reflect/asm_amd64.s index facf07516d8c24..d21d498063b55e 100644 --- a/src/reflect/asm_amd64.s +++ b/src/reflect/asm_amd64.s @@ -24,15 +24,13 @@ // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -// makeFuncStub must be ABIInternal because it is placed directly -// in function values. // This frame contains two locals. See the comment above LOCAL_RETVALID. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$312 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$312 NO_LOCAL_POINTERS // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. LEAQ LOCAL_REGARGS(SP), R12 - CALL runtime·spillArgs(SB) + CALL runtime·spillArgs(SB) MOVQ DX, 24(SP) // outside of moveMakeFuncArgPtrs's arg area MOVQ DX, 0(SP) MOVQ R12, 8(SP) @@ -48,22 +46,20 @@ TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$312 MOVQ AX, 24(SP) CALL ·callReflect(SB) LEAQ LOCAL_REGARGS(SP), R12 - CALL runtime·unspillArgs(SB) + CALL runtime·unspillArgs(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -// methodValueCall must be ABIInternal because it is placed directly -// in function values. // This frame contains two locals. See the comment above LOCAL_RETVALID. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$312 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$312 NO_LOCAL_POINTERS // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. LEAQ LOCAL_REGARGS(SP), R12 - CALL runtime·spillArgs(SB) + CALL runtime·spillArgs(SB) MOVQ DX, 24(SP) // outside of moveMakeFuncArgPtrs's arg area MOVQ DX, 0(SP) MOVQ R12, 8(SP) @@ -79,5 +75,5 @@ TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$312 MOVQ AX, 24(SP) CALL ·callMethod(SB) LEAQ LOCAL_REGARGS(SP), R12 - CALL runtime·unspillArgs(SB) + CALL runtime·unspillArgs(SB) RET diff --git a/src/reflect/asm_arm64.s b/src/reflect/asm_arm64.s index 5fe88e27e400d2..5e91e62aa14525 100644 --- a/src/reflect/asm_arm64.s +++ b/src/reflect/asm_arm64.s @@ -5,34 +5,75 @@ #include "textflag.h" #include "funcdata.h" +// The frames of each of the two functions below contain two locals, at offsets +// that are known to the runtime. +// +// The first local is a bool called retValid with a whole pointer-word reserved +// for it on the stack. The purpose of this word is so that the runtime knows +// whether the stack-allocated return space contains valid values for stack +// scanning. +// +// The second local is an abi.RegArgs value whose offset is also known to the +// runtime, so that a stack map for it can be constructed, since it contains +// pointers visible to the GC. +#define LOCAL_RETVALID 40 +#define LOCAL_REGARGS 48 + +// The frame size of the functions below is +// 32 (args of callReflect) + 8 (bool + padding) + 392 (abi.RegArgs) = 432. + // makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here, runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this + // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. + ADD $LOCAL_REGARGS, RSP, R20 + CALL runtime·spillArgs(SB) + MOVD R26, 32(RSP) // outside of moveMakeFuncArgPtrs's arg area + MOVD R26, R0 + MOVD R20, R1 + CALL ·moveMakeFuncArgPtrs(SB) + MOVD 32(RSP), R26 MOVD R26, 8(RSP) MOVD $argframe+0(FP), R3 MOVD R3, 16(RSP) - MOVB $0, 40(RSP) - ADD $40, RSP, R3 + MOVB $0, LOCAL_RETVALID(RSP) + ADD $LOCAL_RETVALID, RSP, R3 MOVD R3, 24(RSP) - MOVD $0, 32(RSP) - BL ·callReflect(SB) + ADD $LOCAL_REGARGS, RSP, R3 + MOVD R3, 32(RSP) + CALL ·callReflect(SB) + ADD $LOCAL_REGARGS, RSP, R20 + CALL runtime·unspillArgs(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this + // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. + ADD $LOCAL_REGARGS, RSP, R20 + CALL runtime·spillArgs(SB) + MOVD R26, 32(RSP) // outside of moveMakeFuncArgPtrs's arg area + MOVD R26, R0 + MOVD R20, R1 + CALL ·moveMakeFuncArgPtrs(SB) + MOVD 32(RSP), R26 MOVD R26, 8(RSP) MOVD $argframe+0(FP), R3 MOVD R3, 16(RSP) - MOVB $0, 40(RSP) - ADD $40, RSP, R3 + MOVB $0, LOCAL_RETVALID(RSP) + ADD $LOCAL_RETVALID, RSP, R3 MOVD R3, 24(RSP) - MOVD $0, 32(RSP) - BL ·callMethod(SB) + ADD $LOCAL_REGARGS, RSP, R3 + MOVD R3, 32(RSP) + CALL ·callMethod(SB) + ADD $LOCAL_REGARGS, RSP, R20 + CALL runtime·unspillArgs(SB) RET diff --git a/src/reflect/asm_loong64.s b/src/reflect/asm_loong64.s new file mode 100644 index 00000000000000..341a6d55c1b742 --- /dev/null +++ b/src/reflect/asm_loong64.s @@ -0,0 +1,40 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" +#include "funcdata.h" + +#define REGCTXT R29 + +// makeFuncStub is the code half of the function returned by MakeFunc. +// See the comment on the declaration of makeFuncStub in makefunc.go +// for more details. +// No arg size here, runtime pulls arg map out of the func value. +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 + NO_LOCAL_POINTERS + MOVV REGCTXT, 8(R3) + MOVV $argframe+0(FP), R19 + MOVV R19, 16(R3) + MOVB R0, 40(R3) + ADDV $40, R3, R19 + MOVV R19, 24(R3) + MOVV R0, 32(R3) + JAL ·callReflect(SB) + RET + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +// No arg size here; runtime pulls arg map out of the func value. +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 + NO_LOCAL_POINTERS + MOVV REGCTXT, 8(R3) + MOVV $argframe+0(FP), R19 + MOVV R19, 16(R3) + MOVB R0, 40(R3) + ADDV $40, R3, R19 + MOVV R19, 24(R3) + MOVV R0, 32(R3) + JAL ·callMethod(SB) + RET diff --git a/src/reflect/asm_mips64x.s b/src/reflect/asm_mips64x.s index 8d01c5fb7ed737..f21e34df1bc248 100644 --- a/src/reflect/asm_mips64x.s +++ b/src/reflect/asm_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "textflag.h" #include "funcdata.h" diff --git a/src/reflect/asm_mipsx.s b/src/reflect/asm_mipsx.s index 6ea82331084248..636c8a5c71c3d7 100644 --- a/src/reflect/asm_mipsx.s +++ b/src/reflect/asm_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "textflag.h" #include "funcdata.h" diff --git a/src/reflect/asm_ppc64x.s b/src/reflect/asm_ppc64x.s index d955e4110f8b78..3b529be6853529 100644 --- a/src/reflect/asm_ppc64x.s +++ b/src/reflect/asm_ppc64x.s @@ -3,40 +3,81 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" #include "funcdata.h" #include "asm_ppc64x.h" +// The frames of each of the two functions below contain two locals, at offsets +// that are known to the runtime. +// +// The first local is a bool called retValid with a whole pointer-word reserved +// for it on the stack. The purpose of this word is so that the runtime knows +// whether the stack-allocated return space contains valid values for stack +// scanning. +// +// The second local is an abi.RegArgs value whose offset is also known to the +// runtime, so that a stack map for it can be constructed, since it contains +// pointers visible to the GC. + +#define LOCAL_RETVALID 32+FIXED_FRAME +#define LOCAL_REGARGS 40+FIXED_FRAME + +// The frame size of the functions below is +// 32 (args of callReflect) + 8 (bool + padding) + 296 (abi.RegArgs) = 336. + // makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here, runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$336 NO_LOCAL_POINTERS - MOVD R11, FIXED_FRAME+0(R1) - MOVD $argframe+0(FP), R3 - MOVD R3, FIXED_FRAME+8(R1) - MOVB R0, FIXED_FRAME+32(R1) - ADD $FIXED_FRAME+32, R1, R3 - MOVD R3, FIXED_FRAME+16(R1) - MOVD R0, FIXED_FRAME+24(R1) + // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this + // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. + ADD $LOCAL_REGARGS, R1, R20 + CALL runtime·spillArgs(SB) + MOVD R11, FIXED_FRAME+32(R1) // save R11 + MOVD R11, FIXED_FRAME+0(R1) // arg for moveMakeFuncArgPtrs + MOVD R20, FIXED_FRAME+8(R1) // arg for local args + CALL ·moveMakeFuncArgPtrs(SB) + MOVD FIXED_FRAME+32(R1), R11 // restore R11 ctxt + MOVD R11, FIXED_FRAME+0(R1) // ctxt (arg0) + MOVD $argframe+0(FP), R3 // save arg to callArg + MOVD R3, FIXED_FRAME+8(R1) // frame (arg1) + ADD $LOCAL_RETVALID, R1, R3 // addr of return flag + MOVB R0, (R3) // clear flag + MOVD R3, FIXED_FRAME+16(R1) // addr retvalid (arg2) + ADD $LOCAL_REGARGS, R1, R3 + MOVD R3, FIXED_FRAME+24(R1) // abiregargs (arg3) BL ·callReflect(SB) + ADD $LOCAL_REGARGS, R1, R20 // set address of spill area + CALL runtime·unspillArgs(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$336 NO_LOCAL_POINTERS - MOVD R11, FIXED_FRAME+0(R1) - MOVD $argframe+0(FP), R3 - MOVD R3, FIXED_FRAME+8(R1) - MOVB R0, FIXED_FRAME+32(R1) - ADD $FIXED_FRAME+32, R1, R3 - MOVD R3, FIXED_FRAME+16(R1) - MOVD R0, FIXED_FRAME+24(R1) + // NO_LOCAL_POINTERS is a lie. The stack map for the two locals in this + // frame is specially handled in the runtime. See the comment above LOCAL_RETVALID. + ADD $LOCAL_REGARGS, R1, R20 + CALL runtime·spillArgs(SB) + MOVD R11, FIXED_FRAME+0(R1) // arg0 ctxt + MOVD R11, FIXED_FRAME+32(R1) // save for later + MOVD R20, FIXED_FRAME+8(R1) // arg1 abiregargs + CALL ·moveMakeFuncArgPtrs(SB) + MOVD FIXED_FRAME+32(R1), R11 // restore ctxt + MOVD R11, FIXED_FRAME+0(R1) // set as arg0 + MOVD $argframe+0(FP), R3 // frame pointer + MOVD R3, FIXED_FRAME+8(R1) // set as arg1 + ADD $LOCAL_RETVALID, R1, R3 + MOVB $0, (R3) // clear ret flag + MOVD R3, FIXED_FRAME+16(R1) // addr of return flag + ADD $LOCAL_REGARGS, R1, R3 // addr of abiregargs + MOVD R3, FIXED_FRAME+24(R1) // set as arg3 BL ·callMethod(SB) + ADD $LOCAL_REGARGS, R1, R20 + CALL runtime·unspillArgs(SB) RET diff --git a/src/reflect/asm_riscv64.s b/src/reflect/asm_riscv64.s index e7071122778dae..1200b4d08eac27 100644 --- a/src/reflect/asm_riscv64.s +++ b/src/reflect/asm_riscv64.s @@ -5,34 +5,72 @@ #include "textflag.h" #include "funcdata.h" +// The frames of each of the two functions below contain two locals, at offsets +// that are known to the runtime. +// +// The first local is a bool called retValid with a whole pointer-word reserved +// for it on the stack. The purpose of this word is so that the runtime knows +// whether the stack-allocated return space contains valid values for stack +// scanning. +// +// The second local is an abi.RegArgs value whose offset is also known to the +// runtime, so that a stack map for it can be constructed, since it contains +// pointers visible to the GC. +#define LOCAL_RETVALID 40 +#define LOCAL_REGARGS 48 + +// The frame size of the functions below is +// 32 (args of callReflect/callMethod) + (8 bool with padding) + 392 (abi.RegArgs) = 432. + // makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here, runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25 + CALL runtime·spillArgs(SB) + MOV CTXT, 32(SP) // save CTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS + MOV CTXT, 8(SP) + MOV X25, 16(SP) + CALL ·moveMakeFuncArgPtrs(SB) + MOV 32(SP), CTXT // restore CTXT + MOV CTXT, 8(SP) MOV $argframe+0(FP), T0 MOV T0, 16(SP) - ADD $40, SP, T1 + MOV ZERO, LOCAL_RETVALID(SP) + ADD $LOCAL_RETVALID, SP, T1 MOV T1, 24(SP) - MOV ZERO, 32(SP) - MOVB ZERO, 40(SP) + ADD $LOCAL_REGARGS, SP, T1 + MOV T1, 32(SP) CALL ·callReflect(SB) + ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25 + CALL runtime·unspillArgs(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25 + CALL runtime·spillArgs(SB) + MOV CTXT, 32(SP) // save CTXT + MOV CTXT, 8(SP) + MOV X25, 16(SP) + CALL ·moveMakeFuncArgPtrs(SB) + MOV 32(SP), CTXT // restore CTXT MOV CTXT, 8(SP) MOV $argframe+0(FP), T0 MOV T0, 16(SP) - ADD $40, SP, T1 + MOV ZERO, LOCAL_RETVALID(SP) + ADD $LOCAL_RETVALID, SP, T1 MOV T1, 24(SP) - MOV ZERO, 32(SP) - MOVB ZERO, 40(SP) + ADD $LOCAL_REGARGS, SP, T1 + MOV T1, 32(SP) // frame size to 32+SP as callreflect args CALL ·callMethod(SB) + ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25 + CALL runtime·unspillArgs(SB) RET diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index d951d8d9997b36..c898bc834aec5b 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -6,7 +6,10 @@ package reflect -import "unsafe" +import ( + "internal/bytealg" + "unsafe" +) // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all @@ -35,11 +38,11 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { // and it's safe and valid to get Value's internal pointer. hard := func(v1, v2 Value) bool { switch v1.Kind() { - case Ptr: + case Pointer: if v1.typ.ptrdata == 0 { - // go:notinheap pointers can't be cyclic. - // At least, all of our current uses of go:notinheap have - // that property. The runtime ones aren't cyclic (and we don't use + // not-in-heap pointers can't be cyclic. + // At least, all of our current uses of runtime/internal/sys.NotInHeap + // have that property. The runtime ones aren't cyclic (and we don't use // DeepEqual on them anyway), and the cgo-generated ones are // all empty structs. return false @@ -53,13 +56,13 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { } if hard(v1, v2) { - // For a Ptr or Map value, we need to check flagIndir, + // For a Pointer or Map value, we need to check flagIndir, // which we do by calling the pointer method. // For Slice or Interface, flagIndir is always set, // and using v.ptr suffices. ptrval := func(v Value) unsafe.Pointer { switch v.Kind() { - case Ptr, Map: + case Pointer, Map: return v.pointer() default: return v.ptr @@ -99,9 +102,13 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { if v1.Len() != v2.Len() { return false } - if v1.Pointer() == v2.Pointer() { + if v1.UnsafePointer() == v2.UnsafePointer() { return true } + // Special case for []byte, which is common. + if v1.Type().Elem().Kind() == Uint8 { + return bytealg.Equal(v1.Bytes(), v2.Bytes()) + } for i := 0; i < v1.Len(); i++ { if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { return false @@ -113,8 +120,8 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { return v1.IsNil() == v2.IsNil() } return deepValueEqual(v1.Elem(), v2.Elem(), visited) - case Ptr: - if v1.Pointer() == v2.Pointer() { + case Pointer: + if v1.UnsafePointer() == v2.UnsafePointer() { return true } return deepValueEqual(v1.Elem(), v2.Elem(), visited) @@ -132,7 +139,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { if v1.Len() != v2.Len() { return false } - if v1.Pointer() == v2.Pointer() { + if v1.UnsafePointer() == v2.UnsafePointer() { return true } for _, k := range v1.MapKeys() { @@ -149,13 +156,25 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { } // Can't do better than this: return false + case Int, Int8, Int16, Int32, Int64: + return v1.Int() == v2.Int() + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return v1.Uint() == v2.Uint() + case String: + return v1.String() == v2.String() + case Bool: + return v1.Bool() == v2.Bool() + case Float32, Float64: + return v1.Float() == v2.Float() + case Complex64, Complex128: + return v1.Complex() == v2.Complex() default: // Normal equality suffices return valueInterface(v1, false) == valueInterface(v2, false) } } -// DeepEqual reports whether x and y are ``deeply equal,'' defined as follows. +// DeepEqual reports whether x and y are “deeply equal,” defined as follows. // Two values of identical type are deeply equal if one of the following cases applies. // Values of distinct types are never deeply equal. // @@ -206,7 +225,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { // values that have been compared before, it treats the values as // equal rather than examining the values to which they point. // This ensures that DeepEqual terminates. -func DeepEqual(x, y interface{}) bool { +func DeepEqual(x, y any) bool { if x == nil || y == nil { return x == y } diff --git a/src/reflect/example_test.go b/src/reflect/example_test.go index 23c08e4950049e..b4f3b2932f78c3 100644 --- a/src/reflect/example_test.go +++ b/src/reflect/example_test.go @@ -14,7 +14,7 @@ import ( ) func ExampleKind() { - for _, v := range []interface{}{"hi", 42, func() {}} { + for _, v := range []any{"hi", 42, func() {}} { switch v := reflect.ValueOf(v); v.Kind() { case reflect.String: fmt.Println(v.String()) @@ -45,7 +45,7 @@ func ExampleMakeFunc() { // When the function is invoked, reflect turns the arguments // into Values, calls swap, and then turns swap's result slice // into the values returned by the new function. - makeSwap := func(fptr interface{}) { + makeSwap := func(fptr any) { // fptr is a pointer to a function. // Obtain the function value itself (likely nil) as a reflect.Value // so that we can query its type and then set the value. @@ -166,3 +166,44 @@ func ExampleStructOf() { // json: {"height":0.4,"age":2} // value: &{Height:1.5 Age:10} } + +func ExampleValue_FieldByIndex() { + // This example shows a case in which the name of a promoted field + // is hidden by another field: FieldByName will not work, so + // FieldByIndex must be used instead. + type user struct { + firstName string + lastName string + } + + type data struct { + user + firstName string + lastName string + } + + u := data{ + user: user{"Embedded John", "Embedded Doe"}, + firstName: "John", + lastName: "Doe", + } + + s := reflect.ValueOf(u).FieldByIndex([]int{0, 1}) + fmt.Println("embedded last name:", s) + + // Output: + // embedded last name: Embedded Doe +} + +func ExampleValue_FieldByName() { + type user struct { + firstName string + lastName string + } + u := user{firstName: "John", lastName: "Doe"} + s := reflect.ValueOf(u) + + fmt.Println("Name:", s.FieldByName("firstName")) + // Output: + // Name: John +} diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index b6830a98020f54..f7d2cc362d5875 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -5,6 +5,7 @@ package reflect import ( + "internal/goarch" "sync" "unsafe" ) @@ -22,8 +23,6 @@ func IsRO(v Value) bool { var CallGC = &callGC -const PtrSize = ptrSize - // FuncLayout calls funcLayout and returns a subset of the results for testing. // // Bitmaps like stack, gc, inReg, and outReg are expanded such that each bit @@ -65,7 +64,7 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, // Expand frame type's GC bitmap into byte-map. ptrs = ft.ptrdata != 0 if ptrs { - nptrs := ft.ptrdata / ptrSize + nptrs := ft.ptrdata / goarch.PtrSize gcdata := ft.gcSlice(0, (nptrs+7)/8) for i := uintptr(0); i < nptrs; i++ { gc = append(gc, gcdata[i/8]>>(i%8)&1) @@ -89,7 +88,7 @@ func TypeLinks() []string { var GCBits = gcbits -func gcbits(interface{}) []byte // provided by runtime +func gcbits(any) []byte // provided by runtime func MapBucketOf(x, y Type) Type { return bucketOf(x.(*rtype), y.(*rtype)) @@ -141,7 +140,7 @@ func IsExported(t Type) bool { } func ResolveReflectName(s string) { - resolveReflectName(newName(s, "", false)) + resolveReflectName(newName(s, "", false, false)) } type Buffer struct { @@ -162,3 +161,5 @@ func SetArgRegs(ints, floats int, floatSize uintptr) (oldInts, oldFloats int, ol clearLayoutCache() return } + +var MethodValueCallCodePtr = methodValueCallCodePtr diff --git a/src/reflect/float32reg_generic.go b/src/reflect/float32reg_generic.go new file mode 100644 index 00000000000000..23ad4bf285b248 --- /dev/null +++ b/src/reflect/float32reg_generic.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !ppc64 && !ppc64le && !riscv64 + +package reflect + +import "unsafe" + +// This file implements a straightforward conversion of a float32 +// value into its representation in a register. This conversion +// applies for amd64 and arm64. It is also chosen for the case of +// zero argument registers, but is not used. + +func archFloat32FromReg(reg uint64) float32 { + i := uint32(reg) + return *(*float32)(unsafe.Pointer(&i)) +} + +func archFloat32ToReg(val float32) uint64 { + return uint64(*(*uint32)(unsafe.Pointer(&val))) +} diff --git a/src/reflect/float32reg_ppc64x.s b/src/reflect/float32reg_ppc64x.s new file mode 100644 index 00000000000000..a4deb18427af0f --- /dev/null +++ b/src/reflect/float32reg_ppc64x.s @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +#include "textflag.h" + +// On PPC64, the float32 becomes a float64 +// when loaded in a register, different from +// other platforms. These functions are +// needed to ensure correct conversions on PPC64. + +// Convert float32->uint64 +TEXT ·archFloat32ToReg(SB),NOSPLIT,$0-16 + FMOVS val+0(FP), F1 + FMOVD F1, ret+8(FP) + RET + +// Convert uint64->float32 +TEXT ·archFloat32FromReg(SB),NOSPLIT,$0-12 + FMOVD reg+0(FP), F1 + // Normally a float64->float32 conversion + // would need rounding, but that is not needed + // here since the uint64 was originally converted + // from float32, and should be avoided to + // preserve SNaN values. + FMOVS F1, ret+8(FP) + RET + diff --git a/src/reflect/float32reg_riscv64.s b/src/reflect/float32reg_riscv64.s new file mode 100644 index 00000000000000..8fcf3ba3e92f45 --- /dev/null +++ b/src/reflect/float32reg_riscv64.s @@ -0,0 +1,27 @@ +// Copyright 2022 The Go 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" + +// riscv64 allows 32-bit floats to live in the bottom +// part of the register, it expects them to be NaN-boxed. +// These functions are needed to ensure correct conversions +// on riscv64. + +// Convert float32->uint64 +TEXT ·archFloat32ToReg(SB),NOSPLIT,$0-16 + MOVF val+0(FP), F1 + MOVD F1, ret+8(FP) + RET + +// Convert uint64->float32 +TEXT ·archFloat32FromReg(SB),NOSPLIT,$0-12 + // Normally a float64->float32 conversion + // would need rounding, but riscv64 store valid + // float32 in the lower 32 bits, thus we only need to + // unboxed the NaN-box by store a float32. + MOVD reg+0(FP), F1 + MOVF F1, ret+8(FP) + RET + diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go index d53e68a3594a66..ee0729903e38c8 100644 --- a/src/reflect/makefunc.go +++ b/src/reflect/makefunc.go @@ -26,9 +26,9 @@ type makeFuncImpl struct { // that wraps the function fn. When called, that new function // does the following: // -// - converts its arguments to a slice of Values. -// - runs results := fn(args). -// - returns the results as a slice of Values, one per formal result. +// - converts its arguments to a slice of Values. +// - runs results := fn(args). +// - returns the results as a slice of Values, one per formal result. // // The implementation fn can assume that the argument Value slice // has the number and type of arguments given by typ. @@ -43,7 +43,6 @@ type makeFuncImpl struct { // // The Examples section of the documentation includes an illustration // of how to use MakeFunc to build a swap function for different types. -// func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { if typ.Kind() != Func { panic("reflect: call of MakeFunc with non-Func type") @@ -52,21 +51,17 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { t := typ.common() ftyp := (*funcType)(unsafe.Pointer(t)) - // Indirect Go func value (dummy) to obtain - // actual code address. (A Go func value is a pointer - // to a C function pointer. https://golang.org/s/go11func.) - dummy := makeFuncStub - code := **(**uintptr)(unsafe.Pointer(&dummy)) + code := abi.FuncPCABI0(makeFuncStub) // makeFuncImpl contains a stack map for use by the runtime - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) impl := &makeFuncImpl{ makeFuncCtxt: makeFuncCtxt{ fn: code, - stack: abi.stackPtrs, - argLen: abi.stackCallArgsSize, - regPtrs: abi.inRegPtrs, + stack: abid.stackPtrs, + argLen: abid.stackCallArgsSize, + regPtrs: abid.inRegPtrs, }, ftyp: ftyp, fn: fn, @@ -111,20 +106,16 @@ func makeMethodValue(op string, v Value) Value { // v.Type returns the actual type of the method value. ftyp := (*funcType)(unsafe.Pointer(v.Type().(*rtype))) - // Indirect Go func value (dummy) to obtain - // actual code address. (A Go func value is a pointer - // to a C function pointer. https://golang.org/s/go11func.) - dummy := methodValueCall - code := **(**uintptr)(unsafe.Pointer(&dummy)) + code := methodValueCallCodePtr() // methodValue contains a stack map for use by the runtime - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) fv := &methodValue{ makeFuncCtxt: makeFuncCtxt{ fn: code, - stack: abi.stackPtrs, - argLen: abi.stackCallArgsSize, - regPtrs: abi.inRegPtrs, + stack: abid.stackPtrs, + argLen: abid.stackCallArgsSize, + regPtrs: abid.inRegPtrs, }, method: int(v.flag) >> flagMethodShift, rcvr: rcvr, @@ -138,6 +129,10 @@ func makeMethodValue(op string, v Value) Value { return Value{&ftyp.rtype, unsafe.Pointer(fv), v.flag&flagRO | flag(Func)} } +func methodValueCallCodePtr() uintptr { + return abi.FuncPCABI0(methodValueCall) +} + // methodValueCall is an assembly function that is the code half of // the function returned from makeMethodValue. It expects a *methodValue // as its context register, and its job is to invoke callMethod(ctxt, frame) @@ -163,6 +158,7 @@ type makeFuncCtxt struct { // nosplit because pointers are being held in uintptr slots in args, so // having our stack scanned now could lead to accidentally freeing // memory. +// //go:nosplit func moveMakeFuncArgPtrs(ctxt *makeFuncCtxt, args *abi.RegArgs) { for i, arg := range args.Ints { diff --git a/src/reflect/nih_test.go b/src/reflect/nih_test.go new file mode 100644 index 00000000000000..f503939299f6a5 --- /dev/null +++ b/src/reflect/nih_test.go @@ -0,0 +1,38 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo + +package reflect_test + +import ( + . "reflect" + "runtime/cgo" + "testing" + "unsafe" +) + +type nih struct { + _ cgo.Incomplete + x int +} + +var global_nih = nih{x: 7} + +func TestNotInHeapDeref(t *testing.T) { + // See issue 48399. + v := ValueOf((*nih)(nil)) + v.Elem() + shouldPanic("reflect: call of reflect.Value.Field on zero Value", func() { v.Elem().Field(0) }) + + v = ValueOf(&global_nih) + if got := v.Elem().Field(1).Int(); got != 7 { + t.Fatalf("got %d, want 7", got) + } + + v = ValueOf((*nih)(unsafe.Pointer(new(int)))) + shouldPanic("reflect: reflect.Value.Elem on an invalid notinheap pointer", func() { v.Elem() }) + shouldPanic("reflect: reflect.Value.Pointer on an invalid notinheap pointer", func() { v.Pointer() }) + shouldPanic("reflect: reflect.Value.UnsafePointer on an invalid notinheap pointer", func() { v.UnsafePointer() }) +} diff --git a/src/reflect/set_test.go b/src/reflect/set_test.go index a633e6eee2b3e4..028c051cfbd5fb 100644 --- a/src/reflect/set_test.go +++ b/src/reflect/set_test.go @@ -10,6 +10,7 @@ import ( "go/token" "io" . "reflect" + "strings" "testing" "unsafe" ) @@ -31,7 +32,7 @@ func TestImplicitMapConversion(t *testing.T) { } { // convert interface key - m := make(map[interface{}]int) + m := make(map[any]int) mv := ValueOf(m) mv.SetMapIndex(ValueOf(1), ValueOf(2)) x, ok := m[1] @@ -44,7 +45,7 @@ func TestImplicitMapConversion(t *testing.T) { } { // convert interface value - m := make(map[int]interface{}) + m := make(map[int]any) mv := ValueOf(m) mv.SetMapIndex(ValueOf(1), ValueOf(2)) x, ok := m[1] @@ -57,7 +58,7 @@ func TestImplicitMapConversion(t *testing.T) { } { // convert both interface key and interface value - m := make(map[interface{}]interface{}) + m := make(map[any]any) mv := ValueOf(m) mv.SetMapIndex(ValueOf(1), ValueOf(2)) x, ok := m[1] @@ -79,7 +80,7 @@ func TestImplicitMapConversion(t *testing.T) { if x != b2 { t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) { + if p := mv.MapIndex(ValueOf(b1)).Elem().UnsafePointer(); p != unsafe.Pointer(b2) { t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2) } } @@ -94,7 +95,7 @@ func TestImplicitMapConversion(t *testing.T) { if x != c2 { t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m) } - if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() { + if p := mv.MapIndex(ValueOf(c1)).UnsafePointer(); p != ValueOf(c2).UnsafePointer() { t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2) } } @@ -110,7 +111,7 @@ func TestImplicitMapConversion(t *testing.T) { if x != b2 { t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m) } - if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) { + if p := mv.MapIndex(ValueOf(b1)).UnsafePointer(); p != unsafe.Pointer(b2) { t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2) } } @@ -141,7 +142,7 @@ func TestImplicitSendConversion(t *testing.T) { func TestImplicitCallConversion(t *testing.T) { // Arguments must be assignable to parameter types. fv := ValueOf(io.WriteString) - b := new(bytes.Buffer) + b := new(strings.Builder) fv.Call([]Value{ValueOf(b), ValueOf("hello world")}) if b.String() != "hello world" { t.Errorf("After call: string=%q want %q", b.String(), "hello world") @@ -160,8 +161,8 @@ func TestImplicitAppendConversion(t *testing.T) { } var implementsTests = []struct { - x interface{} - t interface{} + x any + t any b bool }{ {new(*bytes.Buffer), new(io.Reader), true}, @@ -198,8 +199,8 @@ func TestImplements(t *testing.T) { } var assignableTests = []struct { - x interface{} - t interface{} + x any + t any b bool }{ {new(chan int), new(<-chan int), true}, @@ -207,13 +208,13 @@ var assignableTests = []struct { {new(*int), new(IntPtr), true}, {new(IntPtr), new(*int), true}, {new(IntPtr), new(IntPtr1), false}, - {new(Ch), new(<-chan interface{}), true}, + {new(Ch), new(<-chan any), true}, // test runs implementsTests too } type IntPtr *int type IntPtr1 *int -type Ch <-chan interface{} +type Ch <-chan any func TestAssignableTo(t *testing.T) { for _, tt := range append(assignableTests, implementsTests...) { diff --git a/src/reflect/stubs_ppc64x.go b/src/reflect/stubs_ppc64x.go new file mode 100644 index 00000000000000..06c8bf5483bbe5 --- /dev/null +++ b/src/reflect/stubs_ppc64x.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64le || ppc64 + +package reflect + +func archFloat32FromReg(reg uint64) float32 +func archFloat32ToReg(val float32) uint64 diff --git a/src/reflect/stubs_riscv64.go b/src/reflect/stubs_riscv64.go new file mode 100644 index 00000000000000..a72ebab9706f02 --- /dev/null +++ b/src/reflect/stubs_riscv64.go @@ -0,0 +1,8 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reflect + +func archFloat32FromReg(reg uint64) float32 +func archFloat32ToReg(val float32) uint64 diff --git a/src/reflect/swapper.go b/src/reflect/swapper.go index 0cf40666b1e8e5..745c7b9f4970ba 100644 --- a/src/reflect/swapper.go +++ b/src/reflect/swapper.go @@ -5,6 +5,7 @@ package reflect import ( + "internal/goarch" "internal/unsafeheader" "unsafe" ) @@ -13,7 +14,7 @@ import ( // slice. // // Swapper panics if the provided interface is not a slice. -func Swapper(slice interface{}) func(i, j int) { +func Swapper(slice any) func(i, j int) { v := ValueOf(slice) if v.Kind() != Slice { panic(&ValueError{Method: "Swapper", Kind: v.Kind()}) @@ -36,7 +37,7 @@ func Swapper(slice interface{}) func(i, j int) { // Some common & small cases, without using memmove: if hasPtr { - if size == ptrSize { + if size == goarch.PtrSize { ps := *(*[]unsafe.Pointer)(v.ptr) return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] } } diff --git a/src/reflect/tostring_test.go b/src/reflect/tostring_test.go index e416fd84da7bd1..193484a01bc4eb 100644 --- a/src/reflect/tostring_test.go +++ b/src/reflect/tostring_test.go @@ -39,7 +39,7 @@ func valueToString(val Value) string { } else { return "false" } - case Ptr: + case Pointer: v := val str = typ.String() + "(" if v.IsNil() { diff --git a/src/reflect/type.go b/src/reflect/type.go index f672a7a5d5fc99..443a4b258d662f 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -16,6 +16,7 @@ package reflect import ( + "internal/goarch" "internal/unsafeheader" "strconv" "sync" @@ -71,7 +72,9 @@ type Type interface { // NumMethod returns the number of methods accessible using Method. // - // Note that NumMethod counts unexported methods only for interface types. + // For a non-interface type, it returns the number of exported methods. + // + // For an interface type, it returns the number of exported and unexported methods. NumMethod() int // Name returns the type's name within its package for a defined type. @@ -125,7 +128,7 @@ type Type interface { // Chan: ChanDir, Elem // Func: In, NumIn, Out, NumOut, IsVariadic. // Map: Key, Elem - // Ptr: Elem + // Pointer: Elem // Slice: Elem // Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField @@ -153,7 +156,7 @@ type Type interface { IsVariadic() bool // Elem returns a type's element type. - // It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice. + // It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice. Elem() Type // Field returns a struct type's i'th field. @@ -228,7 +231,7 @@ type Type interface { // See https://golang.org/issue/4876 for more details. /* - * These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go). + * These data structures are known to the compiler (../cmd/compile/internal/reflectdata/reflect.go). * A few are known to ../runtime/type.go to convey to debuggers. * They are also known to ../runtime/type.go. */ @@ -260,17 +263,21 @@ const ( Func Interface Map - Ptr + Pointer Slice String Struct UnsafePointer ) +// Ptr is the old name for the Pointer kind. +const Ptr = Pointer + // tflag is used by an rtype to signal what extra type information is // available in the memory directly following the rtype value. // // tflag values must be kept in sync with copies in: +// // cmd/compile/internal/reflectdata/reflect.go // cmd/link/internal/ld/decodesym.go // runtime/type.go @@ -426,17 +433,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + name name // name is always non-empty + typ *rtype // type of field + offset uintptr // byte offset of field } func (f *structField) embedded() bool { - return f.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -453,6 +456,7 @@ type structType struct { // 1<<0 the name is exported // 1<<1 tag data follows the name // 1<<2 pkgPath nameOff follows the name and tag +// 1<<3 the name is of an embedded (a.k.a. anonymous) field // // Following that, there is a varint-encoded length of the name, // followed by the name itself. @@ -489,6 +493,10 @@ func (n name) hasTag() bool { return (*n.bytes)&(1<<1) != 0 } +func (n name) embedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + // readVarint parses a varint as encoded by encoding/binary. // It returns the number of encoded bytes and the encoded value. func (n name) readVarint(off int) (int, int) { @@ -558,7 +566,7 @@ func (n name) pkgPath() string { return pkgPathName.name() } -func newName(n, tag string, exported bool) name { +func newName(n, tag string, exported, embedded bool) name { if len(n) >= 1<<29 { panic("reflect.nameFrom: name too long: " + n[:1024] + "...") } @@ -579,6 +587,9 @@ func newName(n, tag string, exported bool) name { l += tagLenLen + len(tag) bits |= 1 << 1 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits @@ -628,8 +639,8 @@ const ( // String returns the name of k. func (k Kind) String() string { - if int(k) < len(kindNames) { - return kindNames[k] + if uint(k) < uint(len(kindNames)) { + return kindNames[uint(k)] } return "kind" + strconv.Itoa(int(k)) } @@ -657,7 +668,7 @@ var kindNames = []string{ Func: "func", Interface: "interface", Map: "map", - Ptr: "ptr", + Pointer: "ptr", Slice: "slice", String: "string", Struct: "struct", @@ -740,7 +751,7 @@ func (t *rtype) uncommon() *uncommonType { switch t.Kind() { case Struct: return &(*structTypeUncommon)(unsafe.Pointer(t)).u - case Ptr: + case Pointer: type u struct { ptrType u uncommonType @@ -911,7 +922,14 @@ func (t *rtype) Name() string { } s := t.String() i := len(s) - 1 - for i >= 0 && s[i] != '.' { + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } i-- } return s[i+1:] @@ -944,7 +962,7 @@ func (t *rtype) Elem() Type { case Map: tt := (*mapType)(unsafe.Pointer(t)) return toType(tt.elem) - case Ptr: + case Pointer: tt := (*ptrType)(unsafe.Pointer(t)) return toType(tt.elem) case Slice: @@ -1242,7 +1260,7 @@ func (t *structType) Field(i int) (f StructField) { if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) } - f.Offset = p.offset() + f.Offset = p.offset // NOTE(rsc): This is the only allocation in the interface // presented by a reflect.Type. It would be nice to avoid, @@ -1256,7 +1274,7 @@ func (t *structType) Field(i int) (f StructField) { } // TODO(gri): Should there be an error/bool indicator if the index -// is wrong for FieldByIndex? +// is wrong for FieldByIndex? // FieldByIndex returns the nested field corresponding to index. func (t *structType) FieldByIndex(index []int) (f StructField) { @@ -1264,7 +1282,7 @@ func (t *structType) FieldByIndex(index []int) (f StructField) { for i, x := range index { if i > 0 { ft := f.Type - if ft.Kind() == Ptr && ft.Elem().Kind() == Struct { + if ft.Kind() == Pointer && ft.Elem().Kind() == Struct { ft = ft.Elem() } f.Type = ft @@ -1335,7 +1353,7 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel if f.embedded() { // Embedded field of type T or *T. ntyp = f.typ - if ntyp.Kind() == Ptr { + if ntyp.Kind() == Pointer { ntyp = ntyp.Elem().common() } } @@ -1410,17 +1428,30 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) { // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. -func TypeOf(i interface{}) Type { +func TypeOf(i any) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } -// ptrMap is the cache for PtrTo. +// rtypeOf directly extracts the *rtype of the provided value. +func rtypeOf(i any) *rtype { + eface := *(*emptyInterface)(unsafe.Pointer(&i)) + return eface.typ +} + +// ptrMap is the cache for PointerTo. var ptrMap sync.Map // map[*rtype]*ptrType // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. -func PtrTo(t Type) Type { +// +// PtrTo is the old spelling of PointerTo. +// The two functions behave identically. +func PtrTo(t Type) Type { return PointerTo(t) } + +// PointerTo returns the pointer type with element t. +// For example, if t represents type Foo, PointerTo(t) represents *Foo. +func PointerTo(t Type) Type { return t.(*rtype).ptrTo() } @@ -1447,11 +1478,11 @@ func (t *rtype) ptrTo() *rtype { // Create a new ptrType starting with the description // of an *unsafe.Pointer. - var iptr interface{} = (*unsafe.Pointer)(nil) + var iptr any = (*unsafe.Pointer)(nil) prototype := *(**ptrType)(unsafe.Pointer(&iptr)) pp := *prototype - pp.str = resolveReflectName(newName(s, "", false)) + pp.str = resolveReflectName(newName(s, "", false, false)) pp.ptrToThis = 0 // For the type structures linked into the binary, the @@ -1694,7 +1725,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { case Map: return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) - case Ptr, Slice: + case Pointer, Slice: return haveIdenticalType(T.Elem(), V.Elem(), cmpTags) case Struct: @@ -1718,7 +1749,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } @@ -1865,12 +1899,12 @@ func ChanOf(dir ChanDir, t Type) Type { } // Make a channel type. - var ichan interface{} = (chan unsafe.Pointer)(nil) + var ichan any = (chan unsafe.Pointer)(nil) prototype := *(**chanType)(unsafe.Pointer(&ichan)) ch := *prototype ch.tflag = tflagRegularMemory ch.dir = uintptr(dir) - ch.str = resolveReflectName(newName(s, "", false)) + ch.str = resolveReflectName(newName(s, "", false, false)) ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ @@ -1911,9 +1945,9 @@ func MapOf(key, elem Type) Type { // Make a map type. // Note: flag values must match those used in the TMAP case // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. - var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) + var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := **(**mapType)(unsafe.Pointer(&imap)) - mt.str = resolveReflectName(newName(s, "", false)) + mt.str = resolveReflectName(newName(s, "", false, false)) mt.tflag = 0 mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp @@ -1924,13 +1958,13 @@ func MapOf(key, elem Type) Type { } mt.flags = 0 if ktyp.size > maxKeySize { - mt.keysize = uint8(ptrSize) + mt.keysize = uint8(goarch.PtrSize) mt.flags |= 1 // indirect key } else { mt.keysize = uint8(ktyp.size) } if etyp.size > maxValSize { - mt.valuesize = uint8(ptrSize) + mt.valuesize = uint8(goarch.PtrSize) mt.flags |= 2 // indirect value } else { mt.valuesize = uint8(etyp.size) @@ -1991,7 +2025,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Make a func type. - var ifunc interface{} = (func())(nil) + var ifunc any = (func())(nil) prototype := *(**funcType)(unsafe.Pointer(&ifunc)) n := len(in) + len(out) @@ -2043,9 +2077,7 @@ func FuncOf(in, out []Type, variadic bool) Type { args = append(args, t) hash = fnv1(hash, byte(t.hash>>24), byte(t.hash>>16), byte(t.hash>>8), byte(t.hash)) } - 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)) @@ -2092,7 +2124,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.str = resolveReflectName(newName(str, "", false)) + ft.str = resolveReflectName(newName(str, "", false, false)) ft.ptrToThis = 0 return addToCache(&ft.rtype) } @@ -2135,7 +2167,7 @@ func funcStr(ft *funcType) string { // That is, x == x for all values x of type t. func isReflexive(t *rtype) bool { switch t.Kind() { - case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Ptr, String, UnsafePointer: + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, String, UnsafePointer: return true case Float32, Float64, Complex64, Complex128, Interface: return false @@ -2159,7 +2191,7 @@ func isReflexive(t *rtype) bool { // needKeyUpdate reports whether map overwrites require the key to be copied. func needKeyUpdate(t *rtype) bool { switch t.Kind() { - case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Ptr, UnsafePointer: + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, UnsafePointer: return false case Float32, Float64, Complex64, Complex128, Interface, String: // Float keys can be updated from +0 to -0. @@ -2204,7 +2236,7 @@ func hashMightPanic(t *rtype) bool { } } -// Make sure these routines stay in sync with ../../runtime/map.go! +// Make sure these routines stay in sync with ../runtime/map.go! // These types exist only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in string // for possible debugging use. @@ -2216,46 +2248,47 @@ const ( func bucketOf(ktyp, etyp *rtype) *rtype { if ktyp.size > maxKeySize { - ktyp = PtrTo(ktyp).(*rtype) + ktyp = PointerTo(ktyp).(*rtype) } if etyp.size > maxValSize { - etyp = PtrTo(etyp).(*rtype) + etyp = PointerTo(etyp).(*rtype) } // Prepare GC data if any. - // A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+2*ptrSize bytes, - // or 2072 bytes, or 259 pointer-size words, or 33 bytes of pointer bitmap. + // A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+ptrSize bytes, + // or 2064 bytes, or 258 pointer-size words, or 33 bytes of pointer bitmap. // Note that since the key and value are known to be <= 128 bytes, // they're guaranteed to have bitmaps instead of GC programs. var gcdata *byte var ptrdata uintptr - var overflowPad uintptr - size := bucketSize*(1+ktyp.size+etyp.size) + overflowPad + ptrSize + size := bucketSize*(1+ktyp.size+etyp.size) + goarch.PtrSize if size&uintptr(ktyp.align-1) != 0 || size&uintptr(etyp.align-1) != 0 { panic("reflect: bad size computation in MapOf") } if ktyp.ptrdata != 0 || etyp.ptrdata != 0 { - nptr := (bucketSize*(1+ktyp.size+etyp.size) + ptrSize) / ptrSize - mask := make([]byte, (nptr+7)/8) - base := bucketSize / ptrSize + nptr := (bucketSize*(1+ktyp.size+etyp.size) + goarch.PtrSize) / goarch.PtrSize + n := (nptr + 7) / 8 + // Runtime needs pointer masks to be a multiple of uintptr in size. + n = (n + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) + mask := make([]byte, n) + base := bucketSize / goarch.PtrSize if ktyp.ptrdata != 0 { emitGCMask(mask, base, ktyp, bucketSize) } - base += bucketSize * ktyp.size / ptrSize + base += bucketSize * ktyp.size / goarch.PtrSize if etyp.ptrdata != 0 { emitGCMask(mask, base, etyp, bucketSize) } - base += bucketSize * etyp.size / ptrSize - base += overflowPad / ptrSize + base += bucketSize * etyp.size / goarch.PtrSize word := base mask[word/8] |= 1 << (word % 8) gcdata = &mask[0] - ptrdata = (word + 1) * ptrSize + ptrdata = (word + 1) * goarch.PtrSize // overflow word must be last if ptrdata != size { @@ -2264,17 +2297,14 @@ func bucketOf(ktyp, etyp *rtype) *rtype { } b := &rtype{ - align: ptrSize, + align: goarch.PtrSize, size: size, kind: uint8(Struct), ptrdata: ptrdata, gcdata: gcdata, } - if overflowPad > 0 { - b.align = 8 - } s := "bucket(" + ktyp.String() + "," + etyp.String() + ")" - b.str = resolveReflectName(newName(s, "", false)) + b.str = resolveReflectName(newName(s, "", false, false)) return b } @@ -2288,8 +2318,8 @@ func emitGCMask(out []byte, base uintptr, typ *rtype, n uintptr) { if typ.kind&kindGCProg != 0 { panic("reflect: unexpected GC program") } - ptrs := typ.ptrdata / ptrSize - words := typ.size / ptrSize + ptrs := typ.ptrdata / goarch.PtrSize + words := typ.size / goarch.PtrSize mask := typ.gcSlice(0, (ptrs+7)/8) for j := uintptr(0); j < ptrs; j++ { if (mask[j/8]>>(j%8))&1 != 0 { @@ -2312,7 +2342,7 @@ func appendGCProg(dst []byte, typ *rtype) []byte { } // Element is small with pointer mask; use as literal bits. - ptrs := typ.ptrdata / ptrSize + ptrs := typ.ptrdata / goarch.PtrSize mask := typ.gcSlice(0, (ptrs+7)/8) // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes). @@ -2349,11 +2379,11 @@ func SliceOf(t Type) Type { } // Make a slice type. - var islice interface{} = ([]unsafe.Pointer)(nil) + var islice any = ([]unsafe.Pointer)(nil) prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := *prototype slice.tflag = 0 - slice.str = resolveReflectName(newName(s, "", false)) + slice.str = resolveReflectName(newName(s, "", false, false)) slice.hash = fnv1(typ.hash, '[') slice.elem = typ slice.ptrToThis = 0 @@ -2457,10 +2487,10 @@ func StructOf(fields []StructField) Type { repr = append(repr, (" " + name)...) if f.embedded() { // Embedded field - if f.typ.Kind() == Ptr { + if f.typ.Kind() == Pointer { // Embedded ** and *interface{} are illegal elem := ft.Elem() - if k := elem.Kind(); k == Ptr || k == Interface { + if k := elem.Kind(); k == Pointer || k == Interface { panic("reflect.StructOf: illegal embedded field type " + ft.String()) } } @@ -2525,7 +2555,7 @@ func StructOf(fields []StructField) Type { tfn: resolveReflectText(unsafe.Pointer(&tfn)), }) } - case Ptr: + case Pointer: ptr := (*ptrType)(unsafe.Pointer(ft)) if unt := ptr.uncommon(); unt != nil { if i > 0 && unt.mcount > 0 { @@ -2593,7 +2623,7 @@ func StructOf(fields []StructField) Type { } } } - if _, dup := fset[name]; dup { + if _, dup := fset[name]; dup && name != "_" { panic("reflect.StructOf: duplicate field " + name) } fset[name] = struct{}{} @@ -2612,11 +2642,17 @@ func StructOf(fields []StructField) Type { comparable = comparable && (ft.equal != nil) offset := align(size, uintptr(ft.align)) + if offset < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } if ft.align > typalign { typalign = ft.align } size = offset + ft.size - f.offsetEmbed |= offset << 1 + if size < offset { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + f.offset = offset if ft.size == 0 { lastzero = size @@ -2632,6 +2668,9 @@ func StructOf(fields []StructField) Type { // zero-sized field can't manufacture a pointer to the // next object in the heap. See issue 9401. size++ + if size == 0 { + panic("reflect.StructOf: struct size would exceed virtual address space") + } } var typ *structType @@ -2653,8 +2692,8 @@ func StructOf(fields []StructField) Type { {Name: "M", Type: ArrayOf(len(methods), TypeOf(methods[0]))}, })) - typ = (*structType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr())) - ut = (*uncommonType)(unsafe.Pointer(tt.Elem().Field(1).UnsafeAddr())) + typ = (*structType)(tt.Elem().Field(0).Addr().UnsafePointer()) + ut = (*uncommonType)(tt.Elem().Field(1).Addr().UnsafePointer()) copy(tt.Elem().Field(2).Slice(0, len(methods)).Interface().([]method), methods) } @@ -2674,15 +2713,19 @@ func StructOf(fields []StructField) Type { str := string(repr) // Round the size up to be a multiple of the alignment. - size = align(size, uintptr(typalign)) + s := align(size, uintptr(typalign)) + if s < size { + panic("reflect.StructOf: struct size would exceed virtual address space") + } + size = s // Make the struct type. - var istruct interface{} = struct{}{} + var istruct any = struct{}{} prototype := *(**structType)(unsafe.Pointer(&istruct)) *typ = *prototype typ.fields = fs if pkgpath != "" { - typ.pkgPath = newName(pkgpath, "", false) + typ.pkgPath = newName(pkgpath, "", false, false) } // Look in cache. @@ -2726,7 +2769,7 @@ func StructOf(fields []StructField) Type { } } - typ.str = resolveReflectName(newName(str, "", false)) + typ.str = resolveReflectName(newName(str, "", false, false)) typ.tflag = 0 // TODO: set tflagRegularMemory typ.hash = hash typ.size = size @@ -2758,14 +2801,14 @@ func StructOf(fields []StructField) Type { continue } // Pad to start of this field with zeros. - if ft.offset() > off { - n := (ft.offset() - off) / ptrSize + if ft.offset > off { + n := (ft.offset - off) / goarch.PtrSize prog = append(prog, 0x01, 0x00) // emit a 0 bit if n > 1 { prog = append(prog, 0x81) // repeat previous bit prog = appendVarint(prog, n-1) // n-1 times } - off = ft.offset() + off = ft.offset } prog = appendGCProg(prog, ft.typ) @@ -2787,8 +2830,8 @@ func StructOf(fields []StructField) Type { if comparable { typ.equal = func(p, q unsafe.Pointer) bool { for _, ft := range typ.fields { - pi := add(p, ft.offset(), "&x.field safe") - qi := add(q, ft.offset(), "&x.field safe") + pi := add(p, ft.offset, "&x.field safe") + qi := add(q, ft.offset, "&x.field safe") if !ft.typ.equal(pi, qi) { return false } @@ -2825,16 +2868,11 @@ func runtimeStructField(field StructField) (structField, string) { } } - offsetEmbed := uintptr(0) - if field.Anonymous { - offsetEmbed |= 1 - } - resolveReflectType(field.Type.common()) // install in runtime f := structField{ - name: newName(field.Name, string(field.Tag), field.IsExported()), - typ: field.Type.common(), - offsetEmbed: offsetEmbed, + name: newName(field.Name, string(field.Tag), field.IsExported(), field.Anonymous), + typ: field.Type.common(), + offset: 0, } return f, field.PkgPath } @@ -2858,7 +2896,7 @@ func typeptrdata(t *rtype) uintptr { return 0 } f := st.fields[field] - return f.offset() + f.typ.ptrdata + return f.offset + f.typ.ptrdata default: panic("reflect.typeptrdata: unexpected type, " + t.String()) @@ -2897,11 +2935,11 @@ func ArrayOf(length int, elem Type) Type { } // Make an array type. - var iarray interface{} = [1]unsafe.Pointer{} + var iarray any = [1]unsafe.Pointer{} prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := *prototype array.tflag = typ.tflag & tflagRegularMemory - array.str = resolveReflectName(newName(s, "", false)) + array.str = resolveReflectName(newName(s, "", false, false)) array.hash = fnv1(typ.hash, '[') for n := uint32(length); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -2936,11 +2974,14 @@ func ArrayOf(length int, elem Type) Type { array.gcdata = typ.gcdata array.ptrdata = typ.ptrdata - case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*ptrSize: + case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*goarch.PtrSize: // Element is small with pointer mask; array is still small. // Create direct pointer mask by turning each 1 bit in elem // into length 1 bits in larger mask. - mask := make([]byte, (array.ptrdata/ptrSize+7)/8) + n := (array.ptrdata/goarch.PtrSize + 7) / 8 + // Runtime needs pointer masks to be a multiple of uintptr in size. + n = (n + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) + mask := make([]byte, n) emitGCMask(mask, 0, typ, array.len) array.gcdata = &mask[0] @@ -2950,8 +2991,8 @@ func ArrayOf(length int, elem Type) Type { prog := []byte{0, 0, 0, 0} // will be length of prog prog = appendGCProg(prog, typ) // Pad from ptrdata to size. - elemPtrs := typ.ptrdata / ptrSize - elemWords := typ.size / ptrSize + elemPtrs := typ.ptrdata / goarch.PtrSize + elemWords := typ.size / goarch.PtrSize if elemPtrs < elemWords { // Emit literal 0 bit, then repeat as needed. prog = append(prog, 0x01, 0x00) @@ -3033,7 +3074,7 @@ type layoutKey struct { type layoutType struct { t *rtype framePool *sync.Pool - abi abiDesc + abid abiDesc } var layoutCache sync.Map // map[layoutKey]layoutType @@ -3045,7 +3086,7 @@ var layoutCache sync.Map // map[layoutKey]layoutType // The returned type exists only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in // the name for possible debugging use. -func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Pool, abi abiDesc) { +func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Pool, abid abiDesc) { if t.Kind() != Func { panic("reflect: funcLayout of non-func type " + t.String()) } @@ -3055,24 +3096,24 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo k := layoutKey{t, rcvr} if lti, ok := layoutCache.Load(k); ok { lt := lti.(layoutType) - return lt.t, lt.framePool, lt.abi + return lt.t, lt.framePool, lt.abid } // Compute the ABI layout. - abi = newAbiDesc(t, rcvr) + abid = newAbiDesc(t, rcvr) // build dummy rtype holding gc program x := &rtype{ - align: ptrSize, + align: goarch.PtrSize, // Don't add spill space here; it's only necessary in // reflectcall's frame, not in the allocated frame. // TODO(mknyszek): Remove this comment when register // spill space in the frame is no longer required. - size: align(abi.retOffset+abi.ret.stackBytes, ptrSize), - ptrdata: uintptr(abi.stackPtrs.n) * ptrSize, + size: align(abid.retOffset+abid.ret.stackBytes, goarch.PtrSize), + ptrdata: uintptr(abid.stackPtrs.n) * goarch.PtrSize, } - if abi.stackPtrs.n > 0 { - x.gcdata = &abi.stackPtrs.data[0] + if abid.stackPtrs.n > 0 { + x.gcdata = &abid.stackPtrs.data[0] } var s string @@ -3081,19 +3122,19 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo } else { s = "funcargs(" + t.String() + ")" } - x.str = resolveReflectName(newName(s, "", false)) + x.str = resolveReflectName(newName(s, "", false, false)) // cache result for future callers - framePool = &sync.Pool{New: func() interface{} { + framePool = &sync.Pool{New: func() any { return unsafe_New(x) }} lti, _ := layoutCache.LoadOrStore(k, layoutType{ t: x, framePool: framePool, - abi: abi, + abid: abid, }) lt := lti.(layoutType) - return lt.t, lt.framePool, lt.abi + return lt.t, lt.framePool, lt.abid } // ifaceIndir reports whether t is stored indirectly in an interface value. @@ -3109,8 +3150,13 @@ type bitVector struct { // append a bit to the bitmap. func (bv *bitVector) append(bit uint8) { - if bv.n%8 == 0 { - bv.data = append(bv.data, 0) + if bv.n%(8*goarch.PtrSize) == 0 { + // Runtime needs pointer masks to be a multiple of uintptr in size. + // Since reflect passes bv.data directly to the runtime as a pointer mask, + // we append a full uintptr of zeros at a time. + for i := 0; i < goarch.PtrSize; i++ { + bv.data = append(bv.data, 0) + } } bv.data[bv.n/8] |= bit << (bv.n % 8) bv.n++ @@ -3122,16 +3168,16 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { } switch Kind(t.kind & kindMask) { - case Chan, Func, Map, Ptr, Slice, String, UnsafePointer: + case Chan, Func, Map, Pointer, Slice, String, UnsafePointer: // 1 pointer at start of representation - for bv.n < uint32(offset/uintptr(ptrSize)) { + for bv.n < uint32(offset/uintptr(goarch.PtrSize)) { bv.append(0) } bv.append(1) case Interface: // 2 pointers - for bv.n < uint32(offset/uintptr(ptrSize)) { + for bv.n < uint32(offset/uintptr(goarch.PtrSize)) { bv.append(0) } bv.append(1) @@ -3149,7 +3195,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { tt := (*structType)(unsafe.Pointer(t)) for i := range tt.fields { f := &tt.fields[i] - addTypeBits(bv, offset+f.offset(), f.typ) + addTypeBits(bv, offset+f.offset, f.typ) } } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 6f878eba5b0411..4e5d3977ec8e41 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -5,7 +5,9 @@ package reflect import ( + "errors" "internal/abi" + "internal/goarch" "internal/itoa" "internal/unsafeheader" "math" @@ -13,8 +15,6 @@ import ( "unsafe" ) -const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const - // Value is the reflection interface to a Go value. // // Not all methods apply to all kinds of values. Restrictions, @@ -45,17 +45,19 @@ type Value struct { ptr unsafe.Pointer // flag holds metadata about the value. - // The lowest bits are flag bits: + // + // The lowest five bits give the Kind of the value, mirroring typ.Kind(). + // + // The next set of bits are flag bits: // - flagStickyRO: obtained via unexported not embedded field, so read-only // - flagEmbedRO: obtained via unexported embedded field, so read-only // - flagIndir: val holds a pointer to the data - // - flagAddr: v.CanAddr is true (implies flagIndir) + // - flagAddr: v.CanAddr is true (implies flagIndir and ptr is non-nil) // - flagMethod: v is a method value. - // The next five bits give the Kind of the value. - // This repeats typ.Kind() except for method values. - // The remaining 23+ bits give a method number for method values. - // If flag.kind() != Func, code can assume that flagMethod is unset. // If ifaceIndir(typ), code can assume that flagIndir is set. + // + // The remaining 22+ bits give a method number for method values. + // If flag.kind() != Func, code can assume that flagMethod is unset. flag // A method value represents a curried method invocation @@ -91,10 +93,10 @@ func (f flag) ro() flag { } // pointer returns the underlying pointer represented by v. -// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer -// if v.Kind() == Ptr, the base type must not be go:notinheap. +// v.Kind() must be Pointer, Map, Chan, Func, or UnsafePointer +// if v.Kind() == Pointer, the base type must not be not-in-heap. func (v Value) pointer() unsafe.Pointer { - if v.typ.size != ptrSize || !v.typ.pointers() { + if v.typ.size != goarch.PtrSize || !v.typ.pointers() { panic("can't call pointer on a non-pointer Value") } if v.flag&flagIndir != 0 { @@ -104,9 +106,9 @@ func (v Value) pointer() unsafe.Pointer { } // packEface converts v to the empty interface. -func packEface(v Value) interface{} { +func packEface(v Value) any { t := v.typ - var i interface{} + var i any e := (*emptyInterface)(unsafe.Pointer(&i)) // First, fill in the data portion of the interface. switch { @@ -141,7 +143,7 @@ func packEface(v Value) interface{} { } // unpackEface converts the empty interface i to a Value. -func unpackEface(i interface{}) Value { +func unpackEface(i any) Value { e := (*emptyInterface)(unsafe.Pointer(&i)) // NOTE: don't read e.word until we know whether it is really a pointer or not. t := e.typ @@ -170,26 +172,24 @@ func (e *ValueError) Error() string { return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" } -// methodName returns the name of the calling method, -// assumed to be two stack frames above. -func methodName() string { - pc, _, _, _ := runtime.Caller(2) - f := runtime.FuncForPC(pc) - if f == nil { - return "unknown method" - } - return f.Name() -} - -// methodNameSkip is like methodName, but skips another stack frame. -// This is a separate function so that reflect.flag.mustBe will be inlined. -func methodNameSkip() string { - pc, _, _, _ := runtime.Caller(3) - f := runtime.FuncForPC(pc) - if f == nil { - return "unknown method" +// valueMethodName returns the name of the exported calling method on Value. +func valueMethodName() string { + var pc [5]uintptr + n := runtime.Callers(1, pc[:]) + frames := runtime.CallersFrames(pc[:n]) + var frame runtime.Frame + for more := true; more; { + const prefix = "reflect.Value." + frame, more = frames.Next() + name := frame.Function + if len(name) > len(prefix) && name[:len(prefix)] == prefix { + methodName := name[len(prefix):] + if len(methodName) > 0 && 'A' <= methodName[0] && methodName[0] <= 'Z' { + return name + } + } } - return f.Name() + return "unknown method" } // emptyInterface is the header for an interface{} value. @@ -220,7 +220,7 @@ type nonEmptyInterface struct { func (f flag) mustBe(expected Kind) { // TODO(mvdan): use f.kind() again once mid-stack inlining gets better if Kind(f&flagKindMask) != expected { - panic(&ValueError{methodName(), f.kind()}) + panic(&ValueError{valueMethodName(), f.kind()}) } } @@ -234,10 +234,10 @@ func (f flag) mustBeExported() { func (f flag) mustBeExportedSlow() { if f == 0 { - panic(&ValueError{methodNameSkip(), Invalid}) + panic(&ValueError{valueMethodName(), Invalid}) } if f&flagRO != 0 { - panic("reflect: " + methodNameSkip() + " using value obtained using unexported field") + panic("reflect: " + valueMethodName() + " using value obtained using unexported field") } } @@ -252,14 +252,14 @@ func (f flag) mustBeAssignable() { func (f flag) mustBeAssignableSlow() { if f == 0 { - panic(&ValueError{methodNameSkip(), Invalid}) + panic(&ValueError{valueMethodName(), Invalid}) } // Assignable if addressable and not read-only. if f&flagRO != 0 { - panic("reflect: " + methodNameSkip() + " using value obtained using unexported field") + panic("reflect: " + valueMethodName() + " using value obtained using unexported field") } if f&flagAddr == 0 { - panic("reflect: " + methodNameSkip() + " using unaddressable value") + panic("reflect: " + valueMethodName() + " using unaddressable value") } } @@ -275,25 +275,56 @@ func (v Value) Addr() Value { // Preserve flagRO instead of using v.flag.ro() so that // v.Addr().Elem() is equivalent to v (#32772) fl := v.flag & flagRO - return Value{v.typ.ptrTo(), v.ptr, fl | flag(Ptr)} + return Value{v.typ.ptrTo(), v.ptr, fl | flag(Pointer)} } // Bool returns v's underlying value. // It panics if v's kind is not Bool. func (v Value) Bool() bool { - v.mustBe(Bool) + // panicNotBool is split out to keep Bool inlineable. + if v.kind() != Bool { + v.panicNotBool() + } return *(*bool)(v.ptr) } +func (v Value) panicNotBool() { + v.mustBe(Bool) +} + +var bytesType = rtypeOf(([]byte)(nil)) + // Bytes returns v's underlying value. -// It panics if v's underlying value is not a slice of bytes. +// It panics if v's underlying value is not a slice of bytes or +// an addressable array of bytes. func (v Value) Bytes() []byte { - v.mustBe(Slice) - if v.typ.Elem().Kind() != Uint8 { - panic("reflect.Value.Bytes of non-byte slice") + // bytesSlow is split out to keep Bytes inlineable for unnamed []byte. + if v.typ == bytesType { + return *(*[]byte)(v.ptr) } - // Slice is always bigger than a word; assume flagIndir. - return *(*[]byte)(v.ptr) + return v.bytesSlow() +} + +func (v Value) bytesSlow() []byte { + switch v.kind() { + case Slice: + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.Bytes of non-byte slice") + } + // Slice is always bigger than a word; assume flagIndir. + return *(*[]byte)(v.ptr) + case Array: + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.Bytes of non-byte array") + } + if !v.CanAddr() { + panic("reflect.Value.Bytes of unaddressable byte array") + } + p := (*byte)(v.ptr) + n := int((*arrayType)(unsafe.Pointer(v.typ)).len) + return unsafe.Slice(p, n) + } + panic(&ValueError{"reflect.Value.Bytes", v.kind()}) } // runes returns v's underlying value. @@ -352,7 +383,7 @@ func (v Value) CallSlice(in []Value) []Value { return v.call("CallSlice", in) } -var callGC bool // for testing; see TestCallMethodJump +var callGC bool // for testing; see TestCallMethodJump and TestCallArgLive const debugReflectCall = false @@ -439,7 +470,7 @@ func (v Value) call(op string, in []Value) []Value { var regArgs abi.RegArgs // Compute frame type. - frametype, framePool, abi := funcLayout(t, rcvrtype) + frametype, framePool, abid := funcLayout(t, rcvrtype) // Allocate a chunk of memory for frame if needed. var stackArgs unsafe.Pointer @@ -456,7 +487,7 @@ func (v Value) call(op string, in []Value) []Value { if debugReflectCall { println("reflect.call", t.String()) - abi.dump() + abid.dump() } // Copy inputs into args. @@ -467,14 +498,13 @@ func (v Value) call(op string, in []Value) []Value { // Guaranteed to only be one word in size, // so it will only take up exactly 1 abiStep (either // in a register or on the stack). - switch st := abi.call.steps[0]; st.kind { + switch st := abid.call.steps[0]; st.kind { case abiStepStack: storeRcvr(rcvr, stackArgs) - case abiStepIntReg, abiStepPointer: - // Even pointers can go into the uintptr slot because - // they'll be kept alive by the Values referenced by - // this frame. Reflection forces these to be heap-allocated, - // so we don't need to worry about stack copying. + case abiStepPointer: + storeRcvr(rcvr, unsafe.Pointer(®Args.Ptrs[st.ireg])) + fallthrough + case abiStepIntReg: storeRcvr(rcvr, unsafe.Pointer(®Args.Ints[st.ireg])) case abiStepFloatReg: storeRcvr(rcvr, unsafe.Pointer(®Args.Floats[st.freg])) @@ -493,7 +523,7 @@ func (v Value) call(op string, in []Value) []Value { // was possible to use space in the argument frame. v = v.assignTo("reflect.Value.Call", targ, nil) stepsLoop: - for _, st := range abi.call.stepsForValue(i + inStart) { + for _, st := range abid.call.stepsForValue(i + inStart) { switch st.kind { case abiStepStack: // Copy values to the "stack." @@ -509,12 +539,16 @@ func (v Value) call(op string, in []Value) []Value { // Copy values to "integer registers." if v.flag&flagIndir != 0 { offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(unsafe.Pointer(®Args.Ints[st.ireg]), offset, st.size) - } else { if st.kind == abiStepPointer { // Duplicate this pointer in the pointer area of the // register space. Otherwise, there's the potential for // this to be the last reference to v.ptr. + regArgs.Ptrs[st.ireg] = *(*unsafe.Pointer)(offset) + } + intToReg(®Args, st.ireg, st.size, offset) + } else { + if st.kind == abiStepPointer { + // See the comment in abiStepPointer case above. regArgs.Ptrs[st.ireg] = v.ptr } regArgs.Ints[st.ireg] = uintptr(v.ptr) @@ -525,7 +559,7 @@ func (v Value) call(op string, in []Value) []Value { panic("attempted to copy pointer to FP register") } offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(unsafe.Pointer(®Args.Floats[st.freg]), offset, st.size) + floatToReg(®Args, st.freg, st.size, offset) default: panic("unknown ABI part kind") } @@ -533,14 +567,23 @@ func (v Value) call(op string, in []Value) []Value { } // TODO(mknyszek): Remove this when we no longer have // caller reserved spill space. - frameSize = align(frameSize, ptrSize) - frameSize += abi.spill + frameSize = align(frameSize, goarch.PtrSize) + frameSize += abid.spill // Mark pointers in registers for the return path. - regArgs.ReturnIsPtr = abi.outRegPtrs + regArgs.ReturnIsPtr = abid.outRegPtrs + + if debugReflectCall { + regArgs.Dump() + } + + // For testing; see TestCallArgLive. + if callGC { + runtime.GC() + } // Call. - call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abi.retOffset), uint32(frameSize), ®Args) + call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abid.retOffset), uint32(frameSize), ®Args) // For testing; see TestCallMethodJump. if callGC { @@ -558,7 +601,7 @@ func (v Value) call(op string, in []Value) []Value { // Zero the now unused input area of args, // because the Values returned by this function contain pointers to the args object, // and will thus keep the args object alive indefinitely. - typedmemclrpartial(frametype, stackArgs, 0, abi.retOffset) + typedmemclrpartial(frametype, stackArgs, 0, abid.retOffset) } // Wrap Values around return values in args. @@ -571,7 +614,7 @@ func (v Value) call(op string, in []Value) []Value { ret[i] = Zero(tv) continue } - steps := abi.ret.stepsForValue(i) + steps := abid.ret.stepsForValue(i) if st := steps[0]; st.kind == abiStepStack { // This value is on the stack. If part of a value is stack // allocated, the entire value is according to the ABI. So @@ -611,13 +654,13 @@ func (v Value) call(op string, in []Value) []Value { switch st.kind { case abiStepIntReg: offset := add(s, st.offset, "precomputed value offset") - memmove(offset, unsafe.Pointer(®Args.Ints[st.ireg]), st.size) + intFromReg(®Args, st.ireg, st.size, offset) case abiStepPointer: s := add(s, st.offset, "precomputed value offset") *((*unsafe.Pointer)(s)) = regArgs.Ptrs[st.ireg] case abiStepFloatReg: offset := add(s, st.offset, "precomputed value offset") - memmove(offset, unsafe.Pointer(®Args.Floats[st.freg]), st.size) + floatFromReg(®Args, st.freg, st.size, offset) case abiStepStack: panic("register-based return value has stack component") default: @@ -663,7 +706,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs ftyp := ctxt.ftyp f := ctxt.fn - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) // Copy arguments into Values. ptr := frame @@ -674,7 +717,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs continue } v := Value{typ, nil, flag(typ.Kind())} - steps := abi.call.stepsForValue(i) + steps := abid.call.stepsForValue(i) if st := steps[0]; st.kind == abiStepStack { if ifaceIndir(typ) { // value cannot be inlined in interface data. @@ -699,13 +742,13 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs switch st.kind { case abiStepIntReg: offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(offset, unsafe.Pointer(®s.Ints[st.ireg]), st.size) + intFromReg(regs, st.ireg, st.size, offset) case abiStepPointer: s := add(v.ptr, st.offset, "precomputed value offset") *((*unsafe.Pointer)(s)) = regs.Ptrs[st.ireg] case abiStepFloatReg: offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(offset, unsafe.Pointer(®s.Floats[st.freg]), st.size) + floatFromReg(regs, st.freg, st.size, offset) case abiStepStack: panic("register-based return value has stack component") default: @@ -764,7 +807,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs // target location used as scratch space. See issue 39541. v = v.assignTo("reflect.MakeFunc", typ, nil) stepsLoop: - for _, st := range abi.ret.stepsForValue(i) { + for _, st := range abid.ret.stepsForValue(i) { switch st.kind { case abiStepStack: // Copy values to the "stack." @@ -785,7 +828,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs // Copy values to "integer registers." if v.flag&flagIndir != 0 { offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(unsafe.Pointer(®s.Ints[st.ireg]), offset, st.size) + intToReg(regs, st.ireg, st.size, offset) } else { // Only populate the Ints space on the return path. // This is safe because out is kept alive until the @@ -800,7 +843,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs panic("attempted to copy pointer to FP register") } offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(unsafe.Pointer(®s.Floats[st.freg]), offset, st.size) + floatToReg(regs, st.freg, st.size, offset) default: panic("unknown ABI part kind") } @@ -930,13 +973,21 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a var methodRegs abi.RegArgs // Deal with the receiver. It's guaranteed to only be one word in size. - if st := methodABI.call.steps[0]; st.kind == abiStepStack { - // Only copy the reciever to the stack if the ABI says so. + switch st := methodABI.call.steps[0]; st.kind { + case abiStepStack: + // Only copy the receiver to the stack if the ABI says so. // Otherwise, it'll be in a register already. storeRcvr(rcvr, methodFrame) - } else { + case abiStepPointer: // Put the receiver in a register. - storeRcvr(rcvr, unsafe.Pointer(&methodRegs.Ints)) + storeRcvr(rcvr, unsafe.Pointer(&methodRegs.Ptrs[st.ireg])) + fallthrough + case abiStepIntReg: + storeRcvr(rcvr, unsafe.Pointer(&methodRegs.Ints[st.ireg])) + case abiStepFloatReg: + storeRcvr(rcvr, unsafe.Pointer(&methodRegs.Floats[st.freg])) + default: + panic("unknown ABI parameter kind") } // Translate the rest of the arguments. @@ -958,9 +1009,6 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a // 2. Stack -> registers translation. // 3. Registers -> stack translation. // 4. Registers -> registers translation. - // TODO(mknyszek): Cases 2 and 3 below only work on little endian - // architectures. This is OK for now, but this needs to be fixed - // before supporting the register ABI on big endian architectures. // If the value ABI passes the value on the stack, // then the method ABI does too, because it has strictly @@ -986,9 +1034,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a methodRegs.Ptrs[mStep.ireg] = *(*unsafe.Pointer)(from) fallthrough // We need to make sure this ends up in Ints, too. case abiStepIntReg: - memmove(unsafe.Pointer(&methodRegs.Ints[mStep.ireg]), from, mStep.size) + intToReg(&methodRegs, mStep.ireg, mStep.size, from) case abiStepFloatReg: - memmove(unsafe.Pointer(&methodRegs.Floats[mStep.freg]), from, mStep.size) + floatToReg(&methodRegs, mStep.freg, mStep.size, from) default: panic("unexpected method step") } @@ -1004,9 +1052,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a // Do the pointer copy directly so we get a write barrier. *(*unsafe.Pointer)(to) = valueRegs.Ptrs[vStep.ireg] case abiStepIntReg: - memmove(to, unsafe.Pointer(&valueRegs.Ints[vStep.ireg]), vStep.size) + intFromReg(valueRegs, vStep.ireg, vStep.size, to) case abiStepFloatReg: - memmove(to, unsafe.Pointer(&valueRegs.Floats[vStep.freg]), vStep.size) + floatFromReg(valueRegs, vStep.freg, vStep.size, to) default: panic("unexpected value step") } @@ -1043,7 +1091,7 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a methodFrameSize := methodFrameType.size // TODO(mknyszek): Remove this when we no longer have // caller reserved spill space. - methodFrameSize = align(methodFrameSize, ptrSize) + methodFrameSize = align(methodFrameSize, goarch.PtrSize) methodFrameSize += methodABI.spill // Mark pointers in registers for the return path. @@ -1103,17 +1151,27 @@ func funcName(f func([]Value) []Value) string { } // Cap returns v's capacity. -// It panics if v's Kind is not Array, Chan, or Slice. +// It panics if v's Kind is not Array, Chan, Slice or pointer to Array. func (v Value) Cap() int { + // capNonSlice is split out to keep Cap inlineable for slice kinds. + if v.kind() == Slice { + return (*unsafeheader.Slice)(v.ptr).Cap + } + return v.capNonSlice() +} + +func (v Value) capNonSlice() int { k := v.kind() switch k { case Array: return v.typ.Len() case Chan: return chancap(v.pointer()) - case Slice: - // Slice is always bigger than a word; assume flagIndir. - return (*unsafeheader.Slice)(v.ptr).Cap + case Ptr: + if v.typ.Elem().Kind() == Array { + return v.typ.Elem().Len() + } + panic("reflect: call of reflect.Value.Cap on ptr to non-array Value") } panic(&ValueError{"reflect.Value.Cap", v.kind()}) } @@ -1126,6 +1184,16 @@ func (v Value) Close() { chanclose(v.pointer()) } +// CanComplex reports whether Complex can be used without panicking. +func (v Value) CanComplex() bool { + switch v.kind() { + case Complex64, Complex128: + return true + default: + return false + } +} + // Complex returns v's underlying value, as a complex128. // It panics if v's Kind is not Complex64 or Complex128 func (v Value) Complex() complex128 { @@ -1141,17 +1209,17 @@ func (v Value) Complex() complex128 { // Elem returns the value that the interface v contains // or that the pointer v points to. -// It panics if v's Kind is not Interface or Ptr. +// It panics if v's Kind is not Interface or Pointer. // It returns the zero Value if v is nil. func (v Value) Elem() Value { k := v.kind() switch k { case Interface: - var eface interface{} + var eface any if v.typ.NumMethod() == 0 { - eface = *(*interface{})(v.ptr) + eface = *(*any)(v.ptr) } else { - eface = (interface{})(*(*interface { + eface = (any)(*(*interface { M() })(v.ptr)) } @@ -1160,9 +1228,24 @@ func (v Value) Elem() Value { x.flag |= v.flag.ro() } return x - case Ptr: + case Pointer: ptr := v.ptr if v.flag&flagIndir != 0 { + if ifaceIndir(v.typ) { + // This is a pointer to a not-in-heap object. ptr points to a uintptr + // in the heap. That uintptr is the address of a not-in-heap object. + // In general, pointers to not-in-heap objects can be total junk. + // But Elem() is asking to dereference it, so the user has asserted + // that at least it is a valid pointer (not just an integer stored in + // a pointer slot). So let's check, to make sure that it isn't a pointer + // that the runtime will crash on if it sees it during GC or write barriers. + // Since it is a not-in-heap pointer, all pointers to the heap are + // forbidden! That makes the test pretty easy. + // See issue 48399. + if !verifyNotInHeapPtr(*(*uintptr)(ptr)) { + panic("reflect: reflect.Value.Elem on an invalid notinheap pointer") + } + } ptr = *(*unsafe.Pointer)(ptr) } // The returned value's address is v's value. @@ -1206,12 +1289,13 @@ func (v Value) Field(i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } // FieldByIndex returns the nested field corresponding to index. -// It panics if v's Kind is not struct. +// It panics if evaluation requires stepping through a nil +// pointer or a field that is not a struct. func (v Value) FieldByIndex(index []int) Value { if len(index) == 1 { return v.Field(index[0]) @@ -1219,7 +1303,7 @@ func (v Value) FieldByIndex(index []int) Value { v.mustBe(Struct) for i, x := range index { if i > 0 { - if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct { + if v.Kind() == Pointer && v.typ.Elem().Kind() == Struct { if v.IsNil() { panic("reflect: indirection through nil pointer to embedded struct") } @@ -1231,6 +1315,29 @@ func (v Value) FieldByIndex(index []int) Value { return v } +// FieldByIndexErr returns the nested field corresponding to index. +// It returns an error if evaluation requires stepping through a nil +// pointer, but panics if it must step through a field that +// is not a struct. +func (v Value) FieldByIndexErr(index []int) (Value, error) { + if len(index) == 1 { + return v.Field(index[0]), nil + } + v.mustBe(Struct) + for i, x := range index { + if i > 0 { + if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct { + if v.IsNil() { + return Value{}, errors.New("reflect: indirection through nil pointer to embedded struct field " + v.typ.Elem().Name()) + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v, nil +} + // FieldByName returns the struct field with the given name. // It returns the zero Value if no field was found. // It panics if v's Kind is not struct. @@ -1253,6 +1360,16 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value { return Value{} } +// CanFloat reports whether Float can be used without panicking. +func (v Value) CanFloat() bool { + switch v.kind() { + case Float32, Float64: + return true + default: + return false + } +} + // Float returns v's underlying value, as a float64. // It panics if v's Kind is not Float32 or Float64 func (v Value) Float() float64 { @@ -1266,7 +1383,7 @@ func (v Value) Float() float64 { panic(&ValueError{"reflect.Value.Float", v.kind()}) } -var uint8Type = TypeOf(uint8(0)).(*rtype) +var uint8Type = rtypeOf(uint8(0)) // Index returns v's i'th element. // It panics if v's Kind is not Array, Slice, or String or i is out of range. @@ -1290,7 +1407,7 @@ func (v Value) Index(i int) Value { return Value{typ, val, fl} case Slice: - // Element flag same as Elem of Ptr. + // Element flag same as Elem of Pointer. // Addressable, indirect, possibly read-only. s := (*unsafeheader.Slice)(v.ptr) if uint(i) >= uint(s.Len) { @@ -1314,6 +1431,16 @@ func (v Value) Index(i int) Value { panic(&ValueError{"reflect.Value.Index", v.kind()}) } +// CanInt reports whether Int can be used without panicking. +func (v Value) CanInt() bool { + switch v.kind() { + case Int, Int8, Int16, Int32, Int64: + return true + default: + return false + } +} + // Int returns v's underlying value, as an int64. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) Int() int64 { @@ -1344,14 +1471,16 @@ func (v Value) CanInterface() bool { // Interface returns v's current value as an interface{}. // It is equivalent to: +// // var i interface{} = (v's underlying value) +// // It panics if the Value was obtained by accessing // unexported struct fields. -func (v Value) Interface() (i interface{}) { +func (v Value) Interface() (i any) { return valueInterface(v, true) } -func valueInterface(v Value, safe bool) interface{} { +func valueInterface(v Value, safe bool) any { if v.flag == 0 { panic(&ValueError{"reflect.Value.Interface", Invalid}) } @@ -1370,7 +1499,7 @@ func valueInterface(v Value, safe bool) interface{} { // Empty interface has one layout, all interfaces with // methods have a second layout. if v.NumMethod() == 0 { - return *(*interface{})(v.ptr) + return *(*any)(v.ptr) } return *(*interface { M() @@ -1410,7 +1539,7 @@ func (v Value) InterfaceData() [2]uintptr { func (v Value) IsNil() bool { k := v.kind() switch k { - case Chan, Func, Map, Ptr, UnsafePointer: + case Chan, Func, Map, Pointer, UnsafePointer: if v.flag&flagMethod != 0 { return false } @@ -1452,30 +1581,102 @@ func (v Value) IsZero() bool { c := v.Complex() return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 case Array: - for i := 0; i < v.Len(); i++ { + // If the type is comparable, then compare directly with zero. + if v.typ.equal != nil && v.typ.size <= maxZero { + if v.flag&flagIndir == 0 { + return v.ptr == nil + } + return v.typ.equal(v.ptr, unsafe.Pointer(&zeroVal[0])) + } + + n := v.Len() + for i := 0; i < n; i++ { if !v.Index(i).IsZero() { return false } } return true - case Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer: + case Chan, Func, Interface, Map, Pointer, Slice, UnsafePointer: return v.IsNil() case String: return v.Len() == 0 case Struct: - for i := 0; i < v.NumField(); i++ { + // If the type is comparable, then compare directly with zero. + if v.typ.equal != nil && v.typ.size <= maxZero { + if v.flag&flagIndir == 0 { + return v.ptr == nil + } + return v.typ.equal(v.ptr, unsafe.Pointer(&zeroVal[0])) + } + + n := v.NumField() + for i := 0; i < n; i++ { if !v.Field(i).IsZero() { return false } } return true default: - // This should never happens, but will act as a safeguard for - // later, as a default value doesn't makes sense here. + // This should never happen, but will act as a safeguard for later, + // as a default value doesn't makes sense here. panic(&ValueError{"reflect.Value.IsZero", v.Kind()}) } } +// SetZero sets v to be the zero value of v's type. +// It panics if CanSet returns false. +func (v Value) SetZero() { + v.mustBeAssignable() + switch v.kind() { + case Bool: + *(*bool)(v.ptr) = false + case Int: + *(*int)(v.ptr) = 0 + case Int8: + *(*int8)(v.ptr) = 0 + case Int16: + *(*int16)(v.ptr) = 0 + case Int32: + *(*int32)(v.ptr) = 0 + case Int64: + *(*int64)(v.ptr) = 0 + case Uint: + *(*uint)(v.ptr) = 0 + case Uint8: + *(*uint8)(v.ptr) = 0 + case Uint16: + *(*uint16)(v.ptr) = 0 + case Uint32: + *(*uint32)(v.ptr) = 0 + case Uint64: + *(*uint64)(v.ptr) = 0 + case Uintptr: + *(*uintptr)(v.ptr) = 0 + case Float32: + *(*float32)(v.ptr) = 0 + case Float64: + *(*float64)(v.ptr) = 0 + case Complex64: + *(*complex64)(v.ptr) = 0 + case Complex128: + *(*complex128)(v.ptr) = 0 + case String: + *(*string)(v.ptr) = "" + case Slice: + *(*unsafeheader.Slice)(v.ptr) = unsafeheader.Slice{} + case Interface: + *(*[2]unsafe.Pointer)(v.ptr) = [2]unsafe.Pointer{} + case Chan, Func, Map, Pointer, UnsafePointer: + *(*unsafe.Pointer)(v.ptr) = nil + case Array, Struct: + typedmemclr(v.typ, v.ptr) + default: + // This should never happen, but will act as a safeguard for later, + // as a default value doesn't makes sense here. + panic(&ValueError{"reflect.Value.SetZero", v.Kind()}) + } +} + // Kind returns v's Kind. // If v is the zero Value (IsValid returns false), Kind returns Invalid. func (v Value) Kind() Kind { @@ -1483,10 +1684,17 @@ func (v Value) Kind() Kind { } // Len returns v's length. -// It panics if v's Kind is not Array, Chan, Map, Slice, or String. +// It panics if v's Kind is not Array, Chan, Map, Slice, String, or pointer to Array. func (v Value) Len() int { - k := v.kind() - switch k { + // lenNonSlice is split out to keep Len inlineable for slice kinds. + if v.kind() == Slice { + return (*unsafeheader.Slice)(v.ptr).Len + } + return v.lenNonSlice() +} + +func (v Value) lenNonSlice() int { + switch k := v.kind(); k { case Array: tt := (*arrayType)(unsafe.Pointer(v.typ)) return int(tt.len) @@ -1494,16 +1702,20 @@ func (v Value) Len() int { return chanlen(v.pointer()) case Map: return maplen(v.pointer()) - case Slice: - // Slice is bigger than a word; assume flagIndir. - return (*unsafeheader.Slice)(v.ptr).Len case String: // String is bigger than a word; assume flagIndir. return (*unsafeheader.String)(v.ptr).Len + case Ptr: + if v.typ.Elem().Kind() == Array { + return v.typ.Elem().Len() + } + panic("reflect: call of reflect.Value.Len on ptr to non-array Value") } panic(&ValueError{"reflect.Value.Len", v.kind()}) } +var stringType = rtypeOf("") + // MapIndex returns the value associated with key in the map v. // It panics if v's Kind is not Map. // It returns the zero Value if key is not found in the map or if v represents a nil map. @@ -1519,15 +1731,21 @@ func (v Value) MapIndex(key Value) Value { // considered unexported. This is consistent with the // behavior for structs, which allow read but not write // of unexported fields. - key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) - var k unsafe.Pointer - if key.flag&flagIndir != 0 { - k = key.ptr + var e unsafe.Pointer + if (tt.key == stringType || key.kind() == String) && tt.key == key.typ && tt.elem.size <= maxValSize { + k := *(*string)(key.ptr) + e = mapaccess_faststr(v.typ, v.pointer(), k) } else { - k = unsafe.Pointer(&key.ptr) + key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) + var k unsafe.Pointer + if key.flag&flagIndir != 0 { + k = key.ptr + } else { + k = unsafe.Pointer(&key.ptr) + } + e = mapaccess(v.typ, v.pointer(), k) } - e := mapaccess(v.typ, v.pointer(), k) if e == nil { return Value{} } @@ -1553,11 +1771,12 @@ func (v Value) MapKeys() []Value { if m != nil { mlen = maplen(m) } - it := mapiterinit(v.typ, m) + var it hiter + mapiterinit(v.typ, m, &it) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { - key := mapiterkey(it) + key := mapiterkey(&it) if key == nil { // Someone deleted an entry from the map since we // called maplen above. It's a data race, but nothing @@ -1565,59 +1784,158 @@ func (v Value) MapKeys() []Value { break } a[i] = copyVal(keyType, fl, key) - mapiternext(it) + mapiternext(&it) } return a[:i] } +// hiter's structure matches runtime.hiter's structure. +// Having a clone here allows us to embed a map iterator +// inside type MapIter so that MapIters can be re-used +// without doing any allocations. +type hiter struct { + key unsafe.Pointer + elem unsafe.Pointer + t unsafe.Pointer + h unsafe.Pointer + buckets unsafe.Pointer + bptr unsafe.Pointer + overflow *[]unsafe.Pointer + oldoverflow *[]unsafe.Pointer + startBucket uintptr + offset uint8 + wrapped bool + B uint8 + i uint8 + bucket uintptr + checkBucket uintptr +} + +func (h *hiter) initialized() bool { + return h.t != nil +} + // A MapIter is an iterator for ranging over a map. // See Value.MapRange. type MapIter struct { - m Value - it unsafe.Pointer + m Value + hiter hiter } -// Key returns the key of the iterator's current map entry. -func (it *MapIter) Key() Value { - if it.it == nil { +// Key returns the key of iter's current map entry. +func (iter *MapIter) Key() Value { + if !iter.hiter.initialized() { panic("MapIter.Key called before Next") } - if mapiterkey(it.it) == nil { + iterkey := mapiterkey(&iter.hiter) + if iterkey == nil { panic("MapIter.Key called on exhausted iterator") } - t := (*mapType)(unsafe.Pointer(it.m.typ)) + t := (*mapType)(unsafe.Pointer(iter.m.typ)) + ktype := t.key + return copyVal(ktype, iter.m.flag.ro()|flag(ktype.Kind()), iterkey) +} + +// SetIterKey assigns to v the key of iter's current map entry. +// It is equivalent to v.Set(iter.Key()), but it avoids allocating a new Value. +// As in Go, the key must be assignable to v's type and +// must not be derived from an unexported field. +func (v Value) SetIterKey(iter *MapIter) { + if !iter.hiter.initialized() { + panic("reflect: Value.SetIterKey called before Next") + } + iterkey := mapiterkey(&iter.hiter) + if iterkey == nil { + panic("reflect: Value.SetIterKey called on exhausted iterator") + } + + v.mustBeAssignable() + var target unsafe.Pointer + if v.kind() == Interface { + target = v.ptr + } + + t := (*mapType)(unsafe.Pointer(iter.m.typ)) ktype := t.key - return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it)) + + iter.m.mustBeExported() // do not let unexported m leak + key := Value{ktype, iterkey, iter.m.flag | flag(ktype.Kind()) | flagIndir} + key = key.assignTo("reflect.MapIter.SetKey", v.typ, target) + typedmemmove(v.typ, v.ptr, key.ptr) } -// Value returns the value of the iterator's current map entry. -func (it *MapIter) Value() Value { - if it.it == nil { +// Value returns the value of iter's current map entry. +func (iter *MapIter) Value() Value { + if !iter.hiter.initialized() { panic("MapIter.Value called before Next") } - if mapiterkey(it.it) == nil { + iterelem := mapiterelem(&iter.hiter) + if iterelem == nil { panic("MapIter.Value called on exhausted iterator") } - t := (*mapType)(unsafe.Pointer(it.m.typ)) + t := (*mapType)(unsafe.Pointer(iter.m.typ)) vtype := t.elem - return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapiterelem(it.it)) + return copyVal(vtype, iter.m.flag.ro()|flag(vtype.Kind()), iterelem) +} + +// SetIterValue assigns to v the value of iter's current map entry. +// It is equivalent to v.Set(iter.Value()), but it avoids allocating a new Value. +// As in Go, the value must be assignable to v's type and +// must not be derived from an unexported field. +func (v Value) SetIterValue(iter *MapIter) { + if !iter.hiter.initialized() { + panic("reflect: Value.SetIterValue called before Next") + } + iterelem := mapiterelem(&iter.hiter) + if iterelem == nil { + panic("reflect: Value.SetIterValue called on exhausted iterator") + } + + v.mustBeAssignable() + var target unsafe.Pointer + if v.kind() == Interface { + target = v.ptr + } + + t := (*mapType)(unsafe.Pointer(iter.m.typ)) + vtype := t.elem + + iter.m.mustBeExported() // do not let unexported m leak + elem := Value{vtype, iterelem, iter.m.flag | flag(vtype.Kind()) | flagIndir} + elem = elem.assignTo("reflect.MapIter.SetValue", v.typ, target) + typedmemmove(v.typ, v.ptr, elem.ptr) } // Next advances the map iterator and reports whether there is another -// entry. It returns false when the iterator is exhausted; subsequent +// entry. It returns false when iter is exhausted; subsequent // calls to Key, Value, or Next will panic. -func (it *MapIter) Next() bool { - if it.it == nil { - it.it = mapiterinit(it.m.typ, it.m.pointer()) +func (iter *MapIter) Next() bool { + if !iter.m.IsValid() { + panic("MapIter.Next called on an iterator that does not have an associated map Value") + } + if !iter.hiter.initialized() { + mapiterinit(iter.m.typ, iter.m.pointer(), &iter.hiter) } else { - if mapiterkey(it.it) == nil { + if mapiterkey(&iter.hiter) == nil { panic("MapIter.Next called on exhausted iterator") } - mapiternext(it.it) + mapiternext(&iter.hiter) + } + return mapiterkey(&iter.hiter) != nil +} + +// Reset modifies iter to iterate over v. +// It panics if v's Kind is not Map and v is not the zero Value. +// Reset(Value{}) causes iter to not to refer to any map, +// which may allow the previously iterated-over map to be garbage collected. +func (iter *MapIter) Reset(v Value) { + if v.IsValid() { + v.mustBe(Map) } - return mapiterkey(it.it) != nil + iter.m = v + iter.hiter = hiter{} } // MapRange returns a range iterator for a map. @@ -1630,17 +1948,26 @@ func (it *MapIter) Next() bool { // Example: // // iter := reflect.ValueOf(m).MapRange() -// for iter.Next() { +// for iter.Next() { // k := iter.Key() // v := iter.Value() // ... // } -// func (v Value) MapRange() *MapIter { - v.mustBe(Map) + // This is inlinable to take advantage of "function outlining". + // The allocation of MapIter can be stack allocated if the caller + // does not allow it to escape. + // See https://blog.filippo.io/efficient-go-apis-with-the-inliner/ + if v.kind() != Map { + v.panicNotMap() + } return &MapIter{m: v} } +func (f flag) panicNotMap() { + f.mustBe(Map) +} + // copyVal returns a Value containing the map key or value at ptr, // allocating a new variable as needed. func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value { @@ -1674,7 +2001,11 @@ func (v Value) Method(i int) Value { return Value{v.typ, v.ptr, fl} } -// NumMethod returns the number of exported methods in the value's method set. +// NumMethod returns the number of methods in the value's method set. +// +// For a non-interface type, it returns the number of exported methods. +// +// For an interface type, it returns the number of exported and unexported methods. func (v Value) NumMethod() int { if v.typ == nil { panic(&ValueError{"reflect.Value.NumMethod", Invalid}) @@ -1777,10 +2108,7 @@ func (v Value) OverflowUint(x uint64) bool { // and make an exception. // Pointer returns v's value as a uintptr. -// It returns uintptr instead of unsafe.Pointer so that -// code using reflect cannot obtain unsafe.Pointers -// without importing the unsafe package explicitly. -// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer. +// It panics if v's Kind is not Chan, Func, Map, Pointer, Slice, or UnsafePointer. // // If v's Kind is Func, the returned pointer is an underlying // code pointer, but not necessarily enough to identify a @@ -1790,17 +2118,20 @@ func (v Value) OverflowUint(x uint64) bool { // If v's Kind is Slice, the returned pointer is to the first // element of the slice. If the slice is nil the returned value // is 0. If the slice is empty but non-nil the return value is non-zero. +// +// It's preferred to use uintptr(Value.UnsafePointer()) to get the equivalent result. func (v Value) Pointer() uintptr { - // TODO: deprecate k := v.kind() switch k { - case Ptr: + case Pointer: if v.typ.ptrdata == 0 { - // Handle pointers to go:notinheap types directly, - // so we never materialize such pointers as an - // unsafe.Pointer. (Such pointers are always indirect.) - // See issue 42076. - return *(*uintptr)(v.ptr) + val := *(*uintptr)(v.ptr) + // Since it is a not-in-heap pointer, all pointers to the heap are + // forbidden! See comment in Value.Elem and issue #48399. + if !verifyNotInHeapPtr(val) { + panic("reflect: reflect.Value.Pointer on an invalid notinheap pointer") + } + return val } fallthrough case Chan, Map, UnsafePointer: @@ -1813,8 +2144,7 @@ func (v Value) Pointer() uintptr { // created via reflect have the same underlying code pointer, // so their Pointers are equal. The function used here must // match the one used in makeMethodValue. - f := methodValueCall - return **(**uintptr)(unsafe.Pointer(&f)) + return methodValueCallCodePtr() } p := v.pointer() // Non-nil func value points at data block. @@ -1825,7 +2155,7 @@ func (v Value) Pointer() uintptr { return uintptr(p) case Slice: - return (*SliceHeader)(v.ptr).Data + return uintptr((*unsafeheader.Slice)(v.ptr).Data) } panic(&ValueError{"reflect.Value.Pointer", v.kind()}) } @@ -1894,7 +2224,8 @@ func (v Value) send(x Value, nb bool) (selected bool) { // Set assigns x to the value v. // It panics if CanSet returns false. -// As in Go, x's value must be assignable to v's type. +// As in Go, x's value must be assignable to v's type and +// must not be derived from an unexported field. func (v Value) Set(x Value) { v.mustBeAssignable() x.mustBeExported() // do not let unexported x leak @@ -2029,6 +2360,25 @@ func (v Value) SetMapIndex(key, elem Value) { v.mustBeExported() key.mustBeExported() tt := (*mapType)(unsafe.Pointer(v.typ)) + + if (tt.key == stringType || key.kind() == String) && tt.key == key.typ && tt.elem.size <= maxValSize { + k := *(*string)(key.ptr) + if elem.typ == nil { + mapdelete_faststr(v.typ, v.pointer(), k) + return + } + elem.mustBeExported() + elem = elem.assignTo("reflect.Value.SetMapIndex", tt.elem, nil) + var e unsafe.Pointer + if elem.flag&flagIndir != 0 { + e = elem.ptr + } else { + e = unsafe.Pointer(&elem.ptr) + } + mapassign_faststr(v.typ, v.pointer(), k, e) + return + } + key = key.assignTo("reflect.Value.SetMapIndex", tt.key, nil) var k unsafe.Pointer if key.flag&flagIndir != 0 { @@ -2073,7 +2423,7 @@ func (v Value) SetUint(x uint64) { } } -// SetPointer sets the unsafe.Pointer value v to x. +// SetPointer sets the [unsafe.Pointer] value v to x. // It panics if v's Kind is not UnsafePointer. func (v Value) SetPointer(x unsafe.Pointer) { v.mustBeAssignable() @@ -2210,12 +2560,17 @@ func (v Value) Slice3(i, j, k int) Value { // The fmt package treats Values specially. It does not call their String // method implicitly but instead prints the concrete values they hold. func (v Value) String() string { - switch k := v.kind(); k { - case Invalid: - return "" - case String: + // stringNonString is split out to keep String inlineable for string kinds. + if v.kind() == String { return *(*string)(v.ptr) } + return v.stringNonString() +} + +func (v Value) stringNonString() string { + if v.kind() == Invalid { + return "" + } // If you call String on a reflect.Value of other type, it's better to // print something than to panic. Useful in debugging. return "<" + v.Type().String() + " Value>" @@ -2244,12 +2599,17 @@ func (v Value) TrySend(x Value) bool { // Type returns v's type. func (v Value) Type() Type { - f := v.flag - if f == 0 { + if v.flag != 0 && v.flag&flagMethod == 0 { + return v.typ + } + return v.typeSlow() +} + +func (v Value) typeSlow() Type { + if v.flag == 0 { panic(&ValueError{"reflect.Value.Type", Invalid}) } - if f&flagMethod == 0 { - // Easy case + if v.flag&flagMethod == 0 { return v.typ } @@ -2274,6 +2634,16 @@ func (v Value) Type() Type { return v.typ.typeOff(m.mtyp) } +// CanUint reports whether Uint can be used without panicking. +func (v Value) CanUint() bool { + switch v.kind() { + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return true + default: + return false + } +} + // Uint returns v's underlying value, as a uint64. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) Uint() uint64 { @@ -2301,11 +2671,11 @@ func (v Value) Uint() uint64 { // which ensures cmd/compile can recognize unsafe.Pointer(v.UnsafeAddr()) // and make an exception. -// UnsafeAddr returns a pointer to v's data. -// It is for advanced clients that also import the "unsafe" package. +// UnsafeAddr returns a pointer to v's data, as a uintptr. // It panics if v is not addressable. +// +// It's preferred to use uintptr(Value.Addr().UnsafePointer()) to get the equivalent result. func (v Value) UnsafeAddr() uintptr { - // TODO: deprecate if v.typ == nil { panic(&ValueError{"reflect.Value.UnsafeAddr", Invalid}) } @@ -2315,12 +2685,65 @@ func (v Value) UnsafeAddr() uintptr { return uintptr(v.ptr) } +// UnsafePointer returns v's value as a [unsafe.Pointer]. +// It panics if v's Kind is not Chan, Func, Map, Pointer, Slice, or UnsafePointer. +// +// If v's Kind is Func, the returned pointer is an underlying +// code pointer, but not necessarily enough to identify a +// single function uniquely. The only guarantee is that the +// result is zero if and only if v is a nil func Value. +// +// If v's Kind is Slice, the returned pointer is to the first +// element of the slice. If the slice is nil the returned value +// is nil. If the slice is empty but non-nil the return value is non-nil. +func (v Value) UnsafePointer() unsafe.Pointer { + k := v.kind() + switch k { + case Pointer: + if v.typ.ptrdata == 0 { + // Since it is a not-in-heap pointer, all pointers to the heap are + // forbidden! See comment in Value.Elem and issue #48399. + if !verifyNotInHeapPtr(*(*uintptr)(v.ptr)) { + panic("reflect: reflect.Value.UnsafePointer on an invalid notinheap pointer") + } + return *(*unsafe.Pointer)(v.ptr) + } + fallthrough + case Chan, Map, UnsafePointer: + return v.pointer() + case Func: + if v.flag&flagMethod != 0 { + // As the doc comment says, the returned pointer is an + // underlying code pointer but not necessarily enough to + // identify a single function uniquely. All method expressions + // created via reflect have the same underlying code pointer, + // so their Pointers are equal. The function used here must + // match the one used in makeMethodValue. + code := methodValueCallCodePtr() + return *(*unsafe.Pointer)(unsafe.Pointer(&code)) + } + p := v.pointer() + // Non-nil func value points at data block. + // First word of data block is actual code. + if p != nil { + p = *(*unsafe.Pointer)(p) + } + return p + + case Slice: + return (*unsafeheader.Slice)(v.ptr).Data + } + panic(&ValueError{"reflect.Value.UnsafePointer", v.kind()}) +} + // StringHeader is the runtime representation of a string. // It cannot be used safely or portably and its representation may // change in a later release. // Moreover, the Data field is not sufficient to guarantee the data // it references will not be garbage collected, so programs must keep // a separate, correctly typed pointer to the underlying data. +// +// Deprecated: Use unsafe.String or unsafe.StringData instead. type StringHeader struct { Data uintptr Len int @@ -2332,6 +2755,8 @@ type StringHeader struct { // Moreover, the Data field is not sufficient to guarantee the data // it references will not be garbage collected, so programs must keep // a separate, correctly typed pointer to the underlying data. +// +// Deprecated: Use unsafe.Slice or unsafe.SliceData instead. type SliceHeader struct { Data uintptr Len int @@ -2370,11 +2795,12 @@ func grow(s Value, extra int) (Value, int, int) { if m == 0 { m = extra } else { + const threshold = 256 for m < i1 { - if i0 < 1024 { + if i0 < threshold { m += m } else { - m += m / 4 + m += (m + 3*threshold) / 4 } } } @@ -2475,6 +2901,7 @@ type runtimeSelect struct { // If the case was a receive, val is filled in with the received value. // The conventional OK bool indicates whether the receive corresponds // to a sent value. +// //go:noescape func rselect([]runtimeSelect) (chosen int, recvOK bool) @@ -2506,7 +2933,6 @@ const ( // Normally Chan's underlying value must be a channel and Send must be a zero Value. // If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value. // When a receive operation is selected, the received Value is returned by Select. -// type SelectCase struct { Dir SelectDir // direction of case Chan Value // channel to use (for send or receive) @@ -2681,7 +3107,7 @@ func MakeMapWithSize(typ Type, n int) Value { // If v is a nil pointer, Indirect returns a zero Value. // If v is not a pointer, Indirect returns v. func Indirect(v Value) Value { - if v.Kind() != Ptr { + if v.Kind() != Pointer { return v } return v.Elem() @@ -2689,7 +3115,7 @@ func Indirect(v Value) Value { // ValueOf returns a new Value initialized to the concrete value // stored in the interface i. ValueOf(nil) returns the zero Value. -func ValueOf(i interface{}) Value { +func ValueOf(i any) Value { if i == nil { return Value{} } @@ -2733,7 +3159,7 @@ const maxZero = 1024 var zeroVal [maxZero]byte // New returns a Value representing a pointer to a new zero value -// for the specified type. That is, the returned Value's Type is PtrTo(typ). +// for the specified type. That is, the returned Value's Type is PointerTo(typ). func New(typ Type) Value { if typ == nil { panic("reflect: New(nil)") @@ -2741,25 +3167,26 @@ func New(typ Type) Value { t := typ.(*rtype) pt := t.ptrTo() if ifaceIndir(pt) { - // This is a pointer to a go:notinheap type. + // This is a pointer to a not-in-heap type. panic("reflect: New of type that may not be allocated in heap (possibly undefined cgo C type)") } ptr := unsafe_New(t) - fl := flag(Ptr) + fl := flag(Pointer) return Value{pt, ptr, fl} } // NewAt returns a Value representing a pointer to a value of the // specified type, using p as that pointer. func NewAt(typ Type, p unsafe.Pointer) Value { - fl := flag(Ptr) + fl := flag(Pointer) t := typ.(*rtype) return Value{t.ptrTo(), p, fl} } -// assignTo returns a value v that can be assigned directly to typ. -// It panics if v is not assignable to typ. -// For a conversion to an interface type, target is a suggested scratch space to use. +// assignTo returns a value v that can be assigned directly to dst. +// It panics if v is not assignable to dst. +// For a conversion to an interface type, target, if not nil, +// is a suggested scratch space to use. // target must be initialized memory (or nil). func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value { if v.flag&flagMethod != 0 { @@ -2775,9 +3202,6 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, v.ptr, fl} case implements(dst, v.typ): - if target == nil { - target = unsafe_New(dst) - } if v.Kind() == Interface && v.IsNil() { // A nil ReadWriter passed to nil Reader is OK, // but using ifaceE2I below will panic. @@ -2785,8 +3209,11 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value return Value{dst, nil, flag(Interface)} } x := valueInterface(v, false) + if target == nil { + target = unsafe_New(dst) + } if dst.NumMethod() == 0 { - *(*interface{})(target) = x + *(*any)(target) = x } else { ifaceE2I(dst, x, target) } @@ -2821,16 +3248,95 @@ func (v Value) CanConvert(t Type) bool { // Currently the only conversion that is OK in terms of type // but that can panic depending on the value is converting // from slice to pointer-to-array. - if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array { + if vt.Kind() == Slice && t.Kind() == Pointer && t.Elem().Kind() == Array { n := t.Elem().Len() - h := (*unsafeheader.Slice)(v.ptr) - if n > h.Len { + if n > v.Len() { return false } } return true } +// Comparable reports whether the type of v is comparable. +// If the type of v is an interface, this checks the dynamic type. +// If this reports true then v.Interface() == x will not panic for any x. +func (v Value) Comparable() bool { + k := v.Kind() + switch k { + case Invalid: + return false + + case Bool, + Int, Int8, Int16, Int32, Int64, + Uint, Uint8, Uint16, Uint32, Uint64, + Uintptr, + Float32, Float64, Complex64, Complex128, + Chan: + return true + + case Array: + switch v.Type().Elem().Kind() { + case Interface, Array, Struct: + for i := 0; i < v.Type().Len(); i++ { + if !v.Index(i).Comparable() { + return false + } + } + } + return v.Type().Comparable() + + case Func: + return false + + case Interface: + return v.Elem().Comparable() + + case Map: + return false + + case Pointer: + return true + + case Slice: + return false + + case String: + return true + + case Struct: + for i := 0; i < v.NumField(); i++ { + if !v.Field(i).Comparable() { + return false + } + } + return true + + case UnsafePointer: + return true + + default: + return false + } +} + +// Equal reports true if v is equal to u. +// For valid values, if either v or u is non-comparable, Equal returns false. +func (v Value) Equal(u Value) bool { + if !v.IsValid() || !u.IsValid() { + return v.IsValid() == u.IsValid() + } + + if v.Comparable() || u.Comparable() { + return valueInterface(v, false) == valueInterface(u, false) + } + + if u.Kind() == Interface && v.kind() == Interface { // this case is for nil interface value + return v.Elem().Equal(u.Elem()) + } + + return false +} + // convertOp returns the function to convert a value of type src // to a value of type dst. If the conversion is illegal, convertOp returns nil. func convertOp(dst, src *rtype) func(Value, Type) Value { @@ -2892,7 +3398,7 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { } // "x is a slice, T is a pointer-to-array type, // and the slice and array types have identical element types." - if dst.Kind() == Ptr && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() { + if dst.Kind() == Pointer && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() { return cvtSliceArrayPtr } @@ -2908,8 +3414,8 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { } // dst and src are non-defined pointer types with same underlying base type. - if dst.Kind() == Ptr && dst.Name() == "" && - src.Kind() == Ptr && src.Name() == "" && + if dst.Kind() == Pointer && dst.Name() == "" && + src.Kind() == Pointer && src.Name() == "" && haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) { return cvtDirect } @@ -3091,11 +3597,11 @@ func cvtStringRunes(v Value, t Type) Value { // convertOp: []T -> *[N]T func cvtSliceArrayPtr(v Value, t Type) Value { n := t.Elem().Len() - h := (*unsafeheader.Slice)(v.ptr) - if n > h.Len { - panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to pointer to array with length " + itoa.Itoa(n)) + if n > v.Len() { + panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to pointer to array with length " + itoa.Itoa(n)) } - return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} + h := (*unsafeheader.Slice)(v.ptr) + return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Pointer)} } // convertOp: direct copy @@ -3118,7 +3624,7 @@ func cvtT2I(v Value, typ Type) Value { target := unsafe_New(typ.common()) x := valueInterface(v, false) if typ.NumMethod() == 0 { - *(*interface{})(target) = x + *(*any)(target) = x } else { ifaceE2I(typ.(*rtype), x, target) } @@ -3160,25 +3666,32 @@ func makemap(t *rtype, cap int) (m unsafe.Pointer) //go:noescape func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) +//go:noescape +func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) + //go:noescape func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer) +//go:noescape +func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) + //go:noescape func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) -// m escapes into the return value, but the caller of mapiterinit -// doesn't let the return value escape. //go:noescape -func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer +func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) //go:noescape -func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer) +func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter) //go:noescape -func mapiterelem(it unsafe.Pointer) (elem unsafe.Pointer) +func mapiterkey(it *hiter) (key unsafe.Pointer) //go:noescape -func mapiternext(it unsafe.Pointer) +func mapiterelem(it *hiter) (elem unsafe.Pointer) + +//go:noescape +func mapiternext(it *hiter) //go:noescape func maplen(m unsafe.Pointer) int @@ -3206,46 +3719,55 @@ func maplen(m unsafe.Pointer) int // Arguments passed through to call do not escape. The type is used only in a // very limited callee of call, the stackArgs are copied, and regArgs is only // used in the call frame. +// //go:noescape //go:linkname call runtime.reflectcall func call(stackArgsType *rtype, f, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs) -func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer) +func ifaceE2I(t *rtype, src any, dst unsafe.Pointer) // memmove copies size bytes to dst from src. No write barriers are used. +// //go:noescape func memmove(dst, src unsafe.Pointer, size uintptr) // typedmemmove copies a value of type t to dst from src. +// //go:noescape func typedmemmove(t *rtype, dst, src unsafe.Pointer) // typedmemmovepartial is like typedmemmove but assumes that // dst and src point off bytes into the value and only copies size bytes. +// //go:noescape func typedmemmovepartial(t *rtype, dst, src unsafe.Pointer, off, size uintptr) // typedmemclr zeros the value at ptr of type t. +// //go:noescape func typedmemclr(t *rtype, ptr unsafe.Pointer) // typedmemclrpartial is like typedmemclr but assumes that // dst points off bytes into the value and only clears size bytes. +// //go:noescape func typedmemclrpartial(t *rtype, ptr unsafe.Pointer, off, size uintptr) // typedslicecopy copies a slice of elemType values from src to dst, // returning the number of elements copied. +// //go:noescape func typedslicecopy(elemType *rtype, dst, src unsafeheader.Slice) int //go:noescape func typehash(t *rtype, p unsafe.Pointer, h uintptr) uintptr +func verifyNotInHeapPtr(p uintptr) bool + // Dummy annotation marking that the value x escapes, // for use in cases where the reflect code is so clever that // the compiler cannot follow. -func escapes(x interface{}) { +func escapes(x any) { if dummy.b { dummy.x = x } @@ -3253,5 +3775,5 @@ func escapes(x interface{}) { var dummy struct { b bool - x interface{} + x any } diff --git a/src/reflect/visiblefields.go b/src/reflect/visiblefields.go index 1a2b53570bef85..9375faa11045a6 100644 --- a/src/reflect/visiblefields.go +++ b/src/reflect/visiblefields.go @@ -92,7 +92,7 @@ func (w *visibleFieldsWalker) walk(t Type) { w.fields = append(w.fields, f) } if f.Anonymous { - if f.Type.Kind() == Ptr { + if f.Type.Kind() == Pointer { f.Type = f.Type.Elem() } if f.Type.Kind() == Struct { diff --git a/src/reflect/visiblefields_test.go b/src/reflect/visiblefields_test.go index 915bbee867c60d..66d545dd1f7c2c 100644 --- a/src/reflect/visiblefields_test.go +++ b/src/reflect/visiblefields_test.go @@ -6,6 +6,7 @@ package reflect_test import ( . "reflect" + "strings" "testing" ) @@ -16,7 +17,7 @@ type structField struct { var fieldsTests = []struct { testName string - val interface{} + val any expect []structField }{{ testName: "SimpleStruct", @@ -77,7 +78,7 @@ var fieldsTests = []struct { index: []int{0, 1}, }}, }, { - testName: "TwoEmbeddedStructsWithCancellingMembers", + testName: "TwoEmbeddedStructsWithCancelingMembers", val: struct { SFG SF @@ -278,7 +279,7 @@ type RS3 struct { RS1 } -type M map[string]interface{} +type M map[string]any type Rec1 struct { *Rec2 @@ -328,3 +329,21 @@ func TestFields(t *testing.T) { }) } } + +// Must not panic with nil embedded pointer. +func TestFieldByIndexErr(t *testing.T) { + type A struct { + S string + } + type B struct { + *A + } + v := ValueOf(B{}) + _, err := v.FieldByIndexErr([]int{0, 0}) + if err == nil { + t.Fatal("expected error") + } + if !strings.Contains(err.Error(), "embedded struct field A") { + t.Fatal(err) + } +} diff --git a/src/regexp/all_test.go b/src/regexp/all_test.go index be7a2e7111876d..c233cfa9eaac75 100644 --- a/src/regexp/all_test.go +++ b/src/regexp/all_test.go @@ -372,6 +372,9 @@ var literalPrefixTests = []MetaTest{ {`^^0$$`, ``, ``, false}, {`^$^$`, ``, ``, false}, {`$$0^^`, ``, ``, false}, + {`a\x{fffd}b`, ``, `a`, false}, + {`\x{fffd}b`, ``, ``, false}, + {"\ufffd", ``, ``, false}, } func TestQuoteMeta(t *testing.T) { diff --git a/src/regexp/backtrack.go b/src/regexp/backtrack.go index 41ae59bcaa2d1f..0739f5ff58866d 100644 --- a/src/regexp/backtrack.go +++ b/src/regexp/backtrack.go @@ -163,7 +163,7 @@ func (re *Regexp) tryBacktrack(b *bitState, i input, pc uint32, pos int) bool { } Skip: - inst := re.prog.Inst[pc] + inst := &re.prog.Inst[pc] switch inst.Op { default: diff --git a/src/regexp/exec.go b/src/regexp/exec.go index 4411e4c3e61188..3fc4b684febd60 100644 --- a/src/regexp/exec.go +++ b/src/regexp/exec.go @@ -427,7 +427,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in flag = i.context(pos) } pc := re.onepass.Start - inst := re.onepass.Inst[pc] + inst := &re.onepass.Inst[pc] // If there is a simple literal prefix, skip over it. if pos == 0 && flag.match(syntax.EmptyOp(inst.Arg)) && len(re.prefix) > 0 && i.canCheckPrefix() { @@ -442,7 +442,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in pc = int(re.prefixEnd) } for { - inst = re.onepass.Inst[pc] + inst = &re.onepass.Inst[pc] pc = int(inst.Out) switch inst.Op { default: @@ -470,7 +470,7 @@ func (re *Regexp) doOnePass(ir io.RuneReader, ib []byte, is string, pos, ncap in } // peek at the input rune to see which branch of the Alt to take case syntax.InstAlt, syntax.InstAltMatch: - pc = int(onePassNext(&inst, r)) + pc = int(onePassNext(inst, r)) continue case syntax.InstFail: goto Return diff --git a/src/regexp/exec2_test.go b/src/regexp/exec2_test.go index 6444bc12f90ebd..b6dac4a058f0ce 100644 --- a/src/regexp/exec2_test.go +++ b/src/regexp/exec2_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race package regexp diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go index 1f9a7a96e0c67d..1694230345177c 100644 --- a/src/regexp/exec_test.go +++ b/src/regexp/exec_test.go @@ -52,8 +52,8 @@ import ( // submatch indices. An unmatched subexpression formats // its pair as a single - (not illustrated above). For now // each regexp run produces two match results, one for a -// ``full match'' that restricts the regexp to matching the entire -// string or nothing, and one for a ``partial match'' that gives +// “full match” that restricts the regexp to matching the entire +// string or nothing, and one for a “partial match” that gives // the leftmost first match found in the string. // // Lines beginning with # are comments. Lines beginning with @@ -62,7 +62,6 @@ import ( // // At time of writing, re2-exhaustive.txt is 59 MB but compresses to 385 kB, // so we store re2-exhaustive.txt.bz2 in the repository and decompress it on the fly. -// func TestRE2Search(t *testing.T) { testRE2(t, "testdata/re2-search.txt") } @@ -294,12 +293,9 @@ func parseResult(t *testing.T, file string, lineno int, res string) []int { out[n] = -1 out[n+1] = -1 } else { - k := strings.Index(pair, "-") - if k < 0 { - t.Fatalf("%s:%d: invalid pair %s", file, lineno, pair) - } - lo, err1 := strconv.Atoi(pair[:k]) - hi, err2 := strconv.Atoi(pair[k+1:]) + loStr, hiStr, _ := strings.Cut(pair, "-") + lo, err1 := strconv.Atoi(loStr) + hi, err2 := strconv.Atoi(hiStr) if err1 != nil || err2 != nil || lo > hi { t.Fatalf("%s:%d: invalid pair %s", file, lineno, pair) } @@ -457,12 +453,11 @@ Reading: continue Reading } case ':': - i := strings.Index(flag[1:], ":") - if i < 0 { + var ok bool + if _, flag, ok = strings.Cut(flag[1:], ":"); !ok { t.Logf("skip: %s", line) continue Reading } - flag = flag[1+i+1:] case 'C', 'N', 'T', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': t.Logf("skip: %s", line) continue Reading diff --git a/src/regexp/find_test.go b/src/regexp/find_test.go index 64c2239d905fdf..2edbe9b86e6154 100644 --- a/src/regexp/find_test.go +++ b/src/regexp/find_test.go @@ -116,6 +116,13 @@ var findTests = []FindTest{ {"\\`", "`", build(1, 0, 1)}, {"[\\`]+", "`", build(1, 0, 1)}, + {"\ufffd", "\xff", build(1, 0, 1)}, + {"\ufffd", "hello\xffworld", build(1, 5, 6)}, + {`.*`, "hello\xffworld", build(1, 0, 11)}, + {`\x{fffd}`, "\xc2\x00", build(1, 0, 1)}, + {"[\ufffd]", "\xff", build(1, 0, 1)}, + {`[\x{fffd}]`, "\xc2\x00", build(1, 0, 1)}, + // long set of matches (longer than startSize) { ".", diff --git a/src/regexp/onepass.go b/src/regexp/onepass.go index 2f3ce6f9f6cb87..bc47f4c4a830da 100644 --- a/src/regexp/onepass.go +++ b/src/regexp/onepass.go @@ -9,6 +9,7 @@ import ( "sort" "strings" "unicode" + "unicode/utf8" ) // "One-pass" regexp execution. @@ -55,7 +56,7 @@ func onePassPrefix(p *syntax.Prog) (prefix string, complete bool, pc uint32) { // Have prefix; gather characters. var buf strings.Builder - for iop(i) == syntax.InstRune && len(i.Rune) == 1 && syntax.Flags(i.Arg)&syntax.FoldCase == 0 { + for iop(i) == syntax.InstRune && len(i.Rune) == 1 && syntax.Flags(i.Arg)&syntax.FoldCase == 0 && i.Rune[0] != utf8.RuneError { buf.WriteRune(i.Rune[0]) pc, i = i.Out, &p.Inst[i.Out] } diff --git a/src/regexp/regexp.go b/src/regexp/regexp.go index b547a2ab97d71a..7958a397285d35 100644 --- a/src/regexp/regexp.go +++ b/src/regexp/regexp.go @@ -9,17 +9,22 @@ // More precisely, it is the syntax accepted by RE2 and described at // https://golang.org/s/re2syntax, except for \C. // For an overview of the syntax, run -// go doc regexp/syntax +// +// go doc regexp/syntax // // The regexp implementation provided by this package is // guaranteed to run in time linear in the size of the input. // (This is a property not guaranteed by most open source // implementations of regular expressions.) For more information // about this property, see +// // https://swtch.com/~rsc/regexp/regexp1.html +// // or any book about automata theory. // // All characters are UTF-8-encoded code points. +// Following utf8.DecodeRune, each byte of an invalid UTF-8 sequence +// is treated as if it encoded utf8.RuneError (U+FFFD). // // There are 16 methods of Regexp that match a regular expression and identify // the matched text. Their names are matched by this regular expression: @@ -40,11 +45,11 @@ // successive submatches of the expression. Submatches are matches of // parenthesized subexpressions (also known as capturing groups) within the // regular expression, numbered from left to right in order of opening -// parenthesis. Submatch 0 is the match of the entire expression, submatch 1 +// parenthesis. Submatch 0 is the match of the entire expression, submatch 1 is // the match of the first parenthesized subexpression, and so on. // // If 'Index' is present, matches and submatches are identified by byte index -// pairs within the input string: result[2*n:2*n+1] identifies the indexes of +// pairs within the input string: result[2*n:2*n+2] identifies the indexes of // the nth submatch. The pair for n==0 identifies the match of the entire // expression. If 'Index' is not present, the match is identified by the text // of the match/submatch. If an index is negative or text is nil, it means that @@ -62,7 +67,6 @@ // before returning. // // (There are a few other methods that do not match this pattern.) -// package regexp import ( @@ -276,7 +280,11 @@ func minInputLen(re *syntax.Regexp) int { case syntax.OpLiteral: l := 0 for _, r := range re.Rune { - l += utf8.RuneLen(r) + if r == utf8.RuneError { + l++ + } else { + l += utf8.RuneLen(r) + } } return l case syntax.OpCapture, syntax.OpPlus: @@ -787,11 +795,12 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) { accept = false } var width int - // TODO: use step() if b == nil { - _, width = utf8.DecodeRuneInString(s[pos:end]) + is := inputString{str: s} + _, width = is.step(pos) } else { - _, width = utf8.DecodeRune(b[pos:end]) + ib := inputBytes{str: b} + _, width = ib.step(pos) } if width > 0 { pos += width @@ -922,23 +931,22 @@ func (re *Regexp) ExpandString(dst []byte, template string, src string, match [] func (re *Regexp) expand(dst []byte, template string, bsrc []byte, src string, match []int) []byte { for len(template) > 0 { - i := strings.Index(template, "$") - if i < 0 { + before, after, ok := strings.Cut(template, "$") + if !ok { break } - dst = append(dst, template[:i]...) - template = template[i:] - if len(template) > 1 && template[1] == '$' { + dst = append(dst, before...) + template = after + if template != "" && template[0] == '$' { // Treat $$ as $. dst = append(dst, '$') - template = template[2:] + template = template[1:] continue } name, num, rest, ok := extract(template) if !ok { // Malformed; treat $ as raw text. dst = append(dst, '$') - template = template[1:] continue } template = rest @@ -967,17 +975,16 @@ func (re *Regexp) expand(dst []byte, template string, bsrc []byte, src string, m return dst } -// extract returns the name from a leading "$name" or "${name}" in str. +// extract returns the name from a leading "name" or "{name}" in str. +// (The $ has already been removed by the caller.) // If it is a number, extract returns num set to that number; otherwise num = -1. func extract(str string) (name string, num int, rest string, ok bool) { - if len(str) < 2 || str[0] != '$' { + if str == "" { return } brace := false - if str[1] == '{' { + if str[0] == '{' { brace = true - str = str[2:] - } else { str = str[1:] } i := 0 @@ -1234,13 +1241,15 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { // that contains no metacharacters, it is equivalent to strings.SplitN. // // Example: -// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) -// // s: ["", "b", "b", "c", "cadaaae"] +// +// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) +// // s: ["", "b", "b", "c", "cadaaae"] // // The count determines the number of substrings to return: -// n > 0: at most n substrings; the last substring will be the unsplit remainder. -// n == 0: the result is nil (zero substrings) -// n < 0: all substrings +// +// n > 0: at most n substrings; the last substring will be the unsplit remainder. +// n == 0: the result is nil (zero substrings) +// n < 0: all substrings func (re *Regexp) Split(s string, n int) []string { if n == 0 { diff --git a/src/regexp/syntax/doc.go b/src/regexp/syntax/doc.go index b3f9136b5f3194..f6a4b43f7aebb1 100644 --- a/src/regexp/syntax/doc.go +++ b/src/regexp/syntax/doc.go @@ -9,123 +9,132 @@ Package syntax parses regular expressions into parse trees and compiles parse trees into programs. Most clients of regular expressions will use the facilities of package regexp (such as Compile and Match) instead of this package. -Syntax +# Syntax The regular expression syntax understood by this package when parsing with the Perl flag is as follows. Parts of the syntax can be disabled by passing alternate flags to Parse. - Single characters: - . any character, possibly including newline (flag s=true) - [xyz] character class - [^xyz] negated character class - \d Perl character class - \D negated Perl character class - [[:alpha:]] ASCII character class - [[:^alpha:]] negated ASCII character class - \pN Unicode character class (one-letter name) - \p{Greek} Unicode character class - \PN negated Unicode character class (one-letter name) - \P{Greek} negated Unicode character class + + . any character, possibly including newline (flag s=true) + [xyz] character class + [^xyz] negated character class + \d Perl character class + \D negated Perl character class + [[:alpha:]] ASCII character class + [[:^alpha:]] negated ASCII character class + \pN Unicode character class (one-letter name) + \p{Greek} Unicode character class + \PN negated Unicode character class (one-letter name) + \P{Greek} negated Unicode character class Composites: - xy x followed by y - x|y x or y (prefer x) + + xy x followed by y + x|y x or y (prefer x) Repetitions: - x* zero or more x, prefer more - x+ one or more x, prefer more - x? zero or one x, prefer one - x{n,m} n or n+1 or ... or m x, prefer more - x{n,} n or more x, prefer more - x{n} exactly n x - x*? zero or more x, prefer fewer - x+? one or more x, prefer fewer - x?? zero or one x, prefer zero - x{n,m}? n or n+1 or ... or m x, prefer fewer - x{n,}? n or more x, prefer fewer - x{n}? exactly n x + + x* zero or more x, prefer more + x+ one or more x, prefer more + x? zero or one x, prefer one + x{n,m} n or n+1 or ... or m x, prefer more + x{n,} n or more x, prefer more + x{n} exactly n x + x*? zero or more x, prefer fewer + x+? one or more x, prefer fewer + x?? zero or one x, prefer zero + x{n,m}? n or n+1 or ... or m x, prefer fewer + x{n,}? n or more x, prefer fewer + x{n}? exactly n x Implementation restriction: The counting forms x{n,m}, x{n,}, and x{n} reject forms that create a minimum or maximum repetition count above 1000. Unlimited repetitions are not subject to this restriction. Grouping: - (re) numbered capturing group (submatch) - (?Pre) named & numbered capturing group (submatch) - (?:re) non-capturing group - (?flags) set flags within current group; non-capturing - (?flags:re) set flags during re; non-capturing - Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are: + (re) numbered capturing group (submatch) + (?Pre) named & numbered capturing group (submatch) + (?:re) non-capturing group + (?flags) set flags within current group; non-capturing + (?flags:re) set flags during re; non-capturing + + Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are: - i case-insensitive (default false) - m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false) - s let . match \n (default false) - U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false) + i case-insensitive (default false) + m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false) + s let . match \n (default false) + U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false) Empty strings: - ^ at beginning of text or line (flag m=true) - $ at end of text (like \z not \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 - \z at end of text + + ^ at beginning of text or line (flag m=true) + $ at end of text (like \z not \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 + \z at end of text Escape sequences: - \a bell (== \007) - \f form feed (== \014) - \t horizontal tab (== \011) - \n newline (== \012) - \r carriage return (== \015) - \v vertical tab character (== \013) - \* literal *, for any punctuation character * - \123 octal character code (up to three digits) - \x7F hex character code (exactly two digits) - \x{10FFFF} hex character code - \Q...\E literal text ... even if ... has punctuation + + \a bell (== \007) + \f form feed (== \014) + \t horizontal tab (== \011) + \n newline (== \012) + \r carriage return (== \015) + \v vertical tab character (== \013) + \* literal *, for any punctuation character * + \123 octal character code (up to three digits) + \x7F hex character code (exactly two digits) + \x{10FFFF} hex character code + \Q...\E literal text ... even if ... has punctuation Character class elements: - x single character - A-Z character range (inclusive) - \d Perl character class - [:foo:] ASCII character class foo - \p{Foo} Unicode character class Foo - \pF Unicode character class F (one-letter name) + + x single character + A-Z character range (inclusive) + \d Perl character class + [:foo:] ASCII character class foo + \p{Foo} Unicode character class Foo + \pF Unicode character class F (one-letter name) Named character classes as character class elements: - [\d] digits (== \d) - [^\d] not digits (== \D) - [\D] not digits (== \D) - [^\D] not not digits (== \d) - [[:name:]] named ASCII class inside character class (== [:name:]) - [^[:name:]] named ASCII class inside negated character class (== [:^name:]) - [\p{Name}] named Unicode property inside character class (== \p{Name}) - [^\p{Name}] named Unicode property inside negated character class (== \P{Name}) + + [\d] digits (== \d) + [^\d] not digits (== \D) + [\D] not digits (== \D) + [^\D] not not digits (== \d) + [[:name:]] named ASCII class inside character class (== [:name:]) + [^[:name:]] named ASCII class inside negated character class (== [:^name:]) + [\p{Name}] named Unicode property inside character class (== \p{Name}) + [^\p{Name}] named Unicode property inside negated character class (== \P{Name}) Perl character classes (all ASCII-only): - \d digits (== [0-9]) - \D not digits (== [^0-9]) - \s whitespace (== [\t\n\f\r ]) - \S not whitespace (== [^\t\n\f\r ]) - \w word characters (== [0-9A-Za-z_]) - \W not word characters (== [^0-9A-Za-z_]) + + \d digits (== [0-9]) + \D not digits (== [^0-9]) + \s whitespace (== [\t\n\f\r ]) + \S not whitespace (== [^\t\n\f\r ]) + \w word characters (== [0-9A-Za-z_]) + \W not word characters (== [^0-9A-Za-z_]) ASCII character classes: - [[:alnum:]] alphanumeric (== [0-9A-Za-z]) - [[:alpha:]] alphabetic (== [A-Za-z]) - [[:ascii:]] ASCII (== [\x00-\x7F]) - [[:blank:]] blank (== [\t ]) - [[:cntrl:]] control (== [\x00-\x1F\x7F]) - [[:digit:]] digits (== [0-9]) - [[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]) - [[:lower:]] lower case (== [a-z]) - [[:print:]] printable (== [ -~] == [ [:graph:]]) - [[:punct:]] punctuation (== [!-/:-@[-`{-~]) - [[:space:]] whitespace (== [\t\n\v\f\r ]) - [[:upper:]] upper case (== [A-Z]) - [[:word:]] word characters (== [0-9A-Za-z_]) - [[:xdigit:]] hex digit (== [0-9A-Fa-f]) + + [[:alnum:]] alphanumeric (== [0-9A-Za-z]) + [[:alpha:]] alphabetic (== [A-Za-z]) + [[:ascii:]] ASCII (== [\x00-\x7F]) + [[:blank:]] blank (== [\t ]) + [[:cntrl:]] control (== [\x00-\x1F\x7F]) + [[:digit:]] digits (== [0-9]) + [[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]) + [[:lower:]] lower case (== [a-z]) + [[:print:]] printable (== [ -~] == [ [:graph:]]) + [[:punct:]] punctuation (== [!-/:-@[-`{-~]) + [[:space:]] whitespace (== [\t\n\v\f\r ]) + [[:upper:]] upper case (== [A-Z]) + [[:word:]] word characters (== [0-9A-Za-z_]) + [[:xdigit:]] hex digit (== [0-9A-Fa-f]) Unicode character classes are those in unicode.Categories and unicode.Scripts. */ diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go index 7b4030935a7bb5..350f297e5b328b 100644 --- a/src/regexp/syntax/parse.go +++ b/src/regexp/syntax/parse.go @@ -43,6 +43,7 @@ const ( ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" ErrUnexpectedParen ErrorCode = "unexpected )" + ErrNestingDepth ErrorCode = "expression nests too deeply" ) func (e ErrorCode) String() string { @@ -76,13 +77,29 @@ const ( opVerticalBar ) +// maxHeight is the maximum height of a regexp parse tree. +// It is somewhat arbitrarily chosen, but the idea is to be large enough +// that no one will actually hit in real use but at the same time small enough +// that recursion on the Regexp tree will not hit the 1GB Go stack limit. +// The maximum amount of stack for a single recursive frame is probably +// closer to 1kB, so this could potentially be raised, but it seems unlikely +// that people have regexps nested even this deeply. +// We ran a test on Google's C++ code base and turned up only +// a single use case with depth > 100; it had depth 128. +// Using depth 1000 should be plenty of margin. +// As an optimization, we don't even bother calculating heights +// until we've allocated at least maxHeight Regexp structures. +const maxHeight = 1000 + type parser struct { flags Flags // parse mode flags stack []*Regexp // stack of parsed expressions free *Regexp numCap int // number of capturing groups seen wholeRegexp string - tmpClass []rune // temporary char class work space + tmpClass []rune // temporary char class work space + numRegexp int // number of regexps allocated + height map[*Regexp]int // regexp height for height limit check } func (p *parser) newRegexp(op Op) *Regexp { @@ -92,16 +109,52 @@ func (p *parser) newRegexp(op Op) *Regexp { *re = Regexp{} } else { re = new(Regexp) + p.numRegexp++ } re.Op = op return re } func (p *parser) reuse(re *Regexp) { + if p.height != nil { + delete(p.height, re) + } re.Sub0[0] = p.free p.free = re } +func (p *parser) checkHeight(re *Regexp) { + if p.numRegexp < maxHeight { + return + } + if p.height == nil { + p.height = make(map[*Regexp]int) + for _, re := range p.stack { + p.checkHeight(re) + } + } + if p.calcHeight(re, true) > maxHeight { + panic(ErrNestingDepth) + } +} + +func (p *parser) calcHeight(re *Regexp, force bool) int { + if !force { + if h, ok := p.height[re]; ok { + return h + } + } + h := 1 + for _, sub := range re.Sub { + hsub := p.calcHeight(sub, false) + if h < 1+hsub { + h = 1 + hsub + } + } + p.height[re] = h + return h +} + // Parse stack manipulation. // push pushes the regexp re onto the parse stack and returns the regexp. @@ -137,6 +190,7 @@ func (p *parser) push(re *Regexp) *Regexp { } p.stack = append(p.stack, re) + p.checkHeight(re) return re } @@ -246,6 +300,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( re.Sub = re.Sub0[:1] re.Sub[0] = sub p.stack[n-1] = re + p.checkHeight(re) if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} @@ -390,12 +445,16 @@ func (p *parser) collapse(subs []*Regexp, op Op) *Regexp { // frees (passes to p.reuse) any removed *Regexps. // // For example, -// ABC|ABD|AEF|BCX|BCY +// +// ABC|ABD|AEF|BCX|BCY +// // simplifies by literal prefix extraction to -// A(B(C|D)|EF)|BC(X|Y) +// +// A(B(C|D)|EF)|BC(X|Y) +// // which simplifies by character class introduction to -// A(B[CD]|EF)|BC[XY] // +// A(B[CD]|EF)|BC[XY] func (p *parser) factor(sub []*Regexp) []*Regexp { if len(sub) < 2 { return sub @@ -693,6 +752,21 @@ func literalRegexp(s string, flags Flags) *Regexp { // Flags, and returns a regular expression parse tree. The syntax is // described in the top-level comment. func Parse(s string, flags Flags) (*Regexp, error) { + return parse(s, flags) +} + +func parse(s string, flags Flags) (_ *Regexp, err error) { + defer func() { + switch r := recover(); r { + default: + panic(r) + case nil: + // ok + case ErrNestingDepth: + err = &Error{Code: ErrNestingDepth, Expr: s} + } + }() + if flags&Literal != 0 { // Trivial parser for literal string. if err := checkUTF8(s); err != nil { @@ -704,7 +778,6 @@ func Parse(s string, flags Flags) (*Regexp, error) { // Otherwise, must do real work. var ( p parser - err error c rune op Op lastRepeat string @@ -824,13 +897,7 @@ func Parse(s string, flags Flags) (*Regexp, error) { case 'Q': // \Q ... \E: the ... is always literals var lit string - if i := strings.Index(t, `\E`); i < 0 { - lit = t[2:] - t = "" - } else { - lit = t[2:i] - t = t[i+2:] - } + lit, t, _ = strings.Cut(t[2:], `\E`) for lit != "" { c, rest, err := nextRune(lit) if err != nil { diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go index 5581ba1ca5ee29..1ef6d8a3fe09ba 100644 --- a/src/regexp/syntax/parse_test.go +++ b/src/regexp/syntax/parse_test.go @@ -207,6 +207,11 @@ var parseTests = []parseTest{ // Valid repetitions. {`((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))`, ``}, {`((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})`, ``}, + + // Valid nesting. + {strings.Repeat("(", 999) + strings.Repeat(")", 999), ``}, + {strings.Repeat("(?:", 999) + strings.Repeat(")*", 999), ``}, + {"(" + strings.Repeat("|", 12345) + ")", ``}, // not nested at all } const testFlags = MatchNL | PerlX | UnicodeGroups @@ -482,6 +487,8 @@ var invalidRegexps = []string{ `a{100000}`, `a{100000,}`, "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", + strings.Repeat("(", 1000) + strings.Repeat(")", 1000), + strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), `\Q\E*`, } diff --git a/src/regexp/syntax/prog.go b/src/regexp/syntax/prog.go index ae7a9a2fe0118a..896cdc42c2a4ff 100644 --- a/src/regexp/syntax/prog.go +++ b/src/regexp/syntax/prog.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "unicode" + "unicode/utf8" ) // Compiled program. @@ -101,7 +102,7 @@ func EmptyOpContext(r1, r2 rune) EmptyOp { return op } -// IsWordChar reports whether r is consider a ``word character'' +// IsWordChar reports whether r is considered a “word character” // during the evaluation of the \b and \B zero-width assertions. // These assertions are ASCII-only: the word characters are [A-Za-z0-9_]. func IsWordChar(r rune) bool { @@ -154,7 +155,7 @@ func (p *Prog) Prefix() (prefix string, complete bool) { // Have prefix; gather characters. var buf strings.Builder - for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 { + for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 && i.Rune[0] != utf8.RuneError { buf.WriteRune(i.Rune[0]) i = p.skipNop(i.Out) } diff --git a/src/run.bash b/src/run.bash index 2123c509f8b251..4f4d4373650e6b 100755 --- a/src/run.bash +++ b/src/run.bash @@ -21,14 +21,11 @@ if [ ! -f ../bin/go ]; then exit 1 fi -eval $(../bin/go env) +export GOENV=off +eval $(../bin/go tool dist env) export GOROOT # The api test requires GOROOT to be set, so set it to match ../bin/go. -export GOPATH=/nonexist-gopath unset CDPATH # in case user has it set -export GOBIN=$GOROOT/bin # Issue 14340 -unset GOFLAGS -unset GO111MODULE export GOHOSTOS export CC @@ -53,4 +50,5 @@ if ulimit -T &> /dev/null; then [ "$(ulimit -H -T)" = "unlimited" ] || ulimit -S -T $(ulimit -H -T) fi +export GOPATH=/nonexist-gopath exec ../bin/go tool dist test -rebuild "$@" diff --git a/src/run.bat b/src/run.bat index edcaf5265931b8..35c8ead8cb2247 100644 --- a/src/run.bat +++ b/src/run.bat @@ -18,32 +18,22 @@ setlocal set GOBUILDFAIL=0 -set GOPATH=c:\nonexist-gopath -:: Issue 14340: ignore GOBIN during all.bat. -set GOBIN= -set GOFLAGS= -set GO111MODULE= +set GOENV=off +..\bin\go tool dist env > env.bat +if errorlevel 1 goto fail +call .\env.bat +del env.bat -rem TODO avoid rebuild if possible +set GOPATH=c:\nonexist-gopath if x%1==x--no-rebuild goto norebuild -echo ##### Building packages and commands. -..\bin\go install -a -v std cmd +..\bin\go tool dist test --rebuild if errorlevel 1 goto fail -echo. -:norebuild - -:: get CGO_ENABLED -..\bin\go env > env.bat -if errorlevel 1 goto fail -call env.bat -del env.bat -echo. +goto end +:norebuild ..\bin\go tool dist test if errorlevel 1 goto fail -echo. - goto end :fail diff --git a/src/run.rc b/src/run.rc index a7b4801207051a..704290ca5ed1fd 100755 --- a/src/run.rc +++ b/src/run.rc @@ -10,11 +10,8 @@ if(! test -f ../bin/go){ exit wrongdir } -eval `{../bin/go env} +GOENV=off +eval `{../bin/go tool dist env} GOPATH=/nonexist-gopath -GOBIN=() # Issue 14340 -GOFLAGS=() -GO111MODULE=() - exec ../bin/go tool dist test -rebuild $* diff --git a/src/runtime/HACKING.md b/src/runtime/HACKING.md index fbf22eeb442249..ce0b42a354ae4b 100644 --- a/src/runtime/HACKING.md +++ b/src/runtime/HACKING.md @@ -41,8 +41,20 @@ All `g`, `m`, and `p` objects are heap allocated, but are never freed, so their memory remains type stable. As a result, the runtime can avoid write barriers in the depths of the scheduler. -User stacks and system stacks ------------------------------ +`getg()` and `getg().m.curg` +---------------------------- + +To get the current user `g`, use `getg().m.curg`. + +`getg()` alone returns the current `g`, but when executing on the +system or signal stacks, this will return the current M's "g0" or +"gsignal", respectively. This is usually not what you want. + +To determine if you're running on the user stack or the system stack, +use `getg() == getg().m.curg`. + +Stacks +====== Every non-dead G has a *user stack* associated with it, which is what user Go code executes on. User stacks start small (e.g., 2K) and grow @@ -63,17 +75,33 @@ non-preemptible and the garbage collector does not scan system stacks. While running on the system stack, the current user stack is not used for execution. -`getg()` and `getg().m.curg` ----------------------------- +nosplit functions +----------------- -To get the current user `g`, use `getg().m.curg`. +Most functions start with a prologue that inspects the stack pointer +and the current G's stack bound and calls `morestack` if the stack +needs to grow. -`getg()` alone returns the current `g`, but when executing on the -system or signal stacks, this will return the current M's "g0" or -"gsignal", respectively. This is usually not what you want. +Functions can be marked `//go:nosplit` (or `NOSPLIT` in assembly) to +indicate that they should not get this prologue. This has several +uses: -To determine if you're running on the user stack or the system stack, -use `getg() == getg().m.curg`. +- Functions that must run on the user stack, but must not call into + stack growth, for example because this would cause a deadlock, or + because they have untyped words on the stack. + +- Functions that must not be preempted on entry. + +- Functions that may run without a valid G. For example, functions + that run in early runtime start-up, or that may be entered from C + code such as cgo callbacks or the signal handler. + +Splittable functions ensure there's some amount of space on the stack +for nosplit functions to run in and the linker checks that any static +chain of nosplit function calls cannot exceed this bound. + +Any function with a `//go:nosplit` annotation should explain why it is +nosplit in its documentation comment. Error handling and reporting ============================ @@ -90,8 +118,15 @@ avoid allocating in perilous situations. By convention, additional details are printed before `throw` using `print` or `println` and the messages are prefixed with "runtime:". -For runtime error debugging, it's useful to run with -`GOTRACEBACK=system` or `GOTRACEBACK=crash`. +For unrecoverable errors where user code is expected to be at fault for the +failure (such as racing map writes), use `fatal`. + +For runtime error debugging, it may be useful to run with `GOTRACEBACK=system` +or `GOTRACEBACK=crash`. The output of `panic` and `fatal` is as described by +`GOTRACEBACK`. The output of `throw` always includes runtime frames, metadata +and all goroutines regardless of `GOTRACEBACK` (i.e., equivalent to +`GOTRACEBACK=system`). Whether `throw` crashes or not is still controlled by +`GOTRACEBACK`. Synchronization =============== @@ -200,7 +235,7 @@ There are three mechanisms for allocating unmanaged memory: objects of the same type. In general, types that are allocated using any of these should be -marked `//go:notinheap` (see below). +marked as not in heap by embedding `runtime/internal/sys.NotInHeap`. Objects that are allocated in unmanaged memory **must not** contain heap pointers unless the following rules are also obeyed: @@ -277,36 +312,21 @@ functions that release the P or may run without a P and Since these are function-level annotations, code that releases or acquires a P may need to be split across two functions. -go:notinheap ------------- - -`go:notinheap` applies to type declarations. It indicates that a type -must never be allocated from the GC'd heap or on the stack. -Specifically, pointers to this type must always fail the -`runtime.inheap` check. The type may be used for global variables, or -for objects in unmanaged memory (e.g., allocated with `sysAlloc`, -`persistentalloc`, `fixalloc`, or from a manually-managed span). -Specifically: - -1. `new(T)`, `make([]T)`, `append([]T, ...)` and implicit heap - allocation of T are disallowed. (Though implicit allocations are - disallowed in the runtime anyway.) - -2. A pointer to a regular type (other than `unsafe.Pointer`) cannot be - converted to a pointer to a `go:notinheap` type, even if they have - the same underlying type. - -3. Any type that contains a `go:notinheap` type is itself - `go:notinheap`. Structs and arrays are `go:notinheap` if their - elements are. Maps and channels of `go:notinheap` types are - disallowed. To keep things explicit, any type declaration where the - type is implicitly `go:notinheap` must be explicitly marked - `go:notinheap` as well. - -4. Write barriers on pointers to `go:notinheap` types can be omitted. - -The last point is the real benefit of `go:notinheap`. The runtime uses -it for low-level internal structures to avoid memory barriers in the -scheduler and the memory allocator where they are illegal or simply -inefficient. This mechanism is reasonably safe and does not compromise -the readability of the runtime. +go:uintptrkeepalive +------------------- + +The //go:uintptrkeepalive directive must be followed by a function declaration. + +It specifies that the function's uintptr arguments may be pointer values that +have been converted to uintptr and must be kept alive for the duration of the +call, even though from the types alone it would appear that the object is no +longer needed during the call. + +This directive is similar to //go:uintptrescapes, but it does not force +arguments to escape. Since stack growth does not understand these arguments, +this directive must be used with //go:nosplit (in the marked function and all +transitive calls) to prevent stack growth. + +The conversion from pointer to uintptr must appear in the argument list of any +call to this function. This directive is used for some low-level system call +implementations. diff --git a/src/runtime/abi_test.go b/src/runtime/abi_test.go index f69d3a9d5012d6..0c9488a5f4ee59 100644 --- a/src/runtime/abi_test.go +++ b/src/runtime/abi_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build goexperiment.regabireflect -// +build goexperiment.regabireflect +//go:build goexperiment.regabiargs // This file contains tests specific to making sure the register ABI // works in a bunch of contexts in the runtime. @@ -79,7 +78,7 @@ func TestFinalizerRegisterABI(t *testing.T) { tests := []struct { name string - fin interface{} + fin any confirmValue int }{ {"Pointer", regFinalizerPointer, -1}, diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 39c74268421636..2a413eeef352ce 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -6,13 +6,13 @@ package runtime import ( "internal/cpu" - "runtime/internal/sys" + "internal/goarch" "unsafe" ) const ( - c0 = uintptr((8-sys.PtrSize)/4*2860486313 + (sys.PtrSize-4)/4*33054211828000289) - c1 = uintptr((8-sys.PtrSize)/4*3267000013 + (sys.PtrSize-4)/4*23344194077549503) + c0 = uintptr((8-goarch.PtrSize)/4*2860486313 + (goarch.PtrSize-4)/4*33054211828000289) + c1 = uintptr((8-goarch.PtrSize)/4*3267000013 + (goarch.PtrSize-4)/4*23344194077549503) ) func memhash0(p unsafe.Pointer, h uintptr) uintptr { @@ -182,7 +182,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { if f.name.isBlank() { continue } - h = typehash(f.typ, add(p, f.offset()), h) + h = typehash(f.typ, add(p, f.offset), h) } return h default: @@ -290,7 +290,7 @@ func int64Hash(i uint64, seed uintptr) uintptr { return memhash64(noescape(unsafe.Pointer(&i)), seed) } -func efaceHash(i interface{}, seed uintptr) uintptr { +func efaceHash(i any, seed uintptr) uintptr { return nilinterhash(noescape(unsafe.Pointer(&i)), seed) } @@ -300,7 +300,7 @@ func ifaceHash(i interface { return interhash(noescape(unsafe.Pointer(&i)), seed) } -const hashRandomBytes = sys.PtrSize / 4 * 64 +const hashRandomBytes = goarch.PtrSize / 4 * 64 // used in asm_{386,amd64,arm64}.s to seed the hash function var aeskeysched [hashRandomBytes]byte @@ -321,7 +321,7 @@ func alginit() { initAlgAES() return } - getRandomData((*[len(hashkey) * sys.PtrSize]byte)(unsafe.Pointer(&hashkey))[:]) + getRandomData((*[len(hashkey) * goarch.PtrSize]byte)(unsafe.Pointer(&hashkey))[:]) hashkey[0] |= 1 // make sure these numbers are odd hashkey[1] |= 1 hashkey[2] |= 1 @@ -337,7 +337,7 @@ func initAlgAES() { // Note: These routines perform the read with a native endianness. func readUnaligned32(p unsafe.Pointer) uint32 { q := (*[4]byte)(p) - if sys.BigEndian { + if goarch.BigEndian { return uint32(q[3]) | uint32(q[2])<<8 | uint32(q[1])<<16 | uint32(q[0])<<24 } return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24 @@ -345,7 +345,7 @@ func readUnaligned32(p unsafe.Pointer) uint32 { func readUnaligned64(p unsafe.Pointer) uint64 { q := (*[8]byte)(p) - if sys.BigEndian { + if goarch.BigEndian { return uint64(q[7]) | uint64(q[6])<<8 | uint64(q[5])<<16 | uint64(q[4])<<24 | uint64(q[3])<<32 | uint64(q[2])<<40 | uint64(q[1])<<48 | uint64(q[0])<<56 } diff --git a/src/runtime/align_runtime_test.go b/src/runtime/align_runtime_test.go new file mode 100644 index 00000000000000..d78b0b2d39b261 --- /dev/null +++ b/src/runtime/align_runtime_test.go @@ -0,0 +1,51 @@ +// Copyright 2022 The Go 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 lives in the runtime package +// so we can get access to the runtime guts. +// The rest of the implementation of this test is in align_test.go. + +package runtime + +import "unsafe" + +// AtomicFields is the set of fields on which we perform 64-bit atomic +// operations (all the *64 operations in runtime/internal/atomic). +var AtomicFields = []uintptr{ + unsafe.Offsetof(m{}.procid), + unsafe.Offsetof(p{}.gcFractionalMarkTime), + unsafe.Offsetof(profBuf{}.overflow), + unsafe.Offsetof(profBuf{}.overflowTime), + unsafe.Offsetof(heapStatsDelta{}.tinyAllocCount), + unsafe.Offsetof(heapStatsDelta{}.smallAllocCount), + unsafe.Offsetof(heapStatsDelta{}.smallFreeCount), + unsafe.Offsetof(heapStatsDelta{}.largeAlloc), + unsafe.Offsetof(heapStatsDelta{}.largeAllocCount), + unsafe.Offsetof(heapStatsDelta{}.largeFree), + unsafe.Offsetof(heapStatsDelta{}.largeFreeCount), + unsafe.Offsetof(heapStatsDelta{}.committed), + unsafe.Offsetof(heapStatsDelta{}.released), + unsafe.Offsetof(heapStatsDelta{}.inHeap), + unsafe.Offsetof(heapStatsDelta{}.inStacks), + unsafe.Offsetof(heapStatsDelta{}.inPtrScalarBits), + unsafe.Offsetof(heapStatsDelta{}.inWorkBufs), + unsafe.Offsetof(lfnode{}.next), + unsafe.Offsetof(mstats{}.last_gc_nanotime), + unsafe.Offsetof(mstats{}.last_gc_unix), + unsafe.Offsetof(workType{}.bytesMarked), +} + +// AtomicVariables is the set of global variables on which we perform +// 64-bit atomic operations. +var AtomicVariables = []unsafe.Pointer{ + unsafe.Pointer(&ncgocall), + unsafe.Pointer(&test_z64), + unsafe.Pointer(&blockprofilerate), + unsafe.Pointer(&mutexprofilerate), + unsafe.Pointer(&gcController), + unsafe.Pointer(&memstats), + unsafe.Pointer(&sched), + unsafe.Pointer(&ticks), + unsafe.Pointer(&work), +} diff --git a/src/runtime/align_test.go b/src/runtime/align_test.go new file mode 100644 index 00000000000000..d3bdf007dccf1e --- /dev/null +++ b/src/runtime/align_test.go @@ -0,0 +1,197 @@ +// Copyright 2022 The Go Authors. 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 ( + "go/ast" + "go/build" + "go/importer" + "go/parser" + "go/printer" + "go/token" + "go/types" + "os" + "regexp" + "runtime" + "strings" + "testing" +) + +// Check that 64-bit fields on which we apply atomic operations +// are aligned to 8 bytes. This can be a problem on 32-bit systems. +func TestAtomicAlignment(t *testing.T) { + // Read the code making the tables above, to see which fields and + // variables we are currently checking. + checked := map[string]bool{} + x, err := os.ReadFile("./align_runtime_test.go") + if err != nil { + t.Fatalf("read failed: %v", err) + } + fieldDesc := map[int]string{} + r := regexp.MustCompile(`unsafe[.]Offsetof[(](\w+){}[.](\w+)[)]`) + matches := r.FindAllStringSubmatch(string(x), -1) + for i, v := range matches { + checked["field runtime."+v[1]+"."+v[2]] = true + fieldDesc[i] = v[1] + "." + v[2] + } + varDesc := map[int]string{} + r = regexp.MustCompile(`unsafe[.]Pointer[(]&(\w+)[)]`) + matches = r.FindAllStringSubmatch(string(x), -1) + for i, v := range matches { + checked["var "+v[1]] = true + varDesc[i] = v[1] + } + + // Check all of our alignemnts. This is the actual core of the test. + for i, d := range runtime.AtomicFields { + if d%8 != 0 { + t.Errorf("field alignment of %s failed: offset is %d", fieldDesc[i], d) + } + } + for i, p := range runtime.AtomicVariables { + if uintptr(p)%8 != 0 { + t.Errorf("variable alignment of %s failed: address is %x", varDesc[i], p) + } + } + + // The code above is the actual test. The code below attempts to check + // that the tables used by the code above are exhaustive. + + // Parse the whole runtime package, checking that arguments of + // appropriate atomic operations are in the list above. + fset := token.NewFileSet() + m, err := parser.ParseDir(fset, ".", nil, 0) + if err != nil { + t.Fatalf("parsing runtime failed: %v", err) + } + pkg := m["runtime"] // Note: ignore runtime_test and main packages + + // Filter files by those for the current architecture/os being tested. + fileMap := map[string]bool{} + for _, f := range buildableFiles(t, ".") { + fileMap[f] = true + } + var files []*ast.File + for fname, f := range pkg.Files { + if fileMap[fname] { + files = append(files, f) + } + } + + // Call go/types to analyze the runtime package. + var info types.Info + info.Types = map[ast.Expr]types.TypeAndValue{} + conf := types.Config{Importer: importer.Default()} + _, err = conf.Check("runtime", fset, files, &info) + if err != nil { + t.Fatalf("typechecking runtime failed: %v", err) + } + + // Analyze all atomic.*64 callsites. + v := Visitor{t: t, fset: fset, types: info.Types, checked: checked} + ast.Walk(&v, pkg) +} + +type Visitor struct { + fset *token.FileSet + types map[ast.Expr]types.TypeAndValue + checked map[string]bool + t *testing.T +} + +func (v *Visitor) Visit(n ast.Node) ast.Visitor { + c, ok := n.(*ast.CallExpr) + if !ok { + return v + } + f, ok := c.Fun.(*ast.SelectorExpr) + if !ok { + return v + } + p, ok := f.X.(*ast.Ident) + if !ok { + return v + } + if p.Name != "atomic" { + return v + } + if !strings.HasSuffix(f.Sel.Name, "64") { + return v + } + + a := c.Args[0] + + // This is a call to atomic.XXX64(a, ...). Make sure a is aligned to 8 bytes. + // XXX = one of Load, Store, Cas, etc. + // The arg we care about the alignment of is always the first one. + + if u, ok := a.(*ast.UnaryExpr); ok && u.Op == token.AND { + v.checkAddr(u.X) + return v + } + + // Other cases there's nothing we can check. Assume we're ok. + v.t.Logf("unchecked atomic operation %s %v", v.fset.Position(n.Pos()), v.print(n)) + + return v +} + +// checkAddr checks to make sure n is a properly aligned address for a 64-bit atomic operation. +func (v *Visitor) checkAddr(n ast.Node) { + switch n := n.(type) { + case *ast.IndexExpr: + // Alignment of an array element is the same as the whole array. + v.checkAddr(n.X) + return + case *ast.Ident: + key := "var " + v.print(n) + if !v.checked[key] { + v.t.Errorf("unchecked variable %s %s", v.fset.Position(n.Pos()), key) + } + return + case *ast.SelectorExpr: + t := v.types[n.X].Type + if t == nil { + // Not sure what is happening here, go/types fails to + // type the selector arg on some platforms. + return + } + if p, ok := t.(*types.Pointer); ok { + // Note: we assume here that the pointer p in p.foo is properly + // aligned. We just check that foo is at a properly aligned offset. + t = p.Elem() + } else { + v.checkAddr(n.X) + } + if t.Underlying() == t { + v.t.Errorf("analysis can't handle unnamed type %s %v", v.fset.Position(n.Pos()), t) + } + key := "field " + t.String() + "." + n.Sel.Name + if !v.checked[key] { + v.t.Errorf("unchecked field %s %s", v.fset.Position(n.Pos()), key) + } + default: + v.t.Errorf("unchecked atomic address %s %v", v.fset.Position(n.Pos()), v.print(n)) + + } +} + +func (v *Visitor) print(n ast.Node) string { + var b strings.Builder + printer.Fprint(&b, v.fset, n) + return b.String() +} + +// buildableFiles returns the list of files in the given directory +// that are actually used for the build, given GOOS/GOARCH restrictions. +func buildableFiles(t *testing.T, dir string) []string { + ctxt := build.Default + ctxt.CgoEnabled = true + pkg, err := ctxt.ImportDir(dir, 0) + if err != nil { + t.Fatalf("can't find buildable files: %v", err) + } + return pkg.GoFiles +} diff --git a/src/runtime/asan.go b/src/runtime/asan.go new file mode 100644 index 00000000000000..25b83277e6d7a1 --- /dev/null +++ b/src/runtime/asan.go @@ -0,0 +1,67 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +package runtime + +import ( + "unsafe" +) + +// Public address sanitizer API. +func ASanRead(addr unsafe.Pointer, len int) { + sp := getcallersp() + pc := getcallerpc() + doasanread(addr, uintptr(len), sp, pc) +} + +func ASanWrite(addr unsafe.Pointer, len int) { + sp := getcallersp() + pc := getcallerpc() + doasanwrite(addr, uintptr(len), sp, pc) +} + +// Private interface for the runtime. +const asanenabled = true + +// asan{read,write} are nosplit because they may be called between +// fork and exec, when the stack must not grow. See issue #50391. + +//go:nosplit +func asanread(addr unsafe.Pointer, sz uintptr) { + sp := getcallersp() + pc := getcallerpc() + doasanread(addr, sz, sp, pc) +} + +//go:nosplit +func asanwrite(addr unsafe.Pointer, sz uintptr) { + sp := getcallersp() + pc := getcallerpc() + doasanwrite(addr, sz, sp, pc) +} + +//go:noescape +func doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) + +//go:noescape +func doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) + +//go:noescape +func asanunpoison(addr unsafe.Pointer, sz uintptr) + +//go:noescape +func asanpoison(addr unsafe.Pointer, sz uintptr) + +//go:noescape +func asanregisterglobals(addr unsafe.Pointer, n uintptr) + +// These are called from asan_GOARCH.s +// +//go:cgo_import_static __asan_read_go +//go:cgo_import_static __asan_write_go +//go:cgo_import_static __asan_unpoison_go +//go:cgo_import_static __asan_poison_go +//go:cgo_import_static __asan_register_globals_go diff --git a/src/runtime/asan/asan.go b/src/runtime/asan/asan.go new file mode 100644 index 00000000000000..25f15ae45b6c7b --- /dev/null +++ b/src/runtime/asan/asan.go @@ -0,0 +1,76 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan && linux && (arm64 || amd64 || riscv64 || ppc64le) + +package asan + +/* +#cgo CFLAGS: -fsanitize=address +#cgo LDFLAGS: -fsanitize=address + +#include +#include +#include + +void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc) { + if (__asan_region_is_poisoned(addr, sz)) { + __asan_report_error(pc, 0, sp, addr, false, sz); + } +} + +void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc) { + if (__asan_region_is_poisoned(addr, sz)) { + __asan_report_error(pc, 0, sp, addr, true, sz); + } +} + +void __asan_unpoison_go(void *addr, uintptr_t sz) { + __asan_unpoison_memory_region(addr, sz); +} + +void __asan_poison_go(void *addr, uintptr_t sz) { + __asan_poison_memory_region(addr, sz); +} + +// Keep in sync with the definition in compiler-rt +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_interface_internal.h#L41 +// This structure is used to describe the source location of +// a place where global was defined. +struct _asan_global_source_location { + const char *filename; + int line_no; + int column_no; +}; + +// Keep in sync with the definition in compiler-rt +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_interface_internal.h#L48 +// So far, the current implementation is only compatible with the ASan library from version v7 to v9. +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_init_version.h +// This structure describes an instrumented global variable. +// +// TODO: If a later version of the ASan library changes __asan_global or __asan_global_source_location +// structure, we need to make the same changes. +struct _asan_global { + uintptr_t beg; + uintptr_t size; + uintptr_t size_with_redzone; + const char *name; + const char *module_name; + uintptr_t has_dynamic_init; + struct _asan_global_source_location *location; + uintptr_t odr_indicator; +}; + + +extern void __asan_register_globals(void*, long int); + +// Register global variables. +// The 'globals' is an array of structures describing 'n' globals. +void __asan_register_globals_go(void *addr, uintptr_t n) { + struct _asan_global *globals = (struct _asan_global *)(addr); + __asan_register_globals(globals, n); +} +*/ +import "C" diff --git a/src/runtime/asan0.go b/src/runtime/asan0.go new file mode 100644 index 00000000000000..0948786200abdd --- /dev/null +++ b/src/runtime/asan0.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !asan + +// Dummy ASan support API, used when not built with -asan. + +package runtime + +import ( + "unsafe" +) + +const asanenabled = false + +// Because asanenabled is false, none of these functions should be called. + +func asanread(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanwrite(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanunpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanregisterglobals(addr unsafe.Pointer, sz uintptr) { throw("asan") } diff --git a/src/runtime/asan_amd64.s b/src/runtime/asan_amd64.s new file mode 100644 index 00000000000000..0489aa86dd44dc --- /dev/null +++ b/src/runtime/asan_amd64.s @@ -0,0 +1,91 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +#include "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +// This is like race_amd64.s, but for the asan calls. +// See race_amd64.s for detailed comments. + +#ifdef GOOS_windows +#define RARG0 CX +#define RARG1 DX +#define RARG2 R8 +#define RARG3 R9 +#else +#define RARG0 DI +#define RARG1 SI +#define RARG2 DX +#define RARG3 CX +#endif + +// Called from intrumented code. +// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanread(SB), NOSPLIT, $0-32 + MOVQ addr+0(FP), RARG0 + MOVQ size+8(FP), RARG1 + MOVQ sp+16(FP), RARG2 + MOVQ pc+24(FP), RARG3 + // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVQ $__asan_read_go(SB), AX + JMP asancall<>(SB) + +// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 + MOVQ addr+0(FP), RARG0 + MOVQ size+8(FP), RARG1 + MOVQ sp+16(FP), RARG2 + MOVQ pc+24(FP), RARG3 + // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVQ $__asan_write_go(SB), AX + JMP asancall<>(SB) + +// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 + MOVQ addr+0(FP), RARG0 + MOVQ size+8(FP), RARG1 + // void __asan_unpoison_go(void *addr, uintptr_t sz); + MOVQ $__asan_unpoison_go(SB), AX + JMP asancall<>(SB) + +// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 + MOVQ addr+0(FP), RARG0 + MOVQ size+8(FP), RARG1 + // void __asan_poison_go(void *addr, uintptr_t sz); + MOVQ $__asan_poison_go(SB), AX + JMP asancall<>(SB) + +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOVD $__asan_register_globals_go(SB), AX + JMP asancall<>(SB) + +// Switches SP to g0 stack and calls (AX). Arguments already set. +TEXT asancall<>(SB), NOSPLIT, $0-0 + get_tls(R12) + MOVQ g(R12), R14 + MOVQ SP, R12 // callee-saved, preserved across the CALL + CMPQ R14, $0 + JE call // no g; still on a system stack + + MOVQ g_m(R14), R13 + // Switch to g0 stack. + MOVQ m_g0(R13), R10 + CMPQ R10, R14 + JE call // already on g0 + + MOVQ (g_sched+gobuf_sp)(R10), SP +call: + ANDQ $~15, SP // alignment for gcc ABI + CALL AX + MOVQ R12, SP + RET diff --git a/src/runtime/asan_arm64.s b/src/runtime/asan_arm64.s new file mode 100644 index 00000000000000..697c98206ea569 --- /dev/null +++ b/src/runtime/asan_arm64.s @@ -0,0 +1,76 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +#include "go_asm.h" +#include "textflag.h" + +#define RARG0 R0 +#define RARG1 R1 +#define RARG2 R2 +#define RARG3 R3 +#define FARG R4 + +// Called from instrumented code. +// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanread(SB), NOSPLIT, $0-32 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + MOVD sp+16(FP), RARG2 + MOVD pc+24(FP), RARG3 + // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVD $__asan_read_go(SB), FARG + JMP asancall<>(SB) + +// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + MOVD sp+16(FP), RARG2 + MOVD pc+24(FP), RARG3 + // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVD $__asan_write_go(SB), FARG + JMP asancall<>(SB) + +// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_unpoison_go(void *addr, uintptr_t sz); + MOVD $__asan_unpoison_go(SB), FARG + JMP asancall<>(SB) + +// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_poison_go(void *addr, uintptr_t sz); + MOVD $__asan_poison_go(SB), FARG + JMP asancall<>(SB) + +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOVD $__asan_register_globals_go(SB), FARG + JMP asancall<>(SB) + +// Switches SP to g0 stack and calls (FARG). Arguments already set. +TEXT asancall<>(SB), NOSPLIT, $0-0 + MOVD RSP, R19 // callee-saved + CBZ g, g0stack // no g, still on a system stack + MOVD g_m(g), R10 + MOVD m_g0(R10), R11 + CMP R11, g + BEQ g0stack + + MOVD (g_sched+gobuf_sp)(R11), R5 + MOVD R5, RSP + +g0stack: + BL (FARG) + MOVD R19, RSP + RET diff --git a/src/runtime/asan_ppc64le.s b/src/runtime/asan_ppc64le.s new file mode 100644 index 00000000000000..d13301a1b1d249 --- /dev/null +++ b/src/runtime/asan_ppc64le.s @@ -0,0 +1,87 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +#include "go_asm.h" +#include "textflag.h" + +#define RARG0 R3 +#define RARG1 R4 +#define RARG2 R5 +#define RARG3 R6 +#define FARG R12 + +// Called from instrumented code. +// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanread(SB),NOSPLIT|NOFRAME,$0-32 + MOVD addr+0(FP), RARG0 + MOVD sz+8(FP), RARG1 + MOVD sp+16(FP), RARG2 + MOVD pc+24(FP), RARG3 + // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVD $__asan_read_go(SB), FARG + BR asancall<>(SB) + +// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanwrite(SB),NOSPLIT|NOFRAME,$0-32 + MOVD addr+0(FP), RARG0 + MOVD sz+8(FP), RARG1 + MOVD sp+16(FP), RARG2 + MOVD pc+24(FP), RARG3 + // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); + MOVD $__asan_write_go(SB), FARG + BR asancall<>(SB) + +// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanunpoison(SB),NOSPLIT|NOFRAME,$0-16 + MOVD addr+0(FP), RARG0 + MOVD sz+8(FP), RARG1 + // void __asan_unpoison_go(void *addr, uintptr_t sz); + MOVD $__asan_unpoison_go(SB), FARG + BR asancall<>(SB) + +// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanpoison(SB),NOSPLIT|NOFRAME,$0-16 + MOVD addr+0(FP), RARG0 + MOVD sz+8(FP), RARG1 + // void __asan_poison_go(void *addr, uintptr_t sz); + MOVD $__asan_poison_go(SB), FARG + BR asancall<>(SB) + +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB),NOSPLIT|NOFRAME,$0-16 + MOVD addr+0(FP), RARG0 + MOVD n+8(FP), RARG1 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOVD $__asan_register_globals_go(SB), FARG + BR asancall<>(SB) + +// Switches SP to g0 stack and calls (FARG). Arguments already set. +TEXT asancall<>(SB), NOSPLIT, $0-0 + // LR saved in generated prologue + // Get info from the current goroutine + MOVD runtime·tls_g(SB), R10 // g offset in TLS + MOVD 0(R10), g + MOVD g_m(g), R7 // m for g + MOVD R1, R16 // callee-saved, preserved across C call + MOVD m_g0(R7), R10 // g0 for m + CMP R10, g // same g0? + BEQ call // already on g0 + MOVD (g_sched+gobuf_sp)(R10), R1 // switch R1 +call: + // prepare frame for C ABI + SUB $32, R1 // create frame for callee saving LR, CR, R2 etc. + RLDCR $0, R1, $~15, R1 // align SP to 16 bytes + MOVD FARG, CTR // address of function to be called + MOVD R0, 0(R1) // clear back chain pointer + BL (CTR) + MOVD $0, R0 // C code can clobber R0 set it back to 0 + MOVD R16, R1 // restore R1; + MOVD runtime·tls_g(SB), R10 // find correct g + MOVD 0(R10), g + RET + +// tls_g, g value for each thread in TLS +GLOBL runtime·tls_g+0(SB), TLSBSS+DUPOK, $8 diff --git a/src/runtime/asan_riscv64.s b/src/runtime/asan_riscv64.s new file mode 100644 index 00000000000000..6fcd94d4b1fcf0 --- /dev/null +++ b/src/runtime/asan_riscv64.s @@ -0,0 +1,68 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +#include "go_asm.h" +#include "textflag.h" + +// Called from instrumented code. +// func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanread(SB), NOSPLIT, $0-32 + MOV addr+0(FP), X10 + MOV sz+8(FP), X11 + MOV sp+16(FP), X12 + MOV pc+24(FP), X13 + // void __asan_read_go(void *addr, uintptr_t sz); + MOV $__asan_read_go(SB), X14 + JMP asancall<>(SB) + +// func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) +TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 + MOV addr+0(FP), X10 + MOV sz+8(FP), X11 + MOV sp+16(FP), X12 + MOV pc+24(FP), X13 + // void __asan_write_go(void *addr, uintptr_t sz); + MOV $__asan_write_go(SB), X14 + JMP asancall<>(SB) + +// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 + MOV addr+0(FP), X10 + MOV sz+8(FP), X11 + // void __asan_unpoison_go(void *addr, uintptr_t sz); + MOV $__asan_unpoison_go(SB), X14 + JMP asancall<>(SB) + +// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) +TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 + MOV addr+0(FP), X10 + MOV sz+8(FP), X11 + // void __asan_poison_go(void *addr, uintptr_t sz); + MOV $__asan_poison_go(SB), X14 + JMP asancall<>(SB) + +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 + MOV addr+0(FP), X10 + MOV n+8(FP), X11 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOV $__asan_register_globals_go(SB), X14 + JMP asancall<>(SB) + +// Switches SP to g0 stack and calls (X14). Arguments already set. +TEXT asancall<>(SB), NOSPLIT, $0-0 + MOV X2, X8 // callee-saved + BEQZ g, g0stack // no g, still on a system stack + MOV g_m(g), X21 + MOV m_g0(X21), X21 + BEQ X21, g, g0stack + + MOV (g_sched+gobuf_sp)(X21), X2 + +g0stack: + JALR RA, X14 + MOV X8, X2 + RET diff --git a/src/runtime/asm.s b/src/runtime/asm.s index 72c744925d89ae..84d56de7dd8516 100644 --- a/src/runtime/asm.s +++ b/src/runtime/asm.s @@ -4,15 +4,7 @@ #include "textflag.h" -// funcdata for functions with no local variables in frame. -// Define two zero-length bitmaps, because the same index is used -// for the local variables as for the argument frame, and assembly -// frames have two argument bitmaps, one without results and one with results. -DATA runtime·no_pointers_stackmap+0x00(SB)/4, $2 -DATA runtime·no_pointers_stackmap+0x04(SB)/4, $0 -GLOBL runtime·no_pointers_stackmap(SB),RODATA, $8 - #ifndef GOARCH_amd64 -TEXT ·sigpanic0(SB),NOSPLIT,$0-0 +TEXT ·sigpanic0(SB),NOSPLIT,$0-0 JMP ·sigpanic(SB) #endif diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index ec5ea58028bf0e..e16880c950b847 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -137,9 +137,6 @@ has_cpuid: CMPL AX, $0 JE nocpuinfo - // Figure out how to serialize RDTSC. - // On Intel processors LFENCE is enough. AMD requires MFENCE. - // Don't know about the rest, so let's do MFENCE. CMPL BX, $0x756E6547 // "Genu" JNE notintel CMPL DX, $0x49656E69 // "ineI" @@ -147,7 +144,6 @@ has_cpuid: CMPL CX, $0x6C65746E // "ntel" JNE notintel MOVB $1, runtime·isIntel(SB) - MOVB $1, runtime·lfenceBeforeRdtsc(SB) notintel: // Load EAX=1 cpuid flags @@ -244,10 +240,8 @@ ok: // create a new goroutine to start program PUSHL $runtime·mainPC(SB) // entry - PUSHL $0 // arg size CALL runtime·newproc(SB) POPL AX - POPL AX // start this M CALL runtime·mstart(SB) @@ -584,26 +578,6 @@ TEXT ·publicationBarrier(SB),NOSPLIT,$0-0 // compile barrier. RET -// void jmpdefer(fn, sp); -// called from deferreturn. -// 1. pop the caller -// 2. sub 5 bytes (the length of CALL & a 32 bit displacement) from the callers -// return (when building for shared libraries, subtract 16 bytes -- 5 bytes -// for CALL & displacement to call __x86.get_pc_thunk.cx, 6 bytes for the -// LEAL to load the offset into BX, and finally 5 for the call & displacement) -// 3. jmp to the argument -TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8 - MOVL fv+0(FP), DX // fn - MOVL argp+4(FP), BX // caller sp - LEAL -4(BX), SP // caller sp after CALL -#ifdef GOBUILDMODE_shared - SUBL $16, (SP) // return to CALL again -#else - SUBL $5, (SP) // return to CALL again -#endif - MOVL 0(DX), BX - JMP BX // but first run the deferred function - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -655,18 +629,18 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-12 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. get_tls(CX) - MOVL g(CX), BP - CMPL BP, $0 - JEQ nosave // Don't even have a G yet. - MOVL g_m(BP), BP - MOVL m_g0(BP), SI MOVL g(CX), DI - CMPL SI, DI - JEQ noswitch + CMPL DI, $0 + JEQ nosave // Don't even have a G yet. + MOVL g_m(DI), BP CMPL DI, m_gsignal(BP) JEQ noswitch + MOVL m_g0(BP), SI + CMPL DI, SI + JEQ noswitch CALL gosave_systemstack_switch<>(SB) get_tls(CX) MOVL SI, g(CX) @@ -860,19 +834,37 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0 // func cputicks() int64 TEXT runtime·cputicks(SB),NOSPLIT,$0-8 - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE done - CMPB runtime·lfenceBeforeRdtsc(SB), $1 - JNE mfence - LFENCE - JMP done -mfence: - MFENCE + // LFENCE/MFENCE instruction support is dependent on SSE2. + // When no SSE2 support is present do not enforce any serialization + // since using CPUID to serialize the instruction stream is + // very costly. +#ifdef GO386_softfloat + JMP rdtsc // no fence instructions available +#endif + CMPB internal∕cpu·X86+const_offsetX86HasRDTSCP(SB), $1 + JNE fences + // Instruction stream serializing RDTSCP is supported. + // RDTSCP is supported by Intel Nehalem (2008) and + // AMD K8 Rev. F (2006) and newer. + RDTSCP done: - RDTSC MOVL AX, ret_lo+0(FP) MOVL DX, ret_hi+4(FP) RET +fences: + // MFENCE is instruction stream serializing and flushes the + // store buffers on AMD. The serialization semantics of LFENCE on AMD + // are dependent on MSR C001_1029 and CPU generation. + // LFENCE on Intel does wait for all previous instructions to have executed. + // Intel recommends MFENCE;LFENCE in its manuals before RDTSC to have all + // previous instructions executed and all previous loads and stores to globally visible. + // Using MFENCE;LFENCE here aligns the serializing properties without + // runtime detection of CPU manufacturer. + MFENCE + LFENCE +rdtsc: + RDTSC + JMP done TEXT ldt0setup<>(SB),NOSPLIT,$16-0 // set up ldt 7 to point at m0.tls @@ -945,8 +937,9 @@ aes0to15: PAND masks<>(SB)(BX*8), X1 final1: - AESENC X0, X1 // scramble input, xor in seed - AESENC X1, X1 // scramble combo 2 times + PXOR X0, X1 // xor data with seed + AESENC X1, X1 // scramble combo 3 times + AESENC X1, X1 AESENC X1, X1 MOVL X1, (DX) RET @@ -979,9 +972,13 @@ aes17to32: MOVOU (AX), X2 MOVOU -16(AX)(BX*1), X3 + // xor with seed + PXOR X0, X2 + PXOR X1, X3 + // scramble 3 times - AESENC X0, X2 - AESENC X1, X3 + AESENC X2, X2 + AESENC X3, X3 AESENC X2, X2 AESENC X3, X3 AESENC X2, X2 @@ -1008,10 +1005,15 @@ aes33to64: MOVOU -32(AX)(BX*1), X6 MOVOU -16(AX)(BX*1), X7 - AESENC X0, X4 - AESENC X1, X5 - AESENC X2, X6 - AESENC X3, X7 + PXOR X0, X4 + PXOR X1, X5 + PXOR X2, X6 + PXOR X3, X7 + + AESENC X4, X4 + AESENC X5, X5 + AESENC X6, X6 + AESENC X7, X7 AESENC X4, X4 AESENC X5, X5 @@ -1077,7 +1079,12 @@ aesloop: DECL BX JNE aesloop - // 2 more scrambles to finish + // 3 more scrambles to finish + AESENC X4, X4 + AESENC X5, X5 + AESENC X6, X6 + AESENC X7, X7 + AESENC X4, X4 AESENC X5, X5 AESENC X6, X6 diff --git a/src/runtime/asm_amd64.h b/src/runtime/asm_amd64.h new file mode 100644 index 00000000000000..f7a8896db63348 --- /dev/null +++ b/src/runtime/asm_amd64.h @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Define features that are guaranteed to be supported by setting the AMD64 variable. +// If a feature is supported, there's no need to check it at runtime every time. + +#ifdef GOAMD64_v2 +#define hasPOPCNT +#define hasSSE42 +#endif + +#ifdef GOAMD64_v3 +#define hasAVX +#define hasAVX2 +#define hasPOPCNT +#define hasSSE42 +#endif + +#ifdef GOAMD64_v4 +#define hasAVX +#define hasAVX2 +#define hasPOPCNT +#define hasSSE42 +#endif diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 14f29e19647aa2..d2f798417898bc 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -78,14 +78,92 @@ GLOBL _rt0_amd64_lib_argc<>(SB),NOPTR, $8 DATA _rt0_amd64_lib_argv<>(SB)/8, $0 GLOBL _rt0_amd64_lib_argv<>(SB),NOPTR, $8 +#ifdef GOAMD64_v2 +DATA bad_cpu_msg<>+0x00(SB)/84, $"This program can only be run on AMD64 processors with v2 microarchitecture support.\n" +#endif + +#ifdef GOAMD64_v3 +DATA bad_cpu_msg<>+0x00(SB)/84, $"This program can only be run on AMD64 processors with v3 microarchitecture support.\n" +#endif + +#ifdef GOAMD64_v4 +DATA bad_cpu_msg<>+0x00(SB)/84, $"This program can only be run on AMD64 processors with v4 microarchitecture support.\n" +#endif + +GLOBL bad_cpu_msg<>(SB), RODATA, $84 + +// Define a list of AMD64 microarchitecture level features +// https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels + + // SSE3 SSSE3 CMPXCHNG16 SSE4.1 SSE4.2 POPCNT +#define V2_FEATURES_CX (1 << 0 | 1 << 9 | 1 << 13 | 1 << 19 | 1 << 20 | 1 << 23) + // LAHF/SAHF +#define V2_EXT_FEATURES_CX (1 << 0) + // FMA MOVBE OSXSAVE AVX F16C +#define V3_FEATURES_CX (V2_FEATURES_CX | 1 << 12 | 1 << 22 | 1 << 27 | 1 << 28 | 1 << 29) + // ABM (FOR LZNCT) +#define V3_EXT_FEATURES_CX (V2_EXT_FEATURES_CX | 1 << 5) + // BMI1 AVX2 BMI2 +#define V3_EXT_FEATURES_BX (1 << 3 | 1 << 5 | 1 << 8) + // XMM YMM +#define V3_OS_SUPPORT_AX (1 << 1 | 1 << 2) + +#define V4_FEATURES_CX V3_FEATURES_CX + +#define V4_EXT_FEATURES_CX V3_EXT_FEATURES_CX + // AVX512F AVX512DQ AVX512CD AVX512BW AVX512VL +#define V4_EXT_FEATURES_BX (V3_EXT_FEATURES_BX | 1 << 16 | 1 << 17 | 1 << 28 | 1 << 30 | 1 << 31) + // OPMASK ZMM +#define V4_OS_SUPPORT_AX (V3_OS_SUPPORT_AX | 1 << 5 | (1 << 6 | 1 << 7)) + +#ifdef GOAMD64_v2 +#define NEED_MAX_CPUID 0x80000001 +#define NEED_FEATURES_CX V2_FEATURES_CX +#define NEED_EXT_FEATURES_CX V2_EXT_FEATURES_CX +#endif + +#ifdef GOAMD64_v3 +#define NEED_MAX_CPUID 0x80000001 +#define NEED_FEATURES_CX V3_FEATURES_CX +#define NEED_EXT_FEATURES_CX V3_EXT_FEATURES_CX +#define NEED_EXT_FEATURES_BX V3_EXT_FEATURES_BX +#define NEED_OS_SUPPORT_AX V3_OS_SUPPORT_AX +#endif + +#ifdef GOAMD64_v4 +#define NEED_MAX_CPUID 0x80000001 +#define NEED_FEATURES_CX V4_FEATURES_CX +#define NEED_EXT_FEATURES_CX V4_EXT_FEATURES_CX +#define NEED_EXT_FEATURES_BX V4_EXT_FEATURES_BX + +// Darwin requires a different approach to check AVX512 support, see CL 285572. +#ifdef GOOS_darwin +#define NEED_OS_SUPPORT_AX V3_OS_SUPPORT_AX +// These values are from: +// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h +#define commpage64_base_address 0x00007fffffe00000 +#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010) +#define commpage64_version (commpage64_base_address+0x01E) +#define hasAVX512F 0x0000004000000000 +#define hasAVX512CD 0x0000008000000000 +#define hasAVX512DQ 0x0000010000000000 +#define hasAVX512BW 0x0000020000000000 +#define hasAVX512VL 0x0000100000000000 +#define NEED_DARWIN_SUPPORT (hasAVX512F | hasAVX512DQ | hasAVX512CD | hasAVX512BW | hasAVX512VL) +#else +#define NEED_OS_SUPPORT_AX V4_OS_SUPPORT_AX +#endif + +#endif + TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // copy arguments forward on an even stack MOVQ DI, AX // argc MOVQ SI, BX // argv - SUBQ $(4*8+7), SP // 2args 2auto + SUBQ $(5*8), SP // 3args 2auto ANDQ $~15, SP - MOVQ AX, 16(SP) - MOVQ BX, 24(SP) + MOVQ AX, 24(SP) + MOVQ BX, 32(SP) // create istack out of the given (operating system) stack. // _cgo_init may update stackguard. @@ -99,13 +177,9 @@ TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // find out information about the processor we're on MOVL $0, AX CPUID - MOVL AX, SI CMPL AX, $0 JE nocpuinfo - // Figure out how to serialize RDTSC. - // On Intel processors LFENCE is enough. AMD requires MFENCE. - // Don't know about the rest, so let's do MFENCE. CMPL BX, $0x756E6547 // "Genu" JNE notintel CMPL DX, $0x49656E69 // "ineI" @@ -113,9 +187,8 @@ TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 CMPL CX, $0x6C65746E // "ntel" JNE notintel MOVB $1, runtime·isIntel(SB) - MOVB $1, runtime·lfenceBeforeRdtsc(SB) -notintel: +notintel: // Load EAX=1 cpuid flags MOVL $1, AX CPUID @@ -201,11 +274,71 @@ ok: MOVQ AX, g_m(CX) CLD // convention is D is always left cleared + + // Check GOAMD64 reqirements + // We need to do this after setting up TLS, so that + // we can report an error if there is a failure. See issue 49586. +#ifdef NEED_FEATURES_CX + MOVL $0, AX + CPUID + CMPL AX, $0 + JE bad_cpu + MOVL $1, AX + CPUID + ANDL $NEED_FEATURES_CX, CX + CMPL CX, $NEED_FEATURES_CX + JNE bad_cpu +#endif + +#ifdef NEED_MAX_CPUID + MOVL $0x80000000, AX + CPUID + CMPL AX, $NEED_MAX_CPUID + JL bad_cpu +#endif + +#ifdef NEED_EXT_FEATURES_BX + MOVL $7, AX + MOVL $0, CX + CPUID + ANDL $NEED_EXT_FEATURES_BX, BX + CMPL BX, $NEED_EXT_FEATURES_BX + JNE bad_cpu +#endif + +#ifdef NEED_EXT_FEATURES_CX + MOVL $0x80000001, AX + CPUID + ANDL $NEED_EXT_FEATURES_CX, CX + CMPL CX, $NEED_EXT_FEATURES_CX + JNE bad_cpu +#endif + +#ifdef NEED_OS_SUPPORT_AX + XORL CX, CX + XGETBV + ANDL $NEED_OS_SUPPORT_AX, AX + CMPL AX, $NEED_OS_SUPPORT_AX + JNE bad_cpu +#endif + +#ifdef NEED_DARWIN_SUPPORT + MOVQ $commpage64_version, BX + CMPW (BX), $13 // cpu_capabilities64 undefined in versions < 13 + JL bad_cpu + MOVQ $commpage64_cpu_capabilities64, BX + MOVQ (BX), BX + MOVQ $NEED_DARWIN_SUPPORT, CX + ANDQ CX, BX + CMPQ BX, CX + JNE bad_cpu +#endif + CALL runtime·check(SB) - MOVL 16(SP), AX // copy argc + MOVL 24(SP), AX // copy argc MOVL AX, 0(SP) - MOVQ 24(SP), AX // copy argv + MOVQ 32(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) @@ -214,10 +347,8 @@ ok: // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry PUSHQ AX - PUSHQ $0 // arg size CALL runtime·newproc(SB) POPQ AX - POPQ AX // start this M CALL runtime·mstart(SB) @@ -225,6 +356,17 @@ ok: CALL runtime·abort(SB) // mstart should never return RET +bad_cpu: // show that the program requires a certain microarchitecture level. + MOVQ $2, 0(SP) + MOVQ $bad_cpu_msg<>(SB), AX + MOVQ AX, 8(SP) + MOVQ $84, 16(SP) + CALL runtime·write(SB) + MOVQ $1, 0(SP) + CALL runtime·exit(SB) + CALL runtime·abort(SB) + RET + // Prevent dead-code elimination of debugCallV2, which is // intended to be called by debuggers. MOVQ $runtime·debugCallV2(SB), AX @@ -279,7 +421,6 @@ TEXT gogo<>(SB), NOSPLIT, $0 // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. -#ifdef GOEXPERIMENT_regabiargs TEXT runtime·mcall(SB), NOSPLIT, $0-8 MOVQ AX, DX // DX = fn @@ -308,38 +449,6 @@ goodm: POPQ AX JMP runtime·badmcall2(SB) RET -#else -TEXT runtime·mcall(SB), NOSPLIT, $0-8 - MOVQ fn+0(FP), DI - - get_tls(CX) - MOVQ g(CX), AX // save state in g->sched - MOVQ 0(SP), BX // caller's PC - MOVQ BX, (g_sched+gobuf_pc)(AX) - LEAQ fn+0(FP), BX // caller's SP - MOVQ BX, (g_sched+gobuf_sp)(AX) - MOVQ BP, (g_sched+gobuf_bp)(AX) - - // switch to m->g0 & its stack, call fn - MOVQ g(CX), BX - MOVQ g_m(BX), BX - MOVQ m_g0(BX), SI - CMPQ SI, AX // if g == m->g0 call badmcall - JNE 3(PC) - MOVQ $runtime·badmcall(SB), AX - JMP AX - MOVQ SI, g(CX) // g = m->g0 - MOVQ SI, R14 // set the g register - MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp - PUSHQ AX - MOVQ DI, DX - MOVQ 0(DI), DI - CALL DI - POPQ AX - MOVQ $runtime·badmcall2(SB), AX - JMP AX - RET -#endif // systemstack_switch is a dummy routine that systemstack leaves at the bottom // of the G stack. We need to distinguish the routine that @@ -467,9 +576,8 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0 MOVL $0, DX JMP runtime·morestack(SB) -#ifdef GOEXPERIMENT_regabireflect // spillArgs stores return values from registers to a *internal/abi.RegArgs in R12. -TEXT ·spillArgs(SB),NOSPLIT,$0-0 +TEXT ·spillArgs(SB),NOSPLIT,$0-0 MOVQ AX, 0(R12) MOVQ BX, 8(R12) MOVQ CX, 16(R12) @@ -497,7 +605,7 @@ TEXT ·spillArgs(SB),NOSPLIT,$0-0 RET // unspillArgs loads args into registers from a *internal/abi.RegArgs in R12. -TEXT ·unspillArgs(SB),NOSPLIT,$0-0 +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 MOVQ 0(R12), AX MOVQ 8(R12), BX MOVQ 16(R12), CX @@ -523,15 +631,6 @@ TEXT ·unspillArgs(SB),NOSPLIT,$0-0 MOVQ 176(R12), X13 MOVQ 184(R12), X14 RET -#else -// spillArgs stores return values from registers to a pointer in R12. -TEXT ·spillArgs(SB),NOSPLIT,$0-0 - RET - -// unspillArgs loads args into registers from a pointer in R12. -TEXT ·unspillArgs(SB),NOSPLIT,$0-0 - RET -#endif // reflectcall: call a function with the given argument list // func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs). @@ -588,7 +687,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ REP;MOVSB; \ /* set up argument registers */ \ MOVQ regArgs+40(FP), R12; \ - CALL ·unspillArgs(SB); \ + CALL ·unspillArgs(SB); \ /* call function */ \ MOVQ f+8(FP), DX; \ PCDATA $PCDATA_StackMapIndex, $0; \ @@ -596,7 +695,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ CALL R12; \ /* copy register return values back */ \ MOVQ regArgs+40(FP), R12; \ - CALL ·spillArgs(SB); \ + CALL ·spillArgs(SB); \ MOVLQZX stackArgsSize+24(FP), CX; \ MOVLQZX stackRetOffset+28(FP), BX; \ MOVQ stackArgs+16(FP), DI; \ @@ -664,31 +763,12 @@ TEXT ·publicationBarrier(SB),NOSPLIT,$0-0 // compile barrier. RET -// func jmpdefer(fv *funcval, argp uintptr) -// argp is a caller SP. -// called from deferreturn. -// 1. pop the caller -// 2. sub 5 bytes from the callers return -// 3. jmp to the argument -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 - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) // or else unwinding from systemstack_switch is incorrect. // Smashes R9. TEXT gosave_systemstack_switch<>(SB),NOSPLIT,$0 -#ifndef GOEXPERIMENT_regabig - get_tls(R14) - MOVQ g(R14), R14 -#endif MOVQ $runtime·systemstack_switch(SB), R9 MOVQ R9, (g_sched+gobuf_pc)(R14) LEAQ 8(SP), R9 @@ -731,22 +811,21 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. get_tls(CX) - MOVQ g(CX), R8 - CMPQ R8, $0 - JEQ nosave - MOVQ g_m(R8), R8 - MOVQ m_g0(R8), SI MOVQ g(CX), DI - CMPQ SI, DI + CMPQ DI, $0 JEQ nosave + MOVQ g_m(DI), R8 MOVQ m_gsignal(R8), SI - CMPQ SI, DI + CMPQ DI, SI + JEQ nosave + MOVQ m_g0(R8), SI + CMPQ DI, SI JEQ nosave // Switch to system stack. - MOVQ m_g0(R8), SI CALL gosave_systemstack_switch<>(SB) MOVQ SI, g(CX) MOVQ (g_sched+gobuf_sp)(SI), SP @@ -993,77 +1072,62 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0 // func cputicks() int64 TEXT runtime·cputicks(SB),NOSPLIT,$0-0 - CMPB runtime·lfenceBeforeRdtsc(SB), $1 - JNE mfence - LFENCE - JMP done -mfence: - MFENCE + CMPB internal∕cpu·X86+const_offsetX86HasRDTSCP(SB), $1 + JNE fences + // Instruction stream serializing RDTSCP is supported. + // RDTSCP is supported by Intel Nehalem (2008) and + // AMD K8 Rev. F (2006) and newer. + RDTSCP done: - RDTSC SHLQ $32, DX ADDQ DX, AX MOVQ AX, ret+0(FP) RET +fences: + // MFENCE is instruction stream serializing and flushes the + // store buffers on AMD. The serialization semantics of LFENCE on AMD + // are dependent on MSR C001_1029 and CPU generation. + // LFENCE on Intel does wait for all previous instructions to have executed. + // Intel recommends MFENCE;LFENCE in its manuals before RDTSC to have all + // previous instructions executed and all previous loads and stores to globally visible. + // Using MFENCE;LFENCE here aligns the serializing properties without + // runtime detection of CPU manufacturer. + MFENCE + LFENCE + RDTSC + JMP done // func memhash(p unsafe.Pointer, h, s uintptr) uintptr // hash function using AES hardware instructions TEXT runtime·memhash(SB),NOSPLIT,$0-32 -#ifdef GOEXPERIMENT_regabiargs // AX = ptr to data // BX = seed // CX = size -#endif CMPB runtime·useAeshash(SB), $0 JEQ noaes -#ifndef GOEXPERIMENT_regabiargs - MOVQ p+0(FP), AX // ptr to data - MOVQ s+16(FP), CX // size - LEAQ ret+24(FP), DX -#endif JMP aeshashbody<>(SB) noaes: JMP runtime·memhashFallback(SB) // func strhash(p unsafe.Pointer, h uintptr) uintptr TEXT runtime·strhash(SB),NOSPLIT,$0-24 -#ifdef GOEXPERIMENT_regabiargs // AX = ptr to string struct // BX = seed -#endif CMPB runtime·useAeshash(SB), $0 JEQ noaes -#ifndef GOEXPERIMENT_regabiargs - MOVQ p+0(FP), AX // ptr to string struct -#endif MOVQ 8(AX), CX // length of string MOVQ (AX), AX // string data -#ifndef GOEXPERIMENT_regabiargs - LEAQ ret+16(FP), DX -#endif JMP aeshashbody<>(SB) noaes: JMP runtime·strhashFallback(SB) // AX: data -#ifdef GOEXPERIMENT_regabiargs // BX: hash seed -#else -// h+8(FP): hash seed -#endif // CX: length -#ifdef GOEXPERIMENT_regabiargs // At return: AX = return value -#else -// DX: address to put return value -#endif TEXT aeshashbody<>(SB),NOSPLIT,$0-0 // Fill an SSE register with our seeds. -#ifdef GOEXPERIMENT_regabiargs MOVQ BX, X0 // 64 bits of per-table hash seed -#else - MOVQ h+8(FP), X0 // 64 bits of per-table hash seed -#endif PINSRW $4, CX, X0 // 16 bits of length PSHUFHW $0, X0, X0 // repeat length 4 times total MOVO X0, X1 // save unscrambled seed @@ -1100,11 +1164,7 @@ final1: AESENC X1, X1 // scramble combo 3 times AESENC X1, X1 AESENC X1, X1 -#ifdef GOEXPERIMENT_regabiargs MOVQ X1, AX // return X1 -#else - MOVQ X1, (DX) -#endif RET endofpage: @@ -1120,11 +1180,7 @@ endofpage: aes0: // Return scrambled input seed AESENC X0, X0 -#ifdef GOEXPERIMENT_regabiargs MOVQ X0, AX // return X0 -#else - MOVQ X0, (DX) -#endif RET aes16: @@ -1154,11 +1210,7 @@ aes17to32: // combine results PXOR X3, X2 -#ifdef GOEXPERIMENT_regabiargs MOVQ X2, AX // return X2 -#else - MOVQ X2, (DX) -#endif RET aes33to64: @@ -1200,11 +1252,7 @@ aes33to64: PXOR X6, X4 PXOR X7, X5 PXOR X5, X4 -#ifdef GOEXPERIMENT_regabiargs MOVQ X4, AX // return X4 -#else - MOVQ X4, (DX) -#endif RET aes65to128: @@ -1286,15 +1334,9 @@ aes65to128: PXOR X10, X8 PXOR X11, X9 PXOR X9, X8 -#ifdef GOEXPERIMENT_regabig // X15 must be zero on return PXOR X15, X15 -#endif -#ifdef GOEXPERIMENT_regabiargs MOVQ X8, AX // return X8 -#else - MOVQ X8, (DX) -#endif RET aes129plus: @@ -1410,41 +1452,24 @@ aesloop: PXOR X10, X8 PXOR X11, X9 PXOR X9, X8 -#ifdef GOEXPERIMENT_regabig // X15 must be zero on return PXOR X15, X15 -#endif -#ifdef GOEXPERIMENT_regabiargs MOVQ X8, AX // return X8 -#else - MOVQ X8, (DX) -#endif RET // func memhash32(p unsafe.Pointer, h uintptr) uintptr // ABIInternal for performance. TEXT runtime·memhash32(SB),NOSPLIT,$0-24 -#ifdef GOEXPERIMENT_regabiargs // AX = ptr to data // BX = seed -#endif CMPB runtime·useAeshash(SB), $0 JEQ noaes -#ifdef GOEXPERIMENT_regabiargs MOVQ BX, X0 // X0 = seed -#else - MOVQ p+0(FP), AX // ptr to data - MOVQ h+8(FP), X0 // seed -#endif PINSRD $2, (AX), X0 // data AESENC runtime·aeskeysched+0(SB), X0 AESENC runtime·aeskeysched+16(SB), X0 AESENC runtime·aeskeysched+32(SB), X0 -#ifdef GOEXPERIMENT_regabiargs MOVQ X0, AX // return X0 -#else - MOVQ X0, ret+16(FP) -#endif RET noaes: JMP runtime·memhash32Fallback(SB) @@ -1452,28 +1477,16 @@ noaes: // func memhash64(p unsafe.Pointer, h uintptr) uintptr // ABIInternal for performance. TEXT runtime·memhash64(SB),NOSPLIT,$0-24 -#ifdef GOEXPERIMENT_regabiargs // AX = ptr to data // BX = seed -#else -#endif CMPB runtime·useAeshash(SB), $0 JEQ noaes -#ifdef GOEXPERIMENT_regabiargs MOVQ BX, X0 // X0 = seed -#else - MOVQ p+0(FP), AX // ptr to data - MOVQ h+8(FP), X0 // seed -#endif PINSRQ $1, (AX), X0 // data AESENC runtime·aeskeysched+0(SB), X0 AESENC runtime·aeskeysched+16(SB), X0 AESENC runtime·aeskeysched+32(SB), X0 -#ifdef GOEXPERIMENT_regabiargs MOVQ X0, AX // return X0 -#else - MOVQ X0, ret+16(FP) -#endif RET noaes: JMP runtime·memhash64Fallback(SB) @@ -1596,10 +1609,10 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 // This function is injected from the signal handler for panicking // signals. It is quite painful to set X15 in the signal context, // so we do it here. -TEXT ·sigpanic0(SB),NOSPLIT,$0-0 -#ifdef GOEXPERIMENT_regabig +TEXT ·sigpanic0(SB),NOSPLIT,$0-0 get_tls(R14) MOVQ g(R14), R14 +#ifndef GOOS_plan9 XORPS X15, X15 #endif JMP ·sigpanic(SB) @@ -1619,13 +1632,7 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112 MOVQ R13, 104(SP) // TODO: Consider passing g.m.p in as an argument so they can be shared // across a sequence of write barriers. -#ifdef GOEXPERIMENT_regabig MOVQ g_m(R14), R13 -#else - get_tls(R13) - MOVQ g(R13), R13 - MOVQ g_m(R13), R13 -#endif MOVQ m_p(R13), R13 MOVQ (p_wbBuf+wbBuf_next)(R13), R12 // Increment wbBuf.next position. @@ -1768,7 +1775,7 @@ GLOBL debugCallFrameTooLarge<>(SB), RODATA, $20 // Size duplicated below // 2. Push the current PC on the stack (updating SP). // 3. Write the desired argument frame size at SP-16 (using the SP // after step 2). -// 4. Save all machine registers (including flags and XMM reigsters) +// 4. Save all machine registers (including flags and XMM registers) // so they can be restored later by the debugger. // 5. Set the PC to debugCallV2 and resume execution. // @@ -1956,146 +1963,61 @@ TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 // The tail call makes these stubs disappear in backtraces. // Defined as ABIInternal since they do not use the stack-based Go ABI. TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicIndex(SB) TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicIndexU(SB) TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSliceAlen(SB) TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSliceAlenU(SB) TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSliceAcap(SB) TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSliceAcapU(SB) TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicSliceB(SB) TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicSliceBU(SB) TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ DX, AX -#else - MOVQ DX, x+0(FP) - MOVQ BX, y+8(FP) -#endif JMP runtime·goPanicSlice3Alen(SB) TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ DX, AX -#else - MOVQ DX, x+0(FP) - MOVQ BX, y+8(FP) -#endif JMP runtime·goPanicSlice3AlenU(SB) TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ DX, AX -#else - MOVQ DX, x+0(FP) - MOVQ BX, y+8(FP) -#endif JMP runtime·goPanicSlice3Acap(SB) TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ DX, AX -#else - MOVQ DX, x+0(FP) - MOVQ BX, y+8(FP) -#endif JMP runtime·goPanicSlice3AcapU(SB) TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSlice3B(SB) TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, AX MOVQ DX, BX -#else - MOVQ CX, x+0(FP) - MOVQ DX, y+8(FP) -#endif JMP runtime·goPanicSlice3BU(SB) TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicSlice3C(SB) TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ CX, BX -#else - MOVQ AX, x+0(FP) - MOVQ CX, y+8(FP) -#endif JMP runtime·goPanicSlice3CU(SB) TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ DX, AX -#else - MOVQ DX, x+0(FP) - MOVQ BX, y+8(FP) -#endif JMP runtime·goPanicSliceConvert(SB) #ifdef GOOS_android diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 6d3573d68fb0ef..591ef2a399fb76 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -168,14 +168,13 @@ TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 BL runtime·schedinit(SB) // create a new goroutine to start program + SUB $8, R13 MOVW $runtime·mainPC(SB), R0 - MOVW.W R0, -4(R13) - MOVW $8, R0 - MOVW.W R0, -4(R13) + MOVW R0, 4(R13) // arg 1: fn MOVW $0, R0 - MOVW.W R0, -4(R13) // push $0 as guard + MOVW R0, 0(R13) // dummy LR BL runtime·newproc(SB) - MOVW $12(R13), R13 // pop args and LR + ADD $8, R13 // pop args and LR // start this M BL runtime·mstart(SB) @@ -388,6 +387,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 RET TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R3), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVW R13, R13 + MOVW $0, R7 B runtime·morestack(SB) @@ -507,20 +513,6 @@ CALLFN(·call268435456, 268435456) CALLFN(·call536870912, 536870912) CALLFN(·call1073741824, 1073741824) -// void jmpdefer(fn, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 4 bytes to get back to BL deferreturn -// 3. B to fn -TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8 - MOVW 0(R13), LR - MOVW $-4(LR), LR // BL deferreturn - MOVW fv+0(FP), R7 - MOVW argp+4(FP), R13 - MOVW $-4(R13), R13 // SP is 4 below argp, due to saved LR - MOVW 0(R7), R1 - B (R1) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -571,7 +563,8 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-12 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVW g_m(g), R8 MOVW m_gsignal(R8), R3 CMP R3, g diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index 2d495397a8d0ae..7eb5bcfd21502b 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -87,27 +87,38 @@ nocgo: // create a new goroutine to start program MOVD $runtime·mainPC(SB), R0 // entry - MOVD RSP, R7 - MOVD.W $0, -8(R7) - MOVD.W R0, -8(R7) - MOVD.W $0, -8(R7) - MOVD.W $0, -8(R7) - MOVD R7, RSP + SUB $16, RSP + MOVD R0, 8(RSP) // arg + MOVD $0, 0(RSP) // dummy LR BL runtime·newproc(SB) - ADD $32, RSP + ADD $16, RSP // start this M BL runtime·mstart(SB) + // Prevent dead-code elimination of debugCallV2, which is + // intended to be called by debuggers. + MOVD $runtime·debugCallV2(SB), R0 + MOVD $0, R0 MOVD R0, (R0) // boom UNDEF -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 +// Windows ARM64 needs an immediate 0xf000 argument. +// See go.dev/issues/53837. +#define BREAK \ +#ifdef GOOS_windows \ + BRK $0xf000 \ +#else \ + BRK \ +#endif \ + + TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 - BRK + BREAK RET TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 @@ -152,7 +163,9 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0 // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. -TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 + MOVD R0, R26 // context + // Save caller state in g->sched MOVD RSP, R0 MOVD R0, (g_sched+gobuf_sp)(g) @@ -168,14 +181,14 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 CMP g, R3 BNE 2(PC) B runtime·badmcall(SB) - MOVD fn+0(FP), R26 // context - MOVD 0(R26), R4 // code pointer + MOVD (g_sched+gobuf_sp)(g), R0 MOVD R0, RSP // sp = m->g0->sched.sp MOVD (g_sched+gobuf_bp)(g), R29 - MOVD R3, -8(RSP) - MOVD $0, -16(RSP) + MOVD R3, R0 // arg = g + MOVD $0, -16(RSP) // dummy LR SUB $16, RSP + MOVD 0(R26), R4 // code pointer BL (R4) B runtime·badmcall2(SB) @@ -307,9 +320,56 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 UNDEF TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R3), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVD RSP, RSP + MOVW $0, R26 B runtime·morestack(SB) +// spillArgs stores return values from registers to a *internal/abi.RegArgs in R20. +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + STP (R0, R1), (0*8)(R20) + STP (R2, R3), (2*8)(R20) + STP (R4, R5), (4*8)(R20) + STP (R6, R7), (6*8)(R20) + STP (R8, R9), (8*8)(R20) + STP (R10, R11), (10*8)(R20) + STP (R12, R13), (12*8)(R20) + STP (R14, R15), (14*8)(R20) + FSTPD (F0, F1), (16*8)(R20) + FSTPD (F2, F3), (18*8)(R20) + FSTPD (F4, F5), (20*8)(R20) + FSTPD (F6, F7), (22*8)(R20) + FSTPD (F8, F9), (24*8)(R20) + FSTPD (F10, F11), (26*8)(R20) + FSTPD (F12, F13), (28*8)(R20) + FSTPD (F14, F15), (30*8)(R20) + RET + +// unspillArgs loads args into registers from a *internal/abi.RegArgs in R20. +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + LDP (0*8)(R20), (R0, R1) + LDP (2*8)(R20), (R2, R3) + LDP (4*8)(R20), (R4, R5) + LDP (6*8)(R20), (R6, R7) + LDP (8*8)(R20), (R8, R9) + LDP (10*8)(R20), (R10, R11) + LDP (12*8)(R20), (R12, R13) + LDP (14*8)(R20), (R14, R15) + FLDPD (16*8)(R20), (F0, F1) + FLDPD (18*8)(R20), (F2, F3) + FLDPD (20*8)(R20), (F4, F5) + FLDPD (22*8)(R20), (F6, F7) + FLDPD (24*8)(R20), (F8, F9) + FLDPD (26*8)(R20), (F10, F11) + FLDPD (28*8)(R20), (F12, F13) + FLDPD (30*8)(R20), (F14, F15) + RET + // reflectcall: call a function with the given argument list // func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs). // we don't have variable-sized frames, so we use a small number @@ -381,12 +441,17 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ MOVBU.P R7, 1(R5); \ CMP R5, R6; \ BNE -3(PC); \ + /* set up argument registers */ \ + MOVD regArgs+40(FP), R20; \ + CALL ·unspillArgs(SB); \ /* call function */ \ MOVD f+8(FP), R26; \ - MOVD (R26), R0; \ - PCDATA $PCDATA_StackMapIndex, $0; \ - BL (R0); \ + MOVD (R26), R20; \ + PCDATA $PCDATA_StackMapIndex, $0; \ + BL (R20); \ /* copy return values back */ \ + MOVD regArgs+40(FP), R20; \ + CALL ·spillArgs(SB); \ MOVD stackArgsType+0(FP), R7; \ MOVD stackArgs+16(FP), R3; \ MOVWU stackArgsSize+24(FP), R4; \ @@ -403,11 +468,10 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ // to reflectcallmove. It does not follow the Go ABI; it expects its // arguments in registers. TEXT callRet<>(SB), NOSPLIT, $48-0 - MOVD R7, 8(RSP) - MOVD R3, 16(RSP) - MOVD R5, 24(RSP) - MOVD R4, 32(RSP) - MOVD $0, 40(RSP) + NO_LOCAL_POINTERS + STP (R7, R3), 8(RSP) + STP (R5, R4), 24(RSP) + MOVD R20, 40(RSP) BL runtime·reflectcallmove(SB) RET @@ -440,12 +504,9 @@ CALLFN(·call536870912, 536870912) CALLFN(·call1073741824, 1073741824) // func memhash32(p unsafe.Pointer, h uintptr) uintptr -TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 - MOVB runtime·useAeshash(SB), R0 - CBZ R0, noaes - MOVD p+0(FP), R0 - MOVD h+8(FP), R1 - MOVD $ret+16(FP), R2 +TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 + MOVB runtime·useAeshash(SB), R10 + CBZ R10, noaes MOVD $runtime·aeskeysched+0(SB), R3 VEOR V0.B16, V0.B16, V0.B16 @@ -459,18 +520,15 @@ TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 AESMC V0.B16, V0.B16 AESE V2.B16, V0.B16 - VST1 [V0.D1], (R2) + VMOV V0.D[0], R0 RET noaes: - B runtime·memhash32Fallback(SB) + B runtime·memhash32Fallback(SB) // func memhash64(p unsafe.Pointer, h uintptr) uintptr -TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 - MOVB runtime·useAeshash(SB), R0 - CBZ R0, noaes - MOVD p+0(FP), R0 - MOVD h+8(FP), R1 - MOVD $ret+16(FP), R2 +TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 + MOVB runtime·useAeshash(SB), R10 + CBZ R10, noaes MOVD $runtime·aeskeysched+0(SB), R3 VEOR V0.B16, V0.B16, V0.B16 @@ -484,75 +542,68 @@ TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 AESMC V0.B16, V0.B16 AESE V2.B16, V0.B16 - VST1 [V0.D1], (R2) + VMOV V0.D[0], R0 RET noaes: - B runtime·memhash64Fallback(SB) + B runtime·memhash64Fallback(SB) // func memhash(p unsafe.Pointer, h, size uintptr) uintptr -TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 - MOVB runtime·useAeshash(SB), R0 - CBZ R0, noaes - MOVD p+0(FP), R0 - MOVD s+16(FP), R1 - MOVD h+8(FP), R3 - MOVD $ret+24(FP), R2 +TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 + MOVB runtime·useAeshash(SB), R10 + CBZ R10, noaes B aeshashbody<>(SB) noaes: - B runtime·memhashFallback(SB) + B runtime·memhashFallback(SB) // func strhash(p unsafe.Pointer, h uintptr) uintptr -TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 - MOVB runtime·useAeshash(SB), R0 - CBZ R0, noaes - MOVD p+0(FP), R10 // string pointer - LDP (R10), (R0, R1) //string data/ length - MOVD h+8(FP), R3 - MOVD $ret+16(FP), R2 // return adddress +TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 + MOVB runtime·useAeshash(SB), R10 + CBZ R10, noaes + LDP (R0), (R0, R2) // string data / length B aeshashbody<>(SB) noaes: - B runtime·strhashFallback(SB) + B runtime·strhashFallback(SB) // R0: data -// R1: length -// R2: address to put return value -// R3: seed data +// R1: seed data +// R2: length +// At return, R0 = return value TEXT aeshashbody<>(SB),NOSPLIT|NOFRAME,$0 VEOR V30.B16, V30.B16, V30.B16 - VMOV R3, V30.D[0] - VMOV R1, V30.D[1] // load length into seed + VMOV R1, V30.D[0] + VMOV R2, V30.D[1] // load length into seed MOVD $runtime·aeskeysched+0(SB), R4 VLD1.P 16(R4), [V0.B16] AESE V30.B16, V0.B16 AESMC V0.B16, V0.B16 - CMP $16, R1 + CMP $16, R2 BLO aes0to15 BEQ aes16 - CMP $32, R1 + CMP $32, R2 BLS aes17to32 - CMP $64, R1 + CMP $64, R2 BLS aes33to64 - CMP $128, R1 + CMP $128, R2 BLS aes65to128 B aes129plus aes0to15: - CBZ R1, aes0 + CBZ R2, aes0 VEOR V2.B16, V2.B16, V2.B16 - TBZ $3, R1, less_than_8 + TBZ $3, R2, less_than_8 VLD1.P 8(R0), V2.D[0] less_than_8: - TBZ $2, R1, less_than_4 + TBZ $2, R2, less_than_4 VLD1.P 4(R0), V2.S[2] less_than_4: - TBZ $1, R1, less_than_2 + TBZ $1, R2, less_than_2 VLD1.P 2(R0), V2.H[6] less_than_2: - TBZ $0, R1, done + TBZ $0, R2, done VLD1 (R0), V2.B[14] done: AESE V0.B16, V2.B16 @@ -561,11 +612,13 @@ done: AESMC V2.B16, V2.B16 AESE V0.B16, V2.B16 - VST1 [V2.D1], (R2) + VMOV V2.D[0], R0 RET + aes0: - VST1 [V0.D1], (R2) + VMOV V0.D[0], R0 RET + aes16: VLD1 (R0), [V2.B16] B done @@ -575,7 +628,7 @@ aes17to32: VLD1 (R4), [V1.B16] AESE V30.B16, V1.B16 AESMC V1.B16, V1.B16 - SUB $16, R1, R10 + SUB $16, R2, R10 VLD1.P (R0)(R10), [V2.B16] VLD1 (R0), [V3.B16] @@ -593,7 +646,8 @@ aes17to32: AESE V1.B16, V3.B16 VEOR V3.B16, V2.B16, V2.B16 - VST1 [V2.D1], (R2) + + VMOV V2.D[0], R0 RET aes33to64: @@ -604,7 +658,7 @@ aes33to64: AESMC V2.B16, V2.B16 AESE V30.B16, V3.B16 AESMC V3.B16, V3.B16 - SUB $32, R1, R10 + SUB $32, R2, R10 VLD1.P (R0)(R10), [V4.B16, V5.B16] VLD1 (R0), [V6.B16, V7.B16] @@ -636,7 +690,7 @@ aes33to64: VEOR V7.B16, V5.B16, V5.B16 VEOR V5.B16, V4.B16, V4.B16 - VST1 [V4.D1], (R2) + VMOV V4.D[0], R0 RET aes65to128: @@ -657,7 +711,7 @@ aes65to128: AESE V30.B16, V7.B16 AESMC V7.B16, V7.B16 - SUB $64, R1, R10 + SUB $64, R2, R10 VLD1.P (R0)(R10), [V8.B16, V9.B16, V10.B16, V11.B16] VLD1 (R0), [V12.B16, V13.B16, V14.B16, V15.B16] AESE V0.B16, V8.B16 @@ -711,7 +765,7 @@ aes65to128: VEOR V11.B16, V9.B16, V9.B16 VEOR V9.B16, V8.B16, V8.B16 - VST1 [V8.D1], (R2) + VMOV V8.D[0], R0 RET aes129plus: @@ -732,12 +786,12 @@ aes129plus: AESMC V6.B16, V6.B16 AESE V30.B16, V7.B16 AESMC V7.B16, V7.B16 - ADD R0, R1, R10 + ADD R0, R2, R10 SUB $128, R10, R10 VLD1.P 64(R10), [V8.B16, V9.B16, V10.B16, V11.B16] VLD1 (R10), [V12.B16, V13.B16, V14.B16, V15.B16] - SUB $1, R1, R1 - LSR $7, R1, R1 + SUB $1, R2, R2 + LSR $7, R2, R2 aesloop: AESE V8.B16, V0.B16 @@ -776,8 +830,8 @@ aesloop: AESMC V6.B16, V6.B16 AESE V15.B16, V7.B16 AESMC V7.B16, V7.B16 - SUB $1, R1, R1 - CBNZ R1, aesloop + SUB $1, R2, R2 + CBNZ R2, aesloop AESE V8.B16, V0.B16 AESMC V0.B16, V0.B16 @@ -830,7 +884,7 @@ aesloop: VEOR V4.B16, V6.B16, V4.B16 VEOR V4.B16, V0.B16, V0.B16 - VST1 [V0.D1], (R2) + VMOV V0.D[0], R0 RET TEXT runtime·procyield(SB),NOSPLIT,$0-0 @@ -841,23 +895,6 @@ again: CBNZ R0, again RET -// void jmpdefer(fv, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 4 bytes to get back to BL deferreturn -// 3. BR to fn -TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16 - MOVD 0(RSP), R0 - SUB $4, R0 - MOVD R0, LR - - MOVD fv+0(FP), R26 - MOVD argp+8(FP), R0 - MOVD R0, RSP - SUB $8, RSP - MOVD 0(R26), R3 - B (R3) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -903,7 +940,8 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVD g_m(g), R8 MOVD m_gsignal(R8), R3 CMP R3, g @@ -1052,7 +1090,8 @@ havem: MOVD R1, 8(RSP) MOVD R2, 16(RSP) MOVD R3, 24(RSP) - BL runtime·cgocallbackg(SB) + MOVD $runtime·cgocallbackg(SB), R0 + CALL (R0) // indirect call to bypass nosplit check. We're on a different stack now. // Restore g->sched (== m->curg->sched) from saved values. MOVD 0(RSP), R5 @@ -1158,10 +1197,12 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // It does not clobber any general-purpose registers, // but may clobber others (e.g., floating point registers) // The act of CALLing gcWriteBarrier will clobber R30 (LR). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$200 +// +// Defined as ABIInternal since the compiler generates ABIInternal +// calls to it directly and it does not use the stack-based Go ABI. +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$200 // Save the registers clobbered by the fast path. - MOVD R0, 184(RSP) - MOVD R1, 192(RSP) + STP (R0, R1), 184(RSP) MOVD g_m(g), R0 MOVD m_p(R0), R0 MOVD (p_wbBuf+wbBuf_next)(R0), R1 @@ -1177,8 +1218,7 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$200 // Is the buffer full? (flags set in CMP above) BEQ flush ret: - MOVD 184(RSP), R0 - MOVD 192(RSP), R1 + LDP 184(RSP), (R0, R1) // Do the write. MOVD R3, (R2) RET @@ -1186,32 +1226,20 @@ ret: flush: // Save all general purpose registers since these could be // clobbered by wbBufFlush and were not saved by the caller. - MOVD R2, 8(RSP) // Also first argument to wbBufFlush - MOVD R3, 16(RSP) // Also second argument to wbBufFlush - // R0 already saved - // R1 already saved - MOVD R4, 24(RSP) - MOVD R5, 32(RSP) - MOVD R6, 40(RSP) - MOVD R7, 48(RSP) - MOVD R8, 56(RSP) - MOVD R9, 64(RSP) - MOVD R10, 72(RSP) - MOVD R11, 80(RSP) - MOVD R12, 88(RSP) - MOVD R13, 96(RSP) - MOVD R14, 104(RSP) - MOVD R15, 112(RSP) + // R0 and R1 already saved + STP (R2, R3), 1*8(RSP) // Also first and second arguments to wbBufFlush + STP (R4, R5), 3*8(RSP) + STP (R6, R7), 5*8(RSP) + STP (R8, R9), 7*8(RSP) + STP (R10, R11), 9*8(RSP) + STP (R12, R13), 11*8(RSP) + STP (R14, R15), 13*8(RSP) // R16, R17 may be clobbered by linker trampoline // R18 is unused. - MOVD R19, 120(RSP) - MOVD R20, 128(RSP) - MOVD R21, 136(RSP) - MOVD R22, 144(RSP) - MOVD R23, 152(RSP) - MOVD R24, 160(RSP) - MOVD R25, 168(RSP) - MOVD R26, 176(RSP) + STP (R19, R20), 15*8(RSP) + STP (R21, R22), 17*8(RSP) + STP (R23, R24), 19*8(RSP) + STP (R25, R26), 21*8(RSP) // R27 is temp register. // R28 is g. // R29 is frame pointer (unused). @@ -1220,101 +1248,278 @@ flush: // This takes arguments R2 and R3. CALL runtime·wbBufFlush(SB) - - MOVD 8(RSP), R2 - MOVD 16(RSP), R3 - MOVD 24(RSP), R4 - MOVD 32(RSP), R5 - MOVD 40(RSP), R6 - MOVD 48(RSP), R7 - MOVD 56(RSP), R8 - MOVD 64(RSP), R9 - MOVD 72(RSP), R10 - MOVD 80(RSP), R11 - MOVD 88(RSP), R12 - MOVD 96(RSP), R13 - MOVD 104(RSP), R14 - MOVD 112(RSP), R15 - MOVD 120(RSP), R19 - MOVD 128(RSP), R20 - MOVD 136(RSP), R21 - MOVD 144(RSP), R22 - MOVD 152(RSP), R23 - MOVD 160(RSP), R24 - MOVD 168(RSP), R25 - MOVD 176(RSP), R26 + LDP 1*8(RSP), (R2, R3) + LDP 3*8(RSP), (R4, R5) + LDP 5*8(RSP), (R6, R7) + LDP 7*8(RSP), (R8, R9) + LDP 9*8(RSP), (R10, R11) + LDP 11*8(RSP), (R12, R13) + LDP 13*8(RSP), (R14, R15) + LDP 15*8(RSP), (R19, R20) + LDP 17*8(RSP), (R21, R22) + LDP 19*8(RSP), (R23, R24) + LDP 21*8(RSP), (R25, R26) JMP ret +DATA debugCallFrameTooLarge<>+0x00(SB)/20, $"call frame too large" +GLOBL debugCallFrameTooLarge<>(SB), RODATA, $20 // Size duplicated below + +// debugCallV2 is the entry point for debugger-injected function +// calls on running goroutines. It informs the runtime that a +// debug call has been injected and creates a call frame for the +// debugger to fill in. +// +// To inject a function call, a debugger should: +// 1. Check that the goroutine is in state _Grunning and that +// there are at least 288 bytes free on the stack. +// 2. Set SP as SP-16. +// 3. Store the current LR in (SP) (using the SP after step 2). +// 4. Store the current PC in the LR register. +// 5. Write the desired argument frame size at SP-16 +// 6. Save all machine registers (including flags and fpsimd registers) +// so they can be restored later by the debugger. +// 7. Set the PC to debugCallV2 and resume execution. +// +// If the goroutine is in state _Grunnable, then it's not generally +// safe to inject a call because it may return out via other runtime +// operations. Instead, the debugger should unwind the stack to find +// the return to non-runtime code, add a temporary breakpoint there, +// and inject the call once that breakpoint is hit. +// +// If the goroutine is in any other state, it's not safe to inject a call. +// +// This function communicates back to the debugger by setting R20 and +// invoking BRK to raise a breakpoint signal. Note that the signal PC of +// the signal triggered by the BRK instruction is the PC where the signal +// is trapped, not the next PC, so to resume execution, the debugger needs +// to set the signal PC to PC+4. See the comments in the implementation for +// the protocol the debugger is expected to follow. InjectDebugCall in the +// runtime tests demonstrates this protocol. +// +// The debugger must ensure that any pointers passed to the function +// obey escape analysis requirements. Specifically, it must not pass +// a stack pointer to an escaping argument. debugCallV2 cannot check +// this invariant. +// +// This is ABIInternal because Go code injects its PC directly into new +// goroutine stacks. +TEXT runtime·debugCallV2(SB),NOSPLIT|NOFRAME,$0-0 + STP (R29, R30), -280(RSP) + SUB $272, RSP, RSP + SUB $8, RSP, R29 + // Save all registers that may contain pointers so they can be + // conservatively scanned. + // + // We can't do anything that might clobber any of these + // registers before this. + STP (R27, g), (30*8)(RSP) + STP (R25, R26), (28*8)(RSP) + STP (R23, R24), (26*8)(RSP) + STP (R21, R22), (24*8)(RSP) + STP (R19, R20), (22*8)(RSP) + STP (R16, R17), (20*8)(RSP) + STP (R14, R15), (18*8)(RSP) + STP (R12, R13), (16*8)(RSP) + STP (R10, R11), (14*8)(RSP) + STP (R8, R9), (12*8)(RSP) + STP (R6, R7), (10*8)(RSP) + STP (R4, R5), (8*8)(RSP) + STP (R2, R3), (6*8)(RSP) + STP (R0, R1), (4*8)(RSP) + + // Perform a safe-point check. + MOVD R30, 8(RSP) // Caller's PC + CALL runtime·debugCallCheck(SB) + MOVD 16(RSP), R0 + CBZ R0, good + + // The safety check failed. Put the reason string at the top + // of the stack. + MOVD R0, 8(RSP) + MOVD 24(RSP), R0 + MOVD R0, 16(RSP) + + // Set R20 to 8 and invoke BRK. The debugger should get the + // reason a call can't be injected from SP+8 and resume execution. + MOVD $8, R20 + BREAK + JMP restore + +good: + // Registers are saved and it's safe to make a call. + // Open up a call frame, moving the stack if necessary. + // + // Once the frame is allocated, this will set R20 to 0 and + // invoke BRK. The debugger should write the argument + // frame for the call at SP+8, set up argument registers, + // set the LR as the signal PC + 4, set the PC to the function + // to call, set R26 to point to the closure (if a closure call), + // and resume execution. + // + // If the function returns, this will set R20 to 1 and invoke + // BRK. The debugger can then inspect any return value saved + // on the stack at SP+8 and in registers. To resume execution, + // the debugger should restore the LR from (SP). + // + // If the function panics, this will set R20 to 2 and invoke BRK. + // The interface{} value of the panic will be at SP+8. The debugger + // can inspect the panic value and resume execution again. +#define DEBUG_CALL_DISPATCH(NAME,MAXSIZE) \ + CMP $MAXSIZE, R0; \ + BGT 5(PC); \ + MOVD $NAME(SB), R0; \ + MOVD R0, 8(RSP); \ + CALL runtime·debugCallWrap(SB); \ + JMP restore + + MOVD 256(RSP), R0 // the argument frame size + DEBUG_CALL_DISPATCH(debugCall32<>, 32) + DEBUG_CALL_DISPATCH(debugCall64<>, 64) + DEBUG_CALL_DISPATCH(debugCall128<>, 128) + DEBUG_CALL_DISPATCH(debugCall256<>, 256) + DEBUG_CALL_DISPATCH(debugCall512<>, 512) + DEBUG_CALL_DISPATCH(debugCall1024<>, 1024) + DEBUG_CALL_DISPATCH(debugCall2048<>, 2048) + DEBUG_CALL_DISPATCH(debugCall4096<>, 4096) + DEBUG_CALL_DISPATCH(debugCall8192<>, 8192) + DEBUG_CALL_DISPATCH(debugCall16384<>, 16384) + DEBUG_CALL_DISPATCH(debugCall32768<>, 32768) + DEBUG_CALL_DISPATCH(debugCall65536<>, 65536) + // The frame size is too large. Report the error. + MOVD $debugCallFrameTooLarge<>(SB), R0 + MOVD R0, 8(RSP) + MOVD $20, R0 + MOVD R0, 16(RSP) // length of debugCallFrameTooLarge string + MOVD $8, R20 + BREAK + JMP restore + +restore: + // Calls and failures resume here. + // + // Set R20 to 16 and invoke BRK. The debugger should restore + // all registers except for PC and RSP and resume execution. + MOVD $16, R20 + BREAK + // We must not modify flags after this point. + + // Restore pointer-containing registers, which may have been + // modified from the debugger's copy by stack copying. + LDP (30*8)(RSP), (R27, g) + LDP (28*8)(RSP), (R25, R26) + LDP (26*8)(RSP), (R23, R24) + LDP (24*8)(RSP), (R21, R22) + LDP (22*8)(RSP), (R19, R20) + LDP (20*8)(RSP), (R16, R17) + LDP (18*8)(RSP), (R14, R15) + LDP (16*8)(RSP), (R12, R13) + LDP (14*8)(RSP), (R10, R11) + LDP (12*8)(RSP), (R8, R9) + LDP (10*8)(RSP), (R6, R7) + LDP (8*8)(RSP), (R4, R5) + LDP (6*8)(RSP), (R2, R3) + LDP (4*8)(RSP), (R0, R1) + + LDP -8(RSP), (R29, R27) + ADD $288, RSP, RSP // Add 16 more bytes, see saveSigContext + MOVD -16(RSP), R30 // restore old lr + JMP (R27) + +// runtime.debugCallCheck assumes that functions defined with the +// DEBUG_CALL_FN macro are safe points to inject calls. +#define DEBUG_CALL_FN(NAME,MAXSIZE) \ +TEXT NAME(SB),WRAPPER,$MAXSIZE-0; \ + NO_LOCAL_POINTERS; \ + MOVD $0, R20; \ + BREAK; \ + MOVD $1, R20; \ + BREAK; \ + RET +DEBUG_CALL_FN(debugCall32<>, 32) +DEBUG_CALL_FN(debugCall64<>, 64) +DEBUG_CALL_FN(debugCall128<>, 128) +DEBUG_CALL_FN(debugCall256<>, 256) +DEBUG_CALL_FN(debugCall512<>, 512) +DEBUG_CALL_FN(debugCall1024<>, 1024) +DEBUG_CALL_FN(debugCall2048<>, 2048) +DEBUG_CALL_FN(debugCall4096<>, 4096) +DEBUG_CALL_FN(debugCall8192<>, 8192) +DEBUG_CALL_FN(debugCall16384<>, 16384) +DEBUG_CALL_FN(debugCall32768<>, 32768) +DEBUG_CALL_FN(debugCall65536<>, 65536) + +// func debugCallPanicked(val interface{}) +TEXT runtime·debugCallPanicked(SB),NOSPLIT,$16-16 + // Copy the panic value to the top of stack at SP+8. + MOVD val_type+0(FP), R0 + MOVD R0, 8(RSP) + MOVD val_data+8(FP), R0 + MOVD R0, 16(RSP) + MOVD $2, R20 + BREAK + RET + // Note: these functions use a special calling convention to save generated code space. // Arguments are passed in registers, but the space for those arguments are allocated // in the caller's stack frame. These stubs write the args into that stack space and // then tail call to the corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 - MOVD R2, x+0(FP) - MOVD R3, y+8(FP) - JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 - MOVD R2, x+0(FP) - MOVD R3, y+8(FP) - JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 - MOVD R2, x+0(FP) - MOVD R3, y+8(FP) - JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 - MOVD R2, x+0(FP) - MOVD R3, y+8(FP) - JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 - MOVD R1, x+0(FP) - MOVD R2, y+8(FP) - JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 - MOVD R0, x+0(FP) - MOVD R1, y+8(FP) - JMP runtime·goPanicSlice3CU(SB) -TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 - MOVD R2, x+0(FP) - MOVD R3, y+8(FP) - JMP runtime·goPanicSliceConvert(SB) +// +// Defined as ABIInternal since the compiler generates ABIInternal +// calls to it directly and it does not use the stack-based Go ABI. +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 + MOVD R2, R0 + MOVD R3, R1 + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 + MOVD R2, R0 + MOVD R3, R1 + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 + MOVD R2, R0 + MOVD R3, R1 + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 + MOVD R2, R0 + MOVD R3, R1 + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 + MOVD R1, R0 + MOVD R2, R1 + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 + MOVD R2, R0 + MOVD R3, R1 + JMP runtime·goPanicSliceConvert(SB) diff --git a/src/runtime/asm_loong64.s b/src/runtime/asm_loong64.s new file mode 100644 index 00000000000000..a6ccd196c9a365 --- /dev/null +++ b/src/runtime/asm_loong64.s @@ -0,0 +1,792 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +#define REGCTXT R29 + +TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 + // R3 = stack; R4 = argc; R5 = argv + + ADDV $-24, R3 + MOVW R4, 8(R3) // argc + MOVV R5, 16(R3) // argv + + // create istack out of the given (operating system) stack. + // _cgo_init may update stackguard. + MOVV $runtime·g0(SB), g + MOVV $(-64*1024), R30 + ADDV R30, R3, R19 + MOVV R19, g_stackguard0(g) + MOVV R19, g_stackguard1(g) + MOVV R19, (g_stack+stack_lo)(g) + MOVV R3, (g_stack+stack_hi)(g) + + // if there is a _cgo_init, call it using the gcc ABI. + MOVV _cgo_init(SB), R25 + BEQ R25, nocgo + + MOVV R0, R7 // arg 3: not used + MOVV R0, R6 // arg 2: not used + MOVV $setg_gcc<>(SB), R5 // arg 1: setg + MOVV g, R4 // arg 0: G + JAL (R25) + +nocgo: + // update stackguard after _cgo_init + MOVV (g_stack+stack_lo)(g), R19 + ADDV $const__StackGuard, R19 + MOVV R19, g_stackguard0(g) + MOVV R19, g_stackguard1(g) + + // set the per-goroutine and per-mach "registers" + MOVV $runtime·m0(SB), R19 + + // save m->g0 = g0 + MOVV g, m_g0(R19) + // save m0 to g0->m + MOVV R19, g_m(g) + + JAL runtime·check(SB) + + // args are already prepared + JAL runtime·args(SB) + JAL runtime·osinit(SB) + JAL runtime·schedinit(SB) + + // create a new goroutine to start program + MOVV $runtime·mainPC(SB), R19 // entry + ADDV $-16, R3 + MOVV R19, 8(R3) + MOVV R0, 0(R3) + JAL runtime·newproc(SB) + ADDV $16, R3 + + // start this M + JAL runtime·mstart(SB) + + MOVV R0, 1(R0) + RET + +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +GLOBL runtime·mainPC(SB),RODATA,$8 + +TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 + BREAK + RET + +TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 + RET + +TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0 + JAL runtime·mstart0(SB) + RET // not reached + +/* + * go-routine + */ + +// void gogo(Gobuf*) +// restore state from Gobuf; longjmp +TEXT runtime·gogo(SB), NOSPLIT|NOFRAME, $0-8 + MOVV buf+0(FP), R4 + MOVV gobuf_g(R4), R5 + MOVV 0(R5), R0 // make sure g != nil + JMP gogo<>(SB) + +TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0 + MOVV R5, g + JAL runtime·save_g(SB) + + MOVV gobuf_sp(R4), R3 + MOVV gobuf_lr(R4), R1 + MOVV gobuf_ret(R4), R19 + MOVV gobuf_ctxt(R4), REGCTXT + MOVV R0, gobuf_sp(R4) + MOVV R0, gobuf_ret(R4) + MOVV R0, gobuf_lr(R4) + MOVV R0, gobuf_ctxt(R4) + MOVV gobuf_pc(R4), R6 + JMP (R6) + +// void mcall(fn func(*g)) +// Switch to m->g0's stack, call fn(g). +// Fn must never return. It should gogo(&g->sched) +// to keep running g. +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 + // Save caller state in g->sched + MOVV R3, (g_sched+gobuf_sp)(g) + MOVV R1, (g_sched+gobuf_pc)(g) + MOVV R0, (g_sched+gobuf_lr)(g) + MOVV g, (g_sched+gobuf_g)(g) + + // Switch to m->g0 & its stack, call fn. + MOVV g, R19 + MOVV g_m(g), R4 + MOVV m_g0(R4), g + JAL runtime·save_g(SB) + BNE g, R19, 2(PC) + JMP runtime·badmcall(SB) + MOVV fn+0(FP), REGCTXT // context + MOVV 0(REGCTXT), R5 // code pointer + MOVV (g_sched+gobuf_sp)(g), R3 // sp = m->g0->sched.sp + ADDV $-16, R3 + MOVV R19, 8(R3) + MOVV R0, 0(R3) + JAL (R5) + JMP runtime·badmcall2(SB) + +// systemstack_switch is a dummy routine that systemstack leaves at the bottom +// of the G stack. We need to distinguish the routine that +// lives at the bottom of the G stack from the one that lives +// at the top of the system stack because the one at the top of +// the system stack terminates the stack walk (see topofstack()). +TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0 + UNDEF + JAL (R1) // make sure this function is not leaf + RET + +// func systemstack(fn func()) +TEXT runtime·systemstack(SB), NOSPLIT, $0-8 + MOVV fn+0(FP), R19 // R19 = fn + MOVV R19, REGCTXT // context + MOVV g_m(g), R4 // R4 = m + + MOVV m_gsignal(R4), R5 // R5 = gsignal + BEQ g, R5, noswitch + + MOVV m_g0(R4), R5 // R5 = g0 + BEQ g, R5, noswitch + + MOVV m_curg(R4), R6 + BEQ g, R6, switch + + // Bad: g is not gsignal, not g0, not curg. What is it? + // Hide call from linker nosplit analysis. + MOVV $runtime·badsystemstack(SB), R7 + JAL (R7) + JAL runtime·abort(SB) + +switch: + // save our state in g->sched. Pretend to + // be systemstack_switch if the G stack is scanned. + JAL gosave_systemstack_switch<>(SB) + + // switch to g0 + MOVV R5, g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R19 + // make it look like mstart called systemstack on g0, to stop traceback + ADDV $-8, R19 + MOVV $runtime·mstart(SB), R6 + MOVV R6, 0(R19) + MOVV R19, R3 + + // call target function + MOVV 0(REGCTXT), R6 // code pointer + JAL (R6) + + // switch back to g + MOVV g_m(g), R4 + MOVV m_curg(R4), g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R3 + MOVV R0, (g_sched+gobuf_sp)(g) + RET + +noswitch: + // already on m stack, just call directly + // Using a tail call here cleans up tracebacks since we won't stop + // at an intermediate systemstack. + MOVV 0(REGCTXT), R4 // code pointer + MOVV 0(R3), R1 // restore LR + ADDV $8, R3 + JMP (R4) + +/* + * support for morestack + */ + +// Called during function prolog when more stack is needed. +// Caller has already loaded: +// loong64: R5: LR +// +// The traceback routines see morestack on a g0 as being +// the top of a stack (for example, morestack calling newstack +// calling the scheduler calling newm calling gc), so we must +// record an argument size. For that purpose, it has no arguments. +TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 + // Cannot grow scheduler stack (m->g0). + MOVV g_m(g), R7 + MOVV m_g0(R7), R8 + BNE g, R8, 3(PC) + JAL runtime·badmorestackg0(SB) + JAL runtime·abort(SB) + + // Cannot grow signal stack (m->gsignal). + MOVV m_gsignal(R7), R8 + BNE g, R8, 3(PC) + JAL runtime·badmorestackgsignal(SB) + JAL runtime·abort(SB) + + // Called from f. + // Set g->sched to context in f. + MOVV R3, (g_sched+gobuf_sp)(g) + MOVV R1, (g_sched+gobuf_pc)(g) + MOVV R5, (g_sched+gobuf_lr)(g) + MOVV REGCTXT, (g_sched+gobuf_ctxt)(g) + + // Called from f. + // Set m->morebuf to f's caller. + MOVV R5, (m_morebuf+gobuf_pc)(R7) // f's caller's PC + MOVV R3, (m_morebuf+gobuf_sp)(R7) // f's caller's SP + MOVV g, (m_morebuf+gobuf_g)(R7) + + // Call newstack on m->g0's stack. + MOVV m_g0(R7), g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R3 + // Create a stack frame on g0 to call newstack. + MOVV R0, -8(R3) // Zero saved LR in frame + ADDV $-8, R3 + JAL runtime·newstack(SB) + + // Not reached, but make sure the return PC from the call to newstack + // is still in this function, and not the beginning of the next. + UNDEF + +TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + MOVV R0, REGCTXT + JMP runtime·morestack(SB) + +// reflectcall: call a function with the given argument list +// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32). +// we don't have variable-sized frames, so we use a small number +// of constant-sized-frame functions to encode a few bits of size in the pc. +// Caution: ugly multiline assembly macros in your future! + +#define DISPATCH(NAME,MAXSIZE) \ + MOVV $MAXSIZE, R30; \ + SGTU R19, R30, R30; \ + BNE R30, 3(PC); \ + MOVV $NAME(SB), R4; \ + JMP (R4) +// Note: can't just "BR NAME(SB)" - bad inlining results. + +TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48 + MOVWU stackArgsSize+24(FP), R19 + DISPATCH(runtime·call32, 32) + DISPATCH(runtime·call64, 64) + DISPATCH(runtime·call128, 128) + DISPATCH(runtime·call256, 256) + DISPATCH(runtime·call512, 512) + DISPATCH(runtime·call1024, 1024) + DISPATCH(runtime·call2048, 2048) + DISPATCH(runtime·call4096, 4096) + DISPATCH(runtime·call8192, 8192) + DISPATCH(runtime·call16384, 16384) + DISPATCH(runtime·call32768, 32768) + DISPATCH(runtime·call65536, 65536) + DISPATCH(runtime·call131072, 131072) + DISPATCH(runtime·call262144, 262144) + DISPATCH(runtime·call524288, 524288) + DISPATCH(runtime·call1048576, 1048576) + DISPATCH(runtime·call2097152, 2097152) + DISPATCH(runtime·call4194304, 4194304) + DISPATCH(runtime·call8388608, 8388608) + DISPATCH(runtime·call16777216, 16777216) + DISPATCH(runtime·call33554432, 33554432) + DISPATCH(runtime·call67108864, 67108864) + DISPATCH(runtime·call134217728, 134217728) + DISPATCH(runtime·call268435456, 268435456) + DISPATCH(runtime·call536870912, 536870912) + DISPATCH(runtime·call1073741824, 1073741824) + MOVV $runtime·badreflectcall(SB), R4 + JMP (R4) + +#define CALLFN(NAME,MAXSIZE) \ +TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ + NO_LOCAL_POINTERS; \ + /* copy arguments to stack */ \ + MOVV arg+16(FP), R4; \ + MOVWU argsize+24(FP), R5; \ + MOVV R3, R12; \ + ADDV $8, R12; \ + ADDV R12, R5; \ + BEQ R12, R5, 6(PC); \ + MOVBU (R4), R6; \ + ADDV $1, R4; \ + MOVBU R6, (R12); \ + ADDV $1, R12; \ + JMP -5(PC); \ + /* call function */ \ + MOVV f+8(FP), REGCTXT; \ + MOVV (REGCTXT), R6; \ + PCDATA $PCDATA_StackMapIndex, $0; \ + JAL (R6); \ + /* copy return values back */ \ + MOVV argtype+0(FP), R7; \ + MOVV arg+16(FP), R4; \ + MOVWU n+24(FP), R5; \ + MOVWU retoffset+28(FP), R6; \ + ADDV $8, R3, R12; \ + ADDV R6, R12; \ + ADDV R6, R4; \ + SUBVU R6, R5; \ + JAL callRet<>(SB); \ + RET + +// callRet copies return values back at the end of call*. This is a +// separate function so it can allocate stack space for the arguments +// to reflectcallmove. It does not follow the Go ABI; it expects its +// arguments in registers. +TEXT callRet<>(SB), NOSPLIT, $32-0 + MOVV R7, 8(R3) + MOVV R4, 16(R3) + MOVV R12, 24(R3) + MOVV R5, 32(R3) + JAL runtime·reflectcallmove(SB) + RET + +CALLFN(·call16, 16) +CALLFN(·call32, 32) +CALLFN(·call64, 64) +CALLFN(·call128, 128) +CALLFN(·call256, 256) +CALLFN(·call512, 512) +CALLFN(·call1024, 1024) +CALLFN(·call2048, 2048) +CALLFN(·call4096, 4096) +CALLFN(·call8192, 8192) +CALLFN(·call16384, 16384) +CALLFN(·call32768, 32768) +CALLFN(·call65536, 65536) +CALLFN(·call131072, 131072) +CALLFN(·call262144, 262144) +CALLFN(·call524288, 524288) +CALLFN(·call1048576, 1048576) +CALLFN(·call2097152, 2097152) +CALLFN(·call4194304, 4194304) +CALLFN(·call8388608, 8388608) +CALLFN(·call16777216, 16777216) +CALLFN(·call33554432, 33554432) +CALLFN(·call67108864, 67108864) +CALLFN(·call134217728, 134217728) +CALLFN(·call268435456, 268435456) +CALLFN(·call536870912, 536870912) +CALLFN(·call1073741824, 1073741824) + +TEXT runtime·procyield(SB),NOSPLIT,$0-0 + RET + +// Save state of caller into g->sched. +// but using fake PC from systemstack_switch. +// Must only be called from functions with no locals ($0) +// or else unwinding from systemstack_switch is incorrect. +// Smashes R19. +TEXT gosave_systemstack_switch<>(SB),NOSPLIT|NOFRAME,$0 + MOVV $runtime·systemstack_switch(SB), R19 + ADDV $8, R19 + MOVV R19, (g_sched+gobuf_pc)(g) + MOVV R3, (g_sched+gobuf_sp)(g) + MOVV R0, (g_sched+gobuf_lr)(g) + MOVV R0, (g_sched+gobuf_ret)(g) + // Assert ctxt is zero. See func save. + MOVV (g_sched+gobuf_ctxt)(g), R19 + BEQ R19, 2(PC) + JAL runtime·abort(SB) + RET + +// func asmcgocall(fn, arg unsafe.Pointer) int32 +// Call fn(arg) on the scheduler stack, +// aligned appropriately for the gcc ABI. +// See cgocall.go for more details. +TEXT ·asmcgocall(SB),NOSPLIT,$0-20 + MOVV fn+0(FP), R25 + MOVV arg+8(FP), R4 + + MOVV R3, R12 // save original stack pointer + MOVV g, R13 + + // Figure out if we need to switch to m->g0 stack. + // We get called to create new OS threads too, and those + // come in on the m->g0 stack already. + MOVV g_m(g), R5 + MOVV m_gsignal(R5), R6 + BEQ R6, g, g0 + MOVV m_g0(R5), R6 + BEQ R6, g, g0 + + JAL gosave_systemstack_switch<>(SB) + MOVV R6, g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R3 + + // Now on a scheduling stack (a pthread-created stack). +g0: + // Save room for two of our pointers. + ADDV $-16, R3 + MOVV R13, 0(R3) // save old g on stack + MOVV (g_stack+stack_hi)(R13), R13 + SUBVU R12, R13 + MOVV R13, 8(R3) // save depth in old g stack (can't just save SP, as stack might be copied during a callback) + JAL (R25) + + // Restore g, stack pointer. R4 is return value. + MOVV 0(R3), g + JAL runtime·save_g(SB) + MOVV (g_stack+stack_hi)(g), R5 + MOVV 8(R3), R6 + SUBVU R6, R5 + MOVV R5, R3 + + MOVW R4, ret+16(FP) + RET + +// func cgocallback(fn, frame unsafe.Pointer, ctxt uintptr) +// See cgocall.go for more details. +TEXT ·cgocallback(SB),NOSPLIT,$24-24 + NO_LOCAL_POINTERS + + // Load m and g from thread-local storage. + MOVB runtime·iscgo(SB), R19 + BEQ R19, nocgo + JAL runtime·load_g(SB) +nocgo: + + // If g is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call. + BEQ g, needm + + MOVV g_m(g), R12 + MOVV R12, savedm-8(SP) + JMP havem + +needm: + MOVV g, savedm-8(SP) // g is zero, so is m. + MOVV $runtime·needm(SB), R4 + JAL (R4) + + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then systemstack will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOVV g_m(g), R12 + MOVV m_g0(R12), R19 + MOVV R3, (g_sched+gobuf_sp)(R19) + +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. + // NOTE: unwindm knows that the saved g->sched.sp is at 8(R29) aka savedsp-16(SP). + MOVV m_g0(R12), R19 + MOVV (g_sched+gobuf_sp)(R19), R13 + MOVV R13, savedsp-24(SP) // must match frame size + MOVV R3, (g_sched+gobuf_sp)(R19) + + // Switch to m->curg stack and call runtime.cgocallbackg. + // Because we are taking over the execution of m->curg + // but *not* resuming what had been running, we need to + // save that information (m->curg->sched) so we can restore it. + // We can restore m->curg->sched.sp easily, because calling + // runtime.cgocallbackg leaves SP unchanged upon return. + // To save m->curg->sched.pc, we push it onto the stack. + // This has the added benefit that it looks to the traceback + // routine like cgocallbackg is going to return to that + // PC (because the frame we allocate below has the same + // size as cgocallback_gofunc's frame declared above) + // so that the traceback will seamlessly trace back into + // the earlier calls. + MOVV m_curg(R12), g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R13 // prepare stack as R13 + MOVV (g_sched+gobuf_pc)(g), R4 + MOVV R4, -(24+8)(R13) // "saved LR"; must match frame size + MOVV fn+0(FP), R5 + MOVV frame+8(FP), R6 + MOVV ctxt+16(FP), R7 + MOVV $-(24+8)(R13), R3 + MOVV R5, 8(R3) + MOVV R6, 16(R3) + MOVV R7, 24(R3) + JAL runtime·cgocallbackg(SB) + + // Restore g->sched (== m->curg->sched) from saved values. + MOVV 0(R3), R4 + MOVV R4, (g_sched+gobuf_pc)(g) + MOVV $(24+8)(R3), R13 // must match frame size + MOVV R13, (g_sched+gobuf_sp)(g) + + // Switch back to m->g0's stack and restore m->g0->sched.sp. + // (Unlike m->curg, the g0 goroutine never uses sched.pc, + // so we do not have to restore it.) + MOVV g_m(g), R12 + MOVV m_g0(R12), g + JAL runtime·save_g(SB) + MOVV (g_sched+gobuf_sp)(g), R3 + MOVV savedsp-24(SP), R13 // must match frame size + MOVV R13, (g_sched+gobuf_sp)(g) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + MOVV savedm-8(SP), R12 + BNE R12, droppedm + MOVV $runtime·dropm(SB), R4 + JAL (R4) +droppedm: + + // Done! + RET + +// void setg(G*); set g. for use by needm. +TEXT runtime·setg(SB), NOSPLIT, $0-8 + MOVV gg+0(FP), g + // This only happens if iscgo, so jump straight to save_g + JAL runtime·save_g(SB) + RET + +// void setg_gcc(G*); set g called from gcc with g in R19 +TEXT setg_gcc<>(SB),NOSPLIT,$0-0 + MOVV R19, g + JAL runtime·save_g(SB) + RET + +TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R0 + UNDEF + +// AES hashing not implemented for loong64 +TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 + JMP runtime·memhashFallback(SB) +TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·strhashFallback(SB) +TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash32Fallback(SB) +TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash64Fallback(SB) + +TEXT runtime·return0(SB), NOSPLIT, $0 + MOVW $0, R19 + RET + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT _cgo_topofstack(SB),NOSPLIT,$16 + // g (R22) and REGTMP (R30) might be clobbered by load_g. They + // are callee-save in the gcc calling convention, so save them. + MOVV R30, savedREGTMP-16(SP) + MOVV g, savedG-8(SP) + + JAL runtime·load_g(SB) + MOVV g_m(g), R19 + MOVV m_curg(R19), R19 + MOVV (g_stack+stack_hi)(R19), R4 // return value in R4 + + MOVV savedG-8(SP), g + MOVV savedREGTMP-16(SP), R30 + RET + +// The top-most function running on a goroutine +// returns to goexit+PCQuantum. +TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0 + NOR R0, R0 // NOP + JAL runtime·goexit1(SB) // does not return + // traceback from goexit1 must hit code range of goexit + NOR R0, R0 // NOP + +TEXT ·checkASM(SB),NOSPLIT,$0-1 + MOVW $1, R19 + MOVB R19, ret+0(FP) + RET + +// gcWriteBarrier performs a heap pointer write and informs the GC. +// +// gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: +// - R27 is the destination of the write +// - R28 is the value being written at R27. +// It clobbers R30 (the linker temp register). +// The act of CALLing gcWriteBarrier will clobber R1 (LR). +// It does not clobber any other general-purpose registers, +// but may clobber others (e.g., floating point registers). +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$216 + // Save the registers clobbered by the fast path. + MOVV R19, 208(R3) + MOVV R13, 216(R3) + MOVV g_m(g), R19 + MOVV m_p(R19), R19 + MOVV (p_wbBuf+wbBuf_next)(R19), R13 + // Increment wbBuf.next position. + ADDV $16, R13 + MOVV R13, (p_wbBuf+wbBuf_next)(R19) + MOVV (p_wbBuf+wbBuf_end)(R19), R19 + MOVV R19, R30 // R30 is linker temp register + // Record the write. + MOVV R28, -16(R13) // Record value + MOVV (R27), R19 // TODO: This turns bad writes into bad reads. + MOVV R19, -8(R13) // Record *slot + // Is the buffer full? + BEQ R13, R30, flush +ret: + MOVV 208(R3), R19 + MOVV 216(R3), R13 + // Do the write. + MOVV R28, (R27) + RET + +flush: + // Save all general purpose registers since these could be + // clobbered by wbBufFlush and were not saved by the caller. + MOVV R27, 8(R3) // Also first argument to wbBufFlush + MOVV R28, 16(R3) // Also second argument to wbBufFlush + // R1 is LR, which was saved by the prologue. + MOVV R2, 24(R3) + // R3 is SP. + MOVV R4, 32(R3) + MOVV R5, 40(R3) + MOVV R6, 48(R3) + MOVV R7, 56(R3) + MOVV R8, 64(R3) + MOVV R9, 72(R3) + MOVV R10, 80(R3) + MOVV R11, 88(R3) + MOVV R12, 96(R3) + // R13 already saved + MOVV R14, 104(R3) + MOVV R15, 112(R3) + MOVV R16, 120(R3) + MOVV R17, 128(R3) + MOVV R18, 136(R3) + // R19 already saved + MOVV R20, 144(R3) + MOVV R21, 152(R3) + // R22 is g. + MOVV R23, 160(R3) + MOVV R24, 168(R3) + MOVV R25, 176(R3) + MOVV R26, 184(R3) + // R27 already saved + // R28 already saved. + MOVV R29, 192(R3) + // R30 is tmp register. + MOVV R31, 200(R3) + + + // This takes arguments R27 and R28. + CALL runtime·wbBufFlush(SB) + + MOVV 8(R3), R27 + MOVV 16(R3), R28 + MOVV 24(R3), R2 + MOVV 32(R3), R4 + MOVV 40(R3), R5 + MOVV 48(R3), R6 + MOVV 56(R3), R7 + MOVV 64(R3), R8 + MOVV 72(R3), R9 + MOVV 80(R3), R10 + MOVV 88(R3), R11 + MOVV 96(R3), R12 + MOVV 104(R3), R14 + MOVV 112(R3), R15 + MOVV 120(R3), R16 + MOVV 128(R3), R17 + MOVV 136(R3), R18 + MOVV 144(R3), R20 + MOVV 152(R3), R21 + MOVV 160(R3), R23 + MOVV 168(R3), R24 + MOVV 176(R3), R25 + MOVV 184(R3), R26 + MOVV 192(R3), R29 + MOVV 200(R3), R31 + JMP ret + +// Note: these functions use a special calling convention to save generated code space. +// Arguments are passed in registers, but the space for those arguments are allocated +// in the caller's stack frame. These stubs write the args into that stack space and +// then tail call to the corresponding runtime handler. +// The tail call makes these stubs disappear in backtraces. +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 + MOVV R17, x+0(FP) + MOVV R4, y+8(FP) + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 + MOVV R17, x+0(FP) + MOVV R4, y+8(FP) + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 + MOVV R17, x+0(FP) + MOVV R4, y+8(FP) + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 + MOVV R17, x+0(FP) + MOVV R4, y+8(FP) + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 + MOVV R18, x+0(FP) + MOVV R17, y+8(FP) + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 + MOVV R19, x+0(FP) + MOVV R18, y+8(FP) + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 + MOVV R17, x+0(FP) + MOVV R4, y+8(FP) + JMP runtime·goPanicSliceConvert(SB) diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s index d4d22801055157..1abadb9c7df308 100644 --- a/src/runtime/asm_mips64x.s +++ b/src/runtime/asm_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "go_tls.h" @@ -63,12 +62,11 @@ nocgo: // create a new goroutine to start program MOVV $runtime·mainPC(SB), R1 // entry - ADDV $-24, R29 - MOVV R1, 16(R29) - MOVV R0, 8(R29) + ADDV $-16, R29 + MOVV R1, 8(R29) MOVV R0, 0(R29) JAL runtime·newproc(SB) - ADDV $24, R29 + ADDV $16, R29 // start this M JAL runtime·mstart(SB) @@ -260,6 +258,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 UNDEF TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R3), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVV R29, R29 + MOVV R0, REGCTXT JMP runtime·morestack(SB) @@ -385,22 +390,6 @@ CALLFN(·call1073741824, 1073741824) TEXT runtime·procyield(SB),NOSPLIT,$0-0 RET -// void jmpdefer(fv, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 8 bytes to get back to JAL deferreturn -// 3. JMP to fn -TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16 - MOVV 0(R29), R31 - ADDV $-8, R31 - - MOVV fv+0(FP), REGCTXT - MOVV argp+8(FP), R29 - ADDV $-8, R29 - NOR R0, R0 // prevent scheduling - MOVV 0(REGCTXT), R4 - JMP (R4) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -441,8 +430,11 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVV g_m(g), R5 + MOVV m_gsignal(R5), R6 + BEQ R6, g, g0 MOVV m_g0(R5), R6 BEQ R6, g, g0 diff --git a/src/runtime/asm_mipsx.s b/src/runtime/asm_mipsx.s index ea7edf20cf9f84..877c1bb97b7bcf 100644 --- a/src/runtime/asm_mipsx.s +++ b/src/runtime/asm_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "go_tls.h" @@ -64,12 +63,11 @@ nocgo: // create a new goroutine to start program MOVW $runtime·mainPC(SB), R1 // entry - ADDU $-12, R29 - MOVW R1, 8(R29) - MOVW R0, 4(R29) + ADDU $-8, R29 + MOVW R1, 4(R29) MOVW R0, 0(R29) JAL runtime·newproc(SB) - ADDU $12, R29 + ADDU $8, R29 // start this M JAL runtime·mstart(SB) @@ -259,6 +257,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 UNDEF TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R3), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVW R29, R29 + MOVW R0, REGCTXT JMP runtime·morestack(SB) @@ -383,22 +388,6 @@ CALLFN(·call1073741824, 1073741824) TEXT runtime·procyield(SB),NOSPLIT,$0-4 RET -// void jmpdefer(fv, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 8 bytes to get back to JAL deferreturn -// 3. JMP to fn -TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8 - MOVW 0(R29), R31 - ADDU $-8, R31 - - MOVW fv+0(FP), REGCTXT - MOVW argp+4(FP), R29 - ADDU $-4, R29 - NOR R0, R0 // prevent scheduling - MOVW 0(REGCTXT), R4 - JMP (R4) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -430,8 +419,11 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-12 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVW g_m(g), R5 + MOVW m_gsignal(R5), R6 + BEQ R6, g, g0 MOVW m_g0(R5), R6 BEQ R6, g, g0 diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index 942cc14f17d044..6a162eff0af583 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "go_tls.h" @@ -94,9 +93,8 @@ nocgo: MOVDU R0, -8(R1) MOVDU R0, -8(R1) MOVDU R0, -8(R1) - MOVDU R0, -8(R1) BL runtime·newproc(SB) - ADD $(16+FIXED_FRAME), R1 + ADD $(8+FIXED_FRAME), R1 // start this M BL runtime·mstart(SB) @@ -104,11 +102,11 @@ nocgo: MOVD R0, 0(R0) RET -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 - MOVD R0, 0(R0) // TODO: TD + TW $31, R0, R0 RET TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 @@ -166,8 +164,10 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0 // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. -TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 // Save caller state in g->sched + // R11 should be safe across save_g?? + MOVD R3, R11 MOVD R1, (g_sched+gobuf_sp)(g) MOVD LR, R31 MOVD R31, (g_sched+gobuf_pc)(g) @@ -181,10 +181,11 @@ TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 CMP g, R3 BNE 2(PC) BR runtime·badmcall(SB) - MOVD fn+0(FP), R11 // context MOVD 0(R11), R12 // code pointer MOVD R12, CTR MOVD (g_sched+gobuf_sp)(g), R1 // sp = m->g0->sched.sp + // Don't need to do anything special for regabiargs here + // R3 is g; stack is set anyway MOVDU R3, -8(R1) MOVDU R0, -8(R1) MOVDU R0, -8(R1) @@ -333,6 +334,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 UNDEF TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R5), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVD R1, R1 + MOVD R0, R11 BR runtime·morestack(SB) @@ -429,6 +437,8 @@ callfn: \ BNE 2(PC) \ MOVD R0, 0(R0) \ #endif \ + MOVD regArgs+40(FP), R20; \ + BL runtime·unspillArgs(SB); \ MOVD (R11), R12; \ MOVD R12, CTR; \ PCDATA $PCDATA_StackMapIndex, $0; \ @@ -437,6 +447,8 @@ callfn: \ MOVD 24(R1), R2; \ #endif \ /* copy return values back */ \ + MOVD regArgs+40(FP), R20; \ + BL runtime·spillArgs(SB); \ MOVD stackArgsType+0(FP), R7; \ MOVD stackArgs+16(FP), R3; \ MOVWZ stackArgsSize+24(FP), R4; \ @@ -453,11 +465,12 @@ callfn: \ // to reflectcallmove. It does not follow the Go ABI; it expects its // arguments in registers. TEXT callRet<>(SB), NOSPLIT, $40-0 + NO_LOCAL_POINTERS MOVD R7, FIXED_FRAME+0(R1) MOVD R3, FIXED_FRAME+8(R1) MOVD R5, FIXED_FRAME+16(R1) MOVD R4, FIXED_FRAME+24(R1) - MOVD $0, FIXED_FRAME+32(R1) + MOVD R20, FIXED_FRAME+32(R1) BL runtime·reflectcallmove(SB) RET @@ -504,34 +517,6 @@ again: OR R6, R6, R6 // Set PPR priority back to medium-low RET -// void jmpdefer(fv, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 8 bytes to get back to either nop or toc reload before deferreturn -// 3. BR to fn -// When dynamically linking Go, it is not sufficient to rewind to the BL -// deferreturn -- we might be jumping between modules and so we need to reset -// the TOC pointer in r2. To do this, codegen inserts MOVD 24(R1), R2 *before* -// the BL deferreturn and jmpdefer rewinds to that. -TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16 - MOVD 0(R1), R31 - SUB $8, R31 - MOVD R31, LR - - MOVD fv+0(FP), R11 - MOVD argp+8(FP), R1 - SUB $FIXED_FRAME, R1 -#ifdef GOOS_aix - // AIX won't trigger a SIGSEGV if R11 = nil - // So it manually triggers it - CMP R0, R11 - BNE 2(PC) - MOVD R0, 0(R0) -#endif - MOVD 0(R11), R12 - MOVD R12, CTR - BR (CTR) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -570,9 +555,8 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. - // Moreover, if it's called inside the signal handler, it must not switch - // to g0 as it can be in use by another syscall. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVD g_m(g), R8 MOVD m_gsignal(R8), R6 CMP R6, g @@ -718,7 +702,10 @@ havem: MOVD R5, FIXED_FRAME+0(R1) MOVD R6, FIXED_FRAME+8(R1) MOVD R7, FIXED_FRAME+16(R1) - BL runtime·cgocallbackg(SB) + + MOVD $runtime·cgocallbackg(SB), R12 + MOVD R12, CTR + CALL (CTR) // indirect call to bypass nosplit check. We're on a different stack now. // Restore g->sched (== m->curg->sched) from saved values. MOVD 0(R1), R5 @@ -804,15 +791,71 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-8 MOVD R3, ret+0(FP) RET +// spillArgs stores return values from registers to a *internal/abi.RegArgs in R20. +TEXT runtime·spillArgs(SB),NOSPLIT,$0-0 + MOVD R3, 0(R20) + MOVD R4, 8(R20) + MOVD R5, 16(R20) + MOVD R6, 24(R20) + MOVD R7, 32(R20) + MOVD R8, 40(R20) + MOVD R9, 48(R20) + MOVD R10, 56(R20) + MOVD R14, 64(R20) + MOVD R15, 72(R20) + MOVD R16, 80(R20) + MOVD R17, 88(R20) + FMOVD F1, 96(R20) + FMOVD F2, 104(R20) + FMOVD F3, 112(R20) + FMOVD F4, 120(R20) + FMOVD F5, 128(R20) + FMOVD F6, 136(R20) + FMOVD F7, 144(R20) + FMOVD F8, 152(R20) + FMOVD F9, 160(R20) + FMOVD F10, 168(R20) + FMOVD F11, 176(R20) + FMOVD F12, 184(R20) + RET + +// unspillArgs loads args into registers from a *internal/abi.RegArgs in R20. +TEXT runtime·unspillArgs(SB),NOSPLIT,$0-0 + MOVD 0(R20), R3 + MOVD 8(R20), R4 + MOVD 16(R20), R5 + MOVD 24(R20), R6 + MOVD 32(R20), R7 + MOVD 40(R20), R8 + MOVD 48(R20), R9 + MOVD 56(R20), R10 + MOVD 64(R20), R14 + MOVD 72(R20), R15 + MOVD 80(R20), R16 + MOVD 88(R20), R17 + FMOVD 96(R20), F1 + FMOVD 104(R20), F2 + FMOVD 112(R20), F3 + FMOVD 120(R20), F4 + FMOVD 128(R20), F5 + FMOVD 136(R20), F6 + FMOVD 144(R20), F7 + FMOVD 152(R20), F8 + FMOVD 160(R20), F9 + FMOVD 168(R20), F10 + FMOVD 176(R20), F11 + FMOVD 184(R20), F12 + RET + // AES hashing not implemented for ppc64 -TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 - JMP runtime·memhashFallback(SB) -TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·strhashFallback(SB) -TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash32Fallback(SB) -TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash64Fallback(SB) +TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 + JMP runtime·memhashFallback(SB) +TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·strhashFallback(SB) +TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash32Fallback(SB) +TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash64Fallback(SB) TEXT runtime·return0(SB), NOSPLIT, $0 MOVW $0, R3 @@ -890,7 +933,7 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // It clobbers condition codes. // It does not clobber R0 through R17 (except special registers), // but may clobber any other register, *including* R31. -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112 +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112 // The standard prologue clobbers R31. // We use R18 and R19 as scratch registers. MOVD g_m(g), R18 @@ -959,71 +1002,262 @@ flush: // in the caller's stack frame. These stubs write the args into that stack space and // then tail call to the corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 - MOVD R5, x+0(FP) - MOVD R6, y+8(FP) - JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 - MOVD R5, x+0(FP) - MOVD R6, y+8(FP) - JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 - MOVD R5, x+0(FP) - MOVD R6, y+8(FP) - JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 - MOVD R5, x+0(FP) - MOVD R6, y+8(FP) - JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 - MOVD R4, x+0(FP) - MOVD R5, y+8(FP) - JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 - MOVD R3, x+0(FP) - MOVD R4, y+8(FP) - JMP runtime·goPanicSlice3CU(SB) -TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 - MOVD R5, x+0(FP) - MOVD R6, y+8(FP) - JMP runtime·goPanicSliceConvert(SB) +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 + MOVD R5, R3 + MOVD R6, R4 + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 + MOVD R5, R3 + MOVD R6, R4 + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 + MOVD R5, R3 + MOVD R6, R4 + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 + MOVD R5, R3 + MOVD R6, R4 + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 + MOVD R4, R3 + MOVD R5, R4 + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 + MOVD R5, R3 + MOVD R6, R4 + JMP runtime·goPanicSliceConvert(SB) + +// These functions are used when internal linking cgo with external +// objects compiled with the -Os on gcc. They reduce prologue/epilogue +// size by deferring preservation of callee-save registers to a shared +// function. These are defined in PPC64 ELFv2 2.3.3 (but also present +// in ELFv1) +// +// These appear unused, but the linker will redirect calls to functions +// like _savegpr0_14 or _restgpr1_14 to runtime.elf_savegpr0 or +// runtime.elf_restgpr1 with an appropriate offset based on the number +// register operations required when linking external objects which +// make these calls. For GPR/FPR saves, the minimum register value is +// 14, for VR it is 20. +// +// These are only used when linking such cgo code internally. Note, R12 +// and R0 may be used in different ways than regular ELF compliant +// functions. +TEXT runtime·elf_savegpr0(SB),NOSPLIT|NOFRAME,$0 + // R0 holds the LR of the caller's caller, R1 holds save location + MOVD R14, -144(R1) + MOVD R15, -136(R1) + MOVD R16, -128(R1) + MOVD R17, -120(R1) + MOVD R18, -112(R1) + MOVD R19, -104(R1) + MOVD R20, -96(R1) + MOVD R21, -88(R1) + MOVD R22, -80(R1) + MOVD R23, -72(R1) + MOVD R24, -64(R1) + MOVD R25, -56(R1) + MOVD R26, -48(R1) + MOVD R27, -40(R1) + MOVD R28, -32(R1) + MOVD R29, -24(R1) + MOVD g, -16(R1) + MOVD R31, -8(R1) + MOVD R0, 16(R1) + RET +TEXT runtime·elf_restgpr0(SB),NOSPLIT|NOFRAME,$0 + // R1 holds save location. This returns to the LR saved on stack (bypassing the caller) + MOVD -144(R1), R14 + MOVD -136(R1), R15 + MOVD -128(R1), R16 + MOVD -120(R1), R17 + MOVD -112(R1), R18 + MOVD -104(R1), R19 + MOVD -96(R1), R20 + MOVD -88(R1), R21 + MOVD -80(R1), R22 + MOVD -72(R1), R23 + MOVD -64(R1), R24 + MOVD -56(R1), R25 + MOVD -48(R1), R26 + MOVD -40(R1), R27 + MOVD -32(R1), R28 + MOVD -24(R1), R29 + MOVD -16(R1), g + MOVD -8(R1), R31 + MOVD 16(R1), R0 // Load and return to saved LR + MOVD R0, LR + RET +TEXT runtime·elf_savegpr1(SB),NOSPLIT|NOFRAME,$0 + // R12 holds the save location + MOVD R14, -144(R12) + MOVD R15, -136(R12) + MOVD R16, -128(R12) + MOVD R17, -120(R12) + MOVD R18, -112(R12) + MOVD R19, -104(R12) + MOVD R20, -96(R12) + MOVD R21, -88(R12) + MOVD R22, -80(R12) + MOVD R23, -72(R12) + MOVD R24, -64(R12) + MOVD R25, -56(R12) + MOVD R26, -48(R12) + MOVD R27, -40(R12) + MOVD R28, -32(R12) + MOVD R29, -24(R12) + MOVD g, -16(R12) + MOVD R31, -8(R12) + RET +TEXT runtime·elf_restgpr1(SB),NOSPLIT|NOFRAME,$0 + // R12 holds the save location + MOVD -144(R12), R14 + MOVD -136(R12), R15 + MOVD -128(R12), R16 + MOVD -120(R12), R17 + MOVD -112(R12), R18 + MOVD -104(R12), R19 + MOVD -96(R12), R20 + MOVD -88(R12), R21 + MOVD -80(R12), R22 + MOVD -72(R12), R23 + MOVD -64(R12), R24 + MOVD -56(R12), R25 + MOVD -48(R12), R26 + MOVD -40(R12), R27 + MOVD -32(R12), R28 + MOVD -24(R12), R29 + MOVD -16(R12), g + MOVD -8(R12), R31 + RET +TEXT runtime·elf_savefpr(SB),NOSPLIT|NOFRAME,$0 + // R0 holds the LR of the caller's caller, R1 holds save location + FMOVD F14, -144(R1) + FMOVD F15, -136(R1) + FMOVD F16, -128(R1) + FMOVD F17, -120(R1) + FMOVD F18, -112(R1) + FMOVD F19, -104(R1) + FMOVD F20, -96(R1) + FMOVD F21, -88(R1) + FMOVD F22, -80(R1) + FMOVD F23, -72(R1) + FMOVD F24, -64(R1) + FMOVD F25, -56(R1) + FMOVD F26, -48(R1) + FMOVD F27, -40(R1) + FMOVD F28, -32(R1) + FMOVD F29, -24(R1) + FMOVD F30, -16(R1) + FMOVD F31, -8(R1) + MOVD R0, 16(R1) + RET +TEXT runtime·elf_restfpr(SB),NOSPLIT|NOFRAME,$0 + // R1 holds save location. This returns to the LR saved on stack (bypassing the caller) + FMOVD -144(R1), F14 + FMOVD -136(R1), F15 + FMOVD -128(R1), F16 + FMOVD -120(R1), F17 + FMOVD -112(R1), F18 + FMOVD -104(R1), F19 + FMOVD -96(R1), F20 + FMOVD -88(R1), F21 + FMOVD -80(R1), F22 + FMOVD -72(R1), F23 + FMOVD -64(R1), F24 + FMOVD -56(R1), F25 + FMOVD -48(R1), F26 + FMOVD -40(R1), F27 + FMOVD -32(R1), F28 + FMOVD -24(R1), F29 + FMOVD -16(R1), F30 + FMOVD -8(R1), F31 + MOVD 16(R1), R0 // Load and return to saved LR + MOVD R0, LR + RET +TEXT runtime·elf_savevr(SB),NOSPLIT|NOFRAME,$0 + // R0 holds the save location, R12 is clobbered + MOVD $-192, R12 + STVX V20, (R0+R12) + MOVD $-176, R12 + STVX V21, (R0+R12) + MOVD $-160, R12 + STVX V22, (R0+R12) + MOVD $-144, R12 + STVX V23, (R0+R12) + MOVD $-128, R12 + STVX V24, (R0+R12) + MOVD $-112, R12 + STVX V25, (R0+R12) + MOVD $-96, R12 + STVX V26, (R0+R12) + MOVD $-80, R12 + STVX V27, (R0+R12) + MOVD $-64, R12 + STVX V28, (R0+R12) + MOVD $-48, R12 + STVX V29, (R0+R12) + MOVD $-32, R12 + STVX V30, (R0+R12) + MOVD $-16, R12 + STVX V31, (R0+R12) + RET +TEXT runtime·elf_restvr(SB),NOSPLIT|NOFRAME,$0 + // R0 holds the save location, R12 is clobbered + MOVD $-192, R12 + LVX (R0+R12), V20 + MOVD $-176, R12 + LVX (R0+R12), V21 + MOVD $-160, R12 + LVX (R0+R12), V22 + MOVD $-144, R12 + LVX (R0+R12), V23 + MOVD $-128, R12 + LVX (R0+R12), V24 + MOVD $-112, R12 + LVX (R0+R12), V25 + MOVD $-96, R12 + LVX (R0+R12), V26 + MOVD $-80, R12 + LVX (R0+R12), V27 + MOVD $-64, R12 + LVX (R0+R12), V28 + MOVD $-48, R12 + LVX (R0+R12), V29 + MOVD $-32, R12 + LVX (R0+R12), V30 + MOVD $-16, R12 + LVX (R0+R12), V31 + RET diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s index ef7af4e10d8826..389851beb97873 100644 --- a/src/runtime/asm_riscv64.s +++ b/src/runtime/asm_riscv64.s @@ -57,12 +57,11 @@ nocgo: // create a new goroutine to start program MOV $runtime·mainPC(SB), T0 // entry - ADD $-24, X2 - MOV T0, 16(X2) - MOV ZERO, 8(X2) + ADD $-16, X2 + MOV T0, 8(X2) MOV ZERO, 0(X2) CALL runtime·newproc(SB) - ADD $24, X2 + ADD $16, X2 // start this M CALL runtime·mstart(SB) @@ -82,6 +81,9 @@ TEXT setg_gcc<>(SB),NOSPLIT,$0-0 // func cputicks() int64 TEXT runtime·cputicks(SB),NOSPLIT,$0-8 + // RDTIME to emulate cpu ticks + // RDCYCLE reads counter that is per HART(core) based + // according to the riscv manual, see issue 46737 RDTIME A0 MOV A0, ret+0(FP) RET @@ -156,8 +158,8 @@ TEXT runtime·getcallerpc(SB),NOSPLIT|NOFRAME,$0-8 */ // Called during function prolog when more stack is needed. -// Caller has already loaded: -// R1: framesize, R2: argsize, R3: LR +// Called with return address (i.e. caller's PC) in X5 (aka T0), +// and the LR register contains the caller's LR. // // The traceback routines see morestack on a g0 as being // the top of a stack (for example, morestack calling newstack @@ -207,18 +209,25 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 // func morestack_noctxt() TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register, and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOV X2, X2 + MOV ZERO, CTXT JMP runtime·morestack(SB) // AES hashing not implemented for riscv64 -TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 - JMP runtime·memhashFallback(SB) -TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·strhashFallback(SB) -TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash32Fallback(SB) -TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash64Fallback(SB) +TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 + JMP runtime·memhashFallback(SB) +TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·strhashFallback(SB) +TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash32Fallback(SB) +TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash64Fallback(SB) // func return0() TEXT runtime·return0(SB), NOSPLIT, $0 @@ -249,21 +258,6 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0 MOV gobuf_pc(T0), T0 JALR ZERO, T0 -// func jmpdefer(fv *funcval, argp uintptr) -// called from deferreturn -// 1. grab stored return address from the caller's frame -// 2. sub 8 bytes to get back to JAL deferreturn -// 3. JMP to fn -TEXT runtime·jmpdefer(SB), NOSPLIT|NOFRAME, $0-16 - MOV 0(X2), RA - ADD $-8, RA - - MOV fv+0(FP), CTXT - MOV argp+8(FP), X2 - ADD $-8, X2 - MOV 0(CTXT), T0 - JALR ZERO, T0 - // func procyield(cycles uint32) TEXT runtime·procyield(SB),NOSPLIT,$0-0 RET @@ -273,25 +267,31 @@ TEXT runtime·procyield(SB),NOSPLIT,$0-0 // to keep running g. // func mcall(fn func(*g)) -TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +#ifdef GOEXPERIMENT_regabiargs + MOV X10, CTXT +#else + MOV fn+0(FP), CTXT +#endif + // Save caller state in g->sched MOV X2, (g_sched+gobuf_sp)(g) MOV RA, (g_sched+gobuf_pc)(g) MOV ZERO, (g_sched+gobuf_lr)(g) // Switch to m->g0 & its stack, call fn. - MOV g, T0 + MOV g, X10 MOV g_m(g), T1 MOV m_g0(T1), g CALL runtime·save_g(SB) - BNE g, T0, 2(PC) + BNE g, X10, 2(PC) JMP runtime·badmcall(SB) - MOV fn+0(FP), CTXT // context MOV 0(CTXT), T1 // code pointer MOV (g_sched+gobuf_sp)(g), X2 // sp = m->g0->sched.sp + // we don't need special macro for regabi since arg0(X10) = g ADD $-16, X2 - MOV T0, 8(X2) - MOV ZERO, 0(X2) + MOV X10, 8(X2) // setup g + MOV ZERO, 0(X2) // clear return address JALR RA, T1 JMP runtime·badmcall2(SB) @@ -326,8 +326,11 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOV g_m(g), X6 + MOV m_gsignal(X6), X7 + BEQ X7, g, g0 MOV m_g0(X6), X7 BEQ X7, g, g0 @@ -427,12 +430,17 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ MOVB A4, (A3); \ ADD $1, A3; \ JMP -5(PC); \ + /* set up argument registers */ \ + MOV regArgs+40(FP), X25; \ + CALL ·unspillArgs(SB); \ /* call function */ \ MOV f+8(FP), CTXT; \ - MOV (CTXT), A4; \ + MOV (CTXT), X25; \ PCDATA $PCDATA_StackMapIndex, $0; \ - JALR RA, A4; \ + JALR RA, X25; \ /* copy return values back */ \ + MOV regArgs+40(FP), X25; \ + CALL ·spillArgs(SB); \ MOV stackArgsType+0(FP), A5; \ MOV stackArgs+16(FP), A1; \ MOVWU stackArgsSize+24(FP), A2; \ @@ -449,11 +457,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ // to reflectcallmove. It does not follow the Go ABI; it expects its // arguments in registers. TEXT callRet<>(SB), NOSPLIT, $40-0 + NO_LOCAL_POINTERS MOV A5, 8(X2) MOV A1, 16(X2) MOV A3, 24(X2) MOV A2, 32(X2) - MOV ZERO, 40(X2) + MOV X25, 40(X2) CALL runtime·reflectcallmove(SB) RET @@ -635,6 +644,86 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 MOV T0, ret+0(FP) RET +#ifdef GOEXPERIMENT_regabiargs +// spillArgs stores return values from registers to a *internal/abi.RegArgs in X25. +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + MOV X10, (0*8)(X25) + MOV X11, (1*8)(X25) + MOV X12, (2*8)(X25) + MOV X13, (3*8)(X25) + MOV X14, (4*8)(X25) + MOV X15, (5*8)(X25) + MOV X16, (6*8)(X25) + MOV X17, (7*8)(X25) + MOV X8, (8*8)(X25) + MOV X9, (9*8)(X25) + MOV X18, (10*8)(X25) + MOV X19, (11*8)(X25) + MOV X20, (12*8)(X25) + MOV X21, (13*8)(X25) + MOV X22, (14*8)(X25) + MOV X23, (15*8)(X25) + MOVD F10, (16*8)(X25) + MOVD F11, (17*8)(X25) + MOVD F12, (18*8)(X25) + MOVD F13, (19*8)(X25) + MOVD F14, (20*8)(X25) + MOVD F15, (21*8)(X25) + MOVD F16, (22*8)(X25) + MOVD F17, (23*8)(X25) + MOVD F8, (24*8)(X25) + MOVD F9, (25*8)(X25) + MOVD F18, (26*8)(X25) + MOVD F19, (27*8)(X25) + MOVD F20, (28*8)(X25) + MOVD F21, (29*8)(X25) + MOVD F22, (30*8)(X25) + MOVD F23, (31*8)(X25) + RET + +// unspillArgs loads args into registers from a *internal/abi.RegArgs in X25. +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + MOV (0*8)(X25), X10 + MOV (1*8)(X25), X11 + MOV (2*8)(X25), X12 + MOV (3*8)(X25), X13 + MOV (4*8)(X25), X14 + MOV (5*8)(X25), X15 + MOV (6*8)(X25), X16 + MOV (7*8)(X25), X17 + MOV (8*8)(X25), X8 + MOV (9*8)(X25), X9 + MOV (10*8)(X25), X18 + MOV (11*8)(X25), X19 + MOV (12*8)(X25), X20 + MOV (13*8)(X25), X21 + MOV (14*8)(X25), X22 + MOV (15*8)(X25), X23 + MOVD (16*8)(X25), F10 + MOVD (17*8)(X25), F11 + MOVD (18*8)(X25), F12 + MOVD (19*8)(X25), F13 + MOVD (20*8)(X25), F14 + MOVD (21*8)(X25), F15 + MOVD (22*8)(X25), F16 + MOVD (23*8)(X25), F17 + MOVD (24*8)(X25), F8 + MOVD (25*8)(X25), F9 + MOVD (26*8)(X25), F18 + MOVD (27*8)(X25), F19 + MOVD (28*8)(X25), F20 + MOVD (29*8)(X25), F21 + MOVD (30*8)(X25), F22 + MOVD (31*8)(X25), F23 + RET +#else +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + RET + +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + RET +#endif + // gcWriteBarrier performs a heap pointer write and informs the GC. // // gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: @@ -644,10 +733,10 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // The act of CALLing gcWriteBarrier will clobber RA (LR). // It does not clobber any other general-purpose registers, // but may clobber others (e.g., floating point registers). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$216 +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$208 // Save the registers clobbered by the fast path. - MOV A0, 25*8(X2) - MOV A1, 26*8(X2) + MOV A0, 24*8(X2) + MOV A1, 25*8(X2) MOV g_m(g), A0 MOV m_p(A0), A0 MOV (p_wbBuf+wbBuf_next)(A0), A1 @@ -663,8 +752,8 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$216 // Is the buffer full? BEQ A1, T6, flush ret: - MOV 25*8(X2), A0 - MOV 26*8(X2), A1 + MOV 24*8(X2), A0 + MOV 25*8(X2), A1 // Do the write. MOV T1, (T0) RET @@ -677,34 +766,34 @@ flush: // X0 is zero register // X1 is LR, saved by prologue // X2 is SP - MOV X3, 3*8(X2) + // X3 is GP // X4 is TP // X5 is first arg to wbBufFlush (T0) // X6 is second arg to wbBufFlush (T1) - MOV X7, 4*8(X2) - MOV X8, 5*8(X2) - MOV X9, 6*8(X2) + MOV X7, 3*8(X2) + MOV X8, 4*8(X2) + MOV X9, 5*8(X2) // X10 already saved (A0) // X11 already saved (A1) - MOV X12, 7*8(X2) - MOV X13, 8*8(X2) - MOV X14, 9*8(X2) - MOV X15, 10*8(X2) - MOV X16, 11*8(X2) - MOV X17, 12*8(X2) - MOV X18, 13*8(X2) - MOV X19, 14*8(X2) - MOV X20, 15*8(X2) - MOV X21, 16*8(X2) - MOV X22, 17*8(X2) - MOV X23, 18*8(X2) - MOV X24, 19*8(X2) - MOV X25, 20*8(X2) - MOV X26, 21*8(X2) + MOV X12, 6*8(X2) + MOV X13, 7*8(X2) + MOV X14, 8*8(X2) + MOV X15, 9*8(X2) + MOV X16, 10*8(X2) + MOV X17, 11*8(X2) + MOV X18, 12*8(X2) + MOV X19, 13*8(X2) + MOV X20, 14*8(X2) + MOV X21, 15*8(X2) + MOV X22, 16*8(X2) + MOV X23, 17*8(X2) + MOV X24, 18*8(X2) + MOV X25, 19*8(X2) + MOV X26, 20*8(X2) // X27 is g. - MOV X28, 22*8(X2) - MOV X29, 23*8(X2) - MOV X30, 24*8(X2) + MOV X28, 21*8(X2) + MOV X29, 22*8(X2) + MOV X30, 23*8(X2) // X31 is tmp register. // This takes arguments T0 and T1. @@ -712,104 +801,189 @@ flush: MOV 1*8(X2), T0 MOV 2*8(X2), T1 - MOV 3*8(X2), X3 - MOV 4*8(X2), X7 - MOV 5*8(X2), X8 - MOV 6*8(X2), X9 - MOV 7*8(X2), X12 - MOV 8*8(X2), X13 - MOV 9*8(X2), X14 - MOV 10*8(X2), X15 - MOV 11*8(X2), X16 - MOV 12*8(X2), X17 - MOV 13*8(X2), X18 - MOV 14*8(X2), X19 - MOV 15*8(X2), X20 - MOV 16*8(X2), X21 - MOV 17*8(X2), X22 - MOV 18*8(X2), X23 - MOV 19*8(X2), X24 - MOV 20*8(X2), X25 - MOV 21*8(X2), X26 - MOV 22*8(X2), X28 - MOV 23*8(X2), X29 - MOV 24*8(X2), X30 + MOV 3*8(X2), X7 + MOV 4*8(X2), X8 + MOV 5*8(X2), X9 + MOV 6*8(X2), X12 + MOV 7*8(X2), X13 + MOV 8*8(X2), X14 + MOV 9*8(X2), X15 + MOV 10*8(X2), X16 + MOV 11*8(X2), X17 + MOV 12*8(X2), X18 + MOV 13*8(X2), X19 + MOV 14*8(X2), X20 + MOV 15*8(X2), X21 + MOV 16*8(X2), X22 + MOV 17*8(X2), X23 + MOV 18*8(X2), X24 + MOV 19*8(X2), X25 + MOV 20*8(X2), X26 + MOV 21*8(X2), X28 + MOV 22*8(X2), X29 + MOV 23*8(X2), X30 JMP ret // Note: these functions use a special calling convention to save generated code space. -// Arguments are passed in registers, but the space for those arguments are allocated -// in the caller's stack frame. These stubs write the args into that stack space and -// then tail call to the corresponding runtime handler. +// Arguments are passed in registers (ssa/gen/RISCV64Ops.go), but the space for those +// arguments are allocated in the caller's stack frame. +// These stubs write the args into that stack space and then tail call to the +// corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSlice3CU(SB) -TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSliceConvert(SB) +#endif + JMP runtime·goPanicSliceConvert(SB) -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s index fb382716303b42..334e1aa909d3ef 100644 --- a/src/runtime/asm_s390x.s +++ b/src/runtime/asm_s390x.s @@ -147,12 +147,11 @@ nocgo: // create a new goroutine to start program MOVD $runtime·mainPC(SB), R2 // entry - SUB $24, R15 - MOVD R2, 16(R15) - MOVD $0, 8(R15) + SUB $16, R15 + MOVD R2, 8(R15) MOVD $0, 0(R15) BL runtime·newproc(SB) - ADD $24, R15 + ADD $16, R15 // start this M BL runtime·mstart(SB) @@ -347,6 +346,13 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 UNDEF TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + // Force SPWRITE. This function doesn't actually write SP, + // but it is called with a special calling convention where + // the caller doesn't save LR on stack but passes it as a + // register (R5), and the unwinder currently doesn't understand. + // Make it SPWRITE to stop unwinding. (See issue 54332) + MOVD R15, R15 + MOVD $0, R12 BR runtime·morestack(SB) @@ -481,21 +487,6 @@ TEXT callfnMVC<>(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·procyield(SB),NOSPLIT,$0-0 RET -// void jmpdefer(fv, sp); -// called from deferreturn. -// 1. grab stored LR for caller -// 2. sub 6 bytes to get back to BL deferreturn (size of BRASL instruction) -// 3. BR to fn -TEXT runtime·jmpdefer(SB),NOSPLIT|NOFRAME,$0-16 - MOVD 0(R15), R1 - SUB $6, R1, LR - - MOVD fv+0(FP), R12 - MOVD argp+8(FP), R15 - SUB $8, R15 - MOVD 0(R12), R3 - BR (R3) - // Save state of caller into g->sched, // but using fake PC from systemstack_switch. // Must only be called from functions with no locals ($0) @@ -529,12 +520,15 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-20 // Figure out if we need to switch to m->g0 stack. // We get called to create new OS threads too, and those - // come in on the m->g0 stack already. + // come in on the m->g0 stack already. Or we might already + // be on the m->gsignal stack. MOVD g_m(g), R6 - MOVD m_g0(R6), R6 - CMPBEQ R6, g, g0 + MOVD m_gsignal(R6), R7 + CMPBEQ R7, g, g0 + MOVD m_g0(R6), R7 + CMPBEQ R7, g, g0 BL gosave_systemstack_switch<>(SB) - MOVD R6, g + MOVD R7, g BL runtime·save_g(SB) MOVD (g_sched+gobuf_sp)(g), R15 @@ -790,13 +784,12 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: // - R2 is the destination of the write // - R3 is the value being written at R2. -// It clobbers R10 (the temp register). +// It clobbers R10 (the temp register) and R1 (used by PLT stub). // It does not clobber any other general-purpose registers, // but may clobber others (e.g., floating point registers). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$104 +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$96 // Save the registers clobbered by the fast path. - MOVD R1, 96(R15) - MOVD R4, 104(R15) + MOVD R4, 96(R15) MOVD g_m(g), R1 MOVD m_p(R1), R1 // Increment wbBuf.next position. @@ -811,8 +804,7 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$104 // Is the buffer full? CMPBEQ R4, R1, flush ret: - MOVD 96(R15), R1 - MOVD 104(R15), R4 + MOVD 96(R15), R4 // Do the write. MOVD R3, (R2) RET diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 33c335ba5af91b..d885da6e70f439 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -18,8 +18,7 @@ TEXT runtime·rt0_go(SB), NOSPLIT|NOFRAME|TOPFRAME, $0 CALLNORESUME runtime·args(SB) CALLNORESUME runtime·osinit(SB) CALLNORESUME runtime·schedinit(SB) - MOVD $0, 0(SP) - MOVD $runtime·mainPC(SB), 8(SP) + MOVD $runtime·mainPC(SB), 0(SP) CALLNORESUME runtime·newproc(SB) CALL runtime·mstart(SB) // WebAssembly stack will unwind when switching to another goroutine UNDEF @@ -194,35 +193,6 @@ TEXT runtime·return0(SB), NOSPLIT, $0-0 MOVD $0, RET0 RET -TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16 - MOVD fv+0(FP), CTXT - - Get CTXT - I64Eqz - If - CALLNORESUME runtime·sigpanic(SB) - End - - // caller sp after CALL - I64Load argp+8(FP) - I64Const $8 - I64Sub - I32WrapI64 - Set SP - - // decrease PC_B by 1 to CALL again - Get SP - I32Load16U (SP) - I32Const $1 - I32Sub - I32Store16 $0 - - // but first run the deferred function - Get CTXT - I32WrapI64 - I64Load $0 - JMP - TEXT runtime·asminit(SB), NOSPLIT, $0-0 // No per-thread init. RET diff --git a/src/runtime/atomic_loong64.s b/src/runtime/atomic_loong64.s new file mode 100644 index 00000000000000..4818a827de4d20 --- /dev/null +++ b/src/runtime/atomic_loong64.s @@ -0,0 +1,9 @@ +// Copyright 2022 The Go 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 ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0 + DBAR + RET diff --git a/src/runtime/atomic_mips64x.s b/src/runtime/atomic_mips64x.s index e2118e6a20b253..dd6380ce409e51 100644 --- a/src/runtime/atomic_mips64x.s +++ b/src/runtime/atomic_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "textflag.h" diff --git a/src/runtime/atomic_mipsx.s b/src/runtime/atomic_mipsx.s index 1eacd273b44154..ac255fe7e62638 100644 --- a/src/runtime/atomic_mipsx.s +++ b/src/runtime/atomic_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "textflag.h" diff --git a/src/runtime/atomic_ppc64x.s b/src/runtime/atomic_ppc64x.s index b63de2dbd311cd..4742b6cf56789d 100644 --- a/src/runtime/atomic_ppc64x.s +++ b/src/runtime/atomic_ppc64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" diff --git a/src/runtime/auxv_none.go b/src/runtime/auxv_none.go index 3178f1a154cec3..5d473cab5ca482 100644 --- a/src/runtime/auxv_none.go +++ b/src/runtime/auxv_none.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !solaris -// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!solaris package runtime diff --git a/src/runtime/callers_test.go b/src/runtime/callers_test.go index 3cf3fbe5acf6f4..d245cbd2d2f1cc 100644 --- a/src/runtime/callers_test.go +++ b/src/runtime/callers_test.go @@ -309,3 +309,33 @@ func TestCallersDeferNilFuncPanicWithLoop(t *testing.T) { // function exit, rather than at the defer statement. state = 2 } + +// issue #51988 +// Func.Endlineno was lost when instantiating generic functions, leading to incorrect +// stack trace positions. +func TestCallersEndlineno(t *testing.T) { + testNormalEndlineno(t) + testGenericEndlineno[int](t) +} + +func testNormalEndlineno(t *testing.T) { + defer testCallerLine(t, callerLine(t, 0)+1) +} + +func testGenericEndlineno[_ any](t *testing.T) { + defer testCallerLine(t, callerLine(t, 0)+1) +} + +func testCallerLine(t *testing.T, want int) { + if have := callerLine(t, 1); have != want { + t.Errorf("callerLine(1) returned %d, but want %d\n", have, want) + } +} + +func callerLine(t *testing.T, skip int) int { + _, _, line, ok := runtime.Caller(skip + 1) + if !ok { + t.Fatalf("runtime.Caller(%d) failed", skip+1) + } + return line +} diff --git a/src/runtime/cgo.go b/src/runtime/cgo.go index 395d54a66e249b..d90468240df97c 100644 --- a/src/runtime/cgo.go +++ b/src/runtime/cgo.go @@ -42,7 +42,7 @@ var cgoHasExtraM bool // 2) they keep the argument alive until the call site; the call is emitted after // the end of the (presumed) use of the argument by C. // cgoUse should not actually be called (see cgoAlwaysFalse). -func cgoUse(interface{}) { throw("cgoUse should not be called") } +func cgoUse(any) { throw("cgoUse should not be called") } // cgoAlwaysFalse is a boolean value that is always false. // The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }. diff --git a/src/runtime/cgo/abi_arm64.h b/src/runtime/cgo/abi_arm64.h new file mode 100644 index 00000000000000..e2b5e6d0be99ec --- /dev/null +++ b/src/runtime/cgo/abi_arm64.h @@ -0,0 +1,43 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP). +// +// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP). +// +// R29 is not saved because Go will save and restore it. + +#define SAVE_R19_TO_R28(offset) \ + STP (R19, R20), ((offset)+0*8)(RSP) \ + STP (R21, R22), ((offset)+2*8)(RSP) \ + STP (R23, R24), ((offset)+4*8)(RSP) \ + STP (R25, R26), ((offset)+6*8)(RSP) \ + STP (R27, g), ((offset)+8*8)(RSP) + +#define RESTORE_R19_TO_R28(offset) \ + LDP ((offset)+0*8)(RSP), (R19, R20) \ + LDP ((offset)+2*8)(RSP), (R21, R22) \ + LDP ((offset)+4*8)(RSP), (R23, R24) \ + LDP ((offset)+6*8)(RSP), (R25, R26) \ + LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */ + +#define SAVE_F8_TO_F15(offset) \ + FSTPD (F8, F9), ((offset)+0*8)(RSP) \ + FSTPD (F10, F11), ((offset)+2*8)(RSP) \ + FSTPD (F12, F13), ((offset)+4*8)(RSP) \ + FSTPD (F14, F15), ((offset)+6*8)(RSP) + +#define RESTORE_F8_TO_F15(offset) \ + FLDPD ((offset)+0*8)(RSP), (F8, F9) \ + FLDPD ((offset)+2*8)(RSP), (F10, F11) \ + FLDPD ((offset)+4*8)(RSP), (F12, F13) \ + FLDPD ((offset)+6*8)(RSP), (F14, F15) + diff --git a/src/runtime/cgo/asm_arm64.s b/src/runtime/cgo/asm_arm64.s index 1cb25cf89efd28..e808dedcfc5815 100644 --- a/src/runtime/cgo/asm_arm64.s +++ b/src/runtime/cgo/asm_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "abi_arm64.h" // Called by C code generated by cmd/cgo. // func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) @@ -14,57 +15,23 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 * push 3 args for fn (R0, R1, R3), skipping R2. * Also note that at procedure entry in gc world, 8(RSP) will be the * first arg. - * TODO(minux): use LDP/STP here if it matters. */ SUB $(8*24), RSP - MOVD R0, (8*1)(RSP) - MOVD R1, (8*2)(RSP) + STP (R0, R1), (8*1)(RSP) MOVD R3, (8*3)(RSP) - MOVD R19, (8*4)(RSP) - MOVD R20, (8*5)(RSP) - MOVD R21, (8*6)(RSP) - MOVD R22, (8*7)(RSP) - MOVD R23, (8*8)(RSP) - MOVD R24, (8*9)(RSP) - MOVD R25, (8*10)(RSP) - MOVD R26, (8*11)(RSP) - MOVD R27, (8*12)(RSP) - MOVD g, (8*13)(RSP) - MOVD R29, (8*14)(RSP) - MOVD R30, (8*15)(RSP) - FMOVD F8, (8*16)(RSP) - FMOVD F9, (8*17)(RSP) - FMOVD F10, (8*18)(RSP) - FMOVD F11, (8*19)(RSP) - FMOVD F12, (8*20)(RSP) - FMOVD F13, (8*21)(RSP) - FMOVD F14, (8*22)(RSP) - FMOVD F15, (8*23)(RSP) + + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) + STP (R29, R30), (8*22)(RSP) + // Initialize Go ABI environment BL runtime·load_g(SB) - BL runtime·cgocallback(SB) - MOVD (8*4)(RSP), R19 - MOVD (8*5)(RSP), R20 - MOVD (8*6)(RSP), R21 - MOVD (8*7)(RSP), R22 - MOVD (8*8)(RSP), R23 - MOVD (8*9)(RSP), R24 - MOVD (8*10)(RSP), R25 - MOVD (8*11)(RSP), R26 - MOVD (8*12)(RSP), R27 - MOVD (8*13)(RSP), g - MOVD (8*14)(RSP), R29 - MOVD (8*15)(RSP), R30 - FMOVD (8*16)(RSP), F8 - FMOVD (8*17)(RSP), F9 - FMOVD (8*18)(RSP), F10 - FMOVD (8*19)(RSP), F11 - FMOVD (8*20)(RSP), F12 - FMOVD (8*21)(RSP), F13 - FMOVD (8*22)(RSP), F14 - FMOVD (8*23)(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + LDP (8*22)(RSP), (R29, R30) + ADD $(8*24), RSP RET diff --git a/src/runtime/cgo/asm_loong64.s b/src/runtime/cgo/asm_loong64.s new file mode 100644 index 00000000000000..961a3dd48426c7 --- /dev/null +++ b/src/runtime/cgo/asm_loong64.s @@ -0,0 +1,67 @@ +// Copyright 2022 The Go 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" + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R4, R5, R7), skipping R6. + * Also note that at procedure entry in gc world, 8(R29) will be the + * first arg. + */ + + ADDV $(-8*22), R3 + MOVV R4, (8*1)(R3) // fn unsafe.Pointer + MOVV R5, (8*2)(R3) // a unsafe.Pointer + MOVV R7, (8*3)(R3) // ctxt uintptr + MOVV R23, (8*4)(R3) + MOVV R24, (8*5)(R3) + MOVV R25, (8*6)(R3) + MOVV R26, (8*7)(R3) + MOVV R27, (8*8)(R3) + MOVV R28, (8*9)(R3) + MOVV R29, (8*10)(R3) + MOVV R30, (8*11)(R3) + MOVV g, (8*12)(R3) + MOVV R1, (8*13)(R3) + MOVD F24, (8*14)(R3) + MOVD F25, (8*15)(R3) + MOVD F26, (8*16)(R3) + MOVD F27, (8*17)(R3) + MOVD F28, (8*18)(R3) + MOVD F29, (8*19)(R3) + MOVD F30, (8*20)(R3) + MOVD F31, (8*21)(R3) + + // Initialize Go ABI environment + JAL runtime·load_g(SB) + + JAL runtime·cgocallback(SB) + + MOVV (8*4)(R3), R23 + MOVV (8*5)(R3), R24 + MOVV (8*6)(R3), R25 + MOVV (8*7)(R3), R26 + MOVV (8*8)(R3), R27 + MOVV (8*9)(R3), R28 + MOVV (8*10)(R3), R29 + MOVV (8*11)(R3), R30 + MOVV (8*12)(R3), g + MOVV (8*13)(R3), R1 + MOVD (8*14)(R3), F24 + MOVD (8*15)(R3), F25 + MOVD (8*16)(R3), F26 + MOVD (8*17)(R3), F27 + MOVD (8*18)(R3), F28 + MOVD (8*19)(R3), F29 + MOVD (8*20)(R3), F30 + MOVD (8*21)(R3), F31 + ADDV $(8*22), R3 + + RET diff --git a/src/runtime/cgo/asm_riscv64.s b/src/runtime/cgo/asm_riscv64.s index fcd1d36ca84f80..45151bf02bacbe 100644 --- a/src/runtime/cgo/asm_riscv64.s +++ b/src/runtime/cgo/asm_riscv64.s @@ -14,7 +14,7 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 * registers. Note that at procedure entry the first argument is at * 8(X2). */ - ADD $(-8*31), X2 + ADD $(-8*29), X2 MOV X10, (8*1)(X2) // fn unsafe.Pointer MOV X11, (8*2)(X2) // a unsafe.Pointer MOV X13, (8*3)(X2) // ctxt uintptr @@ -30,21 +30,19 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 MOV X25, (8*13)(X2) MOV X26, (8*14)(X2) MOV g, (8*15)(X2) - MOV X3, (8*16)(X2) - MOV X4, (8*17)(X2) - MOV X1, (8*18)(X2) - MOVD F8, (8*19)(X2) - MOVD F9, (8*20)(X2) - MOVD F18, (8*21)(X2) - MOVD F19, (8*22)(X2) - MOVD F20, (8*23)(X2) - MOVD F21, (8*24)(X2) - MOVD F22, (8*25)(X2) - MOVD F23, (8*26)(X2) - MOVD F24, (8*27)(X2) - MOVD F25, (8*28)(X2) - MOVD F26, (8*29)(X2) - MOVD F27, (8*30)(X2) + MOV X1, (8*16)(X2) + MOVD F8, (8*17)(X2) + MOVD F9, (8*18)(X2) + MOVD F18, (8*19)(X2) + MOVD F19, (8*20)(X2) + MOVD F20, (8*21)(X2) + MOVD F21, (8*22)(X2) + MOVD F22, (8*23)(X2) + MOVD F23, (8*24)(X2) + MOVD F24, (8*25)(X2) + MOVD F25, (8*26)(X2) + MOVD F26, (8*27)(X2) + MOVD F27, (8*28)(X2) // Initialize Go ABI environment CALL runtime·load_g(SB) @@ -62,21 +60,19 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 MOV (8*13)(X2), X25 MOV (8*14)(X2), X26 MOV (8*15)(X2), g - MOV (8*16)(X2), X3 - MOV (8*17)(X2), X4 - MOV (8*18)(X2), X1 - MOVD (8*19)(X2), F8 - MOVD (8*20)(X2), F9 - MOVD (8*21)(X2), F18 - MOVD (8*22)(X2), F19 - MOVD (8*23)(X2), F20 - MOVD (8*24)(X2), F21 - MOVD (8*25)(X2), F22 - MOVD (8*26)(X2), F23 - MOVD (8*27)(X2), F24 - MOVD (8*28)(X2), F25 - MOVD (8*29)(X2), F26 - MOVD (8*30)(X2), F27 - ADD $(8*31), X2 + MOV (8*16)(X2), X1 + MOVD (8*17)(X2), F8 + MOVD (8*18)(X2), F9 + MOVD (8*19)(X2), F18 + MOVD (8*20)(X2), F19 + MOVD (8*21)(X2), F20 + MOVD (8*22)(X2), F21 + MOVD (8*23)(X2), F22 + MOVD (8*24)(X2), F23 + MOVD (8*25)(X2), F24 + MOVD (8*26)(X2), F25 + MOVD (8*27)(X2), F26 + MOVD (8*28)(X2), F27 + ADD $(8*29), X2 RET diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go index cd8b79538745dd..e7c8ef3e07c2b3 100644 --- a/src/runtime/cgo/callbacks.go +++ b/src/runtime/cgo/callbacks.go @@ -21,6 +21,7 @@ import "unsafe" // that pattern working. In particular, crosscall2 actually takes four // arguments, but it works to call it with three arguments when // calling _cgo_panic. +// //go:cgo_export_static crosscall2 //go:cgo_export_dynamic crosscall2 diff --git a/src/runtime/cgo/callbacks_aix.go b/src/runtime/cgo/callbacks_aix.go index f4b6fe25fa268f..8f756fbdd9f434 100644 --- a/src/runtime/cgo/callbacks_aix.go +++ b/src/runtime/cgo/callbacks_aix.go @@ -6,6 +6,7 @@ package cgo // These functions must be exported in order to perform // longcall on cgo programs (cf gcc_aix_ppc64.c). +// //go:cgo_export_static __cgo_topofstack //go:cgo_export_static runtime.rt0_go //go:cgo_export_static _rt0_ppc64_aix_lib diff --git a/src/runtime/cgo/callbacks_traceback.go b/src/runtime/cgo/callbacks_traceback.go index 7302c1eedfa646..dae31a8fcde16c 100644 --- a/src/runtime/cgo/callbacks_traceback.go +++ b/src/runtime/cgo/callbacks_traceback.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || linux -// +build darwin linux package cgo diff --git a/src/runtime/cgo/cgo.go b/src/runtime/cgo/cgo.go index 4d2caf6c4f326c..b8473e532d4f7e 100644 --- a/src/runtime/cgo/cgo.go +++ b/src/runtime/cgo/cgo.go @@ -21,14 +21,20 @@ package cgo #cgo openbsd LDFLAGS: -lpthread #cgo aix LDFLAGS: -Wl,-berok #cgo solaris LDFLAGS: -lxnet -#cgo illumos LDFLAGS: -lsocket +#cgo solaris LDFLAGS: -lsocket -// Issue 35247. -#cgo darwin CFLAGS: -Wno-nullability-completeness - -#cgo CFLAGS: -Wall -Werror +// We use -fno-stack-protector because internal linking won't find +// the support functions. See issues #52919 and #54313. +#cgo CFLAGS: -Wall -Werror -fno-stack-protector #cgo solaris CPPFLAGS: -D_POSIX_PTHREAD_SEMANTICS */ import "C" + +import "runtime/internal/sys" + +// Incomplete is used specifically for the semantics of incomplete C types. +type Incomplete struct { + _ sys.NotInHeap +} diff --git a/src/runtime/cgo/dragonfly.go b/src/runtime/cgo/dragonfly.go index cfa6fe86e2f360..36d70e34e2d39f 100644 --- a/src/runtime/cgo/dragonfly.go +++ b/src/runtime/cgo/dragonfly.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly -// +build dragonfly package cgo diff --git a/src/runtime/cgo/freebsd.go b/src/runtime/cgo/freebsd.go index d56702ec704b2a..2d9f6245b54360 100644 --- a/src/runtime/cgo/freebsd.go +++ b/src/runtime/cgo/freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd -// +build freebsd package cgo diff --git a/src/runtime/cgo/gcc_amd64.S b/src/runtime/cgo/gcc_amd64.S index d75f8646663ab4..46699d1d9ccaf1 100644 --- a/src/runtime/cgo/gcc_amd64.S +++ b/src/runtime/cgo/gcc_amd64.S @@ -12,7 +12,7 @@ #endif /* - * void crosscall_amd64(void (*fn)(void)) + * void crosscall_amd64(void (*fn)(void), void (*setg_gcc)(void*), void *g) * * Calling into the 6c tool chain, where all registers are caller save. * Called from standard x86-64 ABI, where %rbx, %rbp, %r12-%r15 diff --git a/src/runtime/cgo/gcc_darwin_arm64.c b/src/runtime/cgo/gcc_darwin_arm64.c index a5f07f1f1bc71b..24be6758d928ac 100644 --- a/src/runtime/cgo/gcc_darwin_arm64.c +++ b/src/runtime/cgo/gcc_darwin_arm64.c @@ -37,7 +37,6 @@ _cgo_sys_thread_start(ThreadStart *ts) pthread_sigmask(SIG_SETMASK, &ign, &oset); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_freebsd_arm.c b/src/runtime/cgo/gcc_freebsd_arm.c index 74f2e0ede54a08..5f899783799061 100644 --- a/src/runtime/cgo/gcc_freebsd_arm.c +++ b/src/runtime/cgo/gcc_freebsd_arm.c @@ -37,7 +37,6 @@ x_cgo_init(G *g, void (*setg)(void*)) pthread_attr_destroy(&attr); } - void _cgo_sys_thread_start(ThreadStart *ts) { @@ -50,12 +49,7 @@ _cgo_sys_thread_start(ThreadStart *ts) SIGFILLSET(ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c index ad5038667a5b62..2b5896bb221c6c 100644 --- a/src/runtime/cgo/gcc_libinit_windows.c +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -13,6 +13,16 @@ #include #include "libcgo.h" +#include "libcgo_windows.h" + +// Ensure there's one symbol marked __declspec(dllexport). +// If there are no exported symbols, the unfortunate behavior of +// the binutils linker is to also strip the relocations table, +// resulting in non-PIE binary. The other option is the +// --export-all-symbols flag, but we don't need to export all symbols +// and this may overflow the export table (#40795). +// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 +__declspec(dllexport) int _cgo_dummy_export; static volatile LONG runtime_init_once_gate = 0; static volatile LONG runtime_init_once_done = 0; @@ -53,13 +63,7 @@ _cgo_maybe_run_preinit() { void x_cgo_sys_thread_create(void (*func)(void*), void* arg) { - uintptr_t thandle; - - thandle = _beginthread(func, 0, arg); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(func, arg); } int @@ -123,3 +127,25 @@ void (*(_cgo_get_context_function(void)))(struct context_arg*) { LeaveCriticalSection(&runtime_init_cs); return ret; } + +void _cgo_beginthread(void (*func)(void*), void* arg) { + int tries; + uintptr_t thandle; + + for (tries = 0; tries < 20; tries++) { + thandle = _beginthread(func, 0, arg); + if (thandle == -1 && errno == EACCES) { + // "Insufficient resources", try again in a bit. + // + // Note that the first Sleep(0) is a yield. + Sleep(tries); // milliseconds + continue; + } else if (thandle == -1) { + break; + } + return; // Success! + } + + fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); + abort(); +} diff --git a/src/runtime/cgo/gcc_linux_386.c b/src/runtime/cgo/gcc_linux_386.c index 70c942aeb842d9..0ce93596166961 100644 --- a/src/runtime/cgo/gcc_linux_386.c +++ b/src/runtime/cgo/gcc_linux_386.c @@ -43,12 +43,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_linux_amd64.c b/src/runtime/cgo/gcc_linux_amd64.c index c25e7e769ba23d..fb164c1a1df03a 100644 --- a/src/runtime/cgo/gcc_linux_amd64.c +++ b/src/runtime/cgo/gcc_linux_amd64.c @@ -44,7 +44,9 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) } pthread_attr_init(attr); pthread_attr_getstacksize(attr, &size); - g->stacklo = (uintptr)&size - size + 4096; + g->stacklo = (uintptr)__builtin_frame_address(0) - size + 4096; + if (g->stacklo >= g->stackhi) + fatalf("bad stack bounds: lo=%p hi=%p\n", g->stacklo, g->stackhi); pthread_attr_destroy(attr); free(attr); diff --git a/src/runtime/cgo/gcc_linux_arm.c b/src/runtime/cgo/gcc_linux_arm.c index 5bc0fee90d4949..5e97a9ed314b35 100644 --- a/src/runtime/cgo/gcc_linux_arm.c +++ b/src/runtime/cgo/gcc_linux_arm.c @@ -25,12 +25,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_linux_arm64.c b/src/runtime/cgo/gcc_linux_arm64.c index 17ff274fbbeae6..dac45e418be295 100644 --- a/src/runtime/cgo/gcc_linux_arm64.c +++ b/src/runtime/cgo/gcc_linux_arm64.c @@ -27,12 +27,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_linux_loong64.c b/src/runtime/cgo/gcc_linux_loong64.c new file mode 100644 index 00000000000000..96a06eb96018be --- /dev/null +++ b/src/runtime/cgo/gcc_linux_loong64.c @@ -0,0 +1,69 @@ +// Copyright 2022 The Go 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 +#include +#include +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase); +static void (*setg_gcc)(void*); + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + pthread_attr_t attr; + size_t size; + + setg_gcc = setg; + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stacklo = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); + + if (x_cgo_inittls) { + x_cgo_inittls(tlsg, tlsbase); + } +} diff --git a/src/runtime/cgo/gcc_linux_mips64x.c b/src/runtime/cgo/gcc_linux_mips64x.c index 42837b14df54ae..3ea29b0f86a1c6 100644 --- a/src/runtime/cgo/gcc_linux_mips64x.c +++ b/src/runtime/cgo/gcc_linux_mips64x.c @@ -29,12 +29,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_linux_mipsx.c b/src/runtime/cgo/gcc_linux_mipsx.c index a44ea3057da44e..3b60a0ea5899b4 100644 --- a/src/runtime/cgo/gcc_linux_mipsx.c +++ b/src/runtime/cgo/gcc_linux_mipsx.c @@ -29,12 +29,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; @@ -68,7 +63,6 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) setg_gcc = setg; - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); g->stacklo = (uintptr)&attr - size + 4096; diff --git a/src/runtime/cgo/gcc_linux_riscv64.c b/src/runtime/cgo/gcc_linux_riscv64.c index 22b76c2fccf405..99c2866de42c08 100644 --- a/src/runtime/cgo/gcc_linux_riscv64.c +++ b/src/runtime/cgo/gcc_linux_riscv64.c @@ -25,12 +25,7 @@ _cgo_sys_thread_start(ThreadStart *ts) sigfillset(&ign); pthread_sigmask(SIG_SETMASK, &ign, &oset); - // Not sure why the memset is necessary here, - // but without it, we get a bogus stack size - // out of pthread_attr_getstacksize. C'est la Linux. - memset(&attr, 0, sizeof attr); pthread_attr_init(&attr); - size = 0; pthread_attr_getstacksize(&attr, &size); // Leave stacklo=0 and set stackhi=size; mstart will do the rest. ts->g->stackhi = size; diff --git a/src/runtime/cgo/gcc_loong64.S b/src/runtime/cgo/gcc_loong64.S new file mode 100644 index 00000000000000..100aa33cc2baf0 --- /dev/null +++ b/src/runtime/cgo/gcc_loong64.S @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard lp64d ABI, where $r1, $r3, $r23-$r30, and $f24-$f31 + * are callee-save, so they must be saved explicitly, along with $r1 (LR). + */ +.globl crosscall1 +crosscall1: + addi.d $r3, $r3, -160 + st.d $r1, $r3, 0 + st.d $r23, $r3, 8 + st.d $r24, $r3, 16 + st.d $r25, $r3, 24 + st.d $r26, $r3, 32 + st.d $r27, $r3, 40 + st.d $r28, $r3, 48 + st.d $r29, $r3, 56 + st.d $r30, $r3, 64 + st.d $r2, $r3, 72 + st.d $r22, $r3, 80 + fst.d $f24, $r3, 88 + fst.d $f25, $r3, 96 + fst.d $f26, $r3, 104 + fst.d $f27, $r3, 112 + fst.d $f28, $r3, 120 + fst.d $f29, $r3, 128 + fst.d $f30, $r3, 136 + fst.d $f31, $r3, 144 + + move $r18, $r4 // save R4 + move $r19, $r6 + jirl $r1, $r5, 0 // call setg_gcc (clobbers R4) + jirl $r1, $r18, 0 // call fn + + ld.d $r23, $r3, 8 + ld.d $r24, $r3, 16 + ld.d $r25, $r3, 24 + ld.d $r26, $r3, 32 + ld.d $r27, $r3, 40 + ld.d $r28, $r3, 48 + ld.d $r29, $r3, 56 + ld.d $r30, $r3, 64 + ld.d $r2, $r3, 72 + ld.d $r22, $r3, 80 + fld.d $f24, $r3, 88 + fld.d $f25, $r3, 96 + fld.d $f26, $r3, 104 + fld.d $f27, $r3, 112 + fld.d $f28, $r3, 120 + fld.d $f29, $r3, 128 + fld.d $f30, $r3, 136 + fld.d $f31, $r3, 144 + ld.d $r1, $r3, 0 + addi.d $r3, $r3, 160 + jirl $r0, $r1, 0 + + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c index dd283151f17055..fcf1e50740e855 100644 --- a/src/runtime/cgo/gcc_sigaction.c +++ b/src/runtime/cgo/gcc_sigaction.c @@ -49,13 +49,13 @@ x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *ol sigemptyset(&act.sa_mask); for (i = 0; i < 8 * sizeof(goact->mask); i++) { if (goact->mask & ((uint64_t)(1)<flags & ~SA_RESTORER; + act.sa_flags = (int)(goact->flags & ~(uint64_t)SA_RESTORER); } - ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); + ret = sigaction((int)signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); if (ret == -1) { // runtime.rt_sigaction expects _cgo_sigaction to return errno on error. _cgo_tsan_release(); @@ -70,11 +70,11 @@ x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *ol } oldgoact->mask = 0; for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) { - if (sigismember(&oldact.sa_mask, i+1) == 1) { + if (sigismember(&oldact.sa_mask, (int)(i+1)) == 1) { oldgoact->mask |= (uint64_t)(1)<flags = oldact.sa_flags; + oldgoact->flags = (uint64_t)oldact.sa_flags; } _cgo_tsan_release(); diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c index 60cb011bf24578..56fbaac9b831fa 100644 --- a/src/runtime/cgo/gcc_windows_386.c +++ b/src/runtime/cgo/gcc_windows_386.c @@ -22,13 +22,7 @@ x_cgo_init(G *g) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } static void @@ -50,6 +44,6 @@ threadentry(void *v) "movl %1, 0(%%eax)\n" // MOVL g, 0(FS) :: "r"(ts.tls), "r"(ts.g) : "%eax" ); - + crosscall_386(ts.fn); } diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c index 9df9b9b1e4a031..996947eccf4baa 100644 --- a/src/runtime/cgo/gcc_windows_amd64.c +++ b/src/runtime/cgo/gcc_windows_amd64.c @@ -24,13 +24,7 @@ x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } static void diff --git a/src/runtime/cgo/gcc_windows_arm64.c b/src/runtime/cgo/gcc_windows_arm64.c index 61ef094866a07e..8f113cc3b16727 100644 --- a/src/runtime/cgo/gcc_windows_arm64.c +++ b/src/runtime/cgo/gcc_windows_arm64.c @@ -23,13 +23,7 @@ x_cgo_init(G *g, void (*setg)(void*)) void _cgo_sys_thread_start(ThreadStart *ts) { - uintptr_t thandle; - - thandle = _beginthread(threadentry, 0, ts); - if(thandle == -1) { - fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); - abort(); - } + _cgo_beginthread(threadentry, ts); } extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); diff --git a/src/runtime/cgo/handle.go b/src/runtime/cgo/handle.go index 720acca802c882..d711900d7990ee 100644 --- a/src/runtime/cgo/handle.go +++ b/src/runtime/cgo/handle.go @@ -59,6 +59,41 @@ import ( // void myprint(uintptr_t handle) { // MyGoPrint(handle); // } +// +// Some C functions accept a void* argument that points to an arbitrary +// data value supplied by the caller. It is not safe to coerce a cgo.Handle +// (an integer) to a Go unsafe.Pointer, but instead we can pass the address +// of the cgo.Handle to the void* parameter, as in this variant of the +// previous example: +// +// package main +// +// /* +// extern void MyGoPrint(void *context); +// static inline void myprint(void *context) { +// MyGoPrint(context); +// } +// */ +// import "C" +// import ( +// "runtime/cgo" +// "unsafe" +// ) +// +// //export MyGoPrint +// func MyGoPrint(context unsafe.Pointer) { +// h := *(*cgo.Handle)(context) +// val := h.Value().(string) +// println(val) +// h.Delete() +// } +// +// func main() { +// val := "hello Go" +// h := cgo.NewHandle(val) +// C.myprint(unsafe.Pointer(&h)) +// // Output: hello Go +// } type Handle uintptr // NewHandle returns a handle for a given value. @@ -70,7 +105,7 @@ type Handle uintptr // // The intended use is to pass the returned handle to C code, which // passes it back to Go, which calls Value. -func NewHandle(v interface{}) Handle { +func NewHandle(v any) Handle { h := atomic.AddUintptr(&handleIdx, 1) if h == 0 { panic("runtime/cgo: ran out of handle space") @@ -83,7 +118,7 @@ func NewHandle(v interface{}) Handle { // Value returns the associated Go value for a valid handle. // // The method panics if the handle is invalid. -func (h Handle) Value() interface{} { +func (h Handle) Value() any { v, ok := handles.Load(uintptr(h)) if !ok { panic("runtime/cgo: misuse of an invalid Handle") diff --git a/src/runtime/cgo/handle_test.go b/src/runtime/cgo/handle_test.go index 738051a0ea1304..b341c8e1e4463b 100644 --- a/src/runtime/cgo/handle_test.go +++ b/src/runtime/cgo/handle_test.go @@ -13,8 +13,8 @@ func TestHandle(t *testing.T) { v := 42 tests := []struct { - v1 interface{} - v2 interface{} + v1 any + v2 any }{ {v1: v, v2: v}, {v1: &v, v2: &v}, @@ -44,7 +44,7 @@ func TestHandle(t *testing.T) { } siz := 0 - handles.Range(func(k, v interface{}) bool { + handles.Range(func(k, v any) bool { siz++ return true }) diff --git a/src/runtime/cgo/libcgo_windows.h b/src/runtime/cgo/libcgo_windows.h index 0013f06baebd1b..33d7637fece0ec 100644 --- a/src/runtime/cgo/libcgo_windows.h +++ b/src/runtime/cgo/libcgo_windows.h @@ -2,11 +2,5 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Ensure there's one symbol marked __declspec(dllexport). -// If there are no exported symbols, the unfortunate behavior of -// the binutils linker is to also strip the relocations table, -// resulting in non-PIE binary. The other option is the -// --export-all-symbols flag, but we don't need to export all symbols -// and this may overflow the export table (#40795). -// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 -__declspec(dllexport) int _cgo_dummy_export; +// Call _beginthread, aborting on failure. +void _cgo_beginthread(void (*func)(void*), void* arg); diff --git a/src/runtime/cgo/linux.go b/src/runtime/cgo/linux.go index 070d531beeb5d7..1d6fe03917b819 100644 --- a/src/runtime/cgo/linux.go +++ b/src/runtime/cgo/linux.go @@ -6,7 +6,6 @@ // corresponding cgo->libc (nptl) wrappers for various system calls. //go:build linux -// +build linux package cgo diff --git a/src/runtime/cgo/mmap.go b/src/runtime/cgo/mmap.go index 347ae6b363f002..eae0a9e7cc859d 100644 --- a/src/runtime/cgo/mmap.go +++ b/src/runtime/cgo/mmap.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && amd64) || (linux && arm64) -// +build linux,amd64 linux,arm64 package cgo diff --git a/src/runtime/cgo/netbsd.go b/src/runtime/cgo/netbsd.go index 7e17d1fcd25706..8a8018b7a89b5c 100644 --- a/src/runtime/cgo/netbsd.go +++ b/src/runtime/cgo/netbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build netbsd -// +build netbsd package cgo diff --git a/src/runtime/cgo/openbsd.go b/src/runtime/cgo/openbsd.go index f6215613c33419..26b62fbdaf7596 100644 --- a/src/runtime/cgo/openbsd.go +++ b/src/runtime/cgo/openbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd -// +build openbsd package cgo @@ -18,4 +17,5 @@ var _guard_local uintptr // This is normally marked as hidden and placed in the // .openbsd.randomdata section. +// //go:cgo_export_dynamic __guard_local __guard_local diff --git a/src/runtime/cgo/setenv.go b/src/runtime/cgo/setenv.go index e6e8040ae088c8..2247cb2b59ea9f 100644 --- a/src/runtime/cgo/setenv.go +++ b/src/runtime/cgo/setenv.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package cgo diff --git a/src/runtime/cgo/sigaction.go b/src/runtime/cgo/sigaction.go index 692fd2675f9475..dc714f7ef4a3b0 100644 --- a/src/runtime/cgo/sigaction.go +++ b/src/runtime/cgo/sigaction.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && amd64) || (freebsd && amd64) || (linux && arm64) || (linux && ppc64le) -// +build linux,amd64 freebsd,amd64 linux,arm64 linux,ppc64le package cgo diff --git a/src/runtime/cgo_mmap.go b/src/runtime/cgo_mmap.go index 17d26b4cc82f66..4cb3e65f14ddd4 100644 --- a/src/runtime/cgo_mmap.go +++ b/src/runtime/cgo_mmap.go @@ -5,7 +5,6 @@ // Support for memory sanitizer. See runtime/cgo/mmap.go. //go:build (linux && amd64) || (linux && arm64) -// +build linux,amd64 linux,arm64 package runtime @@ -13,11 +12,13 @@ import "unsafe" // _cgo_mmap is filled in by runtime/cgo when it is linked into the // program, so it is only non-nil when using cgo. +// //go:linkname _cgo_mmap _cgo_mmap var _cgo_mmap unsafe.Pointer // _cgo_munmap is filled in by runtime/cgo when it is linked into the // program, so it is only non-nil when using cgo. +// //go:linkname _cgo_munmap _cgo_munmap var _cgo_munmap unsafe.Pointer @@ -25,6 +26,7 @@ var _cgo_munmap unsafe.Pointer // support sanitizer interceptors. Don't allow stack splits, since this function // (used by sysAlloc) is called in a lot of low-level parts of the runtime and // callers often assume it won't acquire any locks. +// //go:nosplit func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) { if _cgo_mmap != nil { diff --git a/src/runtime/cgo_ppc64x.go b/src/runtime/cgo_ppc64x.go index 4dc92ea3211b87..c723213809e6d0 100644 --- a/src/runtime/cgo_ppc64x.go +++ b/src/runtime/cgo_ppc64x.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package runtime // crosscall_ppc64 calls into the runtime to set up the registers the // Go runtime expects and so the symbol it calls needs to be exported // for external linking to work. +// //go:cgo_export_static _cgo_reginit diff --git a/src/runtime/cgo_sigaction.go b/src/runtime/cgo_sigaction.go index 6099d1b746f80a..9500c522059580 100644 --- a/src/runtime/cgo_sigaction.go +++ b/src/runtime/cgo_sigaction.go @@ -5,7 +5,6 @@ // Support for sanitizers. See runtime/cgo/sigaction.go. //go:build (linux && amd64) || (freebsd && amd64) || (linux && arm64) || (linux && ppc64le) -// +build linux,amd64 freebsd,amd64 linux,arm64 linux,ppc64le package runtime @@ -13,6 +12,7 @@ import "unsafe" // _cgo_sigaction is filled in by runtime/cgo when it is linked into the // program, so it is only non-nil when using cgo. +// //go:linkname _cgo_sigaction _cgo_sigaction var _cgo_sigaction unsafe.Pointer @@ -28,7 +28,9 @@ func sigaction(sig uint32, new, old *sigactiont) { if msanenabled && new != nil { msanwrite(unsafe.Pointer(new), unsafe.Sizeof(*new)) } - + if asanenabled && new != nil { + asanwrite(unsafe.Pointer(new), unsafe.Sizeof(*new)) + } if _cgo_sigaction == nil || inForkedChild { sysSigaction(sig, new, old) } else { @@ -80,9 +82,13 @@ func sigaction(sig uint32, new, old *sigactiont) { if msanenabled && old != nil { msanread(unsafe.Pointer(old), unsafe.Sizeof(*old)) } + if asanenabled && old != nil { + asanread(unsafe.Pointer(old), unsafe.Sizeof(*old)) + } } // callCgoSigaction calls the sigaction function in the runtime/cgo package // using the GCC calling convention. It is implemented in assembly. +// //go:noescape func callCgoSigaction(sig uintptr, new, old *sigactiont) int32 diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 2626216f9584c5..0a25cb65629023 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -85,7 +85,7 @@ package runtime import ( - "runtime/internal/atomic" + "internal/goarch" "runtime/internal/sys" "unsafe" ) @@ -101,6 +101,7 @@ type argset struct { } // wrapper for syscall package to call cgocall for libc (cgo) calls. +// //go:linkname syscall_cgocaller syscall.cgocaller //go:nosplit //go:uintptrescapes @@ -198,6 +199,7 @@ func cgocall(fn, arg unsafe.Pointer) int32 { } // Call from C back to Go. fn must point to an ABIInternal Go entry-point. +// //go:nosplit func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) { gp := getg() @@ -256,7 +258,7 @@ func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) { // We must still stay on the same m. defer unlockOSThread() - if gp.m.needextram || atomic.Load(&extraMWaiters) > 0 { + if gp.m.needextram || extraMWaiters.Load() > 0 { gp.m.needextram = false systemstack(newextram) } @@ -290,6 +292,13 @@ func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) { <-main_init_done } + // Check whether the profiler needs to be turned on or off; this route to + // run Go code does not use runtime.execute, so bypasses the check there. + hz := sched.profilehz + if gp.m.profilehz != hz { + setThreadCPUProfiler(hz) + } + // Add entry to defer stack in case of panic. restore := true defer unwindm(&restore) @@ -381,7 +390,7 @@ var racecgosync uint64 // represents possible synchronization in C code // cgoCheckPointer checks if the argument contains a Go pointer that // points to a Go pointer, and panics if it does. -func cgoCheckPointer(ptr interface{}, arg interface{}) { +func cgoCheckPointer(ptr any, arg any) { if debug.cgocheck == 0 { return } @@ -480,7 +489,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { if inheap(uintptr(unsafe.Pointer(it))) { panic(errorString(msg)) } - p = *(*unsafe.Pointer)(add(p, sys.PtrSize)) + p = *(*unsafe.Pointer)(add(p, goarch.PtrSize)) if !cgoIsGoPointer(p) { return } @@ -526,7 +535,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { if f.typ.ptrdata == 0 { continue } - cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg) + cgoCheckArg(f.typ, add(p, f.offset), true, top, msg) } case kindPtr, kindUnsafePointer: if indir { @@ -558,17 +567,16 @@ func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) { if base == 0 { return } - hbits := heapBitsForAddr(base) n := span.elemsize - for i = uintptr(0); i < n; i += sys.PtrSize { - if !hbits.morePointers() { - // No more possible pointers. + hbits := heapBitsForAddr(base, n) + for { + var addr uintptr + if hbits, addr = hbits.next(); addr == 0 { break } - if hbits.isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) { + if cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(addr))) { panic(errorString(msg)) } - hbits = hbits.next() } return @@ -590,6 +598,7 @@ func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) { // cgoIsGoPointer reports whether the pointer is a Go pointer--a // pointer to Go memory. We only care about Go memory that might // contain pointers. +// //go:nosplit //go:nowritebarrierrec func cgoIsGoPointer(p unsafe.Pointer) bool { @@ -611,6 +620,7 @@ func cgoIsGoPointer(p unsafe.Pointer) bool { } // cgoInRange reports whether p is between start and end. +// //go:nosplit //go:nowritebarrierrec func cgoInRange(p unsafe.Pointer, start, end uintptr) bool { @@ -620,7 +630,7 @@ func cgoInRange(p unsafe.Pointer, start, end uintptr) bool { // cgoCheckResult is called to check the result parameter of an // exported Go function. It panics if the result is or contains a Go // pointer. -func cgoCheckResult(val interface{}) { +func cgoCheckResult(val any) { if debug.cgocheck == 0 { return } diff --git a/src/runtime/cgocheck.go b/src/runtime/cgocheck.go index 516045c16331dc..84e7516758b1b0 100644 --- a/src/runtime/cgocheck.go +++ b/src/runtime/cgocheck.go @@ -8,7 +8,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -32,14 +32,14 @@ func cgoCheckWriteBarrier(dst *uintptr, src uintptr) { // If we are running on the system stack then dst might be an // address on the stack, which is OK. - g := getg() - if g == g.m.g0 || g == g.m.gsignal { + gp := getg() + if gp == gp.m.g0 || gp == gp.m.gsignal { return } // Allocating memory can write to various mfixalloc structs // that look like they are non-Go memory. - if g.m.mallocing != 0 { + if gp.m.mallocing != 0 { return } @@ -61,6 +61,7 @@ func cgoCheckWriteBarrier(dst *uintptr, src uintptr) { // size is the number of bytes to copy. // It throws if the program is copying a block that contains a Go pointer // into non-Go memory. +// //go:nosplit //go:nowritebarrier func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { @@ -81,6 +82,7 @@ func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { // typ is the element type of the slice. // It throws if the program is copying slice elements that contain Go pointers // into non-Go memory. +// //go:nosplit //go:nowritebarrier func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) { @@ -103,6 +105,7 @@ func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) { // cgoCheckTypedBlock checks the block of memory at src, for up to size bytes, // and throws if it finds a Go pointer. The type of the memory is typ, // and src is off bytes into that type. +// //go:nosplit //go:nowritebarrier func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { @@ -150,41 +153,42 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) { // src must be in the regular heap. - hbits := heapBitsForAddr(uintptr(src)) - for i := uintptr(0); i < off+size; i += sys.PtrSize { - bits := hbits.bits() - if i >= off && bits&bitPointer != 0 { - v := *(*unsafe.Pointer)(add(src, i)) - if cgoIsGoPointer(v) { - throw(cgoWriteBarrierFail) - } + hbits := heapBitsForAddr(uintptr(src), size) + for { + var addr uintptr + if hbits, addr = hbits.next(); addr == 0 { + break + } + v := *(*unsafe.Pointer)(unsafe.Pointer(addr)) + if cgoIsGoPointer(v) { + throw(cgoWriteBarrierFail) } - hbits = hbits.next() } } // cgoCheckBits checks the block of memory at src, for up to size // bytes, and throws if it finds a Go pointer. The gcbits mark each // pointer value. The src pointer is off bytes into the gcbits. +// //go:nosplit //go:nowritebarrier func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) { - skipMask := off / sys.PtrSize / 8 - skipBytes := skipMask * sys.PtrSize * 8 + skipMask := off / goarch.PtrSize / 8 + skipBytes := skipMask * goarch.PtrSize * 8 ptrmask := addb(gcbits, skipMask) src = add(src, skipBytes) off -= skipBytes size += off var bits uint32 - for i := uintptr(0); i < size; i += sys.PtrSize { - if i&(sys.PtrSize*8-1) == 0 { + for i := uintptr(0); i < size; i += goarch.PtrSize { + if i&(goarch.PtrSize*8-1) == 0 { bits = uint32(*ptrmask) ptrmask = addb(ptrmask, 1) } else { bits >>= 1 } if off > 0 { - off -= sys.PtrSize + off -= goarch.PtrSize } else { if bits&1 != 0 { v := *(*unsafe.Pointer)(add(src, i)) @@ -201,6 +205,7 @@ func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) { // 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 //go:systemstack func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) { diff --git a/src/runtime/chan.go b/src/runtime/chan.go index f2a75b30f44596..a9ef0781cef9a0 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -18,6 +18,7 @@ package runtime // c.qcount < c.dataqsiz implies that c.sendq is empty. import ( + "internal/abi" "runtime/internal/atomic" "runtime/internal/math" "unsafe" @@ -138,6 +139,7 @@ func full(c *hchan) bool { } // entry point for c <- x from compiled code +// //go:nosplit func chansend1(c *hchan, elem unsafe.Pointer) { chansend(c, elem, true, getcallerpc()) @@ -169,7 +171,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { } if raceenabled { - racereadpc(c.raceaddr(), callerpc, funcPC(chansend)) + racereadpc(c.raceaddr(), callerpc, abi.FuncPCABIInternal(chansend)) } // Fast path: check for failed non-blocking operation without acquiring the lock. @@ -253,7 +255,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { // to park on a channel. The window between when this G's status // changes and when we set gp.activeStackChans is not safe for // stack shrinking. - atomic.Store8(&gp.parkingOnChan, 1) + gp.parkingOnChan.Store(true) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2) // Ensure the value being sent is kept alive until the // receiver copies it out. The sudog has a pointer to the @@ -365,7 +367,7 @@ func closechan(c *hchan) { if raceenabled { callerpc := getcallerpc() - racewritepc(c.raceaddr(), callerpc, funcPC(closechan)) + racewritepc(c.raceaddr(), callerpc, abi.FuncPCABIInternal(closechan)) racerelease(c.raceaddr()) } @@ -434,6 +436,7 @@ func empty(c *hchan) bool { } // entry points for <- c from compiled code +// //go:nosplit func chanrecv1(c *hchan, elem unsafe.Pointer) { chanrecv(c, elem, true) @@ -507,24 +510,28 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) lock(&c.lock) - if c.closed != 0 && c.qcount == 0 { - if raceenabled { - raceacquire(c.raceaddr()) + if c.closed != 0 { + if c.qcount == 0 { + if raceenabled { + raceacquire(c.raceaddr()) + } + unlock(&c.lock) + if ep != nil { + typedmemclr(c.elemtype, ep) + } + return true, false } - unlock(&c.lock) - if ep != nil { - typedmemclr(c.elemtype, ep) + // The channel has been closed, but the channel's buffer have data. + } else { + // Just found waiting sender with not closed. + if sg := c.sendq.dequeue(); sg != nil { + // Found a waiting sender. If buffer is size 0, receive value + // directly from sender. Otherwise, receive from head of queue + // and add sender's value to the tail of the queue (both map to + // the same buffer slot because the queue is full). + recv(c, sg, ep, func() { unlock(&c.lock) }, 3) + return true, true } - return true, false - } - - if sg := c.sendq.dequeue(); sg != nil { - // Found a waiting sender. If buffer is size 0, receive value - // directly from sender. Otherwise, receive from head of queue - // and add sender's value to the tail of the queue (both map to - // the same buffer slot because the queue is full). - recv(c, sg, ep, func() { unlock(&c.lock) }, 3) - return true, true } if c.qcount > 0 { @@ -572,7 +579,7 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) // to park on a channel. The window between when this G's status // changes and when we set gp.activeStackChans is not safe for // stack shrinking. - atomic.Store8(&gp.parkingOnChan, 1) + gp.parkingOnChan.Store(true) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2) // someone woke us up @@ -593,10 +600,11 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) // recv processes a receive operation on a full channel c. // There are 2 parts: -// 1) The value sent by the sender sg is put into the channel -// and the sender is woken up to go on its merry way. -// 2) The value received by the receiver (the current G) is -// written to ep. +// 1. The value sent by the sender sg is put into the channel +// and the sender is woken up to go on its merry way. +// 2. The value received by the receiver (the current G) is +// written to ep. +// // For synchronous channels, both values are the same. // For asynchronous channels, the receiver gets its data from // the channel buffer and the sender's data is put in the @@ -656,7 +664,7 @@ func chanparkcommit(gp *g, chanLock unsafe.Pointer) bool { // Mark that it's safe for stack shrinking to occur now, // because any thread acquiring this G's stack for shrinking // is guaranteed to observe activeStackChans after this store. - atomic.Store8(&gp.parkingOnChan, 0) + gp.parkingOnChan.Store(false) // Make sure we unlock after setting activeStackChans and // unsetting parkingOnChan. The moment we unlock chanLock // we risk gp getting readied by a channel operation and @@ -682,7 +690,6 @@ func chanparkcommit(gp *g, chanLock unsafe.Pointer) bool { // } else { // ... bar // } -// func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) { return chansend(c, elem, false, getcallerpc()) } @@ -703,7 +710,6 @@ func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) { // } else { // ... bar // } -// func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) { return chanrecv(c, elem, false) } @@ -774,7 +780,7 @@ func (q *waitq) dequeue() *sudog { } else { y.prev = nil q.first = y - sgp.next = nil // mark as removed (see dequeueSudog) + sgp.next = nil // mark as removed (see dequeueSudoG) } // if a goroutine was put on this queue because of a @@ -785,7 +791,7 @@ func (q *waitq) dequeue() *sudog { // We use a flag in the G struct to tell us when someone // else has won the race to signal this goroutine but the goroutine // hasn't removed itself from the queue yet. - if sgp.isSelect && !atomic.Cas(&sgp.g.selectDone, 0, 1) { + if sgp.isSelect && !sgp.g.selectDone.CompareAndSwap(0, 1) { continue } diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go index c9ce3ac643184f..256f97676ed55c 100644 --- a/src/runtime/chan_test.go +++ b/src/runtime/chan_test.go @@ -226,11 +226,13 @@ func TestNonblockRecvRace(t *testing.T) { // This test checks that select acts on the state of the channels at one // moment in the execution, not over a smeared time window. // In the test, one goroutine does: +// // create c1, c2 // make c1 ready for receiving // create second goroutine // make c2 ready for receiving // make c1 no longer ready for receiving (if possible) +// // The second goroutine does a non-blocking select receiving from c1 and c2. // From the time the second goroutine is created, at least one of c1 and c2 // is always ready for receiving, so the select in the second goroutine must @@ -494,7 +496,7 @@ func TestSelectFairness(t *testing.T) { func TestChanSendInterface(t *testing.T) { type mt struct{} m := &mt{} - c := make(chan interface{}, 1) + c := make(chan any, 1) c <- m select { case c <- m: @@ -624,6 +626,13 @@ func TestShrinkStackDuringBlockedSend(t *testing.T) { } func TestNoShrinkStackWhileParking(t *testing.T) { + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" { + testenv.SkipFlaky(t, 49382) + } + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 51482) + } + // The goal of this test is to trigger a "racy sudog adjustment" // throw. Basically, there's a window between when a goroutine // becomes available for preemption for stack scanning (and thus, @@ -1121,6 +1130,19 @@ func BenchmarkSelectProdCons(b *testing.B) { } } +func BenchmarkReceiveDataFromClosedChan(b *testing.B) { + count := b.N + ch := make(chan struct{}, count) + for i := 0; i < count; i++ { + ch <- struct{}{} + } + close(ch) + + b.ResetTimer() + for range ch { + } +} + func BenchmarkChanCreation(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { diff --git a/src/runtime/checkptr_test.go b/src/runtime/checkptr_test.go index d5dd101adbe051..811c0f03553420 100644 --- a/src/runtime/checkptr_test.go +++ b/src/runtime/checkptr_test.go @@ -12,6 +12,12 @@ import ( ) func TestCheckPtr(t *testing.T) { + // This test requires rebuilding packages with -d=checkptr=1, + // so it's somewhat slow. + if testing.Short() { + t.Skip("skipping test in -short mode") + } + t.Parallel() testenv.MustHaveGoRun(t) @@ -33,6 +39,51 @@ func TestCheckPtr(t *testing.T) { {"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"}, {"CheckPtrSliceOK", ""}, {"CheckPtrSliceFail", "fatal error: checkptr: unsafe.Slice result straddles multiple allocations\n"}, + {"CheckPtrStringOK", ""}, + {"CheckPtrStringFail", "fatal error: checkptr: unsafe.String result straddles multiple allocations\n"}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.cmd, func(t *testing.T) { + t.Parallel() + got, err := testenv.CleanCmdEnv(exec.Command(exe, tc.cmd)).CombinedOutput() + if err != nil { + t.Log(err) + } + if tc.want == "" { + if len(got) > 0 { + t.Errorf("output:\n%s\nwant no output", got) + } + return + } + if !strings.HasPrefix(string(got), tc.want) { + t.Errorf("output:\n%s\n\nwant output starting with: %s", got, tc.want) + } + }) + } +} + +func TestCheckPtr2(t *testing.T) { + // This test requires rebuilding packages with -d=checkptr=2, + // so it's somewhat slow. + if testing.Short() { + t.Skip("skipping test in -short mode") + } + + t.Parallel() + testenv.MustHaveGoRun(t) + + exe, err := buildTestProg(t, "testprog", "-gcflags=all=-d=checkptr=2") + if err != nil { + t.Fatal(err) + } + + testCases := []struct { + cmd string + want string + }{ + {"CheckPtrAlignmentNested", "fatal error: checkptr: converted pointer straddles multiple allocations\n"}, } for _, tc := range testCases { diff --git a/src/runtime/compiler.go b/src/runtime/compiler.go index 1ebc62dea1919a..f430a277196845 100644 --- a/src/runtime/compiler.go +++ b/src/runtime/compiler.go @@ -9,5 +9,4 @@ package runtime // // gc Also known as cmd/compile. // gccgo The gccgo front end, part of the GCC compiler suite. -// const Compiler = "gc" diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go index 5104650c5d73e8..bbe93c5bea2d7e 100644 --- a/src/runtime/cpuflags.go +++ b/src/runtime/cpuflags.go @@ -11,10 +11,10 @@ import ( // Offsets into internal/cpu records for use in assembly. const ( - offsetX86HasAVX = unsafe.Offsetof(cpu.X86.HasAVX) - offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) - offsetX86HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) - offsetX86HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + offsetX86HasAVX = unsafe.Offsetof(cpu.X86.HasAVX) + offsetX86HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + offsetX86HasERMS = unsafe.Offsetof(cpu.X86.HasERMS) + offsetX86HasRDTSCP = unsafe.Offsetof(cpu.X86.HasRDTSCP) offsetARMHasIDIVA = unsafe.Offsetof(cpu.ARM.HasIDIVA) diff --git a/src/runtime/cpuprof.go b/src/runtime/cpuprof.go index e5d0193b9cbd4f..6ef374eaa429ad 100644 --- a/src/runtime/cpuprof.go +++ b/src/runtime/cpuprof.go @@ -13,12 +13,26 @@ package runtime import ( - "runtime/internal/atomic" + "internal/abi" "runtime/internal/sys" "unsafe" ) -const maxCPUProfStack = 64 +const ( + maxCPUProfStack = 64 + + // profBufWordCount is the size of the CPU profile buffer's storage for the + // header and stack of each sample, measured in 64-bit words. Every sample + // has a required header of two words. With a small additional header (a + // word or two) and stacks at the profiler's maximum length of 64 frames, + // that capacity can support 1900 samples or 19 thread-seconds at a 100 Hz + // sample rate, at a cost of 1 MiB. + profBufWordCount = 1 << 17 + // profBufTagCount is the size of the CPU profile buffer's storage for the + // goroutine tags associated with each sample. A capacity of 1<<14 means + // room for 16k samples, or 160 thread-seconds at a 100 Hz sample rate. + profBufTagCount = 1 << 14 +) type cpuProfile struct { lock mutex @@ -69,7 +83,7 @@ func SetCPUProfileRate(hz int) { } cpuprof.on = true - cpuprof.log = newProfBuf(1, 1<<17, 1<<14) + cpuprof.log = newProfBuf(1, profBufWordCount, profBufTagCount) hdr := [1]uint64{uint64(hz)} cpuprof.log.write(nil, nanotime(), hdr[:], nil) setcpuprofilerate(int32(hz)) @@ -87,14 +101,16 @@ func SetCPUProfileRate(hz int) { // and cannot allocate memory or acquire locks that might be // held at the time of the signal, nor can it use substantial amounts // of stack. +// //go:nowritebarrierrec -func (p *cpuProfile) add(gp *g, stk []uintptr) { +func (p *cpuProfile) add(tagPtr *unsafe.Pointer, stk []uintptr) { // Simple cas-lock to coordinate with setcpuprofilerate. - for !atomic.Cas(&prof.signalLock, 0, 1) { + for !prof.signalLock.CompareAndSwap(0, 1) { + // TODO: Is it safe to osyield here? https://go.dev/issue/52672 osyield() } - if prof.hz != 0 { // implies cpuprof.log != nil + if prof.hz.Load() != 0 { // implies cpuprof.log != nil if p.numExtra > 0 || p.lostExtra > 0 || p.lostAtomic > 0 { p.addExtra() } @@ -103,19 +119,10 @@ func (p *cpuProfile) add(gp *g, stk []uintptr) { // because otherwise its write barrier behavior may not // be correct. See the long comment there before // changing the argument here. - // - // Note: it can happen on Windows, where we are calling - // p.add with a gp that is not the current g, that gp is nil, - // meaning we interrupted a system thread with no g. - // Avoid faulting in that case. - var tagPtr *unsafe.Pointer - if gp != nil { - tagPtr = &gp.labels - } cpuprof.log.write(tagPtr, nanotime(), hdr[:], stk) } - atomic.Store(&prof.signalLock, 0) + prof.signalLock.Store(0) } // addNonGo adds the non-Go stack trace to the profile. @@ -125,14 +132,18 @@ func (p *cpuProfile) add(gp *g, stk []uintptr) { // Instead, we copy the stack into cpuprof.extra, // which will be drained the next time a Go thread // gets the signal handling event. +// //go:nosplit //go:nowritebarrierrec func (p *cpuProfile) addNonGo(stk []uintptr) { // Simple cas-lock to coordinate with SetCPUProfileRate. // (Other calls to add or addNonGo should be blocked out // by the fact that only one SIGPROF can be handled by the - // process at a time. If not, this lock will serialize those too.) - for !atomic.Cas(&prof.signalLock, 0, 1) { + // process at a time. If not, this lock will serialize those too. + // The use of timer_create(2) on Linux to request process-targeted + // signals may have changed this.) + for !prof.signalLock.CompareAndSwap(0, 1) { + // TODO: Is it safe to osyield here? https://go.dev/issue/52672 osyield() } @@ -145,7 +156,7 @@ func (p *cpuProfile) addNonGo(stk []uintptr) { cpuprof.lostExtra++ } - atomic.Store(&prof.signalLock, 0) + prof.signalLock.Store(0) } // addExtra adds the "extra" profiling events, @@ -166,8 +177,8 @@ func (p *cpuProfile) addExtra() { if p.lostExtra > 0 { hdr := [1]uint64{p.lostExtra} lostStk := [2]uintptr{ - funcPC(_LostExternalCode) + sys.PCQuantum, - funcPC(_ExternalCode) + sys.PCQuantum, + abi.FuncPCABIInternal(_LostExternalCode) + sys.PCQuantum, + abi.FuncPCABIInternal(_ExternalCode) + sys.PCQuantum, } p.log.write(nil, 0, hdr[:], lostStk[:]) p.lostExtra = 0 @@ -176,8 +187,8 @@ func (p *cpuProfile) addExtra() { if p.lostAtomic > 0 { hdr := [1]uint64{p.lostAtomic} lostStk := [2]uintptr{ - funcPC(_LostSIGPROFDuringAtomic64) + sys.PCQuantum, - funcPC(_System) + sys.PCQuantum, + abi.FuncPCABIInternal(_LostSIGPROFDuringAtomic64) + sys.PCQuantum, + abi.FuncPCABIInternal(_System) + sys.PCQuantum, } p.log.write(nil, 0, hdr[:], lostStk[:]) p.lostAtomic = 0 @@ -208,6 +219,8 @@ func runtime_pprof_runtime_cyclesPerSecond() int64 { // If profiling is turned off and all the profile data accumulated while it was // on has been returned, readProfile returns eof=true. // The caller must save the returned data and tags before calling readProfile again. +// The returned data contains a whole number of records, and tags contains +// exactly one entry per record. // //go:linkname runtime_pprof_readProfile runtime/pprof.readProfile func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) { diff --git a/src/runtime/cputicks.go b/src/runtime/cputicks.go index 7c926f4a2b8824..91270617fc7705 100644 --- a/src/runtime/cputicks.go +++ b/src/runtime/cputicks.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !arm && !arm64 && !mips64 && !mips64le && !mips && !mipsle && !wasm -// +build !arm,!arm64,!mips64,!mips64le,!mips,!mipsle,!wasm +//go:build !arm && !arm64 && !loong64 && !mips64 && !mips64le && !mips && !mipsle && !wasm package runtime diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 5729942cee3bfb..5e5871229729ef 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -3,12 +3,10 @@ // license that can be found in the LICENSE file. //go:build cgo -// +build cgo package runtime_test import ( - "bytes" "fmt" "internal/testenv" "os" @@ -65,6 +63,10 @@ func TestCgoCallbackGC(t *testing.T) { t.Skip("too slow for mips64x builders") } } + if testenv.Builder() == "darwin-amd64-10_14" { + // TODO(#23011): When the 10.14 builders are gone, remove this skip. + t.Skip("skipping due to platform bug on macOS 10.14; see https://golang.org/issue/43926") + } got := runTestProg(t, "testprogcgo", "CgoCallbackGC") want := "OK\n" if got != want { @@ -91,23 +93,9 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) { case "plan9", "windows": t.Skipf("no pthreads on %s", runtime.GOOS) } - if runtime.GOARCH == "ppc64" && runtime.GOOS == "linux" { - // TODO(austin) External linking not implemented on - // linux/ppc64 (issue #8912) - t.Skipf("no external linking on ppc64") - } - - exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof") - if err != nil { - t.Fatal(err) - } - - got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() - if err != nil { - t.Fatalf("exit status: %v\n%s", err, got) - } - if want := "OK\n"; string(got) != want { + got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF", "GO_START_SIGPROF_THREAD=1") + if want := "OK\n"; got != want { t.Fatalf("expected %q, but got:\n%s", want, got) } } @@ -120,18 +108,8 @@ func TestCgoExternalThreadSignal(t *testing.T) { t.Skipf("no pthreads on %s", runtime.GOOS) } - exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof") - if err != nil { - t.Fatal(err) - } - - got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() - if err != nil { - t.Fatalf("exit status: %v\n%s", err, got) - } - - want := []byte("OK\n") - if !bytes.Equal(got, want) { + got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal") + if want := "OK\n"; got != want { t.Fatalf("expected %q, but got:\n%s", want, got) } } @@ -238,11 +216,25 @@ func TestCgoCCodeSIGPROF(t *testing.T) { } } +func TestCgoPprofCallback(t *testing.T) { + t.Parallel() + switch runtime.GOOS { + case "windows", "plan9": + t.Skipf("skipping cgo pprof callback test on %s", runtime.GOOS) + } + got := runTestProg(t, "testprogcgo", "CgoPprofCallback") + want := "OK\n" + if got != want { + t.Errorf("expected %q got %v", want, got) + } +} + func TestCgoCrashTraceback(t *testing.T) { t.Parallel() switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { case "darwin/amd64": case "linux/amd64": + case "linux/arm64": case "linux/ppc64le": default: t.Skipf("not yet supported on %s", platform) @@ -260,6 +252,7 @@ func TestCgoCrashTracebackGo(t *testing.T) { switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { case "darwin/amd64": case "linux/amd64": + case "linux/arm64": case "linux/ppc64le": default: t.Skipf("not yet supported on %s", platform) @@ -293,7 +286,7 @@ func TestCgoTracebackContextPreemption(t *testing.T) { func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { t.Parallel() - if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { + if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le" && runtime.GOARCH != "arm64") { t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) } testenv.MustHaveGoRun(t) @@ -303,12 +296,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { t.Fatal(err) } - // pprofCgoTraceback is called whenever CGO code is executing and a signal - // is received. Disable signal preemption to increase the likelihood at - // least one SIGPROF signal fired to capture a sample. See issue #37201. cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg)) - cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1") - got, err := cmd.CombinedOutput() if err != nil { if testenv.Builder() == "linux-amd64-alpine" { @@ -321,7 +309,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { defer os.Remove(fn) for try := 0; try < 2; try++ { - cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces")) + cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-tagignore=ignore", "-traces")) // Check that pprof works both with and without explicit executable on command line. if try == 0 { cmd.Args = append(cmd.Args, exe, fn) @@ -526,13 +514,38 @@ func TestCgoTracebackSigpanic(t *testing.T) { } t.Parallel() got := runTestProg(t, "testprogcgo", "TracebackSigpanic") + t.Log(got) want := "runtime.sigpanic" if !strings.Contains(got, want) { - t.Fatalf("want failure containing %q. output:\n%s\n", want, got) + t.Errorf("did not see %q in output", want) + } + // No runtime errors like "runtime: unexpected return pc". + nowant := "runtime: " + if strings.Contains(got, nowant) { + t.Errorf("unexpectedly saw %q in output", nowant) + } +} + +func TestCgoPanicCallback(t *testing.T) { + t.Parallel() + got := runTestProg(t, "testprogcgo", "PanicCallback") + t.Log(got) + want := "panic: runtime error: invalid memory address or nil pointer dereference" + if !strings.Contains(got, want) { + t.Errorf("did not see %q in output", want) + } + want = "panic_callback" + if !strings.Contains(got, want) { + t.Errorf("did not see %q in output", want) + } + want = "PanicCallback" + if !strings.Contains(got, want) { + t.Errorf("did not see %q in output", want) } - nowant := "unexpected return pc" + // No runtime errors like "runtime: unexpected return pc". + nowant := "runtime: " if strings.Contains(got, nowant) { - t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got) + t.Errorf("did not see %q in output", want) } } @@ -591,17 +604,65 @@ func TestSegv(t *testing.T) { } for _, test := range []string{"Segv", "SegvInCgo"} { + test := test t.Run(test, func(t *testing.T) { t.Parallel() got := runTestProg(t, "testprogcgo", test) t.Log(got) - if !strings.Contains(got, "SIGSEGV") { - t.Errorf("expected crash from signal") + want := "SIGSEGV" + if !strings.Contains(got, want) { + if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" && strings.Contains(got, "fatal: morestack on g0") { + testenv.SkipFlaky(t, 39457) + } + t.Errorf("did not see %q in output", want) + } + + // No runtime errors like "runtime: unknown pc". + switch runtime.GOOS { + case "darwin", "illumos", "solaris": + // Runtime sometimes throws when generating the traceback. + testenv.SkipFlaky(t, 49182) + case "linux": + if runtime.GOARCH == "386" { + // Runtime throws when generating a traceback from + // a VDSO call via asmcgocall. + testenv.SkipFlaky(t, 50504) + } + } + if test == "SegvInCgo" && strings.Contains(got, "unknown pc") { + testenv.SkipFlaky(t, 50979) + } + + nowant := "runtime: " + if strings.Contains(got, nowant) { + t.Errorf("unexpectedly saw %q in output", nowant) } }) } } +func TestAbortInCgo(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + // N.B. On Windows, C abort() causes the program to exit + // without going through the runtime at all. + t.Skipf("no signals on %s", runtime.GOOS) + } + + t.Parallel() + got := runTestProg(t, "testprogcgo", "Abort") + t.Log(got) + want := "SIGABRT" + if !strings.Contains(got, want) { + t.Errorf("did not see %q in output", want) + } + // No runtime errors like "runtime: unknown pc". + nowant := "runtime: " + if strings.Contains(got, nowant) { + t.Errorf("did not see %q in output", want) + } +} + // TestEINTR tests that we handle EINTR correctly. // See issue #20400 and friends. func TestEINTR(t *testing.T) { @@ -641,3 +702,11 @@ func TestNeedmDeadlock(t *testing.T) { t.Fatalf("want %s, got %s\n", want, output) } } + +func TestCgoTracebackGoroutineProfile(t *testing.T) { + output := runTestProg(t, "testprogcgo", "GoroutineProfile") + want := "OK\n" + if output != want { + t.Fatalf("want %s, got %s\n", want, output) + } +} diff --git a/src/runtime/crash_nonunix_test.go b/src/runtime/crash_nonunix_test.go deleted file mode 100644 index 5f61476f217069..00000000000000 --- a/src/runtime/crash_nonunix_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build windows || plan9 || (js && wasm) -// +build windows plan9 js,wasm - -package runtime_test - -import "os" - -// sigquit is the signal to send to kill a hanging testdata program. -// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill. -var sigquit = os.Kill diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index e0c0bac8926adc..d5f755296bad09 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -6,6 +6,7 @@ package runtime_test import ( "bytes" + "errors" "flag" "fmt" "internal/testenv" @@ -14,11 +15,9 @@ import ( "path/filepath" "regexp" "runtime" - "strconv" "strings" "sync" "testing" - "time" ) var toRemove []string @@ -34,12 +33,13 @@ func TestMain(m *testing.M) { var testprog struct { sync.Mutex dir string - target map[string]buildexe + target map[string]*buildexe } type buildexe struct { - exe string - err error + once sync.Once + exe string + err error } func runTestProg(t *testing.T, binary, name string, env ...string) string { @@ -69,52 +69,19 @@ func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string { if testing.Short() { cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1") } - var b bytes.Buffer - cmd.Stdout = &b - cmd.Stderr = &b - if err := cmd.Start(); err != nil { - t.Fatalf("starting %s %s: %v", exe, name, err) - } - - // If the process doesn't complete within 1 minute, - // assume it is hanging and kill it to get a stack trace. - p := cmd.Process - done := make(chan bool) - go func() { - scale := 1 - // This GOARCH/GOOS test is copied from cmd/dist/test.go. - // TODO(iant): Have cmd/dist update the environment variable. - if runtime.GOARCH == "arm" || runtime.GOOS == "windows" { - scale = 2 - } - if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { - if sc, err := strconv.Atoi(s); err == nil { - scale = sc - } - } - - select { - case <-done: - case <-time.After(time.Duration(scale) * time.Minute): - p.Signal(sigquit) - } - }() - - if err := cmd.Wait(); err != nil { - t.Logf("%s %s exit status: %v", exe, name, err) - } - close(done) - - return b.String() + out, _ := testenv.RunWithTimeout(t, cmd) + return string(out) } +var serializeBuild = make(chan bool, 2) + func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) { if *flagQuick { t.Skip("-quick") } + testenv.MustHaveGoBuild(t) testprog.Lock() - defer testprog.Unlock() if testprog.dir == "" { dir, err := os.MkdirTemp("", "go-build") if err != nil { @@ -125,29 +92,48 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) } if testprog.target == nil { - testprog.target = make(map[string]buildexe) + testprog.target = make(map[string]*buildexe) } name := binary if len(flags) > 0 { name += "_" + strings.Join(flags, "_") } target, ok := testprog.target[name] - if ok { - return target.exe, target.err - } - - exe := filepath.Join(testprog.dir, name+".exe") - cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...) - cmd.Dir = "testdata/" + binary - out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() - if err != nil { - target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) + if !ok { + target = &buildexe{} testprog.target[name] = target - return "", target.err } - target.exe = exe - testprog.target[name] = target - return exe, nil + + dir := testprog.dir + + // Unlock testprog while actually building, so that other + // tests can look up executables that were already built. + testprog.Unlock() + + target.once.Do(func() { + // Only do two "go build"'s at a time, + // to keep load from getting too high. + serializeBuild <- true + defer func() { <-serializeBuild }() + + // Don't get confused if testenv.GoToolPath calls t.Skip. + target.err = errors.New("building test called t.Skip") + + exe := filepath.Join(dir, name+".exe") + + t.Logf("running go build -o %s %s", exe, strings.Join(flags, " ")) + cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...) + cmd.Dir = "testdata/" + binary + out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() + if err != nil { + target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) + } else { + target.exe = exe + target.err = nil + } + }) + + return target.exe, target.err } func TestVDSO(t *testing.T) { @@ -417,7 +403,7 @@ func TestRuntimePanicWithRuntimeError(t *testing.T) { } } -func panicValue(fn func()) (recovered interface{}) { +func panicValue(fn func()) (recovered any) { defer func() { recovered = recover() }() @@ -679,7 +665,7 @@ retry: func TestBadTraceback(t *testing.T) { output := runTestProg(t, "testprog", "BadTraceback") for _, want := range []string{ - "runtime: unexpected return pc", + "unexpected return pc", "called from 0xbad", "00000bad", // Smashed LR in hex dump "= 2 && os.Args[1] == "testPanicSystemstackInternal" { + // Complete any in-flight GCs and disable future ones. We're going to + // block goroutines on runtime locks, which aren't ever preemptible for the + // GC to scan them. + runtime.GC() + debug.SetGCPercent(-1) // Get two threads running on the system stack with // something recognizable in the stack trace. runtime.GOMAXPROCS(2) @@ -245,9 +245,7 @@ func TestSignalExitStatus(t *testing.T) { func TestSignalIgnoreSIGTRAP(t *testing.T) { if runtime.GOOS == "openbsd" { - if bn := testenv.Builder(); strings.HasSuffix(bn, "-62") || strings.HasSuffix(bn, "-64") { - testenv.SkipFlaky(t, 17496) - } + testenv.SkipFlaky(t, 49725) } output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP") diff --git a/src/runtime/debug.go b/src/runtime/debug.go index 82deefa200cdf7..669c36f0d584bd 100644 --- a/src/runtime/debug.go +++ b/src/runtime/debug.go @@ -61,3 +61,55 @@ func NumGoroutine() int { func debug_modinfo() string { return modinfo } + +// mayMoreStackPreempt is a maymorestack hook that forces a preemption +// at every possible cooperative preemption point. +// +// This is valuable to apply to the runtime, which can be sensitive to +// preemption points. To apply this to all preemption points in the +// runtime and runtime-like code, use the following in bash or zsh: +// +// X=(-{gc,asm}flags={runtime/...,reflect,sync}=-d=maymorestack=runtime.mayMoreStackPreempt) GOFLAGS=${X[@]} +// +// This must be deeply nosplit because it is called from a function +// prologue before the stack is set up and because the compiler will +// call it from any splittable prologue (leading to infinite +// recursion). +// +// Ideally it should also use very little stack because the linker +// doesn't currently account for this in nosplit stack depth checking. +// +// Ensure mayMoreStackPreempt can be called for all ABIs. +// +//go:nosplit +//go:linkname mayMoreStackPreempt +func mayMoreStackPreempt() { + // Don't do anything on the g0 or gsignal stack. + gp := getg() + if gp == gp.m.g0 || gp == gp.m.gsignal { + return + } + // Force a preemption, unless the stack is already poisoned. + if gp.stackguard0 < stackPoisonMin { + gp.stackguard0 = stackPreempt + } +} + +// mayMoreStackMove is a maymorestack hook that forces stack movement +// at every possible point. +// +// See mayMoreStackPreempt. +// +//go:nosplit +//go:linkname mayMoreStackMove +func mayMoreStackMove() { + // Don't do anything on the g0 or gsignal stack. + gp := getg() + if gp == gp.m.g0 || gp == gp.m.gsignal { + return + } + // Force stack movement, unless the stack is already poisoned. + if gp.stackguard0 < stackPoisonMin { + gp.stackguard0 = stackForceMove + } +} diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go index 00f92c3ddfb69c..0f53928fe8530f 100644 --- a/src/runtime/debug/garbage.go +++ b/src/runtime/debug/garbage.go @@ -87,7 +87,11 @@ func ReadGCStats(stats *GCStats) { // SetGCPercent returns the previous setting. // The initial setting is the value of the GOGC environment variable // at startup, or 100 if the variable is not set. -// A negative percentage disables garbage collection. +// This setting may be effectively reduced in order to maintain a memory +// limit. +// A negative percentage effectively disables garbage collection, unless +// the memory limit is reached. +// See SetMemoryLimit for more details. func SetGCPercent(percent int) int { return int(setGCPercent(int32(percent))) } @@ -142,7 +146,9 @@ func SetMaxThreads(threads int) int { // dramatic situations; SetPanicOnFault allows such programs to request // that the runtime trigger only a panic, not a crash. // The runtime.Error that the runtime panics with may have an additional method: -// Addr() uintptr +// +// Addr() uintptr +// // If that method exists, it returns the memory address which triggered the fault. // The results of Addr are best-effort and the veracity of the result // may depend on the platform. @@ -173,3 +179,60 @@ func WriteHeapDump(fd uintptr) // If SetTraceback is called with a level lower than that of the // environment variable, the call is ignored. func SetTraceback(level string) + +// SetMemoryLimit provides the runtime with a soft memory limit. +// +// The runtime undertakes several processes to try to respect this +// memory limit, including adjustments to the frequency of garbage +// collections and returning memory to the underlying system more +// aggressively. This limit will be respected even if GOGC=off (or, +// if SetGCPercent(-1) is executed). +// +// The input limit is provided as bytes, and includes all memory +// mapped, managed, and not released by the Go runtime. Notably, it +// does not account for space used by the Go binary and memory +// external to Go, such as memory managed by the underlying system +// on behalf of the process, or memory managed by non-Go code inside +// the same process. Examples of excluded memory sources include: OS +// kernel memory held on behalf of the process, memory allocated by +// C code, and memory mapped by syscall.Mmap (because it is not +// managed by the Go runtime). +// +// More specifically, the following expression accurately reflects +// the value the runtime attempts to maintain as the limit: +// +// runtime.MemStats.Sys - runtime.MemStats.HeapReleased +// +// or in terms of the runtime/metrics package: +// +// /memory/classes/total:bytes - /memory/classes/heap/released:bytes +// +// A zero limit or a limit that's lower than the amount of memory +// used by the Go runtime may cause the garbage collector to run +// nearly continuously. However, the application may still make +// progress. +// +// The memory limit is always respected by the Go runtime, so to +// effectively disable this behavior, set the limit very high. +// math.MaxInt64 is the canonical value for disabling the limit, +// but values much greater than the available memory on the underlying +// system work just as well. +// +// See https://go.dev/doc/gc-guide for a detailed guide explaining +// the soft memory limit in more detail, as well as a variety of common +// use-cases and scenarios. +// +// The initial setting is math.MaxInt64 unless the GOMEMLIMIT +// environment variable is set, in which case it provides the initial +// setting. GOMEMLIMIT is a numeric value in bytes with an optional +// unit suffix. The supported suffixes include B, KiB, MiB, GiB, and +// TiB. These suffixes represent quantities of bytes as defined by +// the IEC 80000-13 standard. That is, they are based on powers of +// two: KiB means 2^10 bytes, MiB means 2^20 bytes, and so on. +// +// SetMemoryLimit returns the previously set memory limit. +// A negative input does not adjust the limit, and allows for +// retrieval of the currently set memory limit. +func SetMemoryLimit(limit int64) int64 { + return setMemoryLimit(limit) +} diff --git a/src/runtime/debug/garbage_test.go b/src/runtime/debug/garbage_test.go index 69e769ecf29d38..7213bbe641dd6e 100644 --- a/src/runtime/debug/garbage_test.go +++ b/src/runtime/debug/garbage_test.go @@ -6,6 +6,7 @@ package debug_test import ( "internal/testenv" + "os" "runtime" . "runtime/debug" "testing" @@ -87,27 +88,71 @@ func TestReadGCStats(t *testing.T) { } } -var big = make([]byte, 1<<20) +var big []byte func TestFreeOSMemory(t *testing.T) { - var ms1, ms2 runtime.MemStats + // Tests FreeOSMemory by making big susceptible to collection + // and checking that at least that much memory is returned to + // the OS after. - if big == nil { - t.Skip("test is not reliable when run multiple times") - } - big = nil + const bigBytes = 32 << 20 + big = make([]byte, bigBytes) + + // Make sure any in-progress GCs are complete. runtime.GC() - runtime.ReadMemStats(&ms1) + + var before runtime.MemStats + runtime.ReadMemStats(&before) + + // Clear the last reference to the big allocation, making it + // susceptible to collection. + big = nil + + // FreeOSMemory runs a GC cycle before releasing memory, + // so it's fine to skip a GC here. + // + // It's possible the background scavenger runs concurrently + // with this function and does most of the work for it. + // If that happens, it's OK. What we want is a test that fails + // often if FreeOSMemory does not work correctly, and a test + // that passes every time if it does. FreeOSMemory() - runtime.ReadMemStats(&ms2) - if ms1.HeapReleased >= ms2.HeapReleased { - t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased) + + var after runtime.MemStats + runtime.ReadMemStats(&after) + + // Check to make sure that the big allocation (now freed) + // had its memory shift into HeapReleased as a result of that + // FreeOSMemory. + if after.HeapReleased <= before.HeapReleased { + t.Fatalf("no memory released: %d -> %d", before.HeapReleased, after.HeapReleased) + } + + // Check to make sure bigBytes was released, plus some slack. Pages may get + // allocated in between the two measurements above for a variety for reasons, + // most commonly for GC work bufs. Since this can get fairly high, depending + // on scheduling and what GOMAXPROCS is, give a lot of slack up-front. + // + // Add a little more slack too if the page size is bigger than the runtime page size. + // "big" could end up unaligned on its ends, forcing the scavenger to skip at worst + // 2x pages. + slack := uint64(bigBytes / 2) + pageSize := uint64(os.Getpagesize()) + if pageSize > 8<<10 { + slack += pageSize * 2 + } + if slack > bigBytes { + // We basically already checked this. + return + } + if after.HeapReleased-before.HeapReleased < bigBytes-slack { + t.Fatalf("less than %d released: %d -> %d", bigBytes, before.HeapReleased, after.HeapReleased) } } var ( - setGCPercentBallast interface{} - setGCPercentSink interface{} + setGCPercentBallast any + setGCPercentSink any ) func TestSetGCPercent(t *testing.T) { diff --git a/src/runtime/debug/heapdump_test.go b/src/runtime/debug/heapdump_test.go index 768934d05db38a..ee6b054b117afd 100644 --- a/src/runtime/debug/heapdump_test.go +++ b/src/runtime/debug/heapdump_test.go @@ -67,3 +67,29 @@ func TestWriteHeapDumpFinalizers(t *testing.T) { WriteHeapDump(f.Fd()) println("done dump") } + +type G[T any] struct{} +type I interface { + M() +} + +//go:noinline +func (g G[T]) M() {} + +var dummy I = G[int]{} +var dummy2 I = G[G[int]]{} + +func TestWriteHeapDumpTypeName(t *testing.T) { + if runtime.GOOS == "js" { + t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS) + } + f, err := os.CreateTemp("", "heapdumptest") + if err != nil { + t.Fatalf("TempFile failed: %v", err) + } + defer os.Remove(f.Name()) + defer f.Close() + WriteHeapDump(f.Fd()) + dummy.M() + dummy2.M() +} diff --git a/src/runtime/debug/mod.go b/src/runtime/debug/mod.go index 0381bdcc53951c..688e2581ed89e6 100644 --- a/src/runtime/debug/mod.go +++ b/src/runtime/debug/mod.go @@ -5,6 +5,9 @@ package debug import ( + "fmt" + "runtime" + "strconv" "strings" ) @@ -15,15 +18,32 @@ func modinfo() string // in the running binary. The information is available only // in binaries built with module support. func ReadBuildInfo() (info *BuildInfo, ok bool) { - return readBuildInfo(modinfo()) + data := modinfo() + if len(data) < 32 { + return nil, false + } + data = data[16 : len(data)-16] + bi, err := ParseBuildInfo(data) + if err != nil { + return nil, false + } + + // The go version is stored separately from other build info, mostly for + // historical reasons. It is not part of the modinfo() string, and + // ParseBuildInfo does not recognize it. We inject it here to hide this + // awkwardness from the user. + bi.GoVersion = runtime.Version() + + return bi, true } -// BuildInfo represents the build information read from -// the running binary. +// BuildInfo represents the build information read from a Go binary. type BuildInfo struct { - Path string // The main package path - Main Module // The module containing the main package - Deps []*Module // Module dependencies + GoVersion string // Version of Go that produced this binary. + Path string // The main package path + Main Module // The module containing the main package + Deps []*Module // Module dependencies + Settings []BuildSetting // Other information about the build. } // Module represents a module. @@ -34,81 +54,206 @@ type Module struct { Replace *Module // replaced by this module } -func readBuildInfo(data string) (*BuildInfo, bool) { - if len(data) < 32 { - return nil, false +// BuildSetting describes a setting that may be used to understand how the +// binary was built. For example, VCS commit and dirty status is stored here. +type BuildSetting struct { + // Key and Value describe the build setting. + // Key must not contain an equals sign, space, tab, or newline. + // Value must not contain newlines ('\n'). + Key, Value string +} + +// quoteKey reports whether key is required to be quoted. +func quoteKey(key string) bool { + return len(key) == 0 || strings.ContainsAny(key, "= \t\r\n\"`") +} + +// quoteValue reports whether value is required to be quoted. +func quoteValue(value string) bool { + return strings.ContainsAny(value, " \t\r\n\"`") +} + +func (bi *BuildInfo) String() string { + buf := new(strings.Builder) + if bi.GoVersion != "" { + fmt.Fprintf(buf, "go\t%s\n", bi.GoVersion) + } + if bi.Path != "" { + fmt.Fprintf(buf, "path\t%s\n", bi.Path) + } + var formatMod func(string, Module) + formatMod = func(word string, m Module) { + buf.WriteString(word) + buf.WriteByte('\t') + buf.WriteString(m.Path) + buf.WriteByte('\t') + buf.WriteString(m.Version) + if m.Replace == nil { + buf.WriteByte('\t') + buf.WriteString(m.Sum) + } else { + buf.WriteByte('\n') + formatMod("=>", *m.Replace) + } + buf.WriteByte('\n') + } + if bi.Main != (Module{}) { + formatMod("mod", bi.Main) + } + for _, dep := range bi.Deps { + formatMod("dep", *dep) + } + for _, s := range bi.Settings { + key := s.Key + if quoteKey(key) { + key = strconv.Quote(key) + } + value := s.Value + if quoteValue(value) { + value = strconv.Quote(value) + } + fmt.Fprintf(buf, "build\t%s=%s\n", key, value) } - data = data[16 : len(data)-16] - const ( - pathLine = "path\t" - modLine = "mod\t" - depLine = "dep\t" - repLine = "=>\t" + return buf.String() +} + +func ParseBuildInfo(data string) (bi *BuildInfo, err error) { + lineNum := 1 + defer func() { + if err != nil { + err = fmt.Errorf("could not parse Go build info: line %d: %w", lineNum, err) + } + }() + + var ( + pathLine = "path\t" + modLine = "mod\t" + depLine = "dep\t" + repLine = "=>\t" + buildLine = "build\t" + newline = "\n" + tab = "\t" ) - readEntryFirstLine := func(elem []string) (Module, bool) { + readModuleLine := func(elem []string) (Module, error) { if len(elem) != 2 && len(elem) != 3 { - return Module{}, false + return Module{}, fmt.Errorf("expected 2 or 3 columns; got %d", len(elem)) } + version := elem[1] sum := "" if len(elem) == 3 { sum = elem[2] } return Module{ Path: elem[0], - Version: elem[1], + Version: version, Sum: sum, - }, true + }, nil } + bi = new(BuildInfo) var ( - info = &BuildInfo{} last *Module line string ok bool ) - // Reverse of cmd/go/internal/modload.PackageBuildInfo + // Reverse of BuildInfo.String(), except for go version. for len(data) > 0 { - i := strings.IndexByte(data, '\n') - if i < 0 { + line, data, ok = strings.Cut(data, newline) + if !ok { break } - line, data = data[:i], data[i+1:] switch { case strings.HasPrefix(line, pathLine): elem := line[len(pathLine):] - info.Path = elem + bi.Path = string(elem) case strings.HasPrefix(line, modLine): - elem := strings.Split(line[len(modLine):], "\t") - last = &info.Main - *last, ok = readEntryFirstLine(elem) - if !ok { - return nil, false + elem := strings.Split(line[len(modLine):], tab) + last = &bi.Main + *last, err = readModuleLine(elem) + if err != nil { + return nil, err } case strings.HasPrefix(line, depLine): - elem := strings.Split(line[len(depLine):], "\t") + elem := strings.Split(line[len(depLine):], tab) last = new(Module) - info.Deps = append(info.Deps, last) - *last, ok = readEntryFirstLine(elem) - if !ok { - return nil, false + bi.Deps = append(bi.Deps, last) + *last, err = readModuleLine(elem) + if err != nil { + return nil, err } case strings.HasPrefix(line, repLine): - elem := strings.Split(line[len(repLine):], "\t") + elem := strings.Split(line[len(repLine):], tab) if len(elem) != 3 { - return nil, false + return nil, fmt.Errorf("expected 3 columns for replacement; got %d", len(elem)) } if last == nil { - return nil, false + return nil, fmt.Errorf("replacement with no module on previous line") } last.Replace = &Module{ - Path: elem[0], - Version: elem[1], - Sum: elem[2], + Path: string(elem[0]), + Version: string(elem[1]), + Sum: string(elem[2]), } last = nil + case strings.HasPrefix(line, buildLine): + kv := line[len(buildLine):] + if len(kv) < 1 { + return nil, fmt.Errorf("build line missing '='") + } + + var key, rawValue string + switch kv[0] { + case '=': + return nil, fmt.Errorf("build line with missing key") + + case '`', '"': + rawKey, err := strconv.QuotedPrefix(kv) + if err != nil { + return nil, fmt.Errorf("invalid quoted key in build line") + } + if len(kv) == len(rawKey) { + return nil, fmt.Errorf("build line missing '=' after quoted key") + } + if c := kv[len(rawKey)]; c != '=' { + return nil, fmt.Errorf("unexpected character after quoted key: %q", c) + } + key, _ = strconv.Unquote(rawKey) + rawValue = kv[len(rawKey)+1:] + + default: + var ok bool + key, rawValue, ok = strings.Cut(kv, "=") + if !ok { + return nil, fmt.Errorf("build line missing '=' after key") + } + if quoteKey(key) { + return nil, fmt.Errorf("unquoted key %q must be quoted", key) + } + } + + var value string + if len(rawValue) > 0 { + switch rawValue[0] { + case '`', '"': + var err error + value, err = strconv.Unquote(rawValue) + if err != nil { + return nil, fmt.Errorf("invalid quoted value in build line") + } + + default: + value = rawValue + if quoteValue(value) { + return nil, fmt.Errorf("unquoted value %q must be quoted", value) + } + } + } + + bi.Settings = append(bi.Settings, BuildSetting{Key: key, Value: value}) } + lineNum++ } - return info, true + return bi, nil } diff --git a/src/runtime/debug/mod_test.go b/src/runtime/debug/mod_test.go new file mode 100644 index 00000000000000..b2917692f4459d --- /dev/null +++ b/src/runtime/debug/mod_test.go @@ -0,0 +1,75 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package debug_test + +import ( + "reflect" + "runtime/debug" + "strings" + "testing" +) + +// strip removes two leading tabs after each newline of s. +func strip(s string) string { + replaced := strings.ReplaceAll(s, "\n\t\t", "\n") + if len(replaced) > 0 && replaced[0] == '\n' { + replaced = replaced[1:] + } + return replaced +} + +func FuzzParseBuildInfoRoundTrip(f *testing.F) { + // Package built from outside a module, missing some fields.. + f.Add(strip(` + path rsc.io/fortune + mod rsc.io/fortune v1.0.0 + `)) + + // Package built from the standard library, missing some fields.. + f.Add(`path cmd/test2json`) + + // Package built from inside a module. + f.Add(strip(` + go 1.18 + path example.com/m + mod example.com/m (devel) + build -compiler=gc + `)) + + // Package built in GOPATH mode. + f.Add(strip(` + go 1.18 + path example.com/m + build -compiler=gc + `)) + + // Escaped build info. + f.Add(strip(` + go 1.18 + path example.com/m + build CRAZY_ENV="requires\nescaping" + `)) + + f.Fuzz(func(t *testing.T, s string) { + bi, err := debug.ParseBuildInfo(s) + if err != nil { + // Not a round-trippable BuildInfo string. + t.Log(err) + return + } + + // s2 could have different escaping from s. + // However, it should parse to exactly the same contents. + s2 := bi.String() + bi2, err := debug.ParseBuildInfo(s2) + if err != nil { + t.Fatalf("%v:\n%s", err, s2) + } + + if !reflect.DeepEqual(bi2, bi) { + t.Fatalf("Parsed representation differs.\ninput:\n%s\noutput:\n%s", s, s2) + } + }) +} diff --git a/src/runtime/debug/panic_test.go b/src/runtime/debug/panic_test.go index 65f9555f3761c5..ec5294ce4ce766 100644 --- a/src/runtime/debug/panic_test.go +++ b/src/runtime/debug/panic_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build aix darwin dragonfly freebsd linux netbsd openbsd // TODO: test on Windows? diff --git a/src/runtime/debug/stack_test.go b/src/runtime/debug/stack_test.go index 9376e82b845613..671057c3a0d1b6 100644 --- a/src/runtime/debug/stack_test.go +++ b/src/runtime/debug/stack_test.go @@ -5,11 +5,26 @@ package debug_test import ( + "bytes" + "fmt" + "internal/testenv" + "os" + "os/exec" + "path/filepath" + "runtime" . "runtime/debug" "strings" "testing" ) +func TestMain(m *testing.M) { + if os.Getenv("GO_RUNTIME_DEBUG_TEST_DUMP_GOROOT") != "" { + fmt.Println(runtime.GOROOT()) + os.Exit(0) + } + os.Exit(m.Run()) +} + type T int func (t *T) ptrmethod() []byte { @@ -20,22 +35,22 @@ func (t T) method() []byte { } /* - The traceback should look something like this, modulo line numbers and hex constants. - Don't worry much about the base levels, but check the ones in our own package. +The traceback should look something like this, modulo line numbers and hex constants. +Don't worry much about the base levels, but check the ones in our own package. - goroutine 10 [running]: - runtime/debug.Stack(0x0, 0x0, 0x0) - /Users/r/go/src/runtime/debug/stack.go:28 +0x80 - runtime/debug.(*T).ptrmethod(0xc82005ee70, 0x0, 0x0, 0x0) - /Users/r/go/src/runtime/debug/stack_test.go:15 +0x29 - runtime/debug.T.method(0x0, 0x0, 0x0, 0x0) - /Users/r/go/src/runtime/debug/stack_test.go:18 +0x32 - runtime/debug.TestStack(0xc8201ce000) - /Users/r/go/src/runtime/debug/stack_test.go:37 +0x38 - testing.tRunner(0xc8201ce000, 0x664b58) - /Users/r/go/src/testing/testing.go:456 +0x98 - created by testing.RunTests - /Users/r/go/src/testing/testing.go:561 +0x86d + goroutine 10 [running]: + runtime/debug.Stack(0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack.go:28 +0x80 + runtime/debug.(*T).ptrmethod(0xc82005ee70, 0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack_test.go:15 +0x29 + runtime/debug.T.method(0x0, 0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack_test.go:18 +0x32 + runtime/debug.TestStack(0xc8201ce000) + /Users/r/go/src/runtime/debug/stack_test.go:37 +0x38 + testing.tRunner(0xc8201ce000, 0x664b58) + /Users/r/go/src/testing/testing.go:456 +0x98 + created by testing.RunTests + /Users/r/go/src/testing/testing.go:561 +0x86d */ func TestStack(t *testing.T) { b := T(0).method() @@ -43,23 +58,64 @@ func TestStack(t *testing.T) { if len(lines) < 6 { t.Fatal("too few lines") } + + // If built with -trimpath, file locations should start with package paths. + // Otherwise, file locations should start with a GOROOT/src prefix + // (for whatever value of GOROOT is baked into the binary, not the one + // that may be set in the environment). + fileGoroot := "" + if envGoroot := os.Getenv("GOROOT"); envGoroot != "" { + // Since GOROOT is set explicitly in the environment, we can't be certain + // that it is the same GOROOT value baked into the binary, and we can't + // change the value in-process because runtime.GOROOT uses the value from + // initial (not current) environment. Spawn a subprocess to determine the + // real baked-in GOROOT. + t.Logf("found GOROOT %q from environment; checking embedded GOROOT value", envGoroot) + testenv.MustHaveExec(t) + exe, err := os.Executable() + if err != nil { + t.Fatal(err) + } + cmd := exec.Command(exe) + cmd.Env = append(os.Environ(), "GOROOT=", "GO_RUNTIME_DEBUG_TEST_DUMP_GOROOT=1") + out, err := cmd.Output() + if err != nil { + t.Fatal(err) + } + fileGoroot = string(bytes.TrimSpace(out)) + } else { + // Since GOROOT is not set in the environment, its value (if any) must come + // from the path embedded in the binary. + fileGoroot = runtime.GOROOT() + } + filePrefix := "" + if fileGoroot != "" { + filePrefix = filepath.ToSlash(fileGoroot) + "/src/" + } + n := 0 - frame := func(line, code string) { - check(t, lines[n], code) + frame := func(file, code string) { + t.Helper() + + line := lines[n] + if !strings.Contains(line, code) { + t.Errorf("expected %q in %q", code, line) + } n++ - check(t, lines[n], line) + + line = lines[n] + + wantPrefix := "\t" + filePrefix + file + if !strings.HasPrefix(line, wantPrefix) { + t.Errorf("in line %q, expected prefix %q", line, wantPrefix) + } n++ } n++ - frame("src/runtime/debug/stack.go", "runtime/debug.Stack") - frame("src/runtime/debug/stack_test.go", "runtime/debug_test.(*T).ptrmethod") - frame("src/runtime/debug/stack_test.go", "runtime/debug_test.T.method") - frame("src/runtime/debug/stack_test.go", "runtime/debug_test.TestStack") - frame("src/testing/testing.go", "") -} -func check(t *testing.T, line, has string) { - if !strings.Contains(line, has) { - t.Errorf("expected %q in %q", has, line) - } + frame("runtime/debug/stack.go", "runtime/debug.Stack") + frame("runtime/debug/stack_test.go", "runtime/debug_test.(*T).ptrmethod") + frame("runtime/debug/stack_test.go", "runtime/debug_test.T.method") + frame("runtime/debug/stack_test.go", "runtime/debug_test.TestStack") + frame("testing/testing.go", "") } diff --git a/src/runtime/debug/stubs.go b/src/runtime/debug/stubs.go index 2cba136044bbce..913d4b9b09e56a 100644 --- a/src/runtime/debug/stubs.go +++ b/src/runtime/debug/stubs.go @@ -15,3 +15,4 @@ func setMaxStack(int) int func setGCPercent(int32) int32 func setPanicOnFault(bool) bool func setMaxThreads(int) int +func setMemoryLimit(int64) int64 diff --git a/src/runtime/debug_test.go b/src/runtime/debug_test.go index f74383457f3676..75fe07ec2a2df8 100644 --- a/src/runtime/debug_test.go +++ b/src/runtime/debug_test.go @@ -9,15 +9,13 @@ // spends all of its time in the race runtime, which isn't a safe // point. -//go:build amd64 && linux && !race -// +build amd64,linux,!race +//go:build (amd64 || arm64) && linux && !race package runtime_test import ( "fmt" "internal/abi" - "internal/goexperiment" "math" "os" "regexp" @@ -35,12 +33,22 @@ func startDebugCallWorker(t *testing.T) (g *runtime.G, after func()) { skipUnderDebugger(t) // This can deadlock if there aren't enough threads or if a GC - // tries to interrupt an atomic loop (see issue #10958). We - // use 8 Ps so there's room for the debug call worker, + // tries to interrupt an atomic loop (see issue #10958). Execute + // an extra GC to ensure even the sweep phase is done (out of + // caution to prevent #49370 from happening). + // TODO(mknyszek): This extra GC cycle is likely unnecessary + // because preemption (which may happen during the sweep phase) + // isn't much of an issue anymore thanks to asynchronous preemption. + // The biggest risk is having a write barrier in the debug call + // injection test code fire, because it runs in a signal handler + // and may not have a P. + // + // We use 8 Ps so there's room for the debug call worker, // something that's trying to preempt the call worker, and the // goroutine that's trying to stop the call worker. ogomaxprocs := runtime.GOMAXPROCS(8) ogcpercent := debug.SetGCPercent(-1) + runtime.GC() // ready is a buffered channel so debugCallWorker won't block // on sending to it. This makes it less likely we'll catch @@ -135,7 +143,7 @@ func TestDebugCall(t *testing.T) { intRegs := regs.Ints[:] floatRegs := regs.Floats[:] fval := float64(42.0) - if goexperiment.RegabiArgs { + if len(intRegs) > 0 { intRegs[0] = 42 floatRegs[0] = math.Float64bits(fval) } else { @@ -144,12 +152,13 @@ func TestDebugCall(t *testing.T) { x1: 42.0, } } + if _, err := runtime.InjectDebugCall(g, fn, ®s, args, debugCallTKill, false); err != nil { t.Fatal(err) } var result0 int var result1 float64 - if goexperiment.RegabiArgs { + if len(intRegs) > 0 { result0 = int(intRegs[0]) result1 = math.Float64frombits(floatRegs[0]) } else { @@ -234,6 +243,12 @@ func TestDebugCallUnsafePoint(t *testing.T) { // This can deadlock if there aren't enough threads or if a GC // tries to interrupt an atomic loop (see issue #10958). defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) + + // InjectDebugCall cannot be executed while a GC is actively in + // progress. Wait until the current GC is done, and turn it off. + // + // See #49370. + runtime.GC() defer debug.SetGCPercent(debug.SetGCPercent(-1)) // Test that the runtime refuses call injection at unsafe points. @@ -257,6 +272,19 @@ func TestDebugCallPanic(t *testing.T) { // This can deadlock if there aren't enough threads. defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) + // InjectDebugCall cannot be executed while a GC is actively in + // progress. Wait until the current GC is done, and turn it off. + // + // See #10958 and #49370. + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // TODO(mknyszek): This extra GC cycle is likely unnecessary + // because preemption (which may happen during the sweep phase) + // isn't much of an issue anymore thanks to asynchronous preemption. + // The biggest risk is having a write barrier in the debug call + // injection test code fire, because it runs in a signal handler + // and may not have a P. + runtime.GC() + ready := make(chan *runtime.G) var stop uint32 defer atomic.StoreUint32(&stop, 1) diff --git a/src/runtime/debugcall.go b/src/runtime/debugcall.go index faddf59eed6b40..2f164e7fd7ca2f 100644 --- a/src/runtime/debugcall.go +++ b/src/runtime/debugcall.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build amd64 -// +build amd64 +//go:build amd64 || arm64 package runtime @@ -17,7 +16,7 @@ const ( ) func debugCallV2() -func debugCallPanicked(val interface{}) +func debugCallPanicked(val any) // debugCallCheck checks whether it is safe to inject a debugger // function call with return PC pc. If not, it returns a string @@ -78,7 +77,7 @@ func debugCallCheck(pc uintptr) string { } // Check that this isn't an unsafe-point. - if pc != f.entry { + if pc != f.entry() { pc-- } up := pcdatavalue(f, _PCDATA_UnsafePoint, pc, nil) @@ -112,7 +111,7 @@ func debugCallWrap(dispatch uintptr) { // closure and start the goroutine with that closure, but the compiler disallows // implicit closure allocation in the runtime. fn := debugCallWrap1 - newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), nil, 0, gp, callerpc) + newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), gp, callerpc) args := &debugCallWrapArgs{ dispatch: dispatch, callingG: gp, diff --git a/src/runtime/debuglog.go b/src/runtime/debuglog.go index 3ce3273f4def09..83d5a3e9e64f03 100644 --- a/src/runtime/debuglog.go +++ b/src/runtime/debuglog.go @@ -17,6 +17,7 @@ package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -63,7 +64,7 @@ func dlog() *dlogger { allp := (*uintptr)(unsafe.Pointer(&allDloggers)) all := (*dlogger)(unsafe.Pointer(atomic.Loaduintptr(allp))) for l1 := all; l1 != nil; l1 = l1.allLink { - if atomic.Load(&l1.owned) == 0 && atomic.Cas(&l1.owned, 0, 1) { + if l1.owned.Load() == 0 && l1.owned.CompareAndSwap(0, 1) { l = l1 break } @@ -72,12 +73,14 @@ func dlog() *dlogger { // If that failed, allocate a new logger. if l == nil { - l = (*dlogger)(sysAlloc(unsafe.Sizeof(dlogger{}), nil)) + // Use sysAllocOS instead of sysAlloc because we want to interfere + // with the runtime as little as possible, and sysAlloc updates accounting. + l = (*dlogger)(sysAllocOS(unsafe.Sizeof(dlogger{}))) if l == nil { throw("failed to allocate debug log") } l.w.r.data = &l.w.data - l.owned = 1 + l.owned.Store(1) // Prepend to allDloggers list. headp := (*uintptr)(unsafe.Pointer(&allDloggers)) @@ -119,9 +122,8 @@ func dlog() *dlogger { // // To obtain a dlogger, call dlog(). When done with the dlogger, call // end(). -// -//go:notinheap type dlogger struct { + _ sys.NotInHeap w debugLogWriter // allLink is the next dlogger in the allDloggers list. @@ -129,7 +131,7 @@ type dlogger struct { // owned indicates that this dlogger is owned by an M. This is // accessed atomically. - owned uint32 + owned atomic.Uint32 } // allDloggers is a list of all dloggers, linked through @@ -158,7 +160,7 @@ func (l *dlogger) end() { } // Return the logger to the global pool. - atomic.Store(&l.owned, 0) + l.owned.Store(0) } const ( @@ -266,7 +268,7 @@ func (l *dlogger) hex(x uint64) *dlogger { } //go:nosplit -func (l *dlogger) p(x interface{}) *dlogger { +func (l *dlogger) p(x any) *dlogger { if !dlogEnabled { return l } @@ -354,9 +356,8 @@ func (l *dlogger) traceback(x []uintptr) *dlogger { // overwrite old records. Hence, it maintains a reader that consumes // the log as it gets overwritten. That reader state is where an // actual log reader would start. -// -//go:notinheap type debugLogWriter struct { + _ sys.NotInHeap write uint64 data debugLogBuf @@ -374,8 +375,10 @@ type debugLogWriter struct { buf [10]byte } -//go:notinheap -type debugLogBuf [debugLogBytes]byte +type debugLogBuf struct { + _ sys.NotInHeap + b [debugLogBytes]byte +} const ( // debugLogHeaderSize is the number of bytes in the framing @@ -388,7 +391,7 @@ const ( //go:nosplit func (l *debugLogWriter) ensure(n uint64) { - for l.write+n >= l.r.begin+uint64(len(l.data)) { + for l.write+n >= l.r.begin+uint64(len(l.data.b)) { // Consume record at begin. if l.r.skip() == ^uint64(0) { // Wrapped around within a record. @@ -404,8 +407,8 @@ func (l *debugLogWriter) ensure(n uint64) { //go:nosplit func (l *debugLogWriter) writeFrameAt(pos, size uint64) bool { - l.data[pos%uint64(len(l.data))] = uint8(size) - l.data[(pos+1)%uint64(len(l.data))] = uint8(size >> 8) + l.data.b[pos%uint64(len(l.data.b))] = uint8(size) + l.data.b[(pos+1)%uint64(len(l.data.b))] = uint8(size >> 8) return size <= 0xFFFF } @@ -439,7 +442,7 @@ func (l *debugLogWriter) byte(x byte) { l.ensure(1) pos := l.write l.write++ - l.data[pos%uint64(len(l.data))] = x + l.data.b[pos%uint64(len(l.data.b))] = x } //go:nosplit @@ -448,7 +451,7 @@ func (l *debugLogWriter) bytes(x []byte) { pos := l.write l.write += uint64(len(x)) for len(x) > 0 { - n := copy(l.data[pos%uint64(len(l.data)):], x) + n := copy(l.data.b[pos%uint64(len(l.data.b)):], x) pos += uint64(n) x = x[n:] } @@ -511,15 +514,15 @@ func (r *debugLogReader) skip() uint64 { //go:nosplit func (r *debugLogReader) readUint16LEAt(pos uint64) uint16 { - return uint16(r.data[pos%uint64(len(r.data))]) | - uint16(r.data[(pos+1)%uint64(len(r.data))])<<8 + return uint16(r.data.b[pos%uint64(len(r.data.b))]) | + uint16(r.data.b[(pos+1)%uint64(len(r.data.b))])<<8 } //go:nosplit func (r *debugLogReader) readUint64LEAt(pos uint64) uint64 { var b [8]byte for i := range b { - b[i] = r.data[pos%uint64(len(r.data))] + b[i] = r.data.b[pos%uint64(len(r.data.b))] pos++ } return uint64(b[0]) | uint64(b[1])<<8 | @@ -555,7 +558,7 @@ func (r *debugLogReader) peek() (tick uint64) { pos := r.begin + debugLogHeaderSize var u uint64 for i := uint(0); ; i += 7 { - b := r.data[pos%uint64(len(r.data))] + b := r.data.b[pos%uint64(len(r.data.b))] pos++ u |= uint64(b&^0x80) << i if b&0x80 == 0 { @@ -586,7 +589,7 @@ func (r *debugLogReader) header() (end, tick, nano uint64, p int) { func (r *debugLogReader) uvarint() uint64 { var u uint64 for i := uint(0); ; i += 7 { - b := r.data[r.begin%uint64(len(r.data))] + b := r.data.b[r.begin%uint64(len(r.data.b))] r.begin++ u |= uint64(b&^0x80) << i if b&0x80 == 0 { @@ -608,7 +611,7 @@ func (r *debugLogReader) varint() int64 { } func (r *debugLogReader) printVal() bool { - typ := r.data[r.begin%uint64(len(r.data))] + typ := r.data.b[r.begin%uint64(len(r.data.b))] r.begin++ switch typ { @@ -642,7 +645,7 @@ func (r *debugLogReader) printVal() bool { break } for sl > 0 { - b := r.data[r.begin%uint64(len(r.data)):] + b := r.data.b[r.begin%uint64(len(r.data.b)):] if uint64(len(b)) > sl { b = b[:sl] } @@ -654,11 +657,7 @@ func (r *debugLogReader) printVal() bool { case debugLogConstString: len, ptr := int(r.uvarint()), uintptr(r.uvarint()) ptr += firstmoduledata.etext - str := stringStruct{ - str: unsafe.Pointer(ptr), - len: len, - } - s := *(*string)(unsafe.Pointer(&str)) + s := unsafe.String((*byte)(unsafe.Pointer(ptr)), len) print(s) case debugLogStringOverflow: @@ -714,7 +713,9 @@ func printDebugLog() { lost uint64 nextTick uint64 } - state1 := sysAlloc(unsafe.Sizeof(readState{})*uintptr(n), nil) + // Use sysAllocOS instead of sysAlloc because we want to interfere + // with the runtime as little as possible, and sysAlloc updates accounting. + state1 := sysAllocOS(unsafe.Sizeof(readState{}) * uintptr(n)) if state1 == nil { println("failed to allocate read state for", n, "logs") printunlock() @@ -773,7 +774,8 @@ func printDebugLog() { // Logged before runtimeInitTime was set. pnano = 0 } - print(string(itoaDiv(tmpbuf[:], uint64(pnano), 9))) + pnanoBytes := itoaDiv(tmpbuf[:], uint64(pnano), 9) + print(slicebytetostringtmp((*byte)(noescape(unsafe.Pointer(&pnanoBytes[0]))), len(pnanoBytes))) print(" P ", p, "] ") for i := 0; s.begin < s.end; i++ { @@ -802,7 +804,7 @@ func printDebugLog() { // pc is a return PC that must first be converted to a call PC. func printDebugLogPC(pc uintptr, returnPC bool) { fn := findfunc(pc) - if returnPC && (!fn.valid() || pc > fn.entry) { + if returnPC && (!fn.valid() || pc > fn.entry()) { // TODO(austin): Don't back up if the previous frame // was a sigpanic. pc-- @@ -814,7 +816,7 @@ func printDebugLogPC(pc uintptr, returnPC bool) { } else { name := funcname(fn) file, line := funcline(fn, pc) - print(" [", name, "+", hex(pc-fn.entry), + print(" [", name, "+", hex(pc-fn.entry()), " ", file, ":", line, "]") } } diff --git a/src/runtime/debuglog_off.go b/src/runtime/debuglog_off.go index dd381569992941..fa3be39c70f834 100644 --- a/src/runtime/debuglog_off.go +++ b/src/runtime/debuglog_off.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !debuglog -// +build !debuglog package runtime diff --git a/src/runtime/debuglog_on.go b/src/runtime/debuglog_on.go index 2fcdbe70d14428..b8150202251b36 100644 --- a/src/runtime/debuglog_on.go +++ b/src/runtime/debuglog_on.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build debuglog -// +build debuglog package runtime diff --git a/src/runtime/debuglog_test.go b/src/runtime/debuglog_test.go index 2570e3565bf52e..10dc72cf51b11f 100644 --- a/src/runtime/debuglog_test.go +++ b/src/runtime/debuglog_test.go @@ -23,7 +23,6 @@ package runtime_test import ( - "bytes" "fmt" "regexp" "runtime" @@ -94,7 +93,7 @@ func TestDebugLogInterleaving(t *testing.T) { } wg.Done() }() - var want bytes.Buffer + var want strings.Builder for i := 0; i < 1000; i++ { runtime.Dlog().I(i).End() fmt.Fprintf(&want, "[] %d\n", i) @@ -122,7 +121,7 @@ func TestDebugLogWraparound(t *testing.T) { runtime.ResetDebugLog() var longString = strings.Repeat("a", 128) - var want bytes.Buffer + var want strings.Builder for i, j := 0, 0; j < 2*runtime.DebugLogBytes; i, j = i+1, j+len(longString) { runtime.Dlog().I(i).S(longString).End() fmt.Fprintf(&want, "[] %d %s\n", i, longString) diff --git a/src/runtime/defer_test.go b/src/runtime/defer_test.go index fc961445975867..3a54951c318a58 100644 --- a/src/runtime/defer_test.go +++ b/src/runtime/defer_test.go @@ -433,8 +433,86 @@ func TestIssue43921(t *testing.T) { }() } -func expect(t *testing.T, n int, err interface{}) { +func expect(t *testing.T, n int, err any) { if n != err { t.Fatalf("have %v, want %v", err, n) } } + +func TestIssue43920(t *testing.T) { + var steps int + + defer func() { + expect(t, 1, recover()) + }() + defer func() { + defer func() { + defer func() { + expect(t, 5, recover()) + }() + defer panic(5) + func() { + panic(4) + }() + }() + defer func() { + expect(t, 3, recover()) + }() + defer panic(3) + }() + func() { + defer step(t, &steps, 1) + panic(1) + }() +} + +func step(t *testing.T, steps *int, want int) { + *steps++ + if *steps != want { + t.Fatalf("have %v, want %v", *steps, want) + } +} + +func TestIssue43941(t *testing.T) { + var steps int = 7 + defer func() { + step(t, &steps, 14) + expect(t, 4, recover()) + }() + func() { + func() { + defer func() { + defer func() { + expect(t, 3, recover()) + }() + defer panic(3) + panic(2) + }() + defer func() { + expect(t, 1, recover()) + }() + defer panic(1) + }() + defer func() {}() + defer func() {}() + defer step(t, &steps, 10) + defer step(t, &steps, 9) + step(t, &steps, 8) + }() + func() { + defer step(t, &steps, 13) + defer step(t, &steps, 12) + func() { + defer step(t, &steps, 11) + panic(4) + }() + + // Code below isn't executed, + // but removing it breaks the test case. + defer func() {}() + defer panic(-1) + defer step(t, &steps, -1) + defer step(t, &steps, -1) + defer func() {}() + }() +} diff --git a/src/runtime/defs1_linux.go b/src/runtime/defs1_linux.go index df9c05dd5ed147..709f19e599910d 100644 --- a/src/runtime/defs1_linux.go +++ b/src/runtime/defs1_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -cdefs diff --git a/src/runtime/defs1_netbsd_386.go b/src/runtime/defs1_netbsd_386.go index a4548e6f0629bc..33ce63cd378e76 100644 --- a/src/runtime/defs1_netbsd_386.go +++ b/src/runtime/defs1_netbsd_386.go @@ -7,7 +7,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x400000 @@ -21,7 +20,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs1_netbsd_amd64.go b/src/runtime/defs1_netbsd_amd64.go index 4b0e79ebb6fc4c..b87833be65cd97 100644 --- a/src/runtime/defs1_netbsd_amd64.go +++ b/src/runtime/defs1_netbsd_amd64.go @@ -7,7 +7,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x400000 @@ -21,7 +20,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs1_netbsd_arm.go b/src/runtime/defs1_netbsd_arm.go index 2b5d5990d3618b..9cb680e360dcfa 100644 --- a/src/runtime/defs1_netbsd_arm.go +++ b/src/runtime/defs1_netbsd_arm.go @@ -7,7 +7,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x400000 @@ -21,7 +20,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs1_netbsd_arm64.go b/src/runtime/defs1_netbsd_arm64.go index 740dc77658f917..1c05ce6ad8194b 100644 --- a/src/runtime/defs1_netbsd_arm64.go +++ b/src/runtime/defs1_netbsd_arm64.go @@ -7,7 +7,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x400000 @@ -21,7 +20,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs1_solaris_amd64.go b/src/runtime/defs1_solaris_amd64.go index 19e8a2512e9df2..003cd1c42c00ba 100644 --- a/src/runtime/defs1_solaris_amd64.go +++ b/src/runtime/defs1_solaris_amd64.go @@ -13,7 +13,6 @@ const ( _ETIMEDOUT = 0x91 _EWOULDBLOCK = 0xb _EINPROGRESS = 0x96 - _ENOSYS = 0x59 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -24,7 +23,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x8 _SA_RESTART = 0x4 diff --git a/src/runtime/defs2_linux.go b/src/runtime/defs2_linux.go index d016db7d027cf6..41ad73576f15af 100644 --- a/src/runtime/defs2_linux.go +++ b/src/runtime/defs2_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* * Input to cgo -cdefs diff --git a/src/runtime/defs3_linux.go b/src/runtime/defs3_linux.go index 0a06aa2370725b..99479aad06191e 100644 --- a/src/runtime/defs3_linux.go +++ b/src/runtime/defs3_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -cdefs diff --git a/src/runtime/defs_aix.go b/src/runtime/defs_aix.go index 1605002ea20b9d..b794cd5de8b2da 100644 --- a/src/runtime/defs_aix.go +++ b/src/runtime/defs_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs diff --git a/src/runtime/defs_aix_ppc64.go b/src/runtime/defs_aix_ppc64.go index f84ff1160d0df5..4e20c858411d9b 100644 --- a/src/runtime/defs_aix_ppc64.go +++ b/src/runtime/defs_aix_ppc64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix package runtime diff --git a/src/runtime/defs_arm_linux.go b/src/runtime/defs_arm_linux.go index f6b6dd2c09e58a..805735bd0eef8c 100644 --- a/src/runtime/defs_arm_linux.go +++ b/src/runtime/defs_arm_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/defs_darwin.go b/src/runtime/defs_darwin.go index 2d41a97b5741b0..59b81cf7136e24 100644 --- a/src/runtime/defs_darwin.go +++ b/src/runtime/defs_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/defs_dragonfly.go b/src/runtime/defs_dragonfly.go index aca2bf9001e82a..f67835692c084c 100644 --- a/src/runtime/defs_dragonfly.go +++ b/src/runtime/defs_dragonfly.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. @@ -32,7 +31,6 @@ const ( EFAULT = C.EFAULT EBUSY = C.EBUSY EAGAIN = C.EAGAIN - ENOSYS = C.ENOSYS O_NONBLOCK = C.O_NONBLOCK O_CLOEXEC = C.O_CLOEXEC @@ -46,7 +44,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED - MADV_FREE = C.MADV_FREE + MADV_DONTNEED = C.MADV_DONTNEED + MADV_FREE = C.MADV_FREE SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART diff --git a/src/runtime/defs_dragonfly_amd64.go b/src/runtime/defs_dragonfly_amd64.go index f3c6ecd04b7997..7ab5fecb902442 100644 --- a/src/runtime/defs_dragonfly_amd64.go +++ b/src/runtime/defs_dragonfly_amd64.go @@ -10,7 +10,6 @@ const ( _EFAULT = 0xe _EBUSY = 0x10 _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x20000 @@ -24,7 +23,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_freebsd.go b/src/runtime/defs_freebsd.go index c258759549c8b9..c4204ae0e792d7 100644 --- a/src/runtime/defs_freebsd.go +++ b/src/runtime/defs_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. @@ -49,7 +48,6 @@ const ( EINTR = C.EINTR EFAULT = C.EFAULT EAGAIN = C.EAGAIN - ENOSYS = C.ENOSYS O_NONBLOCK = C.O_NONBLOCK O_CLOEXEC = C.O_CLOEXEC @@ -64,7 +62,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED - MADV_FREE = C.MADV_FREE + MADV_DONTNEED = C.MADV_DONTNEED + MADV_FREE = C.MADV_FREE SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART diff --git a/src/runtime/defs_freebsd_386.go b/src/runtime/defs_freebsd_386.go index f822934d582c74..35af133bdda99d 100644 --- a/src/runtime/defs_freebsd_386.go +++ b/src/runtime/defs_freebsd_386.go @@ -16,7 +16,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 @@ -32,7 +31,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_freebsd_amd64.go b/src/runtime/defs_freebsd_amd64.go index 0b696cf2270901..791c4d1d0317f6 100644 --- a/src/runtime/defs_freebsd_amd64.go +++ b/src/runtime/defs_freebsd_amd64.go @@ -16,7 +16,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 @@ -32,7 +31,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_freebsd_arm.go b/src/runtime/defs_freebsd_arm.go index b6f3e790cff931..9ab49c276ae027 100644 --- a/src/runtime/defs_freebsd_arm.go +++ b/src/runtime/defs_freebsd_arm.go @@ -16,7 +16,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 @@ -32,7 +31,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_freebsd_arm64.go b/src/runtime/defs_freebsd_arm64.go index 0759a1238f940c..9d8e2430746230 100644 --- a/src/runtime/defs_freebsd_arm64.go +++ b/src/runtime/defs_freebsd_arm64.go @@ -16,7 +16,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 @@ -32,7 +31,8 @@ const ( _MAP_PRIVATE = 0x2 _MAP_FIXED = 0x10 - _MADV_FREE = 0x5 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_linux.go b/src/runtime/defs_linux.go index 022ef19427e188..e55bb6bbbcfe55 100644 --- a/src/runtime/defs_linux.go +++ b/src/runtime/defs_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -cdefs @@ -38,7 +37,6 @@ const ( EINTR = C.EINTR EAGAIN = C.EAGAIN ENOMEM = C.ENOMEM - ENOSYS = C.ENOSYS PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ @@ -58,6 +56,9 @@ const ( SA_ONSTACK = C.SA_ONSTACK SA_SIGINFO = C.SA_SIGINFO + SI_KERNEL = C.SI_KERNEL + SI_TIMER = C.SI_TIMER + SIGHUP = C.SIGHUP SIGINT = C.SIGINT SIGQUIT = C.SIGQUIT @@ -89,6 +90,8 @@ const ( SIGPWR = C.SIGPWR SIGSYS = C.SIGSYS + SIGRTMIN = C.SIGRTMIN + FPE_INTDIV = C.FPE_INTDIV FPE_INTOVF = C.FPE_INTOVF FPE_FLTDIV = C.FPE_FLTDIV @@ -109,6 +112,10 @@ const ( ITIMER_VIRTUAL = C.ITIMER_VIRTUAL ITIMER_PROF = C.ITIMER_PROF + CLOCK_THREAD_CPUTIME_ID = C.CLOCK_THREAD_CPUTIME_ID + + SIGEV_THREAD_ID = C.SIGEV_THREAD_ID + EPOLLIN = C.POLLIN EPOLLOUT = C.POLLOUT EPOLLERR = C.POLLERR @@ -126,5 +133,7 @@ type Timespec C.struct_timespec type Timeval C.struct_timeval type Sigaction C.struct_sigaction type Siginfo C.siginfo_t +type Itimerspec C.struct_itimerspec type Itimerval C.struct_itimerval +type Sigevent C.struct_sigevent type EpollEvent C.struct_epoll_event diff --git a/src/runtime/defs_linux_386.go b/src/runtime/defs_linux_386.go index 64a0fbcaaaa5f0..5376bded2b1697 100644 --- a/src/runtime/defs_linux_386.go +++ b/src/runtime/defs_linux_386.go @@ -3,11 +3,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -28,6 +29,9 @@ const ( _SA_RESTORER = 0x4000000 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -59,6 +63,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -79,6 +85,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _O_RDONLY = 0x0 _O_NONBLOCK = 0x800 _O_CLOEXEC = 0x80000 @@ -159,7 +169,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -167,6 +177,13 @@ type siginfo struct { si_addr uint32 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + type stackt struct { ss_sp *byte ss_flags int32 @@ -212,11 +229,31 @@ type ucontext struct { uc_sigmask uint32 } +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 data [8]byte // to match amd64 diff --git a/src/runtime/defs_linux_amd64.go b/src/runtime/defs_linux_amd64.go index 1ae18a309bd70c..da4d3575328184 100644 --- a/src/runtime/defs_linux_amd64.go +++ b/src/runtime/defs_linux_amd64.go @@ -3,11 +3,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -28,6 +29,9 @@ const ( _SA_RESTORER = 0x4000000 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -59,6 +63,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -79,6 +85,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -121,7 +131,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -129,11 +139,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 data [8]byte // unaligned uintptr diff --git a/src/runtime/defs_linux_arm.go b/src/runtime/defs_linux_arm.go index 5bc0916f8b66ca..18aa0931e52b28 100644 --- a/src/runtime/defs_linux_arm.go +++ b/src/runtime/defs_linux_arm.go @@ -4,12 +4,13 @@ package runtime +import "unsafe" + // Constants const ( _EINTR = 0x4 _ENOMEM = 0xc _EAGAIN = 0xb - _ENOSYS = 0x26 _PROT_NONE = 0 _PROT_READ = 0x1 @@ -29,6 +30,8 @@ const ( _SA_ONSTACK = 0x8000000 _SA_RESTORER = 0 // unused on ARM _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -59,6 +62,7 @@ const ( _SIGIO = 0x1d _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -79,6 +83,10 @@ const ( _O_NONBLOCK = 0x800 _O_CLOEXEC = 0x80000 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -153,12 +161,32 @@ func (tv *timeval) set_usec(x int32) { tv.tv_usec = x } +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } -type siginfo struct { +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -166,6 +194,13 @@ type siginfo struct { si_addr uint32 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + type sigactiont struct { sa_handler uintptr sa_flags uint32 diff --git a/src/runtime/defs_linux_arm64.go b/src/runtime/defs_linux_arm64.go index 0690cd35b2e07e..c5d7d7e3fd3352 100644 --- a/src/runtime/defs_linux_arm64.go +++ b/src/runtime/defs_linux_arm64.go @@ -3,11 +3,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -28,6 +29,9 @@ const ( _SA_RESTORER = 0x0 // Only used on intel _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -59,6 +63,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -79,6 +85,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -121,7 +131,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -129,11 +139,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 _pad uint32 diff --git a/src/runtime/defs_linux_loong64.go b/src/runtime/defs_linux_loong64.go new file mode 100644 index 00000000000000..dda4009fb07b5d --- /dev/null +++ b/src/runtime/defs_linux_loong64.go @@ -0,0 +1,211 @@ +// Generated using cgo, then manually converted into appropriate naming and code +// for the Go runtime. +// go tool cgo -godefs defs_linux.go defs1_linux.go defs2_linux.go + +package runtime + +import "unsafe" + +const ( + _EINTR = 0x4 + _EAGAIN = 0xb + _ENOMEM = 0xc + + _PROT_NONE = 0x0 + _PROT_READ = 0x1 + _PROT_WRITE = 0x2 + _PROT_EXEC = 0x4 + + _MAP_ANON = 0x20 + _MAP_PRIVATE = 0x2 + _MAP_FIXED = 0x10 + + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x8 + _MADV_HUGEPAGE = 0xe + _MADV_NOHUGEPAGE = 0xf + + _SA_RESTART = 0x10000000 + _SA_ONSTACK = 0x8000000 + _SA_SIGINFO = 0x4 + _SA_RESTORER = 0x0 + + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + + _SIGHUP = 0x1 + _SIGINT = 0x2 + _SIGQUIT = 0x3 + _SIGILL = 0x4 + _SIGTRAP = 0x5 + _SIGABRT = 0x6 + _SIGBUS = 0x7 + _SIGFPE = 0x8 + _SIGKILL = 0x9 + _SIGUSR1 = 0xa + _SIGSEGV = 0xb + _SIGUSR2 = 0xc + _SIGPIPE = 0xd + _SIGALRM = 0xe + _SIGSTKFLT = 0x10 + _SIGCHLD = 0x11 + _SIGCONT = 0x12 + _SIGSTOP = 0x13 + _SIGTSTP = 0x14 + _SIGTTIN = 0x15 + _SIGTTOU = 0x16 + _SIGURG = 0x17 + _SIGXCPU = 0x18 + _SIGXFSZ = 0x19 + _SIGVTALRM = 0x1a + _SIGPROF = 0x1b + _SIGWINCH = 0x1c + _SIGIO = 0x1d + _SIGPWR = 0x1e + _SIGSYS = 0x1f + + _SIGRTMIN = 0x20 + + _FPE_INTDIV = 0x1 + _FPE_INTOVF = 0x2 + _FPE_FLTDIV = 0x3 + _FPE_FLTOVF = 0x4 + _FPE_FLTUND = 0x5 + _FPE_FLTRES = 0x6 + _FPE_FLTINV = 0x7 + _FPE_FLTSUB = 0x8 + + _BUS_ADRALN = 0x1 + _BUS_ADRERR = 0x2 + _BUS_OBJERR = 0x3 + + _SEGV_MAPERR = 0x1 + _SEGV_ACCERR = 0x2 + + _ITIMER_REAL = 0x0 + _ITIMER_VIRTUAL = 0x1 + _ITIMER_PROF = 0x2 + + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + + _EPOLLIN = 0x1 + _EPOLLOUT = 0x4 + _EPOLLERR = 0x8 + _EPOLLHUP = 0x10 + _EPOLLRDHUP = 0x2000 + _EPOLLET = 0x80000000 + _EPOLL_CLOEXEC = 0x80000 + _EPOLL_CTL_ADD = 0x1 + _EPOLL_CTL_DEL = 0x2 + _EPOLL_CTL_MOD = 0x3 +) + +type timespec struct { + tv_sec int64 + tv_nsec int64 +} + +//go:nosplit +func (ts *timespec) setNsec(ns int64) { + ts.tv_sec = ns / 1e9 + ts.tv_nsec = ns % 1e9 +} + +type timeval struct { + tv_sec int64 + tv_usec int64 +} + +func (tv *timeval) set_usec(x int32) { + tv.tv_usec = int64(x) +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + +type itimerval struct { + it_interval timeval + it_value timeval +} + +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + +type epollevent struct { + events uint32 + pad_cgo_0 [4]byte + data [8]byte // unaligned uintptr +} + +const ( + _O_RDONLY = 0x0 + _O_NONBLOCK = 0x800 + _O_CLOEXEC = 0x80000 +) + +type sigactiont struct { + sa_handler uintptr + sa_flags uint64 + sa_mask uint64 + // Linux on loong64 does not have the sa_restorer field, but the setsig + // function references it (for x86). Not much harm to include it at the end. + sa_restorer uintptr +} + +type siginfoFields struct { + si_signo int32 + si_errno int32 + si_code int32 + __pad0 [1]int32 + // below here is a union; si_addr is the only field we use + si_addr uint64 +} + +type siginfo struct { + siginfoFields + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type usigset struct { + val [16]uint64 +} + +type stackt struct { + ss_sp *byte + ss_flags int32 + pad_cgo_0 [4]byte + ss_size uintptr +} + +type sigcontext struct { + sc_pc uint64 + sc_regs [32]uint64 + sc_flags uint32 + sc_extcontext [0]uint64 +} + +type ucontext struct { + uc_flags uint64 + uc_link *ucontext + uc_stack stackt + uc_sigmask usigset + uc_x_unused [0]uint8 + uc_pad_cgo_0 [8]byte + uc_mcontext sigcontext +} diff --git a/src/runtime/defs_linux_mips64x.go b/src/runtime/defs_linux_mips64x.go index 2cafad20cf9882..e64524813180d9 100644 --- a/src/runtime/defs_linux_mips64x.go +++ b/src/runtime/defs_linux_mips64x.go @@ -3,16 +3,15 @@ // license that can be found in the LICENSE file. //go:build (mips64 || mips64le) && linux -// +build mips64 mips64le -// +build linux package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x59 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -32,6 +31,9 @@ const ( _SA_ONSTACK = 0x8000000 _SA_SIGINFO = 0x8 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -63,6 +65,8 @@ const ( _SIGXCPU = 0x1e _SIGXFSZ = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -83,6 +87,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -129,7 +137,7 @@ type sigactiont struct { sa_restorer uintptr } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_code int32 si_errno int32 @@ -138,11 +146,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_linux_mipsx.go b/src/runtime/defs_linux_mipsx.go index 3a8dfe2e990081..5afb6f423f94b1 100644 --- a/src/runtime/defs_linux_mipsx.go +++ b/src/runtime/defs_linux_mipsx.go @@ -3,16 +3,15 @@ // license that can be found in the LICENSE file. //go:build (mips || mipsle) && linux -// +build mips mipsle -// +build linux package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x59 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -32,6 +31,9 @@ const ( _SA_ONSTACK = 0x8000000 _SA_SIGINFO = 0x8 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -63,6 +65,8 @@ const ( _SIGXCPU = 0x1e _SIGXFSZ = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -83,6 +87,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -124,7 +132,7 @@ type sigactiont struct { sa_restorer uintptr } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_code int32 si_errno int32 @@ -132,11 +140,38 @@ type siginfo struct { si_addr uint32 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_linux_ppc64.go b/src/runtime/defs_linux_ppc64.go index 90b1dc1ff9d84e..f3e305e34eab63 100644 --- a/src/runtime/defs_linux_ppc64.go +++ b/src/runtime/defs_linux_ppc64.go @@ -3,11 +3,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -27,6 +28,9 @@ const ( _SA_ONSTACK = 0x8000000 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -58,6 +62,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -78,6 +84,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -122,7 +132,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -130,11 +140,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_linux_ppc64le.go b/src/runtime/defs_linux_ppc64le.go index 90b1dc1ff9d84e..f3e305e34eab63 100644 --- a/src/runtime/defs_linux_ppc64le.go +++ b/src/runtime/defs_linux_ppc64le.go @@ -3,11 +3,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -27,6 +28,9 @@ const ( _SA_ONSTACK = 0x8000000 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -58,6 +62,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -78,6 +84,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -122,7 +132,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -130,11 +140,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_linux_riscv64.go b/src/runtime/defs_linux_riscv64.go index 60da0fae000775..29496acdcb6992 100644 --- a/src/runtime/defs_linux_riscv64.go +++ b/src/runtime/defs_linux_riscv64.go @@ -4,11 +4,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -29,6 +30,9 @@ const ( _SA_RESTORER = 0x0 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -60,6 +64,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -80,6 +86,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -113,13 +123,15 @@ func (tv *timeval) set_usec(x int32) { } type sigactiont struct { - sa_handler uintptr - sa_flags uint64 + sa_handler uintptr + sa_flags uint64 + sa_mask uint64 + // Linux on riscv64 does not have the sa_restorer field, but the setsig + // function references it (for x86). Not much harm to include it at the end. sa_restorer uintptr - sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -127,11 +139,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go index fa289d531c074b..817a29ed30cd34 100644 --- a/src/runtime/defs_linux_s390x.go +++ b/src/runtime/defs_linux_s390x.go @@ -4,11 +4,12 @@ package runtime +import "unsafe" + const ( _EINTR = 0x4 _EAGAIN = 0xb _ENOMEM = 0xc - _ENOSYS = 0x26 _PROT_NONE = 0x0 _PROT_READ = 0x1 @@ -28,6 +29,9 @@ const ( _SA_ONSTACK = 0x8000000 _SA_SIGINFO = 0x4 + _SI_KERNEL = 0x80 + _SI_TIMER = -0x2 + _SIGHUP = 0x1 _SIGINT = 0x2 _SIGQUIT = 0x3 @@ -59,6 +63,8 @@ const ( _SIGPWR = 0x1e _SIGSYS = 0x1f + _SIGRTMIN = 0x20 + _FPE_INTDIV = 0x1 _FPE_INTOVF = 0x2 _FPE_FLTDIV = 0x3 @@ -79,6 +85,10 @@ const ( _ITIMER_VIRTUAL = 0x1 _ITIMER_PROF = 0x2 + _CLOCK_THREAD_CPUTIME_ID = 0x3 + + _SIGEV_THREAD_ID = 0x4 + _EPOLLIN = 0x1 _EPOLLOUT = 0x4 _EPOLLERR = 0x8 @@ -118,7 +128,7 @@ type sigactiont struct { sa_mask uint64 } -type siginfo struct { +type siginfoFields struct { si_signo int32 si_errno int32 si_code int32 @@ -126,11 +136,38 @@ type siginfo struct { si_addr uint64 } +type siginfo struct { + siginfoFields + + // Pad struct to the max size in the kernel. + _ [_si_max_size - unsafe.Sizeof(siginfoFields{})]byte +} + +type itimerspec struct { + it_interval timespec + it_value timespec +} + type itimerval struct { it_interval timeval it_value timeval } +type sigeventFields struct { + value uintptr + signo int32 + notify int32 + // below here is a union; sigev_notify_thread_id is the only field we use + sigev_notify_thread_id int32 +} + +type sigevent struct { + sigeventFields + + // Pad struct to the max size in the kernel. + _ [_sigev_max_size - unsafe.Sizeof(sigeventFields{})]byte +} + type epollevent struct { events uint32 pad_cgo_0 [4]byte diff --git a/src/runtime/defs_netbsd.go b/src/runtime/defs_netbsd.go index 755992d18e0359..865b94600069eb 100644 --- a/src/runtime/defs_netbsd.go +++ b/src/runtime/defs_netbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. @@ -34,7 +33,6 @@ const ( EINTR = C.EINTR EFAULT = C.EFAULT EAGAIN = C.EAGAIN - ENOSYS = C.ENOSYS O_NONBLOCK = C.O_NONBLOCK O_CLOEXEC = C.O_CLOEXEC @@ -48,7 +46,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED - MADV_FREE = C.MADV_FREE + MADV_DONTNEED = C.MADV_DONTNEED + MADV_FREE = C.MADV_FREE SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART diff --git a/src/runtime/defs_netbsd_386.go b/src/runtime/defs_netbsd_386.go index 03c9c2de59e5e6..2943ea3f13eebf 100644 --- a/src/runtime/defs_netbsd_386.go +++ b/src/runtime/defs_netbsd_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/defs_netbsd_amd64.go b/src/runtime/defs_netbsd_amd64.go index 9fda1d7d226572..33d80ff53c0ccc 100644 --- a/src/runtime/defs_netbsd_amd64.go +++ b/src/runtime/defs_netbsd_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/defs_netbsd_arm.go b/src/runtime/defs_netbsd_arm.go index e7f4f6cece7ac1..74b37527df66a6 100644 --- a/src/runtime/defs_netbsd_arm.go +++ b/src/runtime/defs_netbsd_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/defs_openbsd.go b/src/runtime/defs_openbsd.go index 8d323449d1c5d4..4161e216db6983 100644 --- a/src/runtime/defs_openbsd.go +++ b/src/runtime/defs_openbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. @@ -26,6 +25,7 @@ package runtime #include #include #include +#include #include */ import "C" @@ -34,7 +34,6 @@ const ( EINTR = C.EINTR EFAULT = C.EFAULT EAGAIN = C.EAGAIN - ENOSYS = C.ENOSYS O_NONBLOCK = C.O_NONBLOCK O_CLOEXEC = C.O_CLOEXEC @@ -49,7 +48,8 @@ const ( MAP_FIXED = C.MAP_FIXED MAP_STACK = C.MAP_STACK - MADV_FREE = C.MADV_FREE + MADV_DONTNEED = C.MADV_DONTNEED + MADV_FREE = C.MADV_FREE SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART diff --git a/src/runtime/defs_openbsd_386.go b/src/runtime/defs_openbsd_386.go index a866ec880ad22c..72a66ae084cec3 100644 --- a/src/runtime/defs_openbsd_386.go +++ b/src/runtime/defs_openbsd_386.go @@ -9,7 +9,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x10000 @@ -24,7 +23,8 @@ const ( _MAP_FIXED = 0x10 _MAP_STACK = 0x4000 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_openbsd_amd64.go b/src/runtime/defs_openbsd_amd64.go index 46f1245201ce32..b1e8dca359db1d 100644 --- a/src/runtime/defs_openbsd_amd64.go +++ b/src/runtime/defs_openbsd_amd64.go @@ -9,7 +9,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x10000 @@ -24,7 +23,8 @@ const ( _MAP_FIXED = 0x10 _MAP_STACK = 0x4000 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_openbsd_arm.go b/src/runtime/defs_openbsd_arm.go index 6f128c4284b154..ba6b2c17bfc6dd 100644 --- a/src/runtime/defs_openbsd_arm.go +++ b/src/runtime/defs_openbsd_arm.go @@ -9,7 +9,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x10000 @@ -24,7 +23,8 @@ const ( _MAP_FIXED = 0x10 _MAP_STACK = 0x4000 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_openbsd_arm64.go b/src/runtime/defs_openbsd_arm64.go index d2b947feb2741c..13d2e95a86b172 100644 --- a/src/runtime/defs_openbsd_arm64.go +++ b/src/runtime/defs_openbsd_arm64.go @@ -10,7 +10,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x10000 @@ -25,7 +24,8 @@ const ( _MAP_FIXED = 0x10 _MAP_STACK = 0x4000 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_openbsd_mips64.go b/src/runtime/defs_openbsd_mips64.go index 28d70b7a018df8..c0bc766d8ff794 100644 --- a/src/runtime/defs_openbsd_mips64.go +++ b/src/runtime/defs_openbsd_mips64.go @@ -16,7 +16,6 @@ const ( _EINTR = 0x4 _EFAULT = 0xe _EAGAIN = 0x23 - _ENOSYS = 0x4e _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x10000 @@ -31,7 +30,8 @@ const ( _MAP_FIXED = 0x10 _MAP_STACK = 0x4000 - _MADV_FREE = 0x6 + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x6 _SA_SIGINFO = 0x40 _SA_RESTART = 0x2 diff --git a/src/runtime/defs_plan9_386.go b/src/runtime/defs_plan9_386.go index 49129b3c3fcb1c..428044df6871d7 100644 --- a/src/runtime/defs_plan9_386.go +++ b/src/runtime/defs_plan9_386.go @@ -61,4 +61,4 @@ func dumpregs(u *ureg) { print("gs ", hex(u.gs), "\n") } -func sigpanictramp() {} +func sigpanictramp() diff --git a/src/runtime/defs_plan9_amd64.go b/src/runtime/defs_plan9_amd64.go index 00995630341a81..15a27fc7db9e51 100644 --- a/src/runtime/defs_plan9_amd64.go +++ b/src/runtime/defs_plan9_amd64.go @@ -78,4 +78,4 @@ func dumpregs(u *ureg) { print("gs ", hex(u.gs), "\n") } -func sigpanictramp() {} +func sigpanictramp() diff --git a/src/runtime/defs_solaris.go b/src/runtime/defs_solaris.go index e644f9c6ddd5af..bb89eec1bf53df 100644 --- a/src/runtime/defs_solaris.go +++ b/src/runtime/defs_solaris.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. @@ -44,7 +43,6 @@ const ( ETIMEDOUT = C.ETIMEDOUT EWOULDBLOCK = C.EWOULDBLOCK EINPROGRESS = C.EINPROGRESS - ENOSYS = C.ENOSYS PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ @@ -55,7 +53,8 @@ const ( MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED - MADV_FREE = C.MADV_FREE + MADV_DONTNEED = C.MADV_DONTNEED + MADV_FREE = C.MADV_FREE SA_SIGINFO = C.SA_SIGINFO SA_RESTART = C.SA_RESTART diff --git a/src/runtime/defs_solaris_amd64.go b/src/runtime/defs_solaris_amd64.go index a0b38319a5ddca..56e4b38c5e4384 100644 --- a/src/runtime/defs_solaris_amd64.go +++ b/src/runtime/defs_solaris_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo. diff --git a/src/runtime/duff_arm64.s b/src/runtime/duff_arm64.s index 128b076af9db65..33c4905078d242 100644 --- a/src/runtime/duff_arm64.s +++ b/src/runtime/duff_arm64.s @@ -4,7 +4,7 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 STP.P (ZR, ZR), 16(R20) STP.P (ZR, ZR), 16(R20) STP.P (ZR, ZR), 16(R20) @@ -71,7 +71,7 @@ TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 STP (ZR, ZR), (R20) RET -TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 LDP.P 16(R20), (R26, R27) STP.P (R26, R27), 16(R21) diff --git a/src/runtime/duff_loong64.s b/src/runtime/duff_loong64.s new file mode 100644 index 00000000000000..7f78e4fa9f1b59 --- /dev/null +++ b/src/runtime/duff_loong64.s @@ -0,0 +1,907 @@ +// Code generated by mkduff.go; DO NOT EDIT. +// Run go generate from src/runtime to update. +// See mkduff.go for comments. + +#include "textflag.h" + +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + MOVV R0, 8(R19) + ADDV $8, R19 + RET + +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + MOVV (R19), R30 + ADDV $8, R19 + MOVV R30, (R20) + ADDV $8, R20 + + RET diff --git a/src/runtime/duff_mips64x.s b/src/runtime/duff_mips64x.s index a897d7fd9bf25e..3a8524c78bfe1a 100644 --- a/src/runtime/duff_mips64x.s +++ b/src/runtime/duff_mips64x.s @@ -3,7 +3,6 @@ // See mkduff.go for comments. //go:build mips64 || mips64le -// +build mips64 mips64le #include "textflag.h" diff --git a/src/runtime/duff_ppc64x.s b/src/runtime/duff_ppc64x.s index d4e3b409d24b9d..a3caaa881728a0 100644 --- a/src/runtime/duff_ppc64x.s +++ b/src/runtime/duff_ppc64x.s @@ -3,140 +3,395 @@ // See mkduff.go for comments. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) - MOVDU R0, 8(R3) +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) + MOVDU R0, 8(R20) RET -TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 - UNDEF +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + MOVDU 8(R20), R5 + MOVDU R5, 8(R21) + RET diff --git a/src/runtime/duff_riscv64.s b/src/runtime/duff_riscv64.s index f7bd3f326e9d0b..ec447677ad0c17 100644 --- a/src/runtime/duff_riscv64.s +++ b/src/runtime/duff_riscv64.s @@ -4,904 +4,904 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 - MOV ZERO, (X10) - ADD $8, X10 +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 + MOV ZERO, (X25) + ADD $8, X25 RET -TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 - - MOV (X10), X31 - ADD $8, X10 - MOV X31, (X11) - ADD $8, X11 +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 + + MOV (X24), X31 + ADD $8, X24 + MOV X31, (X25) + ADD $8, X25 RET diff --git a/src/runtime/env_plan9.go b/src/runtime/env_plan9.go index f1ac4760a750d2..65480c82177072 100644 --- a/src/runtime/env_plan9.go +++ b/src/runtime/env_plan9.go @@ -25,6 +25,7 @@ const ( // For Plan 9 shared environment semantics, instead of Getenv(key) and // Setenv(key, value), one can use os.ReadFile("/env/" + key) and // os.WriteFile("/env/" + key, value, 0666) respectively. +// //go:nosplit func goenvs() { buf := make([]byte, envBufSize) @@ -71,6 +72,7 @@ func goenvs() { // Dofiles reads the directory opened with file descriptor fd, applying function f // to each filename in it. +// //go:nosplit func dofiles(dirfd int32, f func([]byte)) { dirbuf := new([dirBufSize]byte) @@ -96,6 +98,7 @@ func dofiles(dirfd int32, f func([]byte)) { // 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 2+nameOffset+2 > len(buf) { @@ -116,6 +119,7 @@ func gdirname(buf []byte) (name []byte, rest []byte) { // Gbit16 reads a 16-bit little-endian binary number from b and returns it // with the remaining slice of b. +// //go:nosplit func gbit16(b []byte) (int, []byte) { return int(b[0]) | int(b[1])<<8, b[2:] diff --git a/src/runtime/env_posix.go b/src/runtime/env_posix.go index 95517b2a958808..94a19d80d8e86e 100644 --- a/src/runtime/env_posix.go +++ b/src/runtime/env_posix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows || plan9 -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows plan9 +//go:build unix || (js && wasm) || windows || plan9 package runtime @@ -50,6 +49,7 @@ var _cgo_unsetenv unsafe.Pointer // pointer to C function // Update the C environment if cgo is loaded. // Called from syscall.Setenv. +// //go:linkname syscall_setenv_c syscall.setenv_c func syscall_setenv_c(k string, v string) { if _cgo_setenv == nil { @@ -61,6 +61,7 @@ func syscall_setenv_c(k string, v string) { // Update the C environment if cgo is loaded. // Called from syscall.unsetenv. +// //go:linkname syscall_unsetenv_c syscall.unsetenv_c func syscall_unsetenv_c(k string) { if _cgo_unsetenv == nil { diff --git a/src/runtime/error.go b/src/runtime/error.go index 91f83ae126405c..b11473c6346247 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -53,10 +53,11 @@ func (e *TypeAssertionError) Error() string { ": missing method " + e.missingMethod } -//go:nosplit // itoa converts val to a decimal representation. The result is // written somewhere within buf and the location of the result is returned. // buf must be at least 20 bytes. +// +//go:nosplit func itoa(buf []byte, val uint64) []byte { i := len(buf) - 1 for val >= 10 { @@ -210,7 +211,7 @@ type stringer interface { // printany prints an argument passed to panic. // If panic is called with a value that has a String or Error method, // it has already been converted into a string by preprintpanics. -func printany(i interface{}) { +func printany(i any) { switch v := i.(type) { case nil: print("nil") @@ -253,7 +254,7 @@ func printany(i interface{}) { } } -func printanycustomtype(i interface{}) { +func printanycustomtype(i any) { eface := efaceOf(&i) typestring := eface._type.string() diff --git a/src/runtime/export_aix_test.go b/src/runtime/export_aix_test.go index 162552d04c46d1..51df951738436a 100644 --- a/src/runtime/export_aix_test.go +++ b/src/runtime/export_aix_test.go @@ -5,3 +5,4 @@ package runtime var Fcntl = syscall_fcntl1 +var SetNonblock = setNonblock diff --git a/src/runtime/export_darwin_test.go b/src/runtime/export_darwin_test.go index e9b6eb36da5cda..66e2c02c4f6c21 100644 --- a/src/runtime/export_darwin_test.go +++ b/src/runtime/export_darwin_test.go @@ -11,3 +11,5 @@ func Fcntl(fd, cmd, arg uintptr) (uintptr, uintptr) { } return uintptr(r), 0 } + +var SetNonblock = setNonblock diff --git a/src/runtime/export_debug_amd64_test.go b/src/runtime/export_debug_amd64_test.go new file mode 100644 index 00000000000000..f9908cd494595c --- /dev/null +++ b/src/runtime/export_debug_amd64_test.go @@ -0,0 +1,132 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build amd64 && linux + +package runtime + +import ( + "internal/abi" + "internal/goarch" + "unsafe" +) + +type sigContext struct { + savedRegs sigcontext + // sigcontext.fpstate is a pointer, so we need to save + // the its value with a fpstate1 structure. + savedFP fpstate1 +} + +func sigctxtSetContextRegister(ctxt *sigctxt, x uint64) { + ctxt.regs().rdx = x +} + +func sigctxtAtTrapInstruction(ctxt *sigctxt) bool { + return *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) == 0xcc // INT 3 +} + +func sigctxtStatus(ctxt *sigctxt) uint64 { + return ctxt.r12() +} + +func (h *debugCallHandler) saveSigContext(ctxt *sigctxt) { + // Push current PC on the stack. + rsp := ctxt.rsp() - goarch.PtrSize + *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip() + ctxt.set_rsp(rsp) + // Write the argument frame size. + *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize + // Save current registers. + h.sigCtxt.savedRegs = *ctxt.regs() + h.sigCtxt.savedFP = *h.sigCtxt.savedRegs.fpstate + h.sigCtxt.savedRegs.fpstate = nil +} + +// case 0 +func (h *debugCallHandler) debugCallRun(ctxt *sigctxt) { + rsp := ctxt.rsp() + memmove(unsafe.Pointer(uintptr(rsp)), h.argp, h.argSize) + if h.regArgs != nil { + storeRegArgs(ctxt.regs(), h.regArgs) + } + // Push return PC. + rsp -= goarch.PtrSize + ctxt.set_rsp(rsp) + // The signal PC is the next PC of the trap instruction. + *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip() + // Set PC to call and context register. + ctxt.set_rip(uint64(h.fv.fn)) + sigctxtSetContextRegister(ctxt, uint64(uintptr(unsafe.Pointer(h.fv)))) +} + +// case 1 +func (h *debugCallHandler) debugCallReturn(ctxt *sigctxt) { + rsp := ctxt.rsp() + memmove(h.argp, unsafe.Pointer(uintptr(rsp)), h.argSize) + if h.regArgs != nil { + loadRegArgs(h.regArgs, ctxt.regs()) + } +} + +// case 2 +func (h *debugCallHandler) debugCallPanicOut(ctxt *sigctxt) { + rsp := ctxt.rsp() + memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(rsp)), 2*goarch.PtrSize) +} + +// case 8 +func (h *debugCallHandler) debugCallUnsafe(ctxt *sigctxt) { + rsp := ctxt.rsp() + reason := *(*string)(unsafe.Pointer(uintptr(rsp))) + h.err = plainError(reason) +} + +// case 16 +func (h *debugCallHandler) restoreSigContext(ctxt *sigctxt) { + // Restore all registers except RIP and RSP. + rip, rsp := ctxt.rip(), ctxt.rsp() + fp := ctxt.regs().fpstate + *ctxt.regs() = h.sigCtxt.savedRegs + ctxt.regs().fpstate = fp + *fp = h.sigCtxt.savedFP + ctxt.set_rip(rip) + ctxt.set_rsp(rsp) +} + +// storeRegArgs sets up argument registers in the signal +// context state from an abi.RegArgs. +// +// Both src and dst must be non-nil. +func storeRegArgs(dst *sigcontext, src *abi.RegArgs) { + dst.rax = uint64(src.Ints[0]) + dst.rbx = uint64(src.Ints[1]) + dst.rcx = uint64(src.Ints[2]) + dst.rdi = uint64(src.Ints[3]) + dst.rsi = uint64(src.Ints[4]) + dst.r8 = uint64(src.Ints[5]) + dst.r9 = uint64(src.Ints[6]) + dst.r10 = uint64(src.Ints[7]) + dst.r11 = uint64(src.Ints[8]) + for i := range src.Floats { + dst.fpstate._xmm[i].element[0] = uint32(src.Floats[i] >> 0) + dst.fpstate._xmm[i].element[1] = uint32(src.Floats[i] >> 32) + } +} + +func loadRegArgs(dst *abi.RegArgs, src *sigcontext) { + dst.Ints[0] = uintptr(src.rax) + dst.Ints[1] = uintptr(src.rbx) + dst.Ints[2] = uintptr(src.rcx) + dst.Ints[3] = uintptr(src.rdi) + dst.Ints[4] = uintptr(src.rsi) + dst.Ints[5] = uintptr(src.r8) + dst.Ints[6] = uintptr(src.r9) + dst.Ints[7] = uintptr(src.r10) + dst.Ints[8] = uintptr(src.r11) + for i := range dst.Floats { + dst.Floats[i] = uint64(src.fpstate._xmm[i].element[0]) << 0 + dst.Floats[i] |= uint64(src.fpstate._xmm[i].element[1]) << 32 + } +} diff --git a/src/runtime/export_debug_arm64_test.go b/src/runtime/export_debug_arm64_test.go new file mode 100644 index 00000000000000..ee902414afe92a --- /dev/null +++ b/src/runtime/export_debug_arm64_test.go @@ -0,0 +1,135 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build arm64 && linux + +package runtime + +import ( + "internal/abi" + "internal/goarch" + "unsafe" +) + +type sigContext struct { + savedRegs sigcontext +} + +func sigctxtSetContextRegister(ctxt *sigctxt, x uint64) { + ctxt.regs().regs[26] = x +} + +func sigctxtAtTrapInstruction(ctxt *sigctxt) bool { + return *(*uint32)(unsafe.Pointer(ctxt.sigpc())) == 0xd4200000 // BRK 0 +} + +func sigctxtStatus(ctxt *sigctxt) uint64 { + return ctxt.r20() +} + +func (h *debugCallHandler) saveSigContext(ctxt *sigctxt) { + sp := ctxt.sp() + sp -= 2 * goarch.PtrSize + ctxt.set_sp(sp) + *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.lr() // save the current lr + ctxt.set_lr(ctxt.pc()) // set new lr to the current pc + // Write the argument frame size. + *(*uintptr)(unsafe.Pointer(uintptr(sp - 16))) = h.argSize + // Save current registers. + h.sigCtxt.savedRegs = *ctxt.regs() +} + +// case 0 +func (h *debugCallHandler) debugCallRun(ctxt *sigctxt) { + sp := ctxt.sp() + memmove(unsafe.Pointer(uintptr(sp)+8), h.argp, h.argSize) + if h.regArgs != nil { + storeRegArgs(ctxt.regs(), h.regArgs) + } + // Push return PC, which should be the signal PC+4, because + // the signal PC is the PC of the trap instruction itself. + ctxt.set_lr(ctxt.pc() + 4) + // Set PC to call and context register. + ctxt.set_pc(uint64(h.fv.fn)) + sigctxtSetContextRegister(ctxt, uint64(uintptr(unsafe.Pointer(h.fv)))) +} + +// case 1 +func (h *debugCallHandler) debugCallReturn(ctxt *sigctxt) { + sp := ctxt.sp() + memmove(h.argp, unsafe.Pointer(uintptr(sp)+8), h.argSize) + if h.regArgs != nil { + loadRegArgs(h.regArgs, ctxt.regs()) + } + // Restore the old lr from *sp + olr := *(*uint64)(unsafe.Pointer(uintptr(sp))) + ctxt.set_lr(olr) + pc := ctxt.pc() + ctxt.set_pc(pc + 4) // step to next instruction +} + +// case 2 +func (h *debugCallHandler) debugCallPanicOut(ctxt *sigctxt) { + sp := ctxt.sp() + memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)+8), 2*goarch.PtrSize) + ctxt.set_pc(ctxt.pc() + 4) +} + +// case 8 +func (h *debugCallHandler) debugCallUnsafe(ctxt *sigctxt) { + sp := ctxt.sp() + reason := *(*string)(unsafe.Pointer(uintptr(sp) + 8)) + h.err = plainError(reason) + ctxt.set_pc(ctxt.pc() + 4) +} + +// case 16 +func (h *debugCallHandler) restoreSigContext(ctxt *sigctxt) { + // Restore all registers except for pc and sp + pc, sp := ctxt.pc(), ctxt.sp() + *ctxt.regs() = h.sigCtxt.savedRegs + ctxt.set_pc(pc + 4) + ctxt.set_sp(sp) +} + +// storeRegArgs sets up argument registers in the signal +// context state from an abi.RegArgs. +// +// Both src and dst must be non-nil. +func storeRegArgs(dst *sigcontext, src *abi.RegArgs) { + for i, r := range src.Ints { + dst.regs[i] = uint64(r) + } + for i, r := range src.Floats { + *(fpRegAddr(dst, i)) = r + } +} + +func loadRegArgs(dst *abi.RegArgs, src *sigcontext) { + for i := range dst.Ints { + dst.Ints[i] = uintptr(src.regs[i]) + } + for i := range dst.Floats { + dst.Floats[i] = *(fpRegAddr(src, i)) + } +} + +// fpRegAddr returns the address of the ith fp-simd register in sigcontext. +func fpRegAddr(dst *sigcontext, i int) *uint64 { + /* FP-SIMD registers are saved in sigcontext.__reserved, which is orgnized in + the following C structs: + struct fpsimd_context { + struct _aarch64_ctx head; + __u32 fpsr; + __u32 fpcr; + __uint128_t vregs[32]; + }; + struct _aarch64_ctx { + __u32 magic; + __u32 size; + }; + So the offset of the ith FP_SIMD register is 16+i*128. + */ + return (*uint64)(unsafe.Pointer(&dst.__reserved[16+i*128])) +} diff --git a/src/runtime/export_debug_regabiargs_off_test.go b/src/runtime/export_debug_regabiargs_off_test.go deleted file mode 100644 index 5009003d27d7cb..00000000000000 --- a/src/runtime/export_debug_regabiargs_off_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build amd64 && linux && !goexperiment.regabiargs -// +build amd64,linux,!goexperiment.regabiargs - -package runtime - -import "internal/abi" - -func storeRegArgs(dst *sigcontext, src *abi.RegArgs) { -} - -func loadRegArgs(dst *abi.RegArgs, src *sigcontext) { -} diff --git a/src/runtime/export_debug_regabiargs_on_test.go b/src/runtime/export_debug_regabiargs_on_test.go deleted file mode 100644 index e1b72efd0f765e..00000000000000 --- a/src/runtime/export_debug_regabiargs_on_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build amd64 && linux && goexperiment.regabiargs -// +build amd64,linux,goexperiment.regabiargs - -package runtime - -import "internal/abi" - -// storeRegArgs sets up argument registers in the signal -// context state from an abi.RegArgs. -// -// Both src and dst must be non-nil. -func storeRegArgs(dst *sigcontext, src *abi.RegArgs) { - dst.rax = uint64(src.Ints[0]) - dst.rbx = uint64(src.Ints[1]) - dst.rcx = uint64(src.Ints[2]) - dst.rdi = uint64(src.Ints[3]) - dst.rsi = uint64(src.Ints[4]) - dst.r8 = uint64(src.Ints[5]) - dst.r9 = uint64(src.Ints[6]) - dst.r10 = uint64(src.Ints[7]) - dst.r11 = uint64(src.Ints[8]) - for i := range src.Floats { - dst.fpstate._xmm[i].element[0] = uint32(src.Floats[i] >> 0) - dst.fpstate._xmm[i].element[1] = uint32(src.Floats[i] >> 32) - } -} - -func loadRegArgs(dst *abi.RegArgs, src *sigcontext) { - dst.Ints[0] = uintptr(src.rax) - dst.Ints[1] = uintptr(src.rbx) - dst.Ints[2] = uintptr(src.rcx) - dst.Ints[3] = uintptr(src.rdi) - dst.Ints[4] = uintptr(src.rsi) - dst.Ints[5] = uintptr(src.r8) - dst.Ints[6] = uintptr(src.r9) - dst.Ints[7] = uintptr(src.r10) - dst.Ints[8] = uintptr(src.r11) - for i := range dst.Floats { - dst.Floats[i] = uint64(src.fpstate._xmm[i].element[0]) << 0 - dst.Floats[i] |= uint64(src.fpstate._xmm[i].element[1]) << 32 - } -} diff --git a/src/runtime/export_debug_test.go b/src/runtime/export_debug_test.go index fe4c9045c107eb..2d8a1334093192 100644 --- a/src/runtime/export_debug_test.go +++ b/src/runtime/export_debug_test.go @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build amd64 && linux -// +build amd64,linux +//go:build (amd64 || arm64) && linux package runtime import ( "internal/abi" - "runtime/internal/sys" "unsafe" ) @@ -23,7 +21,7 @@ import ( // // On success, InjectDebugCall returns the panic value of fn or nil. // If fn did not panic, its results will be available in args. -func InjectDebugCall(gp *g, fn interface{}, regArgs *abi.RegArgs, stackArgs interface{}, tkill func(tid int) error, returnOnUnsafePoint bool) (interface{}, error) { +func InjectDebugCall(gp *g, fn any, regArgs *abi.RegArgs, stackArgs any, tkill func(tid int) error, returnOnUnsafePoint bool) (any, error) { if gp.lockedm == 0 { return nil, plainError("goroutine not locked to thread") } @@ -97,35 +95,30 @@ type debugCallHandler struct { regArgs *abi.RegArgs argp unsafe.Pointer argSize uintptr - panic interface{} + panic any handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool - err plainError - done note - savedRegs sigcontext - savedFP fpstate1 + err plainError + done note + sigCtxt sigContext } func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { - switch h.gp.atomicstatus { + // TODO(49370): This code is riddled with write barriers, but called from + // a signal handler. Add the go:nowritebarrierrec annotation and restructure + // this to avoid write barriers. + + switch h.gp.atomicstatus.Load() { case _Grunning: if getg().m != h.mp { println("trap on wrong M", getg().m, h.mp) return false } - // Push current PC on the stack. - rsp := ctxt.rsp() - sys.PtrSize - *(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip() - ctxt.set_rsp(rsp) - // Write the argument frame size. - *(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize - // Save current registers. - h.savedRegs = *ctxt.regs() - h.savedFP = *h.savedRegs.fpstate - h.savedRegs.fpstate = nil + // Save the signal context + h.saveSigContext(ctxt) // Set PC to debugCallV2. - ctxt.set_rip(uint64(funcPC(debugCallV2))) + ctxt.setsigpc(uint64(abi.FuncPCABIInternal(debugCallV2))) // Call injected. Switch to the debugCall protocol. testSigtrap = h.handleF case _Grunnable: @@ -142,62 +135,42 @@ func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool { } func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool { - // Sanity check. + // TODO(49370): This code is riddled with write barriers, but called from + // a signal handler. Add the go:nowritebarrierrec annotation and restructure + // this to avoid write barriers. + + // Double-check m. if getg().m != h.mp { println("trap on wrong M", getg().m, h.mp) return false } - f := findfunc(uintptr(ctxt.rip())) + f := findfunc(ctxt.sigpc()) if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) { println("trap in unknown function", funcname(f)) return false } - if *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) != 0xcc { - println("trap at non-INT3 instruction pc =", hex(ctxt.rip())) + if !sigctxtAtTrapInstruction(ctxt) { + println("trap at non-INT3 instruction pc =", hex(ctxt.sigpc())) return false } - switch status := ctxt.r12(); status { + switch status := sigctxtStatus(ctxt); status { case 0: // Frame is ready. Copy the arguments to the frame and to registers. - sp := ctxt.rsp() - memmove(unsafe.Pointer(uintptr(sp)), h.argp, h.argSize) - if h.regArgs != nil { - storeRegArgs(ctxt.regs(), h.regArgs) - } - // Push return PC. - sp -= sys.PtrSize - ctxt.set_rsp(sp) - *(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip() - // Set PC to call and context register. - ctxt.set_rip(uint64(h.fv.fn)) - ctxt.regs().rdx = uint64(uintptr(unsafe.Pointer(h.fv))) + // Call the debug function. + h.debugCallRun(ctxt) case 1: // Function returned. Copy frame and result registers back out. - sp := ctxt.rsp() - memmove(h.argp, unsafe.Pointer(uintptr(sp)), h.argSize) - if h.regArgs != nil { - loadRegArgs(h.regArgs, ctxt.regs()) - } + h.debugCallReturn(ctxt) case 2: // Function panicked. Copy panic out. - sp := ctxt.rsp() - memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize) + h.debugCallPanicOut(ctxt) case 8: // Call isn't safe. Get the reason. - sp := ctxt.rsp() - reason := *(*string)(unsafe.Pointer(uintptr(sp))) - h.err = plainError(reason) + h.debugCallUnsafe(ctxt) // Don't wake h.done. We need to transition to status 16 first. case 16: - // Restore all registers except RIP and RSP. - rip, rsp := ctxt.rip(), ctxt.rsp() - fp := ctxt.regs().fpstate - *ctxt.regs() = h.savedRegs - ctxt.regs().fpstate = fp - *fp = h.savedFP - ctxt.set_rip(rip) - ctxt.set_rsp(rsp) + h.restoreSigContext(ctxt) // Done notewakeup(&h.done) default: diff --git a/src/runtime/export_debuglog_test.go b/src/runtime/export_debuglog_test.go index 8cd943b43822e4..c9dfdcb3936dcf 100644 --- a/src/runtime/export_debuglog_test.go +++ b/src/runtime/export_debuglog_test.go @@ -14,22 +14,22 @@ const DebugLogStringLimit = debugLogStringLimit var Dlog = dlog -func (l *dlogger) End() { l.end() } -func (l *dlogger) B(x bool) *dlogger { return l.b(x) } -func (l *dlogger) I(x int) *dlogger { return l.i(x) } -func (l *dlogger) I16(x int16) *dlogger { return l.i16(x) } -func (l *dlogger) U64(x uint64) *dlogger { return l.u64(x) } -func (l *dlogger) Hex(x uint64) *dlogger { return l.hex(x) } -func (l *dlogger) P(x interface{}) *dlogger { return l.p(x) } -func (l *dlogger) S(x string) *dlogger { return l.s(x) } -func (l *dlogger) PC(x uintptr) *dlogger { return l.pc(x) } +func (l *dlogger) End() { l.end() } +func (l *dlogger) B(x bool) *dlogger { return l.b(x) } +func (l *dlogger) I(x int) *dlogger { return l.i(x) } +func (l *dlogger) I16(x int16) *dlogger { return l.i16(x) } +func (l *dlogger) U64(x uint64) *dlogger { return l.u64(x) } +func (l *dlogger) Hex(x uint64) *dlogger { return l.hex(x) } +func (l *dlogger) P(x any) *dlogger { return l.p(x) } +func (l *dlogger) S(x string) *dlogger { return l.s(x) } +func (l *dlogger) PC(x uintptr) *dlogger { return l.pc(x) } func DumpDebugLog() string { - g := getg() - g.writebuf = make([]byte, 0, 1<<20) + gp := getg() + gp.writebuf = make([]byte, 0, 1<<20) printDebugLog() - buf := g.writebuf - g.writebuf = nil + buf := gp.writebuf + gp.writebuf = nil return string(buf) } diff --git a/src/runtime/export_futex_test.go b/src/runtime/export_futex_test.go deleted file mode 100644 index 34c970d6d2d337..00000000000000 --- a/src/runtime/export_futex_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build dragonfly || freebsd || linux -// +build dragonfly freebsd linux - -package runtime - -var Futexwakeup = futexwakeup - -//go:nosplit -func Futexsleep(addr *uint32, val uint32, ns int64) { - // Temporarily disable preemption so that a preemption signal - // doesn't interrupt the system call. - poff := debug.asyncpreemptoff - debug.asyncpreemptoff = 1 - futexsleep(addr, val, ns) - debug.asyncpreemptoff = poff -} diff --git a/src/runtime/export_linux_test.go b/src/runtime/export_linux_test.go index b7c901f238ff29..dea94a934cef66 100644 --- a/src/runtime/export_linux_test.go +++ b/src/runtime/export_linux_test.go @@ -8,11 +8,16 @@ package runtime import "unsafe" +const SiginfoMaxSize = _si_max_size +const SigeventMaxSize = _sigev_max_size + var NewOSProc0 = newosproc0 var Mincore = mincore var Add = add type EpollEvent epollevent +type Siginfo siginfo +type Sigevent sigevent func Epollctl(epfd, op, fd int32, ev unsafe.Pointer) int32 { return epollctl(epfd, op, fd, (*epollevent)(ev)) diff --git a/src/runtime/export_mmap_test.go b/src/runtime/export_mmap_test.go index bf4a81589916cc..f73fcbde9bdf95 100644 --- a/src/runtime/export_mmap_test.go +++ b/src/runtime/export_mmap_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix // Export guts for testing. diff --git a/src/runtime/export_openbsd_test.go b/src/runtime/export_openbsd_test.go new file mode 100644 index 00000000000000..ef680dc2821207 --- /dev/null +++ b/src/runtime/export_openbsd_test.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build openbsd && !mips64 + +package runtime + +func Fcntl(fd, cmd, arg uintptr) (uintptr, uintptr) { + r := fcntl(int32(fd), int32(cmd), int32(arg)) + if r < 0 { + return ^uintptr(0), uintptr(-r) + } + return uintptr(r), 0 +} diff --git a/src/runtime/export_pipe2_test.go b/src/runtime/export_pipe2_test.go index 26d8b7d185948a..8d49009b43b280 100644 --- a/src/runtime/export_pipe2_test.go +++ b/src/runtime/export_pipe2_test.go @@ -3,14 +3,9 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build dragonfly freebsd linux netbsd openbsd solaris package runtime func Pipe() (r, w int32, errno int32) { - r, w, errno = pipe2(0) - if errno == _ENOSYS { - return pipe() - } - return r, w, errno + return pipe2(0) } diff --git a/src/runtime/export_pipe_test.go b/src/runtime/export_pipe_test.go index a0c6c0440d8d94..0583039982ebbc 100644 --- a/src/runtime/export_pipe_test.go +++ b/src/runtime/export_pipe_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin -// +build aix darwin package runtime diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index c8d01fbb1574de..c29d64a88507d3 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -7,6 +7,8 @@ package runtime import ( + "internal/goarch" + "internal/goos" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -27,12 +29,11 @@ var Exitsyscall = exitsyscall var LockedOSThread = lockedOSThread var Xadduintptr = atomic.Xadduintptr -var FuncPC = funcPC - var Fastlog2 = fastlog2 var Atoi = atoi var Atoi32 = atoi32 +var ParseByteCount = parseByteCount var Nanotime = nanotime var NetpollBreak = netpollBreak @@ -75,7 +76,7 @@ func Netpoll(delta int64) { }) } -func GCMask(x interface{}) (ret []byte) { +func GCMask(x any) (ret []byte) { systemstack(func() { ret = getgcmask(x) }) @@ -83,22 +84,23 @@ func GCMask(x interface{}) (ret []byte) { } func RunSchedLocalQueueTest() { - _p_ := new(p) - gs := make([]g, len(_p_.runq)) - for i := 0; i < len(_p_.runq); i++ { - if g, _ := runqget(_p_); g != nil { + pp := new(p) + gs := make([]g, len(pp.runq)) + Escape(gs) // Ensure gs doesn't move, since we use guintptrs + for i := 0; i < len(pp.runq); i++ { + if g, _ := runqget(pp); g != nil { throw("runq is not empty initially") } for j := 0; j < i; j++ { - runqput(_p_, &gs[i], false) + runqput(pp, &gs[i], false) } for j := 0; j < i; j++ { - if g, _ := runqget(_p_); g != &gs[i] { + if g, _ := runqget(pp); g != &gs[i] { print("bad element at iter ", i, "/", j, "\n") throw("bad element") } } - if g, _ := runqget(_p_); g != nil { + if g, _ := runqget(pp); g != nil { throw("runq is not empty afterwards") } } @@ -108,6 +110,7 @@ func RunSchedLocalQueueStealTest() { p1 := new(p) p2 := new(p) gs := make([]g, len(p1.runq)) + Escape(gs) // Ensure gs doesn't move, since we use guintptrs for i := 0; i < len(p1.runq); i++ { for j := 0; j < i; j++ { gs[j].sig = 0 @@ -147,40 +150,29 @@ func RunSchedLocalQueueStealTest() { } } -// Temporary to enable register ABI bringup. -// TODO(register args): convert back to local variables in RunSchedLocalQueueEmptyTest that -// get passed to the "go" stmts there. -var RunSchedLocalQueueEmptyState struct { - done chan bool - ready *uint32 - p *p -} - func RunSchedLocalQueueEmptyTest(iters int) { // Test that runq is not spuriously reported as empty. // Runq emptiness affects scheduling decisions and spurious emptiness // can lead to underutilization (both runnable Gs and idle Ps coexist // for arbitrary long time). done := make(chan bool, 1) - RunSchedLocalQueueEmptyState.done = done p := new(p) - RunSchedLocalQueueEmptyState.p = p gs := make([]g, 2) + Escape(gs) // Ensure gs doesn't move, since we use guintptrs ready := new(uint32) - RunSchedLocalQueueEmptyState.ready = ready for i := 0; i < iters; i++ { *ready = 0 next0 := (i & 1) == 0 next1 := (i & 2) == 0 runqput(p, &gs[0], next0) go func() { - for atomic.Xadd(RunSchedLocalQueueEmptyState.ready, 1); atomic.Load(RunSchedLocalQueueEmptyState.ready) != 2; { + for atomic.Xadd(ready, 1); atomic.Load(ready) != 2; { } - if runqempty(RunSchedLocalQueueEmptyState.p) { - //println("next:", next0, next1) + if runqempty(p) { + println("next:", next0, next1) throw("queue is empty") } - RunSchedLocalQueueEmptyState.done <- true + done <- true }() for atomic.Xadd(ready, 1); atomic.Load(ready) != 2; { } @@ -210,7 +202,7 @@ func MemclrBytes(b []byte) { memclrNoHeapPointers(s.array, uintptr(s.len)) } -var HashLoad = &hashLoad +const HashLoad = hashLoad // entry point for testing func GostringW(w []uint16) (s string) { @@ -228,11 +220,9 @@ var Write = write func Envs() []string { return envs } func SetEnvs(e []string) { envs = e } -var BigEndian = sys.BigEndian - // For benchmarking. -func BenchSetType(n int, x interface{}) { +func BenchSetType(n int, x any) { e := *efaceOf(&x) t := e._type var size uintptr @@ -259,7 +249,7 @@ func BenchSetType(n int, x interface{}) { }) } -const PtrSize = sys.PtrSize +const PtrSize = goarch.PtrSize var ForceGCPeriod = &forcegcperiod @@ -277,7 +267,7 @@ var ReadUnaligned64 = readUnaligned64 func CountPagesInUse() (pagesInUse, counted uintptr) { stopTheWorld("CountPagesInUse") - pagesInUse = uintptr(mheap_.pagesInUse) + pagesInUse = uintptr(mheap_.pagesInUse.Load()) for _, s := range mheap_.allspans { if s.state.get() == mSpanInUse { @@ -291,6 +281,7 @@ func CountPagesInUse() (pagesInUse, counted uintptr) { } func Fastrand() uint32 { return fastrand() } +func Fastrand64() uint64 { return fastrand64() } func Fastrandn(n uint32) uint32 { return fastrandn(n) } type ProfBuf profBuf @@ -321,9 +312,9 @@ func ReadMetricsSlow(memStats *MemStats, samplesp unsafe.Pointer, len, cap int) // Initialize the metrics beforehand because this could // allocate and skew the stats. - semacquire(&metricsSema) + metricsLock() initMetrics() - semrelease(&metricsSema) + metricsUnlock() systemstack(func() { // Read memstats first. It's going to flush @@ -469,17 +460,17 @@ func MapBucketsPointerIsNil(m map[int]int) bool { } func LockOSCounts() (external, internal uint32) { - g := getg() - if g.m.lockedExt+g.m.lockedInt == 0 { - if g.lockedm != 0 { + gp := getg() + if gp.m.lockedExt+gp.m.lockedInt == 0 { + if gp.lockedm != 0 { panic("lockedm on non-locked goroutine") } } else { - if g.lockedm == 0 { + if gp.lockedm == 0 { panic("nil lockedm on locked goroutine") } } - return g.m.lockedExt, g.m.lockedInt + return gp.m.lockedExt, gp.m.lockedInt } //go:noinline @@ -560,7 +551,7 @@ func MapTombstoneCheck(m map[int]int) { // We should have a series of filled and emptyOne cells, followed by // a series of emptyRest cells. h := *(**hmap)(unsafe.Pointer(&m)) - i := interface{}(m) + i := any(m) t := *(**maptype)(unsafe.Pointer(&i)) for x := 0; x < 1<= uint(len(t.counts)) { - return t.underflow, false + return t.underflow.Load(), false } - return t.counts[i], true + return t.counts[i].Load(), true } func (th *TimeHistogram) Record(duration int64) { (*timeHistogram)(th).record(duration) } +var TimeHistogramMetricsBuckets = timeHistogramMetricsBuckets + func SetIntArgRegs(a int) int { lock(&finlock) old := intArgRegs @@ -1216,10 +1264,7 @@ func SetIntArgRegs(a int) int { } func FinalizerGAsleep() bool { - lock(&finlock) - result := fingwait - unlock(&finlock) - return result + return fingStatus.Load()&fingWait != 0 } // For GCTestMoveStackOnNextCall, it's important not to introduce an @@ -1244,3 +1289,325 @@ func GCTestPointerClass(p unsafe.Pointer) string { } const Raceenabled = raceenabled + +const ( + GCBackgroundUtilization = gcBackgroundUtilization + GCGoalUtilization = gcGoalUtilization + DefaultHeapMinimum = defaultHeapMinimum + MemoryLimitHeapGoalHeadroom = memoryLimitHeapGoalHeadroom +) + +type GCController struct { + gcControllerState +} + +func NewGCController(gcPercent int, memoryLimit int64) *GCController { + // Force the controller to escape. We're going to + // do 64-bit atomics on it, and if it gets stack-allocated + // on a 32-bit architecture, it may get allocated unaligned + // space. + g := Escape(new(GCController)) + g.gcControllerState.test = true // Mark it as a test copy. + g.init(int32(gcPercent), memoryLimit) + return g +} + +func (c *GCController) StartCycle(stackSize, globalsSize uint64, scannableFrac float64, gomaxprocs int) { + trigger, _ := c.trigger() + if c.heapMarked > trigger { + trigger = c.heapMarked + } + c.maxStackScan.Store(stackSize) + c.globalsScan.Store(globalsSize) + c.heapLive.Store(trigger) + c.heapScan.Add(int64(float64(trigger-c.heapMarked) * scannableFrac)) + c.startCycle(0, gomaxprocs, gcTrigger{kind: gcTriggerHeap}) +} + +func (c *GCController) AssistWorkPerByte() float64 { + return c.assistWorkPerByte.Load() +} + +func (c *GCController) HeapGoal() uint64 { + return c.heapGoal() +} + +func (c *GCController) HeapLive() uint64 { + return c.heapLive.Load() +} + +func (c *GCController) HeapMarked() uint64 { + return c.heapMarked +} + +func (c *GCController) Triggered() uint64 { + return c.triggered +} + +type GCControllerReviseDelta struct { + HeapLive int64 + HeapScan int64 + HeapScanWork int64 + StackScanWork int64 + GlobalsScanWork int64 +} + +func (c *GCController) Revise(d GCControllerReviseDelta) { + c.heapLive.Add(d.HeapLive) + c.heapScan.Add(d.HeapScan) + c.heapScanWork.Add(d.HeapScanWork) + c.stackScanWork.Add(d.StackScanWork) + c.globalsScanWork.Add(d.GlobalsScanWork) + c.revise() +} + +func (c *GCController) EndCycle(bytesMarked uint64, assistTime, elapsed int64, gomaxprocs int) { + c.assistTime.Store(assistTime) + c.endCycle(elapsed, gomaxprocs, false) + c.resetLive(bytesMarked) + c.commit(false) +} + +func (c *GCController) AddIdleMarkWorker() bool { + return c.addIdleMarkWorker() +} + +func (c *GCController) NeedIdleMarkWorker() bool { + return c.needIdleMarkWorker() +} + +func (c *GCController) RemoveIdleMarkWorker() { + c.removeIdleMarkWorker() +} + +func (c *GCController) SetMaxIdleMarkWorkers(max int32) { + c.setMaxIdleMarkWorkers(max) +} + +var alwaysFalse bool +var escapeSink any + +func Escape[T any](x T) T { + if alwaysFalse { + escapeSink = x + } + return x +} + +// Acquirem blocks preemption. +func Acquirem() { + acquirem() +} + +func Releasem() { + releasem(getg().m) +} + +var Timediv = timediv + +type PIController struct { + piController +} + +func NewPIController(kp, ti, tt, min, max float64) *PIController { + return &PIController{piController{ + kp: kp, + ti: ti, + tt: tt, + min: min, + max: max, + }} +} + +func (c *PIController) Next(input, setpoint, period float64) (float64, bool) { + return c.piController.next(input, setpoint, period) +} + +const ( + CapacityPerProc = capacityPerProc + GCCPULimiterUpdatePeriod = gcCPULimiterUpdatePeriod +) + +type GCCPULimiter struct { + limiter gcCPULimiterState +} + +func NewGCCPULimiter(now int64, gomaxprocs int32) *GCCPULimiter { + // Force the controller to escape. We're going to + // do 64-bit atomics on it, and if it gets stack-allocated + // on a 32-bit architecture, it may get allocated unaligned + // space. + l := Escape(new(GCCPULimiter)) + l.limiter.test = true + l.limiter.resetCapacity(now, gomaxprocs) + return l +} + +func (l *GCCPULimiter) Fill() uint64 { + return l.limiter.bucket.fill +} + +func (l *GCCPULimiter) Capacity() uint64 { + return l.limiter.bucket.capacity +} + +func (l *GCCPULimiter) Overflow() uint64 { + return l.limiter.overflow +} + +func (l *GCCPULimiter) Limiting() bool { + return l.limiter.limiting() +} + +func (l *GCCPULimiter) NeedUpdate(now int64) bool { + return l.limiter.needUpdate(now) +} + +func (l *GCCPULimiter) StartGCTransition(enableGC bool, now int64) { + l.limiter.startGCTransition(enableGC, now) +} + +func (l *GCCPULimiter) FinishGCTransition(now int64) { + l.limiter.finishGCTransition(now) +} + +func (l *GCCPULimiter) Update(now int64) { + l.limiter.update(now) +} + +func (l *GCCPULimiter) AddAssistTime(t int64) { + l.limiter.addAssistTime(t) +} + +func (l *GCCPULimiter) ResetCapacity(now int64, nprocs int32) { + l.limiter.resetCapacity(now, nprocs) +} + +const ScavengePercent = scavengePercent + +type Scavenger struct { + Sleep func(int64) int64 + Scavenge func(uintptr) (uintptr, int64) + ShouldStop func() bool + GoMaxProcs func() int32 + + released atomic.Uintptr + scavenger scavengerState + stop chan<- struct{} + done <-chan struct{} +} + +func (s *Scavenger) Start() { + if s.Sleep == nil || s.Scavenge == nil || s.ShouldStop == nil || s.GoMaxProcs == nil { + panic("must populate all stubs") + } + + // Install hooks. + s.scavenger.sleepStub = s.Sleep + s.scavenger.scavenge = s.Scavenge + s.scavenger.shouldStop = s.ShouldStop + s.scavenger.gomaxprocs = s.GoMaxProcs + + // Start up scavenger goroutine, and wait for it to be ready. + stop := make(chan struct{}) + s.stop = stop + done := make(chan struct{}) + s.done = done + go func() { + // This should match bgscavenge, loosely. + s.scavenger.init() + s.scavenger.park() + for { + select { + case <-stop: + close(done) + return + default: + } + released, workTime := s.scavenger.run() + if released == 0 { + s.scavenger.park() + continue + } + s.released.Add(released) + s.scavenger.sleep(workTime) + } + }() + if !s.BlockUntilParked(1e9 /* 1 second */) { + panic("timed out waiting for scavenger to get ready") + } +} + +// BlockUntilParked blocks until the scavenger parks, or until +// timeout is exceeded. Returns true if the scavenger parked. +// +// Note that in testing, parked means something slightly different. +// In anger, the scavenger parks to sleep, too, but in testing, +// it only parks when it actually has no work to do. +func (s *Scavenger) BlockUntilParked(timeout int64) bool { + // Just spin, waiting for it to park. + // + // The actual parking process is racy with respect to + // wakeups, which is fine, but for testing we need something + // a bit more robust. + start := nanotime() + for nanotime()-start < timeout { + lock(&s.scavenger.lock) + parked := s.scavenger.parked + unlock(&s.scavenger.lock) + if parked { + return true + } + Gosched() + } + return false +} + +// Released returns how many bytes the scavenger released. +func (s *Scavenger) Released() uintptr { + return s.released.Load() +} + +// Wake wakes up a parked scavenger to keep running. +func (s *Scavenger) Wake() { + s.scavenger.wake() +} + +// Stop cleans up the scavenger's resources. The scavenger +// must be parked for this to work. +func (s *Scavenger) Stop() { + lock(&s.scavenger.lock) + parked := s.scavenger.parked + unlock(&s.scavenger.lock) + if !parked { + panic("tried to clean up scavenger that is not parked") + } + close(s.stop) + s.Wake() + <-s.done +} + +type ScavengeIndex struct { + i scavengeIndex +} + +func NewScavengeIndex(min, max ChunkIdx) *ScavengeIndex { + s := new(ScavengeIndex) + s.i.chunks = make([]atomic.Uint8, uintptr(1<#-># MB, # MB goal, # P + gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # MB stacks, #MB globals, # P where the fields are as follows: - gc # the GC number, incremented at each GC - @#s time in seconds since program start - #% percentage of time spent in GC since program start - #+...+# wall-clock/CPU times for the phases of the GC - #->#-># MB heap size at GC start, at GC end, and live heap - # MB goal goal heap size - # P number of processors used + gc # the GC number, incremented at each GC + @#s time in seconds since program start + #% percentage of time spent in GC since program start + #+...+# wall-clock/CPU times for the phases of the GC + #->#-># MB heap size at GC start, at GC end, and live heap + # MB goal goal heap size + # MB stacks estimated scannable stack size + # MB globals scannable global size + # P number of processors used The phases are stop-the-world (STW) sweep termination, concurrent mark and scan, and STW mark termination. The CPU times for mark/scan are broken down in to assist time (GC performed in @@ -78,6 +97,11 @@ It is a comma-separated list of name=val pairs setting these named variables: If the line ends with "(forced)", this GC was forced by a runtime.GC() call. + harddecommit: setting harddecommit=1 causes memory that is returned to the OS to + also have protections removed on it. This is the only mode of operation on Windows, + but is helpful in debugging scavenger-related issues on other platforms. Currently, + only supported on Linux. + inittrace: setting inittrace=1 causes the runtime to emit a single line to standard error for each package with init work, summarizing the execution time and memory allocation. No information is printed for inits executed as part of plugin loading @@ -94,7 +118,10 @@ It is a comma-separated list of name=val pairs setting these named variables: madvdontneed: setting madvdontneed=0 will use MADV_FREE instead of MADV_DONTNEED on Linux when returning memory to the kernel. This is more efficient, but means RSS numbers will - drop only when the OS is under memory pressure. + drop only when the OS is under memory pressure. On the BSDs and + Illumos/Solaris, setting madvdontneed=1 will use MADV_DONTNEED instead + of MADV_FREE. This is less efficient, but causes RSS numbers to drop + more quickly. memprofilerate: setting memprofilerate=X will update the value of runtime.MemProfileRate. When set to 0 memory profiling is disabled. Refer to the description of @@ -115,9 +142,8 @@ It is a comma-separated list of name=val pairs setting these named variables: scavenger as well as the total amount of memory returned to the operating system and an estimate of physical memory utilization. The format of this line is subject to change, but currently it is: - scav # # KiB work, # KiB total, #% util + scav # KiB work, # KiB total, #% util where the fields are as follows: - scav # the scavenge cycle number # KiB work the amount of memory returned to the OS since the last line # KiB total the total amount of memory returned to the OS #% util the fraction of all unscavenged memory which is in-use @@ -144,7 +170,7 @@ It is a comma-separated list of name=val pairs setting these named variables: because it also disables the conservative stack scanning used for asynchronously preempted goroutines. -The net, net/http, and crypto/tls packages also refer to debugging variables in GODEBUG. +The net and net/http packages also refer to debugging variables in GODEBUG. See the documentation for those packages for details. The GOMAXPROCS variable limits the number of operating system threads that @@ -165,9 +191,9 @@ or the failure is internal to the run-time. GOTRACEBACK=none omits the goroutine stack traces entirely. GOTRACEBACK=single (the default) behaves as described above. GOTRACEBACK=all adds stack traces for all user-created goroutines. -GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions +GOTRACEBACK=system is like “all” but adds stack frames for run-time functions and shows goroutines created internally by the run-time. -GOTRACEBACK=crash is like ``system'' but crashes in an operating system-specific +GOTRACEBACK=crash is like “system” but crashes in an operating system-specific manner instead of exiting. For example, on Unix systems, the crash raises SIGABRT to trigger a core dump. For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for @@ -186,7 +212,10 @@ of the run-time system. */ package runtime -import "runtime/internal/sys" +import ( + "internal/goarch" + "internal/goos" +) // Caller reports file and line number information about function invocations on // the calling goroutine's stack. The argument skip is the number of stack frames @@ -260,8 +289,8 @@ func Version() string { // GOOS is the running program's operating system target: // one of darwin, freebsd, linux, and so on. // To view possible combinations of GOOS and GOARCH, run "go tool dist list". -const GOOS string = sys.GOOS +const GOOS string = goos.GOOS // GOARCH is the running program's architecture target: // one of 386, amd64, arm, s390x, and so on. -const GOARCH string = sys.GOARCH +const GOARCH string = goarch.GOARCH diff --git a/src/runtime/float.go b/src/runtime/float.go index 459e58dd7ef0af..c80c8b7abfb0c8 100644 --- a/src/runtime/float.go +++ b/src/runtime/float.go @@ -8,7 +8,7 @@ import "unsafe" var inf = float64frombits(0x7FF0000000000000) -// isNaN reports whether f is an IEEE 754 ``not-a-number'' value. +// isNaN reports whether f is an IEEE 754 “not-a-number” value. func isNaN(f float64) (is bool) { // IEEE 754 says that only NaNs satisfy f != f. return f != f @@ -27,6 +27,7 @@ func isInf(f float64) bool { // Abs returns the absolute value of x. // // Special cases are: +// // Abs(±Inf) = +Inf // Abs(NaN) = NaN func abs(x float64) float64 { diff --git a/src/runtime/float_test.go b/src/runtime/float_test.go new file mode 100644 index 00000000000000..b2aa43da597085 --- /dev/null +++ b/src/runtime/float_test.go @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. 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 ( + "testing" +) + +func TestIssue48807(t *testing.T) { + for _, i := range []uint64{ + 0x8234508000000001, // from issue48807 + 1<<56 + 1<<32 + 1, + } { + got := float32(i) + dontwant := float32(float64(i)) + if got == dontwant { + // The test cases above should be uint64s such that + // this equality doesn't hold. These examples trigger + // the case where using an intermediate float64 doesn't work. + t.Errorf("direct float32 conversion doesn't work: arg=%x got=%x dontwant=%x", i, got, dontwant) + } + } +} diff --git a/src/runtime/funcdata.h b/src/runtime/funcdata.h index 1002b181e4cf62..2e2bb30446ccb5 100644 --- a/src/runtime/funcdata.h +++ b/src/runtime/funcdata.h @@ -11,6 +11,7 @@ #define PCDATA_UnsafePoint 0 #define PCDATA_StackMapIndex 1 #define PCDATA_InlTreeIndex 2 +#define PCDATA_ArgLiveIndex 3 #define FUNCDATA_ArgsPointerMaps 0 /* garbage collector blocks */ #define FUNCDATA_LocalsPointerMaps 1 @@ -18,6 +19,8 @@ #define FUNCDATA_InlTree 3 #define FUNCDATA_OpenCodedDeferInfo 4 /* info for func with open-coded defers */ #define FUNCDATA_ArgInfo 5 +#define FUNCDATA_ArgLiveInfo 6 +#define FUNCDATA_WrapInfo 7 // Pseudo-assembly statements. @@ -44,7 +47,7 @@ // NO_LOCAL_POINTERS indicates that the assembly function stores // no pointers to heap objects in its local stack variables. -#define NO_LOCAL_POINTERS FUNCDATA $FUNCDATA_LocalsPointerMaps, runtime·no_pointers_stackmap(SB) +#define NO_LOCAL_POINTERS FUNCDATA $FUNCDATA_LocalsPointerMaps, no_pointers_stackmap(SB) // ArgsSizeUnknown is set in Func.argsize to mark all functions // whose argument size is unknown (C vararg functions, and diff --git a/src/runtime/futex_test.go b/src/runtime/futex_test.go deleted file mode 100644 index 10a735327a7357..00000000000000 --- a/src/runtime/futex_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Futex is only available on DragonFly BSD, FreeBSD and Linux. -// The race detector emits calls to split stack functions so it breaks -// the test. - -//go:build (dragonfly || freebsd || linux) && !race -// +build dragonfly freebsd linux -// +build !race - -package runtime_test - -import ( - "runtime" - "sync" - "sync/atomic" - "testing" - "time" -) - -type futexsleepTest struct { - mtx uint32 - ns int64 - msg string - ch chan *futexsleepTest -} - -var futexsleepTests = []futexsleepTest{ - beforeY2038: {mtx: 0, ns: 86400 * 1e9, msg: "before the year 2038"}, - afterY2038: {mtx: 0, ns: (1<<31 + 100) * 1e9, msg: "after the year 2038"}, -} - -const ( - beforeY2038 = iota - afterY2038 -) - -func TestFutexsleep(t *testing.T) { - if runtime.GOMAXPROCS(0) > 1 { - // futexsleep doesn't handle EINTR or other signals, - // so spurious wakeups may happen. - t.Skip("skipping; GOMAXPROCS>1") - } - - start := time.Now() - var wg sync.WaitGroup - for i := range futexsleepTests { - tt := &futexsleepTests[i] - tt.mtx = 0 - tt.ch = make(chan *futexsleepTest, 1) - wg.Add(1) - go func(tt *futexsleepTest) { - runtime.Entersyscall() - runtime.Futexsleep(&tt.mtx, 0, tt.ns) - runtime.Exitsyscall() - tt.ch <- tt - wg.Done() - }(tt) - } -loop: - for { - select { - case tt := <-futexsleepTests[beforeY2038].ch: - t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start)) - break loop - case tt := <-futexsleepTests[afterY2038].ch: - // Looks like FreeBSD 10 kernel has changed - // the semantics of timedwait on userspace - // mutex to make broken stuff look broken. - switch { - case runtime.GOOS == "freebsd" && runtime.GOARCH == "386": - t.Log("freebsd/386 may not work correctly after the year 2038, see golang.org/issue/7194") - default: - t.Errorf("futexsleep test %q finished early after %s", tt.msg, time.Since(start)) - break loop - } - case <-time.After(time.Second): - break loop - } - } - for i := range futexsleepTests { - tt := &futexsleepTests[i] - atomic.StoreUint32(&tt.mtx, 1) - runtime.Futexwakeup(&tt.mtx, 1) - } - wg.Wait() -} diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index 5e7c6c574fe193..122818fbfeba81 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -21,6 +21,7 @@ import ( ) func TestGcSys(t *testing.T) { + t.Skip("skipping known-flaky test; golang.org/issue/37331") if os.Getenv("GOGC") == "off" { t.Skip("skipping test; GOGC=off in environment") } @@ -135,7 +136,7 @@ func TestGcLastTime(t *testing.T) { } } -var hugeSink interface{} +var hugeSink any func TestHugeGCInfo(t *testing.T) { // The test ensures that compiler can chew these huge types even on weakest machines. @@ -195,7 +196,10 @@ func TestPeriodicGC(t *testing.T) { func TestGcZombieReporting(t *testing.T) { // This test is somewhat sensitive to how the allocator works. - got := runTestProg(t, "testprog", "GCZombie") + // Pointers in zombies slice may cross-span, thus we + // add invalidptr=0 for avoiding the badPointer check. + // See issue https://golang.org/issues/49613/ + got := runTestProg(t, "testprog", "GCZombie", "GODEBUG=invalidptr=0") want := "found pointer to free object" if !strings.Contains(got, want) { t.Fatalf("expected %q in output, but got %q", want, got) @@ -280,7 +284,7 @@ func TestGCTestIsReachable(t *testing.T) { runtime.KeepAlive(half) } -var pointerClassSink *int +var pointerClassBSS *int var pointerClassData = 42 func TestGCTestPointerClass(t *testing.T) { @@ -296,10 +300,9 @@ func TestGCTestPointerClass(t *testing.T) { } var onStack int var notOnStack int - pointerClassSink = ¬OnStack check(unsafe.Pointer(&onStack), "stack") - check(unsafe.Pointer(¬OnStack), "heap") - check(unsafe.Pointer(&pointerClassSink), "bss") + check(unsafe.Pointer(runtime.Escape(¬OnStack)), "heap") + check(unsafe.Pointer(&pointerClassBSS), "bss") check(unsafe.Pointer(&pointerClassData), "data") check(nil, "other") } @@ -453,11 +456,11 @@ func BenchmarkSetTypeNode1024Slice(b *testing.B) { benchSetType(b, make([]Node1024, 32)) } -func benchSetType(b *testing.B, x interface{}) { +func benchSetType(b *testing.B, x any) { v := reflect.ValueOf(x) t := v.Type() switch t.Kind() { - case reflect.Ptr: + case reflect.Pointer: b.SetBytes(int64(t.Elem().Size())) case reflect.Slice: b.SetBytes(int64(t.Elem().Size()) * int64(v.Len())) @@ -519,7 +522,7 @@ func TestPrintGC(t *testing.T) { close(done) } -func testTypeSwitch(x interface{}) error { +func testTypeSwitch(x any) error { switch y := x.(type) { case nil: // ok @@ -529,14 +532,14 @@ func testTypeSwitch(x interface{}) error { return nil } -func testAssert(x interface{}) error { +func testAssert(x any) error { if y, ok := x.(error); ok { return y } return nil } -func testAssertVar(x interface{}) error { +func testAssertVar(x any) error { var y, ok = x.(error) if ok { return y @@ -547,7 +550,7 @@ func testAssertVar(x interface{}) error { var a bool //go:noinline -func testIfaceEqual(x interface{}) { +func testIfaceEqual(x any) { if x == "abc" { a = true } @@ -610,14 +613,13 @@ func BenchmarkReadMemStats(b *testing.B) { for i := range x { x[i] = new([1024]byte) } - hugeSink = x b.ResetTimer() for i := 0; i < b.N; i++ { runtime.ReadMemStats(&ms) } - hugeSink = nil + runtime.KeepAlive(x) } func applyGCLoad(b *testing.B) func() { @@ -902,3 +904,31 @@ func countpwg(n *int, ready *sync.WaitGroup, teardown chan bool) { *n-- countpwg(n, ready, teardown) } + +func TestMemoryLimit(t *testing.T) { + if testing.Short() { + t.Skip("stress test that takes time to run") + } + if runtime.NumCPU() < 4 { + t.Skip("want at least 4 CPUs for this test") + } + got := runTestProg(t, "testprog", "GCMemoryLimit") + want := "OK\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} + +func TestMemoryLimitNoGCPercent(t *testing.T) { + if testing.Short() { + t.Skip("stress test that takes time to run") + } + if runtime.NumCPU() < 4 { + t.Skip("want at least 4 CPUs for this test") + } + got := runTestProg(t, "testprog", "GCMemoryLimitNoGCPercent") + want := "OK\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } +} diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index 0808b416f0a104..787160dc2779c8 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -66,7 +66,7 @@ func TestGCInfo(t *testing.T) { runtime.KeepAlive(x) } { - var x interface{} + var x any verifyGCInfo(t, "stack eface", &x, infoEface) runtime.KeepAlive(x) } @@ -77,19 +77,19 @@ func TestGCInfo(t *testing.T) { } for i := 0; i < 10; i++ { - verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(infoPtr)) - verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10)) - verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr)) - verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4)) - verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), trimDead(infoPtrScalar)) - verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), trimDead(infoBigStruct())) - verifyGCInfo(t, "heap string", escape(new(string)), trimDead(infoString)) - verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface)) - verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface)) + verifyGCInfo(t, "heap Ptr", runtime.Escape(new(Ptr)), trimDead(infoPtr)) + verifyGCInfo(t, "heap PtrSlice", runtime.Escape(&make([]*byte, 10)[0]), trimDead(infoPtr10)) + verifyGCInfo(t, "heap ScalarPtr", runtime.Escape(new(ScalarPtr)), trimDead(infoScalarPtr)) + verifyGCInfo(t, "heap ScalarPtrSlice", runtime.Escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4)) + verifyGCInfo(t, "heap PtrScalar", runtime.Escape(new(PtrScalar)), trimDead(infoPtrScalar)) + verifyGCInfo(t, "heap BigStruct", runtime.Escape(new(BigStruct)), trimDead(infoBigStruct())) + verifyGCInfo(t, "heap string", runtime.Escape(new(string)), trimDead(infoString)) + verifyGCInfo(t, "heap eface", runtime.Escape(new(any)), trimDead(infoEface)) + verifyGCInfo(t, "heap iface", runtime.Escape(new(Iface)), trimDead(infoIface)) } } -func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) { +func verifyGCInfo(t *testing.T, name string, p any, mask0 []byte) { mask := runtime.GCMask(p) if !bytes.Equal(mask, mask0) { t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask) @@ -104,13 +104,6 @@ func trimDead(mask []byte) []byte { return mask } -var gcinfoSink interface{} - -func escape(p interface{}) interface{} { - gcinfoSink = p - return p -} - var infoPtr = []byte{typePointer} type Ptr struct { @@ -164,7 +157,7 @@ func infoBigStruct() []byte { typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64 typePointer, typeScalar, // i string } - case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "wasm": + case "arm64", "amd64", "loong64", "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "wasm": return []byte{ typePointer, // q *int typeScalar, typeScalar, typeScalar, // w byte; e [17]byte @@ -194,18 +187,18 @@ var ( bssBigStruct BigStruct bssString string bssSlice []string - bssEface interface{} + bssEface any bssIface Iface // DATA - dataPtr = Ptr{new(byte)} - dataScalarPtr = ScalarPtr{q: 1} - dataPtrScalar = PtrScalar{w: 1} - dataBigStruct = BigStruct{w: 1} - dataString = "foo" - dataSlice = []string{"foo"} - dataEface interface{} = 42 - dataIface Iface = IfaceImpl(42) + dataPtr = Ptr{new(byte)} + dataScalarPtr = ScalarPtr{q: 1} + dataPtrScalar = PtrScalar{w: 1} + dataBigStruct = BigStruct{w: 1} + dataString = "foo" + dataSlice = []string{"foo"} + dataEface any = 42 + dataIface Iface = IfaceImpl(42) infoString = []byte{typePointer, typeScalar} infoSlice = []byte{typePointer, typeScalar, typeScalar} diff --git a/src/runtime/hash32.go b/src/runtime/hash32.go index 7c22c76b875c37..0616c7dd050751 100644 --- a/src/runtime/hash32.go +++ b/src/runtime/hash32.go @@ -6,7 +6,6 @@ // wyhash: https://github.com/wangyi-fudan/wyhash/blob/ceb019b530e2c1c14d70b79bfa2bc49de7d95bc1/Modern%20Non-Cryptographic%20Hash%20Function%20and%20Pseudorandom%20Number%20Generator.pdf //go:build 386 || arm || mips || mipsle -// +build 386 arm mips mipsle package runtime diff --git a/src/runtime/hash64.go b/src/runtime/hash64.go index 5f7d00bf7f087a..2864a4b963ddde 100644 --- a/src/runtime/hash64.go +++ b/src/runtime/hash64.go @@ -5,8 +5,7 @@ // Hashing algorithm inspired by // wyhash: https://github.com/wangyi-fudan/wyhash -//go:build amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm -// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm +//go:build amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm package runtime diff --git a/src/runtime/hash_test.go b/src/runtime/hash_test.go index 7048874a71fe3a..e72600641f2b9e 100644 --- a/src/runtime/hash_test.go +++ b/src/runtime/hash_test.go @@ -382,7 +382,7 @@ func (k *Int64Key) name() string { } type EfaceKey struct { - i interface{} + i any } func (k *EfaceKey) clear() { @@ -525,6 +525,13 @@ func windowed(t *testing.T, k Key) { if GOARCH == "wasm" { t.Skip("Too slow on wasm") } + if PtrSize == 4 { + // This test tends to be flaky on 32-bit systems. + // There's not enough bits in the hash output, so we + // expect a nontrivial number of collisions, and it is + // often quite a bit higher than expected. See issue 43130. + t.Skip("Flaky on 32-bit systems") + } if testing.Short() { t.Skip("Skipping in short mode") } diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 934e55f4952306..0268e25595295f 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -12,7 +12,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -247,7 +247,7 @@ func dumpbv(cbv *bitvector, offset uintptr) { for i := uintptr(0); i < uintptr(cbv.n); i++ { if cbv.ptrbit(i) == 1 { dumpint(fieldKindPtr) - dumpint(uint64(offset + i*sys.PtrSize)) + dumpint(uint64(offset + i*goarch.PtrSize)) } } } @@ -259,7 +259,7 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool { // Figure out what we can about our stack map pc := s.pc pcdata := int32(-1) // Use the entry map at function entry - if pc != f.entry { + if pc != f.entry() { pc-- pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, pc, nil) } @@ -284,7 +284,7 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool { dumpint(uint64(child.depth)) // # of frames deep on the stack dumpint(uint64(uintptr(unsafe.Pointer(child.sp)))) // sp of child, or 0 if bottom of stack dumpmemrange(unsafe.Pointer(s.sp), s.fp-s.sp) // frame contents - dumpint(uint64(f.entry)) + dumpint(uint64(f.entry())) dumpint(uint64(s.pc)) dumpint(uint64(s.continpc)) name := funcname(f) @@ -298,7 +298,7 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool { dumpbv(&child.args, child.argoff) } else { // conservative - everything might be a pointer - for off := child.argoff; off < child.argoff+child.arglen; off += sys.PtrSize { + for off := child.argoff; off < child.argoff+child.arglen; off += goarch.PtrSize { dumpint(fieldKindPtr) dumpint(uint64(off)) } @@ -307,27 +307,27 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool { // Dump fields in the local vars section if stkmap == nil { // No locals information, dump everything. - for off := child.arglen; off < s.varp-s.sp; off += sys.PtrSize { + for off := child.arglen; off < s.varp-s.sp; off += goarch.PtrSize { dumpint(fieldKindPtr) dumpint(uint64(off)) } } else if stkmap.n < 0 { // Locals size information, dump just the locals. size := uintptr(-stkmap.n) - for off := s.varp - size - s.sp; off < s.varp-s.sp; off += sys.PtrSize { + for off := s.varp - size - s.sp; off < s.varp-s.sp; off += goarch.PtrSize { dumpint(fieldKindPtr) dumpint(uint64(off)) } } else if stkmap.n > 0 { // Locals bitmap information, scan just the pointers in // locals. - dumpbv(&bv, s.varp-uintptr(bv.n)*sys.PtrSize-s.sp) + dumpbv(&bv, s.varp-uintptr(bv.n)*goarch.PtrSize-s.sp) } dumpint(fieldKindEol) // Record arg info for parent. child.argoff = s.argp - s.fp - child.arglen = s.arglen + child.arglen = s.argBytes() child.sp = (*uint8)(unsafe.Pointer(s.sp)) child.depth++ stkmap = (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) @@ -354,7 +354,7 @@ func dumpgoroutine(gp *g) { dumpint(tagGoroutine) dumpint(uint64(uintptr(unsafe.Pointer(gp)))) dumpint(uint64(sp)) - dumpint(uint64(gp.goid)) + dumpint(gp.goid) dumpint(uint64(gp.gopc)) dumpint(uint64(readgstatus(gp))) dumpbool(isSystemGoroutine(gp, false)) @@ -381,12 +381,13 @@ func dumpgoroutine(gp *g) { dumpint(uint64(uintptr(unsafe.Pointer(gp)))) dumpint(uint64(d.sp)) dumpint(uint64(d.pc)) - dumpint(uint64(uintptr(unsafe.Pointer(d.fn)))) + fn := *(**funcval)(unsafe.Pointer(&d.fn)) + dumpint(uint64(uintptr(unsafe.Pointer(fn)))) if d.fn == nil { // d.fn can be nil for open-coded defers dumpint(uint64(0)) } else { - dumpint(uint64(uintptr(unsafe.Pointer(d.fn.fn)))) + dumpint(uint64(uintptr(unsafe.Pointer(fn.fn)))) } dumpint(uint64(uintptr(unsafe.Pointer(d.link)))) } @@ -510,7 +511,7 @@ func dumpparams() { } else { dumpbool(true) // big-endian ptrs } - dumpint(sys.PtrSize) + dumpint(goarch.PtrSize) var arenaStart, arenaEnd uintptr for i1 := range mheap_.arenas { if mheap_.arenas[i1] == nil { @@ -531,7 +532,7 @@ func dumpparams() { } dumpint(uint64(arenaStart)) dumpint(uint64(arenaEnd)) - dumpstr(sys.GOARCH) + dumpstr(goarch.GOARCH) dumpstr(buildVersion) dumpint(uint64(ncpu)) } @@ -630,7 +631,7 @@ func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs, dumpint(0) } else { dumpstr(funcname(f)) - if i > 0 && pc > f.entry { + if i > 0 && pc > f.entry() { pc-- } file, line := funcline(f, pc) @@ -692,14 +693,9 @@ func mdump(m *MemStats) { func writeheapdump_m(fd uintptr, m *MemStats) { assertWorldStopped() - _g_ := getg() - casgstatus(_g_.m.curg, _Grunning, _Gwaiting) - _g_.waitreason = waitReasonDumpingHeap - - // Update stats so we can dump them. - // As a side effect, flushes all the mcaches so the mspan.freelist - // lists contain all the free objects. - updatememstats() + gp := getg() + casgstatus(gp.m.curg, _Grunning, _Gwaiting) + gp.waitreason = waitReasonDumpingHeap // Set dump file. dumpfd = fd @@ -714,7 +710,7 @@ func writeheapdump_m(fd uintptr, m *MemStats) { tmpbuf = nil } - casgstatus(_g_.m.curg, _Gwaiting, _Grunning) + casgstatus(gp.m.curg, _Gwaiting, _Grunning) } // dumpint() the kind & offset of each field in an object. @@ -725,7 +721,7 @@ func dumpfields(bv bitvector) { func makeheapobjbv(p uintptr, size uintptr) bitvector { // Extend the temp buffer if necessary. - nptr := size / sys.PtrSize + nptr := size / goarch.PtrSize if uintptr(len(tmpbuf)) < nptr/8+1 { if tmpbuf != nil { sysFree(unsafe.Pointer(&tmpbuf[0]), uintptr(len(tmpbuf)), &memstats.other_sys) @@ -741,16 +737,16 @@ func makeheapobjbv(p uintptr, size uintptr) bitvector { for i := uintptr(0); i < nptr/8+1; i++ { tmpbuf[i] = 0 } - i := uintptr(0) - hbits := heapBitsForAddr(p) - for ; i < nptr; i++ { - if !hbits.morePointers() { - break // end of object - } - if hbits.isPointer() { - tmpbuf[i/8] |= 1 << (i % 8) + + hbits := heapBitsForAddr(p, size) + for { + var addr uintptr + hbits, addr = hbits.next() + if addr == 0 { + break } - hbits = hbits.next() + i := (addr - p) / goarch.PtrSize + tmpbuf[i/8] |= 1 << (i % 8) } - return bitvector{int32(i), &tmpbuf[0]} + return bitvector{int32(nptr), &tmpbuf[0]} } diff --git a/src/runtime/histogram.go b/src/runtime/histogram.go index 0cccbcca16252b..d2e6367c84b637 100644 --- a/src/runtime/histogram.go +++ b/src/runtime/histogram.go @@ -47,7 +47,7 @@ const ( // │ └---- Next 4 bits -> sub-bucket 1 // └------- Bit 5 set -> super-bucket 2 // - // Following this pattern, bucket 45 will have the bit 48 set. We don't + // Following this pattern, super-bucket 44 will have the bit 47 set. We don't // have any buckets for higher values, so the highest sub-bucket will // contain values of 2^48-1 nanoseconds or approx. 3 days. This range is // more than enough to handle durations produced by the runtime. @@ -66,28 +66,27 @@ const ( // It is an HDR histogram with exponentially-distributed // buckets and linearly distributed sub-buckets. // -// Counts in the histogram are updated atomically, so it is safe -// for concurrent use. It is also safe to read all the values -// atomically. +// The histogram is safe for concurrent reads and writes. type timeHistogram struct { - counts [timeHistNumSuperBuckets * timeHistNumSubBuckets]uint64 + counts [timeHistNumSuperBuckets * timeHistNumSubBuckets]atomic.Uint64 // underflow counts all the times we got a negative duration // sample. Because of how time works on some platforms, it's // possible to measure negative durations. We could ignore them, // but we record them anyway because it's better to have some // signal that it's happening than just missing samples. - underflow uint64 + underflow atomic.Uint64 } // record adds the given duration to the distribution. // // Disallow preemptions and stack growths because this function // may run in sensitive locations. +// //go:nosplit func (h *timeHistogram) record(duration int64) { if duration < 0 { - atomic.Xadd64(&h.underflow, 1) + h.underflow.Add(1) return } // The index of the exponential bucket is just the index @@ -115,7 +114,7 @@ func (h *timeHistogram) record(duration int64) { } else { subBucket = uint(duration) } - atomic.Xadd64(&h.counts[superBucket*timeHistNumSubBuckets+subBucket], 1) + h.counts[superBucket*timeHistNumSubBuckets+subBucket].Add(1) } const ( @@ -139,36 +138,30 @@ func float64NegInf() float64 { func timeHistogramMetricsBuckets() []float64 { b := make([]float64, timeHistTotalBuckets+1) b[0] = float64NegInf() - for i := 0; i < timeHistNumSuperBuckets; i++ { - superBucketMin := uint64(0) - // The (inclusive) minimum for the first non-negative bucket is 0. - if i > 0 { - // The minimum for the second bucket will be - // 1 << timeHistSubBucketBits, indicating that all - // sub-buckets are represented by the next timeHistSubBucketBits - // bits. - // Thereafter, we shift up by 1 each time, so we can represent - // this pattern as (i-1)+timeHistSubBucketBits. - superBucketMin = uint64(1) << uint(i-1+timeHistSubBucketBits) - } - // subBucketShift is the amount that we need to shift the sub-bucket - // index to combine it with the bucketMin. - subBucketShift := uint(0) - if i > 1 { - // The first two super buckets are exact with respect to integers, - // so we'll never have to shift the sub-bucket index. Thereafter, - // we shift up by 1 with each subsequent bucket. - subBucketShift = uint(i - 2) - } + // Super-bucket 0 has no bits above timeHistSubBucketBits + // set, so just iterate over each bucket and assign the + // incrementing bucket. + for i := 0; i < timeHistNumSubBuckets; i++ { + bucketNanos := uint64(i) + b[i+1] = float64(bucketNanos) / 1e9 + } + // Generate the rest of the super-buckets. It's easier to reason + // about if we cut out the 0'th bucket, so subtract one since + // we just handled that bucket. + for i := 0; i < timeHistNumSuperBuckets-1; i++ { for j := 0; j < timeHistNumSubBuckets; j++ { - // j is the sub-bucket index. By shifting the index into position to - // combine with the bucket minimum, we obtain the minimum value for that - // sub-bucket. - subBucketMin := superBucketMin + (uint64(j) << subBucketShift) - - // Convert the subBucketMin which is in nanoseconds to a float64 seconds value. + // Set the super-bucket bit. + bucketNanos := uint64(1) << (i + timeHistSubBucketBits) + // Set the sub-bucket bits. + bucketNanos |= uint64(j) << i + // The index for this bucket is going to be the (i+1)'th super bucket + // (note that we're starting from zero, but handled the first super-bucket + // earlier, so we need to compensate), and the j'th sub bucket. + // Add 1 because we left space for -Inf. + bucketIndex := (i+1)*timeHistNumSubBuckets + j + 1 + // Convert nanoseconds to seconds via a division. // These values will all be exactly representable by a float64. - b[i*timeHistNumSubBuckets+j+1] = float64(subBucketMin) / 1e9 + b[bucketIndex] = float64(bucketNanos) / 1e9 } } b[len(b)-1] = float64Inf() diff --git a/src/runtime/histogram_test.go b/src/runtime/histogram_test.go index dbc64fa5592407..b12b65a41e36e9 100644 --- a/src/runtime/histogram_test.go +++ b/src/runtime/histogram_test.go @@ -68,3 +68,43 @@ func TestTimeHistogram(t *testing.T) { dummyTimeHistogram = TimeHistogram{} } + +func TestTimeHistogramMetricsBuckets(t *testing.T) { + buckets := TimeHistogramMetricsBuckets() + + nonInfBucketsLen := TimeHistNumSubBuckets * TimeHistNumSuperBuckets + expBucketsLen := nonInfBucketsLen + 2 // Count -Inf and +Inf. + if len(buckets) != expBucketsLen { + t.Fatalf("unexpected length of buckets: got %d, want %d", len(buckets), expBucketsLen) + } + // Check the first non-Inf 2*TimeHistNumSubBuckets buckets in order, skipping the + // first bucket which should be -Inf (checked later). + // + // Because of the way this scheme works, the bottom TimeHistNumSubBuckets + // buckets are fully populated, and then the next TimeHistNumSubBuckets + // have the TimeHistSubBucketBits'th bit set, while the bottom are once + // again fully populated. + for i := 1; i <= 2*TimeHistNumSubBuckets+1; i++ { + if got, want := buckets[i], float64(i-1)/1e9; got != want { + t.Errorf("expected bucket %d to have value %e, got %e", i, want, got) + } + } + // Check some values. + idxToBucket := map[int]float64{ + 0: math.Inf(-1), + 33: float64(0x10<<1) / 1e9, + 34: float64(0x11<<1) / 1e9, + 49: float64(0x10<<2) / 1e9, + 58: float64(0x19<<2) / 1e9, + 65: float64(0x10<<3) / 1e9, + 513: float64(0x10<<31) / 1e9, + 519: float64(0x16<<31) / 1e9, + expBucketsLen - 2: float64(0x1f<<43) / 1e9, + expBucketsLen - 1: math.Inf(1), + } + for idx, bucket := range idxToBucket { + if got, want := buckets[idx], bucket; got != want { + t.Errorf("expected bucket %d to have value %e, got %e", idx, want, got) + } + } +} diff --git a/src/runtime/iface.go b/src/runtime/iface.go index cd5fead9990cbf..a4d56dd33bccfb 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -5,8 +5,9 @@ package runtime import ( + "internal/abi" + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -63,7 +64,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { } // Entry doesn't exist yet. Make a new entry & add it. - m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*sys.PtrSize, 0, &memstats.other_sys)) + m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*goarch.PtrSize, 0, &memstats.other_sys)) m.inter = inter m._type = typ // The hash is used in type switches. However, compiler statically generates itab's @@ -100,7 +101,7 @@ func (t *itabTableType) find(inter *interfacetype, typ *_type) *itab { mask := t.size - 1 h := itabHashFunc(inter, typ) & mask for i := uintptr(1); ; i++ { - p := (**itab)(add(unsafe.Pointer(&t.entries), h*sys.PtrSize)) + p := (**itab)(add(unsafe.Pointer(&t.entries), h*goarch.PtrSize)) // Use atomic read here so if we see m != nil, we also see // the initializations of the fields of m. // m := *p @@ -133,7 +134,7 @@ func itabAdd(m *itab) { // t2 = new(itabTableType) + some additional entries // We lie and tell malloc we want pointer-free memory because // all the pointed-to values are not in the heap. - t2 := (*itabTableType)(mallocgc((2+2*t.size)*sys.PtrSize, nil, true)) + t2 := (*itabTableType)(mallocgc((2+2*t.size)*goarch.PtrSize, nil, true)) t2.size = t.size * 2 // Copy over entries. @@ -161,7 +162,7 @@ func (t *itabTableType) add(m *itab) { mask := t.size - 1 h := itabHashFunc(m.inter, m._type) & mask for i := uintptr(1); ; i++ { - p := (**itab)(add(unsafe.Pointer(&t.entries), h*sys.PtrSize)) + p := (**itab)(add(unsafe.Pointer(&t.entries), h*goarch.PtrSize)) m2 := *p if m2 == m { // A given itab may be used in more than one module @@ -295,11 +296,11 @@ type ( ) var ( - uint16Eface interface{} = uint16InterfacePtr(0) - uint32Eface interface{} = uint32InterfacePtr(0) - uint64Eface interface{} = uint64InterfacePtr(0) - stringEface interface{} = stringInterfacePtr("") - sliceEface interface{} = sliceInterfacePtr(nil) + uint16Eface any = uint16InterfacePtr(0) + uint32Eface any = uint32InterfacePtr(0) + uint64Eface any = uint64InterfacePtr(0) + stringEface any = stringInterfacePtr("") + sliceEface any = sliceInterfacePtr(nil) uint16Type *_type = efaceOf(&uint16Eface)._type uint32Type *_type = efaceOf(&uint32Eface)._type @@ -315,26 +316,43 @@ var ( // The convXXX functions succeed on a nil input, whereas the assertXXX // functions fail on a nil input. -func convT2E(t *_type, elem unsafe.Pointer) (e eface) { +// convT converts a value of type t, which is pointed to by v, to a pointer that can +// be used as the second word of an interface value. +func convT(t *_type, v unsafe.Pointer) unsafe.Pointer { if raceenabled { - raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E)) + raceReadObjectPC(t, v, getcallerpc(), abi.FuncPCABIInternal(convT)) } if msanenabled { - msanread(elem, t.size) + msanread(v, t.size) + } + if asanenabled { + asanread(v, t.size) } x := mallocgc(t.size, t, true) - // TODO: We allocate a zeroed object only to overwrite it with actual data. - // Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice. - typedmemmove(t, x, elem) - e._type = t - e.data = x - return + typedmemmove(t, x, v) + return x +} +func convTnoptr(t *_type, v unsafe.Pointer) unsafe.Pointer { + // TODO: maybe take size instead of type? + if raceenabled { + raceReadObjectPC(t, v, getcallerpc(), abi.FuncPCABIInternal(convTnoptr)) + } + if msanenabled { + msanread(v, t.size) + } + if asanenabled { + asanread(v, t.size) + } + + x := mallocgc(t.size, t, false) + memmove(x, v, t.size) + return x } func convT16(val uint16) (x unsafe.Pointer) { if val < uint16(len(staticuint64s)) { x = unsafe.Pointer(&staticuint64s[val]) - if sys.BigEndian { + if goarch.BigEndian { x = add(x, 6) } } else { @@ -347,7 +365,7 @@ func convT16(val uint16) (x unsafe.Pointer) { func convT32(val uint32) (x unsafe.Pointer) { if val < uint32(len(staticuint64s)) { x = unsafe.Pointer(&staticuint64s[val]) - if sys.BigEndian { + if goarch.BigEndian { x = add(x, 4) } } else { @@ -388,63 +406,16 @@ func convTslice(val []byte) (x unsafe.Pointer) { return } -func convT2Enoptr(t *_type, elem unsafe.Pointer) (e eface) { - if raceenabled { - raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Enoptr)) - } - if msanenabled { - msanread(elem, t.size) - } - x := mallocgc(t.size, t, false) - memmove(x, elem, t.size) - e._type = t - e.data = x - return -} - -func convT2I(tab *itab, elem unsafe.Pointer) (i iface) { - t := tab._type - if raceenabled { - raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2I)) - } - if msanenabled { - msanread(elem, t.size) - } - x := mallocgc(t.size, t, true) - typedmemmove(t, x, elem) - i.tab = tab - i.data = x - return -} - -func convT2Inoptr(tab *itab, elem unsafe.Pointer) (i iface) { - t := tab._type - if raceenabled { - raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2Inoptr)) - } - if msanenabled { - msanread(elem, t.size) - } - x := mallocgc(t.size, t, false) - memmove(x, elem, t.size) - i.tab = tab - i.data = x - return -} - -func convI2I(inter *interfacetype, i iface) (r iface) { - tab := i.tab - if tab == nil { - return +// convI2I returns the new itab to be used for the destination value +// when converting a value with itab src to the dst interface. +func convI2I(dst *interfacetype, src *itab) *itab { + if src == nil { + return nil } - if tab.inter == inter { - r.tab = tab - r.data = i.data - return + if src.inter == dst { + return src } - r.tab = getitab(inter, tab._type, false) - r.data = i.data - return + return getitab(dst, src._type, false) } func assertI2I(inter *interfacetype, tab *itab) *itab { @@ -511,7 +482,7 @@ func iterate_itabs(fn func(*itab)) { // so no other locks/atomics needed. t := itabTable for i := uintptr(0); i < t.size; i++ { - m := *(**itab)(add(unsafe.Pointer(&t.entries), i*sys.PtrSize)) + m := *(**itab)(add(unsafe.Pointer(&t.entries), i*goarch.PtrSize)) if m != nil { fn(m) } diff --git a/src/runtime/iface_test.go b/src/runtime/iface_test.go index 4fab6c968a5fa4..06f6eeb9524637 100644 --- a/src/runtime/iface_test.go +++ b/src/runtime/iface_test.go @@ -44,8 +44,8 @@ func (Tstr) Method1() {} func (Tslice) Method1() {} var ( - e interface{} - e_ interface{} + e any + e_ any i1 I1 i2 I2 ts TS @@ -196,7 +196,7 @@ func BenchmarkAssertI2I(b *testing.B) { func BenchmarkAssertI2E(b *testing.B) { i1 = tm for i := 0; i < b.N; i++ { - e = i1.(interface{}) + e = i1.(any) } } @@ -224,33 +224,33 @@ func BenchmarkAssertE2T2Blank(b *testing.B) { func BenchmarkAssertI2E2(b *testing.B) { i1 = tm for i := 0; i < b.N; i++ { - e, ok = i1.(interface{}) + e, ok = i1.(any) } } func BenchmarkAssertI2E2Blank(b *testing.B) { i1 = tm for i := 0; i < b.N; i++ { - _, ok = i1.(interface{}) + _, ok = i1.(any) } } func BenchmarkAssertE2E2(b *testing.B) { e = tm for i := 0; i < b.N; i++ { - e_, ok = e.(interface{}) + e_, ok = e.(any) } } func BenchmarkAssertE2E2Blank(b *testing.B) { e = tm for i := 0; i < b.N; i++ { - _, ok = e.(interface{}) + _, ok = e.(any) } } func TestNonEscapingConvT2E(t *testing.T) { - m := make(map[interface{}]bool) + m := make(map[any]bool) m[42] = true if !m[42] { t.Fatalf("42 is not present in the map") diff --git a/src/runtime/internal/atomic/atomic_386.go b/src/runtime/internal/atomic/atomic_386.go index d4aed6b6717da9..bf2f4b922910eb 100644 --- a/src/runtime/internal/atomic/atomic_386.go +++ b/src/runtime/internal/atomic/atomic_386.go @@ -3,13 +3,13 @@ // license that can be found in the LICENSE file. //go:build 386 -// +build 386 package atomic import "unsafe" // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Load //go:linkname Loadp diff --git a/src/runtime/internal/atomic/atomic_amd64.go b/src/runtime/internal/atomic/atomic_amd64.go index e36eb83a113da7..52a83620c8f696 100644 --- a/src/runtime/internal/atomic/atomic_amd64.go +++ b/src/runtime/internal/atomic/atomic_amd64.go @@ -7,6 +7,7 @@ package atomic import "unsafe" // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Load //go:linkname Loadp //go:linkname Load64 diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go index 2e9374ca269dd6..bdb184727909c5 100644 --- a/src/runtime/internal/atomic/atomic_arm.go +++ b/src/runtime/internal/atomic/atomic_arm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm -// +build arm package atomic @@ -13,6 +12,7 @@ import ( ) // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Xchg //go:linkname Xchguintptr @@ -44,6 +44,7 @@ func addrLock(addr *uint64) *spinlock { } // Atomic add and return new value. +// //go:nosplit func Xadd(val *uint32, delta int32) uint32 { for { diff --git a/src/runtime/internal/atomic/atomic_arm.s b/src/runtime/internal/atomic/atomic_arm.s index be3fd3a3950617..92cbe8a34f68d2 100644 --- a/src/runtime/internal/atomic/atomic_arm.s +++ b/src/runtime/internal/atomic/atomic_arm.s @@ -229,16 +229,22 @@ store64loop: // functions tail-call into the appropriate implementation, which // means they must not open a frame. Hence, when they go down the // panic path, at that point they push the LR to create a real frame -// (they don't need to pop it because panic won't return). +// (they don't need to pop it because panic won't return; however, we +// do need to set the SP delta back). + +// Check if R1 is 8-byte aligned, panic if not. +// Clobbers R2. +#define CHECK_ALIGN \ + AND.S $7, R1, R2 \ + BEQ 4(PC) \ + MOVW.W R14, -4(R13) /* prepare a real frame */ \ + BL ·panicUnaligned(SB) \ + ADD $4, R13 /* compensate SP delta */ TEXT ·Cas64(SB),NOSPLIT,$-4-21 NO_LOCAL_POINTERS MOVW addr+0(FP), R1 - // make unaligned atomic access panic - AND.S $7, R1, R2 - BEQ 3(PC) - MOVW.W R14, -4(R13) // prepare a real frame - BL ·panicUnaligned(SB) + CHECK_ALIGN MOVB runtime·goarm(SB), R11 CMP $7, R11 @@ -249,11 +255,7 @@ TEXT ·Cas64(SB),NOSPLIT,$-4-21 TEXT ·Xadd64(SB),NOSPLIT,$-4-20 NO_LOCAL_POINTERS MOVW addr+0(FP), R1 - // make unaligned atomic access panic - AND.S $7, R1, R2 - BEQ 3(PC) - MOVW.W R14, -4(R13) // prepare a real frame - BL ·panicUnaligned(SB) + CHECK_ALIGN MOVB runtime·goarm(SB), R11 CMP $7, R11 @@ -264,11 +266,7 @@ TEXT ·Xadd64(SB),NOSPLIT,$-4-20 TEXT ·Xchg64(SB),NOSPLIT,$-4-20 NO_LOCAL_POINTERS MOVW addr+0(FP), R1 - // make unaligned atomic access panic - AND.S $7, R1, R2 - BEQ 3(PC) - MOVW.W R14, -4(R13) // prepare a real frame - BL ·panicUnaligned(SB) + CHECK_ALIGN MOVB runtime·goarm(SB), R11 CMP $7, R11 @@ -279,11 +277,7 @@ TEXT ·Xchg64(SB),NOSPLIT,$-4-20 TEXT ·Load64(SB),NOSPLIT,$-4-12 NO_LOCAL_POINTERS MOVW addr+0(FP), R1 - // make unaligned atomic access panic - AND.S $7, R1, R2 - BEQ 3(PC) - MOVW.W R14, -4(R13) // prepare a real frame - BL ·panicUnaligned(SB) + CHECK_ALIGN MOVB runtime·goarm(SB), R11 CMP $7, R11 @@ -294,11 +288,7 @@ TEXT ·Load64(SB),NOSPLIT,$-4-12 TEXT ·Store64(SB),NOSPLIT,$-4-12 NO_LOCAL_POINTERS MOVW addr+0(FP), R1 - // make unaligned atomic access panic - AND.S $7, R1, R2 - BEQ 3(PC) - MOVW.W R14, -4(R13) // prepare a real frame - BL ·panicUnaligned(SB) + CHECK_ALIGN MOVB runtime·goarm(SB), R11 CMP $7, R11 diff --git a/src/runtime/internal/atomic/atomic_arm64.go b/src/runtime/internal/atomic/atomic_arm64.go index dbb1796ec0941a..459fb9978db30a 100644 --- a/src/runtime/internal/atomic/atomic_arm64.go +++ b/src/runtime/internal/atomic/atomic_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 -// +build arm64 package atomic diff --git a/src/runtime/internal/atomic/atomic_loong64.go b/src/runtime/internal/atomic/atomic_loong64.go new file mode 100644 index 00000000000000..908a7d69aa376e --- /dev/null +++ b/src/runtime/internal/atomic/atomic_loong64.go @@ -0,0 +1,83 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package atomic + +import "unsafe" + +//go:noescape +func Xadd(ptr *uint32, delta int32) uint32 + +//go:noescape +func Xadd64(ptr *uint64, delta int64) uint64 + +//go:noescape +func Xadduintptr(ptr *uintptr, delta uintptr) uintptr + +//go:noescape +func Xchg(ptr *uint32, new uint32) uint32 + +//go:noescape +func Xchg64(ptr *uint64, new uint64) uint64 + +//go:noescape +func Xchguintptr(ptr *uintptr, new uintptr) uintptr + +//go:noescape +func Load(ptr *uint32) uint32 + +//go:noescape +func Load8(ptr *uint8) uint8 + +//go:noescape +func Load64(ptr *uint64) uint64 + +// NO go:noescape annotation; *ptr escapes if result escapes (#31525) +func Loadp(ptr unsafe.Pointer) unsafe.Pointer + +//go:noescape +func LoadAcq(ptr *uint32) uint32 + +//go:noescape +func LoadAcquintptr(ptr *uintptr) uintptr + +//go:noescape +func And8(ptr *uint8, val uint8) + +//go:noescape +func And(ptr *uint32, val uint32) + +//go:noescape +func Or8(ptr *uint8, val uint8) + +//go:noescape +func Or(ptr *uint32, val uint32) + +// NOTE: Do not add atomicxor8 (XOR is not idempotent). + +//go:noescape +func Cas64(ptr *uint64, old, new uint64) bool + +//go:noescape +func CasRel(ptr *uint32, old, new uint32) bool + +//go:noescape +func Store(ptr *uint32, val uint32) + +//go:noescape +func Store8(ptr *uint8, val uint8) + +//go:noescape +func Store64(ptr *uint64, val uint64) + +// NO go:noescape annotation; see atomic_pointer.go. +func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) + +//go:noescape +func StoreRel(ptr *uint32, val uint32) + +//go:noescape +func StoreReluintptr(ptr *uintptr, val uintptr) diff --git a/src/runtime/internal/atomic/atomic_loong64.s b/src/runtime/internal/atomic/atomic_loong64.s new file mode 100644 index 00000000000000..bfb6c7e130d653 --- /dev/null +++ b/src/runtime/internal/atomic/atomic_loong64.s @@ -0,0 +1,299 @@ +// Copyright 2022 The Go 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" + +// bool cas(uint32 *ptr, uint32 old, uint32 new) +// Atomically: +// if(*ptr == old){ +// *ptr = new; +// return 1; +// } else +// return 0; +TEXT ·Cas(SB), NOSPLIT, $0-17 + MOVV ptr+0(FP), R4 + MOVW old+8(FP), R5 + MOVW new+12(FP), R6 + DBAR +cas_again: + MOVV R6, R7 + LL (R4), R8 + BNE R5, R8, cas_fail + SC R7, (R4) + BEQ R7, cas_again + MOVV $1, R4 + MOVB R4, ret+16(FP) + DBAR + RET +cas_fail: + MOVV $0, R4 + JMP -4(PC) + +// bool cas64(uint64 *ptr, uint64 old, uint64 new) +// Atomically: +// if(*ptr == old){ +// *ptr = new; +// return 1; +// } else { +// return 0; +// } +TEXT ·Cas64(SB), NOSPLIT, $0-25 + MOVV ptr+0(FP), R4 + MOVV old+8(FP), R5 + MOVV new+16(FP), R6 + DBAR +cas64_again: + MOVV R6, R7 + LLV (R4), R8 + BNE R5, R8, cas64_fail + SCV R7, (R4) + BEQ R7, cas64_again + MOVV $1, R4 + MOVB R4, ret+24(FP) + DBAR + RET +cas64_fail: + MOVV $0, R4 + JMP -4(PC) + +TEXT ·Casuintptr(SB), NOSPLIT, $0-25 + JMP ·Cas64(SB) + +TEXT ·CasRel(SB), NOSPLIT, $0-17 + JMP ·Cas(SB) + +TEXT ·Loaduintptr(SB), NOSPLIT|NOFRAME, $0-16 + JMP ·Load64(SB) + +TEXT ·Loaduint(SB), NOSPLIT|NOFRAME, $0-16 + JMP ·Load64(SB) + +TEXT ·Storeuintptr(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + +TEXT ·Xadduintptr(SB), NOSPLIT, $0-24 + JMP ·Xadd64(SB) + +TEXT ·Loadint64(SB), NOSPLIT, $0-16 + JMP ·Load64(SB) + +TEXT ·Xaddint64(SB), NOSPLIT, $0-24 + JMP ·Xadd64(SB) + +// bool casp(void **val, void *old, void *new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// } else +// return 0; +TEXT ·Casp1(SB), NOSPLIT, $0-25 + JMP runtime∕internal∕atomic·Cas64(SB) + +// uint32 xadd(uint32 volatile *ptr, int32 delta) +// Atomically: +// *val += delta; +// return *val; +TEXT ·Xadd(SB), NOSPLIT, $0-20 + MOVV ptr+0(FP), R4 + MOVW delta+8(FP), R5 + DBAR + LL (R4), R6 + ADDU R6, R5, R7 + MOVV R7, R6 + SC R7, (R4) + BEQ R7, -4(PC) + MOVW R6, ret+16(FP) + DBAR + RET + +TEXT ·Xadd64(SB), NOSPLIT, $0-24 + MOVV ptr+0(FP), R4 + MOVV delta+8(FP), R5 + DBAR + LLV (R4), R6 + ADDVU R6, R5, R7 + MOVV R7, R6 + SCV R7, (R4) + BEQ R7, -4(PC) + MOVV R6, ret+16(FP) + DBAR + RET + +TEXT ·Xchg(SB), NOSPLIT, $0-20 + MOVV ptr+0(FP), R4 + MOVW new+8(FP), R5 + + DBAR + MOVV R5, R6 + LL (R4), R7 + SC R6, (R4) + BEQ R6, -3(PC) + MOVW R7, ret+16(FP) + DBAR + RET + +TEXT ·Xchg64(SB), NOSPLIT, $0-24 + MOVV ptr+0(FP), R4 + MOVV new+8(FP), R5 + + DBAR + MOVV R5, R6 + LLV (R4), R7 + SCV R6, (R4) + BEQ R6, -3(PC) + MOVV R7, ret+16(FP) + DBAR + RET + +TEXT ·Xchguintptr(SB), NOSPLIT, $0-24 + JMP ·Xchg64(SB) + +TEXT ·StorepNoWB(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + +TEXT ·StoreRel(SB), NOSPLIT, $0-12 + JMP ·Store(SB) + +TEXT ·StoreReluintptr(SB), NOSPLIT, $0-16 + JMP ·Store64(SB) + +TEXT ·Store(SB), NOSPLIT, $0-12 + MOVV ptr+0(FP), R4 + MOVW val+8(FP), R5 + DBAR + MOVW R5, 0(R4) + DBAR + RET + +TEXT ·Store8(SB), NOSPLIT, $0-9 + MOVV ptr+0(FP), R4 + MOVB val+8(FP), R5 + DBAR + MOVB R5, 0(R4) + DBAR + RET + +TEXT ·Store64(SB), NOSPLIT, $0-16 + MOVV ptr+0(FP), R4 + MOVV val+8(FP), R5 + DBAR + MOVV R5, 0(R4) + DBAR + RET + +// void Or8(byte volatile*, byte); +TEXT ·Or8(SB), NOSPLIT, $0-9 + MOVV ptr+0(FP), R4 + MOVBU val+8(FP), R5 + // Align ptr down to 4 bytes so we can use 32-bit load/store. + MOVV $~3, R6 + AND R4, R6 + // R7 = ((ptr & 3) * 8) + AND $3, R4, R7 + SLLV $3, R7 + // Shift val for aligned ptr. R5 = val << R4 + SLLV R7, R5 + + DBAR + LL (R6), R7 + OR R5, R7 + SC R7, (R6) + BEQ R7, -4(PC) + DBAR + RET + +// void And8(byte volatile*, byte); +TEXT ·And8(SB), NOSPLIT, $0-9 + MOVV ptr+0(FP), R4 + MOVBU val+8(FP), R5 + // Align ptr down to 4 bytes so we can use 32-bit load/store. + MOVV $~3, R6 + AND R4, R6 + // R7 = ((ptr & 3) * 8) + AND $3, R4, R7 + SLLV $3, R7 + // Shift val for aligned ptr. R5 = val << R7 | ^(0xFF << R7) + MOVV $0xFF, R8 + SLLV R7, R5 + SLLV R7, R8 + NOR R0, R8 + OR R8, R5 + + DBAR + LL (R6), R7 + AND R5, R7 + SC R7, (R6) + BEQ R7, -4(PC) + DBAR + RET + +// func Or(addr *uint32, v uint32) +TEXT ·Or(SB), NOSPLIT, $0-12 + MOVV ptr+0(FP), R4 + MOVW val+8(FP), R5 + DBAR + LL (R4), R6 + OR R5, R6 + SC R6, (R4) + BEQ R6, -4(PC) + DBAR + RET + +// func And(addr *uint32, v uint32) +TEXT ·And(SB), NOSPLIT, $0-12 + MOVV ptr+0(FP), R4 + MOVW val+8(FP), R5 + DBAR + LL (R4), R6 + AND R5, R6 + SC R6, (R4) + BEQ R6, -4(PC) + DBAR + RET + +// uint32 runtime∕internal∕atomic·Load(uint32 volatile* ptr) +TEXT ·Load(SB),NOSPLIT|NOFRAME,$0-12 + MOVV ptr+0(FP), R19 + DBAR + MOVWU 0(R19), R19 + DBAR + MOVW R19, ret+8(FP) + RET + +// uint8 runtime∕internal∕atomic·Load8(uint8 volatile* ptr) +TEXT ·Load8(SB),NOSPLIT|NOFRAME,$0-9 + MOVV ptr+0(FP), R19 + DBAR + MOVBU 0(R19), R19 + DBAR + MOVB R19, ret+8(FP) + RET + +// uint64 runtime∕internal∕atomic·Load64(uint64 volatile* ptr) +TEXT ·Load64(SB),NOSPLIT|NOFRAME,$0-16 + MOVV ptr+0(FP), R19 + DBAR + MOVV 0(R19), R19 + DBAR + MOVV R19, ret+8(FP) + RET + +// void *runtime∕internal∕atomic·Loadp(void *volatile *ptr) +TEXT ·Loadp(SB),NOSPLIT|NOFRAME,$0-16 + MOVV ptr+0(FP), R19 + DBAR + MOVV 0(R19), R19 + DBAR + MOVV R19, ret+8(FP) + RET + +// uint32 runtime∕internal∕atomic·LoadAcq(uint32 volatile* ptr) +TEXT ·LoadAcq(SB),NOSPLIT|NOFRAME,$0-12 + JMP atomic·Load(SB) + +// uintptr ·LoadAcquintptr(uintptr volatile* ptr) +TEXT ·LoadAcquintptr(SB),NOSPLIT|NOFRAME,$0-16 + JMP atomic·Load64(SB) + diff --git a/src/runtime/internal/atomic/atomic_mips64x.go b/src/runtime/internal/atomic/atomic_mips64x.go index 5b407ebc7a5918..1e12b83801dba2 100644 --- a/src/runtime/internal/atomic/atomic_mips64x.go +++ b/src/runtime/internal/atomic/atomic_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package atomic diff --git a/src/runtime/internal/atomic/atomic_mips64x.s b/src/runtime/internal/atomic/atomic_mips64x.s index fedfc4a175cc35..b4411d87da65b5 100644 --- a/src/runtime/internal/atomic/atomic_mips64x.s +++ b/src/runtime/internal/atomic/atomic_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "textflag.h" diff --git a/src/runtime/internal/atomic/atomic_mipsx.go b/src/runtime/internal/atomic/atomic_mipsx.go index 80cd65b33388ab..5dd15a0b022190 100644 --- a/src/runtime/internal/atomic/atomic_mipsx.go +++ b/src/runtime/internal/atomic/atomic_mipsx.go @@ -3,9 +3,9 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Xadd64 //go:linkname Xchg64 //go:linkname Cas64 diff --git a/src/runtime/internal/atomic/atomic_mipsx.s b/src/runtime/internal/atomic/atomic_mipsx.s index c0835d66ed886a..390e9ce7acc386 100644 --- a/src/runtime/internal/atomic/atomic_mipsx.s +++ b/src/runtime/internal/atomic/atomic_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "textflag.h" diff --git a/src/runtime/internal/atomic/atomic_ppc64x.go b/src/runtime/internal/atomic/atomic_ppc64x.go index 98101e3287823a..998d16e3f6862d 100644 --- a/src/runtime/internal/atomic/atomic_ppc64x.go +++ b/src/runtime/internal/atomic/atomic_ppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package atomic diff --git a/src/runtime/internal/atomic/atomic_ppc64x.s b/src/runtime/internal/atomic/atomic_ppc64x.s index 226b3b62162ca8..04f0eadd065e6f 100644 --- a/src/runtime/internal/atomic/atomic_ppc64x.s +++ b/src/runtime/internal/atomic/atomic_ppc64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go index a058d601029c98..9855bf0780fbed 100644 --- a/src/runtime/internal/atomic/atomic_s390x.go +++ b/src/runtime/internal/atomic/atomic_s390x.go @@ -7,6 +7,7 @@ package atomic import "unsafe" // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Load //go:linkname Loadp //go:linkname Load64 diff --git a/src/runtime/internal/atomic/atomic_test.go b/src/runtime/internal/atomic/atomic_test.go index c9c2eba248cf42..2ae60b8507ef58 100644 --- a/src/runtime/internal/atomic/atomic_test.go +++ b/src/runtime/internal/atomic/atomic_test.go @@ -5,9 +5,9 @@ package atomic_test import ( + "internal/goarch" "runtime" "runtime/internal/atomic" - "runtime/internal/sys" "testing" "unsafe" ) @@ -56,7 +56,7 @@ func TestXadduintptr(t *testing.T) { // Tests that xadduintptr correctly updates 64-bit values. The place where // we actually do so is mstats.go, functions mSysStat{Inc,Dec}. func TestXadduintptrOnUint64(t *testing.T) { - if sys.BigEndian { + if goarch.BigEndian { // On big endian architectures, we never use xadduintptr to update // 64-bit values and hence we skip the test. (Note that functions // mSysStat{Inc,Dec} in mstats.go have explicit checks for diff --git a/src/runtime/internal/atomic/atomic_wasm.go b/src/runtime/internal/atomic/atomic_wasm.go index 3f77f16b4efe2b..835fc43ccf9f68 100644 --- a/src/runtime/internal/atomic/atomic_wasm.go +++ b/src/runtime/internal/atomic/atomic_wasm.go @@ -6,6 +6,7 @@ // See https://github.com/WebAssembly/design/issues/1073 // Export some functions via linkname to assembly in sync/atomic. +// //go:linkname Load //go:linkname Loadp //go:linkname Load64 diff --git a/src/runtime/internal/atomic/bench_test.go b/src/runtime/internal/atomic/bench_test.go index 2476c06c52afbd..efc0531a0d3fd6 100644 --- a/src/runtime/internal/atomic/bench_test.go +++ b/src/runtime/internal/atomic/bench_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -var sink interface{} +var sink any func BenchmarkAtomicLoad64(b *testing.B) { var x uint64 diff --git a/src/runtime/internal/atomic/doc.go b/src/runtime/internal/atomic/doc.go new file mode 100644 index 00000000000000..08e6b6ce0bc88c --- /dev/null +++ b/src/runtime/internal/atomic/doc.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package atomic provides atomic operations, independent of sync/atomic, +to the runtime. + +On most platforms, the compiler is aware of the functions defined +in this package, and they're replaced with platform-specific intrinsics. +On other platforms, generic implementations are made available. + +Unless otherwise noted, operations defined in this package are sequentially +consistent across threads with respect to the values they manipulate. More +specifically, operations that happen in a specific order on one thread, +will always be observed to happen in exactly that order by another thread. +*/ +package atomic diff --git a/src/runtime/internal/atomic/stubs.go b/src/runtime/internal/atomic/stubs.go index e7544ba4484f07..7df8d9c86329ee 100644 --- a/src/runtime/internal/atomic/stubs.go +++ b/src/runtime/internal/atomic/stubs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !wasm -// +build !wasm package atomic diff --git a/src/runtime/internal/atomic/sys_linux_arm.s b/src/runtime/internal/atomic/sys_linux_arm.s index 0cc7fa73d1e6cf..9225df843948e9 100644 --- a/src/runtime/internal/atomic/sys_linux_arm.s +++ b/src/runtime/internal/atomic/sys_linux_arm.s @@ -15,9 +15,6 @@ // LR = return address // The function returns with CS true if the swap happened. // http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850 -// On older kernels (before 2.6.24) the function can incorrectly -// report a conflict, so we have to double-check the compare ourselves -// and retry if necessary. // // https://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b49c0f24cf6744a3f4fd09289fe7cade349dead5 // @@ -37,20 +34,13 @@ TEXT kernelcas<>(SB),NOSPLIT,$0 // because we don't know how to traceback through __kuser_cmpxchg MOVW (R2), R0 MOVW old+4(FP), R0 -loop: MOVW new+8(FP), R1 BL cas<>(SB) - BCC check + BCC ret0 MOVW $1, R0 MOVB R0, ret+12(FP) RET -check: - // Kernel lies; double-check. - MOVW ptr+0(FP), R2 - MOVW old+4(FP), R0 - MOVW 0(R2), R3 - CMP R0, R3 - BEQ loop +ret0: MOVW $0, R0 MOVB R0, ret+12(FP) RET diff --git a/src/runtime/internal/atomic/sys_nonlinux_arm.s b/src/runtime/internal/atomic/sys_nonlinux_arm.s index 04036ca9702da4..b55bf908a26056 100644 --- a/src/runtime/internal/atomic/sys_nonlinux_arm.s +++ b/src/runtime/internal/atomic/sys_nonlinux_arm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux #include "textflag.h" diff --git a/src/runtime/internal/atomic/types.go b/src/runtime/internal/atomic/types.go new file mode 100644 index 00000000000000..35d8935c70cd4d --- /dev/null +++ b/src/runtime/internal/atomic/types.go @@ -0,0 +1,557 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package atomic + +import "unsafe" + +// Int32 is an atomically accessed int32 value. +// +// An Int32 must not be copied. +type Int32 struct { + noCopy noCopy + value int32 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (i *Int32) Load() int32 { + return Loadint32(&i.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (i *Int32) Store(value int32) { + Storeint32(&i.value, value) +} + +// CompareAndSwap atomically compares i's value with old, +// and if they're equal, swaps i's value with new. +// +// Returns true if the operation succeeded. +// +//go:nosplit +func (i *Int32) CompareAndSwap(old, new int32) bool { + return Casint32(&i.value, old, new) +} + +// Swap replaces i's value with new, returning +// i's value before the replacement. +// +//go:nosplit +func (i *Int32) Swap(new int32) int32 { + return Xchgint32(&i.value, new) +} + +// Add adds delta to i atomically, returning +// the new updated value. +// +// This operation wraps around in the usual +// two's-complement way. +// +//go:nosplit +func (i *Int32) Add(delta int32) int32 { + return Xaddint32(&i.value, delta) +} + +// Int64 is an atomically accessed int64 value. +// +// 8-byte aligned on all platforms, unlike a regular int64. +// +// An Int64 must not be copied. +type Int64 struct { + noCopy noCopy + _ align64 + value int64 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (i *Int64) Load() int64 { + return Loadint64(&i.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (i *Int64) Store(value int64) { + Storeint64(&i.value, value) +} + +// CompareAndSwap atomically compares i's value with old, +// and if they're equal, swaps i's value with new. +// +// Returns true if the operation succeeded. +// +//go:nosplit +func (i *Int64) CompareAndSwap(old, new int64) bool { + return Casint64(&i.value, old, new) +} + +// Swap replaces i's value with new, returning +// i's value before the replacement. +// +//go:nosplit +func (i *Int64) Swap(new int64) int64 { + return Xchgint64(&i.value, new) +} + +// Add adds delta to i atomically, returning +// the new updated value. +// +// This operation wraps around in the usual +// two's-complement way. +// +//go:nosplit +func (i *Int64) Add(delta int64) int64 { + return Xaddint64(&i.value, delta) +} + +// Uint8 is an atomically accessed uint8 value. +// +// A Uint8 must not be copied. +type Uint8 struct { + noCopy noCopy + value uint8 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (u *Uint8) Load() uint8 { + return Load8(&u.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (u *Uint8) Store(value uint8) { + Store8(&u.value, value) +} + +// And takes value and performs a bit-wise +// "and" operation with the value of u, storing +// the result into u. +// +// The full process is performed atomically. +// +//go:nosplit +func (u *Uint8) And(value uint8) { + And8(&u.value, value) +} + +// Or takes value and performs a bit-wise +// "or" operation with the value of u, storing +// the result into u. +// +// The full process is performed atomically. +// +//go:nosplit +func (u *Uint8) Or(value uint8) { + Or8(&u.value, value) +} + +// Bool is an atomically accessed bool value. +// +// A Bool must not be copied. +type Bool struct { + // Inherits noCopy from Uint8. + u Uint8 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (b *Bool) Load() bool { + return b.u.Load() != 0 +} + +// Store updates the value atomically. +// +//go:nosplit +func (b *Bool) Store(value bool) { + s := uint8(0) + if value { + s = 1 + } + b.u.Store(s) +} + +// Uint32 is an atomically accessed uint32 value. +// +// A Uint32 must not be copied. +type Uint32 struct { + noCopy noCopy + value uint32 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (u *Uint32) Load() uint32 { + return Load(&u.value) +} + +// LoadAcquire is a partially unsynchronized version +// of Load that relaxes ordering constraints. Other threads +// may observe operations that precede this operation to +// occur after it, but no operation that occurs after it +// on this thread can be observed to occur before it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uint32) LoadAcquire() uint32 { + return LoadAcq(&u.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (u *Uint32) Store(value uint32) { + Store(&u.value, value) +} + +// StoreRelease is a partially unsynchronized version +// of Store that relaxes ordering constraints. Other threads +// may observe operations that occur after this operation to +// precede it, but no operation that precedes it +// on this thread can be observed to occur after it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uint32) StoreRelease(value uint32) { + StoreRel(&u.value, value) +} + +// CompareAndSwap atomically compares u's value with old, +// and if they're equal, swaps u's value with new. +// +// Returns true if the operation succeeded. +// +//go:nosplit +func (u *Uint32) CompareAndSwap(old, new uint32) bool { + return Cas(&u.value, old, new) +} + +// CompareAndSwapRelease is a partially unsynchronized version +// of Cas that relaxes ordering constraints. Other threads +// may observe operations that occur after this operation to +// precede it, but no operation that precedes it +// on this thread can be observed to occur after it. +// +// Returns true if the operation succeeded. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uint32) CompareAndSwapRelease(old, new uint32) bool { + return CasRel(&u.value, old, new) +} + +// Swap replaces u's value with new, returning +// u's value before the replacement. +// +//go:nosplit +func (u *Uint32) Swap(value uint32) uint32 { + return Xchg(&u.value, value) +} + +// And takes value and performs a bit-wise +// "and" operation with the value of u, storing +// the result into u. +// +// The full process is performed atomically. +// +//go:nosplit +func (u *Uint32) And(value uint32) { + And(&u.value, value) +} + +// Or takes value and performs a bit-wise +// "or" operation with the value of u, storing +// the result into u. +// +// The full process is performed atomically. +// +//go:nosplit +func (u *Uint32) Or(value uint32) { + Or(&u.value, value) +} + +// Add adds delta to u atomically, returning +// the new updated value. +// +// This operation wraps around in the usual +// two's-complement way. +// +//go:nosplit +func (u *Uint32) Add(delta int32) uint32 { + return Xadd(&u.value, delta) +} + +// Uint64 is an atomically accessed uint64 value. +// +// 8-byte aligned on all platforms, unlike a regular uint64. +// +// A Uint64 must not be copied. +type Uint64 struct { + noCopy noCopy + _ align64 + value uint64 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (u *Uint64) Load() uint64 { + return Load64(&u.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (u *Uint64) Store(value uint64) { + Store64(&u.value, value) +} + +// CompareAndSwap atomically compares u's value with old, +// and if they're equal, swaps u's value with new. +// +// Returns true if the operation succeeded. +// +//go:nosplit +func (u *Uint64) CompareAndSwap(old, new uint64) bool { + return Cas64(&u.value, old, new) +} + +// Swap replaces u's value with new, returning +// u's value before the replacement. +// +//go:nosplit +func (u *Uint64) Swap(value uint64) uint64 { + return Xchg64(&u.value, value) +} + +// Add adds delta to u atomically, returning +// the new updated value. +// +// This operation wraps around in the usual +// two's-complement way. +// +//go:nosplit +func (u *Uint64) Add(delta int64) uint64 { + return Xadd64(&u.value, delta) +} + +// Uintptr is an atomically accessed uintptr value. +// +// A Uintptr must not be copied. +type Uintptr struct { + noCopy noCopy + value uintptr +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (u *Uintptr) Load() uintptr { + return Loaduintptr(&u.value) +} + +// LoadAcquire is a partially unsynchronized version +// of Load that relaxes ordering constraints. Other threads +// may observe operations that precede this operation to +// occur after it, but no operation that occurs after it +// on this thread can be observed to occur before it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uintptr) LoadAcquire() uintptr { + return LoadAcquintptr(&u.value) +} + +// Store updates the value atomically. +// +//go:nosplit +func (u *Uintptr) Store(value uintptr) { + Storeuintptr(&u.value, value) +} + +// StoreRelease is a partially unsynchronized version +// of Store that relaxes ordering constraints. Other threads +// may observe operations that occur after this operation to +// precede it, but no operation that precedes it +// on this thread can be observed to occur after it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uintptr) StoreRelease(value uintptr) { + StoreReluintptr(&u.value, value) +} + +// CompareAndSwap atomically compares u's value with old, +// and if they're equal, swaps u's value with new. +// +// Returns true if the operation succeeded. +// +//go:nosplit +func (u *Uintptr) CompareAndSwap(old, new uintptr) bool { + return Casuintptr(&u.value, old, new) +} + +// Swap replaces u's value with new, returning +// u's value before the replacement. +// +//go:nosplit +func (u *Uintptr) Swap(value uintptr) uintptr { + return Xchguintptr(&u.value, value) +} + +// Add adds delta to u atomically, returning +// the new updated value. +// +// This operation wraps around in the usual +// two's-complement way. +// +//go:nosplit +func (u *Uintptr) Add(delta uintptr) uintptr { + return Xadduintptr(&u.value, delta) +} + +// Float64 is an atomically accessed float64 value. +// +// 8-byte aligned on all platforms, unlike a regular float64. +// +// A Float64 must not be copied. +type Float64 struct { + // Inherits noCopy and align64 from Uint64. + u Uint64 +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (f *Float64) Load() float64 { + r := f.u.Load() + return *(*float64)(unsafe.Pointer(&r)) +} + +// Store updates the value atomically. +// +//go:nosplit +func (f *Float64) Store(value float64) { + f.u.Store(*(*uint64)(unsafe.Pointer(&value))) +} + +// UnsafePointer is an atomically accessed unsafe.Pointer value. +// +// Note that because of the atomicity guarantees, stores to values +// of this type never trigger a write barrier, and the relevant +// methods are suffixed with "NoWB" to indicate that explicitly. +// As a result, this type should be used carefully, and sparingly, +// mostly with values that do not live in the Go heap anyway. +// +// An UnsafePointer must not be copied. +type UnsafePointer struct { + noCopy noCopy + value unsafe.Pointer +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (u *UnsafePointer) Load() unsafe.Pointer { + return Loadp(unsafe.Pointer(&u.value)) +} + +// StoreNoWB updates the value atomically. +// +// WARNING: As the name implies this operation does *not* +// perform a write barrier on value, and so this operation may +// hide pointers from the GC. Use with care and sparingly. +// It is safe to use with values not found in the Go heap. +// +//go:nosplit +func (u *UnsafePointer) StoreNoWB(value unsafe.Pointer) { + StorepNoWB(unsafe.Pointer(&u.value), value) +} + +// CompareAndSwapNoWB atomically (with respect to other methods) +// compares u's value with old, and if they're equal, +// swaps u's value with new. +// +// Returns true if the operation succeeded. +// +// WARNING: As the name implies this operation does *not* +// perform a write barrier on value, and so this operation may +// hide pointers from the GC. Use with care and sparingly. +// It is safe to use with values not found in the Go heap. +// +//go:nosplit +func (u *UnsafePointer) CompareAndSwapNoWB(old, new unsafe.Pointer) bool { + return Casp1(&u.value, old, new) +} + +// Pointer is an atomic pointer of type *T. +type Pointer[T any] struct { + u UnsafePointer +} + +// Load accesses and returns the value atomically. +// +//go:nosplit +func (p *Pointer[T]) Load() *T { + return (*T)(p.u.Load()) +} + +// StoreNoWB updates the value atomically. +// +// WARNING: As the name implies this operation does *not* +// perform a write barrier on value, and so this operation may +// hide pointers from the GC. Use with care and sparingly. +// It is safe to use with values not found in the Go heap. +// +//go:nosplit +func (p *Pointer[T]) StoreNoWB(value *T) { + p.u.StoreNoWB(unsafe.Pointer(value)) +} + +// CompareAndSwapNoWB atomically (with respect to other methods) +// compares u's value with old, and if they're equal, +// swaps u's value with new. +// +// Returns true if the operation succeeded. +// +// WARNING: As the name implies this operation does *not* +// perform a write barrier on value, and so this operation may +// hide pointers from the GC. Use with care and sparingly. +// It is safe to use with values not found in the Go heap. +// +//go:nosplit +func (p *Pointer[T]) CompareAndSwapNoWB(old, new *T) bool { + return p.u.CompareAndSwapNoWB(unsafe.Pointer(old), unsafe.Pointer(new)) +} + +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} + +// align64 may be added to structs that must be 64-bit aligned. +// This struct is recognized by a special case in the compiler +// and will not work if copied to any other package. +type align64 struct{} diff --git a/src/runtime/internal/atomic/types_64bit.go b/src/runtime/internal/atomic/types_64bit.go new file mode 100644 index 00000000000000..fda2de9cc4ccaa --- /dev/null +++ b/src/runtime/internal/atomic/types_64bit.go @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm + +package atomic + +// LoadAcquire is a partially unsynchronized version +// of Load that relaxes ordering constraints. Other threads +// may observe operations that precede this operation to +// occur after it, but no operation that occurs after it +// on this thread can be observed to occur before it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uint64) LoadAcquire() uint64 { + return LoadAcq64(&u.value) +} + +// StoreRelease is a partially unsynchronized version +// of Store that relaxes ordering constraints. Other threads +// may observe operations that occur after this operation to +// precede it, but no operation that precedes it +// on this thread can be observed to occur after it. +// +// WARNING: Use sparingly and with great care. +// +//go:nosplit +func (u *Uint64) StoreRelease(value uint64) { + StoreRel64(&u.value, value) +} diff --git a/src/runtime/internal/math/math.go b/src/runtime/internal/math/math.go index b6bd12d3e8b66d..c3fac366be0210 100644 --- a/src/runtime/internal/math/math.go +++ b/src/runtime/internal/math/math.go @@ -4,14 +4,14 @@ package math -import "runtime/internal/sys" +import "internal/goarch" const MaxUintptr = ^uintptr(0) // MulUintptr returns a * b and whether the multiplication overflowed. // On supported platforms this is an intrinsic lowered by the compiler. func MulUintptr(a, b uintptr) (uintptr, bool) { - if a|b < 1<<(4*sys.PtrSize) || a == 0 { + if a|b < 1<<(4*goarch.PtrSize) || a == 0 { return a * b, false } overflow := b > MaxUintptr/a diff --git a/src/runtime/internal/sys/arch.go b/src/runtime/internal/sys/arch.go deleted file mode 100644 index 3c99a2f7da0831..00000000000000 --- a/src/runtime/internal/sys/arch.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -type ArchFamilyType int - -const ( - AMD64 ArchFamilyType = iota - ARM - ARM64 - I386 - MIPS - MIPS64 - PPC64 - RISCV64 - S390X - WASM -) - -// PtrSize is the size of a pointer in bytes - unsafe.Sizeof(uintptr(0)) but as an ideal constant. -// It is also the size of the machine's native word size (that is, 4 on 32-bit systems, 8 on 64-bit). -const PtrSize = 4 << (^uintptr(0) >> 63) - -// AIX requires a larger stack for syscalls. -const StackGuardMultiplier = StackGuardMultiplierDefault*(1-GoosAix) + 2*GoosAix - -// ArchFamily is the architecture family (AMD64, ARM, ...) -const ArchFamily ArchFamilyType = _ArchFamily - -// BigEndian reports whether the architecture is big-endian. -const BigEndian = GoarchArmbe|GoarchArm64be|GoarchMips|GoarchMips64|GoarchPpc|GoarchPpc64|GoarchS390|GoarchS390x|GoarchSparc|GoarchSparc64 == 1 - -// DefaultPhysPageSize is the default physical page size. -const DefaultPhysPageSize = _DefaultPhysPageSize - -// PCQuantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). -// The various PC tables record PC deltas pre-divided by PCQuantum. -const PCQuantum = _PCQuantum - -// Int64Align is the required alignment for a 64-bit integer (4 on 32-bit systems, 8 on 64-bit). -const Int64Align = PtrSize - -// MinFrameSize is the size of the system-reserved words at the bottom -// of a frame (just above the architectural stack pointer). -// It is zero on x86 and PtrSize on most non-x86 (LR-based) systems. -// On PowerPC it is larger, to cover three more reserved words: -// the compiler word, the link editor word, and the TOC save word. -const MinFrameSize = _MinFrameSize - -// StackAlign is the required alignment of the SP register. -// The stack must be at least word aligned, but some architectures require more. -const StackAlign = _StackAlign diff --git a/src/runtime/internal/sys/arch_386.go b/src/runtime/internal/sys/arch_386.go deleted file mode 100644 index 1ebce3435e4be3..00000000000000 --- a/src/runtime/internal/sys/arch_386.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = I386 - _DefaultPhysPageSize = 4096 - _PCQuantum = 1 - _MinFrameSize = 0 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_amd64.go b/src/runtime/internal/sys/arch_amd64.go deleted file mode 100644 index 7f003d0f1d5034..00000000000000 --- a/src/runtime/internal/sys/arch_amd64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = AMD64 - _DefaultPhysPageSize = 4096 - _PCQuantum = 1 - _MinFrameSize = 0 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_arm.go b/src/runtime/internal/sys/arch_arm.go deleted file mode 100644 index ef2048bb71a5dd..00000000000000 --- a/src/runtime/internal/sys/arch_arm.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = ARM - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 4 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_arm64.go b/src/runtime/internal/sys/arch_arm64.go deleted file mode 100644 index b9f2f7b1fe226a..00000000000000 --- a/src/runtime/internal/sys/arch_arm64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = ARM64 - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 8 - _StackAlign = 16 -) diff --git a/src/runtime/internal/sys/arch_mips.go b/src/runtime/internal/sys/arch_mips.go deleted file mode 100644 index 4cb0eebea7bb5c..00000000000000 --- a/src/runtime/internal/sys/arch_mips.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = MIPS - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 4 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_mips64.go b/src/runtime/internal/sys/arch_mips64.go deleted file mode 100644 index 57636ac4a4e0eb..00000000000000 --- a/src/runtime/internal/sys/arch_mips64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = MIPS64 - _DefaultPhysPageSize = 16384 - _PCQuantum = 4 - _MinFrameSize = 8 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_mips64le.go b/src/runtime/internal/sys/arch_mips64le.go deleted file mode 100644 index 57636ac4a4e0eb..00000000000000 --- a/src/runtime/internal/sys/arch_mips64le.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = MIPS64 - _DefaultPhysPageSize = 16384 - _PCQuantum = 4 - _MinFrameSize = 8 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_mipsle.go b/src/runtime/internal/sys/arch_mipsle.go deleted file mode 100644 index 4240f5ce47bb7f..00000000000000 --- a/src/runtime/internal/sys/arch_mipsle.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = MIPS - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 4 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_ppc64.go b/src/runtime/internal/sys/arch_ppc64.go deleted file mode 100644 index 1869213ce241ee..00000000000000 --- a/src/runtime/internal/sys/arch_ppc64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = PPC64 - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 32 - _StackAlign = 16 -) diff --git a/src/runtime/internal/sys/arch_ppc64le.go b/src/runtime/internal/sys/arch_ppc64le.go deleted file mode 100644 index 1869213ce241ee..00000000000000 --- a/src/runtime/internal/sys/arch_ppc64le.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = PPC64 - _DefaultPhysPageSize = 65536 - _PCQuantum = 4 - _MinFrameSize = 32 - _StackAlign = 16 -) diff --git a/src/runtime/internal/sys/arch_riscv64.go b/src/runtime/internal/sys/arch_riscv64.go deleted file mode 100644 index 360d236e32072a..00000000000000 --- a/src/runtime/internal/sys/arch_riscv64.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = RISCV64 - _DefaultPhysPageSize = 4096 - _PCQuantum = 4 - _MinFrameSize = 8 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_s390x.go b/src/runtime/internal/sys/arch_s390x.go deleted file mode 100644 index e33e0b7f2b890b..00000000000000 --- a/src/runtime/internal/sys/arch_s390x.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = S390X - _DefaultPhysPageSize = 4096 - _PCQuantum = 2 - _MinFrameSize = 8 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/arch_wasm.go b/src/runtime/internal/sys/arch_wasm.go deleted file mode 100644 index ee919ff9e63499..00000000000000 --- a/src/runtime/internal/sys/arch_wasm.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sys - -const ( - _ArchFamily = WASM - _DefaultPhysPageSize = 65536 - _PCQuantum = 1 - _MinFrameSize = 0 - _StackAlign = PtrSize -) diff --git a/src/runtime/internal/sys/consts.go b/src/runtime/internal/sys/consts.go new file mode 100644 index 00000000000000..c6037165801356 --- /dev/null +++ b/src/runtime/internal/sys/consts.go @@ -0,0 +1,34 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sys + +import ( + "internal/goarch" + "internal/goos" +) + +// AIX requires a larger stack for syscalls. +const StackGuardMultiplier = 1*(1-goos.IsAix) + 2*goos.IsAix + +// DefaultPhysPageSize is the default physical page size. +const DefaultPhysPageSize = goarch.DefaultPhysPageSize + +// PCQuantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). +// The various PC tables record PC deltas pre-divided by PCQuantum. +const PCQuantum = goarch.PCQuantum + +// Int64Align is the required alignment for a 64-bit integer (4 on 32-bit systems, 8 on 64-bit). +const Int64Align = goarch.PtrSize + +// MinFrameSize is the size of the system-reserved words at the bottom +// of a frame (just above the architectural stack pointer). +// It is zero on x86 and PtrSize on most non-x86 (LR-based) systems. +// On PowerPC it is larger, to cover three more reserved words: +// the compiler word, the link editor word, and the TOC save word. +const MinFrameSize = goarch.MinFrameSize + +// StackAlign is the required alignment of the SP register. +// The stack must be at least word aligned, but some architectures require more. +const StackAlign = goarch.StackAlign diff --git a/src/runtime/internal/sys/gengoos.go b/src/runtime/internal/sys/gengoos.go deleted file mode 100644 index ffe962f71df61c..00000000000000 --- a/src/runtime/internal/sys/gengoos.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ignore -// +build ignore - -package main - -import ( - "bytes" - "fmt" - "log" - "os" - "strconv" - "strings" -) - -var gooses, goarches []string - -func main() { - data, err := os.ReadFile("../../../go/build/syslist.go") - if err != nil { - log.Fatal(err) - } - const ( - goosPrefix = `const goosList = ` - goarchPrefix = `const goarchList = ` - ) - for _, line := range strings.Split(string(data), "\n") { - if strings.HasPrefix(line, goosPrefix) { - text, err := strconv.Unquote(strings.TrimPrefix(line, goosPrefix)) - if err != nil { - log.Fatalf("parsing goosList: %v", err) - } - gooses = strings.Fields(text) - } - if strings.HasPrefix(line, goarchPrefix) { - text, err := strconv.Unquote(strings.TrimPrefix(line, goarchPrefix)) - if err != nil { - log.Fatalf("parsing goarchList: %v", err) - } - goarches = strings.Fields(text) - } - } - - for _, target := range gooses { - if target == "nacl" { - continue - } - var tags []string - if target == "linux" { - tags = append(tags, "!android") // must explicitly exclude android for linux - } - if target == "solaris" { - tags = append(tags, "!illumos") // must explicitly exclude illumos for solaris - } - if target == "darwin" { - tags = append(tags, "!ios") // must explicitly exclude ios for darwin - } - tags = append(tags, target) // must explicitly include target for bootstrapping purposes - var buf bytes.Buffer - fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") - fmt.Fprintf(&buf, "//go:build %s\n", strings.Join(tags, " && ")) - fmt.Fprintf(&buf, "// +build %s\n\n", strings.Join(tags, ",")) - fmt.Fprintf(&buf, "package sys\n\n") - fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target) - for _, goos := range gooses { - value := 0 - if goos == target { - value = 1 - } - fmt.Fprintf(&buf, "const Goos%s = %d\n", strings.Title(goos), value) - } - err := os.WriteFile("zgoos_"+target+".go", buf.Bytes(), 0666) - if err != nil { - log.Fatal(err) - } - } - - for _, target := range goarches { - if target == "amd64p32" { - continue - } - var buf bytes.Buffer - fmt.Fprintf(&buf, "// Code generated by gengoos.go using 'go generate'. DO NOT EDIT.\n\n") - fmt.Fprintf(&buf, "//go:build %s\n", target) - fmt.Fprintf(&buf, "// +build %s\n\n", target) // must explicitly include target for bootstrapping purposes - fmt.Fprintf(&buf, "package sys\n\n") - fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) - for _, goarch := range goarches { - value := 0 - if goarch == target { - value = 1 - } - fmt.Fprintf(&buf, "const Goarch%s = %d\n", strings.Title(goarch), value) - } - err := os.WriteFile("zgoarch_"+target+".go", buf.Bytes(), 0666) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/src/runtime/internal/sys/intrinsics.go b/src/runtime/internal/sys/intrinsics.go index e76d8dd064616a..5af49011e9e655 100644 --- a/src/runtime/internal/sys/intrinsics.go +++ b/src/runtime/internal/sys/intrinsics.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !386 -// +build !386 // TODO finish intrinsifying 386, deadcode the assembly, remove build tags, merge w/ intrinsics_common // TODO replace all uses of CtzXX with TrailingZerosXX; they are the same. diff --git a/src/runtime/internal/sys/intrinsics_common.go b/src/runtime/internal/sys/intrinsics_common.go index 818d75ecc53ee6..48d9759ca9c54c 100644 --- a/src/runtime/internal/sys/intrinsics_common.go +++ b/src/runtime/internal/sys/intrinsics_common.go @@ -141,3 +141,18 @@ func TrailingZeros8(x uint8) int { func Len8(x uint8) int { return int(len8tab[x]) } + +// Prefetch prefetches data from memory addr to cache +// +// AMD64: Produce PREFETCHT0 instruction +// +// ARM64: Produce PRFM instruction with PLDL1KEEP option +func Prefetch(addr uintptr) {} + +// PrefetchStreamed prefetches data from memory addr, with a hint that this data is being streamed. +// That is, it is likely to be accessed very soon, but only once. If possible, this will avoid polluting the cache. +// +// AMD64: Produce PREFETCHNTA instruction +// +// ARM64: Produce PRFM instruction with PLDL1STRM option +func PrefetchStreamed(addr uintptr) {} diff --git a/src/runtime/internal/sys/intrinsics_stubs.go b/src/runtime/internal/sys/intrinsics_stubs.go index bf1494d48aac7b..a020652f7604b3 100644 --- a/src/runtime/internal/sys/intrinsics_stubs.go +++ b/src/runtime/internal/sys/intrinsics_stubs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 -// +build 386 package sys diff --git a/src/runtime/internal/sys/nih.go b/src/runtime/internal/sys/nih.go new file mode 100644 index 00000000000000..17eab67345b06e --- /dev/null +++ b/src/runtime/internal/sys/nih.go @@ -0,0 +1,41 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sys + +// NOTE: keep in sync with cmd/compile/internal/types.CalcSize +// to make the compiler recognize this as an intrinsic type. +type nih struct{} + +// NotInHeap is a type must never be allocated from the GC'd heap or on the stack, +// and is called not-in-heap. +// +// Other types can embed NotInHeap to make it not-in-heap. Specifically, pointers +// to these types must always fail the `runtime.inheap` check. The type may be used +// for global variables, or for objects in unmanaged memory (e.g., allocated with +// `sysAlloc`, `persistentalloc`, r`fixalloc`, or from a manually-managed span). +// +// Specifically: +// +// 1. `new(T)`, `make([]T)`, `append([]T, ...)` and implicit heap +// allocation of T are disallowed. (Though implicit allocations are +// disallowed in the runtime anyway.) +// +// 2. A pointer to a regular type (other than `unsafe.Pointer`) cannot be +// converted to a pointer to a not-in-heap type, even if they have the +// same underlying type. +// +// 3. Any type that containing a not-in-heap type is itself considered as not-in-heap. +// +// - Structs and arrays are not-in-heap if their elements are not-in-heap. +// - Maps and channels contains no-in-heap types are disallowed. +// +// 4. Write barriers on pointers to not-in-heap types can be omitted. +// +// The last point is the real benefit of NotInHeap. The runtime uses +// it for low-level internal structures to avoid memory barriers in the +// scheduler and the memory allocator where they are illegal or simply +// inefficient. This mechanism is reasonably safe and does not compromise +// the readability of the runtime. +type NotInHeap struct{ _ nih } diff --git a/src/runtime/internal/sys/sys.go b/src/runtime/internal/sys/sys.go index 9d9ac4507f6644..694101d36fda41 100644 --- a/src/runtime/internal/sys/sys.go +++ b/src/runtime/internal/sys/sys.go @@ -5,11 +5,3 @@ // package sys contains system- and configuration- and architecture-specific // constants used by the runtime. package sys - -// The next line makes 'go generate' write the zgo*.go files with -// per-OS and per-arch information, including constants -// named Goos$GOOS and Goarch$GOARCH for every -// known GOOS and GOARCH. The constant is 1 on the -// current system, 0 otherwise; multiplying by them is -// useful for defining GOOS- or GOARCH-specific constants. -//go:generate go run gengoos.go diff --git a/src/runtime/internal/sys/zgoarch_386.go b/src/runtime/internal/sys/zgoarch_386.go deleted file mode 100644 index 5b189e7e738f46..00000000000000 --- a/src/runtime/internal/sys/zgoarch_386.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build 386 -// +build 386 - -package sys - -const GOARCH = `386` - -const Goarch386 = 1 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_amd64.go b/src/runtime/internal/sys/zgoarch_amd64.go deleted file mode 100644 index 312977d07923f8..00000000000000 --- a/src/runtime/internal/sys/zgoarch_amd64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build amd64 -// +build amd64 - -package sys - -const GOARCH = `amd64` - -const Goarch386 = 0 -const GoarchAmd64 = 1 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_arm.go b/src/runtime/internal/sys/zgoarch_arm.go deleted file mode 100644 index 5781870324e0b9..00000000000000 --- a/src/runtime/internal/sys/zgoarch_arm.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build arm -// +build arm - -package sys - -const GOARCH = `arm` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 1 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_arm64.go b/src/runtime/internal/sys/zgoarch_arm64.go deleted file mode 100644 index f72a1f2161c1df..00000000000000 --- a/src/runtime/internal/sys/zgoarch_arm64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build arm64 -// +build arm64 - -package sys - -const GOARCH = `arm64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 1 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_arm64be.go b/src/runtime/internal/sys/zgoarch_arm64be.go deleted file mode 100644 index e8056460587ca1..00000000000000 --- a/src/runtime/internal/sys/zgoarch_arm64be.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build arm64be -// +build arm64be - -package sys - -const GOARCH = `arm64be` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 1 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_armbe.go b/src/runtime/internal/sys/zgoarch_armbe.go deleted file mode 100644 index d8d4e56d9a4e35..00000000000000 --- a/src/runtime/internal/sys/zgoarch_armbe.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build armbe -// +build armbe - -package sys - -const GOARCH = `armbe` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 1 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_loong64.go b/src/runtime/internal/sys/zgoarch_loong64.go deleted file mode 100644 index 6f35eb44a3b8a6..00000000000000 --- a/src/runtime/internal/sys/zgoarch_loong64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build loong64 -// +build loong64 - -package sys - -const GOARCH = `loong64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 1 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips.go b/src/runtime/internal/sys/zgoarch_mips.go deleted file mode 100644 index bd58a92a0e4e93..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mips.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mips -// +build mips - -package sys - -const GOARCH = `mips` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 1 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64.go b/src/runtime/internal/sys/zgoarch_mips64.go deleted file mode 100644 index 8e4a3dcd526d85..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mips64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mips64 -// +build mips64 - -package sys - -const GOARCH = `mips64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 1 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64le.go b/src/runtime/internal/sys/zgoarch_mips64le.go deleted file mode 100644 index d8e00339ea81aa..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mips64le.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mips64le -// +build mips64le - -package sys - -const GOARCH = `mips64le` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 1 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64p32.go b/src/runtime/internal/sys/zgoarch_mips64p32.go deleted file mode 100644 index 8549cc0ba3443f..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mips64p32.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mips64p32 -// +build mips64p32 - -package sys - -const GOARCH = `mips64p32` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 1 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64p32le.go b/src/runtime/internal/sys/zgoarch_mips64p32le.go deleted file mode 100644 index 667b6fe514b0bd..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mips64p32le.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mips64p32le -// +build mips64p32le - -package sys - -const GOARCH = `mips64p32le` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 1 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_mipsle.go b/src/runtime/internal/sys/zgoarch_mipsle.go deleted file mode 100644 index 8bedb2bb901969..00000000000000 --- a/src/runtime/internal/sys/zgoarch_mipsle.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build mipsle -// +build mipsle - -package sys - -const GOARCH = `mipsle` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 1 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_ppc.go b/src/runtime/internal/sys/zgoarch_ppc.go deleted file mode 100644 index fe2196a32789e5..00000000000000 --- a/src/runtime/internal/sys/zgoarch_ppc.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build ppc -// +build ppc - -package sys - -const GOARCH = `ppc` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 1 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_ppc64.go b/src/runtime/internal/sys/zgoarch_ppc64.go deleted file mode 100644 index bd7cc43de3974d..00000000000000 --- a/src/runtime/internal/sys/zgoarch_ppc64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build ppc64 -// +build ppc64 - -package sys - -const GOARCH = `ppc64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 1 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_ppc64le.go b/src/runtime/internal/sys/zgoarch_ppc64le.go deleted file mode 100644 index e101892401650d..00000000000000 --- a/src/runtime/internal/sys/zgoarch_ppc64le.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build ppc64le -// +build ppc64le - -package sys - -const GOARCH = `ppc64le` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 1 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_riscv.go b/src/runtime/internal/sys/zgoarch_riscv.go deleted file mode 100644 index 559f86071a7ea8..00000000000000 --- a/src/runtime/internal/sys/zgoarch_riscv.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build riscv -// +build riscv - -package sys - -const GOARCH = `riscv` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 1 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_riscv64.go b/src/runtime/internal/sys/zgoarch_riscv64.go deleted file mode 100644 index 8485a94b3dd910..00000000000000 --- a/src/runtime/internal/sys/zgoarch_riscv64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build riscv64 -// +build riscv64 - -package sys - -const GOARCH = `riscv64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 1 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_s390.go b/src/runtime/internal/sys/zgoarch_s390.go deleted file mode 100644 index 4c4569e3762159..00000000000000 --- a/src/runtime/internal/sys/zgoarch_s390.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build s390 -// +build s390 - -package sys - -const GOARCH = `s390` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 1 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_s390x.go b/src/runtime/internal/sys/zgoarch_s390x.go deleted file mode 100644 index e50d2edbb5515c..00000000000000 --- a/src/runtime/internal/sys/zgoarch_s390x.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build s390x -// +build s390x - -package sys - -const GOARCH = `s390x` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 1 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_sparc.go b/src/runtime/internal/sys/zgoarch_sparc.go deleted file mode 100644 index 0d08752c7bc590..00000000000000 --- a/src/runtime/internal/sys/zgoarch_sparc.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build sparc -// +build sparc - -package sys - -const GOARCH = `sparc` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 1 -const GoarchSparc64 = 0 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_sparc64.go b/src/runtime/internal/sys/zgoarch_sparc64.go deleted file mode 100644 index ba405bbf3557aa..00000000000000 --- a/src/runtime/internal/sys/zgoarch_sparc64.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build sparc64 -// +build sparc64 - -package sys - -const GOARCH = `sparc64` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 1 -const GoarchWasm = 0 diff --git a/src/runtime/internal/sys/zgoarch_wasm.go b/src/runtime/internal/sys/zgoarch_wasm.go deleted file mode 100644 index 7c3e5afd1ee241..00000000000000 --- a/src/runtime/internal/sys/zgoarch_wasm.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build wasm -// +build wasm - -package sys - -const GOARCH = `wasm` - -const Goarch386 = 0 -const GoarchAmd64 = 0 -const GoarchAmd64p32 = 0 -const GoarchArm = 0 -const GoarchArmbe = 0 -const GoarchArm64 = 0 -const GoarchArm64be = 0 -const GoarchPpc64 = 0 -const GoarchPpc64le = 0 -const GoarchLoong64 = 0 -const GoarchMips = 0 -const GoarchMipsle = 0 -const GoarchMips64 = 0 -const GoarchMips64le = 0 -const GoarchMips64p32 = 0 -const GoarchMips64p32le = 0 -const GoarchPpc = 0 -const GoarchRiscv = 0 -const GoarchRiscv64 = 0 -const GoarchS390 = 0 -const GoarchS390x = 0 -const GoarchSparc = 0 -const GoarchSparc64 = 0 -const GoarchWasm = 1 diff --git a/src/runtime/internal/sys/zgoos_aix.go b/src/runtime/internal/sys/zgoos_aix.go deleted file mode 100644 index f3b907471f6e4a..00000000000000 --- a/src/runtime/internal/sys/zgoos_aix.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build aix -// +build aix - -package sys - -const GOOS = `aix` - -const GoosAix = 1 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_android.go b/src/runtime/internal/sys/zgoos_android.go deleted file mode 100644 index e28baf7c48103e..00000000000000 --- a/src/runtime/internal/sys/zgoos_android.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build android -// +build android - -package sys - -const GOOS = `android` - -const GoosAix = 0 -const GoosAndroid = 1 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_darwin.go b/src/runtime/internal/sys/zgoos_darwin.go deleted file mode 100644 index 3c7f7b543eecdd..00000000000000 --- a/src/runtime/internal/sys/zgoos_darwin.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build !ios && darwin -// +build !ios,darwin - -package sys - -const GOOS = `darwin` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 1 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_dragonfly.go b/src/runtime/internal/sys/zgoos_dragonfly.go deleted file mode 100644 index f844d29e2a1e55..00000000000000 --- a/src/runtime/internal/sys/zgoos_dragonfly.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build dragonfly -// +build dragonfly - -package sys - -const GOOS = `dragonfly` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 1 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_freebsd.go b/src/runtime/internal/sys/zgoos_freebsd.go deleted file mode 100644 index 8999a2797af0cb..00000000000000 --- a/src/runtime/internal/sys/zgoos_freebsd.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build freebsd -// +build freebsd - -package sys - -const GOOS = `freebsd` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 1 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_hurd.go b/src/runtime/internal/sys/zgoos_hurd.go deleted file mode 100644 index a546488bf8999d..00000000000000 --- a/src/runtime/internal/sys/zgoos_hurd.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build hurd -// +build hurd - -package sys - -const GOOS = `hurd` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 1 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_illumos.go b/src/runtime/internal/sys/zgoos_illumos.go deleted file mode 100644 index 02a4ca06e82b1b..00000000000000 --- a/src/runtime/internal/sys/zgoos_illumos.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build illumos -// +build illumos - -package sys - -const GOOS = `illumos` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 1 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_ios.go b/src/runtime/internal/sys/zgoos_ios.go deleted file mode 100644 index 033eec623df053..00000000000000 --- a/src/runtime/internal/sys/zgoos_ios.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build ios -// +build ios - -package sys - -const GOOS = `ios` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 1 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_js.go b/src/runtime/internal/sys/zgoos_js.go deleted file mode 100644 index 28226ad60ad2dc..00000000000000 --- a/src/runtime/internal/sys/zgoos_js.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build js -// +build js - -package sys - -const GOOS = `js` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 1 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_linux.go b/src/runtime/internal/sys/zgoos_linux.go deleted file mode 100644 index 01546e4b9f2616..00000000000000 --- a/src/runtime/internal/sys/zgoos_linux.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build !android && linux -// +build !android,linux - -package sys - -const GOOS = `linux` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 1 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_netbsd.go b/src/runtime/internal/sys/zgoos_netbsd.go deleted file mode 100644 index 9d658b20ee200d..00000000000000 --- a/src/runtime/internal/sys/zgoos_netbsd.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build netbsd -// +build netbsd - -package sys - -const GOOS = `netbsd` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 1 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_openbsd.go b/src/runtime/internal/sys/zgoos_openbsd.go deleted file mode 100644 index 0f55454a953fec..00000000000000 --- a/src/runtime/internal/sys/zgoos_openbsd.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build openbsd -// +build openbsd - -package sys - -const GOOS = `openbsd` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 1 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_plan9.go b/src/runtime/internal/sys/zgoos_plan9.go deleted file mode 100644 index d0347464d6d25d..00000000000000 --- a/src/runtime/internal/sys/zgoos_plan9.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build plan9 -// +build plan9 - -package sys - -const GOOS = `plan9` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 1 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_solaris.go b/src/runtime/internal/sys/zgoos_solaris.go deleted file mode 100644 index 05c3007e2c2260..00000000000000 --- a/src/runtime/internal/sys/zgoos_solaris.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build !illumos && solaris -// +build !illumos,solaris - -package sys - -const GOOS = `solaris` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 1 -const GoosWindows = 0 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_windows.go b/src/runtime/internal/sys/zgoos_windows.go deleted file mode 100644 index 7d07fa3a451150..00000000000000 --- a/src/runtime/internal/sys/zgoos_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build windows -// +build windows - -package sys - -const GOOS = `windows` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 1 -const GoosZos = 0 diff --git a/src/runtime/internal/sys/zgoos_zos.go b/src/runtime/internal/sys/zgoos_zos.go deleted file mode 100644 index d6e5b9b0cb2b98..00000000000000 --- a/src/runtime/internal/sys/zgoos_zos.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by gengoos.go using 'go generate'. DO NOT EDIT. - -//go:build zos -// +build zos - -package sys - -const GOOS = `zos` - -const GoosAix = 0 -const GoosAndroid = 0 -const GoosDarwin = 0 -const GoosDragonfly = 0 -const GoosFreebsd = 0 -const GoosHurd = 0 -const GoosIllumos = 0 -const GoosIos = 0 -const GoosJs = 0 -const GoosLinux = 0 -const GoosNacl = 0 -const GoosNetbsd = 0 -const GoosOpenbsd = 0 -const GoosPlan9 = 0 -const GoosSolaris = 0 -const GoosWindows = 0 -const GoosZos = 1 diff --git a/src/runtime/internal/syscall/asm_linux_386.s b/src/runtime/internal/syscall/asm_linux_386.s new file mode 100644 index 00000000000000..15aae4d8bd98ea --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_386.s @@ -0,0 +1,34 @@ +// Copyright 2022 The Go 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" + +// See ../sys_linux_386.s for the reason why we always use int 0x80 +// instead of the glibc-specific "CALL 0x10(GS)". +#define INVOKE_SYSCALL INT $0x80 + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +// +// Syscall # in AX, args in BX CX DX SI DI BP, return in AX +TEXT ·Syscall6(SB),NOSPLIT,$0-40 + MOVL num+0(FP), AX // syscall entry + MOVL a1+4(FP), BX + MOVL a2+8(FP), CX + MOVL a3+12(FP), DX + MOVL a4+16(FP), SI + MOVL a5+20(FP), DI + MOVL a6+24(FP), BP + INVOKE_SYSCALL + CMPL AX, $0xfffff001 + JLS ok + MOVL $-1, r1+28(FP) + MOVL $0, r2+32(FP) + NEGL AX + MOVL AX, errno+36(FP) + RET +ok: + MOVL AX, r1+28(FP) + MOVL DX, r2+32(FP) + MOVL $0, errno+36(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_amd64.s b/src/runtime/internal/syscall/asm_linux_amd64.s new file mode 100644 index 00000000000000..3740ef1beb570d --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_amd64.s @@ -0,0 +1,47 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +// +// We need to convert to the syscall ABI. +// +// arg | ABIInternal | Syscall +// --------------------------- +// num | AX | AX +// a1 | BX | DI +// a2 | CX | SI +// a3 | DI | DX +// a4 | SI | R10 +// a5 | R8 | R8 +// a6 | R9 | R9 +// +// r1 | AX | AX +// r2 | BX | DX +// err | CX | part of AX +// +// Note that this differs from "standard" ABI convention, which would pass 4th +// arg in CX, not R10. +TEXT ·Syscall6(SB),NOSPLIT,$0 + // a6 already in R9. + // a5 already in R8. + MOVQ SI, R10 // a4 + MOVQ DI, DX // a3 + MOVQ CX, SI // a2 + MOVQ BX, DI // a1 + // num already in AX. + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS ok + NEGQ AX + MOVQ AX, CX // errno + MOVQ $-1, AX // r1 + MOVQ $0, BX // r2 + RET +ok: + // r1 already in AX. + MOVQ DX, BX // r2 + MOVQ $0, CX // errno + RET diff --git a/src/runtime/internal/syscall/asm_linux_arm.s b/src/runtime/internal/syscall/asm_linux_arm.s new file mode 100644 index 00000000000000..dbf1826d94238e --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_arm.s @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-40 + MOVW num+0(FP), R7 // syscall entry + MOVW a1+4(FP), R0 + MOVW a2+8(FP), R1 + MOVW a3+12(FP), R2 + MOVW a4+16(FP), R3 + MOVW a5+20(FP), R4 + MOVW a6+24(FP), R5 + SWI $0 + MOVW $0xfffff001, R6 + CMP R6, R0 + BLS ok + MOVW $-1, R1 + MOVW R1, r1+28(FP) + MOVW $0, R2 + MOVW R2, r2+32(FP) + RSB $0, R0, R0 + MOVW R0, errno+36(FP) + RET +ok: + MOVW R0, r1+28(FP) + MOVW R1, r2+32(FP) + MOVW $0, R0 + MOVW R0, errno+36(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_arm64.s b/src/runtime/internal/syscall/asm_linux_arm64.s new file mode 100644 index 00000000000000..83e862ff727c44 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_arm64.s @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOVD num+0(FP), R8 // syscall entry + MOVD a1+8(FP), R0 + MOVD a2+16(FP), R1 + MOVD a3+24(FP), R2 + MOVD a4+32(FP), R3 + MOVD a5+40(FP), R4 + MOVD a6+48(FP), R5 + SVC + CMN $4095, R0 + BCC ok + MOVD $-1, R4 + MOVD R4, r1+56(FP) + MOVD ZR, r2+64(FP) + NEG R0, R0 + MOVD R0, errno+72(FP) + RET +ok: + MOVD R0, r1+56(FP) + MOVD R1, r2+64(FP) + MOVD ZR, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_loong64.s b/src/runtime/internal/syscall/asm_linux_loong64.s new file mode 100644 index 00000000000000..d6a33f90a70a27 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_loong64.s @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOVV num+0(FP), R11 // syscall entry + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV a3+24(FP), R6 + MOVV a4+32(FP), R7 + MOVV a5+40(FP), R8 + MOVV a6+48(FP), R9 + SYSCALL + MOVW $-4096, R12 + BGEU R12, R4, ok + MOVV $-1, R12 + MOVV R12, r1+56(FP) + MOVV R0, r2+64(FP) + SUBVU R4, R0, R4 + MOVV R4, errno+72(FP) + RET +ok: + MOVV R4, r1+56(FP) + MOVV R0, r2+64(FP) // r2 is not used. Always set to 0. + MOVV R0, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_mips64x.s b/src/runtime/internal/syscall/asm_linux_mips64x.s new file mode 100644 index 00000000000000..0e88a2d8acd376 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_mips64x.s @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (mips64 || mips64le) + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOVV num+0(FP), R2 // syscall entry + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV a3+24(FP), R6 + MOVV a4+32(FP), R7 + MOVV a5+40(FP), R8 + MOVV a6+48(FP), R9 + SYSCALL + BEQ R7, ok + MOVV $-1, R1 + MOVV R1, r1+56(FP) + MOVV R0, r2+64(FP) + MOVV R2, errno+72(FP) + RET +ok: + MOVV R2, r1+56(FP) + MOVV R3, r2+64(FP) + MOVV R0, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_mipsx.s b/src/runtime/internal/syscall/asm_linux_mipsx.s new file mode 100644 index 00000000000000..050029eaa13992 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_mipsx.s @@ -0,0 +1,34 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (mips || mipsle) + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +// +// The 5th and 6th arg go at sp+16, sp+20. +// Note that frame size of 20 means that 24 bytes gets reserved on stack. +TEXT ·Syscall6(SB),NOSPLIT,$20-40 + MOVW num+0(FP), R2 // syscall entry + MOVW a1+4(FP), R4 + MOVW a2+8(FP), R5 + MOVW a3+12(FP), R6 + MOVW a4+16(FP), R7 + MOVW a5+20(FP), R8 + MOVW a6+24(FP), R9 + MOVW R8, 16(R29) + MOVW R9, 20(R29) + SYSCALL + BEQ R7, ok + MOVW $-1, R1 + MOVW R1, r1+28(FP) + MOVW R0, r2+32(FP) + MOVW R2, errno+36(FP) + RET +ok: + MOVW R2, r1+28(FP) + MOVW R3, r2+32(FP) + MOVW R0, errno+36(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_ppc64x.s b/src/runtime/internal/syscall/asm_linux_ppc64x.s new file mode 100644 index 00000000000000..8cf8737df884e1 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_ppc64x.s @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (ppc64 || ppc64le) + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOVD num+0(FP), R9 // syscall entry + MOVD a1+8(FP), R3 + MOVD a2+16(FP), R4 + MOVD a3+24(FP), R5 + MOVD a4+32(FP), R6 + MOVD a5+40(FP), R7 + MOVD a6+48(FP), R8 + SYSCALL R9 + MOVD R0, r2+64(FP) // r2 is not used. Always set to 0. + BVC ok + MOVD $-1, R4 + MOVD R4, r1+56(FP) + MOVD R3, errno+72(FP) + RET +ok: + MOVD R3, r1+56(FP) + MOVD R0, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_riscv64.s b/src/runtime/internal/syscall/asm_linux_riscv64.s new file mode 100644 index 00000000000000..a8652fdd6b33dc --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_riscv64.s @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOV num+0(FP), A7 // syscall entry + MOV a1+8(FP), A0 + MOV a2+16(FP), A1 + MOV a3+24(FP), A2 + MOV a4+32(FP), A3 + MOV a5+40(FP), A4 + MOV a6+48(FP), A5 + ECALL + MOV $-4096, T0 + BLTU T0, A0, err + MOV A0, r1+56(FP) + MOV A1, r2+64(FP) + MOV ZERO, errno+72(FP) + RET +err: + MOV $-1, T0 + MOV T0, r1+56(FP) + MOV ZERO, r2+64(FP) + SUB A0, ZERO, A0 + MOV A0, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/asm_linux_s390x.s b/src/runtime/internal/syscall/asm_linux_s390x.s new file mode 100644 index 00000000000000..1b27f293907295 --- /dev/null +++ b/src/runtime/internal/syscall/asm_linux_s390x.s @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + MOVD num+0(FP), R1 // syscall entry + MOVD a1+8(FP), R2 + MOVD a2+16(FP), R3 + MOVD a3+24(FP), R4 + MOVD a4+32(FP), R5 + MOVD a5+40(FP), R6 + MOVD a6+48(FP), R7 + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, ok + MOVD $-1, r1+56(FP) + MOVD $0, r2+64(FP) + NEG R2, R2 + MOVD R2, errno+72(FP) + RET +ok: + MOVD R2, r1+56(FP) + MOVD R3, r2+64(FP) + MOVD $0, errno+72(FP) + RET diff --git a/src/runtime/internal/syscall/syscall_linux.go b/src/runtime/internal/syscall/syscall_linux.go new file mode 100644 index 00000000000000..7f268e8fba108a --- /dev/null +++ b/src/runtime/internal/syscall/syscall_linux.go @@ -0,0 +1,39 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package syscall provides the syscall primitives required for the runtime. +package syscall + +import ( + _ "unsafe" // for go:linkname +) + +// TODO(https://go.dev/issue/51087): This package is incomplete and currently +// only contains very minimal support for Linux. + +// Syscall6 calls system call number 'num' with arguments a1-6. +func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) + +// syscall_RawSyscall6 is a push linkname to export Syscall6 as +// syscall.RawSyscall6. +// +// //go:uintptrkeepalive because the uintptr argument may be converted pointers +// that need to be kept alive in the caller (this is implied for Syscall6 since +// it has no body). +// +// //go:nosplit because stack copying does not account for uintptrkeepalive, so +// the stack must not grow. Stack copying cannot blindly assume that all +// uintptr arguments are pointers, because some values may look like pointers, +// but not really be pointers, and adjusting their value would break the call. +// +// This is a separate wrapper because we can't export one function as two +// names. The assembly implementations name themselves Syscall6 would not be +// affected by a linkname. +// +//go:uintptrkeepalive +//go:nosplit +//go:linkname syscall_RawSyscall6 syscall.RawSyscall6 +func syscall_RawSyscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) { + return Syscall6(num, a1, a2, a3, a4, a5, a6) +} diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go index c00f0965bd63b1..405923cc34e742 100644 --- a/src/runtime/lfstack_32bit.go +++ b/src/runtime/lfstack_32bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || arm || mips || mipsle -// +build 386 arm mips mipsle package runtime diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go index 4812dd11565724..88cbd3bcc7e4ac 100644 --- a/src/runtime/lfstack_64bit.go +++ b/src/runtime/lfstack_64bit.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm -// +build amd64 arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x wasm +//go:build amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm package runtime @@ -37,12 +36,21 @@ const ( // We use one bit to distinguish between the two ranges. aixAddrBits = 57 aixCntBits = 64 - aixAddrBits + 3 + + // riscv64 SV57 mode gives 56 bits of userspace VA. + // lfstack code supports it, but broader support for SV57 mode is incomplete, + // and there may be other issues (see #54104). + riscv64AddrBits = 56 + riscv64CntBits = 64 - riscv64AddrBits + 3 ) func lfstackPack(node *lfnode, cnt uintptr) uint64 { if GOARCH == "ppc64" && GOOS == "aix" { return uint64(uintptr(unsafe.Pointer(node)))<<(64-aixAddrBits) | uint64(cnt&(1<> aixCntBits << 3) | 0xa<<56))) } + if GOARCH == "riscv64" { + return (*lfnode)(unsafe.Pointer(uintptr(val >> riscv64CntBits << 3))) + } return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3))) } diff --git a/src/runtime/lfstack_test.go b/src/runtime/lfstack_test.go index fb4b45992d8f48..d0a1b6ba0682ef 100644 --- a/src/runtime/lfstack_test.go +++ b/src/runtime/lfstack_test.go @@ -24,7 +24,7 @@ func toMyNode(node *LFNode) *MyNode { return (*MyNode)(unsafe.Pointer(node)) } -var global interface{} +var global any func TestLFStack(t *testing.T) { stack := new(uint64) diff --git a/src/runtime/libfuzzer.go b/src/runtime/libfuzzer.go index 578bce04149baa..6bfaef823b97a4 100644 --- a/src/runtime/libfuzzer.go +++ b/src/runtime/libfuzzer.go @@ -3,44 +3,113 @@ // license that can be found in the LICENSE file. //go:build libfuzzer -// +build libfuzzer package runtime -import _ "unsafe" // for go:linkname +import "unsafe" -func libfuzzerCall(fn *byte, arg0, arg1 uintptr) +func libfuzzerCallWithTwoByteBuffers(fn, start, end *byte) +func libfuzzerCallTraceIntCmp(fn *byte, arg0, arg1, fakePC uintptr) +func libfuzzerCall4(fn *byte, fakePC uintptr, s1, s2 unsafe.Pointer, result uintptr) -func libfuzzerTraceCmp1(arg0, arg1 uint8) { - libfuzzerCall(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1)) +// Keep in sync with the definition of ret_sled in src/runtime/libfuzzer_amd64.s +const retSledSize = 512 + +// In libFuzzer mode, the compiler inserts calls to libfuzzerTraceCmpN and libfuzzerTraceConstCmpN +// (where N can be 1, 2, 4, or 8) for encountered integer comparisons in the code to be instrumented. +// This may result in these functions having callers that are nosplit. That is why they must be nosplit. +// +//go:nosplit +func libfuzzerTraceCmp1(arg0, arg1 uint8, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) +} + +//go:nosplit +func libfuzzerTraceCmp2(arg0, arg1 uint16, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) +} + +//go:nosplit +func libfuzzerTraceCmp4(arg0, arg1 uint32, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) +} + +//go:nosplit +func libfuzzerTraceCmp8(arg0, arg1 uint64, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } -func libfuzzerTraceCmp2(arg0, arg1 uint16) { - libfuzzerCall(&__sanitizer_cov_trace_cmp2, uintptr(arg0), uintptr(arg1)) +//go:nosplit +func libfuzzerTraceConstCmp1(arg0, arg1 uint8, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp1, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } -func libfuzzerTraceCmp4(arg0, arg1 uint32) { - libfuzzerCall(&__sanitizer_cov_trace_cmp4, uintptr(arg0), uintptr(arg1)) +//go:nosplit +func libfuzzerTraceConstCmp2(arg0, arg1 uint16, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp2, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } -func libfuzzerTraceCmp8(arg0, arg1 uint64) { - libfuzzerCall(&__sanitizer_cov_trace_cmp8, uintptr(arg0), uintptr(arg1)) +//go:nosplit +func libfuzzerTraceConstCmp4(arg0, arg1 uint32, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp4, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } -func libfuzzerTraceConstCmp1(arg0, arg1 uint8) { - libfuzzerCall(&__sanitizer_cov_trace_const_cmp1, uintptr(arg0), uintptr(arg1)) +//go:nosplit +func libfuzzerTraceConstCmp8(arg0, arg1 uint64, fakePC int) { + fakePC = fakePC % retSledSize + libfuzzerCallTraceIntCmp(&__sanitizer_cov_trace_const_cmp8, uintptr(arg0), uintptr(arg1), uintptr(fakePC)) } -func libfuzzerTraceConstCmp2(arg0, arg1 uint16) { - libfuzzerCall(&__sanitizer_cov_trace_const_cmp2, uintptr(arg0), uintptr(arg1)) +var pcTables []byte + +func init() { + libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_8bit_counters_init, &__start___sancov_cntrs, &__stop___sancov_cntrs) + start := unsafe.Pointer(&__start___sancov_cntrs) + end := unsafe.Pointer(&__stop___sancov_cntrs) + + // PC tables are arrays of ptr-sized integers representing pairs [PC,PCFlags] for every instrumented block. + // The number of PCs and PCFlags is the same as the number of 8-bit counters. Each PC table entry has + // the size of two ptr-sized integers. We allocate one more byte than what we actually need so that we can + // get a pointer representing the end of the PC table array. + size := (uintptr(end)-uintptr(start))*unsafe.Sizeof(uintptr(0))*2 + 1 + pcTables = make([]byte, size) + libfuzzerCallWithTwoByteBuffers(&__sanitizer_cov_pcs_init, &pcTables[0], &pcTables[size-1]) } -func libfuzzerTraceConstCmp4(arg0, arg1 uint32) { - libfuzzerCall(&__sanitizer_cov_trace_const_cmp4, uintptr(arg0), uintptr(arg1)) +// We call libFuzzer's __sanitizer_weak_hook_strcmp function which takes the +// following four arguments: +// +// 1. caller_pc: location of string comparison call site +// 2. s1: first string used in the comparison +// 3. s2: second string used in the comparison +// 4. result: an integer representing the comparison result. 0 indicates +// equality (comparison will ignored by libfuzzer), non-zero indicates a +// difference (comparison will be taken into consideration). +// +//go:nosplit +func libfuzzerHookStrCmp(s1, s2 string, fakePC int) { + if s1 != s2 { + libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1)) + } + // if s1 == s2 we could call the hook with a last argument of 0 but this is unnecessary since this case will be then + // ignored by libfuzzer } -func libfuzzerTraceConstCmp8(arg0, arg1 uint64) { - libfuzzerCall(&__sanitizer_cov_trace_const_cmp8, uintptr(arg0), uintptr(arg1)) +// This function has now the same implementation as libfuzzerHookStrCmp because we lack better checks +// for case-insensitive string equality in the runtime package. +// +//go:nosplit +func libfuzzerHookEqualFold(s1, s2 string, fakePC int) { + if s1 != s2 { + libfuzzerCall4(&__sanitizer_weak_hook_strcmp, uintptr(fakePC), cstring(s1), cstring(s2), uintptr(1)) + } } //go:linkname __sanitizer_cov_trace_cmp1 __sanitizer_cov_trace_cmp1 @@ -74,3 +143,23 @@ var __sanitizer_cov_trace_const_cmp4 byte //go:linkname __sanitizer_cov_trace_const_cmp8 __sanitizer_cov_trace_const_cmp8 //go:cgo_import_static __sanitizer_cov_trace_const_cmp8 var __sanitizer_cov_trace_const_cmp8 byte + +//go:linkname __sanitizer_cov_8bit_counters_init __sanitizer_cov_8bit_counters_init +//go:cgo_import_static __sanitizer_cov_8bit_counters_init +var __sanitizer_cov_8bit_counters_init byte + +//go:linkname __start___sancov_cntrs __start___sancov_cntrs +//go:cgo_import_static __start___sancov_cntrs +var __start___sancov_cntrs byte + +//go:linkname __stop___sancov_cntrs __stop___sancov_cntrs +//go:cgo_import_static __stop___sancov_cntrs +var __stop___sancov_cntrs byte + +//go:linkname __sanitizer_cov_pcs_init __sanitizer_cov_pcs_init +//go:cgo_import_static __sanitizer_cov_pcs_init +var __sanitizer_cov_pcs_init byte + +//go:linkname __sanitizer_weak_hook_strcmp __sanitizer_weak_hook_strcmp +//go:cgo_import_static __sanitizer_weak_hook_strcmp +var __sanitizer_weak_hook_strcmp byte diff --git a/src/runtime/libfuzzer_amd64.s b/src/runtime/libfuzzer_amd64.s index 13645fc7af849b..65ac7a325d9ab3 100644 --- a/src/runtime/libfuzzer_amd64.s +++ b/src/runtime/libfuzzer_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build libfuzzer -// +build libfuzzer #include "go_asm.h" #include "go_tls.h" @@ -14,17 +13,132 @@ #ifdef GOOS_windows #define RARG0 CX #define RARG1 DX +#define RARG2 R8 +#define RARG3 R9 #else #define RARG0 DI #define RARG1 SI +#define RARG2 DX +#define RARG3 CX #endif -// void runtime·libfuzzerCall(fn, arg0, arg1 uintptr) -// Calls C function fn from libFuzzer and passes 2 arguments to it. -TEXT runtime·libfuzzerCall(SB), NOSPLIT, $0-24 +// void runtime·libfuzzerCall4(fn, hookId int, s1, s2 unsafe.Pointer, result uintptr) +// Calls C function fn from libFuzzer and passes 4 arguments to it. +TEXT runtime·libfuzzerCall4(SB), NOSPLIT, $0-40 + MOVQ fn+0(FP), AX + MOVQ hookId+8(FP), RARG0 + MOVQ s1+16(FP), RARG1 + MOVQ s2+24(FP), RARG2 + MOVQ result+32(FP), RARG3 + + get_tls(R12) + MOVQ g(R12), R14 + MOVQ g_m(R14), R13 + + // Switch to g0 stack. + MOVQ SP, R12 // callee-saved, preserved across the CALL + MOVQ m_g0(R13), R10 + CMPQ R10, R14 + JE call // already on g0 + MOVQ (g_sched+gobuf_sp)(R10), SP +call: + ANDQ $~15, SP // alignment for gcc ABI + CALL AX + MOVQ R12, SP + RET + +// void runtime·libfuzzerCallTraceIntCmp(fn, arg0, arg1, fakePC uintptr) +// Calls C function fn from libFuzzer and passes 2 arguments to it after +// manipulating the return address so that libfuzzer's integer compare hooks +// work +// libFuzzer's compare hooks obtain the caller's address from the compiler +// builtin __builtin_return_adress. Since we invoke the hooks always +// from the same native function, this builtin would always return the same +// value. Internally, the libFuzzer hooks call through to the always inlined +// HandleCmp and thus can't be mimicked without patching libFuzzer. +// +// We solve this problem via an inline assembly trampoline construction that +// translates a runtime argument `fake_pc` in the range [0, 512) into a call to +// a hook with a fake return address whose lower 9 bits are `fake_pc` up to a +// constant shift. This is achieved by pushing a return address pointing into +// 512 ret instructions at offset `fake_pc` onto the stack and then jumping +// directly to the address of the hook. +// +// Note: We only set the lowest 9 bits of the return address since only these +// bits are used by the libFuzzer value profiling mode for integer compares, see +// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp#L390 +// as well as +// https://github.com/llvm/llvm-project/blob/704d92607d26e696daba596b72cb70effe79a872/compiler-rt/lib/fuzzer/FuzzerValueBitMap.h#L34 +// ValueProfileMap.AddValue() truncates its argument to 16 bits and shifts the +// PC to the left by log_2(128)=7, which means that only the lowest 16 - 7 bits +// of the return address matter. String compare hooks use the lowest 12 bits, +// but take the return address as an argument and thus don't require the +// indirection through a trampoline. +// TODO: Remove the inline assembly trampoline once a PC argument has been added to libfuzzer's int compare hooks. +TEXT runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $0-32 MOVQ fn+0(FP), AX MOVQ arg0+8(FP), RARG0 MOVQ arg1+16(FP), RARG1 + MOVQ fakePC+24(FP), R8 + + get_tls(R12) + MOVQ g(R12), R14 + MOVQ g_m(R14), R13 + + // Switch to g0 stack. + MOVQ SP, R12 // callee-saved, preserved across the CALL + MOVQ m_g0(R13), R10 + CMPQ R10, R14 + JE call // already on g0 + MOVQ (g_sched+gobuf_sp)(R10), SP +call: + ANDQ $~15, SP // alignment for gcc ABI + // Load the address of the end of the function and push it into the stack. + // This address will be jumped to after executing the return instruction + // from the return sled. There we reset the stack pointer and return. + MOVQ $end_of_function<>(SB), BX + PUSHQ BX + // Load the starting address of the return sled into BX. + MOVQ $ret_sled<>(SB), BX + // Load the address of the i'th return instruction fron the return sled. + // The index is given in the fakePC argument. + ADDQ R8, BX + PUSHQ BX + // Call the original function with the fakePC return address on the stack. + // Function arguments arg0 and arg1 are passed in the registers specified + // by the x64 calling convention. + JMP AX +// This code will not be executed and is only there to statisfy assembler +// check of a balanced stack. +not_reachable: + POPQ BX + POPQ BX + RET + +TEXT end_of_function<>(SB), NOSPLIT, $0-0 + MOVQ R12, SP + RET + +#define REPEAT_8(a) a \ + a \ + a \ + a \ + a \ + a \ + a \ + a + +#define REPEAT_512(a) REPEAT_8(REPEAT_8(REPEAT_8(a))) + +TEXT ret_sled<>(SB), NOSPLIT, $0-0 + REPEAT_512(RET) + +// void runtime·libfuzzerCallWithTwoByteBuffers(fn, start, end *byte) +// Calls C function fn from libFuzzer and passes 2 arguments of type *byte to it. +TEXT runtime·libfuzzerCallWithTwoByteBuffers(SB), NOSPLIT, $0-24 + MOVQ fn+0(FP), AX + MOVQ start+8(FP), RARG0 + MOVQ end+16(FP), RARG1 get_tls(R12) MOVQ g(R12), R14 diff --git a/src/runtime/libfuzzer_arm64.s b/src/runtime/libfuzzer_arm64.s index 4ad8242804eb11..37b35173c3b52f 100644 --- a/src/runtime/libfuzzer_arm64.s +++ b/src/runtime/libfuzzer_arm64.s @@ -3,19 +3,102 @@ // license that can be found in the LICENSE file. //go:build libfuzzer -// +build libfuzzer #include "go_asm.h" #include "textflag.h" // Based on race_arm64.s; see commentary there. -// func runtime·libfuzzerCall(fn, arg0, arg1 uintptr) -// Calls C function fn from libFuzzer and passes 2 arguments to it. -TEXT runtime·libfuzzerCall(SB), NOSPLIT, $0-24 +#define RARG0 R0 +#define RARG1 R1 +#define RARG2 R2 +#define RARG3 R3 + +#define REPEAT_2(a) a a +#define REPEAT_8(a) REPEAT_2(REPEAT_2(REPEAT_2(a))) +#define REPEAT_128(a) REPEAT_2(REPEAT_8(REPEAT_8(a))) + +// void runtime·libfuzzerCallTraceIntCmp(fn, arg0, arg1, fakePC uintptr) +// Calls C function fn from libFuzzer and passes 2 arguments to it after +// manipulating the return address so that libfuzzer's integer compare hooks +// work. +// The problem statement and solution are documented in detail in libfuzzer_amd64.s. +// See commentary there. +TEXT runtime·libfuzzerCallTraceIntCmp(SB), NOSPLIT, $8-32 + MOVD fn+0(FP), R9 + MOVD arg0+8(FP), RARG0 + MOVD arg1+16(FP), RARG1 + MOVD fakePC+24(FP), R8 + // Save the original return address in a local variable + MOVD R30, savedRetAddr-8(SP) + + MOVD g_m(g), R10 + + // Switch to g0 stack. + MOVD RSP, R19 // callee-saved, preserved across the CALL + MOVD m_g0(R10), R11 + CMP R11, g + BEQ call // already on g0 + MOVD (g_sched+gobuf_sp)(R11), R12 + MOVD R12, RSP +call: + // Load address of the ret sled into the default register for the return + // address. + ADR ret_sled, R30 + // Clear the lowest 2 bits of fakePC. All ARM64 instructions are four + // bytes long, so we cannot get better return address granularity than + // multiples of 4. + AND $-4, R8, R8 + // Add the offset of the fake_pc-th ret. + ADD R8, R30, R30 + // Call the function by jumping to it and reusing all registers except + // for the modified return address register R30. + JMP (R9) + +// The ret sled for ARM64 consists of 128 br instructions jumping to the +// end of the function. Each instruction is 4 bytes long. The sled thus +// has the same byte length of 4 * 128 = 512 as the x86_64 sled, but +// coarser granularity. +#define RET_SLED \ + JMP end_of_function; + +ret_sled: + REPEAT_128(RET_SLED); + +end_of_function: + MOVD R19, RSP + MOVD savedRetAddr-8(SP), R30 + RET + +// void runtime·libfuzzerCall4(fn, hookId int, s1, s2 unsafe.Pointer, result uintptr) +// Calls C function fn from libFuzzer and passes 4 arguments to it. +TEXT runtime·libfuzzerCall4(SB), NOSPLIT, $0-40 + MOVD fn+0(FP), R9 + MOVD hookId+8(FP), RARG0 + MOVD s1+16(FP), RARG1 + MOVD s2+24(FP), RARG2 + MOVD result+32(FP), RARG3 + + MOVD g_m(g), R10 + + // Switch to g0 stack. + MOVD RSP, R19 // callee-saved, preserved across the CALL + MOVD m_g0(R10), R11 + CMP R11, g + BEQ call // already on g0 + MOVD (g_sched+gobuf_sp)(R11), R12 + MOVD R12, RSP +call: + BL R9 + MOVD R19, RSP + RET + +// void runtime·libfuzzerCallWithTwoByteBuffers(fn, start, end *byte) +// Calls C function fn from libFuzzer and passes 2 arguments of type *byte to it. +TEXT runtime·libfuzzerCallWithTwoByteBuffers(SB), NOSPLIT, $0-24 MOVD fn+0(FP), R9 - MOVD arg0+8(FP), R0 - MOVD arg1+16(FP), R1 + MOVD start+8(FP), R0 + MOVD end+16(FP), R1 MOVD g_m(g), R10 diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go index e4c8d01941d196..1578984ce238a4 100644 --- a/src/runtime/lock_futex.go +++ b/src/runtime/lock_futex.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux -// +build dragonfly freebsd linux package runtime @@ -39,6 +38,7 @@ const ( // affect mutex's state. // We use the uintptr mutex.key and note.key as a uint32. +// //go:nosplit func key32(p *uintptr) *uint32 { return (*uint32)(unsafe.Pointer(p)) diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go index 0ca3512baf3c83..f71e7a2b4a59cd 100644 --- a/src/runtime/lock_js.go +++ b/src/runtime/lock_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package runtime @@ -145,8 +144,12 @@ func notetsleepg(n *note, ns int64) bool { } // checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline. +// TODO(drchase): need to understand if write barriers are really okay in this context. +// +//go:yeswritebarrierrec func checkTimeouts() { now := nanotime() + // TODO: map iteration has the write barriers in it; is that okay? for n, nt := range notesWithTimeout { if n.key == note_cleared && now >= nt.deadline { n.key = note_timeout @@ -176,6 +179,9 @@ var idleID int32 // If an event handler returned, we resume it and it will pause the execution. // beforeIdle either returns the specific goroutine to schedule next or // indicates with otherReady that some goroutine became ready. +// TODO(drchase): need to understand if write barriers are really okay in this context. +// +//go:yeswritebarrierrec func beforeIdle(now, pollUntil int64) (gp *g, otherReady bool) { delay := int64(-1) if pollUntil != 0 { @@ -197,6 +203,7 @@ func beforeIdle(now, pollUntil int64) (gp *g, otherReady bool) { } if len(events) == 0 { + // TODO: this is the line that requires the yeswritebarrierrec go handleAsyncEvent() return nil, true } diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go index 7a6af28b561399..c5e8cfe24a7f17 100644 --- a/src/runtime/lock_sema.go +++ b/src/runtime/lock_sema.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || netbsd || openbsd || plan9 || solaris || windows -// +build aix darwin netbsd openbsd plan9 solaris windows package runtime @@ -24,7 +23,6 @@ import ( // // func semawakeup(mp *m) // Wake up mp, which is or will soon be sleeping on its semaphore. -// const ( locked uintptr = 1 @@ -98,8 +96,9 @@ func unlock(l *mutex) { unlockWithRank(l) } -//go:nowritebarrier // We might not be holding a p in this code. +// +//go:nowritebarrier func unlock2(l *mutex) { gp := getg() var mp *m diff --git a/src/runtime/lockrank.go b/src/runtime/lockrank.go index dde9f7c21a7f3a..fbfef357708d8e 100644 --- a/src/runtime/lockrank.go +++ b/src/runtime/lockrank.go @@ -1,175 +1,118 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file records the static ranks of the locks in the runtime. If a lock -// is not given a rank, then it is assumed to be a leaf lock, which means no other -// lock can be acquired while it is held. Therefore, leaf locks do not need to be -// given an explicit rank. We list all of the architecture-independent leaf locks -// for documentation purposes, but don't list any of the architecture-dependent -// locks (which are all leaf locks). debugLock is ignored for ranking, since it is used -// when printing out lock ranking errors. -// -// lockInit(l *mutex, rank int) is used to set the rank of lock before it is used. -// If there is no clear place to initialize a lock, then the rank of a lock can be -// specified during the lock call itself via lockWithrank(l *mutex, rank int). -// -// Besides the static lock ranking (which is a total ordering of the locks), we -// also represent and enforce the actual partial order among the locks in the -// arcs[] array below. That is, if it is possible that lock B can be acquired when -// lock A is the previous acquired lock that is still held, then there should be -// an entry for A in arcs[B][]. We will currently fail not only if the total order -// (the lock ranking) is violated, but also if there is a missing entry in the -// partial order. +// Code generated by mklockrank.go; DO NOT EDIT. package runtime type lockRank int -// Constants representing the lock rank of the architecture-independent locks in -// the runtime. Locks with lower rank must be taken before locks with higher -// rank. +// Constants representing the ranks of all non-leaf runtime locks, in rank order. +// Locks with lower rank must be taken before locks with higher rank, +// in addition to satisfying the partial order in lockPartialOrder. +// A few ranks allow self-cycles, which are specified in lockPartialOrder. const ( - lockRankDummy lockRank = iota + lockRankUnknown lockRank = iota - // Locks held above sched lockRankSysmon lockRankScavenge lockRankForcegc + lockRankDefer lockRankSweepWaiters lockRankAssistQueue - lockRankCpuprof lockRankSweep - lockRankPollDesc + lockRankCpuprof lockRankSched - lockRankDeadlock lockRankAllg lockRankAllp - - lockRankTimers // Multiple timers locked simultaneously in destroy() + lockRankTimers + lockRankNetpollInit + lockRankHchan + lockRankNotifyList + lockRankSudog + lockRankRwmutexW + lockRankRwmutexR + lockRankRoot lockRankItab lockRankReflectOffs - lockRankHchan // Multiple hchans acquired in lock order in syncadjustsudogs() - lockRankFin - lockRankNotifyList + // TRACEGLOBAL lockRankTraceBuf lockRankTraceStrings - lockRankMspanSpecial - lockRankProf + // MALLOC + lockRankFin lockRankGcBitsArenas - lockRankRoot - lockRankTrace - lockRankTraceStackTab - lockRankNetpollInit - - lockRankRwmutexW - lockRankRwmutexR - + lockRankMheapSpecial + lockRankMspanSpecial lockRankSpanSetSpine + // MPROF + lockRankProfInsert + lockRankProfBlock + lockRankProfMemActive + lockRankProfMemFuture + // STACKGROW lockRankGscan lockRankStackpool lockRankStackLarge - lockRankDefer - lockRankSudog - - // Memory-related non-leaf locks + lockRankHchanLeaf + // WB lockRankWbufSpans lockRankMheap - lockRankMheapSpecial - - // Memory-related leaf locks lockRankGlobalAlloc - - // Other leaf locks - lockRankGFree - // Generally, hchan must be acquired before gscan. But in one specific - // case (in syncadjustsudogs from markroot after the g has been suspended - // by suspendG), we allow gscan to be acquired, and then an hchan lock. To - // allow this case, we get this lockRankHchanLeaf rank in - // syncadjustsudogs(), rather than lockRankHchan. By using this special - // rank, we don't allow any further locks to be acquired other than more - // hchan locks. - lockRankHchanLeaf + // TRACE + lockRankTrace + lockRankTraceStackTab lockRankPanic - - // Leaf locks with no dependencies, so these constants are not actually used anywhere. - // There are other architecture-dependent leaf locks as well. - lockRankNewmHandoff - lockRankDebugPtrmask - lockRankFaketimeState - lockRankTicks - lockRankRaceFini - lockRankPollCache - lockRankDebug + lockRankDeadlock ) -// lockRankLeafRank is the rank of lock that does not have a declared rank, and hence is -// a leaf lock. +// lockRankLeafRank is the rank of lock that does not have a declared rank, +// and hence is a leaf lock. const lockRankLeafRank lockRank = 1000 -// lockNames gives the names associated with each of the above ranks +// lockNames gives the names associated with each of the above ranks. var lockNames = []string{ - lockRankDummy: "", - - lockRankSysmon: "sysmon", - lockRankScavenge: "scavenge", - lockRankForcegc: "forcegc", - lockRankSweepWaiters: "sweepWaiters", - lockRankAssistQueue: "assistQueue", - lockRankCpuprof: "cpuprof", - lockRankSweep: "sweep", - - lockRankPollDesc: "pollDesc", - lockRankSched: "sched", - lockRankDeadlock: "deadlock", - lockRankAllg: "allg", - lockRankAllp: "allp", - - lockRankTimers: "timers", - lockRankItab: "itab", - lockRankReflectOffs: "reflectOffs", - + lockRankSysmon: "sysmon", + lockRankScavenge: "scavenge", + lockRankForcegc: "forcegc", + lockRankDefer: "defer", + lockRankSweepWaiters: "sweepWaiters", + lockRankAssistQueue: "assistQueue", + lockRankSweep: "sweep", + lockRankPollDesc: "pollDesc", + lockRankCpuprof: "cpuprof", + lockRankSched: "sched", + lockRankAllg: "allg", + lockRankAllp: "allp", + lockRankTimers: "timers", + lockRankNetpollInit: "netpollInit", lockRankHchan: "hchan", - lockRankFin: "fin", lockRankNotifyList: "notifyList", + lockRankSudog: "sudog", + lockRankRwmutexW: "rwmutexW", + lockRankRwmutexR: "rwmutexR", + lockRankRoot: "root", + lockRankItab: "itab", + lockRankReflectOffs: "reflectOffs", lockRankTraceBuf: "traceBuf", lockRankTraceStrings: "traceStrings", - lockRankMspanSpecial: "mspanSpecial", - lockRankProf: "prof", + lockRankFin: "fin", lockRankGcBitsArenas: "gcBitsArenas", - lockRankRoot: "root", + lockRankMheapSpecial: "mheapSpecial", + lockRankMspanSpecial: "mspanSpecial", + lockRankSpanSetSpine: "spanSetSpine", + lockRankProfInsert: "profInsert", + lockRankProfBlock: "profBlock", + lockRankProfMemActive: "profMemActive", + lockRankProfMemFuture: "profMemFuture", + lockRankGscan: "gscan", + lockRankStackpool: "stackpool", + lockRankStackLarge: "stackLarge", + lockRankHchanLeaf: "hchanLeaf", + lockRankWbufSpans: "wbufSpans", + lockRankMheap: "mheap", + lockRankGlobalAlloc: "globalAlloc", lockRankTrace: "trace", lockRankTraceStackTab: "traceStackTab", - lockRankNetpollInit: "netpollInit", - - lockRankRwmutexW: "rwmutexW", - lockRankRwmutexR: "rwmutexR", - - lockRankSpanSetSpine: "spanSetSpine", - lockRankGscan: "gscan", - lockRankStackpool: "stackpool", - lockRankStackLarge: "stackLarge", - lockRankDefer: "defer", - lockRankSudog: "sudog", - - lockRankWbufSpans: "wbufSpans", - lockRankMheap: "mheap", - lockRankMheapSpecial: "mheapSpecial", - - lockRankGlobalAlloc: "globalAlloc.mutex", - - lockRankGFree: "gFree", - lockRankHchanLeaf: "hchanLeaf", - lockRankPanic: "panic", - - lockRankNewmHandoff: "newmHandoff.lock", - lockRankDebugPtrmask: "debugPtrmask.lock", - lockRankFaketimeState: "faketimeState.lock", - lockRankTicks: "ticks.lock", - lockRankRaceFini: "raceFiniLock", - lockRankPollCache: "pollCache.lock", - lockRankDebug: "debugLock", + lockRankPanic: "panic", + lockRankDeadlock: "deadlock", } func (rank lockRank) String() string { @@ -179,70 +122,60 @@ func (rank lockRank) String() string { if rank == lockRankLeafRank { return "LEAF" } + if rank < 0 || int(rank) >= len(lockNames) { + return "BAD RANK" + } return lockNames[rank] } -// lockPartialOrder is a partial order among the various lock types, listing the -// immediate ordering that has actually been observed in the runtime. Each entry -// (which corresponds to a particular lock rank) specifies the list of locks -// that can already be held immediately "above" it. +// lockPartialOrder is the transitive closure of the lock rank graph. +// An entry for rank X lists all of the ranks that can already be held +// when rank X is acquired. // -// So, for example, the lockRankSched entry shows that all the locks preceding -// it in rank can actually be held. The allp lock shows that only the sysmon or -// sched lock can be held immediately above it when it is acquired. +// Lock ranks that allow self-cycles list themselves. var lockPartialOrder [][]lockRank = [][]lockRank{ - lockRankDummy: {}, lockRankSysmon: {}, lockRankScavenge: {lockRankSysmon}, lockRankForcegc: {lockRankSysmon}, + lockRankDefer: {}, lockRankSweepWaiters: {}, lockRankAssistQueue: {}, - lockRankCpuprof: {}, lockRankSweep: {}, lockRankPollDesc: {}, - lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc}, - lockRankDeadlock: {lockRankDeadlock}, - lockRankAllg: {lockRankSysmon, lockRankSched}, - lockRankAllp: {lockRankSysmon, lockRankSched}, - lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankPollDesc, lockRankSched, lockRankAllp, lockRankTimers}, + lockRankCpuprof: {}, + lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof}, + lockRankAllg: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched}, + lockRankAllp: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched}, + lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllp, lockRankTimers}, + lockRankNetpollInit: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllp, lockRankTimers}, + lockRankHchan: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankHchan}, + lockRankNotifyList: {}, + lockRankSudog: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankHchan, lockRankNotifyList}, + lockRankRwmutexW: {}, + lockRankRwmutexR: {lockRankSysmon, lockRankRwmutexW}, + lockRankRoot: {}, lockRankItab: {}, lockRankReflectOffs: {lockRankItab}, - lockRankHchan: {lockRankScavenge, lockRankSweep, lockRankHchan}, - lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan}, - lockRankNotifyList: {}, lockRankTraceBuf: {lockRankSysmon, lockRankScavenge}, - lockRankTraceStrings: {lockRankTraceBuf}, - lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankProf: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankRoot: {}, - lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankHchan, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot}, - lockRankTraceStackTab: {lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankSched, lockRankAllg, lockRankTimers, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankRoot, lockRankTrace}, - lockRankNetpollInit: {lockRankTimers}, - - lockRankRwmutexW: {}, - lockRankRwmutexR: {lockRankSysmon, lockRankRwmutexW}, - - lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankSpanSetSpine}, - lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankTrace, lockRankTraceStackTab, lockRankNetpollInit, lockRankRwmutexR, lockRankSpanSetSpine, lockRankGscan}, - lockRankStackLarge: {lockRankSysmon, lockRankAssistQueue, lockRankSched, lockRankItab, lockRankHchan, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankSpanSetSpine, lockRankGscan}, - lockRankDefer: {}, - lockRankSudog: {lockRankHchan, lockRankNotifyList}, - lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankAllg, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceStrings, lockRankMspanSpecial, lockRankProf, lockRankRoot, lockRankGscan, lockRankDefer, lockRankSudog}, - lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankSweepWaiters, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankFin, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings, lockRankMspanSpecial, lockRankProf, lockRankGcBitsArenas, lockRankRoot, lockRankSpanSetSpine, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankDefer, lockRankSudog, lockRankWbufSpans}, - lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankAssistQueue, lockRankCpuprof, lockRankSweep, lockRankPollDesc, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankItab, lockRankReflectOffs, lockRankHchan, lockRankNotifyList, lockRankTraceBuf, lockRankTraceStrings}, - lockRankGlobalAlloc: {lockRankProf, lockRankSpanSetSpine, lockRankMheap, lockRankMheapSpecial}, - - lockRankGFree: {lockRankSched}, - lockRankHchanLeaf: {lockRankGscan, lockRankHchanLeaf}, - lockRankPanic: {lockRankDeadlock}, // plus any other lock held on throw. - - lockRankNewmHandoff: {}, - lockRankDebugPtrmask: {}, - lockRankFaketimeState: {}, - lockRankTicks: {}, - lockRankRaceFini: {}, - lockRankPollCache: {}, - lockRankDebug: {}, + lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf}, + lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfInsert: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive}, + lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture}, + lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf}, + lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankMspanSpecial, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankMspanSpecial, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans}, + lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankMheapSpecial, lockRankMspanSpecial, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, + lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankMspanSpecial, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, + lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankGcBitsArenas, lockRankMspanSpecial, lockRankSpanSetSpine, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace}, + lockRankPanic: {}, + lockRankDeadlock: {lockRankPanic, lockRankDeadlock}, } diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go index f3d2c009145766..bf046a104124e3 100644 --- a/src/runtime/lockrank_off.go +++ b/src/runtime/lockrank_off.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !goexperiment.staticlockranking -// +build !goexperiment.staticlockranking package runtime @@ -24,6 +23,7 @@ func lockWithRank(l *mutex, rank lockRank) { } // This function may be called in nosplit context and thus must be nosplit. +// //go:nosplit func acquireLockRank(rank lockRank) { } @@ -33,6 +33,7 @@ func unlockWithRank(l *mutex) { } // This function may be called in nosplit context and thus must be nosplit. +// //go:nosplit func releaseLockRank(rank lockRank) { } diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go index fc8d2dc8d1179f..5dcc79b15eb85d 100644 --- a/src/runtime/lockrank_on.go +++ b/src/runtime/lockrank_on.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build goexperiment.staticlockranking -// +build goexperiment.staticlockranking package runtime @@ -14,7 +13,7 @@ import ( // worldIsStopped is accessed atomically to track world-stops. 1 == world // stopped. -var worldIsStopped uint32 +var worldIsStopped atomic.Uint32 // lockRankStruct is embedded in mutex type lockRankStruct struct { @@ -25,6 +24,9 @@ type lockRankStruct struct { pad int } +// lockInit(l *mutex, rank int) sets the rank of lock before it is used. +// If there is no clear place to initialize a lock, then the rank of a lock can be +// specified during the lock call itself via lockWithRank(l *mutex, rank int). func lockInit(l *mutex, rank lockRank) { l.rank = rank } @@ -83,6 +85,7 @@ func lockWithRank(l *mutex, rank lockRank) { } // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func printHeldLocks(gp *g) { if gp.m.locksHeldLen == 0 { @@ -98,6 +101,7 @@ func printHeldLocks(gp *g) { // acquireLockRank acquires a rank which is not associated with a mutex lock // // This function may be called in nosplit context and thus must be nosplit. +// //go:nosplit func acquireLockRank(rank lockRank) { gp := getg() @@ -182,6 +186,7 @@ func unlockWithRank(l *mutex) { // releaseLockRank releases a rank which is not associated with a mutex lock // // This function may be called in nosplit context and thus must be nosplit. +// //go:nosplit func releaseLockRank(rank lockRank) { gp := getg() @@ -227,6 +232,7 @@ func lockWithRankMayAcquire(l *mutex, rank lockRank) { } // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func checkLockHeld(gp *g, l *mutex) bool { for i := gp.m.locksHeldLen - 1; i >= 0; i-- { @@ -240,6 +246,7 @@ func checkLockHeld(gp *g, l *mutex) bool { // assertLockHeld throws if l is not held by the caller. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func assertLockHeld(l *mutex) { gp := getg() @@ -265,6 +272,7 @@ func assertLockHeld(l *mutex) { // pointer to the exact mutex is not available. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func assertRankHeld(r lockRank) { gp := getg() @@ -290,9 +298,10 @@ func assertRankHeld(r lockRank) { // Caller must hold worldsema. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func worldStopped() { - if stopped := atomic.Xadd(&worldIsStopped, 1); stopped != 1 { + if stopped := worldIsStopped.Add(1); stopped != 1 { systemstack(func() { print("world stop count=", stopped, "\n") throw("recursive world stop") @@ -305,9 +314,10 @@ func worldStopped() { // Caller must hold worldsema. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func worldStarted() { - if stopped := atomic.Xadd(&worldIsStopped, -1); stopped != 0 { + if stopped := worldIsStopped.Add(-1); stopped != 0 { systemstack(func() { print("world stop count=", stopped, "\n") throw("released non-stopped world stop") @@ -316,9 +326,10 @@ func worldStarted() { } // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func checkWorldStopped() bool { - stopped := atomic.Load(&worldIsStopped) + stopped := worldIsStopped.Load() if stopped > 1 { systemstack(func() { print("inconsistent world stop count=", stopped, "\n") @@ -333,6 +344,7 @@ func checkWorldStopped() bool { // which M stopped the world. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func assertWorldStopped() { if checkWorldStopped() { @@ -346,6 +358,7 @@ func assertWorldStopped() { // passed lock is not held. // // nosplit to ensure it can be called in as many contexts as possible. +// //go:nosplit func assertWorldStoppedOrLockHeld(l *mutex) { if checkWorldStopped() { diff --git a/src/runtime/lockrank_test.go b/src/runtime/lockrank_test.go index 4b2fc0eaeeaa67..a7b1b8df7cde26 100644 --- a/src/runtime/lockrank_test.go +++ b/src/runtime/lockrank_test.go @@ -1,41 +1,29 @@ -// Copyright 2021 The Go Authors. All rights reserved. +// Copyright 2022 The Go Authors. 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 ( - . "runtime" + "bytes" + "internal/testenv" + "os" + "os/exec" "testing" ) -// Check that the partial order in lockPartialOrder fits within the total order -// determined by the order of the lockRank constants. -func TestLockRankPartialOrder(t *testing.T) { - for r, list := range LockPartialOrder { - rank := LockRank(r) - for _, e := range list { - entry := LockRank(e) - if entry > rank { - t.Errorf("lockPartialOrder row %v entry %v is inconsistent with total lock ranking order", rank, entry) - } - } +// Test that the generated code for the lock rank graph is up-to-date. +func TestLockRankGenerated(t *testing.T) { + testenv.MustHaveGoRun(t) + want, err := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "run", "mklockrank.go")).CombinedOutput() + if err != nil { + t.Fatal(err) } -} - -// Verify that partial order lists are kept sorted. This is a purely cosemetic -// check to make manual reviews simpler. It does not affect correctness, unlike -// the above test. -func TestLockRankPartialOrderSortedEntries(t *testing.T) { - for r, list := range LockPartialOrder { - rank := LockRank(r) - var prev LockRank - for _, e := range list { - entry := LockRank(e) - if entry <= prev { - t.Errorf("Partial order for rank %v out of order: %v <= %v in %v", rank, entry, prev, list) - } - prev = entry - } + got, err := os.ReadFile("lockrank.go") + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(want, got) { + t.Fatalf("lockrank.go is out of date. Please run go generate.") } } diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index cc22b0f276597e..f2b93c04f4a42a 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -101,6 +101,8 @@ package runtime import ( + "internal/goarch" + "internal/goos" "runtime/internal/atomic" "runtime/internal/math" "runtime/internal/sys" @@ -108,18 +110,12 @@ import ( ) const ( - debugMalloc = false - maxTinySize = _TinySize tinySizeClass = _TinySizeClass maxSmallSize = _MaxSmallSize pageShift = _PageShift pageSize = _PageSize - pageMask = _PageMask - // By construction, single page spans of the smallest object class - // have the most objects per span. - maxObjsPerSpan = pageSize / 8 concurrentSweep = _ConcurrentSweep @@ -150,7 +146,7 @@ const ( // windows/32 | 4KB | 3 // windows/64 | 8KB | 2 // plan9 | 4KB | 3 - _NumStackOrders = 4 - sys.PtrSize/4*sys.GoosWindows - 1*sys.GoosPlan9 + _NumStackOrders = 4 - goarch.PtrSize/4*goos.IsWindows - 1*goos.IsPlan9 // heapAddrBits is the number of bits in a heap address. On // amd64, addresses are sign-extended beyond heapAddrBits. On @@ -199,15 +195,21 @@ const ( // we further limit it to 31 bits. // // On ios/arm64, although 64-bit pointers are presumably - // available, pointers are truncated to 33 bits. Furthermore, - // only the top 4 GiB of the address space are actually available - // to the application, but we allow the whole 33 bits anyway for - // simplicity. - // TODO(mknyszek): Consider limiting it to 32 bits and using - // arenaBaseOffset to offset into the top 4 GiB. + // available, pointers are truncated to 33 bits in iOS <14. + // Furthermore, only the top 4 GiB of the address space are + // actually available to the application. In iOS >=14, more + // of the address space is available, and the OS can now + // provide addresses outside of those 33 bits. Pick 40 bits + // as a reasonable balance between address space usage by the + // page allocator, and flexibility for what mmap'd regions + // we'll accept for the heap. We can't just move to the full + // 48 bits because this uses too much address space for older + // iOS versions. + // TODO(mknyszek): Once iOS <14 is deprecated, promote ios/arm64 + // to a 48-bit address space like every other arm64 platform. // // WebAssembly currently has a limit of 4GB linear memory. - heapAddrBits = (_64bit*(1-sys.GoarchWasm)*(1-sys.GoosIos*sys.GoarchArm64))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle)) + 33*sys.GoosIos*sys.GoarchArm64 + heapAddrBits = (_64bit*(1-goarch.IsWasm)*(1-goos.IsIos*goarch.IsArm64))*48 + (1-_64bit+goarch.IsWasm)*(32-(goarch.IsMips+goarch.IsMipsle)) + 40*goos.IsIos*goarch.IsArm64 // maxAlloc is the maximum size of an allocation. On 64-bit, // it's theoretically possible to allocate 1< 0 { - sysFree(unsafe.Pointer(end), endLen, nil) + sysFreeOS(unsafe.Pointer(end), endLen) } return unsafe.Pointer(pAligned), size } @@ -909,6 +850,19 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { return unsafe.Pointer(&zerobase) } + // It's possible for any malloc to trigger sweeping, which may in + // turn queue finalizers. Record this dynamic lock edge. + lockRankMayQueueFinalizer() + + userSize := size + if asanenabled { + // Refer to ASAN runtime library, the malloc() function allocates extra memory, + // the redzone, around the user requested memory region. And the redzones are marked + // as unaddressable. We perform the same operations in Go to detect the overflows or + // underflows. + size += computeRZlog(size) + } + if debug.malloc { if debug.sbrk != 0 { align := uintptr(16) @@ -971,8 +925,8 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { mp.mallocing = 1 shouldhelpgc := false - dataSize := size - c := getMCache() + dataSize := userSize + c := getMCache(mp) if c == nil { throw("mallocgc called without a P or outside bootstrapping") } @@ -980,8 +934,8 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { var x unsafe.Pointer noscan := typ == nil || typ.ptrdata == 0 // In some cases block zeroing can profitably (for latency reduction purposes) - // be delayed till preemption is possible; isZeroed tracks that state. - isZeroed := true + // be delayed till preemption is possible; delayedZeroing tracks that state. + delayedZeroing := false if size <= maxSmallSize { if noscan && size < maxTinySize { // Tiny allocator. @@ -1017,7 +971,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // Align tiny pointer for required (conservative) alignment. if size&7 == 0 { off = alignUp(off, 8) - } else if sys.PtrSize == 4 && size == 12 { + } else if goarch.PtrSize == 4 && size == 12 { // Conservatively align 12-byte objects to 8 bytes on 32-bit // systems so that objects whose first field is a 64-bit // value is aligned to 8 bytes and does not cause a fault on @@ -1079,24 +1033,27 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { shouldhelpgc = true // For large allocations, keep track of zeroed state so that // bulk zeroing can be happen later in a preemptible context. - span, isZeroed = c.allocLarge(size, needzero && !noscan, noscan) + span = c.allocLarge(size, noscan) span.freeindex = 1 span.allocCount = 1 - x = unsafe.Pointer(span.base()) size = span.elemsize + x = unsafe.Pointer(span.base()) + if needzero && span.needzero != 0 { + if noscan { + delayedZeroing = true + } else { + memclrNoHeapPointers(x, size) + // We've in theory cleared almost the whole span here, + // and could take the extra step of actually clearing + // the whole thing. However, don't. Any GC bits for the + // uncleared parts will be zero, and it's just going to + // be needzero = 1 once freed anyway. + } + } } - var scanSize uintptr if !noscan { - // If allocating a defer+arg block, now that we've picked a malloc size - // large enough to hold everything, cut the "asked for" size down to - // just the defer header, so that the GC bitmap will record the arg block - // as containing nothing at all (as if it were unused space at the end of - // a malloc block caused by size rounding). - // The defer arg areas are scanned as part of scanstack. - if typ == deferType { - dataSize = unsafe.Sizeof(_defer{}) - } + var scanSize uintptr heapBitsSetType(uintptr(x), size, dataSize, typ) if dataSize > typ.size { // Array allocation. If there are any @@ -1124,7 +1081,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // This may be racing with GC so do it atomically if there can be // a race marking the bit. if gcphase != _GCoff { - gcmarknewobject(span, uintptr(x), size, scanSize) + gcmarknewobject(span, uintptr(x), size) } if raceenabled { @@ -1135,6 +1092,17 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { msanmalloc(x, size) } + if asanenabled { + // We should only read/write the memory with the size asked by the user. + // The rest of the allocated memory should be poisoned, so that we can report + // errors when accessing poisoned memory. + // The allocated memory is larger than required userSize, it will also include + // redzone and some other padding bytes. + rzBeg := unsafe.Add(x, userSize) + asanpoison(rzBeg, size-userSize) + asanunpoison(x, userSize) + } + if rate := MemProfileRate; rate > 0 { // Note cache c only valid while m acquired; see #47302 if rate != 1 && size < c.nextSample { @@ -1148,7 +1116,10 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // Pointerfree data can be zeroed late in a context where preemption can occur. // x will keep the memory alive. - if !isZeroed && needzero { + if delayedZeroing { + if !noscan { + throw("delayed zeroing on data that may contain pointers") + } memclrNoHeapPointersChunked(size, x) // This is a possible preemption point: see #47302 } @@ -1256,7 +1227,7 @@ func reflect_unsafe_NewArray(typ *_type, n int) unsafe.Pointer { } func profilealloc(mp *m, x unsafe.Pointer, size uintptr) { - c := getMCache() + c := getMCache(mp) if c == nil { throw("profilealloc called without a P or outside bootstrapping") } @@ -1281,7 +1252,7 @@ func nextSample() uintptr { } if GOOS == "plan9" { // Plan 9 doesn't support floating point in note handler. - if g := getg(); g == g.m.gsignal { + if gp := getg(); gp == gp.m.gsignal { return nextSampleNoFP() } } @@ -1310,7 +1281,7 @@ func fastexprand(mean int) int32 { // x = -log_e(q) * mean // x = log_2(q) * (-log_e(2)) * mean ; Using log_2 for efficiency const randomBitCount = 26 - q := fastrand()%(1< 0 { qlog = 0 @@ -1328,7 +1299,7 @@ func nextSampleNoFP() uintptr { rate = 0x3fffffff } if rate != 0 { - return uintptr(fastrand() % uint32(2*rate)) + return uintptr(fastrandn(uint32(2 * rate))) } return 0 } @@ -1357,8 +1328,10 @@ var persistentChunks *notInHeap // Intended for things like function/type/debug-related persistent data. // If align is 0, uses default align (currently 8). // The returned memory will be zeroed. +// sysStat must be non-nil. // -// Consider marking persistentalloc'd types go:notinheap. +// Consider marking persistentalloc'd types not in heap by embedding +// runtime/internal/sys.NotInHeap. func persistentalloc(size, align uintptr, sysStat *sysMemStat) unsafe.Pointer { var p *notInHeap systemstack(func() { @@ -1369,6 +1342,7 @@ func persistentalloc(size, align uintptr, sysStat *sysMemStat) unsafe.Pointer { // Must run on system stack because stack growth can (re)invoke it. // See issue 9174. +// //go:systemstack func persistentalloc1(size, align uintptr, sysStat *sysMemStat) *notInHeap { const ( @@ -1419,7 +1393,7 @@ func persistentalloc1(size, align uintptr, sysStat *sysMemStat) *notInHeap { break } } - persistent.off = alignUp(sys.PtrSize, align) + persistent.off = alignUp(goarch.PtrSize, align) } p := persistent.base.add(persistent.off) persistent.off += size @@ -1438,6 +1412,7 @@ func persistentalloc1(size, align uintptr, sysStat *sysMemStat) *notInHeap { // inPersistentAlloc reports whether p points to memory allocated by // persistentalloc. This must be nosplit because it is called by the // cgo checker code, which is called by the write barrier code. +// //go:nosplit func inPersistentAlloc(p uintptr) bool { chunk := atomic.Loaduintptr((*uintptr)(unsafe.Pointer(&persistentChunks))) @@ -1485,8 +1460,9 @@ func (l *linearAlloc) alloc(size, align uintptr, sysStat *sysMemStat) unsafe.Poi if pEnd := alignUp(l.next-1, physPageSize); pEnd > l.mapped { if l.mapMemory { // Transition from Reserved to Prepared to Ready. - sysMap(unsafe.Pointer(l.mapped), pEnd-l.mapped, sysStat) - sysUsed(unsafe.Pointer(l.mapped), pEnd-l.mapped) + n := pEnd - l.mapped + sysMap(unsafe.Pointer(l.mapped), n, sysStat) + sysUsed(unsafe.Pointer(l.mapped), n, n) } l.mapped = pEnd } @@ -1496,15 +1472,36 @@ func (l *linearAlloc) alloc(size, align uintptr, sysStat *sysMemStat) unsafe.Poi // notInHeap is off-heap memory allocated by a lower-level allocator // like sysAlloc or persistentAlloc. // -// In general, it's better to use real types marked as go:notinheap, -// but this serves as a generic type for situations where that isn't -// possible (like in the allocators). +// In general, it's better to use real types which embed +// runtime/internal/sys.NotInHeap, but this serves as a generic type +// for situations where that isn't possible (like in the allocators). // // TODO: Use this as the return type of sysAlloc, persistentAlloc, etc? -// -//go:notinheap -type notInHeap struct{} +type notInHeap struct{ _ sys.NotInHeap } func (p *notInHeap) add(bytes uintptr) *notInHeap { return (*notInHeap)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + bytes)) } + +// computeRZlog computes the size of the redzone. +// Refer to the implementation of the compiler-rt. +func computeRZlog(userSize uintptr) uintptr { + switch { + case userSize <= (64 - 16): + return 16 << 0 + case userSize <= (128 - 32): + return 16 << 1 + case userSize <= (512 - 64): + return 16 << 2 + case userSize <= (4096 - 128): + return 16 << 3 + case userSize <= (1<<14)-256: + return 16 << 4 + case userSize <= (1<<15)-512: + return 16 << 5 + case userSize <= (1<<16)-1024: + return 16 << 6 + default: + return 16 << 7 + } +} diff --git a/src/runtime/malloc_test.go b/src/runtime/malloc_test.go index e028554b2386d5..cc2007604ddb6f 100644 --- a/src/runtime/malloc_test.go +++ b/src/runtime/malloc_test.go @@ -33,14 +33,14 @@ func TestMemStats(t *testing.T) { st := new(MemStats) ReadMemStats(st) - nz := func(x interface{}) error { + nz := func(x any) error { if x != reflect.Zero(reflect.TypeOf(x)).Interface() { return nil } return fmt.Errorf("zero value") } - le := func(thresh float64) func(interface{}) error { - return func(x interface{}) error { + le := func(thresh float64) func(any) error { + return func(x any) error { // These sanity tests aren't necessarily valid // with high -test.count values, so only run // them once. @@ -54,8 +54,8 @@ func TestMemStats(t *testing.T) { return fmt.Errorf("insanely high value (overflow?); want <= %v", thresh) } } - eq := func(x interface{}) func(interface{}) error { - return func(y interface{}) error { + eq := func(x any) func(any) error { + return func(y any) error { if x == y { return nil } @@ -64,7 +64,7 @@ func TestMemStats(t *testing.T) { } // Of the uint fields, HeapReleased, HeapIdle can be 0. // PauseTotalNs can be 0 if timer resolution is poor. - fields := map[string][]func(interface{}) error{ + fields := map[string][]func(any) error{ "Alloc": {nz, le(1e10)}, "TotalAlloc": {nz, le(1e11)}, "Sys": {nz, le(1e10)}, "Lookups": {eq(uint64(0))}, "Mallocs": {nz, le(1e10)}, "Frees": {nz, le(1e10)}, "HeapAlloc": {nz, le(1e10)}, "HeapSys": {nz, le(1e10)}, "HeapIdle": {le(1e10)}, @@ -173,12 +173,6 @@ func TestTinyAlloc(t *testing.T) { } } -var ( - tinyByteSink *byte - tinyUint32Sink *uint32 - tinyObj12Sink *obj12 -) - type obj12 struct { a uint64 b uint32 @@ -198,37 +192,39 @@ func TestTinyAllocIssue37262(t *testing.T) { runtime.GC() runtime.GC() + // Disable preemption so we stay on one P's tiny allocator and + // nothing else allocates from it. + runtime.Acquirem() + // Make 1-byte allocations until we get a fresh tiny slot. aligned := false for i := 0; i < 16; i++ { - tinyByteSink = new(byte) - if uintptr(unsafe.Pointer(tinyByteSink))&0xf == 0xf { + x := runtime.Escape(new(byte)) + if uintptr(unsafe.Pointer(x))&0xf == 0xf { aligned = true break } } if !aligned { + runtime.Releasem() t.Fatal("unable to get a fresh tiny slot") } // Create a 4-byte object so that the current // tiny slot is partially filled. - tinyUint32Sink = new(uint32) + runtime.Escape(new(uint32)) // Create a 12-byte object, which fits into the // tiny slot. If it actually gets place there, // then the field "a" will be improperly aligned // for atomic access on 32-bit architectures. // This won't be true if issue 36606 gets resolved. - tinyObj12Sink = new(obj12) + tinyObj12 := runtime.Escape(new(obj12)) // Try to atomically access "x.a". - atomic.StoreUint64(&tinyObj12Sink.a, 10) + atomic.StoreUint64(&tinyObj12.a, 10) - // Clear the sinks. - tinyByteSink = nil - tinyUint32Sink = nil - tinyObj12Sink = nil + runtime.Releasem() } func TestPageCacheLeak(t *testing.T) { diff --git a/src/runtime/map.go b/src/runtime/map.go index 111db56b01a2fe..65be4727fdee7f 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -54,9 +54,10 @@ package runtime // before the table grows. Typical tables will be somewhat less loaded. import ( + "internal/abi" + "internal/goarch" "runtime/internal/atomic" "runtime/internal/math" - "runtime/internal/sys" "unsafe" ) @@ -103,7 +104,7 @@ const ( sameSizeGrow = 8 // the current map growth is to a new map of the same size // sentinel bucket ID for iterator checks - noCheck = 1<<(8*sys.PtrSize) - 1 + noCheck = 1<<(8*goarch.PtrSize) - 1 ) // isEmpty reports whether the given tophash array entry represents an empty bucket entry. @@ -159,8 +160,8 @@ type bmap struct { } // A hash iteration structure. -// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go to indicate -// the layout of this structure. +// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go +// and reflect/value.go to match the layout of this structure. type hiter struct { key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/compile/internal/walk/range.go). elem unsafe.Pointer // Must be in second position (see cmd/compile/internal/walk/range.go). @@ -182,7 +183,7 @@ type hiter struct { // bucketShift returns 1<> (sys.PtrSize*8 - 8)) + top := uint8(hash >> (goarch.PtrSize*8 - 8)) if top < minTopHash { top += minTopHash } @@ -205,11 +206,11 @@ func evacuated(b *bmap) bool { } func (b *bmap) overflow(t *maptype) *bmap { - return *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) + return *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-goarch.PtrSize)) } func (b *bmap) setoverflow(t *maptype, ovf *bmap) { - *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize)) = ovf + *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-goarch.PtrSize)) = ovf } func (b *bmap) keys() unsafe.Pointer { @@ -394,13 +395,16 @@ func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets un func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { if raceenabled && h != nil { callerpc := getcallerpc() - pc := funcPC(mapaccess1) + pc := abi.FuncPCABIInternal(mapaccess1) racereadpc(unsafe.Pointer(h), callerpc, pc) raceReadObjectPC(t.key, key, callerpc, pc) } if msanenabled && h != nil { msanread(key, t.key.size) } + if asanenabled && h != nil { + asanread(key, t.key.size) + } if h == nil || h.count == 0 { if t.hashMightPanic() { t.hasher(key, 0) // see issue 23734 @@ -408,7 +412,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { return unsafe.Pointer(&zeroVal[0]) } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } hash := t.hasher(key, uintptr(h.hash0)) m := bucketMask(h.B) @@ -452,13 +456,16 @@ bucketloop: func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) { if raceenabled && h != nil { callerpc := getcallerpc() - pc := funcPC(mapaccess2) + pc := abi.FuncPCABIInternal(mapaccess2) racereadpc(unsafe.Pointer(h), callerpc, pc) raceReadObjectPC(t.key, key, callerpc, pc) } if msanenabled && h != nil { msanread(key, t.key.size) } + if asanenabled && h != nil { + asanread(key, t.key.size) + } if h == nil || h.count == 0 { if t.hashMightPanic() { t.hasher(key, 0) // see issue 23734 @@ -466,7 +473,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) return unsafe.Pointer(&zeroVal[0]), false } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } hash := t.hasher(key, uintptr(h.hash0)) m := bucketMask(h.B) @@ -574,15 +581,18 @@ func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { } if raceenabled { callerpc := getcallerpc() - pc := funcPC(mapassign) + pc := abi.FuncPCABIInternal(mapassign) racewritepc(unsafe.Pointer(h), callerpc, pc) raceReadObjectPC(t.key, key, callerpc, pc) } if msanenabled { msanread(key, t.key.size) } + if asanenabled { + asanread(key, t.key.size) + } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(key, uintptr(h.hash0)) @@ -673,7 +683,7 @@ bucketloop: done: if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting if t.indirectelem() { @@ -685,13 +695,16 @@ done: func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { if raceenabled && h != nil { callerpc := getcallerpc() - pc := funcPC(mapdelete) + pc := abi.FuncPCABIInternal(mapdelete) racewritepc(unsafe.Pointer(h), callerpc, pc) raceReadObjectPC(t.key, key, callerpc, pc) } if msanenabled && h != nil { msanread(key, t.key.size) } + if asanenabled && h != nil { + asanread(key, t.key.size) + } if h == nil || h.count == 0 { if t.hashMightPanic() { t.hasher(key, 0) // see issue 23734 @@ -699,7 +712,7 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { return } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(key, uintptr(h.hash0)) @@ -790,7 +803,7 @@ search: } if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting } @@ -802,17 +815,17 @@ search: func mapiterinit(t *maptype, h *hmap, it *hiter) { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiterinit)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiterinit)) } + it.t = t if h == nil || h.count == 0 { return } - if unsafe.Sizeof(hiter{})/sys.PtrSize != 12 { + if unsafe.Sizeof(hiter{})/goarch.PtrSize != 12 { throw("hash_iter size incorrect") // see cmd/compile/internal/reflectdata/reflect.go } - it.t = t it.h = h // grab snapshot of bucket state @@ -829,9 +842,11 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) { } // decide where to start - r := uintptr(fastrand()) + var r uintptr if h.B > 31-bucketCntBits { - r += uintptr(fastrand()) << 31 + r = uintptr(fastrand64()) + } else { + r = uintptr(fastrand()) } it.startBucket = r & bucketMask(h.B) it.offset = uint8(r >> h.B & (bucketCnt - 1)) @@ -852,10 +867,10 @@ func mapiternext(it *hiter) { h := it.h if raceenabled { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiternext)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiternext)) } if h.flags&hashWriting != 0 { - throw("concurrent map iteration and map write") + fatal("concurrent map iteration and map write") } t := it.t bucket := it.bucket @@ -978,7 +993,7 @@ next: func mapclear(t *maptype, h *hmap) { if raceenabled && h != nil { callerpc := getcallerpc() - pc := funcPC(mapclear) + pc := abi.FuncPCABIInternal(mapclear) racewritepc(unsafe.Pointer(h), callerpc, pc) } @@ -987,7 +1002,7 @@ func mapclear(t *maptype, h *hmap) { } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags ^= hashWriting @@ -1018,7 +1033,7 @@ func mapclear(t *maptype, h *hmap) { } if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting } @@ -1280,11 +1295,11 @@ func reflect_makemap(t *maptype, cap int) *hmap { if t.key.equal == nil { throw("runtime.reflect_makemap: unsupported map key type") } - if t.key.size > maxKeySize && (!t.indirectkey() || t.keysize != uint8(sys.PtrSize)) || + if t.key.size > maxKeySize && (!t.indirectkey() || t.keysize != uint8(goarch.PtrSize)) || t.key.size <= maxKeySize && (t.indirectkey() || t.keysize != uint8(t.key.size)) { throw("key size wrong") } - if t.elem.size > maxElemSize && (!t.indirectelem() || t.elemsize != uint8(sys.PtrSize)) || + if t.elem.size > maxElemSize && (!t.indirectelem() || t.elemsize != uint8(goarch.PtrSize)) || t.elem.size <= maxElemSize && (t.indirectelem() || t.elemsize != uint8(t.elem.size)) { throw("elem size wrong") } @@ -1323,22 +1338,41 @@ func reflect_mapaccess(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { return elem } +//go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr +func reflect_mapaccess_faststr(t *maptype, h *hmap, key string) unsafe.Pointer { + elem, ok := mapaccess2_faststr(t, h, key) + if !ok { + // reflect wants nil for a missing element + elem = nil + } + return elem +} + //go:linkname reflect_mapassign reflect.mapassign func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, elem unsafe.Pointer) { p := mapassign(t, h, key) typedmemmove(t.elem, p, elem) } +//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr +func reflect_mapassign_faststr(t *maptype, h *hmap, key string, elem unsafe.Pointer) { + p := mapassign_faststr(t, h, key) + typedmemmove(t.elem, p, elem) +} + //go:linkname reflect_mapdelete reflect.mapdelete func reflect_mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { mapdelete(t, h, key) } +//go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr +func reflect_mapdelete_faststr(t *maptype, h *hmap, key string) { + mapdelete_faststr(t, h, key) +} + //go:linkname reflect_mapiterinit reflect.mapiterinit -func reflect_mapiterinit(t *maptype, h *hmap) *hiter { - it := new(hiter) +func reflect_mapiterinit(t *maptype, h *hmap, it *hiter) { mapiterinit(t, h, it) - return it } //go:linkname reflect_mapiternext reflect.mapiternext @@ -1363,7 +1397,7 @@ func reflect_maplen(h *hmap) int { } if raceenabled { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(reflect_maplen)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(reflect_maplen)) } return h.count } @@ -1375,7 +1409,7 @@ func reflectlite_maplen(h *hmap) int { } if raceenabled { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(reflect_maplen)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(reflect_maplen)) } return h.count } diff --git a/src/runtime/map_benchmark_test.go b/src/runtime/map_benchmark_test.go index d0becc9ddb8596..b46d2a4727a752 100644 --- a/src/runtime/map_benchmark_test.go +++ b/src/runtime/map_benchmark_test.go @@ -488,20 +488,20 @@ func BenchmarkMapStringConversion(b *testing.B) { var BoolSink bool func BenchmarkMapInterfaceString(b *testing.B) { - m := map[interface{}]bool{} + m := map[any]bool{} for i := 0; i < 100; i++ { m[fmt.Sprintf("%d", i)] = true } - key := (interface{})("A") + key := (any)("A") b.ResetTimer() for i := 0; i < b.N; i++ { BoolSink = m[key] } } func BenchmarkMapInterfacePtr(b *testing.B) { - m := map[interface{}]bool{} + m := map[any]bool{} for i := 0; i < 100; i++ { i := i diff --git a/src/runtime/map_fast32.go b/src/runtime/map_fast32.go index 8d52dad217afb0..01ea330950f2ca 100644 --- a/src/runtime/map_fast32.go +++ b/src/runtime/map_fast32.go @@ -5,20 +5,21 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_fast32)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]) } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } var b *bmap if h.B == 0 { @@ -52,13 +53,13 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_fast32)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]), false } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } var b *bmap if h.B == 0 { @@ -95,10 +96,10 @@ func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer { } if raceenabled { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast32)) } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -173,7 +174,7 @@ bucketloop: done: elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.elemsize)) if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting return elem @@ -185,10 +186,10 @@ func mapassign_fast32ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer } if raceenabled { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast32)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast32)) } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -263,7 +264,7 @@ bucketloop: done: elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*4+inserti*uintptr(t.elemsize)) if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting return elem @@ -272,13 +273,13 @@ done: func mapdelete_fast32(t *maptype, h *hmap, key uint32) { if raceenabled && h != nil { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast32)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapdelete_fast32)) } if h == nil || h.count == 0 { return } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -301,7 +302,7 @@ search: // Only clear key if there are pointers in it. // This can only happen if pointers are 32 bit // wide as 64 bit pointers do not fit into a 32 bit key. - if sys.PtrSize == 4 && t.key.ptrdata != 0 { + if goarch.PtrSize == 4 && t.key.ptrdata != 0 { // The key must be a pointer as we checked pointers are // 32 bits wide and the key is 32 bits wide also. *(*unsafe.Pointer)(k) = nil @@ -354,7 +355,7 @@ search: } if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting } @@ -427,7 +428,7 @@ func evacuate_fast32(t *maptype, h *hmap, oldbucket uintptr) { dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check // Copy key. - if sys.PtrSize == 4 && t.key.ptrdata != 0 && writeBarrier.enabled { + if goarch.PtrSize == 4 && t.key.ptrdata != 0 && writeBarrier.enabled { // Write with a write barrier. *(*unsafe.Pointer)(dst.k) = *(*unsafe.Pointer)(k) } else { diff --git a/src/runtime/map_fast64.go b/src/runtime/map_fast64.go index f1368dc774ec4d..2967360b769737 100644 --- a/src/runtime/map_fast64.go +++ b/src/runtime/map_fast64.go @@ -5,20 +5,21 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_fast64)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]) } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } var b *bmap if h.B == 0 { @@ -52,13 +53,13 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_fast64)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]), false } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } var b *bmap if h.B == 0 { @@ -95,10 +96,10 @@ func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { } if raceenabled { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast64)) } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -173,7 +174,7 @@ bucketloop: done: elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.elemsize)) if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting return elem @@ -185,10 +186,10 @@ func mapassign_fast64ptr(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer } if raceenabled { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_fast64)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_fast64)) } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -263,7 +264,7 @@ bucketloop: done: elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*8+inserti*uintptr(t.elemsize)) if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting return elem @@ -272,13 +273,13 @@ done: func mapdelete_fast64(t *maptype, h *hmap, key uint64) { if raceenabled && h != nil { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_fast64)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapdelete_fast64)) } if h == nil || h.count == 0 { return } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } hash := t.hasher(noescape(unsafe.Pointer(&key)), uintptr(h.hash0)) @@ -300,7 +301,7 @@ search: } // Only clear key if there are pointers in it. if t.key.ptrdata != 0 { - if sys.PtrSize == 8 { + if goarch.PtrSize == 8 { *(*unsafe.Pointer)(k) = nil } else { // There are three ways to squeeze at one ore more 32 bit pointers into 64 bits. @@ -356,7 +357,7 @@ search: } if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting } @@ -430,7 +431,7 @@ func evacuate_fast64(t *maptype, h *hmap, oldbucket uintptr) { // Copy key. if t.key.ptrdata != 0 && writeBarrier.enabled { - if sys.PtrSize == 8 { + if goarch.PtrSize == 8 { // Write with a write barrier. *(*unsafe.Pointer)(dst.k) = *(*unsafe.Pointer)(k) } else { diff --git a/src/runtime/map_faststr.go b/src/runtime/map_faststr.go index 0673dd39c8ff6a..006c24cee2fb33 100644 --- a/src/runtime/map_faststr.go +++ b/src/runtime/map_faststr.go @@ -5,20 +5,21 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_faststr)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]) } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } key := stringStructOf(&ky) if h.B == 0 { @@ -26,7 +27,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { b := (*bmap)(h.buckets) if key.len < 32 { // short key, doing lots of comparisons is ok - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || isEmpty(b.tophash[i]) { if b.tophash[i] == emptyRest { @@ -35,14 +36,14 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { continue } if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)) + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)) } } return unsafe.Pointer(&zeroVal[0]) } // long key, try not to do more comparisons than necessary keymaybe := uintptr(bucketCnt) - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || isEmpty(b.tophash[i]) { if b.tophash[i] == emptyRest { @@ -51,7 +52,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { continue } if k.str == key.str { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)) + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)) } // check first 4 bytes if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { @@ -68,9 +69,9 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer { keymaybe = i } if keymaybe != bucketCnt { - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*goarch.PtrSize)) if memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.elemsize)) + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+keymaybe*uintptr(t.elemsize)) } } return unsafe.Pointer(&zeroVal[0]) @@ -91,13 +92,13 @@ dohash: } top := tophash(hash) for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || b.tophash[i] != top { continue } if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)) + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)) } } } @@ -107,13 +108,13 @@ dohash: func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { if raceenabled && h != nil { callerpc := getcallerpc() - racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr)) + racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_faststr)) } if h == nil || h.count == 0 { return unsafe.Pointer(&zeroVal[0]), false } if h.flags&hashWriting != 0 { - throw("concurrent map read and map write") + fatal("concurrent map read and map write") } key := stringStructOf(&ky) if h.B == 0 { @@ -121,7 +122,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { b := (*bmap)(h.buckets) if key.len < 32 { // short key, doing lots of comparisons is ok - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || isEmpty(b.tophash[i]) { if b.tophash[i] == emptyRest { @@ -130,14 +131,14 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { continue } if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)), true + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)), true } } return unsafe.Pointer(&zeroVal[0]), false } // long key, try not to do more comparisons than necessary keymaybe := uintptr(bucketCnt) - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || isEmpty(b.tophash[i]) { if b.tophash[i] == emptyRest { @@ -146,7 +147,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { continue } if k.str == key.str { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)), true + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)), true } // check first 4 bytes if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) { @@ -163,9 +164,9 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) { keymaybe = i } if keymaybe != bucketCnt { - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*sys.PtrSize)) + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*goarch.PtrSize)) if memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+keymaybe*uintptr(t.elemsize)), true + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+keymaybe*uintptr(t.elemsize)), true } } return unsafe.Pointer(&zeroVal[0]), false @@ -186,13 +187,13 @@ dohash: } top := tophash(hash) for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || b.tophash[i] != top { continue } if k.str == key.str || memequal(k.str, key.str, uintptr(key.len)) { - return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)), true + return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)), true } } } @@ -205,10 +206,10 @@ func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer { } if raceenabled { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapassign_faststr)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapassign_faststr)) } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } key := stringStructOf(&s) hash := t.hasher(noescape(unsafe.Pointer(&s)), uintptr(h.hash0)) @@ -245,7 +246,7 @@ bucketloop: } continue } - k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*sys.PtrSize)) + k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*goarch.PtrSize)) if k.len != key.len { continue } @@ -283,15 +284,15 @@ bucketloop: } insertb.tophash[inserti&(bucketCnt-1)] = top // mask inserti to avoid bounds checks - insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*sys.PtrSize) + insertk = add(unsafe.Pointer(insertb), dataOffset+inserti*2*goarch.PtrSize) // store new key at insert position *((*stringStruct)(insertk)) = *key h.count++ done: - elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*sys.PtrSize+inserti*uintptr(t.elemsize)) + elem := add(unsafe.Pointer(insertb), dataOffset+bucketCnt*2*goarch.PtrSize+inserti*uintptr(t.elemsize)) if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting return elem @@ -300,13 +301,13 @@ done: func mapdelete_faststr(t *maptype, h *hmap, ky string) { if raceenabled && h != nil { callerpc := getcallerpc() - racewritepc(unsafe.Pointer(h), callerpc, funcPC(mapdelete_faststr)) + racewritepc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapdelete_faststr)) } if h == nil || h.count == 0 { return } if h.flags&hashWriting != 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } key := stringStructOf(&ky) @@ -324,7 +325,7 @@ func mapdelete_faststr(t *maptype, h *hmap, ky string) { top := tophash(hash) search: for ; b != nil; b = b.overflow(t) { - for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*sys.PtrSize) { + for i, kptr := uintptr(0), b.keys(); i < bucketCnt; i, kptr = i+1, add(kptr, 2*goarch.PtrSize) { k := (*stringStruct)(kptr) if k.len != key.len || b.tophash[i] != top { continue @@ -334,7 +335,7 @@ search: } // Clear key's pointer. k.str = nil - e := add(unsafe.Pointer(b), dataOffset+bucketCnt*2*sys.PtrSize+i*uintptr(t.elemsize)) + e := add(unsafe.Pointer(b), dataOffset+bucketCnt*2*goarch.PtrSize+i*uintptr(t.elemsize)) if t.elem.ptrdata != 0 { memclrHasPointers(e, t.elem.size) } else { @@ -382,7 +383,7 @@ search: } if h.flags&hashWriting == 0 { - throw("concurrent map writes") + fatal("concurrent map writes") } h.flags &^= hashWriting } @@ -410,7 +411,7 @@ func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { x := &xy[0] x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) x.k = add(unsafe.Pointer(x.b), dataOffset) - x.e = add(x.k, bucketCnt*2*sys.PtrSize) + x.e = add(x.k, bucketCnt*2*goarch.PtrSize) if !h.sameSizeGrow() { // Only calculate y pointers if we're growing bigger. @@ -418,13 +419,13 @@ func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { y := &xy[1] y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) y.k = add(unsafe.Pointer(y.b), dataOffset) - y.e = add(y.k, bucketCnt*2*sys.PtrSize) + y.e = add(y.k, bucketCnt*2*goarch.PtrSize) } for ; b != nil; b = b.overflow(t) { k := add(unsafe.Pointer(b), dataOffset) - e := add(k, bucketCnt*2*sys.PtrSize) - for i := 0; i < bucketCnt; i, k, e = i+1, add(k, 2*sys.PtrSize), add(e, uintptr(t.elemsize)) { + e := add(k, bucketCnt*2*goarch.PtrSize) + for i := 0; i < bucketCnt; i, k, e = i+1, add(k, 2*goarch.PtrSize), add(e, uintptr(t.elemsize)) { top := b.tophash[i] if isEmpty(top) { b.tophash[i] = evacuatedEmpty @@ -450,7 +451,7 @@ func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { dst.b = h.newoverflow(t, dst.b) dst.i = 0 dst.k = add(unsafe.Pointer(dst.b), dataOffset) - dst.e = add(dst.k, bucketCnt*2*sys.PtrSize) + dst.e = add(dst.k, bucketCnt*2*goarch.PtrSize) } dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check @@ -463,7 +464,7 @@ func evacuate_faststr(t *maptype, h *hmap, oldbucket uintptr) { // key or elem arrays. That's ok, as we have the overflow pointer // at the end of the bucket to protect against pointing past the // end of the bucket. - dst.k = add(dst.k, 2*sys.PtrSize) + dst.k = add(dst.k, 2*goarch.PtrSize) dst.e = add(dst.e, uintptr(t.elemsize)) } } diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go index 302b3c23c12b42..4afbae6bc411ea 100644 --- a/src/runtime/map_test.go +++ b/src/runtime/map_test.go @@ -6,10 +6,10 @@ package runtime_test import ( "fmt" + "internal/goarch" "math" "reflect" "runtime" - "runtime/internal/sys" "sort" "strconv" "strings" @@ -21,7 +21,7 @@ func TestHmapSize(t *testing.T) { // The structure of hmap is defined in runtime/map.go // and in cmd/compile/internal/gc/reflect.go and must be in sync. // The size of hmap should be 48 bytes on 64 bit and 28 bytes on 32 bit platforms. - var hmapSize = uintptr(8 + 5*sys.PtrSize) + var hmapSize = uintptr(8 + 5*goarch.PtrSize) if runtime.RuntimeHmapSize != hmapSize { t.Errorf("sizeof(runtime.hmap{})==%d, want %d", runtime.RuntimeHmapSize, hmapSize) } @@ -29,8 +29,9 @@ func TestHmapSize(t *testing.T) { } // negative zero is a good test because: -// 1) 0 and -0 are equal, yet have distinct representations. -// 2) 0 is represented as all zeros, -0 isn't. +// 1. 0 and -0 are equal, yet have distinct representations. +// 2. 0 is represented as all zeros, -0 isn't. +// // I'm not sure the language spec actually requires this behavior, // but it's what the current map implementation does. func TestNegativeZero(t *testing.T) { @@ -473,7 +474,7 @@ func TestMapNanGrowIterator(t *testing.T) { nan := math.NaN() const nBuckets = 16 // To fill nBuckets buckets takes LOAD * nBuckets keys. - nKeys := int(nBuckets * *runtime.HashLoad) + nKeys := int(nBuckets * runtime.HashLoad) // Get map to full point with nan keys. for i := 0; i < nKeys; i++ { @@ -672,8 +673,6 @@ func TestIgnoreBogusMapHint(t *testing.T) { } } -var mapSink map[int]int - var mapBucketTests = [...]struct { n int // n is the number of map elements noescape int // number of expected buckets for non-escaping map @@ -709,7 +708,7 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(localMap); got != tt.noescape { t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) } - escapingMap := map[int]int{} + escapingMap := runtime.Escape(map[int]int{}) if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) } @@ -719,7 +718,6 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got) } - mapSink = escapingMap } }) t.Run("nohint", func(t *testing.T) { @@ -734,7 +732,7 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(localMap); got != tt.noescape { t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) } - escapingMap := make(map[int]int) + escapingMap := runtime.Escape(make(map[int]int)) if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) } @@ -744,7 +742,6 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) } - mapSink = escapingMap } }) t.Run("makemap", func(t *testing.T) { @@ -759,7 +756,7 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(localMap); got != tt.noescape { t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) } - escapingMap := make(map[int]int, tt.n) + escapingMap := runtime.Escape(make(map[int]int, tt.n)) if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) } @@ -769,7 +766,6 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) } - mapSink = escapingMap } }) t.Run("makemap64", func(t *testing.T) { @@ -784,7 +780,7 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(localMap); got != tt.noescape { t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got) } - escapingMap := make(map[int]int, tt.n) + escapingMap := runtime.Escape(make(map[int]int, tt.n)) if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) { t.Errorf("escape: buckets pointer is nil for n=%d buckets", count) } @@ -794,7 +790,6 @@ func TestMapBuckets(t *testing.T) { if got := runtime.MapBucketsCount(escapingMap); got != tt.escape { t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got) } - mapSink = escapingMap } }) @@ -1050,7 +1045,7 @@ func BenchmarkMapDelete(b *testing.B) { func TestDeferDeleteSlow(t *testing.T) { ks := []complex128{0, 1, 2, 3} - m := make(map[interface{}]int) + m := make(map[any]int) for i, k := range ks { m[k] = i } @@ -1193,14 +1188,14 @@ func TestMapInterfaceKey(t *testing.T) { c64 complex64 c128 complex128 s string - i0 interface{} + i0 any i1 interface { String() string } a [4]string } - m := map[interface{}]bool{} + m := map[any]bool{} // Put a bunch of data in m, so that a bad hash is likely to // lead to a bad bucket, which will lead to a missed lookup. for i := 0; i < 1000; i++ { diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index 4994347bdeb356..c3b45415a95339 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -15,7 +15,7 @@ package runtime import ( "internal/abi" - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -147,7 +147,7 @@ import ( // remove the deletion barrier, we'll have to work out a new way to // handle the profile logging. -// typedmemmove copies a value of type t to dst from src. +// typedmemmove copies a value of type typ to dst from src. // Must be nosplit, see #16026. // // TODO: Perfect for go:nosplitrec since we can't have a safe point @@ -177,13 +177,17 @@ func typedmemmove(typ *_type, dst, src unsafe.Pointer) { //go:linkname reflect_typedmemmove reflect.typedmemmove func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) { if raceenabled { - raceWriteObjectPC(typ, dst, getcallerpc(), funcPC(reflect_typedmemmove)) - raceReadObjectPC(typ, src, getcallerpc(), funcPC(reflect_typedmemmove)) + raceWriteObjectPC(typ, dst, getcallerpc(), abi.FuncPCABIInternal(reflect_typedmemmove)) + raceReadObjectPC(typ, src, getcallerpc(), abi.FuncPCABIInternal(reflect_typedmemmove)) } if msanenabled { msanwrite(dst, typ.size) msanread(src, typ.size) } + if asanenabled { + asanwrite(dst, typ.size) + asanread(src, typ.size) + } typedmemmove(typ, dst, src) } @@ -194,14 +198,15 @@ func reflectlite_typedmemmove(typ *_type, dst, src unsafe.Pointer) { // typedmemmovepartial is like typedmemmove but assumes that // dst and src point off bytes into the value and only copies size bytes. -// off must be a multiple of sys.PtrSize. +// off must be a multiple of goarch.PtrSize. +// //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { - if writeBarrier.needed && typ.ptrdata > off && size >= sys.PtrSize { - if off&(sys.PtrSize-1) != 0 { + if writeBarrier.needed && typ.ptrdata > off && size >= goarch.PtrSize { + if off&(goarch.PtrSize-1) != 0 { panic("reflect: internal error: misaligned offset") } - pwsize := alignDown(size, sys.PtrSize) + pwsize := alignDown(size, goarch.PtrSize) if poff := typ.ptrdata - off; pwsize > poff { pwsize = poff } @@ -225,7 +230,7 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size // //go:nosplit func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr, regs *abi.RegArgs) { - if writeBarrier.needed && typ != nil && typ.ptrdata != 0 && size >= sys.PtrSize { + if writeBarrier.needed && typ != nil && typ.ptrdata != 0 && size >= goarch.PtrSize { bulkBarrierPreWrite(uintptr(dst), uintptr(src), size) } memmove(dst, src, size) @@ -254,7 +259,7 @@ func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe // code and needs its own instrumentation. if raceenabled { callerpc := getcallerpc() - pc := funcPC(slicecopy) + pc := abi.FuncPCABIInternal(slicecopy) racewriterangepc(dstPtr, uintptr(n)*typ.size, callerpc, pc) racereadrangepc(srcPtr, uintptr(n)*typ.size, callerpc, pc) } @@ -262,6 +267,10 @@ func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe msanwrite(dstPtr, uintptr(n)*typ.size) msanread(srcPtr, uintptr(n)*typ.size) } + if asanenabled { + asanwrite(dstPtr, uintptr(n)*typ.size) + asanread(srcPtr, uintptr(n)*typ.size) + } if writeBarrier.cgo { cgoCheckSliceCopy(typ, dstPtr, srcPtr, n) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 32b8db7a50282c..8bb24321a066c5 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -14,56 +14,40 @@ // // Heap bitmap // -// The heap bitmap comprises 2 bits for each pointer-sized word in the heap, -// stored in the heapArena metadata backing each heap arena. -// That is, if ha is the heapArena for the arena starting a start, -// then ha.bitmap[0] holds the 2-bit entries for the four words start -// through start+3*ptrSize, ha.bitmap[1] holds the entries for -// start+4*ptrSize through start+7*ptrSize, and so on. +// The heap bitmap comprises 1 bit for each pointer-sized word in the heap, +// recording whether a pointer is stored in that word or not. This bitmap +// is stored in the heapArena metadata backing each heap arena. +// That is, if ha is the heapArena for the arena starting at "start", +// then ha.bitmap[0] holds the 64 bits for the 64 words "start" +// through start+63*ptrSize, ha.bitmap[1] holds the entries for +// start+64*ptrSize through start+127*ptrSize, and so on. +// Bits correspond to words in little-endian order. ha.bitmap[0]&1 represents +// the word at "start", ha.bitmap[0]>>1&1 represents the word at start+8, etc. +// (For 32-bit platforms, s/64/32/.) // -// In each 2-bit entry, the lower bit is a pointer/scalar bit, just -// like in the stack/data bitmaps described above. The upper bit -// indicates scan/dead: a "1" value ("scan") indicates that there may -// be pointers in later words of the allocation, and a "0" value -// ("dead") indicates there are no more pointers in the allocation. If -// the upper bit is 0, the lower bit must also be 0, and this -// indicates scanning can ignore the rest of the allocation. +// We also keep a noMorePtrs bitmap which allows us to stop scanning +// the heap bitmap early in certain situations. If ha.noMorePtrs[i]>>j&1 +// is 1, then the object containing the last word described by ha.bitmap[8*i+j] +// has no more pointers beyond those described by ha.bitmap[8*i+j]. +// If ha.noMorePtrs[i]>>j&1 is set, the entries in ha.bitmap[8*i+j+1] and +// beyond must all be zero until the start of the next object. // -// The 2-bit entries are split when written into the byte, so that the top half -// of the byte contains 4 high (scan) bits and the bottom half contains 4 low -// (pointer) bits. This form allows a copy from the 1-bit to the 4-bit form to -// keep the pointer bits contiguous, instead of having to space them out. +// The bitmap for noscan spans is set to all zero at span allocation time. // -// The code makes use of the fact that the zero value for a heap -// bitmap means scalar/dead. This property must be preserved when -// modifying the encoding. -// -// The bitmap for noscan spans is not maintained. Code must ensure -// that an object is scannable before consulting its bitmap by -// checking either the noscan bit in the span or by consulting its -// type's information. +// The bitmap for unallocated objects in scannable spans is not maintained +// (can be junk). package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" ) -const ( - bitPointer = 1 << 0 - bitScan = 1 << 4 - - heapBitsShift = 1 // shift offset between successive bitPointer or bitScan entries - wordsPerBitmapByte = 8 / 2 // heap words described by one bitmap byte - - // all scan/pointer bits in a byte - bitScanAll = bitScan | bitScan<>63)*32)) @@ -380,16 +327,17 @@ func badPointer(s *mspan, p, refBase, refOff uintptr) { // // It is nosplit so it is safe for p to be a pointer to the current goroutine's stack. // Since p is a uintptr, it would not be adjusted if the stack were to move. +// //go:nosplit func findObject(p, refBase, refOff uintptr) (base uintptr, s *mspan, objIndex uintptr) { s = spanOf(p) // If s is nil, the virtual address has never been part of the heap. // This pointer may be to some mmap'd region, so we allow it. if s == nil { - if GOARCH == "amd64" && p == clobberdeadPtr && debug.invalidptr != 0 { - // Crash if clobberdeadPtr is seen. Only on AMD64 for now, as - // it is the only platform where compiler's clobberdead mode is - // implemented. On AMD64 clobberdeadPtr cannot be a valid address. + if (GOARCH == "amd64" || GOARCH == "arm64") && p == clobberdeadPtr && debug.invalidptr != 0 { + // Crash if clobberdeadPtr is seen. Only on AMD64 and ARM64 for now, + // as they are the only platform where compiler's clobberdead mode is + // implemented. On these platforms clobberdeadPtr cannot be a valid address. badPointer(s, p, refBase, refOff) } return @@ -416,117 +364,144 @@ func findObject(p, refBase, refOff uintptr) (base uintptr, s *mspan, objIndex ui return } -// next returns the heapBits describing the next pointer-sized word in memory. -// That is, if h describes address p, h.next() describes p+ptrSize. -// Note that next does not modify h. The caller must record the result. +// verifyNotInHeapPtr reports whether converting the not-in-heap pointer into a unsafe.Pointer is ok. // -// nosplit because it is used during write barriers and must not be preempted. -//go:nosplit -func (h heapBits) next() heapBits { - if h.shift < 3*heapBitsShift { - h.shift += heapBitsShift - } else if h.bitp != h.last { - h.bitp, h.shift = add1(h.bitp), 0 - } else { - // Move to the next arena. - return h.nextArena() - } - return h +//go:linkname reflect_verifyNotInHeapPtr reflect.verifyNotInHeapPtr +func reflect_verifyNotInHeapPtr(p uintptr) bool { + // Conversion to a pointer is ok as long as findObject above does not call badPointer. + // Since we're already promised that p doesn't point into the heap, just disallow heap + // pointers and the special clobbered pointer. + return spanOf(p) == nil && p != clobberdeadPtr } -// nextArena advances h to the beginning of the next heap arena. -// -// This is a slow-path helper to next. gc's inliner knows that -// heapBits.next can be inlined even though it calls this. This is -// marked noinline so it doesn't get inlined into next and cause next -// to be too big to inline. -// -//go:nosplit -//go:noinline -func (h heapBits) nextArena() heapBits { - h.arena++ - ai := arenaIdx(h.arena) - l2 := mheap_.arenas[ai.l1()] - if l2 == nil { - // We just passed the end of the object, which - // was also the end of the heap. Poison h. It - // should never be dereferenced at this point. - return heapBits{} - } - ha := l2[ai.l2()] - if ha == nil { - return heapBits{} - } - h.bitp, h.shift = &ha.bitmap[0], 0 - h.last = &ha.bitmap[len(ha.bitmap)-1] - return h +const ptrBits = 8 * goarch.PtrSize + +// heapBits provides access to the bitmap bits for a single heap word. +// The methods on heapBits take value receivers so that the compiler +// can more easily inline calls to those methods and registerize the +// struct fields independently. +type heapBits struct { + // heapBits will report on pointers in the range [addr,addr+size). + // The low bit of mask contains the pointerness of the word at addr + // (assuming valid>0). + addr, size uintptr + + // The next few pointer bits representing words starting at addr. + // Those bits already returned by next() are zeroed. + mask uintptr + // Number of bits in mask that are valid. mask is always less than 1<> off + valid := ptrBits - off + + // Process depending on where the object ends. + nptr := size / goarch.PtrSize + if nptr < valid { + // Bits for this object end before the end of this bitmap word. + // Squash bits for the following objects. + mask &= 1<<(nptr&(ptrBits-1)) - 1 + valid = nptr + } else if nptr == valid { + // Bits for this object end at exactly the end of this bitmap word. + // All good. } else { - h.bitp, h.last = nil, nil + // Bits for this object extend into the next bitmap word. See if there + // may be any pointers recorded there. + if uintptr(ha.noMorePtrs[idx/8])>>(idx%8)&1 != 0 { + // No more pointers in this object after this bitmap word. + // Update size so we know not to look there. + size = valid * goarch.PtrSize + } } - return h -} -// forwardOrBoundary is like forward, but stops at boundaries between -// contiguous sections of the bitmap. It returns the number of words -// advanced over, which will be <= n. -func (h heapBits) forwardOrBoundary(n uintptr) (heapBits, uintptr) { - maxn := 4 * ((uintptr(unsafe.Pointer(h.last)) + 1) - uintptr(unsafe.Pointer(h.bitp))) - if n > maxn { - n = maxn - } - return h.forward(n), n + return heapBits{addr: addr, size: size, mask: mask, valid: valid} } -// The caller can test morePointers and isPointer by &-ing with bitScan and bitPointer. -// The result includes in its higher bits the bits for subsequent words -// described by the same bitmap byte. +// Returns the (absolute) address of the next known pointer and +// a heapBits iterator representing any remaining pointers. +// If there are no more pointers, returns address 0. +// Note that next does not modify h. The caller must record the result. // // nosplit because it is used during write barriers and must not be preempted. +// //go:nosplit -func (h heapBits) bits() uint32 { - // The (shift & 31) eliminates a test and conditional branch - // from the generated code. - return uint32(*h.bitp) >> (h.shift & 31) -} +func (h heapBits) next() (heapBits, uintptr) { + for { + if h.mask != 0 { + var i int + if goarch.PtrSize == 8 { + i = sys.Ctz64(uint64(h.mask)) + } else { + i = sys.Ctz32(uint32(h.mask)) + } + h.mask ^= uintptr(1) << (i & (ptrBits - 1)) + return h, h.addr + uintptr(i)*goarch.PtrSize + } -// morePointers reports whether this word and all remaining words in this object -// are scalars. -// h must not describe the second word of the object. -func (h heapBits) morePointers() bool { - return h.bits()&bitScan != 0 + // Skip words that we've already processed. + h.addr += h.valid * goarch.PtrSize + h.size -= h.valid * goarch.PtrSize + if h.size == 0 { + return h, 0 // no more pointers + } + + // Grab more bits and try again. + h = heapBitsForAddr(h.addr, h.size) + } } -// isPointer reports whether the heap bits describe a pointer word. +// nextFast is like next, but can return 0 even when there are more pointers +// to be found. Callers should call next if nextFast returns 0 as its second +// return value. +// +// if addr, h = h.nextFast(); addr == 0 { +// if addr, h = h.next(); addr == 0 { +// ... no more pointers ... +// } +// } +// ... process pointer at addr ... +// +// nextFast is designed to be inlineable. // -// nosplit because it is used during write barriers and must not be preempted. //go:nosplit -func (h heapBits) isPointer() bool { - return h.bits()&bitPointer != 0 +func (h heapBits) nextFast() (heapBits, uintptr) { + // TESTQ/JEQ + if h.mask == 0 { + return h, 0 + } + // BSFQ + var i int + if goarch.PtrSize == 8 { + i = sys.Ctz64(uint64(h.mask)) + } else { + i = sys.Ctz32(uint32(h.mask)) + } + // BTCQ + h.mask ^= uintptr(1) << (i & (ptrBits - 1)) + // LEAQ (XX)(XX*8) + return h, h.addr + uintptr(i)*goarch.PtrSize } // bulkBarrierPreWrite executes a write barrier @@ -557,7 +532,7 @@ func (h heapBits) isPointer() bool { // //go:nosplit func bulkBarrierPreWrite(dst, src, size uintptr) { - if (dst|src|size)&(sys.PtrSize-1) != 0 { + if (dst|src|size)&(goarch.PtrSize-1) != 0 { throw("bulkBarrierPreWrite: unaligned arguments") } if !writeBarrier.needed { @@ -590,27 +565,29 @@ func bulkBarrierPreWrite(dst, src, size uintptr) { } buf := &getg().m.p.ptr().wbBuf - h := heapBitsForAddr(dst) + h := heapBitsForAddr(dst, size) if src == 0 { - for i := uintptr(0); i < size; i += sys.PtrSize { - if h.isPointer() { - dstx := (*uintptr)(unsafe.Pointer(dst + i)) - if !buf.putFast(*dstx, 0) { - wbBufFlush(nil, 0) - } + for { + var addr uintptr + if h, addr = h.next(); addr == 0 { + break + } + dstx := (*uintptr)(unsafe.Pointer(addr)) + if !buf.putFast(*dstx, 0) { + wbBufFlush(nil, 0) } - h = h.next() } } else { - for i := uintptr(0); i < size; i += sys.PtrSize { - if h.isPointer() { - dstx := (*uintptr)(unsafe.Pointer(dst + i)) - srcx := (*uintptr)(unsafe.Pointer(src + i)) - if !buf.putFast(*dstx, *srcx) { - wbBufFlush(nil, 0) - } + for { + var addr uintptr + if h, addr = h.next(); addr == 0 { + break + } + dstx := (*uintptr)(unsafe.Pointer(addr)) + srcx := (*uintptr)(unsafe.Pointer(src + (addr - dst))) + if !buf.putFast(*dstx, *srcx) { + wbBufFlush(nil, 0) } - h = h.next() } } } @@ -623,24 +600,26 @@ func bulkBarrierPreWrite(dst, src, size uintptr) { // // This is used for special cases where e.g. dst was just // created and zeroed with malloc. +// //go:nosplit func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) { - if (dst|src|size)&(sys.PtrSize-1) != 0 { + if (dst|src|size)&(goarch.PtrSize-1) != 0 { throw("bulkBarrierPreWrite: unaligned arguments") } if !writeBarrier.needed { return } buf := &getg().m.p.ptr().wbBuf - h := heapBitsForAddr(dst) - for i := uintptr(0); i < size; i += sys.PtrSize { - if h.isPointer() { - srcx := (*uintptr)(unsafe.Pointer(src + i)) - if !buf.putFast(0, *srcx) { - wbBufFlush(nil, 0) - } + h := heapBitsForAddr(dst, size) + for { + var addr uintptr + if h, addr = h.next(); addr == 0 { + break + } + srcx := (*uintptr)(unsafe.Pointer(addr - dst + src)) + if !buf.putFast(0, *srcx) { + wbBufFlush(nil, 0) } - h = h.next() } } @@ -653,17 +632,17 @@ func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) { // //go:nosplit func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) { - word := maskOffset / sys.PtrSize + word := maskOffset / goarch.PtrSize bits = addb(bits, word/8) mask := uint8(1) << (word % 8) buf := &getg().m.p.ptr().wbBuf - for i := uintptr(0); i < size; i += sys.PtrSize { + for i := uintptr(0); i < size; i += goarch.PtrSize { if mask == 0 { bits = addb(bits, 1) if *bits == 0 { // Skip 8 words. - i += 7 * sys.PtrSize + i += 7 * goarch.PtrSize continue } mask = 1 @@ -720,8 +699,8 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) { ptrmask := typ.gcdata buf := &getg().m.p.ptr().wbBuf var bits uint32 - for i := uintptr(0); i < typ.ptrdata; i += sys.PtrSize { - if i&(sys.PtrSize*8-1) == 0 { + for i := uintptr(0); i < typ.ptrdata; i += goarch.PtrSize { + if i&(goarch.PtrSize*8-1) == 0 { bits = uint32(*ptrmask) ptrmask = addb(ptrmask, 1) } else { @@ -737,43 +716,31 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) { } } -// The methods operating on spans all require that h has been returned -// by heapBitsForSpan and that size, n, total are the span layout description -// returned by the mspan's layout method. -// If total > size*n, it means that there is extra leftover memory in the span, -// usually due to rounding. -// -// TODO(rsc): Perhaps introduce a different heapBitsSpan type. - -// initSpan initializes the heap bitmap for a span. -// If this is a span of pointer-sized objects, it initializes all -// words to pointer/scan. -// Otherwise, it initializes all words to scalar/dead. -func (h heapBits) initSpan(s *mspan) { - // Clear bits corresponding to objects. - nw := (s.npages << _PageShift) / sys.PtrSize - if nw%wordsPerBitmapByte != 0 { - throw("initSpan: unaligned length") +// initHeapBits initializes the heap bitmap for a span. +// If this is a span of single pointer allocations, it initializes all +// words to pointer. +func (s *mspan) initHeapBits() { + if s.spanclass.noscan() { + // Set all the pointer bits to zero. We do this once + // when the span is allocated so we don't have to do it + // for each object allocation. + base := s.base() + size := s.npages * pageSize + h := writeHeapBitsForAddr(base) + h.flush(base, size) + return } - if h.shift != 0 { - throw("initSpan: unaligned base") + isPtrs := goarch.PtrSize == 8 && s.elemsize == goarch.PtrSize + if !isPtrs { + return // nothing to do } - isPtrs := sys.PtrSize == 8 && s.elemsize == sys.PtrSize - for nw > 0 { - hNext, anw := h.forwardOrBoundary(nw) - nbyte := anw / wordsPerBitmapByte - if isPtrs { - bitp := h.bitp - for i := uintptr(0); i < nbyte; i++ { - *bitp = bitPointerAll | bitScanAll - bitp = add1(bitp) - } - } else { - memclrNoHeapPointers(unsafe.Pointer(h.bitp), nbyte) - } - h = hNext - nw -= anw + h := writeHeapBitsForAddr(s.base()) + size := s.npages * pageSize + nptrs := size / goarch.PtrSize + for i := uintptr(0); i < nptrs; i += ptrBits { + h = h.write(^uintptr(0), ptrBits) } + h.flush(s.base(), size) } // countAlloc returns the number of objects allocated in span s by @@ -796,6 +763,159 @@ func (s *mspan) countAlloc() int { return count } +type writeHeapBits struct { + addr uintptr // address that the low bit of mask represents the pointer state of. + mask uintptr // some pointer bits starting at the address addr. + valid uintptr // number of bits in buf that are valid (including low) + low uintptr // number of low-order bits to not overwrite +} + +func writeHeapBitsForAddr(addr uintptr) (h writeHeapBits) { + // We start writing bits maybe in the middle of a heap bitmap word. + // Remember how many bits into the word we started, so we can be sure + // not to overwrite the previous bits. + h.low = addr / goarch.PtrSize % ptrBits + + // round down to heap word that starts the bitmap word. + h.addr = addr - h.low*goarch.PtrSize + + // We don't have any bits yet. + h.mask = 0 + h.valid = h.low + + return +} + +// write appends the pointerness of the next valid pointer slots +// using the low valid bits of bits. 1=pointer, 0=scalar. +func (h writeHeapBits) write(bits, valid uintptr) writeHeapBits { + if h.valid+valid <= ptrBits { + // Fast path - just accumulate the bits. + h.mask |= bits << h.valid + h.valid += valid + return h + } + // Too many bits to fit in this word. Write the current word + // out and move on to the next word. + + data := h.mask | bits<> (ptrBits - h.valid) // leftover for next word + h.valid += valid - ptrBits // have h.valid+valid bits, writing ptrBits of them + + // Flush mask to the memory bitmap. + // TODO: figure out how to cache arena lookup. + ai := arenaIndex(h.addr) + ha := mheap_.arenas[ai.l1()][ai.l2()] + idx := h.addr / (ptrBits * goarch.PtrSize) % heapArenaBitmapWords + m := uintptr(1)< ptrBits { + h = h.write(0, ptrBits) + words -= ptrBits + } + return h.write(0, words) +} + +// Flush the bits that have been written, and add zeros as needed +// to cover the full object [addr, addr+size). +func (h writeHeapBits) flush(addr, size uintptr) { + // zeros counts the number of bits needed to represent the object minus the + // number of bits we've already written. This is the number of 0 bits + // that need to be added. + zeros := (addr+size-h.addr)/goarch.PtrSize - h.valid + + // Add zero bits up to the bitmap word boundary + if zeros > 0 { + z := ptrBits - h.valid + if z > zeros { + z = zeros + } + h.valid += z + zeros -= z + } + + // Find word in bitmap that we're going to write. + ai := arenaIndex(h.addr) + ha := mheap_.arenas[ai.l1()][ai.l2()] + idx := h.addr / (ptrBits * goarch.PtrSize) % heapArenaBitmapWords + + // Write remaining bits. + if h.valid != h.low { + m := uintptr(1)< 8 { + h = h.write(uintptr(*p), 8) + p = add1(p) + j -= 8 } - } - if typ.size == sys.PtrSize { - // The type contains a pointer otherwise heapBitsSetType wouldn't have been called. - // Since the type is only 1 pointer wide and contains a pointer, its gcdata must be exactly 1. - if doubleCheck && *typ.gcdata != 1 { - print("runtime: heapBitsSetType size=", size, " typ.size=", typ.size, "but *typ.gcdata", *typ.gcdata, "\n") - throw("heapBitsSetType: unexpected gcdata for 1 pointer wide type size in 3 pointer wide size class") + h = h.write(uintptr(*p), j) + + if i+typ.size == dataSize { + break // no padding after last element } - // 3 element array of pointers. Unrolling ptrmask 3 times into p yields 00000111. - b = 7 - } - hb := b & 7 - // Set bitScan bits for all pointers. - hb |= hb << wordsPerBitmapByte - // First bitScan bit is always set since the type contains pointers. - hb |= bitScan - // Second bitScan bit needs to also be set if the third bitScan bit is set. - hb |= hb & (bitScan << (2 * heapBitsShift)) >> 1 - - // For h.shift > 1 heap bits cross a byte boundary and need to be written part - // to h.bitp and part to the next h.bitp. - switch h.shift { - case 0: - *h.bitp &^= mask3 << 0 - *h.bitp |= hb << 0 - case 1: - *h.bitp &^= mask3 << 1 - *h.bitp |= hb << 1 - case 2: - *h.bitp &^= mask2 << 2 - *h.bitp |= (hb & mask2) << 2 - // Two words written to the first byte. - // Advance two words to get to the next byte. - h = h.next().next() - *h.bitp &^= mask1 - *h.bitp |= (hb >> 2) & mask1 - case 3: - *h.bitp &^= mask1 << 3 - *h.bitp |= (hb & mask1) << 3 - // One word written to the first byte. - // Advance one word to get to the next byte. - h = h.next() - *h.bitp &^= mask2 - *h.bitp |= (hb >> 1) & mask2 + // Pad with zeros to the start of the next element. + h = h.pad(typ.size - n*goarch.PtrSize) } - return - } - // Copy from 1-bit ptrmask into 2-bit bitmap. - // The basic approach is to use a single uintptr as a bit buffer, - // alternating between reloading the buffer and writing bitmap bytes. - // In general, one load can supply two bitmap byte writes. - // This is a lot of lines of code, but it compiles into relatively few - // machine instructions. - - outOfPlace := false - if arenaIndex(x+size-1) != arenaIdx(h.arena) || (doubleCheck && fastrand()%2 == 0) { - // This object spans heap arenas, so the bitmap may be - // discontiguous. Unroll it into the object instead - // and then copy it out. - // - // In doubleCheck mode, we randomly do this anyway to - // stress test the bitmap copying path. - outOfPlace = true - h.bitp = (*uint8)(unsafe.Pointer(x)) - h.last = nil - } + h.flush(x, size) - var ( - // Ptrmask input. - p *byte // last ptrmask byte read - b uintptr // ptrmask bits already loaded - nb uintptr // number of bits in b at next read - endp *byte // final ptrmask byte to read (then repeat) - endnb uintptr // number of valid bits in *endp - pbits uintptr // alternate source of bits - - // Heap bitmap output. - w uintptr // words processed - nw uintptr // number of words to process - hbitp *byte // next heap bitmap byte to write - hb uintptr // bits being prepared for *hbitp - ) - - hbitp = h.bitp - - // Handle GC program. Delayed until this part of the code - // so that we can use the same double-checking mechanism - // as the 1-bit case. Nothing above could have encountered - // GC programs: the cases were all too small. - if typ.kind&kindGCProg != 0 { - heapBitsSetTypeGCProg(h, typ.ptrdata, typ.size, dataSize, size, addb(typ.gcdata, 4)) - if doubleCheck { - // Double-check the heap bits written by GC program - // by running the GC program to create a 1-bit pointer mask - // and then jumping to the double-check code below. - // This doesn't catch bugs shared between the 1-bit and 4-bit - // GC program execution, but it does catch mistakes specific - // to just one of those and bugs in heapBitsSetTypeGCProg's - // implementation of arrays. - lock(&debugPtrmask.lock) - if debugPtrmask.data == nil { - debugPtrmask.data = (*byte)(persistentalloc(1<<20, 1, &memstats.other_sys)) - } - ptrmask = debugPtrmask.data - runGCProg(addb(typ.gcdata, 4), nil, ptrmask, 1) - } - goto Phase4 + // Erase the expanded GC program. + memclrNoHeapPointers(unsafe.Pointer(obj), (n+7)/8) + return } // Note about sizes: @@ -1039,424 +1011,98 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // to scan the buffer's heap bitmap at all. // The 1-bit ptrmasks are sized to contain only bits for // the typ.ptrdata prefix, zero padded out to a full byte - // of bitmap. This code sets nw (below) so that heap bitmap - // bits are only written for the typ.ptrdata prefix; if there is - // more room in the allocated object, the next heap bitmap - // entry is a 00, indicating that there are no more pointers - // to scan. So only the ptrmask for the ptrdata bytes is needed. + // of bitmap. If there is more room in the allocated object, + // that space is pointerless. The noMorePtrs bitmap will prevent + // scanning large pointerless tails of an object. // // Replicated copies are not as nice: if there is an array of // objects with scalar tails, all but the last tail does have to // be initialized, because there is no way to say "skip forward". - // However, because of the possibility of a repeated type with - // size not a multiple of 4 pointers (one heap bitmap byte), - // the code already must handle the last ptrmask byte specially - // by treating it as containing only the bits for endnb pointers, - // where endnb <= 4. We represent large scalar tails that must - // be expanded in the replication by setting endnb larger than 4. - // This will have the effect of reading many bits out of b, - // but once the real bits are shifted out, b will supply as many - // zero bits as we try to read, which is exactly what we need. - - p = ptrmask - if typ.size < dataSize { - // Filling in bits for an array of typ. - // Set up for repetition of ptrmask during main loop. - // Note that ptrmask describes only a prefix of - const maxBits = sys.PtrSize*8 - 7 - if typ.ptrdata/sys.PtrSize <= maxBits { - // Entire ptrmask fits in uintptr with room for a byte fragment. - // Load into pbits and never read from ptrmask again. - // This is especially important when the ptrmask has - // fewer than 8 bits in it; otherwise the reload in the middle - // of the Phase 2 loop would itself need to loop to gather - // at least 8 bits. - - // Accumulate ptrmask into b. - // ptrmask is sized to describe only typ.ptrdata, but we record - // it as describing typ.size bytes, since all the high bits are zero. - nb = typ.ptrdata / sys.PtrSize - for i := uintptr(0); i < nb; i += 8 { - b |= uintptr(*p) << i - p = add1(p) - } - nb = typ.size / sys.PtrSize - - // Replicate ptrmask to fill entire pbits uintptr. - // Doubling and truncating is fewer steps than - // iterating by nb each time. (nb could be 1.) - // Since we loaded typ.ptrdata/sys.PtrSize bits - // but are pretending to have typ.size/sys.PtrSize, - // there might be no replication necessary/possible. - pbits = b - endnb = nb - if nb+nb <= maxBits { - for endnb <= sys.PtrSize*8 { - pbits |= pbits << endnb - endnb += endnb - } - // Truncate to a multiple of original ptrmask. - // Because nb+nb <= maxBits, nb fits in a byte. - // Byte division is cheaper than uintptr division. - endnb = uintptr(maxBits/byte(nb)) * nb - pbits &= 1<= nw { - goto Phase3 - } - *hbitp = uint8(hb) - hbitp = add1(hbitp) - b >>= 4 - nb -= 4 - - case h.shift == 2: - // Ptrmask and heap bitmap are misaligned. - // - // On 32 bit architectures only the 6-word object that corresponds - // to a 24 bytes size class can start with h.shift of 2 here since - // all other non 16 byte aligned size classes have been handled by - // special code paths at the beginning of heapBitsSetType on 32 bit. - // - // Many size classes are only 16 byte aligned. On 64 bit architectures - // this results in a heap bitmap position starting with a h.shift of 2. - // - // The bits for the first two words are in a byte shared - // with another object, so we must be careful with the bits - // already there. - // - // We took care of 1-word, 2-word, and 3-word objects above, - // so this is at least a 6-word object. - hb = (b & (bitPointer | bitPointer< 1 { - hb |= bitScan << (3 * heapBitsShift) - } - b >>= 2 - nb -= 2 - *hbitp &^= uint8((bitPointer | bitScan | ((bitPointer | bitScan) << heapBitsShift)) << (2 * heapBitsShift)) - *hbitp |= uint8(hb) - hbitp = add1(hbitp) - if w += 2; w >= nw { - // We know that there is more data, because we handled 2-word and 3-word objects above. - // This must be at least a 6-word object. If we're out of pointer words, - // mark no scan in next bitmap byte and finish. - hb = 0 - w += 4 - goto Phase3 - } - } - // Phase 2: Full bytes in bitmap, up to but not including write to last byte (full or partial) in bitmap. - // The loop computes the bits for that last write but does not execute the write; - // it leaves the bits in hb for processing by phase 3. - // To avoid repeated adjustment of nb, we subtract out the 4 bits we're going to - // use in the first half of the loop right now, and then we only adjust nb explicitly - // if the 8 bits used by each iteration isn't balanced by 8 bits loaded mid-loop. - nb -= 4 - for { - // Emit bitmap byte. - // b has at least nb+4 bits, with one exception: - // if w+4 >= nw, then b has only nw-w bits, - // but we'll stop at the break and then truncate - // appropriately in Phase 3. - hb = b & bitPointerAll - hb |= bitScanAll - if w += 4; w >= nw { - break - } - *hbitp = uint8(hb) - hbitp = add1(hbitp) - b >>= 4 - - // Load more bits. b has nb right now. - if p != endp { - // Fast path: keep reading from ptrmask. - // nb unmodified: we just loaded 8 bits, - // and the next iteration will consume 8 bits, - // leaving us with the same nb the next time we're here. - if nb < 8 { - b |= uintptr(*p) << nb - p = add1(p) - } else { - // Reduce the number of bits in b. - // This is important if we skipped - // over a scalar tail, since nb could - // be larger than the bit width of b. - nb -= 8 - } - } else if p == nil { - // Almost as fast path: track bit count and refill from pbits. - // For short repetitions. - if nb < 8 { - b |= pbits << nb - nb += endnb - } - nb -= 8 // for next iteration - } else { - // Slow path: reached end of ptrmask. - // Process final partial byte and rewind to start. - b |= uintptr(*p) << nb - nb += endnb - if nb < 8 { - b |= uintptr(*ptrmask) << nb - p = add1(ptrmask) - } else { - nb -= 8 - p = ptrmask + ptrs := typ.ptrdata / goarch.PtrSize + if typ.size == dataSize { // Single element + if ptrs <= ptrBits { // Single small element + m := readUintptr(typ.gcdata) + h = h.write(m, ptrs) + } else { // Single large element + p := typ.gcdata + for { + h = h.write(readUintptr(p), ptrBits) + p = addb(p, ptrBits/8) + ptrs -= ptrBits + if ptrs <= ptrBits { + break + } } + m := readUintptr(p) + h = h.write(m, ptrs) } - - // Emit bitmap byte. - hb = b & bitPointerAll - hb |= bitScanAll - if w += 4; w >= nw { - break - } - *hbitp = uint8(hb) - hbitp = add1(hbitp) - b >>= 4 - } - -Phase3: - // Phase 3: Write last byte or partial byte and zero the rest of the bitmap entries. - if w > nw { - // Counting the 4 entries in hb not yet written to memory, - // there are more entries than possible pointer slots. - // Discard the excess entries (can't be more than 3). - mask := uintptr(1)<<(4-(w-nw)) - 1 - hb &= mask | mask<<4 // apply mask to both pointer bits and scan bits - } - - // Change nw from counting possibly-pointer words to total words in allocation. - nw = size / sys.PtrSize - - // Write whole bitmap bytes. - // The first is hb, the rest are zero. - if w <= nw { - *hbitp = uint8(hb) - hbitp = add1(hbitp) - hb = 0 // for possible final half-byte below - for w += 4; w <= nw; w += 4 { - *hbitp = 0 - hbitp = add1(hbitp) - } - } - - // Write final partial bitmap byte if any. - // We know w > nw, or else we'd still be in the loop above. - // It can be bigger only due to the 4 entries in hb that it counts. - // If w == nw+4 then there's nothing left to do: we wrote all nw entries - // and can discard the 4 sitting in hb. - // But if w == nw+2, we need to write first two in hb. - // The byte is shared with the next object, so be careful with - // existing bits. - if w == nw+2 { - *hbitp = *hbitp&^(bitPointer|bitScan|(bitPointer|bitScan)<= 4 { - // This loop processes four words at a time, - // so round cnw down accordingly. - hNext, words := h.forwardOrBoundary(cnw / 4 * 4) - - // n is the number of bitmap bytes to copy. - n := words / 4 - memmove(unsafe.Pointer(h.bitp), unsafe.Pointer(src), n) - cnw -= words - h = hNext - src = addb(src, n) - } - if doubleCheck && h.shift != 0 { - print("cnw=", cnw, " h.shift=", h.shift, "\n") - throw("bad shift after block copy") - } - // Handle the last byte if it's shared. - if cnw == 2 { - *h.bitp = *h.bitp&^(bitPointer|bitScan|(bitPointer|bitScan)< x+size { - throw("copy exceeded object size") + for n > 1 { + h = h.write(m, words) + n-- } - if !(cnw == 0 || cnw == 2) { - print("x=", x, " size=", size, " cnw=", cnw, "\n") - throw("bad number of remaining words") + h = h.write(m, ptrs) + } else { // Repeated large element + for i := uintptr(0); true; i += typ.size { + p := typ.gcdata + j := ptrs + for j > ptrBits { + h = h.write(readUintptr(p), ptrBits) + p = addb(p, ptrBits/8) + j -= ptrBits + } + m := readUintptr(p) + h = h.write(m, j) + if i+typ.size == dataSize { + break // don't need the trailing nonptr bits on the last element. + } + // Pad with zeros to the start of the next element. + h = h.pad(typ.size - typ.ptrdata) } - // Set up hbitp so doubleCheck code below can check it. - hbitp = h.bitp } - // Zero the object where we wrote the bitmap. - memclrNoHeapPointers(unsafe.Pointer(x), uintptr(unsafe.Pointer(src))-x) } + h.flush(x, size) - // Double check the whole bitmap. if doubleCheck { - // x+size may not point to the heap, so back up one - // word and then advance it the way we do above. - end := heapBitsForAddr(x + size - sys.PtrSize) - if outOfPlace { - // In out-of-place copying, we just advance - // using next. - end = end.next() - } else { - // Don't use next because that may advance to - // the next arena and the in-place logic - // doesn't do that. - end.shift += heapBitsShift - if end.shift == 4*heapBitsShift { - end.bitp, end.shift = add1(end.bitp), 0 - } - } - if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) { - println("ended at wrong bitmap byte for", typ.string(), "x", dataSize/typ.size) - print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") - print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") - h0 := heapBitsForAddr(x) - print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n") - print("ended at hbitp=", hbitp, " but next starts at bitp=", end.bitp, " shift=", end.shift, "\n") - throw("bad heapBitsSetType") - } - - // Double-check that bits to be written were written correctly. - // Does not check that other bits were not written, unfortunately. - h := heapBitsForAddr(x) - nptr := typ.ptrdata / sys.PtrSize - ndata := typ.size / sys.PtrSize - count := dataSize / typ.size - totalptr := ((count-1)*typ.size + typ.ptrdata) / sys.PtrSize - for i := uintptr(0); i < size/sys.PtrSize; i++ { - j := i % ndata - var have, want uint8 - have = (*h.bitp >> h.shift) & (bitPointer | bitScan) - if i >= totalptr { - if typ.kind&kindGCProg != 0 && i < (totalptr+3)/4*4 { - // heapBitsSetTypeGCProg always fills - // in full nibbles of bitScan. - want = bitScan - } - } else { - if j < nptr && (*addb(ptrmask, j/8)>>(j%8))&1 != 0 { - want |= bitPointer + h := heapBitsForAddr(x, size) + for i := uintptr(0); i < size; i += goarch.PtrSize { + // Compute the pointer bit we want at offset i. + want := false + if i < dataSize { + off := i % typ.size + if off < typ.ptrdata { + j := off / goarch.PtrSize + want = *addb(typ.gcdata, j/8)>>(j%8)&1 != 0 } - want |= bitScan } - if have != want { - println("mismatch writing bits for", typ.string(), "x", dataSize/typ.size) - print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") - print("kindGCProg=", typ.kind&kindGCProg != 0, " outOfPlace=", outOfPlace, "\n") - print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") - h0 := heapBitsForAddr(x) - print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n") - print("current bits h.bitp=", h.bitp, " h.shift=", h.shift, " *h.bitp=", hex(*h.bitp), "\n") - print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") - println("at word", i, "offset", i*sys.PtrSize, "have", hex(have), "want", hex(want)) - if typ.kind&kindGCProg != 0 { - println("GC program:") - dumpGCProg(addb(typ.gcdata, 4)) + if want { + var addr uintptr + h, addr = h.next() + if addr != x+i { + throw("heapBitsSetType: pointer entry not correct") } - throw("bad heapBitsSetType") } - h = h.next() } - if ptrmask == debugPtrmask.data { - unlock(&debugPtrmask.lock) + if _, addr := h.next(); addr != 0 { + throw("heapBitsSetType: extra pointer") } } } @@ -1466,100 +1112,14 @@ var debugPtrmask struct { data *byte } -// heapBitsSetTypeGCProg implements heapBitsSetType using a GC program. -// progSize is the size of the memory described by the program. -// elemSize is the size of the element that the GC program describes (a prefix of). -// dataSize is the total size of the intended data, a multiple of elemSize. -// allocSize is the total size of the allocated memory. -// -// GC programs are only used for large allocations. -// heapBitsSetType requires that allocSize is a multiple of 4 words, -// so that the relevant bitmap bytes are not shared with surrounding -// objects. -func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize uintptr, prog *byte) { - if sys.PtrSize == 8 && allocSize%(4*sys.PtrSize) != 0 { - // Alignment will be wrong. - throw("heapBitsSetTypeGCProg: small allocation") - } - var totalBits uintptr - if elemSize == dataSize { - totalBits = runGCProg(prog, nil, h.bitp, 2) - if totalBits*sys.PtrSize != progSize { - println("runtime: heapBitsSetTypeGCProg: total bits", totalBits, "but progSize", progSize) - throw("heapBitsSetTypeGCProg: unexpected bit count") - } - } else { - count := dataSize / elemSize - - // Piece together program trailer to run after prog that does: - // literal(0) - // repeat(1, elemSize-progSize-1) // zeros to fill element size - // repeat(elemSize, count-1) // repeat that element for count - // This zero-pads the data remaining in the first element and then - // repeats that first element to fill the array. - var trailer [40]byte // 3 varints (max 10 each) + some bytes - i := 0 - if n := elemSize/sys.PtrSize - progSize/sys.PtrSize; n > 0 { - // literal(0) - trailer[i] = 0x01 - i++ - trailer[i] = 0 - i++ - if n > 1 { - // repeat(1, n-1) - trailer[i] = 0x81 - i++ - n-- - for ; n >= 0x80; n >>= 7 { - trailer[i] = byte(n | 0x80) - i++ - } - trailer[i] = byte(n) - i++ - } - } - // repeat(elemSize/ptrSize, count-1) - trailer[i] = 0x80 - i++ - n := elemSize / sys.PtrSize - for ; n >= 0x80; n >>= 7 { - trailer[i] = byte(n | 0x80) - i++ - } - trailer[i] = byte(n) - i++ - n = count - 1 - for ; n >= 0x80; n >>= 7 { - trailer[i] = byte(n | 0x80) - i++ - } - trailer[i] = byte(n) - i++ - trailer[i] = 0 - i++ - - runGCProg(prog, &trailer[0], h.bitp, 2) - - // Even though we filled in the full array just now, - // record that we only filled in up to the ptrdata of the - // last element. This will cause the code below to - // memclr the dead section of the final array element, - // so that scanobject can stop early in the final element. - totalBits = (elemSize*(count-1) + progSize) / sys.PtrSize - } - endProg := unsafe.Pointer(addb(h.bitp, (totalBits+3)/4)) - endAlloc := unsafe.Pointer(addb(h.bitp, allocSize/sys.PtrSize/wordsPerBitmapByte)) - memclrNoHeapPointers(endProg, uintptr(endAlloc)-uintptr(endProg)) -} - // progToPointerMask returns the 1-bit pointer mask output by the GC program prog. // size the size of the region described by prog, in bytes. -// The resulting bitvector will have no more than size/sys.PtrSize bits. +// The resulting bitvector will have no more than size/goarch.PtrSize bits. func progToPointerMask(prog *byte, size uintptr) bitvector { - n := (size/sys.PtrSize + 7) / 8 + n := (size/goarch.PtrSize + 7) / 8 x := (*[1 << 30]byte)(persistentalloc(n+1, 1, &memstats.buckhash_sys))[:n+1] x[len(x)-1] = 0xa1 // overflow check sentinel - n = runGCProg(prog, nil, &x[0], 1) + n = runGCProg(prog, &x[0]) if x[len(x)-1] != 0xa1 { throw("progToPointerMask: overflow") } @@ -1580,15 +1140,8 @@ func progToPointerMask(prog *byte, size uintptr) bitvector { // 10000000 n c: repeat the previous n bits c times; n, c are varints // 1nnnnnnn c: repeat the previous n bits c times; c is a varint -// runGCProg executes the GC program prog, and then trailer if non-nil, -// writing to dst with entries of the given size. -// If size == 1, dst is a 1-bit pointer mask laid out moving forward from dst. -// If size == 2, dst is the 2-bit heap bitmap, and writes move backward -// starting at dst (because the heap bitmap does). In this case, the caller guarantees -// that only whole bytes in dst need to be written. -// -// runGCProg returns the number of 1- or 2-bit entries written to memory. -func runGCProg(prog, trailer, dst *byte, size int) uintptr { +// runGCProg returns the number of 1-bit entries written to memory. +func runGCProg(prog, dst *byte) uintptr { dstStart := dst // Bits waiting to be written to memory. @@ -1601,20 +1154,9 @@ Run: // Flush accumulated full bytes. // The rest of the loop assumes that nbits <= 7. for ; nbits >= 8; nbits -= 8 { - if size == 1 { - *dst = uint8(bits) - dst = add1(dst) - bits >>= 8 - } else { - v := bits&bitPointerAll | bitScanAll - *dst = uint8(v) - dst = add1(dst) - bits >>= 4 - v = bits&bitPointerAll | bitScanAll - *dst = uint8(v) - dst = add1(dst) - bits >>= 4 - } + *dst = uint8(bits) + dst = add1(dst) + bits >>= 8 } // Process one instruction. @@ -1624,32 +1166,16 @@ Run: if inst&0x80 == 0 { // Literal bits; n == 0 means end of program. if n == 0 { - // Program is over; continue in trailer if present. - if trailer != nil { - p = trailer - trailer = nil - continue - } + // Program is over. break Run } nbyte := n / 8 for i := uintptr(0); i < nbyte; i++ { bits |= uintptr(*p) << nbits p = add1(p) - if size == 1 { - *dst = uint8(bits) - dst = add1(dst) - bits >>= 8 - } else { - v := bits&0xf | bitScanAll - *dst = uint8(v) - dst = add1(dst) - bits >>= 4 - v = bits&0xf | bitScanAll - *dst = uint8(v) - dst = add1(dst) - bits >>= 4 - } + *dst = uint8(bits) + dst = add1(dst) + bits >>= 8 } if n %= 8; n > 0 { bits |= uintptr(*p) << nbits @@ -1687,33 +1213,23 @@ Run: // into a register and use that register for the entire loop // instead of repeatedly reading from memory. // Handling fewer than 8 bits here makes the general loop simpler. - // The cutoff is sys.PtrSize*8 - 7 to guarantee that when we add + // The cutoff is goarch.PtrSize*8 - 7 to guarantee that when we add // the pattern to a bit buffer holding at most 7 bits (a partial byte) // it will not overflow. src := dst - const maxBits = sys.PtrSize*8 - 7 + const maxBits = goarch.PtrSize*8 - 7 if n <= maxBits { // Start with bits in output buffer. pattern := bits npattern := nbits // If we need more bits, fetch them from memory. - if size == 1 { - src = subtract1(src) - for npattern < n { - pattern <<= 8 - pattern |= uintptr(*src) - src = subtract1(src) - npattern += 8 - } - } else { + src = subtract1(src) + for npattern < n { + pattern <<= 8 + pattern |= uintptr(*src) src = subtract1(src) - for npattern < n { - pattern <<= 4 - pattern |= uintptr(*src) & 0xf - src = subtract1(src) - npattern += 4 - } + npattern += 8 } // We started with the whole bit output buffer, @@ -1744,7 +1260,7 @@ Run: nb := npattern if nb+nb <= maxBits { // Double pattern until the whole uintptr is filled. - for nb <= sys.PtrSize*8 { + for nb <= goarch.PtrSize*8 { b |= b << nb nb += nb } @@ -1763,20 +1279,11 @@ Run: for ; c >= npattern; c -= npattern { bits |= pattern << nbits nbits += npattern - if size == 1 { - for nbits >= 8 { - *dst = uint8(bits) - dst = add1(dst) - bits >>= 8 - nbits -= 8 - } - } else { - for nbits >= 4 { - *dst = uint8(bits&0xf | bitScanAll) - dst = add1(dst) - bits >>= 4 - nbits -= 4 - } + for nbits >= 8 { + *dst = uint8(bits) + dst = add1(dst) + bits >>= 8 + nbits -= 8 } } @@ -1793,75 +1300,38 @@ Run: // Since nbits <= 7, we know the first few bytes of repeated data // are already written to memory. off := n - nbits // n > nbits because n > maxBits and nbits <= 7 - if size == 1 { - // Leading src fragment. - src = subtractb(src, (off+7)/8) - if frag := off & 7; frag != 0 { - bits |= uintptr(*src) >> (8 - frag) << nbits - src = add1(src) - nbits += frag - c -= frag - } - // Main loop: load one byte, write another. - // The bits are rotating through the bit buffer. - for i := c / 8; i > 0; i-- { - bits |= uintptr(*src) << nbits - src = add1(src) - *dst = uint8(bits) - dst = add1(dst) - bits >>= 8 - } - // Final src fragment. - if c %= 8; c > 0 { - bits |= (uintptr(*src) & (1<> (4 - frag) << nbits - src = add1(src) - nbits += frag - c -= frag - } - // Main loop: load one byte, write another. - // The bits are rotating through the bit buffer. - for i := c / 4; i > 0; i-- { - bits |= (uintptr(*src) & 0xf) << nbits - src = add1(src) - *dst = uint8(bits&0xf | bitScanAll) - dst = add1(dst) - bits >>= 4 - } - // Final src fragment. - if c %= 4; c > 0 { - bits |= (uintptr(*src) & (1<> (8 - frag) << nbits + src = add1(src) + nbits += frag + c -= frag } - } - - // Write any final bits out, using full-byte writes, even for the final byte. - var totalBits uintptr - if size == 1 { - totalBits = (uintptr(unsafe.Pointer(dst))-uintptr(unsafe.Pointer(dstStart)))*8 + nbits - nbits += -nbits & 7 - for ; nbits > 0; nbits -= 8 { + // Main loop: load one byte, write another. + // The bits are rotating through the bit buffer. + for i := c / 8; i > 0; i-- { + bits |= uintptr(*src) << nbits + src = add1(src) *dst = uint8(bits) dst = add1(dst) bits >>= 8 } - } else { - totalBits = (uintptr(unsafe.Pointer(dst))-uintptr(unsafe.Pointer(dstStart)))*4 + nbits - nbits += -nbits & 3 - for ; nbits > 0; nbits -= 4 { - v := bits&0xf | bitScanAll - *dst = uint8(v) - dst = add1(dst) - bits >>= 4 + // Final src fragment. + if c %= 8; c > 0 { + bits |= (uintptr(*src) & (1< 0; nbits -= 8 { + *dst = uint8(bits) + dst = add1(dst) + bits >>= 8 + } return totalBits } @@ -1872,11 +1342,11 @@ Run: // The result must be deallocated with dematerializeGCProg. func materializeGCProg(ptrdata uintptr, prog *byte) *mspan { // Each word of ptrdata needs one bit in the bitmap. - bitmapBytes := divRoundUp(ptrdata, 8*sys.PtrSize) + bitmapBytes := divRoundUp(ptrdata, 8*goarch.PtrSize) // Compute the number of pages needed for bitmapBytes. pages := divRoundUp(bitmapBytes, pageSize) s := mheap_.allocManual(pages, spanAllocPtrScalarBits) - runGCProg(addb(prog, 4), nil, (*byte)(unsafe.Pointer(s.startAddr)), 1) + runGCProg(addb(prog, 4), (*byte)(unsafe.Pointer(s.startAddr))) return s } func dematerializeGCProg(s *mspan) { @@ -1941,21 +1411,16 @@ func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool { // gcbits returns the GC type info for x, for testing. // The result is the bitmap entries (0 or 1), one entry per byte. +// //go:linkname reflect_gcbits reflect.gcbits -func reflect_gcbits(x interface{}) []byte { - ret := getgcmask(x) - typ := (*ptrtype)(unsafe.Pointer(efaceOf(&x)._type)).elem - nptr := typ.ptrdata / sys.PtrSize - for uintptr(len(ret)) > nptr && ret[len(ret)-1] == 0 { - ret = ret[:len(ret)-1] - } - return ret +func reflect_gcbits(x any) []byte { + return getgcmask(x) } // Returns GC type info for the pointer stored in ep for testing. // If ep points to the stack, only static live information will be returned // (i.e. not for objects which are only dynamically live stack objects). -func getgcmask(ep interface{}) (mask []byte) { +func getgcmask(ep any) (mask []byte) { e := *efaceOf(&ep) p := e.data t := e._type @@ -1965,10 +1430,10 @@ func getgcmask(ep interface{}) (mask []byte) { if datap.data <= uintptr(p) && uintptr(p) < datap.edata { bitmap := datap.gcdatamask.bytedata n := (*ptrtype)(unsafe.Pointer(t)).elem.size - mask = make([]byte, n/sys.PtrSize) - for i := uintptr(0); i < n; i += sys.PtrSize { - off := (uintptr(p) + i - datap.data) / sys.PtrSize - mask[i/sys.PtrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 + mask = make([]byte, n/goarch.PtrSize) + for i := uintptr(0); i < n; i += goarch.PtrSize { + off := (uintptr(p) + i - datap.data) / goarch.PtrSize + mask[i/goarch.PtrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 } return } @@ -1977,10 +1442,10 @@ func getgcmask(ep interface{}) (mask []byte) { if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss { bitmap := datap.gcbssmask.bytedata n := (*ptrtype)(unsafe.Pointer(t)).elem.size - mask = make([]byte, n/sys.PtrSize) - for i := uintptr(0); i < n; i += sys.PtrSize { - off := (uintptr(p) + i - datap.bss) / sys.PtrSize - mask[i/sys.PtrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 + mask = make([]byte, n/goarch.PtrSize) + for i := uintptr(0); i < n; i += goarch.PtrSize { + off := (uintptr(p) + i - datap.bss) / goarch.PtrSize + mask[i/goarch.PtrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 } return } @@ -1988,39 +1453,42 @@ func getgcmask(ep interface{}) (mask []byte) { // heap if base, s, _ := findObject(uintptr(p), 0, 0); base != 0 { - hbits := heapBitsForAddr(base) + if s.spanclass.noscan() { + return nil + } n := s.elemsize - mask = make([]byte, n/sys.PtrSize) - for i := uintptr(0); i < n; i += sys.PtrSize { - if hbits.isPointer() { - mask[i/sys.PtrSize] = 1 - } - if !hbits.morePointers() { - mask = mask[:i/sys.PtrSize] + hbits := heapBitsForAddr(base, n) + mask = make([]byte, n/goarch.PtrSize) + for { + var addr uintptr + if hbits, addr = hbits.next(); addr == 0 { break } - hbits = hbits.next() + mask[(addr-base)/goarch.PtrSize] = 1 + } + // Callers expect this mask to end at the last pointer. + for len(mask) > 0 && mask[len(mask)-1] == 0 { + mask = mask[:len(mask)-1] } return } // stack - if _g_ := getg(); _g_.m.curg.stack.lo <= uintptr(p) && uintptr(p) < _g_.m.curg.stack.hi { + if gp := getg(); gp.m.curg.stack.lo <= uintptr(p) && uintptr(p) < gp.m.curg.stack.hi { var frame stkframe frame.sp = uintptr(p) - _g_ := getg() - gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0) + gentraceback(gp.m.curg.sched.pc, gp.m.curg.sched.sp, 0, gp.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0) if frame.fn.valid() { - locals, _, _ := getStackMap(&frame, nil, false) + locals, _, _ := frame.getStackMap(nil, false) if locals.n == 0 { return } - size := uintptr(locals.n) * sys.PtrSize + size := uintptr(locals.n) * goarch.PtrSize n := (*ptrtype)(unsafe.Pointer(t)).elem.size - mask = make([]byte, n/sys.PtrSize) - for i := uintptr(0); i < n; i += sys.PtrSize { - off := (uintptr(p) + i - frame.varp + size) / sys.PtrSize - mask[i/sys.PtrSize] = locals.ptrbit(off) + mask = make([]byte, n/goarch.PtrSize) + for i := uintptr(0); i < n; i += goarch.PtrSize { + off := (uintptr(p) + i - frame.varp + size) / goarch.PtrSize + mask[i/goarch.PtrSize] = locals.ptrbit(off) } } return diff --git a/src/runtime/mcache.go b/src/runtime/mcache.go index a9e959109abaab..ba45034943fae9 100644 --- a/src/runtime/mcache.go +++ b/src/runtime/mcache.go @@ -6,6 +6,7 @@ package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -15,9 +16,9 @@ import ( // // mcaches are allocated from non-GC'd memory, so any heap pointers // must be specially handled. -// -//go:notinheap type mcache struct { + _ sys.NotInHeap + // The following members are accessed on every malloc, // so they are grouped here for better caching. nextSample uintptr // trigger heap sample after allocating this many bytes @@ -49,7 +50,7 @@ type mcache struct { // was last flushed. If flushGen != mheap_.sweepgen, the spans // in this mcache are stale and need to the flushed so they // can be swept. This is done in acquirep. - flushGen uint32 + flushGen atomic.Uint32 } // A gclink is a node in a linked list of blocks, like mlink, @@ -86,7 +87,7 @@ func allocmcache() *mcache { systemstack(func() { lock(&mheap_.lock) c = (*mcache)(mheap_.cachealloc.alloc()) - c.flushGen = mheap_.sweepgen + c.flushGen.Store(mheap_.sweepgen) unlock(&mheap_.lock) }) for i := range c.alloc { @@ -122,9 +123,9 @@ func freemcache(c *mcache) { // // Returns nil if we're not bootstrapping or we don't have a P. The caller's // P must not change, so we must be in a non-preemptible state. -func getMCache() *mcache { +func getMCache(mp *m) *mcache { // Grab the mcache, since that's where stats live. - pp := getg().m.p.ptr() + pp := mp.p.ptr() var c *mcache if pp == nil { // We will be called without a P while bootstrapping, @@ -156,6 +157,25 @@ func (c *mcache) refill(spc spanClass) { throw("bad sweepgen in refill") } mheap_.central[spc].mcentral.uncacheSpan(s) + + // Count up how many slots were used and record it. + stats := memstats.heapStats.acquire() + slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache) + atomic.Xadd64(&stats.smallAllocCount[spc.sizeclass()], slotsUsed) + + // Flush tinyAllocs. + if spc == tinySpanClass { + atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs)) + c.tinyAllocs = 0 + } + memstats.heapStats.release() + + // Count the allocs in inconsistent, internal stats. + bytesAllocated := slotsUsed * int64(s.elemsize) + gcController.totalAlloc.Add(bytesAllocated) + + // Clear the second allocCount just to be safe. + s.allocCountBeforeCache = 0 } // Get a new cached span from the central lists. @@ -172,44 +192,31 @@ func (c *mcache) refill(spc spanClass) { // sweeping in the next sweep phase. s.sweepgen = mheap_.sweepgen + 3 - // Assume all objects from this span will be allocated in the - // mcache. If it gets uncached, we'll adjust this. - stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.smallAllocCount[spc.sizeclass()], uintptr(s.nelems)-uintptr(s.allocCount)) + // Store the current alloc count for accounting later. + s.allocCountBeforeCache = s.allocCount - // Flush tinyAllocs. - if spc == tinySpanClass { - atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs) - c.tinyAllocs = 0 - } - memstats.heapStats.release() - - // Update gcController.heapLive with the same assumption. + // Update heapLive and flush scanAlloc. + // + // We have not yet allocated anything new into the span, but we + // assume that all of its slots will get used, so this makes + // heapLive an overestimate. + // + // When the span gets uncached, we'll fix up this overestimate + // if necessary (see releaseAll). + // + // We pick an overestimate here because an underestimate leads + // the pacer to believe that it's in better shape than it is, + // which appears to lead to more memory used. See #53738 for + // more details. usedBytes := uintptr(s.allocCount) * s.elemsize - atomic.Xadd64(&gcController.heapLive, int64(s.npages*pageSize)-int64(usedBytes)) - - // While we're here, flush scanAlloc, since we have to call - // revise anyway. - atomic.Xadd64(&gcController.heapScan, int64(c.scanAlloc)) + gcController.update(int64(s.npages*pageSize)-int64(usedBytes), int64(c.scanAlloc)) c.scanAlloc = 0 - if trace.enabled { - // gcController.heapLive changed. - traceHeapAlloc() - } - if gcBlackenEnabled != 0 { - // gcController.heapLive and heapScan changed. - gcController.revise() - } - c.alloc[spc] = s } // allocLarge allocates a span for a large object. -// The boolean result indicates whether the span is known-zeroed. -// If it did not need to be zeroed, it may not have been zeroed; -// but if it came directly from the OS, it is already zeroed. -func (c *mcache) allocLarge(size uintptr, needzero bool, noscan bool) (*mspan, bool) { +func (c *mcache) allocLarge(size uintptr, noscan bool) *mspan { if size+_PageSize < size { throw("out of memory") } @@ -224,57 +231,62 @@ func (c *mcache) allocLarge(size uintptr, needzero bool, noscan bool) (*mspan, b deductSweepCredit(npages*_PageSize, npages) spc := makeSpanClass(0, noscan) - s, isZeroed := mheap_.alloc(npages, spc, needzero) + s := mheap_.alloc(npages, spc) if s == nil { throw("out of memory") } + + // Count the alloc in consistent, external stats. stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.largeAlloc, npages*pageSize) - atomic.Xadduintptr(&stats.largeAllocCount, 1) + atomic.Xadd64(&stats.largeAlloc, int64(npages*pageSize)) + atomic.Xadd64(&stats.largeAllocCount, 1) memstats.heapStats.release() - // Update gcController.heapLive and revise pacing if needed. - atomic.Xadd64(&gcController.heapLive, int64(npages*pageSize)) - if trace.enabled { - // Trace that a heap alloc occurred because gcController.heapLive changed. - traceHeapAlloc() - } - if gcBlackenEnabled != 0 { - gcController.revise() - } + // Count the alloc in inconsistent, internal stats. + gcController.totalAlloc.Add(int64(npages * pageSize)) + + // Update heapLive. + gcController.update(int64(s.npages*pageSize), 0) // Put the large span in the mcentral swept list so that it's // visible to the background sweeper. mheap_.central[spc].mcentral.fullSwept(mheap_.sweepgen).push(s) s.limit = s.base() + size - heapBitsForAddr(s.base()).initSpan(s) - return s, isZeroed + s.initHeapBits() + return s } func (c *mcache) releaseAll() { // Take this opportunity to flush scanAlloc. - atomic.Xadd64(&gcController.heapScan, int64(c.scanAlloc)) + scanAlloc := int64(c.scanAlloc) c.scanAlloc = 0 sg := mheap_.sweepgen + dHeapLive := int64(0) for i := range c.alloc { s := c.alloc[i] if s != &emptymspan { - // Adjust nsmallalloc in case the span wasn't fully allocated. - n := uintptr(s.nelems) - uintptr(s.allocCount) + slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache) + s.allocCountBeforeCache = 0 + + // Adjust smallAllocCount for whatever was allocated. stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.smallAllocCount[spanClass(i).sizeclass()], -n) + atomic.Xadd64(&stats.smallAllocCount[spanClass(i).sizeclass()], slotsUsed) memstats.heapStats.release() + + // Adjust the actual allocs in inconsistent, internal stats. + // We assumed earlier that the full span gets allocated. + gcController.totalAlloc.Add(slotsUsed * int64(s.elemsize)) + if s.sweepgen != sg+1 { // refill conservatively counted unallocated slots in gcController.heapLive. // Undo this. // - // If this span was cached before sweep, then - // gcController.heapLive was totally recomputed since - // caching this span, so we don't do this for - // stale spans. - atomic.Xadd64(&gcController.heapLive, -int64(n)*int64(s.elemsize)) + // If this span was cached before sweep, then gcController.heapLive was totally + // recomputed since caching this span, so we don't do this for stale spans. + dHeapLive -= int64(uintptr(s.nelems)-uintptr(s.allocCount)) * int64(s.elemsize) } + // Release the span to the mcentral. mheap_.central[i].mcentral.uncacheSpan(s) c.alloc[i] = &emptymspan @@ -286,14 +298,12 @@ func (c *mcache) releaseAll() { // Flush tinyAllocs. stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.tinyAllocCount, c.tinyAllocs) + atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs)) c.tinyAllocs = 0 memstats.heapStats.release() - // Updated heapScan and possible gcController.heapLive. - if gcBlackenEnabled != 0 { - gcController.revise() - } + // Update heapLive and heapScan. + gcController.update(dHeapLive, scanAlloc) } // prepareForSweep flushes c if the system has entered a new sweep phase @@ -308,13 +318,14 @@ func (c *mcache) prepareForSweep() { // allocate-black. However, with this approach it's difficult // to avoid spilling mark bits into the *next* GC cycle. sg := mheap_.sweepgen - if c.flushGen == sg { + flushGen := c.flushGen.Load() + if flushGen == sg { return - } else if c.flushGen != sg-2 { - println("bad flushGen", c.flushGen, "in prepareForSweep; sweepgen", sg) + } else if flushGen != sg-2 { + println("bad flushGen", flushGen, "in prepareForSweep; sweepgen", sg) throw("bad flushGen") } c.releaseAll() stackcache_clear(c) - atomic.Store(&c.flushGen, mheap_.sweepgen) // Synchronizes with gcStart + c.flushGen.Store(mheap_.sweepgen) // Synchronizes with gcStart } diff --git a/src/runtime/mcentral.go b/src/runtime/mcentral.go index 6013c94c69808a..8e68955095f1a6 100644 --- a/src/runtime/mcentral.go +++ b/src/runtime/mcentral.go @@ -12,12 +12,14 @@ package runtime -import "runtime/internal/atomic" +import ( + "runtime/internal/atomic" + "runtime/internal/sys" +) // Central list of free objects of a given size. -// -//go:notinheap type mcentral struct { + _ sys.NotInHeap spanclass spanClass // partial and full contain two mspan sets: one of swept in-use @@ -102,56 +104,59 @@ func (c *mcentral) cacheSpan() *mspan { spanBudget := 100 var s *mspan - sl := newSweepLocker() - sg := sl.sweepGen + var sl sweepLocker // Try partial swept spans first. + sg := mheap_.sweepgen if s = c.partialSwept(sg).pop(); s != nil { goto havespan } - // Now try partial unswept spans. - for ; spanBudget >= 0; spanBudget-- { - s = c.partialUnswept(sg).pop() - if s == nil { - break - } - if s, ok := sl.tryAcquire(s); ok { - // We got ownership of the span, so let's sweep it and use it. - s.sweep(true) - sl.dispose() - goto havespan - } - // We failed to get ownership of the span, which means it's being or - // has been swept by an asynchronous sweeper that just couldn't remove it - // from the unswept list. That sweeper took ownership of the span and - // responsibility for either freeing it to the heap or putting it on the - // right swept list. Either way, we should just ignore it (and it's unsafe - // for us to do anything else). - } - // Now try full unswept spans, sweeping them and putting them into the - // right list if we fail to get a span. - for ; spanBudget >= 0; spanBudget-- { - s = c.fullUnswept(sg).pop() - if s == nil { - break - } - if s, ok := sl.tryAcquire(s); ok { - // We got ownership of the span, so let's sweep it. - s.sweep(true) - // Check if there's any free space. - freeIndex := s.nextFreeIndex() - if freeIndex != s.nelems { - s.freeindex = freeIndex - sl.dispose() + sl = sweep.active.begin() + if sl.valid { + // Now try partial unswept spans. + for ; spanBudget >= 0; spanBudget-- { + s = c.partialUnswept(sg).pop() + if s == nil { + break + } + if s, ok := sl.tryAcquire(s); ok { + // We got ownership of the span, so let's sweep it and use it. + s.sweep(true) + sweep.active.end(sl) goto havespan } - // Add it to the swept list, because sweeping didn't give us any free space. - c.fullSwept(sg).push(s.mspan) + // We failed to get ownership of the span, which means it's being or + // has been swept by an asynchronous sweeper that just couldn't remove it + // from the unswept list. That sweeper took ownership of the span and + // responsibility for either freeing it to the heap or putting it on the + // right swept list. Either way, we should just ignore it (and it's unsafe + // for us to do anything else). + } + // Now try full unswept spans, sweeping them and putting them into the + // right list if we fail to get a span. + for ; spanBudget >= 0; spanBudget-- { + s = c.fullUnswept(sg).pop() + if s == nil { + break + } + if s, ok := sl.tryAcquire(s); ok { + // We got ownership of the span, so let's sweep it. + s.sweep(true) + // Check if there's any free space. + freeIndex := s.nextFreeIndex() + if freeIndex != s.nelems { + s.freeindex = freeIndex + sweep.active.end(sl) + goto havespan + } + // Add it to the swept list, because sweeping didn't give us any free space. + c.fullSwept(sg).push(s.mspan) + } + // See comment for partial unswept spans. } - // See comment for partial unswept spans. + sweep.active.end(sl) } - sl.dispose() if trace.enabled { traceGCSweepDone() traceDone = true @@ -238,7 +243,7 @@ func (c *mcentral) grow() *mspan { npages := uintptr(class_to_allocnpages[c.spanclass.sizeclass()]) size := uintptr(class_to_size[c.spanclass.sizeclass()]) - s, _ := mheap_.alloc(npages, c.spanclass, true) + s := mheap_.alloc(npages, c.spanclass) if s == nil { return nil } @@ -247,6 +252,6 @@ func (c *mcentral) grow() *mspan { // n := (npages << _PageShift) / size n := s.divideByElemSize(npages << _PageShift) s.limit = s.base() + size*n - heapBitsForAddr(s.base()).initSpan(s) + s.initHeapBits() return s } diff --git a/src/runtime/mcheckmark.go b/src/runtime/mcheckmark.go index ba80ac1bdf4d60..73c1a10d23bc7d 100644 --- a/src/runtime/mcheckmark.go +++ b/src/runtime/mcheckmark.go @@ -13,6 +13,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -22,9 +23,10 @@ import ( // per-arena bitmap with a bit for every word in the arena. The mark // is stored on the bit corresponding to the first word of the marked // allocation. -// -//go:notinheap -type checkmarksMap [heapArenaBytes / sys.PtrSize / 8]uint8 +type checkmarksMap struct { + _ sys.NotInHeap + b [heapArenaBytes / goarch.PtrSize / 8]uint8 +} // If useCheckmark is true, marking of an object uses the checkmark // bits instead of the standard mark bits. @@ -50,8 +52,8 @@ func startCheckmarks() { arena.checkmarks = bitmap } else { // Otherwise clear the existing bitmap. - for i := range bitmap { - bitmap[i] = 0 + for i := range bitmap.b { + bitmap.b[i] = 0 } } } @@ -88,9 +90,9 @@ func setCheckmark(obj, base, off uintptr, mbits markBits) bool { ai := arenaIndex(obj) arena := mheap_.arenas[ai.l1()][ai.l2()] - arenaWord := (obj / heapArenaBytes / 8) % uintptr(len(arena.checkmarks)) + arenaWord := (obj / heapArenaBytes / 8) % uintptr(len(arena.checkmarks.b)) mask := byte(1 << ((obj / heapArenaBytes) % 8)) - bytep := &arena.checkmarks[arenaWord] + bytep := &arena.checkmarks.b[arenaWord] if atomic.Load8(bytep)&mask != 0 { // Already checkmarked. diff --git a/src/runtime/mem.go b/src/runtime/mem.go new file mode 100644 index 00000000000000..0ca933b25b6174 --- /dev/null +++ b/src/runtime/mem.go @@ -0,0 +1,143 @@ +// Copyright 2022 The Go Authors. 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" + +// OS memory management abstraction layer +// +// Regions of the address space managed by the runtime may be in one of four +// states at any given time: +// 1) None - Unreserved and unmapped, the default state of any region. +// 2) Reserved - Owned by the runtime, but accessing it would cause a fault. +// Does not count against the process' memory footprint. +// 3) Prepared - Reserved, intended not to be backed by physical memory (though +// an OS may implement this lazily). Can transition efficiently to +// Ready. Accessing memory in such a region is undefined (may +// fault, may give back unexpected zeroes, etc.). +// 4) Ready - may be accessed safely. +// +// This set of states is more than is strictly necessary to support all the +// currently supported platforms. One could get by with just None, Reserved, and +// Ready. However, the Prepared state gives us flexibility for performance +// purposes. For example, on POSIX-y operating systems, Reserved is usually a +// private anonymous mmap'd region with PROT_NONE set, and to transition +// to Ready would require setting PROT_READ|PROT_WRITE. However the +// underspecification of Prepared lets us use just MADV_FREE to transition from +// Ready to Prepared. Thus with the Prepared state we can set the permission +// bits just once early on, we can efficiently tell the OS that it's free to +// take pages away from us when we don't strictly need them. +// +// This file defines a cross-OS interface for a common set of helpers +// that transition memory regions between these states. The helpers call into +// OS-specific implementations that handle errors, while the interface boundary +// implements cross-OS functionality, like updating runtime accounting. + +// sysAlloc transitions an OS-chosen region of memory from None to Ready. +// More specifically, it obtains a large chunk of zeroed memory from the +// operating system, typically on the order of a hundred kilobytes +// or a megabyte. This memory is always immediately available for use. +// +// sysStat must be non-nil. +// +// Don't split the stack as this function may be invoked without a valid G, +// which prevents us from allocating more stack. +// +//go:nosplit +func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { + sysStat.add(int64(n)) + gcController.mappedReady.Add(int64(n)) + return sysAllocOS(n) +} + +// sysUnused transitions a memory region from Ready to Prepared. It notifies the +// operating system that the physical pages backing this memory region are no +// longer needed and can be reused for other purposes. The contents of a +// sysUnused memory region are considered forfeit and the region must not be +// accessed again until sysUsed is called. +func sysUnused(v unsafe.Pointer, n uintptr) { + gcController.mappedReady.Add(-int64(n)) + sysUnusedOS(v, n) +} + +// sysUsed transitions a memory region from Prepared to Ready. It notifies the +// operating system that the memory region is needed and ensures that the region +// may be safely accessed. This is typically a no-op on systems that don't have +// an explicit commit step and hard over-commit limits, but is critical on +// Windows, for example. +// +// This operation is idempotent for memory already in the Prepared state, so +// it is safe to refer, with v and n, to a range of memory that includes both +// Prepared and Ready memory. However, the caller must provide the exact amount +// of Prepared memory for accounting purposes. +func sysUsed(v unsafe.Pointer, n, prepared uintptr) { + gcController.mappedReady.Add(int64(prepared)) + sysUsedOS(v, n) +} + +// sysHugePage does not transition memory regions, but instead provides a +// hint to the OS that it would be more efficient to back this memory region +// with pages of a larger size transparently. +func sysHugePage(v unsafe.Pointer, n uintptr) { + sysHugePageOS(v, n) +} + +// sysFree transitions a memory region from any state to None. Therefore, it +// returns memory unconditionally. It is used if an out-of-memory error has been +// detected midway through an allocation or to carve out an aligned section of +// the address space. It is okay if sysFree is a no-op only if sysReserve always +// returns a memory region aligned to the heap allocator's alignment +// restrictions. +// +// sysStat must be non-nil. +// +// Don't split the stack as this function may be invoked without a valid G, +// which prevents us from allocating more stack. +// +//go:nosplit +func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { + sysStat.add(-int64(n)) + gcController.mappedReady.Add(-int64(n)) + sysFreeOS(v, n) +} + +// sysFault transitions a memory region from Ready to Reserved. It +// marks a region such that it will always fault if accessed. Used only for +// debugging the runtime. +// +// TODO(mknyszek): Currently it's true that all uses of sysFault transition +// memory from Ready to Reserved, but this may not be true in the future +// since on every platform the operation is much more general than that. +// If a transition from Prepared is ever introduced, create a new function +// that elides the Ready state accounting. +func sysFault(v unsafe.Pointer, n uintptr) { + gcController.mappedReady.Add(-int64(n)) + sysFaultOS(v, n) +} + +// sysReserve transitions a memory region from None to Reserved. It reserves +// address space in such a way that it would cause a fatal fault upon access +// (either via permissions or not committing the memory). Such a reservation is +// thus never backed by physical memory. +// +// If the pointer passed to it is non-nil, the caller wants the +// reservation there, but sysReserve can still choose another +// location if that one is unavailable. +// +// NOTE: sysReserve returns OS-aligned memory, but the heap allocator +// may use larger alignment, so the caller must be careful to realign the +// memory obtained by sysReserve. +func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { + return sysReserveOS(v, n) +} + +// sysMap transitions a memory region from Reserved to Prepared. It ensures the +// memory region can be efficiently transitioned to Ready. +// +// sysStat must be non-nil. +func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { + sysStat.add(int64(n)) + sysMapOS(v, n) +} diff --git a/src/runtime/mem_aix.go b/src/runtime/mem_aix.go index 957aa4dcc2fa31..21726b56aec910 100644 --- a/src/runtime/mem_aix.go +++ b/src/runtime/mem_aix.go @@ -10,8 +10,9 @@ import ( // Don't split the stack as this method may be invoked without a valid G, which // prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { if err == _EACCES { @@ -24,34 +25,32 @@ func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { } return nil } - sysStat.add(int64(n)) return p } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { madvise(v, n, _MADV_DONTNEED) } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) - } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -59,9 +58,7 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return p } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { // AIX does not allow mapping a range that is already mapped. // So, call mprotect to change permissions. // Note that sysMap is always called with a non-nil pointer @@ -72,6 +69,7 @@ func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { throw("runtime: out of memory") } if err != 0 { + print("runtime: mprotect(", v, ", ", n, ") returned ", err, "\n") throw("runtime: cannot map pages in arena address space") } } diff --git a/src/runtime/mem_bsd.go b/src/runtime/mem_bsd.go index dcbb9a1d51ee52..6c5edb17c232ee 100644 --- a/src/runtime/mem_bsd.go +++ b/src/runtime/mem_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || netbsd || openbsd || solaris -// +build dragonfly freebsd netbsd openbsd solaris package runtime @@ -13,42 +12,46 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil } - sysStat.add(int64(n)) return v } -func sysUnused(v unsafe.Pointer, n uintptr) { - madvise(v, n, _MADV_FREE) +func sysUnusedOS(v unsafe.Pointer, n uintptr) { + if debug.madvdontneed != 0 { + madvise(v, n, _MADV_DONTNEED) + } else { + madvise(v, n, _MADV_FREE) + } } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } // Indicates not to reserve swap space for the mapping. const _sunosMAP_NORESERVE = 0x40 -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { flags := int32(_MAP_ANON | _MAP_PRIVATE) if GOOS == "solaris" || GOOS == "illumos" { // Be explicit that we don't want to reserve swap space @@ -66,14 +69,13 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { const _sunosEAGAIN = 11 const _ENOMEM = 12 -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM || ((GOOS == "solaris" || GOOS == "illumos") && err == _sunosEAGAIN) { throw("runtime: out of memory") } if p != v || err != 0 { + print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n") throw("runtime: cannot map pages in arena address space") } } diff --git a/src/runtime/mem_darwin.go b/src/runtime/mem_darwin.go index 7fccd2bb8eed74..25862cf16176e1 100644 --- a/src/runtime/mem_darwin.go +++ b/src/runtime/mem_darwin.go @@ -10,45 +10,45 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil } - sysStat.add(int64(n)) return v } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { // MADV_FREE_REUSABLE is like MADV_FREE except it also propagates // accounting information about the process to task_info. madvise(v, n, _MADV_FREE_REUSABLE) } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { // MADV_FREE_REUSE is necessary to keep the kernel's accounting // accurate. If called on any memory region that hasn't been // MADV_FREE_REUSABLE'd, it's a no-op. madvise(v, n, _MADV_FREE_REUSE) } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -58,14 +58,13 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { const _ENOMEM = 12 -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM { throw("runtime: out of memory") } if p != v || err != 0 { + print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n") throw("runtime: cannot map pages in arena address space") } } diff --git a/src/runtime/mem_js.go b/src/runtime/mem_js.go index fe940360c0e19a..e87c5f26ae23a1 100644 --- a/src/runtime/mem_js.go +++ b/src/runtime/mem_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package runtime @@ -13,35 +12,36 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { - p := sysReserve(nil, n) - sysMap(p, n, sysStat) +func sysAllocOS(n uintptr) unsafe.Pointer { + p := sysReserveOS(nil, n) + sysMapOS(p, n) return p } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { } var reserveEnd uintptr -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { // TODO(neelance): maybe unify with mem_plan9.go, depending on how https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#finer-grained-control-over-memory turns out if v != nil { @@ -81,6 +81,5 @@ func growMemory(pages int32) int32 // This allows the front-end to replace the old DataView object with a new one. func resetMemoryDataView() -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go index 34368510911b4d..1630664cff8a96 100644 --- a/src/runtime/mem_linux.go +++ b/src/runtime/mem_linux.go @@ -16,8 +16,9 @@ const ( // Don't split the stack as this method may be invoked without a valid G, which // prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { if err == _EACCES { @@ -30,13 +31,12 @@ func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { } return nil } - sysStat.add(int64(n)) return p } var adviseUnused = uint32(_MADV_FREE) -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { // By default, Linux's "transparent huge page" support will // merge pages into a huge page if there's even a single // present regular page, undoing the effects of madvise(adviseUnused) @@ -114,9 +114,29 @@ func sysUnused(v unsafe.Pointer, n uintptr) { atomic.Store(&adviseUnused, _MADV_DONTNEED) madvise(v, n, _MADV_DONTNEED) } + + if debug.harddecommit > 0 { + p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) + if p != v || err != 0 { + throw("runtime: cannot disable permissions in address space") + } + } } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { + if debug.harddecommit > 0 { + p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) + if err == _ENOMEM { + throw("runtime: out of memory") + } + if p != v || err != 0 { + throw("runtime: cannot remap pages in address space") + } + return + + // Don't do the sysHugePage optimization in hard decommit mode. + // We're breaking up pages everywhere, there's no point. + } // Partially undo the NOHUGEPAGE marks from sysUnused // for whole huge pages between v and v+n. This may // leave huge pages off at the end points v and v+n @@ -125,10 +145,10 @@ func sysUsed(v unsafe.Pointer, n uintptr) { // the end points as well, but it's probably not worth // the cost because when neighboring allocations are // freed sysUnused will just set NOHUGEPAGE again. - sysHugePage(v, n) + sysHugePageOS(v, n) } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { if physHugePageSize != 0 { // Round v up to a huge page boundary. beg := alignUp(uintptr(v), physHugePageSize) @@ -143,17 +163,17 @@ func sysHugePage(v unsafe.Pointer, n uintptr) { // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -161,14 +181,13 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return p } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM { throw("runtime: out of memory") } if p != v || err != 0 { + print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n") throw("runtime: cannot map pages in arena address space") } } diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go index 53d8e6dffadb4a..0e8bf7474608f2 100644 --- a/src/runtime/mem_plan9.go +++ b/src/runtime/mem_plan9.go @@ -140,19 +140,15 @@ func sbrk(n uintptr) unsafe.Pointer { return unsafe.Pointer(bl) } -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { lock(&memlock) p := memAlloc(n) memCheck() unlock(&memlock) - if p != nil { - sysStat.add(int64(n)) - } return p } -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { lock(&memlock) if uintptr(v)+n == bloc { // Address range being freed is at the end of memory, @@ -167,25 +163,22 @@ func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { unlock(&memlock) } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - // sysReserve has already allocated all heap memory, - // but has not adjusted stats. - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { lock(&memlock) var p unsafe.Pointer if uintptr(v) == bloc { diff --git a/src/runtime/mem_windows.go b/src/runtime/mem_windows.go index 3a805b9767c6d3..b1292fc72526c1 100644 --- a/src/runtime/mem_windows.go +++ b/src/runtime/mem_windows.go @@ -23,13 +23,13 @@ const ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { - sysStat.add(int64(n)) +func sysAllocOS(n uintptr) unsafe.Pointer { return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_COMMIT|_MEM_RESERVE, _PAGE_READWRITE)) } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { r := stdcall3(_VirtualFree, uintptr(v), n, _MEM_DECOMMIT) if r != 0 { return @@ -59,7 +59,7 @@ func sysUnused(v unsafe.Pointer, n uintptr) { } } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { p := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE) if p == uintptr(v) { return @@ -91,14 +91,14 @@ func sysUsed(v unsafe.Pointer, n uintptr) { } } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. +// //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { r := stdcall3(_VirtualFree, uintptr(v), 0, _MEM_RELEASE) if r == 0 { print("runtime: VirtualFree of ", n, " bytes failed with errno=", getlasterror(), "\n") @@ -106,12 +106,12 @@ func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { } } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { // SysUnused makes the memory inaccessible and prevents its reuse - sysUnused(v, n) + sysUnusedOS(v, n) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { // v is just a hint. // First try at v. // This will fail if any of [v, v+n) is already reserved. @@ -124,6 +124,5 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_RESERVE, _PAGE_READWRITE)) } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s index 046c3441198bf5..a72e5f228dbb6b 100644 --- a/src/runtime/memclr_386.s +++ b/src/runtime/memclr_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 #include "go_asm.h" #include "textflag.h" @@ -30,8 +29,9 @@ tail: JBE _5through8 CMPL BX, $16 JBE _9through16 - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse2 +#ifdef GO386_softfloat + JMP nosse2 +#endif PXOR X0, X0 CMPL BX, $32 JBE _17through32 diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s index a10f57bd8cbe3b..19bfa6f20d49b5 100644 --- a/src/runtime/memclr_amd64.s +++ b/src/runtime/memclr_amd64.s @@ -3,27 +3,22 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 #include "go_asm.h" #include "textflag.h" +#include "asm_amd64.h" // See memclrNoHeapPointers Go doc for important implementation constraints. // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) // ABIInternal for performance. TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-16 -#ifdef GOEXPERIMENT_regabiargs // AX = ptr // BX = n MOVQ AX, DI // DI = ptr -#else - MOVQ ptr+0(FP), DI - MOVQ n+8(FP), BX -#endif XORQ AX, AX - // MOVOU seems always faster than REP STOSQ. + // MOVOU seems always faster than REP STOSQ when Enhanced REP STOSQ is not available. tail: // BSR+branch table make almost all memmove/memclr benchmarks worse. Not worth doing. TESTQ BX, BX @@ -37,9 +32,6 @@ tail: JE _8 CMPQ BX, $16 JBE _9through16 -#ifndef GOEXPERIMENT_regabig - PXOR X15, X15 -#endif CMPQ BX, $32 JBE _17through32 CMPQ BX, $64 @@ -48,8 +40,21 @@ tail: JBE _65through128 CMPQ BX, $256 JBE _129through256 + + CMPB internal∕cpu·X86+const_offsetX86HasERMS(SB), $1 // enhanced REP MOVSB/STOSB + JNE skip_erms + + // If the size is less than 2kb, do not use ERMS as it has a big start-up cost. + // Table 3-4. Relative Performance of Memcpy() Using ERMSB Vs. 128-bit AVX + // in the Intel Optimization Guide shows better performance for ERMSB starting + // from 2KB. Benchmarks show the similar threshold for REP STOS vs AVX. + CMPQ BX, $2048 + JAE loop_preheader_erms + +skip_erms: +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 - JE loop_preheader_avx2 + JE loop_preheader_avx2 // TODO: for really big clears, use MOVNTDQ, even without AVX2. loop: @@ -74,14 +79,16 @@ loop: CMPQ BX, $256 JAE loop JMP tail +#endif loop_preheader_avx2: - VPXOR Y0, Y0, Y0 + VPXOR X0, X0, X0 // For smaller sizes MOVNTDQ may be faster or slower depending on hardware. // For larger sizes it is always faster, even on dual Xeons with 30M cache. // TODO take into account actual LLC size. E. g. glibc uses LLC size/2. CMPQ BX, $0x2000000 - JAE loop_preheader_avx2_huge + JAE loop_preheader_avx2_huge + loop_avx2: VMOVDQU Y0, 0(DI) VMOVDQU Y0, 32(DI) @@ -97,6 +104,29 @@ loop_avx2: VMOVDQU Y0, -128(DI)(BX*1) VZEROUPPER RET + +loop_preheader_erms: +#ifndef hasAVX2 + CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 + JNE loop_erms +#endif + + VPXOR X0, X0, X0 + // At this point both ERMS and AVX2 is supported. While REP STOS can use a no-RFO + // write protocol, ERMS could show the same or slower performance comparing to + // Non-Temporal Stores when the size is bigger than LLC depending on hardware. + CMPQ BX, $0x2000000 + JAE loop_preheader_avx2_huge + +loop_erms: + // STOSQ is used to guarantee that the whole zeroed pointer-sized word is visible + // for a memory subsystem as the GC requires this. + MOVQ BX, CX + SHRQ $3, CX + ANDQ $7, BX + REP; STOSQ + JMP tail + loop_preheader_avx2_huge: // Align to 32 byte boundary VMOVDQU Y0, 0(DI) diff --git a/src/runtime/memclr_arm64.s b/src/runtime/memclr_arm64.s index c1a0dcef584fa1..1c35dfe0cf258b 100644 --- a/src/runtime/memclr_arm64.s +++ b/src/runtime/memclr_arm64.s @@ -8,10 +8,7 @@ // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) // Also called from assembly in sys_windows_arm64.s without g (but using Go stack convention). -TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 - MOVD ptr+0(FP), R0 - MOVD n+8(FP), R1 - +TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 CMP $16, R1 // If n is equal to 16 bytes, use zero_exact_16 to zero BEQ zero_exact_16 diff --git a/src/runtime/memclr_loong64.s b/src/runtime/memclr_loong64.s new file mode 100644 index 00000000000000..e4f20587b7b0c8 --- /dev/null +++ b/src/runtime/memclr_loong64.s @@ -0,0 +1,41 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "textflag.h" + +// func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) +TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 + MOVV ptr+0(FP), R6 + MOVV n+8(FP), R7 + ADDV R6, R7, R4 + + // if less than 8 bytes, do one byte at a time + SGTU $8, R7, R8 + BNE R8, out + + // do one byte at a time until 8-aligned + AND $7, R6, R8 + BEQ R8, words + MOVB R0, (R6) + ADDV $1, R6 + JMP -4(PC) + +words: + // do 8 bytes at a time if there is room + ADDV $-7, R4, R7 + + SGTU R7, R6, R8 + BEQ R8, out + MOVV R0, (R6) + ADDV $8, R6 + JMP -4(PC) + +out: + BEQ R6, R4, done + MOVB R0, (R6) + ADDV $1, R6 + JMP -3(PC) +done: + RET diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s index bc037013fe774f..cf3a9c4ab4fb36 100644 --- a/src/runtime/memclr_mips64x.s +++ b/src/runtime/memclr_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "textflag.h" diff --git a/src/runtime/memclr_mipsx.s b/src/runtime/memclr_mipsx.s index 3d21c3c414c2a0..ee3009d46b8b6f 100644 --- a/src/runtime/memclr_mipsx.s +++ b/src/runtime/memclr_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "textflag.h" diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s index 65639322b2a1cb..354325585d6634 100644 --- a/src/runtime/memclr_ppc64x.s +++ b/src/runtime/memclr_ppc64x.s @@ -3,16 +3,15 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" // See memclrNoHeapPointers Go doc for important implementation constraints. // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) -TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT|NOFRAME, $0-16 - MOVD ptr+0(FP), R3 - MOVD n+8(FP), R4 +TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT|NOFRAME, $0-16 + // R3 = ptr + // R4 = n // Determine if there are doublewords to clear check: @@ -53,37 +52,50 @@ byte4: BR zero512xsetup // ptr should now be 8 byte aligned under512: - MOVD R6, CTR // R6 = number of double words - SRDCC $2, R6, R7 // 32 byte chunks? - BNE zero32setup - - // Clear double words - -zero8: - MOVD R0, 0(R3) // double word - ADD $8, R3 - ADD $-8, R4 - BC 16, 0, zero8 // dec ctr, br zero8 if ctr not 0 - BR nozerolarge // handle leftovers + SRDCC $3, R6, R7 // 64 byte chunks? + XXLXOR VS32, VS32, VS32 // clear VS32 (V0) + BEQ lt64gt8 - // Prepare to clear 32 bytes at a time. + // Prepare to clear 64 bytes at a time. -zero32setup: +zero64setup: DCBTST (R3) // prepare data cache - XXLXOR VS32, VS32, VS32 // clear VS32 (V0) - MOVD R7, CTR // number of 32 byte chunks + MOVD R7, CTR // number of 64 byte chunks MOVD $16, R8 + MOVD $32, R16 + MOVD $48, R17 -zero32: +zero64: STXVD2X VS32, (R3+R0) // store 16 bytes STXVD2X VS32, (R3+R8) - ADD $32, R3 - ADD $-32, R4 - BC 16, 0, zero32 // dec ctr, br zero32 if ctr not 0 - RLDCLCC $61, R4, $3, R6 // remaining doublewords + STXVD2X VS32, (R3+R16) + STXVD2X VS32, (R3+R17) + ADD $64, R3 + ADD $-64, R4 + BDNZ zero64 // dec ctr, br zero64 if ctr not 0 + SRDCC $3, R4, R6 // remaining doublewords BEQ nozerolarge - MOVD R6, CTR // set up the CTR for doublewords - BR zero8 + +lt64gt8: + CMP R4, $32 + BLT lt32gt8 + MOVD $16, R8 + STXVD2X VS32, (R3+R0) + STXVD2X VS32, (R3+R8) + ADD $-32, R4 + ADD $32, R3 +lt32gt8: + CMP R4, $16 + BLT lt16gt8 + STXVD2X VS32, (R3+R0) + ADD $16, R3 + ADD $-16, R4 +lt16gt8: + CMP R4, $8 + BLT nozerolarge + MOVD R0, 0(R3) + ADD $8, R3 + ADD $-8, R4 nozerolarge: ANDCC $7, R4, R5 // any remaining bytes @@ -95,7 +107,7 @@ zerotail: zerotailloop: MOVB R0, 0(R3) // clear single bytes ADD $1, R3 - BC 16, 0, zerotailloop // dec ctr, br zerotailloop if ctr not 0 + BDNZ zerotailloop // dec ctr, br zerotailloop if ctr not 0 RET zero512xsetup: // 512 chunk with extra needed @@ -120,7 +132,7 @@ zero512preloop: // clear up to 128 alignment STXVD2X VS32, (R3+R0) // clear 16 bytes ADD $16, R3 // update ptr ADD $-16, R4 // dec count - BC 16, 0, zero512preloop + BDNZ zero512preloop zero512setup: // setup for dcbz loop CMP R4, $512 // check if at least 512 @@ -130,6 +142,7 @@ zero512setup: // setup for dcbz loop MOVD $128, R9 // index regs for 128 bytes MOVD $256, R10 MOVD $384, R11 + PCALIGN $32 zero512: DCBZ (R3+R0) // clear first chunk @@ -137,8 +150,8 @@ zero512: DCBZ (R3+R10) // clear third chunk DCBZ (R3+R11) // clear fourth chunk ADD $512, R3 - ADD $-512, R4 - BC 16, 0, zero512 + BDNZ zero512 + ANDCC $511, R4 remain: CMP R4, $128 // check if 128 byte chunks left @@ -151,16 +164,11 @@ remain: smaller: ANDCC $127, R4, R7 // find leftovers BEQ done - CMP R7, $64 // more than 64, do 32 at a time - BLT zero8setup // less than 64, do 8 at a time - SRD $5, R7, R7 // set up counter for 32 - BR zero32setup - -zero8setup: - SRDCC $3, R7, R7 // less than 8 bytes - BEQ nozerolarge - MOVD R7, CTR - BR zero8 + CMP R7, $64 // more than 64, do 64 at a time + XXLXOR VS32, VS32, VS32 + BLT lt64gt8 // less than 64 + SRD $6, R7, R7 // set up counter for 64 + BR zero64setup done: RET diff --git a/src/runtime/memclr_riscv64.s b/src/runtime/memclr_riscv64.s index 54ddaa4560c776..f0e517a547eb27 100644 --- a/src/runtime/memclr_riscv64.s +++ b/src/runtime/memclr_riscv64.s @@ -7,40 +7,42 @@ // See memclrNoHeapPointers Go doc for important implementation constraints. // void runtime·memclrNoHeapPointers(void*, uintptr) -TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 - MOV ptr+0(FP), T1 - MOV n+8(FP), T2 - ADD T1, T2, T4 +TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 +#ifndef GOEXPERIMENT_regabiargs + MOV ptr+0(FP), A0 + MOV n+8(FP), A1 +#endif + ADD A0, A1, T4 // If less than eight bytes, do one byte at a time. - SLTU $8, T2, T3 + SLTU $8, A1, T3 BNE T3, ZERO, outcheck // Do one byte at a time until eight-aligned. JMP aligncheck align: - MOVB ZERO, (T1) - ADD $1, T1 + MOVB ZERO, (A0) + ADD $1, A0 aligncheck: - AND $7, T1, T3 + AND $7, A0, T3 BNE T3, ZERO, align // Do eight bytes at a time as long as there is room. ADD $-7, T4, T5 JMP wordscheck words: - MOV ZERO, (T1) - ADD $8, T1 + MOV ZERO, (A0) + ADD $8, A0 wordscheck: - SLTU T5, T1, T3 + SLTU T5, A0, T3 BNE T3, ZERO, words JMP outcheck out: - MOVB ZERO, (T1) - ADD $1, T1 + MOVB ZERO, (A0) + ADD $1, A0 outcheck: - BNE T1, T4, out + BNE A0, T4, out done: RET diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s index 1a43a1f724d733..6d7e17fcbcec87 100644 --- a/src/runtime/memmove_386.s +++ b/src/runtime/memmove_386.s @@ -24,7 +24,6 @@ // THE SOFTWARE. //go:build !plan9 -// +build !plan9 #include "go_asm.h" #include "textflag.h" @@ -55,8 +54,9 @@ tail: JBE move_5through8 CMPL BX, $16 JBE move_9through16 - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse2 +#ifdef GO386_softfloat + JMP nosse2 +#endif CMPL BX, $32 JBE move_17through32 CMPL BX, $64 diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s index 24c6529f584054..018bb0b19d5701 100644 --- a/src/runtime/memmove_amd64.s +++ b/src/runtime/memmove_amd64.s @@ -24,7 +24,6 @@ // THE SOFTWARE. //go:build !plan9 -// +build !plan9 #include "go_asm.h" #include "textflag.h" @@ -34,18 +33,12 @@ // func memmove(to, from unsafe.Pointer, n uintptr) // ABIInternal for performance. TEXT runtime·memmove(SB), NOSPLIT, $0-24 -#ifdef GOEXPERIMENT_regabiargs // AX = to // BX = from // CX = n MOVQ AX, DI MOVQ BX, SI MOVQ CX, BX -#else - MOVQ to+0(FP), DI - MOVQ from+8(FP), SI - MOVQ n+16(FP), BX -#endif // REP instructions have a high startup cost, so we handle small sizes // with some straightline code. The REP MOVSQ instruction is really fast @@ -254,10 +247,8 @@ move_129through256: MOVOU X13, -48(DI)(BX*1) MOVOU X14, -32(DI)(BX*1) MOVOU X15, -16(DI)(BX*1) -#ifdef GOEXPERIMENT_regabig // X15 must be zero on return PXOR X15, X15 -#endif RET move_256through2048: SUBQ $256, BX @@ -297,10 +288,8 @@ move_256through2048: LEAQ 256(SI), SI LEAQ 256(DI), DI JGE move_256through2048 -#ifdef GOEXPERIMENT_regabig // X15 must be zero on return PXOR X15, X15 -#endif JMP tail avxUnaligned: @@ -429,9 +418,9 @@ gobble_mem_fwd_loop: PREFETCHNTA 0x1C0(SI) PREFETCHNTA 0x280(SI) // Prefetch values were chosen empirically. - // Approach for prefetch usage as in 7.6.6 of [1] + // Approach for prefetch usage as in 9.5.6 of [1] // [1] 64-ia-32-architectures-optimization-manual.pdf - // https://www.intel.ru/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf + // https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf VMOVDQU (SI), Y0 VMOVDQU 0x20(SI), Y1 VMOVDQU 0x40(SI), Y2 diff --git a/src/runtime/memmove_arm64.s b/src/runtime/memmove_arm64.s index 43d27629e5bd13..8ec3ed86b9b9f6 100644 --- a/src/runtime/memmove_arm64.s +++ b/src/runtime/memmove_arm64.s @@ -26,10 +26,7 @@ // The loop tail is handled by always copying 64 bytes from the end. // func memmove(to, from unsafe.Pointer, n uintptr) -TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 - MOVD to+0(FP), R0 - MOVD from+8(FP), R1 - MOVD n+16(FP), R2 +TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 CBZ R2, copy0 // Small copies: 1..16 bytes diff --git a/src/runtime/memmove_linux_amd64_test.go b/src/runtime/memmove_linux_amd64_test.go index b3ccd907b9ba8b..5f900623bee83d 100644 --- a/src/runtime/memmove_linux_amd64_test.go +++ b/src/runtime/memmove_linux_amd64_test.go @@ -6,7 +6,6 @@ package runtime_test import ( "os" - "reflect" "syscall" "testing" "unsafe" @@ -45,11 +44,7 @@ func TestMemmoveOverflow(t *testing.T) { defer syscall.Syscall(syscall.SYS_MUNMAP, base+off, 65536, 0) } - var s []byte - sp := (*reflect.SliceHeader)(unsafe.Pointer(&s)) - sp.Data = base - sp.Len, sp.Cap = 3<<30, 3<<30 - + s := unsafe.Slice((*byte)(unsafe.Pointer(base)), 3<<30) n := copy(s[1:], s) if n != 3<<30-1 { t.Fatalf("copied %d bytes, expected %d", n, 3<<30-1) diff --git a/src/runtime/memmove_loong64.s b/src/runtime/memmove_loong64.s new file mode 100644 index 00000000000000..b7b9c566272cf1 --- /dev/null +++ b/src/runtime/memmove_loong64.s @@ -0,0 +1,105 @@ +// Copyright 2022 The Go 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" + +// See memmove Go doc for important implementation constraints. + +// func memmove(to, from unsafe.Pointer, n uintptr) +TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 + MOVV to+0(FP), R4 + MOVV from+8(FP), R5 + MOVV n+16(FP), R6 + BNE R6, check + RET + +check: + SGTU R4, R5, R7 + BNE R7, backward + + ADDV R4, R6, R9 // end pointer + + // if the two pointers are not of same alignments, do byte copying + SUBVU R5, R4, R7 + AND $7, R7 + BNE R7, out + + // if less than 8 bytes, do byte copying + SGTU $8, R6, R7 + BNE R7, out + + // do one byte at a time until 8-aligned + AND $7, R4, R8 + BEQ R8, words + MOVB (R5), R7 + ADDV $1, R5 + MOVB R7, (R4) + ADDV $1, R4 + JMP -6(PC) + +words: + // do 8 bytes at a time if there is room + ADDV $-7, R9, R6 // R6 is end pointer-7 + + SGTU R6, R4, R8 + BEQ R8, out + MOVV (R5), R7 + ADDV $8, R5 + MOVV R7, (R4) + ADDV $8, R4 + JMP -6(PC) + +out: + BEQ R4, R9, done + MOVB (R5), R7 + ADDV $1, R5 + MOVB R7, (R4) + ADDV $1, R4 + JMP -5(PC) +done: + RET + +backward: + ADDV R6, R5 // from-end pointer + ADDV R4, R6, R9 // to-end pointer + + // if the two pointers are not of same alignments, do byte copying + SUBVU R9, R5, R7 + AND $7, R7 + BNE R7, out1 + + // if less than 8 bytes, do byte copying + SGTU $8, R6, R7 + BNE R7, out1 + + // do one byte at a time until 8-aligned + AND $7, R9, R8 + BEQ R8, words1 + ADDV $-1, R5 + MOVB (R5), R7 + ADDV $-1, R9 + MOVB R7, (R9) + JMP -6(PC) + +words1: + // do 8 bytes at a time if there is room + ADDV $7, R4, R6 // R6 is start pointer+7 + + SGTU R9, R6, R8 + BEQ R8, out1 + ADDV $-8, R5 + MOVV (R5), R7 + ADDV $-8, R9 + MOVV R7, (R9) + JMP -6(PC) + +out1: + BEQ R4, R9, done1 + ADDV $-1, R5 + MOVB (R5), R7 + ADDV $-1, R9 + MOVB R7, (R9) + JMP -5(PC) +done1: + RET diff --git a/src/runtime/memmove_mips64x.s b/src/runtime/memmove_mips64x.s index fef3c6be827f56..b69178ccd38bd4 100644 --- a/src/runtime/memmove_mips64x.s +++ b/src/runtime/memmove_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "textflag.h" diff --git a/src/runtime/memmove_mipsx.s b/src/runtime/memmove_mipsx.s index cd02fc25c4b41a..494288cf338cc5 100644 --- a/src/runtime/memmove_mipsx.s +++ b/src/runtime/memmove_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "textflag.h" diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s index fd16ad8129b301..5fa51c0a4cf9d6 100644 --- a/src/runtime/memmove_ppc64x.s +++ b/src/runtime/memmove_ppc64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "textflag.h" @@ -25,13 +24,17 @@ #define IDX16 R8 // temp used for copies, etc. #define TMP R9 -// number of 32 byte chunks +// number of 64 byte chunks #define QWORDS R10 +// index values +#define IDX32 R14 +#define IDX48 R15 +#define OCTWORDS R16 -TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 - MOVD to+0(FP), TGT - MOVD from+8(FP), SRC - MOVD n+16(FP), LEN +TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 + // R3 = TGT = to + // R4 = SRC = from + // R5 = LEN = n // Determine if there are doublewords to // copy so a more efficient move can be done @@ -53,28 +56,46 @@ check: // Copying forward if no overlap. BC 12, 6, checkbytes // BEQ CR1, checkbytes - SRDCC $2, DWORDS, QWORDS // 32 byte chunks? - BEQ lt32gt8 // < 32 bytes + SRDCC $3, DWORDS, OCTWORDS // 64 byte chunks? + MOVD $16, IDX16 + BEQ lt64gt8 // < 64 bytes - // Prepare for moves of 32 bytes at a time. + // Prepare for moves of 64 bytes at a time. -forward32setup: +forward64setup: DCBTST (TGT) // prepare data cache DCBT (SRC) - MOVD QWORDS, CTR // Number of 32 byte chunks - MOVD $16, IDX16 // 16 for index - -forward32: - LXVD2X (R0)(SRC), VS32 // load 16 bytes - LXVD2X (IDX16)(SRC), VS33 // load 16 bytes - ADD $32, SRC - STXVD2X VS32, (R0)(TGT) // store 16 bytes + MOVD OCTWORDS, CTR // Number of 64 byte chunks + MOVD $32, IDX32 + MOVD $48, IDX48 + PCALIGN $32 + +forward64: + LXVD2X (R0)(SRC), VS32 // load 64 bytes + LXVD2X (IDX16)(SRC), VS33 + LXVD2X (IDX32)(SRC), VS34 + LXVD2X (IDX48)(SRC), VS35 + ADD $64, SRC + STXVD2X VS32, (R0)(TGT) // store 64 bytes STXVD2X VS33, (IDX16)(TGT) - ADD $32,TGT // bump up for next set - BC 16, 0, forward32 // continue - ANDCC $3, DWORDS // remaining doublewords + STXVD2X VS34, (IDX32)(TGT) + STXVD2X VS35, (IDX48)(TGT) + ADD $64,TGT // bump up for next set + BC 16, 0, forward64 // continue + ANDCC $7, DWORDS // remaining doublewords BEQ checkbytes // only bytes remain +lt64gt8: + CMP DWORDS, $4 + BLT lt32gt8 + LXVD2X (R0)(SRC), VS32 + LXVD2X (IDX16)(SRC), VS33 + ADD $-4, DWORDS + STXVD2X VS32, (R0)(TGT) + STXVD2X VS33, (IDX16)(TGT) + ADD $32, SRC + ADD $32, TGT + lt32gt8: // At this point >= 8 and < 32 // Move 16 bytes if possible @@ -135,39 +156,41 @@ backwardtailloop: SUB $1,SRC MOVBZ TMP, -1(TGT) SUB $1,TGT - BC 16, 0, backwardtailloop // bndz + BDNZ backwardtailloop nobackwardtail: - BC 4, 5, LR // ble CR1 lr + BC 4, 5, LR // blelr cr1, return if DWORDS == 0 + SRDCC $2,DWORDS,QWORDS // Compute number of 32B blocks and compare to 0 + BNE backward32setup // If QWORDS != 0, start the 32B copy loop. -backwardlarge: - MOVD DWORDS, CTR - SUB TGT, SRC, TMP // Use vsx if moving - CMP TMP, $32 // at least 32 byte chunks - BLT backwardlargeloop // and distance >= 32 - SRDCC $2,DWORDS,QWORDS // 32 byte chunks - BNE backward32setup +backward24: + // DWORDS is a value between 1-3. + CMP DWORDS, $2 -backwardlargeloop: MOVD -8(SRC), TMP - SUB $8,SRC MOVD TMP, -8(TGT) - SUB $8,TGT - BC 16, 0, backwardlargeloop // bndz + BC 12, 0, LR // bltlr, return if DWORDS == 1 + + MOVD -16(SRC), TMP + MOVD TMP, -16(TGT) + BC 12, 2, LR // beqlr, return if DWORDS == 2 + + MOVD -24(SRC), TMP + MOVD TMP, -24(TGT) RET backward32setup: - MOVD QWORDS, CTR // set up loop ctr - MOVD $16, IDX16 // 32 bytes at a time + ANDCC $3,DWORDS // Compute remaining DWORDS and compare to 0 + MOVD QWORDS, CTR // set up loop ctr + MOVD $16, IDX16 // 32 bytes at a time backward32loop: SUB $32, TGT SUB $32, SRC - LXVD2X (R0)(TGT), VS32 // load 16 bytes - LXVD2X (IDX16)(TGT), VS33 - STXVD2X VS32, (R0)(SRC) // store 16 bytes - STXVD2X VS33, (IDX16)(SRC) - BC 16, 0, backward32loop // bndz - BC 4, 5, LR // ble CR1 lr - MOVD DWORDS, CTR - BR backwardlargeloop + LXVD2X (R0)(SRC), VS32 // load 16x2 bytes + LXVD2X (IDX16)(SRC), VS33 + STXVD2X VS32, (R0)(TGT) // store 16x2 bytes + STXVD2X VS33, (IDX16)(TGT) + BDNZ backward32loop + BC 12, 2, LR // beqlr, return if DWORDS == 0 + BR backward24 diff --git a/src/runtime/memmove_riscv64.s b/src/runtime/memmove_riscv64.s index 5dec8d0a33fcca..538aee3642245d 100644 --- a/src/runtime/memmove_riscv64.s +++ b/src/runtime/memmove_riscv64.s @@ -7,59 +7,61 @@ // See memmove Go doc for important implementation constraints. // void runtime·memmove(void*, void*, uintptr) -TEXT runtime·memmove(SB),NOSPLIT,$-0-24 - MOV to+0(FP), T0 - MOV from+8(FP), T1 - MOV n+16(FP), T2 - ADD T1, T2, T5 +TEXT runtime·memmove(SB),NOSPLIT,$-0-24 +#ifndef GOEXPERIMENT_regabiargs + MOV to+0(FP), A0 + MOV from+8(FP), A1 + MOV n+16(FP), A2 +#endif + ADD A1, A2, T5 // If the destination is ahead of the source, start at the end of the // buffer and go backward. - BLTU T1, T0, b + BLTU A1, A0, b // If less than eight bytes, do one byte at a time. - SLTU $8, T2, T3 + SLTU $8, A2, T3 BNE T3, ZERO, f_outcheck // Do one byte at a time until from is eight-aligned. JMP f_aligncheck f_align: - MOVB (T1), T3 - MOVB T3, (T0) - ADD $1, T0 - ADD $1, T1 + MOVB (A1), T3 + MOVB T3, (A0) + ADD $1, A0 + ADD $1, A1 f_aligncheck: - AND $7, T1, T3 + AND $7, A1, T3 BNE T3, ZERO, f_align // Do eight bytes at a time as long as there is room. ADD $-7, T5, T6 JMP f_wordscheck f_words: - MOV (T1), T3 - MOV T3, (T0) - ADD $8, T0 - ADD $8, T1 + MOV (A1), T3 + MOV T3, (A0) + ADD $8, A0 + ADD $8, A1 f_wordscheck: - SLTU T6, T1, T3 + SLTU T6, A1, T3 BNE T3, ZERO, f_words // Finish off the remaining partial word. JMP f_outcheck f_out: - MOVB (T1), T3 - MOVB T3, (T0) - ADD $1, T0 - ADD $1, T1 + MOVB (A1), T3 + MOVB T3, (A0) + ADD $1, A0 + ADD $1, A1 f_outcheck: - BNE T1, T5, f_out + BNE A1, T5, f_out RET b: - ADD T0, T2, T4 + ADD A0, A2, T4 // If less than eight bytes, do one byte at a time. - SLTU $8, T2, T3 + SLTU $8, A2, T3 BNE T3, ZERO, b_outcheck // Do one byte at a time until from+n is eight-aligned. @@ -74,7 +76,7 @@ b_aligncheck: BNE T3, ZERO, b_align // Do eight bytes at a time as long as there is room. - ADD $7, T1, T6 + ADD $7, A1, T6 JMP b_wordscheck b_words: ADD $-8, T4 @@ -93,6 +95,6 @@ b_out: MOVB (T5), T3 MOVB T3, (T4) b_outcheck: - BNE T5, T1, b_out + BNE T5, A1, b_out RET diff --git a/src/runtime/memmove_test.go b/src/runtime/memmove_test.go index 7c9d2ada45fd78..f1247f6ddfcf3e 100644 --- a/src/runtime/memmove_test.go +++ b/src/runtime/memmove_test.go @@ -244,23 +244,23 @@ func TestMemmoveAtomicity(t *testing.T) { dst[i] = nil } - var ready uint32 + var ready atomic.Uint32 go func() { sp := unsafe.Pointer(&src[0]) dp := unsafe.Pointer(&dst[0]) - atomic.StoreUint32(&ready, 1) + ready.Store(1) for i := 0; i < 10000; i++ { Memmove(dp, sp, sz) MemclrNoHeapPointers(dp, sz) } - atomic.StoreUint32(&ready, 2) + ready.Store(2) }() - for atomic.LoadUint32(&ready) == 0 { + for ready.Load() == 0 { Gosched() } - for atomic.LoadUint32(&ready) != 2 { + for ready.Load() != 2 { for i := range dst { p := dst[i] if p != nil && p != &x { @@ -411,160 +411,439 @@ func BenchmarkGoMemclr(b *testing.B) { }) } +func BenchmarkMemclrRange(b *testing.B) { + type RunData struct { + data []int + } + + benchSizes := []RunData{ + {[]int{1043, 1078, 1894, 1582, 1044, 1165, 1467, 1100, 1919, 1562, 1932, 1645, + 1412, 1038, 1576, 1200, 1029, 1336, 1095, 1494, 1350, 1025, 1502, 1548, 1316, 1296, + 1868, 1639, 1546, 1626, 1642, 1308, 1726, 1665, 1678, 1187, 1515, 1598, 1353, 1237, + 1977, 1452, 2012, 1914, 1514, 1136, 1975, 1618, 1536, 1695, 1600, 1733, 1392, 1099, + 1358, 1996, 1224, 1783, 1197, 1838, 1460, 1556, 1554, 2020}}, // 1kb-2kb + {[]int{3964, 5139, 6573, 7775, 6553, 2413, 3466, 5394, 2469, 7336, 7091, 6745, + 4028, 5643, 6164, 3475, 4138, 6908, 7559, 3335, 5660, 4122, 3945, 2082, 7564, 6584, + 5111, 2288, 6789, 2797, 4928, 7986, 5163, 5447, 2999, 4968, 3174, 3202, 7908, 8137, + 4735, 6161, 4646, 7592, 3083, 5329, 3687, 2754, 3599, 7231, 6455, 2549, 8063, 2189, + 7121, 5048, 4277, 6626, 6306, 2815, 7473, 3963, 7549, 7255}}, // 2kb-8kb + {[]int{16304, 15936, 15760, 4736, 9136, 11184, 10160, 5952, 14560, 15744, + 6624, 5872, 13088, 14656, 14192, 10304, 4112, 10384, 9344, 4496, 11392, 7024, + 5200, 10064, 14784, 5808, 13504, 10480, 8512, 4896, 13264, 5600}}, // 4kb-16kb + {[]int{164576, 233136, 220224, 183280, 214112, 217248, 228560, 201728}}, // 128kb-256kb + } + + for _, t := range benchSizes { + total := 0 + minLen := 0 + maxLen := 0 + + for _, clrLen := range t.data { + if clrLen > maxLen { + maxLen = clrLen + } + if clrLen < minLen || minLen == 0 { + minLen = clrLen + } + total += clrLen + } + buffer := make([]byte, maxLen) + + text := "" + if minLen >= (1 << 20) { + text = fmt.Sprint(minLen>>20, "M ", (maxLen+(1<<20-1))>>20, "M") + } else if minLen >= (1 << 10) { + text = fmt.Sprint(minLen>>10, "K ", (maxLen+(1<<10-1))>>10, "K") + } else { + text = fmt.Sprint(minLen, " ", maxLen) + } + b.Run(text, func(b *testing.B) { + b.SetBytes(int64(total)) + for i := 0; i < b.N; i++ { + for _, clrLen := range t.data { + MemclrBytes(buffer[:clrLen]) + } + } + }) + } +} + +func BenchmarkClearFat7(b *testing.B) { + p := new([7]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [7]byte{} + } +} + func BenchmarkClearFat8(b *testing.B) { + p := new([8 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [8 / 4]uint32{} + } +} + +func BenchmarkClearFat11(b *testing.B) { + p := new([11]byte) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [8 / 4]uint32 - _ = x + *p = [11]byte{} } } + func BenchmarkClearFat12(b *testing.B) { + p := new([12 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [12 / 4]uint32{} + } +} + +func BenchmarkClearFat13(b *testing.B) { + p := new([13]byte) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [12 / 4]uint32 - _ = x + *p = [13]byte{} } } + +func BenchmarkClearFat14(b *testing.B) { + p := new([14]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [14]byte{} + } +} + +func BenchmarkClearFat15(b *testing.B) { + p := new([15]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [15]byte{} + } +} + func BenchmarkClearFat16(b *testing.B) { + p := new([16 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [16 / 4]uint32 - _ = x + *p = [16 / 4]uint32{} } } + func BenchmarkClearFat24(b *testing.B) { + p := new([24 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [24 / 4]uint32 - _ = x + *p = [24 / 4]uint32{} } } + func BenchmarkClearFat32(b *testing.B) { + p := new([32 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [32 / 4]uint32 - _ = x + *p = [32 / 4]uint32{} } } + func BenchmarkClearFat40(b *testing.B) { + p := new([40 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [40 / 4]uint32 - _ = x + *p = [40 / 4]uint32{} } } + func BenchmarkClearFat48(b *testing.B) { + p := new([48 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [48 / 4]uint32 - _ = x + *p = [48 / 4]uint32{} } } + func BenchmarkClearFat56(b *testing.B) { + p := new([56 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [56 / 4]uint32 - _ = x + *p = [56 / 4]uint32{} } } + func BenchmarkClearFat64(b *testing.B) { + p := new([64 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [64 / 4]uint32{} + } +} + +func BenchmarkClearFat72(b *testing.B) { + p := new([72 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [64 / 4]uint32 - _ = x + *p = [72 / 4]uint32{} } } + func BenchmarkClearFat128(b *testing.B) { + p := new([128 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [128 / 4]uint32 - _ = x + *p = [128 / 4]uint32{} } } + func BenchmarkClearFat256(b *testing.B) { + p := new([256 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [256 / 4]uint32 - _ = x + *p = [256 / 4]uint32{} } } + func BenchmarkClearFat512(b *testing.B) { + p := new([512 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [512 / 4]uint32 - _ = x + *p = [512 / 4]uint32{} } } + func BenchmarkClearFat1024(b *testing.B) { + p := new([1024 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - var x [1024 / 4]uint32 - _ = x + *p = [1024 / 4]uint32{} + } +} + +func BenchmarkClearFat1032(b *testing.B) { + p := new([1032 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [1032 / 4]uint32{} + } +} + +func BenchmarkClearFat1040(b *testing.B) { + p := new([1040 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = [1040 / 4]uint32{} + } +} + +func BenchmarkCopyFat7(b *testing.B) { + var x [7]byte + p := new([7]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x } } func BenchmarkCopyFat8(b *testing.B) { var x [8 / 4]uint32 + p := new([8 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat11(b *testing.B) { + var x [11]byte + p := new([11]byte) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat12(b *testing.B) { var x [12 / 4]uint32 + p := new([12 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat13(b *testing.B) { + var x [13]byte + p := new([13]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat14(b *testing.B) { + var x [14]byte + p := new([14]byte) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat15(b *testing.B) { + var x [15]byte + p := new([15]byte) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat16(b *testing.B) { var x [16 / 4]uint32 + p := new([16 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat24(b *testing.B) { var x [24 / 4]uint32 + p := new([24 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat32(b *testing.B) { var x [32 / 4]uint32 + p := new([32 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat64(b *testing.B) { var x [64 / 4]uint32 + p := new([64 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat72(b *testing.B) { + var x [72 / 4]uint32 + p := new([72 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat128(b *testing.B) { var x [128 / 4]uint32 + p := new([128 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat256(b *testing.B) { var x [256 / 4]uint32 + p := new([256 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat512(b *testing.B) { var x [512 / 4]uint32 + p := new([512 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat520(b *testing.B) { var x [520 / 4]uint32 + p := new([520 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } + func BenchmarkCopyFat1024(b *testing.B) { var x [1024 / 4]uint32 + p := new([1024 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat1032(b *testing.B) { + var x [1032 / 4]uint32 + p := new([1032 / 4]uint32) + Escape(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + *p = x + } +} + +func BenchmarkCopyFat1040(b *testing.B) { + var x [1040 / 4]uint32 + p := new([1040 / 4]uint32) + Escape(p) + b.ResetTimer() for i := 0; i < b.N; i++ { - y := x - _ = y + *p = x } } diff --git a/src/runtime/metrics.go b/src/runtime/metrics.go index ba0a920a5d16bc..313850a3a0054f 100644 --- a/src/runtime/metrics.go +++ b/src/runtime/metrics.go @@ -7,14 +7,16 @@ package runtime // Metrics implementation exported to runtime/metrics. import ( - "runtime/internal/atomic" "unsafe" ) var ( - // metrics is a map of runtime/metrics keys to - // data used by the runtime to sample each metric's - // value. + // metrics is a map of runtime/metrics keys to data used by the runtime + // to sample each metric's value. metricsInit indicates it has been + // initialized. + // + // These fields are protected by metricsSema which should be + // locked/unlocked with metricsLock() / metricsUnlock(). metricsSema uint32 = 1 metricsInit bool metrics map[string]metricData @@ -34,6 +36,23 @@ type metricData struct { compute func(in *statAggregate, out *metricValue) } +func metricsLock() { + // Acquire the metricsSema but with handoff. Operations are typically + // expensive enough that queueing up goroutines and handing off between + // them will be noticeably better-behaved. + semacquire1(&metricsSema, true, 0, 0) + if raceenabled { + raceacquire(unsafe.Pointer(&metricsSema)) + } +} + +func metricsUnlock() { + if raceenabled { + racerelease(unsafe.Pointer(&metricsSema)) + } + semrelease(&metricsSema) +} + // initMetrics initializes the metrics map if it hasn't been yet. // // metricsSema must be held. @@ -65,6 +84,12 @@ func initMetrics() { timeHistBuckets = timeHistogramMetricsBuckets() metrics = map[string]metricData{ + "/cgo/go-to-c-calls:calls": { + compute: func(_ *statAggregate, out *metricValue) { + out.kind = metricKindUint64 + out.scalar = uint64(NumCgoCall()) + }, + }, "/gc/cycles/automatic:gc-cycles": { deps: makeStatDepSet(sysStatsDep), compute: func(in *statAggregate, out *metricValue) { @@ -159,18 +184,30 @@ func initMetrics() { out.scalar = uint64(in.heapStats.tinyAllocCount) }, }, + "/gc/limiter/last-enabled:gc-cycle": { + compute: func(_ *statAggregate, out *metricValue) { + out.kind = metricKindUint64 + out.scalar = uint64(gcCPULimiter.lastEnabledCycle.Load()) + }, + }, "/gc/pauses:seconds": { compute: func(_ *statAggregate, out *metricValue) { hist := out.float64HistOrInit(timeHistBuckets) // The bottom-most bucket, containing negative values, is tracked // as a separately as underflow, so fill that in manually and then // iterate over the rest. - hist.counts[0] = atomic.Load64(&memstats.gcPauseDist.underflow) + hist.counts[0] = memstats.gcPauseDist.underflow.Load() for i := range memstats.gcPauseDist.counts { - hist.counts[i+1] = atomic.Load64(&memstats.gcPauseDist.counts[i]) + hist.counts[i+1] = memstats.gcPauseDist.counts[i].Load() } }, }, + "/gc/stack/starting-size:bytes": { + compute: func(in *statAggregate, out *metricValue) { + out.kind = metricKindUint64 + out.scalar = uint64(startingStackSize) + }, + }, "/memory/classes/heap/free:bytes": { deps: makeStatDepSet(heapStatsDep), compute: func(in *statAggregate, out *metricValue) { @@ -274,6 +311,12 @@ func initMetrics() { in.sysStats.gcMiscSys + in.sysStats.otherSys }, }, + "/sched/gomaxprocs:threads": { + compute: func(_ *statAggregate, out *metricValue) { + out.kind = metricKindUint64 + out.scalar = uint64(gomaxprocs) + }, + }, "/sched/goroutines:goroutines": { compute: func(_ *statAggregate, out *metricValue) { out.kind = metricKindUint64 @@ -283,9 +326,9 @@ func initMetrics() { "/sched/latencies:seconds": { compute: func(_ *statAggregate, out *metricValue) { hist := out.float64HistOrInit(timeHistBuckets) - hist.counts[0] = atomic.Load64(&sched.timeToRun.underflow) + hist.counts[0] = sched.timeToRun.underflow.Load() for i := range sched.timeToRun.counts { - hist.counts[i+1] = atomic.Load64(&sched.timeToRun.counts[i]) + hist.counts[i+1] = sched.timeToRun.counts[i].Load() } }, }, @@ -388,13 +431,13 @@ func (a *heapStatsAggregate) compute() { memstats.heapStats.read(&a.heapStatsDelta) // Calculate derived stats. - a.totalAllocs = uint64(a.largeAllocCount) - a.totalFrees = uint64(a.largeFreeCount) - a.totalAllocated = uint64(a.largeAlloc) - a.totalFreed = uint64(a.largeFree) + a.totalAllocs = a.largeAllocCount + a.totalFrees = a.largeFreeCount + a.totalAllocated = a.largeAlloc + a.totalFreed = a.largeFree for i := range a.smallAllocCount { - na := uint64(a.smallAllocCount[i]) - nf := uint64(a.smallFreeCount[i]) + na := a.smallAllocCount[i] + nf := a.smallFreeCount[i] a.totalAllocs += na a.totalFrees += nf a.totalAllocated += na * uint64(class_to_size[i]) @@ -431,7 +474,7 @@ func (a *sysStatsAggregate) compute() { a.buckHashSys = memstats.buckhash_sys.load() a.gcMiscSys = memstats.gcMiscSys.load() a.otherSys = memstats.other_sys.load() - a.heapGoal = atomic.Load64(&gcController.heapGoal) + a.heapGoal = gcController.heapGoal() a.gcCyclesDone = uint64(memstats.numgc) a.gcCyclesForced = uint64(memstats.numforcedgc) @@ -546,10 +589,7 @@ func readMetrics(samplesp unsafe.Pointer, len int, cap int) { sl := slice{samplesp, len, cap} samples := *(*[]metricSample)(unsafe.Pointer(&sl)) - // Acquire the metricsSema but with handoff. This operation - // is expensive enough that queueing up goroutines and handing - // off between them will be noticeably better-behaved. - semacquire1(&metricsSema, true, 0, 0) + metricsLock() // Ensure the map is initialized. initMetrics() @@ -573,5 +613,5 @@ func readMetrics(samplesp unsafe.Pointer, len int, cap int) { data.compute(&agg, &sample.value) } - semrelease(&metricsSema) + metricsUnlock() } diff --git a/src/runtime/metrics/description.go b/src/runtime/metrics/description.go index c147cada8944a2..ee99d3938df607 100644 --- a/src/runtime/metrics/description.go +++ b/src/runtime/metrics/description.go @@ -51,6 +51,12 @@ type Description struct { // The English language descriptions below must be kept in sync with the // descriptions of each metric in doc.go. var allDesc = []Description{ + { + Name: "/cgo/go-to-c-calls:calls", + Description: "Count of calls made from Go to C by the current process.", + Kind: KindUint64, + Cumulative: true, + }, { Name: "/gc/cycles/automatic:gc-cycles", Description: "Count of completed GC cycles generated by the Go runtime.", @@ -134,12 +140,27 @@ var allDesc = []Description{ Kind: KindUint64, Cumulative: true, }, + { + Name: "/gc/limiter/last-enabled:gc-cycle", + Description: "GC cycle the last time the GC CPU limiter was enabled. " + + "This metric is useful for diagnosing the root cause of an out-of-memory " + + "error, because the limiter trades memory for CPU time when the GC's CPU " + + "time gets too high. This is most likely to occur with use of SetMemoryLimit. " + + "The first GC cycle is cycle 1, so a value of 0 indicates that it was never enabled.", + Kind: KindUint64, + }, { Name: "/gc/pauses:seconds", Description: "Distribution individual GC-related stop-the-world pause latencies.", Kind: KindFloat64Histogram, Cumulative: true, }, + { + Name: "/gc/stack/starting-size:bytes", + Description: "The stack size of new goroutines.", + Kind: KindUint64, + Cumulative: false, + }, { Name: "/memory/classes/heap/free:bytes", Description: "Memory that is completely free and eligible to be returned to the underlying system, " + @@ -214,6 +235,11 @@ var allDesc = []Description{ Description: "All memory mapped by the Go runtime into the current process as read-write. Note that this does not include memory mapped by code called via cgo or via the syscall package. Sum of all metrics in /memory/classes.", Kind: KindUint64, }, + { + Name: "/sched/gomaxprocs:threads", + Description: "The current runtime.GOMAXPROCS setting, or the number of operating system threads that can execute user-level Go code simultaneously.", + Kind: KindUint64, + }, { Name: "/sched/goroutines:goroutines", Description: "Count of live goroutines.", diff --git a/src/runtime/metrics/description_test.go b/src/runtime/metrics/description_test.go index fd1fd46efc04da..192c1f29ccfbf3 100644 --- a/src/runtime/metrics/description_test.go +++ b/src/runtime/metrics/description_test.go @@ -103,7 +103,7 @@ func TestDescriptionDocs(t *testing.T) { } if len(docs) > len(descriptions) { docsLoop: - for name, _ := range docs { + for name := range docs { for _, d := range descriptions { if name == d.Name { continue docsLoop diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index 91ef03072de49b..28c9f6abb5eb3a 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -11,7 +11,7 @@ The set of metrics defined by this package may evolve as the runtime itself evolves, and also enables variation across Go implementations, whose relevant metric sets may not intersect. -Interface +# Interface Metrics are designated by a string key, rather than, for example, a field name in a struct. The full list of supported metrics is always available in the slice of @@ -30,7 +30,7 @@ In the interest of not breaking users of this package, the "kind" for a given me is guaranteed not to change. If it must change, then a new metric will be introduced with a new key and a new "kind." -Metric key format +# Metric key format As mentioned earlier, metric keys are strings. Their format is simple and well-defined, designed to be both human and machine readable. It is split into two components, @@ -41,16 +41,19 @@ did also, and a new key should be introduced. For more details on the precise definition of the metric key's path and unit formats, see the documentation of the Name field of the Description struct. -A note about floats +# A note about floats This package supports metrics whose values have a floating-point representation. In order to improve ease-of-use, this package promises to never produce the following classes of floating-point values: NaN, infinity. -Supported metrics +# Supported metrics Below is the full list of supported metrics, ordered lexicographically. + /cgo/go-to-c-calls:calls + Count of calls made from Go to C by the current process. + /gc/cycles/automatic:gc-cycles Count of completed GC cycles generated by the Go runtime. @@ -99,9 +102,19 @@ Below is the full list of supported metrics, ordered lexicographically. only their block. Each block is already accounted for in allocs-by-size and frees-by-size. + /gc/limiter/last-enabled:gc-cycle + GC cycle the last time the GC CPU limiter was enabled. + This metric is useful for diagnosing the root cause of an out-of-memory + error, because the limiter trades memory for CPU time when the GC's CPU + time gets too high. This is most likely to occur with use of SetMemoryLimit. + The first GC cycle is cycle 1, so a value of 0 indicates that it was never enabled. + /gc/pauses:seconds Distribution individual GC-related stop-the-world pause latencies. + /gc/stack/starting-size:bytes + The stack size of new goroutines. + /memory/classes/heap/free:bytes Memory that is completely free and eligible to be returned to the underlying system, but has not been. This metric is the @@ -164,6 +177,11 @@ Below is the full list of supported metrics, ordered lexicographically. by code called via cgo or via the syscall package. Sum of all metrics in /memory/classes. + /sched/gomaxprocs:threads + The current runtime.GOMAXPROCS setting, or the number of + operating system threads that can execute user-level Go code + simultaneously. + /sched/goroutines:goroutines Count of live goroutines. diff --git a/src/runtime/metrics_test.go b/src/runtime/metrics_test.go index 5d32ef469caaa9..8baf0209000731 100644 --- a/src/runtime/metrics_test.go +++ b/src/runtime/metrics_test.go @@ -9,6 +9,7 @@ import ( "runtime/metrics" "sort" "strings" + "sync" "testing" "time" "unsafe" @@ -45,6 +46,8 @@ func TestReadMetrics(t *testing.T) { var mallocs, frees uint64 for i := range samples { switch name := samples[i].Name; name { + case "/cgo/go-to-c-calls:calls": + checkUint64(t, name, samples[i].Value.Uint64(), uint64(runtime.NumCgoCall())) case "/memory/classes/heap/free:bytes": checkUint64(t, name, samples[i].Value.Uint64(), mstats.HeapIdle-mstats.HeapReleased) case "/memory/classes/heap/released:bytes": @@ -222,6 +225,10 @@ func TestReadMetricsConsistency(t *testing.T) { for i := range h.Counts { gc.pauses += h.Counts[i] } + case "/sched/gomaxprocs:threads": + if got, want := samples[i].Value.Uint64(), uint64(runtime.GOMAXPROCS(-1)); got != want { + t.Errorf("gomaxprocs doesn't match runtime.GOMAXPROCS: got %d, want %d", got, want) + } case "/sched/goroutines:goroutines": if samples[i].Value.Uint64() < 1 { t.Error("number of goroutines is less than one") @@ -319,3 +326,88 @@ func BenchmarkReadMetricsLatency(b *testing.B) { b.ReportMetric(float64(latencies[len(latencies)*90/100]), "p90-ns") b.ReportMetric(float64(latencies[len(latencies)*99/100]), "p99-ns") } + +var readMetricsSink [1024]interface{} + +func TestReadMetricsCumulative(t *testing.T) { + // Set up the set of metrics marked cumulative. + descs := metrics.All() + var samples [2][]metrics.Sample + samples[0] = make([]metrics.Sample, len(descs)) + samples[1] = make([]metrics.Sample, len(descs)) + total := 0 + for i := range samples[0] { + if !descs[i].Cumulative { + continue + } + samples[0][total].Name = descs[i].Name + total++ + } + samples[0] = samples[0][:total] + samples[1] = samples[1][:total] + copy(samples[1], samples[0]) + + // Start some noise in the background. + var wg sync.WaitGroup + wg.Add(1) + done := make(chan struct{}) + go func() { + defer wg.Done() + for { + // Add more things here that could influence metrics. + for i := 0; i < len(readMetricsSink); i++ { + readMetricsSink[i] = make([]byte, 1024) + select { + case <-done: + return + default: + } + } + runtime.GC() + } + }() + + sum := func(us []uint64) uint64 { + total := uint64(0) + for _, u := range us { + total += u + } + return total + } + + // Populate the first generation. + metrics.Read(samples[0]) + + // Check to make sure that these metrics only grow monotonically. + for gen := 1; gen < 10; gen++ { + metrics.Read(samples[gen%2]) + for i := range samples[gen%2] { + name := samples[gen%2][i].Name + vNew, vOld := samples[gen%2][i].Value, samples[1-(gen%2)][i].Value + + switch vNew.Kind() { + case metrics.KindUint64: + new := vNew.Uint64() + old := vOld.Uint64() + if new < old { + t.Errorf("%s decreased: %d < %d", name, new, old) + } + case metrics.KindFloat64: + new := vNew.Float64() + old := vOld.Float64() + if new < old { + t.Errorf("%s decreased: %f < %f", name, new, old) + } + case metrics.KindFloat64Histogram: + new := sum(vNew.Float64Histogram().Counts) + old := sum(vOld.Float64Histogram().Counts) + if new < old { + t.Errorf("%s counts decreased: %d < %d", name, new, old) + } + } + } + } + close(done) + + wg.Wait() +} diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index c134a0f22d8e6f..ef11b7df96dda6 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -8,6 +8,7 @@ package runtime import ( "internal/abi" + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -19,23 +20,32 @@ import ( // finblock is allocated from non-GC'd memory, so any heap pointers // must be specially handled. GC currently assumes that the finalizer // queue does not grow during marking (but it can shrink). -// -//go:notinheap type finblock struct { + _ sys.NotInHeap alllink *finblock next *finblock cnt uint32 _ int32 - fin [(_FinBlockSize - 2*sys.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer + fin [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer } +var fingStatus atomic.Uint32 + +// finalizer goroutine status. +const ( + fingUninitialized uint32 = iota + fingCreated uint32 = 1 << (iota - 1) + fingRunningFinalizer + fingWait + fingWake +) + var finlock mutex // protects the following variables var fing *g // goroutine that runs finalizers var finq *finblock // list of finalizers that are to be executed var finc *finblock // cache of free blocks -var finptrmask [_FinBlockSize / sys.PtrSize / 8]byte -var fingwait bool -var fingwake bool +var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte + var allfin *finblock // list of all blocks // NOTE: Layout known to queuefinalizer. @@ -75,6 +85,12 @@ var finalizer1 = [...]byte{ 0<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 | 1<<6 | 1<<7, } +// lockRankMayQueueFinalizer records the lock ranking effects of a +// function that may call queuefinalizer. +func lockRankMayQueueFinalizer() { + lockWithRankMayAcquire(&finlock, getLockRank(&finlock)) +} + func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot *ptrtype) { if gcphase != _GCoff { // Currently we assume that the finalizer queue won't @@ -95,12 +111,12 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot if finptrmask[0] == 0 { // Build pointer mask for Finalizer array in block. // Check assumptions made in finalizer1 array above. - if (unsafe.Sizeof(finalizer{}) != 5*sys.PtrSize || + if (unsafe.Sizeof(finalizer{}) != 5*goarch.PtrSize || unsafe.Offsetof(finalizer{}.fn) != 0 || - unsafe.Offsetof(finalizer{}.arg) != sys.PtrSize || - unsafe.Offsetof(finalizer{}.nret) != 2*sys.PtrSize || - unsafe.Offsetof(finalizer{}.fint) != 3*sys.PtrSize || - unsafe.Offsetof(finalizer{}.ot) != 4*sys.PtrSize) { + unsafe.Offsetof(finalizer{}.arg) != goarch.PtrSize || + unsafe.Offsetof(finalizer{}.nret) != 2*goarch.PtrSize || + unsafe.Offsetof(finalizer{}.fint) != 3*goarch.PtrSize || + unsafe.Offsetof(finalizer{}.ot) != 4*goarch.PtrSize) { throw("finalizer out of sync") } for i := range finptrmask { @@ -120,8 +136,8 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot f.fint = fint f.ot = ot f.arg = p - fingwake = true unlock(&finlock) + fingStatus.Or(fingWake) } //go:nowritebarrier @@ -135,29 +151,27 @@ func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrt } func wakefing() *g { - var res *g - lock(&finlock) - if fingwait && fingwake { - fingwait = false - fingwake = false - res = fing + if ok := fingStatus.CompareAndSwap(fingCreated|fingWait|fingWake, fingCreated); ok { + return fing } - unlock(&finlock) - return res + return nil } -var ( - fingCreate uint32 - fingRunning bool -) - func createfing() { // start the finalizer goroutine exactly once - if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) { + if fingStatus.Load() == fingUninitialized && fingStatus.CompareAndSwap(fingUninitialized, fingCreated) { go runfinq() } } +func finalizercommit(gp *g, lock unsafe.Pointer) bool { + unlock((*mutex)(lock)) + // fingStatus should be modified after fing is put into a waiting state + // to avoid waking fing in running state, even if it is about to be parked. + fingStatus.Or(fingWait) + return true +} + // This is the goroutine that runs all of the finalizers func runfinq() { var ( @@ -166,15 +180,17 @@ func runfinq() { argRegs int ) + gp := getg() + lock(&finlock) + fing = gp + unlock(&finlock) + for { lock(&finlock) fb := finq finq = nil if fb == nil { - gp := getg() - fing = gp - fingwait = true - goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1) + gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1) continue } argRegs = intArgRegs @@ -187,21 +203,15 @@ func runfinq() { f := &fb.fin[i-1] var regs abi.RegArgs - var framesz uintptr - if argRegs > 0 { - // The args can always be passed in registers if they're - // available, because platforms we support always have no - // argument registers available, or more than 2. - // - // But unfortunately because we can have an arbitrary - // amount of returns and it would be complex to try and - // figure out how many of those can get passed in registers, - // just conservatively assume none of them do. - framesz = f.nret - } else { - // Need to pass arguments on the stack too. - framesz = unsafe.Sizeof((interface{})(nil)) + f.nret - } + // The args may be passed in registers or on stack. Even for + // the register case, we still need the spill slots. + // TODO: revisit if we remove spill slots. + // + // Unfortunately because we can have an arbitrary + // amount of returns and it would be complex to try and + // figure out how many of those can get passed in registers, + // just conservatively assume none of them do. + framesz := unsafe.Sizeof((any)(nil)) + f.nret if framecap < framesz { // The frame does not contain pointers interesting for GC, // all not yet finalized objects are stored in finq. @@ -241,9 +251,9 @@ func runfinq() { default: throw("bad kind in runfinq") } - fingRunning = true + fingStatus.Or(fingRunningFinalizer) reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), ®s) - fingRunning = false + fingStatus.And(^fingRunningFinalizer) // Drop finalizer queue heap references // before hiding them from markroot. @@ -324,12 +334,24 @@ func runfinq() { // 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. +// call 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. -func SetFinalizer(obj interface{}, finalizer interface{}) { +// +// In the terminology of the Go memory model, a call +// SetFinalizer(x, f) “synchronizes before” the finalization call f(x). +// However, there is no guarantee that KeepAlive(x) or any other use of x +// “synchronizes before” f(x), so in general a finalizer should use a mutex +// or other synchronization mechanism if it needs to access mutable state in x. +// For example, consider a finalizer that inspects a mutable field in x +// that is modified from time to time in the main program before x +// becomes unreachable and the finalizer is invoked. +// The modifications in the main program and the inspection in the finalizer +// need to use appropriate synchronization, such as mutexes or atomic updates, +// to avoid read-write races. +func SetFinalizer(obj any, finalizer any) { if debug.sbrk != 0 { // debug.sbrk never frees memory, so no finalizers run // (and we don't have the data structures to record them). @@ -432,7 +454,7 @@ okarg: for _, t := range ft.out() { nret = alignUp(nret, uintptr(t.align)) + uintptr(t.size) } - nret = alignUp(nret, sys.PtrSize) + nret = alignUp(nret, goarch.PtrSize) // make sure we have a finalizer goroutine createfing() @@ -445,6 +467,7 @@ okarg: } // Mark KeepAlive as noinline so that it is easily detectable as an intrinsic. +// //go:noinline // KeepAlive marks its argument as currently reachable. @@ -452,16 +475,17 @@ okarg: // 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. +// +// 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 @@ -470,7 +494,7 @@ okarg: // Note: KeepAlive should only be used to prevent finalizers from // running prematurely. In particular, when used with unsafe.Pointer, // the rules for valid uses of unsafe.Pointer still apply. -func KeepAlive(x interface{}) { +func KeepAlive(x any) { // Introduce a use of x that the compiler can't eliminate. // This makes sure x is alive on entry. We need x to be alive // on entry for "defer runtime.KeepAlive(x)"; see issue 21402. diff --git a/src/runtime/mfinal_test.go b/src/runtime/mfinal_test.go index 3ca8d31c60598b..902ccc57f87880 100644 --- a/src/runtime/mfinal_test.go +++ b/src/runtime/mfinal_test.go @@ -34,14 +34,23 @@ func TestFinalizerType(t *testing.T) { } var finalizerTests = []struct { - convert func(*int) interface{} - finalizer interface{} + convert func(*int) any + finalizer any }{ - {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }}, - {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }}, - {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }}, - {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }}, - {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }}, + {func(x *int) any { return x }, func(v *int) { finalize(v) }}, + {func(x *int) any { return Tintptr(x) }, func(v Tintptr) { finalize(v) }}, + {func(x *int) any { return Tintptr(x) }, func(v *int) { finalize(v) }}, + {func(x *int) any { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }}, + {func(x *int) any { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }}, + // Test case for argument spill slot. + // If the spill slot was not counted for the frame size, it will (incorrectly) choose + // call32 as the result has (exactly) 32 bytes. When the argument actually spills, + // it clobbers the caller's frame (likely the return PC). + {func(x *int) any { return x }, func(v any) [4]int64 { + print() // force spill + finalize(v.(*int)) + return [4]int64{} + }}, } for i, tt := range finalizerTests { @@ -85,7 +94,7 @@ func TestFinalizerInterfaceBig(t *testing.T) { go func() { v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"} old := *v - runtime.SetFinalizer(v, func(v interface{}) { + runtime.SetFinalizer(v, func(v any) { i, ok := v.(*bigValue) if !ok { t.Errorf("finalizer called with type %T, want *bigValue", v) diff --git a/src/runtime/mfixalloc.go b/src/runtime/mfixalloc.go index 293c16b38b1bc1..8788d95c003c1a 100644 --- a/src/runtime/mfixalloc.go +++ b/src/runtime/mfixalloc.go @@ -8,7 +8,10 @@ package runtime -import "unsafe" +import ( + "runtime/internal/sys" + "unsafe" +) // FixAlloc is a simple free-list allocator for fixed size objects. // Malloc uses a FixAlloc wrapped around sysAlloc to manage its @@ -23,14 +26,16 @@ import "unsafe" // Callers can keep state in the object but the first word is // smashed by freeing and reallocating. // -// Consider marking fixalloc'd types go:notinheap. +// Consider marking fixalloc'd types not in heap by embedding +// runtime/internal/sys.NotInHeap. type fixalloc struct { size uintptr first func(arg, p unsafe.Pointer) // called first time p is returned arg unsafe.Pointer list *mlink chunk uintptr // use uintptr instead of unsafe.Pointer to avoid write barriers - nchunk uint32 + nchunk uint32 // bytes remaining in current chunk + nalloc uint32 // size of new chunks in bytes inuse uintptr // in-use bytes now stat *sysMemStat zero bool // zero allocations @@ -41,21 +46,28 @@ type fixalloc struct { // this cannot be used by some of the internal GC structures. For example when // the sweeper is placing an unmarked object on the free list it does not want the // write barrier to be called since that could result in the object being reachable. -// -//go:notinheap type mlink struct { + _ sys.NotInHeap next *mlink } // Initialize f to allocate objects of the given size, // using the allocator to obtain chunks of memory. func (f *fixalloc) init(size uintptr, first func(arg, p unsafe.Pointer), arg unsafe.Pointer, stat *sysMemStat) { + if size > _FixAllocChunk { + throw("runtime: fixalloc size too large") + } + if min := unsafe.Sizeof(mlink{}); size < min { + size = min + } + f.size = size f.first = first f.arg = arg f.list = nil f.chunk = 0 f.nchunk = 0 + f.nalloc = uint32(_FixAllocChunk / size * size) // Round _FixAllocChunk down to an exact multiple of size to eliminate tail waste f.inuse = 0 f.stat = stat f.zero = true @@ -77,8 +89,8 @@ func (f *fixalloc) alloc() unsafe.Pointer { return v } if uintptr(f.nchunk) < f.size { - f.chunk = uintptr(persistentalloc(_FixAllocChunk, 0, f.stat)) - f.nchunk = _FixAllocChunk + f.chunk = uintptr(persistentalloc(uintptr(f.nalloc), 0, f.stat)) + f.nchunk = f.nalloc } v := unsafe.Pointer(f.chunk) diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 458566353536f5..3243a15b4d1e85 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -113,7 +113,7 @@ // Next GC is after we've allocated an extra amount of memory proportional to // the amount already in use. The proportion is controlled by GOGC environment variable // (100 by default). If GOGC=100 and we're using 4M, we'll GC again when we get to 8M -// (this mark is tracked in gcController.heapGoal variable). This keeps the GC cost in +// (this mark is computed by the gcController.heapGoal method). This keeps the GC cost in // linear proportion to the allocation cost. Adjusting GOGC just changes the linear constant // (and also the amount of extra memory used). @@ -154,11 +154,12 @@ func gcinit() { throw("size of Workbuf is suboptimal") } // No sweep on the first cycle. - mheap_.sweepDrained = 1 + sweep.active.state.Store(sweepDrainedMask) // Initialize GC pacer state. // Use the environment variable GOGC for the initial gcPercent value. - gcController.init(readGOGC()) + // Use the environment variable GOMEMLIMIT for the initial memoryLimit value. + gcController.init(readGOGC(), readGOMEMLIMIT()) work.startSema = 1 work.markDoneSema = 1 @@ -167,22 +168,17 @@ func gcinit() { lockInit(&work.wbufSpans.lock, lockRankWbufSpans) } -// Temporary in order to enable register ABI work. -// TODO(register args): convert back to local chan in gcenabled, passed to "go" stmts. -var gcenable_setup chan int - // gcenable is called after the bulk of the runtime initialization, // just before we're about to start letting user code run. // It kicks off the background sweeper goroutine, the background // scavenger goroutine, and enables GC. func gcenable() { // Kick off sweeping and scavenging. - gcenable_setup = make(chan int, 2) - go bgsweep() - go bgscavenge() - <-gcenable_setup - <-gcenable_setup - gcenable_setup = nil + c := make(chan int, 2) + go bgsweep(c) + go bgscavenge(c) + <-c + <-c memstats.enablegc = true // now that runtime is initialized, GC is okay } @@ -283,7 +279,9 @@ func pollFractionalWorkerExit() bool { return float64(selfTime)/float64(delta) > 1.2*gcController.fractionalUtilizationGoal } -var work struct { +var work workType + +type workType struct { full lfstack // lock-free list of full blocks workbuf empty lfstack // lock-free list of empty blocks workbuf pad0 cpu.CacheLinePad // prevents false-sharing between full/empty and nproc/nwait @@ -325,11 +323,20 @@ var work struct { nwait uint32 // Number of roots of various root types. Set by gcMarkRootPrepare. + // + // nStackRoots == len(stackRoots), but we have nStackRoots for + // consistency. nDataRoots, nBSSRoots, nSpanRoots, nStackRoots int // Base indexes of each root type. Set by gcMarkRootPrepare. baseData, baseBSS, baseSpans, baseStacks, baseEnd uint32 + // stackRoots is a snapshot of all of the Gs that existed + // before the beginning of concurrent marking. The backing + // store of this must not be modified because it might be + // shared with allgs. + stackRoots []*g + // Each type of GC state transition is protected by a lock. // Since multiple threads can simultaneously detect the state // transition condition, any thread that detects a transition @@ -386,7 +393,7 @@ var work struct { // cycle is sweep termination, mark, mark termination, and // sweep. This differs from memstats.numgc, which is // incremented at mark termination. - cycles uint32 + cycles atomic.Uint32 // Timing/utilization stats for this cycle. stwprocs, maxprocs int32 @@ -396,7 +403,7 @@ var work struct { pauseStart int64 // nanotime() of last STW // debug.gctrace heap sizes for this cycle. - heap0, heap1, heap2, heapGoal uint64 + heap0, heap1, heap2 uint64 } // GC runs a garbage collection and blocks the caller until the @@ -429,7 +436,7 @@ func GC() { // Wait until the current sweep termination, mark, and mark // termination complete. - n := atomic.Load(&work.cycles) + n := work.cycles.Load() gcWaitOnMark(n) // We're now in sweep N or later. Trigger GC cycle N+1, which @@ -444,7 +451,7 @@ func GC() { // complete the cycle and because runtime.GC() is often used // as part of tests and benchmarks to get the system into a // relatively stable and isolated state. - for atomic.Load(&work.cycles) == n+1 && sweepone() != ^uintptr(0) { + for work.cycles.Load() == n+1 && sweepone() != ^uintptr(0) { sweep.nbgsweep++ Gosched() } @@ -460,7 +467,7 @@ func GC() { // First, wait for sweeping to finish. (We know there are no // more spans on the sweep queue, but we may be concurrently // sweeping spans, so we have to wait.) - for atomic.Load(&work.cycles) == n+1 && !isSweepDone() { + for work.cycles.Load() == n+1 && !isSweepDone() { Gosched() } @@ -468,7 +475,7 @@ func GC() { // stable heap profile. Only do this if we haven't already hit // another mark termination. mp := acquirem() - cycle := atomic.Load(&work.cycles) + cycle := work.cycles.Load() if cycle == n+1 || (gcphase == _GCmark && cycle == n+2) { mProf_PostSweep() } @@ -481,7 +488,7 @@ func gcWaitOnMark(n uint32) { for { // Disable phase transitions. lock(&work.sweepWaiters.lock) - nMarks := atomic.Load(&work.cycles) + nMarks := work.cycles.Load() if gcphase != _GCmark { // We've already completed this cycle's mark. nMarks++ @@ -539,7 +546,7 @@ const ( // that the exit condition for the _GCoff phase has been met. The exit // condition should be tested when allocating. func (t gcTrigger) test() bool { - if !memstats.enablegc || panicking != 0 || gcphase != _GCoff { + if !memstats.enablegc || panicking.Load() != 0 || gcphase != _GCoff { return false } switch t.kind { @@ -548,16 +555,17 @@ func (t gcTrigger) test() bool { // we are going to trigger on this, this thread just // atomically wrote gcController.heapLive anyway and we'll see our // own write. - return gcController.heapLive >= gcController.trigger + trigger, _ := gcController.trigger() + return gcController.heapLive.Load() >= trigger case gcTriggerTime: - if gcController.gcPercent < 0 { + if gcController.gcPercent.Load() < 0 { return false } lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime)) return lastgc != 0 && t.now-lastgc > forcegcperiod case gcTriggerCycle: // t.n > work.cycles, but accounting for wraparound. - return int32(t.n-work.cycles) > 0 + return int32(t.n-work.cycles.Load()) > 0 } return true } @@ -628,7 +636,7 @@ func gcStart(trigger gcTrigger) { // Check that all Ps have finished deferred mcache flushes. for _, p := range allp { - if fg := atomic.Load(&p.mcache.flushGen); fg != mheap_.sweepgen { + if fg := p.mcache.flushGen.Load(); fg != mheap_.sweepgen { println("runtime: p", p.id, "flushGen", fg, "!= sweepgen", mheap_.sweepgen) throw("p mcache not flushed") } @@ -644,7 +652,7 @@ func gcStart(trigger gcTrigger) { // so it can't be more than ncpu, even if GOMAXPROCS is. work.stwprocs = ncpu } - work.heap0 = atomic.Load64(&gcController.heapLive) + work.heap0 = gcController.heapLive.Load() work.pauseNS = 0 work.mode = mode @@ -664,10 +672,14 @@ func gcStart(trigger gcTrigger) { // reclaimed until the next GC cycle. clearpools() - work.cycles++ + work.cycles.Add(1) - gcController.startCycle() - work.heapGoal = gcController.heapGoal + // Assists and workers can start the moment we start + // the world. + gcController.startCycle(now, int(gomaxprocs), trigger) + + // Notify the CPU limiter that assists may begin. + gcCPULimiter.startGCTransition(true, now) // In STW mode, disable scheduling of user Gs. This may also // disable scheduling of this goroutine, so it may block as @@ -709,10 +721,6 @@ func gcStart(trigger gcTrigger) { // mutators. atomic.Store(&gcBlackenEnabled, 1) - // Assists and workers can start the moment we start - // the world. - gcController.markStartTime = now - // In STW mode, we could block the instant systemstack // returns, so make sure we're not preemptible. mp = acquirem() @@ -723,6 +731,9 @@ func gcStart(trigger gcTrigger) { work.pauseNS += now - work.pauseStart work.tMark = now memstats.gcPauseDist.record(now - work.pauseStart) + + // Release the CPU limiter. + gcCPULimiter.finishGCTransition(now) }) // Release the world sema before Gosched() in STW mode @@ -758,7 +769,7 @@ var gcMarkDoneFlushed uint32 // This should be called when all local mark work has been drained and // there are no remaining workers. Specifically, when // -// work.nwait == work.nproc && !gcMarkWorkAvailable(p) +// work.nwait == work.nproc && !gcMarkWorkAvailable(p) // // The calling context must be preemptible. // @@ -800,21 +811,21 @@ top: // result in a deadlock as we attempt to preempt a worker that's // trying to preempt us (e.g. for a stack scan). casgstatus(gp, _Grunning, _Gwaiting) - forEachP(func(_p_ *p) { + forEachP(func(pp *p) { // Flush the write barrier buffer, since this may add // work to the gcWork. - wbBufFlush1(_p_) + wbBufFlush1(pp) // Flush the gcWork, since this may create global work // and set the flushedWork flag. // // TODO(austin): Break up these workbufs to // better distribute work. - _p_.gcw.dispose() + pp.gcw.dispose() // Collect the flushedWork flag. - if _p_.gcw.flushedWork { + if pp.gcw.flushedWork { atomic.Xadd(&gcMarkDoneFlushed, 1) - _p_.gcw.flushedWork = false + pp.gcw.flushedWork = false } }) casgstatus(gp, _Gwaiting, _Grunning) @@ -876,10 +887,15 @@ top: goto top } + gcComputeStartingStackSize() + // Disable assists and background workers. We must do // this before waking blocked assists. atomic.Store(&gcBlackenEnabled, 0) + // Notify the CPU limiter that GC assists will now cease. + gcCPULimiter.startGCTransition(false, now) + // Wake all blocked assists. These will run when we // start the world again. gcWakeAllAssists() @@ -896,28 +912,27 @@ top: // endCycle depends on all gcWork cache stats being flushed. // The termination algorithm above ensured that up to // allocations since the ragged barrier. - nextTriggerRatio := gcController.endCycle(work.userForced) + gcController.endCycle(now, int(gomaxprocs), work.userForced) // Perform mark termination. This will restart the world. - gcMarkTermination(nextTriggerRatio) + gcMarkTermination() } // World must be stopped and mark assists and background workers must be // disabled. -func gcMarkTermination(nextTriggerRatio float64) { +func gcMarkTermination() { // Start marktermination (write barrier remains enabled for now). setGCPhase(_GCmarktermination) - work.heap1 = gcController.heapLive + work.heap1 = gcController.heapLive.Load() startTime := nanotime() mp := acquirem() mp.preemptoff = "gcing" - _g_ := getg() - _g_.m.traceback = 2 - gp := _g_.m.curg - casgstatus(gp, _Grunning, _Gwaiting) - gp.waitreason = waitReasonGarbageCollection + mp.traceback = 2 + curgp := mp.curg + casgstatus(curgp, _Grunning, _Gwaiting) + curgp.waitreason = waitReasonGarbageCollection // Run gc on the g0 stack. We do this so that the g stack // we're currently running on will no longer change. Cuts @@ -956,8 +971,8 @@ func gcMarkTermination(nextTriggerRatio float64) { gcSweep(work.mode) }) - _g_.m.traceback = 0 - casgstatus(gp, _Gwaiting, _Grunning) + mp.traceback = 0 + casgstatus(curgp, _Gwaiting, _Grunning) if trace.enabled { traceGCDone() @@ -970,12 +985,12 @@ func gcMarkTermination(nextTriggerRatio float64) { throw("gc done but gcphase != _GCoff") } - // Record heapGoal and heap_inuse for scavenger. - gcController.lastHeapGoal = gcController.heapGoal - memstats.last_heap_inuse = memstats.heap_inuse + // Record heapInUse for scavenger. + memstats.lastHeapInUse = gcController.heapInUse.load() - // Update GC trigger and pacing for the next cycle. - gcController.commit(nextTriggerRatio) + // Update GC trigger and pacing, as well as downstream consumers + // of this pacing information, for the next cycle. + systemstack(gcControllerCommit) // Update timing memstats now := nanotime() @@ -994,7 +1009,7 @@ func gcMarkTermination(nextTriggerRatio float64) { sweepTermCpu := int64(work.stwprocs) * (work.tMark - work.tSweepTerm) // We report idle marking time below, but omit it from the // overall utilization here since it's "free". - markCpu := gcController.assistTime + gcController.dedicatedMarkTime + gcController.fractionalMarkTime + markCpu := gcController.assistTime.Load() + gcController.dedicatedMarkTime.Load() + gcController.fractionalMarkTime.Load() markTermCpu := int64(work.stwprocs) * (work.tEnd - work.tMarkTerm) cycleCpu := sweepTermCpu + markCpu + markTermCpu work.totaltime += cycleCpu @@ -1003,6 +1018,12 @@ func gcMarkTermination(nextTriggerRatio float64) { totalCpu := sched.totaltime + (now-sched.procresizetime)*int64(gomaxprocs) memstats.gc_cpu_fraction = float64(work.totaltime) / float64(totalCpu) + // Reset assist time stat. + // + // Do this now, instead of at the start of the next GC cycle, because + // these two may keep accumulating even if the GC is not active. + mheap_.pages.scav.assistTime.Store(0) + // Reset sweep state. sweep.nbgsweep = 0 sweep.npausesweep = 0 @@ -1017,6 +1038,9 @@ func gcMarkTermination(nextTriggerRatio float64) { injectglist(&work.sweepWaiters.list) unlock(&work.sweepWaiters.lock) + // Release the CPU limiter. + gcCPULimiter.finishGCTransition(now) + // Finish the current heap profiling cycle and start a new // heap profiling cycle. We do this before starting the world // so events don't leak into the wrong cycle. @@ -1026,8 +1050,10 @@ func gcMarkTermination(nextTriggerRatio float64) { // Those aren't tracked in any sweep lists, so we need to // count them against sweep completion until we ensure all // those spans have been forced out. - sl := newSweepLocker() - sl.blockCompletion() + sl := sweep.active.begin() + if !sl.valid { + throw("failed to set sweep barrier") + } systemstack(func() { startTheWorldWithSema(true) }) @@ -1048,13 +1074,13 @@ func gcMarkTermination(nextTriggerRatio float64) { // is necessary to sweep all spans, we need to ensure all // mcaches are flushed before we start the next GC cycle. systemstack(func() { - forEachP(func(_p_ *p) { - _p_.mcache.prepareForSweep() + forEachP(func(pp *p) { + pp.mcache.prepareForSweep() }) }) // Now that we've swept stale spans in mcaches, they don't // count against unswept spans. - sl.dispose() + sweep.active.end(sl) // Print gctrace before dropping worldsema. As soon as we drop // worldsema another cycle could start and smash the stats @@ -1076,7 +1102,13 @@ func gcMarkTermination(nextTriggerRatio float64) { prev = ns } print(" ms clock, ") - for i, ns := range []int64{sweepTermCpu, gcController.assistTime, gcController.dedicatedMarkTime + gcController.fractionalMarkTime, gcController.idleMarkTime, markTermCpu} { + for i, ns := range []int64{ + sweepTermCpu, + gcController.assistTime.Load(), + gcController.dedicatedMarkTime.Load() + gcController.fractionalMarkTime.Load(), + gcController.idleMarkTime.Load(), + markTermCpu, + } { if i == 2 || i == 3 { // Separate mark time components with /. print("/") @@ -1087,7 +1119,9 @@ func gcMarkTermination(nextTriggerRatio float64) { } print(" ms cpu, ", work.heap0>>20, "->", work.heap1>>20, "->", work.heap2>>20, " MB, ", - work.heapGoal>>20, " MB goal, ", + gcController.lastHeapGoal>>20, " MB goal, ", + gcController.lastStackScan.Load()>>20, " MB stacks, ", + gcController.globalsScan.Load()>>20, " MB globals, ", work.maxprocs, " P") if work.userForced { print(" (forced)") @@ -1246,6 +1280,10 @@ func gcBgMarkWorker() { startTime := nanotime() pp.gcMarkWorkerStartTime = startTime + var trackLimiterEvent bool + if pp.gcMarkWorkerMode == gcMarkWorkerIdleMode { + trackLimiterEvent = pp.limiterEvent.start(limiterEventIdleMarkWork, startTime) + } decnwait := atomic.Xadd(&work.nwait, -1) if decnwait == work.nproc { @@ -1290,17 +1328,15 @@ func gcBgMarkWorker() { casgstatus(gp, _Gwaiting, _Grunning) }) - // Account for time. - duration := nanotime() - startTime - switch pp.gcMarkWorkerMode { - case gcMarkWorkerDedicatedMode: - atomic.Xaddint64(&gcController.dedicatedMarkTime, duration) - atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 1) - case gcMarkWorkerFractionalMode: - atomic.Xaddint64(&gcController.fractionalMarkTime, duration) + // Account for time and mark us as stopped. + now := nanotime() + duration := now - startTime + gcController.markWorkerStop(pp.gcMarkWorkerMode, duration) + if trackLimiterEvent { + pp.limiterEvent.stop(limiterEventIdleMarkWork, now) + } + if pp.gcMarkWorkerMode == gcMarkWorkerFractionalMode { atomic.Xaddint64(&pp.gcFractionalMarkTime, duration) - case gcMarkWorkerIdleMode: - atomic.Xaddint64(&gcController.idleMarkTime, duration) } // Was this the last worker and did we run out @@ -1322,7 +1358,7 @@ func gcBgMarkWorker() { // point, signal the main GC goroutine. if incnwait == work.nproc && !gcMarkWorkAvailable(nil) { // We don't need the P-local buffers here, allow - // preemption becuse we may schedule like a regular + // preemption because we may schedule like a regular // goroutine in gcMarkDone (block on locks, etc). releasem(node.m.ptr()) node.m.set(nil) @@ -1376,6 +1412,11 @@ func gcMark(startTime int64) { throw("work.full != 0") } + // Drop allg snapshot. allgs may have grown, in which case + // this is the only reference to the old backing store and + // there's no need to keep it around. + work.stackRoots = nil + // Clear out buffers and double-check that all gcWork caches // are empty. This should be ensured by gcMarkDone before we // enter mark termination. @@ -1420,30 +1461,22 @@ func gcMark(startTime int64) { gcw.dispose() } - // Update the marked heap stat. - gcController.heapMarked = work.bytesMarked - // Flush scanAlloc from each mcache since we're about to modify // heapScan directly. If we were to flush this later, then scanAlloc // might have incorrect information. + // + // Note that it's not important to retain this information; we know + // exactly what heapScan is at this point via scanWork. for _, p := range allp { c := p.mcache if c == nil { continue } - gcController.heapScan += uint64(c.scanAlloc) c.scanAlloc = 0 } - // Update other GC heap size stats. This must happen after - // cachestats (which flushes local statistics to these) and - // flushallmcaches (which modifies gcController.heapLive). - gcController.heapLive = work.bytesMarked - gcController.heapScan = uint64(gcController.scanWork) - - if trace.enabled { - traceHeapAlloc() - } + // Reset controller state. + gcController.resetLive(work.bytesMarked) } // gcSweep must be called on the system stack because it acquires the heap @@ -1461,11 +1494,11 @@ func gcSweep(mode gcMode) { lock(&mheap_.lock) mheap_.sweepgen += 2 - mheap_.sweepDrained = 0 - mheap_.pagesSwept = 0 + sweep.active.reset() + mheap_.pagesSwept.Store(0) mheap_.sweepArenas = mheap_.allArenas - mheap_.reclaimIndex = 0 - mheap_.reclaimCredit = 0 + mheap_.reclaimIndex.Store(0) + mheap_.reclaimCredit.Store(0) unlock(&mheap_.lock) sweep.centralIndex.clear() @@ -1532,24 +1565,35 @@ func gcResetMarkState() { } work.bytesMarked = 0 - work.initialHeapLive = atomic.Load64(&gcController.heapLive) + work.initialHeapLive = gcController.heapLive.Load() } // Hooks for other packages var poolcleanup func() +var boringCaches []unsafe.Pointer // for crypto/internal/boring //go:linkname sync_runtime_registerPoolCleanup sync.runtime_registerPoolCleanup func sync_runtime_registerPoolCleanup(f func()) { poolcleanup = f } +//go:linkname boring_registerCache crypto/internal/boring/bcache.registerCache +func boring_registerCache(p unsafe.Pointer) { + boringCaches = append(boringCaches, p) +} + func clearpools() { // clear sync.Pools if poolcleanup != nil { poolcleanup() } + // clear boringcrypto caches + for _, p := range boringCaches { + atomicstorep(p, nil) + } + // Clear central sudog cache. // Leave per-P caches alone, they have strictly bounded size. // Disconnect cached list before dropping it on the floor, @@ -1563,19 +1607,17 @@ func clearpools() { sched.sudogcache = nil unlock(&sched.sudoglock) - // Clear central defer pools. + // Clear central defer pool. // Leave per-P pools alone, they have strictly bounded size. lock(&sched.deferlock) - for i := range sched.deferpool { - // disconnect cached list before dropping it on the floor, - // so that a dangling ref to one entry does not pin all of them. - var d, dlink *_defer - for d = sched.deferpool[i]; d != nil; d = dlink { - dlink = d.link - d.link = nil - } - sched.deferpool[i] = nil + // disconnect cached list before dropping it on the floor, + // so that a dangling ref to one entry does not pin all of them. + var d, dlink *_defer + for d = sched.deferpool; d != nil; d = dlink { + dlink = d.link + d.link = nil } + sched.deferpool = nil unlock(&sched.deferlock) } diff --git a/src/runtime/mgclimit.go b/src/runtime/mgclimit.go new file mode 100644 index 00000000000000..b6fbcb14cf92df --- /dev/null +++ b/src/runtime/mgclimit.go @@ -0,0 +1,482 @@ +// Copyright 2022 The Go Authors. 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" + +// gcCPULimiter is a mechanism to limit GC CPU utilization in situations +// where it might become excessive and inhibit application progress (e.g. +// a death spiral). +// +// The core of the limiter is a leaky bucket mechanism that fills with GC +// CPU time and drains with mutator time. Because the bucket fills and +// drains with time directly (i.e. without any weighting), this effectively +// sets a very conservative limit of 50%. This limit could be enforced directly, +// however, but the purpose of the bucket is to accommodate spikes in GC CPU +// utilization without hurting throughput. +// +// Note that the bucket in the leaky bucket mechanism can never go negative, +// so the GC never gets credit for a lot of CPU time spent without the GC +// running. This is intentional, as an application that stays idle for, say, +// an entire day, could build up enough credit to fail to prevent a death +// spiral the following day. The bucket's capacity is the GC's only leeway. +// +// The capacity thus also sets the window the limiter considers. For example, +// if the capacity of the bucket is 1 cpu-second, then the limiter will not +// kick in until at least 1 full cpu-second in the last 2 cpu-second window +// is spent on GC CPU time. +var gcCPULimiter gcCPULimiterState + +type gcCPULimiterState struct { + lock atomic.Uint32 + + enabled atomic.Bool + bucket struct { + // Invariants: + // - fill >= 0 + // - capacity >= 0 + // - fill <= capacity + fill, capacity uint64 + } + // overflow is the cumulative amount of GC CPU time that we tried to fill the + // bucket with but exceeded its capacity. + overflow uint64 + + // gcEnabled is an internal copy of gcBlackenEnabled that determines + // whether the limiter tracks total assist time. + // + // gcBlackenEnabled isn't used directly so as to keep this structure + // unit-testable. + gcEnabled bool + + // transitioning is true when the GC is in a STW and transitioning between + // the mark and sweep phases. + transitioning bool + + // assistTimePool is the accumulated assist time since the last update. + assistTimePool atomic.Int64 + + // idleMarkTimePool is the accumulated idle mark time since the last update. + idleMarkTimePool atomic.Int64 + + // idleTimePool is the accumulated time Ps spent on the idle list since the last update. + idleTimePool atomic.Int64 + + // lastUpdate is the nanotime timestamp of the last time update was called. + // + // Updated under lock, but may be read concurrently. + lastUpdate atomic.Int64 + + // lastEnabledCycle is the GC cycle that last had the limiter enabled. + lastEnabledCycle atomic.Uint32 + + // nprocs is an internal copy of gomaxprocs, used to determine total available + // CPU time. + // + // gomaxprocs isn't used directly so as to keep this structure unit-testable. + nprocs int32 + + // test indicates whether this instance of the struct was made for testing purposes. + test bool +} + +// limiting returns true if the CPU limiter is currently enabled, meaning the Go GC +// should take action to limit CPU utilization. +// +// It is safe to call concurrently with other operations. +func (l *gcCPULimiterState) limiting() bool { + return l.enabled.Load() +} + +// startGCTransition notifies the limiter of a GC transition. +// +// This call takes ownership of the limiter and disables all other means of +// updating the limiter. Release ownership by calling finishGCTransition. +// +// It is safe to call concurrently with other operations. +func (l *gcCPULimiterState) startGCTransition(enableGC bool, now int64) { + if !l.tryLock() { + // This must happen during a STW, so we can't fail to acquire the lock. + // If we did, something went wrong. Throw. + throw("failed to acquire lock to start a GC transition") + } + if l.gcEnabled == enableGC { + throw("transitioning GC to the same state as before?") + } + // Flush whatever was left between the last update and now. + l.updateLocked(now) + l.gcEnabled = enableGC + l.transitioning = true + // N.B. finishGCTransition releases the lock. + // + // We don't release here to increase the chance that if there's a failure + // to finish the transition, that we throw on failing to acquire the lock. +} + +// finishGCTransition notifies the limiter that the GC transition is complete +// and releases ownership of it. It also accumulates STW time in the bucket. +// now must be the timestamp from the end of the STW pause. +func (l *gcCPULimiterState) finishGCTransition(now int64) { + if !l.transitioning { + throw("finishGCTransition called without starting one?") + } + // Count the full nprocs set of CPU time because the world is stopped + // between startGCTransition and finishGCTransition. Even though the GC + // isn't running on all CPUs, it is preventing user code from doing so, + // so it might as well be. + if lastUpdate := l.lastUpdate.Load(); now >= lastUpdate { + l.accumulate(0, (now-lastUpdate)*int64(l.nprocs)) + } + l.lastUpdate.Store(now) + l.transitioning = false + l.unlock() +} + +// gcCPULimiterUpdatePeriod dictates the maximum amount of wall-clock time +// we can go before updating the limiter. +const gcCPULimiterUpdatePeriod = 10e6 // 10ms + +// needUpdate returns true if the limiter's maximum update period has been +// exceeded, and so would benefit from an update. +func (l *gcCPULimiterState) needUpdate(now int64) bool { + return now-l.lastUpdate.Load() > gcCPULimiterUpdatePeriod +} + +// addAssistTime notifies the limiter of additional assist time. It will be +// included in the next update. +func (l *gcCPULimiterState) addAssistTime(t int64) { + l.assistTimePool.Add(t) +} + +// addIdleTime notifies the limiter of additional time a P spent on the idle list. It will be +// subtracted from the total CPU time in the next update. +func (l *gcCPULimiterState) addIdleTime(t int64) { + l.idleTimePool.Add(t) +} + +// update updates the bucket given runtime-specific information. now is the +// current monotonic time in nanoseconds. +// +// This is safe to call concurrently with other operations, except *GCTransition. +func (l *gcCPULimiterState) update(now int64) { + if !l.tryLock() { + // We failed to acquire the lock, which means something else is currently + // updating. Just drop our update, the next one to update will include + // our total assist time. + return + } + if l.transitioning { + throw("update during transition") + } + l.updateLocked(now) + l.unlock() +} + +// updatedLocked is the implementation of update. l.lock must be held. +func (l *gcCPULimiterState) updateLocked(now int64) { + lastUpdate := l.lastUpdate.Load() + if now < lastUpdate { + // Defensively avoid overflow. This isn't even the latest update anyway. + return + } + windowTotalTime := (now - lastUpdate) * int64(l.nprocs) + l.lastUpdate.Store(now) + + // Drain the pool of assist time. + assistTime := l.assistTimePool.Load() + if assistTime != 0 { + l.assistTimePool.Add(-assistTime) + } + + // Drain the pool of idle time. + idleTime := l.idleTimePool.Load() + if idleTime != 0 { + l.idleTimePool.Add(-idleTime) + } + + if !l.test { + // Consume time from in-flight events. Make sure we're not preemptible so allp can't change. + // + // The reason we do this instead of just waiting for those events to finish and push updates + // is to ensure that all the time we're accounting for happened sometime between lastUpdate + // and now. This dramatically simplifies reasoning about the limiter because we're not at + // risk of extra time being accounted for in this window than actually happened in this window, + // leading to all sorts of weird transient behavior. + mp := acquirem() + for _, pp := range allp { + typ, duration := pp.limiterEvent.consume(now) + switch typ { + case limiterEventIdleMarkWork: + fallthrough + case limiterEventIdle: + idleTime += duration + case limiterEventMarkAssist: + fallthrough + case limiterEventScavengeAssist: + assistTime += duration + case limiterEventNone: + break + default: + throw("invalid limiter event type found") + } + } + releasem(mp) + } + + // Compute total GC time. + windowGCTime := assistTime + if l.gcEnabled { + windowGCTime += int64(float64(windowTotalTime) * gcBackgroundUtilization) + } + + // Subtract out all idle time from the total time. Do this after computing + // GC time, because the background utilization is dependent on the *real* + // total time, not the total time after idle time is subtracted. + // + // Idle time is counted as any time that a P is on the P idle list plus idle mark + // time. Idle mark workers soak up time that the application spends idle. + // + // On a heavily undersubscribed system, any additional idle time can skew GC CPU + // utilization, because the GC might be executing continuously and thrashing, + // yet the CPU utilization with respect to GOMAXPROCS will be quite low, so + // the limiter fails to turn on. By subtracting idle time, we're removing time that + // we know the application was idle giving a more accurate picture of whether + // the GC is thrashing. + // + // Note that this can cause the limiter to turn on even if it's not needed. For + // instance, on a system with 32 Ps but only 1 running goroutine, each GC will have + // 8 dedicated GC workers. Assuming the GC cycle is half mark phase and half sweep + // phase, then the GC CPU utilization over that cycle, with idle time removed, will + // be 8/(8+2) = 80%. Even though the limiter turns on, though, assist should be + // unnecessary, as the GC has way more CPU time to outpace the 1 goroutine that's + // running. + windowTotalTime -= idleTime + + l.accumulate(windowTotalTime-windowGCTime, windowGCTime) +} + +// accumulate adds time to the bucket and signals whether the limiter is enabled. +// +// This is an internal function that deals just with the bucket. Prefer update. +// l.lock must be held. +func (l *gcCPULimiterState) accumulate(mutatorTime, gcTime int64) { + headroom := l.bucket.capacity - l.bucket.fill + enabled := headroom == 0 + + // Let's be careful about three things here: + // 1. The addition and subtraction, for the invariants. + // 2. Overflow. + // 3. Excessive mutation of l.enabled, which is accessed + // by all assists, potentially more than once. + change := gcTime - mutatorTime + + // Handle limiting case. + if change > 0 && headroom <= uint64(change) { + l.overflow += uint64(change) - headroom + l.bucket.fill = l.bucket.capacity + if !enabled { + l.enabled.Store(true) + l.lastEnabledCycle.Store(memstats.numgc + 1) + } + return + } + + // Handle non-limiting cases. + if change < 0 && l.bucket.fill <= uint64(-change) { + // Bucket emptied. + l.bucket.fill = 0 + } else { + // All other cases. + l.bucket.fill -= uint64(-change) + } + if change != 0 && enabled { + l.enabled.Store(false) + } +} + +// tryLock attempts to lock l. Returns true on success. +func (l *gcCPULimiterState) tryLock() bool { + return l.lock.CompareAndSwap(0, 1) +} + +// unlock releases the lock on l. Must be called if tryLock returns true. +func (l *gcCPULimiterState) unlock() { + old := l.lock.Swap(0) + if old != 1 { + throw("double unlock") + } +} + +// capacityPerProc is the limiter's bucket capacity for each P in GOMAXPROCS. +const capacityPerProc = 1e9 // 1 second in nanoseconds + +// resetCapacity updates the capacity based on GOMAXPROCS. Must not be called +// while the GC is enabled. +// +// It is safe to call concurrently with other operations. +func (l *gcCPULimiterState) resetCapacity(now int64, nprocs int32) { + if !l.tryLock() { + // This must happen during a STW, so we can't fail to acquire the lock. + // If we did, something went wrong. Throw. + throw("failed to acquire lock to reset capacity") + } + // Flush the rest of the time for this period. + l.updateLocked(now) + l.nprocs = nprocs + + l.bucket.capacity = uint64(nprocs) * capacityPerProc + if l.bucket.fill > l.bucket.capacity { + l.bucket.fill = l.bucket.capacity + l.enabled.Store(true) + l.lastEnabledCycle.Store(memstats.numgc + 1) + } else if l.bucket.fill < l.bucket.capacity { + l.enabled.Store(false) + } + l.unlock() +} + +// limiterEventType indicates the type of an event occurring on some P. +// +// These events represent the full set of events that the GC CPU limiter tracks +// to execute its function. +// +// This type may use no more than limiterEventBits bits of information. +type limiterEventType uint8 + +const ( + limiterEventNone limiterEventType = iota // None of the following events. + limiterEventIdleMarkWork // Refers to an idle mark worker (see gcMarkWorkerMode). + limiterEventMarkAssist // Refers to mark assist (see gcAssistAlloc). + limiterEventScavengeAssist // Refers to a scavenge assist (see allocSpan). + limiterEventIdle // Refers to time a P spent on the idle list. + + limiterEventBits = 3 +) + +// limiterEventTypeMask is a mask for the bits in p.limiterEventStart that represent +// the event type. The rest of the bits of that field represent a timestamp. +const ( + limiterEventTypeMask = uint64((1<> (64 - limiterEventBits)) +} + +// limiterEvent represents tracking state for an event tracked by the GC CPU limiter. +type limiterEvent struct { + stamp atomic.Uint64 // Stores a limiterEventStamp. +} + +// start begins tracking a new limiter event of the current type. If an event +// is already in flight, then a new event cannot begin because the current time is +// already being attributed to that event. In this case, this function returns false. +// Otherwise, it returns true. +// +// The caller must be non-preemptible until at least stop is called or this function +// returns false. Because this is trying to measure "on-CPU" time of some event, getting +// scheduled away during it can mean that whatever we're measuring isn't a reflection +// of "on-CPU" time. The OS could deschedule us at any time, but we want to maintain as +// close of an approximation as we can. +func (e *limiterEvent) start(typ limiterEventType, now int64) bool { + if limiterEventStamp(e.stamp.Load()).typ() != limiterEventNone { + return false + } + e.stamp.Store(uint64(makeLimiterEventStamp(typ, now))) + return true +} + +// consume acquires the partial event CPU time from any in-flight event. +// It achieves this by storing the current time as the new event time. +// +// Returns the type of the in-flight event, as well as how long it's currently been +// executing for. Returns limiterEventNone if no event is active. +func (e *limiterEvent) consume(now int64) (typ limiterEventType, duration int64) { + // Read the limiter event timestamp and update it to now. + for { + old := limiterEventStamp(e.stamp.Load()) + typ = old.typ() + if typ == limiterEventNone { + // There's no in-flight event, so just push that up. + return + } + duration = old.duration(now) + if duration == 0 { + // We might have a stale now value, or this crossed the + // 2^(64-limiterEventBits) boundary in the clock readings. + // Just ignore it. + return limiterEventNone, 0 + } + new := makeLimiterEventStamp(typ, now) + if e.stamp.CompareAndSwap(uint64(old), uint64(new)) { + break + } + } + return +} + +// stop stops the active limiter event. Throws if the +// +// The caller must be non-preemptible across the event. See start as to why. +func (e *limiterEvent) stop(typ limiterEventType, now int64) { + var stamp limiterEventStamp + for { + stamp = limiterEventStamp(e.stamp.Load()) + if stamp.typ() != typ { + print("runtime: want=", typ, " got=", stamp.typ(), "\n") + throw("limiterEvent.stop: found wrong event in p's limiter event slot") + } + if e.stamp.CompareAndSwap(uint64(stamp), uint64(limiterEventStampNone)) { + break + } + } + duration := stamp.duration(now) + if duration == 0 { + // It's possible that we're missing time because we crossed a + // 2^(64-limiterEventBits) boundary between the start and end. + // In this case, we're dropping that information. This is OK because + // at worst it'll cause a transient hiccup that will quickly resolve + // itself as all new timestamps begin on the other side of the boundary. + // Such a hiccup should be incredibly rare. + return + } + // Account for the event. + switch typ { + case limiterEventIdleMarkWork: + fallthrough + case limiterEventIdle: + gcCPULimiter.addIdleTime(duration) + case limiterEventMarkAssist: + fallthrough + case limiterEventScavengeAssist: + gcCPULimiter.addAssistTime(duration) + default: + throw("limiterEvent.stop: invalid limiter event type found") + } +} diff --git a/src/runtime/mgclimit_test.go b/src/runtime/mgclimit_test.go new file mode 100644 index 00000000000000..124da03ef1d9f3 --- /dev/null +++ b/src/runtime/mgclimit_test.go @@ -0,0 +1,255 @@ +// Copyright 2022 The Go Authors. 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 ( + . "runtime" + "testing" + "time" +) + +func TestGCCPULimiter(t *testing.T) { + const procs = 14 + + // Create mock time. + ticks := int64(0) + advance := func(d time.Duration) int64 { + t.Helper() + ticks += int64(d) + return ticks + } + + // assistTime computes the CPU time for assists using frac of GOMAXPROCS + // over the wall-clock duration d. + assistTime := func(d time.Duration, frac float64) int64 { + t.Helper() + return int64(frac * float64(d) * procs) + } + + l := NewGCCPULimiter(ticks, procs) + + // Do the whole test twice to make sure state doesn't leak across. + var baseOverflow uint64 // Track total overflow across iterations. + for i := 0; i < 2; i++ { + t.Logf("Iteration %d", i+1) + + if l.Capacity() != procs*CapacityPerProc { + t.Fatalf("unexpected capacity: %d", l.Capacity()) + } + if l.Fill() != 0 { + t.Fatalf("expected empty bucket to start") + } + + // Test filling the bucket with just mutator time. + + l.Update(advance(10 * time.Millisecond)) + l.Update(advance(1 * time.Second)) + l.Update(advance(1 * time.Hour)) + if l.Fill() != 0 { + t.Fatalf("expected empty bucket from only accumulating mutator time, got fill of %d cpu-ns", l.Fill()) + } + + // Test needUpdate. + + if l.NeedUpdate(advance(GCCPULimiterUpdatePeriod / 2)) { + t.Fatal("need update even though updated half a period ago") + } + if !l.NeedUpdate(advance(GCCPULimiterUpdatePeriod)) { + t.Fatal("doesn't need update even though updated 1.5 periods ago") + } + l.Update(advance(0)) + if l.NeedUpdate(advance(0)) { + t.Fatal("need update even though just updated") + } + + // Test transitioning the bucket to enable the GC. + + l.StartGCTransition(true, advance(109*time.Millisecond)) + l.FinishGCTransition(advance(2*time.Millisecond + 1*time.Microsecond)) + + if expect := uint64((2*time.Millisecond + 1*time.Microsecond) * procs); l.Fill() != expect { + t.Fatalf("expected fill of %d, got %d cpu-ns", expect, l.Fill()) + } + + // Test passing time without assists during a GC. Specifically, just enough to drain the bucket to + // exactly procs nanoseconds (easier to get to because of rounding). + // + // The window we need to drain the bucket is 1/(1-2*gcBackgroundUtilization) times the current fill: + // + // fill + (window * procs * gcBackgroundUtilization - window * procs * (1-gcBackgroundUtilization)) = n + // fill = n - (window * procs * gcBackgroundUtilization - window * procs * (1-gcBackgroundUtilization)) + // fill = n + window * procs * ((1-gcBackgroundUtilization) - gcBackgroundUtilization) + // fill = n + window * procs * (1-2*gcBackgroundUtilization) + // window = (fill - n) / (procs * (1-2*gcBackgroundUtilization))) + // + // And here we want n=procs: + factor := (1 / (1 - 2*GCBackgroundUtilization)) + fill := (2*time.Millisecond + 1*time.Microsecond) * procs + l.Update(advance(time.Duration(factor * float64(fill-procs) / procs))) + if l.Fill() != procs { + t.Fatalf("expected fill %d cpu-ns from draining after a GC started, got fill of %d cpu-ns", procs, l.Fill()) + } + + // Drain to zero for the rest of the test. + l.Update(advance(2 * procs * CapacityPerProc)) + if l.Fill() != 0 { + t.Fatalf("expected empty bucket from draining, got fill of %d cpu-ns", l.Fill()) + } + + // Test filling up the bucket with 50% total GC work (so, not moving the bucket at all). + l.AddAssistTime(assistTime(10*time.Millisecond, 0.5-GCBackgroundUtilization)) + l.Update(advance(10 * time.Millisecond)) + if l.Fill() != 0 { + t.Fatalf("expected empty bucket from 50%% GC work, got fill of %d cpu-ns", l.Fill()) + } + + // Test adding to the bucket overall with 100% GC work. + l.AddAssistTime(assistTime(time.Millisecond, 1.0-GCBackgroundUtilization)) + l.Update(advance(time.Millisecond)) + if expect := uint64(procs * time.Millisecond); l.Fill() != expect { + t.Errorf("expected %d fill from 100%% GC CPU, got fill of %d cpu-ns", expect, l.Fill()) + } + if l.Limiting() { + t.Errorf("limiter is enabled after filling bucket but shouldn't be") + } + if t.Failed() { + t.FailNow() + } + + // Test filling the bucket exactly full. + l.AddAssistTime(assistTime(CapacityPerProc-time.Millisecond, 1.0-GCBackgroundUtilization)) + l.Update(advance(CapacityPerProc - time.Millisecond)) + if l.Fill() != l.Capacity() { + t.Errorf("expected bucket filled to capacity %d, got %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is not enabled after filling bucket but should be") + } + if l.Overflow() != 0+baseOverflow { + t.Errorf("bucket filled exactly should not have overflow, found %d", l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Test adding with a delta of exactly zero. That is, GC work is exactly 50% of all resources. + // Specifically, the limiter should still be on, and no overflow should accumulate. + l.AddAssistTime(assistTime(1*time.Second, 0.5-GCBackgroundUtilization)) + l.Update(advance(1 * time.Second)) + if l.Fill() != l.Capacity() { + t.Errorf("expected bucket filled to capacity %d, got %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is not enabled after filling bucket but should be") + } + if l.Overflow() != 0+baseOverflow { + t.Errorf("bucket filled exactly should not have overflow, found %d", l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Drain the bucket by half. + l.AddAssistTime(assistTime(CapacityPerProc, 0)) + l.Update(advance(CapacityPerProc)) + if expect := l.Capacity() / 2; l.Fill() != expect { + t.Errorf("failed to drain to %d, got fill %d", expect, l.Fill()) + } + if l.Limiting() { + t.Errorf("limiter is enabled after draining bucket but shouldn't be") + } + if t.Failed() { + t.FailNow() + } + + // Test overfilling the bucket. + l.AddAssistTime(assistTime(CapacityPerProc, 1.0-GCBackgroundUtilization)) + l.Update(advance(CapacityPerProc)) + if l.Fill() != l.Capacity() { + t.Errorf("failed to fill to capacity %d, got fill %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is not enabled after overfill but should be") + } + if expect := uint64(CapacityPerProc * procs / 2); l.Overflow() != expect+baseOverflow { + t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Test ending the cycle with some assists left over. + l.AddAssistTime(assistTime(1*time.Millisecond, 1.0-GCBackgroundUtilization)) + l.StartGCTransition(false, advance(1*time.Millisecond)) + if l.Fill() != l.Capacity() { + t.Errorf("failed to maintain fill to capacity %d, got fill %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is not enabled after overfill but should be") + } + if expect := uint64((CapacityPerProc/2 + time.Millisecond) * procs); l.Overflow() != expect+baseOverflow { + t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Make sure the STW adds to the bucket. + l.FinishGCTransition(advance(5 * time.Millisecond)) + if l.Fill() != l.Capacity() { + t.Errorf("failed to maintain fill to capacity %d, got fill %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is not enabled after overfill but should be") + } + if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow { + t.Errorf("bucket overfilled should have overflow %d, found %d", expect, l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Resize procs up and make sure limiting stops. + expectFill := l.Capacity() + l.ResetCapacity(advance(0), procs+10) + if l.Fill() != expectFill { + t.Errorf("failed to maintain fill at old capacity %d, got fill %d", expectFill, l.Fill()) + } + if l.Limiting() { + t.Errorf("limiter is enabled after resetting capacity higher") + } + if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow { + t.Errorf("bucket overflow %d should have remained constant, found %d", expect, l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Resize procs down and make sure limiting begins again. + // Also make sure resizing doesn't affect overflow. This isn't + // a case where we want to report overflow, because we're not + // actively doing work to achieve it. It's that we have fewer + // CPU resources now. + l.ResetCapacity(advance(0), procs-10) + if l.Fill() != l.Capacity() { + t.Errorf("failed lower fill to new capacity %d, got fill %d", l.Capacity(), l.Fill()) + } + if !l.Limiting() { + t.Errorf("limiter is disabled after resetting capacity lower") + } + if expect := uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs); l.Overflow() != expect+baseOverflow { + t.Errorf("bucket overflow %d should have remained constant, found %d", expect, l.Overflow()) + } + if t.Failed() { + t.FailNow() + } + + // Get back to a zero state. The top of the loop will double check. + l.ResetCapacity(advance(CapacityPerProc*procs), procs) + + // Track total overflow for future iterations. + baseOverflow += uint64((CapacityPerProc/2 + 6*time.Millisecond) * procs) + } +} diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 1fd0732d62dcc6..90240c31594882 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -7,6 +7,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -100,7 +101,8 @@ func gcMarkRootPrepare() { // ignore them because they begin life without any roots, so // there's nothing to scan, and any roots they create during // the concurrent phase will be caught by the write barrier. - work.nStackRoots = int(atomic.Loaduintptr(&allglen)) + work.stackRoots = allGsSnapshot() + work.nStackRoots = len(work.stackRoots) work.markrootNext = 0 work.markrootJobs = uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots) @@ -150,20 +152,28 @@ var oneptrmask = [...]uint8{1} // // Preemption must be disabled (because this uses a gcWork). // +// Returns the amount of GC work credit produced by the operation. +// If flushBgCredit is true, then that credit is also flushed +// to the background credit pool. +// // nowritebarrier is only advisory here. // //go:nowritebarrier -func markroot(gcw *gcWork, i uint32) { +func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 { // Note: if you add a case here, please also update heapdump.go:dumproots. + var workDone int64 + var workCounter *atomic.Int64 switch { case work.baseData <= i && i < work.baseBSS: + workCounter = &gcController.globalsScanWork for _, datap := range activeModules() { - markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-work.baseData)) + workDone += markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-work.baseData)) } case work.baseBSS <= i && i < work.baseSpans: + workCounter = &gcController.globalsScanWork for _, datap := range activeModules() { - markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-work.baseBSS)) + workDone += markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-work.baseBSS)) } case i == fixedRootFinalizers: @@ -183,15 +193,13 @@ func markroot(gcw *gcWork, i uint32) { default: // the rest is scanning goroutine stacks - var gp *g - if work.baseStacks <= i && i < work.baseEnd { - // N.B. Atomic read of allglen in gcMarkRootPrepare - // acts as a barrier to ensure that allgs must be large - // enough to contain all relevant Gs. - gp = allgs[i-work.baseStacks] - } else { + workCounter = &gcController.stackScanWork + if i < work.baseStacks || work.baseEnd <= i { + printlock() + print("runtime: markroot index ", i, " not in stack roots range [", work.baseStacks, ", ", work.baseEnd, ")\n") throw("markroot: bad index") } + gp := work.stackRoots[i-work.baseStacks] // remember when we've first observed the G blocked // needed only to output in traceback @@ -229,7 +237,7 @@ func markroot(gcw *gcWork, i uint32) { if gp.gcscandone { throw("g already scanned") } - scanstack(gp, gcw) + workDone += scanstack(gp, gcw) gp.gcscandone = true resumeG(stopped) @@ -238,14 +246,23 @@ func markroot(gcw *gcWork, i uint32) { } }) } + if workCounter != nil && workDone != 0 { + workCounter.Add(workDone) + if flushBgCredit { + gcFlushBgCredit(workDone) + } + } + return workDone } // markrootBlock scans the shard'th shard of the block of memory [b0, // b0+n0), with the given pointer mask. // +// Returns the amount of work done. +// //go:nowritebarrier -func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) { - if rootBlockBytes%(8*sys.PtrSize) != 0 { +func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) int64 { + if rootBlockBytes%(8*goarch.PtrSize) != 0 { // This is necessary to pick byte offsets in ptrmask0. throw("rootBlockBytes must be a multiple of 8*ptrSize") } @@ -255,10 +272,10 @@ func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) { // These tests are written to avoid any possible overflow. off := uintptr(shard) * rootBlockBytes if off >= n0 { - return + return 0 } b := b0 + off - ptrmask := (*uint8)(add(unsafe.Pointer(ptrmask0), uintptr(shard)*(rootBlockBytes/(8*sys.PtrSize)))) + ptrmask := (*uint8)(add(unsafe.Pointer(ptrmask0), uintptr(shard)*(rootBlockBytes/(8*goarch.PtrSize)))) n := uintptr(rootBlockBytes) if off+n > n0 { n = n0 - off @@ -266,6 +283,7 @@ func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) { // Scan this shard. scanblock(b, n, ptrmask, gcw, nil) + return int64(n) } // markrootFreeGStacks frees stacks of dead Gs. @@ -369,10 +387,12 @@ func markrootSpans(gcw *gcWork, shard int) { // Mark everything that can be reached from // the object (but *not* the object itself or // we'll never collect it). - scanobject(p, gcw) + if !s.spanclass.noscan() { + scanobject(p, gcw) + } // The special itself is a root. - scanblock(uintptr(unsafe.Pointer(&spf.fn)), sys.PtrSize, &oneptrmask[0], gcw, nil) + scanblock(uintptr(unsafe.Pointer(&spf.fn)), goarch.PtrSize, &oneptrmask[0], gcw, nil) } unlock(&s.speciallock) } @@ -380,7 +400,7 @@ func markrootSpans(gcw *gcWork, shard int) { } // gcAssistAlloc performs GC work to make gp's assist debt positive. -// gp must be the calling user gorountine. +// gp must be the calling user goroutine. // // This must be called with preemption enabled. func gcAssistAlloc(gp *g) { @@ -395,12 +415,20 @@ func gcAssistAlloc(gp *g) { traced := false retry: + if go119MemoryLimitSupport && gcCPULimiter.limiting() { + // If the CPU limiter is enabled, intentionally don't + // assist to reduce the amount of CPU time spent in the GC. + if traced { + traceGCMarkAssistDone() + } + return + } // Compute the amount of scan work we need to do to make the // balance positive. When the required amount of work is low, // we over-assist to build up credit for future allocations // and amortize the cost of assisting. - assistWorkPerByte := float64frombits(atomic.Load64(&gcController.assistWorkPerByte)) - assistBytesPerWork := float64frombits(atomic.Load64(&gcController.assistBytesPerWork)) + assistWorkPerByte := gcController.assistWorkPerByte.Load() + assistBytesPerWork := gcController.assistBytesPerWork.Load() debtBytes := -gp.gcAssistBytes scanWork := int64(assistWorkPerByte * float64(debtBytes)) if scanWork < gcOverAssistWork { @@ -414,7 +442,7 @@ retry: // will just cause steals to fail until credit is accumulated // again, so in the long run it doesn't really matter, but we // do have to handle the negative credit case. - bgScanCredit := atomic.Loadint64(&gcController.bgScanCredit) + bgScanCredit := gcController.bgScanCredit.Load() stolen := int64(0) if bgScanCredit > 0 { if bgScanCredit < scanWork { @@ -424,7 +452,7 @@ retry: stolen = scanWork gp.gcAssistBytes += debtBytes } - atomic.Xaddint64(&gcController.bgScanCredit, -stolen) + gcController.bgScanCredit.Add(-stolen) scanWork -= stolen @@ -519,7 +547,11 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // Track time spent in this assist. Since we're on the // system stack, this is non-preemptible, so we can // just measure start and end time. + // + // Limiter event tracking might be disabled if we end up here + // while on a mark worker. startTime := nanotime() + trackLimiterEvent := gp.m.p.ptr().limiterEvent.start(limiterEventMarkAssist, startTime) decnwait := atomic.Xadd(&work.nwait, -1) if decnwait == work.nproc { @@ -544,7 +576,7 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // this scan work counts for. The "1+" is a poor man's // round-up, to ensure this adds credit even if // assistBytesPerWork is very low. - assistBytesPerWork := float64frombits(atomic.Load64(&gcController.assistBytesPerWork)) + assistBytesPerWork := gcController.assistBytesPerWork.Load() gp.gcAssistBytes += 1 + int64(assistBytesPerWork*float64(workDone)) // If this is the last worker and we ran out of work, @@ -563,12 +595,17 @@ func gcAssistAlloc1(gp *g, scanWork int64) { // a valid pointer). gp.param = unsafe.Pointer(gp) } - duration := nanotime() - startTime - _p_ := gp.m.p.ptr() - _p_.gcAssistTime += duration - if _p_.gcAssistTime > gcAssistTimeSlack { - atomic.Xaddint64(&gcController.assistTime, _p_.gcAssistTime) - _p_.gcAssistTime = 0 + now := nanotime() + duration := now - startTime + pp := gp.m.p.ptr() + pp.gcAssistTime += duration + if trackLimiterEvent { + pp.limiterEvent.stop(limiterEventMarkAssist, now) + } + if pp.gcAssistTime > gcAssistTimeSlack { + gcController.assistTime.Add(pp.gcAssistTime) + gcCPULimiter.update(now) + pp.gcAssistTime = 0 } } @@ -586,8 +623,6 @@ func gcWakeAllAssists() { // // gcParkAssist reports whether the assist is now satisfied. If it // returns false, the caller must retry the assist. -// -//go:nowritebarrier func gcParkAssist() bool { lock(&work.assistQueue.lock) // If the GC cycle finished while we were getting the lock, @@ -606,7 +641,7 @@ func gcParkAssist() bool { // the queue, but can still back out. This avoids a // race in case background marking has flushed more // credit since we checked above. - if atomic.Loadint64(&gcController.bgScanCredit) > 0 { + if gcController.bgScanCredit.Load() > 0 { work.assistQueue.q = oldList if oldList.tail != 0 { oldList.tail.ptr().schedlink.set(nil) @@ -635,11 +670,11 @@ func gcFlushBgCredit(scanWork int64) { // small window here where an assist may add itself to // the blocked queue and park. If that happens, we'll // just get it on the next flush. - atomic.Xaddint64(&gcController.bgScanCredit, scanWork) + gcController.bgScanCredit.Add(scanWork) return } - assistBytesPerWork := float64frombits(atomic.Load64(&gcController.assistBytesPerWork)) + assistBytesPerWork := gcController.assistBytesPerWork.Load() scanBytes := int64(float64(scanWork) * assistBytesPerWork) lock(&work.assistQueue.lock) @@ -673,15 +708,21 @@ func gcFlushBgCredit(scanWork int64) { if scanBytes > 0 { // Convert from scan bytes back to work. - assistWorkPerByte := float64frombits(atomic.Load64(&gcController.assistWorkPerByte)) + assistWorkPerByte := gcController.assistWorkPerByte.Load() scanWork = int64(float64(scanBytes) * assistWorkPerByte) - atomic.Xaddint64(&gcController.bgScanCredit, scanWork) + gcController.bgScanCredit.Add(scanWork) } unlock(&work.assistQueue.lock) } // scanstack scans gp's stack, greying all pointers found on the stack. // +// Returns the amount of scan work performed, but doesn't update +// gcController.stackScanWork or flush any credit. Any background credit produced +// by this function should be flushed by its caller. scanstack itself can't +// safely flush because it may result in trying to wake up a goroutine that +// was just scanned, resulting in a self-deadlock. +// // scanstack will also shrink the stack if it is safe to do so. If it // is not, it schedules a stack shrink for the next synchronous safe // point. @@ -691,7 +732,7 @@ func gcFlushBgCredit(scanWork int64) { // //go:nowritebarrier //go:systemstack -func scanstack(gp *g, gcw *gcWork) { +func scanstack(gp *g, gcw *gcWork) int64 { if readgstatus(gp)&_Gscan == 0 { print("runtime:scanstack: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", hex(readgstatus(gp)), "\n") throw("scanstack - bad status") @@ -702,7 +743,7 @@ func scanstack(gp *g, gcw *gcWork) { print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n") throw("mark - bad status") case _Gdead: - return + return 0 case _Grunning: print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n") throw("scanstack: goroutine not stopped") @@ -714,6 +755,23 @@ func scanstack(gp *g, gcw *gcWork) { throw("can't scan our own stack") } + // scannedSize is the amount of work we'll be reporting. + // + // It is less than the allocated size (which is hi-lo). + var sp uintptr + if gp.syscallsp != 0 { + sp = gp.syscallsp // If in a system call this is the stack pointer (gp.sched.sp can be 0 in this case on Windows). + } else { + sp = gp.sched.sp + } + scannedSize := gp.stack.hi - sp + + // Keep statistics for initial stack size calculation. + // Note that this accumulates the scanned size, not the allocated size. + p := getg().m.p.ptr() + p.scannedStackSize += uint64(scannedSize) + p.scannedStacks++ + if isShrinkStackSafe(gp) { // Shrink the stack if not much of it is being used. shrinkstack(gp) @@ -737,7 +795,7 @@ func scanstack(gp *g, gcw *gcWork) { // register that gets moved back and forth between the // register and sched.ctxt without a write barrier. if gp.sched.ctxt != nil { - scanblock(uintptr(unsafe.Pointer(&gp.sched.ctxt)), sys.PtrSize, &oneptrmask[0], gcw, &state) + scanblock(uintptr(unsafe.Pointer(&gp.sched.ctxt)), goarch.PtrSize, &oneptrmask[0], gcw, &state) } // Scan the stack. Accumulate a list of stack objects. @@ -750,26 +808,23 @@ func scanstack(gp *g, gcw *gcWork) { // Find additional pointers that point into the stack from the heap. // Currently this includes defers and panics. See also function copystack. - // Find and trace all defer arguments. - tracebackdefers(gp, scanframe, nil) - // Find and trace other pointers in defer records. for d := gp._defer; d != nil; d = d.link { if d.fn != nil { - // tracebackdefers above does not scan the func value, which could - // be a stack allocated closure. See issue 30453. - scanblock(uintptr(unsafe.Pointer(&d.fn)), sys.PtrSize, &oneptrmask[0], gcw, &state) + // Scan the func value, which could be a stack allocated closure. + // See issue 30453. + scanblock(uintptr(unsafe.Pointer(&d.fn)), goarch.PtrSize, &oneptrmask[0], gcw, &state) } if d.link != nil { // The link field of a stack-allocated defer record might point // to a heap-allocated defer record. Keep that heap record live. - scanblock(uintptr(unsafe.Pointer(&d.link)), sys.PtrSize, &oneptrmask[0], gcw, &state) + scanblock(uintptr(unsafe.Pointer(&d.link)), goarch.PtrSize, &oneptrmask[0], gcw, &state) } // Retain defers records themselves. // Defer records might not be reachable from the G through regular heap // tracing because the defer linked list might weave between the stack and the heap. if d.heap { - scanblock(uintptr(unsafe.Pointer(&d)), sys.PtrSize, &oneptrmask[0], gcw, &state) + scanblock(uintptr(unsafe.Pointer(&d)), goarch.PtrSize, &oneptrmask[0], gcw, &state) } } if gp._panic != nil { @@ -807,7 +862,7 @@ func scanstack(gp *g, gcw *gcWork) { println() printunlock() } - gcdata := r.gcdata + gcdata := r.gcdata() var s *mspan if r.useGCProg() { // This path is pretty unlikely, an object large enough @@ -856,9 +911,11 @@ func scanstack(gp *g, gcw *gcWork) { if state.buf != nil || state.cbuf != nil || state.freeBuf != nil { throw("remaining pointer buffers") } + return int64(scannedSize) } // Scan a stack frame: local variables and function arguments/results. +// //go:nowritebarrier func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) { if _DebugGC > 1 && frame.continpc != 0 { @@ -888,10 +945,10 @@ func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) { } // Scan arguments to this frame. - if frame.arglen != 0 { + if n := frame.argBytes(); n != 0 { // TODO: We could pass the entry argument map // to narrow this down further. - scanConservative(frame.argp, frame.arglen, nil, gcw, state) + scanConservative(frame.argp, n, nil, gcw, state) } if isAsyncPreempt || isDebugCall { @@ -909,17 +966,17 @@ func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) { return } - locals, args, objs := getStackMap(frame, &state.cache, false) + locals, args, objs := frame.getStackMap(&state.cache, false) // Scan local variables if stack frame has been allocated. if locals.n > 0 { - size := uintptr(locals.n) * sys.PtrSize + size := uintptr(locals.n) * goarch.PtrSize scanblock(frame.varp-size, size, locals.bytedata, gcw, state) } // Scan arguments. if args.n > 0 { - scanblock(frame.argp, uintptr(args.n)*sys.PtrSize, args.bytedata, gcw, state) + scanblock(frame.argp, uintptr(args.n)*goarch.PtrSize, args.bytedata, gcw, state) } // Add all stack objects to the stack object list. @@ -927,7 +984,8 @@ func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) { // varp is 0 for defers, where there are no locals. // In that case, there can't be a pointer to its args, either. // (And all args would be scanned above anyway.) - for i, obj := range objs { + for i := range objs { + obj := &objs[i] off := obj.off base := frame.varp // locals base pointer if off >= 0 { @@ -941,7 +999,7 @@ func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) { if stackTraceDebug { println("stkobj at", hex(ptr), "of size", obj.size) } - state.addObject(ptr, &objs[i]) + state.addObject(ptr, obj) } } } @@ -987,7 +1045,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { flushBgCredit := flags&gcDrainFlushBgCredit != 0 idle := flags&gcDrainIdle != 0 - initScanWork := gcw.scanWork + initScanWork := gcw.heapScanWork // checkWork is the scan work before performing the next // self-preempt check. @@ -1005,12 +1063,12 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { // Drain root marking jobs. if work.markrootNext < work.markrootJobs { // Stop if we're preemptible or if someone wants to STW. - for !(gp.preempt && (preemptible || atomic.Load(&sched.gcwaiting) != 0)) { + for !(gp.preempt && (preemptible || sched.gcwaiting.Load())) { job := atomic.Xadd(&work.markrootNext, +1) - 1 if job >= work.markrootJobs { break } - markroot(gcw, job) + markroot(gcw, job, flushBgCredit) if check != nil && check() { goto done } @@ -1019,7 +1077,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { // Drain heap marking jobs. // Stop if we're preemptible or if someone wants to STW. - for !(gp.preempt && (preemptible || atomic.Load(&sched.gcwaiting) != 0)) { + for !(gp.preempt && (preemptible || sched.gcwaiting.Load())) { // Try to keep work available on the global queue. We used to // check if there were waiting workers, but it's better to // just keep work available than to make workers wait. In the @@ -1049,14 +1107,14 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { // Flush background scan work credit to the global // account if we've accumulated enough locally so // mutator assists can draw on it. - if gcw.scanWork >= gcCreditSlack { - atomic.Xaddint64(&gcController.scanWork, gcw.scanWork) + if gcw.heapScanWork >= gcCreditSlack { + gcController.heapScanWork.Add(gcw.heapScanWork) if flushBgCredit { - gcFlushBgCredit(gcw.scanWork - initScanWork) + gcFlushBgCredit(gcw.heapScanWork - initScanWork) initScanWork = 0 } - checkWork -= gcw.scanWork - gcw.scanWork = 0 + checkWork -= gcw.heapScanWork + gcw.heapScanWork = 0 if checkWork <= 0 { checkWork += drainCheckThreshold @@ -1069,12 +1127,12 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) { done: // Flush remaining scan work credit. - if gcw.scanWork > 0 { - atomic.Xaddint64(&gcController.scanWork, gcw.scanWork) + if gcw.heapScanWork > 0 { + gcController.heapScanWork.Add(gcw.heapScanWork) if flushBgCredit { - gcFlushBgCredit(gcw.scanWork - initScanWork) + gcFlushBgCredit(gcw.heapScanWork - initScanWork) } - gcw.scanWork = 0 + gcw.heapScanWork = 0 } } @@ -1098,20 +1156,17 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 { // There may already be scan work on the gcw, which we don't // want to claim was done by this call. - workFlushed := -gcw.scanWork + workFlushed := -gcw.heapScanWork + // In addition to backing out because of a preemption, back out + // if the GC CPU limiter is enabled. gp := getg().m.curg - for !gp.preempt && workFlushed+gcw.scanWork < scanWork { + for !gp.preempt && !gcCPULimiter.limiting() && workFlushed+gcw.heapScanWork < scanWork { // See gcDrain comment. if work.full == 0 { gcw.balance() } - // This might be a good place to add prefetch code... - // if(wbuf.nobj > 4) { - // PREFETCH(wbuf->obj[wbuf.nobj - 3]; - // } - // b := gcw.tryGetFast() if b == 0 { b = gcw.tryGet() @@ -1125,26 +1180,24 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 { if b == 0 { // Try to do a root job. - // - // TODO: Assists should get credit for this - // work. if work.markrootNext < work.markrootJobs { job := atomic.Xadd(&work.markrootNext, +1) - 1 if job < work.markrootJobs { - markroot(gcw, job) + workFlushed += markroot(gcw, job, false) continue } } // No heap or root jobs. break } + scanobject(b, gcw) // Flush background scan work credit. - if gcw.scanWork >= gcCreditSlack { - atomic.Xaddint64(&gcController.scanWork, gcw.scanWork) - workFlushed += gcw.scanWork - gcw.scanWork = 0 + if gcw.heapScanWork >= gcCreditSlack { + gcController.heapScanWork.Add(gcw.heapScanWork) + workFlushed += gcw.heapScanWork + gcw.heapScanWork = 0 } } @@ -1152,16 +1205,17 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 { // here because this never flushes to bgScanCredit and // gcw.dispose will flush any remaining work to scanWork. - return workFlushed + gcw.scanWork + return workFlushed + gcw.heapScanWork } // scanblock scans b as scanobject would, but using an explicit // pointer bitmap instead of the heap bitmap. // // This is used to scan non-heap roots, so it does not update -// gcw.bytesMarked or gcw.scanWork. +// gcw.bytesMarked or gcw.heapScanWork. // // If stk != nil, possible stack pointers are also reported to stk.putPtr. +// //go:nowritebarrier func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) { // Use local copies of original parameters, so that a stack trace @@ -1172,9 +1226,9 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) for i := uintptr(0); i < n; { // Find bits for the next word. - bits := uint32(*addb(ptrmask, i/(sys.PtrSize*8))) + bits := uint32(*addb(ptrmask, i/(goarch.PtrSize*8))) if bits == 0 { - i += sys.PtrSize * 8 + i += goarch.PtrSize * 8 continue } for j := 0; j < 8 && i < n; j++ { @@ -1190,7 +1244,7 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) } } bits >>= 1 - i += sys.PtrSize + i += goarch.PtrSize } } } @@ -1202,33 +1256,32 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) // //go:nowritebarrier func scanobject(b uintptr, gcw *gcWork) { + // Prefetch object before we scan it. + // + // This will overlap fetching the beginning of the object with initial + // setup before we start scanning the object. + sys.Prefetch(b) + // Find the bits for b and the size of the object at b. // // b is either the beginning of an object, in which case this // is the size of the object to scan, or it points to an // oblet, in which case we compute the size to scan below. - hbits := heapBitsForAddr(b) s := spanOfUnchecked(b) n := s.elemsize if n == 0 { throw("scanobject n == 0") } + if s.spanclass.noscan() { + // Correctness-wise this is ok, but it's inefficient + // if noscan objects reach here. + throw("scanobject of a noscan object") + } if n > maxObletBytes { // Large object. Break into oblets for better // parallelism and lower latency. if b == s.base() { - // It's possible this is a noscan object (not - // from greyobject, but from other code - // paths), in which case we must *not* enqueue - // oblets since their bitmaps will be - // uninitialized. - if s.spanclass.noscan() { - // Bypass the whole scan. - gcw.bytesMarked += uint64(n) - return - } - // Enqueue the other oblets to scan later. // Some oblets may be in b's scalar tail, but // these will be marked as "no more pointers", @@ -1250,20 +1303,24 @@ func scanobject(b uintptr, gcw *gcWork) { } } - var i uintptr - for i = 0; i < n; i, hbits = i+sys.PtrSize, hbits.next() { - // Load bits once. See CL 22712 and issue 16973 for discussion. - bits := hbits.bits() - if bits&bitScan == 0 { - break // no more pointers in this object - } - if bits&bitPointer == 0 { - continue // not a pointer + hbits := heapBitsForAddr(b, n) + var scanSize uintptr + for { + var addr uintptr + if hbits, addr = hbits.nextFast(); addr == 0 { + if hbits, addr = hbits.next(); addr == 0 { + break + } } + // Keep track of farthest pointer we found, so we can + // update heapScanWork. TODO: is there a better metric, + // now that we can skip scalar portions pretty efficiently? + scanSize = addr - b + goarch.PtrSize + // Work here is duplicated in scanblock and above. // If you make changes here, make changes there too. - obj := *(*uintptr)(unsafe.Pointer(b + i)) + obj := *(*uintptr)(unsafe.Pointer(addr)) // At this point we have extracted the next potential pointer. // Quickly filter out nil and pointers back to the current object. @@ -1277,13 +1334,13 @@ func scanobject(b uintptr, gcw *gcWork) { // heap. In this case, we know the object was // just allocated and hence will be marked by // allocation itself. - if obj, span, objIndex := findObject(obj, b, i); obj != 0 { - greyobject(obj, b, i, span, gcw, objIndex) + if obj, span, objIndex := findObject(obj, b, addr-b); obj != 0 { + greyobject(obj, b, addr-b, span, gcw, objIndex) } } } gcw.bytesMarked += uint64(n) - gcw.scanWork += int64(i) + gcw.heapScanWork += int64(scanSize) } // scanConservative scans block [b, b+n) conservatively, treating any @@ -1300,7 +1357,7 @@ func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackSca print("conservatively scanning [", hex(b), ",", hex(b+n), ")\n") hexdumpWords(b, b+n, func(p uintptr) byte { if ptrmask != nil { - word := (p - b) / sys.PtrSize + word := (p - b) / goarch.PtrSize bits := *addb(ptrmask, word/8) if (bits>>(word%8))&1 == 0 { return '$' @@ -1325,9 +1382,9 @@ func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackSca printunlock() } - for i := uintptr(0); i < n; i += sys.PtrSize { + for i := uintptr(0); i < n; i += goarch.PtrSize { if ptrmask != nil { - word := i / sys.PtrSize + word := i / goarch.PtrSize bits := *addb(ptrmask, word/8) if bits == 0 { // Skip 8 words (the loop increment will do the 8th) @@ -1336,10 +1393,10 @@ func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackSca // seen this word of ptrmask, so i // must be 8-word-aligned, but check // our reasoning just in case. - if i%(sys.PtrSize*8) != 0 { + if i%(goarch.PtrSize*8) != 0 { throw("misaligned mask") } - i += sys.PtrSize*8 - sys.PtrSize + i += goarch.PtrSize*8 - goarch.PtrSize continue } if (bits>>(word%8))&1 == 0 { @@ -1384,6 +1441,7 @@ func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackSca // Shade the object if it isn't already. // The object is not nil and known to be in the heap. // Preemption must be disabled. +// //go:nowritebarrier func shade(b uintptr) { if obj, span, objIndex := findObject(b, 0, 0); obj != 0 { @@ -1401,7 +1459,7 @@ func shade(b uintptr) { //go:nowritebarrierrec func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr) { // obj should be start of allocation, and so must be at least pointer-aligned. - if obj&(sys.PtrSize-1) != 0 { + if obj&(goarch.PtrSize-1) != 0 { throw("greyobject: obj not pointer-aligned") } mbits := span.markBitsForIndex(objIndex) @@ -1440,12 +1498,12 @@ func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintp } } - // Queue the obj for scanning. The PREFETCH(obj) logic has been removed but - // seems like a nice optimization that can be added back in. - // There needs to be time between the PREFETCH and the use. - // Previously we put the obj in an 8 element buffer that is drained at a rate - // to give the PREFETCH time to do its work. - // Use of PREFETCHNTA might be more appropriate than PREFETCH + // We're adding obj to P's local workbuf, so it's likely + // this object will be processed soon by the same P. + // Even if the workbuf gets flushed, there will likely still be + // some benefit on platforms with inclusive shared caches. + sys.Prefetch(obj) + // Queue the obj for scanning. if !gcw.putFast(obj) { gcw.put(obj) } @@ -1473,13 +1531,13 @@ func gcDumpObject(label string, obj, off uintptr) { // We're printing something from a stack frame. We // don't know how big it is, so just show up to an // including off. - size = off + sys.PtrSize + size = off + goarch.PtrSize } - for i := uintptr(0); i < size; i += sys.PtrSize { + for i := uintptr(0); i < size; i += goarch.PtrSize { // For big objects, just print the beginning (because // that usually hints at the object's type) and the // fields around off. - if !(i < 128*sys.PtrSize || off-16*sys.PtrSize < i && i < off+16*sys.PtrSize) { + if !(i < 128*goarch.PtrSize || off-16*goarch.PtrSize < i && i < off+16*goarch.PtrSize) { skipped = true continue } @@ -1505,7 +1563,7 @@ func gcDumpObject(label string, obj, off uintptr) { // //go:nowritebarrier //go:nosplit -func gcmarknewobject(span *mspan, obj, size, scanSize uintptr) { +func gcmarknewobject(span *mspan, obj, size uintptr) { if useCheckmark { // The world should be stopped so this should not happen. throw("gcmarknewobject called while doing checkmark") } @@ -1522,7 +1580,6 @@ func gcmarknewobject(span *mspan, obj, size, scanSize uintptr) { gcw := &getg().m.p.ptr().gcw gcw.bytesMarked += uint64(size) - gcw.scanWork += int64(scanSize) } // gcMarkTinyAllocs greys all active tiny alloc blocks. diff --git a/src/runtime/mgcpacer.go b/src/runtime/mgcpacer.go index 9338359de7d771..2e73ac29230ce8 100644 --- a/src/runtime/mgcpacer.go +++ b/src/runtime/mgcpacer.go @@ -6,14 +6,25 @@ package runtime import ( "internal/cpu" + "internal/goexperiment" "runtime/internal/atomic" - "unsafe" + _ "unsafe" // for go:linkname ) +// go119MemoryLimitSupport is a feature flag for a number of changes +// related to the memory limit feature (#48409). Disabling this flag +// disables those features, as well as the memory limit mechanism, +// which becomes a no-op. +const go119MemoryLimitSupport = true + const ( // gcGoalUtilization is the goal CPU utilization for // marking as a fraction of GOMAXPROCS. - gcGoalUtilization = 0.30 + // + // Increasing the goal utilization will shorten GC cycles as the GC + // has more resources behind it, lessening costs from the write barrier, + // but comes at the cost of increasing mutator latency. + gcGoalUtilization = gcBackgroundUtilization // gcBackgroundUtilization is the fixed CPU utilization for background // marking. It must be <= gcGoalUtilization. The difference between @@ -21,15 +32,18 @@ const ( // mark assists. The scheduler will aim to use within 50% of this // goal. // - // Setting this to < gcGoalUtilization avoids saturating the trigger - // feedback controller when there are no assists, which allows it to - // better control CPU and heap growth. However, the larger the gap, - // the more mutator assists are expected to happen, which impact - // mutator latency. + // As a general rule, there's little reason to set gcBackgroundUtilization + // < gcGoalUtilization. One reason might be in mostly idle applications, + // where goroutines are unlikely to assist at all, so the actual + // utilization will be lower than the goal. But this is moot point + // because the idle mark workers already soak up idle CPU resources. + // These two values are still kept separate however because they are + // distinct conceptually, and in previous iterations of the pacer the + // distinction was more important. gcBackgroundUtilization = 0.25 // gcCreditSlack is the amount of scan work credit that can - // accumulate locally before updating gcController.scanWork and, + // accumulate locally before updating gcController.heapScanWork and, // optionally, gcController.bgScanCredit. Lower values give a more // accurate assist ratio and make it more likely that assists will // successfully steal background credit. Higher values reduce memory @@ -46,36 +60,47 @@ const ( gcOverAssistWork = 64 << 10 // defaultHeapMinimum is the value of heapMinimum for GOGC==100. - defaultHeapMinimum = 4 << 20 + defaultHeapMinimum = (goexperiment.HeapMinimum512KiBInt)*(512<<10) + + (1-goexperiment.HeapMinimum512KiBInt)*(4<<20) + + // maxStackScanSlack is the bytes of stack space allocated or freed + // that can accumulate on a P before updating gcController.stackSize. + maxStackScanSlack = 8 << 10 + + // memoryLimitHeapGoalHeadroom is the amount of headroom the pacer gives to + // the heap goal when operating in the memory-limited regime. That is, + // it'll reduce the heap goal by this many extra bytes off of the base + // calculation. + memoryLimitHeapGoalHeadroom = 1 << 20 ) -func init() { - if offset := unsafe.Offsetof(gcController.heapLive); offset%8 != 0 { - println(offset) - throw("gcController.heapLive not aligned to 8 bytes") - } -} - // gcController implements the GC pacing controller that determines // when to trigger concurrent garbage collection and how much marking // work to do in mutator assists and background marking. // -// It uses a feedback control algorithm to adjust the gcController.trigger -// trigger based on the heap growth and GC CPU utilization each cycle. -// This algorithm optimizes for heap growth to match GOGC and for CPU -// utilization between assist and background marking to be 25% of +// It calculates the ratio between the allocation rate (in terms of CPU +// time) and the GC scan throughput to determine the heap size at which to +// trigger a GC cycle such that no GC assists are required to finish on time. +// This algorithm thus optimizes GC CPU utilization to the dedicated background +// mark utilization of 25% of GOMAXPROCS by minimizing GC assists. // GOMAXPROCS. The high-level design of this algorithm is documented -// at https://golang.org/s/go15gcpacing. -// -// All fields of gcController are used only during a single mark -// cycle. +// at https://github.com/golang/proposal/blob/master/design/44167-gc-pacer-redesign.md. +// See https://golang.org/s/go15gcpacing for additional historical context. var gcController gcControllerState type gcControllerState struct { - // Initialized from $GOGC. GOGC=off means no GC. - gcPercent int32 + // Initialized from GOGC. GOGC=off means no GC. + gcPercent atomic.Int32 - _ uint32 // padding so following 64-bit values are 8-byte aligned + // memoryLimit is the soft memory limit in bytes. + // + // Initialized from GOMEMLIMIT. GOMEMLIMIT=off is equivalent to MaxInt64 + // which means no soft memory limit in practice. + // + // This is an int64 instead of a uint64 to more easily maintain parity with + // the SetMemoryLimit API, which sets a maximum at MaxInt64. This value + // should never be negative. + memoryLimit atomic.Int64 // heapMinimum is the minimum heap size at which to trigger GC. // For small heaps, this overrides the usual GOGC*live set rule. @@ -91,37 +116,60 @@ type gcControllerState struct { // debugging. heapMinimum uint64 - // triggerRatio is the heap growth ratio that triggers marking. - // - // E.g., if this is 0.6, then GC should start when the live - // heap has reached 1.6 times the heap size marked by the - // previous cycle. This should be ≤ GOGC/100 so the trigger - // heap size is less than the goal heap size. This is set - // during mark termination for the next cycle's trigger. + // runway is the amount of runway in heap bytes allocated by the + // application that we want to give the GC once it starts. // - // Protected by mheap_.lock or a STW. - triggerRatio float64 + // This is computed from consMark during mark termination. + runway atomic.Uint64 - // trigger is the heap size that triggers marking. + // consMark is the estimated per-CPU consMark ratio for the application. // - // When heapLive ≥ trigger, the mark phase will start. - // This is also the heap size by which proportional sweeping - // must be complete. + // It represents the ratio between the application's allocation + // rate, as bytes allocated per CPU-time, and the GC's scan rate, + // as bytes scanned per CPU-time. + // The units of this ratio are (B / cpu-ns) / (B / cpu-ns). // - // This is computed from triggerRatio during mark termination - // for the next cycle's trigger. + // At a high level, this value is computed as the bytes of memory + // allocated (cons) per unit of scan work completed (mark) in a GC + // cycle, divided by the CPU time spent on each activity. // - // Protected by mheap_.lock or a STW. - trigger uint64 + // Updated at the end of each GC cycle, in endCycle. + consMark float64 - // heapGoal is the goal heapLive for when next GC ends. - // Set to ^uint64(0) if disabled. + // consMarkController holds the state for the mark-cons ratio + // estimation over time. // - // Read and written atomically, unless the world is stopped. - heapGoal uint64 + // Its purpose is to smooth out noisiness in the computation of + // consMark; see consMark for details. + consMarkController piController - // lastHeapGoal is the value of heapGoal for the previous GC. - // Note that this is distinct from the last value heapGoal had, + // gcPercentHeapGoal is the goal heapLive for when next GC ends derived + // from gcPercent. + // + // Set to ^uint64(0) if gcPercent is disabled. + gcPercentHeapGoal atomic.Uint64 + + // sweepDistMinTrigger is the minimum trigger to ensure a minimum + // sweep distance. + // + // This bound is also special because it applies to both the trigger + // *and* the goal (all other trigger bounds must be based *on* the goal). + // + // It is computed ahead of time, at commit time. The theory is that, + // absent a sudden change to a parameter like gcPercent, the trigger + // will be chosen to always give the sweeper enough headroom. However, + // such a change might dramatically and suddenly move up the trigger, + // in which case we need to ensure the sweeper still has enough headroom. + sweepDistMinTrigger atomic.Uint64 + + // triggered is the point at which the current GC cycle actually triggered. + // Only valid during the mark phase of a GC cycle, otherwise set to ^uint64(0). + // + // Updated while the world is stopped. + triggered uint64 + + // lastHeapGoal is the value of heapGoal at the moment the last GC + // ended. Note that this is distinct from the last value heapGoal had, // because it could change if e.g. gcPercent changes. // // Read and written with the world stopped or with mheap_.lock held. @@ -129,38 +177,57 @@ type gcControllerState struct { // heapLive is the number of bytes considered live by the GC. // That is: retained by the most recent GC plus allocated - // since then. heapLive ≤ memstats.heapAlloc, since heapAlloc includes - // unmarked objects that have not yet been swept (and hence goes up as we - // allocate and down as we sweep) while heapLive excludes these - // objects (and hence only goes up between GCs). - // - // This is updated atomically without locking. To reduce - // contention, this is updated only when obtaining a span from - // an mcentral and at this point it counts all of the - // unallocated slots in that span (which will be allocated - // before that mcache obtains another span from that - // mcentral). Hence, it slightly overestimates the "true" live - // heap size. It's better to overestimate than to - // underestimate because 1) this triggers the GC earlier than - // necessary rather than potentially too late and 2) this - // leads to a conservative GC rate rather than a GC rate that - // is potentially too low. - // - // Reads should likewise be atomic (or during STW). + // since then. heapLive ≤ memstats.totalAlloc-memstats.totalFree, since + // heapAlloc includes unmarked objects that have not yet been swept (and + // hence goes up as we allocate and down as we sweep) while heapLive + // excludes these objects (and hence only goes up between GCs). + // + // To reduce contention, this is updated only when obtaining a span + // from an mcentral and at this point it counts all of the unallocated + // slots in that span (which will be allocated before that mcache + // obtains another span from that mcentral). Hence, it slightly + // overestimates the "true" live heap size. It's better to overestimate + // than to underestimate because 1) this triggers the GC earlier than + // necessary rather than potentially too late and 2) this leads to a + // conservative GC rate rather than a GC rate that is potentially too + // low. // // Whenever this is updated, call traceHeapAlloc() and // this gcControllerState's revise() method. - heapLive uint64 + heapLive atomic.Uint64 - // heapScan is the number of bytes of "scannable" heap. This - // is the live heap (as counted by heapLive), but omitting - // no-scan objects and no-scan tails of objects. + // heapScan is the number of bytes of "scannable" heap. This is the + // live heap (as counted by heapLive), but omitting no-scan objects and + // no-scan tails of objects. // - // Whenever this is updated, call this gcControllerState's - // revise() method. + // This value is fixed at the start of a GC cycle. It represents the + // maximum scannable heap. + heapScan atomic.Uint64 + + // lastHeapScan is the number of bytes of heap that were scanned + // last GC cycle. It is the same as heapMarked, but only + // includes the "scannable" parts of objects. // - // Read and written atomically or with the world stopped. - heapScan uint64 + // Updated when the world is stopped. + lastHeapScan uint64 + + // lastStackScan is the number of bytes of stack that were scanned + // last GC cycle. + lastStackScan atomic.Uint64 + + // maxStackScan is the amount of allocated goroutine stack space in + // use by goroutines. + // + // This number tracks allocated goroutine stack space rather than used + // goroutine stack space (i.e. what is actually scanned) because used + // goroutine stack space is much harder to measure cheaply. By using + // allocated space, we make an overestimate; this is OK, it's better + // to conservatively overcount than undercount. + maxStackScan atomic.Uint64 + + // globalsScan is the total amount of global variable space + // that is scannable. + globalsScan atomic.Uint64 // heapMarked is the number of bytes marked by the previous // GC. After mark termination, heapLive == heapMarked, but @@ -168,78 +235,107 @@ type gcControllerState struct { // next mark termination. heapMarked uint64 - // scanWork is the total scan work performed this cycle. This - // is updated atomically during the cycle. Updates occur in - // bounded batches, since it is both written and read - // throughout the cycle. At the end of the cycle, this is how + // heapScanWork is the total heap scan work performed this cycle. + // stackScanWork is the total stack scan work performed this cycle. + // globalsScanWork is the total globals scan work performed this cycle. + // + // These are updated atomically during the cycle. Updates occur in + // bounded batches, since they are both written and read + // throughout the cycle. At the end of the cycle, heapScanWork is how // much of the retained heap is scannable. // - // Currently this is the bytes of heap scanned. For most uses, - // this is an opaque unit of work, but for estimation the - // definition is important. - scanWork int64 - - // bgScanCredit is the scan work credit accumulated by the - // concurrent background scan. This credit is accumulated by - // the background scan and stolen by mutator assists. This is - // updated atomically. Updates occur in bounded batches, since - // it is both written and read throughout the cycle. - bgScanCredit int64 + // Currently these are measured in bytes. For most uses, this is an + // opaque unit of work, but for estimation the definition is important. + // + // Note that stackScanWork includes only stack space scanned, not all + // of the allocated stack. + heapScanWork atomic.Int64 + stackScanWork atomic.Int64 + globalsScanWork atomic.Int64 + + // bgScanCredit is the scan work credit accumulated by the concurrent + // background scan. This credit is accumulated by the background scan + // and stolen by mutator assists. Updates occur in bounded batches, + // since it is both written and read throughout the cycle. + bgScanCredit atomic.Int64 // assistTime is the nanoseconds spent in mutator assists - // during this cycle. This is updated atomically. Updates - // occur in bounded batches, since it is both written and read - // throughout the cycle. - assistTime int64 - - // dedicatedMarkTime is the nanoseconds spent in dedicated - // mark workers during this cycle. This is updated atomically - // at the end of the concurrent mark phase. - dedicatedMarkTime int64 - - // fractionalMarkTime is the nanoseconds spent in the - // fractional mark worker during this cycle. This is updated - // atomically throughout the cycle and will be up-to-date if - // the fractional mark worker is not currently running. - fractionalMarkTime int64 - - // idleMarkTime is the nanoseconds spent in idle marking - // during this cycle. This is updated atomically throughout - // the cycle. - idleMarkTime int64 + // during this cycle. This is updated atomically, and must also + // be updated atomically even during a STW, because it is read + // by sysmon. Updates occur in bounded batches, since it is both + // written and read throughout the cycle. + assistTime atomic.Int64 + + // dedicatedMarkTime is the nanoseconds spent in dedicated mark workers + // during this cycle. This is updated at the end of the concurrent mark + // phase. + dedicatedMarkTime atomic.Int64 + + // fractionalMarkTime is the nanoseconds spent in the fractional mark + // worker during this cycle. This is updated throughout the cycle and + // will be up-to-date if the fractional mark worker is not currently + // running. + fractionalMarkTime atomic.Int64 + + // idleMarkTime is the nanoseconds spent in idle marking during this + // cycle. This is updated throughout the cycle. + idleMarkTime atomic.Int64 // markStartTime is the absolute start time in nanoseconds // that assists and background mark workers started. markStartTime int64 - // dedicatedMarkWorkersNeeded is the number of dedicated mark - // workers that need to be started. This is computed at the - // beginning of each cycle and decremented atomically as - // dedicated mark workers get started. - dedicatedMarkWorkersNeeded int64 + // dedicatedMarkWorkersNeeded is the number of dedicated mark workers + // that need to be started. This is computed at the beginning of each + // cycle and decremented as dedicated mark workers get started. + dedicatedMarkWorkersNeeded atomic.Int64 + + // idleMarkWorkers is two packed int32 values in a single uint64. + // These two values are always updated simultaneously. + // + // The bottom int32 is the current number of idle mark workers executing. + // + // The top int32 is the maximum number of idle mark workers allowed to + // execute concurrently. Normally, this number is just gomaxprocs. However, + // during periodic GC cycles it is set to 0 because the system is idle + // anyway; there's no need to go full blast on all of GOMAXPROCS. + // + // The maximum number of idle mark workers is used to prevent new workers + // from starting, but it is not a hard maximum. It is possible (but + // exceedingly rare) for the current number of idle mark workers to + // transiently exceed the maximum. This could happen if the maximum changes + // just after a GC ends, and an M with no P. + // + // Note that if we have no dedicated mark workers, we set this value to + // 1 in this case we only have fractional GC workers which aren't scheduled + // strictly enough to ensure GC progress. As a result, idle-priority mark + // workers are vital to GC progress in these situations. + // + // For example, consider a situation in which goroutines block on the GC + // (such as via runtime.GOMAXPROCS) and only fractional mark workers are + // scheduled (e.g. GOMAXPROCS=1). Without idle-priority mark workers, the + // last running M might skip scheduling a fractional mark worker if its + // utilization goal is met, such that once it goes to sleep (because there's + // nothing to do), there will be nothing else to spin up a new M for the + // fractional worker in the future, stalling GC progress and causing a + // deadlock. However, idle-priority workers will *always* run when there is + // nothing left to do, ensuring the GC makes progress. + // + // See github.com/golang/go/issues/44163 for more details. + idleMarkWorkers atomic.Uint64 // assistWorkPerByte is the ratio of scan work to allocated // bytes that should be performed by mutator assists. This is // computed at the beginning of each cycle and updated every // time heapScan is updated. - // - // Stored as a uint64, but it's actually a float64. Use - // float64frombits to get the value. - // - // Read and written atomically. - assistWorkPerByte uint64 + assistWorkPerByte atomic.Float64 // assistBytesPerWork is 1/assistWorkPerByte. // - // Stored as a uint64, but it's actually a float64. Use - // float64frombits to get the value. - // - // Read and written atomically. - // // Note that because this is read and written independently // from assistWorkPerByte users may notice a skew between // the two values, and such a state should be safe. - assistBytesPerWork uint64 + assistBytesPerWork atomic.Float64 // fractionalUtilizationGoal is the fraction of wall clock // time that should be spent in the fractional mark worker on @@ -253,72 +349,120 @@ type gcControllerState struct { // If this is zero, no fractional workers are needed. fractionalUtilizationGoal float64 + // These memory stats are effectively duplicates of fields from + // memstats.heapStats but are updated atomically or with the world + // stopped and don't provide the same consistency guarantees. + // + // Because the runtime is responsible for managing a memory limit, it's + // useful to couple these stats more tightly to the gcController, which + // is intimately connected to how that memory limit is maintained. + heapInUse sysMemStat // bytes in mSpanInUse spans + heapReleased sysMemStat // bytes released to the OS + heapFree sysMemStat // bytes not in any span, but not released to the OS + totalAlloc atomic.Uint64 // total bytes allocated + totalFree atomic.Uint64 // total bytes freed + mappedReady atomic.Uint64 // total virtual memory in the Ready state (see mem.go). + + // test indicates that this is a test-only copy of gcControllerState. + test bool + _ cpu.CacheLinePad } -func (c *gcControllerState) init(gcPercent int32) { +func (c *gcControllerState) init(gcPercent int32, memoryLimit int64) { c.heapMinimum = defaultHeapMinimum + c.triggered = ^uint64(0) + + c.consMarkController = piController{ + // Tuned first via the Ziegler-Nichols process in simulation, + // then the integral time was manually tuned against real-world + // applications to deal with noisiness in the measured cons/mark + // ratio. + kp: 0.9, + ti: 4.0, + + // Set a high reset time in GC cycles. + // This is inversely proportional to the rate at which we + // accumulate error from clipping. By making this very high + // we make the accumulation slow. In general, clipping is + // OK in our situation, hence the choice. + // + // Tune this if we get unintended effects from clipping for + // a long time. + tt: 1000, + min: -1000, + max: 1000, + } - // Set a reasonable initial GC trigger. - c.triggerRatio = 7 / 8.0 - - // Fake a heapMarked value so it looks like a trigger at - // heapMinimum is the appropriate growth from heapMarked. - // This will go into computing the initial GC goal. - c.heapMarked = uint64(float64(c.heapMinimum) / (1 + c.triggerRatio)) - - // This will also compute and set the GC trigger and goal. c.setGCPercent(gcPercent) + c.setMemoryLimit(memoryLimit) + c.commit(true) // No sweep phase in the first GC cycle. + // N.B. Don't bother calling traceHeapGoal. Tracing is never enabled at + // initialization time. + // N.B. No need to call revise; there's no GC enabled during + // initialization. } // startCycle resets the GC controller's state and computes estimates // for a new GC cycle. The caller must hold worldsema and the world // must be stopped. -func (c *gcControllerState) startCycle() { - c.scanWork = 0 - c.bgScanCredit = 0 - c.assistTime = 0 - c.dedicatedMarkTime = 0 - c.fractionalMarkTime = 0 - c.idleMarkTime = 0 - - // Ensure that the heap goal is at least a little larger than - // the current live heap size. This may not be the case if GC - // start is delayed or if the allocation that pushed gcController.heapLive - // over trigger is large or if the trigger is really close to - // GOGC. Assist is proportional to this distance, so enforce a - // minimum distance, even if it means going over the GOGC goal - // by a tiny bit. - if c.heapGoal < c.heapLive+1024*1024 { - c.heapGoal = c.heapLive + 1024*1024 - } +func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger gcTrigger) { + c.heapScanWork.Store(0) + c.stackScanWork.Store(0) + c.globalsScanWork.Store(0) + c.bgScanCredit.Store(0) + c.assistTime.Store(0) + c.dedicatedMarkTime.Store(0) + c.fractionalMarkTime.Store(0) + c.idleMarkTime.Store(0) + c.markStartTime = markStartTime + + // TODO(mknyszek): This is supposed to be the actual trigger point for the heap, but + // causes regressions in memory use. The cause is that the PI controller used to smooth + // the cons/mark ratio measurements tends to flail when using the less accurate precomputed + // trigger for the cons/mark calculation, and this results in the controller being more + // conservative about steady-states it tries to find in the future. + // + // This conservatism is transient, but these transient states tend to matter for short-lived + // programs, especially because the PI controller is overdamped, partially because it is + // configured with a relatively large time constant. + // + // Ultimately, I think this is just two mistakes piled on one another: the choice of a swingy + // smoothing function that recalls a fairly long history (due to its overdamped time constant) + // coupled with an inaccurate cons/mark calculation. It just so happens this works better + // today, and it makes it harder to change things in the future. + // + // This is described in #53738. Fix this for #53892 by changing back to the actual trigger + // point and simplifying the smoothing function. + heapTrigger, heapGoal := c.trigger() + c.triggered = heapTrigger // Compute the background mark utilization goal. In general, // this may not come out exactly. We round the number of // dedicated workers so that the utilization is closest to // 25%. For small GOMAXPROCS, this would introduce too much // error, so we add fractional workers in that case. - totalUtilizationGoal := float64(gomaxprocs) * gcBackgroundUtilization - c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal + 0.5) - utilError := float64(c.dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 + totalUtilizationGoal := float64(procs) * gcBackgroundUtilization + dedicatedMarkWorkersNeeded := int64(totalUtilizationGoal + 0.5) + utilError := float64(dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 const maxUtilError = 0.3 if utilError < -maxUtilError || utilError > maxUtilError { // Rounding put us more than 30% off our goal. With // gcBackgroundUtilization of 25%, this happens for // GOMAXPROCS<=3 or GOMAXPROCS=6. Enable fractional // workers to compensate. - if float64(c.dedicatedMarkWorkersNeeded) > totalUtilizationGoal { + if float64(dedicatedMarkWorkersNeeded) > totalUtilizationGoal { // Too many dedicated workers. - c.dedicatedMarkWorkersNeeded-- + dedicatedMarkWorkersNeeded-- } - c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded)) / float64(gomaxprocs) + c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(dedicatedMarkWorkersNeeded)) / float64(procs) } else { c.fractionalUtilizationGoal = 0 } // In STW mode, we just want dedicated workers. if debug.gcstoptheworld > 0 { - c.dedicatedMarkWorkersNeeded = int64(gomaxprocs) + dedicatedMarkWorkersNeeded = int64(procs) c.fractionalUtilizationGoal = 0 } @@ -328,25 +472,47 @@ func (c *gcControllerState) startCycle() { p.gcFractionalMarkTime = 0 } + if trigger.kind == gcTriggerTime { + // During a periodic GC cycle, reduce the number of idle mark workers + // required. However, we need at least one dedicated mark worker or + // idle GC worker to ensure GC progress in some scenarios (see comment + // on maxIdleMarkWorkers). + if dedicatedMarkWorkersNeeded > 0 { + c.setMaxIdleMarkWorkers(0) + } else { + // TODO(mknyszek): The fundamental reason why we need this is because + // we can't count on the fractional mark worker to get scheduled. + // Fix that by ensuring it gets scheduled according to its quota even + // if the rest of the application is idle. + c.setMaxIdleMarkWorkers(1) + } + } else { + // N.B. gomaxprocs and dedicatedMarkWorkersNeeded are guaranteed not to + // change during a GC cycle. + c.setMaxIdleMarkWorkers(int32(procs) - int32(dedicatedMarkWorkersNeeded)) + } + // Compute initial values for controls that are updated // throughout the cycle. + c.dedicatedMarkWorkersNeeded.Store(dedicatedMarkWorkersNeeded) c.revise() if debug.gcpacertrace > 0 { - assistRatio := float64frombits(atomic.Load64(&c.assistWorkPerByte)) + assistRatio := c.assistWorkPerByte.Load() print("pacer: assist ratio=", assistRatio, - " (scan ", gcController.heapScan>>20, " MB in ", + " (scan ", gcController.heapScan.Load()>>20, " MB in ", work.initialHeapLive>>20, "->", - c.heapGoal>>20, " MB)", - " workers=", c.dedicatedMarkWorkersNeeded, + heapGoal>>20, " MB)", + " workers=", dedicatedMarkWorkersNeeded, "+", c.fractionalUtilizationGoal, "\n") } } // revise updates the assist ratio during the GC cycle to account for // improved estimates. This should be called whenever gcController.heapScan, -// gcController.heapLive, or gcController.heapGoal is updated. It is safe to -// call concurrently, but it may race with other calls to revise. +// gcController.heapLive, or if any inputs to gcController.heapGoal are +// updated. It is safe to call concurrently, but it may race with other +// calls to revise. // // The result of this race is that the two assist ratio values may not line // up or may be stale. In practice this is OK because the assist ratio @@ -365,40 +531,66 @@ func (c *gcControllerState) startCycle() { // is when assists are enabled and the necessary statistics are // available). func (c *gcControllerState) revise() { - gcPercent := c.gcPercent + gcPercent := c.gcPercent.Load() if gcPercent < 0 { // If GC is disabled but we're running a forced GC, // act like GOGC is huge for the below calculations. gcPercent = 100000 } - live := atomic.Load64(&c.heapLive) - scan := atomic.Load64(&c.heapScan) - work := atomic.Loadint64(&c.scanWork) + live := c.heapLive.Load() + scan := c.heapScan.Load() + work := c.heapScanWork.Load() + c.stackScanWork.Load() + c.globalsScanWork.Load() // Assume we're under the soft goal. Pace GC to complete at // heapGoal assuming the heap is in steady-state. - heapGoal := int64(atomic.Load64(&c.heapGoal)) - - // Compute the expected scan work remaining. - // - // This is estimated based on the expected - // steady-state scannable heap. For example, with - // GOGC=100, only half of the scannable heap is - // expected to be live, so that's what we target. - // - // (This is a float calculation to avoid overflowing on - // 100*heapScan.) - scanWorkExpected := int64(float64(scan) * 100 / float64(100+gcPercent)) - - if int64(live) > heapGoal || work > scanWorkExpected { - // We're past the soft goal, or we've already done more scan - // work than we expected. Pace GC so that in the worst case it - // will complete by the hard goal. + heapGoal := int64(c.heapGoal()) + + // The expected scan work is computed as the amount of bytes scanned last + // GC cycle (both heap and stack), plus our estimate of globals work for this cycle. + scanWorkExpected := int64(c.lastHeapScan + c.lastStackScan.Load() + c.globalsScan.Load()) + + // maxScanWork is a worst-case estimate of the amount of scan work that + // needs to be performed in this GC cycle. Specifically, it represents + // the case where *all* scannable memory turns out to be live, and + // *all* allocated stack space is scannable. + maxStackScan := c.maxStackScan.Load() + maxScanWork := int64(scan + maxStackScan + c.globalsScan.Load()) + if work > scanWorkExpected { + // We've already done more scan work than expected. Because our expectation + // is based on a steady-state scannable heap size, we assume this means our + // heap is growing. Compute a new heap goal that takes our existing runway + // computed for scanWorkExpected and extrapolates it to maxScanWork, the worst-case + // scan work. This keeps our assist ratio stable if the heap continues to grow. + // + // The effect of this mechanism is that assists stay flat in the face of heap + // growths. It's OK to use more memory this cycle to scan all the live heap, + // because the next GC cycle is inevitably going to use *at least* that much + // memory anyway. + extHeapGoal := int64(float64(heapGoal-int64(c.triggered))/float64(scanWorkExpected)*float64(maxScanWork)) + int64(c.triggered) + scanWorkExpected = maxScanWork + + // hardGoal is a hard limit on the amount that we're willing to push back the + // heap goal, and that's twice the heap goal (i.e. if GOGC=100 and the heap and/or + // stacks and/or globals grow to twice their size, this limits the current GC cycle's + // growth to 4x the original live heap's size). + // + // This maintains the invariant that we use no more memory than the next GC cycle + // will anyway. + hardGoal := int64((1.0 + float64(gcPercent)/100.0) * float64(heapGoal)) + if extHeapGoal > hardGoal { + extHeapGoal = hardGoal + } + heapGoal = extHeapGoal + } + if int64(live) > heapGoal { + // We're already past our heap goal, even the extrapolated one. + // Leave ourselves some extra runway, so in the worst case we + // finish by that point. const maxOvershoot = 1.1 heapGoal = int64(float64(heapGoal) * maxOvershoot) // Compute the upper bound on the scan work remaining. - scanWorkExpected = int64(scan) + scanWorkExpected = maxScanWork } // Compute the remaining scan work estimate. @@ -439,81 +631,114 @@ func (c *gcControllerState) revise() { // cycle. assistWorkPerByte := float64(scanWorkRemaining) / float64(heapRemaining) assistBytesPerWork := float64(heapRemaining) / float64(scanWorkRemaining) - atomic.Store64(&c.assistWorkPerByte, float64bits(assistWorkPerByte)) - atomic.Store64(&c.assistBytesPerWork, float64bits(assistBytesPerWork)) + c.assistWorkPerByte.Store(assistWorkPerByte) + c.assistBytesPerWork.Store(assistBytesPerWork) } -// endCycle computes the trigger ratio for the next cycle. +// endCycle computes the consMark estimate for the next cycle. // userForced indicates whether the current GC cycle was forced // by the application. -func (c *gcControllerState) endCycle(userForced bool) float64 { - if userForced { - // Forced GC means this cycle didn't start at the - // trigger, so where it finished isn't good - // information about how to adjust the trigger. - // Just leave it where it is. - return c.triggerRatio - } - - // Proportional response gain for the trigger controller. Must - // be in [0, 1]. Lower values smooth out transient effects but - // take longer to respond to phase changes. Higher values - // react to phase changes quickly, but are more affected by - // transient changes. Values near 1 may be unstable. - const triggerGain = 0.5 - - // Compute next cycle trigger ratio. First, this computes the - // "error" for this cycle; that is, how far off the trigger - // was from what it should have been, accounting for both heap - // growth and GC CPU utilization. We compute the actual heap - // growth during this cycle and scale that by how far off from - // the goal CPU utilization we were (to estimate the heap - // growth if we had the desired CPU utilization). The - // difference between this estimate and the GOGC-based goal - // heap growth is the error. - goalGrowthRatio := c.effectiveGrowthRatio() - actualGrowthRatio := float64(c.heapLive)/float64(c.heapMarked) - 1 - assistDuration := nanotime() - c.markStartTime +func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) { + // Record last heap goal for the scavenger. + // We'll be updating the heap goal soon. + gcController.lastHeapGoal = c.heapGoal() + + // Compute the duration of time for which assists were turned on. + assistDuration := now - c.markStartTime // Assume background mark hit its utilization goal. utilization := gcBackgroundUtilization // Add assist utilization; avoid divide by zero. if assistDuration > 0 { - utilization += float64(c.assistTime) / float64(assistDuration*int64(gomaxprocs)) + utilization += float64(c.assistTime.Load()) / float64(assistDuration*int64(procs)) } - triggerError := goalGrowthRatio - c.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-c.triggerRatio) + if c.heapLive.Load() <= c.triggered { + // Shouldn't happen, but let's be very safe about this in case the + // GC is somehow extremely short. + // + // In this case though, the only reasonable value for c.heapLive-c.triggered + // would be 0, which isn't really all that useful, i.e. the GC was so short + // that it didn't matter. + // + // Ignore this case and don't update anything. + return + } + idleUtilization := 0.0 + if assistDuration > 0 { + idleUtilization = float64(c.idleMarkTime.Load()) / float64(assistDuration*int64(procs)) + } + // Determine the cons/mark ratio. + // + // The units we want for the numerator and denominator are both B / cpu-ns. + // We get this by taking the bytes allocated or scanned, and divide by the amount of + // CPU time it took for those operations. For allocations, that CPU time is + // + // assistDuration * procs * (1 - utilization) + // + // Where utilization includes just background GC workers and assists. It does *not* + // include idle GC work time, because in theory the mutator is free to take that at + // any point. + // + // For scanning, that CPU time is + // + // assistDuration * procs * (utilization + idleUtilization) + // + // In this case, we *include* idle utilization, because that is additional CPU time that + // the GC had available to it. + // + // In effect, idle GC time is sort of double-counted here, but it's very weird compared + // to other kinds of GC work, because of how fluid it is. Namely, because the mutator is + // *always* free to take it. + // + // So this calculation is really: + // (heapLive-trigger) / (assistDuration * procs * (1-utilization)) / + // (scanWork) / (assistDuration * procs * (utilization+idleUtilization) + // + // Note that because we only care about the ratio, assistDuration and procs cancel out. + scanWork := c.heapScanWork.Load() + c.stackScanWork.Load() + c.globalsScanWork.Load() + currentConsMark := (float64(c.heapLive.Load()-c.triggered) * (utilization + idleUtilization)) / + (float64(scanWork) * (1 - utilization)) - // Finally, we adjust the trigger for next time by this error, - // damped by the proportional gain. - triggerRatio := c.triggerRatio + triggerGain*triggerError + // Update cons/mark controller. The time period for this is 1 GC cycle. + // + // This use of a PI controller might seem strange. So, here's an explanation: + // + // currentConsMark represents the consMark we *should've* had to be perfectly + // on-target for this cycle. Given that we assume the next GC will be like this + // one in the steady-state, it stands to reason that we should just pick that + // as our next consMark. In practice, however, currentConsMark is too noisy: + // we're going to be wildly off-target in each GC cycle if we do that. + // + // What we do instead is make a long-term assumption: there is some steady-state + // consMark value, but it's obscured by noise. By constantly shooting for this + // noisy-but-perfect consMark value, the controller will bounce around a bit, + // but its average behavior, in aggregate, should be less noisy and closer to + // the true long-term consMark value, provided its tuned to be slightly overdamped. + var ok bool + oldConsMark := c.consMark + c.consMark, ok = c.consMarkController.next(c.consMark, currentConsMark, 1.0) + if !ok { + // The error spiraled out of control. This is incredibly unlikely seeing + // as this controller is essentially just a smoothing function, but it might + // mean that something went very wrong with how currentConsMark was calculated. + // Just reset consMark and keep going. + c.consMark = 0 + } if debug.gcpacertrace > 0 { - // Print controller state in terms of the design - // document. - H_m_prev := c.heapMarked - h_t := c.triggerRatio - H_T := c.trigger - h_a := actualGrowthRatio - H_a := c.heapLive - h_g := goalGrowthRatio - H_g := int64(float64(H_m_prev) * (1 + h_g)) - u_a := utilization - u_g := gcGoalUtilization - W_a := c.scanWork - print("pacer: H_m_prev=", H_m_prev, - " h_t=", h_t, " H_T=", H_T, - " h_a=", h_a, " H_a=", H_a, - " h_g=", h_g, " H_g=", H_g, - " u_a=", u_a, " u_g=", u_g, - " W_a=", W_a, - " goalΔ=", goalGrowthRatio-h_t, - " actualΔ=", h_a-h_t, - " u_a/u_g=", u_a/u_g, - "\n") - } - - return triggerRatio + printlock() + goal := gcGoalUtilization * 100 + print("pacer: ", int(utilization*100), "% CPU (", int(goal), " exp.) for ") + print(c.heapScanWork.Load(), "+", c.stackScanWork.Load(), "+", c.globalsScanWork.Load(), " B work (", c.lastHeapScan+c.lastStackScan.Load()+c.globalsScan.Load(), " B exp.) ") + live := c.heapLive.Load() + print("in ", c.triggered, " B -> ", live, " B (∆goal ", int64(live)-int64(c.lastHeapGoal), ", cons/mark ", oldConsMark, ")") + if !ok { + print("[controller reset]") + } + println() + printunlock() + } } // enlistWorker encourages another dedicated mark worker to start on @@ -525,14 +750,14 @@ func (c *gcControllerState) enlistWorker() { // If there are idle Ps, wake one so it will run an idle worker. // NOTE: This is suspected of causing deadlocks. See golang.org/issue/19112. // - // if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 { + // if sched.npidle.Load() != 0 && sched.nmspinning.Load() == 0 { // wakep() // return // } // There are no idle Ps. If we need more dedicated workers, // try to preempt a running P so it will switch to a worker. - if c.dedicatedMarkWorkersNeeded <= 0 { + if c.dedicatedMarkWorkersNeeded.Load() <= 0 { return } // Pick a random other P to preempt. @@ -559,19 +784,30 @@ func (c *gcControllerState) enlistWorker() { } } -// findRunnableGCWorker returns a background mark worker for _p_ if it +// findRunnableGCWorker returns a background mark worker for pp if it // should be run. This must only be called when gcBlackenEnabled != 0. -func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g { +func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) { if gcBlackenEnabled == 0 { throw("gcControllerState.findRunnable: blackening not enabled") } - if !gcMarkWorkAvailable(_p_) { + // Since we have the current time, check if the GC CPU limiter + // hasn't had an update in a while. This check is necessary in + // case the limiter is on but hasn't been checked in a while and + // so may have left sufficient headroom to turn off again. + if now == 0 { + now = nanotime() + } + if gcCPULimiter.needUpdate(now) { + gcCPULimiter.update(now) + } + + if !gcMarkWorkAvailable(pp) { // No work to be done right now. This can happen at // the end of the mark phase when there are still // assists tapering off. Don't bother running a worker // now because it'll just return immediately. - return nil + return nil, now } // Grab a worker before we commit to running below. @@ -588,17 +824,17 @@ func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g { // it will always do so with queued global work. Thus, that P // will be immediately eligible to re-run the worker G it was // just using, ensuring work can complete. - return nil + return nil, now } - decIfPositive := func(ptr *int64) bool { + decIfPositive := func(val *atomic.Int64) bool { for { - v := atomic.Loadint64(ptr) + v := val.Load() if v <= 0 { return false } - if atomic.Casint64(ptr, v, v-1) { + if val.CompareAndSwap(v, v-1) { return true } } @@ -607,24 +843,24 @@ func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g { if decIfPositive(&c.dedicatedMarkWorkersNeeded) { // This P is now dedicated to marking until the end of // the concurrent mark phase. - _p_.gcMarkWorkerMode = gcMarkWorkerDedicatedMode + pp.gcMarkWorkerMode = gcMarkWorkerDedicatedMode } else if c.fractionalUtilizationGoal == 0 { // No need for fractional workers. gcBgMarkWorkerPool.push(&node.node) - return nil + return nil, now } else { // Is this P behind on the fractional utilization // goal? // // This should be kept in sync with pollFractionalWorkerExit. - delta := nanotime() - c.markStartTime - if delta > 0 && float64(_p_.gcFractionalMarkTime)/float64(delta) > c.fractionalUtilizationGoal { + delta := now - c.markStartTime + if delta > 0 && float64(pp.gcFractionalMarkTime)/float64(delta) > c.fractionalUtilizationGoal { // Nope. No need to run a fractional worker. gcBgMarkWorkerPool.push(&node.node) - return nil + return nil, now } // Run a fractional worker. - _p_.gcMarkWorkerMode = gcMarkWorkerFractionalMode + pp.gcMarkWorkerMode = gcMarkWorkerFractionalMode } // Run the background mark worker. @@ -633,187 +869,434 @@ func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g { if trace.enabled { traceGoUnpark(gp, 0) } - return gp + return gp, now } -// commit sets the trigger ratio and updates everything -// derived from it: the absolute trigger, the heap goal, mark pacing, -// and sweep pacing. +// resetLive sets up the controller state for the next mark phase after the end +// of the previous one. Must be called after endCycle and before commit, before +// the world is started. // -// This can be called any time. If GC is the in the middle of a -// concurrent phase, it will adjust the pacing of that phase. +// The world must be stopped. +func (c *gcControllerState) resetLive(bytesMarked uint64) { + c.heapMarked = bytesMarked + c.heapLive.Store(bytesMarked) + c.heapScan.Store(uint64(c.heapScanWork.Load())) + c.lastHeapScan = uint64(c.heapScanWork.Load()) + c.lastStackScan.Store(uint64(c.stackScanWork.Load())) + c.triggered = ^uint64(0) // Reset triggered. + + // heapLive was updated, so emit a trace event. + if trace.enabled { + traceHeapAlloc(bytesMarked) + } +} + +// markWorkerStop must be called whenever a mark worker stops executing. // -// This depends on gcPercent, gcController.heapMarked, and -// gcController.heapLive. These must be up to date. +// It updates mark work accounting in the controller by a duration of +// work in nanoseconds and other bookkeeping. // -// mheap_.lock must be held or the world must be stopped. -func (c *gcControllerState) commit(triggerRatio float64) { - assertWorldStoppedOrLockHeld(&mheap_.lock) - - // Compute the next GC goal, which is when the allocated heap - // has grown by GOGC/100 over the heap marked by the last - // cycle. - goal := ^uint64(0) - if c.gcPercent >= 0 { - goal = c.heapMarked + c.heapMarked*uint64(c.gcPercent)/100 - } - - // Set the trigger ratio, capped to reasonable bounds. - if c.gcPercent >= 0 { - scalingFactor := float64(c.gcPercent) / 100 - // Ensure there's always a little margin so that the - // mutator assist ratio isn't infinity. - maxTriggerRatio := 0.95 * scalingFactor - if triggerRatio > maxTriggerRatio { - triggerRatio = maxTriggerRatio - } +// Safe to execute at any time. +func (c *gcControllerState) markWorkerStop(mode gcMarkWorkerMode, duration int64) { + switch mode { + case gcMarkWorkerDedicatedMode: + c.dedicatedMarkTime.Add(duration) + c.dedicatedMarkWorkersNeeded.Add(1) + case gcMarkWorkerFractionalMode: + c.fractionalMarkTime.Add(duration) + case gcMarkWorkerIdleMode: + c.idleMarkTime.Add(duration) + c.removeIdleMarkWorker() + default: + throw("markWorkerStop: unknown mark worker mode") + } +} - // If we let triggerRatio go too low, then if the application - // is allocating very rapidly we might end up in a situation - // where we're allocating black during a nearly always-on GC. - // The result of this is a growing heap and ultimately an - // increase in RSS. By capping us at a point >0, we're essentially - // saying that we're OK using more CPU during the GC to prevent - // this growth in RSS. - // - // The current constant was chosen empirically: given a sufficiently - // fast/scalable allocator with 48 Ps that could drive the trigger ratio - // to <0.05, this constant causes applications to retain the same peak - // RSS compared to not having this allocator. - minTriggerRatio := 0.6 * scalingFactor - if triggerRatio < minTriggerRatio { - triggerRatio = minTriggerRatio +func (c *gcControllerState) update(dHeapLive, dHeapScan int64) { + if dHeapLive != 0 { + live := gcController.heapLive.Add(dHeapLive) + if trace.enabled { + // gcController.heapLive changed. + traceHeapAlloc(live) } - } else if triggerRatio < 0 { - // gcPercent < 0, so just make sure we're not getting a negative - // triggerRatio. This case isn't expected to happen in practice, - // and doesn't really matter because if gcPercent < 0 then we won't - // ever consume triggerRatio further on in this function, but let's - // just be defensive here; the triggerRatio being negative is almost - // certainly undesirable. - triggerRatio = 0 - } - c.triggerRatio = triggerRatio - - // Compute the absolute GC trigger from the trigger ratio. - // - // We trigger the next GC cycle when the allocated heap has - // grown by the trigger ratio over the marked heap size. - trigger := ^uint64(0) - if c.gcPercent >= 0 { - trigger = uint64(float64(c.heapMarked) * (1 + triggerRatio)) - // Don't trigger below the minimum heap size. - minTrigger := c.heapMinimum - if !isSweepDone() { - // Concurrent sweep happens in the heap growth - // from gcController.heapLive to trigger, so ensure - // that concurrent sweep has some heap growth - // in which to perform sweeping before we - // start the next GC cycle. - sweepMin := atomic.Load64(&c.heapLive) + sweepMinHeapDistance - if sweepMin > minTrigger { - minTrigger = sweepMin - } + } + if gcBlackenEnabled == 0 { + // Update heapScan when we're not in a current GC. It is fixed + // at the beginning of a cycle. + if dHeapScan != 0 { + gcController.heapScan.Add(dHeapScan) } - if trigger < minTrigger { - trigger = minTrigger + } else { + // gcController.heapLive changed. + c.revise() + } +} + +func (c *gcControllerState) addScannableStack(pp *p, amount int64) { + if pp == nil { + c.maxStackScan.Add(amount) + return + } + pp.maxStackScanDelta += amount + if pp.maxStackScanDelta >= maxStackScanSlack || pp.maxStackScanDelta <= -maxStackScanSlack { + c.maxStackScan.Add(pp.maxStackScanDelta) + pp.maxStackScanDelta = 0 + } +} + +func (c *gcControllerState) addGlobals(amount int64) { + c.globalsScan.Add(amount) +} + +// heapGoal returns the current heap goal. +func (c *gcControllerState) heapGoal() uint64 { + goal, _ := c.heapGoalInternal() + return goal +} + +// heapGoalInternal is the implementation of heapGoal which returns additional +// information that is necessary for computing the trigger. +// +// The returned minTrigger is always <= goal. +func (c *gcControllerState) heapGoalInternal() (goal, minTrigger uint64) { + // Start with the goal calculated for gcPercent. + goal = c.gcPercentHeapGoal.Load() + + // Check if the memory-limit-based goal is smaller, and if so, pick that. + if newGoal := c.memoryLimitHeapGoal(); go119MemoryLimitSupport && newGoal < goal { + goal = newGoal + } else { + // We're not limited by the memory limit goal, so perform a series of + // adjustments that might move the goal forward in a variety of circumstances. + + sweepDistTrigger := c.sweepDistMinTrigger.Load() + if sweepDistTrigger > goal { + // Set the goal to maintain a minimum sweep distance since + // the last call to commit. Note that we never want to do this + // if we're in the memory limit regime, because it could push + // the goal up. + goal = sweepDistTrigger } - if int64(trigger) < 0 { - print("runtime: heapGoal=", c.heapGoal, " heapMarked=", c.heapMarked, " gcController.heapLive=", c.heapLive, " initialHeapLive=", work.initialHeapLive, "triggerRatio=", triggerRatio, " minTrigger=", minTrigger, "\n") - throw("trigger underflow") + // Since we ignore the sweep distance trigger in the memory + // limit regime, we need to ensure we don't propagate it to + // the trigger, because it could cause a violation of the + // invariant that the trigger < goal. + minTrigger = sweepDistTrigger + + // Ensure that the heap goal is at least a little larger than + // the point at which we triggered. This may not be the case if GC + // start is delayed or if the allocation that pushed gcController.heapLive + // over trigger is large or if the trigger is really close to + // GOGC. Assist is proportional to this distance, so enforce a + // minimum distance, even if it means going over the GOGC goal + // by a tiny bit. + // + // Ignore this if we're in the memory limit regime: we'd prefer to + // have the GC respond hard about how close we are to the goal than to + // push the goal back in such a manner that it could cause us to exceed + // the memory limit. + const minRunway = 64 << 10 + if c.triggered != ^uint64(0) && goal < c.triggered+minRunway { + goal = c.triggered + minRunway } - if trigger > goal { - // The trigger ratio is always less than GOGC/100, but - // other bounds on the trigger may have raised it. - // Push up the goal, too. - goal = trigger + } + return +} + +// memoryLimitHeapGoal returns a heap goal derived from memoryLimit. +func (c *gcControllerState) memoryLimitHeapGoal() uint64 { + // Start by pulling out some values we'll need. Be careful about overflow. + var heapFree, heapAlloc, mappedReady uint64 + for { + heapFree = c.heapFree.load() // Free and unscavenged memory. + heapAlloc = c.totalAlloc.Load() - c.totalFree.Load() // Heap object bytes in use. + mappedReady = c.mappedReady.Load() // Total unreleased mapped memory. + if heapFree+heapAlloc <= mappedReady { + break } + // It is impossible for total unreleased mapped memory to exceed heap memory, but + // because these stats are updated independently, we may observe a partial update + // including only some values. Thus, we appear to break the invariant. However, + // this condition is necessarily transient, so just try again. In the case of a + // persistent accounting error, we'll deadlock here. } - // Commit to the trigger and goal. - c.trigger = trigger - atomic.Store64(&c.heapGoal, goal) - if trace.enabled { - traceHeapGoal() + // Below we compute a goal from memoryLimit. There are a few things to be aware of. + // Firstly, the memoryLimit does not easily compare to the heap goal: the former + // is total mapped memory by the runtime that hasn't been released, while the latter is + // only heap object memory. Intuitively, the way we convert from one to the other is to + // subtract everything from memoryLimit that both contributes to the memory limit (so, + // ignore scavenged memory) and doesn't contain heap objects. This isn't quite what + // lines up with reality, but it's a good starting point. + // + // In practice this computation looks like the following: + // + // memoryLimit - ((mappedReady - heapFree - heapAlloc) + max(mappedReady - memoryLimit, 0)) - memoryLimitHeapGoalHeadroom + // ^1 ^2 ^3 + // + // Let's break this down. + // + // The first term (marker 1) is everything that contributes to the memory limit and isn't + // or couldn't become heap objects. It represents, broadly speaking, non-heap overheads. + // One oddity you may have noticed is that we also subtract out heapFree, i.e. unscavenged + // memory that may contain heap objects in the future. + // + // Let's take a step back. In an ideal world, this term would look something like just + // the heap goal. That is, we "reserve" enough space for the heap to grow to the heap + // goal, and subtract out everything else. This is of course impossible; the definition + // is circular! However, this impossible definition contains a key insight: the amount + // we're *going* to use matters just as much as whatever we're currently using. + // + // Consider if the heap shrinks to 1/10th its size, leaving behind lots of free and + // unscavenged memory. mappedReady - heapAlloc will be quite large, because of that free + // and unscavenged memory, pushing the goal down significantly. + // + // heapFree is also safe to exclude from the memory limit because in the steady-state, it's + // just a pool of memory for future heap allocations, and making new allocations from heapFree + // memory doesn't increase overall memory use. In transient states, the scavenger and the + // allocator actively manage the pool of heapFree memory to maintain the memory limit. + // + // The second term (marker 2) is the amount of memory we've exceeded the limit by, and is + // intended to help recover from such a situation. By pushing the heap goal down, we also + // push the trigger down, triggering and finishing a GC sooner in order to make room for + // other memory sources. Note that since we're effectively reducing the heap goal by X bytes, + // we're actually giving more than X bytes of headroom back, because the heap goal is in + // terms of heap objects, but it takes more than X bytes (e.g. due to fragmentation) to store + // X bytes worth of objects. + // + // The third term (marker 3) subtracts an additional memoryLimitHeapGoalHeadroom bytes from the + // heap goal. As the name implies, this is to provide additional headroom in the face of pacing + // inaccuracies. This is a fixed number of bytes because these inaccuracies disproportionately + // affect small heaps: as heaps get smaller, the pacer's inputs get fuzzier. Shorter GC cycles + // and less GC work means noisy external factors like the OS scheduler have a greater impact. + + memoryLimit := uint64(c.memoryLimit.Load()) + + // Compute term 1. + nonHeapMemory := mappedReady - heapFree - heapAlloc + + // Compute term 2. + var overage uint64 + if mappedReady > memoryLimit { + overage = mappedReady - memoryLimit } - // Update mark pacing. - if gcphase != _GCoff { - c.revise() + if nonHeapMemory+overage >= memoryLimit { + // We're at a point where non-heap memory exceeds the memory limit on its own. + // There's honestly not much we can do here but just trigger GCs continuously + // and let the CPU limiter reign that in. Something has to give at this point. + // Set it to heapMarked, the lowest possible goal. + return c.heapMarked } - // Update sweep pacing. - if isSweepDone() { - mheap_.sweepPagesPerByte = 0 + // Compute the goal. + goal := memoryLimit - (nonHeapMemory + overage) + + // Apply some headroom to the goal to account for pacing inaccuracies. + // Be careful about small limits. + if goal < memoryLimitHeapGoalHeadroom || goal-memoryLimitHeapGoalHeadroom < memoryLimitHeapGoalHeadroom { + goal = memoryLimitHeapGoalHeadroom } else { - // Concurrent sweep needs to sweep all of the in-use - // pages by the time the allocated heap reaches the GC - // trigger. Compute the ratio of in-use pages to sweep - // per byte allocated, accounting for the fact that - // some might already be swept. - heapLiveBasis := atomic.Load64(&c.heapLive) - heapDistance := int64(trigger) - int64(heapLiveBasis) - // Add a little margin so rounding errors and - // concurrent sweep are less likely to leave pages - // unswept when GC starts. - heapDistance -= 1024 * 1024 - if heapDistance < _PageSize { - // Avoid setting the sweep ratio extremely high - heapDistance = _PageSize - } - pagesSwept := atomic.Load64(&mheap_.pagesSwept) - pagesInUse := atomic.Load64(&mheap_.pagesInUse) - sweepDistancePages := int64(pagesInUse) - int64(pagesSwept) - if sweepDistancePages <= 0 { - mheap_.sweepPagesPerByte = 0 - } else { - mheap_.sweepPagesPerByte = float64(sweepDistancePages) / float64(heapDistance) - mheap_.sweepHeapLiveBasis = heapLiveBasis - // Write pagesSweptBasis last, since this - // signals concurrent sweeps to recompute - // their debt. - atomic.Store64(&mheap_.pagesSweptBasis, pagesSwept) - } + goal = goal - memoryLimitHeapGoalHeadroom } + // Don't let us go below the live heap. A heap goal below the live heap doesn't make sense. + if goal < c.heapMarked { + goal = c.heapMarked + } + return goal +} - gcPaceScavenger() +const ( + // These constants determine the bounds on the GC trigger as a fraction + // of heap bytes allocated between the start of a GC (heapLive == heapMarked) + // and the end of a GC (heapLive == heapGoal). + // + // The constants are obscured in this way for efficiency. The denominator + // of the fraction is always a power-of-two for a quick division, so that + // the numerator is a single constant integer multiplication. + triggerRatioDen = 64 + + // The minimum trigger constant was chosen empirically: given a sufficiently + // fast/scalable allocator with 48 Ps that could drive the trigger ratio + // to <0.05, this constant causes applications to retain the same peak + // RSS compared to not having this allocator. + minTriggerRatioNum = 45 // ~0.7 + + // The maximum trigger constant is chosen somewhat arbitrarily, but the + // current constant has served us well over the years. + maxTriggerRatioNum = 61 // ~0.95 +) + +// trigger returns the current point at which a GC should trigger along with +// the heap goal. +// +// The returned value may be compared against heapLive to determine whether +// the GC should trigger. Thus, the GC trigger condition should be (but may +// not be, in the case of small movements for efficiency) checked whenever +// the heap goal may change. +func (c *gcControllerState) trigger() (uint64, uint64) { + goal, minTrigger := c.heapGoalInternal() + + // Invariant: the trigger must always be less than the heap goal. + // + // Note that the memory limit sets a hard maximum on our heap goal, + // but the live heap may grow beyond it. + + if c.heapMarked >= goal { + // The goal should never be smaller than heapMarked, but let's be + // defensive about it. The only reasonable trigger here is one that + // causes a continuous GC cycle at heapMarked, but respect the goal + // if it came out as smaller than that. + return goal, goal + } + + // Below this point, c.heapMarked < goal. + + // heapMarked is our absolute minimum, and it's possible the trigger + // bound we get from heapGoalinternal is less than that. + if minTrigger < c.heapMarked { + minTrigger = c.heapMarked + } + + // If we let the trigger go too low, then if the application + // is allocating very rapidly we might end up in a situation + // where we're allocating black during a nearly always-on GC. + // The result of this is a growing heap and ultimately an + // increase in RSS. By capping us at a point >0, we're essentially + // saying that we're OK using more CPU during the GC to prevent + // this growth in RSS. + triggerLowerBound := uint64(((goal-c.heapMarked)/triggerRatioDen)*minTriggerRatioNum) + c.heapMarked + if minTrigger < triggerLowerBound { + minTrigger = triggerLowerBound + } + + // For small heaps, set the max trigger point at maxTriggerRatio of the way + // from the live heap to the heap goal. This ensures we always have *some* + // headroom when the GC actually starts. For larger heaps, set the max trigger + // point at the goal, minus the minimum heap size. + // + // This choice follows from the fact that the minimum heap size is chosen + // to reflect the costs of a GC with no work to do. With a large heap but + // very little scan work to perform, this gives us exactly as much runway + // as we would need, in the worst case. + maxTrigger := uint64(((goal-c.heapMarked)/triggerRatioDen)*maxTriggerRatioNum) + c.heapMarked + if goal > defaultHeapMinimum && goal-defaultHeapMinimum > maxTrigger { + maxTrigger = goal - defaultHeapMinimum + } + if maxTrigger < minTrigger { + maxTrigger = minTrigger + } + + // Compute the trigger from our bounds and the runway stored by commit. + var trigger uint64 + runway := c.runway.Load() + if runway > goal { + trigger = minTrigger + } else { + trigger = goal - runway + } + if trigger < minTrigger { + trigger = minTrigger + } + if trigger > maxTrigger { + trigger = maxTrigger + } + if trigger > goal { + print("trigger=", trigger, " heapGoal=", goal, "\n") + print("minTrigger=", minTrigger, " maxTrigger=", maxTrigger, "\n") + throw("produced a trigger greater than the heap goal") + } + return trigger, goal } -// effectiveGrowthRatio returns the current effective heap growth -// ratio (GOGC/100) based on heapMarked from the previous GC and -// heapGoal for the current GC. +// commit recomputes all pacing parameters needed to derive the +// trigger and the heap goal. Namely, the gcPercent-based heap goal, +// and the amount of runway we want to give the GC this cycle. // -// This may differ from gcPercent/100 because of various upper and -// lower bounds on gcPercent. For example, if the heap is smaller than -// heapMinimum, this can be higher than gcPercent/100. +// This can be called any time. If GC is the in the middle of a +// concurrent phase, it will adjust the pacing of that phase. +// +// isSweepDone should be the result of calling isSweepDone(), +// unless we're testing or we know we're executing during a GC cycle. +// +// This depends on gcPercent, gcController.heapMarked, and +// gcController.heapLive. These must be up to date. +// +// Callers must call gcControllerState.revise after calling this +// function if the GC is enabled. // // mheap_.lock must be held or the world must be stopped. -func (c *gcControllerState) effectiveGrowthRatio() float64 { - assertWorldStoppedOrLockHeld(&mheap_.lock) +func (c *gcControllerState) commit(isSweepDone bool) { + if !c.test { + assertWorldStoppedOrLockHeld(&mheap_.lock) + } - egogc := float64(atomic.Load64(&c.heapGoal)-c.heapMarked) / float64(c.heapMarked) - if egogc < 0 { - // Shouldn't happen, but just in case. - egogc = 0 + if isSweepDone { + // The sweep is done, so there aren't any restrictions on the trigger + // we need to think about. + c.sweepDistMinTrigger.Store(0) + } else { + // Concurrent sweep happens in the heap growth + // from gcController.heapLive to trigger. Make sure we + // give the sweeper some runway if it doesn't have enough. + c.sweepDistMinTrigger.Store(c.heapLive.Load() + sweepMinHeapDistance) } - return egogc + + // Compute the next GC goal, which is when the allocated heap + // has grown by GOGC/100 over where it started the last cycle, + // plus additional runway for non-heap sources of GC work. + gcPercentHeapGoal := ^uint64(0) + if gcPercent := c.gcPercent.Load(); gcPercent >= 0 { + gcPercentHeapGoal = c.heapMarked + (c.heapMarked+c.lastStackScan.Load()+c.globalsScan.Load())*uint64(gcPercent)/100 + } + // Apply the minimum heap size here. It's defined in terms of gcPercent + // and is only updated by functions that call commit. + if gcPercentHeapGoal < c.heapMinimum { + gcPercentHeapGoal = c.heapMinimum + } + c.gcPercentHeapGoal.Store(gcPercentHeapGoal) + + // Compute the amount of runway we want the GC to have by using our + // estimate of the cons/mark ratio. + // + // The idea is to take our expected scan work, and multiply it by + // the cons/mark ratio to determine how long it'll take to complete + // that scan work in terms of bytes allocated. This gives us our GC's + // runway. + // + // However, the cons/mark ratio is a ratio of rates per CPU-second, but + // here we care about the relative rates for some division of CPU + // resources among the mutator and the GC. + // + // To summarize, we have B / cpu-ns, and we want B / ns. We get that + // by multiplying by our desired division of CPU resources. We choose + // to express CPU resources as GOMAPROCS*fraction. Note that because + // we're working with a ratio here, we can omit the number of CPU cores, + // because they'll appear in the numerator and denominator and cancel out. + // As a result, this is basically just "weighing" the cons/mark ratio by + // our desired division of resources. + // + // Furthermore, by setting the runway so that CPU resources are divided + // this way, assuming that the cons/mark ratio is correct, we make that + // division a reality. + c.runway.Store(uint64((c.consMark * (1 - gcGoalUtilization) / (gcGoalUtilization)) * float64(c.lastHeapScan+c.lastStackScan.Load()+c.globalsScan.Load()))) } -// setGCPercent updates gcPercent and all related pacer state. +// setGCPercent updates gcPercent. commit must be called after. // Returns the old value of gcPercent. // // The world must be stopped, or mheap_.lock must be held. func (c *gcControllerState) setGCPercent(in int32) int32 { - assertWorldStoppedOrLockHeld(&mheap_.lock) + if !c.test { + assertWorldStoppedOrLockHeld(&mheap_.lock) + } - out := c.gcPercent + out := c.gcPercent.Load() if in < 0 { in = -1 } - c.gcPercent = in - c.heapMinimum = defaultHeapMinimum * uint64(c.gcPercent) / 100 - // Update pacing in response to gcPercent change. - c.commit(c.triggerRatio) + c.heapMinimum = defaultHeapMinimum * uint64(in) / 100 + c.gcPercent.Store(in) return out } @@ -824,13 +1307,14 @@ func setGCPercent(in int32) (out int32) { systemstack(func() { lock(&mheap_.lock) out = gcController.setGCPercent(in) + gcControllerCommit() unlock(&mheap_.lock) }) // If we just disabled GC, wait for any concurrent GC mark to // finish so we always return with no GC running. if in < 0 { - gcWaitOnMark(atomic.Load(&work.cycles)) + gcWaitOnMark(work.cycles.Load()) } return out @@ -846,3 +1330,229 @@ func readGOGC() int32 { } return 100 } + +// setMemoryLimit updates memoryLimit. commit must be called after +// Returns the old value of memoryLimit. +// +// The world must be stopped, or mheap_.lock must be held. +func (c *gcControllerState) setMemoryLimit(in int64) int64 { + if !c.test { + assertWorldStoppedOrLockHeld(&mheap_.lock) + } + + out := c.memoryLimit.Load() + if in >= 0 { + c.memoryLimit.Store(in) + } + + return out +} + +//go:linkname setMemoryLimit runtime/debug.setMemoryLimit +func setMemoryLimit(in int64) (out int64) { + // Run on the system stack since we grab the heap lock. + systemstack(func() { + lock(&mheap_.lock) + out = gcController.setMemoryLimit(in) + if in < 0 || out == in { + // If we're just checking the value or not changing + // it, there's no point in doing the rest. + unlock(&mheap_.lock) + return + } + gcControllerCommit() + unlock(&mheap_.lock) + }) + return out +} + +func readGOMEMLIMIT() int64 { + p := gogetenv("GOMEMLIMIT") + if p == "" || p == "off" { + return maxInt64 + } + n, ok := parseByteCount(p) + if !ok { + print("GOMEMLIMIT=", p, "\n") + throw("malformed GOMEMLIMIT; see `go doc runtime/debug.SetMemoryLimit`") + } + return n +} + +type piController struct { + kp float64 // Proportional constant. + ti float64 // Integral time constant. + tt float64 // Reset time. + + min, max float64 // Output boundaries. + + // PI controller state. + + errIntegral float64 // Integral of the error from t=0 to now. + + // Error flags. + errOverflow bool // Set if errIntegral ever overflowed. + inputOverflow bool // Set if an operation with the input overflowed. +} + +// next provides a new sample to the controller. +// +// input is the sample, setpoint is the desired point, and period is how much +// time (in whatever unit makes the most sense) has passed since the last sample. +// +// Returns a new value for the variable it's controlling, and whether the operation +// completed successfully. One reason this might fail is if error has been growing +// in an unbounded manner, to the point of overflow. +// +// In the specific case of an error overflow occurs, the errOverflow field will be +// set and the rest of the controller's internal state will be fully reset. +func (c *piController) next(input, setpoint, period float64) (float64, bool) { + // Compute the raw output value. + prop := c.kp * (setpoint - input) + rawOutput := prop + c.errIntegral + + // Clamp rawOutput into output. + output := rawOutput + if isInf(output) || isNaN(output) { + // The input had a large enough magnitude that either it was already + // overflowed, or some operation with it overflowed. + // Set a flag and reset. That's the safest thing to do. + c.reset() + c.inputOverflow = true + return c.min, false + } + if output < c.min { + output = c.min + } else if output > c.max { + output = c.max + } + + // Update the controller's state. + if c.ti != 0 && c.tt != 0 { + c.errIntegral += (c.kp*period/c.ti)*(setpoint-input) + (period/c.tt)*(output-rawOutput) + if isInf(c.errIntegral) || isNaN(c.errIntegral) { + // So much error has accumulated that we managed to overflow. + // The assumptions around the controller have likely broken down. + // Set a flag and reset. That's the safest thing to do. + c.reset() + c.errOverflow = true + return c.min, false + } + } + return output, true +} + +// reset resets the controller state, except for controller error flags. +func (c *piController) reset() { + c.errIntegral = 0 +} + +// addIdleMarkWorker attempts to add a new idle mark worker. +// +// If this returns true, the caller must become an idle mark worker unless +// there's no background mark worker goroutines in the pool. This case is +// harmless because there are already background mark workers running. +// If this returns false, the caller must NOT become an idle mark worker. +// +// nosplit because it may be called without a P. +// +//go:nosplit +func (c *gcControllerState) addIdleMarkWorker() bool { + for { + old := c.idleMarkWorkers.Load() + n, max := int32(old&uint64(^uint32(0))), int32(old>>32) + if n >= max { + // See the comment on idleMarkWorkers for why + // n > max is tolerated. + return false + } + if n < 0 { + print("n=", n, " max=", max, "\n") + throw("negative idle mark workers") + } + new := uint64(uint32(n+1)) | (uint64(max) << 32) + if c.idleMarkWorkers.CompareAndSwap(old, new) { + return true + } + } +} + +// needIdleMarkWorker is a hint as to whether another idle mark worker is needed. +// +// The caller must still call addIdleMarkWorker to become one. This is mainly +// useful for a quick check before an expensive operation. +// +// nosplit because it may be called without a P. +// +//go:nosplit +func (c *gcControllerState) needIdleMarkWorker() bool { + p := c.idleMarkWorkers.Load() + n, max := int32(p&uint64(^uint32(0))), int32(p>>32) + return n < max +} + +// removeIdleMarkWorker must be called when an new idle mark worker stops executing. +func (c *gcControllerState) removeIdleMarkWorker() { + for { + old := c.idleMarkWorkers.Load() + n, max := int32(old&uint64(^uint32(0))), int32(old>>32) + if n-1 < 0 { + print("n=", n, " max=", max, "\n") + throw("negative idle mark workers") + } + new := uint64(uint32(n-1)) | (uint64(max) << 32) + if c.idleMarkWorkers.CompareAndSwap(old, new) { + return + } + } +} + +// setMaxIdleMarkWorkers sets the maximum number of idle mark workers allowed. +// +// This method is optimistic in that it does not wait for the number of +// idle mark workers to reduce to max before returning; it assumes the workers +// will deschedule themselves. +func (c *gcControllerState) setMaxIdleMarkWorkers(max int32) { + for { + old := c.idleMarkWorkers.Load() + n := int32(old & uint64(^uint32(0))) + if n < 0 { + print("n=", n, " max=", max, "\n") + throw("negative idle mark workers") + } + new := uint64(uint32(n)) | (uint64(max) << 32) + if c.idleMarkWorkers.CompareAndSwap(old, new) { + return + } + } +} + +// gcControllerCommit is gcController.commit, but passes arguments from live +// (non-test) data. It also updates any consumers of the GC pacing, such as +// sweep pacing and the background scavenger. +// +// Calls gcController.commit. +// +// The heap lock must be held, so this must be executed on the system stack. +// +//go:systemstack +func gcControllerCommit() { + assertWorldStoppedOrLockHeld(&mheap_.lock) + + gcController.commit(isSweepDone()) + + // Update mark pacing. + if gcphase != _GCoff { + gcController.revise() + } + + // TODO(mknyszek): This isn't really accurate any longer because the heap + // goal is computed dynamically. Still useful to snapshot, but not as useful. + if trace.enabled { + traceHeapGoal() + } + + trigger, heapGoal := gcController.trigger() + gcPaceSweeper(trigger) + gcPaceScavenger(gcController.memoryLimit.Load(), heapGoal, gcController.lastHeapGoal) +} diff --git a/src/runtime/mgcpacer_test.go b/src/runtime/mgcpacer_test.go new file mode 100644 index 00000000000000..12d885de1256e2 --- /dev/null +++ b/src/runtime/mgcpacer_test.go @@ -0,0 +1,1129 @@ +// Copyright 2021 The Go Authors. 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 ( + "fmt" + "math" + "math/rand" + . "runtime" + "testing" + "time" +) + +func TestGcPacer(t *testing.T) { + t.Parallel() + + const initialHeapBytes = 256 << 10 + for _, e := range []*gcExecTest{ + { + // The most basic test case: a steady-state heap. + // Growth to an O(MiB) heap, then constant heap size, alloc/scan rates. + name: "Steady", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n >= 25 { + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // Same as the steady-state case, but lots of stacks to scan relative to the heap size. + name: "SteadyBigStacks", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(132.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(2048).sum(ramp(128<<20, 8)), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + // Check the same conditions as the steady-state case, except the old pacer can't + // really handle this well, so don't check the goal ratio for it. + n := len(c) + if n >= 25 { + // For the pacer redesign, assert something even stronger: at this alloc/scan rate, + // it should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + } + }, + }, + { + // Same as the steady-state case, but lots of globals to scan relative to the heap size. + name: "SteadyBigGlobals", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 128 << 20, + nCores: 8, + allocRate: constant(132.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + // Check the same conditions as the steady-state case, except the old pacer can't + // really handle this well, so don't check the goal ratio for it. + n := len(c) + if n >= 25 { + // For the pacer redesign, assert something even stronger: at this alloc/scan rate, + // it should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + } + }, + }, + { + // This tests the GC pacer's response to a small change in allocation rate. + name: "StepAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0).sum(ramp(66.0, 1).delay(50)), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 100, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if (n >= 25 && n < 50) || n >= 75 { + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles + // and then is able to settle again after a significant jump in allocation rate. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // This tests the GC pacer's response to a large change in allocation rate. + name: "HeavyStepAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33).sum(ramp(330, 1).delay(50)), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 100, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if (n >= 25 && n < 50) || n >= 75 { + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles + // and then is able to settle again after a significant jump in allocation rate. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // This tests the GC pacer's response to a change in the fraction of the scannable heap. + name: "StepScannableFrac", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(128.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(0.2).sum(unit(0.5).delay(50)), + stackBytes: constant(8192), + length: 100, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if (n >= 25 && n < 50) || n >= 75 { + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles + // and then is able to settle again after a significant jump in allocation rate. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // Tests the pacer for a high GOGC value with a large heap growth happening + // in the middle. The purpose of the large heap growth is to check if GC + // utilization ends up sensitive + name: "HighGOGC", + gcPercent: 1500, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: random(7, 0x53).offset(165), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12), random(0.01, 0x1), unit(14).delay(25)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 12 { + if n == 26 { + // In the 26th cycle there's a heap growth. Overshoot is expected to maintain + // a stable utilization, but we should *never* overshoot more than GOGC of + // the next cycle. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.90, 15) + } else { + // Give a wider goal range here. With such a high GOGC value we're going to be + // forced to undershoot. + // + // TODO(mknyszek): Instead of placing a 0.95 limit on the trigger, make the limit + // based on absolute bytes, that's based somewhat in how the minimum heap size + // is determined. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.90, 1.05) + } + + // Ensure utilization remains stable despite a growth in live heap size + // at GC #25. This test fails prior to the GC pacer redesign. + // + // Because GOGC is so large, we should also be really close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, GCGoalUtilization+0.03) + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.03) + } + }, + }, + { + // This test makes sure that in the face of a varying (in this case, oscillating) allocation + // rate, the pacer does a reasonably good job of staying abreast of the changes. + name: "OscAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: oscillate(13, 0, 8).offset(67), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 12 { + // After the 12th GC, the heap will stop growing. Now, just make sure that: + // 1. Utilization isn't varying _too_ much, and + // 2. The pacer is mostly keeping up with the goal. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) + } + }, + }, + { + // This test is the same as OscAlloc, but instead of oscillating, the allocation rate is jittery. + name: "JitterAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: random(13, 0xf).offset(132), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12), random(0.01, 0xe)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 12 { + // After the 12th GC, the heap will stop growing. Now, just make sure that: + // 1. Utilization isn't varying _too_ much, and + // 2. The pacer is mostly keeping up with the goal. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) + } + }, + }, + { + // This test is the same as JitterAlloc, but with a much higher allocation rate. + // The jitter is proportionally the same. + name: "HeavyJitterAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: random(33.0, 0x0).offset(330), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12), random(0.01, 0x152)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 13 { + // After the 12th GC, the heap will stop growing. Now, just make sure that: + // 1. Utilization isn't varying _too_ much, and + // 2. The pacer is mostly keeping up with the goal. + // We start at the 13th here because we want to use the 12th as a reference. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + // Unlike the other tests, GC utilization here will vary more and tend higher. + // Just make sure it's not going too crazy. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.05) + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) + } + }, + }, + { + // This test sets a slow allocation rate and a small heap (close to the minimum heap size) + // to try to minimize the difference between the trigger and the goal. + name: "SmallHeapSlowAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(1.0), + scanRate: constant(2048.0), + growthRate: constant(2.0).sum(ramp(-1.0, 3)), + scannableFrac: constant(0.01), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 4 { + // After the 4th GC, the heap will stop growing. + // First, let's make sure we're finishing near the goal, with some extra + // room because we're probably going to be triggering early. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.925, 1.025) + // Next, let's make sure there's some minimum distance between the goal + // and the trigger. It should be proportional to the runway (hence the + // trigger ratio check, instead of a check against the runway). + assertInRange(t, "trigger ratio", c[n-1].triggerRatio(), 0.925, 0.975) + } + if n > 25 { + // Double-check that GC utilization looks OK. + + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + // Make sure GC utilization has mostly levelled off. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.05) + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) + } + }, + }, + { + // This test sets a slow allocation rate and a medium heap (around 10x the min heap size) + // to try to minimize the difference between the trigger and the goal. + name: "MediumHeapSlowAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(1.0), + scanRate: constant(2048.0), + growthRate: constant(2.0).sum(ramp(-1.0, 8)), + scannableFrac: constant(0.01), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 9 { + // After the 4th GC, the heap will stop growing. + // First, let's make sure we're finishing near the goal, with some extra + // room because we're probably going to be triggering early. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.925, 1.025) + // Next, let's make sure there's some minimum distance between the goal + // and the trigger. It should be proportional to the runway (hence the + // trigger ratio check, instead of a check against the runway). + assertInRange(t, "trigger ratio", c[n-1].triggerRatio(), 0.925, 0.975) + } + if n > 25 { + // Double-check that GC utilization looks OK. + + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + // Make sure GC utilization has mostly levelled off. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.05) + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) + } + }, + }, + { + // This test sets a slow allocation rate and a large heap to try to minimize the + // difference between the trigger and the goal. + name: "LargeHeapSlowAlloc", + gcPercent: 100, + memoryLimit: math.MaxInt64, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(1.0), + scanRate: constant(2048.0), + growthRate: constant(4.0).sum(ramp(-3.0, 12)), + scannableFrac: constant(0.01), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 13 { + // After the 4th GC, the heap will stop growing. + // First, let's make sure we're finishing near the goal. + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + // Next, let's make sure there's some minimum distance between the goal + // and the trigger. It should be around the default minimum heap size. + assertInRange(t, "runway", c[n-1].runway(), DefaultHeapMinimum-64<<10, DefaultHeapMinimum+64<<10) + } + if n > 25 { + // Double-check that GC utilization looks OK. + + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + // Make sure GC utilization has mostly levelled off. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.05) + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) + } + }, + }, + { + // The most basic test case with a memory limit: a steady-state heap. + // Growth to an O(MiB) heap, then constant heap size, alloc/scan rates. + // Provide a lot of room for the limit. Essentially, this should behave just like + // the "Steady" test. Note that we don't simulate non-heap overheads, so the + // memory limit and the heap limit are identical. + name: "SteadyMemoryLimit", + gcPercent: 100, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if peak := c[n-1].heapPeak; peak >= (512<<20)-MemoryLimitHeapGoalHeadroom { + t.Errorf("peak heap size reaches heap limit: %d", peak) + } + if n >= 25 { + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // This is the same as the previous test, but gcPercent = -1, so the heap *should* grow + // all the way to the peak. + name: "SteadyMemoryLimitNoGCPercent", + gcPercent: -1, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(2.0).sum(ramp(-1.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if goal := c[n-1].heapGoal; goal != (512<<20)-MemoryLimitHeapGoalHeadroom { + t.Errorf("heap goal is not the heap limit: %d", goal) + } + if n >= 25 { + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // This test ensures that the pacer doesn't fall over even when the live heap exceeds + // the memory limit. It also makes sure GC utilization actually rises to push back. + name: "ExceedMemoryLimit", + gcPercent: 100, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(3.5).sum(ramp(-2.5, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 12 { + // We're way over the memory limit, so we want to make sure our goal is set + // as low as it possibly can be. + if goal, live := c[n-1].heapGoal, c[n-1].heapLive; goal != live { + t.Errorf("heap goal is not equal to live heap: %d != %d", goal, live) + } + } + if n >= 25 { + // Due to memory pressure, we should scale to 100% GC CPU utilization. + // Note that in practice this won't actually happen because of the CPU limiter, + // but it's not the pacer's job to limit CPU usage. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, 1.0, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + // In this case, that just means it's not wavering around a whole bunch. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + } + }, + }, + { + // Same as the previous test, but with gcPercent = -1. + name: "ExceedMemoryLimitNoGCPercent", + gcPercent: -1, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(3.5).sum(ramp(-2.5, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n < 10 { + if goal := c[n-1].heapGoal; goal != (512<<20)-MemoryLimitHeapGoalHeadroom { + t.Errorf("heap goal is not the heap limit: %d", goal) + } + } + if n > 12 { + // We're way over the memory limit, so we want to make sure our goal is set + // as low as it possibly can be. + if goal, live := c[n-1].heapGoal, c[n-1].heapLive; goal != live { + t.Errorf("heap goal is not equal to live heap: %d != %d", goal, live) + } + } + if n >= 25 { + // Due to memory pressure, we should scale to 100% GC CPU utilization. + // Note that in practice this won't actually happen because of the CPU limiter, + // but it's not the pacer's job to limit CPU usage. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, 1.0, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. + // In this case, that just means it's not wavering around a whole bunch. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + } + }, + }, + { + // This test ensures that the pacer maintains the memory limit as the heap grows. + name: "MaintainMemoryLimit", + gcPercent: 100, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(3.0).sum(ramp(-2.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if n > 12 { + // We're trying to saturate the memory limit. + if goal := c[n-1].heapGoal; goal != (512<<20)-MemoryLimitHeapGoalHeadroom { + t.Errorf("heap goal is not the heap limit: %d", goal) + } + } + if n >= 25 { + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization, + // even with the additional memory pressure. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles and + // that it's meeting its goal. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + { + // Same as the previous test, but with gcPercent = -1. + name: "MaintainMemoryLimitNoGCPercent", + gcPercent: -1, + memoryLimit: 512 << 20, + globalsBytes: 32 << 10, + nCores: 8, + allocRate: constant(33.0), + scanRate: constant(1024.0), + growthRate: constant(3.0).sum(ramp(-2.0, 12)), + scannableFrac: constant(1.0), + stackBytes: constant(8192), + length: 50, + checker: func(t *testing.T, c []gcCycleResult) { + n := len(c) + if goal := c[n-1].heapGoal; goal != (512<<20)-MemoryLimitHeapGoalHeadroom { + t.Errorf("heap goal is not the heap limit: %d", goal) + } + if n >= 25 { + // At this alloc/scan rate, the pacer should be extremely close to the goal utilization, + // even with the additional memory pressure. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + + // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles and + // that it's meeting its goal. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) + } + }, + }, + // TODO(mknyszek): Write a test that exercises the pacer's hard goal. + // This is difficult in the idealized model this testing framework places + // the pacer in, because the calculated overshoot is directly proportional + // to the runway for the case of the expected work. + // However, it is still possible to trigger this case if something exceptional + // happens between calls to revise; the framework just doesn't support this yet. + } { + e := e + t.Run(e.name, func(t *testing.T) { + t.Parallel() + + c := NewGCController(e.gcPercent, e.memoryLimit) + var bytesAllocatedBlackLast int64 + results := make([]gcCycleResult, 0, e.length) + for i := 0; i < e.length; i++ { + cycle := e.next() + c.StartCycle(cycle.stackBytes, e.globalsBytes, cycle.scannableFrac, e.nCores) + + // Update pacer incrementally as we complete scan work. + const ( + revisePeriod = 500 * time.Microsecond + rateConv = 1024 * float64(revisePeriod) / float64(time.Millisecond) + ) + var nextHeapMarked int64 + if i == 0 { + nextHeapMarked = initialHeapBytes + } else { + nextHeapMarked = int64(float64(int64(c.HeapMarked())-bytesAllocatedBlackLast) * cycle.growthRate) + } + globalsScanWorkLeft := int64(e.globalsBytes) + stackScanWorkLeft := int64(cycle.stackBytes) + heapScanWorkLeft := int64(float64(nextHeapMarked) * cycle.scannableFrac) + doWork := func(work int64) (int64, int64, int64) { + var deltas [3]int64 + + // Do globals work first, then stacks, then heap. + for i, workLeft := range []*int64{&globalsScanWorkLeft, &stackScanWorkLeft, &heapScanWorkLeft} { + if *workLeft == 0 { + continue + } + if *workLeft > work { + deltas[i] += work + *workLeft -= work + work = 0 + break + } else { + deltas[i] += *workLeft + work -= *workLeft + *workLeft = 0 + } + } + return deltas[0], deltas[1], deltas[2] + } + var ( + gcDuration int64 + assistTime int64 + bytesAllocatedBlack int64 + ) + for heapScanWorkLeft+stackScanWorkLeft+globalsScanWorkLeft > 0 { + // Simulate GC assist pacing. + // + // Note that this is an idealized view of the GC assist pacing + // mechanism. + + // From the assist ratio and the alloc and scan rates, we can idealize what + // the GC CPU utilization looks like. + // + // We start with assistRatio = (bytes of scan work) / (bytes of runway) (by definition). + // + // Over revisePeriod, we can also calculate how many bytes are scanned and + // allocated, given some GC CPU utilization u: + // + // bytesScanned = scanRate * rateConv * nCores * u + // bytesAllocated = allocRate * rateConv * nCores * (1 - u) + // + // During revisePeriod, assistRatio is kept constant, and GC assists kick in to + // maintain it. Specifically, they act to prevent too many bytes being allocated + // compared to how many bytes are scanned. It directly defines the ratio of + // bytesScanned to bytesAllocated over this period, hence: + // + // assistRatio = bytesScanned / bytesAllocated + // + // From this, we can solve for utilization, because everything else has already + // been determined: + // + // assistRatio = (scanRate * rateConv * nCores * u) / (allocRate * rateConv * nCores * (1 - u)) + // assistRatio = (scanRate * u) / (allocRate * (1 - u)) + // assistRatio * allocRate * (1-u) = scanRate * u + // assistRatio * allocRate - assistRatio * allocRate * u = scanRate * u + // assistRatio * allocRate = assistRatio * allocRate * u + scanRate * u + // assistRatio * allocRate = (assistRatio * allocRate + scanRate) * u + // u = (assistRatio * allocRate) / (assistRatio * allocRate + scanRate) + // + // Note that this may give a utilization that is _less_ than GCBackgroundUtilization, + // which isn't possible in practice because of dedicated workers. Thus, this case + // must be interpreted as GC assists not kicking in at all, and just round up. All + // downstream values will then have this accounted for. + assistRatio := c.AssistWorkPerByte() + utilization := assistRatio * cycle.allocRate / (assistRatio*cycle.allocRate + cycle.scanRate) + if utilization < GCBackgroundUtilization { + utilization = GCBackgroundUtilization + } + + // Knowing the utilization, calculate bytesScanned and bytesAllocated. + bytesScanned := int64(cycle.scanRate * rateConv * float64(e.nCores) * utilization) + bytesAllocated := int64(cycle.allocRate * rateConv * float64(e.nCores) * (1 - utilization)) + + // Subtract work from our model. + globalsScanned, stackScanned, heapScanned := doWork(bytesScanned) + + // doWork may not use all of bytesScanned. + // In this case, the GC actually ends sometime in this period. + // Let's figure out when, exactly, and adjust bytesAllocated too. + actualElapsed := revisePeriod + actualAllocated := bytesAllocated + if actualScanned := globalsScanned + stackScanned + heapScanned; actualScanned < bytesScanned { + // actualScanned = scanRate * rateConv * (t / revisePeriod) * nCores * u + // => t = actualScanned * revisePeriod / (scanRate * rateConv * nCores * u) + actualElapsed = time.Duration(float64(actualScanned) * float64(revisePeriod) / (cycle.scanRate * rateConv * float64(e.nCores) * utilization)) + actualAllocated = int64(cycle.allocRate * rateConv * float64(actualElapsed) / float64(revisePeriod) * float64(e.nCores) * (1 - utilization)) + } + + // Ask the pacer to revise. + c.Revise(GCControllerReviseDelta{ + HeapLive: actualAllocated, + HeapScan: int64(float64(actualAllocated) * cycle.scannableFrac), + HeapScanWork: heapScanned, + StackScanWork: stackScanned, + GlobalsScanWork: globalsScanned, + }) + + // Accumulate variables. + assistTime += int64(float64(actualElapsed) * float64(e.nCores) * (utilization - GCBackgroundUtilization)) + gcDuration += int64(actualElapsed) + bytesAllocatedBlack += actualAllocated + } + + // Put together the results, log them, and concatenate them. + result := gcCycleResult{ + cycle: i + 1, + heapLive: c.HeapMarked(), + heapScannable: int64(float64(int64(c.HeapMarked())-bytesAllocatedBlackLast) * cycle.scannableFrac), + heapTrigger: c.Triggered(), + heapPeak: c.HeapLive(), + heapGoal: c.HeapGoal(), + gcUtilization: float64(assistTime)/(float64(gcDuration)*float64(e.nCores)) + GCBackgroundUtilization, + } + t.Log("GC", result.String()) + results = append(results, result) + + // Run the checker for this test. + e.check(t, results) + + c.EndCycle(uint64(nextHeapMarked+bytesAllocatedBlack), assistTime, gcDuration, e.nCores) + + bytesAllocatedBlackLast = bytesAllocatedBlack + } + }) + } +} + +type gcExecTest struct { + name string + + gcPercent int + memoryLimit int64 + globalsBytes uint64 + nCores int + + allocRate float64Stream // > 0, KiB / cpu-ms + scanRate float64Stream // > 0, KiB / cpu-ms + growthRate float64Stream // > 0 + scannableFrac float64Stream // Clamped to [0, 1] + stackBytes float64Stream // Multiple of 2048. + length int + + checker func(*testing.T, []gcCycleResult) +} + +// minRate is an arbitrary minimum for allocRate, scanRate, and growthRate. +// These values just cannot be zero. +const minRate = 0.0001 + +func (e *gcExecTest) next() gcCycle { + return gcCycle{ + allocRate: e.allocRate.min(minRate)(), + scanRate: e.scanRate.min(minRate)(), + growthRate: e.growthRate.min(minRate)(), + scannableFrac: e.scannableFrac.limit(0, 1)(), + stackBytes: uint64(e.stackBytes.quantize(2048).min(0)()), + } +} + +func (e *gcExecTest) check(t *testing.T, results []gcCycleResult) { + t.Helper() + + // Do some basic general checks first. + n := len(results) + switch n { + case 0: + t.Fatal("no results passed to check") + return + case 1: + if results[0].cycle != 1 { + t.Error("first cycle has incorrect number") + } + default: + if results[n-1].cycle != results[n-2].cycle+1 { + t.Error("cycle numbers out of order") + } + } + if u := results[n-1].gcUtilization; u < 0 || u > 1 { + t.Fatal("GC utilization not within acceptable bounds") + } + if s := results[n-1].heapScannable; s < 0 { + t.Fatal("heapScannable is negative") + } + if e.checker == nil { + t.Fatal("test-specific checker is missing") + } + + // Run the test-specific checker. + e.checker(t, results) +} + +type gcCycle struct { + allocRate float64 + scanRate float64 + growthRate float64 + scannableFrac float64 + stackBytes uint64 +} + +type gcCycleResult struct { + cycle int + + // These come directly from the pacer, so uint64. + heapLive uint64 + heapTrigger uint64 + heapGoal uint64 + heapPeak uint64 + + // These are produced by the simulation, so int64 and + // float64 are more appropriate, so that we can check for + // bad states in the simulation. + heapScannable int64 + gcUtilization float64 +} + +func (r *gcCycleResult) goalRatio() float64 { + return float64(r.heapPeak) / float64(r.heapGoal) +} + +func (r *gcCycleResult) runway() float64 { + return float64(r.heapGoal - r.heapTrigger) +} + +func (r *gcCycleResult) triggerRatio() float64 { + return float64(r.heapTrigger-r.heapLive) / float64(r.heapGoal-r.heapLive) +} + +func (r *gcCycleResult) String() string { + return fmt.Sprintf("%d %2.1f%% %d->%d->%d (goal: %d)", r.cycle, r.gcUtilization*100, r.heapLive, r.heapTrigger, r.heapPeak, r.heapGoal) +} + +func assertInEpsilon(t *testing.T, name string, a, b, epsilon float64) { + t.Helper() + assertInRange(t, name, a, b-epsilon, b+epsilon) +} + +func assertInRange(t *testing.T, name string, a, min, max float64) { + t.Helper() + if a < min || a > max { + t.Errorf("%s not in range (%f, %f): %f", name, min, max, a) + } +} + +// float64Stream is a function that generates an infinite stream of +// float64 values when called repeatedly. +type float64Stream func() float64 + +// constant returns a stream that generates the value c. +func constant(c float64) float64Stream { + return func() float64 { + return c + } +} + +// unit returns a stream that generates a single peak with +// amplitude amp, followed by zeroes. +// +// In another manner of speaking, this is the Kronecker delta. +func unit(amp float64) float64Stream { + dropped := false + return func() float64 { + if dropped { + return 0 + } + dropped = true + return amp + } +} + +// oscillate returns a stream that oscillates sinusoidally +// with the given amplitude, phase, and period. +func oscillate(amp, phase float64, period int) float64Stream { + var cycle int + return func() float64 { + p := float64(cycle)/float64(period)*2*math.Pi + phase + cycle++ + if cycle == period { + cycle = 0 + } + return math.Sin(p) * amp + } +} + +// ramp returns a stream that moves from zero to height +// over the course of length steps. +func ramp(height float64, length int) float64Stream { + var cycle int + return func() float64 { + h := height * float64(cycle) / float64(length) + if cycle < length { + cycle++ + } + return h + } +} + +// random returns a stream that generates random numbers +// between -amp and amp. +func random(amp float64, seed int64) float64Stream { + r := rand.New(rand.NewSource(seed)) + return func() float64 { + return ((r.Float64() - 0.5) * 2) * amp + } +} + +// delay returns a new stream which is a buffered version +// of f: it returns zero for cycles steps, followed by f. +func (f float64Stream) delay(cycles int) float64Stream { + zeroes := 0 + return func() float64 { + if zeroes < cycles { + zeroes++ + return 0 + } + return f() + } +} + +// scale returns a new stream that is f, but attenuated by a +// constant factor. +func (f float64Stream) scale(amt float64) float64Stream { + return func() float64 { + return f() * amt + } +} + +// offset returns a new stream that is f but offset by amt +// at each step. +func (f float64Stream) offset(amt float64) float64Stream { + return func() float64 { + old := f() + return old + amt + } +} + +// sum returns a new stream that is the sum of all input streams +// at each step. +func (f float64Stream) sum(fs ...float64Stream) float64Stream { + return func() float64 { + sum := f() + for _, s := range fs { + sum += s() + } + return sum + } +} + +// quantize returns a new stream that rounds f to a multiple +// of mult at each step. +func (f float64Stream) quantize(mult float64) float64Stream { + return func() float64 { + r := f() / mult + if r < 0 { + return math.Ceil(r) * mult + } + return math.Floor(r) * mult + } +} + +// min returns a new stream that replaces all values produced +// by f lower than min with min. +func (f float64Stream) min(min float64) float64Stream { + return func() float64 { + return math.Max(min, f()) + } +} + +// max returns a new stream that replaces all values produced +// by f higher than max with max. +func (f float64Stream) max(max float64) float64Stream { + return func() float64 { + return math.Min(max, f()) + } +} + +// limit returns a new stream that replaces all values produced +// by f lower than min with min and higher than max with max. +func (f float64Stream) limit(min, max float64) float64Stream { + return func() float64 { + v := f() + if v < min { + v = min + } else if v > max { + v = max + } + return v + } +} + +func FuzzPIController(f *testing.F) { + isNormal := func(x float64) bool { + return !math.IsInf(x, 0) && !math.IsNaN(x) + } + isPositive := func(x float64) bool { + return isNormal(x) && x > 0 + } + // Seed with constants from controllers in the runtime. + // It's not critical that we keep these in sync, they're just + // reasonable seed inputs. + f.Add(0.3375, 3.2e6, 1e9, 0.001, 1000.0, 0.01) + f.Add(0.9, 4.0, 1000.0, -1000.0, 1000.0, 0.84) + f.Fuzz(func(t *testing.T, kp, ti, tt, min, max, setPoint float64) { + // Ignore uninteresting invalid parameters. These parameters + // are constant, so in practice surprising values will be documented + // or will be other otherwise immediately visible. + // + // We just want to make sure that given a non-Inf, non-NaN input, + // we always get a non-Inf, non-NaN output. + if !isPositive(kp) || !isPositive(ti) || !isPositive(tt) { + return + } + if !isNormal(min) || !isNormal(max) || min > max { + return + } + // Use a random source, but make it deterministic. + rs := rand.New(rand.NewSource(800)) + randFloat64 := func() float64 { + return math.Float64frombits(rs.Uint64()) + } + p := NewPIController(kp, ti, tt, min, max) + state := float64(0) + for i := 0; i < 100; i++ { + input := randFloat64() + // Ignore the "ok" parameter. We're just trying to break it. + // state is intentionally completely uncorrelated with the input. + var ok bool + state, ok = p.Next(input, setPoint, 1.0) + if !isNormal(state) { + t.Fatalf("got NaN or Inf result from controller: %f %v", state, ok) + } + } + }) +} + +func TestIdleMarkWorkerCount(t *testing.T) { + const workers = 10 + c := NewGCController(100, math.MaxInt64) + c.SetMaxIdleMarkWorkers(workers) + for i := 0; i < workers; i++ { + if !c.NeedIdleMarkWorker() { + t.Fatalf("expected to need idle mark workers: i=%d", i) + } + if !c.AddIdleMarkWorker() { + t.Fatalf("expected to be able to add an idle mark worker: i=%d", i) + } + } + if c.NeedIdleMarkWorker() { + t.Fatalf("expected to not need idle mark workers") + } + if c.AddIdleMarkWorker() { + t.Fatalf("expected to not be able to add an idle mark worker") + } + for i := 0; i < workers; i++ { + c.RemoveIdleMarkWorker() + if !c.NeedIdleMarkWorker() { + t.Fatalf("expected to need idle mark workers after removal: i=%d", i) + } + } + for i := 0; i < workers-1; i++ { + if !c.AddIdleMarkWorker() { + t.Fatalf("expected to be able to add idle mark workers after adding again: i=%d", i) + } + } + for i := 0; i < 10; i++ { + if !c.AddIdleMarkWorker() { + t.Fatalf("expected to be able to add idle mark workers interleaved: i=%d", i) + } + if c.AddIdleMarkWorker() { + t.Fatalf("expected to not be able to add idle mark workers interleaved: i=%d", i) + } + c.RemoveIdleMarkWorker() + } + // Support the max being below the count. + c.SetMaxIdleMarkWorkers(0) + if c.NeedIdleMarkWorker() { + t.Fatalf("expected to not need idle mark workers after capacity set to 0") + } + if c.AddIdleMarkWorker() { + t.Fatalf("expected to not be able to add idle mark workers after capacity set to 0") + } + for i := 0; i < workers-1; i++ { + c.RemoveIdleMarkWorker() + } + if c.NeedIdleMarkWorker() { + t.Fatalf("expected to not need idle mark workers after capacity set to 0") + } + if c.AddIdleMarkWorker() { + t.Fatalf("expected to not be able to add idle mark workers after capacity set to 0") + } + c.SetMaxIdleMarkWorkers(1) + if !c.NeedIdleMarkWorker() { + t.Fatalf("expected to need idle mark workers after capacity set to 1") + } + if !c.AddIdleMarkWorker() { + t.Fatalf("expected to be able to add idle mark workers after capacity set to 1") + } +} diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go index 7578129f9d1396..c54ae3446249a5 100644 --- a/src/runtime/mgcscavenge.go +++ b/src/runtime/mgcscavenge.go @@ -17,8 +17,11 @@ // scavenger's primary goal is to bring the estimated heap RSS of the // application down to a goal. // -// That goal is defined as: -// (retainExtraPercent+100) / 100 * (heapGoal / lastHeapGoal) * last_heap_inuse +// Before we consider what this looks like, we need to split the world into two +// halves. One in which a memory limit is not set, and one in which it is. +// +// For the former, the goal is defined as: +// (retainExtraPercent+100) / 100 * (heapGoal / lastHeapGoal) * lastHeapInUse // // Essentially, we wish to have the application's RSS track the heap goal, but // the heap goal is defined in terms of bytes of objects, rather than pages like @@ -26,7 +29,7 @@ // spans. heapGoal / lastHeapGoal defines the ratio between the current heap goal // and the last heap goal, which tells us by how much the heap is growing and // shrinking. We estimate what the heap will grow to in terms of pages by taking -// this ratio and multiplying it by heap_inuse at the end of the last GC, which +// this ratio and multiplying it by heapInUse at the end of the last GC, which // allows us to account for this additional fragmentation. Note that this // procedure makes the assumption that the degree of fragmentation won't change // dramatically over the next GC cycle. Overestimating the amount of @@ -41,11 +44,22 @@ // that there's more unscavenged memory to allocate out of, since each allocation // out of scavenged memory incurs a potentially expensive page fault. // -// The goal is updated after each GC and the scavenger's pacing parameters -// (which live in mheap_) are updated to match. The pacing parameters work much -// like the background sweeping parameters. The parameters define a line whose -// horizontal axis is time and vertical axis is estimated heap RSS, and the -// scavenger attempts to stay below that line at all times. +// If a memory limit is set, then we wish to pick a scavenge goal that maintains +// that memory limit. For that, we look at total memory that has been committed +// (memstats.mappedReady) and try to bring that down below the limit. In this case, +// we want to give buffer space in the *opposite* direction. When the application +// is close to the limit, we want to make sure we push harder to keep it under, so +// if we target below the memory limit, we ensure that the background scavenger is +// giving the situation the urgency it deserves. +// +// In this case, the goal is defined as: +// (100-reduceExtraPercent) / 100 * memoryLimit +// +// We compute both of these goals, and check whether either of them have been met. +// The background scavenger continues operating as long as either one of the goals +// has not been met. +// +// The goals are updated after each GC. // // The synchronous heap-growth scavenging happens whenever the heap grows in // size, for some definition of heap-growth. The intuition behind this is that @@ -56,6 +70,7 @@ package runtime import ( + "internal/goos" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -70,6 +85,7 @@ const ( // retainExtraPercent represents the amount of memory over the heap goal // that the scavenger should keep as a buffer space for the allocator. + // This constant is used when we do not have a memory limit set. // // The purpose of maintaining this overhead is to have a greater pool of // unscavenged memory available for allocation (since using scavenged memory @@ -77,6 +93,17 @@ const ( // the ever-changing layout of the heap. retainExtraPercent = 10 + // reduceExtraPercent represents the amount of memory under the limit + // that the scavenger should target. For example, 5 means we target 95% + // of the limit. + // + // The purpose of shooting lower than the limit is to ensure that, once + // close to the limit, the scavenger is working hard to maintain it. If + // we have a memory limit set but are far away from it, there's no harm + // in leaving up to 100-retainExtraPercent live, and it's more efficient + // anyway, for the same reasons that retainExtraPercent exists. + reduceExtraPercent = 5 + // maxPagesPerPhysPage is the maximum number of supported runtime pages per // physical page, based on maxPhysPageSize. maxPagesPerPhysPage = maxPhysPageSize / pageSize @@ -90,21 +117,17 @@ const ( // // This ratio is used as part of multiplicative factor to help the scavenger account // for the additional costs of using scavenged memory in its pacing. - scavengeCostRatio = 0.7 * (sys.GoosDarwin + sys.GoosIos) - - // scavengeReservationShards determines the amount of memory the scavenger - // should reserve for scavenging at a time. Specifically, the amount of - // memory reserved is (heap size in bytes) / scavengeReservationShards. - scavengeReservationShards = 64 + scavengeCostRatio = 0.7 * (goos.IsDarwin + goos.IsIos) ) // heapRetained returns an estimate of the current heap RSS. func heapRetained() uint64 { - return memstats.heap_sys.load() - atomic.Load64(&memstats.heap_released) + return gcController.heapInUse.load() + gcController.heapFree.load() } // gcPaceScavenger updates the scavenger's pacing, particularly -// its rate and RSS goal. +// its rate and RSS goal. For this, it requires the current heapGoal, +// and the heapGoal for the previous GC cycle. // // The RSS goal is based on the current heap goal with a small overhead // to accommodate non-determinism in the allocator. @@ -112,97 +135,276 @@ func heapRetained() uint64 { // The pacing is based on scavengePageRate, which applies to both regular and // huge pages. See that constant for more information. // +// Must be called whenever GC pacing is updated. +// // mheap_.lock must be held or the world must be stopped. -func gcPaceScavenger() { +func gcPaceScavenger(memoryLimit int64, heapGoal, lastHeapGoal uint64) { + assertWorldStoppedOrLockHeld(&mheap_.lock) + + // As described at the top of this file, there are two scavenge goals here: one + // for gcPercent and one for memoryLimit. Let's handle the latter first because + // it's simpler. + + // We want to target retaining (100-reduceExtraPercent)% of the heap. + memoryLimitGoal := uint64(float64(memoryLimit) * (100.0 - reduceExtraPercent)) + + // mappedReady is comparable to memoryLimit, and represents how much total memory + // the Go runtime has committed now (estimated). + mappedReady := gcController.mappedReady.Load() + + // If we're below the goal already indicate that we don't need the background + // scavenger for the memory limit. This may seems worrisome at first, but note + // that the allocator will assist the background scavenger in the face of a memory + // limit, so we'll be safe even if we stop the scavenger when we shouldn't have. + if mappedReady <= memoryLimitGoal { + scavenge.memoryLimitGoal.Store(^uint64(0)) + } else { + scavenge.memoryLimitGoal.Store(memoryLimitGoal) + } + + // Now handle the gcPercent goal. + // If we're called before the first GC completed, disable scavenging. // We never scavenge before the 2nd GC cycle anyway (we don't have enough // information about the heap yet) so this is fine, and avoids a fault // or garbage data later. - if gcController.lastHeapGoal == 0 { - mheap_.scavengeGoal = ^uint64(0) + if lastHeapGoal == 0 { + scavenge.gcPercentGoal.Store(^uint64(0)) return } // Compute our scavenging goal. - goalRatio := float64(atomic.Load64(&gcController.heapGoal)) / float64(gcController.lastHeapGoal) - retainedGoal := uint64(float64(memstats.last_heap_inuse) * goalRatio) + goalRatio := float64(heapGoal) / float64(lastHeapGoal) + gcPercentGoal := uint64(float64(memstats.lastHeapInUse) * goalRatio) // Add retainExtraPercent overhead to retainedGoal. This calculation // looks strange but the purpose is to arrive at an integer division // (e.g. if retainExtraPercent = 12.5, then we get a divisor of 8) // that also avoids the overflow from a multiplication. - retainedGoal += retainedGoal / (1.0 / (retainExtraPercent / 100.0)) + gcPercentGoal += gcPercentGoal / (1.0 / (retainExtraPercent / 100.0)) // Align it to a physical page boundary to make the following calculations // a bit more exact. - retainedGoal = (retainedGoal + uint64(physPageSize) - 1) &^ (uint64(physPageSize) - 1) + gcPercentGoal = (gcPercentGoal + uint64(physPageSize) - 1) &^ (uint64(physPageSize) - 1) // Represents where we are now in the heap's contribution to RSS in bytes. // // Guaranteed to always be a multiple of physPageSize on systems where - // physPageSize <= pageSize since we map heap_sys at a rate larger than + // physPageSize <= pageSize since we map new heap memory at a size larger than // any physPageSize and released memory in multiples of the physPageSize. // - // However, certain functions recategorize heap_sys as other stats (e.g. - // stack_sys) and this happens in multiples of pageSize, so on systems + // However, certain functions recategorize heap memory as other stats (e.g. + // stacks) and this happens in multiples of pageSize, so on systems // where physPageSize > pageSize the calculations below will not be exact. // Generally this is OK since we'll be off by at most one regular // physical page. - retainedNow := heapRetained() + heapRetainedNow := heapRetained() - // If we're already below our goal, or within one page of our goal, then disable - // the background scavenger. We disable the background scavenger if there's - // less than one physical page of work to do because it's not worth it. - if retainedNow <= retainedGoal || retainedNow-retainedGoal < uint64(physPageSize) { - mheap_.scavengeGoal = ^uint64(0) - return + // If we're already below our goal, or within one page of our goal, then indicate + // that we don't need the background scavenger for maintaining a memory overhead + // proportional to the heap goal. + if heapRetainedNow <= gcPercentGoal || heapRetainedNow-gcPercentGoal < uint64(physPageSize) { + scavenge.gcPercentGoal.Store(^uint64(0)) + } else { + scavenge.gcPercentGoal.Store(gcPercentGoal) } - mheap_.scavengeGoal = retainedGoal } -// Sleep/wait state of the background scavenger. var scavenge struct { - lock mutex - g *g - parked bool - timer *timer - sysmonWake uint32 // Set atomically. + // gcPercentGoal is the amount of retained heap memory (measured by + // heapRetained) that the runtime will try to maintain by returning + // memory to the OS. This goal is derived from gcController.gcPercent + // by choosing to retain enough memory to allocate heap memory up to + // the heap goal. + gcPercentGoal atomic.Uint64 + + // memoryLimitGoal is the amount of memory retained by the runtime ( + // measured by gcController.mappedReady) that the runtime will try to + // maintain by returning memory to the OS. This goal is derived from + // gcController.memoryLimit by choosing to target the memory limit or + // some lower target to keep the scavenger working. + memoryLimitGoal atomic.Uint64 +} + +const ( + // It doesn't really matter what value we start at, but we can't be zero, because + // that'll cause divide-by-zero issues. Pick something conservative which we'll + // also use as a fallback. + startingScavSleepRatio = 0.001 + + // Spend at least 1 ms scavenging, otherwise the corresponding + // sleep time to maintain our desired utilization is too low to + // be reliable. + minScavWorkTime = 1e6 +) + +// Sleep/wait state of the background scavenger. +var scavenger scavengerState + +type scavengerState struct { + // lock protects all fields below. + lock mutex + + // g is the goroutine the scavenger is bound to. + g *g + + // parked is whether or not the scavenger is parked. + parked bool + + // timer is the timer used for the scavenger to sleep. + timer *timer + + // sysmonWake signals to sysmon that it should wake the scavenger. + sysmonWake atomic.Uint32 + + // targetCPUFraction is the target CPU overhead for the scavenger. + targetCPUFraction float64 + + // sleepRatio is the ratio of time spent doing scavenging work to + // time spent sleeping. This is used to decide how long the scavenger + // should sleep for in between batches of work. It is set by + // critSleepController in order to maintain a CPU overhead of + // targetCPUFraction. + // + // Lower means more sleep, higher means more aggressive scavenging. + sleepRatio float64 + + // sleepController controls sleepRatio. + // + // See sleepRatio for more details. + sleepController piController + + // cooldown is the time left in nanoseconds during which we avoid + // using the controller and we hold sleepRatio at a conservative + // value. Used if the controller's assumptions fail to hold. + controllerCooldown int64 + + // printControllerReset instructs printScavTrace to signal that + // the controller was reset. + printControllerReset bool + + // sleepStub is a stub used for testing to avoid actually having + // the scavenger sleep. + // + // Unlike the other stubs, this is not populated if left nil + // Instead, it is called when non-nil because any valid implementation + // of this function basically requires closing over this scavenger + // state, and allocating a closure is not allowed in the runtime as + // a matter of policy. + sleepStub func(n int64) int64 + + // scavenge is a function that scavenges n bytes of memory. + // Returns how many bytes of memory it actually scavenged, as + // well as the time it took in nanoseconds. Usually mheap.pages.scavenge + // with nanotime called around it, but stubbed out for testing. + // Like mheap.pages.scavenge, if it scavenges less than n bytes of + // memory, the caller may assume the heap is exhausted of scavengable + // memory for now. + // + // If this is nil, it is populated with the real thing in init. + scavenge func(n uintptr) (uintptr, int64) + + // shouldStop is a callback called in the work loop and provides a + // point that can force the scavenger to stop early, for example because + // the scavenge policy dictates too much has been scavenged already. + // + // If this is nil, it is populated with the real thing in init. + shouldStop func() bool + + // gomaxprocs returns the current value of gomaxprocs. Stub for testing. + // + // If this is nil, it is populated with the real thing in init. + gomaxprocs func() int32 } -// readyForScavenger signals sysmon to wake the scavenger because -// there may be new work to do. +// init initializes a scavenger state and wires to the current G. // -// There may be a significant delay between when this function runs -// and when the scavenger is kicked awake, but it may be safely invoked -// in contexts where wakeScavenger is unsafe to call directly. -func readyForScavenger() { - atomic.Store(&scavenge.sysmonWake, 1) +// Must be called from a regular goroutine that can allocate. +func (s *scavengerState) init() { + if s.g != nil { + throw("scavenger state is already wired") + } + lockInit(&s.lock, lockRankScavenge) + s.g = getg() + + s.timer = new(timer) + s.timer.arg = s + s.timer.f = func(s any, _ uintptr) { + s.(*scavengerState).wake() + } + + // input: fraction of CPU time actually used. + // setpoint: ideal CPU fraction. + // output: ratio of time worked to time slept (determines sleep time). + // + // The output of this controller is somewhat indirect to what we actually + // want to achieve: how much time to sleep for. The reason for this definition + // is to ensure that the controller's outputs have a direct relationship with + // its inputs (as opposed to an inverse relationship), making it somewhat + // easier to reason about for tuning purposes. + s.sleepController = piController{ + // Tuned loosely via Ziegler-Nichols process. + kp: 0.3375, + ti: 3.2e6, + tt: 1e9, // 1 second reset time. + + // These ranges seem wide, but we want to give the controller plenty of + // room to hunt for the optimal value. + min: 0.001, // 1:1000 + max: 1000.0, // 1000:1 + } + s.sleepRatio = startingScavSleepRatio + + // Install real functions if stubs aren't present. + if s.scavenge == nil { + s.scavenge = func(n uintptr) (uintptr, int64) { + start := nanotime() + r := mheap_.pages.scavenge(n, nil) + end := nanotime() + if start >= end { + return r, 0 + } + return r, end - start + } + } + if s.shouldStop == nil { + s.shouldStop = func() bool { + // If background scavenging is disabled or if there's no work to do just stop. + return heapRetained() <= scavenge.gcPercentGoal.Load() && + (!go119MemoryLimitSupport || + gcController.mappedReady.Load() <= scavenge.memoryLimitGoal.Load()) + } + } + if s.gomaxprocs == nil { + s.gomaxprocs = func() int32 { + return gomaxprocs + } + } +} + +// park parks the scavenger goroutine. +func (s *scavengerState) park() { + lock(&s.lock) + if getg() != s.g { + throw("tried to park scavenger from another goroutine") + } + s.parked = true + goparkunlock(&s.lock, waitReasonGCScavengeWait, traceEvGoBlock, 2) +} + +// ready signals to sysmon that the scavenger should be awoken. +func (s *scavengerState) ready() { + s.sysmonWake.Store(1) } -// wakeScavenger immediately unparks the scavenger if necessary. -// -// May run without a P, but it may allocate, so it must not be called -// on any allocation path. -// -// mheap_.lock, scavenge.lock, and sched.lock must not be held. -func wakeScavenger() { - lock(&scavenge.lock) - if scavenge.parked { - // Notify sysmon that it shouldn't bother waking up the scavenger. - atomic.Store(&scavenge.sysmonWake, 0) - - // Try to stop the timer but we don't really care if we succeed. - // It's possible that either a timer was never started, or that - // we're racing with it. - // In the case that we're racing with there's the low chance that - // we experience a spurious wake-up of the scavenger, but that's - // totally safe. - stopTimer(scavenge.timer) - - // Unpark the goroutine and tell it that there may have been a pacing - // change. Note that we skip the scheduler's runnext slot because we - // want to avoid having the scavenger interfere with the fair - // scheduling of user goroutines. In effect, this schedules the - // scavenger at a "lower priority" but that's OK because it'll - // catch up on the work it missed when it does get scheduled. - scavenge.parked = false +// wake immediately unparks the scavenger if necessary. +// +// Safe to run without a P. +func (s *scavengerState) wake() { + lock(&s.lock) + if s.parked { + // Unset sysmonWake, since the scavenger is now being awoken. + s.sysmonWake.Store(0) + + // s.parked is unset to prevent a double wake-up. + s.parked = false // Ready the goroutine by injecting it. We use injectglist instead // of ready or goready in order to allow us to run this function @@ -211,175 +413,217 @@ func wakeScavenger() { // the scavenger from interfering with user goroutine scheduling // too much. var list gList - list.push(scavenge.g) + list.push(s.g) injectglist(&list) } - unlock(&scavenge.lock) + unlock(&s.lock) } -// scavengeSleep attempts to put the scavenger to sleep for ns. +// sleep puts the scavenger to sleep based on the amount of time that it worked +// in nanoseconds. // // Note that this function should only be called by the scavenger. // // The scavenger may be woken up earlier by a pacing change, and it may not go // to sleep at all if there's a pending pacing change. -// -// Returns the amount of time actually slept. -func scavengeSleep(ns int64) int64 { - lock(&scavenge.lock) - - // Set the timer. - // - // This must happen here instead of inside gopark - // because we can't close over any variables without - // failing escape analysis. - start := nanotime() - resetTimer(scavenge.timer, start+ns) - - // Mark ourself as asleep and go to sleep. - scavenge.parked = true - goparkunlock(&scavenge.lock, waitReasonSleep, traceEvGoSleep, 2) - - // Return how long we actually slept for. - return nanotime() - start -} +func (s *scavengerState) sleep(worked float64) { + lock(&s.lock) + if getg() != s.g { + throw("tried to sleep scavenger from another goroutine") + } -// Background scavenger. -// -// The background scavenger maintains the RSS of the application below -// the line described by the proportional scavenging statistics in -// the mheap struct. -func bgscavenge() { - scavenge.g = getg() + if worked < minScavWorkTime { + // This means there wasn't enough work to actually fill up minScavWorkTime. + // That's fine; we shouldn't try to do anything with this information + // because it's going result in a short enough sleep request that things + // will get messy. Just assume we did at least this much work. + // All this means is that we'll sleep longer than we otherwise would have. + worked = minScavWorkTime + } - lockInit(&scavenge.lock, lockRankScavenge) - lock(&scavenge.lock) - scavenge.parked = true + // Multiply the critical time by 1 + the ratio of the costs of using + // scavenged memory vs. scavenging memory. This forces us to pay down + // the cost of reusing this memory eagerly by sleeping for a longer period + // of time and scavenging less frequently. More concretely, we avoid situations + // where we end up scavenging so often that we hurt allocation performance + // because of the additional overheads of using scavenged memory. + worked *= 1 + scavengeCostRatio + + // sleepTime is the amount of time we're going to sleep, based on the amount + // of time we worked, and the sleepRatio. + sleepTime := int64(worked / s.sleepRatio) + + var slept int64 + if s.sleepStub == nil { + // Set the timer. + // + // This must happen here instead of inside gopark + // because we can't close over any variables without + // failing escape analysis. + start := nanotime() + resetTimer(s.timer, start+sleepTime) + + // Mark ourselves as asleep and go to sleep. + s.parked = true + goparkunlock(&s.lock, waitReasonSleep, traceEvGoSleep, 2) + + // How long we actually slept for. + slept = nanotime() - start + + lock(&s.lock) + // Stop the timer here because s.wake is unable to do it for us. + // We don't really care if we succeed in stopping the timer. One + // reason we might fail is that we've already woken up, but the timer + // might be in the process of firing on some other P; essentially we're + // racing with it. That's totally OK. Double wake-ups are perfectly safe. + stopTimer(s.timer) + unlock(&s.lock) + } else { + unlock(&s.lock) + slept = s.sleepStub(sleepTime) + } - scavenge.timer = new(timer) - scavenge.timer.f = func(_ interface{}, _ uintptr) { - wakeScavenger() + // Stop here if we're cooling down from the controller. + if s.controllerCooldown > 0 { + // worked and slept aren't exact measures of time, but it's OK to be a bit + // sloppy here. We're just hoping we're avoiding some transient bad behavior. + t := slept + int64(worked) + if t > s.controllerCooldown { + s.controllerCooldown = 0 + } else { + s.controllerCooldown -= t + } + return } - gcenable_setup <- 1 - goparkunlock(&scavenge.lock, waitReasonGCScavengeWait, traceEvGoBlock, 1) + // idealFraction is the ideal % of overall application CPU time that we + // spend scavenging. + idealFraction := float64(scavengePercent) / 100.0 - // Exponentially-weighted moving average of the fraction of time this - // goroutine spends scavenging (that is, percent of a single CPU). - // It represents a measure of scheduling overheads which might extend - // the sleep or the critical time beyond what's expected. Assume no - // overhead to begin with. + // Calculate the CPU time spent. // - // TODO(mknyszek): Consider making this based on total CPU time of the - // application (i.e. scavengePercent * GOMAXPROCS). This isn't really - // feasible now because the scavenger acquires the heap lock over the - // scavenging operation, which means scavenging effectively blocks - // allocators and isn't scalable. However, given a scalable allocator, - // it makes sense to also make the scavenger scale with it; if you're - // allocating more frequently, then presumably you're also generating - // more work for the scavenger. - const idealFraction = scavengePercent / 100.0 - scavengeEWMA := float64(idealFraction) - - for { - released := uintptr(0) - - // Time in scavenging critical section. - crit := float64(0) - - // Run on the system stack since we grab the heap lock, - // and a stack growth with the heap lock means a deadlock. - systemstack(func() { - lock(&mheap_.lock) - - // If background scavenging is disabled or if there's no work to do just park. - retained, goal := heapRetained(), mheap_.scavengeGoal - if retained <= goal { - unlock(&mheap_.lock) - return - } - - // Scavenge one page, and measure the amount of time spent scavenging. - start := nanotime() - released = mheap_.pages.scavenge(physPageSize, true) - mheap_.pages.scav.released += released - crit = float64(nanotime() - start) + // This may be slightly inaccurate with respect to GOMAXPROCS, but we're + // recomputing this often enough relative to GOMAXPROCS changes in general + // (it only changes when the world is stopped, and not during a GC) that + // that small inaccuracy is in the noise. + cpuFraction := worked / ((float64(slept) + worked) * float64(s.gomaxprocs())) + + // Update the critSleepRatio, adjusting until we reach our ideal fraction. + var ok bool + s.sleepRatio, ok = s.sleepController.next(cpuFraction, idealFraction, float64(slept)+worked) + if !ok { + // The core assumption of the controller, that we can get a proportional + // response, broke down. This may be transient, so temporarily switch to + // sleeping a fixed, conservative amount. + s.sleepRatio = startingScavSleepRatio + s.controllerCooldown = 5e9 // 5 seconds. + + // Signal the scav trace printer to output this. + s.controllerFailed() + } +} - unlock(&mheap_.lock) - }) +// controllerFailed indicates that the scavenger's scheduling +// controller failed. +func (s *scavengerState) controllerFailed() { + lock(&s.lock) + s.printControllerReset = true + unlock(&s.lock) +} - if released == 0 { - lock(&scavenge.lock) - scavenge.parked = true - goparkunlock(&scavenge.lock, waitReasonGCScavengeWait, traceEvGoBlock, 1) - continue - } +// run is the body of the main scavenging loop. +// +// Returns the number of bytes released and the estimated time spent +// releasing those bytes. +// +// Must be run on the scavenger goroutine. +func (s *scavengerState) run() (released uintptr, worked float64) { + lock(&s.lock) + if getg() != s.g { + throw("tried to run scavenger from another goroutine") + } + unlock(&s.lock) - if released < physPageSize { - // If this happens, it means that we may have attempted to release part - // of a physical page, but the likely effect of that is that it released - // the whole physical page, some of which may have still been in-use. - // This could lead to memory corruption. Throw. - throw("released less than one physical page of memory") + for worked < minScavWorkTime { + // If something from outside tells us to stop early, stop. + if s.shouldStop() { + break } - // On some platforms we may see crit as zero if the time it takes to scavenge - // memory is less than the minimum granularity of its clock (e.g. Windows). + // scavengeQuantum is the amount of memory we try to scavenge + // in one go. A smaller value means the scavenger is more responsive + // to the scheduler in case of e.g. preemption. A larger value means + // that the overheads of scavenging are better amortized, so better + // scavenging throughput. + // + // The current value is chosen assuming a cost of ~10µs/physical page + // (this is somewhat pessimistic), which implies a worst-case latency of + // about 160µs for 4 KiB physical pages. The current value is biased + // toward latency over throughput. + const scavengeQuantum = 64 << 10 + + // Accumulate the amount of time spent scavenging. + r, duration := s.scavenge(scavengeQuantum) + + // On some platforms we may see end >= start if the time it takes to scavenge + // memory is less than the minimum granularity of its clock (e.g. Windows) or + // due to clock bugs. + // // In this case, just assume scavenging takes 10 µs per regular physical page // (determined empirically), and conservatively ignore the impact of huge pages // on timing. - // - // We shouldn't ever see a crit value less than zero unless there's a bug of - // some kind, either on our side or in the platform we're running on, but be - // defensive in that case as well. - const approxCritNSPerPhysicalPage = 10e3 - if crit <= 0 { - crit = approxCritNSPerPhysicalPage * float64(released/physPageSize) + const approxWorkedNSPerPhysicalPage = 10e3 + if duration == 0 { + worked += approxWorkedNSPerPhysicalPage * float64(r/physPageSize) + } else { + // TODO(mknyszek): If duration is small compared to worked, it could be + // rounded down to zero. Probably not a problem in practice because the + // values are all within a few orders of magnitude of each other but maybe + // worth worrying about. + worked += float64(duration) } + released += r - // Multiply the critical time by 1 + the ratio of the costs of using - // scavenged memory vs. scavenging memory. This forces us to pay down - // the cost of reusing this memory eagerly by sleeping for a longer period - // of time and scavenging less frequently. More concretely, we avoid situations - // where we end up scavenging so often that we hurt allocation performance - // because of the additional overheads of using scavenged memory. - crit *= 1 + scavengeCostRatio - - // If we spent more than 10 ms (for example, if the OS scheduled us away, or someone - // put their machine to sleep) in the critical section, bound the time we use to - // calculate at 10 ms to avoid letting the sleep time get arbitrarily high. - const maxCrit = 10e6 - if crit > maxCrit { - crit = maxCrit + // scavenge does not return until it either finds the requisite amount of + // memory to scavenge, or exhausts the heap. If we haven't found enough + // to scavenge, then the heap must be exhausted. + if r < scavengeQuantum { + break } - - // Compute the amount of time to sleep, assuming we want to use at most - // scavengePercent of CPU time. Take into account scheduling overheads - // that may extend the length of our sleep by multiplying by how far - // off we are from the ideal ratio. For example, if we're sleeping too - // much, then scavengeEMWA < idealFraction, so we'll adjust the sleep time - // down. - adjust := scavengeEWMA / idealFraction - sleepTime := int64(adjust * crit / (scavengePercent / 100.0)) - - // Go to sleep. - slept := scavengeSleep(sleepTime) - - // Compute the new ratio. - fraction := crit / (crit + float64(slept)) - - // Set a lower bound on the fraction. - // Due to OS-related anomalies we may "sleep" for an inordinate amount - // of time. Let's avoid letting the ratio get out of hand by bounding - // the sleep time we use in our EWMA. - const minFraction = 1.0 / 1000.0 - if fraction < minFraction { - fraction = minFraction + // When using fake time just do one loop. + if faketime != 0 { + break } + } + if released > 0 && released < physPageSize { + // If this happens, it means that we may have attempted to release part + // of a physical page, but the likely effect of that is that it released + // the whole physical page, some of which may have still been in-use. + // This could lead to memory corruption. Throw. + throw("released less than one physical page of memory") + } + return +} + +// Background scavenger. +// +// The background scavenger maintains the RSS of the application below +// the line described by the proportional scavenging statistics in +// the mheap struct. +func bgscavenge(c chan int) { + scavenger.init() - // Update scavengeEWMA by merging in the new crit/slept ratio. - const alpha = 0.5 - scavengeEWMA = alpha*fraction + (1-alpha)*scavengeEWMA + c <- 1 + scavenger.park() + + for { + released, workTime := scavenger.run() + if released == 0 { + scavenger.park() + continue + } + atomic.Xadduintptr(&mheap_.pages.scav.released, released) + scavenger.sleep(workTime) } } @@ -390,33 +634,22 @@ func bgscavenge() { // // Returns the amount of memory scavenged in bytes. // -// p.mheapLock must be held, but may be temporarily released if -// mayUnlock == true. -// -// Must run on the system stack because p.mheapLock must be held. -// -//go:systemstack -func (p *pageAlloc) scavenge(nbytes uintptr, mayUnlock bool) uintptr { - assertLockHeld(p.mheapLock) - - var ( - addrs addrRange - gen uint32 - ) +// scavenge always tries to scavenge nbytes worth of memory, and will +// only fail to do so if the heap is exhausted for now. +func (p *pageAlloc) scavenge(nbytes uintptr, shouldStop func() bool) uintptr { released := uintptr(0) for released < nbytes { - if addrs.size() == 0 { - if addrs, gen = p.scavengeReserve(); addrs.size() == 0 { - break - } + ci, pageIdx := p.scav.index.find() + if ci == 0 { + break + } + systemstack(func() { + released += p.scavengeOne(ci, pageIdx, nbytes-released) + }) + if shouldStop != nil && shouldStop() { + break } - r, a := p.scavengeOne(addrs, nbytes-released, mayUnlock) - released += r - addrs = a } - // Only unreserve the space which hasn't been scavenged or searched - // to ensure we always make progress. - p.scavengeUnreserve(addrs, gen) return released } @@ -425,153 +658,41 @@ func (p *pageAlloc) scavenge(nbytes uintptr, mayUnlock bool) uintptr { // released should be the amount of memory released since the last time this // was called, and forced indicates whether the scavenge was forced by the // application. -func printScavTrace(gen uint32, released uintptr, forced bool) { +// +// scavenger.lock must be held. +func printScavTrace(released uintptr, forced bool) { + assertLockHeld(&scavenger.lock) + printlock() - print("scav ", gen, " ", + print("scav ", released>>10, " KiB work, ", - atomic.Load64(&memstats.heap_released)>>10, " KiB total, ", - (atomic.Load64(&memstats.heap_inuse)*100)/heapRetained(), "% util", + gcController.heapReleased.load()>>10, " KiB total, ", + (gcController.heapInUse.load()*100)/heapRetained(), "% util", ) if forced { print(" (forced)") + } else if scavenger.printControllerReset { + print(" [controller reset]") + scavenger.printControllerReset = false } println() printunlock() } -// scavengeStartGen starts a new scavenge generation, resetting -// the scavenger's search space to the full in-use address space. -// -// p.mheapLock must be held. -// -// Must run on the system stack because p.mheapLock must be held. -// -//go:systemstack -func (p *pageAlloc) scavengeStartGen() { - assertLockHeld(p.mheapLock) - - if debug.scavtrace > 0 { - printScavTrace(p.scav.gen, p.scav.released, false) - } - p.inUse.cloneInto(&p.scav.inUse) - - // Pick the new starting address for the scavenger cycle. - var startAddr offAddr - if p.scav.scavLWM.lessThan(p.scav.freeHWM) { - // The "free" high watermark exceeds the "scavenged" low watermark, - // so there are free scavengable pages in parts of the address space - // that the scavenger already searched, the high watermark being the - // highest one. Pick that as our new starting point to ensure we - // see those pages. - startAddr = p.scav.freeHWM - } else { - // The "free" high watermark does not exceed the "scavenged" low - // watermark. This means the allocator didn't free any memory in - // the range we scavenged last cycle, so we might as well continue - // scavenging from where we were. - startAddr = p.scav.scavLWM - } - p.scav.inUse.removeGreaterEqual(startAddr.addr()) - - // reservationBytes may be zero if p.inUse.totalBytes is small, or if - // scavengeReservationShards is large. This case is fine as the scavenger - // will simply be turned off, but it does mean that scavengeReservationShards, - // in concert with pallocChunkBytes, dictates the minimum heap size at which - // the scavenger triggers. In practice this minimum is generally less than an - // arena in size, so virtually every heap has the scavenger on. - p.scav.reservationBytes = alignUp(p.inUse.totalBytes, pallocChunkBytes) / scavengeReservationShards - p.scav.gen++ - p.scav.released = 0 - p.scav.freeHWM = minOffAddr - p.scav.scavLWM = maxOffAddr -} - -// scavengeReserve reserves a contiguous range of the address space -// for scavenging. The maximum amount of space it reserves is proportional -// to the size of the heap. The ranges are reserved from the high addresses -// first. -// -// Returns the reserved range and the scavenge generation number for it. -// -// p.mheapLock must be held. -// -// Must run on the system stack because p.mheapLock must be held. -// -//go:systemstack -func (p *pageAlloc) scavengeReserve() (addrRange, uint32) { - assertLockHeld(p.mheapLock) - - // Start by reserving the minimum. - r := p.scav.inUse.removeLast(p.scav.reservationBytes) - - // Return early if the size is zero; we don't want to use - // the bogus address below. - if r.size() == 0 { - return r, p.scav.gen - } - - // The scavenger requires that base be aligned to a - // palloc chunk because that's the unit of operation for - // the scavenger, so align down, potentially extending - // the range. - newBase := alignDown(r.base.addr(), pallocChunkBytes) - - // Remove from inUse however much extra we just pulled out. - p.scav.inUse.removeGreaterEqual(newBase) - r.base = offAddr{newBase} - return r, p.scav.gen -} - -// scavengeUnreserve returns an unscavenged portion of a range that was -// previously reserved with scavengeReserve. -// -// p.mheapLock must be held. -// -// Must run on the system stack because p.mheapLock must be held. -// -//go:systemstack -func (p *pageAlloc) scavengeUnreserve(r addrRange, gen uint32) { - assertLockHeld(p.mheapLock) - - if r.size() == 0 || gen != p.scav.gen { - return - } - if r.base.addr()%pallocChunkBytes != 0 { - throw("unreserving unaligned region") - } - p.scav.inUse.add(r) -} - -// scavengeOne walks over address range work until it finds +// scavengeOne walks over the chunk at chunk index ci and searches for // a contiguous run of pages to scavenge. It will try to scavenge // at most max bytes at once, but may scavenge more to avoid // breaking huge pages. Once it scavenges some memory it returns // how much it scavenged in bytes. // -// Returns the number of bytes scavenged and the part of work -// which was not yet searched. -// -// work's base address must be aligned to pallocChunkBytes. +// searchIdx is the page index to start searching from in ci. // -// p.mheapLock must be held, but may be temporarily released if -// mayUnlock == true. +// Returns the number of bytes scavenged. // -// Must run on the system stack because p.mheapLock must be held. +// Must run on the systemstack because it acquires p.mheapLock. // //go:systemstack -func (p *pageAlloc) scavengeOne(work addrRange, max uintptr, mayUnlock bool) (uintptr, addrRange) { - assertLockHeld(p.mheapLock) - - // Defensively check if we've received an empty address range. - // If so, just return. - if work.size() == 0 { - // Nothing to do. - return 0, work - } - // Check the prerequisites of work. - if work.base.addr()%pallocChunkBytes != 0 { - throw("scavengeOne called with unaligned work region") - } +func (p *pageAlloc) scavengeOne(ci chunkIdx, searchIdx uint, max uintptr) uintptr { // Calculate the maximum number of pages to scavenge. // // This should be alignUp(max, pageSize) / pageSize but max can and will @@ -593,152 +714,61 @@ func (p *pageAlloc) scavengeOne(work addrRange, max uintptr, mayUnlock bool) (ui minPages = 1 } - // Helpers for locking and unlocking only if mayUnlock == true. - lockHeap := func() { - if mayUnlock { - lock(p.mheapLock) - } - } - unlockHeap := func() { - if mayUnlock { - unlock(p.mheapLock) - } - } - - // Fast path: check the chunk containing the top-most address in work, - // starting at that address's page index in the chunk. - // - // Note that work.end() is exclusive, so get the chunk we care about - // by subtracting 1. - maxAddr := work.limit.addr() - 1 - maxChunk := chunkIndex(maxAddr) - if p.summary[len(p.summary)-1][maxChunk].max() >= uint(minPages) { + lock(p.mheapLock) + if p.summary[len(p.summary)-1][ci].max() >= uint(minPages) { // We only bother looking for a candidate if there at least // minPages free pages at all. - base, npages := p.chunkOf(maxChunk).findScavengeCandidate(chunkPageIndex(maxAddr), minPages, maxPages) + base, npages := p.chunkOf(ci).findScavengeCandidate(searchIdx, minPages, maxPages) // If we found something, scavenge it and return! if npages != 0 { - work.limit = offAddr{p.scavengeRangeLocked(maxChunk, base, npages)} - - assertLockHeld(p.mheapLock) // Must be locked on return. - return uintptr(npages) * pageSize, work - } - } - // Update the limit to reflect the fact that we checked maxChunk already. - work.limit = offAddr{chunkBase(maxChunk)} - - // findCandidate finds the next scavenge candidate in work optimistically. - // - // Returns the candidate chunk index and true on success, and false on failure. - // - // The heap need not be locked. - findCandidate := func(work addrRange) (chunkIdx, bool) { - // Iterate over this work's chunks. - for i := chunkIndex(work.limit.addr() - 1); i >= chunkIndex(work.base.addr()); i-- { - // If this chunk is totally in-use or has no unscavenged pages, don't bother - // doing a more sophisticated check. - // - // Note we're accessing the summary and the chunks without a lock, but - // that's fine. We're being optimistic anyway. - - // Check quickly if there are enough free pages at all. - if p.summary[len(p.summary)-1][i].max() < uint(minPages) { - continue - } - - // Run over the chunk looking harder for a candidate. Again, we could - // race with a lot of different pieces of code, but we're just being - // optimistic. Make sure we load the l2 pointer atomically though, to - // avoid races with heap growth. It may or may not be possible to also - // see a nil pointer in this case if we do race with heap growth, but - // just defensively ignore the nils. This operation is optimistic anyway. - l2 := (*[1 << pallocChunksL2Bits]pallocData)(atomic.Loadp(unsafe.Pointer(&p.chunks[i.l1()]))) - if l2 != nil && l2[i.l2()].hasScavengeCandidate(minPages) { - return i, true + // Compute the full address for the start of the range. + addr := chunkBase(ci) + uintptr(base)*pageSize + + // Mark the range we're about to scavenge as allocated, because + // we don't want any allocating goroutines to grab it while + // the scavenging is in progress. + if scav := p.allocRange(addr, uintptr(npages)); scav != 0 { + throw("double scavenge") } - } - return 0, false - } - - // Slow path: iterate optimistically over the in-use address space - // looking for any free and unscavenged page. If we think we see something, - // lock and verify it! - for work.size() != 0 { - unlockHeap() - // Search for the candidate. - candidateChunkIdx, ok := findCandidate(work) + // With that done, it's safe to unlock. + unlock(p.mheapLock) - // Lock the heap. We need to do this now if we found a candidate or not. - // If we did, we'll verify it. If not, we need to lock before returning - // anyway. - lockHeap() + if !p.test { + // Only perform the actual scavenging if we're not in a test. + // It's dangerous to do so otherwise. + sysUnused(unsafe.Pointer(addr), uintptr(npages)*pageSize) + + // Update global accounting only when not in test, otherwise + // the runtime's accounting will be wrong. + nbytes := int64(npages) * pageSize + gcController.heapReleased.add(nbytes) + gcController.heapFree.add(-nbytes) + + stats := memstats.heapStats.acquire() + atomic.Xaddint64(&stats.committed, -nbytes) + atomic.Xaddint64(&stats.released, nbytes) + memstats.heapStats.release() + } - if !ok { - // We didn't find a candidate, so we're done. - work.limit = work.base - break - } + // Relock the heap, because now we need to make these pages + // available allocation. Free them back to the page allocator. + lock(p.mheapLock) + p.free(addr, uintptr(npages), true) - // Find, verify, and scavenge if we can. - chunk := p.chunkOf(candidateChunkIdx) - base, npages := chunk.findScavengeCandidate(pallocChunkPages-1, minPages, maxPages) - if npages > 0 { - work.limit = offAddr{p.scavengeRangeLocked(candidateChunkIdx, base, npages)} + // Mark the range as scavenged. + p.chunkOf(ci).scavenged.setRange(base, npages) + unlock(p.mheapLock) - assertLockHeld(p.mheapLock) // Must be locked on return. - return uintptr(npages) * pageSize, work + return uintptr(npages) * pageSize } - - // We were fooled, so let's continue from where we left off. - work.limit = offAddr{chunkBase(candidateChunkIdx)} - } - - assertLockHeld(p.mheapLock) // Must be locked on return. - return 0, work -} - -// scavengeRangeLocked scavenges the given region of memory. -// The region of memory is described by its chunk index (ci), -// the starting page index of the region relative to that -// chunk (base), and the length of the region in pages (npages). -// -// Returns the base address of the scavenged region. -// -// p.mheapLock must be held. -func (p *pageAlloc) scavengeRangeLocked(ci chunkIdx, base, npages uint) uintptr { - assertLockHeld(p.mheapLock) - - p.chunkOf(ci).scavenged.setRange(base, npages) - - // Compute the full address for the start of the range. - addr := chunkBase(ci) + uintptr(base)*pageSize - - // Update the scavenge low watermark. - if oAddr := (offAddr{addr}); oAddr.lessThan(p.scav.scavLWM) { - p.scav.scavLWM = oAddr - } - - // Only perform the actual scavenging if we're not in a test. - // It's dangerous to do so otherwise. - if p.test { - return addr } - sysUnused(unsafe.Pointer(addr), uintptr(npages)*pageSize) + // Mark this chunk as having no free pages. + p.scav.index.clear(ci) + unlock(p.mheapLock) - // Update global accounting only when not in test, otherwise - // the runtime's accounting will be wrong. - nbytes := int64(npages) * pageSize - atomic.Xadd64(&memstats.heap_released, nbytes) - - // Update consistent accounting too. - stats := memstats.heapStats.acquire() - atomic.Xaddint64(&stats.committed, -nbytes) - atomic.Xaddint64(&stats.released, nbytes) - memstats.heapStats.release() - - return addr + return 0 } // fillAligned returns x but with all zeroes in m-aligned @@ -800,38 +830,6 @@ func fillAligned(x uint64, m uint) uint64 { return ^((x - (x >> (m - 1))) | x) } -// hasScavengeCandidate returns true if there's any min-page-aligned groups of -// min pages of free-and-unscavenged memory in the region represented by this -// pallocData. -// -// min must be a non-zero power of 2 <= maxPagesPerPhysPage. -func (m *pallocData) hasScavengeCandidate(min uintptr) bool { - if min&(min-1) != 0 || min == 0 { - print("runtime: min = ", min, "\n") - throw("min must be a non-zero power of 2") - } else if min > maxPagesPerPhysPage { - print("runtime: min = ", min, "\n") - throw("min too large") - } - - // The goal of this search is to see if the chunk contains any free and unscavenged memory. - for i := len(m.scavenged) - 1; i >= 0; i-- { - // 1s are scavenged OR non-free => 0s are unscavenged AND free - // - // TODO(mknyszek): Consider splitting up fillAligned into two - // functions, since here we technically could get by with just - // the first half of its computation. It'll save a few instructions - // but adds some additional code complexity. - x := fillAligned(m.scavenged[i]|m.pallocBits[i], uint(min)) - - // Quickly skip over chunks of non-free or scavenged pages. - if x != ^uint64(0) { - return true - } - } - return false -} - // findScavengeCandidate returns a start index and a size for this pallocData // segment which represents a contiguous region of free and unscavenged memory. // @@ -951,3 +949,157 @@ func (m *pallocData) findScavengeCandidate(searchIdx uint, min, max uintptr) (ui } return start, size } + +// scavengeIndex is a structure for efficiently managing which pageAlloc chunks have +// memory available to scavenge. +type scavengeIndex struct { + // chunks is a bitmap representing the entire address space. Each bit represents + // a single chunk, and a 1 value indicates the presence of pages available for + // scavenging. Updates to the bitmap are serialized by the pageAlloc lock. + // + // The underlying storage of chunks is platform dependent and may not even be + // totally mapped read/write. min and max reflect the extent that is safe to access. + // min is inclusive, max is exclusive. + // + // searchAddr is the maximum address (in the offset address space, so we have a linear + // view of the address space; see mranges.go:offAddr) containing memory available to + // scavenge. It is a hint to the find operation to avoid O(n^2) behavior in repeated lookups. + // + // searchAddr is always inclusive and should be the base address of the highest runtime + // page available for scavenging. + // + // searchAddr is managed by both find and mark. + // + // Normally, find monotonically decreases searchAddr as it finds no more free pages to + // scavenge. However, mark, when marking a new chunk at an index greater than the current + // searchAddr, sets searchAddr to the *negative* index into chunks of that page. The trick here + // is that concurrent calls to find will fail to monotonically decrease searchAddr, and so they + // won't barge over new memory becoming available to scavenge. Furthermore, this ensures + // that some future caller of find *must* observe the new high index. That caller + // (or any other racing with it), then makes searchAddr positive before continuing, bringing + // us back to our monotonically decreasing steady-state. + // + // A pageAlloc lock serializes updates between min, max, and searchAddr, so abs(searchAddr) + // is always guaranteed to be >= min and < max (converted to heap addresses). + // + // TODO(mknyszek): Ideally we would use something bigger than a uint8 for faster + // iteration like uint32, but we lack the bit twiddling intrinsics. We'd need to either + // copy them from math/bits or fix the fact that we can't import math/bits' code from + // the runtime due to compiler instrumentation. + searchAddr atomicOffAddr + chunks []atomic.Uint8 + minHeapIdx atomic.Int32 + min, max atomic.Int32 +} + +// find returns the highest chunk index that may contain pages available to scavenge. +// It also returns an offset to start searching in the highest chunk. +func (s *scavengeIndex) find() (chunkIdx, uint) { + searchAddr, marked := s.searchAddr.Load() + if searchAddr == minOffAddr.addr() { + // We got a cleared search addr. + return 0, 0 + } + + // Starting from searchAddr's chunk, and moving down to minHeapIdx, + // iterate until we find a chunk with pages to scavenge. + min := s.minHeapIdx.Load() + searchChunk := chunkIndex(uintptr(searchAddr)) + start := int32(searchChunk / 8) + for i := start; i >= min; i-- { + // Skip over irrelevant address space. + chunks := s.chunks[i].Load() + if chunks == 0 { + continue + } + // Note that we can't have 8 leading zeroes here because + // we necessarily skipped that case. So, what's left is + // an index. If there are no zeroes, we want the 7th + // index, if 1 zero, the 6th, and so on. + n := 7 - sys.LeadingZeros8(chunks) + ci := chunkIdx(uint(i)*8 + uint(n)) + if searchChunk == ci { + return ci, chunkPageIndex(uintptr(searchAddr)) + } + // Try to reduce searchAddr to newSearchAddr. + newSearchAddr := chunkBase(ci) + pallocChunkBytes - pageSize + if marked { + // Attempt to be the first one to decrease the searchAddr + // after an increase. If we fail, that means there was another + // increase, or somebody else got to it before us. Either way, + // it doesn't matter. We may lose some performance having an + // incorrect search address, but it's far more important that + // we don't miss updates. + s.searchAddr.StoreUnmark(searchAddr, newSearchAddr) + } else { + // Decrease searchAddr. + s.searchAddr.StoreMin(newSearchAddr) + } + return ci, pallocChunkPages - 1 + } + // Clear searchAddr, because we've exhausted the heap. + s.searchAddr.Clear() + return 0, 0 +} + +// mark sets the inclusive range of chunks between indices start and end as +// containing pages available to scavenge. +// +// Must be serialized with other mark, markRange, and clear calls. +func (s *scavengeIndex) mark(base, limit uintptr) { + start, end := chunkIndex(base), chunkIndex(limit-pageSize) + if start == end { + // Within a chunk. + mask := uint8(1 << (start % 8)) + s.chunks[start/8].Or(mask) + } else if start/8 == end/8 { + // Within the same byte in the index. + mask := uint8(uint16(1<<(end-start+1))-1) << (start % 8) + s.chunks[start/8].Or(mask) + } else { + // Crosses multiple bytes in the index. + startAligned := chunkIdx(alignUp(uintptr(start), 8)) + endAligned := chunkIdx(alignDown(uintptr(end), 8)) + + // Do the end of the first byte first. + if width := startAligned - start; width > 0 { + mask := uint8(uint16(1< 0 { + mask := uint8(uint16(1< avail { + bytes = uintptr(avail) + } + t := workedTime(bytes) + if bytes != 0 { + availableWork.Add(-int64(bytes)) + totalWorked.Add(t) + } + return bytes, t + } + s.ShouldStop = func() bool { + if availableWork.Load() <= stopAt.Load() { + return true + } + return false + } + s.GoMaxProcs = func() int32 { + return 1 + } + + // Define a helper for verifying that various properties hold. + verifyScavengerState := func(t *testing.T, expWork uint64) { + t.Helper() + + // Check to make sure it did the amount of work we expected. + if workDone := uint64(s.Released()); workDone != expWork { + t.Errorf("want %d bytes of work done, got %d", expWork, workDone) + } + // Check to make sure the scavenger is meeting its CPU target. + idealFraction := float64(ScavengePercent) / 100.0 + cpuFraction := float64(totalWorked.Load()) / float64(totalWorked.Load()+totalSlept.Load()) + if cpuFraction < idealFraction-0.005 || cpuFraction > idealFraction+0.005 { + t.Errorf("want %f CPU fraction, got %f", idealFraction, cpuFraction) + } + } + + // Start the scavenger. + s.Start() + + // Set up some work and let the scavenger run to completion. + availableWork.Store(totalWork) + s.Wake() + if !s.BlockUntilParked(2e9 /* 2 seconds */) { + t.Fatal("timed out waiting for scavenger to run to completion") + } + // Run a check. + verifyScavengerState(t, totalWork) + + // Now let's do it again and see what happens when we have no work to do. + // It should've gone right back to sleep. + s.Wake() + if !s.BlockUntilParked(2e9 /* 2 seconds */) { + t.Fatal("timed out waiting for scavenger to run to completion") + } + // Run another check. + verifyScavengerState(t, totalWork) + + // One more time, this time doing the same amount of work as the first time. + // Let's see if we can get the scavenger to continue. + availableWork.Store(totalWork) + s.Wake() + if !s.BlockUntilParked(2e9 /* 2 seconds */) { + t.Fatal("timed out waiting for scavenger to run to completion") + } + // Run another check. + verifyScavengerState(t, 2*totalWork) + + // This time, let's stop after a certain amount of work. + // + // Pick a stopping point such that when subtracted from totalWork + // we get a multiple of a relatively large power of 2. verifyScavengerState + // always makes an exact check, but the scavenger might go a little over, + // which is OK. If this breaks often or gets annoying to maintain, modify + // verifyScavengerState. + availableWork.Store(totalWork) + stoppingPoint := uint64(1<<20 - 3*PhysPageSize) + stopAt.Store(stoppingPoint) + s.Wake() + if !s.BlockUntilParked(2e9 /* 2 seconds */) { + t.Fatal("timed out waiting for scavenger to run to completion") + } + // Run another check. + verifyScavengerState(t, 2*totalWork+(totalWork-stoppingPoint)) + + // Clean up. + s.Stop() +} + +func TestScavengeIndex(t *testing.T) { + setup := func(t *testing.T) (func(ChunkIdx, uint), func(uintptr, uintptr)) { + t.Helper() + + // Pick some reasonable bounds. We don't need a huge range just to test. + si := NewScavengeIndex(BaseChunkIdx, BaseChunkIdx+64) + find := func(want ChunkIdx, wantOffset uint) { + t.Helper() + + got, gotOffset := si.Find() + if want != got { + t.Errorf("find: wanted chunk index %d, got %d", want, got) + } + if want != got { + t.Errorf("find: wanted page offset %d, got %d", wantOffset, gotOffset) + } + if t.Failed() { + t.FailNow() + } + si.Clear(got) + } + mark := func(base, limit uintptr) { + t.Helper() + + si.Mark(base, limit) + } + return find, mark + } + t.Run("Uninitialized", func(t *testing.T) { + find, _ := setup(t) + find(0, 0) + }) + t.Run("OnePage", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 3), PageBase(BaseChunkIdx, 4)) + find(BaseChunkIdx, 3) + find(0, 0) + }) + t.Run("FirstPage", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx, 1)) + find(BaseChunkIdx, 0) + find(0, 0) + }) + t.Run("SeveralPages", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 9), PageBase(BaseChunkIdx, 14)) + find(BaseChunkIdx, 13) + find(0, 0) + }) + t.Run("WholeChunk", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)) + find(BaseChunkIdx, PallocChunkPages-1) + find(0, 0) + }) + t.Run("LastPage", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, PallocChunkPages-1), PageBase(BaseChunkIdx+1, 0)) + find(BaseChunkIdx, PallocChunkPages-1) + find(0, 0) + }) + t.Run("TwoChunks", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 128), PageBase(BaseChunkIdx+1, 128)) + find(BaseChunkIdx+1, 127) + find(BaseChunkIdx, PallocChunkPages-1) + find(0, 0) + }) + t.Run("TwoChunksOffset", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx+7, 128), PageBase(BaseChunkIdx+8, 129)) + find(BaseChunkIdx+8, 128) + find(BaseChunkIdx+7, PallocChunkPages-1) + find(0, 0) + }) + t.Run("SevenChunksOffset", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx+6, 11), PageBase(BaseChunkIdx+13, 15)) + find(BaseChunkIdx+13, 14) + for i := BaseChunkIdx + 12; i >= BaseChunkIdx+6; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("ThirtyTwoChunks", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0)) + for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("ThirtyTwoChunksOffset", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx+3, 0), PageBase(BaseChunkIdx+35, 0)) + for i := BaseChunkIdx + 34; i >= BaseChunkIdx+3; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("Mark", func(t *testing.T) { + find, mark := setup(t) + for i := BaseChunkIdx; i < BaseChunkIdx+32; i++ { + mark(PageBase(i, 0), PageBase(i+1, 0)) + } + for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("MarkInterleaved", func(t *testing.T) { + find, mark := setup(t) + for i := BaseChunkIdx; i < BaseChunkIdx+32; i++ { + mark(PageBase(i, 0), PageBase(i+1, 0)) + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("MarkIdempotentOneChunk", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)) + find(BaseChunkIdx, PallocChunkPages-1) + find(0, 0) + }) + t.Run("MarkIdempotentThirtyTwoChunks", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0)) + mark(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+32, 0)) + for i := BaseChunkIdx + 31; i >= BaseChunkIdx; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) + t.Run("MarkIdempotentThirtyTwoChunksOffset", func(t *testing.T) { + find, mark := setup(t) + mark(PageBase(BaseChunkIdx+4, 0), PageBase(BaseChunkIdx+31, 0)) + mark(PageBase(BaseChunkIdx+5, 0), PageBase(BaseChunkIdx+36, 0)) + for i := BaseChunkIdx + 35; i >= BaseChunkIdx+4; i-- { + find(i, PallocChunkPages-1) + } + find(0, 0) + }) +} diff --git a/src/runtime/mgcstack.go b/src/runtime/mgcstack.go index 92d58485f6f7a2..6b552203ee4243 100644 --- a/src/runtime/mgcstack.go +++ b/src/runtime/mgcstack.go @@ -95,6 +95,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/sys" "unsafe" ) @@ -103,17 +104,15 @@ const stackTraceDebug = false // Buffer for pointers found during stack tracing. // Must be smaller than or equal to workbuf. -// -//go:notinheap type stackWorkBuf struct { + _ sys.NotInHeap stackWorkBufHdr - obj [(_WorkbufSize - unsafe.Sizeof(stackWorkBufHdr{})) / sys.PtrSize]uintptr + obj [(_WorkbufSize - unsafe.Sizeof(stackWorkBufHdr{})) / goarch.PtrSize]uintptr } // Header declaration must come after the buf declaration above, because of issue #14620. -// -//go:notinheap type stackWorkBufHdr struct { + _ sys.NotInHeap workbufhdr next *stackWorkBuf // linked list of workbufs // Note: we could theoretically repurpose lfnode.next as this next pointer. @@ -123,15 +122,14 @@ type stackWorkBufHdr struct { // Buffer for stack objects found on a goroutine stack. // Must be smaller than or equal to workbuf. -// -//go:notinheap type stackObjectBuf struct { + _ sys.NotInHeap stackObjectBufHdr obj [(_WorkbufSize - unsafe.Sizeof(stackObjectBufHdr{})) / unsafe.Sizeof(stackObject{})]stackObject } -//go:notinheap type stackObjectBufHdr struct { + _ sys.NotInHeap workbufhdr next *stackObjectBuf } @@ -147,9 +145,8 @@ func init() { // A stackObject represents a variable on the stack that has had // its address taken. -// -//go:notinheap type stackObject struct { + _ sys.NotInHeap off uint32 // offset above stack.lo size uint32 // size of object r *stackObjectRecord // info of the object (for ptr/nonptr bits). nil if object has been scanned. @@ -158,6 +155,7 @@ type stackObject struct { } // obj.r = r, but with no write barrier. +// //go:nowritebarrier func (obj *stackObject) setRecord(r *stackObjectRecord) { // Types of stack objects are always in read-only memory, not the heap. diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go index 8fe3a653407794..0e2cfdc9c4ee85 100644 --- a/src/runtime/mgcsweep.go +++ b/src/runtime/mgcsweep.go @@ -41,6 +41,10 @@ type sweepdata struct { nbgsweep uint32 npausesweep uint32 + // active tracks outstanding sweepers and the sweep + // termination condition. + active activeSweep + // centralIndex is the current unswept span class. // It represents an index into the mcentral span // sets. Accessed and updated via its load and @@ -116,6 +120,109 @@ func (h *mheap) nextSpanForSweep() *mspan { return nil } +const sweepDrainedMask = 1 << 31 + +// activeSweep is a type that captures whether sweeping +// is done, and whether there are any outstanding sweepers. +// +// Every potential sweeper must call begin() before they look +// for work, and end() after they've finished sweeping. +type activeSweep struct { + // state is divided into two parts. + // + // The top bit (masked by sweepDrainedMask) is a boolean + // value indicating whether all the sweep work has been + // drained from the queue. + // + // The rest of the bits are a counter, indicating the + // number of outstanding concurrent sweepers. + state atomic.Uint32 +} + +// begin registers a new sweeper. Returns a sweepLocker +// for acquiring spans for sweeping. Any outstanding sweeper blocks +// sweep termination. +// +// If the sweepLocker is invalid, the caller can be sure that all +// outstanding sweep work has been drained, so there is nothing left +// to sweep. Note that there may be sweepers currently running, so +// this does not indicate that all sweeping has completed. +// +// Even if the sweepLocker is invalid, its sweepGen is always valid. +func (a *activeSweep) begin() sweepLocker { + for { + state := a.state.Load() + if state&sweepDrainedMask != 0 { + return sweepLocker{mheap_.sweepgen, false} + } + if a.state.CompareAndSwap(state, state+1) { + return sweepLocker{mheap_.sweepgen, true} + } + } +} + +// end deregisters a sweeper. Must be called once for each time +// begin is called if the sweepLocker is valid. +func (a *activeSweep) end(sl sweepLocker) { + if sl.sweepGen != mheap_.sweepgen { + throw("sweeper left outstanding across sweep generations") + } + for { + state := a.state.Load() + if (state&^sweepDrainedMask)-1 >= sweepDrainedMask { + throw("mismatched begin/end of activeSweep") + } + if a.state.CompareAndSwap(state, state-1) { + if state != sweepDrainedMask { + return + } + if debug.gcpacertrace > 0 { + live := gcController.heapLive.Load() + print("pacer: sweep done at heap size ", live>>20, "MB; allocated ", (live-mheap_.sweepHeapLiveBasis)>>20, "MB during sweep; swept ", mheap_.pagesSwept.Load(), " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n") + } + return + } + } +} + +// markDrained marks the active sweep cycle as having drained +// all remaining work. This is safe to be called concurrently +// with all other methods of activeSweep, though may race. +// +// Returns true if this call was the one that actually performed +// the mark. +func (a *activeSweep) markDrained() bool { + for { + state := a.state.Load() + if state&sweepDrainedMask != 0 { + return false + } + if a.state.CompareAndSwap(state, state|sweepDrainedMask) { + return true + } + } +} + +// sweepers returns the current number of active sweepers. +func (a *activeSweep) sweepers() uint32 { + return a.state.Load() &^ sweepDrainedMask +} + +// isDone returns true if all sweep work has been drained and no more +// outstanding sweepers exist. That is, when the sweep phase is +// completely done. +func (a *activeSweep) isDone() bool { + return a.state.Load() == sweepDrainedMask +} + +// reset sets up the activeSweep for the next sweep cycle. +// +// The world must be stopped. +func (a *activeSweep) reset() { + assertWorldStopped() + a.state.Store(0) +} + // finishsweep_m ensures that all spans are swept. // // The world must be stopped. This ensures there are no sweeps in @@ -134,6 +241,15 @@ func finishsweep_m() { sweep.npausesweep++ } + // Make sure there aren't any outstanding sweepers left. + // At this point, with the world stopped, it means one of two + // things. Either we were able to preempt a sweeper, or that + // a sweeper didn't call sweep.active.end when it should have. + // Both cases indicate a bug, so throw. + if sweep.active.sweepers() != 0 { + throw("active sweepers found at start of mark phase") + } + // Reset all the unswept buffers, which should be empty. // Do this in sweep termination as opposed to mark termination // so that we can catch unswept spans and reclaim blocks as @@ -148,18 +264,18 @@ func finishsweep_m() { // Sweeping is done, so if the scavenger isn't already awake, // wake it up. There's definitely work for it to do at this // point. - wakeScavenger() + scavenger.wake() nextMarkBitArenaEpoch() } -func bgsweep() { +func bgsweep(c chan int) { sweep.g = getg() lockInit(&sweep.lock, lockRankSweep) lock(&sweep.lock) sweep.parked = true - gcenable_setup <- 1 + c <- 1 goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) for { @@ -183,15 +299,11 @@ func bgsweep() { } } -// sweepLocker acquires sweep ownership of spans and blocks sweep -// completion. +// sweepLocker acquires sweep ownership of spans. type sweepLocker struct { // sweepGen is the sweep generation of the heap. sweepGen uint32 - // blocking indicates that this tracker is blocking sweep - // completion, usually as a result of acquiring sweep - // ownership of at least one span. - blocking bool + valid bool } // sweepLocked represents sweep ownership of a span. @@ -199,22 +311,16 @@ type sweepLocked struct { *mspan } -func newSweepLocker() sweepLocker { - return sweepLocker{ - sweepGen: mheap_.sweepgen, - } -} - // tryAcquire attempts to acquire sweep ownership of span s. If it // successfully acquires ownership, it blocks sweep completion. func (l *sweepLocker) tryAcquire(s *mspan) (sweepLocked, bool) { + if !l.valid { + throw("use of invalid sweepLocker") + } // Check before attempting to CAS. if atomic.Load(&s.sweepgen) != l.sweepGen-2 { return sweepLocked{}, false } - // Add ourselves to sweepers before potentially taking - // ownership. - l.blockCompletion() // Attempt to acquire sweep ownership of s. if !atomic.Cas(&s.sweepgen, l.sweepGen-2, l.sweepGen-1) { return sweepLocked{}, false @@ -222,48 +328,22 @@ func (l *sweepLocker) tryAcquire(s *mspan) (sweepLocked, bool) { return sweepLocked{s}, true } -// blockCompletion blocks sweep completion without acquiring any -// specific spans. -func (l *sweepLocker) blockCompletion() { - if !l.blocking { - atomic.Xadd(&mheap_.sweepers, +1) - l.blocking = true - } -} - -func (l *sweepLocker) dispose() { - if !l.blocking { - return - } - // Decrement the number of active sweepers and if this is the - // last one, mark sweep as complete. - l.blocking = false - if atomic.Xadd(&mheap_.sweepers, -1) == 0 && atomic.Load(&mheap_.sweepDrained) != 0 { - l.sweepIsDone() - } -} - -func (l *sweepLocker) sweepIsDone() { - if debug.gcpacertrace > 0 { - print("pacer: sweep done at heap size ", gcController.heapLive>>20, "MB; allocated ", (gcController.heapLive-mheap_.sweepHeapLiveBasis)>>20, "MB during sweep; swept ", mheap_.pagesSwept, " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n") - } -} - // sweepone sweeps some unswept heap span and returns the number of pages returned // to the heap, or ^uintptr(0) if there was nothing to sweep. func sweepone() uintptr { - _g_ := getg() + gp := getg() - // increment locks to ensure that the goroutine is not preempted + // Increment locks to ensure that the goroutine is not preempted // in the middle of sweep thus leaving the span in an inconsistent state for next GC - _g_.m.locks++ - if atomic.Load(&mheap_.sweepDrained) != 0 { - _g_.m.locks-- - return ^uintptr(0) - } + gp.m.locks++ + // TODO(austin): sweepone is almost always called in a loop; // lift the sweepLocker into its callers. - sl := newSweepLocker() + sl := sweep.active.begin() + if !sl.valid { + gp.m.locks-- + return ^uintptr(0) + } // Find a span to sweep. npages := ^uintptr(0) @@ -271,7 +351,7 @@ func sweepone() uintptr { for { s := mheap_.nextSpanForSweep() if s == nil { - noMoreWork = atomic.Cas(&mheap_.sweepDrained, 0, 1) + noMoreWork = sweep.active.markDrained() break } if state := s.state.get(); state != mSpanInUse { @@ -291,7 +371,7 @@ func sweepone() uintptr { // Whole span was freed. Count it toward the // page reclaimer credit since these pages can // now be used for span allocation. - atomic.Xadduintptr(&mheap_.reclaimCredit, npages) + mheap_.reclaimCredit.Add(npages) } else { // Span is still in-use, so this returned no // pages to the heap and the span needs to @@ -301,37 +381,37 @@ func sweepone() uintptr { break } } - - sl.dispose() + sweep.active.end(sl) if noMoreWork { // The sweep list is empty. There may still be // concurrent sweeps running, but we're at least very // close to done sweeping. - // Move the scavenge gen forward (signalling + // Move the scavenge gen forward (signaling // that there's new work to do) and wake the scavenger. // // The scavenger is signaled by the last sweeper because once // sweeping is done, we will definitely have useful work for // the scavenger to do, since the scavenger only runs over the - // heap once per GC cyle. This update is not done during sweep + // heap once per GC cycle. This update is not done during sweep // termination because in some cases there may be a long delay // between sweep done and sweep termination (e.g. not enough // allocations to trigger a GC) which would be nice to fill in // with scavenging work. - systemstack(func() { - lock(&mheap_.lock) - mheap_.pages.scavengeStartGen() - unlock(&mheap_.lock) - }) - // Since we might sweep in an allocation path, it's not possible - // for us to wake the scavenger directly via wakeScavenger, since - // it could allocate. Ask sysmon to do it for us instead. - readyForScavenger() - } - - _g_.m.locks-- + if debug.scavtrace > 0 { + systemstack(func() { + lock(&mheap_.lock) + released := atomic.Loaduintptr(&mheap_.pages.scav.released) + printScavTrace(released, false) + atomic.Storeuintptr(&mheap_.pages.scav.released, 0) + unlock(&mheap_.lock) + }) + } + scavenger.ready() + } + + gp.m.locks-- return npages } @@ -342,33 +422,38 @@ func sweepone() uintptr { // GC runs; to prevent that the caller must be non-preemptible or must // somehow block GC progress. func isSweepDone() bool { - // Check that all spans have at least begun sweeping and there - // are no active sweepers. If both are true, then all spans - // have finished sweeping. - return atomic.Load(&mheap_.sweepDrained) != 0 && atomic.Load(&mheap_.sweepers) == 0 + return sweep.active.isDone() } // Returns only when span s has been swept. +// //go:nowritebarrier func (s *mspan) ensureSwept() { // Caller must disable preemption. // Otherwise when this function returns the span can become unswept again // (if GC is triggered on another goroutine). - _g_ := getg() - if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 { + gp := getg() + if gp.m.locks == 0 && gp.m.mallocing == 0 && gp != gp.m.g0 { throw("mspan.ensureSwept: m is not locked") } - sl := newSweepLocker() - // The caller must be sure that the span is a mSpanInUse span. - if s, ok := sl.tryAcquire(s); ok { - s.sweep(false) - sl.dispose() - return + // If this operation fails, then that means that there are + // no more spans to be swept. In this case, either s has already + // been swept, or is about to be acquired for sweeping and swept. + sl := sweep.active.begin() + if sl.valid { + // The caller must be sure that the span is a mSpanInUse span. + if s, ok := sl.tryAcquire(s); ok { + s.sweep(false) + sweep.active.end(sl) + return + } + sweep.active.end(sl) } - sl.dispose() - // unfortunate condition, and we don't have efficient means to wait + // Unfortunately we can't sweep the span ourselves. Somebody else + // got to it first. We don't have efficient means to wait, but that's + // OK, it will be swept fairly soon. for { spangen := atomic.Load(&s.sweepgen) if spangen == sl.sweepGen || spangen == sl.sweepGen+3 { @@ -386,8 +471,8 @@ func (s *mspan) ensureSwept() { func (sl *sweepLocked) sweep(preserve bool) bool { // It's critical that we enter this function with preemption disabled, // GC must not start while we are in the middle of this function. - _g_ := getg() - if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 { + gp := getg() + if gp.m.locks == 0 && gp.m.mallocing == 0 && gp != gp.m.g0 { throw("mspan.sweep: m is not locked") } @@ -408,7 +493,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool { traceGCSweepSpan(s.npages * _PageSize) } - atomic.Xadd64(&mheap_.pagesSwept, int64(s.npages)) + mheap_.pagesSwept.Add(int64(s.npages)) spc := s.spanclass size := s.elemsize @@ -481,7 +566,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool { spanHasNoSpecials(s) } - if debug.allocfreetrace != 0 || debug.clobberfree != 0 || raceenabled || msanenabled { + if debug.allocfreetrace != 0 || debug.clobberfree != 0 || raceenabled || msanenabled || asanenabled { // Find all newly freed objects. This doesn't have to // efficient; allocfreetrace has massive overhead. mbits := s.markBitsForBase() @@ -501,6 +586,9 @@ func (sl *sweepLocked) sweep(preserve bool) bool { if msanenabled { msanfree(unsafe.Pointer(x), size) } + if asanenabled { + asanpoison(unsafe.Pointer(x), size) + } } mbits.advance() abits.advance() @@ -581,8 +669,11 @@ func (sl *sweepLocked) sweep(preserve bool) bool { // free slots zeroed. s.needzero = 1 stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.smallFreeCount[spc.sizeclass()], uintptr(nfreed)) + atomic.Xadd64(&stats.smallFreeCount[spc.sizeclass()], int64(nfreed)) memstats.heapStats.release() + + // Count the frees in the inconsistent, internal stats. + gcController.totalFree.Add(int64(nfreed) * int64(s.elemsize)) } if !preserve { // The caller may not have removed this span from whatever @@ -627,10 +718,16 @@ func (sl *sweepLocked) sweep(preserve bool) bool { } else { mheap_.freeSpan(s) } + + // Count the free in the consistent, external stats. stats := memstats.heapStats.acquire() - atomic.Xadduintptr(&stats.largeFreeCount, 1) - atomic.Xadduintptr(&stats.largeFree, size) + atomic.Xadd64(&stats.largeFreeCount, 1) + atomic.Xadd64(&stats.largeFree, int64(size)) memstats.heapStats.release() + + // Count the free in the inconsistent, internal stats. + gcController.totalFree.Add(int64(size)) + return true } @@ -719,17 +816,17 @@ func deductSweepCredit(spanBytes uintptr, callerSweepPages uintptr) { } retry: - sweptBasis := atomic.Load64(&mheap_.pagesSweptBasis) + sweptBasis := mheap_.pagesSweptBasis.Load() // Fix debt if necessary. - newHeapLive := uintptr(atomic.Load64(&gcController.heapLive)-mheap_.sweepHeapLiveBasis) + spanBytes + newHeapLive := uintptr(gcController.heapLive.Load()-mheap_.sweepHeapLiveBasis) + spanBytes pagesTarget := int64(mheap_.sweepPagesPerByte*float64(newHeapLive)) - int64(callerSweepPages) - for pagesTarget > int64(atomic.Load64(&mheap_.pagesSwept)-sweptBasis) { + for pagesTarget > int64(mheap_.pagesSwept.Load()-sweptBasis) { if sweepone() == ^uintptr(0) { mheap_.sweepPagesPerByte = 0 break } - if atomic.Load64(&mheap_.pagesSweptBasis) != sweptBasis { + if mheap_.pagesSweptBasis.Load() != sweptBasis { // Sweep pacing changed. Recompute debt. goto retry } @@ -748,3 +845,46 @@ func clobberfree(x unsafe.Pointer, size uintptr) { *(*uint32)(add(x, i)) = 0xdeadbeef } } + +// gcPaceSweeper updates the sweeper's pacing parameters. +// +// Must be called whenever the GC's pacing is updated. +// +// The world must be stopped, or mheap_.lock must be held. +func gcPaceSweeper(trigger uint64) { + assertWorldStoppedOrLockHeld(&mheap_.lock) + + // Update sweep pacing. + if isSweepDone() { + mheap_.sweepPagesPerByte = 0 + } else { + // Concurrent sweep needs to sweep all of the in-use + // pages by the time the allocated heap reaches the GC + // trigger. Compute the ratio of in-use pages to sweep + // per byte allocated, accounting for the fact that + // some might already be swept. + heapLiveBasis := gcController.heapLive.Load() + heapDistance := int64(trigger) - int64(heapLiveBasis) + // Add a little margin so rounding errors and + // concurrent sweep are less likely to leave pages + // unswept when GC starts. + heapDistance -= 1024 * 1024 + if heapDistance < _PageSize { + // Avoid setting the sweep ratio extremely high + heapDistance = _PageSize + } + pagesSwept := mheap_.pagesSwept.Load() + pagesInUse := mheap_.pagesInUse.Load() + sweepDistancePages := int64(pagesInUse) - int64(pagesSwept) + if sweepDistancePages <= 0 { + mheap_.sweepPagesPerByte = 0 + } else { + mheap_.sweepPagesPerByte = float64(sweepDistancePages) / float64(heapDistance) + mheap_.sweepHeapLiveBasis = heapLiveBasis + // Write pagesSweptBasis last, since this + // signals concurrent sweeps to recompute + // their debt. + mheap_.pagesSweptBasis.Store(pagesSwept) + } + } +} diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go index 667c7afa97197d..65ac0a6fc7af53 100644 --- a/src/runtime/mgcwork.go +++ b/src/runtime/mgcwork.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -44,9 +45,9 @@ func init() { // // A gcWork can be used on the stack as follows: // -// (preemption must be disabled) -// gcw := &getg().m.p.ptr().gcw -// .. call gcw.put() to produce and gcw.tryGet() to consume .. +// (preemption must be disabled) +// gcw := &getg().m.p.ptr().gcw +// .. call gcw.put() to produce and gcw.tryGet() to consume .. // // It's important that any use of gcWork during the mark phase prevent // the garbage collector from transitioning to mark termination since @@ -77,9 +78,10 @@ type gcWork struct { // into work.bytesMarked by dispose. bytesMarked uint64 - // Scan work performed on this gcWork. This is aggregated into + // Heap scan work performed on this gcWork. This is aggregated into // gcController by dispose and may also be flushed by callers. - scanWork int64 + // Other types of scan work are flushed immediately. + heapScanWork int64 // flushedWork indicates that a non-empty work buffer was // flushed to the global work list since the last gcMarkDone @@ -106,6 +108,7 @@ func (w *gcWork) init() { // put enqueues a pointer for the garbage collector to trace. // obj must point to the beginning of a heap object or an oblet. +// //go:nowritebarrierrec func (w *gcWork) put(obj uintptr) { flushed := false @@ -144,12 +147,11 @@ func (w *gcWork) put(obj uintptr) { // putFast does a put and reports whether it can be done quickly // otherwise it returns false and the caller needs to call put. +// //go:nowritebarrierrec func (w *gcWork) putFast(obj uintptr) bool { wbuf := w.wbuf1 - if wbuf == nil { - return false - } else if wbuf.nobj == len(wbuf.obj) { + if wbuf == nil || wbuf.nobj == len(wbuf.obj) { return false } @@ -197,6 +199,7 @@ func (w *gcWork) putBatch(obj []uintptr) { // If there are no pointers remaining in this gcWork or in the global // queue, tryGet returns 0. Note that there may still be pointers in // other gcWork instances or other caches. +// //go:nowritebarrierrec func (w *gcWork) tryGet() uintptr { wbuf := w.wbuf1 @@ -226,13 +229,11 @@ func (w *gcWork) tryGet() uintptr { // tryGetFast dequeues a pointer for the garbage collector to trace // if one is readily available. Otherwise it returns 0 and // the caller is expected to call tryGet(). +// //go:nowritebarrierrec func (w *gcWork) tryGetFast() uintptr { wbuf := w.wbuf1 - if wbuf == nil { - return 0 - } - if wbuf.nobj == 0 { + if wbuf == nil || wbuf.nobj == 0 { return 0 } @@ -274,14 +275,15 @@ func (w *gcWork) dispose() { atomic.Xadd64(&work.bytesMarked, int64(w.bytesMarked)) w.bytesMarked = 0 } - if w.scanWork != 0 { - atomic.Xaddint64(&gcController.scanWork, w.scanWork) - w.scanWork = 0 + if w.heapScanWork != 0 { + gcController.heapScanWork.Add(w.heapScanWork) + w.heapScanWork = 0 } } // balance moves some work that's cached in this gcWork back on the // global queue. +// //go:nowritebarrierrec func (w *gcWork) balance() { if w.wbuf1 == nil { @@ -304,6 +306,7 @@ func (w *gcWork) balance() { } // empty reports whether w has no mark work available. +// //go:nowritebarrierrec func (w *gcWork) empty() bool { return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0) @@ -318,11 +321,11 @@ type workbufhdr struct { nobj int } -//go:notinheap type workbuf struct { + _ sys.NotInHeap workbufhdr // account for the above fields - obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / sys.PtrSize]uintptr + obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / goarch.PtrSize]uintptr } // workbuf factory routines. These funcs are used to manage the @@ -344,6 +347,7 @@ func (b *workbuf) checkempty() { // getempty pops an empty work buffer off the work.empty list, // allocating new buffers if none are available. +// //go:nowritebarrier func getempty() *workbuf { var b *workbuf @@ -399,6 +403,7 @@ func getempty() *workbuf { // putempty puts a workbuf onto the work.empty list. // Upon entry this goroutine owns b. The lfstack.push relinquishes ownership. +// //go:nowritebarrier func putempty(b *workbuf) { b.checkempty() @@ -408,6 +413,7 @@ func putempty(b *workbuf) { // putfull puts the workbuf on the work.full list for the GC. // putfull accepts partially full buffers so the GC can avoid competing // with the mutators for ownership of partially full buffers. +// //go:nowritebarrier func putfull(b *workbuf) { b.checknonempty() @@ -416,6 +422,7 @@ func putfull(b *workbuf) { // trygetfull tries to get a full or partially empty workbuffer. // If one is not immediately available return nil +// //go:nowritebarrier func trygetfull() *workbuf { b := (*workbuf)(work.full.pop()) diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 84c00ce8f85cd1..cd4634448c32e0 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -10,6 +10,7 @@ package runtime import ( "internal/cpu" + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -57,17 +58,16 @@ const ( // // mheap must not be heap-allocated because it contains mSpanLists, // which must not be heap-allocated. -// -//go:notinheap type mheap struct { + _ sys.NotInHeap + // lock must only be acquired on the system stack, otherwise a g // could self-deadlock if its stack grows with the lock held. - lock mutex + lock mutex + pages pageAlloc // page allocation data structure - sweepgen uint32 // sweep generation, see comment in mspan; written during STW - sweepDrained uint32 // all spans are swept or are being swept - sweepers uint32 // number of active sweepone calls + sweepgen uint32 // sweep generation, see comment in mspan; written during STW // allspans is a slice of all mspans ever created. Each mspan // appears exactly once. @@ -82,8 +82,6 @@ type mheap struct { // access (since that may free the backing store). allspans []*mspan // all spans out there - _ uint32 // align uint64 fields on 32-bit for atomics - // Proportional sweep // // These parameters represent a linear function from gcController.heapLive @@ -96,24 +94,17 @@ type mheap struct { // any given time, the system is at (gcController.heapLive, // pagesSwept) in this space. // - // It's important that the line pass through a point we - // control rather than simply starting at a (0,0) origin + // It is important that the line pass through a point we + // control rather than simply starting at a 0,0 origin // because that lets us adjust sweep pacing at any time while // accounting for current progress. If we could only adjust // the slope, it would create a discontinuity in debt if any // progress has already been made. - pagesInUse uint64 // pages of spans in stats mSpanInUse; updated atomically - pagesSwept uint64 // pages swept this cycle; updated atomically - pagesSweptBasis uint64 // pagesSwept to use as the origin of the sweep ratio; updated atomically - sweepHeapLiveBasis uint64 // value of gcController.heapLive to use as the origin of sweep ratio; written with lock, read without - sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without - // TODO(austin): pagesInUse should be a uintptr, but the 386 - // compiler can't 8-byte align fields. - - // scavengeGoal is the amount of total retained heap memory (measured by - // heapRetained) that the runtime will try to maintain by returning memory - // to the OS. - scavengeGoal uint64 + pagesInUse atomic.Uintptr // pages of spans in stats mSpanInUse + pagesSwept atomic.Uint64 // pages swept this cycle + pagesSweptBasis atomic.Uint64 // pagesSwept to use as the origin of the sweep ratio + sweepHeapLiveBasis uint64 // value of gcController.heapLive to use as the origin of sweep ratio; written with lock, read without + sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without // Page reclaimer state @@ -123,16 +114,13 @@ type mheap struct { // // If this is >= 1<<63, the page reclaimer is done scanning // the page marks. - // - // This is accessed atomically. - reclaimIndex uint64 + reclaimIndex atomic.Uint64 + // reclaimCredit is spare credit for extra pages swept. Since // the page reclaimer works in large chunks, it may reclaim // more than requested. Any spare pages released go to this // credit pool. - // - // This is accessed atomically. - reclaimCredit uintptr + reclaimCredit atomic.Uintptr // arenas is the heap arena map. It points to the metadata for // the heap for every arena frame of the entire usable virtual @@ -197,8 +185,6 @@ type mheap struct { base, end uintptr } - _ uint32 // ensure 64-bit alignment of central - // central free lists for small size classes. // the padding makes sure that the mcentrals are // spaced CacheLinePadSize bytes apart, so that each mcentral.lock @@ -206,7 +192,7 @@ type mheap struct { // central is indexed by spanClass. central [numSpanClasses]struct { mcentral mcentral - pad [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte + pad [(cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize) % cpu.CacheLinePadSize]byte } spanalloc fixalloc // allocator for span* @@ -224,13 +210,26 @@ var mheap_ mheap // A heapArena stores metadata for a heap arena. heapArenas are stored // outside of the Go heap and accessed via the mheap_.arenas index. -// -//go:notinheap type heapArena struct { + _ sys.NotInHeap + // bitmap stores the pointer/scalar bitmap for the words in - // this arena. See mbitmap.go for a description. Use the - // heapBits type to access this. - bitmap [heapArenaBitmapBytes]byte + // this arena. See mbitmap.go for a description. + // This array uses 1 bit per word of heap, or 1.6% of the heap size (for 64-bit). + bitmap [heapArenaBitmapWords]uintptr + + // If the ith bit of noMorePtrs is true, then there are no more + // pointers for the object containing the word described by the + // high bit of bitmap[i]. + // In that case, bitmap[i+1], ... must be zero until the start + // of the next object. + // We never operate on these entries using bit-parallel techniques, + // so it is ok if they are small. Also, they can't be bigger than + // uint16 because at that size a single noMorePtrs entry + // represents 8K of memory, the minimum size of a span. Any larger + // and we'd have to worry about concurrent updates. + // This array uses 1 bit per word of bitmap, or .024% of the heap size (for 64-bit). + noMorePtrs [heapArenaBitmapWords / 8]uint8 // spans maps from virtual address page ID within this arena to *mspan. // For allocated spans, their pages map to the span itself. @@ -297,9 +296,8 @@ type heapArena struct { // arenaHint is a hint for where to grow the heap arenas. See // mheap_.arenaHints. -// -//go:notinheap type arenaHint struct { + _ sys.NotInHeap addr uintptr down bool next *arenaHint @@ -322,16 +320,16 @@ type arenaHint struct { // mSpanManual, or mSpanFree. Transitions between these states are // constrained as follows: // -// * A span may transition from free to in-use or manual during any GC -// phase. +// - A span may transition from free to in-use or manual during any GC +// phase. // -// * During sweeping (gcphase == _GCoff), a span may transition from -// in-use to free (as a result of sweeping) or manual to free (as a -// result of stacks being freed). +// - During sweeping (gcphase == _GCoff), a span may transition from +// in-use to free (as a result of sweeping) or manual to free (as a +// result of stacks being freed). // -// * During GC (gcphase != _GCoff), a span *must not* transition from -// manual or in-use to free. Because concurrent GC may read a pointer -// and then look up its span, the span state must be monotonic. +// - During GC (gcphase != _GCoff), a span *must not* transition from +// manual or in-use to free. Because concurrent GC may read a pointer +// and then look up its span, the span state must be monotonic. // // Setting mspan.state to mSpanInUse or mSpanManual must be done // atomically and only after all other span fields are valid. @@ -357,31 +355,30 @@ var mSpanStateNames = []string{ "mSpanFree", } -// mSpanStateBox holds an mSpanState and provides atomic operations on -// it. This is a separate type to disallow accidental comparison or -// assignment with mSpanState. +// mSpanStateBox holds an atomic.Uint8 to provide atomic operations on +// an mSpanState. This is a separate type to disallow accidental comparison +// or assignment with mSpanState. type mSpanStateBox struct { - s mSpanState + s atomic.Uint8 } func (b *mSpanStateBox) set(s mSpanState) { - atomic.Store8((*uint8)(&b.s), uint8(s)) + b.s.Store(uint8(s)) } func (b *mSpanStateBox) get() mSpanState { - return mSpanState(atomic.Load8((*uint8)(&b.s))) + return mSpanState(b.s.Load()) } // mSpanList heads a linked list of spans. -// -//go:notinheap type mSpanList struct { + _ sys.NotInHeap first *mspan // first span in list, or nil if none last *mspan // last span in list, or nil if none } -//go:notinheap type mspan struct { + _ sys.NotInHeap next *mspan // next span in list, or nil if none prev *mspan // previous span in list, or nil if none list *mSpanList // For debugging. TODO: Remove. @@ -452,16 +449,17 @@ type mspan struct { // if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached // h->sweepgen is incremented by 2 after every GC - sweepgen uint32 - divMul uint32 // for divide by elemsize - allocCount uint16 // number of allocated objects - spanclass spanClass // size class and noscan (uint8) - state mSpanStateBox // mSpanInUse etc; accessed atomically (get/set methods) - needzero uint8 // needs to be zeroed before allocation - elemsize uintptr // computed from sizeclass or from npages - limit uintptr // end of data in span - speciallock mutex // guards specials list - specials *special // linked list of special records sorted by offset. + sweepgen uint32 + divMul uint32 // for divide by elemsize + allocCount uint16 // number of allocated objects + spanclass spanClass // size class and noscan (uint8) + state mSpanStateBox // mSpanInUse etc; accessed atomically (get/set methods) + needzero uint8 // needs to be zeroed before allocation + allocCountBeforeCache uint16 // a copy of allocCount that is stored just before this span is cached + elemsize uintptr // computed from sizeclass or from npages + limit uintptr // end of data in span + speciallock mutex // guards specials list + specials *special // linked list of special records sorted by offset. } func (s *mspan) base() uintptr { @@ -497,13 +495,13 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) { assertLockHeld(&h.lock) if len(h.allspans) >= cap(h.allspans) { - n := 64 * 1024 / sys.PtrSize + n := 64 * 1024 / goarch.PtrSize if n < cap(h.allspans)*3/2 { n = cap(h.allspans) * 3 / 2 } var new []*mspan sp := (*slice)(unsafe.Pointer(&new)) - sp.array = sysAlloc(uintptr(n)*sys.PtrSize, &memstats.other_sys) + sp.array = sysAlloc(uintptr(n)*goarch.PtrSize, &memstats.other_sys) if sp.array == nil { throw("runtime: cannot allocate memory") } @@ -592,6 +590,7 @@ func (i arenaIdx) l2() uint { // inheap reports whether b is a pointer into a (potentially dead) heap object. // It returns false for pointers into mSpanManual spans. // Non-preemptible because it is used by write barriers. +// //go:nowritebarrier //go:nosplit func inheap(b uintptr) bool { @@ -739,7 +738,7 @@ func (h *mheap) reclaim(npage uintptr) { // batching heap frees. // Bail early if there's no more reclaim work. - if atomic.Load64(&h.reclaimIndex) >= 1<<63 { + if h.reclaimIndex.Load() >= 1<<63 { return } @@ -756,23 +755,23 @@ func (h *mheap) reclaim(npage uintptr) { locked := false for npage > 0 { // Pull from accumulated credit first. - if credit := atomic.Loaduintptr(&h.reclaimCredit); credit > 0 { + if credit := h.reclaimCredit.Load(); credit > 0 { take := credit if take > npage { // Take only what we need. take = npage } - if atomic.Casuintptr(&h.reclaimCredit, credit, credit-take) { + if h.reclaimCredit.CompareAndSwap(credit, credit-take) { npage -= take } continue } // Claim a chunk of work. - idx := uintptr(atomic.Xadd64(&h.reclaimIndex, pagesPerReclaimerChunk) - pagesPerReclaimerChunk) + idx := uintptr(h.reclaimIndex.Add(pagesPerReclaimerChunk) - pagesPerReclaimerChunk) if idx/pagesPerArena >= uintptr(len(arenas)) { // Page reclaiming is done. - atomic.Store64(&h.reclaimIndex, 1<<63) + h.reclaimIndex.Store(1 << 63) break } @@ -788,7 +787,7 @@ func (h *mheap) reclaim(npage uintptr) { npage -= nfound } else { // Put spare pages toward global credit. - atomic.Xadduintptr(&h.reclaimCredit, nfound-npage) + h.reclaimCredit.Add(nfound - npage) npage = 0 } } @@ -818,7 +817,10 @@ func (h *mheap) reclaimChunk(arenas []arenaIdx, pageIdx, n uintptr) uintptr { n0 := n var nFreed uintptr - sl := newSweepLocker() + sl := sweep.active.begin() + if !sl.valid { + return 0 + } for n > 0 { ai := arenas[pageIdx/pagesPerArena] ha := h.arenas[ai.l1()][ai.l2()] @@ -864,7 +866,7 @@ func (h *mheap) reclaimChunk(arenas []arenaIdx, pageIdx, n uintptr) uintptr { pageIdx += uintptr(len(inUse) * 8) n -= uintptr(len(inUse) * 8) } - sl.dispose() + sweep.active.end(sl) if trace.enabled { unlock(&h.lock) // Account for pages scanned but not reclaimed. @@ -896,10 +898,9 @@ func (s spanAllocType) manual() bool { // // spanclass indicates the span's size class and scannability. // -// If needzero is true, the memory for the returned span will be zeroed. -// The boolean returned indicates whether the returned span contains zeroes, -// either because this was requested, or because it was already zeroed. -func (h *mheap) alloc(npages uintptr, spanclass spanClass, needzero bool) (*mspan, bool) { +// Returns a span that has been fully initialized. span.needzero indicates +// whether the span has been zeroed. Note that it may not be. +func (h *mheap) alloc(npages uintptr, spanclass spanClass) *mspan { // Don't do any operations that lock the heap on the G stack. // It might trigger stack growth, and the stack growth code needs // to be able to allocate heap. @@ -912,17 +913,7 @@ func (h *mheap) alloc(npages uintptr, spanclass spanClass, needzero bool) (*mspa } s = h.allocSpan(npages, spanAllocHeap, spanclass) }) - - if s == nil { - return nil, false - } - isZeroed := s.needzero == 0 - if needzero && !isZeroed { - memclrNoHeapPointers(unsafe.Pointer(s.base()), s.npages<<_PageShift) - isZeroed = true - } - s.needzero = 0 - return s, isZeroed + return s } // allocManual allocates a manually-managed span of npage pages. @@ -930,7 +921,7 @@ func (h *mheap) alloc(npages uintptr, spanclass spanClass, needzero bool) (*mspa // // allocManual adds the bytes used to *stat, which should be a // memstats in-use field. Unlike allocations in the GC'd heap, the -// allocation does *not* count toward heap_inuse or heap_sys. +// allocation does *not* count toward heapInUse. // // The memory backing the returned span may not be zeroed if // span.needzero is set. @@ -1011,7 +1002,7 @@ func (h *mheap) allocNeedsZero(base, npage uintptr) (needZero bool) { break } zeroedBase = atomic.Loaduintptr(&ha.zeroedBase) - // Sanity check zeroedBase. + // Double check basic conditions of zeroedBase. if zeroedBase <= arenaLimit && zeroedBase > arenaBase { // The zeroedBase moved into the space we were trying to // claim. That's very bad, and indicates someone allocated @@ -1131,6 +1122,7 @@ func (h *mheap) allocSpan(npages uintptr, typ spanAllocType, spanclass spanClass // Function-global state. gp := getg() base, scav := uintptr(0), uintptr(0) + growth := uintptr(0) // On some platforms we need to provide physical page aligned stack // allocations. Where the page size is less than the physical page @@ -1169,14 +1161,39 @@ func (h *mheap) allocSpan(npages uintptr, typ spanAllocType, spanclass spanClass if needPhysPageAlign { // Overallocate by a physical page to allow for later alignment. - npages += physPageSize / pageSize - } + extraPages := physPageSize / pageSize + // Find a big enough region first, but then only allocate the + // aligned portion. We can't just allocate and then free the + // edges because we need to account for scavenged memory, and + // that's difficult with alloc. + // + // Note that we skip updates to searchAddr here. It's OK if + // it's stale and higher than normal; it'll operate correctly, + // just come with a performance cost. + base, _ = h.pages.find(npages + extraPages) + if base == 0 { + var ok bool + growth, ok = h.grow(npages + extraPages) + if !ok { + unlock(&h.lock) + return nil + } + base, _ = h.pages.find(npages + extraPages) + if base == 0 { + throw("grew heap, but no adequate free space found") + } + } + base = alignUp(base, physPageSize) + scav = h.pages.allocRange(base, npages) + } if base == 0 { // Try to acquire a base address. base, scav = h.pages.alloc(npages) if base == 0 { - if !h.grow(npages) { + var ok bool + growth, ok = h.grow(npages) + if !ok { unlock(&h.lock) return nil } @@ -1191,23 +1208,6 @@ func (h *mheap) allocSpan(npages uintptr, typ spanAllocType, spanclass spanClass // one now that we have the heap lock. s = h.allocMSpanLocked() } - - if needPhysPageAlign { - allocBase, allocPages := base, npages - base = alignUp(allocBase, physPageSize) - npages -= physPageSize / pageSize - - // Return memory around the aligned allocation. - spaceBefore := base - allocBase - if spaceBefore > 0 { - h.pages.free(allocBase, spaceBefore/pageSize) - } - spaceAfter := (allocPages-npages)*pageSize - spaceBefore - if spaceAfter > 0 { - h.pages.free(base+npages*pageSize, spaceAfter/pageSize) - } - } - unlock(&h.lock) HaveSpan: @@ -1261,20 +1261,86 @@ HaveSpan: s.state.set(mSpanInUse) } + // Decide if we need to scavenge in response to what we just allocated. + // Specifically, we track the maximum amount of memory to scavenge of all + // the alternatives below, assuming that the maximum satisfies *all* + // conditions we check (e.g. if we need to scavenge X to satisfy the + // memory limit and Y to satisfy heap-growth scavenging, and Y > X, then + // it's fine to pick Y, because the memory limit is still satisfied). + // + // It's fine to do this after allocating because we expect any scavenged + // pages not to get touched until we return. Simultaneously, it's important + // to do this before calling sysUsed because that may commit address space. + bytesToScavenge := uintptr(0) + if limit := gcController.memoryLimit.Load(); go119MemoryLimitSupport && !gcCPULimiter.limiting() { + // Assist with scavenging to maintain the memory limit by the amount + // that we expect to page in. + inuse := gcController.mappedReady.Load() + // Be careful about overflow, especially with uintptrs. Even on 32-bit platforms + // someone can set a really big memory limit that isn't maxInt64. + if uint64(scav)+inuse > uint64(limit) { + bytesToScavenge = uintptr(uint64(scav) + inuse - uint64(limit)) + } + } + if goal := scavenge.gcPercentGoal.Load(); goal != ^uint64(0) && growth > 0 { + // We just caused a heap growth, so scavenge down what will soon be used. + // By scavenging inline we deal with the failure to allocate out of + // memory fragments by scavenging the memory fragments that are least + // likely to be re-used. + // + // Only bother with this because we're not using a memory limit. We don't + // care about heap growths as long as we're under the memory limit, and the + // previous check for scaving already handles that. + if retained := heapRetained(); retained+uint64(growth) > goal { + // The scavenging algorithm requires the heap lock to be dropped so it + // can acquire it only sparingly. This is a potentially expensive operation + // so it frees up other goroutines to allocate in the meanwhile. In fact, + // they can make use of the growth we just created. + todo := growth + if overage := uintptr(retained + uint64(growth) - goal); todo > overage { + todo = overage + } + if todo > bytesToScavenge { + bytesToScavenge = todo + } + } + } + // There are a few very limited cirumstances where we won't have a P here. + // It's OK to simply skip scavenging in these cases. Something else will notice + // and pick up the tab. + if pp != nil && bytesToScavenge > 0 { + // Measure how long we spent scavenging and add that measurement to the assist + // time so we can track it for the GC CPU limiter. + // + // Limiter event tracking might be disabled if we end up here + // while on a mark worker. + start := nanotime() + track := pp.limiterEvent.start(limiterEventScavengeAssist, start) + + // Scavenge, but back out if the limiter turns on. + h.pages.scavenge(bytesToScavenge, func() bool { + return gcCPULimiter.limiting() + }) + + // Finish up accounting. + now := nanotime() + if track { + pp.limiterEvent.stop(limiterEventScavengeAssist, now) + } + h.pages.scav.assistTime.Add(now - start) + } + // Commit and account for any scavenged memory that the span now owns. if scav != 0 { // sysUsed all the pages that are actually available // in the span since some of them might be scavenged. - sysUsed(unsafe.Pointer(base), nbytes) - atomic.Xadd64(&memstats.heap_released, -int64(scav)) + sysUsed(unsafe.Pointer(base), nbytes, scav) + gcController.heapReleased.add(-int64(scav)) } // Update stats. + gcController.heapFree.add(-int64(nbytes - scav)) if typ == spanAllocHeap { - atomic.Xadd64(&memstats.heap_inuse, int64(nbytes)) - } - if typ.manual() { - // Manually managed memory doesn't count toward heap_sys. - memstats.heap_sys.add(-int64(nbytes)) + gcController.heapInUse.add(int64(nbytes)) } // Update consistent stats. stats := memstats.heapStats.acquire() @@ -1311,7 +1377,7 @@ HaveSpan: atomic.Or8(&arena.pageInUse[pageIdx], pageMask) // Update related page sweeper stats. - atomic.Xadd64(&h.pagesInUse, int64(npages)) + h.pagesInUse.Add(npages) } // Make sure the newly allocated span will be observed @@ -1322,10 +1388,10 @@ HaveSpan: } // Try to add at least npage pages of memory to the heap, -// returning whether it worked. +// returning how much the heap grew by and whether it worked. // // h.lock must be held. -func (h *mheap) grow(npage uintptr) bool { +func (h *mheap) grow(npage uintptr) (uintptr, bool) { assertLockHeld(&h.lock) // We must grow the heap in whole palloc chunks. @@ -1346,8 +1412,9 @@ func (h *mheap) grow(npage uintptr) bool { // current arena, so we have to request the full ask. av, asize := h.sysAlloc(ask) if av == nil { - print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n") - return false + inUse := gcController.heapFree.load() + gcController.heapReleased.load() + gcController.heapInUse.load() + print("runtime: out of memory: cannot allocate ", ask, "-byte block (", inUse, " in use)\n") + return 0, false } if uintptr(av) == h.curArena.end { @@ -1362,9 +1429,8 @@ func (h *mheap) grow(npage uintptr) bool { // Transition this space from Reserved to Prepared and mark it // as released since we'll be able to start using it after updating // the page allocator and releasing the lock at any time. - sysMap(unsafe.Pointer(h.curArena.base), size, &memstats.heap_sys) + sysMap(unsafe.Pointer(h.curArena.base), size, &gcController.heapReleased) // Update stats. - atomic.Xadd64(&memstats.heap_released, int64(size)) stats := memstats.heapStats.acquire() atomic.Xaddint64(&stats.released, int64(size)) memstats.heapStats.release() @@ -1390,15 +1456,14 @@ func (h *mheap) grow(npage uintptr) bool { h.curArena.base = nBase // Transition the space we're going to use from Reserved to Prepared. - sysMap(unsafe.Pointer(v), nBase-v, &memstats.heap_sys) - - // The memory just allocated counts as both released - // and idle, even though it's not yet backed by spans. // // The allocation is always aligned to the heap arena // size which is always > physPageSize, so its safe to - // just add directly to heap_released. - atomic.Xadd64(&memstats.heap_released, int64(nBase-v)) + // just add directly to heapReleased. + sysMap(unsafe.Pointer(v), nBase-v, &gcController.heapReleased) + + // The memory just allocated counts as both released + // and idle, even though it's not yet backed by spans. stats := memstats.heapStats.acquire() atomic.Xaddint64(&stats.released, int64(nBase-v)) memstats.heapStats.release() @@ -1407,19 +1472,7 @@ func (h *mheap) grow(npage uintptr) bool { // space ready for allocation. h.pages.grow(v, nBase-v) totalGrowth += nBase - v - - // We just caused a heap growth, so scavenge down what will soon be used. - // By scavenging inline we deal with the failure to allocate out of - // memory fragments by scavenging the memory fragments that are least - // likely to be re-used. - if retained := heapRetained(); retained+uint64(totalGrowth) > h.scavengeGoal { - todo := totalGrowth - if overage := uintptr(retained + uint64(totalGrowth) - h.scavengeGoal); todo > overage { - todo = overage - } - h.pages.scavenge(todo, false) - } - return true + return totalGrowth, true } // Free the span back into the heap. @@ -1432,6 +1485,12 @@ func (h *mheap) freeSpan(s *mspan) { bytes := s.npages << _PageShift msanfree(base, bytes) } + if asanenabled { + // Tell asan that this entire span is no longer in use. + base := unsafe.Pointer(s.base()) + bytes := s.npages << _PageShift + asanpoison(base, bytes) + } h.freeSpanLocked(s, spanAllocHeap) unlock(&h.lock) }) @@ -1468,7 +1527,7 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) { print("mheap.freeSpanLocked - span ", s, " ptr ", hex(s.base()), " allocCount ", s.allocCount, " sweepgen ", s.sweepgen, "/", h.sweepgen, "\n") throw("mheap.freeSpanLocked - invalid free") } - atomic.Xadd64(&h.pagesInUse, -int64(s.npages)) + h.pagesInUse.Add(-s.npages) // Clear in-use bit in arena page bitmap. arena, pageIdx, pageMask := pageIndexOf(s.base()) @@ -1481,12 +1540,9 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) { // // Mirrors the code in allocSpan. nbytes := s.npages * pageSize + gcController.heapFree.add(int64(nbytes)) if typ == spanAllocHeap { - atomic.Xadd64(&memstats.heap_inuse, -int64(nbytes)) - } - if typ.manual() { - // Manually managed memory doesn't count toward heap_sys, so add it back. - memstats.heap_sys.add(int64(nbytes)) + gcController.heapInUse.add(-int64(nbytes)) } // Update consistent stats. stats := memstats.heapStats.acquire() @@ -1503,7 +1559,7 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) { memstats.heapStats.release() // Mark the space as free. - h.pages.free(s.base(), s.npages) + h.pages.free(s.base(), s.npages, false) // Free the span structure. We no longer have a use for it. s.state.set(mSpanDead) @@ -1519,17 +1575,13 @@ func (h *mheap) scavengeAll() { // the mheap API. gp := getg() gp.m.mallocing++ - lock(&h.lock) - // Start a new scavenge generation so we have a chance to walk - // over the whole heap. - h.pages.scavengeStartGen() - released := h.pages.scavenge(^uintptr(0), false) - gen := h.pages.scav.gen - unlock(&h.lock) + + released := h.pages.scavenge(^uintptr(0), nil) + gp.m.mallocing-- if debug.scavtrace > 0 { - printScavTrace(gen, released, true) + printScavTrace(released, true) } } @@ -1667,8 +1719,8 @@ const ( // if that happens. ) -//go:notinheap type special struct { + _ sys.NotInHeap next *special // linked list in span offset uint16 // span offset of object kind byte // kind of special @@ -1695,7 +1747,7 @@ func spanHasNoSpecials(s *mspan) { // offset & next, which this routine will fill in. // Returns true if the special was successfully added, false otherwise. // (The add will fail only if a record with the same p and s->kind -// already exists.) +// already exists.) func addspecial(p unsafe.Pointer, s *special) bool { span := spanOfHeap(uintptr(p)) if span == nil { @@ -1788,9 +1840,8 @@ func removespecial(p unsafe.Pointer, kind uint8) *special { // // specialfinalizer is allocated from non-GC'd memory, so any heap // pointers must be specially handled. -// -//go:notinheap type specialfinalizer struct { + _ sys.NotInHeap special special fn *funcval // May be a heap pointer. nret uintptr @@ -1814,15 +1865,17 @@ func addfinalizer(p unsafe.Pointer, f *funcval, nret uintptr, fint *_type, ot *p // situation where it's possible that markrootSpans // has already run but mark termination hasn't yet. if gcphase != _GCoff { - base, _, _ := findObject(uintptr(p), 0, 0) + base, span, _ := findObject(uintptr(p), 0, 0) mp := acquirem() gcw := &mp.p.ptr().gcw // Mark everything reachable from the object // so it's retained for the finalizer. - scanobject(base, gcw) + if !span.spanclass.noscan() { + scanobject(base, gcw) + } // Mark the finalizer itself, since the // special isn't part of the GC'd heap. - scanblock(uintptr(unsafe.Pointer(&s.fn)), sys.PtrSize, &oneptrmask[0], gcw, nil) + scanblock(uintptr(unsafe.Pointer(&s.fn)), goarch.PtrSize, &oneptrmask[0], gcw, nil) releasem(mp) } return true @@ -1847,9 +1900,8 @@ func removefinalizer(p unsafe.Pointer) { } // The described object is being heap profiled. -// -//go:notinheap type specialprofile struct { + _ sys.NotInHeap special special b *bucket } @@ -1928,14 +1980,15 @@ func freeSpecial(s *special, p unsafe.Pointer, size uintptr) { } } -// gcBits is an alloc/mark bitmap. This is always used as *gcBits. -// -//go:notinheap -type gcBits uint8 +// gcBits is an alloc/mark bitmap. This is always used as gcBits.x. +type gcBits struct { + _ sys.NotInHeap + x uint8 +} // bytep returns a pointer to the n'th byte of b. func (b *gcBits) bytep(n uintptr) *uint8 { - return addb((*uint8)(b), n) + return addb(&b.x, n) } // bitp returns a pointer to the byte containing bit n and a mask for @@ -1952,8 +2005,8 @@ type gcBitsHeader struct { next uintptr // *gcBits triggers recursive type bug. (issue 14620) } -//go:notinheap type gcBitsArena struct { + _ sys.NotInHeap // gcBitsHeader // side step recursive type bug (issue 14620) by including fields by hand. free uintptr // free is the index into bits of the next free byte; read/write atomically next *gcBitsArena diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go index da191cc594c0de..6b42b8524b4117 100644 --- a/src/runtime/mkduff.go +++ b/src/runtime/mkduff.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // runtime·duffzero is a Duff's device for zeroing memory. // The compiler jumps to computed addresses within @@ -37,6 +36,7 @@ func main() { gen("386", notags, zero386, copy386) gen("arm", notags, zeroARM, copyARM) gen("arm64", notags, zeroARM64, copyARM64) + gen("loong64", notags, zeroLOONG64, copyLOONG64) gen("ppc64x", tagsPPC64x, zeroPPC64x, copyPPC64x) gen("mips64x", tagsMIPS64x, zeroMIPS64x, copyMIPS64x) gen("riscv64", notags, zeroRISCV64, copyRISCV64) @@ -154,7 +154,7 @@ func zeroARM64(w io.Writer) { // ZR: always zero // R20: ptr to memory to be zeroed // On return, R20 points to the last zeroed dword. - fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") + fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 63; i++ { fmt.Fprintln(w, "\tSTP.P\t(ZR, ZR), 16(R20)") } @@ -167,7 +167,7 @@ func copyARM64(w io.Writer) { // R21: ptr to destination memory // R26, R27 (aka REGTMP): scratch space // R20 and R21 are updated as a side effect - fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") + fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 64; i++ { fmt.Fprintln(w, "\tLDP.P\t16(R20), (R26, R27)") @@ -177,10 +177,33 @@ func copyARM64(w io.Writer) { fmt.Fprintln(w, "\tRET") } +func zeroLOONG64(w io.Writer) { + // R0: always zero + // R19 (aka REGRT1): ptr to memory to be zeroed - 8 + // On return, R19 points to the last zeroed dword. + fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") + for i := 0; i < 128; i++ { + fmt.Fprintln(w, "\tMOVV\tR0, 8(R19)") + fmt.Fprintln(w, "\tADDV\t$8, R19") + } + fmt.Fprintln(w, "\tRET") +} + +func copyLOONG64(w io.Writer) { + fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") + for i := 0; i < 128; i++ { + fmt.Fprintln(w, "\tMOVV\t(R19), R30") + fmt.Fprintln(w, "\tADDV\t$8, R19") + fmt.Fprintln(w, "\tMOVV\tR30, (R20)") + fmt.Fprintln(w, "\tADDV\t$8, R20") + fmt.Fprintln(w) + } + fmt.Fprintln(w, "\tRET") +} + func tagsPPC64x(w io.Writer) { fmt.Fprintln(w) fmt.Fprintln(w, "//go:build ppc64 || ppc64le") - fmt.Fprintln(w, "// +build ppc64 ppc64le") fmt.Fprintln(w) } @@ -188,23 +211,26 @@ func zeroPPC64x(w io.Writer) { // R0: always zero // R3 (aka REGRT1): ptr to memory to be zeroed - 8 // On return, R3 points to the last zeroed dword. - fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") + fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 128; i++ { - fmt.Fprintln(w, "\tMOVDU\tR0, 8(R3)") + fmt.Fprintln(w, "\tMOVDU\tR0, 8(R20)") } fmt.Fprintln(w, "\tRET") } func copyPPC64x(w io.Writer) { // duffcopy is not used on PPC64. - fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") - fmt.Fprintln(w, "\tUNDEF") + fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") + for i := 0; i < 128; i++ { + fmt.Fprintln(w, "\tMOVDU\t8(R20), R5") + fmt.Fprintln(w, "\tMOVDU\tR5, 8(R21)") + } + fmt.Fprintln(w, "\tRET") } func tagsMIPS64x(w io.Writer) { fmt.Fprintln(w) fmt.Fprintln(w, "//go:build mips64 || mips64le") - fmt.Fprintln(w, "// +build mips64 mips64le") fmt.Fprintln(w) } @@ -234,26 +260,26 @@ func copyMIPS64x(w io.Writer) { func zeroRISCV64(w io.Writer) { // ZERO: always zero - // X10: ptr to memory to be zeroed - // X10 is updated as a side effect. - fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") + // X25: ptr to memory to be zeroed + // X25 is updated as a side effect. + fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 128; i++ { - fmt.Fprintln(w, "\tMOV\tZERO, (X10)") - fmt.Fprintln(w, "\tADD\t$8, X10") + fmt.Fprintln(w, "\tMOV\tZERO, (X25)") + fmt.Fprintln(w, "\tADD\t$8, X25") } fmt.Fprintln(w, "\tRET") } func copyRISCV64(w io.Writer) { - // X10: ptr to source memory - // X11: ptr to destination memory - // X10 and X11 are updated as a side effect - fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") + // X24: ptr to source memory + // X25: ptr to destination memory + // X24 and X25 are updated as a side effect + fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 128; i++ { - fmt.Fprintln(w, "\tMOV\t(X10), X31") - fmt.Fprintln(w, "\tADD\t$8, X10") - fmt.Fprintln(w, "\tMOV\tX31, (X11)") - fmt.Fprintln(w, "\tADD\t$8, X11") + fmt.Fprintln(w, "\tMOV\t(X24), X31") + fmt.Fprintln(w, "\tADD\t$8, X24") + fmt.Fprintln(w, "\tMOV\tX31, (X25)") + fmt.Fprintln(w, "\tADD\t$8, X25") fmt.Fprintln(w) } fmt.Fprintln(w, "\tRET") diff --git a/src/runtime/mkfastlog2table.go b/src/runtime/mkfastlog2table.go index 8d78a3923a094b..614d1f7e03fac0 100644 --- a/src/runtime/mkfastlog2table.go +++ b/src/runtime/mkfastlog2table.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // fastlog2Table contains log2 approximations for 5 binary digits. // This is used to implement fastlog2, which is used for heap sampling. @@ -47,7 +46,64 @@ const fastlogNumBits = 5 func computeTable() []float64 { fastlog2Table := make([]float64, 1<" partial order. This + // ways, locks are acquired from the top going down + // and time moves forward over the edges instead of + // backward. + g.Transpose() + generateDot(&b, g) + out = b.Bytes() + } else { + var b bytes.Buffer + generateGo(&b, g) + out, err = format.Source(b.Bytes()) + if err != nil { + log.Fatal(err) + } + } + + if *flagO != "" { + err = os.WriteFile(*flagO, out, 0666) + } else { + _, err = os.Stdout.Write(out) + } + if err != nil { + log.Fatal(err) + } +} + +func generateGo(w io.Writer, g *dag.Graph) { + fmt.Fprintf(w, `// Code generated by mklockrank.go; DO NOT EDIT. + +package runtime + +type lockRank int + +`) + + // Create numeric ranks. + topo := g.Topo() + for i, j := 0, len(topo)-1; i < j; i, j = i+1, j-1 { + topo[i], topo[j] = topo[j], topo[i] + } + fmt.Fprintf(w, ` +// Constants representing the ranks of all non-leaf runtime locks, in rank order. +// Locks with lower rank must be taken before locks with higher rank, +// in addition to satisfying the partial order in lockPartialOrder. +// A few ranks allow self-cycles, which are specified in lockPartialOrder. +const ( + lockRankUnknown lockRank = iota + +`) + for _, rank := range topo { + if isPseudo(rank) { + fmt.Fprintf(w, "\t// %s\n", rank) + } else { + fmt.Fprintf(w, "\t%s\n", cname(rank)) + } + } + fmt.Fprintf(w, `) + +// lockRankLeafRank is the rank of lock that does not have a declared rank, +// and hence is a leaf lock. +const lockRankLeafRank lockRank = 1000 +`) + + // Create string table. + fmt.Fprintf(w, ` +// lockNames gives the names associated with each of the above ranks. +var lockNames = []string{ +`) + for _, rank := range topo { + if !isPseudo(rank) { + fmt.Fprintf(w, "\t%s: %q,\n", cname(rank), rank) + } + } + fmt.Fprintf(w, `} + +func (rank lockRank) String() string { + if rank == 0 { + return "UNKNOWN" + } + if rank == lockRankLeafRank { + return "LEAF" + } + if rank < 0 || int(rank) >= len(lockNames) { + return "BAD RANK" + } + return lockNames[rank] +} +`) + + // Create partial order structure. + fmt.Fprintf(w, ` +// lockPartialOrder is the transitive closure of the lock rank graph. +// An entry for rank X lists all of the ranks that can already be held +// when rank X is acquired. +// +// Lock ranks that allow self-cycles list themselves. +var lockPartialOrder [][]lockRank = [][]lockRank{ +`) + for _, rank := range topo { + if isPseudo(rank) { + continue + } + list := []string{} + for _, before := range g.Edges(rank) { + if !isPseudo(before) { + list = append(list, cname(before)) + } + } + if cyclicRanks[rank] { + list = append(list, cname(rank)) + } + + fmt.Fprintf(w, "\t%s: {%s},\n", cname(rank), strings.Join(list, ", ")) + } + fmt.Fprintf(w, "}\n") +} + +// cname returns the Go const name for the given lock rank label. +func cname(label string) string { + return "lockRank" + strings.ToUpper(label[:1]) + label[1:] +} + +func isPseudo(label string) bool { + return strings.ToUpper(label) == label +} + +// generateDot emits a Graphviz dot representation of g to w. +func generateDot(w io.Writer, g *dag.Graph) { + fmt.Fprintf(w, "digraph g {\n") + + // Define all nodes. + for _, node := range g.Nodes { + fmt.Fprintf(w, "%q;\n", node) + } + + // Create edges. + for _, node := range g.Nodes { + for _, to := range g.Edges(node) { + fmt.Fprintf(w, "%q -> %q;\n", node, to) + } + } + + fmt.Fprintf(w, "}\n") +} diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 6c980540f57760..61d2d0247e8038 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // mkpreempt generates the asyncPreempt functions for each // architecture. @@ -81,6 +80,7 @@ var arches = map[string]func(){ "amd64": genAMD64, "arm": genARM, "arm64": genARM64, + "loong64": genLoong64, "mips64x": func() { genMIPS(true) }, "mipsx": func() { genMIPS(false) }, "ppc64x": genPPC64, @@ -123,16 +123,17 @@ func header(arch string) { fmt.Fprintf(out, "// Code generated by mkpreempt.go; DO NOT EDIT.\n\n") if beLe[arch] { base := arch[:len(arch)-1] - fmt.Fprintf(out, "//go:build %s || %sle\n", base, base) - fmt.Fprintf(out, "// +build %s %sle\n\n", base, base) + fmt.Fprintf(out, "//go:build %s || %sle\n\n", base, base) } fmt.Fprintf(out, "#include \"go_asm.h\"\n") + if arch == "amd64" { + fmt.Fprintf(out, "#include \"asm_amd64.h\"\n") + } fmt.Fprintf(out, "#include \"textflag.h\"\n\n") - fmt.Fprintf(out, "// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally.\n") - fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") + fmt.Fprintf(out, "TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0\n") } -func p(f string, args ...interface{}) { +func p(f string, args ...any) { fmted := fmt.Sprintf(f, args...) fmt.Fprintf(out, "\t%s\n", strings.ReplaceAll(fmted, "\n", "\n\t")) } @@ -150,8 +151,9 @@ type layout struct { type regPos struct { pos int - op string - reg string + saveOp string + restoreOp string + reg string // If this register requires special save and restore, these // give those operations with a %d placeholder for the stack @@ -160,7 +162,12 @@ type regPos struct { } func (l *layout) add(op, reg string, size int) { - l.regs = append(l.regs, regPos{op: op, reg: reg, pos: l.stack}) + l.regs = append(l.regs, regPos{saveOp: op, restoreOp: op, reg: reg, pos: l.stack}) + l.stack += size +} + +func (l *layout) add2(sop, rop, reg string, size int) { + l.regs = append(l.regs, regPos{saveOp: sop, restoreOp: rop, reg: reg, pos: l.stack}) l.stack += size } @@ -174,7 +181,7 @@ func (l *layout) save() { if reg.save != "" { p(reg.save, reg.pos) } else { - p("%s %s, %d(%s)", reg.op, reg.reg, reg.pos, l.sp) + p("%s %s, %d(%s)", reg.saveOp, reg.reg, reg.pos, l.sp) } } } @@ -185,7 +192,7 @@ func (l *layout) restore() { if reg.restore != "" { p(reg.restore, reg.pos) } else { - p("%s %d(%s), %s", reg.op, reg.pos, l.sp, reg.reg) + p("%s %d(%s), %s", reg.restoreOp, reg.pos, l.sp, reg.reg) } } } @@ -201,6 +208,8 @@ func gen386() { l.add("MOVL", reg, 4) } + softfloat := "GO386_softfloat" + // Save SSE state only if supported. lSSE := layout{stack: l.stack, sp: "SP"} for i := 0; i < 8; i++ { @@ -210,13 +219,13 @@ func gen386() { p("ADJSP $%d", lSSE.stack) p("NOP SP") l.save() - p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse") + p("#ifndef %s", softfloat) lSSE.save() - label("nosse:") + p("#endif") p("CALL ·asyncPreempt2(SB)") - p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2") + p("#ifndef %s", softfloat) lSSE.restore() - label("nosse2:") + p("#endif") l.restore() p("ADJSP $%d", -lSSE.stack) @@ -261,8 +270,10 @@ func genAMD64() { // Clear the upper bits to get to a clean state. See issue #37174. // It is safe here as Go code don't use the upper bits of Y registers. p("#ifdef GOOS_darwin") + p("#ifndef hasAVX") p("CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0") p("JE 2(PC)") + p("#endif") p("VZEROUPPER") p("#endif") @@ -325,12 +336,13 @@ func genARM64() { // R27 (REGTMP), R28 (g), R29 (FP), R30 (LR), R31 (SP) are special // and not saved here. var l = layout{sp: "RSP", stack: 8} // add slot to save PC of interrupted instruction - for i := 0; i <= 26; i++ { + for i := 0; i < 26; i += 2 { if i == 18 { + i-- continue // R18 is not used, skip } - reg := fmt.Sprintf("R%d", i) - l.add("MOVD", reg, 8) + reg := fmt.Sprintf("(R%d, R%d)", i, i+1) + l.add2("STP", "LDP", reg, 16) } // Add flag registers. l.addSpecial( @@ -343,9 +355,9 @@ func genARM64() { 8) // TODO: FPCR? I don't think we'll change it, so no need to save. // Add floating point registers F0-F31. - for i := 0; i <= 31; i++ { - reg := fmt.Sprintf("F%d", i) - l.add("FMOVD", reg, 8) + for i := 0; i < 31; i += 2 { + reg := fmt.Sprintf("(F%d, F%d)", i, i+1) + l.add2("FSTPD", "FLDPD", reg, 16) } if l.stack%16 != 0 { l.stack += 8 // SP needs 16-byte alignment @@ -354,10 +366,8 @@ func genARM64() { // allocate frame, save PC of interrupted instruction (in LR) p("MOVD R30, %d(RSP)", -l.stack) p("SUB $%d, RSP", l.stack) - p("#ifdef GOOS_linux") p("MOVD R29, -8(RSP)") // save frame pointer (only used on Linux) p("SUB $8, RSP, R29") // set up new frame pointer - p("#endif") // On iOS, save the LR again after decrementing SP. We run the // signal handler on the G stack (as it doesn't support sigaltstack), // so any writes below SP may be clobbered. @@ -370,11 +380,9 @@ func genARM64() { l.restore() p("MOVD %d(RSP), R30", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it - p("#ifdef GOOS_linux") - p("MOVD -8(RSP), R29") // restore frame pointer - p("#endif") - p("MOVD (RSP), R27") // load PC to REGTMP - p("ADD $%d, RSP", l.stack+16) // pop frame (including the space pushed by sigctxt.pushCall) + p("MOVD -8(RSP), R29") // restore frame pointer + p("MOVD (RSP), R27") // load PC to REGTMP + p("ADD $%d, RSP", l.stack+16) // pop frame (including the space pushed by sigctxt.pushCall) p("JMP (R27)") } @@ -449,6 +457,46 @@ func genMIPS(_64bit bool) { p("JMP (R23)") } +func genLoong64() { + mov := "MOVV" + movf := "MOVD" + add := "ADDV" + sub := "SUBV" + r31 := "RSB" + regsize := 8 + + // Add integer registers r4-r21 r23-r29 r31 + // R0 (zero), R30 (REGTMP), R2 (tp), R3 (SP), R22 (g), R1 (LR) are special, + var l = layout{sp: "R3", stack: regsize} // add slot to save PC of interrupted instruction (in LR) + for i := 4; i <= 29; i++ { + if i == 22 { + continue // R3 is REGSP R22 is g + } + reg := fmt.Sprintf("R%d", i) + l.add(mov, reg, regsize) + } + l.add(mov, r31, regsize) + + // Add floating point registers F0-F31. + for i := 0; i <= 31; i++ { + reg := fmt.Sprintf("F%d", i) + l.add(movf, reg, regsize) + } + + // allocate frame, save PC of interrupted instruction (in LR) + p(mov+" R1, -%d(R3)", l.stack) + p(sub+" $%d, R3", l.stack) + + l.save() + p("CALL ·asyncPreempt2(SB)") + l.restore() + + p(mov+" %d(R3), R1", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it + p(mov + " (R3), R30") // load PC to REGTMP + p(add+" $%d, R3", l.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall) + p("JMP (R30)") +} + func genPPC64() { // Add integer registers R3-R29 // R0 (zero), R1 (SP), R30 (g) are special and not saved here. @@ -505,12 +553,12 @@ func genPPC64() { } func genRISCV64() { - // X0 (zero), X1 (LR), X2 (SP), X4 (TP), X27 (g), X31 (TMP) are special. + // X0 (zero), X1 (LR), X2 (SP), X3 (GP), X4 (TP), X27 (g), X31 (TMP) are special. var l = layout{sp: "X2", stack: 8} - // Add integer registers (X3, X5-X26, X28-30). - for i := 3; i < 31; i++ { - if i == 4 || i == 27 { + // Add integer registers (X5-X26, X28-30). + for i := 5; i < 31; i++ { + if i == 27 { continue } reg := fmt.Sprintf("X%d", i) diff --git a/src/runtime/mksizeclasses.go b/src/runtime/mksizeclasses.go index b1b10e9e0230a9..64ed8443292308 100644 --- a/src/runtime/mksizeclasses.go +++ b/src/runtime/mksizeclasses.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Generate tables for small malloc size classes. // diff --git a/src/runtime/mmap.go b/src/runtime/mmap.go index 7460eb31046fa8..3280a62e8dd9e4 100644 --- a/src/runtime/mmap.go +++ b/src/runtime/mmap.go @@ -3,15 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !js && (!linux || !amd64) && (!linux || !arm64) && !openbsd && !plan9 && !solaris && !windows -// +build !aix -// +build !darwin -// +build !js -// +build !linux !amd64 -// +build !linux !arm64 -// +build !openbsd -// +build !plan9 -// +build !solaris -// +build !windows package runtime diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go index 071f1fc27499cf..853d7fc9ca5562 100644 --- a/src/runtime/mpagealloc.go +++ b/src/runtime/mpagealloc.go @@ -83,11 +83,16 @@ const ( pallocChunksL1Shift = pallocChunksL2Bits ) -// Maximum searchAddr value, which indicates that the heap has no free space. +// maxSearchAddr returns the maximum searchAddr value, which indicates +// that the heap has no free space. // -// We alias maxOffAddr just to make it clear that this is the maximum address +// This function exists just to make it clear that this is the maximum address // for the page allocator's search space. See maxOffAddr for details. -var maxSearchAddr = maxOffAddr +// +// It's a function (rather than a variable) because it needs to be +// usable before package runtime's dynamic initialization is complete. +// See #51913 for details. +func maxSearchAddr() offAddr { return maxOffAddr } // Global chunk index. // @@ -155,7 +160,7 @@ func addrsToSummaryRange(level int, base, limit uintptr) (lo int, hi int) { // upper-bound. Note that the exclusive upper bound may be within a // summary at this level, meaning if we just do the obvious computation // hi will end up being an inclusive upper bound. Unfortunately, just - // adding 1 to that is too broad since we might be on the very edge of + // adding 1 to that is too broad since we might be on the very edge // of a summary's max page count boundary for this level // (1 << levelLogPages[level]). So, make limit an inclusive upper bound // then shift, then add 1, so we get an exclusive upper bound at the end. @@ -226,6 +231,8 @@ type pageAlloc struct { // are currently available. Otherwise one might iterate over unused // ranges. // + // Protected by mheapLock. + // // TODO(mknyszek): Consider changing the definition of the bitmap // such that 1 means free and 0 means in-use so that summaries and // the bitmaps align better on zero-values. @@ -261,30 +268,20 @@ type pageAlloc struct { inUse addrRanges // scav stores the scavenger state. - // - // All fields are protected by mheapLock. scav struct { - // inUse is a slice of ranges of address space which have not - // yet been looked at by the scavenger. - inUse addrRanges - - // gen is the scavenge generation number. - gen uint32 - - // reservationBytes is how large of a reservation should be made - // in bytes of address space for each scavenge iteration. - reservationBytes uintptr + // index is an efficient index of chunks that have pages available to + // scavenge. + index scavengeIndex // released is the amount of memory released this generation. + // + // Updated atomically. released uintptr - // scavLWM is the lowest (offset) address that the scavenger reached this - // scavenge generation. - scavLWM offAddr - - // freeHWM is the highest (offset) address of a page that was freed to - // the page allocator this scavenge generation. - freeHWM offAddr + // scavengeAssistTime is the time spent scavenging in the last GC cycle. + // + // This is reset once a GC cycle ends. + assistTime atomic.Int64 } // mheap_.lock. This level of indirection makes it possible @@ -295,6 +292,12 @@ type pageAlloc struct { // memory is committed by the pageAlloc for allocation metadata. sysStat *sysMemStat + // summaryMappedReady is the number of bytes mapped in the Ready state + // in the summary structure. Used only for testing currently. + // + // Protected by mheapLock. + summaryMappedReady uintptr + // Whether or not this struct is being used in tests. test bool } @@ -317,13 +320,10 @@ func (p *pageAlloc) init(mheapLock *mutex, sysStat *sysMemStat) { p.sysInit() // Start with the searchAddr in a state indicating there's no free memory. - p.searchAddr = maxSearchAddr + p.searchAddr = maxSearchAddr() // Set the mheapLock. p.mheapLock = mheapLock - - // Initialize scavenge tracking state. - p.scav.scavLWM = maxSearchAddr } // tryChunkOf returns the bitmap data for the given chunk. @@ -391,14 +391,13 @@ func (p *pageAlloc) grow(base, size uintptr) { for c := chunkIndex(base); c < chunkIndex(limit); c++ { if p.chunks[c.l1()] == nil { // Create the necessary l2 entry. - // - // Store it atomically to avoid races with readers which - // don't acquire the heap lock. r := sysAlloc(unsafe.Sizeof(*p.chunks[0]), p.sysStat) if r == nil { throw("pageAlloc: out of memory") } - atomic.StorepNoWB(unsafe.Pointer(&p.chunks[c.l1()]), r) + // Store the new chunk block but avoid a write barrier. + // grow is used in call chains that disallow write barriers. + *(*uintptr)(unsafe.Pointer(&p.chunks[c.l1()])) = uintptr(r) } p.chunkOf(c).scavenged.setRange(0, pallocChunkPages) } @@ -674,7 +673,7 @@ nextLevel: // Determine j0, the first index we should start iterating from. // The searchAddr may help us eliminate iterations if we followed the - // searchAddr on the previous level or we're on the root leve, in which + // searchAddr on the previous level or we're on the root level, in which // case the searchAddr should be the same as i after levelShift. j0 := 0 if searchIdx := offAddrToLevelIndex(l, p.searchAddr); searchIdx&^(entriesPerBlock-1) == i { @@ -746,7 +745,7 @@ nextLevel: } if l == 0 { // We're at level zero, so that means we've exhausted our search. - return 0, maxSearchAddr + return 0, maxSearchAddr() } // We're not at level zero, and we exhausted the level we were looking in. @@ -840,7 +839,7 @@ func (p *pageAlloc) alloc(npages uintptr) (addr uintptr, scav uintptr) { // exhausted. Otherwise, the heap still might have free // space in it, just not enough contiguous space to // accommodate npages. - p.searchAddr = maxSearchAddr + p.searchAddr = maxSearchAddr() } return 0, 0 } @@ -864,17 +863,16 @@ Found: // Must run on the system stack because p.mheapLock must be held. // //go:systemstack -func (p *pageAlloc) free(base, npages uintptr) { +func (p *pageAlloc) free(base, npages uintptr, scavenged bool) { assertLockHeld(p.mheapLock) // If we're freeing pages below the p.searchAddr, update searchAddr. if b := (offAddr{base}); b.lessThan(p.searchAddr) { p.searchAddr = b } - // Update the free high watermark for the scavenger. limit := base + npages*pageSize - 1 - if offLimit := (offAddr{limit}); p.scav.freeHWM.lessThan(offLimit) { - p.scav.freeHWM = offLimit + if !scavenged { + p.scav.index.mark(base, limit+1) } if npages == 1 { // Fast path: we're clearing a single bit, and we know exactly diff --git a/src/runtime/mpagealloc_32bit.go b/src/runtime/mpagealloc_32bit.go index fceb4e7a187b7a..859c61d8a5e78e 100644 --- a/src/runtime/mpagealloc_32bit.go +++ b/src/runtime/mpagealloc_32bit.go @@ -2,23 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build 386 || arm || mips || mipsle || wasm || (ios && arm64) -// +build 386 arm mips mipsle wasm ios,arm64 +//go:build 386 || arm || mips || mipsle || wasm // wasm is a treated as a 32-bit architecture for the purposes of the page // allocator, even though it has 64-bit pointers. This is because any wasm // pointer always has its top 32 bits as zero, so the effective heap address // space is only 2^32 bytes in size (see heapAddrBits). -// ios/arm64 is treated as a 32-bit architecture for the purposes of the -// page allocator, even though it has 64-bit pointers and a 33-bit address -// space (see heapAddrBits). The 33 bit address space cannot be rounded up -// to 64 bits because there are too many summary levels to fit in just 33 -// bits. - package runtime -import "unsafe" +import ( + "runtime/internal/atomic" + "unsafe" +) const ( // The number of levels in the radix tree. @@ -60,6 +56,10 @@ var levelLogPages = [summaryLevels]uint{ logPallocChunkPages, } +// scavengeIndexArray is the backing store for p.scav.index.chunks. +// On 32-bit platforms, it's small enough to just be a global. +var scavengeIndexArray [((1 << heapAddrBits) / pallocChunkBytes) / 8]atomic.Uint8 + // See mpagealloc_64bit.go for details. func (p *pageAlloc) sysInit() { // Calculate how much memory all our entries will take up. @@ -78,7 +78,8 @@ func (p *pageAlloc) sysInit() { } // There isn't much. Just map it and mark it as used immediately. sysMap(reservation, totalSize, p.sysStat) - sysUsed(reservation, totalSize) + sysUsed(reservation, totalSize, totalSize) + p.summaryMappedReady += totalSize // Iterate over the reservation and cut it up into slices. // @@ -93,6 +94,9 @@ func (p *pageAlloc) sysInit() { reservation = add(reservation, uintptr(entries)*pallocSumBytes) } + + // Set up the scavenge index. + p.scav.index.chunks = scavengeIndexArray[:] } // See mpagealloc_64bit.go for details. diff --git a/src/runtime/mpagealloc_64bit.go b/src/runtime/mpagealloc_64bit.go index 16577346a7fbe2..371c1fb31c2e78 100644 --- a/src/runtime/mpagealloc_64bit.go +++ b/src/runtime/mpagealloc_64bit.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build amd64 || (!ios && arm64) || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x -// +build amd64 !ios,arm64 mips64 mips64le ppc64 ppc64le riscv64 s390x - -// See mpagealloc_32bit.go for why ios/arm64 is excluded here. +//go:build amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x package runtime -import "unsafe" +import ( + "runtime/internal/atomic" + "unsafe" +) const ( // The number of levels in the radix tree. @@ -44,7 +44,8 @@ var levelBits = [summaryLevels]uint{ // // With levelShift, one can compute the index of the summary at level l related to a // pointer p by doing: -// p >> levelShift[l] +// +// p >> levelShift[l] var levelShift = [summaryLevels]uint{ heapAddrBits - summaryL0Bits, heapAddrBits - summaryL0Bits - 1*summaryLevelBits, @@ -85,6 +86,12 @@ func (p *pageAlloc) sysInit() { sl := notInHeapSlice{(*notInHeap)(r), 0, entries} p.summary[l] = *(*[]pallocSum)(unsafe.Pointer(&sl)) } + + // Set up the scavenge index. + nbytes := uintptr(1< haveMax { + needMin = haveMax + } + have := makeAddrRange( + // Avoid a panic from indexing one past the last element. + uintptr(unsafe.Pointer(&s.chunks[0]))+uintptr(haveMin), + uintptr(unsafe.Pointer(&s.chunks[0]))+uintptr(haveMax), + ) + need := makeAddrRange( + // Avoid a panic from indexing one past the last element. + uintptr(unsafe.Pointer(&s.chunks[0]))+uintptr(needMin), + uintptr(unsafe.Pointer(&s.chunks[0]))+uintptr(needMax), + ) + // Subtract any overlap from rounding. We can't re-map memory because + // it'll be zeroed. + need = need.subtract(have) + + // If we've got something to map, map it, and update the slice bounds. + if need.size() != 0 { + sysMap(unsafe.Pointer(need.base.addr()), need.size(), sysStat) + sysUsed(unsafe.Pointer(need.base.addr()), need.size(), need.size()) + // Update the indices only after the new memory is valid. + if haveMin == 0 || needMin < haveMin { + s.min.Store(needMin) + } + if haveMax == 0 || needMax > haveMax { + s.max.Store(needMax) + } + } + // Update minHeapIdx. Note that even if there's no mapping work to do, + // we may still have a new, lower minimum heap address. + minHeapIdx := s.minHeapIdx.Load() + if baseIdx := int32(chunkIndex(base) / 8); minHeapIdx == 0 || baseIdx < minHeapIdx { + s.minHeapIdx.Store(baseIdx) } + return need.size() } diff --git a/src/runtime/mpagealloc_test.go b/src/runtime/mpagealloc_test.go index 5d979fa95b9ef5..f2b82e3f506a58 100644 --- a/src/runtime/mpagealloc_test.go +++ b/src/runtime/mpagealloc_test.go @@ -6,6 +6,7 @@ package runtime_test import ( "fmt" + "internal/goos" . "runtime" "testing" ) @@ -165,7 +166,9 @@ func TestPageAllocGrow(t *testing.T) { }, }, } - if PageAlloc64Bit != 0 { + // Disable these tests on iOS since we have a small address space. + // See #46860. + if PageAlloc64Bit != 0 && goos.IsIos == 0 { tests["ExtremelyDiscontiguous"] = test{ chunks: []ChunkIdx{ BaseChunkIdx, @@ -571,7 +574,9 @@ func TestPageAllocAlloc(t *testing.T) { }, }, } - if PageAlloc64Bit != 0 { + // Disable these tests on iOS since we have a small address space. + // See #46860. + if PageAlloc64Bit != 0 && goos.IsIos == 0 { const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB) // This test attempts to trigger a bug wherein we look at unmapped summary diff --git a/src/runtime/mpagecache.go b/src/runtime/mpagecache.go index 4b5c66d8d6e14f..5bad4f789a1198 100644 --- a/src/runtime/mpagecache.go +++ b/src/runtime/mpagecache.go @@ -123,9 +123,10 @@ func (p *pageAlloc) allocToCache() pageCache { } c := pageCache{} ci := chunkIndex(p.searchAddr.addr()) // chunk index + var chunk *pallocData if p.summary[len(p.summary)-1][ci] != 0 { // Fast path: there's free pages at or near the searchAddr address. - chunk := p.chunkOf(ci) + chunk = p.chunkOf(ci) j, _ := chunk.find(1, chunkPageIndex(p.searchAddr.addr())) if j == ^uint(0) { throw("bad summary data") @@ -142,11 +143,11 @@ func (p *pageAlloc) allocToCache() pageCache { if addr == 0 { // We failed to find adequate free space, so mark the searchAddr as OoM // and return an empty pageCache. - p.searchAddr = maxSearchAddr + p.searchAddr = maxSearchAddr() return pageCache{} } ci := chunkIndex(addr) - chunk := p.chunkOf(ci) + chunk = p.chunkOf(ci) c = pageCache{ base: alignDown(addr, 64*pageSize), cache: ^chunk.pages64(chunkPageIndex(addr)), @@ -154,8 +155,11 @@ func (p *pageAlloc) allocToCache() pageCache { } } - // Set the bits as allocated and clear the scavenged bits. - p.allocRange(c.base, pageCachePages) + // Set the page bits as allocated and clear the scavenged bits, but + // be careful to only set and clear the relevant bits. + cpi := chunkPageIndex(c.base) + chunk.allocPages64(cpi, c.cache) + chunk.scavenged.clearBlock64(cpi, c.cache&c.scav /* free and scavenged */) // Update as an allocation, but note that it's not contiguous. p.update(c.base, pageCachePages, false, true) diff --git a/src/runtime/mpagecache_test.go b/src/runtime/mpagecache_test.go index 2ed0c0aa6a0ba8..6cb0620f7b883c 100644 --- a/src/runtime/mpagecache_test.go +++ b/src/runtime/mpagecache_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "internal/goos" "math/rand" . "runtime" "testing" @@ -261,17 +262,18 @@ func TestPageAllocAllocToCache(t *testing.T) { t.Skip("skipping because virtual memory is limited; see #36210") } type test struct { - before map[ChunkIdx][]BitRange - scav map[ChunkIdx][]BitRange - hits []PageCache // expected base addresses and patterns - after map[ChunkIdx][]BitRange + beforeAlloc map[ChunkIdx][]BitRange + beforeScav map[ChunkIdx][]BitRange + hits []PageCache // expected base addresses and patterns + afterAlloc map[ChunkIdx][]BitRange + afterScav map[ChunkIdx][]BitRange } tests := map[string]test{ "AllFree": { - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {}, }, - scav: map[ChunkIdx][]BitRange{ + beforeScav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{1, 1}, {64, 64}}, }, hits: []PageCache{ @@ -280,17 +282,17 @@ func TestPageAllocAllocToCache(t *testing.T) { NewPageCache(PageBase(BaseChunkIdx, 128), ^uint64(0), 0), NewPageCache(PageBase(BaseChunkIdx, 192), ^uint64(0), 0), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 256}}, }, }, "ManyArena": { - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {{0, PallocChunkPages - 64}}, }, - scav: map[ChunkIdx][]BitRange{ + beforeScav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {}, @@ -298,46 +300,50 @@ func TestPageAllocAllocToCache(t *testing.T) { hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx+2, PallocChunkPages-64), ^uint64(0), 0), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {{0, PallocChunkPages}}, }, }, "NotContiguous": { - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{0, 0}}, }, - scav: map[ChunkIdx][]BitRange{ + beforeScav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{31, 67}}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx+0xff, 0), ^uint64(0), ((uint64(1)<<33)-1)<<31), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{0, 64}}, }, + afterScav: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, PallocChunkPages}}, + BaseChunkIdx + 0xff: {{64, 34}}, + }, }, "First": { - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 32}, {33, 31}, {96, 32}}, }, - scav: map[ChunkIdx][]BitRange{ + beforeScav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{1, 4}, {31, 5}, {66, 2}}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx, 0), 1<<32, 1<<32), NewPageCache(PageBase(BaseChunkIdx, 64), (uint64(1)<<32)-1, 0x3<<2), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 128}}, }, }, "Fail": { - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, }, hits: []PageCache{ @@ -345,12 +351,31 @@ func TestPageAllocAllocToCache(t *testing.T) { NewPageCache(0, 0, 0), NewPageCache(0, 0, 0), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, }, }, + "RetainScavBits": { + beforeAlloc: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, 1}, {10, 2}}, + }, + beforeScav: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, 4}, {11, 1}}, + }, + hits: []PageCache{ + NewPageCache(PageBase(BaseChunkIdx, 0), ^uint64(0x1|(0x3<<10)), 0x7<<1), + }, + afterAlloc: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, 64}}, + }, + afterScav: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, 1}, {11, 1}}, + }, + }, } - if PageAlloc64Bit != 0 { + // Disable these tests on iOS since we have a small address space. + // See #46860. + if PageAlloc64Bit != 0 && goos.IsIos == 0 { const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB) // This test is similar to the one with the same name for @@ -359,11 +384,11 @@ func TestPageAllocAllocToCache(t *testing.T) { sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes) baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1) tests["DiscontiguousMappedSumBoundary"] = test{ - before: map[ChunkIdx][]BitRange{ + beforeAlloc: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}}, baseChunkIdx + chunkIdxBigJump: {{1, PallocChunkPages - 1}}, }, - scav: map[ChunkIdx][]BitRange{ + beforeScav: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {}, baseChunkIdx + chunkIdxBigJump: {}, }, @@ -372,7 +397,7 @@ func TestPageAllocAllocToCache(t *testing.T) { NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0), NewPageCache(0, 0, 0), }, - after: map[ChunkIdx][]BitRange{ + afterAlloc: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}}, baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}}, }, @@ -381,7 +406,7 @@ func TestPageAllocAllocToCache(t *testing.T) { for name, v := range tests { v := v t.Run(name, func(t *testing.T) { - b := NewPageAlloc(v.before, v.scav) + b := NewPageAlloc(v.beforeAlloc, v.beforeScav) defer FreePageAlloc(b) for _, expect := range v.hits { @@ -390,7 +415,7 @@ func TestPageAllocAllocToCache(t *testing.T) { return } } - want := NewPageAlloc(v.after, v.scav) + want := NewPageAlloc(v.afterAlloc, v.afterScav) defer FreePageAlloc(want) checkPageAlloc(t, want, b) diff --git a/src/runtime/mpallocbits.go b/src/runtime/mpallocbits.go index ff112300c37365..f63164becd392b 100644 --- a/src/runtime/mpallocbits.go +++ b/src/runtime/mpallocbits.go @@ -57,6 +57,12 @@ func (b *pageBits) setAll() { } } +// setBlock64 sets the 64-bit aligned block of bits containing the i'th bit that +// are set in v. +func (b *pageBits) setBlock64(i uint, v uint64) { + b[i/64] |= v +} + // clear clears bit i of pageBits. func (b *pageBits) clear(i uint) { b[i/64] &^= 1 << (i % 64) @@ -93,6 +99,12 @@ func (b *pageBits) clearAll() { } } +// clearBlock64 clears the 64-bit aligned block of bits containing the i'th bit that +// are set in v. +func (b *pageBits) clearBlock64(i uint, v uint64) { + b[i/64] &^= v +} + // popcntRange counts the number of set bits in the // range [i, i+n). func (b *pageBits) popcntRange(i, n uint) (s uint) { @@ -367,6 +379,12 @@ func (b *pallocBits) pages64(i uint) uint64 { return (*pageBits)(b).block64(i) } +// allocPages64 allocates a 64-bit block of 64 pages aligned to 64 pages according +// to the bits set in alloc. The block set is the one containing the i'th page. +func (b *pallocBits) allocPages64(i uint, alloc uint64) { + (*pageBits)(b).setBlock64(i, alloc) +} + // findBitRange64 returns the bit index of the first set of // n consecutive 1 bits. If no consecutive set of 1 bits of // size n may be found in c, then it returns an integer >= 64. diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index 5235b898e4f56c..8cef0b0601a03e 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -8,12 +8,24 @@ package runtime import ( + "internal/abi" "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) // NOTE(rsc): Everything here could use cas if contention became an issue. -var proflock mutex +var ( + // profInsertLock protects changes to the start of all *bucket linked lists + profInsertLock mutex + // profBlockLock protects the contents of every blockRecord struct + profBlockLock mutex + // profMemActiveLock protects the active field of every memRecord struct + profMemActiveLock mutex + // profMemFutureLock is a set of locks that protect the respective elements + // of the future array of every memRecord struct + profMemFutureLock [len(memRecord{}.future)]mutex +) // All memory allocations are local and do not escape outside of the profiler. // The profiler is forbidden from referring to garbage-collected memory. @@ -42,10 +54,12 @@ type bucketType int // Per-call-stack profiling information. // Lookup by hashing call stack into a linked-list hash table. // -// No heap pointers. +// None of the fields in this bucket header are modified after +// creation, including its next and allnext links. // -//go:notinheap +// No heap pointers. type bucket struct { + _ sys.NotInHeap next *bucket allnext *bucket typ bucketType // memBucket or blockBucket (includes mutexProfile) @@ -138,26 +152,64 @@ type blockRecord struct { } var ( - mbuckets *bucket // memory profile buckets - bbuckets *bucket // blocking profile buckets - xbuckets *bucket // mutex profile buckets - buckhash *[179999]*bucket - bucketmem uintptr - - mProf struct { - // All fields in mProf are protected by proflock. - - // cycle is the global heap profile cycle. This wraps - // at mProfCycleWrap. - cycle uint32 - // flushed indicates that future[cycle] in all buckets - // has been flushed to the active profile. - flushed bool - } + mbuckets atomic.UnsafePointer // *bucket, memory profile buckets + bbuckets atomic.UnsafePointer // *bucket, blocking profile buckets + xbuckets atomic.UnsafePointer // *bucket, mutex profile buckets + buckhash atomic.UnsafePointer // *buckhashArray + + mProfCycle mProfCycleHolder ) +type buckhashArray [buckHashSize]atomic.UnsafePointer // *bucket + const mProfCycleWrap = uint32(len(memRecord{}.future)) * (2 << 24) +// mProfCycleHolder holds the global heap profile cycle number (wrapped at +// mProfCycleWrap, stored starting at bit 1), and a flag (stored at bit 0) to +// indicate whether future[cycle] in all buckets has been queued to flush into +// the active profile. +type mProfCycleHolder struct { + value atomic.Uint32 +} + +// read returns the current cycle count. +func (c *mProfCycleHolder) read() (cycle uint32) { + v := c.value.Load() + cycle = v >> 1 + return cycle +} + +// setFlushed sets the flushed flag. It returns the current cycle count and the +// previous value of the flushed flag. +func (c *mProfCycleHolder) setFlushed() (cycle uint32, alreadyFlushed bool) { + for { + prev := c.value.Load() + cycle = prev >> 1 + alreadyFlushed = (prev & 0x1) != 0 + next := prev | 0x1 + if c.value.CompareAndSwap(prev, next) { + return cycle, alreadyFlushed + } + } +} + +// increment increases the cycle count by one, wrapping the value at +// mProfCycleWrap. It clears the flushed flag. +func (c *mProfCycleHolder) increment() { + // We explicitly wrap mProfCycle rather than depending on + // uint wraparound because the memRecord.future ring does not + // itself wrap at a power of two. + for { + prev := c.value.Load() + cycle := prev >> 1 + cycle = (cycle + 1) % mProfCycleWrap + next := cycle << 1 + if c.value.CompareAndSwap(prev, next) { + break + } + } +} + // newBucket allocates a bucket with the given type and number of stack entries. func newBucket(typ bucketType, nstk int) *bucket { size := unsafe.Sizeof(bucket{}) + uintptr(nstk)*unsafe.Sizeof(uintptr(0)) @@ -171,7 +223,6 @@ func newBucket(typ bucketType, nstk int) *bucket { } b := (*bucket)(persistentalloc(size, 0, &memstats.buckhash_sys)) - bucketmem += size b.typ = typ b.nstk = uintptr(nstk) return b @@ -203,11 +254,19 @@ func (b *bucket) bp() *blockRecord { // Return the bucket for stk[0:nstk], allocating new bucket if needed. func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket { - if buckhash == nil { - buckhash = (*[buckHashSize]*bucket)(sysAlloc(unsafe.Sizeof(*buckhash), &memstats.buckhash_sys)) - if buckhash == nil { - throw("runtime: cannot allocate memory") + bh := (*buckhashArray)(buckhash.Load()) + if bh == nil { + lock(&profInsertLock) + // check again under the lock + bh = (*buckhashArray)(buckhash.Load()) + if bh == nil { + bh = (*buckhashArray)(sysAlloc(unsafe.Sizeof(buckhashArray{}), &memstats.buckhash_sys)) + if bh == nil { + throw("runtime: cannot allocate memory") + } + buckhash.StoreNoWB(unsafe.Pointer(bh)) } + unlock(&profInsertLock) } // Hash stack. @@ -226,7 +285,8 @@ func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket h ^= h >> 11 i := int(h % buckHashSize) - for b := buckhash[i]; b != nil; b = b.next { + // first check optimistically, without the lock + for b := (*bucket)(bh[i].Load()); b != nil; b = b.next { if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) { return b } @@ -236,23 +296,37 @@ func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket return nil } + lock(&profInsertLock) + // check again under the insertion lock + for b := (*bucket)(bh[i].Load()); b != nil; b = b.next { + if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) { + unlock(&profInsertLock) + return b + } + } + // Create new bucket. b := newBucket(typ, len(stk)) copy(b.stk(), stk) b.hash = h b.size = size - b.next = buckhash[i] - buckhash[i] = b + + var allnext *atomic.UnsafePointer if typ == memProfile { - b.allnext = mbuckets - mbuckets = b + allnext = &mbuckets } else if typ == mutexProfile { - b.allnext = xbuckets - xbuckets = b + allnext = &xbuckets } else { - b.allnext = bbuckets - bbuckets = b + allnext = &bbuckets } + + b.next = (*bucket)(bh[i].Load()) + b.allnext = (*bucket)(allnext.Load()) + + bh[i].StoreNoWB(unsafe.Pointer(b)) + allnext.StoreNoWB(unsafe.Pointer(b)) + + unlock(&profInsertLock) return b } @@ -277,13 +351,7 @@ func eqslice(x, y []uintptr) bool { // frees after the world is started again count towards a new heap // profiling cycle. func mProf_NextCycle() { - lock(&proflock) - // We explicitly wrap mProf.cycle rather than depending on - // uint wraparound because the memRecord.future ring does not - // itself wrap at a power of two. - mProf.cycle = (mProf.cycle + 1) % mProfCycleWrap - mProf.flushed = false - unlock(&proflock) + mProfCycle.increment() } // mProf_Flush flushes the events from the current heap profiling @@ -294,22 +362,33 @@ func mProf_NextCycle() { // contrast with mProf_NextCycle, this is somewhat expensive, but safe // to do concurrently. func mProf_Flush() { - lock(&proflock) - if !mProf.flushed { - mProf_FlushLocked() - mProf.flushed = true + cycle, alreadyFlushed := mProfCycle.setFlushed() + if alreadyFlushed { + return } - unlock(&proflock) + + index := cycle % uint32(len(memRecord{}.future)) + lock(&profMemActiveLock) + lock(&profMemFutureLock[index]) + mProf_FlushLocked(index) + unlock(&profMemFutureLock[index]) + unlock(&profMemActiveLock) } -func mProf_FlushLocked() { - c := mProf.cycle - for b := mbuckets; b != nil; b = b.allnext { +// mProf_FlushLocked flushes the events from the heap profiling cycle at index +// into the active profile. The caller must hold the lock for the active profile +// (profMemActiveLock) and for the profiling cycle at index +// (profMemFutureLock[index]). +func mProf_FlushLocked(index uint32) { + assertLockHeld(&profMemActiveLock) + assertLockHeld(&profMemFutureLock[index]) + head := (*bucket)(mbuckets.Load()) + for b := head; b != nil; b = b.allnext { mp := b.mp() // Flush cycle C into the published profile and clear // it for reuse. - mpc := &mp.future[c%uint32(len(mp.future))] + mpc := &mp.future[index] mp.active.add(mpc) *mpc = memRecordCycle{} } @@ -320,39 +399,41 @@ func mProf_FlushLocked() { // snapshot as of the last mark termination without advancing the heap // profile cycle. func mProf_PostSweep() { - lock(&proflock) // Flush cycle C+1 to the active profile so everything as of // the last mark termination becomes visible. *Don't* advance // the cycle, since we're still accumulating allocs in cycle // C+2, which have to become C+1 in the next mark termination // and so on. - c := mProf.cycle - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - mpc := &mp.future[(c+1)%uint32(len(mp.future))] - mp.active.add(mpc) - *mpc = memRecordCycle{} - } - unlock(&proflock) + cycle := mProfCycle.read() + 1 + + index := cycle % uint32(len(memRecord{}.future)) + lock(&profMemActiveLock) + lock(&profMemFutureLock[index]) + mProf_FlushLocked(index) + unlock(&profMemFutureLock[index]) + unlock(&profMemActiveLock) } // Called by malloc to record a profiled block. func mProf_Malloc(p unsafe.Pointer, size uintptr) { var stk [maxStack]uintptr nstk := callers(4, stk[:]) - lock(&proflock) + + index := (mProfCycle.read() + 2) % uint32(len(memRecord{}.future)) + b := stkbucket(memProfile, size, stk[:nstk], true) - c := mProf.cycle mp := b.mp() - mpc := &mp.future[(c+2)%uint32(len(mp.future))] + mpc := &mp.future[index] + + lock(&profMemFutureLock[index]) mpc.allocs++ mpc.alloc_bytes += size - unlock(&proflock) + unlock(&profMemFutureLock[index]) - // Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock. - // This reduces potential contention and chances of deadlocks. - // Since the object must be alive during call to mProf_Malloc, - // it's fine to do this non-atomically. + // Setprofilebucket locks a bunch of other mutexes, so we call it outside of + // the profiler locks. This reduces potential contention and chances of + // deadlocks. Since the object must be alive during the call to + // mProf_Malloc, it's fine to do this non-atomically. systemstack(func() { setprofilebucket(p, b) }) @@ -360,13 +441,15 @@ func mProf_Malloc(p unsafe.Pointer, size uintptr) { // Called when freeing a profiled block. func mProf_Free(b *bucket, size uintptr) { - lock(&proflock) - c := mProf.cycle + index := (mProfCycle.read() + 1) % uint32(len(memRecord{}.future)) + mp := b.mp() - mpc := &mp.future[(c+1)%uint32(len(mp.future))] + mpc := &mp.future[index] + + lock(&profMemFutureLock[index]) mpc.frees++ mpc.free_bytes += size - unlock(&proflock) + unlock(&profMemFutureLock[index]) } var blockprofilerate uint64 // in CPU ticks @@ -423,18 +506,19 @@ func saveblockevent(cycles, rate int64, skip int, which bucketType) { } else { nstk = gcallers(gp.m.curg, skip, stk[:]) } - lock(&proflock) b := stkbucket(which, 0, stk[:nstk], true) + bp := b.bp() + lock(&profBlockLock) if which == blockProfile && cycles < rate { // Remove sampling bias, see discussion on http://golang.org/cl/299991. - b.bp().count += float64(rate) / float64(cycles) - b.bp().cycles += rate + bp.count += float64(rate) / float64(cycles) + bp.cycles += rate } else { - b.bp().count++ - b.bp().cycles += cycles + bp.count++ + bp.cycles += cycles } - unlock(&proflock) + unlock(&profBlockLock) } var mutexprofilerate uint64 // fraction sampled @@ -566,13 +650,18 @@ func (r *MemProfileRecord) Stack() []uintptr { // the testing package's -test.memprofile flag instead // of calling MemProfile directly. func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { - lock(&proflock) + cycle := mProfCycle.read() // If we're between mProf_NextCycle and mProf_Flush, take care // of flushing to the active profile so we only have to look // at the active profile below. - mProf_FlushLocked() + index := cycle % uint32(len(memRecord{}.future)) + lock(&profMemActiveLock) + lock(&profMemFutureLock[index]) + mProf_FlushLocked(index) + unlock(&profMemFutureLock[index]) clear := true - for b := mbuckets; b != nil; b = b.allnext { + head := (*bucket)(mbuckets.Load()) + for b := head; b != nil; b = b.allnext { mp := b.mp() if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { n++ @@ -587,11 +676,13 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { // garbage collection is disabled from the beginning of execution, // accumulate all of the cycles, and recount buckets. n = 0 - for b := mbuckets; b != nil; b = b.allnext { + for b := head; b != nil; b = b.allnext { mp := b.mp() for c := range mp.future { + lock(&profMemFutureLock[c]) mp.active.add(&mp.future[c]) mp.future[c] = memRecordCycle{} + unlock(&profMemFutureLock[c]) } if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { n++ @@ -601,7 +692,7 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { if n <= len(p) { ok = true idx := 0 - for b := mbuckets; b != nil; b = b.allnext { + for b := head; b != nil; b = b.allnext { mp := b.mp() if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { record(&p[idx], b) @@ -609,7 +700,7 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { } } } - unlock(&proflock) + unlock(&profMemActiveLock) return } @@ -621,11 +712,14 @@ func record(r *MemProfileRecord, b *bucket) { r.AllocObjects = int64(mp.active.allocs) r.FreeObjects = int64(mp.active.frees) if raceenabled { - racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(), funcPC(MemProfile)) + racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(), abi.FuncPCABIInternal(MemProfile)) } if msanenabled { msanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0)) } + if asanenabled { + asanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0)) + } copy(r.Stack0[:], b.stk()) for i := int(b.nstk); i < len(r.Stack0); i++ { r.Stack0[i] = 0 @@ -633,12 +727,13 @@ func record(r *MemProfileRecord, b *bucket) { } func iterate_memprof(fn func(*bucket, uintptr, *uintptr, uintptr, uintptr, uintptr)) { - lock(&proflock) - for b := mbuckets; b != nil; b = b.allnext { + lock(&profMemActiveLock) + head := (*bucket)(mbuckets.Load()) + for b := head; b != nil; b = b.allnext { mp := b.mp() fn(b, b.nstk, &b.stk()[0], b.size, mp.active.allocs, mp.active.frees) } - unlock(&proflock) + unlock(&profMemActiveLock) } // BlockProfileRecord describes blocking events originated @@ -657,13 +752,14 @@ type BlockProfileRecord struct { // the testing package's -test.blockprofile flag instead // of calling BlockProfile directly. func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { - lock(&proflock) - for b := bbuckets; b != nil; b = b.allnext { + lock(&profBlockLock) + head := (*bucket)(bbuckets.Load()) + for b := head; b != nil; b = b.allnext { n++ } if n <= len(p) { ok = true - for b := bbuckets; b != nil; b = b.allnext { + for b := head; b != nil; b = b.allnext { bp := b.bp() r := &p[0] r.Count = int64(bp.count) @@ -674,11 +770,14 @@ func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { } r.Cycles = bp.cycles if raceenabled { - racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(), funcPC(BlockProfile)) + racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(), abi.FuncPCABIInternal(BlockProfile)) } if msanenabled { msanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0)) } + if asanenabled { + asanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0)) + } i := copy(r.Stack0[:], b.stk()) for ; i < len(r.Stack0); i++ { r.Stack0[i] = 0 @@ -686,7 +785,7 @@ func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { p = p[1:] } } - unlock(&proflock) + unlock(&profBlockLock) return } @@ -697,13 +796,14 @@ func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { // Most clients should use the runtime/pprof package // instead of calling MutexProfile directly. func MutexProfile(p []BlockProfileRecord) (n int, ok bool) { - lock(&proflock) - for b := xbuckets; b != nil; b = b.allnext { + lock(&profBlockLock) + head := (*bucket)(xbuckets.Load()) + for b := head; b != nil; b = b.allnext { n++ } if n <= len(p) { ok = true - for b := xbuckets; b != nil; b = b.allnext { + for b := head; b != nil; b = b.allnext { bp := b.bp() r := &p[0] r.Count = int64(bp.count) @@ -715,7 +815,7 @@ func MutexProfile(p []BlockProfileRecord) (n int, ok bool) { p = p[1:] } } - unlock(&proflock) + unlock(&profBlockLock) return } @@ -746,11 +846,260 @@ func runtime_goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer return goroutineProfileWithLabels(p, labels) } +const go119ConcurrentGoroutineProfile = true + // labels may be nil. If labels is non-nil, it must have the same length as p. func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) { if labels != nil && len(labels) != len(p) { labels = nil } + + if go119ConcurrentGoroutineProfile { + return goroutineProfileWithLabelsConcurrent(p, labels) + } + return goroutineProfileWithLabelsSync(p, labels) +} + +var goroutineProfile = struct { + sema uint32 + active bool + offset atomic.Int64 + records []StackRecord + labels []unsafe.Pointer +}{ + sema: 1, +} + +// goroutineProfileState indicates the status of a goroutine's stack for the +// current in-progress goroutine profile. Goroutines' stacks are initially +// "Absent" from the profile, and end up "Satisfied" by the time the profile is +// complete. While a goroutine's stack is being captured, its +// goroutineProfileState will be "InProgress" and it will not be able to run +// until the capture completes and the state moves to "Satisfied". +// +// Some goroutines (the finalizer goroutine, which at various times can be +// either a "system" or a "user" goroutine, and the goroutine that is +// coordinating the profile, any goroutines created during the profile) move +// directly to the "Satisfied" state. +type goroutineProfileState uint32 + +const ( + goroutineProfileAbsent goroutineProfileState = iota + goroutineProfileInProgress + goroutineProfileSatisfied +) + +type goroutineProfileStateHolder atomic.Uint32 + +func (p *goroutineProfileStateHolder) Load() goroutineProfileState { + return goroutineProfileState((*atomic.Uint32)(p).Load()) +} + +func (p *goroutineProfileStateHolder) Store(value goroutineProfileState) { + (*atomic.Uint32)(p).Store(uint32(value)) +} + +func (p *goroutineProfileStateHolder) CompareAndSwap(old, new goroutineProfileState) bool { + return (*atomic.Uint32)(p).CompareAndSwap(uint32(old), uint32(new)) +} + +func goroutineProfileWithLabelsConcurrent(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) { + semacquire(&goroutineProfile.sema) + + ourg := getg() + + stopTheWorld("profile") + // Using gcount while the world is stopped should give us a consistent view + // of the number of live goroutines, minus the number of goroutines that are + // alive and permanently marked as "system". But to make this count agree + // with what we'd get from isSystemGoroutine, we need special handling for + // goroutines that can vary between user and system to ensure that the count + // doesn't change during the collection. So, check the finalizer goroutine + // in particular. + n = int(gcount()) + if fingStatus.Load()&fingRunningFinalizer != 0 { + n++ + } + + if n > len(p) { + // There's not enough space in p to store the whole profile, so (per the + // contract of runtime.GoroutineProfile) we're not allowed to write to p + // at all and must return n, false. + startTheWorld() + semrelease(&goroutineProfile.sema) + return n, false + } + + // Save current goroutine. + sp := getcallersp() + pc := getcallerpc() + systemstack(func() { + saveg(pc, sp, ourg, &p[0]) + }) + ourg.goroutineProfiled.Store(goroutineProfileSatisfied) + goroutineProfile.offset.Store(1) + + // Prepare for all other goroutines to enter the profile. Aside from ourg, + // every goroutine struct in the allgs list has its goroutineProfiled field + // cleared. Any goroutine created from this point on (while + // goroutineProfile.active is set) will start with its goroutineProfiled + // field set to goroutineProfileSatisfied. + goroutineProfile.active = true + goroutineProfile.records = p + goroutineProfile.labels = labels + // The finalizer goroutine needs special handling because it can vary over + // time between being a user goroutine (eligible for this profile) and a + // system goroutine (to be excluded). Pick one before restarting the world. + if fing != nil { + fing.goroutineProfiled.Store(goroutineProfileSatisfied) + if readgstatus(fing) != _Gdead && !isSystemGoroutine(fing, false) { + doRecordGoroutineProfile(fing) + } + } + startTheWorld() + + // Visit each goroutine that existed as of the startTheWorld call above. + // + // New goroutines may not be in this list, but we didn't want to know about + // them anyway. If they do appear in this list (via reusing a dead goroutine + // struct, or racing to launch between the world restarting and us getting + // the list), they will already have their goroutineProfiled field set to + // goroutineProfileSatisfied before their state transitions out of _Gdead. + // + // Any goroutine that the scheduler tries to execute concurrently with this + // call will start by adding itself to the profile (before the act of + // executing can cause any changes in its stack). + forEachGRace(func(gp1 *g) { + tryRecordGoroutineProfile(gp1, Gosched) + }) + + stopTheWorld("profile cleanup") + endOffset := goroutineProfile.offset.Swap(0) + goroutineProfile.active = false + goroutineProfile.records = nil + goroutineProfile.labels = nil + startTheWorld() + + // Restore the invariant that every goroutine struct in allgs has its + // goroutineProfiled field cleared. + forEachGRace(func(gp1 *g) { + gp1.goroutineProfiled.Store(goroutineProfileAbsent) + }) + + if raceenabled { + raceacquire(unsafe.Pointer(&labelSync)) + } + + if n != int(endOffset) { + // It's a big surprise that the number of goroutines changed while we + // were collecting the profile. But probably better to return a + // truncated profile than to crash the whole process. + // + // For instance, needm moves a goroutine out of the _Gdead state and so + // might be able to change the goroutine count without interacting with + // the scheduler. For code like that, the race windows are small and the + // combination of features is uncommon, so it's hard to be (and remain) + // sure we've caught them all. + } + + semrelease(&goroutineProfile.sema) + return n, true +} + +// tryRecordGoroutineProfileWB asserts that write barriers are allowed and calls +// tryRecordGoroutineProfile. +// +//go:yeswritebarrierrec +func tryRecordGoroutineProfileWB(gp1 *g) { + if getg().m.p.ptr() == nil { + throw("no P available, write barriers are forbidden") + } + tryRecordGoroutineProfile(gp1, osyield) +} + +// tryRecordGoroutineProfile ensures that gp1 has the appropriate representation +// in the current goroutine profile: either that it should not be profiled, or +// that a snapshot of its call stack and labels are now in the profile. +func tryRecordGoroutineProfile(gp1 *g, yield func()) { + if readgstatus(gp1) == _Gdead { + // Dead goroutines should not appear in the profile. Goroutines that + // start while profile collection is active will get goroutineProfiled + // set to goroutineProfileSatisfied before transitioning out of _Gdead, + // so here we check _Gdead first. + return + } + if isSystemGoroutine(gp1, true) { + // System goroutines should not appear in the profile. (The finalizer + // goroutine is marked as "already profiled".) + return + } + + for { + prev := gp1.goroutineProfiled.Load() + if prev == goroutineProfileSatisfied { + // This goroutine is already in the profile (or is new since the + // start of collection, so shouldn't appear in the profile). + break + } + if prev == goroutineProfileInProgress { + // Something else is adding gp1 to the goroutine profile right now. + // Give that a moment to finish. + yield() + continue + } + + // While we have gp1.goroutineProfiled set to + // goroutineProfileInProgress, gp1 may appear _Grunnable but will not + // actually be able to run. Disable preemption for ourselves, to make + // sure we finish profiling gp1 right away instead of leaving it stuck + // in this limbo. + mp := acquirem() + if gp1.goroutineProfiled.CompareAndSwap(goroutineProfileAbsent, goroutineProfileInProgress) { + doRecordGoroutineProfile(gp1) + gp1.goroutineProfiled.Store(goroutineProfileSatisfied) + } + releasem(mp) + } +} + +// doRecordGoroutineProfile writes gp1's call stack and labels to an in-progress +// goroutine profile. Preemption is disabled. +// +// This may be called via tryRecordGoroutineProfile in two ways: by the +// goroutine that is coordinating the goroutine profile (running on its own +// stack), or from the scheduler in preparation to execute gp1 (running on the +// system stack). +func doRecordGoroutineProfile(gp1 *g) { + if readgstatus(gp1) == _Grunning { + print("doRecordGoroutineProfile gp1=", gp1.goid, "\n") + throw("cannot read stack of running goroutine") + } + + offset := int(goroutineProfile.offset.Add(1)) - 1 + + if offset >= len(goroutineProfile.records) { + // Should be impossible, but better to return a truncated profile than + // to crash the entire process at this point. Instead, deal with it in + // goroutineProfileWithLabelsConcurrent where we have more context. + return + } + + // saveg calls gentraceback, which may call cgo traceback functions. When + // called from the scheduler, this is on the system stack already so + // traceback.go:cgoContextPCs will avoid calling back into the scheduler. + // + // When called from the goroutine coordinating the profile, we still have + // set gp1.goroutineProfiled to goroutineProfileInProgress and so are still + // preventing it from being truly _Grunnable. So we'll use the system stack + // to avoid schedule delays. + systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &goroutineProfile.records[offset]) }) + + if goroutineProfile.labels != nil { + goroutineProfile.labels[offset] = gp1.labels + } +} + +func goroutineProfileWithLabelsSync(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) { gp := getg() isOK := func(gp1 *g) bool { @@ -798,7 +1147,11 @@ func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int // truncated profile than to crash the entire process. return } - saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) + // saveg calls gentraceback, which may call cgo traceback functions. + // The world is stopped, so it cannot use cgocall (which will be + // blocked at exitsyscall). Do it on the system stack so it won't + // call into the schedular (see traceback.go:cgoContextPCs). + systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) }) if labels != nil { lbl[0] = gp1.labels lbl = lbl[1:] @@ -807,6 +1160,10 @@ func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int }) } + if raceenabled { + raceacquire(unsafe.Pointer(&labelSync)) + } + startTheWorld() return n, ok } diff --git a/src/runtime/mranges.go b/src/runtime/mranges.go index 84a2c06dbb2e66..9cf83cc6131dc1 100644 --- a/src/runtime/mranges.go +++ b/src/runtime/mranges.go @@ -10,7 +10,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" + "runtime/internal/atomic" "unsafe" ) @@ -141,6 +142,69 @@ func (l offAddr) addr() uintptr { return l.a } +// atomicOffAddr is like offAddr, but operations on it are atomic. +// It also contains operations to be able to store marked addresses +// to ensure that they're not overridden until they've been seen. +type atomicOffAddr struct { + // a contains the offset address, unlike offAddr. + a atomic.Int64 +} + +// Clear attempts to store minOffAddr in atomicOffAddr. It may fail +// if a marked value is placed in the box in the meanwhile. +func (b *atomicOffAddr) Clear() { + for { + old := b.a.Load() + if old < 0 { + return + } + if b.a.CompareAndSwap(old, int64(minOffAddr.addr()-arenaBaseOffset)) { + return + } + } +} + +// StoreMin stores addr if it's less than the current value in the +// offset address space if the current value is not marked. +func (b *atomicOffAddr) StoreMin(addr uintptr) { + new := int64(addr - arenaBaseOffset) + for { + old := b.a.Load() + if old < new { + return + } + if b.a.CompareAndSwap(old, new) { + return + } + } +} + +// StoreUnmark attempts to unmark the value in atomicOffAddr and +// replace it with newAddr. markedAddr must be a marked address +// returned by Load. This function will not store newAddr if the +// box no longer contains markedAddr. +func (b *atomicOffAddr) StoreUnmark(markedAddr, newAddr uintptr) { + b.a.CompareAndSwap(-int64(markedAddr-arenaBaseOffset), int64(newAddr-arenaBaseOffset)) +} + +// StoreMarked stores addr but first converted to the offset address +// space and then negated. +func (b *atomicOffAddr) StoreMarked(addr uintptr) { + b.a.Store(-int64(addr - arenaBaseOffset)) +} + +// Load returns the address in the box as a virtual address. It also +// returns if the value was marked or not. +func (b *atomicOffAddr) Load() (uintptr, bool) { + v := b.a.Load() + wasMarked := false + if v < 0 { + wasMarked = true + v = -v + } + return uintptr(v) + arenaBaseOffset, wasMarked +} + // addrRanges is a data structure holding a collection of ranges of // address space. // @@ -167,7 +231,7 @@ func (a *addrRanges) init(sysStat *sysMemStat) { ranges := (*notInHeapSlice)(unsafe.Pointer(&a.ranges)) ranges.len = 0 ranges.cap = 16 - ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), sys.PtrSize, sysStat)) + ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), goarch.PtrSize, sysStat)) a.sysStat = sysStat a.totalBytes = 0 } @@ -294,7 +358,7 @@ func (a *addrRanges) add(r addrRange) { ranges := (*notInHeapSlice)(unsafe.Pointer(&a.ranges)) ranges.len = len(oldRanges) + 1 ranges.cap = cap(oldRanges) * 2 - ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), sys.PtrSize, a.sysStat)) + ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), goarch.PtrSize, a.sysStat)) // Copy in the old array, but make space for the new range. copy(a.ranges[:i], oldRanges[:i]) @@ -364,7 +428,7 @@ func (a *addrRanges) cloneInto(b *addrRanges) { ranges := (*notInHeapSlice)(unsafe.Pointer(&b.ranges)) ranges.len = 0 ranges.cap = cap(a.ranges) - ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), sys.PtrSize, b.sysStat)) + ranges.array = (*notInHeap)(persistentalloc(unsafe.Sizeof(addrRange{})*uintptr(ranges.cap), goarch.PtrSize, b.sysStat)) } b.ranges = b.ranges[:len(a.ranges)] b.totalBytes = a.totalBytes diff --git a/src/runtime/msan.go b/src/runtime/msan.go index 25aaf94e26cec2..5e2aae1bd1616e 100644 --- a/src/runtime/msan.go +++ b/src/runtime/msan.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build msan -// +build msan package runtime @@ -32,8 +31,8 @@ const msanenabled = true // //go:nosplit func msanread(addr unsafe.Pointer, sz uintptr) { - g := getg() - if g == nil || g.m == nil || g == g.m.g0 || g == g.m.gsignal { + gp := getg() + if gp == nil || gp.m == nil || gp == gp.m.g0 || gp == gp.m.gsignal { return } domsanread(addr, sz) @@ -55,6 +54,7 @@ func msanfree(addr unsafe.Pointer, sz uintptr) func msanmove(dst, src unsafe.Pointer, sz uintptr) // These are called from msan_GOARCH.s +// //go:cgo_import_static __msan_read_go //go:cgo_import_static __msan_write_go //go:cgo_import_static __msan_malloc_go diff --git a/src/runtime/msan/msan.go b/src/runtime/msan/msan.go index c81577dddac08c..f1bf4e1065cf96 100644 --- a/src/runtime/msan/msan.go +++ b/src/runtime/msan/msan.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build msan,linux -// +build amd64 arm64 +//go:build msan && linux && (amd64 || arm64) package msan diff --git a/src/runtime/msan0.go b/src/runtime/msan0.go index b1096a6750e5d2..2f5fd2d98216e5 100644 --- a/src/runtime/msan0.go +++ b/src/runtime/msan0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !msan -// +build !msan // Dummy MSan support API, used when not built with -msan. diff --git a/src/runtime/msan_amd64.s b/src/runtime/msan_amd64.s index 1bb57a3b7e80b8..89ed3048d0ed5c 100644 --- a/src/runtime/msan_amd64.s +++ b/src/runtime/msan_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build msan -// +build msan #include "go_asm.h" #include "go_tls.h" diff --git a/src/runtime/msan_arm64.s b/src/runtime/msan_arm64.s index 93ade8dd8970f8..b9eff34ab606a0 100644 --- a/src/runtime/msan_arm64.s +++ b/src/runtime/msan_arm64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build msan -// +build msan #include "go_asm.h" #include "textflag.h" diff --git a/src/runtime/mspanset.go b/src/runtime/mspanset.go index 10d2596c38ccb3..abbd4501b14ed4 100644 --- a/src/runtime/mspanset.go +++ b/src/runtime/mspanset.go @@ -6,8 +6,8 @@ package runtime import ( "internal/cpu" + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -33,9 +33,9 @@ type spanSet struct { // anyway. (In principle, we could do this during STW.) spineLock mutex - spine unsafe.Pointer // *[N]*spanSetBlock, accessed atomically - spineLen uintptr // Spine array length, accessed atomically - spineCap uintptr // Spine array cap, accessed under lock + spine atomicSpanSetSpinePointer // *[N]atomic.Pointer[spanSetBlock] + spineLen atomic.Uintptr // Spine array length + spineCap uintptr // Spine array cap, accessed under spineLock // index is the head and tail of the spanSet in a single field. // The head and the tail both represent an index into the logical @@ -48,7 +48,7 @@ type spanSet struct { // span in the heap were stored in this set, and each span were // the minimum size (1 runtime page, 8 KiB), then roughly the // smallest heap which would be unrepresentable is 32 TiB in size. - index headTailIndex + index atomicHeadTailIndex } const ( @@ -63,10 +63,10 @@ type spanSetBlock struct { // popped is the number of pop operations that have occurred on // this block. This number is used to help determine when a block // may be safely recycled. - popped uint32 + popped atomic.Uint32 // spans is the set of spans in this block. - spans [spanSetBlockEntries]*mspan + spans [spanSetBlockEntries]atomicMSpanPointer } // push adds span s to buffer b. push is safe to call concurrently @@ -77,39 +77,40 @@ func (b *spanSet) push(s *mspan) { top, bottom := cursor/spanSetBlockEntries, cursor%spanSetBlockEntries // Do we need to add a block? - spineLen := atomic.Loaduintptr(&b.spineLen) + spineLen := b.spineLen.Load() var block *spanSetBlock retry: if top < spineLen { - spine := atomic.Loadp(unsafe.Pointer(&b.spine)) - blockp := add(spine, sys.PtrSize*top) - block = (*spanSetBlock)(atomic.Loadp(blockp)) + block = b.spine.Load().lookup(top).Load() } else { // Add a new block to the spine, potentially growing // the spine. lock(&b.spineLock) // spineLen cannot change until we release the lock, // but may have changed while we were waiting. - spineLen = atomic.Loaduintptr(&b.spineLen) + spineLen = b.spineLen.Load() if top < spineLen { unlock(&b.spineLock) goto retry } + spine := b.spine.Load() if spineLen == b.spineCap { // Grow the spine. newCap := b.spineCap * 2 if newCap == 0 { newCap = spanSetInitSpineCap } - newSpine := persistentalloc(newCap*sys.PtrSize, cpu.CacheLineSize, &memstats.gcMiscSys) + newSpine := persistentalloc(newCap*goarch.PtrSize, cpu.CacheLineSize, &memstats.gcMiscSys) if b.spineCap != 0 { // Blocks are allocated off-heap, so // no write barriers. - memmove(newSpine, b.spine, b.spineCap*sys.PtrSize) + memmove(newSpine, spine.p, b.spineCap*goarch.PtrSize) } + spine = spanSetSpinePointer{newSpine} + // Spine is allocated off-heap, so no write barrier. - atomic.StorepNoWB(unsafe.Pointer(&b.spine), newSpine) + b.spine.StoreNoWB(spine) b.spineCap = newCap // We can't immediately free the old spine // since a concurrent push with a lower index @@ -124,16 +125,15 @@ retry: block = spanSetBlockPool.alloc() // Add it to the spine. - blockp := add(b.spine, sys.PtrSize*top) // Blocks are allocated off-heap, so no write barrier. - atomic.StorepNoWB(blockp, unsafe.Pointer(block)) - atomic.Storeuintptr(&b.spineLen, spineLen+1) + spine.lookup(top).StoreNoWB(block) + b.spineLen.Store(spineLen + 1) unlock(&b.spineLock) } // We have a block. Insert the span atomically, since there may be // concurrent readers via the block API. - atomic.StorepNoWB(unsafe.Pointer(&block.spans[bottom]), unsafe.Pointer(s)) + block.spans[bottom].StoreNoWB(s) } // pop removes and returns a span from buffer b, or nil if b is empty. @@ -150,7 +150,7 @@ claimLoop: } // Check if the head position we want to claim is actually // backed by a block. - spineLen := atomic.Loaduintptr(&b.spineLen) + spineLen := b.spineLen.Load() if spineLen <= uintptr(head)/spanSetBlockEntries { // We're racing with a spine growth and the allocation of // a new block (and maybe a new spine!), and trying to grab @@ -180,28 +180,27 @@ claimLoop: // We may be reading a stale spine pointer, but because the length // grows monotonically and we've already verified it, we'll definitely // be reading from a valid block. - spine := atomic.Loadp(unsafe.Pointer(&b.spine)) - blockp := add(spine, sys.PtrSize*uintptr(top)) + blockp := b.spine.Load().lookup(uintptr(top)) // Given that the spine length is correct, we know we will never // see a nil block here, since the length is always updated after // the block is set. - block := (*spanSetBlock)(atomic.Loadp(blockp)) - s := (*mspan)(atomic.Loadp(unsafe.Pointer(&block.spans[bottom]))) + block := blockp.Load() + s := block.spans[bottom].Load() for s == nil { // We raced with the span actually being set, but given that we // know a block for this span exists, the race window here is // extremely small. Try again. - s = (*mspan)(atomic.Loadp(unsafe.Pointer(&block.spans[bottom]))) + s = block.spans[bottom].Load() } // Clear the pointer. This isn't strictly necessary, but defensively // avoids accidentally re-using blocks which could lead to memory // corruption. This way, we'll get a nil pointer access instead. - atomic.StorepNoWB(unsafe.Pointer(&block.spans[bottom]), nil) + block.spans[bottom].StoreNoWB(nil) // Increase the popped count. If we are the last possible popper // in the block (note that bottom need not equal spanSetBlockEntries-1 - // due to races) then it's our resposibility to free the block. + // due to races) then it's our responsibility to free the block. // // If we increment popped to spanSetBlockEntries, we can be sure that // we're the last popper for this block, and it's thus safe to free it. @@ -211,9 +210,9 @@ claimLoop: // pushers (there can't be any). Note that we may not be the popper // which claimed the last slot in the block, we're just the last one // to finish popping. - if atomic.Xadd(&block.popped, 1) == spanSetBlockEntries { + if block.popped.Add(1) == spanSetBlockEntries { // Clear the block's pointer. - atomic.StorepNoWB(blockp, nil) + blockp.StoreNoWB(nil) // Return the block to the block pool. spanSetBlockPool.free(block) @@ -235,23 +234,23 @@ func (b *spanSet) reset() { throw("attempt to clear non-empty span set") } top := head / spanSetBlockEntries - if uintptr(top) < b.spineLen { + if uintptr(top) < b.spineLen.Load() { // If the head catches up to the tail and the set is empty, // we may not clean up the block containing the head and tail // since it may be pushed into again. In order to avoid leaking // memory since we're going to reset the head and tail, clean // up such a block now, if it exists. - blockp := (**spanSetBlock)(add(b.spine, sys.PtrSize*uintptr(top))) - block := *blockp + blockp := b.spine.Load().lookup(uintptr(top)) + block := blockp.Load() if block != nil { - // Sanity check the popped value. - if block.popped == 0 { + // Check the popped value. + if block.popped.Load() == 0 { // popped should never be zero because that means we have // pushed at least one value but not yet popped if this // block pointer is not nil. throw("span set block with unpopped elements found in reset") } - if block.popped == spanSetBlockEntries { + if block.popped.Load() == spanSetBlockEntries { // popped should also never be equal to spanSetBlockEntries // because the last popper should have made the block pointer // in this slot nil. @@ -259,14 +258,45 @@ func (b *spanSet) reset() { } // Clear the pointer to the block. - atomic.StorepNoWB(unsafe.Pointer(blockp), nil) + blockp.StoreNoWB(nil) // Return the block to the block pool. spanSetBlockPool.free(block) } } b.index.reset() - atomic.Storeuintptr(&b.spineLen, 0) + b.spineLen.Store(0) +} + +// atomicSpanSetSpinePointer is an atomically-accessed spanSetSpinePointer. +// +// It has the same semantics as atomic.UnsafePointer. +type atomicSpanSetSpinePointer struct { + a atomic.UnsafePointer +} + +// Loads the spanSetSpinePointer and returns it. +// +// It has the same semantics as atomic.UnsafePointer. +func (s *atomicSpanSetSpinePointer) Load() spanSetSpinePointer { + return spanSetSpinePointer{s.a.Load()} +} + +// Stores the spanSetSpinePointer. +// +// It has the same semantics as atomic.UnsafePointer. +func (s *atomicSpanSetSpinePointer) StoreNoWB(p spanSetSpinePointer) { + s.a.StoreNoWB(p.p) +} + +// spanSetSpinePointer represents a pointer to a contiguous block of atomic.Pointer[spanSetBlock]. +type spanSetSpinePointer struct { + p unsafe.Pointer +} + +// lookup returns &s[idx]. +func (s spanSetSpinePointer) lookup(idx uintptr) *atomic.Pointer[spanSetBlock] { + return (*atomic.Pointer[spanSetBlock])(add(unsafe.Pointer(s.p), goarch.PtrSize*idx)) } // spanSetBlockPool is a global pool of spanSetBlocks. @@ -288,7 +318,7 @@ func (p *spanSetBlockAlloc) alloc() *spanSetBlock { // free returns a spanSetBlock back to the pool. func (p *spanSetBlockAlloc) free(block *spanSetBlock) { - atomic.Store(&block.popped, 0) + block.popped.Store(0) p.stack.push(&block.lfnode) } @@ -317,29 +347,34 @@ func (h headTailIndex) split() (head uint32, tail uint32) { return h.head(), h.tail() } +// atomicHeadTailIndex is an atomically-accessed headTailIndex. +type atomicHeadTailIndex struct { + u atomic.Uint64 +} + // load atomically reads a headTailIndex value. -func (h *headTailIndex) load() headTailIndex { - return headTailIndex(atomic.Load64((*uint64)(h))) +func (h *atomicHeadTailIndex) load() headTailIndex { + return headTailIndex(h.u.Load()) } // cas atomically compares-and-swaps a headTailIndex value. -func (h *headTailIndex) cas(old, new headTailIndex) bool { - return atomic.Cas64((*uint64)(h), uint64(old), uint64(new)) +func (h *atomicHeadTailIndex) cas(old, new headTailIndex) bool { + return h.u.CompareAndSwap(uint64(old), uint64(new)) } // incHead atomically increments the head of a headTailIndex. -func (h *headTailIndex) incHead() headTailIndex { - return headTailIndex(atomic.Xadd64((*uint64)(h), (1 << 32))) +func (h *atomicHeadTailIndex) incHead() headTailIndex { + return headTailIndex(h.u.Add(1 << 32)) } // decHead atomically decrements the head of a headTailIndex. -func (h *headTailIndex) decHead() headTailIndex { - return headTailIndex(atomic.Xadd64((*uint64)(h), -(1 << 32))) +func (h *atomicHeadTailIndex) decHead() headTailIndex { + return headTailIndex(h.u.Add(-(1 << 32))) } // incTail atomically increments the tail of a headTailIndex. -func (h *headTailIndex) incTail() headTailIndex { - ht := headTailIndex(atomic.Xadd64((*uint64)(h), +1)) +func (h *atomicHeadTailIndex) incTail() headTailIndex { + ht := headTailIndex(h.u.Add(1)) // Check for overflow. if ht.tail() == 0 { print("runtime: head = ", ht.head(), ", tail = ", ht.tail(), "\n") @@ -349,6 +384,21 @@ func (h *headTailIndex) incTail() headTailIndex { } // reset clears the headTailIndex to (0, 0). -func (h *headTailIndex) reset() { - atomic.Store64((*uint64)(h), 0) +func (h *atomicHeadTailIndex) reset() { + h.u.Store(0) +} + +// atomicMSpanPointer is an atomic.Pointer[mspan]. Can't use generics because it's NotInHeap. +type atomicMSpanPointer struct { + p atomic.UnsafePointer +} + +// Load returns the *mspan. +func (p *atomicMSpanPointer) Load() *mspan { + return (*mspan)(p.p.Load()) +} + +// Store stores an *mspan. +func (p *atomicMSpanPointer) StoreNoWB(s *mspan) { + p.p.StoreNoWB(unsafe.Pointer(s)) } diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index eeb2a7b4bc73be..d4ef933611158b 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -8,55 +8,23 @@ package runtime import ( "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) -// Statistics. -// -// For detailed descriptions see the documentation for MemStats. -// Fields that differ from MemStats are further documented here. -// -// Many of these fields are updated on the fly, while others are only -// updated when updatememstats is called. type mstats struct { - // General statistics. - alloc uint64 // bytes allocated and not yet freed - total_alloc uint64 // bytes allocated (even if freed) - sys uint64 // bytes obtained from system (should be sum of xxx_sys below, no locking, approximate) - nlookup uint64 // number of pointer lookups (unused) - nmalloc uint64 // number of mallocs - nfree uint64 // number of frees - // Statistics about malloc heap. - // Updated atomically, or with the world stopped. - // - // Like MemStats, heap_sys and heap_inuse do not count memory - // in manually-managed spans. - heap_sys sysMemStat // virtual address space obtained from system for GC'd heap - heap_inuse uint64 // bytes in mSpanInUse spans - heap_released uint64 // bytes released to the os - - // heap_objects is not used by the runtime directly and instead - // computed on the fly by updatememstats. - heap_objects uint64 // total number of allocated objects + heapStats consistentHeapStats // Statistics about stacks. - stacks_inuse uint64 // bytes in manually-managed stack spans; computed by updatememstats - stacks_sys sysMemStat // only counts newosproc0 stack in mstats; differs from MemStats.StackSys + stacks_sys sysMemStat // only counts newosproc0 stack in mstats; differs from MemStats.StackSys // Statistics about allocation of low-level fixed-size structures. - // Protected by FixAlloc locks. - mspan_inuse uint64 // mspan structures mspan_sys sysMemStat - mcache_inuse uint64 // mcache structures mcache_sys sysMemStat buckhash_sys sysMemStat // profiling bucket hash table // Statistics about GC overhead. - gcWorkBufInUse uint64 // computed by updatememstats - gcProgPtrScalarBitsInUse uint64 // computed by updatememstats - gcMiscSys sysMemStat // updated atomically or during STW + gcMiscSys sysMemStat // updated atomically or during STW // Miscellaneous statistics. other_sys sysMemStat // updated atomically or during STW @@ -71,28 +39,11 @@ type mstats struct { numgc uint32 numforcedgc uint32 // number of user-forced GCs gc_cpu_fraction float64 // fraction of CPU time used by GC - enablegc bool - debuggc bool - - // Statistics about allocation size classes. - - by_size [_NumSizeClasses]struct { - size uint32 - nmalloc uint64 - nfree uint64 - } - - // Add an uint32 for even number of size classes to align below fields - // to 64 bits for atomic operations on 32 bit platforms. - _ [1 - _NumSizeClasses%2]uint32 last_gc_nanotime uint64 // last gc (monotonic time) - last_heap_inuse uint64 // heap_inuse at mark termination of the previous GC - - // heapStats is a set of statistics - heapStats consistentHeapStats + lastHeapInUse uint64 // heapInUse at mark termination of the previous GC - // _ uint32 // ensure gcPauseDist is aligned + enablegc bool // gcPauseDist represents the distribution of all GC-related // application pauses in the runtime. @@ -381,10 +332,6 @@ func init() { println(offset) throw("memstats.heapStats not aligned to 8 bytes") } - if offset := unsafe.Offsetof(memstats.gcPauseDist); offset%8 != 0 { - println(offset) - throw("memstats.gcPauseDist not aligned to 8 bytes") - } // Ensure the size of heapStatsDelta causes adjacent fields/slots (e.g. // [3]heapStatsDelta) to be 8-byte aligned. if size := unsafe.Sizeof(heapStatsDelta{}); size%8 != 0 { @@ -409,16 +356,141 @@ func ReadMemStats(m *MemStats) { startTheWorld() } +// readmemstats_m populates stats for internal runtime values. +// +// The world must be stopped. func readmemstats_m(stats *MemStats) { - updatememstats() - - stats.Alloc = memstats.alloc - stats.TotalAlloc = memstats.total_alloc - stats.Sys = memstats.sys - stats.Mallocs = memstats.nmalloc - stats.Frees = memstats.nfree - stats.HeapAlloc = memstats.alloc - stats.HeapSys = memstats.heap_sys.load() + assertWorldStopped() + + // Flush mcaches to mcentral before doing anything else. + // + // Flushing to the mcentral may in general cause stats to + // change as mcentral data structures are manipulated. + systemstack(flushallmcaches) + + // Calculate memory allocator stats. + // During program execution we only count number of frees and amount of freed memory. + // Current number of alive objects in the heap and amount of alive heap memory + // are calculated by scanning all spans. + // Total number of mallocs is calculated as number of frees plus number of alive objects. + // Similarly, total amount of allocated memory is calculated as amount of freed memory + // plus amount of alive heap memory. + + // Collect consistent stats, which are the source-of-truth in some cases. + var consStats heapStatsDelta + memstats.heapStats.unsafeRead(&consStats) + + // Collect large allocation stats. + totalAlloc := consStats.largeAlloc + nMalloc := consStats.largeAllocCount + totalFree := consStats.largeFree + nFree := consStats.largeFreeCount + + // Collect per-sizeclass stats. + var bySize [_NumSizeClasses]struct { + Size uint32 + Mallocs uint64 + Frees uint64 + } + for i := range bySize { + bySize[i].Size = uint32(class_to_size[i]) + + // Malloc stats. + a := consStats.smallAllocCount[i] + totalAlloc += a * uint64(class_to_size[i]) + nMalloc += a + bySize[i].Mallocs = a + + // Free stats. + f := consStats.smallFreeCount[i] + totalFree += f * uint64(class_to_size[i]) + nFree += f + bySize[i].Frees = f + } + + // Account for tiny allocations. + // For historical reasons, MemStats includes tiny allocations + // in both the total free and total alloc count. This double-counts + // memory in some sense because their tiny allocation block is also + // counted. Tracking the lifetime of individual tiny allocations is + // currently not done because it would be too expensive. + nFree += consStats.tinyAllocCount + nMalloc += consStats.tinyAllocCount + + // Calculate derived stats. + + stackInUse := uint64(consStats.inStacks) + gcWorkBufInUse := uint64(consStats.inWorkBufs) + gcProgPtrScalarBitsInUse := uint64(consStats.inPtrScalarBits) + + totalMapped := gcController.heapInUse.load() + gcController.heapFree.load() + gcController.heapReleased.load() + + memstats.stacks_sys.load() + memstats.mspan_sys.load() + memstats.mcache_sys.load() + + memstats.buckhash_sys.load() + memstats.gcMiscSys.load() + memstats.other_sys.load() + + stackInUse + gcWorkBufInUse + gcProgPtrScalarBitsInUse + + heapGoal := gcController.heapGoal() + + // The world is stopped, so the consistent stats (after aggregation) + // should be identical to some combination of memstats. In particular: + // + // * memstats.heapInUse == inHeap + // * memstats.heapReleased == released + // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits + // * memstats.totalAlloc == totalAlloc + // * memstats.totalFree == totalFree + // + // Check if that's actually true. + // + // TODO(mknyszek): Maybe don't throw here. It would be bad if a + // bug in otherwise benign accounting caused the whole application + // to crash. + if gcController.heapInUse.load() != uint64(consStats.inHeap) { + print("runtime: heapInUse=", gcController.heapInUse.load(), "\n") + print("runtime: consistent value=", consStats.inHeap, "\n") + throw("heapInUse and consistent stats are not equal") + } + if gcController.heapReleased.load() != uint64(consStats.released) { + print("runtime: heapReleased=", gcController.heapReleased.load(), "\n") + print("runtime: consistent value=", consStats.released, "\n") + throw("heapReleased and consistent stats are not equal") + } + heapRetained := gcController.heapInUse.load() + gcController.heapFree.load() + consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) + if heapRetained != consRetained { + print("runtime: global value=", heapRetained, "\n") + print("runtime: consistent value=", consRetained, "\n") + throw("measures of the retained heap are not equal") + } + if gcController.totalAlloc.Load() != totalAlloc { + print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n") + print("runtime: consistent value=", totalAlloc, "\n") + throw("totalAlloc and consistent stats are not equal") + } + if gcController.totalFree.Load() != totalFree { + print("runtime: totalFree=", gcController.totalFree.Load(), "\n") + print("runtime: consistent value=", totalFree, "\n") + throw("totalFree and consistent stats are not equal") + } + // Also check that mappedReady lines up with totalMapped - released. + // This isn't really the same type of "make sure consistent stats line up" situation, + // but this is an opportune time to check. + if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) { + print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n") + print("runtime: totalMapped=", totalMapped, "\n") + print("runtime: released=", uint64(consStats.released), "\n") + print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n") + throw("mappedReady and other memstats are not equal") + } + + // We've calculated all the values we need. Now, populate stats. + + stats.Alloc = totalAlloc - totalFree + stats.TotalAlloc = totalAlloc + stats.Sys = totalMapped + stats.Mallocs = nMalloc + stats.Frees = nFree + stats.HeapAlloc = totalAlloc - totalFree + stats.HeapSys = gcController.heapInUse.load() + gcController.heapFree.load() + gcController.heapReleased.load() // By definition, HeapIdle is memory that was mapped // for the heap but is not currently used to hold heap // objects. It also specifically is memory that can be @@ -426,34 +498,34 @@ func readmemstats_m(stats *MemStats) { // is subtracted out of HeapSys before it makes that // transition. Put another way: // - // heap_sys = bytes allocated from the OS for the heap - bytes ultimately used for non-heap purposes - // heap_idle = bytes allocated from the OS for the heap - bytes ultimately used for any purpose + // HeapSys = bytes allocated from the OS for the heap - bytes ultimately used for non-heap purposes + // HeapIdle = bytes allocated from the OS for the heap - bytes ultimately used for any purpose // // or // - // heap_sys = sys - stacks_inuse - gcWorkBufInUse - gcProgPtrScalarBitsInUse - // heap_idle = sys - stacks_inuse - gcWorkBufInUse - gcProgPtrScalarBitsInUse - heap_inuse + // HeapSys = sys - stacks_inuse - gcWorkBufInUse - gcProgPtrScalarBitsInUse + // HeapIdle = sys - stacks_inuse - gcWorkBufInUse - gcProgPtrScalarBitsInUse - heapInUse // - // => heap_idle = heap_sys - heap_inuse - stats.HeapIdle = memstats.heap_sys.load() - memstats.heap_inuse - stats.HeapInuse = memstats.heap_inuse - stats.HeapReleased = memstats.heap_released - stats.HeapObjects = memstats.heap_objects - stats.StackInuse = memstats.stacks_inuse + // => HeapIdle = HeapSys - heapInUse = heapFree + heapReleased + stats.HeapIdle = gcController.heapFree.load() + gcController.heapReleased.load() + stats.HeapInuse = gcController.heapInUse.load() + stats.HeapReleased = gcController.heapReleased.load() + stats.HeapObjects = nMalloc - nFree + stats.StackInuse = stackInUse // memstats.stacks_sys is only memory mapped directly for OS stacks. // Add in heap-allocated stack memory for user consumption. - stats.StackSys = memstats.stacks_inuse + memstats.stacks_sys.load() - stats.MSpanInuse = memstats.mspan_inuse + stats.StackSys = stackInUse + memstats.stacks_sys.load() + stats.MSpanInuse = uint64(mheap_.spanalloc.inuse) stats.MSpanSys = memstats.mspan_sys.load() - stats.MCacheInuse = memstats.mcache_inuse + stats.MCacheInuse = uint64(mheap_.cachealloc.inuse) stats.MCacheSys = memstats.mcache_sys.load() stats.BuckHashSys = memstats.buckhash_sys.load() // MemStats defines GCSys as an aggregate of all memory related // to the memory management system, but we track this memory // at a more granular level in the runtime. - stats.GCSys = memstats.gcMiscSys.load() + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse + stats.GCSys = memstats.gcMiscSys.load() + gcWorkBufInUse + gcProgPtrScalarBitsInUse stats.OtherSys = memstats.other_sys.load() - stats.NextGC = gcController.heapGoal + stats.NextGC = heapGoal stats.LastGC = memstats.last_gc_unix stats.PauseTotalNs = memstats.pause_total_ns stats.PauseNs = memstats.pause_ns @@ -463,23 +535,11 @@ func readmemstats_m(stats *MemStats) { stats.GCCPUFraction = memstats.gc_cpu_fraction stats.EnableGC = true - // Handle BySize. Copy N values, where N is - // the minimum of the lengths of the two arrays. - // Unfortunately copy() won't work here because - // the arrays have different structs. - // - // TODO(mknyszek): Consider renaming the fields - // of by_size's elements to align so we can use - // the copy built-in. - bySizeLen := len(stats.BySize) - if l := len(memstats.by_size); l < bySizeLen { - bySizeLen = l - } - for i := 0; i < bySizeLen; i++ { - stats.BySize[i].Size = memstats.by_size[i].size - stats.BySize[i].Mallocs = memstats.by_size[i].nmalloc - stats.BySize[i].Frees = memstats.by_size[i].nfree - } + // stats.BySize and bySize might not match in length. + // That's OK, stats.BySize cannot change due to backwards + // compatibility issues. copy will copy the minimum amount + // of values between the two of them. + copy(stats.BySize[:], bySize[:]) } //go:linkname readGCStats runtime/debug.readGCStats @@ -491,6 +551,7 @@ func readGCStats(pauses *[]uint64) { // readGCStats_m must be called on the system stack because it acquires the heap // lock. See mheap for details. +// //go:systemstack func readGCStats_m(pauses *[]uint64) { p := *pauses @@ -525,113 +586,6 @@ func readGCStats_m(pauses *[]uint64) { *pauses = p[:n+n+3] } -// Updates the memstats structure. -// -// The world must be stopped. -// -//go:nowritebarrier -func updatememstats() { - assertWorldStopped() - - // Flush mcaches to mcentral before doing anything else. - // - // Flushing to the mcentral may in general cause stats to - // change as mcentral data structures are manipulated. - systemstack(flushallmcaches) - - memstats.mcache_inuse = uint64(mheap_.cachealloc.inuse) - memstats.mspan_inuse = uint64(mheap_.spanalloc.inuse) - memstats.sys = memstats.heap_sys.load() + memstats.stacks_sys.load() + memstats.mspan_sys.load() + - memstats.mcache_sys.load() + memstats.buckhash_sys.load() + memstats.gcMiscSys.load() + - memstats.other_sys.load() - - // Calculate memory allocator stats. - // During program execution we only count number of frees and amount of freed memory. - // Current number of alive objects in the heap and amount of alive heap memory - // are calculated by scanning all spans. - // Total number of mallocs is calculated as number of frees plus number of alive objects. - // Similarly, total amount of allocated memory is calculated as amount of freed memory - // plus amount of alive heap memory. - memstats.alloc = 0 - memstats.total_alloc = 0 - memstats.nmalloc = 0 - memstats.nfree = 0 - for i := 0; i < len(memstats.by_size); i++ { - memstats.by_size[i].nmalloc = 0 - memstats.by_size[i].nfree = 0 - } - // Collect consistent stats, which are the source-of-truth in the some cases. - var consStats heapStatsDelta - memstats.heapStats.unsafeRead(&consStats) - - // Collect large allocation stats. - totalAlloc := uint64(consStats.largeAlloc) - memstats.nmalloc += uint64(consStats.largeAllocCount) - totalFree := uint64(consStats.largeFree) - memstats.nfree += uint64(consStats.largeFreeCount) - - // Collect per-sizeclass stats. - for i := 0; i < _NumSizeClasses; i++ { - // Malloc stats. - a := uint64(consStats.smallAllocCount[i]) - totalAlloc += a * uint64(class_to_size[i]) - memstats.nmalloc += a - memstats.by_size[i].nmalloc = a - - // Free stats. - f := uint64(consStats.smallFreeCount[i]) - totalFree += f * uint64(class_to_size[i]) - memstats.nfree += f - memstats.by_size[i].nfree = f - } - - // Account for tiny allocations. - memstats.nfree += uint64(consStats.tinyAllocCount) - memstats.nmalloc += uint64(consStats.tinyAllocCount) - - // Calculate derived stats. - memstats.total_alloc = totalAlloc - memstats.alloc = totalAlloc - totalFree - memstats.heap_objects = memstats.nmalloc - memstats.nfree - - memstats.stacks_inuse = uint64(consStats.inStacks) - memstats.gcWorkBufInUse = uint64(consStats.inWorkBufs) - memstats.gcProgPtrScalarBitsInUse = uint64(consStats.inPtrScalarBits) - - // We also count stacks_inuse, gcWorkBufInUse, and gcProgPtrScalarBitsInUse as sys memory. - memstats.sys += memstats.stacks_inuse + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse - - // The world is stopped, so the consistent stats (after aggregation) - // should be identical to some combination of memstats. In particular: - // - // * heap_inuse == inHeap - // * heap_released == released - // * heap_sys - heap_released == committed - inStacks - inWorkBufs - inPtrScalarBits - // - // Check if that's actually true. - // - // TODO(mknyszek): Maybe don't throw here. It would be bad if a - // bug in otherwise benign accounting caused the whole application - // to crash. - if memstats.heap_inuse != uint64(consStats.inHeap) { - print("runtime: heap_inuse=", memstats.heap_inuse, "\n") - print("runtime: consistent value=", consStats.inHeap, "\n") - throw("heap_inuse and consistent stats are not equal") - } - if memstats.heap_released != uint64(consStats.released) { - print("runtime: heap_released=", memstats.heap_released, "\n") - print("runtime: consistent value=", consStats.released, "\n") - throw("heap_released and consistent stats are not equal") - } - globalRetained := memstats.heap_sys.load() - memstats.heap_released - consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) - if globalRetained != consRetained { - print("runtime: global value=", globalRetained, "\n") - print("runtime: consistent value=", consRetained, "\n") - throw("measures of the retained heap are not equal") - } -} - // flushmcache flushes the mcache of allp[i]. // // The world must be stopped. @@ -670,6 +624,7 @@ type sysMemStat uint64 // load atomically reads the value of the stat. // // Must be nosplit as it is called in runtime initialization, e.g. newosproc0. +// //go:nosplit func (s *sysMemStat) load() uint64 { return atomic.Load64((*uint64)(s)) @@ -678,11 +633,9 @@ func (s *sysMemStat) load() uint64 { // add atomically adds the sysMemStat by n. // // Must be nosplit as it is called in runtime initialization, e.g. newosproc0. +// //go:nosplit func (s *sysMemStat) add(n int64) { - if s == nil { - return - } val := atomic.Xadd64((*uint64)(s), n) if (n > 0 && int64(val) < n) || (n < 0 && int64(val)+n < n) { print("runtime: val=", val, " n=", n, "\n") @@ -703,17 +656,20 @@ type heapStatsDelta struct { inPtrScalarBits int64 // byte delta of memory reserved for unrolled GC prog bits // Allocator stats. - tinyAllocCount uintptr // number of tiny allocations - largeAlloc uintptr // bytes allocated for large objects - largeAllocCount uintptr // number of large object allocations - smallAllocCount [_NumSizeClasses]uintptr // number of allocs for small objects - largeFree uintptr // bytes freed for large objects (>maxSmallSize) - largeFreeCount uintptr // number of frees for large objects (>maxSmallSize) - smallFreeCount [_NumSizeClasses]uintptr // number of frees for small objects (<=maxSmallSize) - - // Add a uint32 to ensure this struct is a multiple of 8 bytes in size. - // Only necessary on 32-bit platforms. - _ [(sys.PtrSize / 4) % 2]uint32 + // + // These are all uint64 because they're cumulative, and could quickly wrap + // around otherwise. + tinyAllocCount uint64 // number of tiny allocations + largeAlloc uint64 // bytes allocated for large objects + largeAllocCount uint64 // number of large object allocations + smallAllocCount [_NumSizeClasses]uint64 // number of allocs for small objects + largeFree uint64 // bytes freed for large objects (>maxSmallSize) + largeFreeCount uint64 // number of frees for large objects (>maxSmallSize) + smallFreeCount [_NumSizeClasses]uint64 // number of frees for small objects (<=maxSmallSize) + + // NOTE: This struct must be a multiple of 8 bytes in size because it + // is stored in an array. If it's not, atomic accesses to the above + // fields may be unaligned and fail on 32-bit platforms. } // merge adds in the deltas from b into a. @@ -771,8 +727,7 @@ type consistentHeapStats struct { // gen represents the current index into which writers // are writing, and can take on the value of 0, 1, or 2. - // This value is updated atomically. - gen uint32 + gen atomic.Uint32 // noPLock is intended to provide mutual exclusion for updating // stats when no P is available. It does not block other writers @@ -790,10 +745,18 @@ type consistentHeapStats struct { // // The caller's P must not change between acquire and // release. This also means that the caller should not -// acquire a P or release its P in between. +// acquire a P or release its P in between. A P also must +// not acquire a given consistentHeapStats if it hasn't +// yet released it. +// +// nosplit because a stack growth in this function could +// lead to a stack allocation that could reenter the +// function. +// +//go:nosplit func (m *consistentHeapStats) acquire() *heapStatsDelta { if pp := getg().m.p.ptr(); pp != nil { - seq := atomic.Xadd(&pp.statsSeq, 1) + seq := pp.statsSeq.Add(1) if seq%2 == 0 { // Should have been incremented to odd. print("runtime: seq=", seq, "\n") @@ -802,7 +765,7 @@ func (m *consistentHeapStats) acquire() *heapStatsDelta { } else { lock(&m.noPLock) } - gen := atomic.Load(&m.gen) % 3 + gen := m.gen.Load() % 3 return &m.stats[gen] } @@ -814,9 +777,15 @@ func (m *consistentHeapStats) acquire() *heapStatsDelta { // The caller's P must not change between acquire and // release. This also means that the caller should not // acquire a P or release its P in between. +// +// nosplit because a stack growth in this function could +// lead to a stack allocation that causes another acquire +// before this operation has completed. +// +//go:nosplit func (m *consistentHeapStats) release() { if pp := getg().m.p.ptr(); pp != nil { - seq := atomic.Xadd(&pp.statsSeq, 1) + seq := pp.statsSeq.Add(1) if seq%2 != 0 { // Should have been incremented to even. print("runtime: seq=", seq, "\n") @@ -867,7 +836,7 @@ func (m *consistentHeapStats) read(out *heapStatsDelta) { // Get the current generation. We can be confident that this // will not change since read is serialized and is the only // one that modifies currGen. - currGen := atomic.Load(&m.gen) + currGen := m.gen.Load() prevGen := currGen - 1 if currGen == 0 { prevGen = 2 @@ -882,7 +851,7 @@ func (m *consistentHeapStats) read(out *heapStatsDelta) { // // This exchange is safe to do because we won't race // with anyone else trying to update this value. - atomic.Xchg(&m.gen, (currGen+1)%3) + m.gen.Swap((currGen + 1) % 3) // Allow P-less writers to continue. They'll be writing to the // next generation now. @@ -890,7 +859,7 @@ func (m *consistentHeapStats) read(out *heapStatsDelta) { for _, p := range allp { // Spin until there are no more writers. - for atomic.Load(&p.statsSeq)%2 != 0 { + for p.statsSeq.Load()%2 != 0 { } } diff --git a/src/runtime/mwbbuf.go b/src/runtime/mwbbuf.go index 6efc00007d5f0e..3b7cbf8f1fb767 100644 --- a/src/runtime/mwbbuf.go +++ b/src/runtime/mwbbuf.go @@ -23,8 +23,8 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -116,11 +116,11 @@ func (b *wbBuf) empty() bool { // putFast adds old and new to the write barrier buffer and returns // false if a flush is necessary. Callers should use this as: // -// buf := &getg().m.p.ptr().wbBuf -// if !buf.putFast(old, new) { -// wbBufFlush(...) -// } -// ... actual memory write ... +// buf := &getg().m.p.ptr().wbBuf +// if !buf.putFast(old, new) { +// wbBufFlush(...) +// } +// ... actual memory write ... // // The arguments to wbBufFlush depend on whether the caller is doing // its own cgo pointer checks. If it is, then this can be @@ -145,7 +145,7 @@ func (b *wbBuf) putFast(old, new uintptr) bool { p := (*[2]uintptr)(unsafe.Pointer(b.next)) p[0] = old p[1] = new - b.next += 2 * sys.PtrSize + b.next += 2 * goarch.PtrSize return b.next != b.end } @@ -212,22 +212,22 @@ func wbBufFlush(dst *uintptr, src uintptr) { // //go:nowritebarrierrec //go:systemstack -func wbBufFlush1(_p_ *p) { +func wbBufFlush1(pp *p) { // Get the buffered pointers. - start := uintptr(unsafe.Pointer(&_p_.wbBuf.buf[0])) - n := (_p_.wbBuf.next - start) / unsafe.Sizeof(_p_.wbBuf.buf[0]) - ptrs := _p_.wbBuf.buf[:n] + start := uintptr(unsafe.Pointer(&pp.wbBuf.buf[0])) + n := (pp.wbBuf.next - start) / unsafe.Sizeof(pp.wbBuf.buf[0]) + ptrs := pp.wbBuf.buf[:n] // Poison the buffer to make extra sure nothing is enqueued // while we're processing the buffer. - _p_.wbBuf.next = 0 + pp.wbBuf.next = 0 if useCheckmark { // Slow path for checkmark mode. for _, ptr := range ptrs { shade(ptr) } - _p_.wbBuf.reset() + pp.wbBuf.reset() return } @@ -245,7 +245,7 @@ func wbBufFlush1(_p_ *p) { // could track whether any un-shaded goroutine has used the // buffer, or just track globally whether there are any // un-shaded stacks and flush after each stack scan. - gcw := &_p_.gcw + gcw := &pp.gcw pos := 0 for _, ptr := range ptrs { if ptr < minLegalPointer { @@ -286,5 +286,5 @@ func wbBufFlush1(_p_ *p) { // Enqueue the greyed objects. gcw.putBatch(ptrs[:pos]) - _p_.wbBuf.reset() + pp.wbBuf.reset() } diff --git a/src/runtime/nbpipe_fcntl_libc_test.go b/src/runtime/nbpipe_fcntl_libc_test.go index 076a722eb73eba..170245defec8f6 100644 --- a/src/runtime/nbpipe_fcntl_libc_test.go +++ b/src/runtime/nbpipe_fcntl_libc_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || solaris -// +build aix darwin solaris +//go:build aix || darwin || (openbsd && !mips64) || solaris package runtime_test diff --git a/src/runtime/nbpipe_fcntl_unix_test.go b/src/runtime/nbpipe_fcntl_unix_test.go index 6d5e4ff0215a08..b7252ea9fafec3 100644 --- a/src/runtime/nbpipe_fcntl_unix_test.go +++ b/src/runtime/nbpipe_fcntl_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd +//go:build dragonfly || freebsd || linux || netbsd || (openbsd && mips64) package runtime_test diff --git a/src/runtime/nbpipe_pipe.go b/src/runtime/nbpipe_pipe.go index b17257e9ec3649..408e1ec410fec4 100644 --- a/src/runtime/nbpipe_pipe.go +++ b/src/runtime/nbpipe_pipe.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin -// +build aix darwin package runtime diff --git a/src/runtime/nbpipe_pipe2.go b/src/runtime/nbpipe_pipe2.go index f22b2b591fd592..22d60b4a63205c 100644 --- a/src/runtime/nbpipe_pipe2.go +++ b/src/runtime/nbpipe_pipe2.go @@ -3,21 +3,9 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build dragonfly freebsd linux netbsd openbsd solaris package runtime func nonblockingPipe() (r, w int32, errno int32) { - r, w, errno = pipe2(_O_NONBLOCK | _O_CLOEXEC) - if errno == -_ENOSYS { - r, w, errno = pipe() - if errno != 0 { - return -1, -1, errno - } - closeonexec(r) - setNonblock(r) - closeonexec(w) - setNonblock(w) - } - return r, w, errno + return pipe2(_O_NONBLOCK | _O_CLOEXEC) } diff --git a/src/runtime/nbpipe_pipe_test.go b/src/runtime/nbpipe_pipe_test.go new file mode 100644 index 00000000000000..c8cb3cf6914786 --- /dev/null +++ b/src/runtime/nbpipe_pipe_test.go @@ -0,0 +1,38 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin + +package runtime_test + +import ( + "runtime" + "syscall" + "testing" +) + +func TestSetNonblock(t *testing.T) { + t.Parallel() + + r, w, errno := runtime.Pipe() + if errno != 0 { + t.Fatal(syscall.Errno(errno)) + } + defer func() { + runtime.Close(r) + runtime.Close(w) + }() + + checkIsPipe(t, r, w) + + runtime.SetNonblock(r) + runtime.SetNonblock(w) + checkNonblocking(t, r, "reader") + checkNonblocking(t, w, "writer") + + runtime.Closeonexec(r) + runtime.Closeonexec(w) + checkCloseonexec(t, r, "reader") + checkCloseonexec(t, w, "writer") +} diff --git a/src/runtime/nbpipe_test.go b/src/runtime/nbpipe_test.go index 1d6a9b525c8850..0b0f64d0764375 100644 --- a/src/runtime/nbpipe_test.go +++ b/src/runtime/nbpipe_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package runtime_test @@ -67,28 +66,3 @@ func checkCloseonexec(t *testing.T, fd int32, name string) { t.Errorf("FD_CLOEXEC not set in %s flags %#x", name, flags) } } - -func TestSetNonblock(t *testing.T) { - t.Parallel() - - r, w, errno := runtime.Pipe() - if errno != 0 { - t.Fatal(syscall.Errno(errno)) - } - defer func() { - runtime.Close(r) - runtime.Close(w) - }() - - checkIsPipe(t, r, w) - - runtime.SetNonblock(r) - runtime.SetNonblock(w) - checkNonblocking(t, r, "reader") - checkNonblocking(t, w, "writer") - - runtime.Closeonexec(r) - runtime.Closeonexec(w) - checkCloseonexec(t, r, "reader") - checkCloseonexec(t, w, "writer") -} diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index 6c26fdbbeb8765..5ac1f37048c080 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows +//go:build unix || (js && wasm) || windows package runtime import ( "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -48,17 +48,19 @@ const ( // pollDesc contains 2 binary semaphores, rg and wg, to park reader and writer // goroutines respectively. The semaphore can be in the following states: -// pdReady - io readiness notification is pending; -// a goroutine consumes the notification by changing the state to nil. -// pdWait - a goroutine prepares to park on the semaphore, but not yet parked; -// the goroutine commits to park by changing the state to G pointer, -// or, alternatively, concurrent io notification changes the state to pdReady, -// or, alternatively, concurrent timeout/close changes the state to nil. -// G pointer - the goroutine is blocked on the semaphore; -// io notification or timeout/close changes the state to pdReady or nil respectively -// and unparks the goroutine. -// nil - none of the above. +// +// pdReady - io readiness notification is pending; +// a goroutine consumes the notification by changing the state to pdNil. +// pdWait - a goroutine prepares to park on the semaphore, but not yet parked; +// the goroutine commits to park by changing the state to G pointer, +// or, alternatively, concurrent io notification changes the state to pdReady, +// or, alternatively, concurrent timeout/close changes the state to pdNil. +// G pointer - the goroutine is blocked on the semaphore; +// io notification or timeout/close changes the state to pdReady or pdNil respectively +// and unparks the goroutine. +// pdNil - none of the above. const ( + pdNil uintptr = 0 pdReady uintptr = 1 pdWait uintptr = 2 ) @@ -68,34 +70,102 @@ const pollBlockSize = 4 * 1024 // Network poller descriptor. // // No heap pointers. -// -//go:notinheap type pollDesc struct { + _ sys.NotInHeap link *pollDesc // in pollcache, protected by pollcache.lock + fd uintptr // constant for pollDesc usage lifetime + + // atomicInfo holds bits from closing, rd, and wd, + // which are only ever written while holding the lock, + // summarized for use by netpollcheckerr, + // which cannot acquire the lock. + // After writing these fields under lock in a way that + // might change the summary, code must call publishInfo + // before releasing the lock. + // Code that changes fields and then calls netpollunblock + // (while still holding the lock) must call publishInfo + // before calling netpollunblock, because publishInfo is what + // stops netpollblock from blocking anew + // (by changing the result of netpollcheckerr). + // atomicInfo also holds the eventErr bit, + // recording whether a poll event on the fd got an error; + // atomicInfo is the only source of truth for that bit. + atomicInfo atomic.Uint32 // atomic pollInfo + + // rg, wg are accessed atomically and hold g pointers. + // (Using atomic.Uintptr here is similar to using guintptr elsewhere.) + rg atomic.Uintptr // pdReady, pdWait, G waiting for read or pdNil + wg atomic.Uintptr // pdReady, pdWait, G waiting for write or pdNil - // The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations. - // This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime. - // pollReset, pollWait, pollWaitCanceled and runtime·netpollready (IO readiness notification) - // proceed w/o taking the lock. So closing, everr, rg, rd, wg and wd are manipulated - // in a lock-free way by all operations. - // NOTE(dvyukov): the following code uses uintptr to store *g (rg/wg), - // that will blow up when GC starts moving objects. lock mutex // protects the following fields - fd uintptr closing bool - everr bool // marks event scanning error happened user uint32 // user settable cookie rseq uintptr // protects from stale read timers - rg uintptr // pdReady, pdWait, G waiting for read or nil rt timer // read deadline timer (set if rt.f != nil) - rd int64 // read deadline + rd int64 // read deadline (a nanotime in the future, -1 when expired) wseq uintptr // protects from stale write timers - wg uintptr // pdReady, pdWait, G waiting for write or nil wt timer // write deadline timer - wd int64 // write deadline + wd int64 // write deadline (a nanotime in the future, -1 when expired) self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg. } +// pollInfo is the bits needed by netpollcheckerr, stored atomically, +// mostly duplicating state that is manipulated under lock in pollDesc. +// The one exception is the pollEventErr bit, which is maintained only +// in the pollInfo. +type pollInfo uint32 + +const ( + pollClosing = 1 << iota + pollEventErr + pollExpiredReadDeadline + pollExpiredWriteDeadline +) + +func (i pollInfo) closing() bool { return i&pollClosing != 0 } +func (i pollInfo) eventErr() bool { return i&pollEventErr != 0 } +func (i pollInfo) expiredReadDeadline() bool { return i&pollExpiredReadDeadline != 0 } +func (i pollInfo) expiredWriteDeadline() bool { return i&pollExpiredWriteDeadline != 0 } + +// info returns the pollInfo corresponding to pd. +func (pd *pollDesc) info() pollInfo { + return pollInfo(pd.atomicInfo.Load()) +} + +// publishInfo updates pd.atomicInfo (returned by pd.info) +// using the other values in pd. +// It must be called while holding pd.lock, +// and it must be called after changing anything +// that might affect the info bits. +// In practice this means after changing closing +// or changing rd or wd from < 0 to >= 0. +func (pd *pollDesc) publishInfo() { + var info uint32 + if pd.closing { + info |= pollClosing + } + if pd.rd < 0 { + info |= pollExpiredReadDeadline + } + if pd.wd < 0 { + info |= pollExpiredWriteDeadline + } + + // Set all of x except the pollEventErr bit. + x := pd.atomicInfo.Load() + for !pd.atomicInfo.CompareAndSwap(x, (x&pollEventErr)|info) { + x = pd.atomicInfo.Load() + } +} + +// setEventErr sets the result of pd.info().eventErr() to b. +func (pd *pollDesc) setEventErr(b bool) { + x := pd.atomicInfo.Load() + for (x&pollEventErr != 0) != b && !pd.atomicInfo.CompareAndSwap(x, x^pollEventErr) { + x = pd.atomicInfo.Load() + } +} + type pollCache struct { lock mutex first *pollDesc @@ -108,10 +178,10 @@ type pollCache struct { var ( netpollInitLock mutex - netpollInited uint32 + netpollInited atomic.Uint32 pollcache pollCache - netpollWaiters uint32 + netpollWaiters atomic.Uint32 ) //go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit @@ -120,19 +190,19 @@ func poll_runtime_pollServerInit() { } func netpollGenericInit() { - if atomic.Load(&netpollInited) == 0 { + if netpollInited.Load() == 0 { lockInit(&netpollInitLock, lockRankNetpollInit) lock(&netpollInitLock) - if netpollInited == 0 { + if netpollInited.Load() == 0 { netpollinit() - atomic.Store(&netpollInited, 1) + netpollInited.Store(1) } unlock(&netpollInitLock) } } func netpollinited() bool { - return atomic.Load(&netpollInited) != 0 + return netpollInited.Load() != 0 } //go:linkname poll_runtime_isPollServerDescriptor internal/poll.runtime_isPollServerDescriptor @@ -147,22 +217,25 @@ func poll_runtime_isPollServerDescriptor(fd uintptr) bool { func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { pd := pollcache.alloc() lock(&pd.lock) - if pd.wg != 0 && pd.wg != pdReady { + wg := pd.wg.Load() + if wg != pdNil && wg != pdReady { throw("runtime: blocked write on free polldesc") } - if pd.rg != 0 && pd.rg != pdReady { + rg := pd.rg.Load() + if rg != pdNil && rg != pdReady { throw("runtime: blocked read on free polldesc") } pd.fd = fd pd.closing = false - pd.everr = false + pd.setEventErr(false) pd.rseq++ - pd.rg = 0 + pd.rg.Store(pdNil) pd.rd = 0 pd.wseq++ - pd.wg = 0 + pd.wg.Store(pdNil) pd.wd = 0 pd.self = pd + pd.publishInfo() unlock(&pd.lock) errno := netpollopen(fd, pd) @@ -178,10 +251,12 @@ func poll_runtime_pollClose(pd *pollDesc) { if !pd.closing { throw("runtime: close polldesc w/o unblock") } - if pd.wg != 0 && pd.wg != pdReady { + wg := pd.wg.Load() + if wg != pdNil && wg != pdReady { throw("runtime: blocked write on closing polldesc") } - if pd.rg != 0 && pd.rg != pdReady { + rg := pd.rg.Load() + if rg != pdNil && rg != pdReady { throw("runtime: blocked read on closing polldesc") } netpollclose(pd.fd) @@ -198,6 +273,7 @@ func (c *pollCache) free(pd *pollDesc) { // poll_runtime_pollReset, which is internal/poll.runtime_pollReset, // prepares a descriptor for polling in mode, which is 'r' or 'w'. // This returns an error code; the codes are defined above. +// //go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset func poll_runtime_pollReset(pd *pollDesc, mode int) int { errcode := netpollcheckerr(pd, int32(mode)) @@ -205,9 +281,9 @@ func poll_runtime_pollReset(pd *pollDesc, mode int) int { return errcode } if mode == 'r' { - pd.rg = 0 + pd.rg.Store(pdNil) } else if mode == 'w' { - pd.wg = 0 + pd.wg.Store(pdNil) } return pollNoError } @@ -216,6 +292,7 @@ func poll_runtime_pollReset(pd *pollDesc, mode int) int { // waits for a descriptor to be ready for reading or writing, // according to mode, which is 'r' or 'w'. // This returns an error code; the codes are defined above. +// //go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait func poll_runtime_pollWait(pd *pollDesc, mode int) int { errcode := netpollcheckerr(pd, int32(mode)) @@ -269,6 +346,7 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { if mode == 'w' || mode == 'r'+'w' { pd.wd = d } + pd.publishInfo() combo := pd.rd > 0 && pd.rd == pd.wd rtf := netpollReadDeadline if combo { @@ -310,15 +388,13 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) { } } // If we set the new deadline in the past, unblock currently pending IO if any. + // Note that pd.publishInfo has already been called, above, immediately after modifying rd and wd. var rg, wg *g - if pd.rd < 0 || pd.wd < 0 { - atomic.StorepNoWB(noescape(unsafe.Pointer(&wg)), nil) // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock - if pd.rd < 0 { - rg = netpollunblock(pd, 'r', false) - } - if pd.wd < 0 { - wg = netpollunblock(pd, 'w', false) - } + if pd.rd < 0 { + rg = netpollunblock(pd, 'r', false) + } + if pd.wd < 0 { + wg = netpollunblock(pd, 'w', false) } unlock(&pd.lock) if rg != nil { @@ -339,7 +415,7 @@ func poll_runtime_pollUnblock(pd *pollDesc) { pd.rseq++ pd.wseq++ var rg, wg *g - atomic.StorepNoWB(noescape(unsafe.Pointer(&rg)), nil) // full memory barrier between store to closing and read of rg/wg in netpollunblock + pd.publishInfo() rg = netpollunblock(pd, 'r', false) wg = netpollunblock(pd, 'w', false) if pd.rt.f != nil { @@ -366,6 +442,7 @@ func poll_runtime_pollUnblock(pd *pollDesc) { // whether the fd is ready for reading or writing or both. // // This may run while the world is stopped, so write barriers are not allowed. +// //go:nowritebarrier func netpollready(toRun *gList, pd *pollDesc, mode int32) { var rg, wg *g @@ -384,16 +461,17 @@ func netpollready(toRun *gList, pd *pollDesc, mode int32) { } func netpollcheckerr(pd *pollDesc, mode int32) int { - if pd.closing { + info := pd.info() + if info.closing() { return pollErrClosing } - if (mode == 'r' && pd.rd < 0) || (mode == 'w' && pd.wd < 0) { + if (mode == 'r' && info.expiredReadDeadline()) || (mode == 'w' && info.expiredWriteDeadline()) { return pollErrTimeout } // Report an event scanning error only on a read event. // An error on a write event will be captured in a subsequent // write call that is able to report a more specific error. - if mode == 'r' && pd.everr { + if mode == 'r' && info.eventErr() { return pollErrNotPollable } return pollNoError @@ -405,18 +483,20 @@ func netpollblockcommit(gp *g, gpp unsafe.Pointer) bool { // Bump the count of goroutines waiting for the poller. // The scheduler uses this to decide whether to block // waiting for the poller if there is nothing else to do. - atomic.Xadd(&netpollWaiters, 1) + netpollWaiters.Add(1) } return r } func netpollgoready(gp *g, traceskip int) { - atomic.Xadd(&netpollWaiters, -1) + netpollWaiters.Add(-1) goready(gp, traceskip+1) } -// returns true if IO is ready, or false if timedout or closed +// returns true if IO is ready, or false if timed out or closed // waitio - wait only for completed IO, ignore errors +// Concurrent calls to netpollblock in the same mode are forbidden, as pollDesc +// can hold only a single waiting goroutine for each mode. func netpollblock(pd *pollDesc, mode int32, waitio bool) bool { gpp := &pd.rg if mode == 'w' { @@ -425,27 +505,29 @@ func netpollblock(pd *pollDesc, mode int32, waitio bool) bool { // set the gpp semaphore to pdWait for { - old := *gpp - if old == pdReady { - *gpp = 0 + // Consume notification if already ready. + if gpp.CompareAndSwap(pdReady, pdNil) { return true } - if old != 0 { - throw("runtime: double wait") - } - if atomic.Casuintptr(gpp, 0, pdWait) { + if gpp.CompareAndSwap(pdNil, pdWait) { break } + + // Double check that this isn't corrupt; otherwise we'd loop + // forever. + if v := gpp.Load(); v != pdReady && v != pdNil { + throw("runtime: double wait") + } } // need to recheck error states after setting gpp to pdWait // this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl - // do the opposite: store to closing/rd/wd, membarrier, load of rg/wg - if waitio || netpollcheckerr(pd, mode) == 0 { + // do the opposite: store to closing/rd/wd, publishInfo, load of rg/wg + if waitio || netpollcheckerr(pd, mode) == pollNoError { gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5) } // be careful to not lose concurrent pdReady notification - old := atomic.Xchguintptr(gpp, 0) + old := gpp.Swap(pdNil) if old > pdWait { throw("runtime: corrupted polldesc") } @@ -459,11 +541,11 @@ func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g { } for { - old := *gpp + old := gpp.Load() if old == pdReady { return nil } - if old == 0 && !ioready { + if old == pdNil && !ioready { // Only set pdReady for ioready. runtime_pollWait // will check for timeout/cancel before waiting. return nil @@ -472,9 +554,9 @@ func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g { if ioready { new = pdReady } - if atomic.Casuintptr(gpp, old, new) { + if gpp.CompareAndSwap(old, new) { if old == pdWait { - old = 0 + old = pdNil } return (*g)(unsafe.Pointer(old)) } @@ -500,7 +582,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) { throw("runtime: inconsistent read deadline") } pd.rd = -1 - atomic.StorepNoWB(unsafe.Pointer(&pd.rt.f), nil) // full memory barrier between store to rd and load of rg in netpollunblock + pd.publishInfo() rg = netpollunblock(pd, 'r', false) } var wg *g @@ -509,7 +591,7 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) { throw("runtime: inconsistent write deadline") } pd.wd = -1 - atomic.StorepNoWB(unsafe.Pointer(&pd.wt.f), nil) // full memory barrier between store to wd and load of wg in netpollunblock + pd.publishInfo() wg = netpollunblock(pd, 'w', false) } unlock(&pd.lock) @@ -521,15 +603,15 @@ func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) { } } -func netpollDeadline(arg interface{}, seq uintptr) { +func netpollDeadline(arg any, seq uintptr) { netpolldeadlineimpl(arg.(*pollDesc), seq, true, true) } -func netpollReadDeadline(arg interface{}, seq uintptr) { +func netpollReadDeadline(arg any, seq uintptr) { netpolldeadlineimpl(arg.(*pollDesc), seq, true, false) } -func netpollWriteDeadline(arg interface{}, seq uintptr) { +func netpollWriteDeadline(arg any, seq uintptr) { netpolldeadlineimpl(arg.(*pollDesc), seq, false, true) } @@ -560,9 +642,9 @@ func (c *pollCache) alloc() *pollDesc { // makeArg converts pd to an interface{}. // makeArg does not do any allocation. Normally, such // a conversion requires an allocation because pointers to -// go:notinheap types (which pollDesc is) must be stored -// in interfaces indirectly. See issue 42076. -func (pd *pollDesc) makeArg() (i interface{}) { +// types which embed runtime/internal/sys.NotInHeap (which pollDesc is) +// must be stored in interfaces indirectly. See issue 42076. +func (pd *pollDesc) makeArg() (i any) { x := (*eface)(unsafe.Pointer(&i)) x._type = pdType x.data = unsafe.Pointer(&pd.self) @@ -570,6 +652,6 @@ func (pd *pollDesc) makeArg() (i interface{}) { } var ( - pdEface interface{} = (*pollDesc)(nil) - pdType *_type = efaceOf(&pdEface)._type + pdEface any = (*pollDesc)(nil) + pdType *_type = efaceOf(&pdEface)._type ) diff --git a/src/runtime/netpoll_aix.go b/src/runtime/netpoll_aix.go index 4590ed81a60c09..5184aad421d3b3 100644 --- a/src/runtime/netpoll_aix.go +++ b/src/runtime/netpoll_aix.go @@ -45,7 +45,7 @@ var ( wrwake int32 pendingUpdates int32 - netpollWakeSig uint32 // used to avoid duplicate calls of netpollBreak + netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak ) func netpollinit() { @@ -135,10 +135,13 @@ func netpollarm(pd *pollDesc, mode int) { // netpollBreak interrupts a poll. func netpollBreak() { - if atomic.Cas(&netpollWakeSig, 0, 1) { - b := [1]byte{0} - write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) + // Failing to cas indicates there is an in-flight wakeup, so we're done here. + if !netpollWakeSig.CompareAndSwap(0, 1) { + return } + + b := [1]byte{0} + write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) } // netpoll checks for ready network connections. @@ -146,6 +149,7 @@ func netpollBreak() { // delay < 0: blocks indefinitely // delay == 0: does not block, just polls // delay > 0: block for up to that many nanoseconds +// //go:nowritebarrierrec func netpoll(delay int64) gList { var timeout uintptr @@ -192,7 +196,7 @@ retry: var b [1]byte for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { } - atomic.Store(&netpollWakeSig, 0) + netpollWakeSig.Store(0) } // Still look at the other fds even if the mode may have // changed, as netpollBreak might have been called. @@ -212,10 +216,7 @@ retry: pfd.events &= ^_POLLOUT } if mode != 0 { - pds[i].everr = false - if pfd.revents == _POLLERR { - pds[i].everr = true - } + pds[i].setEventErr(pfd.revents == _POLLERR) netpollready(&toRun, pds[i], mode) n-- } diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go index 371ac59f8e2ec0..09da662c92da37 100644 --- a/src/runtime/netpoll_epoll.go +++ b/src/runtime/netpoll_epoll.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package runtime @@ -27,7 +26,7 @@ var ( netpollBreakRd, netpollBreakWr uintptr // for netpollBreak - netpollWakeSig uint32 // used to avoid duplicate calls of netpollBreak + netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak ) func netpollinit() { @@ -80,22 +79,25 @@ func netpollarm(pd *pollDesc, mode int) { // netpollBreak interrupts an epollwait. func netpollBreak() { - if atomic.Cas(&netpollWakeSig, 0, 1) { - for { - var b byte - n := write(netpollBreakWr, unsafe.Pointer(&b), 1) - if n == 1 { - break - } - if n == -_EINTR { - continue - } - if n == -_EAGAIN { - return - } - println("runtime: netpollBreak write failed with", -n) - throw("runtime: netpollBreak write failed") + // Failing to cas indicates there is an in-flight wakeup, so we're done here. + if !netpollWakeSig.CompareAndSwap(0, 1) { + return + } + + for { + var b byte + n := write(netpollBreakWr, unsafe.Pointer(&b), 1) + if n == 1 { + break } + if n == -_EINTR { + continue + } + if n == -_EAGAIN { + return + } + println("runtime: netpollBreak write failed with", -n) + throw("runtime: netpollBreak write failed") } } @@ -155,7 +157,7 @@ retry: // if blocking. var tmp [16]byte read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) - atomic.Store(&netpollWakeSig, 0) + netpollWakeSig.Store(0) } continue } @@ -169,10 +171,7 @@ retry: } if mode != 0 { pd := *(**pollDesc)(unsafe.Pointer(&ev.data)) - pd.everr = false - if ev.events == _EPOLLERR { - pd.everr = true - } + pd.setEventErr(ev.events == _EPOLLERR) netpollready(&toRun, pd, mode) } } diff --git a/src/runtime/netpoll_fake.go b/src/runtime/netpoll_fake.go index 8366f289144cf4..de1dcae7acf812 100644 --- a/src/runtime/netpoll_fake.go +++ b/src/runtime/netpoll_fake.go @@ -6,7 +6,6 @@ // Should never be used, because wasm/js network connections do not honor "SetNonblock". //go:build js && wasm -// +build js,wasm package runtime diff --git a/src/runtime/netpoll_kqueue.go b/src/runtime/netpoll_kqueue.go index 80d1b0cf18a17a..5ae77b57f2d38b 100644 --- a/src/runtime/netpoll_kqueue.go +++ b/src/runtime/netpoll_kqueue.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package runtime @@ -19,7 +18,7 @@ var ( netpollBreakRd, netpollBreakWr uintptr // for netpollBreak - netpollWakeSig uint32 // used to avoid duplicate calls of netpollBreak + netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak ) func netpollinit() { @@ -84,19 +83,22 @@ func netpollarm(pd *pollDesc, mode int) { // netpollBreak interrupts a kevent. func netpollBreak() { - if atomic.Cas(&netpollWakeSig, 0, 1) { - for { - var b byte - n := write(netpollBreakWr, unsafe.Pointer(&b), 1) - if n == 1 || n == -_EAGAIN { - break - } - if n == -_EINTR { - continue - } - println("runtime: netpollBreak write failed with", -n) - throw("runtime: netpollBreak write failed") + // Failing to cas indicates there is an in-flight wakeup, so we're done here. + if !netpollWakeSig.CompareAndSwap(0, 1) { + return + } + + for { + var b byte + n := write(netpollBreakWr, unsafe.Pointer(&b), 1) + if n == 1 || n == -_EAGAIN { + break } + if n == -_EINTR { + continue + } + println("runtime: netpollBreak write failed with", -n) + throw("runtime: netpollBreak write failed") } } @@ -153,7 +155,7 @@ retry: // if blocking. var tmp [16]byte read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp))) - atomic.Store(&netpollWakeSig, 0) + netpollWakeSig.Store(0) } continue } @@ -180,10 +182,7 @@ retry: } if mode != 0 { pd := (*pollDesc)(unsafe.Pointer(ev.udata)) - pd.everr = false - if ev.flags == _EV_ERROR { - pd.everr = true - } + pd.setEventErr(ev.flags == _EV_ERROR) netpollready(&toRun, pd, mode) } } diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go index d217d5b1604ae7..d835cd9bf09c37 100644 --- a/src/runtime/netpoll_solaris.go +++ b/src/runtime/netpoll_solaris.go @@ -88,7 +88,7 @@ var ( libc_port_dissociate, libc_port_getn, libc_port_alert libcFunc - netpollWakeSig uint32 // used to avoid duplicate calls of netpollBreak + netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak ) func errno() int32 { @@ -158,7 +158,7 @@ func netpollclose(fd uintptr) int32 { // this call, port_getn will return one and only one event for that // particular descriptor, so this function needs to be called again. func netpollupdate(pd *pollDesc, set, clear uint32) { - if pd.closing { + if pd.info().closing() { return } @@ -191,17 +191,20 @@ func netpollarm(pd *pollDesc, mode int) { // netpollBreak interrupts a port_getn wait. func netpollBreak() { - if atomic.Cas(&netpollWakeSig, 0, 1) { - // Use port_alert to put portfd into alert mode. - // This will wake up all threads sleeping in port_getn on portfd, - // and cause their calls to port_getn to return immediately. - // Further, until portfd is taken out of alert mode, - // all calls to port_getn will return immediately. - if port_alert(portfd, _PORT_ALERT_UPDATE, _POLLHUP, uintptr(unsafe.Pointer(&portfd))) < 0 { - if e := errno(); e != _EBUSY { - println("runtime: port_alert failed with", e) - throw("runtime: netpoll: port_alert failed") - } + // Failing to cas indicates there is an in-flight wakeup, so we're done here. + if !netpollWakeSig.CompareAndSwap(0, 1) { + return + } + + // Use port_alert to put portfd into alert mode. + // This will wake up all threads sleeping in port_getn on portfd, + // and cause their calls to port_getn to return immediately. + // Further, until portfd is taken out of alert mode, + // all calls to port_getn will return immediately. + if port_alert(portfd, _PORT_ALERT_UPDATE, _POLLHUP, uintptr(unsafe.Pointer(&portfd))) < 0 { + if e := errno(); e != _EBUSY { + println("runtime: port_alert failed with", e) + throw("runtime: netpoll: port_alert failed") } } } @@ -274,7 +277,7 @@ retry: println("runtime: port_alert failed with", e) throw("runtime: netpoll: port_alert failed") } - atomic.Store(&netpollWakeSig, 0) + netpollWakeSig.Store(0) } continue } diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go index 33ab8eba58cc48..14cf0c327fd758 100644 --- a/src/runtime/netpoll_stub.go +++ b/src/runtime/netpoll_stub.go @@ -3,14 +3,13 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package runtime import "runtime/internal/atomic" -var netpollInited uint32 -var netpollWaiters uint32 +var netpollInited atomic.Uint32 +var netpollWaiters atomic.Uint32 var netpollStubLock mutex var netpollNote note @@ -20,7 +19,7 @@ var netpollBrokenLock mutex var netpollBroken bool func netpollGenericInit() { - atomic.Store(&netpollInited, 1) + netpollInited.Store(1) } func netpollBreak() { @@ -58,5 +57,5 @@ func netpoll(delay int64) gList { } func netpollinited() bool { - return atomic.Load(&netpollInited) != 0 + return netpollInited.Load() != 0 } diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 4c1cd2633a86ba..796bf1dd19529a 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -35,7 +35,7 @@ type overlappedEntry struct { var ( iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle - netpollWakeSig uint32 // used to avoid duplicate calls of netpollBreak + netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak ) func netpollinit() { @@ -67,11 +67,14 @@ func netpollarm(pd *pollDesc, mode int) { } func netpollBreak() { - if atomic.Cas(&netpollWakeSig, 0, 1) { - if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 { - println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")") - throw("runtime: netpoll: PostQueuedCompletionStatus failed") - } + // Failing to cas indicates there is an in-flight wakeup, so we're done here. + if !netpollWakeSig.CompareAndSwap(0, 1) { + return + } + + if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 { + println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")") + throw("runtime: netpoll: PostQueuedCompletionStatus failed") } } @@ -133,7 +136,7 @@ func netpoll(delay int64) gList { } handlecompletion(&toRun, op, errno, qty) } else { - atomic.Store(&netpollWakeSig, 0) + netpollWakeSig.Store(0) if delay == 0 { // Forward the notification to the // blocked poller. diff --git a/src/runtime/norace_linux_test.go b/src/runtime/norace_linux_test.go index 94b7c7a46777d8..3521b24655bc95 100644 --- a/src/runtime/norace_linux_test.go +++ b/src/runtime/norace_linux_test.go @@ -3,12 +3,13 @@ // license that can be found in the LICENSE file. // The file contains tests that cannot run under race detector for some reason. +// //go:build !race -// +build !race package runtime_test import ( + "internal/abi" "runtime" "testing" "time" @@ -25,7 +26,7 @@ func newOSProcCreated() { // Can't be run with -race because it inserts calls into newOSProcCreated() // that require a valid G/M. func TestNewOSProc0(t *testing.T) { - runtime.NewOSProc0(0x800000, unsafe.Pointer(runtime.FuncPC(newOSProcCreated))) + runtime.NewOSProc0(0x800000, unsafe.Pointer(abi.FuncPCABIInternal(newOSProcCreated))) check := time.NewTicker(100 * time.Millisecond) defer check.Stop() end := time.After(5 * time.Second) diff --git a/src/runtime/norace_test.go b/src/runtime/norace_test.go index 9ad5dde3829924..3b5eca5341ec5d 100644 --- a/src/runtime/norace_test.go +++ b/src/runtime/norace_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // The file contains tests that cannot run under race detector for some reason. +// //go:build !race -// +build !race package runtime_test diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go index 4d77f0de6dd2fe..2efc56554cddcc 100644 --- a/src/runtime/os2_aix.go +++ b/src/runtime/os2_aix.go @@ -388,11 +388,11 @@ func exit1(code int32) //go:nosplit func exit(code int32) { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { syscall1(&libc_exit, uintptr(code)) return } @@ -403,11 +403,11 @@ func write2(fd, p uintptr, n int32) int32 //go:nosplit func write1(fd uintptr, p unsafe.Pointer, n int32) int32 { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { r, errno := syscall3(&libc_write, uintptr(fd), uintptr(p), uintptr(n)) if int32(r) < 0 { return -int32(errno) @@ -452,6 +452,7 @@ func pipe() (r, w int32, errno int32) { // assembly routine; the higher bits (if required), should be provided // by the assembly routine as 0. // The err result is an OS error code such as ENOMEM. +// //go:nosplit func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) { r, err0 := syscall6(&libc_mmap, uintptr(addr), uintptr(n), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(off)) @@ -492,11 +493,11 @@ func sigaction1(sig, new, old uintptr) //go:nosplit func sigaction(sig uintptr, new, old *sigactiont) { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // runtime.libpreinit. - if _g_ != nil { + if gp != nil { r, err := syscall3(&libc_sigaction, sig, uintptr(unsafe.Pointer(new)), uintptr(unsafe.Pointer(old))) if int32(r) == -1 { println("Sigaction failed for sig: ", sig, " with error:", hex(err)) @@ -644,11 +645,11 @@ func pthread_attr_init1(attr uintptr) int32 //go:nosplit func pthread_attr_init(attr *pthread_attr) int32 { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { r, _ := syscall1(&libpthread_attr_init, uintptr(unsafe.Pointer(attr))) return int32(r) } @@ -660,11 +661,11 @@ func pthread_attr_setdetachstate1(attr uintptr, state int32) int32 //go:nosplit func pthread_attr_setdetachstate(attr *pthread_attr, state int32) int32 { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { r, _ := syscall2(&libpthread_attr_setdetachstate, uintptr(unsafe.Pointer(attr)), uintptr(state)) return int32(r) } @@ -688,11 +689,11 @@ func pthread_attr_setstacksize1(attr uintptr, size uint64) int32 //go:nosplit func pthread_attr_setstacksize(attr *pthread_attr, size uint64) int32 { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { r, _ := syscall2(&libpthread_attr_setstacksize, uintptr(unsafe.Pointer(attr)), uintptr(size)) return int32(r) } @@ -704,11 +705,11 @@ func pthread_create1(tid, attr, fn, arg uintptr) int32 //go:nosplit func pthread_create(tid *pthread, attr *pthread_attr, fn *funcDescriptor, arg unsafe.Pointer) int32 { - _g_ := getg() + gp := getg() // Check the validity of g because without a g during // newosproc0. - if _g_ != nil { + if gp != nil { r, _ := syscall4(&libpthread_create, uintptr(unsafe.Pointer(tid)), uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(fn)), uintptr(arg)) return int32(r) } @@ -722,11 +723,11 @@ func sigprocmask1(how, new, old uintptr) //go:nosplit func sigprocmask(how int32, new, old *sigset) { - _g_ := getg() + gp := getg() // Check the validity of m because it might be called during a cgo // callback early enough where m isn't available yet. - if _g_ != nil && _g_.m != nil { + if gp != nil && gp.m != nil { r, err := syscall3(&libpthread_sigthreadmask, uintptr(how), uintptr(unsafe.Pointer(new)), uintptr(unsafe.Pointer(old))) if int32(r) != 0 { println("syscall sigthreadmask failed: ", hex(err)) diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go index c5dc23de8b6089..8c9cbe28ec2fb7 100644 --- a/src/runtime/os3_plan9.go +++ b/src/runtime/os3_plan9.go @@ -5,7 +5,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -13,7 +14,9 @@ import ( // //go:nowritebarrierrec func sighandler(_ureg *ureg, note *byte, gp *g) int { - _g_ := getg() + gsignal := getg() + mp := gsignal.m + var t sigTabT var docrash bool var sig int @@ -60,7 +63,7 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int { if flags&_SigPanic != 0 { // Copy the error string from sigtramp's stack into m->notesig so // we can reliably access it from the panic routines. - memmove(unsafe.Pointer(_g_.m.notesig), unsafe.Pointer(note), uintptr(len(notestr)+1)) + memmove(unsafe.Pointer(mp.notesig), unsafe.Pointer(note), uintptr(len(notestr)+1)) gp.sig = uint32(sig) gp.sigpc = c.pc() @@ -92,15 +95,15 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int { if usesLR { c.setlr(pc) } else { - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = pc c.setsp(sp) } } if usesLR { - c.setpc(funcPC(sigpanictramp)) + c.setpc(abi.FuncPCABI0(sigpanictramp)) } else { - c.setpc(funcPC(sigpanic0)) + c.setpc(abi.FuncPCABI0(sigpanic0)) } return _NCONT } @@ -119,8 +122,8 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int { return _NCONT } Throw: - _g_.m.throwing = 1 - _g_.m.caughtsig.set(gp) + mp.throwing = throwTypeRuntime + mp.caughtsig.set(gp) startpanic_m() print(notestr, "\n") print("PC=", hex(c.pc()), "\n") diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go index 39ef831acfef87..76cf59772bcb05 100644 --- a/src/runtime/os3_solaris.go +++ b/src/runtime/os3_solaris.go @@ -5,7 +5,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -46,7 +47,6 @@ import ( //go:cgo_import_dynamic libc_sysconf sysconf "libc.so" //go:cgo_import_dynamic libc_usleep usleep "libc.so" //go:cgo_import_dynamic libc_write write "libc.so" -//go:cgo_import_dynamic libc_pipe pipe "libc.so" //go:cgo_import_dynamic libc_pipe2 pipe2 "libc.so" //go:linkname libc____errno libc____errno @@ -82,7 +82,6 @@ import ( //go:linkname libc_sysconf libc_sysconf //go:linkname libc_usleep libc_usleep //go:linkname libc_write libc_write -//go:linkname libc_pipe libc_pipe //go:linkname libc_pipe2 libc_pipe2 var ( @@ -119,7 +118,6 @@ var ( libc_sysconf, libc_usleep, libc_write, - libc_pipe, libc_pipe2 libcFunc ) @@ -143,6 +141,7 @@ func osinit() { func tstart_sysvicall(newm *m) uint32 // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { var ( @@ -172,7 +171,7 @@ func newosproc(mp *m) { // Disable signals during create, so that the new thread starts // with signals disabled. It will enable them in minit. sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - ret = pthread_create(&tid, &attr, funcPC(tstart_sysvicall), unsafe.Pointer(mp)) + ret = pthread_create(&tid, &attr, abi.FuncPCABI0(tstart_sysvicall), unsafe.Pointer(mp)) sigprocmask(_SIG_SETMASK, &oset, nil) if ret != 0 { print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n") @@ -215,7 +214,7 @@ func miniterrno() // Called to initialize a new m (including the bootstrap m). // Called on the new thread, cannot allocate memory. func minit() { - asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno)) + asmcgocall(unsafe.Pointer(abi.FuncPCABI0(miniterrno)), unsafe.Pointer(&libc____errno)) minitSignals() @@ -241,8 +240,8 @@ func setsig(i uint32, fn uintptr) { sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go + fn = abi.FuncPCABI0(sigtramp) } *((*uintptr)(unsafe.Pointer(&sa._funcptr))) = fn sigaction(i, &sa, nil) @@ -269,6 +268,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp @@ -288,6 +288,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + //go:nosplit func semacreate(mp *m) { if mp.waitsema != 0 { @@ -295,18 +308,17 @@ func semacreate(mp *m) { } var sem *semt - _g_ := getg() // Call libc's malloc rather than malloc. This will // allocate space on the C heap. We can't call malloc // here because it could cause a deadlock. - _g_.m.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc)) - _g_.m.libcall.n = 1 - _g_.m.scratch = mscratch{} - _g_.m.scratch.v[0] = unsafe.Sizeof(*sem) - _g_.m.libcall.args = uintptr(unsafe.Pointer(&_g_.m.scratch)) - asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&_g_.m.libcall)) - sem = (*semt)(unsafe.Pointer(_g_.m.libcall.r1)) + mp.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc)) + mp.libcall.n = 1 + mp.scratch = mscratch{} + mp.scratch.v[0] = unsafe.Sizeof(*sem) + mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch)) + asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall)) + sem = (*semt)(unsafe.Pointer(mp.libcall.r1)) if sem_init(sem, 0, 0) != 0 { throw("sem_init") } @@ -315,20 +327,20 @@ func semacreate(mp *m) { //go:nosplit func semasleep(ns int64) int32 { - _m_ := getg().m + mp := getg().m if ns >= 0 { - _m_.ts.tv_sec = ns / 1000000000 - _m_.ts.tv_nsec = ns % 1000000000 - - _m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np)) - _m_.libcall.n = 2 - _m_.scratch = mscratch{} - _m_.scratch.v[0] = _m_.waitsema - _m_.scratch.v[1] = uintptr(unsafe.Pointer(&_m_.ts)) - _m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch)) - asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&_m_.libcall)) - if *_m_.perrno != 0 { - if *_m_.perrno == _ETIMEDOUT || *_m_.perrno == _EAGAIN || *_m_.perrno == _EINTR { + mp.ts.tv_sec = ns / 1000000000 + mp.ts.tv_nsec = ns % 1000000000 + + mp.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np)) + mp.libcall.n = 2 + mp.scratch = mscratch{} + mp.scratch.v[0] = mp.waitsema + mp.scratch.v[1] = uintptr(unsafe.Pointer(&mp.ts)) + mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch)) + asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall)) + if *mp.perrno != 0 { + if *mp.perrno == _ETIMEDOUT || *mp.perrno == _EAGAIN || *mp.perrno == _EINTR { return -1 } throw("sem_reltimedwait_np") @@ -336,16 +348,16 @@ func semasleep(ns int64) int32 { return 0 } for { - _m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait)) - _m_.libcall.n = 1 - _m_.scratch = mscratch{} - _m_.scratch.v[0] = _m_.waitsema - _m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch)) - asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&_m_.libcall)) - if _m_.libcall.r1 == 0 { + mp.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait)) + mp.libcall.n = 1 + mp.scratch = mscratch{} + mp.scratch.v[0] = mp.waitsema + mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch)) + asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall)) + if mp.libcall.r1 == 0 { break } - if *_m_.perrno == _EINTR { + if *mp.perrno == _EINTR { continue } throw("sem_wait") @@ -390,6 +402,7 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (un } //go:nosplit +//go:cgo_unsafe_args func doMmap(addr, n, prot, flags, fd, off uintptr) (uintptr, uintptr) { var libcall libcall libcall.fn = uintptr(unsafe.Pointer(&libc_mmap)) @@ -546,13 +559,6 @@ func write1(fd uintptr, buf unsafe.Pointer, nbyte int32) int32 { return -int32(err) } -//go:nosplit -func pipe() (r, w int32, errno int32) { - var p [2]int32 - _, e := sysvicall1Err(&libc_pipe, uintptr(noescape(unsafe.Pointer(&p)))) - return p[0], p[1], int32(e) -} - //go:nosplit func pipe2(flags int32) (r, w int32, errno int32) { var p [2]int32 @@ -565,12 +571,6 @@ func closeonexec(fd int32) { fcntl(fd, _F_SETFD, _FD_CLOEXEC) } -//go:nosplit -func setNonblock(fd int32) { - flags := fcntl(fd, _F_GETFL, 0) - fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) -} - func osyield1() //go:nosplit @@ -598,7 +598,7 @@ func sysargs(argc int32, argv **byte) { n++ // now argv+n is auxv - auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) sysauxv(auxv[:]) } @@ -619,3 +619,12 @@ func sysauxv(auxv []uintptr) { } } } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go index 4fb1c8e845c87c..15e4929779a2c5 100644 --- a/src/runtime/os_aix.go +++ b/src/runtime/os_aix.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix package runtime import ( + "internal/abi" "unsafe" ) @@ -48,7 +48,7 @@ func semacreate(mp *m) { //go:nosplit func semasleep(ns int64) int32 { - _m_ := getg().m + mp := getg().m if ns >= 0 { var ts timespec @@ -62,17 +62,17 @@ func semasleep(ns int64) int32 { ts.tv_nsec -= 1e9 } - if r, err := sem_timedwait((*semt)(unsafe.Pointer(_m_.waitsema)), &ts); r != 0 { + if r, err := sem_timedwait((*semt)(unsafe.Pointer(mp.waitsema)), &ts); r != 0 { if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR { return -1 } - println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", _m_.id) + println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", mp.id) throw("sem_timedwait") } return 0 } for { - r1, err := sem_wait((*semt)(unsafe.Pointer(_m_.waitsema))) + r1, err := sem_wait((*semt)(unsafe.Pointer(mp.waitsema))) if r1 == 0 { break } @@ -150,6 +150,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n") // Called to do synchronous initialization of Go code built with // -buildmode=c-archive or -buildmode=c-shared. // None of the Go runtime is initialized. +// //go:nosplit //go:nowritebarrierrec func libpreinit() { @@ -267,7 +268,7 @@ func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go fn = uintptr(unsafe.Pointer(&sigtramp)) } sa.sa_handler = fn @@ -296,6 +297,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp @@ -322,6 +324,19 @@ func sigdelset(mask *sigset, i int) { (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63) } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + const ( _CLOCK_REALTIME = 9 _CLOCK_MONOTONIC = 10 @@ -360,3 +375,12 @@ func setNonblock(fd int32) { flags := fcntl(fd, _F_GETFL, 0) fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index 079be107d719a2..8562d7d9069ee2 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -195,6 +195,7 @@ func goenvs() { } // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrierrec func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) @@ -292,6 +293,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n") // Called to do synchronous initialization of Go code built with // -buildmode=c-archive or -buildmode=c-shared. // None of the Go runtime is initialized. +// //go:nosplit //go:nowritebarrierrec func libpreinit() { @@ -324,6 +326,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { // iOS does not support alternate signal stack. @@ -369,7 +372,7 @@ func setsig(i uint32, fn uintptr) { var sa usigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = ^uint32(0) - if fn == funcPC(sighandler) { // funcPC(sighandler) matches the callers in signal_unix.go + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go if iscgo { fn = abi.FuncPCABI0(cgoSigtramp) } else { @@ -410,6 +413,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp @@ -425,6 +429,19 @@ func sigdelset(mask *sigset, i int) { *mask &^= 1 << (uint32(i) - 1) } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + //go:linkname executablePath os.executablePath var executablePath string @@ -446,3 +463,12 @@ func sysargs(argc int32, argv **byte) { func signalM(mp *m, sig int) { pthread_kill(pthread(mp.procid), uint32(sig)) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go index 5c688a31096a6b..83478143b933b1 100644 --- a/src/runtime/os_dragonfly.go +++ b/src/runtime/os_dragonfly.go @@ -5,7 +5,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -61,10 +62,8 @@ func kqueue() int32 //go:noescape func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 -func pipe() (r, w int32, errno int32) func pipe2(flags int32) (r, w int32, errno int32) func closeonexec(fd int32) -func setNonblock(fd int32) // From DragonFly's const ( @@ -143,18 +142,19 @@ func futexwakeup(addr *uint32, cnt uint32) { func lwp_start(uintptr) // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", funcPC(lwp_start), " id=", mp.id, " ostk=", &mp, "\n") + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", abi.FuncPCABI0(lwp_start), " id=", mp.id, " ostk=", &mp, "\n") } var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) params := lwpparams{ - start_func: funcPC(lwp_start), + start_func: abi.FuncPCABI0(lwp_start), arg: unsafe.Pointer(mp), stack: uintptr(stk), tid1: nil, // minit will record tid @@ -202,6 +202,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { unminitSignals() @@ -226,8 +227,8 @@ func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go + fn = abi.FuncPCABI0(sigtramp) } sa.sa_sigaction = fn sigaction(i, &sa, nil) @@ -248,6 +249,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { s.ss_sp = sp @@ -267,6 +269,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 @@ -278,7 +293,7 @@ func sysargs(argc int32, argv **byte) { // skip NULL separator n++ - auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) sysauxv(auxv[:]) } @@ -310,3 +325,12 @@ func raise(sig uint32) { func signalM(mp *m, sig int) { lwp_kill(-1, int32(mp.procid), sig) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 09dd50ce594775..23efd1a46e8349 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -5,7 +5,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -46,10 +47,8 @@ func kqueue() int32 //go:noescape func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 -func pipe() (r, w int32, errno int32) func pipe2(flags int32) (r, w int32, errno int32) func closeonexec(fd int32) -func setNonblock(fd int32) // From FreeBSD's const ( @@ -116,8 +115,8 @@ func getncpu() int32 { } maskSize := int(maxcpus+7) / 8 - if maskSize < sys.PtrSize { - maskSize = sys.PtrSize + if maskSize < goarch.PtrSize { + maskSize = goarch.PtrSize } if maskSize > len(mask) { maskSize = len(mask) @@ -193,15 +192,16 @@ func futexwakeup(addr *uint32, cnt uint32) { func thr_start() // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", funcPC(thr_start), " id=", mp.id, " ostk=", &mp, "\n") + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " thr_start=", abi.FuncPCABI0(thr_start), " id=", mp.id, " ostk=", &mp, "\n") } param := thrparam{ - start_func: funcPC(thr_start), + start_func: abi.FuncPCABI0(thr_start), arg: unsafe.Pointer(mp), stack_base: mp.g0.stack.lo, stack_size: uintptr(stk) - mp.g0.stack.lo, @@ -222,6 +222,7 @@ func newosproc(mp *m) { } // Version of newosproc that doesn't require a valid G. +// //go:nosplit func newosproc0(stacksize uintptr, fn unsafe.Pointer) { stack := sysAlloc(stacksize, &memstats.stacks_sys) @@ -236,7 +237,7 @@ func newosproc0(stacksize uintptr, fn unsafe.Pointer) { // However, newosproc0 is currently unreachable because builds // utilizing c-shared/c-archive force external linking. param := thrparam{ - start_func: funcPC(fn), + start_func: uintptr(fn), arg: nil, stack_base: uintptr(stack), //+stacksize? stack_size: stacksize, @@ -262,6 +263,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n") // Called to do synchronous initialization of Go code built with // -buildmode=c-archive or -buildmode=c-shared. // None of the Go runtime is initialized. +// //go:nosplit //go:nowritebarrierrec func libpreinit() { @@ -319,6 +321,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { unminitSignals() @@ -360,6 +363,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { s.ss_sp = sp @@ -379,6 +383,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 @@ -391,7 +408,7 @@ func sysargs(argc int32, argv **byte) { n++ // now argv+n is auxv - auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) sysauxv(auxv[:]) } @@ -419,6 +436,7 @@ func sysauxv(auxv []uintptr) { } // sysSigaction calls the sigaction system call. +// //go:nosplit func sysSigaction(sig uint32, new, old *sigactiont) { // Use system stack to avoid split stack overflow on amd64 @@ -430,6 +448,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) { } // asmSigaction is implemented in assembly. +// //go:noescape func asmSigaction(sig uintptr, new, old *sigactiont) int32 @@ -446,3 +465,12 @@ func raise(sig uint32) { func signalM(mp *m, sig int) { thr_kill(thread(mp.procid), sig) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_freebsd2.go b/src/runtime/os_freebsd2.go index fde6fbf1b144eb..3eaedf0b8b99a8 100644 --- a/src/runtime/os_freebsd2.go +++ b/src/runtime/os_freebsd2.go @@ -3,18 +3,19 @@ // license that can be found in the LICENSE file. //go:build freebsd && !amd64 -// +build freebsd,!amd64 package runtime +import "internal/abi" + //go:nosplit //go:nowritebarrierrec func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go + fn = abi.FuncPCABI0(sigtramp) } sa.sa_handler = fn sigaction(i, &sa, nil) diff --git a/src/runtime/os_freebsd_amd64.go b/src/runtime/os_freebsd_amd64.go index dc0bb9ff96fd33..b179383eacc769 100644 --- a/src/runtime/os_freebsd_amd64.go +++ b/src/runtime/os_freebsd_amd64.go @@ -4,6 +4,8 @@ package runtime +import "internal/abi" + func cgoSigtramp() //go:nosplit @@ -12,11 +14,11 @@ func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go if iscgo { - fn = funcPC(cgoSigtramp) + fn = abi.FuncPCABI0(cgoSigtramp) } else { - fn = funcPC(sigtramp) + fn = abi.FuncPCABI0(sigtramp) } } sa.sa_handler = fn diff --git a/src/runtime/os_freebsd_noauxv.go b/src/runtime/os_freebsd_noauxv.go index 8fe0cb6718f14d..1d9452bda5fc33 100644 --- a/src/runtime/os_freebsd_noauxv.go +++ b/src/runtime/os_freebsd_noauxv.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd && !arm -// +build freebsd,!arm package runtime diff --git a/src/runtime/os_js.go b/src/runtime/os_js.go index 52b64e76027648..7ae0e8d3ece718 100644 --- a/src/runtime/os_js.go +++ b/src/runtime/os_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package runtime @@ -50,13 +49,13 @@ func osyield_no_g() { const _SIGSEGV = 0xb func sigpanic() { - g := getg() - if !canpanic(g) { + gp := getg() + if !canpanic() { throw("unexpected signal during runtime execution") } // js only invokes the exception handler for memory faults. - g.sig = _SIGSEGV + gp.sig = _SIGSEGV panicmem() } @@ -127,9 +126,10 @@ func initsig(preinit bool) { } // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { - panic("newosproc: not implemented") + throw("newosproc: not implemented") } func setProcessCPUProfiler(hz int32) {} diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index c8b29e396c7128..3ae665c83d867a 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -5,11 +5,33 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" + "runtime/internal/atomic" + "runtime/internal/syscall" "unsafe" ) -type mOS struct{} +// sigPerThreadSyscall is the same signal (SIGSETXID) used by glibc for +// per-thread syscalls on Linux. We use it for the same purpose in non-cgo +// binaries. +const sigPerThreadSyscall = _SIGRTMIN + 1 + +type mOS struct { + // profileTimer holds the ID of the POSIX interval timer for profiling CPU + // usage on this thread. + // + // It is valid when the profileTimerValid field is true. A thread + // creates and manages its own timer, and these fields are read and written + // only by this thread. But because some of the reads on profileTimerValid + // are in signal handling code, this field should be atomic type. + profileTimer int32 + profileTimerValid atomic.Bool + + // needPerThreadSyscall indicates that a per-thread syscall is required + // for doAllThreadsSyscall. + needPerThreadSyscall atomic.Uint8 +} //go:noescape func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 @@ -30,9 +52,12 @@ const ( ) // Atomically, +// // if(*addr == val) sleep +// // Might be woken up spuriously; that's allowed. // Don't sleep longer than ns; ns < 0 means forever. +// //go:nosplit func futexsleep(addr *uint32, val uint32, ns int64) { // Some Linux kernels have a bug where futex of @@ -51,6 +76,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) { } // If any procs are sleeping on addr, wake up at most cnt. +// //go:nosplit func futexwakeup(addr *uint32, cnt uint32) { ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0) @@ -135,6 +161,7 @@ const ( func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32 // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) @@ -142,14 +169,14 @@ func newosproc(mp *m) { * note: strace gets confused if we use CLONE_PTRACE here. */ if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n") + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", abi.FuncPCABI0(clone), " id=", mp.id, " ostk=", &mp, "\n") } // Disable signals during clone, so that the new thread starts // with signals disabled. It will enable them in minit. var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) + ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(abi.FuncPCABI0(mstart))) sigprocmask(_SIG_SETMASK, &oset, nil) if ret < 0 { @@ -162,6 +189,7 @@ func newosproc(mp *m) { } // Version of newosproc that doesn't require a valid G. +// //go:nosplit func newosproc0(stacksize uintptr, fn unsafe.Pointer) { stack := sysAlloc(stacksize, &memstats.stacks_sys) @@ -205,7 +233,7 @@ func sysargs(argc int32, argv **byte) { n++ // now argv+n is auxv - auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) if sysauxv(auxv[:]) != 0 { return } @@ -343,6 +371,7 @@ func goenvs() { // Called to do synchronous initialization of Go code built with // -buildmode=c-archive or -buildmode=c-shared. // None of the Go runtime is initialized. +// //go:nosplit //go:nowritebarrierrec func libpreinit() { @@ -370,6 +399,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { unminitSignals() @@ -394,6 +424,15 @@ func sigaltstack(new, old *stackt) //go:noescape func setitimer(mode int32, new, old *itimerval) +//go:noescape +func timer_create(clockid int32, sevp *sigevent, timerid *int32) int32 + +//go:noescape +func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 + +//go:noescape +func timer_delete(timerid int32) int32 + //go:noescape func rtsigprocmask(how int32, new, old *sigset, size int32) @@ -415,9 +454,12 @@ func osyield_no_g() { osyield() } -func pipe() (r, w int32, errno int32) func pipe2(flags int32) (r, w int32, errno int32) -func setNonblock(fd int32) + +const ( + _si_max_size = 128 + _sigev_max_size = 64 +) //go:nosplit //go:nowritebarrierrec @@ -429,13 +471,13 @@ func setsig(i uint32, fn uintptr) { // should not be used". x86_64 kernel requires it. Only use it on // x86. if GOARCH == "386" || GOARCH == "amd64" { - sa.sa_restorer = funcPC(sigreturn) + sa.sa_restorer = abi.FuncPCABI0(sigreturn) } - if fn == funcPC(sighandler) { + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go if iscgo { - fn = funcPC(cgoSigtramp) + fn = abi.FuncPCABI0(cgoSigtramp) } else { - fn = funcPC(sigtramp) + fn = abi.FuncPCABI0(sigtramp) } } sa.sa_handler = fn @@ -463,6 +505,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { *(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp @@ -473,6 +516,7 @@ func (c *sigctxt) fixsigcode(sig uint32) { } // sysSigaction calls the rt_sigaction system call. +// //go:nosplit func sysSigaction(sig uint32, new, old *sigactiont) { if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 { @@ -497,6 +541,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) { } // rt_sigaction is implemented in assembly. +// //go:noescape func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 @@ -507,3 +552,337 @@ func tgkill(tgid, tid, sig int) func signalM(mp *m, sig int) { tgkill(getpid(), int(mp.procid), sig) } + +// go118UseTimerCreateProfiler enables the per-thread CPU profiler. +const go118UseTimerCreateProfiler = true + +// validSIGPROF compares this signal delivery's code against the signal sources +// that the profiler uses, returning whether the delivery should be processed. +// To be processed, a signal delivery from a known profiling mechanism should +// correspond to the best profiling mechanism available to this thread. Signals +// from other sources are always considered valid. +// +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + code := int32(c.sigcode()) + setitimer := code == _SI_KERNEL + timer_create := code == _SI_TIMER + + if !(setitimer || timer_create) { + // The signal doesn't correspond to a profiling mechanism that the + // runtime enables itself. There's no reason to process it, but there's + // no reason to ignore it either. + return true + } + + if mp == nil { + // Since we don't have an M, we can't check if there's an active + // per-thread timer for this thread. We don't know how long this thread + // has been around, and if it happened to interact with the Go scheduler + // at a time when profiling was active (causing it to have a per-thread + // timer). But it may have never interacted with the Go scheduler, or + // never while profiling was active. To avoid double-counting, process + // only signals from setitimer. + // + // When a custom cgo traceback function has been registered (on + // platforms that support runtime.SetCgoTraceback), SIGPROF signals + // delivered to a thread that cannot find a matching M do this check in + // the assembly implementations of runtime.cgoSigtramp. + return setitimer + } + + // Having an M means the thread interacts with the Go scheduler, and we can + // check whether there's an active per-thread timer for this thread. + if mp.profileTimerValid.Load() { + // If this M has its own per-thread CPU profiling interval timer, we + // should track the SIGPROF signals that come from that timer (for + // accurate reporting of its CPU usage; see issue 35057) and ignore any + // that it gets from the process-wide setitimer (to not over-count its + // CPU consumption). + return timer_create + } + + // No active per-thread timer means the only valid profiler is setitimer. + return setitimer +} + +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + mp := getg().m + mp.profilehz = hz + + if !go118UseTimerCreateProfiler { + return + } + + // destroy any active timer + if mp.profileTimerValid.Load() { + timerid := mp.profileTimer + mp.profileTimerValid.Store(false) + mp.profileTimer = 0 + + ret := timer_delete(timerid) + if ret != 0 { + print("runtime: failed to disable profiling timer; timer_delete(", timerid, ") errno=", -ret, "\n") + throw("timer_delete") + } + } + + if hz == 0 { + // If the goal was to disable profiling for this thread, then the job's done. + return + } + + // The period of the timer should be 1/Hz. For every "1/Hz" of additional + // work, the user should expect one additional sample in the profile. + // + // But to scale down to very small amounts of application work, to observe + // even CPU usage of "one tenth" of the requested period, set the initial + // timing delay in a different way: So that "one tenth" of a period of CPU + // spend shows up as a 10% chance of one sample (for an expected value of + // 0.1 samples), and so that "two and six tenths" periods of CPU spend show + // up as a 60% chance of 3 samples and a 40% chance of 2 samples (for an + // expected value of 2.6). Set the initial delay to a value in the unifom + // random distribution between 0 and the desired period. And because "0" + // means "disable timer", add 1 so the half-open interval [0,period) turns + // into (0,period]. + // + // Otherwise, this would show up as a bias away from short-lived threads and + // from threads that are only occasionally active: for example, when the + // garbage collector runs on a mostly-idle system, the additional threads it + // activates may do a couple milliseconds of GC-related work and nothing + // else in the few seconds that the profiler observes. + spec := new(itimerspec) + spec.it_value.setNsec(1 + int64(fastrandn(uint32(1e9/hz)))) + spec.it_interval.setNsec(1e9 / int64(hz)) + + var timerid int32 + var sevp sigevent + sevp.notify = _SIGEV_THREAD_ID + sevp.signo = _SIGPROF + sevp.sigev_notify_thread_id = int32(mp.procid) + ret := timer_create(_CLOCK_THREAD_CPUTIME_ID, &sevp, &timerid) + if ret != 0 { + // If we cannot create a timer for this M, leave profileTimerValid false + // to fall back to the process-wide setitimer profiler. + return + } + + ret = timer_settime(timerid, 0, spec, nil) + if ret != 0 { + print("runtime: failed to configure profiling timer; timer_settime(", timerid, + ", 0, {interval: {", + spec.it_interval.tv_sec, "s + ", spec.it_interval.tv_nsec, "ns} value: {", + spec.it_value.tv_sec, "s + ", spec.it_value.tv_nsec, "ns}}, nil) errno=", -ret, "\n") + throw("timer_settime") + } + + mp.profileTimer = timerid + mp.profileTimerValid.Store(true) +} + +// perThreadSyscallArgs contains the system call number, arguments, and +// expected return values for a system call to be executed on all threads. +type perThreadSyscallArgs struct { + trap uintptr + a1 uintptr + a2 uintptr + a3 uintptr + a4 uintptr + a5 uintptr + a6 uintptr + r1 uintptr + r2 uintptr +} + +// perThreadSyscall is the system call to execute for the ongoing +// doAllThreadsSyscall. +// +// perThreadSyscall may only be written while mp.needPerThreadSyscall == 0 on +// all Ms. +var perThreadSyscall perThreadSyscallArgs + +// syscall_runtime_doAllThreadsSyscall and executes a specified system call on +// all Ms. +// +// The system call is expected to succeed and return the same value on every +// thread. If any threads do not match, the runtime throws. +// +//go:linkname syscall_runtime_doAllThreadsSyscall syscall.runtime_doAllThreadsSyscall +//go:uintptrescapes +func syscall_runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { + if iscgo { + // In cgo, we are not aware of threads created in C, so this approach will not work. + panic("doAllThreadsSyscall not supported with cgo enabled") + } + + // STW to guarantee that user goroutines see an atomic change to thread + // state. Without STW, goroutines could migrate Ms while change is in + // progress and e.g., see state old -> new -> old -> new. + // + // N.B. Internally, this function does not depend on STW to + // successfully change every thread. It is only needed for user + // expectations, per above. + stopTheWorld("doAllThreadsSyscall") + + // This function depends on several properties: + // + // 1. All OS threads that already exist are associated with an M in + // allm. i.e., we won't miss any pre-existing threads. + // 2. All Ms listed in allm will eventually have an OS thread exist. + // i.e., they will set procid and be able to receive signals. + // 3. OS threads created after we read allm will clone from a thread + // that has executed the system call. i.e., they inherit the + // modified state. + // + // We achieve these through different mechanisms: + // + // 1. Addition of new Ms to allm in allocm happens before clone of its + // OS thread later in newm. + // 2. newm does acquirem to avoid being preempted, ensuring that new Ms + // created in allocm will eventually reach OS thread clone later in + // newm. + // 3. We take allocmLock for write here to prevent allocation of new Ms + // while this function runs. Per (1), this prevents clone of OS + // threads that are not yet in allm. + allocmLock.lock() + + // Disable preemption, preventing us from changing Ms, as we handle + // this M specially. + // + // N.B. STW and lock() above do this as well, this is added for extra + // clarity. + acquirem() + + // N.B. allocmLock also prevents concurrent execution of this function, + // serializing use of perThreadSyscall, mp.needPerThreadSyscall, and + // ensuring all threads execute system calls from multiple calls in the + // same order. + + r1, r2, errno := syscall.Syscall6(trap, a1, a2, a3, a4, a5, a6) + if GOARCH == "ppc64" || GOARCH == "ppc64le" { + // TODO(https://go.dev/issue/51192 ): ppc64 doesn't use r2. + r2 = 0 + } + if errno != 0 { + releasem(getg().m) + allocmLock.unlock() + startTheWorld() + return r1, r2, errno + } + + perThreadSyscall = perThreadSyscallArgs{ + trap: trap, + a1: a1, + a2: a2, + a3: a3, + a4: a4, + a5: a5, + a6: a6, + r1: r1, + r2: r2, + } + + // Wait for all threads to start. + // + // As described above, some Ms have been added to allm prior to + // allocmLock, but not yet completed OS clone and set procid. + // + // At minimum we must wait for a thread to set procid before we can + // send it a signal. + // + // We take this one step further and wait for all threads to start + // before sending any signals. This prevents system calls from getting + // applied twice: once in the parent and once in the child, like so: + // + // A B C + // add C to allm + // doAllThreadsSyscall + // allocmLock.lock() + // signal B + // + // execute syscall + // + // clone C + // + // set procid + // signal C + // + // execute syscall + // + // + // In this case, thread C inherited the syscall-modified state from + // thread B and did not need to execute the syscall, but did anyway + // because doAllThreadsSyscall could not be sure whether it was + // required. + // + // Some system calls may not be idempotent, so we ensure each thread + // executes the system call exactly once. + for mp := allm; mp != nil; mp = mp.alllink { + for atomic.Load64(&mp.procid) == 0 { + // Thread is starting. + osyield() + } + } + + // Signal every other thread, where they will execute perThreadSyscall + // from the signal handler. + gp := getg() + tid := gp.m.procid + for mp := allm; mp != nil; mp = mp.alllink { + if atomic.Load64(&mp.procid) == tid { + // Our thread already performed the syscall. + continue + } + mp.needPerThreadSyscall.Store(1) + signalM(mp, sigPerThreadSyscall) + } + + // Wait for all threads to complete. + for mp := allm; mp != nil; mp = mp.alllink { + if mp.procid == tid { + continue + } + for mp.needPerThreadSyscall.Load() != 0 { + osyield() + } + } + + perThreadSyscall = perThreadSyscallArgs{} + + releasem(getg().m) + allocmLock.unlock() + startTheWorld() + + return r1, r2, errno +} + +// runPerThreadSyscall runs perThreadSyscall for this M if required. +// +// This function throws if the system call returns with anything other than the +// expected values. +// +//go:nosplit +func runPerThreadSyscall() { + gp := getg() + if gp.m.needPerThreadSyscall.Load() == 0 { + return + } + + args := perThreadSyscall + r1, r2, errno := syscall.Syscall6(args.trap, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6) + if GOARCH == "ppc64" || GOARCH == "ppc64le" { + // TODO(https://go.dev/issue/51192 ): ppc64 doesn't use r2. + r2 = 0 + } + if errno != 0 || r1 != args.r1 || r2 != args.r2 { + print("trap:", args.trap, ", a123456=[", args.a1, ",", args.a2, ",", args.a3, ",", args.a4, ",", args.a5, ",", args.a6, "]\n") + print("results: got {r1=", r1, ",r2=", r2, ",errno=", errno, "}, want {r1=", args.r1, ",r2=", args.r2, ",errno=0\n") + fatal("AllThreadsSyscall6 results differ between threads; runtime corrupted") + } + + gp.m.needPerThreadSyscall.Store(0) +} diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go index b590da750f6f48..bd3ab44a110f0f 100644 --- a/src/runtime/os_linux_arm.go +++ b/src/runtime/os_linux_arm.go @@ -11,6 +11,8 @@ const ( _HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30 ) +func vdsoCall() + func checkgoarm() { // On Android, /proc/self/auxv might be unreadable and hwcap won't // reflect the CPU capabilities. Assume that every Android arm device diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 5260f22f57396b..2daa56fce75e53 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 -// +build arm64 package runtime diff --git a/src/runtime/os_linux_be64.go b/src/runtime/os_linux_be64.go index 498d7cec6d8e45..537515fcf25a8f 100644 --- a/src/runtime/os_linux_be64.go +++ b/src/runtime/os_linux_be64.go @@ -5,8 +5,6 @@ // The standard Linux sigset type on big-endian 64-bit machines. //go:build linux && (ppc64 || s390x) -// +build linux -// +build ppc64 s390x package runtime diff --git a/src/runtime/os_linux_generic.go b/src/runtime/os_linux_generic.go index fe1973dbde6ce0..bed9e66e156e2c 100644 --- a/src/runtime/os_linux_generic.go +++ b/src/runtime/os_linux_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !mips && !mipsle && !mips64 && !mips64le && !s390x && !ppc64 && linux -// +build !mips,!mipsle,!mips64,!mips64le,!s390x,!ppc64,linux package runtime diff --git a/src/runtime/os_linux_loong64.go b/src/runtime/os_linux_loong64.go new file mode 100644 index 00000000000000..3d84e9accb8042 --- /dev/null +++ b/src/runtime/os_linux_loong64.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 + +package runtime + +func archauxv(tag, val uintptr) {} + +func osArchInit() {} + +//go:nosplit +func cputicks() int64 { + // Currently cputicks() is used in blocking profiler and to seed fastrand(). + // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + return nanotime() +} diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go index bd76442dbd8432..188db0103481d7 100644 --- a/src/runtime/os_linux_mips64x.go +++ b/src/runtime/os_linux_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package runtime diff --git a/src/runtime/os_linux_mipsx.go b/src/runtime/os_linux_mipsx.go index ef8b3f7d434823..73016f81d91b7c 100644 --- a/src/runtime/os_linux_mipsx.go +++ b/src/runtime/os_linux_mipsx.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package runtime diff --git a/src/runtime/os_linux_noauxv.go b/src/runtime/os_linux_noauxv.go index 59b5aacaebc318..ff377277aa24c5 100644 --- a/src/runtime/os_linux_noauxv.go +++ b/src/runtime/os_linux_noauxv.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !arm && !arm64 && !mips && !mipsle && !mips64 && !mips64le && !s390x && !ppc64 && !ppc64le -// +build linux,!arm,!arm64,!mips,!mipsle,!mips64,!mips64le,!s390x,!ppc64,!ppc64le +//go:build linux && !arm && !arm64 && !loong64 && !mips && !mipsle && !mips64 && !mips64le && !s390x && !ppc64 && !ppc64le package runtime diff --git a/src/runtime/os_linux_novdso.go b/src/runtime/os_linux_novdso.go index 8104f63627f748..d7e1ea06929d3d 100644 --- a/src/runtime/os_linux_novdso.go +++ b/src/runtime/os_linux_novdso.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !386 && !amd64 && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le -// +build linux,!386,!amd64,!arm,!arm64,!mips64,!mips64le,!ppc64,!ppc64le +//go:build linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !riscv64 && !s390x package runtime diff --git a/src/runtime/os_linux_ppc64x.go b/src/runtime/os_linux_ppc64x.go index c093d2ec0f5930..25d7ccc0356c16 100644 --- a/src/runtime/os_linux_ppc64x.go +++ b/src/runtime/os_linux_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package runtime diff --git a/src/runtime/os_linux_x86.go b/src/runtime/os_linux_x86.go index 5667774d82f055..c88f61fa2e99e6 100644 --- a/src/runtime/os_linux_x86.go +++ b/src/runtime/os_linux_x86.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (386 || amd64) -// +build linux -// +build 386 amd64 package runtime diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go index 6fbb3aa6947bad..bb23adff0763e1 100644 --- a/src/runtime/os_netbsd.go +++ b/src/runtime/os_netbsd.go @@ -5,8 +5,9 @@ package runtime import ( + "internal/abi" + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -77,10 +78,8 @@ func kqueue() int32 //go:noescape func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 -func pipe() (r, w int32, errno int32) func pipe2(flags int32) (r, w int32, errno int32) func closeonexec(fd int32) -func setNonblock(fd int32) const ( _ESRCH = 3 @@ -100,6 +99,9 @@ var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0) // From NetBSD's const ( + _CTL_KERN = 1 + _KERN_OSREV = 3 + _CTL_HW = 6 _HW_NCPU = 3 _HW_PAGESIZE = 7 @@ -137,22 +139,29 @@ func getPageSize() uintptr { return 0 } +func getOSRev() int { + if osrev, ok := sysctlInt([]uint32{_CTL_KERN, _KERN_OSREV}); ok { + return int(osrev) + } + return 0 +} + //go:nosplit func semacreate(mp *m) { } //go:nosplit func semasleep(ns int64) int32 { - _g_ := getg() + gp := getg() var deadline int64 if ns >= 0 { deadline = nanotime() + ns } for { - v := atomic.Load(&_g_.m.waitsemacount) + v := atomic.Load(&gp.m.waitsemacount) if v > 0 { - if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { + if atomic.Cas(&gp.m.waitsemacount, v, v-1) { return 0 // semaphore acquired } continue @@ -169,7 +178,7 @@ func semasleep(ns int64) int32 { ts.setNsec(wait) tsp = &ts } - ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil) + ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&gp.m.waitsemacount), nil) if ret == _ETIMEDOUT { return -1 } @@ -192,6 +201,7 @@ func semawakeup(mp *m) { } // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) @@ -215,7 +225,7 @@ func newosproc(mp *m) { var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, funcPC(netbsdMstart)) + lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, abi.FuncPCABI0(netbsdMstart)) ret := lwp_create(unsafe.Pointer(&uc), _LWP_DETACHED, unsafe.Pointer(&mp.procid)) sigprocmask(_SIG_SETMASK, &oset, nil) @@ -239,6 +249,7 @@ func netbsdMstart() // baroque to remove a signal stack here only to add one in minit, but // it's a simple change that keeps NetBSD working like other OS's. // At this point all signals are blocked, so there is no race. +// //go:nosplit func netbsdMstart0() { st := stackt{ss_flags: _SS_DISABLE} @@ -251,6 +262,7 @@ func osinit() { if physPageSize == 0 { physPageSize = getPageSize() } + needSysmonWorkaround = getOSRev() < 902000000 // NetBSD 9.2 } var urandom_dev = []byte("/dev/urandom\x00") @@ -277,8 +289,8 @@ func mpreinit(mp *m) { // Called to initialize a new m (including the bootstrap m). // Called on the new thread, cannot allocate memory. func minit() { - _g_ := getg() - _g_.m.procid = uint64(lwp_self()) + gp := getg() + gp.m.procid = uint64(lwp_self()) // On NetBSD a thread created by pthread_create inherits the // signal stack of the creating thread. We always create a @@ -287,13 +299,14 @@ func minit() { // created in C that calls sigaltstack and then calls a Go // function, because we will lose track of the C code's // sigaltstack, but it's the best we can do. - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true + signalstack(&gp.m.gsignal.stack) + gp.m.newSigstack = true minitSignalMask() } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { unminitSignals() @@ -318,8 +331,8 @@ func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go + fn = abi.FuncPCABI0(sigtramp) } sa.sa_sigaction = fn sigaction(i, &sa, nil) @@ -340,6 +353,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { s.ss_sp = sp @@ -359,6 +373,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + func sysargs(argc int32, argv **byte) { n := argc + 1 @@ -371,7 +398,7 @@ func sysargs(argc int32, argv **byte) { n++ // now argv+n is auxv - auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize)) sysauxv(auxv[:]) } @@ -403,3 +430,12 @@ func raise(sig uint32) { func signalM(mp *m, sig int) { lwp_kill(int32(mp.procid), sig) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_netbsd_386.go b/src/runtime/os_netbsd_386.go index 037f7e36dc6ae7..ac89b9852c7f64 100644 --- a/src/runtime/os_netbsd_386.go +++ b/src/runtime/os_netbsd_386.go @@ -4,11 +4,14 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_EIP] = uint32(funcPC(lwp_tramp)) + mc.__gregs[_REG_EIP] = uint32(abi.FuncPCABI0(lwp_tramp)) mc.__gregs[_REG_UESP] = uint32(uintptr(stk)) mc.__gregs[_REG_EBX] = uint32(uintptr(unsafe.Pointer(mp))) mc.__gregs[_REG_EDX] = uint32(uintptr(unsafe.Pointer(gp))) diff --git a/src/runtime/os_netbsd_amd64.go b/src/runtime/os_netbsd_amd64.go index 5118b0c4ffda78..74eea0ceabc4d9 100644 --- a/src/runtime/os_netbsd_amd64.go +++ b/src/runtime/os_netbsd_amd64.go @@ -4,11 +4,14 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_RIP] = uint64(funcPC(lwp_tramp)) + mc.__gregs[_REG_RIP] = uint64(abi.FuncPCABI0(lwp_tramp)) mc.__gregs[_REG_RSP] = uint64(uintptr(stk)) mc.__gregs[_REG_R8] = uint64(uintptr(unsafe.Pointer(mp))) mc.__gregs[_REG_R9] = uint64(uintptr(unsafe.Pointer(gp))) diff --git a/src/runtime/os_netbsd_arm.go b/src/runtime/os_netbsd_arm.go index b5ec23e45b0a88..5fb4e08d66a915 100644 --- a/src/runtime/os_netbsd_arm.go +++ b/src/runtime/os_netbsd_arm.go @@ -4,11 +4,14 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_R15] = uint32(funcPC(lwp_tramp)) + mc.__gregs[_REG_R15] = uint32(abi.FuncPCABI0(lwp_tramp)) mc.__gregs[_REG_R13] = uint32(uintptr(stk)) mc.__gregs[_REG_R0] = uint32(uintptr(unsafe.Pointer(mp))) mc.__gregs[_REG_R1] = uint32(uintptr(unsafe.Pointer(gp))) diff --git a/src/runtime/os_netbsd_arm64.go b/src/runtime/os_netbsd_arm64.go index 8d21b0a430f9ab..2dda9c9274d73b 100644 --- a/src/runtime/os_netbsd_arm64.go +++ b/src/runtime/os_netbsd_arm64.go @@ -4,11 +4,14 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_ELR] = uint64(funcPC(lwp_tramp)) + mc.__gregs[_REG_ELR] = uint64(abi.FuncPCABI0(lwp_tramp)) mc.__gregs[_REG_X31] = uint64(uintptr(stk)) mc.__gregs[_REG_X0] = uint64(uintptr(unsafe.Pointer(mp))) mc.__gregs[_REG_X1] = uint64(uintptr(unsafe.Pointer(mp.g0))) diff --git a/src/runtime/os_nonopenbsd.go b/src/runtime/os_nonopenbsd.go index 6134b6c02f2068..a5775961e8fa87 100644 --- a/src/runtime/os_nonopenbsd.go +++ b/src/runtime/os_nonopenbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !openbsd -// +build !openbsd package runtime diff --git a/src/runtime/os_only_solaris.go b/src/runtime/os_only_solaris.go index 3829683c807186..0c72500674f72e 100644 --- a/src/runtime/os_only_solaris.go +++ b/src/runtime/os_only_solaris.go @@ -5,7 +5,6 @@ // Solaris code that doesn't also apply to illumos. //go:build !illumos -// +build !illumos package runtime diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 6259b96c22b84e..7af6c4afe746c1 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/abi" "runtime/internal/atomic" "unsafe" ) @@ -50,6 +51,21 @@ func sysctlInt(mib []uint32) (int32, bool) { return out, true } +func sysctlUint64(mib []uint32) (uint64, bool) { + var out uint64 + nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret < 0 { + return 0, false + } + return out, true +} + +//go:linkname internal_cpu_sysctlUint64 internal/cpu.sysctlUint64 +func internal_cpu_sysctlUint64(mib []uint32) (uint64, bool) { + return sysctlUint64(mib) +} + func getncpu() int32 { // Try hw.ncpuonline first because hw.ncpu would report a number twice as // high as the actual CPUs running on OpenBSD 6.4 with hyperthreading @@ -83,7 +99,7 @@ func semacreate(mp *m) { //go:nosplit func semasleep(ns int64) int32 { - _g_ := getg() + gp := getg() // Compute sleep deadline. var tsp *timespec @@ -94,9 +110,9 @@ func semasleep(ns int64) int32 { } for { - v := atomic.Load(&_g_.m.waitsemacount) + v := atomic.Load(&gp.m.waitsemacount) if v > 0 { - if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { + if atomic.Cas(&gp.m.waitsemacount, v, v-1) { return 0 // semaphore acquired } continue @@ -109,7 +125,7 @@ func semasleep(ns int64) int32 { // 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) + ret := thrsleep(uintptr(unsafe.Pointer(&gp.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &gp.m.waitsemacount) if ret == _EWOULDBLOCK { return -1 } @@ -167,6 +183,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { unminitSignals() @@ -191,8 +208,8 @@ func setsig(i uint32, fn uintptr) { var sa sigactiont sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART sa.sa_mask = uint32(sigset_all) - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) + if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go + fn = abi.FuncPCABI0(sigtramp) } sa.sa_sigaction = fn sigaction(i, &sa, nil) @@ -213,6 +230,7 @@ func getsig(i uint32) uintptr { } // setSignaltstackSP sets the ss_sp field of a stackt. +// //go:nosplit func setSignalstackSP(s *stackt, sp uintptr) { s.ss_sp = sp @@ -232,6 +250,19 @@ func sigdelset(mask *sigset, i int) { func (c *sigctxt) fixsigcode(sig uint32) { } +func setProcessCPUProfiler(hz int32) { + setProcessCPUProfilerTimer(hz) +} + +func setThreadCPUProfiler(hz int32) { + setThreadCPUProfilerHz(hz) +} + +//go:nosplit +func validSIGPROF(mp *m, c *sigctxt) bool { + return true +} + var haveMapStack = false func osStackAlloc(s *mspan) { @@ -272,3 +303,12 @@ func raise(sig uint32) { func signalM(mp *m, sig int) { thrkill(int32(mp.procid), sig) } + +// sigPerThreadSyscall is only used on linux, so we assign a bogus signal +// number. +const sigPerThreadSyscall = 1 << 31 + +//go:nosplit +func runPerThreadSyscall() { + throw("runPerThreadSyscall only valid on linux") +} diff --git a/src/runtime/os_openbsd_libc.go b/src/runtime/os_openbsd_libc.go index 0a342e553398a6..4ad2a061bd88d0 100644 --- a/src/runtime/os_openbsd_libc.go +++ b/src/runtime/os_openbsd_libc.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package runtime import ( + "internal/abi" "unsafe" ) @@ -17,6 +17,7 @@ var failThreadCreate = []byte("runtime: failed to create new OS thread\n") func mstart_stub() // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrierrec func newosproc(mp *m) { if false { @@ -48,7 +49,7 @@ func newosproc(mp *m) { // setup and then calls mstart. var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - err := pthread_create(&attr, funcPC(mstart_stub), unsafe.Pointer(mp)) + err := pthread_create(&attr, abi.FuncPCABI0(mstart_stub), unsafe.Pointer(mp)) sigprocmask(_SIG_SETMASK, &oset, nil) if err != 0 { write(2, unsafe.Pointer(&failThreadCreate[0]), int32(len(failThreadCreate))) diff --git a/src/runtime/os_openbsd_syscall.go b/src/runtime/os_openbsd_syscall.go index 3cdcb6c707bfe2..9d67a7ebbdaec4 100644 --- a/src/runtime/os_openbsd_syscall.go +++ b/src/runtime/os_openbsd_syscall.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build openbsd && mips64 -// +build openbsd,mips64 package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -16,6 +16,7 @@ import ( func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32 // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) @@ -28,12 +29,12 @@ func newosproc(mp *m) { param := tforkt{ tf_tcb: unsafe.Pointer(&mp.tls[0]), tf_tid: nil, // minit will record tid - tf_stack: uintptr(stk) - sys.PtrSize, + tf_stack: uintptr(stk) - goarch.PtrSize, } var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - ret := tfork(¶m, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart)) + ret := tfork(¶m, unsafe.Sizeof(param), mp, mp.g0, abi.FuncPCABI0(mstart)) sigprocmask(_SIG_SETMASK, &oset, nil) if ret < 0 { diff --git a/src/runtime/os_openbsd_syscall1.go b/src/runtime/os_openbsd_syscall1.go index c20ee8300e3001..d32894ba6a38b2 100644 --- a/src/runtime/os_openbsd_syscall1.go +++ b/src/runtime/os_openbsd_syscall1.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd && mips64 -// +build openbsd,mips64 package runtime diff --git a/src/runtime/os_openbsd_syscall2.go b/src/runtime/os_openbsd_syscall2.go index af1997131feba9..e4c9d2fe893084 100644 --- a/src/runtime/os_openbsd_syscall2.go +++ b/src/runtime/os_openbsd_syscall2.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd && mips64 -// +build openbsd,mips64 package runtime @@ -40,6 +39,7 @@ func usleep_no_g(usec uint32) { // write calls the write system call. // It returns a non-negative number of bytes written or a negative errno value. +// //go:noescape func write1(fd uintptr, p unsafe.Pointer, n int32) int32 @@ -71,7 +71,6 @@ func sigprocmask(how int32, new, old *sigset) { } } -func pipe() (r, w int32, errno int32) func pipe2(flags int32) (r, w int32, errno int32) //go:noescape @@ -96,6 +95,5 @@ func nanotime1() int64 func sigaltstack(new, old *stackt) func closeonexec(fd int32) -func setNonblock(fd int32) func walltime() (sec int64, nsec int32) diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index 4d428346f0e37d..6f4578ff486eb7 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/abi" "runtime/internal/atomic" "unsafe" ) @@ -74,13 +75,13 @@ func os_sigpipe() { } func sigpanic() { - g := getg() - if !canpanic(g) { + gp := getg() + if !canpanic() { throw("unexpected signal during runtime execution") } - note := gostringnocopy((*byte)(unsafe.Pointer(g.m.notesig))) - switch g.sig { + note := gostringnocopy((*byte)(unsafe.Pointer(gp.m.notesig))) + switch gp.sig { case _SIGRFAULT, _SIGWFAULT: i := indexNoFloat(note, "addr=") if i >= 0 { @@ -91,17 +92,17 @@ func sigpanic() { panicmem() } addr := note[i:] - g.sigcode1 = uintptr(atolwhex(addr)) - if g.sigcode1 < 0x1000 { + gp.sigcode1 = uintptr(atolwhex(addr)) + if gp.sigcode1 < 0x1000 { panicmem() } - if g.paniconfault { - panicmemAddr(g.sigcode1) + if gp.paniconfault { + panicmemAddr(gp.sigcode1) } - print("unexpected fault address ", hex(g.sigcode1), "\n") + print("unexpected fault address ", hex(gp.sigcode1), "\n") throw("fault") case _SIGTRAP: - if g.paniconfault { + if gp.paniconfault { panicmem() } throw(note) @@ -346,7 +347,7 @@ func getRandomData(r []byte) { func initsig(preinit bool) { if !preinit { - notify(unsafe.Pointer(funcPC(sigtramp))) + notify(unsafe.Pointer(abi.FuncPCABI0(sigtramp))) } } @@ -436,13 +437,16 @@ func exit(e int32) { } else { // build error string var tmp [32]byte - status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0) + sl := itoa(tmp[:len(tmp)-1], uint64(e)) + // Don't append, rely on the existing data being zero. + status = sl[:len(sl)+1] } goexitsall(&status[0]) exits(&status[0]) } // May run with m.p==nil, so write barriers are not allowed. +// //go:nowritebarrier func newosproc(mp *m) { if false { @@ -469,19 +473,19 @@ func semacreate(mp *m) { //go:nosplit func semasleep(ns int64) int { - _g_ := getg() + gp := getg() if ns >= 0 { ms := timediv(ns, 1000000, nil) if ms == 0 { ms = 1 } - ret := plan9_tsemacquire(&_g_.m.waitsemacount, ms) + ret := plan9_tsemacquire(&gp.m.waitsemacount, ms) if ret == 1 { return 0 // success } return -1 // timeout or interrupted } - for plan9_semacquire(&_g_.m.waitsemacount, 1) < 0 { + for plan9_semacquire(&gp.m.waitsemacount, 1) < 0 { // interrupted; try again (c.f. lock_sema.go) } return 0 // success @@ -505,6 +509,7 @@ func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 { 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) diff --git a/src/runtime/os_solaris.go b/src/runtime/os_solaris.go index 89129e5f1acfa1..8ac1b08f690a8a 100644 --- a/src/runtime/os_solaris.go +++ b/src/runtime/os_solaris.go @@ -179,6 +179,7 @@ func sysvicall3Err(fn *libcFunc, a1, a2, a3 uintptr) (r1, err uintptr) { } //go:nosplit +//go:cgo_unsafe_args func sysvicall4(fn *libcFunc, a1, a2, a3, a4 uintptr) uintptr { // Leave caller's PC/SP around for traceback. gp := getg() @@ -208,6 +209,7 @@ func sysvicall4(fn *libcFunc, a1, a2, a3, a4 uintptr) uintptr { } //go:nosplit +//go:cgo_unsafe_args func sysvicall5(fn *libcFunc, a1, a2, a3, a4, a5 uintptr) uintptr { // Leave caller's PC/SP around for traceback. gp := getg() @@ -237,6 +239,7 @@ func sysvicall5(fn *libcFunc, a1, a2, a3, a4, a5 uintptr) uintptr { } //go:nosplit +//go:cgo_unsafe_args func sysvicall6(fn *libcFunc, a1, a2, a3, a4, a5, a6 uintptr) uintptr { // Leave caller's PC/SP around for traceback. gp := getg() diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index f0935264ac6604..54261d6fc029ca 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -5,8 +5,9 @@ package runtime import ( + "internal/abi" + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -322,7 +323,7 @@ func monitorSuspendResume() { if powerRegisterSuspendResumeNotification == nil { return // Running on Windows 7, where we don't need it anyway. } - var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr { + var fn any = func(context uintptr, changeType uint32, setting uintptr) uintptr { for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink { if mp.resumesema != 0 { stdcall1(_SetEvent, mp.resumesema) @@ -543,7 +544,7 @@ func initLongPathSupport() { } func osinit() { - asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall)) + asmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall)) setBadSignalMsg() @@ -681,7 +682,7 @@ func goenvs() { // We call these all the way here, late in init, so that malloc works // for the callback functions these generate. - var fn interface{} = ctrlHandler + var fn any = ctrlHandler ctrlHandlerPC := compileCallback(*efaceOf(&fn), true) stdcall2(_SetConsoleCtrlHandler, ctrlHandlerPC, 1) @@ -901,12 +902,13 @@ func semacreate(mp *m) { // May run with m.p==nil, so write barriers are not allowed. This // function is called by newosproc0, so it is also required to // operate without stack guards. +// //go:nowritebarrierrec //go:nosplit func newosproc(mp *m) { // We pass 0 for the stack size to use the default for this binary. thandle := stdcall6(_CreateThread, 0, 0, - funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), + abi.FuncPCABI0(tstart_stdcall), uintptr(unsafe.Pointer(mp)), 0, 0) if thandle == 0 { @@ -929,6 +931,7 @@ func newosproc(mp *m) { // Used by the C library build mode. On Linux this function would allocate a // stack, but that's not necessary for Windows. No stack guards are present // and the GC has not been initialized, so write barriers will fail. +// //go:nowritebarrierrec //go:nosplit func newosproc0(mp *m, stk unsafe.Pointer) { @@ -1018,6 +1021,7 @@ func minit() { } // Called from dropm to undo the effect of an minit. +// //go:nosplit func unminit() { mp := getg().m @@ -1031,6 +1035,7 @@ func unminit() { // Called from exitm, but not from drop, to undo the effect of thread-owned // resources in minit, semacreate, or elsewhere. Do not take locks after calling this. +// //go:nosplit func mdestroy(mp *m) { if mp.highResTimer != 0 { @@ -1049,6 +1054,7 @@ func mdestroy(mp *m) { // Calling stdcall on os stack. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrier //go:nosplit func stdcall(fn stdFunction) uintptr { @@ -1193,8 +1199,10 @@ func ctrlHandler(_type uint32) uintptr { if sigsend(s) { if s == _SIGTERM { // Windows terminates the process after this handler returns. - // Block indefinitely to give signal handlers a chance to clean up. - stdcall1(_Sleep, uintptr(_INFINITE)) + // Block indefinitely to give signal handlers a chance to clean up, + // but make sure to be properly parked first, so the rest of the + // program can continue executing. + block() } return 1 } @@ -1303,18 +1311,13 @@ func setThreadCPUProfiler(hz int32) { atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz)) } -const preemptMSupported = GOARCH == "386" || GOARCH == "amd64" +const preemptMSupported = true // suspendLock protects simultaneous SuspendThread operations from // suspending each other. var suspendLock mutex func preemptM(mp *m) { - if !preemptMSupported { - // TODO: Implement call injection - return - } - if mp == getg().m { throw("self-preempt") } @@ -1323,7 +1326,7 @@ func preemptM(mp *m) { if !atomic.Cas(&mp.preemptExtLock, 0, 1) { // External code is running. Fail the preemption // attempt. - atomic.Xadd(&mp.preemptGen, 1) + mp.preemptGen.Add(1) return } @@ -1333,7 +1336,7 @@ func preemptM(mp *m) { // The M hasn't been minit'd yet (or was just unminit'd). unlock(&mp.threadLock) atomic.Store(&mp.preemptExtLock, 0) - atomic.Xadd(&mp.preemptGen, 1) + mp.preemptGen.Add(1) return } var thread uintptr @@ -1363,7 +1366,7 @@ func preemptM(mp *m) { atomic.Store(&mp.preemptExtLock, 0) // The thread no longer exists. This shouldn't be // possible, but just acknowledge the request. - atomic.Xadd(&mp.preemptGen, 1) + mp.preemptGen.Add(1) return } @@ -1385,19 +1388,42 @@ func preemptM(mp *m) { if gp != nil && wantAsyncPreempt(gp) { if ok, newpc := isAsyncSafePoint(gp, c.ip(), c.sp(), c.lr()); ok { // Inject call to asyncPreempt - targetPC := funcPC(asyncPreempt) + targetPC := abi.FuncPCABI0(asyncPreempt) switch GOARCH { default: throw("unsupported architecture") case "386", "amd64": // Make it look like the thread called targetPC. sp := c.sp() - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = newpc c.set_sp(sp) c.set_ip(targetPC) - } + case "arm": + // Push LR. The injected call is responsible + // for restoring LR. gentraceback is aware of + // this extra slot. See sigctxt.pushCall in + // signal_arm.go, which is similar except we + // subtract 1 from IP here. + sp := c.sp() + sp -= goarch.PtrSize + c.set_sp(sp) + *(*uint32)(unsafe.Pointer(sp)) = uint32(c.lr()) + c.set_lr(newpc - 1) + c.set_ip(targetPC) + + case "arm64": + // Push LR. The injected call is responsible + // for restoring LR. gentraceback is aware of + // this extra slot. See sigctxt.pushCall in + // signal_arm64.go. + sp := c.sp() - 16 // SP needs 16-byte alignment + c.set_sp(sp) + *(*uint64)(unsafe.Pointer(sp)) = uint64(c.lr()) + c.set_lr(newpc) + c.set_ip(targetPC) + } stdcall2(_SetThreadContext, thread, uintptr(unsafe.Pointer(c))) } } @@ -1405,7 +1431,7 @@ func preemptM(mp *m) { atomic.Store(&mp.preemptExtLock, 0) // Acknowledge the preemption. - atomic.Xadd(&mp.preemptGen, 1) + mp.preemptGen.Add(1) stdcall1(_ResumeThread, thread) stdcall1(_CloseHandle, thread) diff --git a/src/runtime/panic.go b/src/runtime/panic.go index f6c38aafcc825d..92ef96882fad86 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -5,13 +5,34 @@ package runtime import ( - "internal/abi" - "internal/goexperiment" + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" ) +// throwType indicates the current type of ongoing throw, which affects the +// amount of detail printed to stderr. Higher values include more detail. +type throwType uint32 + +const ( + // throwTypeNone means that we are not throwing. + throwTypeNone throwType = iota + + // throwTypeUser is a throw due to a problem with the application. + // + // These throws do not include runtime frames, system goroutines, or + // frame metadata. + throwTypeUser + + // throwTypeRuntime is a throw due to a problem with Go itself. + // + // These throws include as much information as possible to aid in + // debugging the runtime, including runtime frames, system goroutines, + // and frame metadata. + throwTypeRuntime +) + // We have two different ways of doing defers. The older way involves creating a // defer record at the time that a defer statement is executing and adding it to a // defer chain. This chain is inspected by the deferreturn call at all function @@ -31,7 +52,7 @@ import ( // pc should be the program counter of the compiler-generated code that // triggered this panic. func panicCheck1(pc uintptr, msg string) { - if sys.GoarchWasm == 0 && hasPrefix(funcname(findfunc(pc)), "runtime.") { + if goarch.IsWasm == 0 && hasPrefix(funcname(findfunc(pc)), "runtime.") { // Note: wasm can't tail call, so we can't get the original caller's pc. throw(msg) } @@ -85,38 +106,54 @@ func panicCheck2(err string) { // a space-minimal register calling convention. // failures in the comparisons for s[x], 0 <= x < y (y == len(s)) +// +//go:yeswritebarrierrec func goPanicIndex(x int, y int) { panicCheck1(getcallerpc(), "index out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex}) } + +//go:yeswritebarrierrec func goPanicIndexU(x uint, y int) { panicCheck1(getcallerpc(), "index out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex}) } // failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s)) +// +//go:yeswritebarrierrec func goPanicSliceAlen(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen}) } + +//go:yeswritebarrierrec func goPanicSliceAlenU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen}) } + +//go:yeswritebarrierrec func goPanicSliceAcap(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap}) } + +//go:yeswritebarrierrec func goPanicSliceAcapU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap}) } // failures in the comparisons for s[x:y], 0 <= x <= y +// +//go:yeswritebarrierrec func goPanicSliceB(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB}) } + +//go:yeswritebarrierrec func goPanicSliceBU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB}) @@ -188,6 +225,7 @@ func panicSliceConvert(x int, y int) var shiftError = error(errorString("negative shift amount")) +//go:yeswritebarrierrec func panicshift() { panicCheck1(getcallerpc(), "negative shift amount") panic(shiftError) @@ -195,6 +233,7 @@ func panicshift() { var divideError = error(errorString("integer divide by zero")) +//go:yeswritebarrierrec func panicdivide() { panicCheck2("integer divide by zero") panic(divideError) @@ -226,47 +265,27 @@ func panicmemAddr(addr uintptr) { panic(errorAddressString{msg: "invalid memory address or nil pointer dereference", addr: addr}) } -// Create a new deferred function fn with siz bytes of arguments. +// Create a new deferred function fn, which has no arguments and results. // The compiler turns a defer statement into a call to this. -//go:nosplit -func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn +func deferproc(fn func()) { gp := getg() if gp.m.curg != gp { // go code on the system stack can't defer throw("defer on system stack") } - if goexperiment.RegabiDefer && siz != 0 { - // TODO: Make deferproc just take a func(). - throw("defer with non-empty frame") - } - - // the arguments of fn are in a perilous state. The stack map - // for deferproc does not describe them. So we can't let garbage - // collection or stack copying trigger until we've copied them out - // to somewhere safe. The memmove below does that. - // Until the copy completes, we can only call nosplit routines. - sp := getcallersp() - argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn) - callerpc := getcallerpc() - - d := newdefer(siz) + d := newdefer() if d._panic != nil { throw("deferproc: d.panic != nil after newdefer") } d.link = gp._defer gp._defer = d d.fn = fn - d.pc = callerpc - d.sp = sp - switch siz { - case 0: - // Do nothing. - case sys.PtrSize: - *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp)) - default: - memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz)) - } + d.pc = getcallerpc() + // We must not be preempted between calling getcallersp and + // storing it to d.sp because getcallersp's result is a + // uintptr stack pointer. + d.sp = getcallersp() // deferproc returns 0 normally. // a deferred func that stops a panic @@ -280,12 +299,10 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn } // deferprocStack queues a new deferred function with a defer record on the stack. -// The defer record must have its siz and fn fields initialized. +// The defer record must have its fn field initialized. // All other fields can contain junk. -// The defer record must be immediately followed in memory by -// the arguments of the defer. -// Nosplit because the arguments on the stack won't be scanned -// until the defer record is spliced into the gp._defer list. +// Nosplit because of the uninitialized pointer fields on the stack. +// //go:nosplit func deferprocStack(d *_defer) { gp := getg() @@ -293,10 +310,7 @@ func deferprocStack(d *_defer) { // go code on the system stack can't defer throw("defer on system stack") } - if goexperiment.RegabiDefer && d.siz != 0 { - throw("defer with non-empty frame") - } - // siz and fn are already set. + // fn is already set. // The other fields are junk on entry to deferprocStack and // are initialized here. d.started = false @@ -327,132 +341,37 @@ func deferprocStack(d *_defer) { // been set and must not be clobbered. } -// Small malloc size classes >= 16 are the multiples of 16: 16, 32, 48, 64, 80, 96, 112, 128, 144, ... -// Each P holds a pool for defers with small arg sizes. -// Assign defer allocations to pools by rounding to 16, to match malloc size classes. - -const ( - deferHeaderSize = unsafe.Sizeof(_defer{}) - minDeferAlloc = (deferHeaderSize + 15) &^ 15 - minDeferArgs = minDeferAlloc - deferHeaderSize -) - -// defer size class for arg size sz -//go:nosplit -func deferclass(siz uintptr) uintptr { - if siz <= minDeferArgs { - return 0 - } - return (siz - minDeferArgs + 15) / 16 -} - -// total size of memory block for defer with arg size sz -func totaldefersize(siz uintptr) uintptr { - if siz <= minDeferArgs { - return minDeferAlloc - } - return deferHeaderSize + siz -} - -// Ensure that defer arg sizes that map to the same defer size class -// also map to the same malloc size class. -func testdefersizes() { - var m [len(p{}.deferpool)]int32 - - for i := range m { - m[i] = -1 - } - for i := uintptr(0); ; i++ { - defersc := deferclass(i) - if defersc >= uintptr(len(m)) { - break - } - siz := roundupsize(totaldefersize(i)) - if m[defersc] < 0 { - m[defersc] = int32(siz) - continue - } - if m[defersc] != int32(siz) { - print("bad defer size class: i=", i, " siz=", siz, " defersc=", defersc, "\n") - throw("bad defer size class") - } - } -} - -// The arguments associated with a deferred call are stored -// immediately after the _defer header in memory. -//go:nosplit -func deferArgs(d *_defer) unsafe.Pointer { - if d.siz == 0 { - // Avoid pointer past the defer allocation. - return nil - } - return add(unsafe.Pointer(d), unsafe.Sizeof(*d)) -} - -// deferFunc returns d's deferred function. This is temporary while we -// support both modes of GOEXPERIMENT=regabidefer. Once we commit to -// that experiment, we should change the type of d.fn. -//go:nosplit -func deferFunc(d *_defer) func() { - if !goexperiment.RegabiDefer { - throw("requires GOEXPERIMENT=regabidefer") - } - var fn func() - *(**funcval)(unsafe.Pointer(&fn)) = d.fn - return fn -} - -var deferType *_type // type of _defer struct - -func init() { - var x interface{} - x = (*_defer)(nil) - deferType = (*(**ptrtype)(unsafe.Pointer(&x))).elem -} +// Each P holds a pool for defers. // Allocate a Defer, usually using per-P pool. // Each defer must be released with freedefer. The defer is not // added to any defer chain yet. -// -// This must not grow the stack because there may be a frame without -// stack map information when this is called. -// -//go:nosplit -func newdefer(siz int32) *_defer { +func newdefer() *_defer { var d *_defer - sc := deferclass(uintptr(siz)) - gp := getg() - if sc < uintptr(len(p{}.deferpool)) { - pp := gp.m.p.ptr() - if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil { - // Take the slow path on the system stack so - // we don't grow newdefer's stack. - systemstack(func() { - lock(&sched.deferlock) - for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil { - d := sched.deferpool[sc] - sched.deferpool[sc] = d.link - d.link = nil - pp.deferpool[sc] = append(pp.deferpool[sc], d) - } - unlock(&sched.deferlock) - }) - } - if n := len(pp.deferpool[sc]); n > 0 { - d = pp.deferpool[sc][n-1] - pp.deferpool[sc][n-1] = nil - pp.deferpool[sc] = pp.deferpool[sc][:n-1] + mp := acquirem() + pp := mp.p.ptr() + if len(pp.deferpool) == 0 && sched.deferpool != nil { + lock(&sched.deferlock) + for len(pp.deferpool) < cap(pp.deferpool)/2 && sched.deferpool != nil { + d := sched.deferpool + sched.deferpool = d.link + d.link = nil + pp.deferpool = append(pp.deferpool, d) } + unlock(&sched.deferlock) + } + if n := len(pp.deferpool); n > 0 { + d = pp.deferpool[n-1] + pp.deferpool[n-1] = nil + pp.deferpool = pp.deferpool[:n-1] } + releasem(mp) + mp, pp = nil, nil + if d == nil { - // Allocate new defer+args. - systemstack(func() { - total := roundupsize(totaldefersize(uintptr(siz))) - d = (*_defer)(mallocgc(total, deferType, true)) - }) + // Allocate new defer. + d = new(_defer) } - d.siz = siz d.heap = true return d } @@ -460,11 +379,16 @@ func newdefer(siz int32) *_defer { // Free the given defer. // The defer cannot be used after this call. // -// This must not grow the stack because there may be a frame without a -// stack map when this is called. +// This is nosplit because the incoming defer is in a perilous state. +// It's not on any defer list, so stack copying won't adjust stack +// pointers in it (namely, d.link). Hence, if we were to copy the +// stack, d could then contain a stale pointer. // //go:nosplit func freedefer(d *_defer) { + d.link = nil + // After this point we can copy the stack. + if d._panic != nil { freedeferpanic() } @@ -474,53 +398,36 @@ func freedefer(d *_defer) { if !d.heap { return } - sc := deferclass(uintptr(d.siz)) - if sc >= uintptr(len(p{}.deferpool)) { - return - } - pp := getg().m.p.ptr() - if len(pp.deferpool[sc]) == cap(pp.deferpool[sc]) { + + mp := acquirem() + pp := mp.p.ptr() + if len(pp.deferpool) == cap(pp.deferpool) { // Transfer half of local cache to the central cache. - // - // Take this slow path on the system stack so - // we don't grow freedefer's stack. - systemstack(func() { - var first, last *_defer - for len(pp.deferpool[sc]) > cap(pp.deferpool[sc])/2 { - n := len(pp.deferpool[sc]) - d := pp.deferpool[sc][n-1] - pp.deferpool[sc][n-1] = nil - pp.deferpool[sc] = pp.deferpool[sc][:n-1] - if first == nil { - first = d - } else { - last.link = d - } - last = d + var first, last *_defer + for len(pp.deferpool) > cap(pp.deferpool)/2 { + n := len(pp.deferpool) + d := pp.deferpool[n-1] + pp.deferpool[n-1] = nil + pp.deferpool = pp.deferpool[:n-1] + if first == nil { + first = d + } else { + last.link = d } - lock(&sched.deferlock) - last.link = sched.deferpool[sc] - sched.deferpool[sc] = first - unlock(&sched.deferlock) - }) + last = d + } + lock(&sched.deferlock) + last.link = sched.deferpool + sched.deferpool = first + unlock(&sched.deferlock) } - // These lines used to be simply `*d = _defer{}` but that - // started causing a nosplit stack overflow via typedmemmove. - d.siz = 0 - d.started = false - d.openDefer = false - d.sp = 0 - d.pc = 0 - d.framepc = 0 - d.varp = 0 - d.fd = nil - // d._panic and d.fn must be nil already. - // If not, we would have called freedeferpanic or freedeferfn above, - // both of which throw. - d.link = nil + *d = _defer{} - pp.deferpool[sc] = append(pp.deferpool[sc], d) + pp.deferpool = append(pp.deferpool, d) + + releasem(mp) + mp, pp = nil, nil } // Separate function so that it can split stack. @@ -535,66 +442,39 @@ func freedeferfn() { throw("freedefer with d.fn != nil") } -// Run a deferred function if there is one. +// deferreturn runs deferred functions for the caller's frame. // The compiler inserts a call to this at the end of any // function which calls defer. -// If there is a deferred function, this will call runtime·jmpdefer, -// which will jump to the deferred function such that it appears -// to have been called by the caller of deferreturn at the point -// just before deferreturn was called. The effect is that deferreturn -// is called again and again until there are no more deferred functions. -// -// Declared as nosplit, because the function should not be preempted once we start -// modifying the caller's frame in order to reuse the frame to call the deferred -// function. -// -//go:nosplit func deferreturn() { gp := getg() - d := gp._defer - if d == nil { - return - } - sp := getcallersp() - if d.sp != sp { - return - } - if d.openDefer { - done := runOpenDeferFrame(gp, d) - if !done { - throw("unfinished open-coded defers in deferreturn") + for { + d := gp._defer + if d == nil { + return } + sp := getcallersp() + if d.sp != sp { + return + } + if d.openDefer { + done := runOpenDeferFrame(d) + if !done { + throw("unfinished open-coded defers in deferreturn") + } + gp._defer = d.link + freedefer(d) + // If this frame uses open defers, then this + // must be the only defer record for the + // frame, so we can just return. + return + } + + fn := d.fn + d.fn = nil gp._defer = d.link freedefer(d) - return + fn() } - - // Moving arguments around. - // - // Everything called after this point must be recursively - // nosplit because the garbage collector won't know the form - // of the arguments until the jmpdefer can flip the PC over to - // fn. - argp := getcallersp() + sys.MinFrameSize - switch d.siz { - case 0: - // Do nothing. - case sys.PtrSize: - *(*uintptr)(unsafe.Pointer(argp)) = *(*uintptr)(deferArgs(d)) - default: - memmove(unsafe.Pointer(argp), deferArgs(d), uintptr(d.siz)) - } - fn := d.fn - d.fn = nil - gp._defer = d.link - freedefer(d) - // If the defer function pointer is nil, force the seg fault to happen - // here rather than in jmpdefer. gentraceback() throws an error if it is - // called with a callback on an LR architecture and jmpdefer is on the - // stack, because the stack trace can be incorrect in that case - see - // issue #8153). - _ = fn.fn - jmpdefer(fn, argp) } // Goexit terminates the goroutine that calls it. No other goroutine is affected. @@ -639,7 +519,7 @@ func Goexit() { d.started = true d._panic = (*_panic)(noescape(unsafe.Pointer(&p))) if d.openDefer { - done := runOpenDeferFrame(gp, d) + done := runOpenDeferFrame(d) if !done { // We should always run all defers in the frame, // since there is no panic associated with this @@ -655,15 +535,9 @@ func Goexit() { addOneOpenDeferFrame(gp, 0, nil) } } else { - if goexperiment.RegabiDefer { - // Save the pc/sp in deferCallSave(), so we can "recover" back to this - // loop if necessary. - deferCallSave(&p, deferFunc(d)) - } else { - // Save the pc/sp in reflectcallSave(), so we can "recover" back to this - // loop if necessary. - reflectcallSave(&p, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz)) - } + // Save the pc/sp in deferCallSave(), so we can "recover" back to this + // loop if necessary. + deferCallSave(&p, d.fn) } if p.aborted { // We had a recursive panic in the defer d we started, and @@ -691,8 +565,14 @@ func Goexit() { // Used when crashing with panicking. func preprintpanics(p *_panic) { defer func() { - if recover() != nil { - throw("panic while printing panic value") + text := "panic while printing panic value" + switch r := recover().(type) { + case nil: + // nothing to do + case string: + throw(text + ": " + r) + default: + throw(text + ": type " + efaceOf(&r)._type.string()) } }() for p != nil { @@ -726,14 +606,28 @@ func printpanics(p *_panic) { print("\n") } -// addOneOpenDeferFrame scans the stack for the first frame (if any) with -// open-coded defers and if it finds one, adds a single record to the defer chain -// for that frame. If sp is non-nil, it starts the stack scan from the frame -// specified by sp. If sp is nil, it uses the sp from the current defer record -// (which has just been finished). Hence, it continues the stack scan from the -// frame of the defer that just finished. It skips any frame that already has an -// open-coded _defer record, which would have been created from a previous -// (unrecovered) panic. +// addOneOpenDeferFrame scans the stack (in gentraceback order, from inner frames to +// outer frames) for the first frame (if any) with open-coded defers. If it finds +// one, it adds a single entry to the defer chain for that frame. The entry added +// represents all the defers in the associated open defer frame, and is sorted in +// order with respect to any non-open-coded defers. +// +// addOneOpenDeferFrame stops (possibly without adding a new entry) if it encounters +// an in-progress open defer entry. An in-progress open defer entry means there has +// been a new panic because of a defer in the associated frame. addOneOpenDeferFrame +// does not add an open defer entry past a started entry, because that started entry +// still needs to finished, and addOneOpenDeferFrame will be called when that started +// entry is completed. The defer removal loop in gopanic() similarly stops at an +// in-progress defer entry. Together, addOneOpenDeferFrame and the defer removal loop +// ensure the invariant that there is no open defer entry further up the stack than +// an in-progress defer, and also that the defer removal loop is guaranteed to remove +// all not-in-progress open defer entries from the defer chain. +// +// If sp is non-nil, addOneOpenDeferFrame starts the stack scan from the frame +// specified by sp. If sp is nil, it uses the sp from the current defer record (which +// has just been finished). Hence, it continues the stack scan from the frame of the +// defer that just finished. It skips any frame that already has a (not-in-progress) +// open-coded _defer record in the defer chain. // // Note: All entries of the defer chain (including this new open-coded entry) have // their pointers (including sp) adjusted properly if the stack moves while @@ -774,6 +668,16 @@ func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) { if !d.openDefer { throw("duplicated defer entry") } + // Don't add any record past an + // in-progress defer entry. We don't + // need it, and more importantly, we + // want to keep the invariant that + // there is no open defer entry + // passed an in-progress entry (see + // header comment). + if d.started { + return false + } return true } prev = d @@ -783,8 +687,7 @@ func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) { throw("missing deferreturn") } - maxargsize, _ := readvarintUnsafe(fd) - d1 := newdefer(int32(maxargsize)) + d1 := newdefer() d1.openDefer = true d1._panic = nil // These are the pc/sp to set after we've @@ -793,7 +696,7 @@ func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) { // deferreturn that runs any remaining // defers and then returns from the // function. - d1.pc = frame.fn.entry + uintptr(frame.fn.deferreturn) + d1.pc = frame.fn.entry() + uintptr(frame.fn.deferreturn) d1.varp = frame.varp d1.fd = fd // Save the SP/PC associated with current frame, @@ -841,61 +744,33 @@ func readvarintUnsafe(fd unsafe.Pointer) (uint32, unsafe.Pointer) { // d. It normally processes all active defers in the frame, but stops immediately // if a defer does a successful recover. It returns true if there are no // remaining defers to run in the frame. -func runOpenDeferFrame(gp *g, d *_defer) bool { +func runOpenDeferFrame(d *_defer) bool { done := true fd := d.fd - // Skip the maxargsize - _, fd = readvarintUnsafe(fd) deferBitsOffset, fd := readvarintUnsafe(fd) nDefers, fd := readvarintUnsafe(fd) deferBits := *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) for i := int(nDefers) - 1; i >= 0; i-- { // read the funcdata info for this defer - var argWidth, closureOffset, nArgs uint32 - argWidth, fd = readvarintUnsafe(fd) + var closureOffset uint32 closureOffset, fd = readvarintUnsafe(fd) - nArgs, fd = readvarintUnsafe(fd) - if goexperiment.RegabiDefer && argWidth != 0 { - throw("defer with non-empty frame") - } if deferBits&(1<0 has the side-effect of disabling this G's writebuf. - _g_.m.dying = 1 - atomic.Xadd(&panicking, 1) + gp.m.dying = 1 + panicking.Add(1) lock(&paniclk) if debug.schedtrace > 0 || debug.scheddetail > 0 { schedtrace(true) @@ -1348,13 +1218,13 @@ func startpanic_m() bool { case 1: // Something failed while panicking. // Just print a stack trace and exit. - _g_.m.dying = 2 + gp.m.dying = 2 print("panic during panic\n") return false case 2: // This is a genuine bug in the runtime, we couldn't even // print the stack trace successfully. - _g_.m.dying = 3 + gp.m.dying = 3 print("stack trace unavailable\n") exit(4) fallthrough @@ -1368,6 +1238,8 @@ func startpanic_m() bool { var didothers bool var deadlock mutex +// gp is the crashing g running on this M, but may be a user G, while getg() is +// always g0. func dopanic_m(gp *g, pc, sp uintptr) bool { if gp.sig != 0 { signame := signame(gp.sig) @@ -1380,7 +1252,6 @@ func dopanic_m(gp *g, pc, sp uintptr) bool { } level, all, docrash := gotraceback() - _g_ := getg() if level > 0 { if gp != gp.m.curg { all = true @@ -1389,7 +1260,7 @@ func dopanic_m(gp *g, pc, sp uintptr) bool { print("\n") goroutineheader(gp) traceback(pc, sp, 0, gp) - } else if level >= 2 || _g_.m.throwing > 0 { + } else if level >= 2 || gp.m.throwing >= throwTypeRuntime { print("\nruntime stack:\n") traceback(pc, sp, 0, gp) } @@ -1400,7 +1271,7 @@ func dopanic_m(gp *g, pc, sp uintptr) bool { } unlock(&paniclk) - if atomic.Xadd(&panicking, -1) != 0 { + if panicking.Add(-1) != 0 { // Some other m is panicking too. // Let it print what it needs to print. // Wait forever without chewing up cpu. @@ -1418,29 +1289,32 @@ func dopanic_m(gp *g, pc, sp uintptr) bool { // panicking. // //go:nosplit -func canpanic(gp *g) bool { - // Note that g is m->gsignal, different from gp. - // Note also that g->m can change at preemption, so m can go stale - // if this function ever makes a function call. - _g_ := getg() - _m_ := _g_.m +func canpanic() bool { + gp := getg() + mp := acquirem() // Is it okay for gp to panic instead of crashing the program? // Yes, as long as it is running Go code, not runtime code, // and not stuck in a system call. - if gp == nil || gp != _m_.curg { + if gp != mp.curg { + releasem(mp) return false } - if _m_.locks != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { + // N.B. mp.locks != 1 instead of 0 to account for acquirem. + if mp.locks != 1 || mp.mallocing != 0 || mp.throwing != throwTypeNone || mp.preemptoff != "" || mp.dying != 0 { + releasem(mp) return false } status := readgstatus(gp) if status&^_Gscan != _Grunning || gp.syscallsp != 0 { + releasem(mp) return false } - if GOOS == "windows" && _m_.libcallsp != 0 { + if GOOS == "windows" && mp.libcallsp != 0 { + releasem(mp) return false } + releasem(mp) return true } diff --git a/src/runtime/panic32.go b/src/runtime/panic32.go index acbdd1ff458d62..fa3f2bf2f8bdf1 100644 --- a/src/runtime/panic32.go +++ b/src/runtime/panic32.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 || arm || mips || mipsle -// +build 386 arm mips mipsle package runtime diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go index cd7fc5f8489a98..a61dcc3b5d2d9e 100644 --- a/src/runtime/plugin.go +++ b/src/runtime/plugin.go @@ -7,7 +7,7 @@ package runtime import "unsafe" //go:linkname plugin_lastmoduleinit plugin.lastmoduleinit -func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr string) { +func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) { var md *moduledata for pmd := firstmoduledata.next; pmd != nil; pmd = pmd.next { if pmd.bad { @@ -76,11 +76,11 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr s // Because functions are handled specially in the plugin package, // function symbol names are prefixed here with '.' to avoid // a dependency on the reflect package. - syms = make(map[string]interface{}, len(md.ptab)) + syms = make(map[string]any, len(md.ptab)) for _, ptab := range md.ptab { symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name) t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ) - var val interface{} + var val any valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&val)) (*valp)[0] = unsafe.Pointer(t) @@ -96,7 +96,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, errstr s func pluginftabverify(md *moduledata) { badtable := false for i := 0; i < len(md.ftab); i++ { - entry := md.ftab[i].entry + entry := md.textAddr(md.ftab[i].entryoff) if md.minpc <= entry && entry <= md.maxpc { continue } @@ -112,7 +112,7 @@ func pluginftabverify(md *moduledata) { f2 := findfunc(entry) if f2.valid() { name2 = funcname(f2) - entry2 = f2.entry + entry2 = f2.entry() } badtable = true println("ftab entry", hex(entry), "/", hex(entry2), ": ", diff --git a/src/runtime/pprof/label.go b/src/runtime/pprof/label.go index b614f1254491e2..0c58a7ac47e5a0 100644 --- a/src/runtime/pprof/label.go +++ b/src/runtime/pprof/label.go @@ -37,7 +37,7 @@ func labelValue(ctx context.Context) labelMap { // that admits incremental immutable modification more efficiently. type labelMap map[string]string -// String statisfies Stringer and returns key, value pairs in a consistent +// String satisfies Stringer and returns key, value pairs in a consistent // order. func (l *labelMap) String() string { if l == nil { diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go index b4680fbdee99ec..391588d4acd0ec 100644 --- a/src/runtime/pprof/mprof_test.go +++ b/src/runtime/pprof/mprof_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package pprof @@ -18,7 +17,7 @@ import ( "unsafe" ) -var memSink interface{} +var memSink any func allocateTransient1M() { for i := 0; i < 1024; i++ { @@ -86,17 +85,6 @@ func TestMemoryProfiler(t *testing.T) { runtime.GC() // materialize stats - // TODO(mknyszek): Fix #45315 and remove this extra call. - // - // Unfortunately, it's possible for the sweep termination condition - // to flap, so with just one runtime.GC call, a freed object could be - // missed, leading this test to fail. A second call reduces the chance - // of this happening to zero, because sweeping actually has to finish - // to move on to the next GC, during which nothing will happen. - // - // See #46500 for more details. - runtime.GC() - memoryProfilerRun++ tests := []struct { @@ -105,31 +93,31 @@ func TestMemoryProfiler(t *testing.T) { }{{ stk: []string{"runtime/pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"}, legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ -# 0x[0-9,a-f]+ runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:48 -# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test\.go:83 +# 0x[0-9,a-f]+ runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test\.go:47 +# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test\.go:82 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun), }, { stk: []string{"runtime/pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"}, legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ -# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:25 -# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:80 +# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:24 +# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:79 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun), }, { stk: []string{"runtime/pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"}, legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ -# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:31 -# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:81 +# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:30 +# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:80 `, memoryProfilerRun, (2<<20)*memoryProfilerRun), }, { stk: []string{"runtime/pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"}, legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ -# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:35 -# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:82 +# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:34 +# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:81 `, memoryProfilerRun, (2<<20)*memoryProfilerRun), }, { stk: []string{"runtime/pprof.allocateReflectTransient"}, legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @( 0x[0-9,a-f]+)+ -# 0x[0-9,a-f]+ runtime/pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*/runtime/pprof/mprof_test.go:56 +# 0x[0-9,a-f]+ runtime/pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:55 `, memoryProfilerRun, (2<<20)*memoryProfilerRun), }} diff --git a/src/runtime/pprof/pe.go b/src/runtime/pprof/pe.go new file mode 100644 index 00000000000000..41054585e9887e --- /dev/null +++ b/src/runtime/pprof/pe.go @@ -0,0 +1,19 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pprof + +import "os" + +// peBuildID returns a best effort unique ID for the named executable. +// +// It would be wasteful to calculate the hash of the whole file, +// instead use the binary name and the last modified time for the buildid. +func peBuildID(file string) string { + s, err := os.Stat(file) + if err != nil { + return file + } + return file + s.ModTime().String() +} diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index 99eda10f1c1ada..3a7191e092e7b9 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -5,7 +5,7 @@ // Package pprof writes runtime profiling data in the format expected // by the pprof visualization tool. // -// Profiling a Go program +// # Profiling a Go program // // The first step to profiling a Go program is to enable profiling. // Support for profiling benchmarks built with the standard testing @@ -13,54 +13,54 @@ // runs benchmarks in the current directory and writes the CPU and // memory profiles to cpu.prof and mem.prof: // -// go test -cpuprofile cpu.prof -memprofile mem.prof -bench . +// go test -cpuprofile cpu.prof -memprofile mem.prof -bench . // // To add equivalent profiling support to a standalone program, add // code like the following to your main function: // -// var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") -// var memprofile = flag.String("memprofile", "", "write memory profile to `file`") +// var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") +// var memprofile = flag.String("memprofile", "", "write memory profile to `file`") // -// func main() { -// flag.Parse() -// if *cpuprofile != "" { -// f, err := os.Create(*cpuprofile) -// if err != nil { -// log.Fatal("could not create CPU profile: ", err) -// } -// defer f.Close() // error handling omitted for example -// if err := pprof.StartCPUProfile(f); err != nil { -// log.Fatal("could not start CPU profile: ", err) -// } -// defer pprof.StopCPUProfile() -// } +// func main() { +// flag.Parse() +// if *cpuprofile != "" { +// f, err := os.Create(*cpuprofile) +// if err != nil { +// log.Fatal("could not create CPU profile: ", err) +// } +// defer f.Close() // error handling omitted for example +// if err := pprof.StartCPUProfile(f); err != nil { +// log.Fatal("could not start CPU profile: ", err) +// } +// defer pprof.StopCPUProfile() +// } // -// // ... rest of the program ... +// // ... rest of the program ... // -// if *memprofile != "" { -// f, err := os.Create(*memprofile) -// if err != nil { -// log.Fatal("could not create memory profile: ", err) -// } -// defer f.Close() // error handling omitted for example -// runtime.GC() // get up-to-date statistics -// if err := pprof.WriteHeapProfile(f); err != nil { -// log.Fatal("could not write memory profile: ", err) -// } -// } -// } +// if *memprofile != "" { +// f, err := os.Create(*memprofile) +// if err != nil { +// log.Fatal("could not create memory profile: ", err) +// } +// defer f.Close() // error handling omitted for example +// runtime.GC() // get up-to-date statistics +// if err := pprof.WriteHeapProfile(f); err != nil { +// log.Fatal("could not write memory profile: ", err) +// } +// } +// } // // There is also a standard HTTP interface to profiling data. Adding // the following line will install handlers under the /debug/pprof/ // URL to download live profiles: // -// import _ "net/http/pprof" +// import _ "net/http/pprof" // // See the net/http/pprof package for more details. // // Profiles can then be visualized with the pprof tool: // -// go tool pprof cpu.prof +// go tool pprof cpu.prof // // There are many commands available from the pprof command line. // Commonly used commands include "top", which prints a summary of the @@ -74,8 +74,8 @@ package pprof import ( "bufio" - "bytes" "fmt" + "internal/abi" "io" "runtime" "sort" @@ -129,11 +129,10 @@ import ( // The CPU profile is not available as a Profile. It has a special API, // the StartCPUProfile and StopCPUProfile functions, because it streams // output to a writer during profiling. -// type Profile struct { name string mu sync.Mutex - m map[interface{}][]uintptr + m map[any][]uintptr count func() int write func(io.Writer, int) error } @@ -216,7 +215,7 @@ func NewProfile(name string) *Profile { } p := &Profile{ name: name, - m: map[interface{}][]uintptr{}, + m: map[any][]uintptr{}, } profiles.m[name] = p return p @@ -275,8 +274,7 @@ func (p *Profile) Count() int { // // Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient. // Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run. -// -func (p *Profile) Add(value interface{}, skip int) { +func (p *Profile) Add(value any, skip int) { if p.name == "" { panic("pprof: use of uninitialized Profile") } @@ -289,7 +287,7 @@ func (p *Profile) Add(value interface{}, skip int) { stk = stk[:n] if len(stk) == 0 { // The value for skip is too large, and there's no stack trace to record. - stk = []uintptr{funcPC(lostProfileEvent)} + stk = []uintptr{abi.FuncPCABIInternal(lostProfileEvent)} } p.mu.Lock() @@ -302,7 +300,7 @@ func (p *Profile) Add(value interface{}, skip int) { // Remove removes the execution stack associated with value from the profile. // It is a no-op if the value is not in the profile. -func (p *Profile) Remove(value interface{}) { +func (p *Profile) Remove(value any) { p.mu.Lock() defer p.mu.Unlock() delete(p.m, value) @@ -403,7 +401,7 @@ func printCountCycleProfile(w io.Writer, countName, cycleName string, scaler fun // The profile will be in compressed proto format unless debug is nonzero. func printCountProfile(w io.Writer, debug int, name string, p countProfile) error { // Build count of each stack. - var buf bytes.Buffer + var buf strings.Builder key := func(stk []uintptr, lbls *labelMap) string { buf.Reset() fmt.Fprintf(&buf, "@") diff --git a/src/runtime/pprof/pprof_norusage.go b/src/runtime/pprof/pprof_norusage.go index e175dd380cfb62..8de38086c716ce 100644 --- a/src/runtime/pprof/pprof_norusage.go +++ b/src/runtime/pprof/pprof_norusage.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !darwin && !linux -// +build !darwin,!linux +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows package pprof diff --git a/src/runtime/pprof/pprof_rusage.go b/src/runtime/pprof/pprof_rusage.go index 269f21bc2f93c5..a3ca4c8d5dd7eb 100644 --- a/src/runtime/pprof/pprof_rusage.go +++ b/src/runtime/pprof/pprof_rusage.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin || linux -// +build darwin linux +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris package pprof @@ -18,15 +17,19 @@ import ( func addMaxRSS(w io.Writer) { var rssToBytes uintptr switch runtime.GOOS { - case "linux", "android": + case "aix", "android", "dragonfly", "freebsd", "linux", "netbsd", "openbsd": rssToBytes = 1024 case "darwin", "ios": rssToBytes = 1 + case "illumos", "solaris": + rssToBytes = uintptr(syscall.Getpagesize()) default: panic("unsupported OS") } var rusage syscall.Rusage - syscall.Getrusage(0, &rusage) - fmt.Fprintf(w, "# MaxRSS = %d\n", uintptr(rusage.Maxrss)*rssToBytes) + err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) + if err == nil { + fmt.Fprintf(w, "# MaxRSS = %d\n", uintptr(rusage.Maxrss)*rssToBytes) + } } diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 7c71d8263be8b8..79febc4285cb27 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js package pprof @@ -11,7 +10,9 @@ import ( "bytes" "context" "fmt" + "internal/abi" "internal/profile" + "internal/syscall/unix" "internal/testenv" "io" "math" @@ -20,6 +21,7 @@ import ( "os/exec" "regexp" "runtime" + "runtime/debug" "strings" "sync" "sync/atomic" @@ -88,14 +90,16 @@ func avoidFunctions() []string { } func TestCPUProfile(t *testing.T) { - testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions(), func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions()) + testCPUProfile(t, matches, func(dur time.Duration) { cpuHogger(cpuHog1, &salt1, dur) }) } func TestCPUProfileMultithreaded(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) - testCPUProfile(t, stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, avoidFunctions(), func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1", "runtime/pprof.cpuHog2"}, avoidFunctions()) + testCPUProfile(t, matches, func(dur time.Duration) { c := make(chan int) go func() { cpuHogger(cpuHog1, &salt1, dur) @@ -106,17 +110,148 @@ func TestCPUProfileMultithreaded(t *testing.T) { }) } +func TestCPUProfileMultithreadMagnitude(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("issue 35057 is only confirmed on Linux") + } + + // Linux [5.9,5.16) has a kernel bug that can break CPU timers on newly + // created threads, breaking our CPU accounting. + major, minor := unix.KernelVersion() + t.Logf("Running on Linux %d.%d", major, minor) + defer func() { + if t.Failed() { + t.Logf("Failure of this test may indicate that your system suffers from a known Linux kernel bug fixed on newer kernels. See https://golang.org/issue/49065.") + } + }() + + // Disable on affected builders to avoid flakiness, but otherwise keep + // it enabled to potentially warn users that they are on a broken + // kernel. + if testenv.Builder() != "" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64") { + have59 := major > 5 || (major == 5 && minor >= 9) + have516 := major > 5 || (major == 5 && minor >= 16) + if have59 && !have516 { + testenv.SkipFlaky(t, 49065) + } + } + + // Run a workload in a single goroutine, then run copies of the same + // workload in several goroutines. For both the serial and parallel cases, + // the CPU time the process measures with its own profiler should match the + // total CPU usage that the OS reports. + // + // We could also check that increases in parallelism (GOMAXPROCS) lead to a + // linear increase in the CPU usage reported by both the OS and the + // profiler, but without a guarantee of exclusive access to CPU resources + // that is likely to be a flaky test. + + // Require the smaller value to be within 10%, or 40% in short mode. + maxDiff := 0.10 + if testing.Short() { + maxDiff = 0.40 + } + + compare := func(a, b time.Duration, maxDiff float64) error { + if a <= 0 || b <= 0 { + return fmt.Errorf("Expected both time reports to be positive") + } + + if a < b { + a, b = b, a + } + + diff := float64(a-b) / float64(a) + if diff > maxDiff { + return fmt.Errorf("CPU usage reports are too different (limit -%.1f%%, got -%.1f%%)", maxDiff*100, diff*100) + } + + return nil + } + + for _, tc := range []struct { + name string + workers int + }{ + { + name: "serial", + workers: 1, + }, + { + name: "parallel", + workers: runtime.GOMAXPROCS(0), + }, + } { + // check that the OS's perspective matches what the Go runtime measures. + t.Run(tc.name, func(t *testing.T) { + t.Logf("Running with %d workers", tc.workers) + + var userTime, systemTime time.Duration + matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.cpuHog1"}, avoidFunctions()) + acceptProfile := func(t *testing.T, p *profile.Profile) bool { + if !matches(t, p) { + return false + } + + ok := true + for i, unit := range []string{"count", "nanoseconds"} { + if have, want := p.SampleType[i].Unit, unit; have != want { + t.Logf("pN SampleType[%d]; %q != %q", i, have, want) + ok = false + } + } + + // cpuHog1 called below is the primary source of CPU + // load, but there may be some background work by the + // runtime. Since the OS rusage measurement will + // include all work done by the process, also compare + // against all samples in our profile. + var value time.Duration + for _, sample := range p.Sample { + value += time.Duration(sample.Value[1]) * time.Nanosecond + } + + totalTime := userTime + systemTime + t.Logf("compare %s user + %s system = %s vs %s", userTime, systemTime, totalTime, value) + if err := compare(totalTime, value, maxDiff); err != nil { + t.Logf("compare got %v want nil", err) + ok = false + } + + return ok + } + + testCPUProfile(t, acceptProfile, func(dur time.Duration) { + userTime, systemTime = diffCPUTime(t, func() { + var wg sync.WaitGroup + var once sync.Once + for i := 0; i < tc.workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + var salt = 0 + cpuHogger(cpuHog1, &salt, dur) + once.Do(func() { salt1 = salt }) + }() + } + wg.Wait() + }) + }) + }) + } +} + // containsInlinedCall reports whether the function body for the function f is // known to contain an inlined function call within the first maxBytes bytes. -func containsInlinedCall(f interface{}, maxBytes int) bool { +func containsInlinedCall(f any, maxBytes int) bool { _, found := findInlinedCall(f, maxBytes) return found } // findInlinedCall returns the PC of an inlined function call within // the function body for the function f if any. -func findInlinedCall(f interface{}, maxBytes int) (pc uint64, found bool) { - fFunc := runtime.FuncForPC(uintptr(funcPC(f))) +func findInlinedCall(f any, maxBytes int) (pc uint64, found bool) { + fFunc := runtime.FuncForPC(uintptr(abi.FuncPCABIInternal(f))) if fFunc == nil || fFunc.Entry() == 0 { panic("failed to locate function entry") } @@ -148,7 +283,8 @@ func TestCPUProfileInlining(t *testing.T) { t.Skip("Can't determine whether inlinedCallee was inlined into inlinedCaller.") } - p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions(), func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.inlinedCaller"}, avoidFunctions()) + p := testCPUProfile(t, matches, func(dur time.Duration) { cpuHogger(inlinedCaller, &salt1, dur) }) @@ -198,7 +334,8 @@ func inlinedCalleeDump(pcs []uintptr) { } func TestCPUProfileRecursion(t *testing.T) { - p := testCPUProfile(t, stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.recursionCallee", "runtime/pprof.recursionCaller"}, avoidFunctions(), func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContains, []string{"runtime/pprof.inlinedCallee", "runtime/pprof.recursionCallee", "runtime/pprof.recursionCaller"}, avoidFunctions()) + p := testCPUProfile(t, matches, func(dur time.Duration) { cpuHogger(recursionCaller, &salt1, dur) }) @@ -283,7 +420,7 @@ func cpuProfilingBroken() bool { // testCPUProfile runs f under the CPU profiler, checking for some conditions specified by need, // as interpreted by matches, and returns the parsed profile. -func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []string, f func(dur time.Duration)) *profile.Profile { +func testCPUProfile(t *testing.T, matches profileMatchFunc, f func(dur time.Duration)) *profile.Profile { switch runtime.GOOS { case "darwin": out, err := exec.Command("uname", "-a").CombinedOutput() @@ -298,10 +435,14 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri broken := cpuProfilingBroken() - maxDuration := 5 * time.Second - if testing.Short() && broken { - // If it's expected to be broken, no point waiting around. - maxDuration /= 10 + deadline, ok := t.Deadline() + if broken || !ok { + if broken && testing.Short() { + // If it's expected to be broken, no point waiting around. + deadline = time.Now().Add(1 * time.Second) + } else { + deadline = time.Now().Add(10 * time.Second) + } } // If we're running a long test, start with a long duration @@ -316,7 +457,7 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri // several others under go test std. If a test fails in a way // that could mean it just didn't run long enough, try with a // longer duration. - for duration <= maxDuration { + for { var prof bytes.Buffer if err := StartCPUProfile(&prof); err != nil { t.Fatal(err) @@ -324,14 +465,15 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri f(duration) StopCPUProfile() - if p, ok := profileOk(t, matches, need, avoid, prof, duration); ok { + if p, ok := profileOk(t, matches, prof, duration); ok { return p } duration *= 2 - if duration <= maxDuration { - t.Logf("retrying with %s duration", duration) + if time.Until(deadline) < duration { + break } + t.Logf("retrying with %s duration", duration) } if broken { @@ -349,6 +491,16 @@ func testCPUProfile(t *testing.T, matches matchFunc, need []string, avoid []stri return nil } +var diffCPUTimeImpl func(f func()) (user, system time.Duration) + +func diffCPUTime(t *testing.T, f func()) (user, system time.Duration) { + if fn := diffCPUTimeImpl; fn != nil { + return fn(f) + } + t.Fatalf("cannot measure CPU time on GOOS=%s GOARCH=%s", runtime.GOOS, runtime.GOARCH) + return 0, 0 +} + func contains(slice []string, s string) bool { for i := range slice { if slice[i] == s { @@ -370,35 +522,18 @@ func stackContains(spec string, count uintptr, stk []*profile.Location, labels m return false } -type matchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool +type sampleMatchFunc func(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool -func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, prof bytes.Buffer, duration time.Duration) (_ *profile.Profile, ok bool) { +func profileOk(t *testing.T, matches profileMatchFunc, prof bytes.Buffer, duration time.Duration) (_ *profile.Profile, ok bool) { ok = true - // Check that profile is well formed, contains 'need', and does not contain - // anything from 'avoid'. - have := make([]uintptr, len(need)) - avoidSamples := make([]uintptr, len(avoid)) var samples uintptr - var buf bytes.Buffer + var buf strings.Builder p := parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, labels map[string][]string) { fmt.Fprintf(&buf, "%d:", count) fprintStack(&buf, stk) + fmt.Fprintf(&buf, " labels: %v\n", labels) samples += count - for i, spec := range need { - if matches(spec, count, stk, labels) { - have[i] += count - } - } - for i, name := range avoid { - for _, loc := range stk { - for _, line := range loc.Line { - if strings.Contains(line.Function.Name, name) { - avoidSamples[i] += count - } - } - } - } fmt.Fprintf(&buf, "\n") }) t.Logf("total %d CPU profile samples collected:\n%s", samples, buf.String()) @@ -421,39 +556,77 @@ func profileOk(t *testing.T, matches matchFunc, need []string, avoid []string, p ok = false } - for i, name := range avoid { - bad := avoidSamples[i] - if bad != 0 { - t.Logf("found %d samples in avoid-function %s\n", bad, name) - ok = false - } + if matches != nil && !matches(t, p) { + ok = false } - if len(need) == 0 { - return p, ok - } + return p, ok +} - var total uintptr - for i, name := range need { - total += have[i] - t.Logf("%s: %d\n", name, have[i]) - } - if total == 0 { - t.Logf("no samples in expected functions") - ok = false - } - // We'd like to check a reasonable minimum, like - // total / len(have) / smallconstant, but this test is - // pretty flaky (see bug 7095). So we'll just test to - // make sure we got at least one sample. - min := uintptr(1) - for i, name := range need { - if have[i] < min { - t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have))) +type profileMatchFunc func(*testing.T, *profile.Profile) bool + +func matchAndAvoidStacks(matches sampleMatchFunc, need []string, avoid []string) profileMatchFunc { + return func(t *testing.T, p *profile.Profile) (ok bool) { + ok = true + + // Check that profile is well formed, contains 'need', and does not contain + // anything from 'avoid'. + have := make([]uintptr, len(need)) + avoidSamples := make([]uintptr, len(avoid)) + + for _, sample := range p.Sample { + count := uintptr(sample.Value[0]) + for i, spec := range need { + if matches(spec, count, sample.Location, sample.Label) { + have[i] += count + } + } + for i, name := range avoid { + for _, loc := range sample.Location { + for _, line := range loc.Line { + if strings.Contains(line.Function.Name, name) { + avoidSamples[i] += count + } + } + } + } + } + + for i, name := range avoid { + bad := avoidSamples[i] + if bad != 0 { + t.Logf("found %d samples in avoid-function %s\n", bad, name) + ok = false + } + } + + if len(need) == 0 { + return + } + + var total uintptr + for i, name := range need { + total += have[i] + t.Logf("%s: %d\n", name, have[i]) + } + if total == 0 { + t.Logf("no samples in expected functions") ok = false } + + // We'd like to check a reasonable minimum, like + // total / len(have) / smallconstant, but this test is + // pretty flaky (see bug 7095). So we'll just test to + // make sure we got at least one sample. + min := uintptr(1) + for i, name := range need { + if have[i] < min { + t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have))) + ok = false + } + } + return } - return p, ok } // Fork can hang if preempted with signals frequently enough (see issue 5517). @@ -545,7 +718,7 @@ func TestGoroutineSwitch(t *testing.T) { // The place we'd see it would be the inner most frame. name := stk[0].Line[0].Function.Name if name == "gogo" { - var buf bytes.Buffer + var buf strings.Builder fprintStack(&buf, stk) t.Fatalf("found profile entry for gogo:\n%s", buf.String()) } @@ -565,12 +738,11 @@ func fprintStack(w io.Writer, stk []*profile.Location) { } fmt.Fprintf(w, ")") } - fmt.Fprintf(w, "\n") } // Test that profiling of division operations is okay, especially on ARM. See issue 6681. func TestMathBigDivide(t *testing.T) { - testCPUProfile(t, nil, nil, nil, func(duration time.Duration) { + testCPUProfile(t, nil, func(duration time.Duration) { t := time.After(duration) pi := new(big.Int) for { @@ -599,7 +771,8 @@ func stackContainsAll(spec string, count uintptr, stk []*profile.Location, label } func TestMorestack(t *testing.T) { - testCPUProfile(t, stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions(), func(duration time.Duration) { + matches := matchAndAvoidStacks(stackContainsAll, []string{"runtime.newstack,runtime/pprof.growstack"}, avoidFunctions()) + testCPUProfile(t, matches, func(duration time.Duration) { t := time.After(duration) c := make(chan bool) for { @@ -636,7 +809,7 @@ func use(x [8 << 18]byte) {} func TestBlockProfile(t *testing.T) { type TestCase struct { name string - f func() + f func(*testing.T) stk []string re string } @@ -651,9 +824,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ runtime\.chanrecv1\+0x[0-9a-f]+ .*/src/runtime/chan.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockChanRecv\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime\.chanrecv1\+0x[0-9a-f]+ .*runtime/chan.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockChanRecv\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "chan send", @@ -665,9 +838,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ runtime\.chansend1\+0x[0-9a-f]+ .*/src/runtime/chan.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockChanSend\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime\.chansend1\+0x[0-9a-f]+ .*runtime/chan.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockChanSend\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "chan close", @@ -679,9 +852,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ runtime\.chanrecv1\+0x[0-9a-f]+ .*/src/runtime/chan.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockChanClose\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime\.chanrecv1\+0x[0-9a-f]+ .*runtime/chan.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockChanClose\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "select recv async", @@ -693,9 +866,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ runtime\.selectgo\+0x[0-9a-f]+ .*/src/runtime/select.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockSelectRecvAsync\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime\.selectgo\+0x[0-9a-f]+ .*runtime/select.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockSelectRecvAsync\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "select send sync", @@ -707,9 +880,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ runtime\.selectgo\+0x[0-9a-f]+ .*/src/runtime/select.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockSelectSendSync\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime\.selectgo\+0x[0-9a-f]+ .*runtime/select.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockSelectSendSync\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "mutex", @@ -721,9 +894,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9a-f]+ .*/src/sync/mutex\.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockMutex\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9a-f]+ .*sync/mutex\.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockMutex\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, { name: "cond", @@ -735,9 +908,9 @@ func TestBlockProfile(t *testing.T) { }, re: ` [0-9]+ [0-9]+ @( 0x[[:xdigit:]]+)+ -# 0x[0-9a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9a-f]+ .*/src/sync/cond\.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.blockCond\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ -# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9a-f]+ .*sync/cond\.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.blockCond\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9a-f]+ runtime/pprof\.TestBlockProfile\+0x[0-9a-f]+ .*runtime/pprof/pprof_test.go:[0-9]+ `}, } @@ -745,11 +918,11 @@ func TestBlockProfile(t *testing.T) { runtime.SetBlockProfileRate(1) defer runtime.SetBlockProfileRate(0) for _, test := range tests { - test.f() + test.f(t) } t.Run("debug=1", func(t *testing.T) { - var w bytes.Buffer + var w strings.Builder Lookup("block").WriteTo(&w, 1) prof := w.String() @@ -821,42 +994,73 @@ func containsStack(got [][]string, want []string) bool { return false } -const blockDelay = 10 * time.Millisecond +// awaitBlockedGoroutine spins on runtime.Gosched until a runtime stack dump +// shows a goroutine in the given state with a stack frame in +// runtime/pprof.. +func awaitBlockedGoroutine(t *testing.T, state, fName string) { + re := fmt.Sprintf(`(?m)^goroutine \d+ \[%s\]:\n(?:.+\n\t.+\n)*runtime/pprof\.%s`, regexp.QuoteMeta(state), fName) + r := regexp.MustCompile(re) + + if deadline, ok := t.Deadline(); ok { + if d := time.Until(deadline); d > 1*time.Second { + timer := time.AfterFunc(d-1*time.Second, func() { + debug.SetTraceback("all") + panic(fmt.Sprintf("timed out waiting for %#q", re)) + }) + defer timer.Stop() + } + } -func blockChanRecv() { + buf := make([]byte, 64<<10) + for { + runtime.Gosched() + n := runtime.Stack(buf, true) + if n == len(buf) { + // Buffer wasn't large enough for a full goroutine dump. + // Resize it and try again. + buf = make([]byte, 2*len(buf)) + continue + } + if r.Match(buf[:n]) { + return + } + } +} + +func blockChanRecv(t *testing.T) { c := make(chan bool) go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "chan receive", "blockChanRecv") c <- true }() <-c } -func blockChanSend() { +func blockChanSend(t *testing.T) { c := make(chan bool) go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "chan send", "blockChanSend") <-c }() c <- true } -func blockChanClose() { +func blockChanClose(t *testing.T) { c := make(chan bool) go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "chan receive", "blockChanClose") close(c) }() <-c } -func blockSelectRecvAsync() { +func blockSelectRecvAsync(t *testing.T) { const numTries = 3 c := make(chan bool, 1) c2 := make(chan bool, 1) go func() { for i := 0; i < numTries; i++ { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "select", "blockSelectRecvAsync") c <- true } }() @@ -868,11 +1072,11 @@ func blockSelectRecvAsync() { } } -func blockSelectSendSync() { +func blockSelectSendSync(t *testing.T) { c := make(chan bool) c2 := make(chan bool) go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "select", "blockSelectSendSync") <-c }() select { @@ -881,11 +1085,11 @@ func blockSelectSendSync() { } } -func blockMutex() { +func blockMutex(t *testing.T) { var mu sync.Mutex mu.Lock() go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "semacquire", "blockMutex") mu.Unlock() }() // Note: Unlock releases mu before recording the mutex event, @@ -895,12 +1099,12 @@ func blockMutex() { mu.Lock() } -func blockCond() { +func blockCond(t *testing.T) { var mu sync.Mutex c := sync.NewCond(&mu) mu.Lock() go func() { - time.Sleep(blockDelay) + awaitBlockedGoroutine(t, "sync.Cond.Wait", "blockCond") mu.Lock() c.Signal() mu.Unlock() @@ -974,6 +1178,7 @@ func blockInfrequentLong(rate int) { } // Used by TestBlockProfileBias. +// //go:linkname blockevent runtime.blockevent func blockevent(cycles int64, skip int) @@ -986,10 +1191,10 @@ func TestMutexProfile(t *testing.T) { t.Fatalf("need MutexProfileRate 0, got %d", old) } - blockMutex() + blockMutex(t) t.Run("debug=1", func(t *testing.T) { - var w bytes.Buffer + var w strings.Builder Lookup("mutex").WriteTo(&w, 1) prof := w.String() t.Logf("received profile: %v", prof) @@ -1112,13 +1317,13 @@ func TestGoroutineCounts(t *testing.T) { t.Errorf("protobuf profile is invalid: %v", err) } expectedLabels := map[int64]map[string]string{ - 50: map[string]string{}, - 44: map[string]string{"label": "value"}, - 40: map[string]string{}, - 36: map[string]string{"label": "value"}, - 10: map[string]string{}, - 9: map[string]string{"label": "value"}, - 1: map[string]string{}, + 50: {}, + 44: {"label": "value"}, + 40: {}, + 36: {"label": "value"}, + 10: {}, + 9: {"label": "value"}, + 1: {}, } if !containsCountsLabels(p, expectedLabels) { t.Errorf("expected count profile to contain goroutines with counts and labels %v, got %v", @@ -1132,11 +1337,10 @@ func TestGoroutineCounts(t *testing.T) { func containsInOrder(s string, all ...string) bool { for _, t := range all { - i := strings.Index(s, t) - if i < 0 { + var ok bool + if _, s, ok = strings.Cut(s, t); !ok { return false } - s = s[i+len(t):] } return true } @@ -1188,6 +1392,283 @@ func containsCountsLabels(prof *profile.Profile, countLabels map[int64]map[strin return true } +func TestGoroutineProfileConcurrency(t *testing.T) { + goroutineProf := Lookup("goroutine") + + profilerCalls := func(s string) int { + return strings.Count(s, "\truntime/pprof.runtime_goroutineProfileWithLabels+") + } + + includesFinalizer := func(s string) bool { + return strings.Contains(s, "runtime.runfinq") + } + + // Concurrent calls to the goroutine profiler should not trigger data races + // or corruption. + t.Run("overlapping profile requests", func(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + var wg sync.WaitGroup + for i := 0; i < 2; i++ { + wg.Add(1) + Do(ctx, Labels("i", fmt.Sprint(i)), func(context.Context) { + go func() { + defer wg.Done() + for ctx.Err() == nil { + var w strings.Builder + goroutineProf.WriteTo(&w, 1) + prof := w.String() + count := profilerCalls(prof) + if count >= 2 { + t.Logf("prof %d\n%s", count, prof) + cancel() + } + } + }() + }) + } + wg.Wait() + }) + + // The finalizer goroutine should not show up in most profiles, since it's + // marked as a system goroutine when idle. + t.Run("finalizer not present", func(t *testing.T) { + var w strings.Builder + goroutineProf.WriteTo(&w, 1) + prof := w.String() + if includesFinalizer(prof) { + t.Errorf("profile includes finalizer (but finalizer should be marked as system):\n%s", prof) + } + }) + + // The finalizer goroutine should show up when it's running user code. + t.Run("finalizer present", func(t *testing.T) { + obj := new(byte) + ch1, ch2 := make(chan int), make(chan int) + defer close(ch2) + runtime.SetFinalizer(obj, func(_ interface{}) { + close(ch1) + <-ch2 + }) + obj = nil + for i := 10; i >= 0; i-- { + select { + case <-ch1: + default: + if i == 0 { + t.Fatalf("finalizer did not run") + } + runtime.GC() + } + } + var w strings.Builder + goroutineProf.WriteTo(&w, 1) + prof := w.String() + if !includesFinalizer(prof) { + t.Errorf("profile does not include finalizer (and it should be marked as user):\n%s", prof) + } + }) + + // Check that new goroutines only show up in order. + testLaunches := func(t *testing.T) { + var done sync.WaitGroup + defer done.Wait() + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + ch := make(chan int) + defer close(ch) + + var ready sync.WaitGroup + + // These goroutines all survive until the end of the subtest, so we can + // check that a (numbered) goroutine appearing in the profile implies + // that all older goroutines also appear in the profile. + ready.Add(1) + done.Add(1) + go func() { + defer done.Done() + for i := 0; ctx.Err() == nil; i++ { + // Use SetGoroutineLabels rather than Do we can always expect an + // extra goroutine (this one) with most recent label. + SetGoroutineLabels(WithLabels(ctx, Labels(t.Name()+"-loop-i", fmt.Sprint(i)))) + done.Add(1) + go func() { + <-ch + done.Done() + }() + for j := 0; j < i; j++ { + // Spin for longer and longer as the test goes on. This + // goroutine will do O(N^2) work with the number of + // goroutines it launches. This should be slow relative to + // the work involved in collecting a goroutine profile, + // which is O(N) with the high-water mark of the number of + // goroutines in this process (in the allgs slice). + runtime.Gosched() + } + if i == 0 { + ready.Done() + } + } + }() + + // Short-lived goroutines exercise different code paths (goroutines with + // status _Gdead, for instance). This churn doesn't have behavior that + // we can test directly, but does help to shake out data races. + ready.Add(1) + var churn func(i int) + churn = func(i int) { + SetGoroutineLabels(WithLabels(ctx, Labels(t.Name()+"-churn-i", fmt.Sprint(i)))) + if i == 0 { + ready.Done() + } else if i%16 == 0 { + // Yield on occasion so this sequence of goroutine launches + // doesn't monopolize a P. See issue #52934. + runtime.Gosched() + } + if ctx.Err() == nil { + go churn(i + 1) + } + } + go func() { + churn(0) + }() + + ready.Wait() + + var w [3]bytes.Buffer + for i := range w { + goroutineProf.WriteTo(&w[i], 0) + } + for i := range w { + p, err := profile.Parse(bytes.NewReader(w[i].Bytes())) + if err != nil { + t.Errorf("error parsing protobuf profile: %v", err) + } + + // High-numbered loop-i goroutines imply that every lower-numbered + // loop-i goroutine should be present in the profile too. + counts := make(map[string]int) + for _, s := range p.Sample { + label := s.Label[t.Name()+"-loop-i"] + if len(label) > 0 { + counts[label[0]]++ + } + } + for j, max := 0, len(counts)-1; j <= max; j++ { + n := counts[fmt.Sprint(j)] + if n == 1 || (n == 2 && j == max) { + continue + } + t.Errorf("profile #%d's goroutines with label loop-i:%d; %d != 1 (or 2 for the last entry, %d)", + i+1, j, n, max) + t.Logf("counts %v", counts) + break + } + } + } + + runs := 100 + if testing.Short() { + runs = 5 + } + for i := 0; i < runs; i++ { + // Run multiple times to shake out data races + t.Run("goroutine launches", testLaunches) + } +} + +func BenchmarkGoroutine(b *testing.B) { + withIdle := func(n int, fn func(b *testing.B)) func(b *testing.B) { + return func(b *testing.B) { + c := make(chan int) + var ready, done sync.WaitGroup + defer func() { + close(c) + done.Wait() + }() + + for i := 0; i < n; i++ { + ready.Add(1) + done.Add(1) + go func() { + ready.Done() + <-c + done.Done() + }() + } + // Let goroutines block on channel + ready.Wait() + for i := 0; i < 5; i++ { + runtime.Gosched() + } + + fn(b) + } + } + + withChurn := func(fn func(b *testing.B)) func(b *testing.B) { + return func(b *testing.B) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var ready sync.WaitGroup + ready.Add(1) + var count int64 + var churn func(i int) + churn = func(i int) { + SetGoroutineLabels(WithLabels(ctx, Labels("churn-i", fmt.Sprint(i)))) + atomic.AddInt64(&count, 1) + if i == 0 { + ready.Done() + } + if ctx.Err() == nil { + go churn(i + 1) + } + } + go func() { + churn(0) + }() + ready.Wait() + + fn(b) + b.ReportMetric(float64(atomic.LoadInt64(&count))/float64(b.N), "concurrent_launches/op") + } + } + + benchWriteTo := func(b *testing.B) { + goroutineProf := Lookup("goroutine") + b.ResetTimer() + for i := 0; i < b.N; i++ { + goroutineProf.WriteTo(io.Discard, 0) + } + b.StopTimer() + } + + benchGoroutineProfile := func(b *testing.B) { + p := make([]runtime.StackRecord, 10000) + b.ResetTimer() + for i := 0; i < b.N; i++ { + runtime.GoroutineProfile(p) + } + b.StopTimer() + } + + // Note that some costs of collecting a goroutine profile depend on the + // length of the runtime.allgs slice, which never shrinks. Stay within race + // detector's 8k-goroutine limit + for _, n := range []int{50, 500, 5000} { + b.Run(fmt.Sprintf("Profile.WriteTo idle %d", n), withIdle(n, benchWriteTo)) + b.Run(fmt.Sprintf("Profile.WriteTo churn %d", n), withIdle(n, withChurn(benchWriteTo))) + b.Run(fmt.Sprintf("runtime.GoroutineProfile churn %d", n), withIdle(n, withChurn(benchGoroutineProfile))) + } +} + var emptyCallStackTestRun int64 // Issue 18836. @@ -1196,7 +1677,7 @@ func TestEmptyCallStack(t *testing.T) { emptyCallStackTestRun++ t.Parallel() - var buf bytes.Buffer + var buf strings.Builder p := NewProfile(name) p.Add("foo", 47674) @@ -1216,22 +1697,23 @@ func TestEmptyCallStack(t *testing.T) { // stackContainsLabeled takes a spec like funcname;key=value and matches if the stack has that key // and value and has funcname somewhere in the stack. func stackContainsLabeled(spec string, count uintptr, stk []*profile.Location, labels map[string][]string) bool { - semi := strings.Index(spec, ";") - if semi == -1 { + base, kv, ok := strings.Cut(spec, ";") + if !ok { panic("no semicolon in key/value spec") } - kv := strings.SplitN(spec[semi+1:], "=", 2) - if len(kv) != 2 { + k, v, ok := strings.Cut(kv, "=") + if !ok { panic("missing = in key/value spec") } - if !contains(labels[kv[0]], kv[1]) { + if !contains(labels[k], v) { return false } - return stackContains(spec[:semi], count, stk, labels) + return stackContains(base, count, stk, labels) } func TestCPUProfileLabel(t *testing.T) { - testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions(), func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, avoidFunctions()) + testCPUProfile(t, matches, func(dur time.Duration) { Do(context.Background(), Labels("key", "value"), func(context.Context) { cpuHogger(cpuHog1, &salt1, dur) }) @@ -1240,9 +1722,10 @@ func TestCPUProfileLabel(t *testing.T) { func TestLabelRace(t *testing.T) { // Test the race detector annotations for synchronization - // between settings labels and consuming them from the + // between setting labels and consuming them from the // profile. - testCPUProfile(t, stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil, func(dur time.Duration) { + matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime/pprof.cpuHogger;key=value"}, nil) + testCPUProfile(t, matches, func(dur time.Duration) { start := time.Now() var wg sync.WaitGroup for time.Since(start) < dur { @@ -1261,6 +1744,183 @@ func TestLabelRace(t *testing.T) { }) } +func TestGoroutineProfileLabelRace(t *testing.T) { + // Test the race detector annotations for synchronization + // between setting labels and consuming them from the + // goroutine profile. See issue #50292. + + t.Run("reset", func(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + goroutineProf := Lookup("goroutine") + for ctx.Err() == nil { + var w strings.Builder + goroutineProf.WriteTo(&w, 1) + prof := w.String() + if strings.Contains(prof, "loop-i") { + cancel() + } + } + }() + + for i := 0; ctx.Err() == nil; i++ { + Do(ctx, Labels("loop-i", fmt.Sprint(i)), func(ctx context.Context) { + }) + } + }) + + t.Run("churn", func(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var ready sync.WaitGroup + ready.Add(1) + var churn func(i int) + churn = func(i int) { + SetGoroutineLabels(WithLabels(ctx, Labels("churn-i", fmt.Sprint(i)))) + if i == 0 { + ready.Done() + } + if ctx.Err() == nil { + go churn(i + 1) + } + } + go func() { + churn(0) + }() + ready.Wait() + + goroutineProf := Lookup("goroutine") + for i := 0; i < 10; i++ { + goroutineProf.WriteTo(io.Discard, 1) + } + }) +} + +// TestLabelSystemstack makes sure CPU profiler samples of goroutines running +// on systemstack include the correct pprof labels. See issue #48577 +func TestLabelSystemstack(t *testing.T) { + // Grab and re-set the initial value before continuing to ensure + // GOGC doesn't actually change following the test. + gogc := debug.SetGCPercent(100) + debug.SetGCPercent(gogc) + + matches := matchAndAvoidStacks(stackContainsLabeled, []string{"runtime.systemstack;key=value"}, avoidFunctions()) + p := testCPUProfile(t, matches, func(dur time.Duration) { + Do(context.Background(), Labels("key", "value"), func(ctx context.Context) { + parallelLabelHog(ctx, dur, gogc) + }) + }) + + // Two conditions to check: + // * labelHog should always be labeled. + // * The label should _only_ appear on labelHog and the Do call above. + for _, s := range p.Sample { + isLabeled := s.Label != nil && contains(s.Label["key"], "value") + var ( + mayBeLabeled bool + mustBeLabeled bool + mustNotBeLabeled bool + ) + for _, loc := range s.Location { + for _, l := range loc.Line { + switch l.Function.Name { + case "runtime/pprof.labelHog", "runtime/pprof.parallelLabelHog", "runtime/pprof.parallelLabelHog.func1": + mustBeLabeled = true + case "runtime/pprof.Do": + // Do sets the labels, so samples may + // or may not be labeled depending on + // which part of the function they are + // at. + mayBeLabeled = true + case "runtime.bgsweep", "runtime.bgscavenge", "runtime.forcegchelper", "runtime.gcBgMarkWorker", "runtime.runfinq", "runtime.sysmon": + // Runtime system goroutines or threads + // (such as those identified by + // runtime.isSystemGoroutine). These + // should never be labeled. + mustNotBeLabeled = true + case "gogo", "gosave_systemstack_switch", "racecall": + // These are context switch/race + // critical that we can't do a full + // traceback from. Typically this would + // be covered by the runtime check + // below, but these symbols don't have + // the package name. + mayBeLabeled = true + } + + if strings.HasPrefix(l.Function.Name, "runtime.") { + // There are many places in the runtime + // where we can't do a full traceback. + // Ideally we'd list them all, but + // barring that allow anything in the + // runtime, unless explicitly excluded + // above. + mayBeLabeled = true + } + } + } + if mustNotBeLabeled { + // If this must not be labeled, then mayBeLabeled hints + // are not relevant. + mayBeLabeled = false + } + if mustBeLabeled && !isLabeled { + var buf strings.Builder + fprintStack(&buf, s.Location) + t.Errorf("Sample labeled got false want true: %s", buf.String()) + } + if mustNotBeLabeled && isLabeled { + var buf strings.Builder + fprintStack(&buf, s.Location) + t.Errorf("Sample labeled got true want false: %s", buf.String()) + } + if isLabeled && !(mayBeLabeled || mustBeLabeled) { + var buf strings.Builder + fprintStack(&buf, s.Location) + t.Errorf("Sample labeled got true want false: %s", buf.String()) + } + } +} + +// labelHog is designed to burn CPU time in a way that a high number of CPU +// samples end up running on systemstack. +func labelHog(stop chan struct{}, gogc int) { + // Regression test for issue 50032. We must give GC an opportunity to + // be initially triggered by a labelled goroutine. + runtime.GC() + + for i := 0; ; i++ { + select { + case <-stop: + return + default: + debug.SetGCPercent(gogc) + } + } +} + +// parallelLabelHog runs GOMAXPROCS goroutines running labelHog. +func parallelLabelHog(ctx context.Context, dur time.Duration, gogc int) { + var wg sync.WaitGroup + stop := make(chan struct{}) + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + wg.Add(1) + go func() { + defer wg.Done() + labelHog(stop, gogc) + }() + } + + time.Sleep(dur) + close(stop) + wg.Wait() +} + // Check that there is no deadlock when the program receives SIGPROF while in // 64bit atomics' critical section. Used to happen on mips{,le}. See #20146. func TestAtomicLoadStore64(t *testing.T) { @@ -1367,6 +2027,7 @@ func TestTryAdd(t *testing.T) { testCases := []struct { name string input []uint64 // following the input format assumed by profileBuilder.addCPUData. + count int // number of records in input. wantLocs [][]string // ordered location entries with function names. wantSamples []*profile.Sample // ordered samples, we care only about Value and the profile location IDs. }{{ @@ -1376,6 +2037,7 @@ func TestTryAdd(t *testing.T) { 3, 0, 500, // hz = 500. Must match the period. 5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1], }, + count: 2, wantLocs: [][]string{ {"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}, }, @@ -1392,6 +2054,7 @@ func TestTryAdd(t *testing.T) { 7, 0, 10, inlinedCallerStack[0], inlinedCallerStack[1], inlinedCallerStack[0], inlinedCallerStack[1], 5, 0, 20, inlinedCallerStack[0], inlinedCallerStack[1], }, + count: 3, wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}}, wantSamples: []*profile.Sample{ {Value: []int64{10, 10 * period}, Location: []*profile.Location{{ID: 1}, {ID: 1}}}, @@ -1405,6 +2068,7 @@ func TestTryAdd(t *testing.T) { // entry. The "stk" entry is actually the count. 4, 0, 0, 4242, }, + count: 2, wantLocs: [][]string{{"runtime/pprof.lostProfileEvent"}}, wantSamples: []*profile.Sample{ {Value: []int64{4242, 4242 * period}, Location: []*profile.Location{{ID: 1}}}, @@ -1423,6 +2087,7 @@ func TestTryAdd(t *testing.T) { 5, 0, 30, inlinedCallerStack[0], inlinedCallerStack[0], 4, 0, 40, inlinedCallerStack[0], }, + count: 3, // inlinedCallerDump shows up here because // runtime_expandFinalInlineFrame adds it to the stack frame. wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump"}, {"runtime/pprof.inlinedCallerDump"}}, @@ -1436,6 +2101,7 @@ func TestTryAdd(t *testing.T) { 3, 0, 500, // hz = 500. Must match the period. 9, 0, 10, recursionStack[0], recursionStack[1], recursionStack[2], recursionStack[3], recursionStack[4], recursionStack[5], }, + count: 2, wantLocs: [][]string{ {"runtime/pprof.recursionChainBottom"}, { @@ -1459,6 +2125,7 @@ func TestTryAdd(t *testing.T) { 5, 0, 50, inlinedCallerStack[0], inlinedCallerStack[1], 4, 0, 60, inlinedCallerStack[0], }, + count: 3, wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}}, wantSamples: []*profile.Sample{ {Value: []int64{50, 50 * period}, Location: []*profile.Location{{ID: 1}}}, @@ -1471,6 +2138,7 @@ func TestTryAdd(t *testing.T) { 4, 0, 70, inlinedCallerStack[0], 5, 0, 80, inlinedCallerStack[0], inlinedCallerStack[1], }, + count: 3, wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}}, wantSamples: []*profile.Sample{ {Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}}, @@ -1483,6 +2151,7 @@ func TestTryAdd(t *testing.T) { 3, 0, 500, // hz = 500. Must match the period. 4, 0, 70, inlinedCallerStack[0], }, + count: 2, wantLocs: [][]string{{"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}}, wantSamples: []*profile.Sample{ {Value: []int64{70, 70 * period}, Location: []*profile.Location{{ID: 1}}}, @@ -1498,6 +2167,7 @@ func TestTryAdd(t *testing.T) { // from getting merged into above. 5, 0, 80, inlinedCallerStack[1], inlinedCallerStack[0], }, + count: 3, wantLocs: [][]string{ {"runtime/pprof.inlinedCalleeDump", "runtime/pprof.inlinedCallerDump"}, {"runtime/pprof.inlinedCallerDump"}, @@ -1510,7 +2180,7 @@ func TestTryAdd(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - p, err := translateCPUProfile(tc.input) + p, err := translateCPUProfile(tc.input, tc.count) if err != nil { t.Fatalf("translating profile: %v", err) } @@ -1543,3 +2213,39 @@ func TestTryAdd(t *testing.T) { }) } } + +func TestTimeVDSO(t *testing.T) { + // Test that time functions have the right stack trace. In particular, + // it shouldn't be recursive. + + if runtime.GOOS == "android" { + // Flaky on Android, issue 48655. VDSO may not be enabled. + testenv.SkipFlaky(t, 48655) + } + + matches := matchAndAvoidStacks(stackContains, []string{"time.now"}, avoidFunctions()) + p := testCPUProfile(t, matches, func(dur time.Duration) { + t0 := time.Now() + for { + t := time.Now() + if t.Sub(t0) >= dur { + return + } + } + }) + + // Check for recursive time.now sample. + for _, sample := range p.Sample { + var seenNow bool + for _, loc := range sample.Location { + for _, line := range loc.Line { + if line.Function.Name == "time.now" { + if seenNow { + t.Fatalf("unexpected recursive time.now") + } + seenNow = true + } + } + } + } +} diff --git a/src/runtime/pprof/pprof_windows.go b/src/runtime/pprof/pprof_windows.go new file mode 100644 index 00000000000000..23ef2f80fe8b8b --- /dev/null +++ b/src/runtime/pprof/pprof_windows.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pprof + +import ( + "fmt" + "internal/syscall/windows" + "io" + "syscall" + "unsafe" +) + +func addMaxRSS(w io.Writer) { + var m windows.PROCESS_MEMORY_COUNTERS + p, _ := syscall.GetCurrentProcess() + err := windows.GetProcessMemoryInfo(p, &m, uint32(unsafe.Sizeof(m))) + if err == nil { + fmt.Fprintf(w, "# MaxRSS = %d\n", m.PeakWorkingSetSize) + } +} diff --git a/src/runtime/pprof/proto.go b/src/runtime/pprof/proto.go index bdb4454b6e1cc8..7e3c5f1c184d66 100644 --- a/src/runtime/pprof/proto.go +++ b/src/runtime/pprof/proto.go @@ -8,10 +8,11 @@ import ( "bytes" "compress/gzip" "fmt" + "internal/abi" "io" - "os" "runtime" "strconv" + "strings" "time" "unsafe" ) @@ -21,11 +22,6 @@ import ( // (The name shows up in the pprof graphs.) func lostProfileEvent() { lostProfileEvent() } -// funcPC returns the PC for the func value f. -func funcPC(f interface{}) uintptr { - return *(*[2]*uintptr)(unsafe.Pointer(&f))[1] -} - // A profileBuilder writes a profile incrementally from a // stream of profile samples delivered by the runtime. type profileBuilder struct { @@ -49,19 +45,21 @@ type profileBuilder struct { type memMap struct { // initialized as reading mapping - start uintptr - end uintptr - offset uint64 - file, buildID string + start uintptr // Address at which the binary (or DLL) is loaded into memory. + end uintptr // The limit of the address range occupied by this mapping. + offset uint64 // Offset in the binary that corresponds to the first mapped address. + file string // The object this entry is loaded from. + buildID string // A string that uniquely identifies a particular program version with high probability. funcs symbolizeFlag fake bool // map entry was faked; /proc/self/maps wasn't available } // symbolizeFlag keeps track of symbolization result. -// 0 : no symbol lookup was performed -// 1<<0 (lookupTried) : symbol lookup was performed -// 1<<1 (lookupFailed): symbol lookup was performed but failed +// +// 0 : no symbol lookup was performed +// 1<<0 (lookupTried) : symbol lookup was performed +// 1<<1 (lookupFailed): symbol lookup was performed but failed type symbolizeFlag uint8 const ( @@ -247,6 +245,11 @@ type locInfo struct { // to represent inlined functions // https://github.com/golang/go/blob/d6f2f833c93a41ec1c68e49804b8387a06b131c5/src/runtime/traceback.go#L347-L368 pcs []uintptr + + // firstPCFrames and firstPCSymbolizeResult hold the results of the + // allFrames call for the first (leaf-most) PC this locInfo represents + firstPCFrames []runtime.Frame + firstPCSymbolizeResult symbolizeFlag } // newProfileBuilder returns a new profileBuilder. @@ -269,8 +272,9 @@ func newProfileBuilder(w io.Writer) *profileBuilder { } // addCPUData adds the CPU profiling data to the profile. -// The data must be a whole number of records, -// as delivered by the runtime. +// +// The data must be a whole number of records, as delivered by the runtime. +// len(tags) must be equal to the number of records in data. func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error { if !b.havePeriod { // first record is period @@ -285,6 +289,9 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error b.period = 1e9 / int64(data[2]) b.havePeriod = true data = data[3:] + // Consume tag slot. Note that there isn't a meaningful tag + // value for this record. + tags = tags[1:] } // Parse CPU samples from the profile. @@ -309,14 +316,14 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error if data[0] < 3 || tags != nil && len(tags) < 1 { return fmt.Errorf("malformed profile") } + if len(tags) < 1 { + return fmt.Errorf("mismatched profile records and tags") + } count := data[2] stk := data[3:data[0]] data = data[data[0]:] - var tag unsafe.Pointer - if tags != nil { - tag = tags[0] - tags = tags[1:] - } + tag := tags[0] + tags = tags[1:] if count == 0 && len(stk) == 1 { // overflow record @@ -325,11 +332,15 @@ func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error // gentraceback guarantees that PCs in the // stack can be unconditionally decremented and // still be valid, so we must do the same. - uint64(funcPC(lostProfileEvent) + 1), + uint64(abi.FuncPCABIInternal(lostProfileEvent) + 1), } } b.m.lookup(stk, tag).count += int64(count) } + + if len(tags) != 0 { + return fmt.Errorf("mismatched profile records and tags") + } return nil } @@ -394,6 +405,24 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo for len(stk) > 0 { addr := stk[0] if l, ok := b.locs[addr]; ok { + // When generating code for an inlined function, the compiler adds + // NOP instructions to the outermost function as a placeholder for + // each layer of inlining. When the runtime generates tracebacks for + // stacks that include inlined functions, it uses the addresses of + // those NOPs as "fake" PCs on the stack as if they were regular + // function call sites. But if a profiling signal arrives while the + // CPU is executing one of those NOPs, its PC will show up as a leaf + // in the profile with its own Location entry. So, always check + // whether addr is a "fake" PC in the context of the current call + // stack by trying to add it to the inlining deck before assuming + // that the deck is complete. + if len(b.deck.pcs) > 0 { + if added := b.deck.tryAdd(addr, l.firstPCFrames, l.firstPCSymbolizeResult); added { + stk = stk[1:] + continue + } + } + // first record the location if there is any pending accumulated info. if id := b.emitLocation(); id > 0 { locs = append(locs, id) @@ -446,6 +475,27 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo return locs } +// Here's an example of how Go 1.17 writes out inlined functions, compiled for +// linux/amd64. The disassembly of main.main shows two levels of inlining: main +// calls b, b calls a, a does some work. +// +// inline.go:9 0x4553ec 90 NOPL // func main() { b(v) } +// inline.go:6 0x4553ed 90 NOPL // func b(v *int) { a(v) } +// inline.go:5 0x4553ee 48c7002a000000 MOVQ $0x2a, 0(AX) // func a(v *int) { *v = 42 } +// +// If a profiling signal arrives while executing the MOVQ at 0x4553ee (for line +// 5), the runtime will report the stack as the MOVQ frame being called by the +// NOPL at 0x4553ed (for line 6) being called by the NOPL at 0x4553ec (for line +// 9). +// +// The role of pcDeck is to collapse those three frames back into a single +// location at 0x4553ee, with file/line/function symbolization info representing +// the three layers of calls. It does that via sequential calls to pcDeck.tryAdd +// starting with the leaf-most address. The fourth call to pcDeck.tryAdd will be +// for the caller of main.main. Because main.main was not inlined in its caller, +// the deck will reject the addition, and the fourth PC on the stack will get +// its own location. + // pcDeck is a helper to detect a sequence of inlined functions from // a stack trace returned by the runtime. // @@ -459,9 +509,10 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo // and looking up debug info is not ideal, so we use a heuristic to filter // the fake pcs and restore the inlined and entry functions. Inlined functions // have the following properties: -// Frame's Func is nil (note: also true for non-Go functions), and -// Frame's Entry matches its entry function frame's Entry (note: could also be true for recursive calls and non-Go functions), and -// Frame's Name does not match its entry function frame's name (note: inlined functions cannot be directly recursive). +// +// Frame's Func is nil (note: also true for non-Go functions), and +// Frame's Entry matches its entry function frame's Entry (note: could also be true for recursive calls and non-Go functions), and +// Frame's Name does not match its entry function frame's name (note: inlined functions cannot be directly recursive). // // As reading and processing the pcs in a stack trace one by one (from leaf to the root), // we use pcDeck to temporarily hold the observed pcs and their expanded frames @@ -470,19 +521,28 @@ type pcDeck struct { pcs []uintptr frames []runtime.Frame symbolizeResult symbolizeFlag + + // firstPCFrames indicates the number of frames associated with the first + // (leaf-most) PC in the deck + firstPCFrames int + // firstPCSymbolizeResult holds the results of the allFrames call for the + // first (leaf-most) PC in the deck + firstPCSymbolizeResult symbolizeFlag } func (d *pcDeck) reset() { d.pcs = d.pcs[:0] d.frames = d.frames[:0] d.symbolizeResult = 0 + d.firstPCFrames = 0 + d.firstPCSymbolizeResult = 0 } // tryAdd tries to add the pc and Frames expanded from it (most likely one, // since the stack trace is already fully expanded) and the symbolizeResult // to the deck. If it fails the caller needs to flush the deck and retry. func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symbolizeFlag) (success bool) { - if existing := len(d.pcs); existing > 0 { + if existing := len(d.frames); existing > 0 { // 'd.frames' are all expanded from one 'pc' and represent all // inlined functions so we check only the last one. newFrame := frames[0] @@ -504,6 +564,10 @@ func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symb d.pcs = append(d.pcs, pc) d.frames = append(d.frames, frames...) d.symbolizeResult |= symbolizeResult + if len(d.pcs) == 1 { + d.firstPCFrames = len(d.frames) + d.firstPCSymbolizeResult = symbolizeResult + } return true } @@ -530,7 +594,12 @@ func (b *profileBuilder) emitLocation() uint64 { newFuncs := make([]newFunc, 0, 8) id := uint64(len(b.locs)) + 1 - b.locs[addr] = locInfo{id: id, pcs: append([]uintptr{}, b.deck.pcs...)} + b.locs[addr] = locInfo{ + id: id, + pcs: append([]uintptr{}, b.deck.pcs...), + firstPCSymbolizeResult: b.deck.firstPCSymbolizeResult, + firstPCFrames: append([]runtime.Frame{}, b.deck.frames[:b.deck.firstPCFrames]...), + } start := b.pb.startMessage() b.pb.uint64Opt(tagLocation_ID, id) @@ -571,19 +640,8 @@ func (b *profileBuilder) emitLocation() uint64 { return id } -// readMapping reads /proc/self/maps and writes mappings to b.pb. -// It saves the address ranges of the mappings in b.mem for use -// when emitting locations. -func (b *profileBuilder) readMapping() { - data, _ := os.ReadFile("/proc/self/maps") - parseProcSelfMaps(data, b.addMapping) - if len(b.mem) == 0 { // pprof expects a map entry, so fake one. - b.addMappingEntry(0, 0, 0, "", "", true) - // TODO(hyangah): make addMapping return *memMap or - // take a memMap struct, and get rid of addMappingEntry - // that takes a bunch of positional arguments. - } -} +var space = []byte(" ") +var newline = []byte("\n") func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) { // $ cat /proc/self/maps @@ -611,37 +669,24 @@ func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, // next removes and returns the next field in the line. // It also removes from line any spaces following the field. next := func() []byte { - j := bytes.IndexByte(line, ' ') - if j < 0 { - f := line - line = nil - return f - } - f := line[:j] - line = line[j+1:] - for len(line) > 0 && line[0] == ' ' { - line = line[1:] - } + var f []byte + f, line, _ = bytes.Cut(line, space) + line = bytes.TrimLeft(line, " ") return f } for len(data) > 0 { - i := bytes.IndexByte(data, '\n') - if i < 0 { - line, data = data, nil - } else { - line, data = data[:i], data[i+1:] - } + line, data, _ = bytes.Cut(data, newline) addr := next() - i = bytes.IndexByte(addr, '-') - if i < 0 { + loStr, hiStr, ok := strings.Cut(string(addr), "-") + if !ok { continue } - lo, err := strconv.ParseUint(string(addr[:i]), 16, 64) + lo, err := strconv.ParseUint(loStr, 16, 64) if err != nil { continue } - hi, err := strconv.ParseUint(string(addr[i+1:]), 16, 64) + hi, err := strconv.ParseUint(hiStr, 16, 64) if err != nil { continue } @@ -676,13 +721,12 @@ func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, continue } - // TODO: pprof's remapMappingIDs makes two adjustments: + // TODO: pprof's remapMappingIDs makes one adjustment: // 1. If there is an /anon_hugepage mapping first and it is // consecutive to a next mapping, drop the /anon_hugepage. - // 2. If start-offset = 0x400000, change start to 0x400000 and offset to 0. - // There's no indication why either of these is needed. - // Let's try not doing these and see what breaks. - // If we do need them, they would go here, before we + // There's no indication why this is needed. + // Let's try not doing this and see what breaks. + // If we do need it, it would go here, before we // enter the mappings into b.mem in the first place. buildID, _ := elfBuildID(file) diff --git a/src/runtime/pprof/proto_other.go b/src/runtime/pprof/proto_other.go new file mode 100644 index 00000000000000..4a7fe79501b68a --- /dev/null +++ b/src/runtime/pprof/proto_other.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows + +package pprof + +import ( + "errors" + "os" +) + +// readMapping reads /proc/self/maps and writes mappings to b.pb. +// It saves the address ranges of the mappings in b.mem for use +// when emitting locations. +func (b *profileBuilder) readMapping() { + data, _ := os.ReadFile("/proc/self/maps") + parseProcSelfMaps(data, b.addMapping) + if len(b.mem) == 0 { // pprof expects a map entry, so fake one. + b.addMappingEntry(0, 0, 0, "", "", true) + // TODO(hyangah): make addMapping return *memMap or + // take a memMap struct, and get rid of addMappingEntry + // that takes a bunch of positional arguments. + } +} + +func readMainModuleMapping() (start, end uint64, err error) { + return 0, 0, errors.New("not implemented") +} diff --git a/src/runtime/pprof/proto_test.go b/src/runtime/pprof/proto_test.go index 5eb1aab14012a1..780b481de83b7c 100644 --- a/src/runtime/pprof/proto_test.go +++ b/src/runtime/pprof/proto_test.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/json" "fmt" + "internal/abi" "internal/profile" "internal/testenv" "os" @@ -16,16 +17,20 @@ import ( "runtime" "strings" "testing" + "unsafe" ) // translateCPUProfile parses binary CPU profiling stack trace data // generated by runtime.CPUProfile() into a profile struct. // This is only used for testing. Real conversions stream the // data into the profileBuilder as it becomes available. -func translateCPUProfile(data []uint64) (*profile.Profile, error) { +// +// count is the number of records in data. +func translateCPUProfile(data []uint64, count int) (*profile.Profile, error) { var buf bytes.Buffer b := newProfileBuilder(&buf) - if err := b.addCPUData(data, nil); err != nil { + tags := make([]unsafe.Pointer, count) + if err := b.addCPUData(data, tags); err != nil { return nil, err } b.build() @@ -35,7 +40,7 @@ func translateCPUProfile(data []uint64) (*profile.Profile, error) { // fmtJSON returns a pretty-printed JSON form for x. // It works reasonbly well for printing protocol-buffer // data structures like profile.Profile. -func fmtJSON(x interface{}) string { +func fmtJSON(x any) string { js, _ := json.MarshalIndent(x, "", "\t") return string(js) } @@ -45,7 +50,7 @@ func TestConvertCPUProfileEmpty(t *testing.T) { var buf bytes.Buffer b := []uint64{3, 0, 500} // empty profile at 500 Hz (2ms sample period) - p, err := translateCPUProfile(b) + p, err := translateCPUProfile(b, 1) if err != nil { t.Fatalf("translateCPUProfile: %v", err) } @@ -96,12 +101,42 @@ func testPCs(t *testing.T) (addr1, addr2 uint64, map1, map2 *profile.Mapping) { addr2 = mprof.Mapping[1].Start map2 = mprof.Mapping[1] map2.BuildID, _ = elfBuildID(map2.File) + case "windows": + addr1 = uint64(abi.FuncPCABIInternal(f1)) + addr2 = uint64(abi.FuncPCABIInternal(f2)) + + exe, err := os.Executable() + if err != nil { + t.Fatal(err) + } + + start, end, err := readMainModuleMapping() + if err != nil { + t.Fatal(err) + } + + map1 = &profile.Mapping{ + ID: 1, + Start: start, + Limit: end, + File: exe, + BuildID: peBuildID(exe), + HasFunctions: true, + } + map2 = &profile.Mapping{ + ID: 1, + Start: start, + Limit: end, + File: exe, + BuildID: peBuildID(exe), + HasFunctions: true, + } case "js": - addr1 = uint64(funcPC(f1)) - addr2 = uint64(funcPC(f2)) + addr1 = uint64(abi.FuncPCABIInternal(f1)) + addr2 = uint64(abi.FuncPCABIInternal(f2)) default: - addr1 = uint64(funcPC(f1)) - addr2 = uint64(funcPC(f2)) + addr1 = uint64(abi.FuncPCABIInternal(f1)) + addr2 = uint64(abi.FuncPCABIInternal(f2)) // Fake mapping - HasFunctions will be true because two PCs from Go // will be fully symbolized. fake := &profile.Mapping{ID: 1, HasFunctions: true} @@ -119,7 +154,7 @@ func TestConvertCPUProfile(t *testing.T) { 5, 0, 40, uint64(addr2 + 1), uint64(addr2 + 2), // 40 samples in addr2 5, 0, 10, uint64(addr1 + 1), uint64(addr1 + 2), // 10 samples in addr1 } - p, err := translateCPUProfile(b) + p, err := translateCPUProfile(b, 4) if err != nil { t.Fatalf("translating profile: %v", err) } @@ -273,15 +308,14 @@ func TestProcSelfMaps(t *testing.T) { f := func(t *testing.T, input string) { for tx, tt := range strings.Split(input, "\n\n") { - i := strings.Index(tt, "->\n") - if i < 0 { + in, out, ok := strings.Cut(tt, "->\n") + if !ok { t.Fatal("malformed test case") } - in, out := tt[:i], tt[i+len("->\n"):] if len(out) > 0 && out[len(out)-1] != '\n' { out += "\n" } - var buf bytes.Buffer + var buf strings.Builder parseProcSelfMaps([]byte(in), func(lo, hi, offset uint64, file, buildID string) { fmt.Fprintf(&buf, "%08x %08x %08x %s\n", lo, hi, offset, file) }) @@ -429,7 +463,7 @@ func TestEmptyStack(t *testing.T) { 3, 0, 500, // hz = 500 3, 0, 10, // 10 samples with an empty stack trace } - _, err := translateCPUProfile(b) + _, err := translateCPUProfile(b, 2) if err != nil { t.Fatalf("translating profile: %v", err) } diff --git a/src/runtime/pprof/proto_windows.go b/src/runtime/pprof/proto_windows.go new file mode 100644 index 00000000000000..d5ae4a5eecbd00 --- /dev/null +++ b/src/runtime/pprof/proto_windows.go @@ -0,0 +1,73 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pprof + +import ( + "errors" + "internal/syscall/windows" + "syscall" +) + +// readMapping adds memory mapping information to the profile. +func (b *profileBuilder) readMapping() { + snap, err := createModuleSnapshot() + if err != nil { + // pprof expects a map entry, so fake one, when we haven't added anything yet. + b.addMappingEntry(0, 0, 0, "", "", true) + return + } + defer func() { _ = syscall.CloseHandle(snap) }() + + var module windows.ModuleEntry32 + module.Size = uint32(windows.SizeofModuleEntry32) + err = windows.Module32First(snap, &module) + if err != nil { + // pprof expects a map entry, so fake one, when we haven't added anything yet. + b.addMappingEntry(0, 0, 0, "", "", true) + return + } + for err == nil { + exe := syscall.UTF16ToString(module.ExePath[:]) + b.addMappingEntry( + uint64(module.ModBaseAddr), + uint64(module.ModBaseAddr)+uint64(module.ModBaseSize), + 0, + exe, + peBuildID(exe), + false, + ) + err = windows.Module32Next(snap, &module) + } +} + +func readMainModuleMapping() (start, end uint64, err error) { + snap, err := createModuleSnapshot() + if err != nil { + return 0, 0, err + } + defer func() { _ = syscall.CloseHandle(snap) }() + + var module windows.ModuleEntry32 + module.Size = uint32(windows.SizeofModuleEntry32) + err = windows.Module32First(snap, &module) + if err != nil { + return 0, 0, err + } + + return uint64(module.ModBaseAddr), uint64(module.ModBaseAddr) + uint64(module.ModBaseSize), nil +} + +func createModuleSnapshot() (syscall.Handle, error) { + for { + snap, err := syscall.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE|windows.TH32CS_SNAPMODULE32, uint32(syscall.Getpid())) + var errno syscall.Errno + if err != nil && errors.As(err, &errno) && errno == windows.ERROR_BAD_LENGTH { + // When CreateToolhelp32Snapshot(SNAPMODULE|SNAPMODULE32, ...) fails + // with ERROR_BAD_LENGTH then it should be retried until it succeeds. + continue + } + return snap, err + } +} diff --git a/src/runtime/pprof/rusage_test.go b/src/runtime/pprof/rusage_test.go new file mode 100644 index 00000000000000..b82b1af7680a33 --- /dev/null +++ b/src/runtime/pprof/rusage_test.go @@ -0,0 +1,41 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package pprof + +import ( + "syscall" + "time" +) + +func init() { + diffCPUTimeImpl = diffCPUTimeRUsage +} + +func diffCPUTimeRUsage(f func()) (user, system time.Duration) { + ok := true + var before, after syscall.Rusage + + err := syscall.Getrusage(syscall.RUSAGE_SELF, &before) + if err != nil { + ok = false + } + + f() + + err = syscall.Getrusage(syscall.RUSAGE_SELF, &after) + if err != nil { + ok = false + } + + if !ok { + return 0, 0 + } + + user = time.Duration(after.Utime.Nano() - before.Utime.Nano()) + system = time.Duration(after.Stime.Nano() - before.Stime.Nano()) + return user, system +} diff --git a/src/runtime/preempt.go b/src/runtime/preempt.go index 1d5aae1363193d..4f62fc628b6608 100644 --- a/src/runtime/preempt.go +++ b/src/runtime/preempt.go @@ -53,9 +53,8 @@ package runtime import ( - "runtime/internal/atomic" - "runtime/internal/sys" - "unsafe" + "internal/abi" + "internal/goarch" ) type suspendGState struct { @@ -192,7 +191,7 @@ func suspendG(gp *g) suspendGState { case _Grunning: // Optimization: if there is already a pending preemption request // (from the previous loop iteration), don't bother with the atomics. - if gp.preemptStop && gp.preempt && gp.stackguard0 == stackPreempt && asyncM == gp.m && atomic.Load(&asyncM.preemptGen) == asyncGen { + if gp.preemptStop && gp.preempt && gp.stackguard0 == stackPreempt && asyncM == gp.m && asyncM.preemptGen.Load() == asyncGen { break } @@ -208,7 +207,7 @@ func suspendG(gp *g) suspendGState { // Prepare for asynchronous preemption. asyncM2 := gp.m - asyncGen2 := atomic.Load(&asyncM2.preemptGen) + asyncGen2 := asyncM2.preemptGen.Load() needAsync := asyncM != asyncM2 || asyncGen != asyncGen2 asyncM = asyncM2 asyncGen = asyncGen2 @@ -315,12 +314,12 @@ func asyncPreempt2() { var asyncPreemptStack = ^uintptr(0) func init() { - f := findfunc(funcPC(asyncPreempt)) + f := findfunc(abi.FuncPCABI0(asyncPreempt)) total := funcMaxSPDelta(f) - f = findfunc(funcPC(asyncPreempt2)) + f = findfunc(abi.FuncPCABIInternal(asyncPreempt2)) total += funcMaxSPDelta(f) // Add some overhead for return PCs, etc. - asyncPreemptStack = uintptr(total) + 8*sys.PtrSize + asyncPreemptStack = uintptr(total) + 8*goarch.PtrSize if asyncPreemptStack > _StackLimit { // We need more than the nosplit limit. This isn't // unsafe, but it may limit asynchronous preemption. @@ -398,18 +397,15 @@ func isAsyncSafePoint(gp *g, pc, sp, lr uintptr) (bool, uintptr) { return false, 0 } up, startpc := pcdatavalue2(f, _PCDATA_UnsafePoint, pc) - if up != _PCDATA_UnsafePointSafe { + if up == _PCDATA_UnsafePointUnsafe { // Unsafe-point marked by compiler. This includes // atomic sequences (e.g., write barrier) and nosplit // functions (except at calls). return false, 0 } - if fd := funcdata(f, _FUNCDATA_LocalsPointerMaps); fd == nil || fd == unsafe.Pointer(&no_pointers_stackmap) { - // This is assembly code. Don't assume it's - // well-formed. We identify assembly code by - // checking that it has either no stack map, or - // no_pointers_stackmap, which is the stack map - // for ones marked as NO_LOCAL_POINTERS. + if fd := funcdata(f, _FUNCDATA_LocalsPointerMaps); fd == nil || f.flag&funcFlag_ASM != 0 { + // This is assembly code. Don't assume it's well-formed. + // TODO: Empirically we still need the fd == nil check. Why? // // TODO: Are there cases that are safe but don't have a // locals pointer map, like empty frame functions? @@ -422,7 +418,7 @@ func isAsyncSafePoint(gp *g, pc, sp, lr uintptr) (bool, uintptr) { inltree := (*[1 << 20]inlinedCall)(inldata) ix := pcdatavalue(f, _PCDATA_InlTreeIndex, pc, nil) if ix >= 0 { - name = funcnameFromNameoff(f, inltree[ix].func_) + name = funcnameFromNameOff(f, inltree[ix].nameOff) } } if hasPrefix(name, "runtime.") || @@ -450,9 +446,7 @@ func isAsyncSafePoint(gp *g, pc, sp, lr uintptr) (bool, uintptr) { return true, startpc case _PCDATA_RestartAtEntry: // Restart from the function entry at resumption. - return true, f.entry + return true, f.entry() } return true, pc } - -var no_pointers_stackmap uint64 // defined in assembly, for NO_LOCAL_POINTERS macro diff --git a/src/runtime/preempt_386.s b/src/runtime/preempt_386.s index a803b24dc6f0b0..d57bc3d37c061e 100644 --- a/src/runtime/preempt_386.s +++ b/src/runtime/preempt_386.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHFL ADJSP $156 NOP SP @@ -15,8 +14,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVL BP, 16(SP) MOVL SI, 20(SP) MOVL DI, 24(SP) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse + #ifndef GO386_softfloat MOVUPS X0, 28(SP) MOVUPS X1, 44(SP) MOVUPS X2, 60(SP) @@ -25,10 +23,9 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVUPS X5, 108(SP) MOVUPS X6, 124(SP) MOVUPS X7, 140(SP) -nosse: + #endif CALL ·asyncPreempt2(SB) - CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1 - JNE nosse2 + #ifndef GO386_softfloat MOVUPS 140(SP), X7 MOVUPS 124(SP), X6 MOVUPS 108(SP), X5 @@ -37,7 +34,7 @@ nosse: MOVUPS 60(SP), X2 MOVUPS 44(SP), X1 MOVUPS 28(SP), X0 -nosse2: + #endif MOVL 24(SP), DI MOVL 20(SP), SI MOVL 16(SP), BP diff --git a/src/runtime/preempt_amd64.s b/src/runtime/preempt_amd64.s index dc7af806d32f3e..94a84fb74cafa7 100644 --- a/src/runtime/preempt_amd64.s +++ b/src/runtime/preempt_amd64.s @@ -1,10 +1,10 @@ // Code generated by mkpreempt.go; DO NOT EDIT. #include "go_asm.h" +#include "asm_amd64.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 PUSHQ BP MOVQ SP, BP // Save flags before clobbering them @@ -28,8 +28,10 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVQ R14, 96(SP) MOVQ R15, 104(SP) #ifdef GOOS_darwin + #ifndef hasAVX CMPB internal∕cpu·X86+const_offsetX86HasAVX(SB), $0 JE 2(PC) + #endif VZEROUPPER #endif MOVUPS X0, 112(SP) diff --git a/src/runtime/preempt_arm.s b/src/runtime/preempt_arm.s index bbc9fbb1eae1d6..8f243c0dcd6081 100644 --- a/src/runtime/preempt_arm.s +++ b/src/runtime/preempt_arm.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW.W R14, -188(R13) MOVW R0, 4(R13) MOVW R1, 8(R13) diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s index 2b70a28479dd35..c27d475dee6ae0 100644 --- a/src/runtime/preempt_arm64.s +++ b/src/runtime/preempt_arm64.s @@ -3,146 +3,83 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R30, -496(RSP) SUB $496, RSP - #ifdef GOOS_linux MOVD R29, -8(RSP) SUB $8, RSP, R29 - #endif #ifdef GOOS_ios MOVD R30, (RSP) #endif - MOVD R0, 8(RSP) - MOVD R1, 16(RSP) - MOVD R2, 24(RSP) - MOVD R3, 32(RSP) - MOVD R4, 40(RSP) - MOVD R5, 48(RSP) - MOVD R6, 56(RSP) - MOVD R7, 64(RSP) - MOVD R8, 72(RSP) - MOVD R9, 80(RSP) - MOVD R10, 88(RSP) - MOVD R11, 96(RSP) - MOVD R12, 104(RSP) - MOVD R13, 112(RSP) - MOVD R14, 120(RSP) - MOVD R15, 128(RSP) - MOVD R16, 136(RSP) - MOVD R17, 144(RSP) - MOVD R19, 152(RSP) - MOVD R20, 160(RSP) - MOVD R21, 168(RSP) - MOVD R22, 176(RSP) - MOVD R23, 184(RSP) - MOVD R24, 192(RSP) - MOVD R25, 200(RSP) - MOVD R26, 208(RSP) + STP (R0, R1), 8(RSP) + STP (R2, R3), 24(RSP) + STP (R4, R5), 40(RSP) + STP (R6, R7), 56(RSP) + STP (R8, R9), 72(RSP) + STP (R10, R11), 88(RSP) + STP (R12, R13), 104(RSP) + STP (R14, R15), 120(RSP) + STP (R16, R17), 136(RSP) + STP (R19, R20), 152(RSP) + STP (R21, R22), 168(RSP) + STP (R23, R24), 184(RSP) + STP (R25, R26), 200(RSP) MOVD NZCV, R0 MOVD R0, 216(RSP) MOVD FPSR, R0 MOVD R0, 224(RSP) - FMOVD F0, 232(RSP) - FMOVD F1, 240(RSP) - FMOVD F2, 248(RSP) - FMOVD F3, 256(RSP) - FMOVD F4, 264(RSP) - FMOVD F5, 272(RSP) - FMOVD F6, 280(RSP) - FMOVD F7, 288(RSP) - FMOVD F8, 296(RSP) - FMOVD F9, 304(RSP) - FMOVD F10, 312(RSP) - FMOVD F11, 320(RSP) - FMOVD F12, 328(RSP) - FMOVD F13, 336(RSP) - FMOVD F14, 344(RSP) - FMOVD F15, 352(RSP) - FMOVD F16, 360(RSP) - FMOVD F17, 368(RSP) - FMOVD F18, 376(RSP) - FMOVD F19, 384(RSP) - FMOVD F20, 392(RSP) - FMOVD F21, 400(RSP) - FMOVD F22, 408(RSP) - FMOVD F23, 416(RSP) - FMOVD F24, 424(RSP) - FMOVD F25, 432(RSP) - FMOVD F26, 440(RSP) - FMOVD F27, 448(RSP) - FMOVD F28, 456(RSP) - FMOVD F29, 464(RSP) - FMOVD F30, 472(RSP) - FMOVD F31, 480(RSP) + FSTPD (F0, F1), 232(RSP) + FSTPD (F2, F3), 248(RSP) + FSTPD (F4, F5), 264(RSP) + FSTPD (F6, F7), 280(RSP) + FSTPD (F8, F9), 296(RSP) + FSTPD (F10, F11), 312(RSP) + FSTPD (F12, F13), 328(RSP) + FSTPD (F14, F15), 344(RSP) + FSTPD (F16, F17), 360(RSP) + FSTPD (F18, F19), 376(RSP) + FSTPD (F20, F21), 392(RSP) + FSTPD (F22, F23), 408(RSP) + FSTPD (F24, F25), 424(RSP) + FSTPD (F26, F27), 440(RSP) + FSTPD (F28, F29), 456(RSP) + FSTPD (F30, F31), 472(RSP) CALL ·asyncPreempt2(SB) - FMOVD 480(RSP), F31 - FMOVD 472(RSP), F30 - FMOVD 464(RSP), F29 - FMOVD 456(RSP), F28 - FMOVD 448(RSP), F27 - FMOVD 440(RSP), F26 - FMOVD 432(RSP), F25 - FMOVD 424(RSP), F24 - FMOVD 416(RSP), F23 - FMOVD 408(RSP), F22 - FMOVD 400(RSP), F21 - FMOVD 392(RSP), F20 - FMOVD 384(RSP), F19 - FMOVD 376(RSP), F18 - FMOVD 368(RSP), F17 - FMOVD 360(RSP), F16 - FMOVD 352(RSP), F15 - FMOVD 344(RSP), F14 - FMOVD 336(RSP), F13 - FMOVD 328(RSP), F12 - FMOVD 320(RSP), F11 - FMOVD 312(RSP), F10 - FMOVD 304(RSP), F9 - FMOVD 296(RSP), F8 - FMOVD 288(RSP), F7 - FMOVD 280(RSP), F6 - FMOVD 272(RSP), F5 - FMOVD 264(RSP), F4 - FMOVD 256(RSP), F3 - FMOVD 248(RSP), F2 - FMOVD 240(RSP), F1 - FMOVD 232(RSP), F0 + FLDPD 472(RSP), (F30, F31) + FLDPD 456(RSP), (F28, F29) + FLDPD 440(RSP), (F26, F27) + FLDPD 424(RSP), (F24, F25) + FLDPD 408(RSP), (F22, F23) + FLDPD 392(RSP), (F20, F21) + FLDPD 376(RSP), (F18, F19) + FLDPD 360(RSP), (F16, F17) + FLDPD 344(RSP), (F14, F15) + FLDPD 328(RSP), (F12, F13) + FLDPD 312(RSP), (F10, F11) + FLDPD 296(RSP), (F8, F9) + FLDPD 280(RSP), (F6, F7) + FLDPD 264(RSP), (F4, F5) + FLDPD 248(RSP), (F2, F3) + FLDPD 232(RSP), (F0, F1) MOVD 224(RSP), R0 MOVD R0, FPSR MOVD 216(RSP), R0 MOVD R0, NZCV - MOVD 208(RSP), R26 - MOVD 200(RSP), R25 - MOVD 192(RSP), R24 - MOVD 184(RSP), R23 - MOVD 176(RSP), R22 - MOVD 168(RSP), R21 - MOVD 160(RSP), R20 - MOVD 152(RSP), R19 - MOVD 144(RSP), R17 - MOVD 136(RSP), R16 - MOVD 128(RSP), R15 - MOVD 120(RSP), R14 - MOVD 112(RSP), R13 - MOVD 104(RSP), R12 - MOVD 96(RSP), R11 - MOVD 88(RSP), R10 - MOVD 80(RSP), R9 - MOVD 72(RSP), R8 - MOVD 64(RSP), R7 - MOVD 56(RSP), R6 - MOVD 48(RSP), R5 - MOVD 40(RSP), R4 - MOVD 32(RSP), R3 - MOVD 24(RSP), R2 - MOVD 16(RSP), R1 - MOVD 8(RSP), R0 + LDP 200(RSP), (R25, R26) + LDP 184(RSP), (R23, R24) + LDP 168(RSP), (R21, R22) + LDP 152(RSP), (R19, R20) + LDP 136(RSP), (R16, R17) + LDP 120(RSP), (R14, R15) + LDP 104(RSP), (R12, R13) + LDP 88(RSP), (R10, R11) + LDP 72(RSP), (R8, R9) + LDP 56(RSP), (R6, R7) + LDP 40(RSP), (R4, R5) + LDP 24(RSP), (R2, R3) + LDP 8(RSP), (R0, R1) MOVD 496(RSP), R30 - #ifdef GOOS_linux MOVD -8(RSP), R29 - #endif MOVD (RSP), R27 ADD $512, RSP JMP (R27) diff --git a/src/runtime/preempt_loong64.s b/src/runtime/preempt_loong64.s new file mode 100644 index 00000000000000..ba59a07b7fd5af --- /dev/null +++ b/src/runtime/preempt_loong64.s @@ -0,0 +1,129 @@ +// Code generated by mkpreempt.go; DO NOT EDIT. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 + MOVV R1, -472(R3) + SUBV $472, R3 + MOVV R4, 8(R3) + MOVV R5, 16(R3) + MOVV R6, 24(R3) + MOVV R7, 32(R3) + MOVV R8, 40(R3) + MOVV R9, 48(R3) + MOVV R10, 56(R3) + MOVV R11, 64(R3) + MOVV R12, 72(R3) + MOVV R13, 80(R3) + MOVV R14, 88(R3) + MOVV R15, 96(R3) + MOVV R16, 104(R3) + MOVV R17, 112(R3) + MOVV R18, 120(R3) + MOVV R19, 128(R3) + MOVV R20, 136(R3) + MOVV R21, 144(R3) + MOVV R23, 152(R3) + MOVV R24, 160(R3) + MOVV R25, 168(R3) + MOVV R26, 176(R3) + MOVV R27, 184(R3) + MOVV R28, 192(R3) + MOVV R29, 200(R3) + MOVV RSB, 208(R3) + MOVD F0, 216(R3) + MOVD F1, 224(R3) + MOVD F2, 232(R3) + MOVD F3, 240(R3) + MOVD F4, 248(R3) + MOVD F5, 256(R3) + MOVD F6, 264(R3) + MOVD F7, 272(R3) + MOVD F8, 280(R3) + MOVD F9, 288(R3) + MOVD F10, 296(R3) + MOVD F11, 304(R3) + MOVD F12, 312(R3) + MOVD F13, 320(R3) + MOVD F14, 328(R3) + MOVD F15, 336(R3) + MOVD F16, 344(R3) + MOVD F17, 352(R3) + MOVD F18, 360(R3) + MOVD F19, 368(R3) + MOVD F20, 376(R3) + MOVD F21, 384(R3) + MOVD F22, 392(R3) + MOVD F23, 400(R3) + MOVD F24, 408(R3) + MOVD F25, 416(R3) + MOVD F26, 424(R3) + MOVD F27, 432(R3) + MOVD F28, 440(R3) + MOVD F29, 448(R3) + MOVD F30, 456(R3) + MOVD F31, 464(R3) + CALL ·asyncPreempt2(SB) + MOVD 464(R3), F31 + MOVD 456(R3), F30 + MOVD 448(R3), F29 + MOVD 440(R3), F28 + MOVD 432(R3), F27 + MOVD 424(R3), F26 + MOVD 416(R3), F25 + MOVD 408(R3), F24 + MOVD 400(R3), F23 + MOVD 392(R3), F22 + MOVD 384(R3), F21 + MOVD 376(R3), F20 + MOVD 368(R3), F19 + MOVD 360(R3), F18 + MOVD 352(R3), F17 + MOVD 344(R3), F16 + MOVD 336(R3), F15 + MOVD 328(R3), F14 + MOVD 320(R3), F13 + MOVD 312(R3), F12 + MOVD 304(R3), F11 + MOVD 296(R3), F10 + MOVD 288(R3), F9 + MOVD 280(R3), F8 + MOVD 272(R3), F7 + MOVD 264(R3), F6 + MOVD 256(R3), F5 + MOVD 248(R3), F4 + MOVD 240(R3), F3 + MOVD 232(R3), F2 + MOVD 224(R3), F1 + MOVD 216(R3), F0 + MOVV 208(R3), RSB + MOVV 200(R3), R29 + MOVV 192(R3), R28 + MOVV 184(R3), R27 + MOVV 176(R3), R26 + MOVV 168(R3), R25 + MOVV 160(R3), R24 + MOVV 152(R3), R23 + MOVV 144(R3), R21 + MOVV 136(R3), R20 + MOVV 128(R3), R19 + MOVV 120(R3), R18 + MOVV 112(R3), R17 + MOVV 104(R3), R16 + MOVV 96(R3), R15 + MOVV 88(R3), R14 + MOVV 80(R3), R13 + MOVV 72(R3), R12 + MOVV 64(R3), R11 + MOVV 56(R3), R10 + MOVV 48(R3), R9 + MOVV 40(R3), R8 + MOVV 32(R3), R7 + MOVV 24(R3), R6 + MOVV 16(R3), R5 + MOVV 8(R3), R4 + MOVV 472(R3), R1 + MOVV (R3), R30 + ADDV $480, R3 + JMP (R30) diff --git a/src/runtime/preempt_mips64x.s b/src/runtime/preempt_mips64x.s index b755425bc5d327..996b592ae06eb0 100644 --- a/src/runtime/preempt_mips64x.s +++ b/src/runtime/preempt_mips64x.s @@ -1,13 +1,11 @@ // Code generated by mkpreempt.go; DO NOT EDIT. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVV R31, -488(R29) SUBV $488, R29 MOVV R1, 8(R29) diff --git a/src/runtime/preempt_mipsx.s b/src/runtime/preempt_mipsx.s index c1bff60859610c..7b169acd99991b 100644 --- a/src/runtime/preempt_mipsx.s +++ b/src/runtime/preempt_mipsx.s @@ -1,13 +1,11 @@ // Code generated by mkpreempt.go; DO NOT EDIT. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW R31, -244(R29) SUB $244, R29 MOVW R1, 4(R29) diff --git a/src/runtime/preempt_nonwindows.go b/src/runtime/preempt_nonwindows.go index 365e86a611a4da..d6a2408cb7cd2e 100644 --- a/src/runtime/preempt_nonwindows.go +++ b/src/runtime/preempt_nonwindows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package runtime diff --git a/src/runtime/preempt_ppc64x.s b/src/runtime/preempt_ppc64x.s index 70bd91982bfa89..2c4d02edfe2abc 100644 --- a/src/runtime/preempt_ppc64x.s +++ b/src/runtime/preempt_ppc64x.s @@ -1,13 +1,11 @@ // Code generated by mkpreempt.go; DO NOT EDIT. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD R31, -488(R1) MOVD LR, R31 MOVDU R31, -520(R1) diff --git a/src/runtime/preempt_riscv64.s b/src/runtime/preempt_riscv64.s index d4f9cc277f4b62..56df6c30e07e44 100644 --- a/src/runtime/preempt_riscv64.s +++ b/src/runtime/preempt_riscv64.s @@ -3,128 +3,125 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 - MOV X1, -472(X2) - ADD $-472, X2 - MOV X3, 8(X2) - MOV X5, 16(X2) - MOV X6, 24(X2) - MOV X7, 32(X2) - MOV X8, 40(X2) - MOV X9, 48(X2) - MOV X10, 56(X2) - MOV X11, 64(X2) - MOV X12, 72(X2) - MOV X13, 80(X2) - MOV X14, 88(X2) - MOV X15, 96(X2) - MOV X16, 104(X2) - MOV X17, 112(X2) - MOV X18, 120(X2) - MOV X19, 128(X2) - MOV X20, 136(X2) - MOV X21, 144(X2) - MOV X22, 152(X2) - MOV X23, 160(X2) - MOV X24, 168(X2) - MOV X25, 176(X2) - MOV X26, 184(X2) - MOV X28, 192(X2) - MOV X29, 200(X2) - MOV X30, 208(X2) - MOVD F0, 216(X2) - MOVD F1, 224(X2) - MOVD F2, 232(X2) - MOVD F3, 240(X2) - MOVD F4, 248(X2) - MOVD F5, 256(X2) - MOVD F6, 264(X2) - MOVD F7, 272(X2) - MOVD F8, 280(X2) - MOVD F9, 288(X2) - MOVD F10, 296(X2) - MOVD F11, 304(X2) - MOVD F12, 312(X2) - MOVD F13, 320(X2) - MOVD F14, 328(X2) - MOVD F15, 336(X2) - MOVD F16, 344(X2) - MOVD F17, 352(X2) - MOVD F18, 360(X2) - MOVD F19, 368(X2) - MOVD F20, 376(X2) - MOVD F21, 384(X2) - MOVD F22, 392(X2) - MOVD F23, 400(X2) - MOVD F24, 408(X2) - MOVD F25, 416(X2) - MOVD F26, 424(X2) - MOVD F27, 432(X2) - MOVD F28, 440(X2) - MOVD F29, 448(X2) - MOVD F30, 456(X2) - MOVD F31, 464(X2) +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 + MOV X1, -464(X2) + ADD $-464, X2 + MOV X5, 8(X2) + MOV X6, 16(X2) + MOV X7, 24(X2) + MOV X8, 32(X2) + MOV X9, 40(X2) + MOV X10, 48(X2) + MOV X11, 56(X2) + MOV X12, 64(X2) + MOV X13, 72(X2) + MOV X14, 80(X2) + MOV X15, 88(X2) + MOV X16, 96(X2) + MOV X17, 104(X2) + MOV X18, 112(X2) + MOV X19, 120(X2) + MOV X20, 128(X2) + MOV X21, 136(X2) + MOV X22, 144(X2) + MOV X23, 152(X2) + MOV X24, 160(X2) + MOV X25, 168(X2) + MOV X26, 176(X2) + MOV X28, 184(X2) + MOV X29, 192(X2) + MOV X30, 200(X2) + MOVD F0, 208(X2) + MOVD F1, 216(X2) + MOVD F2, 224(X2) + MOVD F3, 232(X2) + MOVD F4, 240(X2) + MOVD F5, 248(X2) + MOVD F6, 256(X2) + MOVD F7, 264(X2) + MOVD F8, 272(X2) + MOVD F9, 280(X2) + MOVD F10, 288(X2) + MOVD F11, 296(X2) + MOVD F12, 304(X2) + MOVD F13, 312(X2) + MOVD F14, 320(X2) + MOVD F15, 328(X2) + MOVD F16, 336(X2) + MOVD F17, 344(X2) + MOVD F18, 352(X2) + MOVD F19, 360(X2) + MOVD F20, 368(X2) + MOVD F21, 376(X2) + MOVD F22, 384(X2) + MOVD F23, 392(X2) + MOVD F24, 400(X2) + MOVD F25, 408(X2) + MOVD F26, 416(X2) + MOVD F27, 424(X2) + MOVD F28, 432(X2) + MOVD F29, 440(X2) + MOVD F30, 448(X2) + MOVD F31, 456(X2) CALL ·asyncPreempt2(SB) - MOVD 464(X2), F31 - MOVD 456(X2), F30 - MOVD 448(X2), F29 - MOVD 440(X2), F28 - MOVD 432(X2), F27 - MOVD 424(X2), F26 - MOVD 416(X2), F25 - MOVD 408(X2), F24 - MOVD 400(X2), F23 - MOVD 392(X2), F22 - MOVD 384(X2), F21 - MOVD 376(X2), F20 - MOVD 368(X2), F19 - MOVD 360(X2), F18 - MOVD 352(X2), F17 - MOVD 344(X2), F16 - MOVD 336(X2), F15 - MOVD 328(X2), F14 - MOVD 320(X2), F13 - MOVD 312(X2), F12 - MOVD 304(X2), F11 - MOVD 296(X2), F10 - MOVD 288(X2), F9 - MOVD 280(X2), F8 - MOVD 272(X2), F7 - MOVD 264(X2), F6 - MOVD 256(X2), F5 - MOVD 248(X2), F4 - MOVD 240(X2), F3 - MOVD 232(X2), F2 - MOVD 224(X2), F1 - MOVD 216(X2), F0 - MOV 208(X2), X30 - MOV 200(X2), X29 - MOV 192(X2), X28 - MOV 184(X2), X26 - MOV 176(X2), X25 - MOV 168(X2), X24 - MOV 160(X2), X23 - MOV 152(X2), X22 - MOV 144(X2), X21 - MOV 136(X2), X20 - MOV 128(X2), X19 - MOV 120(X2), X18 - MOV 112(X2), X17 - MOV 104(X2), X16 - MOV 96(X2), X15 - MOV 88(X2), X14 - MOV 80(X2), X13 - MOV 72(X2), X12 - MOV 64(X2), X11 - MOV 56(X2), X10 - MOV 48(X2), X9 - MOV 40(X2), X8 - MOV 32(X2), X7 - MOV 24(X2), X6 - MOV 16(X2), X5 - MOV 8(X2), X3 - MOV 472(X2), X1 + MOVD 456(X2), F31 + MOVD 448(X2), F30 + MOVD 440(X2), F29 + MOVD 432(X2), F28 + MOVD 424(X2), F27 + MOVD 416(X2), F26 + MOVD 408(X2), F25 + MOVD 400(X2), F24 + MOVD 392(X2), F23 + MOVD 384(X2), F22 + MOVD 376(X2), F21 + MOVD 368(X2), F20 + MOVD 360(X2), F19 + MOVD 352(X2), F18 + MOVD 344(X2), F17 + MOVD 336(X2), F16 + MOVD 328(X2), F15 + MOVD 320(X2), F14 + MOVD 312(X2), F13 + MOVD 304(X2), F12 + MOVD 296(X2), F11 + MOVD 288(X2), F10 + MOVD 280(X2), F9 + MOVD 272(X2), F8 + MOVD 264(X2), F7 + MOVD 256(X2), F6 + MOVD 248(X2), F5 + MOVD 240(X2), F4 + MOVD 232(X2), F3 + MOVD 224(X2), F2 + MOVD 216(X2), F1 + MOVD 208(X2), F0 + MOV 200(X2), X30 + MOV 192(X2), X29 + MOV 184(X2), X28 + MOV 176(X2), X26 + MOV 168(X2), X25 + MOV 160(X2), X24 + MOV 152(X2), X23 + MOV 144(X2), X22 + MOV 136(X2), X21 + MOV 128(X2), X20 + MOV 120(X2), X19 + MOV 112(X2), X18 + MOV 104(X2), X17 + MOV 96(X2), X16 + MOV 88(X2), X15 + MOV 80(X2), X14 + MOV 72(X2), X13 + MOV 64(X2), X12 + MOV 56(X2), X11 + MOV 48(X2), X10 + MOV 40(X2), X9 + MOV 32(X2), X8 + MOV 24(X2), X7 + MOV 16(X2), X6 + MOV 8(X2), X5 + MOV 464(X2), X1 MOV (X2), X31 - ADD $480, X2 + ADD $472, X2 JMP (X31) diff --git a/src/runtime/preempt_s390x.s b/src/runtime/preempt_s390x.s index c6f11571df7788..ca9e47cde17913 100644 --- a/src/runtime/preempt_s390x.s +++ b/src/runtime/preempt_s390x.s @@ -3,8 +3,7 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 IPM R10 MOVD R14, -248(R15) ADD $-248, R15 diff --git a/src/runtime/preempt_wasm.s b/src/runtime/preempt_wasm.s index da90e8aa6dd311..0cf57d3d2265bb 100644 --- a/src/runtime/preempt_wasm.s +++ b/src/runtime/preempt_wasm.s @@ -3,7 +3,6 @@ #include "go_asm.h" #include "textflag.h" -// Note: asyncPreempt doesn't use the internal ABI, but we must be able to inject calls to it from the signal handler, so Go code has to see the PC of this function literally. -TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 // No async preemption on wasm UNDEF diff --git a/src/runtime/print.go b/src/runtime/print.go index f15296cf024587..a1e0b8e134e945 100644 --- a/src/runtime/print.go +++ b/src/runtime/print.go @@ -5,8 +5,7 @@ package runtime import ( - "runtime/internal/atomic" - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -40,7 +39,7 @@ var ( func recordForPanic(b []byte) { printlock() - if atomic.Load(&panicking) == 0 { + if panicking.Load() == 0 { // Not actively crashing: maintain circular buffer of print output. for i := 0; i < len(b); { n := copy(printBacklog[printBacklogIndex:], b[i:]) @@ -271,7 +270,7 @@ func hexdumpWords(p, end uintptr, mark func(uintptr) byte) { var markbuf [1]byte markbuf[0] = ' ' minhexdigits = int(unsafe.Sizeof(uintptr(0)) * 2) - for i := uintptr(0); p+i < end; i += sys.PtrSize { + for i := uintptr(0); p+i < end; i += goarch.PtrSize { if i%16 == 0 { if i != 0 { println() @@ -293,7 +292,7 @@ func hexdumpWords(p, end uintptr, mark func(uintptr) byte) { // Can we symbolize val? fn := findfunc(val) if fn.valid() { - print("<", funcname(fn), "+", hex(val-fn.entry), "> ") + print("<", funcname(fn), "+", hex(val-fn.entry()), "> ") } } minhexdigits = 0 diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 7bc2a92590755e..1e4d4098b63286 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -7,7 +7,7 @@ package runtime import ( "internal/abi" "internal/cpu" - "internal/goexperiment" + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -73,7 +73,7 @@ var modinfo string // If there is at least one spinning thread (sched.nmspinning>1), we don't // unpark new threads when submitting work. To compensate for that, if the last // spinning thread finds work and stops spinning, it must unpark a new spinning -// thread. This approach smooths out unjustified spikes of thread unparking, +// thread. This approach smooths out unjustified spikes of thread unparking, // but at the same time guarantees eventual maximal CPU parallelism // utilization. // @@ -143,16 +143,16 @@ var initSigmask sigset // The main goroutine. func main() { - g := getg() + mp := getg().m // Racectx of m0->g0 is used only as the parent of the main goroutine. // It must not be used for anything else. - g.m.g0.racectx = 0 + mp.g0.racectx = 0 // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit. // Using decimal instead of binary GB and MB because // they look nicer in the stack overflow failure message. - if sys.PtrSize == 8 { + if goarch.PtrSize == 8 { maxstacksize = 1000000000 } else { maxstacksize = 250000000 @@ -167,10 +167,6 @@ func main() { mainStarted = true if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon - // For runtime_syscall_doAllThreadsSyscall, we - // register sysmon is not ready for the world to be - // stopped. - atomic.Store(&sched.sysmonStarting, 1) systemstack(func() { newm(sysmon, nil, -1) }) @@ -184,10 +180,9 @@ func main() { // to preserve the lock. lockOSThread() - if g.m != &m0 { + if mp != &m0 { throw("runtime.main not on m0") } - m0.doesPark = true // Record when the world started. // Must be before doInit for tracing init. @@ -261,16 +256,16 @@ func main() { // another goroutine at the same time as main returns, // let the other goroutine finish printing the panic trace. // Once it does, it will exit. See issues 3934 and 20018. - if atomic.Load(&runningPanicDefers) != 0 { + if runningPanicDefers.Load() != 0 { // Running deferred functions should not take long. for c := 0; c < 1000; c++ { - if atomic.Load(&runningPanicDefers) == 0 { + if runningPanicDefers.Load() == 0 { break } Gosched() } } - if atomic.Load(&panicking) != 0 { + if panicking.Load() != 0 { gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1) } @@ -282,6 +277,7 @@ func main() { } // os_beforeExit is called from os.Exit(0). +// //go:linkname os_beforeExit os.runtime_beforeExit func os_beforeExit() { if raceenabled { @@ -299,10 +295,10 @@ func forcegchelper() { lockInit(&forcegc.lock, lockRankForcegc) for { lock(&forcegc.lock) - if forcegc.idle != 0 { + if forcegc.idle.Load() { throw("forcegc: phase error") } - atomic.Store(&forcegc.idle, 1) + forcegc.idle.Store(true) goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceEvGoBlock, 1) // this goroutine is explicitly resumed by sysmon if debug.gctrace > 0 { @@ -324,6 +320,7 @@ func Gosched() { // goschedguarded yields the processor like gosched, but also checks // for forbidden states and opts out of the yield in those cases. +// //go:nosplit func goschedguarded() { mcall(goschedguarded_m) @@ -466,18 +463,6 @@ func releaseSudog(s *sudog) { releasem(mp) } -// funcPC returns the entry PC of the function f. -// It assumes that f is a func value. Otherwise the behavior is undefined. -// CAREFUL: In programs with plugins, funcPC can return different values -// for the same function (because there are actually multiple copies of -// the same function in the address space). To be safe, don't use the -// results of this function in any == expression. It is only safe to -// use the result as an address at which to start executing code. -//go:nosplit -func funcPC(f interface{}) uintptr { - return *(*uintptr)(efaceOf(&f).data) -} - // called from assembly func badmcall(fn func(*g)) { throw("runtime: mcall called on m->g0 stack") @@ -559,6 +544,20 @@ func allgadd(gp *g) { unlock(&allglock) } +// allGsSnapshot returns a snapshot of the slice of all Gs. +// +// The world must be stopped or allglock must be held. +func allGsSnapshot() []*g { + assertWorldStoppedOrLockHeld(&allglock) + + // Because the world is stopped or allglock is held, allgadd + // cannot happen concurrently with this. allgs grows + // monotonically and existing entries never change, so we can + // simply return a copy of the slice header. For added safety, + // we trim everything past len because that can still change. + return allgs[:len(allgs):len(allgs)] +} + // atomicAllG returns &allgs[0] and len(allgs) for use with atomicAllGIndex. func atomicAllG() (**g, uintptr) { length := atomic.Loaduintptr(&allglen) @@ -568,7 +567,7 @@ func atomicAllG() (**g, uintptr) { // atomicAllGIndex returns ptr[i] with the allgptr returned from atomicAllG. func atomicAllGIndex(ptr **g, i uintptr) *g { - return *(**g)(add(unsafe.Pointer(ptr), i*sys.PtrSize)) + return *(**g)(add(unsafe.Pointer(ptr), i*goarch.PtrSize)) } // forEachG calls fn on every G from allgs. @@ -621,7 +620,7 @@ func cpuinit() { for i := int32(0); i < n; i++ { p := argv_index(argv, argc+1+i) - s := *(*string)(unsafe.Pointer(&stringStruct{unsafe.Pointer(p), findnull(p)})) + s := unsafe.String(p, findnull(p)) if hasPrefix(s, prefix) { env = gostring(p)[len(prefix):] @@ -634,13 +633,18 @@ func cpuinit() { // Support cpu feature variables are used in code generated by the compiler // to guard execution of instructions that can not be assumed to be always supported. - x86HasPOPCNT = cpu.X86.HasPOPCNT - x86HasSSE41 = cpu.X86.HasSSE41 - x86HasFMA = cpu.X86.HasFMA + switch GOARCH { + case "386", "amd64": + x86HasPOPCNT = cpu.X86.HasPOPCNT + x86HasSSE41 = cpu.X86.HasSSE41 + x86HasFMA = cpu.X86.HasFMA - armHasVFPv4 = cpu.ARM.HasVFPv4 + case "arm": + armHasVFPv4 = cpu.ARM.HasVFPv4 - arm64HasATOMICS = cpu.ARM64.HasATOMICS + case "arm64": + arm64HasATOMICS = cpu.ARM64.HasATOMICS + } } // The bootstrap sequence is: @@ -674,9 +678,9 @@ func schedinit() { // raceinit must be the first call to race detector. // In particular, it must be done before mallocinit below calls racemapshadow. - _g_ := getg() + gp := getg() if raceenabled { - _g_.racectx, raceprocctx0 = raceinit() + gp.racectx, raceprocctx0 = raceinit() } sched.maxmcount = 10000 @@ -687,21 +691,17 @@ func schedinit() { moduledataverify() stackinit() mallocinit() + cpuinit() // must run before alginit + alginit() // maps, hash, fastrand must not be used before this call fastrandinit() // must run before mcommoninit - mcommoninit(_g_.m, -1) - cpuinit() // must run before alginit - alginit() // maps must not be used before this call + mcommoninit(gp.m, -1) modulesinit() // provides activeModules typelinksinit() // uses maps, activeModules itabsinit() // uses activeModules + stkobjinit() // must run before GC starts - sigsave(&_g_.m.sigmask) - initSigmask = _g_.m.sigmask - - if offset := unsafe.Offsetof(sched.timeToRun); offset%8 != 0 { - println(offset) - throw("sched.timeToRun not aligned to 8 bytes") - } + sigsave(&gp.m.sigmask) + initSigmask = gp.m.sigmask goargs() goenvs() @@ -709,7 +709,7 @@ func schedinit() { gcinit() lock(&sched.lock) - sched.lastpoll = uint64(nanotime()) + sched.lastpoll.Store(nanotime()) procs := ncpu if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 { procs = n @@ -728,8 +728,8 @@ func schedinit() { if debug.cgocheck > 1 { writeBarrier.cgo = true writeBarrier.enabled = true - for _, p := range allp { - p.wbBuf.reset() + for _, pp := range allp { + pp.wbBuf.reset() } } @@ -746,9 +746,9 @@ func schedinit() { } func dumpgstatus(gp *g) { - _g_ := getg() - print("runtime: gp: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n") - print("runtime: g: g=", _g_, ", goid=", _g_.goid, ", g->atomicstatus=", readgstatus(_g_), "\n") + thisg := getg() + print("runtime: gp: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n") + print("runtime: getg: g=", thisg, ", goid=", thisg.goid, ", g->atomicstatus=", readgstatus(thisg), "\n") } // sched.lock must be held. @@ -779,10 +779,10 @@ func mReserveID() int64 { // Pre-allocated ID may be passed as 'id', or omitted by passing -1. func mcommoninit(mp *m, id int64) { - _g_ := getg() + gp := getg() // g0 stack won't make sense for user (and is not necessary unwindable). - if _g_ != _g_.m.g0 { + if gp != gp.m.g0 { callers(1, mp.createstack[:]) } @@ -794,10 +794,17 @@ func mcommoninit(mp *m, id int64) { mp.id = mReserveID() } - mp.fastrand[0] = uint32(int64Hash(uint64(mp.id), fastrandseed)) - mp.fastrand[1] = uint32(int64Hash(uint64(cputicks()), ^fastrandseed)) - if mp.fastrand[0]|mp.fastrand[1] == 0 { - mp.fastrand[1] = 1 + lo := uint32(int64Hash(uint64(mp.id), fastrandseed)) + hi := uint32(int64Hash(uint64(cputicks()), ^fastrandseed)) + if lo|hi == 0 { + hi = 1 + } + // Same behavior as for 1.17. + // TODO: Simplify ths. + if goarch.BigEndian { + mp.fastrand = uint64(lo)<<32 | uint64(hi) + } else { + mp.fastrand = uint64(hi)<<32 | uint64(lo) } mpreinit(mp) @@ -820,6 +827,12 @@ func mcommoninit(mp *m, id int64) { } } +func (mp *m) becomeSpinning() { + mp.spinning = true + sched.nmspinning.Add(1) + sched.needspinning.Store(0) +} + var fastrandseed uintptr func fastrandinit() { @@ -836,7 +849,6 @@ func ready(gp *g, traceskip int, next bool) { status := readgstatus(gp) // Mark runnable. - _g_ := getg() mp := acquirem() // disable preemption because it can be holding p in a local var if status&^_Gscan != _Gwaiting { dumpgstatus(gp) @@ -845,7 +857,7 @@ func ready(gp *g, traceskip int, next bool) { // status is Gwaiting or Gscanwaiting, make Grunnable and put on runq casgstatus(gp, _Gwaiting, _Grunnable) - runqput(_g_.m.p.ptr(), gp, next) + runqput(mp.p.ptr(), gp, next) wakep() releasem(mp) } @@ -856,20 +868,20 @@ const freezeStopWait = 0x7fffffff // freezing is set to non-zero if the runtime is trying to freeze the // world. -var freezing uint32 +var freezing atomic.Bool // Similar to stopTheWorld but best-effort and can be called several times. // There is no reverse operation, used during crashing. // This function must not lock any mutexes. func freezetheworld() { - atomic.Store(&freezing, 1) + freezing.Store(true) // stopwait and preemption requests can be lost // due to races with concurrently executing threads, // so try several times for i := 0; i < 5; i++ { // this should tell the scheduler to not start any new goroutines sched.stopwait = freezeStopWait - atomic.Store(&sched.gcwaiting, 1) + sched.gcwaiting.Store(true) // this should stop running goroutines if !preemptall() { break // no running goroutines @@ -884,9 +896,10 @@ func freezetheworld() { // All reads and writes of g's status go through readgstatus, casgstatus // castogscanstatus, casfrom_Gscanstatus. +// //go:nosplit func readgstatus(gp *g) uint32 { - return atomic.Load(&gp.atomicstatus) + return gp.atomicstatus.Load() } // The Gscanstatuses are acting like locks and this releases them. @@ -908,7 +921,7 @@ func casfrom_Gscanstatus(gp *g, oldval, newval uint32) { _Gscansyscall, _Gscanpreempted: if newval == oldval&^_Gscan { - success = atomic.Cas(&gp.atomicstatus, oldval, newval) + success = gp.atomicstatus.CompareAndSwap(oldval, newval) } } if !success { @@ -928,7 +941,7 @@ func castogscanstatus(gp *g, oldval, newval uint32) bool { _Gwaiting, _Gsyscall: if newval == oldval|_Gscan { - r := atomic.Cas(&gp.atomicstatus, oldval, newval) + r := gp.atomicstatus.CompareAndSwap(oldval, newval) if r { acquireLockRank(lockRankGscan) } @@ -945,6 +958,7 @@ func castogscanstatus(gp *g, oldval, newval uint32) bool { // and casfrom_Gscanstatus instead. // casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that // put it in the Gscan state is finished. +// //go:nosplit func casgstatus(gp *g, oldval, newval uint32) { if (oldval&_Gscan != 0) || (newval&_Gscan != 0) || oldval == newval { @@ -963,15 +977,15 @@ func casgstatus(gp *g, oldval, newval uint32) { // loop if gp->atomicstatus is in a scan state giving // GC time to finish and change the state to oldval. - for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ { - if oldval == _Gwaiting && gp.atomicstatus == _Grunnable { + for i := 0; !gp.atomicstatus.CompareAndSwap(oldval, newval); i++ { + if oldval == _Gwaiting && gp.atomicstatus.Load() == _Grunnable { throw("casgstatus: waiting for Gwaiting but is Grunnable") } if i == 0 { nextYield = nanotime() + yieldDelay } if nanotime() < nextYield { - for x := 0; x < 10 && gp.atomicstatus != oldval; x++ { + for x := 0; x < 10 && gp.atomicstatus.Load() != oldval; x++ { procyield(1) } } else { @@ -989,17 +1003,18 @@ func casgstatus(gp *g, oldval, newval uint32) { gp.trackingSeq++ } if gp.tracking { - now := nanotime() if oldval == _Grunnable { // We transitioned out of runnable, so measure how much // time we spent in this state and add it to // runnableTime. + now := nanotime() gp.runnableTime += now - gp.runnableStamp gp.runnableStamp = 0 } if newval == _Grunnable { // We just transitioned into runnable, so record what // time that happened. + now := nanotime() gp.runnableStamp = now } else if newval == _Grunning { // We're transitioning into running, so turn off @@ -1017,6 +1032,7 @@ func casgstatus(gp *g, oldval, newval uint32) { // async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus, // it might have become Grunnable by the time we get to the cas. If we called casgstatus, // it would loop waiting for the status to go back to Gwaiting, which it never will. +// //go:nosplit func casgcopystack(gp *g) uint32 { for { @@ -1024,7 +1040,7 @@ func casgcopystack(gp *g) uint32 { if oldstatus != _Gwaiting && oldstatus != _Grunnable { throw("copystack: bad status, not Gwaiting or Grunnable") } - if atomic.Cas(&gp.atomicstatus, oldstatus, _Gcopystack) { + if gp.atomicstatus.CompareAndSwap(oldstatus, _Gcopystack) { return oldstatus } } @@ -1039,7 +1055,7 @@ func casGToPreemptScan(gp *g, old, new uint32) { throw("bad g transition") } acquireLockRank(lockRankGscan) - for !atomic.Cas(&gp.atomicstatus, _Grunning, _Gscan|_Gpreempted) { + for !gp.atomicstatus.CompareAndSwap(_Grunning, _Gscan|_Gpreempted) { } } @@ -1050,7 +1066,7 @@ func casGFromPreempted(gp *g, old, new uint32) bool { if old != _Gpreempted || new != _Gwaiting { throw("bad g transition") } - return atomic.Cas(&gp.atomicstatus, _Gpreempted, _Gwaiting) + return gp.atomicstatus.CompareAndSwap(_Gpreempted, _Gwaiting) } // stopTheWorld stops all P's from executing goroutines, interrupting @@ -1161,40 +1177,41 @@ var gcsema uint32 = 1 // Holding worldsema causes any other goroutines invoking // stopTheWorld to block. func stopTheWorldWithSema() { - _g_ := getg() + gp := getg() // If we hold a lock, then we won't be able to stop another M // that is blocked trying to acquire the lock. - if _g_.m.locks > 0 { + if gp.m.locks > 0 { throw("stopTheWorld: holding locks") } lock(&sched.lock) sched.stopwait = gomaxprocs - atomic.Store(&sched.gcwaiting, 1) + sched.gcwaiting.Store(true) preemptall() // stop current P - _g_.m.p.ptr().status = _Pgcstop // Pgcstop is only diagnostic. + gp.m.p.ptr().status = _Pgcstop // Pgcstop is only diagnostic. sched.stopwait-- // try to retake all P's in Psyscall status - for _, p := range allp { - s := p.status - if s == _Psyscall && atomic.Cas(&p.status, s, _Pgcstop) { + for _, pp := range allp { + s := pp.status + if s == _Psyscall && atomic.Cas(&pp.status, s, _Pgcstop) { if trace.enabled { - traceGoSysBlock(p) - traceProcStop(p) + traceGoSysBlock(pp) + traceProcStop(pp) } - p.syscalltick++ + pp.syscalltick++ sched.stopwait-- } } // stop idle P's + now := nanotime() for { - p := pidleget() - if p == nil { + pp, _ := pidleget(now) + if pp == nil { break } - p.status = _Pgcstop + pp.status = _Pgcstop sched.stopwait-- } wait := sched.stopwait > 0 @@ -1217,13 +1234,13 @@ func stopTheWorldWithSema() { if sched.stopwait != 0 { bad = "stopTheWorld: not stopped (stopwait != 0)" } else { - for _, p := range allp { - if p.status != _Pgcstop { + for _, pp := range allp { + if pp.status != _Pgcstop { bad = "stopTheWorld: not stopped (status != _Pgcstop)" } } } - if atomic.Load(&freezing) != 0 { + if freezing.Load() { // Some other thread is panicking. This can cause the // sanity checks above to fail if the panic happens in // the signal handler on a stopped thread. Either way, @@ -1254,9 +1271,9 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { newprocs = 0 } p1 := procresize(procs) - sched.gcwaiting = 0 - if sched.sysmonwait != 0 { - sched.sysmonwait = 0 + sched.gcwaiting.Store(false) + if sched.sysmonwait.Load() { + sched.sysmonwait.Store(false) notewakeup(&sched.sysmonnote) } unlock(&sched.lock) @@ -1337,9 +1354,9 @@ func mstart() //go:nosplit //go:nowritebarrierrec func mstart0() { - _g_ := getg() + gp := getg() - osStack := _g_.stack.lo == 0 + osStack := gp.stack.lo == 0 if osStack { // Initialize stack bounds from system stack. // Cgo may have left stack size in stack.hi. @@ -1349,25 +1366,25 @@ func mstart0() { // We set hi to &size, but there are things above // it. The 1024 is supposed to compensate this, // but is somewhat arbitrary. - size := _g_.stack.hi + size := gp.stack.hi if size == 0 { size = 8192 * sys.StackGuardMultiplier } - _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&size))) - _g_.stack.lo = _g_.stack.hi - size + 1024 + gp.stack.hi = uintptr(noescape(unsafe.Pointer(&size))) + gp.stack.lo = gp.stack.hi - size + 1024 } // Initialize stack guard so that we can start calling regular // Go code. - _g_.stackguard0 = _g_.stack.lo + _StackGuard + gp.stackguard0 = gp.stack.lo + _StackGuard // This is the g0, so we can also call go:systemstack // functions, which check stackguard1. - _g_.stackguard1 = _g_.stackguard0 + gp.stackguard1 = gp.stackguard0 mstart1() // Exit this thread. if mStackIsSystemAllocated() { // Windows, Solaris, illumos, Darwin, AIX and Plan 9 always system-allocate - // the stack, but put it in _g_.stack before mstart, + // the stack, but put it in gp.stack before mstart, // so the logic above hasn't set osStack yet. osStack = true } @@ -1376,11 +1393,12 @@ func mstart0() { // The go:noinline is to guarantee the getcallerpc/getcallersp below are safe, // so that we can set up g0.sched to return to the call of mstart1 above. +// //go:noinline func mstart1() { - _g_ := getg() + gp := getg() - if _g_ != _g_.m.g0 { + if gp != gp.m.g0 { throw("bad runtime·mstart") } @@ -1390,26 +1408,26 @@ func mstart1() { // so other calls can reuse the current frame. // And goexit0 does a gogo that needs to return from mstart1 // and let mstart0 exit the thread. - _g_.sched.g = guintptr(unsafe.Pointer(_g_)) - _g_.sched.pc = getcallerpc() - _g_.sched.sp = getcallersp() + gp.sched.g = guintptr(unsafe.Pointer(gp)) + gp.sched.pc = getcallerpc() + gp.sched.sp = getcallersp() asminit() minit() // Install signal handlers; after minit so that minit can // prepare the thread to be able to handle the signals. - if _g_.m == &m0 { + if gp.m == &m0 { mstartm0() } - if fn := _g_.m.mstartfn; fn != nil { + if fn := gp.m.mstartfn; fn != nil { fn() } - if _g_.m != &m0 { - acquirep(_g_.m.nextp.ptr()) - _g_.m.nextp = 0 + if gp.m != &m0 { + acquirep(gp.m.nextp.ptr()) + gp.m.nextp = 0 } schedule() } @@ -1431,28 +1449,19 @@ func mstartm0() { initsig(false) } -// mPark causes a thread to park itself - temporarily waking for -// fixups but otherwise waiting to be fully woken. This is the -// only way that m's should park themselves. +// mPark causes a thread to park itself, returning once woken. +// //go:nosplit func mPark() { - g := getg() - for { - notesleep(&g.m.park) - // Note, because of signal handling by this parked m, - // a preemptive mDoFixup() may actually occur via - // mDoFixupAndOSYield(). (See golang.org/issue/44193) - noteclear(&g.m.park) - if !mDoFixup() { - return - } - } + gp := getg() + notesleep(&gp.m.park) + noteclear(&gp.m.park) } // mexit tears down and exits the current thread. // // Don't call this directly to exit the thread, since it must run at -// the top of the thread stack. Instead, use gogo(&_g_.m.g0.sched) to +// the top of the thread stack. Instead, use gogo(&gp.m.g0.sched) to // unwind the stack to the point that exits the thread. // // It is entered with m.p != nil, so write barriers are allowed. It @@ -1460,10 +1469,9 @@ func mPark() { // //go:yeswritebarrierrec func mexit(osStack bool) { - g := getg() - m := g.m + mp := getg().m - if m == &m0 { + if mp == &m0 { // This is the main thread. Just wedge it. // // On Linux, exiting the main thread puts the process @@ -1488,20 +1496,20 @@ func mexit(osStack bool) { unminit() // Free the gsignal stack. - if m.gsignal != nil { - stackfree(m.gsignal.stack) + if mp.gsignal != nil { + stackfree(mp.gsignal.stack) // On some platforms, when calling into VDSO (e.g. nanotime) // we store our g on the gsignal stack, if there is one. // Now the stack is freed, unlink it from the m, so we // won't write to it when calling VDSO code. - m.gsignal = nil + mp.gsignal = nil } // Remove m from allm. lock(&sched.lock) for pprev := &allm; *pprev != nil; pprev = &(*pprev).alllink { - if *pprev == m { - *pprev = m.alllink + if *pprev == mp { + *pprev = mp.alllink goto found } } @@ -1512,17 +1520,17 @@ found: // // If this is using an OS stack, the OS will free it // so there's no need for reaping. - atomic.Store(&m.freeWait, 1) + atomic.Store(&mp.freeWait, 1) // Put m on the free list, though it will not be reaped until // freeWait is 0. Note that the free list must not be linked // through alllink because some functions walk allm without // locking, so may be using alllink. - m.freelink = sched.freem - sched.freem = m + mp.freelink = sched.freem + sched.freem = mp } unlock(&sched.lock) - atomic.Xadd64(&ncgocall, int64(m.ncgocall)) + atomic.Xadd64(&ncgocall, int64(mp.ncgocall)) // Release the P. handoffp(releasep()) @@ -1539,14 +1547,14 @@ found: if GOOS == "darwin" || GOOS == "ios" { // Make sure pendingPreemptSignals is correct when an M exits. // For #41702. - if atomic.Load(&m.signalPending) != 0 { - atomic.Xadd(&pendingPreemptSignals, -1) + if mp.signalPending.Load() != 0 { + pendingPreemptSignals.Add(-1) } } // Destroy all allocated resources. After this is called, we may no // longer take any locks. - mdestroy(m) + mdestroy(mp) if osStack { // Return from mstart and let the system thread @@ -1558,7 +1566,7 @@ found: // return to. Exit the thread directly. exitThread will clear // m.freeWait when it's done with the stack and the m can be // reaped. - exitThread(&m.freeWait) + exitThread(&mp.freeWait) } // forEachP calls fn(p) for every P p when p reaches a GC safe point. @@ -1574,7 +1582,7 @@ found: //go:systemstack func forEachP(fn func(*p)) { mp := acquirem() - _p_ := getg().m.p.ptr() + pp := getg().m.p.ptr() lock(&sched.lock) if sched.safePointWait != 0 { @@ -1584,9 +1592,9 @@ func forEachP(fn func(*p)) { sched.safePointFn = fn // Ask all Ps to run the safe point function. - for _, p := range allp { - if p != _p_ { - atomic.Store(&p.runSafePointFn, 1) + for _, p2 := range allp { + if p2 != pp { + atomic.Store(&p2.runSafePointFn, 1) } } preemptall() @@ -1608,19 +1616,19 @@ func forEachP(fn func(*p)) { unlock(&sched.lock) // Run fn for the current P. - fn(_p_) + fn(pp) // Force Ps currently in _Psyscall into _Pidle and hand them // off to induce safe point function execution. - for _, p := range allp { - s := p.status - if s == _Psyscall && p.runSafePointFn == 1 && atomic.Cas(&p.status, s, _Pidle) { + for _, p2 := range allp { + s := p2.status + if s == _Psyscall && p2.runSafePointFn == 1 && atomic.Cas(&p2.status, s, _Pidle) { if trace.enabled { - traceGoSysBlock(p) - traceProcStop(p) + traceGoSysBlock(p2) + traceProcStop(p2) } - p.syscalltick++ - handoffp(p) + p2.syscalltick++ + handoffp(p2) } } @@ -1641,8 +1649,8 @@ func forEachP(fn func(*p)) { if sched.safePointWait != 0 { throw("forEachP: not done") } - for _, p := range allp { - if p.runSafePointFn != 0 { + for _, p2 := range allp { + if p2.runSafePointFn != 0 { throw("forEachP: P did not run fn") } } @@ -1653,151 +1661,12 @@ func forEachP(fn func(*p)) { releasem(mp) } -// syscall_runtime_doAllThreadsSyscall serializes Go execution and -// executes a specified fn() call on all m's. -// -// The boolean argument to fn() indicates whether the function's -// return value will be consulted or not. That is, fn(true) should -// return true if fn() succeeds, and fn(true) should return false if -// it failed. When fn(false) is called, its return status will be -// ignored. -// -// syscall_runtime_doAllThreadsSyscall first invokes fn(true) on a -// single, coordinating, m, and only if it returns true does it go on -// to invoke fn(false) on all of the other m's known to the process. -// -//go:linkname syscall_runtime_doAllThreadsSyscall syscall.runtime_doAllThreadsSyscall -func syscall_runtime_doAllThreadsSyscall(fn func(bool) bool) { - if iscgo { - panic("doAllThreadsSyscall not supported with cgo enabled") - } - if fn == nil { - return - } - for atomic.Load(&sched.sysmonStarting) != 0 { - osyield() - } - - // We don't want this thread to handle signals for the - // duration of this critical section. The underlying issue - // being that this locked coordinating m is the one monitoring - // for fn() execution by all the other m's of the runtime, - // while no regular go code execution is permitted (the world - // is stopped). If this present m were to get distracted to - // run signal handling code, and find itself waiting for a - // second thread to execute go code before being able to - // return from that signal handling, a deadlock will result. - // (See golang.org/issue/44193.) - lockOSThread() - var sigmask sigset - sigsave(&sigmask) - sigblock(false) - - stopTheWorldGC("doAllThreadsSyscall") - if atomic.Load(&newmHandoff.haveTemplateThread) != 0 { - // Ensure that there are no in-flight thread - // creations: don't want to race with allm. - lock(&newmHandoff.lock) - for !newmHandoff.waiting { - unlock(&newmHandoff.lock) - osyield() - lock(&newmHandoff.lock) - } - unlock(&newmHandoff.lock) - } - if netpollinited() { - netpollBreak() - } - sigRecvPrepareForFixup() - _g_ := getg() - if raceenabled { - // For m's running without racectx, we loan out the - // racectx of this call. - lock(&mFixupRace.lock) - mFixupRace.ctx = _g_.racectx - unlock(&mFixupRace.lock) - } - if ok := fn(true); ok { - tid := _g_.m.procid - for mp := allm; mp != nil; mp = mp.alllink { - if mp.procid == tid { - // This m has already completed fn() - // call. - continue - } - // Be wary of mp's without procid values if - // they are known not to park. If they are - // marked as parking with a zero procid, then - // they will be racing with this code to be - // allocated a procid and we will annotate - // them with the need to execute the fn when - // they acquire a procid to run it. - if mp.procid == 0 && !mp.doesPark { - // Reaching here, we are either - // running Windows, or cgo linked - // code. Neither of which are - // currently supported by this API. - throw("unsupported runtime environment") - } - // stopTheWorldGC() doesn't guarantee stopping - // all the threads, so we lock here to avoid - // the possibility of racing with mp. - lock(&mp.mFixup.lock) - mp.mFixup.fn = fn - atomic.Store(&mp.mFixup.used, 1) - if mp.doesPark { - // For non-service threads this will - // cause the wakeup to be short lived - // (once the mutex is unlocked). The - // next real wakeup will occur after - // startTheWorldGC() is called. - notewakeup(&mp.park) - } - unlock(&mp.mFixup.lock) - } - for { - done := true - for mp := allm; done && mp != nil; mp = mp.alllink { - if mp.procid == tid { - continue - } - done = atomic.Load(&mp.mFixup.used) == 0 - } - if done { - break - } - // if needed force sysmon and/or newmHandoff to wakeup. - lock(&sched.lock) - if atomic.Load(&sched.sysmonwait) != 0 { - atomic.Store(&sched.sysmonwait, 0) - notewakeup(&sched.sysmonnote) - } - unlock(&sched.lock) - lock(&newmHandoff.lock) - if newmHandoff.waiting { - newmHandoff.waiting = false - notewakeup(&newmHandoff.wake) - } - unlock(&newmHandoff.lock) - osyield() - } - } - if raceenabled { - lock(&mFixupRace.lock) - mFixupRace.ctx = 0 - unlock(&mFixupRace.lock) - } - startTheWorldGC() - msigrestore(sigmask) - unlockOSThread() -} - // runSafePointFn runs the safe point function, if any, for this P. // This should be called like // -// if getg().m.p.runSafePointFn != 0 { -// runSafePointFn() -// } +// if getg().m.p.runSafePointFn != 0 { +// runSafePointFn() +// } // // runSafePointFn must be checked on any transition in to _Pidle or // _Psyscall to avoid a race where forEachP sees that the P is running @@ -1837,14 +1706,20 @@ type cgothreadstart struct { // id is optional pre-allocated m ID. Omit by passing -1. // // This function is allowed to have write barriers even if the caller -// isn't because it borrows _p_. +// isn't because it borrows pp. // //go:yeswritebarrierrec -func allocm(_p_ *p, fn func(), id int64) *m { - _g_ := getg() - acquirem() // disable GC because it can be called from sysmon - if _g_.m.p == 0 { - acquirep(_p_) // temporarily borrow p for mallocs in this function +func allocm(pp *p, fn func(), id int64) *m { + allocmLock.rlock() + + // The caller owns pp, but we may borrow (i.e., acquirep) it. We must + // disable preemption to ensure it is not stolen, which would make the + // caller lose ownership. + acquirem() + + gp := getg() + if gp.m.p == 0 { + acquirep(pp) // temporarily borrow p for mallocs in this function } // Release the free M list. We need to do this somewhere and @@ -1885,11 +1760,12 @@ func allocm(_p_ *p, fn func(), id int64) *m { } mp.g0.m = mp - if _p_ == _g_.m.p.ptr() { + if pp == gp.m.p.ptr() { releasep() } - releasem(_g_.m) + releasem(gp.m) + allocmLock.runlock() return mp } @@ -1926,6 +1802,7 @@ func allocm(_p_ *p, fn func(), id int64) *m { // // When the callback is done with the m, it calls dropm to // put the m back on the list. +// //go:nosplit func needm() { if (iscgo || GOOS == "windows") && !cgoHasExtraM { @@ -1981,10 +1858,10 @@ func needm() { // scheduling stack is, but we assume there's at least 32 kB, // which is more than enough for us. setg(mp.g0) - _g_ := getg() - _g_.stack.hi = getcallersp() + 1024 - _g_.stack.lo = getcallersp() - 32*1024 - _g_.stackguard0 = _g_.stack.lo + _StackGuard + gp := getg() + gp.stack.hi = getcallersp() + 1024 + gp.stack.lo = getcallersp() - 32*1024 + gp.stackguard0 = gp.stack.lo + _StackGuard // Initialize this thread to use the m. asminit() @@ -1992,7 +1869,7 @@ func needm() { // mp.curg is now a real goroutine. casgstatus(mp.curg, _Gdead, _Gsyscall) - atomic.Xadd(&sched.ngsys, -1) + sched.ngsys.Add(-1) } var earlycgocallback = []byte("fatal error: cgo callback before cgo call\n") @@ -2001,7 +1878,7 @@ var earlycgocallback = []byte("fatal error: cgo callback before cgo call\n") // It is called with a working local m, so that it can do things // like call schedlock and allocate. func newextram() { - c := atomic.Xchg(&extraMWaiters, 0) + c := extraMWaiters.Swap(0) if c > 0 { for i := uint32(0); i < c; i++ { oneNewExtraM() @@ -2027,7 +1904,7 @@ func oneNewExtraM() { gp := malg(4096) gp.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum gp.sched.sp = gp.stack.hi - gp.sched.sp -= 4 * sys.PtrSize // extra space in case of reads slightly beyond frame + gp.sched.sp -= 4 * goarch.PtrSize // extra space in case of reads slightly beyond frame gp.sched.lr = 0 gp.sched.g = guintptr(unsafe.Pointer(gp)) gp.syscallpc = gp.sched.pc @@ -2043,9 +1920,9 @@ func oneNewExtraM() { mp.lockedInt++ mp.lockedg.set(gp) gp.lockedm.set(mp) - gp.goid = int64(atomic.Xadd64(&sched.goidgen, 1)) + gp.goid = sched.goidgen.Add(1) if raceenabled { - gp.racectx = racegostart(funcPC(newextram) + sys.PCQuantum) + gp.racectx = racegostart(abi.FuncPCABIInternal(newextram) + sys.PCQuantum) } // put on allg for garbage collector allgadd(gp) @@ -2054,7 +1931,7 @@ func oneNewExtraM() { // counted by gcount. It would be more "proper" to increment // sched.ngfree, but that requires locking. Incrementing ngsys // has the same effect. - atomic.Xadd(&sched.ngsys, +1) + sched.ngsys.Add(1) // Add m to the extra list. mnext := lockextra(true) @@ -2095,7 +1972,7 @@ func dropm() { // Return mp.curg to dead state. casgstatus(mp.curg, _Gsyscall, _Gdead) mp.curg.preemptStop = false - atomic.Xadd(&sched.ngsys, +1) + sched.ngsys.Add(1) // Block signals before unminit. // Unminit unregisters the signal handling stack (but needs g on some systems). @@ -2122,22 +1999,23 @@ func getm() uintptr { return uintptr(unsafe.Pointer(getg().m)) } -var extram uintptr +var extram atomic.Uintptr var extraMCount uint32 // Protected by lockextra -var extraMWaiters uint32 +var extraMWaiters atomic.Uint32 // lockextra locks the extra list and returns the list head. // The caller must unlock the list by storing a new list head // to extram. If nilokay is true, then lockextra will // return a nil list head if that's what it finds. If nilokay is false, // lockextra will keep waiting until the list head is no longer nil. +// //go:nosplit func lockextra(nilokay bool) *m { const locked = 1 incr := false for { - old := atomic.Loaduintptr(&extram) + old := extram.Load() if old == locked { osyield_no_g() continue @@ -2147,13 +2025,13 @@ func lockextra(nilokay bool) *m { // Add 1 to the number of threads // waiting for an M. // This is cleared by newextram. - atomic.Xadd(&extraMWaiters, 1) + extraMWaiters.Add(1) incr = true } usleep_no_g(1) continue } - if atomic.Casuintptr(&extram, old, locked) { + if extram.CompareAndSwap(old, locked) { return (*m)(unsafe.Pointer(old)) } osyield_no_g() @@ -2163,12 +2041,20 @@ func lockextra(nilokay bool) *m { //go:nosplit func unlockextra(mp *m) { - atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp))) + extram.Store(uintptr(unsafe.Pointer(mp))) } -// execLock serializes exec and clone to avoid bugs or unspecified behaviour -// around exec'ing while creating/destroying threads. See issue #19546. -var execLock rwmutex +var ( + // allocmLock is locked for read when creating new Ms in allocm and their + // addition to allm. Thus acquiring this lock for write blocks the + // creation of new Ms. + allocmLock rwmutex + + // execLock serializes exec and clone to avoid bugs or unspecified + // behaviour around exec'ing while creating/destroying threads. See + // issue #19546. + execLock rwmutex +) // newmHandoff contains a list of m structures that need new OS threads. // This is used by newm in situations where newm itself can't safely @@ -2196,11 +2082,23 @@ var newmHandoff struct { // May run with m.p==nil, so write barriers are not allowed. // // id is optional pre-allocated m ID. Omit by passing -1. +// //go:nowritebarrierrec -func newm(fn func(), _p_ *p, id int64) { - mp := allocm(_p_, fn, id) - mp.doesPark = (_p_ != nil) - mp.nextp.set(_p_) +func newm(fn func(), pp *p, id int64) { + // allocm adds a new M to allm, but they do not start until created by + // the OS in newm1 or the template thread. + // + // doAllThreadsSyscall requires that every M in allm will eventually + // start and be signal-able, even with a STW. + // + // Disable preemption here until we start the thread to ensure that + // newm is not preempted between allocm and starting the new thread, + // ensuring that anything added to allm is guaranteed to eventually + // start. + acquirem() + + mp := allocm(pp, fn, id) + mp.nextp.set(pp) mp.sigmask = initSigmask if gp := getg(); gp != nil && gp.m != nil && (gp.m.lockedExt != 0 || gp.m.incgo) && GOOS != "plan9" { // We're on a locked M or a thread that may have been @@ -2225,9 +2123,14 @@ func newm(fn func(), _p_ *p, id int64) { notewakeup(&newmHandoff.wake) } unlock(&newmHandoff.lock) + // The M has not started yet, but the template thread does not + // participate in STW, so it will always process queued Ms and + // it is safe to releasem. + releasem(getg().m) return } newm1(mp) + releasem(getg().m) } func newm1(mp *m) { @@ -2238,10 +2141,13 @@ func newm1(mp *m) { } ts.g.set(mp.g0) ts.tls = (*uint64)(unsafe.Pointer(&mp.tls[0])) - ts.fn = unsafe.Pointer(funcPC(mstart)) + ts.fn = unsafe.Pointer(abi.FuncPCABI0(mstart)) if msanenabled { msanwrite(unsafe.Pointer(&ts), unsafe.Sizeof(ts)) } + if asanenabled { + asanwrite(unsafe.Pointer(&ts), unsafe.Sizeof(ts)) + } execLock.rlock() // Prevent process clone. asmcgocall(_cgo_thread_start, unsafe.Pointer(&ts)) execLock.runlock() @@ -2272,81 +2178,6 @@ func startTemplateThread() { releasem(mp) } -// mFixupRace is used to temporarily borrow the race context from the -// coordinating m during a syscall_runtime_doAllThreadsSyscall and -// loan it out to each of the m's of the runtime so they can execute a -// mFixup.fn in that context. -var mFixupRace struct { - lock mutex - ctx uintptr -} - -// mDoFixup runs any outstanding fixup function for the running m. -// Returns true if a fixup was outstanding and actually executed. -// -// Note: to avoid deadlocks, and the need for the fixup function -// itself to be async safe, signals are blocked for the working m -// while it holds the mFixup lock. (See golang.org/issue/44193) -// -//go:nosplit -func mDoFixup() bool { - _g_ := getg() - if used := atomic.Load(&_g_.m.mFixup.used); used == 0 { - return false - } - - // slow path - if fixup fn is used, block signals and lock. - var sigmask sigset - sigsave(&sigmask) - sigblock(false) - lock(&_g_.m.mFixup.lock) - fn := _g_.m.mFixup.fn - if fn != nil { - if gcphase != _GCoff { - // We can't have a write barrier in this - // context since we may not have a P, but we - // clear fn to signal that we've executed the - // fixup. As long as fn is kept alive - // elsewhere, technically we should have no - // issues with the GC, but fn is likely - // generated in a different package altogether - // that may change independently. Just assert - // the GC is off so this lack of write barrier - // is more obviously safe. - throw("GC must be disabled to protect validity of fn value") - } - if _g_.racectx != 0 || !raceenabled { - fn(false) - } else { - // temporarily acquire the context of the - // originator of the - // syscall_runtime_doAllThreadsSyscall and - // block others from using it for the duration - // of the fixup call. - lock(&mFixupRace.lock) - _g_.racectx = mFixupRace.ctx - fn(false) - _g_.racectx = 0 - unlock(&mFixupRace.lock) - } - *(*uintptr)(unsafe.Pointer(&_g_.m.mFixup.fn)) = 0 - atomic.Store(&_g_.m.mFixup.used, 0) - } - unlock(&_g_.m.mFixup.lock) - msigrestore(sigmask) - return fn != nil -} - -// mDoFixupAndOSYield is called when an m is unable to send a signal -// because the allThreadsSyscall mechanism is in progress. That is, an -// mPark() has been interrupted with this signal handler so we need to -// ensure the fixup is executed from this context. -//go:nosplit -func mDoFixupAndOSYield() { - mDoFixup() - osyield() -} - // templateThread is a thread in a known-good state that exists solely // to start new threads in known-good states when the calling thread // may not be in a good state. @@ -2383,31 +2214,30 @@ func templateThread() { noteclear(&newmHandoff.wake) unlock(&newmHandoff.lock) notesleep(&newmHandoff.wake) - mDoFixup() } } // Stops execution of the current m until new work is available. // Returns with acquired P. func stopm() { - _g_ := getg() + gp := getg() - if _g_.m.locks != 0 { + if gp.m.locks != 0 { throw("stopm holding locks") } - if _g_.m.p != 0 { + if gp.m.p != 0 { throw("stopm holding p") } - if _g_.m.spinning { + if gp.m.spinning { throw("stopm spinning") } lock(&sched.lock) - mput(_g_.m) + mput(gp.m) unlock(&sched.lock) mPark() - acquirep(_g_.m.nextp.ptr()) - _g_.m.nextp = 0 + acquirep(gp.m.nextp.ptr()) + gp.m.nextp = 0 } func mspinning() { @@ -2418,15 +2248,16 @@ func mspinning() { // Schedules some M to run the p (creates an M if necessary). // If p==nil, tries to get an idle P, if no idle P's does nothing. // May run with m.p==nil, so write barriers are not allowed. -// If spinning is set, the caller has incremented nmspinning and startm will -// either decrement nmspinning or set m.spinning in the newly started M. +// If spinning is set, the caller has incremented nmspinning and must provide a +// P. startm will set m.spinning in the newly started M. // // Callers passing a non-nil P must call from a non-preemptible context. See // comment on acquirem below. // // Must not have write barriers because this may be called without a P. +// //go:nowritebarrierrec -func startm(_p_ *p, spinning bool) { +func startm(pp *p, spinning bool) { // Disable preemption. // // Every owned P must have an owner that will eventually stop it in the @@ -2445,17 +2276,16 @@ func startm(_p_ *p, spinning bool) { // disable preemption before acquiring a P from pidleget below. mp := acquirem() lock(&sched.lock) - if _p_ == nil { - _p_ = pidleget() - if _p_ == nil { + if pp == nil { + if spinning { + // TODO(prattmic): All remaining calls to this function + // with _p_ == nil could be cleaned up to find a P + // before calling startm. + throw("startm: P required for spinning=true") + } + pp, _ = pidleget(0) + if pp == nil { unlock(&sched.lock) - if spinning { - // The caller incremented nmspinning, but there are no idle Ps, - // so it's okay to just undo the increment and give up. - if int32(atomic.Xadd(&sched.nmspinning, -1)) < 0 { - throw("startm: negative nmspinning") - } - } releasem(mp) return } @@ -2482,8 +2312,8 @@ func startm(_p_ *p, spinning bool) { // The caller incremented nmspinning, so set m.spinning in the new M. fn = mspinning } - newm(fn, _p_, id) - // Ownership transfer of _p_ committed by start in newm. + newm(fn, pp, id) + // Ownership transfer of pp committed by start in newm. // Preemption is now safe. releasem(mp) return @@ -2495,44 +2325,51 @@ func startm(_p_ *p, spinning bool) { if nmp.nextp != 0 { throw("startm: m has p") } - if spinning && !runqempty(_p_) { + if spinning && !runqempty(pp) { throw("startm: p has runnable gs") } // The caller incremented nmspinning, so set m.spinning in the new M. nmp.spinning = spinning - nmp.nextp.set(_p_) + nmp.nextp.set(pp) notewakeup(&nmp.park) - // Ownership transfer of _p_ committed by wakeup. Preemption is now + // Ownership transfer of pp committed by wakeup. Preemption is now // safe. releasem(mp) } // Hands off P from syscall or locked M. // Always runs without a P, so write barriers are not allowed. +// //go:nowritebarrierrec -func handoffp(_p_ *p) { +func handoffp(pp *p) { // handoffp must start an M in any situation where - // findrunnable would return a G to run on _p_. + // findrunnable would return a G to run on pp. // if it has local work, start it straight away - if !runqempty(_p_) || sched.runqsize != 0 { - startm(_p_, false) + if !runqempty(pp) || sched.runqsize != 0 { + startm(pp, false) + return + } + // if there's trace work to do, start it straight away + if (trace.enabled || trace.shutdown) && traceReaderAvailable() != nil { + startm(pp, false) return } // if it has GC work, start it straight away - if gcBlackenEnabled != 0 && gcMarkWorkAvailable(_p_) { - startm(_p_, false) + if gcBlackenEnabled != 0 && gcMarkWorkAvailable(pp) { + startm(pp, false) return } // no local work, check that there are no spinning/idle M's, // otherwise our help is not required - if atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) == 0 && atomic.Cas(&sched.nmspinning, 0, 1) { // TODO: fast atomic - startm(_p_, true) + if sched.nmspinning.Load()+sched.npidle.Load() == 0 && sched.nmspinning.CompareAndSwap(0, 1) { // TODO: fast atomic + sched.needspinning.Store(0) + startm(pp, true) return } lock(&sched.lock) - if sched.gcwaiting != 0 { - _p_.status = _Pgcstop + if sched.gcwaiting.Load() { + pp.status = _Pgcstop sched.stopwait-- if sched.stopwait == 0 { notewakeup(&sched.stopnote) @@ -2540,8 +2377,8 @@ func handoffp(_p_ *p) { unlock(&sched.lock) return } - if _p_.runSafePointFn != 0 && atomic.Cas(&_p_.runSafePointFn, 1, 0) { - sched.safePointFn(_p_) + if pp.runSafePointFn != 0 && atomic.Cas(&pp.runSafePointFn, 1, 0) { + sched.safePointFn(pp) sched.safePointWait-- if sched.safePointWait == 0 { notewakeup(&sched.safePointNote) @@ -2549,21 +2386,21 @@ func handoffp(_p_ *p) { } if sched.runqsize != 0 { unlock(&sched.lock) - startm(_p_, false) + startm(pp, false) return } // If this is the last running P and nobody is polling network, // need to wakeup another M to poll network. - if sched.npidle == uint32(gomaxprocs-1) && atomic.Load64(&sched.lastpoll) != 0 { + if sched.npidle.Load() == gomaxprocs-1 && sched.lastpoll.Load() != 0 { unlock(&sched.lock) - startm(_p_, false) + startm(pp, false) return } // The scheduler lock cannot be held when calling wakeNetPoller below // because wakeNetPoller may call wakep which may call startm. - when := nobarrierWakeTime(_p_) - pidleput(_p_) + when := nobarrierWakeTime(pp) + pidleput(pp, 0) unlock(&sched.lock) if when != 0 { @@ -2573,51 +2410,76 @@ func handoffp(_p_ *p) { // Tries to add one more P to execute G's. // Called when a G is made runnable (newproc, ready). +// Must be called with a P. func wakep() { - if atomic.Load(&sched.npidle) == 0 { + // Be conservative about spinning threads, only start one if none exist + // already. + if sched.nmspinning.Load() != 0 || !sched.nmspinning.CompareAndSwap(0, 1) { return } - // be conservative about spinning threads - if atomic.Load(&sched.nmspinning) != 0 || !atomic.Cas(&sched.nmspinning, 0, 1) { + + // Disable preemption until ownership of pp transfers to the next M in + // startm. Otherwise preemption here would leave pp stuck waiting to + // enter _Pgcstop. + // + // See preemption comment on acquirem in startm for more details. + mp := acquirem() + + var pp *p + lock(&sched.lock) + pp, _ = pidlegetSpinning(0) + if pp == nil { + if sched.nmspinning.Add(-1) < 0 { + throw("wakep: negative nmspinning") + } + unlock(&sched.lock) + releasem(mp) return } - startm(nil, true) + // Since we always have a P, the race in the "No M is available" + // comment in startm doesn't apply during the small window between the + // unlock here and lock in startm. A checkdead in between will always + // see at least one running M (ours). + unlock(&sched.lock) + + startm(pp, true) + + releasem(mp) } // Stops execution of the current m that is locked to a g until the g is runnable again. // Returns with acquired P. func stoplockedm() { - _g_ := getg() + gp := getg() - if _g_.m.lockedg == 0 || _g_.m.lockedg.ptr().lockedm.ptr() != _g_.m { + if gp.m.lockedg == 0 || gp.m.lockedg.ptr().lockedm.ptr() != gp.m { throw("stoplockedm: inconsistent locking") } - if _g_.m.p != 0 { + if gp.m.p != 0 { // Schedule another M to run this p. - _p_ := releasep() - handoffp(_p_) + pp := releasep() + handoffp(pp) } incidlelocked(1) // Wait until another thread schedules lockedg again. mPark() - status := readgstatus(_g_.m.lockedg.ptr()) + status := readgstatus(gp.m.lockedg.ptr()) if status&^_Gscan != _Grunnable { print("runtime:stoplockedm: lockedg (atomicstatus=", status, ") is not Grunnable or Gscanrunnable\n") - dumpgstatus(_g_.m.lockedg.ptr()) + dumpgstatus(gp.m.lockedg.ptr()) throw("stoplockedm: not runnable") } - acquirep(_g_.m.nextp.ptr()) - _g_.m.nextp = 0 + acquirep(gp.m.nextp.ptr()) + gp.m.nextp = 0 } // Schedules the locked m to run the locked gp. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func startlockedm(gp *g) { - _g_ := getg() - mp := gp.lockedm.ptr() - if mp == _g_.m { + if mp == getg().m { throw("startlockedm: locked to me") } if mp.nextp != 0 { @@ -2625,8 +2487,8 @@ func startlockedm(gp *g) { } // directly handoff current P to the locked m incidlelocked(-1) - _p_ := releasep() - mp.nextp.set(_p_) + pp := releasep() + mp.nextp.set(pp) notewakeup(&mp.park) stopm() } @@ -2634,22 +2496,22 @@ func startlockedm(gp *g) { // Stops the current m for stopTheWorld. // Returns when the world is restarted. func gcstopm() { - _g_ := getg() + gp := getg() - if sched.gcwaiting == 0 { + if !sched.gcwaiting.Load() { throw("gcstopm: not waiting for gc") } - if _g_.m.spinning { - _g_.m.spinning = false + if gp.m.spinning { + gp.m.spinning = false // OK to just drop nmspinning here, // startTheWorld will unpark threads as necessary. - if int32(atomic.Xadd(&sched.nmspinning, -1)) < 0 { + if sched.nmspinning.Add(-1) < 0 { throw("gcstopm: negative nmspinning") } } - _p_ := releasep() + pp := releasep() lock(&sched.lock) - _p_.status = _Pgcstop + pp.status = _Pgcstop sched.stopwait-- if sched.stopwait == 0 { notewakeup(&sched.stopnote) @@ -2668,23 +2530,30 @@ func gcstopm() { // //go:yeswritebarrierrec func execute(gp *g, inheritTime bool) { - _g_ := getg() + mp := getg().m + + if goroutineProfile.active { + // Make sure that gp has had its stack written out to the goroutine + // profile, exactly as it was when the goroutine profiler first stopped + // the world. + tryRecordGoroutineProfile(gp, osyield) + } // Assign gp.m before entering _Grunning so running Gs have an // M. - _g_.m.curg = gp - gp.m = _g_.m + mp.curg = gp + gp.m = mp casgstatus(gp, _Grunnable, _Grunning) gp.waitsince = 0 gp.preempt = false gp.stackguard0 = gp.stack.lo + _StackGuard if !inheritTime { - _g_.m.p.ptr().schedtick++ + mp.p.ptr().schedtick++ } // Check whether the profiler needs to be turned on or off. hz := sched.profilehz - if _g_.m.profilehz != hz { + if mp.profilehz != hz { setThreadCPUProfiler(hz) } @@ -2702,26 +2571,64 @@ func execute(gp *g, inheritTime bool) { // Finds a runnable goroutine to execute. // Tries to steal from other P's, get g from local or global queue, poll network. -func findrunnable() (gp *g, inheritTime bool) { - _g_ := getg() +// tryWakeP indicates that the returned goroutine is not normal (GC worker, trace +// reader) so the caller should try to wake a P. +func findRunnable() (gp *g, inheritTime, tryWakeP bool) { + mp := getg().m // The conditions here and in handoffp must agree: if // findrunnable would return a G to run, handoffp must start // an M. top: - _p_ := _g_.m.p.ptr() - if sched.gcwaiting != 0 { + pp := mp.p.ptr() + if sched.gcwaiting.Load() { gcstopm() goto top } - if _p_.runSafePointFn != 0 { + if pp.runSafePointFn != 0 { runSafePointFn() } - now, pollUntil, _ := checkTimers(_p_, 0) + // now and pollUntil are saved for work stealing later, + // which may steal timers. It's important that between now + // and then, nothing blocks, so these numbers remain mostly + // relevant. + now, pollUntil, _ := checkTimers(pp, 0) + + // Try to schedule the trace reader. + if trace.enabled || trace.shutdown { + gp := traceReader() + if gp != nil { + casgstatus(gp, _Gwaiting, _Grunnable) + traceGoUnpark(gp, 0) + return gp, false, true + } + } + + // Try to schedule a GC worker. + if gcBlackenEnabled != 0 { + gp, tnow := gcController.findRunnableGCWorker(pp, now) + if gp != nil { + return gp, false, true + } + now = tnow + } + + // Check the global runnable queue once in a while to ensure fairness. + // Otherwise two goroutines can completely occupy the local runqueue + // by constantly respawning each other. + if pp.schedtick%61 == 0 && sched.runqsize > 0 { + lock(&sched.lock) + gp := globrunqget(pp, 1) + unlock(&sched.lock) + if gp != nil { + return gp, false, false + } + } - if fingwait && fingwake { + // Wake up the finalizer G. + if fingStatus.Load()&(fingWait|fingWake) == fingWait|fingWake { if gp := wakefing(); gp != nil { ready(gp, 0, true) } @@ -2731,17 +2638,17 @@ top: } // local runq - if gp, inheritTime := runqget(_p_); gp != nil { - return gp, inheritTime + if gp, inheritTime := runqget(pp); gp != nil { + return gp, inheritTime, false } // global runq if sched.runqsize != 0 { lock(&sched.lock) - gp := globrunqget(_p_, 0) + gp := globrunqget(pp, 0) unlock(&sched.lock) if gp != nil { - return gp, false + return gp, false, false } } @@ -2752,7 +2659,7 @@ top: // blocked thread (e.g. it has already returned from netpoll, but does // not set lastpoll yet), this thread will do blocking netpoll below // anyway. - if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Load64(&sched.lastpoll) != 0 { + if netpollinited() && netpollWaiters.Load() > 0 && sched.lastpoll.Load() != 0 { if list := netpoll(0); !list.empty() { // non-blocking gp := list.pop() injectglist(&list) @@ -2760,7 +2667,7 @@ top: if trace.enabled { traceGoUnpark(gp, 0) } - return gp, false + return gp, false, false } } @@ -2769,24 +2676,23 @@ top: // Limit the number of spinning Ms to half the number of busy Ps. // This is necessary to prevent excessive CPU consumption when // GOMAXPROCS>>1 but the program parallelism is low. - procs := uint32(gomaxprocs) - if _g_.m.spinning || 2*atomic.Load(&sched.nmspinning) < procs-atomic.Load(&sched.npidle) { - if !_g_.m.spinning { - _g_.m.spinning = true - atomic.Xadd(&sched.nmspinning, 1) + if mp.spinning || 2*sched.nmspinning.Load() < gomaxprocs-sched.npidle.Load() { + if !mp.spinning { + mp.becomeSpinning() } gp, inheritTime, tnow, w, newWork := stealWork(now) - now = tnow if gp != nil { // Successfully stole. - return gp, inheritTime + return gp, inheritTime, false } if newWork { // There may be new timer or GC work; restart to // discover. goto top } + + now = tnow if w != 0 && (pollUntil == 0 || w < pollUntil) { // Earlier timer to wait for. pollUntil = w @@ -2796,19 +2702,19 @@ top: // We have nothing to do. // // If we're in the GC mark phase, can safely scan and blacken objects, - // and have work to do, run idle-time marking rather than give up the - // P. - if gcBlackenEnabled != 0 && gcMarkWorkAvailable(_p_) { + // and have work to do, run idle-time marking rather than give up the P. + if gcBlackenEnabled != 0 && gcMarkWorkAvailable(pp) && gcController.addIdleMarkWorker() { node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) if node != nil { - _p_.gcMarkWorkerMode = gcMarkWorkerIdleMode + pp.gcMarkWorkerMode = gcMarkWorkerIdleMode gp := node.gp.ptr() casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) } - return gp, false + return gp, false, false } + gcController.removeIdleMarkWorker() } // wasm only: @@ -2821,7 +2727,7 @@ top: if trace.enabled { traceGoUnpark(gp, 0) } - return gp, false + return gp, false, false } if otherReady { goto top @@ -2839,19 +2745,25 @@ top: // return P and block lock(&sched.lock) - if sched.gcwaiting != 0 || _p_.runSafePointFn != 0 { + if sched.gcwaiting.Load() || pp.runSafePointFn != 0 { unlock(&sched.lock) goto top } if sched.runqsize != 0 { - gp := globrunqget(_p_, 0) + gp := globrunqget(pp, 0) unlock(&sched.lock) - return gp, false + return gp, false, false } - if releasep() != _p_ { + if !mp.spinning && sched.needspinning.Load() == 1 { + // See "Delicate dance" comment below. + mp.becomeSpinning() + unlock(&sched.lock) + goto top + } + if releasep() != pp { throw("findrunnable: wrong p") } - pidleput(_p_) + now = pidleput(pp, now) unlock(&sched.lock) // Delicate dance: thread transitions from spinning to non-spinning @@ -2868,48 +2780,65 @@ top: // * New/modified-earlier timers on a per-P timer heap. // * Idle-priority GC work (barring golang.org/issue/19112). // - // If we discover new work below, we need to restore m.spinning as a signal - // for resetspinning to unpark a new worker thread (because there can be more - // than one starving goroutine). However, if after discovering new work - // we also observe no idle Ps it is OK to skip unparking a new worker - // thread: the system is fully loaded so no spinning threads are required. - // Also see "Worker thread parking/unparking" comment at the top of the file. - wasSpinning := _g_.m.spinning - if _g_.m.spinning { - _g_.m.spinning = false - if int32(atomic.Xadd(&sched.nmspinning, -1)) < 0 { + // If we discover new work below, we need to restore m.spinning as a + // signal for resetspinning to unpark a new worker thread (because + // there can be more than one starving goroutine). + // + // However, if after discovering new work we also observe no idle Ps + // (either here or in resetspinning), we have a problem. We may be + // racing with a non-spinning M in the block above, having found no + // work and preparing to release its P and park. Allowing that P to go + // idle will result in loss of work conservation (idle P while there is + // runnable work). This could result in complete deadlock in the + // unlikely event that we discover new work (from netpoll) right as we + // are racing with _all_ other Ps going idle. + // + // We use sched.needspinning to synchronize with non-spinning Ms going + // idle. If needspinning is set when they are about to drop their P, + // they abort the drop and instead become a new spinning M on our + // behalf. If we are not racing and the system is truly fully loaded + // then no spinning threads are required, and the next thread to + // naturally become spinning will clear the flag. + // + // Also see "Worker thread parking/unparking" comment at the top of the + // file. + wasSpinning := mp.spinning + if mp.spinning { + mp.spinning = false + if sched.nmspinning.Add(-1) < 0 { throw("findrunnable: negative nmspinning") } // Note the for correctness, only the last M transitioning from // spinning to non-spinning must perform these rechecks to - // ensure no missed work. We are performing it on every M that - // transitions as a conservative change to monitor effects on - // latency. See golang.org/issue/43997. + // ensure no missed work. However, the runtime has some cases + // of transient increments of nmspinning that are decremented + // without going through this path, so we must be conservative + // and perform the check on all spinning Ms. + // + // See https://go.dev/issue/43997. // Check all runqueues once again. - _p_ = checkRunqsNoP(allpSnapshot, idlepMaskSnapshot) - if _p_ != nil { - acquirep(_p_) - _g_.m.spinning = true - atomic.Xadd(&sched.nmspinning, 1) + pp := checkRunqsNoP(allpSnapshot, idlepMaskSnapshot) + if pp != nil { + acquirep(pp) + mp.becomeSpinning() goto top } // Check for idle-priority GC work again. - _p_, gp = checkIdleGCNoP() - if _p_ != nil { - acquirep(_p_) - _g_.m.spinning = true - atomic.Xadd(&sched.nmspinning, 1) + pp, gp := checkIdleGCNoP() + if pp != nil { + acquirep(pp) + mp.becomeSpinning() // Run the idle worker. - _p_.gcMarkWorkerMode = gcMarkWorkerIdleMode + pp.gcMarkWorkerMode = gcMarkWorkerIdleMode casgstatus(gp, _Gwaiting, _Grunnable) if trace.enabled { traceGoUnpark(gp, 0) } - return gp, false + return gp, false, false } // Finally, check for timer creation or expiry concurrently with @@ -2922,19 +2851,18 @@ top: } // Poll network until next timer. - if netpollinited() && (atomic.Load(&netpollWaiters) > 0 || pollUntil != 0) && atomic.Xchg64(&sched.lastpoll, 0) != 0 { - atomic.Store64(&sched.pollUntil, uint64(pollUntil)) - if _g_.m.p != 0 { + if netpollinited() && (netpollWaiters.Load() > 0 || pollUntil != 0) && sched.lastpoll.Swap(0) != 0 { + sched.pollUntil.Store(pollUntil) + if mp.p != 0 { throw("findrunnable: netpoll with p") } - if _g_.m.spinning { + if mp.spinning { throw("findrunnable: netpoll with spinning") } + // Refresh now. + now = nanotime() delay := int64(-1) if pollUntil != 0 { - if now == 0 { - now = nanotime() - } delay = pollUntil - now if delay < 0 { delay = 0 @@ -2945,8 +2873,8 @@ top: delay = 0 } list := netpoll(delay) // block until new work is available - atomic.Store64(&sched.pollUntil, 0) - atomic.Store64(&sched.lastpoll, uint64(nanotime())) + sched.pollUntil.Store(0) + sched.lastpoll.Store(now) if faketime != 0 && list.empty() { // Using fake time and nothing is ready; stop M. // When all M's stop, checkdead will call timejump. @@ -2954,12 +2882,12 @@ top: goto top } lock(&sched.lock) - _p_ = pidleget() + pp, _ := pidleget(now) unlock(&sched.lock) - if _p_ == nil { + if pp == nil { injectglist(&list) } else { - acquirep(_p_) + acquirep(pp) if !list.empty() { gp := list.pop() injectglist(&list) @@ -2967,16 +2895,15 @@ top: if trace.enabled { traceGoUnpark(gp, 0) } - return gp, false + return gp, false, false } if wasSpinning { - _g_.m.spinning = true - atomic.Xadd(&sched.nmspinning, 1) + mp.becomeSpinning() } goto top } } else if pollUntil != 0 && netpollinited() { - pollerPollUntil := int64(atomic.Load64(&sched.pollUntil)) + pollerPollUntil := sched.pollUntil.Load() if pollerPollUntil == 0 || pollerPollUntil > pollUntil { netpollBreak() } @@ -2997,7 +2924,7 @@ func pollWork() bool { if !runqempty(p) { return true } - if netpollinited() && atomic.Load(&netpollWaiters) > 0 && sched.lastpoll != 0 { + if netpollinited() && netpollWaiters.Load() > 0 && sched.lastpoll.Load() != 0 { if list := netpoll(0); !list.empty() { injectglist(&list) return true @@ -3022,7 +2949,7 @@ func stealWork(now int64) (gp *g, inheritTime bool, rnow, pollUntil int64, newWo stealTimersOrRunNextG := i == stealTries-1 for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() { - if sched.gcwaiting != 0 { + if sched.gcwaiting.Load() { // GC work may be available. return nil, false, now, pollUntil, true } @@ -3090,17 +3017,18 @@ func checkRunqsNoP(allpSnapshot []*p, idlepMaskSnapshot pMask) *p { for id, p2 := range allpSnapshot { if !idlepMaskSnapshot.read(uint32(id)) && !runqempty(p2) { lock(&sched.lock) - pp := pidleget() - unlock(&sched.lock) - if pp != nil { - return pp + pp, _ := pidlegetSpinning(0) + if pp == nil { + // Can't get a P, don't bother checking remaining Ps. + unlock(&sched.lock) + return nil } - - // Can't get a P, don't bother checking remaining Ps. - break + unlock(&sched.lock) + return pp } } + // No work available. return nil } @@ -3126,8 +3054,12 @@ func checkTimersNoP(allpSnapshot []*p, timerpMaskSnapshot pMask, pollUntil int64 // returned. The returned P has not been wired yet. func checkIdleGCNoP() (*p, *g) { // N.B. Since we have no P, gcBlackenEnabled may change at any time; we - // must check again after acquiring a P. - if atomic.Load(&gcBlackenEnabled) == 0 { + // must check again after acquiring a P. As an optimization, we also check + // if an idle mark worker is needed at all. This is OK here, because if we + // observe that one isn't needed, at least one is currently running. Even if + // it stops running, its own journey into the scheduler should schedule it + // again, if need be (at which point, this check will pass, if relevant). + if atomic.Load(&gcBlackenEnabled) == 0 || !gcController.needIdleMarkWorker() { return nil, nil } if !gcMarkWorkAvailable(nil) { @@ -3152,24 +3084,24 @@ func checkIdleGCNoP() (*p, *g) { // the assumption in gcControllerState.findRunnableGCWorker that an // empty gcBgMarkWorkerPool is only possible if gcMarkDone is running. lock(&sched.lock) - pp := pidleget() + pp, now := pidlegetSpinning(0) if pp == nil { unlock(&sched.lock) return nil, nil } - // Now that we own a P, gcBlackenEnabled can't change (as it requires - // STW). - if gcBlackenEnabled == 0 { - pidleput(pp) + // Now that we own a P, gcBlackenEnabled can't change (as it requires STW). + if gcBlackenEnabled == 0 || !gcController.addIdleMarkWorker() { + pidleput(pp, now) unlock(&sched.lock) return nil, nil } node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) if node == nil { - pidleput(pp) + pidleput(pp, now) unlock(&sched.lock) + gcController.removeIdleMarkWorker() return nil, nil } @@ -3182,12 +3114,12 @@ func checkIdleGCNoP() (*p, *g) { // going to wake up before the when argument; or it wakes an idle P to service // timers and the network poller if there isn't one already. func wakeNetPoller(when int64) { - if atomic.Load64(&sched.lastpoll) == 0 { + if sched.lastpoll.Load() == 0 { // In findrunnable we ensure that when polling the pollUntil // field is either zero or the time to which the current // poll is expected to run. This can have a spurious wakeup // but should never miss a wakeup. - pollerPollUntil := int64(atomic.Load64(&sched.pollUntil)) + pollerPollUntil := sched.pollUntil.Load() if pollerPollUntil == 0 || pollerPollUntil > when { netpollBreak() } @@ -3201,13 +3133,13 @@ func wakeNetPoller(when int64) { } func resetspinning() { - _g_ := getg() - if !_g_.m.spinning { + gp := getg() + if !gp.m.spinning { throw("resetspinning: not a spinning m") } - _g_.m.spinning = false - nmspinning := atomic.Xadd(&sched.nmspinning, -1) - if int32(nmspinning) < 0 { + gp.m.spinning = false + nmspinning := sched.nmspinning.Add(-1) + if nmspinning < 0 { throw("findrunnable: negative nmspinning") } // M wakeup policy is deliberately somewhat conservative, so check if we @@ -3252,8 +3184,20 @@ func injectglist(glist *gList) { *glist = gList{} startIdle := func(n int) { - for ; n != 0 && sched.npidle != 0; n-- { - startm(nil, false) + for i := 0; i < n; i++ { + mp := acquirem() // See comment in startm. + lock(&sched.lock) + + pp, _ := pidlegetSpinning(0) + if pp == nil { + unlock(&sched.lock) + releasem(mp) + break + } + + unlock(&sched.lock) + startm(pp, false) + releasem(mp) } } @@ -3266,7 +3210,7 @@ func injectglist(glist *gList) { return } - npidle := int(atomic.Load(&sched.npidle)) + npidle := int(sched.npidle.Load()) var globq gQueue var n int for n = 0; n < npidle && !q.empty(); n++ { @@ -3289,88 +3233,40 @@ func injectglist(glist *gList) { // One round of scheduler: find a runnable goroutine and execute it. // Never returns. func schedule() { - _g_ := getg() + mp := getg().m - if _g_.m.locks != 0 { + if mp.locks != 0 { throw("schedule: holding locks") } - if _g_.m.lockedg != 0 { + if mp.lockedg != 0 { stoplockedm() - execute(_g_.m.lockedg.ptr(), false) // Never returns. + execute(mp.lockedg.ptr(), false) // Never returns. } // We should not schedule away from a g that is executing a cgo call, // since the cgo call is using the m's g0 stack. - if _g_.m.incgo { + if mp.incgo { throw("schedule: in cgo") } top: - pp := _g_.m.p.ptr() + pp := mp.p.ptr() pp.preempt = false - if sched.gcwaiting != 0 { - gcstopm() - goto top - } - if pp.runSafePointFn != 0 { - runSafePointFn() - } - - // Sanity check: if we are spinning, the run queue should be empty. + // Safety check: if we are spinning, the run queue should be empty. // Check this before calling checkTimers, as that might call // goready to put a ready goroutine on the local run queue. - if _g_.m.spinning && (pp.runnext != 0 || pp.runqhead != pp.runqtail) { + if mp.spinning && (pp.runnext != 0 || pp.runqhead != pp.runqtail) { throw("schedule: spinning with local work") } - checkTimers(pp, 0) - - var gp *g - var inheritTime bool - - // Normal goroutines will check for need to wakeP in ready, - // but GCworkers and tracereaders will not, so the check must - // be done here instead. - tryWakeP := false - if trace.enabled || trace.shutdown { - gp = traceReader() - if gp != nil { - casgstatus(gp, _Gwaiting, _Grunnable) - traceGoUnpark(gp, 0) - tryWakeP = true - } - } - if gp == nil && gcBlackenEnabled != 0 { - gp = gcController.findRunnableGCWorker(_g_.m.p.ptr()) - if gp != nil { - tryWakeP = true - } - } - if gp == nil { - // Check the global runnable queue once in a while to ensure fairness. - // Otherwise two goroutines can completely occupy the local runqueue - // by constantly respawning each other. - if _g_.m.p.ptr().schedtick%61 == 0 && sched.runqsize > 0 { - lock(&sched.lock) - gp = globrunqget(_g_.m.p.ptr(), 1) - unlock(&sched.lock) - } - } - if gp == nil { - gp, inheritTime = runqget(_g_.m.p.ptr()) - // We can see gp != nil here even if the M is spinning, - // if checkTimers added a local goroutine via goready. - } - if gp == nil { - gp, inheritTime = findrunnable() // blocks until work is available - } + gp, inheritTime, tryWakeP := findRunnable() // blocks until work is available // This thread is going to run a goroutine and is not spinning anymore, // so if it was marked as spinning we need to reset it now and potentially // start a new spinning M. - if _g_.m.spinning { + if mp.spinning { resetspinning() } @@ -3414,10 +3310,10 @@ top: // readied later, the caller can do other work but eventually should // call schedule to restart the scheduling of goroutines on this m. func dropg() { - _g_ := getg() + gp := getg() - setMNoWB(&_g_.m.curg.m, nil) - setGNoWB(&_g_.m.curg, nil) + setMNoWB(&gp.m.curg.m, nil) + setGNoWB(&gp.m.curg, nil) } // checkTimers runs any timers for the P that are ready. @@ -3428,12 +3324,13 @@ func dropg() { // If the time when the next timer should run is not 0, // it is always larger than the returned time. // We pass now in and out to avoid extra calls of nanotime. +// //go:yeswritebarrierrec func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) { // If it's not yet time for the first timer, or the first adjusted // timer, then there is nothing to do. - next := int64(atomic.Load64(&pp.timer0When)) - nextAdj := int64(atomic.Load64(&pp.timerModifiedEarliest)) + next := pp.timer0When.Load() + nextAdj := pp.timerModifiedEarliest.Load() if next == 0 || (nextAdj != 0 && nextAdj < next) { next = nextAdj } @@ -3451,7 +3348,7 @@ func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) { // if we would clear deleted timers. // This corresponds to the condition below where // we decide whether to call clearDeletedTimers. - if pp != getg().m.p.ptr() || int(atomic.Load(&pp.deletedTimers)) <= int(atomic.Load(&pp.numTimers)/4) { + if pp != getg().m.p.ptr() || int(pp.deletedTimers.Load()) <= int(pp.numTimers.Load()/4) { return now, next, false } } @@ -3476,7 +3373,7 @@ func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) { // If this is the local P, and there are a lot of deleted timers, // clear them out. We only do this for the local P to reduce // lock contention on timersLock. - if pp == getg().m.p.ptr() && int(atomic.Load(&pp.deletedTimers)) > len(pp.timers)/4 { + if pp == getg().m.p.ptr() && int(pp.deletedTimers.Load()) > len(pp.timers)/4 { clearDeletedTimers(pp) } @@ -3492,19 +3389,19 @@ func parkunlock_c(gp *g, lock unsafe.Pointer) bool { // park continuation on g0. func park_m(gp *g) { - _g_ := getg() + mp := getg().m if trace.enabled { - traceGoPark(_g_.m.waittraceev, _g_.m.waittraceskip) + traceGoPark(mp.waittraceev, mp.waittraceskip) } casgstatus(gp, _Grunning, _Gwaiting) dropg() - if fn := _g_.m.waitunlockf; fn != nil { - ok := fn(gp, _g_.m.waitlock) - _g_.m.waitunlockf = nil - _g_.m.waitlock = nil + if fn := mp.waitunlockf; fn != nil { + ok := fn(gp, mp.waitlock) + mp.waitunlockf = nil + mp.waitlock = nil if !ok { if trace.enabled { traceGoUnpark(gp, 2) @@ -3631,16 +3528,18 @@ func goexit1() { // goexit continuation on g0. func goexit0(gp *g) { - _g_ := getg() + mp := getg().m + pp := mp.p.ptr() casgstatus(gp, _Grunning, _Gdead) + gcController.addScannableStack(pp, -int64(gp.stack.hi-gp.stack.lo)) if isSystemGoroutine(gp, false) { - atomic.Xadd(&sched.ngsys, -1) + sched.ngsys.Add(-1) } gp.m = nil locked := gp.lockedm != 0 gp.lockedm = 0 - _g_.m.lockedg = 0 + mp.lockedg = 0 gp.preemptStop = false gp.paniconfault = false gp._defer = nil // should be true already but just in case. @@ -3655,24 +3554,24 @@ func goexit0(gp *g) { // Flush assist credit to the global pool. This gives // better information to pacing if the application is // rapidly creating an exiting goroutines. - assistWorkPerByte := float64frombits(atomic.Load64(&gcController.assistWorkPerByte)) + assistWorkPerByte := gcController.assistWorkPerByte.Load() scanCredit := int64(assistWorkPerByte * float64(gp.gcAssistBytes)) - atomic.Xaddint64(&gcController.bgScanCredit, scanCredit) + gcController.bgScanCredit.Add(scanCredit) gp.gcAssistBytes = 0 } dropg() if GOARCH == "wasm" { // no threads yet on wasm - gfput(_g_.m.p.ptr(), gp) + gfput(pp, gp) schedule() // never returns } - if _g_.m.lockedInt != 0 { - print("invalid m->lockedInt = ", _g_.m.lockedInt, "\n") + if mp.lockedInt != 0 { + print("invalid m->lockedInt = ", mp.lockedInt, "\n") throw("internal lockOSThread error") } - gfput(_g_.m.p.ptr(), gp) + gfput(pp, gp) if locked { // The goroutine may have locked this thread because // it put it in an unusual kernel state. Kill it @@ -3681,11 +3580,11 @@ func goexit0(gp *g) { // Return to mstart, which will release the P and exit // the thread. if GOOS != "plan9" { // See golang.org/issue/22227. - gogo(&_g_.m.g0.sched) + gogo(&mp.g0.sched) } else { // Clear lockedExt on plan9 since we may end up re-using // this thread. - _g_.m.lockedExt = 0 + mp.lockedExt = 0 } } schedule() @@ -3700,9 +3599,9 @@ func goexit0(gp *g) { //go:nosplit //go:nowritebarrierrec func save(pc, sp uintptr) { - _g_ := getg() + gp := getg() - if _g_ == _g_.m.g0 || _g_ == _g_.m.gsignal { + if gp == gp.m.g0 || gp == gp.m.gsignal { // m.g0.sched is special and must describe the context // for exiting the thread. mstart1 writes to it directly. // m.gsignal.sched should not be used at all. @@ -3711,14 +3610,14 @@ func save(pc, sp uintptr) { throw("save on system g not allowed") } - _g_.sched.pc = pc - _g_.sched.sp = sp - _g_.sched.lr = 0 - _g_.sched.ret = 0 + gp.sched.pc = pc + gp.sched.sp = sp + gp.sched.lr = 0 + gp.sched.ret = 0 // We need to ensure ctxt is zero, but can't have a write // barrier here. However, it should always already be zero. // Assert that. - if _g_.sched.ctxt != nil { + if gp.sched.ctxt != nil { badctxt() } } @@ -3753,7 +3652,7 @@ func save(pc, sp uintptr) { // when syscall returns we emit traceGoSysExit and when the goroutine starts running // (potentially instantly, if exitsyscallfast returns true) we emit traceGoStart. // To ensure that traceGoSysExit is emitted strictly after traceGoSysBlock, -// we remember current value of syscalltick in m (_g_.m.syscalltick = _g_.m.p.ptr().syscalltick), +// we remember current value of syscalltick in m (gp.m.syscalltick = gp.m.p.ptr().syscalltick), // whoever emits traceGoSysBlock increments p.syscalltick afterwards; // and we wait for the increment before emitting traceGoSysExit. // Note that the increment is done even if tracing is not enabled, @@ -3761,27 +3660,27 @@ func save(pc, sp uintptr) { // //go:nosplit func reentersyscall(pc, sp uintptr) { - _g_ := getg() + gp := getg() // Disable preemption because during this function g is in Gsyscall status, // but can have inconsistent g->sched, do not let GC observe it. - _g_.m.locks++ + gp.m.locks++ // Entersyscall must not call any function that might split/grow the stack. // (See details in comment above.) // Catch calls that might, by replacing the stack guard with something that // will trip any stack check and leaving a flag to tell newstack to die. - _g_.stackguard0 = stackPreempt - _g_.throwsplit = true + gp.stackguard0 = stackPreempt + gp.throwsplit = true // Leave SP around for GC and traceback. save(pc, sp) - _g_.syscallsp = sp - _g_.syscallpc = pc - casgstatus(_g_, _Grunning, _Gsyscall) - if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp { + gp.syscallsp = sp + gp.syscallpc = pc + casgstatus(gp, _Grunning, _Gsyscall) + if gp.syscallsp < gp.stack.lo || gp.stack.hi < gp.syscallsp { systemstack(func() { - print("entersyscall inconsistent ", hex(_g_.syscallsp), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n") + print("entersyscall inconsistent ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n") throw("entersyscall") }) } @@ -3794,35 +3693,35 @@ func reentersyscall(pc, sp uintptr) { save(pc, sp) } - if atomic.Load(&sched.sysmonwait) != 0 { + if sched.sysmonwait.Load() { systemstack(entersyscall_sysmon) save(pc, sp) } - if _g_.m.p.ptr().runSafePointFn != 0 { + if gp.m.p.ptr().runSafePointFn != 0 { // runSafePointFn may stack split if run on this stack systemstack(runSafePointFn) save(pc, sp) } - _g_.m.syscalltick = _g_.m.p.ptr().syscalltick - _g_.sysblocktraced = true - pp := _g_.m.p.ptr() + gp.m.syscalltick = gp.m.p.ptr().syscalltick + gp.sysblocktraced = true + pp := gp.m.p.ptr() pp.m = 0 - _g_.m.oldp.set(pp) - _g_.m.p = 0 + gp.m.oldp.set(pp) + gp.m.p = 0 atomic.Store(&pp.status, _Psyscall) - if sched.gcwaiting != 0 { + if sched.gcwaiting.Load() { systemstack(entersyscall_gcwait) save(pc, sp) } - _g_.m.locks-- + gp.m.locks-- } // Standard syscall entry used by the go syscall library and normal cgo calls. // -// This is exported via linkname to assembly in the syscall package. +// This is exported via linkname to assembly in the syscall package and x/sys. // //go:nosplit //go:linkname entersyscall @@ -3832,24 +3731,24 @@ func entersyscall() { func entersyscall_sysmon() { lock(&sched.lock) - if atomic.Load(&sched.sysmonwait) != 0 { - atomic.Store(&sched.sysmonwait, 0) + if sched.sysmonwait.Load() { + sched.sysmonwait.Store(false) notewakeup(&sched.sysmonnote) } unlock(&sched.lock) } func entersyscall_gcwait() { - _g_ := getg() - _p_ := _g_.m.oldp.ptr() + gp := getg() + pp := gp.m.oldp.ptr() lock(&sched.lock) - if sched.stopwait > 0 && atomic.Cas(&_p_.status, _Psyscall, _Pgcstop) { + if sched.stopwait > 0 && atomic.Cas(&pp.status, _Psyscall, _Pgcstop) { if trace.enabled { - traceGoSysBlock(_p_) - traceProcStop(_p_) + traceGoSysBlock(pp) + traceProcStop(pp) } - _p_.syscalltick++ + pp.syscalltick++ if sched.stopwait--; sched.stopwait == 0 { notewakeup(&sched.stopnote) } @@ -3858,36 +3757,37 @@ func entersyscall_gcwait() { } // The same as entersyscall(), but with a hint that the syscall is blocking. +// //go:nosplit func entersyscallblock() { - _g_ := getg() + gp := getg() - _g_.m.locks++ // see comment in entersyscall - _g_.throwsplit = true - _g_.stackguard0 = stackPreempt // see comment in entersyscall - _g_.m.syscalltick = _g_.m.p.ptr().syscalltick - _g_.sysblocktraced = true - _g_.m.p.ptr().syscalltick++ + gp.m.locks++ // see comment in entersyscall + gp.throwsplit = true + gp.stackguard0 = stackPreempt // see comment in entersyscall + gp.m.syscalltick = gp.m.p.ptr().syscalltick + gp.sysblocktraced = true + gp.m.p.ptr().syscalltick++ // Leave SP around for GC and traceback. pc := getcallerpc() sp := getcallersp() save(pc, sp) - _g_.syscallsp = _g_.sched.sp - _g_.syscallpc = _g_.sched.pc - if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp { + gp.syscallsp = gp.sched.sp + gp.syscallpc = gp.sched.pc + if gp.syscallsp < gp.stack.lo || gp.stack.hi < gp.syscallsp { sp1 := sp - sp2 := _g_.sched.sp - sp3 := _g_.syscallsp + sp2 := gp.sched.sp + sp3 := gp.syscallsp systemstack(func() { - print("entersyscallblock inconsistent ", hex(sp1), " ", hex(sp2), " ", hex(sp3), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n") + print("entersyscallblock inconsistent ", hex(sp1), " ", hex(sp2), " ", hex(sp3), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n") throw("entersyscallblock") }) } - casgstatus(_g_, _Grunning, _Gsyscall) - if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp { + casgstatus(gp, _Grunning, _Gsyscall) + if gp.syscallsp < gp.stack.lo || gp.stack.hi < gp.syscallsp { systemstack(func() { - print("entersyscallblock inconsistent ", hex(sp), " ", hex(_g_.sched.sp), " ", hex(_g_.syscallsp), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n") + print("entersyscallblock inconsistent ", hex(sp), " ", hex(gp.sched.sp), " ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n") throw("entersyscallblock") }) } @@ -3897,7 +3797,7 @@ func entersyscallblock() { // Resave for traceback during blocked call. save(getcallerpc(), getcallersp()) - _g_.m.locks-- + gp.m.locks-- } func entersyscallblock_handoff() { @@ -3921,41 +3821,51 @@ func entersyscallblock_handoff() { //go:nowritebarrierrec //go:linkname exitsyscall func exitsyscall() { - _g_ := getg() + gp := getg() - _g_.m.locks++ // see comment in entersyscall - if getcallersp() > _g_.syscallsp { + gp.m.locks++ // see comment in entersyscall + if getcallersp() > gp.syscallsp { throw("exitsyscall: syscall frame is no longer valid") } - _g_.waitsince = 0 - oldp := _g_.m.oldp.ptr() - _g_.m.oldp = 0 + gp.waitsince = 0 + oldp := gp.m.oldp.ptr() + gp.m.oldp = 0 if exitsyscallfast(oldp) { + // When exitsyscallfast returns success, we have a P so can now use + // write barriers + if goroutineProfile.active { + // Make sure that gp has had its stack written out to the goroutine + // profile, exactly as it was when the goroutine profiler first + // stopped the world. + systemstack(func() { + tryRecordGoroutineProfileWB(gp) + }) + } if trace.enabled { - if oldp != _g_.m.p.ptr() || _g_.m.syscalltick != _g_.m.p.ptr().syscalltick { + if oldp != gp.m.p.ptr() || gp.m.syscalltick != gp.m.p.ptr().syscalltick { systemstack(traceGoStart) } } // There's a cpu for us, so we can run. - _g_.m.p.ptr().syscalltick++ + gp.m.p.ptr().syscalltick++ // We need to cas the status and scan before resuming... - casgstatus(_g_, _Gsyscall, _Grunning) + casgstatus(gp, _Gsyscall, _Grunning) // Garbage collector isn't running (since we are), // so okay to clear syscallsp. - _g_.syscallsp = 0 - _g_.m.locks-- - if _g_.preempt { + gp.syscallsp = 0 + gp.m.locks-- + if gp.preempt { // restore the preemption request in case we've cleared it in newstack - _g_.stackguard0 = stackPreempt + gp.stackguard0 = stackPreempt } else { // otherwise restore the real _StackGuard, we've spoiled it in entersyscall/entersyscallblock - _g_.stackguard0 = _g_.stack.lo + _StackGuard + gp.stackguard0 = gp.stack.lo + _StackGuard } - _g_.throwsplit = false + gp.throwsplit = false - if sched.disable.user && !schedEnabled(_g_) { + if sched.disable.user && !schedEnabled(gp) { // Scheduling of this goroutine is disabled. Gosched() } @@ -3963,21 +3873,21 @@ func exitsyscall() { return } - _g_.sysexitticks = 0 + gp.sysexitticks = 0 if trace.enabled { // Wait till traceGoSysBlock event is emitted. // This ensures consistency of the trace (the goroutine is started after it is blocked). - for oldp != nil && oldp.syscalltick == _g_.m.syscalltick { + for oldp != nil && oldp.syscalltick == gp.m.syscalltick { osyield() } // We can't trace syscall exit right now because we don't have a P. // Tracing code can invoke write barriers that cannot run without a P. // So instead we remember the syscall exit time and emit the event // in execute when we have a P. - _g_.sysexitticks = cputicks() + gp.sysexitticks = cputicks() } - _g_.m.locks-- + gp.m.locks-- // Call the scheduler. mcall(exitsyscall0) @@ -3988,14 +3898,14 @@ func exitsyscall() { // Must wait until now because until gosched returns // we don't know for sure that the garbage collector // is not running. - _g_.syscallsp = 0 - _g_.m.p.ptr().syscalltick++ - _g_.throwsplit = false + gp.syscallsp = 0 + gp.m.p.ptr().syscalltick++ + gp.throwsplit = false } //go:nosplit func exitsyscallfast(oldp *p) bool { - _g_ := getg() + gp := getg() // Freezetheworld sets stopwait but does not retake P's. if sched.stopwait == freezeStopWait { @@ -4019,7 +3929,7 @@ func exitsyscallfast(oldp *p) bool { if oldp != nil { // Wait till traceGoSysBlock event is emitted. // This ensures consistency of the trace (the goroutine is started after it is blocked). - for oldp.syscalltick == _g_.m.syscalltick { + for oldp.syscalltick == gp.m.syscalltick { osyield() } } @@ -4039,33 +3949,33 @@ func exitsyscallfast(oldp *p) bool { // //go:nosplit func exitsyscallfast_reacquired() { - _g_ := getg() - if _g_.m.syscalltick != _g_.m.p.ptr().syscalltick { + gp := getg() + if gp.m.syscalltick != gp.m.p.ptr().syscalltick { if trace.enabled { - // The p was retaken and then enter into syscall again (since _g_.m.syscalltick has changed). + // The p was retaken and then enter into syscall again (since gp.m.syscalltick has changed). // traceGoSysBlock for this syscall was already emitted, // but here we effectively retake the p from the new syscall running on the same p. systemstack(func() { // Denote blocking of the new syscall. - traceGoSysBlock(_g_.m.p.ptr()) + traceGoSysBlock(gp.m.p.ptr()) // Denote completion of the current syscall. traceGoSysExit(0) }) } - _g_.m.p.ptr().syscalltick++ + gp.m.p.ptr().syscalltick++ } } func exitsyscallfast_pidle() bool { lock(&sched.lock) - _p_ := pidleget() - if _p_ != nil && atomic.Load(&sched.sysmonwait) != 0 { - atomic.Store(&sched.sysmonwait, 0) + pp, _ := pidleget(0) + if pp != nil && sched.sysmonwait.Load() { + sched.sysmonwait.Store(false) notewakeup(&sched.sysmonnote) } unlock(&sched.lock) - if _p_ != nil { - acquirep(_p_) + if pp != nil { + acquirep(pp) return true } return false @@ -4081,12 +3991,12 @@ func exitsyscall0(gp *g) { casgstatus(gp, _Gsyscall, _Grunnable) dropg() lock(&sched.lock) - var _p_ *p + var pp *p if schedEnabled(gp) { - _p_ = pidleget() + pp, _ = pidleget(0) } var locked bool - if _p_ == nil { + if pp == nil { globrunqput(gp) // Below, we stoplockedm if gp is locked. globrunqput releases @@ -4095,13 +4005,13 @@ func exitsyscall0(gp *g) { // could race with another M transitioning gp from unlocked to // locked. locked = gp.lockedm != 0 - } else if atomic.Load(&sched.sysmonwait) != 0 { - atomic.Store(&sched.sysmonwait, 0) + } else if sched.sysmonwait.Load() { + sched.sysmonwait.Store(false) notewakeup(&sched.sysmonnote) } unlock(&sched.lock) - if _p_ != nil { - acquirep(_p_) + if pp != nil { + acquirep(pp) execute(gp, false) // Never returns. } if locked { @@ -4116,7 +4026,11 @@ func exitsyscall0(gp *g) { schedule() // Never returns. } -func beforefork() { +// Called from syscall package before fork. +// +//go:linkname syscall_runtime_BeforeFork syscall.runtime_BeforeFork +//go:nosplit +func syscall_runtime_BeforeFork() { gp := getg().m.curg // Block signals during a fork, so that the child does not run @@ -4133,14 +4047,11 @@ func beforefork() { gp.stackguard0 = stackFork } -// Called from syscall package before fork. -//go:linkname syscall_runtime_BeforeFork syscall.runtime_BeforeFork +// Called from syscall package after fork in parent. +// +//go:linkname syscall_runtime_AfterFork syscall.runtime_AfterFork //go:nosplit -func syscall_runtime_BeforeFork() { - systemstack(beforefork) -} - -func afterfork() { +func syscall_runtime_AfterFork() { gp := getg().m.curg // See the comments in beforefork. @@ -4151,13 +4062,6 @@ func afterfork() { gp.m.locks-- } -// Called from syscall package after fork in parent. -//go:linkname syscall_runtime_AfterFork syscall.runtime_AfterFork -//go:nosplit -func syscall_runtime_AfterFork() { - systemstack(afterfork) -} - // inForkedChild is true while manipulating signals in the child process. // This is used to avoid calling libc functions in case we are using vfork. var inForkedChild bool @@ -4192,9 +4096,10 @@ func syscall_runtime_AfterForkInChild() { // pendingPreemptSignals is the number of preemption signals // that have been sent but not received. This is only used on Darwin. // For #41702. -var pendingPreemptSignals uint32 +var pendingPreemptSignals atomic.Int32 // Called from syscall package before Exec. +// //go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec func syscall_runtime_BeforeExec() { // Prevent thread creation during exec. @@ -4203,13 +4108,14 @@ func syscall_runtime_BeforeExec() { // On Darwin, wait for all pending preemption signals to // be received. See issue #41702. if GOOS == "darwin" || GOOS == "ios" { - for int32(atomic.Load(&pendingPreemptSignals)) > 0 { + for pendingPreemptSignals.Load() > 0 { osyield() } } } // Called from syscall package after Exec. +// //go:linkname syscall_runtime_AfterExec syscall.runtime_AfterExec func syscall_runtime_AfterExec() { execLock.unlock() @@ -4232,30 +4138,17 @@ func malg(stacksize int32) *g { return newg } -// Create a new g running fn with siz bytes of arguments. +// Create a new g running fn. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. -// -// The stack layout of this call is unusual: it assumes that the -// arguments to pass to fn are on the stack sequentially immediately -// after &fn. Hence, they are logically part of newproc's argument -// frame, even though they don't appear in its signature (and can't -// because their types differ between call sites). -// -// This must be nosplit because this stack layout means there are -// untyped arguments in newproc's argument frame. Stack copies won't -// be able to adjust them and stack splits won't be able to copy them. -// -//go:nosplit -func newproc(siz int32, fn *funcval) { - argp := add(unsafe.Pointer(&fn), sys.PtrSize) +func newproc(fn *funcval) { gp := getg() pc := getcallerpc() systemstack(func() { - newg := newproc1(fn, argp, siz, gp, pc) + newg := newproc1(fn, gp, pc) - _p_ := getg().m.p.ptr() - runqput(_p_, newg, true) + pp := getg().m.p.ptr() + runqput(pp, newg, true) if mainStarted { wakep() @@ -4263,44 +4156,17 @@ func newproc(siz int32, fn *funcval) { }) } -// Create a new g in state _Grunnable, starting at fn, with narg bytes -// of arguments starting at argp. callerpc is the address of the go -// statement that created this. The caller is responsible for adding -// the new g to the scheduler. -// -// This must run on the system stack because it's the continuation of -// newproc, which cannot split the stack. -// -//go:systemstack -func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *g { - if goexperiment.RegabiDefer && narg != 0 { - // TODO: When we commit to GOEXPERIMENT=regabidefer, - // rewrite the comments for newproc and newproc1. - // newproc will no longer have a funny stack layout or - // need to be nosplit. - throw("go with non-empty frame") - } - - _g_ := getg() - +// Create a new g in state _Grunnable, starting at fn. callerpc is the +// address of the go statement that created this. The caller is responsible +// for adding the new g to the scheduler. +func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g { if fn == nil { - _g_.m.throwing = -1 // do not dump full stacks - throw("go of nil func value") - } - acquirem() // disable preemption because it can be holding p in a local var - siz := narg - siz = (siz + 7) &^ 7 - - // We could allocate a larger initial stack if necessary. - // Not worth it: this is almost always an error. - // 4*PtrSize: extra space added below - // PtrSize: caller's LR (arm) or return address (x86, in gostartcall). - if siz >= _StackMin-4*sys.PtrSize-sys.PtrSize { - throw("newproc: function arguments too large for new goroutine") + fatal("go of nil func value") } - _p_ := _g_.m.p.ptr() - newg := gfget(_p_) + mp := acquirem() // disable preemption because we hold M and P in local vars. + pp := mp.p.ptr() + newg := gfget(pp) if newg == nil { newg = malg(_StackMin) casgstatus(newg, _Gidle, _Gdead) @@ -4314,8 +4180,8 @@ func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerp throw("newproc1: new g is not Gdead") } - totalSize := 4*sys.PtrSize + uintptr(siz) + sys.MinFrameSize // extra space in case of reads slightly beyond frame - totalSize += -totalSize & (sys.StackAlign - 1) // align to StackAlign + totalSize := uintptr(4*goarch.PtrSize + sys.MinFrameSize) // extra space in case of reads slightly beyond frame + totalSize = alignUp(totalSize, sys.StackAlign) sp := newg.stack.hi - totalSize spArg := sp if usesLR { @@ -4324,24 +4190,6 @@ func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerp prepGoExitFrame(sp) spArg += sys.MinFrameSize } - if narg > 0 { - memmove(unsafe.Pointer(spArg), argp, uintptr(narg)) - // This is a stack-to-stack copy. If write barriers - // are enabled and the source stack is grey (the - // destination is always black), then perform a - // barrier copy. We do this *after* the memmove - // because the destination stack may have garbage on - // it. - if writeBarrier.needed && !_g_.m.curg.gcscandone { - f := findfunc(fn.fn) - stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) - if stkmap.nbit > 0 { - // We're in the prologue, so it's always stack map index 0. - bv := stackmapdata(stkmap, 0) - bulkBarrierBitmap(spArg, spArg, uintptr(bv.n)*sys.PtrSize, 0, bv.bytedata) - } - } - } memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched)) newg.sched.sp = sp @@ -4352,11 +4200,21 @@ func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerp newg.gopc = callerpc newg.ancestors = saveAncestors(callergp) newg.startpc = fn.fn - if _g_.m.curg != nil { - newg.labels = _g_.m.curg.labels - } if isSystemGoroutine(newg, false) { - atomic.Xadd(&sched.ngsys, +1) + sched.ngsys.Add(1) + } else { + // Only user goroutines inherit pprof labels. + if mp.curg != nil { + newg.labels = mp.curg.labels + } + if goroutineProfile.active { + // A concurrent goroutine profile is running. It should include + // exactly the set of goroutines that were alive when the goroutine + // profiler first stopped the world. That does not include newg, so + // mark it as not needing a profile before transitioning it from + // _Gdead. + newg.goroutineProfiled.Store(goroutineProfileSatisfied) + } } // Track initial transition? newg.trackingSeq = uint8(fastrand()) @@ -4364,24 +4222,30 @@ func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerp newg.tracking = true } casgstatus(newg, _Gdead, _Grunnable) + gcController.addScannableStack(pp, int64(newg.stack.hi-newg.stack.lo)) - if _p_.goidcache == _p_.goidcacheend { + if pp.goidcache == pp.goidcacheend { // Sched.goidgen is the last allocated id, // this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch]. // At startup sched.goidgen=0, so main goroutine receives goid=1. - _p_.goidcache = atomic.Xadd64(&sched.goidgen, _GoidCacheBatch) - _p_.goidcache -= _GoidCacheBatch - 1 - _p_.goidcacheend = _p_.goidcache + _GoidCacheBatch + pp.goidcache = sched.goidgen.Add(_GoidCacheBatch) + pp.goidcache -= _GoidCacheBatch - 1 + pp.goidcacheend = pp.goidcache + _GoidCacheBatch } - newg.goid = int64(_p_.goidcache) - _p_.goidcache++ + newg.goid = pp.goidcache + pp.goidcache++ if raceenabled { newg.racectx = racegostart(callerpc) + if newg.labels != nil { + // See note in proflabel.go on labelSync's role in synchronizing + // with the reads in the signal handler. + racereleasemergeg(newg, unsafe.Pointer(&labelSync)) + } } if trace.enabled { traceGoCreate(newg, newg.startpc) } - releasem(_g_.m) + releasem(mp) return newg } @@ -4422,14 +4286,14 @@ func saveAncestors(callergp *g) *[]ancestorInfo { // Put on gfree list. // If local list is too long, transfer a batch to the global list. -func gfput(_p_ *p, gp *g) { +func gfput(pp *p, gp *g) { if readgstatus(gp) != _Gdead { throw("gfput: bad status (not Gdead)") } stksize := gp.stack.hi - gp.stack.lo - if stksize != _FixedStack { + if stksize != uintptr(startingStackSize) { // non-standard stack size - free it. stackfree(gp.stack) gp.stack.lo = 0 @@ -4437,17 +4301,17 @@ func gfput(_p_ *p, gp *g) { gp.stackguard0 = 0 } - _p_.gFree.push(gp) - _p_.gFree.n++ - if _p_.gFree.n >= 64 { + pp.gFree.push(gp) + pp.gFree.n++ + if pp.gFree.n >= 64 { var ( inc int32 stackQ gQueue noStackQ gQueue ) - for _p_.gFree.n >= 32 { - gp = _p_.gFree.pop() - _p_.gFree.n-- + for pp.gFree.n >= 32 { + gp := pp.gFree.pop() + pp.gFree.n-- if gp.stack.lo == 0 { noStackQ.push(gp) } else { @@ -4465,12 +4329,12 @@ func gfput(_p_ *p, gp *g) { // Get from gfree list. // If local list is empty, grab a batch from global list. -func gfget(_p_ *p) *g { +func gfget(pp *p) *g { retry: - if _p_.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) { + if pp.gFree.empty() && (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) { lock(&sched.gFree.lock) // Move a batch of free Gs to the P. - for _p_.gFree.n < 32 { + for pp.gFree.n < 32 { // Prefer Gs with stacks. gp := sched.gFree.stack.pop() if gp == nil { @@ -4480,21 +4344,32 @@ retry: } } sched.gFree.n-- - _p_.gFree.push(gp) - _p_.gFree.n++ + pp.gFree.push(gp) + pp.gFree.n++ } unlock(&sched.gFree.lock) goto retry } - gp := _p_.gFree.pop() + gp := pp.gFree.pop() if gp == nil { return nil } - _p_.gFree.n-- + pp.gFree.n-- + if gp.stack.lo != 0 && gp.stack.hi-gp.stack.lo != uintptr(startingStackSize) { + // Deallocate old stack. We kept it in gfput because it was the + // right size when the goroutine was put on the free list, but + // the right size has changed since then. + systemstack(func() { + stackfree(gp.stack) + gp.stack.lo = 0 + gp.stack.hi = 0 + gp.stackguard0 = 0 + }) + } if gp.stack.lo == 0 { - // Stack was deallocated in gfput. Allocate a new one. + // Stack was deallocated in gfput or just above. Allocate a new one. systemstack(func() { - gp.stack = stackalloc(_FixedStack) + gp.stack = stackalloc(startingStackSize) }) gp.stackguard0 = gp.stack.lo + _StackGuard } else { @@ -4504,20 +4379,23 @@ retry: if msanenabled { msanmalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) } + if asanenabled { + asanunpoison(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo) + } } return gp } // Purge all cached G's from gfree list to the global list. -func gfpurge(_p_ *p) { +func gfpurge(pp *p) { var ( inc int32 stackQ gQueue noStackQ gQueue ) - for !_p_.gFree.empty() { - gp := _p_.gFree.pop() - _p_.gFree.n-- + for !pp.gFree.empty() { + gp := pp.gFree.pop() + pp.gFree.n-- if gp.stack.lo == 0 { noStackQ.push(gp) } else { @@ -4540,14 +4418,15 @@ func Breakpoint() { // dolockOSThread is called by LockOSThread and lockOSThread below // after they modify m.locked. Do not allow preemption during this call, // or else the m might be different in this function than in the caller. +// //go:nosplit func dolockOSThread() { if GOARCH == "wasm" { return // no threads on wasm yet } - _g_ := getg() - _g_.m.lockedg.set(_g_) - _g_.lockedm.set(_g_.m) + gp := getg() + gp.m.lockedg.set(gp) + gp.lockedm.set(gp.m) } //go:nosplit @@ -4573,10 +4452,10 @@ func LockOSThread() { // while we're in a known-good state. startTemplateThread() } - _g_ := getg() - _g_.m.lockedExt++ - if _g_.m.lockedExt == 0 { - _g_.m.lockedExt-- + gp := getg() + gp.m.lockedExt++ + if gp.m.lockedExt == 0 { + gp.m.lockedExt-- panic("LockOSThread nesting overflow") } dolockOSThread() @@ -4591,17 +4470,18 @@ func lockOSThread() { // dounlockOSThread is called by UnlockOSThread and unlockOSThread below // after they update m->locked. Do not allow preemption during this call, // or else the m might be in different in this function than in the caller. +// //go:nosplit func dounlockOSThread() { if GOARCH == "wasm" { return // no threads on wasm yet } - _g_ := getg() - if _g_.m.lockedInt != 0 || _g_.m.lockedExt != 0 { + gp := getg() + if gp.m.lockedInt != 0 || gp.m.lockedExt != 0 { return } - _g_.m.lockedg = 0 - _g_.lockedm = 0 + gp.m.lockedg = 0 + gp.lockedm = 0 } //go:nosplit @@ -4619,21 +4499,21 @@ func dounlockOSThread() { // the goroutine locked to the OS thread until the goroutine (and // hence the thread) exits. func UnlockOSThread() { - _g_ := getg() - if _g_.m.lockedExt == 0 { + gp := getg() + if gp.m.lockedExt == 0 { return } - _g_.m.lockedExt-- + gp.m.lockedExt-- dounlockOSThread() } //go:nosplit func unlockOSThread() { - _g_ := getg() - if _g_.m.lockedInt == 0 { + gp := getg() + if gp.m.lockedInt == 0 { systemstack(badunlockosthread) } - _g_.m.lockedInt-- + gp.m.lockedInt-- dounlockOSThread() } @@ -4642,9 +4522,9 @@ func badunlockosthread() { } func gcount() int32 { - n := int32(atomic.Loaduintptr(&allglen)) - sched.gFree.n - int32(atomic.Load(&sched.ngsys)) - for _, _p_ := range allp { - n -= _p_.gFree.n + n := int32(atomic.Loaduintptr(&allglen)) - sched.gFree.n - sched.ngsys.Load() + for _, pp := range allp { + n -= pp.gFree.n } // All these variables can be changed concurrently, so the result can be inconsistent. @@ -4660,8 +4540,11 @@ func mcount() int32 { } var prof struct { - signalLock uint32 - hz int32 + signalLock atomic.Uint32 + + // Must hold signalLock to write. Reads may be lock-free, but + // signalLock should be taken to synchronize with changes. + hz atomic.Int32 } func _System() { _System() } @@ -4673,9 +4556,10 @@ func _VDSO() { _VDSO() } // Called if we receive a SIGPROF signal. // Called by the signal handler, may run during STW. +// //go:nowritebarrierrec func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { - if prof.hz == 0 { + if prof.hz.Load() == 0 { return } @@ -4686,7 +4570,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { return } - // On mips{,le}, 64bit atomics are emulated with spinlocks, in + // On mips{,le}/arm, 64bit atomics are emulated with spinlocks, in // runtime/internal/atomic. If SIGPROF arrives while the program is inside // the critical section, it creates a deadlock (when writing the sample). // As a workaround, create a counter of SIGPROFs while in critical section @@ -4699,6 +4583,13 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { return } } + if GOARCH == "arm" && goarm < 7 && GOOS == "linux" && pc&0xffff0000 == 0xffff0000 { + // runtime/internal/atomic functions call into kernel + // helpers on arm < 7. See + // runtime/internal/atomic/sys_linux_arm.s. + cpuprof.lostAtomic++ + return + } } // Profiling runs concurrently with GC, so it must not allocate. @@ -4718,7 +4609,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // cgoCallers. We are running in a signal handler // with all signals blocked, so we don't have to worry // about any other code interrupting us. - if atomic.Load(&mp.cgoCallersUse) == 0 && mp.cgoCallers != nil && mp.cgoCallers[0] != 0 { + if mp.cgoCallersUse.Load() == 0 && mp.cgoCallers != nil && mp.cgoCallers[0] != 0 { for cgoOff < len(mp.cgoCallers) && mp.cgoCallers[cgoOff] != 0 { cgoOff++ } @@ -4751,63 +4642,41 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // If all of the above has failed, account it against abstract "System" or "GC". n = 2 if inVDSOPage(pc) { - pc = funcPC(_VDSO) + sys.PCQuantum + pc = abi.FuncPCABIInternal(_VDSO) + sys.PCQuantum } else if pc > firstmoduledata.etext { // "ExternalCode" is better than "etext". - pc = funcPC(_ExternalCode) + sys.PCQuantum + pc = abi.FuncPCABIInternal(_ExternalCode) + sys.PCQuantum } stk[0] = pc if mp.preemptoff != "" { - stk[1] = funcPC(_GC) + sys.PCQuantum + stk[1] = abi.FuncPCABIInternal(_GC) + sys.PCQuantum } else { - stk[1] = funcPC(_System) + sys.PCQuantum + stk[1] = abi.FuncPCABIInternal(_System) + sys.PCQuantum } } } - if prof.hz != 0 { - cpuprof.add(gp, stk[:n]) - } - getg().m.mallocing-- -} - -// If the signal handler receives a SIGPROF signal on a non-Go thread, -// it tries to collect a traceback into sigprofCallers. -// sigprofCallersUse is set to non-zero while sigprofCallers holds a traceback. -var sigprofCallers cgoCallers -var sigprofCallersUse uint32 - -// sigprofNonGo is called if we receive a SIGPROF signal on a non-Go thread, -// and the signal handler collected a stack trace in sigprofCallers. -// When this is called, sigprofCallersUse will be non-zero. -// g is nil, and what we can do is very limited. -//go:nosplit -//go:nowritebarrierrec -func sigprofNonGo() { - if prof.hz != 0 { - n := 0 - for n < len(sigprofCallers) && sigprofCallers[n] != 0 { - n++ + if prof.hz.Load() != 0 { + // Note: it can happen on Windows that we interrupted a system thread + // with no g, so gp could nil. The other nil checks are done out of + // caution, but not expected to be nil in practice. + var tagPtr *unsafe.Pointer + if gp != nil && gp.m != nil && gp.m.curg != nil { + tagPtr = &gp.m.curg.labels } - cpuprof.addNonGo(sigprofCallers[:n]) - } + cpuprof.add(tagPtr, stk[:n]) - atomic.Store(&sigprofCallersUse, 0) -} - -// sigprofNonGoPC is called when a profiling signal arrived on a -// non-Go thread and we have a single PC value, not a stack trace. -// g is nil, and what we can do is very limited. -//go:nosplit -//go:nowritebarrierrec -func sigprofNonGoPC(pc uintptr) { - if prof.hz != 0 { - stk := []uintptr{ - pc, - funcPC(_ExternalCode) + sys.PCQuantum, + gprof := gp + var pp *p + if gp != nil && gp.m != nil { + if gp.m.curg != nil { + gprof = gp.m.curg + } + pp = gp.m.p.ptr() } - cpuprof.addNonGo(stk) + traceCPUSample(gprof, pp, stk[:n]) } + getg().m.mallocing-- } // setcpuprofilerate sets the CPU profiling rate to hz times per second. @@ -4820,22 +4689,22 @@ func setcpuprofilerate(hz int32) { // Disable preemption, otherwise we can be rescheduled to another thread // that has profiling enabled. - _g_ := getg() - _g_.m.locks++ + gp := getg() + gp.m.locks++ // Stop profiler on this thread so that it is safe to lock prof. // if a profiling signal came in while we had prof locked, // it would deadlock. setThreadCPUProfiler(0) - for !atomic.Cas(&prof.signalLock, 0, 1) { + for !prof.signalLock.CompareAndSwap(0, 1) { osyield() } - if prof.hz != hz { + if prof.hz.Load() != hz { setProcessCPUProfiler(hz) - prof.hz = hz + prof.hz.Store(hz) } - atomic.Store(&prof.signalLock, 0) + prof.signalLock.Store(0) lock(&sched.lock) sched.profilehz = hz @@ -4845,7 +4714,7 @@ func setcpuprofilerate(hz int32) { setThreadCPUProfiler(hz) } - _g_.m.locks-- + gp.m.locks-- } // init initializes pp, which may be a freshly allocated p or a @@ -4854,9 +4723,7 @@ func (pp *p) init(id int32) { pp.id = id pp.status = _Pgcstop pp.sudogcache = pp.sudogbuf[:0] - for i := range pp.deferpool { - pp.deferpool[i] = pp.deferpoolbuf[i][:0] - } + pp.deferpool = pp.deferpoolbuf[:0] pp.wbBuf.reset() if pp.mcache == nil { if id == 0 { @@ -4918,9 +4785,9 @@ func (pp *p) destroy() { lock(&pp.timersLock) moveTimers(plocal, pp.timers) pp.timers = nil - pp.numTimers = 0 - pp.deletedTimers = 0 - atomic.Store64(&pp.timer0When, 0) + pp.numTimers.Store(0) + pp.deletedTimers.Store(0) + pp.timer0When.Store(0) unlock(&pp.timersLock) unlock(&plocal.timersLock) } @@ -4933,12 +4800,10 @@ func (pp *p) destroy() { pp.sudogbuf[i] = nil } pp.sudogcache = pp.sudogbuf[:0] - for i := range pp.deferpool { - for j := range pp.deferpoolbuf[i] { - pp.deferpoolbuf[i][j] = nil - } - pp.deferpool[i] = pp.deferpoolbuf[i][:0] + for j := range pp.deferpoolbuf { + pp.deferpoolbuf[j] = nil } + pp.deferpool = pp.deferpoolbuf[:0] systemstack(func() { for i := 0; i < pp.mspancache.len; i++ { // Safe to call since the world is stopped. @@ -5046,32 +4911,32 @@ func procresize(nprocs int32) *p { atomicstorep(unsafe.Pointer(&allp[i]), unsafe.Pointer(pp)) } - _g_ := getg() - if _g_.m.p != 0 && _g_.m.p.ptr().id < nprocs { + gp := getg() + if gp.m.p != 0 && gp.m.p.ptr().id < nprocs { // continue to use the current P - _g_.m.p.ptr().status = _Prunning - _g_.m.p.ptr().mcache.prepareForSweep() + gp.m.p.ptr().status = _Prunning + gp.m.p.ptr().mcache.prepareForSweep() } else { // release the current P and acquire allp[0]. // // We must do this before destroying our current P // because p.destroy itself has write barriers, so we // need to do that from a valid P. - if _g_.m.p != 0 { + if gp.m.p != 0 { if trace.enabled { // Pretend that we were descheduled // and then scheduled again to keep // the trace sane. traceGoSched() - traceProcStop(_g_.m.p.ptr()) + traceProcStop(gp.m.p.ptr()) } - _g_.m.p.ptr().m = 0 + gp.m.p.ptr().m = 0 } - _g_.m.p = 0 - p := allp[0] - p.m = 0 - p.status = _Pidle - acquirep(p) + gp.m.p = 0 + pp := allp[0] + pp.m = 0 + pp.status = _Pidle + acquirep(pp) if trace.enabled { traceGoStart() } @@ -5082,8 +4947,8 @@ func procresize(nprocs int32) *p { // release resources from unused P's for i := nprocs; i < old; i++ { - p := allp[i] - p.destroy() + pp := allp[i] + pp.destroy() // can't free P itself because it can be referenced by an M in syscall } @@ -5098,40 +4963,44 @@ func procresize(nprocs int32) *p { var runnablePs *p for i := nprocs - 1; i >= 0; i-- { - p := allp[i] - if _g_.m.p.ptr() == p { + pp := allp[i] + if gp.m.p.ptr() == pp { continue } - p.status = _Pidle - if runqempty(p) { - pidleput(p) + pp.status = _Pidle + if runqempty(pp) { + pidleput(pp, now) } else { - p.m.set(mget()) - p.link.set(runnablePs) - runnablePs = p + pp.m.set(mget()) + pp.link.set(runnablePs) + runnablePs = pp } } stealOrder.reset(uint32(nprocs)) var int32p *int32 = &gomaxprocs // make compiler check that gomaxprocs is an int32 atomic.Store((*uint32)(unsafe.Pointer(int32p)), uint32(nprocs)) + if old != nprocs { + // Notify the limiter that the amount of procs has changed. + gcCPULimiter.resetCapacity(now, nprocs) + } return runnablePs } // Associate p and the current m. // // This function is allowed to have write barriers even if the caller -// isn't because it immediately acquires _p_. +// isn't because it immediately acquires pp. // //go:yeswritebarrierrec -func acquirep(_p_ *p) { +func acquirep(pp *p) { // Do the part that isn't allowed to have write barriers. - wirep(_p_) + wirep(pp) // Have p; write barriers now allowed. // Perform deferred mcache flush before this P can allocate // from a potentially stale mcache. - _p_.mcache.prepareForSweep() + pp.mcache.prepareForSweep() if trace.enabled { traceProcStart() @@ -5139,49 +5008,49 @@ func acquirep(_p_ *p) { } // wirep is the first step of acquirep, which actually associates the -// current M to _p_. This is broken out so we can disallow write +// current M to pp. This is broken out so we can disallow write // barriers for this part, since we don't yet have a P. // //go:nowritebarrierrec //go:nosplit -func wirep(_p_ *p) { - _g_ := getg() +func wirep(pp *p) { + gp := getg() - if _g_.m.p != 0 { + if gp.m.p != 0 { throw("wirep: already in go") } - if _p_.m != 0 || _p_.status != _Pidle { + if pp.m != 0 || pp.status != _Pidle { id := int64(0) - if _p_.m != 0 { - id = _p_.m.ptr().id + if pp.m != 0 { + id = pp.m.ptr().id } - print("wirep: p->m=", _p_.m, "(", id, ") p->status=", _p_.status, "\n") + print("wirep: p->m=", pp.m, "(", id, ") p->status=", pp.status, "\n") throw("wirep: invalid p state") } - _g_.m.p.set(_p_) - _p_.m.set(_g_.m) - _p_.status = _Prunning + gp.m.p.set(pp) + pp.m.set(gp.m) + pp.status = _Prunning } // Disassociate p and the current m. func releasep() *p { - _g_ := getg() + gp := getg() - if _g_.m.p == 0 { + if gp.m.p == 0 { throw("releasep: invalid arg") } - _p_ := _g_.m.p.ptr() - if _p_.m.ptr() != _g_.m || _p_.status != _Prunning { - print("releasep: m=", _g_.m, " m->p=", _g_.m.p.ptr(), " p->m=", hex(_p_.m), " p->status=", _p_.status, "\n") + pp := gp.m.p.ptr() + if pp.m.ptr() != gp.m || pp.status != _Prunning { + print("releasep: m=", gp.m, " m->p=", gp.m.p.ptr(), " p->m=", hex(pp.m), " p->status=", pp.status, "\n") throw("releasep: invalid p state") } if trace.enabled { - traceProcStop(_g_.m.p.ptr()) + traceProcStop(gp.m.p.ptr()) } - _g_.m.p = 0 - _p_.m = 0 - _p_.status = _Pidle - return _p_ + gp.m.p = 0 + pp.m = 0 + pp.status = _Pidle + return pp } func incidlelocked(v int32) { @@ -5210,7 +5079,7 @@ func checkdead() { // freezetheworld will cause all running threads to block. // And runtime will essentially enter into deadlock state, // except that there is a thread that will call exit soon. - if panicking > 0 { + if panicking.Load() > 0 { return } @@ -5256,19 +5125,20 @@ func checkdead() { }) if grunning == 0 { // possible if main goroutine calls runtime·Goexit() unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang - throw("no goroutines (main called runtime.Goexit) - deadlock!") + fatal("no goroutines (main called runtime.Goexit) - deadlock!") } // Maybe jump time forward for playground. if faketime != 0 { - when, _p_ := timeSleepUntil() - if _p_ != nil { + if when := timeSleepUntil(); when < maxWhen { faketime = when - for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link { - if (*pp).ptr() == _p_ { - *pp = _p_.link - break - } + + // Start an M to steal the timer. + pp, _ := pidleget(faketime) + if pp == nil { + // There should always be a free P since + // nothing is running. + throw("checkdead: no p for timer") } mp := mget() if mp == nil { @@ -5276,22 +5146,26 @@ func checkdead() { // nothing is running. throw("checkdead: no m for timer") } - mp.nextp.set(_p_) + // M must be spinning to steal. We set this to be + // explicit, but since this is the only M it would + // become spinning on its own anyways. + sched.nmspinning.Add(1) + mp.spinning = true + mp.nextp.set(pp) notewakeup(&mp.park) return } } // There are no goroutines running, so we can look at the P's. - for _, _p_ := range allp { - if len(_p_.timers) > 0 { + for _, pp := range allp { + if len(pp.timers) > 0 { return } } - getg().m.throwing = -1 // do not dump full stacks - unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang - throw("all goroutines are asleep - deadlock!") + unlock(&sched.lock) // unlock so that GODEBUG=scheddetail=1 doesn't hang + fatal("all goroutines are asleep - deadlock!") } // forcegcperiod is the maximum time in nanoseconds between garbage @@ -5301,6 +5175,10 @@ func checkdead() { // This is a variable for testing purposes. It normally doesn't change. var forcegcperiod int64 = 2 * 60 * 1e9 +// needSysmonWorkaround is true if the workaround for +// golang.org/issue/42515 is needed on NetBSD. +var needSysmonWorkaround bool = false + // Always runs without a P, so write barriers are not allowed. // //go:nowritebarrierrec @@ -5310,10 +5188,6 @@ func sysmon() { checkdead() unlock(&sched.lock) - // For syscall_runtime_doAllThreadsSyscall, sysmon is - // sufficiently up to participate in fixups. - atomic.Store(&sched.sysmonStarting, 0) - lasttrace := int64(0) idle := 0 // how many cycles in succession we had not wokeup somebody delay := uint32(0) @@ -5328,7 +5202,6 @@ func sysmon() { delay = 10 * 1000 } usleep(delay) - mDoFixup() // sysmon should not enter deep sleep if schedtrace is enabled so that // it can print that information at the right time. @@ -5346,13 +5219,13 @@ func sysmon() { // from a timer to avoid adding system load to applications that spend // most of their time sleeping. now := nanotime() - if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) { + if debug.schedtrace <= 0 && (sched.gcwaiting.Load() || sched.npidle.Load() == gomaxprocs) { lock(&sched.lock) - if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) { + if sched.gcwaiting.Load() || sched.npidle.Load() == gomaxprocs { syscallWake := false - next, _ := timeSleepUntil() + next := timeSleepUntil() if next > now { - atomic.Store(&sched.sysmonwait, 1) + sched.sysmonwait.Store(true) unlock(&sched.lock) // Make wake-up period small enough // for the sampling to be correct. @@ -5365,12 +5238,11 @@ func sysmon() { osRelax(true) } syscallWake = notetsleep(&sched.sysmonnote, sleep) - mDoFixup() if shouldRelax { osRelax(false) } lock(&sched.lock) - atomic.Store(&sched.sysmonwait, 0) + sched.sysmonwait.Store(false) noteclear(&sched.sysmonnote) } if syscallWake { @@ -5391,9 +5263,9 @@ func sysmon() { asmcgocall(*cgo_yield, nil) } // poll network if not polled for more than 10ms - lastpoll := int64(atomic.Load64(&sched.lastpoll)) + lastpoll := sched.lastpoll.Load() if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now { - atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now)) + sched.lastpoll.CompareAndSwap(lastpoll, now) list := netpoll(0) // non-blocking - returns list of goroutines if !list.empty() { // Need to decrement number of idle locked M's @@ -5408,8 +5280,7 @@ func sysmon() { incidlelocked(1) } } - mDoFixup() - if GOOS == "netbsd" { + if GOOS == "netbsd" && needSysmonWorkaround { // netpoll is responsible for waiting for timer // expiration, so we typically don't have to worry // about starting an M to service timers. (Note that @@ -5425,13 +5296,13 @@ func sysmon() { // // See issue 42515 and // https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094. - if next, _ := timeSleepUntil(); next < now { + if next := timeSleepUntil(); next < now { startm(nil, false) } } - if atomic.Load(&scavenge.sysmonWake) != 0 { + if scavenger.sysmonWake.Load() != 0 { // Kick the scavenger awake if someone requested it. - wakeScavenger() + scavenger.wake() } // retake P's blocked in syscalls // and preempt long running G's @@ -5441,9 +5312,9 @@ func sysmon() { idle++ } // check if we need to force a GC - if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { + if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && forcegc.idle.Load() { lock(&forcegc.lock) - forcegc.idle = 0 + forcegc.idle.Store(false) var list gList list.push(forcegc.g) injectglist(&list) @@ -5477,23 +5348,23 @@ func retake(now int64) uint32 { // temporarily drop the allpLock. Hence, we need to re-fetch // allp each time around the loop. for i := 0; i < len(allp); i++ { - _p_ := allp[i] - if _p_ == nil { + pp := allp[i] + if pp == nil { // This can happen if procresize has grown // allp but not yet created new Ps. continue } - pd := &_p_.sysmontick - s := _p_.status + pd := &pp.sysmontick + s := pp.status sysretake := false if s == _Prunning || s == _Psyscall { // Preempt G if it's running for too long. - t := int64(_p_.schedtick) + t := int64(pp.schedtick) if int64(pd.schedtick) != t { pd.schedtick = uint32(t) pd.schedwhen = now } else if pd.schedwhen+forcePreemptNS <= now { - preemptone(_p_) + preemptone(pp) // In case of syscall, preemptone() doesn't // work, because there is no M wired to P. sysretake = true @@ -5501,7 +5372,7 @@ func retake(now int64) uint32 { } if s == _Psyscall { // Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us). - t := int64(_p_.syscalltick) + t := int64(pp.syscalltick) if !sysretake && int64(pd.syscalltick) != t { pd.syscalltick = uint32(t) pd.syscallwhen = now @@ -5510,7 +5381,7 @@ func retake(now int64) uint32 { // On the one hand we don't want to retake Ps if there is no other work to do, // but on the other hand we want to retake them eventually // because they can prevent the sysmon thread from deep sleep. - if runqempty(_p_) && atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now { + if runqempty(pp) && sched.nmspinning.Load()+sched.npidle.Load() > 0 && pd.syscallwhen+10*1000*1000 > now { continue } // Drop allpLock so we can take sched.lock. @@ -5520,14 +5391,14 @@ func retake(now int64) uint32 { // Otherwise the M from which we retake can exit the syscall, // increment nmidle and report deadlock. incidlelocked(-1) - if atomic.Cas(&_p_.status, s, _Pidle) { + if atomic.Cas(&pp.status, s, _Pidle) { if trace.enabled { - traceGoSysBlock(_p_) - traceProcStop(_p_) + traceGoSysBlock(pp) + traceProcStop(pp) } n++ - _p_.syscalltick++ - handoffp(_p_) + pp.syscalltick++ + handoffp(pp) } incidlelocked(1) lock(&allpLock) @@ -5544,11 +5415,11 @@ func retake(now int64) uint32 { // Returns true if preemption request was issued to at least one goroutine. func preemptall() bool { res := false - for _, _p_ := range allp { - if _p_.status != _Prunning { + for _, pp := range allp { + if pp.status != _Prunning { continue } - if preemptone(_p_) { + if preemptone(pp) { res = true } } @@ -5565,8 +5436,8 @@ func preemptall() bool { // The actual preemption will happen at some point in the future // and will be indicated by the gp->status no longer being // Grunning -func preemptone(_p_ *p) bool { - mp := _p_.m.ptr() +func preemptone(pp *p) bool { + mp := pp.m.ptr() if mp == nil || mp == getg().m { return false } @@ -5585,7 +5456,7 @@ func preemptone(_p_ *p) bool { // Request an async preemption of this P. if preemptMSupported && debug.asyncpreemptoff == 0 { - _p_.preempt = true + pp.preempt = true preemptM(mp) } @@ -5601,23 +5472,25 @@ func schedtrace(detailed bool) { } lock(&sched.lock) - print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle, " threads=", mcount(), " spinningthreads=", sched.nmspinning, " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize) + print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle.Load(), " threads=", mcount(), " spinningthreads=", sched.nmspinning.Load(), " needspinning=", sched.needspinning.Load(), " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize) if detailed { - print(" gcwaiting=", sched.gcwaiting, " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait, "\n") + print(" gcwaiting=", sched.gcwaiting.Load(), " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait.Load(), "\n") } // We must be careful while reading data from P's, M's and G's. // Even if we hold schedlock, most data can be changed concurrently. // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil. - for i, _p_ := range allp { - mp := _p_.m.ptr() - h := atomic.Load(&_p_.runqhead) - t := atomic.Load(&_p_.runqtail) + for i, pp := range allp { + mp := pp.m.ptr() + h := atomic.Load(&pp.runqhead) + t := atomic.Load(&pp.runqtail) if detailed { - id := int64(-1) + print(" P", i, ": status=", pp.status, " schedtick=", pp.schedtick, " syscalltick=", pp.syscalltick, " m=") if mp != nil { - id = mp.id + print(mp.id) + } else { + print("nil") } - print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gFree.n, " timerslen=", len(_p_.timers), "\n") + print(" runqsize=", t-h, " gfreecnt=", pp.gFree.n, " timerslen=", len(pp.timers), "\n") } else { // In non-detailed mode format lengths of per-P run queues as: // [len1 len2 len3 len4] @@ -5638,36 +5511,42 @@ func schedtrace(detailed bool) { } for mp := allm; mp != nil; mp = mp.alllink { - _p_ := mp.p.ptr() - gp := mp.curg - lockedg := mp.lockedg.ptr() - id1 := int32(-1) - if _p_ != nil { - id1 = _p_.id - } - id2 := int64(-1) - if gp != nil { - id2 = gp.goid + pp := mp.p.ptr() + print(" M", mp.id, ": p=") + if pp != nil { + print(pp.id) + } else { + print("nil") + } + print(" curg=") + if mp.curg != nil { + print(mp.curg.goid) + } else { + print("nil") } - id3 := int64(-1) - if lockedg != nil { - id3 = lockedg.goid + print(" mallocing=", mp.mallocing, " throwing=", mp.throwing, " preemptoff=", mp.preemptoff, " locks=", mp.locks, " dying=", mp.dying, " spinning=", mp.spinning, " blocked=", mp.blocked, " lockedg=") + if lockedg := mp.lockedg.ptr(); lockedg != nil { + print(lockedg.goid) + } else { + print("nil") } - print(" M", mp.id, ": p=", id1, " curg=", id2, " mallocing=", mp.mallocing, " throwing=", mp.throwing, " preemptoff=", mp.preemptoff, ""+" locks=", mp.locks, " dying=", mp.dying, " spinning=", mp.spinning, " blocked=", mp.blocked, " lockedg=", id3, "\n") + print("\n") } forEachG(func(gp *g) { - mp := gp.m - lockedm := gp.lockedm.ptr() - id1 := int64(-1) - if mp != nil { - id1 = mp.id + print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason.String(), ") m=") + if gp.m != nil { + print(gp.m.id) + } else { + print("nil") } - id2 := int64(-1) - if lockedm != nil { - id2 = lockedm.id + print(" lockedm=") + if lockedm := gp.lockedm.ptr(); lockedm != nil { + print(lockedm.id) + } else { + print("nil") } - print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason.String(), ") m=", id1, " lockedm=", id2, "\n") + print("\n") }) unlock(&sched.lock) } @@ -5689,7 +5568,7 @@ func schedEnableUser(enable bool) { sched.disable.n = 0 globrunqputbatch(&sched.disable.runnable, n) unlock(&sched.lock) - for ; n != 0 && sched.npidle != 0; n-- { + for ; n != 0 && sched.npidle.Load() != 0; n-- { startm(nil, false) } } else { @@ -5713,6 +5592,7 @@ func schedEnabled(gp *g) bool { // Put mp on midle list. // sched.lock must be held. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func mput(mp *m) { assertLockHeld(&sched.lock) @@ -5726,6 +5606,7 @@ func mput(mp *m) { // Try to get an m from midle list. // sched.lock must be held. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func mget() *m { assertLockHeld(&sched.lock) @@ -5741,6 +5622,7 @@ func mget() *m { // Put gp on the global runnable queue. // sched.lock must be held. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func globrunqput(gp *g) { assertLockHeld(&sched.lock) @@ -5752,6 +5634,7 @@ func globrunqput(gp *g) { // Put gp at the head of the global runnable queue. // sched.lock must be held. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func globrunqputhead(gp *g) { assertLockHeld(&sched.lock) @@ -5764,6 +5647,7 @@ func globrunqputhead(gp *g) { // This clears *batch. // sched.lock must be held. // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec func globrunqputbatch(batch *gQueue, n int32) { assertLockHeld(&sched.lock) @@ -5775,7 +5659,7 @@ func globrunqputbatch(batch *gQueue, n int32) { // Try get a batch of G's from the global runnable queue. // sched.lock must be held. -func globrunqget(_p_ *p, max int32) *g { +func globrunqget(pp *p, max int32) *g { assertLockHeld(&sched.lock) if sched.runqsize == 0 { @@ -5789,8 +5673,8 @@ func globrunqget(_p_ *p, max int32) *g { if max > 0 && n > max { n = max } - if n > int32(len(_p_.runq))/2 { - n = int32(len(_p_.runq)) / 2 + if n > int32(len(pp.runq))/2 { + n = int32(len(pp.runq)) / 2 } sched.runqsize -= n @@ -5799,7 +5683,7 @@ func globrunqget(_p_ *p, max int32) *g { n-- for ; n > 0; n-- { gp1 := sched.runq.pop() - runqput(_p_, gp1, false) + runqput(pp, gp1, false) } return gp } @@ -5842,11 +5726,11 @@ func (p pMask) clear(id int32) { // // Thus, we get the following effects on timer-stealing in findrunnable: // -// * Idle Ps with no timers when they go idle are never checked in findrunnable -// (for work- or timer-stealing; this is the ideal case). -// * Running Ps must always be checked. -// * Idle Ps whose timers are stolen must continue to be checked until they run -// again, even after timer expiration. +// - Idle Ps with no timers when they go idle are never checked in findrunnable +// (for work- or timer-stealing; this is the ideal case). +// - Running Ps must always be checked. +// - Idle Ps whose timers are stolen must continue to be checked until they run +// again, even after timer expiration. // // When the P starts running again, the mask should be set, as a timer may be // added at any time. @@ -5854,7 +5738,7 @@ func (p pMask) clear(id int32) { // TODO(prattmic): Additional targeted updates may improve the above cases. // e.g., updating the mask when stealing a timer. func updateTimerPMask(pp *p) { - if atomic.Load(&pp.numTimers) > 0 { + if pp.numTimers.Load() > 0 { return } @@ -5862,13 +5746,14 @@ func updateTimerPMask(pp *p) { // decrement numTimers when handling a timerModified timer in // checkTimers. We must take timersLock to serialize with these changes. lock(&pp.timersLock) - if atomic.Load(&pp.numTimers) == 0 { + if pp.numTimers.Load() == 0 { timerpMask.clear(pp.id) } unlock(&pp.timersLock) } -// pidleput puts p to on the _Pidle list. +// pidleput puts p on the _Pidle list. now must be a relatively recent call +// to nanotime or zero. Returns now or the current time if now was zero. // // This releases ownership of p. Once sched.lock is released it is no longer // safe to use p. @@ -5876,18 +5761,26 @@ func updateTimerPMask(pp *p) { // sched.lock must be held. // // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec -func pidleput(_p_ *p) { +func pidleput(pp *p, now int64) int64 { assertLockHeld(&sched.lock) - if !runqempty(_p_) { + if !runqempty(pp) { throw("pidleput: P has non-empty run queue") } - updateTimerPMask(_p_) // clear if there are no timers. - idlepMask.set(_p_.id) - _p_.link = sched.pidle - sched.pidle.set(_p_) - atomic.Xadd(&sched.npidle, 1) // TODO: fast atomic + if now == 0 { + now = nanotime() + } + updateTimerPMask(pp) // clear if there are no timers. + idlepMask.set(pp.id) + pp.link = sched.pidle + sched.pidle.set(pp) + sched.npidle.Add(1) + if !pp.limiterEvent.start(limiterEventIdle, now) { + throw("must be able to track idle limiter event") + } + return now } // pidleget tries to get a p from the _Pidle list, acquiring ownership. @@ -5895,33 +5788,63 @@ func pidleput(_p_ *p) { // sched.lock must be held. // // May run during STW, so write barriers are not allowed. +// //go:nowritebarrierrec -func pidleget() *p { +func pidleget(now int64) (*p, int64) { assertLockHeld(&sched.lock) - _p_ := sched.pidle.ptr() - if _p_ != nil { + pp := sched.pidle.ptr() + if pp != nil { // Timer may get added at any time now. - timerpMask.set(_p_.id) - idlepMask.clear(_p_.id) - sched.pidle = _p_.link - atomic.Xadd(&sched.npidle, -1) // TODO: fast atomic + if now == 0 { + now = nanotime() + } + timerpMask.set(pp.id) + idlepMask.clear(pp.id) + sched.pidle = pp.link + sched.npidle.Add(-1) + pp.limiterEvent.stop(limiterEventIdle, now) } - return _p_ + return pp, now } -// runqempty reports whether _p_ has no Gs on its local run queue. +// pidlegetSpinning tries to get a p from the _Pidle list, acquiring ownership. +// This is called by spinning Ms (or callers than need a spinning M) that have +// found work. If no P is available, this must synchronized with non-spinning +// Ms that may be preparing to drop their P without discovering this work. +// +// sched.lock must be held. +// +// May run during STW, so write barriers are not allowed. +// +//go:nowritebarrierrec +func pidlegetSpinning(now int64) (*p, int64) { + assertLockHeld(&sched.lock) + + pp, now := pidleget(now) + if pp == nil { + // See "Delicate dance" comment in findrunnable. We found work + // that we cannot take, we must synchronize with non-spinning + // Ms that may be preparing to drop their P. + sched.needspinning.Store(1) + return nil, now + } + + return pp, now +} + +// runqempty reports whether pp has no Gs on its local run queue. // It never returns true spuriously. -func runqempty(_p_ *p) bool { - // Defend against a race where 1) _p_ has G1 in runqnext but runqhead == runqtail, - // 2) runqput on _p_ kicks G1 to the runq, 3) runqget on _p_ empties runqnext. +func runqempty(pp *p) bool { + // Defend against a race where 1) pp has G1 in runqnext but runqhead == runqtail, + // 2) runqput on pp kicks G1 to the runq, 3) runqget on pp empties runqnext. // Simply observing that runqhead == runqtail and then observing that runqnext == nil // does not mean the queue is empty. for { - head := atomic.Load(&_p_.runqhead) - tail := atomic.Load(&_p_.runqtail) - runnext := atomic.Loaduintptr((*uintptr)(unsafe.Pointer(&_p_.runnext))) - if tail == atomic.Load(&_p_.runqtail) { + head := atomic.Load(&pp.runqhead) + tail := atomic.Load(&pp.runqtail) + runnext := atomic.Loaduintptr((*uintptr)(unsafe.Pointer(&pp.runnext))) + if tail == atomic.Load(&pp.runqtail) { return head == tail && runnext == 0 } } @@ -5940,18 +5863,18 @@ const randomizeScheduler = raceenabled // runqput tries to put g on the local runnable queue. // If next is false, runqput adds g to the tail of the runnable queue. -// If next is true, runqput puts g in the _p_.runnext slot. +// If next is true, runqput puts g in the pp.runnext slot. // If the run queue is full, runnext puts g on the global queue. // Executed only by the owner P. -func runqput(_p_ *p, gp *g, next bool) { - if randomizeScheduler && next && fastrand()%2 == 0 { +func runqput(pp *p, gp *g, next bool) { + if randomizeScheduler && next && fastrandn(2) == 0 { next = false } if next { retryNext: - oldnext := _p_.runnext - if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) { + oldnext := pp.runnext + if !pp.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) { goto retryNext } if oldnext == 0 { @@ -5962,14 +5885,14 @@ func runqput(_p_ *p, gp *g, next bool) { } retry: - h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with consumers - t := _p_.runqtail - if t-h < uint32(len(_p_.runq)) { - _p_.runq[t%uint32(len(_p_.runq))].set(gp) - atomic.StoreRel(&_p_.runqtail, t+1) // store-release, makes the item available for consumption + h := atomic.LoadAcq(&pp.runqhead) // load-acquire, synchronize with consumers + t := pp.runqtail + if t-h < uint32(len(pp.runq)) { + pp.runq[t%uint32(len(pp.runq))].set(gp) + atomic.StoreRel(&pp.runqtail, t+1) // store-release, makes the item available for consumption return } - if runqputslow(_p_, gp, h, t) { + if runqputslow(pp, gp, h, t) { return } // the queue is not full, now the put above must succeed @@ -5978,19 +5901,19 @@ retry: // Put g and a batch of work from local runnable queue on global queue. // Executed only by the owner P. -func runqputslow(_p_ *p, gp *g, h, t uint32) bool { - var batch [len(_p_.runq)/2 + 1]*g +func runqputslow(pp *p, gp *g, h, t uint32) bool { + var batch [len(pp.runq)/2 + 1]*g // First, grab a batch from local queue. n := t - h n = n / 2 - if n != uint32(len(_p_.runq)/2) { + if n != uint32(len(pp.runq)/2) { throw("runqputslow: queue is not full") } for i := uint32(0); i < n; i++ { - batch[i] = _p_.runq[(h+i)%uint32(len(_p_.runq))].ptr() + batch[i] = pp.runq[(h+i)%uint32(len(pp.runq))].ptr() } - if !atomic.CasRel(&_p_.runqhead, h, h+n) { // cas-release, commits consume + if !atomic.CasRel(&pp.runqhead, h, h+n) { // cas-release, commits consume return false } batch[n] = gp @@ -6055,52 +5978,50 @@ func runqputbatch(pp *p, q *gQueue, qsize int) { // If inheritTime is true, gp should inherit the remaining time in the // current time slice. Otherwise, it should start a new time slice. // Executed only by the owner P. -func runqget(_p_ *p) (gp *g, inheritTime bool) { +func runqget(pp *p) (gp *g, inheritTime bool) { // If there's a runnext, it's the next G to run. - for { - next := _p_.runnext - if next == 0 { - break - } - if _p_.runnext.cas(next, 0) { - return next.ptr(), true - } + next := pp.runnext + // If the runnext is non-0 and the CAS fails, it could only have been stolen by another P, + // because other Ps can race to set runnext to 0, but only the current P can set it to non-0. + // Hence, there's no need to retry this CAS if it fails. + if next != 0 && pp.runnext.cas(next, 0) { + return next.ptr(), true } for { - h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers - t := _p_.runqtail + h := atomic.LoadAcq(&pp.runqhead) // load-acquire, synchronize with other consumers + t := pp.runqtail if t == h { return nil, false } - gp := _p_.runq[h%uint32(len(_p_.runq))].ptr() - if atomic.CasRel(&_p_.runqhead, h, h+1) { // cas-release, commits consume + gp := pp.runq[h%uint32(len(pp.runq))].ptr() + if atomic.CasRel(&pp.runqhead, h, h+1) { // cas-release, commits consume return gp, false } } } -// runqdrain drains the local runnable queue of _p_ and returns all goroutines in it. +// runqdrain drains the local runnable queue of pp and returns all goroutines in it. // Executed only by the owner P. -func runqdrain(_p_ *p) (drainQ gQueue, n uint32) { - oldNext := _p_.runnext - if oldNext != 0 && _p_.runnext.cas(oldNext, 0) { +func runqdrain(pp *p) (drainQ gQueue, n uint32) { + oldNext := pp.runnext + if oldNext != 0 && pp.runnext.cas(oldNext, 0) { drainQ.pushBack(oldNext.ptr()) n++ } retry: - h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers - t := _p_.runqtail + h := atomic.LoadAcq(&pp.runqhead) // load-acquire, synchronize with other consumers + t := pp.runqtail qn := t - h if qn == 0 { return } - if qn > uint32(len(_p_.runq)) { // read inconsistent h and t + if qn > uint32(len(pp.runq)) { // read inconsistent h and t goto retry } - if !atomic.CasRel(&_p_.runqhead, h, h+qn) { // cas-release, commits consume + if !atomic.CasRel(&pp.runqhead, h, h+qn) { // cas-release, commits consume goto retry } @@ -6112,48 +6033,48 @@ retry: // meanwhile, other P's can't access to all G's in local P's runnable queue and steal them. // See https://groups.google.com/g/golang-dev/c/0pTKxEKhHSc/m/6Q85QjdVBQAJ for more details. for i := uint32(0); i < qn; i++ { - gp := _p_.runq[(h+i)%uint32(len(_p_.runq))].ptr() + gp := pp.runq[(h+i)%uint32(len(pp.runq))].ptr() drainQ.pushBack(gp) n++ } return } -// Grabs a batch of goroutines from _p_'s runnable queue into batch. +// Grabs a batch of goroutines from pp's runnable queue into batch. // Batch is a ring buffer starting at batchHead. // Returns number of grabbed goroutines. // Can be executed by any P. -func runqgrab(_p_ *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool) uint32 { +func runqgrab(pp *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool) uint32 { for { - h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with other consumers - t := atomic.LoadAcq(&_p_.runqtail) // load-acquire, synchronize with the producer + h := atomic.LoadAcq(&pp.runqhead) // load-acquire, synchronize with other consumers + t := atomic.LoadAcq(&pp.runqtail) // load-acquire, synchronize with the producer n := t - h n = n - n/2 if n == 0 { if stealRunNextG { - // Try to steal from _p_.runnext. - if next := _p_.runnext; next != 0 { - if _p_.status == _Prunning { - // Sleep to ensure that _p_ isn't about to run the g + // Try to steal from pp.runnext. + if next := pp.runnext; next != 0 { + if pp.status == _Prunning { + // Sleep to ensure that pp isn't about to run the g // we are about to steal. // The important use case here is when the g running - // on _p_ ready()s another g and then almost + // on pp ready()s another g and then almost // immediately blocks. Instead of stealing runnext - // in this window, back off to give _p_ a chance to + // in this window, back off to give pp a chance to // schedule runnext. This will avoid thrashing gs // between different Ps. // A sync chan send/recv takes ~50ns as of time of // writing, so 3us gives ~50x overshoot. - if GOOS != "windows" { + if GOOS != "windows" && GOOS != "openbsd" && GOOS != "netbsd" { usleep(3) } else { - // On windows system timer granularity is + // On some platforms system timer granularity is // 1-15ms, which is way too much for this // optimization. So just yield. osyield() } } - if !_p_.runnext.cas(next, 0) { + if !pp.runnext.cas(next, 0) { continue } batch[batchHead%uint32(len(batch))] = next @@ -6162,14 +6083,14 @@ func runqgrab(_p_ *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool } return 0 } - if n > uint32(len(_p_.runq)/2) { // read inconsistent h and t + if n > uint32(len(pp.runq)/2) { // read inconsistent h and t continue } for i := uint32(0); i < n; i++ { - g := _p_.runq[(h+i)%uint32(len(_p_.runq))] + g := pp.runq[(h+i)%uint32(len(pp.runq))] batch[(batchHead+i)%uint32(len(batch))] = g } - if atomic.CasRel(&_p_.runqhead, h, h+n) { // cas-release, commits consume + if atomic.CasRel(&pp.runqhead, h, h+n) { // cas-release, commits consume return n } } @@ -6178,22 +6099,22 @@ func runqgrab(_p_ *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool // Steal half of elements from local runnable queue of p2 // and put onto local runnable queue of p. // Returns one of the stolen elements (or nil if failed). -func runqsteal(_p_, p2 *p, stealRunNextG bool) *g { - t := _p_.runqtail - n := runqgrab(p2, &_p_.runq, t, stealRunNextG) +func runqsteal(pp, p2 *p, stealRunNextG bool) *g { + t := pp.runqtail + n := runqgrab(p2, &pp.runq, t, stealRunNextG) if n == 0 { return nil } n-- - gp := _p_.runq[(t+n)%uint32(len(_p_.runq))].ptr() + gp := pp.runq[(t+n)%uint32(len(pp.runq))].ptr() if n == 0 { return gp } - h := atomic.LoadAcq(&_p_.runqhead) // load-acquire, synchronize with consumers - if t-h+n >= uint32(len(_p_.runq)) { + h := atomic.LoadAcq(&pp.runqhead) // load-acquire, synchronize with consumers + if t-h+n >= uint32(len(pp.runq)) { throw("runqsteal: runq overflow") } - atomic.StoreRel(&_p_.runqtail, t+n) // store-release, makes the item available for consumption + atomic.StoreRel(&pp.runqtail, t+n) // store-release, makes the item available for consumption return gp } @@ -6229,7 +6150,7 @@ func (q *gQueue) pushBack(gp *g) { q.tail.set(gp) } -// pushBackAll adds all Gs in l2 to the tail of q. After this q2 must +// pushBackAll adds all Gs in q2 to the tail of q. After this q2 must // not be used. func (q *gQueue) pushBackAll(q2 gQueue) { if q2.tail == 0 { @@ -6314,8 +6235,8 @@ func setMaxThreads(in int) (out int) { //go:nosplit func procPin() int { - _g_ := getg() - mp := _g_.m + gp := getg() + mp := gp.m mp.locks++ return int(mp.p.ptr().id) @@ -6323,8 +6244,8 @@ func procPin() int { //go:nosplit func procUnpin() { - _g_ := getg() - _g_.m.locks-- + gp := getg() + gp.m.locks-- } //go:linkname sync_runtime_procPin sync.runtime_procPin @@ -6352,6 +6273,7 @@ func sync_atomic_runtime_procUnpin() { } // Active spinning for sync.Mutex. +// //go:linkname sync_runtime_canSpin sync.runtime_canSpin //go:nosplit func sync_runtime_canSpin(i int) bool { @@ -6360,7 +6282,7 @@ func sync_runtime_canSpin(i int) bool { // GOMAXPROCS>1 and there is at least one other running P and local runq is empty. // As opposed to runtime mutex we don't do passive spinning here, // because there can be work on global runq or on other Ps. - if i >= active_spin || ncpu <= 1 || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 { + if i >= active_spin || ncpu <= 1 || gomaxprocs <= sched.npidle.Load()+sched.nmspinning.Load()+1 { return false } if p := getg().m.p.ptr(); !runqempty(p) { @@ -6407,7 +6329,7 @@ func (ord *randomOrder) start(i uint32) randomEnum { return randomEnum{ count: ord.count, pos: i % ord.count, - inc: ord.coprimes[i%uint32(len(ord.coprimes))], + inc: ord.coprimes[i/ord.count%uint32(len(ord.coprimes))], } } @@ -6448,7 +6370,7 @@ var inittrace tracestat type tracestat struct { active bool // init tracing activation status - id int64 // init goroutine id + id uint64 // init goroutine id allocs uint64 // heap allocations bytes uint64 // heap allocated bytes } @@ -6463,7 +6385,7 @@ func doInit(t *initTask) { t.state = 1 // initialization in progress for i := uintptr(0); i < t.ndeps; i++ { - p := add(unsafe.Pointer(t), (3+i)*sys.PtrSize) + p := add(unsafe.Pointer(t), (3+i)*goarch.PtrSize) t2 := *(**initTask)(p) doInit(t2) } @@ -6484,9 +6406,9 @@ func doInit(t *initTask) { before = inittrace } - firstFunc := add(unsafe.Pointer(t), (3+t.ndeps)*sys.PtrSize) + firstFunc := add(unsafe.Pointer(t), (3+t.ndeps)*goarch.PtrSize) for i := uintptr(0); i < t.nfns; i++ { - p := add(firstFunc, i*sys.PtrSize) + p := add(firstFunc, i*goarch.PtrSize) f := *(*func())(unsafe.Pointer(&p)) f() } @@ -6496,7 +6418,8 @@ func doInit(t *initTask) { // Load stats non-atomically since tracinit is updated only by this init goroutine. after := inittrace - pkg := funcpkgpath(findfunc(funcPC(firstFunc))) + f := *(*func())(unsafe.Pointer(&firstFunc)) + pkg := funcpkgpath(findfunc(abi.FuncPCABIInternal(f))) var sbuf [24]byte print("init ", pkg, " @") diff --git a/src/runtime/proc_runtime_test.go b/src/runtime/proc_runtime_test.go index a7bde2c6df7c3e..90aed83d46a52e 100644 --- a/src/runtime/proc_runtime_test.go +++ b/src/runtime/proc_runtime_test.go @@ -30,4 +30,21 @@ func RunStealOrderTest() { } } } + // Make sure that different arguments to ord.start don't generate the + // same pos+inc twice. + for procs := 2; procs <= 64; procs++ { + ord.reset(uint32(procs)) + checked := make([]bool, procs*procs) + // We want at least procs*len(ord.coprimes) different pos+inc values + // before we start repeating. + for i := 0; i < procs*len(ord.coprimes); i++ { + enum := ord.start(uint32(i)) + j := enum.pos*uint32(procs) + enum.inc + if checked[j] { + println("procs:", procs, "pos:", enum.pos, "inc:", enum.inc) + panic("duplicate pos+inc during enumeration") + } + checked[j] = true + } + } } diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 53cafe8907fc09..f354facc49539c 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -119,6 +119,10 @@ func TestGoroutineParallelism(t *testing.T) { // since the goroutines can't be stopped/preempted. // Disable GC for this test (see issue #10958). defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #45867. + runtime.GC() for try := 0; try < N; try++ { done := make(chan bool) x := uint32(0) @@ -163,6 +167,10 @@ func testGoroutineParallelism2(t *testing.T, load, netpoll bool) { // since the goroutines can't be stopped/preempted. // Disable GC for this test (see issue #10958). defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #45867. + runtime.GC() for try := 0; try < N; try++ { if load { // Create P goroutines and wait until they all run. @@ -469,12 +477,13 @@ func TestPingPongHog(t *testing.T) { <-lightChan // Check that hogCount and lightCount are within a factor of - // 5, which indicates that both pairs of goroutines handed off + // 20, which indicates that both pairs of goroutines handed off // the P within a time-slice to their buddy. We can use a // fairly large factor here to make this robust: if the - // scheduler isn't working right, the gap should be ~1000X. - const factor = 5 - if hogCount > lightCount*factor || lightCount > hogCount*factor { + // scheduler isn't working right, the gap should be ~1000X + // (was 5, increased to 20, see issue 52207). + const factor = 20 + if hogCount/factor > lightCount || lightCount/factor > hogCount { t.Fatalf("want hogCount/lightCount in [%v, %v]; got %d/%d = %g", 1.0/factor, factor, hogCount, lightCount, float64(hogCount)/float64(lightCount)) } } @@ -623,6 +632,10 @@ func TestSchedLocalQueueEmpty(t *testing.T) { // If runtime triggers a forced GC during this test then it will deadlock, // since the goroutines can't be stopped/preempted during spin wait. defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #45867. + runtime.GC() iters := int(1e5) if testing.Short() { @@ -1011,6 +1024,7 @@ func TestLockOSThreadTemplateThreadRace(t *testing.T) { } // fakeSyscall emulates a system call. +// //go:nosplit func fakeSyscall(duration time.Duration) { runtime.Entersyscall() @@ -1032,7 +1046,7 @@ func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) { interations = 1 } const ( - maxDuration = 3 * time.Second + maxDuration = 5 * time.Second nroutines = 8 ) @@ -1068,6 +1082,10 @@ func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) { } func TestPreemptionAfterSyscall(t *testing.T) { + if runtime.GOOS == "plan9" { + testenv.SkipFlaky(t, 41015) + } + for _, i := range []time.Duration{10, 100, 1000} { d := i * time.Microsecond t.Run(fmt.Sprint(d), func(t *testing.T) { diff --git a/src/runtime/profbuf.go b/src/runtime/profbuf.go index f40881aed5150d..c579f214887cff 100644 --- a/src/runtime/profbuf.go +++ b/src/runtime/profbuf.go @@ -84,13 +84,12 @@ import ( // if uint32(overflow) > 0 { // emit entry for uint32(overflow), time // } -// type profBuf struct { // accessed atomically r, w profAtomic - overflow uint64 - overflowTime uint64 - eof uint32 + overflow atomic.Uint64 + overflowTime atomic.Uint64 + eof atomic.Uint32 // immutable (excluding slice content) hdrsize uintptr @@ -151,15 +150,15 @@ func (x profIndex) addCountsAndClearFlags(data, tag int) profIndex { // hasOverflow reports whether b has any overflow records pending. func (b *profBuf) hasOverflow() bool { - return uint32(atomic.Load64(&b.overflow)) > 0 + return uint32(b.overflow.Load()) > 0 } // takeOverflow consumes the pending overflow records, returning the overflow count // and the time of the first overflow. // When called by the reader, it is racing against incrementOverflow. func (b *profBuf) takeOverflow() (count uint32, time uint64) { - overflow := atomic.Load64(&b.overflow) - time = atomic.Load64(&b.overflowTime) + overflow := b.overflow.Load() + time = b.overflowTime.Load() for { count = uint32(overflow) if count == 0 { @@ -167,11 +166,11 @@ func (b *profBuf) takeOverflow() (count uint32, time uint64) { break } // Increment generation, clear overflow count in low bits. - if atomic.Cas64(&b.overflow, overflow, ((overflow>>32)+1)<<32) { + if b.overflow.CompareAndSwap(overflow, ((overflow>>32)+1)<<32) { break } - overflow = atomic.Load64(&b.overflow) - time = atomic.Load64(&b.overflowTime) + overflow = b.overflow.Load() + time = b.overflowTime.Load() } return uint32(overflow), time } @@ -180,14 +179,14 @@ func (b *profBuf) takeOverflow() (count uint32, time uint64) { // It is racing against a possible takeOverflow in the reader. func (b *profBuf) incrementOverflow(now int64) { for { - overflow := atomic.Load64(&b.overflow) + overflow := b.overflow.Load() // Once we see b.overflow reach 0, it's stable: no one else is changing it underfoot. // We need to set overflowTime if we're incrementing b.overflow from 0. if uint32(overflow) == 0 { // Store overflowTime first so it's always available when overflow != 0. - atomic.Store64(&b.overflowTime, uint64(now)) - atomic.Store64(&b.overflow, (((overflow>>32)+1)<<32)+1) + b.overflowTime.Store(uint64(now)) + b.overflow.Store((((overflow >> 32) + 1) << 32) + 1) break } // Otherwise we're racing to increment against reader @@ -197,7 +196,7 @@ func (b *profBuf) incrementOverflow(now int64) { if int32(overflow) == -1 { break } - if atomic.Cas64(&b.overflow, overflow, overflow+1) { + if b.overflow.CompareAndSwap(overflow, overflow+1) { break } } @@ -395,10 +394,10 @@ func (b *profBuf) write(tagPtr *unsafe.Pointer, now int64, hdr []uint64, stk []u // close signals that there will be no more writes on the buffer. // Once all the data has been read from the buffer, reads will return eof=true. func (b *profBuf) close() { - if atomic.Load(&b.eof) > 0 { + if b.eof.Load() > 0 { throw("runtime: profBuf already closed") } - atomic.Store(&b.eof, 1) + b.eof.Store(1) b.wakeupExtra() } @@ -476,7 +475,7 @@ Read: dst[2+b.hdrsize] = uint64(count) return dst[:2+b.hdrsize+1], overflowTag[:1], false } - if atomic.Load(&b.eof) > 0 { + if b.eof.Load() > 0 { // No data, no overflow, EOF set: done. return nil, nil, true } diff --git a/src/runtime/race.go b/src/runtime/race.go index ce6b5b54681533..f68e1aeaaca6a2 100644 --- a/src/runtime/race.go +++ b/src/runtime/race.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package runtime import ( + "internal/abi" "unsafe" ) @@ -67,21 +67,21 @@ func RaceReleaseMerge(addr unsafe.Pointer) { // Non-synchronization events (memory accesses, function entry/exit) still affect // the race detector. func RaceDisable() { - _g_ := getg() - if _g_.raceignore == 0 { - racecall(&__tsan_go_ignore_sync_begin, _g_.racectx, 0, 0, 0) + gp := getg() + if gp.raceignore == 0 { + racecall(&__tsan_go_ignore_sync_begin, gp.racectx, 0, 0, 0) } - _g_.raceignore++ + gp.raceignore++ } //go:nosplit // RaceEnable re-enables handling of race events in the current goroutine. func RaceEnable() { - _g_ := getg() - _g_.raceignore-- - if _g_.raceignore == 0 { - racecall(&__tsan_go_ignore_sync_end, _g_.racectx, 0, 0, 0) + gp := getg() + gp.raceignore-- + if gp.raceignore == 0 { + racecall(&__tsan_go_ignore_sync_end, gp.racectx, 0, 0, 0) } } @@ -187,7 +187,7 @@ func raceSymbolizeCode(ctx *symbolizeCodeContext) { continue } ctx.pc = f.Entry() + uintptr(inltree[ix].parentPc) // "caller" pc - ctx.fn = cfuncnameFromNameoff(fi, inltree[ix].func_) + ctx.fn = cfuncnameFromNameOff(fi, inltree[ix].nameOff) ctx.line = uintptr(line) ctx.file = &bytes(file)[0] // assume NUL-terminated ctx.off = pc - f.Entry() @@ -233,6 +233,7 @@ func raceSymbolizeData(ctx *symbolizeDataContext) { } // Race runtime functions called via runtime·racecall. +// //go:linkname __tsan_init __tsan_init var __tsan_init byte @@ -285,6 +286,7 @@ var __tsan_go_ignore_sync_end byte var __tsan_report_count byte // Mimic what cmd/cgo would do. +// //go:cgo_import_static __tsan_init //go:cgo_import_static __tsan_fini //go:cgo_import_static __tsan_proc_create @@ -304,6 +306,7 @@ var __tsan_report_count byte //go:cgo_import_static __tsan_report_count // These are called from race_amd64.s. +// //go:cgo_import_static __tsan_read //go:cgo_import_static __tsan_read_pc //go:cgo_import_static __tsan_read_range @@ -348,6 +351,7 @@ func racecallbackthunk(uintptr) func racecall(fn *byte, arg0, arg1, arg2, arg3 uintptr) // checks if the address has shadow (i.e. heap or data/bss) +// //go:nosplit func isvalidaddr(addr unsafe.Pointer) bool { return racearenastart <= uintptr(addr) && uintptr(addr) < racearenaend || @@ -361,7 +365,7 @@ func raceinit() (gctx, pctx uintptr) { throw("raceinit: race build must use cgo") } - racecall(&__tsan_init, uintptr(unsafe.Pointer(&gctx)), uintptr(unsafe.Pointer(&pctx)), funcPC(racecallbackthunk), 0) + racecall(&__tsan_init, uintptr(unsafe.Pointer(&gctx)), uintptr(unsafe.Pointer(&pctx)), abi.FuncPCABI0(racecallbackthunk), 0) // Round data segment to page boundaries, because it's used in mmap(). start := ^uintptr(0) @@ -449,12 +453,12 @@ func racefree(p unsafe.Pointer, sz uintptr) { //go:nosplit func racegostart(pc uintptr) uintptr { - _g_ := getg() + gp := getg() var spawng *g - if _g_.m.curg != nil { - spawng = _g_.m.curg + if gp.m.curg != nil { + spawng = gp.m.curg } else { - spawng = _g_ + spawng = gp } var racectx uintptr @@ -474,8 +478,8 @@ func racectxend(racectx uintptr) { //go:nosplit func racewriterangepc(addr unsafe.Pointer, sz, callpc, pc uintptr) { - _g_ := getg() - if _g_ != _g_.m.curg { + gp := getg() + if gp != gp.m.curg { // The call is coming from manual instrumentation of Go code running on g0/gsignal. // Not interesting. return @@ -491,8 +495,8 @@ func racewriterangepc(addr unsafe.Pointer, sz, callpc, pc uintptr) { //go:nosplit func racereadrangepc(addr unsafe.Pointer, sz, callpc, pc uintptr) { - _g_ := getg() - if _g_ != _g_.m.curg { + gp := getg() + if gp != gp.m.curg { // The call is coming from manual instrumentation of Go code running on g0/gsignal. // Not interesting. return diff --git a/src/runtime/race/README b/src/runtime/race/README index 3b188a03619fc7..ad8f55fb73da6a 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -4,12 +4,13 @@ the LLVM project (https://github.com/llvm/llvm-project/tree/main/compiler-rt). To update the .syso files use golang.org/x/build/cmd/racebuild. -race_darwin_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_freebsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_linux_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_linux_ppc64le.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_netbsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. +race_darwin_amd64.syso built with LLVM 127e59048cd3d8dbb80c14b3036918c114089529 and Go 59ab6f351a370a27458755dc69f4a837e55a05a6. +race_freebsd_amd64.syso built with LLVM 127e59048cd3d8dbb80c14b3036918c114089529 and Go 59ab6f351a370a27458755dc69f4a837e55a05a6. +race_linux_amd64.syso built with LLVM 127e59048cd3d8dbb80c14b3036918c114089529 and Go 59ab6f351a370a27458755dc69f4a837e55a05a6. +race_linux_ppc64le.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_netbsd_amd64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. race_windows_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_linux_arm64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_darwin_arm64.syso built with LLVM 00da38ce2d36c07f12c287dc515d37bb7bc410e9 and Go fe70a3a0fd31441bcbb9932ecab11a6083cf2119. +race_linux_arm64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_darwin_arm64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. race_openbsd_amd64.syso built with LLVM fcf6ae2f070eba73074b6ec8d8281e54d29dbeeb and Go 8f2db14cd35bbd674cb2988a508306de6655e425. +race_linux_s390x.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go index 99052071d0089d..0dcdabe641107f 100644 --- a/src/runtime/race/output_test.go +++ b/src/runtime/race/output_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package race_test @@ -148,7 +147,7 @@ exit status 66 package main func main() { done := make(chan bool) - x := 0 + x := 0; _ = x go func() { x = 42 done <- true @@ -162,7 +161,7 @@ func main() { package main func main() { done := make(chan bool) - x := 0 + x := 0; _ = x go func() { x = 42 done <- true @@ -178,7 +177,7 @@ func main() { package main func main() { done := make(chan bool) - x := 0 + x := 0; _ = x go func() { x = 42 done <- true @@ -208,7 +207,7 @@ func TestFail(t *testing.T) { } `, []string{` ================== ---- FAIL: TestFail \(0...s\) +--- FAIL: TestFail \([0-9.]+s\) .*main_test.go:14: true .*testing.go:.*: race detected during execution of test FAIL`}}, @@ -364,7 +363,7 @@ func TestPass(t *testing.T) { } `, []string{` ================== ---- FAIL: TestFail \(0...s\) +--- FAIL: TestFail \([0-9.]+s\) .*testing.go:.*: race detected during execution of test FAIL`}}, {"mutex", "run", "", "atexit_sleep_ms=0", ` diff --git a/src/runtime/race/race.go b/src/runtime/race/race.go index 84050e87711181..8692066341c301 100644 --- a/src/runtime/race/race.go +++ b/src/runtime/race/race.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (race && linux && amd64) || (race && freebsd && amd64) || (race && netbsd && amd64) || (race && darwin && amd64) || (race && windows && amd64) || (race && linux && ppc64le) || (race && linux && arm64) || (race && darwin && arm64) || (race && openbsd && amd64) -// +build race,linux,amd64 race,freebsd,amd64 race,netbsd,amd64 race,darwin,amd64 race,windows,amd64 race,linux,ppc64le race,linux,arm64 race,darwin,arm64 race,openbsd,amd64 +//go:build (race && linux && amd64) || (race && freebsd && amd64) || (race && netbsd && amd64) || (race && darwin && amd64) || (race && windows && amd64) || (race && linux && ppc64le) || (race && linux && arm64) || (race && darwin && arm64) || (race && openbsd && amd64) || (race && linux && s390x) package race diff --git a/src/runtime/race/race_darwin_amd64.syso b/src/runtime/race/race_darwin_amd64.syso index 3f95ecc8eef955..e5d848c883c2bf 100644 Binary files a/src/runtime/race/race_darwin_amd64.syso and b/src/runtime/race/race_darwin_amd64.syso differ diff --git a/src/runtime/race/race_darwin_arm64.syso b/src/runtime/race/race_darwin_arm64.syso index f6eaa62ae3a887..4a23df2725c97f 100644 Binary files a/src/runtime/race/race_darwin_arm64.syso and b/src/runtime/race/race_darwin_arm64.syso differ diff --git a/src/runtime/race/race_freebsd_amd64.syso b/src/runtime/race/race_freebsd_amd64.syso index 2a5b46f4ce05e2..b3a438347f1b2b 100644 Binary files a/src/runtime/race/race_freebsd_amd64.syso and b/src/runtime/race/race_freebsd_amd64.syso differ diff --git a/src/runtime/race/race_linux_amd64.syso b/src/runtime/race/race_linux_amd64.syso index e00398c9645cc4..6885610f2566a5 100644 Binary files a/src/runtime/race/race_linux_amd64.syso and b/src/runtime/race/race_linux_amd64.syso differ diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso index 9dae738700e330..c8b3f48ca75103 100644 Binary files a/src/runtime/race/race_linux_arm64.syso and b/src/runtime/race/race_linux_arm64.syso differ diff --git a/src/runtime/race/race_linux_ppc64le.syso b/src/runtime/race/race_linux_ppc64le.syso index b562656d569c32..1939f29ac0aeed 100644 Binary files a/src/runtime/race/race_linux_ppc64le.syso and b/src/runtime/race/race_linux_ppc64le.syso differ diff --git a/src/runtime/race/race_linux_s390x.syso b/src/runtime/race/race_linux_s390x.syso new file mode 100644 index 00000000000000..ed4a3007da2bbf Binary files /dev/null and b/src/runtime/race/race_linux_s390x.syso differ diff --git a/src/runtime/race/race_linux_test.go b/src/runtime/race/race_linux_test.go index 9c0d48d6bda6c5..e8a2d0fd8c91c4 100644 --- a/src/runtime/race/race_linux_test.go +++ b/src/runtime/race/race_linux_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && race -// +build linux,race package race_test diff --git a/src/runtime/race/race_netbsd_amd64.syso b/src/runtime/race/race_netbsd_amd64.syso index 11af16f0468997..e6cc4bf2d880cc 100644 Binary files a/src/runtime/race/race_netbsd_amd64.syso and b/src/runtime/race/race_netbsd_amd64.syso differ diff --git a/src/runtime/race/race_test.go b/src/runtime/race/race_test.go index 8c880b857007a1..4fe61683eb02d7 100644 --- a/src/runtime/race/race_test.go +++ b/src/runtime/race/race_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race // This program is used to verify the race detector // by running the tests and parsing their output. @@ -188,7 +187,7 @@ func runTests(t *testing.T) ([]byte, error) { func TestIssue8102(t *testing.T) { // If this compiles with -race, the test passes. type S struct { - x interface{} + x any i int } c := make(chan int) diff --git a/src/runtime/race/race_unix_test.go b/src/runtime/race/race_unix_test.go index acd6e47f982764..3cf53b01871cf0 100644 --- a/src/runtime/race/race_unix_test.go +++ b/src/runtime/race/race_unix_test.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build race && (darwin || freebsd || linux) -// +build race -// +build darwin freebsd linux package race_test @@ -21,11 +19,11 @@ func TestNonGoMemory(t *testing.T) { if err != nil { t.Fatalf("failed to mmap memory: %v", err) } + defer syscall.Munmap(data) p := (*uint32)(unsafe.Pointer(&data[0])) atomic.AddUint32(p, 1) (*p)++ if *p != 2 { t.Fatalf("data[0] = %v, expect 2", *p) } - syscall.Munmap(data) } diff --git a/src/runtime/race/race_windows_test.go b/src/runtime/race/race_windows_test.go index e490d766dd05dd..143b483f97f890 100644 --- a/src/runtime/race/race_windows_test.go +++ b/src/runtime/race/race_windows_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows && race -// +build windows,race package race_test diff --git a/src/runtime/race/sched_test.go b/src/runtime/race/sched_test.go index e904ebd20d8c1c..a66860cda0f46d 100644 --- a/src/runtime/race/sched_test.go +++ b/src/runtime/race/sched_test.go @@ -3,15 +3,14 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package race_test import ( - "bytes" "fmt" "reflect" "runtime" + "strings" "testing" ) @@ -41,7 +40,7 @@ func TestRandomScheduling(t *testing.T) { } } - var buf bytes.Buffer + var buf strings.Builder for i := 0; i < N; i++ { fmt.Fprintf(&buf, "%v\n", out[i]) } diff --git a/src/runtime/race/syso_test.go b/src/runtime/race/syso_test.go index f5095737a4f632..2f1a91cab1d9c1 100644 --- a/src/runtime/race/syso_test.go +++ b/src/runtime/race/syso_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package race diff --git a/src/runtime/race/testdata/issue12664_test.go b/src/runtime/race/testdata/issue12664_test.go index c9f790edc85362..714e83d318b256 100644 --- a/src/runtime/race/testdata/issue12664_test.go +++ b/src/runtime/race/testdata/issue12664_test.go @@ -56,7 +56,7 @@ func TestRaceIssue12664_3(t *testing.T) { close(c) }() var r MyT - var i interface{} = r + var i any = r issue12664_3 = i.(MyT) <-c } diff --git a/src/runtime/race/testdata/mop_test.go b/src/runtime/race/testdata/mop_test.go index 5d25ed4bb64f5f..0d79091df46676 100644 --- a/src/runtime/race/testdata/mop_test.go +++ b/src/runtime/race/testdata/mop_test.go @@ -6,9 +6,9 @@ package race_test import ( "bytes" - "crypto/sha1" "errors" "fmt" + "hash/crc32" "io" "os" "runtime" @@ -255,7 +255,7 @@ func TestRaceCaseIssue6418(t *testing.T) { func TestRaceCaseType(t *testing.T) { var x, y int - var i interface{} = x + var i any = x c := make(chan int, 1) go func() { switch i.(type) { @@ -270,7 +270,7 @@ func TestRaceCaseType(t *testing.T) { func TestRaceCaseTypeBody(t *testing.T) { var x, y int - var i interface{} = &x + var i any = &x c := make(chan int, 1) go func() { switch i := i.(type) { @@ -288,8 +288,8 @@ func TestRaceCaseTypeIssue5890(t *testing.T) { // spurious extra instrumentation of the initial interface // value. var x, y int - m := make(map[int]map[int]interface{}) - m[0] = make(map[int]interface{}) + m := make(map[int]map[int]any) + m[0] = make(map[int]any) c := make(chan int, 1) go func() { switch i := m[0][1].(type) { @@ -758,7 +758,7 @@ func TestRaceStructFieldRW3(t *testing.T) { } func TestRaceEfaceWW(t *testing.T) { - var a, b interface{} + var a, b any ch := make(chan bool, 1) go func() { a = 1 @@ -810,7 +810,7 @@ func TestRaceEfaceConv(t *testing.T) { c := make(chan bool) v := 0 go func() { - go func(x interface{}) { + go func(x any) { }(v) c <- true }() @@ -1127,7 +1127,7 @@ func TestRaceRune(t *testing.T) { func TestRaceEmptyInterface1(t *testing.T) { c := make(chan bool) - var x interface{} + var x any go func() { x = nil c <- true @@ -1138,7 +1138,7 @@ func TestRaceEmptyInterface1(t *testing.T) { func TestRaceEmptyInterface2(t *testing.T) { c := make(chan bool) - var x interface{} + var x any go func() { x = &Point{} c <- true @@ -1579,7 +1579,7 @@ func TestRaceAddrExpr(t *testing.T) { func TestRaceTypeAssert(t *testing.T) { c := make(chan bool, 1) x := 0 - var i interface{} = x + var i any = x go func() { y := 0 i = y @@ -1896,6 +1896,14 @@ func TestRaceNestedStruct(t *testing.T) { } func TestRaceIssue5567(t *testing.T) { + testRaceRead(t, false) +} + +func TestRaceIssue51618(t *testing.T) { + testRaceRead(t, true) +} + +func testRaceRead(t *testing.T, pread bool) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) in := make(chan []byte) res := make(chan error) @@ -1914,7 +1922,11 @@ func TestRaceIssue5567(t *testing.T) { var n, total int b := make([]byte, 17) // the race is on b buffer for err == nil { - n, err = f.Read(b) + if pread { + n, err = f.ReadAt(b, int64(total)) + } else { + n, err = f.Read(b) + } total += n if n > 0 { in <- b[:n] @@ -1924,7 +1936,7 @@ func TestRaceIssue5567(t *testing.T) { err = nil } }() - h := sha1.New() + h := crc32.New(crc32.MakeTable(0x12345678)) for b := range in { h.Write(b) } diff --git a/src/runtime/race/testdata/pool_test.go b/src/runtime/race/testdata/pool_test.go index 161f4b7c23b536..a96913e3814a53 100644 --- a/src/runtime/race/testdata/pool_test.go +++ b/src/runtime/race/testdata/pool_test.go @@ -15,7 +15,7 @@ func TestRacePool(t *testing.T) { // Repeat so that at least one iteration gets reuse. for i := 0; i < 10; i++ { c := make(chan int) - p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }} + p := &sync.Pool{New: func() any { return make([]byte, 10) }} x := p.Get().([]byte) x[0] = 1 p.Put(x) @@ -31,7 +31,7 @@ func TestRacePool(t *testing.T) { func TestNoRacePool(t *testing.T) { for i := 0; i < 10; i++ { - p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }} + p := &sync.Pool{New: func() any { return make([]byte, 10) }} x := p.Get().([]byte) x[0] = 1 p.Put(x) diff --git a/src/runtime/race/timer_test.go b/src/runtime/race/timer_test.go index f11f8456a0fd4a..dd59005564273a 100644 --- a/src/runtime/race/timer_test.go +++ b/src/runtime/race/timer_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package race_test diff --git a/src/runtime/race0.go b/src/runtime/race0.go index 0e431b8103e060..f36d4387c74fe2 100644 --- a/src/runtime/race0.go +++ b/src/runtime/race0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race // Dummy race detection API, used when not built with -race. diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 8d4813eadddd5e..c679a876b8d5ea 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race #include "go_asm.h" #include "go_tls.h" @@ -46,11 +45,7 @@ // Defined as ABIInternal so as to avoid introducing a wrapper, // which would render runtime.getcallerpc ineffective. TEXT runtime·raceread(SB), NOSPLIT, $0-8 -#ifdef GOEXPERIMENT_regabiargs MOVQ AX, RARG1 -#else - MOVQ addr+0(FP), RARG1 -#endif MOVQ (SP), RARG2 // void __tsan_read(ThreadState *thr, void *addr, void *pc); MOVQ $__tsan_read(SB), AX @@ -76,11 +71,7 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // Defined as ABIInternal so as to avoid introducing a wrapper, // which would render runtime.getcallerpc ineffective. TEXT runtime·racewrite(SB), NOSPLIT, $0-8 -#ifdef GOEXPERIMENT_regabiargs MOVQ AX, RARG1 -#else - MOVQ addr+0(FP), RARG1 -#endif MOVQ (SP), RARG2 // void __tsan_write(ThreadState *thr, void *addr, void *pc); MOVQ $__tsan_write(SB), AX @@ -103,9 +94,11 @@ TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 // func runtime·racereadrange(addr, size uintptr) // Called from instrumented code. -TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 - MOVQ addr+0(FP), RARG1 - MOVQ size+8(FP), RARG2 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would render runtime.getcallerpc ineffective. +TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 + MOVQ AX, RARG1 + MOVQ BX, RARG2 MOVQ (SP), RARG3 // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVQ $__tsan_read_range(SB), AX @@ -131,13 +124,8 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 // Defined as ABIInternal so as to avoid introducing a wrapper, // which would render runtime.getcallerpc ineffective. TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 -#ifdef GOEXPERIMENT_regabiargs MOVQ AX, RARG1 MOVQ BX, RARG2 -#else - MOVQ addr+0(FP), RARG1 - MOVQ size+8(FP), RARG2 -#endif MOVQ (SP), RARG3 // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVQ $__tsan_write_range(SB), AX @@ -161,10 +149,6 @@ TEXT runtime·racewriterangepc1(SB), NOSPLIT, $0-24 // If addr (RARG1) is out of range, do nothing. // Otherwise, setup goroutine context and invoke racecall. Other arguments already set. TEXT racecalladdr<>(SB), NOSPLIT, $0-0 -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_racectx(R14), RARG0 // goroutine context // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). CMPQ RARG1, runtime·racearenastart(SB) @@ -192,10 +176,6 @@ TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 // R11 = caller's return address TEXT racefuncenter<>(SB), NOSPLIT, $0-0 MOVQ DX, BX // save function entry context (for closures) -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_racectx(R14), RARG0 // goroutine context MOVQ R11, RARG1 // void __tsan_func_enter(ThreadState *thr, void *pc); @@ -208,10 +188,6 @@ TEXT racefuncenter<>(SB), NOSPLIT, $0-0 // func runtime·racefuncexit() // Called from instrumented code. TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_racectx(R14), RARG0 // goroutine context // void __tsan_func_exit(ThreadState *thr); MOVQ $__tsan_func_exit(SB), AX @@ -370,10 +346,6 @@ racecallatomic_data: JAE racecallatomic_ignore racecallatomic_ok: // Addr is within the good range, call the atomic function. -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_racectx(R14), RARG0 // goroutine context MOVQ 8(SP), RARG1 // caller pc MOVQ (SP), RARG2 // pc @@ -385,10 +357,6 @@ racecallatomic_ignore: // An attempt to synchronize on the address would cause crash. MOVQ AX, BX // remember the original function MOVQ $__tsan_go_ignore_sync_begin(SB), AX -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_racectx(R14), RARG0 // goroutine context CALL racecall<>(SB) MOVQ BX, AX // restore the original function @@ -416,10 +384,6 @@ TEXT runtime·racecall(SB), NOSPLIT, $0-0 // Switches SP to g0 stack and calls (AX). Arguments already set. TEXT racecall<>(SB), NOSPLIT, $0-0 -#ifndef GOEXPERIMENT_regabig - get_tls(R12) - MOVQ g(R12), R14 -#endif MOVQ g_m(R14), R13 // Switch to g0 stack. MOVQ SP, R12 // callee-saved, preserved across the CALL @@ -441,9 +405,7 @@ call: // The overall effect of Go->C->Go call chain is similar to that of mcall. // RARG0 contains command code. RARG1 contains command-specific context. // See racecallback for command codes. -// Defined as ABIInternal so as to avoid introducing a wrapper, -// because its address is passed to C via funcPC. -TEXT runtime·racecallbackthunk(SB), NOSPLIT, $0-0 +TEXT runtime·racecallbackthunk(SB), NOSPLIT, $0-0 // Handle command raceGetProcCmd (0) here. // First, code below assumes that we are on curg, while raceGetProcCmd // can be executed on g0. Second, it is called frequently, so will diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s index c6d5b91edc0f3e..edbb3b12c73423 100644 --- a/src/runtime/race_arm64.s +++ b/src/runtime/race_arm64.s @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build race -// +build race #include "go_asm.h" #include "funcdata.h" #include "textflag.h" #include "tls_arm64.h" +#include "cgo/abi_arm64.h" // The following thunks allow calling the gcc-compiled race runtime directly // from Go code without going all the way through cgo. @@ -43,8 +43,10 @@ // func runtime·raceread(addr uintptr) // Called from instrumented code. -TEXT runtime·raceread(SB), NOSPLIT, $0-8 - MOVD addr+0(FP), R1 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would make caller's PC ineffective. +TEXT runtime·raceread(SB), NOSPLIT, $0-8 + MOVD R0, R1 // addr MOVD LR, R2 // void __tsan_read(ThreadState *thr, void *addr, void *pc); MOVD $__tsan_read(SB), R9 @@ -66,8 +68,10 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // func runtime·racewrite(addr uintptr) // Called from instrumented code. -TEXT runtime·racewrite(SB), NOSPLIT, $0-8 - MOVD addr+0(FP), R1 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would make caller's PC ineffective. +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 + MOVD R0, R1 // addr MOVD LR, R2 // void __tsan_write(ThreadState *thr, void *addr, void *pc); MOVD $__tsan_write(SB), R9 @@ -89,9 +93,11 @@ TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 // func runtime·racereadrange(addr, size uintptr) // Called from instrumented code. -TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 - MOVD addr+0(FP), R1 - MOVD size+8(FP), R2 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would make caller's PC ineffective. +TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 + MOVD R1, R2 // size + MOVD R0, R1 // addr MOVD LR, R3 // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVD $__tsan_read_range(SB), R9 @@ -114,9 +120,11 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 // func runtime·racewriterange(addr, size uintptr) // Called from instrumented code. -TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 - MOVD addr+0(FP), R1 - MOVD size+8(FP), R2 +// Defined as ABIInternal so as to avoid introducing a wrapper, +// which would make caller's PC ineffective. +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 + MOVD R1, R2 // size + MOVD R0, R1 // addr MOVD LR, R3 // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVD $__tsan_write_range(SB), R9 @@ -163,8 +171,8 @@ ret: // func runtime·racefuncenter(pc uintptr) // Called from instrumented code. -TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 - MOVD callpc+0(FP), R9 +TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 + MOVD R0, R9 // callpc JMP racefuncenter<>(SB) // Common code for racefuncenter @@ -180,7 +188,7 @@ TEXT racefuncenter<>(SB), NOSPLIT, $0-0 // func runtime·racefuncexit() // Called from instrumented code. -TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 +TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 load_g MOVD g_racectx(g), R0 // race context // void __tsan_func_exit(ThreadState *thr); @@ -367,12 +375,12 @@ racecallatomic_ignore: // Addr is outside the good range. // Call __tsan_go_ignore_sync_begin to ignore synchronization during the atomic op. // An attempt to synchronize on the address would cause crash. - MOVD R9, R20 // remember the original function + MOVD R9, R21 // remember the original function MOVD $__tsan_go_ignore_sync_begin(SB), R9 load_g MOVD g_racectx(g), R0 // goroutine context BL racecall<>(SB) - MOVD R20, R9 // restore the original function + MOVD R21, R9 // restore the original function // Call the atomic function. // racecall will call LLVM race code which might clobber R28 (g) load_g @@ -399,10 +407,12 @@ TEXT runtime·racecall(SB), NOSPLIT, $0-0 JMP racecall<>(SB) // Switches SP to g0 stack and calls (R9). Arguments already set. -TEXT racecall<>(SB), NOSPLIT, $0-0 +// Clobbers R19, R20. +TEXT racecall<>(SB), NOSPLIT|NOFRAME, $0-0 MOVD g_m(g), R10 // Switch to g0 stack. MOVD RSP, R19 // callee-saved, preserved across the CALL + MOVD R30, R20 // callee-saved, preserved across the CALL MOVD m_g0(R10), R11 CMP R11, g BEQ call // already on g0 @@ -411,7 +421,7 @@ TEXT racecall<>(SB), NOSPLIT, $0-0 call: BL R9 MOVD R19, RSP - RET + JMP (R20) // C->Go callback thunk that allows to call runtime·racesymbolize from C code. // Direct Go->C race call has only switched SP, finish g->g0 switch by setting correct g. @@ -441,13 +451,12 @@ TEXT runtime·racecallbackthunk(SB), NOSPLIT|NOFRAME, $0 rest: // Save callee-saved registers (Go code won't respect that). // 8(RSP) and 16(RSP) are for args passed through racecallback - SUB $112, RSP + SUB $176, RSP MOVD LR, 0(RSP) - STP (R19, R20), 24(RSP) - STP (R21, R22), 40(RSP) - STP (R23, R24), 56(RSP) - STP (R25, R26), 72(RSP) - STP (R27, g), 88(RSP) + + SAVE_R19_TO_R28(8*3) + SAVE_F8_TO_F15(8*13) + MOVD R29, (8*21)(RSP) // Set g = g0. // load_g will clobber R0, Save R0 MOVD R0, R13 @@ -470,12 +479,10 @@ rest: ret: // Restore callee-saved registers. MOVD 0(RSP), LR - LDP 24(RSP), (R19, R20) - LDP 40(RSP), (R21, R22) - LDP 56(RSP), (R23, R24) - LDP 72(RSP), (R25, R26) - LDP 88(RSP), (R27, g) - ADD $112, RSP + MOVD (8*21)(RSP), R29 + RESTORE_F8_TO_F15(8*13) + RESTORE_R19_TO_R28(8*3) + ADD $176, RSP JMP (LR) noswitch: diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s index 963e57099c9c4c..ac335b1819d5b7 100644 --- a/src/runtime/race_ppc64le.s +++ b/src/runtime/race_ppc64le.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race #include "go_asm.h" #include "go_tls.h" @@ -43,8 +42,8 @@ // func runtime·RaceRead(addr uintptr) // Called from instrumented Go code -TEXT runtime·raceread(SB), NOSPLIT, $0-8 - MOVD addr+0(FP), R4 +TEXT runtime·raceread(SB), NOSPLIT, $0-8 + MOVD R3, R4 // addr MOVD LR, R5 // caller of this? // void __tsan_read(ThreadState *thr, void *addr, void *pc); MOVD $__tsan_read(SB), R8 @@ -64,8 +63,8 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 // func runtime·RaceWrite(addr uintptr) // Called from instrumented Go code -TEXT runtime·racewrite(SB), NOSPLIT, $0-8 - MOVD addr+0(FP), R4 +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 + MOVD R3, R4 // addr MOVD LR, R5 // caller has set LR via BL inst // void __tsan_write(ThreadState *thr, void *addr, void *pc); MOVD $__tsan_write(SB), R8 @@ -85,9 +84,9 @@ TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 // func runtime·RaceReadRange(addr, size uintptr) // Called from instrumented Go code. -TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 - MOVD addr+0(FP), R4 - MOVD size+8(FP), R5 +TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 + MOVD R4, R5 // size + MOVD R3, R4 // addr MOVD LR, R6 // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVD $__tsan_read_range(SB), R8 @@ -108,9 +107,9 @@ TEXT runtime·RaceReadRange(SB), NOSPLIT, $0-16 // func runtime·RaceWriteRange(addr, size uintptr) // Called from instrumented Go code. -TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 - MOVD addr+0(FP), R4 - MOVD size+8(FP), R5 +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 + MOVD R4, R5 // size + MOVD R3, R4 // addr MOVD LR, R6 // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); MOVD $__tsan_write_range(SB), R8 @@ -411,7 +410,7 @@ racecallatomic_ignore: BL racecall<>(SB) // Call __tsan_go_ignore_sync_end. MOVD $__tsan_go_ignore_sync_end(SB), R8 - MOVD g_racectx(g), R3 // goroutine context g should sitll be good? + MOVD g_racectx(g), R3 // goroutine context g should still be good? BL racecall<>(SB) RET @@ -443,6 +442,9 @@ TEXT racecall<>(SB), NOSPLIT, $0-0 BEQ call // already on g0 MOVD (g_sched+gobuf_sp)(R10), R1 // switch R1 call: + // prepare frame for C ABI + SUB $32, R1 // create frame for callee saving LR, CR, R2 etc. + RLDCR $0, R1, $~15, R1 // align SP to 16 bytes MOVD R8, CTR // R8 = caller addr MOVD R8, R12 // expected by PPC64 ABI BL (CTR) diff --git a/src/runtime/race_s390x.s b/src/runtime/race_s390x.s new file mode 100644 index 00000000000000..beb7f830f22af1 --- /dev/null +++ b/src/runtime/race_s390x.s @@ -0,0 +1,391 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build race +// +build race + +#include "go_asm.h" +#include "funcdata.h" +#include "textflag.h" + +// The following thunks allow calling the gcc-compiled race runtime directly +// from Go code without going all the way through cgo. +// First, it's much faster (up to 50% speedup for real Go programs). +// Second, it eliminates race-related special cases from cgocall and scheduler. +// Third, in long-term it will allow to remove cyclic runtime/race dependency on cmd/go. + +// A brief recap of the s390x C calling convention. +// Arguments are passed in R2...R6, the rest is on stack. +// Callee-saved registers are: R6...R13, R15. +// Temporary registers are: R0...R5, R14. + +// When calling racecalladdr, R1 is the call target address. + +// The race ctx, ThreadState *thr below, is passed in R2 and loaded in racecalladdr. + +// func runtime·raceread(addr uintptr) +// Called from instrumented code. +TEXT runtime·raceread(SB), NOSPLIT, $0-8 + // void __tsan_read(ThreadState *thr, void *addr, void *pc); + MOVD $__tsan_read(SB), R1 + MOVD addr+0(FP), R3 + MOVD R14, R4 + JMP racecalladdr<>(SB) + +// func runtime·RaceRead(addr uintptr) +TEXT runtime·RaceRead(SB), NOSPLIT, $0-8 + // This needs to be a tail call, because raceread reads caller pc. + JMP runtime·raceread(SB) + +// func runtime·racereadpc(void *addr, void *callpc, void *pc) +TEXT runtime·racereadpc(SB), NOSPLIT, $0-24 + // void __tsan_read_pc(ThreadState *thr, void *addr, void *callpc, void *pc); + MOVD $__tsan_read_pc(SB), R1 + LMG addr+0(FP), R3, R5 + JMP racecalladdr<>(SB) + +// func runtime·racewrite(addr uintptr) +// Called from instrumented code. +TEXT runtime·racewrite(SB), NOSPLIT, $0-8 + // void __tsan_write(ThreadState *thr, void *addr, void *pc); + MOVD $__tsan_write(SB), R1 + MOVD addr+0(FP), R3 + MOVD R14, R4 + JMP racecalladdr<>(SB) + +// func runtime·RaceWrite(addr uintptr) +TEXT runtime·RaceWrite(SB), NOSPLIT, $0-8 + // This needs to be a tail call, because racewrite reads caller pc. + JMP runtime·racewrite(SB) + +// func runtime·racewritepc(void *addr, void *callpc, void *pc) +TEXT runtime·racewritepc(SB), NOSPLIT, $0-24 + // void __tsan_write_pc(ThreadState *thr, void *addr, void *callpc, void *pc); + MOVD $__tsan_write_pc(SB), R1 + LMG addr+0(FP), R3, R5 + JMP racecalladdr<>(SB) + +// func runtime·racereadrange(addr, size uintptr) +// Called from instrumented code. +TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 + // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVD $__tsan_read_range(SB), R1 + LMG addr+0(FP), R3, R4 + MOVD R14, R5 + JMP racecalladdr<>(SB) + +// func runtime·RaceReadRange(addr, size uintptr) +TEXT runtime·RaceReadRange(SB), NOSPLIT, $0-16 + // This needs to be a tail call, because racereadrange reads caller pc. + JMP runtime·racereadrange(SB) + +// func runtime·racereadrangepc1(void *addr, uintptr sz, void *pc) +TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24 + // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVD $__tsan_read_range(SB), R1 + LMG addr+0(FP), R3, R5 + // pc is an interceptor address, but TSan expects it to point to the + // middle of an interceptor (see LLVM's SCOPED_INTERCEPTOR_RAW). + ADD $2, R5 + JMP racecalladdr<>(SB) + +// func runtime·racewriterange(addr, size uintptr) +// Called from instrumented code. +TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 + // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVD $__tsan_write_range(SB), R1 + LMG addr+0(FP), R3, R4 + MOVD R14, R5 + JMP racecalladdr<>(SB) + +// func runtime·RaceWriteRange(addr, size uintptr) +TEXT runtime·RaceWriteRange(SB), NOSPLIT, $0-16 + // This needs to be a tail call, because racewriterange reads caller pc. + JMP runtime·racewriterange(SB) + +// func runtime·racewriterangepc1(void *addr, uintptr sz, void *pc) +TEXT runtime·racewriterangepc1(SB), NOSPLIT, $0-24 + // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); + MOVD $__tsan_write_range(SB), R1 + LMG addr+0(FP), R3, R5 + // pc is an interceptor address, but TSan expects it to point to the + // middle of an interceptor (see LLVM's SCOPED_INTERCEPTOR_RAW). + ADD $2, R5 + JMP racecalladdr<>(SB) + +// If R3 is out of range, do nothing. Otherwise, setup goroutine context and +// invoke racecall. Other arguments are already set. +TEXT racecalladdr<>(SB), NOSPLIT, $0-0 + MOVD runtime·racearenastart(SB), R0 + CMPUBLT R3, R0, data // Before racearena start? + MOVD runtime·racearenaend(SB), R0 + CMPUBLT R3, R0, call // Before racearena end? +data: + MOVD runtime·racedatastart(SB), R0 + CMPUBLT R3, R0, ret // Before racedata start? + MOVD runtime·racedataend(SB), R0 + CMPUBGE R3, R0, ret // At or after racedata end? +call: + MOVD g_racectx(g), R2 + JMP racecall<>(SB) +ret: + RET + +// func runtime·racefuncenter(pc uintptr) +// Called from instrumented code. +TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8 + MOVD callpc+0(FP), R3 + JMP racefuncenter<>(SB) + +// Common code for racefuncenter +// R3 = caller's return address +TEXT racefuncenter<>(SB), NOSPLIT, $0-0 + // void __tsan_func_enter(ThreadState *thr, void *pc); + MOVD $__tsan_func_enter(SB), R1 + MOVD g_racectx(g), R2 + BL racecall<>(SB) + RET + +// func runtime·racefuncexit() +// Called from instrumented code. +TEXT runtime·racefuncexit(SB), NOSPLIT, $0-0 + // void __tsan_func_exit(ThreadState *thr); + MOVD $__tsan_func_exit(SB), R1 + MOVD g_racectx(g), R2 + JMP racecall<>(SB) + +// Atomic operations for sync/atomic package. + +// Load + +TEXT sync∕atomic·LoadInt32(SB), NOSPLIT, $0-12 + GO_ARGS + MOVD $__tsan_go_atomic32_load(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·LoadInt64(SB), NOSPLIT, $0-16 + GO_ARGS + MOVD $__tsan_go_atomic64_load(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·LoadUint32(SB), NOSPLIT, $0-12 + GO_ARGS + JMP sync∕atomic·LoadInt32(SB) + +TEXT sync∕atomic·LoadUint64(SB), NOSPLIT, $0-16 + GO_ARGS + JMP sync∕atomic·LoadInt64(SB) + +TEXT sync∕atomic·LoadUintptr(SB), NOSPLIT, $0-16 + GO_ARGS + JMP sync∕atomic·LoadInt64(SB) + +TEXT sync∕atomic·LoadPointer(SB), NOSPLIT, $0-16 + GO_ARGS + JMP sync∕atomic·LoadInt64(SB) + +// Store + +TEXT sync∕atomic·StoreInt32(SB), NOSPLIT, $0-12 + GO_ARGS + MOVD $__tsan_go_atomic32_store(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·StoreInt64(SB), NOSPLIT, $0-16 + GO_ARGS + MOVD $__tsan_go_atomic64_store(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·StoreUint32(SB), NOSPLIT, $0-12 + GO_ARGS + JMP sync∕atomic·StoreInt32(SB) + +TEXT sync∕atomic·StoreUint64(SB), NOSPLIT, $0-16 + GO_ARGS + JMP sync∕atomic·StoreInt64(SB) + +TEXT sync∕atomic·StoreUintptr(SB), NOSPLIT, $0-16 + GO_ARGS + JMP sync∕atomic·StoreInt64(SB) + +// Swap + +TEXT sync∕atomic·SwapInt32(SB), NOSPLIT, $0-20 + GO_ARGS + MOVD $__tsan_go_atomic32_exchange(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·SwapInt64(SB), NOSPLIT, $0-24 + GO_ARGS + MOVD $__tsan_go_atomic64_exchange(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·SwapUint32(SB), NOSPLIT, $0-20 + GO_ARGS + JMP sync∕atomic·SwapInt32(SB) + +TEXT sync∕atomic·SwapUint64(SB), NOSPLIT, $0-24 + GO_ARGS + JMP sync∕atomic·SwapInt64(SB) + +TEXT sync∕atomic·SwapUintptr(SB), NOSPLIT, $0-24 + GO_ARGS + JMP sync∕atomic·SwapInt64(SB) + +// Add + +TEXT sync∕atomic·AddInt32(SB), NOSPLIT, $0-20 + GO_ARGS + MOVD $__tsan_go_atomic32_fetch_add(SB), R1 + BL racecallatomic<>(SB) + // TSan performed fetch_add, but Go needs add_fetch. + MOVW add+8(FP), R0 + MOVW ret+16(FP), R1 + ADD R0, R1, R0 + MOVW R0, ret+16(FP) + RET + +TEXT sync∕atomic·AddInt64(SB), NOSPLIT, $0-24 + GO_ARGS + MOVD $__tsan_go_atomic64_fetch_add(SB), R1 + BL racecallatomic<>(SB) + // TSan performed fetch_add, but Go needs add_fetch. + MOVD add+8(FP), R0 + MOVD ret+16(FP), R1 + ADD R0, R1, R0 + MOVD R0, ret+16(FP) + RET + +TEXT sync∕atomic·AddUint32(SB), NOSPLIT, $0-20 + GO_ARGS + JMP sync∕atomic·AddInt32(SB) + +TEXT sync∕atomic·AddUint64(SB), NOSPLIT, $0-24 + GO_ARGS + JMP sync∕atomic·AddInt64(SB) + +TEXT sync∕atomic·AddUintptr(SB), NOSPLIT, $0-24 + GO_ARGS + JMP sync∕atomic·AddInt64(SB) + +// CompareAndSwap + +TEXT sync∕atomic·CompareAndSwapInt32(SB), NOSPLIT, $0-17 + GO_ARGS + MOVD $__tsan_go_atomic32_compare_exchange(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·CompareAndSwapInt64(SB), NOSPLIT, $0-25 + GO_ARGS + MOVD $__tsan_go_atomic64_compare_exchange(SB), R1 + BL racecallatomic<>(SB) + RET + +TEXT sync∕atomic·CompareAndSwapUint32(SB), NOSPLIT, $0-17 + GO_ARGS + JMP sync∕atomic·CompareAndSwapInt32(SB) + +TEXT sync∕atomic·CompareAndSwapUint64(SB), NOSPLIT, $0-25 + GO_ARGS + JMP sync∕atomic·CompareAndSwapInt64(SB) + +TEXT sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-25 + GO_ARGS + JMP sync∕atomic·CompareAndSwapInt64(SB) + +// Common code for atomic operations. Calls R1. +TEXT racecallatomic<>(SB), NOSPLIT, $0 + MOVD 24(R15), R5 // Address (arg1, after 2xBL). + // If we pass an invalid pointer to the TSan runtime, it will cause a + // "fatal error: unknown caller pc". So trigger a SEGV here instead. + MOVB (R5), R0 + MOVD runtime·racearenastart(SB), R0 + CMPUBLT R5, R0, racecallatomic_data // Before racearena start? + MOVD runtime·racearenaend(SB), R0 + CMPUBLT R5, R0, racecallatomic_ok // Before racearena end? +racecallatomic_data: + MOVD runtime·racedatastart(SB), R0 + CMPUBLT R5, R0, racecallatomic_ignore // Before racedata start? + MOVD runtime·racedataend(SB), R0 + CMPUBGE R5, R0, racecallatomic_ignore // At or after racearena end? +racecallatomic_ok: + MOVD g_racectx(g), R2 // ThreadState *. + MOVD 8(R15), R3 // Caller PC. + MOVD R14, R4 // PC. + ADD $24, R15, R5 // Arguments. + // Tail call fails to restore R15, so use a normal one. + BL racecall<>(SB) + RET +racecallatomic_ignore: + // Call __tsan_go_ignore_sync_begin to ignore synchronization during + // the atomic op. An attempt to synchronize on the address would cause + // a crash. + MOVD R1, R6 // Save target function. + MOVD R14, R7 // Save PC. + MOVD $__tsan_go_ignore_sync_begin(SB), R1 + MOVD g_racectx(g), R2 // ThreadState *. + BL racecall<>(SB) + MOVD R6, R1 // Restore target function. + MOVD g_racectx(g), R2 // ThreadState *. + MOVD 8(R15), R3 // Caller PC. + MOVD R7, R4 // PC. + ADD $24, R15, R5 // Arguments. + BL racecall<>(SB) + MOVD $__tsan_go_ignore_sync_end(SB), R1 + MOVD g_racectx(g), R2 // ThreadState *. + BL racecall<>(SB) + RET + +// func runtime·racecall(void(*f)(...), ...) +// Calls C function f from race runtime and passes up to 4 arguments to it. +// The arguments are never heap-object-preserving pointers, so we pretend there +// are no arguments. +TEXT runtime·racecall(SB), NOSPLIT, $0-0 + MOVD fn+0(FP), R1 + MOVD arg0+8(FP), R2 + MOVD arg1+16(FP), R3 + MOVD arg2+24(FP), R4 + MOVD arg3+32(FP), R5 + JMP racecall<>(SB) + +// Switches SP to g0 stack and calls R1. Arguments are already set. +TEXT racecall<>(SB), NOSPLIT, $0-0 + BL runtime·save_g(SB) // Save g for callbacks. + MOVD R15, R7 // Save SP. + MOVD g_m(g), R8 // R8 = thread. + MOVD m_g0(R8), R8 // R8 = g0. + CMPBEQ R8, g, call // Already on g0? + MOVD (g_sched+gobuf_sp)(R8), R15 // Switch SP to g0. +call: SUB $160, R15 // Allocate C frame. + BL R1 // Call C code. + MOVD R7, R15 // Restore SP. + RET // Return to Go. + +// C->Go callback thunk that allows to call runtime·racesymbolize from C +// code. racecall has only switched SP, finish g->g0 switch by setting correct +// g. R2 contains command code, R3 contains command-specific context. See +// racecallback for command codes. +TEXT runtime·racecallbackthunk(SB), NOSPLIT|NOFRAME, $0 + STMG R6, R15, 48(R15) // Save non-volatile regs. + BL runtime·load_g(SB) // Saved by racecall. + CMPBNE R2, $0, rest // raceGetProcCmd? + MOVD g_m(g), R2 // R2 = thread. + MOVD m_p(R2), R2 // R2 = processor. + MVC $8, p_raceprocctx(R2), (R3) // *R3 = ThreadState *. + LMG 48(R15), R6, R15 // Restore non-volatile regs. + BR R14 // Return to C. +rest: MOVD g_m(g), R4 // R4 = current thread. + MOVD m_g0(R4), g // Switch to g0. + SUB $24, R15 // Allocate Go argument slots. + STMG R2, R3, 8(R15) // Fill Go frame. + BL runtime·racecallback(SB) // Call Go code. + LMG 72(R15), R6, R15 // Restore non-volatile regs. + BR R14 // Return to C. diff --git a/src/runtime/rand_test.go b/src/runtime/rand_test.go index 1b84c79d24fb0a..92d07ebadac95c 100644 --- a/src/runtime/rand_test.go +++ b/src/runtime/rand_test.go @@ -18,6 +18,14 @@ func BenchmarkFastrand(b *testing.B) { }) } +func BenchmarkFastrand64(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + Fastrand64() + } + }) +} + func BenchmarkFastrandHashiter(b *testing.B) { var m = make(map[int]int, 10) for i := 0; i < 10; i++ { diff --git a/src/runtime/rdebug.go b/src/runtime/rdebug.go index 1b213f19340548..7ecb2a52eec2b6 100644 --- a/src/runtime/rdebug.go +++ b/src/runtime/rdebug.go @@ -15,8 +15,8 @@ func setMaxStack(in int) (out int) { //go:linkname setPanicOnFault runtime/debug.setPanicOnFault func setPanicOnFault(new bool) (old bool) { - _g_ := getg() - old = _g_.paniconfault - _g_.paniconfault = new + gp := getg() + old = gp.paniconfault + gp.paniconfault = new return old } diff --git a/src/runtime/relax_stub.go b/src/runtime/relax_stub.go index 5b92879c20bc5c..e507702fc10e83 100644 --- a/src/runtime/relax_stub.go +++ b/src/runtime/relax_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows package runtime diff --git a/src/runtime/rt0_darwin_arm64.s b/src/runtime/rt0_darwin_arm64.s index 00403612154924..697104ac641e8e 100644 --- a/src/runtime/rt0_darwin_arm64.s +++ b/src/runtime/rt0_darwin_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_darwin(SB),NOSPLIT|NOFRAME,$0 MOVD $runtime·rt0_go(SB), R2 @@ -18,26 +19,10 @@ exit: // // Note that all currently shipping darwin/arm64 platforms require // cgo and do not support c-shared. -TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$168 +TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$152 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - MOVD g, 96(RSP) - FMOVD F8, 104(RSP) - FMOVD F9, 112(RSP) - FMOVD F10, 120(RSP) - FMOVD F11, 128(RSP) - FMOVD F12, 136(RSP) - FMOVD F13, 144(RSP) - FMOVD F14, 152(RSP) - FMOVD F15, 160(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD R0, _rt0_arm64_darwin_lib_argc<>(SB) MOVD R1, _rt0_arm64_darwin_lib_argv<>(SB) @@ -57,24 +42,8 @@ TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$168 ADD $16, RSP // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - MOVD 96(RSP), g - FMOVD 104(RSP), F8 - FMOVD 112(RSP), F9 - FMOVD 120(RSP), F10 - FMOVD 128(RSP), F11 - FMOVD 136(RSP), F12 - FMOVD 144(RSP), F13 - FMOVD 152(RSP), F14 - FMOVD 160(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) RET diff --git a/src/runtime/rt0_freebsd_arm64.s b/src/runtime/rt0_freebsd_arm64.s index a938d98262e7aa..e517ae059dc793 100644 --- a/src/runtime/rt0_freebsd_arm64.s +++ b/src/runtime/rt0_freebsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" // On FreeBSD argc/argv are passed in R0, not RSP TEXT _rt0_arm64_freebsd(SB),NOSPLIT|NOFRAME,$0 @@ -14,24 +15,8 @@ TEXT _rt0_arm64_freebsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_freebsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -63,24 +48,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_freebsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_linux_arm64.s b/src/runtime/rt0_linux_arm64.s index f48a8d61902e42..0eb8fc2f481a08 100644 --- a/src/runtime/rt0_linux_arm64.s +++ b/src/runtime/rt0_linux_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_linux(SB),NOSPLIT|NOFRAME,$0 MOVD 0(RSP), R0 // argc @@ -13,24 +14,8 @@ TEXT _rt0_arm64_linux(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_linux_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -62,24 +47,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_linux_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_linux_loong64.s b/src/runtime/rt0_linux_loong64.s new file mode 100644 index 00000000000000..b23ae7837abd72 --- /dev/null +++ b/src/runtime/rt0_linux_loong64.s @@ -0,0 +1,24 @@ +// Copyright 2022 The Go 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 _rt0_loong64_linux(SB),NOSPLIT,$0 + JMP _main<>(SB) + +TEXT _main<>(SB),NOSPLIT|NOFRAME,$0 + // In a statically linked binary, the stack contains argc, + // argv as argc string pointers followed by a NULL, envv as a + // sequence of string pointers followed by a NULL, and auxv. + // There is no TLS base pointer. + MOVW 0(R3), R4 // argc + ADDV $8, R3, R5 // argv + JMP main(SB) + +TEXT main(SB),NOSPLIT|NOFRAME,$0 + // in external linking, glibc jumps to main with argc in R4 + // and argv in R5 + + MOVV $runtime·rt0_go(SB), R19 + JMP (R19) diff --git a/src/runtime/rt0_linux_mips64x.s b/src/runtime/rt0_linux_mips64x.s index fabd8570b57970..e9328b73263199 100644 --- a/src/runtime/rt0_linux_mips64x.s +++ b/src/runtime/rt0_linux_mips64x.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le #include "textflag.h" diff --git a/src/runtime/rt0_linux_mipsx.s b/src/runtime/rt0_linux_mipsx.s index 9f5842b51a28b3..3cbb7fc37713c7 100644 --- a/src/runtime/rt0_linux_mipsx.s +++ b/src/runtime/rt0_linux_mipsx.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle #include "textflag.h" diff --git a/src/runtime/rt0_linux_ppc64.s b/src/runtime/rt0_linux_ppc64.s index 897d61052a823c..c9300a9caf602f 100644 --- a/src/runtime/rt0_linux_ppc64.s +++ b/src/runtime/rt0_linux_ppc64.s @@ -22,6 +22,7 @@ TEXT _main<>(SB),NOSPLIT,$-8 // There is no TLS base pointer. // // TODO(austin): Support ABI v1 dynamic linking entry point + XOR R0, R0 // Note, newer kernels may not always set R0 to 0. MOVD $runtime·rt0_go(SB), R12 MOVD R12, CTR MOVBZ runtime·iscgo(SB), R5 diff --git a/src/runtime/rt0_linux_ppc64le.s b/src/runtime/rt0_linux_ppc64le.s index 4f7c6e6c99f60a..66f7e7b22a411d 100644 --- a/src/runtime/rt0_linux_ppc64le.s +++ b/src/runtime/rt0_linux_ppc64le.s @@ -147,25 +147,35 @@ 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 // sequence of string pointers followed by a NULL, and auxv. - // There is no TLS base pointer. + // The TLS pointer should be initialized to 0. // - // In a dynamically linked binary, r3 contains argc, r4 - // contains argv, r5 contains envp, r6 contains auxv, and r13 + // In an ELFv2 compliant dynamically linked binary, R3 contains argc, + // R4 contains argv, R5 contains envp, R6 contains auxv, and R13 // contains the TLS pointer. // - // Figure out which case this is by looking at r4: if it's 0, - // we're statically linked; otherwise we're dynamically - // linked. - CMP R0, R4 - BNE dlink - - // Statically linked + // When loading via glibc, the first doubleword on the stack points + // to NULL a value. (that is *(uintptr)(R1) == 0). This is used to + // differentiate static vs dynamicly linked binaries. + // + // If loading with the musl loader, it doesn't follow the ELFv2 ABI. It + // passes argc/argv similar to the linux kernel, R13 (TLS) is + // initialized, and R3/R4 are undefined. + MOVD (R1), R12 + CMP R0, R12 + BEQ tls_and_argcv_in_reg + + // Arguments are passed via the stack (musl loader or a static binary) MOVD 0(R1), R3 // argc ADD $8, R1, R4 // argv + + // Did the TLS pointer get set? If so, don't change it (e.g musl). + CMP R0, R13 + BNE tls_and_argcv_in_reg + MOVD $runtime·m0+m_tls(SB), R13 // TLS ADD $0x7000, R13 -dlink: +tls_and_argcv_in_reg: BR main(SB) TEXT main(SB),NOSPLIT,$-8 diff --git a/src/runtime/rt0_linux_riscv64.s b/src/runtime/rt0_linux_riscv64.s index f31f7f75e5153e..d6b8ac85dca646 100644 --- a/src/runtime/rt0_linux_riscv64.s +++ b/src/runtime/rt0_linux_riscv64.s @@ -9,6 +9,104 @@ TEXT _rt0_riscv64_linux(SB),NOSPLIT|NOFRAME,$0 ADD $8, X2, A1 // argv JMP main(SB) +// When building with -buildmode=c-shared, this symbol is called when the shared +// library is loaded. +TEXT _rt0_riscv64_linux_lib(SB),NOSPLIT,$224 + // Preserve callee-save registers, along with X1 (LR). + MOV X1, (8*3)(X2) + MOV X8, (8*4)(X2) + MOV X9, (8*5)(X2) + MOV X18, (8*6)(X2) + MOV X19, (8*7)(X2) + MOV X20, (8*8)(X2) + MOV X21, (8*9)(X2) + MOV X22, (8*10)(X2) + MOV X23, (8*11)(X2) + MOV X24, (8*12)(X2) + MOV X25, (8*13)(X2) + MOV X26, (8*14)(X2) + MOV g, (8*15)(X2) + MOVD F8, (8*16)(X2) + MOVD F9, (8*17)(X2) + MOVD F18, (8*18)(X2) + MOVD F19, (8*19)(X2) + MOVD F20, (8*20)(X2) + MOVD F21, (8*21)(X2) + MOVD F22, (8*22)(X2) + MOVD F23, (8*23)(X2) + MOVD F24, (8*24)(X2) + MOVD F25, (8*25)(X2) + MOVD F26, (8*26)(X2) + MOVD F27, (8*27)(X2) + + // Initialize g as nil in case of using g later e.g. sigaction in cgo_sigaction.go + MOV X0, g + + MOV A0, _rt0_riscv64_linux_lib_argc<>(SB) + MOV A1, _rt0_riscv64_linux_lib_argv<>(SB) + + // Synchronous initialization. + MOV $runtime·libpreinit(SB), T0 + JALR RA, T0 + + // Create a new thread to do the runtime initialization and return. + MOV _cgo_sys_thread_create(SB), T0 + BEQZ T0, nocgo + MOV $_rt0_riscv64_linux_lib_go(SB), A0 + MOV $0, A1 + JALR RA, T0 + JMP restore + +nocgo: + MOV $0x800000, A0 // stacksize = 8192KB + MOV $_rt0_riscv64_linux_lib_go(SB), A1 + MOV A0, 8(X2) + MOV A1, 16(X2) + MOV $runtime·newosproc0(SB), T0 + JALR RA, T0 + +restore: + // Restore callee-save registers, along with X1 (LR). + MOV (8*3)(X2), X1 + MOV (8*4)(X2), X8 + MOV (8*5)(X2), X9 + MOV (8*6)(X2), X18 + MOV (8*7)(X2), X19 + MOV (8*8)(X2), X20 + MOV (8*9)(X2), X21 + MOV (8*10)(X2), X22 + MOV (8*11)(X2), X23 + MOV (8*12)(X2), X24 + MOV (8*13)(X2), X25 + MOV (8*14)(X2), X26 + MOV (8*15)(X2), g + MOVD (8*16)(X2), F8 + MOVD (8*17)(X2), F9 + MOVD (8*18)(X2), F18 + MOVD (8*19)(X2), F19 + MOVD (8*20)(X2), F20 + MOVD (8*21)(X2), F21 + MOVD (8*22)(X2), F22 + MOVD (8*23)(X2), F23 + MOVD (8*24)(X2), F24 + MOVD (8*25)(X2), F25 + MOVD (8*26)(X2), F26 + MOVD (8*27)(X2), F27 + + RET + +TEXT _rt0_riscv64_linux_lib_go(SB),NOSPLIT,$0 + MOV _rt0_riscv64_linux_lib_argc<>(SB), A0 + MOV _rt0_riscv64_linux_lib_argv<>(SB), A1 + MOV $runtime·rt0_go(SB), T0 + JALR ZERO, T0 + +DATA _rt0_riscv64_linux_lib_argc<>(SB)/8, $0 +GLOBL _rt0_riscv64_linux_lib_argc<>(SB),NOPTR, $8 +DATA _rt0_riscv64_linux_lib_argv<>(SB)/8, $0 +GLOBL _rt0_riscv64_linux_lib_argv<>(SB),NOPTR, $8 + + TEXT main(SB),NOSPLIT|NOFRAME,$0 MOV $runtime·rt0_go(SB), T0 JALR ZERO, T0 diff --git a/src/runtime/rt0_netbsd_arm64.s b/src/runtime/rt0_netbsd_arm64.s index 2f3b5a5a87a0cd..691a8e4be77eed 100644 --- a/src/runtime/rt0_netbsd_arm64.s +++ b/src/runtime/rt0_netbsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_netbsd(SB),NOSPLIT|NOFRAME,$0 MOVD 0(RSP), R0 // argc @@ -13,24 +14,8 @@ TEXT _rt0_arm64_netbsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_netbsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -62,24 +47,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_netbsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_openbsd_arm64.s b/src/runtime/rt0_openbsd_arm64.s index 722fab6129296e..49d49b34ac4f81 100644 --- a/src/runtime/rt0_openbsd_arm64.s +++ b/src/runtime/rt0_openbsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" // See comment in runtime/sys_openbsd_arm64.s re this construction. #define INVOKE_SYSCALL \ @@ -19,24 +20,8 @@ TEXT _rt0_arm64_openbsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_openbsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -68,24 +53,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_openbsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/runtime-gdb.py b/src/runtime/runtime-gdb.py index 8d96dfb6094ae0..c4462de851a24c 100644 --- a/src/runtime/runtime-gdb.py +++ b/src/runtime/runtime-gdb.py @@ -219,6 +219,9 @@ def children(self): yield ('[{0}]'.format(i), (ptr + j).dereference()) +def paramtypematch(t, pattern): + return t.code == gdb.TYPE_CODE_TYPEDEF and str(t).startswith(".param") and pattern.match(str(t.target())) + # # Register all the *Printer classes above. # @@ -228,6 +231,8 @@ def matcher(val): try: if klass.pattern.match(str(val.type)): return klass(val) + elif paramtypematch(val.type, klass.pattern): + return klass(val.cast(val.type.target())) except Exception: pass return matcher @@ -387,7 +392,7 @@ def __init__(self): def invoke(self, obj): typename = str(obj.type) for klass, fld in self.how: - if klass.pattern.match(typename): + if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern): return obj[fld] @@ -402,7 +407,7 @@ def __init__(self): def invoke(self, obj): typename = str(obj.type) for klass, fld in self.how: - if klass.pattern.match(typename): + if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern): return obj[fld] @@ -442,7 +447,7 @@ def invoke(self, _arg, _from_tty): # args = gdb.string_to_argv(arg) vp = gdb.lookup_type('void').pointer() for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): - if ptr['atomicstatus'] == G_DEAD: + if ptr['atomicstatus']['value'] == G_DEAD: continue s = ' ' if ptr['m']: @@ -450,7 +455,7 @@ def invoke(self, _arg, _from_tty): pc = ptr['sched']['pc'].cast(vp) pc = pc_to_int(pc) blk = gdb.block_for_pc(pc) - status = int(ptr['atomicstatus']) + status = int(ptr['atomicstatus']['value']) st = sts.get(status, "unknown(%d)" % status) print(s, ptr['goid'], "{0:8s}".format(st), blk.function) @@ -467,7 +472,7 @@ def find_goroutine(goid): """ vp = gdb.lookup_type('void').pointer() for ptr in SliceValue(gdb.parse_and_eval("'runtime.allgs'")): - if ptr['atomicstatus'] == G_DEAD: + if ptr['atomicstatus']['value'] == G_DEAD: continue if ptr['goid'] == goid: break @@ -475,7 +480,7 @@ def find_goroutine(goid): return None, None # Get the goroutine's saved state. pc, sp = ptr['sched']['pc'], ptr['sched']['sp'] - status = ptr['atomicstatus']&~G_SCAN + status = ptr['atomicstatus']['value']&~G_SCAN # Goroutine is not running nor in syscall, so use the info in goroutine if status != G_RUNNING and status != G_SYSCALL: return pc.cast(vp), sp.cast(vp) diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 8c76a9123c69ef..efc09c67e45349 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -40,6 +40,10 @@ func checkGdbEnvironment(t *testing.T) { if runtime.GOARCH == "mips" { t.Skip("skipping gdb tests on linux/mips; see https://golang.org/issue/25939") } + // Disable GDB tests on alpine until issue #54352 resolved. + if strings.HasSuffix(testenv.Builder(), "-alpine") { + t.Skip("skipping gdb tests on alpine; see https://golang.org/issue/54352") + } case "freebsd": t.Skip("skipping gdb tests on FreeBSD; see https://golang.org/issue/29508") case "aix": @@ -49,7 +53,7 @@ func checkGdbEnvironment(t *testing.T) { case "plan9": t.Skip("there is no gdb on Plan 9") } - if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { + if final := os.Getenv("GOROOT_FINAL"); final != "" && testenv.GOROOT(t) != final { t.Skip("gdb test can fail with GOROOT_FINAL pending") } } @@ -153,8 +157,8 @@ func TestGdbPython(t *testing.T) { } func TestGdbPythonCgo(t *testing.T) { - if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" || runtime.GOARCH == "mips64" { - testenv.SkipFlaky(t, 18784) + if strings.HasPrefix(runtime.GOARCH, "mips") { + testenv.SkipFlaky(t, 37794) } testGdbPython(t, true) } @@ -204,7 +208,7 @@ func testGdbPython(t *testing.T, cgo bool) { } args := []string{"-nx", "-q", "--batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", "-ex", "set print thread-events off", } @@ -215,7 +219,7 @@ func testGdbPython(t *testing.T, cgo bool) { // Until gold and gdb can work together, temporarily load the // python script directly. args = append(args, - "-ex", "source "+filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime-gdb.py"), + "-ex", "source "+filepath.Join(testenv.GOROOT(t), "src", "runtime", "runtime-gdb.py"), ) } else { args = append(args, @@ -267,7 +271,7 @@ func testGdbPython(t *testing.T, cgo bool) { t.Fatalf("gdb exited with error: %v", err) } - firstLine := bytes.SplitN(got, []byte("\n"), 2)[0] + firstLine, _, _ := bytes.Cut(got, []byte("\n")) if string(firstLine) != "Loading Go Runtime support." { // This can happen when using all.bash with // GOROOT_FINAL set, because the tests are run before @@ -276,7 +280,7 @@ func testGdbPython(t *testing.T, cgo bool) { cmd.Env = []string{} out, err := cmd.CombinedOutput() if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) { - t.Skipf("skipping because GOROOT=%s does not exist", runtime.GOROOT()) + t.Skipf("skipping because GOROOT=%s does not exist", testenv.GOROOT(t)) } _, file, _, _ := runtime.Caller(1) @@ -416,7 +420,7 @@ func TestGdbBacktrace(t *testing.T) { // Execute gdb commands. args := []string{"-nx", "-batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", "-ex", "break main.eee", "-ex", "run", @@ -424,9 +428,22 @@ func TestGdbBacktrace(t *testing.T) { "-ex", "continue", filepath.Join(dir, "a.exe"), } - got, err := exec.Command("gdb", args...).CombinedOutput() + got, err := testenv.RunWithTimeout(t, exec.Command("gdb", args...)) t.Logf("gdb output:\n%s", got) if err != nil { + if bytes.Contains(got, []byte("internal-error: wait returned unexpected status 0x0")) { + // GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28551 + testenv.SkipFlaky(t, 43068) + } + if bytes.Contains(got, []byte("Couldn't get registers: No such process.")) { + // GDB bug: https://sourceware.org/bugzilla/show_bug.cgi?id=9086 + testenv.SkipFlaky(t, 50838) + } + if bytes.Contains(got, []byte(" exited normally]\n")) { + // GDB bug: Sometimes the inferior exits fine, + // but then GDB hangs. + testenv.SkipFlaky(t, 37405) + } t.Fatalf("gdb exited with error: %v", err) } @@ -490,8 +507,12 @@ func TestGdbAutotmpTypes(t *testing.T) { // Execute gdb commands. args := []string{"-nx", "-batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", + // Some gdb may set scheduling-locking as "step" by default. This prevents background tasks + // (e.g GC) from completing which may result in a hang when executing the step command. + // See #49852. + "-ex", "set scheduler-locking off", "-ex", "break main.main", "-ex", "run", "-ex", "step", @@ -555,7 +576,7 @@ func TestGdbConst(t *testing.T) { // Execute gdb commands. args := []string{"-nx", "-batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", "-ex", "break main.main", "-ex", "run", @@ -618,7 +639,7 @@ func TestGdbPanic(t *testing.T) { // Execute gdb commands. args := []string{"-nx", "-batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", "-ex", "run", "-ex", "backtrace", @@ -693,7 +714,7 @@ func TestGdbInfCallstack(t *testing.T) { // Execute gdb commands. // 'setg_gcc' is the first point where we can reproduce the issue with just one 'run' command. args := []string{"-nx", "-batch", - "-iex", "add-auto-load-safe-path " + filepath.Join(runtime.GOROOT(), "src", "runtime"), + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", "-ex", "break setg_gcc", "-ex", "run", diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 33ecc260dda330..50f68a327caadc 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -12,21 +12,23 @@ import ( //go:generate go run wincallback.go //go:generate go run mkduff.go //go:generate go run mkfastlog2table.go +//go:generate go run mklockrank.go -o lockrank.go -var ticks struct { +var ticks ticksType + +type ticksType struct { lock mutex - pad uint32 // ensure 8-byte alignment of val on 386 - val uint64 + val atomic.Int64 } // Note: Called by runtime/pprof in addition to runtime code. func tickspersecond() int64 { - r := int64(atomic.Load64(&ticks.val)) + r := ticks.val.Load() if r != 0 { return r } lock(&ticks.lock) - r = int64(ticks.val) + r = ticks.val.Load() if r == 0 { t0 := nanotime() c0 := cputicks() @@ -40,7 +42,7 @@ func tickspersecond() int64 { if r == 0 { r++ } - atomic.Store64(&ticks.val, uint64(r)) + ticks.val.Store(r) } unlock(&ticks.lock) return r diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index b238da8f514e42..b0a458d18719b5 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -6,8 +6,8 @@ package runtime import ( "internal/bytealg" + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -35,12 +35,16 @@ var traceback_env uint32 // //go:nosplit func gotraceback() (level int32, all, crash bool) { - _g_ := getg() + gp := getg() t := atomic.Load(&traceback_cache) crash = t&tracebackCrash != 0 - all = _g_.m.throwing > 0 || t&tracebackAll != 0 - if _g_.m.traceback != 0 { - level = int32(_g_.m.traceback) + all = gp.m.throwing >= throwTypeUser || t&tracebackAll != 0 + if gp.m.traceback != 0 { + level = int32(gp.m.traceback) + } else if gp.m.throwing >= throwTypeRuntime { + // Always include runtime frames in runtime throws unless + // otherwise overridden by m.traceback. + level = 2 } else { level = int32(t >> tracebackShift) } @@ -53,9 +57,10 @@ var ( ) // nosplit for use in linux startup sysargs +// //go:nosplit func argv_index(argv **byte, i int32) *byte { - return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*sys.PtrSize)) + return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*goarch.PtrSize)) } func args(c int32, v **byte) { @@ -190,10 +195,10 @@ func check() { if unsafe.Sizeof(j) != 8 { throw("bad j") } - if unsafe.Sizeof(k) != sys.PtrSize { + if unsafe.Sizeof(k) != goarch.PtrSize { throw("bad k") } - if unsafe.Sizeof(l) != sys.PtrSize { + if unsafe.Sizeof(l) != goarch.PtrSize { throw("bad l") } if unsafe.Sizeof(x1) != 1 { @@ -315,6 +320,8 @@ var debug struct { schedtrace int32 tracebackancestors int32 asyncpreemptoff int32 + harddecommit int32 + adaptivestackstart int32 // debug.malloc is used as a combined debug check // in the malloc function and should be set @@ -344,12 +351,15 @@ var dbgvars = []dbgVar{ {"tracebackancestors", &debug.tracebackancestors}, {"asyncpreemptoff", &debug.asyncpreemptoff}, {"inittrace", &debug.inittrace}, + {"harddecommit", &debug.harddecommit}, + {"adaptivestackstart", &debug.adaptivestackstart}, } func parsedebugvars() { // defaults debug.cgocheck = 1 debug.invalidptr = 1 + debug.adaptivestackstart = 1 // go119 - set this to 0 to turn larger initial goroutine stacks off if GOOS == "linux" { // On Linux, MADV_FREE is faster than MADV_DONTNEED, // but doesn't affect many of the statistics that @@ -436,6 +446,7 @@ func setTraceback(level string) { // int64 division is lowered into _divv() call on 386, which does not fit into nosplit functions. // Handles overflow in a time-specific manner. // This keeps us within no-split stack limits on 32-bit processors. +// //go:nosplit func timediv(v int64, div int32, rem *int32) int32 { res := int32(0) @@ -463,18 +474,18 @@ func timediv(v int64, div int32, rem *int32) int32 { //go:nosplit func acquirem() *m { - _g_ := getg() - _g_.m.locks++ - return _g_.m + gp := getg() + gp.m.locks++ + return gp.m } //go:nosplit func releasem(mp *m) { - _g_ := getg() + gp := getg() mp.locks-- - if mp.locks == 0 && _g_.preempt { + if mp.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack - _g_.stackguard0 = stackPreempt + gp.stackguard0 = stackPreempt } } @@ -491,18 +502,21 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) { } // reflect_resolveNameOff resolves a name offset from a base pointer. +// //go:linkname reflect_resolveNameOff reflect.resolveNameOff func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer { return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes) } // reflect_resolveTypeOff resolves an *rtype offset from a base type. +// //go:linkname reflect_resolveTypeOff reflect.resolveTypeOff func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off))) } // reflect_resolveTextOff resolves a function pointer offset from a base type. +// //go:linkname reflect_resolveTextOff reflect.resolveTextOff func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { return (*_type)(rtype).textOff(textOff(off)) @@ -510,18 +524,21 @@ func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { } // reflectlite_resolveNameOff resolves a name offset from a base pointer. +// //go:linkname reflectlite_resolveNameOff internal/reflectlite.resolveNameOff func reflectlite_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer { return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes) } // reflectlite_resolveTypeOff resolves an *rtype offset from a base type. +// //go:linkname reflectlite_resolveTypeOff internal/reflectlite.resolveTypeOff func reflectlite_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off))) } // reflect_addReflectOff adds a pointer to the reflection offset lookup map. +// //go:linkname reflect_addReflectOff reflect.addReflectOff func reflect_addReflectOff(ptr unsafe.Pointer) int32 { reflectOffsLock() diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 5051ec4d3e95ee..1c620bbfbeeab8 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -5,8 +5,8 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" - "runtime/internal/sys" "unsafe" ) @@ -209,7 +209,7 @@ type eface struct { data unsafe.Pointer } -func efaceOf(ep *interface{}) *eface { +func efaceOf(ep *any) *eface { return (*eface)(unsafe.Pointer(ep)) } @@ -255,6 +255,8 @@ func efaceOf(ep *interface{}) *eface { // so I can't see them ever moving. If we did want to start moving data // in the GC, we'd need to allocate the goroutine structs from an // alternate arena. Using guintptr doesn't make that problem any worse. +// Note that pollDesc.rg, pollDesc.wg also store g in uintptr form, +// so they would need to be updated too if g's start moving. type guintptr uintptr //go:nosplit @@ -270,6 +272,7 @@ func (gp *guintptr) cas(old, new guintptr) bool { // setGNoWB performs *gp = new without a write barrier. // For times when it's impractical to use a guintptr. +// //go:nosplit //go:nowritebarrier func setGNoWB(gp **g, new *g) { @@ -289,10 +292,10 @@ func (pp *puintptr) set(p *p) { *pp = puintptr(unsafe.Pointer(p)) } // Because we do free Ms, there are some additional constrains on // muintptrs: // -// 1. Never hold an muintptr locally across a safe point. +// 1. Never hold an muintptr locally across a safe point. // -// 2. Any muintptr in the heap must be owned by the M itself so it can -// ensure it is not in use when the last true *m is released. +// 2. Any muintptr in the heap must be owned by the M itself so it can +// ensure it is not in use when the last true *m is released. type muintptr uintptr //go:nosplit @@ -303,6 +306,7 @@ func (mp *muintptr) set(m *m) { *mp = muintptr(unsafe.Pointer(m)) } // setMNoWB performs *mp = new without a write barrier. // For times when it's impractical to use an muintptr. +// //go:nosplit //go:nowritebarrier func setMNoWB(mp **m, new *m) { @@ -431,9 +435,9 @@ type g struct { // 3. By debugCallWrap to pass parameters to a new goroutine because allocating a // closure in the runtime is forbidden. param unsafe.Pointer - atomicstatus uint32 + atomicstatus atomic.Uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus - goid int64 + goid uint64 schedlink guintptr waitsince int64 // approx time when the g become blocked waitreason waitReason // if status==Gwaiting @@ -457,8 +461,8 @@ type g struct { activeStackChans bool // parkingOnChan indicates that the goroutine is about to // park on a chansend or chanrecv. Used to signal an unsafe point - // for stack shrinking. It's a boolean value, but is updated atomically. - parkingOnChan uint8 + // for stack shrinking. + parkingOnChan atomic.Bool raceignore int8 // ignore race detection events sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine @@ -483,7 +487,11 @@ type g struct { cgoCtxt []uintptr // cgo traceback context labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep - selectDone uint32 // are we participating in a select and did someone win the race? + selectDone atomic.Uint32 // are we participating in a select and did someone win the race? + + // goroutineProfiled indicates the status of this goroutine's stack for the + // current in-progress goroutine profile + goroutineProfiled goroutineProfileStateHolder // Per-G GC state @@ -505,13 +513,14 @@ const ( // tlsSlots is the number of pointer-sized slots reserved for TLS on some platforms, // like Windows. tlsSlots = 6 - tlsSize = tlsSlots * sys.PtrSize + tlsSize = tlsSlots * goarch.PtrSize ) type m struct { g0 *g // goroutine with scheduling stack morebuf gobuf // gobuf arg to morestack divmod uint32 // div/mod denominator for arm - known to liblink + _ uint32 // align next field to 8 bytes // Fields not known to debuggers. procid uint64 // for debuggers, but offset not hard-coded @@ -527,7 +536,7 @@ type m struct { oldp puintptr // the p that was attached before executing a syscall id int64 mallocing int32 - throwing int32 + throwing throwType preemptoff string // if != "", keep curg running on this m locks int32 dying int32 @@ -538,14 +547,13 @@ type m struct { printlock int8 incgo bool // m is executing a cgo call freeWait uint32 // if == 0, safe to free g0 and delete m (atomic) - fastrand [2]uint32 + fastrand uint64 needextram bool traceback uint8 - ncgocall uint64 // number of cgo calls in total - ncgo int32 // number of cgo calls currently in progress - cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily - cgoCallers *cgoCallers // cgo traceback if crashing in cgo call - doesPark bool // non-P running threads: sysmon and newmHandoff never use .park + ncgocall uint64 // number of cgo calls in total + ncgo int32 // number of cgo calls currently in progress + cgoCallersUse atomic.Uint32 // if non-zero, cgoCallers in use temporarily + cgoCallers *cgoCallers // cgo traceback if crashing in cgo call park note alllink *m // on allm schedlink muintptr @@ -562,16 +570,6 @@ type m struct { syscalltick uint32 freelink *m // on sched.freem - // mFixup is used to synchronize OS related m state - // (credentials etc) use mutex to access. To avoid deadlocks - // an atomic.Load() of used being zero in mDoFixupFn() - // guarantees fn is nil. - mFixup struct { - lock mutex - used uint32 - fn func(bool) bool - } - // these are here because they are too large to be on the stack // of low-level NOSPLIT functions. libcall libcall @@ -585,12 +583,11 @@ type m struct { // preemptGen counts the number of completed preemption // signals. This is used to detect when a preemption is - // requested, but fails. Accessed atomically. - preemptGen uint32 + // requested, but fails. + preemptGen atomic.Uint32 // Whether this is a pending preemption signal on this M. - // Accessed atomically. - signalPending uint32 + signalPending atomic.Uint32 dlogPerM @@ -613,8 +610,8 @@ type p struct { pcache pageCache raceprocctx uintptr - deferpool [5][]*_defer // pool of available defer structs of different sizes (see panic.go) - deferpoolbuf [5][32]*_defer + deferpool []*_defer // pool of available defer structs (see panic.go) + deferpoolbuf [32]*_defer // Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen. goidcache uint64 @@ -670,24 +667,23 @@ type p struct { palloc persistentAlloc // per-P to avoid mutex - _ uint32 // Alignment for atomic fields below - // The when field of the first entry on the timer heap. - // This is updated using atomic functions. // This is 0 if the timer heap is empty. - timer0When uint64 + timer0When atomic.Int64 // The earliest known nextwhen field of a timer with // timerModifiedEarlier status. Because the timer may have been // modified again, there need not be any timer with this value. - // This is updated using atomic functions. // This is 0 if there are no timerModifiedEarlier timers. - timerModifiedEarliest uint64 + timerModifiedEarliest atomic.Int64 // Per-P GC state gcAssistTime int64 // Nanoseconds in assistAlloc gcFractionalMarkTime int64 // Nanoseconds in fractional mark worker (atomic) + // limiterEvent tracks events for the GC CPU limiter. + limiterEvent limiterEvent + // gcMarkWorkerMode is the mode for the next mark worker to run in. // That is, this is used to communicate with the worker goroutine // selected for immediate execution by @@ -712,7 +708,7 @@ type p struct { // statsSeq is a counter indicating whether this P is currently // writing any stats. Its value is even when not, odd when it is. - statsSeq uint32 + statsSeq atomic.Uint32 // Lock for timers. We normally access the timers while running // on this P, but the scheduler can also do it from a different P. @@ -724,16 +720,28 @@ type p struct { timers []*timer // Number of timers in P's heap. - // Modified using atomic instructions. - numTimers uint32 + numTimers atomic.Uint32 // Number of timerDeleted timers in P's heap. - // Modified using atomic instructions. - deletedTimers uint32 + deletedTimers atomic.Uint32 // Race context used while executing timer functions. timerRaceCtx uintptr + // maxStackScanDelta accumulates the amount of stack space held by + // live goroutines (i.e. those eligible for stack scanning). + // Flushed to gcController.maxStackScan once maxStackScanSlack + // or -maxStackScanSlack is reached. + maxStackScanDelta int64 + + // gc-time statistics about current goroutines + // Note that this differs from maxStackScan in that this + // accumulates the actual stack observed to be used at GC time (hi - sp), + // not an instantaneous measure of the total stack size that might need + // to be scanned (hi - lo). + scannedStackSize uint64 // stack size of goroutines scanned by this P + scannedStacks uint64 // number of goroutines scanned by this P + // preempt is set to indicate that this P should be enter the // scheduler ASAP (regardless of what G is running on it). preempt bool @@ -743,10 +751,9 @@ type p struct { } type schedt struct { - // accessed atomically. keep at top to ensure alignment on 32-bit systems. - goidgen uint64 - lastpoll uint64 // time of last network poll, 0 if currently polling - pollUntil uint64 // time to which current poll is sleeping + goidgen atomic.Uint64 + lastpoll atomic.Int64 // time of last network poll, 0 if currently polling + pollUntil atomic.Int64 // time to which current poll is sleeping lock mutex @@ -761,11 +768,12 @@ type schedt struct { nmsys int32 // number of system m's not counted for deadlock nmfreed int64 // cumulative number of freed m's - ngsys uint32 // number of system goroutines; updated atomically + ngsys atomic.Int32 // number of system goroutines - pidle puintptr // idle p's - npidle uint32 - nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go. + pidle puintptr // idle p's + npidle atomic.Int32 + nmspinning atomic.Int32 // See "Worker thread parking/unparking" comment in proc.go. + needspinning atomic.Uint32 // See "Delicate dance" comment in proc.go. Boolean. Must hold sched.lock to set to 1. // Global runnable queue. runq gQueue @@ -795,24 +803,20 @@ type schedt struct { sudoglock mutex sudogcache *sudog - // Central pool of available defer structs of different sizes. + // Central pool of available defer structs. deferlock mutex - deferpool [5]*_defer + deferpool *_defer // freem is the list of m's waiting to be freed when their // m.exited is set. Linked through m.freelink. freem *m - gcwaiting uint32 // gc is waiting to run + gcwaiting atomic.Bool // gc is waiting to run stopwait int32 stopnote note - sysmonwait uint32 + sysmonwait atomic.Bool sysmonnote note - // While true, sysmon not ready for mFixup calls. - // Accessed atomically. - sysmonStarting uint32 - // safepointFn should be called on each P at the next GC // safepoint if p.runSafePointFn is set. safePointFn func(*p) @@ -830,13 +834,9 @@ type schedt struct { // with the rest of the runtime. sysmonlock mutex - _ uint32 // ensure timeToRun has 8-byte alignment - // timeToRun is a distribution of scheduling latencies, defined // as the sum of time a G spends in the _Grunnable state before // it transitions to _Grunning. - // - // timeToRun is protected by sched.lock. timeToRun timeHistogram } @@ -848,7 +848,7 @@ const ( _SigPanic // if the signal is from the kernel, panic _SigDefault // if the signal isn't explicitly requested, don't monitor it _SigGoExit // cause all runtime procs to exit (only used on Plan 9). - _SigSetStack // add SA_ONSTACK to libc handler + _SigSetStack // Don't explicitly install handler, but add SA_ONSTACK to existing libc handler _SigUnblock // always unblock; see blockableSig _SigIgn // _SIG_DFL action is to ignore the signal ) @@ -858,8 +858,8 @@ const ( // Keep in sync with linker (../cmd/link/internal/ld/pcln.go:/pclntab) // and with package debug/gosym and with symtab.go in package runtime. type _func struct { - entry uintptr // start pc - nameoff int32 // function name + entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart + nameOff int32 // function name, as index into moduledata.funcnametab. args int32 // in/out args size deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. @@ -873,14 +873,36 @@ type _func struct { flag funcFlag _ [1]byte // pad nfuncdata uint8 // must be last, must end on a uint32-aligned boundary + + // The end of the struct is followed immediately by two variable-length + // arrays that reference the pcdata and funcdata locations for this + // function. + + // pcdata contains the offset into moduledata.pctab for the start of + // that index's table. e.g., + // &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of + // the unsafe point table. + // + // An offset of 0 indicates that there is no table. + // + // pcdata [npcdata]uint32 + + // funcdata contains the offset past moduledata.gofunc which contains a + // pointer to that index's funcdata. e.g., + // *(moduledata.gofunc + _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is + // the argument pointer map. + // + // An offset of ^uint32(0) indicates that there is no entry. + // + // funcdata [nfuncdata]uint32 } // Pseudo-Func that is returned for PCs that occur in inlined code. // A *Func can be either a *_func or a *funcinl, and they are distinguished // by the first uintptr. type funcinl struct { - zero uintptr // set to 0 to distinguish from _func - entry uintptr // entry of the real (the "outermost") frame. + ones uint32 // set to ^0 to distinguish from _func + entry uintptr // entry of the real (the "outermost") frame name string file string line int @@ -908,7 +930,7 @@ type lfnode struct { type forcegcstate struct { lock mutex g *g - idle uint32 + idle atomic.Bool } // extendRandom extends the random numbers in r[:n] to the whole slice r. @@ -924,7 +946,7 @@ func extendRandom(r []byte, n int) { w = 16 } h := memhash(unsafe.Pointer(&r[n-w]), uintptr(nanotime()), uintptr(w)) - for i := 0; i < sys.PtrSize && n < len(r); i++ { + for i := 0; i < goarch.PtrSize && n < len(r); i++ { r[n] = byte(h) n++ h >>= 8 @@ -933,26 +955,25 @@ func extendRandom(r []byte, n int) { } // A _defer holds an entry on the list of deferred calls. -// If you add a field here, add code to clear it in freedefer and deferProcStack -// This struct must match the code in cmd/compile/internal/reflectdata/reflect.go:deferstruct -// and cmd/compile/internal/gc/ssa.go:(*state).call. +// If you add a field here, add code to clear it in deferProcStack. +// This struct must match the code in cmd/compile/internal/ssagen/ssa.go:deferstruct +// and cmd/compile/internal/ssagen/ssa.go:(*state).call. // Some defers will be allocated on the stack and some on the heap. // All defers are logically part of the stack, so write barriers to // initialize them are not required. All defers must be manually scanned, // and for heap defers, marked. type _defer struct { - siz int32 // includes both arguments and results started bool heap bool // openDefer indicates that this _defer is for a frame with open-coded // defers. We have only one defer record for the entire frame (which may // currently have 0, 1, or more defers active). openDefer bool - sp uintptr // sp at time of defer - pc uintptr // pc at time of defer - fn *funcval // can be nil for open-coded defers - _panic *_panic // panic that is running defer - link *_defer + sp uintptr // sp at time of defer + pc uintptr // pc at time of defer + fn func() // can be nil for open-coded defers + _panic *_panic // panic that is running defer + link *_defer // next defer on G; can point to either heap or stack! // If openDefer is true, the fields below record values about the stack // frame and associated function that has the open-coded defer(s). sp @@ -977,7 +998,7 @@ type _defer struct { // adjustment takes care of them. type _panic struct { argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink - arg interface{} // argument to panic + arg any // argument to panic link *_panic // link to earlier panic pc uintptr // where to return to in runtime if this panic is bypassed sp unsafe.Pointer // where to return to in runtime if this panic is bypassed @@ -986,24 +1007,10 @@ type _panic struct { goexit bool } -// stack traces -type stkframe struct { - fn funcInfo // function being run - pc uintptr // program counter within fn - continpc uintptr // program counter where execution can continue, or 0 if not - lr uintptr // program counter at caller aka link register - sp uintptr // stack pointer at pc - fp uintptr // stack pointer at caller aka frame pointer - varp uintptr // top of local variables - argp uintptr // pointer to function arguments - arglen uintptr // number of bytes at argp - argmap *bitvector // force use of this argmap -} - // ancestorInfo records details of where a goroutine was started. type ancestorInfo struct { pcs []uintptr // pcs from the stack of this goroutine - goid int64 // goroutine id of this goroutine; original goroutine possibly dead + goid uint64 // goroutine id of this goroutine; original goroutine possibly dead gopc uintptr // pc of go statement that created this goroutine } @@ -1129,7 +1136,6 @@ var ( // Set on startup in asm_{386,amd64}.s processorVersionInfo uint32 isIntel bool - lfenceBeforeRdtsc bool goarm uint8 // set by cmd/link on arm systems ) @@ -1140,5 +1146,5 @@ var ( isarchive bool // -buildmode=c-archive ) -// Must agree with internal/buildcfg.Experiment.FramePointer. +// Must agree with internal/buildcfg.FramePointerEnabled. const framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" diff --git a/src/runtime/runtime_boring.go b/src/runtime/runtime_boring.go new file mode 100644 index 00000000000000..5a98b20253181c --- /dev/null +++ b/src/runtime/runtime_boring.go @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. 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" // for go:linkname + +//go:linkname boring_runtime_arg0 crypto/internal/boring.runtime_arg0 +func boring_runtime_arg0() string { + // On Windows, argslice is not set, and it's too much work to find argv0. + if len(argslice) == 0 { + return "" + } + return argslice[0] +} + +//go:linkname fipstls_runtime_arg0 crypto/internal/boring/fipstls.runtime_arg0 +func fipstls_runtime_arg0() string { return boring_runtime_arg0() } diff --git a/src/runtime/runtime_linux_test.go b/src/runtime/runtime_linux_test.go index cd59368cb2af43..a753aeea583d66 100644 --- a/src/runtime/runtime_linux_test.go +++ b/src/runtime/runtime_linux_test.go @@ -61,3 +61,14 @@ func TestEpollctlErrorSign(t *testing.T) { t.Errorf("epollctl = %v, want %v", v, -EBADF) } } + +func TestKernelStructSize(t *testing.T) { + // Check that the Go definitions of structures exchanged with the kernel are + // the same size as what the kernel defines. + if have, want := unsafe.Sizeof(Siginfo{}), uintptr(SiginfoMaxSize); have != want { + t.Errorf("Go's siginfo struct is %d bytes long; kernel expects %d", have, want) + } + if have, want := unsafe.Sizeof(Sigevent{}), uintptr(SigeventMaxSize); have != want { + t.Errorf("Go's sigevent struct is %d bytes long; kernel expects %d", have, want) + } +} diff --git a/src/runtime/runtime_mmap_test.go b/src/runtime/runtime_mmap_test.go index f71f8afa5746ae..456f9139540057 100644 --- a/src/runtime/runtime_mmap_test.go +++ b/src/runtime/runtime_mmap_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package runtime_test diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go index 4572a25195d3e6..018a8dbaa68f54 100644 --- a/src/runtime/runtime_test.go +++ b/src/runtime/runtime_test.go @@ -6,15 +6,24 @@ package runtime_test import ( "flag" + "fmt" "io" . "runtime" "runtime/debug" + "sort" "strings" + "sync" "testing" + "time" "unsafe" ) -var flagQuick = flag.Bool("quick", false, "skip slow tests, for second run in all.bash") +// flagQuick is set by the -quick option to skip some relatively slow tests. +// This is used by the cmd/dist test runtime:cpu124. +// The cmd/dist test passes both -test.short and -quick; +// there are tests that only check testing.Short, and those tests will +// not be skipped if only -quick is used. +var flagQuick = flag.Bool("quick", false, "skip slow tests, for cmd/dist test runtime:cpu124") func init() { // We're testing the runtime, so make tracebacks show things @@ -53,8 +62,8 @@ func BenchmarkIfaceCmpNil100(b *testing.B) { } } -var efaceCmp1 interface{} -var efaceCmp2 interface{} +var efaceCmp1 any +var efaceCmp2 any func BenchmarkEfaceCmpDiff(b *testing.B) { x := 5 @@ -195,6 +204,7 @@ func TestSetPanicOnFault(t *testing.T) { // testSetPanicOnFault tests one potentially faulting address. // It deliberately constructs and uses an invalid pointer, // so mark it as nocheckptr. +// //go:nocheckptr func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) { if GOOS == "js" { @@ -355,6 +365,100 @@ func TestGoroutineProfileTrivial(t *testing.T) { } } +func BenchmarkGoroutineProfile(b *testing.B) { + run := func(fn func() bool) func(b *testing.B) { + runOne := func(b *testing.B) { + latencies := make([]time.Duration, 0, b.N) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + start := time.Now() + ok := fn() + if !ok { + b.Fatal("goroutine profile failed") + } + latencies = append(latencies, time.Now().Sub(start)) + } + b.StopTimer() + + // Sort latencies then report percentiles. + sort.Slice(latencies, func(i, j int) bool { + return latencies[i] < latencies[j] + }) + b.ReportMetric(float64(latencies[len(latencies)*50/100]), "p50-ns") + b.ReportMetric(float64(latencies[len(latencies)*90/100]), "p90-ns") + b.ReportMetric(float64(latencies[len(latencies)*99/100]), "p99-ns") + } + return func(b *testing.B) { + b.Run("idle", runOne) + + b.Run("loaded", func(b *testing.B) { + stop := applyGCLoad(b) + runOne(b) + // Make sure to stop the timer before we wait! The load created above + // is very heavy-weight and not easy to stop, so we could end up + // confusing the benchmarking framework for small b.N. + b.StopTimer() + stop() + }) + } + } + + // Measure the cost of counting goroutines + b.Run("small-nil", run(func() bool { + GoroutineProfile(nil) + return true + })) + + // Measure the cost with a small set of goroutines + n := NumGoroutine() + p := make([]StackRecord, 2*n+2*GOMAXPROCS(0)) + b.Run("small", run(func() bool { + _, ok := GoroutineProfile(p) + return ok + })) + + // Measure the cost with a large set of goroutines + ch := make(chan int) + var ready, done sync.WaitGroup + for i := 0; i < 5000; i++ { + ready.Add(1) + done.Add(1) + go func() { ready.Done(); <-ch; done.Done() }() + } + ready.Wait() + + // Count goroutines with a large allgs list + b.Run("large-nil", run(func() bool { + GoroutineProfile(nil) + return true + })) + + n = NumGoroutine() + p = make([]StackRecord, 2*n+2*GOMAXPROCS(0)) + b.Run("large", run(func() bool { + _, ok := GoroutineProfile(p) + return ok + })) + + close(ch) + done.Wait() + + // Count goroutines with a large (but unused) allgs list + b.Run("sparse-nil", run(func() bool { + GoroutineProfile(nil) + return true + })) + + // Measure the cost of a large (but unused) allgs list + n = NumGoroutine() + p = make([]StackRecord, 2*n+2*GOMAXPROCS(0)) + b.Run("sparse", run(func() bool { + _, ok := GoroutineProfile(p) + return ok + })) +} + func TestVersion(t *testing.T) { // Test that version does not contain \r or \n. vers := Version() @@ -362,3 +466,78 @@ func TestVersion(t *testing.T) { t.Fatalf("cr/nl in version: %q", vers) } } + +func TestTimediv(t *testing.T) { + for _, tc := range []struct { + num int64 + div int32 + ret int32 + rem int32 + }{ + { + num: 8, + div: 2, + ret: 4, + rem: 0, + }, + { + num: 9, + div: 2, + ret: 4, + rem: 1, + }, + { + // Used by runtime.check. + num: 12345*1000000000 + 54321, + div: 1000000000, + ret: 12345, + rem: 54321, + }, + { + num: 1<<32 - 1, + div: 2, + ret: 1<<31 - 1, // no overflow. + rem: 1, + }, + { + num: 1 << 32, + div: 2, + ret: 1<<31 - 1, // overflow. + rem: 0, + }, + { + num: 1 << 40, + div: 2, + ret: 1<<31 - 1, // overflow. + rem: 0, + }, + { + num: 1<<40 + 1, + div: 1 << 10, + ret: 1 << 30, + rem: 1, + }, + } { + name := fmt.Sprintf("%d div %d", tc.num, tc.div) + t.Run(name, func(t *testing.T) { + // Double check that the inputs make sense using + // standard 64-bit division. + ret64 := tc.num / int64(tc.div) + rem64 := tc.num % int64(tc.div) + if ret64 != int64(int32(ret64)) { + // Simulate timediv overflow value. + ret64 = 1<<31 - 1 + rem64 = 0 + } + if ret64 != int64(tc.ret) { + t.Errorf("%d / %d got ret %d rem %d want ret %d rem %d", tc.num, tc.div, ret64, rem64, tc.ret, tc.rem) + } + + var rem int32 + ret := Timediv(tc.num, tc.div, &rem) + if ret != tc.ret || rem != tc.rem { + t.Errorf("timediv %d / %d got ret %d rem %d want ret %d rem %d", tc.num, tc.div, ret, rem, tc.ret, tc.rem) + } + }) + } +} diff --git a/src/runtime/runtime_unix_test.go b/src/runtime/runtime_unix_test.go index 0251c676e6df6b..642a9462803a99 100644 --- a/src/runtime/runtime_unix_test.go +++ b/src/runtime/runtime_unix_test.go @@ -7,7 +7,6 @@ // and Close(-1) is nearly universally fast. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9 -// +build aix darwin dragonfly freebsd linux netbsd openbsd plan9 package runtime_test diff --git a/src/runtime/rwmutex.go b/src/runtime/rwmutex.go index 7713c3f1ccff9f..ede3d13599c4d5 100644 --- a/src/runtime/rwmutex.go +++ b/src/runtime/rwmutex.go @@ -23,8 +23,8 @@ type rwmutex struct { wLock mutex // serializes writers writer muintptr // pending writer waiting for completing readers - readerCount uint32 // number of pending readers - readerWait uint32 // number of departing readers + readerCount atomic.Int32 // number of pending readers + readerWait atomic.Int32 // number of departing readers } const rwmutexMaxReaders = 1 << 30 @@ -36,7 +36,7 @@ func (rw *rwmutex) rlock() { // deadlock (issue #20903). Alternatively, we could drop the P // while sleeping. acquirem() - if int32(atomic.Xadd(&rw.readerCount, 1)) < 0 { + if rw.readerCount.Add(1) < 0 { // A writer is pending. Park on the reader queue. systemstack(func() { lockWithRank(&rw.rLock, lockRankRwmutexR) @@ -60,12 +60,12 @@ func (rw *rwmutex) rlock() { // runlock undoes a single rlock call on rw. func (rw *rwmutex) runlock() { - if r := int32(atomic.Xadd(&rw.readerCount, -1)); r < 0 { + if r := rw.readerCount.Add(-1); r < 0 { if r+1 == 0 || r+1 == -rwmutexMaxReaders { throw("runlock of unlocked rwmutex") } // A writer is pending. - if atomic.Xadd(&rw.readerWait, -1) == 0 { + if rw.readerWait.Add(-1) == 0 { // The last reader unblocks the writer. lockWithRank(&rw.rLock, lockRankRwmutexR) w := rw.writer.ptr() @@ -84,10 +84,10 @@ func (rw *rwmutex) lock() { lockWithRank(&rw.wLock, lockRankRwmutexW) m := getg().m // Announce that there is a pending writer. - r := int32(atomic.Xadd(&rw.readerCount, -rwmutexMaxReaders)) + rwmutexMaxReaders + r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders // Wait for any active readers to complete. lockWithRank(&rw.rLock, lockRankRwmutexR) - if r != 0 && atomic.Xadd(&rw.readerWait, r) != 0 { + if r != 0 && rw.readerWait.Add(r) != 0 { // Wait for reader to wake us up. systemstack(func() { rw.writer.set(m) @@ -103,7 +103,7 @@ func (rw *rwmutex) lock() { // unlock unlocks rw for writing. func (rw *rwmutex) unlock() { // Announce to readers that there is no active writer. - r := int32(atomic.Xadd(&rw.readerCount, rwmutexMaxReaders)) + r := rw.readerCount.Add(rwmutexMaxReaders) if r >= rwmutexMaxReaders { throw("unlock of unlocked rwmutex") } diff --git a/src/runtime/rwmutex_test.go b/src/runtime/rwmutex_test.go index 291a32ea5ec319..ddb16aead4382b 100644 --- a/src/runtime/rwmutex_test.go +++ b/src/runtime/rwmutex_test.go @@ -17,10 +17,10 @@ import ( "testing" ) -func parallelReader(m *RWMutex, clocked chan bool, cunlock *uint32, cdone chan bool) { +func parallelReader(m *RWMutex, clocked chan bool, cunlock *atomic.Bool, cdone chan bool) { m.RLock() clocked <- true - for atomic.LoadUint32(cunlock) == 0 { + for !cunlock.Load() { } m.RUnlock() cdone <- true @@ -30,7 +30,7 @@ func doTestParallelReaders(numReaders int) { GOMAXPROCS(numReaders + 1) var m RWMutex clocked := make(chan bool, numReaders) - var cunlock uint32 + var cunlock atomic.Bool cdone := make(chan bool) for i := 0; i < numReaders; i++ { go parallelReader(&m, clocked, &cunlock, cdone) @@ -39,7 +39,7 @@ func doTestParallelReaders(numReaders int) { for i := 0; i < numReaders; i++ { <-clocked } - atomic.StoreUint32(&cunlock, 1) + cunlock.Store(true) // Wait for the goroutines to finish. for i := 0; i < numReaders; i++ { <-cdone @@ -55,6 +55,11 @@ func TestParallelRWMutexReaders(t *testing.T) { // since the goroutines can't be stopped/preempted. // Disable GC for this test (see issue #10958). defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. + GC() + doTestParallelReaders(1) doTestParallelReaders(3) doTestParallelReaders(4) diff --git a/src/runtime/select.go b/src/runtime/select.go index 06edb69f429ba0..1072465365e11c 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -7,7 +7,7 @@ package runtime // This file contains the implementation of Go select statements. import ( - "runtime/internal/atomic" + "internal/abi" "unsafe" ) @@ -22,8 +22,8 @@ type scase struct { } var ( - chansendpc = funcPC(chansend) - chanrecvpc = funcPC(chanrecv) + chansendpc = abi.FuncPCABIInternal(chansend) + chanrecvpc = abi.FuncPCABIInternal(chanrecv) ) func selectsetpc(pc *uintptr) { @@ -69,7 +69,7 @@ func selparkcommit(gp *g, _ unsafe.Pointer) bool { // Mark that it's safe for stack shrinking to occur now, // because any thread acquiring this G's stack for shrinking // is guaranteed to observe activeStackChans after this store. - atomic.Store8(&gp.parkingOnChan, 0) + gp.parkingOnChan.Store(false) // Make sure we unlock after setting activeStackChans and // unsetting parkingOnChan. The moment we unlock any of the // channel locks we risk gp getting readied by a channel operation @@ -323,13 +323,13 @@ func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, blo // to park on a channel. The window between when this G's status // changes and when we set gp.activeStackChans is not safe for // stack shrinking. - atomic.Store8(&gp.parkingOnChan, 1) + gp.parkingOnChan.Store(true) gopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1) gp.activeStackChans = false sellock(scases, lockorder) - gp.selectDone = 0 + gp.selectDone.Store(0) sg = (*sudog)(gp.param) gp.param = nil @@ -405,6 +405,13 @@ func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, blo msanwrite(cas.elem, c.elemtype.size) } } + if asanenabled { + if casi < nsends { + asanread(cas.elem, c.elemtype.size) + } else if cas.elem != nil { + asanwrite(cas.elem, c.elemtype.size) + } + } selunlock(scases, lockorder) goto retc @@ -420,6 +427,9 @@ bufrecv: if msanenabled && cas.elem != nil { msanwrite(cas.elem, c.elemtype.size) } + if asanenabled && cas.elem != nil { + asanwrite(cas.elem, c.elemtype.size) + } recvOK = true qp = chanbuf(c, c.recvx) if cas.elem != nil { @@ -443,6 +453,9 @@ bufsend: if msanenabled { msanread(cas.elem, c.elemtype.size) } + if asanenabled { + asanread(cas.elem, c.elemtype.size) + } typedmemmove(c.elemtype, chanbuf(c, c.sendx), cas.elem) c.sendx++ if c.sendx == c.dataqsiz { @@ -481,6 +494,9 @@ send: if msanenabled { msanread(cas.elem, c.elemtype.size) } + if asanenabled { + asanread(cas.elem, c.elemtype.size) + } send(c, sg, cas.elem, func() { selunlock(scases, lockorder) }, 2) if debugSelect { print("syncsend: cas0=", cas0, " c=", c, "\n") diff --git a/src/runtime/sema.go b/src/runtime/sema.go index f94c1aa89106dd..c654889caca9d0 100644 --- a/src/runtime/sema.go +++ b/src/runtime/sema.go @@ -35,22 +35,28 @@ import ( // where n is the number of distinct addresses with goroutines blocked // on them that hash to the given semaRoot. // See golang.org/issue/17953 for a program that worked badly -// before we introduced the second level of list, and test/locklinear.go -// for a test that exercises this. +// before we introduced the second level of list, and +// BenchmarkSemTable/OneAddrCollision/* for a benchmark that exercises this. type semaRoot struct { lock mutex - treap *sudog // root of balanced tree of unique waiters. - nwait uint32 // Number of waiters. Read w/o the lock. + treap *sudog // root of balanced tree of unique waiters. + nwait atomic.Uint32 // Number of waiters. Read w/o the lock. } +var semtable semTable + // Prime to not correlate with any user patterns. const semTabSize = 251 -var semtable [semTabSize]struct { +type semTable [semTabSize]struct { root semaRoot pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte } +func (t *semTable) rootFor(addr *uint32) *semaRoot { + return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root +} + //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire func sync_runtime_Semacquire(addr *uint32) { semacquire1(addr, false, semaBlockProfile, 0) @@ -113,7 +119,7 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes i // sleep // (waiter descriptor is dequeued by signaler) s := acquireSudog() - root := semroot(addr) + root := semtable.rootFor(addr) t0 := int64(0) s.releasetime = 0 s.acquiretime = 0 @@ -131,10 +137,10 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes i for { lockWithRank(&root.lock, lockRankRoot) // Add ourselves to nwait to disable "easy case" in semrelease. - atomic.Xadd(&root.nwait, 1) + root.nwait.Add(1) // Check cansemacquire to avoid missed wakeup. if cansemacquire(addr) { - atomic.Xadd(&root.nwait, -1) + root.nwait.Add(-1) unlock(&root.lock) break } @@ -157,19 +163,19 @@ func semrelease(addr *uint32) { } func semrelease1(addr *uint32, handoff bool, skipframes int) { - root := semroot(addr) + root := semtable.rootFor(addr) atomic.Xadd(addr, 1) // Easy case: no waiters? // This check must happen after the xadd, to avoid a missed wakeup // (see loop in semacquire). - if atomic.Load(&root.nwait) == 0 { + if root.nwait.Load() == 0 { return } // Harder case: search for a waiter and wake it. lockWithRank(&root.lock, lockRankRoot) - if atomic.Load(&root.nwait) == 0 { + if root.nwait.Load() == 0 { // The count is already consumed by another goroutine, // so no need to wake up another goroutine. unlock(&root.lock) @@ -177,7 +183,7 @@ func semrelease1(addr *uint32, handoff bool, skipframes int) { } s, t0 := root.dequeue(addr) if s != nil { - atomic.Xadd(&root.nwait, -1) + root.nwait.Add(-1) } unlock(&root.lock) if s != nil { // May be slow or even yield, so unlock first @@ -214,10 +220,6 @@ func semrelease1(addr *uint32, handoff bool, skipframes int) { } } -func semroot(addr *uint32) *semaRoot { - return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root -} - func cansemacquire(addr *uint32) bool { for { v := atomic.Load(addr) @@ -449,7 +451,7 @@ func (root *semaRoot) rotateRight(y *sudog) { type notifyList struct { // wait is the ticket number of the next waiter. It is atomically // incremented outside the lock. - wait uint32 + wait atomic.Uint32 // notify is the ticket number of the next waiter to be notified. It can // be read outside the lock, but is only written to with lock held. @@ -475,15 +477,17 @@ func less(a, b uint32) bool { // notifyListAdd adds the caller to a notify list such that it can receive // notifications. The caller must eventually call notifyListWait to wait for // such a notification, passing the returned ticket number. +// //go:linkname notifyListAdd sync.runtime_notifyListAdd func notifyListAdd(l *notifyList) uint32 { // This may be called concurrently, for example, when called from // sync.Cond.Wait while holding a RWMutex in read mode. - return atomic.Xadd(&l.wait, 1) - 1 + return l.wait.Add(1) - 1 } // notifyListWait waits for a notification. If one has been sent since // notifyListAdd was called, it returns immediately. Otherwise, it blocks. +// //go:linkname notifyListWait sync.runtime_notifyListWait func notifyListWait(l *notifyList, t uint32) { lockWithRank(&l.lock, lockRankNotifyList) @@ -518,11 +522,12 @@ func notifyListWait(l *notifyList, t uint32) { } // notifyListNotifyAll notifies all entries in the list. +// //go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll func notifyListNotifyAll(l *notifyList) { // Fast-path: if there are no new waiters since the last notification // we don't need to acquire the lock. - if atomic.Load(&l.wait) == atomic.Load(&l.notify) { + if l.wait.Load() == atomic.Load(&l.notify) { return } @@ -537,7 +542,7 @@ func notifyListNotifyAll(l *notifyList) { // value of wait because any previous waiters are already in the list // or will notice that they have already been notified when trying to // add themselves to the list. - atomic.Store(&l.notify, atomic.Load(&l.wait)) + atomic.Store(&l.notify, l.wait.Load()) unlock(&l.lock) // Go through the local list and ready all waiters. @@ -550,11 +555,12 @@ func notifyListNotifyAll(l *notifyList) { } // notifyListNotifyOne notifies one entry in the list. +// //go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne func notifyListNotifyOne(l *notifyList) { // Fast-path: if there are no new waiters since the last notification // we don't need to acquire the lock at all. - if atomic.Load(&l.wait) == atomic.Load(&l.notify) { + if l.wait.Load() == atomic.Load(&l.notify) { return } @@ -562,7 +568,7 @@ func notifyListNotifyOne(l *notifyList) { // Re-check under the lock if we need to do anything. t := l.notify - if t == atomic.Load(&l.wait) { + if t == l.wait.Load() { unlock(&l.lock) return } diff --git a/src/runtime/sema_test.go b/src/runtime/sema_test.go index cf3de0a1909831..9943d2ed39ce78 100644 --- a/src/runtime/sema_test.go +++ b/src/runtime/sema_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "fmt" . "runtime" "sync" "sync/atomic" @@ -101,3 +102,69 @@ func testSemaHandoff() bool { return res == 1 // did the waiter run first? } + +func BenchmarkSemTable(b *testing.B) { + for _, n := range []int{1000, 2000, 4000, 8000} { + b.Run(fmt.Sprintf("OneAddrCollision/n=%d", n), func(b *testing.B) { + tab := Escape(new(SemTable)) + u := make([]uint32, SemTableSize+1) + + b.ResetTimer() + + for j := 0; j < b.N; j++ { + // Simulate two locks colliding on the same semaRoot. + // + // Specifically enqueue all the waiters for the first lock, + // then all the waiters for the second lock. + // + // Then, dequeue all the waiters from the first lock, then + // the second. + // + // Each enqueue/dequeue operation should be O(1), because + // there are exactly 2 locks. This could be O(n) if all + // the waiters for both locks are on the same list, as it + // once was. + for i := 0; i < n; i++ { + if i < n/2 { + tab.Enqueue(&u[0]) + } else { + tab.Enqueue(&u[SemTableSize]) + } + } + for i := 0; i < n; i++ { + var ok bool + if i < n/2 { + ok = tab.Dequeue(&u[0]) + } else { + ok = tab.Dequeue(&u[SemTableSize]) + } + if !ok { + b.Fatal("failed to dequeue") + } + } + } + }) + b.Run(fmt.Sprintf("ManyAddrCollision/n=%d", n), func(b *testing.B) { + tab := Escape(new(SemTable)) + u := make([]uint32, n*SemTableSize) + + b.ResetTimer() + + for j := 0; j < b.N; j++ { + // Simulate n locks colliding on the same semaRoot. + // + // Each enqueue/dequeue operation should be O(log n), because + // each semaRoot is a tree. This could be O(n) if it was + // some simpler data structure. + for i := 0; i < n; i++ { + tab.Enqueue(&u[i*SemTableSize]) + } + for i := 0; i < n; i++ { + if !tab.Dequeue(&u[i*SemTableSize]) { + b.Fatal("failed to dequeue") + } + } + } + }) + } +} diff --git a/src/runtime/semasleep_test.go b/src/runtime/semasleep_test.go index 905e932b7d857e..726285346555d5 100644 --- a/src/runtime/semasleep_test.go +++ b/src/runtime/semasleep_test.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build !plan9 && !windows && !js -// +build !plan9,!windows,!js package runtime_test import ( + "io" "os/exec" "syscall" "testing" @@ -21,43 +21,98 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { if *flagQuick { t.Skip("-quick") } + t.Parallel() // Waits for a program to sleep for 1s. exe, err := buildTestProg(t, "testprog") if err != nil { t.Fatal(err) } - start := time.Now() cmd := exec.Command(exe, "After1") + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe: %v", err) + } + beforeStart := time.Now() if err := cmd.Start(); err != nil { t.Fatalf("Failed to start command: %v", err) } + + waiting := false doneCh := make(chan error, 1) + t.Cleanup(func() { + cmd.Process.Kill() + if waiting { + <-doneCh + } else { + cmd.Wait() + } + }) + + // Wait for After1 to close its stdout so that we know the runtime's SIGIO + // handler is registered. + b, err := io.ReadAll(stdout) + if len(b) > 0 { + t.Logf("read from testprog stdout: %s", b) + } + if err != nil { + t.Fatalf("error reading from testprog: %v", err) + } + + // Wait for child exit. + // + // Note that we must do this after waiting for the write/child end of + // stdout to close. Wait closes the read/parent end of stdout, so + // starting this goroutine prior to io.ReadAll introduces a race + // condition where ReadAll may get fs.ErrClosed if the child exits too + // quickly. + waiting = true go func() { doneCh <- cmd.Wait() + close(doneCh) }() + // Wait for an arbitrary timeout longer than one second. The subprocess itself + // attempts to sleep for one second, but if the machine running the test is + // heavily loaded that subprocess may not schedule very quickly even if the + // bug remains fixed. (This is fine, because if the bug really is unfixed we + // can keep the process hung indefinitely, as long as we signal it often + // enough.) + timeout := 10 * time.Second + + // The subprocess begins sleeping for 1s after it writes to stdout, so measure + // the timeout from here (not from when we started creating the process). + // That should reduce noise from process startup overhead. + ready := time.Now() + // With the repro running, we can continuously send to it - // a non-terminal signal such as SIGIO, to spuriously - // wakeup pthread_cond_timedwait_relative_np. - unfixedTimer := time.NewTimer(2 * time.Second) + // a signal that the runtime considers non-terminal, + // such as SIGIO, to spuriously wake up + // pthread_cond_timedwait_relative_np. + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() for { select { - case <-time.After(200 * time.Millisecond): + case now := <-ticker.C: + if now.Sub(ready) > timeout { + t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") + // Send SIGQUIT to get a goroutine dump. + // Stop sending SIGIO so that the program can clean up and actually terminate. + cmd.Process.Signal(syscall.SIGQUIT) + return + } + // Send the pesky signal that toggles spinning // indefinitely if #27520 is not fixed. cmd.Process.Signal(syscall.SIGIO) - case <-unfixedTimer.C: - t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") - cmd.Process.Signal(syscall.SIGKILL) - return - case err := <-doneCh: if err != nil { t.Fatalf("The program returned but unfortunately with an error: %v", err) } - if time.Since(start) < 100*time.Millisecond { + if time.Since(beforeStart) < 1*time.Second { + // The program was supposed to sleep for a full (monotonic) second; + // it should not return before that has elapsed. t.Fatalf("The program stopped too quickly.") } return diff --git a/src/runtime/sigaction.go b/src/runtime/sigaction.go index 30050efcc78a02..05f44f65dbcd56 100644 --- a/src/runtime/sigaction.go +++ b/src/runtime/sigaction.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux && !amd64 && !arm64 && !ppc64le) || (freebsd && !amd64) -// +build linux,!amd64,!arm64,!ppc64le freebsd,!amd64 package runtime diff --git a/src/runtime/signal_386.go b/src/runtime/signal_386.go index 5824eaddb5630d..aa66032caa361a 100644 --- a/src/runtime/signal_386.go +++ b/src/runtime/signal_386.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -42,17 +42,17 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { sp := uintptr(c.esp()) if shouldPushSigpanic(gp, pc, *(*uintptr)(unsafe.Pointer(sp))) { - c.pushCall(funcPC(sigpanic), pc) + c.pushCall(abi.FuncPCABIInternal(sigpanic), pc) } else { // Not safe to push the call. Just clobber the frame. - c.set_eip(uint32(funcPC(sigpanic))) + c.set_eip(uint32(abi.FuncPCABIInternal(sigpanic))) } } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { // Make it look like we called target at resumePC. sp := uintptr(c.esp()) - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = resumePC c.set_esp(uint32(sp)) c.set_eip(uint32(targetPC)) diff --git a/src/runtime/signal_aix_ppc64.go b/src/runtime/signal_aix_ppc64.go index a0becd431ebdf6..c6cb91a0a25731 100644 --- a/src/runtime/signal_aix_ppc64.go +++ b/src/runtime/signal_aix_ppc64.go @@ -3,12 +3,11 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -82,5 +81,5 @@ func (c *sigctxt) set_link(x uint64) { c.regs().lr = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_amd64.go b/src/runtime/signal_amd64.go index e45fbb4a87a39f..8ade2088362e6c 100644 --- a/src/runtime/signal_amd64.go +++ b/src/runtime/signal_amd64.go @@ -3,13 +3,12 @@ // license that can be found in the LICENSE file. //go:build amd64 && (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) -// +build amd64 -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -41,9 +40,10 @@ func dumpregs(c *sigctxt) { //go:nowritebarrierrec func (c *sigctxt) sigpc() uintptr { return uintptr(c.rip()) } -func (c *sigctxt) sigsp() uintptr { return uintptr(c.rsp()) } -func (c *sigctxt) siglr() uintptr { return 0 } -func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) } +func (c *sigctxt) setsigpc(x uint64) { c.set_rip(x) } +func (c *sigctxt) sigsp() uintptr { return uintptr(c.rsp()) } +func (c *sigctxt) siglr() uintptr { return 0 } +func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) } // preparePanic sets up the stack to look like a call to sigpanic. func (c *sigctxt) preparePanic(sig uint32, gp *g) { @@ -70,17 +70,17 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // Go special registers. We inject sigpanic0 (instead of sigpanic), // which takes care of that. if shouldPushSigpanic(gp, pc, *(*uintptr)(unsafe.Pointer(sp))) { - c.pushCall(funcPC(sigpanic0), pc) + c.pushCall(abi.FuncPCABI0(sigpanic0), pc) } else { // Not safe to push the call. Just clobber the frame. - c.set_rip(uint64(funcPC(sigpanic0))) + c.set_rip(uint64(abi.FuncPCABI0(sigpanic0))) } } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { // Make it look like we called target at resumePC. sp := uintptr(c.rsp()) - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = resumePC c.set_rsp(uint64(sp)) c.set_rip(uint64(targetPC)) diff --git a/src/runtime/signal_arm.go b/src/runtime/signal_arm.go index 4d9c6224a29d1e..fff302f4d4b92b 100644 --- a/src/runtime/signal_arm.go +++ b/src/runtime/signal_arm.go @@ -3,11 +3,13 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) func dumpregs(c *sigctxt) { print("trap ", hex(c.trap()), "\n") @@ -61,7 +63,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_r10(uint32(uintptr(unsafe.Pointer(gp)))) - c.set_pc(uint32(funcPC(sigpanic))) + c.set_pc(uint32(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { diff --git a/src/runtime/signal_arm64.go b/src/runtime/signal_arm64.go index f04750084f4b01..c8b87817b435ab 100644 --- a/src/runtime/signal_arm64.go +++ b/src/runtime/signal_arm64.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux || netbsd || openbsd -// +build darwin freebsd linux netbsd openbsd package runtime import ( + "internal/abi" "runtime/internal/sys" "unsafe" ) @@ -53,8 +53,9 @@ func dumpregs(c *sigctxt) { //go:nowritebarrierrec func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) } -func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) } -func (c *sigctxt) siglr() uintptr { return uintptr(c.lr()) } +func (c *sigctxt) setsigpc(x uint64) { c.set_pc(x) } +func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) } +func (c *sigctxt) siglr() uintptr { return uintptr(c.lr()) } // preparePanic sets up the stack to look like a call to sigpanic. func (c *sigctxt) preparePanic(sig uint32, gp *g) { @@ -77,7 +78,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_r28(uint64(uintptr(unsafe.Pointer(gp)))) - c.set_pc(uint64(funcPC(sigpanic))) + c.set_pc(uint64(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { diff --git a/src/runtime/signal_darwin_amd64.go b/src/runtime/signal_darwin_amd64.go index abc212ad5151e2..20544d8489b8e6 100644 --- a/src/runtime/signal_darwin_amd64.go +++ b/src/runtime/signal_darwin_amd64.go @@ -84,6 +84,10 @@ func (c *sigctxt) fixsigcode(sig uint32) { // in real life, people will probably search for it and find this code. // There are no Google hits for b01dfacedebac1e or 0xb01dfacedebac1e // as I type this comment. + // + // Note: if this code is removed, please consider + // enabling TestSignalForwardingGo for darwin-amd64 in + // misc/cgo/testcarchive/carchive_test.go. if c.sigcode() == _SI_USER { c.set_sigcode(_SI_USER + 1) c.set_sigaddr(0xb01dfacedebac1e) diff --git a/src/runtime/signal_linux_386.go b/src/runtime/signal_linux_386.go index 13d9df407102e5..321518c18e3f50 100644 --- a/src/runtime/signal_linux_386.go +++ b/src/runtime/signal_linux_386.go @@ -5,7 +5,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -42,5 +42,5 @@ func (c *sigctxt) set_eip(x uint32) { c.regs().eip = x } func (c *sigctxt) set_esp(x uint32) { c.regs().esp = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint32) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_amd64.go b/src/runtime/signal_linux_amd64.go index 210e8967e5da29..573b11839740de 100644 --- a/src/runtime/signal_linux_amd64.go +++ b/src/runtime/signal_linux_amd64.go @@ -5,7 +5,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -52,5 +52,5 @@ func (c *sigctxt) set_rip(x uint64) { c.regs().rip = x } func (c *sigctxt) set_rsp(x uint64) { c.regs().rsp = x } func (c *sigctxt) set_sigcode(x uint64) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_arm.go b/src/runtime/signal_linux_arm.go index 876b505917394f..eb107d68d1b181 100644 --- a/src/runtime/signal_linux_arm.go +++ b/src/runtime/signal_linux_arm.go @@ -5,7 +5,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -54,5 +54,5 @@ func (c *sigctxt) set_r10(x uint32) { c.regs().r10 = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint32) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_arm64.go b/src/runtime/signal_linux_arm64.go index 2075f253d7d0f8..4ccc0307923546 100644 --- a/src/runtime/signal_linux_arm64.go +++ b/src/runtime/signal_linux_arm64.go @@ -5,7 +5,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -67,5 +67,5 @@ func (c *sigctxt) set_lr(x uint64) { c.regs().regs[30] = x } func (c *sigctxt) set_r28(x uint64) { c.regs().regs[28] = x } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_loong64.go b/src/runtime/signal_linux_loong64.go new file mode 100644 index 00000000000000..51aaacbbbdd455 --- /dev/null +++ b/src/runtime/signal_linux_loong64.go @@ -0,0 +1,75 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 + +package runtime + +import ( + "internal/goarch" + "unsafe" +) + +type sigctxt struct { + info *siginfo + ctxt unsafe.Pointer +} + +//go:nosplit +//go:nowritebarrierrec +func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext } + +func (c *sigctxt) r0() uint64 { return c.regs().sc_regs[0] } +func (c *sigctxt) r1() uint64 { return c.regs().sc_regs[1] } +func (c *sigctxt) r2() uint64 { return c.regs().sc_regs[2] } +func (c *sigctxt) r3() uint64 { return c.regs().sc_regs[3] } +func (c *sigctxt) r4() uint64 { return c.regs().sc_regs[4] } +func (c *sigctxt) r5() uint64 { return c.regs().sc_regs[5] } +func (c *sigctxt) r6() uint64 { return c.regs().sc_regs[6] } +func (c *sigctxt) r7() uint64 { return c.regs().sc_regs[7] } +func (c *sigctxt) r8() uint64 { return c.regs().sc_regs[8] } +func (c *sigctxt) r9() uint64 { return c.regs().sc_regs[9] } +func (c *sigctxt) r10() uint64 { return c.regs().sc_regs[10] } +func (c *sigctxt) r11() uint64 { return c.regs().sc_regs[11] } +func (c *sigctxt) r12() uint64 { return c.regs().sc_regs[12] } +func (c *sigctxt) r13() uint64 { return c.regs().sc_regs[13] } +func (c *sigctxt) r14() uint64 { return c.regs().sc_regs[14] } +func (c *sigctxt) r15() uint64 { return c.regs().sc_regs[15] } +func (c *sigctxt) r16() uint64 { return c.regs().sc_regs[16] } +func (c *sigctxt) r17() uint64 { return c.regs().sc_regs[17] } +func (c *sigctxt) r18() uint64 { return c.regs().sc_regs[18] } +func (c *sigctxt) r19() uint64 { return c.regs().sc_regs[19] } +func (c *sigctxt) r20() uint64 { return c.regs().sc_regs[20] } +func (c *sigctxt) r21() uint64 { return c.regs().sc_regs[21] } +func (c *sigctxt) r22() uint64 { return c.regs().sc_regs[22] } +func (c *sigctxt) r23() uint64 { return c.regs().sc_regs[23] } +func (c *sigctxt) r24() uint64 { return c.regs().sc_regs[24] } +func (c *sigctxt) r25() uint64 { return c.regs().sc_regs[25] } +func (c *sigctxt) r26() uint64 { return c.regs().sc_regs[26] } +func (c *sigctxt) r27() uint64 { return c.regs().sc_regs[27] } +func (c *sigctxt) r28() uint64 { return c.regs().sc_regs[28] } +func (c *sigctxt) r29() uint64 { return c.regs().sc_regs[29] } +func (c *sigctxt) r30() uint64 { return c.regs().sc_regs[30] } +func (c *sigctxt) r31() uint64 { return c.regs().sc_regs[31] } +func (c *sigctxt) sp() uint64 { return c.regs().sc_regs[3] } + +//go:nosplit +//go:nowritebarrierrec +func (c *sigctxt) pc() uint64 { return c.regs().sc_pc } + +func (c *sigctxt) link() uint64 { return c.regs().sc_regs[1] } + +func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) } +func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr } + +func (c *sigctxt) set_r31(x uint64) { c.regs().sc_regs[31] = x } +func (c *sigctxt) set_r22(x uint64) { c.regs().sc_regs[22] = x } +func (c *sigctxt) set_pc(x uint64) { c.regs().sc_pc = x } +func (c *sigctxt) set_sp(x uint64) { c.regs().sc_regs[3] = x } +func (c *sigctxt) set_link(x uint64) { c.regs().sc_regs[1] = x } + +func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } +func (c *sigctxt) set_sigaddr(x uint64) { + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) +} diff --git a/src/runtime/signal_linux_mips64x.go b/src/runtime/signal_linux_mips64x.go index f0a75ac3ea5715..9c2a2860016603 100644 --- a/src/runtime/signal_linux_mips64x.go +++ b/src/runtime/signal_linux_mips64x.go @@ -3,13 +3,11 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -75,5 +73,5 @@ func (c *sigctxt) set_link(x uint64) { c.regs().sc_regs[31] = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_mipsx.go b/src/runtime/signal_linux_mipsx.go index f3969c5aac4a74..f11bfc9b3a2c79 100644 --- a/src/runtime/signal_linux_mipsx.go +++ b/src/runtime/signal_linux_mipsx.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package runtime diff --git a/src/runtime/signal_linux_ppc64x.go b/src/runtime/signal_linux_ppc64x.go index d9d3e55ec20009..31754289ece367 100644 --- a/src/runtime/signal_linux_ppc64x.go +++ b/src/runtime/signal_linux_ppc64x.go @@ -3,13 +3,11 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -79,5 +77,5 @@ func (c *sigctxt) set_link(x uint64) { c.regs().link = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_riscv64.go b/src/runtime/signal_linux_riscv64.go index 9f68e5c548fb45..b26450dbfac5cd 100644 --- a/src/runtime/signal_linux_riscv64.go +++ b/src/runtime/signal_linux_riscv64.go @@ -5,7 +5,7 @@ package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -64,5 +64,5 @@ func (c *sigctxt) set_gp(x uint64) { c.regs().sc_regs.gp = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } diff --git a/src/runtime/signal_linux_s390x.go b/src/runtime/signal_linux_s390x.go index 12d5c315931c89..18c3b115efc87f 100644 --- a/src/runtime/signal_linux_s390x.go +++ b/src/runtime/signal_linux_s390x.go @@ -5,6 +5,8 @@ package runtime import ( + "internal/abi" + "internal/goarch" "runtime/internal/sys" "unsafe" ) @@ -53,7 +55,7 @@ func (c *sigctxt) set_sp(x uint64) { c.regs().gregs[15] = x } func (c *sigctxt) set_pc(x uint64) { c.regs().psw_addr = x } func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } func (c *sigctxt) set_sigaddr(x uint64) { - *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) + *(*uintptr)(add(unsafe.Pointer(c.info), 2*goarch.PtrSize)) = uintptr(x) } func dumpregs(c *sigctxt) { @@ -107,7 +109,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_r0(0) c.set_r13(uint64(uintptr(unsafe.Pointer(gp)))) - c.set_pc(uint64(funcPC(sigpanic))) + c.set_pc(uint64(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { diff --git a/src/runtime/signal_loong64.go b/src/runtime/signal_loong64.go new file mode 100644 index 00000000000000..26717a6e598187 --- /dev/null +++ b/src/runtime/signal_loong64.go @@ -0,0 +1,98 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 + +package runtime + +import ( + "internal/abi" + "internal/goarch" + "unsafe" +) + +func dumpregs(c *sigctxt) { + print("r0 ", hex(c.r0()), "\t") + print("r1 ", hex(c.r1()), "\n") + print("r2 ", hex(c.r2()), "\t") + print("r3 ", hex(c.r3()), "\n") + print("r4 ", hex(c.r4()), "\t") + print("r5 ", hex(c.r5()), "\n") + print("r6 ", hex(c.r6()), "\t") + print("r7 ", hex(c.r7()), "\n") + print("r8 ", hex(c.r8()), "\t") + print("r9 ", hex(c.r9()), "\n") + print("r10 ", hex(c.r10()), "\t") + print("r11 ", hex(c.r11()), "\n") + print("r12 ", hex(c.r12()), "\t") + print("r13 ", hex(c.r13()), "\n") + print("r14 ", hex(c.r14()), "\t") + print("r15 ", hex(c.r15()), "\n") + print("r16 ", hex(c.r16()), "\t") + print("r17 ", hex(c.r17()), "\n") + print("r18 ", hex(c.r18()), "\t") + print("r19 ", hex(c.r19()), "\n") + print("r20 ", hex(c.r20()), "\t") + print("r21 ", hex(c.r21()), "\n") + print("r22 ", hex(c.r22()), "\t") + print("r23 ", hex(c.r23()), "\n") + print("r24 ", hex(c.r24()), "\t") + print("r25 ", hex(c.r25()), "\n") + print("r26 ", hex(c.r26()), "\t") + print("r27 ", hex(c.r27()), "\n") + print("r28 ", hex(c.r28()), "\t") + print("r29 ", hex(c.r29()), "\n") + print("r30 ", hex(c.r30()), "\t") + print("r31 ", hex(c.r31()), "\n") + print("pc ", hex(c.pc()), "\t") + print("link ", hex(c.link()), "\n") +} + +//go:nosplit +//go:nowritebarrierrec +func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) } + +func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) } +func (c *sigctxt) siglr() uintptr { return uintptr(c.link()) } +func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) } + +// preparePanic sets up the stack to look like a call to sigpanic. +func (c *sigctxt) preparePanic(sig uint32, gp *g) { + // We arrange link, and pc to pretend the panicking + // function calls sigpanic directly. + // Always save LINK to stack so that panics in leaf + // functions are correctly handled. This smashes + // the stack frame but we're not going back there + // anyway. + sp := c.sp() - goarch.PtrSize + c.set_sp(sp) + *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link() + + pc := gp.sigpc + + if shouldPushSigpanic(gp, pc, uintptr(c.link())) { + // Make it look the like faulting PC called sigpanic. + c.set_link(uint64(pc)) + } + + // In case we are panicking from external C code + sigpanicPC := uint64(abi.FuncPCABIInternal(sigpanic)) + c.set_r31(sigpanicPC >> 32 << 32) // RSB register + c.set_r22(uint64(uintptr(unsafe.Pointer(gp)))) + c.set_pc(sigpanicPC) +} + +func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { + // Push the LR to stack, as we'll clobber it in order to + // push the call. The function being pushed is responsible + // for restoring the LR and setting the SP back. + // This extra slot is known to gentraceback. + sp := c.sp() - 8 + c.set_sp(sp) + *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link() + // Set up PC and LR to pretend the function being signaled + // calls targetPC at resumePC. + c.set_link(uint64(resumePC)) + c.set_pc(uint64(targetPC)) +} diff --git a/src/runtime/signal_mips64x.go b/src/runtime/signal_mips64x.go index 1616b570278a0c..cee1bf7a1b23dc 100644 --- a/src/runtime/signal_mips64x.go +++ b/src/runtime/signal_mips64x.go @@ -3,13 +3,12 @@ // license that can be found in the LICENSE file. //go:build (linux || openbsd) && (mips64 || mips64le) -// +build linux openbsd -// +build mips64 mips64le package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -68,7 +67,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // functions are correctly handled. This smashes // the stack frame but we're not going back there // anyway. - sp := c.sp() - sys.PtrSize + sp := c.sp() - goarch.PtrSize c.set_sp(sp) *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link() @@ -80,7 +79,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { } // In case we are panicking from external C code - sigpanicPC := uint64(funcPC(sigpanic)) + sigpanicPC := uint64(abi.FuncPCABIInternal(sigpanic)) c.set_r28(sigpanicPC >> 32 << 32) // RSB register c.set_r30(uint64(uintptr(unsafe.Pointer(gp)))) c.set_pc(sigpanicPC) diff --git a/src/runtime/signal_mipsx.go b/src/runtime/signal_mipsx.go index dcc7f1e9dd5e03..ba92655152e84b 100644 --- a/src/runtime/signal_mipsx.go +++ b/src/runtime/signal_mipsx.go @@ -3,12 +3,11 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package runtime import ( + "internal/abi" "runtime/internal/sys" "unsafe" ) @@ -78,7 +77,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_r30(uint32(uintptr(unsafe.Pointer(gp)))) - c.set_pc(uint32(funcPC(sigpanic))) + c.set_pc(uint32(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { diff --git a/src/runtime/signal_ppc64x.go b/src/runtime/signal_ppc64x.go index f2225da9a1771d..bdd354026cb0ad 100644 --- a/src/runtime/signal_ppc64x.go +++ b/src/runtime/signal_ppc64x.go @@ -3,12 +3,11 @@ // license that can be found in the LICENSE file. //go:build (aix || linux) && (ppc64 || ppc64le) -// +build aix linux -// +build ppc64 ppc64le package runtime import ( + "internal/abi" "runtime/internal/sys" "unsafe" ) @@ -83,8 +82,8 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_r0(0) c.set_r30(uint64(uintptr(unsafe.Pointer(gp)))) - c.set_r12(uint64(funcPC(sigpanic))) - c.set_pc(uint64(funcPC(sigpanic))) + c.set_r12(uint64(abi.FuncPCABIInternal(sigpanic))) + c.set_pc(uint64(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { diff --git a/src/runtime/signal_riscv64.go b/src/runtime/signal_riscv64.go index e6b1b14130e53b..5eeb227aa05862 100644 --- a/src/runtime/signal_riscv64.go +++ b/src/runtime/signal_riscv64.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build linux && riscv64 -// +build linux,riscv64 package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -63,7 +63,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // functions are correctly handled. This smashes // the stack frame but we're not going back there // anyway. - sp := c.sp() - sys.PtrSize + sp := c.sp() - goarch.PtrSize c.set_sp(sp) *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.ra() @@ -76,7 +76,7 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) { // In case we are panicking from external C code c.set_gp(uint64(uintptr(unsafe.Pointer(gp)))) - c.set_pc(uint64(funcPC(sigpanic))) + c.set_pc(uint64(abi.FuncPCABIInternal(sigpanic))) } func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { @@ -84,7 +84,7 @@ func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { // push the call. The function being pushed is responsible // for restoring the LR and setting the SP back. // This extra slot is known to gentraceback. - sp := c.sp() - sys.PtrSize + sp := c.sp() - goarch.PtrSize c.set_sp(sp) *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.ra() // Set up PC and LR to pretend the function being signaled diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index 6096760b50c242..545094c6403180 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.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. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package runtime import ( + "internal/abi" "runtime/internal/atomic" + "runtime/internal/sys" "unsafe" ) @@ -107,6 +108,7 @@ var signalsOK bool // Initialize signals. // Called by libpreinit so runtime may not be initialized. +// //go:nosplit //go:nowritebarrierrec func initsig(preinit bool) { @@ -143,7 +145,7 @@ func initsig(preinit bool) { } handlingSig[i] = 1 - setsig(i, funcPC(sighandler)) + setsig(i, abi.FuncPCABIInternal(sighandler)) } } @@ -160,14 +162,21 @@ func sigInstallGoHandler(sig uint32) bool { } } + if (GOOS == "linux" || GOOS == "android") && !iscgo && sig == sigPerThreadSyscall { + // sigPerThreadSyscall is the same signal used by glibc for + // per-thread syscalls on Linux. We use it for the same purpose + // in non-cgo binaries. + return true + } + t := &sigtable[sig] if t.flags&_SigSetStack != 0 { return false } // When built using c-archive or c-shared, only install signal - // handlers for synchronous signals and SIGPIPE. - if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE { + // handlers for synchronous signals and SIGPIPE and sigPreempt. + if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE && sig != sigPreempt { return false } @@ -194,7 +203,7 @@ func sigenable(sig uint32) { <-maskUpdatedChan if atomic.Cas(&handlingSig[sig], 0, 1) { atomic.Storeuintptr(&fwdSig[sig], getsig(sig)) - setsig(sig, funcPC(sighandler)) + setsig(sig, abi.FuncPCABIInternal(sighandler)) } } } @@ -252,6 +261,7 @@ func sigignore(sig uint32) { // back to the default. This is called by the child after a fork, so that // we can enable the signal mask for the exec without worrying about // running a signal handler in the child. +// //go:nosplit //go:nowritebarrierrec func clearSignalHandlers() { @@ -262,16 +272,33 @@ func clearSignalHandlers() { } } -// setProcessCPUProfiler is called when the profiling timer changes. -// It is called with prof.lock held. hz is the new timer, and is 0 if +// setProcessCPUProfilerTimer is called when the profiling timer changes. +// It is called with prof.signalLock held. hz is the new timer, and is 0 if // profiling is being disabled. Enable or disable the signal as // required for -buildmode=c-archive. -func setProcessCPUProfiler(hz int32) { +func setProcessCPUProfilerTimer(hz int32) { if hz != 0 { // Enable the Go signal handler if not enabled. if atomic.Cas(&handlingSig[_SIGPROF], 0, 1) { - atomic.Storeuintptr(&fwdSig[_SIGPROF], getsig(_SIGPROF)) - setsig(_SIGPROF, funcPC(sighandler)) + h := getsig(_SIGPROF) + // If no signal handler was installed before, then we record + // _SIG_IGN here. When we turn off profiling (below) we'll start + // ignoring SIGPROF signals. We do this, rather than change + // to SIG_DFL, because there may be a pending SIGPROF + // signal that has not yet been delivered to some other thread. + // If we change to SIG_DFL when turning off profiling, the + // program will crash when that SIGPROF is delivered. We assume + // that programs that use profiling don't want to crash on a + // stray SIGPROF. See issue 19320. + // We do the change here instead of when turning off profiling, + // because there we may race with a signal handler running + // concurrently, in particular, sigfwdgo may observe _SIG_DFL and + // die. See issue 43828. + if h == _SIG_DFL { + h = _SIG_IGN + } + atomic.Storeuintptr(&fwdSig[_SIGPROF], h) + setsig(_SIGPROF, abi.FuncPCABIInternal(sighandler)) } var it itimerval @@ -287,31 +314,19 @@ func setProcessCPUProfiler(hz int32) { // when we enabled profiling. We don't try to handle the case // of a program that changes the SIGPROF handler while Go // profiling is enabled. - // - // If no signal handler was installed before, then start - // ignoring SIGPROF signals. We do this, rather than change - // to SIG_DFL, because there may be a pending SIGPROF - // signal that has not yet been delivered to some other thread. - // If we change to SIG_DFL here, the program will crash - // when that SIGPROF is delivered. We assume that programs - // that use profiling don't want to crash on a stray SIGPROF. - // See issue 19320. if !sigInstallGoHandler(_SIGPROF) { if atomic.Cas(&handlingSig[_SIGPROF], 1, 0) { h := atomic.Loaduintptr(&fwdSig[_SIGPROF]) - if h == _SIG_DFL { - h = _SIG_IGN - } setsig(_SIGPROF, h) } } } } -// setThreadCPUProfiler makes any thread-specific changes required to +// setThreadCPUProfilerHz makes any thread-specific changes required to // implement profiling at a rate of hz. -// No changes required on Unix systems. -func setThreadCPUProfiler(hz int32) { +// No changes required on Unix systems when using setitimer. +func setThreadCPUProfilerHz(hz int32) { getg().m.profilehz = hz } @@ -329,16 +344,16 @@ func doSigPreempt(gp *g, ctxt *sigctxt) { if wantAsyncPreempt(gp) { if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok { // Adjust the PC and inject a call to asyncPreempt. - ctxt.pushCall(funcPC(asyncPreempt), newpc) + ctxt.pushCall(abi.FuncPCABI0(asyncPreempt), newpc) } } // Acknowledge the preemption. - atomic.Xadd(&gp.m.preemptGen, 1) - atomic.Store(&gp.m.signalPending, 0) + gp.m.preemptGen.Add(1) + gp.m.signalPending.Store(0) if GOOS == "darwin" || GOOS == "ios" { - atomic.Xadd(&pendingPreemptSignals, -1) + pendingPreemptSignals.Add(-1) } } @@ -357,9 +372,9 @@ func preemptM(mp *m) { execLock.rlock() } - if atomic.Cas(&mp.signalPending, 0, 1) { + if mp.signalPending.CompareAndSwap(0, 1) { if GOOS == "darwin" || GOOS == "ios" { - atomic.Xadd(&pendingPreemptSignals, 1) + pendingPreemptSignals.Add(1) } // If multiple threads are preempting the same M, it may send many @@ -382,7 +397,7 @@ func preemptM(mp *m) { //go:nosplit func sigFetchG(c *sigctxt) *g { switch GOARCH { - case "arm", "arm64", "ppc64", "ppc64le": + case "arm", "arm64", "ppc64", "ppc64le", "riscv64", "s390x": if !iscgo && inVDSOPage(c.sigpc()) { // When using cgo, we save the g on TLS and load it from there // in sigtramp. Just use that. @@ -418,11 +433,15 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { return } c := &sigctxt{info, ctx} - g := sigFetchG(c) - setg(g) - if g == nil { + gp := sigFetchG(c) + setg(gp) + if gp == nil { if sig == _SIGPROF { - sigprofNonGoPC(c.sigpc()) + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(nil, c) { + sigprofNonGoPC(c.sigpc()) + } return } if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 { @@ -434,7 +453,7 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { // The default behavior for sigPreempt is to ignore // the signal, so badsignal will be a no-op anyway. if GOOS == "darwin" || GOOS == "ios" { - atomic.Xadd(&pendingPreemptSignals, -1) + pendingPreemptSignals.Add(-1) } return } @@ -443,32 +462,84 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { return } - setg(g.m.gsignal) + setg(gp.m.gsignal) // If some non-Go code called sigaltstack, adjust. var gsignalStack gsignalStack - setStack := adjustSignalStack(sig, g.m, &gsignalStack) + setStack := adjustSignalStack(sig, gp.m, &gsignalStack) if setStack { - g.m.gsignal.stktopsp = getcallersp() + gp.m.gsignal.stktopsp = getcallersp() } - if g.stackguard0 == stackFork { + if gp.stackguard0 == stackFork { signalDuringFork(sig) } c.fixsigcode(sig) - sighandler(sig, info, ctx, g) - setg(g) + sighandler(sig, info, ctx, gp) + setg(gp) if setStack { restoreGsignalStack(&gsignalStack) } } +// If the signal handler receives a SIGPROF signal on a non-Go thread, +// it tries to collect a traceback into sigprofCallers. +// sigprofCallersUse is set to non-zero while sigprofCallers holds a traceback. +var sigprofCallers cgoCallers +var sigprofCallersUse uint32 + +// sigprofNonGo is called if we receive a SIGPROF signal on a non-Go thread, +// and the signal handler collected a stack trace in sigprofCallers. +// When this is called, sigprofCallersUse will be non-zero. +// g is nil, and what we can do is very limited. +// +// It is called from the signal handling functions written in assembly code that +// are active for cgo programs, cgoSigtramp and sigprofNonGoWrapper, which have +// not verified that the SIGPROF delivery corresponds to the best available +// profiling source for this thread. +// +//go:nosplit +//go:nowritebarrierrec +func sigprofNonGo(sig uint32, info *siginfo, ctx unsafe.Pointer) { + if prof.hz.Load() != 0 { + c := &sigctxt{info, ctx} + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if validSIGPROF(nil, c) { + n := 0 + for n < len(sigprofCallers) && sigprofCallers[n] != 0 { + n++ + } + cpuprof.addNonGo(sigprofCallers[:n]) + } + } + + atomic.Store(&sigprofCallersUse, 0) +} + +// sigprofNonGoPC is called when a profiling signal arrived on a +// non-Go thread and we have a single PC value, not a stack trace. +// g is nil, and what we can do is very limited. +// +//go:nosplit +//go:nowritebarrierrec +func sigprofNonGoPC(pc uintptr) { + if prof.hz.Load() != 0 { + stk := []uintptr{ + pc, + abi.FuncPCABIInternal(_ExternalCode) + sys.PCQuantum, + } + cpuprof.addNonGo(stk) + } +} + // adjustSignalStack adjusts the current stack guard based on the // stack pointer that is actually in use while handling a signal. // We do this in case some non-Go code called sigaltstack. // This reports whether the stack was adjusted, and if so stores the old // signal stack in *gsigstack. +// //go:nosplit func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool { sp := uintptr(unsafe.Pointer(&sig)) @@ -525,7 +596,7 @@ var testSigusr1 func(gp *g) bool // sighandler is invoked when a signal occurs. The global g will be // set to a gsignal goroutine and we will be running on the alternate -// signal stack. The parameter g will be the value of the global g +// signal stack. The parameter gp will be the value of the global g // when the signal occurred. The sig, info, and ctxt parameters are // from the system signal handler: they are the parameters passed when // the SA is passed to the sigaction system call. @@ -535,11 +606,30 @@ var testSigusr1 func(gp *g) bool // //go:nowritebarrierrec func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { - _g_ := getg() + // The g executing the signal handler. This is almost always + // mp.gsignal. See delayedSignal for an exception. + gsignal := getg() + mp := gsignal.m c := &sigctxt{info, ctxt} + // Cgo TSAN (not the Go race detector) intercepts signals and calls the + // signal handler at a later time. When the signal handler is called, the + // memory may have changed, but the signal context remains old. The + // unmatched signal context and memory makes it unsafe to unwind or inspect + // the stack. So we ignore delayed non-fatal signals that will cause a stack + // inspection (profiling signal and preemption signal). + // cgo_yield is only non-nil for TSAN, and is specifically used to trigger + // signal delivery. We use that as an indicator of delayed signals. + // For delayed signals, the handler is called on the g0 stack (see + // adjustSignalStack). + delayedSignal := *cgo_yield != nil && mp != nil && gsignal.stack == mp.g0.stack + if sig == _SIGPROF { - sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m) + // Some platforms (Linux) have per-thread timers, which we use in + // combination with the process-wide timer. Avoid double-counting. + if !delayedSignal && validSIGPROF(mp, c) { + sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, mp) + } return } @@ -551,7 +641,16 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { return } - if sig == sigPreempt && debug.asyncpreemptoff == 0 { + if (GOOS == "linux" || GOOS == "android") && sig == sigPerThreadSyscall { + // sigPerThreadSyscall is the same signal used by glibc for + // per-thread syscalls on Linux. We use it for the same purpose + // in non-cgo binaries. Since this signal is not _SigNotify, + // there is nothing more to do once we run the syscall. + runPerThreadSyscall() + return + } + + if sig == sigPreempt && debug.asyncpreemptoff == 0 && !delayedSignal { // Might be a preemption signal. doSigPreempt(gp, c) // Even if this was definitely a preemption signal, it @@ -613,8 +712,8 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { return } - _g_.m.throwing = 1 - _g_.m.caughtsig.set(gp) + mp.throwing = throwTypeRuntime + mp.caughtsig.set(gp) if crashing == 0 { startpanic_m() @@ -626,10 +725,12 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { print("Signal ", sig, "\n") } - print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n") - if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 { + print("PC=", hex(c.sigpc()), " m=", mp.id, " sigcode=", c.sigcode(), "\n") + if mp.incgo && gp == mp.g0 && mp.curg != nil { print("signal arrived during cgo execution\n") - gp = _g_.m.lockedg.ptr() + // Switch to curg so that we get a traceback of the Go code + // leading up to the cgocall, which switched from curg to g0. + gp = mp.curg } if sig == _SIGILL || sig == _SIGFPE { // It would be nice to know how long the instruction is. @@ -661,10 +762,10 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { if level > 0 { goroutineheader(gp) tracebacktrap(c.sigpc(), c.sigsp(), c.siglr(), gp) - if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning { + if crashing > 0 && gp != mp.curg && mp.curg != nil && readgstatus(mp.curg)&^_Gscan == _Grunning { // tracebackothers on original m skipped this one; trace it now. - goroutineheader(_g_.m.curg) - traceback(^uintptr(0), ^uintptr(0), 0, _g_.m.curg) + goroutineheader(mp.curg) + traceback(^uintptr(0), ^uintptr(0), 0, mp.curg) } else if crashing == 0 { tracebackothers(gp) print("\n") @@ -712,36 +813,37 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { // getg().throwsplit, since sigpanic may need to grow the stack. // // This is exported via linkname to assembly in runtime/cgo. +// //go:linkname sigpanic func sigpanic() { - g := getg() - if !canpanic(g) { + gp := getg() + if !canpanic() { throw("unexpected signal during runtime execution") } - switch g.sig { + switch gp.sig { case _SIGBUS: - if g.sigcode0 == _BUS_ADRERR && g.sigcode1 < 0x1000 { + if gp.sigcode0 == _BUS_ADRERR && gp.sigcode1 < 0x1000 { panicmem() } // Support runtime/debug.SetPanicOnFault. - if g.paniconfault { - panicmemAddr(g.sigcode1) + if gp.paniconfault { + panicmemAddr(gp.sigcode1) } - print("unexpected fault address ", hex(g.sigcode1), "\n") + print("unexpected fault address ", hex(gp.sigcode1), "\n") throw("fault") case _SIGSEGV: - if (g.sigcode0 == 0 || g.sigcode0 == _SEGV_MAPERR || g.sigcode0 == _SEGV_ACCERR) && g.sigcode1 < 0x1000 { + if (gp.sigcode0 == 0 || gp.sigcode0 == _SEGV_MAPERR || gp.sigcode0 == _SEGV_ACCERR) && gp.sigcode1 < 0x1000 { panicmem() } // Support runtime/debug.SetPanicOnFault. - if g.paniconfault { - panicmemAddr(g.sigcode1) + if gp.paniconfault { + panicmemAddr(gp.sigcode1) } - print("unexpected fault address ", hex(g.sigcode1), "\n") + print("unexpected fault address ", hex(gp.sigcode1), "\n") throw("fault") case _SIGFPE: - switch g.sigcode0 { + switch gp.sigcode0 { case _FPE_INTDIV: panicdivide() case _FPE_INTOVF: @@ -750,16 +852,17 @@ func sigpanic() { panicfloat() } - if g.sig >= uint32(len(sigtable)) { - // can't happen: we looked up g.sig in sigtable to decide to call sigpanic + if gp.sig >= uint32(len(sigtable)) { + // can't happen: we looked up gp.sig in sigtable to decide to call sigpanic throw("unexpected signal value") } - panic(errorString(sigtable[g.sig].name)) + panic(errorString(sigtable[gp.sig].name)) } // dieFromSignal kills the program with a signal. // This provides the expected exit status for the shell. // This is only called with fatal signals expected to kill the process. +// //go:nosplit //go:nowritebarrierrec func dieFromSignal(sig uint32) { @@ -843,7 +946,7 @@ func raisebadsignal(sig uint32, c *sigctxt) { // We may receive another instance of the signal before we // restore the Go handler, but that is not so bad: we know // that the Go program has been ignoring the signal. - setsig(sig, funcPC(sighandler)) + setsig(sig, abi.FuncPCABIInternal(sighandler)) } //go:nosplit @@ -932,6 +1035,7 @@ func signalDuringFork(sig uint32) { var badginsignalMsg = "fatal: bad g in signal handler\n" // This runs on a foreign stack, without an m or a g. No stack split. +// //go:nosplit //go:norace //go:nowritebarrierrec @@ -961,6 +1065,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) // signal to the handler that was installed before Go's. Returns whether the // signal was forwarded. // This is called by the signal handler, and the world may be stopped. +// //go:nosplit //go:nowritebarrierrec func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { @@ -1012,8 +1117,8 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { // (1) we weren't in VDSO page, // (2) we were in a goroutine (i.e., m.curg != nil), and // (3) we weren't in CGO. - g := sigFetchG(c) - if g != nil && g.m != nil && g.m.curg != nil && !g.m.incgo { + gp := sigFetchG(c) + if gp != nil && gp.m != nil && gp.m.curg != nil && !gp.m.incgo { return false } @@ -1030,6 +1135,7 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { // thread calls a Go function. // This is nosplit and nowritebarrierrec because it is called by needm // which may be called on a non-Go thread with no g available. +// //go:nosplit //go:nowritebarrierrec func sigsave(p *sigset) { @@ -1041,6 +1147,7 @@ func sigsave(p *sigset) { // calls a Go function. // This is nosplit and nowritebarrierrec because it is called by dropm // after g has been cleared. +// //go:nosplit //go:nowritebarrierrec func msigrestore(sigmask sigset) { @@ -1060,6 +1167,7 @@ var sigsetAllExiting = sigset_all // definition of sigset_all is used. // This is nosplit and nowritebarrierrec because it is called by needm // which may be called on a non-Go thread with no g available. +// //go:nosplit //go:nowritebarrierrec func sigblock(exiting bool) { @@ -1074,6 +1182,7 @@ func sigblock(exiting bool) { // This is nosplit and nowritebarrierrec because it is called from // dieFromSignal, which can be called by sigfwdgo while running in the // signal handler, on the signal stack, with no g available. +// //go:nosplit //go:nowritebarrierrec func unblocksig(sig uint32) { @@ -1100,15 +1209,15 @@ func minitSignals() { // of whether it is already set). Record which choice was made in // newSigstack, so that it can be undone in unminit. func minitSignalStack() { - _g_ := getg() + mp := getg().m var st stackt sigaltstack(nil, &st) if st.ss_flags&_SS_DISABLE != 0 || !iscgo { - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true + signalstack(&mp.gsignal.stack) + mp.newSigstack = true } else { - setGsignalStack(&st, &_g_.m.goSigStack) - _g_.m.newSigstack = false + setGsignalStack(&st, &mp.goSigStack) + mp.newSigstack = false } } @@ -1132,6 +1241,7 @@ func minitSignalMask() { // unminitSignals is called from dropm, via unminit, to undo the // effect of calling minit on a non-Go thread. +// //go:nosplit func unminitSignals() { if getg().m.newSigstack { @@ -1151,6 +1261,7 @@ func unminitSignals() { // blockableSig reports whether sig may be blocked by the signal mask. // We never want to block the signals marked _SigUnblock; // these are the synchronous signals that turn into a Go panic. +// We never want to block the preemption signal if it is being used. // In a Go program--not a c-archive/c-shared--we never want to block // the signals marked _SigKill or _SigThrow, as otherwise it's possible // for all running threads to block them and delay their delivery until @@ -1161,6 +1272,9 @@ func blockableSig(sig uint32) bool { if flags&_SigUnblock != 0 { return false } + if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 { + return false + } if isarchive || islibrary { return true } @@ -1181,25 +1295,27 @@ type gsignalStack struct { // It saves the old values in *old for use by restoreGsignalStack. // This is used when handling a signal if non-Go code has set the // alternate signal stack. +// //go:nosplit //go:nowritebarrierrec func setGsignalStack(st *stackt, old *gsignalStack) { - g := getg() + gp := getg() if old != nil { - old.stack = g.m.gsignal.stack - old.stackguard0 = g.m.gsignal.stackguard0 - old.stackguard1 = g.m.gsignal.stackguard1 - old.stktopsp = g.m.gsignal.stktopsp + old.stack = gp.m.gsignal.stack + old.stackguard0 = gp.m.gsignal.stackguard0 + old.stackguard1 = gp.m.gsignal.stackguard1 + old.stktopsp = gp.m.gsignal.stktopsp } 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 + gp.m.gsignal.stack.lo = stsp + gp.m.gsignal.stack.hi = stsp + st.ss_size + gp.m.gsignal.stackguard0 = stsp + _StackGuard + gp.m.gsignal.stackguard1 = stsp + _StackGuard } // restoreGsignalStack restores the gsignal stack to the value it had // before entering the signal handler. +// //go:nosplit //go:nowritebarrierrec func restoreGsignalStack(st *gsignalStack) { @@ -1211,6 +1327,7 @@ func restoreGsignalStack(st *gsignalStack) { } // signalstack sets the current thread's alternate signal stack to s. +// //go:nosplit func signalstack(s *stack) { st := stackt{ss_size: s.hi - s.lo} @@ -1225,9 +1342,9 @@ func signalstack(s *stack) { //go:nosplit //go:linkname setsigsegv func setsigsegv(pc uintptr) { - g := getg() - g.sig = _SIGSEGV - g.sigpc = pc - g.sigcode0 = _SEGV_MAPERR - g.sigcode1 = 0 // TODO: emulate si_addr + gp := getg() + gp.sig = _SIGSEGV + gp.sigpc = pc + gp.sigcode0 = _SEGV_MAPERR + gp.sigcode1 = 0 // TODO: emulate si_addr } diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index b720ddcf164ea5..0f1929e09a943d 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/abi" "runtime/internal/sys" "unsafe" ) @@ -27,15 +28,15 @@ func firstcontinuetramp() func lastcontinuetramp() func initExceptionHandler() { - stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp)) + stdcall2(_AddVectoredExceptionHandler, 1, abi.FuncPCABI0(exceptiontramp)) if _AddVectoredContinueHandler == nil || GOARCH == "386" { // use SetUnhandledExceptionFilter for windows-386 or // if VectoredContinueHandler is unavailable. // note: SetUnhandledExceptionFilter handler won't be called, if debugging. - stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp)) + stdcall1(_SetUnhandledExceptionFilter, abi.FuncPCABI0(lastcontinuetramp)) } else { - stdcall2(_AddVectoredContinueHandler, 1, funcPC(firstcontinuetramp)) - stdcall2(_AddVectoredContinueHandler, 0, funcPC(lastcontinuetramp)) + stdcall2(_AddVectoredContinueHandler, 1, abi.FuncPCABI0(firstcontinuetramp)) + stdcall2(_AddVectoredContinueHandler, 0, abi.FuncPCABI0(lastcontinuetramp)) } } @@ -133,7 +134,7 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { // The exception is not from asyncPreempt, so not to push a // sigpanic call to make it look like that. Instead, just // overwrite the PC. (See issue #35773) - if r.ip() != 0 && r.ip() != funcPC(asyncPreempt) { + if r.ip() != 0 && r.ip() != abi.FuncPCABI0(asyncPreempt) { sp := unsafe.Pointer(r.sp()) delta := uintptr(sys.StackAlign) sp = add(sp, -delta) @@ -145,7 +146,7 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { *((*uintptr)(sp)) = r.ip() } } - r.set_ip(funcPC(sigpanic0)) + r.set_ip(abi.FuncPCABI0(sigpanic0)) return _EXCEPTION_CONTINUE_EXECUTION } @@ -198,35 +199,37 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { return 0 // not reached } +// Always called on g0. gp is the G where the exception occurred. +// //go:nosplit func winthrow(info *exceptionrecord, r *context, gp *g) { - _g_ := getg() + g0 := getg() - if panicking != 0 { // traceback already printed + if panicking.Load() != 0 { // traceback already printed exit(2) } - panicking = 1 + panicking.Store(1) // In case we're handling a g0 stack overflow, blow away the // g0 stack bounds so we have room to print the traceback. If // this somehow overflows the stack, the OS will trap it. - _g_.stack.lo = 0 - _g_.stackguard0 = _g_.stack.lo + _StackGuard - _g_.stackguard1 = _g_.stackguard0 + g0.stack.lo = 0 + g0.stackguard0 = g0.stack.lo + _StackGuard + g0.stackguard1 = g0.stackguard0 print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n") print("PC=", hex(r.ip()), "\n") - if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 { + if g0.m.incgo && gp == g0.m.g0 && g0.m.curg != nil { if iscgo { print("signal arrived during external code execution\n") } - gp = _g_.m.lockedg.ptr() + gp = g0.m.curg } print("\n") - _g_.m.throwing = 1 - _g_.m.caughtsig.set(gp) + g0.m.throwing = throwTypeRuntime + g0.m.caughtsig.set(gp) level, _, docrash := gotraceback() if level > 0 { @@ -243,20 +246,20 @@ func winthrow(info *exceptionrecord, r *context, gp *g) { } func sigpanic() { - g := getg() - if !canpanic(g) { + gp := getg() + if !canpanic() { throw("unexpected signal during runtime execution") } - switch g.sig { + switch gp.sig { case _EXCEPTION_ACCESS_VIOLATION: - if g.sigcode1 < 0x1000 { + if gp.sigcode1 < 0x1000 { panicmem() } - if g.paniconfault { - panicmemAddr(g.sigcode1) + if gp.paniconfault { + panicmemAddr(gp.sigcode1) } - print("unexpected fault address ", hex(g.sigcode1), "\n") + print("unexpected fault address ", hex(gp.sigcode1), "\n") throw("fault") case _EXCEPTION_INT_DIVIDE_BY_ZERO: panicdivide() diff --git a/src/runtime/signal_windows_test.go b/src/runtime/signal_windows_test.go index 1b7cb9d4c453ae..8b32ad89746c3e 100644 --- a/src/runtime/signal_windows_test.go +++ b/src/runtime/signal_windows_test.go @@ -1,5 +1,6 @@ -//go:build windows -// +build windows +// Copyright 2019 The Go Authors. 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 @@ -11,7 +12,6 @@ import ( "os/exec" "path/filepath" "runtime" - "strconv" "strings" "syscall" "testing" @@ -25,6 +25,7 @@ func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { t.Skip("this test can only run on windows/amd64") } testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) testenv.MustHaveExecPath(t, "gcc") testprog.Lock() defer testprog.Unlock() @@ -92,13 +93,16 @@ func TestCtrlHandler(t *testing.T) { // run test program cmd = exec.Command(exe) - var stderr bytes.Buffer + var stdout strings.Builder + var stderr strings.Builder + cmd.Stdout = &stdout cmd.Stderr = &stderr - outPipe, err := cmd.StdoutPipe() + inPipe, err := cmd.StdinPipe() if err != nil { - t.Fatalf("Failed to create stdout pipe: %v", err) + t.Fatalf("Failed to create stdin pipe: %v", err) } - outReader := bufio.NewReader(outPipe) + // keep inPipe alive until the end of the test + defer inPipe.Close() // in a new command window const _CREATE_NEW_CONSOLE = 0x00000010 @@ -114,29 +118,15 @@ func TestCtrlHandler(t *testing.T) { cmd.Wait() }() - // wait for child to be ready to receive signals - if line, err := outReader.ReadString('\n'); err != nil { - t.Fatalf("could not read stdout: %v", err) - } else if strings.TrimSpace(line) != "ready" { - t.Fatalf("unexpected message: %s", line) - } - - // gracefully kill pid, this closes the command window - if err := exec.Command("taskkill.exe", "/pid", strconv.Itoa(cmd.Process.Pid)).Run(); err != nil { - t.Fatalf("failed to kill: %v", err) + // check child exited gracefully, did not timeout + if err := cmd.Wait(); err != nil { + t.Fatalf("Program exited with error: %v\n%s", err, &stderr) } // check child received, handled SIGTERM - if line, err := outReader.ReadString('\n'); err != nil { - t.Fatalf("could not read stdout: %v", err) - } else if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(line); expected != got { + if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(stdout.String()); expected != got { t.Fatalf("Expected '%s' got: %s", expected, got) } - - // check child exited gracefully, did not timeout - if err := cmd.Wait(); err != nil { - t.Fatalf("Program exited with error: %v\n%s", err, &stderr) - } } // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. @@ -149,6 +139,7 @@ func TestLibraryCtrlHandler(t *testing.T) { t.Skip("this test can only run on windows/amd64") } testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) testenv.MustHaveExecPath(t, "gcc") testprog.Lock() defer testprog.Unlock() diff --git a/src/runtime/sigqueue.go b/src/runtime/sigqueue.go index 6c91fb3fb3538c..51e424d55b645f 100644 --- a/src/runtime/sigqueue.go +++ b/src/runtime/sigqueue.go @@ -11,25 +11,24 @@ // // sigsend is called by the signal handler to queue a new signal. // signal_recv is called by the Go program to receive a newly queued signal. +// // Synchronization between sigsend and signal_recv is based on the sig.state -// variable. It can be in 4 states: sigIdle, sigReceiving, sigSending and sigFixup. -// sigReceiving means that signal_recv is blocked on sig.Note and there are no -// new pending signals. -// sigSending means that sig.mask *may* contain new pending signals, -// signal_recv can't be blocked in this state. -// sigIdle means that there are no new pending signals and signal_recv is not blocked. -// sigFixup is a transient state that can only exist as a short -// transition from sigReceiving and then on to sigIdle: it is -// used to ensure the AllThreadsSyscall()'s mDoFixup() operation -// occurs on the sleeping m, waiting to receive a signal. +// variable. It can be in three states: +// * sigReceiving means that signal_recv is blocked on sig.Note and there are +// no new pending signals. +// * sigSending means that sig.mask *may* contain new pending signals, +// signal_recv can't be blocked in this state. +// * sigIdle means that there are no new pending signals and signal_recv is not +// blocked. +// // Transitions between states are done atomically with CAS. +// // When signal_recv is unblocked, it resets sig.Note and rechecks sig.mask. // If several sigsends and signal_recv execute concurrently, it can lead to // unnecessary rechecks of sig.mask, but it cannot lead to missed signals // nor deadlocks. //go:build !plan9 -// +build !plan9 package runtime @@ -55,8 +54,8 @@ var sig struct { wanted [(_NSIG + 31) / 32]uint32 ignored [(_NSIG + 31) / 32]uint32 recv [(_NSIG + 31) / 32]uint32 - state uint32 - delivering uint32 + state atomic.Uint32 + delivering atomic.Uint32 inuse bool } @@ -64,7 +63,6 @@ const ( sigIdle = iota sigReceiving sigSending - sigFixup ) // sigsend delivers a signal from sighandler to the internal signal delivery queue. @@ -76,11 +74,11 @@ func sigsend(s uint32) bool { return false } - atomic.Xadd(&sig.delivering, 1) + sig.delivering.Add(1) // We are running in the signal handler; defer is not available. if w := atomic.Load(&sig.wanted[s/32]); w&bit == 0 { - atomic.Xadd(&sig.delivering, -1) + sig.delivering.Add(-1) return false } @@ -88,7 +86,7 @@ func sigsend(s uint32) bool { for { mask := sig.mask[s/32] if mask&bit != 0 { - atomic.Xadd(&sig.delivering, -1) + sig.delivering.Add(-1) return true // signal already in queue } if atomic.Cas(&sig.mask[s/32], mask, mask|bit) { @@ -99,18 +97,18 @@ func sigsend(s uint32) bool { // Notify receiver that queue has new bit. Send: for { - switch atomic.Load(&sig.state) { + switch sig.state.Load() { default: throw("sigsend: inconsistent state") case sigIdle: - if atomic.Cas(&sig.state, sigIdle, sigSending) { + if sig.state.CompareAndSwap(sigIdle, sigSending) { break Send } case sigSending: // notification already pending break Send case sigReceiving: - if atomic.Cas(&sig.state, sigReceiving, sigIdle) { + if sig.state.CompareAndSwap(sigReceiving, sigIdle) { if GOOS == "darwin" || GOOS == "ios" { sigNoteWakeup(&sig.note) break Send @@ -118,31 +116,16 @@ Send: notewakeup(&sig.note) break Send } - case sigFixup: - // nothing to do - we need to wait for sigIdle. - mDoFixupAndOSYield() } } - atomic.Xadd(&sig.delivering, -1) + sig.delivering.Add(-1) return true } -// sigRecvPrepareForFixup is used to temporarily wake up the -// signal_recv() running thread while it is blocked waiting for the -// arrival of a signal. If it causes the thread to wake up, the -// sig.state travels through this sequence: sigReceiving -> sigFixup -// -> sigIdle -> sigReceiving and resumes. (This is only called while -// GC is disabled.) -//go:nosplit -func sigRecvPrepareForFixup() { - if atomic.Cas(&sig.state, sigReceiving, sigFixup) { - notewakeup(&sig.note) - } -} - // Called to receive the next queued signal. // Must only be called from a single goroutine at a time. +// //go:linkname signal_recv os/signal.signal_recv func signal_recv() uint32 { for { @@ -157,30 +140,21 @@ func signal_recv() uint32 { // Wait for updates to be available from signal sender. Receive: for { - switch atomic.Load(&sig.state) { + switch sig.state.Load() { default: throw("signal_recv: inconsistent state") case sigIdle: - if atomic.Cas(&sig.state, sigIdle, sigReceiving) { + if sig.state.CompareAndSwap(sigIdle, sigReceiving) { if GOOS == "darwin" || GOOS == "ios" { sigNoteSleep(&sig.note) break Receive } notetsleepg(&sig.note, -1) noteclear(&sig.note) - if !atomic.Cas(&sig.state, sigFixup, sigIdle) { - break Receive - } - // Getting here, the code will - // loop around again to sleep - // in state sigReceiving. This - // path is taken when - // sigRecvPrepareForFixup() - // has been called by another - // thread. + break Receive } case sigSending: - if atomic.Cas(&sig.state, sigSending, sigIdle) { + if sig.state.CompareAndSwap(sigSending, sigIdle) { break Receive } } @@ -200,6 +174,7 @@ func signal_recv() uint32 { // the signal(s) in question, and here we are just waiting to make sure // that all the signals have been delivered to the user channels // by the os/signal package. +// //go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle func signalWaitUntilIdle() { // Although the signals we care about have been removed from @@ -207,19 +182,20 @@ func signalWaitUntilIdle() { // a signal, has read from sig.wanted, is now updating sig.mask, // and has not yet woken up the processor thread. We need to wait // until all current signal deliveries have completed. - for atomic.Load(&sig.delivering) != 0 { + for sig.delivering.Load() != 0 { Gosched() } // Although WaitUntilIdle seems like the right name for this // function, the state we are looking for is sigReceiving, not // sigIdle. The sigIdle state is really more like sigProcessing. - for atomic.Load(&sig.state) != sigReceiving { + for sig.state.Load() != sigReceiving { Gosched() } } // Must only be called from a single goroutine at a time. +// //go:linkname signal_enable os/signal.signal_enable func signal_enable(s uint32) { if !sig.inuse { @@ -248,6 +224,7 @@ func signal_enable(s uint32) { } // Must only be called from a single goroutine at a time. +// //go:linkname signal_disable os/signal.signal_disable func signal_disable(s uint32) { if s >= uint32(len(sig.wanted)*32) { @@ -261,6 +238,7 @@ func signal_disable(s uint32) { } // Must only be called from a single goroutine at a time. +// //go:linkname signal_ignore os/signal.signal_ignore func signal_ignore(s uint32) { if s >= uint32(len(sig.wanted)*32) { @@ -280,6 +258,7 @@ func signal_ignore(s uint32) { // sigInitIgnored marks the signal as already ignored. This is called at // program start by initsig. In a shared library initsig is called by // libpreinit, so the runtime may not be initialized yet. +// //go:nosplit func sigInitIgnored(s uint32) { i := sig.ignored[s/32] @@ -288,6 +267,7 @@ func sigInitIgnored(s uint32) { } // Checked by signal handlers. +// //go:linkname signal_ignored os/signal.signal_ignored func signal_ignored(s uint32) bool { i := atomic.Load(&sig.ignored[s/32]) diff --git a/src/runtime/sigqueue_note.go b/src/runtime/sigqueue_note.go index e23446bea47b77..fb1a517fa57330 100644 --- a/src/runtime/sigqueue_note.go +++ b/src/runtime/sigqueue_note.go @@ -8,7 +8,6 @@ // those functions. These functions will never be called. //go:build !darwin && !plan9 -// +build !darwin,!plan9 package runtime diff --git a/src/runtime/sigqueue_plan9.go b/src/runtime/sigqueue_plan9.go index aebd2060e791ac..9ed6fb5886c05e 100644 --- a/src/runtime/sigqueue_plan9.go +++ b/src/runtime/sigqueue_plan9.go @@ -92,15 +92,9 @@ func sendNote(s *byte) bool { return true } -// sigRecvPrepareForFixup is a no-op on plan9. (This would only be -// called while GC is disabled.) -// -//go:nosplit -func sigRecvPrepareForFixup() { -} - // Called to receive the next queued signal. // Must only be called from a single goroutine at a time. +// //go:linkname signal_recv os/signal.signal_recv func signal_recv() string { for { @@ -124,6 +118,7 @@ func signal_recv() string { // the signal(s) in question, and here we are just waiting to make sure // that all the signals have been delivered to the user channels // by the os/signal package. +// //go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle func signalWaitUntilIdle() { for { @@ -138,6 +133,7 @@ func signalWaitUntilIdle() { } // Must only be called from a single goroutine at a time. +// //go:linkname signal_enable os/signal.signal_enable func signal_enable(s uint32) { if !sig.inuse { @@ -148,11 +144,13 @@ func signal_enable(s uint32) { } // Must only be called from a single goroutine at a time. +// //go:linkname signal_disable os/signal.signal_disable func signal_disable(s uint32) { } // Must only be called from a single goroutine at a time. +// //go:linkname signal_ignore os/signal.signal_ignore func signal_ignore(s uint32) { } diff --git a/src/runtime/sigtab_linux_generic.go b/src/runtime/sigtab_linux_generic.go index dc1debddab3c4f..fe93bbafb5988a 100644 --- a/src/runtime/sigtab_linux_generic.go +++ b/src/runtime/sigtab_linux_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !mips && !mipsle && !mips64 && !mips64le && linux -// +build !mips,!mipsle,!mips64,!mips64le,linux package runtime diff --git a/src/runtime/sigtab_linux_mipsx.go b/src/runtime/sigtab_linux_mipsx.go index af9c7e56eb907c..295ced5bbbbe48 100644 --- a/src/runtime/sigtab_linux_mipsx.go +++ b/src/runtime/sigtab_linux_mipsx.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (mips || mipsle || mips64 || mips64le) && linux -// +build mips mipsle mips64 mips64le -// +build linux package runtime diff --git a/src/runtime/sizeof_test.go b/src/runtime/sizeof_test.go index bbbd1becf7a070..9ce0a3afcdca5c 100644 --- a/src/runtime/sizeof_test.go +++ b/src/runtime/sizeof_test.go @@ -17,11 +17,11 @@ func TestSizeof(t *testing.T) { const _64bit = unsafe.Sizeof(uintptr(0)) == 8 var tests = []struct { - val interface{} // type as a value - _32bit uintptr // size on 32bit platforms - _64bit uintptr // size on 64bit platforms + val any // type as a value + _32bit uintptr // size on 32bit platforms + _64bit uintptr // size on 64bit platforms }{ - {runtime.G{}, 236, 392}, // g, but exported for testing + {runtime.G{}, 240, 392}, // g, but exported for testing {runtime.Sudog{}, 56, 88}, // sudog, but exported for testing } diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 01cdcaeee3b0b1..284ee1f484c929 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -5,6 +5,8 @@ package runtime import ( + "internal/abi" + "internal/goarch" "runtime/internal/math" "runtime/internal/sys" "unsafe" @@ -16,7 +18,7 @@ type slice struct { cap int } -// A notInHeapSlice is a slice backed by go:notinheap memory. +// A notInHeapSlice is a slice backed by runtime/internal/sys.NotInHeap memory. type notInHeapSlice struct { array *notInHeap len int @@ -68,12 +70,15 @@ func makeslicecopy(et *_type, tolen int, fromlen int, from unsafe.Pointer) unsaf if raceenabled { callerpc := getcallerpc() - pc := funcPC(makeslicecopy) + pc := abi.FuncPCABIInternal(makeslicecopy) racereadrangepc(from, copymem, callerpc, pc) } if msanenabled { msanread(from, copymem) } + if asanenabled { + asanread(from, copymem) + } memmove(to, from, copymem) @@ -112,89 +117,85 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer { return makeslice(et, len, cap) } -func unsafeslice(et *_type, ptr unsafe.Pointer, len int) { - if len == 0 { - return - } - - if ptr == nil { - panic(errorString("unsafe.Slice: ptr is nil and len is not zero")) - } - - mem, overflow := math.MulUintptr(et.size, uintptr(len)) - if overflow || mem > maxAlloc || len < 0 { - panicunsafeslicelen() - } +// This is a wrapper over runtime/internal/math.MulUintptr, +// so the compiler can recognize and treat it as an intrinsic. +func mulUintptr(a, b uintptr) (uintptr, bool) { + return math.MulUintptr(a, b) } -func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) { - len := int(len64) - if int64(len) != len64 { - panicunsafeslicelen() - } - unsafeslice(et, ptr, len) -} - -func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) { - unsafeslice64(et, ptr, len64) - - // Check that underlying array doesn't straddle multiple heap objects. - // unsafeslice64 has already checked for overflow. - if checkptrStraddles(ptr, uintptr(len64)*et.size) { - throw("checkptr: unsafe.Slice result straddles multiple allocations") - } -} - -func panicunsafeslicelen() { - panic(errorString("unsafe.Slice: len out of range")) -} - -// growslice handles slice growth during append. -// It is passed the slice element type, the old slice, and the desired new minimum capacity, -// and it returns a new slice with at least that capacity, with the old data -// copied into it. -// The new slice's length is set to the old slice's length, -// NOT to the new requested capacity. -// This is for codegen convenience. The old slice's length is used immediately -// to calculate where to write new values during an append. -// TODO: When the old backend is gone, reconsider this decision. -// The SSA backend might prefer the new length or to return only ptr/cap and save stack space. -func growslice(et *_type, old slice, cap int) slice { +// growslice allocates new backing store for a slice. +// +// arguments: +// oldPtr = pointer to the slice's backing array +// newLen = new length (= oldLen + num) +// oldCap = original slice's capacity. +// num = number of elements being added +// et = element type +// +// return values: +// newPtr = pointer to the new backing store +// newLen = same value as the argument +// newCap = capacity of the new backing store +// +// Requires that uint(newLen) > uint(oldCap). +// Assumes the original slice length is newLen - num +// +// A new backing store is allocated with space for at least newLen elements. +// Existing entries [0, oldLen) are copied over to the new backing store. +// Added entries [oldLen, newLen) are not initialized by growslice +// (although for pointer-containing element types, they are zeroed). They +// must be initialized by the caller. +// Trailing entries [newLen, newCap) are zeroed. +// +// growslice's odd calling convention makes the generated code that calls +// this function simpler. In particular, it accepts and returns the +// new length so that the old length is not live (does not need to be +// spilled/restored) and the new length is returned (also does not need +// to be spilled/restored). +func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice { + oldLen := newLen - num if raceenabled { callerpc := getcallerpc() - racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice)) + racereadrangepc(oldPtr, uintptr(oldLen*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice)) } if msanenabled { - msanread(old.array, uintptr(old.len*int(et.size))) + msanread(oldPtr, uintptr(oldLen*int(et.size))) + } + if asanenabled { + asanread(oldPtr, uintptr(oldLen*int(et.size))) } - if cap < old.cap { - panic(errorString("growslice: cap out of range")) + if newLen < 0 { + panic(errorString("growslice: len out of range")) } if et.size == 0 { // append should not create a slice with nil pointer but non-zero len. - // We assume that append doesn't need to preserve old.array in this case. - return slice{unsafe.Pointer(&zerobase), old.len, cap} + // We assume that append doesn't need to preserve oldPtr in this case. + return slice{unsafe.Pointer(&zerobase), newLen, newLen} } - newcap := old.cap + newcap := oldCap doublecap := newcap + newcap - if cap > doublecap { - newcap = cap + if newLen > doublecap { + newcap = newLen } else { - if old.cap < 1024 { + const threshold = 256 + if oldCap < threshold { newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. - for 0 < newcap && newcap < cap { - newcap += newcap / 4 + for 0 < newcap && newcap < newLen { + // Transition from growing 2x for small slices + // to growing 1.25x for large slices. This formula + // gives a smooth-ish transition between the two. + newcap += (newcap + 3*threshold) / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { - newcap = cap + newcap = newLen } } } @@ -203,40 +204,42 @@ func growslice(et *_type, old slice, cap int) slice { var lenmem, newlenmem, capmem uintptr // Specialize for common values of et.size. // For 1 we don't need any division/multiplication. - // For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant. + // For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant. // For powers of 2, use a variable shift. switch { case et.size == 1: - lenmem = uintptr(old.len) - newlenmem = uintptr(cap) + lenmem = uintptr(oldLen) + newlenmem = uintptr(newLen) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) - case et.size == sys.PtrSize: - lenmem = uintptr(old.len) * sys.PtrSize - newlenmem = uintptr(cap) * sys.PtrSize - capmem = roundupsize(uintptr(newcap) * sys.PtrSize) - overflow = uintptr(newcap) > maxAlloc/sys.PtrSize - newcap = int(capmem / sys.PtrSize) + case et.size == goarch.PtrSize: + lenmem = uintptr(oldLen) * goarch.PtrSize + newlenmem = uintptr(newLen) * goarch.PtrSize + capmem = roundupsize(uintptr(newcap) * goarch.PtrSize) + overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize + newcap = int(capmem / goarch.PtrSize) case isPowerOfTwo(et.size): var shift uintptr - if sys.PtrSize == 8 { + if goarch.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.Ctz64(uint64(et.size))) & 63 } else { shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 } - lenmem = uintptr(old.len) << shift - newlenmem = uintptr(cap) << shift + lenmem = uintptr(oldLen) << shift + newlenmem = uintptr(newLen) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) + capmem = uintptr(newcap) << shift default: - lenmem = uintptr(old.len) * et.size - newlenmem = uintptr(cap) * et.size + lenmem = uintptr(oldLen) * et.size + newlenmem = uintptr(newLen) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem = roundupsize(capmem) newcap = int(capmem / et.size) + capmem = uintptr(newcap) * et.size } // The check of overflow in addition to capmem > maxAlloc is needed @@ -253,27 +256,27 @@ func growslice(et *_type, old slice, cap int) slice { // print(len(s), "\n") // } if overflow || capmem > maxAlloc { - panic(errorString("growslice: cap out of range")) + panic(errorString("growslice: len out of range")) } var p unsafe.Pointer if et.ptrdata == 0 { p = mallocgc(capmem, nil, false) - // The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length). + // The append() that calls growslice is going to overwrite from oldLen to newLen. // Only clear the part that will not be overwritten. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) } else { // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory. p = mallocgc(capmem, et, true) if lenmem > 0 && writeBarrier.enabled { - // Only shade the pointers in old.array since we know the destination slice p + // Only shade the pointers in oldPtr since we know the destination slice p // only contains nil pointers because it has been cleared during alloc. - bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata) + bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(oldPtr), lenmem-et.size+et.ptrdata) } } - memmove(p, old.array, lenmem) + memmove(p, oldPtr, lenmem) - return slice{p, old.len, newcap} + return slice{p, newLen, newcap} } func isPowerOfTwo(x uintptr) bool { @@ -298,7 +301,7 @@ func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen size := uintptr(n) * width if raceenabled { callerpc := getcallerpc() - pc := funcPC(slicecopy) + pc := abi.FuncPCABIInternal(slicecopy) racereadrangepc(fromPtr, size, callerpc, pc) racewriterangepc(toPtr, size, callerpc, pc) } @@ -306,6 +309,10 @@ func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen msanread(fromPtr, size) msanwrite(toPtr, size) } + if asanenabled { + asanread(fromPtr, size) + asanwrite(toPtr, size) + } if size == 1 { // common case worth about 2x to do here // TODO: is this still worth it with new memmove impl? diff --git a/src/runtime/softfloat64.go b/src/runtime/softfloat64.go index 13bee6c1d7a064..42ef0092970b3e 100644 --- a/src/runtime/softfloat64.go +++ b/src/runtime/softfloat64.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Software IEEE754 64-bit floating point. -// Only referred to (and thus linked in) by arm port +// Only referred to (and thus linked in) by softfloat targets // and by tests in this directory. package runtime @@ -414,6 +414,25 @@ func fintto64(val int64) (f uint64) { } return fpack64(fs, mant, int(mantbits64), 0) } +func fintto32(val int64) (f uint32) { + fs := uint64(val) & (1 << 63) + mant := uint64(val) + if fs != 0 { + mant = -mant + } + // Reduce mantissa size until it fits into a uint32. + // Keep track of the bits we throw away, and if any are + // nonzero or them into the lowest bit. + exp := int(mantbits32) + var trunc uint32 + for mant >= 1<<32 { + trunc |= uint32(mant) & 1 + mant >>= 1 + exp++ + } + + return fpack32(uint32(fs>>32), uint32(mant), exp, trunc) +} // 64x64 -> 128 multiply. // adapted from hacker's delight. @@ -493,6 +512,7 @@ func fmul32(x, y uint32) uint32 { } func fdiv32(x, y uint32) uint32 { + // TODO: are there double-rounding problems here? See issue 48807. return f64to32(fdiv64(f32to64(x), f32to64(y))) } @@ -527,7 +547,7 @@ func fge64(x, y uint64) bool { } func fint32to32(x int32) uint32 { - return f64to32(fintto64(int64(x))) + return fintto32(int64(x)) } func fint32to64(x int32) uint64 { @@ -535,7 +555,7 @@ func fint32to64(x int32) uint64 { } func fint64to32(x int64) uint32 { - return f64to32(fintto64(x)) + return fintto32(x) } func fint64to64(x int64) uint64 { @@ -562,36 +582,46 @@ func f64toint64(x uint64) int64 { return val } -func f64touint64(x float64) uint64 { - if x < float64(1<<63) { - return uint64(int64(x)) +func f64touint64(x uint64) uint64 { + var m uint64 = 0x43e0000000000000 // float64 1<<63 + if fgt64(m, x) { + return uint64(f64toint64(x)) } - y := x - float64(1<<63) - z := uint64(int64(y)) + y := fadd64(x, -m) + z := uint64(f64toint64(y)) return z | (1 << 63) } -func f32touint64(x float32) uint64 { - if x < float32(1<<63) { - return uint64(int64(x)) +func f32touint64(x uint32) uint64 { + var m uint32 = 0x5f000000 // float32 1<<63 + if fgt32(m, x) { + return uint64(f32toint64(x)) } - y := x - float32(1<<63) - z := uint64(int64(y)) + y := fadd32(x, -m) + z := uint64(f32toint64(y)) return z | (1 << 63) } -func fuint64to64(x uint64) float64 { +func fuint64to64(x uint64) uint64 { if int64(x) >= 0 { - return float64(int64(x)) + return fint64to64(int64(x)) } - // See ../cmd/compile/internal/gc/ssa.go:uint64Tofloat + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat y := x & 1 z := x >> 1 z = z | y - r := float64(int64(z)) - return r + r + r := fint64to64(int64(z)) + return fadd64(r, r) } -func fuint64to32(x uint64) float32 { - return float32(fuint64to64(x)) +func fuint64to32(x uint64) uint32 { + if int64(x) >= 0 { + return fint64to32(int64(x)) + } + // See ../cmd/compile/internal/ssagen/ssa.go:uint64Tofloat + y := x & 1 + z := x >> 1 + z = z | y + r := fint64to32(int64(z)) + return fadd32(r, r) } diff --git a/src/runtime/softfloat64_test.go b/src/runtime/softfloat64_test.go index 7347aff8f13c7c..3f53e8bc55810c 100644 --- a/src/runtime/softfloat64_test.go +++ b/src/runtime/softfloat64_test.go @@ -127,7 +127,7 @@ func fromint64sw(f float64) float64 { var nerr int -func err(t *testing.T, format string, args ...interface{}) { +func err(t *testing.T, format string, args ...any) { t.Errorf(format, args...) // cut errors off after a while. diff --git a/src/runtime/stack.go b/src/runtime/stack.go index 6e0d157630b47a..10c45045d90345 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -7,6 +7,8 @@ package runtime import ( "internal/abi" "internal/cpu" + "internal/goarch" + "internal/goos" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -67,7 +69,7 @@ const ( // to each stack below the usual guard area for OS-specific // purposes like signal handling. Used on Windows, Plan 9, // and iOS because they do not use a separate stack. - _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + sys.GoosIos*sys.GoarchArm64*1024 + _StackSystem = goos.IsWindows*512*goarch.PtrSize + goos.IsPlan9*512 + goos.IsIos*goarch.IsArm64*1024 // The minimum size of stack used by Go code _StackMin = 2048 @@ -125,7 +127,7 @@ const ( ) const ( - uintptrMask = 1<<(8*sys.PtrSize) - 1 + uintptrMask = 1<<(8*goarch.PtrSize) - 1 // The values below can be stored to g.stackguard0 to force // the next stack check to fail. @@ -142,19 +144,24 @@ const ( // Force a stack movement. Used for debugging. // 0xfffffeed in hex. stackForceMove = uintptrMask & -275 + + // stackPoisonMin is the lowest allowed stack poison value. + stackPoisonMin = uintptrMask & -4096 ) // Global pool of spans that have free stacks. // Stacks are assigned an order according to size. -// order = log_2(size/FixedStack) +// +// order = log_2(size/FixedStack) +// // There is a free list for each order. var stackpool [_NumStackOrders]struct { item stackpoolItem - _ [cpu.CacheLinePadSize - unsafe.Sizeof(stackpoolItem{})%cpu.CacheLinePadSize]byte + _ [(cpu.CacheLinePadSize - unsafe.Sizeof(stackpoolItem{})%cpu.CacheLinePadSize) % cpu.CacheLinePadSize]byte } -//go:notinheap type stackpoolItem struct { + _ sys.NotInHeap mu mutex span mSpanList } @@ -422,6 +429,9 @@ func stackalloc(n uint32) stack { if msanenabled { msanmalloc(v, uintptr(n)) } + if asanenabled { + asanunpoison(v, uintptr(n)) + } if stackDebug >= 1 { print(" allocated ", v, "\n") } @@ -459,6 +469,9 @@ func stackfree(stk stack) { if msanenabled { msanfree(v, n) } + if asanenabled { + asanpoison(v, n) + } if n < _FixedStack<<_NumStackOrders && n < _StackCacheSize { order := uint8(0) n2 := n @@ -599,14 +612,14 @@ func adjustpointers(scanp unsafe.Pointer, bv *bitvector, adjinfo *adjustinfo, f for i := uintptr(0); i < num; i += 8 { if stackDebug >= 4 { for j := uintptr(0); j < 8; j++ { - print(" ", add(scanp, (i+j)*sys.PtrSize), ":", ptrnames[bv.ptrbit(i+j)], ":", hex(*(*uintptr)(add(scanp, (i+j)*sys.PtrSize))), " # ", i, " ", *addb(bv.bytedata, i/8), "\n") + print(" ", add(scanp, (i+j)*goarch.PtrSize), ":", ptrnames[bv.ptrbit(i+j)], ":", hex(*(*uintptr)(add(scanp, (i+j)*goarch.PtrSize))), " # ", i, " ", *addb(bv.bytedata, i/8), "\n") } } b := *(addb(bv.bytedata, i/8)) for b != 0 { j := uintptr(sys.Ctz8(b)) b &= b - 1 - pp := (*uintptr)(add(scanp, (i+j)*sys.PtrSize)) + pp := (*uintptr)(add(scanp, (i+j)*goarch.PtrSize)) retry: p := *pp if f.valid() && 0 < p && p < minLegalPointer && debug.invalidptr != 0 { @@ -651,17 +664,17 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { return true } - locals, args, objs := getStackMap(frame, &adjinfo.cache, true) + locals, args, objs := frame.getStackMap(&adjinfo.cache, true) // Adjust local variables if stack frame has been allocated. if locals.n > 0 { - size := uintptr(locals.n) * sys.PtrSize + size := uintptr(locals.n) * goarch.PtrSize adjustpointers(unsafe.Pointer(frame.varp-size), &locals, adjinfo, f) } // Adjust saved base pointer if there is one. // TODO what about arm64 frame pointer adjustment? - if sys.ArchFamily == sys.AMD64 && frame.argp-frame.varp == 2*sys.PtrSize { + if goarch.ArchFamily == goarch.AMD64 && frame.argp-frame.varp == 2*goarch.PtrSize { if stackDebug >= 3 { print(" saved bp\n") } @@ -689,7 +702,8 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { // Adjust pointers in all stack objects (whether they are live or not). // See comments in mgcmark.go:scanframeworker. if frame.varp != 0 { - for _, obj := range objs { + for i := range objs { + obj := &objs[i] off := obj.off base := frame.varp // locals base pointer if off >= 0 { @@ -703,15 +717,15 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { continue } ptrdata := obj.ptrdata() - gcdata := obj.gcdata + gcdata := obj.gcdata() var s *mspan if obj.useGCProg() { // See comments in mgcmark.go:scanstack s = materializeGCProg(ptrdata, gcdata) gcdata = (*byte)(unsafe.Pointer(s.startAddr)) } - for i := uintptr(0); i < ptrdata; i += sys.PtrSize { - if *addb(gcdata, i/(8*sys.PtrSize))>>(i/sys.PtrSize&7)&1 != 0 { + for i := uintptr(0); i < ptrdata; i += goarch.PtrSize { + if *addb(gcdata, i/(8*goarch.PtrSize))>>(i/goarch.PtrSize&7)&1 != 0 { adjustpointer(adjinfo, unsafe.Pointer(p+i)) } } @@ -753,11 +767,6 @@ func adjustdefers(gp *g, adjinfo *adjustinfo) { adjustpointer(adjinfo, unsafe.Pointer(&d.varp)) adjustpointer(adjinfo, unsafe.Pointer(&d.fd)) } - - // Adjust defer argument blocks the same way we adjust active stack frames. - // Note: this code is after the loop above, so that if a defer record is - // stack allocated, we work on the copy in the new stack. - tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo))) } func adjustpanics(gp *g, adjinfo *adjustinfo) { @@ -854,6 +863,11 @@ func copystack(gp *g, newsize uintptr) { throw("nil stackbase") } used := old.hi - gp.sched.sp + // Add just the difference to gcController.addScannableStack. + // g0 stacks never move, so this will never account for them. + // It's also fine if we have no P, addScannableStack can deal with + // that case. + gcController.addScannableStack(getg().m.p.ptr(), int64(newsize)-int64(old.hi-old.lo)) // allocate new stack new := stackalloc(uint32(newsize)) @@ -872,7 +886,7 @@ func copystack(gp *g, newsize uintptr) { // Adjust sudogs, synchronizing with channel ops if necessary. ncopy := used if !gp.activeStackChans { - if newsize < old.hi-old.lo && atomic.Load8(&gp.parkingOnChan) != 0 { + if newsize < old.hi-old.lo && gp.parkingOnChan.Load() { // It's not safe for someone to shrink this stack while we're actively // parking on a channel, but it is safe to grow since we do that // ourselves and explicitly don't want to synchronize with channels @@ -969,7 +983,7 @@ func newstack() { f := findfunc(gp.sched.pc) if f.valid() { pcname = funcname(f) - pcoff = gp.sched.pc - f.entry + pcoff = gp.sched.pc - f.entry() } print("runtime: newstack at ", pcname, "+", hex(pcoff), " sp=", hex(gp.sched.sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n", @@ -990,7 +1004,7 @@ func newstack() { // NOTE: stackguard0 may change underfoot, if another thread // is about to try to preempt gp. Read it just once and use that same // value now and below. - preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt + stackguard0 := atomic.Loaduintptr(&gp.stackguard0) // Be conservative about where we preempt. // We are interested in preempting user Go code, not runtime code. @@ -1004,6 +1018,7 @@ func newstack() { // If the GC is in some way dependent on this goroutine (for example, // it needs a lock held by the goroutine), that small preemption turns // into a real deadlock. + preempt := stackguard0 == stackPreempt if preempt { if !canPreemptM(thisg.m) { // Let the goroutine keep running for now. @@ -1017,9 +1032,9 @@ func newstack() { throw("missing stack in newstack") } sp := gp.sched.sp - if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 || sys.ArchFamily == sys.WASM { + if goarch.ArchFamily == goarch.AMD64 || goarch.ArchFamily == goarch.I386 || goarch.ArchFamily == goarch.WASM { // The call to morestack cost a word. - sp -= sys.PtrSize + sp -= goarch.PtrSize } if stackDebug >= 1 || sp < gp.stack.lo { print("runtime: newstack sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n", @@ -1071,7 +1086,7 @@ func newstack() { } } - if gp.stackguard0 == stackForceMove { + if stackguard0 == stackForceMove { // Forced stack movement used for debugging. // Don't double the stack (or we may quickly run out // if this is done repeatedly). @@ -1114,7 +1129,7 @@ func gostartcallfn(gobuf *gobuf, fv *funcval) { if fv != nil { fn = unsafe.Pointer(fv.fn) } else { - fn = unsafe.Pointer(funcPC(nilfunc)) + fn = unsafe.Pointer(abi.FuncPCABIInternal(nilfunc)) } gostartcall(gobuf, fn, unsafe.Pointer(fv)) } @@ -1135,7 +1150,7 @@ func isShrinkStackSafe(gp *g) bool { // We also can't *shrink* the stack in the window between the // goroutine calling gopark to park on a channel and // gp.activeStackChans being set. - return gp.syscallsp == 0 && !gp.asyncSafePoint && atomic.Load8(&gp.parkingOnChan) == 0 + return gp.syscallsp == 0 && !gp.asyncSafePoint && !gp.parkingOnChan.Load() } // Maybe shrink the stack being used by gp. @@ -1201,7 +1216,6 @@ func shrinkstack(gp *g) { // freeStackSpans frees unused stack spans at the end of GC. func freeStackSpans() { - // Scan stack pools for empty stack spans. for order := range stackpool { lock(&stackpool[order].item.mu) @@ -1233,145 +1247,16 @@ func freeStackSpans() { unlock(&stackLarge.lock) } -// getStackMap returns the locals and arguments live pointer maps, and -// stack object list for frame. -func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) { - targetpc := frame.continpc - if targetpc == 0 { - // Frame is dead. Return empty bitvectors. - return - } - - f := frame.fn - pcdata := int32(-1) - if targetpc != f.entry { - // Back up to the CALL. If we're at the function entry - // point, we want to use the entry map (-1), even if - // the first instruction of the function changes the - // stack map. - targetpc-- - pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache) - } - if pcdata == -1 { - // We do not have a valid pcdata value but there might be a - // stackmap for this function. It is likely that we are looking - // at the function prologue, assume so and hope for the best. - pcdata = 0 - } - - // Local variables. - size := frame.varp - frame.sp - var minsize uintptr - switch sys.ArchFamily { - case sys.ARM64: - minsize = sys.StackAlign - default: - minsize = sys.MinFrameSize - } - if size > minsize { - stackid := pcdata - stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) - if stkmap == nil || stkmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") - throw("missing stackmap") - } - // If nbit == 0, there's no work to do. - if stkmap.nbit > 0 { - if stackid < 0 || stackid >= stkmap.n { - // don't know where we are - print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") - throw("bad symbol table") - } - locals = stackmapdata(stkmap, stackid) - if stackDebug >= 3 && debug { - print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n") - } - } else if stackDebug >= 3 && debug { - print(" no locals to adjust\n") - } - } - - // Arguments. - if frame.arglen > 0 { - if frame.argmap != nil { - // argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall. - // In this case, arglen specifies how much of the args section is actually live. - // (It could be either all the args + results, or just the args.) - args = *frame.argmap - n := int32(frame.arglen / sys.PtrSize) - if n < args.n { - args.n = n // Don't use more of the arguments than arglen. - } - } else { - stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) - if stackmap == nil || stackmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(frame.arglen), "\n") - throw("missing stackmap") - } - if pcdata < 0 || pcdata >= stackmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") - throw("bad symbol table") - } - if stackmap.nbit > 0 { - args = stackmapdata(stackmap, pcdata) - } - } - } - - // stack objects. - if GOARCH == "amd64" && unsafe.Sizeof(abi.RegArgs{}) > 0 && frame.argmap != nil { - // argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall. - // We don't actually use argmap in this case, but we need to fake the stack object - // record for these frames which contain an internal/abi.RegArgs at a hard-coded offset - // on amd64. - objs = methodValueCallFrameObjs - } else { - p := funcdata(f, _FUNCDATA_StackObjects) - if p != nil { - n := *(*uintptr)(p) - p = add(p, sys.PtrSize) - *(*slice)(unsafe.Pointer(&objs)) = slice{array: noescape(p), len: int(n), cap: int(n)} - // Note: the noescape above is needed to keep - // getStackMap from "leaking param content: - // frame". That leak propagates up to getgcmask, then - // GCMask, then verifyGCInfo, which converts the stack - // gcinfo tests into heap gcinfo tests :( - } - } - - return -} - -var ( - abiRegArgsEface interface{} = abi.RegArgs{} - abiRegArgsType *_type = efaceOf(&abiRegArgsEface)._type - methodValueCallFrameObjs = []stackObjectRecord{ - { - off: -int32(alignUp(abiRegArgsType.size, 8)), // It's always the highest address local. - size: int32(abiRegArgsType.size), - _ptrdata: int32(abiRegArgsType.ptrdata), - gcdata: abiRegArgsType.gcdata, - }, - } -) - -func init() { - if abiRegArgsType.kind&kindGCProg != 0 { - throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs") - } -} - // A stackObjectRecord is generated by the compiler for each stack object in a stack frame. // This record must match the generator code in cmd/compile/internal/liveness/plive.go:emitStackObjects. type stackObjectRecord struct { // offset in frame // if negative, offset from varp // if non-negative, offset from argp - off int32 - size int32 - _ptrdata int32 // ptrdata, or -ptrdata is GC prog is used - gcdata *byte // pointer map or GC prog of the type + off int32 + size int32 + _ptrdata int32 // ptrdata, or -ptrdata is GC prog is used + gcdataoff uint32 // offset to gcdata from moduledata.rodata } func (r *stackObjectRecord) useGCProg() bool { @@ -1386,6 +1271,23 @@ func (r *stackObjectRecord) ptrdata() uintptr { return uintptr(x) } +// gcdata returns pointer map or GC prog of the type. +func (r *stackObjectRecord) gcdata() *byte { + ptr := uintptr(unsafe.Pointer(r)) + var mod *moduledata + for datap := &firstmoduledata; datap != nil; datap = datap.next { + if datap.gofunc <= ptr && ptr < datap.end { + mod = datap + break + } + } + // If you get a panic here due to a nil mod, + // you may have made a copy of a stackObjectRecord. + // You must use the original pointer. + res := mod.rodata + uintptr(r.gcdataoff) + return (*byte)(unsafe.Pointer(res)) +} + // This is exported as ABI0 via linkname so obj can call it. // //go:nosplit @@ -1393,3 +1295,49 @@ func (r *stackObjectRecord) ptrdata() uintptr { func morestackc() { throw("attempt to execute system stack code on user stack") } + +// startingStackSize is the amount of stack that new goroutines start with. +// It is a power of 2, and between _FixedStack and maxstacksize, inclusive. +// startingStackSize is updated every GC by tracking the average size of +// stacks scanned during the GC. +var startingStackSize uint32 = _FixedStack + +func gcComputeStartingStackSize() { + if debug.adaptivestackstart == 0 { + return + } + // For details, see the design doc at + // https://docs.google.com/document/d/1YDlGIdVTPnmUiTAavlZxBI1d9pwGQgZT7IKFKlIXohQ/edit?usp=sharing + // The basic algorithm is to track the average size of stacks + // and start goroutines with stack equal to that average size. + // Starting at the average size uses at most 2x the space that + // an ideal algorithm would have used. + // This is just a heuristic to avoid excessive stack growth work + // early in a goroutine's lifetime. See issue 18138. Stacks that + // are allocated too small can still grow, and stacks allocated + // too large can still shrink. + var scannedStackSize uint64 + var scannedStacks uint64 + for _, p := range allp { + scannedStackSize += p.scannedStackSize + scannedStacks += p.scannedStacks + // Reset for next time + p.scannedStackSize = 0 + p.scannedStacks = 0 + } + if scannedStacks == 0 { + startingStackSize = _FixedStack + return + } + avg := scannedStackSize/scannedStacks + _StackGuard + // Note: we add _StackGuard to ensure that a goroutine that + // uses the average space will not trigger a growth. + if avg > uint64(maxstacksize) { + avg = uint64(maxstacksize) + } + if avg < _FixedStack { + avg = _FixedStack + } + // Note: maxstacksize fits in 30 bits, so avg also does. + startingStackSize = uint32(round2(int32(avg))) +} diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index 43fc5cac55fba1..92d58803fc306f 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -5,13 +5,10 @@ package runtime_test import ( - "bytes" "fmt" - "os" "reflect" "regexp" . "runtime" - "strconv" "strings" "sync" "sync/atomic" @@ -83,12 +80,7 @@ func TestStackGrowth(t *testing.T) { t.Skip("-quick") } - if GOARCH == "wasm" { - t.Skip("fails on wasm (too slow?)") - } - - // Don't make this test parallel as this makes the 20 second - // timeout unreliable on slow builders. (See issue #19381.) + t.Parallel() var wg sync.WaitGroup @@ -102,6 +94,7 @@ func TestStackGrowth(t *testing.T) { growDuration = time.Since(start) }() wg.Wait() + t.Log("first growStack took", growDuration) // in locked goroutine wg.Add(1) @@ -114,48 +107,39 @@ func TestStackGrowth(t *testing.T) { wg.Wait() // in finalizer + var finalizerStart time.Time + var started atomic.Bool + var progress atomic.Uint32 wg.Add(1) - go func() { + s := new(string) // Must be of a type that avoids the tiny allocator, or else the finalizer might not run. + SetFinalizer(s, func(ss *string) { defer wg.Done() - done := make(chan bool) - var startTime time.Time - var started, progress uint32 - go func() { - s := new(string) - SetFinalizer(s, func(ss *string) { - startTime = time.Now() - atomic.StoreUint32(&started, 1) - growStack(&progress) - done <- true - }) - s = nil - done <- true - }() - <-done - GC() - - timeout := 20 * time.Second - if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { - scale, err := strconv.Atoi(s) - if err == nil { - timeout *= time.Duration(scale) - } - } - - select { - case <-done: - case <-time.After(timeout): - if atomic.LoadUint32(&started) == 0 { - t.Log("finalizer did not start") + finalizerStart = time.Now() + started.Store(true) + growStack(&progress) + }) + setFinalizerTime := time.Now() + s = nil + + if d, ok := t.Deadline(); ok { + // Pad the timeout by an arbitrary 5% to give the AfterFunc time to run. + timeout := time.Until(d) * 19 / 20 + timer := time.AfterFunc(timeout, func() { + // Panic — instead of calling t.Error and returning from the test — so + // that we get a useful goroutine dump if the test times out, especially + // if GOTRACEBACK=system or GOTRACEBACK=crash is set. + if !started.Load() { + panic("finalizer did not start") } else { - t.Logf("finalizer started %s ago and finished %d iterations", time.Since(startTime), atomic.LoadUint32(&progress)) + panic(fmt.Sprintf("finalizer started %s ago (%s after registration) and ran %d iterations, but did not return", time.Since(finalizerStart), finalizerStart.Sub(setFinalizerTime), progress.Load())) } - t.Log("first growStack took", growDuration) - t.Error("finalizer did not run") - return - } - }() + }) + defer timer.Stop() + } + + GC() wg.Wait() + t.Logf("finalizer started after %s and ran %d iterations in %v", finalizerStart.Sub(setFinalizerTime), progress.Load(), time.Since(finalizerStart)) } // ... and in init @@ -163,7 +147,7 @@ func TestStackGrowth(t *testing.T) { // growStack() //} -func growStack(progress *uint32) { +func growStack(progress *atomic.Uint32) { n := 1 << 10 if testing.Short() { n = 1 << 8 @@ -175,7 +159,7 @@ func growStack(progress *uint32) { panic("stack is corrupted") } if progress != nil { - atomic.StoreUint32(progress, uint32(i)) + progress.Store(uint32(i)) } } GC() @@ -585,6 +569,67 @@ func count21(n int) int { return 1 + count22(n-1) } func count22(n int) int { return 1 + count23(n-1) } func count23(n int) int { return 1 + count1(n-1) } +type stkobjT struct { + p *stkobjT + x int64 + y [20]int // consume some stack +} + +// Sum creates a linked list of stkobjTs. +func Sum(n int64, p *stkobjT) { + if n == 0 { + return + } + s := stkobjT{p: p, x: n} + Sum(n-1, &s) + p.x += s.x +} + +func BenchmarkStackCopyWithStkobj(b *testing.B) { + c := make(chan bool) + for i := 0; i < b.N; i++ { + go func() { + var s stkobjT + Sum(100000, &s) + c <- true + }() + <-c + } +} + +func BenchmarkIssue18138(b *testing.B) { + // Channel with N "can run a goroutine" tokens + const N = 10 + c := make(chan []byte, N) + for i := 0; i < N; i++ { + c <- make([]byte, 1) + } + + for i := 0; i < b.N; i++ { + <-c // get token + go func() { + useStackPtrs(1000, false) // uses ~1MB max + m := make([]byte, 8192) // make GC trigger occasionally + c <- m // return token + }() + } +} + +func useStackPtrs(n int, b bool) { + if b { + // This code contributes to the stack frame size, and hence to the + // stack copying cost. But since b is always false, it costs no + // execution time (not even the zeroing of a). + var a [128]*int // 1KB of pointers + a[n] = &n + n = *a[0] + } + if n == 0 { + return + } + useStackPtrs(n-1, b) +} + type structWithMethod struct{} func (s structWithMethod) caller() string { @@ -732,7 +777,7 @@ func TestTracebackSystemstack(t *testing.T) { // and that we see TestTracebackSystemstack. countIn, countOut := 0, 0 frames := CallersFrames(pcs) - var tb bytes.Buffer + var tb strings.Builder for { frame, more := frames.Next() fmt.Fprintf(&tb, "\n%s+0x%x %s:%d", frame.Function, frame.PC-frame.Entry, frame.File, frame.Line) @@ -851,7 +896,7 @@ func deferHeapAndStack(n int) (r int) { } // Pass a value to escapeMe to force it to escape. -var escapeMe = func(x interface{}) {} +var escapeMe = func(x any) {} // Test that when F -> G is inlined and F is excluded from stack // traces, G still appears. diff --git a/src/runtime/stkframe.go b/src/runtime/stkframe.go new file mode 100644 index 00000000000000..3ecf3a828c27e7 --- /dev/null +++ b/src/runtime/stkframe.go @@ -0,0 +1,289 @@ +// Copyright 2022 The Go Authors. 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 ( + "internal/abi" + "internal/goarch" + "runtime/internal/sys" + "unsafe" +) + +// A stkframe holds information about a single physical stack frame. +type stkframe struct { + // fn is the function being run in this frame. If there is + // inlining, this is the outermost function. + fn funcInfo + + // pc is the program counter within fn. + // + // The meaning of this is subtle: + // + // - Typically, this frame performed a regular function call + // and this is the return PC (just after the CALL + // instruction). In this case, pc-1 reflects the CALL + // instruction itself and is the correct source of symbolic + // information. + // + // - If this frame "called" sigpanic, then pc is the + // instruction that panicked, and pc is the correct address + // to use for symbolic information. + // + // - If this is the innermost frame, then PC is where + // execution will continue, but it may not be the + // instruction following a CALL. This may be from + // cooperative preemption, in which case this is the + // instruction after the call to morestack. Or this may be + // from a signal or an un-started goroutine, in which case + // PC could be any instruction, including the first + // instruction in a function. Conventionally, we use pc-1 + // for symbolic information, unless pc == fn.entry(), in + // which case we use pc. + pc uintptr + + // continpc is the PC where execution will continue in fn, or + // 0 if execution will not continue in this frame. + // + // This is usually the same as pc, unless this frame "called" + // sigpanic, in which case it's either the address of + // deferreturn or 0 if this frame will never execute again. + // + // This is the PC to use to look up GC liveness for this frame. + continpc uintptr + + lr uintptr // program counter at caller aka link register + sp uintptr // stack pointer at pc + fp uintptr // stack pointer at caller aka frame pointer + varp uintptr // top of local variables + argp uintptr // pointer to function arguments +} + +// reflectMethodValue is a partial duplicate of reflect.makeFuncImpl +// and reflect.methodValue. +type reflectMethodValue struct { + fn uintptr + stack *bitvector // ptrmap for both args and results + argLen uintptr // just args +} + +// argBytes returns the argument frame size for a call to frame.fn. +func (frame *stkframe) argBytes() uintptr { + if frame.fn.args != _ArgsSizeUnknown { + return uintptr(frame.fn.args) + } + // This is an uncommon and complicated case. Fall back to fully + // fetching the argument map to compute its size. + argMap, _ := frame.argMapInternal() + return uintptr(argMap.n) * goarch.PtrSize +} + +// argMapInternal is used internally by stkframe to fetch special +// argument maps. +// +// argMap.n is always populated with the size of the argument map. +// +// argMap.bytedata is only populated for dynamic argument maps (used +// by reflect). If the caller requires the argument map, it should use +// this if non-nil, and otherwise fetch the argument map using the +// current PC. +// +// hasReflectStackObj indicates that this frame also has a reflect +// function stack object, which the caller must synthesize. +func (frame *stkframe) argMapInternal() (argMap bitvector, hasReflectStackObj bool) { + f := frame.fn + if f.args != _ArgsSizeUnknown { + argMap.n = f.args / goarch.PtrSize + return + } + // Extract argument bitmaps for reflect stubs from the calls they made to reflect. + switch funcname(f) { + case "reflect.makeFuncStub", "reflect.methodValueCall": + // These take a *reflect.methodValue as their + // context register and immediately save it to 0(SP). + // Get the methodValue from 0(SP). + arg0 := frame.sp + sys.MinFrameSize + + minSP := frame.fp + if !usesLR { + // The CALL itself pushes a word. + // Undo that adjustment. + minSP -= goarch.PtrSize + } + if arg0 >= minSP { + // The function hasn't started yet. + // This only happens if f was the + // start function of a new goroutine + // that hasn't run yet *and* f takes + // no arguments and has no results + // (otherwise it will get wrapped in a + // closure). In this case, we can't + // reach into its locals because it + // doesn't have locals yet, but we + // also know its argument map is + // empty. + if frame.pc != f.entry() { + print("runtime: confused by ", funcname(f), ": no frame (sp=", hex(frame.sp), " fp=", hex(frame.fp), ") at entry+", hex(frame.pc-f.entry()), "\n") + throw("reflect mismatch") + } + return bitvector{}, false // No locals, so also no stack objects + } + hasReflectStackObj = true + mv := *(**reflectMethodValue)(unsafe.Pointer(arg0)) + // Figure out whether the return values are valid. + // Reflect will update this value after it copies + // in the return values. + retValid := *(*bool)(unsafe.Pointer(arg0 + 4*goarch.PtrSize)) + if mv.fn != f.entry() { + print("runtime: confused by ", funcname(f), "\n") + throw("reflect mismatch") + } + argMap = *mv.stack + if !retValid { + // argMap.n includes the results, but + // those aren't valid, so drop them. + n := int32((uintptr(mv.argLen) &^ (goarch.PtrSize - 1)) / goarch.PtrSize) + if n < argMap.n { + argMap.n = n + } + } + } + return +} + +// getStackMap returns the locals and arguments live pointer maps, and +// stack object list for frame. +func (frame *stkframe) getStackMap(cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) { + targetpc := frame.continpc + if targetpc == 0 { + // Frame is dead. Return empty bitvectors. + return + } + + f := frame.fn + pcdata := int32(-1) + if targetpc != f.entry() { + // Back up to the CALL. If we're at the function entry + // point, we want to use the entry map (-1), even if + // the first instruction of the function changes the + // stack map. + targetpc-- + pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache) + } + if pcdata == -1 { + // We do not have a valid pcdata value but there might be a + // stackmap for this function. It is likely that we are looking + // at the function prologue, assume so and hope for the best. + pcdata = 0 + } + + // Local variables. + size := frame.varp - frame.sp + var minsize uintptr + switch goarch.ArchFamily { + case goarch.ARM64: + minsize = sys.StackAlign + default: + minsize = sys.MinFrameSize + } + if size > minsize { + stackid := pcdata + stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) + if stkmap == nil || stkmap.n <= 0 { + print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") + throw("missing stackmap") + } + // If nbit == 0, there's no work to do. + if stkmap.nbit > 0 { + if stackid < 0 || stackid >= stkmap.n { + // don't know where we are + print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") + throw("bad symbol table") + } + locals = stackmapdata(stkmap, stackid) + if stackDebug >= 3 && debug { + print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n") + } + } else if stackDebug >= 3 && debug { + print(" no locals to adjust\n") + } + } + + // Arguments. First fetch frame size and special-case argument maps. + var isReflect bool + args, isReflect = frame.argMapInternal() + if args.n > 0 && args.bytedata == nil { + // Non-empty argument frame, but not a special map. + // Fetch the argument map at pcdata. + stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) + if stackmap == nil || stackmap.n <= 0 { + print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(args.n*goarch.PtrSize), "\n") + throw("missing stackmap") + } + if pcdata < 0 || pcdata >= stackmap.n { + // don't know where we are + print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") + throw("bad symbol table") + } + if stackmap.nbit == 0 { + args.n = 0 + } else { + args = stackmapdata(stackmap, pcdata) + } + } + + // stack objects. + if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && + unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect { + // For reflect.makeFuncStub and reflect.methodValueCall, + // we need to fake the stack object record. + // These frames contain an internal/abi.RegArgs at a hard-coded offset. + // This offset matches the assembly code on amd64 and arm64. + objs = methodValueCallFrameObjs[:] + } else { + p := funcdata(f, _FUNCDATA_StackObjects) + if p != nil { + n := *(*uintptr)(p) + p = add(p, goarch.PtrSize) + r0 := (*stackObjectRecord)(noescape(p)) + objs = unsafe.Slice(r0, int(n)) + // Note: the noescape above is needed to keep + // getStackMap from "leaking param content: + // frame". That leak propagates up to getgcmask, then + // GCMask, then verifyGCInfo, which converts the stack + // gcinfo tests into heap gcinfo tests :( + } + } + + return +} + +var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit + +func stkobjinit() { + var abiRegArgsEface any = abi.RegArgs{} + abiRegArgsType := efaceOf(&abiRegArgsEface)._type + if abiRegArgsType.kind&kindGCProg != 0 { + throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs") + } + // Set methodValueCallFrameObjs[0].gcdataoff so that + // stackObjectRecord.gcdata() will work correctly with it. + ptr := uintptr(unsafe.Pointer(&methodValueCallFrameObjs[0])) + var mod *moduledata + for datap := &firstmoduledata; datap != nil; datap = datap.next { + if datap.gofunc <= ptr && ptr < datap.end { + mod = datap + break + } + } + if mod == nil { + throw("methodValueCallFrameObjs is not in a module") + } + methodValueCallFrameObjs[0] = stackObjectRecord{ + off: -int32(alignUp(abiRegArgsType.size, 8)), // It's always the highest address local. + size: int32(abiRegArgsType.size), + _ptrdata: int32(abiRegArgsType.ptrdata), + gcdataoff: uint32(uintptr(unsafe.Pointer(abiRegArgsType.gcdata)) - mod.rodata), + } +} diff --git a/src/runtime/string.go b/src/runtime/string.go index d6030a1dca433b..359a5658c5acf7 100644 --- a/src/runtime/string.go +++ b/src/runtime/string.go @@ -5,8 +5,9 @@ package runtime import ( + "internal/abi" "internal/bytealg" - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -88,14 +89,17 @@ func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) { racereadrangepc(unsafe.Pointer(ptr), uintptr(n), getcallerpc(), - funcPC(slicebytetostring)) + abi.FuncPCABIInternal(slicebytetostring)) } if msanenabled { msanread(unsafe.Pointer(ptr), uintptr(n)) } + if asanenabled { + asanread(unsafe.Pointer(ptr), uintptr(n)) + } if n == 1 { p := unsafe.Pointer(&staticuint64s[*ptr]) - if sys.BigEndian { + if goarch.BigEndian { p = add(p, 7) } stringStructOf(&str).str = p @@ -143,20 +147,23 @@ func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) { // and otherwise intrinsified by the compiler. // // Some internal compiler optimizations use this function. -// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)] -// where k is []byte, T1 to Tn is a nesting of struct and array literals. -// - Used for "<"+string(b)+">" concatenation where b is []byte. -// - Used for string(b)=="foo" comparison where b is []byte. +// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)] +// where k is []byte, T1 to Tn is a nesting of struct and array literals. +// - Used for "<"+string(b)+">" concatenation where b is []byte. +// - Used for string(b)=="foo" comparison where b is []byte. func slicebytetostringtmp(ptr *byte, n int) (str string) { if raceenabled && n > 0 { racereadrangepc(unsafe.Pointer(ptr), uintptr(n), getcallerpc(), - funcPC(slicebytetostringtmp)) + abi.FuncPCABIInternal(slicebytetostringtmp)) } if msanenabled && n > 0 { msanread(unsafe.Pointer(ptr), uintptr(n)) } + if asanenabled && n > 0 { + asanread(unsafe.Pointer(ptr), uintptr(n)) + } stringStructOf(&str).str = unsafe.Pointer(ptr) stringStructOf(&str).len = n return @@ -203,11 +210,14 @@ func slicerunetostring(buf *tmpBuf, a []rune) string { racereadrangepc(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0]), getcallerpc(), - funcPC(slicerunetostring)) + abi.FuncPCABIInternal(slicerunetostring)) } if msanenabled && len(a) > 0 { msanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0])) } + if asanenabled && len(a) > 0 { + asanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0])) + } var dum [4]byte size1 := 0 for _, r := range a { @@ -315,6 +325,7 @@ func gobytes(p *byte, n int) (b []byte) { } // This is exported via linkname to assembly in syscall (for Plan9). +// //go:linkname gostring func gostring(p *byte) string { l := findnull(p) @@ -340,14 +351,14 @@ func hasPrefix(s, prefix string) bool { } const ( - maxUint = ^uint(0) - maxInt = int(maxUint >> 1) + maxUint64 = ^uint64(0) + maxInt64 = int64(maxUint64 >> 1) ) -// atoi parses an int from a string s. +// atoi64 parses an int64 from a string s. // The bool result reports whether s is a number -// representable by a value of type int. -func atoi(s string) (int, bool) { +// representable by a value of type int64. +func atoi64(s string) (int64, bool) { if s == "" { return 0, false } @@ -358,18 +369,18 @@ func atoi(s string) (int, bool) { s = s[1:] } - un := uint(0) + un := uint64(0) for i := 0; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { return 0, false } - if un > maxUint/10 { + if un > maxUint64/10 { // overflow return 0, false } un *= 10 - un1 := un + uint(c) - '0' + un1 := un + uint64(c) - '0' if un1 < un { // overflow return 0, false @@ -377,14 +388,14 @@ func atoi(s string) (int, bool) { un = un1 } - if !neg && un > uint(maxInt) { + if !neg && un > uint64(maxInt64) { return 0, false } - if neg && un > uint(maxInt)+1 { + if neg && un > uint64(maxInt64)+1 { return 0, false } - n := int(un) + n := int64(un) if neg { n = -n } @@ -392,15 +403,108 @@ func atoi(s string) (int, bool) { return n, true } +// atoi is like atoi64 but for integers +// that fit into an int. +func atoi(s string) (int, bool) { + if n, ok := atoi64(s); n == int64(int(n)) { + return int(n), ok + } + return 0, false +} + // atoi32 is like atoi but for integers // that fit into an int32. func atoi32(s string) (int32, bool) { - if n, ok := atoi(s); n == int(int32(n)) { + if n, ok := atoi64(s); n == int64(int32(n)) { return int32(n), ok } return 0, false } +// parseByteCount parses a string that represents a count of bytes. +// +// s must match the following regular expression: +// +// ^[0-9]+(([KMGT]i)?B)?$ +// +// In other words, an integer byte count with an optional unit +// suffix. Acceptable suffixes include one of +// - KiB, MiB, GiB, TiB which represent binary IEC/ISO 80000 units, or +// - B, which just represents bytes. +// +// Returns an int64 because that's what its callers want and receive, +// but the result is always non-negative. +func parseByteCount(s string) (int64, bool) { + // The empty string is not valid. + if s == "" { + return 0, false + } + // Handle the easy non-suffix case. + last := s[len(s)-1] + if last >= '0' && last <= '9' { + n, ok := atoi64(s) + if !ok || n < 0 { + return 0, false + } + return n, ok + } + // Failing a trailing digit, this must always end in 'B'. + // Also at this point there must be at least one digit before + // that B. + if last != 'B' || len(s) < 2 { + return 0, false + } + // The one before that must always be a digit or 'i'. + if c := s[len(s)-2]; c >= '0' && c <= '9' { + // Trivial 'B' suffix. + n, ok := atoi64(s[:len(s)-1]) + if !ok || n < 0 { + return 0, false + } + return n, ok + } else if c != 'i' { + return 0, false + } + // Finally, we need at least 4 characters now, for the unit + // prefix and at least one digit. + if len(s) < 4 { + return 0, false + } + power := 0 + switch s[len(s)-3] { + case 'K': + power = 1 + case 'M': + power = 2 + case 'G': + power = 3 + case 'T': + power = 4 + default: + // Invalid suffix. + return 0, false + } + m := uint64(1) + for i := 0; i < power; i++ { + m *= 1024 + } + n, ok := atoi64(s[:len(s)-3]) + if !ok || n < 0 { + return 0, false + } + un := uint64(n) + if un > maxUint64/m { + // Overflow. + return 0, false + } + un *= m + if un > uint64(maxInt64) { + // Overflow. + return 0, false + } + return int64(un), true +} + //go:nosplit func findnull(s *byte) int { if s == nil { diff --git a/src/runtime/string_test.go b/src/runtime/string_test.go index 4eda12c35ddf38..1ea7f5e481cc0d 100644 --- a/src/runtime/string_test.go +++ b/src/runtime/string_test.go @@ -454,3 +454,126 @@ func TestAtoi32(t *testing.T) { } } } + +func TestParseByteCount(t *testing.T) { + for _, test := range []struct { + in string + out int64 + ok bool + }{ + // Good numeric inputs. + {"1", 1, true}, + {"12345", 12345, true}, + {"012345", 12345, true}, + {"98765432100", 98765432100, true}, + {"9223372036854775807", 1<<63 - 1, true}, + + // Good trivial suffix inputs. + {"1B", 1, true}, + {"12345B", 12345, true}, + {"012345B", 12345, true}, + {"98765432100B", 98765432100, true}, + {"9223372036854775807B", 1<<63 - 1, true}, + + // Good binary suffix inputs. + {"1KiB", 1 << 10, true}, + {"05KiB", 5 << 10, true}, + {"1MiB", 1 << 20, true}, + {"10MiB", 10 << 20, true}, + {"1GiB", 1 << 30, true}, + {"100GiB", 100 << 30, true}, + {"1TiB", 1 << 40, true}, + {"99TiB", 99 << 40, true}, + + // Good zero inputs. + // + // -0 is an edge case, but no harm in supporting it. + {"-0", 0, true}, + {"0", 0, true}, + {"0B", 0, true}, + {"0KiB", 0, true}, + {"0MiB", 0, true}, + {"0GiB", 0, true}, + {"0TiB", 0, true}, + + // Bad inputs. + {"", 0, false}, + {"-1", 0, false}, + {"a12345", 0, false}, + {"a12345B", 0, false}, + {"12345x", 0, false}, + {"0x12345", 0, false}, + + // Bad numeric inputs. + {"9223372036854775808", 0, false}, + {"9223372036854775809", 0, false}, + {"18446744073709551615", 0, false}, + {"20496382327982653440", 0, false}, + {"18446744073709551616", 0, false}, + {"18446744073709551617", 0, false}, + {"9999999999999999999999", 0, false}, + + // Bad trivial suffix inputs. + {"9223372036854775808B", 0, false}, + {"9223372036854775809B", 0, false}, + {"18446744073709551615B", 0, false}, + {"20496382327982653440B", 0, false}, + {"18446744073709551616B", 0, false}, + {"18446744073709551617B", 0, false}, + {"9999999999999999999999B", 0, false}, + + // Bad binary suffix inputs. + {"1Ki", 0, false}, + {"05Ki", 0, false}, + {"10Mi", 0, false}, + {"100Gi", 0, false}, + {"99Ti", 0, false}, + {"22iB", 0, false}, + {"B", 0, false}, + {"iB", 0, false}, + {"KiB", 0, false}, + {"MiB", 0, false}, + {"GiB", 0, false}, + {"TiB", 0, false}, + {"-120KiB", 0, false}, + {"-891MiB", 0, false}, + {"-704GiB", 0, false}, + {"-42TiB", 0, false}, + {"99999999999999999999KiB", 0, false}, + {"99999999999999999MiB", 0, false}, + {"99999999999999GiB", 0, false}, + {"99999999999TiB", 0, false}, + {"555EiB", 0, false}, + + // Mistaken SI suffix inputs. + {"0KB", 0, false}, + {"0MB", 0, false}, + {"0GB", 0, false}, + {"0TB", 0, false}, + {"1KB", 0, false}, + {"05KB", 0, false}, + {"1MB", 0, false}, + {"10MB", 0, false}, + {"1GB", 0, false}, + {"100GB", 0, false}, + {"1TB", 0, false}, + {"99TB", 0, false}, + {"1K", 0, false}, + {"05K", 0, false}, + {"10M", 0, false}, + {"100G", 0, false}, + {"99T", 0, false}, + {"99999999999999999999KB", 0, false}, + {"99999999999999999MB", 0, false}, + {"99999999999999GB", 0, false}, + {"99999999999TB", 0, false}, + {"99999999999TiB", 0, false}, + {"555EB", 0, false}, + } { + out, ok := runtime.ParseByteCount(test.in) + if test.out != out || test.ok != ok { + t.Errorf("parseByteCount(%q) = (%v, %v) want (%v, %v)", + test.in, out, ok, test.out, test.ok) + } + } +} diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index 16d75832029450..929f8fadca81aa 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -6,11 +6,13 @@ package runtime import ( "internal/abi" - "internal/goexperiment" + "internal/goarch" + "runtime/internal/math" "unsafe" ) // Should be a built-in for unsafe.Pointer? +// //go:nosplit func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { return unsafe.Pointer(uintptr(p) + x) @@ -110,6 +112,7 @@ func reflect_memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) { func memmove(to, from unsafe.Pointer, n uintptr) // Outside assembly calls memmove. Make sure it has ABI wrappers. +// //go:linkname memmove //go:linkname reflect_memmove reflect.memmove @@ -118,20 +121,32 @@ func reflect_memmove(to, from unsafe.Pointer, n uintptr) { } // exported value for testing -var hashLoad = float32(loadFactorNum) / float32(loadFactorDen) +const hashLoad = float32(loadFactorNum) / float32(loadFactorDen) //go:nosplit func fastrand() uint32 { mp := getg().m + // Implement wyrand: https://github.com/wangyi-fudan/wyhash + // Only the platform that math.Mul64 can be lowered + // by the compiler should be in this list. + if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| + goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| + goarch.IsS390x|goarch.IsRiscv64 == 1 { + mp.fastrand += 0xa0761d6478bd642f + hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db) + return uint32(hi ^ lo) + } + // Implement xorshift64+: 2 32-bit xorshift sequences added together. // Shift triplet [17,7,16] was calculated as indicated in Marsaglia's // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf // This generator passes the SmallCrush suite, part of TestU01 framework: // http://simul.iro.umontreal.ca/testu01/tu01.html - s1, s0 := mp.fastrand[0], mp.fastrand[1] + t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand)) + s1, s0 := t[0], t[1] s1 ^= s1 << 17 s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 - mp.fastrand[0], mp.fastrand[1] = s0, s1 + t[0], t[1] = s0, s1 return s0 + s1 } @@ -142,16 +157,56 @@ func fastrandn(n uint32) uint32 { return uint32(uint64(fastrand()) * uint64(n) >> 32) } -//go:linkname sync_fastrand sync.fastrand -func sync_fastrand() uint32 { return fastrand() } +func fastrand64() uint64 { + mp := getg().m + // Implement wyrand: https://github.com/wangyi-fudan/wyhash + // Only the platform that math.Mul64 can be lowered + // by the compiler should be in this list. + if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| + goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| + goarch.IsS390x|goarch.IsRiscv64 == 1 { + mp.fastrand += 0xa0761d6478bd642f + hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db) + return hi ^ lo + } + + // Implement xorshift64+: 2 32-bit xorshift sequences added together. + // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf + // This generator passes the SmallCrush suite, part of TestU01 framework: + // http://simul.iro.umontreal.ca/testu01/tu01.html + t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand)) + s1, s0 := t[0], t[1] + s1 ^= s1 << 17 + s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 + r := uint64(s0 + s1) + + s0, s1 = s1, s0 + s1 ^= s1 << 17 + s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 + r += uint64(s0+s1) << 32 + + t[0], t[1] = s0, s1 + return r +} + +func fastrandu() uint { + if goarch.PtrSize == 4 { + return uint(fastrand()) + } + return uint(fastrand64()) +} + +//go:linkname sync_fastrandn sync.fastrandn +func sync_fastrandn(n uint32) uint32 { return fastrandn(n) } -//go:linkname net_fastrand net.fastrand -func net_fastrand() uint32 { return fastrand() } +//go:linkname net_fastrandu net.fastrandu +func net_fastrandu() uint { return fastrandu() } //go:linkname os_fastrand os.fastrand func os_fastrand() uint32 { return fastrand() } // in internal/bytealg/equal_*.s +// //go:noescape func memequal(a, b unsafe.Pointer, size uintptr) bool @@ -160,6 +215,7 @@ func memequal(a, b unsafe.Pointer, size uintptr) bool // output depends on the input. noescape is inlined and currently // compiles down to zero instructions. // USE CAREFULLY! +// //go:nosplit func noescape(p unsafe.Pointer) unsafe.Pointer { x := uintptr(p) @@ -176,8 +232,6 @@ func cgocallback(fn, frame, ctxt uintptr) func gogo(buf *gobuf) -//go:noescape -func jmpdefer(fv *funcval, argp uintptr) func asminit() func setg(gg *g) func breakpoint() @@ -224,6 +278,7 @@ func breakpoint() // Arguments passed through to reflectcall do not escape. The type is used // only in a very limited callee of reflectcall, the stackArgs are copied, and // regArgs is only used in the reflectcall frame. +// //go:noescape func reflectcall(stackArgsType *_type, fn, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs) @@ -421,12 +476,5 @@ func sigpanic0() // structure that is at least large enough to hold the // registers the system supports. // -// Currently it's set to zero because using the actual -// constant will break every part of the toolchain that -// uses finalizers or Windows callbacks to call functions -// The value that is currently commented out there should be -// the actual value once we're ready to use the register ABI -// everywhere. -// // Protected by finlock. -var intArgRegs = abi.IntArgRegs * goexperiment.RegabiArgsInt +var intArgRegs = abi.IntArgRegs diff --git a/src/runtime/stubs2.go b/src/runtime/stubs2.go index 525b324c8163bd..94a888dec6289a 100644 --- a/src/runtime/stubs2.go +++ b/src/runtime/stubs2.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !js && !openbsd && !plan9 && !solaris && !windows -// +build !aix,!darwin,!js,!openbsd,!plan9,!solaris,!windows package runtime @@ -25,6 +24,7 @@ func usleep_no_g(usec uint32) { // write calls the write system call. // It returns a non-negative number of bytes written or a negative errno value. +// //go:noescape func write1(fd uintptr, p unsafe.Pointer, n int32) int32 diff --git a/src/runtime/stubs3.go b/src/runtime/stubs3.go index b895be4c707519..891663b1109b19 100644 --- a/src/runtime/stubs3.go +++ b/src/runtime/stubs3.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !freebsd && !openbsd && !plan9 && !solaris -// +build !aix,!darwin,!freebsd,!openbsd,!plan9,!solaris package runtime diff --git a/src/runtime/stubs_arm64.go b/src/runtime/stubs_arm64.go index f5e3bb4854083c..bd0533d158125a 100644 --- a/src/runtime/stubs_arm64.go +++ b/src/runtime/stubs_arm64.go @@ -14,3 +14,10 @@ func save_g() func asmcgocall_no_g(fn, arg unsafe.Pointer) func emptyfunc() + +// Used by reflectcall and the reflect package. +// +// Spills/loads arguments in registers to/from an internal/abi.RegArgs +// respectively. Does not follow the Go ABI. +func spillArgs() +func unspillArgs() diff --git a/src/runtime/stubs_linux.go b/src/runtime/stubs_linux.go index ba267009ca11f8..2367dc2bd031f8 100644 --- a/src/runtime/stubs_linux.go +++ b/src/runtime/stubs_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package runtime @@ -14,6 +13,7 @@ func sbrk0() uintptr // Called from write_err_android.go only, but defined in sys_linux_*.s; // declared here (instead of in write_err_android.go) for go vet on non-android builds. // The return value is the raw syscall result, which may encode an error number. +// //go:noescape func access(name *byte, mode int32) int32 func connect(fd int32, addr unsafe.Pointer, len int32) int32 diff --git a/src/runtime/stubs_loong64.go b/src/runtime/stubs_loong64.go new file mode 100644 index 00000000000000..22366f508c9d47 --- /dev/null +++ b/src/runtime/stubs_loong64.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package runtime + +// Called from assembly only; declared for go vet. +func load_g() +func save_g() diff --git a/src/runtime/stubs_mips64x.go b/src/runtime/stubs_mips64x.go index 05a4d0d38d5f87..a9ddfc02568cae 100644 --- a/src/runtime/stubs_mips64x.go +++ b/src/runtime/stubs_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package runtime diff --git a/src/runtime/stubs_mipsx.go b/src/runtime/stubs_mipsx.go index 9bffb35b67e920..d48f9b88e82ecf 100644 --- a/src/runtime/stubs_mipsx.go +++ b/src/runtime/stubs_mipsx.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle package runtime diff --git a/src/runtime/stubs_nonlinux.go b/src/runtime/stubs_nonlinux.go index f9b98595fc4403..1a06d7cc1d6dc0 100644 --- a/src/runtime/stubs_nonlinux.go +++ b/src/runtime/stubs_nonlinux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package runtime diff --git a/src/runtime/stubs_ppc64.go b/src/runtime/stubs_ppc64.go index f692947109abbb..6919b748f0c5fb 100644 --- a/src/runtime/stubs_ppc64.go +++ b/src/runtime/stubs_ppc64.go @@ -3,14 +3,10 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package runtime -// Called from assembly only; declared for go vet. -func load_g() -func save_g() -func reginit() - +// This is needed for vet +// //go:noescape func callCgoSigaction(sig uintptr, new, old *sigactiont) int32 diff --git a/src/runtime/stubs_ppc64le.go b/src/runtime/stubs_ppc64le.go deleted file mode 100644 index 5b733136e3b57a..00000000000000 --- a/src/runtime/stubs_ppc64le.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019 The Go Authors. 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 - -// Called from assembly only; declared for go vet. -func load_g() -func save_g() -func reginit() diff --git a/src/runtime/stubs_ppc64x.go b/src/runtime/stubs_ppc64x.go new file mode 100644 index 00000000000000..95e43a5162adb4 --- /dev/null +++ b/src/runtime/stubs_ppc64x.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64le || ppc64 + +package runtime + +// Called from assembly only; declared for go vet. +func load_g() +func save_g() +func reginit() + +// Spills/loads arguments in registers to/from an internal/abi.RegArgs +// respectively. Does not follow the Go ABI. +func spillArgs() +func unspillArgs() diff --git a/src/runtime/stubs_riscv64.go b/src/runtime/stubs_riscv64.go new file mode 100644 index 00000000000000..f6771178718eb7 --- /dev/null +++ b/src/runtime/stubs_riscv64.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. 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 + +// Called from assembly only; declared for go vet. +func load_g() +func save_g() + +// Used by reflectcall and the reflect package. +// +// Spills/loads arguments in registers to/from an internal/abi.RegArgs +// respectively. Does not follow the Go ABI. +func spillArgs() +func unspillArgs() diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 999300a58ec340..79ca5cfc44093b 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -115,9 +116,8 @@ func (ci *Frames) Next() (frame Frame, more bool) { if ix >= 0 { // Note: entry is not modified. It always refers to a real frame, not an inlined one. f = nil - name = funcnameFromNameoff(funcInfo, inltree[ix].func_) - // File/line is already correct. - // TODO: remove file/line from InlinedCall? + name = funcnameFromNameOff(funcInfo, inltree[ix].nameOff) + // File/line from funcline1 below are already correct. } } ci.frames = append(ci.frames, Frame{ @@ -205,7 +205,7 @@ func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr { } lastFuncID = inltree[ix].funcID // Back up to an instruction in the "caller". - tracepc = f.entry + uintptr(inltree[ix].parentPc) + tracepc = f.entry() + uintptr(inltree[ix].parentPc) pc = tracepc + 1 } @@ -271,8 +271,26 @@ func (f *Func) raw() *_func { } func (f *Func) funcInfo() funcInfo { - fn := f.raw() - return funcInfo{fn, findmoduledatap(fn.entry)} + return f.raw().funcInfo() +} + +func (f *_func) funcInfo() funcInfo { + // Find the module containing fn. fn is located in the pclntable. + // The unsafe.Pointer to uintptr conversions and arithmetic + // are safe because we are working with module addresses. + ptr := uintptr(unsafe.Pointer(f)) + var mod *moduledata + for datap := &firstmoduledata; datap != nil; datap = datap.next { + if len(datap.pclntable) == 0 { + continue + } + base := uintptr(unsafe.Pointer(&datap.pclntable[0])) + if base <= ptr && ptr < base+uintptr(len(datap.pclntable)) { + mod = datap + break + } + } + return funcInfo{f, mod} } // PCDATA and FUNCDATA table indexes. @@ -282,6 +300,7 @@ const ( _PCDATA_UnsafePoint = 0 _PCDATA_StackMapIndex = 1 _PCDATA_InlTreeIndex = 2 + _PCDATA_ArgLiveIndex = 3 _FUNCDATA_ArgsPointerMaps = 0 _FUNCDATA_LocalsPointerMaps = 1 @@ -289,6 +308,8 @@ const ( _FUNCDATA_InlTree = 3 _FUNCDATA_OpenCodedDeferInfo = 4 _FUNCDATA_ArgInfo = 5 + _FUNCDATA_ArgLiveInfo = 6 + _FUNCDATA_WrapInfo = 7 _ArgsSizeUnknown = -0x80000000 ) @@ -330,7 +351,6 @@ const ( funcID_gogo funcID_gopanic funcID_handleAsyncEvent - funcID_jmpdefer funcID_mcall funcID_morestack funcID_mstart @@ -365,26 +385,30 @@ const ( // to be an incomplete unwinding of the stack. In certain contexts // (in particular garbage collector stack scans) that is a fatal error. funcFlag_SPWRITE + + // ASM indicates that a function was implemented in assembly. + funcFlag_ASM ) // pcHeader holds data used by the pclntab lookups. type pcHeader struct { - magic uint32 // 0xFFFFFFFA + magic uint32 // 0xFFFFFFF1 pad1, pad2 uint8 // 0,0 minLC uint8 // min instruction size ptrSize uint8 // size of a ptr in bytes nfunc int // number of functions in the module - nfiles uint // number of entries in the file tab. + nfiles uint // number of entries in the file tab + textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text funcnameOffset uintptr // offset to the funcnametab variable from pcHeader cuOffset uintptr // offset to the cutab variable from pcHeader filetabOffset uintptr // offset to the filetab variable from pcHeader - pctabOffset uintptr // offset to the pctab varible from pcHeader + pctabOffset uintptr // offset to the pctab variable from pcHeader pclnOffset uintptr // offset to the pclntab variable from pcHeader } // moduledata records information about the layout of the executable // image. It is written by the linker. Any changes here must be -// matched changes to the code in cmd/internal/ld/symtab.go:symtab. +// matched changes to the code in cmd/link/internal/ld/symtab.go:symtab. // moduledata is stored in statically allocated non-pointer memory; // none of the pointers here are visible to the garbage collector. type moduledata struct { @@ -405,6 +429,8 @@ type moduledata struct { noptrbss, enoptrbss uintptr end, gcdata, gcbss uintptr types, etypes uintptr + rodata uintptr + gofunc uintptr // go.func.* textsectmap []textsect typelinks []int32 // offsets from types @@ -467,6 +493,7 @@ var modulesSlice *[]*moduledata // see activeModules // // This is nosplit/nowritebarrier because it is called by the // cgo pointer checking code. +// //go:nosplit //go:nowritebarrier func activeModules() []*moduledata { @@ -503,8 +530,11 @@ func modulesinit() { } *modules = append(*modules, md) if md.gcdatamask == (bitvector{}) { - md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), md.edata-md.data) - md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss) + scanDataSize := md.edata - md.data + md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), scanDataSize) + scanBSSSize := md.ebss - md.bss + md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), scanBSSSize) + gcController.addGlobals(int64(scanDataSize + scanBSSSize)) } } @@ -529,15 +559,15 @@ func modulesinit() { } type functab struct { - entry uintptr - funcoff uintptr + entryoff uint32 // relative to runtime.text + funcoff uint32 } // Mapping information for secondary text sections type textsect struct { vaddr uintptr // prelinked section vaddr - length uintptr // section length + end uintptr // vaddr + section length baseaddr uintptr // relocated section address } @@ -568,33 +598,28 @@ const debugPcln = false func moduledataverify1(datap *moduledata) { // Check that the pclntab's format is valid. hdr := datap.pcHeader - if hdr.magic != 0xfffffffa || hdr.pad1 != 0 || hdr.pad2 != 0 || hdr.minLC != sys.PCQuantum || hdr.ptrSize != sys.PtrSize { - print("runtime: function symbol table header:", hex(hdr.magic), hex(hdr.pad1), hex(hdr.pad2), hex(hdr.minLC), hex(hdr.ptrSize)) - if datap.pluginpath != "" { - print(", plugin:", datap.pluginpath) - } - println() - throw("invalid function symbol table\n") + if hdr.magic != 0xfffffff1 || hdr.pad1 != 0 || hdr.pad2 != 0 || + hdr.minLC != sys.PCQuantum || hdr.ptrSize != goarch.PtrSize || hdr.textStart != datap.text { + println("runtime: pcHeader: magic=", hex(hdr.magic), "pad1=", hdr.pad1, "pad2=", hdr.pad2, + "minLC=", hdr.minLC, "ptrSize=", hdr.ptrSize, "pcHeader.textStart=", hex(hdr.textStart), + "text=", hex(datap.text), "pluginpath=", datap.pluginpath) + throw("invalid function symbol table") } // ftab is lookup table for function by program counter. nftab := len(datap.ftab) - 1 for i := 0; i < nftab; i++ { // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function. - if datap.ftab[i].entry > datap.ftab[i+1].entry { + if datap.ftab[i].entryoff > datap.ftab[i+1].entryoff { f1 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])), datap} f2 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])), datap} f2name := "end" if i+1 < nftab { f2name = funcname(f2) } - print("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name) - if datap.pluginpath != "" { - print(", plugin:", datap.pluginpath) - } - println() + println("function symbol table not sorted by PC offset:", hex(datap.ftab[i].entryoff), funcname(f1), ">", hex(datap.ftab[i+1].entryoff), f2name, ", plugin:", datap.pluginpath) for j := 0; j <= i; j++ { - print("\t", hex(datap.ftab[j].entry), " ", funcname(funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff])), datap}), "\n") + println("\t", hex(datap.ftab[j].entryoff), funcname(funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff])), datap})) } if GOOS == "aix" && isarchive { println("-Wl,-bnoobjreorder is mandatory on aix/ppc64 with c-archive") @@ -603,8 +628,10 @@ func moduledataverify1(datap *moduledata) { } } - if datap.minpc != datap.ftab[0].entry || - datap.maxpc != datap.ftab[nftab].entry { + min := datap.textAddr(datap.ftab[0].entryoff) + max := datap.textAddr(datap.ftab[nftab].entryoff) + if datap.minpc != min || datap.maxpc != max { + println("minpc=", hex(datap.minpc), "min=", hex(min), "maxpc=", hex(datap.maxpc), "max=", hex(max)) throw("minpc or maxpc invalid") } @@ -616,6 +643,71 @@ func moduledataverify1(datap *moduledata) { } } +// textAddr returns md.text + off, with special handling for multiple text sections. +// off is a (virtual) offset computed at internal linking time, +// before the external linker adjusts the sections' base addresses. +// +// The text, or instruction stream is generated as one large buffer. +// The off (offset) for a function is its offset within this buffer. +// If the total text size gets too large, there can be issues on platforms like ppc64 +// if the target of calls are too far for the call instruction. +// To resolve the large text issue, the text is split into multiple text sections +// to allow the linker to generate long calls when necessary. +// When this happens, the vaddr for each text section is set to its offset within the text. +// Each function's offset is compared against the section vaddrs and ends to determine the containing section. +// Then the section relative offset is added to the section's +// relocated baseaddr to compute the function address. +// +// It is nosplit because it is part of the findfunc implementation. +// +//go:nosplit +func (md *moduledata) textAddr(off32 uint32) uintptr { + off := uintptr(off32) + res := md.text + off + if len(md.textsectmap) > 1 { + for i, sect := range md.textsectmap { + // For the last section, include the end address (etext), as it is included in the functab. + if off >= sect.vaddr && off < sect.end || (i == len(md.textsectmap)-1 && off == sect.end) { + res = sect.baseaddr + off - sect.vaddr + break + } + } + if res > md.etext && GOARCH != "wasm" { // on wasm, functions do not live in the same address space as the linear memory + println("runtime: textAddr", hex(res), "out of range", hex(md.text), "-", hex(md.etext)) + throw("runtime: text offset out of range") + } + } + return res +} + +// textOff is the opposite of textAddr. It converts a PC to a (virtual) offset +// to md.text, and returns if the PC is in any Go text section. +// +// It is nosplit because it is part of the findfunc implementation. +// +//go:nosplit +func (md *moduledata) textOff(pc uintptr) (uint32, bool) { + res := uint32(pc - md.text) + if len(md.textsectmap) > 1 { + for i, sect := range md.textsectmap { + if sect.baseaddr > pc { + // pc is not in any section. + return 0, false + } + end := sect.baseaddr + (sect.end - sect.vaddr) + // For the last section, include the end address (etext), as it is included in the functab. + if i == len(md.textsectmap) { + end++ + } + if pc < end { + res = uint32(pc - sect.baseaddr + sect.vaddr) + break + } + } + } + return res, true +} + // FuncForPC returns a *Func describing the function that contains the // given program counter address, or else nil. // @@ -634,10 +726,11 @@ func FuncForPC(pc uintptr) *Func { // The runtime currently doesn't have function end info, alas. if ix := pcdatavalue1(f, _PCDATA_InlTreeIndex, pc, nil, false); ix >= 0 { inltree := (*[1 << 20]inlinedCall)(inldata) - name := funcnameFromNameoff(f, inltree[ix].func_) + name := funcnameFromNameOff(f, inltree[ix].nameOff) file, line := funcline(f, pc) fi := &funcinl{ - entry: f.entry, // entry of the real (the outermost) function. + ones: ^uint32(0), + entry: f.entry(), // entry of the real (the outermost) function. name: name, file: file, line: int(line), @@ -654,7 +747,7 @@ func (f *Func) Name() string { return "" } fn := f.raw() - if fn.entry == 0 { // inlined version + if fn.isInlined() { // inlined version fi := (*funcinl)(unsafe.Pointer(fn)) return fi.name } @@ -664,11 +757,11 @@ func (f *Func) Name() string { // Entry returns the entry address of the function. func (f *Func) Entry() uintptr { fn := f.raw() - if fn.entry == 0 { // inlined version + if fn.isInlined() { // inlined version fi := (*funcinl)(unsafe.Pointer(fn)) return fi.entry } - return fn.entry + return fn.funcInfo().entry() } // FileLine returns the file name and line number of the @@ -677,7 +770,7 @@ func (f *Func) Entry() uintptr { // counter within f. func (f *Func) FileLine(pc uintptr) (file string, line int) { fn := f.raw() - if fn.entry == 0 { // inlined version + if fn.isInlined() { // inlined version fi := (*funcinl)(unsafe.Pointer(fn)) return fi.file, fi.line } @@ -715,6 +808,16 @@ func (f funcInfo) _Func() *Func { return (*Func)(unsafe.Pointer(f._func)) } +// isInlined reports whether f should be re-interpreted as a *funcinl. +func (f *_func) isInlined() bool { + return f.entryOff == ^uint32(0) // see comment for funcinl.ones +} + +// entry returns the entry PC for f. +func (f funcInfo) entry() uintptr { + return f.datap.textAddr(f.entryOff) +} + // findfunc looks up function metadata for a PC. // // It is nosplit because it's part of the isgoexception @@ -728,44 +831,24 @@ func findfunc(pc uintptr) funcInfo { } const nsub = uintptr(len(findfuncbucket{}.subbuckets)) - x := pc - datap.minpc + pcOff, ok := datap.textOff(pc) + if !ok { + return funcInfo{} + } + + x := uintptr(pcOff) + datap.text - datap.minpc // TODO: are datap.text and datap.minpc always equal? b := x / pcbucketsize i := x % pcbucketsize / (pcbucketsize / nsub) ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{}))) idx := ffb.idx + uint32(ffb.subbuckets[i]) - // If the idx is beyond the end of the ftab, set it to the end of the table and search backward. - // This situation can occur if multiple text sections are generated to handle large text sections - // and the linker has inserted jump tables between them. - - if idx >= uint32(len(datap.ftab)) { - idx = uint32(len(datap.ftab) - 1) + // Find the ftab entry. + for datap.ftab[idx+1].entryoff <= pcOff { + idx++ } - if pc < datap.ftab[idx].entry { - // With multiple text sections, the idx might reference a function address that - // is higher than the pc being searched, so search backward until the matching address is found. - for datap.ftab[idx].entry > pc && idx > 0 { - idx-- - } - if idx == 0 { - throw("findfunc: bad findfunctab entry idx") - } - } else { - // linear search to find func with pc >= entry. - for datap.ftab[idx+1].entry <= pc { - idx++ - } - } funcoff := datap.ftab[idx].funcoff - if funcoff == ^uintptr(0) { - // With multiple text sections, there may be functions inserted by the external - // linker that are not known by Go. This means there may be holes in the PC - // range covered by the func table. The invalid funcoff value indicates a hole. - // See also cmd/link/internal/ld/pcln.go:pclntab - return funcInfo{} - } return funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[funcoff])), datap} } @@ -783,10 +866,10 @@ type pcvalueCacheEnt struct { // pcvalueCacheKey returns the outermost index in a pcvalueCache to use for targetpc. // It must be very cheap to calculate. -// For now, align to sys.PtrSize and reduce mod the number of entries. +// For now, align to goarch.PtrSize and reduce mod the number of entries. // In practice, this appears to be fairly randomly and evenly distributed. func pcvalueCacheKey(targetpc uintptr) uintptr { - return (targetpc / sys.PtrSize) % uintptr(len(pcvalueCache{}.entries)) + return (targetpc / goarch.PtrSize) % uintptr(len(pcvalueCache{}.entries)) } // Returns the PCData value, and the PC where this value starts. @@ -818,20 +901,20 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, stri } if !f.valid() { - if strict && panicking == 0 { - print("runtime: no module data for ", hex(f.entry), "\n") + if strict && panicking.Load() == 0 { + println("runtime: no module data for", hex(f.entry())) throw("no module data") } return -1, 0 } datap := f.datap p := datap.pctab[off:] - pc := f.entry + pc := f.entry() prevpc := pc val := int32(-1) for { var ok bool - p, ok = step(p, &pc, &val, pc == f.entry) + p, ok = step(p, &pc, &val, pc == f.entry()) if !ok { break } @@ -845,7 +928,7 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, stri if cache != nil { x := pcvalueCacheKey(targetpc) e := &cache.entries[x] - ci := fastrand() % uint32(len(cache.entries[x])) + ci := fastrandn(uint32(len(cache.entries[x]))) e[ci] = e[0] e[0] = pcvalueCacheEnt{ targetpc: targetpc, @@ -861,18 +944,18 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, stri // If there was a table, it should have covered all program counters. // If not, something is wrong. - if panicking != 0 || !strict { + if panicking.Load() != 0 || !strict { return -1, 0 } print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n") p = datap.pctab[off:] - pc = f.entry + pc = f.entry() val = -1 for { var ok bool - p, ok = step(p, &pc, &val, pc == f.entry) + p, ok = step(p, &pc, &val, pc == f.entry()) if !ok { break } @@ -884,10 +967,10 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, stri } func cfuncname(f funcInfo) *byte { - if !f.valid() || f.nameoff == 0 { + if !f.valid() || f.nameOff == 0 { return nil } - return &f.datap.funcnametab[f.nameoff] + return &f.datap.funcnametab[f.nameOff] } func funcname(f funcInfo) string { @@ -910,15 +993,15 @@ func funcpkgpath(f funcInfo) string { return name[:i] } -func cfuncnameFromNameoff(f funcInfo, nameoff int32) *byte { +func cfuncnameFromNameOff(f funcInfo, nameOff int32) *byte { if !f.valid() { return nil } - return &f.datap.funcnametab[nameoff] + return &f.datap.funcnametab[nameOff] } -func funcnameFromNameoff(f funcInfo, nameoff int32) string { - return gostringnocopy(cfuncnameFromNameoff(f, nameoff)) +func funcnameFromNameOff(f funcInfo, nameOff int32) string { + return gostringnocopy(cfuncnameFromNameOff(f, nameOff)) } func funcfile(f funcInfo, fileno int32) string { @@ -955,8 +1038,9 @@ func funcline(f funcInfo, targetpc uintptr) (file string, line int32) { func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 { x, _ := pcvalue(f, f.pcsp, targetpc, cache, true) - if x&(sys.PtrSize-1) != 0 { - print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n") + if debugPcln && x&(goarch.PtrSize-1) != 0 { + print("invalid spdelta ", funcname(f), " ", hex(f.entry()), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n") + throw("bad spdelta") } return x } @@ -965,12 +1049,12 @@ func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 { func funcMaxSPDelta(f funcInfo) int32 { datap := f.datap p := datap.pctab[f.pcsp:] - pc := f.entry + pc := f.entry() val := int32(-1) max := int32(0) for { var ok bool - p, ok = step(p, &pc, &val, pc == f.entry) + p, ok = step(p, &pc, &val, pc == f.entry()) if !ok { return max } @@ -1009,18 +1093,24 @@ func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) { return pcvalue(f, pcdatastart(f, table), targetpc, nil, true) } +// funcdata returns a pointer to the ith funcdata for f. +// funcdata should be kept in sync with cmd/link:writeFuncs. func funcdata(f funcInfo, i uint8) unsafe.Pointer { if i < 0 || i >= f.nfuncdata { return nil } - p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4) - if sys.PtrSize == 8 && uintptr(p)&4 != 0 { - if uintptr(unsafe.Pointer(f._func))&4 != 0 { - println("runtime: misaligned func", f._func) - } - p = add(p, 4) + base := f.datap.gofunc // load gofunc address early so that we calculate during cache misses + p := uintptr(unsafe.Pointer(&f.nfuncdata)) + unsafe.Sizeof(f.nfuncdata) + uintptr(f.npcdata)*4 + uintptr(i)*4 + off := *(*uint32)(unsafe.Pointer(p)) + // Return off == ^uint32(0) ? 0 : f.datap.gofunc + uintptr(off), but without branches. + // The compiler calculates mask on most architectures using conditional assignment. + var mask uintptr + if off == ^uint32(0) { + mask = 1 } - return *(*unsafe.Pointer)(add(p, uintptr(i)*sys.PtrSize)) + mask-- + raw := base + uintptr(off) + return unsafe.Pointer(raw & mask) } // step advances to the next pc, value pair in the encoded table. @@ -1082,11 +1172,8 @@ func stackmapdata(stkmap *stackmap, n int32) bitvector { // inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. type inlinedCall struct { - parent int16 // index of parent in the inltree, or < 0 funcID funcID // type of the called function - _ byte - file int32 // perCU file index for inlined call. See cmd/link:pcln.go - line int32 // line number of the call site - func_ int32 // offset into pclntab for name of called function + _ [3]byte + nameOff int32 // offset into pclntab for name of called function parentPc int32 // position of an instruction whose source position is the call site (offset from entry) } diff --git a/src/runtime/symtab_test.go b/src/runtime/symtab_test.go index ffa07c7f3a9f8d..cf20ea7a0e7896 100644 --- a/src/runtime/symtab_test.go +++ b/src/runtime/symtab_test.go @@ -29,6 +29,7 @@ func TestCaller(t *testing.T) { // These are marked noinline so that we can use FuncForPC // in testCallerBar. +// //go:noinline func testCallerFoo(t *testing.T) { testCallerBar(t) @@ -200,20 +201,20 @@ func tracebackFunc(t *testing.T) uintptr { // Go will never generate a stack trace containing such an address, as it is // not a valid call site. However, the cgo traceback function passed to // runtime.SetCgoTraceback may not be completely accurate and may incorrect -// provide PCs in Go code or the alignement region between functions. +// provide PCs in Go code or the alignment region between functions. // // Go obviously doesn't easily expose the problematic PCs to running programs, // so this test is a bit fragile. Some details: // -// * tracebackFunc is our target function. We want to get a PC in the -// alignment region following this function. This function also has other -// functions inlined into it to ensure it has an InlTree (this was the source -// of the bug in issue 44971). +// - tracebackFunc is our target function. We want to get a PC in the +// alignment region following this function. This function also has other +// functions inlined into it to ensure it has an InlTree (this was the source +// of the bug in issue 44971). // -// * We acquire a PC in tracebackFunc, walking forwards until FuncForPC says -// we're in a new function. The last PC of the function according to FuncForPC -// should be in the alignment region (assuming the function isn't already -// perfectly aligned). +// - We acquire a PC in tracebackFunc, walking forwards until FuncForPC says +// we're in a new function. The last PC of the function according to FuncForPC +// should be in the alignment region (assuming the function isn't already +// perfectly aligned). // // This is a regression test for issue 44971. func TestFunctionAlignmentTraceback(t *testing.T) { @@ -250,3 +251,35 @@ func TestFunctionAlignmentTraceback(t *testing.T) { t.Errorf("frames.Next() got %+v want %+v", frame.Func, f) } } + +func BenchmarkFunc(b *testing.B) { + pc, _, _, ok := runtime.Caller(0) + if !ok { + b.Fatal("failed to look up PC") + } + f := runtime.FuncForPC(pc) + b.Run("Name", func(b *testing.B) { + for i := 0; i < b.N; i++ { + name := f.Name() + if name != "runtime_test.BenchmarkFunc" { + b.Fatalf("unexpected name %q", name) + } + } + }) + b.Run("Entry", func(b *testing.B) { + for i := 0; i < b.N; i++ { + pc := f.Entry() + if pc == 0 { + b.Fatal("zero PC") + } + } + }) + b.Run("FileLine", func(b *testing.B) { + for i := 0; i < b.N; i++ { + file, line := f.FileLine(pc) + if !strings.HasSuffix(file, "symtab_test.go") || line == 0 { + b.Fatalf("unexpected file/line %q:%d", file, line) + } + } + }) +} diff --git a/src/runtime/sys_aix_ppc64.s b/src/runtime/sys_aix_ppc64.s index c171c191c0e5ec..ab18c5eb00bcc6 100644 --- a/src/runtime/sys_aix_ppc64.s +++ b/src/runtime/sys_aix_ppc64.s @@ -22,10 +22,15 @@ TEXT callCfunction<>(SB), NOSPLIT|NOFRAME,$0 // asmsyscall6 calls a library function with a function descriptor -// stored in libcall_fn and store the results in libcall struture +// stored in libcall_fn and store the results in libcall structure // Up to 6 arguments can be passed to this C function // Called by runtime.asmcgocall -// It reserves a stack of 288 bytes for the C function. +// It reserves a stack of 288 bytes for the C function. It must +// follow AIX convention, thus the first local variable must +// be stored at the offset 112, after the linker area (48 bytes) +// and the argument area (64). +// The AIX convention is described here: +// https://www.ibm.com/docs/en/aix/7.2?topic=overview-runtime-process-stack // NOT USING GO CALLING CONVENTION // runtime.asmsyscall6 is a function descriptor to the real asmsyscall6. DATA runtime·asmsyscall6+0(SB)/8, $asmsyscall6<>(SB) @@ -34,7 +39,8 @@ DATA runtime·asmsyscall6+16(SB)/8, $0 GLOBL runtime·asmsyscall6(SB), NOPTR, $24 TEXT asmsyscall6<>(SB),NOSPLIT,$256 - MOVD R3, 48(R1) // Save libcall for later + // Save libcall for later + MOVD R3, 112(R1) MOVD libcall_fn(R3), R12 MOVD libcall_args(R3), R9 MOVD 0(R9), R3 @@ -50,7 +56,7 @@ TEXT asmsyscall6<>(SB),NOSPLIT,$256 MOVD 40(R1), R2 // Store result in libcall - MOVD 48(R1), R5 + MOVD 112(R1), R5 MOVD R3, (libcall_r1)(R5) MOVD $-1, R6 CMP R6, R3 @@ -95,7 +101,7 @@ GLOBL runtime·sigtramp(SB), NOPTR, $24 // This function must not have any frame as we want to control how // every registers are used. // TODO(aix): Implement SetCgoTraceback handler. -TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0 +TEXT sigtramp<>(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 MOVD LR, R0 MOVD R0, 16(R1) // initialize essential registers (just in case) diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go index 0f91685d6ca14d..1547fdceb0ca0c 100644 --- a/src/runtime/sys_darwin.go +++ b/src/runtime/sys_darwin.go @@ -17,87 +17,91 @@ import ( //go:linkname syscall_syscall syscall.syscall //go:nosplit -//go:cgo_unsafe_args func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { + args := struct{ fn, a1, a2, a3, r1, r2, err uintptr }{fn, a1, a2, a3, r1, r2, err} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1, args.r2, args.err } func syscall() //go:linkname syscall_syscallX syscall.syscallX //go:nosplit -//go:cgo_unsafe_args func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { + args := struct{ fn, a1, a2, a3, r1, r2, err uintptr }{fn, a1, a2, a3, r1, r2, err} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallX)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallX)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1, args.r2, args.err } func syscallX() //go:linkname syscall_syscall6 syscall.syscall6 //go:nosplit -//go:cgo_unsafe_args func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { + args := struct{ fn, a1, a2, a3, a4, a5, a6, r1, r2, err uintptr }{fn, a1, a2, a3, a4, a5, a6, r1, r2, err} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1, args.r2, args.err } func syscall6() //go:linkname syscall_syscall6X syscall.syscall6X //go:nosplit -//go:cgo_unsafe_args func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { + args := struct{ fn, a1, a2, a3, a4, a5, a6, r1, r2, err uintptr }{fn, a1, a2, a3, a4, a5, a6, r1, r2, err} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6X)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6X)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1, args.r2, args.err } func syscall6X() //go:linkname syscall_syscallPtr syscall.syscallPtr //go:nosplit -//go:cgo_unsafe_args func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { + args := struct{ fn, a1, a2, a3, r1, r2, err uintptr }{fn, a1, a2, a3, r1, r2, err} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallPtr)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallPtr)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1, args.r2, args.err } func syscallPtr() //go:linkname syscall_rawSyscall syscall.rawSyscall //go:nosplit -//go:cgo_unsafe_args func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&fn)) - return + args := struct{ fn, a1, a2, a3, r1, r2, err uintptr }{fn, a1, a2, a3, r1, r2, err} + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&args)) + return args.r1, args.r2, args.err } //go:linkname syscall_rawSyscall6 syscall.rawSyscall6 //go:nosplit -//go:cgo_unsafe_args func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&fn)) - return + args := struct{ fn, a1, a2, a3, a4, a5, a6, r1, r2, err uintptr }{fn, a1, a2, a3, a4, a5, a6, r1, r2, err} + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&args)) + return args.r1, args.r2, args.err } // syscallNoErr is used in crypto/x509 to call into Security.framework and CF. //go:linkname crypto_x509_syscall crypto/x509/internal/macos.syscall //go:nosplit -//go:cgo_unsafe_args -func crypto_x509_syscall(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1 uintptr) { +func crypto_x509_syscall(fn, a1, a2, a3, a4, a5 uintptr, f1 float64) (r1 uintptr) { + args := struct { + fn, a1, a2, a3, a4, a5 uintptr + f1 float64 + r1 uintptr + }{fn, a1, a2, a3, a4, a5, f1, r1} entersyscall() - libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallNoErr)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall_x509)), unsafe.Pointer(&args)) exitsyscall() - return + return args.r1 } -func syscallNoErr() +func syscall_x509() // The *_trampoline functions convert from the Go calling convention to the C calling convention // and then call the underlying libc function. They are defined in sys_darwin_$ARCH.s. @@ -105,28 +109,38 @@ func syscallNoErr() //go:nosplit //go:cgo_unsafe_args func pthread_attr_init(attr *pthreadattr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + return ret } func pthread_attr_init_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_attr_getstacksize(attr *pthreadattr, size *uintptr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + KeepAlive(size) + return ret } func pthread_attr_getstacksize_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + return ret } func pthread_attr_setdetachstate_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + KeepAlive(arg) // Just for consistency. Arg of course needs to be kept alive for the start function. + return ret } func pthread_create_trampoline() @@ -156,7 +170,8 @@ func pthread_kill_trampoline() // mmap is used to do low-level memory allocation via mmap. Don't allow stack // splits, since this function (used by sysAlloc) is called in a lot of low-level // parts of the runtime and callers often assume it won't acquire any locks. -// go:nosplit +// +//go:nosplit func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) { args := struct { addr unsafe.Pointer @@ -175,6 +190,7 @@ func mmap_trampoline() //go:cgo_unsafe_args func munmap(addr unsafe.Pointer, n uintptr) { libcCall(unsafe.Pointer(abi.FuncPCABI0(munmap_trampoline)), unsafe.Pointer(&addr)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. } func munmap_trampoline() @@ -182,6 +198,7 @@ func munmap_trampoline() //go:cgo_unsafe_args func madvise(addr unsafe.Pointer, n uintptr, flags int32) { libcCall(unsafe.Pointer(abi.FuncPCABI0(madvise_trampoline)), unsafe.Pointer(&addr)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. } func madvise_trampoline() @@ -189,13 +206,16 @@ func madvise_trampoline() //go:cgo_unsafe_args func mlock(addr unsafe.Pointer, n uintptr) { libcCall(unsafe.Pointer(abi.FuncPCABI0(mlock_trampoline)), unsafe.Pointer(&addr)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. } func mlock_trampoline() //go:nosplit //go:cgo_unsafe_args func read(fd int32, p unsafe.Pointer, n int32) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd)) + KeepAlive(p) + return ret } func read_trampoline() @@ -213,10 +233,10 @@ func closefd(fd int32) int32 { } func close_trampoline() +// This is exported via linkname to assembly in runtime/cgo. +// //go:nosplit //go:cgo_unsafe_args -// -// This is exported via linkname to assembly in runtime/cgo. //go:linkname exit func exit(code int32) { libcCall(unsafe.Pointer(abi.FuncPCABI0(exit_trampoline)), unsafe.Pointer(&code)) @@ -239,14 +259,18 @@ func usleep_no_g(usec uint32) { //go:nosplit //go:cgo_unsafe_args func write1(fd uintptr, p unsafe.Pointer, n int32) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd)) + KeepAlive(p) + return ret } func write_trampoline() //go:nosplit //go:cgo_unsafe_args func open(name *byte, mode, perm int32) (ret int32) { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name)) + ret = libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name)) + KeepAlive(name) + return } func open_trampoline() @@ -285,6 +309,8 @@ func walltime_trampoline() //go:cgo_unsafe_args func sigaction(sig uint32, new *usigactiont, old *usigactiont) { libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaction_trampoline)), unsafe.Pointer(&sig)) + KeepAlive(new) + KeepAlive(old) } func sigaction_trampoline() @@ -292,6 +318,8 @@ func sigaction_trampoline() //go:cgo_unsafe_args func sigprocmask(how uint32, new *sigset, old *sigset) { libcCall(unsafe.Pointer(abi.FuncPCABI0(sigprocmask_trampoline)), unsafe.Pointer(&how)) + KeepAlive(new) + KeepAlive(old) } func sigprocmask_trampoline() @@ -306,6 +334,8 @@ func sigaltstack(new *stackt, old *stackt) { new.ss_size = 32768 } libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaltstack_trampoline)), unsafe.Pointer(&new)) + KeepAlive(new) + KeepAlive(old) } func sigaltstack_trampoline() @@ -320,20 +350,32 @@ func raiseproc_trampoline() //go:cgo_unsafe_args func setitimer(mode int32, new, old *itimerval) { libcCall(unsafe.Pointer(abi.FuncPCABI0(setitimer_trampoline)), unsafe.Pointer(&mode)) + KeepAlive(new) + KeepAlive(old) } func setitimer_trampoline() //go:nosplit //go:cgo_unsafe_args func sysctl(mib *uint32, miblen uint32, oldp *byte, oldlenp *uintptr, newp *byte, newlen uintptr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib)) + KeepAlive(mib) + KeepAlive(oldp) + KeepAlive(oldlenp) + KeepAlive(newp) + return ret } func sysctl_trampoline() //go:nosplit //go:cgo_unsafe_args func sysctlbyname(name *byte, oldp *byte, oldlenp *uintptr, newp *byte, newlen uintptr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctlbyname_trampoline)), unsafe.Pointer(&name)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctlbyname_trampoline)), unsafe.Pointer(&name)) + KeepAlive(name) + KeepAlive(oldp) + KeepAlive(oldlenp) + KeepAlive(newp) + return ret } func sysctlbyname_trampoline() @@ -355,56 +397,79 @@ func kqueue_trampoline() //go:nosplit //go:cgo_unsafe_args func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq)) + KeepAlive(ch) + KeepAlive(ev) + KeepAlive(ts) + return ret } func kevent_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_mutex_init(m *pthreadmutex, attr *pthreadmutexattr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_init_trampoline)), unsafe.Pointer(&m)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_init_trampoline)), unsafe.Pointer(&m)) + KeepAlive(m) + KeepAlive(attr) + return ret } func pthread_mutex_init_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_mutex_lock(m *pthreadmutex) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_lock_trampoline)), unsafe.Pointer(&m)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_lock_trampoline)), unsafe.Pointer(&m)) + KeepAlive(m) + return ret } func pthread_mutex_lock_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_mutex_unlock(m *pthreadmutex) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_unlock_trampoline)), unsafe.Pointer(&m)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_mutex_unlock_trampoline)), unsafe.Pointer(&m)) + KeepAlive(m) + return ret } func pthread_mutex_unlock_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_cond_init(c *pthreadcond, attr *pthreadcondattr) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_init_trampoline)), unsafe.Pointer(&c)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_init_trampoline)), unsafe.Pointer(&c)) + KeepAlive(c) + KeepAlive(attr) + return ret } func pthread_cond_init_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_cond_wait(c *pthreadcond, m *pthreadmutex) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_wait_trampoline)), unsafe.Pointer(&c)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_wait_trampoline)), unsafe.Pointer(&c)) + KeepAlive(c) + KeepAlive(m) + return ret } func pthread_cond_wait_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_cond_timedwait_relative_np(c *pthreadcond, m *pthreadmutex, t *timespec) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_timedwait_relative_np_trampoline)), unsafe.Pointer(&c)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_timedwait_relative_np_trampoline)), unsafe.Pointer(&c)) + KeepAlive(c) + KeepAlive(m) + KeepAlive(t) + return ret } func pthread_cond_timedwait_relative_np_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_cond_signal(c *pthreadcond) int32 { - return libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_signal_trampoline)), unsafe.Pointer(&c)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_cond_signal_trampoline)), unsafe.Pointer(&c)) + KeepAlive(c) + return ret } func pthread_cond_signal_trampoline() diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s index 3bd027f98242f0..ba81fcc35c4424 100644 --- a/src/runtime/sys_darwin_amd64.s +++ b/src/runtime/sys_darwin_amd64.s @@ -214,7 +214,32 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 // This is the function registered during sigaction and is invoked when // a signal is received. It just redirects to the Go function sigtrampgo. // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 + // Transition from C ABI to Go ABI. + PUSH_REGS_HOST_TO_ABI0() + + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. + NOP SP // disable vet stack checking + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + + ADJSP $-24 + + POP_REGS_HOST_TO_ABI0() + RET + +// Called using C ABI. +TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() @@ -224,7 +249,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0 MOVL DI, 0(SP) // sig MOVQ SI, 8(SP) // info MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + CALL ·sigprofNonGo(SB) ADJSP $-24 POP_REGS_HOST_TO_ABI0() @@ -297,12 +322,12 @@ sigtrampnog: JNZ sigtramp // Skip stack trace if already locked. // Jump to the traceback function in runtime/cgo. - // It will call back to sigprofNonGo, which will ignore the - // arguments passed in registers. + // It will call back to sigprofNonGo, via sigprofNonGoWrapper, to convert + // the arguments to the Go calling convention. // First three arguments to traceback function are in registers already. MOVQ runtime·cgoTraceback(SB), CX MOVQ $runtime·sigprofCallers(SB), R8 - MOVQ $runtime·sigprofNonGo(SB), R9 + MOVQ $runtime·sigprofNonGoWrapper<>(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX @@ -814,9 +839,10 @@ ok: POPQ BP RET -// syscallNoErr is like syscall6 but does not check for errors, and -// only returns one value, for use with standard C ABI library functions. -TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 +// syscall_x509 is for crypto/x509. It is like syscall6 but does not check for errors, +// takes 5 uintptrs and 1 float64, and only returns one value, +// for use with standard C ABI functions. +TEXT runtime·syscall_x509(SB),NOSPLIT,$0 PUSHQ BP MOVQ SP, BP SUBQ $16, SP @@ -825,7 +851,7 @@ TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 MOVQ (3*8)(DI), DX // a3 MOVQ (4*8)(DI), CX // a4 MOVQ (5*8)(DI), R8 // a5 - MOVQ (6*8)(DI), R9 // a6 + MOVQ (6*8)(DI), X0 // f1 MOVQ DI, (SP) MOVQ (1*8)(DI), DI // a1 XORL AX, AX // vararg: say "no float args" diff --git a/src/runtime/sys_darwin_arm64.go b/src/runtime/sys_darwin_arm64.go index 9c14f33a1ce238..6170f4fddab51e 100644 --- a/src/runtime/sys_darwin_arm64.go +++ b/src/runtime/sys_darwin_arm64.go @@ -5,7 +5,8 @@ package runtime import ( - "runtime/internal/sys" + "internal/abi" + "internal/goarch" "unsafe" ) @@ -14,14 +15,16 @@ import ( //go:nosplit //go:cgo_unsafe_args func g0_pthread_key_create(k *pthreadkey, destructor uintptr) int32 { - return asmcgocall(unsafe.Pointer(funcPC(pthread_key_create_trampoline)), unsafe.Pointer(&k)) + ret := asmcgocall(unsafe.Pointer(abi.FuncPCABI0(pthread_key_create_trampoline)), unsafe.Pointer(&k)) + KeepAlive(k) + return ret } func pthread_key_create_trampoline() //go:nosplit //go:cgo_unsafe_args func g0_pthread_setspecific(k pthreadkey, value uintptr) int32 { - return asmcgocall(unsafe.Pointer(funcPC(pthread_setspecific_trampoline)), unsafe.Pointer(&k)) + return asmcgocall(unsafe.Pointer(abi.FuncPCABI0(pthread_setspecific_trampoline)), unsafe.Pointer(&k)) } func pthread_setspecific_trampoline() @@ -53,7 +56,7 @@ func tlsinit(tlsg *uintptr, tlsbase *[_PTHREAD_KEYS_MAX]uintptr) { for i, x := range tlsbase { if x == magic { - *tlsg = uintptr(i * sys.PtrSize) + *tlsg = uintptr(i * goarch.PtrSize) g0_pthread_setspecific(k, 0) return } diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index 96d2ed10767d85..bf0dc9d8ccb6ce 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 @@ -175,28 +176,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // Save arguments. MOVW R0, (8*1)(RSP) // sig @@ -249,25 +233,8 @@ nog: #endif // Restore callee-save registers. - MOVD (8*4)(RSP), R19 - MOVD (8*5)(RSP), R20 - MOVD (8*6)(RSP), R21 - MOVD (8*7)(RSP), R22 - MOVD (8*8)(RSP), R23 - MOVD (8*9)(RSP), R24 - MOVD (8*10)(RSP), R25 - MOVD (8*11)(RSP), R26 - MOVD (8*12)(RSP), R27 - MOVD (8*13)(RSP), g - MOVD (8*14)(RSP), R29 - FMOVD (8*15)(RSP), F8 - FMOVD (8*16)(RSP), F9 - FMOVD (8*17)(RSP), F10 - FMOVD (8*18)(RSP), F11 - FMOVD (8*19)(RSP), F12 - FMOVD (8*20)(RSP), F13 - FMOVD (8*21)(RSP), F14 - FMOVD (8*22)(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET @@ -376,25 +343,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 // We are already on m's g0 stack. // Save callee-save registers. - MOVD R19, 8(RSP) - MOVD R20, 16(RSP) - MOVD R21, 24(RSP) - MOVD R22, 32(RSP) - MOVD R23, 40(RSP) - MOVD R24, 48(RSP) - MOVD R25, 56(RSP) - MOVD R26, 64(RSP) - MOVD R27, 72(RSP) - MOVD g, 80(RSP) - MOVD R29, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD m_g0(R0), g BL ·save_g(SB) @@ -402,25 +352,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 BL runtime·mstart(SB) // Restore callee-save registers. - MOVD 8(RSP), R19 - MOVD 16(RSP), R20 - MOVD 24(RSP), R21 - MOVD 32(RSP), R22 - MOVD 40(RSP), R23 - MOVD 48(RSP), R24 - MOVD 56(RSP), R25 - MOVD 64(RSP), R26 - MOVD 72(RSP), R27 - MOVD 80(RSP), g - MOVD 88(RSP), R29 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) // Go is all done with this OS thread. // Tell pthread everything is ok (we never join with this thread, so @@ -736,9 +669,10 @@ TEXT runtime·syscall6X(SB),NOSPLIT,$0 ok: RET -// syscallNoErr is like syscall6 but does not check for errors, and -// only returns one value, for use with standard C ABI library functions. -TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 +// syscall_x509 is for crypto/x509. It is like syscall6 but does not check for errors, +// takes 5 uintptrs and 1 float64, and only returns one value, +// for use with standard C ABI functions. +TEXT runtime·syscall_x509(SB),NOSPLIT,$0 SUB $16, RSP // push structure pointer MOVD R0, (RSP) @@ -747,7 +681,7 @@ TEXT runtime·syscallNoErr(SB),NOSPLIT,$0 MOVD 24(R0), R2 // a3 MOVD 32(R0), R3 // a4 MOVD 40(R0), R4 // a5 - MOVD 48(R0), R5 // a6 + FMOVD 48(R0), F0 // f1 MOVD 8(R0), R0 // a1 BL (R12) diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s index d57bc2a7a4ee85..602d5e9b76b451 100644 --- a/src/runtime/sys_dragonfly_amd64.s +++ b/src/runtime/sys_dragonfly_amd64.s @@ -109,21 +109,6 @@ TEXT runtime·read(SB),NOSPLIT,$-8 MOVL AX, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVL $42, AX - SYSCALL - JCC pipeok - MOVL $-1,r+0(FP) - MOVL $-1,w+4(FP) - MOVL AX, errno+8(FP) - RET -pipeok: - MOVL AX, r+0(FP) - MOVL DX, w+4(FP) - MOVL $0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-20 MOVL $0, DI @@ -237,17 +222,25 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() - // Call into the Go signal handler + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. NOP SP // disable vet stack checking - ADJSP $24 - MOVQ DI, 0(SP) // sig - MOVQ SI, 8(SP) // info - MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + ADJSP $-24 POP_REGS_HOST_TO_ABI0() @@ -402,18 +395,3 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 MOVL $92, AX // fcntl SYSCALL RET - -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVL fd+0(FP), DI // fd - MOVQ $3, SI // F_GETFL - MOVQ $0, DX - MOVL $92, AX // fcntl - SYSCALL - MOVL fd+0(FP), DI // fd - MOVQ $4, SI // F_SETFL - MOVQ $4, DX // O_NONBLOCK - ORL AX, DX - MOVL $92, AX // fcntl - SYSCALL - RET diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s index 97e6d9ab36634c..e10c89665c6289 100644 --- a/src/runtime/sys_freebsd_386.s +++ b/src/runtime/sys_freebsd_386.s @@ -10,8 +10,44 @@ #include "go_tls.h" #include "textflag.h" +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 4 +#define FD_CLOEXEC 1 +#define F_SETFD 2 + +#define SYS_exit 1 +#define SYS_read 3 +#define SYS_write 4 +#define SYS_open 5 +#define SYS_close 6 +#define SYS_getpid 20 +#define SYS_kill 37 +#define SYS_sigaltstack 53 +#define SYS_munmap 73 +#define SYS_madvise 75 +#define SYS_setitimer 83 +#define SYS_fcntl 92 +#define SYS_sysarch 165 +#define SYS___sysctl 202 +#define SYS_clock_gettime 232 +#define SYS_nanosleep 240 +#define SYS_sched_yield 331 +#define SYS_sigprocmask 340 +#define SYS_kqueue 362 +#define SYS_kevent 363 +#define SYS_sigaction 416 +#define SYS_sigreturn 417 +#define SYS_thr_exit 431 +#define SYS_thr_self 432 +#define SYS_thr_kill 433 +#define SYS__umtx_op 454 +#define SYS_thr_new 455 +#define SYS_mmap 477 +#define SYS_cpuset_getaffinity 487 +#define SYS_pipe2 542 + TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4 - MOVL $454, AX + MOVL $SYS__umtx_op, AX INT $0x80 JAE 2(PC) NEGL AX @@ -19,7 +55,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4 RET TEXT runtime·thr_new(SB),NOSPLIT,$-4 - MOVL $455, AX + MOVL $SYS_thr_new, AX INT $0x80 JAE 2(PC) NEGL AX @@ -54,7 +90,7 @@ TEXT runtime·thr_start(SB),NOSPLIT,$0 // Exit the entire program (like C exit) TEXT runtime·exit(SB),NOSPLIT,$-4 - MOVL $1, AX + MOVL $SYS_exit, AX INT $0x80 MOVL $0xf1, 0xf1 // crash RET @@ -72,13 +108,13 @@ TEXT runtime·exitThread(SB),NOSPLIT,$0-4 // on the stack. We want to pass 0, so switch over to a fake // stack of 0s. It won't write to the stack. MOVL $exitStack<>(SB), SP - MOVL $431, AX // thr_exit + MOVL $SYS_thr_exit, AX INT $0x80 MOVL $0xf1, 0xf1 // crash JMP 0(PC) TEXT runtime·open(SB),NOSPLIT,$-4 - MOVL $5, AX + MOVL $SYS_open, AX INT $0x80 JAE 2(PC) MOVL $-1, AX @@ -86,7 +122,7 @@ TEXT runtime·open(SB),NOSPLIT,$-4 RET TEXT runtime·closefd(SB),NOSPLIT,$-4 - MOVL $6, AX + MOVL $SYS_close, AX INT $0x80 JAE 2(PC) MOVL $-1, AX @@ -94,31 +130,16 @@ TEXT runtime·closefd(SB),NOSPLIT,$-4 RET TEXT runtime·read(SB),NOSPLIT,$-4 - MOVL $3, AX + MOVL $SYS_read, AX INT $0x80 JAE 2(PC) NEGL AX // caller expects negative errno MOVL AX, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$8-12 - MOVL $42, AX - INT $0x80 - JAE ok - MOVL $0, r+0(FP) - MOVL $0, w+4(FP) - MOVL AX, errno+8(FP) - RET -ok: - MOVL AX, r+0(FP) - MOVL DX, w+4(FP) - MOVL $0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$12-16 - MOVL $542, AX + MOVL $SYS_pipe2, AX LEAL r+4(FP), BX MOVL BX, 4(SP) MOVL flags+0(FP), BX @@ -130,7 +151,7 @@ TEXT runtime·pipe2(SB),NOSPLIT,$12-16 RET TEXT runtime·write1(SB),NOSPLIT,$-4 - MOVL $4, AX + MOVL $SYS_write, AX INT $0x80 JAE 2(PC) NEGL AX // caller expects negative errno @@ -141,25 +162,25 @@ TEXT runtime·thr_self(SB),NOSPLIT,$8-4 // thr_self(&0(FP)) LEAL ret+0(FP), AX MOVL AX, 4(SP) - MOVL $432, AX + MOVL $SYS_thr_self, AX INT $0x80 RET TEXT runtime·thr_kill(SB),NOSPLIT,$-4 // thr_kill(tid, sig) - MOVL $433, AX + MOVL $SYS_thr_kill, AX INT $0x80 RET TEXT runtime·raiseproc(SB),NOSPLIT,$16 // getpid - MOVL $20, AX + MOVL $SYS_getpid, AX INT $0x80 // kill(self, sig) MOVL AX, 4(SP) MOVL sig+0(FP), AX MOVL AX, 8(SP) - MOVL $37, AX + MOVL $SYS_kill, AX INT $0x80 RET @@ -175,7 +196,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$32 MOVSL MOVL $0, AX // top 32 bits of file offset STOSL - MOVL $477, AX + MOVL $SYS_mmap, AX INT $0x80 JAE ok MOVL $0, p+24(FP) @@ -187,14 +208,14 @@ ok: RET TEXT runtime·munmap(SB),NOSPLIT,$-4 - MOVL $73, AX + MOVL $SYS_munmap, AX INT $0x80 JAE 2(PC) MOVL $0xf1, 0xf1 // crash RET TEXT runtime·madvise(SB),NOSPLIT,$-4 - MOVL $75, AX // madvise + MOVL $SYS_madvise, AX INT $0x80 JAE 2(PC) MOVL $-1, AX @@ -202,15 +223,15 @@ TEXT runtime·madvise(SB),NOSPLIT,$-4 RET TEXT runtime·setitimer(SB), NOSPLIT, $-4 - MOVL $83, AX + MOVL $SYS_setitimer, AX INT $0x80 RET // func fallback_walltime() (sec int64, nsec int32) TEXT runtime·fallback_walltime(SB), NOSPLIT, $32-12 - MOVL $232, AX // clock_gettime + MOVL $SYS_clock_gettime, AX LEAL 12(SP), BX - MOVL $0, 4(SP) // CLOCK_REALTIME + MOVL $CLOCK_REALTIME, 4(SP) MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec @@ -224,9 +245,9 @@ TEXT runtime·fallback_walltime(SB), NOSPLIT, $32-12 // func fallback_nanotime() int64 TEXT runtime·fallback_nanotime(SB), NOSPLIT, $32-8 - MOVL $232, AX + MOVL $SYS_clock_gettime, AX LEAL 12(SP), BX - MOVL $4, 4(SP) // CLOCK_MONOTONIC + MOVL $CLOCK_MONOTONIC, 4(SP) MOVL BX, 8(SP) INT $0x80 MOVL 12(SP), AX // sec @@ -245,7 +266,7 @@ TEXT runtime·fallback_nanotime(SB), NOSPLIT, $32-8 TEXT runtime·asmSigaction(SB),NOSPLIT,$-4 - MOVL $416, AX + MOVL $SYS_sigaction, AX INT $0x80 MOVL AX, ret+12(FP) RET @@ -268,7 +289,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$12-16 RET // Called by OS using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$12 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$12 NOP SP // tell vet SP changed - stop checking offsets MOVL 16(SP), BX // signo MOVL BX, 0(SP) @@ -282,13 +303,13 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12 MOVL 24(SP), AX // context MOVL $0, 0(SP) // syscall gap MOVL AX, 4(SP) - MOVL $417, AX // sigreturn(ucontext) + MOVL $SYS_sigreturn, AX INT $0x80 MOVL $0xf1, 0xf1 // crash RET TEXT runtime·sigaltstack(SB),NOSPLIT,$0 - MOVL $53, AX + MOVL $SYS_sigaltstack, AX INT $0x80 JAE 2(PC) MOVL $0xf1, 0xf1 // crash @@ -308,7 +329,7 @@ TEXT runtime·usleep(SB),NOSPLIT,$20 LEAL 12(SP), AX MOVL AX, 4(SP) // arg 1 - rqtp MOVL $0, 8(SP) // arg 2 - rmtp - MOVL $240, AX // sys_nanosleep + MOVL $SYS_nanosleep, AX INT $0x80 RET @@ -367,7 +388,7 @@ TEXT i386_set_ldt<>(SB),NOSPLIT,$16 MOVL $0, 0(SP) // syscall gap MOVL $1, 4(SP) MOVL AX, 8(SP) - MOVL $165, AX + MOVL $SYS_sysarch, AX INT $0x80 JAE 2(PC) INT $3 @@ -383,7 +404,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28 MOVSL // arg 4 - oldlenp MOVSL // arg 5 - newp MOVSL // arg 6 - newlen - MOVL $202, AX // sys___sysctl + MOVL $SYS___sysctl, AX INT $0x80 JAE 4(PC) NEGL AX @@ -394,7 +415,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$28 RET TEXT runtime·osyield(SB),NOSPLIT,$-4 - MOVL $331, AX // sys_sched_yield + MOVL $SYS_sched_yield, AX INT $0x80 RET @@ -406,7 +427,7 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$16 MOVL AX, 8(SP) // arg 2 - set MOVL old+8(FP), AX MOVL AX, 12(SP) // arg 3 - oset - MOVL $340, AX // sys_sigprocmask + MOVL $SYS_sigprocmask, AX INT $0x80 JAE 2(PC) MOVL $0xf1, 0xf1 // crash @@ -414,7 +435,7 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$16 // int32 runtime·kqueue(void); TEXT runtime·kqueue(SB),NOSPLIT,$0 - MOVL $362, AX + MOVL $SYS_kqueue, AX INT $0x80 JAE 2(PC) NEGL AX @@ -423,7 +444,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout); TEXT runtime·kevent(SB),NOSPLIT,$0 - MOVL $363, AX + MOVL $SYS_kevent, AX INT $0x80 JAE 2(PC) NEGL AX @@ -432,37 +453,20 @@ TEXT runtime·kevent(SB),NOSPLIT,$0 // int32 runtime·closeonexec(int32 fd); TEXT runtime·closeonexec(SB),NOSPLIT,$32 - MOVL $92, AX // fcntl + MOVL $SYS_fcntl, AX // 0(SP) is where the caller PC would be; kernel skips it MOVL fd+0(FP), BX MOVL BX, 4(SP) // fd - MOVL $2, 8(SP) // F_SETFD - MOVL $1, 12(SP) // FD_CLOEXEC + MOVL $F_SETFD, 8(SP) + MOVL $FD_CLOEXEC, 12(SP) INT $0x80 JAE 2(PC) NEGL AX RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$16-4 - MOVL $92, AX // fcntl - MOVL fd+0(FP), BX // fd - MOVL BX, 4(SP) - MOVL $3, 8(SP) // F_GETFL - MOVL $0, 12(SP) - INT $0x80 - MOVL fd+0(FP), BX // fd - MOVL BX, 4(SP) - MOVL $4, 8(SP) // F_SETFL - ORL $4, AX // O_NONBLOCK - MOVL AX, 12(SP) - MOVL $92, AX // fcntl - INT $0x80 - RET - // func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32 TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-28 - MOVL $487, AX + MOVL $SYS_cpuset_getaffinity, AX INT $0x80 JAE 2(PC) NEGL AX diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s index 71a60cae65c236..ab6e09a81b174e 100644 --- a/src/runtime/sys_freebsd_amd64.s +++ b/src/runtime/sys_freebsd_amd64.s @@ -11,13 +11,49 @@ #include "textflag.h" #include "cgo/abi_amd64.h" +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 4 +#define FD_CLOEXEC 1 +#define F_SETFD 2 +#define AMD64_SET_FSBASE 129 + +#define SYS_exit 1 +#define SYS_read 3 +#define SYS_write 4 +#define SYS_open 5 +#define SYS_close 6 +#define SYS_getpid 20 +#define SYS_kill 37 +#define SYS_sigaltstack 53 +#define SYS_munmap 73 +#define SYS_madvise 75 +#define SYS_setitimer 83 +#define SYS_fcntl 92 +#define SYS_sysarch 165 +#define SYS___sysctl 202 +#define SYS_clock_gettime 232 +#define SYS_nanosleep 240 +#define SYS_sched_yield 331 +#define SYS_sigprocmask 340 +#define SYS_kqueue 362 +#define SYS_kevent 363 +#define SYS_sigaction 416 +#define SYS_thr_exit 431 +#define SYS_thr_self 432 +#define SYS_thr_kill 433 +#define SYS__umtx_op 454 +#define SYS_thr_new 455 +#define SYS_mmap 477 +#define SYS_cpuset_getaffinity 487 +#define SYS_pipe2 542 + TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0 MOVQ addr+0(FP), DI MOVL mode+8(FP), SI MOVL val+12(FP), DX MOVQ uaddr1+16(FP), R10 MOVQ ut+24(FP), R8 - MOVL $454, AX + MOVL $SYS__umtx_op, AX SYSCALL JCC 2(PC) NEGQ AX @@ -27,7 +63,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0 TEXT runtime·thr_new(SB),NOSPLIT,$0 MOVQ param+0(FP), DI MOVL size+8(FP), SI - MOVL $455, AX + MOVL $SYS_thr_new, AX SYSCALL JCC 2(PC) NEGQ AX @@ -55,7 +91,7 @@ TEXT runtime·thr_start(SB),NOSPLIT,$0 // Exit the entire program (like C exit) TEXT runtime·exit(SB),NOSPLIT,$-8 MOVL code+0(FP), DI // arg 1 exit status - MOVL $1, AX + MOVL $SYS_exit, AX SYSCALL MOVL $0xf1, 0xf1 // crash RET @@ -66,7 +102,7 @@ TEXT runtime·exitThread(SB),NOSPLIT,$0-8 // We're done using the stack. MOVL $0, (AX) MOVL $0, DI // arg 1 long *state - MOVL $431, AX // thr_exit + MOVL $SYS_thr_exit, AX SYSCALL MOVL $0xf1, 0xf1 // crash JMP 0(PC) @@ -75,7 +111,7 @@ TEXT runtime·open(SB),NOSPLIT,$-8 MOVQ name+0(FP), DI // arg 1 pathname MOVL mode+8(FP), SI // arg 2 flags MOVL perm+12(FP), DX // arg 3 mode - MOVL $5, AX + MOVL $SYS_open, AX SYSCALL JCC 2(PC) MOVL $-1, AX @@ -84,7 +120,7 @@ TEXT runtime·open(SB),NOSPLIT,$-8 TEXT runtime·closefd(SB),NOSPLIT,$-8 MOVL fd+0(FP), DI // arg 1 fd - MOVL $6, AX + MOVL $SYS_close, AX SYSCALL JCC 2(PC) MOVL $-1, AX @@ -95,33 +131,18 @@ TEXT runtime·read(SB),NOSPLIT,$-8 MOVL fd+0(FP), DI // arg 1 fd MOVQ p+8(FP), SI // arg 2 buf MOVL n+16(FP), DX // arg 3 count - MOVL $3, AX + MOVL $SYS_read, AX SYSCALL JCC 2(PC) NEGQ AX // caller expects negative errno MOVL AX, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVL $42, AX - SYSCALL - JCC ok - MOVL $0, r+0(FP) - MOVL $0, w+4(FP) - MOVL AX, errno+8(FP) - RET -ok: - MOVL AX, r+0(FP) - MOVL DX, w+4(FP) - MOVL $0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-20 LEAQ r+8(FP), DI MOVL flags+0(FP), SI - MOVL $542, AX + MOVL $SYS_pipe2, AX SYSCALL JCC 2(PC) NEGQ AX @@ -132,7 +153,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8 MOVQ fd+0(FP), DI // arg 1 fd MOVQ p+8(FP), SI // arg 2 buf MOVL n+16(FP), DX // arg 3 count - MOVL $4, AX + MOVL $SYS_write, AX SYSCALL JCC 2(PC) NEGQ AX // caller expects negative errno @@ -142,7 +163,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8 TEXT runtime·thr_self(SB),NOSPLIT,$0-8 // thr_self(&0(FP)) LEAQ ret+0(FP), DI // arg 1 - MOVL $432, AX + MOVL $SYS_thr_self, AX SYSCALL RET @@ -150,18 +171,18 @@ TEXT runtime·thr_kill(SB),NOSPLIT,$0-16 // thr_kill(tid, sig) MOVQ tid+0(FP), DI // arg 1 id MOVQ sig+8(FP), SI // arg 2 sig - MOVL $433, AX + MOVL $SYS_thr_kill, AX SYSCALL RET TEXT runtime·raiseproc(SB),NOSPLIT,$0 // getpid - MOVL $20, AX + MOVL $SYS_getpid, AX SYSCALL // kill(self, sig) MOVQ AX, DI // arg 1 pid MOVL sig+0(FP), SI // arg 2 sig - MOVL $37, AX + MOVL $SYS_kill, AX SYSCALL RET @@ -169,14 +190,14 @@ TEXT runtime·setitimer(SB), NOSPLIT, $-8 MOVL mode+0(FP), DI MOVQ new+8(FP), SI MOVQ old+16(FP), DX - MOVL $83, AX + MOVL $SYS_setitimer, AX SYSCALL RET // func fallback_walltime() (sec int64, nsec int32) TEXT runtime·fallback_walltime(SB), NOSPLIT, $32-12 - MOVL $232, AX // clock_gettime - MOVQ $0, DI // CLOCK_REALTIME + MOVL $SYS_clock_gettime, AX + MOVQ $CLOCK_REALTIME, DI LEAQ 8(SP), SI SYSCALL MOVQ 8(SP), AX // sec @@ -188,8 +209,8 @@ TEXT runtime·fallback_walltime(SB), NOSPLIT, $32-12 RET TEXT runtime·fallback_nanotime(SB), NOSPLIT, $32-8 - MOVL $232, AX - MOVQ $4, DI // CLOCK_MONOTONIC + MOVL $SYS_clock_gettime, AX + MOVQ $CLOCK_MONOTONIC, DI LEAQ 8(SP), SI SYSCALL MOVQ 8(SP), AX // sec @@ -206,7 +227,7 @@ TEXT runtime·asmSigaction(SB),NOSPLIT,$0 MOVQ sig+0(FP), DI // arg 1 sig MOVQ new+8(FP), SI // arg 2 act MOVQ old+16(FP), DX // arg 3 oact - MOVL $416, AX + MOVL $SYS_sigaction, AX SYSCALL JCC 2(PC) MOVL $-1, AX @@ -239,22 +260,55 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() - // Call into the Go signal handler + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. NOP SP // disable vet stack checking - ADJSP $24 - MOVQ DI, 0(SP) // sig - MOVQ SI, 8(SP) // info - MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + ADJSP $-24 POP_REGS_HOST_TO_ABI0() RET +// Called using C ABI. +TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$0 + // Transition from C ABI to Go ABI. + PUSH_REGS_HOST_TO_ABI0() + + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. + NOP SP // disable vet stack checking + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigprofNonGo(SB) + + ADJSP $-24 + + POP_REGS_HOST_TO_ABI0() + RET + // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 @@ -322,12 +376,12 @@ sigtrampnog: JNZ sigtramp // Skip stack trace if already locked. // Jump to the traceback function in runtime/cgo. - // It will call back to sigprofNonGo, which will ignore the - // arguments passed in registers. + // It will call back to sigprofNonGo, via sigprofNonGoWrapper, to convert + // the arguments to the Go calling convention. // First three arguments to traceback function are in registers already. MOVQ runtime·cgoTraceback(SB), CX MOVQ $runtime·sigprofCallers(SB), R8 - MOVQ $runtime·sigprofNonGo(SB), R9 + MOVQ $runtime·sigprofNonGoWrapper<>(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX @@ -338,7 +392,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$0 MOVL flags+20(FP), R10 // arg 4 flags MOVL fd+24(FP), R8 // arg 5 fid MOVL off+28(FP), R9 // arg 6 offset - MOVL $477, AX + MOVL $SYS_mmap, AX SYSCALL JCC ok MOVQ $0, p+32(FP) @@ -352,7 +406,7 @@ ok: TEXT runtime·munmap(SB),NOSPLIT,$0 MOVQ addr+0(FP), DI // arg 1 addr MOVQ n+8(FP), SI // arg 2 len - MOVL $73, AX + MOVL $SYS_munmap, AX SYSCALL JCC 2(PC) MOVL $0xf1, 0xf1 // crash @@ -362,7 +416,7 @@ TEXT runtime·madvise(SB),NOSPLIT,$0 MOVQ addr+0(FP), DI MOVQ n+8(FP), SI MOVL flags+16(FP), DX - MOVQ $75, AX // madvise + MOVQ $SYS_madvise, AX SYSCALL JCC 2(PC) MOVL $-1, AX @@ -372,7 +426,7 @@ TEXT runtime·madvise(SB),NOSPLIT,$0 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8 MOVQ new+0(FP), DI MOVQ old+8(FP), SI - MOVQ $53, AX + MOVQ $SYS_sigaltstack, AX SYSCALL JCC 2(PC) MOVL $0xf1, 0xf1 // crash @@ -390,7 +444,7 @@ TEXT runtime·usleep(SB),NOSPLIT,$16 MOVQ SP, DI // arg 1 - rqtp MOVQ $0, SI // arg 2 - rmtp - MOVL $240, AX // sys_nanosleep + MOVL $SYS_nanosleep, AX SYSCALL RET @@ -399,8 +453,8 @@ TEXT runtime·settls(SB),NOSPLIT,$8 ADDQ $8, DI // adjust for ELF: wants to use -8(FS) for g and m MOVQ DI, 0(SP) MOVQ SP, SI - MOVQ $129, DI // AMD64_SET_FSBASE - MOVQ $165, AX // sysarch + MOVQ $AMD64_SET_FSBASE, DI + MOVQ $SYS_sysarch, AX SYSCALL JCC 2(PC) MOVL $0xf1, 0xf1 // crash @@ -413,7 +467,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$0 MOVQ size+24(FP), R10 // arg 4 - oldlenp MOVQ dst+32(FP), R8 // arg 5 - newp MOVQ ndst+40(FP), R9 // arg 6 - newlen - MOVQ $202, AX // sys___sysctl + MOVQ $SYS___sysctl, AX SYSCALL JCC 4(PC) NEGQ AX @@ -424,7 +478,7 @@ TEXT runtime·sysctl(SB),NOSPLIT,$0 RET TEXT runtime·osyield(SB),NOSPLIT,$-4 - MOVL $331, AX // sys_sched_yield + MOVL $SYS_sched_yield, AX SYSCALL RET @@ -432,7 +486,7 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$0 MOVL how+0(FP), DI // arg 1 - how MOVQ new+8(FP), SI // arg 2 - set MOVQ old+16(FP), DX // arg 3 - oset - MOVL $340, AX // sys_sigprocmask + MOVL $SYS_sigprocmask, AX SYSCALL JAE 2(PC) MOVL $0xf1, 0xf1 // crash @@ -443,7 +497,7 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0 MOVQ $0, DI MOVQ $0, SI MOVQ $0, DX - MOVL $362, AX + MOVL $SYS_kqueue, AX SYSCALL JCC 2(PC) NEGQ AX @@ -458,7 +512,7 @@ TEXT runtime·kevent(SB),NOSPLIT,$0 MOVQ ev+24(FP), R10 MOVL nev+32(FP), R8 MOVQ ts+40(FP), R9 - MOVL $363, AX + MOVL $SYS_kevent, AX SYSCALL JCC 2(PC) NEGQ AX @@ -468,24 +522,9 @@ TEXT runtime·kevent(SB),NOSPLIT,$0 // void runtime·closeonexec(int32 fd); TEXT runtime·closeonexec(SB),NOSPLIT,$0 MOVL fd+0(FP), DI // fd - MOVQ $2, SI // F_SETFD - MOVQ $1, DX // FD_CLOEXEC - MOVL $92, AX // fcntl - SYSCALL - RET - -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVL fd+0(FP), DI // fd - MOVQ $3, SI // F_GETFL - MOVQ $0, DX - MOVL $92, AX // fcntl - SYSCALL - MOVL fd+0(FP), DI // fd - MOVQ $4, SI // F_SETFL - MOVQ $4, DX // O_NONBLOCK - ORL AX, DX - MOVL $92, AX // fcntl + MOVQ $F_SETFD, SI + MOVQ $FD_CLOEXEC, DX + MOVL $SYS_fcntl, AX SYSCALL RET @@ -496,7 +535,7 @@ TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-44 MOVQ id+16(FP), DX MOVQ size+24(FP), R10 MOVQ mask+32(FP), R8 - MOVL $487, AX + MOVL $SYS_cpuset_getaffinity, AX SYSCALL JCC 2(PC) NEGQ AX diff --git a/src/runtime/sys_freebsd_arm.s b/src/runtime/sys_freebsd_arm.s index b12e47c576daeb..cbee34d13be7f7 100644 --- a/src/runtime/sys_freebsd_arm.s +++ b/src/runtime/sys_freebsd_arm.s @@ -20,7 +20,6 @@ #define SYS_close (SYS_BASE + 6) #define SYS_getpid (SYS_BASE + 20) #define SYS_kill (SYS_BASE + 37) -#define SYS_pipe (SYS_BASE + 42) #define SYS_sigaltstack (SYS_BASE + 53) #define SYS_munmap (SYS_BASE + 73) #define SYS_madvise (SYS_BASE + 75) @@ -123,23 +122,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0 MOVW R0, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVW $SYS_pipe, R7 - SWI $0 - BCC ok - MOVW $0, R1 - MOVW R1, r+0(FP) - MOVW R1, w+4(FP) - MOVW R0, errno+8(FP) - RET -ok: - MOVW R0, r+0(FP) - MOVW R1, w+4(FP) - MOVW $0, R1 - MOVW R1, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVW $r+4(FP), R0 @@ -249,7 +231,7 @@ TEXT runtime·asmSigaction(SB),NOSPLIT|NOFRAME,$0 MOVW R0, ret+12(FP) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Reserve space for callee-save registers and arguments. MOVM.DB.W [R4-R11], (R13) SUB $16, R13 @@ -414,20 +396,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 SWI $0 RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVW fd+0(FP), R0 // fd - MOVW $3, R1 // F_GETFL - MOVW $0, R2 - MOVW $SYS_fcntl, R7 - SWI $0 - ORR $0x4, R0, R2 // O_NONBLOCK - MOVW fd+0(FP), R0 // fd - MOVW $4, R1 // F_SETFL - MOVW $SYS_fcntl, R7 - SWI $0 - RET - // TODO: this is only valid for ARMv7+ TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0 B runtime·armPublicationBarrier(SB) diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s index 1aa09e87ca1dc3..cb960773ebeed3 100644 --- a/src/runtime/sys_freebsd_arm64.s +++ b/src/runtime/sys_freebsd_arm64.s @@ -10,6 +10,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 4 @@ -133,18 +134,6 @@ ok: MOVW R0, ret+8(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVD $r+0(FP), R0 - MOVW $0, R1 - MOVD $SYS_pipe2, R8 - SVC - BCC ok - NEG R0, R0 -ok: - MOVW R0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVD $r+8(FP), R0 @@ -291,28 +280,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // func sigtramp() -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // this might be called in external code context, // where g is not set. @@ -323,31 +295,20 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BEQ 2(PC) BL runtime·load_g(SB) +#ifdef GOEXPERIMENT_regabiargs + // Restore signum to R0. + MOVW 8(RSP), R0 + // R1 and R2 already contain info and ctx, respectively. +#else MOVD R1, 16(RSP) MOVD R2, 24(RSP) - MOVD $runtime·sigtrampgo(SB), R0 - BL (R0) +#endif + MOVD $runtime·sigtrampgo(SB), R3 + BL (R3) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET @@ -492,20 +453,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SVC RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVW fd+0(FP), R0 - MOVD $F_GETFL, R1 - MOVD $0, R2 - MOVD $SYS_fcntl, R8 - SVC - ORR $O_NONBLOCK, R0, R2 - MOVW fd+0(FP), R0 - MOVW $F_SETFL, R1 - MOVW $SYS_fcntl, R7 - SVC - RET - // func getCntxct(physical bool) uint32 TEXT runtime·getCntxct(SB),NOSPLIT,$0 MOVB physical+0(FP), R0 @@ -513,7 +460,7 @@ TEXT runtime·getCntxct(SB),NOSPLIT,$0 BEQ 3(PC) // get CNTPCT (Physical Count Register) into R0 - MRS CNTPCT_EL0, R0 // SIGILL + MRS CNTPCT_EL0, R0 B 2(PC) // get CNTVCT (Virtual Count Register) into R0 diff --git a/src/runtime/sys_libc.go b/src/runtime/sys_libc.go index b1a9f8b5365709..0c6f13ca9f6fc1 100644 --- a/src/runtime/sys_libc.go +++ b/src/runtime/sys_libc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || (openbsd && !mips64) -// +build darwin openbsd,!mips64 package runtime @@ -13,6 +12,7 @@ import "unsafe" // fn is the raw pc value of the entry point of the desired function. // Switches to the system stack, if not already there. // Preserves the calling point as the location where a profiler traceback will begin. +// //go:nosplit func libcCall(fn, arg unsafe.Pointer) int32 { // Leave caller's PC/SP/G around for traceback. diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 3ae5a9099f096a..4942f21e4f430c 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -32,7 +32,6 @@ #define SYS_getpid 20 #define SYS_access 33 #define SYS_kill 37 -#define SYS_pipe 42 #define SYS_brk 45 #define SYS_fcntl 55 #define SYS_munmap 91 @@ -56,6 +55,9 @@ #define SYS_epoll_create 254 #define SYS_epoll_ctl 255 #define SYS_epoll_wait 256 +#define SYS_timer_create 259 +#define SYS_timer_settime 260 +#define SYS_timer_delete 263 #define SYS_clock_gettime 265 #define SYS_tgkill 270 #define SYS_epoll_create1 329 @@ -127,14 +129,6 @@ TEXT runtime·read(SB),NOSPLIT,$0 MOVL AX, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVL $SYS_pipe, AX - LEAL r+0(FP), BX - INVOKE_SYSCALL - MOVL AX, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVL $SYS_pipe2, AX @@ -210,6 +204,32 @@ TEXT runtime·setitimer(SB),NOSPLIT,$0-12 INVOKE_SYSCALL RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-16 + MOVL $SYS_timer_create, AX + MOVL clockid+0(FP), BX + MOVL sevp+4(FP), CX + MOVL timerid+8(FP), DX + INVOKE_SYSCALL + MOVL AX, ret+12(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-20 + MOVL $SYS_timer_settime, AX + MOVL timerid+0(FP), BX + MOVL flags+4(FP), CX + MOVL new+8(FP), DX + MOVL old+12(FP), SI + INVOKE_SYSCALL + MOVL AX, ret+16(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-8 + MOVL $SYS_timer_delete, AX + MOVL timerid+0(FP), BX + INVOKE_SYSCALL + MOVL AX, ret+4(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT,$0-16 MOVL $SYS_mincore, AX MOVL addr+0(FP), BX @@ -413,7 +433,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$12-16 RET // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$28 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$28 // Save callee-saved C registers, since the caller may be a C signal handler. MOVL BX, bx-4(SP) MOVL BP, bp-8(SP) @@ -753,21 +773,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 INVOKE_SYSCALL RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVL $SYS_fcntl, AX - MOVL fd+0(FP), BX // fd - MOVL $3, CX // F_GETFL - MOVL $0, DX - INVOKE_SYSCALL - MOVL fd+0(FP), BX // fd - MOVL $4, CX // F_SETFL - MOVL $0x800, DX // O_NONBLOCK - ORL AX, DX - MOVL $SYS_fcntl, AX - INVOKE_SYSCALL - RET - // int access(const char *name, int mode) TEXT runtime·access(SB),NOSPLIT,$0 MOVL $SYS_access, AX diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 33cc670b641590..ca6ecb13eb7e06 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -22,7 +22,6 @@ #define SYS_rt_sigaction 13 #define SYS_rt_sigprocmask 14 #define SYS_rt_sigreturn 15 -#define SYS_pipe 22 #define SYS_sched_yield 24 #define SYS_mincore 27 #define SYS_madvise 28 @@ -41,6 +40,9 @@ #define SYS_futex 202 #define SYS_sched_getaffinity 204 #define SYS_epoll_create 213 +#define SYS_timer_create 222 +#define SYS_timer_settime 223 +#define SYS_timer_delete 226 #define SYS_clock_gettime 228 #define SYS_exit_group 231 #define SYS_epoll_ctl 233 @@ -111,14 +113,6 @@ TEXT runtime·read(SB),NOSPLIT,$0-28 MOVL AX, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - LEAQ r+0(FP), DI - MOVL $SYS_pipe, AX - SYSCALL - MOVL AX, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-20 LEAQ r+8(FP), DI @@ -195,6 +189,32 @@ TEXT runtime·setitimer(SB),NOSPLIT,$0-24 SYSCALL RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVL clockid+0(FP), DI + MOVQ sevp+8(FP), SI + MOVQ timerid+16(FP), DX + MOVL $SYS_timer_create, AX + SYSCALL + MOVL AX, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVL timerid+0(FP), DI + MOVL flags+4(FP), SI + MOVQ new+8(FP), DX + MOVQ old+16(FP), R10 + MOVL $SYS_timer_settime, AX + SYSCALL + MOVL AX, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVL timerid+0(FP), DI + MOVL $SYS_timer_delete, AX + SYSCALL + MOVL AX, ret+8(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT,$0-28 MOVQ addr+0(FP), DI MOVQ n+8(FP), SI @@ -215,13 +235,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. -#ifdef GOEXPERIMENT_regabig MOVQ g_m(R14), BX // BX unchanged by C code. -#else - get_tls(CX) - MOVQ g(CX), AX - MOVQ g_m(AX), BX // BX unchanged by C code. -#endif // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, @@ -236,11 +250,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVQ CX, m_vdsoPC(BX) MOVQ DX, m_vdsoSP(BX) -#ifdef GOEXPERIMENT_regabig CMPQ R14, m_curg(BX) // Only switch if on curg. -#else - CMPQ AX, m_curg(BX) // Only switch if on curg. -#endif JNE noswitch MOVQ m_g0(BX), DX @@ -328,28 +338,59 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 POPQ BP RET -// Defined as ABIInternal since it does not use the stack-based Go ABI. // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() - // Call into the Go signal handler + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. NOP SP // disable vet stack checking - ADJSP $24 - MOVQ DI, 0(SP) // sig - MOVQ SI, 8(SP) // info - MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + ADJSP $-24 POP_REGS_HOST_TO_ABI0() RET +// Called using C ABI. +TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$0 + // Transition from C ABI to Go ABI. + PUSH_REGS_HOST_TO_ABI0() + + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. + NOP SP // disable vet stack checking + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigprofNonGo(SB) + + ADJSP $-24 + + POP_REGS_HOST_TO_ABI0() + RET + // Used instead of sigtramp in programs that use cgo. // Arguments from kernel are in DI, SI, DX. -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // If no traceback function, do usual sigtramp. MOVQ runtime·cgoTraceback(SB), AX TESTQ AX, AX @@ -392,12 +433,12 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 // The first three arguments, and the fifth, are already in registers. // Set the two remaining arguments now. MOVQ runtime·cgoTraceback(SB), CX - MOVQ $runtime·sigtramp(SB), R9 + MOVQ $runtime·sigtramp(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX sigtramp: - JMP runtime·sigtramp(SB) + JMP runtime·sigtramp(SB) sigtrampnog: // Signal arrived on a non-Go thread. If this is SIGPROF, get a @@ -414,12 +455,12 @@ sigtrampnog: JNZ sigtramp // Skip stack trace if already locked. // Jump to the traceback function in runtime/cgo. - // It will call back to sigprofNonGo, which will ignore the - // arguments passed in registers. + // It will call back to sigprofNonGo, via sigprofNonGoWrapper, to convert + // the arguments to the Go calling convention. // First three arguments to traceback function are in registers already. MOVQ runtime·cgoTraceback(SB), CX MOVQ $runtime·sigprofCallers(SB), R8 - MOVQ $runtime·sigprofNonGo(SB), R9 + MOVQ $runtime·sigprofNonGoWrapper<>(SB), R9 MOVQ _cgo_callers(SB), AX JMP AX @@ -428,8 +469,7 @@ sigtrampnog: // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c // The code that cares about the precise instructions used is: // https://gcc.gnu.org/viewcvs/gcc/trunk/libgcc/config/i386/linux-unwind.h?revision=219188&view=markup -// Defined as ABIInternal since it does not use the stack-based Go ABI. -TEXT runtime·sigreturn(SB),NOSPLIT,$0 +TEXT runtime·sigreturn(SB),NOSPLIT,$0 MOVQ $SYS_rt_sigreturn, AX SYSCALL INT $3 // not reached @@ -675,21 +715,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 SYSCALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVL fd+0(FP), DI // fd - MOVQ $3, SI // F_GETFL - MOVQ $0, DX - MOVL $SYS_fcntl, AX - SYSCALL - MOVL fd+0(FP), DI // fd - MOVQ $4, SI // F_SETFL - MOVQ $0x800, DX // O_NONBLOCK - ORL AX, DX - MOVL $SYS_fcntl, AX - SYSCALL - RET - // int access(const char *name, int mode) TEXT runtime·access(SB),NOSPLIT,$0 // This uses faccessat instead of access, because Android O blocks access. diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s index 02a5d4a6427402..9ac64328c19523 100644 --- a/src/runtime/sys_linux_arm.s +++ b/src/runtime/sys_linux_arm.s @@ -23,7 +23,6 @@ #define SYS_close (SYS_BASE + 6) #define SYS_getpid (SYS_BASE + 20) #define SYS_kill (SYS_BASE + 37) -#define SYS_pipe (SYS_BASE + 42) #define SYS_clone (SYS_BASE + 120) #define SYS_rt_sigreturn (SYS_BASE + 173) #define SYS_rt_sigaction (SYS_BASE + 174) @@ -45,6 +44,9 @@ #define SYS_epoll_create (SYS_BASE + 250) #define SYS_epoll_ctl (SYS_BASE + 251) #define SYS_epoll_wait (SYS_BASE + 252) +#define SYS_timer_create (SYS_BASE + 257) +#define SYS_timer_settime (SYS_BASE + 258) +#define SYS_timer_delete (SYS_BASE + 261) #define SYS_epoll_create1 (SYS_BASE + 357) #define SYS_pipe2 (SYS_BASE + 359) #define SYS_fcntl (SYS_BASE + 55) @@ -95,14 +97,6 @@ TEXT runtime·read(SB),NOSPLIT,$0 MOVW R0, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVW $r+0(FP), R0 - MOVW $SYS_pipe, R7 - SWI $0 - MOVW R0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVW $r+4(FP), R0 @@ -233,6 +227,32 @@ TEXT runtime·setitimer(SB),NOSPLIT,$0 SWI $0 RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-16 + MOVW clockid+0(FP), R0 + MOVW sevp+4(FP), R1 + MOVW timerid+8(FP), R2 + MOVW $SYS_timer_create, R7 + SWI $0 + MOVW R0, ret+12(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-20 + MOVW timerid+0(FP), R0 + MOVW flags+4(FP), R1 + MOVW new+8(FP), R2 + MOVW old+12(FP), R3 + MOVW $SYS_timer_settime, R7 + SWI $0 + MOVW R0, ret+16(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-8 + MOVW timerid+0(FP), R0 + MOVW $SYS_timer_delete, R7 + SWI $0 + MOVW R0, ret+4(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT,$0 MOVW addr+0(FP), R0 MOVW n+4(FP), R1 @@ -242,192 +262,150 @@ TEXT runtime·mincore(SB),NOSPLIT,$0 MOVW R0, ret+12(FP) RET -TEXT runtime·walltime(SB),NOSPLIT,$8-12 +// Call a VDSO function. +// +// R0-R3: arguments to VDSO function (C calling convention) +// R4: uintptr function to call +// +// There is no return value. +TEXT runtime·vdsoCall(SB),NOSPLIT,$8-0 + // R0-R3 may be arguments to fn, do not touch. + // R4 is function to call. + // R5-R9 are available as locals. They are unchanged by the C call + // (callee-save). + // We don't know how much stack space the VDSO code will need, // so switch to g0. // Save old SP. Use R13 instead of SP to avoid linker rewriting the offsets. - MOVW R13, R4 // R4 is unchanged by C code. + MOVW R13, R5 - MOVW g_m(g), R5 // R5 is unchanged by C code. + MOVW g_m(g), R6 // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, // so this function is reentrant. - MOVW m_vdsoPC(R5), R1 - MOVW m_vdsoSP(R5), R2 - MOVW R1, 4(R13) - MOVW R2, 8(R13) + MOVW m_vdsoPC(R6), R7 + MOVW m_vdsoSP(R6), R8 + MOVW R7, 4(R13) + MOVW R8, 8(R13) - MOVW LR, m_vdsoPC(R5) - MOVW R13, m_vdsoSP(R5) + MOVW $sp-4(FP), R7 // caller's SP + MOVW LR, m_vdsoPC(R6) + MOVW R7, m_vdsoSP(R6) - MOVW m_curg(R5), R0 + MOVW m_curg(R6), R7 - CMP g, R0 // Only switch if on curg. + CMP g, R7 // Only switch if on curg. B.NE noswitch - MOVW m_g0(R5), R0 - MOVW (g_sched+gobuf_sp)(R0), R13 // Set SP to g0 stack + MOVW m_g0(R6), R7 + MOVW (g_sched+gobuf_sp)(R7), R13 // Set SP to g0 stack noswitch: - SUB $24, R13 // Space for results BIC $0x7, R13 // Align for C code - MOVW $CLOCK_REALTIME, R0 - MOVW $8(R13), R1 // timespec - MOVW runtime·vdsoClockgettimeSym(SB), R2 - CMP $0, R2 - B.EQ fallback - // Store g on gsignal's stack, so if we receive a signal // during VDSO code we can find the g. - // If we don't have a signal stack, we won't receive signal, - // so don't bother saving g. - // When using cgo, we already saved g on TLS, also don't save - // g here. - // Also don't save g if we are already on the signal stack. - // We won't get a nested signal. - MOVB runtime·iscgo(SB), R6 - CMP $0, R6 + + // When using cgo, we already saved g on TLS, also don't save g here. + MOVB runtime·iscgo(SB), R7 + CMP $0, R7 BNE nosaveg - MOVW m_gsignal(R5), R6 // g.m.gsignal - CMP $0, R6 + // If we don't have a signal stack, we won't receive signal, so don't + // bother saving g. + MOVW m_gsignal(R6), R7 // g.m.gsignal + CMP $0, R7 + BEQ nosaveg + // Don't save g if we are already on the signal stack, as we won't get + // a nested signal. + CMP g, R7 BEQ nosaveg - CMP g, R6 + // If we don't have a signal stack, we won't receive signal, so don't + // bother saving g. + MOVW (g_stack+stack_lo)(R7), R7 // g.m.gsignal.stack.lo + CMP $0, R7 BEQ nosaveg - MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo - MOVW g, (R6) + MOVW g, (R7) - BL (R2) + BL (R4) - MOVW $0, R1 - MOVW R1, (R6) // clear g slot, R6 is unchanged by C code + MOVW $0, R8 + MOVW R8, (R7) // clear g slot JMP finish nosaveg: - BL (R2) - JMP finish - -fallback: - MOVW $SYS_clock_gettime, R7 - SWI $0 + BL (R4) finish: - MOVW 8(R13), R0 // sec - MOVW 12(R13), R2 // nsec - - MOVW R4, R13 // Restore real SP + MOVW R5, R13 // Restore real SP // Restore vdsoPC, vdsoSP // We don't worry about being signaled between the two stores. // If we are not in a signal handler, we'll restore vdsoSP to 0, // and no one will care about vdsoPC. If we are in a signal handler, // we cannot receive another signal. - MOVW 8(R13), R1 - MOVW R1, m_vdsoSP(R5) - MOVW 4(R13), R1 - MOVW R1, m_vdsoPC(R5) - - MOVW R0, sec_lo+0(FP) - MOVW R1, sec_hi+4(FP) - MOVW R2, nsec+8(FP) + MOVW 8(R13), R7 + MOVW R7, m_vdsoSP(R6) + MOVW 4(R13), R7 + MOVW R7, m_vdsoPC(R6) RET -// int64 nanotime1(void) -TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 - // Switch to g0 stack. See comment above in runtime·walltime. - - // Save old SP. Use R13 instead of SP to avoid linker rewriting the offsets. - MOVW R13, R4 // R4 is unchanged by C code. - - MOVW g_m(g), R5 // R5 is unchanged by C code. +TEXT runtime·walltime(SB),NOSPLIT,$12-12 + MOVW $CLOCK_REALTIME, R0 + MOVW $spec-12(SP), R1 // timespec - // Set vdsoPC and vdsoSP for SIGPROF traceback. - // Save the old values on stack and restore them on exit, - // so this function is reentrant. - MOVW m_vdsoPC(R5), R1 - MOVW m_vdsoSP(R5), R2 - MOVW R1, 4(R13) - MOVW R2, 8(R13) + MOVW runtime·vdsoClockgettimeSym(SB), R4 + CMP $0, R4 + B.EQ fallback - MOVW LR, m_vdsoPC(R5) - MOVW R13, m_vdsoSP(R5) + BL runtime·vdsoCall(SB) - MOVW m_curg(R5), R0 + JMP finish - CMP g, R0 // Only switch if on curg. - B.NE noswitch +fallback: + MOVW $SYS_clock_gettime, R7 + SWI $0 - MOVW m_g0(R5), R0 - MOVW (g_sched+gobuf_sp)(R0), R13 // Set SP to g0 stack +finish: + MOVW sec-12(SP), R0 // sec + MOVW nsec-8(SP), R2 // nsec -noswitch: - SUB $24, R13 // Space for results - BIC $0x7, R13 // Align for C code + MOVW R0, sec_lo+0(FP) + MOVW $0, R1 + MOVW R1, sec_hi+4(FP) + MOVW R2, nsec+8(FP) + RET +// func nanotime1() int64 +TEXT runtime·nanotime1(SB),NOSPLIT,$12-8 MOVW $CLOCK_MONOTONIC, R0 - MOVW $8(R13), R1 // timespec - MOVW runtime·vdsoClockgettimeSym(SB), R2 - CMP $0, R2 - B.EQ fallback - - // Store g on gsignal's stack, so if we receive a signal - // during VDSO code we can find the g. - // If we don't have a signal stack, we won't receive signal, - // so don't bother saving g. - // When using cgo, we already saved g on TLS, also don't save - // g here. - // Also don't save g if we are already on the signal stack. - // We won't get a nested signal. - MOVB runtime·iscgo(SB), R6 - CMP $0, R6 - BNE nosaveg - MOVW m_gsignal(R5), R6 // g.m.gsignal - CMP $0, R6 - BEQ nosaveg - CMP g, R6 - BEQ nosaveg - MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo - MOVW g, (R6) + MOVW $spec-12(SP), R1 // timespec - BL (R2) + MOVW runtime·vdsoClockgettimeSym(SB), R4 + CMP $0, R4 + B.EQ fallback - MOVW $0, R1 - MOVW R1, (R6) // clear g slot, R6 is unchanged by C code + BL runtime·vdsoCall(SB) JMP finish -nosaveg: - BL (R2) - JMP finish - fallback: MOVW $SYS_clock_gettime, R7 SWI $0 finish: - MOVW 8(R13), R0 // sec - MOVW 12(R13), R2 // nsec - - MOVW R4, R13 // Restore real SP - // Restore vdsoPC, vdsoSP - // We don't worry about being signaled between the two stores. - // If we are not in a signal handler, we'll restore vdsoSP to 0, - // and no one will care about vdsoPC. If we are in a signal handler, - // we cannot receive another signal. - MOVW 8(R13), R4 - MOVW R4, m_vdsoSP(R5) - MOVW 4(R13), R4 - MOVW R4, m_vdsoPC(R5) + MOVW sec-12(SP), R0 // sec + MOVW nsec-8(SP), R2 // nsec MOVW $1000000000, R3 MULLU R0, R3, (R1, R0) ADD.S R2, R0 - ADC R4, R1 + ADC $0, R1 // Add carry bit to upper half. MOVW R0, ret_lo+0(FP) MOVW R1, ret_hi+4(FP) + RET // int32 futex(int32 *uaddr, int32 op, int32 val, @@ -543,7 +521,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 MOVW R4, R13 RET -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Reserve space for callee-save registers and arguments. MOVM.DB.W [R4-R11], (R13) SUB $16, R13 @@ -685,20 +663,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 SWI $0 RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVW fd+0(FP), R0 // fd - MOVW $3, R1 // F_GETFL - MOVW $0, R2 - MOVW $SYS_fcntl, R7 - SWI $0 - ORR $0x800, R0, R2 // O_NONBLOCK - MOVW fd+0(FP), R0 // fd - MOVW $4, R1 // F_SETFL - MOVW $SYS_fcntl, R7 - SWI $0 - RET - // b __kuser_get_tls @ 0xffff0fe0 TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0 MOVW $0xffff0fe0, R0 diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index 69ac16027800f3..b47b6fd0a05f6d 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define AT_FDCWD -100 @@ -49,6 +50,9 @@ #define SYS_socket 198 #define SYS_connect 203 #define SYS_brk 214 +#define SYS_timer_create 107 +#define SYS_timer_settime 110 +#define SYS_timer_delete 111 TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4 MOVW code+0(FP), R0 @@ -110,15 +114,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW R0, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVD $r+0(FP), R0 - MOVW $0, R1 - MOVW $SYS_pipe2, R8 - SVC - MOVW R0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVD $r+8(FP), R0 @@ -197,6 +192,32 @@ TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 SVC RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVW clockid+0(FP), R0 + MOVD sevp+8(FP), R1 + MOVD timerid+16(FP), R2 + MOVD $SYS_timer_create, R8 + SVC + MOVW R0, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVW timerid+0(FP), R0 + MOVW flags+4(FP), R1 + MOVD new+8(FP), R2 + MOVD old+16(FP), R3 + MOVD $SYS_timer_settime, R8 + SVC + MOVW R0, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVW timerid+0(FP), R0 + MOVD $SYS_timer_delete, R8 + SVC + MOVW R0, ret+8(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOVD addr+0(FP), R0 MOVD n+8(FP), R1 @@ -221,8 +242,9 @@ TEXT runtime·walltime(SB),NOSPLIT,$24-12 MOVD R2, 8(RSP) MOVD R3, 16(RSP) + MOVD $ret-8(FP), R2 // caller's SP MOVD LR, m_vdsoPC(R21) - MOVD R20, m_vdsoSP(R21) + MOVD R2, m_vdsoSP(R21) MOVD m_curg(R21), R0 CMP g, R0 @@ -304,8 +326,9 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$24-8 MOVD R2, 8(RSP) MOVD R3, 16(RSP) + MOVD $ret-8(FP), R2 // caller's SP MOVD LR, m_vdsoPC(R21) - MOVD R20, m_vdsoSP(R21) + MOVD R2, m_vdsoSP(R21) MOVD m_curg(R21), R0 CMP g, R0 @@ -421,28 +444,12 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +// Called from c-abi, R0: sig, R1: info, R2: cxt +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // this might be called in external code context, // where g is not set. @@ -452,37 +459,131 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 CBZ R0, 2(PC) BL runtime·load_g(SB) +#ifdef GOEXPERIMENT_regabiargs + // Restore signum to R0. + MOVW 8(RSP), R0 + // R1 and R2 already contain info and ctx, respectively. +#else MOVD R1, 16(RSP) MOVD R2, 24(RSP) - MOVD $runtime·sigtrampgo(SB), R0 - BL (R0) +#endif + MOVD $runtime·sigtrampgo(SB), R3 + BL (R3) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 - - RET - -TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 - MOVD $runtime·sigtramp(SB), R3 - B (R3) + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + + RET + +// Called from c-abi, R0: sig, R1: info, R2: cxt +TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$176 + // Save callee-save registers because it's a callback from c code. + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) + +#ifdef GOEXPERIMENT_regabiargs + // R0, R1 and R2 already contain sig, info and ctx, respectively. +#else + MOVW R0, 8(RSP) // sig + MOVD R1, 16(RSP) // info + MOVD R2, 24(RSP) // ctx +#endif + CALL runtime·sigprofNonGo(SB) + + // Restore callee-save registers. + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + RET + +// Called from c-abi, R0: sig, R1: info, R2: cxt +TEXT runtime·cgoSigtramp(SB),NOSPLIT|NOFRAME,$0 + // The stack unwinder, presumably written in C, may not be able to + // handle Go frame correctly. So, this function is NOFRAME, and we + // save/restore LR manually. + MOVD LR, R10 + // Save R27, g because they will be clobbered, + // we need to restore them before jump to sigtramp. + MOVD R27, R11 + MOVD g, R12 + + // If no traceback function, do usual sigtramp. + MOVD runtime·cgoTraceback(SB), R6 + CBZ R6, sigtramp + + // If no traceback support function, which means that + // runtime/cgo was not linked in, do usual sigtramp. + MOVD _cgo_callers(SB), R7 + CBZ R7, sigtramp + + // Figure out if we are currently in a cgo call. + // If not, just do usual sigtramp. + // first save R0, because runtime·load_g will clobber it. + MOVD R0, R8 + // Set up g register. + CALL runtime·load_g(SB) + MOVD R8, R0 + + CBZ g, sigtrampnog // g == nil + MOVD g_m(g), R6 + CBZ R6, sigtramp // g.m == nil + MOVW m_ncgo(R6), R7 + CBZW R7, sigtramp // g.m.ncgo = 0 + MOVD m_curg(R6), R8 + CBZ R8, sigtramp // g.m.curg == nil + MOVD g_syscallsp(R8), R7 + CBZ R7, sigtramp // g.m.curg.syscallsp == 0 + MOVD m_cgoCallers(R6), R4 // R4 is the fifth arg in C calling convention. + CBZ R4, sigtramp // g.m.cgoCallers == nil + MOVW m_cgoCallersUse(R6), R8 + CBNZW R8, sigtramp // g.m.cgoCallersUse != 0 + + // Jump to a function in runtime/cgo. + // That function, written in C, will call the user's traceback + // function with proper unwind info, and will then call back here. + // The first three arguments, and the fifth, are already in registers. + // Set the two remaining arguments now. + MOVD runtime·cgoTraceback(SB), R3 + MOVD $runtime·sigtramp(SB), R5 + MOVD _cgo_callers(SB), R13 + MOVD R10, LR // restore + MOVD R11, R27 + MOVD R12, g + B (R13) + +sigtramp: + MOVD R10, LR // restore + MOVD R11, R27 + MOVD R12, g + B runtime·sigtramp(SB) + +sigtrampnog: + // Signal arrived on a non-Go thread. If this is SIGPROF, get a + // stack trace. + CMPW $27, R0 // 27 == SIGPROF + BNE sigtramp + + // Lock sigprofCallersUse (cas from 0 to 1). + MOVW $1, R7 + MOVD $runtime·sigprofCallersUse(SB), R8 +load_store_loop: + LDAXRW (R8), R9 + CBNZW R9, sigtramp // Skip stack trace if already locked. + STLXRW R7, (R8), R9 + CBNZ R9, load_store_loop + + // Jump to the traceback function in runtime/cgo. + // It will call back to sigprofNonGo, which will ignore the + // arguments passed in registers. + // First three arguments to traceback function are in registers already. + MOVD runtime·cgoTraceback(SB), R3 + MOVD $runtime·sigprofCallers(SB), R4 + MOVD $runtime·sigprofNonGoWrapper<>(SB), R5 + MOVD _cgo_callers(SB), R13 + MOVD R10, LR // restore + MOVD R11, R27 + MOVD R12, g + B (R13) TEXT runtime·sysMmap(SB),NOSPLIT|NOFRAME,$0 MOVD addr+0(FP), R0 @@ -709,21 +810,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SVC RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R0 // fd - MOVD $3, R1 // F_GETFL - MOVD $0, R2 - MOVD $SYS_fcntl, R8 - SVC - MOVD $0x800, R2 // O_NONBLOCK - ORR R0, R2 - MOVW fd+0(FP), R0 // fd - MOVD $4, R1 // F_SETFL - MOVD $SYS_fcntl, R8 - SVC - RET - // int access(const char *name, int mode) TEXT runtime·access(SB),NOSPLIT,$0-20 MOVD $AT_FDCWD, R0 diff --git a/src/runtime/sys_linux_loong64.s b/src/runtime/sys_linux_loong64.s new file mode 100644 index 00000000000000..36a92df87cf6a5 --- /dev/null +++ b/src/runtime/sys_linux_loong64.s @@ -0,0 +1,607 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// System calls and other sys.stuff for loong64, Linux +// + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" + +#define AT_FDCWD -100 + +#define SYS_exit 93 +#define SYS_read 63 +#define SYS_write 64 +#define SYS_close 57 +#define SYS_getpid 172 +#define SYS_kill 129 +#define SYS_fcntl 25 +#define SYS_mmap 222 +#define SYS_munmap 215 +#define SYS_setitimer 103 +#define SYS_clone 220 +#define SYS_nanosleep 101 +#define SYS_sched_yield 124 +#define SYS_rt_sigreturn 139 +#define SYS_rt_sigaction 134 +#define SYS_rt_sigprocmask 135 +#define SYS_sigaltstack 132 +#define SYS_madvise 233 +#define SYS_mincore 232 +#define SYS_gettid 178 +#define SYS_futex 98 +#define SYS_sched_getaffinity 123 +#define SYS_exit_group 94 +#define SYS_epoll_ctl 21 +#define SYS_tgkill 131 +#define SYS_openat 56 +#define SYS_epoll_pwait 22 +#define SYS_clock_gettime 113 +#define SYS_epoll_create1 20 +#define SYS_brk 214 +#define SYS_pipe2 59 +#define SYS_timer_create 107 +#define SYS_timer_settime 110 +#define SYS_timer_delete 111 + +TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4 + MOVW code+0(FP), R4 + MOVV $SYS_exit_group, R11 + SYSCALL + RET + +// func exitThread(wait *uint32) +TEXT runtime·exitThread(SB),NOSPLIT|NOFRAME,$0-8 + MOVV wait+0(FP), R19 + // We're done using the stack. + MOVW $0, R11 + DBAR + MOVW R11, (R19) + DBAR + MOVW $0, R4 // exit code + MOVV $SYS_exit, R11 + SYSCALL + JMP 0(PC) + +TEXT runtime·open(SB),NOSPLIT|NOFRAME,$0-20 + MOVW $AT_FDCWD, R4 // AT_FDCWD, so this acts like open + MOVV name+0(FP), R5 + MOVW mode+8(FP), R6 + MOVW perm+12(FP), R7 + MOVV $SYS_openat, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVW $-1, R4 + MOVW R4, ret+16(FP) + RET + +TEXT runtime·closefd(SB),NOSPLIT|NOFRAME,$0-12 + MOVW fd+0(FP), R4 + MOVV $SYS_close, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVW $-1, R4 + MOVW R4, ret+8(FP) + RET + +TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28 + MOVV fd+0(FP), R4 + MOVV p+8(FP), R5 + MOVW n+16(FP), R6 + MOVV $SYS_write, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 + MOVW fd+0(FP), R4 + MOVV p+8(FP), R5 + MOVW n+16(FP), R6 + MOVV $SYS_read, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// func pipe2(flags int32) (r, w int32, errno int32) +TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 + MOVV $r+8(FP), R4 + MOVW flags+0(FP), R5 + MOVV $SYS_pipe2, R11 + SYSCALL + MOVW R4, errno+16(FP) + RET + +TEXT runtime·usleep(SB),NOSPLIT,$16-4 + MOVWU usec+0(FP), R6 + MOVV R6, R5 + MOVW $1000000, R4 + DIVVU R4, R6, R6 + MOVV R6, 8(R3) + MOVW $1000, R4 + MULVU R6, R4, R4 + SUBVU R4, R5 + MOVV R5, 16(R3) + + // nanosleep(&ts, 0) + ADDV $8, R3, R4 + MOVW $0, R5 + MOVV $SYS_nanosleep, R11 + SYSCALL + RET + +TEXT runtime·gettid(SB),NOSPLIT,$0-4 + MOVV $SYS_gettid, R11 + SYSCALL + MOVW R4, ret+0(FP) + RET + +TEXT runtime·raise(SB),NOSPLIT|NOFRAME,$0 + MOVV $SYS_getpid, R11 + SYSCALL + MOVW R4, R23 + MOVV $SYS_gettid, R11 + SYSCALL + MOVW R4, R5 // arg 2 tid + MOVW R23, R4 // arg 1 pid + MOVW sig+0(FP), R6 // arg 3 + MOVV $SYS_tgkill, R11 + SYSCALL + RET + +TEXT runtime·raiseproc(SB),NOSPLIT|NOFRAME,$0 + MOVV $SYS_getpid, R11 + SYSCALL + //MOVW R4, R4 // arg 1 pid + MOVW sig+0(FP), R5 // arg 2 + MOVV $SYS_kill, R11 + SYSCALL + RET + +TEXT ·getpid(SB),NOSPLIT|NOFRAME,$0-8 + MOVV $SYS_getpid, R11 + SYSCALL + MOVV R4, ret+0(FP) + RET + +TEXT ·tgkill(SB),NOSPLIT|NOFRAME,$0-24 + MOVV tgid+0(FP), R4 + MOVV tid+8(FP), R5 + MOVV sig+16(FP), R6 + MOVV $SYS_tgkill, R11 + SYSCALL + RET + +TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 + MOVW mode+0(FP), R4 + MOVV new+8(FP), R5 + MOVV old+16(FP), R6 + MOVV $SYS_setitimer, R11 + SYSCALL + RET + +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVW clockid+0(FP), R4 + MOVV sevp+8(FP), R5 + MOVV timerid+16(FP), R6 + MOVV $SYS_timer_create, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVW timerid+0(FP), R4 + MOVW flags+4(FP), R5 + MOVV new+8(FP), R6 + MOVV old+16(FP), R7 + MOVV $SYS_timer_settime, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVW timerid+0(FP), R4 + MOVV $SYS_timer_delete, R11 + SYSCALL + MOVW R4, ret+8(FP) + RET + +TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 + MOVV addr+0(FP), R4 + MOVV n+8(FP), R5 + MOVV dst+16(FP), R6 + MOVV $SYS_mincore, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// func walltime() (sec int64, nsec int32) +TEXT runtime·walltime(SB),NOSPLIT,$16-12 + MOVV R3, R23 // R23 is unchanged by C code + MOVV R3, R25 + + MOVV g_m(g), R24 // R24 = m + + // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVV m_vdsoPC(R24), R11 + MOVV m_vdsoSP(R24), R7 + MOVV R11, 8(R3) + MOVV R7, 16(R3) + + MOVV $ret-8(FP), R11 // caller's SP + MOVV R1, m_vdsoPC(R24) + MOVV R11, m_vdsoSP(R24) + + MOVV m_curg(R24), R4 + MOVV g, R5 + BNE R4, R5, noswitch + + MOVV m_g0(R24), R4 + MOVV (g_sched+gobuf_sp)(R4), R25 // Set SP to g0 stack + +noswitch: + SUBV $16, R25 + AND $~15, R25 // Align for C code + MOVV R25, R3 + + MOVW $0, R4 // CLOCK_REALTIME=0 + MOVV $0(R3), R5 + + MOVV runtime·vdsoClockgettimeSym(SB), R20 + BEQ R20, fallback + + JAL (R20) + +finish: + MOVV 0(R3), R7 // sec + MOVV 8(R3), R5 // nsec + + MOVV R23, R3 // restore SP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVV 16(R3), R25 + MOVV R25, m_vdsoSP(R24) + MOVV 8(R3), R25 + MOVV R25, m_vdsoPC(R24) + + MOVV R7, sec+0(FP) + MOVW R5, nsec+8(FP) + RET + +fallback: + MOVV $SYS_clock_gettime, R11 + SYSCALL + JMP finish + +TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 + MOVV R3, R23 // R23 is unchanged by C code + MOVV R3, R25 + + MOVV g_m(g), R24 // R24 = m + + // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVV m_vdsoPC(R24), R11 + MOVV m_vdsoSP(R24), R7 + MOVV R11, 8(R3) + MOVV R7, 16(R3) + + MOVV $ret-8(FP), R11 // caller's SP + MOVV R1, m_vdsoPC(R24) + MOVV R11, m_vdsoSP(R24) + + MOVV m_curg(R24), R4 + MOVV g, R5 + BNE R4, R5, noswitch + + MOVV m_g0(R24), R4 + MOVV (g_sched+gobuf_sp)(R4), R25 // Set SP to g0 stack + +noswitch: + SUBV $16, R25 + AND $~15, R25 // Align for C code + MOVV R25, R3 + + MOVW $1, R4 // CLOCK_MONOTONIC=1 + MOVV $0(R3), R5 + + MOVV runtime·vdsoClockgettimeSym(SB), R20 + BEQ R20, fallback + + JAL (R20) + +finish: + MOVV 0(R3), R7 // sec + MOVV 8(R3), R5 // nsec + + MOVV R23, R3 // restore SP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVV 16(R3), R25 + MOVV R25, m_vdsoSP(R24) + MOVV 8(R3), R25 + MOVV R25, m_vdsoPC(R24) + + // sec is in R7, nsec in R5 + // return nsec in R7 + MOVV $1000000000, R4 + MULVU R4, R7, R7 + ADDVU R5, R7 + MOVV R7, ret+0(FP) + RET + +fallback: + MOVV $SYS_clock_gettime, R11 + SYSCALL + JMP finish + +TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 + MOVW how+0(FP), R4 + MOVV new+8(FP), R5 + MOVV old+16(FP), R6 + MOVW size+24(FP), R7 + MOVV $SYS_rt_sigprocmask, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVV R0, 0xf1(R0) // crash + RET + +TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 + MOVV sig+0(FP), R4 + MOVV new+8(FP), R5 + MOVV old+16(FP), R6 + MOVV size+24(FP), R7 + MOVV $SYS_rt_sigaction, R11 + SYSCALL + MOVW R4, ret+32(FP) + RET + +TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 + MOVW sig+8(FP), R4 + MOVV info+16(FP), R5 + MOVV ctx+24(FP), R6 + MOVV fn+0(FP), R20 + JAL (R20) + RET + +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$64 + // this might be called in external code context, + // where g is not set. + MOVB runtime·iscgo(SB), R19 + BEQ R19, 2(PC) + JAL runtime·load_g(SB) + + MOVW R4, 8(R3) + MOVV R5, 16(R3) + MOVV R6, 24(R3) + MOVV $runtime·sigtrampgo(SB), R19 + JAL (R19) + RET + +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + JMP runtime·sigtramp(SB) + +TEXT runtime·mmap(SB),NOSPLIT|NOFRAME,$0 + MOVV addr+0(FP), R4 + MOVV n+8(FP), R5 + MOVW prot+16(FP), R6 + MOVW flags+20(FP), R7 + MOVW fd+24(FP), R8 + MOVW off+28(FP), R9 + + MOVV $SYS_mmap, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, ok + MOVV $0, p+32(FP) + SUBVU R4, R0, R4 + MOVV R4, err+40(FP) + RET +ok: + MOVV R4, p+32(FP) + MOVV $0, err+40(FP) + RET + +TEXT runtime·munmap(SB),NOSPLIT|NOFRAME,$0 + MOVV addr+0(FP), R4 + MOVV n+8(FP), R5 + MOVV $SYS_munmap, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVV R0, 0xf3(R0) // crash + RET + +TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0 + MOVV addr+0(FP), R4 + MOVV n+8(FP), R5 + MOVW flags+16(FP), R6 + MOVV $SYS_madvise, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0 + MOVV addr+0(FP), R4 + MOVW op+8(FP), R5 + MOVW val+12(FP), R6 + MOVV ts+16(FP), R7 + MOVV addr2+24(FP), R8 + MOVW val3+32(FP), R9 + MOVV $SYS_futex, R11 + SYSCALL + MOVW R4, ret+40(FP) + RET + +// int64 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void)); +TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 + MOVW flags+0(FP), R4 + MOVV stk+8(FP), R5 + + // Copy mp, gp, fn off parent stack for use by child. + // Careful: Linux system call clobbers ???. + MOVV mp+16(FP), R23 + MOVV gp+24(FP), R24 + MOVV fn+32(FP), R25 + + MOVV R23, -8(R5) + MOVV R24, -16(R5) + MOVV R25, -24(R5) + MOVV $1234, R23 + MOVV R23, -32(R5) + + MOVV $SYS_clone, R11 + SYSCALL + + // In parent, return. + BEQ R4, 3(PC) + MOVW R4, ret+40(FP) + RET + + // In child, on new stack. + MOVV -32(R3), R23 + MOVV $1234, R19 + BEQ R23, R19, 2(PC) + MOVV R0, 0(R0) + + // Initialize m->procid to Linux tid + MOVV $SYS_gettid, R11 + SYSCALL + + MOVV -24(R3), R25 // fn + MOVV -16(R3), R24 // g + MOVV -8(R3), R23 // m + + BEQ R23, nog + BEQ R24, nog + + MOVV R4, m_procid(R23) + + // TODO: setup TLS. + + // In child, set up new stack + MOVV R23, g_m(R24) + MOVV R24, g + //CALL runtime·stackcheck(SB) + +nog: + // Call fn + JAL (R25) + + // It shouldn't return. If it does, exit that thread. + MOVW $111, R4 + MOVV $SYS_exit, R11 + SYSCALL + JMP -3(PC) // keep exiting + +TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0 + MOVV new+0(FP), R4 + MOVV old+8(FP), R5 + MOVV $SYS_sigaltstack, R11 + SYSCALL + MOVW $-4096, R5 + BGEU R5, R4, 2(PC) + MOVV R0, 0xf1(R0) // crash + RET + +TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0 + MOVV $SYS_sched_yield, R11 + SYSCALL + RET + +TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 + MOVV pid+0(FP), R4 + MOVV len+8(FP), R5 + MOVV buf+16(FP), R6 + MOVV $SYS_sched_getaffinity, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// int32 runtime·epollcreate(int32 size); +TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0 + MOVW size+0(FP), R4 + MOVV $SYS_epoll_create1, R11 + SYSCALL + MOVW R4, ret+8(FP) + RET + +// int32 runtime·epollcreate1(int32 flags); +TEXT runtime·epollcreate1(SB),NOSPLIT|NOFRAME,$0 + MOVW flags+0(FP), R4 + MOVV $SYS_epoll_create1, R11 + SYSCALL + MOVW R4, ret+8(FP) + RET + +// func epollctl(epfd, op, fd int32, ev *epollEvent) int +TEXT runtime·epollctl(SB),NOSPLIT|NOFRAME,$0 + MOVW epfd+0(FP), R4 + MOVW op+4(FP), R5 + MOVW fd+8(FP), R6 + MOVV ev+16(FP), R7 + MOVV $SYS_epoll_ctl, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout); +TEXT runtime·epollwait(SB),NOSPLIT|NOFRAME,$0 + MOVW epfd+0(FP), R4 + MOVV ev+8(FP), R5 + MOVW nev+16(FP), R6 + MOVW timeout+20(FP), R7 + MOVV $0, R8 + MOVV $SYS_epoll_pwait, R11 + SYSCALL + MOVW R4, ret+24(FP) + RET + +// void runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 + MOVW fd+0(FP), R4 // fd + MOVV $2, R5 // F_SETFD + MOVV $1, R6 // FD_CLOEXEC + MOVV $SYS_fcntl, R11 + SYSCALL + RET + +// func sbrk0() uintptr +TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8 + // Implemented as brk(NULL). + MOVV $0, R4 + MOVV $SYS_brk, R11 + SYSCALL + MOVV R4, ret+0(FP) + RET + +TEXT runtime·access(SB),$0-20 + MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go + MOVW R0, ret+16(FP) // for vet + RET + +TEXT runtime·connect(SB),$0-28 + MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go + MOVW R0, ret+24(FP) // for vet + RET + +TEXT runtime·socket(SB),$0-20 + MOVV R0, 2(R0) // unimplemented, only needed for android; declared in stubs_linux.go + MOVW R0, ret+16(FP) // for vet + RET diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s index e18d29144563fe..06d54dff78f0c6 100644 --- a/src/runtime/sys_linux_mips64x.s +++ b/src/runtime/sys_linux_mips64x.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le // // System calls and other sys.stuff for mips64, Linux @@ -41,6 +39,9 @@ #define SYS_exit_group 5205 #define SYS_epoll_create 5207 #define SYS_epoll_ctl 5208 +#define SYS_timer_create 5216 +#define SYS_timer_settime 5217 +#define SYS_timer_delete 5220 #define SYS_tgkill 5225 #define SYS_openat 5247 #define SYS_epoll_pwait 5272 @@ -112,17 +113,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW R2, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVV $r+0(FP), R4 - MOVV R0, R5 - MOVV $SYS_pipe2, R2 - SYSCALL - BEQ R7, 2(PC) - SUBVU R2, R0, R2 // caller expects negative errno - MOVW R2, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVV $r+8(FP), R4 @@ -204,6 +194,32 @@ TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 SYSCALL RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVW clockid+0(FP), R4 + MOVV sevp+8(FP), R5 + MOVV timerid+16(FP), R6 + MOVV $SYS_timer_create, R2 + SYSCALL + MOVW R2, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVW timerid+0(FP), R4 + MOVW flags+4(FP), R5 + MOVV new+8(FP), R6 + MOVV old+16(FP), R7 + MOVV $SYS_timer_settime, R2 + SYSCALL + MOVW R2, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVW timerid+0(FP), R4 + MOVV $SYS_timer_delete, R2 + SYSCALL + MOVW R2, ret+8(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOVV addr+0(FP), R4 MOVV n+8(FP), R5 @@ -229,8 +245,9 @@ TEXT runtime·walltime(SB),NOSPLIT,$16-12 MOVV R2, 8(R29) MOVV R3, 16(R29) + MOVV $ret-8(FP), R2 // caller's SP MOVV R31, m_vdsoPC(R17) - MOVV R29, m_vdsoSP(R17) + MOVV R2, m_vdsoSP(R17) MOVV m_curg(R17), R4 MOVV g, R5 @@ -298,8 +315,9 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVV R2, 8(R29) MOVV R3, 16(R29) + MOVV $ret-8(FP), R2 // caller's SP MOVV R31, m_vdsoPC(R17) - MOVV R29, m_vdsoSP(R17) + MOVV R2, m_vdsoSP(R17) MOVV m_curg(R17), R4 MOVV g, R5 @@ -387,7 +405,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 JAL (R25) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$64 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$64 // initialize REGSB = PC&0xffffffff00000000 BGEZAL R0, 1(PC) SRLV $32, R31, RSB @@ -606,21 +624,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SYSCALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R4 // fd - MOVV $3, R5 // F_GETFL - MOVV $0, R6 - MOVV $SYS_fcntl, R2 - SYSCALL - MOVW $0x80, R6 // O_NONBLOCK - OR R2, R6 - MOVW fd+0(FP), R4 // fd - MOVV $4, R5 // F_SETFL - MOVV $SYS_fcntl, R2 - SYSCALL - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8 // Implemented as brk(NULL). diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s index b3970be9cf8f74..e70edcc0e22fdc 100644 --- a/src/runtime/sys_linux_mipsx.s +++ b/src/runtime/sys_linux_mipsx.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle // // System calls and other sys.stuff for mips, Linux @@ -21,7 +19,6 @@ #define SYS_close 4006 #define SYS_getpid 4020 #define SYS_kill 4037 -#define SYS_pipe 4042 #define SYS_brk 4045 #define SYS_fcntl 4055 #define SYS_mmap 4090 @@ -43,6 +40,9 @@ #define SYS_epoll_create 4248 #define SYS_epoll_ctl 4249 #define SYS_epoll_wait 4250 +#define SYS_timer_create 4257 +#define SYS_timer_settime 4258 +#define SYS_timer_delete 4261 #define SYS_clock_gettime 4263 #define SYS_tgkill 4266 #define SYS_epoll_create1 4326 @@ -111,23 +111,6 @@ TEXT runtime·read(SB),NOSPLIT,$0-16 MOVW R2, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVW $SYS_pipe, R2 - SYSCALL - BEQ R7, pipeok - MOVW $-1, R1 - MOVW R1, r+0(FP) - MOVW R1, w+4(FP) - SUBU R2, R0, R2 // caller expects negative errno - MOVW R2, errno+8(FP) - RET -pipeok: - MOVW R2, r+0(FP) - MOVW R3, w+4(FP) - MOVW R0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVW $r+4(FP), R4 @@ -209,6 +192,32 @@ TEXT runtime·setitimer(SB),NOSPLIT,$0-12 SYSCALL RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-16 + MOVW clockid+0(FP), R4 + MOVW sevp+4(FP), R5 + MOVW timerid+8(FP), R6 + MOVW $SYS_timer_create, R2 + SYSCALL + MOVW R2, ret+12(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-20 + MOVW timerid+0(FP), R4 + MOVW flags+4(FP), R5 + MOVW new+8(FP), R6 + MOVW old+12(FP), R7 + MOVW $SYS_timer_settime, R2 + SYSCALL + MOVW R2, ret+16(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-8 + MOVW timerid+0(FP), R4 + MOVW $SYS_timer_delete, R2 + SYSCALL + MOVW R2, ret+4(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT,$0-16 MOVW addr+0(FP), R4 MOVW n+4(FP), R5 @@ -302,7 +311,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 MOVW R22, R29 RET -TEXT runtime·sigtramp(SB),NOSPLIT,$12 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$12 // this might be called in external code context, // where g is not set. MOVB runtime·iscgo(SB), R1 @@ -532,21 +541,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0-4 SYSCALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVW fd+0(FP), R4 // fd - MOVW $3, R5 // F_GETFL - MOVW $0, R6 - MOVW $SYS_fcntl, R2 - SYSCALL - MOVW $0x80, R6 // O_NONBLOCK - OR R2, R6 - MOVW fd+0(FP), R4 // fd - MOVW $4, R5 // F_SETFL - MOVW $SYS_fcntl, R2 - SYSCALL - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT,$0-4 // Implemented as brk(NULL). diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index 005fa4d2b4d002..2913a05f56f86e 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le // // System calls and other sys.stuff for ppc64, Linux @@ -22,7 +20,6 @@ #define SYS_close 6 #define SYS_getpid 20 #define SYS_kill 37 -#define SYS_pipe 42 #define SYS_brk 45 #define SYS_fcntl 55 #define SYS_mmap 90 @@ -44,6 +41,9 @@ #define SYS_epoll_create 236 #define SYS_epoll_ctl 237 #define SYS_epoll_wait 238 +#define SYS_timer_create 240 +#define SYS_timer_settime 241 +#define SYS_timer_delete 244 #define SYS_clock_gettime 246 #define SYS_tgkill 250 #define SYS_epoll_create1 315 @@ -103,13 +103,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW R3, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - ADD $FIXED_FRAME, R1, R3 - SYSCALL $SYS_pipe - MOVW R3, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 ADD $FIXED_FRAME+8, R1, R3 @@ -176,6 +169,29 @@ TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 SYSCALL $SYS_setitimer RET +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVW clockid+0(FP), R3 + MOVD sevp+8(FP), R4 + MOVD timerid+16(FP), R5 + SYSCALL $SYS_timer_create + MOVW R3, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVW timerid+0(FP), R3 + MOVW flags+4(FP), R4 + MOVD new+8(FP), R5 + MOVD old+16(FP), R6 + SYSCALL $SYS_timer_settime + MOVW R3, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVW timerid+0(FP), R3 + SYSCALL $SYS_timer_delete + MOVW R3, ret+8(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOVD addr+0(FP), R3 MOVD n+8(FP), R4 @@ -205,8 +221,9 @@ TEXT runtime·walltime(SB),NOSPLIT,$16-12 MOVD R5, 40(R1) MOVD LR, R14 + MOVD $ret-FIXED_FRAME(FP), R5 // caller's SP MOVD R14, m_vdsoPC(R21) - MOVD R15, m_vdsoSP(R21) + MOVD R5, m_vdsoSP(R21) MOVD m_curg(R21), R6 CMP g, R6 @@ -297,9 +314,10 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVD R4, 32(R1) MOVD R5, 40(R1) - MOVD LR, R14 // R14 is unchanged by C code + MOVD LR, R14 // R14 is unchanged by C code + MOVD $ret-FIXED_FRAME(FP), R5 // caller's SP MOVD R14, m_vdsoPC(R21) - MOVD R15, m_vdsoSP(R21) + MOVD R5, m_vdsoSP(R21) MOVD m_curg(R21), R6 CMP g, R6 @@ -441,7 +459,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT|NOFRAME,$0 DWORD $sigtramp<>(SB) DWORD $0 DWORD $0 -TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0 +TEXT sigtramp<>(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 #endif // Start with standard C stack frame layout and linkage. MOVD LR, R0 @@ -715,6 +733,9 @@ TEXT cgoSigtramp<>(SB),NOSPLIT,$0 TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$0 // We're coming from C code, set up essential register, then call sigprofNonGo. CALL runtime·reginit(SB) + MOVW R3, FIXED_FRAME+0(R1) // sig + MOVD R4, FIXED_FRAME+8(R1) // info + MOVD R5, FIXED_FRAME+16(R1) // ctx CALL runtime·sigprofNonGo(SB) RET @@ -904,18 +925,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SYSCALL $SYS_fcntl RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R3 // fd - MOVD $3, R4 // F_GETFL - MOVD $0, R5 - SYSCALL $SYS_fcntl - OR $0x800, R3, R5 // O_NONBLOCK - MOVW fd+0(FP), R3 // fd - MOVD $4, R4 // F_SETFL - SYSCALL $SYS_fcntl - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0 // Implemented as brk(NULL). diff --git a/src/runtime/sys_linux_riscv64.s b/src/runtime/sys_linux_riscv64.s index 2389f1cc182489..afb2d11da98fdc 100644 --- a/src/runtime/sys_linux_riscv64.s +++ b/src/runtime/sys_linux_riscv64.s @@ -10,6 +10,8 @@ #include "go_asm.h" #define AT_FDCWD -100 +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 #define SYS_brk 214 #define SYS_clock_gettime 113 @@ -25,7 +27,6 @@ #define SYS_fcntl 25 #define SYS_futex 98 #define SYS_getpid 172 -#define SYS_getrlimit 163 #define SYS_gettid 178 #define SYS_gettimeofday 169 #define SYS_kill 129 @@ -47,6 +48,9 @@ #define SYS_sigaltstack 132 #define SYS_socket 198 #define SYS_tgkill 131 +#define SYS_timer_create 107 +#define SYS_timer_delete 111 +#define SYS_timer_settime 110 #define SYS_tkill 130 #define SYS_write 64 @@ -114,15 +118,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW A0, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOV $r+0(FP), A0 - MOV ZERO, A1 - MOV $SYS_pipe2, A7 - ECALL - MOVW A0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOV $r+8(FP), A0 @@ -132,15 +127,6 @@ TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVW A0, errno+16(FP) RET -// func getrlimit(kind int32, limit unsafe.Pointer) int32 -TEXT runtime·getrlimit(SB),NOSPLIT|NOFRAME,$0-20 - MOVW kind+0(FP), A0 - MOV limit+8(FP), A1 - MOV $SYS_getrlimit, A7 - ECALL - MOVW A0, ret+16(FP) - RET - // func usleep(usec uint32) TEXT runtime·usleep(SB),NOSPLIT,$24-4 MOVWU usec+0(FP), A0 @@ -209,6 +195,35 @@ TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 ECALL RET +// func timer_create(clockid int32, sevp *sigevent, timerid *int32) int32 +TEXT runtime·timer_create(SB),NOSPLIT,$0-28 + MOVW clockid+0(FP), A0 + MOV sevp+8(FP), A1 + MOV timerid+16(FP), A2 + MOV $SYS_timer_create, A7 + ECALL + MOVW A0, ret+24(FP) + RET + +// func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 +TEXT runtime·timer_settime(SB),NOSPLIT,$0-28 + MOVW timerid+0(FP), A0 + MOVW flags+4(FP), A1 + MOV new+8(FP), A2 + MOV old+16(FP), A3 + MOV $SYS_timer_settime, A7 + ECALL + MOVW A0, ret+24(FP) + RET + +// func timer_delete(timerid int32) int32 +TEXT runtime·timer_delete(SB),NOSPLIT,$0-12 + MOVW timerid+0(FP), A0 + MOV $SYS_timer_delete, A7 + ECALL + MOVW A0, ret+8(FP) + RET + // func mincore(addr unsafe.Pointer, n uintptr, dst *byte) int32 TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOV addr+0(FP), A0 @@ -220,8 +235,68 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 RET // func walltime() (sec int64, nsec int32) -TEXT runtime·walltime(SB),NOSPLIT,$24-12 - MOV $0, A0 // CLOCK_REALTIME +TEXT runtime·walltime(SB),NOSPLIT,$40-12 + MOV $CLOCK_REALTIME, A0 + + MOV runtime·vdsoClockgettimeSym(SB), A7 + BEQZ A7, fallback + MOV X2, S2 // S2,S3,S4 is unchanged by C code + MOV g_m(g), S3 // S3 = m + + // Save the old values on stack for reentrant + MOV m_vdsoPC(S3), T0 + MOV T0, 24(X2) + MOV m_vdsoSP(S3), T0 + MOV T0, 32(X2) + + MOV RA, m_vdsoPC(S3) + MOV $ret-8(FP), T1 // caller's SP + MOV T1, m_vdsoSP(S3) + + MOV m_curg(S3), T1 + BNE g, T1, noswitch + + MOV m_g0(S3), T1 + MOV (g_sched+gobuf_sp)(T1), X2 + +noswitch: + ADDI $-24, X2 // Space for result + ANDI $~7, X2 // Align for C code + MOV $8(X2), A1 + + // Store g on gsignal's stack, see sys_linux_arm64.s for detail + MOVBU runtime·iscgo(SB), S4 + BNEZ S4, nosaveg + MOV m_gsignal(S3), S4 // g.m.gsignal + BEQZ S4, nosaveg + BEQ g, S4, nosaveg + MOV (g_stack+stack_lo)(S4), S4 // g.m.gsignal.stack.lo + MOV g, (S4) + + JALR RA, A7 + + MOV ZERO, (S4) + JMP finish + +nosaveg: + JALR RA, A7 + +finish: + MOV 8(X2), T0 // sec + MOV 16(X2), T1 // nsec + + MOV S2, X2 // restore stack + MOV 24(X2), A2 + MOV A2, m_vdsoPC(S3) + + MOV 32(X2), A3 + MOV A3, m_vdsoSP(S3) + + MOV T0, sec+0(FP) + MOVW T1, nsec+8(FP) + RET + +fallback: MOV $8(X2), A1 MOV $SYS_clock_gettime, A7 ECALL @@ -232,13 +307,62 @@ TEXT runtime·walltime(SB),NOSPLIT,$24-12 RET // func nanotime1() int64 -TEXT runtime·nanotime1(SB),NOSPLIT,$24-8 - MOV $1, A0 // CLOCK_MONOTONIC +TEXT runtime·nanotime1(SB),NOSPLIT,$40-8 + MOV $CLOCK_MONOTONIC, A0 + + MOV runtime·vdsoClockgettimeSym(SB), A7 + BEQZ A7, fallback + + MOV X2, S2 // S2 = RSP, S2 is unchanged by C code + MOV g_m(g), S3 // S3 = m + // Save the old values on stack for reentrant + MOV m_vdsoPC(S3), T0 + MOV T0, 24(X2) + MOV m_vdsoSP(S3), T0 + MOV T0, 32(X2) + + MOV RA, m_vdsoPC(S3) + MOV $ret-8(FP), T0 // caller's SP + MOV T0, m_vdsoSP(S3) + + MOV m_curg(S3), T1 + BNE g, T1, noswitch + + MOV m_g0(S3), T1 + MOV (g_sched+gobuf_sp)(T1), X2 + +noswitch: + ADDI $-24, X2 // Space for result + ANDI $~7, X2 // Align for C code MOV $8(X2), A1 - MOV $SYS_clock_gettime, A7 - ECALL + + // Store g on gsignal's stack, see sys_linux_arm64.s for detail + MOVBU runtime·iscgo(SB), S4 + BNEZ S4, nosaveg + MOV m_gsignal(S3), S4 // g.m.gsignal + BEQZ S4, nosaveg + BEQ g, S4, nosaveg + MOV (g_stack+stack_lo)(S4), S4 // g.m.gsignal.stack.lo + MOV g, (S4) + + JALR RA, A7 + + MOV ZERO, (S4) + JMP finish + +nosaveg: + JALR RA, A7 + +finish: MOV 8(X2), T0 // sec MOV 16(X2), T1 // nsec + // restore stack + MOV S2, X2 + MOV 24(X2), T2 + MOV T2, m_vdsoPC(S3) + + MOV 32(X2), T2 + MOV T2, m_vdsoSP(S3) // sec is in T0, nsec in T1 // return nsec in T0 MOV $1000000000, T2 @@ -247,6 +371,18 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$24-8 MOV T0, ret+0(FP) RET +fallback: + MOV $8(X2), A1 + MOV $SYS_clock_gettime, A7 + ECALL + MOV 8(X2), T0 // sec + MOV 16(X2), T1 // nsec + MOV $1000000000, T2 + MUL T2, T0 + ADD T1, T0 + MOV T0, ret+0(FP) + RET + // func rtsigprocmask(how int32, new, old *sigset, size int32) TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 MOVW how+0(FP), A0 @@ -281,7 +417,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // func sigtramp(signo, ureg, ctxt unsafe.Pointer) -TEXT runtime·sigtramp(SB),NOSPLIT,$64 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$64 MOVW A0, 8(X2) MOV A1, 16(X2) MOV A2, 24(X2) @@ -490,21 +626,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 ECALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), A0 // fd - MOV $3, A1 // F_GETFL - MOV $0, A2 - MOV $SYS_fcntl, A7 - ECALL - MOV $0x800, A2 // O_NONBLOCK - OR A0, A2 - MOVW fd+0(FP), A0 // fd - MOV $4, A1 // F_SETFL - MOV $SYS_fcntl, A7 - ECALL - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT,$0-8 // Implemented as brk(NULL). diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s index 916dfada8d58e6..c82cb9b4aab126 100644 --- a/src/runtime/sys_linux_s390x.s +++ b/src/runtime/sys_linux_s390x.s @@ -16,7 +16,6 @@ #define SYS_close 6 #define SYS_getpid 20 #define SYS_kill 37 -#define SYS_pipe 42 #define SYS_brk 45 #define SYS_fcntl 55 #define SYS_mmap 90 @@ -39,6 +38,9 @@ #define SYS_epoll_create 249 #define SYS_epoll_ctl 250 #define SYS_epoll_wait 251 +#define SYS_timer_create 254 +#define SYS_timer_settime 255 +#define SYS_timer_delete 258 #define SYS_clock_gettime 260 #define SYS_pipe2 325 #define SYS_epoll_create1 327 @@ -100,14 +102,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 MOVW R2, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVD $r+0(FP), R2 - MOVW $SYS_pipe, R1 - SYSCALL - MOVW R2, errno+8(FP) - RET - // func pipe2() (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVD $r+8(FP), R2 @@ -185,6 +179,32 @@ TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 SYSCALL RET +TEXT runtime·timer_create(SB),NOSPLIT|NOFRAME,$0-28 + MOVW clockid+0(FP), R2 + MOVD sevp+8(FP), R3 + MOVD timerid+16(FP), R4 + MOVW $SYS_timer_create, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +TEXT runtime·timer_settime(SB),NOSPLIT|NOFRAME,$0-28 + MOVW timerid+0(FP), R2 + MOVW flags+4(FP), R3 + MOVD new+8(FP), R4 + MOVD old+16(FP), R5 + MOVW $SYS_timer_settime, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +TEXT runtime·timer_delete(SB),NOSPLIT|NOFRAME,$0-12 + MOVW timerid+0(FP), R2 + MOVW $SYS_timer_delete, R1 + SYSCALL + MOVW R2, ret+8(FP) + RET + TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 MOVD addr+0(FP), R2 MOVD n+8(FP), R3 @@ -195,29 +215,177 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 RET // func walltime() (sec int64, nsec int32) -TEXT runtime·walltime(SB),NOSPLIT,$16 - MOVW $0, R2 // CLOCK_REALTIME +TEXT runtime·walltime(SB),NOSPLIT,$32-12 + MOVW $0, R2 // CLOCK_REALTIME + MOVD R15, R7 // Backup stack pointer + + MOVD g_m(g), R6 //m + + MOVD runtime·vdsoClockgettimeSym(SB), R9 // Check for VDSO availability + CMPBEQ R9, $0, fallback + + MOVD m_vdsoPC(R6), R4 + MOVD R4, 16(R15) + MOVD m_vdsoSP(R6), R4 + MOVD R4, 24(R15) + + MOVD R14, R8 // Backup return address + MOVD $sec+0(FP), R4 // return parameter caller + + MOVD R8, m_vdsoPC(R6) + MOVD R4, m_vdsoSP(R6) + + MOVD m_curg(R6), R5 + CMP g, R5 + BNE noswitch + + MOVD m_g0(R6), R4 + MOVD (g_sched+gobuf_sp)(R4), R15 // Set SP to g0 stack + +noswitch: + SUB $16, R15 // reserve 2x 8 bytes for parameters + MOVD $~7, R4 // align to 8 bytes because of gcc ABI + AND R4, R15 + MOVD R15, R3 // R15 needs to be in R3 as expected by kernel_clock_gettime + + MOVB runtime·iscgo(SB),R12 + CMPBNE R12, $0, nosaveg + + MOVD m_gsignal(R6), R12 // g.m.gsignal + CMPBEQ R12, $0, nosaveg + + CMPBEQ g, R12, nosaveg + MOVD (g_stack+stack_lo)(R12), R12 // g.m.gsignal.stack.lo + MOVD g, (R12) + + BL R9 // to vdso lookup + + MOVD $0, (R12) + + JMP finish + +nosaveg: + BL R9 // to vdso lookup + +finish: + MOVD 0(R15), R3 // sec + MOVD 8(R15), R5 // nsec + MOVD R7, R15 // Restore SP + + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVD 24(R15), R12 + MOVD R12, m_vdsoSP(R6) + MOVD 16(R15), R12 + MOVD R12, m_vdsoPC(R6) + +return: + // sec is in R3, nsec in R5 + // return nsec in R3 + MOVD R3, sec+0(FP) + MOVW R5, nsec+8(FP) + RET + + // Syscall fallback +fallback: MOVD $tp-16(SP), R3 MOVW $SYS_clock_gettime, R1 SYSCALL - LMG tp-16(SP), R2, R3 + LMG tp-16(SP), R2, R3 // sec is in R2, nsec in R3 MOVD R2, sec+0(FP) MOVW R3, nsec+8(FP) RET -TEXT runtime·nanotime1(SB),NOSPLIT,$16 - MOVW $1, R2 // CLOCK_MONOTONIC +TEXT runtime·nanotime1(SB),NOSPLIT,$32-8 + MOVW $1, R2 // CLOCK_MONOTONIC + + MOVD R15, R7 // Backup stack pointer + + MOVD g_m(g), R6 //m + + MOVD runtime·vdsoClockgettimeSym(SB), R9 // Check for VDSO availability + CMPBEQ R9, $0, fallback + + MOVD m_vdsoPC(R6), R4 + MOVD R4, 16(R15) + MOVD m_vdsoSP(R6), R4 + MOVD R4, 24(R15) + + MOVD R14, R8 // Backup return address + MOVD $ret+0(FP), R4 // caller's SP + + MOVD R8, m_vdsoPC(R6) + MOVD R4, m_vdsoSP(R6) + + MOVD m_curg(R6), R5 + CMP g, R5 + BNE noswitch + + MOVD m_g0(R6), R4 + MOVD (g_sched+gobuf_sp)(R4), R15 // Set SP to g0 stack + +noswitch: + SUB $16, R15 // reserve 2x 8 bytes for parameters + MOVD $~7, R4 // align to 8 bytes because of gcc ABI + AND R4, R15 + MOVD R15, R3 // R15 needs to be in R3 as expected by kernel_clock_gettime + + MOVB runtime·iscgo(SB),R12 + CMPBNE R12, $0, nosaveg + + MOVD m_gsignal(R6), R12 // g.m.gsignal + CMPBEQ R12, $0, nosaveg + + CMPBEQ g, R12, nosaveg + MOVD (g_stack+stack_lo)(R12), R12 // g.m.gsignal.stack.lo + MOVD g, (R12) + + BL R9 // to vdso lookup + + MOVD $0, (R12) + + JMP finish + +nosaveg: + BL R9 // to vdso lookup + +finish: + MOVD 0(R15), R3 // sec + MOVD 8(R15), R5 // nsec + MOVD R7, R15 // Restore SP + + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + + MOVD 24(R15), R12 + MOVD R12, m_vdsoSP(R6) + MOVD 16(R15), R12 + MOVD R12, m_vdsoPC(R6) + +return: + // sec is in R3, nsec in R5 + // return nsec in R3 + MULLD $1000000000, R3 + ADD R5, R3 + MOVD R3, ret+0(FP) + RET + + // Syscall fallback +fallback: MOVD $tp-16(SP), R3 - MOVW $SYS_clock_gettime, R1 + MOVD $SYS_clock_gettime, R1 SYSCALL - LMG tp-16(SP), R2, R3 - // sec is in R2, nsec in R3 - // return nsec in R2 - MULLD $1000000000, R2 - ADD R3, R2 - MOVD R2, ret+0(FP) - RET + LMG tp-16(SP), R2, R3 + MOVD R3, R5 + MOVD R2, R3 + JMP return TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 MOVW how+0(FP), R2 @@ -252,7 +420,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 TEXT runtime·sigreturn(SB),NOSPLIT,$0-0 RET -TEXT runtime·sigtramp(SB),NOSPLIT,$64 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$64 // initialize essential registers (just in case) XOR R0, R0 @@ -468,21 +636,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 SYSCALL RET -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R2 // fd - MOVD $3, R3 // F_GETFL - XOR R4, R4 - MOVW $SYS_fcntl, R1 - SYSCALL - MOVD $0x800, R4 // O_NONBLOCK - OR R2, R4 - MOVW fd+0(FP), R2 // fd - MOVD $4, R3 // F_SETFL - MOVW $SYS_fcntl, R1 - SYSCALL - RET - // func sbrk0() uintptr TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8 // Implemented as brk(NULL). diff --git a/src/runtime/sys_loong64.go b/src/runtime/sys_loong64.go new file mode 100644 index 00000000000000..812db5cf0dfb19 --- /dev/null +++ b/src/runtime/sys_loong64.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package runtime + +import "unsafe" + +// adjust Gobuf as if it executed a call to fn with context ctxt +// and then did an immediate Gosave. +func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { + if buf.lr != 0 { + throw("invalid use of gostartcall") + } + buf.lr = buf.pc + buf.pc = uintptr(fn) + buf.ctxt = ctxt +} diff --git a/src/runtime/sys_mips64x.go b/src/runtime/sys_mips64x.go index 842a4a7084ffe6..b71538409c6fcf 100644 --- a/src/runtime/sys_mips64x.go +++ b/src/runtime/sys_mips64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le package runtime diff --git a/src/runtime/sys_mipsx.go b/src/runtime/sys_mipsx.go index 2038eb7d794f9a..b60135f9649d44 100644 --- a/src/runtime/sys_mipsx.go +++ b/src/runtime/sys_mipsx.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle package runtime diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s index 8a33894892f1f1..581b4fc9b68933 100644 --- a/src/runtime/sys_netbsd_386.s +++ b/src/runtime/sys_netbsd_386.s @@ -87,21 +87,6 @@ TEXT runtime·read(SB),NOSPLIT,$-4 MOVL AX, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVL $42, AX - INT $0x80 - JCC pipeok - MOVL $-1, r+0(FP) - MOVL $-1, w+4(FP) - MOVL AX, errno+8(FP) - RET -pipeok: - MOVL AX, r+0(FP) - MOVL DX, w+4(FP) - MOVL $0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$12-16 MOVL $453, AX @@ -305,7 +290,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$12-16 RET // Called by OS using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$28 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$28 NOP SP // tell vet SP changed - stop checking offsets // Save callee-saved C registers, since the caller may be a C signal handler. MOVL BX, bx-4(SP) @@ -484,20 +469,3 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32 JAE 2(PC) NEGL AX RET - -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$16-4 - MOVL $92, AX // fcntl - MOVL fd+0(FP), BX // fd - MOVL BX, 4(SP) - MOVL $3, 8(SP) // F_GETFL - MOVL $0, 12(SP) - INT $0x80 - MOVL fd+0(FP), BX // fd - MOVL BX, 4(SP) - MOVL $4, 8(SP) // F_SETFL - ORL $4, AX // O_NONBLOCK - MOVL AX, 12(SP) - MOVL $92, AX // fcntl - INT $0x80 - RET diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s index 02f5b4ba3b71c3..ab11f6ff6690b0 100644 --- a/src/runtime/sys_netbsd_amd64.s +++ b/src/runtime/sys_netbsd_amd64.s @@ -163,21 +163,6 @@ TEXT runtime·read(SB),NOSPLIT,$-8 MOVL AX, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - MOVL $42, AX - SYSCALL - JCC pipeok - MOVL $-1, r+0(FP) - MOVL $-1, w+4(FP) - MOVL AX, errno+8(FP) - RET -pipeok: - MOVL AX, r+0(FP) - MOVL DX, w+4(FP) - MOVL $0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-20 LEAQ r+8(FP), DI @@ -320,17 +305,25 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() - // Call into the Go signal handler + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. NOP SP // disable vet stack checking - ADJSP $24 - MOVQ DI, 0(SP) // sig - MOVQ SI, 8(SP) // info - MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + ADJSP $-24 POP_REGS_HOST_TO_ABI0() @@ -449,18 +442,3 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 MOVL $SYS_fcntl, AX SYSCALL RET - -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVL fd+0(FP), DI // fd - MOVQ $3, SI // F_GETFL - MOVQ $0, DX - MOVL $92, AX // fcntl - SYSCALL - MOVL fd+0(FP), DI // fd - MOVQ $4, SI // F_SETFL - MOVQ $4, DX // O_NONBLOCK - ORL AX, DX - MOVL $92, AX // fcntl - SYSCALL - RET diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s index 3a763b2a6a7730..dbe3dbcffc0eeb 100644 --- a/src/runtime/sys_netbsd_arm.s +++ b/src/runtime/sys_netbsd_arm.s @@ -96,22 +96,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0 MOVW R0, ret+12(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT,$0-12 - SWI $0xa0002a - BCC pipeok - MOVW $-1,R2 - MOVW R2, r+0(FP) - MOVW R2, w+4(FP) - MOVW R0, errno+8(FP) - RET -pipeok: - MOVW $0, R2 - MOVW R0, r+0(FP) - MOVW R1, w+4(FP) - MOVW R2, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVW $r+4(FP), R0 @@ -304,7 +288,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 MOVW R4, R13 RET -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Reserve space for callee-save registers and arguments. MOVM.DB.W [R4-R11], (R13) SUB $16, R13 @@ -422,18 +406,6 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 SWI $SYS_fcntl RET -// func runtime·setNonblock(fd int32) -TEXT runtime·setNonblock(SB),NOSPLIT,$0-4 - MOVW fd+0(FP), R0 // fd - MOVW $3, R1 // F_GETFL - MOVW $0, R2 - SWI $0xa0005c // sys_fcntl - ORR $0x4, R0, R2 // O_NONBLOCK - MOVW fd+0(FP), R0 // fd - MOVW $4, R1 // F_SETFL - SWI $0xa0005c // sys_fcntl - RET - // TODO: this is only valid for ARMv7+ TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0 B runtime·armPublicationBarrier(SB) diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s index 2d0b894d47f6f2..fc126cad7d2246 100644 --- a/src/runtime/sys_netbsd_arm64.s +++ b/src/runtime/sys_netbsd_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 3 @@ -154,17 +155,6 @@ ok: MOVW R0, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - ADD $8, RSP, R0 - MOVW $0, R1 - SVC $SYS_pipe2 - BCC pipeok - NEG R0, R0 -pipeok: - MOVW R0, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 ADD $16, RSP, R0 @@ -279,8 +269,8 @@ fail: TEXT sigreturn_tramp<>(SB),NOSPLIT,$-8 MOVD g, R0 SVC $SYS_setcontext - MOVD $0x4242, R0 // Something failed, return magic number - SVC $SYS_exit + MOVD $0, R0 + MOVD R0, (R0) // crash TEXT runtime·sigaction(SB),NOSPLIT,$-8 MOVW sig+0(FP), R0 // arg 1 - signum @@ -305,34 +295,17 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // Unclobber g for now (kernel uses it as ucontext ptr) // See https://github.com/golang/go/issues/30824#issuecomment-492772426 // This is only correct in the non-cgo case. // XXX should use lwp_getprivate as suggested. // 8*36 is ucontext.uc_mcontext.__gregs[_REG_X28] MOVD 8*36(g), g - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) // this might be called in external code context, // where g is not set. @@ -344,30 +317,19 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BEQ 2(PC) BL runtime·load_g(SB) +#ifdef GOEXPERIMENT_regabiargs + // Restore signum to R0. + MOVW 8(RSP), R0 + // R1 and R2 already contain info and ctx, respectively. +#else MOVD R1, 16(RSP) MOVD R2, 24(RSP) - BL runtime·sigtrampgo(SB) +#endif + BL runtime·sigtrampgo(SB) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET @@ -466,16 +428,3 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 MOVW $FD_CLOEXEC, R2 SVC $SYS_fcntl RET - -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R0 // arg 1 - fd - MOVD $F_GETFL, R1 // arg 2 - cmd - MOVD $0, R2 // arg 3 - SVC $SYS_fcntl - MOVD $O_NONBLOCK, R2 - EOR R0, R2 // arg 3 - flags - MOVW fd+0(FP), R0 // arg 1 - fd - MOVD $F_SETFL, R1 // arg 2 - cmd - SVC $SYS_fcntl - RET diff --git a/src/runtime/sys_nonppc64x.go b/src/runtime/sys_nonppc64x.go index 66821b1f76b06d..653f1c999f0ad9 100644 --- a/src/runtime/sys_nonppc64x.go +++ b/src/runtime/sys_nonppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !ppc64 && !ppc64le -// +build !ppc64,!ppc64le package runtime diff --git a/src/runtime/sys_openbsd.go b/src/runtime/sys_openbsd.go index ab3149558b3087..c4b8489612a5db 100644 --- a/src/runtime/sys_openbsd.go +++ b/src/runtime/sys_openbsd.go @@ -3,11 +3,13 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) // The *_trampoline functions convert from the Go calling convention to the C calling convention // and then call the underlying libc function. These are defined in sys_openbsd_$ARCH.s. @@ -15,35 +17,47 @@ import "unsafe" //go:nosplit //go:cgo_unsafe_args func pthread_attr_init(attr *pthreadattr) int32 { - return libcCall(unsafe.Pointer(funcPC(pthread_attr_init_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_init_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + return ret } func pthread_attr_init_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_attr_destroy(attr *pthreadattr) int32 { - return libcCall(unsafe.Pointer(funcPC(pthread_attr_destroy_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_destroy_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + return ret } func pthread_attr_destroy_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_attr_getstacksize(attr *pthreadattr, size *uintptr) int32 { - return libcCall(unsafe.Pointer(funcPC(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_getstacksize_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + KeepAlive(size) + return ret } func pthread_attr_getstacksize_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 { - return libcCall(unsafe.Pointer(funcPC(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + return ret } func pthread_attr_setdetachstate_trampoline() //go:nosplit //go:cgo_unsafe_args func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 { - return libcCall(unsafe.Pointer(funcPC(pthread_create_trampoline)), unsafe.Pointer(&attr)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(pthread_create_trampoline)), unsafe.Pointer(&attr)) + KeepAlive(attr) + KeepAlive(arg) // Just for consistency. Arg of course needs to be kept alive for the start function. + return ret } func pthread_create_trampoline() diff --git a/src/runtime/sys_openbsd1.go b/src/runtime/sys_openbsd1.go index cb5d35879cdef6..d852e3c58ae0d5 100644 --- a/src/runtime/sys_openbsd1.go +++ b/src/runtime/sys_openbsd1.go @@ -3,35 +3,40 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) //go:nosplit //go:cgo_unsafe_args func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32 { - return libcCall(unsafe.Pointer(funcPC(thrsleep_trampoline)), unsafe.Pointer(&ident)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(thrsleep_trampoline)), unsafe.Pointer(&ident)) + KeepAlive(tsp) + KeepAlive(abort) + return ret } func thrsleep_trampoline() //go:nosplit //go:cgo_unsafe_args func thrwakeup(ident uintptr, n int32) int32 { - return libcCall(unsafe.Pointer(funcPC(thrwakeup_trampoline)), unsafe.Pointer(&ident)) + return libcCall(unsafe.Pointer(abi.FuncPCABI0(thrwakeup_trampoline)), unsafe.Pointer(&ident)) } func thrwakeup_trampoline() //go:nosplit func osyield() { - libcCall(unsafe.Pointer(funcPC(sched_yield_trampoline)), unsafe.Pointer(nil)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(sched_yield_trampoline)), unsafe.Pointer(nil)) } func sched_yield_trampoline() //go:nosplit func osyield_no_g() { - asmcgocall_no_g(unsafe.Pointer(funcPC(sched_yield_trampoline)), unsafe.Pointer(nil)) + asmcgocall_no_g(unsafe.Pointer(abi.FuncPCABI0(sched_yield_trampoline)), unsafe.Pointer(nil)) } //go:cgo_import_dynamic libc_thrsleep __thrsleep "libc.so" diff --git a/src/runtime/sys_openbsd2.go b/src/runtime/sys_openbsd2.go index cd1a4e879fb2df..f936e0cfc3bb77 100644 --- a/src/runtime/sys_openbsd2.go +++ b/src/runtime/sys_openbsd2.go @@ -3,25 +3,28 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) // This is exported via linkname to assembly in runtime/cgo. +// //go:linkname exit //go:nosplit //go:cgo_unsafe_args func exit(code int32) { - libcCall(unsafe.Pointer(funcPC(exit_trampoline)), unsafe.Pointer(&code)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(exit_trampoline)), unsafe.Pointer(&code)) } func exit_trampoline() //go:nosplit //go:cgo_unsafe_args func getthrid() (tid int32) { - libcCall(unsafe.Pointer(funcPC(getthrid_trampoline)), unsafe.Pointer(&tid)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(getthrid_trampoline)), unsafe.Pointer(&tid)) return } func getthrid_trampoline() @@ -29,21 +32,22 @@ func getthrid_trampoline() //go:nosplit //go:cgo_unsafe_args func raiseproc(sig uint32) { - libcCall(unsafe.Pointer(funcPC(raiseproc_trampoline)), unsafe.Pointer(&sig)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(raiseproc_trampoline)), unsafe.Pointer(&sig)) } func raiseproc_trampoline() //go:nosplit //go:cgo_unsafe_args func thrkill(tid int32, sig int) { - libcCall(unsafe.Pointer(funcPC(thrkill_trampoline)), unsafe.Pointer(&tid)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(thrkill_trampoline)), unsafe.Pointer(&tid)) } func thrkill_trampoline() // mmap is used to do low-level memory allocation via mmap. Don't allow stack // splits, since this function (used by sysAlloc) is called in a lot of low-level // parts of the runtime and callers often assume it won't acquire any locks. -// go:nosplit +// +//go:nosplit func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) { args := struct { addr unsafe.Pointer @@ -53,7 +57,8 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (un ret1 unsafe.Pointer ret2 int }{addr, n, prot, flags, fd, off, nil, 0} - libcCall(unsafe.Pointer(funcPC(mmap_trampoline)), unsafe.Pointer(&args)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(mmap_trampoline)), unsafe.Pointer(&args)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. return args.ret1, args.ret2 } func mmap_trampoline() @@ -61,56 +66,60 @@ func mmap_trampoline() //go:nosplit //go:cgo_unsafe_args func munmap(addr unsafe.Pointer, n uintptr) { - libcCall(unsafe.Pointer(funcPC(munmap_trampoline)), unsafe.Pointer(&addr)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(munmap_trampoline)), unsafe.Pointer(&addr)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. } func munmap_trampoline() //go:nosplit //go:cgo_unsafe_args func madvise(addr unsafe.Pointer, n uintptr, flags int32) { - libcCall(unsafe.Pointer(funcPC(madvise_trampoline)), unsafe.Pointer(&addr)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(madvise_trampoline)), unsafe.Pointer(&addr)) + KeepAlive(addr) // Just for consistency. Hopefully addr is not a Go address. } func madvise_trampoline() //go:nosplit //go:cgo_unsafe_args func open(name *byte, mode, perm int32) (ret int32) { - return libcCall(unsafe.Pointer(funcPC(open_trampoline)), unsafe.Pointer(&name)) + ret = libcCall(unsafe.Pointer(abi.FuncPCABI0(open_trampoline)), unsafe.Pointer(&name)) + KeepAlive(name) + return } func open_trampoline() //go:nosplit //go:cgo_unsafe_args func closefd(fd int32) int32 { - return libcCall(unsafe.Pointer(funcPC(close_trampoline)), unsafe.Pointer(&fd)) + return libcCall(unsafe.Pointer(abi.FuncPCABI0(close_trampoline)), unsafe.Pointer(&fd)) } func close_trampoline() //go:nosplit //go:cgo_unsafe_args func read(fd int32, p unsafe.Pointer, n int32) int32 { - return libcCall(unsafe.Pointer(funcPC(read_trampoline)), unsafe.Pointer(&fd)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(read_trampoline)), unsafe.Pointer(&fd)) + KeepAlive(p) + return ret } func read_trampoline() //go:nosplit //go:cgo_unsafe_args func write1(fd uintptr, p unsafe.Pointer, n int32) int32 { - return libcCall(unsafe.Pointer(funcPC(write_trampoline)), unsafe.Pointer(&fd)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(write_trampoline)), unsafe.Pointer(&fd)) + KeepAlive(p) + return ret } func write_trampoline() -func pipe() (r, w int32, errno int32) { - return pipe2(0) -} - func pipe2(flags int32) (r, w int32, errno int32) { var p [2]int32 args := struct { p unsafe.Pointer flags int32 }{noescape(unsafe.Pointer(&p)), flags} - errno = libcCall(unsafe.Pointer(funcPC(pipe2_trampoline)), unsafe.Pointer(&args)) + errno = libcCall(unsafe.Pointer(abi.FuncPCABI0(pipe2_trampoline)), unsafe.Pointer(&args)) return p[0], p[1], errno } func pipe2_trampoline() @@ -118,34 +127,41 @@ func pipe2_trampoline() //go:nosplit //go:cgo_unsafe_args func setitimer(mode int32, new, old *itimerval) { - libcCall(unsafe.Pointer(funcPC(setitimer_trampoline)), unsafe.Pointer(&mode)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(setitimer_trampoline)), unsafe.Pointer(&mode)) + KeepAlive(new) + KeepAlive(old) } func setitimer_trampoline() //go:nosplit //go:cgo_unsafe_args func usleep(usec uint32) { - libcCall(unsafe.Pointer(funcPC(usleep_trampoline)), unsafe.Pointer(&usec)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(usleep_trampoline)), unsafe.Pointer(&usec)) } func usleep_trampoline() //go:nosplit //go:cgo_unsafe_args func usleep_no_g(usec uint32) { - asmcgocall_no_g(unsafe.Pointer(funcPC(usleep_trampoline)), unsafe.Pointer(&usec)) + asmcgocall_no_g(unsafe.Pointer(abi.FuncPCABI0(usleep_trampoline)), unsafe.Pointer(&usec)) } //go:nosplit //go:cgo_unsafe_args func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 { - return libcCall(unsafe.Pointer(funcPC(sysctl_trampoline)), unsafe.Pointer(&mib)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(sysctl_trampoline)), unsafe.Pointer(&mib)) + KeepAlive(mib) + KeepAlive(out) + KeepAlive(size) + KeepAlive(dst) + return ret } func sysctl_trampoline() //go:nosplit //go:cgo_unsafe_args func fcntl(fd, cmd, arg int32) int32 { - return libcCall(unsafe.Pointer(funcPC(fcntl_trampoline)), unsafe.Pointer(&fd)) + return libcCall(unsafe.Pointer(abi.FuncPCABI0(fcntl_trampoline)), unsafe.Pointer(&fd)) } func fcntl_trampoline() @@ -156,7 +172,13 @@ func nanotime1() int64 { clock_id int32 tp unsafe.Pointer }{_CLOCK_MONOTONIC, unsafe.Pointer(&ts)} - libcCall(unsafe.Pointer(funcPC(clock_gettime_trampoline)), unsafe.Pointer(&args)) + if errno := libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args)); errno < 0 { + // Avoid growing the nosplit stack. + systemstack(func() { + println("runtime: errno", -errno) + throw("clock_gettime failed") + }) + } return ts.tv_sec*1e9 + int64(ts.tv_nsec) } func clock_gettime_trampoline() @@ -168,42 +190,60 @@ func walltime() (int64, int32) { clock_id int32 tp unsafe.Pointer }{_CLOCK_REALTIME, unsafe.Pointer(&ts)} - libcCall(unsafe.Pointer(funcPC(clock_gettime_trampoline)), unsafe.Pointer(&args)) + if errno := libcCall(unsafe.Pointer(abi.FuncPCABI0(clock_gettime_trampoline)), unsafe.Pointer(&args)); errno < 0 { + // Avoid growing the nosplit stack. + systemstack(func() { + println("runtime: errno", -errno) + throw("clock_gettime failed") + }) + } return ts.tv_sec, int32(ts.tv_nsec) } //go:nosplit //go:cgo_unsafe_args func kqueue() int32 { - return libcCall(unsafe.Pointer(funcPC(kqueue_trampoline)), nil) + return libcCall(unsafe.Pointer(abi.FuncPCABI0(kqueue_trampoline)), nil) } func kqueue_trampoline() //go:nosplit //go:cgo_unsafe_args func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32 { - return libcCall(unsafe.Pointer(funcPC(kevent_trampoline)), unsafe.Pointer(&kq)) + ret := libcCall(unsafe.Pointer(abi.FuncPCABI0(kevent_trampoline)), unsafe.Pointer(&kq)) + KeepAlive(ch) + KeepAlive(ev) + KeepAlive(ts) + return ret } func kevent_trampoline() //go:nosplit //go:cgo_unsafe_args func sigaction(sig uint32, new *sigactiont, old *sigactiont) { - libcCall(unsafe.Pointer(funcPC(sigaction_trampoline)), unsafe.Pointer(&sig)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaction_trampoline)), unsafe.Pointer(&sig)) + KeepAlive(new) + KeepAlive(old) } func sigaction_trampoline() //go:nosplit //go:cgo_unsafe_args func sigprocmask(how uint32, new *sigset, old *sigset) { - libcCall(unsafe.Pointer(funcPC(sigprocmask_trampoline)), unsafe.Pointer(&how)) + // sigprocmask is called from sigsave, which is called from needm. + // As such, we have to be able to run with no g here. + asmcgocall_no_g(unsafe.Pointer(abi.FuncPCABI0(sigprocmask_trampoline)), unsafe.Pointer(&how)) + KeepAlive(new) + KeepAlive(old) } func sigprocmask_trampoline() //go:nosplit //go:cgo_unsafe_args func sigaltstack(new *stackt, old *stackt) { - libcCall(unsafe.Pointer(funcPC(sigaltstack_trampoline)), unsafe.Pointer(&new)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(sigaltstack_trampoline)), unsafe.Pointer(&new)) + KeepAlive(new) + KeepAlive(old) } func sigaltstack_trampoline() @@ -216,12 +256,6 @@ func closeonexec(fd int32) { fcntl(fd, _F_SETFD, _FD_CLOEXEC) } -//go:nosplit -func setNonblock(fd int32) { - flags := fcntl(fd, _F_GETFL, 0) - fcntl(fd, _F_SETFL, flags|_O_NONBLOCK) -} - // Tell the linker that the libc_* functions are to be found // in a system library, with the libc_ prefix missing. diff --git a/src/runtime/sys_openbsd3.go b/src/runtime/sys_openbsd3.go index 8d77a4b216e931..269bf86f10de43 100644 --- a/src/runtime/sys_openbsd3.go +++ b/src/runtime/sys_openbsd3.go @@ -3,11 +3,13 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) // The X versions of syscall expect the libc call to return a 64-bit result. // Otherwise (the non-X version) expects a 32-bit result. @@ -20,7 +22,7 @@ import "unsafe" //go:cgo_unsafe_args func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscall)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -31,7 +33,7 @@ func syscall() //go:cgo_unsafe_args func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscallX)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscallX)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -42,7 +44,7 @@ func syscallX() //go:cgo_unsafe_args func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscall6)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -53,7 +55,7 @@ func syscall6() //go:cgo_unsafe_args func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscall6X)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6X)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -64,7 +66,7 @@ func syscall6X() //go:cgo_unsafe_args func syscall_syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscall10)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall10)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -75,7 +77,7 @@ func syscall10() //go:cgo_unsafe_args func syscall_syscall10X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2, err uintptr) { entersyscall() - libcCall(unsafe.Pointer(funcPC(syscall10X)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall10X)), unsafe.Pointer(&fn)) exitsyscall() return } @@ -85,7 +87,7 @@ func syscall10X() //go:nosplit //go:cgo_unsafe_args func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(funcPC(syscall)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall)), unsafe.Pointer(&fn)) return } @@ -93,7 +95,7 @@ func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { //go:nosplit //go:cgo_unsafe_args func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(funcPC(syscall6)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6)), unsafe.Pointer(&fn)) return } @@ -101,7 +103,7 @@ func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintpt //go:nosplit //go:cgo_unsafe_args func syscall_rawSyscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(funcPC(syscall6X)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall6X)), unsafe.Pointer(&fn)) return } @@ -109,6 +111,6 @@ func syscall_rawSyscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintp //go:nosplit //go:cgo_unsafe_args func syscall_rawSyscall10X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2, err uintptr) { - libcCall(unsafe.Pointer(funcPC(syscall10X)), unsafe.Pointer(&fn)) + libcCall(unsafe.Pointer(abi.FuncPCABI0(syscall10X)), unsafe.Pointer(&fn)) return } diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s index 7830b61b7d0eff..963678a2c3c8a8 100644 --- a/src/runtime/sys_openbsd_386.s +++ b/src/runtime/sys_openbsd_386.s @@ -69,7 +69,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 RET // Called by OS using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$28 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$28 NOP SP // tell vet SP changed - stop checking offsets // Save callee-saved C registers, since the caller may be a C signal handler. MOVL BX, bx-4(SP) @@ -520,8 +520,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0 MOVL BX, 4(SP) // arg 2 - clock_id CALL libc_clock_gettime(SB) CMPL AX, $-1 - JNE 2(PC) - MOVL $0xf1, 0xf1 // crash on failure + JNE noerr + CALL libc_errno(SB) + MOVL (AX), AX + NEGL AX // caller expects negative errno +noerr: MOVL BP, SP POPL BP RET diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s index 522e98cf4f5ab0..2c026c8d0dba63 100644 --- a/src/runtime/sys_openbsd_amd64.s +++ b/src/runtime/sys_openbsd_amd64.s @@ -58,20 +58,28 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // Called using C ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Transition from C ABI to Go ABI. PUSH_REGS_HOST_TO_ABI0() - // Call into the Go signal handler + // Set up ABIInternal environment: g in R14, cleared X15. + get_tls(R12) + MOVQ g(R12), R14 + PXOR X15, X15 + + // Reserve space for spill slots. NOP SP // disable vet stack checking - ADJSP $24 - MOVQ DI, 0(SP) // sig - MOVQ SI, 8(SP) // info - MOVQ DX, 16(SP) // ctx - CALL ·sigtrampgo(SB) + ADJSP $24 + + // Call into the Go signal handler + MOVQ DI, AX // sig + MOVQ SI, BX // info + MOVQ DX, CX // ctx + CALL ·sigtrampgo(SB) + ADJSP $-24 - POP_REGS_HOST_TO_ABI0() + POP_REGS_HOST_TO_ABI0() RET // @@ -369,8 +377,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0 MOVL 0(DI), DI // arg 1 clock_id CALL libc_clock_gettime(SB) TESTL AX, AX - JEQ 2(PC) - MOVL $0xf1, 0xf1 // crash + JEQ noerr + CALL libc_errno(SB) + MOVL (AX), AX // errno + NEGL AX // caller expects negative errno value +noerr: POPQ BP RET diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s index 143fcf05184221..e03cfb52f60fdf 100644 --- a/src/runtime/sys_openbsd_arm.s +++ b/src/runtime/sys_openbsd_arm.s @@ -61,7 +61,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16 MOVW R9, R13 RET -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Reserve space for callee-save registers and arguments. MOVM.DB.W [R4-R11], (R13) SUB $16, R13 @@ -407,9 +407,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0 MOVW 0(R0), R0 // arg 1 clock_id BL libc_clock_gettime(SB) CMP $-1, R0 - BNE 3(PC) - MOVW $0, R8 // crash on failure - MOVW R8, (R8) + BNE noerr + BL libc_errno(SB) + MOVW (R0), R0 // errno + RSB.CS $0, R0 // caller expects negative errno +noerr: MOVW R9, R13 RET diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s index 9b4acc90a58c88..db92326efbaec5 100644 --- a/src/runtime/sys_openbsd_arm64.s +++ b/src/runtime/sys_openbsd_arm64.s @@ -11,6 +11,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME $0 #define CLOCK_MONOTONIC $3 @@ -18,30 +19,13 @@ // mstart_stub is the first function executed on a new thread started by pthread_create. // It just does some low-level setup and then calls mstart. // Note: called with the C calling convention. -TEXT runtime·mstart_stub(SB),NOSPLIT,$160 +TEXT runtime·mstart_stub(SB),NOSPLIT,$144 // R0 points to the m. // We are already on m's g0 stack. // Save callee-save registers. - MOVD R19, 8(RSP) - MOVD R20, 16(RSP) - MOVD R21, 24(RSP) - MOVD R22, 32(RSP) - MOVD R23, 40(RSP) - MOVD R24, 48(RSP) - MOVD R25, 56(RSP) - MOVD R26, 64(RSP) - MOVD R27, 72(RSP) - MOVD g, 80(RSP) - MOVD R29, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD m_g0(R0), g BL runtime·save_g(SB) @@ -49,25 +33,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 BL runtime·mstart(SB) // Restore callee-save registers. - MOVD 8(RSP), R19 - MOVD 16(RSP), R20 - MOVD 24(RSP), R21 - MOVD 32(RSP), R22 - MOVD 40(RSP), R23 - MOVD 48(RSP), R24 - MOVD 56(RSP), R25 - MOVD 64(RSP), R26 - MOVD 72(RSP), R27 - MOVD 80(RSP), g - MOVD 88(RSP), R29 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) // Go is all done with this OS thread. // Tell pthread everything is ok (we never join with this thread, so @@ -84,58 +51,30 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) // Alignment for ELF ABI? RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$192 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // If called from an external code context, g will not be set. // Save R0, since runtime·load_g will clobber it. MOVW R0, 8(RSP) // signum BL runtime·load_g(SB) +#ifdef GOEXPERIMENT_regabiargs + // Restore signum to R0. + MOVW 8(RSP), R0 + // R1 and R2 already contain info and ctx, respectively. +#else MOVD R1, 16(RSP) MOVD R2, 24(RSP) - BL runtime·sigtrampgo(SB) +#endif + BL runtime·sigtrampgo(SB) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET @@ -359,9 +298,11 @@ TEXT runtime·clock_gettime_trampoline(SB),NOSPLIT,$0 MOVD 0(R0), R0 // arg 1 - clock_id CALL libc_clock_gettime(SB) CMP $-1, R0 - BNE 3(PC) - MOVD $0, R0 // crash on failure - MOVD R0, (R0) + BNE noerr + CALL libc_errno(SB) + MOVW (R0), R0 // errno + NEG R0, R0 // caller expects negative errno value +noerr: RET TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0 diff --git a/src/runtime/sys_openbsd_mips64.s b/src/runtime/sys_openbsd_mips64.s index f8ae8e7c3014b7..c2b209205315f2 100644 --- a/src/runtime/sys_openbsd_mips64.s +++ b/src/runtime/sys_openbsd_mips64.s @@ -64,17 +64,6 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0 MOVW R2, ret+24(FP) RET -// func pipe() (r, w int32, errno int32) -TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12 - MOVV $r+0(FP), R4 - MOVW $0, R5 - MOVV $101, R2 // sys_pipe2 - SYSCALL - BEQ R7, 2(PC) - SUBVU R2, R0, R2 // caller expects negative errno - MOVW R2, errno+8(FP) - RET - // func pipe2(flags int32) (r, w int32, errno int32) TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20 MOVV $r+8(FP), R4 @@ -248,7 +237,7 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 CALL (R25) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$192 // initialize REGSB = PC&0xffffffff00000000 BGEZAL R0, 1(PC) SRLV $32, R31, RSB @@ -383,18 +372,3 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0 MOVV $92, R2 // sys_fcntl SYSCALL RET - -// func runtime·setNonblock(int32 fd) -TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4 - MOVW fd+0(FP), R4 // arg 1 - fd - MOVV $3, R5 // arg 2 - cmd (F_GETFL) - MOVV $0, R6 // arg 3 - MOVV $92, R2 // sys_fcntl - SYSCALL - MOVV $4, R6 // O_NONBLOCK - OR R2, R6 // arg 3 - flags - MOVW fd+0(FP), R4 // arg 1 - fd - MOVV $4, R5 // arg 2 - cmd (F_SETFL) - MOVV $92, R2 // sys_fcntl - SYSCALL - RET diff --git a/src/runtime/sys_plan9_386.s b/src/runtime/sys_plan9_386.s index b3d2f1376d624c..bdcb98e19e0bbf 100644 --- a/src/runtime/sys_plan9_386.s +++ b/src/runtime/sys_plan9_386.s @@ -250,3 +250,7 @@ TEXT runtime·errstr(SB),NOSPLIT,$8-8 MOVL 0(SP), AX MOVL AX, ret_base+0(FP) RET + +// never called on this platform +TEXT ·sigpanictramp(SB),NOSPLIT,$0-0 + UNDEF diff --git a/src/runtime/sys_plan9_amd64.s b/src/runtime/sys_plan9_amd64.s index 731306ab448463..638300dfb93548 100644 --- a/src/runtime/sys_plan9_amd64.s +++ b/src/runtime/sys_plan9_amd64.s @@ -94,7 +94,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12 MOVQ 0(SP), AX // generated code for - // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 } + // func f(x uint64) (uint64, uint64) { return x/1000000000, x%1000000000 } // adapted to reduce duplication MOVQ AX, CX MOVQ $1360296554856532783, AX @@ -251,3 +251,7 @@ TEXT runtime·errstr(SB),NOSPLIT,$16-16 MOVQ 0(SP), AX MOVQ AX, ret_base+0(FP) RET + +// never called on this platform +TEXT ·sigpanictramp(SB),NOSPLIT,$0-0 + UNDEF diff --git a/src/runtime/sys_ppc64x.go b/src/runtime/sys_ppc64x.go index 69bd99fa09e11d..56c5c9575e5503 100644 --- a/src/runtime/sys_ppc64x.go +++ b/src/runtime/sys_ppc64x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le package runtime diff --git a/src/runtime/sys_solaris_amd64.s b/src/runtime/sys_solaris_amd64.s index 05fd187517ee19..7376e065a027c0 100644 --- a/src/runtime/sys_solaris_amd64.s +++ b/src/runtime/sys_solaris_amd64.s @@ -29,18 +29,6 @@ TEXT runtime·miniterrno(SB),NOSPLIT,$0 MOVQ AX, (m_mOS+mOS_perrno)(BX) RET -// pipe(3c) wrapper that returns fds in AX, DX. -// NOT USING GO CALLING CONVENTION. -TEXT runtime·pipe1(SB),NOSPLIT,$0 - SUBQ $16, SP // 8 bytes will do, but stack has to be 16-byte aligned - MOVQ SP, DI - LEAQ libc_pipe(SB), AX - CALL AX - MOVL 0(SP), AX - MOVL 4(SP), DX - ADDQ $16, SP - RET - // Call a library function with SysV calling conventions. // The called function can take a maximum of 6 INTEGER class arguments, // see @@ -133,7 +121,7 @@ TEXT runtime·tstart_sysvicall(SB),NOSPLIT,$0 // Careful, this is called by __sighndlr, a libc function. We must preserve // registers as per AMD 64 ABI. -TEXT runtime·sigtramp(SB),NOSPLIT,$0 +TEXT runtime·sigtramp(SB),NOSPLIT|TOPFRAME,$0 // Note that we are executing on altsigstack here, so we have // more stack available than NOSPLIT would have us believe. // To defeat the linker, we make our own stack frame with diff --git a/src/runtime/sys_wasm.go b/src/runtime/sys_wasm.go index 057ed4ccd90a75..e6e7f471eea9f8 100644 --- a/src/runtime/sys_wasm.go +++ b/src/runtime/sys_wasm.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/sys" "unsafe" ) @@ -30,7 +31,7 @@ func wasmExit(code int32) // and then stopped before the first instruction in fn. func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { sp := buf.sp - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = buf.pc buf.sp = sp buf.pc = uintptr(fn) diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s index 0b3933502ab3f4..cf3a439523c741 100644 --- a/src/runtime/sys_windows_386.s +++ b/src/runtime/sys_windows_386.s @@ -8,7 +8,7 @@ #include "time_windows.h" // void runtime·asmstdcall(void *c); -TEXT runtime·asmstdcall(SB),NOSPLIT,$0 +TEXT runtime·asmstdcall(SB),NOSPLIT,$0 MOVL fn+0(FP), BX // SetLastError(0). @@ -147,21 +147,21 @@ done: BYTE $0xC2; WORD $4 RET // unreached; make assembler happy -TEXT runtime·exceptiontramp(SB),NOSPLIT,$0 +TEXT runtime·exceptiontramp(SB),NOSPLIT,$0 MOVL $runtime·exceptionhandler(SB), AX JMP sigtramp<>(SB) -TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0 +TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0 // is never called INT $3 -TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0 +TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0 MOVL $runtime·lastcontinuehandler(SB), AX JMP sigtramp<>(SB) GLOBL runtime·cbctxts(SB), NOPTR, $4 -TEXT runtime·callbackasm1(SB),NOSPLIT,$0 +TEXT runtime·callbackasm1(SB),NOSPLIT,$0 MOVL 0(SP), AX // will use to find our callback context // remove return address from stack, we are not returning to callbackasm, but to its caller. @@ -180,7 +180,7 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0 CLD // determine index into runtime·cbs table - SUBL $runtime·callbackasm(SB), AX + SUBL $runtime·callbackasm(SB), AX MOVL $0, DX MOVL $5, BX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long DIVL BX @@ -250,7 +250,7 @@ TEXT tstart<>(SB),NOSPLIT,$0 RET // uint32 tstart_stdcall(M *newm); -TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 +TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 MOVL newm+0(FP), BX PUSHL BX diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s index e7782846b25c7c..1467b4d57bf01b 100644 --- a/src/runtime/sys_windows_amd64.s +++ b/src/runtime/sys_windows_amd64.s @@ -8,12 +8,8 @@ #include "time_windows.h" #include "cgo/abi_amd64.h" -// maxargs should be divisible by 2, as Windows stack -// must be kept 16-byte aligned on syscall entry. -#define maxargs 18 - // void runtime·asmstdcall(void *c); -TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$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 @@ -24,14 +20,14 @@ TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 MOVQ 0x30(GS), DI MOVL $0, 0x68(DI) - SUBQ $(maxargs*8), SP // room for args + SUBQ $(const_maxArgs*8), SP // room for args // Fast version, do not store args on the stack. CMPL CX, $4 JLE loadregs // Check we have enough room for args. - CMPL CX, $maxargs + CMPL CX, $const_maxArgs JLE 2(PC) INT $3 // not enough room -> crash @@ -59,7 +55,7 @@ loadregs: // Call stdcall function. CALL AX - ADDQ $(maxargs*8), SP + ADDQ $(const_maxArgs*8), SP // Return result. POPQ CX @@ -179,15 +175,15 @@ done: RET -TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 MOVQ $runtime·exceptionhandler(SB), AX JMP sigtramp<>(SB) -TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 +TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 MOVQ $runtime·firstcontinuehandler(SB), AX JMP sigtramp<>(SB) -TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 +TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0 MOVQ $runtime·lastcontinuehandler(SB), AX JMP sigtramp<>(SB) @@ -212,7 +208,7 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0 ADDQ $8, SP // determine index into runtime·cbs table - MOVQ $runtime·callbackasm(SB), DX + MOVQ $runtime·callbackasm(SB), DX SUBQ DX, AX MOVQ $0, DX MOVQ $5, CX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long @@ -245,7 +241,7 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0 RET // uint32 tstart_stdcall(M *newm); -TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 +TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 // Switch from the host ABI to the Go ABI. PUSH_REGS_HOST_TO_ABI0() @@ -348,16 +344,9 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$0-8 CMPB runtime·useQPCTime(SB), $0 JNE useQPC MOVQ $_INTERRUPT_TIME, DI -loop: - MOVL time_hi1(DI), AX - MOVL time_lo(DI), BX - MOVL time_hi2(DI), CX - CMPL AX, CX - JNE loop - SHLQ $32, CX - ORQ BX, CX - IMULQ $100, CX - MOVQ CX, ret+0(FP) + MOVQ time_lo(DI), AX + IMULQ $100, AX + MOVQ AX, ret+0(FP) RET useQPC: JMP runtime·nanotimeQPC(SB) diff --git a/src/runtime/sys_windows_arm.s b/src/runtime/sys_windows_arm.s index 48f8c7dedffd5e..5dc576a260986a 100644 --- a/src/runtime/sys_windows_arm.s +++ b/src/runtime/sys_windows_arm.s @@ -10,7 +10,7 @@ // Note: For system ABI, R0-R3 are args, R4-R11 are callee-save. // void runtime·asmstdcall(void *c); -TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 MOVM.DB.W [R4, R5, R14], (R13) // push {r4, r5, lr} MOVW R0, R4 // put libcall * in r4 MOVW R13, R5 // save stack pointer in r5 @@ -222,21 +222,21 @@ TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0 MOVW R0, R13 B (R1) -TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 MOVW $runtime·exceptionhandler(SB), R1 B sigtramp<>(SB) -TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0 MOVW $runtime·firstcontinuehandler(SB), R1 B sigtramp<>(SB) -TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0 MOVW $runtime·lastcontinuehandler(SB), R1 B sigtramp<>(SB) GLOBL runtime·cbctxts(SB), NOPTR, $4 -TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0 // On entry, the trampoline in zcallback_windows_arm.s left // the callback index in R12 (which is volatile in the C ABI). @@ -275,7 +275,7 @@ TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0 B (R12) // return // uint32 tstart_stdcall(M *newm); -TEXT runtime·tstart_stdcall(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·tstart_stdcall(SB),NOSPLIT|NOFRAME,$0 MOVM.DB.W [R4-R11, R14], (R13) // push {r4-r11, lr} MOVW m_g0(R0), g @@ -319,7 +319,7 @@ TEXT runtime·usleep2(SB),NOSPLIT|NOFRAME,$0-4 // Runs on OS stack. // duration (in -100ns units) is in dt+0(FP). // g is valid. -// TODO: neeeds to be implemented properly. +// TODO: needs to be implemented properly. TEXT runtime·usleep2HighRes(SB),NOSPLIT|NOFRAME,$0-4 B runtime·abort(SB) @@ -350,7 +350,9 @@ TEXT runtime·nanotime1(SB),NOSPLIT|NOFRAME,$0-8 MOVW $_INTERRUPT_TIME, R3 loop: MOVW time_hi1(R3), R1 + DMB MB_ISH MOVW time_lo(R3), R0 + DMB MB_ISH MOVW time_hi2(R3), R2 CMP R1, R2 BNE loop diff --git a/src/runtime/sys_windows_arm64.s b/src/runtime/sys_windows_arm64.s index 7a2e11f5ae2983..024625f8210668 100644 --- a/src/runtime/sys_windows_arm64.s +++ b/src/runtime/sys_windows_arm64.s @@ -7,6 +7,7 @@ #include "textflag.h" #include "funcdata.h" #include "time_windows.h" +#include "cgo/abi_arm64.h" // Offsets into Thread Environment Block (pointer in R18) #define TEB_error 0x68 @@ -18,7 +19,7 @@ // load_g and save_g (in tls_arm64.s) clobber R27 (REGTMP) and R0. // void runtime·asmstdcall(void *c); -TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0 STP.W (R29, R30), -32(RSP) // allocate C ABI stack frame STP (R19, R20), 16(RSP) // save old R19, R20 MOVD R0, R19 // save libcall pointer @@ -128,30 +129,6 @@ TEXT runtime·getlasterror(SB),NOSPLIT|NOFRAME,$0 MOVD R0, ret+0(FP) RET -#define SAVE_R19_TO_R28(offset) \ - MOVD R19, savedR19+((offset)+0*8)(SP); \ - MOVD R20, savedR20+((offset)+1*8)(SP); \ - MOVD R21, savedR21+((offset)+2*8)(SP); \ - MOVD R22, savedR22+((offset)+3*8)(SP); \ - MOVD R23, savedR23+((offset)+4*8)(SP); \ - MOVD R24, savedR24+((offset)+5*8)(SP); \ - MOVD R25, savedR25+((offset)+6*8)(SP); \ - MOVD R26, savedR26+((offset)+7*8)(SP); \ - MOVD R27, savedR27+((offset)+8*8)(SP); \ - MOVD g, savedR28+((offset)+9*8)(SP); - -#define RESTORE_R19_TO_R28(offset) \ - MOVD savedR19+((offset)+0*8)(SP), R19; \ - MOVD savedR20+((offset)+1*8)(SP), R20; \ - MOVD savedR21+((offset)+2*8)(SP), R21; \ - MOVD savedR22+((offset)+3*8)(SP), R22; \ - MOVD savedR23+((offset)+4*8)(SP), R23; \ - MOVD savedR24+((offset)+5*8)(SP), R24; \ - MOVD savedR25+((offset)+6*8)(SP), R25; \ - MOVD savedR26+((offset)+7*8)(SP), R26; \ - MOVD savedR27+((offset)+8*8)(SP), R27; \ - MOVD savedR28+((offset)+9*8)(SP), g; /* R28 */ - // Called by Windows as a Vectored Exception Handler (VEH). // First argument is pointer to struct containing // exception record and context pointers. @@ -221,7 +198,8 @@ TEXT sigtramp_g0<>(SB),NOSPLIT,$128 NO_LOCAL_POINTERS // Push C callee-save registers R19-R28. LR, FP already saved. - SAVE_R19_TO_R28(-10*8) + // These registers will occupy the upper 10 words of the frame. + SAVE_R19_TO_R28(8*7) MOVD 0(R0), R5 // R5 = ExceptionPointers->ExceptionRecord MOVD 8(R0), R6 // R6 = ExceptionPointers->ContextRecord @@ -275,7 +253,7 @@ TEXT sigtramp_g0<>(SB),NOSPLIT,$128 MOVD R2, context_pc(R6) return: - RESTORE_R19_TO_R28(-10*8) // smashes g + RESTORE_R19_TO_R28(8*7) // smashes g RET // Trampoline to resume execution from exception handler. @@ -290,21 +268,21 @@ TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0 MOVD R0, RSP B (R1) -TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 - MOVD $runtime·exceptionhandler(SB), R1 +TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0 + MOVD $runtime·exceptionhandler(SB), R1 B sigtramp<>(SB) -TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0 - MOVD $runtime·firstcontinuehandler(SB), R1 +TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0 + MOVD $runtime·firstcontinuehandler(SB), R1 B sigtramp<>(SB) TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0 - MOVD $runtime·lastcontinuehandler(SB), R1 + MOVD $runtime·lastcontinuehandler(SB), R1 B sigtramp<>(SB) GLOBL runtime·cbctxts(SB), NOPTR, $4 -TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 +TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 NO_LOCAL_POINTERS // On entry, the trampoline in zcallback_windows_arm64.s left @@ -317,18 +295,14 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 // but we are not called from Go so that space is ours to use, // and we must to be contiguous with the stack arguments. MOVD $arg0-(7*8)(SP), R14 - MOVD R0, (0*8)(R14) - MOVD R1, (1*8)(R14) - MOVD R2, (2*8)(R14) - MOVD R3, (3*8)(R14) - MOVD R4, (4*8)(R14) - MOVD R5, (5*8)(R14) - MOVD R6, (6*8)(R14) - MOVD R7, (7*8)(R14) + STP (R0, R1), (0*8)(R14) + STP (R2, R3), (2*8)(R14) + STP (R4, R5), (4*8)(R14) + STP (R6, R7), (6*8)(R14) // Push C callee-save registers R19-R28. // LR, FP already saved. - SAVE_R19_TO_R28(-18*8) + SAVE_R19_TO_R28(8*9) // Create a struct callbackArgs on our stack. MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 @@ -339,11 +313,10 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 MOVD R0, callbackArgs_result(R13) // result // Call cgocallback, which will call callbackWrap(frame). - MOVD $·callbackWrap(SB), R0 // PC of function to call + MOVD $·callbackWrap(SB), R0 // PC of function to call, cgocallback takes an ABIInternal entry-point MOVD R13, R1 // frame (&callbackArgs{...}) MOVD $0, R2 // context - MOVD R0, (1*8)(RSP) - MOVD R1, (2*8)(RSP) + STP (R0, R1), (1*8)(RSP) MOVD R2, (3*8)(RSP) BL runtime·cgocallback(SB) @@ -351,13 +324,13 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 MOVD callbackArgs_result(R13), R0 - RESTORE_R19_TO_R28(-18*8) + RESTORE_R19_TO_R28(8*9) RET // uint32 tstart_stdcall(M *newm); -TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 - SAVE_R19_TO_R28(-10*8) +TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 + SAVE_R19_TO_R28(8*3) MOVD m_g0(R0), g MOVD R0, g_m(g) @@ -374,7 +347,7 @@ TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 BL runtime·emptyfunc(SB) // fault if stack check is wrong BL runtime·mstart(SB) - RESTORE_R19_TO_R28(-10*8) + RESTORE_R19_TO_R28(8*3) // Exit the thread. MOVD $0, R0 @@ -398,7 +371,7 @@ TEXT runtime·usleep2(SB),NOSPLIT,$32-4 // Runs on OS stack. // duration (in -100ns units) is in dt+0(FP). // g is valid. -// TODO: neeeds to be implemented properly. +// TODO: needs to be implemented properly. TEXT runtime·usleep2HighRes(SB),NOSPLIT,$0-4 B runtime·abort(SB) @@ -415,15 +388,7 @@ TEXT runtime·nanotime1(SB),NOSPLIT|NOFRAME,$0-8 CMP $0, R0 BNE useQPC MOVD $_INTERRUPT_TIME, R3 -loop: - MOVWU time_hi1(R3), R1 - MOVWU time_lo(R3), R0 - MOVWU time_hi2(R3), R2 - CMP R1, R2 - BNE loop - - // wintime = R1:R0, multiply by 100 - ORR R1<<32, R0 + MOVD time_lo(R3), R0 MOVD $100, R1 MUL R1, R0 MOVD R0, ret+0(FP) diff --git a/src/runtime/sys_x86.go b/src/runtime/sys_x86.go index 0866e3140e09d0..9fb36c2a661d5c 100644 --- a/src/runtime/sys_x86.go +++ b/src/runtime/sys_x86.go @@ -3,12 +3,11 @@ // license that can be found in the LICENSE file. //go:build amd64 || 386 -// +build amd64 386 package runtime import ( - "runtime/internal/sys" + "internal/goarch" "unsafe" ) @@ -16,7 +15,7 @@ import ( // and then stopped before the first instruction in fn. func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { sp := buf.sp - sp -= sys.PtrSize + sp -= goarch.PtrSize *(*uintptr)(unsafe.Pointer(sp)) = buf.pc buf.sp = sp buf.pc = uintptr(fn) diff --git a/src/runtime/syscall_aix.go b/src/runtime/syscall_aix.go index 79b51240e906c3..cc9e912613dcac 100644 --- a/src/runtime/syscall_aix.go +++ b/src/runtime/syscall_aix.go @@ -126,9 +126,10 @@ func syscall_chroot1(path uintptr) (err uintptr) { } // like close, but must not split stack, for fork. -//go:linkname syscall_close syscall.close +// +//go:linkname syscall_closeFD syscall.closeFD //go:nosplit -func syscall_close(fd int32) int32 { +func syscall_closeFD(fd int32) int32 { _, err := syscall1(&libc_close, uintptr(fd)) return int32(err) } @@ -148,6 +149,7 @@ func syscall_execve(path, argv, envp uintptr) (err uintptr) { } // like exit, but must not split stack, for fork. +// //go:linkname syscall_exit syscall.exit //go:nosplit func syscall_exit(code uintptr) { diff --git a/src/runtime/syscall_solaris.go b/src/runtime/syscall_solaris.go index 094516927f3f49..e7bab3b23fb54f 100644 --- a/src/runtime/syscall_solaris.go +++ b/src/runtime/syscall_solaris.go @@ -25,16 +25,12 @@ var ( libc_wait4 libcFunc ) -//go:linkname pipe1x runtime.pipe1 -var pipe1x libcFunc // name to take addr of pipe1 - -func pipe1() // declared for vet; do NOT call - // Many of these are exported via linkname to assembly in the syscall // package. //go:nosplit //go:linkname syscall_sysvicall6 +//go:cgo_unsafe_args func syscall_sysvicall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { call := libcall{ fn: fn, @@ -49,6 +45,7 @@ func syscall_sysvicall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err //go:nosplit //go:linkname syscall_rawsysvicall6 +//go:cgo_unsafe_args func syscall_rawsysvicall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { call := libcall{ fn: fn, @@ -88,6 +85,7 @@ func syscall_chroot(path uintptr) (err uintptr) { } // like close, but must not split stack, for forkx. +// //go:nosplit //go:linkname syscall_close func syscall_close(fd int32) int32 { @@ -104,6 +102,7 @@ func syscall_dup2(oldfd, newfd uintptr) (val, err uintptr) { //go:nosplit //go:linkname syscall_execve +//go:cgo_unsafe_args func syscall_execve(path, argv, envp uintptr) (err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_execve)), @@ -115,6 +114,7 @@ func syscall_execve(path, argv, envp uintptr) (err uintptr) { } // like exit, but must not split stack, for forkx. +// //go:nosplit //go:linkname syscall_exit func syscall_exit(code uintptr) { @@ -123,6 +123,7 @@ func syscall_exit(code uintptr) { //go:nosplit //go:linkname syscall_fcntl +//go:cgo_unsafe_args func syscall_fcntl(fd, cmd, arg uintptr) (val, err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_fcntl)), @@ -181,6 +182,7 @@ func syscall_getpid() (pid, err uintptr) { //go:nosplit //go:linkname syscall_ioctl +//go:cgo_unsafe_args func syscall_ioctl(fd, req, arg uintptr) (err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_ioctl)), @@ -191,19 +193,6 @@ func syscall_ioctl(fd, req, arg uintptr) (err uintptr) { return call.err } -//go:linkname syscall_pipe -func syscall_pipe() (r, w, err uintptr) { - call := libcall{ - fn: uintptr(unsafe.Pointer(&pipe1x)), - n: 0, - args: uintptr(unsafe.Pointer(&pipe1x)), // it's unused but must be non-nil, otherwise crashes - } - entersyscallblock() - asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&call)) - exitsyscall() - return call.r1, call.r2, call.err -} - // This is syscall.RawSyscall, it exists to satisfy some build dependency, // but it doesn't work. // @@ -234,6 +223,7 @@ func syscall_setgid(gid uintptr) (err uintptr) { //go:nosplit //go:linkname syscall_setgroups +//go:cgo_unsafe_args func syscall_setgroups(ngid, gid uintptr) (err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_setgroups)), @@ -270,6 +260,7 @@ func syscall_setuid(uid uintptr) (err uintptr) { //go:nosplit //go:linkname syscall_setpgid +//go:cgo_unsafe_args func syscall_setpgid(pid, pgid uintptr) (err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_setpgid)), @@ -281,6 +272,7 @@ func syscall_setpgid(pid, pgid uintptr) (err uintptr) { } //go:linkname syscall_syscall +//go:cgo_unsafe_args func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_syscall)), @@ -294,6 +286,7 @@ func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { } //go:linkname syscall_wait4 +//go:cgo_unsafe_args func syscall_wait4(pid uintptr, wstatus *uint32, options uintptr, rusage unsafe.Pointer) (wpid int, err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_wait4)), @@ -303,11 +296,14 @@ func syscall_wait4(pid uintptr, wstatus *uint32, options uintptr, rusage unsafe. entersyscallblock() asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&call)) exitsyscall() + KeepAlive(wstatus) + KeepAlive(rusage) return int(call.r1), call.err } //go:nosplit //go:linkname syscall_write +//go:cgo_unsafe_args func syscall_write(fd, buf, nbyte uintptr) (n, err uintptr) { call := libcall{ fn: uintptr(unsafe.Pointer(&libc_write)), diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index 4763a440e7ef1c..76036ad098e372 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -6,18 +6,36 @@ package runtime import ( "internal/abi" - "runtime/internal/sys" + "internal/goarch" "unsafe" ) // cbs stores all registered Go callbacks. var cbs struct { - lock mutex + lock mutex // use cbsLock / cbsUnlock for race instrumentation. ctxt [cb_max]winCallback index map[winCallbackKey]int n int } +func cbsLock() { + lock(&cbs.lock) + // compileCallback is used by goenvs prior to completion of schedinit. + // raceacquire involves a racecallback to get the proc, which is not + // safe prior to scheduler initialization. Thus avoid instrumentation + // until then. + if raceenabled && mainStarted { + raceacquire(unsafe.Pointer(&cbs.lock)) + } +} + +func cbsUnlock() { + if raceenabled && mainStarted { + racerelease(unsafe.Pointer(&cbs.lock)) + } + unlock(&cbs.lock) +} + // winCallback records information about a registered Go callback. type winCallback struct { fn *funcval // Go function @@ -73,7 +91,7 @@ type abiDesc struct { } func (p *abiDesc) assignArg(t *_type) { - if t.size > sys.PtrSize { + if t.size > goarch.PtrSize { // We don't support this right now. In // stdcall/cdecl, 64-bit ints and doubles are // passed as two words (little endian); and @@ -146,13 +164,13 @@ func (p *abiDesc) assignArg(t *_type) { // cdecl, stdcall, fastcall, and arm pad arguments to word size. // TODO(rsc): On arm and arm64 do we need to skip the caller's saved LR? - p.srcStackSize += sys.PtrSize + p.srcStackSize += goarch.PtrSize } // tryRegAssignArg tries to register-assign a value of type t. // If this type is nested in an aggregate type, then offset is the // offset of this type within its parent type. -// Assumes t.size <= sys.PtrSize and t.size != 0. +// Assumes t.size <= goarch.PtrSize and t.size != 0. // // Returns whether the assignment succeeded. func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool { @@ -162,7 +180,7 @@ func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool { return p.assignReg(t.size, offset) case kindInt64, kindUint64: // Only register-assign if the registers are big enough. - if sys.PtrSize == 8 { + if goarch.PtrSize == 8 { return p.assignReg(t.size, offset) } case kindArray: @@ -174,7 +192,7 @@ func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool { st := (*structtype)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !p.tryRegAssignArg(f.typ, offset+f.offset()) { + if !p.tryRegAssignArg(f.typ, offset+f.offset) { return false } } @@ -232,10 +250,10 @@ func callbackasmAddr(i int) uintptr { // followed by a branch instruction entrySize = 8 } - return funcPC(callbackasm) + uintptr(i*entrySize) + return abi.FuncPCABI0(callbackasm) + uintptr(i*entrySize) } -const callbackMaxFrame = 64 * sys.PtrSize +const callbackMaxFrame = 64 * goarch.PtrSize // compileCallback converts a Go function fn into a C function pointer // that can be passed to Windows APIs. @@ -263,13 +281,13 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) { } // The Go ABI aligns the result to the word size. src is // already aligned. - abiMap.dstStackSize = alignUp(abiMap.dstStackSize, sys.PtrSize) + abiMap.dstStackSize = alignUp(abiMap.dstStackSize, goarch.PtrSize) abiMap.retOffset = abiMap.dstStackSize if len(ft.out()) != 1 { panic("compileCallback: expected function with one uintptr-sized result") } - if ft.out()[0].size != sys.PtrSize { + if ft.out()[0].size != goarch.PtrSize { panic("compileCallback: expected function with one uintptr-sized result") } if k := ft.out()[0].kind & kindMask; k == kindFloat32 || k == kindFloat64 { @@ -282,12 +300,12 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) { // Make room for the uintptr-sized result. // If there are argument registers, the return value will // be passed in the first register. - abiMap.dstStackSize += sys.PtrSize + abiMap.dstStackSize += goarch.PtrSize } // TODO(mknyszek): Remove dstSpill from this calculation when we no longer have // caller reserved spill space. - frameSize := alignUp(abiMap.dstStackSize, sys.PtrSize) + frameSize := alignUp(abiMap.dstStackSize, goarch.PtrSize) frameSize += abiMap.dstSpill if frameSize > callbackMaxFrame { panic("compileCallback: function argument frame too large") @@ -302,11 +320,11 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) { key := winCallbackKey{(*funcval)(fn.data), cdecl} - lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack. + cbsLock() // Check if this callback is already registered. if n, ok := cbs.index[key]; ok { - unlock(&cbs.lock) + cbsUnlock() return callbackasmAddr(n) } @@ -316,7 +334,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) { } n := cbs.n if n >= len(cbs.ctxt) { - unlock(&cbs.lock) + cbsUnlock() throw("too many callback functions") } c := winCallback{key.fn, retPop, abiMap} @@ -324,7 +342,7 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) { cbs.index[key] = n cbs.n++ - unlock(&cbs.lock) + cbsUnlock() return callbackasmAddr(n) } @@ -370,7 +388,7 @@ func callbackWrap(a *callbackArgs) { // TODO(mknyszek): Remove this when we no longer have // caller reserved spill space. - frameSize := alignUp(c.abiMap.dstStackSize, sys.PtrSize) + frameSize := alignUp(c.abiMap.dstStackSize, goarch.PtrSize) frameSize += c.abiMap.dstSpill // Even though this is copying back results, we can pass a nil @@ -399,6 +417,7 @@ const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 // parameter and the important SEARCH_SYSTEM32 argument. But on systems that // do not have that option, absoluteFilepath should contain a fallback // to the full path inside of system32 for use with vanilla LoadLibrary. +// //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary //go:nosplit //go:cgo_unsafe_args @@ -422,6 +441,8 @@ func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (hand } cgocall(asmstdcallAddr, unsafe.Pointer(c)) + KeepAlive(filename) + KeepAlive(absoluteFilepath) handle = c.r1 if handle == 0 { err = c.err @@ -441,6 +462,7 @@ func syscall_loadlibrary(filename *uint16) (handle, err uintptr) { c.n = 1 c.args = uintptr(noescape(unsafe.Pointer(&filename))) cgocall(asmstdcallAddr, unsafe.Pointer(c)) + KeepAlive(filename) handle = c.r1 if handle == 0 { err = c.err @@ -459,6 +481,7 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint c.n = 2 c.args = uintptr(noescape(unsafe.Pointer(&handle))) cgocall(asmstdcallAddr, unsafe.Pointer(c)) + KeepAlive(procname) outhandle = c.r1 if outhandle == 0 { err = c.err @@ -468,84 +491,69 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint //go:linkname syscall_Syscall syscall.Syscall //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) { - lockOSThread() - defer unlockOSThread() - c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - return c.r1, c.r2, c.err + return syscall_SyscallN(fn, a1, a2, a3) } //go:linkname syscall_Syscall6 syscall.Syscall6 //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { - lockOSThread() - defer unlockOSThread() - c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - return c.r1, c.r2, c.err + return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6) } //go:linkname syscall_Syscall9 syscall.Syscall9 //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { - lockOSThread() - c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - unlockOSThread() - return c.r1, c.r2, c.err + return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9) } //go:linkname syscall_Syscall12 syscall.Syscall12 //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) { - lockOSThread() - c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - unlockOSThread() - return c.r1, c.r2, c.err + return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) } //go:linkname syscall_Syscall15 syscall.Syscall15 //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { - lockOSThread() - c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) - cgocall(asmstdcallAddr, unsafe.Pointer(c)) - unlockOSThread() - return c.r1, c.r2, c.err + return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) } //go:linkname syscall_Syscall18 syscall.Syscall18 //go:nosplit -//go:cgo_unsafe_args func syscall_Syscall18(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2, err uintptr) { + return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) +} + +// maxArgs should be divisible by 2, as Windows stack +// must be kept 16-byte aligned on syscall entry. +// +// Although it only permits maximum 42 parameters, it +// is arguably large enough. +const maxArgs = 42 + +//go:linkname syscall_SyscallN syscall.SyscallN +//go:nosplit +func syscall_SyscallN(trap uintptr, args ...uintptr) (r1, r2, err uintptr) { + nargs := len(args) + + // asmstdcall expects it can access the first 4 arguments + // to load them into registers. + var tmp [4]uintptr + switch { + case nargs < 4: + copy(tmp[:], args) + args = tmp[:] + case nargs > maxArgs: + panic("runtime: SyscallN has too many arguments") + } + lockOSThread() + defer unlockOSThread() c := &getg().m.syscall - c.fn = fn - c.n = nargs - c.args = uintptr(noescape(unsafe.Pointer(&a1))) + c.fn = trap + c.n = uintptr(nargs) + c.args = uintptr(noescape(unsafe.Pointer(&args[0]))) cgocall(asmstdcallAddr, unsafe.Pointer(c)) - unlockOSThread() return c.r1, c.r2, c.err } diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index e3f772ac4bb945..37f8f40cfba730 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -288,7 +288,7 @@ func TestCallbackInAnotherThread(t *testing.T) { } type cbFunc struct { - goFunc interface{} + goFunc any } func (f cbFunc) cName(cdecl bool) string { @@ -469,6 +469,7 @@ func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr { // that insufficient spill slots allocated (according to the ABI) // may cause compiler-generated spills to clobber the return PC. // Then, the GC stack scanning will catch that. +// //go:registerparams func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr { runtime.GC() @@ -628,6 +629,9 @@ func TestOutputDebugString(t *testing.T) { } func TestRaiseException(t *testing.T) { + if testenv.Builder() == "windows-amd64-2012" { + testenv.SkipFlaky(t, 49681) + } o := runTestProg(t, "testprog", "RaiseException") if strings.Contains(o, "RaiseException should not return") { t.Fatalf("RaiseException did not crash program: %v", o) @@ -759,7 +763,7 @@ uintptr_t cfunc(callback f, uintptr_t n) { } } -func TestSyscall18(t *testing.T) { +func TestSyscallN(t *testing.T) { if _, err := exec.LookPath("gcc"); err != nil { t.Skip("skipping test: gcc is missing") } @@ -767,40 +771,52 @@ func TestSyscall18(t *testing.T) { t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) } - const src = ` -#include -#include + for arglen := 0; arglen <= runtime.MaxArgs; arglen++ { + arglen := arglen + t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) { + t.Parallel() + args := make([]string, arglen) + rets := make([]string, arglen+1) + params := make([]uintptr, arglen) + for i := range args { + args[i] = fmt.Sprintf("int a%d", i) + rets[i] = fmt.Sprintf("(a%d == %d)", i, i) + params[i] = uintptr(i) + } + rets[arglen] = "1" // for arglen == 0 -int cfunc( int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, - int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18) { - return 1; -} -` - tmpdir := t.TempDir() + src := fmt.Sprintf(` + #include + #include + int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && ")) - srcname := "mydll.c" - err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) - if err != nil { - t.Fatal(err) - } - outname := "mydll.dll" - cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) - cmd.Dir = tmpdir - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("failed to build dll: %v - %v", err, string(out)) - } - dllpath := filepath.Join(tmpdir, outname) + tmpdir := t.TempDir() - dll := syscall.MustLoadDLL(dllpath) - defer dll.Release() + srcname := "mydll.c" + err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) + if err != nil { + t.Fatal(err) + } + outname := "mydll.dll" + cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) + cmd.Dir = tmpdir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to build dll: %v\n%s", err, out) + } + dllpath := filepath.Join(tmpdir, outname) - proc := dll.MustFindProc("cfunc") + dll := syscall.MustLoadDLL(dllpath) + defer dll.Release() - // proc.Call() will call Syscall18() internally. - r, _, err := proc.Call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) - if r != 1 { - t.Errorf("got %d want 1 (err=%v)", r, err) + proc := dll.MustFindProc("cfunc") + + // proc.Call() will call SyscallN() internally. + r, _, err := proc.Call(params...) + if r != 1 { + t.Errorf("got %d want 1 (err=%v)", r, err) + } + }) } } diff --git a/src/runtime/testdata/testprog/badtraceback.go b/src/runtime/testdata/testprog/badtraceback.go index d558adceec3f95..09aa2b877ecf5a 100644 --- a/src/runtime/testdata/testprog/badtraceback.go +++ b/src/runtime/testdata/testprog/badtraceback.go @@ -17,6 +17,9 @@ func init() { func BadTraceback() { // Disable GC to prevent traceback at unexpected time. debug.SetGCPercent(-1) + // Out of an abundance of caution, also make sure that there are + // no GCs actively in progress. + runtime.GC() // Run badLR1 on its own stack to minimize the stack size and // exercise the stack bounds logic in the hex dump. diff --git a/src/runtime/testdata/testprog/checkptr.go b/src/runtime/testdata/testprog/checkptr.go index 9c5561396e5d43..60e71e66d7f4f5 100644 --- a/src/runtime/testdata/testprog/checkptr.go +++ b/src/runtime/testdata/testprog/checkptr.go @@ -20,6 +20,9 @@ func init() { register("CheckPtrSmall", CheckPtrSmall) register("CheckPtrSliceOK", CheckPtrSliceOK) register("CheckPtrSliceFail", CheckPtrSliceFail) + register("CheckPtrStringOK", CheckPtrStringOK) + register("CheckPtrStringFail", CheckPtrStringFail) + register("CheckPtrAlignmentNested", CheckPtrAlignmentNested) } func CheckPtrAlignmentNoPtr() { @@ -96,3 +99,21 @@ func CheckPtrSliceFail() { sink2 = p sink2 = unsafe.Slice(p, 100) } + +func CheckPtrStringOK() { + p := new([4]byte) + sink2 = unsafe.String(&p[1], 3) +} + +func CheckPtrStringFail() { + p := new(byte) + sink2 = p + sink2 = unsafe.String(p, 100) +} + +func CheckPtrAlignmentNested() { + s := make([]int8, 100) + p := unsafe.Pointer(&s[0]) + n := 9 + _ = ((*[10]int8)(unsafe.Pointer((*[10]int64)(unsafe.Pointer(&p)))))[:n:n] +} diff --git a/src/runtime/testdata/testprog/crash.go b/src/runtime/testdata/testprog/crash.go index c4990cdda9a078..38c8f6a2fa26b3 100644 --- a/src/runtime/testdata/testprog/crash.go +++ b/src/runtime/testdata/testprog/crash.go @@ -12,6 +12,13 @@ import ( func init() { register("Crash", Crash) register("DoublePanic", DoublePanic) + register("ErrorPanic", ErrorPanic) + register("StringerPanic", StringerPanic) + register("DoubleErrorPanic", DoubleErrorPanic) + register("DoubleStringerPanic", DoubleStringerPanic) + register("StringPanic", StringPanic) + register("NilPanic", NilPanic) + register("CircularPanic", CircularPanic) } func test(name string) { @@ -64,3 +71,69 @@ func DoublePanic() { }() panic(P("XXX")) } + +// Test that panic while panicking discards error message +// See issue 52257 +type exampleError struct{} + +func (e exampleError) Error() string { + panic("important error message") +} + +func ErrorPanic() { + panic(exampleError{}) +} + +type examplePanicError struct{} + +func (e examplePanicError) Error() string { + panic(exampleError{}) +} + +func DoubleErrorPanic() { + panic(examplePanicError{}) +} + +type exampleStringer struct{} + +func (s exampleStringer) String() string { + panic("important stringer message") +} + +func StringerPanic() { + panic(exampleStringer{}) +} + +type examplePanicStringer struct{} + +func (s examplePanicStringer) String() string { + panic(exampleStringer{}) +} + +func DoubleStringerPanic() { + panic(examplePanicStringer{}) +} + +func StringPanic() { + panic("important string message") +} + +func NilPanic() { + panic(nil) +} + +type exampleCircleStartError struct{} + +func (e exampleCircleStartError) Error() string { + panic(exampleCircleEndError{}) +} + +type exampleCircleEndError struct{} + +func (e exampleCircleEndError) Error() string { + panic(exampleCircleStartError{}) +} + +func CircularPanic() { + panic(exampleCircleStartError{}) +} diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go index 74732cd9f4b1b2..0f44575381ed57 100644 --- a/src/runtime/testdata/testprog/gc.go +++ b/src/runtime/testdata/testprog/gc.go @@ -6,9 +6,12 @@ package main import ( "fmt" + "math" "os" "runtime" "runtime/debug" + "runtime/metrics" + "sync" "sync/atomic" "time" "unsafe" @@ -21,6 +24,8 @@ func init() { register("GCPhys", GCPhys) register("DeferLiveness", DeferLiveness) register("GCZombie", GCZombie) + register("GCMemoryLimit", GCMemoryLimit) + register("GCMemoryLimitNoGCPercent", GCMemoryLimitNoGCPercent) } func GCSys() { @@ -90,7 +95,7 @@ func GCFairness2() { runtime.GOMAXPROCS(1) debug.SetGCPercent(1) var count [3]int64 - var sink [3]interface{} + var sink [3]any for i := range count { go func(i int) { for { @@ -132,81 +137,88 @@ func GCFairness2() { func GCPhys() { // This test ensures that heap-growth scavenging is working as intended. // - // It sets up a specific scenario: it allocates two pairs of objects whose - // sizes sum to size. One object in each pair is "small" (though must be - // large enough to be considered a large object by the runtime) and one is - // large. The small objects are kept while the large objects are freed, - // creating two large unscavenged holes in the heap. The heap goal should - // also be small as a result (so size must be at least as large as the - // minimum heap size). We then allocate one large object, bigger than both - // pairs of objects combined. This allocation, because it will tip - // HeapSys-HeapReleased well above the heap goal, should trigger heap-growth - // scavenging and scavenge most, if not all, of the large holes we created - // earlier. + // It attempts to construct a sizeable "swiss cheese" heap, with many + // allocChunk-sized holes. Then, it triggers a heap growth by trying to + // allocate as much memory as would fit in those holes. + // + // The heap growth should cause a large number of those holes to be + // returned to the OS. + const ( - // Size must be also large enough to be considered a large - // object (not in any size-segregated span). - size = 4 << 20 - split = 64 << 10 - objects = 2 + // The total amount of memory we're willing to allocate. + allocTotal = 32 << 20 // The page cache could hide 64 8-KiB pages from the scavenger today. maxPageCache = (8 << 10) * 64 - - // Reduce GOMAXPROCS down to 4 if it's greater. We need to bound the amount - // of memory held in the page cache because the scavenger can't reach it. - // The page cache will hold at most maxPageCache of memory per-P, so this - // bounds the amount of memory hidden from the scavenger to 4*maxPageCache - // at most. - maxProcs = 4 ) - // Set GOGC so that this test operates under consistent assumptions. - debug.SetGCPercent(100) - procs := runtime.GOMAXPROCS(-1) - if procs > maxProcs { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) - procs = runtime.GOMAXPROCS(-1) + + // How big the allocations are needs to depend on the page size. + // If the page size is too big and the allocations are too small, + // they might not be aligned to the physical page size, so the scavenger + // will gloss over them. + pageSize := os.Getpagesize() + var allocChunk int + if pageSize <= 8<<10 { + allocChunk = 64 << 10 + } else { + allocChunk = 512 << 10 } - // Save objects which we want to survive, and condemn objects which we don't. - // Note that we condemn objects in this way and release them all at once in - // order to avoid having the GC start freeing up these objects while the loop - // is still running and filling in the holes we intend to make. - saved := make([][]byte, 0, objects+1) - condemned := make([][]byte, 0, objects) - for i := 0; i < 2*objects; i++ { + allocs := allocTotal / allocChunk + + // Set GC percent just so this test is a little more consistent in the + // face of varying environments. + debug.SetGCPercent(100) + + // Set GOMAXPROCS to 1 to minimize the amount of memory held in the page cache, + // and to reduce the chance that the background scavenger gets scheduled. + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + // Allocate allocTotal bytes of memory in allocChunk byte chunks. + // Alternate between whether the chunk will be held live or will be + // condemned to GC to create holes in the heap. + saved := make([][]byte, allocs/2+1) + condemned := make([][]byte, allocs/2) + for i := 0; i < allocs; i++ { + b := make([]byte, allocChunk) if i%2 == 0 { - saved = append(saved, make([]byte, split)) + saved = append(saved, b) } else { - condemned = append(condemned, make([]byte, size-split)) + condemned = append(condemned, b) } } - condemned = nil - // Clean up the heap. This will free up every other object created above - // (i.e. everything in condemned) creating holes in the heap. - // Also, if the condemned objects are still being swept, its possible that - // the scavenging that happens as a result of the next allocation won't see - // the holes at all. We call runtime.GC() twice here so that when we allocate - // our large object there's no race with sweeping. - runtime.GC() + + // Run a GC cycle just so we're at a consistent state. runtime.GC() - // Perform one big allocation which should also scavenge any holes. - // - // The heap goal will rise after this object is allocated, so it's very - // important that we try to do all the scavenging in a single allocation - // that exceeds the heap goal. Otherwise the rising heap goal could foil our - // test. - saved = append(saved, make([]byte, objects*size)) - // Clean up the heap again just to put it in a known state. + + // Drop the only reference to all the condemned memory. + condemned = nil + + // Clear the condemned memory. runtime.GC() + + // At this point, the background scavenger is likely running + // and could pick up the work, so the next line of code doesn't + // end up doing anything. That's fine. What's important is that + // this test fails somewhat regularly if the runtime doesn't + // scavenge on heap growth, and doesn't fail at all otherwise. + + // Make a large allocation that in theory could fit, but won't + // because we turned the heap into swiss cheese. + saved = append(saved, make([]byte, allocTotal/2)) + // heapBacked is an estimate of the amount of physical memory used by // this test. HeapSys is an estimate of the size of the mapped virtual // address space (which may or may not be backed by physical pages) // whereas HeapReleased is an estimate of the amount of bytes returned // to the OS. Their difference then roughly corresponds to the amount // of virtual address space that is backed by physical pages. + // + // heapBacked also subtracts out maxPageCache bytes of memory because + // this is memory that may be hidden from the scavenger per-P. Since + // GOMAXPROCS=1 here, subtracting it out once is fine. var stats runtime.MemStats runtime.ReadMemStats(&stats) - heapBacked := stats.HeapSys - stats.HeapReleased + heapBacked := stats.HeapSys - stats.HeapReleased - maxPageCache // If heapBacked does not exceed the heap goal by more than retainExtraPercent // then the scavenger is working as expected; the newly-created holes have been // scavenged immediately as part of the allocations which cannot fit in the holes. @@ -216,19 +228,14 @@ func GCPhys() { // to other allocations that happen during this test we may still see some physical // memory over-use. overuse := (float64(heapBacked) - float64(stats.HeapAlloc)) / float64(stats.HeapAlloc) - // Compute the threshold. + // Check against our overuse threshold, which is what the scavenger always reserves + // to encourage allocation of memory that doesn't need to be faulted in. // - // In theory, this threshold should just be zero, but that's not possible in practice. - // Firstly, the runtime's page cache can hide up to maxPageCache of free memory from the - // scavenger per P. To account for this, we increase the threshold by the ratio between the - // total amount the runtime could hide from the scavenger to the amount of memory we expect - // to be able to scavenge here, which is (size-split)*objects. This computation is the crux - // GOMAXPROCS above; if GOMAXPROCS is too high the threshold just becomes 100%+ since the - // amount of memory being allocated is fixed. Then we add 5% to account for noise, such as - // other allocations this test may have performed that we don't explicitly account for The - // baseline threshold here is around 11% for GOMAXPROCS=1, capping out at around 30% for - // GOMAXPROCS=4. - threshold := 0.05 + float64(procs)*maxPageCache/float64((size-split)*objects) + // Add additional slack in case the page size is large and the scavenger + // can't reach that memory because it doesn't constitute a complete aligned + // physical page. Assume the worst case: a full physical page out of each + // allocation. + threshold := 0.1 + float64(pageSize)/float64(allocChunk) if overuse <= threshold { fmt.Println("OK") return @@ -243,6 +250,7 @@ func GCPhys() { "(alloc: %d, goal: %d, sys: %d, rel: %d, objs: %d)\n", threshold*100, overuse*100, stats.HeapAlloc, stats.NextGC, stats.HeapSys, stats.HeapReleased, len(saved)) runtime.KeepAlive(saved) + runtime.KeepAlive(condemned) } // Test that defer closure is correctly scanned when the stack is scanned. @@ -263,9 +271,9 @@ func DeferLiveness() { } //go:noinline -func escape(x interface{}) { sink2 = x; sink2 = nil } +func escape(x any) { sink2 = x; sink2 = nil } -var sink2 interface{} +var sink2 any // Test zombie object detection and reporting. func GCZombie() { @@ -300,3 +308,113 @@ func GCZombie() { runtime.KeepAlive(keep) runtime.KeepAlive(zombies) } + +func GCMemoryLimit() { + gcMemoryLimit(100) +} + +func GCMemoryLimitNoGCPercent() { + gcMemoryLimit(-1) +} + +// Test SetMemoryLimit functionality. +// +// This test lives here instead of runtime/debug because the entire +// implementation is in the runtime, and testprog gives us a more +// consistent testing environment to help avoid flakiness. +func gcMemoryLimit(gcPercent int) { + if oldProcs := runtime.GOMAXPROCS(4); oldProcs < 4 { + // Fail if the default GOMAXPROCS isn't at least 4. + // Whatever invokes this should check and do a proper t.Skip. + println("insufficient CPUs") + return + } + debug.SetGCPercent(gcPercent) + + const myLimit = 256 << 20 + if limit := debug.SetMemoryLimit(-1); limit != math.MaxInt64 { + print("expected MaxInt64 limit, got ", limit, " bytes instead\n") + return + } + if limit := debug.SetMemoryLimit(myLimit); limit != math.MaxInt64 { + print("expected MaxInt64 limit, got ", limit, " bytes instead\n") + return + } + if limit := debug.SetMemoryLimit(-1); limit != myLimit { + print("expected a ", myLimit, "-byte limit, got ", limit, " bytes instead\n") + return + } + + target := make(chan int64) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + sinkSize := int(<-target / memLimitUnit) + for { + if len(memLimitSink) != sinkSize { + memLimitSink = make([]*[memLimitUnit]byte, sinkSize) + } + for i := 0; i < len(memLimitSink); i++ { + memLimitSink[i] = new([memLimitUnit]byte) + // Write to this memory to slow down the allocator, otherwise + // we get flaky behavior. See #52433. + for j := range memLimitSink[i] { + memLimitSink[i][j] = 9 + } + } + // Again, Gosched to slow down the allocator. + runtime.Gosched() + select { + case newTarget := <-target: + if newTarget == math.MaxInt64 { + return + } + sinkSize = int(newTarget / memLimitUnit) + default: + } + } + }() + var m [2]metrics.Sample + m[0].Name = "/memory/classes/total:bytes" + m[1].Name = "/memory/classes/heap/released:bytes" + + // Don't set this too high, because this is a *live heap* target which + // is not directly comparable to a total memory limit. + maxTarget := int64((myLimit / 10) * 8) + increment := int64((myLimit / 10) * 1) + for i := increment; i < maxTarget; i += increment { + target <- i + + // Check to make sure the memory limit is maintained. + // We're just sampling here so if it transiently goes over we might miss it. + // The internal accounting is inconsistent anyway, so going over by a few + // pages is certainly possible. Just make sure we're within some bound. + // Note that to avoid flakiness due to #52433 (especially since we're allocating + // somewhat heavily here) this bound is kept loose. In practice the Go runtime + // should do considerably better than this bound. + bound := int64(myLimit + 16<<20) + start := time.Now() + for time.Now().Sub(start) < 200*time.Millisecond { + metrics.Read(m[:]) + retained := int64(m[0].Value.Uint64() - m[1].Value.Uint64()) + if retained > bound { + print("retained=", retained, " limit=", myLimit, " bound=", bound, "\n") + panic("exceeded memory limit by more than bound allows") + } + runtime.Gosched() + } + } + + if limit := debug.SetMemoryLimit(math.MaxInt64); limit != myLimit { + print("expected a ", myLimit, "-byte limit, got ", limit, " bytes instead\n") + return + } + println("OK") +} + +// Pick a value close to the page size. We want to m +const memLimitUnit = 8000 + +var memLimitSink []*[memLimitUnit]byte diff --git a/src/runtime/testdata/testprog/numcpu_freebsd.go b/src/runtime/testdata/testprog/numcpu_freebsd.go index aff36ec702b066..7209f67959a0a2 100644 --- a/src/runtime/testdata/testprog/numcpu_freebsd.go +++ b/src/runtime/testdata/testprog/numcpu_freebsd.go @@ -85,19 +85,18 @@ func getList() ([]string, error) { if err != nil { return nil, fmt.Errorf("fail to execute '%s': %s", cmdline, err) } - pos := bytes.IndexRune(output, '\n') - if pos == -1 { + output, _, ok := bytes.Cut(output, []byte("\n")) + if !ok { return nil, fmt.Errorf("invalid output from '%s', '\\n' not found: %s", cmdline, output) } - output = output[0:pos] - pos = bytes.IndexRune(output, ':') - if pos == -1 { + _, cpus, ok := bytes.Cut(output, []byte(":")) + if !ok { return nil, fmt.Errorf("invalid output from '%s', ':' not found: %s", cmdline, output) } var list []string - for _, val := range bytes.Split(output[pos+1:], []byte(",")) { + for _, val := range bytes.Split(cpus, []byte(",")) { index := string(bytes.TrimSpace(val)) if len(index) == 0 { continue diff --git a/src/runtime/testdata/testprog/preempt.go b/src/runtime/testdata/testprog/preempt.go index 1c74d0e435238a..fb6755a37253a9 100644 --- a/src/runtime/testdata/testprog/preempt.go +++ b/src/runtime/testdata/testprog/preempt.go @@ -20,6 +20,10 @@ func AsyncPreempt() { runtime.GOMAXPROCS(1) // Disable GC so we have complete control of what we're testing. debug.SetGCPercent(-1) + // Out of an abundance of caution, also make sure that there are + // no GCs actively in progress. The sweep phase of a GC cycle + // for instance tries to preempt Ps at the very beginning. + runtime.GC() // Start a goroutine with no sync safe-points. var ready, ready2 uint32 diff --git a/src/runtime/testdata/testprog/signal.go b/src/runtime/testdata/testprog/signal.go index 417e105c68a5b9..cc5ac8af58d817 100644 --- a/src/runtime/testdata/testprog/signal.go +++ b/src/runtime/testdata/testprog/signal.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !plan9 // +build !windows,!plan9 package main diff --git a/src/runtime/testdata/testprog/sleep.go b/src/runtime/testdata/testprog/sleep.go index 86e2f6cfe6ce7f..b230e601817939 100644 --- a/src/runtime/testdata/testprog/sleep.go +++ b/src/runtime/testdata/testprog/sleep.go @@ -4,7 +4,10 @@ package main -import "time" +import ( + "os" + "time" +) // for golang.org/issue/27250 @@ -13,5 +16,7 @@ func init() { } func After1() { + os.Stdout.WriteString("ready\n") + os.Stdout.Close() <-time.After(1 * time.Second) } diff --git a/src/runtime/testdata/testprog/syscalls_none.go b/src/runtime/testdata/testprog/syscalls_none.go index 7f8ded3994f18e..068bb59af37d11 100644 --- a/src/runtime/testdata/testprog/syscalls_none.go +++ b/src/runtime/testdata/testprog/syscalls_none.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !linux // +build !linux package main diff --git a/src/runtime/testdata/testprog/traceback_ancestors.go b/src/runtime/testdata/testprog/traceback_ancestors.go index 0ee402c4bdc608..1d0d00bab7d4ae 100644 --- a/src/runtime/testdata/testprog/traceback_ancestors.go +++ b/src/runtime/testdata/testprog/traceback_ancestors.go @@ -33,30 +33,27 @@ func printStack() { for { n := runtime.Stack(buf, true) if n < len(buf) { - tb := string(buf[:n]) + all := string(buf[:n]) + var saved string // Delete any ignored goroutines, if present. - pos := 0 - for pos < len(tb) { - next := pos + strings.Index(tb[pos:], "\n\n") - if next < pos { - next = len(tb) - } else { - next += len("\n\n") - } + for all != "" { + var g string + g, all, _ = strings.Cut(all, "\n\n") - if strings.HasPrefix(tb[pos:], "goroutine ") { - id := tb[pos+len("goroutine "):] - id = id[:strings.IndexByte(id, ' ')] + if strings.HasPrefix(g, "goroutine ") { + id, _, _ := strings.Cut(strings.TrimPrefix(g, "goroutine "), " ") if ignoreGoroutines[id] { - tb = tb[:pos] + tb[next:] - next = pos + continue } } - pos = next + if saved != "" { + saved += "\n\n" + } + saved += g } - fmt.Print(tb) + fmt.Print(saved) return } buf = make([]byte, 2*len(buf)) @@ -89,11 +86,10 @@ func recurseThenCallGo(w chan struct{}, frames int, goroutines int, main bool) { func goroutineID() string { buf := make([]byte, 128) runtime.Stack(buf, false) - const prefix = "goroutine " - if !bytes.HasPrefix(buf, []byte(prefix)) { + prefix := []byte("goroutine ") + if !bytes.HasPrefix(buf, prefix) { panic(fmt.Sprintf("expected %q at beginning of traceback:\n%s", prefix, buf)) } - buf = buf[len(prefix):] - n := bytes.IndexByte(buf, ' ') - return string(buf[:n]) + id, _, _ := bytes.Cut(bytes.TrimPrefix(buf, prefix), []byte(" ")) + return string(id) } diff --git a/src/runtime/testdata/testprog/unsafe.go b/src/runtime/testdata/testprog/unsafe.go new file mode 100644 index 00000000000000..021b08fa528f5f --- /dev/null +++ b/src/runtime/testdata/testprog/unsafe.go @@ -0,0 +1,12 @@ +package main + +import "unsafe" + +func init() { + register("panicOnNilAndEleSizeIsZero", panicOnNilAndEleSizeIsZero) +} + +func panicOnNilAndEleSizeIsZero() { + var p *struct{} + _ = unsafe.Slice(p, 5) +} diff --git a/src/runtime/testdata/testprog/vdso.go b/src/runtime/testdata/testprog/vdso.go index d2a300d8f2004c..b18bc74a064e23 100644 --- a/src/runtime/testdata/testprog/vdso.go +++ b/src/runtime/testdata/testprog/vdso.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. -// Invoke signal hander in the VDSO context (see issue 32912). +// Invoke signal handler in the VDSO context (see issue 32912). package main diff --git a/src/runtime/testdata/testprogcgo/aprof.go b/src/runtime/testdata/testprogcgo/aprof.go index 44a15b08650377..16870144dda014 100644 --- a/src/runtime/testdata/testprogcgo/aprof.go +++ b/src/runtime/testdata/testprogcgo/aprof.go @@ -7,8 +7,11 @@ package main // Test that SIGPROF received in C code does not crash the process // looking for the C code's func pointer. -// The test fails when the function is the first C function. -// The exported functions are the first C functions, so we use that. +// This is a regression test for issue 14599, where profiling fails when the +// function is the first C function. Exported functions are the first C +// functions, so we use an exported function. Exported functions are created in +// lexicographical order of source files, so this file is named aprof.go to +// ensure its function is first. // extern void CallGoNop(); import "C" diff --git a/src/runtime/testdata/testprogcgo/callback.go b/src/runtime/testdata/testprogcgo/callback.go index be0409f39d2412..25f07159b86c92 100644 --- a/src/runtime/testdata/testprogcgo/callback.go +++ b/src/runtime/testdata/testprogcgo/callback.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main @@ -65,7 +66,7 @@ func grow1(x, sum *int) int { func CgoCallbackGC() { P := 100 - if os.Getenv("RUNTIME_TESTING_SHORT") != "" { + if os.Getenv("RUNTIME_TEST_SHORT") != "" { P = 10 } done := make(chan bool) diff --git a/src/runtime/testdata/testprogcgo/catchpanic.go b/src/runtime/testdata/testprogcgo/catchpanic.go index 55a606d1bc854d..c722d40a196a9f 100644 --- a/src/runtime/testdata/testprogcgo/catchpanic.go +++ b/src/runtime/testdata/testprogcgo/catchpanic.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/dropm.go b/src/runtime/testdata/testprogcgo/dropm.go index 9e782f504fe12b..700b7fa0c23e58 100644 --- a/src/runtime/testdata/testprogcgo/dropm.go +++ b/src/runtime/testdata/testprogcgo/dropm.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows // Test that a sequence of callbacks from C to Go get the same m. diff --git a/src/runtime/testdata/testprogcgo/dropm_stub.go b/src/runtime/testdata/testprogcgo/dropm_stub.go index f7f142c1fdd5e9..6997cfd3fa8c5f 100644 --- a/src/runtime/testdata/testprogcgo/dropm_stub.go +++ b/src/runtime/testdata/testprogcgo/dropm_stub.go @@ -7,5 +7,6 @@ package main import _ "unsafe" // for go:linkname // Defined in the runtime package. +// //go:linkname runtime_getm_for_test runtime.getm func runtime_getm_for_test() uintptr diff --git a/src/runtime/testdata/testprogcgo/eintr.go b/src/runtime/testdata/testprogcgo/eintr.go index 1722a75eb9a49c..6e9677f988bb5d 100644 --- a/src/runtime/testdata/testprogcgo/eintr.go +++ b/src/runtime/testdata/testprogcgo/eintr.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main @@ -69,6 +70,7 @@ func EINTR() { // spin does CPU bound spinning and allocating for a millisecond, // to get a SIGURG. +// //go:noinline func spin() (float64, []byte) { stop := time.Now().Add(time.Millisecond) diff --git a/src/runtime/testdata/testprogcgo/exec.go b/src/runtime/testdata/testprogcgo/exec.go index 15723c73695bdf..c268bcd9b281e9 100644 --- a/src/runtime/testdata/testprogcgo/exec.go +++ b/src/runtime/testdata/testprogcgo/exec.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/gprof.go b/src/runtime/testdata/testprogcgo/gprof.go new file mode 100644 index 00000000000000..d453b4d0ce330a --- /dev/null +++ b/src/runtime/testdata/testprogcgo/gprof.go @@ -0,0 +1,46 @@ +// Copyright 2021 The Go Authors. 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 + +// Test taking a goroutine profile with C traceback. + +/* +// Defined in gprof_c.c. +void CallGoSleep(void); +void gprofCgoTraceback(void* parg); +void gprofCgoContext(void* parg); +*/ +import "C" + +import ( + "fmt" + "io" + "runtime" + "runtime/pprof" + "time" + "unsafe" +) + +func init() { + register("GoroutineProfile", GoroutineProfile) +} + +func GoroutineProfile() { + runtime.SetCgoTraceback(0, unsafe.Pointer(C.gprofCgoTraceback), unsafe.Pointer(C.gprofCgoContext), nil) + + go C.CallGoSleep() + go C.CallGoSleep() + go C.CallGoSleep() + time.Sleep(1 * time.Second) + + prof := pprof.Lookup("goroutine") + prof.WriteTo(io.Discard, 1) + fmt.Println("OK") +} + +//export GoSleep +func GoSleep() { + time.Sleep(time.Hour) +} diff --git a/src/runtime/testdata/testprogcgo/gprof_c.c b/src/runtime/testdata/testprogcgo/gprof_c.c new file mode 100644 index 00000000000000..5c7cd770221133 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/gprof_c.c @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The C definitions for gprof.go. That file uses //export so +// it can't put function definitions in the "C" import comment. + +#include +#include + +// Functions exported from Go. +extern void GoSleep(); + +struct cgoContextArg { + uintptr_t context; +}; + +void gprofCgoContext(void *arg) { + ((struct cgoContextArg*)arg)->context = 1; +} + +void gprofCgoTraceback(void *arg) { + // spend some time here so the P is more likely to be retaken. + volatile int i; + for (i = 0; i < 123456789; i++); +} + +void CallGoSleep() { + GoSleep(); +} diff --git a/src/runtime/testdata/testprogcgo/lockosthread.go b/src/runtime/testdata/testprogcgo/lockosthread.go index 36423d9eb0cf31..8fcea35f524a86 100644 --- a/src/runtime/testdata/testprogcgo/lockosthread.go +++ b/src/runtime/testdata/testprogcgo/lockosthread.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/needmdeadlock.go b/src/runtime/testdata/testprogcgo/needmdeadlock.go index 5a9c359006d7af..b95ec7746895b2 100644 --- a/src/runtime/testdata/testprogcgo/needmdeadlock.go +++ b/src/runtime/testdata/testprogcgo/needmdeadlock.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/numgoroutine.go b/src/runtime/testdata/testprogcgo/numgoroutine.go index 5bdfe52ed41b4d..1b9f202f46c827 100644 --- a/src/runtime/testdata/testprogcgo/numgoroutine.go +++ b/src/runtime/testdata/testprogcgo/numgoroutine.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/panic.c b/src/runtime/testdata/testprogcgo/panic.c new file mode 100644 index 00000000000000..deb5ed5a6054f8 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/panic.c @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern void panic_callback(); + +void call_callback(void) { + panic_callback(); +} diff --git a/src/runtime/testdata/testprogcgo/panic.go b/src/runtime/testdata/testprogcgo/panic.go new file mode 100644 index 00000000000000..57ac89540ec015 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/panic.go @@ -0,0 +1,23 @@ +package main + +// This program will crash. +// We want to test unwinding from a cgo callback. + +/* +void call_callback(void); +*/ +import "C" + +func init() { + register("PanicCallback", PanicCallback) +} + +//export panic_callback +func panic_callback() { + var i *int + *i = 42 +} + +func PanicCallback() { + C.call_callback() +} diff --git a/src/runtime/testdata/testprogcgo/pprof.go b/src/runtime/testdata/testprogcgo/pprof.go index 3b73fa0bdd9d8a..8870d0c4157451 100644 --- a/src/runtime/testdata/testprogcgo/pprof.go +++ b/src/runtime/testdata/testprogcgo/pprof.go @@ -29,8 +29,6 @@ void cpuHog() { void cpuHog2() { } -static int cpuHogCount; - struct cgoTracebackArg { uintptr_t context; uintptr_t sigContext; @@ -47,13 +45,6 @@ void pprofCgoTraceback(void* parg) { arg->buf[0] = (uintptr_t)(cpuHog) + 0x10; arg->buf[1] = (uintptr_t)(cpuHog2) + 0x4; arg->buf[2] = 0; - ++cpuHogCount; -} - -// getCpuHogCount fetches the number of times we've seen cpuHog in the -// traceback. -int getCpuHogCount() { - return cpuHogCount; } */ import "C" @@ -86,7 +77,7 @@ func CgoPprof() { } t0 := time.Now() - for C.getCpuHogCount() < 2 && time.Since(t0) < time.Second { + for time.Since(t0) < time.Second { C.cpuHog() } diff --git a/src/runtime/testdata/testprogcgo/pprof_callback.go b/src/runtime/testdata/testprogcgo/pprof_callback.go new file mode 100644 index 00000000000000..fd87eb87dd260e --- /dev/null +++ b/src/runtime/testdata/testprogcgo/pprof_callback.go @@ -0,0 +1,89 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !plan9 && !windows + +package main + +// Make many C-to-Go callback while collecting a CPU profile. +// +// This is a regression test for issue 50936. + +/* +#include + +void goCallbackPprof(); + +static void callGo() { + // Spent >20us in C so this thread is eligible for sysmon to retake its + // P. + usleep(50); + goCallbackPprof(); +} +*/ +import "C" + +import ( + "fmt" + "os" + "runtime" + "runtime/pprof" + "time" +) + +func init() { + register("CgoPprofCallback", CgoPprofCallback) +} + +//export goCallbackPprof +func goCallbackPprof() { + // No-op. We want to stress the cgocall and cgocallback internals, + // landing as many pprof signals there as possible. +} + +func CgoPprofCallback() { + // Issue 50936 was a crash in the SIGPROF handler when the signal + // arrived during the exitsyscall following a cgocall(back) in dropg or + // execute, when updating mp.curg. + // + // These are reachable only when exitsyscall finds no P available. Thus + // we make C calls from significantly more Gs than there are available + // Ps. Lots of runnable work combined with >20us spent in callGo makes + // it possible for sysmon to retake Ps, forcing C calls to go down the + // desired exitsyscall path. + // + // High GOMAXPROCS is used to increase opportunities for failure on + // high CPU machines. + const ( + P = 16 + G = 64 + ) + runtime.GOMAXPROCS(P) + + f, err := os.CreateTemp("", "prof") + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + defer f.Close() + + if err := pprof.StartCPUProfile(f); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + + for i := 0; i < G; i++ { + go func() { + for { + C.callGo() + } + }() + } + + time.Sleep(time.Second) + + pprof.StopCPUProfile() + + fmt.Println("OK") +} diff --git a/src/runtime/testdata/testprogcgo/raceprof.go b/src/runtime/testdata/testprogcgo/raceprof.go index f7ca629789be7f..c098e16196d1ed 100644 --- a/src/runtime/testdata/testprogcgo/raceprof.go +++ b/src/runtime/testdata/testprogcgo/raceprof.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build (linux && amd64) || (freebsd && amd64) // +build linux,amd64 freebsd,amd64 package main diff --git a/src/runtime/testdata/testprogcgo/racesig.go b/src/runtime/testdata/testprogcgo/racesig.go index a079b3fd1a531e..93526797146bec 100644 --- a/src/runtime/testdata/testprogcgo/racesig.go +++ b/src/runtime/testdata/testprogcgo/racesig.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build (linux && amd64) || (freebsd && amd64) // +build linux,amd64 freebsd,amd64 package main diff --git a/src/runtime/testdata/testprogcgo/segv.go b/src/runtime/testdata/testprogcgo/segv.go index 3237a8c69c6f36..0632475228d9b0 100644 --- a/src/runtime/testdata/testprogcgo/segv.go +++ b/src/runtime/testdata/testprogcgo/segv.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main diff --git a/src/runtime/testdata/testprogcgo/sigstack.go b/src/runtime/testdata/testprogcgo/sigstack.go index 21b668d6c00965..12ca661033b36f 100644 --- a/src/runtime/testdata/testprogcgo/sigstack.go +++ b/src/runtime/testdata/testprogcgo/sigstack.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows // Test handling of Go-allocated signal stacks when calling from diff --git a/src/runtime/testdata/testprogcgo/sigthrow.go b/src/runtime/testdata/testprogcgo/sigthrow.go new file mode 100644 index 00000000000000..665e3b02df191a --- /dev/null +++ b/src/runtime/testdata/testprogcgo/sigthrow.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program will abort. + +/* +#include +*/ +import "C" + +func init() { + register("Abort", Abort) +} + +func Abort() { + C.abort() +} diff --git a/src/runtime/testdata/testprogcgo/threadpanic.go b/src/runtime/testdata/testprogcgo/threadpanic.go index f9b48a9026da4c..2d24fe68eaa368 100644 --- a/src/runtime/testdata/testprogcgo/threadpanic.go +++ b/src/runtime/testdata/testprogcgo/threadpanic.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 // +build !plan9 package main diff --git a/src/runtime/testdata/testprogcgo/threadpprof.go b/src/runtime/testdata/testprogcgo/threadpprof.go index feb774ba59a91c..70717e0099afc3 100644 --- a/src/runtime/testdata/testprogcgo/threadpprof.go +++ b/src/runtime/testdata/testprogcgo/threadpprof.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !plan9 && !windows // +build !plan9,!windows package main @@ -16,6 +17,8 @@ package main int threadSalt1; int threadSalt2; +static pthread_t tid; + void cpuHogThread() { int foo = threadSalt1; int i; @@ -33,8 +36,6 @@ void cpuHogThread() { void cpuHogThread2() { } -static int cpuHogThreadCount; - struct cgoTracebackArg { uintptr_t context; uintptr_t sigContext; @@ -43,19 +44,16 @@ struct cgoTracebackArg { }; // pprofCgoThreadTraceback is passed to runtime.SetCgoTraceback. -// For testing purposes it pretends that all CPU hits in C code are in cpuHog. +// For testing purposes it pretends that all CPU hits on the cpuHog +// C thread are in cpuHog. void pprofCgoThreadTraceback(void* parg) { struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg); - arg->buf[0] = (uintptr_t)(cpuHogThread) + 0x10; - arg->buf[1] = (uintptr_t)(cpuHogThread2) + 0x4; - arg->buf[2] = 0; - __sync_add_and_fetch(&cpuHogThreadCount, 1); -} - -// getCPUHogThreadCount fetches the number of times we've seen cpuHogThread -// in the traceback. -int getCPUHogThreadCount() { - return __sync_add_and_fetch(&cpuHogThreadCount, 0); + if (pthread_self() == tid) { + arg->buf[0] = (uintptr_t)(cpuHogThread) + 0x10; + arg->buf[1] = (uintptr_t)(cpuHogThread2) + 0x4; + arg->buf[2] = 0; + } else + arg->buf[0] = 0; } static void* cpuHogDriver(void* arg __attribute__ ((unused))) { @@ -66,13 +64,13 @@ static void* cpuHogDriver(void* arg __attribute__ ((unused))) { } void runCPUHogThread(void) { - pthread_t tid; pthread_create(&tid, 0, cpuHogDriver, 0); } */ import "C" import ( + "context" "fmt" "os" "runtime" @@ -107,12 +105,16 @@ func pprofThread() { os.Exit(2) } - C.runCPUHogThread() + // This goroutine may receive a profiling signal while creating the C-owned + // thread. If it does, the SetCgoTraceback handler will make the leaf end of + // the stack look almost (but not exactly) like the stacks the test case is + // trying to find. Attach a profiler label so the test can filter out those + // confusing samples. + pprof.Do(context.Background(), pprof.Labels("ignore", "ignore"), func(ctx context.Context) { + C.runCPUHogThread() + }) - t0 := time.Now() - for C.getCPUHogThreadCount() < 2 && time.Since(t0) < time.Second { - time.Sleep(100 * time.Millisecond) - } + time.Sleep(1 * time.Second) pprof.StopCPUProfile() diff --git a/src/runtime/testdata/testprogcgo/threadprof.go b/src/runtime/testdata/testprogcgo/threadprof.go index 2d4c1039fb737f..d62d4b4be839f7 100644 --- a/src/runtime/testdata/testprogcgo/threadprof.go +++ b/src/runtime/testdata/testprogcgo/threadprof.go @@ -2,21 +2,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// We only build this file with the tag "threadprof", since it starts -// a thread running a busy loop at constructor time. - +//go:build !plan9 && !windows // +build !plan9,!windows -// +build threadprof package main /* #include +#include #include #include volatile int32_t spinlock; +// Note that this thread is only started if GO_START_SIGPROF_THREAD +// is set in the environment, which is only done when running the +// CgoExternalThreadSIGPROF test. static void *thread1(void *p) { (void)p; while (spinlock == 0) @@ -26,9 +27,13 @@ static void *thread1(void *p) { return NULL; } +// This constructor function is run when the program starts. +// It is used for the CgoExternalThreadSIGPROF test. __attribute__((constructor)) void issue9456() { - pthread_t tid; - pthread_create(&tid, 0, thread1, NULL); + if (getenv("GO_START_SIGPROF_THREAD") != NULL) { + pthread_t tid; + pthread_create(&tid, 0, thread1, NULL); + } } void **nullptr; diff --git a/src/runtime/testdata/testprogcgo/windows/win.go b/src/runtime/testdata/testprogcgo/windows/win.go index 12488aa6588b08..9d9f86c9beacb5 100644 --- a/src/runtime/testdata/testprogcgo/windows/win.go +++ b/src/runtime/testdata/testprogcgo/windows/win.go @@ -1,8 +1,6 @@ package windows /* -#cgo amd64 386 CFLAGS: -mnop-fun-dllimport - #include DWORD agetthread() { diff --git a/src/runtime/testdata/testprognet/signal.go b/src/runtime/testdata/testprognet/signal.go index 4d2de79d97c033..dfa2e10e7ab935 100644 --- a/src/runtime/testdata/testprognet/signal.go +++ b/src/runtime/testdata/testprognet/signal.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !plan9 // +build !windows,!plan9 // This is in testprognet instead of testprog because testprog diff --git a/src/runtime/testdata/testprognet/signalexec.go b/src/runtime/testdata/testprognet/signalexec.go index 4a988ef6c1da24..62ebce7176f220 100644 --- a/src/runtime/testdata/testprognet/signalexec.go +++ b/src/runtime/testdata/testprognet/signalexec.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd // +build darwin dragonfly freebsd linux netbsd openbsd // This is in testprognet instead of testprog because testprog diff --git a/src/runtime/testdata/testwinlib/main.c b/src/runtime/testdata/testwinlib/main.c index e84a32f7539131..c3fe3cb0712a40 100644 --- a/src/runtime/testdata/testwinlib/main.c +++ b/src/runtime/testdata/testwinlib/main.c @@ -41,17 +41,20 @@ int main() if (NULL == exceptionHandlerHandle) { printf("cannot add vectored exception handler\n"); + fflush(stdout); return 2; } void *continueHandlerHandle = AddVectoredContinueHandler(0, customContinueHandlder); if (NULL == continueHandlerHandle) { printf("cannot add vectored continue handler\n"); + fflush(stdout); return 2; } CallMeBack(throwFromC); RemoveVectoredContinueHandler(continueHandlerHandle); RemoveVectoredExceptionHandler(exceptionHandlerHandle); printf("exceptionCount: %d\ncontinueCount: %d\n", exceptionCount, continueCount); + fflush(stdout); return 0; -} \ No newline at end of file +} diff --git a/src/runtime/testdata/testwinlib/main.go b/src/runtime/testdata/testwinlib/main.go index 400eaa1c82dfa8..407331bb8323d4 100644 --- a/src/runtime/testdata/testwinlib/main.go +++ b/src/runtime/testdata/testwinlib/main.go @@ -1,3 +1,4 @@ +//go:build windows && cgo // +build windows,cgo package main @@ -10,6 +11,7 @@ package main import "C" // CallMeBack call backs C code. +// //export CallMeBack func CallMeBack(callback C.callmeBackFunc) { C.bridgeCallback(callback) @@ -20,6 +22,7 @@ func CallMeBack(callback C.callmeBackFunc) { // validate that it does not crash the program before another handler could take an action. // The idea here is to reproduce what happens when you attach a debugger to a running program. // It also simulate the behavior of the .Net debugger, which register its exception/continue handlers lazily. +// //export Dummy func Dummy() int { return 42 diff --git a/src/runtime/testdata/testwinsignal/main.go b/src/runtime/testdata/testwinsignal/main.go index 1e7c9475fd6631..e1136f3887929b 100644 --- a/src/runtime/testdata/testwinsignal/main.go +++ b/src/runtime/testdata/testwinsignal/main.go @@ -2,18 +2,52 @@ package main import ( "fmt" + "io" + "log" "os" "os/signal" + "syscall" "time" ) func main() { + // Ensure that this process terminates when the test times out, + // even if the expected signal never arrives. + go func() { + io.Copy(io.Discard, os.Stdin) + log.Fatal("stdin is closed; terminating") + }() + + // Register to receive all signals. c := make(chan os.Signal, 1) signal.Notify(c) - fmt.Println("ready") + // Get console window handle. + kernel32 := syscall.NewLazyDLL("kernel32.dll") + getConsoleWindow := kernel32.NewProc("GetConsoleWindow") + hwnd, _, err := getConsoleWindow.Call() + if hwnd == 0 { + log.Fatal("no associated console: ", err) + } + + // Send message to close the console window. + const _WM_CLOSE = 0x0010 + user32 := syscall.NewLazyDLL("user32.dll") + postMessage := user32.NewProc("PostMessageW") + ok, _, err := postMessage.Call(hwnd, _WM_CLOSE, 0, 0) + if ok == 0 { + log.Fatal("post message failed: ", err) + } + sig := <-c + // Allow some time for the handler to complete if it's going to. + // + // (In https://go.dev/issue/41884 the handler returned immediately, + // which caused Windows to terminate the program before the goroutine + // that received the SIGTERM had a chance to actually clean up.) time.Sleep(time.Second) + + // Print the signal's name: "terminated" makes the test succeed. fmt.Println(sig) } diff --git a/src/runtime/time.go b/src/runtime/time.go index 666b2423164162..945756109a1752 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -7,6 +7,7 @@ package runtime import ( + "internal/abi" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -27,8 +28,8 @@ type timer struct { // when must be positive on an active timer. when int64 period int64 - f func(interface{}, uintptr) - arg interface{} + f func(any, uintptr) + arg any seq uintptr // What to set the when field to in timerModifiedXX status. @@ -172,6 +173,7 @@ const verifyTimers = false // time.now is implemented in assembly. // timeSleep puts the current goroutine to sleep for at least ns nanoseconds. +// //go:linkname timeSleep time.Sleep func timeSleep(ns int64) { if ns <= 0 { @@ -204,6 +206,7 @@ func resetForSleep(gp *g, ut unsafe.Pointer) bool { } // startTimer adds t to the timer heap. +// //go:linkname startTimer time.startTimer func startTimer(t *timer) { if raceenabled { @@ -214,14 +217,17 @@ func startTimer(t *timer) { // stopTimer stops a timer. // It reports whether t was stopped before being run. +// //go:linkname stopTimer time.stopTimer func stopTimer(t *timer) bool { return deltimer(t) } // resetTimer resets an inactive timer, adding it to the heap. -//go:linkname resetTimer time.resetTimer +// // Reports whether the timer was modified before it was run. +// +//go:linkname resetTimer time.resetTimer func resetTimer(t *timer, when int64) bool { if raceenabled { racerelease(unsafe.Pointer(t)) @@ -230,15 +236,16 @@ func resetTimer(t *timer, when int64) bool { } // modTimer modifies an existing timer. +// //go:linkname modTimer time.modTimer -func modTimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { +func modTimer(t *timer, when, period int64, f func(any, uintptr), arg any, seq uintptr) { modtimer(t, when, period, f, arg, seq) } // Go runtime. // Ready the goroutine arg. -func goroutineReady(arg interface{}, seq uintptr) { +func goroutineReady(arg any, seq uintptr) { goready(arg.(*g), 0) } @@ -282,7 +289,7 @@ func addtimer(t *timer) { func doaddtimer(pp *p, t *timer) { // Timers rely on the network poller, so make sure the poller // has started. - if netpollInited == 0 { + if netpollInited.Load() == 0 { netpollGenericInit() } @@ -294,9 +301,9 @@ func doaddtimer(pp *p, t *timer) { pp.timers = append(pp.timers, t) siftupTimer(pp.timers, i) if t == pp.timers[0] { - atomic.Store64(&pp.timer0When, uint64(t.when)) + pp.timer0When.Store(t.when) } - atomic.Xadd(&pp.numTimers, 1) + pp.numTimers.Add(1) } // deltimer deletes the timer t. It may be on some other P, so we can't @@ -319,7 +326,7 @@ func deltimer(t *timer) bool { badTimer() } releasem(mp) - atomic.Xadd(&tpp.deletedTimers, 1) + tpp.deletedTimers.Add(1) // Timer was not yet run. return true } else { @@ -337,7 +344,7 @@ func deltimer(t *timer) bool { badTimer() } releasem(mp) - atomic.Xadd(&tpp.deletedTimers, 1) + tpp.deletedTimers.Add(1) // Timer was not yet run. return true } else { @@ -366,9 +373,9 @@ func deltimer(t *timer) bool { // dodeltimer removes timer i from the current P's heap. // We are locked on the P when this is called. -// It reports whether it saw no problems due to races. +// It returns the smallest changed index in pp.timers. // The caller must have locked the timers for pp. -func dodeltimer(pp *p, i int) { +func dodeltimer(pp *p, i int) int { if t := pp.timers[i]; t.pp.ptr() != pp { throw("dodeltimer: wrong P") } else { @@ -380,16 +387,22 @@ func dodeltimer(pp *p, i int) { } pp.timers[last] = nil pp.timers = pp.timers[:last] + smallestChanged := i if i != last { // Moving to i may have moved the last timer to a new parent, // so sift up to preserve the heap guarantee. - siftupTimer(pp.timers, i) + smallestChanged = siftupTimer(pp.timers, i) siftdownTimer(pp.timers, i) } if i == 0 { updateTimer0When(pp) } - atomic.Xadd(&pp.numTimers, -1) + n := pp.numTimers.Add(-1) + if n == 0 { + // If there are no timers, then clearly none are modified. + pp.timerModifiedEarliest.Store(0) + } + return smallestChanged } // dodeltimer0 removes timer 0 from the current P's heap. @@ -412,13 +425,17 @@ func dodeltimer0(pp *p) { siftdownTimer(pp.timers, 0) } updateTimer0When(pp) - atomic.Xadd(&pp.numTimers, -1) + n := pp.numTimers.Add(-1) + if n == 0 { + // If there are no timers, then clearly none are modified. + pp.timerModifiedEarliest.Store(0) + } } // modtimer modifies an existing timer. // This is called by the netpoll code or time.Ticker.Reset or time.Timer.Reset. // Reports whether the timer was modified before it was run. -func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) bool { +func modtimer(t *timer, when, period int64, f func(any, uintptr), arg any, seq uintptr) bool { if when <= 0 { throw("timer when must be positive") } @@ -460,7 +477,7 @@ loop: // This could lead to a self-deadlock. See #38070. mp = acquirem() if atomic.Cas(&t.status, status, timerModifying) { - atomic.Xadd(&t.pp.ptr().deletedTimers, -1) + t.pp.ptr().deletedTimers.Add(-1) pending = false // timer already stopped break loop } @@ -569,7 +586,7 @@ func cleantimers(pp *p) { if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { badTimer() } - atomic.Xadd(&pp.deletedTimers, -1) + pp.deletedTimers.Add(-1) case timerModifiedEarlier, timerModifiedLater: if !atomic.Cas(&t.status, s, timerMoving) { continue @@ -654,8 +671,8 @@ func adjusttimers(pp *p, now int64) { // a lot of timers back and forth if the timers rarely expire. // We'll postpone looking through all the adjusted timers until // one would actually expire. - first := atomic.Load64(&pp.timerModifiedEarliest) - if first == 0 || int64(first) > now { + first := pp.timerModifiedEarliest.Load() + if first == 0 || first > now { if verifyTimers { verifyTimerHeap(pp) } @@ -663,7 +680,7 @@ func adjusttimers(pp *p, now int64) { } // We are going to clear all timerModifiedEarlier timers. - atomic.Store64(&pp.timerModifiedEarliest, 0) + pp.timerModifiedEarliest.Store(0) var moved []*timer for i := 0; i < len(pp.timers); i++ { @@ -674,13 +691,14 @@ func adjusttimers(pp *p, now int64) { switch s := atomic.Load(&t.status); s { case timerDeleted: if atomic.Cas(&t.status, s, timerRemoving) { - dodeltimer(pp, i) + changed := dodeltimer(pp, i) if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { badTimer() } - atomic.Xadd(&pp.deletedTimers, -1) - // Look at this heap position again. - i-- + pp.deletedTimers.Add(-1) + // Go back to the earliest changed heap entry. + // "- 1" because the loop will add 1. + i = changed - 1 } case timerModifiedEarlier, timerModifiedLater: if atomic.Cas(&t.status, s, timerMoving) { @@ -690,10 +708,11 @@ func adjusttimers(pp *p, now int64) { // We don't add it back yet because the // heap manipulation could cause our // loop to skip some other timer. - dodeltimer(pp, i) + changed := dodeltimer(pp, i) moved = append(moved, t) - // Look at this heap position again. - i-- + // Go back to the earliest changed heap entry. + // "- 1" because the loop will add 1. + i = changed - 1 } case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving: badTimer() @@ -732,10 +751,11 @@ func addAdjustedTimers(pp *p, moved []*timer) { // should wake up the netpoller. It returns 0 if there are no timers. // This function is invoked when dropping a P, and must run without // any write barriers. +// //go:nowritebarrierrec func nobarrierWakeTime(pp *p) int64 { - next := int64(atomic.Load64(&pp.timer0When)) - nextAdj := int64(atomic.Load64(&pp.timerModifiedEarliest)) + next := pp.timer0When.Load() + nextAdj := pp.timerModifiedEarliest.Load() if next == 0 || (nextAdj != 0 && nextAdj < next) { next = nextAdj } @@ -748,6 +768,7 @@ func nobarrierWakeTime(pp *p) int64 { // when the first timer should run. // The caller must have locked the timers for pp. // If a timer is run, this will temporarily unlock the timers. +// //go:systemstack func runtimer(pp *p, now int64) int64 { for { @@ -778,7 +799,7 @@ func runtimer(pp *p, now int64) int64 { if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { badTimer() } - atomic.Xadd(&pp.deletedTimers, -1) + pp.deletedTimers.Add(-1) if len(pp.timers) == 0 { return -1 } @@ -814,12 +835,13 @@ func runtimer(pp *p, now int64) int64 { // runOneTimer runs a single timer. // The caller must have locked the timers for pp. // This will temporarily unlock the timers while running the timer function. +// //go:systemstack func runOneTimer(pp *p, t *timer, now int64) { if raceenabled { ppcur := getg().m.p.ptr() if ppcur.timerRaceCtx == 0 { - ppcur.timerRaceCtx = racegostart(funcPC(runtimer) + sys.PCQuantum) + ppcur.timerRaceCtx = racegostart(abi.FuncPCABIInternal(runtimer) + sys.PCQuantum) } raceacquirectx(ppcur.timerRaceCtx, unsafe.Pointer(t)) } @@ -881,7 +903,7 @@ func runOneTimer(pp *p, t *timer, now int64) { func clearDeletedTimers(pp *p) { // We are going to clear all timerModifiedEarlier timers. // Do this now in case new ones show up while we are looping. - atomic.Store64(&pp.timerModifiedEarliest, 0) + pp.timerModifiedEarliest.Store(0) cdel := int32(0) to := 0 @@ -942,8 +964,8 @@ nextTimer: timers[i] = nil } - atomic.Xadd(&pp.deletedTimers, -cdel) - atomic.Xadd(&pp.numTimers, -cdel) + pp.deletedTimers.Add(-cdel) + pp.numTimers.Add(-cdel) timers = timers[:to] pp.timers = timers @@ -971,7 +993,7 @@ func verifyTimerHeap(pp *p) { throw("bad timer heap") } } - if numTimers := int(atomic.Load(&pp.numTimers)); len(pp.timers) != numTimers { + if numTimers := int(pp.numTimers.Load()); len(pp.timers) != numTimers { println("timer heap len", len(pp.timers), "!= numTimers", numTimers) throw("bad timer heap len") } @@ -981,9 +1003,9 @@ func verifyTimerHeap(pp *p) { // The caller must have locked the timers for pp. func updateTimer0When(pp *p) { if len(pp.timers) == 0 { - atomic.Store64(&pp.timer0When, 0) + pp.timer0When.Store(0) } else { - atomic.Store64(&pp.timer0When, uint64(pp.timers[0].when)) + pp.timer0When.Store(pp.timers[0].when) } } @@ -992,22 +1014,22 @@ func updateTimer0When(pp *p) { // The timers for pp will not be locked. func updateTimerModifiedEarliest(pp *p, nextwhen int64) { for { - old := atomic.Load64(&pp.timerModifiedEarliest) + old := pp.timerModifiedEarliest.Load() if old != 0 && int64(old) < nextwhen { return } - if atomic.Cas64(&pp.timerModifiedEarliest, old, uint64(nextwhen)) { + + if pp.timerModifiedEarliest.CompareAndSwap(old, nextwhen) { return } } } -// timeSleepUntil returns the time when the next timer should fire, -// and the P that holds the timer heap that that timer is on. +// timeSleepUntil returns the time when the next timer should fire. Returns +// maxWhen if there are no timers. // This is only called by sysmon and checkdead. -func timeSleepUntil() (int64, *p) { +func timeSleepUntil() int64 { next := int64(maxWhen) - var pret *p // Prevent allp slice changes. This is like retake. lock(&allpLock) @@ -1018,21 +1040,19 @@ func timeSleepUntil() (int64, *p) { continue } - w := int64(atomic.Load64(&pp.timer0When)) + w := pp.timer0When.Load() if w != 0 && w < next { next = w - pret = pp } - w = int64(atomic.Load64(&pp.timerModifiedEarliest)) + w = pp.timerModifiedEarliest.Load() if w != 0 && w < next { next = w - pret = pp } } unlock(&allpLock) - return next, pret + return next } // Heap maintenance algorithms. @@ -1043,7 +1063,10 @@ func timeSleepUntil() (int64, *p) { // "panic holding locks" message. Instead, we panic while not // holding a lock. -func siftupTimer(t []*timer, i int) { +// siftupTimer puts the timer at position i in the right place +// in the heap by moving it up toward the top of the heap. +// It returns the smallest changed index. +func siftupTimer(t []*timer, i int) int { if i >= len(t) { badTimer() } @@ -1063,8 +1086,11 @@ func siftupTimer(t []*timer, i int) { if tmp != t[i] { t[i] = tmp } + return i } +// siftdownTimer puts the timer at position i in the right place +// in the heap by moving it down toward the bottom of the heap. func siftdownTimer(t []*timer, i int) { n := len(t) if i >= n { diff --git a/src/runtime/time_fake.go b/src/runtime/time_fake.go index c790faba3d6b75..9e24f7093105ba 100644 --- a/src/runtime/time_fake.go +++ b/src/runtime/time_fake.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build faketime && !windows -// +build faketime,!windows // Faketime isn't currently supported on Windows. This would require // modifying syscall.Write to call syscall.faketimeWrite, @@ -42,6 +41,11 @@ func time_now() (sec int64, nsec int32, mono int64) { return faketime / 1e9, int32(faketime % 1e9), faketime } +// write is like the Unix write system call. +// We have to avoid write barriers to avoid potential deadlock +// on write calls. +// +//go:nowritebarrierrec func write(fd uintptr, p unsafe.Pointer, n int32) int32 { if !(fd == 1 || fd == 2) { // Do an ordinary write. diff --git a/src/runtime/time_linux_amd64.s b/src/runtime/time_linux_amd64.s index 0dd791989685cd..1416d232304f39 100644 --- a/src/runtime/time_linux_amd64.s +++ b/src/runtime/time_linux_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime #include "go_asm.h" #include "go_tls.h" @@ -12,19 +11,10 @@ #define SYS_clock_gettime 228 // func time.now() (sec int64, nsec int32, mono int64) -TEXT time·now(SB),NOSPLIT,$16-24 +TEXT time·now(SB),NOSPLIT,$16-24 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. -#ifdef GOEXPERIMENT_regabig MOVQ g_m(R14), BX // BX unchanged by C code. -#else - get_tls(CX) - MOVQ g(CX), AX - MOVQ g_m(AX), BX // BX unchanged by C code. -#endif - - // Store CLOCK_REALTIME results directly to return space. - LEAQ sec+0(FP), SI // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, @@ -34,25 +24,23 @@ TEXT time·now(SB),NOSPLIT,$16-24 MOVQ CX, 0(SP) MOVQ DX, 8(SP) - MOVQ -8(SI), CX // Sets CX to function return address. + LEAQ sec+0(FP), DX + MOVQ -8(DX), CX // Sets CX to function return address. MOVQ CX, m_vdsoPC(BX) - MOVQ SI, m_vdsoSP(BX) + MOVQ DX, m_vdsoSP(BX) -#ifdef GOEXPERIMENT_regabig CMPQ R14, m_curg(BX) // Only switch if on curg. -#else - CMPQ AX, m_curg(BX) // Only switch if on curg. -#endif JNE noswitch MOVQ m_g0(BX), DX MOVQ (g_sched+gobuf_sp)(DX), SP // Set SP to g0 stack noswitch: - SUBQ $16, SP // Space for monotonic time results + SUBQ $32, SP // Space for two time results ANDQ $~15, SP // Align for C code MOVL $0, DI // CLOCK_REALTIME + LEAQ 16(SP), SI MOVQ runtime·vdsoClockgettimeSym(SB), AX CMPQ AX, $0 JEQ fallback @@ -64,25 +52,27 @@ noswitch: CALL AX ret: - MOVQ 0(SP), AX // sec - MOVQ 8(SP), DX // nsec + MOVQ 16(SP), AX // realtime sec + MOVQ 24(SP), DI // realtime nsec (moved to BX below) + MOVQ 0(SP), CX // monotonic sec + IMULQ $1000000000, CX + MOVQ 8(SP), DX // monotonic nsec MOVQ R12, SP // Restore real SP + // Restore vdsoPC, vdsoSP // We don't worry about being signaled between the two stores. // If we are not in a signal handler, we'll restore vdsoSP to 0, // and no one will care about vdsoPC. If we are in a signal handler, // we cannot receive another signal. - MOVQ 8(SP), CX - MOVQ CX, m_vdsoSP(BX) - MOVQ 0(SP), CX - MOVQ CX, m_vdsoPC(BX) + MOVQ 8(SP), SI + MOVQ SI, m_vdsoSP(BX) + MOVQ 0(SP), SI + MOVQ SI, m_vdsoPC(BX) - // sec is in AX, nsec in DX - // return nsec in AX - IMULQ $1000000000, AX - ADDQ DX, AX - MOVQ AX, mono+16(FP) + // set result registers; AX is already correct + MOVQ DI, BX + ADDQ DX, CX RET fallback: diff --git a/src/runtime/time_nofake.go b/src/runtime/time_nofake.go index 5a4ceaf43d280c..70a2102b22e84c 100644 --- a/src/runtime/time_nofake.go +++ b/src/runtime/time_nofake.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime package runtime @@ -20,9 +19,14 @@ func nanotime() int64 { return nanotime1() } +var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32 + // write must be nosplit on Windows (see write1) // //go:nosplit func write(fd uintptr, p unsafe.Pointer, n int32) int32 { + if overrideWrite != nil { + return overrideWrite(fd, noescape(p), n) + } return write1(fd, p, n) } diff --git a/src/runtime/time_windows.h b/src/runtime/time_windows.h index cd16fd163bb429..7c2e65c328b82a 100644 --- a/src/runtime/time_windows.h +++ b/src/runtime/time_windows.h @@ -9,6 +9,7 @@ // http://web.archive.org/web/20210411000829/https://wrkhpi.wordpress.com/2007/08/09/getting-os-information-the-kuser_shared_data-structure/ // Must read hi1, then lo, then hi2. The snapshot is valid if hi1 == hi2. +// Or, on 64-bit, just read lo:hi1 all at once atomically. #define _INTERRUPT_TIME 0x7ffe0008 #define _SYSTEM_TIME 0x7ffe0014 #define time_lo 0 diff --git a/src/runtime/time_windows_386.s b/src/runtime/time_windows_386.s index 19ce6910d73b59..b8b636ef306bb7 100644 --- a/src/runtime/time_windows_386.s +++ b/src/runtime/time_windows_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime #include "go_asm.h" #include "textflag.h" diff --git a/src/runtime/time_windows_amd64.s b/src/runtime/time_windows_amd64.s index 93ab960b067cd5..226f2b5136e020 100644 --- a/src/runtime/time_windows_amd64.s +++ b/src/runtime/time_windows_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime #include "go_asm.h" #include "textflag.h" @@ -12,33 +11,20 @@ TEXT time·now(SB),NOSPLIT,$0-24 CMPB runtime·useQPCTime(SB), $0 JNE useQPC + MOVQ $_INTERRUPT_TIME, DI -loop: - MOVL time_hi1(DI), AX - MOVL time_lo(DI), BX - MOVL time_hi2(DI), CX - CMPL AX, CX - JNE loop - SHLQ $32, AX - ORQ BX, AX + MOVQ time_lo(DI), AX IMULQ $100, AX MOVQ AX, mono+16(FP) MOVQ $_SYSTEM_TIME, DI -wall: - MOVL time_hi1(DI), AX - MOVL time_lo(DI), BX - MOVL time_hi2(DI), CX - CMPL AX, CX - JNE wall - SHLQ $32, AX - ORQ BX, AX + MOVQ time_lo(DI), AX MOVQ $116444736000000000, DI SUBQ DI, AX IMULQ $100, AX // generated code for - // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 } + // func f(x uint64) (uint64, uint64) { return x/1000000000, x%1000000000 } // adapted to reduce duplication MOVQ AX, CX MOVQ $1360296554856532783, AX diff --git a/src/runtime/time_windows_arm.s b/src/runtime/time_windows_arm.s index 7c763b66edd2f6..711af883072073 100644 --- a/src/runtime/time_windows_arm.s +++ b/src/runtime/time_windows_arm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime #include "go_asm.h" #include "textflag.h" @@ -17,7 +16,9 @@ TEXT time·now(SB),NOSPLIT|NOFRAME,$0-20 MOVW $_INTERRUPT_TIME, R3 loop: MOVW time_hi1(R3), R1 + DMB MB_ISH MOVW time_lo(R3), R0 + DMB MB_ISH MOVW time_hi2(R3), R2 CMP R1, R2 BNE loop @@ -34,7 +35,9 @@ loop: MOVW $_SYSTEM_TIME, R3 wall: MOVW time_hi1(R3), R1 + DMB MB_ISH MOVW time_lo(R3), R0 + DMB MB_ISH MOVW time_hi2(R3), R2 CMP R1, R2 BNE wall diff --git a/src/runtime/time_windows_arm64.s b/src/runtime/time_windows_arm64.s index ef52ce4c99441e..e0c7d28e153f72 100644 --- a/src/runtime/time_windows_arm64.s +++ b/src/runtime/time_windows_arm64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime #include "go_asm.h" #include "textflag.h" @@ -13,34 +12,18 @@ TEXT time·now(SB),NOSPLIT|NOFRAME,$0-24 MOVB runtime·useQPCTime(SB), R0 CMP $0, R0 BNE useQPC - MOVD $_INTERRUPT_TIME, R3 -loop: - MOVWU time_hi1(R3), R1 - MOVWU time_lo(R3), R0 - MOVWU time_hi2(R3), R2 - CMP R1, R2 - BNE loop - // wintime = R1:R0, multiply by 100 - ORR R1<<32, R0 + MOVD $_INTERRUPT_TIME, R3 + MOVD time_lo(R3), R0 MOVD $100, R1 MUL R1, R0 MOVD R0, mono+16(FP) MOVD $_SYSTEM_TIME, R3 -wall: - MOVWU time_hi1(R3), R1 - MOVWU time_lo(R3), R0 - MOVWU time_hi2(R3), R2 - CMP R1, R2 - BNE wall - - // w = R1:R0 in 100ns units + MOVD time_lo(R3), R0 // convert to Unix epoch (but still 100ns units) #define delta 116444736000000000 - ORR R1<<32, R0 SUB $delta, R0 - // Convert to nSec MOVD $100, R1 MUL R1, R0 @@ -48,17 +31,14 @@ wall: // Code stolen from compiler output for: // // var x uint64 - // func f() (sec uint64, nsec uint32) { return x / 1000000000, uint32(x % 100000000) } + // func f() (sec uint64, nsec uint32) { return x / 1000000000, uint32(x % 1000000000) } // LSR $1, R0, R1 MOVD $-8543223759426509416, R2 - UMULH R2, R1, R1 + UMULH R1, R2, R1 LSR $28, R1, R1 MOVD R1, sec+0(FP) - MOVD $-6067343680855748867, R1 - UMULH R0, R1, R1 - LSR $26, R1, R1 - MOVD $100000000, R2 + MOVD $1000000000, R2 MSUB R1, R0, R2, R0 MOVW R0, nsec+8(FP) RET diff --git a/src/runtime/timeasm.go b/src/runtime/timeasm.go index 468ff8a0d33dcd..0421388686a253 100644 --- a/src/runtime/timeasm.go +++ b/src/runtime/timeasm.go @@ -5,8 +5,6 @@ // Declarations for operating systems implementing time.now directly in assembly. //go:build !faketime && (windows || (linux && amd64)) -// +build !faketime -// +build windows linux,amd64 package runtime diff --git a/src/runtime/timestub.go b/src/runtime/timestub.go index 6f16c70b8160b1..1d2926b43dc3d4 100644 --- a/src/runtime/timestub.go +++ b/src/runtime/timestub.go @@ -6,9 +6,6 @@ // indirectly, in terms of walltime and nanotime assembly. //go:build !faketime && !windows && !(linux && amd64) -// +build !faketime -// +build !windows -// +build !linux !amd64 package runtime diff --git a/src/runtime/timestub2.go b/src/runtime/timestub2.go index 800a2a94e0a0d8..b9a5cc6345ee2d 100644 --- a/src/runtime/timestub2.go +++ b/src/runtime/timestub2.go @@ -3,13 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !freebsd && !openbsd && !solaris && !windows && !(linux && amd64) -// +build !aix -// +build !darwin -// +build !freebsd -// +build !openbsd -// +build !solaris -// +build !windows -// +build !linux !amd64 package runtime diff --git a/src/runtime/tls_arm.s b/src/runtime/tls_arm.s index 879caac9e16743..d224c5545204df 100644 --- a/src/runtime/tls_arm.s +++ b/src/runtime/tls_arm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows -// +build !windows #include "go_asm.h" #include "go_tls.h" @@ -30,10 +29,12 @@ // NOTE: runtime.gogo assumes that R1 is preserved by this function. // runtime.mcall assumes this function only clobbers R0 and R11. // Returns with g in R0. -TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·save_g(SB),NOSPLIT,$0 // If the host does not support MRC the linker will replace it with // a call to runtime.read_tls_fallback which jumps to __kuser_get_tls. // The replacement function saves LR in R11 over the call to read_tls_fallback. + // To make stack unwinding work, this function should NOT be marked as NOFRAME, + // as it may contain a call, which clobbers LR even just temporarily. MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer BIC $3, R0 // Darwin/ARM might return unaligned pointer MOVW runtime·tls_g(SB), R11 diff --git a/src/runtime/tls_arm64.h b/src/runtime/tls_arm64.h index fe5e4cee12db92..3aa8c63d39d7b2 100644 --- a/src/runtime/tls_arm64.h +++ b/src/runtime/tls_arm64.h @@ -10,7 +10,6 @@ #define TLS_linux #endif #ifdef TLS_linux -#define TPIDR TPIDR_EL0 #define MRS_TPIDR_R0 WORD $0xd53bd040 // MRS TPIDR_EL0, R0 #endif @@ -21,23 +20,19 @@ #define TLS_darwin #endif #ifdef TLS_darwin -#define TPIDR TPIDRRO_EL0 #define TLSG_IS_VARIABLE #define MRS_TPIDR_R0 WORD $0xd53bd060 // MRS TPIDRRO_EL0, R0 #endif #ifdef GOOS_freebsd -#define TPIDR TPIDR_EL0 #define MRS_TPIDR_R0 WORD $0xd53bd040 // MRS TPIDR_EL0, R0 #endif #ifdef GOOS_netbsd -#define TPIDR TPIDRRO_EL0 #define MRS_TPIDR_R0 WORD $0xd53bd040 // MRS TPIDRRO_EL0, R0 #endif #ifdef GOOS_openbsd -#define TPIDR TPIDR_EL0 #define MRS_TPIDR_R0 WORD $0xd53bd040 // MRS TPIDR_EL0, R0 #endif diff --git a/src/runtime/tls_loong64.s b/src/runtime/tls_loong64.s new file mode 100644 index 00000000000000..bc3be3da1b470c --- /dev/null +++ b/src/runtime/tls_loong64.s @@ -0,0 +1,26 @@ +// Copyright 2022 The Go 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 "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +// If !iscgo, this is a no-op. +// +// NOTE: mcall() assumes this clobbers only R30 (REGTMP). +TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 + MOVB runtime·iscgo(SB), R30 + BEQ R30, nocgo + + MOVV g, runtime·tls_g(SB) + +nocgo: + RET + +TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 + MOVV runtime·tls_g(SB), g + RET + +GLOBL runtime·tls_g(SB), TLSBSS, $8 diff --git a/src/runtime/tls_mips64x.s b/src/runtime/tls_mips64x.s index 779d64ba31ab24..ec2748e5b22f03 100644 --- a/src/runtime/tls_mips64x.s +++ b/src/runtime/tls_mips64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips64 || mips64le -// +build mips64 mips64le #include "go_asm.h" #include "go_tls.h" diff --git a/src/runtime/tls_mipsx.s b/src/runtime/tls_mipsx.s index ada8d06a9e743b..acc3eb5a17854d 100644 --- a/src/runtime/tls_mipsx.s +++ b/src/runtime/tls_mipsx.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build mips || mipsle -// +build mips mipsle #include "go_asm.h" #include "go_tls.h" diff --git a/src/runtime/tls_ppc64x.s b/src/runtime/tls_ppc64x.s index 7e935d0eb29591..17aec9fc1e3cb5 100644 --- a/src/runtime/tls_ppc64x.s +++ b/src/runtime/tls_ppc64x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ppc64 || ppc64le -// +build ppc64 ppc64le #include "go_asm.h" #include "go_tls.h" diff --git a/src/runtime/tls_riscv64.s b/src/runtime/tls_riscv64.s index 22b550b76114b6..397919aeba2363 100644 --- a/src/runtime/tls_riscv64.s +++ b/src/runtime/tls_riscv64.s @@ -15,7 +15,7 @@ TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 BEQ X0, X31, nocgo MOV runtime·tls_g(SB), X31 - ADD X4, X31 // add offset to thread pointer (X4) + ADD TP, X31 // add offset to thread pointer (X4) MOV g, (X31) nocgo: @@ -23,7 +23,7 @@ nocgo: TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 MOV runtime·tls_g(SB), X31 - ADD X4, X31 // add offset to thread pointer (X4) + ADD TP, X31 // add offset to thread pointer (X4) MOV (X31), g RET diff --git a/src/runtime/tls_stub.go b/src/runtime/tls_stub.go index 95dafd007c130c..7bdfc6b89a6149 100644 --- a/src/runtime/tls_stub.go +++ b/src/runtime/tls_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (windows && !amd64) || !windows -// +build windows,!amd64 !windows package runtime diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 1530178c853d01..ab6402c706e28a 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -13,6 +13,7 @@ package runtime import ( + "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -54,7 +55,7 @@ const ( traceEvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] traceEvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] traceEvHeapAlloc = 33 // gcController.heapLive change [timestamp, heap_alloc] - traceEvHeapGoal = 34 // gcController.heapGoal (formerly next_gc) change [timestamp, heap goal in bytes] + traceEvHeapGoal = 34 // gcController.heapGoal() (formerly next_gc) change [timestamp, heap goal in bytes] traceEvTimerGoroutine = 35 // not currently used; previously denoted timer goroutine [timer goroutine id] traceEvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] traceEvString = 37 // string dictionary entry [ID, length, string] @@ -69,7 +70,8 @@ const ( traceEvUserTaskEnd = 46 // end of a task [timestamp, internal task id, stack] traceEvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), stack, name string] traceEvUserLog = 48 // trace.Log [timestamp, internal task id, key string id, stack, value string] - traceEvCount = 49 + traceEvCPUSample = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] + traceEvCount = 50 // Byte is used but only 6 bits are available for event type. // The remaining 2 bits are used to specify the number of arguments. // That means, the max event type value is 63. @@ -85,7 +87,7 @@ const ( // and ppc64le. // Tracing won't work reliably for architectures where cputicks is emulated // by nanotime, so the value doesn't matter for those architectures. - traceTickDiv = 16 + 48*(sys.Goarch386|sys.GoarchAmd64) + traceTickDiv = 16 + 48*(goarch.Is386|goarch.IsAmd64) // Maximum number of PCs in a single stack trace. // Since events contain only stack id rather than whole stack trace, // we can allow quite large values here. @@ -107,6 +109,8 @@ const ( // trace is global tracing context. var trace struct { + // trace.lock must only be acquired on the system stack where + // stack splits cannot happen while it is held. lock mutex // protects the following members lockOwner *g // to avoid deadlocks during recursive lock locks enabled bool // when set runtime traces events @@ -124,8 +128,27 @@ var trace struct { empty traceBufPtr // stack of empty buffers fullHead traceBufPtr // queue of full buffers fullTail traceBufPtr - reader guintptr // goroutine that called ReadTrace, or nil stackTab traceStackTable // maps stack traces to unique ids + // cpuLogRead accepts CPU profile samples from the signal handler where + // they're generated. It uses a two-word header to hold the IDs of the P and + // G (respectively) that were active at the time of the sample. Because + // profBuf uses a record with all zeros in its header to indicate overflow, + // we make sure to make the P field always non-zero: The ID of a real P will + // start at bit 1, and bit 0 will be set. Samples that arrive while no P is + // running (such as near syscalls) will set the first header field to 0b10. + // This careful handling of the first header field allows us to store ID of + // the active G directly in the second field, even though that will be 0 + // when sampling g0. + cpuLogRead *profBuf + // cpuLogBuf is a trace buffer to hold events corresponding to CPU profile + // samples, which arrive out of band and not directly connected to a + // specific P. + cpuLogBuf traceBufPtr + + reader atomic.Pointer[g] // goroutine that called ReadTrace, or nil + + signalLock atomic.Uint32 // protects use of the following member, only usable in signal handlers + cpuLogWrite *profBuf // copy of cpuLogRead for use in signal handlers, set without signalLock // Dictionary for traceEvString. // @@ -153,9 +176,8 @@ type traceBufHeader struct { } // traceBuf is per-P tracing buffer. -// -//go:notinheap type traceBuf struct { + _ sys.NotInHeap traceBufHeader arr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf } @@ -166,7 +188,7 @@ type traceBuf struct { // manipulated in contexts where write barriers are not allowed, so // this is necessary. // -// TODO: Since traceBuf is now go:notinheap, this isn't necessary. +// TODO: Since traceBuf is now embedded runtime/internal/sys.NotInHeap, this isn't necessary. type traceBufPtr uintptr func (tp traceBufPtr) ptr() *traceBuf { return (*traceBuf)(unsafe.Pointer(tp)) } @@ -212,14 +234,24 @@ func StartTrace() error { // - or GoSysExit appears for a goroutine for which we don't emit EvGoInSyscall below. // To instruct traceEvent that it must not ignore events below, we set startingtrace. // trace.enabled is set afterwards once we have emitted all preliminary events. - _g_ := getg() - _g_.m.startingtrace = true + mp := getg().m + mp.startingtrace = true // Obtain current stack ID to use in all traceEvGoCreate events below. - mp := acquirem() stkBuf := make([]uintptr, traceStackSize) stackID := traceStackID(mp, stkBuf, 2) - releasem(mp) + + profBuf := newProfBuf(2, profBufWordCount, profBufTagCount) // after the timestamp, header is [pp.id, gp.goid] + trace.cpuLogRead = profBuf + + // We must not acquire trace.signalLock outside of a signal handler: a + // profiling signal may arrive at any time and try to acquire it, leading to + // deadlock. Because we can't use that lock to protect updates to + // trace.cpuLogWrite (only use of the structure it references), reads and + // writes of the pointer must be atomic. (And although this field is never + // the sole pointer to the profBuf value, it's best to allow a write barrier + // here.) + atomicstorep(unsafe.Pointer(&trace.cpuLogWrite), unsafe.Pointer(profBuf)) // World is stopped, no need to lock. forEachGRace(func(gp *g) { @@ -228,17 +260,17 @@ func StartTrace() error { gp.traceseq = 0 gp.tracelastp = getg().m.p // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. - id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum}) - traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID) + id := trace.stackTab.put([]uintptr{startPCforTrace(gp.startpc) + sys.PCQuantum}) + traceEvent(traceEvGoCreate, -1, gp.goid, uint64(id), stackID) } if status == _Gwaiting { // traceEvGoWaiting is implied to have seq=1. gp.traceseq++ - traceEvent(traceEvGoWaiting, -1, uint64(gp.goid)) + traceEvent(traceEvGoWaiting, -1, gp.goid) } if status == _Gsyscall { gp.traceseq++ - traceEvent(traceEvGoInSyscall, -1, uint64(gp.goid)) + traceEvent(traceEvGoInSyscall, -1, gp.goid) } else { gp.sysblocktraced = false } @@ -261,7 +293,7 @@ func StartTrace() error { trace.strings = make(map[string]uint64) trace.seqGC = 0 - _g_.m.startingtrace = false + mp.startingtrace = false trace.enabled = true // Register runtime goroutine labels. @@ -301,6 +333,10 @@ func StopTrace() { traceGoSched() + atomicstorep(unsafe.Pointer(&trace.cpuLogWrite), nil) + trace.cpuLogRead.close() + traceReadCPU() + // Loop over all allocated Ps because dead Ps may still have // trace buffers. for _, p := range allp[:cap(allp)] { @@ -317,6 +353,13 @@ func StopTrace() { traceFullQueue(buf) } } + if trace.cpuLogBuf != 0 { + buf := trace.cpuLogBuf + trace.cpuLogBuf = 0 + if buf.ptr().pos != 0 { + traceFullQueue(buf) + } + } for { trace.ticksEnd = cputicks() @@ -343,30 +386,33 @@ func StopTrace() { raceacquire(unsafe.Pointer(&trace.shutdownSema)) } - // The lock protects us from races with StartTrace/StopTrace because they do stop-the-world. - lock(&trace.lock) - for _, p := range allp[:cap(allp)] { - if p.tracebuf != 0 { - throw("trace: non-empty trace buffer in proc") + systemstack(func() { + // The lock protects us from races with StartTrace/StopTrace because they do stop-the-world. + lock(&trace.lock) + for _, p := range allp[:cap(allp)] { + if p.tracebuf != 0 { + throw("trace: non-empty trace buffer in proc") + } } - } - if trace.buf != 0 { - throw("trace: non-empty global trace buffer") - } - if trace.fullHead != 0 || trace.fullTail != 0 { - throw("trace: non-empty full trace buffer") - } - if trace.reading != 0 || trace.reader != 0 { - throw("trace: reading after shutdown") - } - for trace.empty != 0 { - buf := trace.empty - trace.empty = buf.ptr().link - sysFree(unsafe.Pointer(buf), unsafe.Sizeof(*buf.ptr()), &memstats.other_sys) - } - trace.strings = nil - trace.shutdown = false - unlock(&trace.lock) + if trace.buf != 0 { + throw("trace: non-empty global trace buffer") + } + if trace.fullHead != 0 || trace.fullTail != 0 { + throw("trace: non-empty full trace buffer") + } + if trace.reading != 0 || trace.reader.Load() != nil { + throw("trace: reading after shutdown") + } + for trace.empty != 0 { + buf := trace.empty + trace.empty = buf.ptr().link + sysFree(unsafe.Pointer(buf), unsafe.Sizeof(*buf.ptr()), &memstats.other_sys) + } + trace.strings = nil + trace.shutdown = false + trace.cpuLogRead = nil + unlock(&trace.lock) + }) } // ReadTrace returns the next chunk of binary tracing data, blocking until data @@ -375,6 +421,55 @@ func StopTrace() { // returned data before calling ReadTrace again. // ReadTrace must be called from one goroutine at a time. func ReadTrace() []byte { +top: + var buf []byte + var park bool + systemstack(func() { + buf, park = readTrace0() + }) + if park { + gopark(func(gp *g, _ unsafe.Pointer) bool { + if !trace.reader.CompareAndSwapNoWB(nil, gp) { + // We're racing with another reader. + // Wake up and handle this case. + return false + } + + if g2 := traceReader(); gp == g2 { + // New data arrived between unlocking + // and the CAS and we won the wake-up + // race, so wake up directly. + return false + } else if g2 != nil { + printlock() + println("runtime: got trace reader", g2, g2.goid) + throw("unexpected trace reader") + } + + return true + }, nil, waitReasonTraceReaderBlocked, traceEvGoBlock, 2) + goto top + } + + return buf +} + +// readTrace0 is ReadTrace's continuation on g0. This must run on the +// system stack because it acquires trace.lock. +// +//go:systemstack +func readTrace0() (buf []byte, park bool) { + if raceenabled { + // g0 doesn't have a race context. Borrow the user G's. + if getg().racectx != 0 { + throw("expected racectx == 0") + } + getg().racectx = getg().m.curg.racectx + // (This defer should get open-coded, which is safe on + // the system stack.) + defer func() { getg().racectx = 0 }() + } + // This function may need to lock trace.lock recursively // (goparkunlock -> traceGoPark -> traceEvent -> traceFlush). // To allow this we use trace.lockOwner. @@ -382,16 +477,16 @@ func ReadTrace() []byte { // allocation can call heap allocate, which will try to emit a trace // event while holding heap lock. lock(&trace.lock) - trace.lockOwner = getg() + trace.lockOwner = getg().m.curg - if trace.reader != 0 { + if trace.reader.Load() != nil { // More than one goroutine reads trace. This is bad. // But we rather do not crash the program because of tracing, // because tracing can be enabled at runtime on prod servers. trace.lockOwner = nil unlock(&trace.lock) println("runtime: ReadTrace called from multiple goroutines simultaneously") - return nil + return nil, false } // Recycle the old buffer. if buf := trace.reading; buf != 0 { @@ -404,36 +499,59 @@ func ReadTrace() []byte { trace.headerWritten = true trace.lockOwner = nil unlock(&trace.lock) - return []byte("go 1.11 trace\x00\x00\x00") + return []byte("go 1.19 trace\x00\x00\x00"), false + } + // Optimistically look for CPU profile samples. This may write new stack + // records, and may write new tracing buffers. + if !trace.footerWritten && !trace.shutdown { + traceReadCPU() } // Wait for new data. if trace.fullHead == 0 && !trace.shutdown { - trace.reader.set(getg()) - goparkunlock(&trace.lock, waitReasonTraceReaderBlocked, traceEvGoBlock, 2) - lock(&trace.lock) + // We don't simply use a note because the scheduler + // executes this goroutine directly when it wakes up + // (also a note would consume an M). + trace.lockOwner = nil + unlock(&trace.lock) + return nil, true } +newFull: + assertLockHeld(&trace.lock) // Write a buffer. if trace.fullHead != 0 { buf := traceFullDequeue() trace.reading = buf trace.lockOwner = nil unlock(&trace.lock) - return buf.ptr().arr[:buf.ptr().pos] + return buf.ptr().arr[:buf.ptr().pos], false } + // Write footer with timer frequency. if !trace.footerWritten { trace.footerWritten = true // Use float64 because (trace.ticksEnd - trace.ticksStart) * 1e9 can overflow int64. freq := float64(trace.ticksEnd-trace.ticksStart) * 1e9 / float64(trace.timeEnd-trace.timeStart) / traceTickDiv + if freq <= 0 { + throw("trace: ReadTrace got invalid frequency") + } trace.lockOwner = nil unlock(&trace.lock) - var data []byte - data = append(data, traceEvFrequency|0<= 0 { + if stackID != 0 || skip >= 0 { narg++ } // We have only 2 bits for number of arguments. @@ -574,7 +737,9 @@ func traceEventLocked(extraBytes int, mp *m, pid int32, bufp *traceBufPtr, ev by for _, a := range args { buf.varint(a) } - if skip == 0 { + if stackID != 0 { + buf.varint(uint64(stackID)) + } else if skip == 0 { buf.varint(0) } else if skip > 0 { buf.varint(traceStackID(mp, buf.stk[:], skip)) @@ -589,20 +754,126 @@ func traceEventLocked(extraBytes int, mp *m, pid int32, bufp *traceBufPtr, ev by } } +// traceCPUSample writes a CPU profile sample stack to the execution tracer's +// profiling buffer. It is called from a signal handler, so is limited in what +// it can do. +func traceCPUSample(gp *g, pp *p, stk []uintptr) { + if !trace.enabled { + // Tracing is usually turned off; don't spend time acquiring the signal + // lock unless it's active. + return + } + + // Match the clock used in traceEventLocked + now := cputicks() + // The "header" here is the ID of the P that was running the profiled code, + // followed by the ID of the goroutine. (For normal CPU profiling, it's + // usually the number of samples with the given stack.) Near syscalls, pp + // may be nil. Reporting goid of 0 is fine for either g0 or a nil gp. + var hdr [2]uint64 + if pp != nil { + // Overflow records in profBuf have all header values set to zero. Make + // sure that real headers have at least one bit set. + hdr[0] = uint64(pp.id)<<1 | 0b1 + } else { + hdr[0] = 0b10 + } + if gp != nil { + hdr[1] = gp.goid + } + + // Allow only one writer at a time + for !trace.signalLock.CompareAndSwap(0, 1) { + // TODO: Is it safe to osyield here? https://go.dev/issue/52672 + osyield() + } + + if log := (*profBuf)(atomic.Loadp(unsafe.Pointer(&trace.cpuLogWrite))); log != nil { + // Note: we don't pass a tag pointer here (how should profiling tags + // interact with the execution tracer?), but if we did we'd need to be + // careful about write barriers. See the long comment in profBuf.write. + log.write(nil, now, hdr[:], stk) + } + + trace.signalLock.Store(0) +} + +func traceReadCPU() { + bufp := &trace.cpuLogBuf + + for { + data, tags, _ := trace.cpuLogRead.read(profBufNonBlocking) + if len(data) == 0 { + break + } + for len(data) > 0 { + if len(data) < 4 || data[0] > uint64(len(data)) { + break // truncated profile + } + if data[0] < 4 || tags != nil && len(tags) < 1 { + break // malformed profile + } + if len(tags) < 1 { + break // mismatched profile records and tags + } + timestamp := data[1] + ppid := data[2] >> 1 + if hasP := (data[2] & 0b1) != 0; !hasP { + ppid = ^uint64(0) + } + goid := data[3] + stk := data[4:data[0]] + empty := len(stk) == 1 && data[2] == 0 && data[3] == 0 + data = data[data[0]:] + // No support here for reporting goroutine tags at the moment; if + // that information is to be part of the execution trace, we'd + // probably want to see when the tags are applied and when they + // change, instead of only seeing them when we get a CPU sample. + tags = tags[1:] + + if empty { + // Looks like an overflow record from the profBuf. Not much to + // do here, we only want to report full records. + // + // TODO: should we start a goroutine to drain the profBuf, + // rather than relying on a high-enough volume of tracing events + // to keep ReadTrace busy? https://go.dev/issue/52674 + continue + } + + buf := bufp.ptr() + if buf == nil { + systemstack(func() { + *bufp = traceFlush(*bufp, 0) + }) + buf = bufp.ptr() + } + for i := range stk { + if i >= len(buf.stk) { + break + } + buf.stk[i] = uintptr(stk[i]) + } + stackID := trace.stackTab.put(buf.stk[:len(stk)]) + + traceEventLocked(0, nil, 0, bufp, traceEvCPUSample, stackID, 1, timestamp/traceTickDiv, ppid, goid) + } + } +} + func traceStackID(mp *m, buf []uintptr, skip int) uint64 { - _g_ := getg() - gp := mp.curg + gp := getg() + curgp := mp.curg var nstk int - if gp == _g_ { + if curgp == gp { nstk = callers(skip+1, buf) - } else if gp != nil { - gp = mp.curg - nstk = gcallers(gp, skip, buf) + } else if curgp != nil { + nstk = gcallers(curgp, skip, buf) } if nstk > 0 { nstk-- // skip runtime.goexit } - if nstk > 0 && gp.goid == 1 { + if nstk > 0 && curgp.goid == 1 { nstk-- // skip runtime.main } id := trace.stackTab.put(buf[:nstk]) @@ -611,6 +882,11 @@ func traceStackID(mp *m, buf []uintptr, skip int) uint64 { // traceAcquireBuffer returns trace buffer to use and, if necessary, locks it. func traceAcquireBuffer() (mp *m, pid int32, bufp *traceBufPtr) { + // Any time we acquire a buffer, we may end up flushing it, + // but flushes are rare. Record the lock edge even if it + // doesn't happen this time. + lockRankMayTraceFlush() + mp = acquirem() if p := mp.p.ptr(); p != nil { return mp, p.id, &p.tracebuf @@ -627,7 +903,21 @@ func traceReleaseBuffer(pid int32) { releasem(getg().m) } +// lockRankMayTraceFlush records the lock ranking effects of a +// potential call to traceFlush. +func lockRankMayTraceFlush() { + owner := trace.lockOwner + dolock := owner == nil || owner != getg().m.curg + if dolock { + lockWithRankMayAcquire(&trace.lock, getLockRank(&trace.lock)) + } +} + // traceFlush puts buf onto stack of full buffers and returns an empty buffer. +// +// This must run on the system stack because it acquires trace.lock. +// +//go:systemstack func traceFlush(buf traceBufPtr, pid int32) traceBufPtr { owner := trace.lockOwner dolock := owner == nil || owner != getg().m.curg @@ -652,6 +942,9 @@ func traceFlush(buf traceBufPtr, pid int32) traceBufPtr { // initialize the buffer for a new batch ticks := uint64(cputicks()) / traceTickDiv + if ticks == bufp.lastTicks { + ticks = bufp.lastTicks + 1 + } bufp.lastTicks = ticks bufp.byte(traceEvBatch | 1<= 0x80; v >>= 7 { - buf = append(buf, 0x80|byte(v)) - } - buf = append(buf, byte(v)) - return buf -} - // varint appends v to buf in little-endian-base-128 encoding. func (buf *traceBuf) varint(v uint64) { pos := buf.pos @@ -743,6 +1029,22 @@ func (buf *traceBuf) varint(v uint64) { buf.pos = pos } +// varintAt writes varint v at byte position pos in buf. This always +// consumes traceBytesPerNumber bytes. This is intended for when the +// caller needs to reserve space for a varint but can't populate it +// until later. +func (buf *traceBuf) varintAt(pos int, v uint64) { + for i := 0; i < traceBytesPerNumber; i++ { + if i < traceBytesPerNumber-1 { + buf.arr[pos] = 0x80 | byte(v) + } else { + buf.arr[pos] = byte(v) + } + v >>= 7 + pos++ + } +} + // byte appends v to buf. func (buf *traceBuf) byte(v byte) { buf.arr[buf.pos] = v @@ -752,7 +1054,7 @@ func (buf *traceBuf) byte(v byte) { // traceStackTable maps stack traces (arrays of PC's) to unique uint32 ids. // It is lock-free for reading. type traceStackTable struct { - lock mutex + lock mutex // Must be acquired on the system stack seq uint32 mem traceAlloc tab [1 << 13]traceStackPtr @@ -788,26 +1090,31 @@ func (tab *traceStackTable) put(pcs []uintptr) uint32 { return id } // Now, double check under the mutex. - lock(&tab.lock) - if id := tab.find(pcs, hash); id != 0 { + // Switch to the system stack so we can acquire tab.lock + var id uint32 + systemstack(func() { + lock(&tab.lock) + if id = tab.find(pcs, hash); id != 0 { + unlock(&tab.lock) + return + } + // Create new record. + tab.seq++ + stk := tab.newStack(len(pcs)) + stk.hash = hash + stk.id = tab.seq + id = stk.id + stk.n = len(pcs) + stkpc := stk.stack() + for i, pc := range pcs { + stkpc[i] = pc + } + part := int(hash % uintptr(len(tab.tab))) + stk.link = tab.tab[part] + atomicstorep(unsafe.Pointer(&tab.tab[part]), unsafe.Pointer(stk)) unlock(&tab.lock) - return id - } - // Create new record. - tab.seq++ - stk := tab.newStack(len(pcs)) - stk.hash = hash - stk.id = tab.seq - stk.n = len(pcs) - stkpc := stk.stack() - for i, pc := range pcs { - stkpc[i] = pc - } - part := int(hash % uintptr(len(tab.tab))) - stk.link = tab.tab[part] - atomicstorep(unsafe.Pointer(&tab.tab[part]), unsafe.Pointer(stk)) - unlock(&tab.lock) - return stk.id + }) + return id } // find checks if the stack trace pcs is already present in the table. @@ -829,64 +1136,78 @@ Search: // newStack allocates a new stack of size n. func (tab *traceStackTable) newStack(n int) *traceStack { - return (*traceStack)(tab.mem.alloc(unsafe.Sizeof(traceStack{}) + uintptr(n)*sys.PtrSize)) + return (*traceStack)(tab.mem.alloc(unsafe.Sizeof(traceStack{}) + uintptr(n)*goarch.PtrSize)) } -// allFrames returns all of the Frames corresponding to pcs. -func allFrames(pcs []uintptr) []Frame { - frames := make([]Frame, 0, len(pcs)) +// traceFrames returns the frames corresponding to pcs. It may +// allocate and may emit trace events. +func traceFrames(bufp traceBufPtr, pcs []uintptr) ([]traceFrame, traceBufPtr) { + frames := make([]traceFrame, 0, len(pcs)) ci := CallersFrames(pcs) for { + var frame traceFrame f, more := ci.Next() - frames = append(frames, f) + frame, bufp = traceFrameForPC(bufp, 0, f) + frames = append(frames, frame) if !more { - return frames + return frames, bufp } } } // dump writes all previously cached stacks to trace buffers, // releases all memory and resets state. -func (tab *traceStackTable) dump() { - var tmp [(2 + 4*traceStackSize) * traceBytesPerNumber]byte - bufp := traceFlush(0, 0) - for _, stk := range tab.tab { - stk := stk.ptr() +// +// This must run on the system stack because it calls traceFlush. +// +//go:systemstack +func (tab *traceStackTable) dump(bufp traceBufPtr) traceBufPtr { + for i := range tab.tab { + stk := tab.tab[i].ptr() for ; stk != nil; stk = stk.link.ptr() { - tmpbuf := tmp[:0] - tmpbuf = traceAppend(tmpbuf, uint64(stk.id)) - frames := allFrames(stk.stack()) - tmpbuf = traceAppend(tmpbuf, uint64(len(frames))) - for _, f := range frames { - var frame traceFrame - frame, bufp = traceFrameForPC(bufp, 0, f) - tmpbuf = traceAppend(tmpbuf, uint64(f.PC)) - tmpbuf = traceAppend(tmpbuf, uint64(frame.funcID)) - tmpbuf = traceAppend(tmpbuf, uint64(frame.fileID)) - tmpbuf = traceAppend(tmpbuf, uint64(frame.line)) - } - // Now copy to the buffer. - size := 1 + traceBytesPerNumber + len(tmpbuf) - if buf := bufp.ptr(); len(buf.arr)-buf.pos < size { + var frames []traceFrame + frames, bufp = traceFrames(bufp, stk.stack()) + + // Estimate the size of this record. This + // bound is pretty loose, but avoids counting + // lots of varint sizes. + maxSize := 1 + traceBytesPerNumber + (2+4*len(frames))*traceBytesPerNumber + // Make sure we have enough buffer space. + if buf := bufp.ptr(); len(buf.arr)-buf.pos < maxSize { bufp = traceFlush(bufp, 0) } + + // Emit header, with space reserved for length. buf := bufp.ptr() buf.byte(traceEvStack | 3< uintptr(len(a.head.ptr().data)) { if n > uintptr(len(a.head.ptr().data)) { throw("trace: alloc too large") @@ -1013,11 +1334,11 @@ func traceGCSTWDone() { func traceGCSweepStart() { // Delay the actual GCSweepStart event until the first span // sweep. If we don't sweep anything, don't emit any events. - _p_ := getg().m.p.ptr() - if _p_.traceSweep { + pp := getg().m.p.ptr() + if pp.traceSweep { throw("double traceGCSweepStart") } - _p_.traceSweep, _p_.traceSwept, _p_.traceReclaimed = true, 0, 0 + pp.traceSweep, pp.traceSwept, pp.traceReclaimed = true, 0, 0 } // traceGCSweepSpan traces the sweep of a single page. @@ -1025,24 +1346,24 @@ func traceGCSweepStart() { // This may be called outside a traceGCSweepStart/traceGCSweepDone // pair; however, it will not emit any trace events in this case. func traceGCSweepSpan(bytesSwept uintptr) { - _p_ := getg().m.p.ptr() - if _p_.traceSweep { - if _p_.traceSwept == 0 { + pp := getg().m.p.ptr() + if pp.traceSweep { + if pp.traceSwept == 0 { traceEvent(traceEvGCSweepStart, 1) } - _p_.traceSwept += bytesSwept + pp.traceSwept += bytesSwept } } func traceGCSweepDone() { - _p_ := getg().m.p.ptr() - if !_p_.traceSweep { + pp := getg().m.p.ptr() + if !pp.traceSweep { throw("missing traceGCSweepStart") } - if _p_.traceSwept != 0 { - traceEvent(traceEvGCSweepDone, -1, uint64(_p_.traceSwept), uint64(_p_.traceReclaimed)) + if pp.traceSwept != 0 { + traceEvent(traceEvGCSweepDone, -1, uint64(pp.traceSwept), uint64(pp.traceReclaimed)) } - _p_.traceSweep = false + pp.traceSweep = false } func traceGCMarkAssistStart() { @@ -1057,21 +1378,21 @@ func traceGoCreate(newg *g, pc uintptr) { newg.traceseq = 0 newg.tracelastp = getg().m.p // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. - id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum}) - traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id)) + id := trace.stackTab.put([]uintptr{startPCforTrace(pc) + sys.PCQuantum}) + traceEvent(traceEvGoCreate, 2, newg.goid, uint64(id)) } func traceGoStart() { - _g_ := getg().m.curg - _p_ := _g_.m.p - _g_.traceseq++ - if _p_.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker { - traceEvent(traceEvGoStartLabel, -1, uint64(_g_.goid), _g_.traceseq, trace.markWorkerLabels[_p_.ptr().gcMarkWorkerMode]) - } else if _g_.tracelastp == _p_ { - traceEvent(traceEvGoStartLocal, -1, uint64(_g_.goid)) + gp := getg().m.curg + pp := gp.m.p + gp.traceseq++ + if pp.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker { + traceEvent(traceEvGoStartLabel, -1, gp.goid, gp.traceseq, trace.markWorkerLabels[pp.ptr().gcMarkWorkerMode]) + } else if gp.tracelastp == pp { + traceEvent(traceEvGoStartLocal, -1, gp.goid) } else { - _g_.tracelastp = _p_ - traceEvent(traceEvGoStart, -1, uint64(_g_.goid), _g_.traceseq) + gp.tracelastp = pp + traceEvent(traceEvGoStart, -1, gp.goid, gp.traceseq) } } @@ -1080,14 +1401,14 @@ func traceGoEnd() { } func traceGoSched() { - _g_ := getg() - _g_.tracelastp = _g_.m.p + gp := getg() + gp.tracelastp = gp.m.p traceEvent(traceEvGoSched, 1) } func traceGoPreempt() { - _g_ := getg() - _g_.tracelastp = _g_.m.p + gp := getg() + gp.tracelastp = gp.m.p traceEvent(traceEvGoPreempt, 1) } @@ -1099,13 +1420,13 @@ func traceGoPark(traceEv byte, skip int) { } func traceGoUnpark(gp *g, skip int) { - _p_ := getg().m.p + pp := getg().m.p gp.traceseq++ - if gp.tracelastp == _p_ { - traceEvent(traceEvGoUnblockLocal, skip, uint64(gp.goid)) + if gp.tracelastp == pp { + traceEvent(traceEvGoUnblockLocal, skip, gp.goid) } else { - gp.tracelastp = _p_ - traceEvent(traceEvGoUnblock, skip, uint64(gp.goid), gp.traceseq) + gp.tracelastp = pp + traceEvent(traceEvGoUnblock, skip, gp.goid, gp.traceseq) } } @@ -1126,10 +1447,10 @@ func traceGoSysExit(ts int64) { // aka right now), and assign a fresh time stamp to keep the log consistent. ts = 0 } - _g_ := getg().m.curg - _g_.traceseq++ - _g_.tracelastp = _g_.m.p - traceEvent(traceEvGoSysExit, -1, uint64(_g_.goid), _g_.traceseq, uint64(ts)/traceTickDiv) + gp := getg().m.curg + gp.traceseq++ + gp.tracelastp = gp.m.p + traceEvent(traceEvGoSysExit, -1, gp.goid, gp.traceseq, uint64(ts)/traceTickDiv) } func traceGoSysBlock(pp *p) { @@ -1143,12 +1464,13 @@ func traceGoSysBlock(pp *p) { releasem(mp) } -func traceHeapAlloc() { - traceEvent(traceEvHeapAlloc, -1, gcController.heapLive) +func traceHeapAlloc(live uint64) { + traceEvent(traceEvHeapAlloc, -1, live) } func traceHeapGoal() { - if heapGoal := atomic.Load64(&gcController.heapGoal); heapGoal == ^uint64(0) { + heapGoal := gcController.heapGoal() + if heapGoal == ^uint64(0) { // Heap-based triggering is disabled. traceEvent(traceEvHeapGoal, -1, 0) } else { @@ -1173,7 +1495,7 @@ func trace_userTaskCreate(id, parentID uint64, taskType string) { } typeStringID, bufp := traceString(bufp, pid, taskType) - traceEventLocked(0, mp, pid, bufp, traceEvUserTaskCreate, 3, id, parentID, typeStringID) + traceEventLocked(0, mp, pid, bufp, traceEvUserTaskCreate, 0, 3, id, parentID, typeStringID) traceReleaseBuffer(pid) } @@ -1195,7 +1517,7 @@ func trace_userRegion(id, mode uint64, name string) { } nameStringID, bufp := traceString(bufp, pid, name) - traceEventLocked(0, mp, pid, bufp, traceEvUserRegion, 3, id, mode, nameStringID) + traceEventLocked(0, mp, pid, bufp, traceEvUserRegion, 0, 3, id, mode, nameStringID) traceReleaseBuffer(pid) } @@ -1214,7 +1536,7 @@ func trace_userLog(id uint64, category, message string) { categoryID, bufp := traceString(bufp, pid, category) extraSpace := traceBytesPerNumber + len(message) // extraSpace for the value string - traceEventLocked(extraSpace, mp, pid, bufp, traceEvUserLog, 3, id, categoryID) + traceEventLocked(extraSpace, mp, pid, bufp, traceEvUserLog, 0, 3, id, categoryID) // traceEventLocked reserved extra space for val and len(val) // in buf, so buf now has room for the following. buf := bufp.ptr() @@ -1230,3 +1552,17 @@ func trace_userLog(id uint64, category, message string) { traceReleaseBuffer(pid) } + +// the start PC of a goroutine for tracing purposes. If pc is a wrapper, +// it returns the PC of the wrapped function. Otherwise it returns pc. +func startPCforTrace(pc uintptr) uintptr { + f := findfunc(pc) + if !f.valid() { + return pc // should not happen, but don't care + } + w := funcdata(f, _FUNCDATA_WrapInfo) + if w == nil { + return pc // not a wrapper + } + return f.datap.textAddr(*(*uint32)(w)) +} diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go index 6e18bfb75554b5..d47cb8573c61f5 100644 --- a/src/runtime/trace/annotation.go +++ b/src/runtime/trace/annotation.go @@ -28,13 +28,13 @@ type traceContextKey struct{} // If the end function is called multiple times, only the first // call is used in the latency measurement. // -// ctx, task := trace.NewTask(ctx, "awesomeTask") -// trace.WithRegion(ctx, "preparation", prepWork) -// // preparation of the task -// go func() { // continue processing the task in a separate goroutine. -// defer task.End() -// trace.WithRegion(ctx, "remainingWork", remainingWork) -// }() +// ctx, task := trace.NewTask(ctx, "awesomeTask") +// trace.WithRegion(ctx, "preparation", prepWork) +// // preparation of the task +// go func() { // continue processing the task in a separate goroutine. +// defer task.End() +// trace.WithRegion(ctx, "remainingWork", remainingWork) +// }() func NewTask(pctx context.Context, taskType string) (ctx context.Context, task *Task) { pid := fromContext(pctx).id id := newID() @@ -98,7 +98,7 @@ func Log(ctx context.Context, category, message string) { } // Logf is like Log, but the value is formatted using the specified format spec. -func Logf(ctx context.Context, category, format string, args ...interface{}) { +func Logf(ctx context.Context, category, format string, args ...any) { if IsEnabled() { // Ideally this should be just Log, but that will // add one more frame in the stack trace. @@ -148,8 +148,7 @@ func WithRegion(ctx context.Context, regionType string, fn func()) { // after this region must be ended before this region can be ended. // Recommended usage is // -// defer trace.StartRegion(ctx, "myTracedRegion").End() -// +// defer trace.StartRegion(ctx, "myTracedRegion").End() func StartRegion(ctx context.Context, regionType string) *Region { if !IsEnabled() { return noopRegion @@ -179,8 +178,7 @@ func (r *Region) End() { // The information is advisory only. The tracing status // may have changed by the time this function returns. func IsEnabled() bool { - enabled := atomic.LoadInt32(&tracing.enabled) - return enabled == 1 + return tracing.enabled.Load() } // diff --git a/src/runtime/trace/trace.go b/src/runtime/trace/trace.go index b34aef03c511a9..86c97e2a11d8e9 100644 --- a/src/runtime/trace/trace.go +++ b/src/runtime/trace/trace.go @@ -5,11 +5,13 @@ // Package trace contains facilities for programs to generate traces // for the Go execution tracer. // -// Tracing runtime activities +// # Tracing runtime activities // // The execution trace captures a wide range of execution events such as // goroutine creation/blocking/unblocking, syscall enter/exit/block, // GC-related events, changes of heap size, processor start/stop, etc. +// When CPU profiling is active, the execution tracer makes an effort to +// include those samples as well. // A precise nanosecond-precision timestamp and a stack trace is // captured for most events. The generated trace can be interpreted // using `go tool trace`. @@ -19,7 +21,7 @@ // command runs the test in the current directory and writes the trace // file (trace.out). // -// go test -trace=trace.out +// go test -trace=trace.out // // This runtime/trace package provides APIs to add equivalent tracing // support to a standalone program. See the Example that demonstrates @@ -29,12 +31,12 @@ // following line will install a handler under the /debug/pprof/trace URL // to download a live trace: // -// import _ "net/http/pprof" +// import _ "net/http/pprof" // // See the net/http/pprof package for more details about all of the // debug endpoints installed by this import. // -// User annotation +// # User annotation // // Package trace provides user annotation APIs that can be used to // log interesting events during execution. @@ -55,16 +57,16 @@ // trace to trace the durations of sequential steps in a cappuccino making // operation. // -// trace.WithRegion(ctx, "makeCappuccino", func() { +// trace.WithRegion(ctx, "makeCappuccino", func() { // -// // orderID allows to identify a specific order -// // among many cappuccino order region records. -// trace.Log(ctx, "orderID", orderID) +// // orderID allows to identify a specific order +// // among many cappuccino order region records. +// trace.Log(ctx, "orderID", orderID) // -// trace.WithRegion(ctx, "steamMilk", steamMilk) -// trace.WithRegion(ctx, "extractCoffee", extractCoffee) -// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) -// }) +// trace.WithRegion(ctx, "steamMilk", steamMilk) +// trace.WithRegion(ctx, "extractCoffee", extractCoffee) +// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) +// }) // // A task is a higher-level component that aids tracing of logical // operations such as an RPC request, an HTTP request, or an @@ -80,27 +82,26 @@ // the trace tool can identify the goroutines involved in a specific // cappuccino order. // -// ctx, task := trace.NewTask(ctx, "makeCappuccino") -// trace.Log(ctx, "orderID", orderID) -// -// milk := make(chan bool) -// espresso := make(chan bool) -// -// go func() { -// trace.WithRegion(ctx, "steamMilk", steamMilk) -// milk <- true -// }() -// go func() { -// trace.WithRegion(ctx, "extractCoffee", extractCoffee) -// espresso <- true -// }() -// go func() { -// defer task.End() // When assemble is done, the order is complete. -// <-espresso -// <-milk -// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) -// }() -// +// ctx, task := trace.NewTask(ctx, "makeCappuccino") +// trace.Log(ctx, "orderID", orderID) +// +// milk := make(chan bool) +// espresso := make(chan bool) +// +// go func() { +// trace.WithRegion(ctx, "steamMilk", steamMilk) +// milk <- true +// }() +// go func() { +// trace.WithRegion(ctx, "extractCoffee", extractCoffee) +// espresso <- true +// }() +// go func() { +// defer task.End() // When assemble is done, the order is complete. +// <-espresso +// <-milk +// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee) +// }() // // The trace tool computes the latency of a task by measuring the // time between the task creation and the task end and provides @@ -133,7 +134,7 @@ func Start(w io.Writer) error { w.Write(data) } }() - atomic.StoreInt32(&tracing.enabled, 1) + tracing.enabled.Store(true) return nil } @@ -142,12 +143,12 @@ func Start(w io.Writer) error { func Stop() { tracing.Lock() defer tracing.Unlock() - atomic.StoreInt32(&tracing.enabled, 0) + tracing.enabled.Store(false) runtime.StopTrace() } var tracing struct { - sync.Mutex // gate mutators (Start, Stop) - enabled int32 // accessed via atomic + sync.Mutex // gate mutators (Start, Stop) + enabled atomic.Bool } diff --git a/src/runtime/trace/trace_test.go b/src/runtime/trace/trace_test.go index b316eafe4c5563..19f7dbe77533ee 100644 --- a/src/runtime/trace/trace_test.go +++ b/src/runtime/trace/trace_test.go @@ -6,15 +6,20 @@ package trace_test import ( "bytes" + "context" "flag" + "fmt" + "internal/profile" "internal/race" "internal/trace" "io" "net" "os" "runtime" + "runtime/pprof" . "runtime/trace" "strconv" + "strings" "sync" "testing" "time" @@ -581,6 +586,202 @@ func TestTraceFutileWakeup(t *testing.T) { } } +func TestTraceCPUProfile(t *testing.T) { + if IsEnabled() { + t.Skip("skipping because -test.trace is set") + } + + cpuBuf := new(bytes.Buffer) + if err := pprof.StartCPUProfile(cpuBuf); err != nil { + t.Skipf("failed to start CPU profile: %v", err) + } + + buf := new(bytes.Buffer) + if err := Start(buf); err != nil { + t.Fatalf("failed to start tracing: %v", err) + } + + dur := 100 * time.Millisecond + func() { + // Create a region in the execution trace. Set and clear goroutine + // labels fully within that region, so we know that any CPU profile + // sample with the label must also be eligible for inclusion in the + // execution trace. + ctx := context.Background() + defer StartRegion(ctx, "cpuHogger").End() + pprof.Do(ctx, pprof.Labels("tracing", "on"), func(ctx context.Context) { + cpuHogger(cpuHog1, &salt1, dur) + }) + // Be sure the execution trace's view, when filtered to this goroutine + // via the explicit goroutine ID in each event, gets many more samples + // than the CPU profiler when filtered to this goroutine via labels. + cpuHogger(cpuHog1, &salt1, dur) + }() + + Stop() + pprof.StopCPUProfile() + saveTrace(t, buf, "TestTraceCPUProfile") + + prof, err := profile.Parse(cpuBuf) + if err != nil { + t.Fatalf("failed to parse CPU profile: %v", err) + } + // Examine the CPU profiler's view. Filter it to only include samples from + // the single test goroutine. Use labels to execute that filter: they should + // apply to all work done while that goroutine is getg().m.curg, and they + // should apply to no other goroutines. + pprofSamples := 0 + pprofStacks := make(map[string]int) + for _, s := range prof.Sample { + if s.Label["tracing"] != nil { + var fns []string + var leaf string + for _, loc := range s.Location { + for _, line := range loc.Line { + fns = append(fns, fmt.Sprintf("%s:%d", line.Function.Name, line.Line)) + leaf = line.Function.Name + } + } + // runtime.sigprof synthesizes call stacks when "normal traceback is + // impossible or has failed", using particular placeholder functions + // to represent common failure cases. Look for those functions in + // the leaf position as a sign that the call stack and its + // symbolization are more complex than this test can handle. + // + // TODO: Make the symbolization done by the execution tracer and CPU + // profiler match up even in these harder cases. See #53378. + switch leaf { + case "runtime._System", "runtime._GC", "runtime._ExternalCode", "runtime._VDSO": + continue + } + stack := strings.Join(fns, " ") + samples := int(s.Value[0]) + pprofSamples += samples + pprofStacks[stack] += samples + } + } + if pprofSamples == 0 { + t.Skipf("CPU profile did not include any samples while tracing was active\n%s", prof) + } + + // Examine the execution tracer's view of the CPU profile samples. Filter it + // to only include samples from the single test goroutine. Use the goroutine + // ID that was recorded in the events: that should reflect getg().m.curg, + // same as the profiler's labels (even when the M is using its g0 stack). + totalTraceSamples := 0 + traceSamples := 0 + traceStacks := make(map[string]int) + events, _ := parseTrace(t, buf) + var hogRegion *trace.Event + for _, ev := range events { + if ev.Type == trace.EvUserRegion && ev.Args[1] == 0 && ev.SArgs[0] == "cpuHogger" { + // mode "0" indicates region start + hogRegion = ev + } + } + if hogRegion == nil { + t.Fatalf("execution trace did not identify cpuHogger goroutine") + } else if hogRegion.Link == nil { + t.Fatalf("execution trace did not close cpuHogger region") + } + for _, ev := range events { + if ev.Type == trace.EvCPUSample { + totalTraceSamples++ + if ev.G == hogRegion.G { + traceSamples++ + var fns []string + for _, frame := range ev.Stk { + if frame.Fn != "runtime.goexit" { + fns = append(fns, fmt.Sprintf("%s:%d", frame.Fn, frame.Line)) + } + } + stack := strings.Join(fns, " ") + traceStacks[stack]++ + } + } + } + + // The execution trace may drop CPU profile samples if the profiling buffer + // overflows. Based on the size of profBufWordCount, that takes a bit over + // 1900 CPU samples or 19 thread-seconds at a 100 Hz sample rate. If we've + // hit that case, then we definitely have at least one full buffer's worth + // of CPU samples, so we'll call that success. + overflowed := totalTraceSamples >= 1900 + if traceSamples < pprofSamples { + t.Logf("exectution trace did not include all CPU profile samples; %d in profile, %d in trace", pprofSamples, traceSamples) + if !overflowed { + t.Fail() + } + } + + for stack, traceSamples := range traceStacks { + pprofSamples := pprofStacks[stack] + delete(pprofStacks, stack) + if traceSamples < pprofSamples { + t.Logf("execution trace did not include all CPU profile samples for stack %q; %d in profile, %d in trace", + stack, pprofSamples, traceSamples) + if !overflowed { + t.Fail() + } + } + } + for stack, pprofSamples := range pprofStacks { + t.Logf("CPU profile included %d samples at stack %q not present in execution trace", pprofSamples, stack) + if !overflowed { + t.Fail() + } + } + + if t.Failed() { + t.Logf("execution trace CPU samples:") + for stack, samples := range traceStacks { + t.Logf("%d: %q", samples, stack) + } + t.Logf("CPU profile:\n%v", prof) + } +} + +func cpuHogger(f func(x int) int, y *int, dur time.Duration) { + // We only need to get one 100 Hz clock tick, so we've got + // a large safety buffer. + // But do at least 500 iterations (which should take about 100ms), + // otherwise TestCPUProfileMultithreaded can fail if only one + // thread is scheduled during the testing period. + t0 := time.Now() + accum := *y + for i := 0; i < 500 || time.Since(t0) < dur; i++ { + accum = f(accum) + } + *y = accum +} + +var ( + salt1 = 0 +) + +// The actual CPU hogging function. +// Must not call other functions nor access heap/globals in the loop, +// otherwise under race detector the samples will be in the race runtime. +func cpuHog1(x int) int { + return cpuHog0(x, 1e5) +} + +func cpuHog0(x, n int) int { + foo := x + for i := 0; i < n; i++ { + if i%1000 == 0 { + // Spend time in mcall, stored as gp.m.curg, with g0 running + runtime.Gosched() + } + if foo > 0 { + foo *= foo + } else { + foo *= foo + 1 + } + } + return foo +} + func saveTrace(t *testing.T, buf *bytes.Buffer, name string) { if !*saveTraces { return diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 814c3236345404..396b3fafbc582d 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -6,7 +6,7 @@ package runtime import ( "internal/bytealg" - "runtime/internal/atomic" + "internal/goarch" "runtime/internal/sys" "unsafe" ) @@ -21,41 +21,6 @@ import ( const usesLR = sys.MinFrameSize > 0 -// Traceback over the deferred function calls. -// Report them like calls that have been invoked but not started executing yet. -func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) { - var frame stkframe - for d := gp._defer; d != nil; d = d.link { - fn := d.fn - if fn == nil { - // Defer of nil function. Args don't matter. - frame.pc = 0 - frame.fn = funcInfo{} - frame.argp = 0 - frame.arglen = 0 - frame.argmap = nil - } else { - frame.pc = fn.fn - f := findfunc(frame.pc) - if !f.valid() { - print("runtime: unknown pc in defer ", hex(frame.pc), "\n") - throw("unknown pc") - } - frame.fn = f - frame.argp = uintptr(deferArgs(d)) - var ok bool - frame.arglen, frame.argmap, ok = getArgInfoFast(f, true) - if !ok { - frame.arglen, frame.argmap = getArgInfo(&frame, f, true, fn) - } - } - frame.continpc = frame.pc - if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) { - return - } - } -} - // Generic traceback. Handles runtime stack prints (pcbuf == nil), // the runtime.Callers function (pcbuf != nil), as well as the garbage // collector (callback != nil). A little clunky to merge these, but avoids @@ -88,8 +53,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } level, _, _ := gotraceback() - var ctxt *funcval // Context pointer for unstarted goroutines. See issue #25897. - if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp. if gp.syscallsp != 0 { pc0 = gp.syscallpc @@ -103,7 +66,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if usesLR { lr0 = gp.sched.lr } - ctxt = (*funcval)(gp.sched.ctxt) } } @@ -116,6 +78,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } waspanic := false cgoCtxt := gp.cgoCtxt + stack := gp.stack printing := pcbuf == nil && callback == nil // If the PC is zero, it's likely a nil function call. @@ -126,15 +89,29 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.lr = 0 } else { frame.pc = uintptr(*(*uintptr)(unsafe.Pointer(frame.sp))) - frame.sp += sys.PtrSize + frame.sp += goarch.PtrSize } } + // runtime/internal/atomic functions call into kernel helpers on + // arm < 7. See runtime/internal/atomic/sys_linux_arm.s. + // + // Start in the caller's frame. + if GOARCH == "arm" && goarm < 7 && GOOS == "linux" && frame.pc&0xffff0000 == 0xffff0000 { + // Note that the calls are simple BL without pushing the return + // address, so we use LR directly. + // + // The kernel helpers are frameless leaf functions, so SP and + // LR are not touched. + frame.pc = frame.lr + frame.lr = 0 + } + f := findfunc(frame.pc) if !f.valid() { if callback != nil || printing { - print("runtime: unknown pc ", hex(frame.pc), "\n") - tracebackHexdump(gp.stack, &frame, 0) + print("runtime: g ", gp.goid, ": unknown pc ", hex(frame.pc), "\n") + tracebackHexdump(stack, &frame, 0) } if callback != nil { throw("unknown pc") @@ -182,7 +159,10 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in if frame.fp == 0 { // Jump over system stack transitions. If we're on g0 and there's a user // goroutine, try to jump. Otherwise this is a regular call. - if flags&_TraceJumpStack != 0 && gp == gp.m.g0 && gp.m.curg != nil { + // We also defensively check that this won't switch M's on us, + // which could happen at critical points in the scheduler. + // This ensures gp.m doesn't change from a stack jump. + if flags&_TraceJumpStack != 0 && gp == gp.m.g0 && gp.m.curg != nil && gp.m.curg.m == gp.m { switch f.funcID { case funcID_morestack: // morestack does not return normally -- newstack() @@ -190,24 +170,29 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // This keeps morestack() from showing up in the backtrace, // but that makes some sense since it'll never be returned // to. - frame.pc = gp.m.curg.sched.pc + gp = gp.m.curg + frame.pc = gp.sched.pc frame.fn = findfunc(frame.pc) f = frame.fn flag = f.flag - frame.sp = gp.m.curg.sched.sp - cgoCtxt = gp.m.curg.cgoCtxt + frame.lr = gp.sched.lr + frame.sp = gp.sched.sp + stack = gp.stack + cgoCtxt = gp.cgoCtxt case funcID_systemstack: // systemstack returns normally, so just follow the // stack transition. - frame.sp = gp.m.curg.sched.sp - cgoCtxt = gp.m.curg.cgoCtxt + gp = gp.m.curg + frame.sp = gp.sched.sp + stack = gp.stack + cgoCtxt = gp.cgoCtxt flag &^= funcFlag_SPWRITE } } frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc, &cache)) if !usesLR { // On x86, call instruction pushes return PC before entering new function. - frame.fp += sys.PtrSize + frame.fp += goarch.PtrSize } } var flr funcInfo @@ -248,7 +233,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } } else { if frame.lr == 0 { - lrPtr = frame.fp - sys.PtrSize + lrPtr = frame.fp - goarch.PtrSize frame.lr = uintptr(*(*uintptr)(unsafe.Pointer(lrPtr))) } } @@ -267,8 +252,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in doPrint = false } if callback != nil || doPrint { - print("runtime: unexpected return pc for ", funcname(f), " called from ", hex(frame.lr), "\n") - tracebackHexdump(gp.stack, &frame, lrPtr) + print("runtime: g ", gp.goid, ": unexpected return pc for ", funcname(f), " called from ", hex(frame.lr), "\n") + tracebackHexdump(stack, &frame, lrPtr) } if callback != nil { throw("unknown caller pc") @@ -279,7 +264,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.varp = frame.fp if !usesLR { // On x86, call instruction pushes return PC before entering new function. - frame.varp -= sys.PtrSize + frame.varp -= goarch.PtrSize } // For architectures with frame pointers, if there's @@ -300,24 +285,10 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // And it happens to end up mimicking the x86 layout. // Other architectures may make different decisions. if frame.varp > frame.sp && framepointer_enabled { - frame.varp -= sys.PtrSize + frame.varp -= goarch.PtrSize } - // Derive size of arguments. - // Most functions have a fixed-size argument block, - // so we can use metadata about the function f. - // Not all, though: there are some variadic functions - // in package runtime and reflect, and for those we use call-specific - // metadata recorded by f's caller. - if callback != nil || printing { - frame.argp = frame.fp + sys.MinFrameSize - var ok bool - frame.arglen, frame.argmap, ok = getArgInfoFast(f, callback != nil) - if !ok { - frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil, ctxt) - } - } - ctxt = nil // ctxt is only needed to get arg maps for the topmost frame + frame.argp = frame.fp + sys.MinFrameSize // Determine frame's 'continuation PC', where it can continue. // Normally this is the return address on the stack, but if sigpanic @@ -331,7 +302,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in frame.continpc = frame.pc if waspanic { if frame.fn.deferreturn != 0 { - frame.continpc = frame.fn.entry + uintptr(frame.fn.deferreturn) + 1 + frame.continpc = frame.fn.entry() + uintptr(frame.fn.deferreturn) + 1 // Note: this may perhaps keep return variables alive longer than // strictly necessary, as we are using "function has a defer statement" // as a proxy for "function actually deferred something". It seems @@ -367,7 +338,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // See issue 34123. // The pc can be at function entry when the frame is initialized without // actually running code, like runtime.mstart. - if (n == 0 && flags&_TraceTrap != 0) || waspanic || pc == f.entry { + if (n == 0 && flags&_TraceTrap != 0) || waspanic || pc == f.entry() { pc++ } else { tracepc-- @@ -391,7 +362,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } lastFuncID = inltree[ix].funcID // Back up to an instruction in the "caller". - tracepc = frame.fn.entry + uintptr(inltree[ix].parentPc) + tracepc = frame.fn.entry() + uintptr(inltree[ix].parentPc) pc = tracepc + 1 } } @@ -418,7 +389,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // backup to CALL instruction to read inlining info (same logic as below) tracepc := frame.pc - if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic { + if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry() && !waspanic { tracepc-- } // If there is inlining info, print the inner frames. @@ -434,7 +405,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // Create a fake _func for the // inlined function. - inlFunc.nameoff = inltree[ix].func_ + inlFunc.nameOff = inltree[ix].nameOff inlFunc.funcID = inltree[ix].funcID if (flags&_TraceRuntimeFrames) != 0 || showframe(inlFuncInfo, gp, nprint == 0, inlFuncInfo.funcID, lastFuncID) { @@ -446,7 +417,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } lastFuncID = inltree[ix].funcID // Back up to an instruction in the "caller". - tracepc = frame.fn.entry + uintptr(inltree[ix].parentPc) + tracepc = frame.fn.entry() + uintptr(inltree[ix].parentPc) } } if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, f.funcID, lastFuncID) { @@ -461,13 +432,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } print(name, "(") argp := unsafe.Pointer(frame.argp) - printArgs(f, argp) + printArgs(f, argp, tracepc) print(")\n") print("\t", file, ":", line) - if frame.pc > f.entry { - print(" +", hex(frame.pc-f.entry)) + if frame.pc > f.entry() { + print(" +", hex(frame.pc-f.entry())) } - if gp.m != nil && gp.m.throwing > 0 && gp == gp.m.curg || level >= 2 { + if gp.m != nil && gp.m.throwing >= throwTypeRuntime && gp == gp.m.curg || level >= 2 { print(" fp=", hex(frame.fp), " sp=", hex(frame.sp), " pc=", hex(frame.pc)) } print("\n") @@ -490,20 +461,26 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } waspanic = f.funcID == funcID_sigpanic - injectedCall := waspanic || f.funcID == funcID_asyncPreempt + injectedCall := waspanic || f.funcID == funcID_asyncPreempt || f.funcID == funcID_debugCallV2 // Do not unwind past the bottom of the stack. if !flr.valid() { break } + if frame.pc == frame.lr && frame.sp == frame.fp { + // If the next frame is identical to the current frame, we cannot make progress. + print("runtime: traceback stuck. pc=", hex(frame.pc), " sp=", hex(frame.sp), "\n") + tracebackHexdump(stack, &frame, frame.sp) + throw("traceback stuck") + } + // Unwind to next frame. frame.fn = flr frame.pc = frame.lr frame.lr = 0 frame.sp = frame.fp frame.fp = 0 - frame.argmap = nil // On link register architectures, sighandler saves the LR on stack // before faking a call. @@ -574,7 +551,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in } // printArgs prints function arguments in traceback. -func printArgs(f funcInfo, argp unsafe.Pointer) { +func printArgs(f funcInfo, argp unsafe.Pointer, pc uintptr) { // The "instruction" of argument printing is encoded in _FUNCDATA_ArgInfo. // See cmd/compile/internal/ssagen.emitArgInfo for the description of the // encoding. @@ -598,18 +575,39 @@ func printArgs(f funcInfo, argp unsafe.Pointer) { return } - print1 := func(off, sz uint8) { + liveInfo := funcdata(f, _FUNCDATA_ArgLiveInfo) + liveIdx := pcdatavalue(f, _PCDATA_ArgLiveIndex, pc, nil) + startOffset := uint8(0xff) // smallest offset that needs liveness info (slots with a lower offset is always live) + if liveInfo != nil { + startOffset = *(*uint8)(liveInfo) + } + + isLive := func(off, slotIdx uint8) bool { + if liveInfo == nil || liveIdx <= 0 { + return true // no liveness info, always live + } + if off < startOffset { + return true + } + bits := *(*uint8)(add(liveInfo, uintptr(liveIdx)+uintptr(slotIdx/8))) + return bits&(1<<(slotIdx%8)) != 0 + } + + print1 := func(off, sz, slotIdx uint8) { x := readUnaligned64(add(argp, uintptr(off))) - // mask out irrelavant bits + // mask out irrelevant bits if sz < 8 { shift := 64 - sz*8 - if sys.BigEndian { + if goarch.BigEndian { x = x >> shift } else { x = x << shift >> shift } } print(hex(x)) + if !isLive(off, slotIdx) { + print("?") + } } start := true @@ -619,6 +617,7 @@ func printArgs(f funcInfo, argp unsafe.Pointer) { } } pi := 0 + slotIdx := uint8(0) // register arg spill slot index printloop: for { o := p[pi] @@ -643,78 +642,13 @@ printloop: printcomma() sz := p[pi] pi++ - print1(o, sz) - } - start = false - } -} - -// reflectMethodValue is a partial duplicate of reflect.makeFuncImpl -// and reflect.methodValue. -type reflectMethodValue struct { - fn uintptr - stack *bitvector // ptrmap for both args and results - argLen uintptr // just args -} - -// getArgInfoFast returns the argument frame information for a call to f. -// It is short and inlineable. However, it does not handle all functions. -// If ok reports false, you must call getArgInfo instead. -// TODO(josharian): once we do mid-stack inlining, -// call getArgInfo directly from getArgInfoFast and stop returning an ok bool. -func getArgInfoFast(f funcInfo, needArgMap bool) (arglen uintptr, argmap *bitvector, ok bool) { - return uintptr(f.args), nil, !(needArgMap && f.args == _ArgsSizeUnknown) -} - -// getArgInfo returns the argument frame information for a call to f -// with call frame frame. -// -// This is used for both actual calls with active stack frames and for -// deferred calls or goroutines that are not yet executing. If this is an actual -// call, ctxt must be nil (getArgInfo will retrieve what it needs from -// the active stack frame). If this is a deferred call or unstarted goroutine, -// ctxt must be the function object that was deferred or go'd. -func getArgInfo(frame *stkframe, f funcInfo, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) { - arglen = uintptr(f.args) - if needArgMap && f.args == _ArgsSizeUnknown { - // Extract argument bitmaps for reflect stubs from the calls they made to reflect. - switch funcname(f) { - case "reflect.makeFuncStub", "reflect.methodValueCall": - // These take a *reflect.methodValue as their - // context register. - var mv *reflectMethodValue - var retValid bool - if ctxt != nil { - // This is not an actual call, but a - // deferred call or an unstarted goroutine. - // The function value is itself the *reflect.methodValue. - mv = (*reflectMethodValue)(unsafe.Pointer(ctxt)) - } else { - // This is a real call that took the - // *reflect.methodValue as its context - // register and immediately saved it - // to 0(SP). Get the methodValue from - // 0(SP). - arg0 := frame.sp + sys.MinFrameSize - mv = *(**reflectMethodValue)(unsafe.Pointer(arg0)) - // Figure out whether the return values are valid. - // Reflect will update this value after it copies - // in the return values. - retValid = *(*bool)(unsafe.Pointer(arg0 + 4*sys.PtrSize)) + print1(o, sz, slotIdx) + if o >= startOffset { + slotIdx++ } - if mv.fn != f.entry { - print("runtime: confused by ", funcname(f), "\n") - throw("reflect mismatch") - } - bv := mv.stack - arglen = uintptr(bv.n * sys.PtrSize) - if !retValid { - arglen = uintptr(mv.argLen) &^ (sys.PtrSize - 1) - } - argmap = bv } + start = false } - return } // tracebackCgoContext handles tracing back a cgo context value, from @@ -762,13 +696,13 @@ func printcreatedby(gp *g) { func printcreatedby1(f funcInfo, pc uintptr) { print("created by ", funcname(f), "\n") tracepc := pc // back up to CALL instruction for funcline. - if pc > f.entry { + if pc > f.entry() { tracepc -= sys.PCQuantum } file, line := funcline(f, tracepc) print("\t", file, ":", line) - if pc > f.entry { - print(" +", hex(pc-f.entry)) + if pc > f.entry() { + print(" +", hex(pc-f.entry())) } print("\n") } @@ -803,24 +737,31 @@ func traceback1(pc, sp, lr uintptr, gp *g, flags uint) { // concurrently with a signal handler. // We just have to stop a signal handler from interrupting // in the middle of our copy. - atomic.Store(&gp.m.cgoCallersUse, 1) + gp.m.cgoCallersUse.Store(1) cgoCallers := *gp.m.cgoCallers gp.m.cgoCallers[0] = 0 - atomic.Store(&gp.m.cgoCallersUse, 0) + gp.m.cgoCallersUse.Store(0) printCgoTraceback(&cgoCallers) } - var n int if readgstatus(gp)&^_Gscan == _Gsyscall { // Override registers if blocked in system call. pc = gp.syscallpc sp = gp.syscallsp flags &^= _TraceTrap } + if gp.m != nil && gp.m.vdsoSP != 0 { + // Override registers if running in VDSO. This comes after the + // _Gsyscall check to cover VDSO calls after entersyscall. + pc = gp.m.vdsoPC + sp = gp.m.vdsoSP + flags &^= _TraceTrap + } + // Print traceback. By default, omits runtime frames. // If that means we print nothing at all, repeat forcing all frames printed. - n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags) + n := gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags) if n == 0 && (flags&_TraceRuntimeFrames) == 0 { n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags|_TraceRuntimeFrames) } @@ -867,7 +808,7 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) { inltree := (*[1 << 20]inlinedCall)(inldata) ix := pcdatavalue(f, _PCDATA_InlTreeIndex, pc, nil) if ix >= 0 { - name = funcnameFromNameoff(f, inltree[ix].func_) + name = funcnameFromNameOff(f, inltree[ix].nameOff) } } file, line := funcline(f, pc) @@ -876,8 +817,8 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) { } print(name, "(...)\n") print("\t", file, ":", line) - if pc > f.entry { - print(" +", hex(pc-f.entry)) + if pc > f.entry() { + print(" +", hex(pc-f.entry())) } print("\n") } @@ -900,8 +841,8 @@ func gcallers(gp *g, skip int, pcbuf []uintptr) int { // showframe reports whether the frame with the given characteristics should // be printed during a traceback. func showframe(f funcInfo, gp *g, firstFrame bool, funcID, childID funcID) bool { - g := getg() - if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) { + mp := getg().m + if mp.throwing >= throwTypeRuntime && gp != nil && (gp == mp.curg || gp == mp.caughtsig.ptr()) { return true } return showfuncinfo(f, firstFrame, funcID, childID) @@ -911,7 +852,7 @@ func showframe(f funcInfo, gp *g, firstFrame bool, funcID, childID funcID) bool // be printed during a traceback. func showfuncinfo(f funcInfo, firstFrame bool, funcID, childID funcID) bool { // Note that f may be a synthesized funcInfo for an inlined - // function, in which case only nameoff and funcID are set. + // function, in which case only nameOff and funcID are set. level, _, _ := gotraceback() if level > 1 { @@ -1028,10 +969,10 @@ func tracebackothers(me *g) { } print("\n") goroutineheader(gp) - // Note: gp.m == g.m occurs when tracebackothers is - // called from a signal handler initiated during a - // systemstack call. The original G is still in the - // running state, and we want to print its stack. + // Note: gp.m == getg().m occurs when tracebackothers is called + // from a signal handler initiated during a systemstack call. + // The original G is still in the running state, and we want to + // print its stack. if gp.m != getg().m && readgstatus(gp)&^_Gscan == _Grunning { print("\tgoroutine running on other thread; stack unavailable\n") printcreatedby(gp) @@ -1045,8 +986,8 @@ func tracebackothers(me *g) { // for debugging purposes. If the address bad is included in the // hexdumped range, it will mark it as well. func tracebackHexdump(stk stack, frame *stkframe, bad uintptr) { - const expand = 32 * sys.PtrSize - const maxExpand = 256 * sys.PtrSize + const expand = 32 * goarch.PtrSize + const maxExpand = 256 * goarch.PtrSize // Start around frame.sp. lo, hi := frame.sp, frame.sp // Expand to include frame.fp. @@ -1097,7 +1038,7 @@ func tracebackHexdump(stk stack, frame *stkframe, bad uintptr) { // system (that is, the finalizer goroutine) is considered a user // goroutine. func isSystemGoroutine(gp *g, fixed bool) bool { - // Keep this in sync with cmd/trace/trace.go:isSystemGoroutine. + // Keep this in sync with internal/trace.IsSystemGoroutine. f := findfunc(gp.startpc) if !f.valid() { return false @@ -1113,7 +1054,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool { // always consider it a user goroutine. return false } - return !fingRunning + return fingStatus.Load()&fingRunningFinalizer == 0 } return hasPrefix(funcname(f), "runtime.") } @@ -1217,9 +1158,9 @@ func isSystemGoroutine(gp *g, fixed bool) bool { // // On all platforms, the traceback function is invoked when a call from // Go to C to Go requests a stack trace. On linux/amd64, linux/ppc64le, -// and freebsd/amd64, the traceback function is also invoked when a -// signal is received by a thread that is executing a cgo call. The -// traceback function should not make assumptions about when it is +// linux/arm64, and freebsd/amd64, the traceback function is also invoked +// when a signal is received by a thread that is executing a cgo call. +// The traceback function should not make assumptions about when it is // called, as future versions of Go may make additional calls. // // The symbolizer function will be called with a single argument, a @@ -1384,7 +1325,7 @@ func printOneCgoTraceback(pc uintptr, max int, arg *cgoSymbolizerArg) int { // callCgoSymbolizer calls the cgoSymbolizer function. func callCgoSymbolizer(arg *cgoSymbolizerArg) { call := cgocall - if panicking > 0 || getg().m.curg != getg() { + if panicking.Load() > 0 || getg().m.curg != getg() { // We do not want to call into the scheduler when panicking // or when on the system stack. call = asmcgocall @@ -1392,6 +1333,9 @@ func callCgoSymbolizer(arg *cgoSymbolizerArg) { if msanenabled { msanwrite(unsafe.Pointer(arg), unsafe.Sizeof(cgoSymbolizerArg{})) } + if asanenabled { + asanwrite(unsafe.Pointer(arg), unsafe.Sizeof(cgoSymbolizerArg{})) + } call(cgoSymbolizer, noescape(unsafe.Pointer(arg))) } @@ -1401,7 +1345,7 @@ func cgoContextPCs(ctxt uintptr, buf []uintptr) { return } call := cgocall - if panicking > 0 || getg().m.curg != getg() { + if panicking.Load() > 0 || getg().m.curg != getg() { // We do not want to call into the scheduler when panicking // or when on the system stack. call = asmcgocall @@ -1414,5 +1358,8 @@ func cgoContextPCs(ctxt uintptr, buf []uintptr) { if msanenabled { msanwrite(unsafe.Pointer(&arg), unsafe.Sizeof(arg)) } + if asanenabled { + asanwrite(unsafe.Pointer(&arg), unsafe.Sizeof(arg)) + } call(cgoTraceback, noescape(unsafe.Pointer(&arg))) } diff --git a/src/runtime/traceback_test.go b/src/runtime/traceback_test.go index 83b86a7e909acd..97eb92103b47d3 100644 --- a/src/runtime/traceback_test.go +++ b/src/runtime/traceback_test.go @@ -6,6 +6,8 @@ package runtime_test import ( "bytes" + "internal/abi" + "internal/testenv" "runtime" "testing" ) @@ -13,6 +15,19 @@ import ( var testTracebackArgsBuf [1000]byte func TestTracebackArgs(t *testing.T) { + if *flagQuick { + t.Skip("-quick") + } + optimized := !testenv.OptimizationOff() + abiSel := func(x, y string) string { + // select expected output based on ABI + // In noopt build we always spill arguments so the output is the same as stack ABI. + if optimized && abi.IntArgRegs > 0 { + return x + } + return y + } + tests := []struct { fn func() int expect string @@ -105,6 +120,52 @@ func TestTracebackArgs(t *testing.T) { func() int { return testTracebackArgs8d(testArgsType8d{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}, 12}) }, "testTracebackArgs8d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}, ...})", }, + + // Register argument liveness. + // 1, 3 are used and live, 2, 4 are dead (in register ABI). + // Address-taken (7) and stack ({5, 6}) args are always live. + { + func() int { + poisonStack() // poison arg area to make output deterministic + return testTracebackArgs9(1, 2, 3, 4, [2]int{5, 6}, 7) + }, + abiSel( + "testTracebackArgs9(0x1, 0xffffffff?, 0x3, 0xff?, {0x5, 0x6}, 0x7)", + "testTracebackArgs9(0x1, 0x2, 0x3, 0x4, {0x5, 0x6}, 0x7)"), + }, + // No live. + // (Note: this assume at least 5 int registers if register ABI is used.) + { + func() int { + poisonStack() // poison arg area to make output deterministic + return testTracebackArgs10(1, 2, 3, 4, 5) + }, + abiSel( + "testTracebackArgs10(0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?)", + "testTracebackArgs10(0x1, 0x2, 0x3, 0x4, 0x5)"), + }, + // Conditional spills. + // Spill in conditional, not executed. + { + func() int { + poisonStack() // poison arg area to make output deterministic + return testTracebackArgs11a(1, 2, 3) + }, + abiSel( + "testTracebackArgs11a(0xffffffff?, 0xffffffff?, 0xffffffff?)", + "testTracebackArgs11a(0x1, 0x2, 0x3)"), + }, + // 2 spills in conditional, not executed; 3 spills in conditional, executed, but not statically known. + // So print 0x3?. + { + func() int { + poisonStack() // poison arg area to make output deterministic + return testTracebackArgs11b(1, 2, 3, 4) + }, + abiSel( + "testTracebackArgs11b(0xffffffff?, 0xffffffff?, 0x3?, 0x4)", + "testTracebackArgs11b(0x1, 0x2, 0x3, 0x4)"), + }, } for _, test := range tests { n := test.fn() @@ -290,3 +351,72 @@ func testTracebackArgs8d(a testArgsType8d) int { } return n } + +// nosplit to avoid preemption or morestack spilling registers. +// +//go:nosplit +//go:noinline +func testTracebackArgs9(a int64, b int32, c int16, d int8, x [2]int, y int) int { + if a < 0 { + println(&y) // take address, make y live, even if no longer used at traceback + } + n := runtime.Stack(testTracebackArgsBuf[:], false) + if a < 0 { + // use half of in-reg args to keep them alive, the other half are dead + return int(a) + int(c) + } + return n +} + +// nosplit to avoid preemption or morestack spilling registers. +// +//go:nosplit +//go:noinline +func testTracebackArgs10(a, b, c, d, e int32) int { + // no use of any args + return runtime.Stack(testTracebackArgsBuf[:], false) +} + +// norace to avoid race instrumentation changing spill locations. +// nosplit to avoid preemption or morestack spilling registers. +// +//go:norace +//go:nosplit +//go:noinline +func testTracebackArgs11a(a, b, c int32) int { + if a < 0 { + println(a, b, c) // spill in a conditional, may not execute + } + if b < 0 { + return int(a + b + c) + } + return runtime.Stack(testTracebackArgsBuf[:], false) +} + +// norace to avoid race instrumentation changing spill locations. +// nosplit to avoid preemption or morestack spilling registers. +// +//go:norace +//go:nosplit +//go:noinline +func testTracebackArgs11b(a, b, c, d int32) int { + var x int32 + if a < 0 { + print() // spill b in a conditional + x = b + } else { + print() // spill c in a conditional + x = c + } + if d < 0 { // d is always needed + return int(x + d) + } + return runtime.Stack(testTracebackArgsBuf[:], false) +} + +// Poison the arg area with deterministic values. +// +//go:noinline +func poisonStack() [20]int { + return [20]int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} +} diff --git a/src/runtime/type.go b/src/runtime/type.go index 4039273695b106..e8e7819ecfe6cd 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -6,15 +6,19 @@ package runtime -import "unsafe" +import ( + "internal/abi" + "unsafe" +) // tflag is documented in reflect/type.go. // // tflag values must be kept in sync with copies in: +// // cmd/compile/internal/reflectdata/reflect.go // cmd/link/internal/ld/decodesym.go // reflect/type.go -// internal/reflectlite/type.go +// internal/reflectlite/type.go type tflag uint8 const ( @@ -123,7 +127,14 @@ func (t *_type) name() string { } s := t.string() i := len(s) - 1 - for i >= 0 && s[i] != '.' { + sqBrackets := 0 + for i >= 0 && (s[i] != '.' || sqBrackets != 0) { + switch s[i] { + case ']': + sqBrackets++ + case '[': + sqBrackets-- + } i-- } return s[i+1:] @@ -262,7 +273,7 @@ func (t *_type) textOff(off textOff) unsafe.Pointer { if off == -1 { // -1 is the sentinel value for unreachable code. // See cmd/link/internal/ld/data.go:relocsym. - return unsafe.Pointer(funcPC(unreachableMethod)) + return unsafe.Pointer(abi.FuncPCABIInternal(unreachableMethod)) } base := uintptr(unsafe.Pointer(t)) var md *moduledata @@ -285,34 +296,7 @@ func (t *_type) textOff(off textOff) unsafe.Pointer { } return res } - res := uintptr(0) - - // The text, or instruction stream is generated as one large buffer. The off (offset) for a method is - // its offset within this buffer. If the total text size gets too large, there can be issues on platforms like ppc64 if - // the target of calls are too far for the call instruction. To resolve the large text issue, the text is split - // into multiple text sections to allow the linker to generate long calls when necessary. When this happens, the vaddr - // for each text section is set to its offset within the text. Each method's offset is compared against the section - // vaddrs and sizes to determine the containing section. Then the section relative offset is added to the section's - // relocated baseaddr to compute the method addess. - - if len(md.textsectmap) > 1 { - for i := range md.textsectmap { - sectaddr := md.textsectmap[i].vaddr - sectlen := md.textsectmap[i].length - if uintptr(off) >= sectaddr && uintptr(off) < sectaddr+sectlen { - res = md.textsectmap[i].baseaddr + uintptr(off) - uintptr(md.textsectmap[i].vaddr) - break - } - } - } else { - // single text section - res = md.text + uintptr(off) - } - - if res > md.etext && GOARCH != "wasm" { // on wasm, functions do not live in the same address space as the linear memory - println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext)) - throw("runtime: text offset out of range") - } + res := md.textAddr(uint32(off)) return unsafe.Pointer(res) } @@ -430,13 +414,9 @@ type ptrtype struct { } type structfield struct { - name name - typ *_type - offsetAnon uintptr -} - -func (f *structfield) offset() uintptr { - return f.offsetAnon >> 1 + name name + typ *_type + offset uintptr } type structtype struct { @@ -459,6 +439,10 @@ func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } +func (n name) isEmbedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + func (n name) readvarint(off int) (int, int) { v := 0 for i := 0; ; i++ { @@ -719,7 +703,10 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { if tf.name.tag() != vf.name.tag() { return false } - if tf.offsetAnon != vf.offsetAnon { + if tf.offset != vf.offset { + return false + } + if tf.name.isEmbedded() != vf.name.isEmbedded() { return false } } diff --git a/src/runtime/unsafe.go b/src/runtime/unsafe.go new file mode 100644 index 00000000000000..54649e8ff5da72 --- /dev/null +++ b/src/runtime/unsafe.go @@ -0,0 +1,98 @@ +// Copyright 2022 The Go Authors. 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/math" + "unsafe" +) + +func unsafestring(ptr unsafe.Pointer, len int) { + if len < 0 { + panicunsafestringlen() + } + + if uintptr(len) > -uintptr(ptr) { + if ptr == nil { + panicunsafestringnilptr() + } + panicunsafestringlen() + } +} + +// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeString +func unsafestring64(ptr unsafe.Pointer, len64 int64) { + len := int(len64) + if int64(len) != len64 { + panicunsafestringlen() + } + unsafestring(ptr, len) +} + +func unsafestringcheckptr(ptr unsafe.Pointer, len64 int64) { + unsafestring64(ptr, len64) + + // Check that underlying array doesn't straddle multiple heap objects. + // unsafestring64 has already checked for overflow. + if checkptrStraddles(ptr, uintptr(len64)) { + throw("checkptr: unsafe.String result straddles multiple allocations") + } +} + +func panicunsafestringlen() { + panic(errorString("unsafe.String: len out of range")) +} + +func panicunsafestringnilptr() { + panic(errorString("unsafe.String: ptr is nil and len is not zero")) +} + +// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice +func unsafeslice(et *_type, ptr unsafe.Pointer, len int) { + if len < 0 { + panicunsafeslicelen() + } + + if et.size == 0 { + if ptr == nil && len > 0 { + panicunsafeslicenilptr() + } + } + + mem, overflow := math.MulUintptr(et.size, uintptr(len)) + if overflow || mem > -uintptr(ptr) { + if ptr == nil { + panicunsafeslicenilptr() + } + panicunsafeslicelen() + } +} + +// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice +func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) { + len := int(len64) + if int64(len) != len64 { + panicunsafeslicelen() + } + unsafeslice(et, ptr, len) +} + +func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) { + unsafeslice64(et, ptr, len64) + + // Check that underlying array doesn't straddle multiple heap objects. + // unsafeslice64 has already checked for overflow. + if checkptrStraddles(ptr, uintptr(len64)*et.size) { + throw("checkptr: unsafe.Slice result straddles multiple allocations") + } +} + +func panicunsafeslicelen() { + panic(errorString("unsafe.Slice: len out of range")) +} + +func panicunsafeslicenilptr() { + panic(errorString("unsafe.Slice: ptr is nil and len is not zero")) +} diff --git a/src/runtime/vdso_elf32.go b/src/runtime/vdso_elf32.go index 456173b0f51c14..1b8afbedf45792 100644 --- a/src/runtime/vdso_elf32.go +++ b/src/runtime/vdso_elf32.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (386 || arm) -// +build linux -// +build 386 arm package runtime diff --git a/src/runtime/vdso_elf64.go b/src/runtime/vdso_elf64.go index 9923bd46971e5f..d41d25e7705765 100644 --- a/src/runtime/vdso_elf64.go +++ b/src/runtime/vdso_elf64.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && (amd64 || arm64 || mips64 || mips64le || ppc64 || ppc64le) -// +build linux -// +build amd64 arm64 mips64 mips64le ppc64 ppc64le +//go:build linux && (amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x) package runtime diff --git a/src/runtime/vdso_freebsd.go b/src/runtime/vdso_freebsd.go index 7ca7b2810bf0de..0fe21cf6470446 100644 --- a/src/runtime/vdso_freebsd.go +++ b/src/runtime/vdso_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd -// +build freebsd package runtime diff --git a/src/runtime/vdso_freebsd_arm64.go b/src/runtime/vdso_freebsd_arm64.go index 7d9f62d5f928ca..37b26d73e78801 100644 --- a/src/runtime/vdso_freebsd_arm64.go +++ b/src/runtime/vdso_freebsd_arm64.go @@ -14,7 +14,7 @@ func getCntxct(physical bool) uint32 func (th *vdsoTimehands) getTimecounter() (uint32, bool) { switch th.algo { case _VDSO_TH_ALGO_ARM_GENTIM: - return getCntxct(false), true + return getCntxct(th.physical != 0), true default: return 0, false } diff --git a/src/runtime/vdso_freebsd_x86.go b/src/runtime/vdso_freebsd_x86.go index 23a5a8c322707a..66d1c654885ab8 100644 --- a/src/runtime/vdso_freebsd_x86.go +++ b/src/runtime/vdso_freebsd_x86.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd && (386 || amd64) -// +build freebsd -// +build 386 amd64 package runtime @@ -36,10 +34,8 @@ func (th *vdsoTimehands) getTSCTimecounter() uint32 { return uint32(tsc) } -//go:systemstack +//go:nosplit func (th *vdsoTimehands) getHPETTimecounter() (uint32, bool) { - const digits = "0123456789" - idx := int(th.x86_hpet_idx) if idx >= len(hpetDevMap) { return 0, false @@ -47,25 +43,7 @@ func (th *vdsoTimehands) getHPETTimecounter() (uint32, bool) { p := atomic.Loaduintptr(&hpetDevMap[idx]) if p == 0 { - var devPath [len(hpetDevPath)]byte - copy(devPath[:], hpetDevPath) - devPath[9] = digits[idx] - - fd := open(&devPath[0], 0 /* O_RDONLY */, 0) - if fd < 0 { - atomic.Casuintptr(&hpetDevMap[idx], 0, ^uintptr(0)) - return 0, false - } - - addr, mmapErr := mmap(nil, physPageSize, _PROT_READ, _MAP_SHARED, fd, 0) - closefd(fd) - newP := uintptr(addr) - if mmapErr != 0 { - newP = ^uintptr(0) - } - if !atomic.Casuintptr(&hpetDevMap[idx], 0, newP) && mmapErr == 0 { - munmap(addr, physPageSize) - } + systemstack(func() { initHPETTimecounter(idx) }) p = atomic.Loaduintptr(&hpetDevMap[idx]) } if p == ^uintptr(0) { @@ -74,20 +52,38 @@ func (th *vdsoTimehands) getHPETTimecounter() (uint32, bool) { return *(*uint32)(unsafe.Pointer(p + _HPET_MAIN_COUNTER)), true } +//go:systemstack +func initHPETTimecounter(idx int) { + const digits = "0123456789" + + var devPath [len(hpetDevPath)]byte + copy(devPath[:], hpetDevPath) + devPath[9] = digits[idx] + + fd := open(&devPath[0], 0 /* O_RDONLY */ |_O_CLOEXEC, 0) + if fd < 0 { + atomic.Casuintptr(&hpetDevMap[idx], 0, ^uintptr(0)) + return + } + + addr, mmapErr := mmap(nil, physPageSize, _PROT_READ, _MAP_SHARED, fd, 0) + closefd(fd) + newP := uintptr(addr) + if mmapErr != 0 { + newP = ^uintptr(0) + } + if !atomic.Casuintptr(&hpetDevMap[idx], 0, newP) && mmapErr == 0 { + munmap(addr, physPageSize) + } +} + //go:nosplit func (th *vdsoTimehands) getTimecounter() (uint32, bool) { switch th.algo { case _VDSO_TH_ALGO_X86_TSC: return th.getTSCTimecounter(), true case _VDSO_TH_ALGO_X86_HPET: - var ( - tc uint32 - ok bool - ) - systemstack(func() { - tc, ok = th.getHPETTimecounter() - }) - return tc, ok + return th.getHPETTimecounter() default: return 0, false } diff --git a/src/runtime/vdso_in_none.go b/src/runtime/vdso_in_none.go index c66fbf821669fd..3a6ee6f049dcde 100644 --- a/src/runtime/vdso_in_none.go +++ b/src/runtime/vdso_in_none.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (linux && !386 && !amd64 && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le) || !linux -// +build linux,!386,!amd64,!arm,!arm64,!mips64,!mips64le,!ppc64,!ppc64le !linux +//go:build (linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !riscv64 && !s390x) || !linux package runtime diff --git a/src/runtime/vdso_linux.go b/src/runtime/vdso_linux.go index ae211f96b1b058..45236157116149 100644 --- a/src/runtime/vdso_linux.go +++ b/src/runtime/vdso_linux.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && (386 || amd64 || arm || arm64 || mips64 || mips64le || ppc64 || ppc64le) -// +build linux -// +build 386 amd64 arm arm64 mips64 mips64le ppc64 ppc64le +//go:build linux && (386 || amd64 || arm || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x) package runtime @@ -234,9 +232,11 @@ func vdsoParseSymbols(info *vdsoInfo, version int32) { if !info.isGNUHash { // Old-style DT_HASH table. for _, k := range vdsoSymbolKeys { - for chain := info.bucket[k.symHash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] { - if apply(chain, k) { - break + if len(info.bucket) > 0 { + for chain := info.bucket[k.symHash%uint32(len(info.bucket))]; chain != 0; chain = info.chain[chain] { + if apply(chain, k) { + break + } } } } @@ -282,6 +282,7 @@ func vdsoauxv(tag, val uintptr) { } // vdsoMarker reports whether PC is on the VDSO page. +// //go:nosplit func inVDSOPage(pc uintptr) bool { for _, k := range vdsoSymbolKeys { diff --git a/src/runtime/vdso_linux_loong64.go b/src/runtime/vdso_linux_loong64.go new file mode 100644 index 00000000000000..e00ef952b376d8 --- /dev/null +++ b/src/runtime/vdso_linux_loong64.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 + +package runtime + +const ( + // vdsoArrayMax is the byte-size of a maximally sized array on this architecture. + // See cmd/compile/internal/loong64/galign.go arch.MAXWIDTH initialization. + vdsoArrayMax = 1<<50 - 1 +) + +// not currently described in manpages as of May 2022, but will eventually +// appear +// when that happens, see man 7 vdso : loongarch +var vdsoLinuxVersion = vdsoVersionKey{"LINUX_5.10", 0xae78f70} + +var vdsoSymbolKeys = []vdsoSymbolKey{ + {"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym}, +} + +// initialize to fall back to syscall +var ( + vdsoClockgettimeSym uintptr = 0 +) diff --git a/src/runtime/vdso_linux_mips64x.go b/src/runtime/vdso_linux_mips64x.go index 395ddbba69a730..1444f8e5246aec 100644 --- a/src/runtime/vdso_linux_mips64x.go +++ b/src/runtime/vdso_linux_mips64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package runtime diff --git a/src/runtime/vdso_linux_ppc64x.go b/src/runtime/vdso_linux_ppc64x.go index b741dbfcdc527d..09c8d9d20ed8a8 100644 --- a/src/runtime/vdso_linux_ppc64x.go +++ b/src/runtime/vdso_linux_ppc64x.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package runtime diff --git a/src/runtime/vdso_linux_riscv64.go b/src/runtime/vdso_linux_riscv64.go new file mode 100644 index 00000000000000..f427124c3c4951 --- /dev/null +++ b/src/runtime/vdso_linux_riscv64.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. 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 + +const ( + // vdsoArrayMax is the byte-size of a maximally sized array on this architecture. + // See cmd/compile/internal/riscv64/galign.go arch.MAXWIDTH initialization. + vdsoArrayMax = 1<<50 - 1 +) + +// key and version at man 7 vdso : riscv +var vdsoLinuxVersion = vdsoVersionKey{"LINUX_4.15", 0xae77f75} + +var vdsoSymbolKeys = []vdsoSymbolKey{ + {"__vdso_clock_gettime", 0xd35ec75, 0x6e43a318, &vdsoClockgettimeSym}, +} + +// initialize to fall back to syscall +var vdsoClockgettimeSym uintptr = 0 diff --git a/src/runtime/vdso_linux_s390x.go b/src/runtime/vdso_linux_s390x.go new file mode 100644 index 00000000000000..c1c0b1baa47c7d --- /dev/null +++ b/src/runtime/vdso_linux_s390x.go @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && s390x +// +build linux,s390x + +package runtime + +const ( + // vdsoArrayMax is the byte-size of a maximally sized array on this architecture. + // See cmd/compile/internal/s390x/galign.go arch.MAXWIDTH initialization. + vdsoArrayMax = 1<<50 - 1 +) + +var vdsoLinuxVersion = vdsoVersionKey{"LINUX_2.6.29", 0x75fcbb9} + +var vdsoSymbolKeys = []vdsoSymbolKey{ + {"__kernel_clock_gettime", 0xb0cd725, 0xdfa941fd, &vdsoClockgettimeSym}, +} + +// initialize with vsyscall fallbacks +var ( + vdsoClockgettimeSym uintptr = 0 +) diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go index cf631bdcca7d24..4b12f593c8a8ec 100644 --- a/src/runtime/vlrt.go +++ b/src/runtime/vlrt.go @@ -24,7 +24,6 @@ // THE SOFTWARE. //go:build arm || 386 || mips || mipsle -// +build arm 386 mips mipsle package runtime @@ -59,6 +58,36 @@ func uint64tofloat64(y uint64) float64 { return d } +func int64tofloat32(y int64) float32 { + if y < 0 { + return -uint64tofloat32(-uint64(y)) + } + return uint64tofloat32(uint64(y)) +} + +func uint64tofloat32(y uint64) float32 { + // divide into top 18, mid 23, and bottom 23 bits. + // (23-bit integers fit into a float32 without loss.) + top := uint32(y >> 46) + mid := uint32(y >> 23 & (1<<23 - 1)) + bot := uint32(y & (1<<23 - 1)) + if top == 0 { + return float32(mid)*(1<<23) + float32(bot) + } + if bot != 0 { + // Top is not zero, so the bits in bot + // won't make it into the final mantissa. + // In fact, the bottom bit of mid won't + // make it into the mantissa either. + // We only need to make sure that if top+mid + // is about to round down in a round-to-even + // scenario, and bot is not zero, we make it + // round up instead. + mid |= 1 + } + return float32(top)*(1<<46) + float32(mid)*(1<<23) +} + func _d2v(y *uint64, d float64) { x := *(*uint64)(unsafe.Pointer(&d)) @@ -267,11 +296,14 @@ func slowdodiv(n, d uint64) (q, r uint64) { // Floating point control word values. // Bits 0-5 are bits to disable floating-point exceptions. // Bits 8-9 are the precision control: -// 0 = single precision a.k.a. float32 -// 2 = double precision a.k.a. float64 +// +// 0 = single precision a.k.a. float32 +// 2 = double precision a.k.a. float64 +// // Bits 10-11 are the rounding mode: -// 0 = round to nearest (even on a tie) -// 3 = round toward zero +// +// 0 = round to nearest (even on a tie) +// 3 = round toward zero var ( controlWord64 uint16 = 0x3f + 2<<8 + 0<<10 controlWord64trunc uint16 = 0x3f + 2<<8 + 3<<10 diff --git a/src/runtime/wincallback.go b/src/runtime/wincallback.go index a7a787d8f6d694..442a98470837b8 100644 --- a/src/runtime/wincallback.go +++ b/src/runtime/wincallback.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // Generate Windows callback assembly file. @@ -23,7 +22,6 @@ func genasm386Amd64() { buf.WriteString(`// Code generated by wincallback.go using 'go generate'. DO NOT EDIT. //go:build 386 || amd64 -// +build 386 amd64 // runtime·callbackasm is called by external code to // execute Go implemented callback function. It is not @@ -33,7 +31,7 @@ func genasm386Amd64() { // CALL instruction in runtime·callbackasm. This determines // which Go callback function is executed later on. -TEXT runtime·callbackasm(SB),7,$0 +TEXT runtime·callbackasm(SB),7,$0 `) for i := 0; i < maxCallback; i++ { buf.WriteString("\tCALL\truntime·callbackasm1(SB)\n") @@ -61,7 +59,7 @@ func genasmArm() { // It then calls the Go implementation for that callback. #include "textflag.h" -TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 `) for i := 0; i < maxCallback; i++ { buf.WriteString(fmt.Sprintf("\tMOVW\t$%d, R12\n", i)) @@ -89,7 +87,7 @@ func genasmArm64() { // It then calls the Go implementation for that callback. #include "textflag.h" -TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 `) for i := 0; i < maxCallback; i++ { buf.WriteString(fmt.Sprintf("\tMOVD\t$%d, R12\n", i)) diff --git a/src/runtime/write_err.go b/src/runtime/write_err.go index a4656fd72810d7..81ae872e9c035d 100644 --- a/src/runtime/write_err.go +++ b/src/runtime/write_err.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !android -// +build !android package runtime diff --git a/src/runtime/zcallback_windows.s b/src/runtime/zcallback_windows.s index e451c2b9d0508b..bd23d71333ba06 100644 --- a/src/runtime/zcallback_windows.s +++ b/src/runtime/zcallback_windows.s @@ -1,7 +1,6 @@ // Code generated by wincallback.go using 'go generate'. DO NOT EDIT. //go:build 386 || amd64 -// +build 386 amd64 // runtime·callbackasm is called by external code to // execute Go implemented callback function. It is not @@ -11,7 +10,7 @@ // CALL instruction in runtime·callbackasm. This determines // which Go callback function is executed later on. -TEXT runtime·callbackasm(SB),7,$0 +TEXT runtime·callbackasm(SB),7,$0 CALL runtime·callbackasm1(SB) CALL runtime·callbackasm1(SB) CALL runtime·callbackasm1(SB) diff --git a/src/runtime/zcallback_windows_arm.s b/src/runtime/zcallback_windows_arm.s index a73a813acb3c5d..f943d84cbfe5fd 100644 --- a/src/runtime/zcallback_windows_arm.s +++ b/src/runtime/zcallback_windows_arm.s @@ -9,7 +9,7 @@ // It then calls the Go implementation for that callback. #include "textflag.h" -TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 MOVW $0, R12 B runtime·callbackasm1(SB) MOVW $1, R12 diff --git a/src/runtime/zcallback_windows_arm64.s b/src/runtime/zcallback_windows_arm64.s index 2a6bda0990c108..69fb05788cdab2 100644 --- a/src/runtime/zcallback_windows_arm64.s +++ b/src/runtime/zcallback_windows_arm64.s @@ -9,7 +9,7 @@ // It then calls the Go implementation for that callback. #include "textflag.h" -TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 +TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0 MOVD $0, R12 B runtime·callbackasm1(SB) MOVD $1, R12 diff --git a/src/sort/example_multi_test.go b/src/sort/example_multi_test.go index de6ec142d1c44c..93f2d3ec5754a5 100644 --- a/src/sort/example_multi_test.go +++ b/src/sort/example_multi_test.go @@ -126,7 +126,7 @@ func Example_sortMultiKeys() { // By user: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}] // By user,lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}] - // By language, a && {{Less "data" "j" "j-1"}}; j-- { + {{Swap "data" "j" "j-1"}} + } + } +} + +// siftDown{{.FuncSuffix}} implements the heap property on data[lo:hi]. +// first is an offset into the array where the root of the heap lies. +func siftDown{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, lo, hi, first int {{.ExtraParam}}) { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && {{Less "data" "first+child" "first+child+1"}} { + child++ + } + if !{{Less "data" "first+root" "first+child"}} { + return + } + {{Swap "data" "first+root" "first+child"}} + root = child + } +} + +func heapSort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) { + first := a + lo := 0 + hi := b - a + + // Build heap with greatest element at top. + for i := (hi - 1) / 2; i >= 0; i-- { + siftDown{{.FuncSuffix}}(data, i, hi, first {{.ExtraArg}}) + } + + // Pop elements, largest first, into end of data. + for i := hi - 1; i >= 0; i-- { + {{Swap "data" "first" "first+i"}} + siftDown{{.FuncSuffix}}(data, lo, i, first {{.ExtraArg}}) + } +} + +// pdqsort{{.FuncSuffix}} sorts data[a:b]. +// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. +// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf +// C++ implementation: https://github.com/orlp/pdqsort +// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ +// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. +func pdqsort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, limit int {{.ExtraParam}}) { + const maxInsertion = 12 + + var ( + wasBalanced = true // whether the last partitioning was reasonably balanced + wasPartitioned = true // whether the slice was already partitioned + ) + + for { + length := b - a + + if length <= maxInsertion { + insertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + return + } + + // Fall back to heapsort if too many bad choices were made. + if limit == 0 { + heapSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + return + } + + // If the last partitioning was imbalanced, we need to breaking patterns. + if !wasBalanced { + breakPatterns{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + limit-- + } + + pivot, hint := choosePivot{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + if hint == decreasingHint { + reverseRange{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + // The chosen pivot was pivot-a elements after the start of the array. + // After reversing it is pivot-a elements before the end of the array. + // The idea came from Rust's implementation. + pivot = (b - 1) - (pivot - a) + hint = increasingHint + } + + // The slice is likely already sorted. + if wasBalanced && wasPartitioned && hint == increasingHint { + if partialInsertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) { + return + } + } + + // Probably the slice contains many duplicate elements, partition the slice into + // elements equal to and elements greater than the pivot. + if a > 0 && !{{Less "data" "a-1" "pivot"}} { + mid := partitionEqual{{.FuncSuffix}}(data, a, b, pivot {{.ExtraArg}}) + a = mid + continue + } + + mid, alreadyPartitioned := partition{{.FuncSuffix}}(data, a, b, pivot {{.ExtraArg}}) + wasPartitioned = alreadyPartitioned + + leftLen, rightLen := mid-a, b-mid + balanceThreshold := length / 8 + if leftLen < rightLen { + wasBalanced = leftLen >= balanceThreshold + pdqsort{{.FuncSuffix}}(data, a, mid, limit {{.ExtraArg}}) + a = mid + 1 + } else { + wasBalanced = rightLen >= balanceThreshold + pdqsort{{.FuncSuffix}}(data, mid+1, b, limit {{.ExtraArg}}) + b = mid + } + } +} + +// partition{{.FuncSuffix}} does one quicksort partition. +// Let p = data[pivot] +// Moves elements in data[a:b] around, so that data[i]

        =p for inewpivot. +// On return, data[newpivot] = p +func partition{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, pivot int {{.ExtraParam}}) (newpivot int, alreadyPartitioned bool) { + {{Swap "data" "a" "pivot"}} + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for i <= j && {{Less "data" "i" "a"}} { + i++ + } + for i <= j && !{{Less "data" "j" "a"}} { + j-- + } + if i > j { + {{Swap "data" "j" "a"}} + return j, true + } + {{Swap "data" "i" "j"}} + i++ + j-- + + for { + for i <= j && {{Less "data" "i" "a"}} { + i++ + } + for i <= j && !{{Less "data" "j" "a"}} { + j-- + } + if i > j { + break + } + {{Swap "data" "i" "j"}} + i++ + j-- + } + {{Swap "data" "j" "a"}} + return j, false +} + +// partitionEqual{{.FuncSuffix}} partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. +// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. +func partitionEqual{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, pivot int {{.ExtraParam}}) (newpivot int) { + {{Swap "data" "a" "pivot"}} + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for { + for i <= j && !{{Less "data" "a" "i"}} { + i++ + } + for i <= j && {{Less "data" "a" "j"}} { + j-- + } + if i > j { + break + } + {{Swap "data" "i" "j"}} + i++ + j-- + } + return i +} + +// partialInsertionSort{{.FuncSuffix}} partially sorts a slice, returns true if the slice is sorted at the end. +func partialInsertionSort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) bool { + const ( + maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted + shortestShifting = 50 // don't shift any elements on short arrays + ) + i := a + 1 + for j := 0; j < maxSteps; j++ { + for i < b && !{{Less "data" "i" "i-1"}} { + i++ + } + + if i == b { + return true + } + + if b-a < shortestShifting { + return false + } + + {{Swap "data" "i" "i-1"}} + + // Shift the smaller one to the left. + if i-a >= 2 { + for j := i - 1; j >= 1; j-- { + if !{{Less "data" "j" "j-1"}} { + break + } + {{Swap "data" "j" "j-1"}} + } + } + // Shift the greater one to the right. + if b-i >= 2 { + for j := i + 1; j < b; j++ { + if !{{Less "data" "j" "j-1"}} { + break + } + {{Swap "data" "j" "j-1"}} + } + } + } + return false +} + +// breakPatterns{{.FuncSuffix}} scatters some elements around in an attempt to break some patterns +// that might cause imbalanced partitions in quicksort. +func breakPatterns{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) { + length := b - a + if length >= 8 { + random := xorshift(length) + modulus := nextPowerOfTwo(length) + + for idx := a + (length/4)*2 - 1; idx <= a + (length/4)*2 + 1; idx++ { + other := int(uint(random.Next()) & (modulus - 1)) + if other >= length { + other -= length + } + {{Swap "data" "idx" "a+other"}} + } + } +} + +// choosePivot{{.FuncSuffix}} chooses a pivot in data[a:b]. +// +// [0,8): chooses a static pivot. +// [8,shortestNinther): uses the simple median-of-three method. +// [shortestNinther,∞): uses the Tukey ninther method. +func choosePivot{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) (pivot int, hint sortedHint) { + const ( + shortestNinther = 50 + maxSwaps = 4 * 3 + ) + + l := b - a + + var ( + swaps int + i = a + l/4*1 + j = a + l/4*2 + k = a + l/4*3 + ) + + if l >= 8 { + if l >= shortestNinther { + // Tukey ninther method, the idea came from Rust's implementation. + i = medianAdjacent{{.FuncSuffix}}(data, i, &swaps {{.ExtraArg}}) + j = medianAdjacent{{.FuncSuffix}}(data, j, &swaps {{.ExtraArg}}) + k = medianAdjacent{{.FuncSuffix}}(data, k, &swaps {{.ExtraArg}}) + } + // Find the median among i, j, k and stores it into j. + j = median{{.FuncSuffix}}(data, i, j, k, &swaps {{.ExtraArg}}) + } + + switch swaps { + case 0: + return j, increasingHint + case maxSwaps: + return j, decreasingHint + default: + return j, unknownHint + } +} + +// order2{{.FuncSuffix}} returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. +func order2{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int, swaps *int {{.ExtraParam}}) (int, int) { + if {{Less "data" "b" "a"}} { + *swaps++ + return b, a + } + return a, b +} + +// median{{.FuncSuffix}} returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. +func median{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, c int, swaps *int {{.ExtraParam}}) int { + a, b = order2{{.FuncSuffix}}(data, a, b, swaps {{.ExtraArg}}) + b, c = order2{{.FuncSuffix}}(data, b, c, swaps {{.ExtraArg}}) + a, b = order2{{.FuncSuffix}}(data, a, b, swaps {{.ExtraArg}}) + return b +} + +// medianAdjacent{{.FuncSuffix}} finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. +func medianAdjacent{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a int, swaps *int {{.ExtraParam}}) int { + return median{{.FuncSuffix}}(data, a-1, a, a+1, swaps {{.ExtraArg}}) +} + +func reverseRange{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) { + i := a + j := b - 1 + for i < j { + {{Swap "data" "i" "j"}} + i++ + j-- + } +} + +func swapRange{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, n int {{.ExtraParam}}) { + for i := 0; i < n; i++ { + {{Swap "data" "a+i" "b+i"}} + } +} + +func stable{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, n int {{.ExtraParam}}) { + blockSize := 20 // must be > 0 + a, b := 0, blockSize + for b <= n { + insertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) + a = b + b += blockSize + } + insertionSort{{.FuncSuffix}}(data, a, n {{.ExtraArg}}) + + for blockSize < n { + a, b = 0, 2*blockSize + for b <= n { + symMerge{{.FuncSuffix}}(data, a, a+blockSize, b {{.ExtraArg}}) + a = b + b += 2 * blockSize + } + if m := a + blockSize; m < n { + symMerge{{.FuncSuffix}}(data, a, m, n {{.ExtraArg}}) + } + blockSize *= 2 + } +} + +// symMerge{{.FuncSuffix}} merges the two sorted subsequences data[a:m] and data[m:b] using +// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum +// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz +// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in +// Computer Science, pages 714-723. Springer, 2004. +// +// Let M = m-a and N = b-n. Wolog M < N. +// The recursion depth is bound by ceil(log(N+M)). +// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. +// The algorithm needs O((M+N)*log(M)) calls to data.Swap. +// +// The paper gives O((M+N)*log(M)) as the number of assignments assuming a +// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation +// in the paper carries through for Swap operations, especially as the block +// swapping rotate uses only O(M+N) Swaps. +// +// symMerge assumes non-degenerate arguments: a < m && m < b. +// Having the caller check this condition eliminates many leaf recursion calls, +// which improves performance. +func symMerge{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, m, b int {{.ExtraParam}}) { + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[a] into data[m:b] + // if data[a:m] only contains one element. + if m-a == 1 { + // Use binary search to find the lowest index i + // such that data[i] >= data[a] for m <= i < b. + // Exit the search loop with i == b in case no such index exists. + i := m + j := b + for i < j { + h := int(uint(i+j) >> 1) + if {{Less "data" "h" "a"}} { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[a] reaches the position before i. + for k := a; k < i-1; k++ { + {{Swap "data" "k" "k+1"}} + } + return + } + + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[m] into data[a:m] + // if data[m:b] only contains one element. + if b-m == 1 { + // Use binary search to find the lowest index i + // such that data[i] > data[m] for a <= i < m. + // Exit the search loop with i == m in case no such index exists. + i := a + j := m + for i < j { + h := int(uint(i+j) >> 1) + if !{{Less "data" "m" "h"}} { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[m] reaches the position i. + for k := m; k > i; k-- { + {{Swap "data" "k" "k-1"}} + } + return + } + + mid := int(uint(a+b) >> 1) + n := mid + m + var start, r int + if m > mid { + start = n - b + r = mid + } else { + start = a + r = m + } + p := n - 1 + + for start < r { + c := int(uint(start+r) >> 1) + if !{{Less "data" "p-c" "c"}} { + start = c + 1 + } else { + r = c + } + } + + end := n - start + if start < m && m < end { + rotate{{.FuncSuffix}}(data, start, m, end {{.ExtraArg}}) + } + if a < start && start < mid { + symMerge{{.FuncSuffix}}(data, a, start, mid {{.ExtraArg}}) + } + if mid < end && end < b { + symMerge{{.FuncSuffix}}(data, mid, end, b {{.ExtraArg}}) + } +} + +// rotate{{.FuncSuffix}} rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: +// Data of the form 'x u v y' is changed to 'x v u y'. +// rotate performs at most b-a many calls to data.Swap, +// and it assumes non-degenerate arguments: a < m && m < b. +func rotate{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, m, b int {{.ExtraParam}}) { + i := m - a + j := b - m + + for i != j { + if i > j { + swapRange{{.FuncSuffix}}(data, m-i, m, j {{.ExtraArg}}) + i -= j + } else { + swapRange{{.FuncSuffix}}(data, m-i, m+j-i, i {{.ExtraArg}}) + j -= i + } + } + // i == j + swapRange{{.FuncSuffix}}(data, m-i, m, i {{.ExtraArg}}) +} +` diff --git a/src/sort/genzfunc.go b/src/sort/genzfunc.go deleted file mode 100644 index ed04e33568f4c6..00000000000000 --- a/src/sort/genzfunc.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build ignore -// +build ignore - -// This program is run via "go generate" (via a directive in sort.go) -// to generate zfuncversion.go. -// -// It copies sort.go to zfuncversion.go, only retaining funcs which -// take a "data Interface" parameter, and renaming each to have a -// "_func" suffix and taking a "data lessSwap" instead. It then rewrites -// each internal function call to the appropriate _func variants. - -package main - -import ( - "bytes" - "go/ast" - "go/format" - "go/parser" - "go/token" - "log" - "os" - "regexp" -) - -var fset = token.NewFileSet() - -func main() { - af, err := parser.ParseFile(fset, "sort.go", nil, 0) - if err != nil { - log.Fatal(err) - } - af.Doc = nil - af.Imports = nil - af.Comments = nil - - var newDecl []ast.Decl - for _, d := range af.Decls { - fd, ok := d.(*ast.FuncDecl) - if !ok { - continue - } - if fd.Recv != nil || fd.Name.IsExported() { - continue - } - typ := fd.Type - if len(typ.Params.List) < 1 { - continue - } - arg0 := typ.Params.List[0] - arg0Name := arg0.Names[0].Name - arg0Type := arg0.Type.(*ast.Ident) - if arg0Name != "data" || arg0Type.Name != "Interface" { - continue - } - arg0Type.Name = "lessSwap" - - newDecl = append(newDecl, fd) - } - af.Decls = newDecl - ast.Walk(visitFunc(rewriteCalls), af) - - var out bytes.Buffer - if err := format.Node(&out, fset, af); err != nil { - log.Fatalf("format.Node: %v", err) - } - - // Get rid of blank lines after removal of comments. - src := regexp.MustCompile(`\n{2,}`).ReplaceAll(out.Bytes(), []byte("\n")) - - // Add comments to each func, for the lost reader. - // This is so much easier than adding comments via the AST - // and trying to get position info correct. - src = regexp.MustCompile(`(?m)^func (\w+)`).ReplaceAll(src, []byte("\n// Auto-generated variant of sort.go:$1\nfunc ${1}_func")) - - // Final gofmt. - src, err = format.Source(src) - if err != nil { - log.Fatalf("format.Source: %v on\n%s", err, src) - } - - out.Reset() - out.WriteString(`// Code generated from sort.go using genzfunc.go; DO NOT EDIT. - -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -`) - out.Write(src) - - const target = "zfuncversion.go" - if err := os.WriteFile(target, out.Bytes(), 0644); err != nil { - log.Fatal(err) - } -} - -type visitFunc func(ast.Node) ast.Visitor - -func (f visitFunc) Visit(n ast.Node) ast.Visitor { return f(n) } - -func rewriteCalls(n ast.Node) ast.Visitor { - ce, ok := n.(*ast.CallExpr) - if ok { - rewriteCall(ce) - } - return visitFunc(rewriteCalls) -} - -func rewriteCall(ce *ast.CallExpr) { - ident, ok := ce.Fun.(*ast.Ident) - if !ok { - // e.g. skip SelectorExpr (data.Less(..) calls) - return - } - // skip casts - if ident.Name == "int" || ident.Name == "uint" { - return - } - if len(ce.Args) < 1 { - return - } - ident.Name += "_func" -} diff --git a/src/sort/search.go b/src/sort/search.go index fcff0f9491bd38..874e40813d1229 100644 --- a/src/sort/search.go +++ b/src/sort/search.go @@ -55,7 +55,6 @@ package sort // }) // fmt.Printf("Your number is %d.\n", answer) // } -// func Search(n int, f func(int) bool) int { // Define f(-1) == false and f(n) == true. // Invariant: f(i-1) == false, f(j) == true. @@ -73,13 +72,54 @@ func Search(n int, f func(int) bool) int { return i } +// Find uses binary search to find and return the smallest index i in [0, n) +// at which cmp(i) <= 0. If there is no such index i, Find returns i = n. +// The found result is true if i < n and cmp(i) == 0. +// Find calls cmp(i) only for i in the range [0, n). +// +// To permit binary search, Find requires that cmp(i) > 0 for a leading +// prefix of the range, cmp(i) == 0 in the middle, and cmp(i) < 0 for +// the final suffix of the range. (Each subrange could be empty.) +// The usual way to establish this condition is to interpret cmp(i) +// as a comparison of a desired target value t against entry i in an +// underlying indexed data structure x, returning <0, 0, and >0 +// when t < x[i], t == x[i], and t > x[i], respectively. +// +// For example, to look for a particular string in a sorted, random-access +// list of strings: +// +// i, found := sort.Find(x.Len(), func(i int) int { +// return strings.Compare(target, x.At(i)) +// }) +// if found { +// fmt.Printf("found %s at entry %d\n", target, i) +// } else { +// fmt.Printf("%s not found, would insert at %d", target, i) +// } +func Find(n int, cmp func(int) int) (i int, found bool) { + // The invariants here are similar to the ones in Search. + // Define cmp(-1) > 0 and cmp(n) <= 0 + // Invariant: cmp(i-1) > 0, cmp(j) <= 0 + i, j := 0, n + for i < j { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + // i ≤ h < j + if cmp(h) > 0 { + i = h + 1 // preserves cmp(i-1) > 0 + } else { + j = h // preserves cmp(j) <= 0 + } + } + // i == j, cmp(i-1) > 0 and cmp(j) <= 0 + return i, i < n && cmp(i) == 0 +} + // Convenience wrappers for common cases. // SearchInts searches for x in a sorted slice of ints and returns the index // as specified by Search. The return value is the index to insert x if x is // not present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchInts(a []int, x int) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } @@ -88,7 +128,6 @@ func SearchInts(a []int, x int) int { // as specified by Search. The return value is the index to insert x if x is not // present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchFloat64s(a []float64, x float64) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } @@ -97,7 +136,6 @@ func SearchFloat64s(a []float64, x float64) int { // as specified by Search. The return value is the index to insert x if x is not // present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchStrings(a []string, x string) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } diff --git a/src/sort/search_test.go b/src/sort/search_test.go index ded68ebde0a2b3..49813eaecb0fe4 100644 --- a/src/sort/search_test.go +++ b/src/sort/search_test.go @@ -7,6 +7,7 @@ package sort_test import ( "runtime" . "sort" + stringspkg "strings" "testing" ) @@ -57,9 +58,82 @@ func TestSearch(t *testing.T) { } } +func TestFind(t *testing.T) { + str1 := []string{"foo"} + str2 := []string{"ab", "ca"} + str3 := []string{"mo", "qo", "vo"} + str4 := []string{"ab", "ad", "ca", "xy"} + + // slice with repeating elements + strRepeats := []string{"ba", "ca", "da", "da", "da", "ka", "ma", "ma", "ta"} + + // slice with all element equal + strSame := []string{"xx", "xx", "xx"} + + tests := []struct { + data []string + target string + wantPos int + wantFound bool + }{ + {[]string{}, "foo", 0, false}, + {[]string{}, "", 0, false}, + + {str1, "foo", 0, true}, + {str1, "bar", 0, false}, + {str1, "zx", 1, false}, + + {str2, "aa", 0, false}, + {str2, "ab", 0, true}, + {str2, "ad", 1, false}, + {str2, "ca", 1, true}, + {str2, "ra", 2, false}, + + {str3, "bb", 0, false}, + {str3, "mo", 0, true}, + {str3, "nb", 1, false}, + {str3, "qo", 1, true}, + {str3, "tr", 2, false}, + {str3, "vo", 2, true}, + {str3, "xr", 3, false}, + + {str4, "aa", 0, false}, + {str4, "ab", 0, true}, + {str4, "ac", 1, false}, + {str4, "ad", 1, true}, + {str4, "ax", 2, false}, + {str4, "ca", 2, true}, + {str4, "cc", 3, false}, + {str4, "dd", 3, false}, + {str4, "xy", 3, true}, + {str4, "zz", 4, false}, + + {strRepeats, "da", 2, true}, + {strRepeats, "db", 5, false}, + {strRepeats, "ma", 6, true}, + {strRepeats, "mb", 8, false}, + + {strSame, "xx", 0, true}, + {strSame, "ab", 0, false}, + {strSame, "zz", 3, false}, + } + + for _, tt := range tests { + t.Run(tt.target, func(t *testing.T) { + cmp := func(i int) int { + return stringspkg.Compare(tt.target, tt.data[i]) + } + + pos, found := Find(len(tt.data), cmp) + if pos != tt.wantPos || found != tt.wantFound { + t.Errorf("Find got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) + } + }) + } +} + // log2 computes the binary logarithm of x, rounded up to the next integer. // (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.) -// func log2(x int) int { n := 0 for p := 1; p < x; p += p { @@ -159,3 +233,34 @@ func TestSearchExhaustive(t *testing.T) { } } } + +// Abstract exhaustive test for Find. +func TestFindExhaustive(t *testing.T) { + // Test Find for different sequence sizes and search targets. + // For each size, we have a (unmaterialized) sequence of integers: + // 2,4...size*2 + // And we're looking for every possible integer between 1 and size*2 + 1. + for size := 0; size <= 100; size++ { + for x := 1; x <= size*2+1; x++ { + var wantFound bool + var wantPos int + + cmp := func(i int) int { + // Encodes the unmaterialized sequence with elem[i] == (i+1)*2 + return x - (i+1)*2 + } + pos, found := Find(size, cmp) + + if x%2 == 0 { + wantPos = x/2 - 1 + wantFound = true + } else { + wantPos = x / 2 + wantFound = false + } + if found != wantFound || pos != wantPos { + t.Errorf("Find(%d, %d): got (%v, %v), want (%v, %v)", size, x, pos, found, wantPos, wantFound) + } + } + } +} diff --git a/src/sort/slice.go b/src/sort/slice.go index 992ad1559df58c..d0b2102013876c 100644 --- a/src/sort/slice.go +++ b/src/sort/slice.go @@ -4,6 +4,11 @@ package sort +import ( + "internal/reflectlite" + "math/bits" +) + // Slice sorts the slice x given the provided less function. // It panics if x is not a slice. // @@ -13,11 +18,12 @@ package sort // // The less function must satisfy the same requirements as // the Interface type's Less method. -func Slice(x interface{}, less func(i, j int) bool) { - rv := reflectValueOf(x) - swap := reflectSwapper(x) +func Slice(x any, less func(i, j int) bool) { + rv := reflectlite.ValueOf(x) + swap := reflectlite.Swapper(x) length := rv.Len() - quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length)) + limit := bits.Len(uint(length)) + pdqsort_func(lessSwap{less, swap}, 0, length, limit) } // SliceStable sorts the slice x using the provided less @@ -26,16 +32,16 @@ func Slice(x interface{}, less func(i, j int) bool) { // // The less function must satisfy the same requirements as // the Interface type's Less method. -func SliceStable(x interface{}, less func(i, j int) bool) { - rv := reflectValueOf(x) - swap := reflectSwapper(x) +func SliceStable(x any, less func(i, j int) bool) { + rv := reflectlite.ValueOf(x) + swap := reflectlite.Swapper(x) stable_func(lessSwap{less, swap}, rv.Len()) } // SliceIsSorted reports whether the slice x is sorted according to the provided less function. // It panics if x is not a slice. -func SliceIsSorted(x interface{}, less func(i, j int) bool) bool { - rv := reflectValueOf(x) +func SliceIsSorted(x any, less func(i, j int) bool) bool { + rv := reflectlite.ValueOf(x) n := rv.Len() for i := n - 1; i > 0; i-- { if less(i, i-1) { diff --git a/src/sort/slice_go113.go b/src/sort/slice_go113.go deleted file mode 100644 index 53542dbd1a9b97..00000000000000 --- a/src/sort/slice_go113.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.13 -// +build go1.13 - -package sort - -import "internal/reflectlite" - -var reflectValueOf = reflectlite.ValueOf -var reflectSwapper = reflectlite.Swapper diff --git a/src/sort/slice_go14.go b/src/sort/slice_go14.go deleted file mode 100644 index 5d5949f3cade56..00000000000000 --- a/src/sort/slice_go14.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.8 -// +build !go1.8 - -package sort - -import "reflect" - -var reflectValueOf = reflect.ValueOf - -func reflectSwapper(x interface{}) func(int, int) { - v := reflectValueOf(x) - tmp := reflect.New(v.Type().Elem()).Elem() - return func(i, j int) { - a, b := v.Index(i), v.Index(j) - tmp.Set(a) - a.Set(b) - b.Set(tmp) - } -} diff --git a/src/sort/slice_go18.go b/src/sort/slice_go18.go deleted file mode 100644 index 1538477bc5d205..00000000000000 --- a/src/sort/slice_go18.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.8 && !go1.13 -// +build go1.8,!go1.13 - -package sort - -import "reflect" - -var reflectValueOf = reflect.ValueOf -var reflectSwapper = reflect.Swapper diff --git a/src/sort/sort.go b/src/sort/sort.go index cbaa8c3aac20f1..68e2f0d082f181 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate go run genzfunc.go +//go:generate go run gen_sort_variants.go // Package sort provides primitives for sorting slices and user-defined collections. package sort +import "math/bits" + // An implementation of Interface can be sorted by the routines in this package. // The methods refer to elements of the underlying collection by integer index. type Interface interface { @@ -34,211 +36,39 @@ type Interface interface { Swap(i, j int) } -// insertionSort sorts data[a:b] using insertion sort. -func insertionSort(data Interface, a, b int) { - for i := a + 1; i < b; i++ { - for j := i; j > a && data.Less(j, j-1); j-- { - data.Swap(j, j-1) - } - } -} - -// siftDown implements the heap property on data[lo:hi]. -// first is an offset into the array where the root of the heap lies. -func siftDown(data Interface, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && data.Less(first+child, first+child+1) { - child++ - } - if !data.Less(first+root, first+child) { - return - } - data.Swap(first+root, first+child) - root = child - } -} - -func heapSort(data Interface, a, b int) { - first := a - lo := 0 - hi := b - a - - // Build heap with greatest element at top. - for i := (hi - 1) / 2; i >= 0; i-- { - siftDown(data, i, hi, first) - } - - // Pop elements, largest first, into end of data. - for i := hi - 1; i >= 0; i-- { - data.Swap(first, first+i) - siftDown(data, lo, i, first) - } -} - -// Quicksort, loosely following Bentley and McIlroy, -// ``Engineering a Sort Function,'' SP&E November 1993. - -// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. -func medianOfThree(data Interface, m1, m0, m2 int) { - // sort 3 elements - if data.Less(m1, m0) { - data.Swap(m1, m0) - } - // data[m0] <= data[m1] - if data.Less(m2, m1) { - data.Swap(m2, m1) - // data[m0] <= data[m2] && data[m1] < data[m2] - if data.Less(m1, m0) { - data.Swap(m1, m0) - } +// Sort sorts data in ascending order as determined by the Less method. +// It makes one call to data.Len to determine n and O(n*log(n)) calls to +// data.Less and data.Swap. The sort is not guaranteed to be stable. +func Sort(data Interface) { + n := data.Len() + if n <= 1 { + return } - // now data[m0] <= data[m1] <= data[m2] + limit := bits.Len(uint(n)) + pdqsort(data, 0, n, limit) } -func swapRange(data Interface, a, b, n int) { - for i := 0; i < n; i++ { - data.Swap(a+i, b+i) - } -} +type sortedHint int // hint for pdqsort when choosing the pivot -func doPivot(data Interface, lo, hi int) (midlo, midhi int) { - m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. - if hi-lo > 40 { - // Tukey's ``Ninther,'' median of three medians of three. - s := (hi - lo) / 8 - medianOfThree(data, lo, lo+s, lo+2*s) - medianOfThree(data, m, m-s, m+s) - medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) - } - medianOfThree(data, lo, m, hi-1) - - // Invariants are: - // data[lo] = pivot (set up by ChoosePivot) - // data[lo < i < a] < pivot - // data[a <= i < b] <= pivot - // data[b <= i < c] unexamined - // data[c <= i < hi-1] > pivot - // data[hi-1] >= pivot - pivot := lo - a, c := lo+1, hi-1 - - for ; a < c && data.Less(a, pivot); a++ { - } - b := a - for { - for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot - } - for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot - } - if b >= c { - break - } - // data[b] > pivot; data[c-1] <= pivot - data.Swap(b, c-1) - b++ - c-- - } - // If hi-c<3 then there are duplicates (by property of median of nine). - // Let's be a bit more conservative, and set border to 5. - protect := hi-c < 5 - if !protect && hi-c < (hi-lo)/4 { - // Lets test some points for equality to pivot - dups := 0 - if !data.Less(pivot, hi-1) { // data[hi-1] = pivot - data.Swap(c, hi-1) - c++ - dups++ - } - if !data.Less(b-1, pivot) { // data[b-1] = pivot - b-- - dups++ - } - // m-lo = (hi-lo)/2 > 6 - // b-lo > (hi-lo)*3/4-1 > 8 - // ==> m < b ==> data[m] <= pivot - if !data.Less(m, pivot) { // data[m] = pivot - data.Swap(m, b-1) - b-- - dups++ - } - // if at least 2 points are equal to pivot, assume skewed distribution - protect = dups > 1 - } - if protect { - // Protect against a lot of duplicates - // Add invariant: - // data[a <= i < b] unexamined - // data[b <= i < c] = pivot - for { - for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot - } - for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot - } - if a >= b { - break - } - // data[a] == pivot; data[b-1] < pivot - data.Swap(a, b-1) - a++ - b-- - } - } - // Swap pivot into middle - data.Swap(pivot, b-1) - return b - 1, c -} +const ( + unknownHint sortedHint = iota + increasingHint + decreasingHint +) -func quickSort(data Interface, a, b, maxDepth int) { - for b-a > 12 { // Use ShellSort for slices <= 12 elements - if maxDepth == 0 { - heapSort(data, a, b) - return - } - maxDepth-- - mlo, mhi := doPivot(data, a, b) - // Avoiding recursion on the larger subproblem guarantees - // a stack depth of at most lg(b-a). - if mlo-a < b-mhi { - quickSort(data, a, mlo, maxDepth) - a = mhi // i.e., quickSort(data, mhi, b) - } else { - quickSort(data, mhi, b, maxDepth) - b = mlo // i.e., quickSort(data, a, mlo) - } - } - if b-a > 1 { - // Do ShellSort pass with gap 6 - // It could be written in this simplified form cause b-a <= 12 - for i := a + 6; i < b; i++ { - if data.Less(i, i-6) { - data.Swap(i, i-6) - } - } - insertionSort(data, a, b) - } -} +// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf +type xorshift uint64 -// Sort sorts data. -// It makes one call to data.Len to determine n and O(n*log(n)) calls to -// data.Less and data.Swap. The sort is not guaranteed to be stable. -func Sort(data Interface) { - n := data.Len() - quickSort(data, 0, n, maxDepth(n)) +func (r *xorshift) Next() uint64 { + *r ^= *r << 13 + *r ^= *r >> 17 + *r ^= *r << 5 + return uint64(*r) } -// maxDepth returns a threshold at which quicksort should switch -// to heapsort. It returns 2*ceil(lg(n+1)). -func maxDepth(n int) int { - var depth int - for i := n; i > 0; i >>= 1 { - depth++ - } - return depth * 2 +func nextPowerOfTwo(length int) uint { + shift := uint(bits.Len(uint(length))) + return uint(1 << shift) } // lessSwap is a pair of Less and Swap function for use with the @@ -300,7 +130,6 @@ func (x Float64Slice) Len() int { return len(x) } // This implementation of Less places NaN values before any others, by using: // // x[i] < x[j] || (math.IsNaN(x[i]) && !math.IsNaN(x[j])) -// func (x Float64Slice) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) } func (x Float64Slice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } @@ -370,7 +199,8 @@ func StringsAreSorted(x []string) bool { return IsSorted(StringSlice(x)) } // - Often "optimal" algorithms are optimal in the number of assignments // but Interface has only Swap as operation. -// Stable sorts data while keeping the original order of equal elements. +// Stable sorts data in ascending order as determined by the Less method, +// while keeping the original order of equal elements. // // It makes one call to data.Len to determine n, O(n*log(n)) calls to // data.Less and O(n*log(n)*log(n)) calls to data.Swap. @@ -378,152 +208,6 @@ func Stable(data Interface) { stable(data, data.Len()) } -func stable(data Interface, n int) { - blockSize := 20 // must be > 0 - a, b := 0, blockSize - for b <= n { - insertionSort(data, a, b) - a = b - b += blockSize - } - insertionSort(data, a, n) - - for blockSize < n { - a, b = 0, 2*blockSize - for b <= n { - symMerge(data, a, a+blockSize, b) - a = b - b += 2 * blockSize - } - if m := a + blockSize; m < n { - symMerge(data, a, m, n) - } - blockSize *= 2 - } -} - -// symMerge merges the two sorted subsequences data[a:m] and data[m:b] using -// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum -// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz -// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in -// Computer Science, pages 714-723. Springer, 2004. -// -// Let M = m-a and N = b-n. Wolog M < N. -// The recursion depth is bound by ceil(log(N+M)). -// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. -// The algorithm needs O((M+N)*log(M)) calls to data.Swap. -// -// The paper gives O((M+N)*log(M)) as the number of assignments assuming a -// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation -// in the paper carries through for Swap operations, especially as the block -// swapping rotate uses only O(M+N) Swaps. -// -// symMerge assumes non-degenerate arguments: a < m && m < b. -// Having the caller check this condition eliminates many leaf recursion calls, -// which improves performance. -func symMerge(data Interface, a, m, b int) { - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[a] into data[m:b] - // if data[a:m] only contains one element. - if m-a == 1 { - // Use binary search to find the lowest index i - // such that data[i] >= data[a] for m <= i < b. - // Exit the search loop with i == b in case no such index exists. - i := m - j := b - for i < j { - h := int(uint(i+j) >> 1) - if data.Less(h, a) { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[a] reaches the position before i. - for k := a; k < i-1; k++ { - data.Swap(k, k+1) - } - return - } - - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[m] into data[a:m] - // if data[m:b] only contains one element. - if b-m == 1 { - // Use binary search to find the lowest index i - // such that data[i] > data[m] for a <= i < m. - // Exit the search loop with i == m in case no such index exists. - i := a - j := m - for i < j { - h := int(uint(i+j) >> 1) - if !data.Less(m, h) { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[m] reaches the position i. - for k := m; k > i; k-- { - data.Swap(k, k-1) - } - return - } - - mid := int(uint(a+b) >> 1) - n := mid + m - var start, r int - if m > mid { - start = n - b - r = mid - } else { - start = a - r = m - } - p := n - 1 - - for start < r { - c := int(uint(start+r) >> 1) - if !data.Less(p-c, c) { - start = c + 1 - } else { - r = c - } - } - - end := n - start - if start < m && m < end { - rotate(data, start, m, end) - } - if a < start && start < mid { - symMerge(data, a, start, mid) - } - if mid < end && end < b { - symMerge(data, mid, end, b) - } -} - -// rotate rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: -// Data of the form 'x u v y' is changed to 'x v u y'. -// rotate performs at most b-a many calls to data.Swap, -// and it assumes non-degenerate arguments: a < m && m < b. -func rotate(data Interface, a, m, b int) { - i := m - a - j := b - m - - for i != j { - if i > j { - swapRange(data, m-i, m, j) - i -= j - } else { - swapRange(data, m-i, m+j-i, i) - j -= i - } - } - // i == j - swapRange(data, m-i, m, i) -} - /* Complexity of Stable Sorting diff --git a/src/sort/sort_test.go b/src/sort/sort_test.go index bfff3528d3ff57..862bba2d4415a2 100644 --- a/src/sort/sort_test.go +++ b/src/sort/sort_test.go @@ -122,6 +122,37 @@ func TestReverseSortIntSlice(t *testing.T) { } } +func TestBreakPatterns(t *testing.T) { + // Special slice used to trigger breakPatterns. + data := make([]int, 30) + for i := range data { + data[i] = 10 + } + data[(len(data)/4)*1] = 0 + data[(len(data)/4)*2] = 1 + data[(len(data)/4)*3] = 2 + Sort(IntSlice(data)) +} + +func TestReverseRange(t *testing.T) { + data := []int{1, 2, 3, 4, 5, 6, 7} + ReverseRange(IntSlice(data), 0, len(data)) + for i := len(data) - 1; i > 0; i-- { + if data[i] > data[i-1] { + t.Fatalf("reverseRange didn't work") + } + } + + data1 := []int{1, 2, 3, 4, 5, 6, 7} + data2 := []int{1, 2, 5, 4, 3, 6, 7} + ReverseRange(IntSlice(data1), 2, 5) + for i, v := range data1 { + if v != data2[i] { + t.Fatalf("reverseRange didn't work") + } + } +} + type nonDeterministicTestingData struct { r *rand.Rand } @@ -220,6 +251,45 @@ func BenchmarkSortInt1K(b *testing.B) { } } +func BenchmarkSortInt1K_Sorted(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = i + } + b.StartTimer() + Ints(data) + b.StopTimer() + } +} + +func BenchmarkSortInt1K_Reversed(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = len(data) - i + } + b.StartTimer() + Ints(data) + b.StopTimer() + } +} + +func BenchmarkSortInt1K_Mod8(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = i % 8 + } + b.StartTimer() + Ints(data) + b.StopTimer() + } +} + func BenchmarkStableInt1K(b *testing.B) { b.StopTimer() unsorted := make([]int, 1<<10) diff --git a/src/sort/zfuncversion.go b/src/sort/zfuncversion.go deleted file mode 100644 index 30067cbe077bd0..00000000000000 --- a/src/sort/zfuncversion.go +++ /dev/null @@ -1,265 +0,0 @@ -// Code generated from sort.go using genzfunc.go; DO NOT EDIT. - -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package sort - -// Auto-generated variant of sort.go:insertionSort -func insertionSort_func(data lessSwap, a, b int) { - for i := a + 1; i < b; i++ { - for j := i; j > a && data.Less(j, j-1); j-- { - data.Swap(j, j-1) - } - } -} - -// Auto-generated variant of sort.go:siftDown -func siftDown_func(data lessSwap, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && data.Less(first+child, first+child+1) { - child++ - } - if !data.Less(first+root, first+child) { - return - } - data.Swap(first+root, first+child) - root = child - } -} - -// Auto-generated variant of sort.go:heapSort -func heapSort_func(data lessSwap, a, b int) { - first := a - lo := 0 - hi := b - a - for i := (hi - 1) / 2; i >= 0; i-- { - siftDown_func(data, i, hi, first) - } - for i := hi - 1; i >= 0; i-- { - data.Swap(first, first+i) - siftDown_func(data, lo, i, first) - } -} - -// Auto-generated variant of sort.go:medianOfThree -func medianOfThree_func(data lessSwap, m1, m0, m2 int) { - if data.Less(m1, m0) { - data.Swap(m1, m0) - } - if data.Less(m2, m1) { - data.Swap(m2, m1) - if data.Less(m1, m0) { - data.Swap(m1, m0) - } - } -} - -// Auto-generated variant of sort.go:swapRange -func swapRange_func(data lessSwap, a, b, n int) { - for i := 0; i < n; i++ { - data.Swap(a+i, b+i) - } -} - -// Auto-generated variant of sort.go:doPivot -func doPivot_func(data lessSwap, lo, hi int) (midlo, midhi int) { - m := int(uint(lo+hi) >> 1) - if hi-lo > 40 { - s := (hi - lo) / 8 - medianOfThree_func(data, lo, lo+s, lo+2*s) - medianOfThree_func(data, m, m-s, m+s) - medianOfThree_func(data, hi-1, hi-1-s, hi-1-2*s) - } - medianOfThree_func(data, lo, m, hi-1) - pivot := lo - a, c := lo+1, hi-1 - for ; a < c && data.Less(a, pivot); a++ { - } - b := a - for { - for ; b < c && !data.Less(pivot, b); b++ { - } - for ; b < c && data.Less(pivot, c-1); c-- { - } - if b >= c { - break - } - data.Swap(b, c-1) - b++ - c-- - } - protect := hi-c < 5 - if !protect && hi-c < (hi-lo)/4 { - dups := 0 - if !data.Less(pivot, hi-1) { - data.Swap(c, hi-1) - c++ - dups++ - } - if !data.Less(b-1, pivot) { - b-- - dups++ - } - if !data.Less(m, pivot) { - data.Swap(m, b-1) - b-- - dups++ - } - protect = dups > 1 - } - if protect { - for { - for ; a < b && !data.Less(b-1, pivot); b-- { - } - for ; a < b && data.Less(a, pivot); a++ { - } - if a >= b { - break - } - data.Swap(a, b-1) - a++ - b-- - } - } - data.Swap(pivot, b-1) - return b - 1, c -} - -// Auto-generated variant of sort.go:quickSort -func quickSort_func(data lessSwap, a, b, maxDepth int) { - for b-a > 12 { - if maxDepth == 0 { - heapSort_func(data, a, b) - return - } - maxDepth-- - mlo, mhi := doPivot_func(data, a, b) - if mlo-a < b-mhi { - quickSort_func(data, a, mlo, maxDepth) - a = mhi - } else { - quickSort_func(data, mhi, b, maxDepth) - b = mlo - } - } - if b-a > 1 { - for i := a + 6; i < b; i++ { - if data.Less(i, i-6) { - data.Swap(i, i-6) - } - } - insertionSort_func(data, a, b) - } -} - -// Auto-generated variant of sort.go:stable -func stable_func(data lessSwap, n int) { - blockSize := 20 - a, b := 0, blockSize - for b <= n { - insertionSort_func(data, a, b) - a = b - b += blockSize - } - insertionSort_func(data, a, n) - for blockSize < n { - a, b = 0, 2*blockSize - for b <= n { - symMerge_func(data, a, a+blockSize, b) - a = b - b += 2 * blockSize - } - if m := a + blockSize; m < n { - symMerge_func(data, a, m, n) - } - blockSize *= 2 - } -} - -// Auto-generated variant of sort.go:symMerge -func symMerge_func(data lessSwap, a, m, b int) { - if m-a == 1 { - i := m - j := b - for i < j { - h := int(uint(i+j) >> 1) - if data.Less(h, a) { - i = h + 1 - } else { - j = h - } - } - for k := a; k < i-1; k++ { - data.Swap(k, k+1) - } - return - } - if b-m == 1 { - i := a - j := m - for i < j { - h := int(uint(i+j) >> 1) - if !data.Less(m, h) { - i = h + 1 - } else { - j = h - } - } - for k := m; k > i; k-- { - data.Swap(k, k-1) - } - return - } - mid := int(uint(a+b) >> 1) - n := mid + m - var start, r int - if m > mid { - start = n - b - r = mid - } else { - start = a - r = m - } - p := n - 1 - for start < r { - c := int(uint(start+r) >> 1) - if !data.Less(p-c, c) { - start = c + 1 - } else { - r = c - } - } - end := n - start - if start < m && m < end { - rotate_func(data, start, m, end) - } - if a < start && start < mid { - symMerge_func(data, a, start, mid) - } - if mid < end && end < b { - symMerge_func(data, mid, end, b) - } -} - -// Auto-generated variant of sort.go:rotate -func rotate_func(data lessSwap, a, m, b int) { - i := m - a - j := b - m - for i != j { - if i > j { - swapRange_func(data, m-i, m, j) - i -= j - } else { - swapRange_func(data, m-i, m+j-i, i) - j -= i - } - } - swapRange_func(data, m-i, m, i) -} diff --git a/src/sort/zsortfunc.go b/src/sort/zsortfunc.go new file mode 100644 index 00000000000000..49b6169b9769d8 --- /dev/null +++ b/src/sort/zsortfunc.go @@ -0,0 +1,479 @@ +// Code generated by gen_sort_variants.go; DO NOT EDIT. + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sort + +// insertionSort_func sorts data[a:b] using insertion sort. +func insertionSort_func(data lessSwap, a, b int) { + for i := a + 1; i < b; i++ { + for j := i; j > a && data.Less(j, j-1); j-- { + data.Swap(j, j-1) + } + } +} + +// siftDown_func implements the heap property on data[lo:hi]. +// first is an offset into the array where the root of the heap lies. +func siftDown_func(data lessSwap, lo, hi, first int) { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && data.Less(first+child, first+child+1) { + child++ + } + if !data.Less(first+root, first+child) { + return + } + data.Swap(first+root, first+child) + root = child + } +} + +func heapSort_func(data lessSwap, a, b int) { + first := a + lo := 0 + hi := b - a + + // Build heap with greatest element at top. + for i := (hi - 1) / 2; i >= 0; i-- { + siftDown_func(data, i, hi, first) + } + + // Pop elements, largest first, into end of data. + for i := hi - 1; i >= 0; i-- { + data.Swap(first, first+i) + siftDown_func(data, lo, i, first) + } +} + +// pdqsort_func sorts data[a:b]. +// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. +// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf +// C++ implementation: https://github.com/orlp/pdqsort +// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ +// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. +func pdqsort_func(data lessSwap, a, b, limit int) { + const maxInsertion = 12 + + var ( + wasBalanced = true // whether the last partitioning was reasonably balanced + wasPartitioned = true // whether the slice was already partitioned + ) + + for { + length := b - a + + if length <= maxInsertion { + insertionSort_func(data, a, b) + return + } + + // Fall back to heapsort if too many bad choices were made. + if limit == 0 { + heapSort_func(data, a, b) + return + } + + // If the last partitioning was imbalanced, we need to breaking patterns. + if !wasBalanced { + breakPatterns_func(data, a, b) + limit-- + } + + pivot, hint := choosePivot_func(data, a, b) + if hint == decreasingHint { + reverseRange_func(data, a, b) + // The chosen pivot was pivot-a elements after the start of the array. + // After reversing it is pivot-a elements before the end of the array. + // The idea came from Rust's implementation. + pivot = (b - 1) - (pivot - a) + hint = increasingHint + } + + // The slice is likely already sorted. + if wasBalanced && wasPartitioned && hint == increasingHint { + if partialInsertionSort_func(data, a, b) { + return + } + } + + // Probably the slice contains many duplicate elements, partition the slice into + // elements equal to and elements greater than the pivot. + if a > 0 && !data.Less(a-1, pivot) { + mid := partitionEqual_func(data, a, b, pivot) + a = mid + continue + } + + mid, alreadyPartitioned := partition_func(data, a, b, pivot) + wasPartitioned = alreadyPartitioned + + leftLen, rightLen := mid-a, b-mid + balanceThreshold := length / 8 + if leftLen < rightLen { + wasBalanced = leftLen >= balanceThreshold + pdqsort_func(data, a, mid, limit) + a = mid + 1 + } else { + wasBalanced = rightLen >= balanceThreshold + pdqsort_func(data, mid+1, b, limit) + b = mid + } + } +} + +// partition_func does one quicksort partition. +// Let p = data[pivot] +// Moves elements in data[a:b] around, so that data[i]

        =p for inewpivot. +// On return, data[newpivot] = p +func partition_func(data lessSwap, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { + data.Swap(a, pivot) + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for i <= j && data.Less(i, a) { + i++ + } + for i <= j && !data.Less(j, a) { + j-- + } + if i > j { + data.Swap(j, a) + return j, true + } + data.Swap(i, j) + i++ + j-- + + for { + for i <= j && data.Less(i, a) { + i++ + } + for i <= j && !data.Less(j, a) { + j-- + } + if i > j { + break + } + data.Swap(i, j) + i++ + j-- + } + data.Swap(j, a) + return j, false +} + +// partitionEqual_func partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. +// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. +func partitionEqual_func(data lessSwap, a, b, pivot int) (newpivot int) { + data.Swap(a, pivot) + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for { + for i <= j && !data.Less(a, i) { + i++ + } + for i <= j && data.Less(a, j) { + j-- + } + if i > j { + break + } + data.Swap(i, j) + i++ + j-- + } + return i +} + +// partialInsertionSort_func partially sorts a slice, returns true if the slice is sorted at the end. +func partialInsertionSort_func(data lessSwap, a, b int) bool { + const ( + maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted + shortestShifting = 50 // don't shift any elements on short arrays + ) + i := a + 1 + for j := 0; j < maxSteps; j++ { + for i < b && !data.Less(i, i-1) { + i++ + } + + if i == b { + return true + } + + if b-a < shortestShifting { + return false + } + + data.Swap(i, i-1) + + // Shift the smaller one to the left. + if i-a >= 2 { + for j := i - 1; j >= 1; j-- { + if !data.Less(j, j-1) { + break + } + data.Swap(j, j-1) + } + } + // Shift the greater one to the right. + if b-i >= 2 { + for j := i + 1; j < b; j++ { + if !data.Less(j, j-1) { + break + } + data.Swap(j, j-1) + } + } + } + return false +} + +// breakPatterns_func scatters some elements around in an attempt to break some patterns +// that might cause imbalanced partitions in quicksort. +func breakPatterns_func(data lessSwap, a, b int) { + length := b - a + if length >= 8 { + random := xorshift(length) + modulus := nextPowerOfTwo(length) + + for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { + other := int(uint(random.Next()) & (modulus - 1)) + if other >= length { + other -= length + } + data.Swap(idx, a+other) + } + } +} + +// choosePivot_func chooses a pivot in data[a:b]. +// +// [0,8): chooses a static pivot. +// [8,shortestNinther): uses the simple median-of-three method. +// [shortestNinther,∞): uses the Tukey ninther method. +func choosePivot_func(data lessSwap, a, b int) (pivot int, hint sortedHint) { + const ( + shortestNinther = 50 + maxSwaps = 4 * 3 + ) + + l := b - a + + var ( + swaps int + i = a + l/4*1 + j = a + l/4*2 + k = a + l/4*3 + ) + + if l >= 8 { + if l >= shortestNinther { + // Tukey ninther method, the idea came from Rust's implementation. + i = medianAdjacent_func(data, i, &swaps) + j = medianAdjacent_func(data, j, &swaps) + k = medianAdjacent_func(data, k, &swaps) + } + // Find the median among i, j, k and stores it into j. + j = median_func(data, i, j, k, &swaps) + } + + switch swaps { + case 0: + return j, increasingHint + case maxSwaps: + return j, decreasingHint + default: + return j, unknownHint + } +} + +// order2_func returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. +func order2_func(data lessSwap, a, b int, swaps *int) (int, int) { + if data.Less(b, a) { + *swaps++ + return b, a + } + return a, b +} + +// median_func returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. +func median_func(data lessSwap, a, b, c int, swaps *int) int { + a, b = order2_func(data, a, b, swaps) + b, c = order2_func(data, b, c, swaps) + a, b = order2_func(data, a, b, swaps) + return b +} + +// medianAdjacent_func finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. +func medianAdjacent_func(data lessSwap, a int, swaps *int) int { + return median_func(data, a-1, a, a+1, swaps) +} + +func reverseRange_func(data lessSwap, a, b int) { + i := a + j := b - 1 + for i < j { + data.Swap(i, j) + i++ + j-- + } +} + +func swapRange_func(data lessSwap, a, b, n int) { + for i := 0; i < n; i++ { + data.Swap(a+i, b+i) + } +} + +func stable_func(data lessSwap, n int) { + blockSize := 20 // must be > 0 + a, b := 0, blockSize + for b <= n { + insertionSort_func(data, a, b) + a = b + b += blockSize + } + insertionSort_func(data, a, n) + + for blockSize < n { + a, b = 0, 2*blockSize + for b <= n { + symMerge_func(data, a, a+blockSize, b) + a = b + b += 2 * blockSize + } + if m := a + blockSize; m < n { + symMerge_func(data, a, m, n) + } + blockSize *= 2 + } +} + +// symMerge_func merges the two sorted subsequences data[a:m] and data[m:b] using +// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum +// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz +// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in +// Computer Science, pages 714-723. Springer, 2004. +// +// Let M = m-a and N = b-n. Wolog M < N. +// The recursion depth is bound by ceil(log(N+M)). +// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. +// The algorithm needs O((M+N)*log(M)) calls to data.Swap. +// +// The paper gives O((M+N)*log(M)) as the number of assignments assuming a +// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation +// in the paper carries through for Swap operations, especially as the block +// swapping rotate uses only O(M+N) Swaps. +// +// symMerge assumes non-degenerate arguments: a < m && m < b. +// Having the caller check this condition eliminates many leaf recursion calls, +// which improves performance. +func symMerge_func(data lessSwap, a, m, b int) { + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[a] into data[m:b] + // if data[a:m] only contains one element. + if m-a == 1 { + // Use binary search to find the lowest index i + // such that data[i] >= data[a] for m <= i < b. + // Exit the search loop with i == b in case no such index exists. + i := m + j := b + for i < j { + h := int(uint(i+j) >> 1) + if data.Less(h, a) { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[a] reaches the position before i. + for k := a; k < i-1; k++ { + data.Swap(k, k+1) + } + return + } + + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[m] into data[a:m] + // if data[m:b] only contains one element. + if b-m == 1 { + // Use binary search to find the lowest index i + // such that data[i] > data[m] for a <= i < m. + // Exit the search loop with i == m in case no such index exists. + i := a + j := m + for i < j { + h := int(uint(i+j) >> 1) + if !data.Less(m, h) { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[m] reaches the position i. + for k := m; k > i; k-- { + data.Swap(k, k-1) + } + return + } + + mid := int(uint(a+b) >> 1) + n := mid + m + var start, r int + if m > mid { + start = n - b + r = mid + } else { + start = a + r = m + } + p := n - 1 + + for start < r { + c := int(uint(start+r) >> 1) + if !data.Less(p-c, c) { + start = c + 1 + } else { + r = c + } + } + + end := n - start + if start < m && m < end { + rotate_func(data, start, m, end) + } + if a < start && start < mid { + symMerge_func(data, a, start, mid) + } + if mid < end && end < b { + symMerge_func(data, mid, end, b) + } +} + +// rotate_func rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: +// Data of the form 'x u v y' is changed to 'x v u y'. +// rotate performs at most b-a many calls to data.Swap, +// and it assumes non-degenerate arguments: a < m && m < b. +func rotate_func(data lessSwap, a, m, b int) { + i := m - a + j := b - m + + for i != j { + if i > j { + swapRange_func(data, m-i, m, j) + i -= j + } else { + swapRange_func(data, m-i, m+j-i, i) + j -= i + } + } + // i == j + swapRange_func(data, m-i, m, i) +} diff --git a/src/sort/zsortinterface.go b/src/sort/zsortinterface.go new file mode 100644 index 00000000000000..51fa5032e9912a --- /dev/null +++ b/src/sort/zsortinterface.go @@ -0,0 +1,479 @@ +// Code generated by gen_sort_variants.go; DO NOT EDIT. + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sort + +// insertionSort sorts data[a:b] using insertion sort. +func insertionSort(data Interface, a, b int) { + for i := a + 1; i < b; i++ { + for j := i; j > a && data.Less(j, j-1); j-- { + data.Swap(j, j-1) + } + } +} + +// siftDown implements the heap property on data[lo:hi]. +// first is an offset into the array where the root of the heap lies. +func siftDown(data Interface, lo, hi, first int) { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && data.Less(first+child, first+child+1) { + child++ + } + if !data.Less(first+root, first+child) { + return + } + data.Swap(first+root, first+child) + root = child + } +} + +func heapSort(data Interface, a, b int) { + first := a + lo := 0 + hi := b - a + + // Build heap with greatest element at top. + for i := (hi - 1) / 2; i >= 0; i-- { + siftDown(data, i, hi, first) + } + + // Pop elements, largest first, into end of data. + for i := hi - 1; i >= 0; i-- { + data.Swap(first, first+i) + siftDown(data, lo, i, first) + } +} + +// pdqsort sorts data[a:b]. +// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. +// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf +// C++ implementation: https://github.com/orlp/pdqsort +// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ +// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. +func pdqsort(data Interface, a, b, limit int) { + const maxInsertion = 12 + + var ( + wasBalanced = true // whether the last partitioning was reasonably balanced + wasPartitioned = true // whether the slice was already partitioned + ) + + for { + length := b - a + + if length <= maxInsertion { + insertionSort(data, a, b) + return + } + + // Fall back to heapsort if too many bad choices were made. + if limit == 0 { + heapSort(data, a, b) + return + } + + // If the last partitioning was imbalanced, we need to breaking patterns. + if !wasBalanced { + breakPatterns(data, a, b) + limit-- + } + + pivot, hint := choosePivot(data, a, b) + if hint == decreasingHint { + reverseRange(data, a, b) + // The chosen pivot was pivot-a elements after the start of the array. + // After reversing it is pivot-a elements before the end of the array. + // The idea came from Rust's implementation. + pivot = (b - 1) - (pivot - a) + hint = increasingHint + } + + // The slice is likely already sorted. + if wasBalanced && wasPartitioned && hint == increasingHint { + if partialInsertionSort(data, a, b) { + return + } + } + + // Probably the slice contains many duplicate elements, partition the slice into + // elements equal to and elements greater than the pivot. + if a > 0 && !data.Less(a-1, pivot) { + mid := partitionEqual(data, a, b, pivot) + a = mid + continue + } + + mid, alreadyPartitioned := partition(data, a, b, pivot) + wasPartitioned = alreadyPartitioned + + leftLen, rightLen := mid-a, b-mid + balanceThreshold := length / 8 + if leftLen < rightLen { + wasBalanced = leftLen >= balanceThreshold + pdqsort(data, a, mid, limit) + a = mid + 1 + } else { + wasBalanced = rightLen >= balanceThreshold + pdqsort(data, mid+1, b, limit) + b = mid + } + } +} + +// partition does one quicksort partition. +// Let p = data[pivot] +// Moves elements in data[a:b] around, so that data[i]

        =p for inewpivot. +// On return, data[newpivot] = p +func partition(data Interface, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { + data.Swap(a, pivot) + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for i <= j && data.Less(i, a) { + i++ + } + for i <= j && !data.Less(j, a) { + j-- + } + if i > j { + data.Swap(j, a) + return j, true + } + data.Swap(i, j) + i++ + j-- + + for { + for i <= j && data.Less(i, a) { + i++ + } + for i <= j && !data.Less(j, a) { + j-- + } + if i > j { + break + } + data.Swap(i, j) + i++ + j-- + } + data.Swap(j, a) + return j, false +} + +// partitionEqual partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. +// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. +func partitionEqual(data Interface, a, b, pivot int) (newpivot int) { + data.Swap(a, pivot) + i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned + + for { + for i <= j && !data.Less(a, i) { + i++ + } + for i <= j && data.Less(a, j) { + j-- + } + if i > j { + break + } + data.Swap(i, j) + i++ + j-- + } + return i +} + +// partialInsertionSort partially sorts a slice, returns true if the slice is sorted at the end. +func partialInsertionSort(data Interface, a, b int) bool { + const ( + maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted + shortestShifting = 50 // don't shift any elements on short arrays + ) + i := a + 1 + for j := 0; j < maxSteps; j++ { + for i < b && !data.Less(i, i-1) { + i++ + } + + if i == b { + return true + } + + if b-a < shortestShifting { + return false + } + + data.Swap(i, i-1) + + // Shift the smaller one to the left. + if i-a >= 2 { + for j := i - 1; j >= 1; j-- { + if !data.Less(j, j-1) { + break + } + data.Swap(j, j-1) + } + } + // Shift the greater one to the right. + if b-i >= 2 { + for j := i + 1; j < b; j++ { + if !data.Less(j, j-1) { + break + } + data.Swap(j, j-1) + } + } + } + return false +} + +// breakPatterns scatters some elements around in an attempt to break some patterns +// that might cause imbalanced partitions in quicksort. +func breakPatterns(data Interface, a, b int) { + length := b - a + if length >= 8 { + random := xorshift(length) + modulus := nextPowerOfTwo(length) + + for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { + other := int(uint(random.Next()) & (modulus - 1)) + if other >= length { + other -= length + } + data.Swap(idx, a+other) + } + } +} + +// choosePivot chooses a pivot in data[a:b]. +// +// [0,8): chooses a static pivot. +// [8,shortestNinther): uses the simple median-of-three method. +// [shortestNinther,∞): uses the Tukey ninther method. +func choosePivot(data Interface, a, b int) (pivot int, hint sortedHint) { + const ( + shortestNinther = 50 + maxSwaps = 4 * 3 + ) + + l := b - a + + var ( + swaps int + i = a + l/4*1 + j = a + l/4*2 + k = a + l/4*3 + ) + + if l >= 8 { + if l >= shortestNinther { + // Tukey ninther method, the idea came from Rust's implementation. + i = medianAdjacent(data, i, &swaps) + j = medianAdjacent(data, j, &swaps) + k = medianAdjacent(data, k, &swaps) + } + // Find the median among i, j, k and stores it into j. + j = median(data, i, j, k, &swaps) + } + + switch swaps { + case 0: + return j, increasingHint + case maxSwaps: + return j, decreasingHint + default: + return j, unknownHint + } +} + +// order2 returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. +func order2(data Interface, a, b int, swaps *int) (int, int) { + if data.Less(b, a) { + *swaps++ + return b, a + } + return a, b +} + +// median returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. +func median(data Interface, a, b, c int, swaps *int) int { + a, b = order2(data, a, b, swaps) + b, c = order2(data, b, c, swaps) + a, b = order2(data, a, b, swaps) + return b +} + +// medianAdjacent finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. +func medianAdjacent(data Interface, a int, swaps *int) int { + return median(data, a-1, a, a+1, swaps) +} + +func reverseRange(data Interface, a, b int) { + i := a + j := b - 1 + for i < j { + data.Swap(i, j) + i++ + j-- + } +} + +func swapRange(data Interface, a, b, n int) { + for i := 0; i < n; i++ { + data.Swap(a+i, b+i) + } +} + +func stable(data Interface, n int) { + blockSize := 20 // must be > 0 + a, b := 0, blockSize + for b <= n { + insertionSort(data, a, b) + a = b + b += blockSize + } + insertionSort(data, a, n) + + for blockSize < n { + a, b = 0, 2*blockSize + for b <= n { + symMerge(data, a, a+blockSize, b) + a = b + b += 2 * blockSize + } + if m := a + blockSize; m < n { + symMerge(data, a, m, n) + } + blockSize *= 2 + } +} + +// symMerge merges the two sorted subsequences data[a:m] and data[m:b] using +// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum +// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz +// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in +// Computer Science, pages 714-723. Springer, 2004. +// +// Let M = m-a and N = b-n. Wolog M < N. +// The recursion depth is bound by ceil(log(N+M)). +// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. +// The algorithm needs O((M+N)*log(M)) calls to data.Swap. +// +// The paper gives O((M+N)*log(M)) as the number of assignments assuming a +// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation +// in the paper carries through for Swap operations, especially as the block +// swapping rotate uses only O(M+N) Swaps. +// +// symMerge assumes non-degenerate arguments: a < m && m < b. +// Having the caller check this condition eliminates many leaf recursion calls, +// which improves performance. +func symMerge(data Interface, a, m, b int) { + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[a] into data[m:b] + // if data[a:m] only contains one element. + if m-a == 1 { + // Use binary search to find the lowest index i + // such that data[i] >= data[a] for m <= i < b. + // Exit the search loop with i == b in case no such index exists. + i := m + j := b + for i < j { + h := int(uint(i+j) >> 1) + if data.Less(h, a) { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[a] reaches the position before i. + for k := a; k < i-1; k++ { + data.Swap(k, k+1) + } + return + } + + // Avoid unnecessary recursions of symMerge + // by direct insertion of data[m] into data[a:m] + // if data[m:b] only contains one element. + if b-m == 1 { + // Use binary search to find the lowest index i + // such that data[i] > data[m] for a <= i < m. + // Exit the search loop with i == m in case no such index exists. + i := a + j := m + for i < j { + h := int(uint(i+j) >> 1) + if !data.Less(m, h) { + i = h + 1 + } else { + j = h + } + } + // Swap values until data[m] reaches the position i. + for k := m; k > i; k-- { + data.Swap(k, k-1) + } + return + } + + mid := int(uint(a+b) >> 1) + n := mid + m + var start, r int + if m > mid { + start = n - b + r = mid + } else { + start = a + r = m + } + p := n - 1 + + for start < r { + c := int(uint(start+r) >> 1) + if !data.Less(p-c, c) { + start = c + 1 + } else { + r = c + } + } + + end := n - start + if start < m && m < end { + rotate(data, start, m, end) + } + if a < start && start < mid { + symMerge(data, a, start, mid) + } + if mid < end && end < b { + symMerge(data, mid, end, b) + } +} + +// rotate rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: +// Data of the form 'x u v y' is changed to 'x v u y'. +// rotate performs at most b-a many calls to data.Swap, +// and it assumes non-degenerate arguments: a < m && m < b. +func rotate(data Interface, a, m, b int) { + i := m - a + j := b - m + + for i != j { + if i > j { + swapRange(data, m-i, m, j) + i -= j + } else { + swapRange(data, m-i, m+j-i, i) + j -= i + } + } + // i == j + swapRange(data, m-i, m, i) +} diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go index 85c7bafefa4f3c..f6fdd14e649609 100644 --- a/src/strconv/atoc.go +++ b/src/strconv/atoc.go @@ -11,7 +11,7 @@ const fnParseComplex = "ParseComplex" func convErr(err error, s string) (syntax, range_ error) { if x, ok := err.(*NumError); ok { x.Func = fnParseComplex - x.Num = s + x.Num = cloneString(s) if x.Err == ErrRange { return nil, x } diff --git a/src/strconv/atof.go b/src/strconv/atof.go index 57556c704751b5..8fc90425f69801 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -420,9 +420,11 @@ var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1 // If possible to convert decimal representation to 64-bit float f exactly, // entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits. // Three common cases: +// // value is exact integer // value is exact integer * exact power of ten // value is exact integer / exact power of ten +// // These all produce potentially inexact but correctly rounded answers. func atof64exact(mantissa uint64, exp int, neg bool) (f float64, ok bool) { if mantissa>>float64info.mantbits != 0 { @@ -668,7 +670,8 @@ func atof64(s string) (f float64, n int, err error) { // When bitSize=32, the result still has type float64, but it will be // convertible to float32 without changing its value. // -// ParseFloat accepts decimal and hexadecimal floating-point number syntax. +// ParseFloat accepts decimal and hexadecimal floating-point numbers +// as defined by the Go syntax for [floating-point literals]. // If s is well-formed and near a valid floating-point number, // ParseFloat returns the nearest floating-point number rounded // using IEEE754 unbiased rounding. @@ -685,8 +688,10 @@ func atof64(s string) (f float64, n int, err error) { // away from the largest floating point number of the given size, // ParseFloat returns f = ±Inf, err.Err = ErrRange. // -// ParseFloat recognizes the strings "NaN", and the (possibly signed) strings "Inf" and "Infinity" +// ParseFloat recognizes the string "NaN", and the (possibly signed) strings "Inf" and "Infinity" // as their respective special floating point values. It ignores case when matching. +// +// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals func ParseFloat(s string, bitSize int) (float64, error) { f, n, err := parseFloatPrefix(s, bitSize) if n != len(s) && (err == nil || err.(*NumError).Err != ErrSyntax) { diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go index 631b487d9762bb..520d826323bfd8 100644 --- a/src/strconv/atoi.go +++ b/src/strconv/atoi.go @@ -33,20 +33,36 @@ func (e *NumError) Error() string { func (e *NumError) Unwrap() error { return e.Err } +// cloneString returns a string copy of x. +// +// All ParseXXX functions allow the input string to escape to the error value. +// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since +// the conversion from []byte must allocate a string on the heap. +// If we assume errors are infrequent, then we can avoid escaping the input +// back to the output by copying it first. This allows the compiler to call +// strconv.ParseXXX without a heap allocation for most []byte to string +// conversions, since it can now prove that the string cannot escape Parse. +// +// TODO: Use strings.Clone instead? However, we cannot depend on "strings" +// since it incurs a transitive dependency on "unicode". +// Either move strings.Clone to an internal/bytealg or make the +// "strings" to "unicode" dependency lighter (see https://go.dev/issue/54098). +func cloneString(x string) string { return string([]byte(x)) } + func syntaxError(fn, str string) *NumError { - return &NumError{fn, str, ErrSyntax} + return &NumError{fn, cloneString(str), ErrSyntax} } func rangeError(fn, str string) *NumError { - return &NumError{fn, str, ErrRange} + return &NumError{fn, cloneString(str), ErrRange} } func baseError(fn, str string, base int) *NumError { - return &NumError{fn, str, errors.New("invalid base " + Itoa(base))} + return &NumError{fn, cloneString(str), errors.New("invalid base " + Itoa(base))} } func bitSizeError(fn, str string, bitSize int) *NumError { - return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))} + return &NumError{fn, cloneString(str), errors.New("invalid bit size " + Itoa(bitSize))} } const intSize = 32 << (^uint(0) >> 63) @@ -167,7 +183,7 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) { // prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o", // 16 for "0x", and 10 otherwise. Also, for argument base 0 only, // underscore characters are permitted as defined by the Go syntax for -// integer literals. +// [integer literals]. // // The bitSize argument specifies the integer type // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 @@ -181,6 +197,8 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) { // signed integer of the given size, err.Err = ErrRange and the // returned value is the maximum magnitude integer of the // appropriate bitSize and sign. +// +// [integer literals]: https://go.dev/ref/spec#Integer_literals func ParseInt(s string, base int, bitSize int) (i int64, err error) { const fnParseInt = "ParseInt" @@ -203,7 +221,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) { un, err = ParseUint(s, base, bitSize) if err != nil && err.(*NumError).Err != ErrRange { err.(*NumError).Func = fnParseInt - err.(*NumError).Num = s0 + err.(*NumError).Num = cloneString(s0) return 0, err } @@ -237,7 +255,7 @@ func Atoi(s string) (int, error) { if s[0] == '-' || s[0] == '+' { s = s[1:] if len(s) < 1 { - return 0, &NumError{fnAtoi, s0, ErrSyntax} + return 0, syntaxError(fnAtoi, s0) } } @@ -245,7 +263,7 @@ func Atoi(s string) (int, error) { for _, ch := range []byte(s) { ch -= '0' if ch > 9 { - return 0, &NumError{fnAtoi, s0, ErrSyntax} + return 0, syntaxError(fnAtoi, s0) } n = n*10 + int(ch) } diff --git a/src/strconv/doc.go b/src/strconv/doc.go index 8db725f96ae5c0..769ecd9a21ca9c 100644 --- a/src/strconv/doc.go +++ b/src/strconv/doc.go @@ -5,7 +5,7 @@ // Package strconv implements conversions to and from string representations // of basic data types. // -// Numeric Conversions +// # Numeric Conversions // // The most common numeric conversions are Atoi (string to int) and Itoa (int to string). // @@ -40,7 +40,7 @@ // AppendBool, AppendFloat, AppendInt, and AppendUint are similar but // append the formatted value to a destination slice. // -// String Conversions +// # String Conversions // // Quote and QuoteToASCII convert strings to quoted Go string literals. // The latter guarantees that the result is an ASCII string, by escaping @@ -53,5 +53,4 @@ // return quoted Go rune literals. // // Unquote and UnquoteChar unquote Go string and rune literals. -// package strconv diff --git a/src/strconv/eisel_lemire.go b/src/strconv/eisel_lemire.go index fecd1b93451d07..03842e50797ca0 100644 --- a/src/strconv/eisel_lemire.go +++ b/src/strconv/eisel_lemire.go @@ -176,8 +176,8 @@ const ( // detailedPowersOfTen contains 128-bit mantissa approximations (rounded down) // to the powers of 10. For example: // -// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79)) -// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15)) +// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79)) +// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15)) // // The mantissas are explicitly listed. The exponents are implied by a linear // expression with slope 217706.0/65536.0 ≈ log(10)/log(2). diff --git a/src/strconv/fp_test.go b/src/strconv/fp_test.go index 39dd9c4a581539..fd73958c97d29e 100644 --- a/src/strconv/fp_test.go +++ b/src/strconv/fp_test.go @@ -28,15 +28,14 @@ func pow2(i int) float64 { // Wrapper around strconv.ParseFloat(x, 64). Handles dddddp+ddd (binary exponent) // itself, passes the rest on to strconv.ParseFloat. func myatof64(s string) (f float64, ok bool) { - a := strings.SplitN(s, "p", 2) - if len(a) == 2 { - n, err := strconv.ParseInt(a[0], 10, 64) + if mant, exp, ok := strings.Cut(s, "p"); ok { + n, err := strconv.ParseInt(mant, 10, 64) if err != nil { return 0, false } - e, err1 := strconv.Atoi(a[1]) + e, err1 := strconv.Atoi(exp) if err1 != nil { - println("bad e", a[1]) + println("bad e", exp) return 0, false } v := float64(n) @@ -72,16 +71,15 @@ func myatof64(s string) (f float64, ok bool) { // Wrapper around strconv.ParseFloat(x, 32). Handles dddddp+ddd (binary exponent) // itself, passes the rest on to strconv.ParseFloat. func myatof32(s string) (f float32, ok bool) { - a := strings.SplitN(s, "p", 2) - if len(a) == 2 { - n, err := strconv.Atoi(a[0]) + if mant, exp, ok := strings.Cut(s, "p"); ok { + n, err := strconv.Atoi(mant) if err != nil { - println("bad n", a[0]) + println("bad n", mant) return 0, false } - e, err1 := strconv.Atoi(a[1]) + e, err1 := strconv.Atoi(exp) if err1 != nil { - println("bad p", a[1]) + println("bad p", exp) return 0, false } return float32(float64(n) * pow2(e)), true diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go index eca04b851c3ad0..f602d0ffe6cc9b 100644 --- a/src/strconv/ftoa.go +++ b/src/strconv/ftoa.go @@ -138,6 +138,9 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { prec = 1 } digits = prec + default: + // Invalid mode. + digits = 1 } var buf [24]byte if bitSize == 32 && digits <= 9 { diff --git a/src/strconv/ftoa_test.go b/src/strconv/ftoa_test.go index 73008b1c621913..3512ccf58070ec 100644 --- a/src/strconv/ftoa_test.go +++ b/src/strconv/ftoa_test.go @@ -151,6 +151,11 @@ var ftoatests = []ftoaTest{ {498484681984085570, 'f', -1, "498484681984085570"}, {-5.8339553793802237e+23, 'g', -1, "-5.8339553793802237e+23"}, + // Issue 52187 + {123.45, '?', 0, "%?"}, + {123.45, '?', 1, "%?"}, + {123.45, '?', -1, "%?"}, + // rounding {2.275555555555555, 'x', -1, "0x1.23456789abcdep+01"}, {2.275555555555555, 'x', 0, "0x1p+01"}, diff --git a/src/strconv/ftoaryu.go b/src/strconv/ftoaryu.go index 1c61288b9ffa8e..2e7bf71df0b66a 100644 --- a/src/strconv/ftoaryu.go +++ b/src/strconv/ftoaryu.go @@ -33,7 +33,7 @@ func ryuFtoaFixed32(d *decimalSlice, mant uint32, exp int, prec int) { e2 := exp if b := bits.Len32(mant); b < 25 { mant <<= uint(25 - b) - e2 += int(b) - 25 + e2 += b - 25 } // Choose an exponent such that rounded mant*(2^e2)*(10^q) has // at least prec decimal digits, i.e @@ -100,7 +100,7 @@ func ryuFtoaFixed64(d *decimalSlice, mant uint64, exp int, prec int) { e2 := exp if b := bits.Len64(mant); b < 55 { mant = mant << uint(55-b) - e2 += int(b) - 55 + e2 += b - 55 } // Choose an exponent such that rounded mant*(2^e2)*(10^q) has // at least prec decimal digits, i.e @@ -194,7 +194,7 @@ func formatDecimal(d *decimalSlice, m uint64, trunc bool, roundUp bool, prec int } // render digits (similar to formatBits) n := uint(prec) - d.nd = int(prec) + d.nd = prec v := m for v >= 100 { var v1, v2 uint64 @@ -291,7 +291,7 @@ func ryuFtoaShortest(d *decimalSlice, mant uint64, exp int, flt *floatInfo) { // Is it allowed to use 'du' as a result? // It is always allowed when it is truncated, but also // if it is exact and the original binary mantissa is even - // When disallowed, we can substract 1. + // When disallowed, we can subtract 1. uok := !du0 || fracu > 0 if du0 && fracu == 0 { uok = mant&1 == 0 @@ -487,8 +487,9 @@ func ryuDigits32(d *decimalSlice, lower, central, upper uint32, // The returned boolean is true if all trimmed bits were zero. // // That is: -// m*2^e2 * round(10^q) = resM * 2^resE + ε -// exact = ε == 0 +// +// m*2^e2 * round(10^q) = resM * 2^resE + ε +// exact = ε == 0 func mult64bitPow10(m uint32, e2, q int) (resM uint32, resE int, exact bool) { if q == 0 { // P == 1<<63 @@ -515,8 +516,9 @@ func mult64bitPow10(m uint32, e2, q int) (resM uint32, resE int, exact bool) { // The returned boolean is true is all trimmed bits were zero. // // That is: -// m*2^e2 * round(10^q) = resM * 2^resE + ε -// exact = ε == 0 +// +// m*2^e2 * round(10^q) = resM * 2^resE + ε +// exact = ε == 0 func mult128bitPow10(m uint64, e2, q int) (resM uint64, resE int, exact bool) { if q == 0 { // P == 1<<127 diff --git a/src/strconv/itoa.go b/src/strconv/itoa.go index 45e4192c82e16f..b0c2666e7cb732 100644 --- a/src/strconv/itoa.go +++ b/src/strconv/itoa.go @@ -85,7 +85,6 @@ const digits = "0123456789abcdefghijklmnopqrstuvwxyz" // set, the string is appended to dst and the resulting byte slice is // returned as the first result value; otherwise the string is returned // as the second result value. -// func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) { if base < 2 || base > len(digits) { panic("strconv: illegal AppendInt/FormatInt base") diff --git a/src/strconv/quote.go b/src/strconv/quote.go index b3bbb1612b9f63..1b5bddfeaea7bc 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -99,11 +99,11 @@ func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bo buf = append(buf, `\v`...) default: switch { - case r < ' ': + case r < ' ' || r == 0x7f: buf = append(buf, `\x`...) buf = append(buf, lowerhex[byte(r)>>4]) buf = append(buf, lowerhex[byte(r)&0xF]) - case r > utf8.MaxRune: + case !utf8.ValidRune(r): r = 0xFFFD fallthrough case r < 0x10000: @@ -165,6 +165,8 @@ func AppendQuoteToGraphic(dst []byte, s string) []byte { // QuoteRune returns a single-quoted Go character literal representing the // rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) // for control characters and non-printable characters as defined by IsPrint. +// If r is not a valid Unicode code point, it is interpreted as the Unicode +// replacement character U+FFFD. func QuoteRune(r rune) string { return quoteRuneWith(r, '\'', false, false) } @@ -179,6 +181,8 @@ func AppendQuoteRune(dst []byte, r rune) []byte { // the rune. The returned string uses Go escape sequences (\t, \n, \xFF, // \u0100) for non-ASCII characters and non-printable characters as defined // by IsPrint. +// If r is not a valid Unicode code point, it is interpreted as the Unicode +// replacement character U+FFFD. func QuoteRuneToASCII(r rune) string { return quoteRuneWith(r, '\'', true, false) } @@ -193,6 +197,8 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { // the rune. If the rune is not a Unicode graphic character, // as defined by IsGraphic, the returned string will use a Go escape sequence // (\t, \n, \xFF, \u0100). +// If r is not a valid Unicode code point, it is interpreted as the Unicode +// replacement character U+FFFD. func QuoteRuneToGraphic(r rune) string { return quoteRuneWith(r, '\'', false, true) } @@ -243,10 +249,10 @@ func unhex(b byte) (v rune, ok bool) { // or character literal represented by the string s. // It returns four values: // -// 1) value, the decoded Unicode code point or byte value; -// 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation; -// 3) tail, the remainder of the string after the character; and -// 4) an error that will be nil if the character is syntactically valid. +// 1. value, the decoded Unicode code point or byte value; +// 2. multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation; +// 3. tail, the remainder of the string after the character; and +// 4. an error that will be nil if the character is syntactically valid. // // The second argument, quote, specifies the type of literal being parsed // and therefore which escaped quote character is permitted. @@ -322,7 +328,7 @@ func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, value = v break } - if v > utf8.MaxRune { + if !utf8.ValidRune(v) { err = ErrSyntax return } diff --git a/src/strconv/quote_test.go b/src/strconv/quote_test.go index 4750be27408a65..fc000de7b17f1d 100644 --- a/src/strconv/quote_test.go +++ b/src/strconv/quote_test.go @@ -55,6 +55,7 @@ var quotetests = []quoteTest{ {"\x04", `"\x04"`, `"\x04"`, `"\x04"`}, // Some non-printable but graphic runes. Final column is double-quoted. {"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""}, + {"\x7f", `"\x7f"`, `"\x7f"`, `"\x7f"`}, } func TestQuote(t *testing.T) { @@ -131,6 +132,7 @@ var quoterunetests = []quoteRuneTest{ {'\\', `'\\'`, `'\\'`, `'\\'`}, {0xFF, `'ÿ'`, `'\u00ff'`, `'ÿ'`}, {0x263a, `'☺'`, `'\u263a'`, `'☺'`}, + {0xdead, `'�'`, `'\ufffd'`, `'�'`}, {0xfffd, `'�'`, `'\ufffd'`, `'�'`}, {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`, `'\U0010ffff'`}, {0x0010ffff + 1, `'�'`, `'\ufffd'`, `'�'`}, @@ -305,6 +307,8 @@ var misquoted = []string{ "\"\n\"", "\"\\n\n\"", "'\n'", + `"\udead"`, + `"\ud83d\ude4f"`, } func TestUnquote(t *testing.T) { diff --git a/src/strconv/strconv_test.go b/src/strconv/strconv_test.go index d3c1e953de0db6..41b8fa7e33742d 100644 --- a/src/strconv/strconv_test.go +++ b/src/strconv/strconv_test.go @@ -66,6 +66,68 @@ func TestCountMallocs(t *testing.T) { } } +// Sink makes sure the compiler cannot optimize away the benchmarks. +var Sink struct { + Bool bool + Int int + Int64 int64 + Uint64 uint64 + Float64 float64 + Complex128 complex128 + Error error + Bytes []byte +} + +func TestAllocationsFromBytes(t *testing.T) { + const runsPerTest = 100 + bytes := struct{ Bool, Number, String, Buffer []byte }{ + Bool: []byte("false"), + Number: []byte("123456789"), + String: []byte("hello, world!"), + Buffer: make([]byte, 1024), + } + + checkNoAllocs := func(f func()) func(t *testing.T) { + return func(t *testing.T) { + t.Helper() + if allocs := testing.AllocsPerRun(runsPerTest, f); allocs != 0 { + t.Errorf("got %v allocs, want 0 allocs", allocs) + } + } + } + + t.Run("Atoi", checkNoAllocs(func() { + Sink.Int, Sink.Error = Atoi(string(bytes.Number)) + })) + t.Run("ParseBool", checkNoAllocs(func() { + Sink.Bool, Sink.Error = ParseBool(string(bytes.Bool)) + })) + t.Run("ParseInt", checkNoAllocs(func() { + Sink.Int64, Sink.Error = ParseInt(string(bytes.Number), 10, 64) + })) + t.Run("ParseUint", checkNoAllocs(func() { + Sink.Uint64, Sink.Error = ParseUint(string(bytes.Number), 10, 64) + })) + t.Run("ParseFloat", checkNoAllocs(func() { + Sink.Float64, Sink.Error = ParseFloat(string(bytes.Number), 64) + })) + t.Run("ParseComplex", checkNoAllocs(func() { + Sink.Complex128, Sink.Error = ParseComplex(string(bytes.Number), 128) + })) + t.Run("CanBackquote", checkNoAllocs(func() { + Sink.Bool = CanBackquote(string(bytes.String)) + })) + t.Run("AppendQuote", checkNoAllocs(func() { + Sink.Bytes = AppendQuote(bytes.Buffer[:0], string(bytes.String)) + })) + t.Run("AppendQuoteToASCII", checkNoAllocs(func() { + Sink.Bytes = AppendQuoteToASCII(bytes.Buffer[:0], string(bytes.String)) + })) + t.Run("AppendQuoteToGraphic", checkNoAllocs(func() { + Sink.Bytes = AppendQuoteToGraphic(bytes.Buffer[:0], string(bytes.String)) + })) +} + func TestErrorPrefixes(t *testing.T) { _, errInt := Atoi("INVALID") _, errBool := ParseBool("INVALID") diff --git a/src/strings/builder.go b/src/strings/builder.go index 547e52e84dde03..7710464a0d6f3b 100644 --- a/src/strings/builder.go +++ b/src/strings/builder.go @@ -17,12 +17,12 @@ type Builder struct { buf []byte } -// noescape hides a pointer from escape analysis. noescape is -// the identity function but escape analysis doesn't think the -// output depends on the input. noescape is inlined and currently -// compiles down to zero instructions. +// noescape hides a pointer from escape analysis. It is the identity function +// but escape analysis doesn't think the output depends on the input. +// noescape is inlined and currently compiles down to zero instructions. // USE CAREFULLY! // This was copied from the runtime; see issues 23382 and 7921. +// //go:nosplit //go:nocheckptr func noescape(p unsafe.Pointer) unsafe.Pointer { @@ -45,7 +45,7 @@ func (b *Builder) copyCheck() { // String returns the accumulated string. func (b *Builder) String() string { - return *(*string)(unsafe.Pointer(&b.buf)) + return unsafe.String(unsafe.SliceData(b.buf), len(b.buf)) } // Len returns the number of accumulated bytes; b.Len() == len(b.String()). @@ -103,18 +103,9 @@ func (b *Builder) WriteByte(c byte) error { // It returns the length of r and a nil error. func (b *Builder) WriteRune(r rune) (int, error) { b.copyCheck() - // Compare as uint32 to correctly handle negative runes. - if uint32(r) < utf8.RuneSelf { - b.buf = append(b.buf, byte(r)) - return 1, nil - } - l := len(b.buf) - if cap(b.buf)-l < utf8.UTFMax { - b.grow(utf8.UTFMax) - } - n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r) - b.buf = b.buf[:l+n] - return n, nil + n := len(b.buf) + b.buf = utf8.AppendRune(b.buf, r) + return len(b.buf) - n, nil } // WriteString appends the contents of s to b's buffer. diff --git a/src/strings/clone.go b/src/strings/clone.go new file mode 100644 index 00000000000000..d14df11d497918 --- /dev/null +++ b/src/strings/clone.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strings + +import ( + "unsafe" +) + +// Clone returns a fresh copy of s. +// It guarantees to make a copy of s into a new allocation, +// which can be important when retaining only a small substring +// of a much larger string. Using Clone can help such programs +// use less memory. Of course, since using Clone makes a copy, +// overuse of Clone can make programs use more memory. +// Clone should typically be used only rarely, and only when +// profiling indicates that it is needed. +// For strings of length zero the string "" will be returned +// and no allocation is made. +func Clone(s string) string { + if len(s) == 0 { + return "" + } + b := make([]byte, len(s)) + copy(b, s) + return unsafe.String(&b[0], len(b)) +} diff --git a/src/strings/clone_test.go b/src/strings/clone_test.go new file mode 100644 index 00000000000000..77479cfacf6fa2 --- /dev/null +++ b/src/strings/clone_test.go @@ -0,0 +1,45 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file.ß + +package strings_test + +import ( + "strings" + "testing" + "unsafe" +) + +var emptyString string + +func TestClone(t *testing.T) { + var cloneTests = []string{ + "", + strings.Clone(""), + strings.Repeat("a", 42)[:0], + "short", + strings.Repeat("a", 42), + } + for _, input := range cloneTests { + clone := strings.Clone(input) + if clone != input { + t.Errorf("Clone(%q) = %q; want %q", input, clone, input) + } + + if len(input) != 0 && unsafe.StringData(clone) == unsafe.StringData(input) { + t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input) + } + + if len(input) == 0 && unsafe.StringData(clone) != unsafe.StringData(emptyString) { + t.Errorf("Clone(%#v) return value should be equal to empty string.", unsafe.StringData(input)) + } + } +} + +func BenchmarkClone(b *testing.B) { + var str = strings.Repeat("a", 42) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + stringSink = strings.Clone(str) + } +} diff --git a/src/strings/compare.go b/src/strings/compare.go index 1fe6b8d89a3f94..2bd4a243db2f83 100644 --- a/src/strings/compare.go +++ b/src/strings/compare.go @@ -5,7 +5,7 @@ package strings // Compare returns an integer comparing two strings lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. +// The result will be 0 if a == b, -1 if a < b, and +1 if a > b. // // Compare is included only for symmetry with package bytes. // It is usually clearer and always faster to use the built-in diff --git a/src/strings/compare_test.go b/src/strings/compare_test.go index 94554e0af794c5..a43578423da013 100644 --- a/src/strings/compare_test.go +++ b/src/strings/compare_test.go @@ -57,7 +57,7 @@ func TestCompareStrings(t *testing.T) { // unsafeString converts a []byte to a string with no allocation. // The caller must not modify b while the result string is in use. unsafeString := func(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) + return unsafe.String(unsafe.SliceData(b), len(b)) } lengths := make([]int, 0) // lengths to test in ascending order diff --git a/src/strings/example_test.go b/src/strings/example_test.go index 375f9cac6502e8..2a59512ceb9341 100644 --- a/src/strings/example_test.go +++ b/src/strings/example_test.go @@ -10,17 +10,15 @@ import ( "unicode" ) -func ExampleFields() { - fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz ")) - // Output: Fields are: ["foo" "bar" "baz"] -} - -func ExampleFieldsFunc() { - f := func(c rune) bool { - return !unicode.IsLetter(c) && !unicode.IsNumber(c) +func ExampleBuilder() { + var b strings.Builder + for i := 3; i >= 1; i-- { + fmt.Fprintf(&b, "%d...", i) } - fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f)) - // Output: Fields are: ["foo1" "bar2" "baz3"] + b.WriteString("ignition") + fmt.Println(b.String()) + + // Output: 3...2...1...ignition } func ExampleCompare() { @@ -79,9 +77,43 @@ func ExampleCount() { // 5 } +func ExampleCut() { + show := func(s, sep string) { + before, after, found := strings.Cut(s, sep) + fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found) + } + show("Gopher", "Go") + show("Gopher", "ph") + show("Gopher", "er") + show("Gopher", "Badger") + // Output: + // Cut("Gopher", "Go") = "", "pher", true + // Cut("Gopher", "ph") = "Go", "er", true + // Cut("Gopher", "er") = "Goph", "", true + // Cut("Gopher", "Badger") = "Gopher", "", false +} + func ExampleEqualFold() { fmt.Println(strings.EqualFold("Go", "go")) - // Output: true + fmt.Println(strings.EqualFold("AB", "ab")) // true because comparison uses simple case-folding + fmt.Println(strings.EqualFold("ß", "ss")) // false because comparison does not use full case-folding + // Output: + // true + // true + // false +} + +func ExampleFields() { + fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz ")) + // Output: Fields are: ["foo" "bar" "baz"] +} + +func ExampleFieldsFunc() { + f := func(c rune) bool { + return !unicode.IsLetter(c) && !unicode.IsNumber(c) + } + fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f)) + // Output: Fields are: ["foo1" "bar2" "baz3"] } func ExampleHasPrefix() { @@ -370,14 +402,3 @@ func ExampleTrimRightFunc() { })) // Output: ¡¡¡Hello, Gophers } - -func ExampleBuilder() { - var b strings.Builder - for i := 3; i >= 1; i-- { - fmt.Fprintf(&b, "%d...", i) - } - b.WriteString("ignition") - fmt.Println(b.String()) - - // Output: 3...2...1...ignition -} diff --git a/src/strings/export_test.go b/src/strings/export_test.go index b39cee6b1ded16..81d1cab9d0064e 100644 --- a/src/strings/export_test.go +++ b/src/strings/export_test.go @@ -4,7 +4,7 @@ package strings -func (r *Replacer) Replacer() interface{} { +func (r *Replacer) Replacer() any { r.once.Do(r.buildOnce) return r.r } diff --git a/src/strings/reader_test.go b/src/strings/reader_test.go index 5adea6f7ab2b32..dc99f9c2485816 100644 --- a/src/strings/reader_test.go +++ b/src/strings/reader_test.go @@ -77,7 +77,7 @@ func TestReaderAt(t *testing.T) { off int64 n int want string - wanterr interface{} + wanterr any }{ {0, 10, "0123456789", nil}, {1, 10, "123456789", io.EOF}, diff --git a/src/strings/replace.go b/src/strings/replace.go index e28d42887930b9..f504fb48dfbdd9 100644 --- a/src/strings/replace.go +++ b/src/strings/replace.go @@ -107,14 +107,14 @@ func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) { // and values may be empty. For example, the trie containing keys "ax", "ay", // "bcbc", "x" and "xy" could have eight nodes: // -// n0 - -// n1 a- -// n2 .x+ -// n3 .y+ -// n4 b- -// n5 .cbc+ -// n6 x+ -// n7 .y+ +// n0 - +// n1 a- +// n2 .x+ +// n3 .y+ +// n4 b- +// n5 .cbc+ +// n6 x+ +// n7 .y+ // // n0 is the root node, and its children are n1, n4 and n6; n1's children are // n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked @@ -387,7 +387,7 @@ func makeSingleStringReplacer(pattern string, value string) *singleStringReplace } func (r *singleStringReplacer) Replace(s string) string { - var buf []byte + var buf Builder i, matched := 0, false for { match := r.finder.next(s[i:]) @@ -395,15 +395,16 @@ func (r *singleStringReplacer) Replace(s string) string { break } matched = true - buf = append(buf, s[i:i+match]...) - buf = append(buf, r.value...) + buf.Grow(match + len(r.value)) + buf.WriteString(s[i : i+match]) + buf.WriteString(r.value) i += match + len(r.finder.pattern) } if !matched { return s } - buf = append(buf, s[i:]...) - return string(buf) + buf.WriteString(s[i:]) + return buf.String() } func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) { @@ -454,21 +455,30 @@ func (r *byteReplacer) Replace(s string) string { } func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) { - // TODO(bradfitz): use io.WriteString with slices of s, avoiding allocation. - bufsize := 32 << 10 - if len(s) < bufsize { - bufsize = len(s) - } - buf := make([]byte, bufsize) - - for len(s) > 0 { - ncopy := copy(buf, s) - s = s[ncopy:] - for i, b := range buf[:ncopy] { - buf[i] = r[b] + sw := getStringWriter(w) + last := 0 + for i := 0; i < len(s); i++ { + b := s[i] + if r[b] == b { + continue } - wn, err := w.Write(buf[:ncopy]) - n += wn + if last != i { + wn, err := sw.WriteString(s[last:i]) + n += wn + if err != nil { + return n, err + } + } + last = i + 1 + nw, err := w.Write(r[b : int(b)+1]) + n += nw + if err != nil { + return n, err + } + } + if last != len(s) { + nw, err := sw.WriteString(s[last:]) + n += nw if err != nil { return n, err } diff --git a/src/strings/strings.go b/src/strings/strings.go index b429735feadf9d..1e8de2bc34cacb 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -15,7 +15,7 @@ import ( // explode splits s into a slice of UTF-8 strings, // one string per Unicode character up to a maximum of n (n < 0 means no limit). -// Invalid UTF-8 sequences become correct encodings of U+FFFD. +// Invalid UTF-8 bytes are sliced individually. func explode(s string, n int) []string { l := utf8.RuneCountInString(s) if n < 0 || n > l { @@ -23,12 +23,9 @@ func explode(s string, n int) []string { } a := make([]string, n) for i := 0; i < n-1; i++ { - ch, size := utf8.DecodeRuneInString(s) + _, size := utf8.DecodeRuneInString(s) a[i] = s[:size] s = s[size:] - if ch == utf8.RuneError { - a[i] = string(utf8.RuneError) - } } if n > 0 { a[n-1] = s @@ -244,6 +241,9 @@ func genSplit(s, sep string, sepSave, n int) []string { n = Count(s, sep) + 1 } + if n > len(s)+1 { + n = len(s) + 1 + } a := make([]string, n) n-- i := 0 @@ -264,21 +264,25 @@ func genSplit(s, sep string, sepSave, n int) []string { // the substrings between those separators. // // The count determines the number of substrings to return: -// n > 0: at most n substrings; the last substring will be the unsplit remainder. -// n == 0: the result is nil (zero substrings) -// n < 0: all substrings +// +// n > 0: at most n substrings; the last substring will be the unsplit remainder. +// n == 0: the result is nil (zero substrings) +// n < 0: all substrings // // Edge cases for s and sep (for example, empty strings) are handled // as described in the documentation for Split. +// +// To split around the first instance of a separator, see Cut. func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } // SplitAfterN slices s into substrings after each instance of sep and // returns a slice of those substrings. // // The count determines the number of substrings to return: -// n > 0: at most n substrings; the last substring will be the unsplit remainder. -// n == 0: the result is nil (zero substrings) -// n < 0: all substrings +// +// n > 0: at most n substrings; the last substring will be the unsplit remainder. +// n == 0: the result is nil (zero substrings) +// n < 0: all substrings // // Edge cases for s and sep (for example, empty strings) are handled // as described in the documentation for SplitAfter. @@ -296,6 +300,8 @@ func SplitAfterN(s, sep string, n int) []string { // and sep are empty, Split returns an empty slice. // // It is equivalent to SplitN with a count of -1. +// +// To split around the first instance of a separator, see Cut. func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } // SplitAfter slices s into all substrings after each instance of sep and @@ -562,14 +568,24 @@ func ToUpper(s string) string { if !hasLower { return s } - var b Builder + var ( + b Builder + pos int + ) b.Grow(len(s)) for i := 0; i < len(s); i++ { c := s[i] if 'a' <= c && c <= 'z' { c -= 'a' - 'A' + if pos < i { + b.WriteString(s[pos:i]) + } + b.WriteByte(c) + pos = i + 1 } - b.WriteByte(c) + } + if pos < len(s) { + b.WriteString(s[pos:]) } return b.String() } @@ -592,14 +608,24 @@ func ToLower(s string) string { if !hasUpper { return s } - var b Builder + var ( + b Builder + pos int + ) b.Grow(len(s)) for i := 0; i < len(s); i++ { c := s[i] if 'A' <= c && c <= 'Z' { c += 'a' - 'A' + if pos < i { + b.WriteString(s[pos:i]) + } + b.WriteByte(c) + pos = i + 1 } - b.WriteByte(c) + } + if pos < len(s) { + b.WriteString(s[pos:]) } return b.String() } @@ -706,7 +732,8 @@ func isSeparator(r rune) bool { // Title returns a copy of the string s with all Unicode letters that begin words // mapped to their Unicode title case. // -// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly. +// Deprecated: The rule Title uses for word boundaries does not handle Unicode +// punctuation properly. Use golang.org/x/text/cases instead. func Title(s string) string { // Use a closure here to remember state. // Hackish but effective. Depends on Map scanning in order and calling @@ -797,6 +824,8 @@ func lastIndexFunc(s string, f func(rune) bool, truth bool) int { // most-significant bit of the highest word, map to the full range of all // 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed, // ensuring that any non-ASCII character will be reported as not in the set. +// This allocates a total of 32 bytes even though the upper half +// is unused to avoid bounds checks in asciiSet.contains. type asciiSet [8]uint32 // makeASCIISet creates a set of ASCII characters and reports whether all @@ -807,28 +836,14 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) { if c >= utf8.RuneSelf { return as, false } - as[c>>5] |= 1 << uint(c&31) + as[c/32] |= 1 << (c % 32) } return as, true } // contains reports whether c is inside the set. func (as *asciiSet) contains(c byte) bool { - return (as[c>>5] & (1 << uint(c&31))) != 0 -} - -func makeCutsetFunc(cutset string) func(rune) bool { - if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { - return func(r rune) bool { - return r == rune(cutset[0]) - } - } - if as, isASCII := makeASCIISet(cutset); isASCII { - return func(r rune) bool { - return r < utf8.RuneSelf && as.contains(byte(r)) - } - } - return func(r rune) bool { return IndexRune(cutset, r) >= 0 } + return (as[c/32] & (1 << (c % 32))) != 0 } // Trim returns a slice of the string s with all leading and @@ -837,7 +852,13 @@ func Trim(s, cutset string) string { if s == "" || cutset == "" { return s } - return TrimFunc(s, makeCutsetFunc(cutset)) + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimLeftASCII(trimRightASCII(s, &as), &as) + } + return trimLeftUnicode(trimRightUnicode(s, cutset), cutset) } // TrimLeft returns a slice of the string s with all leading @@ -848,7 +869,44 @@ func TrimLeft(s, cutset string) string { if s == "" || cutset == "" { return s } - return TrimLeftFunc(s, makeCutsetFunc(cutset)) + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimLeftByte(s, cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimLeftASCII(s, &as) + } + return trimLeftUnicode(s, cutset) +} + +func trimLeftByte(s string, c byte) string { + for len(s) > 0 && s[0] == c { + s = s[1:] + } + return s +} + +func trimLeftASCII(s string, as *asciiSet) string { + for len(s) > 0 { + if !as.contains(s[0]) { + break + } + s = s[1:] + } + return s +} + +func trimLeftUnicode(s, cutset string) string { + for len(s) > 0 { + r, n := rune(s[0]), 1 + if r >= utf8.RuneSelf { + r, n = utf8.DecodeRuneInString(s) + } + if !ContainsRune(cutset, r) { + break + } + s = s[n:] + } + return s } // TrimRight returns a slice of the string s, with all trailing @@ -859,7 +917,44 @@ func TrimRight(s, cutset string) string { if s == "" || cutset == "" { return s } - return TrimRightFunc(s, makeCutsetFunc(cutset)) + if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { + return trimRightByte(s, cutset[0]) + } + if as, ok := makeASCIISet(cutset); ok { + return trimRightASCII(s, &as) + } + return trimRightUnicode(s, cutset) +} + +func trimRightByte(s string, c byte) string { + for len(s) > 0 && s[len(s)-1] == c { + s = s[:len(s)-1] + } + return s +} + +func trimRightASCII(s string, as *asciiSet) string { + for len(s) > 0 { + if !as.contains(s[len(s)-1]) { + break + } + s = s[:len(s)-1] + } + return s +} + +func trimRightUnicode(s, cutset string) string { + for len(s) > 0 { + r, n := rune(s[len(s)-1]), 1 + if r >= utf8.RuneSelf { + r, n = utf8.DecodeLastRuneInString(s) + } + if !ContainsRune(cutset, r) { + break + } + s = s[:len(s)-n] + } + return s } // TrimSpace returns a slice of the string s, with all leading @@ -884,7 +979,8 @@ func TrimSpace(s string) string { for ; stop > start; stop-- { c := s[stop-1] if c >= utf8.RuneSelf { - return TrimFunc(s[start:stop], unicode.IsSpace) + // start has been already trimmed above, should trim end only + return TrimRightFunc(s[start:stop], unicode.IsSpace) } if asciiSpace[c] == 0 { break @@ -965,7 +1061,7 @@ func ReplaceAll(s, old, new string) string { } // EqualFold reports whether s and t, interpreted as UTF-8 strings, -// are equal under Unicode case-folding, which is a more general +// are equal under simple Unicode case-folding, which is a more general // form of case-insensitivity. func EqualFold(s, t string) bool { for s != "" && t != "" { @@ -1100,3 +1196,36 @@ func Index(s, substr string) int { } return -1 } + +// Cut slices s around the first instance of sep, +// returning the text before and after sep. +// The found result reports whether sep appears in s. +// If sep does not appear in s, cut returns s, "", false. +func Cut(s, sep string) (before, after string, found bool) { + if i := Index(s, sep); i >= 0 { + return s[:i], s[i+len(sep):], true + } + return s, "", false +} + +// CutPrefix returns s without the provided leading prefix string +// and reports whether it found the prefix. +// If s doesn't start with prefix, CutPrefix returns s, false. +// If prefix is the empty string, CutPrefix returns s, true. +func CutPrefix(s, prefix string) (after string, found bool) { + if !HasPrefix(s, prefix) { + return s, false + } + return s[len(prefix):], true +} + +// CutSuffix returns s without the provided ending suffix string +// and reports whether it found the suffix. +// If s doesn't end with suffix, CutSuffix returns s, false. +// If suffix is the empty string, CutSuffix returns s, true. +func CutSuffix(s, suffix string) (before string, found bool) { + if !HasSuffix(s, suffix) { + return s, false + } + return s[:len(s)-len(suffix)], true +} diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go index 09e5b27cc3857a..8af81a556bf476 100644 --- a/src/strings/strings_test.go +++ b/src/strings/strings_test.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "math" "math/rand" "reflect" "strconv" @@ -404,6 +405,9 @@ var splittests = []SplitTest{ {faces, "~", -1, []string{faces}}, {"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}}, {"1 2", " ", 3, []string{"1", "2"}}, + {"", "T", math.MaxInt / 4, []string{""}}, + {"\xff-\xff", "", -1, []string{"\xff", "-", "\xff"}}, + {"\xff-\xff", "-", -1, []string{"\xff", "\xff"}}, } func TestSplit(t *testing.T) { @@ -545,6 +549,7 @@ var upperTests = []StringTest{ {"AbC123", "ABC123"}, {"azAZ09_", "AZAZ09_"}, {"longStrinGwitHmixofsmaLLandcAps", "LONGSTRINGWITHMIXOFSMALLANDCAPS"}, + {"RENAN BASTOS 93 AOSDAJDJAIDJAIDAJIaidsjjaidijadsjiadjiOOKKO", "RENAN BASTOS 93 AOSDAJDJAIDJAIDAJIAIDSJJAIDIJADSJIADJIOOKKO"}, {"long\u0250string\u0250with\u0250nonascii\u2C6Fchars", "LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS"}, {"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"}, // grows one byte per char {"a\u0080\U0010FFFF", "A\u0080\U0010FFFF"}, // test utf8.RuneSelf and utf8.MaxRune @@ -556,6 +561,7 @@ var lowerTests = []StringTest{ {"AbC123", "abc123"}, {"azAZ09_", "azaz09_"}, {"longStrinGwitHmixofsmaLLandcAps", "longstringwithmixofsmallandcaps"}, + {"renan bastos 93 AOSDAJDJAIDJAIDAJIaidsjjaidijadsjiadjiOOKKO", "renan bastos 93 aosdajdjaidjaidajiaidsjjaidijadsjiadjiookko"}, {"LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS", "long\u0250string\u0250with\u0250nonascii\u0250chars"}, {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char {"A\u0080\U0010FFFF", "a\u0080\U0010FFFF"}, // test utf8.RuneSelf and utf8.MaxRune @@ -654,8 +660,7 @@ func TestMap(t *testing.T) { } orig := "Input string that we expect not to be copied." m = Map(identity, orig) - if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data != - (*reflect.StringHeader)(unsafe.Pointer(&m)).Data { + if unsafe.StringData(orig) != unsafe.StringData(m) { t.Error("unexpected copy during identity map") } @@ -808,7 +813,9 @@ var trimTests = []struct { {"TrimLeft", "abba", "ab", ""}, {"TrimRight", "abba", "ab", ""}, {"TrimLeft", "abba", "a", "bba"}, + {"TrimLeft", "abba", "b", "abba"}, {"TrimRight", "abba", "a", "abb"}, + {"TrimRight", "abba", "b", "abba"}, {"Trim", "", "<>", "tag"}, {"Trim", "* listitem", " *", "listitem"}, {"Trim", `"quote"`, `"`, "quote"}, @@ -1577,7 +1584,72 @@ var CountTests = []struct { func TestCount(t *testing.T) { for _, tt := range CountTests { if num := Count(tt.s, tt.sep); num != tt.num { - t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num) + t.Errorf("Count(%q, %q) = %d, want %d", tt.s, tt.sep, num, tt.num) + } + } +} + +var cutTests = []struct { + s, sep string + before, after string + found bool +}{ + {"abc", "b", "a", "c", true}, + {"abc", "a", "", "bc", true}, + {"abc", "c", "ab", "", true}, + {"abc", "abc", "", "", true}, + {"abc", "", "", "abc", true}, + {"abc", "d", "abc", "", false}, + {"", "d", "", "", false}, + {"", "", "", "", true}, +} + +func TestCut(t *testing.T) { + for _, tt := range cutTests { + if before, after, found := Cut(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found { + t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found) + } + } +} + +var cutPrefixTests = []struct { + s, sep string + after string + found bool +}{ + {"abc", "a", "bc", true}, + {"abc", "abc", "", true}, + {"abc", "", "abc", true}, + {"abc", "d", "abc", false}, + {"", "d", "", false}, + {"", "", "", true}, +} + +func TestCutPrefix(t *testing.T) { + for _, tt := range cutPrefixTests { + if after, found := CutPrefix(tt.s, tt.sep); after != tt.after || found != tt.found { + t.Errorf("CutPrefix(%q, %q) = %q, %v, want %q, %v", tt.s, tt.sep, after, found, tt.after, tt.found) + } + } +} + +var cutSuffixTests = []struct { + s, sep string + after string + found bool +}{ + {"abc", "bc", "a", true}, + {"abc", "abc", "", true}, + {"abc", "", "abc", true}, + {"abc", "d", "abc", false}, + {"", "d", "", false}, + {"", "", "", true}, +} + +func TestCutSuffix(t *testing.T) { + for _, tt := range cutSuffixTests { + if after, found := CutSuffix(tt.s, tt.sep); after != tt.after || found != tt.found { + t.Errorf("CutSuffix(%q, %q) = %q, %v, want %q, %v", tt.s, tt.sep, after, found, tt.after, tt.found) } } } @@ -1860,6 +1932,13 @@ func BenchmarkTrimASCII(b *testing.B) { } } +func BenchmarkTrimByte(b *testing.B) { + x := " the quick brown fox " + for i := 0; i < b.N; i++ { + Trim(x, " ") + } +} + func BenchmarkIndexPeriodic(b *testing.B) { key := "aa" for _, skip := range [...]int{2, 4, 8, 16, 32, 64} { diff --git a/src/sync/atomic/asm.s b/src/sync/atomic/asm.s index 7b8c9b9430ef40..2022304665ef91 100644 --- a/src/sync/atomic/asm.s +++ b/src/sync/atomic/asm.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race #include "textflag.h" diff --git a/src/sync/atomic/atomic_test.go b/src/sync/atomic/atomic_test.go index eadc962f70634b..0cc9b06a6c910c 100644 --- a/src/sync/atomic/atomic_test.go +++ b/src/sync/atomic/atomic_test.go @@ -6,7 +6,9 @@ package atomic_test import ( "fmt" + "reflect" "runtime" + "runtime/debug" "strings" . "sync/atomic" "testing" @@ -30,16 +32,6 @@ const ( magic64 = 0xdeddeadbeefbeef ) -// Do the 64-bit functions panic? If so, don't bother testing. -var test64err = func() (err interface{}) { - defer func() { - err = recover() - }() - var x int64 - AddInt64(&x, 1) - return nil -}() - func TestSwapInt32(t *testing.T) { var x struct { before int32 @@ -61,6 +53,27 @@ func TestSwapInt32(t *testing.T) { } } +func TestSwapInt32Method(t *testing.T) { + var x struct { + before int32 + i Int32 + after int32 + } + x.before = magic32 + x.after = magic32 + var j int32 + for delta := int32(1); delta+delta > delta; delta += delta { + k := x.i.Swap(delta) + if x.i.Load() != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + func TestSwapUint32(t *testing.T) { var x struct { before uint32 @@ -82,15 +95,34 @@ func TestSwapUint32(t *testing.T) { } } -func TestSwapInt64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestSwapUint32Method(t *testing.T) { + var x struct { + before uint32 + i Uint32 + after uint32 + } + x.before = magic32 + x.after = magic32 + var j uint32 + for delta := uint32(1); delta+delta > delta; delta += delta { + k := x.i.Swap(delta) + if x.i.Load() != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) } +} + +func TestSwapInt64(t *testing.T) { var x struct { before int64 i int64 after int64 } + magic64 := int64(magic64) x.before = magic64 x.after = magic64 var j int64 @@ -102,19 +134,39 @@ func TestSwapInt64(t *testing.T) { j = delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } -func TestSwapUint64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestSwapInt64Method(t *testing.T) { + var x struct { + before int64 + i Int64 + after int64 + } + magic64 := int64(magic64) + x.before = magic64 + x.after = magic64 + var j int64 + for delta := int64(1); delta+delta > delta; delta += delta { + k := x.i.Swap(delta) + if x.i.Load() != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + j = delta } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestSwapUint64(t *testing.T) { var x struct { before uint64 i uint64 after uint64 } + magic64 := uint64(magic64) x.before = magic64 x.after = magic64 var j uint64 @@ -126,7 +178,29 @@ func TestSwapUint64(t *testing.T) { j = delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestSwapUint64Method(t *testing.T) { + var x struct { + before uint64 + i Uint64 + after uint64 + } + magic64 := uint64(magic64) + x.before = magic64 + x.after = magic64 + var j uint64 + for delta := uint64(1); delta+delta > delta; delta += delta { + k := x.i.Swap(delta) + if x.i.Load() != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + j = delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } @@ -153,6 +227,29 @@ func TestSwapUintptr(t *testing.T) { } } +func TestSwapUintptrMethod(t *testing.T) { + var x struct { + before uintptr + i Uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := x.i.Swap(delta) + if x.i.Load() != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + j = delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + var global [1024]byte func testPointers() []unsafe.Pointer { @@ -192,6 +289,30 @@ func TestSwapPointer(t *testing.T) { } } +func TestSwapPointerMethod(t *testing.T) { + var x struct { + before uintptr + i Pointer[byte] + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j *byte + for _, p := range testPointers() { + p := (*byte)(p) + k := x.i.Swap(p) + if x.i.Load() != p || k != j { + t.Fatalf("p=%p i=%p j=%p k=%p", p, x.i.Load(), j, k) + } + j = p + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestAddInt32(t *testing.T) { var x struct { before int32 @@ -213,6 +334,27 @@ func TestAddInt32(t *testing.T) { } } +func TestAddInt32Method(t *testing.T) { + var x struct { + before int32 + i Int32 + after int32 + } + x.before = magic32 + x.after = magic32 + var j int32 + for delta := int32(1); delta+delta > delta; delta += delta { + k := x.i.Add(delta) + j += delta + if x.i.Load() != j || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + func TestAddUint32(t *testing.T) { var x struct { before uint32 @@ -234,15 +376,34 @@ func TestAddUint32(t *testing.T) { } } -func TestAddInt64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestAddUint32Method(t *testing.T) { + var x struct { + before uint32 + i Uint32 + after uint32 } + x.before = magic32 + x.after = magic32 + var j uint32 + for delta := uint32(1); delta+delta > delta; delta += delta { + k := x.i.Add(delta) + j += delta + if x.i.Load() != j || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestAddInt64(t *testing.T) { var x struct { before int64 i int64 after int64 } + magic64 := int64(magic64) x.before = magic64 x.after = magic64 var j int64 @@ -254,19 +415,39 @@ func TestAddInt64(t *testing.T) { } } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, int64(magic64), int64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } -func TestAddUint64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestAddInt64Method(t *testing.T) { + var x struct { + before int64 + i Int64 + after int64 } + magic64 := int64(magic64) + x.before = magic64 + x.after = magic64 + var j int64 + for delta := int64(1); delta+delta > delta; delta += delta { + k := x.i.Add(delta) + j += delta + if x.i.Load() != j || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestAddUint64(t *testing.T) { var x struct { before uint64 i uint64 after uint64 } + magic64 := uint64(magic64) x.before = magic64 x.after = magic64 var j uint64 @@ -278,7 +459,29 @@ func TestAddUint64(t *testing.T) { } } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestAddUint64Method(t *testing.T) { + var x struct { + before uint64 + i Uint64 + after uint64 + } + magic64 := uint64(magic64) + x.before = magic64 + x.after = magic64 + var j uint64 + for delta := uint64(1); delta+delta > delta; delta += delta { + k := x.i.Add(delta) + j += delta + if x.i.Load() != j || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } @@ -305,6 +508,29 @@ func TestAddUintptr(t *testing.T) { } } +func TestAddUintptrMethod(t *testing.T) { + var x struct { + before uintptr + i Uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := x.i.Add(delta) + j += delta + if x.i.Load() != j || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i.Load(), j, k) + } + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestCompareAndSwapInt32(t *testing.T) { var x struct { before int32 @@ -334,6 +560,35 @@ func TestCompareAndSwapInt32(t *testing.T) { } } +func TestCompareAndSwapInt32Method(t *testing.T) { + var x struct { + before int32 + i Int32 + after int32 + } + x.before = magic32 + x.after = magic32 + for val := int32(1); val+val > val; val += val { + x.i.Store(val) + if !x.i.CompareAndSwap(val, val+1) { + t.Fatalf("should have swapped %#x %#x", val, val+1) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + x.i.Store(val + 1) + if x.i.CompareAndSwap(val, val+2) { + t.Fatalf("should not have swapped %#x %#x", val, val+2) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + func TestCompareAndSwapUint32(t *testing.T) { var x struct { before uint32 @@ -363,15 +618,42 @@ func TestCompareAndSwapUint32(t *testing.T) { } } -func TestCompareAndSwapInt64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestCompareAndSwapUint32Method(t *testing.T) { + var x struct { + before uint32 + i Uint32 + after uint32 } + x.before = magic32 + x.after = magic32 + for val := uint32(1); val+val > val; val += val { + x.i.Store(val) + if !x.i.CompareAndSwap(val, val+1) { + t.Fatalf("should have swapped %#x %#x", val, val+1) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + x.i.Store(val + 1) + if x.i.CompareAndSwap(val, val+2) { + t.Fatalf("should not have swapped %#x %#x", val, val+2) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestCompareAndSwapInt64(t *testing.T) { var x struct { before int64 i int64 after int64 } + magic64 := int64(magic64) x.before = magic64 x.after = magic64 for val := int64(1); val+val > val; val += val { @@ -391,19 +673,47 @@ func TestCompareAndSwapInt64(t *testing.T) { } } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } -func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bool) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestCompareAndSwapInt64Method(t *testing.T) { + var x struct { + before int64 + i Int64 + after int64 + } + magic64 := int64(magic64) + x.before = magic64 + x.after = magic64 + for val := int64(1); val+val > val; val += val { + x.i.Store(val) + if !x.i.CompareAndSwap(val, val+1) { + t.Fatalf("should have swapped %#x %#x", val, val+1) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + x.i.Store(val + 1) + if x.i.CompareAndSwap(val, val+2) { + t.Fatalf("should not have swapped %#x %#x", val, val+2) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bool) { var x struct { before uint64 i uint64 after uint64 } + magic64 := uint64(magic64) x.before = magic64 x.after = magic64 for val := uint64(1); val+val > val; val += val { @@ -423,7 +733,7 @@ func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bo } } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } @@ -431,6 +741,36 @@ func TestCompareAndSwapUint64(t *testing.T) { testCompareAndSwapUint64(t, CompareAndSwapUint64) } +func TestCompareAndSwapUint64Method(t *testing.T) { + var x struct { + before uint64 + i Uint64 + after uint64 + } + magic64 := uint64(magic64) + x.before = magic64 + x.after = magic64 + for val := uint64(1); val+val > val; val += val { + x.i.Store(val) + if !x.i.CompareAndSwap(val, val+1) { + t.Fatalf("should have swapped %#x %#x", val, val+1) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + x.i.Store(val + 1) + if x.i.CompareAndSwap(val, val+2) { + t.Fatalf("should not have swapped %#x %#x", val, val+2) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + func TestCompareAndSwapUintptr(t *testing.T) { var x struct { before uintptr @@ -462,6 +802,37 @@ func TestCompareAndSwapUintptr(t *testing.T) { } } +func TestCompareAndSwapUintptrMethod(t *testing.T) { + var x struct { + before uintptr + i Uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + for val := uintptr(1); val+val > val; val += val { + x.i.Store(val) + if !x.i.CompareAndSwap(val, val+1) { + t.Fatalf("should have swapped %#x %#x", val, val+1) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + x.i.Store(val + 1) + if x.i.CompareAndSwap(val, val+2) { + t.Fatalf("should not have swapped %#x %#x", val, val+2) + } + if x.i.Load() != val+1 { + t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i.Load(), val+1) + } + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uintptr(magicptr), uintptr(magicptr)) + } +} + func TestCompareAndSwapPointer(t *testing.T) { var x struct { before uintptr @@ -493,6 +864,38 @@ func TestCompareAndSwapPointer(t *testing.T) { } } +func TestCompareAndSwapPointerMethod(t *testing.T) { + var x struct { + before uintptr + i Pointer[byte] + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + q := new(byte) + for _, p := range testPointers() { + p := (*byte)(p) + x.i.Store(p) + if !x.i.CompareAndSwap(p, q) { + t.Fatalf("should have swapped %p %p", p, q) + } + if x.i.Load() != q { + t.Fatalf("wrong x.i after swap: x.i=%p want %p", x.i.Load(), q) + } + if x.i.CompareAndSwap(p, nil) { + t.Fatalf("should not have swapped %p nil", p) + } + if x.i.Load() != q { + t.Fatalf("wrong x.i after swap: x.i=%p want %p", x.i.Load(), q) + } + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestLoadInt32(t *testing.T) { var x struct { before int32 @@ -513,6 +916,28 @@ func TestLoadInt32(t *testing.T) { } } +func TestLoadInt32Method(t *testing.T) { + var x struct { + before int32 + i Int32 + after int32 + } + x.before = magic32 + x.after = magic32 + want := int32(0) + for delta := int32(1); delta+delta > delta; delta += delta { + k := x.i.Load() + if k != want { + t.Fatalf("delta=%d i=%d k=%d want=%d", delta, x.i.Load(), k, want) + } + x.i.Store(k + delta) + want = k + delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + func TestLoadUint32(t *testing.T) { var x struct { before uint32 @@ -533,15 +958,35 @@ func TestLoadUint32(t *testing.T) { } } -func TestLoadInt64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestLoadUint32Method(t *testing.T) { + var x struct { + before uint32 + i Uint32 + after uint32 } + x.before = magic32 + x.after = magic32 + want := uint32(0) + for delta := uint32(1); delta+delta > delta; delta += delta { + k := x.i.Load() + if k != want { + t.Fatalf("delta=%d i=%d k=%d want=%d", delta, x.i.Load(), k, want) + } + x.i.Store(k + delta) + want = k + delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestLoadInt64(t *testing.T) { var x struct { before int64 i int64 after int64 } + magic64 := int64(magic64) x.before = magic64 x.after = magic64 for delta := int64(1); delta+delta > delta; delta += delta { @@ -552,19 +997,40 @@ func TestLoadInt64(t *testing.T) { x.i += delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } -func TestLoadUint64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestLoadInt64Method(t *testing.T) { + var x struct { + before int64 + i Int64 + after int64 } + magic64 := int64(magic64) + x.before = magic64 + x.after = magic64 + want := int64(0) + for delta := int64(1); delta+delta > delta; delta += delta { + k := x.i.Load() + if k != want { + t.Fatalf("delta=%d i=%d k=%d want=%d", delta, x.i.Load(), k, want) + } + x.i.Store(k + delta) + want = k + delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestLoadUint64(t *testing.T) { var x struct { before uint64 i uint64 after uint64 } + magic64 := uint64(magic64) x.before = magic64 x.after = magic64 for delta := uint64(1); delta+delta > delta; delta += delta { @@ -575,7 +1041,30 @@ func TestLoadUint64(t *testing.T) { x.i += delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestLoadUint64Method(t *testing.T) { + var x struct { + before uint64 + i Uint64 + after uint64 + } + magic64 := uint64(magic64) + x.before = magic64 + x.after = magic64 + want := uint64(0) + for delta := uint64(1); delta+delta > delta; delta += delta { + k := x.i.Load() + if k != want { + t.Fatalf("delta=%d i=%d k=%d want=%d", delta, x.i.Load(), k, want) + } + x.i.Store(k + delta) + want = k + delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } @@ -601,6 +1090,30 @@ func TestLoadUintptr(t *testing.T) { } } +func TestLoadUintptrMethod(t *testing.T) { + var x struct { + before uintptr + i Uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + want := uintptr(0) + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := x.i.Load() + if k != want { + t.Fatalf("delta=%d i=%d k=%d want=%d", delta, x.i.Load(), k, want) + } + x.i.Store(k + delta) + want = k + delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestLoadPointer(t *testing.T) { var x struct { before uintptr @@ -623,6 +1136,29 @@ func TestLoadPointer(t *testing.T) { } } +func TestLoadPointerMethod(t *testing.T) { + var x struct { + before uintptr + i Pointer[byte] + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + for _, p := range testPointers() { + p := (*byte)(p) + x.i.Store(p) + k := x.i.Load() + if k != p { + t.Fatalf("p=%x k=%x", p, k) + } + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestStoreInt32(t *testing.T) { var x struct { before int32 @@ -644,6 +1180,27 @@ func TestStoreInt32(t *testing.T) { } } +func TestStoreInt32Method(t *testing.T) { + var x struct { + before int32 + i Int32 + after int32 + } + x.before = magic32 + x.after = magic32 + v := int32(0) + for delta := int32(1); delta+delta > delta; delta += delta { + x.i.Store(v) + if x.i.Load() != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i.Load(), v) + } + v += delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + func TestStoreUint32(t *testing.T) { var x struct { before uint32 @@ -665,15 +1222,34 @@ func TestStoreUint32(t *testing.T) { } } -func TestStoreInt64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestStoreUint32Method(t *testing.T) { + var x struct { + before uint32 + i Uint32 + after uint32 + } + x.before = magic32 + x.after = magic32 + v := uint32(0) + for delta := uint32(1); delta+delta > delta; delta += delta { + x.i.Store(v) + if x.i.Load() != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i.Load(), v) + } + v += delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) } +} + +func TestStoreInt64(t *testing.T) { var x struct { before int64 i int64 after int64 } + magic64 := int64(magic64) x.before = magic64 x.after = magic64 v := int64(0) @@ -685,19 +1261,39 @@ func TestStoreInt64(t *testing.T) { v += delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } -func TestStoreUint64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func TestStoreInt64Method(t *testing.T) { + var x struct { + before int64 + i Int64 + after int64 } + magic64 := int64(magic64) + x.before = magic64 + x.after = magic64 + v := int64(0) + for delta := int64(1); delta+delta > delta; delta += delta { + x.i.Store(v) + if x.i.Load() != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i.Load(), v) + } + v += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestStoreUint64(t *testing.T) { var x struct { before uint64 i uint64 after uint64 } + magic64 := uint64(magic64) x.before = magic64 x.after = magic64 v := uint64(0) @@ -709,7 +1305,29 @@ func TestStoreUint64(t *testing.T) { v += delta } if x.before != magic64 || x.after != magic64 { - t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) + } +} + +func TestStoreUint64Method(t *testing.T) { + var x struct { + before uint64 + i Uint64 + after uint64 + } + magic64 := uint64(magic64) + x.before = magic64 + x.after = magic64 + v := uint64(0) + for delta := uint64(1); delta+delta > delta; delta += delta { + x.i.Store(v) + if x.i.Load() != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i.Load(), v) + } + v += delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic64, magic64) } } @@ -729,17 +1347,61 @@ func TestStoreUintptr(t *testing.T) { if x.i != v { t.Fatalf("delta=%d i=%d v=%d", delta, x.i, v) } - v += delta + v += delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + +func TestStoreUintptrMethod(t *testing.T) { + var x struct { + before uintptr + i Uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + v := uintptr(0) + for delta := uintptr(1); delta+delta > delta; delta += delta { + x.i.Store(v) + if x.i.Load() != v { + t.Fatalf("delta=%d i=%d v=%d", delta, x.i.Load(), v) + } + v += delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + +func TestStorePointer(t *testing.T) { + var x struct { + before uintptr + i unsafe.Pointer + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + for _, p := range testPointers() { + StorePointer(&x.i, p) + if x.i != p { + t.Fatalf("x.i=%p p=%p", x.i, p) + } } if x.before != magicptr || x.after != magicptr { t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) } } -func TestStorePointer(t *testing.T) { +func TestStorePointerMethod(t *testing.T) { var x struct { before uintptr - i unsafe.Pointer + i Pointer[byte] after uintptr } var m uint64 = magic64 @@ -747,9 +1409,10 @@ func TestStorePointer(t *testing.T) { x.before = magicptr x.after = magicptr for _, p := range testPointers() { - StorePointer(&x.i, p) - if x.i != p { - t.Fatalf("x.i=%p p=%p", x.i, p) + p := (*byte)(p) + x.i.Store(p) + if x.i.Load() != p { + t.Fatalf("x.i=%p p=%p", x.i.Load(), p) } } if x.before != magicptr || x.after != magicptr { @@ -779,6 +1442,16 @@ var hammer32 = map[string]func(*uint32, int){ "CompareAndSwapInt32": hammerCompareAndSwapInt32, "CompareAndSwapUint32": hammerCompareAndSwapUint32, "CompareAndSwapUintptr": hammerCompareAndSwapUintptr32, + + "SwapInt32Method": hammerSwapInt32Method, + "SwapUint32Method": hammerSwapUint32Method, + "SwapUintptrMethod": hammerSwapUintptr32Method, + "AddInt32Method": hammerAddInt32Method, + "AddUint32Method": hammerAddUint32Method, + "AddUintptrMethod": hammerAddUintptr32Method, + "CompareAndSwapInt32Method": hammerCompareAndSwapInt32Method, + "CompareAndSwapUint32Method": hammerCompareAndSwapUint32Method, + "CompareAndSwapUintptrMethod": hammerCompareAndSwapUintptr32Method, } func init() { @@ -788,6 +1461,9 @@ func init() { delete(hammer32, "SwapUintptr") delete(hammer32, "AddUintptr") delete(hammer32, "CompareAndSwapUintptr") + delete(hammer32, "SwapUintptrMethod") + delete(hammer32, "AddUintptrMethod") + delete(hammer32, "CompareAndSwapUintptrMethod") } } @@ -803,6 +1479,18 @@ func hammerSwapInt32(uaddr *uint32, count int) { } } +func hammerSwapInt32Method(uaddr *uint32, count int) { + addr := (*Int32)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := uint32(addr.Swap(int32(new))) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapInt32 is not atomic: %v", old)) + } + } +} + func hammerSwapUint32(addr *uint32, count int) { seed := int(uintptr(unsafe.Pointer(&count))) for i := 0; i < count; i++ { @@ -814,6 +1502,18 @@ func hammerSwapUint32(addr *uint32, count int) { } } +func hammerSwapUint32Method(uaddr *uint32, count int) { + addr := (*Uint32)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := addr.Swap(new) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapUint32 is not atomic: %v", old)) + } + } +} + func hammerSwapUintptr32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. @@ -828,6 +1528,20 @@ func hammerSwapUintptr32(uaddr *uint32, count int) { } } +func hammerSwapUintptr32Method(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16 + old := addr.Swap(new) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("Uintptr.Swap is not atomic: %#08x", old)) + } + } +} + func hammerAddInt32(uaddr *uint32, count int) { addr := (*int32)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { @@ -835,12 +1549,26 @@ func hammerAddInt32(uaddr *uint32, count int) { } } +func hammerAddInt32Method(uaddr *uint32, count int) { + addr := (*Int32)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerAddUint32(addr *uint32, count int) { for i := 0; i < count; i++ { AddUint32(addr, 1) } } +func hammerAddUint32Method(uaddr *uint32, count int) { + addr := (*Uint32)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerAddUintptr32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. @@ -850,6 +1578,15 @@ func hammerAddUintptr32(uaddr *uint32, count int) { } } +func hammerAddUintptr32Method(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerCompareAndSwapInt32(uaddr *uint32, count int) { addr := (*int32)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { @@ -862,6 +1599,18 @@ func hammerCompareAndSwapInt32(uaddr *uint32, count int) { } } +func hammerCompareAndSwapInt32Method(uaddr *uint32, count int) { + addr := (*Int32)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } + } +} + func hammerCompareAndSwapUint32(addr *uint32, count int) { for i := 0; i < count; i++ { for { @@ -873,6 +1622,18 @@ func hammerCompareAndSwapUint32(addr *uint32, count int) { } } +func hammerCompareAndSwapUint32Method(uaddr *uint32, count int) { + addr := (*Uint32)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } + } +} + func hammerCompareAndSwapUintptr32(uaddr *uint32, count int) { // only safe when uintptr is 32-bit. // not called on 64-bit systems. @@ -887,6 +1648,20 @@ func hammerCompareAndSwapUintptr32(uaddr *uint32, count int) { } } +func hammerCompareAndSwapUintptr32Method(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } + } +} + func TestHammer32(t *testing.T) { const p = 4 n := 100000 @@ -928,6 +1703,16 @@ var hammer64 = map[string]func(*uint64, int){ "CompareAndSwapInt64": hammerCompareAndSwapInt64, "CompareAndSwapUint64": hammerCompareAndSwapUint64, "CompareAndSwapUintptr": hammerCompareAndSwapUintptr64, + + "SwapInt64Method": hammerSwapInt64Method, + "SwapUint64Method": hammerSwapUint64Method, + "SwapUintptrMethod": hammerSwapUintptr64Method, + "AddInt64Method": hammerAddInt64Method, + "AddUint64Method": hammerAddUint64Method, + "AddUintptrMethod": hammerAddUintptr64Method, + "CompareAndSwapInt64Method": hammerCompareAndSwapInt64Method, + "CompareAndSwapUint64Method": hammerCompareAndSwapUint64Method, + "CompareAndSwapUintptrMethod": hammerCompareAndSwapUintptr64Method, } func init() { @@ -935,8 +1720,11 @@ func init() { if uintptr(v) == 0 { // 32-bit system; clear uintptr tests delete(hammer64, "SwapUintptr") + delete(hammer64, "SwapUintptrMethod") delete(hammer64, "AddUintptr") + delete(hammer64, "AddUintptrMethod") delete(hammer64, "CompareAndSwapUintptr") + delete(hammer64, "CompareAndSwapUintptrMethod") } } @@ -952,6 +1740,18 @@ func hammerSwapInt64(uaddr *uint64, count int) { } } +func hammerSwapInt64Method(uaddr *uint64, count int) { + addr := (*Int64)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := uint64(addr.Swap(int64(new))) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapInt64 is not atomic: %v", old)) + } + } +} + func hammerSwapUint64(addr *uint64, count int) { seed := int(uintptr(unsafe.Pointer(&count))) for i := 0; i < count; i++ { @@ -963,6 +1763,18 @@ func hammerSwapUint64(addr *uint64, count int) { } } +func hammerSwapUint64Method(uaddr *uint64, count int) { + addr := (*Uint64)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := addr.Swap(new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUint64 is not atomic: %v", old)) + } + } +} + const arch32 = unsafe.Sizeof(uintptr(0)) == 4 func hammerSwapUintptr64(uaddr *uint64, count int) { @@ -981,6 +1793,22 @@ func hammerSwapUintptr64(uaddr *uint64, count int) { } } +func hammerSwapUintptr64Method(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + if !arch32 { + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32 + old := addr.Swap(new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old)) + } + } + } +} + func hammerAddInt64(uaddr *uint64, count int) { addr := (*int64)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { @@ -988,12 +1816,26 @@ func hammerAddInt64(uaddr *uint64, count int) { } } +func hammerAddInt64Method(uaddr *uint64, count int) { + addr := (*Int64)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerAddUint64(addr *uint64, count int) { for i := 0; i < count; i++ { AddUint64(addr, 1) } } +func hammerAddUint64Method(uaddr *uint64, count int) { + addr := (*Uint64)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerAddUintptr64(uaddr *uint64, count int) { // only safe when uintptr is 64-bit. // not called on 32-bit systems. @@ -1003,6 +1845,15 @@ func hammerAddUintptr64(uaddr *uint64, count int) { } } +func hammerAddUintptr64Method(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + addr.Add(1) + } +} + func hammerCompareAndSwapInt64(uaddr *uint64, count int) { addr := (*int64)(unsafe.Pointer(uaddr)) for i := 0; i < count; i++ { @@ -1015,6 +1866,18 @@ func hammerCompareAndSwapInt64(uaddr *uint64, count int) { } } +func hammerCompareAndSwapInt64Method(uaddr *uint64, count int) { + addr := (*Int64)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } + } +} + func hammerCompareAndSwapUint64(addr *uint64, count int) { for i := 0; i < count; i++ { for { @@ -1026,6 +1889,18 @@ func hammerCompareAndSwapUint64(addr *uint64, count int) { } } +func hammerCompareAndSwapUint64Method(uaddr *uint64, count int) { + addr := (*Uint64)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } + } +} + func hammerCompareAndSwapUintptr64(uaddr *uint64, count int) { // only safe when uintptr is 64-bit. // not called on 32-bit systems. @@ -1040,10 +1915,21 @@ func hammerCompareAndSwapUintptr64(uaddr *uint64, count int) { } } -func TestHammer64(t *testing.T) { - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) +func hammerCompareAndSwapUintptr64Method(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*Uintptr)(unsafe.Pointer(uaddr)) + for i := 0; i < count; i++ { + for { + v := addr.Load() + if addr.CompareAndSwap(v, v+1) { + break + } + } } +} + +func TestHammer64(t *testing.T) { const p = 4 n := 100000 if testing.Short() { @@ -1089,6 +1975,21 @@ func hammerStoreLoadInt32(t *testing.T, paddr unsafe.Pointer) { StoreInt32(addr, new) } +func hammerStoreLoadInt32Method(t *testing.T, paddr unsafe.Pointer) { + addr := (*int32)(paddr) + v := LoadInt32(addr) + vlo := v & ((1 << 16) - 1) + vhi := v >> 16 + if vlo != vhi { + t.Fatalf("Int32: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<16 + if vlo == 1e4 { + new = 0 + } + StoreInt32(addr, new) +} + func hammerStoreLoadUint32(t *testing.T, paddr unsafe.Pointer) { addr := (*uint32)(paddr) v := LoadUint32(addr) @@ -1104,6 +2005,21 @@ func hammerStoreLoadUint32(t *testing.T, paddr unsafe.Pointer) { StoreUint32(addr, new) } +func hammerStoreLoadUint32Method(t *testing.T, paddr unsafe.Pointer) { + addr := (*Uint32)(paddr) + v := addr.Load() + vlo := v & ((1 << 16) - 1) + vhi := v >> 16 + if vlo != vhi { + t.Fatalf("Uint32: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<16 + if vlo == 1e4 { + new = 0 + } + addr.Store(new) +} + func hammerStoreLoadInt64(t *testing.T, paddr unsafe.Pointer) { addr := (*int64)(paddr) v := LoadInt64(addr) @@ -1116,6 +2032,18 @@ func hammerStoreLoadInt64(t *testing.T, paddr unsafe.Pointer) { StoreInt64(addr, new) } +func hammerStoreLoadInt64Method(t *testing.T, paddr unsafe.Pointer) { + addr := (*Int64)(paddr) + v := addr.Load() + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Int64: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<32 + addr.Store(new) +} + func hammerStoreLoadUint64(t *testing.T, paddr unsafe.Pointer) { addr := (*uint64)(paddr) v := LoadUint64(addr) @@ -1128,6 +2056,18 @@ func hammerStoreLoadUint64(t *testing.T, paddr unsafe.Pointer) { StoreUint64(addr, new) } +func hammerStoreLoadUint64Method(t *testing.T, paddr unsafe.Pointer) { + addr := (*Uint64)(paddr) + v := addr.Load() + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Uint64: %#x != %#x", vlo, vhi) + } + new := v + 1 + 1<<32 + addr.Store(new) +} + func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) { addr := (*uintptr)(paddr) v := LoadUintptr(addr) @@ -1155,8 +2095,36 @@ func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) { } //go:nocheckptr +func hammerStoreLoadUintptrMethod(t *testing.T, paddr unsafe.Pointer) { + addr := (*Uintptr)(paddr) + v := addr.Load() + new := v + if arch32 { + vlo := v & ((1 << 16) - 1) + vhi := v >> 16 + if vlo != vhi { + t.Fatalf("Uintptr: %#x != %#x", vlo, vhi) + } + new = v + 1 + 1<<16 + if vlo == 1e4 { + new = 0 + } + } else { + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Uintptr: %#x != %#x", vlo, vhi) + } + inc := uint64(1 + 1<<32) + new = v + uintptr(inc) + } + addr.Store(new) +} + // This code is just testing that LoadPointer/StorePointer operate // atomically; it's not actually calculating pointers. +// +//go:nocheckptr func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) { addr := (*unsafe.Pointer)(paddr) v := uintptr(LoadPointer(addr)) @@ -1183,12 +2151,44 @@ func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) { StorePointer(addr, unsafe.Pointer(new)) } +// This code is just testing that LoadPointer/StorePointer operate +// atomically; it's not actually calculating pointers. +// +//go:nocheckptr +func hammerStoreLoadPointerMethod(t *testing.T, paddr unsafe.Pointer) { + addr := (*Pointer[byte])(paddr) + v := uintptr(unsafe.Pointer(addr.Load())) + new := v + if arch32 { + vlo := v & ((1 << 16) - 1) + vhi := v >> 16 + if vlo != vhi { + t.Fatalf("Pointer: %#x != %#x", vlo, vhi) + } + new = v + 1 + 1<<16 + if vlo == 1e4 { + new = 0 + } + } else { + vlo := v & ((1 << 32) - 1) + vhi := v >> 32 + if vlo != vhi { + t.Fatalf("Pointer: %#x != %#x", vlo, vhi) + } + inc := uint64(1 + 1<<32) + new = v + uintptr(inc) + } + addr.Store((*byte)(unsafe.Pointer(new))) +} + func TestHammerStoreLoad(t *testing.T) { - var tests []func(*testing.T, unsafe.Pointer) - tests = append(tests, hammerStoreLoadInt32, hammerStoreLoadUint32, - hammerStoreLoadUintptr, hammerStoreLoadPointer) - if test64err == nil { - tests = append(tests, hammerStoreLoadInt64, hammerStoreLoadUint64) + tests := []func(*testing.T, unsafe.Pointer){ + hammerStoreLoadInt32, hammerStoreLoadUint32, + hammerStoreLoadUintptr, hammerStoreLoadPointer, + hammerStoreLoadInt32Method, hammerStoreLoadUint32Method, + hammerStoreLoadUintptrMethod, hammerStoreLoadPointerMethod, + hammerStoreLoadInt64, hammerStoreLoadUint64, + hammerStoreLoadInt64Method, hammerStoreLoadUint64Method, } n := int(1e6) if testing.Short() { @@ -1196,6 +2196,11 @@ func TestHammerStoreLoad(t *testing.T) { } const procs = 8 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(procs)) + // Disable the GC because hammerStoreLoadPointer invokes + // write barriers on values that aren't real pointers. + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // Ensure any in-progress GC is finished. + runtime.GC() for _, tt := range tests { c := make(chan int) var val uint64 @@ -1259,9 +2264,6 @@ func TestStoreLoadSeqCst64(t *testing.T) { if runtime.NumCPU() == 1 { t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) - } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int64(1e3) if testing.Short() { @@ -1350,9 +2352,6 @@ func TestStoreLoadRelAcq64(t *testing.T) { if runtime.NumCPU() == 1 { t.Skipf("Skipping test on %v processor machine", runtime.NumCPU()) } - if test64err != nil { - t.Skipf("Skipping 64-bit tests: %v", test64err) - } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) N := int64(1e3) if testing.Short() { @@ -1423,42 +2422,99 @@ func TestUnaligned64(t *testing.T) { p := (*uint64)(unsafe.Pointer(&x[1])) // misaligned shouldPanic(t, "LoadUint64", func() { LoadUint64(p) }) + shouldPanic(t, "LoadUint64Method", func() { (*Uint64)(unsafe.Pointer(p)).Load() }) shouldPanic(t, "StoreUint64", func() { StoreUint64(p, 1) }) + shouldPanic(t, "StoreUint64Method", func() { (*Uint64)(unsafe.Pointer(p)).Store(1) }) shouldPanic(t, "CompareAndSwapUint64", func() { CompareAndSwapUint64(p, 1, 2) }) + shouldPanic(t, "CompareAndSwapUint64Method", func() { (*Uint64)(unsafe.Pointer(p)).CompareAndSwap(1, 2) }) shouldPanic(t, "AddUint64", func() { AddUint64(p, 3) }) + shouldPanic(t, "AddUint64Method", func() { (*Uint64)(unsafe.Pointer(p)).Add(3) }) +} + +func TestAutoAligned64(t *testing.T) { + var signed struct { + _ uint32 + i Int64 + } + if o := reflect.TypeOf(&signed).Elem().Field(1).Offset; o != 8 { + t.Fatalf("Int64 offset = %d, want 8", o) + } + if p := reflect.ValueOf(&signed).Elem().Field(1).Addr().Pointer(); p&7 != 0 { + t.Fatalf("Int64 pointer = %#x, want 8-aligned", p) + } + + var unsigned struct { + _ uint32 + i Uint64 + } + if o := reflect.TypeOf(&unsigned).Elem().Field(1).Offset; o != 8 { + t.Fatalf("Uint64 offset = %d, want 8", o) + } + if p := reflect.ValueOf(&unsigned).Elem().Field(1).Addr().Pointer(); p&7 != 0 { + t.Fatalf("Int64 pointer = %#x, want 8-aligned", p) + } } func TestNilDeref(t *testing.T) { funcs := [...]func(){ func() { CompareAndSwapInt32(nil, 0, 0) }, + func() { (*Int32)(nil).CompareAndSwap(0, 0) }, func() { CompareAndSwapInt64(nil, 0, 0) }, + func() { (*Int64)(nil).CompareAndSwap(0, 0) }, func() { CompareAndSwapUint32(nil, 0, 0) }, + func() { (*Uint32)(nil).CompareAndSwap(0, 0) }, func() { CompareAndSwapUint64(nil, 0, 0) }, + func() { (*Uint64)(nil).CompareAndSwap(0, 0) }, func() { CompareAndSwapUintptr(nil, 0, 0) }, + func() { (*Uintptr)(nil).CompareAndSwap(0, 0) }, func() { CompareAndSwapPointer(nil, nil, nil) }, + func() { (*Pointer[byte])(nil).CompareAndSwap(nil, nil) }, func() { SwapInt32(nil, 0) }, + func() { (*Int32)(nil).Swap(0) }, func() { SwapUint32(nil, 0) }, + func() { (*Uint32)(nil).Swap(0) }, func() { SwapInt64(nil, 0) }, + func() { (*Int64)(nil).Swap(0) }, func() { SwapUint64(nil, 0) }, + func() { (*Uint64)(nil).Swap(0) }, func() { SwapUintptr(nil, 0) }, + func() { (*Uintptr)(nil).Swap(0) }, func() { SwapPointer(nil, nil) }, + func() { (*Pointer[byte])(nil).Swap(nil) }, func() { AddInt32(nil, 0) }, + func() { (*Int32)(nil).Add(0) }, func() { AddUint32(nil, 0) }, + func() { (*Uint32)(nil).Add(0) }, func() { AddInt64(nil, 0) }, + func() { (*Int64)(nil).Add(0) }, func() { AddUint64(nil, 0) }, + func() { (*Uint64)(nil).Add(0) }, func() { AddUintptr(nil, 0) }, + func() { (*Uintptr)(nil).Add(0) }, func() { LoadInt32(nil) }, + func() { (*Int32)(nil).Load() }, func() { LoadInt64(nil) }, + func() { (*Int64)(nil).Load() }, func() { LoadUint32(nil) }, + func() { (*Uint32)(nil).Load() }, func() { LoadUint64(nil) }, + func() { (*Uint64)(nil).Load() }, func() { LoadUintptr(nil) }, + func() { (*Uintptr)(nil).Load() }, func() { LoadPointer(nil) }, + func() { (*Pointer[byte])(nil).Load() }, func() { StoreInt32(nil, 0) }, + func() { (*Int32)(nil).Store(0) }, func() { StoreInt64(nil, 0) }, + func() { (*Int64)(nil).Store(0) }, func() { StoreUint32(nil, 0) }, + func() { (*Uint32)(nil).Store(0) }, func() { StoreUint64(nil, 0) }, + func() { (*Uint64)(nil).Store(0) }, func() { StoreUintptr(nil, 0) }, + func() { (*Uintptr)(nil).Store(0) }, func() { StorePointer(nil, nil) }, + func() { (*Pointer[byte])(nil).Store(nil) }, } for _, f := range funcs { func() { diff --git a/src/sync/atomic/doc.go b/src/sync/atomic/doc.go index 805ef956d55c50..7977d131684498 100644 --- a/src/sync/atomic/doc.go +++ b/src/sync/atomic/doc.go @@ -37,6 +37,13 @@ // functions, are the atomic equivalents of "return *addr" and // "*addr = val". // +// In the terminology of the Go memory model, if the effect of +// an atomic operation A is observed by atomic operation B, +// then A “synchronizes before” B. +// Additionally, all the atomic operations executed in a program +// behave as though executed in some sequentially consistent order. +// This definition provides the same semantics as +// C++'s sequentially consistent atomics and Java's volatile variables. package atomic import ( @@ -47,10 +54,12 @@ import ( // // On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core. // -// On ARM, 386, and 32-bit MIPS, it is the caller's responsibility -// to arrange for 64-bit alignment of 64-bit words accessed atomically. -// The first word in a variable or in an allocated struct, array, or slice can -// be relied upon to be 64-bit aligned. +// On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange +// for 64-bit alignment of 64-bit words accessed atomically via the primitive +// atomic functions (types Int64 and Uint64 are automatically aligned). +// The first word in an allocated struct, array, or slice; in a global +// variable; or in a local variable (because the subject of all atomic operations +// will escape to the heap) can be relied upon to be 64-bit aligned. // SwapInt32 atomically stores new into *addr and returns the previous *addr value. func SwapInt32(addr *int32, new int32) (old int32) diff --git a/src/sync/atomic/race.s b/src/sync/atomic/race.s index 0866487cc71a90..90bd69f32153fd 100644 --- a/src/sync/atomic/race.s +++ b/src/sync/atomic/race.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race // This file is here only to allow external functions. // The operations are implemented in src/runtime/race_amd64.s diff --git a/src/sync/atomic/type.go b/src/sync/atomic/type.go new file mode 100644 index 00000000000000..87c98b1e77e907 --- /dev/null +++ b/src/sync/atomic/type.go @@ -0,0 +1,195 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package atomic + +import "unsafe" + +// A Bool is an atomic boolean value. +// The zero value is false. +type Bool struct { + _ noCopy + v uint32 +} + +// Load atomically loads and returns the value stored in x. +func (x *Bool) Load() bool { return LoadUint32(&x.v) != 0 } + +// Store atomically stores val into x. +func (x *Bool) Store(val bool) { StoreUint32(&x.v, b32(val)) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Bool) Swap(new bool) (old bool) { return SwapUint32(&x.v, b32(new)) != 0 } + +// CompareAndSwap executes the compare-and-swap operation for the boolean value x. +func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) { + return CompareAndSwapUint32(&x.v, b32(old), b32(new)) +} + +// b32 returns a uint32 0 or 1 representing b. +func b32(b bool) uint32 { + if b { + return 1 + } + return 0 +} + +// For testing *Pointer[T]'s methods can be inlined. +// Keep in sync with cmd/compile/internal/test/inl_test.go:TestIntendedInlining. +var _ = &Pointer[int]{} + +// A Pointer is an atomic pointer of type *T. The zero value is a nil *T. +type Pointer[T any] struct { + _ noCopy + v unsafe.Pointer +} + +// Load atomically loads and returns the value stored in x. +func (x *Pointer[T]) Load() *T { return (*T)(LoadPointer(&x.v)) } + +// Store atomically stores val into x. +func (x *Pointer[T]) Store(val *T) { StorePointer(&x.v, unsafe.Pointer(val)) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Pointer[T]) Swap(new *T) (old *T) { return (*T)(SwapPointer(&x.v, unsafe.Pointer(new))) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool) { + return CompareAndSwapPointer(&x.v, unsafe.Pointer(old), unsafe.Pointer(new)) +} + +// An Int32 is an atomic int32. The zero value is zero. +type Int32 struct { + _ noCopy + v int32 +} + +// Load atomically loads and returns the value stored in x. +func (x *Int32) Load() int32 { return LoadInt32(&x.v) } + +// Store atomically stores val into x. +func (x *Int32) Store(val int32) { StoreInt32(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Int32) Swap(new int32) (old int32) { return SwapInt32(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) { + return CompareAndSwapInt32(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Int32) Add(delta int32) (new int32) { return AddInt32(&x.v, delta) } + +// An Int64 is an atomic int64. The zero value is zero. +type Int64 struct { + _ noCopy + _ align64 + v int64 +} + +// Load atomically loads and returns the value stored in x. +func (x *Int64) Load() int64 { return LoadInt64(&x.v) } + +// Store atomically stores val into x. +func (x *Int64) Store(val int64) { StoreInt64(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Int64) Swap(new int64) (old int64) { return SwapInt64(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Int64) CompareAndSwap(old, new int64) (swapped bool) { + return CompareAndSwapInt64(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Int64) Add(delta int64) (new int64) { return AddInt64(&x.v, delta) } + +// An Uint32 is an atomic uint32. The zero value is zero. +type Uint32 struct { + _ noCopy + v uint32 +} + +// Load atomically loads and returns the value stored in x. +func (x *Uint32) Load() uint32 { return LoadUint32(&x.v) } + +// Store atomically stores val into x. +func (x *Uint32) Store(val uint32) { StoreUint32(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Uint32) Swap(new uint32) (old uint32) { return SwapUint32(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { + return CompareAndSwapUint32(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uint32) Add(delta uint32) (new uint32) { return AddUint32(&x.v, delta) } + +// An Uint64 is an atomic uint64. The zero value is zero. +type Uint64 struct { + _ noCopy + _ align64 + v uint64 +} + +// Load atomically loads and returns the value stored in x. +func (x *Uint64) Load() uint64 { return LoadUint64(&x.v) } + +// Store atomically stores val into x. +func (x *Uint64) Store(val uint64) { StoreUint64(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Uint64) Swap(new uint64) (old uint64) { return SwapUint64(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { + return CompareAndSwapUint64(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uint64) Add(delta uint64) (new uint64) { return AddUint64(&x.v, delta) } + +// An Uintptr is an atomic uintptr. The zero value is zero. +type Uintptr struct { + _ noCopy + v uintptr +} + +// Load atomically loads and returns the value stored in x. +func (x *Uintptr) Load() uintptr { return LoadUintptr(&x.v) } + +// Store atomically stores val into x. +func (x *Uintptr) Store(val uintptr) { StoreUintptr(&x.v, val) } + +// Swap atomically stores new into x and returns the previous value. +func (x *Uintptr) Swap(new uintptr) (old uintptr) { return SwapUintptr(&x.v, new) } + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { + return CompareAndSwapUintptr(&x.v, old, new) +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uintptr) Add(delta uintptr) (new uintptr) { return AddUintptr(&x.v, delta) } + +// noCopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} + +// align64 may be added to structs that must be 64-bit aligned. +// This struct is recognized by a special case in the compiler +// and will not work if copied to any other package. +type align64 struct{} diff --git a/src/sync/atomic/value.go b/src/sync/atomic/value.go index 61f81d8fd377b0..88315f2d88a55b 100644 --- a/src/sync/atomic/value.go +++ b/src/sync/atomic/value.go @@ -14,7 +14,7 @@ import ( // // A Value must not be copied after first use. type Value struct { - v interface{} + v any } // ifaceWords is interface{} internal representation. @@ -25,10 +25,10 @@ type ifaceWords struct { // Load returns the value set by the most recent Store. // It returns nil if there has been no call to Store for this Value. -func (v *Value) Load() (val interface{}) { +func (v *Value) Load() (val any) { vp := (*ifaceWords)(unsafe.Pointer(v)) typ := LoadPointer(&vp.typ) - if typ == nil || uintptr(typ) == ^uintptr(0) { + if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) { // First store not yet completed. return nil } @@ -39,10 +39,12 @@ func (v *Value) Load() (val interface{}) { return } +var firstStoreInProgress byte + // Store sets the value of the Value to x. // All calls to Store for a given Value must use values of the same concrete type. // Store of an inconsistent type panics, as does Store(nil). -func (v *Value) Store(val interface{}) { +func (v *Value) Store(val any) { if val == nil { panic("sync/atomic: store of nil value into Value") } @@ -53,10 +55,9 @@ func (v *Value) Store(val interface{}) { if typ == nil { // Attempt to start first store. // Disable preemption so that other goroutines can use - // active spin wait to wait for completion; and so that - // GC does not see the fake type accidentally. + // active spin wait to wait for completion. runtime_procPin() - if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { + if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { runtime_procUnpin() continue } @@ -66,7 +67,7 @@ func (v *Value) Store(val interface{}) { runtime_procUnpin() return } - if uintptr(typ) == ^uintptr(0) { + if typ == unsafe.Pointer(&firstStoreInProgress) { // First store in progress. Wait. // Since we disable preemption around the first store, // we can wait with active spinning. @@ -86,7 +87,7 @@ func (v *Value) Store(val interface{}) { // // All calls to Swap for a given Value must use values of the same concrete // type. Swap of an inconsistent type panics, as does Swap(nil). -func (v *Value) Swap(new interface{}) (old interface{}) { +func (v *Value) Swap(new any) (old any) { if new == nil { panic("sync/atomic: swap of nil value into Value") } @@ -100,7 +101,7 @@ func (v *Value) Swap(new interface{}) (old interface{}) { // active spin wait to wait for completion; and so that // GC does not see the fake type accidentally. runtime_procPin() - if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { + if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { runtime_procUnpin() continue } @@ -110,7 +111,7 @@ func (v *Value) Swap(new interface{}) (old interface{}) { runtime_procUnpin() return nil } - if uintptr(typ) == ^uintptr(0) { + if typ == unsafe.Pointer(&firstStoreInProgress) { // First store in progress. Wait. // Since we disable preemption around the first store, // we can wait with active spinning. @@ -126,12 +127,12 @@ func (v *Value) Swap(new interface{}) (old interface{}) { } } -// CompareAndSwapPointer executes the compare-and-swap operation for the Value. +// CompareAndSwap executes the compare-and-swap operation for the Value. // // All calls to CompareAndSwap for a given Value must use values of the same // concrete type. CompareAndSwap of an inconsistent type panics, as does // CompareAndSwap(old, nil). -func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { +func (v *Value) CompareAndSwap(old, new any) (swapped bool) { if new == nil { panic("sync/atomic: compare and swap of nil value into Value") } @@ -152,7 +153,7 @@ func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { // active spin wait to wait for completion; and so that // GC does not see the fake type accidentally. runtime_procPin() - if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { + if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) { runtime_procUnpin() continue } @@ -162,7 +163,7 @@ func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { runtime_procUnpin() return true } - if uintptr(typ) == ^uintptr(0) { + if typ == unsafe.Pointer(&firstStoreInProgress) { // First store in progress. Wait. // Since we disable preemption around the first store, // we can wait with active spinning. @@ -178,7 +179,7 @@ func (v *Value) CompareAndSwap(old, new interface{}) (swapped bool) { // CompareAndSwapPointer below only ensures vp.data // has not changed since LoadPointer. data := LoadPointer(&vp.data) - var i interface{} + var i any (*ifaceWords)(unsafe.Pointer(&i)).typ = typ (*ifaceWords)(unsafe.Pointer(&i)).data = data if i != old { diff --git a/src/sync/atomic/value_test.go b/src/sync/atomic/value_test.go index a5e717d6e0ccd1..721da965e35800 100644 --- a/src/sync/atomic/value_test.go +++ b/src/sync/atomic/value_test.go @@ -80,7 +80,7 @@ func TestValuePanic(t *testing.T) { } func TestValueConcurrent(t *testing.T) { - tests := [][]interface{}{ + tests := [][]any{ {uint16(0), ^uint16(0), uint16(1 + 2<<8), uint16(3 + 4<<8)}, {uint32(0), ^uint32(0), uint32(1 + 2<<16), uint32(3 + 4<<16)}, {uint64(0), ^uint64(0), uint64(1 + 2<<32), uint64(3 + 4<<32)}, @@ -138,10 +138,10 @@ func BenchmarkValueRead(b *testing.B) { } var Value_SwapTests = []struct { - init interface{} - new interface{} - want interface{} - err interface{} + init any + new any + want any + err any }{ {init: nil, new: nil, err: "sync/atomic: swap of nil value into Value"}, {init: nil, new: true, want: nil, err: nil}, @@ -207,11 +207,11 @@ func TestValueSwapConcurrent(t *testing.T) { var heapA, heapB = struct{ uint }{0}, struct{ uint }{0} var Value_CompareAndSwapTests = []struct { - init interface{} - new interface{} - old interface{} + init any + new any + old any want bool - err interface{} + err any }{ {init: nil, new: nil, old: nil, err: "sync/atomic: compare and swap of nil value into Value"}, {init: nil, new: true, old: "", err: "sync/atomic: compare and swap of inconsistently typed values into Value"}, diff --git a/src/sync/cond.go b/src/sync/cond.go index b254c9360ac8b3..cbf5ba60714fd3 100644 --- a/src/sync/cond.go +++ b/src/sync/cond.go @@ -18,6 +18,21 @@ import ( // when calling the Wait method. // // A Cond must not be copied after first use. +// +// In the terminology of the Go memory model, Cond arranges that +// a call to Broadcast or Signal “synchronizes before” any Wait call +// that it unblocks. +// +// For many simple use cases, users will be better off using channels than a +// Cond (Broadcast corresponds to closing a channel, and Signal corresponds to +// sending on a channel). +// +// For more on replacements for sync.Cond, see [Roberto Clapis's series on +// advanced concurrency patterns], as well as [Bryan Mills's talk on concurrency +// patterns]. +// +// [Roberto Clapis's series on advanced concurrency patterns]: https://blogtitle.github.io/categories/concurrency/ +// [Bryan Mills's talk on concurrency patterns]: https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view type Cond struct { noCopy noCopy @@ -42,13 +57,12 @@ func NewCond(l Locker) *Cond { // typically cannot assume that the condition is true when // Wait returns. Instead, the caller should Wait in a loop: // -// c.L.Lock() -// for !condition() { -// c.Wait() -// } -// ... make use of condition ... -// c.L.Unlock() -// +// c.L.Lock() +// for !condition() { +// c.Wait() +// } +// ... make use of condition ... +// c.L.Unlock() func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) @@ -61,6 +75,9 @@ func (c *Cond) Wait() { // // It is allowed but not required for the caller to hold c.L // during the call. +// +// Signal() does not affect goroutine scheduling priority; if other goroutines +// are attempting to lock c.L, they may be awoken before a "waiting" goroutine. func (c *Cond) Signal() { c.checker.check() runtime_notifyListNotifyOne(&c.notify) @@ -86,11 +103,13 @@ func (c *copyChecker) check() { } } -// noCopy may be embedded into structs which must not be copied +// noCopy may be added to structs which must not be copied // after the first use. // // See https://golang.org/issues/8005#issuecomment-190753527 // for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. diff --git a/src/sync/example_pool_test.go b/src/sync/example_pool_test.go index 8288d41e8c0843..2fb4c1e6b913be 100644 --- a/src/sync/example_pool_test.go +++ b/src/sync/example_pool_test.go @@ -13,7 +13,7 @@ import ( ) var bufPool = sync.Pool{ - New: func() interface{} { + New: func() any { // The Pool's New function should generally only return pointer // types, since a pointer can be put into the return interface // value without an allocation: diff --git a/src/sync/example_test.go b/src/sync/example_test.go index bdd3af6fedaa4b..f009a68cf21f25 100644 --- a/src/sync/example_test.go +++ b/src/sync/example_test.go @@ -22,7 +22,7 @@ func ExampleWaitGroup() { var urls = []string{ "http://www.golang.org/", "http://www.google.com/", - "http://www.somestupidname.com/", + "http://www.example.com/", } for _, url := range urls { // Increment the WaitGroup counter. diff --git a/src/sync/export_test.go b/src/sync/export_test.go index ffbe5674640628..c020ef737da402 100644 --- a/src/sync/export_test.go +++ b/src/sync/export_test.go @@ -12,9 +12,9 @@ var Runtime_procUnpin = runtime_procUnpin // poolDequeue testing. type PoolDequeue interface { - PushHead(val interface{}) bool - PopHead() (interface{}, bool) - PopTail() (interface{}, bool) + PushHead(val any) bool + PopHead() (any, bool) + PopTail() (any, bool) } func NewPoolDequeue(n int) PoolDequeue { @@ -27,15 +27,15 @@ func NewPoolDequeue(n int) PoolDequeue { return d } -func (d *poolDequeue) PushHead(val interface{}) bool { +func (d *poolDequeue) PushHead(val any) bool { return d.pushHead(val) } -func (d *poolDequeue) PopHead() (interface{}, bool) { +func (d *poolDequeue) PopHead() (any, bool) { return d.popHead() } -func (d *poolDequeue) PopTail() (interface{}, bool) { +func (d *poolDequeue) PopTail() (any, bool) { return d.popTail() } @@ -43,15 +43,15 @@ func NewPoolChain() PoolDequeue { return new(poolChain) } -func (c *poolChain) PushHead(val interface{}) bool { +func (c *poolChain) PushHead(val any) bool { c.pushHead(val) return true } -func (c *poolChain) PopHead() (interface{}, bool) { +func (c *poolChain) PopHead() (any, bool) { return c.popHead() } -func (c *poolChain) PopTail() (interface{}, bool) { +func (c *poolChain) PopTail() (any, bool) { return c.popTail() } diff --git a/src/sync/map.go b/src/sync/map.go index dfb62dd3e801cc..bde78b88783b3b 100644 --- a/src/sync/map.go +++ b/src/sync/map.go @@ -24,6 +24,13 @@ import ( // contention compared to a Go map paired with a separate Mutex or RWMutex. // // The zero Map is empty and ready for use. A Map must not be copied after first use. +// +// In the terminology of the Go memory model, Map arranges that a write operation +// “synchronizes before” any read operation that observes the effect of the write, where +// read and write operations are defined as follows. +// Load, LoadAndDelete, LoadOrStore are read operations; +// Delete, LoadAndDelete, and Store are write operations; +// and LoadOrStore is a write operation when it returns loaded set to false. type Map struct { mu Mutex @@ -36,7 +43,7 @@ type Map struct { // Entries stored in read may be updated concurrently without mu, but updating // a previously-expunged entry requires that the entry be copied to the dirty // map and unexpunged with mu held. - read atomic.Value // readOnly + read atomic.Pointer[readOnly] // dirty contains the portion of the map's contents that require mu to be // held. To ensure that the dirty map can be promoted to the read map quickly, @@ -48,7 +55,7 @@ type Map struct { // // If the dirty map is nil, the next write to the map will initialize it by // making a shallow copy of the clean map, omitting stale entries. - dirty map[interface{}]*entry + dirty map[any]*entry // misses counts the number of loads since the read map was last updated that // needed to lock mu to determine whether the key was present. @@ -61,13 +68,13 @@ type Map struct { // readOnly is an immutable struct stored atomically in the Map.read field. type readOnly struct { - m map[interface{}]*entry + m map[any]*entry amended bool // true if the dirty map contains some key not in m. } // expunged is an arbitrary pointer that marks entries which have been deleted // from the dirty map. -var expunged = unsafe.Pointer(new(interface{})) +var expunged = unsafe.Pointer(new(any)) // An entry is a slot in the map corresponding to a particular key. type entry struct { @@ -93,22 +100,29 @@ type entry struct { p unsafe.Pointer // *interface{} } -func newEntry(i interface{}) *entry { +func newEntry(i any) *entry { return &entry{p: unsafe.Pointer(&i)} } +func (m *Map) loadReadOnly() readOnly { + if p := m.read.Load(); p != nil { + return *p + } + return readOnly{} +} + // Load returns the value stored in the map for a key, or nil if no // value is present. // The ok result indicates whether value was found in the map. -func (m *Map) Load(key interface{}) (value interface{}, ok bool) { - read, _ := m.read.Load().(readOnly) +func (m *Map) Load(key any) (value any, ok bool) { + read := m.loadReadOnly() e, ok := read.m[key] if !ok && read.amended { m.mu.Lock() // Avoid reporting a spurious miss if m.dirty got promoted while we were // blocked on m.mu. (If further loads of the same key will not miss, it's // not worth copying the dirty map for this key.) - read, _ = m.read.Load().(readOnly) + read = m.loadReadOnly() e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] @@ -125,23 +139,23 @@ func (m *Map) Load(key interface{}) (value interface{}, ok bool) { return e.load() } -func (e *entry) load() (value interface{}, ok bool) { +func (e *entry) load() (value any, ok bool) { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { return nil, false } - return *(*interface{})(p), true + return *(*any)(p), true } // Store sets the value for a key. -func (m *Map) Store(key, value interface{}) { - read, _ := m.read.Load().(readOnly) +func (m *Map) Store(key, value any) { + read := m.loadReadOnly() if e, ok := read.m[key]; ok && e.tryStore(&value) { return } m.mu.Lock() - read, _ = m.read.Load().(readOnly) + read = m.loadReadOnly() if e, ok := read.m[key]; ok { if e.unexpungeLocked() { // The entry was previously expunged, which implies that there is a @@ -156,7 +170,7 @@ func (m *Map) Store(key, value interface{}) { // We're adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. m.dirtyLocked() - m.read.Store(readOnly{m: read.m, amended: true}) + m.read.Store(&readOnly{m: read.m, amended: true}) } m.dirty[key] = newEntry(value) } @@ -167,7 +181,7 @@ func (m *Map) Store(key, value interface{}) { // // If the entry is expunged, tryStore returns false and leaves the entry // unchanged. -func (e *entry) tryStore(i *interface{}) bool { +func (e *entry) tryStore(i *any) bool { for { p := atomic.LoadPointer(&e.p) if p == expunged { @@ -190,16 +204,16 @@ func (e *entry) unexpungeLocked() (wasExpunged bool) { // storeLocked unconditionally stores a value to the entry. // // The entry must be known not to be expunged. -func (e *entry) storeLocked(i *interface{}) { +func (e *entry) storeLocked(i *any) { atomic.StorePointer(&e.p, unsafe.Pointer(i)) } // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. -func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { +func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) { // Avoid locking if it's a clean hit. - read, _ := m.read.Load().(readOnly) + read := m.loadReadOnly() if e, ok := read.m[key]; ok { actual, loaded, ok := e.tryLoadOrStore(value) if ok { @@ -208,7 +222,7 @@ func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bo } m.mu.Lock() - read, _ = m.read.Load().(readOnly) + read = m.loadReadOnly() if e, ok := read.m[key]; ok { if e.unexpungeLocked() { m.dirty[key] = e @@ -222,7 +236,7 @@ func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bo // We're adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. m.dirtyLocked() - m.read.Store(readOnly{m: read.m, amended: true}) + m.read.Store(&readOnly{m: read.m, amended: true}) } m.dirty[key] = newEntry(value) actual, loaded = value, false @@ -237,13 +251,13 @@ func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bo // // If the entry is expunged, tryLoadOrStore leaves the entry unchanged and // returns with ok==false. -func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bool) { +func (e *entry) tryLoadOrStore(i any) (actual any, loaded, ok bool) { p := atomic.LoadPointer(&e.p) if p == expunged { return nil, false, false } if p != nil { - return *(*interface{})(p), true, true + return *(*any)(p), true, true } // Copy the interface after the first load to make this method more amenable @@ -259,19 +273,19 @@ func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bo return nil, false, false } if p != nil { - return *(*interface{})(p), true, true + return *(*any)(p), true, true } } } // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. -func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { - read, _ := m.read.Load().(readOnly) +func (m *Map) LoadAndDelete(key any) (value any, loaded bool) { + read := m.loadReadOnly() e, ok := read.m[key] if !ok && read.amended { m.mu.Lock() - read, _ = m.read.Load().(readOnly) + read = m.loadReadOnly() e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] @@ -290,18 +304,18 @@ func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { } // Delete deletes the value for a key. -func (m *Map) Delete(key interface{}) { +func (m *Map) Delete(key any) { m.LoadAndDelete(key) } -func (e *entry) delete() (value interface{}, ok bool) { +func (e *entry) delete() (value any, ok bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { return nil, false } if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(*interface{})(p), true + return *(*any)(p), true } } } @@ -311,27 +325,28 @@ func (e *entry) delete() (value interface{}, ok bool) { // // Range does not necessarily correspond to any consistent snapshot of the Map's // contents: no key will be visited more than once, but if the value for any key -// is stored or deleted concurrently, Range may reflect any mapping for that key -// from any point during the Range call. +// is stored or deleted concurrently (including by f), Range may reflect any +// mapping for that key from any point during the Range call. Range does not +// block other methods on the receiver; even f itself may call any method on m. // // Range may be O(N) with the number of elements in the map even if f returns // false after a constant number of calls. -func (m *Map) Range(f func(key, value interface{}) bool) { +func (m *Map) Range(f func(key, value any) bool) { // We need to be able to iterate over all of the keys that were already // present at the start of the call to Range. // If read.amended is false, then read.m satisfies that property without // requiring us to hold m.mu for a long time. - read, _ := m.read.Load().(readOnly) + read := m.loadReadOnly() if read.amended { // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) // (assuming the caller does not break out early), so a call to Range // amortizes an entire copy of the map: we can promote the dirty copy // immediately! m.mu.Lock() - read, _ = m.read.Load().(readOnly) + read = m.loadReadOnly() if read.amended { read = readOnly{m: m.dirty} - m.read.Store(read) + m.read.Store(&read) m.dirty = nil m.misses = 0 } @@ -354,7 +369,7 @@ func (m *Map) missLocked() { if m.misses < len(m.dirty) { return } - m.read.Store(readOnly{m: m.dirty}) + m.read.Store(&readOnly{m: m.dirty}) m.dirty = nil m.misses = 0 } @@ -364,8 +379,8 @@ func (m *Map) dirtyLocked() { return } - read, _ := m.read.Load().(readOnly) - m.dirty = make(map[interface{}]*entry, len(read.m)) + read := m.loadReadOnly() + m.dirty = make(map[any]*entry, len(read.m)) for k, e := range read.m { if !e.tryExpungeLocked() { m.dirty[k] = e diff --git a/src/sync/map_bench_test.go b/src/sync/map_bench_test.go index cf0a3d7fdedc89..e7b0e6039c44d1 100644 --- a/src/sync/map_bench_test.go +++ b/src/sync/map_bench_test.go @@ -216,7 +216,7 @@ func BenchmarkRange(b *testing.B) { perG: func(b *testing.B, pb *testing.PB, i int, m mapInterface) { for ; pb.Next(); i++ { - m.Range(func(_, _ interface{}) bool { return true }) + m.Range(func(_, _ any) bool { return true }) } }, }) @@ -263,7 +263,7 @@ func BenchmarkAdversarialDelete(b *testing.B) { m.Load(i) if i%mapSize == 0 { - m.Range(func(k, _ interface{}) bool { + m.Range(func(k, _ any) bool { m.Delete(k) return false }) diff --git a/src/sync/map_reference_test.go b/src/sync/map_reference_test.go index d105a24e926e3c..1122b40b9b8359 100644 --- a/src/sync/map_reference_test.go +++ b/src/sync/map_reference_test.go @@ -13,43 +13,43 @@ import ( // mapInterface is the interface Map implements. type mapInterface interface { - Load(interface{}) (interface{}, bool) - Store(key, value interface{}) - LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) - LoadAndDelete(key interface{}) (value interface{}, loaded bool) - Delete(interface{}) - Range(func(key, value interface{}) (shouldContinue bool)) + Load(any) (any, bool) + Store(key, value any) + LoadOrStore(key, value any) (actual any, loaded bool) + LoadAndDelete(key any) (value any, loaded bool) + Delete(any) + Range(func(key, value any) (shouldContinue bool)) } // RWMutexMap is an implementation of mapInterface using a sync.RWMutex. type RWMutexMap struct { mu sync.RWMutex - dirty map[interface{}]interface{} + dirty map[any]any } -func (m *RWMutexMap) Load(key interface{}) (value interface{}, ok bool) { +func (m *RWMutexMap) Load(key any) (value any, ok bool) { m.mu.RLock() value, ok = m.dirty[key] m.mu.RUnlock() return } -func (m *RWMutexMap) Store(key, value interface{}) { +func (m *RWMutexMap) Store(key, value any) { m.mu.Lock() if m.dirty == nil { - m.dirty = make(map[interface{}]interface{}) + m.dirty = make(map[any]any) } m.dirty[key] = value m.mu.Unlock() } -func (m *RWMutexMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { +func (m *RWMutexMap) LoadOrStore(key, value any) (actual any, loaded bool) { m.mu.Lock() actual, loaded = m.dirty[key] if !loaded { actual = value if m.dirty == nil { - m.dirty = make(map[interface{}]interface{}) + m.dirty = make(map[any]any) } m.dirty[key] = value } @@ -57,7 +57,7 @@ func (m *RWMutexMap) LoadOrStore(key, value interface{}) (actual interface{}, lo return actual, loaded } -func (m *RWMutexMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { +func (m *RWMutexMap) LoadAndDelete(key any) (value any, loaded bool) { m.mu.Lock() value, loaded = m.dirty[key] if !loaded { @@ -69,15 +69,15 @@ func (m *RWMutexMap) LoadAndDelete(key interface{}) (value interface{}, loaded b return value, loaded } -func (m *RWMutexMap) Delete(key interface{}) { +func (m *RWMutexMap) Delete(key any) { m.mu.Lock() delete(m.dirty, key) m.mu.Unlock() } -func (m *RWMutexMap) Range(f func(key, value interface{}) (shouldContinue bool)) { +func (m *RWMutexMap) Range(f func(key, value any) (shouldContinue bool)) { m.mu.RLock() - keys := make([]interface{}, 0, len(m.dirty)) + keys := make([]any, 0, len(m.dirty)) for k := range m.dirty { keys = append(keys, k) } @@ -102,13 +102,13 @@ type DeepCopyMap struct { clean atomic.Value } -func (m *DeepCopyMap) Load(key interface{}) (value interface{}, ok bool) { - clean, _ := m.clean.Load().(map[interface{}]interface{}) +func (m *DeepCopyMap) Load(key any) (value any, ok bool) { + clean, _ := m.clean.Load().(map[any]any) value, ok = clean[key] return value, ok } -func (m *DeepCopyMap) Store(key, value interface{}) { +func (m *DeepCopyMap) Store(key, value any) { m.mu.Lock() dirty := m.dirty() dirty[key] = value @@ -116,8 +116,8 @@ func (m *DeepCopyMap) Store(key, value interface{}) { m.mu.Unlock() } -func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { - clean, _ := m.clean.Load().(map[interface{}]interface{}) +func (m *DeepCopyMap) LoadOrStore(key, value any) (actual any, loaded bool) { + clean, _ := m.clean.Load().(map[any]any) actual, loaded = clean[key] if loaded { return actual, loaded @@ -125,7 +125,7 @@ func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, l m.mu.Lock() // Reload clean in case it changed while we were waiting on m.mu. - clean, _ = m.clean.Load().(map[interface{}]interface{}) + clean, _ = m.clean.Load().(map[any]any) actual, loaded = clean[key] if !loaded { dirty := m.dirty() @@ -137,7 +137,7 @@ func (m *DeepCopyMap) LoadOrStore(key, value interface{}) (actual interface{}, l return actual, loaded } -func (m *DeepCopyMap) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { +func (m *DeepCopyMap) LoadAndDelete(key any) (value any, loaded bool) { m.mu.Lock() dirty := m.dirty() value, loaded = dirty[key] @@ -147,7 +147,7 @@ func (m *DeepCopyMap) LoadAndDelete(key interface{}) (value interface{}, loaded return } -func (m *DeepCopyMap) Delete(key interface{}) { +func (m *DeepCopyMap) Delete(key any) { m.mu.Lock() dirty := m.dirty() delete(dirty, key) @@ -155,8 +155,8 @@ func (m *DeepCopyMap) Delete(key interface{}) { m.mu.Unlock() } -func (m *DeepCopyMap) Range(f func(key, value interface{}) (shouldContinue bool)) { - clean, _ := m.clean.Load().(map[interface{}]interface{}) +func (m *DeepCopyMap) Range(f func(key, value any) (shouldContinue bool)) { + clean, _ := m.clean.Load().(map[any]any) for k, v := range clean { if !f(k, v) { break @@ -164,9 +164,9 @@ func (m *DeepCopyMap) Range(f func(key, value interface{}) (shouldContinue bool) } } -func (m *DeepCopyMap) dirty() map[interface{}]interface{} { - clean, _ := m.clean.Load().(map[interface{}]interface{}) - dirty := make(map[interface{}]interface{}, len(clean)+1) +func (m *DeepCopyMap) dirty() map[any]any { + clean, _ := m.clean.Load().(map[any]any) + dirty := make(map[any]any, len(clean)+1) for k, v := range clean { dirty[k] = v } diff --git a/src/sync/map_test.go b/src/sync/map_test.go index 7f163caa5c95d1..8352471104381c 100644 --- a/src/sync/map_test.go +++ b/src/sync/map_test.go @@ -29,10 +29,10 @@ var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opLoadAndDelete, opDelet // mapCall is a quick.Generator for calls on mapInterface. type mapCall struct { op mapOp - k, v interface{} + k, v any } -func (c mapCall) apply(m mapInterface) (interface{}, bool) { +func (c mapCall) apply(m mapInterface) (any, bool) { switch c.op { case opLoad: return m.Load(c.k) @@ -52,11 +52,11 @@ func (c mapCall) apply(m mapInterface) (interface{}, bool) { } type mapResult struct { - value interface{} + value any ok bool } -func randValue(r *rand.Rand) interface{} { +func randValue(r *rand.Rand) any { b := make([]byte, r.Intn(4)) for i := range b { b[i] = 'a' + byte(rand.Intn(26)) @@ -73,14 +73,14 @@ func (mapCall) Generate(r *rand.Rand, size int) reflect.Value { return reflect.ValueOf(c) } -func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[interface{}]interface{}) { +func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[any]any) { for _, c := range calls { v, ok := c.apply(m) results = append(results, mapResult{v, ok}) } - final = make(map[interface{}]interface{}) - m.Range(func(k, v interface{}) bool { + final = make(map[any]any) + m.Range(func(k, v any) bool { final[k] = v return true }) @@ -88,15 +88,15 @@ func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map return results, final } -func applyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { +func applyMap(calls []mapCall) ([]mapResult, map[any]any) { return applyCalls(new(sync.Map), calls) } -func applyRWMutexMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { +func applyRWMutexMap(calls []mapCall) ([]mapResult, map[any]any) { return applyCalls(new(RWMutexMap), calls) } -func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { +func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[any]any) { return applyCalls(new(DeepCopyMap), calls) } @@ -155,7 +155,7 @@ func TestConcurrentRange(t *testing.T) { for n := iters; n > 0; n-- { seen := make(map[int64]bool, mapSize) - m.Range(func(ki, vi interface{}) bool { + m.Range(func(ki, vi any) bool { k, v := ki.(int64), vi.(int64) if v%k != 0 { t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v) @@ -195,3 +195,53 @@ func TestIssue40999(t *testing.T) { runtime.GC() } } + +func TestMapRangeNestedCall(t *testing.T) { // Issue 46399 + var m sync.Map + for i, v := range [3]string{"hello", "world", "Go"} { + m.Store(i, v) + } + m.Range(func(key, value any) bool { + m.Range(func(key, value any) bool { + // We should be able to load the key offered in the Range callback, + // because there are no concurrent Delete involved in this tested map. + if v, ok := m.Load(key); !ok || !reflect.DeepEqual(v, value) { + t.Fatalf("Nested Range loads unexpected value, got %+v want %+v", v, value) + } + + // We didn't keep 42 and a value into the map before, if somehow we loaded + // a value from such a key, meaning there must be an internal bug regarding + // nested range in the Map. + if _, loaded := m.LoadOrStore(42, "dummy"); loaded { + t.Fatalf("Nested Range loads unexpected value, want store a new value") + } + + // Try to Store then LoadAndDelete the corresponding value with the key + // 42 to the Map. In this case, the key 42 and associated value should be + // removed from the Map. Therefore any future range won't observe key 42 + // as we checked in above. + val := "sync.Map" + m.Store(42, val) + if v, loaded := m.LoadAndDelete(42); !loaded || !reflect.DeepEqual(v, val) { + t.Fatalf("Nested Range loads unexpected value, got %v, want %v", v, val) + } + return true + }) + + // Remove key from Map on-the-fly. + m.Delete(key) + return true + }) + + // After a Range of Delete, all keys should be removed and any + // further Range won't invoke the callback. Hence length remains 0. + length := 0 + m.Range(func(key, value any) bool { + length++ + return true + }) + + if length != 0 { + t.Fatalf("Unexpected sync.Map size, got %v want %v", length, 0) + } +} diff --git a/src/sync/mutex.go b/src/sync/mutex.go index 3028552f743d1f..2ea024e5856440 100644 --- a/src/sync/mutex.go +++ b/src/sync/mutex.go @@ -16,12 +16,21 @@ import ( "unsafe" ) -func throw(string) // provided by runtime +// Provided by runtime via linkname. +func throw(string) +func fatal(string) // A Mutex is a mutual exclusion lock. // The zero value for a Mutex is an unlocked mutex. // // A Mutex must not be copied after first use. +// +// In the terminology of the Go memory model, +// the n'th call to Unlock “synchronizes before” the m'th call to Lock +// for any n < m. +// A successful call to TryLock is equivalent to a call to Lock. +// A failed call to TryLock does not establish any “synchronizes before” +// relation at all. type Mutex struct { state int32 sema uint32 @@ -81,6 +90,30 @@ func (m *Mutex) Lock() { m.lockSlow() } +// TryLock tries to lock m and reports whether it succeeded. +// +// Note that while correct uses of TryLock do exist, they are rare, +// and use of TryLock is often a sign of a deeper problem +// in a particular use of mutexes. +func (m *Mutex) TryLock() bool { + old := m.state + if old&(mutexLocked|mutexStarving) != 0 { + return false + } + + // There may be a goroutine waiting for the mutex, but we are + // running now and can try to grab the mutex before that + // goroutine wakes up. + if !atomic.CompareAndSwapInt32(&m.state, old, old|mutexLocked) { + return false + } + + if race.Enabled { + race.Acquire(unsafe.Pointer(m)) + } + return true +} + func (m *Mutex) lockSlow() { var waitStartTime int64 starving := false @@ -193,7 +226,7 @@ func (m *Mutex) Unlock() { func (m *Mutex) unlockSlow(new int32) { if (new+mutexLocked)&mutexLocked == 0 { - throw("sync: unlock of unlocked mutex") + fatal("sync: unlock of unlocked mutex") } if new&mutexStarving == 0 { old := new diff --git a/src/sync/mutex_test.go b/src/sync/mutex_test.go index 98c1bf2a5f9bc2..cca0986a30975e 100644 --- a/src/sync/mutex_test.go +++ b/src/sync/mutex_test.go @@ -60,6 +60,12 @@ func BenchmarkContendedSemaphore(b *testing.B) { func HammerMutex(m *Mutex, loops int, cdone chan bool) { for i := 0; i < loops; i++ { + if i%3 == 0 { + if m.TryLock() { + m.Unlock() + } + continue + } m.Lock() m.Unlock() } @@ -71,7 +77,19 @@ func TestMutex(t *testing.T) { t.Logf("got mutexrate %d expected 0", n) } defer runtime.SetMutexProfileFraction(0) + m := new(Mutex) + + m.Lock() + if m.TryLock() { + t.Fatalf("TryLock succeeded with mutex locked") + } + m.Unlock() + if !m.TryLock() { + t.Fatalf("TryLock failed with mutex unlocked") + } + m.Unlock() + c := make(chan bool) for i := 0; i < 10; i++ { go HammerMutex(m, 1000, c) diff --git a/src/sync/once.go b/src/sync/once.go index 8844314e7e9dc0..b6399cfc3d100a 100644 --- a/src/sync/once.go +++ b/src/sync/once.go @@ -11,6 +11,10 @@ import ( // Once is an object that will perform exactly one action. // // A Once must not be copied after first use. +// +// In the terminology of the Go memory model, +// the return from f “synchronizes before” +// the return from any call of once.Do(f). type Once struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. @@ -23,7 +27,9 @@ type Once struct { // Do calls the function f if and only if Do is being called for the // first time for this instance of Once. In other words, given -// var once Once +// +// var once Once +// // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of // Once is required for each function to execute. @@ -31,14 +37,14 @@ type Once struct { // Do is intended for initialization that must be run exactly once. Since f // is niladic, it may be necessary to use a function literal to capture the // arguments to a function to be invoked by Do: -// config.once.Do(func() { config.init(filename) }) +// +// config.once.Do(func() { config.init(filename) }) // // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. -// func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // diff --git a/src/sync/pool.go b/src/sync/pool.go index 1ae70127ac24ec..cf01e2e1891a10 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -41,6 +41,11 @@ import ( // free list. // // A Pool must not be copied after first use. +// +// In the terminology of the Go memory model, a call to Put(x) “synchronizes before” +// a call to Get returning that same value x. +// Similarly, a call to New returning x “synchronizes before” +// a call to Get returning that same value x. type Pool struct { noCopy noCopy @@ -53,13 +58,13 @@ type Pool struct { // New optionally specifies a function to generate // a value when Get would otherwise return nil. // It may not be changed concurrently with calls to Get. - New func() interface{} + New func() any } // Local per-P Pool appendix. type poolLocalInternal struct { - private interface{} // Can be used only by the respective P. - shared poolChain // Local P can pushHead/popHead; any P can popTail. + private any // Can be used only by the respective P. + shared poolChain // Local P can pushHead/popHead; any P can popTail. } type poolLocal struct { @@ -71,7 +76,7 @@ type poolLocal struct { } // from runtime -func fastrand() uint32 +func fastrandn(n uint32) uint32 var poolRaceHash [128]uint64 @@ -80,19 +85,19 @@ var poolRaceHash [128]uint64 // directly, for fear of conflicting with other synchronization on that address. // Instead, we hash the pointer to get an index into poolRaceHash. // See discussion on golang.org/cl/31589. -func poolRaceAddr(x interface{}) unsafe.Pointer { +func poolRaceAddr(x any) unsafe.Pointer { ptr := uintptr((*[2]unsafe.Pointer)(unsafe.Pointer(&x))[1]) h := uint32((uint64(uint32(ptr)) * 0x85ebca6b) >> 16) return unsafe.Pointer(&poolRaceHash[h%uint32(len(poolRaceHash))]) } // Put adds x to the pool. -func (p *Pool) Put(x interface{}) { +func (p *Pool) Put(x any) { if x == nil { return } if race.Enabled { - if fastrand()%4 == 0 { + if fastrandn(4) == 0 { // Randomly drop x on floor. return } @@ -102,9 +107,7 @@ func (p *Pool) Put(x interface{}) { l, _ := p.pin() if l.private == nil { l.private = x - x = nil - } - if x != nil { + } else { l.shared.pushHead(x) } runtime_procUnpin() @@ -121,7 +124,7 @@ func (p *Pool) Put(x interface{}) { // // If Get would otherwise return nil and p.New is non-nil, Get returns // the result of calling p.New. -func (p *Pool) Get() interface{} { +func (p *Pool) Get() any { if race.Enabled { race.Disable() } @@ -150,7 +153,7 @@ func (p *Pool) Get() interface{} { return x } -func (p *Pool) getSlow(pid int) interface{} { +func (p *Pool) getSlow(pid int) any { // See the comment in pin regarding ordering of the loads. size := runtime_LoadAcquintptr(&p.localSize) // load-acquire locals := p.local // load-consume diff --git a/src/sync/pool_test.go b/src/sync/pool_test.go index 65666daab48e66..5e385974414cbf 100644 --- a/src/sync/pool_test.go +++ b/src/sync/pool_test.go @@ -3,8 +3,8 @@ // license that can be found in the LICENSE file. // Pool is no-op under race detector, so all these tests do not work. +// //go:build !race -// +build !race package sync_test @@ -65,7 +65,7 @@ func TestPoolNew(t *testing.T) { i := 0 p := Pool{ - New: func() interface{} { + New: func() any { i++ return i }, @@ -144,7 +144,7 @@ func TestPoolStress(t *testing.T) { done := make(chan bool) for i := 0; i < P; i++ { go func() { - var v interface{} = 0 + var v any = 0 for j := 0; j < N; j++ { if v == nil { v = 0 @@ -271,7 +271,27 @@ func BenchmarkPoolOverflow(b *testing.B) { }) } -var globalSink interface{} +// Simulate object starvation in order to force Ps to steal objects +// from other Ps. +func BenchmarkPoolStarvation(b *testing.B) { + var p Pool + count := 100 + // Reduce number of putted objects by 33 %. It creates objects starvation + // that force P-local storage to steal objects from other Ps. + countStarved := count - int(float32(count)*0.33) + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + for b := 0; b < countStarved; b++ { + p.Put(1) + } + for b := 0; b < count; b++ { + p.Get() + } + } + }) +} + +var globalSink any func BenchmarkPoolSTW(b *testing.B) { // Take control of GC. @@ -284,7 +304,7 @@ func BenchmarkPoolSTW(b *testing.B) { for i := 0; i < b.N; i++ { // Put a large number of items into a pool. const N = 100000 - var item interface{} = 42 + var item any = 42 for i := 0; i < N; i++ { p.Put(item) } @@ -319,7 +339,7 @@ func BenchmarkPoolExpensiveNew(b *testing.B) { // Create a pool that's "expensive" to fill. var p Pool var nNew uint64 - p.New = func() interface{} { + p.New = func() any { atomic.AddUint64(&nNew, 1) time.Sleep(time.Millisecond) return 42 @@ -329,7 +349,7 @@ func BenchmarkPoolExpensiveNew(b *testing.B) { b.RunParallel(func(pb *testing.PB) { // Simulate 100X the number of goroutines having items // checked out from the Pool simultaneously. - items := make([]interface{}, 100) + items := make([]any, 100) var sink []byte for pb.Next() { // Stress the pool. diff --git a/src/sync/poolqueue.go b/src/sync/poolqueue.go index 9be83e9a433ef2..631f2c15fda267 100644 --- a/src/sync/poolqueue.go +++ b/src/sync/poolqueue.go @@ -77,7 +77,7 @@ func (d *poolDequeue) pack(head, tail uint32) uint64 { // pushHead adds val at the head of the queue. It returns false if the // queue is full. It must only be called by a single producer. -func (d *poolDequeue) pushHead(val interface{}) bool { +func (d *poolDequeue) pushHead(val any) bool { ptrs := atomic.LoadUint64(&d.headTail) head, tail := d.unpack(ptrs) if (tail+uint32(len(d.vals)))&(1<= rwmutexMaxReaders { race.Enable() - throw("sync: Unlock of unlocked RWMutex") + fatal("sync: Unlock of unlocked RWMutex") } // Unblock blocked readers, if any. for i := 0; i < int(r); i++ { diff --git a/src/sync/rwmutex_test.go b/src/sync/rwmutex_test.go index c98e69fd07d745..dfbdd9bbeef706 100644 --- a/src/sync/rwmutex_test.go +++ b/src/sync/rwmutex_test.go @@ -108,6 +108,34 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { } func TestRWMutex(t *testing.T) { + var m RWMutex + + m.Lock() + if m.TryLock() { + t.Fatalf("TryLock succeeded with mutex locked") + } + if m.TryRLock() { + t.Fatalf("TryRLock succeeded with mutex locked") + } + m.Unlock() + + if !m.TryLock() { + t.Fatalf("TryLock failed with mutex unlocked") + } + m.Unlock() + + if !m.TryRLock() { + t.Fatalf("TryRLock failed with mutex unlocked") + } + if !m.TryRLock() { + t.Fatalf("TryRLock failed with mutex rlocked") + } + if m.TryLock() { + t.Fatalf("TryLock succeeded with mutex rlocked") + } + m.RUnlock() + m.RUnlock() + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) n := 1000 if testing.Short() { diff --git a/src/sync/waitgroup.go b/src/sync/waitgroup.go index e81a493dea21d5..be21417f9c22e4 100644 --- a/src/sync/waitgroup.go +++ b/src/sync/waitgroup.go @@ -17,24 +17,14 @@ import ( // Wait can be used to block until all goroutines have finished. // // A WaitGroup must not be copied after first use. +// +// In the terminology of the Go memory model, a call to Done +// “synchronizes before” the return of any Wait call that it unblocks. 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 - // the aligned 8 bytes in them as state, and the other 4 as storage - // for the sema. - state1 [3]uint32 -} - -// state returns pointers to the state and sema fields stored within wg.state1. -func (wg *WaitGroup) state() (statep *uint64, semap *uint32) { - if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 { - return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2] - } else { - return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0] - } + state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count. + sema uint32 } // Add adds delta, which may be negative, to the WaitGroup counter. @@ -51,9 +41,7 @@ func (wg *WaitGroup) state() (statep *uint64, semap *uint32) { // new Add calls must happen after all previous Wait calls have returned. // See the WaitGroup example. func (wg *WaitGroup) Add(delta int) { - statep, semap := wg.state() if race.Enabled { - _ = *statep // trigger nil deref early if delta < 0 { // Synchronize decrements with Wait. race.ReleaseMerge(unsafe.Pointer(wg)) @@ -61,14 +49,14 @@ func (wg *WaitGroup) Add(delta int) { race.Disable() defer race.Enable() } - state := atomic.AddUint64(statep, uint64(delta)<<32) + state := wg.state.Add(uint64(delta) << 32) v := int32(state >> 32) w := uint32(state) if race.Enabled && delta > 0 && v == int32(delta) { // The first increment must be synchronized with Wait. // Need to model this as a read, because there can be // several concurrent wg.counter transitions from 0. - race.Read(unsafe.Pointer(semap)) + race.Read(unsafe.Pointer(&wg.sema)) } if v < 0 { panic("sync: negative WaitGroup counter") @@ -84,13 +72,13 @@ func (wg *WaitGroup) Add(delta int) { // - Adds must not happen concurrently with Wait, // - Wait does not increment waiters if it sees counter == 0. // Still do a cheap sanity check to detect WaitGroup misuse. - if *statep != state { + if wg.state.Load() != state { panic("sync: WaitGroup misuse: Add called concurrently with Wait") } // Reset waiters count to 0. - *statep = 0 + wg.state.Store(0) for ; w != 0; w-- { - runtime_Semrelease(semap, false, 0) + runtime_Semrelease(&wg.sema, false, 0) } } @@ -101,13 +89,11 @@ func (wg *WaitGroup) Done() { // Wait blocks until the WaitGroup counter is zero. func (wg *WaitGroup) Wait() { - statep, semap := wg.state() if race.Enabled { - _ = *statep // trigger nil deref early race.Disable() } for { - state := atomic.LoadUint64(statep) + state := wg.state.Load() v := int32(state >> 32) w := uint32(state) if v == 0 { @@ -119,16 +105,16 @@ func (wg *WaitGroup) Wait() { return } // Increment waiters count. - if atomic.CompareAndSwapUint64(statep, state, state+1) { + if wg.state.CompareAndSwap(state, state+1) { if race.Enabled && w == 0 { // Wait must be synchronized with the first Add. // Need to model this is as a write to race with the read in Add. // As a consequence, can do the write only for the first waiter, // otherwise concurrent Waits will race with each other. - race.Write(unsafe.Pointer(semap)) + race.Write(unsafe.Pointer(&wg.sema)) } - runtime_Semacquire(semap) - if *statep != 0 { + runtime_Semacquire(&wg.sema) + if wg.state.Load() != 0 { panic("sync: WaitGroup is reused before previous Wait has returned") } if race.Enabled { diff --git a/src/sync/waitgroup_test.go b/src/sync/waitgroup_test.go index c569e0faa2ebf2..4ded218d2d8d0c 100644 --- a/src/sync/waitgroup_test.go +++ b/src/sync/waitgroup_test.go @@ -5,8 +5,6 @@ package sync_test import ( - "internal/race" - "runtime" . "sync" "sync/atomic" "testing" @@ -48,12 +46,6 @@ func TestWaitGroup(t *testing.T) { } } -func knownRacy(t *testing.T) { - if race.Enabled { - t.Skip("skipping known-racy test under the race detector") - } -} - func TestWaitGroupMisuse(t *testing.T) { defer func() { err := recover() @@ -68,124 +60,6 @@ func TestWaitGroupMisuse(t *testing.T) { t.Fatal("Should panic") } -// pollUntilEqual blocks until v, loaded atomically, is -// equal to the target. -func pollUntilEqual(v *uint32, target uint32) { - for { - for i := 0; i < 1e3; i++ { - if atomic.LoadUint32(v) == target { - return - } - } - // yield to avoid deadlock with the garbage collector - // see issue #20072 - runtime.Gosched() - } -} - -func TestWaitGroupMisuse2(t *testing.T) { - knownRacy(t) - if runtime.NumCPU() <= 4 { - t.Skip("NumCPU<=4, skipping: this test requires parallelism") - } - defer func() { - err := recover() - if err != "sync: negative WaitGroup counter" && - err != "sync: WaitGroup misuse: Add called concurrently with Wait" && - err != "sync: WaitGroup is reused before previous Wait has returned" { - t.Fatalf("Unexpected panic: %#v", err) - } - }() - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) - done := make(chan interface{}, 2) - // The detection is opportunistic, so we want it to panic - // at least in one run out of a million. - for i := 0; i < 1e6; i++ { - var wg WaitGroup - var here uint32 - wg.Add(1) - go func() { - defer func() { - done <- recover() - }() - atomic.AddUint32(&here, 1) - pollUntilEqual(&here, 3) - wg.Wait() - }() - go func() { - defer func() { - done <- recover() - }() - atomic.AddUint32(&here, 1) - pollUntilEqual(&here, 3) - wg.Add(1) // This is the bad guy. - wg.Done() - }() - atomic.AddUint32(&here, 1) - pollUntilEqual(&here, 3) - wg.Done() - for j := 0; j < 2; j++ { - if err := <-done; err != nil { - panic(err) - } - } - } - t.Fatal("Should panic") -} - -func TestWaitGroupMisuse3(t *testing.T) { - knownRacy(t) - if runtime.NumCPU() <= 1 { - t.Skip("NumCPU==1, skipping: this test requires parallelism") - } - defer func() { - err := recover() - if err != "sync: negative WaitGroup counter" && - err != "sync: WaitGroup misuse: Add called concurrently with Wait" && - err != "sync: WaitGroup is reused before previous Wait has returned" { - t.Fatalf("Unexpected panic: %#v", err) - } - }() - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) - done := make(chan interface{}, 3) - // The detection is opportunistically, so we want it to panic - // at least in one run out of a million. - for i := 0; i < 1e6; i++ { - var wg WaitGroup - wg.Add(1) - go func() { - defer func() { - done <- recover() - }() - wg.Done() - }() - go func() { - defer func() { - done <- recover() - }() - wg.Wait() - // Start reusing the wg before waiting for the Wait below to return. - wg.Add(1) - go func() { - wg.Done() - }() - wg.Wait() - }() - go func() { - defer func() { - done <- recover() - }() - wg.Wait() - }() - for j := 0; j < 3; j++ { - if err := <-done; err != nil { - panic(err) - } - } - } - t.Fatal("Should panic") -} - func TestWaitGroupRace(t *testing.T) { // Run this test for about 1ms. for i := 0; i < 1000; i++ { diff --git a/src/syscall/asan.go b/src/syscall/asan.go new file mode 100644 index 00000000000000..eff30781e4d9b8 --- /dev/null +++ b/src/syscall/asan.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build asan + +package syscall + +import ( + "runtime" + "unsafe" +) + +const asanenabled = true + +func asanRead(addr unsafe.Pointer, len int) { + runtime.ASanRead(addr, len) +} + +func asanWrite(addr unsafe.Pointer, len int) { + runtime.ASanWrite(addr, len) +} diff --git a/src/syscall/asan0.go b/src/syscall/asan0.go new file mode 100644 index 00000000000000..08bc44dea1c657 --- /dev/null +++ b/src/syscall/asan0.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !asan + +package syscall + +import ( + "unsafe" +) + +const asanenabled = false + +func asanRead(addr unsafe.Pointer, len int) { +} + +func asanWrite(addr unsafe.Pointer, len int) { +} diff --git a/src/syscall/asm9_unix1_amd64.s b/src/syscall/asm9_unix1_amd64.s deleted file mode 100644 index e4609d075c9918..00000000000000 --- a/src/syscall/asm9_unix1_amd64.s +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build netbsd -// +build netbsd - -#include "textflag.h" -#include "funcdata.h" - -// -// Syscall9 support for AMD64, NetBSD -// - -// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64); -TEXT ·Syscall9(SB),NOSPLIT,$0-104 - CALL runtime·entersyscall(SB) - MOVQ num+0(FP), AX // syscall entry - MOVQ a1+8(FP), DI - MOVQ a2+16(FP), SI - MOVQ a3+24(FP), DX - MOVQ a4+32(FP), R10 - MOVQ a5+40(FP), R8 - MOVQ a6+48(FP), R9 - MOVQ a7+56(FP), R11 - MOVQ a8+64(FP), R12 - MOVQ a9+72(FP), R13 - SUBQ $32, SP - MOVQ R11, 8(SP) // arg 7 - MOVQ R12, 16(SP) // arg 8 - MOVQ R13, 24(SP) // arg 9 - SYSCALL - JCC ok9 - ADDQ $32, SP - MOVQ $-1, 88(SP) // r1 - MOVQ $0, 96(SP) // r2 - MOVQ AX, 104(SP) // errno - CALL runtime·exitsyscall(SB) - RET -ok9: - ADDQ $32, SP - MOVQ AX, 88(SP) // r1 - MOVQ DX, 96(SP) // r2 - MOVQ $0, 104(SP) // errno - CALL runtime·exitsyscall(SB) - RET diff --git a/src/syscall/asm9_unix2_amd64.s b/src/syscall/asm9_unix2_amd64.s index 7e5e3c52ad5d88..5bf53a125116a2 100644 --- a/src/syscall/asm9_unix2_amd64.s +++ b/src/syscall/asm9_unix2_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd -// +build dragonfly freebsd #include "textflag.h" #include "funcdata.h" @@ -14,7 +13,7 @@ // func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64); TEXT ·Syscall9(SB),NOSPLIT,$0-104 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ num+0(FP), AX // syscall entry MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI @@ -39,11 +38,11 @@ TEXT ·Syscall9(SB),NOSPLIT,$0-104 MOVQ $-1, r1+80(FP) // r1 MOVQ $0, r2+88(FP) // r2 MOVQ AX, err+96(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok9: MOVQ AX, r1+80(FP) // r1 MOVQ DX, r2+88(FP) // r2 MOVQ $0, err+96(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET diff --git a/src/syscall/asm_darwin_amd64.s b/src/syscall/asm_darwin_amd64.s index c863889a713852..77b58e051b96e8 100644 --- a/src/syscall/asm_darwin_amd64.s +++ b/src/syscall/asm_darwin_amd64.s @@ -13,7 +13,7 @@ // func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno); TEXT ·Syscall(SB),NOSPLIT,$0-56 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX @@ -24,18 +24,18 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56 MOVQ $-1, r1+32(FP) MOVQ $0, r2+40(FP) MOVQ AX, err+48(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok: MOVQ AX, r1+32(FP) MOVQ DX, r2+40(FP) MOVQ $0, err+48(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET // func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno); TEXT ·Syscall6(SB),NOSPLIT,$0-80 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX @@ -49,18 +49,18 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVQ $-1, r1+56(FP) MOVQ $0, r2+64(FP) MOVQ AX, err+72(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok6: MOVQ AX, r1+56(FP) MOVQ DX, r2+64(FP) MOVQ $0, err+72(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET // func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) TEXT ·Syscall9(SB),NOSPLIT,$0-104 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ trap+0(FP), AX // syscall entry MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI @@ -82,14 +82,14 @@ TEXT ·Syscall9(SB),NOSPLIT,$0-104 MOVQ $-1, r1+80(FP) MOVQ $0, r2+88(FP) MOVQ AX, err+96(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok9: ADDQ $32, SP MOVQ AX, r1+80(FP) MOVQ DX, r2+88(FP) MOVQ $0, err+96(FP) - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET // func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) diff --git a/src/syscall/asm_darwin_arm64.s b/src/syscall/asm_darwin_arm64.s index 95b6dc0db574b3..22e07666df2a19 100644 --- a/src/syscall/asm_darwin_arm64.s +++ b/src/syscall/asm_darwin_arm64.s @@ -10,7 +10,7 @@ // func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R16 MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -21,13 +21,13 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56 MOVD R1, r1+32(FP) // r1 MOVD ZR, r2+40(FP) // r2 MOVD R0, err+48(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+32(FP) // r1 MOVD R1, r2+40(FP) // r2 MOVD ZR, err+48(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) @@ -51,7 +51,7 @@ ok: // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R16 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -65,13 +65,13 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVD R1, r1+56(FP) // r1 MOVD ZR, r2+64(FP) // r2 MOVD R0, err+72(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+56(FP) // r1 MOVD R1, r2+64(FP) // r2 MOVD ZR, err+72(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) @@ -99,7 +99,7 @@ ok: // Actually Syscall7 TEXT ·Syscall9(SB),NOSPLIT,$0-104 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD num+0(FP), R16 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -116,12 +116,12 @@ TEXT ·Syscall9(SB),NOSPLIT,$0-104 MOVD R1, r1+80(FP) // r1 MOVD ZR, r2+88(FP) // r2 MOVD R0, err+96(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+80(FP) // r1 MOVD R1, r2+88(FP) // r2 MOVD ZR, err+96(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET diff --git a/src/syscall/asm_freebsd_arm64.s b/src/syscall/asm_freebsd_arm64.s index 7a0809b8ec918e..b032ce7f69c2db 100644 --- a/src/syscall/asm_freebsd_arm64.s +++ b/src/syscall/asm_freebsd_arm64.s @@ -12,7 +12,7 @@ // func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R8 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -23,13 +23,13 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56 MOVD R1, r1+32(FP) MOVD ZR, r2+40(FP) MOVD R0, err+48(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+32(FP) MOVD R1, r2+40(FP) MOVD ZR, err+48(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) @@ -53,7 +53,7 @@ ok: // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R8 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -67,13 +67,13 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVD R1, r1+56(FP) MOVD ZR, r2+64(FP) MOVD R0, err+72(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+56(FP) MOVD R1, r2+64(FP) MOVD ZR, err+72(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) @@ -101,7 +101,7 @@ ok: // Actually Syscall7 // func Syscall9(num uintptr, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) TEXT ·Syscall9(SB),NOSPLIT,$0-104 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD num+0(FP), R8 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -118,11 +118,11 @@ TEXT ·Syscall9(SB),NOSPLIT,$0-104 MOVD R1, r1+80(FP) MOVD ZR, r2+88(FP) MOVD R0, err+96(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+80(FP) MOVD R1, r2+88(FP) MOVD ZR, err+96(FP) - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET diff --git a/src/syscall/asm_linux_386.s b/src/syscall/asm_linux_386.s index 1c69083118c35b..a8e63f70798afd 100644 --- a/src/syscall/asm_linux_386.s +++ b/src/syscall/asm_linux_386.s @@ -13,121 +13,24 @@ // instead of the glibc-specific "CALL 0x10(GS)". #define INVOKE_SYSCALL INT $0x80 -// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); -// Trap # in AX, args in BX CX DX SI DI, return in AX -TEXT ·Syscall(SB),NOSPLIT,$0-28 - CALL runtime·entersyscall(SB) - MOVL trap+0(FP), AX // syscall entry - MOVL a1+4(FP), BX - MOVL a2+8(FP), CX - MOVL a3+12(FP), DX - MOVL $0, SI - MOVL $0, DI - INVOKE_SYSCALL - CMPL AX, $0xfffff001 - JLS ok - MOVL $-1, r1+16(FP) - MOVL $0, r2+20(FP) - NEGL AX - MOVL AX, err+24(FP) - CALL runtime·exitsyscall(SB) - RET -ok: - MOVL AX, r1+16(FP) - MOVL DX, r2+20(FP) - MOVL $0, err+24(FP) - CALL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); -TEXT ·Syscall6(SB),NOSPLIT,$0-40 - CALL runtime·entersyscall(SB) +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-20 MOVL trap+0(FP), AX // syscall entry MOVL a1+4(FP), BX MOVL a2+8(FP), CX - MOVL a3+12(FP), DX - MOVL a4+16(FP), SI - MOVL a5+20(FP), DI - MOVL a6+24(FP), BP - INVOKE_SYSCALL - CMPL AX, $0xfffff001 - JLS ok6 - MOVL $-1, r1+28(FP) - MOVL $0, r2+32(FP) - NEGL AX - MOVL AX, err+36(FP) - CALL runtime·exitsyscall(SB) - RET -ok6: - MOVL AX, r1+28(FP) - MOVL DX, r2+32(FP) - MOVL $0, err+36(FP) - CALL runtime·exitsyscall(SB) - RET - -// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); -TEXT ·RawSyscall(SB),NOSPLIT,$0-28 - MOVL trap+0(FP), AX // syscall entry - MOVL a1+4(FP), BX - MOVL a2+8(FP), CX - MOVL a3+12(FP), DX - MOVL $0, SI - MOVL $0, DI - INVOKE_SYSCALL - CMPL AX, $0xfffff001 - JLS ok1 - MOVL $-1, r1+16(FP) - MOVL $0, r2+20(FP) - NEGL AX - MOVL AX, err+24(FP) - RET -ok1: - MOVL AX, r1+16(FP) - MOVL DX, r2+20(FP) - MOVL $0, err+24(FP) - RET - -// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); -TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - MOVL trap+0(FP), AX // syscall entry - MOVL a1+4(FP), BX - MOVL a2+8(FP), CX - MOVL a3+12(FP), DX - MOVL a4+16(FP), SI - MOVL a5+20(FP), DI - MOVL a6+24(FP), BP - INVOKE_SYSCALL - CMPL AX, $0xfffff001 - JLS ok2 - MOVL $-1, r1+28(FP) - MOVL $0, r2+32(FP) - NEGL AX - MOVL AX, err+36(FP) - RET -ok2: - MOVL AX, r1+28(FP) - MOVL DX, r2+32(FP) - MOVL $0, err+36(FP) - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-16 - MOVL trap+0(FP), AX // syscall entry - MOVL a1+4(FP), BX - MOVL $0, CX MOVL $0, DX POPL SI // preserve return address INVOKE_SYSCALL PUSHL SI CMPL AX, $0xfffff001 JLS ok - MOVL $-1, r1+8(FP) + MOVL $-1, r1+12(FP) NEGL AX - MOVL AX, err+12(FP) + MOVL AX, err+16(FP) RET ok: - MOVL AX, r1+8(FP) - MOVL $0, err+12(FP) + MOVL AX, r1+12(FP) + MOVL $0, err+16(FP) RET // func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr); diff --git a/src/syscall/asm_linux_amd64.s b/src/syscall/asm_linux_amd64.s index a9af68d51db96d..00d6fedc62b775 100644 --- a/src/syscall/asm_linux_amd64.s +++ b/src/syscall/asm_linux_amd64.s @@ -11,106 +11,10 @@ #define SYS_gettimeofday 96 -// func Syscall(trap int64, a1, a2, a3 uintptr) (r1, r2, err uintptr); -// Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX -// Note that this differs from "standard" ABI convention, which -// would pass 4th arg in CX, not R10. - -TEXT ·Syscall(SB),NOSPLIT,$0-56 - CALL runtime·entersyscall(SB) - MOVQ a1+8(FP), DI - MOVQ a2+16(FP), SI - MOVQ a3+24(FP), DX - MOVQ trap+0(FP), AX // syscall entry - SYSCALL - CMPQ AX, $0xfffffffffffff001 - JLS ok - MOVQ $-1, r1+32(FP) - MOVQ $0, r2+40(FP) - NEGQ AX - MOVQ AX, err+48(FP) - CALL runtime·exitsyscall(SB) - RET -ok: - MOVQ AX, r1+32(FP) - MOVQ DX, r2+40(FP) - MOVQ $0, err+48(FP) - CALL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - CALL runtime·entersyscall(SB) - MOVQ a1+8(FP), DI - MOVQ a2+16(FP), SI - MOVQ a3+24(FP), DX - MOVQ a4+32(FP), R10 - MOVQ a5+40(FP), R8 - MOVQ a6+48(FP), R9 - MOVQ trap+0(FP), AX // syscall entry - SYSCALL - CMPQ AX, $0xfffffffffffff001 - JLS ok6 - MOVQ $-1, r1+56(FP) - MOVQ $0, r2+64(FP) - NEGQ AX - MOVQ AX, err+72(FP) - CALL runtime·exitsyscall(SB) - RET -ok6: - MOVQ AX, r1+56(FP) - MOVQ DX, r2+64(FP) - MOVQ $0, err+72(FP) - CALL runtime·exitsyscall(SB) - RET - -// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40 MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI - MOVQ a3+24(FP), DX - MOVQ trap+0(FP), AX // syscall entry - SYSCALL - CMPQ AX, $0xfffffffffffff001 - JLS ok1 - MOVQ $-1, r1+32(FP) - MOVQ $0, r2+40(FP) - NEGQ AX - MOVQ AX, err+48(FP) - RET -ok1: - MOVQ AX, r1+32(FP) - MOVQ DX, r2+40(FP) - MOVQ $0, err+48(FP) - RET - -// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - MOVQ a1+8(FP), DI - MOVQ a2+16(FP), SI - MOVQ a3+24(FP), DX - MOVQ a4+32(FP), R10 - MOVQ a5+40(FP), R8 - MOVQ a6+48(FP), R9 - MOVQ trap+0(FP), AX // syscall entry - SYSCALL - CMPQ AX, $0xfffffffffffff001 - JLS ok2 - MOVQ $-1, r1+56(FP) - MOVQ $0, r2+64(FP) - NEGQ AX - MOVQ AX, err+72(FP) - RET -ok2: - MOVQ AX, r1+56(FP) - MOVQ DX, r2+64(FP) - MOVQ $0, err+72(FP) - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 - MOVQ a1+8(FP), DI - MOVQ $0, SI MOVQ $0, DX MOVQ $0, R10 MOVQ $0, R8 @@ -121,13 +25,13 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 PUSHQ R12 CMPQ AX, $0xfffffffffffff001 JLS ok2 - MOVQ $-1, r1+16(FP) + MOVQ $-1, r1+24(FP) NEGQ AX - MOVQ AX, err+24(FP) + MOVQ AX, err+32(FP) RET ok2: - MOVQ AX, r1+16(FP) - MOVQ $0, err+24(FP) + MOVQ AX, r1+24(FP) + MOVQ $0, err+32(FP) RET // func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) diff --git a/src/syscall/asm_linux_arm.s b/src/syscall/asm_linux_arm.s index 6bb4df81a0ec5b..d3995416c22e8c 100644 --- a/src/syscall/asm_linux_arm.s +++ b/src/syscall/asm_linux_arm.s @@ -9,95 +9,6 @@ // System calls for arm, Linux // -// func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); -TEXT ·Syscall(SB),NOSPLIT,$0-28 - BL runtime·entersyscall(SB) - MOVW trap+0(FP), R7 - MOVW a1+4(FP), R0 - MOVW a2+8(FP), R1 - MOVW a3+12(FP), R2 - MOVW $0, R3 - MOVW $0, R4 - MOVW $0, R5 - SWI $0 - MOVW $0xfffff001, R1 - CMP R1, R0 - BLS ok - MOVW $-1, R1 - MOVW R1, r1+16(FP) - MOVW $0, R2 - MOVW R2, r2+20(FP) - RSB $0, R0, R0 - MOVW R0, err+24(FP) - BL runtime·exitsyscall(SB) - RET -ok: - MOVW R0, r1+16(FP) - MOVW $0, R0 - MOVW R0, r2+20(FP) - MOVW R0, err+24(FP) - BL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); -// Actually Syscall5 but the rest of the code expects it to be named Syscall6. -TEXT ·Syscall6(SB),NOSPLIT,$0-40 - BL runtime·entersyscall(SB) - MOVW trap+0(FP), R7 // syscall entry - MOVW a1+4(FP), R0 - MOVW a2+8(FP), R1 - MOVW a3+12(FP), R2 - MOVW a4+16(FP), R3 - MOVW a5+20(FP), R4 - MOVW a6+24(FP), R5 - SWI $0 - MOVW $0xfffff001, R6 - CMP R6, R0 - BLS ok6 - MOVW $-1, R1 - MOVW R1, r1+28(FP) - MOVW $0, R2 - MOVW R2, r2+32(FP) - RSB $0, R0, R0 - MOVW R0, err+36(FP) - BL runtime·exitsyscall(SB) - RET -ok6: - MOVW R0, r1+28(FP) - MOVW R1, r2+32(FP) - MOVW $0, R0 - MOVW R0, err+36(FP) - BL runtime·exitsyscall(SB) - RET - -// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); -// Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6. -TEXT ·RawSyscall6(SB),NOSPLIT,$0-40 - MOVW trap+0(FP), R7 // syscall entry - MOVW a1+4(FP), R0 - MOVW a2+8(FP), R1 - MOVW a3+12(FP), R2 - MOVW a4+16(FP), R3 - MOVW a5+20(FP), R4 - MOVW a6+24(FP), R5 - SWI $0 - MOVW $0xfffff001, R6 - CMP R6, R0 - BLS ok2 - MOVW $-1, R1 - MOVW R1, r1+28(FP) - MOVW $0, R2 - MOVW R2, r2+32(FP) - RSB $0, R0, R0 - MOVW R0, err+36(FP) - RET -ok2: - MOVW R0, r1+28(FP) - MOVW R1, r2+32(FP) - MOVW $0, R0 - MOVW R0, err+36(FP) - RET - #define SYS__LLSEEK 140 /* from zsysnum_linux_arm.go */ // func seek(fd int, offset int64, whence int) (newoffset int64, errno int) // Implemented in assembly to avoid allocation when @@ -130,49 +41,25 @@ okseek: BL runtime·exitsyscall(SB) RET -// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); -TEXT ·RawSyscall(SB),NOSPLIT,$0-28 +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-20 MOVW trap+0(FP), R7 // syscall entry MOVW a1+4(FP), R0 MOVW a2+8(FP), R1 - MOVW a3+12(FP), R2 - SWI $0 - MOVW $0xfffff001, R1 - CMP R1, R0 - BLS ok1 - MOVW $-1, R1 - MOVW R1, r1+16(FP) - MOVW $0, R2 - MOVW R2, r2+20(FP) - RSB $0, R0, R0 - MOVW R0, err+24(FP) - RET -ok1: - MOVW R0, r1+16(FP) - MOVW $0, R0 - MOVW R0, r2+20(FP) - MOVW R0, err+24(FP) - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-16 - MOVW trap+0(FP), R7 // syscall entry - MOVW a1+4(FP), R0 - MOVW $0, R1 MOVW $0, R2 SWI $0 MOVW $0xfffff001, R1 CMP R1, R0 BLS ok MOVW $-1, R1 - MOVW R1, r1+8(FP) + MOVW R1, r1+12(FP) RSB $0, R0, R0 - MOVW R0, err+12(FP) + MOVW R0, err+16(FP) RET ok: - MOVW R0, r1+8(FP) + MOVW R0, r1+12(FP) MOVW $0, R0 - MOVW R0, err+12(FP) + MOVW R0, err+16(FP) RET // func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr); diff --git a/src/syscall/asm_linux_arm64.s b/src/syscall/asm_linux_arm64.s index a30e4d87d4c08a..7fa789a349daa7 100644 --- a/src/syscall/asm_linux_arm64.s +++ b/src/syscall/asm_linux_arm64.s @@ -4,109 +4,10 @@ #include "textflag.h" -// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); -TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-40 MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 - MOVD a3+24(FP), R2 - MOVD $0, R3 - MOVD $0, R4 - MOVD $0, R5 - MOVD trap+0(FP), R8 // syscall entry - SVC - CMN $4095, R0 - BCC ok - MOVD $-1, R4 - MOVD R4, r1+32(FP) // r1 - MOVD ZR, r2+40(FP) // r2 - NEG R0, R0 - MOVD R0, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok: - MOVD R0, r1+32(FP) // r1 - MOVD R1, r2+40(FP) // r2 - MOVD ZR, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET - -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) - MOVD a1+8(FP), R0 - MOVD a2+16(FP), R1 - MOVD a3+24(FP), R2 - MOVD a4+32(FP), R3 - MOVD a5+40(FP), R4 - MOVD a6+48(FP), R5 - MOVD trap+0(FP), R8 // syscall entry - SVC - CMN $4095, R0 - BCC ok - MOVD $-1, R4 - MOVD R4, r1+56(FP) // r1 - MOVD ZR, r2+64(FP) // r2 - NEG R0, R0 - MOVD R0, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok: - MOVD R0, r1+56(FP) // r1 - MOVD R1, r2+64(FP) // r2 - MOVD ZR, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET - -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - MOVD a1+8(FP), R0 - MOVD a2+16(FP), R1 - MOVD a3+24(FP), R2 - MOVD $0, R3 - MOVD $0, R4 - MOVD $0, R5 - MOVD trap+0(FP), R8 // syscall entry - SVC - CMN $4095, R0 - BCC ok - MOVD $-1, R4 - MOVD R4, r1+32(FP) // r1 - MOVD ZR, r2+40(FP) // r2 - NEG R0, R0 - MOVD R0, err+48(FP) // errno - RET -ok: - MOVD R0, r1+32(FP) // r1 - MOVD R1, r2+40(FP) // r2 - MOVD ZR, err+48(FP) // errno - RET - -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - MOVD a1+8(FP), R0 - MOVD a2+16(FP), R1 - MOVD a3+24(FP), R2 - MOVD a4+32(FP), R3 - MOVD a5+40(FP), R4 - MOVD a6+48(FP), R5 - MOVD trap+0(FP), R8 // syscall entry - SVC - CMN $4095, R0 - BCC ok - MOVD $-1, R4 - MOVD R4, r1+56(FP) // r1 - MOVD ZR, r2+64(FP) // r2 - NEG R0, R0 - MOVD R0, err+72(FP) // errno - RET -ok: - MOVD R0, r1+56(FP) // r1 - MOVD R1, r2+64(FP) // r2 - MOVD ZR, err+72(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-32 - MOVD a1+8(FP), R0 - MOVD $0, R1 MOVD $0, R2 MOVD $0, R3 MOVD $0, R4 @@ -116,13 +17,13 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-32 CMN $4095, R0 BCC ok MOVD $-1, R4 - MOVD R4, r1+16(FP) // r1 + MOVD R4, r1+24(FP) // r1 NEG R0, R0 - MOVD R0, err+24(FP) // errno + MOVD R0, err+32(FP) // errno RET ok: - MOVD R0, r1+16(FP) // r1 - MOVD ZR, err+24(FP) // errno + MOVD R0, r1+24(FP) // r1 + MOVD ZR, err+32(FP) // errno RET // func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr); diff --git a/src/syscall/asm_linux_loong64.s b/src/syscall/asm_linux_loong64.s new file mode 100644 index 00000000000000..1a7457c7ead893 --- /dev/null +++ b/src/syscall/asm_linux_loong64.s @@ -0,0 +1,44 @@ +// Copyright 2022 The Go 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" + +// +// System calls for loong64, Linux +// + +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-40 + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV $0, R6 + MOVV $0, R7 + MOVV $0, R8 + MOVV $0, R9 + MOVV trap+0(FP), R11 // syscall entry + SYSCALL + MOVW $-4096, R12 + BGEU R12, R4, ok + MOVV $-1, R12 + MOVV R12, r1+24(FP) // r1 + SUBVU R4, R0, R4 + MOVV R4, err+32(FP) // errno + RET +ok: + MOVV R4, r1+24(FP) // r1 + MOVV R0, err+32(FP) // errno + RET + +TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV a3+24(FP), R6 + MOVV R0, R7 + MOVV R0, R8 + MOVV R0, R9 + MOVV trap+0(FP), R11 // syscall entry + SYSCALL + MOVV R4, r1+32(FP) + MOVV R0, r2+40(FP) // r2 is not used. Always set to 0. + RET diff --git a/src/syscall/asm_linux_mips64x.s b/src/syscall/asm_linux_mips64x.s index d18a7b89440648..ceafeb6b013b13 100644 --- a/src/syscall/asm_linux_mips64x.s +++ b/src/syscall/asm_linux_mips64x.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le #include "textflag.h" @@ -12,101 +10,10 @@ // System calls for mips64, Linux // -// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); -TEXT ·Syscall(SB),NOSPLIT,$0-56 - JAL runtime·entersyscall(SB) +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40 MOVV a1+8(FP), R4 MOVV a2+16(FP), R5 - MOVV a3+24(FP), R6 - MOVV R0, R7 - MOVV R0, R8 - MOVV R0, R9 - MOVV trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok - MOVV $-1, R1 - MOVV R1, r1+32(FP) // r1 - MOVV R0, r2+40(FP) // r2 - MOVV R2, err+48(FP) // errno - JAL runtime·exitsyscall(SB) - RET -ok: - MOVV R2, r1+32(FP) // r1 - MOVV R3, r2+40(FP) // r2 - MOVV R0, err+48(FP) // errno - JAL runtime·exitsyscall(SB) - RET - -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - JAL runtime·entersyscall(SB) - MOVV a1+8(FP), R4 - MOVV a2+16(FP), R5 - MOVV a3+24(FP), R6 - MOVV a4+32(FP), R7 - MOVV a5+40(FP), R8 - MOVV a6+48(FP), R9 - MOVV trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok6 - MOVV $-1, R1 - MOVV R1, r1+56(FP) // r1 - MOVV R0, r2+64(FP) // r2 - MOVV R2, err+72(FP) // errno - JAL runtime·exitsyscall(SB) - RET -ok6: - MOVV R2, r1+56(FP) // r1 - MOVV R3, r2+64(FP) // r2 - MOVV R0, err+72(FP) // errno - JAL runtime·exitsyscall(SB) - RET - -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - MOVV a1+8(FP), R4 - MOVV a2+16(FP), R5 - MOVV a3+24(FP), R6 - MOVV R0, R7 - MOVV R0, R8 - MOVV R0, R9 - MOVV trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok1 - MOVV $-1, R1 - MOVV R1, r1+32(FP) // r1 - MOVV R0, r2+40(FP) // r2 - MOVV R2, err+48(FP) // errno - RET -ok1: - MOVV R2, r1+32(FP) // r1 - MOVV R3, r2+40(FP) // r2 - MOVV R0, err+48(FP) // errno - RET - -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - MOVV a1+8(FP), R4 - MOVV a2+16(FP), R5 - MOVV a3+24(FP), R6 - MOVV a4+32(FP), R7 - MOVV a5+40(FP), R8 - MOVV a6+48(FP), R9 - MOVV trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok2 - MOVV $-1, R1 - MOVV R1, r1+56(FP) // r1 - MOVV R0, r2+64(FP) // r2 - MOVV R2, err+72(FP) // errno - RET -ok2: - MOVV R2, r1+56(FP) // r1 - MOVV R3, r2+64(FP) // r2 - MOVV R0, err+72(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 - MOVV a1+8(FP), R4 - MOVV R0, R5 MOVV R0, R6 MOVV R0, R7 MOVV R0, R8 @@ -115,12 +22,12 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 SYSCALL BEQ R7, ok MOVV $-1, R1 - MOVV R1, r1+16(FP) // r1 - MOVV R2, err+24(FP) // errno + MOVV R1, r1+24(FP) // r1 + MOVV R2, err+32(FP) // errno RET ok: - MOVV R2, r1+16(FP) // r1 - MOVV R0, err+24(FP) // errno + MOVV R2, r1+24(FP) // r1 + MOVV R0, err+32(FP) // errno RET TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 diff --git a/src/syscall/asm_linux_mipsx.s b/src/syscall/asm_linux_mipsx.s index cafa6a35c7bf48..3e5e8b11397795 100644 --- a/src/syscall/asm_linux_mipsx.s +++ b/src/syscall/asm_linux_mipsx.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle #include "textflag.h" #include "funcdata.h" @@ -13,59 +11,6 @@ // System calls for mips, Linux // -// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr); -TEXT ·Syscall(SB),NOSPLIT,$0-28 - JAL runtime·entersyscall(SB) - MOVW a1+4(FP), R4 - MOVW a2+8(FP), R5 - MOVW a3+12(FP), R6 - MOVW R0, R7 - MOVW trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok - MOVW $-1, R1 - MOVW R1, r1+16(FP) // r1 - MOVW R0, r2+20(FP) // r2 - MOVW R2, err+24(FP) // errno - JAL runtime·exitsyscall(SB) - RET -ok: - MOVW R2, r1+16(FP) // r1 - MOVW R3, r2+20(FP) // r2 - MOVW R0, err+24(FP) // errno - JAL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); -// 5th and 6th arg go at sp+16, sp+20. -// Note that frame size of 20 means that 24 bytes gets reserved on stack. -TEXT ·Syscall6(SB),NOSPLIT,$20-40 - NO_LOCAL_POINTERS - JAL runtime·entersyscall(SB) - MOVW a1+4(FP), R4 - MOVW a2+8(FP), R5 - MOVW a3+12(FP), R6 - MOVW a4+16(FP), R7 - MOVW a5+20(FP), R8 - MOVW a6+24(FP), R9 - MOVW R8, 16(R29) - MOVW R9, 20(R29) - MOVW trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok6 - MOVW $-1, R1 - MOVW R1, r1+28(FP) // r1 - MOVW R0, r2+32(FP) // r2 - MOVW R2, err+36(FP) // errno - JAL runtime·exitsyscall(SB) - RET -ok6: - MOVW R2, r1+28(FP) // r1 - MOVW R3, r2+32(FP) // r2 - MOVW R0, err+36(FP) // errno - JAL runtime·exitsyscall(SB) - RET - // func Syscall9(trap trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr); // Actually Syscall8 but the rest of the code expects it to be named Syscall9. TEXT ·Syscall9(SB),NOSPLIT,$28-52 @@ -99,62 +44,21 @@ ok9: JAL runtime·exitsyscall(SB) RET -TEXT ·RawSyscall(SB),NOSPLIT,$24-28 +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-20 MOVW a1+4(FP), R4 MOVW a2+8(FP), R5 - MOVW a3+12(FP), R6 - MOVW trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok1 - MOVW $-1, R1 - MOVW R1, r1+16(FP) // r1 - MOVW R0, r2+20(FP) // r2 - MOVW R2, err+24(FP) // errno - RET -ok1: - MOVW R2, r1+16(FP) // r1 - MOVW R3, r2+20(FP) // r2 - MOVW R0, err+24(FP) // errno - RET - -TEXT ·RawSyscall6(SB),NOSPLIT,$20-40 - MOVW a1+4(FP), R4 - MOVW a2+8(FP), R5 - MOVW a3+12(FP), R6 - MOVW a4+16(FP), R7 - MOVW a5+20(FP), R8 - MOVW a6+24(FP), R9 - MOVW R8, 16(R29) - MOVW R9, 20(R29) - MOVW trap+0(FP), R2 // syscall entry - SYSCALL - BEQ R7, ok2 - MOVW $-1, R1 - MOVW R1, r1+28(FP) // r1 - MOVW R0, r2+32(FP) // r2 - MOVW R2, err+36(FP) // errno - RET -ok2: - MOVW R2, r1+28(FP) // r1 - MOVW R3, r2+32(FP) // r2 - MOVW R0, err+36(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-16 - MOVW a1+4(FP), R4 - MOVW R0, R5 MOVW R0, R6 MOVW trap+0(FP), R2 // syscall entry SYSCALL BEQ R7, ok MOVW $-1, R1 - MOVW R1, r1+8(FP) // r1 - MOVW R2, err+12(FP) // errno + MOVW R1, r1+12(FP) // r1 + MOVW R2, err+16(FP) // errno RET ok: - MOVW R2, r1+8(FP) // r1 - MOVW R0, err+12(FP) // errno + MOVW R2, r1+12(FP) // r1 + MOVW R0, err+16(FP) // errno RET TEXT ·rawSyscallNoError(SB),NOSPLIT,$20-24 diff --git a/src/syscall/asm_linux_ppc64x.s b/src/syscall/asm_linux_ppc64x.s index eac7272f1d8f42..b9412fec1d418d 100644 --- a/src/syscall/asm_linux_ppc64x.s +++ b/src/syscall/asm_linux_ppc64x.s @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le #include "textflag.h" @@ -12,101 +10,10 @@ // System calls for ppc64, Linux // -// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); -TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40 MOVD a1+8(FP), R3 MOVD a2+16(FP), R4 - MOVD a3+24(FP), R5 - MOVD R0, R6 - MOVD R0, R7 - MOVD R0, R8 - MOVD trap+0(FP), R9 // syscall entry - SYSCALL R9 - BVC ok - MOVD $-1, R4 - MOVD R4, r1+32(FP) // r1 - MOVD R0, r2+40(FP) // r2 - MOVD R3, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok: - MOVD R3, r1+32(FP) // r1 - MOVD R4, r2+40(FP) // r2 - MOVD R0, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET - -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) - MOVD a1+8(FP), R3 - MOVD a2+16(FP), R4 - MOVD a3+24(FP), R5 - MOVD a4+32(FP), R6 - MOVD a5+40(FP), R7 - MOVD a6+48(FP), R8 - MOVD trap+0(FP), R9 // syscall entry - SYSCALL R9 - BVC ok6 - MOVD $-1, R4 - MOVD R4, r1+56(FP) // r1 - MOVD R0, r2+64(FP) // r2 - MOVD R3, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok6: - MOVD R3, r1+56(FP) // r1 - MOVD R4, r2+64(FP) // r2 - MOVD R0, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET - -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - MOVD a1+8(FP), R3 - MOVD a2+16(FP), R4 - MOVD a3+24(FP), R5 - MOVD R0, R6 - MOVD R0, R7 - MOVD R0, R8 - MOVD trap+0(FP), R9 // syscall entry - SYSCALL R9 - BVC ok1 - MOVD $-1, R4 - MOVD R4, r1+32(FP) // r1 - MOVD R0, r2+40(FP) // r2 - MOVD R3, err+48(FP) // errno - RET -ok1: - MOVD R3, r1+32(FP) // r1 - MOVD R4, r2+40(FP) // r2 - MOVD R0, err+48(FP) // errno - RET - -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - MOVD a1+8(FP), R3 - MOVD a2+16(FP), R4 - MOVD a3+24(FP), R5 - MOVD a4+32(FP), R6 - MOVD a5+40(FP), R7 - MOVD a6+48(FP), R8 - MOVD trap+0(FP), R9 // syscall entry - SYSCALL R9 - BVC ok2 - MOVD $-1, R4 - MOVD R4, r1+56(FP) // r1 - MOVD R0, r2+64(FP) // r2 - MOVD R3, err+72(FP) // errno - RET -ok2: - MOVD R3, r1+56(FP) // r1 - MOVD R4, r2+64(FP) // r2 - MOVD R0, err+72(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 - MOVD a1+8(FP), R3 - MOVD R0, R4 MOVD R0, R5 MOVD R0, R6 MOVD R0, R7 @@ -115,12 +22,12 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 SYSCALL R9 BVC ok MOVD $-1, R4 - MOVD R4, r1+16(FP) // r1 - MOVD R3, err+24(FP) // errno + MOVD R4, r1+24(FP) // r1 + MOVD R3, err+32(FP) // errno RET ok: - MOVD R3, r1+16(FP) // r1 - MOVD R0, err+24(FP) // errno + MOVD R3, r1+24(FP) // r1 + MOVD R0, err+32(FP) // errno RET TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 @@ -133,5 +40,5 @@ TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 MOVD trap+0(FP), R9 // syscall entry SYSCALL R9 MOVD R3, r1+32(FP) - MOVD R4, r2+40(FP) + MOVD R0, r2+40(FP) RET diff --git a/src/syscall/asm_linux_riscv64.s b/src/syscall/asm_linux_riscv64.s index f172dd3d9b139c..6fd09ec4222a93 100644 --- a/src/syscall/asm_linux_riscv64.s +++ b/src/syscall/asm_linux_riscv64.s @@ -8,106 +8,10 @@ // System calls for riscv64, Linux // -// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64) -TEXT ·Syscall(SB),NOSPLIT,$0-56 - CALL runtime·entersyscall(SB) +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40 MOV a1+8(FP), A0 MOV a2+16(FP), A1 - MOV a3+24(FP), A2 - MOV trap+0(FP), A7 // syscall entry - ECALL - MOV $-4096, T0 - BLTU T0, A0, err - MOV A0, r1+32(FP) // r1 - MOV A1, r2+40(FP) // r2 - MOV ZERO, err+48(FP) // errno - CALL runtime·exitsyscall(SB) - RET -err: - MOV $-1, T0 - MOV T0, r1+32(FP) // r1 - MOV ZERO, r2+40(FP) // r2 - SUB A0, ZERO, A0 - MOV A0, err+48(FP) // errno - CALL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - CALL runtime·entersyscall(SB) - MOV a1+8(FP), A0 - MOV a2+16(FP), A1 - MOV a3+24(FP), A2 - MOV a4+32(FP), A3 - MOV a5+40(FP), A4 - MOV a6+48(FP), A5 - MOV trap+0(FP), A7 // syscall entry - ECALL - MOV $-4096, T0 - BLTU T0, A0, err - MOV A0, r1+56(FP) // r1 - MOV A1, r2+64(FP) // r2 - MOV ZERO, err+72(FP) // errno - CALL runtime·exitsyscall(SB) - RET -err: - MOV $-1, T0 - MOV T0, r1+56(FP) // r1 - MOV ZERO, r2+64(FP) // r2 - SUB A0, ZERO, A0 - MOV A0, err+72(FP) // errno - CALL runtime·exitsyscall(SB) - RET - -// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - MOV a1+8(FP), A0 - MOV a2+16(FP), A1 - MOV a3+24(FP), A2 - MOV trap+0(FP), A7 // syscall entry - ECALL - MOV $-4096, T0 - BLTU T0, A0, err - MOV A0, r1+32(FP) // r1 - MOV A1, r2+40(FP) // r2 - MOV ZERO, err+48(FP) // errno - RET -err: - MOV $-1, T0 - MOV T0, r1+32(FP) // r1 - MOV ZERO, r2+40(FP) // r2 - SUB A0, ZERO, A0 - MOV A0, err+48(FP) // errno - RET - -// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 - MOV a1+8(FP), A0 - MOV a2+16(FP), A1 - MOV a3+24(FP), A2 - MOV a4+32(FP), A3 - MOV a5+40(FP), A4 - MOV a6+48(FP), A5 - MOV trap+0(FP), A7 // syscall entry - ECALL - MOV $-4096, T0 - BLTU T0, A0, err - MOV A0, r1+56(FP) // r1 - MOV A1, r2+64(FP) // r2 - MOV ZERO, err+72(FP) // errno - RET -err: - MOV $-1, T0 - MOV T0, r1+56(FP) // r1 - MOV ZERO, r2+64(FP) // r2 - SUB A0, ZERO, A0 - MOV A0, err+72(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 - MOV a1+8(FP), A0 - MOV ZERO, A1 MOV ZERO, A2 MOV ZERO, A3 MOV ZERO, A4 @@ -116,14 +20,14 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 ECALL MOV $-4096, T0 BLTU T0, A0, err - MOV A0, r1+16(FP) // r1 - MOV ZERO, err+24(FP) // errno + MOV A0, r1+24(FP) // r1 + MOV ZERO, err+32(FP) // errno RET err: MOV $-1, T0 - MOV T0, r1+16(FP) // r1 + MOV T0, r1+24(FP) // r1 SUB A0, ZERO, A0 - MOV A0, err+24(FP) // errno + MOV A0, err+32(FP) // errno RET TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 diff --git a/src/syscall/asm_linux_s390x.s b/src/syscall/asm_linux_s390x.s index 86a5c51ee29dfb..41c34b1e178e26 100644 --- a/src/syscall/asm_linux_s390x.s +++ b/src/syscall/asm_linux_s390x.s @@ -8,108 +8,10 @@ // System calls for s390x, Linux // -// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64) -TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) - MOVD a1+8(FP), R2 - MOVD a2+16(FP), R3 - MOVD a3+24(FP), R4 - MOVD $0, R5 - MOVD $0, R6 - MOVD $0, R7 - MOVD trap+0(FP), R1 // syscall entry - SYSCALL - MOVD $0xfffffffffffff001, R8 - CMPUBLT R2, R8, ok - MOVD $-1, r1+32(FP) - MOVD $0, r2+40(FP) - NEG R2, R2 - MOVD R2, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok: - MOVD R2, r1+32(FP) - MOVD R3, r2+40(FP) - MOVD $0, err+48(FP) // errno - BL runtime·exitsyscall(SB) - RET - -// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) - MOVD a1+8(FP), R2 - MOVD a2+16(FP), R3 - MOVD a3+24(FP), R4 - MOVD a4+32(FP), R5 - MOVD a5+40(FP), R6 - MOVD a6+48(FP), R7 - MOVD trap+0(FP), R1 // syscall entry - SYSCALL - MOVD $0xfffffffffffff001, R8 - CMPUBLT R2, R8, ok6 - MOVD $-1, r1+56(FP) - MOVD $0, r2+64(FP) - NEG R2, R2 - MOVD R2, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET -ok6: - MOVD R2, r1+56(FP) - MOVD R3, r2+64(FP) - MOVD $0, err+72(FP) // errno - BL runtime·exitsyscall(SB) - RET - -// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall(SB),NOSPLIT,$0-56 - MOVD a1+8(FP), R2 - MOVD a2+16(FP), R3 - MOVD a3+24(FP), R4 - MOVD $0, R5 - MOVD $0, R6 - MOVD $0, R7 - MOVD trap+0(FP), R1 // syscall entry - SYSCALL - MOVD $0xfffffffffffff001, R8 - CMPUBLT R2, R8, ok1 - MOVD $-1, r1+32(FP) - MOVD $0, r2+40(FP) - NEG R2, R2 - MOVD R2, err+48(FP) // errno - RET -ok1: - MOVD R2, r1+32(FP) - MOVD R3, r2+40(FP) - MOVD $0, err+48(FP) // errno - RET - -// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) -TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 +// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40 MOVD a1+8(FP), R2 MOVD a2+16(FP), R3 - MOVD a3+24(FP), R4 - MOVD a4+32(FP), R5 - MOVD a5+40(FP), R6 - MOVD a6+48(FP), R7 - MOVD trap+0(FP), R1 // syscall entry - SYSCALL - MOVD $0xfffffffffffff001, R8 - CMPUBLT R2, R8, ok2 - MOVD $-1, r1+56(FP) - MOVD $0, r2+64(FP) - NEG R2, R2 - MOVD R2, err+72(FP) // errno - RET -ok2: - MOVD R2, r1+56(FP) - MOVD R3, r2+64(FP) - MOVD $0, err+72(FP) // errno - RET - -// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) -TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 - MOVD $0, R2 - MOVD a1+8(FP), R3 MOVD $0, R4 MOVD $0, R5 MOVD $0, R6 @@ -118,13 +20,13 @@ TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-32 SYSCALL MOVD $0xfffffffffffff001, R8 CMPUBLT R2, R8, ok2 - MOVD $-1, r1+16(FP) + MOVD $-1, r1+24(FP) NEG R2, R2 - MOVD R2, err+24(FP) // errno + MOVD R2, err+32(FP) // errno RET ok2: - MOVD R2, r1+16(FP) - MOVD $0, err+24(FP) // errno + MOVD R2, r1+24(FP) + MOVD $0, err+32(FP) // errno RET // func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) diff --git a/src/syscall/asm_netbsd_amd64.s b/src/syscall/asm_netbsd_amd64.s new file mode 100644 index 00000000000000..457e207296ed0b --- /dev/null +++ b/src/syscall/asm_netbsd_amd64.s @@ -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. + +#include "textflag.h" +#include "funcdata.h" + +// +// Syscall9 support for AMD64, NetBSD +// + +// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64); +TEXT ·Syscall9(SB),NOSPLIT,$0-104 + CALL runtime·entersyscall(SB) + MOVQ num+0(FP), AX // syscall entry + MOVQ a1+8(FP), DI + MOVQ a2+16(FP), SI + MOVQ a3+24(FP), DX + MOVQ a4+32(FP), R10 + MOVQ a5+40(FP), R8 + MOVQ a6+48(FP), R9 + MOVQ a7+56(FP), R11 + MOVQ a8+64(FP), R12 + MOVQ a9+72(FP), R13 + SUBQ $32, SP + MOVQ R11, 8(SP) // arg 7 + MOVQ R12, 16(SP) // arg 8 + MOVQ R13, 24(SP) // arg 9 + SYSCALL + JCC ok9 + ADDQ $32, SP + MOVQ $-1, 88(SP) // r1 + MOVQ $0, 96(SP) // r2 + MOVQ AX, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET +ok9: + ADDQ $32, SP + MOVQ AX, 88(SP) // r1 + MOVQ DX, 96(SP) // r2 + MOVQ $0, 104(SP) // errno + CALL runtime·exitsyscall(SB) + RET diff --git a/src/syscall/asm_netbsd_arm64.s b/src/syscall/asm_netbsd_arm64.s index fbcd3388c98858..aebd83f32592f2 100644 --- a/src/syscall/asm_netbsd_arm64.s +++ b/src/syscall/asm_netbsd_arm64.s @@ -12,7 +12,7 @@ // func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) TEXT ·Syscall(SB),NOSPLIT,$0-56 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R17 MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -23,13 +23,13 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56 MOVD R1, r1+32(FP) // r1 MOVD ZR, r2+40(FP) // r2 MOVD R0, err+48(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+32(FP) // r1 MOVD R1, r2+40(FP) // r2 MOVD ZR, err+48(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) @@ -53,7 +53,7 @@ ok: // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) TEXT ·Syscall6(SB),NOSPLIT,$0-80 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD trap+0(FP), R17 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -67,13 +67,13 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVD R1, r1+56(FP) // r1 MOVD ZR, r2+64(FP) // r2 MOVD R0, err+72(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+56(FP) // r1 MOVD R1, r2+64(FP) // r2 MOVD ZR, err+72(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) @@ -101,7 +101,7 @@ ok: // Actually Syscall7 TEXT ·Syscall9(SB),NOSPLIT,$0-104 - BL runtime·entersyscall(SB) + BL runtime·entersyscall(SB) MOVD num+0(FP), R17 // syscall entry MOVD a1+8(FP), R0 MOVD a2+16(FP), R1 @@ -118,12 +118,11 @@ TEXT ·Syscall9(SB),NOSPLIT,$0-104 MOVD R1, r1+80(FP) // r1 MOVD ZR, r2+88(FP) // r2 MOVD R0, err+96(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET ok: MOVD R0, r1+80(FP) // r1 MOVD R1, r2+88(FP) // r2 MOVD ZR, err+96(FP) // err - BL runtime·exitsyscall(SB) + BL runtime·exitsyscall(SB) RET - diff --git a/src/syscall/asm_plan9_amd64.s b/src/syscall/asm_plan9_amd64.s index d5c9f6c63f1e29..0641513f376736 100644 --- a/src/syscall/asm_plan9_amd64.s +++ b/src/syscall/asm_plan9_amd64.s @@ -18,7 +18,7 @@ TEXT ·Syscall(SB),NOSPLIT,$168-64 NO_LOCAL_POINTERS - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ trap+0(FP), BP // syscall entry // copy args down LEAQ a1+8(FP), SI @@ -38,7 +38,7 @@ TEXT ·Syscall(SB),NOSPLIT,$168-64 MOVQ $128, sysargs1-152(SP) MOVQ $SYS_ERRSTR, BP SYSCALL - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set MOVQ sysargs-160(SP), AX MOVQ AX, errbuf-168(SP) CALL runtime·gostring(SB) @@ -46,7 +46,7 @@ TEXT ·Syscall(SB),NOSPLIT,$168-64 JMP copyresult3 ok3: - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set LEAQ ·emptystring(SB), SI copyresult3: @@ -60,7 +60,7 @@ copyresult3: TEXT ·Syscall6(SB),NOSPLIT,$168-88 NO_LOCAL_POINTERS - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ trap+0(FP), BP // syscall entry // copy args down LEAQ a1+8(FP), SI @@ -83,7 +83,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$168-88 MOVQ $128, sysargs1-152(SP) MOVQ $SYS_ERRSTR, BP SYSCALL - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set MOVQ sysargs-160(SP), AX MOVQ AX, errbuf-168(SP) CALL runtime·gostring(SB) @@ -91,7 +91,7 @@ TEXT ·Syscall6(SB),NOSPLIT,$168-88 JMP copyresult4 ok4: - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set LEAQ ·emptystring(SB), SI copyresult4: diff --git a/src/syscall/asm_solaris_amd64.s b/src/syscall/asm_solaris_amd64.s index c61e04a42fab4c..6891e9c8f96d5e 100644 --- a/src/syscall/asm_solaris_amd64.s +++ b/src/syscall/asm_solaris_amd64.s @@ -20,7 +20,7 @@ TEXT ·chdir(SB),NOSPLIT,$0 TEXT ·chroot1(SB),NOSPLIT,$0 JMP runtime·syscall_chroot(SB) -TEXT ·close(SB),NOSPLIT,$0 +TEXT ·closeFD(SB),NOSPLIT,$0 JMP runtime·syscall_close(SB) TEXT ·dup2child(SB),NOSPLIT,$0 @@ -48,9 +48,6 @@ TEXT ·getpid(SB),NOSPLIT,$0 TEXT ·ioctl(SB),NOSPLIT,$0 JMP runtime·syscall_ioctl(SB) -TEXT ·pipe(SB),NOSPLIT,$0 - JMP runtime·syscall_pipe(SB) - TEXT ·RawSyscall(SB),NOSPLIT,$0 JMP runtime·syscall_rawsyscall(SB) diff --git a/src/syscall/asm_unix_386.s b/src/syscall/asm_unix_386.s index 9f9b7f362d231c..22700194cbea95 100644 --- a/src/syscall/asm_unix_386.s +++ b/src/syscall/asm_unix_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || netbsd -// +build freebsd netbsd #include "textflag.h" #include "funcdata.h" diff --git a/src/syscall/asm_unix_amd64.s b/src/syscall/asm_unix_amd64.s index c53e1a42b6471a..6d8da715a2f509 100644 --- a/src/syscall/asm_unix_amd64.s +++ b/src/syscall/asm_unix_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || netbsd -// +build dragonfly freebsd netbsd #include "textflag.h" #include "funcdata.h" @@ -17,7 +16,7 @@ // Trap # in AX, args in DI SI DX, return in AX DX TEXT ·Syscall(SB),NOSPLIT,$0-56 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ trap+0(FP), AX // syscall entry MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI @@ -27,17 +26,17 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56 MOVQ $-1, r1+32(FP) // r1 MOVQ $0, r2+40(FP) // r2 MOVQ AX, err+48(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok: MOVQ AX, r1+32(FP) // r1 MOVQ DX, r2+40(FP) // r2 MOVQ $0, err+48(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET TEXT ·Syscall6(SB),NOSPLIT,$0-80 - CALL runtime·entersyscall(SB) + CALL runtime·entersyscall(SB) MOVQ trap+0(FP), AX // syscall entry MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI @@ -50,13 +49,13 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVQ $-1, r1+56(FP) // r1 MOVQ $0, r2+64(FP) // r2 MOVQ AX, err+72(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET ok6: MOVQ AX, r1+56(FP) // r1 MOVQ DX, r2+64(FP) // r2 MOVQ $0, err+72(FP) // errno - CALL runtime·exitsyscall(SB) + CALL runtime·exitsyscall(SB) RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 diff --git a/src/syscall/bpf_bsd.go b/src/syscall/bpf_bsd.go index 452d4cf14b6b13..735c0788950db2 100644 --- a/src/syscall/bpf_bsd.go +++ b/src/syscall/bpf_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || netbsd || openbsd -// +build dragonfly freebsd netbsd openbsd // Berkeley packet filter for BSD variants diff --git a/src/syscall/creds_test.go b/src/syscall/creds_test.go index c1a8b516e81a40..2fc61df1c5023c 100644 --- a/src/syscall/creds_test.go +++ b/src/syscall/creds_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package syscall_test @@ -78,8 +77,10 @@ func TestSCMCredentials(t *testing.T) { if sys, ok := err.(*os.SyscallError); ok { err = sys.Err } - if err != syscall.EPERM { - t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err) + switch err { + case syscall.EPERM, syscall.EINVAL: + default: + t.Fatalf("WriteMsgUnix failed with %v, want EPERM or EINVAL", err) } } diff --git a/src/syscall/dir_plan9.go b/src/syscall/dir_plan9.go index 4ed052de7619ed..1667cbc02f49e0 100644 --- a/src/syscall/dir_plan9.go +++ b/src/syscall/dir_plan9.go @@ -184,6 +184,7 @@ func gbit8(b []byte) (uint8, []byte) { } // gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b. +// //go:nosplit func gbit16(b []byte) (uint16, []byte) { return uint16(b[0]) | uint16(b[1])<<8, b[2:] diff --git a/src/syscall/dirent.go b/src/syscall/dirent.go index 9e1222e81c6dfc..b10608a6629c53 100644 --- a/src/syscall/dirent.go +++ b/src/syscall/dirent.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package syscall diff --git a/src/syscall/dirent_test.go b/src/syscall/dirent_test.go index 71b445ba9b533b..68e766e6b01986 100644 --- a/src/syscall/dirent_test.go +++ b/src/syscall/dirent_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package syscall_test @@ -23,7 +22,7 @@ import ( func TestDirent(t *testing.T) { const ( - direntBufSize = 2048 + direntBufSize = 2048 // arbitrary? See https://go.dev/issue/37323. filenameMinSize = 11 ) @@ -38,23 +37,38 @@ func TestDirent(t *testing.T) { } } - buf := bytes.Repeat([]byte("DEADBEAF"), direntBufSize/8) + names := make([]string, 0, 10) + fd, err := syscall.Open(d, syscall.O_RDONLY, 0) if err != nil { t.Fatalf("syscall.open: %v", err) } defer syscall.Close(fd) - n, err := syscall.ReadDirent(fd, buf) - if err != nil { - t.Fatalf("syscall.readdir: %v", err) - } - buf = buf[:n] - names := make([]string, 0, 10) - for len(buf) > 0 { - var bc int - bc, _, names = syscall.ParseDirent(buf, -1, names) - buf = buf[bc:] + buf := bytes.Repeat([]byte{0xCD}, direntBufSize) + for { + n, err := syscall.ReadDirent(fd, buf) + if err == syscall.EINVAL { + // On linux, 'man getdents64' says that EINVAL indicates “result buffer is too small”. + // Try a bigger buffer. + t.Logf("ReadDirent: %v; retrying with larger buffer", err) + buf = bytes.Repeat([]byte{0xCD}, len(buf)*2) + continue + } + if err != nil { + t.Fatalf("syscall.readdir: %v", err) + } + t.Logf("ReadDirent: read %d bytes", n) + if n == 0 { + break + } + + var consumed, count int + consumed, count, names = syscall.ParseDirent(buf[:n], -1, names) + t.Logf("ParseDirent: %d new name(s)", count) + if consumed != n { + t.Fatalf("ParseDirent: consumed %d bytes; expected %d", consumed, n) + } } sort.Strings(names) @@ -83,9 +97,6 @@ func TestDirentRepeat(t *testing.T) { if size < 1024 { size = 1024 // DIRBLKSIZ, see issue 31403. } - if runtime.GOOS == "freebsd" { - t.Skip("need to fix issue 31416 first") - } } // Make a directory containing N files diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go index 16210ca5b58a53..34b481d6e63543 100644 --- a/src/syscall/dll_windows.go +++ b/src/syscall/dll_windows.go @@ -5,7 +5,6 @@ package syscall import ( - "internal/itoa" "internal/syscall/windows/sysdll" "sync" "sync/atomic" @@ -25,12 +24,25 @@ func (e *DLLError) Unwrap() error { return e.Err } // Implemented in ../runtime/syscall_windows.go. +// Deprecated: Use SyscallN instead. func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) + +// Deprecated: Use SyscallN instead. func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +// Deprecated: Use SyscallN instead. func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) + +// Deprecated: Use SyscallN instead. func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno) + +// Deprecated: Use SyscallN instead. func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno) + +// Deprecated: Use SyscallN instead. func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno) + +func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno) func loadlibrary(filename *uint16) (handle uintptr, err Errno) func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno) func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno) @@ -160,8 +172,7 @@ func (p *Proc) Addr() uintptr { //go:uintptrescapes -// Call executes procedure p with arguments a. It will panic if more than 18 arguments -// are supplied. +// Call executes procedure p with arguments a. // // The returned error is always non-nil, constructed from the result of GetLastError. // Callers must inspect the primary return value to decide whether an error occurred @@ -175,49 +186,8 @@ func (p *Proc) Addr() uintptr { // values are returned in r2. The return value for C type "float" is // math.Float32frombits(uint32(r2)). For C type "double", it is // math.Float64frombits(uint64(r2)). -func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { - switch len(a) { - case 0: - return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0) - case 1: - return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0) - case 2: - return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0) - case 3: - return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2]) - case 4: - return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0) - case 5: - return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0) - case 6: - return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5]) - case 7: - return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0) - case 8: - return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0) - case 9: - return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]) - case 10: - return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0) - case 11: - return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0) - case 12: - return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]) - case 13: - return Syscall15(p.Addr(), uintptr(len(a)), 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], 0, 0) - case 14: - return Syscall15(p.Addr(), uintptr(len(a)), 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], 0) - case 15: - return Syscall15(p.Addr(), uintptr(len(a)), 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]) - case 16: - return Syscall18(p.Addr(), uintptr(len(a)), 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], 0, 0) - case 17: - return Syscall18(p.Addr(), uintptr(len(a)), 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], a[16], 0) - case 18: - return Syscall18(p.Addr(), uintptr(len(a)), 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], a[16], a[17]) - default: - panic("Call " + p.Name + " with too many arguments " + itoa.Itoa(len(a)) + ".") - } +func (p *Proc) Call(a ...uintptr) (uintptr, uintptr, error) { + return SyscallN(p.Addr(), a...) } // A LazyDLL implements access to a single DLL. diff --git a/src/syscall/endian_big.go b/src/syscall/endian_big.go index dc0947fa6e84b3..8e3874eb863074 100644 --- a/src/syscall/endian_big.go +++ b/src/syscall/endian_big.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // //go:build ppc64 || s390x || mips || mips64 -// +build ppc64 s390x mips mips64 package syscall diff --git a/src/syscall/endian_little.go b/src/syscall/endian_little.go index a894445f73cb51..f5fcb58db4e4f6 100644 --- a/src/syscall/endian_little.go +++ b/src/syscall/endian_little.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -//go:build 386 || amd64 || arm || arm64 || ppc64le || mips64le || mipsle || riscv64 || wasm -// +build 386 amd64 arm arm64 ppc64le mips64le mipsle riscv64 wasm +//go:build 386 || amd64 || arm || arm64 || loong64 || ppc64le || mips64le || mipsle || riscv64 || wasm package syscall diff --git a/src/syscall/env_unix.go b/src/syscall/env_unix.go index 022ef6ed7342c6..67e6c5fbe2f6e8 100644 --- a/src/syscall/env_unix.go +++ b/src/syscall/env_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || plan9 -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris plan9 +//go:build unix || (js && wasm) || plan9 // Unix environment variables. diff --git a/src/syscall/exec_aix_test.go b/src/syscall/exec_aix_test.go index 17c7ac0664a0a8..f2d54a40bd1969 100644 --- a/src/syscall/exec_aix_test.go +++ b/src/syscall/exec_aix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix -// +build aix package syscall diff --git a/src/syscall/exec_bsd.go b/src/syscall/exec_bsd.go index 709066e809baa5..4762ae751a3430 100644 --- a/src/syscall/exec_bsd.go +++ b/src/syscall/exec_bsd.go @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || netbsd || (openbsd && mips64) -// +build dragonfly freebsd netbsd openbsd,mips64 +//go:build dragonfly || netbsd || (openbsd && mips64) package syscall import ( + "runtime" "unsafe" ) @@ -49,6 +49,7 @@ func runtime_AfterForkInChild() // For the same reason compiler does not race instrument it. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. +// //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any @@ -181,11 +182,18 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Pass 1: look for fd[i] < i and move those up above len(fd) // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { - _, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0) + if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") { + _, _, err1 = RawSyscall(_SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC) + } else { + _, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0) + if err1 != 0 { + goto childerror + } + _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) + } if err1 != 0 { goto childerror } - RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) pipe = nextfd nextfd++ } @@ -194,11 +202,18 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if nextfd == pipe { // don't stomp on pipe nextfd++ } - _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0) + if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") { + _, _, err1 = RawSyscall(_SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC) + } else { + _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0) + if err1 != 0 { + goto childerror + } + _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) + } if err1 != 0 { goto childerror } - RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC) fd[i] = nextfd nextfd++ } diff --git a/src/syscall/exec_freebsd.go b/src/syscall/exec_freebsd.go new file mode 100644 index 00000000000000..851b8fbd06a917 --- /dev/null +++ b/src/syscall/exec_freebsd.go @@ -0,0 +1,298 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +import ( + "runtime" + "unsafe" +) + +type SysProcAttr struct { + Chroot string // Chroot. + Credential *Credential // Credential. + Ptrace bool // Enable tracing. + Setsid bool // Create session. + // Setpgid sets the process group ID of the child to Pgid, + // or, if Pgid == 0, to the new child's process ID. + Setpgid bool + // Setctty sets the controlling terminal of the child to + // file descriptor Ctty. Ctty must be a descriptor number + // in the child process: an index into ProcAttr.Files. + // This is only meaningful if Setsid is true. + Setctty bool + Noctty bool // Detach fd 0 from controlling terminal + Ctty int // Controlling TTY fd + // Foreground places the child process group in the foreground. + // This implies Setpgid. The Ctty field must be set to + // the descriptor of the controlling TTY. + // Unlike Setctty, in this case Ctty must be a descriptor + // number in the parent process. + Foreground bool + Pgid int // Child's process group ID if Setpgid. + Pdeathsig Signal // Signal that the process will get when its parent dies (Linux and FreeBSD only) +} + +const ( + _P_PID = 0 + + _PROC_PDEATHSIG_CTL = 11 +) + +// Implemented in runtime package. +func runtime_BeforeFork() +func runtime_AfterFork() +func runtime_AfterForkInChild() + +// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. +// If a dup or exec fails, write the errno error to pipe. +// (Pipe is close-on-exec so if exec succeeds, it will be closed.) +// In the child, this function must not acquire any locks, because +// they might have been locked at the time of the fork. This means +// no rescheduling, no malloc calls, and no new stack segments. +// For the same reason compiler does not race instrument it. +// The calls to RawSyscall are okay because they are assembly +// functions that do not grow the stack. +// +//go:norace +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { + // Declare all variables at top in case any + // declarations require heap allocation (e.g., err1). + var ( + r1 uintptr + err1 Errno + nextfd int + i int + ) + + // Record parent PID so child can test if it has died. + ppid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + + // guard against side effects of shuffling fds below. + // Make sure that nextfd is beyond any currently open files so + // that we can't run the risk of overwriting any of them. + fd := make([]int, len(attr.Files)) + nextfd = len(attr.Files) + for i, ufd := range attr.Files { + if nextfd < int(ufd) { + nextfd = int(ufd) + } + fd[i] = int(ufd) + } + nextfd++ + + // About to call fork. + // No more allocation or calls of non-assembly functions. + runtime_BeforeFork() + r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0) + if err1 != 0 { + runtime_AfterFork() + return 0, err1 + } + + if r1 != 0 { + // parent; return PID + runtime_AfterFork() + return int(r1), 0 + } + + // Fork succeeded, now in child. + + // Enable tracing if requested. + if sys.Ptrace { + _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0) + if err1 != 0 { + goto childerror + } + } + + // Session ID + if sys.Setsid { + _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0) + if err1 != 0 { + goto childerror + } + } + + // Set process group + if sys.Setpgid || sys.Foreground { + // Place child in process group. + _, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0) + if err1 != 0 { + goto childerror + } + } + + if sys.Foreground { + // This should really be pid_t, however _C_int (aka int32) is + // generally equivalent. + pgrp := _C_int(sys.Pgid) + if pgrp == 0 { + r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0) + if err1 != 0 { + goto childerror + } + + pgrp = _C_int(r1) + } + + // Place process group in foreground. + _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp))) + if err1 != 0 { + goto childerror + } + } + + // Restore the signal mask. We do this after TIOCSPGRP to avoid + // having the kernel send a SIGTTOU signal to the process group. + runtime_AfterForkInChild() + + // Chroot + if chroot != nil { + _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0) + if err1 != 0 { + goto childerror + } + } + + // User and groups + if cred := sys.Credential; cred != nil { + ngroups := uintptr(len(cred.Groups)) + groups := uintptr(0) + if ngroups > 0 { + groups = uintptr(unsafe.Pointer(&cred.Groups[0])) + } + if !cred.NoSetGroups { + _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0) + if err1 != 0 { + goto childerror + } + } + _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0) + if err1 != 0 { + goto childerror + } + _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0) + if err1 != 0 { + goto childerror + } + } + + // Chdir + if dir != nil { + _, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0) + if err1 != 0 { + goto childerror + } + } + + // Parent death signal + if sys.Pdeathsig != 0 { + switch runtime.GOARCH { + case "386", "arm": + _, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 0) + default: + _, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 0, 0) + } + if err1 != 0 { + goto childerror + } + + // Signal self if parent is already dead. This might cause a + // duplicate signal in rare cases, but it won't matter when + // using SIGKILL. + r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0) + if r1 != ppid { + pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0) + if err1 != 0 { + goto childerror + } + } + } + + // Pass 1: look for fd[i] < i and move those up above len(fd) + // so that pass 2 won't stomp on an fd it needs later. + if pipe < nextfd { + _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(pipe), F_DUP2FD_CLOEXEC, uintptr(nextfd)) + if err1 != 0 { + goto childerror + } + pipe = nextfd + nextfd++ + } + for i = 0; i < len(fd); i++ { + if fd[i] >= 0 && fd[i] < int(i) { + if nextfd == pipe { // don't stomp on pipe + nextfd++ + } + _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_DUP2FD_CLOEXEC, uintptr(nextfd)) + if err1 != 0 { + goto childerror + } + fd[i] = nextfd + nextfd++ + } + } + + // Pass 2: dup fd[i] down onto i. + for i = 0; i < len(fd); i++ { + if fd[i] == -1 { + RawSyscall(SYS_CLOSE, uintptr(i), 0, 0) + continue + } + if fd[i] == int(i) { + // dup2(i, i) won't clear close-on-exec flag on Linux, + // probably not elsewhere either. + _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0) + if err1 != 0 { + goto childerror + } + continue + } + // The new fd is created NOT close-on-exec, + // which is exactly what we want. + _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0) + if err1 != 0 { + goto childerror + } + } + + // By convention, we don't close-on-exec the fds we are + // started with, so if len(fd) < 3, close 0, 1, 2 as needed. + // Programs that know they inherit fds >= 3 will need + // to set them close-on-exec. + for i = len(fd); i < 3; i++ { + RawSyscall(SYS_CLOSE, uintptr(i), 0, 0) + } + + // Detach fd 0 from tty + if sys.Noctty { + _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0) + if err1 != 0 { + goto childerror + } + } + + // Set the controlling TTY to Ctty + if sys.Setctty { + _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0) + if err1 != 0 { + goto childerror + } + } + + // Time to exec. + _, _, err1 = RawSyscall(SYS_EXECVE, + uintptr(unsafe.Pointer(argv0)), + uintptr(unsafe.Pointer(&argv[0])), + uintptr(unsafe.Pointer(&envv[0]))) + +childerror: + // send error code on pipe + RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) + for { + RawSyscall(SYS_EXIT, 253, 0, 0) + } +} diff --git a/src/syscall/exec_libc.go b/src/syscall/exec_libc.go index 8a84954051a1dc..72f73a268a05be 100644 --- a/src/syscall/exec_libc.go +++ b/src/syscall/exec_libc.go @@ -3,13 +3,13 @@ // license that can be found in the LICENSE file. //go:build aix || solaris -// +build aix solaris // This file handles forkAndExecInChild function for OS using libc syscall like AIX or Solaris. package syscall import ( + "runtime" "unsafe" ) @@ -43,7 +43,7 @@ func runtime_AfterForkInChild() func chdir(path uintptr) (err Errno) func chroot1(path uintptr) (err Errno) -func close(fd uintptr) (err Errno) +func closeFD(fd uintptr) (err Errno) func dup2child(old uintptr, new uintptr) (val uintptr, err Errno) func execve(path uintptr, argv uintptr, envp uintptr) (err Errno) func exit(code uintptr) @@ -75,6 +75,7 @@ func init() { // because we need to avoid lazy-loading the functions (might malloc, // split the stack, or acquire mutexes). We can't call RawSyscall // because it's not safe even for BSD-subsystem calls. +// //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any @@ -197,11 +198,19 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Pass 1: look for fd[i] < i and move those up above len(fd) // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { - _, err1 = dup2child(uintptr(pipe), uintptr(nextfd)) + switch runtime.GOOS { + case "illumos": + _, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd)) + default: + _, err1 = dup2child(uintptr(pipe), uintptr(nextfd)) + if err1 != 0 { + goto childerror + } + _, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) + } if err1 != 0 { goto childerror } - fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) pipe = nextfd nextfd++ } @@ -210,11 +219,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if nextfd == pipe { // don't stomp on pipe nextfd++ } - _, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd)) - if err1 != 0 { - goto childerror + switch runtime.GOOS { + case "illumos": + _, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd)) + default: + _, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd)) + if err1 != 0 { + goto childerror + } + _, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) } - _, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) if err1 != 0 { goto childerror } @@ -226,7 +240,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Pass 2: dup fd[i] down onto i. for i = 0; i < len(fd); i++ { if fd[i] == -1 { - close(uintptr(i)) + closeFD(uintptr(i)) continue } if fd[i] == int(i) { @@ -251,7 +265,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Programs that know they inherit fds >= 3 will need // to set them close-on-exec. for i = len(fd); i < 3; i++ { - close(uintptr(i)) + closeFD(uintptr(i)) } // Detach fd 0 from tty diff --git a/src/syscall/exec_libc2.go b/src/syscall/exec_libc2.go index b999754c2e81bf..9eb61a5d35168d 100644 --- a/src/syscall/exec_libc2.go +++ b/src/syscall/exec_libc2.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build darwin || (openbsd && !mips64) -// +build darwin openbsd,!mips64 package syscall import ( "internal/abi" + "runtime" "unsafe" ) @@ -50,6 +50,7 @@ func runtime_AfterForkInChild() // For the same reason compiler does not race instrument it. // The calls to rawSyscall are okay because they are assembly // functions that do not grow the stack. +// //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any @@ -117,14 +118,15 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } if sys.Foreground { - pgrp := sys.Pgid + // This should really be pid_t, however _C_int (aka int32) is + // generally equivalent. + pgrp := _C_int(sys.Pgid) if pgrp == 0 { r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_getpid_trampoline), 0, 0, 0) if err1 != 0 { goto childerror } - - pgrp = int(r1) + pgrp = _C_int(r1) } // Place process group in foreground. @@ -180,11 +182,18 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Pass 1: look for fd[i] < i and move those up above len(fd) // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { - _, _, err1 = rawSyscall(abi.FuncPCABI0(libc_dup2_trampoline), uintptr(pipe), uintptr(nextfd), 0) + if runtime.GOOS == "openbsd" { + _, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), O_CLOEXEC) + } else { + _, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), 0) + if err1 != 0 { + goto childerror + } + _, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC) + } if err1 != 0 { goto childerror } - rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC) pipe = nextfd nextfd++ } @@ -193,11 +202,18 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if nextfd == pipe { // don't stomp on pipe nextfd++ } - _, _, err1 = rawSyscall(abi.FuncPCABI0(libc_dup2_trampoline), uintptr(fd[i]), uintptr(nextfd), 0) + if runtime.GOOS == "openbsd" { + _, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC) + } else { + _, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), 0) + if err1 != 0 { + goto childerror + } + _, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC) + } if err1 != 0 { goto childerror } - rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC) fd[i] = nextfd nextfd++ } diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index ccc0e39e30bda2..72b56f484ac07f 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package syscall @@ -13,6 +12,45 @@ import ( "unsafe" ) +// Linux unshare/clone/clone2/clone3 flags, architecture-independent, +// copied from linux/sched.h. +const ( + CLONE_VM = 0x00000100 // set if VM shared between processes + CLONE_FS = 0x00000200 // set if fs info shared between processes + CLONE_FILES = 0x00000400 // set if open files shared between processes + CLONE_SIGHAND = 0x00000800 // set if signal handlers and blocked signals shared + CLONE_PIDFD = 0x00001000 // set if a pidfd should be placed in parent + CLONE_PTRACE = 0x00002000 // set if we want to let tracing continue on the child too + CLONE_VFORK = 0x00004000 // set if the parent wants the child to wake it up on mm_release + CLONE_PARENT = 0x00008000 // set if we want to have the same parent as the cloner + CLONE_THREAD = 0x00010000 // Same thread group? + CLONE_NEWNS = 0x00020000 // New mount namespace group + CLONE_SYSVSEM = 0x00040000 // share system V SEM_UNDO semantics + CLONE_SETTLS = 0x00080000 // create a new TLS for the child + CLONE_PARENT_SETTID = 0x00100000 // set the TID in the parent + CLONE_CHILD_CLEARTID = 0x00200000 // clear the TID in the child + CLONE_DETACHED = 0x00400000 // Unused, ignored + CLONE_UNTRACED = 0x00800000 // set if the tracing process can't force CLONE_PTRACE on this clone + CLONE_CHILD_SETTID = 0x01000000 // set the TID in the child + CLONE_NEWCGROUP = 0x02000000 // New cgroup namespace + CLONE_NEWUTS = 0x04000000 // New utsname namespace + CLONE_NEWIPC = 0x08000000 // New ipc namespace + CLONE_NEWUSER = 0x10000000 // New user namespace + CLONE_NEWPID = 0x20000000 // New pid namespace + CLONE_NEWNET = 0x40000000 // New network namespace + CLONE_IO = 0x80000000 // Clone io context + + // Flags for the clone3() syscall. + + CLONE_CLEAR_SIGHAND = 0x100000000 // Clear any signal handler and reset to SIG_DFL. + CLONE_INTO_CGROUP = 0x200000000 // Clone into a specific cgroup given the right permissions. + + // Cloning flags intersect with CSIGNAL so can be used with unshare and clone3 + // syscalls only: + + CLONE_NEWTIME = 0x00000080 // New time namespace +) + // SysProcIDMap holds Container ID to Host ID mappings used for User Namespaces in Linux. // See user_namespaces(7). type SysProcIDMap struct { @@ -44,9 +82,13 @@ type SysProcAttr struct { // the descriptor of the controlling TTY. // Unlike Setctty, in this case Ctty must be a descriptor // number in the parent process. - Foreground bool - Pgid int // Child's process group ID if Setpgid. - Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) + Foreground bool + Pgid int // Child's process group ID if Setpgid. + // Pdeathsig, if non-zero, is a signal that the kernel will send to + // the child process when the creating thread dies. Note that the signal + // is sent on thread termination, which may happen before process termination. + // There are more details at https://go.dev/issue/27505. + Pdeathsig Signal Cloneflags uintptr // Flags for clone calls (Linux only) Unshareflags uintptr // Flags for unshare calls (Linux only) UidMappings []SysProcIDMap // User ID mappings for user namespaces. @@ -57,6 +99,8 @@ type SysProcAttr struct { // users this should be set to false for mappings work. GidMappingsEnableSetgroups bool AmbientCaps []uintptr // Ambient capabilities (Linux only) + UseCgroupFD bool // Whether to make use of the CgroupFD field. + CgroupFD int // File descriptor of a cgroup to put the new process into. } var ( @@ -78,6 +122,7 @@ func runtime_AfterForkInChild() // For the same reason compiler does not race instrument it. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. +// //go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Set up and fork. This returns immediately in the parent or @@ -133,6 +178,21 @@ func capToIndex(cap uintptr) uintptr { return cap >> 5 } // See CAP_TO_MASK in linux/capability.h: func capToMask(cap uintptr) uint32 { return 1 << uint(cap&31) } +// cloneArgs holds arguments for clone3 Linux syscall. +type cloneArgs struct { + flags uint64 // Flags bit mask + pidFD uint64 // Where to store PID file descriptor (int *) + childTID uint64 // Where to store child TID, in child's memory (pid_t *) + parentTID uint64 // Where to store child TID, in parent's memory (pid_t *) + exitSignal uint64 // Signal to deliver to parent on child termination + stack uint64 // Pointer to lowest byte of stack + stackSize uint64 // Size of stack + tls uint64 // Location of new TLS + setTID uint64 // Pointer to a pid_t array (since Linux 5.5) + setTIDSize uint64 // Number of elements in set_tid (since Linux 5.5) + cgroup uint64 // File descriptor for target cgroup of child (since Linux 5.7) +} + // forkAndExecInChild1 implements the body of forkAndExecInChild up to // the parent's post-fork path. This is a separate function so we can // separate the child's and parent's stack frames if we're using @@ -162,9 +222,10 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att nextfd int i int caps caps - fd1 uintptr + fd1, flags uintptr puid, psetgroups, pgid []byte uidmap, setgroups, gidmap []byte + clone3 *cloneArgs ) if sys.UidMappings != nil { @@ -209,17 +270,33 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att } } + flags = sys.Cloneflags + if sys.Cloneflags&CLONE_NEWUSER == 0 && sys.Unshareflags&CLONE_NEWUSER == 0 { + flags |= CLONE_VFORK | CLONE_VM + } + // Whether to use clone3. + if sys.UseCgroupFD { + clone3 = &cloneArgs{ + flags: uint64(flags) | CLONE_INTO_CGROUP, + exitSignal: uint64(SIGCHLD), + cgroup: uint64(sys.CgroupFD), + } + } + // About to call fork. // No more allocation or calls of non-assembly functions. runtime_BeforeFork() locked = true - switch { - case sys.Cloneflags&CLONE_NEWUSER == 0 && sys.Unshareflags&CLONE_NEWUSER == 0: - r1, err1 = rawVforkSyscall(SYS_CLONE, uintptr(SIGCHLD|CLONE_VFORK|CLONE_VM)|sys.Cloneflags) - case runtime.GOARCH == "s390x": - r1, _, err1 = RawSyscall6(SYS_CLONE, 0, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0) - default: - r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0) + if clone3 != nil { + r1, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3)) + } else { + flags |= uintptr(SIGCHLD) + if runtime.GOARCH == "s390x" { + // On Linux/s390, the first two arguments of clone(2) are swapped. + r1, err1 = rawVforkSyscall(SYS_CLONE, 0, flags) + } else { + r1, err1 = rawVforkSyscall(SYS_CLONE, flags, 0) + } } if err1 != 0 || r1 != 0 { // If we're in the parent, we must return immediately @@ -448,13 +525,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { _, _, err1 = RawSyscall(SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC) - if _SYS_dup != SYS_DUP3 && err1 == ENOSYS { - _, _, err1 = RawSyscall(_SYS_dup, uintptr(pipe), uintptr(nextfd), 0) - if err1 != 0 { - goto childerror - } - RawSyscall(fcntl64Syscall, uintptr(nextfd), F_SETFD, FD_CLOEXEC) - } else if err1 != 0 { + if err1 != 0 { goto childerror } pipe = nextfd @@ -466,13 +537,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att nextfd++ } _, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC) - if _SYS_dup != SYS_DUP3 && err1 == ENOSYS { - _, _, err1 = RawSyscall(_SYS_dup, uintptr(fd[i]), uintptr(nextfd), 0) - if err1 != 0 { - goto childerror - } - RawSyscall(fcntl64Syscall, uintptr(nextfd), F_SETFD, FD_CLOEXEC) - } else if err1 != 0 { + if err1 != 0 { goto childerror } fd[i] = nextfd @@ -497,7 +562,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att } // The new fd is created NOT close-on-exec, // which is exactly what we want. - _, _, err1 = RawSyscall(_SYS_dup, uintptr(fd[i]), uintptr(i), 0) + _, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(i), 0) if err1 != 0 { goto childerror } @@ -553,25 +618,13 @@ childerror: // Try to open a pipe with O_CLOEXEC set on both file descriptors. func forkExecPipe(p []int) (err error) { - err = Pipe2(p, O_CLOEXEC) - // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it - // might not be implemented. - if err == ENOSYS { - if err = Pipe(p); err != nil { - return - } - if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { - return - } - _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC) - } - return + return Pipe2(p, O_CLOEXEC) } func formatIDMappings(idMap []SysProcIDMap) []byte { var data []byte for _, im := range idMap { - data = append(data, []byte(itoa.Itoa(im.ContainerID)+" "+itoa.Itoa(im.HostID)+" "+itoa.Itoa(im.Size)+"\n")...) + data = append(data, itoa.Itoa(im.ContainerID)+" "+itoa.Itoa(im.HostID)+" "+itoa.Itoa(im.Size)+"\n"...) } return data } diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index 85b59ad00d9465..a035d415ed539a 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package syscall_test import ( + "bytes" "flag" "fmt" "internal/testenv" @@ -15,6 +15,7 @@ import ( "os" "os/exec" "os/user" + "path" "path/filepath" "runtime" "strconv" @@ -66,6 +67,10 @@ func skipUnprivilegedUserClone(t *testing.T) { // Skip the test if the sysctl that prevents unprivileged user // from creating user namespaces is enabled. data, errRead := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone") + if os.IsNotExist(errRead) { + // This file is only available in some Debian/Ubuntu kernels. + return + } if errRead != nil || len(data) < 1 || data[0] == '0' { t.Skip("kernel prohibits user namespace in unprivileged process") } @@ -111,14 +116,6 @@ func checkUserNS(t *testing.T) { t.Skip("kernel doesn't support user namespaces") } } - - // 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") - } } func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { @@ -201,14 +198,6 @@ func TestUnshare(t *testing.T) { 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") - } - path := "/proc/net/dev" if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { @@ -275,12 +264,14 @@ func TestGroupCleanup(t *testing.T) { t.Fatalf("Cmd failed with err %v, output: %s", err, out) } strOut := strings.TrimSpace(string(out)) + t.Logf("id: %s", strOut) + expected := "uid=0(root) gid=0(root)" // Just check prefix because some distros reportedly output a // context parameter; see https://golang.org/issue/16224. // Alpine does not output groups; see https://golang.org/issue/19938. if !strings.HasPrefix(strOut, expected) { - t.Errorf("id command output: %q, expected prefix: %q", strOut, expected) + t.Errorf("expected prefix: %q", expected) } } @@ -309,23 +300,14 @@ func TestGroupCleanupUserNamespace(t *testing.T) { t.Fatalf("Cmd failed with err %v, output: %s", err, out) } strOut := strings.TrimSpace(string(out)) + t.Logf("id: %s", strOut) - // Strings we've seen in the wild. - expected := []string{ - "uid=0(root) gid=0(root) groups=0(root)", - "uid=0(root) gid=0(root) groups=0(root),65534(nobody)", - "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)", - "uid=0(root) gid=0(root) groups=0(root),65534", - "uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)", // Alpine; see https://golang.org/issue/19938 - "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023", // CentOS with SELinux context, see https://golang.org/issue/34547 - "uid=0(root) gid=0(root) groups=0(root),65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023", // Fedora with SElinux context, see https://golang.org/issue/46752 - } - for _, e := range expected { - if strOut == e { - return - } + // As in TestGroupCleanup, just check prefix. + // The actual groups and contexts seem to vary from one distro to the next. + expected := "uid=0(root) gid=0(root) groups=0(root)" + if !strings.HasPrefix(strOut, expected) { + t.Errorf("expected prefix: %q", expected) } - t.Errorf("id command output: %q, expected one of %q", strOut, expected) } // TestUnshareHelperProcess isn't a real test. It's used as a helper process @@ -481,6 +463,96 @@ func TestUnshareUidGidMapping(t *testing.T) { } } +func prepareCgroupFD(t *testing.T) (int, string) { + t.Helper() + + const O_PATH = 0x200000 // Same for all architectures, but for some reason not defined in syscall for 386||amd64. + + // Requires cgroup v2. + const prefix = "/sys/fs/cgroup" + selfCg, err := os.ReadFile("/proc/self/cgroup") + if err != nil { + if os.IsNotExist(err) || os.IsPermission(err) { + t.Skip(err) + } + t.Fatal(err) + } + + // Expect a single line like this: + // 0::/user.slice/user-1000.slice/user@1000.service/app.slice/vte-spawn-891992a2-efbb-4f28-aedb-b24f9e706770.scope + // Otherwise it's either cgroup v1 or a hybrid hierarchy. + if bytes.Count(selfCg, []byte("\n")) > 1 { + t.Skip("cgroup v2 not available") + } + cg := bytes.TrimPrefix(selfCg, []byte("0::")) + if len(cg) == len(selfCg) { // No prefix found. + t.Skipf("cgroup v2 not available (/proc/self/cgroup contents: %q)", selfCg) + } + + // Need clone3 with CLONE_INTO_CGROUP support. + _, err = syscall.ForkExec("non-existent binary", nil, &syscall.ProcAttr{ + Sys: &syscall.SysProcAttr{ + UseCgroupFD: true, + CgroupFD: -1, + }, + }) + // // EPERM can be returned if clone3 is not enabled by seccomp. + if err == syscall.ENOSYS || err == syscall.EPERM { + t.Skipf("clone3 with CLONE_INTO_CGROUP not available: %v", err) + } + + // Need an ability to create a sub-cgroup. + subCgroup, err := os.MkdirTemp(prefix+string(bytes.TrimSpace(cg)), "subcg-") + if err != nil { + if os.IsPermission(err) { + t.Skip(err) + } + t.Fatal(err) + } + t.Cleanup(func() { syscall.Rmdir(subCgroup) }) + + cgroupFD, err := syscall.Open(subCgroup, O_PATH, 0) + if err != nil { + t.Fatal(&os.PathError{Op: "open", Path: subCgroup, Err: err}) + } + t.Cleanup(func() { syscall.Close(cgroupFD) }) + + return cgroupFD, "/" + path.Base(subCgroup) +} + +func TestUseCgroupFD(t *testing.T) { + fd, suffix := prepareCgroupFD(t) + + cmd := exec.Command(os.Args[0], "-test.run=TestUseCgroupFDHelper") + cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") + cmd.SysProcAttr = &syscall.SysProcAttr{ + UseCgroupFD: true, + CgroupFD: fd, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + // NB: this wouldn't work with cgroupns. + if !bytes.HasSuffix(bytes.TrimSpace(out), []byte(suffix)) { + t.Fatalf("got: %q, want: a line that ends with %q", out, suffix) + } +} + +func TestUseCgroupFDHelper(*testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + defer os.Exit(0) + // Read and print own cgroup path. + selfCg, err := os.ReadFile("/proc/self/cgroup") + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + fmt.Print(string(selfCg)) +} + type capHeader struct { version uint32 pid int32 @@ -526,9 +598,7 @@ func mustSupportAmbientCaps(t *testing.T) { buf[i] = byte(b) } ver := string(buf[:]) - if i := strings.Index(ver, "\x00"); i != -1 { - ver = ver[:i] - } + ver, _, _ = strings.Cut(ver, "\x00") if strings.HasPrefix(ver, "2.") || strings.HasPrefix(ver, "3.") || strings.HasPrefix(ver, "4.1.") || diff --git a/src/syscall/exec_pdeathsig_test.go b/src/syscall/exec_pdeathsig_test.go new file mode 100644 index 00000000000000..96ae27b494ec0e --- /dev/null +++ b/src/syscall/exec_pdeathsig_test.go @@ -0,0 +1,134 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build freebsd || linux + +package syscall_test + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "testing" + "time" +) + +func TestDeathSignal(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + + // Copy the test binary to a location that a non-root user can read/execute + // after we drop privileges + tempDir, err := os.MkdirTemp("", "TestDeathSignal") + if err != nil { + t.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(tempDir) + os.Chmod(tempDir, 0755) + + tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) + + src, err := os.Open(os.Args[0]) + if err != nil { + t.Fatalf("cannot open binary %q, %v", os.Args[0], err) + } + defer src.Close() + + dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) + } + if _, err := io.Copy(dst, src); err != nil { + t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) + } + err = dst.Close() + if err != nil { + t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = append(os.Environ(), "GO_DEATHSIG_PARENT=1") + chldStdin, err := cmd.StdinPipe() + if err != nil { + t.Fatalf("failed to create new stdin pipe: %v", err) + } + chldStdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("failed to create new stdout pipe: %v", err) + } + cmd.Stderr = os.Stderr + + err = cmd.Start() + defer cmd.Wait() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + chldPipe := bufio.NewReader(chldStdout) + + if got, err := chldPipe.ReadString('\n'); got == "start\n" { + syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) + + go func() { + time.Sleep(5 * time.Second) + chldStdin.Close() + }() + + want := "ok\n" + if got, err = chldPipe.ReadString('\n'); got != want { + t.Fatalf("expected %q, received %q, %v", want, got, err) + } + } else { + t.Fatalf("did not receive start from child, received %q, %v", got, err) + } +} + +func deathSignalParent() { + cmd := exec.Command(os.Args[0]) + cmd.Env = append(os.Environ(), + "GO_DEATHSIG_PARENT=", + "GO_DEATHSIG_CHILD=1", + ) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + attrs := syscall.SysProcAttr{ + Pdeathsig: syscall.SIGUSR1, + // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is + // unused on Ubuntu + Credential: &syscall.Credential{Uid: 99, Gid: 99}, + } + cmd.SysProcAttr = &attrs + + err := cmd.Start() + if err != nil { + fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err) + os.Exit(1) + } + cmd.Wait() + os.Exit(0) +} + +func deathSignalChild() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGUSR1) + go func() { + <-c + fmt.Println("ok") + os.Exit(0) + }() + fmt.Println("start") + + buf := make([]byte, 32) + os.Stdin.Read(buf) + + // We expected to be signaled before stdin closed + fmt.Println("not ok") + os.Exit(1) +} diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go index c469fe1812632a..6680e6f2ef91c3 100644 --- a/src/syscall/exec_plan9.go +++ b/src/syscall/exec_plan9.go @@ -19,6 +19,7 @@ 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 { @@ -37,6 +38,7 @@ 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 { @@ -119,6 +121,7 @@ var dupdev, _ = BytePtrFromString("#d") // no rescheduling, no malloc calls, and no new stack segments. // 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, pipe int, rflag int) (pid int, err error) { // Declare all variables at top in case any @@ -302,6 +305,7 @@ childerror1: } // 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 { diff --git a/src/syscall/exec_solaris_test.go b/src/syscall/exec_solaris_test.go index f54fc8385d5920..90e5349bf458db 100644 --- a/src/syscall/exec_solaris_test.go +++ b/src/syscall/exec_solaris_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build solaris -// +build solaris package syscall diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go index 54b18dccd73e26..286be454d88f02 100644 --- a/src/syscall/exec_unix.go +++ b/src/syscall/exec_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix // Fork, exec, wait, etc. @@ -153,9 +152,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) sys = &zeroSysProcAttr } - p[0] = -1 - p[1] = -1 - // Convert args to C form. argv0p, err := BytePtrFromString(argv0) if err != nil { @@ -205,14 +201,17 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) // Allocate child status pipe close on exec. if err = forkExecPipe(p[:]); err != nil { - goto error + ForkLock.Unlock() + return 0, err } // Kick off child. pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) if err1 != 0 { - err = Errno(err1) - goto error + Close(p[0]) + Close(p[1]) + ForkLock.Unlock() + return 0, Errno(err1) } ForkLock.Unlock() @@ -244,14 +243,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) // Read got EOF, so pipe closed on exec, so exec succeeded. return pid, nil - -error: - if p[0] >= 0 { - Close(p[0]) - Close(p[1]) - } - ForkLock.Unlock() - return 0, err } // Combination of fork and exec, careful to be thread safe. diff --git a/src/syscall/exec_unix_test.go b/src/syscall/exec_unix_test.go index 866671ba2aca75..4253cda5cb6c7d 100644 --- a/src/syscall/exec_unix_test.go +++ b/src/syscall/exec_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package syscall_test @@ -14,7 +13,6 @@ import ( "os" "os/exec" "os/signal" - "runtime" "syscall" "testing" "time" @@ -328,7 +326,6 @@ func TestExecHelper(t *testing.T) { // We don't have to worry about restoring these values. // We are in a child process that only runs this test, // and we are going to call syscall.Exec anyhow. - runtime.GOMAXPROCS(50) os.Setenv("GO_WANT_HELPER_PROCESS", "3") stop := time.Now().Add(time.Second) diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go index 18d15028c3d3bb..92464e089c5e84 100644 --- a/src/syscall/exec_windows.go +++ b/src/syscall/exec_windows.go @@ -19,11 +19,11 @@ var ForkLock sync.RWMutex // in https://msdn.microsoft.com/en-us/library/ms880421. // This function returns "" (2 double quotes) if s is empty. // Alternatively, these transformations are done: -// - every back slash (\) is doubled, but only if immediately -// followed by double quote ("); -// - every double quote (") is escaped by back slash (\); -// - finally, s is wrapped with double quotes (arg -> "arg"), -// but only if there is space or tab inside s. +// - every back slash (\) is doubled, but only if immediately +// followed by double quote ("); +// - every double quote (") is escaped by back slash (\); +// - finally, s is wrapped with double quotes (arg -> "arg"), +// but only if there is space or tab inside s. func EscapeArg(s string) string { if len(s) == 0 { return `""` @@ -390,8 +390,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle } fd = fd[:j] + willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles + // Do not accidentally inherit more than these handles. - if len(fd) > 0 { + if willInheritHandles { err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil) if err != nil { return 0, 0, err @@ -401,9 +403,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle pi := new(ProcessInformation) flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT if sys.Token != 0 { - err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } else { - err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } if err != nil { return 0, 0, err diff --git a/src/syscall/export_unix_test.go b/src/syscall/export_unix_test.go index 2d2c67673d7887..2f678d25664bc3 100644 --- a/src/syscall/export_unix_test.go +++ b/src/syscall/export_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || linux || netbsd || openbsd -// +build dragonfly freebsd linux netbsd openbsd package syscall diff --git a/src/syscall/flock.go b/src/syscall/flock.go index 3b43b6aeded16e..8cb8f16153a9be 100644 --- a/src/syscall/flock.go +++ b/src/syscall/flock.go @@ -1,5 +1,4 @@ //go:build linux || freebsd || openbsd || netbsd || dragonfly -// +build linux freebsd openbsd netbsd dragonfly // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/src/syscall/flock_linux_32bit.go b/src/syscall/flock_linux_32bit.go index 2f3277497cd577..76a09fc47e3fa8 100644 --- a/src/syscall/flock_linux_32bit.go +++ b/src/syscall/flock_linux_32bit.go @@ -6,7 +6,6 @@ // internal/syscall/unix/fcntl_linux_32bit.go. //go:build (linux && 386) || (linux && arm) || (linux && mips) || (linux && mipsle) -// +build linux,386 linux,arm linux,mips linux,mipsle package syscall diff --git a/src/syscall/forkpipe.go b/src/syscall/forkpipe.go index 79cbdf4150f535..5082abc41cf140 100644 --- a/src/syscall/forkpipe.go +++ b/src/syscall/forkpipe.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || solaris -// +build aix darwin solaris +//go:build aix || darwin package syscall diff --git a/src/syscall/forkpipe2.go b/src/syscall/forkpipe2.go index e57240c156157a..6ab1391c1275fe 100644 --- a/src/syscall/forkpipe2.go +++ b/src/syscall/forkpipe2.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || netbsd || openbsd -// +build dragonfly freebsd netbsd openbsd +//go:build dragonfly || freebsd || netbsd || openbsd || solaris package syscall diff --git a/src/syscall/fs_js.go b/src/syscall/fs_js.go index 0170516201473f..3541446b0bd31a 100644 --- a/src/syscall/fs_js.go +++ b/src/syscall/fs_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package syscall @@ -492,14 +491,14 @@ func Pipe(fd []int) error { return ENOSYS } -func fsCall(name string, args ...interface{}) (js.Value, error) { +func fsCall(name string, args ...any) (js.Value, error) { type callResult struct { val js.Value err error } c := make(chan callResult, 1) - f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + f := js.FuncOf(func(this js.Value, args []js.Value) any { var res callResult if len(args) >= 1 { // on Node.js 8, fs.utimes calls the callback without any arguments diff --git a/src/syscall/getdirentries_test.go b/src/syscall/getdirentries_test.go index 814e656649b8ed..ddd8208c154837 100644 --- a/src/syscall/getdirentries_test.go +++ b/src/syscall/getdirentries_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || netbsd || openbsd -// +build darwin freebsd netbsd openbsd package syscall_test diff --git a/src/syscall/js/export_test.go b/src/syscall/js/export_test.go index 1b5ed3ce8491c2..fb61daea7cea00 100644 --- a/src/syscall/js/export_test.go +++ b/src/syscall/js/export_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. -// +build js,wasm +//go:build js && wasm package js diff --git a/src/syscall/js/func.go b/src/syscall/js/func.go index da4cf68774ddcd..cc9497236450bb 100644 --- a/src/syscall/js/func.go +++ b/src/syscall/js/func.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 js,wasm +//go:build js && wasm package js @@ -10,12 +10,10 @@ import "sync" var ( funcsMu sync.Mutex - funcs = make(map[uint32]func(Value, []Value) interface{}) + funcs = make(map[uint32]func(Value, []Value) any) nextFuncID uint32 = 1 ) -var _ Wrapper = Func{} // Func must implement Wrapper - // Func is a wrapped Go function to be called by JavaScript. type Func struct { Value // the JavaScript function that invokes the Go function @@ -40,7 +38,7 @@ type Func struct { // new goroutine. // // Func.Release must be called to free up resources when the function will not be invoked any more. -func FuncOf(fn func(this Value, args []Value) interface{}) Func { +func FuncOf(fn func(this Value, args []Value) any) Func { funcsMu.Lock() id := nextFuncID nextFuncID++ diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index a48bbd4dd794b7..2f4f5adda02738 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.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 js,wasm +//go:build js && wasm // Package js gives access to the WebAssembly host environment when using the js/wasm architecture. // Its API is based on JavaScript semantics. @@ -27,12 +27,6 @@ type ref uint64 // nanHead are the upper 32 bits of a ref which are set if the value is not encoded as an IEEE 754 number (see above). const nanHead = 0x7FF80000 -// Wrapper is implemented by types that are backed by a JavaScript value. -type Wrapper interface { - // JSValue returns a JavaScript value associated with an object. - JSValue() Value -} - // Value represents a JavaScript value. The zero value is the JavaScript value "undefined". // Values can be checked for equality with the Equal method. type Value struct { @@ -50,11 +44,6 @@ const ( typeFlagFunction ) -// JSValue implements Wrapper interface. -func (v Value) JSValue() Value { - return v -} - func makeValue(r ref) Value { var gcPtr *ref typeFlag := (r >> 32) & 7 @@ -147,24 +136,24 @@ func Global() Value { // ValueOf returns x as a JavaScript value: // -// | Go | JavaScript | -// | ---------------------- | ---------------------- | -// | js.Value | [its value] | -// | js.Func | function | -// | nil | null | -// | bool | boolean | -// | integers and floats | number | -// | string | string | -// | []interface{} | new array | -// | map[string]interface{} | new object | +// | Go | JavaScript | +// | ---------------------- | ---------------------- | +// | js.Value | [its value] | +// | js.Func | function | +// | nil | null | +// | bool | boolean | +// | integers and floats | number | +// | string | string | +// | []interface{} | new array | +// | map[string]interface{} | new object | // // Panics if x is not one of the expected types. -func ValueOf(x interface{}) Value { +func ValueOf(x any) Value { switch x := x.(type) { - case Value: // should precede Wrapper to avoid a loop + case Value: return x - case Wrapper: - return x.JSValue() + case Func: + return x.Value case nil: return valueNull case bool: @@ -203,13 +192,13 @@ func ValueOf(x interface{}) Value { return floatValue(x) case string: return makeValue(stringVal(x)) - case []interface{}: + case []any: a := arrayConstructor.New(len(x)) for i, s := range x { a.SetIndex(i, s) } return a - case map[string]interface{}: + case map[string]any: o := objectConstructor.New() for k, v := range x { o.Set(k, v) @@ -307,7 +296,7 @@ func valueGet(v ref, p string) ref // Set sets the JavaScript property p of value v to ValueOf(x). // It panics if v is not a JavaScript object. -func (v Value) Set(p string, x interface{}) { +func (v Value) Set(p string, x any) { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.Set", vType}) } @@ -346,7 +335,7 @@ func valueIndex(v ref, i int) ref // SetIndex sets the JavaScript index i of value v to ValueOf(x). // It panics if v is not a JavaScript object. -func (v Value) SetIndex(i int, x interface{}) { +func (v Value) SetIndex(i int, x any) { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.SetIndex", vType}) } @@ -358,7 +347,7 @@ func (v Value) SetIndex(i int, x interface{}) { func valueSetIndex(v ref, i int, x ref) -func makeArgs(args []interface{}) ([]Value, []ref) { +func makeArgs(args []any) ([]Value, []ref) { argVals := make([]Value, len(args)) argRefs := make([]ref, len(args)) for i, arg := range args { @@ -385,7 +374,7 @@ func valueLength(v ref) int // Call does a JavaScript call to the method m of value v with the given arguments. // It panics if v has no method m. // The arguments get mapped to JavaScript values according to the ValueOf function. -func (v Value) Call(m string, args ...interface{}) Value { +func (v Value) Call(m string, args ...any) Value { argVals, argRefs := makeArgs(args) res, ok := valueCall(v.ref, m, argRefs) runtime.KeepAlive(v) @@ -407,7 +396,7 @@ func valueCall(v ref, m string, args []ref) (ref, bool) // Invoke does a JavaScript call of the value v with the given arguments. // It panics if v is not a JavaScript function. // The arguments get mapped to JavaScript values according to the ValueOf function. -func (v Value) Invoke(args ...interface{}) Value { +func (v Value) Invoke(args ...any) Value { argVals, argRefs := makeArgs(args) res, ok := valueInvoke(v.ref, argRefs) runtime.KeepAlive(v) @@ -426,7 +415,7 @@ func valueInvoke(v ref, args []ref) (ref, bool) // New uses JavaScript's "new" operator with value v as constructor and the given arguments. // It panics if v is not a JavaScript function. // The arguments get mapped to JavaScript values according to the ValueOf function. -func (v Value) New(args ...interface{}) Value { +func (v Value) New(args ...any) Value { argVals, argRefs := makeArgs(args) res, ok := valueNew(v.ref, argRefs) runtime.KeepAlive(v) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 5fc9107d40250c..f860a5bb50581b 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_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. -// +build js,wasm +//go:build js && wasm // To run these tests: // @@ -364,8 +364,8 @@ func TestType(t *testing.T) { } } -type object = map[string]interface{} -type array = []interface{} +type object = map[string]any +type array = []any func TestValueOf(t *testing.T) { a := js.ValueOf(array{0, array{0, 42, 0}, 0}) @@ -388,7 +388,7 @@ func TestZeroValue(t *testing.T) { func TestFuncOf(t *testing.T) { c := make(chan struct{}) - cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + cb := js.FuncOf(func(this js.Value, args []js.Value) any { if got := args[0].Int(); got != 42 { t.Errorf("got %#v, want %#v", got, 42) } @@ -402,8 +402,8 @@ func TestFuncOf(t *testing.T) { func TestInvokeFunction(t *testing.T) { called := false - cb := js.FuncOf(func(this js.Value, args []js.Value) interface{} { - cb2 := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + cb := js.FuncOf(func(this js.Value, args []js.Value) any { + cb2 := js.FuncOf(func(this js.Value, args []js.Value) any { called = true return 42 }) @@ -423,7 +423,7 @@ func TestInterleavedFunctions(t *testing.T) { c1 := make(chan struct{}) c2 := make(chan struct{}) - js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} { + js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) any { c1 <- struct{}{} <-c2 return nil @@ -432,7 +432,7 @@ func TestInterleavedFunctions(t *testing.T) { <-c1 c2 <- struct{}{} // this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now - f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + f := js.FuncOf(func(this js.Value, args []js.Value) any { return nil }) f.Invoke() @@ -440,7 +440,7 @@ func TestInterleavedFunctions(t *testing.T) { func ExampleFuncOf() { var cb js.Func - cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + cb = js.FuncOf(func(this js.Value, args []js.Value) any { fmt.Println("button clicked") cb.Release() // release the function if the button will not be clicked again return nil @@ -593,7 +593,7 @@ func BenchmarkDOM(b *testing.B) { } func TestGlobal(t *testing.T) { - ident := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + ident := js.FuncOf(func(this js.Value, args []js.Value) any { return args[0] }) defer ident.Release() diff --git a/src/syscall/lsf_linux.go b/src/syscall/lsf_linux.go index 28e96d54e6a195..838acc5fb1944e 100644 --- a/src/syscall/lsf_linux.go +++ b/src/syscall/lsf_linux.go @@ -48,7 +48,7 @@ type iflags struct { // Deprecated: Use golang.org/x/net/bpf instead. func SetLsfPromisc(name string, m bool) error { - s, e := cloexecSocket(AF_INET, SOCK_DGRAM, 0) + s, e := Socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0) if e != nil { return e } diff --git a/src/syscall/mkall.sh b/src/syscall/mkall.sh index dffb52864bad59..2ee500a8d1139b 100755 --- a/src/syscall/mkall.sh +++ b/src/syscall/mkall.sh @@ -88,11 +88,12 @@ run="sh" case "$1" in -syscalls) - for i in zsyscall*go + shift + for i in ${@:-zsyscall*go} do # Run the command line that appears in the first line # of the generated file to regenerate it. - sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i + sed 1q $i | sed 's;^// ;./;' | sh > _$i && gofmt < _$i > $i rm _$i done exit 0 @@ -198,6 +199,16 @@ linux_arm64) # API consistent between platforms. mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" ;; +linux_loong64) + GOOSARCH_in=syscall_linux_loong64.go + unistd_h=$(ls -1 /usr/include/asm/unistd.h /usr/include/asm-generic/unistd.h 2>/dev/null | head -1) + if [ "$unistd_h" = "" ]; then + echo >&2 cannot find unistd.h + exit 1 + fi + mksysnum="./mksysnum_linux.pl $unistd_h" + mktypes="GOARCH=$GOARCH go tool cgo -godefs" + ;; linux_mips) GOOSARCH_in=syscall_linux_mipsx.go unistd_h=/usr/include/asm/unistd.h @@ -283,7 +294,7 @@ netbsd_arm64) mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; openbsd_386) - GOOSARCH_in="syscall_openbsd1.go syscall_openbsd_$GOARCH.go" + GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go" mkerrors="$mkerrors -m32" mksyscall="./mksyscall.pl -l32 -openbsd -libc" mksysctl="./mksysctl_openbsd.pl" diff --git a/src/syscall/mkasm.go b/src/syscall/mkasm.go index e0ec46681d14a5..dce61f322c08e3 100644 --- a/src/syscall/mkasm.go +++ b/src/syscall/mkasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // mkasm.go generates assembly trampolines to call library routines from Go. // This program must be run after mksyscall.pl. diff --git a/src/syscall/mkerrors.sh b/src/syscall/mkerrors.sh index fc86d8bd7e052c..f7d37279ed997e 100755 --- a/src/syscall/mkerrors.sh +++ b/src/syscall/mkerrors.sh @@ -131,6 +131,7 @@ includes_Linux=' #include #include #include +#include #include #include #include @@ -269,6 +270,7 @@ ccflags="$@" $2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next} $2 ~ /^(SCM_SRCRT)$/ {next} $2 ~ /^(MAP_FAILED)$/ {next} + $2 ~ /^CLONE_[A-Z_]+/ {next} # These are defined in exec_linux.go. $2 ~ /^ELF_.*$/ {next} # contains ELF_ARCH, etc. $2 !~ /^ETH_/ && @@ -316,7 +318,6 @@ ccflags="$@" $2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ || $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || - $2 ~ /^CLONE_[A-Z_]+/ || $2 !~ /^(BPF_TIMEVAL)$/ && $2 ~ /^(BPF|DLT)_/ || $2 !~ "WMESGLEN" && diff --git a/src/syscall/mkpost.go b/src/syscall/mkpost.go index 94e8d92eff86c0..4f81b81b57e528 100644 --- a/src/syscall/mkpost.go +++ b/src/syscall/mkpost.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // mkpost processes the output of cgo -godefs to // modify the generated types. It is used to clean up diff --git a/src/syscall/mksyscall.pl b/src/syscall/mksyscall.pl index 758948bc53fe13..25ab911b7c6a46 100755 --- a/src/syscall/mksyscall.pl +++ b/src/syscall/mksyscall.pl @@ -387,7 +387,6 @@ ($) // Code generated by the command above; DO NOT EDIT. //go:build $newtags -// +build $tags package syscall diff --git a/src/syscall/mksyscall_libc.pl b/src/syscall/mksyscall_libc.pl index 631a07c819cbab..37314765d8c92c 100755 --- a/src/syscall/mksyscall_libc.pl +++ b/src/syscall/mksyscall_libc.pl @@ -309,7 +309,6 @@ ($) // Code generated by the command above; DO NOT EDIT. //go:build $newtags -// +build $tags package $package diff --git a/src/syscall/mksyscall_windows.go b/src/syscall/mksyscall_windows.go index d8e8a713c554cc..39517bc7f141e9 100644 --- a/src/syscall/mksyscall_windows.go +++ b/src/syscall/mksyscall_windows.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // mksyscall_windows wraps golang.org/x/sys/windows/mkwinsyscall. package main diff --git a/src/syscall/mmap_unix_test.go b/src/syscall/mmap_unix_test.go index e3909f1afb05fd..5e08b20679313e 100644 --- a/src/syscall/mmap_unix_test.go +++ b/src/syscall/mmap_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build aix darwin dragonfly freebsd linux netbsd openbsd +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris package syscall_test diff --git a/src/syscall/msan.go b/src/syscall/msan.go index 89fb75f03c9d96..89c580799fc858 100644 --- a/src/syscall/msan.go +++ b/src/syscall/msan.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build msan -// +build msan package syscall diff --git a/src/syscall/msan0.go b/src/syscall/msan0.go index 85097025a0bdbe..fba8a5f716ed26 100644 --- a/src/syscall/msan0.go +++ b/src/syscall/msan0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !msan -// +build !msan package syscall diff --git a/src/syscall/net_js.go b/src/syscall/net_js.go index ed462025bbc88c..2ed4e191bd3493 100644 --- a/src/syscall/net_js.go +++ b/src/syscall/net_js.go @@ -6,7 +6,6 @@ // This file only exists to make the compiler happy. //go:build js && wasm -// +build js,wasm package syscall @@ -46,8 +45,7 @@ const ( SYS_FCNTL = 500 // unsupported ) -type Sockaddr interface { -} +type Sockaddr any type SockaddrInet4 struct { Port int diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go index 0937ff797ad4dc..e976c70ef1670a 100644 --- a/src/syscall/netlink_linux.go +++ b/src/syscall/netlink_linux.go @@ -50,19 +50,27 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte { // NetlinkRIB returns routing information base, as known as RIB, which // consists of network facility information, states and parameters. func NetlinkRIB(proto, family int) ([]byte, error) { - s, err := cloexecSocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) + s, err := Socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) if err != nil { return nil, err } defer Close(s) - lsa := &SockaddrNetlink{Family: AF_NETLINK} - if err := Bind(s, lsa); err != nil { + sa := &SockaddrNetlink{Family: AF_NETLINK} + if err := Bind(s, sa); err != nil { return nil, err } wb := newNetlinkRouteRequest(proto, 1, family) - if err := Sendto(s, wb, 0, lsa); err != nil { + if err := Sendto(s, wb, 0, sa); err != nil { return nil, err } + lsa, err := Getsockname(s) + if err != nil { + return nil, err + } + lsanl, ok := lsa.(*SockaddrNetlink) + if !ok { + return nil, EINVAL + } var tab []byte rbNew := make([]byte, Getpagesize()) done: @@ -82,16 +90,7 @@ done: return nil, err } for _, m := range msgs { - lsa, err := Getsockname(s) - if err != nil { - return nil, err - } - switch v := lsa.(type) { - case *SockaddrNetlink: - if m.Header.Seq != 1 || m.Header.Pid != v.Pid { - return nil, EINVAL - } - default: + if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid { return nil, EINVAL } if m.Header.Type == NLMSG_DONE { diff --git a/src/syscall/ptrace_darwin.go b/src/syscall/ptrace_darwin.go index b968c7c7f3a910..519e451c73ff94 100644 --- a/src/syscall/ptrace_darwin.go +++ b/src/syscall/ptrace_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !ios -// +build !ios package syscall diff --git a/src/syscall/ptrace_ios.go b/src/syscall/ptrace_ios.go index 5209d1e0dd1605..fa8d0007154997 100644 --- a/src/syscall/ptrace_ios.go +++ b/src/syscall/ptrace_ios.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ios -// +build ios package syscall diff --git a/src/syscall/route_bsd.go b/src/syscall/route_bsd.go index e9321a4e645596..8e47ff888e9358 100644 --- a/src/syscall/route_bsd.go +++ b/src/syscall/route_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package syscall diff --git a/src/syscall/route_freebsd_32bit.go b/src/syscall/route_freebsd_32bit.go index 412833a37cb2d5..c70f0bb0d3c16f 100644 --- a/src/syscall/route_freebsd_32bit.go +++ b/src/syscall/route_freebsd_32bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd && 386) || (freebsd && arm) -// +build freebsd,386 freebsd,arm package syscall diff --git a/src/syscall/route_freebsd_64bit.go b/src/syscall/route_freebsd_64bit.go index 5300bed4717385..9febdfaf3540b2 100644 --- a/src/syscall/route_freebsd_64bit.go +++ b/src/syscall/route_freebsd_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (freebsd && amd64) || (freebsd && arm64) -// +build freebsd,amd64 freebsd,arm64 package syscall diff --git a/src/syscall/setuidgid_32_linux.go b/src/syscall/setuidgid_32_linux.go index 64897fe43c6408..6920f33987cd60 100644 --- a/src/syscall/setuidgid_32_linux.go +++ b/src/syscall/setuidgid_32_linux.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && (386 || arm) -// +build linux -// +build 386 arm package syscall diff --git a/src/syscall/setuidgid_linux.go b/src/syscall/setuidgid_linux.go index 3b36f66fa2639d..c995d258eb93dd 100644 --- a/src/syscall/setuidgid_linux.go +++ b/src/syscall/setuidgid_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !386 && !arm -// +build linux,!386,!arm package syscall diff --git a/src/syscall/sock_cloexec_linux.go b/src/syscall/sock_cloexec_linux.go deleted file mode 100644 index 600cf25c150188..00000000000000 --- a/src/syscall/sock_cloexec_linux.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package syscall - -// This is a stripped down version of sysSocket from net/sock_cloexec.go. -func cloexecSocket(family, sotype, proto int) (int, error) { - s, err := Socket(family, sotype|SOCK_CLOEXEC, proto) - switch err { - case nil: - return s, nil - default: - return -1, err - case EINVAL: - } - - ForkLock.RLock() - s, err = Socket(family, sotype, proto) - if err == nil { - CloseOnExec(s) - } - ForkLock.RUnlock() - if err != nil { - Close(s) - return -1, err - } - return s, nil -} diff --git a/src/syscall/sockcmsg_unix.go b/src/syscall/sockcmsg_unix.go index 99913b9a88bdc0..6ade73e87e0f8a 100644 --- a/src/syscall/sockcmsg_unix.go +++ b/src/syscall/sockcmsg_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix // Socket control messages diff --git a/src/syscall/sockcmsg_unix_other.go b/src/syscall/sockcmsg_unix_other.go index bd8dcfa34cf7ef..845bd9df990970 100644 --- a/src/syscall/sockcmsg_unix_other.go +++ b/src/syscall/sockcmsg_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin freebsd linux netbsd openbsd solaris package syscall diff --git a/src/syscall/syscall.go b/src/syscall/syscall.go index 91173033eecf81..62bfa449cff5f2 100644 --- a/src/syscall/syscall.go +++ b/src/syscall/syscall.go @@ -23,9 +23,10 @@ // That is also where updates required by new systems or versions // should be applied. See https://golang.org/s/go1.4-syscall for more // information. -// package syscall +import "internal/bytealg" + //go:generate go run ./mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go // StringByteSlice converts a string to a NUL-terminated []byte, @@ -45,10 +46,8 @@ func StringByteSlice(s string) []byte { // containing the text of s. If s contains a NUL byte at any // location, it returns (nil, EINVAL). func ByteSliceFromString(s string) ([]byte, error) { - for i := 0; i < len(s); i++ { - if s[i] == 0 { - return nil, EINVAL - } + if bytealg.IndexByteString(s, 0) != -1 { + return nil, EINVAL } a := make([]byte, len(s)+1) copy(a, s) diff --git a/src/syscall/syscall_aix.go b/src/syscall/syscall_aix.go index 9c6afba442e2f2..dbcb7bb7173170 100644 --- a/src/syscall/syscall_aix.go +++ b/src/syscall/syscall_aix.go @@ -15,6 +15,11 @@ import ( "unsafe" ) +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + // Implemented in runtime/syscall_aix.go. func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) @@ -31,6 +36,8 @@ const ( F_DUPFD_CLOEXEC = 0 // AF_LOCAL doesn't exist on AIX AF_LOCAL = AF_UNIX + + _F_DUP2FD_CLOEXEC = 0 ) func (ts *StTimespec_t) Unix() (sec int64, nsec int64) { @@ -58,24 +65,29 @@ func Access(path string, mode uint32) (err error) { //sys Dup2(old int, new int) (err error) //sysnb pipe(p *[2]_C_int) (err error) + func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } var pp [2]_C_int err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return } //sys readlink(path string, buf []byte, bufSize uint64) (n int, err error) + func Readlink(path string, buf []byte) (n int, err error) { s := uint64(len(buf)) return readlink(path, buf, s) } //sys utimes(path string, times *[2]Timeval) (err error) + func Utimes(path string, tv []Timeval) error { if len(tv) != 2 { return EINVAL @@ -84,6 +96,7 @@ func Utimes(path string, tv []Timeval) error { } //sys utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error) + func UtimesNano(path string, ts []Timespec) error { if len(ts) != 2 { return EINVAL @@ -92,6 +105,7 @@ func UtimesNano(path string, ts []Timespec) error { } //sys unlinkat(dirfd int, path string, flags int) (err error) + func Unlinkat(dirfd int, path string) (err error) { return unlinkat(dirfd, path, 0) } @@ -197,11 +211,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } //sys getdirent(fd int, buf []byte) (n int, err error) + func ReadDirent(fd int, buf []byte) (n int, err error) { return getdirent(fd, buf) } //sys wait4(pid _Pid_t, status *_C_int, options int, rusage *Rusage) (wpid _Pid_t, err error) + func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status _C_int var r _Pid_t @@ -219,6 +235,7 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, } //sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range + func Fsync(fd int) error { return fsyncRange(fd, O_SYNC, 0, 0) } @@ -253,9 +270,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) { p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil } @@ -268,9 +283,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) { p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) sa.raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil } @@ -308,6 +321,7 @@ func Getsockname(fd int) (sa Sockaddr, err error) { } //sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) + func Accept(fd int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny @@ -323,10 +337,9 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { return } -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -355,27 +368,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } oobn = int(msg.Controllen) recvflags = int(msg.Flags) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(&rsa) - } - return -} - -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) return } -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) @@ -441,9 +437,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa := new(SockaddrInet4) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_INET6: @@ -451,9 +445,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa := new(SockaddrInet6) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil } return nil, EAFNOSUPPORT @@ -616,6 +608,7 @@ func PtraceDetach(pid int) (err error) { return ptrace64(PT_DETACH, int64(pid), //sys Getppid() (ppid int) //sys Getpriority(which int, who int) (n int, err error) //sysnb Getrlimit(which int, lim *Rlimit) (err error) +//sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Getuid() (uid int) //sys Kill(pid int, signum Signal) (err error) //sys Lchown(path string, uid int, gid int) (err error) @@ -625,8 +618,8 @@ func PtraceDetach(pid int) (err error) { return ptrace64(PT_DETACH, int64(pid), //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Reboot(how int) (err error) //sys Rename(from string, to string) (err error) diff --git a/src/syscall/syscall_bsd.go b/src/syscall/syscall_bsd.go index 595e705856ec3f..5e636d52580462 100644 --- a/src/syscall/syscall_bsd.go +++ b/src/syscall/syscall_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd // BSD system call wrappers shared by *BSD based systems // including OS X (Darwin) and FreeBSD. Like the other @@ -168,9 +167,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) { p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil } @@ -184,9 +181,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) { p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) sa.raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil } @@ -215,9 +210,7 @@ func (sa *SockaddrDatalink) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Nlen = sa.Nlen sa.raw.Alen = sa.Alen sa.raw.Slen = sa.Slen - for i := 0; i < len(sa.raw.Data); i++ { - sa.raw.Data[i] = sa.Data[i] - } + sa.raw.Data = sa.Data return unsafe.Pointer(&sa.raw), SizeofSockaddrDatalink, nil } @@ -233,9 +226,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa.Nlen = pp.Nlen sa.Alen = pp.Alen sa.Slen = pp.Slen - for i := 0; i < len(sa.Data); i++ { - sa.Data[i] = pp.Data[i] - } + sa.Data = pp.Data return sa, nil case AF_UNIX: @@ -267,9 +258,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa := new(SockaddrInet4) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_INET6: @@ -278,9 +267,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) sa.ZoneId = pp.Scope_id - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil } return nil, EAFNOSUPPORT @@ -371,10 +358,9 @@ func GetsockoptICMPv6Filter(fd, level, opt int) (*ICMPv6Filter, error) { //sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -398,29 +384,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } oobn = int(msg.Controllen) recvflags = int(msg.Flags) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(&rsa) - } return } //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) - return -} - -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) @@ -524,12 +493,7 @@ func UtimesNano(path string, ts []Timespec) error { if len(ts) != 2 { return EINVAL } - // Darwin setattrlist can set nanosecond timestamps - err := setattrlistTimes(path, ts) - if err != ENOSYS { - return err - } - err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) + err := utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) if err != ENOSYS { return err } diff --git a/src/syscall/syscall_bsd_test.go b/src/syscall/syscall_bsd_test.go index 2d8a8cbfe6125b..98fab1d3963c8d 100644 --- a/src/syscall/syscall_bsd_test.go +++ b/src/syscall/syscall_bsd_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || openbsd -// +build darwin dragonfly freebsd openbsd package syscall_test diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go index a4fe4f1962bb7e..663ac4e94c3b07 100644 --- a/src/syscall/syscall_darwin.go +++ b/src/syscall/syscall_darwin.go @@ -17,6 +17,13 @@ import ( "unsafe" ) +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +var dupTrampoline = abi.FuncPCABI0(libc_dup2_trampoline) + type SockaddrDatalink struct { Len uint8 Family uint8 @@ -72,22 +79,6 @@ func direntNamlen(buf []byte) (uint64, bool) { func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) } func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) } -const ( - attrBitMapCount = 5 - attrCmnModtime = 0x00000400 - attrCmnAcctime = 0x00001000 -) - -type attrList struct { - bitmapCount uint16 - _ uint16 - CommonAttr uint32 - VolAttr uint32 - DirAttr uint32 - FileAttr uint32 - Forkattr uint32 -} - //sysnb pipe(p *[2]int32) (err error) func Pipe(p []int) (err error) { @@ -96,8 +87,10 @@ func Pipe(p []int) (err error) { } var q [2]int32 err = pipe(&q) - p[0] = int(q[0]) - p[1] = int(q[1]) + if err == nil { + p[0] = int(q[0]) + p[1] = int(q[1]) + } return } @@ -120,42 +113,7 @@ func libc_getfsstat_trampoline() //go:cgo_import_dynamic libc_getfsstat getfsstat "/usr/lib/libSystem.B.dylib" -func setattrlistTimes(path string, times []Timespec) error { - _p0, err := BytePtrFromString(path) - if err != nil { - return err - } - - var attrList attrList - attrList.bitmapCount = attrBitMapCount - attrList.CommonAttr = attrCmnModtime | attrCmnAcctime - - // order is mtime, atime: the opposite of Chtimes - attributes := [2]Timespec{times[1], times[0]} - const options = 0 - _, _, e1 := syscall6( - abi.FuncPCABI0(libc_setattrlist_trampoline), - uintptr(unsafe.Pointer(_p0)), - uintptr(unsafe.Pointer(&attrList)), - uintptr(unsafe.Pointer(&attributes)), - uintptr(unsafe.Sizeof(attributes)), - uintptr(options), - 0, - ) - if e1 != 0 { - return e1 - } - return nil -} - -func libc_setattrlist_trampoline() - -//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib" - -func utimensat(dirfd int, path string, times *[2]Timespec, flag int) error { - // Darwin doesn't support SYS_UTIMENSAT - return ENOSYS -} +//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) /* * Wrapped @@ -217,8 +175,8 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1) //sys Munlockall() (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) //sys Readlink(path string, buf []byte) (n int, err error) @@ -352,12 +310,7 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { break } // Copy entry into return buffer. - s := struct { - ptr unsafe.Pointer - siz int - cap int - }{ptr: unsafe.Pointer(&entry), siz: reclen, cap: reclen} - copy(buf, *(*[]byte)(unsafe.Pointer(&s))) + copy(buf, unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)) buf = buf[reclen:] n += reclen cnt++ diff --git a/src/syscall/syscall_dragonfly.go b/src/syscall/syscall_dragonfly.go index b01a4ada67525e..1a3cfe51fa4b4f 100644 --- a/src/syscall/syscall_dragonfly.go +++ b/src/syscall/syscall_dragonfly.go @@ -17,6 +17,13 @@ import ( "unsafe" ) +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +const _SYS_DUP3 = 0 + // See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h var ( osreldateOnce sync.Once @@ -96,8 +103,11 @@ func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } - p[0], p[1], err = pipe() - return + r, w, err := pipe() + if err == nil { + p[0], p[1] = r, w + } + return err } //sysnb pipe2(p *[2]_C_int, flags int) (r int, w int, err error) @@ -109,17 +119,22 @@ func Pipe2(p []int, flags int) (err error) { var pp [2]_C_int // pipe2 on dragonfly takes an fds array as an argument, but still // returns the file descriptors. - p[0], p[1], err = pipe2(&pp, flags) + r, w, err := pipe2(&pp, flags) + if err == nil { + p[0], p[1] = r, w + } return err } //sys extpread(fd int, p []byte, flags int, offset int64) (n int, err error) -func Pread(fd int, p []byte, offset int64) (n int, err error) { + +func pread(fd int, p []byte, offset int64) (n int, err error) { return extpread(fd, p, 0, offset) } //sys extpwrite(fd int, p []byte, flags int, offset int64) (n int, err error) -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + +func pwrite(fd int, p []byte, offset int64) (n int, err error) { return extpwrite(fd, p, 0, offset) } @@ -156,11 +171,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec) error { - // used on Darwin for UtimesNano - return ENOSYS -} - /* * Exposed directly */ diff --git a/src/syscall/syscall_dup2_linux.go b/src/syscall/syscall_dup2_linux.go deleted file mode 100644 index 351a96edc18c2f..00000000000000 --- a/src/syscall/syscall_dup2_linux.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !android && (386 || amd64 || arm || mips || mipsle || mips64 || mips64le || ppc64 || ppc64le || s390x) -// +build !android -// +build 386 amd64 arm mips mipsle mips64 mips64le ppc64 ppc64le s390x - -package syscall - -const _SYS_dup = SYS_DUP2 diff --git a/src/syscall/syscall_dup3_linux.go b/src/syscall/syscall_dup3_linux.go deleted file mode 100644 index 66ec67b0ab4a92..00000000000000 --- a/src/syscall/syscall_dup3_linux.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build android || arm64 || riscv64 -// +build android arm64 riscv64 - -package syscall - -const _SYS_dup = SYS_DUP3 diff --git a/src/syscall/syscall_freebsd.go b/src/syscall/syscall_freebsd.go index 7c7b89aab9030a..0637215f8921bd 100644 --- a/src/syscall/syscall_freebsd.go +++ b/src/syscall/syscall_freebsd.go @@ -17,6 +17,11 @@ import ( "unsafe" ) +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + const ( _SYS_FSTAT_FREEBSD12 = 551 // { int fstat(int fd, _Out_ struct stat *sb); } _SYS_FSTATAT_FREEBSD12 = 552 // { int fstatat(int fd, _In_z_ char *path, _Out_ struct stat *buf, int flag); } @@ -105,8 +110,10 @@ func Pipe2(p []int, flags int) error { } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } @@ -176,11 +183,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec) error { - // used on Darwin for UtimesNano - return ENOSYS -} - func Stat(path string, st *Stat_t) (err error) { var oldStat stat_freebsd11_t if supportsABI(_ino64First) { @@ -472,8 +474,8 @@ func convertFromDirents11(buf []byte, old []byte) int { //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) diff --git a/src/syscall/syscall_freebsd_test.go b/src/syscall/syscall_freebsd_test.go index 89c7959d0c43ac..f04b12b6d3ca4d 100644 --- a/src/syscall/syscall_freebsd_test.go +++ b/src/syscall/syscall_freebsd_test.go @@ -3,12 +3,12 @@ // license that can be found in the LICENSE file. //go:build freebsd -// +build freebsd package syscall_test import ( "fmt" + "os" "syscall" "testing" "unsafe" @@ -53,3 +53,13 @@ func TestConvertFromDirent11(t *testing.T) { } } } + +func TestMain(m *testing.M) { + if os.Getenv("GO_DEATHSIG_PARENT") == "1" { + deathSignalParent() + } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { + deathSignalChild() + } + + os.Exit(m.Run()) +} diff --git a/src/syscall/syscall_illumos.go b/src/syscall/syscall_illumos.go index ef95fe58f7c58c..04f9e7e5d6370d 100644 --- a/src/syscall/syscall_illumos.go +++ b/src/syscall/syscall_illumos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build illumos -// +build illumos // Illumos system calls not present on Solaris. @@ -11,34 +10,11 @@ package syscall import "unsafe" -//go:cgo_import_dynamic libc_accept4 accept4 "libsocket.so" //go:cgo_import_dynamic libc_flock flock "libc.so" -//go:linkname procAccept4 libc_accept4 //go:linkname procFlock libc_flock -var ( - procAccept4, - procFlock libcFunc -) - -func Accept4(fd int, flags int) (int, Sockaddr, error) { - var rsa RawSockaddrAny - var addrlen _Socklen = SizeofSockaddrAny - nfd, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procAccept4)), 4, uintptr(fd), uintptr(unsafe.Pointer(&rsa)), uintptr(unsafe.Pointer(&addrlen)), uintptr(flags), 0, 0) - if errno != 0 { - return 0, nil, errno - } - if addrlen > SizeofSockaddrAny { - panic("RawSockaddrAny too small") - } - sa, err := anyToSockaddr(&rsa) - if err != nil { - Close(int(nfd)) - return 0, nil, err - } - return int(nfd), sa, nil -} +var procFlock libcFunc func Flock(fd int, how int) error { _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procFlock)), 2, uintptr(fd), uintptr(how), 0, 0, 0, 0) diff --git a/src/syscall/syscall_js.go b/src/syscall/syscall_js.go index ed70d622842da2..c9c65229804c5d 100644 --- a/src/syscall/syscall_js.go +++ b/src/syscall/syscall_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package syscall @@ -42,6 +41,7 @@ const PathMax = 256 // An Errno is an unsigned number describing an error condition. // It implements the error interface. The zero Errno is by convention // a non-error, so code to convert from Errno to error should use: +// // err = nil // if errno != 0 { // err = errno diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index dfce3d0a4bac33..bdee570ddaef73 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -16,7 +16,85 @@ import ( "unsafe" ) +// N.B. RawSyscall6 is provided via linkname by runtime/internal/syscall. +// +// Errno is uintptr and thus compatible with the runtime/internal/syscall +// definition. + +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +// Pull in entersyscall/exitsyscall for Syscall/Syscall6. +// +// Note that this can't be a push linkname because the runtime already has a +// nameless linkname to export to assembly here and in x/sys. Additionally, +// entersyscall fetches the caller PC and SP and thus can't have a wrapper +// inbetween. + +//go:linkname runtime_entersyscall runtime.entersyscall +func runtime_entersyscall() + +//go:linkname runtime_exitsyscall runtime.exitsyscall +func runtime_exitsyscall() + +// N.B. For the Syscall functions below: +// +// //go:uintptrkeepalive because the uintptr argument may be converted pointers +// that need to be kept alive in the caller (this is implied for RawSyscall6 +// since it has no body). +// +// //go:nosplit because stack copying does not account for uintptrkeepalive, so +// the stack must not grow. Stack copying cannot blindly assume that all +// uintptr arguments are pointers, because some values may look like pointers, +// but not really be pointers, and adjusting their value would break the call. +// +// //go:norace, on RawSyscall, to avoid race instrumentation if RawSyscall is +// called after fork, or from a signal handler. +// +// //go:linkname to ensure ABI wrappers are generated for external callers +// (notably x/sys/unix assembly). + +//go:uintptrkeepalive +//go:nosplit +//go:norace +//go:linkname RawSyscall +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + return RawSyscall6(trap, a1, a2, a3, 0, 0, 0) +} + +//go:uintptrkeepalive +//go:nosplit +//go:linkname Syscall +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + runtime_entersyscall() + // N.B. Calling RawSyscall here is unsafe with atomic coverage + // instrumentation and race mode. + // + // Coverage instrumentation will add a sync/atomic call to RawSyscall. + // Race mode will add race instrumentation to sync/atomic. Race + // instrumentation requires a P, which we no longer have. + // + // RawSyscall6 is fine because it is implemented in assembly and thus + // has no coverage instrumentation. + // + // This is typically not a problem in the runtime because cmd/go avoids + // adding coverage instrumentation to the runtime in race mode. + r1, r2, err = RawSyscall6(trap, a1, a2, a3, 0, 0, 0) + runtime_exitsyscall() + return +} + +//go:uintptrkeepalive +//go:nosplit +//go:linkname Syscall6 +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { + runtime_entersyscall() + r1, r2, err = RawSyscall6(trap, a1, a2, a3, a4, a5, a6) + runtime_exitsyscall() + return +} + func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr) +func rawVforkSyscall(trap, a1, a2 uintptr) (r1 uintptr, err Errno) /* * Wrapped @@ -38,6 +116,13 @@ func Creat(path string, mode uint32) (fd int, err error) { return Open(path, O_CREAT|O_WRONLY|O_TRUNC, mode) } +func EpollCreate(size int) (fd int, err error) { + if size <= 0 { + return -1, EINVAL + } + return EpollCreate1(0) +} + func isGroupMember(gid int) bool { groups, err := Getgroups() if err != nil { @@ -53,10 +138,15 @@ func isGroupMember(gid int) bool { } //sys faccessat(dirfd int, path string, mode uint32) (err error) +//sys faccessat2(dirfd int, path string, mode uint32, flags int) (err error) = _SYS_faccessat2 func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { - if flags & ^(_AT_SYMLINK_NOFOLLOW|_AT_EACCESS) != 0 { - return EINVAL + if flags == 0 { + return faccessat(dirfd, path, mode) + } + + if err := faccessat2(dirfd, path, mode, flags); err != ENOSYS && err != EPERM { + return err } // The Linux kernel faccessat system call does not take any flags. @@ -65,8 +155,8 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { // Because people naturally expect syscall.Faccessat to act // like C faccessat, we do the same. - if flags == 0 { - return faccessat(dirfd, path, mode) + if flags & ^(_AT_SYMLINK_NOFOLLOW|_AT_EACCESS) != 0 { + return EINVAL } var st Stat_t @@ -109,7 +199,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { gid = Getgid() } - if uint32(gid) == st.Gid || isGroupMember(gid) { + if uint32(gid) == st.Gid || isGroupMember(int(st.Gid)) { fmode = (st.Mode >> 3) & 7 } else { fmode = st.Mode & 7 @@ -161,6 +251,25 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) return openat(dirfd, path, flags|O_LARGEFILE, mode) } +func Pipe(p []int) error { + return Pipe2(p, 0) +} + +//sysnb pipe2(p *[2]_C_int, flags int) (err error) + +func Pipe2(p []int, flags int) error { + if len(p) != 2 { + return EINVAL + } + var pp [2]_C_int + err := pipe2(&pp, flags) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } + return err +} + //sys readlinkat(dirfd int, path string, buf []byte) (n int, err error) func Readlink(path string, buf []byte) (n int, err error) { @@ -204,18 +313,7 @@ func UtimesNano(path string, ts []Timespec) (err error) { if len(ts) != 2 { return EINVAL } - err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) - if err != ENOSYS { - return err - } - // If the utimensat syscall isn't available (utimensat was added to Linux - // in 2.6.22, Released, 8 July 2007) then fall back to utimes - var tv [2]Timeval - for i := 0; i < 2; i++ { - tv[i].Sec = ts[i].Sec - tv[i].Usec = ts[i].Nsec / 1000 - } - return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) + return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) } func Futimesat(dirfd int, path string, tv []Timeval) (err error) { @@ -245,6 +343,13 @@ func Getwd() (wd string, err error) { if n < 1 || n > len(buf) || buf[n-1] != 0 { return "", EINVAL } + // In some cases, Linux can return a path that starts with the + // "(unreachable)" prefix, which can potentially be a valid relative + // path. To work around that, return ENOENT if path is not absolute. + if buf[0] != '/' { + return "", ENOENT + } + return string(buf[0 : n-1]), nil } @@ -387,9 +492,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) { p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil } @@ -402,9 +505,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) { p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) sa.raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil } @@ -455,9 +556,7 @@ func (sa *SockaddrLinklayer) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Hatype = sa.Hatype sa.raw.Pkttype = sa.Pkttype sa.raw.Halen = sa.Halen - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrLinklayer, nil } @@ -496,9 +595,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa.Hatype = pp.Hatype sa.Pkttype = pp.Pkttype sa.Halen = pp.Halen - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_UNIX: @@ -531,9 +628,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa := new(SockaddrInet4) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_INET6: @@ -542,9 +637,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) sa.ZoneId = pp.Scope_id - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil } return nil, EAFNOSUPPORT @@ -553,11 +646,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { func Accept(fd int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny - // Try accept4 first for Android, then try accept for kernel older than 2.6.28 nfd, err = accept4(fd, &rsa, &len, 0) - if err == ENOSYS { - nfd, err = accept(fd, &rsa, &len) - } if err != nil { return } @@ -648,10 +737,9 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { return setsockopt(fd, level, opt, unsafe.Pointer(mreq), unsafe.Sizeof(*mreq)) } -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -682,28 +770,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } oobn = int(msg.Controllen) recvflags = int(msg.Flags) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(&rsa) - } - return -} - -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) return } -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - var err error - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(ptr) msg.Namelen = uint32(salen) @@ -985,62 +1055,12 @@ func Getpgrp() (pid int) { //sysnb Setsid() (pid int, err error) //sysnb Settimeofday(tv *Timeval) (err error) -// allThreadsCaller holds the input and output state for performing a -// allThreadsSyscall that needs to synchronize all OS thread state. Linux -// generally does not always support this natively, so we have to -// manipulate the runtime to fix things up. -type allThreadsCaller struct { - // arguments - trap, a1, a2, a3, a4, a5, a6 uintptr - - // return values (only set by 0th invocation) - r1, r2 uintptr - - // err is the error code - err Errno -} - -// doSyscall is a callback for executing a syscall on the current m -// (OS thread). -//go:nosplit -//go:norace -func (pc *allThreadsCaller) doSyscall(initial bool) bool { - r1, r2, err := RawSyscall(pc.trap, pc.a1, pc.a2, pc.a3) - if initial { - pc.r1 = r1 - pc.r2 = r2 - pc.err = err - } else if pc.r1 != r1 || (archHonorsR2 && pc.r2 != r2) || pc.err != err { - print("trap:", pc.trap, ", a123=[", pc.a1, ",", pc.a2, ",", pc.a3, "]\n") - print("results: got {r1=", r1, ",r2=", r2, ",err=", err, "}, want {r1=", pc.r1, ",r2=", pc.r2, ",r3=", pc.err, "}\n") - panic("AllThreadsSyscall results differ between threads; runtime corrupted") - } - return err == 0 -} - -// doSyscall6 is a callback for executing a syscall6 on the current m -// (OS thread). -//go:nosplit -//go:norace -func (pc *allThreadsCaller) doSyscall6(initial bool) bool { - r1, r2, err := RawSyscall6(pc.trap, pc.a1, pc.a2, pc.a3, pc.a4, pc.a5, pc.a6) - if initial { - pc.r1 = r1 - pc.r2 = r2 - pc.err = err - } else if pc.r1 != r1 || (archHonorsR2 && pc.r2 != r2) || pc.err != err { - print("trap:", pc.trap, ", a123456=[", pc.a1, ",", pc.a2, ",", pc.a3, ",", pc.a4, ",", pc.a5, ",", pc.a6, "]\n") - print("results: got {r1=", r1, ",r2=", r2, ",err=", err, "}, want {r1=", pc.r1, ",r2=", pc.r2, ",r3=", pc.err, "}\n") - panic("AllThreadsSyscall6 results differ between threads; runtime corrupted") - } - return err == 0 -} - -// Provided by runtime.syscall_runtime_doAllThreadsSyscall which -// serializes the world and invokes the fn on each OS thread (what the -// runtime refers to as m's). Once this function returns, all threads -// are in sync. -func runtime_doAllThreadsSyscall(fn func(bool) bool) +// Provided by runtime.syscall_runtime_doAllThreadsSyscall which stops the +// world and invokes the syscall on each OS thread. Once this function returns, +// all threads are in sync. +// +//go:uintptrescapes +func runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) // AllThreadsSyscall performs a syscall on each OS thread of the Go // runtime. It first invokes the syscall on one thread. Should that @@ -1057,48 +1077,30 @@ func runtime_doAllThreadsSyscall(fn func(bool) bool) // AllThreadsSyscall is unaware of any threads that are launched // explicitly by cgo linked code, so the function always returns // ENOTSUP in binaries that use cgo. +// //go:uintptrescapes func AllThreadsSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { if cgo_libc_setegid != nil { return minus1, minus1, ENOTSUP } - pc := &allThreadsCaller{ - trap: trap, - a1: a1, - a2: a2, - a3: a3, - } - runtime_doAllThreadsSyscall(pc.doSyscall) - r1 = pc.r1 - r2 = pc.r2 - err = pc.err - return + r1, r2, errno := runtime_doAllThreadsSyscall(trap, a1, a2, a3, 0, 0, 0) + return r1, r2, Errno(errno) } // AllThreadsSyscall6 is like AllThreadsSyscall, but extended to six // arguments. +// //go:uintptrescapes func AllThreadsSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { if cgo_libc_setegid != nil { return minus1, minus1, ENOTSUP } - pc := &allThreadsCaller{ - trap: trap, - a1: a1, - a2: a2, - a3: a3, - a4: a4, - a5: a5, - a6: a6, - } - runtime_doAllThreadsSyscall(pc.doSyscall6) - r1 = pc.r1 - r2 = pc.r2 - err = pc.err - return + r1, r2, errno := runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6) + return r1, r2, Errno(errno) } // linked by runtime.cgocall.go +// //go:uintptrescapes func cgocaller(unsafe.Pointer, ...uintptr) uintptr diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go index 0db037470d4387..0c9c6aa7551db5 100644 --- a/src/syscall/syscall_linux_386.go +++ b/src/syscall/syscall_linux_386.go @@ -6,13 +6,11 @@ package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS32 +const ( + _SYS_setgroups = SYS_SETGROUPS32 + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) func setTimespec(sec, nsec int64) Timespec { return Timespec{Sec: int32(sec), Nsec: int32(nsec)} @@ -22,36 +20,9 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // 64-bit file system and 32-bit uid calls // (386 default is 32-bit file system and 16-bit uid). //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64 @@ -64,8 +35,8 @@ func Pipe2(p []int, flags int) (err error) { //sys Ioperm(from int, num int, on int) (err error) //sys Iopl(level int) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 //sys Setfsgid(gid int) (err error) = SYS_SETFSGID32 @@ -215,14 +186,6 @@ const ( func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno) func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno) -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - fd, e := socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0) - if e != 0 { - err = e - } - return -} - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) if e != 0 { @@ -386,5 +349,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go index 5df3f796d16777..77e1393de1dd24 100644 --- a/src/syscall/syscall_linux_amd64.go +++ b/src/syscall/syscall_linux_amd64.go @@ -4,16 +4,13 @@ package syscall -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) @@ -28,8 +25,8 @@ const _SYS_setgroups = SYS_SETGROUPS //sys Iopl(level int) (err error) //sys Listen(s int, n int) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) @@ -43,7 +40,6 @@ const _SYS_setgroups = SYS_SETGROUPS //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -110,32 +106,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Rip } func (r *PtraceRegs) SetPC(pc uint64) { r.Rip = pc } @@ -151,5 +121,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint64(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index e887cf788f3ae2..f4740af5863dec 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -6,13 +6,11 @@ package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". [EABI assumed.] -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS32 +const ( + _SYS_setgroups = SYS_SETGROUPS32 + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) func setTimespec(sec, nsec int64) Timespec { return Timespec{Sec: int32(sec), Nsec: int32(nsec)} @@ -22,36 +20,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - // Try pipe2 first for Android O, then try pipe for kernel 2.6.23. - err = pipe2(&pp, 0) - if err == ENOSYS { - err = pipe(&pp) - } - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Underlying system call writes to newoffset via pointer. // Implemented in assembly to avoid allocation. func seek(fd int, offset int64, whence int) (newoffset int64, err Errno) @@ -64,7 +32,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { return newoffset, nil } -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -83,7 +50,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { // 64-bit file system and 32-bit uid calls // (16-bit uid calls are not always supported in newer kernels) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32 //sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64 //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64 @@ -109,8 +75,8 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { //sys Utime(path string, buf *Utimbuf) (err error) //sys utimes(path string, times *[2]Timeval) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Truncate(path string, length int64) (err error) = SYS_TRUNCATE64 //sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64 @@ -235,5 +201,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index f575c84c930c2d..f42686262ac930 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -6,26 +6,21 @@ package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS - -func EpollCreate(size int) (fd int, err error) { - if size <= 0 { - return -1, EINVAL - } - return EpollCreate1(0) -} +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) -//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) + +func Fstatat(fd int, path string, stat *Stat_t, flags int) error { + return fstatat(fd, path, stat, flags) +} + //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Ftruncate(fd int, length int64) (err error) //sysnb Getegid() (egid int) @@ -34,20 +29,19 @@ func EpollCreate(size int) (fd int, err error) { //sysnb getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Listen(s int, n int) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) //sys Setfsgid(gid int) (err error) //sys Setfsuid(uid int) (err error) //sysnb setrlimit(resource int, rlim *Rlimit) (err error) -//sysnb Setreuid(ruid int, euid int) (err error) //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) func Stat(path string, stat *Stat_t) (err error) { - return Fstatat(_AT_FDCWD, path, stat, 0) + return fstatat(_AT_FDCWD, path, stat, 0) } func Lchown(path string, uid int, gid int) (err error) { @@ -55,13 +49,12 @@ func Lchown(path string, uid int, gid int) (err error) { } func Lstat(path string, stat *Stat_t) (err error) { - return Fstatat(_AT_FDCWD, path, stat, _AT_SYMLINK_NOFOLLOW) + return fstatat(_AT_FDCWD, path, stat, _AT_SYMLINK_NOFOLLOW) } //sys Statfs(path string, buf *Statfs_t) (err error) //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) = SYS_SYNC_FILE_RANGE2 //sys Truncate(path string, length int64) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -146,30 +139,6 @@ func utimes(path string, tv *[2]Timeval) (err error) { return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Getrlimit prefers the prlimit64 system call. See issue 38604. func Getrlimit(resource int, rlim *Rlimit) error { err := prlimit(0, resource, nil, rlim) @@ -214,5 +183,3 @@ func Pause() error { _, err := ppoll(nil, 0, nil, nil) return err } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_loong64.go b/src/syscall/syscall_linux_loong64.go new file mode 100644 index 00000000000000..5a0fa0834d0704 --- /dev/null +++ b/src/syscall/syscall_linux_loong64.go @@ -0,0 +1,220 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +import "unsafe" + +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) + +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT +//sys Fchown(fd int, uid int, gid int) (err error) +//sys Fstatfs(fd int, buf *Statfs_t) (err error) +//sys Ftruncate(fd int, length int64) (err error) +//sysnb Getegid() (egid int) +//sysnb Geteuid() (euid int) +//sysnb Getgid() (gid int) +//sysnb Getuid() (uid int) +//sys Listen(s int, n int) (err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) = SYS_RENAMEAT2 +//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) +//sys Setfsgid(gid int) (err error) +//sys Setfsuid(uid int) (err error) +//sys Shutdown(fd int, how int) (err error) +//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) +//sys statx(dirfd int, path string, flags int, mask int, stat *statx_t) (err error) + +// makedev makes C dev_t from major and minor numbers the glibc way: +// 0xMMMM_MMMM 0xmmmm_mmmm -> 0xMMMM_Mmmm_mmmM_MMmm +func makedev(major uint32, minor uint32) uint64 { + majorH := uint64(major >> 12) + majorL := uint64(major & 0xfff) + minorH := uint64(minor >> 8) + minorL := uint64(minor & 0xff) + return (majorH << 44) | (minorH << 20) | (majorL << 8) | minorL +} + +func timespecFromStatxTimestamp(x statxTimestamp) Timespec { + return Timespec{ + Sec: x.Sec, + Nsec: int64(x.Nsec), + } +} + +func fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) { + var r statx_t + // Do it the glibc way, add AT_NO_AUTOMOUNT. + if err = statx(dirfd, path, _AT_NO_AUTOMOUNT|flags, _STATX_BASIC_STATS, &r); err != nil { + return err + } + + stat.Dev = makedev(r.Dev_major, r.Dev_minor) + stat.Ino = r.Ino + stat.Mode = uint32(r.Mode) + stat.Nlink = r.Nlink + stat.Uid = r.Uid + stat.Gid = r.Gid + stat.Rdev = makedev(r.Rdev_major, r.Rdev_minor) + // hope we don't get to process files so large to overflow these size + // fields... + stat.Size = int64(r.Size) + stat.Blksize = int32(r.Blksize) + stat.Blocks = int64(r.Blocks) + stat.Atim = timespecFromStatxTimestamp(r.Atime) + stat.Mtim = timespecFromStatxTimestamp(r.Mtime) + stat.Ctim = timespecFromStatxTimestamp(r.Ctime) + + return nil +} + +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { + return fstatat(fd, path, stat, flags) +} + +func Fstat(fd int, stat *Stat_t) (err error) { + return fstatat(fd, "", stat, _AT_EMPTY_PATH) +} + +func Stat(path string, stat *Stat_t) (err error) { + return fstatat(_AT_FDCWD, path, stat, 0) +} + +func Lchown(path string, uid int, gid int) (err error) { + return Fchownat(_AT_FDCWD, path, uid, gid, _AT_SYMLINK_NOFOLLOW) +} + +func Lstat(path string, stat *Stat_t) (err error) { + return fstatat(_AT_FDCWD, path, stat, _AT_SYMLINK_NOFOLLOW) +} + +//sys Statfs(path string, buf *Statfs_t) (err error) +//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) +//sys Truncate(path string, length int64) (err error) +//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) +//sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) +//sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) +//sysnb getgroups(n int, list *_Gid_t) (nn int, err error) +//sys getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) +//sys setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) +//sysnb socket(domain int, typ int, proto int) (fd int, err error) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) +//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) +//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) +//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) +//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) +//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) +//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) + +type sigset_t struct { + X__val [16]uint64 +} + +//sys pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_t) (n int, err error) = SYS_PSELECT6 + +func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) { + var ts *Timespec + if timeout != nil { + ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} + } + return pselect(nfd, r, w, e, ts, nil) +} + +//sysnb Gettimeofday(tv *Timeval) (err error) + +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} +} + +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} +} + +func futimesat(dirfd int, path string, tv *[2]Timeval) (err error) { + if tv == nil { + return utimensat(dirfd, path, nil, 0) + } + + ts := []Timespec{ + NsecToTimespec(TimevalToNsec(tv[0])), + NsecToTimespec(TimevalToNsec(tv[1])), + } + return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) +} + +func Time(t *Time_t) (Time_t, error) { + var tv Timeval + err := Gettimeofday(&tv) + if err != nil { + return 0, err + } + if t != nil { + *t = Time_t(tv.Sec) + } + return Time_t(tv.Sec), nil +} + +func Utime(path string, buf *Utimbuf) error { + tv := []Timeval{ + {Sec: buf.Actime}, + {Sec: buf.Modtime}, + } + return Utimes(path, tv) +} + +func utimes(path string, tv *[2]Timeval) (err error) { + if tv == nil { + return utimensat(_AT_FDCWD, path, nil, 0) + } + + ts := []Timespec{ + NsecToTimespec(TimevalToNsec(tv[0])), + NsecToTimespec(TimevalToNsec(tv[1])), + } + return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) +} + +// Getrlimit prefers the prlimit64 system call. +func Getrlimit(resource int, rlim *Rlimit) error { + return prlimit(0, resource, nil, rlim) +} + +// Setrlimit prefers the prlimit64 system call. +func Setrlimit(resource int, rlim *Rlimit) error { + return prlimit(0, resource, rlim, nil) +} + +func (r *PtraceRegs) GetEra() uint64 { return r.Era } + +func (r *PtraceRegs) SetEra(era uint64) { r.Era = era } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint64(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint64(length) +} + +func InotifyInit() (fd int, err error) { + return InotifyInit1(0) +} + +//sys ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) + +func Pause() error { + _, err := ppoll(nil, 0, nil, nil) + return err +} diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go index 5feb03e915adf0..8a0aa5c91e1695 100644 --- a/src/syscall/syscall_linux_mips64x.go +++ b/src/syscall/syscall_linux_mips64x.go @@ -3,21 +3,16 @@ // license that can be found in the LICENSE file. //go:build linux && (mips64 || mips64le) -// +build linux -// +build mips64 mips64le package syscall -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 5435 + _SYS_faccessat2 = 5439 +) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT @@ -31,8 +26,8 @@ const _SYS_setgroups = SYS_SETGROUPS //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) @@ -45,7 +40,6 @@ const _SYS_setgroups = SYS_SETGROUPS //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -103,30 +97,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func Ioperm(from int, num int, on int) (err error) { return ENOSYS } @@ -213,5 +183,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint64(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go index 39104d71d84520..c8468fb5b596f6 100644 --- a/src/syscall/syscall_linux_mipsx.go +++ b/src/syscall/syscall_linux_mipsx.go @@ -3,25 +3,20 @@ // license that can be found in the LICENSE file. //go:build linux && (mips || mipsle) -// +build linux -// +build mips mipsle package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 4435 + _SYS_faccessat2 = 4439 +) func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64 //sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64 @@ -32,8 +27,8 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 @@ -45,7 +40,6 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) = SYS_TRUNCATE64 //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -112,29 +106,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe() (p1 int, p2 int, err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - p[0], p[1], err = pipe() - return -} - //sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { @@ -223,5 +194,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go index 495ae29757cac6..5c076d8beae356 100644 --- a/src/syscall/syscall_linux_ppc64x.go +++ b/src/syscall/syscall_linux_ppc64x.go @@ -3,21 +3,16 @@ // license that can be found in the LICENSE file. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package syscall -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = false - -const _SYS_setgroups = SYS_SETGROUPS +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) @@ -36,8 +31,8 @@ const _SYS_setgroups = SYS_SETGROUPS //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT @@ -51,7 +46,6 @@ const _SYS_setgroups = SYS_SETGROUPS //sys Statfs(path string, buf *Statfs_t) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -82,30 +76,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Nip } func (r *PtraceRegs) SetPC(pc uint64) { r.Nip = pc } @@ -122,8 +92,6 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint64(length) } -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) - //sys syncFileRange2(fd int, flags int, off int64, n int64) (err error) = SYS_SYNC_FILE_RANGE2 func SyncFileRange(fd int, off int64, n int64, flags int) error { diff --git a/src/syscall/syscall_linux_riscv64.go b/src/syscall/syscall_linux_riscv64.go index 2a0fe64d258617..3bb54600a83a24 100644 --- a/src/syscall/syscall_linux_riscv64.go +++ b/src/syscall/syscall_linux_riscv64.go @@ -6,26 +6,21 @@ package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS - -func EpollCreate(size int) (fd int, err error) { - if size <= 0 { - return -1, EINVAL - } - return EpollCreate1(0) -} +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) -//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) //sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) + +func Fstatat(fd int, path string, stat *Stat_t, flags int) error { + return fstatat(fd, path, stat, flags) +} + //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Ftruncate(fd int, length int64) (err error) //sysnb Getegid() (egid int) @@ -34,8 +29,8 @@ func EpollCreate(size int) (fd int, err error) { //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Listen(s int, n int) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) @@ -50,7 +45,7 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e } func Stat(path string, stat *Stat_t) (err error) { - return Fstatat(_AT_FDCWD, path, stat, 0) + return fstatat(_AT_FDCWD, path, stat, 0) } func Lchown(path string, uid int, gid int) (err error) { @@ -58,13 +53,12 @@ func Lchown(path string, uid int, gid int) (err error) { } func Lstat(path string, stat *Stat_t) (err error) { - return Fstatat(_AT_FDCWD, path, stat, _AT_SYMLINK_NOFOLLOW) + return fstatat(_AT_FDCWD, path, stat, _AT_SYMLINK_NOFOLLOW) } //sys Statfs(path string, buf *Statfs_t) (err error) //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -149,30 +143,6 @@ func utimes(path string, tv *[2]Timeval) (err error) { return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Pc } func (r *PtraceRegs) SetPC(pc uint64) { r.Pc = pc } @@ -199,5 +169,3 @@ func Pause() error { _, err := ppoll(nil, 0, nil, nil) return err } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go index 0f6f6277bbfda2..cb83697be4c10a 100644 --- a/src/syscall/syscall_linux_s390x.go +++ b/src/syscall/syscall_linux_s390x.go @@ -6,16 +6,13 @@ package syscall import "unsafe" -// archHonorsR2 captures the fact that r2 is honored by the -// runtime.GOARCH. Syscall conventions are generally r1, r2, err := -// syscall(trap, ...). Not all architectures define r2 in their -// ABI. See "man syscall". -const archHonorsR2 = true - -const _SYS_setgroups = SYS_SETGROUPS +const ( + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 +) //sys Dup2(oldfd int, newfd int) (err error) -//sysnb EpollCreate(size int) (fd int, err error) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstat(fd int, stat *Stat_t) (err error) @@ -31,8 +28,8 @@ const _SYS_setgroups = SYS_SETGROUPS //sys Lchown(path string, uid int, gid int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) @@ -74,30 +71,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Linux on s390x uses the old mmap interface, which requires arguments to be passed in a struct. // mmap2 also requires arguments to be passed in a struct; it is currently not exposed in . func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { @@ -142,14 +115,6 @@ const ( func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno) func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno) -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - fd, e := socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0) - if e != 0 { - err = e - } - return -} - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) if e != 0 { @@ -293,5 +258,3 @@ func (msghdr *Msghdr) SetControllen(length int) { func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint64(length) } - -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go index 442dc9f10eb87f..ff128b1a19fc03 100644 --- a/src/syscall/syscall_linux_test.go +++ b/src/syscall/syscall_linux_test.go @@ -5,21 +5,19 @@ package syscall_test import ( - "bufio" "fmt" "io" "io/fs" "os" "os/exec" - "os/signal" "path/filepath" "runtime" "sort" "strconv" "strings" + "sync" "syscall" "testing" - "time" "unsafe" ) @@ -153,120 +151,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestLinuxDeathSignal(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping root only test") - } - - // Copy the test binary to a location that a non-root user can read/execute - // after we drop privileges - tempDir, err := os.MkdirTemp("", "TestDeathSignal") - if err != nil { - t.Fatalf("cannot create temporary directory: %v", err) - } - defer os.RemoveAll(tempDir) - os.Chmod(tempDir, 0755) - - tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) - - src, err := os.Open(os.Args[0]) - if err != nil { - t.Fatalf("cannot open binary %q, %v", os.Args[0], err) - } - defer src.Close() - - dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) - } - if _, err := io.Copy(dst, src); err != nil { - t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) - } - err = dst.Close() - if err != nil { - t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) - } - - cmd := exec.Command(tmpBinary) - cmd.Env = append(os.Environ(), "GO_DEATHSIG_PARENT=1") - chldStdin, err := cmd.StdinPipe() - if err != nil { - t.Fatalf("failed to create new stdin pipe: %v", err) - } - chldStdout, err := cmd.StdoutPipe() - if err != nil { - t.Fatalf("failed to create new stdout pipe: %v", err) - } - cmd.Stderr = os.Stderr - - err = cmd.Start() - defer cmd.Wait() - if err != nil { - t.Fatalf("failed to start first child process: %v", err) - } - - chldPipe := bufio.NewReader(chldStdout) - - if got, err := chldPipe.ReadString('\n'); got == "start\n" { - syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) - - go func() { - time.Sleep(5 * time.Second) - chldStdin.Close() - }() - - want := "ok\n" - if got, err = chldPipe.ReadString('\n'); got != want { - t.Fatalf("expected %q, received %q, %v", want, got, err) - } - } else { - t.Fatalf("did not receive start from child, received %q, %v", got, err) - } -} - -func deathSignalParent() { - cmd := exec.Command(os.Args[0]) - cmd.Env = append(os.Environ(), - "GO_DEATHSIG_PARENT=", - "GO_DEATHSIG_CHILD=1", - ) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - attrs := syscall.SysProcAttr{ - Pdeathsig: syscall.SIGUSR1, - // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is - // unused on Ubuntu - Credential: &syscall.Credential{Uid: 99, Gid: 99}, - } - cmd.SysProcAttr = &attrs - - err := cmd.Start() - if err != nil { - fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err) - os.Exit(1) - } - cmd.Wait() - os.Exit(0) -} - -func deathSignalChild() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGUSR1) - go func() { - <-c - fmt.Println("ok") - os.Exit(0) - }() - fmt.Println("start") - - buf := make([]byte, 32) - os.Stdin.Read(buf) - - // We expected to be signaled before stdin closed - fmt.Println("not ok") - os.Exit(1) -} - func TestParseNetlinkMessage(t *testing.T) { for i, b := range [][]byte{ {103, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 5, 8, 0, 3, @@ -632,6 +516,9 @@ func TestSetuidEtc(t *testing.T) { if syscall.Getuid() != 0 { t.Skip("skipping root only test") } + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("skipping glibc test on alpine - go.dev/issue/19938") + } vs := []struct { call string fn func() error @@ -682,3 +569,73 @@ func TestSetuidEtc(t *testing.T) { } } } + +// TestAllThreadsSyscallError verifies that errors are properly returned when +// the syscall fails on the original thread. +func TestAllThreadsSyscallError(t *testing.T) { + // SYS_CAPGET takes pointers as the first two arguments. Since we pass + // 0, we expect to get EFAULT back. + r1, r2, err := syscall.AllThreadsSyscall(syscall.SYS_CAPGET, 0, 0, 0) + if err == syscall.ENOTSUP { + t.Skip("AllThreadsSyscall disabled with cgo") + } + if err != syscall.EFAULT { + t.Errorf("AllThreadSyscall(SYS_CAPGET) got %d, %d, %v, want err %v", r1, r2, err, syscall.EFAULT) + } +} + +// TestAllThreadsSyscallBlockedSyscall confirms that AllThreadsSyscall +// can interrupt threads in long-running system calls. This test will +// deadlock if this doesn't work correctly. +func TestAllThreadsSyscallBlockedSyscall(t *testing.T) { + if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, 0, 0); err == syscall.ENOTSUP { + t.Skip("AllThreadsSyscall disabled with cgo") + } + + rd, wr, err := os.Pipe() + if err != nil { + t.Fatalf("unable to obtain a pipe: %v", err) + } + + // Perform a blocking read on the pipe. + var wg sync.WaitGroup + ready := make(chan bool) + wg.Add(1) + go func() { + data := make([]byte, 1) + + // To narrow the window we have to wait for this + // goroutine to block in read, synchronize just before + // calling read. + ready <- true + + // We use syscall.Read directly to avoid the poller. + // This will return when the write side is closed. + n, err := syscall.Read(int(rd.Fd()), data) + if !(n == 0 && err == nil) { + t.Errorf("expected read to return 0, got %d, %s", n, err) + } + + // Clean up rd and also ensure rd stays reachable so + // it doesn't get closed by GC. + rd.Close() + wg.Done() + }() + <-ready + + // Loop here to give the goroutine more time to block in read. + // Generally this will trigger on the first iteration anyway. + pid := syscall.Getpid() + for i := 0; i < 100; i++ { + if id, _, e := syscall.AllThreadsSyscall(syscall.SYS_GETPID, 0, 0, 0); e != 0 { + t.Errorf("[%d] getpid failed: %v", i, e) + } else if int(id) != pid { + t.Errorf("[%d] getpid got=%d, want=%d", i, id, pid) + } + // Provide an explicit opportunity for this goroutine + // to change Ms. + runtime.Gosched() + } + wr.Close() + wg.Wait() +} diff --git a/src/syscall/syscall_netbsd.go b/src/syscall/syscall_netbsd.go index fc13b706b5da3c..d8efb41d20cd7b 100644 --- a/src/syscall/syscall_netbsd.go +++ b/src/syscall/syscall_netbsd.go @@ -14,6 +14,14 @@ package syscall import "unsafe" +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +const _SYS_DUP3 = SYS_DUP3 + type SockaddrDatalink struct { Len uint8 Family uint8 @@ -26,8 +34,6 @@ type SockaddrDatalink struct { raw RawSockaddrDatalink } -func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) - func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) { var olen uintptr @@ -106,18 +112,22 @@ func Pipe(p []int) (err error) { } //sysnb pipe2(p *[2]_C_int, flags int) (err error) + func Pipe2(p []int, flags int) error { if len(p) != 2 { return EINVAL } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } //sys paccept(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, sigmask *sigset, flags int) (nfd int, err error) + func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny @@ -137,6 +147,7 @@ func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) { } //sys getdents(fd int, buf []byte) (n int, err error) + func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { return getdents(fd, buf) } @@ -146,11 +157,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e return -1, ENOSYS } -func setattrlistTimes(path string, times []Timespec) error { - // used on Darwin for UtimesNano - return ENOSYS -} - /* * Exposed directly */ @@ -199,8 +205,8 @@ func setattrlistTimes(path string, times []Timespec) error { //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) diff --git a/src/syscall/syscall_openbsd.go b/src/syscall/syscall_openbsd.go index 5a5ba5a51b07b7..75217344df1251 100644 --- a/src/syscall/syscall_openbsd.go +++ b/src/syscall/syscall_openbsd.go @@ -14,6 +14,12 @@ package syscall import "unsafe" +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + type SockaddrDatalink struct { Len uint8 Family uint8 @@ -26,8 +32,6 @@ type SockaddrDatalink struct { raw RawSockaddrDatalink } -func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) - func nametomib(name string) (mib []_C_int, err error) { // Perform lookup via a binary search left := 0 @@ -66,18 +70,22 @@ func Pipe(p []int) error { } //sysnb pipe2(p *[2]_C_int, flags int) (err error) + func Pipe2(p []int, flags int) error { if len(p) != 2 { return EINVAL } var pp [2]_C_int err := pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return err } //sys accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error) + func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len _Socklen = SizeofSockaddrAny @@ -97,6 +105,7 @@ func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) { } //sys getdents(fd int, buf []byte) (n int, err error) + func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { return getdents(fd, buf) } @@ -121,11 +130,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec) error { - // used on Darwin for UtimesNano - return ENOSYS -} - /* * Exposed directly */ @@ -139,6 +143,7 @@ func setattrlistTimes(path string, times []Timespec) error { //sys Close(fd int) (err error) //sys Dup(fd int) (nfd int, err error) //sys Dup2(from int, to int) (err error) +//sys dup3(from int, to int, flags int) (err error) //sys Fchdir(fd int) (err error) //sys Fchflags(fd int, flags int) (err error) //sys Fchmod(fd int, mode uint32) (err error) @@ -175,8 +180,8 @@ func setattrlistTimes(path string, times []Timespec) error { //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) diff --git a/src/syscall/syscall_openbsd1.go b/src/syscall/syscall_openbsd1.go index 15870ce1ee1523..bddeda637a9815 100644 --- a/src/syscall/syscall_openbsd1.go +++ b/src/syscall/syscall_openbsd1.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd && mips64 -// +build openbsd,mips64 package syscall diff --git a/src/syscall/syscall_openbsd_libc.go b/src/syscall/syscall_openbsd_libc.go index e67ee4e571c264..516d02975c530a 100644 --- a/src/syscall/syscall_openbsd_libc.go +++ b/src/syscall/syscall_openbsd_libc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build openbsd && !mips64 -// +build openbsd,!mips64 package syscall @@ -11,6 +10,8 @@ import ( "internal/abi" ) +var dupTrampoline = abi.FuncPCABI0(libc_dup3_trampoline) + func init() { execveOpenBSD = execve } diff --git a/src/syscall/syscall_openbsd_mips64.go b/src/syscall/syscall_openbsd_mips64.go index b259dc69755b7d..4508ad99b492a5 100644 --- a/src/syscall/syscall_openbsd_mips64.go +++ b/src/syscall/syscall_openbsd_mips64.go @@ -4,6 +4,8 @@ package syscall +const _SYS_DUP3 = SYS_DUP3 + func setTimespec(sec, nsec int64) Timespec { return Timespec{Sec: sec, Nsec: nsec} } diff --git a/src/syscall/syscall_plan9.go b/src/syscall/syscall_plan9.go index d16cad45d851da..ca286c8c97522f 100644 --- a/src/syscall/syscall_plan9.go +++ b/src/syscall/syscall_plan9.go @@ -181,6 +181,7 @@ func Write(fd int, p []byte) (n int, err error) { var ioSync int64 //sys fd2path(fd int, buf []byte) (err error) + func Fd2path(fd int) (path string, err error) { var buf [512]byte @@ -192,14 +193,17 @@ func Fd2path(fd int) (path string, err error) { } //sys pipe(p *[2]int32) (err error) + func Pipe(p []int) (err error) { if len(p) != 2 { return NewError("bad arg in system call") } var pp [2]int32 err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + } return } @@ -258,6 +262,7 @@ func (w Waitmsg) ExitStatus() int { } //sys await(s []byte) (n int, err error) + func Await(w *Waitmsg) (err error) { var buf [512]byte var f [5][]byte @@ -377,42 +382,49 @@ func Getgroups() (gids []int, err error) { } //sys open(path string, mode int) (fd int, err error) + func Open(path string, mode int) (fd int, err error) { fixwd(path) return open(path, mode) } //sys create(path string, mode int, perm uint32) (fd int, err error) + func Create(path string, mode int, perm uint32) (fd int, err error) { fixwd(path) return create(path, mode, perm) } //sys remove(path string) (err error) + func Remove(path string) error { fixwd(path) return remove(path) } //sys stat(path string, edir []byte) (n int, err error) + func Stat(path string, edir []byte) (n int, err error) { fixwd(path) return stat(path, edir) } //sys bind(name string, old string, flag int) (err error) + func Bind(name string, old string, flag int) (err error) { fixwd(name, old) return bind(name, old, flag) } //sys mount(fd int, afd int, old string, flag int, aname string) (err error) + func Mount(fd int, afd int, old string, flag int, aname string) (err error) { fixwd(old) return mount(fd, afd, old, flag, aname) } //sys wstat(path string, edir []byte) (err error) + func Wstat(path string, edir []byte) (err error) { fixwd(path) return wstat(path, edir) diff --git a/src/syscall/syscall_ptrace_test.go b/src/syscall/syscall_ptrace_test.go index 45729d9e8e9698..5b128de7795457 100644 --- a/src/syscall/syscall_ptrace_test.go +++ b/src/syscall/syscall_ptrace_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd package syscall_test diff --git a/src/syscall/syscall_solaris.go b/src/syscall/syscall_solaris.go index daa4b88a71d6fc..f6d7e46cd35842 100644 --- a/src/syscall/syscall_solaris.go +++ b/src/syscall/syscall_solaris.go @@ -14,6 +14,13 @@ package syscall import "unsafe" +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +const _F_DUP2FD_CLOEXEC = F_DUP2FD_CLOEXEC + // Implemented in asm_solaris_amd64.s. func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) @@ -45,18 +52,43 @@ func direntNamlen(buf []byte) (uint64, bool) { return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true } -func pipe() (r uintptr, w uintptr, err uintptr) - func Pipe(p []int) (err error) { + return Pipe2(p, 0) +} + +//sysnb pipe2(p *[2]_C_int, flags int) (err error) + +func Pipe2(p []int, flags int) error { if len(p) != 2 { return EINVAL } - r0, w0, e1 := pipe() - if e1 != 0 { - err = Errno(e1) + var pp [2]_C_int + err := pipe2(&pp, flags) + if err == nil { + p[0] = int(pp[0]) + p[1] = int(pp[1]) } - p[0], p[1] = int(r0), int(w0) - return + return err +} + +//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) = libsocket.accept4 + +func Accept4(fd int, flags int) (int, Sockaddr, error) { + var rsa RawSockaddrAny + var addrlen _Socklen = SizeofSockaddrAny + nfd, err := accept4(fd, &rsa, &addrlen, flags) + if err != nil { + return 0, nil, err + } + if addrlen > SizeofSockaddrAny { + panic("RawSockaddrAny too small") + } + sa, err := anyToSockaddr(&rsa) + if err != nil { + Close(nfd) + return 0, nil, err + } + return nfd, sa, nil } func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) { @@ -67,9 +99,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) { p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet4, nil } @@ -82,9 +112,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) { p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) sa.raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), SizeofSockaddrInet6, nil } @@ -302,9 +330,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { sa := new(SockaddrInet4) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_INET6: @@ -313,9 +339,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) sa.ZoneId = pp.Scope_id - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil } return nil, EAFNOSUPPORT @@ -338,10 +362,9 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { return } -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -364,29 +387,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from return } oobn = int(msg.Accrightslen) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(&rsa) - } - return -} - -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) return } //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) @@ -440,6 +446,7 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) //sys Getppid() (ppid int) //sys Getpriority(which int, who int) (n int, err error) //sysnb Getrlimit(which int, lim *Rlimit) (err error) +//sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Gettimeofday(tv *Timeval) (err error) //sysnb Getuid() (uid int) //sys Kill(pid int, signum Signal) (err error) @@ -452,8 +459,8 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) @@ -529,6 +536,20 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) { return } +var mapper = &mmapper{ + active: make(map[*byte][]byte), + mmap: mmap, + munmap: munmap, +} + +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return mapper.Mmap(fd, offset, length, prot, flags) +} + +func Munmap(b []byte) (err error) { + return mapper.Munmap(b) +} + func Utimes(path string, tv []Timeval) error { if len(tv) != 2 { return EINVAL diff --git a/src/syscall/syscall_unix.go b/src/syscall/syscall_unix.go index 5b405b99b4f237..3133becd7ca520 100644 --- a/src/syscall/syscall_unix.go +++ b/src/syscall/syscall_unix.go @@ -2,16 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package syscall import ( + "internal/bytealg" "internal/itoa" "internal/oserror" "internal/race" - "internal/unsafeheader" "runtime" "sync" "unsafe" @@ -28,17 +27,10 @@ const ( netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4 ) -func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) -func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) -func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) -func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) - // clen returns the index of the first NULL byte in n or len(n) if n contains no NULL byte. func clen(n []byte) int { - for i := 0; i < len(n); i++ { - if n[i] == 0 { - return i - } + if i := bytealg.IndexByte(n, 0); i != -1 { + return i } return len(n) } @@ -64,11 +56,7 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d } // Use unsafe to turn addr into a []byte. - var b []byte - hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b)) - hdr.Data = unsafe.Pointer(addr) - hdr.Cap = length - hdr.Len = length + b := unsafe.Slice((*byte)(unsafe.Pointer(addr)), length) // Register mapping in m and return it. p := &b[cap(b)-1] @@ -103,6 +91,7 @@ func (m *mmapper) Munmap(data []byte) (err error) { // An Errno is an unsigned number describing an error condition. // It implements the error interface. The zero Errno is by convention // a non-error, so code to convert from Errno to error should use: +// // err = nil // if errno != 0 { // err = errno @@ -198,6 +187,9 @@ func Read(fd int, p []byte) (n int, err error) { if msanenabled && n > 0 { msanWrite(unsafe.Pointer(&p[0]), n) } + if asanenabled && n > 0 { + asanWrite(unsafe.Pointer(&p[0]), n) + } return } @@ -219,6 +211,45 @@ func Write(fd int, p []byte) (n int, err error) { if msanenabled && n > 0 { msanRead(unsafe.Pointer(&p[0]), n) } + if asanenabled && n > 0 { + asanRead(unsafe.Pointer(&p[0]), n) + } + return +} + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + n, err = pread(fd, p, offset) + if race.Enabled { + if n > 0 { + race.WriteRange(unsafe.Pointer(&p[0]), n) + } + if err == nil { + race.Acquire(unsafe.Pointer(&ioSync)) + } + } + if msanenabled && n > 0 { + msanWrite(unsafe.Pointer(&p[0]), n) + } + if asanenabled && n > 0 { + asanWrite(unsafe.Pointer(&p[0]), n) + } + return +} + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + if race.Enabled { + race.ReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = pwrite(fd, p, offset) + if race.Enabled && n > 0 { + race.ReadRange(unsafe.Pointer(&p[0]), n) + } + if msanenabled && n > 0 { + msanRead(unsafe.Pointer(&p[0]), n) + } + if asanenabled && n > 0 { + asanRead(unsafe.Pointer(&p[0]), n) + } return } @@ -292,6 +323,119 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { return } +func recvfromInet4(fd int, p []byte, flags int, from *SockaddrInet4) (n int, err error) { + var rsa RawSockaddrAny + var socklen _Socklen = SizeofSockaddrAny + if n, err = recvfrom(fd, p, flags, &rsa, &socklen); err != nil { + return + } + pp := (*RawSockaddrInet4)(unsafe.Pointer(&rsa)) + port := (*[2]byte)(unsafe.Pointer(&pp.Port)) + from.Port = int(port[0])<<8 + int(port[1]) + from.Addr = pp.Addr + return +} + +func recvfromInet6(fd int, p []byte, flags int, from *SockaddrInet6) (n int, err error) { + var rsa RawSockaddrAny + var socklen _Socklen = SizeofSockaddrAny + if n, err = recvfrom(fd, p, flags, &rsa, &socklen); err != nil { + return + } + pp := (*RawSockaddrInet6)(unsafe.Pointer(&rsa)) + port := (*[2]byte)(unsafe.Pointer(&pp.Port)) + from.Port = int(port[0])<<8 + int(port[1]) + from.ZoneId = pp.Scope_id + from.Addr = pp.Addr + return +} + +func recvmsgInet4(fd int, p, oob []byte, flags int, from *SockaddrInet4) (n, oobn int, recvflags int, err error) { + var rsa RawSockaddrAny + n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa) + if err != nil { + return + } + pp := (*RawSockaddrInet4)(unsafe.Pointer(&rsa)) + port := (*[2]byte)(unsafe.Pointer(&pp.Port)) + from.Port = int(port[0])<<8 + int(port[1]) + from.Addr = pp.Addr + return +} + +func recvmsgInet6(fd int, p, oob []byte, flags int, from *SockaddrInet6) (n, oobn int, recvflags int, err error) { + var rsa RawSockaddrAny + n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa) + if err != nil { + return + } + pp := (*RawSockaddrInet6)(unsafe.Pointer(&rsa)) + port := (*[2]byte)(unsafe.Pointer(&pp.Port)) + from.Port = int(port[0])<<8 + int(port[1]) + from.ZoneId = pp.Scope_id + from.Addr = pp.Addr + return +} + +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { + var rsa RawSockaddrAny + n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa) + // source address is only specified if the socket is unconnected + if rsa.Addr.Family != AF_UNSPEC { + from, err = anyToSockaddr(&rsa) + } + return +} + +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { + _, err = SendmsgN(fd, p, oob, to, flags) + return +} + +func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { + var ptr unsafe.Pointer + var salen _Socklen + if to != nil { + ptr, salen, err = to.sockaddr() + if err != nil { + return 0, err + } + } + return sendmsgN(fd, p, oob, ptr, salen, flags) +} + +func sendmsgNInet4(fd int, p, oob []byte, to *SockaddrInet4, flags int) (n int, err error) { + ptr, salen, err := to.sockaddr() + if err != nil { + return 0, err + } + return sendmsgN(fd, p, oob, ptr, salen, flags) +} + +func sendmsgNInet6(fd int, p, oob []byte, to *SockaddrInet6, flags int) (n int, err error) { + ptr, salen, err := to.sockaddr() + if err != nil { + return 0, err + } + return sendmsgN(fd, p, oob, ptr, salen, flags) +} + +func sendtoInet4(fd int, p []byte, flags int, to *SockaddrInet4) (err error) { + ptr, n, err := to.sockaddr() + if err != nil { + return err + } + return sendto(fd, p, flags, ptr, n) +} + +func sendtoInet6(fd int, p []byte, flags int, to *SockaddrInet6) (err error) { + ptr, n, err := to.sockaddr() + if err != nil { + return err + } + return sendto(fd, p, flags, ptr, n) +} + func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) { ptr, n, err := to.sockaddr() if err != nil { diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go index af0bc856ee8f9d..56e771e08603d0 100644 --- a/src/syscall/syscall_unix_test.go +++ b/src/syscall/syscall_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build unix package syscall_test @@ -85,16 +84,24 @@ func TestFcntlFlock(t *testing.T) { if err != nil { t.Fatalf("Open failed: %v", err) } - defer syscall.Close(fd) - if err := syscall.Ftruncate(fd, 1<<20); err != nil { + // f takes ownership of fd, and will close it. + // + // N.B. This defer is also necessary to keep f alive + // while we use its fd, preventing its finalizer from + // executing. + f := os.NewFile(uintptr(fd), name) + defer f.Close() + + if err := syscall.Ftruncate(int(f.Fd()), 1<<20); err != nil { t.Fatalf("Ftruncate(1<<20) failed: %v", err) } - if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil { + if err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &flock); err != nil { t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err) } + cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$") cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") - cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)} + cmd.ExtraFiles = []*os.File{f} out, err := cmd.CombinedOutput() if len(out) > 0 || err != nil { t.Fatalf("child process: %q, %v", out, err) @@ -252,6 +259,10 @@ func passFDChild() { fmt.Printf("TempFile: %v", err) return } + // N.B. This defer is also necessary to keep f alive + // while we use its fd, preventing its finalizer from + // executing. + defer f.Close() f.Write([]byte("Hello from child process!\n")) f.Seek(0, io.SeekStart) @@ -315,46 +326,6 @@ func TestUnixRightsRoundtrip(t *testing.T) { } } -func TestRlimit(t *testing.T) { - var rlimit, zero syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit) - if err != nil { - t.Fatalf("Getrlimit: save failed: %v", err) - } - if zero == rlimit { - t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit) - } - set := rlimit - set.Cur = set.Max - 1 - if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && set.Cur > 4096 { - // rlim_min for RLIMIT_NOFILE should be equal to - // or lower than kern.maxfilesperproc, which on - // some machines are 4096. See #40564. - set.Cur = 4096 - } - err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set) - if err != nil { - t.Fatalf("Setrlimit: set failed: %#v %v", set, err) - } - var get syscall.Rlimit - err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &get) - if err != nil { - t.Fatalf("Getrlimit: get failed: %v", err) - } - set = rlimit - set.Cur = set.Max - 1 - if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && set.Cur > 4096 { - set.Cur = 4096 - } - if set != get { - t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get) - } - err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit) - if err != nil { - t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err) - } -} - func TestSeekFailure(t *testing.T) { _, err := syscall.Seek(-1, 0, io.SeekStart) if err == nil { diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 660179ae9e8cf3..76a2f5ad54192c 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -8,10 +8,10 @@ package syscall import ( errorspkg "errors" + "internal/bytealg" "internal/itoa" "internal/oserror" "internal/race" - "internal/unsafeheader" "runtime" "sync" "unicode/utf16" @@ -39,12 +39,16 @@ func StringToUTF16(s string) []uint16 { // s, with a terminating NUL added. If s contains a NUL byte at any // location, it returns (nil, EINVAL). func UTF16FromString(s string) ([]uint16, error) { - for i := 0; i < len(s); i++ { - if s[i] == 0 { - return nil, EINVAL - } + if bytealg.IndexByteString(s, 0) != -1 { + return nil, EINVAL + } + // In the worst case all characters require two uint16. + // Also account for the terminating NULL character. + buf := make([]uint16, 0, len(s)*2+1) + for _, r := range s { + buf = utf16.AppendRune(buf, r) } - return utf16.Encode([]rune(s + "\x00")), nil + return utf16.AppendRune(buf, '\x00'), nil } // UTF16ToString returns the UTF-8 encoding of the UTF-16 sequence s, @@ -73,11 +77,7 @@ func utf16PtrToString(p *uint16) string { n++ } // Turn *uint16 into []uint16. - var s []uint16 - hdr := (*unsafeheader.Slice)(unsafe.Pointer(&s)) - hdr.Data = unsafe.Pointer(p) - hdr.Cap = n - hdr.Len = n + s := unsafe.Slice((*uint16)(unsafe.Pointer(p)), n) // Decode []uint16 into string. return string(utf16.Decode(s)) } @@ -169,7 +169,7 @@ func (e Errno) Timeout() bool { } // Implemented in runtime/syscall_windows.go. -func compileCallback(fn interface{}, cleanstack bool) uintptr +func compileCallback(fn any, cleanstack bool) uintptr // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. // This is useful when interoperating with Windows code requiring callbacks. @@ -177,7 +177,7 @@ func compileCallback(fn interface{}, cleanstack bool) uintptr // Only a limited number of callbacks may be created in a single Go process, and any memory allocated // for these callbacks is never released. // Between NewCallback and NewCallbackCDecl, at least 1024 callbacks can always be created. -func NewCallback(fn interface{}) uintptr { +func NewCallback(fn any) uintptr { return compileCallback(fn, true) } @@ -187,7 +187,7 @@ func NewCallback(fn interface{}) uintptr { // Only a limited number of callbacks may be created in a single Go process, and any memory allocated // for these callbacks is never released. // Between NewCallback and NewCallbackCDecl, at least 1024 callbacks can always be created. -func NewCallbackCDecl(fn interface{}) uintptr { +func NewCallbackCDecl(fn any) uintptr { return compileCallback(fn, false) } @@ -202,8 +202,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys formatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW //sys ExitProcess(exitcode uint32) //sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW -//sys ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) -//sys WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) +//sys readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = ReadFile +//sys writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = WriteFile //sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) [failretval==0xffffffff] //sys CloseHandle(handle Handle) (err error) //sys GetStdHandle(stdhandle int) (handle Handle, err error) [failretval==InvalidHandle] @@ -279,7 +279,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys RegOpenKeyEx(key Handle, subkey *uint16, options uint32, desiredAccess uint32, result *Handle) (regerrno error) = advapi32.RegOpenKeyExW //sys RegCloseKey(key Handle) (regerrno error) = advapi32.RegCloseKey //sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW -//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW +//sys regEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW //sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId //sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode @@ -385,34 +385,50 @@ func Read(fd Handle, p []byte) (n int, err error) { } return 0, e } + return int(done), nil +} + +func Write(fd Handle, p []byte) (n int, err error) { + var done uint32 + e := WriteFile(fd, p, &done, nil) + if e != nil { + return 0, e + } + return int(done), nil +} + +func ReadFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error { + err := readFile(fd, p, done, overlapped) if race.Enabled { - if done > 0 { - race.WriteRange(unsafe.Pointer(&p[0]), int(done)) + if *done > 0 { + race.WriteRange(unsafe.Pointer(&p[0]), int(*done)) } race.Acquire(unsafe.Pointer(&ioSync)) } - if msanenabled && done > 0 { - msanWrite(unsafe.Pointer(&p[0]), int(done)) + if msanenabled && *done > 0 { + msanWrite(unsafe.Pointer(&p[0]), int(*done)) } - return int(done), nil + if asanenabled && *done > 0 { + asanWrite(unsafe.Pointer(&p[0]), int(*done)) + } + return err } -func Write(fd Handle, p []byte) (n int, err error) { +func WriteFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error { if race.Enabled { race.ReleaseMerge(unsafe.Pointer(&ioSync)) } - var done uint32 - e := WriteFile(fd, p, &done, nil) - if e != nil { - return 0, e + err := writeFile(fd, p, done, overlapped) + if race.Enabled && *done > 0 { + race.ReadRange(unsafe.Pointer(&p[0]), int(*done)) } - if race.Enabled && done > 0 { - race.ReadRange(unsafe.Pointer(&p[0]), int(done)) + if msanenabled && *done > 0 { + msanRead(unsafe.Pointer(&p[0]), int(*done)) } - if msanenabled && done > 0 { - msanRead(unsafe.Pointer(&p[0]), int(done)) + if asanenabled && *done > 0 { + asanRead(unsafe.Pointer(&p[0]), int(*done)) } - return int(done), nil + return err } var ioSync int64 @@ -433,12 +449,11 @@ func setFilePointerEx(handle Handle, distToMove int64, newFilePointer *int64, wh default: panic("unsupported 32-bit architecture") case "386": - // distToMove is a LARGE_INTEGER: - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx + // distToMove is a LARGE_INTEGER, which is 64 bits. _, _, e1 = Syscall6(procSetFilePointerEx.Addr(), 5, uintptr(handle), uintptr(distToMove), uintptr(distToMove>>32), uintptr(unsafe.Pointer(newFilePointer)), uintptr(whence), 0) case "arm": // distToMove must be 8-byte aligned per ARM calling convention - // https://msdn.microsoft.com/en-us/library/dn736986.aspx#Anchor_7 + // https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions#stage-c-assignment-of-arguments-to-registers-and-stack _, _, e1 = Syscall6(procSetFilePointerEx.Addr(), 6, uintptr(handle), 0, uintptr(distToMove), uintptr(distToMove>>32), uintptr(unsafe.Pointer(newFilePointer)), uintptr(whence)) } } @@ -738,9 +753,7 @@ func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, int32, error) { p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)) p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), int32(unsafe.Sizeof(sa.raw)), nil } @@ -760,9 +773,7 @@ func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, int32, error) { p[0] = byte(sa.Port >> 8) p[1] = byte(sa.Port) sa.raw.Scope_id = sa.ZoneId - for i := 0; i < len(sa.Addr); i++ { - sa.raw.Addr[i] = sa.Addr[i] - } + sa.raw.Addr = sa.Addr return unsafe.Pointer(&sa.raw), int32(unsafe.Sizeof(sa.raw)), nil } @@ -835,9 +846,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { sa := new(SockaddrInet4) p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil case AF_INET6: @@ -846,9 +855,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { p := (*[2]byte)(unsafe.Pointer(&pp.Port)) sa.Port = int(p[0])<<8 + int(p[1]) sa.ZoneId = pp.Scope_id - for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i] - } + sa.Addr = pp.Addr return sa, nil } return nil, EAFNOSUPPORT @@ -924,6 +931,38 @@ func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32 return err } +func wsaSendtoInet4(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *SockaddrInet4, overlapped *Overlapped, croutine *byte) (err error) { + rsa, len, err := to.sockaddr() + if err != nil { + return err + } + r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(rsa)), uintptr(len), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + if r1 == socket_error { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = EINVAL + } + } + return err +} + +func wsaSendtoInet6(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *SockaddrInet6, overlapped *Overlapped, croutine *byte) (err error) { + rsa, len, err := to.sockaddr() + if err != nil { + return err + } + r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(rsa)), uintptr(len), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + if r1 == socket_error { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = EINVAL + } + } + return err +} + func LoadGetAddrInfo() error { return procGetAddrInfoW.Find() } @@ -1271,3 +1310,31 @@ func newProcThreadAttributeList(maxAttrCount uint32) (*_PROC_THREAD_ATTRIBUTE_LI } return al, nil } + +// RegEnumKeyEx enumerates the subkeys of an open registry key. +// Each call retrieves information about one subkey. name is +// a buffer that should be large enough to hold the name of the +// subkey plus a null terminating character. nameLen is its +// length. On return, nameLen will contain the actual length of the +// subkey. +// +// Should name not be large enough to hold the subkey, this function +// will return ERROR_MORE_DATA, and must be called again with an +// appropriately sized buffer. +// +// reserved must be nil. class and classLen behave like name and nameLen +// but for the class of the subkey, except that they are optional. +// lastWriteTime, if not nil, will be populated with the time the subkey +// was last written. +// +// The caller must enumerate all subkeys in order. That is +// RegEnumKeyEx must be called with index starting at 0, incrementing +// the index until the function returns ERROR_NO_MORE_ITEMS, or with +// the index of the last subkey (obtainable from RegQueryInfoKey), +// decrementing until index 0 is enumerated. +// +// Successive calls to this API must happen on the same OS thread, +// so call runtime.LockOSThread before calling this function. +func RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) { + return regEnumKeyEx(key, index, name, nameLen, reserved, class, classLen, lastWriteTime) +} diff --git a/src/syscall/syscall_windows_test.go b/src/syscall/syscall_windows_test.go index 194c87805cdd75..87f6580bdc553b 100644 --- a/src/syscall/syscall_windows_test.go +++ b/src/syscall/syscall_windows_test.go @@ -130,7 +130,7 @@ int main(int argc, char *argv[]) if err != nil { t.Fatalf("failed to build c executable: %s\n%s", err, out) } - out, err = exec.Command(exe).CombinedOutput() + out, err = exec.Command(exe).Output() if err != nil { t.Fatalf("c program execution failed: %v: %v", err, string(out)) } diff --git a/src/syscall/tables_js.go b/src/syscall/tables_js.go index 64d958415d8f8b..78a1e71a016e30 100644 --- a/src/syscall/tables_js.go +++ b/src/syscall/tables_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package syscall diff --git a/src/syscall/time_fake.go b/src/syscall/time_fake.go index cf88aeb921a162..b60fe60b03c403 100644 --- a/src/syscall/time_fake.go +++ b/src/syscall/time_fake.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build faketime -// +build faketime package syscall diff --git a/src/syscall/time_nofake.go b/src/syscall/time_nofake.go index 5eaa2daabd3f05..231875d8c3c92c 100644 --- a/src/syscall/time_nofake.go +++ b/src/syscall/time_nofake.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !faketime -// +build !faketime package syscall diff --git a/src/syscall/timestruct.go b/src/syscall/timestruct.go index e4f3d50f56b68b..8a03171ee52c0e 100644 --- a/src/syscall/timestruct.go +++ b/src/syscall/timestruct.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package syscall diff --git a/src/syscall/types_aix.go b/src/syscall/types_aix.go index 6588d690eabd68..9e05af90ee5bca 100644 --- a/src/syscall/types_aix.go +++ b/src/syscall/types_aix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_darwin.go b/src/syscall/types_darwin.go index c2a32c0782edae..c9404aaf1bebda 100644 --- a/src/syscall/types_darwin.go +++ b/src/syscall/types_darwin.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_dragonfly.go b/src/syscall/types_dragonfly.go index 9f8d5bc3ddc06e..33420c4a92434d 100644 --- a/src/syscall/types_dragonfly.go +++ b/src/syscall/types_dragonfly.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_freebsd.go b/src/syscall/types_freebsd.go index d7414117036779..83bc7dc49979a9 100644 --- a/src/syscall/types_freebsd.go +++ b/src/syscall/types_freebsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_illumos_amd64.go b/src/syscall/types_illumos_amd64.go index 254e3e7cfeddb4..22c172f1f7d988 100644 --- a/src/syscall/types_illumos_amd64.go +++ b/src/syscall/types_illumos_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build illumos -// +build illumos // Illumos consts not present on Solaris. These are added manually rather than // auto-generated by mkerror.sh diff --git a/src/syscall/types_linux.go b/src/syscall/types_linux.go index bf76be978bce1f..37ed9ce03e24ee 100644 --- a/src/syscall/types_linux.go +++ b/src/syscall/types_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh @@ -119,7 +118,7 @@ struct my_epoll_event { int32_t padFd; #endif #if defined(__powerpc64__) || defined(__s390x__) || (defined(__riscv_xlen) && __riscv_xlen == 64) \ - || (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) + || (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) || defined(__loongarch64) int32_t _padFd; #endif int32_t fd; @@ -184,6 +183,10 @@ type _Gid_t C.gid_t type Stat_t C.struct_stat +type statxTimestamp C.struct_statx_timestamp + +type statx_t C.struct_statx + type Statfs_t C.struct_statfs type Dirent C.struct_dirent @@ -417,6 +420,9 @@ const ( _AT_REMOVEDIR = C.AT_REMOVEDIR _AT_SYMLINK_NOFOLLOW = C.AT_SYMLINK_NOFOLLOW _AT_EACCESS = C.AT_EACCESS + _AT_EMPTY_PATH = C.AT_EMPTY_PATH + _AT_NO_AUTOMOUNT = C.AT_NO_AUTOMOUNT + _STATX_BASIC_STATS = C.STATX_BASIC_STATS ) type pollFd C.struct_pollfd diff --git a/src/syscall/types_netbsd.go b/src/syscall/types_netbsd.go index 0bd25ea3c953d8..e53e8c9386f5a7 100644 --- a/src/syscall/types_netbsd.go +++ b/src/syscall/types_netbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_openbsd.go b/src/syscall/types_openbsd.go index 8b41cdca230bf5..bfe6e9f592f5ae 100644 --- a/src/syscall/types_openbsd.go +++ b/src/syscall/types_openbsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/types_solaris.go b/src/syscall/types_solaris.go index 179f7914817e85..2f56bc06010261 100644 --- a/src/syscall/types_solaris.go +++ b/src/syscall/types_solaris.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore /* Input to cgo -godefs. See also mkerrors.sh and mkall.sh diff --git a/src/syscall/zerrors_darwin_amd64.go b/src/syscall/zerrors_darwin_amd64.go index 0b9897284c4d44..ecbe89c5473a7c 100644 --- a/src/syscall/zerrors_darwin_amd64.go +++ b/src/syscall/zerrors_darwin_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && darwin -// +build amd64,darwin package syscall diff --git a/src/syscall/zerrors_darwin_arm64.go b/src/syscall/zerrors_darwin_arm64.go index 5f210fd1c40b2a..fa7cb845c31689 100644 --- a/src/syscall/zerrors_darwin_arm64.go +++ b/src/syscall/zerrors_darwin_arm64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build arm64 && darwin -// +build arm64,darwin package syscall diff --git a/src/syscall/zerrors_dragonfly_amd64.go b/src/syscall/zerrors_dragonfly_amd64.go index 35e2a52d253b6d..bca2f50c9695da 100644 --- a/src/syscall/zerrors_dragonfly_amd64.go +++ b/src/syscall/zerrors_dragonfly_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && dragonfly -// +build amd64,dragonfly package syscall diff --git a/src/syscall/zerrors_freebsd_386.go b/src/syscall/zerrors_freebsd_386.go index aec26ad7785c8f..b1441e76a3c77d 100644 --- a/src/syscall/zerrors_freebsd_386.go +++ b/src/syscall/zerrors_freebsd_386.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m32 _const.go //go:build 386 && freebsd -// +build 386,freebsd package syscall diff --git a/src/syscall/zerrors_freebsd_amd64.go b/src/syscall/zerrors_freebsd_amd64.go index d6d13e4155865c..3aed004986d0be 100644 --- a/src/syscall/zerrors_freebsd_amd64.go +++ b/src/syscall/zerrors_freebsd_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && freebsd -// +build amd64,freebsd package syscall diff --git a/src/syscall/zerrors_freebsd_arm.go b/src/syscall/zerrors_freebsd_arm.go index 15c714fad81073..e1f91ff86cb7ec 100644 --- a/src/syscall/zerrors_freebsd_arm.go +++ b/src/syscall/zerrors_freebsd_arm.go @@ -5,7 +5,6 @@ // cgo -godefs -- _const.go //go:build arm && freebsd -// +build arm,freebsd package syscall diff --git a/src/syscall/zerrors_freebsd_arm64.go b/src/syscall/zerrors_freebsd_arm64.go index b20ce7d82319e2..d0cb6c8ac751ba 100644 --- a/src/syscall/zerrors_freebsd_arm64.go +++ b/src/syscall/zerrors_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build freebsd && arm64 -// +build freebsd,arm64 // Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs -- -m64 _const.go diff --git a/src/syscall/zerrors_linux_386.go b/src/syscall/zerrors_linux_386.go index fb64932ad62094..045a4166b0418b 100644 --- a/src/syscall/zerrors_linux_386.go +++ b/src/syscall/zerrors_linux_386.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m32 _const.go //go:build 386 && linux -// +build 386,linux package syscall @@ -149,28 +148,6 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 diff --git a/src/syscall/zerrors_linux_amd64.go b/src/syscall/zerrors_linux_amd64.go index 3a92bcdbb46b1e..4eb44746487634 100644 --- a/src/syscall/zerrors_linux_amd64.go +++ b/src/syscall/zerrors_linux_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && linux -// +build amd64,linux package syscall @@ -149,28 +148,6 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 diff --git a/src/syscall/zerrors_linux_arm.go b/src/syscall/zerrors_linux_arm.go index e013d8e7fd682f..a5f925ec6dde65 100644 --- a/src/syscall/zerrors_linux_arm.go +++ b/src/syscall/zerrors_linux_arm.go @@ -5,7 +5,6 @@ // cgo -godefs -- _const.go //go:build arm && linux -// +build arm,linux package syscall @@ -149,28 +148,6 @@ const ( BPF_TXA = 0x80 BPF_W = 0x0 BPF_X = 0x8 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 diff --git a/src/syscall/zerrors_linux_arm64.go b/src/syscall/zerrors_linux_arm64.go index 1a4d33e3fd5cc9..ec8ac0708e0e49 100644 --- a/src/syscall/zerrors_linux_arm64.go +++ b/src/syscall/zerrors_linux_arm64.go @@ -5,7 +5,6 @@ // cgo -godefs -- _const.go //go:build arm64 && linux -// +build arm64,linux package syscall @@ -193,28 +192,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_loong64.go b/src/syscall/zerrors_linux_loong64.go new file mode 100644 index 00000000000000..a28439cf67a8a0 --- /dev/null +++ b/src/syscall/zerrors_linux_loong64.go @@ -0,0 +1,2101 @@ +// mkerrors.sh +// Code generated by the command above; DO NOT EDIT. + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs -- _const.go + +package syscall + +const ( + AF_ALG = 0x26 + AF_APPLETALK = 0x5 + AF_ASH = 0x12 + AF_ATMPVC = 0x8 + AF_ATMSVC = 0x14 + AF_AX25 = 0x3 + AF_BLUETOOTH = 0x1f + AF_BRIDGE = 0x7 + AF_CAIF = 0x25 + AF_CAN = 0x1d + AF_DECnet = 0xc + AF_ECONET = 0x13 + AF_FILE = 0x1 + AF_IB = 0x1b + AF_IEEE802154 = 0x24 + AF_INET = 0x2 + AF_INET6 = 0xa + AF_IPX = 0x4 + AF_IRDA = 0x17 + AF_ISDN = 0x22 + AF_IUCV = 0x20 + AF_KCM = 0x29 + AF_KEY = 0xf + AF_LLC = 0x1a + AF_LOCAL = 0x1 + AF_MAX = 0x2e + AF_MCTP = 0x2d + AF_MPLS = 0x1c + AF_NETBEUI = 0xd + AF_NETLINK = 0x10 + AF_NETROM = 0x6 + AF_NFC = 0x27 + AF_PACKET = 0x11 + AF_PHONET = 0x23 + AF_PPPOX = 0x18 + AF_QIPCRTR = 0x2a + AF_RDS = 0x15 + AF_ROSE = 0xb + AF_ROUTE = 0x10 + AF_RXRPC = 0x21 + AF_SECURITY = 0xe + AF_SMC = 0x2b + AF_SNA = 0x16 + AF_TIPC = 0x1e + AF_UNIX = 0x1 + AF_UNSPEC = 0x0 + AF_VSOCK = 0x28 + AF_WANPIPE = 0x19 + AF_X25 = 0x9 + AF_XDP = 0x2c + ARPHRD_6LOWPAN = 0x339 + ARPHRD_ADAPT = 0x108 + ARPHRD_APPLETLK = 0x8 + ARPHRD_ARCNET = 0x7 + ARPHRD_ASH = 0x30d + ARPHRD_ATM = 0x13 + ARPHRD_AX25 = 0x3 + ARPHRD_BIF = 0x307 + ARPHRD_CAIF = 0x336 + ARPHRD_CAN = 0x118 + ARPHRD_CHAOS = 0x5 + ARPHRD_CISCO = 0x201 + ARPHRD_CSLIP = 0x101 + ARPHRD_CSLIP6 = 0x103 + ARPHRD_DDCMP = 0x205 + ARPHRD_DLCI = 0xf + ARPHRD_ECONET = 0x30e + ARPHRD_EETHER = 0x2 + ARPHRD_ETHER = 0x1 + ARPHRD_EUI64 = 0x1b + ARPHRD_FCAL = 0x311 + ARPHRD_FCFABRIC = 0x313 + ARPHRD_FCPL = 0x312 + ARPHRD_FCPP = 0x310 + ARPHRD_FDDI = 0x306 + ARPHRD_FRAD = 0x302 + ARPHRD_HDLC = 0x201 + ARPHRD_HIPPI = 0x30c + ARPHRD_HWX25 = 0x110 + ARPHRD_IEEE1394 = 0x18 + ARPHRD_IEEE802 = 0x6 + ARPHRD_IEEE80211 = 0x321 + ARPHRD_IEEE80211_PRISM = 0x322 + ARPHRD_IEEE80211_RADIOTAP = 0x323 + ARPHRD_IEEE802154 = 0x324 + ARPHRD_IEEE802154_MONITOR = 0x325 + ARPHRD_IEEE802_TR = 0x320 + ARPHRD_INFINIBAND = 0x20 + ARPHRD_IP6GRE = 0x337 + ARPHRD_IPDDP = 0x309 + ARPHRD_IPGRE = 0x30a + ARPHRD_IRDA = 0x30f + ARPHRD_LAPB = 0x204 + ARPHRD_LOCALTLK = 0x305 + ARPHRD_LOOPBACK = 0x304 + ARPHRD_MCTP = 0x122 + ARPHRD_METRICOM = 0x17 + ARPHRD_NETLINK = 0x338 + ARPHRD_NETROM = 0x0 + ARPHRD_NONE = 0xfffe + ARPHRD_PHONET = 0x334 + ARPHRD_PHONET_PIPE = 0x335 + ARPHRD_PIMREG = 0x30b + ARPHRD_PPP = 0x200 + ARPHRD_PRONET = 0x4 + ARPHRD_RAWHDLC = 0x206 + ARPHRD_RAWIP = 0x207 + ARPHRD_ROSE = 0x10e + ARPHRD_RSRVD = 0x104 + ARPHRD_SIT = 0x308 + ARPHRD_SKIP = 0x303 + ARPHRD_SLIP = 0x100 + ARPHRD_SLIP6 = 0x102 + ARPHRD_TUNNEL = 0x300 + ARPHRD_TUNNEL6 = 0x301 + ARPHRD_VOID = 0xffff + ARPHRD_VSOCKMON = 0x33a + ARPHRD_X25 = 0x10f + B0 = 0x0 + B1000000 = 0x1008 + B110 = 0x3 + B115200 = 0x1002 + B1152000 = 0x1009 + B1200 = 0x9 + B134 = 0x4 + B150 = 0x5 + B1500000 = 0x100a + B1800 = 0xa + B19200 = 0xe + B200 = 0x6 + B2000000 = 0x100b + B230400 = 0x1003 + B2400 = 0xb + B2500000 = 0x100c + B300 = 0x7 + B3000000 = 0x100d + B3500000 = 0x100e + B38400 = 0xf + B4000000 = 0x100f + B460800 = 0x1004 + B4800 = 0xc + B50 = 0x1 + B500000 = 0x1005 + B57600 = 0x1001 + B576000 = 0x1006 + B600 = 0x8 + B75 = 0x2 + B921600 = 0x1007 + B9600 = 0xd + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0x0 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0x0 + BPF_IND = 0x40 + BPF_JA = 0x0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0x0 + BPF_LD = 0x0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LL_OFF = -0x200000 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXINSNS = 0x1000 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MOD = 0x90 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_NET_OFF = -0x100000 + BPF_OR = 0x40 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0x0 + BPF_TXA = 0x80 + BPF_W = 0x0 + BPF_X = 0x8 + BPF_XOR = 0xa0 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x800 + CLONE_ARGS_SIZE_VER0 = 0x40 + CLONE_ARGS_SIZE_VER1 = 0x50 + CLONE_ARGS_SIZE_VER2 = 0x58 + CREAD = 0x80 + CS5 = 0x0 + CS6 = 0x10 + CS7 = 0x20 + CS8 = 0x30 + CSIGNAL = 0xff + CSIZE = 0x30 + CSTART = 0x11 + CSTATUS = 0x0 + CSTOP = 0x13 + CSTOPB = 0x40 + CSUSP = 0x1a + DT_BLK = 0x6 + DT_CHR = 0x2 + DT_DIR = 0x4 + DT_FIFO = 0x1 + DT_LNK = 0xa + DT_REG = 0x8 + DT_SOCK = 0xc + DT_UNKNOWN = 0x0 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x200 + ECHOE = 0x10 + ECHOK = 0x20 + ECHOKE = 0x800 + ECHONL = 0x40 + ECHOPRT = 0x400 + ENCODING_DEFAULT = 0x0 + ENCODING_FM_MARK = 0x3 + ENCODING_FM_SPACE = 0x4 + ENCODING_MANCHESTER = 0x5 + ENCODING_NRZ = 0x1 + ENCODING_NRZI = 0x2 + EPOLLERR = 0x8 + EPOLLET = 0x80000000 + EPOLLEXCLUSIVE = 0x10000000 + EPOLLHUP = 0x10 + EPOLLIN = 0x1 + EPOLLMSG = 0x400 + EPOLLONESHOT = 0x40000000 + EPOLLOUT = 0x4 + EPOLLPRI = 0x2 + EPOLLRDBAND = 0x80 + EPOLLRDHUP = 0x2000 + EPOLLRDNORM = 0x40 + EPOLLWAKEUP = 0x20000000 + EPOLLWRBAND = 0x200 + EPOLLWRNORM = 0x100 + EPOLL_CLOEXEC = 0x80000 + EPOLL_CTL_ADD = 0x1 + EPOLL_CTL_DEL = 0x2 + EPOLL_CTL_MOD = 0x3 + ETH_P_1588 = 0x88f7 + ETH_P_8021AD = 0x88a8 + ETH_P_8021AH = 0x88e7 + ETH_P_8021Q = 0x8100 + ETH_P_80221 = 0x8917 + ETH_P_802_2 = 0x4 + ETH_P_802_3 = 0x1 + ETH_P_802_3_MIN = 0x600 + ETH_P_802_EX1 = 0x88b5 + ETH_P_AARP = 0x80f3 + ETH_P_AF_IUCV = 0xfbfb + ETH_P_ALL = 0x3 + ETH_P_AOE = 0x88a2 + ETH_P_ARCNET = 0x1a + ETH_P_ARP = 0x806 + ETH_P_ATALK = 0x809b + ETH_P_ATMFATE = 0x8884 + ETH_P_ATMMPOA = 0x884c + ETH_P_AX25 = 0x2 + ETH_P_BATMAN = 0x4305 + ETH_P_BPQ = 0x8ff + ETH_P_CAIF = 0xf7 + ETH_P_CAN = 0xc + ETH_P_CANFD = 0xd + ETH_P_CFM = 0x8902 + ETH_P_CONTROL = 0x16 + ETH_P_CUST = 0x6006 + ETH_P_DDCMP = 0x6 + ETH_P_DEC = 0x6000 + ETH_P_DIAG = 0x6005 + ETH_P_DNA_DL = 0x6001 + ETH_P_DNA_RC = 0x6002 + ETH_P_DNA_RT = 0x6003 + ETH_P_DSA = 0x1b + ETH_P_DSA_8021Q = 0xdadb + ETH_P_ECONET = 0x18 + ETH_P_EDSA = 0xdada + ETH_P_ERSPAN = 0x88be + ETH_P_ERSPAN2 = 0x22eb + ETH_P_FCOE = 0x8906 + ETH_P_FIP = 0x8914 + ETH_P_HDLC = 0x19 + ETH_P_HSR = 0x892f + ETH_P_IBOE = 0x8915 + ETH_P_IEEE802154 = 0xf6 + ETH_P_IEEEPUP = 0xa00 + ETH_P_IEEEPUPAT = 0xa01 + ETH_P_IFE = 0xed3e + ETH_P_IP = 0x800 + ETH_P_IPV6 = 0x86dd + ETH_P_IPX = 0x8137 + ETH_P_IRDA = 0x17 + ETH_P_LAT = 0x6004 + ETH_P_LINK_CTL = 0x886c + ETH_P_LLDP = 0x88cc + ETH_P_LOCALTALK = 0x9 + ETH_P_LOOP = 0x60 + ETH_P_LOOPBACK = 0x9000 + ETH_P_MACSEC = 0x88e5 + ETH_P_MAP = 0xf9 + ETH_P_MCTP = 0xfa + ETH_P_MOBITEX = 0x15 + ETH_P_MPLS_MC = 0x8848 + ETH_P_MPLS_UC = 0x8847 + ETH_P_MRP = 0x88e3 + ETH_P_MVRP = 0x88f5 + ETH_P_NCSI = 0x88f8 + ETH_P_NSH = 0x894f + ETH_P_PAE = 0x888e + ETH_P_PAUSE = 0x8808 + ETH_P_PHONET = 0xf5 + ETH_P_PPPTALK = 0x10 + ETH_P_PPP_DISC = 0x8863 + ETH_P_PPP_MP = 0x8 + ETH_P_PPP_SES = 0x8864 + ETH_P_PREAUTH = 0x88c7 + ETH_P_PRP = 0x88fb + ETH_P_PUP = 0x200 + ETH_P_PUPAT = 0x201 + ETH_P_QINQ1 = 0x9100 + ETH_P_QINQ2 = 0x9200 + ETH_P_QINQ3 = 0x9300 + ETH_P_RARP = 0x8035 + ETH_P_REALTEK = 0x8899 + ETH_P_SCA = 0x6007 + ETH_P_SLOW = 0x8809 + ETH_P_SNAP = 0x5 + ETH_P_TDLS = 0x890d + ETH_P_TEB = 0x6558 + ETH_P_TIPC = 0x88ca + ETH_P_TRAILER = 0x1c + ETH_P_TR_802_2 = 0x11 + ETH_P_TSN = 0x22f0 + ETH_P_WAN_PPP = 0x7 + ETH_P_WCCP = 0x883e + ETH_P_X25 = 0x805 + ETH_P_XDSA = 0xf8 + EXTA = 0xe + EXTB = 0xf + EXTPROC = 0x10000 + FD_CLOEXEC = 0x1 + FD_SETSIZE = 0x400 + FLUSHO = 0x1000 + F_ADD_SEALS = 0x409 + F_DUPFD = 0x0 + F_DUPFD_CLOEXEC = 0x406 + F_EXLCK = 0x4 + F_GETFD = 0x1 + F_GETFL = 0x3 + F_GETLEASE = 0x401 + F_GETLK = 0x5 + F_GETLK64 = 0x5 + F_GETOWN = 0x9 + F_GETOWN_EX = 0x10 + F_GETPIPE_SZ = 0x408 + F_GETSIG = 0xb + F_GET_FILE_RW_HINT = 0x40d + F_GET_RW_HINT = 0x40b + F_GET_SEALS = 0x40a + F_LOCK = 0x1 + F_NOTIFY = 0x402 + F_OFD_GETLK = 0x24 + F_OFD_SETLK = 0x25 + F_OFD_SETLKW = 0x26 + F_OK = 0x0 + F_RDLCK = 0x0 + F_SEAL_FUTURE_WRITE = 0x10 + F_SEAL_GROW = 0x4 + F_SEAL_SEAL = 0x1 + F_SEAL_SHRINK = 0x2 + F_SEAL_WRITE = 0x8 + F_SETFD = 0x2 + F_SETFL = 0x4 + F_SETLEASE = 0x400 + F_SETLK = 0x6 + F_SETLK64 = 0x6 + F_SETLKW = 0x7 + F_SETLKW64 = 0x7 + F_SETOWN = 0x8 + F_SETOWN_EX = 0xf + F_SETPIPE_SZ = 0x407 + F_SETSIG = 0xa + F_SET_FILE_RW_HINT = 0x40e + F_SET_RW_HINT = 0x40c + F_SHLCK = 0x8 + F_TEST = 0x3 + F_TLOCK = 0x2 + F_ULOCK = 0x0 + F_UNLCK = 0x2 + F_WRLCK = 0x1 + HUPCL = 0x400 + ICANON = 0x2 + ICMPV6_FILTER = 0x1 + ICRNL = 0x100 + IEXTEN = 0x8000 + IFA_F_DADFAILED = 0x8 + IFA_F_DEPRECATED = 0x20 + IFA_F_HOMEADDRESS = 0x10 + IFA_F_MANAGETEMPADDR = 0x100 + IFA_F_MCAUTOJOIN = 0x400 + IFA_F_NODAD = 0x2 + IFA_F_NOPREFIXROUTE = 0x200 + IFA_F_OPTIMISTIC = 0x4 + IFA_F_PERMANENT = 0x80 + IFA_F_SECONDARY = 0x1 + IFA_F_STABLE_PRIVACY = 0x800 + IFA_F_TEMPORARY = 0x1 + IFA_F_TENTATIVE = 0x40 + IFA_MAX = 0xa + IFF_ALLMULTI = 0x200 + IFF_ATTACH_QUEUE = 0x200 + IFF_AUTOMEDIA = 0x4000 + IFF_BROADCAST = 0x2 + IFF_DEBUG = 0x4 + IFF_DETACH_QUEUE = 0x400 + IFF_DORMANT = 0x20000 + IFF_DYNAMIC = 0x8000 + IFF_ECHO = 0x40000 + IFF_LOOPBACK = 0x8 + IFF_LOWER_UP = 0x10000 + IFF_MASTER = 0x400 + IFF_MULTICAST = 0x1000 + IFF_MULTI_QUEUE = 0x100 + IFF_NAPI = 0x10 + IFF_NAPI_FRAGS = 0x20 + IFF_NOARP = 0x80 + IFF_NOFILTER = 0x1000 + IFF_NOTRAILERS = 0x20 + IFF_NO_PI = 0x1000 + IFF_ONE_QUEUE = 0x2000 + IFF_PERSIST = 0x800 + IFF_POINTOPOINT = 0x10 + IFF_PORTSEL = 0x2000 + IFF_PROMISC = 0x100 + IFF_RUNNING = 0x40 + IFF_SLAVE = 0x800 + IFF_TAP = 0x2 + IFF_TUN = 0x1 + IFF_TUN_EXCL = 0x8000 + IFF_UP = 0x1 + IFF_VNET_HDR = 0x4000 + IFF_VOLATILE = 0x70c5a + IFNAMSIZ = 0x10 + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 + IN_ACCESS = 0x1 + IN_ALL_EVENTS = 0xfff + IN_ATTRIB = 0x4 + IN_CLASSA_HOST = 0xffffff + IN_CLASSA_MAX = 0x80 + IN_CLASSA_NET = 0xff000000 + IN_CLASSA_NSHIFT = 0x18 + IN_CLASSB_HOST = 0xffff + IN_CLASSB_MAX = 0x10000 + IN_CLASSB_NET = 0xffff0000 + IN_CLASSB_NSHIFT = 0x10 + IN_CLASSC_HOST = 0xff + IN_CLASSC_NET = 0xffffff00 + IN_CLASSC_NSHIFT = 0x8 + IN_CLOEXEC = 0x80000 + IN_CLOSE = 0x18 + IN_CLOSE_NOWRITE = 0x10 + IN_CLOSE_WRITE = 0x8 + IN_CREATE = 0x100 + IN_DELETE = 0x200 + IN_DELETE_SELF = 0x400 + IN_DONT_FOLLOW = 0x2000000 + IN_EXCL_UNLINK = 0x4000000 + IN_IGNORED = 0x8000 + IN_ISDIR = 0x40000000 + IN_LOOPBACKNET = 0x7f + IN_MASK_ADD = 0x20000000 + IN_MASK_CREATE = 0x10000000 + IN_MODIFY = 0x2 + IN_MOVE = 0xc0 + IN_MOVED_FROM = 0x40 + IN_MOVED_TO = 0x80 + IN_MOVE_SELF = 0x800 + IN_NONBLOCK = 0x800 + IN_ONESHOT = 0x80000000 + IN_ONLYDIR = 0x1000000 + IN_OPEN = 0x20 + IN_Q_OVERFLOW = 0x4000 + IN_UNMOUNT = 0x2000 + IPPROTO_AH = 0x33 + IPPROTO_BEETPH = 0x5e + IPPROTO_COMP = 0x6c + IPPROTO_DCCP = 0x21 + IPPROTO_DSTOPTS = 0x3c + IPPROTO_EGP = 0x8 + IPPROTO_ENCAP = 0x62 + IPPROTO_ESP = 0x32 + IPPROTO_ETHERNET = 0x8f + IPPROTO_FRAGMENT = 0x2c + IPPROTO_GRE = 0x2f + IPPROTO_HOPOPTS = 0x0 + IPPROTO_ICMP = 0x1 + IPPROTO_ICMPV6 = 0x3a + IPPROTO_IDP = 0x16 + IPPROTO_IGMP = 0x2 + IPPROTO_IP = 0x0 + IPPROTO_IPIP = 0x4 + IPPROTO_IPV6 = 0x29 + IPPROTO_MH = 0x87 + IPPROTO_MPLS = 0x89 + IPPROTO_MPTCP = 0x106 + IPPROTO_MTP = 0x5c + IPPROTO_NONE = 0x3b + IPPROTO_PIM = 0x67 + IPPROTO_PUP = 0xc + IPPROTO_RAW = 0xff + IPPROTO_ROUTING = 0x2b + IPPROTO_RSVP = 0x2e + IPPROTO_SCTP = 0x84 + IPPROTO_TCP = 0x6 + IPPROTO_TP = 0x1d + IPPROTO_UDP = 0x11 + IPPROTO_UDPLITE = 0x88 + IPV6_2292DSTOPTS = 0x4 + IPV6_2292HOPLIMIT = 0x8 + IPV6_2292HOPOPTS = 0x3 + IPV6_2292PKTINFO = 0x2 + IPV6_2292PKTOPTIONS = 0x6 + IPV6_2292RTHDR = 0x5 + IPV6_ADDRFORM = 0x1 + IPV6_ADDR_PREFERENCES = 0x48 + IPV6_ADD_MEMBERSHIP = 0x14 + IPV6_AUTHHDR = 0xa + IPV6_AUTOFLOWLABEL = 0x46 + IPV6_CHECKSUM = 0x7 + IPV6_DONTFRAG = 0x3e + IPV6_DROP_MEMBERSHIP = 0x15 + IPV6_DSTOPTS = 0x3b + IPV6_FREEBIND = 0x4e + IPV6_HDRINCL = 0x24 + IPV6_HOPLIMIT = 0x34 + IPV6_HOPOPTS = 0x36 + IPV6_IPSEC_POLICY = 0x22 + IPV6_JOIN_ANYCAST = 0x1b + IPV6_JOIN_GROUP = 0x14 + IPV6_LEAVE_ANYCAST = 0x1c + IPV6_LEAVE_GROUP = 0x15 + IPV6_MINHOPCOUNT = 0x49 + IPV6_MTU = 0x18 + IPV6_MTU_DISCOVER = 0x17 + IPV6_MULTICAST_ALL = 0x1d + IPV6_MULTICAST_HOPS = 0x12 + IPV6_MULTICAST_IF = 0x11 + IPV6_MULTICAST_LOOP = 0x13 + IPV6_NEXTHOP = 0x9 + IPV6_ORIGDSTADDR = 0x4a + IPV6_PATHMTU = 0x3d + IPV6_PKTINFO = 0x32 + IPV6_PMTUDISC_DO = 0x2 + IPV6_PMTUDISC_DONT = 0x0 + IPV6_PMTUDISC_INTERFACE = 0x4 + IPV6_PMTUDISC_OMIT = 0x5 + IPV6_PMTUDISC_PROBE = 0x3 + IPV6_PMTUDISC_WANT = 0x1 + IPV6_RECVDSTOPTS = 0x3a + IPV6_RECVERR = 0x19 + IPV6_RECVERR_RFC4884 = 0x1f + IPV6_RECVFRAGSIZE = 0x4d + IPV6_RECVHOPLIMIT = 0x33 + IPV6_RECVHOPOPTS = 0x35 + IPV6_RECVORIGDSTADDR = 0x4a + IPV6_RECVPATHMTU = 0x3c + IPV6_RECVPKTINFO = 0x31 + IPV6_RECVRTHDR = 0x38 + IPV6_RECVTCLASS = 0x42 + IPV6_ROUTER_ALERT = 0x16 + IPV6_ROUTER_ALERT_ISOLATE = 0x1e + IPV6_RTHDR = 0x39 + IPV6_RTHDRDSTOPTS = 0x37 + IPV6_RTHDR_LOOSE = 0x0 + IPV6_RTHDR_STRICT = 0x1 + IPV6_RTHDR_TYPE_0 = 0x0 + IPV6_RXDSTOPTS = 0x3b + IPV6_RXHOPOPTS = 0x36 + IPV6_TCLASS = 0x43 + IPV6_TRANSPARENT = 0x4b + IPV6_UNICAST_HOPS = 0x10 + IPV6_UNICAST_IF = 0x4c + IPV6_V6ONLY = 0x1a + IPV6_XFRM_POLICY = 0x23 + IP_ADD_MEMBERSHIP = 0x23 + IP_ADD_SOURCE_MEMBERSHIP = 0x27 + IP_BIND_ADDRESS_NO_PORT = 0x18 + IP_BLOCK_SOURCE = 0x26 + IP_CHECKSUM = 0x17 + IP_DEFAULT_MULTICAST_LOOP = 0x1 + IP_DEFAULT_MULTICAST_TTL = 0x1 + IP_DF = 0x4000 + IP_DROP_MEMBERSHIP = 0x24 + IP_DROP_SOURCE_MEMBERSHIP = 0x28 + IP_FREEBIND = 0xf + IP_HDRINCL = 0x3 + IP_IPSEC_POLICY = 0x10 + IP_MAXPACKET = 0xffff + IP_MAX_MEMBERSHIPS = 0x14 + IP_MF = 0x2000 + IP_MINTTL = 0x15 + IP_MSFILTER = 0x29 + IP_MSS = 0x240 + IP_MTU = 0xe + IP_MTU_DISCOVER = 0xa + IP_MULTICAST_ALL = 0x31 + IP_MULTICAST_IF = 0x20 + IP_MULTICAST_LOOP = 0x22 + IP_MULTICAST_TTL = 0x21 + IP_NODEFRAG = 0x16 + IP_OFFMASK = 0x1fff + IP_OPTIONS = 0x4 + IP_ORIGDSTADDR = 0x14 + IP_PASSSEC = 0x12 + IP_PKTINFO = 0x8 + IP_PKTOPTIONS = 0x9 + IP_PMTUDISC = 0xa + IP_PMTUDISC_DO = 0x2 + IP_PMTUDISC_DONT = 0x0 + IP_PMTUDISC_INTERFACE = 0x4 + IP_PMTUDISC_OMIT = 0x5 + IP_PMTUDISC_PROBE = 0x3 + IP_PMTUDISC_WANT = 0x1 + IP_RECVERR = 0xb + IP_RECVERR_RFC4884 = 0x1a + IP_RECVFRAGSIZE = 0x19 + IP_RECVOPTS = 0x6 + IP_RECVORIGDSTADDR = 0x14 + IP_RECVRETOPTS = 0x7 + IP_RECVTOS = 0xd + IP_RECVTTL = 0xc + IP_RETOPTS = 0x7 + IP_RF = 0x8000 + IP_ROUTER_ALERT = 0x5 + IP_TOS = 0x1 + IP_TRANSPARENT = 0x13 + IP_TTL = 0x2 + IP_UNBLOCK_SOURCE = 0x25 + IP_UNICAST_IF = 0x32 + IP_XFRM_POLICY = 0x11 + ISIG = 0x1 + ISTRIP = 0x20 + IUTF8 = 0x4000 + IXANY = 0x800 + IXOFF = 0x1000 + IXON = 0x400 + LINUX_REBOOT_CMD_CAD_OFF = 0x0 + LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef + LINUX_REBOOT_CMD_HALT = 0xcdef0123 + LINUX_REBOOT_CMD_KEXEC = 0x45584543 + LINUX_REBOOT_CMD_POWER_OFF = 0x4321fedc + LINUX_REBOOT_CMD_RESTART = 0x1234567 + LINUX_REBOOT_CMD_RESTART2 = 0xa1b2c3d4 + LINUX_REBOOT_CMD_SW_SUSPEND = 0xd000fce2 + LINUX_REBOOT_MAGIC1 = 0xfee1dead + LINUX_REBOOT_MAGIC2 = 0x28121969 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 + MADV_COLD = 0x14 + MADV_DODUMP = 0x11 + MADV_DOFORK = 0xb + MADV_DONTDUMP = 0x10 + MADV_DONTFORK = 0xa + MADV_DONTNEED = 0x4 + MADV_FREE = 0x8 + MADV_HUGEPAGE = 0xe + MADV_HWPOISON = 0x64 + MADV_KEEPONFORK = 0x13 + MADV_MERGEABLE = 0xc + MADV_NOHUGEPAGE = 0xf + MADV_NORMAL = 0x0 + MADV_PAGEOUT = 0x15 + MADV_POPULATE_READ = 0x16 + MADV_POPULATE_WRITE = 0x17 + MADV_RANDOM = 0x1 + MADV_REMOVE = 0x9 + MADV_SEQUENTIAL = 0x2 + MADV_UNMERGEABLE = 0xd + MADV_WILLNEED = 0x3 + MADV_WIPEONFORK = 0x12 + MAP_ANON = 0x20 + MAP_ANONYMOUS = 0x20 + MAP_DENYWRITE = 0x800 + MAP_EXECUTABLE = 0x1000 + MAP_FILE = 0x0 + MAP_FIXED = 0x10 + MAP_FIXED_NOREPLACE = 0x100000 + MAP_GROWSDOWN = 0x100 + MAP_HUGETLB = 0x40000 + MAP_HUGE_MASK = 0x3f + MAP_HUGE_SHIFT = 0x1a + MAP_LOCKED = 0x2000 + MAP_NONBLOCK = 0x10000 + MAP_NORESERVE = 0x4000 + MAP_POPULATE = 0x8000 + MAP_PRIVATE = 0x2 + MAP_SHARED = 0x1 + MAP_SHARED_VALIDATE = 0x3 + MAP_STACK = 0x20000 + MAP_SYNC = 0x80000 + MAP_TYPE = 0xf + MCL_CURRENT = 0x1 + MCL_FUTURE = 0x2 + MCL_ONFAULT = 0x4 + MNT_DETACH = 0x2 + MNT_EXPIRE = 0x4 + MNT_FORCE = 0x1 + MSG_BATCH = 0x40000 + MSG_CMSG_CLOEXEC = 0x40000000 + MSG_CONFIRM = 0x800 + MSG_CTRUNC = 0x8 + MSG_DONTROUTE = 0x4 + MSG_DONTWAIT = 0x40 + MSG_EOR = 0x80 + MSG_ERRQUEUE = 0x2000 + MSG_FASTOPEN = 0x20000000 + MSG_FIN = 0x200 + MSG_MORE = 0x8000 + MSG_NOSIGNAL = 0x4000 + MSG_OOB = 0x1 + MSG_PEEK = 0x2 + MSG_PROXY = 0x10 + MSG_RST = 0x1000 + MSG_SYN = 0x400 + MSG_TRUNC = 0x20 + MSG_TRYHARD = 0x4 + MSG_WAITALL = 0x100 + MSG_WAITFORONE = 0x10000 + MSG_ZEROCOPY = 0x4000000 + MS_ACTIVE = 0x40000000 + MS_ASYNC = 0x1 + MS_BIND = 0x1000 + MS_DIRSYNC = 0x80 + MS_INVALIDATE = 0x2 + MS_I_VERSION = 0x800000 + MS_KERNMOUNT = 0x400000 + MS_LAZYTIME = 0x2000000 + MS_MANDLOCK = 0x40 + MS_MGC_MSK = 0xffff0000 + MS_MGC_VAL = 0xc0ed0000 + MS_MOVE = 0x2000 + MS_NOATIME = 0x400 + MS_NODEV = 0x4 + MS_NODIRATIME = 0x800 + MS_NOEXEC = 0x8 + MS_NOSUID = 0x2 + MS_NOSYMFOLLOW = 0x100 + MS_NOUSER = -0x80000000 + MS_POSIXACL = 0x10000 + MS_PRIVATE = 0x40000 + MS_RDONLY = 0x1 + MS_REC = 0x4000 + MS_RELATIME = 0x200000 + MS_REMOUNT = 0x20 + MS_RMT_MASK = 0x2800051 + MS_SHARED = 0x100000 + MS_SILENT = 0x8000 + MS_SLAVE = 0x80000 + MS_STRICTATIME = 0x1000000 + MS_SYNC = 0x4 + MS_SYNCHRONOUS = 0x10 + MS_UNBINDABLE = 0x20000 + NAME_MAX = 0xff + NETLINK_ADD_MEMBERSHIP = 0x1 + NETLINK_AUDIT = 0x9 + NETLINK_BROADCAST_ERROR = 0x4 + NETLINK_CAP_ACK = 0xa + NETLINK_CONNECTOR = 0xb + NETLINK_CRYPTO = 0x15 + NETLINK_DNRTMSG = 0xe + NETLINK_DROP_MEMBERSHIP = 0x2 + NETLINK_ECRYPTFS = 0x13 + NETLINK_EXT_ACK = 0xb + NETLINK_FIB_LOOKUP = 0xa + NETLINK_FIREWALL = 0x3 + NETLINK_GENERIC = 0x10 + NETLINK_GET_STRICT_CHK = 0xc + NETLINK_INET_DIAG = 0x4 + NETLINK_IP6_FW = 0xd + NETLINK_ISCSI = 0x8 + NETLINK_KOBJECT_UEVENT = 0xf + NETLINK_LISTEN_ALL_NSID = 0x8 + NETLINK_LIST_MEMBERSHIPS = 0x9 + NETLINK_NETFILTER = 0xc + NETLINK_NFLOG = 0x5 + NETLINK_NO_ENOBUFS = 0x5 + NETLINK_PKTINFO = 0x3 + NETLINK_RDMA = 0x14 + NETLINK_ROUTE = 0x0 + NETLINK_RX_RING = 0x6 + NETLINK_SCSITRANSPORT = 0x12 + NETLINK_SELINUX = 0x7 + NETLINK_SMC = 0x16 + NETLINK_SOCK_DIAG = 0x4 + NETLINK_TX_RING = 0x7 + NETLINK_UNUSED = 0x1 + NETLINK_USERSOCK = 0x2 + NETLINK_XFRM = 0x6 + NLA_ALIGNTO = 0x4 + NLA_F_NESTED = 0x8000 + NLA_F_NET_BYTEORDER = 0x4000 + NLA_HDRLEN = 0x4 + NLMSG_ALIGNTO = 0x4 + NLMSG_DONE = 0x3 + NLMSG_ERROR = 0x2 + NLMSG_HDRLEN = 0x10 + NLMSG_MIN_TYPE = 0x10 + NLMSG_NOOP = 0x1 + NLMSG_OVERRUN = 0x4 + NLM_F_ACK = 0x4 + NLM_F_ACK_TLVS = 0x200 + NLM_F_APPEND = 0x800 + NLM_F_ATOMIC = 0x400 + NLM_F_CAPPED = 0x100 + NLM_F_CREATE = 0x400 + NLM_F_DUMP = 0x300 + NLM_F_DUMP_FILTERED = 0x20 + NLM_F_DUMP_INTR = 0x10 + NLM_F_ECHO = 0x8 + NLM_F_EXCL = 0x200 + NLM_F_MATCH = 0x200 + NLM_F_MULTI = 0x2 + NLM_F_NONREC = 0x100 + NLM_F_REPLACE = 0x100 + NLM_F_REQUEST = 0x1 + NLM_F_ROOT = 0x100 + NOFLSH = 0x80 + OCRNL = 0x8 + OFDEL = 0x80 + OFILL = 0x40 + ONLCR = 0x4 + ONLRET = 0x20 + ONOCR = 0x10 + OPOST = 0x1 + O_ACCMODE = 0x3 + O_APPEND = 0x400 + O_ASYNC = 0x2000 + O_CLOEXEC = 0x80000 + O_CREAT = 0x40 + O_DIRECT = 0x4000 + O_DIRECTORY = 0x10000 + O_DSYNC = 0x1000 + O_EXCL = 0x80 + O_FSYNC = 0x101000 + O_LARGEFILE = 0x0 + O_NDELAY = 0x800 + O_NOATIME = 0x40000 + O_NOCTTY = 0x100 + O_NOFOLLOW = 0x20000 + O_NONBLOCK = 0x800 + O_PATH = 0x200000 + O_RDONLY = 0x0 + O_RDWR = 0x2 + O_RSYNC = 0x101000 + O_SYNC = 0x101000 + O_TMPFILE = 0x410000 + O_TRUNC = 0x200 + O_WRONLY = 0x1 + PACKET_ADD_MEMBERSHIP = 0x1 + PACKET_AUXDATA = 0x8 + PACKET_BROADCAST = 0x1 + PACKET_COPY_THRESH = 0x7 + PACKET_DROP_MEMBERSHIP = 0x2 + PACKET_FANOUT = 0x12 + PACKET_FANOUT_CBPF = 0x6 + PACKET_FANOUT_CPU = 0x2 + PACKET_FANOUT_DATA = 0x16 + PACKET_FANOUT_EBPF = 0x7 + PACKET_FANOUT_FLAG_DEFRAG = 0x8000 + PACKET_FANOUT_FLAG_ROLLOVER = 0x1000 + PACKET_FANOUT_FLAG_UNIQUEID = 0x2000 + PACKET_FANOUT_HASH = 0x0 + PACKET_FANOUT_LB = 0x1 + PACKET_FANOUT_QM = 0x5 + PACKET_FANOUT_RND = 0x4 + PACKET_FANOUT_ROLLOVER = 0x3 + PACKET_FASTROUTE = 0x6 + PACKET_HDRLEN = 0xb + PACKET_HOST = 0x0 + PACKET_IGNORE_OUTGOING = 0x17 + PACKET_KERNEL = 0x7 + PACKET_LOOPBACK = 0x5 + PACKET_LOSS = 0xe + PACKET_MR_ALLMULTI = 0x2 + PACKET_MR_MULTICAST = 0x0 + PACKET_MR_PROMISC = 0x1 + PACKET_MR_UNICAST = 0x3 + PACKET_MULTICAST = 0x2 + PACKET_ORIGDEV = 0x9 + PACKET_OTHERHOST = 0x3 + PACKET_OUTGOING = 0x4 + PACKET_QDISC_BYPASS = 0x14 + PACKET_RECV_OUTPUT = 0x3 + PACKET_RESERVE = 0xc + PACKET_ROLLOVER_STATS = 0x15 + PACKET_RX_RING = 0x5 + PACKET_STATISTICS = 0x6 + PACKET_TIMESTAMP = 0x11 + PACKET_TX_HAS_OFF = 0x13 + PACKET_TX_RING = 0xd + PACKET_TX_TIMESTAMP = 0x10 + PACKET_USER = 0x6 + PACKET_VERSION = 0xa + PACKET_VNET_HDR = 0xf + PARENB = 0x100 + PARITY_CRC16_PR0 = 0x2 + PARITY_CRC16_PR0_CCITT = 0x4 + PARITY_CRC16_PR1 = 0x3 + PARITY_CRC16_PR1_CCITT = 0x5 + PARITY_CRC32_PR0_CCITT = 0x6 + PARITY_CRC32_PR1_CCITT = 0x7 + PARITY_DEFAULT = 0x0 + PARITY_NONE = 0x1 + PARMRK = 0x8 + PARODD = 0x200 + PENDIN = 0x4000 + PRIO_PGRP = 0x1 + PRIO_PROCESS = 0x0 + PRIO_USER = 0x2 + PROT_EXEC = 0x4 + PROT_GROWSDOWN = 0x1000000 + PROT_GROWSUP = 0x2000000 + PROT_NONE = 0x0 + PROT_READ = 0x1 + PROT_WRITE = 0x2 + PR_CAPBSET_DROP = 0x18 + PR_CAPBSET_READ = 0x17 + PR_CAP_AMBIENT = 0x2f + PR_CAP_AMBIENT_CLEAR_ALL = 0x4 + PR_CAP_AMBIENT_IS_SET = 0x1 + PR_CAP_AMBIENT_LOWER = 0x3 + PR_CAP_AMBIENT_RAISE = 0x2 + PR_ENDIAN_BIG = 0x0 + PR_ENDIAN_LITTLE = 0x1 + PR_ENDIAN_PPC_LITTLE = 0x2 + PR_FPEMU_NOPRINT = 0x1 + PR_FPEMU_SIGFPE = 0x2 + PR_FP_EXC_ASYNC = 0x2 + PR_FP_EXC_DISABLED = 0x0 + PR_FP_EXC_DIV = 0x10000 + PR_FP_EXC_INV = 0x100000 + PR_FP_EXC_NONRECOV = 0x1 + PR_FP_EXC_OVF = 0x20000 + PR_FP_EXC_PRECISE = 0x3 + PR_FP_EXC_RES = 0x80000 + PR_FP_EXC_SW_ENABLE = 0x80 + PR_FP_EXC_UND = 0x40000 + PR_FP_MODE_FR = 0x1 + PR_FP_MODE_FRE = 0x2 + PR_GET_CHILD_SUBREAPER = 0x25 + PR_GET_DUMPABLE = 0x3 + PR_GET_ENDIAN = 0x13 + PR_GET_FPEMU = 0x9 + PR_GET_FPEXC = 0xb + PR_GET_FP_MODE = 0x2e + PR_GET_IO_FLUSHER = 0x3a + PR_GET_KEEPCAPS = 0x7 + PR_GET_NAME = 0x10 + PR_GET_NO_NEW_PRIVS = 0x27 + PR_GET_PDEATHSIG = 0x2 + PR_GET_SECCOMP = 0x15 + PR_GET_SECUREBITS = 0x1b + PR_GET_SPECULATION_CTRL = 0x34 + PR_GET_TAGGED_ADDR_CTRL = 0x38 + PR_GET_THP_DISABLE = 0x2a + PR_GET_TID_ADDRESS = 0x28 + PR_GET_TIMERSLACK = 0x1e + PR_GET_TIMING = 0xd + PR_GET_TSC = 0x19 + PR_GET_UNALIGN = 0x5 + PR_MCE_KILL = 0x21 + PR_MCE_KILL_CLEAR = 0x0 + PR_MCE_KILL_DEFAULT = 0x2 + PR_MCE_KILL_EARLY = 0x1 + PR_MCE_KILL_GET = 0x22 + PR_MCE_KILL_LATE = 0x0 + PR_MCE_KILL_SET = 0x1 + PR_MPX_DISABLE_MANAGEMENT = 0x2c + PR_MPX_ENABLE_MANAGEMENT = 0x2b + PR_MTE_TAG_MASK = 0x7fff8 + PR_MTE_TAG_SHIFT = 0x3 + PR_MTE_TCF_ASYNC = 0x4 + PR_MTE_TCF_MASK = 0x6 + PR_MTE_TCF_NONE = 0x0 + PR_MTE_TCF_SHIFT = 0x1 + PR_MTE_TCF_SYNC = 0x2 + PR_PAC_APDAKEY = 0x4 + PR_PAC_APDBKEY = 0x8 + PR_PAC_APGAKEY = 0x10 + PR_PAC_APIAKEY = 0x1 + PR_PAC_APIBKEY = 0x2 + PR_PAC_GET_ENABLED_KEYS = 0x3d + PR_PAC_RESET_KEYS = 0x36 + PR_PAC_SET_ENABLED_KEYS = 0x3c + PR_SCHED_CORE = 0x3e + PR_SCHED_CORE_CREATE = 0x1 + PR_SCHED_CORE_GET = 0x0 + PR_SCHED_CORE_MAX = 0x4 + PR_SCHED_CORE_SCOPE_PROCESS_GROUP = 0x2 + PR_SCHED_CORE_SCOPE_THREAD = 0x0 + PR_SCHED_CORE_SCOPE_THREAD_GROUP = 0x1 + PR_SCHED_CORE_SHARE_FROM = 0x3 + PR_SCHED_CORE_SHARE_TO = 0x2 + PR_SET_CHILD_SUBREAPER = 0x24 + PR_SET_DUMPABLE = 0x4 + PR_SET_ENDIAN = 0x14 + PR_SET_FPEMU = 0xa + PR_SET_FPEXC = 0xc + PR_SET_FP_MODE = 0x2d + PR_SET_IO_FLUSHER = 0x39 + PR_SET_KEEPCAPS = 0x8 + PR_SET_MM = 0x23 + PR_SET_MM_ARG_END = 0x9 + PR_SET_MM_ARG_START = 0x8 + PR_SET_MM_AUXV = 0xc + PR_SET_MM_BRK = 0x7 + PR_SET_MM_END_CODE = 0x2 + PR_SET_MM_END_DATA = 0x4 + PR_SET_MM_ENV_END = 0xb + PR_SET_MM_ENV_START = 0xa + PR_SET_MM_EXE_FILE = 0xd + PR_SET_MM_MAP = 0xe + PR_SET_MM_MAP_SIZE = 0xf + PR_SET_MM_START_BRK = 0x6 + PR_SET_MM_START_CODE = 0x1 + PR_SET_MM_START_DATA = 0x3 + PR_SET_MM_START_STACK = 0x5 + PR_SET_NAME = 0xf + PR_SET_NO_NEW_PRIVS = 0x26 + PR_SET_PDEATHSIG = 0x1 + PR_SET_PTRACER = 0x59616d61 + PR_SET_PTRACER_ANY = 0xffffffffffffffff + PR_SET_SECCOMP = 0x16 + PR_SET_SECUREBITS = 0x1c + PR_SET_SPECULATION_CTRL = 0x35 + PR_SET_SYSCALL_USER_DISPATCH = 0x3b + PR_SET_TAGGED_ADDR_CTRL = 0x37 + PR_SET_THP_DISABLE = 0x29 + PR_SET_TIMERSLACK = 0x1d + PR_SET_TIMING = 0xe + PR_SET_TSC = 0x1a + PR_SET_UNALIGN = 0x6 + PR_SET_VMA = 0x53564d41 + PR_SET_VMA_ANON_NAME = 0x0 + PR_SPEC_DISABLE = 0x4 + PR_SPEC_DISABLE_NOEXEC = 0x10 + PR_SPEC_ENABLE = 0x2 + PR_SPEC_FORCE_DISABLE = 0x8 + PR_SPEC_INDIRECT_BRANCH = 0x1 + PR_SPEC_L1D_FLUSH = 0x2 + PR_SPEC_NOT_AFFECTED = 0x0 + PR_SPEC_PRCTL = 0x1 + PR_SPEC_STORE_BYPASS = 0x0 + PR_SVE_GET_VL = 0x33 + PR_SVE_SET_VL = 0x32 + PR_SVE_SET_VL_ONEXEC = 0x40000 + PR_SVE_VL_INHERIT = 0x20000 + PR_SVE_VL_LEN_MASK = 0xffff + PR_SYS_DISPATCH_OFF = 0x0 + PR_SYS_DISPATCH_ON = 0x1 + PR_TAGGED_ADDR_ENABLE = 0x1 + PR_TASK_PERF_EVENTS_DISABLE = 0x1f + PR_TASK_PERF_EVENTS_ENABLE = 0x20 + PR_TIMING_STATISTICAL = 0x0 + PR_TIMING_TIMESTAMP = 0x1 + PR_TSC_ENABLE = 0x1 + PR_TSC_SIGSEGV = 0x2 + PR_UNALIGN_NOPRINT = 0x1 + PR_UNALIGN_SIGBUS = 0x2 + PTRACE_ATTACH = 0x10 + PTRACE_CONT = 0x7 + PTRACE_DETACH = 0x11 + PTRACE_EVENTMSG_SYSCALL_ENTRY = 0x1 + PTRACE_EVENTMSG_SYSCALL_EXIT = 0x2 + PTRACE_EVENT_CLONE = 0x3 + PTRACE_EVENT_EXEC = 0x4 + PTRACE_EVENT_EXIT = 0x6 + PTRACE_EVENT_FORK = 0x1 + PTRACE_EVENT_SECCOMP = 0x7 + PTRACE_EVENT_STOP = 0x80 + PTRACE_EVENT_VFORK = 0x2 + PTRACE_EVENT_VFORK_DONE = 0x5 + PTRACE_GETEVENTMSG = 0x4201 + PTRACE_GETREGS = 0xc + PTRACE_GETREGSET = 0x4204 + PTRACE_GETSIGINFO = 0x4202 + PTRACE_GETSIGMASK = 0x420a + PTRACE_GET_RSEQ_CONFIGURATION = 0x420f + PTRACE_GET_SYSCALL_INFO = 0x420e + PTRACE_INTERRUPT = 0x4207 + PTRACE_KILL = 0x8 + PTRACE_LISTEN = 0x4208 + PTRACE_O_EXITKILL = 0x100000 + PTRACE_O_MASK = 0x3000ff + PTRACE_O_SUSPEND_SECCOMP = 0x200000 + PTRACE_O_TRACECLONE = 0x8 + PTRACE_O_TRACEEXEC = 0x10 + PTRACE_O_TRACEEXIT = 0x40 + PTRACE_O_TRACEFORK = 0x2 + PTRACE_O_TRACESECCOMP = 0x80 + PTRACE_O_TRACESYSGOOD = 0x1 + PTRACE_O_TRACEVFORK = 0x4 + PTRACE_O_TRACEVFORKDONE = 0x20 + PTRACE_PEEKDATA = 0x2 + PTRACE_PEEKSIGINFO = 0x4209 + PTRACE_PEEKSIGINFO_SHARED = 0x1 + PTRACE_PEEKTEXT = 0x1 + PTRACE_PEEKUSR = 0x3 + PTRACE_POKEDATA = 0x5 + PTRACE_POKETEXT = 0x4 + PTRACE_POKEUSR = 0x6 + PTRACE_SECCOMP_GET_FILTER = 0x420c + PTRACE_SECCOMP_GET_METADATA = 0x420d + PTRACE_SEIZE = 0x4206 + PTRACE_SETOPTIONS = 0x4200 + PTRACE_SETREGS = 0xd + PTRACE_SETREGSET = 0x4205 + PTRACE_SETSIGINFO = 0x4203 + PTRACE_SETSIGMASK = 0x420b + PTRACE_SINGLESTEP = 0x9 + PTRACE_SYSCALL = 0x18 + PTRACE_SYSCALL_INFO_ENTRY = 0x1 + PTRACE_SYSCALL_INFO_EXIT = 0x2 + PTRACE_SYSCALL_INFO_NONE = 0x0 + PTRACE_SYSCALL_INFO_SECCOMP = 0x3 + PTRACE_SYSEMU = 0x1f + PTRACE_SYSEMU_SINGLESTEP = 0x20 + PTRACE_TRACEME = 0x0 + RLIMIT_AS = 0x9 + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x7 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0xffffffffffffffff + RTAX_ADVMSS = 0x8 + RTAX_CC_ALGO = 0x10 + RTAX_CWND = 0x7 + RTAX_FASTOPEN_NO_COOKIE = 0x11 + RTAX_FEATURES = 0xc + RTAX_FEATURE_ALLFRAG = 0x8 + RTAX_FEATURE_ECN = 0x1 + RTAX_FEATURE_MASK = 0xf + RTAX_FEATURE_SACK = 0x2 + RTAX_FEATURE_TIMESTAMP = 0x4 + RTAX_HOPLIMIT = 0xa + RTAX_INITCWND = 0xb + RTAX_INITRWND = 0xe + RTAX_LOCK = 0x1 + RTAX_MAX = 0x11 + RTAX_MTU = 0x2 + RTAX_QUICKACK = 0xf + RTAX_REORDERING = 0x9 + RTAX_RTO_MIN = 0xd + RTAX_RTT = 0x4 + RTAX_RTTVAR = 0x5 + RTAX_SSTHRESH = 0x6 + RTAX_UNSPEC = 0x0 + RTAX_WINDOW = 0x3 + RTA_ALIGNTO = 0x4 + RTA_MAX = 0x1e + RTCF_DIRECTSRC = 0x4000000 + RTCF_DOREDIRECT = 0x1000000 + RTCF_LOG = 0x2000000 + RTCF_MASQ = 0x400000 + RTCF_NAT = 0x800000 + RTCF_VALVE = 0x200000 + RTF_ADDRCLASSMASK = 0xf8000000 + RTF_ADDRCONF = 0x40000 + RTF_ALLONLINK = 0x20000 + RTF_BROADCAST = 0x10000000 + RTF_CACHE = 0x1000000 + RTF_DEFAULT = 0x10000 + RTF_DYNAMIC = 0x10 + RTF_FLOW = 0x2000000 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_INTERFACE = 0x40000000 + RTF_IRTT = 0x100 + RTF_LINKRT = 0x100000 + RTF_LOCAL = 0x80000000 + RTF_MODIFIED = 0x20 + RTF_MSS = 0x40 + RTF_MTU = 0x40 + RTF_MULTICAST = 0x20000000 + RTF_NAT = 0x8000000 + RTF_NOFORWARD = 0x1000 + RTF_NONEXTHOP = 0x200000 + RTF_NOPMTUDISC = 0x4000 + RTF_POLICY = 0x4000000 + RTF_REINSTATE = 0x8 + RTF_REJECT = 0x200 + RTF_STATIC = 0x400 + RTF_THROW = 0x2000 + RTF_UP = 0x1 + RTF_WINDOW = 0x80 + RTF_XRESOLVE = 0x800 + RTM_BASE = 0x10 + RTM_DELACTION = 0x31 + RTM_DELADDR = 0x15 + RTM_DELADDRLABEL = 0x49 + RTM_DELCHAIN = 0x65 + RTM_DELLINK = 0x11 + RTM_DELLINKPROP = 0x6d + RTM_DELMDB = 0x55 + RTM_DELNEIGH = 0x1d + RTM_DELNETCONF = 0x51 + RTM_DELNEXTHOP = 0x69 + RTM_DELNEXTHOPBUCKET = 0x75 + RTM_DELNSID = 0x59 + RTM_DELQDISC = 0x25 + RTM_DELROUTE = 0x19 + RTM_DELRULE = 0x21 + RTM_DELTCLASS = 0x29 + RTM_DELTFILTER = 0x2d + RTM_DELVLAN = 0x71 + RTM_F_CLONED = 0x200 + RTM_F_EQUALIZE = 0x400 + RTM_F_FIB_MATCH = 0x2000 + RTM_F_LOOKUP_TABLE = 0x1000 + RTM_F_NOTIFY = 0x100 + RTM_F_OFFLOAD = 0x4000 + RTM_F_OFFLOAD_FAILED = 0x20000000 + RTM_F_PREFIX = 0x800 + RTM_F_TRAP = 0x8000 + RTM_GETACTION = 0x32 + RTM_GETADDR = 0x16 + RTM_GETADDRLABEL = 0x4a + RTM_GETANYCAST = 0x3e + RTM_GETCHAIN = 0x66 + RTM_GETDCB = 0x4e + RTM_GETLINK = 0x12 + RTM_GETLINKPROP = 0x6e + RTM_GETMDB = 0x56 + RTM_GETMULTICAST = 0x3a + RTM_GETNEIGH = 0x1e + RTM_GETNEIGHTBL = 0x42 + RTM_GETNETCONF = 0x52 + RTM_GETNEXTHOP = 0x6a + RTM_GETNEXTHOPBUCKET = 0x76 + RTM_GETNSID = 0x5a + RTM_GETQDISC = 0x26 + RTM_GETROUTE = 0x1a + RTM_GETRULE = 0x22 + RTM_GETSTATS = 0x5e + RTM_GETTCLASS = 0x2a + RTM_GETTFILTER = 0x2e + RTM_GETVLAN = 0x72 + RTM_MAX = 0x77 + RTM_NEWACTION = 0x30 + RTM_NEWADDR = 0x14 + RTM_NEWADDRLABEL = 0x48 + RTM_NEWCACHEREPORT = 0x60 + RTM_NEWCHAIN = 0x64 + RTM_NEWLINK = 0x10 + RTM_NEWLINKPROP = 0x6c + RTM_NEWMDB = 0x54 + RTM_NEWNDUSEROPT = 0x44 + RTM_NEWNEIGH = 0x1c + RTM_NEWNEIGHTBL = 0x40 + RTM_NEWNETCONF = 0x50 + RTM_NEWNEXTHOP = 0x68 + RTM_NEWNEXTHOPBUCKET = 0x74 + RTM_NEWNSID = 0x58 + RTM_NEWNVLAN = 0x70 + RTM_NEWPREFIX = 0x34 + RTM_NEWQDISC = 0x24 + RTM_NEWROUTE = 0x18 + RTM_NEWRULE = 0x20 + RTM_NEWSTATS = 0x5c + RTM_NEWTCLASS = 0x28 + RTM_NEWTFILTER = 0x2c + RTM_NR_FAMILIES = 0x1a + RTM_NR_MSGTYPES = 0x68 + RTM_SETDCB = 0x4f + RTM_SETLINK = 0x13 + RTM_SETNEIGHTBL = 0x43 + RTNH_ALIGNTO = 0x4 + RTNH_COMPARE_MASK = 0x59 + RTNH_F_DEAD = 0x1 + RTNH_F_LINKDOWN = 0x10 + RTNH_F_OFFLOAD = 0x8 + RTNH_F_ONLINK = 0x4 + RTNH_F_PERVASIVE = 0x2 + RTNH_F_TRAP = 0x40 + RTNH_F_UNRESOLVED = 0x20 + RTN_MAX = 0xb + RTPROT_BABEL = 0x2a + RTPROT_BGP = 0xba + RTPROT_BIRD = 0xc + RTPROT_BOOT = 0x3 + RTPROT_DHCP = 0x10 + RTPROT_DNROUTED = 0xd + RTPROT_EIGRP = 0xc0 + RTPROT_GATED = 0x8 + RTPROT_ISIS = 0xbb + RTPROT_KEEPALIVED = 0x12 + RTPROT_KERNEL = 0x2 + RTPROT_MROUTED = 0x11 + RTPROT_MRT = 0xa + RTPROT_NTK = 0xf + RTPROT_OPENR = 0x63 + RTPROT_OSPF = 0xbc + RTPROT_RA = 0x9 + RTPROT_REDIRECT = 0x1 + RTPROT_RIP = 0xbd + RTPROT_STATIC = 0x4 + RTPROT_UNSPEC = 0x0 + RTPROT_XORP = 0xe + RTPROT_ZEBRA = 0xb + RT_CLASS_DEFAULT = 0xfd + RT_CLASS_LOCAL = 0xff + RT_CLASS_MAIN = 0xfe + RT_CLASS_MAX = 0xff + RT_CLASS_UNSPEC = 0x0 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + RUSAGE_THREAD = 0x1 + SCM_CREDENTIALS = 0x2 + SCM_RIGHTS = 0x1 + SCM_TIMESTAMP = 0x1d + SCM_TIMESTAMPING = 0x25 + SCM_TIMESTAMPING_OPT_STATS = 0x36 + SCM_TIMESTAMPING_PKTINFO = 0x3a + SCM_TIMESTAMPNS = 0x23 + SCM_TXTIME = 0x3d + SCM_WIFI_STATUS = 0x29 + SHUT_RD = 0x0 + SHUT_RDWR = 0x2 + SHUT_WR = 0x1 + SIOCADDDLCI = 0x8980 + SIOCADDMULTI = 0x8931 + SIOCADDRT = 0x890b + SIOCATMARK = 0x8905 + SIOCDARP = 0x8953 + SIOCDELDLCI = 0x8981 + SIOCDELMULTI = 0x8932 + SIOCDELRT = 0x890c + SIOCDEVPRIVATE = 0x89f0 + SIOCDIFADDR = 0x8936 + SIOCDRARP = 0x8960 + SIOCGARP = 0x8954 + SIOCGIFADDR = 0x8915 + SIOCGIFBR = 0x8940 + SIOCGIFBRDADDR = 0x8919 + SIOCGIFCONF = 0x8912 + SIOCGIFCOUNT = 0x8938 + SIOCGIFDSTADDR = 0x8917 + SIOCGIFENCAP = 0x8925 + SIOCGIFFLAGS = 0x8913 + SIOCGIFHWADDR = 0x8927 + SIOCGIFINDEX = 0x8933 + SIOCGIFMAP = 0x8970 + SIOCGIFMEM = 0x891f + SIOCGIFMETRIC = 0x891d + SIOCGIFMTU = 0x8921 + SIOCGIFNAME = 0x8910 + SIOCGIFNETMASK = 0x891b + SIOCGIFPFLAGS = 0x8935 + SIOCGIFSLAVE = 0x8929 + SIOCGIFTXQLEN = 0x8942 + SIOCGPGRP = 0x8904 + SIOCGRARP = 0x8961 + SIOCGSTAMPNS_OLD = 0x8907 + SIOCGSTAMP_OLD = 0x8906 + SIOCPROTOPRIVATE = 0x89e0 + SIOCRTMSG = 0x890d + SIOCSARP = 0x8955 + SIOCSIFADDR = 0x8916 + SIOCSIFBR = 0x8941 + SIOCSIFBRDADDR = 0x891a + SIOCSIFDSTADDR = 0x8918 + SIOCSIFENCAP = 0x8926 + SIOCSIFFLAGS = 0x8914 + SIOCSIFHWADDR = 0x8924 + SIOCSIFHWBROADCAST = 0x8937 + SIOCSIFLINK = 0x8911 + SIOCSIFMAP = 0x8971 + SIOCSIFMEM = 0x8920 + SIOCSIFMETRIC = 0x891e + SIOCSIFMTU = 0x8922 + SIOCSIFNAME = 0x8923 + SIOCSIFNETMASK = 0x891c + SIOCSIFPFLAGS = 0x8934 + SIOCSIFSLAVE = 0x8930 + SIOCSIFTXQLEN = 0x8943 + SIOCSPGRP = 0x8902 + SIOCSRARP = 0x8962 + SOCK_BUF_LOCK_MASK = 0x3 + SOCK_CLOEXEC = 0x80000 + SOCK_DCCP = 0x6 + SOCK_DGRAM = 0x2 + SOCK_NONBLOCK = 0x800 + SOCK_PACKET = 0xa + SOCK_RAW = 0x3 + SOCK_RCVBUF_LOCK = 0x2 + SOCK_RDM = 0x4 + SOCK_SEQPACKET = 0x5 + SOCK_SNDBUF_LOCK = 0x1 + SOCK_STREAM = 0x1 + SOL_AAL = 0x109 + SOL_ALG = 0x117 + SOL_ATM = 0x108 + SOL_BLUETOOTH = 0x112 + SOL_CAIF = 0x116 + SOL_DCCP = 0x10d + SOL_DECNET = 0x105 + SOL_ICMPV6 = 0x3a + SOL_IP = 0x0 + SOL_IPV6 = 0x29 + SOL_IRDA = 0x10a + SOL_IUCV = 0x115 + SOL_KCM = 0x119 + SOL_LLC = 0x10c + SOL_NETBEUI = 0x10b + SOL_NETLINK = 0x10e + SOL_NFC = 0x118 + SOL_PACKET = 0x107 + SOL_PNPIPE = 0x113 + SOL_PPPOL2TP = 0x111 + SOL_RAW = 0xff + SOL_RDS = 0x114 + SOL_RXRPC = 0x110 + SOL_SOCKET = 0x1 + SOL_TCP = 0x6 + SOL_TIPC = 0x10f + SOL_TLS = 0x11a + SOL_X25 = 0x106 + SOL_XDP = 0x11b + SOMAXCONN = 0x1000 + SO_ACCEPTCONN = 0x1e + SO_ATTACH_BPF = 0x32 + SO_ATTACH_FILTER = 0x1a + SO_ATTACH_REUSEPORT_CBPF = 0x33 + SO_ATTACH_REUSEPORT_EBPF = 0x34 + SO_BINDTODEVICE = 0x19 + SO_BINDTOIFINDEX = 0x3e + SO_BPF_EXTENSIONS = 0x30 + SO_BROADCAST = 0x6 + SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 + SO_BUSY_POLL = 0x2e + SO_BUSY_POLL_BUDGET = 0x46 + SO_CNX_ADVICE = 0x35 + SO_COOKIE = 0x39 + SO_DEBUG = 0x1 + SO_DETACH_BPF = 0x1b + SO_DETACH_FILTER = 0x1b + SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DOMAIN = 0x27 + SO_DONTROUTE = 0x5 + SO_ERROR = 0x4 + SO_GET_FILTER = 0x1a + SO_INCOMING_CPU = 0x31 + SO_INCOMING_NAPI_ID = 0x38 + SO_KEEPALIVE = 0x9 + SO_LINGER = 0xd + SO_LOCK_FILTER = 0x2c + SO_MARK = 0x24 + SO_MAX_PACING_RATE = 0x2f + SO_MEMINFO = 0x37 + SO_NETNS_COOKIE = 0x47 + SO_NOFCS = 0x2b + SO_NO_CHECK = 0xb + SO_OOBINLINE = 0xa + SO_PASSCRED = 0x10 + SO_PASSSEC = 0x22 + SO_PEEK_OFF = 0x2a + SO_PEERCRED = 0x11 + SO_PEERGROUPS = 0x3b + SO_PEERNAME = 0x1c + SO_PEERSEC = 0x1f + SO_PREFER_BUSY_POLL = 0x45 + SO_PRIORITY = 0xc + SO_PROTOCOL = 0x26 + SO_RCVBUF = 0x8 + SO_RCVBUFFORCE = 0x21 + SO_RCVLOWAT = 0x12 + SO_RCVTIMEO = 0x14 + SO_RCVTIMEO_NEW = 0x42 + SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 + SO_REUSEADDR = 0x2 + SO_REUSEPORT = 0xf + SO_RXQ_OVFL = 0x28 + SO_SECURITY_AUTHENTICATION = 0x16 + SO_SECURITY_ENCRYPTION_NETWORK = 0x18 + SO_SECURITY_ENCRYPTION_TRANSPORT = 0x17 + SO_SELECT_ERR_QUEUE = 0x2d + SO_SNDBUF = 0x7 + SO_SNDBUFFORCE = 0x20 + SO_SNDLOWAT = 0x13 + SO_SNDTIMEO = 0x15 + SO_SNDTIMEO_NEW = 0x43 + SO_SNDTIMEO_OLD = 0x15 + SO_TIMESTAMP = 0x1d + SO_TIMESTAMPING = 0x25 + SO_TIMESTAMPING_NEW = 0x41 + SO_TIMESTAMPING_OLD = 0x25 + SO_TIMESTAMPNS = 0x23 + SO_TIMESTAMPNS_NEW = 0x40 + SO_TIMESTAMPNS_OLD = 0x23 + SO_TIMESTAMP_NEW = 0x3f + SO_TIMESTAMP_OLD = 0x1d + SO_TXTIME = 0x3d + SO_TYPE = 0x3 + SO_WIFI_STATUS = 0x29 + SO_ZEROCOPY = 0x3c + S_BLKSIZE = 0x200 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + TCFLSH = 0x540b + TCIFLUSH = 0x0 + TCIOFLUSH = 0x2 + TCOFLUSH = 0x1 + TCP_CC_INFO = 0x1a + TCP_CM_INQ = 0x24 + TCP_CONGESTION = 0xd + TCP_COOKIE_IN_ALWAYS = 0x1 + TCP_COOKIE_MAX = 0x10 + TCP_COOKIE_MIN = 0x8 + TCP_COOKIE_OUT_NEVER = 0x2 + TCP_COOKIE_PAIR_SIZE = 0x20 + TCP_COOKIE_TRANSACTIONS = 0xf + TCP_CORK = 0x3 + TCP_DEFER_ACCEPT = 0x9 + TCP_FASTOPEN = 0x17 + TCP_FASTOPEN_CONNECT = 0x1e + TCP_FASTOPEN_KEY = 0x21 + TCP_FASTOPEN_NO_COOKIE = 0x22 + TCP_INFO = 0xb + TCP_INQ = 0x24 + TCP_KEEPCNT = 0x6 + TCP_KEEPIDLE = 0x4 + TCP_KEEPINTVL = 0x5 + TCP_LINGER2 = 0x8 + TCP_MAXSEG = 0x2 + TCP_MAXWIN = 0xffff + TCP_MAX_WINSHIFT = 0xe + TCP_MD5SIG = 0xe + TCP_MD5SIG_EXT = 0x20 + TCP_MD5SIG_FLAG_PREFIX = 0x1 + TCP_MD5SIG_MAXKEYLEN = 0x50 + TCP_MSS = 0x200 + TCP_MSS_DEFAULT = 0x218 + TCP_MSS_DESIRED = 0x4c4 + TCP_NODELAY = 0x1 + TCP_NOTSENT_LOWAT = 0x19 + TCP_QUEUE_SEQ = 0x15 + TCP_QUICKACK = 0xc + TCP_REPAIR = 0x13 + TCP_REPAIR_OFF = 0x0 + TCP_REPAIR_OFF_NO_WP = -0x1 + TCP_REPAIR_ON = 0x1 + TCP_REPAIR_OPTIONS = 0x16 + TCP_REPAIR_QUEUE = 0x14 + TCP_REPAIR_WINDOW = 0x1d + TCP_SAVED_SYN = 0x1c + TCP_SAVE_SYN = 0x1b + TCP_SYNCNT = 0x7 + TCP_S_DATA_IN = 0x4 + TCP_S_DATA_OUT = 0x8 + TCP_THIN_DUPACK = 0x11 + TCP_THIN_LINEAR_TIMEOUTS = 0x10 + TCP_TIMESTAMP = 0x18 + TCP_TX_DELAY = 0x25 + TCP_ULP = 0x1f + TCP_USER_TIMEOUT = 0x12 + TCP_WINDOW_CLAMP = 0xa + TCP_ZEROCOPY_RECEIVE = 0x23 + TCSAFLUSH = 0x2 + TIOCCBRK = 0x5428 + TIOCCONS = 0x541d + TIOCEXCL = 0x540c + TIOCGDEV = 0x80045432 + TIOCGETD = 0x5424 + TIOCGEXCL = 0x80045440 + TIOCGICOUNT = 0x545d + TIOCGISO7816 = 0x80285442 + TIOCGLCKTRMIOS = 0x5456 + TIOCGPGRP = 0x540f + TIOCGPKT = 0x80045438 + TIOCGPTLCK = 0x80045439 + TIOCGPTN = 0x80045430 + TIOCGPTPEER = 0x5441 + TIOCGRS485 = 0x542e + TIOCGSERIAL = 0x541e + TIOCGSID = 0x5429 + TIOCGSOFTCAR = 0x5419 + TIOCGWINSZ = 0x5413 + TIOCINQ = 0x541b + TIOCLINUX = 0x541c + TIOCMBIC = 0x5417 + TIOCMBIS = 0x5416 + TIOCMGET = 0x5415 + TIOCMIWAIT = 0x545c + TIOCMSET = 0x5418 + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x5422 + TIOCNXCL = 0x540d + TIOCOUTQ = 0x5411 + TIOCPKT = 0x5420 + TIOCPKT_DATA = 0x0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCSBRK = 0x5427 + TIOCSCTTY = 0x540e + TIOCSERCONFIG = 0x5453 + TIOCSERGETLSR = 0x5459 + TIOCSERGETMULTI = 0x545a + TIOCSERGSTRUCT = 0x5458 + TIOCSERGWILD = 0x5454 + TIOCSERSETMULTI = 0x545b + TIOCSERSWILD = 0x5455 + TIOCSER_TEMT = 0x1 + TIOCSETD = 0x5423 + TIOCSIG = 0x40045436 + TIOCSISO7816 = 0xc0285443 + TIOCSLCKTRMIOS = 0x5457 + TIOCSPGRP = 0x5410 + TIOCSPTLCK = 0x40045431 + TIOCSRS485 = 0x542f + TIOCSSERIAL = 0x541f + TIOCSSOFTCAR = 0x541a + TIOCSTI = 0x5412 + TIOCSWINSZ = 0x5414 + TIOCVHANGUP = 0x5437 + TOSTOP = 0x100 + TUNATTACHFILTER = 0x401054d5 + TUNDETACHFILTER = 0x401054d6 + TUNGETDEVNETNS = 0x54e3 + TUNGETFEATURES = 0x800454cf + TUNGETFILTER = 0x801054db + TUNGETIFF = 0x800454d2 + TUNGETSNDBUF = 0x800454d3 + TUNGETVNETBE = 0x800454df + TUNGETVNETHDRSZ = 0x800454d7 + TUNGETVNETLE = 0x800454dd + TUNSETCARRIER = 0x400454e2 + TUNSETDEBUG = 0x400454c9 + TUNSETFILTEREBPF = 0x800454e1 + TUNSETGROUP = 0x400454ce + TUNSETIFF = 0x400454ca + TUNSETIFINDEX = 0x400454da + TUNSETLINK = 0x400454cd + TUNSETNOCSUM = 0x400454c8 + TUNSETOFFLOAD = 0x400454d0 + TUNSETOWNER = 0x400454cc + TUNSETPERSIST = 0x400454cb + TUNSETQUEUE = 0x400454d9 + TUNSETSNDBUF = 0x400454d4 + TUNSETSTEERINGEBPF = 0x800454e0 + TUNSETTXFILTER = 0x400454d1 + TUNSETVNETBE = 0x400454de + TUNSETVNETHDRSZ = 0x400454d8 + TUNSETVNETLE = 0x400454dc + VDISCARD = 0xd + VEOF = 0x4 + VEOL = 0xb + VEOL2 = 0x10 + VERASE = 0x2 + VINTR = 0x0 + VKILL = 0x3 + VLNEXT = 0xf + VMIN = 0x6 + VQUIT = 0x1 + VREPRINT = 0xc + VSTART = 0x8 + VSTOP = 0x9 + VSUSP = 0xa + VSWTC = 0x7 + VT0 = 0x0 + VT1 = 0x4000 + VTDLY = 0x4000 + VTIME = 0x5 + VWERASE = 0xe + WALL = 0x40000000 + WCLONE = 0x80000000 + WCONTINUED = 0x8 + WEXITED = 0x4 + WNOHANG = 0x1 + WNOTHREAD = 0x20000000 + WNOWAIT = 0x1000000 + WORDSIZE = 0x40 + WSTOPPED = 0x2 + WUNTRACED = 0x2 +) + +// Errors +const ( + E2BIG = Errno(0x7) + EACCES = Errno(0xd) + EADDRINUSE = Errno(0x62) + EADDRNOTAVAIL = Errno(0x63) + EADV = Errno(0x44) + EAFNOSUPPORT = Errno(0x61) + EAGAIN = Errno(0xb) + EALREADY = Errno(0x72) + EBADE = Errno(0x34) + EBADF = Errno(0x9) + EBADFD = Errno(0x4d) + EBADMSG = Errno(0x4a) + EBADR = Errno(0x35) + EBADRQC = Errno(0x38) + EBADSLT = Errno(0x39) + EBFONT = Errno(0x3b) + EBUSY = Errno(0x10) + ECANCELED = Errno(0x7d) + ECHILD = Errno(0xa) + ECHRNG = Errno(0x2c) + ECOMM = Errno(0x46) + ECONNABORTED = Errno(0x67) + ECONNREFUSED = Errno(0x6f) + ECONNRESET = Errno(0x68) + EDEADLK = Errno(0x23) + EDEADLOCK = Errno(0x23) + EDESTADDRREQ = Errno(0x59) + EDOM = Errno(0x21) + EDOTDOT = Errno(0x49) + EDQUOT = Errno(0x7a) + EEXIST = Errno(0x11) + EFAULT = Errno(0xe) + EFBIG = Errno(0x1b) + EHOSTDOWN = Errno(0x70) + EHOSTUNREACH = Errno(0x71) + EHWPOISON = Errno(0x85) + EIDRM = Errno(0x2b) + EILSEQ = Errno(0x54) + EINPROGRESS = Errno(0x73) + EINTR = Errno(0x4) + EINVAL = Errno(0x16) + EIO = Errno(0x5) + EISCONN = Errno(0x6a) + EISDIR = Errno(0x15) + EISNAM = Errno(0x78) + EKEYEXPIRED = Errno(0x7f) + EKEYREJECTED = Errno(0x81) + EKEYREVOKED = Errno(0x80) + EL2HLT = Errno(0x33) + EL2NSYNC = Errno(0x2d) + EL3HLT = Errno(0x2e) + EL3RST = Errno(0x2f) + ELIBACC = Errno(0x4f) + ELIBBAD = Errno(0x50) + ELIBEXEC = Errno(0x53) + ELIBMAX = Errno(0x52) + ELIBSCN = Errno(0x51) + ELNRNG = Errno(0x30) + ELOOP = Errno(0x28) + EMEDIUMTYPE = Errno(0x7c) + EMFILE = Errno(0x18) + EMLINK = Errno(0x1f) + EMSGSIZE = Errno(0x5a) + EMULTIHOP = Errno(0x48) + ENAMETOOLONG = Errno(0x24) + ENAVAIL = Errno(0x77) + ENETDOWN = Errno(0x64) + ENETRESET = Errno(0x66) + ENETUNREACH = Errno(0x65) + ENFILE = Errno(0x17) + ENOANO = Errno(0x37) + ENOBUFS = Errno(0x69) + ENOCSI = Errno(0x32) + ENODATA = Errno(0x3d) + ENODEV = Errno(0x13) + ENOENT = Errno(0x2) + ENOEXEC = Errno(0x8) + ENOKEY = Errno(0x7e) + ENOLCK = Errno(0x25) + ENOLINK = Errno(0x43) + ENOMEDIUM = Errno(0x7b) + ENOMEM = Errno(0xc) + ENOMSG = Errno(0x2a) + ENONET = Errno(0x40) + ENOPKG = Errno(0x41) + ENOPROTOOPT = Errno(0x5c) + ENOSPC = Errno(0x1c) + ENOSR = Errno(0x3f) + ENOSTR = Errno(0x3c) + ENOSYS = Errno(0x26) + ENOTBLK = Errno(0xf) + ENOTCONN = Errno(0x6b) + ENOTDIR = Errno(0x14) + ENOTEMPTY = Errno(0x27) + ENOTNAM = Errno(0x76) + ENOTRECOVERABLE = Errno(0x83) + ENOTSOCK = Errno(0x58) + ENOTSUP = Errno(0x5f) + ENOTTY = Errno(0x19) + ENOTUNIQ = Errno(0x4c) + ENXIO = Errno(0x6) + EOPNOTSUPP = Errno(0x5f) + EOVERFLOW = Errno(0x4b) + EOWNERDEAD = Errno(0x82) + EPERM = Errno(0x1) + EPFNOSUPPORT = Errno(0x60) + EPIPE = Errno(0x20) + EPROTO = Errno(0x47) + EPROTONOSUPPORT = Errno(0x5d) + EPROTOTYPE = Errno(0x5b) + ERANGE = Errno(0x22) + EREMCHG = Errno(0x4e) + EREMOTE = Errno(0x42) + EREMOTEIO = Errno(0x79) + ERESTART = Errno(0x55) + ERFKILL = Errno(0x84) + EROFS = Errno(0x1e) + ESHUTDOWN = Errno(0x6c) + ESOCKTNOSUPPORT = Errno(0x5e) + ESPIPE = Errno(0x1d) + ESRCH = Errno(0x3) + ESRMNT = Errno(0x45) + ESTALE = Errno(0x74) + ESTRPIPE = Errno(0x56) + ETIME = Errno(0x3e) + ETIMEDOUT = Errno(0x6e) + ETOOMANYREFS = Errno(0x6d) + ETXTBSY = Errno(0x1a) + EUCLEAN = Errno(0x75) + EUNATCH = Errno(0x31) + EUSERS = Errno(0x57) + EWOULDBLOCK = Errno(0xb) + EXDEV = Errno(0x12) + EXFULL = Errno(0x36) +) + +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0x7) + SIGCHLD = Signal(0x11) + SIGCLD = Signal(0x11) + SIGCONT = Signal(0x12) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINT = Signal(0x2) + SIGIO = Signal(0x1d) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGPIPE = Signal(0xd) + SIGPOLL = Signal(0x1d) + SIGPROF = Signal(0x1b) + SIGPWR = Signal(0x1e) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTKFLT = Signal(0x10) + SIGSTOP = Signal(0x13) + SIGSYS = Signal(0x1f) + SIGTERM = Signal(0xf) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x14) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x17) + SIGUSR1 = Signal(0xa) + SIGUSR2 = Signal(0xc) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + +// Error table +var errors = [...]string{ + 1: "operation not permitted", + 2: "no such file or directory", + 3: "no such process", + 4: "interrupted system call", + 5: "input/output error", + 6: "no such device or address", + 7: "argument list too long", + 8: "exec format error", + 9: "bad file descriptor", + 10: "no child processes", + 11: "resource temporarily unavailable", + 12: "cannot allocate memory", + 13: "permission denied", + 14: "bad address", + 15: "block device required", + 16: "device or resource busy", + 17: "file exists", + 18: "invalid cross-device link", + 19: "no such device", + 20: "not a directory", + 21: "is a directory", + 22: "invalid argument", + 23: "too many open files in system", + 24: "too many open files", + 25: "inappropriate ioctl for device", + 26: "text file busy", + 27: "file too large", + 28: "no space left on device", + 29: "illegal seek", + 30: "read-only file system", + 31: "too many links", + 32: "broken pipe", + 33: "numerical argument out of domain", + 34: "numerical result out of range", + 35: "resource deadlock avoided", + 36: "file name too long", + 37: "no locks available", + 38: "function not implemented", + 39: "directory not empty", + 40: "too many levels of symbolic links", + 42: "no message of desired type", + 43: "identifier removed", + 44: "channel number out of range", + 45: "level 2 not synchronized", + 46: "level 3 halted", + 47: "level 3 reset", + 48: "link number out of range", + 49: "protocol driver not attached", + 50: "no CSI structure available", + 51: "level 2 halted", + 52: "invalid exchange", + 53: "invalid request descriptor", + 54: "exchange full", + 55: "no anode", + 56: "invalid request code", + 57: "invalid slot", + 59: "bad font file format", + 60: "device not a stream", + 61: "no data available", + 62: "timer expired", + 63: "out of streams resources", + 64: "machine is not on the network", + 65: "package not installed", + 66: "object is remote", + 67: "link has been severed", + 68: "advertise error", + 69: "srmount error", + 70: "communication error on send", + 71: "protocol error", + 72: "multihop attempted", + 73: "RFS specific error", + 74: "bad message", + 75: "value too large for defined data type", + 76: "name not unique on network", + 77: "file descriptor in bad state", + 78: "remote address changed", + 79: "can not access a needed shared library", + 80: "accessing a corrupted shared library", + 81: ".lib section in a.out corrupted", + 82: "attempting to link in too many shared libraries", + 83: "cannot exec a shared library directly", + 84: "invalid or incomplete multibyte or wide character", + 85: "interrupted system call should be restarted", + 86: "streams pipe error", + 87: "too many users", + 88: "socket operation on non-socket", + 89: "destination address required", + 90: "message too long", + 91: "protocol wrong type for socket", + 92: "protocol not available", + 93: "protocol not supported", + 94: "socket type not supported", + 95: "operation not supported", + 96: "protocol family not supported", + 97: "address family not supported by protocol", + 98: "address already in use", + 99: "cannot assign requested address", + 100: "network is down", + 101: "network is unreachable", + 102: "network dropped connection on reset", + 103: "software caused connection abort", + 104: "connection reset by peer", + 105: "no buffer space available", + 106: "transport endpoint is already connected", + 107: "transport endpoint is not connected", + 108: "cannot send after transport endpoint shutdown", + 109: "too many references: cannot splice", + 110: "connection timed out", + 111: "connection refused", + 112: "host is down", + 113: "no route to host", + 114: "operation already in progress", + 115: "operation now in progress", + 116: "stale file handle", + 117: "structure needs cleaning", + 118: "not a XENIX named type file", + 119: "no XENIX semaphores available", + 120: "is a named type file", + 121: "remote I/O error", + 122: "disk quota exceeded", + 123: "no medium found", + 124: "wrong medium type", + 125: "operation canceled", + 126: "required key not available", + 127: "key has expired", + 128: "key has been revoked", + 129: "key was rejected by service", + 130: "owner died", + 131: "state not recoverable", + 132: "operation not possible due to RF-kill", + 133: "memory page has hardware error", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/breakpoint trap", + 6: "aborted", + 7: "bus error", + 8: "floating point exception", + 9: "killed", + 10: "user defined signal 1", + 11: "segmentation fault", + 12: "user defined signal 2", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "stack fault", + 17: "child exited", + 18: "continued", + 19: "stopped (signal)", + 20: "stopped", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "urgent I/O condition", + 24: "CPU time limit exceeded", + 25: "file size limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window changed", + 29: "I/O possible", + 30: "power failure", + 31: "bad system call", +} diff --git a/src/syscall/zerrors_linux_mips.go b/src/syscall/zerrors_linux_mips.go index d3dae6d84a6916..3fe5c007576783 100644 --- a/src/syscall/zerrors_linux_mips.go +++ b/src/syscall/zerrors_linux_mips.go @@ -191,28 +191,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_mips64.go b/src/syscall/zerrors_linux_mips64.go index 84c9e8fb6fe132..74a1843ec2975d 100644 --- a/src/syscall/zerrors_linux_mips64.go +++ b/src/syscall/zerrors_linux_mips64.go @@ -189,28 +189,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_mips64le.go b/src/syscall/zerrors_linux_mips64le.go index 84c9e8fb6fe132..74a1843ec2975d 100644 --- a/src/syscall/zerrors_linux_mips64le.go +++ b/src/syscall/zerrors_linux_mips64le.go @@ -189,28 +189,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_mipsle.go b/src/syscall/zerrors_linux_mipsle.go index d3dae6d84a6916..3fe5c007576783 100644 --- a/src/syscall/zerrors_linux_mipsle.go +++ b/src/syscall/zerrors_linux_mipsle.go @@ -191,28 +191,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_ppc64.go b/src/syscall/zerrors_linux_ppc64.go index 1dda43be70fc25..b63daea853b2c8 100644 --- a/src/syscall/zerrors_linux_ppc64.go +++ b/src/syscall/zerrors_linux_ppc64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build ppc64 && linux -// +build ppc64,linux package syscall @@ -192,28 +191,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x8000 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x800 CS5 = 0x0 CS6 = 0x100 diff --git a/src/syscall/zerrors_linux_ppc64le.go b/src/syscall/zerrors_linux_ppc64le.go index 6d56f1c99817c2..01f8adb0275280 100644 --- a/src/syscall/zerrors_linux_ppc64le.go +++ b/src/syscall/zerrors_linux_ppc64le.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build ppc64le && linux -// +build ppc64le,linux package syscall @@ -193,28 +192,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x8000 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x800 CS5 = 0x0 CS6 = 0x100 diff --git a/src/syscall/zerrors_linux_riscv64.go b/src/syscall/zerrors_linux_riscv64.go index 582537d0899a0d..f4b1d9ae65f21f 100644 --- a/src/syscall/zerrors_linux_riscv64.go +++ b/src/syscall/zerrors_linux_riscv64.go @@ -196,28 +196,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_linux_s390x.go b/src/syscall/zerrors_linux_s390x.go index cd1aa161e8626b..8b99a60995e26e 100644 --- a/src/syscall/zerrors_linux_s390x.go +++ b/src/syscall/zerrors_linux_s390x.go @@ -193,29 +193,6 @@ const ( BRKINT = 0x2 CFLUSH = 0xf CLOCAL = 0x800 - CLONE_CHILD_CLEARTID = 0x200000 - CLONE_CHILD_SETTID = 0x1000000 - CLONE_DETACHED = 0x400000 - CLONE_FILES = 0x400 - CLONE_FS = 0x200 - CLONE_IO = 0x80000000 - CLONE_NEWCGROUP = 0x2000000 - CLONE_NEWIPC = 0x8000000 - CLONE_NEWNET = 0x40000000 - CLONE_NEWNS = 0x20000 - CLONE_NEWPID = 0x20000000 - CLONE_NEWUSER = 0x10000000 - CLONE_NEWUTS = 0x4000000 - CLONE_PARENT = 0x8000 - CLONE_PARENT_SETTID = 0x100000 - CLONE_PTRACE = 0x2000 - CLONE_SETTLS = 0x80000 - CLONE_SIGHAND = 0x800 - CLONE_SYSVSEM = 0x40000 - CLONE_THREAD = 0x10000 - CLONE_UNTRACED = 0x800000 - CLONE_VFORK = 0x4000 - CLONE_VM = 0x100 CREAD = 0x80 CS5 = 0x0 CS6 = 0x10 diff --git a/src/syscall/zerrors_netbsd_386.go b/src/syscall/zerrors_netbsd_386.go index 934e9ef073abf7..b2fcb65782e98c 100644 --- a/src/syscall/zerrors_netbsd_386.go +++ b/src/syscall/zerrors_netbsd_386.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m32 _const.go //go:build 386 && netbsd -// +build 386,netbsd package syscall diff --git a/src/syscall/zerrors_netbsd_amd64.go b/src/syscall/zerrors_netbsd_amd64.go index 0f5ad61a9f29a8..dc52c3ca573ec8 100644 --- a/src/syscall/zerrors_netbsd_amd64.go +++ b/src/syscall/zerrors_netbsd_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && netbsd -// +build amd64,netbsd package syscall diff --git a/src/syscall/zerrors_netbsd_arm.go b/src/syscall/zerrors_netbsd_arm.go index 9d8678750d4cca..3137e18a2471f5 100644 --- a/src/syscall/zerrors_netbsd_arm.go +++ b/src/syscall/zerrors_netbsd_arm.go @@ -5,7 +5,6 @@ // cgo -godefs -- -marm _const.go //go:build arm && netbsd -// +build arm,netbsd package syscall diff --git a/src/syscall/zerrors_netbsd_arm64.go b/src/syscall/zerrors_netbsd_arm64.go index 7adb72f7edd622..cc1b0088c9ab95 100644 --- a/src/syscall/zerrors_netbsd_arm64.go +++ b/src/syscall/zerrors_netbsd_arm64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build arm64 && netbsd -// +build arm64,netbsd package syscall diff --git a/src/syscall/zerrors_openbsd_386.go b/src/syscall/zerrors_openbsd_386.go index a8457c6edfe8b6..d17ecb96e50c5d 100644 --- a/src/syscall/zerrors_openbsd_386.go +++ b/src/syscall/zerrors_openbsd_386.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m32 _const.go //go:build 386 && openbsd -// +build 386,openbsd package syscall diff --git a/src/syscall/zerrors_openbsd_amd64.go b/src/syscall/zerrors_openbsd_amd64.go index eb19537a2ac9ed..4904e7614f3d18 100644 --- a/src/syscall/zerrors_openbsd_amd64.go +++ b/src/syscall/zerrors_openbsd_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && openbsd -// +build amd64,openbsd package syscall diff --git a/src/syscall/zerrors_openbsd_arm.go b/src/syscall/zerrors_openbsd_arm.go index 89a4e6d89a54b5..76ac9173a93f2a 100644 --- a/src/syscall/zerrors_openbsd_arm.go +++ b/src/syscall/zerrors_openbsd_arm.go @@ -5,7 +5,6 @@ // cgo -godefs -- _const.go //go:build arm && openbsd -// +build arm,openbsd package syscall diff --git a/src/syscall/zerrors_solaris_amd64.go b/src/syscall/zerrors_solaris_amd64.go index b7dee696029654..fb25dacee43f8d 100644 --- a/src/syscall/zerrors_solaris_amd64.go +++ b/src/syscall/zerrors_solaris_amd64.go @@ -5,7 +5,6 @@ // cgo -godefs -- -m64 _const.go //go:build amd64 && solaris -// +build amd64,solaris package syscall diff --git a/src/syscall/zsyscall_aix_ppc64.go b/src/syscall/zsyscall_aix_ppc64.go index 134ae41165bd76..39838a42e651c1 100644 --- a/src/syscall/zsyscall_aix_ppc64.go +++ b/src/syscall/zsyscall_aix_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build aix && ppc64 -// +build aix,ppc64 package syscall @@ -63,6 +62,7 @@ import "unsafe" //go:cgo_import_dynamic libc_Getppid getppid "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getpriority getpriority "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getrlimit getrlimit "libc.a/shr_64.o" +//go:cgo_import_dynamic libc_Getrusage getrusage "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Getuid getuid "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Kill kill "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Lchown lchown "libc.a/shr_64.o" @@ -72,8 +72,8 @@ import "unsafe" //go:cgo_import_dynamic libc_Mkdirat mkdirat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Mknodat mknodat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Open open "libc.a/shr_64.o" -//go:cgo_import_dynamic libc_Pread pread "libc.a/shr_64.o" -//go:cgo_import_dynamic libc_Pwrite pwrite "libc.a/shr_64.o" +//go:cgo_import_dynamic libc_pread pread "libc.a/shr_64.o" +//go:cgo_import_dynamic libc_pwrite pwrite "libc.a/shr_64.o" //go:cgo_import_dynamic libc_read read "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Reboot reboot "libc.a/shr_64.o" //go:cgo_import_dynamic libc_Rename rename "libc.a/shr_64.o" @@ -155,6 +155,7 @@ import "unsafe" //go:linkname libc_Getppid libc_Getppid //go:linkname libc_Getpriority libc_Getpriority //go:linkname libc_Getrlimit libc_Getrlimit +//go:linkname libc_Getrusage libc_Getrusage //go:linkname libc_Getuid libc_Getuid //go:linkname libc_Kill libc_Kill //go:linkname libc_Lchown libc_Lchown @@ -164,8 +165,8 @@ import "unsafe" //go:linkname libc_Mkdirat libc_Mkdirat //go:linkname libc_Mknodat libc_Mknodat //go:linkname libc_Open libc_Open -//go:linkname libc_Pread libc_Pread -//go:linkname libc_Pwrite libc_Pwrite +//go:linkname libc_pread libc_pread +//go:linkname libc_pwrite libc_pwrite //go:linkname libc_read libc_read //go:linkname libc_Reboot libc_Reboot //go:linkname libc_Rename libc_Rename @@ -250,6 +251,7 @@ var ( libc_Getppid, libc_Getpriority, libc_Getrlimit, + libc_Getrusage, libc_Getuid, libc_Kill, libc_Lchown, @@ -259,8 +261,8 @@ var ( libc_Mkdirat, libc_Mknodat, libc_Open, - libc_Pread, - libc_Pwrite, + libc_pread, + libc_pwrite, libc_read, libc_Reboot, libc_Rename, @@ -926,6 +928,16 @@ func Getrlimit(which int, lim *Rlimit) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := rawSyscall6(uintptr(unsafe.Pointer(&libc_Getrusage)), 2, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Getuid() (uid int) { r0, _, _ := rawSyscall6(uintptr(unsafe.Pointer(&libc_Getuid)), 0, 0, 0, 0, 0, 0, 0) uid = int(r0) @@ -1055,12 +1067,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_Pread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_pread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = errnoErr(e1) @@ -1070,12 +1082,12 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_Pwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_pwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = errnoErr(e1) diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go index 07a519d7d65fa9..ee78a572fcbebb 100644 --- a/src/syscall/zsyscall_darwin_amd64.go +++ b/src/syscall/zsyscall_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build darwin && amd64 -// +build darwin,amd64 package syscall @@ -345,6 +344,25 @@ func libc_pipe_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall6(abi.FuncPCABI0(libc_utimensat_trampoline), uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_utimensat_trampoline() + +//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kill(pid int, signum int, posix int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_kill_trampoline), uintptr(pid), uintptr(signum), uintptr(posix)) if e1 != 0 { @@ -1119,7 +1137,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1140,7 +1158,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_darwin_amd64.s b/src/syscall/zsyscall_darwin_amd64.s index 492f9478554875..563083d441a17c 100644 --- a/src/syscall/zsyscall_darwin_amd64.s +++ b/src/syscall/zsyscall_darwin_amd64.s @@ -3,8 +3,6 @@ #include "textflag.h" TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_getfsstat(SB) -TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 - JMP libc_setattrlist(SB) TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fdopendir(SB) TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 @@ -53,6 +51,8 @@ TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe(SB) +TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 + JMP libc_utimensat(SB) TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go index 5ae096730eae21..ac1eccf755978c 100644 --- a/src/syscall/zsyscall_darwin_arm64.go +++ b/src/syscall/zsyscall_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build darwin && arm64 -// +build darwin,arm64 package syscall @@ -345,6 +344,25 @@ func libc_pipe_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall6(abi.FuncPCABI0(libc_utimensat_trampoline), uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_utimensat_trampoline() + +//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kill(pid int, signum int, posix int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_kill_trampoline), uintptr(pid), uintptr(signum), uintptr(posix)) if e1 != 0 { @@ -1119,7 +1137,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1140,7 +1158,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_darwin_arm64.s b/src/syscall/zsyscall_darwin_arm64.s index b606c6e49e35bb..0567a42fa33798 100644 --- a/src/syscall/zsyscall_darwin_arm64.s +++ b/src/syscall/zsyscall_darwin_arm64.s @@ -3,8 +3,6 @@ #include "textflag.h" TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0 JMP libc_getfsstat(SB) -TEXT ·libc_setattrlist_trampoline(SB),NOSPLIT,$0-0 - JMP libc_setattrlist(SB) TEXT ·libc_fdopendir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fdopendir(SB) TEXT ·libc_sendfile_trampoline(SB),NOSPLIT,$0-0 @@ -53,6 +51,8 @@ TEXT ·libc_fcntl_trampoline(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) TEXT ·libc_pipe_trampoline(SB),NOSPLIT,$0-0 JMP libc_pipe(SB) +TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0 + JMP libc_utimensat(SB) TEXT ·libc_kill_trampoline(SB),NOSPLIT,$0-0 JMP libc_kill(SB) TEXT ·libc_access_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_dragonfly_amd64.go b/src/syscall/zsyscall_dragonfly_amd64.go index aa327c00109536..5d5576d76ff646 100644 --- a/src/syscall/zsyscall_dragonfly_amd64.go +++ b/src/syscall/zsyscall_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build dragonfly && amd64 -// +build dragonfly,amd64 package syscall diff --git a/src/syscall/zsyscall_freebsd_386.go b/src/syscall/zsyscall_freebsd_386.go index 7137d66f0b232f..04bad4acc922c9 100644 --- a/src/syscall/zsyscall_freebsd_386.go +++ b/src/syscall/zsyscall_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build freebsd && 386 -// +build freebsd,386 package syscall @@ -906,7 +905,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -923,7 +922,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_freebsd_amd64.go b/src/syscall/zsyscall_freebsd_amd64.go index d721dafde28b7a..eeb9c0cb9b54e7 100644 --- a/src/syscall/zsyscall_freebsd_amd64.go +++ b/src/syscall/zsyscall_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build freebsd && amd64 -// +build freebsd,amd64 package syscall @@ -906,7 +905,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -923,7 +922,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_freebsd_arm.go b/src/syscall/zsyscall_freebsd_arm.go index d9dbea921a1411..8ea4282ba45f43 100644 --- a/src/syscall/zsyscall_freebsd_arm.go +++ b/src/syscall/zsyscall_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build freebsd && arm -// +build freebsd,arm package syscall @@ -906,7 +905,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -923,7 +922,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_freebsd_arm64.go b/src/syscall/zsyscall_freebsd_arm64.go index a24f0115e2ce09..73bf50559d63b3 100644 --- a/src/syscall/zsyscall_freebsd_arm64.go +++ b/src/syscall/zsyscall_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build freebsd && arm64 -// +build freebsd,arm64 package syscall @@ -906,7 +905,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -923,7 +922,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go index ac822d6f7a435d..6a646fba4f5fc0 100644 --- a/src/syscall/zsyscall_linux_386.go +++ b/src/syscall/zsyscall_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && 386 -// +build linux,386 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1048,26 +1072,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Dup2(oldfd int, newfd int) (err error) { _, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0) if e1 != 0 { @@ -1078,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1207,7 +1200,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1224,7 +1217,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go index ed37fa8decaa08..d5b0099a9a70e2 100644 --- a/src/syscall/zsyscall_linux_amd64.go +++ b/src/syscall/zsyscall_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && amd64 -// +build linux,amd64 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1202,7 +1215,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1219,7 +1232,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1390,17 +1403,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1644,23 +1646,3 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go index 213aaf3bac40a1..db5ec6044822be 100644 --- a/src/syscall/zsyscall_linux_arm.go +++ b/src/syscall/zsyscall_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && arm -// +build linux,arm package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1048,37 +1072,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1247,17 +1240,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1515,7 +1497,7 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1532,7 +1514,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go index e2f9c0fd9b06e2..7655ccbb3d7935 100644 --- a/src/syscall/zsyscall_linux_arm64.go +++ b/src/syscall/zsyscall_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && arm64 -// +build linux,arm64 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1085,21 +1109,6 @@ func Fstat(fd int, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1187,7 +1196,7 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1204,7 +1213,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1354,17 +1363,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1555,16 +1553,6 @@ func Gettimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) { r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/src/syscall/zsyscall_linux_loong64.go b/src/syscall/zsyscall_linux_loong64.go new file mode 100644 index 00000000000000..abbc19be6046bf --- /dev/null +++ b/src/syscall/zsyscall_linux_loong64.go @@ -0,0 +1,1533 @@ +// mksyscall.pl -tags linux,loong64 syscall_linux.go syscall_linux_loong64.go +// Code generated by the command above; DO NOT EDIT. + +//go:build linux && loong64 + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func faccessat(dirfd int, path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fchmodat(dirfd int, path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_LINKAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_READLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func symlinkat(oldpath string, newdirfd int, newpath string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINKAT, uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unlinkat(dirfd int, path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flag), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getcwd(buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) { + r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0) + wpid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(arg) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(source) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(target) + if err != nil { + return + } + var _p2 *byte + _p2, err = BytePtrFromString(fstype) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Acct(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Adjtimex(buf *Timex) (state int, err error) { + r0, _, e1 := Syscall(SYS_ADJTIMEX, uintptr(unsafe.Pointer(buf)), 0, 0) + state = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chroot(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(oldfd int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup3(oldfd int, newfd int, flags int) (err error) { + _, _, e1 := Syscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func EpollCreate1(flag int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE1, uintptr(flag), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { + _, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fallocate(fd int, mode uint32, off int64, len int64) (err error) { + _, _, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off), uintptr(len), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchdir(fd int) (err error) { + _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmod(fd int, mode uint32) (err error) { + _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntl(fd int, cmd int, arg int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)) + val = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fdatasync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FDATASYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flock(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdents(fd int, buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(_p0), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0) + pgid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpid() (pid int) { + r0, _ := rawSyscallNoError(SYS_GETPID, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getppid() (ppid int) { + r0, _ := rawSyscallNoError(SYS_GETPPID, 0, 0, 0) + ppid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpriority(which int, who int) (prio int, err error) { + r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0) + prio = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettid() (tid int) { + r0, _ := rawSyscallNoError(SYS_GETTID, 0, 0, 0) + tid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getxattr(path string, attr string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(dest) > 0 { + _p2 = unsafe.Pointer(&dest[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0) + sz = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(pathname) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask)) + watchdesc = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func InotifyInit1(flags int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0) + success = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kill(pid int, sig Signal) (err error) { + _, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Klogctl(typ int, buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listxattr(path string, dest []byte) (sz int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(dest) > 0 { + _p1 = unsafe.Pointer(&dest[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest))) + sz = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdirat(dirfd int, path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Nanosleep(time *Timespec, leftover *Timespec) (err error) { + _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func PivotRoot(newroot string, putold string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(newroot) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(putold) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) { + _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Removexattr(path string, attr string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setdomainname(p []byte) (err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sethostname(p []byte) (err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpgid(pid int, pgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Settimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpriority(which int, who int, prio int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setxattr(path string, attr string, data []byte, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attr) + if err != nil { + return + } + var _p2 unsafe.Pointer + if len(data) > 0 { + _p2 = unsafe.Pointer(&data[0]) + } else { + _p2 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sync() { + Syscall(SYS_SYNC, 0, 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sysinfo(info *Sysinfo_t) (err error) { + _, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { + r0, _, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0) + n = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Tgkill(tgid int, tid int, sig Signal) (err error) { + _, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Times(tms *Tms) (ticks uintptr, err error) { + r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0) + ticks = uintptr(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Umask(mask int) (oldmask int) { + r0, _ := rawSyscallNoError(SYS_UMASK, uintptr(mask), 0, 0) + oldmask = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Uname(buf *Utsname) (err error) { + _, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unmount(target string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(target) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unshare(flags int) (err error) { + _, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func write(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func exitThread(code int) (err error) { + _, _, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlen(fd int, p *byte, np int) (n int, err error) { + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writelen(fd int, p *byte, np int) (n int, err error) { + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func munmap(addr uintptr, length uintptr) (err error) { + _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Madvise(b []byte, advice int) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(advice)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mprotect(b []byte, prot int) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MPROTECT, uintptr(_p0), uintptr(len(b)), uintptr(prot)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mlock(b []byte) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MLOCK, uintptr(_p0), uintptr(len(b)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Munlock(b []byte) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MUNLOCK, uintptr(_p0), uintptr(len(b)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mlockall(flags int) (err error) { + _, _, e1 := Syscall(SYS_MLOCKALL, uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Munlockall() (err error) { + _, _, e1 := Syscall(SYS_MUNLOCKALL, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatfs(fd int, buf *Statfs_t) (err error) { + _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _ := rawSyscallNoError(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (euid int) { + r0, _ := rawSyscallNoError(SYS_GETEUID, 0, 0, 0) + euid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _ := rawSyscallNoError(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _ := rawSyscallNoError(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, n int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(n), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT2, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (off int64, err error) { + r0, _, e1 := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence)) + off = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) + written = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setfsgid(gid int) (err error) { + _, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setfsuid(uid int) (err error) { + _, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) { + r0, _, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags)) + n = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func statx(dirfd int, path string, flags int, mask int, stat *statx_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_STATX, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mask), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Statfs(path string, buf *Statfs_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func SyncFileRange(fd int, off int64, n int64, flags int) (err error) { + _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE, uintptr(fd), uintptr(off), uintptr(n), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(n int, list *_Gid_t) (nn int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0) + nn = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { + r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)) + xaddr = uintptr(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_t) (n int, err error) { + r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) { + r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/src/syscall/zsyscall_linux_mips.go b/src/syscall/zsyscall_linux_mips.go index 617c2f54663309..792668c1d5c375 100644 --- a/src/syscall/zsyscall_linux_mips.go +++ b/src/syscall/zsyscall_linux_mips.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && mips -// +build linux,mips package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1171,7 +1184,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1188,7 +1201,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1323,17 +1336,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1646,28 +1648,6 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe() (p1 int, p2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - p1 = int(r0) - p2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) { r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset)) xaddr = uintptr(r0) diff --git a/src/syscall/zsyscall_linux_mips64.go b/src/syscall/zsyscall_linux_mips64.go index 793d4b98846a4e..27dbcb8a2d24a9 100644 --- a/src/syscall/zsyscall_linux_mips64.go +++ b/src/syscall/zsyscall_linux_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && mips64 -// +build linux,mips64 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1202,7 +1215,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1219,7 +1232,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1379,17 +1392,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1642,16 +1644,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_mips64le.go b/src/syscall/zsyscall_linux_mips64le.go index 54e1760bda6d08..0362303c5f4ce4 100644 --- a/src/syscall/zsyscall_linux_mips64le.go +++ b/src/syscall/zsyscall_linux_mips64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && mips64le -// +build linux,mips64le package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1202,7 +1215,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1219,7 +1232,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1379,17 +1392,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1642,16 +1644,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_mipsle.go b/src/syscall/zsyscall_linux_mipsle.go index ba7e2118c04e57..5320dfa65a7f53 100644 --- a/src/syscall/zsyscall_linux_mipsle.go +++ b/src/syscall/zsyscall_linux_mipsle.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && mipsle -// +build linux,mipsle package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fchown(fd int, uid int, gid int) (err error) { _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) if e1 != 0 { @@ -1171,7 +1184,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1188,7 +1201,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1323,17 +1336,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1646,28 +1648,6 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe() (p1 int, p2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - p1 = int(r0) - p2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) { r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset)) xaddr = uintptr(r0) diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go index c3437722e02f50..61f3063352c871 100644 --- a/src/syscall/zsyscall_linux_ppc64.go +++ b/src/syscall/zsyscall_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && ppc64 -// +build linux,ppc64 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { var _p0 unsafe.Pointer if len(events) > 0 { @@ -1264,7 +1277,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1281,7 +1294,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1457,17 +1470,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1703,16 +1705,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func syncFileRange2(fd int, flags int, off int64, n int64) (err error) { _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go index acc34a76d2b631..3e14ab37a1dfe1 100644 --- a/src/syscall/zsyscall_linux_ppc64le.go +++ b/src/syscall/zsyscall_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && ppc64le -// +build linux,ppc64le package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { var _p0 unsafe.Pointer if len(events) > 0 { @@ -1264,7 +1277,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1281,7 +1294,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1457,17 +1470,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1703,16 +1705,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func syncFileRange2(fd int, flags int, off int64, n int64) (err error) { _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_riscv64.go b/src/syscall/zsyscall_linux_riscv64.go index d662d780db9d8f..4a3fa5dbd86b91 100644 --- a/src/syscall/zsyscall_linux_riscv64.go +++ b/src/syscall/zsyscall_linux_riscv64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && riscv64 -// +build linux,riscv64 package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1085,21 +1109,6 @@ func Fstat(fd int, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1187,7 +1196,7 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1204,7 +1213,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1354,17 +1363,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -1555,16 +1553,6 @@ func Gettimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) { r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/src/syscall/zsyscall_linux_s390x.go b/src/syscall/zsyscall_linux_s390x.go index 20f8c61366ca1e..1fca71e3044511 100644 --- a/src/syscall/zsyscall_linux_s390x.go +++ b/src/syscall/zsyscall_linux_s390x.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build linux && s390x -// +build linux,s390x package syscall @@ -25,6 +24,21 @@ func faccessat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func faccessat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(_SYS_faccessat2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fchmodat(dirfd int, path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -76,6 +90,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1058,17 +1082,6 @@ func Dup2(oldfd int, newfd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func EpollCreate(size int) (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { var _p0 unsafe.Pointer if len(events) > 0 { @@ -1234,7 +1247,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1251,7 +1264,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1490,13 +1503,3 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/src/syscall/zsyscall_netbsd_386.go b/src/syscall/zsyscall_netbsd_386.go index 07ff5fba5f6a51..9b9285920601a8 100644 --- a/src/syscall/zsyscall_netbsd_386.go +++ b/src/syscall/zsyscall_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build netbsd && 386 -// +build netbsd,386 package syscall @@ -817,7 +816,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -834,7 +833,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_netbsd_amd64.go b/src/syscall/zsyscall_netbsd_amd64.go index ffb4e059a4b5aa..ac34c00b5a2935 100644 --- a/src/syscall/zsyscall_netbsd_amd64.go +++ b/src/syscall/zsyscall_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build netbsd && amd64 -// +build netbsd,amd64 package syscall @@ -817,7 +816,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -834,7 +833,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_netbsd_arm.go b/src/syscall/zsyscall_netbsd_arm.go index 37df77e5e8cbbf..2be5e7baa4d32e 100644 --- a/src/syscall/zsyscall_netbsd_arm.go +++ b/src/syscall/zsyscall_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build netbsd && arm -// +build netbsd,arm package syscall @@ -817,7 +816,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -834,7 +833,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_netbsd_arm64.go b/src/syscall/zsyscall_netbsd_arm64.go index c5eb57a2269d6f..02a652bbbb2463 100644 --- a/src/syscall/zsyscall_netbsd_arm64.go +++ b/src/syscall/zsyscall_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build netbsd && arm64 -// +build netbsd,arm64 package syscall @@ -817,7 +816,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -834,7 +833,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_openbsd_386.go b/src/syscall/zsyscall_openbsd_386.go index 1ff5c95fa19855..f7986d5ea5c94a 100644 --- a/src/syscall/zsyscall_openbsd_386.go +++ b/src/syscall/zsyscall_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build openbsd && 386 -// +build openbsd,386 package syscall @@ -552,6 +551,20 @@ func libc_dup2_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func dup3(from int, to int, flags int) (err error) { + _, _, e1 := syscall(abi.FuncPCABI0(libc_dup3_trampoline), uintptr(from), uintptr(to), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_dup3_trampoline() + +//go:cgo_import_dynamic libc_dup3 dup3 "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchdir(fd int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_fchdir_trampoline), uintptr(fd), 0, 0) if e1 != 0 { @@ -1092,7 +1105,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1113,7 +1126,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_openbsd_386.s b/src/syscall/zsyscall_openbsd_386.s index d47a4f480d914a..e2c58625bb3b1c 100644 --- a/src/syscall/zsyscall_openbsd_386.s +++ b/src/syscall/zsyscall_openbsd_386.s @@ -69,6 +69,8 @@ TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) +TEXT ·libc_dup3_trampoline(SB),NOSPLIT,$0-0 + JMP libc_dup3(SB) TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_openbsd_amd64.go b/src/syscall/zsyscall_openbsd_amd64.go index 85cb195739f296..605443d8904370 100644 --- a/src/syscall/zsyscall_openbsd_amd64.go +++ b/src/syscall/zsyscall_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build openbsd && amd64 -// +build openbsd,amd64 package syscall @@ -552,6 +551,20 @@ func libc_dup2_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func dup3(from int, to int, flags int) (err error) { + _, _, e1 := syscall(abi.FuncPCABI0(libc_dup3_trampoline), uintptr(from), uintptr(to), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_dup3_trampoline() + +//go:cgo_import_dynamic libc_dup3 dup3 "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchdir(fd int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_fchdir_trampoline), uintptr(fd), 0, 0) if e1 != 0 { @@ -1092,7 +1105,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1113,7 +1126,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_openbsd_amd64.s b/src/syscall/zsyscall_openbsd_amd64.s index e5c5dde930bb60..964c9ed9e1236d 100644 --- a/src/syscall/zsyscall_openbsd_amd64.s +++ b/src/syscall/zsyscall_openbsd_amd64.s @@ -69,6 +69,8 @@ TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) +TEXT ·libc_dup3_trampoline(SB),NOSPLIT,$0-0 + JMP libc_dup3(SB) TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_openbsd_arm.go b/src/syscall/zsyscall_openbsd_arm.go index 04a2fadccddde2..0f2312fbc407bb 100644 --- a/src/syscall/zsyscall_openbsd_arm.go +++ b/src/syscall/zsyscall_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build openbsd && arm -// +build openbsd,arm package syscall @@ -552,6 +551,20 @@ func libc_dup2_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func dup3(from int, to int, flags int) (err error) { + _, _, e1 := syscall(abi.FuncPCABI0(libc_dup3_trampoline), uintptr(from), uintptr(to), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_dup3_trampoline() + +//go:cgo_import_dynamic libc_dup3 dup3 "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchdir(fd int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_fchdir_trampoline), uintptr(fd), 0, 0) if e1 != 0 { @@ -1092,7 +1105,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1113,7 +1126,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_openbsd_arm.s b/src/syscall/zsyscall_openbsd_arm.s index d33f3aa3e05cf0..5975780edb272b 100644 --- a/src/syscall/zsyscall_openbsd_arm.s +++ b/src/syscall/zsyscall_openbsd_arm.s @@ -69,6 +69,8 @@ TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) +TEXT ·libc_dup3_trampoline(SB),NOSPLIT,$0-0 + JMP libc_dup3(SB) TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_openbsd_arm64.go b/src/syscall/zsyscall_openbsd_arm64.go index fa35905d1bcf9c..1367e2aba9ee74 100644 --- a/src/syscall/zsyscall_openbsd_arm64.go +++ b/src/syscall/zsyscall_openbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build openbsd && arm64 -// +build openbsd,arm64 package syscall @@ -552,6 +551,20 @@ func libc_dup2_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func dup3(from int, to int, flags int) (err error) { + _, _, e1 := syscall(abi.FuncPCABI0(libc_dup3_trampoline), uintptr(from), uintptr(to), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +func libc_dup3_trampoline() + +//go:cgo_import_dynamic libc_dup3 dup3 "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fchdir(fd int) (err error) { _, _, e1 := syscall(abi.FuncPCABI0(libc_fchdir_trampoline), uintptr(fd), 0, 0) if e1 != 0 { @@ -1092,7 +1105,7 @@ func libc_pathconf_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1113,7 +1126,7 @@ func libc_pread_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_openbsd_arm64.s b/src/syscall/zsyscall_openbsd_arm64.s index 37778b1db5417b..2c4a0b0faf57d3 100644 --- a/src/syscall/zsyscall_openbsd_arm64.s +++ b/src/syscall/zsyscall_openbsd_arm64.s @@ -69,6 +69,8 @@ TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup(SB) TEXT ·libc_dup2_trampoline(SB),NOSPLIT,$0-0 JMP libc_dup2(SB) +TEXT ·libc_dup3_trampoline(SB),NOSPLIT,$0-0 + JMP libc_dup3(SB) TEXT ·libc_fchdir_trampoline(SB),NOSPLIT,$0-0 JMP libc_fchdir(SB) TEXT ·libc_fchflags_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/syscall/zsyscall_openbsd_mips64.go b/src/syscall/zsyscall_openbsd_mips64.go index 70fd1474fd3f9b..2cf84653191b57 100644 --- a/src/syscall/zsyscall_openbsd_mips64.go +++ b/src/syscall/zsyscall_openbsd_mips64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build openbsd && mips64 -// +build openbsd,mips64 package syscall @@ -811,7 +810,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -828,7 +827,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/src/syscall/zsyscall_plan9_386.go b/src/syscall/zsyscall_plan9_386.go index 8b7727b3435e88..32fa0f0eb6e2b7 100644 --- a/src/syscall/zsyscall_plan9_386.go +++ b/src/syscall/zsyscall_plan9_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build plan9 && 386 -// +build plan9,386 package syscall diff --git a/src/syscall/zsyscall_plan9_amd64.go b/src/syscall/zsyscall_plan9_amd64.go index bed9108ea6e2d5..b3337ce8110c97 100644 --- a/src/syscall/zsyscall_plan9_amd64.go +++ b/src/syscall/zsyscall_plan9_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build plan9 && amd64 -// +build plan9,amd64 package syscall diff --git a/src/syscall/zsyscall_plan9_arm.go b/src/syscall/zsyscall_plan9_arm.go index 7bbcf9b4b79506..3705566fa3a489 100644 --- a/src/syscall/zsyscall_plan9_arm.go +++ b/src/syscall/zsyscall_plan9_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build plan9 && arm -// +build plan9,arm package syscall diff --git a/src/syscall/zsyscall_solaris_amd64.go b/src/syscall/zsyscall_solaris_amd64.go index 9b37dc09506f28..a2bbeed844915c 100644 --- a/src/syscall/zsyscall_solaris_amd64.go +++ b/src/syscall/zsyscall_solaris_amd64.go @@ -2,12 +2,13 @@ // Code generated by the command above; DO NOT EDIT. //go:build solaris && amd64 -// +build solaris,amd64 package syscall import "unsafe" +//go:cgo_import_dynamic libc_pipe2 pipe2 "libc.so" +//go:cgo_import_dynamic libc_accept4 accept4 "libsocket.so" //go:cgo_import_dynamic libc_Getcwd getcwd "libc.so" //go:cgo_import_dynamic libc_getgroups getgroups "libc.so" //go:cgo_import_dynamic libc_setgroups setgroups "libc.so" @@ -35,6 +36,7 @@ import "unsafe" //go:cgo_import_dynamic libc_Getppid getppid "libc.so" //go:cgo_import_dynamic libc_Getpriority getpriority "libc.so" //go:cgo_import_dynamic libc_Getrlimit getrlimit "libc.so" +//go:cgo_import_dynamic libc_Getrusage getrusage "libc.so" //go:cgo_import_dynamic libc_Gettimeofday gettimeofday "libc.so" //go:cgo_import_dynamic libc_Getuid getuid "libc.so" //go:cgo_import_dynamic libc_Kill kill "libc.so" @@ -47,8 +49,8 @@ import "unsafe" //go:cgo_import_dynamic libc_Nanosleep nanosleep "libc.so" //go:cgo_import_dynamic libc_Open open "libc.so" //go:cgo_import_dynamic libc_Pathconf pathconf "libc.so" -//go:cgo_import_dynamic libc_Pread pread "libc.so" -//go:cgo_import_dynamic libc_Pwrite pwrite "libc.so" +//go:cgo_import_dynamic libc_pread pread "libc.so" +//go:cgo_import_dynamic libc_pwrite pwrite "libc.so" //go:cgo_import_dynamic libc_read read "libc.so" //go:cgo_import_dynamic libc_Readlink readlink "libc.so" //go:cgo_import_dynamic libc_Rename rename "libc.so" @@ -92,6 +94,8 @@ import "unsafe" //go:cgo_import_dynamic libc_getexecname getexecname "libc.so" //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" +//go:linkname libc_pipe2 libc_pipe2 +//go:linkname libc_accept4 libc_accept4 //go:linkname libc_Getcwd libc_Getcwd //go:linkname libc_getgroups libc_getgroups //go:linkname libc_setgroups libc_setgroups @@ -119,6 +123,7 @@ import "unsafe" //go:linkname libc_Getppid libc_Getppid //go:linkname libc_Getpriority libc_Getpriority //go:linkname libc_Getrlimit libc_Getrlimit +//go:linkname libc_Getrusage libc_Getrusage //go:linkname libc_Gettimeofday libc_Gettimeofday //go:linkname libc_Getuid libc_Getuid //go:linkname libc_Kill libc_Kill @@ -131,8 +136,8 @@ import "unsafe" //go:linkname libc_Nanosleep libc_Nanosleep //go:linkname libc_Open libc_Open //go:linkname libc_Pathconf libc_Pathconf -//go:linkname libc_Pread libc_Pread -//go:linkname libc_Pwrite libc_Pwrite +//go:linkname libc_pread libc_pread +//go:linkname libc_pwrite libc_pwrite //go:linkname libc_read libc_read //go:linkname libc_Readlink libc_Readlink //go:linkname libc_Rename libc_Rename @@ -179,6 +184,8 @@ import "unsafe" type libcFunc uintptr var ( + libc_pipe2, + libc_accept4, libc_Getcwd, libc_getgroups, libc_setgroups, @@ -206,6 +213,7 @@ var ( libc_Getppid, libc_Getpriority, libc_Getrlimit, + libc_Getrusage, libc_Gettimeofday, libc_Getuid, libc_Kill, @@ -218,8 +226,8 @@ var ( libc_Nanosleep, libc_Open, libc_Pathconf, - libc_Pread, - libc_Pwrite, + libc_pread, + libc_pwrite, libc_read, libc_Readlink, libc_Rename, @@ -266,6 +274,27 @@ var ( // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc_pipe2)), 2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_accept4)), 4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Getcwd(buf []byte) (n int, err error) { var _p0 *byte if len(buf) > 0 { @@ -568,6 +597,16 @@ func Getrlimit(which int, lim *Rlimit) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc_Getrusage)), 2, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Gettimeofday(tv *Timeval) (err error) { _, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc_Gettimeofday)), 1, uintptr(unsafe.Pointer(tv)), 0, 0, 0, 0, 0) if e1 != 0 { @@ -728,12 +767,12 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_Pread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_pread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = errnoErr(e1) @@ -743,12 +782,12 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_Pwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_pwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = errnoErr(e1) diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go index 7bfff16be6ac32..61d89f146048ce 100644 --- a/src/syscall/zsyscall_windows.go +++ b/src/syscall/zsyscall_windows.go @@ -305,7 +305,7 @@ func RegCloseKey(key Handle) (regerrno error) { return } -func RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) { +func regEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) { r0, _, _ := Syscall9(procRegEnumKeyExW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(classLen)), uintptr(unsafe.Pointer(lastWriteTime)), 0) if r0 != 0 { regerrno = Errno(r0) @@ -1016,7 +1016,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree return } -func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { +func readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] @@ -1158,7 +1158,7 @@ func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, return } -func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { +func writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] diff --git a/src/syscall/zsysnum_darwin_amd64.go b/src/syscall/zsysnum_darwin_amd64.go index f66f7d2715feba..08e003f2926448 100644 --- a/src/syscall/zsysnum_darwin_amd64.go +++ b/src/syscall/zsysnum_darwin_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && darwin -// +build amd64,darwin package syscall diff --git a/src/syscall/zsysnum_darwin_arm64.go b/src/syscall/zsysnum_darwin_arm64.go index 6fa146368aff59..71309bb4d6f7f8 100644 --- a/src/syscall/zsysnum_darwin_arm64.go +++ b/src/syscall/zsysnum_darwin_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && darwin -// +build arm64,darwin package syscall diff --git a/src/syscall/zsysnum_dragonfly_amd64.go b/src/syscall/zsysnum_dragonfly_amd64.go index e8996db0f8dd58..03d4b06a09b401 100644 --- a/src/syscall/zsysnum_dragonfly_amd64.go +++ b/src/syscall/zsysnum_dragonfly_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && dragonfly -// +build amd64,dragonfly package syscall diff --git a/src/syscall/zsysnum_freebsd_386.go b/src/syscall/zsysnum_freebsd_386.go index 1ed7e3ee8d3e3d..355b2ec3039cda 100644 --- a/src/syscall/zsysnum_freebsd_386.go +++ b/src/syscall/zsysnum_freebsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && freebsd -// +build 386,freebsd package syscall diff --git a/src/syscall/zsysnum_freebsd_amd64.go b/src/syscall/zsysnum_freebsd_amd64.go index d72dbc944a1b74..84c821c955f9e1 100644 --- a/src/syscall/zsysnum_freebsd_amd64.go +++ b/src/syscall/zsysnum_freebsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && freebsd -// +build amd64,freebsd package syscall diff --git a/src/syscall/zsysnum_freebsd_arm.go b/src/syscall/zsysnum_freebsd_arm.go index 4f4dc4db7959e3..785e7875f40d29 100644 --- a/src/syscall/zsysnum_freebsd_arm.go +++ b/src/syscall/zsysnum_freebsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && freebsd -// +build arm,freebsd package syscall diff --git a/src/syscall/zsysnum_freebsd_arm64.go b/src/syscall/zsysnum_freebsd_arm64.go index ab1a05258e7e95..7144a8abed126c 100644 --- a/src/syscall/zsysnum_freebsd_arm64.go +++ b/src/syscall/zsysnum_freebsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && freebsd -// +build arm64,freebsd package syscall diff --git a/src/syscall/zsysnum_linux_386.go b/src/syscall/zsysnum_linux_386.go index 792f43550e99f5..4966d2a94714e8 100644 --- a/src/syscall/zsysnum_linux_386.go +++ b/src/syscall/zsysnum_linux_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && linux -// +build 386,linux package syscall diff --git a/src/syscall/zsysnum_linux_amd64.go b/src/syscall/zsysnum_linux_amd64.go index 9ea18d6111398c..576c7c36a6b7eb 100644 --- a/src/syscall/zsysnum_linux_amd64.go +++ b/src/syscall/zsysnum_linux_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && linux -// +build amd64,linux package syscall diff --git a/src/syscall/zsysnum_linux_arm.go b/src/syscall/zsysnum_linux_arm.go index ccae9c15e32249..b0da97c64bc32d 100644 --- a/src/syscall/zsysnum_linux_arm.go +++ b/src/syscall/zsysnum_linux_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && linux -// +build arm,linux package syscall diff --git a/src/syscall/zsysnum_linux_arm64.go b/src/syscall/zsysnum_linux_arm64.go index 17c54a2c833bf4..0136d9440b897a 100644 --- a/src/syscall/zsysnum_linux_arm64.go +++ b/src/syscall/zsysnum_linux_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && linux -// +build arm64,linux package syscall diff --git a/src/syscall/zsysnum_linux_loong64.go b/src/syscall/zsysnum_linux_loong64.go new file mode 100644 index 00000000000000..b117438f02fe37 --- /dev/null +++ b/src/syscall/zsysnum_linux_loong64.go @@ -0,0 +1,307 @@ +// mksysnum_linux.pl /usr/include/asm-generic/unistd.h +// Code generated by the command above; DO NOT EDIT. + +package syscall + +const ( + SYS_IO_SETUP = 0 + SYS_IO_DESTROY = 1 + SYS_IO_SUBMIT = 2 + SYS_IO_CANCEL = 3 + SYS_IO_GETEVENTS = 4 + SYS_SETXATTR = 5 + SYS_LSETXATTR = 6 + SYS_FSETXATTR = 7 + SYS_GETXATTR = 8 + SYS_LGETXATTR = 9 + SYS_FGETXATTR = 10 + SYS_LISTXATTR = 11 + SYS_LLISTXATTR = 12 + SYS_FLISTXATTR = 13 + SYS_REMOVEXATTR = 14 + SYS_LREMOVEXATTR = 15 + SYS_FREMOVEXATTR = 16 + SYS_GETCWD = 17 + SYS_LOOKUP_DCOOKIE = 18 + SYS_EVENTFD2 = 19 + SYS_EPOLL_CREATE1 = 20 + SYS_EPOLL_CTL = 21 + SYS_EPOLL_PWAIT = 22 + SYS_DUP = 23 + SYS_DUP3 = 24 + SYS_FCNTL = 25 + SYS_INOTIFY_INIT1 = 26 + SYS_INOTIFY_ADD_WATCH = 27 + SYS_INOTIFY_RM_WATCH = 28 + SYS_IOCTL = 29 + SYS_IOPRIO_SET = 30 + SYS_IOPRIO_GET = 31 + SYS_FLOCK = 32 + SYS_MKNODAT = 33 + SYS_MKDIRAT = 34 + SYS_UNLINKAT = 35 + SYS_SYMLINKAT = 36 + SYS_LINKAT = 37 + SYS_UMOUNT2 = 39 + SYS_MOUNT = 40 + SYS_PIVOT_ROOT = 41 + SYS_NFSSERVCTL = 42 + SYS_STATFS = 43 + SYS_FSTATFS = 44 + SYS_TRUNCATE = 45 + SYS_FTRUNCATE = 46 + SYS_FALLOCATE = 47 + SYS_FACCESSAT = 48 + SYS_CHDIR = 49 + SYS_FCHDIR = 50 + SYS_CHROOT = 51 + SYS_FCHMOD = 52 + SYS_FCHMODAT = 53 + SYS_FCHOWNAT = 54 + SYS_FCHOWN = 55 + SYS_OPENAT = 56 + SYS_CLOSE = 57 + SYS_VHANGUP = 58 + SYS_PIPE2 = 59 + SYS_QUOTACTL = 60 + SYS_GETDENTS64 = 61 + SYS_LSEEK = 62 + SYS_READ = 63 + SYS_WRITE = 64 + SYS_READV = 65 + SYS_WRITEV = 66 + SYS_PREAD64 = 67 + SYS_PWRITE64 = 68 + SYS_PREADV = 69 + SYS_PWRITEV = 70 + SYS_SENDFILE = 71 + SYS_PSELECT6 = 72 + SYS_PPOLL = 73 + SYS_SIGNALFD4 = 74 + SYS_VMSPLICE = 75 + SYS_SPLICE = 76 + SYS_TEE = 77 + SYS_READLINKAT = 78 + SYS_SYNC = 81 + SYS_FSYNC = 82 + SYS_FDATASYNC = 83 + SYS_SYNC_FILE_RANGE = 84 + SYS_TIMERFD_CREATE = 85 + SYS_TIMERFD_SETTIME = 86 + SYS_TIMERFD_GETTIME = 87 + SYS_UTIMENSAT = 88 + SYS_ACCT = 89 + SYS_CAPGET = 90 + SYS_CAPSET = 91 + SYS_PERSONALITY = 92 + SYS_EXIT = 93 + SYS_EXIT_GROUP = 94 + SYS_WAITID = 95 + SYS_SET_TID_ADDRESS = 96 + SYS_UNSHARE = 97 + SYS_FUTEX = 98 + SYS_SET_ROBUST_LIST = 99 + SYS_GET_ROBUST_LIST = 100 + SYS_NANOSLEEP = 101 + SYS_GETITIMER = 102 + SYS_SETITIMER = 103 + SYS_KEXEC_LOAD = 104 + SYS_INIT_MODULE = 105 + SYS_DELETE_MODULE = 106 + SYS_TIMER_CREATE = 107 + SYS_TIMER_GETTIME = 108 + SYS_TIMER_GETOVERRUN = 109 + SYS_TIMER_SETTIME = 110 + SYS_TIMER_DELETE = 111 + SYS_CLOCK_SETTIME = 112 + SYS_CLOCK_GETTIME = 113 + SYS_CLOCK_GETRES = 114 + SYS_CLOCK_NANOSLEEP = 115 + SYS_SYSLOG = 116 + SYS_PTRACE = 117 + SYS_SCHED_SETPARAM = 118 + SYS_SCHED_SETSCHEDULER = 119 + SYS_SCHED_GETSCHEDULER = 120 + SYS_SCHED_GETPARAM = 121 + SYS_SCHED_SETAFFINITY = 122 + SYS_SCHED_GETAFFINITY = 123 + SYS_SCHED_YIELD = 124 + SYS_SCHED_GET_PRIORITY_MAX = 125 + SYS_SCHED_GET_PRIORITY_MIN = 126 + SYS_SCHED_RR_GET_INTERVAL = 127 + SYS_RESTART_SYSCALL = 128 + SYS_KILL = 129 + SYS_TKILL = 130 + SYS_TGKILL = 131 + SYS_SIGALTSTACK = 132 + SYS_RT_SIGSUSPEND = 133 + SYS_RT_SIGACTION = 134 + SYS_RT_SIGPROCMASK = 135 + SYS_RT_SIGPENDING = 136 + SYS_RT_SIGTIMEDWAIT = 137 + SYS_RT_SIGQUEUEINFO = 138 + SYS_RT_SIGRETURN = 139 + SYS_SETPRIORITY = 140 + SYS_GETPRIORITY = 141 + SYS_REBOOT = 142 + SYS_SETREGID = 143 + SYS_SETGID = 144 + SYS_SETREUID = 145 + SYS_SETUID = 146 + SYS_SETRESUID = 147 + SYS_GETRESUID = 148 + SYS_SETRESGID = 149 + SYS_GETRESGID = 150 + SYS_SETFSUID = 151 + SYS_SETFSGID = 152 + SYS_TIMES = 153 + SYS_SETPGID = 154 + SYS_GETPGID = 155 + SYS_GETSID = 156 + SYS_SETSID = 157 + SYS_GETGROUPS = 158 + SYS_SETGROUPS = 159 + SYS_UNAME = 160 + SYS_SETHOSTNAME = 161 + SYS_SETDOMAINNAME = 162 + SYS_GETRUSAGE = 165 + SYS_UMASK = 166 + SYS_PRCTL = 167 + SYS_GETCPU = 168 + SYS_GETTIMEOFDAY = 169 + SYS_SETTIMEOFDAY = 170 + SYS_ADJTIMEX = 171 + SYS_GETPID = 172 + SYS_GETPPID = 173 + SYS_GETUID = 174 + SYS_GETEUID = 175 + SYS_GETGID = 176 + SYS_GETEGID = 177 + SYS_GETTID = 178 + SYS_SYSINFO = 179 + SYS_MQ_OPEN = 180 + SYS_MQ_UNLINK = 181 + SYS_MQ_TIMEDSEND = 182 + SYS_MQ_TIMEDRECEIVE = 183 + SYS_MQ_NOTIFY = 184 + SYS_MQ_GETSETATTR = 185 + SYS_MSGGET = 186 + SYS_MSGCTL = 187 + SYS_MSGRCV = 188 + SYS_MSGSND = 189 + SYS_SEMGET = 190 + SYS_SEMCTL = 191 + SYS_SEMTIMEDOP = 192 + SYS_SEMOP = 193 + SYS_SHMGET = 194 + SYS_SHMCTL = 195 + SYS_SHMAT = 196 + SYS_SHMDT = 197 + SYS_SOCKET = 198 + SYS_SOCKETPAIR = 199 + SYS_BIND = 200 + SYS_LISTEN = 201 + SYS_ACCEPT = 202 + SYS_CONNECT = 203 + SYS_GETSOCKNAME = 204 + SYS_GETPEERNAME = 205 + SYS_SENDTO = 206 + SYS_RECVFROM = 207 + SYS_SETSOCKOPT = 208 + SYS_GETSOCKOPT = 209 + SYS_SHUTDOWN = 210 + SYS_SENDMSG = 211 + SYS_RECVMSG = 212 + SYS_READAHEAD = 213 + SYS_BRK = 214 + SYS_MUNMAP = 215 + SYS_MREMAP = 216 + SYS_ADD_KEY = 217 + SYS_REQUEST_KEY = 218 + SYS_KEYCTL = 219 + SYS_CLONE = 220 + SYS_EXECVE = 221 + SYS_MMAP = 222 + SYS_FADVISE64 = 223 + SYS_SWAPON = 224 + SYS_SWAPOFF = 225 + SYS_MPROTECT = 226 + SYS_MSYNC = 227 + SYS_MLOCK = 228 + SYS_MUNLOCK = 229 + SYS_MLOCKALL = 230 + SYS_MUNLOCKALL = 231 + SYS_MINCORE = 232 + SYS_MADVISE = 233 + SYS_REMAP_FILE_PAGES = 234 + SYS_MBIND = 235 + SYS_GET_MEMPOLICY = 236 + SYS_SET_MEMPOLICY = 237 + SYS_MIGRATE_PAGES = 238 + SYS_MOVE_PAGES = 239 + SYS_RT_TGSIGQUEUEINFO = 240 + SYS_PERF_EVENT_OPEN = 241 + SYS_ACCEPT4 = 242 + SYS_RECVMMSG = 243 + SYS_ARCH_SPECIFIC_SYSCALL = 244 + SYS_WAIT4 = 260 + SYS_PRLIMIT64 = 261 + SYS_FANOTIFY_INIT = 262 + SYS_FANOTIFY_MARK = 263 + SYS_NAME_TO_HANDLE_AT = 264 + SYS_OPEN_BY_HANDLE_AT = 265 + SYS_CLOCK_ADJTIME = 266 + SYS_SYNCFS = 267 + SYS_SETNS = 268 + SYS_SENDMMSG = 269 + SYS_PROCESS_VM_READV = 270 + SYS_PROCESS_VM_WRITEV = 271 + SYS_KCMP = 272 + SYS_FINIT_MODULE = 273 + SYS_SCHED_SETATTR = 274 + SYS_SCHED_GETATTR = 275 + SYS_RENAMEAT2 = 276 + SYS_SECCOMP = 277 + SYS_GETRANDOM = 278 + SYS_MEMFD_CREATE = 279 + SYS_BPF = 280 + SYS_EXECVEAT = 281 + SYS_USERFAULTFD = 282 + SYS_MEMBARRIER = 283 + SYS_MLOCK2 = 284 + SYS_COPY_FILE_RANGE = 285 + SYS_PREADV2 = 286 + SYS_PWRITEV2 = 287 + SYS_PKEY_MPROTECT = 288 + SYS_PKEY_ALLOC = 289 + SYS_PKEY_FREE = 290 + SYS_STATX = 291 + SYS_IO_PGETEVENTS = 292 + SYS_RSEQ = 293 + SYS_KEXEC_FILE_LOAD = 294 + SYS_PIDFD_SEND_SIGNAL = 424 + SYS_IO_URING_SETUP = 425 + SYS_IO_URING_ENTER = 426 + SYS_IO_URING_REGISTER = 427 + SYS_OPEN_TREE = 428 + SYS_MOVE_MOUNT = 429 + SYS_FSOPEN = 430 + SYS_FSCONFIG = 431 + SYS_FSMOUNT = 432 + SYS_FSPICK = 433 + SYS_PIDFD_OPEN = 434 + SYS_CLOSE_RANGE = 436 + SYS_OPENAT2 = 437 + SYS_PIDFD_GETFD = 438 + SYS_FACCESSAT2 = 439 + SYS_PROCESS_MADVISE = 440 + SYS_EPOLL_PWAIT2 = 441 + SYS_MOUNT_SETATTR = 442 + SYS_QUOTACTL_FD = 443 + SYS_LANDLOCK_CREATE_RULESET = 444 + SYS_LANDLOCK_ADD_RULE = 445 + SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 +) diff --git a/src/syscall/zsysnum_linux_ppc64.go b/src/syscall/zsysnum_linux_ppc64.go index a0d37ff1f27013..cc964c23e2eef0 100644 --- a/src/syscall/zsysnum_linux_ppc64.go +++ b/src/syscall/zsysnum_linux_ppc64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build ppc64 && linux -// +build ppc64,linux package syscall diff --git a/src/syscall/zsysnum_linux_ppc64le.go b/src/syscall/zsysnum_linux_ppc64le.go index f8f82d20432359..57bfb7795f2341 100644 --- a/src/syscall/zsysnum_linux_ppc64le.go +++ b/src/syscall/zsysnum_linux_ppc64le.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build ppc64le && linux -// +build ppc64le,linux package syscall diff --git a/src/syscall/zsysnum_netbsd_386.go b/src/syscall/zsysnum_netbsd_386.go index fd0c32995142f8..5696c4befebc89 100644 --- a/src/syscall/zsysnum_netbsd_386.go +++ b/src/syscall/zsysnum_netbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && netbsd -// +build 386,netbsd package syscall diff --git a/src/syscall/zsysnum_netbsd_amd64.go b/src/syscall/zsysnum_netbsd_amd64.go index 03f2cd3bd328e7..9fb85cddbaf858 100644 --- a/src/syscall/zsysnum_netbsd_amd64.go +++ b/src/syscall/zsysnum_netbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && netbsd -// +build amd64,netbsd package syscall diff --git a/src/syscall/zsysnum_netbsd_arm.go b/src/syscall/zsysnum_netbsd_arm.go index 7b356e7102c06b..e0e8994641ab71 100644 --- a/src/syscall/zsysnum_netbsd_arm.go +++ b/src/syscall/zsysnum_netbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && netbsd -// +build arm,netbsd package syscall diff --git a/src/syscall/zsysnum_netbsd_arm64.go b/src/syscall/zsysnum_netbsd_arm64.go index 3f57ec4d894e09..9653364dae9411 100644 --- a/src/syscall/zsysnum_netbsd_arm64.go +++ b/src/syscall/zsysnum_netbsd_arm64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm64 && netbsd -// +build arm64,netbsd package syscall diff --git a/src/syscall/zsysnum_openbsd_386.go b/src/syscall/zsysnum_openbsd_386.go index b289886d6c61fc..3b12639bd73993 100644 --- a/src/syscall/zsysnum_openbsd_386.go +++ b/src/syscall/zsysnum_openbsd_386.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build 386 && openbsd -// +build 386,openbsd package syscall diff --git a/src/syscall/zsysnum_openbsd_amd64.go b/src/syscall/zsysnum_openbsd_amd64.go index 8cf2b68dcd3abe..bce309dc5d94db 100644 --- a/src/syscall/zsysnum_openbsd_amd64.go +++ b/src/syscall/zsysnum_openbsd_amd64.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build amd64 && openbsd -// +build amd64,openbsd package syscall diff --git a/src/syscall/zsysnum_openbsd_arm.go b/src/syscall/zsysnum_openbsd_arm.go index cc33773a058339..05aed70762df58 100644 --- a/src/syscall/zsysnum_openbsd_arm.go +++ b/src/syscall/zsysnum_openbsd_arm.go @@ -2,7 +2,6 @@ // Code generated by the command above; DO NOT EDIT. //go:build arm && openbsd -// +build arm,openbsd package syscall diff --git a/src/syscall/zsysnum_solaris_amd64.go b/src/syscall/zsysnum_solaris_amd64.go index 23c9c715b5215c..ea244e535ec42a 100644 --- a/src/syscall/zsysnum_solaris_amd64.go +++ b/src/syscall/zsysnum_solaris_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package syscall diff --git a/src/syscall/ztypes_darwin_amd64.go b/src/syscall/ztypes_darwin_amd64.go index 8feacc47ab5142..551edc702578d1 100644 --- a/src/syscall/ztypes_darwin_amd64.go +++ b/src/syscall/ztypes_darwin_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_darwin.go //go:build amd64 && darwin -// +build amd64,darwin package syscall diff --git a/src/syscall/ztypes_darwin_arm64.go b/src/syscall/ztypes_darwin_arm64.go index 8079d22429b450..46f78a97eb2950 100644 --- a/src/syscall/ztypes_darwin_arm64.go +++ b/src/syscall/ztypes_darwin_arm64.go @@ -2,7 +2,6 @@ // cgo -godefs types_darwin.go //go:build arm64 && darwin -// +build arm64,darwin package syscall diff --git a/src/syscall/ztypes_dragonfly_amd64.go b/src/syscall/ztypes_dragonfly_amd64.go index a51e0038bb9bda..ec519b72ec1584 100644 --- a/src/syscall/ztypes_dragonfly_amd64.go +++ b/src/syscall/ztypes_dragonfly_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_dragonfly.go //go:build amd64 && dragonfly -// +build amd64,dragonfly package syscall diff --git a/src/syscall/ztypes_freebsd_386.go b/src/syscall/ztypes_freebsd_386.go index 173972688387bf..e1946dece8d3f2 100644 --- a/src/syscall/ztypes_freebsd_386.go +++ b/src/syscall/ztypes_freebsd_386.go @@ -2,7 +2,6 @@ // cgo -godefs types_freebsd.go | go run mkpost.go //go:build 386 && freebsd -// +build 386,freebsd package syscall diff --git a/src/syscall/ztypes_freebsd_amd64.go b/src/syscall/ztypes_freebsd_amd64.go index 0457d8e9959187..a718345d799b94 100644 --- a/src/syscall/ztypes_freebsd_amd64.go +++ b/src/syscall/ztypes_freebsd_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_freebsd.go | go run mkpost.go //go:build amd64 && freebsd -// +build amd64,freebsd package syscall diff --git a/src/syscall/ztypes_freebsd_arm.go b/src/syscall/ztypes_freebsd_arm.go index 29c8380d898784..9c5a0667132a99 100644 --- a/src/syscall/ztypes_freebsd_arm.go +++ b/src/syscall/ztypes_freebsd_arm.go @@ -2,7 +2,6 @@ // cgo -godefs -- -fsigned-char types_freebsd.go //go:build arm && freebsd -// +build arm,freebsd package syscall diff --git a/src/syscall/ztypes_freebsd_arm64.go b/src/syscall/ztypes_freebsd_arm64.go index 6472db00809a51..3ccd9fc0dd7962 100644 --- a/src/syscall/ztypes_freebsd_arm64.go +++ b/src/syscall/ztypes_freebsd_arm64.go @@ -2,7 +2,6 @@ // cgo -godefs types_freebsd.go | go run mkpost.go //go:build arm64 && freebsd -// +build arm64,freebsd package syscall diff --git a/src/syscall/ztypes_linux_386.go b/src/syscall/ztypes_linux_386.go index 251a0c0b4ad249..a45511e8476085 100644 --- a/src/syscall/ztypes_linux_386.go +++ b/src/syscall/ztypes_linux_386.go @@ -2,7 +2,6 @@ // cgo -godefs types_linux.go //go:build 386 && linux -// +build 386,linux package syscall diff --git a/src/syscall/ztypes_linux_amd64.go b/src/syscall/ztypes_linux_amd64.go index 34c953fc8bf45a..1bab13bf431ece 100644 --- a/src/syscall/ztypes_linux_amd64.go +++ b/src/syscall/ztypes_linux_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_linux.go //go:build amd64 && linux -// +build amd64,linux package syscall diff --git a/src/syscall/ztypes_linux_arm.go b/src/syscall/ztypes_linux_arm.go index 4de656b491e74a..a4d61bd19bbe88 100644 --- a/src/syscall/ztypes_linux_arm.go +++ b/src/syscall/ztypes_linux_arm.go @@ -2,7 +2,6 @@ // cgo -godefs types_linux.go //go:build arm && linux -// +build arm,linux package syscall diff --git a/src/syscall/ztypes_linux_arm64.go b/src/syscall/ztypes_linux_arm64.go index bed9cb08515eb0..1e469c36d2eddb 100644 --- a/src/syscall/ztypes_linux_arm64.go +++ b/src/syscall/ztypes_linux_arm64.go @@ -2,7 +2,6 @@ // cgo -godefs -- -fsigned-char types_linux.go //go:build arm64 && linux -// +build arm64,linux package syscall diff --git a/src/syscall/ztypes_linux_loong64.go b/src/syscall/ztypes_linux_loong64.go new file mode 100644 index 00000000000000..b0e068d74b2599 --- /dev/null +++ b/src/syscall/ztypes_linux_loong64.go @@ -0,0 +1,635 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs types_linux.go | go run mkpost.go + +package syscall + +const ( + sizeofPtr = 0x8 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x8 + sizeofLongLong = 0x8 + PathMax = 0x1000 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Timex struct { + Modes uint32 + Offset int64 + Freq int64 + Maxerror int64 + Esterror int64 + Status int32 + Constant int64 + Precision int64 + Tolerance int64 + Time Timeval + Tick int64 + Ppsfreq int64 + Jitter int64 + Shift int32 + Stabil int64 + Jitcnt int64 + Calcnt int64 + Errcnt int64 + Stbcnt int64 + Tai int32 + Pad_cgo_0 [44]byte +} + +type Time_t int64 + +type Tms struct { + Utime int64 + Stime int64 + Cutime int64 + Cstime int64 +} + +type Utimbuf struct { + Actime int64 + Modtime int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type _Gid_t uint32 + +type Stat_t struct { + Dev uint64 + Ino uint64 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint64 + X__pad1 uint64 + Size int64 + Blksize int32 + X__pad2 int32 + Blocks int64 + Atim Timespec + Mtim Timespec + Ctim Timespec + X__glibc_reserved [2]int32 +} + +type statxTimestamp struct { + Sec int64 + Nsec uint32 + X__reserved int32 +} + +type statx_t struct { + Mask uint32 + Blksize uint32 + Attributes uint64 + Nlink uint32 + Uid uint32 + Gid uint32 + Mode uint16 + X__spare0 [1]uint16 + Ino uint64 + Size uint64 + Blocks uint64 + Attributes_mask uint64 + Atime statxTimestamp + Btime statxTimestamp + Ctime statxTimestamp + Mtime statxTimestamp + Rdev_major uint32 + Rdev_minor uint32 + Dev_major uint32 + Dev_minor uint32 + Mnt_id uint64 + X__spare2 uint64 + X__spare3 [12]uint64 +} + +type Statfs_t struct { + Type int64 + Bsize int64 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid Fsid + Namelen int64 + Frsize int64 + Flags int64 + Spare [4]int64 +} + +type Dirent struct { + Ino uint64 + Off int64 + Reclen uint16 + Type uint8 + Name [256]int8 + Pad_cgo_0 [5]byte +} + +type Fsid struct { + X__val [2]int32 +} + +type Flock_t struct { + Type int16 + Whence int16 + Start int64 + Len int64 + Pid int32 + Pad_cgo_0 [4]byte +} + +type RawSockaddrInet4 struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]uint8 +} + +type RawSockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type RawSockaddrUnix struct { + Family uint16 + Path [108]int8 +} + +type RawSockaddrLinklayer struct { + Family uint16 + Protocol uint16 + Ifindex int32 + Hatype uint16 + Pkttype uint8 + Halen uint8 + Addr [8]uint8 +} + +type RawSockaddrNetlink struct { + Family uint16 + Pad uint16 + Pid uint32 + Groups uint32 +} + +type RawSockaddr struct { + Family uint16 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [96]int8 +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32 + Linger int32 +} + +type Iovec struct { + Base *byte + Len uint64 +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_0 [4]byte +} + +type Cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type Inet4Pktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type Inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type IPv6MTUInfo struct { + Addr RawSockaddrInet6 + Mtu uint32 +} + +type ICMPv6Filter struct { + Data [8]uint32 +} + +type Ucred struct { + Pid int32 + Uid uint32 + Gid uint32 +} + +type TCPInfo struct { + State uint8 + Ca_state uint8 + Retransmits uint8 + Probes uint8 + Backoff uint8 + Options uint8 + Rto uint32 + Ato uint32 + Snd_mss uint32 + Rcv_mss uint32 + Unacked uint32 + Sacked uint32 + Lost uint32 + Retrans uint32 + Fackets uint32 + Last_data_sent uint32 + Last_ack_sent uint32 + Last_data_recv uint32 + Last_ack_recv uint32 + Pmtu uint32 + Rcv_ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Advmss uint32 + Reordering uint32 + Rcv_rtt uint32 + Rcv_space uint32 + Total_retrans uint32 +} + +const ( + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x70 + SizeofSockaddrUnix = 0x6e + SizeofSockaddrLinklayer = 0x14 + SizeofSockaddrNetlink = 0xc + SizeofLinger = 0x8 + SizeofIPMreq = 0x8 + SizeofIPMreqn = 0xc + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x38 + SizeofCmsghdr = 0x10 + SizeofInet4Pktinfo = 0xc + SizeofInet6Pktinfo = 0x14 + SizeofIPv6MTUInfo = 0x20 + SizeofICMPv6Filter = 0x20 + SizeofUcred = 0xc + SizeofTCPInfo = 0x68 +) + +const ( + IFA_UNSPEC = 0x0 + IFA_ADDRESS = 0x1 + IFA_LOCAL = 0x2 + IFA_LABEL = 0x3 + IFA_BROADCAST = 0x4 + IFA_ANYCAST = 0x5 + IFA_CACHEINFO = 0x6 + IFA_MULTICAST = 0x7 + IFLA_UNSPEC = 0x0 + IFLA_ADDRESS = 0x1 + IFLA_BROADCAST = 0x2 + IFLA_IFNAME = 0x3 + IFLA_MTU = 0x4 + IFLA_LINK = 0x5 + IFLA_QDISC = 0x6 + IFLA_STATS = 0x7 + IFLA_COST = 0x8 + IFLA_PRIORITY = 0x9 + IFLA_MASTER = 0xa + IFLA_WIRELESS = 0xb + IFLA_PROTINFO = 0xc + IFLA_TXQLEN = 0xd + IFLA_MAP = 0xe + IFLA_WEIGHT = 0xf + IFLA_OPERSTATE = 0x10 + IFLA_LINKMODE = 0x11 + IFLA_LINKINFO = 0x12 + IFLA_NET_NS_PID = 0x13 + IFLA_IFALIAS = 0x14 + IFLA_MAX = 0x3a + RT_SCOPE_UNIVERSE = 0x0 + RT_SCOPE_SITE = 0xc8 + RT_SCOPE_LINK = 0xfd + RT_SCOPE_HOST = 0xfe + RT_SCOPE_NOWHERE = 0xff + RT_TABLE_UNSPEC = 0x0 + RT_TABLE_COMPAT = 0xfc + RT_TABLE_DEFAULT = 0xfd + RT_TABLE_MAIN = 0xfe + RT_TABLE_LOCAL = 0xff + RT_TABLE_MAX = 0xffffffff + RTA_UNSPEC = 0x0 + RTA_DST = 0x1 + RTA_SRC = 0x2 + RTA_IIF = 0x3 + RTA_OIF = 0x4 + RTA_GATEWAY = 0x5 + RTA_PRIORITY = 0x6 + RTA_PREFSRC = 0x7 + RTA_METRICS = 0x8 + RTA_MULTIPATH = 0x9 + RTA_FLOW = 0xb + RTA_CACHEINFO = 0xc + RTA_TABLE = 0xf + RTN_UNSPEC = 0x0 + RTN_UNICAST = 0x1 + RTN_LOCAL = 0x2 + RTN_BROADCAST = 0x3 + RTN_ANYCAST = 0x4 + RTN_MULTICAST = 0x5 + RTN_BLACKHOLE = 0x6 + RTN_UNREACHABLE = 0x7 + RTN_PROHIBIT = 0x8 + RTN_THROW = 0x9 + RTN_NAT = 0xa + RTN_XRESOLVE = 0xb + RTNLGRP_NONE = 0x0 + RTNLGRP_LINK = 0x1 + RTNLGRP_NOTIFY = 0x2 + RTNLGRP_NEIGH = 0x3 + RTNLGRP_TC = 0x4 + RTNLGRP_IPV4_IFADDR = 0x5 + RTNLGRP_IPV4_MROUTE = 0x6 + RTNLGRP_IPV4_ROUTE = 0x7 + RTNLGRP_IPV4_RULE = 0x8 + RTNLGRP_IPV6_IFADDR = 0x9 + RTNLGRP_IPV6_MROUTE = 0xa + RTNLGRP_IPV6_ROUTE = 0xb + RTNLGRP_IPV6_IFINFO = 0xc + RTNLGRP_IPV6_PREFIX = 0x12 + RTNLGRP_IPV6_RULE = 0x13 + RTNLGRP_ND_USEROPT = 0x14 + SizeofNlMsghdr = 0x10 + SizeofNlMsgerr = 0x14 + SizeofRtGenmsg = 0x1 + SizeofNlAttr = 0x4 + SizeofRtAttr = 0x4 + SizeofIfInfomsg = 0x10 + SizeofIfAddrmsg = 0x8 + SizeofRtMsg = 0xc + SizeofRtNexthop = 0x8 +) + +type NlMsghdr struct { + Len uint32 + Type uint16 + Flags uint16 + Seq uint32 + Pid uint32 +} + +type NlMsgerr struct { + Error int32 + Msg NlMsghdr +} + +type RtGenmsg struct { + Family uint8 +} + +type NlAttr struct { + Len uint16 + Type uint16 +} + +type RtAttr struct { + Len uint16 + Type uint16 +} + +type IfInfomsg struct { + Family uint8 + X__ifi_pad uint8 + Type uint16 + Index int32 + Flags uint32 + Change uint32 +} + +type IfAddrmsg struct { + Family uint8 + Prefixlen uint8 + Flags uint8 + Scope uint8 + Index uint32 +} + +type RtMsg struct { + Family uint8 + Dst_len uint8 + Src_len uint8 + Tos uint8 + Table uint8 + Protocol uint8 + Scope uint8 + Type uint8 + Flags uint32 +} + +type RtNexthop struct { + Len uint16 + Flags uint8 + Hops uint8 + Ifindex int32 +} + +const ( + SizeofSockFilter = 0x8 + SizeofSockFprog = 0x10 +) + +type SockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type SockFprog struct { + Len uint16 + Filter *SockFilter +} + +type InotifyEvent struct { + Wd int32 + Mask uint32 + Cookie uint32 + Len uint32 +} + +const SizeofInotifyEvent = 0x10 + +type PtraceRegs struct { + Regs [32]uint64 + Orig_a0 uint64 + Era uint64 + Badv uint64 + Reserved [10]uint64 +} + +type ptracePsw struct { +} + +type ptraceFpregs struct { +} + +type ptracePer struct { +} + +type FdSet struct { + Bits [16]int64 +} + +type Sysinfo_t struct { + Uptime int64 + Loads [3]uint64 + Totalram uint64 + Freeram uint64 + Sharedram uint64 + Bufferram uint64 + Totalswap uint64 + Freeswap uint64 + Procs uint16 + Pad uint16 + Totalhigh uint64 + Freehigh uint64 + Unit uint32 + X_f [0]int8 + Pad_cgo_0 [4]byte +} + +type Utsname struct { + Sysname [65]int8 + Nodename [65]int8 + Release [65]int8 + Version [65]int8 + Machine [65]int8 + Domainname [65]int8 +} + +type Ustat_t struct { + Tfree int32 + Tinode uint64 + Fname [6]int8 + Fpack [6]int8 + Pad_cgo_0 [4]byte +} + +type EpollEvent struct { + Events uint32 + X_padFd int32 + Fd int32 + Pad int32 +} + +const ( + _AT_FDCWD = -0x64 + _AT_REMOVEDIR = 0x200 + _AT_SYMLINK_NOFOLLOW = 0x100 + _AT_EACCESS = 0x200 + _AT_EMPTY_PATH = 0x1000 + _AT_NO_AUTOMOUNT = 0x800 + _STATX_BASIC_STATS = 0x7ff +) + +type pollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Line uint8 + Cc [32]uint8 + Ispeed uint32 + Ospeed uint32 +} + +const ( + IUCLC = 0x200 + OLCUC = 0x2 + TCGETS = 0x5401 + TCSETS = 0x5402 + XCASE = 0x4 +) diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go index 355533fb27cb47..c830cee966bf86 100644 --- a/src/syscall/ztypes_linux_ppc64.go +++ b/src/syscall/ztypes_linux_ppc64.go @@ -2,7 +2,6 @@ // cgo -godefs types_linux.go //go:build ppc64 && linux -// +build ppc64,linux package syscall diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go index 94e12c742c8033..770ddc9fe9a23b 100644 --- a/src/syscall/ztypes_linux_ppc64le.go +++ b/src/syscall/ztypes_linux_ppc64le.go @@ -2,7 +2,6 @@ // cgo -godefs types_linux.go //go:build ppc64le && linux -// +build ppc64le,linux package syscall diff --git a/src/syscall/ztypes_netbsd_386.go b/src/syscall/ztypes_netbsd_386.go index 321460f45aa386..74eaa4a15034b0 100644 --- a/src/syscall/ztypes_netbsd_386.go +++ b/src/syscall/ztypes_netbsd_386.go @@ -2,7 +2,6 @@ // cgo -godefs types_netbsd.go //go:build 386 && netbsd -// +build 386,netbsd package syscall diff --git a/src/syscall/ztypes_netbsd_amd64.go b/src/syscall/ztypes_netbsd_amd64.go index 370d7172634361..fc28fc9bb8f77f 100644 --- a/src/syscall/ztypes_netbsd_amd64.go +++ b/src/syscall/ztypes_netbsd_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_netbsd.go //go:build amd64 && netbsd -// +build amd64,netbsd package syscall diff --git a/src/syscall/ztypes_netbsd_arm.go b/src/syscall/ztypes_netbsd_arm.go index 557c6345339c08..1f885048dd65f7 100644 --- a/src/syscall/ztypes_netbsd_arm.go +++ b/src/syscall/ztypes_netbsd_arm.go @@ -2,7 +2,6 @@ // cgo -godefs types_netbsd.go //go:build arm && netbsd -// +build arm,netbsd package syscall diff --git a/src/syscall/ztypes_netbsd_arm64.go b/src/syscall/ztypes_netbsd_arm64.go index 19f36903412e76..cac74693d756a8 100644 --- a/src/syscall/ztypes_netbsd_arm64.go +++ b/src/syscall/ztypes_netbsd_arm64.go @@ -2,7 +2,6 @@ // cgo -godefs types_netbsd.go //go:build arm64 && netbsd -// +build arm64,netbsd package syscall diff --git a/src/syscall/ztypes_openbsd_386.go b/src/syscall/ztypes_openbsd_386.go index 222c6c7e46520b..f9ba685e25269d 100644 --- a/src/syscall/ztypes_openbsd_386.go +++ b/src/syscall/ztypes_openbsd_386.go @@ -2,7 +2,6 @@ // cgo -godefs types_openbsd.go //go:build 386 && openbsd -// +build 386,openbsd package syscall diff --git a/src/syscall/ztypes_openbsd_amd64.go b/src/syscall/ztypes_openbsd_amd64.go index 644ee9b3dfbe71..889b9551ae9024 100644 --- a/src/syscall/ztypes_openbsd_amd64.go +++ b/src/syscall/ztypes_openbsd_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_openbsd.go //go:build amd64 && openbsd -// +build amd64,openbsd package syscall diff --git a/src/syscall/ztypes_solaris_amd64.go b/src/syscall/ztypes_solaris_amd64.go index 64e16b49435073..d486cd002f967b 100644 --- a/src/syscall/ztypes_solaris_amd64.go +++ b/src/syscall/ztypes_solaris_amd64.go @@ -2,7 +2,6 @@ // cgo -godefs types_solaris.go //go:build amd64 && solaris -// +build amd64,solaris package syscall diff --git a/src/testing/allocs_test.go b/src/testing/allocs_test.go index 5b346aaf83873a..bbd3ae79c85311 100644 --- a/src/testing/allocs_test.go +++ b/src/testing/allocs_test.go @@ -6,7 +6,7 @@ package testing_test import "testing" -var global interface{} +var global any var allocsPerRunTests = []struct { name string diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index 15b4426c5a544c..2f7936611f7df6 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -32,35 +32,36 @@ var ( matchBenchmarks *string benchmarkMemory *bool - benchTime = benchTimeFlag{d: 1 * time.Second} // changed during test of testing package + benchTime = durationOrCountFlag{d: 1 * time.Second} // changed during test of testing package ) -type benchTimeFlag struct { - d time.Duration - n int +type durationOrCountFlag struct { + d time.Duration + n int + allowZero bool } -func (f *benchTimeFlag) String() string { +func (f *durationOrCountFlag) String() string { if f.n > 0 { return fmt.Sprintf("%dx", f.n) } - return time.Duration(f.d).String() + return f.d.String() } -func (f *benchTimeFlag) Set(s string) error { +func (f *durationOrCountFlag) Set(s string) error { if strings.HasSuffix(s, "x") { n, err := strconv.ParseInt(s[:len(s)-1], 10, 0) - if err != nil || n <= 0 { + if err != nil || n < 0 || (!f.allowZero && n == 0) { return fmt.Errorf("invalid count") } - *f = benchTimeFlag{n: int(n)} + *f = durationOrCountFlag{n: int(n)} return nil } d, err := time.ParseDuration(s) - if err != nil || d <= 0 { + if err != nil || d < 0 || (!f.allowZero && d == 0) { return fmt.Errorf("invalid duration") } - *f = benchTimeFlag{d: d} + *f = durationOrCountFlag{d: d} return nil } @@ -98,7 +99,7 @@ type B struct { previousN int // number of iterations in the previous run previousDuration time.Duration // total duration of the previous run benchFunc func(b *B) - benchTime benchTimeFlag + benchTime durationOrCountFlag bytes int64 missingBytes bool // one of the subbenchmarks does not have bytes set. timerOn bool @@ -241,7 +242,7 @@ func (b *B) run1() bool { b.mu.RLock() finished := b.finished b.mu.RUnlock() - if atomic.LoadInt32(&b.hasSub) != 0 || finished { + if b.hasSub.Load() || finished { tag := "BENCH" if b.skipped { tag = "SKIP" @@ -298,7 +299,12 @@ func (b *B) launch() { // Run the benchmark for at least the specified amount of time. if b.benchTime.n > 0 { - b.runN(b.benchTime.n) + // We already ran a single iteration in run1. + // If -benchtime=1x was requested, use that result. + // See https://golang.org/issue/32051. + if b.benchTime.n > 1 { + b.runN(b.benchTime.n) + } } else { d := b.benchTime.d for n := int64(1); !b.failed && b.duration < d && n < 1e9; { @@ -331,6 +337,17 @@ func (b *B) launch() { b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes, b.extra} } +// Elapsed returns the measured elapsed time of the benchmark. +// The duration reported by Elapsed matches the one measured by +// StartTimer, StopTimer, and ResetTimer. +func (b *B) Elapsed() time.Duration { + d := b.duration + if b.timerOn { + d += time.Since(b.start) + } + return d +} + // ReportMetric adds "n unit" to the reported benchmark results. // If the metric is per-iteration, the caller should divide by b.N, // and by convention units should end in "/op". @@ -609,6 +626,11 @@ func (ctx *benchContext) processBench(b *B) { } } +// If hideStdoutForTesting is true, Run does not print the benchName. +// This avoids a spurious print during 'go test' on package testing itself, +// which invokes b.Run in its own tests (see sub_test.go). +var hideStdoutForTesting = false + // Run benchmarks f as a subbenchmark with the given name. It reports // whether there were any failures. // @@ -617,7 +639,7 @@ func (ctx *benchContext) processBench(b *B) { func (b *B) Run(name string, f func(b *B)) bool { // Since b has subbenchmarks, we will no longer run it as a benchmark itself. // Release the lock and acquire it on exit to ensure locks stay paired. - atomic.StoreInt32(&b.hasSub, 1) + b.hasSub.Store(true) benchmarkLock.Unlock() defer benchmarkLock.Lock() @@ -649,7 +671,7 @@ func (b *B) Run(name string, f func(b *B)) bool { if partial { // Partial name match, like -bench=X/Y matching BenchmarkX. // Only process sub-benchmarks, if any. - atomic.StoreInt32(&sub.hasSub, 1) + sub.hasSub.Store(true) } if b.chatty != nil { @@ -664,7 +686,9 @@ func (b *B) Run(name string, f func(b *B)) bool { } }) - fmt.Println(benchName) + if !hideStdoutForTesting { + fmt.Println(benchName) + } } if sub.run1() { diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go index 3b1dc8275b5b83..7e456f9a409e57 100644 --- a/src/testing/benchmark_test.go +++ b/src/testing/benchmark_test.go @@ -176,5 +176,8 @@ func ExampleB_ReportMetric() { // This metric is per-operation, so divide by b.N and // report it as a "/op" unit. b.ReportMetric(float64(compares)/float64(b.N), "compares/op") + // This metric is per-time, so divide by b.Elapsed and + // report it as a "/ns" unit. + b.ReportMetric(float64(compares)/float64(b.Elapsed().Nanoseconds()), "compares/ns") }) } diff --git a/src/testing/example.go b/src/testing/example.go index 0217c5d2425a61..f33e8d2f926e0a 100644 --- a/src/testing/example.go +++ b/src/testing/example.go @@ -64,7 +64,7 @@ func sortLines(output string) string { // If recovered is non-nil, it'll panic with that value. // If the test panicked with nil, or invoked runtime.Goexit, it'll be // made to fail and panic with errNilPanicOrGoexit -func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, finished bool, recovered interface{}) (passed bool) { +func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, finished bool, recovered any) (passed bool) { passed = true dstr := fmtDuration(timeSpent) var fail string diff --git a/src/testing/fstest/mapfs.go b/src/testing/fstest/mapfs.go index 9fef2f4696ec79..4595b7313db9dd 100644 --- a/src/testing/fstest/mapfs.go +++ b/src/testing/fstest/mapfs.go @@ -37,7 +37,7 @@ type MapFile struct { Data []byte // file content Mode fs.FileMode // FileInfo.Mode ModTime time.Time // FileInfo.ModTime - Sys interface{} // FileInfo.Sys + Sys any // FileInfo.Sys } var _ fs.FS = MapFS(nil) @@ -66,7 +66,9 @@ func (fsys MapFS) Open(name string) (fs.File, error) { for fname, f := range fsys { i := strings.Index(fname, "/") if i < 0 { - list = append(list, mapFileInfo{fname, f}) + if fname != "." { + list = append(list, mapFileInfo{fname, f}) + } } else { need[fname[:i]] = true } @@ -154,7 +156,7 @@ func (i *mapFileInfo) Mode() fs.FileMode { return i.f.Mode } func (i *mapFileInfo) Type() fs.FileMode { return i.f.Mode.Type() } func (i *mapFileInfo) ModTime() time.Time { return i.f.ModTime } func (i *mapFileInfo) IsDir() bool { return i.f.Mode&fs.ModeDir != 0 } -func (i *mapFileInfo) Sys() interface{} { return i.f.Sys } +func (i *mapFileInfo) Sys() any { return i.f.Sys } func (i *mapFileInfo) Info() (fs.FileInfo, error) { return i, nil } // An openMapFile is a regular (non-directory) fs.File open for reading. diff --git a/src/testing/fstest/mapfs_test.go b/src/testing/fstest/mapfs_test.go index 2abedd67351fc4..c8d29283b28989 100644 --- a/src/testing/fstest/mapfs_test.go +++ b/src/testing/fstest/mapfs_test.go @@ -5,6 +5,9 @@ package fstest import ( + "fmt" + "io/fs" + "strings" "testing" ) @@ -17,3 +20,28 @@ func TestMapFS(t *testing.T) { t.Fatal(err) } } + +func TestMapFSChmodDot(t *testing.T) { + m := MapFS{ + "a/b.txt": &MapFile{Mode: 0666}, + ".": &MapFile{Mode: 0777 | fs.ModeDir}, + } + buf := new(strings.Builder) + fs.WalkDir(m, ".", func(path string, d fs.DirEntry, err error) error { + fi, err := d.Info() + if err != nil { + return err + } + fmt.Fprintf(buf, "%s: %v\n", path, fi.Mode()) + return nil + }) + want := ` +.: drwxrwxrwx +a: d--------- +a/b.txt: -rw-rw-rw- +`[1:] + got := buf.String() + if want != got { + t.Errorf("MapFS modes want:\n%s\ngot:\n%s\n", want, got) + } +} diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go index 5c4f30af168422..ddb608088233ec 100644 --- a/src/testing/fstest/testfs.go +++ b/src/testing/fstest/testfs.go @@ -33,7 +33,6 @@ import ( // if err := fstest.TestFS(myFS, "file/that/should/be/present"); err != nil { // t.Fatal(err) // } -// func TestFS(fsys fs.FS, expected ...string) error { if err := testFS(fsys, expected...); err != nil { return err @@ -105,7 +104,7 @@ type fsTester struct { } // errorf adds an error line to errText. -func (t *fsTester) errorf(format string, args ...interface{}) { +func (t *fsTester) errorf(format string, args ...any) { if len(t.errText) > 0 { t.errText = append(t.errText, '\n') } diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go new file mode 100644 index 00000000000000..87b60fc1bbe228 --- /dev/null +++ b/src/testing/fuzz.go @@ -0,0 +1,706 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testing + +import ( + "errors" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "time" +) + +func initFuzzFlags() { + matchFuzz = flag.String("test.fuzz", "", "run the fuzz test matching `regexp`") + flag.Var(&fuzzDuration, "test.fuzztime", "time to spend fuzzing; default is to run indefinitely") + flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a failing input") + + fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored (for use only by cmd/go)") + isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values (for use only by cmd/go)") +} + +var ( + matchFuzz *string + fuzzDuration durationOrCountFlag + minimizeDuration = durationOrCountFlag{d: 60 * time.Second, allowZero: true} + fuzzCacheDir *string + isFuzzWorker *bool + + // corpusDir is the parent directory of the fuzz test's seed corpus within + // the package. + corpusDir = "testdata/fuzz" +) + +// fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an +// internal error. This distinguishes internal errors from uncontrolled panics +// and other failiures. Keep in sync with internal/fuzz.workerExitCode. +const fuzzWorkerExitCode = 70 + +// InternalFuzzTarget is an internal type but exported because it is +// cross-package; it is part of the implementation of the "go test" command. +type InternalFuzzTarget struct { + Name string + Fn func(f *F) +} + +// F is a type passed to fuzz tests. +// +// Fuzz tests run generated inputs against a provided fuzz target, which can +// find and report potential bugs in the code being tested. +// +// A fuzz test runs the seed corpus by default, which includes entries provided +// by (*F).Add and entries in the testdata/fuzz/ directory. After +// any necessary setup and calls to (*F).Add, the fuzz test must then call +// (*F).Fuzz to provide the fuzz target. See the testing package documentation +// for an example, and see the F.Fuzz and F.Add method documentation for +// details. +// +// *F methods can only be called before (*F).Fuzz. Once the test is +// executing the fuzz target, only (*T) methods can be used. The only *F methods +// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name. +type F struct { + common + fuzzContext *fuzzContext + testContext *testContext + + // inFuzzFn is true when the fuzz function is running. Most F methods cannot + // be called when inFuzzFn is true. + inFuzzFn bool + + // corpus is a set of seed corpus entries, added with F.Add and loaded + // from testdata. + corpus []corpusEntry + + result fuzzResult + fuzzCalled bool +} + +var _ TB = (*F)(nil) + +// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry. +// We use a type alias because we don't want to export this type, and we can't +// import internal/fuzz from testing. +type corpusEntry = struct { + Parent string + Path string + Data []byte + Values []any + Generation int + IsSeed bool +} + +// Helper marks the calling function as a test helper function. +// When printing file and line information, that function will be skipped. +// Helper may be called simultaneously from multiple goroutines. +func (f *F) Helper() { + if f.inFuzzFn { + panic("testing: f.Helper was called inside the fuzz target, use t.Helper instead") + } + + // common.Helper is inlined here. + // If we called it, it would mark F.Helper as the helper + // instead of the caller. + f.mu.Lock() + defer f.mu.Unlock() + if f.helperPCs == nil { + f.helperPCs = make(map[uintptr]struct{}) + } + // repeating code from callerName here to save walking a stack frame + var pc [1]uintptr + n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper + if n == 0 { + panic("testing: zero callers found") + } + if _, found := f.helperPCs[pc[0]]; !found { + f.helperPCs[pc[0]] = struct{}{} + f.helperNames = nil // map will be recreated next time it is needed + } +} + +// Fail marks the function as having failed but continues execution. +func (f *F) Fail() { + // (*F).Fail may be called by (*T).Fail, which we should allow. However, we + // shouldn't allow direct (*F).Fail calls from inside the (*F).Fuzz function. + if f.inFuzzFn { + panic("testing: f.Fail was called inside the fuzz target, use t.Fail instead") + } + f.common.Helper() + f.common.Fail() +} + +// Skipped reports whether the test was skipped. +func (f *F) Skipped() bool { + // (*F).Skipped may be called by tRunner, which we should allow. However, we + // shouldn't allow direct (*F).Skipped calls from inside the (*F).Fuzz function. + if f.inFuzzFn { + panic("testing: f.Skipped was called inside the fuzz target, use t.Skipped instead") + } + f.common.Helper() + return f.common.Skipped() +} + +// Add will add the arguments to the seed corpus for the fuzz test. This will be +// a no-op if called after or within the fuzz target, and args must match the +// arguments for the fuzz target. +func (f *F) Add(args ...any) { + var values []any + for i := range args { + if t := reflect.TypeOf(args[i]); !supportedTypes[t] { + panic(fmt.Sprintf("testing: unsupported type to Add %v", t)) + } + values = append(values, args[i]) + } + f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))}) +} + +// supportedTypes represents all of the supported types which can be fuzzed. +var supportedTypes = map[reflect.Type]bool{ + reflect.TypeOf(([]byte)("")): true, + reflect.TypeOf((string)("")): true, + reflect.TypeOf((bool)(false)): true, + reflect.TypeOf((byte)(0)): true, + reflect.TypeOf((rune)(0)): true, + reflect.TypeOf((float32)(0)): true, + reflect.TypeOf((float64)(0)): true, + reflect.TypeOf((int)(0)): true, + reflect.TypeOf((int8)(0)): true, + reflect.TypeOf((int16)(0)): true, + reflect.TypeOf((int32)(0)): true, + reflect.TypeOf((int64)(0)): true, + reflect.TypeOf((uint)(0)): true, + reflect.TypeOf((uint8)(0)): true, + reflect.TypeOf((uint16)(0)): true, + reflect.TypeOf((uint32)(0)): true, + reflect.TypeOf((uint64)(0)): true, +} + +// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of +// arguments, those arguments will be added to the seed corpus. +// +// ff must be a function with no return value whose first argument is *T and +// whose remaining arguments are the types to be fuzzed. +// For example: +// +// f.Fuzz(func(t *testing.T, b []byte, i int) { ... }) +// +// The following types are allowed: []byte, string, bool, byte, rune, float32, +// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64. +// More types may be supported in the future. +// +// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use +// the corresponding *T method instead. The only *F methods that are allowed in +// the (*F).Fuzz function are (*F).Failed and (*F).Name. +// +// This function should be fast and deterministic, and its behavior should not +// depend on shared state. No mutatable input arguments, or pointers to them, +// should be retained between executions of the fuzz function, as the memory +// backing them may be mutated during a subsequent invocation. ff must not +// modify the underlying data of the arguments provided by the fuzzing engine. +// +// When fuzzing, F.Fuzz does not return until a problem is found, time runs out +// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz +// should be called exactly once, unless F.Skip or F.Fail is called beforehand. +func (f *F) Fuzz(ff any) { + if f.fuzzCalled { + panic("testing: F.Fuzz called more than once") + } + f.fuzzCalled = true + if f.failed { + return + } + f.Helper() + + // ff should be in the form func(*testing.T, ...interface{}) + fn := reflect.ValueOf(ff) + fnType := fn.Type() + if fnType.Kind() != reflect.Func { + panic("testing: F.Fuzz must receive a function") + } + if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) { + panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T") + } + if fnType.NumOut() != 0 { + panic("testing: fuzz target must not return a value") + } + + // Save the types of the function to compare against the corpus. + var types []reflect.Type + for i := 1; i < fnType.NumIn(); i++ { + t := fnType.In(i) + if !supportedTypes[t] { + panic(fmt.Sprintf("testing: unsupported type for fuzzing %v", t)) + } + types = append(types, t) + } + + // Load the testdata seed corpus. Check types of entries in the testdata + // corpus and entries declared with F.Add. + // + // Don't load the seed corpus if this is a worker process; we won't use it. + if f.fuzzContext.mode != fuzzWorker { + for _, c := range f.corpus { + if err := f.fuzzContext.deps.CheckCorpus(c.Values, types); err != nil { + // TODO(#48302): Report the source location of the F.Add call. + f.Fatal(err) + } + } + + // Load seed corpus + c, err := f.fuzzContext.deps.ReadCorpus(filepath.Join(corpusDir, f.name), types) + if err != nil { + f.Fatal(err) + } + for i := range c { + c[i].IsSeed = true // these are all seed corpus values + if f.fuzzContext.mode == fuzzCoordinator { + // If this is the coordinator process, zero the values, since we don't need + // to hold onto them. + c[i].Values = nil + } + } + + f.corpus = append(f.corpus, c...) + } + + // run calls fn on a given input, as a subtest with its own T. + // run is analogous to T.Run. The test filtering and cleanup works similarly. + // fn is called in its own goroutine. + run := func(captureOut io.Writer, e corpusEntry) (ok bool) { + if e.Values == nil { + // The corpusEntry must have non-nil Values in order to run the + // test. If Values is nil, it is a bug in our code. + panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Path)) + } + if shouldFailFast() { + return true + } + testName := f.name + if e.Path != "" { + testName = fmt.Sprintf("%s/%s", testName, filepath.Base(e.Path)) + } + if f.testContext.isFuzzing { + // Don't preserve subtest names while fuzzing. If fn calls T.Run, + // there will be a very large number of subtests with duplicate names, + // which will use a large amount of memory. The subtest names aren't + // useful since there's no way to re-run them deterministically. + f.testContext.match.clearSubNames() + } + + // Record the stack trace at the point of this call so that if the subtest + // function - which runs in a separate stack - is marked as a helper, we can + // continue walking the stack into the parent test. + var pc [maxStackLen]uintptr + n := runtime.Callers(2, pc[:]) + t := &T{ + common: common{ + barrier: make(chan bool), + signal: make(chan bool), + name: testName, + parent: &f.common, + level: f.level + 1, + creator: pc[:n], + chatty: f.chatty, + }, + context: f.testContext, + } + if captureOut != nil { + // t.parent aliases f.common. + t.parent.w = captureOut + } + t.w = indenter{&t.common} + if t.chatty != nil { + // TODO(#48132): adjust this to work with test2json. + t.chatty.Updatef(t.name, "=== RUN %s\n", t.name) + } + f.common.inFuzzFn, f.inFuzzFn = true, true + go tRunner(t, func(t *T) { + args := []reflect.Value{reflect.ValueOf(t)} + for _, v := range e.Values { + args = append(args, reflect.ValueOf(v)) + } + // Before resetting the current coverage, defer the snapshot so that + // we make sure it is called right before the tRunner function + // exits, regardless of whether it was executed cleanly, panicked, + // or if the fuzzFn called t.Fatal. + if f.testContext.isFuzzing { + defer f.fuzzContext.deps.SnapshotCoverage() + f.fuzzContext.deps.ResetCoverage() + } + fn.Call(args) + }) + <-t.signal + f.common.inFuzzFn, f.inFuzzFn = false, false + return !t.Failed() + } + + switch f.fuzzContext.mode { + case fuzzCoordinator: + // Fuzzing is enabled, and this is the test process started by 'go test'. + // Act as the coordinator process, and coordinate workers to perform the + // actual fuzzing. + corpusTargetDir := filepath.Join(corpusDir, f.name) + cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name) + err := f.fuzzContext.deps.CoordinateFuzzing( + fuzzDuration.d, + int64(fuzzDuration.n), + minimizeDuration.d, + int64(minimizeDuration.n), + *parallel, + f.corpus, + types, + corpusTargetDir, + cacheTargetDir) + if err != nil { + f.result = fuzzResult{Error: err} + f.Fail() + fmt.Fprintf(f.w, "%v\n", err) + if crashErr, ok := err.(fuzzCrashError); ok { + crashPath := crashErr.CrashPath() + fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath) + testName := filepath.Base(crashPath) + fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName) + } + } + // TODO(jayconrod,katiehockman): Aggregate statistics across workers + // and add to FuzzResult (ie. time taken, num iterations) + + case fuzzWorker: + // Fuzzing is enabled, and this is a worker process. Follow instructions + // from the coordinator. + if err := f.fuzzContext.deps.RunFuzzWorker(func(e corpusEntry) error { + // Don't write to f.w (which points to Stdout) if running from a + // fuzz worker. This would become very verbose, particularly during + // minimization. Return the error instead, and let the caller deal + // with the output. + var buf strings.Builder + if ok := run(&buf, e); !ok { + return errors.New(buf.String()) + } + return nil + }); err != nil { + // Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz. + // The worker will exit with fuzzWorkerExitCode, indicating this is a failure + // (and 'go test' should exit non-zero) but a failing input should not be recorded. + f.Errorf("communicating with fuzzing coordinator: %v", err) + } + + default: + // Fuzzing is not enabled, or will be done later. Only run the seed + // corpus now. + for _, e := range f.corpus { + name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path)) + if _, ok, _ := f.testContext.match.fullName(nil, name); ok { + run(f.w, e) + } + } + } +} + +func (f *F) report() { + if *isFuzzWorker || f.parent == nil { + return + } + dstr := fmtDuration(f.duration) + format := "--- %s: %s (%s)\n" + if f.Failed() { + f.flushToParent(f.name, format, "FAIL", f.name, dstr) + } else if f.chatty != nil { + if f.Skipped() { + f.flushToParent(f.name, format, "SKIP", f.name, dstr) + } else { + f.flushToParent(f.name, format, "PASS", f.name, dstr) + } + } +} + +// fuzzResult contains the results of a fuzz run. +type fuzzResult struct { + N int // The number of iterations. + T time.Duration // The total time taken. + Error error // Error is the error from the failing input +} + +func (r fuzzResult) String() string { + if r.Error == nil { + return "" + } + return r.Error.Error() +} + +// fuzzCrashError is satisfied by a failing input detected while fuzzing. +// These errors are written to the seed corpus and can be re-run with 'go test'. +// Errors within the fuzzing framework (like I/O errors between coordinator +// and worker processes) don't satisfy this interface. +type fuzzCrashError interface { + error + Unwrap() error + + // CrashPath returns the path of the subtest that corresponds to the saved + // crash input file in the seed corpus. The test can be re-run with go test + // -run=$test/$name $test is the fuzz test name, and $name is the + // filepath.Base of the string returned here. + CrashPath() string +} + +// fuzzContext holds fields common to all fuzz tests. +type fuzzContext struct { + deps testDeps + mode fuzzMode +} + +type fuzzMode uint8 + +const ( + seedCorpusOnly fuzzMode = iota + fuzzCoordinator + fuzzWorker +) + +// runFuzzTests runs the fuzz tests matching the pattern for -run. This will +// only run the (*F).Fuzz function for each seed corpus without using the +// fuzzing engine to generate or mutate inputs. +func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) { + ok = true + if len(fuzzTests) == 0 || *isFuzzWorker { + return ran, ok + } + m := newMatcher(deps.MatchString, *match, "-test.run") + tctx := newTestContext(*parallel, m) + tctx.deadline = deadline + var mFuzz *matcher + if *matchFuzz != "" { + mFuzz = newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz") + } + fctx := &fuzzContext{deps: deps, mode: seedCorpusOnly} + root := common{w: os.Stdout} // gather output in one place + if Verbose() { + root.chatty = newChattyPrinter(root.w) + } + for _, ft := range fuzzTests { + if shouldFailFast() { + break + } + testName, matched, _ := tctx.match.fullName(nil, ft.Name) + if !matched { + continue + } + if mFuzz != nil { + if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched { + // If this will be fuzzed, then don't run the seed corpus + // right now. That will happen later. + continue + } + } + f := &F{ + common: common{ + signal: make(chan bool), + barrier: make(chan bool), + name: testName, + parent: &root, + level: root.level + 1, + chatty: root.chatty, + }, + testContext: tctx, + fuzzContext: fctx, + } + f.w = indenter{&f.common} + if f.chatty != nil { + // TODO(#48132): adjust this to work with test2json. + f.chatty.Updatef(f.name, "=== RUN %s\n", f.name) + } + + go fRunner(f, ft.Fn) + <-f.signal + } + return root.ran, !root.Failed() +} + +// runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such +// fuzz test must match. This will run the fuzzing engine to generate and +// mutate new inputs against the fuzz target. +// +// If fuzzing is disabled (-test.fuzz is not set), runFuzzing +// returns immediately. +func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) { + if len(fuzzTests) == 0 || *matchFuzz == "" { + return true + } + m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz") + tctx := newTestContext(1, m) + tctx.isFuzzing = true + fctx := &fuzzContext{ + deps: deps, + } + root := common{w: os.Stdout} + if *isFuzzWorker { + root.w = io.Discard + fctx.mode = fuzzWorker + } else { + fctx.mode = fuzzCoordinator + } + if Verbose() && !*isFuzzWorker { + root.chatty = newChattyPrinter(root.w) + } + var fuzzTest *InternalFuzzTarget + var testName string + var matched []string + for i := range fuzzTests { + name, ok, _ := tctx.match.fullName(nil, fuzzTests[i].Name) + if !ok { + continue + } + matched = append(matched, name) + fuzzTest = &fuzzTests[i] + testName = name + } + if len(matched) == 0 { + fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz") + return true + } + if len(matched) > 1 { + fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched) + return false + } + + f := &F{ + common: common{ + signal: make(chan bool), + barrier: nil, // T.Parallel has no effect when fuzzing. + name: testName, + parent: &root, + level: root.level + 1, + chatty: root.chatty, + }, + fuzzContext: fctx, + testContext: tctx, + } + f.w = indenter{&f.common} + if f.chatty != nil { + // TODO(#48132): adjust this to work with test2json. + f.chatty.Updatef(f.name, "=== FUZZ %s\n", f.name) + } + go fRunner(f, fuzzTest.Fn) + <-f.signal + return !f.failed +} + +// fRunner wraps a call to a fuzz test and ensures that cleanup functions are +// called and status flags are set. fRunner should be called in its own +// goroutine. To wait for its completion, receive from f.signal. +// +// fRunner is analogous to tRunner, which wraps subtests started with T.Run. +// Unit tests and fuzz tests work a little differently, so for now, these +// functions aren't consolidated. In particular, because there are no F.Run and +// F.Parallel methods, i.e., no fuzz sub-tests or parallel fuzz tests, a few +// simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is +// called. +func fRunner(f *F, fn func(*F)) { + // When this goroutine is done, either because runtime.Goexit was called, a + // panic started, or fn returned normally, record the duration and send + // t.signal, indicating the fuzz test is done. + defer func() { + // Detect whether the fuzz test panicked or called runtime.Goexit + // without calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly + // replacing a nil panic value). Nothing should recover after fRunner + // unwinds, so this should crash the process and print stack. + // Unfortunately, recovering here adds stack frames, but the location of + // the original panic should still be + // clear. + if f.Failed() { + numFailed.Add(1) + } + err := recover() + if err == nil { + f.mu.RLock() + fuzzNotCalled := !f.fuzzCalled && !f.skipped && !f.failed + if !f.finished && !f.skipped && !f.failed { + err = errNilPanicOrGoexit + } + f.mu.RUnlock() + if fuzzNotCalled && err == nil { + f.Error("returned without calling F.Fuzz, F.Fail, or F.Skip") + } + } + + // Use a deferred call to ensure that we report that the test is + // complete even if a cleanup function calls F.FailNow. See issue 41355. + didPanic := false + defer func() { + if !didPanic { + // Only report that the test is complete if it doesn't panic, + // as otherwise the test binary can exit before the panic is + // reported to the user. See issue 41479. + f.signal <- true + } + }() + + // If we recovered a panic or inappropriate runtime.Goexit, fail the test, + // flush the output log up to the root, then panic. + doPanic := func(err any) { + f.Fail() + if r := f.runCleanup(recoverAndReturnPanic); r != nil { + f.Logf("cleanup panicked with %v", r) + } + for root := &f.common; root.parent != nil; root = root.parent { + root.mu.Lock() + root.duration += time.Since(root.start) + d := root.duration + root.mu.Unlock() + root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d)) + } + didPanic = true + panic(err) + } + if err != nil { + doPanic(err) + } + + // No panic or inappropriate Goexit. + f.duration += time.Since(f.start) + + if len(f.sub) > 0 { + // Unblock inputs that called T.Parallel while running the seed corpus. + // This only affects fuzz tests run as normal tests. + // While fuzzing, T.Parallel has no effect, so f.sub is empty, and this + // branch is not taken. f.barrier is nil in that case. + f.testContext.release() + close(f.barrier) + // Wait for the subtests to complete. + for _, sub := range f.sub { + <-sub.signal + } + cleanupStart := time.Now() + err := f.runCleanup(recoverAndReturnPanic) + f.duration += time.Since(cleanupStart) + if err != nil { + doPanic(err) + } + } + + // Report after all subtests have finished. + f.report() + f.done = true + f.setRan() + }() + defer func() { + if len(f.sub) == 0 { + f.runCleanup(normalPanic) + } + }() + + f.start = time.Now() + fn(f) + + // Code beyond this point will not be executed when FailNow or SkipNow + // is invoked. + f.mu.Lock() + f.finished = true + f.mu.Unlock() +} diff --git a/src/testing/helper_test.go b/src/testing/helper_test.go index b27fd62ee8f718..fa1d2b608252b4 100644 --- a/src/testing/helper_test.go +++ b/src/testing/helper_test.go @@ -5,13 +5,12 @@ package testing import ( - "bytes" "regexp" "strings" ) func TestTBHelper(t *T) { - var buf bytes.Buffer + var buf strings.Builder ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "")) t1 := &T{ common: common{ @@ -24,17 +23,22 @@ func TestTBHelper(t *T) { want := `--- FAIL: Test (?s) helperfuncs_test.go:12: 0 -helperfuncs_test.go:33: 1 +helperfuncs_test.go:40: 1 helperfuncs_test.go:21: 2 -helperfuncs_test.go:35: 3 -helperfuncs_test.go:42: 4 +helperfuncs_test.go:42: 3 +helperfuncs_test.go:49: 4 --- FAIL: Test/sub (?s) -helperfuncs_test.go:45: 5 +helperfuncs_test.go:52: 5 helperfuncs_test.go:21: 6 -helperfuncs_test.go:44: 7 -helperfuncs_test.go:56: 8 -helperfuncs_test.go:64: 9 -helperfuncs_test.go:60: 10 +helperfuncs_test.go:51: 7 +helperfuncs_test.go:63: 8 +--- FAIL: Test/sub2 (?s) +helperfuncs_test.go:78: 11 +helperfuncs_test.go:82: recover 12 +helperfuncs_test.go:84: GenericFloat64 +helperfuncs_test.go:85: GenericInt +helperfuncs_test.go:71: 9 +helperfuncs_test.go:67: 10 ` lines := strings.Split(buf.String(), "\n") durationRE := regexp.MustCompile(`\(.*\)$`) @@ -50,7 +54,7 @@ helperfuncs_test.go:60: 10 } func TestTBHelperParallel(t *T) { - var buf bytes.Buffer + var buf strings.Builder ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "")) t1 := &T{ common: common{ @@ -71,38 +75,6 @@ func TestTBHelperParallel(t *T) { } } -func TestTBHelperLineNumer(t *T) { - var buf bytes.Buffer - ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "")) - t1 := &T{ - common: common{ - signal: make(chan bool), - w: &buf, - }, - context: ctx, - } - t1.Run("Test", func(t *T) { - helperA := func(t *T) { - t.Helper() - t.Run("subtest", func(t *T) { - t.Helper() - t.Fatal("fatal error message") - }) - } - helperA(t) - }) - - want := "helper_test.go:92: fatal error message" - got := "" - lines := strings.Split(strings.TrimSpace(buf.String()), "\n") - if len(lines) > 0 { - got = strings.TrimSpace(lines[len(lines)-1]) - } - if got != want { - t.Errorf("got output:\n\n%v\nwant:\n\n%v", got, want) - } -} - type noopWriter int func (nw *noopWriter) Write(b []byte) (int, error) { return len(b), nil } diff --git a/src/testing/helperfuncs_test.go b/src/testing/helperfuncs_test.go index df0476ed7323de..b63bc91ac20f07 100644 --- a/src/testing/helperfuncs_test.go +++ b/src/testing/helperfuncs_test.go @@ -26,6 +26,13 @@ func helperCallingHelper(t *T, msg string) { helper(t, msg) } +func genericHelper[G any](t *T, msg string) { + t.Helper() + t.Error(msg) +} + +var genericIntHelper = genericHelper[int] + func testHelper(t *T) { // Check combinations of directly and indirectly // calling helper functions. @@ -65,6 +72,17 @@ func testHelper(t *T) { t.Helper() t.Error("9") }) + + // Check that helper-ness propagates up through subtests + // to helpers above. See https://golang.org/issue/44887. + helperSubCallingHelper(t, "11") + + // Check that helper-ness propagates up through panic/recover. + // See https://golang.org/issue/31154. + recoverHelper(t, "12") + + genericHelper[float64](t, "GenericFloat64") + genericIntHelper(t, "GenericInt") } func parallelTestHelper(t *T) { @@ -78,3 +96,27 @@ func parallelTestHelper(t *T) { } wg.Wait() } + +func helperSubCallingHelper(t *T, msg string) { + t.Helper() + t.Run("sub2", func(t *T) { + t.Helper() + t.Fatal(msg) + }) +} + +func recoverHelper(t *T, msg string) { + t.Helper() + defer func() { + t.Helper() + if err := recover(); err != nil { + t.Errorf("recover %s", err) + } + }() + doPanic(t, msg) +} + +func doPanic(t *T, msg string) { + t.Helper() + panic(msg) +} diff --git a/src/testing/internal/testdeps/deps.go b/src/testing/internal/testdeps/deps.go index 3608d332946e2e..2e85a41b07751c 100644 --- a/src/testing/internal/testdeps/deps.go +++ b/src/testing/internal/testdeps/deps.go @@ -12,12 +12,18 @@ package testdeps import ( "bufio" + "context" + "internal/fuzz" "internal/testlog" "io" + "os" + "os/signal" + "reflect" "regexp" "runtime/pprof" "strings" "sync" + "time" ) // TestDeps is an implementation of the testing.testDeps interface, @@ -126,3 +132,68 @@ func (TestDeps) StopTestLog() error { func (TestDeps) SetPanicOnExit0(v bool) { testlog.SetPanicOnExit0(v) } + +func (TestDeps) CoordinateFuzzing( + timeout time.Duration, + limit int64, + minimizeTimeout time.Duration, + minimizeLimit int64, + parallel int, + seed []fuzz.CorpusEntry, + types []reflect.Type, + corpusDir, + cacheDir string) (err error) { + // Fuzzing may be interrupted with a timeout or if the user presses ^C. + // In either case, we'll stop worker processes gracefully and save + // crashers and interesting values. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + err = fuzz.CoordinateFuzzing(ctx, fuzz.CoordinateFuzzingOpts{ + Log: os.Stderr, + Timeout: timeout, + Limit: limit, + MinimizeTimeout: minimizeTimeout, + MinimizeLimit: minimizeLimit, + Parallel: parallel, + Seed: seed, + Types: types, + CorpusDir: corpusDir, + CacheDir: cacheDir, + }) + if err == ctx.Err() { + return nil + } + return err +} + +func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error { + // Worker processes may or may not receive a signal when the user presses ^C + // On POSIX operating systems, a signal sent to a process group is delivered + // to all processes in that group. This is not the case on Windows. + // If the worker is interrupted, return quickly and without error. + // If only the coordinator process is interrupted, it tells each worker + // process to stop by closing its "fuzz_in" pipe. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + err := fuzz.RunFuzzWorker(ctx, fn) + if err == ctx.Err() { + return nil + } + return err +} + +func (TestDeps) ReadCorpus(dir string, types []reflect.Type) ([]fuzz.CorpusEntry, error) { + return fuzz.ReadCorpus(dir, types) +} + +func (TestDeps) CheckCorpus(vals []any, types []reflect.Type) error { + return fuzz.CheckCorpus(vals, types) +} + +func (TestDeps) ResetCoverage() { + fuzz.ResetCoverage() +} + +func (TestDeps) SnapshotCoverage() { + fuzz.SnapshotCoverage() +} diff --git a/src/testing/iotest/logger_test.go b/src/testing/iotest/logger_test.go index fec4467cc67d19..7a7d0aa9f0815e 100644 --- a/src/testing/iotest/logger_test.go +++ b/src/testing/iotest/logger_test.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "log" + "strings" "testing" ) @@ -32,12 +33,12 @@ func TestWriteLogger(t *testing.T) { log.SetOutput(olw) }() - lOut := new(bytes.Buffer) + lOut := new(strings.Builder) log.SetPrefix("lw: ") log.SetOutput(lOut) log.SetFlags(0) - lw := new(bytes.Buffer) + lw := new(strings.Builder) wl := NewWriteLogger("write:", lw) if _, err := wl.Write([]byte("Hello, World!")); err != nil { t.Fatalf("Unexpectedly failed to write: %v", err) @@ -64,7 +65,7 @@ func TestWriteLogger_errorOnWrite(t *testing.T) { log.SetOutput(olw) }() - lOut := new(bytes.Buffer) + lOut := new(strings.Builder) log.SetPrefix("lw: ") log.SetOutput(lOut) log.SetFlags(0) @@ -93,7 +94,7 @@ func TestReadLogger(t *testing.T) { log.SetOutput(olw) }() - lOut := new(bytes.Buffer) + lOut := new(strings.Builder) log.SetPrefix("lr: ") log.SetOutput(lOut) log.SetFlags(0) @@ -130,7 +131,7 @@ func TestReadLogger_errorOnRead(t *testing.T) { log.SetOutput(olw) }() - lOut := new(bytes.Buffer) + lOut := new(strings.Builder) log.SetPrefix("lr: ") log.SetOutput(lOut) log.SetFlags(0) diff --git a/src/testing/iotest/reader_test.go b/src/testing/iotest/reader_test.go index f149e74c74de37..1d222372caf384 100644 --- a/src/testing/iotest/reader_test.go +++ b/src/testing/iotest/reader_test.go @@ -26,7 +26,7 @@ func TestOneByteReader_nonEmptyReader(t *testing.T) { b = make([]byte, 3) // Read from obr until EOF. - got := new(bytes.Buffer) + got := new(strings.Builder) for i := 0; ; i++ { n, err = obr.Read(b) if err != nil { @@ -77,7 +77,7 @@ func TestHalfReader_nonEmptyReader(t *testing.T) { } // non empty read buffer b = make([]byte, 2) - got := new(bytes.Buffer) + got := new(strings.Builder) for i := 0; ; i++ { n, err = hr.Read(b) if err != nil { @@ -190,7 +190,7 @@ func TestDataErrReader_nonEmptyReader(t *testing.T) { der := DataErrReader(buf) b := make([]byte, 3) - got := new(bytes.Buffer) + got := new(strings.Builder) var n int var err error for { diff --git a/src/testing/iotest/writer_test.go b/src/testing/iotest/writer_test.go index 5aaa77cc74cfee..276251336949f1 100644 --- a/src/testing/iotest/writer_test.go +++ b/src/testing/iotest/writer_test.go @@ -5,7 +5,7 @@ package iotest import ( - "bytes" + "strings" "testing" ) @@ -23,7 +23,7 @@ var truncateWriterTests = []struct { func TestTruncateWriter(t *testing.T) { for _, tt := range truncateWriterTests { - buf := new(bytes.Buffer) + buf := new(strings.Builder) tw := TruncateWriter(buf, tt.trunc) n, err := tw.Write([]byte(tt.in)) if err != nil { diff --git a/src/testing/match.go b/src/testing/match.go index b18c6e7f389ac6..d530f70c2676c6 100644 --- a/src/testing/match.go +++ b/src/testing/match.go @@ -14,36 +14,52 @@ import ( // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks. type matcher struct { - filter []string + filter filterMatch matchFunc func(pat, str string) (bool, error) - mu sync.Mutex - subNames map[string]int64 + mu sync.Mutex + + // subNames is used to deduplicate subtest names. + // Each key is the subtest name joined to the deduplicated name of the parent test. + // Each value is the count of the number of occurrences of the given subtest name + // already seen. + subNames map[string]int32 +} + +type filterMatch interface { + // matches checks the name against the receiver's pattern strings using the + // given match function. + matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) + + // verify checks that the receiver's pattern strings are valid filters by + // calling the given match function. + verify(name string, matchString func(pat, str string) (bool, error)) error } +// simpleMatch matches a test name if all of the pattern strings match in +// sequence. +type simpleMatch []string + +// alternationMatch matches a test name if one of the alternations match. +type alternationMatch []filterMatch + // TODO: fix test_main to avoid race and improve caching, also allowing to // eliminate this Mutex. var matchMutex sync.Mutex func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher { - var filter []string + var impl filterMatch if patterns != "" { - filter = splitRegexp(patterns) - for i, s := range filter { - filter[i] = rewrite(s) - } - // Verify filters before doing any processing. - for i, s := range filter { - if _, err := matchString(s, "non-empty"); err != nil { - fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err) - os.Exit(1) - } + impl = splitRegexp(patterns) + if err := impl.verify(name, matchString); err != nil { + fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err) + os.Exit(1) } } return &matcher{ - filter: filter, + filter: impl, matchFunc: matchString, - subNames: map[string]int64{}, + subNames: map[string]int32{}, } } @@ -60,22 +76,74 @@ func (m *matcher) fullName(c *common, subname string) (name string, ok, partial matchMutex.Lock() defer matchMutex.Unlock() + if m.filter == nil { + return name, true, false + } + // We check the full array of paths each time to allow for the case that // a pattern contains a '/'. elem := strings.Split(name, "/") - for i, s := range elem { - if i >= len(m.filter) { + ok, partial = m.filter.matches(elem, m.matchFunc) + return name, ok, partial +} + +// clearSubNames clears the matcher's internal state, potentially freeing +// memory. After this is called, T.Name may return the same strings as it did +// for earlier subtests. +func (m *matcher) clearSubNames() { + m.mu.Lock() + defer m.mu.Unlock() + for key := range m.subNames { + delete(m.subNames, key) + } +} + +func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) { + for i, s := range name { + if i >= len(m) { break } - if ok, _ := m.matchFunc(m.filter[i], s); !ok { - return name, false, false + if ok, _ := matchString(m[i], s); !ok { + return false, false + } + } + return true, len(name) < len(m) +} + +func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error { + for i, s := range m { + m[i] = rewrite(s) + } + // Verify filters before doing any processing. + for i, s := range m { + if _, err := matchString(s, "non-empty"); err != nil { + return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err) + } + } + return nil +} + +func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) { + for _, m := range m { + if ok, partial = m.matches(name, matchString); ok { + return ok, partial + } + } + return false, false +} + +func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error { + for i, m := range m { + if err := m.verify(name, matchString); err != nil { + return fmt.Errorf("alternation %d of %s", i, err) } } - return name, true, len(elem) < len(m.filter) + return nil } -func splitRegexp(s string) []string { - a := make([]string, 0, strings.Count(s, "/")) +func splitRegexp(s string) filterMatch { + a := make(simpleMatch, 0, strings.Count(s, "/")) + b := make(alternationMatch, 0, strings.Count(s, "|")) cs := 0 cp := 0 for i := 0; i < len(s); { @@ -103,31 +171,88 @@ func splitRegexp(s string) []string { i = 0 continue } + case '|': + if cs == 0 && cp == 0 { + a = append(a, s[:i]) + s = s[i+1:] + i = 0 + b = append(b, a) + a = make(simpleMatch, 0, len(a)) + continue + } } i++ } - return append(a, s) + + a = append(a, s) + if len(b) == 0 { + return a + } + return append(b, a) } // unique creates a unique name for the given parent and subname by affixing it // with one or more counts, if necessary. func (m *matcher) unique(parent, subname string) string { - name := fmt.Sprintf("%s/%s", parent, subname) - empty := subname == "" + base := parent + "/" + subname + for { - next, exists := m.subNames[name] - if !empty && !exists { - m.subNames[name] = 1 // next count is 1 - return name + n := m.subNames[base] + if n < 0 { + panic("subtest count overflow") } - // Name was already used. We increment with the count and append a - // string with the count. - m.subNames[name] = next + 1 + m.subNames[base] = n + 1 + + if n == 0 && subname != "" { + prefix, nn := parseSubtestNumber(base) + if len(prefix) < len(base) && nn < m.subNames[prefix] { + // This test is explicitly named like "parent/subname#NN", + // and #NN was already used for the NNth occurrence of "parent/subname". + // Loop to add a disambiguating suffix. + continue + } + return base + } + + name := fmt.Sprintf("%s#%02d", base, n) + if m.subNames[name] != 0 { + // This is the nth occurrence of base, but the name "parent/subname#NN" + // collides with the first occurrence of a subtest *explicitly* named + // "parent/subname#NN". Try the next number. + continue + } + + return name + } +} + +// parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32 +// suffix (if present), and a prefix preceding that suffix (always). +func parseSubtestNumber(s string) (prefix string, nn int32) { + i := strings.LastIndex(s, "#") + if i < 0 { + return s, 0 + } + + prefix, suffix := s[:i], s[i+1:] + if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') { + // Even if suffix is numeric, it is not a possible output of a "%02" format + // string: it has either too few digits or too many leading zeroes. + return s, 0 + } + if suffix == "00" { + if !strings.HasSuffix(prefix, "/") { + // We only use "#00" as a suffix for subtests named with the empty + // string — it isn't a valid suffix if the subtest name is non-empty. + return s, 0 + } + } - // Add a count to guarantee uniqueness. - name = fmt.Sprintf("%s#%02d", name, next) - empty = false + n, err := strconv.ParseInt(suffix, 10, 32) + if err != nil || n < 0 { + return s, 0 } + return prefix, int32(n) } // rewrite rewrites a subname to having only printable characters and no white diff --git a/src/testing/match_test.go b/src/testing/match_test.go index 8c09dc660fb1a4..206ac0b651f408 100644 --- a/src/testing/match_test.go +++ b/src/testing/match_test.go @@ -5,8 +5,10 @@ package testing import ( + "fmt" "reflect" "regexp" + "strings" "unicode" ) @@ -25,10 +27,11 @@ func TestIsSpace(t *T) { } func TestSplitRegexp(t *T) { - res := func(s ...string) []string { return s } + res := func(s ...string) filterMatch { return simpleMatch(s) } + alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) } testCases := []struct { pattern string - result []string + result filterMatch }{ // Correct patterns // If a regexp pattern is correct, all split regexps need to be correct @@ -49,6 +52,8 @@ func TestSplitRegexp(t *T) { {`([)/][(])`, res(`([)/][(])`)}, {"[(]/[)]", res("[(]", "[)]")}, + {"A/B|C/D", alt(res("A", "B"), res("C", "D"))}, + // Faulty patterns // Errors in original should produce at least one faulty regexp in results. {")/", res(")/")}, @@ -71,10 +76,8 @@ func TestSplitRegexp(t *T) { // needs to have an error as well. if _, err := regexp.Compile(tc.pattern); err != nil { ok := true - for _, re := range a { - if _, err := regexp.Compile(re); err != nil { - ok = false - } + if err := a.verify("", regexp.MatchString); err != nil { + ok = false } if ok { t.Errorf("%s: expected error in any of %q", tc.pattern, a) @@ -113,6 +116,10 @@ func TestMatcher(t *T) { {"TestFoo/", "TestBar", "x", false, false}, {"TestFoo/bar/baz", "TestBar", "x/bar/baz", false, false}, + {"A/B|C/D", "TestA", "B", true, false}, + {"A/B|C/D", "TestC", "D", true, false}, + {"A/B|C/D", "TestA", "C", false, false}, + // subtests only {"", "TestFoo", "x", true, false}, {"/", "TestFoo", "x", true, false}, @@ -142,45 +149,90 @@ func TestMatcher(t *T) { } } +var namingTestCases = []struct{ name, want string }{ + // Uniqueness + {"", "x/#00"}, + {"", "x/#01"}, + {"#0", "x/#0"}, // Doesn't conflict with #00 because the number of digits differs. + {"#00", "x/#00#01"}, // Conflicts with implicit #00 (used above), so add a suffix. + {"#", "x/#"}, + {"#", "x/##01"}, + + {"t", "x/t"}, + {"t", "x/t#01"}, + {"t", "x/t#02"}, + {"t#00", "x/t#00"}, // Explicit "#00" doesn't conflict with the unsuffixed first subtest. + + {"a#01", "x/a#01"}, // user has subtest with this name. + {"a", "x/a"}, // doesn't conflict with this name. + {"a", "x/a#02"}, // This string is claimed now, so resume + {"a", "x/a#03"}, // with counting. + {"a#02", "x/a#02#01"}, // We already used a#02 once, so add a suffix. + + {"b#00", "x/b#00"}, + {"b", "x/b"}, // Implicit 0 doesn't conflict with explicit "#00". + {"b", "x/b#01"}, + {"b#9223372036854775807", "x/b#9223372036854775807"}, // MaxInt64 + {"b", "x/b#02"}, + {"b", "x/b#03"}, + + // Sanitizing + {"A:1 B:2", "x/A:1_B:2"}, + {"s\t\r\u00a0", "x/s___"}, + {"\x01", `x/\x01`}, + {"\U0010ffff", `x/\U0010ffff`}, +} + func TestNaming(t *T) { m := newMatcher(regexp.MatchString, "", "") - parent := &common{name: "x", level: 1} // top-level test. - // Rig the matcher with some preloaded values. - m.subNames["x/b"] = 1000 + for i, tc := range namingTestCases { + if got, _, _ := m.fullName(parent, tc.name); got != tc.want { + t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want) + } + } +} - testCases := []struct { - name, want string - }{ - // Uniqueness - {"", "x/#00"}, - {"", "x/#01"}, - - {"t", "x/t"}, - {"t", "x/t#01"}, - {"t", "x/t#02"}, - - {"a#01", "x/a#01"}, // user has subtest with this name. - {"a", "x/a"}, // doesn't conflict with this name. - {"a", "x/a#01#01"}, // conflict, add disambiguating string. - {"a", "x/a#02"}, // This string is claimed now, so resume - {"a", "x/a#03"}, // with counting. - {"a#02", "x/a#02#01"}, - - {"b", "x/b#1000"}, // rigged, see above - {"b", "x/b#1001"}, - - // // Sanitizing - {"A:1 B:2", "x/A:1_B:2"}, - {"s\t\r\u00a0", "x/s___"}, - {"\x01", `x/\x01`}, - {"\U0010ffff", `x/\U0010ffff`}, +func FuzzNaming(f *F) { + for _, tc := range namingTestCases { + f.Add(tc.name) + } + parent := &common{name: "x", level: 1} + var m *matcher + var seen map[string]string + reset := func() { + m = newMatcher(regexp.MatchString, "", "") + seen = make(map[string]string) } + reset() - for i, tc := range testCases { - if got, _, _ := m.fullName(parent, tc.name); got != tc.want { - t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want) + f.Fuzz(func(t *T, subname string) { + if len(subname) > 10 { + // Long names attract the OOM killer. + t.Skip() } + name := m.unique(parent.name, subname) + if !strings.Contains(name, "/"+subname) { + t.Errorf("name %q does not contain subname %q", name, subname) + } + if prev, ok := seen[name]; ok { + t.Errorf("name %q generated by both %q and %q", name, prev, subname) + } + if len(seen) > 1e6 { + // Free up memory. + reset() + } + seen[name] = subname + }) +} + +// GoString returns a string that is more readable than the default, which makes +// it easier to read test errors. +func (m alternationMatch) GoString() string { + s := make([]string, len(m)) + for i, m := range m { + s[i] = fmt.Sprintf("%#v", m) } + return fmt.Sprintf("(%s)", strings.Join(s, " | ")) } diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go index c01647ecf0c911..95a635badec19e 100644 --- a/src/testing/quick/quick.go +++ b/src/testing/quick/quick.go @@ -113,7 +113,7 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value, } v.SetMapIndex(key, value) } - case reflect.Ptr: + case reflect.Pointer: if rand.Intn(size) == 0 { v.Set(reflect.Zero(concrete)) // Generate nil pointer. } else { @@ -227,7 +227,7 @@ func (s SetupError) Error() string { return string(s) } // A CheckError is the result of Check finding an error. type CheckError struct { Count int - In []interface{} + In []any } func (s *CheckError) Error() string { @@ -237,8 +237,8 @@ func (s *CheckError) Error() string { // A CheckEqualError is the result CheckEqual finding an error. type CheckEqualError struct { CheckError - Out1 []interface{} - Out2 []interface{} + Out1 []any + Out2 []any } func (s *CheckEqualError) Error() string { @@ -251,16 +251,16 @@ func (s *CheckEqualError) Error() string { // Check returns that input as a *CheckError. // For example: // -// func TestOddMultipleOfThree(t *testing.T) { -// f := func(x int) bool { -// y := OddMultipleOfThree(x) -// return y%2 == 1 && y%3 == 0 -// } -// if err := quick.Check(f, nil); err != nil { -// t.Error(err) -// } -// } -func Check(f interface{}, config *Config) error { +// func TestOddMultipleOfThree(t *testing.T) { +// f := func(x int) bool { +// y := OddMultipleOfThree(x) +// return y%2 == 1 && y%3 == 0 +// } +// if err := quick.Check(f, nil); err != nil { +// t.Error(err) +// } +// } +func Check(f any, config *Config) error { if config == nil { config = &defaultConfig } @@ -299,7 +299,7 @@ func Check(f interface{}, config *Config) error { // It calls f and g repeatedly with arbitrary values for each argument. // If f and g return different answers, CheckEqual returns a *CheckEqualError // describing the input and the outputs. -func CheckEqual(f, g interface{}, config *Config) error { +func CheckEqual(f, g any, config *Config) error { if config == nil { config = &defaultConfig } @@ -358,7 +358,7 @@ func arbitraryValues(args []reflect.Value, f reflect.Type, config *Config, rand return } -func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) { +func functionAndType(f any) (v reflect.Value, t reflect.Type, ok bool) { v = reflect.ValueOf(f) ok = v.Kind() == reflect.Func if !ok { @@ -368,15 +368,15 @@ func functionAndType(f interface{}) (v reflect.Value, t reflect.Type, ok bool) { return } -func toInterfaces(values []reflect.Value) []interface{} { - ret := make([]interface{}, len(values)) +func toInterfaces(values []reflect.Value) []any { + ret := make([]any, len(values)) for i, v := range values { ret[i] = v.Interface() } return ret } -func toString(interfaces []interface{}) string { +func toString(interfaces []any) string { s := make([]string, len(interfaces)) for i, v := range interfaces { s[i] = fmt.Sprintf("%#v", v) diff --git a/src/testing/run_example.go b/src/testing/run_example.go index d9e342d4951edd..e7eab1e50efd79 100644 --- a/src/testing/run_example.go +++ b/src/testing/run_example.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js -// +build !js // TODO(@musiol, @odeke-em): re-unify this entire file back into // example.go when js/wasm gets an os.Pipe implementation diff --git a/src/testing/run_example_js.go b/src/testing/run_example_js.go index d914633ba91889..f3a11201a10578 100644 --- a/src/testing/run_example_js.go +++ b/src/testing/run_example_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js -// +build js package testing @@ -37,7 +36,7 @@ func runExample(eg InternalExample) (ok bool) { // Restore stdout, get output and remove temporary file. os.Stdout = stdout var buf strings.Builder - _, seekErr := f.Seek(0, os.SEEK_SET) + _, seekErr := f.Seek(0, io.SeekStart) _, readErr := io.Copy(&buf, f) out := buf.String() f.Close() diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 6c7d83aac2c036..6d8badfbf8194a 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -477,12 +477,13 @@ func TestTRun(t *T) { for _, tc := range testCases { t.Run(tc.desc, func(t *T) { ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) - buf := &bytes.Buffer{} + buf := &strings.Builder{} root := &T{ common: common{ - signal: make(chan bool), - name: "Test", - w: buf, + signal: make(chan bool), + barrier: make(chan bool), + name: "Test", + w: buf, }, context: ctx, } @@ -656,10 +657,14 @@ func TestBRun(t *T) { } }, }} + hideStdoutForTesting = true + defer func() { + hideStdoutForTesting = false + }() for _, tc := range testCases { t.Run(tc.desc, func(t *T) { var ok bool - buf := &bytes.Buffer{} + buf := &strings.Builder{} // This is almost like the Benchmark function, except that we override // the benchtime and catch the failure result of the subbenchmark. root := &B{ @@ -669,7 +674,7 @@ func TestBRun(t *T) { w: buf, }, benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure. - benchTime: benchTimeFlag{d: 1 * time.Microsecond}, + benchTime: durationOrCountFlag{d: 1 * time.Microsecond}, } if tc.chatty { root.chatty = newChattyPrinter(root.w) @@ -723,7 +728,7 @@ func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) { var first = true Benchmark(func(b *B) { if first && (b.startAllocs == 0 || b.startBytes == 0) { - panic(fmt.Sprintf("ReadMemStats not called before first run")) + panic("ReadMemStats not called before first run") } first = false }) diff --git a/src/testing/testing.go b/src/testing/testing.go index a19238d31e23fa..0228d2904b2c58 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -5,208 +5,338 @@ // Package testing provides support for automated testing of Go packages. // It is intended to be used in concert with the "go test" command, which automates // execution of any function of the form -// func TestXxx(*testing.T) +// +// func TestXxx(*testing.T) +// // where Xxx does not start with a lowercase letter. The function name // serves to identify the test routine. // // Within these functions, use the Error, Fail or related methods to signal failure. // // To write a new test suite, create a file whose name ends _test.go that -// contains the TestXxx functions as described here. Put the file in the same -// package as the one being tested. The file will be excluded from regular +// contains the TestXxx functions as described here. +// The file will be excluded from regular // package builds but will be included when the "go test" command is run. -// For more detail, run "go help test" and "go help testflag". // -// A simple test function looks like this: +// The test file can be in the same package as the one being tested, +// or in a corresponding package with the suffix "_test". +// +// If the test file is in the same package, it may refer to unexported +// identifiers within the package, as in this example: // -// func TestAbs(t *testing.T) { -// got := Abs(-1) -// if got != 1 { -// t.Errorf("Abs(-1) = %d; want 1", got) -// } -// } +// package abs +// +// import "testing" +// +// func TestAbs(t *testing.T) { +// got := Abs(-1) +// if got != 1 { +// t.Errorf("Abs(-1) = %d; want 1", got) +// } +// } // -// Benchmarks +// If the file is in a separate "_test" package, the package being tested +// must be imported explicitly and only its exported identifiers may be used. +// This is known as "black box" testing. +// +// package abs_test +// +// import ( +// "testing" +// +// "path_to_pkg/abs" +// ) +// +// func TestAbs(t *testing.T) { +// got := abs.Abs(-1) +// if got != 1 { +// t.Errorf("Abs(-1) = %d; want 1", got) +// } +// } +// +// For more detail, run "go help test" and "go help testflag". +// +// # Benchmarks // // Functions of the form -// func BenchmarkXxx(*testing.B) +// +// func BenchmarkXxx(*testing.B) +// // are considered benchmarks, and are executed by the "go test" command when // its -bench flag is provided. Benchmarks are run sequentially. // // For a description of the testing flags, see -// https://golang.org/cmd/go/#hdr-Testing_flags +// https://golang.org/cmd/go/#hdr-Testing_flags. // // A sample benchmark function looks like this: -// func BenchmarkRandInt(b *testing.B) { -// for i := 0; i < b.N; i++ { -// rand.Int() -// } -// } +// +// func BenchmarkRandInt(b *testing.B) { +// for i := 0; i < b.N; i++ { +// rand.Int() +// } +// } // // The benchmark function must run the target code b.N times. // During benchmark execution, b.N is adjusted until the benchmark function lasts // long enough to be timed reliably. The output -// BenchmarkRandInt-8 68453040 17.8 ns/op +// +// BenchmarkRandInt-8 68453040 17.8 ns/op +// // means that the loop ran 68453040 times at a speed of 17.8 ns per loop. // // If a benchmark needs some expensive setup before running, the timer // may be reset: // -// func BenchmarkBigLen(b *testing.B) { -// big := NewBig() -// b.ResetTimer() -// for i := 0; i < b.N; i++ { -// big.Len() -// } -// } +// func BenchmarkBigLen(b *testing.B) { +// big := NewBig() +// b.ResetTimer() +// for i := 0; i < b.N; i++ { +// big.Len() +// } +// } // // If a benchmark needs to test performance in a parallel setting, it may use // the RunParallel helper function; such benchmarks are intended to be used with // the go test -cpu flag: // -// func BenchmarkTemplateParallel(b *testing.B) { -// templ := template.Must(template.New("test").Parse("Hello, {{.}}!")) -// b.RunParallel(func(pb *testing.PB) { -// var buf bytes.Buffer -// for pb.Next() { -// buf.Reset() -// templ.Execute(&buf, "World") -// } -// }) -// } +// func BenchmarkTemplateParallel(b *testing.B) { +// templ := template.Must(template.New("test").Parse("Hello, {{.}}!")) +// b.RunParallel(func(pb *testing.PB) { +// var buf bytes.Buffer +// for pb.Next() { +// buf.Reset() +// templ.Execute(&buf, "World") +// } +// }) +// } +// +// A detailed specification of the benchmark results format is given +// in https://golang.org/design/14313-benchmark-format. +// +// There are standard tools for working with benchmark results at +// https://golang.org/x/perf/cmd. +// In particular, https://golang.org/x/perf/cmd/benchstat performs +// statistically robust A/B comparisons. // -// Examples +// # Examples // // The package also runs and verifies example code. Example functions may // include a concluding line comment that begins with "Output:" and is compared with // the standard output of the function when the tests are run. (The comparison // ignores leading and trailing space.) These are examples of an example: // -// func ExampleHello() { -// fmt.Println("hello") -// // Output: hello -// } +// func ExampleHello() { +// fmt.Println("hello") +// // Output: hello +// } // -// func ExampleSalutations() { -// fmt.Println("hello, and") -// fmt.Println("goodbye") -// // Output: -// // hello, and -// // goodbye -// } +// func ExampleSalutations() { +// fmt.Println("hello, and") +// fmt.Println("goodbye") +// // Output: +// // hello, and +// // goodbye +// } // // The comment prefix "Unordered output:" is like "Output:", but matches any // line order: // -// func ExamplePerm() { -// for _, value := range Perm(5) { -// fmt.Println(value) -// } -// // Unordered output: 4 -// // 2 -// // 1 -// // 3 -// // 0 -// } +// func ExamplePerm() { +// for _, value := range Perm(5) { +// fmt.Println(value) +// } +// // Unordered output: 4 +// // 2 +// // 1 +// // 3 +// // 0 +// } // // Example functions without output comments are compiled but not executed. // // The naming convention to declare examples for the package, a function F, a type T and // method M on type T are: // -// func Example() { ... } -// func ExampleF() { ... } -// func ExampleT() { ... } -// func ExampleT_M() { ... } +// func Example() { ... } +// func ExampleF() { ... } +// func ExampleT() { ... } +// func ExampleT_M() { ... } // // Multiple example functions for a package/type/function/method may be provided by // appending a distinct suffix to the name. The suffix must start with a // lower-case letter. // -// func Example_suffix() { ... } -// func ExampleF_suffix() { ... } -// func ExampleT_suffix() { ... } -// func ExampleT_M_suffix() { ... } +// func Example_suffix() { ... } +// func ExampleF_suffix() { ... } +// func ExampleT_suffix() { ... } +// func ExampleT_M_suffix() { ... } // // The entire test file is presented as the example when it contains a single // example function, at least one other function, type, variable, or constant // declaration, and no test or benchmark functions. // -// Skipping +// # Fuzzing +// +// 'go test' and the testing package support fuzzing, a testing technique where +// a function is called with randomly generated inputs to find bugs not +// anticipated by unit tests. +// +// Functions of the form +// +// func FuzzXxx(*testing.F) +// +// are considered fuzz tests. +// +// For example: +// +// func FuzzHex(f *testing.F) { +// for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} { +// f.Add(seed) +// } +// f.Fuzz(func(t *testing.T, in []byte) { +// enc := hex.EncodeToString(in) +// out, err := hex.DecodeString(enc) +// if err != nil { +// t.Fatalf("%v: decode: %v", in, err) +// } +// if !bytes.Equal(in, out) { +// t.Fatalf("%v: not equal after round trip: %v", in, out) +// } +// }) +// } +// +// A fuzz test maintains a seed corpus, or a set of inputs which are run by +// default, and can seed input generation. Seed inputs may be registered by +// calling (*F).Add or by storing files in the directory testdata/fuzz/ +// (where is the name of the fuzz test) within the package containing +// the fuzz test. Seed inputs are optional, but the fuzzing engine may find +// bugs more efficiently when provided with a set of small seed inputs with good +// code coverage. These seed inputs can also serve as regression tests for bugs +// identified through fuzzing. +// +// The function passed to (*F).Fuzz within the fuzz test is considered the fuzz +// target. A fuzz target must accept a *T parameter, followed by one or more +// parameters for random inputs. The types of arguments passed to (*F).Add must +// be identical to the types of these parameters. The fuzz target may signal +// that it's found a problem the same way tests do: by calling T.Fail (or any +// method that calls it like T.Error or T.Fatal) or by panicking. +// +// When fuzzing is enabled (by setting the -fuzz flag to a regular expression +// that matches a specific fuzz test), the fuzz target is called with arguments +// generated by repeatedly making random changes to the seed inputs. On +// supported platforms, 'go test' compiles the test executable with fuzzing +// coverage instrumentation. The fuzzing engine uses that instrumentation to +// find and cache inputs that expand coverage, increasing the likelihood of +// finding bugs. If the fuzz target fails for a given input, the fuzzing engine +// writes the inputs that caused the failure to a file in the directory +// testdata/fuzz/ within the package directory. This file later serves as +// a seed input. If the file can't be written at that location (for example, +// because the directory is read-only), the fuzzing engine writes the file to +// the fuzz cache directory within the build cache instead. +// +// When fuzzing is disabled, the fuzz target is called with the seed inputs +// registered with F.Add and seed inputs from testdata/fuzz/. In this +// mode, the fuzz test acts much like a regular test, with subtests started +// with F.Fuzz instead of T.Run. +// +// See https://go.dev/doc/fuzz for documentation about fuzzing. +// +// # Skipping // // Tests or benchmarks may be skipped at run time with a call to // the Skip method of *T or *B: // -// func TestTimeConsuming(t *testing.T) { -// if testing.Short() { -// t.Skip("skipping test in short mode.") -// } -// ... -// } +// func TestTimeConsuming(t *testing.T) { +// if testing.Short() { +// t.Skip("skipping test in short mode.") +// } +// ... +// } +// +// The Skip method of *T can be used in a fuzz target if the input is invalid, +// but should not be considered a failing input. For example: // -// Subtests and Sub-benchmarks +// func FuzzJSONMarshaling(f *testing.F) { +// f.Fuzz(func(t *testing.T, b []byte) { +// var v interface{} +// if err := json.Unmarshal(b, &v); err != nil { +// t.Skip() +// } +// if _, err := json.Marshal(v); err != nil { +// t.Error("Marshal: %v", err) +// } +// }) +// } +// +// # 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) { ... }) -// // -// } +// 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 an unanchored regular +// The argument to the -run, -bench, and -fuzz command-line flags is an unanchored regular // expression that matches the test's name. For tests with multiple slash-separated // elements, such as subtests, the argument is itself slash-separated, with // expressions matching each name element in turn. Because it is unanchored, an // empty expression matches any string. // For example, using "matching" to mean "whose name contains": // -// go test -run '' # Run all tests. -// go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar". -// go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=". -// go test -run /A=1 # For all top-level tests, run subtests matching "A=1". +// go test -run '' # Run all tests. +// go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar". +// go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=". +// go test -run /A=1 # For all top-level tests, run subtests matching "A=1". +// go test -fuzz FuzzFoo # Fuzz the target matching "FuzzFoo" +// +// The -run argument can also be used to run a specific value in the seed +// corpus, for debugging. For example: +// +// go test -run=FuzzFoo/9ddb952d9814 +// +// The -fuzz and -run flags can both be set, in order to fuzz a target but +// skip the execution of all other tests. // // 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() -// ... -// }) -// } -// } -// -// The race detector kills the program if it exceeds 8128 concurrent goroutines, -// so use care when running parallel tests with the -race flag set. +// 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) -// }) -// // -// } +// 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 +// # Main // // It is sometimes necessary for a test or benchmark program to do extra setup or teardown // before or after it executes. It is also sometimes necessary to control @@ -246,6 +376,7 @@ import ( "io" "math/rand" "os" + "reflect" "runtime" "runtime/debug" "runtime/trace" @@ -307,6 +438,7 @@ func Init() { shuffle = flag.String("test.shuffle", "off", "randomize the execution order of tests and benchmarks") initBenchmarkFlags() + initFuzzFlags() } var ( @@ -339,7 +471,7 @@ var ( cpuList []int testlogFile *os.File - numFailed uint32 // number of test failures + numFailed atomic.Uint32 // number of test failures ) type chattyPrinter struct { @@ -355,7 +487,7 @@ func newChattyPrinter(w io.Writer) *chattyPrinter { // Updatef prints a message about the status of the named test to w. // // The formatted message must include the test name itself. -func (p *chattyPrinter) Updatef(testName, format string, args ...interface{}) { +func (p *chattyPrinter) Updatef(testName, format string, args ...any) { p.lastNameMu.Lock() defer p.lastNameMu.Unlock() @@ -369,7 +501,7 @@ func (p *chattyPrinter) Updatef(testName, format string, args ...interface{}) { // Printf prints a message, generated by the named test, that does not // necessarily mention that tests's name itself. -func (p *chattyPrinter) Printf(testName, format string, args ...interface{}) { +func (p *chattyPrinter) Printf(testName, format string, args ...any) { p.lastNameMu.Lock() defer p.lastNameMu.Unlock() @@ -403,10 +535,11 @@ type common struct { cleanupName string // Name of the cleanup function. cleanupPc []uintptr // The stack trace at the point where Cleanup was called. finished bool // Test function has completed. + inFuzzFn bool // Whether the fuzz target, if this is one, is running. chatty *chattyPrinter // A copy of chattyPrinter, if the chatty flag is set. bench bool // Whether the current test is a benchmark. - hasSub int32 // Written atomically. + hasSub atomic.Bool // whether there are sub-benchmarks. raceErrors int // Number of races detected during test. runner string // Function name of tRunner running the test. @@ -416,7 +549,7 @@ type common struct { name string // Name of test or benchmark. start time.Time // Time test or benchmark started duration time.Duration - barrier chan bool // To signal parallel subtests they may start. + barrier chan bool // To signal parallel subtests they may start. Nil when T.Parallel is not present (B) or not usable (when fuzzing). signal chan bool // To signal a test is done. sub []*T // Queue of subtests to be run in parallel. @@ -458,6 +591,12 @@ func Verbose() bool { return *chatty } +func (c *common) checkFuzzFn(name string) { + if c.inFuzzFn { + panic(fmt.Sprintf("testing: f.%s was called inside the fuzz target, use t.%s instead", name, name)) + } +} + // frameSkip searches, starting after skip frames, for the first caller frame // in a function not marked as a helper and returns that frame. // The search stops if it finds a tRunner function that @@ -483,6 +622,9 @@ func (c *common) frameSkip(skip int) runtime.Frame { var firstFrame, prevFrame, frame runtime.Frame for more := true; more; prevFrame = frame { frame, more = frames.Next() + if frame.Function == "runtime.gopanic" { + continue + } if frame.Function == c.cleanupName { frames = runtime.CallersFrames(c.cleanupPc) continue @@ -572,7 +714,7 @@ func (c *common) decorate(s string, skip int) string { // flushToParent writes c.output to the parent after first writing the header // with the given format and arguments. -func (c *common) flushToParent(testName, format string, args ...interface{}) { +func (c *common) flushToParent(testName, format string, args ...any) { p := c.parent p.mu.Lock() defer p.mu.Unlock() @@ -632,24 +774,24 @@ func fmtDuration(d time.Duration) string { return fmt.Sprintf("%.2fs", d.Seconds()) } -// TB is the interface common to T and B. +// TB is the interface common to T, B, and F. type TB interface { Cleanup(func()) - Error(args ...interface{}) - Errorf(format string, args ...interface{}) + Error(args ...any) + Errorf(format string, args ...any) Fail() FailNow() Failed() bool - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) + Fatal(args ...any) + Fatalf(format string, args ...any) Helper() - Log(args ...interface{}) - Logf(format string, args ...interface{}) + Log(args ...any) + Logf(format string, args ...any) Name() string Setenv(key, value string) - Skip(args ...interface{}) + Skip(args ...any) SkipNow() - Skipf(format string, args ...interface{}) + Skipf(format string, args ...any) Skipped() bool TempDir() string @@ -729,6 +871,7 @@ func (c *common) Failed() bool { // created during the test. Calling FailNow does not stop // those other goroutines. func (c *common) FailNow() { + c.checkFuzzFn("FailNow") c.Fail() // Calling runtime.Goexit will exit the goroutine, which @@ -797,47 +940,59 @@ func (c *common) logDepth(s string, depth int) { // and records the text in the error log. For tests, the text will be printed only if // the test fails or the -test.v flag is set. For benchmarks, the text is always // printed to avoid having performance depend on the value of the -test.v flag. -func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) } +func (c *common) Log(args ...any) { + c.checkFuzzFn("Log") + c.log(fmt.Sprintln(args...)) +} // Logf formats its arguments according to the format, analogous to Printf, and // records the text in the error log. A final newline is added if not provided. For // tests, the text will be printed only if the test fails or the -test.v flag is // set. For benchmarks, the text is always printed to avoid having performance // depend on the value of the -test.v flag. -func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) } +func (c *common) Logf(format string, args ...any) { + c.checkFuzzFn("Logf") + c.log(fmt.Sprintf(format, args...)) +} // Error is equivalent to Log followed by Fail. -func (c *common) Error(args ...interface{}) { +func (c *common) Error(args ...any) { + c.checkFuzzFn("Error") c.log(fmt.Sprintln(args...)) c.Fail() } // Errorf is equivalent to Logf followed by Fail. -func (c *common) Errorf(format string, args ...interface{}) { +func (c *common) Errorf(format string, args ...any) { + c.checkFuzzFn("Errorf") c.log(fmt.Sprintf(format, args...)) c.Fail() } // Fatal is equivalent to Log followed by FailNow. -func (c *common) Fatal(args ...interface{}) { +func (c *common) Fatal(args ...any) { + c.checkFuzzFn("Fatal") c.log(fmt.Sprintln(args...)) c.FailNow() } // Fatalf is equivalent to Logf followed by FailNow. -func (c *common) Fatalf(format string, args ...interface{}) { +func (c *common) Fatalf(format string, args ...any) { + c.checkFuzzFn("Fatalf") c.log(fmt.Sprintf(format, args...)) c.FailNow() } // Skip is equivalent to Log followed by SkipNow. -func (c *common) Skip(args ...interface{}) { +func (c *common) Skip(args ...any) { + c.checkFuzzFn("Skip") c.log(fmt.Sprintln(args...)) c.SkipNow() } // Skipf is equivalent to Logf followed by SkipNow. -func (c *common) Skipf(format string, args ...interface{}) { +func (c *common) Skipf(format string, args ...any) { + c.checkFuzzFn("Skipf") c.log(fmt.Sprintf(format, args...)) c.SkipNow() } @@ -851,6 +1006,7 @@ func (c *common) Skipf(format string, args ...interface{}) { // other goroutines created during the test. Calling SkipNow does not stop // those other goroutines. func (c *common) SkipNow() { + c.checkFuzzFn("SkipNow") c.mu.Lock() c.skipped = true c.finished = true @@ -890,6 +1046,7 @@ func (c *common) Helper() { // subtests complete. Cleanup functions will be called in last added, // first called order. func (c *common) Cleanup(f func()) { + c.checkFuzzFn("Cleanup") var pc [maxStackLen]uintptr // Skip two extra frames to account for this function and runtime.Callers itself. n := runtime.Callers(2, pc[:]) @@ -923,6 +1080,7 @@ func (c *common) Cleanup(f func()) { // Each subsequent call to t.TempDir returns a unique directory; // if the directory creation fails, TempDir terminates the test by calling Fatal. func (c *common) TempDir() string { + c.checkFuzzFn("TempDir") // Use a single parent directory for all the temporary directories // created by a test, each numbered sequentially. c.tempDirMu.Lock() @@ -963,18 +1121,23 @@ func (c *common) TempDir() string { c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern) if c.tempDirErr == nil { c.Cleanup(func() { - if err := os.RemoveAll(c.tempDir); err != nil { + if err := removeAll(c.tempDir); err != nil { c.Errorf("TempDir RemoveAll cleanup: %v", err) } }) } } + + if c.tempDirErr == nil { + c.tempDirSeq++ + } + seq := c.tempDirSeq c.tempDirMu.Unlock() if c.tempDirErr != nil { c.Fatalf("TempDir: %v", c.tempDirErr) } - seq := atomic.AddInt32(&c.tempDirSeq, 1) + dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq) if err := os.Mkdir(dir, 0777); err != nil { c.Fatalf("TempDir: %v", err) @@ -982,12 +1145,43 @@ func (c *common) TempDir() string { return dir } +// removeAll is like os.RemoveAll, but retries Windows "Access is denied." +// errors up to an arbitrary timeout. +// +// Those errors have been known to occur spuriously on at least the +// windows-amd64-2012 builder (https://go.dev/issue/50051), and can only occur +// legitimately if the test leaves behind a temp file that either is still open +// or the test otherwise lacks permission to delete. In the case of legitimate +// failures, a failing test may take a bit longer to fail, but once the test is +// fixed the extra latency will go away. +func removeAll(path string) error { + const arbitraryTimeout = 2 * time.Second + var ( + start time.Time + nextSleep = 1 * time.Millisecond + ) + for { + err := os.RemoveAll(path) + if !isWindowsRetryable(err) { + return err + } + if start.IsZero() { + start = time.Now() + } else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout { + return err + } + time.Sleep(nextSleep) + nextSleep += time.Duration(rand.Int63n(int64(nextSleep))) + } +} + // Setenv calls os.Setenv(key, value) and uses Cleanup to // restore the environment variable to its original value // after the test. // // This cannot be used in parallel tests. func (c *common) Setenv(key, value string) { + c.checkFuzzFn("Setenv") prevValue, ok := os.LookupEnv(key) if err := os.Setenv(key, value); err != nil { @@ -1016,7 +1210,7 @@ const ( // runCleanup is called at the end of the test. // If catchPanic is true, this will catch panics, and return the recovered // value if any. -func (c *common) runCleanup(ph panicHandling) (panicVal interface{}) { +func (c *common) runCleanup(ph panicHandling) (panicVal any) { if ph == recoverAndReturnPanic { defer func() { panicVal = recover() @@ -1080,6 +1274,12 @@ func (t *T) Parallel() { panic("testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests") } t.isParallel = true + if t.parent.barrier == nil { + // T.Parallel has no effect when fuzzing. + // Multiple processes may run in parallel, but only one input can run at a + // time per process so we can attribute crashes to specific inputs. + return + } // We don't want to include the time we spend waiting for serial tests // in the test duration. Record the elapsed time thus far and reset the @@ -1145,14 +1345,21 @@ func tRunner(t *T, fn func(t *T)) { // a signal saying that the test is done. defer func() { if t.Failed() { - atomic.AddUint32(&numFailed, 1) + numFailed.Add(1) } if t.raceErrors+race.Errors() > 0 { t.Errorf("race detected during execution of test") } - // If the test panicked, print any test output before dying. + // Check if the test panicked or Goexited inappropriately. + // + // If this happens in a normal test, print output but continue panicking. + // tRunner is called in its own goroutine, so this terminates the process. + // + // If this happens while fuzzing, recover from the panic and treat it like a + // normal failure. It's important that the process keeps running in order to + // find short inputs that cause panics. err := recover() signal := true @@ -1173,6 +1380,19 @@ func tRunner(t *T, fn func(t *T)) { } } } + + if err != nil && t.context.isFuzzing { + prefix := "panic: " + if err == errNilPanicOrGoexit { + prefix = "" + } + t.Errorf("%s%s\n%s\n", prefix, err, string(debug.Stack())) + t.mu.Lock() + t.finished = true + t.mu.Unlock() + err = nil + } + // Use a deferred call to ensure that we report that the test is // complete even if a cleanup function calls t.FailNow. See issue 41355. didPanic := false @@ -1189,7 +1409,7 @@ func tRunner(t *T, fn func(t *T)) { t.signal <- signal }() - doPanic := func(err interface{}) { + doPanic := func(err any) { t.Fail() if r := t.runCleanup(recoverAndReturnPanic); r != nil { t.Logf("cleanup panicked with %v", r) @@ -1242,9 +1462,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. + // the user does not appropriately synchronize a goroutine. t.done = true - if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 { + if t.parent != nil && !t.hasSub.Load() { t.setRan() } }() @@ -1271,7 +1491,7 @@ func tRunner(t *T, fn func(t *T)) { // Run may be called simultaneously from multiple goroutines, but all such calls // must return before the outer test function for t returns. func (t *T) Run(name string, f func(t *T)) bool { - atomic.StoreInt32(&t.hasSub, 1) + t.hasSub.Store(true) testName, ok, _ := t.context.match.fullName(&t.common, name) if !ok || shouldFailFast() { return true @@ -1327,6 +1547,12 @@ type testContext struct { match *matcher deadline time.Time + // isFuzzing is true in the context used when generating random inputs + // for fuzz targets. isFuzzing is false when running normal tests and + // when running fuzz tests as unit tests (without -fuzz or when -fuzz + // does not match). + isFuzzing bool + mu sync.Mutex // Channel used to signal tests that are ready to be run in parallel. @@ -1390,6 +1616,16 @@ func (f matchStringOnly) ImportPath() string { return " func (f matchStringOnly) StartTestLog(io.Writer) {} func (f matchStringOnly) StopTestLog() error { return errMain } func (f matchStringOnly) SetPanicOnExit0(bool) {} +func (f matchStringOnly) CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error { + return errMain +} +func (f matchStringOnly) RunFuzzWorker(func(corpusEntry) error) error { return errMain } +func (f matchStringOnly) ReadCorpus(string, []reflect.Type) ([]corpusEntry, error) { + return nil, errMain +} +func (f matchStringOnly) CheckCorpus([]any, []reflect.Type) error { return nil } +func (f matchStringOnly) ResetCoverage() {} +func (f matchStringOnly) SnapshotCoverage() {} // Main is an internal function, part of the implementation of the "go test" command. // It was exported because it is cross-package and predates "internal" packages. @@ -1398,15 +1634,16 @@ func (f matchStringOnly) SetPanicOnExit0(bool) {} // new functionality is added to the testing package. // Systems simulating "go test" should be updated to use MainStart. func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { - os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, examples).Run()) + os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, nil, examples).Run()) } // M is a type passed to a TestMain function to run the actual tests. type M struct { - deps testDeps - tests []InternalTest - benchmarks []InternalBenchmark - examples []InternalExample + deps testDeps + tests []InternalTest + benchmarks []InternalBenchmark + fuzzTargets []InternalFuzzTarget + examples []InternalExample timer *time.Timer afterOnce sync.Once @@ -1431,18 +1668,25 @@ type testDeps interface { StartTestLog(io.Writer) StopTestLog() error WriteProfileTo(string, io.Writer, int) error + CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error + RunFuzzWorker(func(corpusEntry) error) error + ReadCorpus(string, []reflect.Type) ([]corpusEntry, error) + CheckCorpus([]any, []reflect.Type) error + ResetCoverage() + SnapshotCoverage() } // MainStart is meant for use by tests generated by 'go test'. // It is not meant to be called directly and is not subject to the Go 1 compatibility document. // It may change signature from release to release. -func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M { +func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M { Init() return &M{ - deps: deps, - tests: tests, - benchmarks: benchmarks, - examples: examples, + deps: deps, + tests: tests, + benchmarks: benchmarks, + fuzzTargets: fuzzTargets, + examples: examples, } } @@ -1469,9 +1713,15 @@ func (m *M) Run() (code int) { m.exitCode = 2 return } + if *matchFuzz != "" && *fuzzCacheDir == "" { + fmt.Fprintln(os.Stderr, "testing: -test.fuzzcachedir must be set if -test.fuzz is set") + flag.Usage() + m.exitCode = 2 + return + } if len(*matchList) != 0 { - listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples) + listTests(m.deps.MatchString, m.tests, m.benchmarks, m.fuzzTargets, m.examples) m.exitCode = 0 return } @@ -1499,22 +1749,42 @@ func (m *M) Run() (code int) { m.before() defer m.after() - deadline := m.startAlarm() - haveExamples = len(m.examples) > 0 - testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline) - exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples) - m.stopAlarm() - if !testRan && !exampleRan && *matchBenchmarks == "" { - fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") + + // Run tests, examples, and benchmarks unless this is a fuzz worker process. + // Workers start after this is done by their parent process, and they should + // not repeat this work. + if !*isFuzzWorker { + deadline := m.startAlarm() + haveExamples = len(m.examples) > 0 + testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline) + fuzzTargetsRan, fuzzTargetsOk := runFuzzTests(m.deps, m.fuzzTargets, deadline) + exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples) + m.stopAlarm() + if !testRan && !exampleRan && !fuzzTargetsRan && *matchBenchmarks == "" && *matchFuzz == "" { + fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") + } + if !testOk || !exampleOk || !fuzzTargetsOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { + fmt.Println("FAIL") + m.exitCode = 1 + return + } } - if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { + + fuzzingOk := runFuzzing(m.deps, m.fuzzTargets) + if !fuzzingOk { fmt.Println("FAIL") - m.exitCode = 1 + if *isFuzzWorker { + m.exitCode = fuzzWorkerExitCode + } else { + m.exitCode = 1 + } return } - fmt.Println("PASS") m.exitCode = 0 + if !*isFuzzWorker { + fmt.Println("PASS") + } return } @@ -1535,7 +1805,7 @@ func (t *T) report() { } } -func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { +func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) { if _, err := matchString(*matchList, "non-empty"); err != nil { fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err) os.Exit(1) @@ -1551,6 +1821,11 @@ func listTests(matchString func(pat, str string) (bool, error), tests []Internal fmt.Println(bench.Name) } } + for _, fuzzTarget := range fuzzTargets { + if ok, _ := matchString(*matchList, fuzzTarget.Name); ok { + fmt.Println(fuzzTarget.Name) + } + } for _, example := range examples { if ok, _ := matchString(*matchList, example.Name); ok { fmt.Println(example.Name) @@ -1580,6 +1855,12 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT if shouldFailFast() { break } + if i > 0 && !ran { + // There were no tests to run on the first + // iteration. This won't change, so no reason + // to keep trying. + break + } ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run")) ctx.deadline = deadline t := &T{ @@ -1816,5 +2097,5 @@ func parseCpuList() { } func shouldFailFast() bool { - return *failFast && atomic.LoadUint32(&numFailed) > 0 + return *failFast && numFailed.Load() > 0 } diff --git a/src/testing/testing_other.go b/src/testing/testing_other.go new file mode 100644 index 00000000000000..99a6276a4a39a9 --- /dev/null +++ b/src/testing/testing_other.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows + +package testing + +// isWindowsRetryable reports whether err is a Windows error code +// that may be fixed by retrying a failed filesystem operation. +func isWindowsRetryable(err error) bool { + return false +} diff --git a/src/testing/testing_windows.go b/src/testing/testing_windows.go new file mode 100644 index 00000000000000..fd48ae9579e052 --- /dev/null +++ b/src/testing/testing_windows.go @@ -0,0 +1,32 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows + +package testing + +import ( + "errors" + "internal/syscall/windows" + "syscall" +) + +// isWindowsRetryable reports whether err is a Windows error code +// that may be fixed by retrying a failed filesystem operation. +func isWindowsRetryable(err error) bool { + for { + unwrapped := errors.Unwrap(err) + if unwrapped == nil { + break + } + err = unwrapped + } + if err == syscall.ERROR_ACCESS_DENIED { + return true // Observed in https://go.dev/issue/50051. + } + if err == windows.ERROR_SHARING_VIOLATION { + return true // Observed in https://go.dev/issue/51442. + } + return false +} diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go index c5fc4ff93b9061..44be0b6bd4b814 100644 --- a/src/text/scanner/scanner.go +++ b/src/text/scanner/scanner.go @@ -60,7 +60,6 @@ func (pos Position) String() string { // // Use GoTokens to configure the Scanner such that it accepts all Go // literal tokens including Go identifiers. Comments will be skipped. -// const ( ScanIdents = 1 << -Ident ScanInts = 1 << -Int @@ -340,13 +339,13 @@ func (s *Scanner) error(msg string) { fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) } -func (s *Scanner) errorf(format string, args ...interface{}) { +func (s *Scanner) errorf(format string, args ...any) { s.error(fmt.Sprintf(format, args...)) } func (s *Scanner) isIdentRune(ch rune, i int) bool { if s.IsIdentRune != nil { - return s.IsIdentRune(ch, i) + return ch != EOF && s.IsIdentRune(ch, i) } return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0 } diff --git a/src/text/scanner/scanner_test.go b/src/text/scanner/scanner_test.go index fe39d3060bf185..6a454d9be77ba6 100644 --- a/src/text/scanner/scanner_test.go +++ b/src/text/scanner/scanner_test.go @@ -913,3 +913,22 @@ func extractInts(t string, mode uint) (res string) { } } } + +func TestIssue50909(t *testing.T) { + var s Scanner + s.Init(strings.NewReader("hello \n\nworld\n!\n")) + s.IsIdentRune = func(ch rune, _ int) bool { return ch != '\n' } + + r := "" + n := 0 + for s.Scan() != EOF && n < 10 { + r += s.TokenText() + n++ + } + + const R = "hello world!" + const N = 3 + if r != R || n != N { + t.Errorf("got %q (n = %d); want %q (n = %d)", r, n, R, N) + } +} diff --git a/src/text/tabwriter/tabwriter.go b/src/text/tabwriter/tabwriter.go index 76dec7b358011a..d4cfcf556a4024 100644 --- a/src/text/tabwriter/tabwriter.go +++ b/src/text/tabwriter/tabwriter.go @@ -23,7 +23,6 @@ import ( // The text itself is stored in a separate buffer; cell only describes the // segment's size in bytes, its width in runes, and whether it's an htab // ('\t') terminated cell. -// type cell struct { size int // cell size in bytes width int // cell width in runes @@ -87,7 +86,6 @@ type cell struct { // The Writer must buffer input internally, because proper spacing // of one line may depend on the cells in future lines. Clients must // call Flush when done calling Write. -// type Writer struct { // configuration output io.Writer @@ -207,7 +205,6 @@ const ( // (for correct-looking results, tabwidth must correspond // to the tab width in the viewer displaying the result) // flags formatting control -// func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { if minwidth < 0 || tabwidth < 0 || padding < 0 { panic("negative minwidth, tabwidth, or padding") @@ -350,7 +347,6 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { // is the buffer position corresponding to the beginning of line0. // Returns the buffer position corresponding to the beginning of // line1 and an error, if any. -// func (b *Writer) format(pos0 int, line0, line1 int) (pos int) { pos = pos0 column := len(b.widths) @@ -427,7 +423,6 @@ func (b *Writer) updateWidth() { // width one for formatting purposes. // // The value 0xff was chosen because it cannot appear in a valid UTF-8 sequence. -// const Escape = '\xff' // Start escaped mode. @@ -446,7 +441,6 @@ func (b *Writer) startEscape(ch byte) { // is assumed to be zero for formatting purposes; if it was an HTML entity, // its width is assumed to be one. In all other cases, the width is the // unicode width of the text. -// func (b *Writer) endEscape() { switch b.endChar { case Escape: @@ -464,7 +458,6 @@ func (b *Writer) endEscape() { // Terminate the current cell by adding it to the list of cells of the // current line. Returns the number of cells in that line. -// func (b *Writer) terminateCell(htab bool) int { b.cell.htab = htab line := &b.lines[len(b.lines)-1] @@ -526,7 +519,6 @@ var hbar = []byte("---\n") // Write writes buf to the writer b. // The only errors returned are ones encountered // while writing to the underlying output stream. -// func (b *Writer) Write(buf []byte) (n int, err error) { defer b.handlePanic(&err, "Write") @@ -603,7 +595,6 @@ func (b *Writer) Write(buf []byte) (n int, err error) { // NewWriter allocates and initializes a new tabwriter.Writer. // The parameters are the same as for the Init function. -// func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { return new(Writer).Init(output, minwidth, tabwidth, padding, padchar, flags) } diff --git a/src/text/template/doc.go b/src/text/template/doc.go index 7b3029433690c9..58cc97371b0027 100644 --- a/src/text/template/doc.go +++ b/src/text/template/doc.go @@ -18,7 +18,6 @@ structure as execution proceeds. The input text for a template is UTF-8-encoded text in any format. "Actions"--data evaluations or control structures--are delimited by "{{" and "}}"; all text outside actions is copied to the output unchanged. -Except for raw strings, actions may not span newlines, although comments can. Once parsed, a template may be executed safely in parallel, although if parallel executions share a Writer the output may be interleaved. @@ -112,6 +111,14 @@ data, defined in detail in the corresponding sections that follow. T0 is executed; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. + {{break}} + The innermost {{range pipeline}} loop is ended early, stopping the + current iteration and bypassing all remaining iterations. + + {{continue}} + The current iteration of the innermost {{range pipeline}} loop is + stopped, and the loop starts the next iteration. + {{template "name"}} The template with the specified name is executed with nil data. @@ -307,9 +314,10 @@ Predefined global functions are named as follows. and Returns the boolean AND of its arguments by returning the - first empty argument or the last argument, that is, - "and x y" behaves as "if x then y else x". All the - arguments are evaluated. + first empty argument or the last argument. That is, + "and x y" behaves as "if x then y else x." + Evaluation proceeds through the arguments left to right + and returns when the result is determined. call Returns the result of calling the first argument, which must be a function, with the remaining arguments as parameters. @@ -344,8 +352,9 @@ Predefined global functions are named as follows. or Returns the boolean OR of its arguments by returning the first non-empty argument or the last argument, that is, - "or x y" behaves as "if x then x else y". All the - arguments are evaluated. + "or x y" behaves as "if x then x else y". + Evaluation proceeds through the arguments left to right + and returns when the result is determined. print An alias for fmt.Sprint printf diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 5ad3b4ec582c41..66cb535c47f2ed 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -5,6 +5,7 @@ package template import ( + "errors" "fmt" "internal/fmtsort" "io" @@ -93,6 +94,12 @@ type missingValType struct{} var missingVal = reflect.ValueOf(missingValType{}) +var missingValReflectType = reflect.TypeOf(missingValType{}) + +func isMissing(v reflect.Value) bool { + return v.IsValid() && v.Type() == missingValReflectType +} + // at marks the state to be on node n, for error reporting. func (s *state) at(node parse.Node) { s.node = node @@ -125,7 +132,7 @@ func (e ExecError) Unwrap() error { } // errorf records an ExecError and terminates processing. -func (s *state) errorf(format string, args ...interface{}) { +func (s *state) errorf(format string, args ...any) { name := doublePercent(s.tmpl.Name()) if s.node == nil { format = fmt.Sprintf("template: %s: %s", name, format) @@ -178,7 +185,7 @@ func errRecover(errp *error) { // the output writer. // A template may be executed safely in parallel, although if parallel // executions share a Writer the output may be interleaved. -func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error { tmpl := t.Lookup(name) if tmpl == nil { return fmt.Errorf("template: no template %q associated with template %q", name, t.name) @@ -196,11 +203,11 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) // // If data is a reflect.Value, the template applies to the concrete // value that the reflect.Value holds, as in fmt.Print. -func (t *Template) Execute(wr io.Writer, data interface{}) error { +func (t *Template) Execute(wr io.Writer, data any) error { return t.execute(wr, data) } -func (t *Template) execute(wr io.Writer, data interface{}) (err error) { +func (t *Template) execute(wr io.Writer, data any) (err error) { defer errRecover(&err) value, ok := data.(reflect.Value) if !ok { @@ -243,6 +250,12 @@ func (t *Template) DefinedTemplates() string { return b.String() } +// Sentinel errors for use with panic to signal early exits from range loops. +var ( + walkBreak = errors.New("break") + walkContinue = errors.New("continue") +) + // Walk functions step through the major pieces of the template structure, // generating output as they go. func (s *state) walk(dot reflect.Value, node parse.Node) { @@ -255,7 +268,11 @@ func (s *state) walk(dot reflect.Value, node parse.Node) { if len(node.Pipe.Decl) == 0 { s.printValue(node, val) } + case *parse.BreakNode: + panic(walkBreak) case *parse.CommentNode: + case *parse.ContinueNode: + panic(walkContinue) case *parse.IfNode: s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) case *parse.ListNode: @@ -300,7 +317,7 @@ func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse. // IsTrue reports whether the value is 'true', in the sense of not the zero of its type, // and whether the value has a meaningful truth value. This is the definition of // truth used by if and other such actions. -func IsTrue(val interface{}) (truth, ok bool) { +func IsTrue(val any) (truth, ok bool) { return isTrue(reflect.ValueOf(val)) } @@ -316,7 +333,7 @@ func isTrue(val reflect.Value) (truth, ok bool) { truth = val.Bool() case reflect.Complex64, reflect.Complex128: truth = val.Complex() != 0 - case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: + case reflect.Chan, reflect.Func, reflect.Pointer, reflect.Interface: truth = !val.IsNil() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: truth = val.Int() != 0 @@ -334,6 +351,11 @@ func isTrue(val reflect.Value) (truth, ok bool) { func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { s.at(r) + defer func() { + if r := recover(); r != nil && r != walkBreak { + panic(r) + } + }() defer s.pop(s.mark()) val, _ := indirect(s.evalPipeline(dot, r.Pipe)) // mark top of stack before any variables in the body are pushed. @@ -347,8 +369,14 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { if len(r.Pipe.Decl) > 1 { s.setTopVar(2, index) } + defer s.pop(mark) + defer func() { + // Consume panic(walkContinue) + if r := recover(); r != nil && r != walkContinue { + panic(r) + } + }() s.walk(elem, r.List) - s.pop(mark) } switch val.Kind() { case reflect.Array, reflect.Slice: @@ -449,7 +477,7 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value ref } func (s *state) notAFunction(args []parse.Node, final reflect.Value) { - if len(args) > 1 || final != missingVal { + if len(args) > 1 || !isMissing(final) { s.errorf("can't give argument to non-function %s", args[0]) } } @@ -572,11 +600,11 @@ func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ide func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { s.at(node) name := node.Ident - function, ok := findFunction(name, s.tmpl) + function, isBuiltin, ok := findFunction(name, s.tmpl) if !ok { s.errorf("%q is not a defined function", name) } - return s.evalCall(dot, function, cmd, name, args, final) + return s.evalCall(dot, function, isBuiltin, cmd, name, args, final) } // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). @@ -601,22 +629,25 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, // Unless it's an interface, need to get to a value of type *T to guarantee // we see all methods of T and *T. ptr := receiver - if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() { + if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Pointer && ptr.CanAddr() { ptr = ptr.Addr() } if method := ptr.MethodByName(fieldName); method.IsValid() { - return s.evalCall(dot, method, node, fieldName, args, final) + return s.evalCall(dot, method, false, node, fieldName, args, final) } - hasArgs := len(args) > 1 || final != missingVal + hasArgs := len(args) > 1 || !isMissing(final) // It's not a method; must be a field of a struct or an element of a map. switch receiver.Kind() { case reflect.Struct: tField, ok := receiver.Type().FieldByName(fieldName) if ok { - field := receiver.FieldByIndex(tField.Index) + field, err := receiver.FieldByIndexErr(tField.Index) if !tField.IsExported() { s.errorf("%s is an unexported field of struct type %s", fieldName, typ) } + if err != nil { + s.errorf("%v", err) + } // If it's a function, we must call it. if hasArgs { s.errorf("%s has arguments but cannot be invoked as function", fieldName) @@ -643,7 +674,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, } return result } - case reflect.Ptr: + case reflect.Pointer: etyp := receiver.Type().Elem() if etyp.Kind() == reflect.Struct { if _, ok := etyp.FieldByName(fieldName); !ok { @@ -669,13 +700,13 @@ var ( // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] // as the function itself. -func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { if args != nil { args = args[1:] // Zeroth arg is function name/node; not passed to function. } typ := fun.Type() numIn := len(args) - if final != missingVal { + if !isMissing(final) { numIn++ } numFixed := len(args) @@ -691,6 +722,38 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a // TODO: This could still be a confusing error; maybe goodFunc should provide info. s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) } + + unwrap := func(v reflect.Value) reflect.Value { + if v.Type() == reflectValueType { + v = v.Interface().(reflect.Value) + } + return v + } + + // Special case for builtin and/or, which short-circuit. + if isBuiltin && (name == "and" || name == "or") { + argType := typ.In(0) + var v reflect.Value + for _, arg := range args { + v = s.evalArg(dot, argType, arg).Interface().(reflect.Value) + if truth(v) == (name == "or") { + // This value was already unwrapped + // by the .Interface().(reflect.Value). + return v + } + } + if final != missingVal { + // The last argument to and/or is coming from + // the pipeline. We didn't short circuit on an earlier + // argument, so we are going to return this one. + // We don't have to evaluate final, but we do + // have to check its type. Then, since we are + // going to return it, we have to unwrap it. + v = unwrap(s.validateType(final, argType)) + } + return v + } + // Build the arg list. argv := make([]reflect.Value, numIn) // Args must be evaluated. Fixed args first. @@ -706,7 +769,7 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a } } // Add final value if necessary. - if final != missingVal { + if !isMissing(final) { t := typ.In(typ.NumIn() - 1) if typ.IsVariadic() { if numIn-1 < numFixed { @@ -728,16 +791,13 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a s.at(node) s.errorf("error calling %s: %w", name, err) } - if v.Type() == reflectValueType { - v = v.Interface().(reflect.Value) - } - return v + return unwrap(v) } // canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. func canBeNil(typ reflect.Type) bool { switch typ.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return true case reflect.Struct: return typ == reflectValueType @@ -774,12 +834,12 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu // are much more constrained, so it makes more sense there than here. // Besides, one is almost always all you need. switch { - case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ): + case value.Kind() == reflect.Pointer && value.Type().Elem().AssignableTo(typ): value = value.Elem() if !value.IsValid() { s.errorf("dereference of nil pointer of type %s", typ) } - case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr(): + case reflect.PointerTo(value.Type()).AssignableTo(typ) && value.CanAddr(): value = value.Addr() default: s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) @@ -931,7 +991,7 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu // if it's nil. If the returned bool is true, the returned value's kind will be // either a pointer or interface. func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { - for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { + for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() { if v.IsNil() { return v, true } @@ -969,8 +1029,8 @@ func (s *state) printValue(n parse.Node, v reflect.Value) { // printableValue returns the, possibly indirected, interface value inside v that // is best for a call to formatted printer. -func printableValue(v reflect.Value) (interface{}, bool) { - if v.Kind() == reflect.Ptr { +func printableValue(v reflect.Value) (any, bool) { + if v.Kind() == reflect.Pointer { v, _ = indirect(v) // fmt.Fprint handles nil. } if !v.IsValid() { @@ -978,7 +1038,7 @@ func printableValue(v reflect.Value) (interface{}, bool) { } if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { - if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) { + if v.CanAddr() && (reflect.PointerTo(v.Type()).Implements(errorType) || reflect.PointerTo(v.Type()).Implements(fmtStringerType)) { v = v.Addr() } else { switch v.Kind() { diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index ef521645a7f5d5..6bfae3d319d8f6 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -46,7 +46,7 @@ type T struct { MSI map[string]int MSIone map[string]int // one element, for deterministic output MSIEmpty map[string]int - MXI map[interface{}]int + MXI map[any]int MII map[int]int MI32S map[int32]string MI64S map[int64]string @@ -56,11 +56,11 @@ type T struct { MUI8S map[uint8]string SMSI []map[string]int // Empty interfaces; used to see if we can dig inside one. - Empty0 interface{} // nil - Empty1 interface{} - Empty2 interface{} - Empty3 interface{} - Empty4 interface{} + Empty0 any // nil + Empty1 any + Empty2 any + Empty3 any + Empty4 any // Non-empty interfaces. NonEmptyInterface I NonEmptyInterfacePtS *I @@ -138,7 +138,7 @@ var tVal = &T{ SB: []bool{true, false}, MSI: map[string]int{"one": 1, "two": 2, "three": 3}, MSIone: map[string]int{"one": 1}, - MXI: map[interface{}]int{"one": 1}, + MXI: map[any]int{"one": 1}, MII: map[int]int{1: 1}, MI32S: map[int32]string{1: "one", 2: "two"}, MI64S: map[int64]string{2: "i642", 3: "i643"}, @@ -209,7 +209,7 @@ func (t *T) Method2(a uint16, b string) string { return fmt.Sprintf("Method2: %d %s", a, b) } -func (t *T) Method3(v interface{}) string { +func (t *T) Method3(v any) string { return fmt.Sprintf("Method3: %v", v) } @@ -249,7 +249,7 @@ func (u *U) TrueFalse(b bool) string { return "" } -func typeOf(arg interface{}) string { +func typeOf(arg any) string { return fmt.Sprintf("%T", arg) } @@ -257,7 +257,7 @@ type execTest struct { name string input string output string - data interface{} + data any ok bool } @@ -390,7 +390,7 @@ var execTests = []execTest{ {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=", tVal, true}, {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, - {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, + {"Interface Call", `{{stringer .S}}`, "foozle", map[string]any{"S": bytes.NewBufferString("foozle")}, true}, {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, {"call nil", "{{call nil}}", "", tVal, false}, @@ -481,8 +481,19 @@ var execTests = []execTest{ {"not", "{{not true}} {{not false}}", "false true", nil, true}, {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, + {"or short-circuit", "{{or 0 1 (die)}}", "1", nil, true}, + {"and short-circuit", "{{and 1 0 (die)}}", "0", nil, true}, + {"or short-circuit2", "{{or 0 0 (die)}}", "", nil, false}, + {"and short-circuit2", "{{and 1 1 (die)}}", "", nil, false}, + {"and pipe-true", "{{1 | and 1}}", "1", nil, true}, + {"and pipe-false", "{{0 | and 1}}", "0", nil, true}, + {"or pipe-true", "{{1 | or 0}}", "1", nil, true}, + {"or pipe-false", "{{0 | or 0}}", "0", nil, true}, + {"and undef", "{{and 1 .Unknown}}", "", nil, true}, + {"or undef", "{{or 0 .Unknown}}", "", nil, true}, {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, + {"boolean if pipe", "{{if true | not | and 1}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, // Indexing. {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, @@ -564,6 +575,8 @@ var execTests = []execTest{ {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true}, + {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, @@ -735,7 +748,7 @@ func add(args ...int) int { return sum } -func echo(arg interface{}) interface{} { +func echo(arg any) any { return arg } @@ -754,16 +767,17 @@ func stringer(s fmt.Stringer) string { return s.String() } -func mapOfThree() interface{} { +func mapOfThree() any { return map[string]int{"three": 3} } func testExecute(execTests []execTest, template *Template, t *testing.T) { - b := new(bytes.Buffer) + b := new(strings.Builder) funcs := FuncMap{ "add": add, "count": count, "dddArg": dddArg, + "die": func() bool { panic("die") }, "echo": echo, "makemap": makemap, "mapOfThree": mapOfThree, @@ -847,7 +861,7 @@ func TestDelims(t *testing.T) { if err != nil { t.Fatalf("delim %q text %q parse err %s", left, text, err) } - var b = new(bytes.Buffer) + var b = new(strings.Builder) err = tmpl.Execute(b, value) if err != nil { t.Fatalf("delim %q exec err %s", left, err) @@ -1010,7 +1024,7 @@ func TestTree(t *testing.T) { if err != nil { t.Fatal("parse error:", err) } - var b bytes.Buffer + var b strings.Builder const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" // First by looking up the template. err = tmpl.Lookup("tree").Execute(&b, tree) @@ -1206,33 +1220,39 @@ var cmpTests = []cmpTest{ {"eq .NilIface .Iface1", "false", true}, {"eq .NilIface 0", "false", true}, {"eq 0 .NilIface", "false", true}, + {"eq .Map .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map nil", "true", true}, // Uncomparable types but nil is OK. + {"eq nil .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map .NonNilMap", "false", true}, // Uncomparable types but nil is OK. // Errors - {"eq `xy` 1", "", false}, // Different types. - {"eq 2 2.0", "", false}, // Different types. - {"lt true true", "", false}, // Unordered types. - {"lt 1+0i 1+0i", "", false}, // Unordered types. - {"eq .Ptr 1", "", false}, // Incompatible types. - {"eq .Ptr .NegOne", "", false}, // Incompatible types. - {"eq .Map .Map", "", false}, // Uncomparable types. - {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. + {"eq .Ptr 1", "", false}, // Incompatible types. + {"eq .Ptr .NegOne", "", false}, // Incompatible types. + {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq .NonNilMap .NonNilMap", "", false}, // Uncomparable types. } func TestComparison(t *testing.T) { - b := new(bytes.Buffer) + b := new(strings.Builder) var cmpStruct = struct { Uthree, Ufour uint NegOne, Three int Ptr, NilPtr *int + NonNilMap map[int]int Map map[int]int V1, V2 V Iface1, NilIface fmt.Stringer }{ - Uthree: 3, - Ufour: 4, - NegOne: -1, - Three: 3, - Ptr: new(int), - Iface1: b, + Uthree: 3, + Ufour: 4, + NegOne: -1, + Three: 3, + Ptr: new(int), + NonNilMap: make(map[int]int), + Iface1: b, } for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) @@ -1264,7 +1284,7 @@ func TestMissingMapKey(t *testing.T) { if err != nil { t.Fatal(err) } - var b bytes.Buffer + var b strings.Builder // By default, just get "" err = tmpl.Execute(&b, data) if err != nil { @@ -1434,7 +1454,7 @@ func TestBlock(t *testing.T) { t.Fatal(err) } - var buf bytes.Buffer + var buf strings.Builder if err := tmpl.Execute(&buf, "hello"); err != nil { t.Fatal(err) } @@ -1454,7 +1474,7 @@ func TestBlock(t *testing.T) { func TestEvalFieldErrors(t *testing.T) { tests := []struct { name, src string - value interface{} + value any want string }{ { @@ -1540,7 +1560,7 @@ func TestAddrOfIndex(t *testing.T) { } for _, text := range texts { tmpl := Must(New("tmpl").Parse(text)) - var buf bytes.Buffer + var buf strings.Builder err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}})) if err != nil { t.Fatalf("%s: Execute: %v", text, err) @@ -1596,8 +1616,8 @@ func TestInterfaceValues(t *testing.T) { for _, tt := range tests { tmpl := Must(New("tmpl").Parse(tt.text)) - var buf bytes.Buffer - err := tmpl.Execute(&buf, map[string]interface{}{ + var buf strings.Builder + err := tmpl.Execute(&buf, map[string]any{ "PlusOne": func(n int) int { return n + 1 }, @@ -1626,7 +1646,7 @@ func TestInterfaceValues(t *testing.T) { // Check that panics during calls are recovered and returned as errors. func TestExecutePanicDuringCall(t *testing.T) { - funcs := map[string]interface{}{ + funcs := map[string]any{ "doPanic": func() string { panic("custom panic string") }, @@ -1634,7 +1654,7 @@ func TestExecutePanicDuringCall(t *testing.T) { tests := []struct { name string input string - data interface{} + data any wantErr string }{ { @@ -1689,7 +1709,7 @@ func TestExecutePanicDuringCall(t *testing.T) { // Issue 31810. Check that a parenthesized first argument behaves properly. func TestIssue31810(t *testing.T) { // A simple value with no arguments is fine. - var b bytes.Buffer + var b strings.Builder const text = "{{ (.) }}" tmpl, err := New("").Parse(text) if err != nil { @@ -1773,3 +1793,26 @@ func TestIssue39807(t *testing.T) { wg.Wait() } + +// Issue 48215: embedded nil pointer causes panic. +// Fixed by adding FieldByIndexErr to the reflect package. +func TestIssue48215(t *testing.T) { + type A struct { + S string + } + type B struct { + *A + } + tmpl, err := New("").Parse(`{{ .S }}`) + if err != nil { + t.Fatal(err) + } + err = tmpl.Execute(io.Discard, B{}) + // We expect an error, not a panic. + if err == nil { + t.Fatal("did not get error for nil embedded struct") + } + if !strings.Contains(err.Error(), "reflect: indirection through nil pointer to embedded struct field A") { + t.Fatal(err) + } +} diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index fff833ed29813e..dbea6e705a0243 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -5,7 +5,6 @@ package template import ( - "bytes" "errors" "fmt" "io" @@ -31,7 +30,7 @@ import ( // apply to arguments of arbitrary type can use parameters of type interface{} or // of type reflect.Value. Similarly, functions meant to return a result of arbitrary // type can return interface{} or reflect.Value. -type FuncMap map[string]interface{} +type FuncMap map[string]any // builtins returns the FuncMap. // It is not a global variable so the linker can dead code eliminate @@ -139,18 +138,18 @@ func goodName(name string) bool { } // findFunction looks for a function in the template, and global map. -func findFunction(name string, tmpl *Template) (reflect.Value, bool) { +func findFunction(name string, tmpl *Template) (v reflect.Value, isBuiltin, ok bool) { if tmpl != nil && tmpl.common != nil { tmpl.muFuncs.RLock() defer tmpl.muFuncs.RUnlock() if fn := tmpl.execFuncs[name]; fn.IsValid() { - return fn, true + return fn, false, true } } if fn := builtinFuncs()[name]; fn.IsValid() { - return fn, true + return fn, true, true } - return reflect.Value{}, false + return reflect.Value{}, false, false } // prepareArg checks if value can be used as an argument of type argType, and @@ -382,31 +381,13 @@ func truth(arg reflect.Value) bool { // and computes the Boolean AND of its arguments, returning // the first false argument it encounters, or the last argument. func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value { - if !truth(arg0) { - return arg0 - } - for i := range args { - arg0 = args[i] - if !truth(arg0) { - break - } - } - return arg0 + panic("unreachable") // implemented as a special case in evalCall } // or computes the Boolean OR of its arguments, returning // the first true argument it encounters, or the last argument. func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value { - if truth(arg0) { - return arg0 - } - for i := range args { - arg0 = args[i] - if truth(arg0) { - break - } - } - return arg0 + panic("unreachable") // implemented as a special case in evalCall } // not returns the Boolean negation of its argument. @@ -454,14 +435,33 @@ func basicKind(v reflect.Value) (kind, error) { return invalidKind, errBadComparisonType } +// isNil returns true if v is the zero reflect.Value, or nil of its type. +func isNil(v reflect.Value) bool { + if !v.IsValid() { + return true + } + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return v.IsNil() + } + return false +} + +// canCompare reports whether v1 and v2 are both the same kind, or one is nil. +// Called only when dealing with nillable types, or there's about to be an error. +func canCompare(v1, v2 reflect.Value) bool { + k1 := v1.Kind() + k2 := v2.Kind() + if k1 == k2 { + return true + } + // We know the type can be compared to nil. + return k1 == reflect.Invalid || k2 == reflect.Invalid +} + // eq evaluates the comparison a == b || a == c || ... func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { arg1 = indirectInterface(arg1) - if arg1 != zero { - if t1 := arg1.Type(); !t1.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t1, arg1) - } - } if len(arg2) == 0 { return false, errNoComparison } @@ -497,11 +497,14 @@ func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { case uintKind: truth = arg1.Uint() == arg.Uint() default: - if arg == zero || arg1 == zero { - truth = arg1 == arg + if !canCompare(arg1, arg) { + return false, fmt.Errorf("non-comparable types %s: %v, %s: %v", arg1, arg1.Type(), arg.Type(), arg) + } + if isNil(arg1) || isNil(arg) { + truth = isNil(arg) == isNil(arg1) } else { - if t2 := arg.Type(); !t2.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t2, arg) + if !arg.Type().Comparable() { + return false, fmt.Errorf("non-comparable type %s: %v", arg, arg.Type()) } truth = arg1.Interface() == arg.Interface() } @@ -638,14 +641,14 @@ func HTMLEscapeString(s string) string { if !strings.ContainsAny(s, "'\"&<>\000") { return s } - var b bytes.Buffer + var b strings.Builder HTMLEscape(&b, []byte(s)) return b.String() } // HTMLEscaper returns the escaped HTML equivalent of the textual // representation of its arguments. -func HTMLEscaper(args ...interface{}) string { +func HTMLEscaper(args ...any) string { return HTMLEscapeString(evalArgs(args)) } @@ -721,7 +724,7 @@ func JSEscapeString(s string) string { if strings.IndexFunc(s, jsIsSpecial) < 0 { return s } - var b bytes.Buffer + var b strings.Builder JSEscape(&b, []byte(s)) return b.String() } @@ -736,22 +739,24 @@ func jsIsSpecial(r rune) bool { // JSEscaper returns the escaped JavaScript equivalent of the textual // representation of its arguments. -func JSEscaper(args ...interface{}) string { +func JSEscaper(args ...any) string { return JSEscapeString(evalArgs(args)) } // URLQueryEscaper returns the escaped value of the textual representation of // its arguments in a form suitable for embedding in a URL query. -func URLQueryEscaper(args ...interface{}) string { +func URLQueryEscaper(args ...any) string { return url.QueryEscape(evalArgs(args)) } // evalArgs formats the list of arguments into a string. It is therefore equivalent to +// // fmt.Sprint(args...) +// // except that each argument is indirected (if a pointer), as required, // using the same rules as the default string evaluation during template // execution. -func evalArgs(args []interface{}) string { +func evalArgs(args []any) string { ok := false var s string // Fast path for simple common case. diff --git a/src/text/template/helper.go b/src/text/template/helper.go index 57905e613a4364..48af3928b39dbb 100644 --- a/src/text/template/helper.go +++ b/src/text/template/helper.go @@ -19,6 +19,7 @@ import ( // Must is a helper that wraps a call to a function returning (*Template, error) // and panics if the error is non-nil. It is intended for use in variable // initializations such as +// // var t = template.Must(template.New("name").Parse("text")) func Must(t *Template, err error) *Template { if err != nil { diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go index b543ab5c47c6d3..63cd3f74b270a6 100644 --- a/src/text/template/multi_test.go +++ b/src/text/template/multi_test.go @@ -7,9 +7,9 @@ package template // Tests for multiple-template parsing and execution. import ( - "bytes" "fmt" "os" + "strings" "testing" "text/template/parse" ) @@ -242,7 +242,7 @@ func TestClone(t *testing.T) { } } // Execute root. - var b bytes.Buffer + var b strings.Builder err = root.ExecuteTemplate(&b, "a", 0) if err != nil { t.Fatal(err) @@ -281,7 +281,7 @@ func TestAddParseTree(t *testing.T) { t.Fatal(err) } // Execute. - var b bytes.Buffer + var b strings.Builder err = added.ExecuteTemplate(&b, "a", 0) if err != nil { t.Fatal(err) @@ -410,7 +410,7 @@ func TestEmptyTemplate(t *testing.T) { t.Fatal(err) } } - buf := &bytes.Buffer{} + buf := &strings.Builder{} if err := m.Execute(buf, c.in); err != nil { t.Error(i, err) continue @@ -445,10 +445,20 @@ func TestIssue19294(t *testing.T) { t.Fatal(err) } } - var buf bytes.Buffer + var buf strings.Builder res.Execute(&buf, 0) if buf.String() != "stylesheet" { t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") } } } + +// Issue 48436 +func TestAddToZeroTemplate(t *testing.T) { + tree, err := parse.Parse("c", cloneText3, "", "", nil, builtins()) + if err != nil { + t.Fatal(err) + } + var tmpl Template + tmpl.AddParseTree("x", tree["c"]) +} diff --git a/src/text/template/option.go b/src/text/template/option.go index addce2d890d6d8..ea2fd80c069832 100644 --- a/src/text/template/option.go +++ b/src/text/template/option.go @@ -30,6 +30,7 @@ type option struct { // // missingkey: Control the behavior during execution if a map is // indexed with a key that is not present in the map. +// // "missingkey=default" or "missingkey=invalid" // The default behavior: Do nothing and continue execution. // If printed, the result of the index operation is the string @@ -38,7 +39,6 @@ type option struct { // The operation returns the zero value for the map type's element. // "missingkey=error" // Execution stops immediately with an error. -// func (t *Template) Option(opt ...string) *Template { t.init() for _, s := range opt { @@ -51,13 +51,11 @@ func (t *Template) setOption(opt string) { if opt == "" { panic("empty option string") } - elems := strings.Split(opt, "=") - switch len(elems) { - case 2: - // key=value - switch elems[0] { + // key=value + if key, value, ok := strings.Cut(opt, "="); ok { + switch key { case "missingkey": - switch elems[1] { + switch value { case "invalid", "default": t.option.missingKey = mapInvalid return diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go index 6784071b1118d1..29403dd947c93c 100644 --- a/src/text/template/parse/lex.go +++ b/src/text/template/parse/lex.go @@ -62,6 +62,8 @@ const ( // Keywords appear after all the rest. itemKeyword // used only to delimit the keywords itemBlock // block keyword + itemBreak // break keyword + itemContinue // continue keyword itemDot // the cursor, spelled '.' itemDefine // define keyword itemElse // else keyword @@ -76,6 +78,8 @@ const ( var key = map[string]itemType{ ".": itemDot, "block": itemBlock, + "break": itemBreak, + "continue": itemContinue, "define": itemDefine, "else": itemElse, "end": itemEnd, @@ -114,22 +118,23 @@ type lexer struct { emitComment bool // emit itemComment tokens. pos Pos // current position in the input start Pos // start position of this item - width Pos // width of last rune read from input + atEOF bool // we have hit the end of input and returned eof items chan item // channel of scanned items parenDepth int // nesting depth of ( ) exprs line int // 1+number of newlines seen startLine int // start line of this item + breakOK bool // break keyword allowed + continueOK bool // continue keyword allowed } // next returns the next rune in the input. func (l *lexer) next() rune { if int(l.pos) >= len(l.input) { - l.width = 0 + l.atEOF = true return eof } r, w := utf8.DecodeRuneInString(l.input[l.pos:]) - l.width = Pos(w) - l.pos += l.width + l.pos += Pos(w) if r == '\n' { l.line++ } @@ -143,12 +148,15 @@ func (l *lexer) peek() rune { return r } -// backup steps back one rune. Can only be called once per call of next. +// backup steps back one rune. func (l *lexer) backup() { - l.pos -= l.width - // Correct newline count. - if l.width == 1 && l.input[l.pos] == '\n' { - l.line-- + if !l.atEOF && l.pos > 0 { + r, w := utf8.DecodeLastRuneInString(l.input[:l.pos]) + l.pos -= Pos(w) + // Correct newline count. + if r == '\n' { + l.line-- + } } } @@ -184,7 +192,7 @@ func (l *lexer) acceptRun(valid string) { // errorf returns an error token and terminates the scan by passing // back a nil pointer that will be the next state, terminating l.nextItem. -func (l *lexer) errorf(format string, args ...interface{}) stateFn { +func (l *lexer) errorf(format string, args ...any) stateFn { l.items <- item{itemError, l.start, fmt.Sprintf(format, args...), l.startLine} return nil } @@ -203,7 +211,7 @@ func (l *lexer) drain() { } // lex creates a new scanner for the input string. -func lex(name, input, left, right string, emitComment bool) *lexer { +func lex(name, input, left, right string, emitComment, breakOK, continueOK bool) *lexer { if left == "" { left = leftDelim } @@ -216,6 +224,8 @@ func lex(name, input, left, right string, emitComment bool) *lexer { leftDelim: left, rightDelim: right, emitComment: emitComment, + breakOK: breakOK, + continueOK: continueOK, items: make(chan item), line: 1, startLine: 1, @@ -243,7 +253,6 @@ const ( // lexText scans until an opening action delimiter, "{{". func lexText(l *lexer) stateFn { - l.width = 0 if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 { ldn := Pos(len(l.leftDelim)) l.pos += Pos(x) @@ -461,7 +470,12 @@ Loop: } switch { case key[word] > itemKeyword: - l.emit(key[word]) + item := key[word] + if item == itemBreak && !l.breakOK || item == itemContinue && !l.continueOK { + l.emit(itemIdentifier) + } else { + l.emit(item) + } case word[0] == '.': l.emit(itemField) case word == "true", word == "false": @@ -530,13 +544,7 @@ func (l *lexer) atTerminator() bool { case eof, '.', ',', '|', ':', ')', '(': return true } - // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will - // succeed but should fail) but only in extremely rare cases caused by willfully - // bad choice of delimiter. - if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r { - return true - } - return false + return strings.HasPrefix(l.input[l.pos:], l.rightDelim) } // lexChar scans a character constant. The initial quote is already diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go index 6510eed674dd9f..c5f429667c08c3 100644 --- a/src/text/template/parse/lex_test.go +++ b/src/text/template/parse/lex_test.go @@ -35,6 +35,8 @@ var itemName = map[itemType]string{ // keywords itemDot: ".", itemBlock: "block", + itemBreak: "break", + itemContinue: "continue", itemDefine: "define", itemElse: "else", itemIf: "if", @@ -392,7 +394,7 @@ var lexTests = []lexTest{ // collect gathers the emitted items into a slice. func collect(t *lexTest, left, right string) (items []item) { - l := lex(t.name, t.input, left, right, true) + l := lex(t.name, t.input, left, right, true, true, true) for { item := l.nextItem() items = append(items, item) @@ -467,6 +469,22 @@ func TestDelims(t *testing.T) { } } +func TestDelimsAlphaNumeric(t *testing.T) { + test := lexTest{"right delimiter with alphanumeric start", "{{hub .host hub}}", []item{ + mkItem(itemLeftDelim, "{{hub"), + mkItem(itemSpace, " "), + mkItem(itemField, ".host"), + mkItem(itemSpace, " "), + mkItem(itemRightDelim, "hub}}"), + tEOF, + }} + items := collect(&test, "{{hub", "hub}}") + + if !equal(items, test.items, false) { + t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + } +} + var lexPosTests = []lexTest{ {"empty", "", []item{{itemEOF, 0, "", 1}}}, {"punctuation", "{{,@%#}}", []item{ @@ -532,7 +550,7 @@ func TestPos(t *testing.T) { func TestShutdown(t *testing.T) { // We need to duplicate template.Parse here to hold on to the lexer. const text = "erroneous{{define}}{{else}}1234" - lexer := lex("foo", text, "{{", "}}", false) + lexer := lex("foo", text, "{{", "}}", false, true, true) _, err := New("root").parseLexer(lexer) if err == nil { t.Fatalf("expected error") diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go index 177482f9b26059..47268225c8ca1c 100644 --- a/src/text/template/parse/node.go +++ b/src/text/template/parse/node.go @@ -71,6 +71,8 @@ const ( NodeVariable // A $ variable. NodeWith // A with action. NodeComment // A comment. + NodeBreak // A break action. + NodeContinue // A continue action. ) // Nodes. @@ -907,6 +909,40 @@ func (i *IfNode) Copy() Node { return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) } +// BreakNode represents a {{break}} action. +type BreakNode struct { + tr *Tree + NodeType + Pos + Line int +} + +func (t *Tree) newBreak(pos Pos, line int) *BreakNode { + return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line} +} + +func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) } +func (b *BreakNode) String() string { return "{{break}}" } +func (b *BreakNode) tree() *Tree { return b.tr } +func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") } + +// ContinueNode represents a {{continue}} action. +type ContinueNode struct { + tr *Tree + NodeType + Pos + Line int +} + +func (t *Tree) newContinue(pos Pos, line int) *ContinueNode { + return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line} +} + +func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) } +func (c *ContinueNode) String() string { return "{{continue}}" } +func (c *ContinueNode) tree() *Tree { return c.tr } +func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") } + // RangeNode represents a {{range}} action and its commands. type RangeNode struct { BranchNode diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go index 1a63961c13ecce..00c258ad5deab6 100644 --- a/src/text/template/parse/parse.go +++ b/src/text/template/parse/parse.go @@ -24,14 +24,14 @@ type Tree struct { Mode Mode // parsing mode. text string // text parsed to create the template (or its parent) // Parsing only; cleared after parse. - funcs []map[string]interface{} + funcs []map[string]any lex *lexer token [3]item // three-token lookahead for parser. peekCount int vars []string // variables defined at the moment. treeSet map[string]*Tree actionLine int // line of left delim starting action - mode Mode + rangeDepth int } // A mode value is a set of flags (or 0). Modes control parser behavior. @@ -59,7 +59,7 @@ func (t *Tree) Copy() *Tree { // templates described in the argument string. The top-level template will be // given the specified name. If an error is encountered, parsing stops and an // empty map is returned with the error. -func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (map[string]*Tree, error) { +func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]any) (map[string]*Tree, error) { treeSet := make(map[string]*Tree) t := New(name) t.text = text @@ -128,7 +128,7 @@ func (t *Tree) peekNonSpace() item { // Parsing. // New allocates a new parse tree with the given name. -func New(name string, funcs ...map[string]interface{}) *Tree { +func New(name string, funcs ...map[string]any) *Tree { return &Tree{ Name: name, funcs: funcs, @@ -158,7 +158,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) { } // errorf formats the error and terminates processing. -func (t *Tree) errorf(format string, args ...interface{}) { +func (t *Tree) errorf(format string, args ...any) { t.Root = nil format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.token[0].line, format) panic(fmt.Errorf(format, args...)) @@ -218,7 +218,7 @@ func (t *Tree) recover(errp *error) { } // startParse initializes the parser, using the lexer. -func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet map[string]*Tree) { +func (t *Tree) startParse(funcs []map[string]any, lex *lexer, treeSet map[string]*Tree) { t.Root = nil t.lex = lex t.vars = []string{"$"} @@ -238,11 +238,14 @@ func (t *Tree) stopParse() { // the template for execution. If either action delimiter string is empty, the // default ("{{" or "}}") is used. Embedded template definitions are added to // the treeSet map. -func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { +func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]any) (tree *Tree, err error) { defer t.recover(&err) t.ParseName = t.Name emitComment := t.Mode&ParseComments != 0 - t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim, emitComment), treeSet) + breakOK := !t.hasFunction("break") + continueOK := !t.hasFunction("continue") + lexer := lex(t.Name, text, leftDelim, rightDelim, emitComment, breakOK, continueOK) + t.startParse(funcs, lexer, treeSet) t.text = text t.parse() t.add() @@ -339,7 +342,9 @@ func (t *Tree) parseDefinition() { } // itemList: +// // textOrAction* +// // Terminates at {{end}} or {{else}}, returned separately. func (t *Tree) itemList() (list *ListNode, next Node) { list = t.newList(t.peekNonSpace().pos) @@ -356,6 +361,7 @@ func (t *Tree) itemList() (list *ListNode, next Node) { } // textOrAction: +// // text | comment | action func (t *Tree) textOrAction() Node { switch token := t.nextNonSpace(); token.typ { @@ -378,14 +384,20 @@ func (t *Tree) clearActionLine() { } // Action: +// // control // command ("|" command)* +// // Left delim is past. Now get actions. // First word could be a keyword such as range. func (t *Tree) action() (n Node) { switch token := t.nextNonSpace(); token.typ { case itemBlock: return t.blockControl() + case itemBreak: + return t.breakControl(token.pos, token.line) + case itemContinue: + return t.continueControl(token.pos, token.line) case itemElse: return t.elseControl() case itemEnd: @@ -405,7 +417,38 @@ func (t *Tree) action() (n Node) { return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim)) } +// Break: +// +// {{break}} +// +// Break keyword is past. +func (t *Tree) breakControl(pos Pos, line int) Node { + if token := t.nextNonSpace(); token.typ != itemRightDelim { + t.unexpected(token, "{{break}}") + } + if t.rangeDepth == 0 { + t.errorf("{{break}} outside {{range}}") + } + return t.newBreak(pos, line) +} + +// Continue: +// +// {{continue}} +// +// Continue keyword is past. +func (t *Tree) continueControl(pos Pos, line int) Node { + if token := t.nextNonSpace(); token.typ != itemRightDelim { + t.unexpected(token, "{{continue}}") + } + if t.rangeDepth == 0 { + t.errorf("{{continue}} outside {{range}}") + } + return t.newContinue(pos, line) +} + // Pipeline: +// // declarations? command ('|' command)* func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) { token := t.peekNonSpace() @@ -480,8 +523,14 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) { func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { defer t.popVars(len(t.vars)) pipe = t.pipeline(context, itemRightDelim) + if context == "range" { + t.rangeDepth++ + } var next Node list, next = t.itemList() + if context == "range" { + t.rangeDepth-- + } switch next.Type() { case nodeEnd: //done case nodeElse: @@ -511,38 +560,49 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int } // If: +// // {{if pipeline}} itemList {{end}} // {{if pipeline}} itemList {{else}} itemList {{end}} +// // If keyword is past. func (t *Tree) ifControl() Node { return t.newIf(t.parseControl(true, "if")) } // Range: +// // {{range pipeline}} itemList {{end}} // {{range pipeline}} itemList {{else}} itemList {{end}} +// // Range keyword is past. func (t *Tree) rangeControl() Node { - return t.newRange(t.parseControl(false, "range")) + r := t.newRange(t.parseControl(false, "range")) + return r } // With: +// // {{with pipeline}} itemList {{end}} // {{with pipeline}} itemList {{else}} itemList {{end}} +// // If keyword is past. func (t *Tree) withControl() Node { return t.newWith(t.parseControl(false, "with")) } // End: +// // {{end}} +// // End keyword is past. func (t *Tree) endControl() Node { return t.newEnd(t.expect(itemRightDelim, "end").pos) } // Else: +// // {{else}} +// // Else keyword is past. func (t *Tree) elseControl() Node { // Special case for "else if". @@ -556,7 +616,9 @@ func (t *Tree) elseControl() Node { } // Block: +// // {{block stringValue pipeline}} +// // Block keyword is past. // The name must be something that can evaluate to a string. // The pipeline is mandatory. @@ -584,7 +646,9 @@ func (t *Tree) blockControl() Node { } // Template: +// // {{template stringValue pipeline}} +// // Template keyword is past. The name must be something that can evaluate // to a string. func (t *Tree) templateControl() Node { @@ -615,7 +679,9 @@ func (t *Tree) parseTemplateName(token item, context string) (name string) { } // command: +// // operand (space operand)* +// // space-separated arguments up to a pipeline character or right delimiter. // we consume the pipe character but leave the right delim to terminate the action. func (t *Tree) command() *CommandNode { @@ -645,7 +711,9 @@ func (t *Tree) command() *CommandNode { } // operand: +// // term .Field* +// // An operand is a space-separated component of a command, // a term possibly followed by field accesses. // A nil return means the next item is not an operand. @@ -679,12 +747,14 @@ func (t *Tree) operand() Node { } // term: +// // literal (number, string, nil, boolean) // function (identifier) // . // .Field // $ // '(' pipeline ')' +// // A term is a simple "expression". // A nil return means the next item is not a term. func (t *Tree) term() Node { diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go index 9b1be272e573d9..fdb25d78f5510d 100644 --- a/src/text/template/parse/parse_test.go +++ b/src/text/template/parse/parse_test.go @@ -230,6 +230,10 @@ var parseTests = []parseTest{ `{{range $x := .SI}}{{.}}{{end}}`}, {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, `{{range $x, $y := .SI}}{{.}}{{end}}`}, + {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError, + `{{range .SI}}{{.}}{{break}}{{end}}`}, + {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError, + `{{range .SI}}{{.}}{{continue}}{{end}}`}, {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, {"template", "{{template `x`}}", noError, @@ -256,6 +260,10 @@ var parseTests = []parseTest{ {"newline in pipeline", "{{\n\"x\"\n|\nprintf\n}}", noError, `{{"x" | printf}}`}, {"newline in comment", "{{/*\nhello\n*/}}", noError, ""}, {"newline in comment", "{{-\n/*\nhello\n*/\n-}}", noError, ""}, + {"spaces around continue", "{{range .SI}}{{.}}{{ continue }}{{end}}", noError, + `{{range .SI}}{{.}}{{continue}}{{end}}`}, + {"spaces around break", "{{range .SI}}{{.}}{{ break }}{{end}}", noError, + `{{range .SI}}{{.}}{{break}}{{end}}`}, // Errors. {"unclosed action", "hello{{range", hasError, ""}, @@ -279,6 +287,10 @@ var parseTests = []parseTest{ {"adjacent args", "{{printf 3`x`}}", hasError, ""}, {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, + {"break outside range", "{{range .}}{{end}} {{break}}", hasError, ""}, + {"continue outside range", "{{range .}}{{end}} {{continue}}", hasError, ""}, + {"break in range else", "{{range .}}{{else}}{{break}}{{end}}", hasError, ""}, + {"continue in range else", "{{range .}}{{else}}{{continue}}{{end}}", hasError, ""}, // Other kinds of assignments and operators aren't available yet. {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, {"bug0b", "{{$x += 1}}{{$x}}", hasError, ""}, @@ -310,7 +322,7 @@ var parseTests = []parseTest{ {"block definition", `{{block "foo"}}hello{{end}}`, hasError, ""}, } -var builtins = map[string]interface{}{ +var builtins = map[string]any{ "printf": fmt.Sprintf, "contains": strings.Contains, } diff --git a/src/text/template/template.go b/src/text/template/template.go index fd74d45e9b1c6d..776be9cd075d37 100644 --- a/src/text/template/template.go +++ b/src/text/template/template.go @@ -127,9 +127,9 @@ func (t *Template) copy(c *common) *Template { // its definition. If it has been defined and already has that name, the existing // definition is replaced; otherwise a new template is created, defined, and returned. func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { + t.init() t.muTmpl.Lock() defer t.muTmpl.Unlock() - t.init() nt := t if name != t.name { nt = t.New(name) diff --git a/src/time/embed.go b/src/time/embed.go index 34490c859ddb51..2a9821baa91959 100644 --- a/src/time/embed.go +++ b/src/time/embed.go @@ -6,7 +6,6 @@ // the binary. //go:build timetzdata -// +build timetzdata package time diff --git a/src/time/example_test.go b/src/time/example_test.go index 0afb18aba6b4e5..059c6310a63511 100644 --- a/src/time/example_test.go +++ b/src/time/example_test.go @@ -212,6 +212,11 @@ func ExampleTime_Format() { panic(err) } + tz, err := time.LoadLocation("Asia/Shanghai") + if err != nil { // Always check errors even if they should not happen. + panic(err) + } + // time.Time's Stringer method is useful without any format. fmt.Println("default format:", t) @@ -221,6 +226,10 @@ func ExampleTime_Format() { // The time zone attached to the time value affects its output. fmt.Println("Same, in UTC:", t.UTC().Format(time.UnixDate)) + fmt.Println("in Shanghai with seconds:", t.In(tz).Format("2006-01-02T15:04:05 -070000")) + + fmt.Println("in Shanghai with colon seconds:", t.In(tz).Format("2006-01-02T15:04:05 -07:00:00")) + // The rest of this function demonstrates the properties of the // layout string used in the format. @@ -286,6 +295,8 @@ func ExampleTime_Format() { // default format: 2015-02-25 11:06:39 -0800 PST // Unix format: Wed Feb 25 11:06:39 PST 2015 // Same, in UTC: Wed Feb 25 19:06:39 UTC 2015 + //in Shanghai with seconds: 2015-02-26T03:06:39 +080000 + //in Shanghai with colon seconds: 2015-02-26T03:06:39 +08:00:00 // // Formats: // @@ -344,6 +355,23 @@ func ExampleTime_Format_pad() { } +func ExampleTime_GoString() { + t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Println(t.GoString()) + t = t.Add(1 * time.Minute) + fmt.Println(t.GoString()) + t = t.AddDate(0, 1, 0) + fmt.Println(t.GoString()) + t, _ = time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Feb 3, 2013 at 7:54pm (UTC)") + fmt.Println(t.GoString()) + + // Output: + // time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + // time.Date(2009, time.November, 10, 23, 1, 0, 0, time.UTC) + // time.Date(2009, time.December, 10, 23, 1, 0, 0, time.UTC) + // time.Date(2013, time.February, 3, 19, 54, 0, 0, time.UTC) +} + func ExampleParse() { // See the example for Time.Format for a thorough description of how // to define the layout string to parse a time.Time value; Parse and @@ -401,6 +429,39 @@ func ExampleParseInLocation() { // 2012-07-09 00:00:00 +0200 CEST } +func ExampleUnix() { + unixTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Println(unixTime.Unix()) + t := time.Unix(unixTime.Unix(), 0).UTC() + fmt.Println(t) + + // Output: + // 1257894000 + // 2009-11-10 23:00:00 +0000 UTC +} + +func ExampleUnixMicro() { + umt := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Println(umt.UnixMicro()) + t := time.UnixMicro(umt.UnixMicro()).UTC() + fmt.Println(t) + + // Output: + // 1257894000000000 + // 2009-11-10 23:00:00 +0000 UTC +} + +func ExampleUnixMilli() { + umt := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Println(umt.UnixMilli()) + t := time.UnixMilli(umt.UnixMilli()).UTC() + fmt.Println(t) + + // Output: + // 1257894000000 + // 2009-11-10 23:00:00 +0000 UTC +} + func ExampleTime_Unix() { // 1 billion seconds of Unix, three ways. fmt.Println(time.Unix(1e9, 0).UTC()) // 1e9 seconds diff --git a/src/time/export_android_test.go b/src/time/export_android_test.go index f80e7da717660f..17e021923cc45a 100644 --- a/src/time/export_android_test.go +++ b/src/time/export_android_test.go @@ -4,9 +4,13 @@ package time -func ForceAndroidTzdataForTest(tzdata bool) { - forceZipFileForTesting(false) - if tzdata { - zoneSources = zoneSources[:len(zoneSources)-1] +func ForceAndroidTzdataForTest() (undo func()) { + allowGorootSource = false + origLoadFromEmbeddedTZData := loadFromEmbeddedTZData + loadFromEmbeddedTZData = nil + + return func() { + allowGorootSource = true + loadFromEmbeddedTZData = origLoadFromEmbeddedTZData } } diff --git a/src/time/export_test.go b/src/time/export_test.go index 9baad60a92aaf3..afe1560dead5b2 100644 --- a/src/time/export_test.go +++ b/src/time/export_test.go @@ -28,7 +28,8 @@ func ResetZoneinfoForTesting() { } var ( - ForceZipFileForTesting = forceZipFileForTesting + DisablePlatformSources = disablePlatformSources + GorootZoneSource = gorootZoneSource ParseTimeZone = parseTimeZone SetMono = (*Time).setMono GetMono = (*Time).mono @@ -131,3 +132,6 @@ var StdChunkNames = map[int]string{ } var Quote = quote + +var AppendFormatAny = Time.appendFormat +var AppendFormatRFC3339 = Time.appendFormatRFC3339 diff --git a/src/time/format.go b/src/time/format.go index bb173a21c2d458..6d5da323dc1cbf 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -8,12 +8,16 @@ import "errors" // These are predefined layouts for use in Time.Format and time.Parse. // The reference time used in these layouts is the specific time stamp: +// // 01/02 03:04:05PM '06 -0700 +// // (January 2, 15:04:05, 2006, in time zone seven hours west of GMT). // That value is recorded as the constant named Layout, listed below. As a Unix // time, this is 1136239445. Since MST is GMT-0700, the reference would be // printed by the Unix date command as: +// // Mon Jan 2 15:04:05 MST 2006 +// // It is a regrettable historic error that the date uses the American convention // of putting the numerical month before the day. // @@ -49,37 +53,44 @@ import "errors" // verbatim in the input to Parse. // // Year: "2006" "06" -// Month: "Jan" "January" -// Textual day of the week: "Mon" "Monday" -// Numeric day of the month: "2" "_2" "02" -// Numeric day of the year: "__2" "002" +// Month: "Jan" "January" "01" "1" +// Day of the week: "Mon" "Monday" +// Day of the month: "2" "_2" "02" +// Day of the year: "__2" "002" // Hour: "15" "3" "03" (PM or AM) // Minute: "4" "04" // Second: "5" "05" // AM/PM mark: "PM" // // Numeric time zone offsets format as follows: -// "-0700" ±hhmm -// "-07:00" ±hh:mm -// "-07" ±hh +// +// "-0700" ±hhmm +// "-07:00" ±hh:mm +// "-07" ±hh +// "-070000" ±hhmmss +// "-07:00:00" ±hh:mm:ss +// // Replacing the sign in the format with a Z triggers // the ISO 8601 behavior of printing Z instead of an // offset for the UTC zone. Thus: -// "Z0700" Z or ±hhmm -// "Z07:00" Z or ±hh:mm -// "Z07" Z or ±hh +// +// "Z0700" Z or ±hhmm +// "Z07:00" Z or ±hh:mm +// "Z07" Z or ±hh +// "Z070000" Z or ±hhmmss +// "Z07:00:00" Z or ±hh:mm:ss // // Within the format string, the underscores in "_2" and "__2" represent spaces // that may be replaced by digits if the following number has multiple digits, // for compatibility with fixed-width Unix time formats. A leading zero represents // a zero-padded value. // -// The formats and 002 are space-padded and zero-padded +// The formats __2 and 002 are space-padded and zero-padded // three-character day of year; there is no unpadded day of year format. // -// A decimal point followed by one or more zeros represents a fractional -// second, printed to the given number of decimal places. -// Either a comma or decimal point followed by one or more nines represents +// A comma or decimal point followed by one or more zeros represents +// a fractional second, printed to the given number of decimal places. +// A comma or decimal point followed by one or more nines represents // a fractional second, printed to the given number of decimal places, with // trailing zeros removed. // For example "15:04:05,000" or "15:04:05.000" formats or parses with @@ -87,7 +98,6 @@ import "errors" // // Some valid layouts are invalid time values for time.Parse, due to formats // such as _ for space padding and Z for zone information. -// const ( Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order. ANSIC = "Mon Jan _2 15:04:05 2006" @@ -106,6 +116,9 @@ const ( StampMilli = "Jan _2 15:04:05.000" StampMicro = "Jan _2 15:04:05.000000" StampNano = "Jan _2 15:04:05.000000000" + DateTime = "2006-01-02 15:04:05" + DateOnly = "2006-01-02" + TimeOnly = "15:04:05" ) const ( @@ -146,10 +159,11 @@ const ( stdFracSecond0 // ".0", ".00", ... , trailing zeros included stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted - stdNeedDate = 1 << 8 // need month, day, year - stdNeedClock = 2 << 8 // need hour, minute, second - stdArgShift = 16 // extra argument in high bits, above low stdArgShift - stdMask = 1<> stdArgShift) & 0xfff +} + +func separator(std int) byte { + if (std >> stdSeparatorShift) == 0 { + return '.' + } + return ',' +} + // formatNano appends a fractional second, as nanoseconds, to b // and returns the result. -func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { +func formatNano(b []byte, nanosec uint, std int) []byte { + var ( + n = digitsLen(std) + separator = separator(std) + trim = std&stdMask == stdFracSecond9 + ) u := nanosec var buf [9]byte for start := len(buf); start > 0; { @@ -452,11 +493,12 @@ func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { return b } } - b = append(b, '.') + b = append(b, separator) return append(b, buf[:n]...) } // String returns the time formatted using the format string +// // "2006-01-02 15:04:05.999999999 -0700 MST" // // If the time has a monotonic clock reading, the returned string @@ -479,7 +521,7 @@ func (t Time) String() string { } m1, m2 := m2/1e9, m2%1e9 m0, m1 := m1/1e9, m1%1e9 - var buf []byte + buf := make([]byte, 0, 24) buf = append(buf, " m="...) buf = append(buf, sign) wid := 0 @@ -498,25 +540,29 @@ func (t Time) String() string { // GoString implements fmt.GoStringer and formats t to be printed in Go source // code. func (t Time) GoString() string { - buf := []byte("time.Date(") - buf = appendInt(buf, t.Year(), 0) - month := t.Month() + abs := t.abs() + year, month, day, _ := absDate(abs, true) + hour, minute, second := absClock(abs) + + buf := make([]byte, 0, len("time.Date(9999, time.September, 31, 23, 59, 59, 999999999, time.Local)")) + buf = append(buf, "time.Date("...) + buf = appendInt(buf, year, 0) if January <= month && month <= December { buf = append(buf, ", time."...) - buf = append(buf, t.Month().String()...) + buf = append(buf, longMonthNames[month-1]...) } else { // It's difficult to construct a time.Time with a date outside the // standard range but we might as well try to handle the case. buf = appendInt(buf, int(month), 0) } buf = append(buf, ", "...) - buf = appendInt(buf, t.Day(), 0) + buf = appendInt(buf, day, 0) buf = append(buf, ", "...) - buf = appendInt(buf, t.Hour(), 0) + buf = appendInt(buf, hour, 0) buf = append(buf, ", "...) - buf = appendInt(buf, t.Minute(), 0) + buf = appendInt(buf, minute, 0) buf = append(buf, ", "...) - buf = appendInt(buf, t.Second(), 0) + buf = appendInt(buf, second, 0) buf = append(buf, ", "...) buf = appendInt(buf, t.Nanosecond(), 0) buf = append(buf, ", "...) @@ -542,8 +588,8 @@ func (t Time) GoString() string { // Of these, Location(loc.name) is the least disruptive. This is an edge // case we hope not to hit too often. buf = append(buf, `time.Location(`...) - buf = append(buf, []byte(quote(loc.name))...) - buf = append(buf, `)`...) + buf = append(buf, quote(loc.name)...) + buf = append(buf, ')') } buf = append(buf, ')') return string(buf) @@ -572,6 +618,17 @@ func (t Time) Format(layout string) string { // AppendFormat is like Format but appends the textual // representation to b and returns the extended buffer. func (t Time) AppendFormat(b []byte, layout string) []byte { + switch layout { + case RFC3339: + return t.appendFormatRFC3339(b, false) + case RFC3339Nano: + return t.appendFormatRFC3339(b, true) + default: + return t.appendFormat(b, layout) + } +} + +func (t Time) appendFormat(b []byte, layout string) []byte { var ( name, offset, abs = t.locabs() @@ -583,6 +640,7 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { min int sec int ) + // Each iteration generates one std value. for layout != "" { prefix, std, suffix := nextStdChunk(layout) @@ -732,12 +790,56 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { b = appendInt(b, zone/60, 2) b = appendInt(b, zone%60, 2) case stdFracSecond0, stdFracSecond9: - b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9) + b = formatNano(b, uint(t.Nanosecond()), std) } } return b } +func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte { + _, offset, abs := t.locabs() + + // Format date. + year, month, day, _ := absDate(abs, true) + b = appendInt(b, year, 4) + b = append(b, '-') + b = appendInt(b, int(month), 2) + b = append(b, '-') + b = appendInt(b, day, 2) + + b = append(b, 'T') + + // Format time. + hour, min, sec := absClock(abs) + b = appendInt(b, hour, 2) + b = append(b, ':') + b = appendInt(b, min, 2) + b = append(b, ':') + b = appendInt(b, sec, 2) + + if nanos { + std := stdFracSecond(stdFracSecond9, 9, '.') + b = formatNano(b, uint(t.Nanosecond()), std) + } + + if offset == 0 { + return append(b, 'Z') + } + + // Format zone. + zone := offset / 60 // convert to minutes + if zone < 0 { + b = append(b, '-') + zone = -zone + } else { + b = append(b, '+') + } + b = appendInt(b, zone/60, 2) + b = append(b, ':') + b = appendInt(b, zone%60, 2) + return b +} + var errBad = errors.New("bad value for field") // placeholder not passed to user // ParseError describes a problem parsing a time string. @@ -885,6 +987,7 @@ func skip(value, prefix string) (string, error) { // field immediately after the seconds field, even if the layout does not // signify its presence. In that case either a comma or a decimal point // followed by a maximal series of digits is parsed as a fractional second. +// Fractional seconds are truncated to nanosecond precision. // // Elements omitted from the layout are assumed to be zero or, when // zero is impossible, one, so parsing "3:04pm" returns the time @@ -1037,6 +1140,9 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) } case stdSecond, stdZeroSecond: sec, value, err = getnum(value, std == stdZeroSecond) + if err != nil { + break + } if sec < 0 || 60 <= sec { rangeErrString = "second" break @@ -1132,12 +1238,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:] } var hr, mm, ss int - hr, err = atoi(hour) + hr, _, err = getnum(hour, true) if err == nil { - mm, err = atoi(min) + mm, _, err = getnum(min, true) } if err == nil { - ss, err = atoi(seconds) + ss, _, err = getnum(seconds, true) } zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds switch sign[0] { @@ -1164,7 +1270,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) case stdFracSecond0: // stdFracSecond0 requires the exact number of digits as specified in // the layout. - ndigit := 1 + (std >> stdArgShift) + ndigit := 1 + digitsLen(std) if len(value) < ndigit { err = errBad break @@ -1180,7 +1286,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) // Take any number of digits, even more than asked for, // because it is what the stdSecond case would do. i := 0 - for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { + for i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { i++ } nsec, rangeErrString, err = parseNanoseconds(value, 1+i) @@ -1373,10 +1479,7 @@ func parseSignedOffset(value string) int { if err != nil || value[1:] == rem { return 0 } - if sign == '-' { - x = -x - } - if x < -23 || 23 < x { + if x > 23 { return 0 } return len(value) - len(rem) @@ -1391,16 +1494,19 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err = errBad return } + if nbytes > 10 { + value = value[:10] + nbytes = 10 + } if ns, err = atoi(value[1:nbytes]); err != nil { return } - if ns < 0 || 1e9 <= ns { + if ns < 0 { rangeErrString = "fractional second" return } // We need nanoseconds, which means scaling by the number - // of missing digits in the format, maximum length 10. If it's - // longer than 10, we won't scale. + // of missing digits in the format, maximum length 10. scaleDigits := 10 - nbytes for i := 0; i < scaleDigits; i++ { ns *= 10 @@ -1411,19 +1517,19 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, var errLeadingInt = errors.New("time: bad [0-9]*") // never printed // leadingInt consumes the leading [0-9]* from s. -func leadingInt(s string) (x int64, rem string, err error) { +func leadingInt(s string) (x uint64, rem string, err error) { i := 0 for ; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } - if x > (1<<63-1)/10 { + if x > 1<<63/10 { // overflow return 0, "", errLeadingInt } - x = x*10 + int64(c) - '0' - if x < 0 { + x = x*10 + uint64(c) - '0' + if x > 1<<63 { // overflow return 0, "", errLeadingInt } @@ -1434,7 +1540,7 @@ func leadingInt(s string) (x int64, rem string, err error) { // leadingFraction consumes the leading [0-9]* from s. // It is used only for fractions, so does not return an error on overflow, // it just stops accumulating precision. -func leadingFraction(s string) (x int64, scale float64, rem string) { +func leadingFraction(s string) (x uint64, scale float64, rem string) { i := 0 scale = 1 overflow := false @@ -1451,8 +1557,8 @@ func leadingFraction(s string) (x int64, scale float64, rem string) { overflow = true continue } - y := x*10 + int64(c) - '0' - if y < 0 { + y := x*10 + uint64(c) - '0' + if y > 1<<63 { overflow = true continue } @@ -1462,15 +1568,15 @@ func leadingFraction(s string) (x int64, scale float64, rem string) { return x, scale, s[i:] } -var unitMap = map[string]int64{ - "ns": int64(Nanosecond), - "us": int64(Microsecond), - "µs": int64(Microsecond), // U+00B5 = micro symbol - "μs": int64(Microsecond), // U+03BC = Greek letter mu - "ms": int64(Millisecond), - "s": int64(Second), - "m": int64(Minute), - "h": int64(Hour), +var unitMap = map[string]uint64{ + "ns": uint64(Nanosecond), + "us": uint64(Microsecond), + "µs": uint64(Microsecond), // U+00B5 = micro symbol + "μs": uint64(Microsecond), // U+03BC = Greek letter mu + "ms": uint64(Millisecond), + "s": uint64(Second), + "m": uint64(Minute), + "h": uint64(Hour), } // ParseDuration parses a duration string. @@ -1481,7 +1587,7 @@ var unitMap = map[string]int64{ func ParseDuration(s string) (Duration, error) { // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ orig := s - var d int64 + var d uint64 neg := false // Consume [-+]? @@ -1501,7 +1607,7 @@ func ParseDuration(s string) (Duration, error) { } for s != "" { var ( - v, f int64 // integers before, after decimal point + v, f uint64 // integers before, after decimal point scale float64 = 1 // value = v + f/scale ) @@ -1549,7 +1655,7 @@ func ParseDuration(s string) (Duration, error) { if !ok { return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig)) } - if v > (1<<63-1)/unit { + if v > 1<<63/unit { // overflow return 0, errors.New("time: invalid duration " + quote(orig)) } @@ -1557,21 +1663,22 @@ func ParseDuration(s string) (Duration, error) { if f > 0 { // float64 is needed to be nanosecond accurate for fractions of hours. // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) - v += int64(float64(f) * (float64(unit) / scale)) - if v < 0 { + v += uint64(float64(f) * (float64(unit) / scale)) + if v > 1<<63 { // overflow return 0, errors.New("time: invalid duration " + quote(orig)) } } d += v - if d < 0 { - // overflow + if d > 1<<63 { return 0, errors.New("time: invalid duration " + quote(orig)) } } - if neg { - d = -d + return -Duration(d), nil + } + if d > 1<<63-1 { + return 0, errors.New("time: invalid duration " + quote(orig)) } return Duration(d), nil } diff --git a/src/time/format_test.go b/src/time/format_test.go index 1af41e2dfb315a..4880e1703c1525 100644 --- a/src/time/format_test.go +++ b/src/time/format_test.go @@ -5,7 +5,9 @@ package time_test import ( + "bytes" "fmt" + "math" "strconv" "strings" "testing" @@ -115,7 +117,17 @@ var formatTests = []FormatTest{ {"StampMilli", StampMilli, "Feb 4 21:00:57.012"}, {"StampMicro", StampMicro, "Feb 4 21:00:57.012345"}, {"StampNano", StampNano, "Feb 4 21:00:57.012345600"}, + {"DateTime", DateTime, "2009-02-04 21:00:57"}, + {"DateOnly", DateOnly, "2009-02-04"}, + {"TimeOnly", TimeOnly, "21:00:57"}, {"YearDay", "Jan 2 002 __2 2", "Feb 4 035 35 4"}, + {"Year", "2006 6 06 _6 __6 ___6", "2009 6 09 _6 __6 ___6"}, + {"Month", "Jan January 1 01 _1", "Feb February 2 02 _2"}, + {"DayOfMonth", "2 02 _2 __2", "4 04 4 35"}, + {"DayOfWeek", "Mon Monday", "Wed Wednesday"}, + {"Hour", "15 3 03 _3", "21 9 09 _9"}, + {"Minute", "4 04 _4", "0 00 _0"}, + {"Second", "5 05 _5", "57 57 _57"}, } func TestFormat(t *testing.T) { @@ -408,8 +420,8 @@ func TestParseInLocation(t *testing.T) { } func TestLoadLocationZipFile(t *testing.T) { - ForceZipFileForTesting(true) - defer ForceZipFileForTesting(false) + undo := DisablePlatformSources() + defer undo() _, err := LoadLocation("Australia/Sydney") if err != nil { @@ -581,6 +593,8 @@ var parseErrorTests = []ParseErrorTest{ {RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: "_abc"`}, // invalid second followed by optional fractional seconds {RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"}, + // issue 54569 + {RFC3339, "0000-01-01T00:00:.0+00:00", `parsing time "0000-01-01T00:00:.0+00:00" as "2006-01-02T15:04:05Z07:00": cannot parse ".0+00:00" as "05"`}, // issue 21113 {"_2 Jan 06 15:04 MST", "4 --- 00 00:00 GMT", "cannot parse"}, {"_2 January 06 15:04 MST", "4 --- 00 00:00 GMT", "cannot parse"}, @@ -592,6 +606,10 @@ var parseErrorTests = []ParseErrorTest{ // issue 45391. {`"2006-01-02T15:04:05Z07:00"`, "0", `parsing time "0" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "0" as "\""`}, {RFC3339, "\"", `parsing time "\"" as "2006-01-02T15:04:05Z07:00": cannot parse "\"" as "2006"`}, + + // issue 54570 + {RFC3339, "0000-01-01T00:00:00+00:+0", `parsing time "0000-01-01T00:00:00+00:+0" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "Z07:00"`}, + {RFC3339, "0000-01-01T00:00:00+-0:00", `parsing time "0000-01-01T00:00:00+-0:00" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "Z07:00"`}, } func TestParseErrors(t *testing.T) { @@ -832,3 +850,108 @@ func TestQuote(t *testing.T) { } } + +// Issue 48037 +func TestFormatFractionalSecondSeparators(t *testing.T) { + tests := []struct { + s, want string + }{ + {`15:04:05.000`, `21:00:57.012`}, + {`15:04:05.999`, `21:00:57.012`}, + {`15:04:05,000`, `21:00:57,012`}, + {`15:04:05,999`, `21:00:57,012`}, + } + + // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009 + time := Unix(0, 1233810057012345600) + for _, tt := range tests { + if q := time.Format(tt.s); q != tt.want { + t.Errorf("Format(%q) = got %q, want %q", tt.s, q, tt.want) + } + } +} + +// Issue 48685 and 54567. +func TestParseFractionalSecondsLongerThanNineDigits(t *testing.T) { + tests := []struct { + s string + want int + }{ + // 9 digits + {"2021-09-29T16:04:33.000000000Z", 0}, + {"2021-09-29T16:04:33.000000001Z", 1}, + {"2021-09-29T16:04:33.100000000Z", 100_000_000}, + {"2021-09-29T16:04:33.100000001Z", 100_000_001}, + {"2021-09-29T16:04:33.999999999Z", 999_999_999}, + {"2021-09-29T16:04:33.012345678Z", 12_345_678}, + // 10 digits, truncates + {"2021-09-29T16:04:33.0000000000Z", 0}, + {"2021-09-29T16:04:33.0000000001Z", 0}, + {"2021-09-29T16:04:33.1000000000Z", 100_000_000}, + {"2021-09-29T16:04:33.1000000009Z", 100_000_000}, + {"2021-09-29T16:04:33.9999999999Z", 999_999_999}, + {"2021-09-29T16:04:33.0123456789Z", 12_345_678}, + // 11 digits, truncates + {"2021-09-29T16:04:33.10000000000Z", 100_000_000}, + {"2021-09-29T16:04:33.00123456789Z", 1_234_567}, + // 12 digits, truncates + {"2021-09-29T16:04:33.000123456789Z", 123_456}, + // 15 digits, truncates + {"2021-09-29T16:04:33.9999999999999999Z", 999_999_999}, + } + + for _, tt := range tests { + for _, format := range []string{RFC3339, RFC3339Nano} { + tm, err := Parse(format, tt.s) + if err != nil { + t.Errorf("Parse(%q, %q) error: %v", format, tt.s, err) + continue + } + if got := tm.Nanosecond(); got != tt.want { + t.Errorf("Parse(%q, %q) = got %d, want %d", format, tt.s, got, tt.want) + } + } + } +} + +func FuzzFormatRFC3339(f *testing.F) { + for _, ts := range [][2]int64{ + {math.MinInt64, math.MinInt64}, // 292277026304-08-26T15:42:51Z + {-62167219200, 0}, // 0000-01-01T00:00:00Z + {1661201140, 676836973}, // 2022-08-22T20:45:40.676836973Z + {253402300799, 999999999}, // 9999-12-31T23:59:59.999999999Z + {math.MaxInt64, math.MaxInt64}, // -292277022365-05-08T08:17:07Z + } { + f.Add(ts[0], ts[1], true, false, 0) + f.Add(ts[0], ts[1], false, true, 0) + for _, offset := range []int{0, 60, 60 * 60, 99*60*60 + 99*60, 123456789} { + f.Add(ts[0], ts[1], false, false, -offset) + f.Add(ts[0], ts[1], false, false, +offset) + } + } + + f.Fuzz(func(t *testing.T, sec, nsec int64, useUTC, useLocal bool, tzOffset int) { + var loc *Location + switch { + case useUTC: + loc = UTC + case useLocal: + loc = Local + default: + loc = FixedZone("", tzOffset) + } + ts := Unix(sec, nsec).In(loc) + + got := AppendFormatRFC3339(ts, nil, false) + want := AppendFormatAny(ts, nil, RFC3339) + if !bytes.Equal(got, want) { + t.Errorf("Format(%s, RFC3339) mismatch:\n\tgot: %s\n\twant: %s", ts, got, want) + } + + gotNanos := AppendFormatRFC3339(ts, nil, true) + wantNanos := AppendFormatAny(ts, nil, RFC3339Nano) + if !bytes.Equal(got, want) { + t.Errorf("Format(%s, RFC3339Nano) mismatch:\n\tgot: %s\n\twant: %s", ts, gotNanos, wantNanos) + } + }) +} diff --git a/src/time/genzabbrs.go b/src/time/genzabbrs.go index 9fd2f2b7626ea6..a8651bfdb87b55 100644 --- a/src/time/genzabbrs.go +++ b/src/time/genzabbrs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // // usage: diff --git a/src/time/internal_test.go b/src/time/internal_test.go index 87a4208b058e1b..4c4a720f74e4e2 100644 --- a/src/time/internal_test.go +++ b/src/time/internal_test.go @@ -5,33 +5,38 @@ package time func init() { - // force US/Pacific for time zone tests + // Force US/Pacific for time zone tests. ForceUSPacificForTesting() } func initTestingZone() { - z, err := loadLocation("America/Los_Angeles", zoneSources[len(zoneSources)-1:]) + // For hermeticity, use only tzinfo source from the test's GOROOT, + // not the system sources and not whatever GOROOT may happen to be + // set in the process's environment (if any). + // This test runs in GOROOT/src/time, so GOROOT is "../..", + // but it is theoretically possible + sources := []string{"../../lib/time/zoneinfo.zip"} + z, err := loadLocation("America/Los_Angeles", sources) if err != nil { - panic("cannot load America/Los_Angeles for testing: " + err.Error()) + panic("cannot load America/Los_Angeles for testing: " + err.Error() + "; you may want to use -tags=timetzdata") } z.name = "Local" localLoc = *z } -var OrigZoneSources = zoneSources +var origPlatformZoneSources []string = platformZoneSources -func forceZipFileForTesting(zipOnly bool) { - zoneSources = make([]string, len(OrigZoneSources)) - copy(zoneSources, OrigZoneSources) - if zipOnly { - zoneSources = zoneSources[len(zoneSources)-1:] +func disablePlatformSources() (undo func()) { + platformZoneSources = nil + return func() { + platformZoneSources = origPlatformZoneSources } } var Interrupt = interrupt var DaysIn = daysIn -func empty(arg interface{}, seq uintptr) {} +func empty(arg any, seq uintptr) {} // Test that a runtimeTimer with a period that would overflow when on // expiration does not throw or cause other timers to hang. diff --git a/src/time/sleep.go b/src/time/sleep.go index 4f4579941469cd..cdab4782ada3ac 100644 --- a/src/time/sleep.go +++ b/src/time/sleep.go @@ -14,8 +14,8 @@ type runtimeTimer struct { pp uintptr when int64 period int64 - f func(interface{}, uintptr) // NOTE: must not be closure - arg interface{} + f func(any, uintptr) // NOTE: must not be closure + arg any seq uintptr nextwhen int64 status uint32 @@ -41,7 +41,7 @@ func when(d Duration) int64 { func startTimer(*runtimeTimer) func stopTimer(*runtimeTimer) bool func resetTimer(*runtimeTimer, int64) bool -func modTimer(t *runtimeTimer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) +func modTimer(t *runtimeTimer, when, period int64, f func(any, uintptr), arg any, seq uintptr) // The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, @@ -62,9 +62,9 @@ type Timer struct { // return value and drain the channel. // For example, assuming the program has not received from t.C already: // -// if !t.Stop() { -// <-t.C -// } +// if !t.Stop() { +// <-t.C +// } // // This cannot be done concurrent to other receives from the Timer's // channel or other calls to the Timer's Stop method. @@ -110,10 +110,10 @@ func NewTimer(d Duration) *Timer { // the timer must be stopped and—if Stop reports that the timer expired // before being stopped—the channel explicitly drained: // -// if !t.Stop() { -// <-t.C -// } -// t.Reset(d) +// if !t.Stop() { +// <-t.C +// } +// t.Reset(d) // // This should not be done concurrent to other receives from the Timer's // channel. @@ -139,12 +139,8 @@ func (t *Timer) Reset(d Duration) bool { return resetTimer(&t.r, w) } -func sendTime(c interface{}, seq uintptr) { - // Non-blocking send of time on c. - // Used in NewTimer, it cannot block anyway (buffer). - // Used in NewTicker, dropping sends on the floor is - // the desired behavior when the reader gets behind, - // because the sends are periodic. +// sendTime does a non-blocking send of the current time on c. +func sendTime(c any, seq uintptr) { select { case c.(chan Time) <- Now(): default: @@ -176,6 +172,6 @@ func AfterFunc(d Duration, f func()) *Timer { return t } -func goFunc(arg interface{}, seq uintptr) { +func goFunc(arg any, seq uintptr) { go arg.(func())() } diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go index e0172bf5e0b736..8aac3b68f674d6 100644 --- a/src/time/sleep_test.go +++ b/src/time/sleep_test.go @@ -7,6 +7,8 @@ package time_test import ( "errors" "fmt" + "internal/testenv" + "math/rand" "runtime" "strings" "sync" @@ -63,9 +65,9 @@ func TestAfterFunc(t *testing.T) { } func TestAfterStress(t *testing.T) { - stop := uint32(0) + var stop atomic.Bool go func() { - for atomic.LoadUint32(&stop) == 0 { + for !stop.Load() { runtime.GC() // Yield so that the OS can wake up the timer thread, // so that it can generate channel sends for the main goroutine, @@ -78,7 +80,7 @@ func TestAfterStress(t *testing.T) { <-ticker.C } ticker.Stop() - atomic.StoreUint32(&stop, 1) + stop.Store(true) } func benchmark(b *testing.B, bench func(n int)) { @@ -530,6 +532,10 @@ func TestZeroTimer(t *testing.T) { // Test that rapidly moving a timer earlier doesn't cause it to get dropped. // Issue 47329. func TestTimerModifiedEarlier(t *testing.T) { + if runtime.GOOS == "plan9" && runtime.GOARCH == "arm" { + testenv.SkipFlaky(t, 50470) + } + past := Until(Unix(0, 0)) count := 1000 fail := 0 @@ -561,6 +567,72 @@ func TestTimerModifiedEarlier(t *testing.T) { } } +// Test that rapidly moving timers earlier and later doesn't cause +// some of the sleep times to be lost. +// Issue 47762 +func TestAdjustTimers(t *testing.T) { + var rnd = rand.New(rand.NewSource(Now().UnixNano())) + + timers := make([]*Timer, 100) + states := make([]int, len(timers)) + indices := rnd.Perm(len(timers)) + + for len(indices) != 0 { + var ii = rnd.Intn(len(indices)) + var i = indices[ii] + + var timer = timers[i] + var state = states[i] + states[i]++ + + switch state { + case 0: + timers[i] = NewTimer(0) + case 1: + <-timer.C // Timer is now idle. + + // Reset to various long durations, which we'll cancel. + case 2: + if timer.Reset(1 * Minute) { + panic("shouldn't be active (1)") + } + case 4: + if timer.Reset(3 * Minute) { + panic("shouldn't be active (3)") + } + case 6: + if timer.Reset(2 * Minute) { + panic("shouldn't be active (2)") + } + + // Stop and drain a long-duration timer. + case 3, 5, 7: + if !timer.Stop() { + t.Logf("timer %d state %d Stop returned false", i, state) + <-timer.C + } + + // Start a short-duration timer we expect to select without blocking. + case 8: + if timer.Reset(0) { + t.Fatal("timer.Reset returned true") + } + case 9: + now := Now() + <-timer.C + dur := Since(now) + if dur > 750*Millisecond { + t.Errorf("timer %d took %v to complete", i, dur) + } + + // Timer is done. Swap with tail and remove. + case 10: + indices[ii] = indices[len(indices)-1] + indices = indices[:len(indices)-1] + } + } +} + // Benchmark timer latency when the thread that creates the timer is busy with // other work and the timers must be serviced by other threads. // https://golang.org/issue/38860 diff --git a/src/time/sys_plan9.go b/src/time/sys_plan9.go index 4dc55e44aa5fb6..ba37cf54244bee 100644 --- a/src/time/sys_plan9.go +++ b/src/time/sys_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package time diff --git a/src/time/sys_unix.go b/src/time/sys_unix.go index 60fc090dc967ff..0f06aa6ccd7dcd 100644 --- a/src/time/sys_unix.go +++ b/src/time/sys_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris +//go:build unix || (js && wasm) package time diff --git a/src/time/sys_windows.go b/src/time/sys_windows.go index 481aea562e8b2e..78e182d4c5f59c 100644 --- a/src/time/sys_windows.go +++ b/src/time/sys_windows.go @@ -16,6 +16,10 @@ func interrupt() { func open(name string) (uintptr, error) { fd, err := syscall.Open(name, syscall.O_RDONLY, 0) if err != nil { + // This condition solves issue https://go.dev/issue/50248 + if err == syscall.ERROR_PATH_NOT_FOUND { + err = syscall.ENOENT + } return 0, err } return uintptr(fd), nil diff --git a/src/time/tick.go b/src/time/tick.go index 81d2a43f283880..dcfeca8783cb28 100644 --- a/src/time/tick.go +++ b/src/time/tick.go @@ -6,7 +6,7 @@ package time import "errors" -// A Ticker holds a channel that delivers ``ticks'' of a clock +// A Ticker holds a channel that delivers “ticks” of a clock // at intervals. type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. @@ -14,9 +14,9 @@ type Ticker struct { } // NewTicker returns a new Ticker containing a channel that will send -// the time on the channel after each tick. The period of the ticks is -// specified by the duration argument. The ticker will adjust the time -// interval or drop ticks to make up for slow receivers. +// the current time on the channel after each tick. The period of the +// ticks is specified by the duration argument. The ticker will adjust +// the time interval or drop ticks to make up for slow receivers. // The duration d must be greater than zero; if not, NewTicker will // panic. Stop the ticker to release associated resources. func NewTicker(d Duration) *Ticker { @@ -48,8 +48,12 @@ func (t *Ticker) Stop() { } // Reset stops a ticker and resets its period to the specified duration. -// The next tick will arrive after the new period elapses. +// The next tick will arrive after the new period elapses. The duration d +// must be greater than zero; if not, Reset will panic. func (t *Ticker) Reset(d Duration) { + if d <= 0 { + panic("non-positive interval for Ticker.Reset") + } if t.r.f == nil { panic("time: Reset called on uninitialized Ticker") } diff --git a/src/time/tick_test.go b/src/time/tick_test.go index b5d0a189bc8404..f539091869fcaf 100644 --- a/src/time/tick_test.go +++ b/src/time/tick_test.go @@ -15,10 +15,11 @@ func TestTicker(t *testing.T) { // We want to test that a ticker takes as much time as expected. // Since we don't want the test to run for too long, we don't // want to use lengthy times. This makes the test inherently flaky. - // So only report an error if it fails five times in a row. + // Start with a short time, but try again with a long one if the + // first test fails. - count := 10 - delta := 20 * Millisecond + baseCount := 10 + baseDelta := 20 * Millisecond // On Darwin ARM64 the tick frequency seems limited. Issue 35692. if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" { @@ -27,8 +28,8 @@ func TestTicker(t *testing.T) { // Since tick frequency is limited on Darwin ARM64, use even // number to give the ticks more time to let the test pass. // See CL 220638. - count = 6 - delta = 100 * Millisecond + baseCount = 6 + baseDelta = 100 * Millisecond } var errs []string @@ -38,7 +39,17 @@ func TestTicker(t *testing.T) { } } - for i := 0; i < 5; i++ { + for _, test := range []struct { + count int + delta Duration + }{{ + count: baseCount, + delta: baseDelta, + }, { + count: 8, + delta: 1 * Second, + }} { + count, delta := test.count, test.delta ticker := NewTicker(delta) t0 := Now() for i := 0; i < count/2; i++ { @@ -123,6 +134,17 @@ func TestNewTickerLtZeroDuration(t *testing.T) { NewTicker(-1) } +// Test that Ticker.Reset panics when given a duration less than zero. +func TestTickerResetLtZeroDuration(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Errorf("Ticker.Reset(0) should have panicked") + } + }() + tk := NewTicker(Second) + tk.Reset(0) +} + func BenchmarkTicker(b *testing.B) { benchmark(b, func(n int) { ticker := NewTicker(Nanosecond) diff --git a/src/time/time.go b/src/time/time.go index 4ecc3d82dcc958..47b26e39a8bb4a 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -7,7 +7,7 @@ // The calendrical calculations always assume a Gregorian calendar, with // no leap seconds. // -// Monotonic Clocks +// # Monotonic Clocks // // Operating systems provide both a “wall clock,” which is subject to // changes for clock synchronization, and a “monotonic clock,” which is @@ -64,6 +64,10 @@ // t.UnmarshalJSON, and t.UnmarshalText always create times with // no monotonic clock reading. // +// The monotonic clock reading exists only in Time values. It is not +// a part of Duration values or the Unix times returned by t.Unix and +// friends. +// // Note that the Go == operator compares not just the time instant but // also the Location and the monotonic clock reading. See the // documentation for the Time type for a discussion of equality @@ -72,7 +76,6 @@ // For debugging, the result of t.String does include the monotonic // clock reading if present. If t != u because of different monotonic clock readings, // that difference will be visible when printing t.String() and u.String(). -// package time import ( @@ -123,7 +126,6 @@ import ( // to t == u, since t.Equal uses the most accurate comparison available and // correctly handles the case when only one of its arguments has a monotonic // clock reading. -// type Time struct { // wall and ext encode the wall time seconds, wall time nanoseconds, // and optional monotonic clock reading in nanoseconds. @@ -425,7 +427,6 @@ const ( internalToUnix int64 = -unixToInternal wallToInternal int64 = (1884*365 + 1884/4 - 1884/100 + 1884/400) * secondsPerDay - internalToWall int64 = -wallToInternal ) // IsZero reports whether t represents the zero time instant, @@ -598,13 +599,14 @@ const ( // to avoid confusion across daylight savings time zone transitions. // // To count the number of units in a Duration, divide: +// // second := time.Second // fmt.Print(int64(second/time.Millisecond)) // prints 1000 // // To convert an integer number of units to a Duration, multiply: +// // seconds := 10 // fmt.Print(time.Duration(seconds)*time.Second) // prints 10s -// const ( Nanosecond Duration = 1 Microsecond = 1000 * Nanosecond @@ -816,6 +818,19 @@ func (d Duration) Round(m Duration) Duration { return maxDuration // overflow } +// Abs returns the absolute value of d. +// As a special case, math.MinInt64 is converted to math.MaxInt64. +func (d Duration) Abs() Duration { + switch { + case d >= 0: + return d + case d == minDuration: + return maxDuration + default: + return -d + } +} + // Add returns the time t+d. func (t Time) Add(d Duration) Time { dsec := int64(d / 1e9) @@ -1058,6 +1073,7 @@ func daysSinceEpoch(year int) uint64 { func now() (sec int64, nsec int32, mono int64) // runtimeNano returns the current value of the runtime clock in nanoseconds. +// //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 @@ -1125,6 +1141,24 @@ func (t Time) Zone() (name string, offset int) { return } +// ZoneBounds returns the bounds of the time zone in effect at time t. +// The zone begins at start and the next zone begins at end. +// If the zone begins at the beginning of time, start will be returned as a zero Time. +// If the zone goes on forever, end will be returned as a zero Time. +// The Location of the returned times will be the same as t. +func (t Time) ZoneBounds() (start, end Time) { + _, _, startSec, endSec, _ := t.loc.lookup(t.unixSec()) + if startSec != alpha { + start = unixTime(startSec, 0) + start.setLoc(t.loc) + } + if endSec != omega { + end = unixTime(endSec, 0) + end.setLoc(t.loc) + } + return +} + // Unix returns t as a Unix time, the number of seconds elapsed // since January 1, 1970 UTC. The result does not depend on the // location associated with t. @@ -1163,19 +1197,26 @@ func (t Time) UnixNano() int64 { return (t.unixSec())*1e9 + int64(t.nsec()) } -const timeBinaryVersion byte = 1 +const ( + timeBinaryVersionV1 byte = iota + 1 // For general situation + timeBinaryVersionV2 // For LMT only +) // MarshalBinary implements the encoding.BinaryMarshaler interface. func (t Time) MarshalBinary() ([]byte, error) { var offsetMin int16 // minutes east of UTC. -1 is UTC. + var offsetSec int8 + version := timeBinaryVersionV1 if t.Location() == UTC { offsetMin = -1 } else { _, offset := t.Zone() if offset%60 != 0 { - return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute") + version = timeBinaryVersionV2 + offsetSec = int8(offset % 60) } + offset /= 60 if offset < -32768 || offset == -1 || offset > 32767 { return nil, errors.New("Time.MarshalBinary: unexpected zone offset") @@ -1186,8 +1227,8 @@ func (t Time) MarshalBinary() ([]byte, error) { sec := t.sec() nsec := t.nsec() enc := []byte{ - timeBinaryVersion, // byte 0 : version - byte(sec >> 56), // bytes 1-8: seconds + version, // byte 0 : version + byte(sec >> 56), // bytes 1-8: seconds byte(sec >> 48), byte(sec >> 40), byte(sec >> 32), @@ -1202,6 +1243,9 @@ func (t Time) MarshalBinary() ([]byte, error) { byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes byte(offsetMin), } + if version == timeBinaryVersionV2 { + enc = append(enc, byte(offsetSec)) + } return enc, nil } @@ -1213,11 +1257,16 @@ func (t *Time) UnmarshalBinary(data []byte) error { return errors.New("Time.UnmarshalBinary: no data") } - if buf[0] != timeBinaryVersion { + version := buf[0] + if version != timeBinaryVersionV1 && version != timeBinaryVersionV2 { return errors.New("Time.UnmarshalBinary: unsupported version") } - if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 { + wantLen := /*version*/ 1 + /*sec*/ 8 + /*nsec*/ 4 + /*zone offset*/ 2 + if version == timeBinaryVersionV2 { + wantLen++ + } + if len(buf) != wantLen { return errors.New("Time.UnmarshalBinary: invalid length") } @@ -1230,6 +1279,9 @@ func (t *Time) UnmarshalBinary(data []byte) error { buf = buf[4:] offset := int(int16(buf[1])|int16(buf[0])<<8) * 60 + if version == timeBinaryVersionV2 { + offset += int(buf[2]) + } *t = Time{} t.wall = uint64(nsec) @@ -1350,6 +1402,7 @@ func isLeap(year int) bool { } // norm returns nhi, nlo such that +// // hi * base + lo == nhi * base + nlo // 0 <= nlo < base func norm(hi, lo, base int) (nhi, nlo int) { @@ -1367,7 +1420,9 @@ func norm(hi, lo, base int) (nhi, nlo int) { } // Date returns the Time corresponding to +// // yyyy-mm-dd hh:mm:ss + nsec nanoseconds +// // in the appropriate zone for that time in the given location. // // The month, day, hour, min, sec, and nsec values may be outside @@ -1416,17 +1471,17 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T unix := int64(abs) + (absoluteToInternal + internalToUnix) - // Look for zone offset for t, so we can adjust to UTC. - // The lookup function expects UTC, so we pass t in the + // Look for zone offset for expected time, so we can adjust to UTC. + // The lookup function expects UTC, so first we pass unix in the // hope that it will not be too close to a zone transition, // and then adjust if it is. _, offset, start, end, _ := loc.lookup(unix) if offset != 0 { - switch utc := unix - int64(offset); { - case utc < start: - _, offset, _, _, _ = loc.lookup(start - 1) - case utc >= end: - _, offset, _, _, _ = loc.lookup(end) + utc := unix - int64(offset) + // If utc is valid for the time zone we found, then we have the right offset. + // If not, we get the correct offset by looking up utc in the location. + if utc < start || utc >= end { + _, offset, _, _, _ = loc.lookup(utc) } unix -= int64(offset) } diff --git a/src/time/time_test.go b/src/time/time_test.go index cea5f2d3f5aceb..f2c6c3977ed1f9 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -9,6 +9,7 @@ import ( "encoding/gob" "encoding/json" "fmt" + "math" "math/big" "math/rand" "os" @@ -280,6 +281,8 @@ func TestTruncateRound(t *testing.T) { b1e9.SetInt64(1e9) testOne := func(ti, tns, di int64) bool { + t.Helper() + t0 := Unix(ti, int64(tns)).UTC() d := Duration(di) if d < 0 { @@ -366,6 +369,13 @@ func TestTruncateRound(t *testing.T) { for i := 0; i < int(b); i++ { d *= 5 } + + // Make room for unix ↔ internal conversion. + // We don't care about behavior too close to ± 2^63 Unix seconds. + // It is full of wraparounds but will never happen in a reasonable program. + // (Or maybe not? See go.dev/issue/20678. In any event, they're not handled today.) + ti >>= 1 + return testOne(ti, int64(tns), int64(d)) } quick.Check(f1, cfg) @@ -376,6 +386,7 @@ func TestTruncateRound(t *testing.T) { if d < 0 { d = -d } + ti >>= 1 // see comment in f1 return testOne(ti, int64(tns), int64(d)) } quick.Check(f2, cfg) @@ -398,6 +409,7 @@ func TestTruncateRound(t *testing.T) { // full generality f4 := func(ti int64, tns int32, di int64) bool { + ti >>= 1 // see comment in f1 return testOne(ti, int64(tns), di) } quick.Check(f4, cfg) @@ -767,7 +779,6 @@ var notEncodableTimes = []struct { time Time want string }{ - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"}, @@ -886,8 +897,13 @@ var parseDurationTests = []struct { {"9223372036854775807ns", (1<<63 - 1) * Nanosecond}, {"9223372036854775.807us", (1<<63 - 1) * Nanosecond}, {"9223372036s854ms775us807ns", (1<<63 - 1) * Nanosecond}, - // large negative value - {"-9223372036854775807ns", -1<<63 + 1*Nanosecond}, + {"-9223372036854775808ns", -1 << 63 * Nanosecond}, + {"-9223372036854775.808us", -1 << 63 * Nanosecond}, + {"-9223372036s854ms775us808ns", -1 << 63 * Nanosecond}, + // largest negative value + {"-9223372036854775808ns", -1 << 63 * Nanosecond}, + // largest negative round trip value, see https://golang.org/issue/48629 + {"-2562047h47m16.854775808s", -1 << 63 * Nanosecond}, // huge string; issue 15011. {"0.100000000000000000000h", 6 * Minute}, // This value tests the first overflow check in leadingFraction. @@ -925,9 +941,7 @@ var parseDurationErrorTests = []struct { // overflow {"9223372036854775810ns", `"9223372036854775810ns"`}, {"9223372036854775808ns", `"9223372036854775808ns"`}, - // largest negative value of type int64 in nanoseconds should fail - // see https://go-review.googlesource.com/#/c/2461/ - {"-9223372036854775808ns", `"-9223372036854775808ns"`}, + {"-9223372036854775809ns", `"-9223372036854775809ns"`}, {"9223372036854776us", `"9223372036854776us"`}, {"3000000h", `"3000000h"`}, {"9223372036854775.808us", `"9223372036854775.808us"`}, @@ -946,6 +960,19 @@ func TestParseDurationErrors(t *testing.T) { } func TestParseDurationRoundTrip(t *testing.T) { + // https://golang.org/issue/48629 + max0 := Duration(math.MaxInt64) + max1, err := ParseDuration(max0.String()) + if err != nil || max0 != max1 { + t.Errorf("round-trip failed: %d => %q => %d, %v", max0, max0.String(), max1, err) + } + + min0 := Duration(math.MinInt64) + min1, err := ParseDuration(min0.String()) + if err != nil || min0 != min1 { + t.Errorf("round-trip failed: %d => %q => %d, %v", min0, min0.String(), min1, err) + } + for i := 0; i < 100; i++ { // Resolutions finer than milliseconds will result in // imprecise round-trips. @@ -1224,6 +1251,30 @@ func TestDurationRound(t *testing.T) { } } +var durationAbsTests = []struct { + d Duration + want Duration +}{ + {0, 0}, + {1, 1}, + {-1, 1}, + {1 * Minute, 1 * Minute}, + {-1 * Minute, 1 * Minute}, + {minDuration, maxDuration}, + {minDuration + 1, maxDuration}, + {minDuration + 2, maxDuration - 1}, + {maxDuration, maxDuration}, + {maxDuration - 1, maxDuration - 1}, +} + +func TestDurationAbs(t *testing.T) { + for _, tt := range durationAbsTests { + if got := tt.d.Abs(); got != tt.want { + t.Errorf("Duration(%s).Abs() = %s; want: %s", tt.d, got, tt.want) + } + } +} + var defaultLocTests = []struct { name string f func(t1, t2 Time) bool @@ -1351,6 +1402,20 @@ func BenchmarkFormat(b *testing.B) { } } +func BenchmarkFormatRFC3339(b *testing.B) { + t := Unix(1265346057, 0) + for i := 0; i < b.N; i++ { + t.Format("2006-01-02T15:04:05Z07:00") + } +} + +func BenchmarkFormatRFC3339Nano(b *testing.B) { + t := Unix(1265346057, 0) + for i := 0; i < b.N; i++ { + t.Format("2006-01-02T15:04:05.999999999Z07:00") + } +} + func BenchmarkFormatNow(b *testing.B) { // Like BenchmarkFormat, but easier, because the time zone // lookup cache is optimized for the present. @@ -1422,6 +1487,21 @@ func BenchmarkISOWeek(b *testing.B) { } } +func BenchmarkGoString(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _ = t.GoString() + } +} + +func BenchmarkUnmarshalText(b *testing.B) { + var t Time + in := []byte("2020-08-22T11:27:43.123456789-02:00") + for i := 0; i < b.N; i++ { + t.UnmarshalText(in) + } +} + func TestMarshalBinaryZeroTime(t *testing.T) { t0 := Time{} enc, err := t0.MarshalBinary() @@ -1437,6 +1517,37 @@ func TestMarshalBinaryZeroTime(t *testing.T) { } } +func TestMarshalBinaryVersion2(t *testing.T) { + t0, err := Parse(RFC3339, "1880-01-01T00:00:00Z") + if err != nil { + t.Errorf("Failed to parse time, error = %v", err) + } + loc, err := LoadLocation("US/Eastern") + if err != nil { + t.Errorf("Failed to load location, error = %v", err) + } + t1 := t0.In(loc) + b, err := t1.MarshalBinary() + if err != nil { + t.Errorf("Failed to Marshal, error = %v", err) + } + + t2 := Time{} + err = t2.UnmarshalBinary(b) + if err != nil { + t.Errorf("Failed to Unmarshal, error = %v", err) + } + + if !(t0.Equal(t1) && t1.Equal(t2)) { + if !t0.Equal(t1) { + t.Errorf("The result t1: %+v after Marshal is not matched original t0: %+v", t1, t0) + } + if !t1.Equal(t2) { + t.Errorf("The result t2: %+v after Unmarshal is not matched original t1: %+v", t2, t1) + } + } +} + // Issue 17720: Zero value of time.Month fails to print func TestZeroMonthString(t *testing.T) { if got, want := Month(0).String(), "%!Month(0)"; got != want { @@ -1510,8 +1621,8 @@ func TestConcurrentTimerResetStop(t *testing.T) { } func TestTimeIsDST(t *testing.T) { - ForceZipFileForTesting(true) - defer ForceZipFileForTesting(false) + undo := DisablePlatformSources() + defer undo() tzWithDST, err := LoadLocation("Australia/Sydney") if err != nil { @@ -1569,3 +1680,119 @@ func TestTimeAddSecOverflow(t *testing.T) { } } } + +// Issue 49284: time: ParseInLocation incorrectly because of Daylight Saving Time +func TestTimeWithZoneTransition(t *testing.T) { + undo := DisablePlatformSources() + defer undo() + + loc, err := LoadLocation("Asia/Shanghai") + if err != nil { + t.Fatal(err) + } + + tests := [...]struct { + give Time + want Time + }{ + // 14 Apr 1991 - Daylight Saving Time Started + // When time of "Asia/Shanghai" was about to reach + // Sunday, 14 April 1991, 02:00:00 clocks were turned forward 1 hour to + // Sunday, 14 April 1991, 03:00:00 local daylight time instead. + // The UTC time was 13 April 1991, 18:00:00 + 0: {Date(1991, April, 13, 17, 50, 0, 0, loc), Date(1991, April, 13, 9, 50, 0, 0, UTC)}, + 1: {Date(1991, April, 13, 18, 0, 0, 0, loc), Date(1991, April, 13, 10, 0, 0, 0, UTC)}, + 2: {Date(1991, April, 14, 1, 50, 0, 0, loc), Date(1991, April, 13, 17, 50, 0, 0, UTC)}, + 3: {Date(1991, April, 14, 3, 0, 0, 0, loc), Date(1991, April, 13, 18, 0, 0, 0, UTC)}, + + // 15 Sep 1991 - Daylight Saving Time Ended + // When local daylight time of "Asia/Shanghai" was about to reach + // Sunday, 15 September 1991, 02:00:00 clocks were turned backward 1 hour to + // Sunday, 15 September 1991, 01:00:00 local standard time instead. + // The UTC time was 14 September 1991, 17:00:00 + 4: {Date(1991, September, 14, 16, 50, 0, 0, loc), Date(1991, September, 14, 7, 50, 0, 0, UTC)}, + 5: {Date(1991, September, 14, 17, 0, 0, 0, loc), Date(1991, September, 14, 8, 0, 0, 0, UTC)}, + 6: {Date(1991, September, 15, 0, 50, 0, 0, loc), Date(1991, September, 14, 15, 50, 0, 0, UTC)}, + 7: {Date(1991, September, 15, 2, 00, 0, 0, loc), Date(1991, September, 14, 18, 00, 0, 0, UTC)}, + } + + for i, tt := range tests { + if !tt.give.Equal(tt.want) { + t.Errorf("#%d:: %#v is not equal to %#v", i, tt.give.Format(RFC3339), tt.want.Format(RFC3339)) + } + } +} + +func TestZoneBounds(t *testing.T) { + undo := DisablePlatformSources() + defer undo() + loc, err := LoadLocation("Asia/Shanghai") + if err != nil { + t.Fatal(err) + } + + // The ZoneBounds of a UTC location would just return two zero Time. + for _, test := range utctests { + sec := test.seconds + golden := &test.golden + tm := Unix(sec, 0).UTC() + start, end := tm.ZoneBounds() + if !(start.IsZero() && end.IsZero()) { + t.Errorf("ZoneBounds of %+v expects two zero Time, got:\n start=%v\n end=%v", *golden, start, end) + } + } + + // If the zone begins at the beginning of time, start will be returned as a zero Time. + // Use math.MinInt32 to avoid overflow of int arguments on 32-bit systems. + beginTime := Date(math.MinInt32, January, 1, 0, 0, 0, 0, loc) + start, end := beginTime.ZoneBounds() + if !start.IsZero() || end.IsZero() { + t.Errorf("ZoneBounds of %v expects start is zero Time, got:\n start=%v\n end=%v", beginTime, start, end) + } + + // If the zone goes on forever, end will be returned as a zero Time. + // Use math.MaxInt32 to avoid overflow of int arguments on 32-bit systems. + foreverTime := Date(math.MaxInt32, January, 1, 0, 0, 0, 0, loc) + start, end = foreverTime.ZoneBounds() + if start.IsZero() || !end.IsZero() { + t.Errorf("ZoneBounds of %v expects end is zero Time, got:\n start=%v\n end=%v", foreverTime, start, end) + } + + // Check some real-world cases to make sure we're getting the right bounds. + boundOne := Date(1990, September, 16, 1, 0, 0, 0, loc) + boundTwo := Date(1991, April, 14, 3, 0, 0, 0, loc) + boundThree := Date(1991, September, 15, 1, 0, 0, 0, loc) + makeLocalTime := func(sec int64) Time { return Unix(sec, 0) } + realTests := [...]struct { + giveTime Time + wantStart Time + wantEnd Time + }{ + // The ZoneBounds of "Asia/Shanghai" Daylight Saving Time + 0: {Date(1991, April, 13, 17, 50, 0, 0, loc), boundOne, boundTwo}, + 1: {Date(1991, April, 13, 18, 0, 0, 0, loc), boundOne, boundTwo}, + 2: {Date(1991, April, 14, 1, 50, 0, 0, loc), boundOne, boundTwo}, + 3: {boundTwo, boundTwo, boundThree}, + 4: {Date(1991, September, 14, 16, 50, 0, 0, loc), boundTwo, boundThree}, + 5: {Date(1991, September, 14, 17, 0, 0, 0, loc), boundTwo, boundThree}, + 6: {Date(1991, September, 15, 0, 50, 0, 0, loc), boundTwo, boundThree}, + + // The ZoneBounds of a local time would return two local Time. + // Note: We preloaded "America/Los_Angeles" as time.Local for testing + 7: {makeLocalTime(0), makeLocalTime(-5756400), makeLocalTime(9972000)}, + 8: {makeLocalTime(1221681866), makeLocalTime(1205056800), makeLocalTime(1225616400)}, + 9: {makeLocalTime(2152173599), makeLocalTime(2145916800), makeLocalTime(2152173600)}, + 10: {makeLocalTime(2152173600), makeLocalTime(2152173600), makeLocalTime(2172733200)}, + 11: {makeLocalTime(2152173601), makeLocalTime(2152173600), makeLocalTime(2172733200)}, + 12: {makeLocalTime(2159200800), makeLocalTime(2152173600), makeLocalTime(2172733200)}, + 13: {makeLocalTime(2172733199), makeLocalTime(2152173600), makeLocalTime(2172733200)}, + 14: {makeLocalTime(2172733200), makeLocalTime(2172733200), makeLocalTime(2177452800)}, + } + for i, tt := range realTests { + start, end := tt.giveTime.ZoneBounds() + if !start.Equal(tt.wantStart) || !end.Equal(tt.wantEnd) { + t.Errorf("#%d:: ZoneBounds of %v expects right bounds:\n got start=%v\n want start=%v\n got end=%v\n want end=%v", + i, tt.giveTime, start, tt.wantStart, end, tt.wantEnd) + } + } +} diff --git a/src/time/tzdata/generate_zipdata.go b/src/time/tzdata/generate_zipdata.go index 64b5b1b22c234f..653577cafaf140 100644 --- a/src/time/tzdata/generate_zipdata.go +++ b/src/time/tzdata/generate_zipdata.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // This program generates zipdata.go from $GOROOT/lib/time/zoneinfo.zip. package main @@ -31,8 +30,8 @@ const header = `// Copyright 2020 The Go Authors. All rights reserved. // For more information, see // https://www.iana.org/time-zones -// ftp://ftp.iana.org/tz/code/tz-link.htm -// http://tools.ietf.org/html/rfc6557 +// ftp://ftp.iana.org/tz/code/tz-link.html +// https://datatracker.ietf.org/doc/html/rfc6557 package tzdata @@ -72,7 +71,7 @@ func main() { } } -func die(format string, args ...interface{}) { +func die(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) os.Exit(1) } diff --git a/src/time/tzdata/tzdata.go b/src/time/tzdata/tzdata.go index 25725bd84d2c68..324de5cd85c558 100644 --- a/src/time/tzdata/tzdata.go +++ b/src/time/tzdata/tzdata.go @@ -29,6 +29,7 @@ import ( ) // registerLoadFromEmbeddedTZData is defined in package time. +// //go:linkname registerLoadFromEmbeddedTZData time.registerLoadFromEmbeddedTZData func registerLoadFromEmbeddedTZData(func(string) (string, error)) diff --git a/src/time/tzdata/zipdata.go b/src/time/tzdata/zipdata.go index 03b59720e27eb9..a08e9904d67738 100644 --- a/src/time/tzdata/zipdata.go +++ b/src/time/tzdata/zipdata.go @@ -11,1032 +11,605 @@ // For more information, see // https://www.iana.org/time-zones -// ftp://ftp.iana.org/tz/code/tz-link.htm -// http://tools.ietf.org/html/rfc6557 +// ftp://ftp.iana.org/tz/code/tz-link.html +// https://datatracker.ietf.org/doc/html/rfc6557 package tzdata -const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Africa/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00Africa/NairobiUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + +const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Africa/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00Africa/NairobiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc" + "\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00" + - "\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00Af" + - "rica/FreetownUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\x9f\x1b\xeb\xdd2\x02\x00\x002\x02\x00\x00\f\x00\x1c\x00Africa/CeutaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff~6\xb5\x00\xff\xff\xff\xff\x9e\xd6up\xff\xff\xff\xff\x9f\xa1n`" + - "\xff\xff\xff\xff\xaa\x05\xefp\xff\xff\xff\xff\xaa\xe7n\x00\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89z\x00\xff\xff\xff\xff" + - "\xb2p0\x80\xff\xff\xff\xff\xfb%r@\xff\xff\xff\xff\xfb\xc2\xefp\x00\x00\x00\x00\bk\x84\x80\x00\x00\x00\x00\b\xc6m\xf0\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00\x00\x00\x00\r\xc9?\x80" + - "\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00\x00\x0f\xd3Q\x80\x00\x00\x00\x00\x10'\xa3p\x00\x00\x00\x00\x1a\xb7\xa6\x00\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00" + - "!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x8fn\x00\x00\x00\x00\x00?Z\x83\x10\x00\x00\x00\x00@oP\x00\x00\x00\x00\x00A:e\x10\x00\x00\x00\x00BO2\x00\x00\x00\x00\x00C\x1aG\x10\x00\x00\x00\x00" + + "D/\x14\x00\x00\x00\x00\x00D\xfa)\x10\x00\x00\x00\x00F\x0e\xf6\x00\x00\x00\x00\x00F\xda\v\x10\x00\x00\x00\x00G\xf8\x12\x80\x00\x00\x00\x00H\xc3'\x90\x00\x00\x00\x00I\xd7\xf4\x80\x00\x00\x00\x00J\xa3\t\x90" + + "\x00\x00\x00\x00K\xb7ր\x00\x00\x00\x00L\x82\xeb\x90\x00\x00\x00\x00M\x97\xb8\x80\x00\x00\x00\x00Nb͐\x00\x00\x00\x00Ow\x9a\x80\x00\x00\x00\x00PB\xaf\x90\x00\x00\x00\x00Q`\xb7\x00\x00\x00\x00\x00" + + "R\"\x91\x90\x00\x00\x00\x00S@\x99\x00\x00\x00\x00\x00T\v\xae\x10\x00\x00\x00\x00U {\x00\x00\x00\x00\x00U\xeb\x90\x10\x00\x00\x00\x00W\x00]\x00\x00\x00\x00\x00W\xcbr\x10\x00\x00\x00\x00X\xe0?\x00" + + "\x00\x00\x00\x00Y\xabT\x10\x01\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x00\x00\x10\b\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00\x1c \x00\n\x00\x00*0\x01\n\x00\x00\x0e\x10\x01\x0f\x00\x00\x1c \x00\x13LMT\x00+0130\x00SAST\x00WAT\x00CAT\x00" + + "\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0f\x00\x1c\x00Africa/DjiboutiUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b" + + "\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00" + + "&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x0e\x00\x1c" + + "\x00Africa/MbabaneUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\t\xff\xff\xff\xffm{A@\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff\xff̮\x8c\x80\xff\xff\xff\xff͞op\xff\xff\xff\xffΎn\x80\xff\xff" + + "\xff\xff\xcf~Qp\x01\x03\x02\x03\x02\x03\x00\x00\x1a@\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00*0\x01\x04\x00\x00\x1c \x00\x04LMT\x00SAST\x00\nSAST-2\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/MaputoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04" + + "LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x1c\x00Africa/AsmaraUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00" + + "\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00" + + "*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82" + + "\x00\x00\x00\f\x00\x1c\x00Africa/DakarUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU)\xae\x8eo&\a\x00\x00&\a\x00\x00\x0f\x00\x1c\x00Africa/El_AaiunUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xba\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xbcH\xf0\xe0\x00\x00\x00\x00\vѰ" + + "\x90\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00\x00\x0f\xd3Q\x80\x00\x00\x00\x00\x10'\xa3p\x00\x00\x00\x00HA\xe6\x80\x00\x00\x00" + + "\x00H\xbb\"p\x00\x00\x00\x00J#\x1a\x00\x00\x00\x00\x00J\x8d\xd5p\x00\x00\x00\x00K\xdc\xc0\x80\x00\x00\x00\x00L]\xe5p\x00\x00\x00\x00M\x97\xb8\x80\x00\x00\x00\x00N4\x8c\xf0\x00\x00\x00\x00O\x9c\xa0" + + "\xa0\x00\x00\x00\x00P\b\xbb\xa0\x00\x00\x00\x00P1\x9a \x00\x00\x00\x00Pg\xa7\xa0\x00\x00\x00\x00Q|\x82\xa0\x00\x00\x00\x00Q\xd8ˠ\x00\x00\x00\x00R\x05\x9e\xa0\x00\x00\x00\x00Rls\xa0\x00\x00\x00" + + "\x00S7z\xa0\x00\x00\x00\x00S\xae!\xa0\x00\x00\x00\x00S\xdcF \x00\x00\x00\x00TLU\xa0\x00\x00\x00\x00U\x17\\\xa0\x00\x00\x00\x00U|\xe0 \x00\x00\x00\x00U\xab\x04\xa0\x00\x00\x00\x00V,7" + + "\xa0\x00\x00\x00\x00V\xf7>\xa0\x00\x00\x00\x00WS\x87\xa0\x00\x00\x00\x00W\x81\xac \x00\x00\x00\x00X\x15T \x00\x00\x00\x00X\xd7 \xa0\x00\x00\x00\x00Y \xf4\xa0\x00\x00\x00\x00YXS\xa0\x00\x00\x00" + + "\x00Y\xf56 \x00\x00\x00\x00Z\xb7\x02\xa0\x00\x00\x00\x00Z\xf7\x9c \x00\x00\x00\x00[%\xc0\xa0\x00\x00\x00\x00[\xd5\x18 \x00\x00\x00\x00\\\xceC\xa0\x00\x00\x00\x00\\\xfch \x00\x00\x00\x00^\x9b\xb0" + + "\xa0\x00\x00\x00\x00^\xd3\x0f\xa0\x00\x00\x00\x00`rX \x00\x00\x00\x00`\xa0|\xa0\x00\x00\x00\x00b?\xc5 \x00\x00\x00\x00bw$ \x00\x00\x00\x00d\x16l\xa0\x00\x00\x00\x00dMˠ\x00\x00\x00" + + "\x00e\xed\x14 \x00\x00\x00\x00f\x1b8\xa0\x00\x00\x00\x00g\xba\x81 \x00\x00\x00\x00g\xf1\xe0 \x00\x00\x00\x00i\x91(\xa0\x00\x00\x00\x00i\xbfM \x00\x00\x00\x00kg\xd0 \x00\x00\x00\x00k\x95\xf4" + + "\xa0\x00\x00\x00\x00m5= \x00\x00\x00\x00ml\x9c \x00\x00\x00\x00o\v\xe4\xa0\x00\x00\x00\x00o:\t \x00\x00\x00\x00p\xd9Q\xa0\x00\x00\x00\x00q\x10\xb0\xa0\x00\x00\x00\x00r\xaf\xf9 \x00\x00\x00" + + "\x00r\xe7X \x00\x00\x00\x00t\x86\xa0\xa0\x00\x00\x00\x00t\xb4\xc5 \x00\x00\x00\x00vT\r\xa0\x00\x00\x00\x00v\x8bl\xa0\x00\x00\x00\x00x*\xb5 \x00\x00\x00\x00xX٠\x00\x00\x00\x00y\xf8\"" + + " \x00\x00\x00\x00z/\x81 \x00\x00\x00\x00{\xceɠ\x00\x00\x00\x00|\x06(\xa0\x00\x00\x00\x00}\xa5q \x00\x00\x00\x00}ӕ\xa0\x00\x00\x00\x00\x7fr\xde \x00\x00\x00\x00\x7f\xaa= \x00\x00\x00" + + "\x00\x81I\x85\xa0\x00\x00\x00\x00\x81\x80\xe4\xa0\x00\x00\x00\x00\x83 - \x00\x00\x00\x00\x83NQ\xa0\x00\x00\x00\x00\x84\xed\x9a \x00\x00\x00\x00\x85$\xf9 \x00\x00\x00\x00\x86\xc4A\xa0\x00\x00\x00\x00\x86\xf2f" + + " \x00\x00\x00\x00\x88\x91\xae\xa0\x00\x00\x00\x00\x88\xc9\r\xa0\x00\x00\x00\x00\x8ahV \x00\x00\x00\x00\x8a\x9f\xb5 \x00\x00\x00\x00\x8c>\xfd\xa0\x00\x00\x00\x00\x8cm\" \x00\x00\x00\x00\x8e\fj\xa0\x00\x00\x00" + + "\x00\x8eCɠ\x00\x00\x00\x00\x8f\xe3\x12 \x00\x00\x00\x00\x90\x1aq \x00\x00\x00\x00\x91\xb9\xb9\xa0\x00\x00\x00\x00\x91\xe7\xde \x00\x00\x00\x00\x93\x87&\xa0\x00\x00\x00\x00\x93\xbe\x85\xa0\x00\x00\x00\x00\x95]\xce" + + " \x00\x00\x00\x00\x95\x8b\xf2\xa0\x00\x00\x00\x00\x97+; \x00\x00\x00\x00\x97b\x9a \x00\x00\x00\x00\x99\x01\xe2\xa0\x00\x00\x00\x00\x999A\xa0\x00\x00\x00\x00\x9a؊ \x00\x00\x00\x00\x9b\x06\xae\xa0\x00\x00\x00" + + "\x00\x9c\xa5\xf7 \x00\x00\x00\x00\x9c\xddV \x00\x00\x00\x00\x9e|\x9e\xa0\x00\x00\x00\x00\x9e\xb3\xfd\xa0\x00\x00\x00\x00\xa0SF \x00\x00\x00\x00\xa0\x81j\xa0\x00\x00\x00\x00\xa2 \xb3 \x00\x00\x00\x00\xa2X\x12" + + " \x00\x00\x00\x00\xa3\xf7Z\xa0\x00\x00\x00\x00\xa4%\x7f \x00\x00\x00\x00\xa5\xc4Ǡ\x00\x00\x00\x00\xa5\xfc&\xa0\x00\x00\x00\x00\xa7\x9bo \x00\x00\x00\x00\xa7\xd2\xce \x00\x00\x00\x00\xa9r\x16\xa0\x00\x00\x00" + + "\x00\xa9\xa0; \x00\x00\x00\x00\xab?\x83\xa0\x00\x00\x00\x00\xabv\xe2\xa0\x00\x00\x00\x00\xad\x16+ \x00\x00\x00\x00\xadM\x8a \x00\x00\x00\x00\xae\xecҠ\x00\x00\x00\x00\xaf\x1a\xf7 \x00\x00\x00\x00\xb0\xba?" + + "\xa0\x00\x00\x00\x00\xb0\xf1\x9e\xa0\x00\x00\x00\x00\xb2\x90\xe7 \x00\x00\x00\x00\xb2\xbf\v\xa0\x00\x00\x00\x00\xb4^T \x00\x00\x00\x00\xb4\x95\xb3 \x00\x00\x00\x00\xb64\xfb\xa0\x00\x00\x00\x00\xb6lZ\xa0\x00\x00\x00" + + "\x00\xb8\v\xa3 \x00\x00\x00\x00\xb89Ǡ\x00\x00\x00\x00\xb9\xd9\x10 \x00\x00\x00\x00\xba\x10o \x00\x00\x00\x00\xbb\xaf\xb7\xa0\x00\x00\x00\x00\xbb\xe7\x16\xa0\x00\x00\x00\x00\xbd\x86_ \x00\x00\x00\x00\xbd\xb4\x83" + + "\xa0\x00\x00\x00\x00\xbfS\xcc \x00\x00\x00\x00\xbf\x8b+ \x00\x00\x00\x00\xc1*s\xa0\x00\x00\x00\x00\xc1X\x98 \x00\x00\x00\x00\xc2\xf7\xe0\xa0\x00\x00\x00\x00\xc3/?\xa0\x00\x00\x00\x00\xc4Έ \x00\x00\x00" + + "\x00\xc5\x05\xe7 \x00\x00\x00\x00ƥ/\xa0\x00\x00\x00\x00\xc6\xd3T \x00\x00\x00\x00\xc8r\x9c\xa0\x00\x00\x00\x00ȩ\xfb\xa0\x00\x00\x00\x00\xcaID \x00\x00\x00\x00ʀ\xa3 \x00\x00\x00\x00\xcc\x1f\xeb" + + "\xa0\x00\x00\x00\x00\xccN\x10 \x00\x00\x00\x00\xcd\xedX\xa0\x00\x00\x00\x00\xce$\xb7\xa0\x00\x00\x00\x00\xcf\xc4\x00 \x00\x00\x00\x00\xcf\xf2$\xa0\x00\x00\x00\x00ёm \x00\x00\x00\x00\xd1\xc8\xcc \x00\x00\x00" + + "\x00\xd3h\x14\xa0\x00\x00\x00\x00ӟs\xa0\x00\x00\x00\x00\xd5>\xbc \x00\x00\x00\x00\xd5l\xe0\xa0\x00\x00\x00\x00\xd7\f) \x00\x00\x00\x00\xd7C\x88 \x00\x00\x00\x00\xd8\xe2Р\x00\x00\x00\x00\xd9\x1a/" + + "\xa0\x00\x00\x00\x00ڹx \x00\x00\x00\x00\xda眠\x00\x00\x00\x00܆\xe5 \x00\x00\x00\x00ܾD \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\xff\xff\xf3\xa0\x00\x00\xff\xff\xf1\xf0\x00\x04\x00\x00\x0e\x10\x01\b\x00\x00\x00" + + "\x00\x00\f\x00\x00\x00\x00\x01\f\x00\x00\x0e\x10\x00\bLMT\x00-01\x00+01\x00+00\x00\n<+01>-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00" + + "\x00\x00\xb4\x00\x00\x00\x11\x00\x1c\x00Africa/LibrevilleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q" + + "\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vUV\xadD\xef\xca\x01\x00\x00\xca\x01\x00\x00\x0f\x00\x1c\x00Africa/KhartoumUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xb6\xa3\xda\x00\x00\x00\x00\x00\x00\x9e\x17\xe0\x00" + + "\x00\x00\x00\x01z4P\x00\x00\x00\x00\x02}\xf9\xe0\x00\x00\x00\x00\x03[g\xd0\x00\x00\x00\x00\x04`~\xe0\x00\x00\x00\x00\x05=\xec\xd0\x00\x00\x00\x00\x06@`\xe0\x00\x00\x00\x00\a\x1f P\x00\x00\x00\x00\b" + + " B\xe0\x00\x00\x00\x00\t\x00S\xd0\x00\x00\x00\x00\n\x00$\xe0\x00\x00\x00\x00\n\xe1\x87P\x00\x00\x00\x00\v\xe0\x06\xe0\x00\x00\x00\x00\f\xc4\fP\x00\x00\x00\x00\r\xbf\xe8\xe0\x00\x00\x00\x00\x0e\xa5?\xd0\x00" + + "\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00\x00\x10\x86sP\x00\x00\x00\x00\x11\x88\xe7`\x00\x00\x00\x00\x12g\xa6\xd0\x00\x00\x00\x00\x13h\xc9`\x00\x00\x00\x00\x14J+\xd0\x00\x00\x00\x00\x15H\xab`\x00\x00\x00\x00\x16" + + "+_P\x00\x00\x00\x00\x17(\x8d`\x00\x00\x00\x00\x18\f\x92\xd0\x00\x00\x00\x00\x19\bo`\x00\x00\x00\x00\x19\xed\xc6P\x00\x00\x00\x00\x1a\xf1\x8b\xe0\x00\x00\x00\x00\x1b\xd0KP\x00\x00\x00\x00\x1c\xd1m\xe0\x00" + + "\x00\x00\x00\x1d\xb1~\xd0\x00\x00\x00\x008\x80E \x00\x00\x00\x00Y\xf8\xe4P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x00\x00" + + "\x1e\x80\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00CAST\x00CAT\x00EAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\f\x00\x1c\x00Africa/AccraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00" + + "\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x1c\x00Africa/BamakoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H" + + "\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x1c\x00Afr" + + "ica/AsmeraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00" + + "\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x12\x00\x1c\x00Africa/Addis_AbabaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff" + + "\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00E" + + "AT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x0f\x00\x1c\x00Africa/Kinshas" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04" + + "\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00" + + "\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x0f\x00\x1c\x00A" + + "frica/BlantyreUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/BanguiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96" + + "\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWA" + + "T-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x1c\x00Africa/AbidjanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01" + + "\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\v\x00\x1c\x00Afri" + + "ca/LomeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7" + + "\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/MalaboUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1" + "Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9R\x14\xcf\x10n\xca\x01\x00\x00\xca\x01\x00\x00\v\x00\x1c\x00Africa/JubaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xb6\xa3\xda\xdc\x00\x00\x00\x00\x00\x9e\x17\xe0\x00\x00\x00\x00" + - "\x01z4P\x00\x00\x00\x00\x02}\xf9\xe0\x00\x00\x00\x00\x03[g\xd0\x00\x00\x00\x00\x04`~\xe0\x00\x00\x00\x00\x05=\xec\xd0\x00\x00\x00\x00\x06@`\xe0\x00\x00\x00\x00\a\x1f P\x00\x00\x00\x00\b B\xe0" + - "\x00\x00\x00\x00\t\x00S\xd0\x00\x00\x00\x00\n\x00$\xe0\x00\x00\x00\x00\n\xe1\x87P\x00\x00\x00\x00\v\xe0\x06\xe0\x00\x00\x00\x00\f\xc4\fP\x00\x00\x00\x00\r\xbf\xe8\xe0\x00\x00\x00\x00\x0e\xa5?\xd0\x00\x00\x00\x00" + - "\x0f\xa9\x05`\x00\x00\x00\x00\x10\x86sP\x00\x00\x00\x00\x11\x88\xe7`\x00\x00\x00\x00\x12g\xa6\xd0\x00\x00\x00\x00\x13h\xc9`\x00\x00\x00\x00\x14J+\xd0\x00\x00\x00\x00\x15H\xab`\x00\x00\x00\x00\x16+_P" + - "\x00\x00\x00\x00\x17(\x8d`\x00\x00\x00\x00\x18\f\x92\xd0\x00\x00\x00\x00\x19\bo`\x00\x00\x00\x00\x19\xed\xc6P\x00\x00\x00\x00\x1a\xf1\x8b\xe0\x00\x00\x00\x00\x1b\xd0KP\x00\x00\x00\x00\x1c\xd1m\xe0\x00\x00\x00\x00" + - "\x1d\xb1~\xd0\x00\x00\x00\x008\x80E \x00\x00\x00\x00`\x17\x1aP\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x00\x00\x1d\xa4\x00" + - "\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00CAST\x00CAT\x00EAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcc\fT" + - "ξ\x00\x00\x00\xbe\x00\x00\x00\x13\x00\x1c\x00Africa/JohannesburgUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\t\xff\xff\xff\xffm{A@\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff\xff̮\x8c\x80\xff" + - "\xff\xff\xff͞op\xff\xff\xff\xffΎn\x80\xff\xff\xff\xff\xcf~Qp\x01\x03\x02\x03\x02\x03\x00\x00\x1a@\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00*0\x01\x04\x00\x00\x1c \x00\x04LMT\x00SAS" + - "T\x00\nSAST-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x10\x00\x1c\x00Africa/BujumburaUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff" + - "\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x12tnj\xfc\x04\x00\x00\xfc\x04\x00" + - "\x00\f\x00\x1c\x00Africa/CairoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\u007f\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff}\xbdM\xab\xff\xff\xff\xffȓ\xb4\xe0\xff\xff\xff\xff\xc8\xfa{\xd0\xff\xff\xff\xff\xc9\xfc\xef\xe0\xff\xff\xff\xff\xca\xc7\xe8\xd0" + - "\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xcc\xdf)\xd0\xff\xff\xff\xffͬ\xe1\xe0\xff\xff\xff\xff\xce\xc6\xf4\xd0\xff\xff\xff\xffϏf\xe0\xff\xff\xff\xffЩy\xd0\xff\xff\xff\xffф`\xe0\xff\xff\xff\xff" + - "Ҋ\xadP\xff\xff\xff\xff\xe86c`\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xfa\xf0\xff\xff\xff\xff\xec\xb5m\x00\xff\xff\xff\xff\xed\xcf\u007f\xf0" + - "\xff\xff\xff\xff\xee\x97\xf2\x00\xff\xff\xff\xffﰳp\xff\xff\xff\xff\xf0y%\x80\xff\xff\xff\xff\xf1\x91\xe6\xf0\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3s\x1ap\xff\xff\xff\xff\xf4;\x8c\x80\xff\xff\xff\xff" + - "\xf5U\x9fp\xff\xff\xff\xff\xf6\x1e\x11\x80\xff\xff\xff\xff\xf76\xd2\xf0\xff\xff\xff\xff\xf7\xffE\x00\xff\xff\xff\xff\xf9\x18\x06p\xff\xff\xff\xff\xf9\xe1\xca\x00\xff\xff\xff\xff\xfa\xf99\xf0\xff\xff\xff\xff\xfb\xc2\xfd\x80" + - "\xff\xff\xff\xff\xfc۾\xf0\xff\xff\xff\xff\xfd\xa5\x82\x80\xff\xff\xff\xff\xfe\xbc\xf2p\xff\xff\xff\xff\xff\x86\xb6\x00\x00\x00\x00\x00\x00\x9e%\xf0\x00\x00\x00\x00\x01g\xe9\x80\x00\x00\x00\x00\x02\u007fYp\x00\x00\x00\x00" + - "\x03I\x1d\x00\x00\x00\x00\x00\x04a\xdep\x00\x00\x00\x00\x05+\xa2\x00\x00\x00\x00\x00\x06C\x11\xf0\x00\x00\x00\x00\a\fՀ\x00\x00\x00\x00\b$Ep\x00\x00\x00\x00\b\xee\t\x00\x00\x00\x00\x00\n\x05x\xf0" + - "\x00\x00\x00\x00\n\xcf<\x80\x00\x00\x00\x00\v\xe7\xfd\xf0\x00\x00\x00\x00\f\xb1\xc1\x80\x00\x00\x00\x00\r\xc91p\x00\x00\x00\x00\x0e\x92\xf5\x00\x00\x00\x00\x00\x0f\xaad\xf0\x00\x00\x00\x00\x10t(\x80\x00\x00\x00\x00" + - "\x11\x8b\x98p\x00\x00\x00\x00\x12U\\\x00\x00\x00\x00\x00\x13n\x1dp\x00\x00\x00\x00\x147\xe1\x00\x00\x00\x00\x00\x15OP\xf0\x00\x00\x00\x00\x16\x19\x14\x80\x00\x00\x00\x00\x17\xa0\x93\xf0\x00\x00\x00\x00\x17\xfaH\x00" + - "\x00\x00\x00\x00\x19p\xa3\xf0\x00\x00\x00\x00\x19\xdb{\x80\x00\x00\x00\x00\x1a\xf4<\xf0\x00\x00\x00\x00\x1b\xbe\x00\x80\x00\x00\x00\x00\x1c\xd5pp\x00\x00\x00\x00\x1d\x9f4\x00\x00\x00\x00\x00\x1e\xb6\xa3\xf0\x00\x00\x00\x00" + - "\x1f\x80g\x80\x00\x00\x00\x00 \x97\xd7p\x00\x00\x00\x00!a\x9b\x00\x00\x00\x00\x00\"z\\p\x00\x00\x00\x00#D \x00\x00\x00\x00\x00$b'p\x00\x00\x00\x00%%S\x80\x00\x00\x00\x00&<\xc3p" + - "\x00\x00\x00\x00'\x06\x87\x00\x00\x00\x00\x00(\x1d\xf6\xf0\x00\x00\x00\x00(纀\x00\x00\x00\x00*\x00{\xf0\x00\x00\x00\x00*\xca?\x80\x00\x00\x00\x00+\xe1\xafp\x00\x00\x00\x00,\xabs\x00\x00\x00\x00\x00" + - "-\xc2\xe2\xf0\x00\x00\x00\x00.\x8c\xa6\x80\x00\x00\x00\x00/\xa0\x13\xe0\x00\x00\x00\x000k\f\xd0\x00\x00\x00\x001\u007f\xf5\xe0\x00\x00\x00\x002J\xee\xd0\x00\x00\x00\x003_\xd7\xe0\x00\x00\x00\x004*\xd0\xd0" + - "\x00\x00\x00\x005?\xb9\xe0\x00\x00\x00\x006\n\xb2\xd0\x00\x00\x00\x007(\xd6`\x00\x00\x00\x007\xf3\xcfP\x00\x00\x00\x009\b\xb8`\x00\x00\x00\x009ӱP\x00\x00\x00\x00:\xe8\x9a`\x00\x00\x00\x00" + - ";\xb3\x93P\x00\x00\x00\x00<\xc8|`\x00\x00\x00\x00=\x93uP\x00\x00\x00\x00>\xa8^`\x00\x00\x00\x00?sWP\x00\x00\x00\x00@\x91z\xe0\x00\x00\x00\x00A\\s\xd0\x00\x00\x00\x00Bq\\\xe0" + - "\x00\x00\x00\x00C\xe0\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F1 \xe0\x00\x00\x00\x00F\xe0jP\x00\x00\x00\x00H\x11\x02\xe0\x00\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00" + - "I\xf0\xe4\xe0\x00\x00\x00\x00J\x8d\xb9P\x00\x00\x00\x00K\xda\x01`\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00L\x89X\xe0\x00\x00\x00\x00L\xa4\xfaP\x00\x00\x00\x00Su8\xe0\x00\x00\x00\x00S\xac\x89\xd0" + - "\x00\x00\x00\x00Sڼ`\x00\x00\x00\x00T$\x82P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x1dU\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RV\xadD\xef\xca\x01\x00\x00\xca\x01\x00\x00\x0f\x00\x1c\x00Africa/KhartoumUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xb6\xa3\xda\x00\x00\x00\x00" + - "\x00\x00\x9e\x17\xe0\x00\x00\x00\x00\x01z4P\x00\x00\x00\x00\x02}\xf9\xe0\x00\x00\x00\x00\x03[g\xd0\x00\x00\x00\x00\x04`~\xe0\x00\x00\x00\x00\x05=\xec\xd0\x00\x00\x00\x00\x06@`\xe0\x00\x00\x00\x00\a\x1f " + - "P\x00\x00\x00\x00\b B\xe0\x00\x00\x00\x00\t\x00S\xd0\x00\x00\x00\x00\n\x00$\xe0\x00\x00\x00\x00\n\xe1\x87P\x00\x00\x00\x00\v\xe0\x06\xe0\x00\x00\x00\x00\f\xc4\fP\x00\x00\x00\x00\r\xbf\xe8\xe0\x00\x00\x00" + - "\x00\x0e\xa5?\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00\x00\x10\x86sP\x00\x00\x00\x00\x11\x88\xe7`\x00\x00\x00\x00\x12g\xa6\xd0\x00\x00\x00\x00\x13h\xc9`\x00\x00\x00\x00\x14J+\xd0\x00\x00\x00\x00\x15H\xab" + - "`\x00\x00\x00\x00\x16+_P\x00\x00\x00\x00\x17(\x8d`\x00\x00\x00\x00\x18\f\x92\xd0\x00\x00\x00\x00\x19\bo`\x00\x00\x00\x00\x19\xed\xc6P\x00\x00\x00\x00\x1a\xf1\x8b\xe0\x00\x00\x00\x00\x1b\xd0KP\x00\x00\x00" + - "\x00\x1c\xd1m\xe0\x00\x00\x00\x00\x1d\xb1~\xd0\x00\x00\x00\x008\x80E \x00\x00\x00\x00Y\xf8\xe4P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x03\x02\x00\x00\x1e\x80\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00CAST\x00CAT\x00EAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x0e\x00\x1c\x00Africa/MbabaneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\t\xff\xff\xff\xffm{A@\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff\xff\xcc" + - "\xae\x8c\x80\xff\xff\xff\xff͞op\xff\xff\xff\xffΎn\x80\xff\xff\xff\xff\xcf~Qp\x01\x03\x02\x03\x02\x03\x00\x00\x1a@\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00*0\x01\x04\x00\x00\x1c \x00\x04LMT" + - "\x00SAST\x00\nSAST-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R)\xae\x8eo&\a\x00\x00&\a\x00\x00\x0f\x00\x1c\x00Africa/El_AaiunU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xba\x00\x00\x00\x06\x00\x00" + - "\x00\x10\xff\xff\xff\xff\xbcH\xf0\xe0\x00\x00\x00\x00\vѰ\x90\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00\x00\x0f\xd3Q\x80\x00\x00" + - "\x00\x00\x10'\xa3p\x00\x00\x00\x00HA\xe6\x80\x00\x00\x00\x00H\xbb\"p\x00\x00\x00\x00J#\x1a\x00\x00\x00\x00\x00J\x8d\xd5p\x00\x00\x00\x00K\xdc\xc0\x80\x00\x00\x00\x00L]\xe5p\x00\x00\x00\x00M\x97" + - "\xb8\x80\x00\x00\x00\x00N4\x8c\xf0\x00\x00\x00\x00O\x9c\xa0\xa0\x00\x00\x00\x00P\b\xbb\xa0\x00\x00\x00\x00P1\x9a \x00\x00\x00\x00Pg\xa7\xa0\x00\x00\x00\x00Q|\x82\xa0\x00\x00\x00\x00Q\xd8ˠ\x00\x00" + - "\x00\x00R\x05\x9e\xa0\x00\x00\x00\x00Rls\xa0\x00\x00\x00\x00S7z\xa0\x00\x00\x00\x00S\xae!\xa0\x00\x00\x00\x00S\xdcF \x00\x00\x00\x00TLU\xa0\x00\x00\x00\x00U\x17\\\xa0\x00\x00\x00\x00U|" + - "\xe0 \x00\x00\x00\x00U\xab\x04\xa0\x00\x00\x00\x00V,7\xa0\x00\x00\x00\x00V\xf7>\xa0\x00\x00\x00\x00WS\x87\xa0\x00\x00\x00\x00W\x81\xac \x00\x00\x00\x00X\x15T \x00\x00\x00\x00X\xd7 \xa0\x00\x00" + - "\x00\x00Y \xf4\xa0\x00\x00\x00\x00YXS\xa0\x00\x00\x00\x00Y\xf56 \x00\x00\x00\x00Z\xb7\x02\xa0\x00\x00\x00\x00Z\xf7\x9c \x00\x00\x00\x00[%\xc0\xa0\x00\x00\x00\x00[\xd5\x18 \x00\x00\x00\x00\\\xce" + - "C\xa0\x00\x00\x00\x00\\\xfch \x00\x00\x00\x00^\x9b\xb0\xa0\x00\x00\x00\x00^\xd3\x0f\xa0\x00\x00\x00\x00`rX \x00\x00\x00\x00`\xa0|\xa0\x00\x00\x00\x00b?\xc5 \x00\x00\x00\x00bw$ \x00\x00" + - "\x00\x00d\x16l\xa0\x00\x00\x00\x00dMˠ\x00\x00\x00\x00e\xed\x14 \x00\x00\x00\x00f\x1b8\xa0\x00\x00\x00\x00g\xba\x81 \x00\x00\x00\x00g\xf1\xe0 \x00\x00\x00\x00i\x91(\xa0\x00\x00\x00\x00i\xbf" + - "M \x00\x00\x00\x00kg\xd0 \x00\x00\x00\x00k\x95\xf4\xa0\x00\x00\x00\x00m5= \x00\x00\x00\x00ml\x9c \x00\x00\x00\x00o\v\xe4\xa0\x00\x00\x00\x00o:\t \x00\x00\x00\x00p\xd9Q\xa0\x00\x00" + - "\x00\x00q\x10\xb0\xa0\x00\x00\x00\x00r\xaf\xf9 \x00\x00\x00\x00r\xe7X \x00\x00\x00\x00t\x86\xa0\xa0\x00\x00\x00\x00t\xb4\xc5 \x00\x00\x00\x00vT\r\xa0\x00\x00\x00\x00v\x8bl\xa0\x00\x00\x00\x00x*" + - "\xb5 \x00\x00\x00\x00xX٠\x00\x00\x00\x00y\xf8\" \x00\x00\x00\x00z/\x81 \x00\x00\x00\x00{\xceɠ\x00\x00\x00\x00|\x06(\xa0\x00\x00\x00\x00}\xa5q \x00\x00\x00\x00}ӕ\xa0\x00\x00" + - "\x00\x00\u007fr\xde \x00\x00\x00\x00\u007f\xaa= \x00\x00\x00\x00\x81I\x85\xa0\x00\x00\x00\x00\x81\x80\xe4\xa0\x00\x00\x00\x00\x83 - \x00\x00\x00\x00\x83NQ\xa0\x00\x00\x00\x00\x84\xed\x9a \x00\x00\x00\x00\x85$" + - "\xf9 \x00\x00\x00\x00\x86\xc4A\xa0\x00\x00\x00\x00\x86\xf2f \x00\x00\x00\x00\x88\x91\xae\xa0\x00\x00\x00\x00\x88\xc9\r\xa0\x00\x00\x00\x00\x8ahV \x00\x00\x00\x00\x8a\x9f\xb5 \x00\x00\x00\x00\x8c>\xfd\xa0\x00\x00" + - "\x00\x00\x8cm\" \x00\x00\x00\x00\x8e\fj\xa0\x00\x00\x00\x00\x8eCɠ\x00\x00\x00\x00\x8f\xe3\x12 \x00\x00\x00\x00\x90\x1aq \x00\x00\x00\x00\x91\xb9\xb9\xa0\x00\x00\x00\x00\x91\xe7\xde \x00\x00\x00\x00\x93\x87" + - "&\xa0\x00\x00\x00\x00\x93\xbe\x85\xa0\x00\x00\x00\x00\x95]\xce \x00\x00\x00\x00\x95\x8b\xf2\xa0\x00\x00\x00\x00\x97+; \x00\x00\x00\x00\x97b\x9a \x00\x00\x00\x00\x99\x01\xe2\xa0\x00\x00\x00\x00\x999A\xa0\x00\x00" + - "\x00\x00\x9a؊ \x00\x00\x00\x00\x9b\x06\xae\xa0\x00\x00\x00\x00\x9c\xa5\xf7 \x00\x00\x00\x00\x9c\xddV \x00\x00\x00\x00\x9e|\x9e\xa0\x00\x00\x00\x00\x9e\xb3\xfd\xa0\x00\x00\x00\x00\xa0SF \x00\x00\x00\x00\xa0\x81" + - "j\xa0\x00\x00\x00\x00\xa2 \xb3 \x00\x00\x00\x00\xa2X\x12 \x00\x00\x00\x00\xa3\xf7Z\xa0\x00\x00\x00\x00\xa4%\u007f \x00\x00\x00\x00\xa5\xc4Ǡ\x00\x00\x00\x00\xa5\xfc&\xa0\x00\x00\x00\x00\xa7\x9bo \x00\x00" + - "\x00\x00\xa7\xd2\xce \x00\x00\x00\x00\xa9r\x16\xa0\x00\x00\x00\x00\xa9\xa0; \x00\x00\x00\x00\xab?\x83\xa0\x00\x00\x00\x00\xabv\xe2\xa0\x00\x00\x00\x00\xad\x16+ \x00\x00\x00\x00\xadM\x8a \x00\x00\x00\x00\xae\xec" + - "Ҡ\x00\x00\x00\x00\xaf\x1a\xf7 \x00\x00\x00\x00\xb0\xba?\xa0\x00\x00\x00\x00\xb0\xf1\x9e\xa0\x00\x00\x00\x00\xb2\x90\xe7 \x00\x00\x00\x00\xb2\xbf\v\xa0\x00\x00\x00\x00\xb4^T \x00\x00\x00\x00\xb4\x95\xb3 \x00\x00" + - "\x00\x00\xb64\xfb\xa0\x00\x00\x00\x00\xb6lZ\xa0\x00\x00\x00\x00\xb8\v\xa3 \x00\x00\x00\x00\xb89Ǡ\x00\x00\x00\x00\xb9\xd9\x10 \x00\x00\x00\x00\xba\x10o \x00\x00\x00\x00\xbb\xaf\xb7\xa0\x00\x00\x00\x00\xbb\xe7" + - "\x16\xa0\x00\x00\x00\x00\xbd\x86_ \x00\x00\x00\x00\xbd\xb4\x83\xa0\x00\x00\x00\x00\xbfS\xcc \x00\x00\x00\x00\xbf\x8b+ \x00\x00\x00\x00\xc1*s\xa0\x00\x00\x00\x00\xc1X\x98 \x00\x00\x00\x00\xc2\xf7\xe0\xa0\x00\x00" + - "\x00\x00\xc3/?\xa0\x00\x00\x00\x00\xc4Έ \x00\x00\x00\x00\xc5\x05\xe7 \x00\x00\x00\x00ƥ/\xa0\x00\x00\x00\x00\xc6\xd3T \x00\x00\x00\x00\xc8r\x9c\xa0\x00\x00\x00\x00ȩ\xfb\xa0\x00\x00\x00\x00\xcaI" + - "D \x00\x00\x00\x00ʀ\xa3 \x00\x00\x00\x00\xcc\x1f\xeb\xa0\x00\x00\x00\x00\xccN\x10 \x00\x00\x00\x00\xcd\xedX\xa0\x00\x00\x00\x00\xce$\xb7\xa0\x00\x00\x00\x00\xcf\xc4\x00 \x00\x00\x00\x00\xcf\xf2$\xa0\x00\x00" + - "\x00\x00ёm \x00\x00\x00\x00\xd1\xc8\xcc \x00\x00\x00\x00\xd3h\x14\xa0\x00\x00\x00\x00ӟs\xa0\x00\x00\x00\x00\xd5>\xbc \x00\x00\x00\x00\xd5l\xe0\xa0\x00\x00\x00\x00\xd7\f) \x00\x00\x00\x00\xd7C" + - "\x88 \x00\x00\x00\x00\xd8\xe2Р\x00\x00\x00\x00\xd9\x1a/\xa0\x00\x00\x00\x00ڹx \x00\x00\x00\x00\xda眠\x00\x00\x00\x00܆\xe5 \x00\x00\x00\x00ܾD \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\xff\xff\xf3\xa0" + - "\x00\x00\xff\xff\xf1\xf0\x00\x04\x00\x00\x0e\x10\x01\b\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x01\f\x00\x00\x0e\x10\x00\bLMT\x00-01\x00+01\x00+00\x00\n<+01>-1\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R6\x99rU\xa4\x00\x00\x00\xa4\x00\x00\x00\x0f\x00\x1c\x00Africa/MonroviaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xffZz\xa6\x9c\xff\xff\xff\xff\xa0_l" + - "\x9c\x00\x00\x00\x00\x03\xcaZn\x01\x02\x03\xff\xff\xf5\xe4\x00\x00\xff\xff\xf5\xe4\x00\x04\xff\xff\xf5\x92\x00\x04\x00\x00\x00\x00\x00\bLMT\x00MMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/LusakaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00" + - "\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x1c\x00Africa/BamakoU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00" + - "\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4" + - "\x00\x00\x00\r\x00\x1c\x00Africa/NiameyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00" + - "\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/KigaliUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00C" + - "AT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xca>\xd5\xe0\x95\x00\x00\x00\x95\x00\x00\x00\r\x00\x1c\x00Africa/BissauUT\t\x00\x03\x15\xac" + - "\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff" + - "\x92朐\x00\x00\x00\x00\tga\x10\x01\x02\xff\xff\xf1d\x00\x00\xff\xff\xf1\xf0\x00\x04\x00\x00\x00\x00\x00\bLMT\x00-01\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x0f\x00\x1c\x00Africa/KinshasaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/LusakaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c" + + " \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xca>\xd5\xe0\x95\x00\x00\x00\x95\x00\x00\x00\r\x00\x1c\x00Africa/Bissa" + + "uUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03" + + "\x00\x00\x00\f\xff\xff\xff\xff\x92朐\x00\x00\x00\x00\tga\x10\x01\x02\xff\xff\xf1d\x00\x00\xff\xff\xf1\xf0\x00\x04\x00\x00\x00\x00\x00\bLMT\x00-01\x00GMT\x00\nGMT0\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x10\x00\x1c\x00Africa/BujumburaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c" + + "\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc1\n\x8a\x84\xad\x00\x00\x00\xad\x00\x00\x00\x0f\x00\x1c\x00Africa/" + + "Sao_TomeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff^<\xfd0\xff\xff\xff\xff\x92掀\x00\x00\x00\x00ZI\x88\x10\x00\x00\x00\x00\\*\xbb\x90\x01\x02\x03\x02\x00\x00\x06P\x00\x00\xff\xff\xf7c\x00\x00\x00" + + "\x00\x00\x00\x00\x04\x00\x00\x0e\x10\x00\bLMT\x00GMT\x00WAT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00A" + + "frica/KampalaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03" + + "\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU6\x99rU\xa4\x00\x00\x00\xa4\x00\x00\x00\x0f\x00\x1c\x00Africa/MonroviaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xffZz\xa6\x9c\xff\xff\xff\xff\xa0_l\x9c\x00\x00\x00\x00" + + "\x03\xcaZn\x01\x02\x03\xff\xff\xf5\xe4\x00\x00\xff\xff\xf5\xe4\x00\x04\xff\xff\xf5\x92\x00\x04\x00\x00\x00\x00\x00\bLMT\x00MMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x10\x00\x1c\x00Africa/MogadishuUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7" + + "\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT" + + "\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU_\x7f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x0e\x00\x1c\x00Africa/TripoliUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x04\x00\x00\x00" + + "\x11\xff\xff\xff\xff\xa1\xf2\xc1$\xff\xff\xff\xffݻ\xb1\x10\xff\xff\xff\xff\xde#\xad`\xff\xff\xff\xff\xe1x\xd2\x10\xff\xff\xff\xff\xe1\xe7e\xe0\xff\xff\xff\xff\xe5/?p\xff\xff\xff\xff\xe5\xa9\xcc\xe0\xff\xff\xff" + + "\xff\xebN\xc6\xf0\x00\x00\x00\x00\x16\x92B`\x00\x00\x00\x00\x17\b\xf7p\x00\x00\x00\x00\x17\xfa+\xe0\x00\x00\x00\x00\x18\xea*\xf0\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0\x00\x00\x00\x00\x1b\xbd\xe4" + + "`\x00\x00\x00\x00\x1c\xb4z\xf0\x00\x00\x00\x00\x1d\x9f\x17\xe0\x00\x00\x00\x00\x1e\x93\vp\x00\x00\x00\x00\x1f\x82\xee`\x00\x00\x00\x00 pJp\x00\x00\x00\x00!a~\xe0\x00\x00\x00\x00\"R\xcfp\x00\x00\x00" + + "\x00#D\x03\xe0\x00\x00\x00\x00$4\x02\xf0\x00\x00\x00\x00%%7`\x00\x00\x00\x00&@\xb7\xf0\x00\x00\x00\x002N\xf1`\x00\x00\x00\x003D6p\x00\x00\x00\x0045j\xe0\x00\x00\x00\x00P\x9d\x99" + + "\x00\x00\x00\x00\x00QTـ\x00\x00\x00\x00Ri\xb4\x80\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x03\x02\x01\x03\x00\x00\f\\\x00\x00\x00\x00\x1c \x01" + + "\x04\x00\x00\x0e\x10\x00\t\x00\x00\x1c \x00\rLMT\x00CEST\x00CET\x00EET\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x93\xf4\x94\v\xc1\x01\x00\x00\xc1" + + "\x01\x00\x00\f\x00\x1c\x00Africa/TunisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffYF\x13\xf4\xff\xff\xff\xff\x91`PO\xff\xff\xff\xff\xc6:\x88\xe0\xff\xff\xff\xff\xc7X\x9e`\xff\xff\xff\xff\xc7\xdb" + + "\"\xe0\xff\xff\xff\xff\xca\xe2T\xe0\xff\xff\xff\xff˭i\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\xcd\xc2\x16\x00\xff\xff\xff\xff\xcd̰\x10\xff\xff\xff\xff\u03a25\x00\xff\xff" + + "\xff\xffϒ4\x10\xff\xff\xff\xffЉ\xe3\xe0\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N\x16`\x00\x00\x00\x00\r\xc7\xdf\xf0\x00\x00\x00\x00\x0e\x89\xacp\x00\x00\x00\x00\x0f\xaad\xf0\x00\x00\x00\x00\x10t" + + "\x1ap\x00\x00\x00\x00\"\xa3:\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&<\xc3p\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00Bt\r\xf0\x00\x00" + + "\x00\x00C<\x80\x00\x00\x00\x00\x00D%\xe7\x90\x00\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x01\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\t\x8c\x00\x00\x00\x00\x021\x00\x04\x00\x00\x1c \x01\b\x00\x00\x0e\x10\x00\rLMT\x00PMT\x00" + + "CEST\x00CET\x00\nCET-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xaa\x81\t\x03\xa0\x00\x00\x00\xa0\x00\x00\x00\x0f\x00\x1c\x00Africa/Ndjamen" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03" + + "\x00\x00\x00\r\xff\xff\xff\xff\x92\xe6\x80d\x00\x00\x00\x00\x12fqp\x00\x00\x00\x00\x13&\xde`\x01\x02\x01\x00\x00\x0e\x1c\x00\x00\x00\x00\x0e\x10\x00\x04\x00\x00\x1c \x01\bLMT\x00WAT\x00WAS" + + "T\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x14\xcf\x10n\xca\x01\x00\x00\xca\x01\x00\x00\v\x00\x1c\x00Africa/JubaUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xb6\xa3\xda" + + "\xdc\x00\x00\x00\x00\x00\x9e\x17\xe0\x00\x00\x00\x00\x01z4P\x00\x00\x00\x00\x02}\xf9\xe0\x00\x00\x00\x00\x03[g\xd0\x00\x00\x00\x00\x04`~\xe0\x00\x00\x00\x00\x05=\xec\xd0\x00\x00\x00\x00\x06@`\xe0\x00\x00\x00" + + "\x00\a\x1f P\x00\x00\x00\x00\b B\xe0\x00\x00\x00\x00\t\x00S\xd0\x00\x00\x00\x00\n\x00$\xe0\x00\x00\x00\x00\n\xe1\x87P\x00\x00\x00\x00\v\xe0\x06\xe0\x00\x00\x00\x00\f\xc4\fP\x00\x00\x00\x00\r\xbf\xe8" + + "\xe0\x00\x00\x00\x00\x0e\xa5?\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00\x00\x10\x86sP\x00\x00\x00\x00\x11\x88\xe7`\x00\x00\x00\x00\x12g\xa6\xd0\x00\x00\x00\x00\x13h\xc9`\x00\x00\x00\x00\x14J+\xd0\x00\x00\x00" + + "\x00\x15H\xab`\x00\x00\x00\x00\x16+_P\x00\x00\x00\x00\x17(\x8d`\x00\x00\x00\x00\x18\f\x92\xd0\x00\x00\x00\x00\x19\bo`\x00\x00\x00\x00\x19\xed\xc6P\x00\x00\x00\x00\x1a\xf1\x8b\xe0\x00\x00\x00\x00\x1b\xd0K" + + "P\x00\x00\x00\x00\x1c\xd1m\xe0\x00\x00\x00\x00\x1d\xb1~\xd0\x00\x00\x00\x008\x80E \x00\x00\x00\x00`\x17\x1aP\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x03\x02\x00\x00\x1d\xa4\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00CAST\x00CAT\x00EAT\x00\nCAT-2\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/DoualaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff" + + "\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00" + + "\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00Africa/TimbuktuUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92" + + "\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00" + + "Africa/HarareUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/LuandaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaa" + "C\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT" + - "-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x12\x00\x1c\x00Africa/Addis_AbabaUT\t\x00\x03\x15\xac\x0e`" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + - "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff" + - "\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&" + - "\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x93\xf4\x94\v\xc1\x01\x00\x00\xc1\x01\x00\x00\f\x00\x1c\x00" + - "Africa/TunisUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffYF\x13\xf4\xff\xff\xff\xff\x91`PO\xff\xff\xff\xff\xc6:\x88\xe0\xff\xff\xff\xff\xc7X\x9e`\xff\xff\xff\xff\xc7\xdb\"\xe0\xff\xff\xff\xff\xca" + - "\xe2T\xe0\xff\xff\xff\xff˭i\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\xcd\xc2\x16\x00\xff\xff\xff\xff\xcd̰\x10\xff\xff\xff\xff\u03a25\x00\xff\xff\xff\xffϒ4\x10\xff" + - "\xff\xff\xffЉ\xe3\xe0\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N\x16`\x00\x00\x00\x00\r\xc7\xdf\xf0\x00\x00\x00\x00\x0e\x89\xacp\x00\x00\x00\x00\x0f\xaad\xf0\x00\x00\x00\x00\x10t\x1ap\x00\x00\x00\x00\"" + - "\xa3:\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&<\xc3p\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00Bt\r\xf0\x00\x00\x00\x00C<\x80\x00\x00" + - "\x00\x00\x00D%\xe7\x90\x00\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\t\x8c\x00\x00\x00\x00\x021\x00\x04\x00\x00\x1c \x01\b\x00\x00\x0e\x10\x00\rLMT\x00PMT\x00CEST\x00CE" + - "T\x00\nCET-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x1c\x00Africa/BanjulUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92" + - "\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x1c\x00" + - "Africa/OuagadougouUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x1c\x00Africa/LibrevilleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP" + - "`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00" + - "WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x12\x00\x1c\x00Africa/BrazzavilleU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00" + - "\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10" + - "\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Afr" + - "ica/BanguiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00" + - "\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00" + - "\x82\x00\x00\x00\x0e\x00\x1c\x00Africa/AbidjanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RÊ\x0e\xc0\xd6\x01\x00\x00\xd6\x01\x00\x00\x0e\x00\x1c\x00Africa/AlgiersUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffkɛ$\xff\xff\xff\xff\x91" + - "`PO\xff\xff\xff\xff\x9bGx\xf0\xff\xff\xff\xff\x9b\xd7,p\xff\xff\xff\xff\x9c\xbc\x91p\xff\xff\xff\xff\x9d\xc0H\xf0\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0*\xf0\xff\xff\xff\xff\xa0`\xa5\xf0\xff" + - "\xff\xff\xff\xa1\x80\f\xf0\xff\xff\xff\xff\xa2.\x12\xf0\xff\xff\xff\xff\xa3zL\xf0\xff\xff\xff\xff\xa45\x81\xf0\xff\xff\xff\xff\xa4\xb8\x06p\xff\xff\xff\xff\xc6\xff\x06p\xff\xff\xff\xff\xc7X\xba\x80\xff\xff\xff\xff\xc7" + - "\xda\t\xa0\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЊ\x00\x00\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N$p\xff\xff\xff\xff\xd4K\ap\xff\xff\xff\xff\xe5\xce\xd3\x00\xff\xff\xff\xff\xf3\\\xb0\xf0\x00" + - "\x00\x00\x00\x02x\xc1\xf0\x00\x00\x00\x00\x03C\xc8\xf0\x00\x00\x00\x00\r\xcf\xd7\x00\x00\x00\x00\x00\x0e\xadD\xf0\x00\x00\x00\x00\x0fxZ\x00\x00\x00\x00\x00\x10hY\x10\x00\x00\x00\x00\x12vCp\x00\x00\x00\x00\x13" + - "fB\x80\x00\x00\x00\x00\x14_|\x10\x00\x00\x00\x00\x15O_\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x03\x05\x03\x02\x03\x02\x05\x04\x05\x03\x02\x03\x05\x00\x00\x02\xdc\x00\x00\x00" + - "\x00\x021\x00\x04\x00\x00\x0e\x10\x01\b\x00\x00\x00\x00\x00\r\x00\x00\x1c \x01\x11\x00\x00\x0e\x10\x00\x16LMT\x00PMT\x00WEST\x00WET\x00CEST\x00CET\x00\nCET-" + - "1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x11\x00\x1c\x00Africa/NouakchottUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUÊ\x0e\xc0\xd6\x01\x00\x00\xd6\x01\x00\x00\x0e\x00\x1c\x00Africa/AlgiersUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffkɛ$\xff\xff" + + "\xff\xff\x91`PO\xff\xff\xff\xff\x9bGx\xf0\xff\xff\xff\xff\x9b\xd7,p\xff\xff\xff\xff\x9c\xbc\x91p\xff\xff\xff\xff\x9d\xc0H\xf0\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0*\xf0\xff\xff\xff\xff\xa0`" + + "\xa5\xf0\xff\xff\xff\xff\xa1\x80\f\xf0\xff\xff\xff\xff\xa2.\x12\xf0\xff\xff\xff\xff\xa3zL\xf0\xff\xff\xff\xff\xa45\x81\xf0\xff\xff\xff\xff\xa4\xb8\x06p\xff\xff\xff\xff\xc6\xff\x06p\xff\xff\xff\xff\xc7X\xba\x80\xff\xff" + + "\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЊ\x00\x00\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N$p\xff\xff\xff\xff\xd4K\ap\xff\xff\xff\xff\xe5\xce\xd3\x00\xff\xff\xff\xff\xf3\\" + + "\xb0\xf0\x00\x00\x00\x00\x02x\xc1\xf0\x00\x00\x00\x00\x03C\xc8\xf0\x00\x00\x00\x00\r\xcf\xd7\x00\x00\x00\x00\x00\x0e\xadD\xf0\x00\x00\x00\x00\x0fxZ\x00\x00\x00\x00\x00\x10hY\x10\x00\x00\x00\x00\x12vCp\x00\x00" + + "\x00\x00\x13fB\x80\x00\x00\x00\x00\x14_|\x10\x00\x00\x00\x00\x15O_\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x03\x05\x03\x02\x03\x02\x05\x04\x05\x03\x02\x03\x05\x00\x00\x02\xdc" + + "\x00\x00\x00\x00\x021\x00\x04\x00\x00\x0e\x10\x01\b\x00\x00\x00\x00\x00\r\x00\x00\x1c \x01\x11\x00\x00\x0e\x10\x00\x16LMT\x00PMT\x00WEST\x00WET\x00CEST\x00CET\x00\nC" + + "ET-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x1c\x00Africa/ConakryUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H" + - "\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc1\n\x8a\x84\xad\x00\x00\x00\xad\x00\x00\x00\x0f\x00\x1c\x00Afr" + - "ica/Sao_TomeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff^<\xfd0\xff\xff\xff\xff\x92掀\x00\x00\x00\x00ZI\x88\x10\x00\x00\x00\x00\\*\xbb\x90\x01\x02\x03\x02\x00\x00\x06P\x00\x00\xff\xff\xf7" + - "c\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x0e\x10\x00\bLMT\x00GMT\x00WAT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x14" + - "\x00\x1c\x00Africa/Dar_es_SalaamUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff" + - "\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-" + - "3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x11\x00\x1c\x00Africa/LubumbashiUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4" + - "\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00Af" + - "rica/KampalaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02" + - "\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x0f\x00\x1c\x00Africa/BlantyreUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04" + - "LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/MalaboUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00" + - "\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00" + - "\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xaa\x81\t\x03\xa0\x00\x00\x00\xa0\x00\x00\x00\x0f\x00\x1c\x00Afri" + - "ca/NdjamenaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\x92\xe6\x80d\x00\x00\x00\x00\x12fqp\x00\x00\x00\x00\x13&\xde`\x01\x02\x01\x00\x00\x0e\x1c\x00\x00\x00\x00\x0e\x10\x00\x04\x00\x00\x1c \x01\bL" + - "MT\x00WAT\x00WAST\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00Africa/Timb" + - "uktuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + - "\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_" + - "\x83\x00\x00\x00\x83\x00\x00\x00\x0f\x00\x1c\x00Africa/GaboroneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\n" + - "CAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rd\x01\x05\x89\u007f\a\x00\x00\u007f\a\x00\x00\x11\x00\x1c\x00Africa/CasablancaUT\t\x00\x03\x15\xac" + - "\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc5\x00\x00\x00\x05\x00\x00\x00\f\xff\xff\xff\xff" + - "\x96Q\xf9\x9c\xff\xff\xff\xff\xc6\xff\x14\x80\xff\xff\xff\xff\xc7X\xacp\xff\xff\xff\xff\xc7\xd9\xed\x80\xff\xff\xff\xffҡ2\xf0\xff\xff\xff\xff\xdb5\xa4\x00\xff\xff\xff\xff\xdb\xee'\xf0\xff\xff\xff\xff\xfb%r@" + - "\xff\xff\xff\xff\xfb\xc2\xefp\x00\x00\x00\x00\bk\x84\x80\x00\x00\x00\x00\b\xc6m\xf0\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00\x00" + - "\x0f\xd3Q\x80\x00\x00\x00\x00\x10'\xa3p\x00\x00\x00\x00\x1a\xb7\xa6\x00\x00\x00\x00\x00\x1e\x18o\xf0\x00\x00\x00\x00HA\xe6\x80\x00\x00\x00\x00H\xbb\"p\x00\x00\x00\x00J#\x1a\x00\x00\x00\x00\x00J\x8d\xd5p" + - "\x00\x00\x00\x00K\xdc\xc0\x80\x00\x00\x00\x00L]\xe5p\x00\x00\x00\x00M\x97\xb8\x80\x00\x00\x00\x00N4\x8c\xf0\x00\x00\x00\x00O\x9c\xa0\xa0\x00\x00\x00\x00P\b\xbb\xa0\x00\x00\x00\x00P1\x9a \x00\x00\x00\x00" + - "Pg\xa7\xa0\x00\x00\x00\x00Q|\x82\xa0\x00\x00\x00\x00Q\xd8ˠ\x00\x00\x00\x00R\x05\x9e\xa0\x00\x00\x00\x00Rls\xa0\x00\x00\x00\x00S7z\xa0\x00\x00\x00\x00S\xae!\xa0\x00\x00\x00\x00S\xdcF " + - "\x00\x00\x00\x00TLU\xa0\x00\x00\x00\x00U\x17\\\xa0\x00\x00\x00\x00U|\xe0 \x00\x00\x00\x00U\xab\x04\xa0\x00\x00\x00\x00V,7\xa0\x00\x00\x00\x00V\xf7>\xa0\x00\x00\x00\x00WS\x87\xa0\x00\x00\x00\x00" + - "W\x81\xac \x00\x00\x00\x00X\x15T \x00\x00\x00\x00X\xd7 \xa0\x00\x00\x00\x00Y \xf4\xa0\x00\x00\x00\x00YXS\xa0\x00\x00\x00\x00Y\xf56 \x00\x00\x00\x00Z\xb7\x02\xa0\x00\x00\x00\x00Z\xf7\x9c " + - "\x00\x00\x00\x00[%\xc0\xa0\x00\x00\x00\x00[\xd5\x18 \x00\x00\x00\x00\\\xceC\xa0\x00\x00\x00\x00\\\xfch \x00\x00\x00\x00^\x9b\xb0\xa0\x00\x00\x00\x00^\xd3\x0f\xa0\x00\x00\x00\x00`rX \x00\x00\x00\x00" + - "`\xa0|\xa0\x00\x00\x00\x00b?\xc5 \x00\x00\x00\x00bw$ \x00\x00\x00\x00d\x16l\xa0\x00\x00\x00\x00dMˠ\x00\x00\x00\x00e\xed\x14 \x00\x00\x00\x00f\x1b8\xa0\x00\x00\x00\x00g\xba\x81 " + - "\x00\x00\x00\x00g\xf1\xe0 \x00\x00\x00\x00i\x91(\xa0\x00\x00\x00\x00i\xbfM \x00\x00\x00\x00kg\xd0 \x00\x00\x00\x00k\x95\xf4\xa0\x00\x00\x00\x00m5= \x00\x00\x00\x00ml\x9c \x00\x00\x00\x00" + - "o\v\xe4\xa0\x00\x00\x00\x00o:\t \x00\x00\x00\x00p\xd9Q\xa0\x00\x00\x00\x00q\x10\xb0\xa0\x00\x00\x00\x00r\xaf\xf9 \x00\x00\x00\x00r\xe7X \x00\x00\x00\x00t\x86\xa0\xa0\x00\x00\x00\x00t\xb4\xc5 " + - "\x00\x00\x00\x00vT\r\xa0\x00\x00\x00\x00v\x8bl\xa0\x00\x00\x00\x00x*\xb5 \x00\x00\x00\x00xX٠\x00\x00\x00\x00y\xf8\" \x00\x00\x00\x00z/\x81 \x00\x00\x00\x00{\xceɠ\x00\x00\x00\x00" + - "|\x06(\xa0\x00\x00\x00\x00}\xa5q \x00\x00\x00\x00}ӕ\xa0\x00\x00\x00\x00\u007fr\xde \x00\x00\x00\x00\u007f\xaa= \x00\x00\x00\x00\x81I\x85\xa0\x00\x00\x00\x00\x81\x80\xe4\xa0\x00\x00\x00\x00\x83 - " + - "\x00\x00\x00\x00\x83NQ\xa0\x00\x00\x00\x00\x84\xed\x9a \x00\x00\x00\x00\x85$\xf9 \x00\x00\x00\x00\x86\xc4A\xa0\x00\x00\x00\x00\x86\xf2f \x00\x00\x00\x00\x88\x91\xae\xa0\x00\x00\x00\x00\x88\xc9\r\xa0\x00\x00\x00\x00" + - "\x8ahV \x00\x00\x00\x00\x8a\x9f\xb5 \x00\x00\x00\x00\x8c>\xfd\xa0\x00\x00\x00\x00\x8cm\" \x00\x00\x00\x00\x8e\fj\xa0\x00\x00\x00\x00\x8eCɠ\x00\x00\x00\x00\x8f\xe3\x12 \x00\x00\x00\x00\x90\x1aq " + - "\x00\x00\x00\x00\x91\xb9\xb9\xa0\x00\x00\x00\x00\x91\xe7\xde \x00\x00\x00\x00\x93\x87&\xa0\x00\x00\x00\x00\x93\xbe\x85\xa0\x00\x00\x00\x00\x95]\xce \x00\x00\x00\x00\x95\x8b\xf2\xa0\x00\x00\x00\x00\x97+; \x00\x00\x00\x00" + - "\x97b\x9a \x00\x00\x00\x00\x99\x01\xe2\xa0\x00\x00\x00\x00\x999A\xa0\x00\x00\x00\x00\x9a؊ \x00\x00\x00\x00\x9b\x06\xae\xa0\x00\x00\x00\x00\x9c\xa5\xf7 \x00\x00\x00\x00\x9c\xddV \x00\x00\x00\x00\x9e|\x9e\xa0" + - "\x00\x00\x00\x00\x9e\xb3\xfd\xa0\x00\x00\x00\x00\xa0SF \x00\x00\x00\x00\xa0\x81j\xa0\x00\x00\x00\x00\xa2 \xb3 \x00\x00\x00\x00\xa2X\x12 \x00\x00\x00\x00\xa3\xf7Z\xa0\x00\x00\x00\x00\xa4%\u007f \x00\x00\x00\x00" + - "\xa5\xc4Ǡ\x00\x00\x00\x00\xa5\xfc&\xa0\x00\x00\x00\x00\xa7\x9bo \x00\x00\x00\x00\xa7\xd2\xce \x00\x00\x00\x00\xa9r\x16\xa0\x00\x00\x00\x00\xa9\xa0; \x00\x00\x00\x00\xab?\x83\xa0\x00\x00\x00\x00\xabv\xe2\xa0" + - "\x00\x00\x00\x00\xad\x16+ \x00\x00\x00\x00\xadM\x8a \x00\x00\x00\x00\xae\xecҠ\x00\x00\x00\x00\xaf\x1a\xf7 \x00\x00\x00\x00\xb0\xba?\xa0\x00\x00\x00\x00\xb0\xf1\x9e\xa0\x00\x00\x00\x00\xb2\x90\xe7 \x00\x00\x00\x00" + - "\xb2\xbf\v\xa0\x00\x00\x00\x00\xb4^T \x00\x00\x00\x00\xb4\x95\xb3 \x00\x00\x00\x00\xb64\xfb\xa0\x00\x00\x00\x00\xb6lZ\xa0\x00\x00\x00\x00\xb8\v\xa3 \x00\x00\x00\x00\xb89Ǡ\x00\x00\x00\x00\xb9\xd9\x10 " + - "\x00\x00\x00\x00\xba\x10o \x00\x00\x00\x00\xbb\xaf\xb7\xa0\x00\x00\x00\x00\xbb\xe7\x16\xa0\x00\x00\x00\x00\xbd\x86_ \x00\x00\x00\x00\xbd\xb4\x83\xa0\x00\x00\x00\x00\xbfS\xcc \x00\x00\x00\x00\xbf\x8b+ \x00\x00\x00\x00" + - "\xc1*s\xa0\x00\x00\x00\x00\xc1X\x98 \x00\x00\x00\x00\xc2\xf7\xe0\xa0\x00\x00\x00\x00\xc3/?\xa0\x00\x00\x00\x00\xc4Έ \x00\x00\x00\x00\xc5\x05\xe7 \x00\x00\x00\x00ƥ/\xa0\x00\x00\x00\x00\xc6\xd3T " + - "\x00\x00\x00\x00\xc8r\x9c\xa0\x00\x00\x00\x00ȩ\xfb\xa0\x00\x00\x00\x00\xcaID \x00\x00\x00\x00ʀ\xa3 \x00\x00\x00\x00\xcc\x1f\xeb\xa0\x00\x00\x00\x00\xccN\x10 \x00\x00\x00\x00\xcd\xedX\xa0\x00\x00\x00\x00" + - "\xce$\xb7\xa0\x00\x00\x00\x00\xcf\xc4\x00 \x00\x00\x00\x00\xcf\xf2$\xa0\x00\x00\x00\x00ёm \x00\x00\x00\x00\xd1\xc8\xcc \x00\x00\x00\x00\xd3h\x14\xa0\x00\x00\x00\x00ӟs\xa0\x00\x00\x00\x00\xd5>\xbc " + - "\x00\x00\x00\x00\xd5l\xe0\xa0\x00\x00\x00\x00\xd7\f) \x00\x00\x00\x00\xd7C\x88 \x00\x00\x00\x00\xd8\xe2Р\x00\x00\x00\x00\xd9\x1a/\xa0\x00\x00\x00\x00ڹx \x00\x00\x00\x00\xda眠\x00\x00\x00\x00" + - "܆\xe5 \x00\x00\x00\x00ܾD \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\xff\xff\xf8\xe4\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x0e\x10\x00\x04\x00\x00\x00\x00\x01\bL" + - "MT\x00+01\x00+00\x00\n<+01>-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/Map" + - "utoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c" + - "\xb4\x00\x00\x00\xb4\x00\x00\x00\f\x00\x1c\x00Africa/LagosUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01" + - "\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\r\x00\x1c\x00Africa/MaseruUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x11\x00\x1c\x00Afr" + + "ica/NouakchottUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\r\x00\x1c\x00Africa/MaseruUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\t\xff\xff\xff\xffm{A@\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff\xff̮" + "\x8c\x80\xff\xff\xff\xff͞op\xff\xff\xff\xffΎn\x80\xff\xff\xff\xff\xcf~Qp\x01\x03\x02\x03\x02\x03\x00\x00\x1a@\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00*0\x01\x04\x00\x00\x1c \x00\x04LMT\x00" + - "SAST\x00\nSAST-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x1c\x00Africa/Porto-Novo" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00" + - "\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e" + - "\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x1c\x00Af" + - "rica/ConakryUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "SAST\x00\nSAST-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x13\x00\x1c\x00Africa/Johannesbu" + + "rgUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00" + + "\x04\x00\x00\x00\t\xff\xff\xff\xffm{A@\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff\xff̮\x8c\x80\xff\xff\xff\xff͞op\xff\xff\xff\xffΎn\x80\xff\xff\xff\xff\xcf~Qp\x01\x03\x02\x03\x02\x03\x00" + + "\x00\x1a@\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00*0\x01\x04\x00\x00\x1c \x00\x04LMT\x00SAST\x00\nSAST-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f\x1b\xeb\xdd2\x02" + + "\x00\x002\x02\x00\x00\f\x00\x1c\x00Africa/CeutaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff~6\xb5\x00\xff\xff\xff\xff\x9e\xd6up\xff\xff\xff\xff\x9f\xa1n`\xff\xff\xff\xff\xaa\x05\xefp\xff\xff\xff" + + "\xff\xaa\xe7n\x00\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89z\x00\xff\xff\xff\xff\xb2p0\x80\xff\xff\xff\xff\xfb%r" + + "@\xff\xff\xff\xff\xfb\xc2\xefp\x00\x00\x00\x00\bk\x84\x80\x00\x00\x00\x00\b\xc6m\xf0\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00" + + "\x00\x0f\xd3Q\x80\x00\x00\x00\x00\x10'\xa3p\x00\x00\x00\x00\x1a\xb7\xa6\x00\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT" + + "\x10\x00\x00\x00\x00#\xa8^`\x00\x00\x00\x00?sWP\x00\x00\x00\x00@\x91z\xe0\x00\x00\x00\x00A\\s\xd0\x00\x00\x00\x00Bq\\\xe0\x00\x00\x00\x00C\xe0\x00\x00\x00\x00E\x12\xfd" + + "P\x00\x00\x00\x00F1 \xe0\x00\x00\x00\x00F\xe0jP\x00\x00\x00\x00H\x11\x02\xe0\x00\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00I\xf0\xe4\xe0\x00\x00\x00\x00J\x8d\xb9P\x00\x00\x00\x00K\xda\x01`\x00\x00\x00" + + "\x00La\xbd\xd0\x00\x00\x00\x00L\x89X\xe0\x00\x00\x00\x00L\xa4\xfaP\x00\x00\x00\x00Su8\xe0\x00\x00\x00\x00S\xac\x89\xd0\x00\x00\x00\x00Sڼ`\x00\x00\x00\x00T$\x82P\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x00\x00\x1dU\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00" + + "\x83\x00\x00\x00\x11\x00\x1c\x00Africa/LubumbashiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCA" + + "T-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x1c\x00Africa/BanjulUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff" + + "\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUd\x01\x05\x89\x7f\a\x00\x00\x7f\a\x00\x00\x11\x00\x1c\x00Afric" + + "a/CasablancaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x1c\x00Africa/DoualaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1" + - "\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT\x00+0030\x00WAT\x00\nWAT-1" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x10\x00\x1c\x00Africa/MogadishuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff" + - "\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eL" + - "MT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\f\x00\x1c\x00Afri" + - "ca/DakarUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xc5\x00\x00\x00\x05\x00\x00\x00\f\xff\xff\xff\xff\x96Q\xf9\x9c\xff\xff\xff\xff\xc6\xff\x14\x80\xff\xff\xff\xff\xc7X\xacp\xff\xff\xff\xff\xc7\xd9\xed\x80\xff\xff\xff\xffҡ2\xf0\xff\xff\xff\xff\xdb" + + "5\xa4\x00\xff\xff\xff\xff\xdb\xee'\xf0\xff\xff\xff\xff\xfb%r@\xff\xff\xff\xff\xfb\xc2\xefp\x00\x00\x00\x00\bk\x84\x80\x00\x00\x00\x00\b\xc6m\xf0\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\faG\xf0\x00" + + "\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0e\x8e\xf2p\x00\x00\x00\x00\x0f\xd3Q\x80\x00\x00\x00\x00\x10'\xa3p\x00\x00\x00\x00\x1a\xb7\xa6\x00\x00\x00\x00\x00\x1e\x18o\xf0\x00\x00\x00\x00HA\xe6\x80\x00\x00\x00\x00H" + + "\xbb\"p\x00\x00\x00\x00J#\x1a\x00\x00\x00\x00\x00J\x8d\xd5p\x00\x00\x00\x00K\xdc\xc0\x80\x00\x00\x00\x00L]\xe5p\x00\x00\x00\x00M\x97\xb8\x80\x00\x00\x00\x00N4\x8c\xf0\x00\x00\x00\x00O\x9c\xa0\xa0\x00" + + "\x00\x00\x00P\b\xbb\xa0\x00\x00\x00\x00P1\x9a \x00\x00\x00\x00Pg\xa7\xa0\x00\x00\x00\x00Q|\x82\xa0\x00\x00\x00\x00Q\xd8ˠ\x00\x00\x00\x00R\x05\x9e\xa0\x00\x00\x00\x00Rls\xa0\x00\x00\x00\x00S" + + "7z\xa0\x00\x00\x00\x00S\xae!\xa0\x00\x00\x00\x00S\xdcF \x00\x00\x00\x00TLU\xa0\x00\x00\x00\x00U\x17\\\xa0\x00\x00\x00\x00U|\xe0 \x00\x00\x00\x00U\xab\x04\xa0\x00\x00\x00\x00V,7\xa0\x00" + + "\x00\x00\x00V\xf7>\xa0\x00\x00\x00\x00WS\x87\xa0\x00\x00\x00\x00W\x81\xac \x00\x00\x00\x00X\x15T \x00\x00\x00\x00X\xd7 \xa0\x00\x00\x00\x00Y \xf4\xa0\x00\x00\x00\x00YXS\xa0\x00\x00\x00\x00Y" + + "\xf56 \x00\x00\x00\x00Z\xb7\x02\xa0\x00\x00\x00\x00Z\xf7\x9c \x00\x00\x00\x00[%\xc0\xa0\x00\x00\x00\x00[\xd5\x18 \x00\x00\x00\x00\\\xceC\xa0\x00\x00\x00\x00\\\xfch \x00\x00\x00\x00^\x9b\xb0\xa0\x00" + + "\x00\x00\x00^\xd3\x0f\xa0\x00\x00\x00\x00`rX \x00\x00\x00\x00`\xa0|\xa0\x00\x00\x00\x00b?\xc5 \x00\x00\x00\x00bw$ \x00\x00\x00\x00d\x16l\xa0\x00\x00\x00\x00dMˠ\x00\x00\x00\x00e" + + "\xed\x14 \x00\x00\x00\x00f\x1b8\xa0\x00\x00\x00\x00g\xba\x81 \x00\x00\x00\x00g\xf1\xe0 \x00\x00\x00\x00i\x91(\xa0\x00\x00\x00\x00i\xbfM \x00\x00\x00\x00kg\xd0 \x00\x00\x00\x00k\x95\xf4\xa0\x00" + + "\x00\x00\x00m5= \x00\x00\x00\x00ml\x9c \x00\x00\x00\x00o\v\xe4\xa0\x00\x00\x00\x00o:\t \x00\x00\x00\x00p\xd9Q\xa0\x00\x00\x00\x00q\x10\xb0\xa0\x00\x00\x00\x00r\xaf\xf9 \x00\x00\x00\x00r" + + "\xe7X \x00\x00\x00\x00t\x86\xa0\xa0\x00\x00\x00\x00t\xb4\xc5 \x00\x00\x00\x00vT\r\xa0\x00\x00\x00\x00v\x8bl\xa0\x00\x00\x00\x00x*\xb5 \x00\x00\x00\x00xX٠\x00\x00\x00\x00y\xf8\" \x00" + + "\x00\x00\x00z/\x81 \x00\x00\x00\x00{\xceɠ\x00\x00\x00\x00|\x06(\xa0\x00\x00\x00\x00}\xa5q \x00\x00\x00\x00}ӕ\xa0\x00\x00\x00\x00\x7fr\xde \x00\x00\x00\x00\x7f\xaa= \x00\x00\x00\x00\x81" + + "I\x85\xa0\x00\x00\x00\x00\x81\x80\xe4\xa0\x00\x00\x00\x00\x83 - \x00\x00\x00\x00\x83NQ\xa0\x00\x00\x00\x00\x84\xed\x9a \x00\x00\x00\x00\x85$\xf9 \x00\x00\x00\x00\x86\xc4A\xa0\x00\x00\x00\x00\x86\xf2f \x00" + + "\x00\x00\x00\x88\x91\xae\xa0\x00\x00\x00\x00\x88\xc9\r\xa0\x00\x00\x00\x00\x8ahV \x00\x00\x00\x00\x8a\x9f\xb5 \x00\x00\x00\x00\x8c>\xfd\xa0\x00\x00\x00\x00\x8cm\" \x00\x00\x00\x00\x8e\fj\xa0\x00\x00\x00\x00\x8e" + + "Cɠ\x00\x00\x00\x00\x8f\xe3\x12 \x00\x00\x00\x00\x90\x1aq \x00\x00\x00\x00\x91\xb9\xb9\xa0\x00\x00\x00\x00\x91\xe7\xde \x00\x00\x00\x00\x93\x87&\xa0\x00\x00\x00\x00\x93\xbe\x85\xa0\x00\x00\x00\x00\x95]\xce \x00" + + "\x00\x00\x00\x95\x8b\xf2\xa0\x00\x00\x00\x00\x97+; \x00\x00\x00\x00\x97b\x9a \x00\x00\x00\x00\x99\x01\xe2\xa0\x00\x00\x00\x00\x999A\xa0\x00\x00\x00\x00\x9a؊ \x00\x00\x00\x00\x9b\x06\xae\xa0\x00\x00\x00\x00\x9c" + + "\xa5\xf7 \x00\x00\x00\x00\x9c\xddV \x00\x00\x00\x00\x9e|\x9e\xa0\x00\x00\x00\x00\x9e\xb3\xfd\xa0\x00\x00\x00\x00\xa0SF \x00\x00\x00\x00\xa0\x81j\xa0\x00\x00\x00\x00\xa2 \xb3 \x00\x00\x00\x00\xa2X\x12 \x00" + + "\x00\x00\x00\xa3\xf7Z\xa0\x00\x00\x00\x00\xa4%\x7f \x00\x00\x00\x00\xa5\xc4Ǡ\x00\x00\x00\x00\xa5\xfc&\xa0\x00\x00\x00\x00\xa7\x9bo \x00\x00\x00\x00\xa7\xd2\xce \x00\x00\x00\x00\xa9r\x16\xa0\x00\x00\x00\x00\xa9" + + "\xa0; \x00\x00\x00\x00\xab?\x83\xa0\x00\x00\x00\x00\xabv\xe2\xa0\x00\x00\x00\x00\xad\x16+ \x00\x00\x00\x00\xadM\x8a \x00\x00\x00\x00\xae\xecҠ\x00\x00\x00\x00\xaf\x1a\xf7 \x00\x00\x00\x00\xb0\xba?\xa0\x00" + + "\x00\x00\x00\xb0\xf1\x9e\xa0\x00\x00\x00\x00\xb2\x90\xe7 \x00\x00\x00\x00\xb2\xbf\v\xa0\x00\x00\x00\x00\xb4^T \x00\x00\x00\x00\xb4\x95\xb3 \x00\x00\x00\x00\xb64\xfb\xa0\x00\x00\x00\x00\xb6lZ\xa0\x00\x00\x00\x00\xb8" + + "\v\xa3 \x00\x00\x00\x00\xb89Ǡ\x00\x00\x00\x00\xb9\xd9\x10 \x00\x00\x00\x00\xba\x10o \x00\x00\x00\x00\xbb\xaf\xb7\xa0\x00\x00\x00\x00\xbb\xe7\x16\xa0\x00\x00\x00\x00\xbd\x86_ \x00\x00\x00\x00\xbd\xb4\x83\xa0\x00" + + "\x00\x00\x00\xbfS\xcc \x00\x00\x00\x00\xbf\x8b+ \x00\x00\x00\x00\xc1*s\xa0\x00\x00\x00\x00\xc1X\x98 \x00\x00\x00\x00\xc2\xf7\xe0\xa0\x00\x00\x00\x00\xc3/?\xa0\x00\x00\x00\x00\xc4Έ \x00\x00\x00\x00\xc5" + + "\x05\xe7 \x00\x00\x00\x00ƥ/\xa0\x00\x00\x00\x00\xc6\xd3T \x00\x00\x00\x00\xc8r\x9c\xa0\x00\x00\x00\x00ȩ\xfb\xa0\x00\x00\x00\x00\xcaID \x00\x00\x00\x00ʀ\xa3 \x00\x00\x00\x00\xcc\x1f\xeb\xa0\x00" + + "\x00\x00\x00\xccN\x10 \x00\x00\x00\x00\xcd\xedX\xa0\x00\x00\x00\x00\xce$\xb7\xa0\x00\x00\x00\x00\xcf\xc4\x00 \x00\x00\x00\x00\xcf\xf2$\xa0\x00\x00\x00\x00ёm \x00\x00\x00\x00\xd1\xc8\xcc \x00\x00\x00\x00\xd3" + + "h\x14\xa0\x00\x00\x00\x00ӟs\xa0\x00\x00\x00\x00\xd5>\xbc \x00\x00\x00\x00\xd5l\xe0\xa0\x00\x00\x00\x00\xd7\f) \x00\x00\x00\x00\xd7C\x88 \x00\x00\x00\x00\xd8\xe2Р\x00\x00\x00\x00\xd9\x1a/\xa0\x00" + + "\x00\x00\x00ڹx \x00\x00\x00\x00\xda眠\x00\x00\x00\x00܆\xe5 \x00\x00\x00\x00ܾD \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\xff\xff\xf8\xe4\x00\x00\x00\x00\x0e\x10\x01\x04" + + "\x00\x00\x00\x00\x00\b\x00\x00\x0e\x10\x00\x04\x00\x00\x00\x00\x01\bLMT\x00+01\x00+00\x00\n<+01>-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00" + + "\x00\x82\x00\x00\x00\x0f\x00\x1c\x00Africa/FreetownUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT" + + "0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x12\x00\x1c\x00Africa/BrazzavilleUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x86\xabp" + + "\xd1\xff\xff\xff\xff\x8cP`\x00\xff\xff\xff\xff\x96\xaaC\xd1\xff\xff\xff\xff\xa1Q\xefx\x01\x00\x02\x03\x00\x00\x03/\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\a\b\x00\b\x00\x00\x0e\x10\x00\x0eLMT\x00GMT" + + "\x00+0030\x00WAT\x00\nWAT-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x14\x00\x1c\x00Africa/Dar_e" + + "s_SalaamUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "_\u007f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x0e\x00\x1c\x00Africa/TripoliUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa1\xf2\xc1$\xff\xff\xff\xffݻ\xb1\x10\xff\xff\xff\xff\xde#\xad`\xff\xff\xff" + - "\xff\xe1x\xd2\x10\xff\xff\xff\xff\xe1\xe7e\xe0\xff\xff\xff\xff\xe5/?p\xff\xff\xff\xff\xe5\xa9\xcc\xe0\xff\xff\xff\xff\xebN\xc6\xf0\x00\x00\x00\x00\x16\x92B`\x00\x00\x00\x00\x17\b\xf7p\x00\x00\x00\x00\x17\xfa+" + - "\xe0\x00\x00\x00\x00\x18\xea*\xf0\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0\x00\x00\x00\x00\x1b\xbd\xe4`\x00\x00\x00\x00\x1c\xb4z\xf0\x00\x00\x00\x00\x1d\x9f\x17\xe0\x00\x00\x00\x00\x1e\x93\vp\x00\x00\x00" + - "\x00\x1f\x82\xee`\x00\x00\x00\x00 pJp\x00\x00\x00\x00!a~\xe0\x00\x00\x00\x00\"R\xcfp\x00\x00\x00\x00#D\x03\xe0\x00\x00\x00\x00$4\x02\xf0\x00\x00\x00\x00%%7`\x00\x00\x00\x00&@\xb7" + - "\xf0\x00\x00\x00\x002N\xf1`\x00\x00\x00\x003D6p\x00\x00\x00\x0045j\xe0\x00\x00\x00\x00P\x9d\x99\x00\x00\x00\x00\x00QTـ\x00\x00\x00\x00Ri\xb4\x80\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x03\x02\x01\x03\x00\x00\f\\\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00\x1c \x00\rLMT\x00CEST\x00CET\x00EE" + - "T\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x1c\x00Africa/HarareUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x82" + - "F\xc5\xf4\x01\x00\x00\x1e\x8c\x00\x00\x00\x00\x1c \x00\x04LMT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x1c" + - "\x00Africa/AsmaraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01" + - "\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9Rm)\xb8P~\x02\x00\x00~\x02\x00\x00\x0f\x00\x1c\x00Africa/WindhoekUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x06\x00\x00\x00\x17\xff\xff\xff\xffm{Kx\xff\xff\xff\xff\x82F\xcfh\xff\xff\xff" + - "\xff̮\x8c\x80\xff\xff\xff\xff͞op\x00\x00\x00\x00&\x06\xa7\xe0\x00\x00\x00\x00-\x8c\xc7`\x00\x00\x00\x00.i\x1c\x10\x00\x00\x00\x00/}\xe9\x00\x00\x00\x00\x000H\xfe\x10\x00\x00\x00\x001g\x05" + - "\x80\x00\x00\x00\x002(\xe0\x10\x00\x00\x00\x003F\xe7\x80\x00\x00\x00\x004\x11\xfc\x90\x00\x00\x00\x005&ɀ\x00\x00\x00\x005\xf1ސ\x00\x00\x00\x007\x06\xab\x80\x00\x00\x00\x007\xd1\xc0\x90\x00\x00\x00" + - "\x008捀\x00\x00\x00\x009\xb1\xa2\x90\x00\x00\x00\x00:\xc6o\x80\x00\x00\x00\x00;\x91\x84\x90\x00\x00\x00\x00<\xaf\x8c\x00\x00\x00\x00\x00=qf\x90\x00\x00\x00\x00>\x8fn\x00\x00\x00\x00\x00?Z\x83" + - "\x10\x00\x00\x00\x00@oP\x00\x00\x00\x00\x00A:e\x10\x00\x00\x00\x00BO2\x00\x00\x00\x00\x00C\x1aG\x10\x00\x00\x00\x00D/\x14\x00\x00\x00\x00\x00D\xfa)\x10\x00\x00\x00\x00F\x0e\xf6\x00\x00\x00\x00" + - "\x00F\xda\v\x10\x00\x00\x00\x00G\xf8\x12\x80\x00\x00\x00\x00H\xc3'\x90\x00\x00\x00\x00I\xd7\xf4\x80\x00\x00\x00\x00J\xa3\t\x90\x00\x00\x00\x00K\xb7ր\x00\x00\x00\x00L\x82\xeb\x90\x00\x00\x00\x00M\x97\xb8" + - "\x80\x00\x00\x00\x00Nb͐\x00\x00\x00\x00Ow\x9a\x80\x00\x00\x00\x00PB\xaf\x90\x00\x00\x00\x00Q`\xb7\x00\x00\x00\x00\x00R\"\x91\x90\x00\x00\x00\x00S@\x99\x00\x00\x00\x00\x00T\v\xae\x10\x00\x00\x00" + - "\x00U {\x00\x00\x00\x00\x00U\xeb\x90\x10\x00\x00\x00\x00W\x00]\x00\x00\x00\x00\x00W\xcbr\x10\x00\x00\x00\x00X\xe0?\x00\x00\x00\x00\x00Y\xabT\x10\x01\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x00\x00\x10\b\x00\x00\x00\x00\x15\x18\x00\x04\x00\x00\x1c \x00\n\x00\x00*0" + - "\x01\n\x00\x00\x0e\x10\x01\x0f\x00\x00\x1c \x00\x13LMT\x00+0130\x00SAST\x00WAT\x00CAT\x00\nCAT-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xee\xc4" + - "h2\xbc\x02\x00\x00\xbc\x02\x00\x00\f\x00\x1c\x00Africa/AccraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x9a\x1d\x944\xff\xff\xff\xff\xa1\xc0\xb4\x80\xff\xff\xff\xff\xa1\xf2\xe4\xf0\xff\xff\xff\xff\xa34\x97" + - "\xa0\xff\xff\xff\xff\xa3\xd5i\xf0\xff\xff\xff\xff\xa5\x15\xcb \xff\xff\xff\xff\xa5\xb6\x9dp\xff\xff\xff\xff\xa6\xf6\xfe\xa0\xff\xff\xff\xff\xa7\x97\xd0\xf0\xff\xff\xff\xff\xa8\xd82 \xff\xff\xff\xff\xa9y\x04p\xff\xff\xff" + - "\xff\xaa\xba\xb7 \xff\xff\xff\xff\xab[\x89p\xff\xff\xff\xff\xac\x9b\xea\xa0\xff\xff\xff\xff\xad<\xbc\xf0\xff\xff\xff\xff\xae}\x1e \xff\xff\xff\xff\xaf\x1d\xf0p\xff\xff\xff\xff\xb0^Q\xa0\xff\xff\xff\xff\xb0\xff#" + - "\xf0\xff\xff\xff\xff\xb2@֠\xff\xff\xff\xff\xb2\xe1\xa8\xf0\xff\xff\xff\xff\xb4\"\n \xff\xff\xff\xff\xb4\xc2\xdcp\xff\xff\xff\xff\xb6\x03=\xa0\xff\xff\xff\xff\xb6\xa4\x0f\xf0\xff\xff\xff\xff\xb7\xe4q \xff\xff\xff" + - "\xff\xb8\x85Cp\xff\xff\xff\xff\xb9\xc6\xf6 \xff\xff\xff\xff\xbag\xc8p\xff\xff\xff\xff\xbb\xa8)\xa0\xff\xff\xff\xff\xbcH\xfb\xf0\xff\xff\xff\xff\xbd\x89] \xff\xff\xff\xff\xbe*/p\xff\xff\xff\xff\xbfj\x90" + - "\xa0\xff\xff\xff\xff\xc0\vb\xf0\xff\xff\xff\xff\xc1M\x15\xa0\xff\xff\xff\xff\xc1\xed\xe7\xf0\xff\xff\xff\xff\xc3.I \xff\xff\xff\xff\xc3\xcf\x1bp\xff\xff\xff\xff\xc5\x0f|\xa0\xff\xff\xff\xffŰN\xf0\xff\xff\xff" + - "\xff\xc6\xf0\xb0 \xff\xff\xff\xffǑ\x82p\xff\xff\xff\xff\xc81\f\xa0\xff\xff\xff\xff\xc9t\ap\xff\xff\xff\xff\xca\x12@ \xff\xff\xff\xff\xcbU:\xf0\xff\xff\xff\xffˇ<\x80\xff\xff\xff\xff\xd2\xe1\xd3" + - "x\xff\xff\xff\xffۡ\xdb \xff\xff\xff\xff\xdcB\xab\x18\xff\xff\xff\xff݃\x0e\xa0\xff\xff\xff\xff\xde#ޘ\xff\xff\xff\xff\xdfe\x93\xa0\xff\xff\xff\xff\xe0\x06c\x98\xff\xff\xff\xff\xe1F\xc7 \xff\xff\xff" + - "\xff\xe1\xe7\x97\x18\xff\xff\xff\xff\xe3'\xfa\xa0\xff\xff\xff\xff\xe3\xc8ʘ\xff\xff\xff\xff\xe5\t. \xff\xff\xff\xff\xe5\xa9\xfe\x18\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\xff\xff\xff\xcc\x00\x00\x00\x00\x04\xb0\x01\x04\x00\x00\x00\x00\x00\n\x00\x00\a\b" + - "\x00\x0e\x00\x00\a\b\x01\x0eLMT\x00+0020\x00GMT\x00+0030\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00" + - "\v\x00\x1c\x00Africa/LomeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0f\x00\x1c\x00Africa/DjiboutiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff" + - "\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00" + - "EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x1c\x00America/UT\t\x00\x03" + - "\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x82\x13z\xe2\xc2\x00\x00\x00\xc2\x00\x00\x00\x13\x00\x1c\x00America" + - "/TegucigalpaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa4LKD\x00\x00\x00\x00 \x9a\xdc\xe0\x00\x00\x00\x00!\\\x9bP\x00\x00\x00\x00\"z\xbe\xe0\x00\x00\x00\x00#<}P\x00\x00\x00\x00D" + - "]\x8c\xe0\x00\x00\x00\x00D\xd6\xc8\xd0\x02\x01\x02\x01\x02\x01\x02\xff\xff\xae<\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00CDT\x00CST\x00\nCST6\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x1c\x00America/St_KittsUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff" + - "\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x13\x00\x1c\x00America/Puer" + - "to_RicoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff" + - "\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xdf\b\x9c\x9f\xe7\x00\x00\x00\xe7\x00\x00\x00\x10\x00" + - "\x1c\x00America/BarbadosUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\xa9y$\xe5\xff\xff\xff\xff\xb8\x85c\xe5\x00\x00\x00\x00\x0e\x00\xf2\xe0\x00\x00\x00\x00\x0e\x94\x8c\xd0\x00\x00\x00\x00\x0f\x97\x00" + - "\xe0\x00\x00\x00\x00\x10tn\xd0\x00\x00\x00\x00\x11v\xe2\xe0\x00\x00\x00\x00\x12TP\xd0\x00\x00\x00\x00\x13_\xff`\x00\x00\x00\x00\x140>P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xc8\x1b\x00\x00\xff\xff\xc8" + - "\x1b\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\fLMT\x00BMT\x00ADT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x14\xc1r8\xe0\x00\x00\x00\xe0" + - "\x00\x00\x00\x10\x00\x1c\x00America/AtikokanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xffr\xee\x84d\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xc8\xf8W`\xff\xff" + - "\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\x02\x01\x02\x01\x03\x04\x05\xff\xff\xaa\x1c\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff" + - "\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00" + - "\x00\x10\x00\x1c\x00America/DominicaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xac\x8a\x83S\xd4\x00\x00\x00\xd4\x00\x00\x00\x11\x00\x1c\x00America/GuatemalaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9f\x9d\xea\xdc\x00\x00\x00" + - "\x00\aU\xac`\x00\x00\x00\x00\a͖\xd0\x00\x00\x00\x00\x19,x`\x00\x00\x00\x00\x19\xcf\xe4P\x00\x00\x00\x00'\xea\xee\xe0\x00\x00\x00\x00(\xc8\\\xd0\x00\x00\x00\x00DTR`\x00\x00\x00\x00E\x1fK" + - "P\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xab$\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00CDT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\x1e+}\x15\xb4\x02\x00\x00\xb4\x02\x00\x00\x14\x00\x1c\x00America/Rankin_InletUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xe7\x8cn\x00\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8" + - "(w\xe0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00" + - "\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 " + - "v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00" + - "\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00." + - "\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00" + - "\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<" + - "\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00" + - "\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xab\xa0\x00\t\xff\xff\xb9\xb0\x01\r\xff\xff\xb9\xb0\x00\x11-00\x00CDDT\x00" + - "CST\x00CDT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00" + - "\x00\x00\x0f\x00\x1c\x00America/TortolaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R<\xb9\x18\x87\xe4\x02\x00\x00\xe4\x02\x00\x00\x0f\x00\x1c\x00America/IqaluitUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\b\x00\x00\x00!\xff\xff\xff\xff\xccl\xa1\x80\xff\xff\xff\xff\xd2" + - "#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xf7/>P\xff\xff\xff\xff\xf8(i\xd0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00" + - "\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d" + - "\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00" + - "\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+" + - "\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00" + - "\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009" + - "\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00" + - "\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x05\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02" + - "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x06\a\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff" + - "\xff\xb9\xb0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xc7\xc0\x01\x11\xff\xff\xc7\xc0\x01\x15\xff\xff\xab\xa0\x00\x19\xff\xff\xb9\xb0\x01\x1d-00\x00EPT\x00EST\x00EDDT\x00EDT\x00EWT\x00" + - "CST\x00CDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd7\b\\\xc6&\x02\x00\x00&\x02\x00\x00\x10\x00" + - "\x1c\x00America/MiquelonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x91\xb68\xa8\x00\x00\x00\x00\x13nc\xc0\x00\x00\x00\x00 u\xe4\xd0\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"U\xc6" + - "\xd0\x00\x00\x00\x00#j\x93\xc0\x00\x00\x00\x00$5\xa8\xd0\x00\x00\x00\x00%Ju\xc0\x00\x00\x00\x00&\x15\x8a\xd0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00)\n9\xc0\x00\x00\x00" + - "\x00)މP\x00\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+\xbekP\x00\x00\x00\x00,\xd38@\x00\x00\x00\x00-\x9eMP\x00\x00\x00\x00.\xb3\x1a@\x00\x00\x00\x00/~/P\x00\x00\x00\x000\x92\xfc" + - "@\x00\x00\x00\x001gK\xd0\x00\x00\x00\x002r\xde@\x00\x00\x00\x003G-\xd0\x00\x00\x00\x004R\xc0@\x00\x00\x00\x005'\x0f\xd0\x00\x00\x00\x0062\xa2@\x00\x00\x00\x007\x06\xf1\xd0\x00\x00\x00" + - "\x008\x1b\xbe\xc0\x00\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xa0\xc0\x00\x00\x00\x00:Ƶ\xd0\x00\x00\x00\x00;ۂ\xc0\x00\x00\x00\x00<\xaf\xd2P\x00\x00\x00\x00=\xbbd\xc0\x00\x00\x00\x00>\x8f\xb4" + - "P\x00\x00\x00\x00?\x9bF\xc0\x00\x00\x00\x00@o\x96P\x00\x00\x00\x00A\x84c@\x00\x00\x00\x00BOxP\x00\x00\x00\x00CdE@\x00\x00\x00\x00D/ZP\x00\x00\x00\x00ED'@\x00\x00\x00" + - "\x00E\xf3\x8c\xd0\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xcbX\x00\x00\xff\xff\xc7\xc0\x00\x04" + - "\xff\xff\xd5\xd0\x00\b\xff\xff\xe3\xe0\x01\fLMT\x00AST\x00-03\x00-02\x00\n<-03>3<-02>,M3.2.0,M11.1.0\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x11Z\xde\xe4\x01\x00\x00\xe4\x01\x00\x00\x11\x00\x1c\x00America/FortalezaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaak\x18\xff\xff\xff\xff\xb8\x0f" + - "I\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff" + - "\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e" + - "\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00" + - "\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7" + - "\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x009\xf2J \x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x003\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd6\xfe\xf3%\xb4\x02\x00\x00\xb4\x02\x00\x00\x10\x00\x1c\x00America/Resolut" + - "eUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x05" + - "\x00\x00\x00\x15\xff\xff\xff\xff\xd5\xfb\x81\x80\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(w\xe0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0" + - "\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00" + - "\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00" + - "\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00" + - "+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp" + - "\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x00" + - "9\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80" + - "\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff" + - "\xab\xa0\x00\t\xff\xff\xb9\xb0\x01\r\xff\xff\xb9\xb0\x00\x11-00\x00CDDT\x00CST\x00CDT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x11\x00\x1c\x00America/St_ThomasUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + - "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01" + - "\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xbf\x03u\xf3\xe4\x01\x00\x00\xe4\x01\x00\x00\x0e\x00\x1c\x00Amer" + - "ica/RecifeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaag\xb8\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae" + - "0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff" + - "\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8" + - "\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00" + - "\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe3" + - "0\x00\x00\x00\x009\xe9\x0f\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x003\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x19vv\xa0" + - "\x97\x00\x00\x00\x97\x00\x00\x00\x0f\x00\x1c\x00America/CuracaoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\x93\x1e.#\xff\xff\xff\xff\xf6\x98\xecH\x01\x02\xff\xff\xbf]\x00\x00\xff\xff\xc0\xb8\x00\x04" + - "\xff\xff\xc7\xc0\x00\nLMT\x00-0430\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x17jҲ\x00\x00\x00\xb2\x00\x00\x00\x12\x00\x1c\x00Amer" + - "ica/MartiniqueUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffi\x87\x14\xc4\xff\xff\xff\xff\x91\xa3\xc8D\x00\x00\x00\x00\x13Mn@\x00\x00\x00\x00\x144\x16\xb0\x01\x02\x03\x02\xff\xffƼ\x00\x00\xff" + - "\xffƼ\x00\x04\xff\xff\xc7\xc0\x00\t\xff\xff\xd5\xd0\x01\rLMT\x00FFMT\x00AST\x00ADT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?\xc9\x1c\xd4\xc6\x03" + - "\x00\x00\xc6\x03\x00\x00\x0e\x00\x1c\x00America/JuneauUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84" + + "\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x1c\x00America/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x1c\x00America/CordobaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff" + + "\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd7" + + "0\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff" + + "\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86" + + "\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff" + + "\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS" + + "0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00" + + "\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W" + + " \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0" + + "\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUMv\xa1\x0f%\x01\x00\x00%\x01\x00\x00\x11\x00\x1c\x00America/MonterreyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\"U\xf1\x00" + + "\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x00" + + "7\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xa1\xf4\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x01\bLMT\x00CST\x00CDT\x00\nCST6CDT,M4.1.0,M10.5." + + "0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf2\x04\xde\xdd\x11\x02\x00\x00\x11\x02\x00\x00\x0e\x00\x1c\x00America/CancunUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00" + + "\x00\x16\x86\xd5`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x005\xc4\x00`\x00\x00\x00\x0062\xcc" + + "p\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00" + + "\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84" + + "\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00F\x0ff\x80\x00\x00\x00\x00G$3p\x00\x00\x00\x00G\xf8\x83\x00\x00\x00\x00\x00I\x04\x15p\x00\x00\x00\x00I\xd8e\x00\x00\x00\x00\x00J\xe3\xf7p\x00\x00\x00" + + "\x00K\xb8G\x00\x00\x00\x00\x00L\xcd\x13\xf0\x00\x00\x00\x00M\x98)\x00\x00\x00\x00\x00N\xac\xf5\xf0\x00\x00\x00\x00Ox\v\x00\x00\x00\x00\x00P\x8c\xd7\xf0\x00\x00\x00\x00Qa'\x80\x00\x00\x00\x00Rl\xb9" + + "\xf0\x00\x00\x00\x00SA\t\x80\x00\x00\x00\x00TL\x9b\xf0\x00\x00\x00\x00T\xcd\xdd\x00\x01\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + + "\x04\x01\x04\x01\x04\x01\x03\xff\xff\xae\xa8\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10LMT\x00CST\x00EDT\x00EST\x00CDT\x00\nES" + + "T5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xad`\x12\xe9\xaa\x00\x00\x00\xaa\x00\x00\x00\x0e\x00\x1c\x00America/La_PazUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87\x1bd\xff\xff" + + "\xff\xff\xb8\x1e\x96\xe4\xff\xff\xff\xff\xb8\xee\xd5\xd4\x01\x02\x03\xff\xff\xc0\x1c\x00\x00\xff\xff\xc0\x1c\x00\x04\xff\xff\xce,\x01\b\xff\xff\xc7\xc0\x00\fLMT\x00CMT\x00BST\x00-04\x00\n<-" + + "04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1b\vKdC\x03\x00\x00C\x03\x00\x00\x13\x00\x1c\x00America/Rainy_RiverUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff" + + "\xffr\xee\x87(\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\x00\x00\x00\x00\b \xcf" + + "\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\n\x00\xb1\x80\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00" + + "\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a" + + "\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00" + + "\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3" + + "\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00" + + "\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\b" + + "p\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00" + + "\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0" + + "\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\xff\xff\xa7X\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CWT\x00CPT\x00\nCST6CD" + + "T,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\r\x00\x1c\x00America/Jujuy" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x06\x00" + + "\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xb8\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff" + + "\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1" + + "\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff" + + "\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4" + + "=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff" + + "\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b" + + "$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xe2۰\x00\x00\x00\x00(\xee\x8a@\x00" + + "\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x04\x05\x04\x05\x03\x05\x04\x05\xff\xff\xc2\xc8\x00\x00" + + "\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00America/St_KittsUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb" + + "\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00A" + + "WT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUU9#\xbe2\x05\x00\x002\x05\x00\x00\x11\x00\x1c\x00America/VancouverUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x05\x00\x00\x00\x14\xff" + + "\xff\xff\xff^=v\xec\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd3v\x0f \xff\xff\xff\xff\xd4" + + "A\b\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff" + + "\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2" + + "~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff" + + "\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0" + + "q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff" + + "\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe" + + "\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00" + + "\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\b \xeb\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\n\x00͠\x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f" + + "\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00" + + "\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a" + + "\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00" + + "\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)" + + "\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00" + + "\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007" + + "\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00" + + "\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00E" + + "Dm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x8c\x94\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PS" + + "T\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00" + + "\x14\x00\x1c\x00America/IndianapolisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\n\x00\x00\x00&\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x872\xc5\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff" + - "\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04" + - "a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00" + - "\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12" + - "ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14Yc \x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00" + - "\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f" + - "\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00" + - "\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-" + - "\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00" + - "\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;" + - "\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00" + - "\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x06\x02" + - "\x05\x02\x05\x02\x05\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xd3{\x00\x00" + - "\xff\xff\x81\xfb\x00\x00\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x8f\x80\x01\x14\xff\xff\x81p\x00\x18\xff\xff\x8f\x80\x01\x1c\xff\xff\x81p\x00!LMT\x00PS" + - "T\x00PWT\x00PPT\x00PDT\x00YDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R.\xbe\x1a>\xe7\x03\x00\x00\xe7\x03\x00\x00\r\x00\x1c\x00America/BoiseUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0" + - "\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xff\xa8FL \xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff" + - "\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00" + - "\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\xb2\x1f\x90\x00\x00\x00\x00" + - "\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10" + - "\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00" + - "\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80" + - "\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00" + - "%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90" + - "\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x00" + - "3Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00" + - "\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00" + - "A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x05\x03\x04\x05\x06\x05\x06\x05\x06\x05\x06" + - "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + - "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\x93\x0f\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\x9d\x90\x00\x14\xff\xff\xab\xa0\x01\x18LMT\x00" + - "PDT\x00PST\x00MWT\x00MPT\x00MST\x00MDT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xaaʂA\xcd\x00\x00\x00\xcd\x00\x00\x00\x14\x00\x1c\x00America/Blanc-SablonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^=9\f\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff" + - "\xff\x9f\xba\xddP\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x02\x01\x02\x03\x04\x02\xff\xff\xcat\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff" + - "\xff\xd5\xd0\x01\x10LMT\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\u007f$*\xa0\xa6\x03\x00\x00\xa6\x03\x00\x00\x0e\x00\x1c" + - "\x00America/CuiabaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa{\x94\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff" + - "\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0T" + - "A0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff" + - "\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81" + - "w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00#X\x1e\xc0\x00\x00\x00\x00#\xe2~0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xd4\xd50\x00\x00\x00\x00'!\x1d@\x00\x00\x00\x00'\xbd\xf1\xb0\x00\x00" + - "\x00\x00)\x00\xff@\x00\x00\x00\x00)\x94\x990\x00\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+k@\xb0\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x00\x00\x00\x00.\xa0\xa5@\x00\x00\x00\x00/F" + - "\xb40\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001\x1d[\xb0\x00\x00\x00\x002W.\xc0\x00\x00\x00\x003\x06x0\x00\x00\x00\x0048b@\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006 -@\x00\x00" + - "\x00\x006\xcfv\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x00:\x8f:\xb0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00N\xfe\xb0\x00\x00\x00\x00A\x87\x06@\x00\x00\x00\x00B\x17\xfd0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00C\xf7\xdf0\x00\x00\x00\x00EMa\xc0\x00\x00\x00\x00E\xe0\xfb\xb0\x00\x00" + - "\x00\x00G\x11\x94@\x00\x00\x00\x00G\xb7\xa30\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00\x00\x00I\x97\x850\x00\x00\x00\x00Jڒ\xc0\x00\x00\x00\x00K\x80\xa1\xb0\x00\x00\x00\x00L\xbat\xc0\x00\x00\x00\x00M`" + - "\x83\xb0\x00\x00\x00\x00N\x9aV\xc0\x00\x00\x00\x00OI\xa00\x00\x00\x00\x00P\x83s@\x00\x00\x00\x00Q G\xb0\x00\x00\x00\x00RcU@\x00\x00\x00\x00S\x00)\xb0\x00\x00\x00\x00TC7@\x00\x00" + - "\x00\x00T\xe9F0\x00\x00\x00\x00V#\x19@\x00\x00\x00\x00V\xc9(0\x00\x00\x00\x00X\x02\xfb@\x00\x00\x00\x00X\xa9\n0\x00\x00\x00\x00Y\xe2\xdd@\x00\x00\x00\x00Z\x88\xec0\x00\x00\x00\x00[\xde" + - "n\xc0\x00\x00\x00\x00\\h\xce0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xcbl\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT" + - "\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf7\xe9 y\xbd\x02\x00\x00\xbd\x02\x00\x00\x0e\x00\x1c\x00America/Inuvi" + - "kUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x05" + - "\x00\x00\x00\x15\xff\xff\xff\xff\xe0\x06N\x80\xff\xff\xff\xff\xf7/h\x80\xff\xff\xff\xff\xf8(\x94\x00\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10" + - "\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00" + - "\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00" + - "\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00" + - "*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10" + - "\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x00" + - "8\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00" + - "\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00\x00\x00\x00" + - "\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x8f\x80\x00\t\xff\xff\x9d\x90\x00\r\xff\xff\xab\xa0\x01\x11-00\x00PDDT\x00PST\x00MST\x00MDT\x00\nMST7MDT,M3.2." + - "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RU!\x12f\xd9\x02\x00\x00\xd9\x02\x00\x00\x13\x00\x1c\x00America/YellowknifeU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00" + - "\x00\x19\xff\xff\xff\xff\xbe*\x18\x00\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(\x85\xf0\x00\x00\x00\x00\x13id\x10\x00\x00" + - "\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2" + - "&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00" + - "\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\n" + - "r\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00" + - "\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a" + - "*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00" + - "\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED" + - "_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x03\x01\x02\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x00\x00\x00\x00\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xab\xa0\x01\x15-00\x00MWT\x00MPT\x00M" + - "ST\x00MDDT\x00MDT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x10\x00\x1c\x00America/Indiana/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x14\x00\x1c\x00America/Indiana/KnoxUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff" + - "\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6" + - " \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff" + - "\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4" + - "^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff" + - "\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf4" + - "_\x87p\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00" + - "\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a" + - "\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00" + - "\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x16" + - "9\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00" + - "\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$" + - "5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00" + - "\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\xff\xff\xae\xca\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff" + - "\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M11." + - "1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x01\xd8N\x8c\xab\x02\x00\x00\xab\x02\x00\x00\x1a\x00\x1c\x00America/Indiana/Petersburg" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x06\x00" + - "\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff" + - "\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xe4g=\xe0\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea" + - "\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff" + - "\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\u007f\xa5p\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff\xff\xf6?ip\xff\xff\xff\xff\xf7/h\x80\xff\xff\xff\xff\xfa" + - "\bg\xf0\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00" + - "\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a" + - "\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00" + - "\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-m\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\x05\xff\xff\xae-\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9" + - "\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x1c\x00\x1c\x00America/Indiana/IndianapolisUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0" + - "\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff" + - "\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0" + - "\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff" + - "߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00" + - "\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01" + - "\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff" + - "\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0," + - "M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RصK\xa6\n\x02\x00\x00\n\x02\x00\x00\x19\x00\x1c\x00America/Indiana/Tell_C" + - "ityUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00\x00" + - "\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#" + - "\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xe4g=\xe0\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff" + - "\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f" + - "\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\u007f\xa5p\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff" + - "\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3" + - "\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x06\x05\x06\x05\x01\x02\x01\xff\xff\xae\xa9\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9" + - "\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nCST6CDT,M3" + - ".2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R \x17\x89}q\x01\x00\x00q\x01\x00\x00\x15\x00\x1c\x00America/Indiana/V" + - "evayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00" + - "\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2" + - "#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00" + - "\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05" + - "\x06\x05\x06\x05\x06\x05\x06\xff\xff\xb0@\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST" + - "\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RK-E\xfad" + - "\x02\x00\x00d\x02\x00\x00\x17\x00\x1c\x00America/Indiana/WinamacUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9" + - "p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff" + - "\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s" + - "\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff" + - "\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00" + - "\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff" + - "\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-_\xe0\x02\x01\x02\x01\x02\x03\x04" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x06\x05\x06\x05\x01\x02\x06\x05\xff\xff\xae\xcf\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff" + - "\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M" + - "3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RM/U\x9f7\x02\x00\x007\x02\x00\x00\x17\x00\x1c\x00America/Indiana/" + - "MarengoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00*\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff" + - "\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)" + - "\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff" + - "\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87" + - "\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00" + - "\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x01\x05\x06\x05\x06\x05\x06\xff\xff\xaf\r\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff" + - "\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1." + - "0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\r\xedsp.\x02\x00\x00.\x02\x00\x00\x19\x00\x1c\x00America/Indiana/VincennesUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\a\x00\x00\x00\x1c" + - "\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff" + - "\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4g=\xe0" + - "\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff" + - "\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0q\x9e\xf0\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\u007f\xa5p" + - "\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00" + - "D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-m\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x05\x06\x05\x06\x05\x01\x02\x01\x05\xff\xff\xad\xf1\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00C" + - "ST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rc)\xf6" + - ")\xb3\x00\x00\x00\xb3\x00\x00\x00\x0e\x00\x1c\x00America/BogotaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff^\x9c4\xf0\xff\xff\xff\xff\x98XUp\x00\x00\x00\x00*\x03sP\x00\x00\x00\x00+\xbe" + - "]@\x01\x03\x02\x03\xff\xff\xba\x90\x00\x00\xff\xff\xba\x90\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00BMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R.\xf9\xc0\x1e\xd5\x05\x00\x00\xd5\x05\x00\x00\x0f\x00\x1c\x00America/MonctonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x92\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x1e\xed\xbc\xff\xff\xff\xff\x80\xf1\xb6P\xff\xff" + - "\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xff\xbb<8\xd0\xff\xff\xff\xff\xbb\xb4#@\xff\xff\xff\xff\xbd\x1c\x1a\xd0\xff\xff\xff\xff\xbd\x94\x05@\xff\xff\xff\xff\xbe\xfb\xfc\xd0\xff\xff\xff\xff\xbfs" + - "\xe7@\xff\xff\xff\xff\xc0\xdb\xde\xd0\xff\xff\xff\xff\xc1S\xc9@\xff\xff\xff\xff»\xc0\xd0\xff\xff\xff\xff\xc33\xab@\xff\xff\xff\xffě\xa2\xd0\xff\xff\xff\xff\xc5\x13\x8d@\xff\xff\xff\xff\xc6p\xf8\xd0\xff\xff" + - "\xff\xff\xc7\r\xcd@\xff\xff\xff\xff\xc8H\xf1\xd0\xff\xff\xff\xff\xc8\xed\xaf@\xff\xff\xff\xff\xca\x16^\xd0\xff\xff\xff\xff\xca\xd6\xcb\xc0\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`" + - "\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff\xff\xff\xff\xd4@\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0\xff\xff\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff" + - "\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xda\xfe\x99`\xff\xff\xff\xff\xdb\xc0W\xd0\xff\xff\xff\xff\xdc\xde{`\xff\xff\xff\xffݩtP\xff\xff\xff\xff\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0\x9e" + - "?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff\xff\xff\xff\xe4^\x03`\xff\xff\xff\xff\xe5(\xfcP\xff\xff\xff\xff\xe6G\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff" + - "\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe9\x16\xe4\xd0\xff\xff\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xf6\xc6\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff\xec֨\xd0\xff\xff\xff\xff\xedƧ\xe0\xff\xff\xff\xff\xee\xbf" + - "\xc5P\xff\xff\xff\xff\xef\xaf\xc4`\xff\xff\xff\xff\xf0\x9f\xa7P\xff\xff\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\u007f\x89P\xff\xff\xff\xff\xf3o\x88`\xff\xff\xff\xff\xf4_kP\xff\xff\xff\xff\xf5Oj`\xff\xff" + - "\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8" + - ",\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00" + - "\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\f\xd9" + - "\x94\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00" + - "\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1" + - "\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00" + - "\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\n" + - "G\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbe]|\x00\x00\x00\x00,\xd3*l\x00\x00\x00\x00-\x9e?|\x00\x00\x00\x00.\xb3\fl\x00\x00\x00\x00/~!|\x00\x00" + - "\x00\x000\x92\xeel\x00\x00\x00\x001g=\xfc\x00\x00\x00\x002r\xd0l\x00\x00\x00\x003G\x1f\xfc\x00\x00\x00\x004R\xb2l\x00\x00\x00\x005'\x01\xfc\x00\x00\x00\x0062\x94l\x00\x00\x00\x007\x06" + - "\xe3\xfc\x00\x00\x00\x008\x1b\xb0\xec\x00\x00\x00\x008\xe6\xc5\xfc\x00\x00\x00\x009\xfb\x92\xec\x00\x00\x00\x00:Ƨ\xfc\x00\x00\x00\x00;\xdbt\xec\x00\x00\x00\x00<\xaf\xc4|\x00\x00\x00\x00=\xbbV\xec\x00\x00" + - "\x00\x00>\x8f\xa6|\x00\x00\x00\x00?\x9b8\xec\x00\x00\x00\x00@o\x88|\x00\x00\x00\x00A\x84Ul\x00\x00\x00\x00BOj|\x00\x00\x00\x00Cd7l\x00\x00\x00\x00D/L|\x00\x00\x00\x00ED" + - "\x19l\x00\x00\x00\x00E\xf3\x9a\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x05\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xc3D\x00\x00\xff\xff\xb9\xb0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\f" + - "\xff\xff\xd5\xd0\x01\x10\xff\xff\xd5\xd0\x01\x14LMT\x00EST\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3.2.0,M11.1.0\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00America/MarigotUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6" + - "T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R挋\x92\xf6\x01\x00\x00\xf6\x01\x00\x00\x0e\x00\x1c\x00America" + - "/MaceioUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00)\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaah|\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff" + - "\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97" + - "\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff" + - "\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\v" + - "Ƞ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x007\xf6ư\x00\x00" + - "\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x009\xf2J \x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x003\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x1c\x00America/PanamaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffi\x87&\x10\xff\xff\xff\xff\x8b\xf4a\xe8" + - "\x01\x02\xff\xff\xb5p\x00\x00\xff\xff\xb5\x18\x00\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa2\x81\xbfyS\x02\x00\x00" + - "S\x02\x00\x00\x12\x00\x1c\x00America/MetlakatlaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00\b\x00\x00\x00\x1e\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x870\x1a\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4" + - "p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00" + - "\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0" + - "\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00" + - "\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18" + - " \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00V5\xe2\xa0\x00\x00\x00\x00V\xe5H0\x00\x00\x00\x00X\x1e\xff \x00\x00\x00\x00X\xc5*0\x00\x00\x00\x00Y\xfe\xe1 \x00\x00\x00\x00Z\xa5\f0\x00\x00\x00" + - "\x00[\xde\xc3 \x00\x00\x00\x00\\DF\xa0\x00\x00\x00\x00\\\x84\xee0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x06\a\x06\a" + - "\x06\a\x02\x06\a\x00\x00\xd6&\x00\x00\xff\xff\x84\xa6\x00\x00\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x81p\x00\x14\xff\xff\x8f\x80\x01\x19LMT\x00PST" + - "\x00PWT\x00PPT\x00PDT\x00AKST\x00AKDT\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R\xfe\xe6\xf5J\x05\x04\x00\x00\x05\x04\x00\x00\x0e\x00\x1c\x00America/DawsonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff" + + "\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4" + + "@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff" + + "\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2" + + "~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00" + + "\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05" + + "\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00C" + + "ST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUJtZ" + + "\x8c\x01\x03\x00\x00\x01\x03\x00\x00\x13\x00\x1c\x00America/PangnirtungUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\n\x00\x00\x00)\xff\xff\xff\xff\xa3\xd5R\x80\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff" + + "\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xf7/0@\xff\xff\xff\xff\xf8([\xc0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17" + + "(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00" + + "\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%" + + "J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00" + + "\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003" + + "GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00" + + "\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A" + + "\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x03\x01\x02\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x06\a\x06\a\x06\a\x06\a\x06\b\t\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\x00\x00\x00\x00\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xd5\xd0" + + "\x01\b\xff\xff\xc7\xc0\x00\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x01\x15\xff\xff\xc7\xc0\x01\x19\xff\xff\xb9\xb0\x00\x1d\xff\xff\xab\xa0\x00!\xff\xff\xb9\xb0\x01%-00\x00AWT\x00APT\x00AST\x00" + + "ADDT\x00ADT\x00EDT\x00EST\x00CST\x00CDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU挋\x92\xf6\x01\x00\x00\xf6\x01\x00\x00\x0e\x00\x1c\x00America/MaceioUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8e\xb4\xff\xff\xff\xff\x9e\xb8˰\xff\xff\xff\xff\x9f\xbb#\xa0" + - "\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2Ҁ\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\x00\x00\x00\x00" + - "\a0\xec\x90\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 " + - "\x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00" + - " v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10" + - "\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00" + - ".\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V " + - "\x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00" + - "<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90" + - "\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00" + - "J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90\x00\x00\x00\x00Q3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00America/AntiguaUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" + - "\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x11\x00\x1c\x00America/Kentucky/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xdf\xe5\x8d\xc4\xda\x04\x00\x00\xda\x04\x00\x00\x1b\x00\x1c\x00America/Kentucky/LouisvilleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + - "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff" + - "\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa4s\xf7\x00\xff\xff\xff\xff\xa5\x16\x11p\xff\xff\xff\xff\xca\rN\x80\xff\xff\xff\xff\xca" + - "\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xd7\x1c\xff\xff\xff\xffӤ\tp\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff" + - "\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3" + - "I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe9\x17\x00\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff" + - "\xff\xff\xff\xea\xf6\xe2\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x1e\x90p\xff\xff\xff\xff\xfc" + - "\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00" + - "\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n" + - "\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00" + - "\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19" + - "\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00" + - "\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'" + - "*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00" + - "\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005" + - "'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00" + - "\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00C" + - "da`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + - "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf\x9a\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14" + - "\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x03\x1a|J\xcc\x03\x00\x00\xcc\x03\x00\x00\x1b\x00\x1c\x00America/Kentucky/MonticelloUT\t\x00\x03" + - "\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00W\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff" + - "\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a" + - "\t\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00" + - "\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad" + - "\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00" + - "\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"" + - "7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00" + - "\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15" + - "\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00" + - "\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R" + - "\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00" + - "\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO" + - "\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + - "\x05\xff\xff\xb0t\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xc7\xc0\x01\x14\xff\xff\xb9\xb0\x00\x18LMT\x00CDT\x00CST\x00CWT\x00C" + - "PT\x00EDT\x00EST\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R$\r\x89l\xe4\x01\x00\x00\xe4\x01\x00" + - "\x00\x0f\x00\x1c\x00America/OjinagaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaah|\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0" + + "\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xff" + + "ޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0" + + "\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00" + + "\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 " + + "\x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x009\xf2J \x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x00" + + "3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\f\x00\x1c\x00America" + + "/NuukUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"" + + "\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9b\x80h\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00" + + "\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10" + + "\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#3<-02>,M3." + + "5.0/-2,M10.5.0/-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUc)\xf6)\xb3\x00\x00\x00\xb3\x00\x00\x00\x0e\x00\x1c\x00America/Bogo" + + "taUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00" + + "\x04\x00\x00\x00\x10\xff\xff\xff\xff^\x9c4\xf0\xff\xff\xff\xff\x98XUp\x00\x00\x00\x00*\x03sP\x00\x00\x00\x00+\xbe]@\x01\x03\x02\x03\xff\xff\xba\x90\x00\x00\xff\xff\xba\x90\x00\x04\xff\xff\xc7\xc0\x01\b\xff" + + "\xff\xb9\xb0\x00\fLMT\x00BMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x14\x00\x1c\x00A" + + "merica/Buenos_AiresUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xa8L\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e" + + "\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff" + + "\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe" + + "*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff" + + "\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13" + + "C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff" + + "\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!" + + "\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00" + + "\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc94\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01" + + "\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00" + + "\x00\x12\x00\x1c\x00America/MontserratUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03" + + "\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vUl=\xad\xbe\x16\x01\x00\x00\x16\x01\x00\x00\x10\x00\x1c\x00America/BarbadosUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x92@\xa9e\xff\xff\xff\xff\xcb\xe3\xcb\xd0\xff\xff\xff\xff̔\x82" + + "\xe0\xff\xff\xff\xff\xcd\xd6\"\xd0\xff\xff\xff\xff\xce|M\xe0\xff\xff\xff\xffϛ\xa6\xd0\xff\xff\xff\xff\xd0ej`\x00\x00\x00\x00\x0e\x00\xf2\xe0\x00\x00\x00\x00\x0e\x94\x8c\xd0\x00\x00\x00\x00\x0f\x97\x00\xe0\x00\x00\x00" + + "\x00\x10tn\xd0\x00\x00\x00\x00\x11v\xe2\xe0\x00\x00\x00\x00\x12TP\xd0\x00\x00\x00\x00\x13_\xff`\x00\x00\x00\x00\x140>P\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xc8\x1b\x00\x00\xff\xff" + + "\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xce\xc8\x01\fLMT\x00ADT\x00AST\x00-0330\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1d\xf7\a ,\x06" + + "\x00\x00,\x06\x00\x00\x11\x00\x1c\x00America/Goose_BayUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\n\x00\x00\x00!\xff\xff\xff\xff^=<$\xff\xff\xff\xff\x9e\xb8~\x8c\xff\xff\xff\xff\x9f\xba\xd6|\xff\xff\xff\xff\xbe\x9e" + + "Ml\xff\xff\xff\xff\xc0\xb818\xff\xff\xff\xff\xc1y\xef\xa8\xff\xff\xff\xff\u0098\x138\xff\xff\xff\xff\xc3YѨ\xff\xff\xff\xff\xc4w\xf58\xff\xff\xff\xff\xc59\xb3\xa8\xff\xff\xff\xff\xc6a\x11\xb8\xff\xff" + + "\xff\xff\xc7\x19\x95\xa8\xff\xff\xff\xff\xc8@\xf3\xb8\xff\xff\xff\xff\xc9\x02\xb2(\xff\xff\xff\xff\xca ո\xff\xff\xff\xff\xca\xe2\x94(\xff\xff\xff\xff\xcc\x00\xb7\xb8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`" + + "\xe6\xc8\xff\xff\xff\xffӈD\xd8\xff\xff\xff\xff\xd4J\x03H\xff\xff\xff\xff\xd5h&\xd8\xff\xff\xff\xff\xd6)\xe5H\xff\xff\xff\xff\xd7H\b\xd8\xff\xff\xff\xff\xd8\t\xc7H\xff\xff\xff\xff\xd9'\xea\xd8\xff\xff" + + "\xff\xff\xd9\xe9\xa9H\xff\xff\xff\xff\xdb\x11\aX\xff\xff\xff\xff\xdb\xd2\xc5\xc8\xff\xff\xff\xff\xdc\xdetX\xff\xff\xff\xffݩmH\xff\xff\xff\xff\u07beVX\xff\xff\xff\xff߉OH\xff\xff\xff\xff\xe0\x9e" + + "8X\xff\xff\xff\xff\xe1i1H\xff\xff\xff\xff\xe2~\x1aX\xff\xff\xff\xff\xe3I\x13H\xff\xff\xff\xff\xe4]\xfcX\xff\xff\xff\xff\xe5(\xf5H\xff\xff\xff\xff\xe6G\x18\xd8\xff\xff\xff\xff\xe7\x12\x11\xc8\xff\xff" + + "\xff\xff\xe8&\xfa\xd8\xff\xff\xff\xff\xe8\xf1\xf3\xc8\xff\xff\xff\xff\xea\x06\xdc\xd8\xff\xff\xff\xff\xea\xd1\xd5\xc8\xff\xff\xff\xff\xeb\xe6\xbe\xd8\xff\xff\xff\xff챷\xc8\xff\xff\xff\xff\xedƠ\xd8\xff\xff\xff\xff\xee\xbf" + + "\xbeH\xff\xff\xff\xffﯽX\xff\xff\xff\xff\xf0\x9f\xa0H\xff\xff\xff\xff\xf1\x8f\x9fX\xff\xff\xff\xff\xf2\x7f\x82H\xff\xff\xff\xff\xf3o\x81X\xff\xff\xff\xff\xf4_dH\xff\xff\xff\xff\xf5OcX\xff\xff" + + "\xff\xff\xf6?FH\xff\xff\xff\xff\xf7/EX\xff\xff\xff\xff\xf8(b\xc8\xff\xff\xff\xff\xf8\xdakX\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8" + + "-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00" + + "\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00" + + "\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00" + + "\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"" + + "\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00" + + "\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xd6\xfc\x00\x00\x00\x00!\x81il\x00\x00\x00\x00\"U\xb8\xfc\x00\x00\x00\x00#jw\xdc\x00\x00\x00\x00$5\x9a\xfc\x00\x00\x00\x00%Jg\xec\x00\x00\x00\x00&\x15" + + "|\xfc\x00\x00\x00\x00'*I\xec\x00\x00\x00\x00'\xfe\x99|\x00\x00\x00\x00)\n+\xec\x00\x00\x00\x00)\xde{|\x00\x00\x00\x00*\xea\r\xec\x00\x00\x00\x00+\xbe]|\x00\x00\x00\x00,\xd3*l\x00\x00" + + "\x00\x00-\x9e?|\x00\x00\x00\x00.\xb3\fl\x00\x00\x00\x00/~!|\x00\x00\x00\x000\x92\xeel\x00\x00\x00\x001g=\xfc\x00\x00\x00\x002r\xd0l\x00\x00\x00\x003G\x1f\xfc\x00\x00\x00\x004R" + + "\xb2l\x00\x00\x00\x005'\x01\xfc\x00\x00\x00\x0062\x94l\x00\x00\x00\x007\x06\xe3\xfc\x00\x00\x00\x008\x1b\xb0\xec\x00\x00\x00\x008\xe6\xc5\xfc\x00\x00\x00\x009\xfb\x92\xec\x00\x00\x00\x00:Ƨ\xfc\x00\x00" + + "\x00\x00;\xdbt\xec\x00\x00\x00\x00<\xaf\xc4|\x00\x00\x00\x00=\xbbV\xec\x00\x00\x00\x00>\x8f\xa6|\x00\x00\x00\x00?\x9b8\xec\x00\x00\x00\x00@o\x88|\x00\x00\x00\x00A\x84Ul\x00\x00\x00\x00BO" + + "j|\x00\x00\x00\x00Cd7l\x00\x00\x00\x00D/L|\x00\x00\x00\x00ED\x19l\x00\x00\x00\x00E\xf3~\xfc\x00\x00\x00\x00G-5\xec\x00\x00\x00\x00G\xd3`\xfc\x00\x00\x00\x00I\r\x17\xec\x00\x00" + + "\x00\x00I\xb3B\xfc\x00\x00\x00\x00J\xec\xf9\xec\x00\x00\x00\x00K\x9c_|\x00\x00\x00\x00L\xd6\x16l\x00\x00\x00\x00M|A|\x00\x00\x00\x00N\xb6\x14P\x01\x02\x01\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x06\x05\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b" + + "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\t\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b" + + "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\xff\xff\xc7\\\x00\x00\xff\xffΔ\x00\x04\xff\xffܤ\x01\b\xff\xff\xce\xc8\x00\x04\xff\xff\xdc\xd8\x01\b\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10" + + "\xff\xff\xd5\xd0\x01\x14\xff\xff\xc7\xc0\x00\x18\xff\xff\xe3\xe0\x01\x1cLMT\x00NST\x00NDT\x00NPT\x00NWT\x00ADT\x00AST\x00ADDT\x00\nAST4ADT," + + "M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU$\r\x89l\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x1c\x00America/Ojinaga" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x05\x00" + + "\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\x00\x00\x00\x001gv\x00\x00" + + "\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008" + + "\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00" + + "\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00F\x0ft\x90\x00\x00\x00\x00G" + + "$A\x80\x00\x00\x00\x00G\xf8\x91\x10\x00\x00\x00\x00I\x04#\x80\x00\x00\x00\x00I\xd8s\x10\x00\x00\x00\x00J\xe4\x05\x80\x00\x00\x00\x00K\x9c\xa5\x90\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04" + + "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff\xff\x9e\x1c\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00" + + "CDT\x00MDT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00" + + "\x1c\x00America/Porto_AcreUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8" + - "\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00" + - "\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=" + - "\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00" + - "\x00\x00\x00ED_\x80\x00\x00\x00\x00F\x0ft\x90\x00\x00\x00\x00G$A\x80\x00\x00\x00\x00G\xf8\x91\x10\x00\x00\x00\x00I\x04#\x80\x00\x00\x00\x00I\xd8s\x10\x00\x00\x00\x00J\xe4\x05\x80\x00\x00\x00\x00K" + - "\x9c\xa5\x90\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff\xff\x9e\x1c\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0" + - "\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00CDT\x00MDT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\r\x00\x1c\x00America/ArubaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\x93\x1e.#\xff\xff\xff\xff\xf6\x98\xecH\x01\x02\xff\xff\xbf]" + - "\x00\x00\xff\xff\xc0\xb8\x00\x04\xff\xff\xc7\xc0\x00\nLMT\x00-0430\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x81{\xc1\x92\xbc\x03\x00\x00\xbc\x03\x00\x00" + - "\r\x00\x1c\x00America/SitkaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\t\x00\x00\x00\"\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x873\x99\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10" + - "\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00" + - "\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0" + - "\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00" + - "\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90" + - "\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00" + - " v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac " + - "\x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00" + - ".\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0" + - "\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00" + - "<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0" + - "\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x06\b" + - "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x00\x00ҧ\x00\x00\xff\xff\x81'\x00\x00\xff" + - "\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x81p\x00\x14\xff\xff\x8f\x80\x01\x18\xff\xff\x81p\x00\x1dLMT\x00PST\x00PWT\x00PPT\x00PDT" + - "\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rѱ\x86b\xee" + - "\x03\x00\x00\xee\x03\x00\x00\x0e\x00\x1c\x00America/NassauUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x937B\x8a\xff\xff\xff\xff\xcb\xf4\xefP\xff\xff\xff\xff\xd0\xfaG\xc0\xff\xff\xff\xff\xd1#4P" + - "\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2x\x9a\xc0\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00" + - "@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x03\x02\x04\x02" + - "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02" + - "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\xff\xff\xb7v\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00E" + - "WT\x00EST\x00EPT\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9d?\xdfڸ\x03\x00" + - "\x00\xb8\x03\x00\x00\x11\x00\x1c\x00America/Sao_PauloUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaar\xb4\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf14" + - "0\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff" + - "\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4Z\t0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5" + - " \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00" + - "\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x00'!\x0f" + - "0\x00\x00\x00\x00'\xbd\xe3\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\x94\x8b \x00\x00\x00\x00*\xea\r\xb0\x00\x00\x00\x00+k2\xa0\x00\x00\x00\x00,\xc0\xb50\x00\x00\x00\x00-f\xc4 \x00\x00\x00" + - "\x00.\xa0\x970\x00\x00\x00\x00/F\xa6 \x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00\x00\x004\xf8\xc1" + - " \x00\x00\x00\x006 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00" + - "\x00N\xf0\xa0\x00\x00\x00\x00?\x91\xfe0\x00\x00\x00\x00@.Ҡ\x00\x00\x00\x00A\x86\xf80\x00\x00\x00\x00B\x17\xef \x00\x00\x00\x00CQ\xc2" + - "0\x00\x00\x00\x00C\xf7\xd1 \x00\x00\x00\x00EMS\xb0\x00\x00\x00\x00E\xe0\xed\xa0\x00\x00\x00\x00G\x11\x860\x00\x00\x00\x00G\xb7\x95 \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\x97w \x00\x00\x00" + - "\x00Jڄ\xb0\x00\x00\x00\x00K\x80\x93\xa0\x00\x00\x00\x00L\xbaf\xb0\x00\x00\x00\x00M`u\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x00\x00\x00\x00P\x83e0\x00\x00\x00\x00Q 9" + - "\xa0\x00\x00\x00\x00RcG0\x00\x00\x00\x00S\x00\x1b\xa0\x00\x00\x00\x00TC)0\x00\x00\x00\x00T\xe98 \x00\x00\x00\x00V#\v0\x00\x00\x00\x00V\xc9\x1a \x00\x00\x00\x00X\x02\xed0\x00\x00\x00" + - "\x00X\xa8\xfc \x00\x00\x00\x00Y\xe2\xcf0\x00\x00\x00\x00Z\x88\xde \x00\x00\x00\x00[\xde`\xb0\x00\x00\x00\x00\\h\xc0 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd4L\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "g\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00\x1c\x00America/Rio_BrancoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x86\x90\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\" + - "\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xbaސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff" + - "\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QH" + - "P\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00" + - "\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00\x00\x00\x00H`\u007fP\x00\x00\x00\x00R\u007f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xc0p\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb1݂x\xe8\x00\x00\x00\xe8\x00\x00\x00\x12\x00\x1c\x00America/Costa_RicaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + - "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffi\x87*M\xff" + - "\xff\xff\xff\xa3\xe8\x16M\x00\x00\x00\x00\x116I`\x00\x00\x00\x00\x11\xb7nP\x00\x00\x00\x00\x13\x16+`\x00\x00\x00\x00\x13\x97PP\x00\x00\x00\x00'\x97\xe0`\x00\x00\x00\x00(n\xb6\xd0\x00\x00\x00\x00)" + - "w\xc2`\x00\x00\x00\x00)\xc2\xd9\xd0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb13\x00\x00\xff\xff\xb13\x00\x04\xff\xff\xb9\xb0\x01\t\xff\xff\xab\xa0\x00\rLMT\x00SJMT\x00CDT\x00CS" + - "T\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1b\x81-\xa9\x8a\x01\x00\x00\x8a\x01\x00\x00\x13\x00\x1c\x00America/Porto_VelhoUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f" + - "\xff\xff\xff\xff\x96\xaa\x82\xe8\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff" + - "\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0" + - "\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff" + - "\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xc4\x18\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x14\x00\x1c\x00America/IndianapolisUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0" + - "\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff" + - "\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0" + - "\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff" + - "߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00" + - "\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01" + - "\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff" + - "\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0," + - "M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4T\xbd\xeb5\x02\x00\x005\x02\x00\x00\x16\x00\x1c\x00America/Port-au-Prince" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x00\x04\x00" + - "\x00\x00\x11\xff\xff\xff\xffi\x87\x1fP\xff\xff\xff\xff\x9cnq\xfc\x00\x00\x00\x00\x19\x1bF\xd0\x00\x00\x00\x00\x1a\x01\xef@\x00\x00\x00\x00\x1a\xf1\xeeP\x00\x00\x00\x00\x1b\xe1\xd1@\x00\x00\x00\x00\x1c\xd1\xd0P\x00" + - "\x00\x00\x00\x1d\xc1\xb3@\x00\x00\x00\x00\x1e\xb1\xb2P\x00\x00\x00\x00\x1f\xa1\x95@\x00\x00\x00\x00 \x91\x94P\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$" + - "5\xb6\xe0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea7\xe0\x00" + - "\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002" + - "r\xfa`\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x00BOxP\x00\x00\x00\x00CdE@\x00\x00\x00\x00D/ZP\x00\x00\x00\x00ED'@\x00\x00\x00\x00O\\Mp\x00" + - "\x00\x00\x00P\x96\x04`\x00\x00\x00\x00Q\x90\x16\xc0\x00" + - "\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0\x00\x00\x00\x00D/\xbc\xc0\x00\x00\x00\x00ED\x89\xb0\x00\x00\x00\x00E" + - "\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t" + - "\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00\xff\xffeP\x00\x04\xff\xffs`\x01\b\xff\xffs`\x01\f\xff\xffe" + - "P\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00NPT\x00BST\x00BDT\x00AHST\x00HDT\x00" + - "\nHST10HDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R~\xb2\x0e\x19V\a\x00\x00V\a\x00\x00\x10\x00\x1c\x00Ameri" + - "ca/St_JohnsUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xbb\x00\x00\x00\b\x00\x00\x00\x19\xff\xff\xff\xff^=4\xec\xff\xff\xff\xff\x9c\xcfb\f\xff\xff\xff\xff\x9d\xa4\xe6\xfc\xff\xff\xff\xff\x9e\xb8~\x8c\xff\xff\xff\xff\x9f\xba\xd6|\xff\xff\xff\xff\xa0\xb6" + - "\x88\xdc\xff\xff\xff\xff\xa18\xffL\xff\xff\xff\xff\xa2\x95\x19\\\xff\xff\xff\xff\xa3\x84\xfcL\xff\xff\xff\xff\xa4t\xfb\\\xff\xff\xff\xff\xa5d\xdeL\xff\xff\xff\xff\xa6^\x17\xdc\xff\xff\xff\xff\xa7D\xc0L\xff\xff" + - "\xff\xff\xa8=\xf9\xdc\xff\xff\xff\xff\xa9$\xa2L\xff\xff\xff\xff\xaa\x1d\xdb\xdc\xff\xff\xff\xff\xab\x04\x84L\xff\xff\xff\xff\xab\xfd\xbd\xdc\xff\xff\xff\xff\xac\xe4fL\xff\xff\xff\xff\xadݟ\xdc\xff\xff\xff\xff\xae\xcd" + - "\x82\xcc\xff\xff\xff\xff\xaf\xbd\x81\xdc\xff\xff\xff\xff\xb0\xadd\xcc\xff\xff\xff\xff\xb1\xa6\x9e\\\xff\xff\xff\xff\xb2\x8dF\xcc\xff\xff\xff\xff\xb3\x86\x80\\\xff\xff\xff\xff\xb4m(\xcc\xff\xff\xff\xff\xb5fb\\\xff\xff" + - "\xff\xff\xb6M\n\xcc\xff\xff\xff\xff\xb7FD\\\xff\xff\xff\xff\xb8,\xec\xcc\xff\xff\xff\xff\xb9&&\\\xff\xff\xff\xff\xba\x16\tL\xff\xff\xff\xff\xbb\x0fB\xdc\xff\xff\xff\xff\xbb\xf5\xebL\xff\xff\xff\xff\xbc\xef" + - "$\xdc\xff\xff\xff\xff\xbd\xd5\xcdL\xff\xff\xff\xff\xbe\x9eMl\xff\xff\xff\xff\xbe\xcf\x06\xa8\xff\xff\xff\xff\xbf\xb5\xaf\x18\xff\xff\xff\xff\xc0\xb818\xff\xff\xff\xff\xc1y\xef\xa8\xff\xff\xff\xff\u0098\x138\xff\xff" + - "\xff\xff\xc3YѨ\xff\xff\xff\xff\xc4w\xf58\xff\xff\xff\xff\xc59\xb3\xa8\xff\xff\xff\xff\xc6a\x11\xb8\xff\xff\xff\xff\xc7\x19\x95\xa8\xff\xff\xff\xff\xc8@\xf3\xb8\xff\xff\xff\xff\xc9\x02\xb2(\xff\xff\xff\xff\xca " + - "ո\xff\xff\xff\xff\xca\xe2\x94(\xff\xff\xff\xff\xcc\x00\xb7\xb8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xe6\xc8\xff\xff\xff\xffӈD\xd8\xff\xff\xff\xff\xd4J\x03H\xff\xff\xff\xff\xd5h&\xd8\xff\xff" + - "\xff\xff\xd6)\xe5H\xff\xff\xff\xff\xd7H\b\xd8\xff\xff\xff\xff\xd8\t\xc7H\xff\xff\xff\xff\xd9'\xea\xd8\xff\xff\xff\xff\xd9\xe9\xa9H\xff\xff\xff\xff\xdb\x11\aX\xff\xff\xff\xff\xdb\xd2\xc5\xc8\xff\xff\xff\xff\xdc\xde" + - "tX\xff\xff\xff\xffݩmH\xff\xff\xff\xff\u07beVX\xff\xff\xff\xff߉OH\xff\xff\xff\xff\xe0\x9e8X\xff\xff\xff\xff\xe1i1H\xff\xff\xff\xff\xe2~\x1aX\xff\xff\xff\xff\xe3I\x13H\xff\xff" + - "\xff\xff\xe4]\xfcX\xff\xff\xff\xff\xe5(\xf5H\xff\xff\xff\xff\xe6G\x18\xd8\xff\xff\xff\xff\xe7\x12\x11\xc8\xff\xff\xff\xff\xe8&\xfa\xd8\xff\xff\xff\xff\xe8\xf1\xf3\xc8\xff\xff\xff\xff\xea\x06\xdc\xd8\xff\xff\xff\xff\xea\xd1" + - "\xd5\xc8\xff\xff\xff\xff\xeb\xe6\xbe\xd8\xff\xff\xff\xff챷\xc8\xff\xff\xff\xff\xedƠ\xd8\xff\xff\xff\xff\ueffeH\xff\xff\xff\xffﯽX\xff\xff\xff\xff\xf0\x9f\xa0H\xff\xff\xff\xff\xf1\x8f\x9fX\xff\xff" + - "\xff\xff\xf2\u007f\x82H\xff\xff\xff\xff\xf3o\x81X\xff\xff\xff\xff\xf4_dH\xff\xff\xff\xff\xf5OcX\xff\xff\xff\xff\xf6?FH\xff\xff\xff\xff\xf7/EX\xff\xff\xff\xff\xf8(b\xc8\xff\xff\xff\xff\xf9\x0f" + - "'X\xff\xff\xff\xff\xfa\bD\xc8\xff\xff\xff\xff\xfa\xf8C\xd8\xff\xff\xff\xff\xfb\xe8&\xc8\xff\xff\xff\xff\xfc\xd8%\xd8\xff\xff\xff\xff\xfd\xc8\b\xc8\xff\xff\xff\xff\xfe\xb8\a\xd8\xff\xff\xff\xff\xff\xa7\xea\xc8\x00\x00" + - "\x00\x00\x00\x97\xe9\xd8\x00\x00\x00\x00\x01\x87\xcc\xc8\x00\x00\x00\x00\x02w\xcb\xd8\x00\x00\x00\x00\x03p\xe9H\x00\x00\x00\x00\x04`\xe8X\x00\x00\x00\x00\x05P\xcbH\x00\x00\x00\x00\x06@\xcaX\x00\x00\x00\x00\a0" + - "\xadH\x00\x00\x00\x00\b \xacX\x00\x00\x00\x00\t\x10\x8fH\x00\x00\x00\x00\n\x00\x8eX\x00\x00\x00\x00\n\xf0qH\x00\x00\x00\x00\v\xe0pX\x00\x00\x00\x00\fٍ\xc8\x00\x00\x00\x00\r\xc0RX\x00\x00" + - "\x00\x00\x0e\xb9o\xc8\x00\x00\x00\x00\x0f\xa9n\xd8\x00\x00\x00\x00\x10\x99Q\xc8\x00\x00\x00\x00\x11\x89P\xd8\x00\x00\x00\x00\x12y3\xc8\x00\x00\x00\x00\x13i2\xd8\x00\x00\x00\x00\x14Y\x15\xc8\x00\x00\x00\x00\x15I" + - "\x14\xd8\x00\x00\x00\x00\x168\xf7\xc8\x00\x00\x00\x00\x17(\xf6\xd8\x00\x00\x00\x00\x18\"\x14H\x00\x00\x00\x00\x19\b\xd8\xd8\x00\x00\x00\x00\x1a\x01\xf6H\x00\x00\x00\x00\x1a\xf1\xf5X\x00\x00\x00\x00\x1b\xe1\xd8H\x00\x00" + - "\x00\x00\x1c\xd1\xd7X\x00\x00\x00\x00\x1d\xc1\xbaH\x00\x00\x00\x00\x1e\xb1\xb9X\x00\x00\x00\x00\x1f\xa1\x9cH\x00\x00\x00\x00 u\xcf\xf4\x00\x00\x00\x00!\x81bd\x00\x00\x00\x00\"U\xb1\xf4\x00\x00\x00\x00#j" + - "p\xd4\x00\x00\x00\x00$5\x93\xf4\x00\x00\x00\x00%J`\xe4\x00\x00\x00\x00&\x15u\xf4\x00\x00\x00\x00'*B\xe4\x00\x00\x00\x00'\xfe\x92t\x00\x00\x00\x00)\n$\xe4\x00\x00\x00\x00)\xdett\x00\x00" + - "\x00\x00*\xea\x06\xe4\x00\x00\x00\x00+\xbeVt\x00\x00\x00\x00,\xd3#d\x00\x00\x00\x00-\x9e8t\x00\x00\x00\x00.\xb3\x05d\x00\x00\x00\x00/~\x1at\x00\x00\x00\x000\x92\xe7d\x00\x00\x00\x001g" + - "6\xf4\x00\x00\x00\x002r\xc9d\x00\x00\x00\x003G\x18\xf4\x00\x00\x00\x004R\xabd\x00\x00\x00\x005&\xfa\xf4\x00\x00\x00\x0062\x8dd\x00\x00\x00\x007\x06\xdc\xf4\x00\x00\x00\x008\x1b\xa9\xe4\x00\x00" + - "\x00\x008\xe6\xbe\xf4\x00\x00\x00\x009\xfb\x8b\xe4\x00\x00\x00\x00:Ơ\xf4\x00\x00\x00\x00;\xdbm\xe4\x00\x00\x00\x00<\xaf\xbdt\x00\x00\x00\x00=\xbbO\xe4\x00\x00\x00\x00>\x8f\x9ft\x00\x00\x00\x00?\x9b" + - "1\xe4\x00\x00\x00\x00@o\x81t\x00\x00\x00\x00A\x84Nd\x00\x00\x00\x00BOct\x00\x00\x00\x00Cd0d\x00\x00\x00\x00D/Et\x00\x00\x00\x00ED\x12d\x00\x00\x00\x00E\xf3w\xf4\x00\x00" + - "\x00\x00G-.\xe4\x00\x00\x00\x00G\xd3Y\xf4\x00\x00\x00\x00I\r\x10\xe4\x00\x00\x00\x00I\xb3;\xf4\x00\x00\x00\x00J\xec\xf2\xe4\x00\x00\x00\x00K\x9cXt\x00\x00\x00\x00L\xd6\x0fd\x00\x00\x00\x00M|" + - ":t\x00\x00\x00\x00N\xb6\rH\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x06\x05\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\xff\xffΔ\x00\x00\xff\xffܤ\x01\x04\xff\xffΔ\x00\b\xff\xff\xdc\xd8\x01\x04\xff\xff\xce\xc8\x00\b\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10\xff" + - "\xff\xea\xe8\x01\x14LMT\x00NDT\x00NST\x00NPT\x00NWT\x00NDDT\x00\nNST3:30NDT,M3.2.0,M11.1.0\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RJtZ\x8c\x01\x03\x00\x00\x01\x03\x00\x00\x13\x00\x1c\x00America/PangnirtungUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\n\x00\x00\x00)\xff\xff\xff\xff\xa3\xd5R\x80\xff\xff\xff" + - "\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xf7/0@\xff\xff\xff\xff\xf8([\xc0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b" + - "\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00" + - "\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1" + - "\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00" + - "\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg" + - "\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00" + - "\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb" + - "\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x03\x01\x02" + - "\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x06\a\x06\a\x06\a\x06\a\x06\b\t\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\x00\x00" + - "\x00\x00\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x01\x15\xff\xff\xc7\xc0\x01\x19\xff\xff\xb9\xb0\x00\x1d\xff\xff\xab\xa0\x00!\xff\xff\xb9\xb0\x01%-0" + - "0\x00AWT\x00APT\x00AST\x00ADDT\x00ADT\x00EDT\x00EST\x00CST\x00CDT\x00\nEST5EDT,M3.2.0,M11.1" + - ".0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RU\xactA\xb5\x01\x00\x00\xb5\x01\x00\x00\x11\x00\x1c\x00America/MatamorosUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa5\xb6\xda" + - "`\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00" + - "\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc" + - "\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00" + - "\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00F\x0ff\x80\x00\x00\x00\x00G$3p\x00\x00\x00\x00G\xf8\x83\x00\x00\x00\x00\x00I\x04\x15p\x00\x00\x00\x00I\xd8e\x00\x00\x00\x00\x00J\xe3\xf7" + - "p\x00\x00\x00\x00K\x9c\x97\x80\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xa2@\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x01\bL" + - "MT\x00CST\x00CDT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rs\xb0\xeau\xb4\x01\x00\x00\xb4\x01\x00" + - "\x00\x10\x00\x1c\x00America/EirunepeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x88\x80\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff" + - "\xbaސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP" + - "\xff\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff" + - "\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0" + - "\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00\x00\x00\x00,\xc0\xd1P\x00\x00\x00\x00-f\xe0@\x00\x00\x00\x00H`\u007fP\x00\x00\x00\x00R\u007f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xbe\x80\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-" + - "05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x10\x00\x1c\x00America/ShiprockUT\t\x00\x03\x15\xac\x0e`" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + - "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04" + - "\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff" + - "\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\b" + - "v\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00" + - "\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d" + - "5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00" + - "\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169" + - ")\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00" + - "\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5" + - "\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00" + - "\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s" + - "\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00" + - "\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@o" + - "ΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03" + - "\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT" + - "\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe90T\x16\xd1" + - "\x01\x00\x00\xd1\x01\x00\x00\x0f\x00\x1c\x00America/GodthabUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9b\x80h\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb" + - "\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00" + - "\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#3<-02>,M3.5.0/-2,M10.5.0/-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RU\r\xf7\xd3\xc7\x01\x00" + - "\x00\xc7\x01\x00\x00\r\x00\x1c\x00America/ThuleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9b\x80w\xfc\x00\x00\x00\x00'\xf5z\xe0\x00\x00\x00\x00(\xe5]\xd0\x00\x00\x00\x00)\xd5\\\xe0\x00\x00\x00" + - "\x00*\xc5?\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY" + - "\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00" + - "\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT" + - "\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xbf\x84\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00ADT\x00AST" + - "\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe5s\xb3\\'\x01\x00\x00'\x01\x00\x00\x0f\x00\x1c\x00Ameri" + - "ca/ManaguaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x10\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffi\x87,d\xff\xff\xff\xff\xbd-H\xe8\x00\x00\x00\x00\x06Ct`\x00\x00\x00\x00\t\xa4>P\x00\x00\x00\x00\x11Q\xf8\xe0\x00\x00\x00\x00\x11\xd4o" + - "P\x00\x00\x00\x00\x131\xda\xe0\x00\x00\x00\x00\x13\xb4QP\x00\x00\x00\x00)a\x91 \x00\x00\x00\x00*\xc1KP\x00\x00\x00\x00+C\xdd\xe0\x00\x00\x00\x002\xc9\xefP\x00\x00\x00\x00BX\xc0\xe0\x00\x00\x00" + - "\x00C?iP\x00\x00\x00\x00DTn\x80\x00\x00\x00\x00E\x1fY`\x01\x02\x03\x02\x04\x02\x04\x02\x03\x02\x03\x02\x04\x02\x04\x02\xff\xff\xaf\x1c\x00\x00\xff\xff\xaf\x18\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00" + - "\f\xff\xff\xb9\xb0\x01\x10LMT\x00MMT\x00CST\x00EST\x00CDT\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RM\x94\xc7Kp\x03\x00\x00p\x03\x00\x00\x11" + - "\x00\x1c\x00America/Glace_BayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x86\x90\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xba" + + "ސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff" + + "\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa" + + "\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00" + + "\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00\x00\x00\x00H`\x7fP\x00\x00\x00\x00R\x7f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x03\x02\xff\xff\xc0p\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "]\"_WJ\x05\x00\x00J\x05\x00\x00\x10\x00\x1c\x00America/SantiagoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x1d\xc5\xff\xff\xff\xff\x8f0GE\xff\xff\xff\xff\x9b\\\xe5P\xff" + + "\xff\xff\xff\x9f|\xe2\xc5\xff\xff\xff\xff\xa1\x00q\xc0\xff\xff\xff\xff\xb0^w\xc5\xff\xff\xff\xff\xb1w=@\xff\xff\xff\xff\xb2A\x00\xd0\xff\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb5" + + "9\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff" + + "\xff\xff\xff\xd3\u070f\xc0\xff\xff\xff\xff\xd4\x17\xd50\xff\xff\xff\xff\xd53U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00" + + "rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00" + + "\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e" + + "\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00" + + "\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c" + + "\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \x7f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00" + + "\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*" + + "צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00" + + "\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008" + + "\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00" + + "\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G" + + "\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00" + + "\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W" + + "7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x00\x00\x00\x00]t|\xc0\x00" + + "\x00\x00\x00^\x89I\xb0\x00\x00\x00\x00_T^\xc0\x00\x00\x00\x00`i+\xb0\x00\x00\x00\x00a4@\xc0\x00\x00\x00\x00bI\r\xb0\x00\x00\x00\x00c\x1d]@\x00\x00\x00\x00d(\xef\xb0\x01\x02\x01\x03\x01" + + "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x05\x04\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x03\x05\x03\x05\x03\xff\xff\xbd\xbb\x00\x00\xff\xff\xbd\xbb\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00SMT\x00-05\x00-04\x00-03" + + "\x00\n<-04>4<-03>,M9.1.6/24,M4.1.6/24\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00" + + "\x10\x00\x1c\x00America/ShiprockUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80\xf1\xa84\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2" + - "#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xe0\x9e?`\xff\xff\xff\xff\xe1i8P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00" + - "\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e" + - "\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00" + - "\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c" + - "\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00" + - "\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*" + - "\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00" + - "\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008" + - "\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00" + - "\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x03\x04" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xc7\xcc\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00ADT\x00AST\x00AWT\x00" + - "APT\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R⚵\xfb\x9e\x00\x00\x00\x9e\x00\x00\x00\x0f\x00\x1c\x00Am" + - "erica/CrestonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff^=p\xbc\xff\xff\xff\xff\x9b\xd6Kp\xff\xff\xff\xff\x9e\xf9;\x00\x01\x02\x01\xff\xff\x92\xc4\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00" + - "\bLMT\x00MST\x00PST\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RB\xa0=:\x1e\x01\x00\x00\x1e\x01\x00\x00\x12\x00\x1c\x00America/Her" + - "mosilloUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x0f\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff" + - "\xff\xff\xcb\xeaq`\xff\xff\xff\xffؑ\xb4\xf0\x00\x00\x00\x00\x00\x00p\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'" + - "H\x10\x00\x00\x00\x0062ڀ\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\xff\xff\x97\xf8\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10LMT\x00M" + - "ST\x00CST\x00PST\x00MDT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x14\x00\x1c\x00America/S" + - "anta_IsabelUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1" + + "\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff" + + "\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd" + + "\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00" + + "\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v" + + "ࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00" + + "\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a" + + "\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00" + + "\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'" + + "\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00" + + "\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x006" + + "2ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00" + + "\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D" + + "/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94" + + "\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2." + + "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU8O:\xbf\x95\x03\x00\x00\x95\x03\x00\x00\x11\x00\x1c\x00America/MenomineeUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x06\x00\x00\x00\x18" + + "\xff\xff\xff\xffawIc\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff" + + "\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff\xff\xff\xfa\bg\xf0\xff\xff\xff\xff\xfe\xb8+\x00\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xd0p" + + "\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00" + + "\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00" + + "\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00" + + "\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0" + + "\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00" + + "*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00" + + "\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x00" + + "8\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0" + + "\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01" + + "\x02\x03\x04\x02\x01\x02\x01\x02\x05\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad\xdd\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CD" + + "T\x00CST\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUԾ\xe7#" + + "\x95\x00\x00\x00\x95\x00\x00\x00\x10\x00\x1c\x00America/AtikokanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffi\x87&\x10\xff\xff\xff\xff\x8b\xf4a\xe8\x01\x02\xff\xff\xb5p\x00\x00\xff\xff\xb5\x18\x00" + + "\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x10\x00\x1c\x00Ameri" + + "ca/EnsenadaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fdp\xff\xff\xff\xff\xb7\x1b\x10\x00\xff\xff\xff\xff\xb8\n" + "\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1bY\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff\xff" + @@ -1053,459 +626,715 @@ const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00 "\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02\x03\x02\x03" + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14LMT\x00" + - "MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\x0f" + - "(\b=\x01\x00\x00=\x01\x00\x00\x15\x00\x1c\x00America/Santo_DomingoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x06\x00\x00\x00\x1b\xff\xff\xff\xffi\x87\x1d\b\xff\xff\xff\xff\xba\xdfB`\xff\xff\xff\xff\xfa\b" + - "K\xd0\xff\xff\xff\xff\xfa\xa7\xc3@\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00C{\xc8\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x01\xfa\u007fH\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x03\xdd\x04H\x00\x00" + - "\x00\x00\x05P\xd2P\x00\x00\x00\x00\x05\xbf\x89H\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\a\xa0\xbc\xc8\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:)\xe1`\x01\x03\x02\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x05\x03\x05\xff\xff\xbex\x00\x00\xff\xff\xbe`\x00\x04\xff\xff\xc7\xc0\x01\t\xff\xff\xb9\xb0\x00\r\xff\xff\xc0\xb8\x01\x11\xff\xff\xc7\xc0\x00\x17LMT\x00SDMT\x00EDT\x00" + - "EST\x00-0430\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x1c\x00America/St" + - "_VincentUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\xd6\xe1Հ\x9c\x01\x00\x00\x9c\x01\x00\x00\x13\x00\x1c\x00America/Mexico_CityUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6f" + - "V`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff\xc5ް`\xff\xff\xff\xffƗ4P\xff\xff\xff\xff\xc9U\xf1\xe0\xff\xff\xff\xff\xc9\xea\xddP\xff\xff" + - "\xff\xff\xcf\x02\xc6\xe0\xff\xff\xff\xffϷVP\xff\xff\xff\xffڙ\x15\xe0\xff\xff\xff\xff\xdbv\x83\xd0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R" + - "\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00" + - "\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xa3\f\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff" + - "\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00MST\x00CST\x00CDT\x00CWT\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x15\xc8\xcb\x00\xac\x00\x00\x00\xac\x00\x00\x00\x0e\x00\x1c\x00America/GuyanaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x98\xd9y\x88\x00\x00\x00\x00\n}\xb4<\x00\x00" + - "\x00\x00'\u007f\xfb0\x01\x02\x03\xff\xff\xc9x\x00\x00\xff\xff\xcbD\x00\x04\xff\xff\xd5\xd0\x00\n\xff\xff\xc7\xc0\x00\x0eLMT\x00-0345\x00-03\x00-04\x00\n<-04>4\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x15\x00\x1c\x00America/Port_of_SpainUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373" + - "\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\x0f\x00\x1c\x00Am" + - "erica/DetroitUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x04," + + "2h\x99\x01\x00\x00\x99\x01\x00\x00\x10\x00\x1c\x00America/SantaremUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaazH\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff" + + "\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec" + + "0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff" + + "\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C" + + "\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00H`q@\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x03\xff\xff̸\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x00\x04LMT\x00-03\x00-04\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "Uq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00America/St_LuciaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p" + + "\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x1c\x00America/Kentucky/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x03\x1a|J\xcc\x03\x00\x00\xcc\x03\x00\x00\x1b\x00\x1c\x00America/Kentucky/" + + "MonticelloUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00W\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe" + + "\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00" + + "\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'" + + "\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00" + + "\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a" + + "\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00" + + "\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3" + + "\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00" + + "\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\b" + + "p\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00" + + "\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2" + + "p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\xff\xff\xb0t\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xc7\xc0\x01\x14\xff\xff\xb9\xb0\x00\x18LM" + + "T\x00CDT\x00CST\x00CWT\x00CPT\x00EDT\x00EST\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vU\xdf\xe5\x8d\xc4\xda\x04\x00\x00\xda\x04\x00\x00\x1b\x00\x1c\x00America/Kentucky/LouisvilleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff" + + "\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa4s\xf7\x00\xff\xff\xff\xff\xa5\x16\x11p\xff\xff\xff\xff\xca\rN\x80\xff\xff\xff\xff\xca\xd8" + + "Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xd7\x1c\xff\xff\xff\xffӤ\tp\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff" + + "\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I" + + "6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe9\x17\x00\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff" + + "\xff\xff\xea\xf6\xe2\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x1e\x90p\xff\xff\xff\xff\xfc\xd8" + + ":\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00" + + "\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0" + + "\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00" + + "\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b" + + "\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00" + + "\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*" + + "s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00" + + "\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'" + + "+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00" + + "\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cd" + + "a`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + + "\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf\x9a\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff" + + "\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00America/DominicaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf6" + + "2\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AW" + + "T\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x15\x00\x1c\x00America/Port_of_SpainU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00" + + "\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0" + + "\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xea$\xc1\xbf\xb0\x00\x00\x00\xb0\x00\x00\x00\x13\x00\x1c\x00Americ" + + "a/El_SalvadorUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x85\xbd\"[\xff\xff\xff\xff\x99<\x94\x00\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff" + - "\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0\xff\xff\xff\xff\xfb3\x90\x8c\xff\xff\xff\xff\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`" + - "\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00" + - "\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0" + + "\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa3զ \x00\x00\x00\x00 \x9a\xdc\xe0\x00\x00\x00\x00!\\\x9bP\x00\x00\x00\x00\"z\xbe\xe0\x00\x00\x00\x00#<}P\x02\x01\x02\x01" + + "\x02\xff\xff\xac`\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00CDT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xfe7\xa1\x87\x1b\x01\x00\x00\x1b" + + "\x01\x00\x00\f\x00\x1c\x00America/LimaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xffi\x87#\xbc\xff\xff\xff\xff\x8ct@\xd4\xff\xff\xff\xff\xc3\xcfJP\xff\xff\xff\xff\xc4E\xe3@\xff\xff\xff\xff\xc5/" + + "J\xd0\xff\xff\xff\xff\xc6\x1f-\xc0\xff\xff\xff\xff\xc7\x0f,\xd0\xff\xff\xff\xff\xc7\xff\x0f\xc0\x00\x00\x00\x00\x1e\x18\xc4P\x00\x00\x00\x00\x1e\x8f]@\x00\x00\x00\x00\x1f\xf9\xf7\xd0\x00\x00\x00\x00 p\x90\xc0\x00\x00" + + "\x00\x00%\x9e\xe3\xd0\x00\x00\x00\x00&\x15|\xc0\x00\x00\x00\x00-%\x03P\x00\x00\x00\x00-\x9b\x9c@\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb7\xc4\x00\x00\xff\xff\xb7\xac\x00\x00\xff\xff" + + "\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\bLMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU@\x13\x9b\xb1\xc2\x04\x00\x00\xc2\x04\x00\x00\x14\x00\x1c\x00" + + "America/Punta_ArenasUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x1d\xfc\xff\xff\xff\xff\x8f0GE\xff\xff\xff\xff\x9b\\\xe5P\xff\xff\xff\xff\x9f|\xe2\xc5\xff\xff\xff\xff\xa1" + + "\x00q\xc0\xff\xff\xff\xff\xb0^w\xc5\xff\xff\xff\xff\xb1w=@\xff\xff\xff\xff\xb2A\x00\xd0\xff\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb59\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff" + + "\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff\xff\xff\xff\xd4\x17\xe3@\xff\xff\xff\xff\xd5" + + "3U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00" + + "\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t" + + "\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00" + + "\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18" + + "\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00" + + "\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \x7f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%" + + "\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00" + + "\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004" + + "@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00" + + "\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B" + + "3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00" + + "\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00P" + + "B\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00XC\x86\xb0\x01" + + "\x02\x01\x03\x01\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x04\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + + "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x06\xff\xff\xbd\x84" + + "\x00\x00\xff\xff\xbd\xbb\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01\f\xff\xff\xd5\xd0\x01\x10\xff\xff\xd5\xd0\x00\x10LMT\x00SMT\x00-05\x00-04\x00-03\x00\n<" + + "-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUd\xa9y\x9at\x03\x00\x00t\x03\x00\x00\x10\x00\x1c\x00America/AsuncionUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xffi" + + "\x87\x11\x90\xff\xff\xff\xff\xb8\x17\xf5\x90\x00\x00\x00\x00\x05+\xda@\x00\x00\x00\x00\a\xfc\xf0\xb0\x00\x00\x00\x00\n\xcft\xc0\x00\x00\x00\x00\v\x97ʰ\x00\x00\x00\x00\f\xb1\xf9\xc0\x00\x00\x00\x00\rx\xfe0\x00" + + "\x00\x00\x00\x0e\x93-@\x00\x00\x00\x00\x0fZ1\xb0\x00\x00\x00\x00\x10t`\xc0\x00\x00\x00\x00\x11dC\xb0\x00\x00\x00\x00\x12U\x94@\x00\x00\x00\x00\x13FȰ\x00\x00\x00\x00\x148\x19@\x00\x00\x00\x00\x15" + + "'\xfc0\x00\x00\x00\x00\x16\x19L\xc0\x00\x00\x00\x00\x17\t/\xb0\x00\x00\x00\x00\x17\xfa\x80@\x00\x00\x00\x00\x18\xeac0\x00\x00\x00\x00\x19۳\xc0\x00\x00\x00\x00\x1a\xcc\xe80\x00\x00\x00\x00\x1b\xbe8\xc0\x00" + + "\x00\x00\x00\x1c\xae\x1b\xb0\x00\x00\x00\x00\x1d\x9fl@\x00\x00\x00\x00\x1e\x8fO0\x00\x00\x00\x00\x1f\x80\x9f\xc0\x00\x00\x00\x00 p\x82\xb0\x00\x00\x00\x00!a\xd3@\x00\x00\x00\x00\"S\a\xb0\x00\x00\x00\x00#" + + "DX@\x00\x00\x00\x00$4;0\x00\x00\x00\x00%A;@\x00\x00\x00\x00&\x15n\xb0\x00\x00\x00\x00'\x06\xbf@\x00\x00\x00\x00'\xf6\xa20\x00\x00\x00\x00(\xee\x8a@\x00\x00\x00\x00)\xb0H\xb0\x00" + + "\x00\x00\x00*Ͻ\xc0\x00\x00\x00\x00+\xb9\t0\x00\x00\x00\x00,\xab\xab@\x00\x00\x00\x00-p\f\xb0\x00\x00\x00\x00.\x8c\xde\xc0\x00\x00\x00\x00/O\xee\xb0\x00\x00\x00\x000n\x12@\x00\x00\x00\x001" + + "6h0\x00\x00\x00\x002W.\xc0\x00\x00\x00\x003\x0f\xb2\xb0\x00\x00\x00\x0047\x10\xc0\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006\x16\xf2\xc0\x00\x00\x00\x006\xe1\xeb\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00" + + "\x00\x00\x008\xc1Ͱ\x00\x00\x00\x009ֶ\xc0\x00\x00\x00\x00:\xa1\xaf\xb0\x00\x00\x00\x00;\xbf\xd3@\x00\x00\x00\x00<\xaf\xb60\x00\x00\x00\x00=q\x90\xc0\x00\x00\x00\x00>\x8f\x980\x00\x00\x00\x00?" + + "Z\xad@\x00\x00\x00\x00@oz0\x00\x00\x00\x00Aq\xee@\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00" + + "\x00\x00\x00G\x1a\xce\xc0\x00\x00\x00\x00G\xd3R\xb0\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00\x00\x00I\xb34\xb0\x00\x00\x00\x00Jڒ\xc0\x00\x00\x00\x00K\xc1;0\x00\x00\x00\x00L\xa7\xff\xc0\x00\x00\x00\x00M" + + "\xa1\x1d0\x00\x00\x00\x00N\x87\xe1\xc0\x00\x00\x00\x00O\x80\xff0\x00\x00\x00\x00Pp\xfe@\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04" + + "\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\xff\xff\xc9\xf0\x00\x00\xff\xff\xc9\xf0\x00\x04\xff\xff" + + "\xc7\xc0\x00\b\xff\xff\xd5\xd0\x00\f\xff\xff\xd5\xd0\x01\fLMT\x00AMT\x00-04\x00-03\x00\n<-04>4<-03>,M10.1.0/0,M3.4." + + "0/0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\x0f\x00\x1c\x00America/JamaicaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87#~" + + "\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00" + + "\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0" + + "\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xb8\x02\x00\x00\xff\xff\xb8\x02\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\fLMT\x00KMT\x00EST\x00EDT\x00\nEST5\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x0f\x00\x1c\x00America/TortolaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0" + + "\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00" + + "\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe5s\xb3\\'\x01\x00\x00'\x01\x00\x00\x0f\x00\x1c\x00America/ManaguaUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffi\x87" + + ",d\xff\xff\xff\xff\xbd-H\xe8\x00\x00\x00\x00\x06Ct`\x00\x00\x00\x00\t\xa4>P\x00\x00\x00\x00\x11Q\xf8\xe0\x00\x00\x00\x00\x11\xd4oP\x00\x00\x00\x00\x131\xda\xe0\x00\x00\x00\x00\x13\xb4QP\x00\x00" + + "\x00\x00)a\x91 \x00\x00\x00\x00*\xc1KP\x00\x00\x00\x00+C\xdd\xe0\x00\x00\x00\x002\xc9\xefP\x00\x00\x00\x00BX\xc0\xe0\x00\x00\x00\x00C?iP\x00\x00\x00\x00DTn\x80\x00\x00\x00\x00E\x1f" + + "Y`\x01\x02\x03\x02\x04\x02\x04\x02\x03\x02\x03\x02\x04\x02\x04\x02\xff\xff\xaf\x1c\x00\x00\xff\xff\xaf\x18\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10LMT\x00MMT\x00CST\x00" + + "EST\x00CDT\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x0f\x00\x1c\x00America/GrenadaU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00" + + "\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0" + + "\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x14\x00\x1c\x00Americ" + + "a/Santa_IsabelUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fdp\xff\xff\xff\xff\xb7\x1b\x10\x00\xff\xff\xff" + + "\xff\xb8\n\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1bY\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR" + + "\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff" + + "\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91" + + "\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00" + + "\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17" + + "\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00" + + "\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xde\xcf" + + "\xa0\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00" + + "\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05" + + "\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00" + + "\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00F\x0f\x82" + + "\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14L" + + "MT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\x0f\x00\x1c\x00America/GodthabUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9b\x80h\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00" + + "\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b" + + "\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00" + + "\x00\x00\x00#3<-02>,M3.5.0/-2,M10.5.0/-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1e" + + "\xfbn۸\x03\x00\x00\xb8\x03\x00\x00\x14\x00\x1c\x00America/Campo_GrandeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaaz4\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfd" + + "N\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff" + + "\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q" + + ":@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00" + + "\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00#X\x1e\xc0\x00\x00\x00\x00#\xe2~0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xd4" + + "\xd50\x00\x00\x00\x00'!\x1d@\x00\x00\x00\x00'\xbd\xf1\xb0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\x94\x990\x00\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+k@\xb0\x00\x00\x00\x00,\xc0\xc3@\x00\x00" + + "\x00\x00-f\xd20\x00\x00\x00\x00.\xa0\xa5@\x00\x00\x00\x00/F\xb40\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001\x1d[\xb0\x00\x00\x00\x002W.\xc0\x00\x00\x00\x003\x06x0\x00\x00\x00\x0048" + + "b@\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006 -@\x00\x00\x00\x006\xcfv\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x00:\x8f:\xb0\x00\x00" + + "\x00\x00;\xc9\r\xc0\x00\x00\x00\x00N\xfe\xb0\x00\x00\x00\x00?\x92\f@\x00\x00\x00\x00@.\xe0\xb0\x00\x00\x00\x00A\x87\x06@\x00\x00\x00\x00B\x17" + + "\xfd0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00C\xf7\xdf0\x00\x00\x00\x00EMa\xc0\x00\x00\x00\x00E\xe0\xfb\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xb7\xa30\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00" + + "\x00\x00I\x97\x850\x00\x00\x00\x00Jڒ\xc0\x00\x00\x00\x00K\x80\xa1\xb0\x00\x00\x00\x00L\xbat\xc0\x00\x00\x00\x00M`\x83\xb0\x00\x00\x00\x00N\x9aV\xc0\x00\x00\x00\x00OI\xa00\x00\x00\x00\x00P\x83" + + "s@\x00\x00\x00\x00Q G\xb0\x00\x00\x00\x00RcU@\x00\x00\x00\x00S\x00)\xb0\x00\x00\x00\x00TC7@\x00\x00\x00\x00T\xe9F0\x00\x00\x00\x00V#\x19@\x00\x00\x00\x00V\xc9(0\x00\x00" + + "\x00\x00X\x02\xfb@\x00\x00\x00\x00X\xa9\n0\x00\x00\x00\x00Y\xe2\xdd@\x00\x00\x00\x00Z\x88\xec0\x00\x00\x00\x00[\xden\xc0\x00\x00\x00\x00\\h\xce0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xcc\xcc\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vUV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x0e\x00\x1c\x00America/DenverUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff" + + "\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ" + + "\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff" + + "\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w" + + "\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00" + + "\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99" + + "\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00" + + "\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1" + + "\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00" + + "\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3" + + "p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00" + + "\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6" + + "\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00" + + "\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT" + + "\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\x13\x00\x1c\x00A" + + "merica/Los_AngelesUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7" + + "\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd6\xfet\\\xff\xff\xff\xff\u0600\xad\x90\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff" + + "\xff\xdcޥ\x90\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR" + + "\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff" + + "\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8f\xd0" + + "\x90\xff\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xb2\x90\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\x94\x90\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff" + + "\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*" + + "\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00" + + "\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91" + + "\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00" + + "\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17" + + "\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00" + + "\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xde\xcf" + + "\xa0\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00" + + "\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05" + + "\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00" + + "\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3" + + " \x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\xff\xff\x91&\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST" + + "8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf7\xe9 y\xbd\x02\x00\x00\xbd\x02\x00\x00\x0e\x00\x1c\x00America/In" + + "uvikUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00" + + "\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xe0\x06N\x80\xff\xff\xff\xff\xf7/h\x80\xff\xff\xff\xff\xf8(\x94\x00\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15" + + "IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00" + + "\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#" + + "j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00" + + "\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001" + + "g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00" + + "\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?" + + "\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02" + + "\x01\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00" + + "\x00\x00\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x8f\x80\x00\t\xff\xff\x9d\x90\x00\r\xff\xff\xab\xa0\x01\x11-00\x00PDDT\x00PST\x00MST\x00MDT\x00\nMST7MDT,M3" + + ".2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00\x11\x00\x1c\x00America/Catamarca" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00" + + "\x00\x00\x14\xff\xff\xff\xffr\x9c\xaf,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff" + + "\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1" + + "\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff" + + "\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4" + + "=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff" + + "\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b" + + "$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00" + + "\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00G" + + "w\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05" + + "\x04\x02\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xc2T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00" + + "-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\f\x00\x1c\x00America/AtkaUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00" + + "!\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87Z^\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0\xff\xff\xff\xff\xfe\xb8qP\xff\xff\xff" + + "\xff\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00\x00\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00\x05Q4\xc0\x00\x00\x00\x00\x06A3" + + "\xd0\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t\xad\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0\x00\x00\x00\x00\f\xd9\xf7@\x00\x00\x00" + + "\x00\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00\x00\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00\x13i\x9cP\x00\x00\x00\x00\x14Y\x7f" + + "@\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18\"}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0\x00\x00\x00\x00\x1a+\" \x00\x00\x00" + + "\x00\x1a\xf2P\xc0\x00\x00\x00\x00\x1b\xe23\xb0\x00\x00\x00\x00\x1c\xd22\xc0\x00\x00\x00\x00\x1d\xc2\x15\xb0\x00\x00\x00\x00\x1e\xb2\x14\xc0\x00\x00\x00\x00\x1f\xa1\xf7\xb0\x00\x00\x00\x00 vG@\x00\x00\x00\x00!\x81\xd9" + + "\xb0\x00\x00\x00\x00\"V)@\x00\x00\x00\x00#j\xf60\x00\x00\x00\x00$6\v@\x00\x00\x00\x00%J\xd80\x00\x00\x00\x00&\x15\xed@\x00\x00\x00\x00'*\xba0\x00\x00\x00\x00'\xff\t\xc0\x00\x00\x00" + + "\x00)\n\x9c0\x00\x00\x00\x00)\xde\xeb\xc0\x00\x00\x00\x00*\xea~0\x00\x00\x00\x00+\xbe\xcd\xc0\x00\x00\x00\x00,Ӛ\xb0\x00\x00\x00\x00-\x9e\xaf\xc0\x00\x00\x00\x00.\xb3|\xb0\x00\x00\x00\x00/~\x91" + + "\xc0\x00\x00\x00\x000\x93^\xb0\x00\x00\x00\x001g\xae@\x00\x00\x00\x002s@\xb0\x00\x00\x00\x003G\x90@\x00\x00\x00\x004S\"\xb0\x00\x00\x00\x005'r@\x00\x00\x00\x0063\x04\xb0\x00\x00\x00" + + "\x007\aT@\x00\x00\x00\x008\x1c!0\x00\x00\x00\x008\xe76@\x00\x00\x00\x009\xfc\x030\x00\x00\x00\x00:\xc7\x18@\x00\x00\x00\x00;\xdb\xe50\x00\x00\x00\x00<\xb04\xc0\x00\x00\x00\x00=\xbb\xc7" + + "0\x00\x00\x00\x00>\x90\x16\xc0\x00\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0\x00\x00\x00\x00D/\xbc\xc0\x00\x00\x00" + + "\x00ED\x89\xb0\x00\x00\x00\x00E\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t" + + "\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00\xff\xffeP\x00\x04\xff\xffs`\x01" + + "\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00NPT\x00BST\x00BDT" + + "\x00AHST\x00HDT\x00\nHST10HDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU):\x17-\x88\x06\x00\x00\x88\x06\x00" + + "\x00\x0f\x00\x1c\x00America/HalifaxUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa7\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80\xf1\xab\xa0\xff\xff\xff\xff\x9a\xe4\xde\xc0\xff\xff\xff\xff\x9b\xd6\x130\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f" + + "\xba\xddP\xff\xff\xff\xff\xa2\x9d\x17@\xff\xff\xff\xff\xa30\xb10\xff\xff\xff\xff\xa4zV@\xff\xff\xff\xff\xa5\x1b\x1f0\xff\xff\xff\xff\xa6S\xa0\xc0\xff\xff\xff\xff\xa6\xfcR\xb0\xff\xff\xff\xff\xa8<\xbd@\xff" + + "\xff\xff\xff\xa8\xdc4\xb0\xff\xff\xff\xff\xaa\x1c\x9f@\xff\xff\xff\xff\xaa\xcd:0\xff\xff\xff\xff\xab\xfc\x81@\xff\xff\xff\xff\xac\xbf\x910\xff\xff\xff\xff\xad\xee\xd8@\xff\xff\xff\xff\xae\x8c\xfe0\xff\xff\xff\xff\xaf" + + "\xbcE@\xff\xff\xff\xff\xb0\x7fU0\xff\xff\xff\xff\xb1\xae\x9c@\xff\xff\xff\xff\xb2Kp\xb0\xff\xff\xff\xff\xb3\x8e~@\xff\xff\xff\xff\xb4$\xbb0\xff\xff\xff\xff\xb5n`@\xff\xff\xff\xff\xb6\x15\xc0\xb0\xff" + + "\xff\xff\xff\xb7NB@\xff\xff\xff\xff\xb8\b\x17\xb0\xff\xff\xff\xff\xb9$\xe9\xc0\xff\xff\xff\xff\xb9\xe7\xf9\xb0\xff\xff\xff\xff\xbb\x04\xcb\xc0\xff\xff\xff\xff\xbb\xd1\x160\xff\xff\xff\xff\xbd\x00]@\xff\xff\xff\xff\xbd" + + "\x9d1\xb0\xff\xff\xff\xff\xbe\xf2\xb4@\xff\xff\xff\xff\xbf\x90\xda0\xff\xff\xff\xff\xc0\xd3\xe7\xc0\xff\xff\xff\xff\xc1^G0\xff\xff\xff\xff\u008d\x8e@\xff\xff\xff\xff\xc3P\x9e0\xff\xff\xff\xff\xc4mp@\xff" + + "\xff\xff\xff\xc50\x800\xff\xff\xff\xff\xc6r<@\xff\xff\xff\xff\xc7\x10b0\xff\xff\xff\xff\xc86n\xc0\xff\xff\xff\xff\xc8\xf9~\xb0\xff\xff\xff\xff\xca\x16P\xc0\xff\xff\xff\xff\xca\xd9`\xb0\xff\xff\xff\xff\xcb" + + "\x88\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff\xff\xff\xff\xd4@\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0\xff" + + "\xff\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xdc\xde{`\xff\xff\xff\xffݩtP\xff\xff\xff\xff\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0" + + "\x9e?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff\xff\xff\xff\xe6G\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe8\xf1\xfa\xd0\xff" + + "\xff\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xd1\xdc\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff챾\xd0\xff\xff\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\x7f\x89P\xff\xff\xff\xff\xf3o\x88`\xff\xff\xff\xff\xf4" + + "_kP\xff\xff\xff\xff\xf5Oj`\xff\xff\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff" + + "\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02" + + "w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00" + + "\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10" + + "\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00" + + "\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e" + + "\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00" + + "\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00," + + "\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00" + + "\x00\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:" + + "\xc6\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00" + + "\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xc4`\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00" + + "ADT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x01\x05\xf3\x89\xb5\x00" + + "\x00\x00\xb5\x00\x00\x00\x0e\x00\x1c\x00America/GuyanaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\x92\x1d\x0f\x87\xff\xff\xff\xff\x98\xd9{@\x00\x00\x00\x00\n\x7f\x05\xbc\x00\x00\x00\x00)\xd5@\xc0\x01" + + "\x02\x03\x01\xff\xff\xc9y\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xcbD\x00\b\xff\xff\xd5\xd0\x00\x0eLMT\x00-04\x00-0345\x00-03\x00\n<-04>4\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vUp\x1b\xceRC\x03\x00\x00C\x03\x00\x00\x0f\x00\x1c\x00America/NipigonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xee\x81@\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff" + + "\xff\x9f\xba\xeb`\xff\xff\xff\xff\xc8\xf8IP\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\x00\x00\x00\x00\b \xc1p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3" + + "p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00" + + "\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")" + + "`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00" + + "\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6" + + "\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00" + + "\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc" + + "`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00" + + "\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94" + + "p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad@\x00\x00\xff\xff\xc7\xc0\x01\x04\xff" + + "\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xdf\xe5\x8d\xc4\xda\x04\x00\x00\xda\x04\x00\x00\x12\x00\x1c\x00America/LouisvilleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0" + + "\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa4s\xf7\x00\xff\xff\xff\xff\xa5\x16\x11p\xff\xff\xff\xff\xca\rN\x80\xff\xff\xff\xff" + + "\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xd7\x1c\xff\xff\xff\xffӤ\tp\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0" + + "\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff" + + "\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe9\x17\x00\xf0\xff\xff\xff\xff\xea\a\x00\x00" + + "\xff\xff\xff\xff\xea\xf6\xe2\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x1e\x90p\xff\xff\xff\xff" + + "\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`" + + "\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00" + + "\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0" + + "\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00" + + "\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`" + + "\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00" + + "'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip" + + "\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x00" + + "5'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0" + + "\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00" + + "Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + + "\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf\x9a\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00" + + "\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vUU!\x12f\xd9\x02\x00\x00\xd9\x02\x00\x00\x13\x00\x1c\x00America/YellowknifeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x19\xff\xff\xff\xff\xbe*\x18\x00\xff" + + "\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(\x85\xf0\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15" + + "IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00" + + "\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#" + + "j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00" + + "\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001" + + "g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00" + + "\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?" + + "\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x03" + + "\x01\x02\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x00\x00\x00\x00\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xab\xa0\x01\x15-00\x00MWT\x00MPT\x00MST\x00MDDT\x00MDT" + + "\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\a\x1c\x9e\x9a]\x04\x00\x00]\x04\x00\x00\x0e\x00\x1c\x00Ameri" + + "ca/HavanaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00j\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87(\xb8\xff\xff\xff\xff\xacb\u0080\xff\xff\xff\xff\xb1ӔP\xff\xff\xff\xff\xb2t]@\xff\xff\xff\xff\xc8[f\xd0\xff\xff\xff\xff\xc8\xd3Q@" + + "\xff\xff\xff\xff\xca;H\xd0\xff\xff\xff\xffʼm\xc0\xff\xff\xff\xff\xcc$eP\xff\xff\xff\xff̜O\xc0\xff\xff\xff\xff\xd1\xc4\vP\xff\xff\xff\xff\xd2;\xf5\xc0\xff\xff\xff\xffӣ\xedP\xff\xff\xff\xff" + + "\xd4\x1b\xd7\xc0\xff\xff\xff\xff\xf7`\x05\xd0\xff\xff\xff\xff\xf7\xff}@\xff\xff\xff\xff\xf9=D\xd0\xff\xff\xff\xff\xf9\xe3S\xc0\xff\xff\xff\xff\xfa\xdb;\xd0\xff\xff\xff\xff\xfb\xa7\x86@\xff\xff\xff\xff\xfcũ\xd0" + + "\xff\xff\xff\xff\xfd\x87h@\xff\xff\xff\xff\xfe\xb8\x00\xd0\xff\xff\xff\xff\xff\xa7\xe3\xc0\x00\x00\x00\x00\x00\x97\xe2\xd0\x00\x00\x00\x00\x01\x87\xc5\xc0\x00\x00\x00\x00\x02w\xc4\xd0\x00\x00\x00\x00\x03p\xe2@\x00\x00\x00\x00" + + "\x04`\xe1P\x00\x00\x00\x00\x055\x14\xc0\x00\x00\x00\x00\x06@\xc3P\x00\x00\x00\x00\a\x16H@\x00\x00\x00\x00\b \xa5P\x00\x00\x00\x00\b\xf7{\xc0\x00\x00\x00\x00\n\x00\x87P\x00\x00\x00\x00\n\xf0j@" + + "\x00\x00\x00\x00\v\xe0iP\x00\x00\x00\x00\fن\xc0\x00\x00\x00\x00\r\xc0KP\x00\x00\x00\x00\x0e\xb9h\xc0\x00\x00\x00\x00\x0f\xb2\xa2P\x00\x00\x00\x00\x10}\x9b@\x00\x00\x00\x00\x11Q\xea\xd0\x00\x00\x00\x00" + + "\x12f\xb7\xc0\x00\x00\x00\x00\x131\xcc\xd0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15[\x82\xd0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x17;d\xd0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x19\x1bF\xd0" + + "\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xfb(\xd0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\xdb\n\xd0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ezSP\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00" + + " Z5P\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"CQ\xd0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$#3\xd0\x00\x00\x00\x00%.\xc6@\x00\x00\x00\x00&\x15\x8a\xd0\x00\x00\x00\x00'\x17\xe2\xc0" + + "\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00(\xf7\xd2\xd0\x00\x00\x00\x00)މP\x00\x00\x00\x00*״\xd0\x00\x00\x00\x00+\xbekP\x00\x00\x00\x00,\xb7\x96\xd0\x00\x00\x00\x00-\x9eMP\x00\x00\x00\x00" + + ".\x97x\xd0\x00\x00\x00\x00/~/P\x00\x00\x00\x000wZ\xd0\x00\x00\x00\x001gK\xd0\x00\x00\x00\x002W<\xd0\x00\x00\x00\x003G-\xd0\x00\x00\x00\x004@YP\x00\x00\x00\x005\x1d\xd5P" + + "\x00\x00\x00\x0062\xb0P\x00\x00\x00\x006\xfd\xb7P\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:Ƶ\xd0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00" + + "<\xaf\xd2P\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xb4P\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@f[\xd0\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x8c\xd0\x00\x00\x00\x00G$\x17P" + + "\x00\x00\x00\x00GܩP\x00\x00\x00\x00I\x03\xf9P\x00\x00\x00\x00I\xb3P\xd0\x00\x00\x00\x00J\xe3\xdbP\x00\x00\x00\x00K\x9cmP\x00\x00\x00\x00L\xcc\xf7\xd0\x00\x00\x00\x00M\x85\x89\xd0\x00\x00\x00\x00" + + "N\xbfN\xd0\x00\x00\x00\x00Ow\xe0\xd0\x00\x00\x00\x00P\x95\xf6P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\xff\xff\xb2\xc8\x00\x00\xff\xff\xb2\xc0\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00HMT\x00CDT\x00CST\x00\nCST5CDT,M3.2." + + "0/0,M11.1.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU:\x9a1T\xdf\x01\x00\x00\xdf\x01\x00\x00\x14\x00\x1c\x00America/Scoresby" + + "sundUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00" + + "\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\x9b\x80L\x18\x00\x00\x00\x00\x13Mn@\x00\x00\x00\x00\x144$\xc0\x00\x00\x00\x00\x15#\xf9\xa0\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17" + + "\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00" + + "\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#1<+00>,M3.5.0/0,M10.5.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\r\x00\x1c" + + "\x00America/ArubaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff" + + "\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUU\xactA\xb5\x01\x00" + + "\x00\xb5\x01\x00\x00\x11\x00\x1c\x00America/MatamorosUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x001gv" + + "\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00" + + "\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp" + + "\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00F\x0ff\x80\x00\x00\x00" + + "\x00G$3p\x00\x00\x00\x00G\xf8\x83\x00\x00\x00\x00\x00I\x04\x15p\x00\x00\x00\x00I\xd8e\x00\x00\x00\x00\x00J\xe3\xf7p\x00\x00\x00\x00K\x9c\x97\x80\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xa2@\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x01\bLMT\x00CST\x00CDT\x00\nCST6CDT,M3.2" + + ".0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4T\xbd\xeb5\x02\x00\x005\x02\x00\x00\x16\x00\x1c\x00America/Port-au-Pri" + + "nceUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00" + + "\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffi\x87\x1fP\xff\xff\xff\xff\x9cnq\xfc\x00\x00\x00\x00\x19\x1bF\xd0\x00\x00\x00\x00\x1a\x01\xef@\x00\x00\x00\x00\x1a\xf1\xeeP\x00\x00\x00\x00\x1b\xe1\xd1@\x00\x00\x00\x00\x1c\xd1" + + "\xd0P\x00\x00\x00\x00\x1d\xc1\xb3@\x00\x00\x00\x00\x1e\xb1\xb2P\x00\x00\x00\x00\x1f\xa1\x95@\x00\x00\x00\x00 \x91\x94P\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xaf\xe0\x00\x00" + + "\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea" + + "7\xe0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gY\xe0\x00\x00" + + "\x00\x002r\xfa`\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x00BOxP\x00\x00\x00\x00CdE@\x00\x00\x00\x00D/ZP\x00\x00\x00\x00ED'@\x00\x00\x00\x00O\\" + + "Mp\x00\x00\x00\x00P\x96\x04`\x00\x00\x00\x00Q\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92" + + "\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00F\x0ft\x90\x00\x00\x00\x00G$A\x80\x00\x00\x00\x00G\xf8\x91\x10\x00\x00\x00\x00I\x04#\x80\x00\x00\x00\x00I\xd8s\x10\x00\x00\x00\x00J\xe4\x05\x80\x00\x00\x00" + + "\x00K\xb8U\x10\x00\x00\x00\x00L\xcd\x13\xf0\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x05\x02\xff\xff\x9dT\x00\x00\xff\xff" + + "\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00MST\x00CST\x00PST\x00MDT\x00CDT\x00\nCST6CDT" + + ",M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU+\x10`ȫ\x02\x00\x00\xab\x02\x00\x00\x14\x00\x1c\x00America/Dawson" + + "_CreekUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + ":\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^=t8\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff" + + "\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90" + + "\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff" + + "\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c" + + " \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff" + + "\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2" + + "\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff" + + "\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05\x01\xf0\x90\x02\x01\x02\x03\x04\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\xff\xff\x8fH\x00\x00\xff\xff\x9d" + + "\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x00\x14LMT\x00PDT\x00PST\x00PWT\x00PPT\x00MST\x00\nMST7\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\xfe\xe6\xf5J\x05\x04\x00\x00\x05\x04\x00\x00\x0e\x00\x1c\x00America/DawsonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8e\xb4\xff\xff\xff\xff\x9e\xb8˰" + + "\xff\xff\xff\xff\x9f\xbb#\xa0\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2Ҁ\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff" + + "\xf8(\xa2\x10\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90" + + "\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00" + + "\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 " + + "\x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00" + + "-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90" + + "\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00" + + ";\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0" + + "\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00" + + "I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90" + + "\x00\x00\x00\x00Q\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00" + + "\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t" + + "\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xb6n\x00\x00\xff\xffd\xee\x00\x00\xff\xffeP\x00\x04\xff\xffs" + + "`\x01\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xff\x81p\x00\x18\xff\xff\x8f\x80\x01\x1c\xff\xff\x81p\x00!LMT\x00NST\x00NWT\x00NPT\x00BST\x00B" + + "DT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*" + + ";\xb1\x00\x00\x00\xb1\x00\x00\x00\x0f\x00\x1c\x00America/CuracaoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2" + + "`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xa7\x17jҲ\x00\x00\x00\xb2\x00\x00\x00\x12\x00\x1c\x00America/MartiniqueUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xffi\x87\x14\xc4\xff\xff\xff\xff\x91\xa3\xc8D" + + "\x00\x00\x00\x00\x13Mn@\x00\x00\x00\x00\x144\x16\xb0\x01\x02\x03\x02\xff\xffƼ\x00\x00\xff\xffƼ\x00\x04\xff\xff\xc7\xc0\x00\t\xff\xff\xd5\xd0\x01\rLMT\x00FFMT\x00AST\x00ADT" + + "\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUs\xb0\xeau\xb4\x01\x00\x00\xb4\x01\x00\x00\x10\x00\x1c\x00America/EirunepeUT\t\x00\x03\xaf\x16" + + "\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff" + + "\x96\xaa\x88\x80\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xbaސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0" + + "\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff" + + "\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0" + + "\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00\x00\x00\x00,\xc0\xd1P\x00\x00\x00\x00" + + "-f\xe0@\x00\x00\x00\x00H`\x7fP\x00\x00\x00\x00R\x7f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xbe\x80\x00\x00\xff" + + "\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU<\xb9\x18\x87\xe4\x02\x00\x00\xe4" + + "\x02\x00\x00\x0f\x00\x1c\x00America/IqaluitUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\b\x00\x00\x00!\xff\xff\xff\xff\xccl\xa1\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xf7/>P\xff\xff\xff" + + "\xff\xf8(i\xd0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed" + + "\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00" + + "\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s" + + "\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00" + + "\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+" + + "\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00" + + "\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda" + + "`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x05\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02" + + "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x06\a\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xc7\xc0\x01\x11\xff\xff\xc7\xc0\x01" + + "\x15\xff\xff\xab\xa0\x00\x19\xff\xff\xb9\xb0\x01\x1d-00\x00EPT\x00EST\x00EDDT\x00EDT\x00EWT\x00CST\x00CDT\x00\nEST5EDT,M3.2." + + "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa1'\a\xbd\x97\x00\x00\x00\x97\x00\x00\x00\x0f\x00\x1c\x00America/CayenneUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff" + + "\xff\xff\x91\xf4+\x90\xff\xff\xff\xff\xfb\xc35\xc0\x01\x02\xff\xff\xce\xf0\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x00\bLMT\x00-04\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU<\x01V\rP\x02\x00\x00P\x02\x00\x00\x11\x00\x1c\x00America/AraguainaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaat0\xff\xff\xff\xff\xb8\x0fI\xe0" + + "\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff" + + "\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0" + + "\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00" + + "\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0" + + "\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00\x00\x004\xf8\xc1 \x00\x00\x00\x00" + + "6 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x00N\xf0\xa0\x00\x00\x00\x00P\x83e0\x00\x00\x00\x00Q 9\xa0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd2\xd0\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x14\x00\x1c\x00America/Blanc-SablonUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz\xe6" + + "\x95\xb9\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AS" + + "T\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd6\xe1Հ\x9c\x01\x00\x00\x9c\x01\x00\x00\x13\x00\x1c\x00America/Mexico" + + "_CityUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b" + + "\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff" + + "\xc5ް`\xff\xff\xff\xffƗ4P\xff\xff\xff\xff\xc9U\xf1\xe0\xff\xff\xff\xff\xc9\xea\xddP\xff\xff\xff\xff\xcf\x02\xc6\xe0\xff\xff\xff\xffϷVP\xff\xff\xff\xffڙ\x15\xe0\xff\xff\xff\xff\xdbv\x83\xd0" + + "\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x00" + + "8\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xa3\f\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00MST\x00CST\x00CDT\x00CWT" + + "\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?_p\x99\x0e\x05\x00\x00\x0e\x05\x00\x00\x10\x00\x1c\x00Ameri" + + "ca/WinnipegUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffd䰔\xff\xff\xff\xff\x9b\x01\xfb\xe0\xff\xff\xff\xff\x9búP\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\u00a0" + + ";\x80\xff\xff\xff\xff\xc3O\x84\xf0\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xffӈh\x00\xff\xff\xff\xff\xd4S`\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff" + + "\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xdb\x00\a\x00\xff\xff\xff\xff\xdb\xc8\\\xf0\xff\xff\xff\xff\xdc\xde" + + "\x97\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff" + + "\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1" + + "\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf41b\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff" + + "\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98" + + "\r\x00\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0ހ\x00\x00" + + "\x00\x00\b π\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\n\x00\xb1\x80\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9" + + "\xa1\x00\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15I8\x00\x00\x00" + + "\x00\x00\x169)\x00\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd1" + + "\xfa\x80\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xcc\x00\x00\x00" + + "\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xea" + + "T\x00\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001gv\x00\x00\x00" + + "\x00\x002s\x16\x80\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005':\x00\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6" + + "\xfe\x00\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00" + + "\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x01" + + "\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff" + + "\xff\xa4\xec\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3" + + ".2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe3\xc9I\xd0U\x03\x00\x00U\x03\x00\x00\x12\x00\x1c\x00America/Grand_Tur" + + "kUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00L\x00\x00\x00\x05" + + "\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x1e0\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0" + "\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00" + "\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0" + "\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00" + "*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0" + "\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x00" + "8\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0" + - "\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x02\x03\x04" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\xff\xff\xb2%\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14LMT\x00CST\x00" + - "EST\x00EWT\x00EPT\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x12\x00\x1c\x00America/Argentina/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9RR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00\x1b\x00\x1c\x00America/Argentina/CatamarcaUT\t\x00\x03\x15\xac" + - "\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xff" + - "r\x9c\xaf,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0" + - "\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff" + - "\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@" + - "\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff" + - "\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0" + - "\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00" + - "#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0" + - "\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00" + - "G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03" + - "\x05\x02\x05\x04\x05\xff\xff\xc2T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02" + - "\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00 \x00\x1c\x00America/Argentina/Com" + - "odRivadaviaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xaf,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4" + - "p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff" + - "\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A" + - "7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff" + - "\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7" + - "\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00" + - "\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0" + - "X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00" + - "\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xc2T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fL" + - "MT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8b}\xb6\x1e\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x1c\x00Ame" + - "rica/Argentina/UshuaiaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb1\x88\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff" + - "\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n" + - "\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff" + - "\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed" + - "\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff" + - "\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c5" + - "0\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00" + - "\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*" + - "\xb0\x00\x00\x00\x00@\xb9N0\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf\xf8\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff" + - "\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RŒZ\x8c\xc4\x02\x00\x00" + - "\xc4\x02\x00\x00\x19\x00\x1c\x00America/Argentina/MendozaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2\x04\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@" + - "\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff" + - "\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0" + - "\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff" + - "\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@" + - "\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff" + - "\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0" + - "\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00\x00'\xcdð\x00\x00\x00\x00(\xfag\xc0\x00\x00\x00\x00)\xb0H\xb0\x00\x00\x00\x00*\xe0\xe1@\x00\x00\x00\x00+\x99W \x00\x00\x00\x00" + - "7\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xb0\x13\xb0\x00\x00\x00\x00AV>\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x03\x02\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf|\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7" + - "\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x1e\x00\x1c\x00America/Argentina/Buenos_AiresUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xa8L" + - "\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff" + - "\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30" + - "\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff" + - "\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0" + - "\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff" + - "\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0" + - "\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00" + - "*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca " + - "\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x04\x05\x04" + - "\x05\xff\xff\xc94\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-" + - "03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rm\aD\x0e\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x1c\x00America/Argentina/La_Rioj" + - "aUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06" + - "\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb0,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0" + - "\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff" + - "\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0" + - "\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff" + - "\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@" + - "\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00" + - "\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'͵\xa0\x00\x00\x00\x00(&&@" + - "\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00" + - "@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xc1T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLM" + - "T\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8ep\xb4c\xc4\x02\x00\x00\xc4\x02\x00\x00\x1e\x00\x1c\x00Amer" + - "ica/Argentina/Rio_GallegosUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + + "\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x00\x00\x00\x00" + + "G-_\xe0\x00\x00\x00\x00Gӊ\xf0\x00\x00\x00\x00I\rA\xe0\x00\x00\x00\x00I\xb3l\xf0\x00\x00\x00\x00J\xed#\xe0\x00\x00\x00\x00K\x9c\x89p\x00\x00\x00\x00L\xd6@`\x00\x00\x00\x00M|kp" + + "\x00\x00\x00\x00N\xb6\"`\x00\x00\x00\x00O\\Mp\x00\x00\x00\x00P\x96\x04`\x00\x00\x00\x00Q\x8f\xd0p\x00\x00\x00\x00?\x9b" + + "b\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x02" + + "\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\xff\xff\xacT\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14LMT\x00CST\x00" + + "EST\x00EWT\x00EPT\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06" + + "\x00\x00\xb5\x06\x00\x00\x10\x00\x1c\x00America/MontrealUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87." + + "\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff" + + "\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11" + + "`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff" + + "\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7" + + "\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff" + + "\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb" + + "\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff" + + "\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eM" + + "p\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff" + + "\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3" + + "`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\x7f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff" + + "\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda" + + "`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00E" + + "WT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU,\xdb~\xab\xb2\x03\x00\x00\xb2\x03\x00\x00\x0f\x00\x1c" + + "\x00America/YakutatUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\b\x00\x00\x00\x1e\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x877\xbf\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff" + + "\xff\xff\xff\xfe\xb8U0\xff\xff\xff\xff\xff\xa88 \x00\x00\x00\x00\x00\x9870\x00\x00\x00\x00\x01\x88\x1a \x00\x00\x00\x00\x02x\x190\x00\x00\x00\x00\x03q6\xa0\x00\x00\x00\x00\x04a5\xb0\x00\x00\x00\x00\x05" + + "Q\x18\xa0\x00\x00\x00\x00\x06A\x17\xb0\x00\x00\x00\x00\a0\xfa\xa0\x00\x00\x00\x00\a\x8dQ\xb0\x00\x00\x00\x00\t\x10ܠ\x00\x00\x00\x00\t\xad\xcd0\x00\x00\x00\x00\n\xf0\xbe\xa0\x00\x00\x00\x00\v\u0f70\x00" + + "\x00\x00\x00\f\xd9\xdb \x00\x00\x00\x00\r\xc0\x9f\xb0\x00\x00\x00\x00\x0e\xb9\xbd \x00\x00\x00\x00\x0f\xa9\xbc0\x00\x00\x00\x00\x10\x99\x9f \x00\x00\x00\x00\x11\x89\x9e0\x00\x00\x00\x00\x12y\x81 \x00\x00\x00\x00\x13" + + "i\x800\x00\x00\x00\x00\x14Yc \x00\x00\x00\x00\x15Ib0\x00\x00\x00\x00\x169E \x00\x00\x00\x00\x17)D0\x00\x00\x00\x00\x18\"a\xa0\x00\x00\x00\x00\x19\t&0\x00\x00\x00\x00\x1a\x02C\xa0\x00" + + "\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 " + + "v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00" + + "\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00." + + "\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00" + + "\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<" + + "\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00" + + "\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\a\x06" + + "\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\x00\x00\u0381\x00\x00\xff\xff}\x01\x00\x00\xff\xff" + + "\x81p\x00\x04\xff\xff\x8f\x80\x01\b\xff\xff\x8f\x80\x01\f\xff\xff\x8f\x80\x01\x10\xff\xff\x8f\x80\x01\x14\xff\xff\x81p\x00\x19LMT\x00YST\x00YWT\x00YPT\x00YDT\x00AKDT\x00A" + + "KST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\x0f\x00\x1c\x00" + + "America/ChicagoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff" + + "\xff\xff\xa2\xcbt\x00\xff\xff\xff\xff\xa3\x83\xf7\xf0\xff\xff\xff\xff\xa4EҀ\xff\xff\xff\xff\xa5c\xd9\xf0\xff\xff\xff\xff\xa6S\xd9\x00\xff\xff\xff\xff\xa7\x15\x97p\xff\xff\xff\xff\xa83\xbb\x00\xff\xff\xff\xff\xa8\xfe" + + "\xb3\xf0\xff\xff\xff\xff\xaa\x13\x9d\x00\xff\xff\xff\xff\xaaޕ\xf0\xff\xff\xff\xff\xab\xf3\x7f\x00\xff\xff\xff\xff\xac\xbew\xf0\xff\xff\xff\xff\xad\xd3a\x00\xff\xff\xff\xff\xae\x9eY\xf0\xff\xff\xff\xff\xaf\xb3C\x00\xff\xff" + + "\xff\xff\xb0~;\xf0\xff\xff\xff\xff\xb1\x9c_\x80\xff\xff\xff\xff\xb2gXp\xff\xff\xff\xff\xb3|A\x80\xff\xff\xff\xff\xb4G:p\xff\xff\xff\xff\xb5\\#\x80\xff\xff\xff\xff\xb6'\x1cp\xff\xff\xff\xff\xb7<" + + "\x05\x80\xff\xff\xff\xff\xb8\x06\xfep\xff\xff\xff\xff\xb9\x1b\xe7\x80\xff\xff\xff\xff\xb9\xe6\xe0p\xff\xff\xff\xff\xbb\x05\x04\x00\xff\xff\xff\xff\xbb\xc6\xc2p\xff\xff\xff\xff\xbc\xe4\xe6\x00\xff\xff\xff\xff\xbd\xaf\xde\xf0\xff\xff" + + "\xff\xff\xbe\xc4\xc8\x00\xff\xff\xff\xff\xbf\x8f\xc0\xf0\xff\xff\xff\xff\xc0Z\xd6\x00\xff\xff\xff\xff\xc1\xb0\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00" + + "\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad\xd4\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9" + + "\xb0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00CDT\x00CST\x00EST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU?\xc9\x1c\xd4\xc6\x03\x00\x00\xc6\x03\x00\x00\x0e\x00\x1c\x00America/JuneauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\n\x00\x00\x00&\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x872\xc5" + + "\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00" + + "\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ" + + "\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00" + + "\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14Yc \x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 " + + "\x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00" + + "\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0" + + "\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00" + + "+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0" + + "\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x00" + + "9\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0" + + "\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x06\x02\x05\x02\x05\x02\x05\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t" + + "\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xd3{\x00\x00\xff\xff\x81\xfb\x00\x00\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x8f\x80\x01\x14\xff\xff\x81p\x00\x18\xff" + + "\xff\x8f\x80\x01\x1c\xff\xff\x81p\x00!LMT\x00PST\x00PWT\x00PPT\x00PDT\x00YDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT," + + "M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x1c\x00America/Argenti" + + "na/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x1c" + + "\x00America/Argentina/CordobaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2d\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1a\xc9" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1a\xc9" + "\xb0\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff" + "\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04" + "@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff" + "\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6\xe6\x9f" + "\xb0\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff" + "\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v" + - "\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00" + - "\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf\x1c\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0" + - "\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RutZ\x1a" + - "\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x1c\x00America/Argentina/JujuyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xb8\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{" + - "R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff" + - "\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c" + - "\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff" + - "\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62" + - "\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff" + - "\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7" + - "\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xe2۰\x00\x00\x00\x00(\xee\x8a@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00" + - "\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x04\x05\x04\x05\x03\x05\x04\x05\xff\xff\xc2\xc8\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff" + - "\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xfcz=\xe1\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a" + - "\x00\x1c\x00America/Argentina/San_JuanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb1\xbc\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff" + - "\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0" + - "\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff" + - "\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0" + - "\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff" + - "\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@" + - "\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00" + - "%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'͵\xa0\x00\x00\x00\x00(&&@\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W " + - "\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xba\x9f\xb0\x00\x00\x00\x00A\x030@\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf\xc4\x00\x00\xff\xff\xc3\xd0" + - "\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R\x1c\x80\xb9\\\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x1c\x00America/Argentina/San_LuisUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xaf" + - "\xb4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff" + - "\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc3" + - "0\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff" + - "\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6" + - "\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff" + - "\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5" + - "\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xfd\xa5\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00\x00'\xcdð\x00\x00\x00\x00(G\x1b\xc0\x00\x00\x00\x007\xf6ư\x00\x00\x00" + - "\x008\xbf*\xb0\x00\x00\x00\x00@\xba\x9f\xb0\x00\x00\x00\x00A\x030@\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\x93\xfc\xa0\x00\x00\x00\x00G\xd3R\xb0\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xb34" + - "\xb0\x00\x00\x00\x00J\xd1X@\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02" + - "\x05\x03\x05\x02\x05\x04\x03\x02\x03\x02\x05\xff\xff\xc1\xcc\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-" + - "03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rt*\x9b!\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x1c\x00America/Argenti" + - "na/SaltaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00;\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xd4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff" + - "\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0" + - "Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff" + - "\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4" + - "Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff" + - "\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a" + - "\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00" + - "\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G" + - "\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05" + - "\x04\x05\xff\xff¬\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<" + - "-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RY\xd8֭\xd6\x02\x00\x00\xd6\x02\x00\x00\x19\x00\x1c\x00America/Argentina/Tucuma" + - "nUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\x00\x00\x00\x06" + - "\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xa4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0" + - "\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff" + - "\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0" + - "\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff" + - "\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@" + - "\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00" + - "\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@" + - "\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xcb\xd1@\x00\x00\x00\x00" + - "Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\x04\x05\xff\xff\xc2\xdc\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01" + - "\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00" + - "\x00\x19\x00\x1c\x00America/Argentina/CordobaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff" + - "\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4" + - "\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff" + - "\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff" + - "0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff" + - "\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR" + - "@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00" + - "\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6\xc6" + - "\xb0\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b" + - "\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\xe3\xc9I\xd0U\x03\x00\x00U\x03\x00\x00\x12\x00\x1c\x00America/Grand_TurkUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00L\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x1e0\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\x11\x89e" + - "\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00" + - "\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1" + - "`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00" + - "\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9ei" + - "p\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00" + - "\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞" + - "\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00" + - "\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x00\x00\x00\x00G-_\xe0\x00\x00\x00\x00Gӊ\xf0\x00\x00\x00\x00I\rA\xe0\x00\x00\x00\x00I\xb3l" + - "\xf0\x00\x00\x00\x00J\xed#\xe0\x00\x00\x00\x00K\x9c\x89p\x00\x00\x00\x00L\xd6@`\x00\x00\x00\x00M|kp\x00\x00\x00\x00N\xb6\"`\x00\x00\x00\x00O\\Mp\x00\x00\x00\x00P\x96\x04`\x00\x00\x00" + - "\x00Q\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00B" + - "O\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00F\x0ft\x90\x00\x00\x00\x00G$A\x80\x00\x00\x00\x00G\xf8\x91\x10\x00\x00\x00\x00I\x04#\x80\x00" + - "\x00\x00\x00I\xd8s\x10\x00\x00\x00\x00J\xe4\x05\x80\x00\x00\x00\x00K\xb8U\x10\x00\x00\x00\x00L\xcd\x13\xf0\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + - "\x04\x01\x04\x01\x04\x01\x04\x01\x05\x02\xff\xff\x9dT\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00MST\x00CST\x00PS" + - "T\x00MDT\x00CDT\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00" + - "\x0e\x00\x1c\x00America/VirginUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9R\xa1'\a\xbd\x97\x00\x00\x00\x97\x00\x00\x00\x0f\x00\x1c\x00America/CayenneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x91\xf4+\x90\xff\xff\xff\xff\xfb\xc35\xc0" + - "\x01\x02\xff\xff\xce\xf0\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x00\bLMT\x00-04\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00" + - "\x00\x00\x82\x00\x00\x00\x12\x00\x1c\x00America/MontserratUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00" + - "\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?_p\x99\x0e\x05\x00\x00\x0e\x05\x00\x00\x10\x00\x1c\x00America/WinnipegUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffd" + - "䰔\xff\xff\xff\xff\x9b\x01\xfb\xe0\xff\xff\xff\xff\x9búP\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\u00a0;\x80\xff\xff\xff\xff\xc3O\x84\xf0\xff\xff\xff\xffˈ\xfe\x80\xff" + - "\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xffӈh\x00\xff\xff\xff\xff\xd4S`\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8" + - "\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xdb\x00\a\x00\xff\xff\xff\xff\xdb\xc8\\\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff" + - "\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6" + - "G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff" + - "\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf41b\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb" + - "\xe8X\x00\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xef\x00\x00" + - "\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b π\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\n" + - "\x00\xb1\x80\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99\x83\x00\x00" + - "\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18" + - "\"E\x80\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1܀\x00" + - "\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&" + - "\x15\xb5\x00\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3p\x80\x00" + - "\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003GX\x00\x00\x00\x00\x004" + - "R\xf8\x80\x00\x00\x00\x005':\x00\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xe0\x00\x00" + - "\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00B" + - "O\xa2\x80\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xa4\xec\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff" + - "\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x1c\x00America/AnguillaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff" + - "\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RŒZ\x8c\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x1c\x00America/Mend" + - "ozaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00" + - "\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2\x04\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17" + - "}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff" + - "\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0" + - "\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff" + - "\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4" + - "w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00" + - "\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00\x00'\xcdð\x00\x00\x00\x00(\xfa" + - "g\xc0\x00\x00\x00\x00)\xb0H\xb0\x00\x00\x00\x00*\xe0\xe1@\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xb0\x13\xb0\x00\x00\x00\x00AV>\xc0\x00\x00" + - "\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04" + - "\x05\x04\x02\x03\x02\x03\x02\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf|\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-" + - "04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x04,2h\x99\x01\x00\x00\x99\x01\x00\x00\x10\x00\x1c\x00America/San" + - "taremUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e" + - "\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaazH\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff" + - "\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0" + - "\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff" + - "\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ" + - "\x00\x00\x00\x00H`q@\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\xff\xff̸\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0" + - "\x00\x04LMT\x00-03\x00-04\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x1c\x00America/" + - "St_LuciaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "Ծ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x1c\x00America/CaymanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffi\x87&\x10\xff\xff\xff\xff\x8b\xf4a\xe8\x01\x02\xff\xff\xb5p\x00\x00\xff\xff\xb5" + - "\x18\x00\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rp\x1b\xceRC\x03\x00\x00C\x03\x00\x00\x0f\x00\x1c\x00Ame" + - "rica/NipigonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00J\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xee\x81@\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xc8\xf8IP\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2" + - "#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\x00\x00\x00\x00\b \xc1p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00" + - "\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14" + - "Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00" + - "\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"" + - "U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00" + - "\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000" + - "\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00" + - "\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>" + - "\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00" + - "\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad@\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00E" + - "ST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00" + - "\x00\x0e\x00\x1c\x00America/DenverUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a" + - "\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff" + - "\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8" + - ":\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00" + - "\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v\xe0" + - "\xa1\x90\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00" + - "\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02" + - "'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00" + - "\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfe" + - "ߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00" + - "\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062" + - "ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00" + - "\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/" + - "\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00" + - "\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2.0" + - ",M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R+\x10`ȫ\x02\x00\x00\xab\x02\x00\x00\x14\x00\x1c\x00America/Dawson_CreekU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x06\x00\x00" + - "\x00\x18\xff\xff\xff\xff^=t8\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff" + - "\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\xde" + - "\xb3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff" + - "\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2" + - "\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff" + - "\xff\xff\xf2\u007f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0f" + - "f\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00" + - "\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05\x01\xf0\x90\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\xff\xff\x8fH\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80" + - "\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x00\x14LMT\x00PDT\x00PST\x00PWT\x00PPT\x00MST\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\f\x00\x1c\x00America/NuukUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9b\x80h\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00" + - "\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b" + - "\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00" + - "\x00\x00\x00#3<-02>,M3.5.0/-2,M10.5.0/-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf2" + - "\x04\xde\xdd\x11\x02\x00\x00\x11\x02\x00\x00\x0e\x00\x1c\x00America/CancunUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\x16\x86\xd5`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x00" + - "2r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x005\xc4\x00`\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0" + - "\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00" + - "?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00F\x0ff\x80" + - "\x00\x00\x00\x00G$3p\x00\x00\x00\x00G\xf8\x83\x00\x00\x00\x00\x00I\x04\x15p\x00\x00\x00\x00I\xd8e\x00\x00\x00\x00\x00J\xe3\xf7p\x00\x00\x00\x00K\xb8G\x00\x00\x00\x00\x00L\xcd\x13\xf0\x00\x00\x00\x00" + - "M\x98)\x00\x00\x00\x00\x00N\xac\xf5\xf0\x00\x00\x00\x00Ox\v\x00\x00\x00\x00\x00P\x8c\xd7\xf0\x00\x00\x00\x00Qa'\x80\x00\x00\x00\x00Rl\xb9\xf0\x00\x00\x00\x00SA\t\x80\x00\x00\x00\x00TL\x9b\xf0" + - "\x00\x00\x00\x00T\xcd\xdd\x00\x01\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\xff\xff\xae\xa8\x00\x00\xff\xff\xab\xa0" + - "\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10LMT\x00CST\x00EDT\x00EST\x00CDT\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "n\xab\xd5\xf9\xcf\x03\x00\x00\xcf\x03\x00\x00\f\x00\x1c\x00America/NomeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00&\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87O\xd2\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2" + - "#\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0\xff\xff\xff\xff\xfe\xb8qP\xff\xff\xff\xff\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00" + - "\x00\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00\x05Q4\xc0\x00\x00\x00\x00\x06A3\xd0\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t" + - "\xad\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0\x00\x00\x00\x00\f\xd9\xf7@\x00\x00\x00\x00\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00" + - "\x00\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00\x13i\x9cP\x00\x00\x00\x00\x14Y\u007f@\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18" + - "\"}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00" + - "\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%" + - "J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00" + - "\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003" + - "G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00" + - "\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A" + - "\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + - "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t" + - "\b\t\b\t\b\t\b\x00\x00\xb6n\x00\x00\xff\xffd\xee\x00\x00\xff\xffeP\x00\x04\xff\xffs`\x01\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xff\x81p\x00\x18\xff\xff\x8f\x80\x01" + - "\x1c\xff\xff\x81p\x00!LMT\x00NST\x00NWT\x00NPT\x00BST\x00BDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2" + - ".0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R,\xdb~\xab\xb2\x03\x00\x00\xb2\x03\x00\x00\x0f\x00\x1c\x00America/YakutatUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\b\x00\x00\x00\x1e\xff" + - "\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x877\xbf\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xfe\xb8U0\xff\xff\xff\xff\xff\xa88 \x00\x00\x00\x00\x00" + - "\x9870\x00\x00\x00\x00\x01\x88\x1a \x00\x00\x00\x00\x02x\x190\x00\x00\x00\x00\x03q6\xa0\x00\x00\x00\x00\x04a5\xb0\x00\x00\x00\x00\x05Q\x18\xa0\x00\x00\x00\x00\x06A\x17\xb0\x00\x00\x00\x00\a0\xfa\xa0\x00" + - "\x00\x00\x00\a\x8dQ\xb0\x00\x00\x00\x00\t\x10ܠ\x00\x00\x00\x00\t\xad\xcd0\x00\x00\x00\x00\n\xf0\xbe\xa0\x00\x00\x00\x00\v\u0f70\x00\x00\x00\x00\f\xd9\xdb \x00\x00\x00\x00\r\xc0\x9f\xb0\x00\x00\x00\x00\x0e" + - "\xb9\xbd \x00\x00\x00\x00\x0f\xa9\xbc0\x00\x00\x00\x00\x10\x99\x9f \x00\x00\x00\x00\x11\x89\x9e0\x00\x00\x00\x00\x12y\x81 \x00\x00\x00\x00\x13i\x800\x00\x00\x00\x00\x14Yc \x00\x00\x00\x00\x15Ib0\x00" + - "\x00\x00\x00\x169E \x00\x00\x00\x00\x17)D0\x00\x00\x00\x00\x18\"a\xa0\x00\x00\x00\x00\x19\t&0\x00\x00\x00\x00\x1a\x02C\xa0\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b" + - "\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00" + - "\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)" + - "\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00" + - "\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008" + - "\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00" + - "\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E" + - "\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06" + - "\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\x00\x00\u0381\x00\x00\xff\xff}\x01\x00\x00\xff\xff\x81p\x00\x04\xff\xff\x8f\x80\x01\b\xff\xff\x8f\x80\x01\f\xff\xff\x8f\x80" + - "\x01\x10\xff\xff\x8f\x80\x01\x14\xff\xff\x81p\x00\x19LMT\x00YST\x00YWT\x00YPT\x00YDT\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2." + - "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x1c\x00America/GrenadaUT\t\x00\x03" + - "\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff" + - "\xff\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f" + - "\x00\x1c\x00America/RosarioUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f" + - "@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff" + - "\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*" + - "0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff" + - "\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C" + - "\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff" + - "\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f" + - "0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00" + - "\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10" + - "\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xac\x8e\xee\x13\xbe\x00\x00\x00\xbe\x00\x00\x00" + - "\x0f\x00\x1c\x00America/CaracasUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffi\x87\x1a@\xff\xff\xff\xff\x93\x1e,<\xff\xff\xff\xff\xf6\x98\xecH\x00\x00\x00\x00G[\x92p\x00\x00\x00\x00W%" + - "\xa9p\x01\x02\x03\x02\x03\xff\xff\xc1@\x00\x00\xff\xff\xc1D\x00\x04\xff\xff\xc0\xb8\x00\b\xff\xff\xc7\xc0\x00\x0eLMT\x00CMT\x00-0430\x00-04\x00\n<-04>4\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9Ro_\x00v/\x01\x00\x00/\x01\x00\x00\x0e\x00\x1c\x00America/MeridaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\x16\x86\xd5`" + - "\x00\x00\x00\x00\x18LKP\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x00" + - "7\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x03\x01\x03\x01\x03" + - "\x01\x03\x01\x03\x01\x03\x01\x03\xff\xff\xab\xfc\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xb9\xb0\x01\fLMT\x00CST\x00EST\x00CDT\x00\nCST6CDT,M4." + - "1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x14\x00\x1c\x00America/Buenos_Air" + - "esUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00" + + "\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0" + + "\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUY\xd8֭" + + "\xd6\x02\x00\x00\xd6\x02\x00\x00\x19\x00\x1c\x00America/Argentina/TucumanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xa4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff" + + "\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70" + + "\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff" + + "\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0" + + "\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff" + + "\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0" + + "\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00" + + "%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W " + + "\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xcb\xd1@\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00" + + "I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03" + + "\x05\x02\x05\x04\x05\x04\x05\xff\xff\xc2\xdc\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-" + + "02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x1c\x00America/Argentina/J" + + "ujuyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00" + + "\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xb8\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba" + + "\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff" + + "\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7" + + "\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff" + + "\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9" + + "\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00" + + "\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xe2۰\x00\x00\x00\x00(" + + "\xee\x8a@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x04\x05\x04\x05\x03\x05\x04\x05\xff\xff" + + "\xc2\xc8\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>" + + "3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x1e\x00\x1c\x00America/Argentina/Buenos_Air" + + "esUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00" + "\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xa8L\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}" + "\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff" + @@ -1514,2290 +1343,2254 @@ const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00 "\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w" + "@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00" + "\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf1" + - "0\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00" + + "0\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00" + "\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05" + "\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc94\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-0" + - "4\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1b\vKdC\x03\x00\x00C\x03\x00\x00\x13\x00\x1c\x00America/Rain" + - "y_RiverUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00J\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xee\x87(\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff" + - "\xff\xff\xd2a\t\xf0\x00\x00\x00\x00\b π\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\n\x00\xb1\x80\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0" + - "u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00" + - "\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1" + - "\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00" + - "\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\xde" + - "\xb3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00" + - "\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b" + - "\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00" + - "\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3" + - "\xb7\x00\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xa7X\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CW" + - "T\x00CPT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\x0f\x00\x1c\x00" + - "America/PhoenixUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff" + - "\xff\xffˉ\f\x90\xff\xff\xff\xff\xcf\x17\xdf\x1c\xff\xff\xff\xffϏ\xe5\xac\xff\xff\xff\xffЁ\x1a\x1c\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\x02\x01\x02\x01\x02\x03\x02\x03\x02\x01\x02\xff\xff\x96" + - "\xee\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\fLMT\x00MDT\x00MST\x00MWT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xdf\xe5\x8d" + - "\xc4\xda\x04\x00\x00\xda\x04\x00\x00\x12\x00\x1c\x00America/LouisvilleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff" + - "\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa4s\xf7\x00\xff\xff\xff\xff\xa5\x16\x11p\xff\xff\xff\xff\xca\rN\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#" + - "\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xd7\x1c\xff\xff\xff\xffӤ\tp\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff" + - "\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)" + - "\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe9\x17\x00\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xf6\xe2\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff" + - "\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x1e\x90p\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff\xff\xff\xff\xfe\xb8" + - "\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00" + - "\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f\xd9" + - "\xa2\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00" + - "\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2" + - "\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00" + - "\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\n" + - "U\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00" + - "\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a" + - "\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00" + - "\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00ED" + - "C`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + - "\x01\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + - "\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf\x9a\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST" + - "\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk^2S\xb9" + - "\x04\x00\x00\xb9\x04\x00\x00\x14\x00\x1c\x00America/Punta_ArenasUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00t\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x1d\xfc\xff\xff\xff\xff\x8f0GF\xff\xff\xff\xff\x9b\\\xe5P\xff\xff" + - "\xff\xff\x9f|\xe2\xc6\xff\xff\xff\xff\xa1\x00q\xc0\xff\xff\xff\xff\xb0^w\xc6\xff\xff\xff\xff\xb1w=@\xff\xff\xff\xff\xb2A\x00\xd0\xff\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb59" + - "\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff\xff" + - "\xff\xff\xd53U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@" + - "I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00" + - "\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86" + - "\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00" + - "\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep" + - "\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00" + - "\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7" + - "\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00" + - "\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa" + - "\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00" + - "\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1" + - "v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00" + - "\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00XC" + - "\x86\xb0\x01\x02\x01\x03\x01\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x06\xff\xff" + - "\xbd\x84\x00\x00\xff\xff\xbd\xba\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01\f\xff\xff\xd5\xd0\x01\x10\xff\xff\xd5\xd0\x00\x10LMT\x00SMT\x00-05\x00-04\x00-03\x00" + - "\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00\x1c\x00America/Porto_AcreUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x04\x00\x00\x00\f\xff" + - "\xff\xff\xff\x96\xaa\x86\x90\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xbaސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc" + - "\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff" + - "\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc" + - "\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00\x00\x00\x00H`\u007fP\x00" + - "\x00\x00\x00R\u007f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xc0p\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0" + - "\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06\x00\x00\x10\x00\x1c\x00America/" + - "New_YorkUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xaf\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x03\xf0\x90\xff\xff\xff\xff\x9e\xa6\x1ep\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x86\x00p\xff\xff\xff\xff\xa1\x9a\xcd`\xff\xff\xff\xff\xa2e\xe2p\xff" + - "\xff\xff\xff\xa3\x83\xe9\xe0\xff\xff\xff\xff\xa4j\xaep\xff\xff\xff\xff\xa55\xa7`\xff\xff\xff\xff\xa6S\xca\xf0\xff\xff\xff\xff\xa7\x15\x89`\xff\xff\xff\xff\xa83\xac\xf0\xff\xff\xff\xff\xa8\xfe\xa5\xe0\xff\xff\xff\xff\xaa" + - "\x13\x8e\xf0\xff\xff\xff\xff\xaaއ\xe0\xff\xff\xff\xff\xab\xf3p\xf0\xff\xff\xff\xff\xac\xbei\xe0\xff\xff\xff\xff\xad\xd3R\xf0\xff\xff\xff\xff\xae\x9eK\xe0\xff\xff\xff\xff\xaf\xb34\xf0\xff\xff\xff\xff\xb0~-\xe0\xff" + - "\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8" + - "\x06\xf0`\xff\xff\xff\xff\xb9\x1b\xd9p\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbbƴ`\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff" + - "\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6" + - "M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xff\xca\r@p\xff\xff\xff\xff\xca\xd89`\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff" + - "\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xc6\xf0\xff\xff\xff\xff\xd6 \xbf\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0\xff\xff\xff\xff\xd9" + - "\x15\x8a\xf0\xff\xff\xff\xff\xd9\xe0\x83\xe0\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdb\xc0e\xe0\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff" + - "\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5W.\xe0\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7" + - "7\x10\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff" + - "\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\u007f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5" + - "Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00" + - "\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xba\x9e\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00" + - "EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xad`\x12\xe9\xaa\x00" + - "\x00\x00\xaa\x00\x00\x00\x0e\x00\x1c\x00America/La_PazUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + + "4\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1c\x80\xb9\\\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x1c\x00America/Arge" + + "ntina/San_LuisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xaf\xb4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff" + + "\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18" + + "@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff" + + "\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5" + + "\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff" + + "\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4" + + "@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xfd\xa5\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00" + + "\x00'\xcdð\x00\x00\x00\x00(G\x1b\xc0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xba\x9f\xb0\x00\x00\x00\x00A\x030@\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\x93\xfc" + + "\xa0\x00\x00\x00\x00G\xd3R\xb0\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xb34\xb0\x00\x00\x00\x00J\xd1X@\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x05\x03\x05\x02\x05\x04\x03\x02\x03\x02\x05\xff\xff\xc1\xcc\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff" + + "\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8ep\xb4c\xc4\x02\x00" + + "\x00\xc4\x02\x00\x00\x1e\x00\x1c\x00America/Argentina/Rio_GallegosUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2d\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff" + + "\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96" + + "\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff" + + "\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee" + + "\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff" + + "\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbc" + + "S0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00" + + "\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99" + + "W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf\x1c\x00\x00\xff\xff\xc3" + + "\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vUR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00\x1b\x00\x1c\x00America/Argentina/CatamarcaUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr" + + "\x9c\xaf,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff" + + "\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2" + + ";\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff" + + "\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4" + + "\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff" + + "\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#" + + "\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00" + + "\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G" + + "\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05" + + "\x02\x05\x04\x05\xff\xff\xc2T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00" + + "\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8b}\xb6\x1e\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x1c\x00America/Argentina/Ushu" + + "aiaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00" + + "\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb1\x88\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17" + + "}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff" + + "\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0" + + "\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff" + + "\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4" + + "w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00" + + "\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00" + + "\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xb9N0\x00\x00\x00\x00@\xd5\v\xc0\x00\x00" + + "\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf\xf8\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-" + + "04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUt*\x9b!\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x1c\x00America/Arg" + + "entina/SaltaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xd4\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8" + + "\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff" + + "\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7" + + "A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff" + + "\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8" + + "\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00" + + "\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'" + + "\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00" + + "\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05" + + "\x04\x05\x03\x05\x04\x05\xff\xff¬\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-0" + + "2\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00 \x00\x1c\x00America/Argentina/Co" + + "modRivadaviaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xaf,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8" + + "\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff" + + "\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7" + + "A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff" + + "\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8" + + "\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00" + + "\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'" + + "\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00" + + "\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xc2T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\f" + + "LMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUm\aD\x0e\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x1c\x00Am" + + "erica/Argentina/La_RiojaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87\x1bd\xff\xff\xff\xff\xb8\x1e\x96\xe4\xff\xff\xff\xff\xb8\xee\xd5\xd4\x01\x02\x03\xff\xff\xc0\x1c\x00\x00" + - "\xff\xff\xc0\x1c\x00\x04\xff\xff\xce,\x01\b\xff\xff\xc7\xc0\x00\fLMT\x00CMT\x00BST\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb7-2f" + - "\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x1c\x00America/NoronhaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaaed\xff\xff\xff\xff\xb8\x0f;\xd0\xff\xff\xff\xff\xb8\xfd2\x90\xff\xff\xff\xff\xb9\xf1" + - "& \xff\xff\xff\xff\xba\xdef\x10\xff\xff\xff\xff\xda8\xa0 \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdc\x19Ӡ\xff\xff\xff\xffܹK\x10\xff\xff\xff\xff\xdd\xfb\a \xff\xff\xff\xffޛ\xd0\x10\xff\xff" + - "\xff\xff\xdf\u074c \xff\xff\xff\xff\xe0T%\x10\xff\xff\xff\xff\xf4\x97\xf1\xa0\xff\xff\xff\xff\xf5\x05P\x10\xff\xff\xff\xff\xf6\xc0V \xff\xff\xff\xff\xf7\x0e\x10\x90\xff\xff\xff\xff\xf8Q\x1e \xff\xff\xff\xff\xf8\xc7" + - "\xb7\x10\xff\xff\xff\xff\xfa\nĠ\xff\xff\xff\xff\xfa\xa8\xea\x90\xff\xff\xff\xff\xfb\xeb\xf8 \xff\xff\xff\xff\xfc\x8bo\x90\x00\x00\x00\x00\x1dɀ \x00\x00\x00\x00\x1exɐ\x00\x00\x00\x00\x1f\xa0'\xa0\x00\x00" + - "\x00\x00 3\xc1\x90\x00\x00\x00\x00!\x81[ \x00\x00\x00\x00\"\v\xba\x90\x00\x00\x00\x00#X\x02\xa0\x00\x00\x00\x00#\xe2b\x10\x00\x00\x00\x00%7\xe4\xa0\x00\x00\x00\x00%Թ\x10\x00\x00\x00\x007\xf6" + - "\xb8\xa0\x00\x00\x00\x008\xb8w\x10\x00\x00\x00\x009\xdf\xd5 \x00\x00\x00\x009\xe9\x01\x90\x00\x00\x00\x00;\xc8\xf1\xa0\x00\x00\x00\x002\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x1c\x00America/GuadeloupeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x9373\xac\x01\xff" + - "\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1e\xfbn۸\x03\x00\x00\xb8\x03\x00\x00\x14\x00\x1c\x00Ameri" + - "ca/Campo_GrandeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaaz4\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff" + - "\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0T" + - "A0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff" + - "\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81" + - "w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00#X\x1e\xc0\x00\x00\x00\x00#\xe2~0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xd4\xd50\x00\x00\x00\x00'!\x1d@\x00\x00\x00\x00'\xbd\xf1\xb0\x00\x00" + - "\x00\x00)\x00\xff@\x00\x00\x00\x00)\x94\x990\x00\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+k@\xb0\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x00\x00\x00\x00.\xa0\xa5@\x00\x00\x00\x00/F" + - "\xb40\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001\x1d[\xb0\x00\x00\x00\x002W.\xc0\x00\x00\x00\x003\x06x0\x00\x00\x00\x0048b@\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006 -@\x00\x00" + - "\x00\x006\xcfv\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x00:\x8f:\xb0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00N\xfe\xb0\x00\x00\x00\x00?\x92\f@\x00\x00\x00\x00@.\xe0\xb0\x00\x00\x00\x00A\x87\x06@\x00\x00\x00\x00B\x17\xfd0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00C\xf7\xdf0\x00\x00" + - "\x00\x00EMa\xc0\x00\x00\x00\x00E\xe0\xfb\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xb7\xa30\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00\x00\x00I\x97\x850\x00\x00\x00\x00Jڒ\xc0\x00\x00\x00\x00K\x80" + - "\xa1\xb0\x00\x00\x00\x00L\xbat\xc0\x00\x00\x00\x00M`\x83\xb0\x00\x00\x00\x00N\x9aV\xc0\x00\x00\x00\x00OI\xa00\x00\x00\x00\x00P\x83s@\x00\x00\x00\x00Q G\xb0\x00\x00\x00\x00RcU@\x00\x00" + - "\x00\x00S\x00)\xb0\x00\x00\x00\x00TC7@\x00\x00\x00\x00T\xe9F0\x00\x00\x00\x00V#\x19@\x00\x00\x00\x00V\xc9(0\x00\x00\x00\x00X\x02\xfb@\x00\x00\x00\x00X\xa9\n0\x00\x00\x00\x00Y\xe2" + - "\xdd@\x00\x00\x00\x00Z\x88\xec0\x00\x00\x00\x00[\xden\xc0\x00\x00\x00\x00\\h\xce0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xcc" + - "\xcc\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R:\x9a1T\xdf\x01\x00\x00\xdf\x01\x00" + - "\x00\x14\x00\x1c\x00America/ScoresbysundUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\x9b\x80L\x18\x00\x00\x00\x00\x13Mn@\x00\x00\x00\x00\x144$\xc0\x00\x00\x00\x00\x15#\xf9\xa0" + - "\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00" + - "\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#1<+00>,M3.5.0/0,M10.5.0/1\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x1c\x00America/North_Dakota/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RH\xeam\xef\xde\x03\x00\x00\xde\x03\x00\x00\x1b\x00\x1c\x00America/North_Dakot" + - "a/CenterUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00Y\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff" + - "\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff" + - "\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00" + - "\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r" + - "\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00" + - "\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b" + - "\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00" + - "\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)" + - "\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00" + - "\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008" + - "\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00" + - "\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E" + - "\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\xff\xff\xa1\b\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0" + - "\x01\x10\xff\xff\xb9\xb0\x01\x14\xff\xff\xab\xa0\x00\x18LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CDT\x00CST\x00\nCST6CDT,M3.2.0,M1" + - "1.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RR\x1b\x8b(\xde\x03\x00\x00\xde\x03\x00\x00\x1e\x00\x1c\x00America/North_Dakota/New" + - "_SalemUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "Y\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff" + - "\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c" + - "\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00" + - "\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83" + - "\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00" + - "\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t" + - "\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00" + - "\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1" + - "\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00" + - "\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7" + - "\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00" + - "\x00?\x9b\u007f\x00\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7" + - "\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\xff\xff\xa0\xed\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10" + - "\xff\xff\xb9\xb0\x01\x14\xff\xff\xab\xa0\x00\x18LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CDT\x00CST\x00\nCST6CDT,M3.2.0,M11." + - "1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb7.\xb6*\x13\x04\x00\x00\x13\x04\x00\x00\x1b\x00\x1c\x00America/North_Dakota/Beula" + - "hUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06" + - "\x00\x00\x00\x18\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p" + - "\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00" + - "\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ" + - "\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00" + - "\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10" + - "\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00" + - "\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00" + - "\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00" + - "*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10" + - "\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x00" + - "8\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00" + - "\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x00\x00\x00\x00" + - "G-|\x00\x00\x00\x00\x00Gӧ\x10\x00\x00\x00\x00I\r^\x00\x00\x00\x00\x00I\xb3\x89\x10\x00\x00\x00\x00J\xed@\x00\x00\x00\x00\x00K\x9c\xa5\x90\x00\x00\x00\x00L\xd6\\\x80\x02\x01\x02\x01\x02\x03\x04\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\xff\xff\xa0\x95\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff" + - "\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x1d\xf7\a ,\x06\x00\x00,\x06\x00\x00\x11\x00\x1c\x00America/Goose_BayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\n\x00\x00\x00!\xff\xff\xff\xff^=<$\xff\xff\xff\xff\x9e\xb8~\x8c\xff\xff" + - "\xff\xff\x9f\xba\xd6|\xff\xff\xff\xff\xbe\x9eMl\xff\xff\xff\xff\xc0\xb818\xff\xff\xff\xff\xc1y\xef\xa8\xff\xff\xff\xff\u0098\x138\xff\xff\xff\xff\xc3YѨ\xff\xff\xff\xff\xc4w\xf58\xff\xff\xff\xff\xc59" + - "\xb3\xa8\xff\xff\xff\xff\xc6a\x11\xb8\xff\xff\xff\xff\xc7\x19\x95\xa8\xff\xff\xff\xff\xc8@\xf3\xb8\xff\xff\xff\xff\xc9\x02\xb2(\xff\xff\xff\xff\xca ո\xff\xff\xff\xff\xca\xe2\x94(\xff\xff\xff\xff\xcc\x00\xb7\xb8\xff\xff" + - "\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xe6\xc8\xff\xff\xff\xffӈD\xd8\xff\xff\xff\xff\xd4J\x03H\xff\xff\xff\xff\xd5h&\xd8\xff\xff\xff\xff\xd6)\xe5H\xff\xff\xff\xff\xd7H\b\xd8\xff\xff\xff\xff\xd8\t" + - "\xc7H\xff\xff\xff\xff\xd9'\xea\xd8\xff\xff\xff\xff\xd9\xe9\xa9H\xff\xff\xff\xff\xdb\x11\aX\xff\xff\xff\xff\xdb\xd2\xc5\xc8\xff\xff\xff\xff\xdc\xdetX\xff\xff\xff\xffݩmH\xff\xff\xff\xff\u07beVX\xff\xff" + - "\xff\xff߉OH\xff\xff\xff\xff\xe0\x9e8X\xff\xff\xff\xff\xe1i1H\xff\xff\xff\xff\xe2~\x1aX\xff\xff\xff\xff\xe3I\x13H\xff\xff\xff\xff\xe4]\xfcX\xff\xff\xff\xff\xe5(\xf5H\xff\xff\xff\xff\xe6G" + - "\x18\xd8\xff\xff\xff\xff\xe7\x12\x11\xc8\xff\xff\xff\xff\xe8&\xfa\xd8\xff\xff\xff\xff\xe8\xf1\xf3\xc8\xff\xff\xff\xff\xea\x06\xdc\xd8\xff\xff\xff\xff\xea\xd1\xd5\xc8\xff\xff\xff\xff\xeb\xe6\xbe\xd8\xff\xff\xff\xff챷\xc8\xff\xff" + - "\xff\xff\xedƠ\xd8\xff\xff\xff\xff\ueffeH\xff\xff\xff\xffﯽX\xff\xff\xff\xff\xf0\x9f\xa0H\xff\xff\xff\xff\xf1\x8f\x9fX\xff\xff\xff\xff\xf2\u007f\x82H\xff\xff\xff\xff\xf3o\x81X\xff\xff\xff\xff\xf4_" + - "dH\xff\xff\xff\xff\xf5OcX\xff\xff\xff\xff\xf6?FH\xff\xff\xff\xff\xf7/EX\xff\xff\xff\xff\xf8(b\xc8\xff\xff\xff\xff\xf8\xdakX\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff" + - "\xff\xff\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87" + - "\xd3\xd0\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00" + - "\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9" + - "u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00" + - "\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1" + - "\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xd6\xfc\x00\x00\x00\x00!\x81il\x00\x00\x00\x00\"U\xb8\xfc\x00\x00\x00\x00#jw\xdc\x00\x00\x00\x00$5\x9a\xfc\x00\x00" + - "\x00\x00%Jg\xec\x00\x00\x00\x00&\x15|\xfc\x00\x00\x00\x00'*I\xec\x00\x00\x00\x00'\xfe\x99|\x00\x00\x00\x00)\n+\xec\x00\x00\x00\x00)\xde{|\x00\x00\x00\x00*\xea\r\xec\x00\x00\x00\x00+\xbe" + - "]|\x00\x00\x00\x00,\xd3*l\x00\x00\x00\x00-\x9e?|\x00\x00\x00\x00.\xb3\fl\x00\x00\x00\x00/~!|\x00\x00\x00\x000\x92\xeel\x00\x00\x00\x001g=\xfc\x00\x00\x00\x002r\xd0l\x00\x00" + - "\x00\x003G\x1f\xfc\x00\x00\x00\x004R\xb2l\x00\x00\x00\x005'\x01\xfc\x00\x00\x00\x0062\x94l\x00\x00\x00\x007\x06\xe3\xfc\x00\x00\x00\x008\x1b\xb0\xec\x00\x00\x00\x008\xe6\xc5\xfc\x00\x00\x00\x009\xfb" + - "\x92\xec\x00\x00\x00\x00:Ƨ\xfc\x00\x00\x00\x00;\xdbt\xec\x00\x00\x00\x00<\xaf\xc4|\x00\x00\x00\x00=\xbbV\xec\x00\x00\x00\x00>\x8f\xa6|\x00\x00\x00\x00?\x9b8\xec\x00\x00\x00\x00@o\x88|\x00\x00" + - "\x00\x00A\x84Ul\x00\x00\x00\x00BOj|\x00\x00\x00\x00Cd7l\x00\x00\x00\x00D/L|\x00\x00\x00\x00ED\x19l\x00\x00\x00\x00E\xf3~\xfc\x00\x00\x00\x00G-5\xec\x00\x00\x00\x00G\xd3" + - "`\xfc\x00\x00\x00\x00I\r\x17\xec\x00\x00\x00\x00I\xb3B\xfc\x00\x00\x00\x00J\xec\xf9\xec\x00\x00\x00\x00K\x9c_|\x00\x00\x00\x00L\xd6\x16l\x00\x00\x00\x00M|A|\x00\x00\x00\x00N\xb6\x14P\x01\x02" + - "\x01\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x06\x05\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\b\a\b" + - "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\t\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b" + - "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\xff\xff\xc7\\\x00\x00\xff\xffΔ\x00\x04\xff\xffܤ\x01\b\xff\xff\xce\xc8\x00\x04\xff\xff\xdc\xd8\x01\b" + - "\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10\xff\xff\xd5\xd0\x01\x14\xff\xff\xc7\xc0\x00\x18\xff\xff\xe3\xe0\x01\x1cLMT\x00NST\x00NDT\x00NPT\x00NWT\x00ADT\x00AST\x00AD" + - "DT\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x12\x00\x1c\x00Ame" + - "rica/Fort_WayneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff" + - "\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U" + - "\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff" + - "\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I" + - "6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00" + - "\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06" + - "\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CP" + - "T\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R):\x17-\x88\x06\x00\x00\x88\x06\x00\x00" + - "\x0f\x00\x1c\x00America/HalifaxUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa7\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80\xf1\xab\xa0\xff\xff\xff\xff\x9a\xe4\xde\xc0\xff\xff\xff\xff\x9b\xd6\x130\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba" + - "\xddP\xff\xff\xff\xff\xa2\x9d\x17@\xff\xff\xff\xff\xa30\xb10\xff\xff\xff\xff\xa4zV@\xff\xff\xff\xff\xa5\x1b\x1f0\xff\xff\xff\xff\xa6S\xa0\xc0\xff\xff\xff\xff\xa6\xfcR\xb0\xff\xff\xff\xff\xa8<\xbd@\xff\xff" + - "\xff\xff\xa8\xdc4\xb0\xff\xff\xff\xff\xaa\x1c\x9f@\xff\xff\xff\xff\xaa\xcd:0\xff\xff\xff\xff\xab\xfc\x81@\xff\xff\xff\xff\xac\xbf\x910\xff\xff\xff\xff\xad\xee\xd8@\xff\xff\xff\xff\xae\x8c\xfe0\xff\xff\xff\xff\xaf\xbc" + - "E@\xff\xff\xff\xff\xb0\u007fU0\xff\xff\xff\xff\xb1\xae\x9c@\xff\xff\xff\xff\xb2Kp\xb0\xff\xff\xff\xff\xb3\x8e~@\xff\xff\xff\xff\xb4$\xbb0\xff\xff\xff\xff\xb5n`@\xff\xff\xff\xff\xb6\x15\xc0\xb0\xff\xff" + - "\xff\xff\xb7NB@\xff\xff\xff\xff\xb8\b\x17\xb0\xff\xff\xff\xff\xb9$\xe9\xc0\xff\xff\xff\xff\xb9\xe7\xf9\xb0\xff\xff\xff\xff\xbb\x04\xcb\xc0\xff\xff\xff\xff\xbb\xd1\x160\xff\xff\xff\xff\xbd\x00]@\xff\xff\xff\xff\xbd\x9d" + - "1\xb0\xff\xff\xff\xff\xbe\xf2\xb4@\xff\xff\xff\xff\xbf\x90\xda0\xff\xff\xff\xff\xc0\xd3\xe7\xc0\xff\xff\xff\xff\xc1^G0\xff\xff\xff\xff\u008d\x8e@\xff\xff\xff\xff\xc3P\x9e0\xff\xff\xff\xff\xc4mp@\xff\xff" + - "\xff\xff\xc50\x800\xff\xff\xff\xff\xc6r<@\xff\xff\xff\xff\xc7\x10b0\xff\xff\xff\xff\xc86n\xc0\xff\xff\xff\xff\xc8\xf9~\xb0\xff\xff\xff\xff\xca\x16P\xc0\xff\xff\xff\xff\xca\xd9`\xb0\xff\xff\xff\xffˈ" + - "\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff\xff\xff\xff\xd4@\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0\xff\xff" + - "\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xdc\xde{`\xff\xff\xff\xffݩtP\xff\xff\xff\xff\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0\x9e" + - "?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff\xff\xff\xff\xe6G\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe8\xf1\xfa\xd0\xff\xff" + - "\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xd1\xdc\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff챾\xd0\xff\xff\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\u007f\x89P\xff\xff\xff\xff\xf3o\x88`\xff\xff\xff\xff\xf4_" + - "kP\xff\xff\xff\xff\xf5Oj`\xff\xff\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff\xff" + - "\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02w" + - "\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00" + - "\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99" + - "X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00" + - "\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1" + - "\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00" + - "\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3" + - "FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00" + - "\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6" + - "\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00" + - "\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xc4`\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00A" + - "DT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1c\xd8\x19\x9dp\x01\x00" + - "\x00p\x01\x00\x00\x15\x00\x1c\x00America/Swift_CurrentUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x86\xfd\x96\x18\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff" + - "\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd3v\x01\x10\xff\xff\xff\xff\xd4So\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\xff\xff\xff\xff\xd75\xc5" + - "\x10\xff\xff\xff\xff\xd8\x00\xbe\x00\xff\xff\xff\xff\xd9\x15\xa7\x10\xff\xff\xff\xff\xd9\xe0\xa0\x00\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe9\x17\x0f\x00\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xd6\xd3\x00\xff\xff\xff" + - "\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xcb\x00\xff\xff\xff\xff\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xad\x00\x00\x00\x00\x00\x04a\x19\x90\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05" + - "\xff\xff\x9a\xe8\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CST\x00" + - "\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R[Sp\x90\x02\x05\x00\x00\x02\x05\x00\x00\x10\x00\x1c\x00America/SantiagoUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffi" + - "\x87\x1d\xc6\xff\xff\xff\xff\x8f0GF\xff\xff\xff\xff\x9b\\\xe5P\xff\xff\xff\xff\x9f|\xe2\xc6\xff\xff\xff\xff\xa1\x00q\xc0\xff\xff\xff\xff\xb0^w\xc6\xff\xff\xff\xff\xb1w=@\xff\xff\xff\xff\xb2A\x00\xd0\xff" + - "\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb59\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9" + - "\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff\xff\xff\xff\xd3\u070f\xc0\xff\xff\xff\xff\xd4\x1bɰ\xff\xff\xff\xff\xd53U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff\xff\xff\xfd\xd1<@\xff" + - "\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05" + - ">O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00" + - "\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13" + - "(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00" + - "\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f\x030\x00\x00\x00\x00!" + - "o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00" + - "\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/" + - "bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00" + - "\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=" + - "\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00" + - "\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K" + - "\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00" + - "\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[" + - "o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x01\x02\x01\x03\x01\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x05\x03\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + - "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + - "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\xff\xff\xbd\xba\x00\x00\xff\xff\xbd\xba\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00SMT\x00-05" + - "\x00-04\x00-03\x00\n<-04>4<-03>,M9.1.6/24,M4.1.6/24\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R8\xcdZ\x05" + - "o\x01\x00\x00o\x01\x00\x00\x10\x00\x1c\x00America/MazatlanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb0,\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff" + + "\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbe" + + "x\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff" + + "\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xff\xce" + + "\xb0\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff" + + "\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe" + + "\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00" + + "\x00\x00\x00'!\x0f0\x00\x00\x00\x00'͵\xa0\x00\x00\x00\x00(&&@\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007" + + "\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xbb\xf10\x00\x00\x00\x00@\xd5\v\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x05\x04\x05\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xc1T\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7" + + "\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU\xfcz=\xe1\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x1c\x00America/Argentina/San_JuanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb1\xbc\xff\xff\xff\xff" + + "\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@" + + "\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff" + + "\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0" + + "\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff" + + "\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0" + + "\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00" + + "$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'͵\xa0\x00\x00\x00\x00(&&@\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\xb0:\xa0" + + "\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xba\x9f\xb0\x00\x00\x00\x00A\x030@\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00" + + "G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x05\x04\x05\x04\x05" + + "\x03\x05\x02\x05\x04\x05\xff\xff\xbf\xc4\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-0" + + "2\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUŒZ\x8c\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x1c\x00America/Argentina/Me" + + "ndozaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=" + + "\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2\x04\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff" + + "\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0" + + "\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff" + + "\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0" + + "\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff" + + "\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0" + + "\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00\x00'\xcdð\x00\x00\x00\x00" + + "(\xfag\xc0\x00\x00\x00\x00)\xb0H\xb0\x00\x00\x00\x00*\xe0\xe1@\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xb0\x13\xb0\x00\x00\x00\x00AV>\xc0" + + "\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04" + + "\x05\x04\x05\x04\x02\x03\x02\x03\x02\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf|\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT" + + "\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1b\x81-\xa9\x8a\x01\x00\x00\x8a\x01\x00\x00\x13\x00\x1c\x00America/P" + + "orto_VelhoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x82\xe8\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc" + + "@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff" + + "\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06" + + "\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00" + + "\x00\"\vְ\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xc4\x18\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00" + + "-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x1c\x00America/North_Dako" + + "ta/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb7.\xb6*\x13\x04\x00\x00\x13\x04\x00\x00\x1b\x00\x1c" + + "\x00America/North_Dakota/BeulahUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7" + - "C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff\xcb\xeaq`\xff\xff\xff\xffؑ\xb4\xf0\x00\x00\x00\x00\x00\x00p\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00" + - "\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009" + - "\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff\xff\x9c<\x00\x00\xff\xff\x9d\x90\x00" + - "\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00PST\x00MDT\x00\nMST7MDT,M4.1.0,M10.5" + - ".0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x15\x00\x1c\x00America/St_BarthelemyUT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff" + - "\xff\x9373\xac\x01\xff\xff\xc6T\x00\x00\xff\xff\xc7\xc0\x00\x04LMT\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x89غ\xee\x15\x04\x00\x00\x15\x04\x00\x00\x0e\x00" + - "\x1c\x00America/BelizeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0" + + "\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff" + + "\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04" + + "a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00" + + "\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12" + + "ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00" + + "\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 " + + "v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00" + + "\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00." + + "\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00" + + "\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<" + + "\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00" + + "\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x00\x00\x00\x00G-|\x00\x00\x00\x00\x00Gӧ\x10\x00\x00\x00\x00I\r^\x00\x00\x00\x00\x00I\xb3\x89\x10\x00\x00\x00\x00J" + + "\xed@\x00\x00\x00\x00\x00K\x9c\xa5\x90\x00\x00\x00\x00L\xd6\\\x80\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\xff\xff\xa0\x95\x00" + + "\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CST\x00\nCST6" + + "CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUR\x1b\x8b(\xde\x03\x00\x00\xde\x03\x00\x00\x1e\x00\x1c\x00America/Nor" + + "th_Dakota/New_SalemUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a" + + "\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff" + + "\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P" + + "\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00" + + "\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13i" + + "d\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00" + + "\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81" + + "\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00" + + "\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~" + + "g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00" + + "\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb" + + "\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00" + + "\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\xff\xff\xa0\xed\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00" + + "\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xb9\xb0\x01\x14\xff\xff\xab\xa0\x00\x18LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CDT\x00CST\x00\nCST6CD" + + "T,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUH\xeam\xef\xde\x03\x00\x00\xde\x03\x00\x00\x1b\x00\x1c\x00America/North" + + "_Dakota/CenterUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff" + + "\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89" + + "\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00" + + "\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ" + + "\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00" + + "\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&" + + "\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00" + + "\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr" + + "\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00" + + "\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c" + + "\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00" + + "\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQ" + + "p\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\xff\xff\xa1\b\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0" + + "\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xb9\xb0\x01\x14\xff\xff\xab\xa0\x00\x18LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CDT\x00CST\x00\nCST6CDT,M3." + + "2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUB\xa0=:\x1e\x01\x00\x00\x1e\x01\x00\x00\x12\x00\x1c\x00America/Hermosillo" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x05\x00" + + "\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff\xcb\xeaq`\xff" + + "\xff\xff\xffؑ\xb4\xf0\x00\x00\x00\x00\x00\x00p\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x006" + + "2ڀ\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\xff\xff\x97\xf8\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00" + + "PST\x00MDT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU.\xbe\x1a>\xe7\x03\x00\x00\xe7\x03\x00\x00\r\x00\x1c\x00America/BoiseUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\a\x00\x00\x00\x1c" + + "\xff\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xff\xa8FL \xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff" + + "\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00" + + "\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00" + + "\a0ހ\x00\x00\x00\x00\a\xb2\x1f\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90" + + "\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00" + + "\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80" + + "\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00" + + "#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90" + + "\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x00" + + "1g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00" + + "\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00" + + "?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10" + + "\x02\x01\x02\x01\x02\x05\x03\x04\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\x93\x0f\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10" + + "\xff\xff\x9d\x90\x00\x14\xff\xff\xab\xa0\x01\x18LMT\x00PDT\x00PST\x00MWT\x00MPT\x00MST\x00MDT\x00\nMST7MDT,M3.2.0,M11." + + "1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x11\x00\x1c\x00America/St_ThomasUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz\xe6" + + "\x95\xb9\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AS" + + "T\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUo_\x00v/\x01\x00\x00/\x01\x00\x00\x0e\x00\x1c\x00America/Merida" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x04\x00" + + "\x00\x00\x10\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\x16\x86\xd5`\x00\x00\x00\x00\x18LKP\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00" + + "\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;" + + "\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\xff\xff\xab\xfc\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xb9\xb0\x01\fLMT\x00CST\x00E" + + "ST\x00CDT\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04\x00\x00\x12\x00\x1c" + + "\x00America/WhitehorseUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8a\x9c\xff\xff\xff\xff\x9e\xb8˰\xff\xff\xff\xff\x9f\xbb#\xa0\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2" + + "Ҁ\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xfb\x1d_\x10\x00\x00\x00\x00\x13ir \x00\x00" + + "\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf2" + + "4\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00" + + "\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n" + + "\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00" + + "\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a" + + "8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00" + + "\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00ED" + + "m\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00" + + "\x00\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90\x00\x00\x00\x00Q5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x11Z\xde\xe4\x01\x00\x00\xe4\x01\x00\x00\x11\x00\x1c\x00America/FortalezaUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f" + + "\xff\xff\xff\xff\x96\xaak\x18\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff" + + "\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ " + + "\xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff" + + "\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0" + + "\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x009\xf2J \x00\x00\x00\x00" + + ";\xc8\xff\xb0\x00\x00\x00\x003\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf8Dz\x97\xae\x01\x00\x00\xae\x01\x00\x00\x11\x00\x1c\x00A" + + "merica/Boa_VistaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xff\x93^ٰ\xff\xff\xff\xff\x9f\x9f;\xe0\xff\xff\xff\xff\xa0EQ\xd8\xff\xff\xff\xff\xa1\u007f\x1d\xe0\xff\xff\xff\xff\xa2.nX\xff" + - "\xff\xff\xff\xa3^\xff\xe0\xff\xff\xff\xff\xa4\x0ePX\xff\xff\xff\xff\xa5>\xe1\xe0\xff\xff\xff\xff\xa5\xee2X\xff\xff\xff\xff\xa7'\xfe`\xff\xff\xff\xff\xa7\xce\x14X\xff\xff\xff\xff\xa9\a\xe0`\xff\xff\xff\xff\xa9" + - "\xad\xf6X\xff\xff\xff\xff\xaa\xe7\xc2`\xff\xff\xff\xff\xab\x97\x12\xd8\xff\xff\xff\xff\xacǤ`\xff\xff\xff\xff\xadv\xf4\xd8\xff\xff\xff\xff\xae\xa7\x86`\xff\xff\xff\xff\xafV\xd6\xd8\xff\xff\xff\xff\xb0\x87h`\xff" + - "\xff\xff\xff\xb16\xb8\xd8\xff\xff\xff\xff\xb2p\x84\xe0\xff\xff\xff\xff\xb3\x16\x9a\xd8\xff\xff\xff\xff\xb4Pf\xe0\xff\xff\xff\xff\xb4\xf6|\xd8\xff\xff\xff\xff\xb60H\xe0\xff\xff\xff\xff\xb6ߙX\xff\xff\xff\xff\xb8" + - "\x10*\xe0\xff\xff\xff\xff\xb8\xbf{X\xff\xff\xff\xff\xb9\xf0\f\xe0\xff\xff\xff\xff\xba\x9f]X\xff\xff\xff\xff\xbb\xd9)`\xff\xff\xff\xff\xbc\u007f?X\xff\xff\xff\xff\xbd\xb9\v`\xff\xff\xff\xff\xbe_!X\xff" + - "\xff\xff\xff\xbf\x98\xed`\xff\xff\xff\xff\xc0?\x03X\xff\xff\xff\xff\xc1x\xcf`\xff\xff\xff\xff\xc2(\x1f\xd8\xff\xff\xff\xff\xc3X\xb1`\xff\xff\xff\xff\xc4\b\x01\xd8\xff\xff\xff\xff\xc58\x93`\xff\xff\xff\xff\xc5" + - "\xe7\xe3\xd8\xff\xff\xff\xff\xc7!\xaf\xe0\xff\xff\xff\xff\xc7\xc7\xc5\xd8\xff\xff\xff\xff\xc9\x01\x91\xe0\xff\xff\xff\xffɧ\xa7\xd8\xff\xff\xff\xff\xca\xe1s\xe0\xff\xff\xff\xffː\xc4X\xff\xff\xff\xff\xcc@\"\xe0\xff" + - "\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2\xc6qP\xff\xff\xff\xff\xd6)\xfa`\xff\xff\xff\xff\xd6\xd9J\xd8\xff\xff\xff\xff\xd8\t\xdc`\xff\xff\xff\xffع,\xd8\xff\xff\xff\xff\xd9\xe9\xbe`\xff\xff\xff\xff\xda" + - "\x99\x0e\xd8\xff\xff\xff\xff\xdb\xd2\xda\xe0\xff\xff\xff\xff\xdcx\xf0\xd8\xff\xff\xff\xffݲ\xbc\xe0\xff\xff\xff\xff\xdeX\xd2\xd8\xff\xff\xff\xffߒ\x9e\xe0\xff\xff\xff\xff\xe0A\xefX\xff\xff\xff\xff\xe1r\x80\xe0\xff" + - "\xff\xff\xff\xe2!\xd1X\xff\xff\xff\xff\xe3Rb\xe0\xff\xff\xff\xff\xe4\x01\xb3X\xff\xff\xff\xff\xe52D\xe0\xff\xff\xff\xff\xe5\xe1\x95X\xff\xff\xff\xff\xe7\x1ba`\xff\xff\xff\xff\xe7\xc1wX\xff\xff\xff\xff\xe8" + - "\xfbC`\xff\xff\xff\xff\xe9\xa1YX\xff\xff\xff\xff\xea\xdb%`\xff\xff\xff\xff\xeb\x8au\xd8\xff\xff\xff\xff\xec\xbb\a`\xff\xff\xff\xff\xedjW\xd8\xff\xff\xff\xff\xee\x9a\xe9`\xff\xff\xff\xff\xefJ9\xd8\xff" + - "\xff\xff\xff\xf0\x84\x05\xe0\xff\xff\xff\xff\xf1*\x1b\xd8\xff\xff\xff\xff\xf2c\xe7\xe0\xff\xff\xff\xff\xf3\t\xfd\xd8\xff\xff\xff\xff\xf4C\xc9\xe0\xff\xff\xff\xff\xf4\xe9\xdf\xd8\xff\xff\xff\xff\xf6#\xab\xe0\xff\xff\xff\xff\xf6" + - "\xd2\xfcX\xff\xff\xff\xff\xf8\x03\x8d\xe0\xff\xff\xff\xff\xf8\xb2\xdeX\xff\xff\xff\xff\xf9\xe3o\xe0\xff\xff\xff\xff\xfa\x92\xc0X\xff\xff\xff\xff\xfb̌`\xff\xff\xff\xff\xfcr\xa2X\x00\x00\x00\x00\ab\xdb`\x00" + - "\x00\x00\x00\a\xb9\xd0P\x00\x00\x00\x00\x18aq`\x00\x00\x00\x00\x18\xab7P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05" + - "\x02\xff\xff\xadP\x00\x00\xff\xff\xb2\xa8\x01\x04\xff\xff\xab\xa0\x00\n\xff\xff\xb9\xb0\x01\x0e\xff\xff\xb9\xb0\x01\x12\xff\xff\xb9\xb0\x01\x16LMT\x00-0530\x00CST\x00CWT\x00CPT\x00C" + - "DT\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcd\xc3v\xe3\xb3\x00\x00\x00\xb3\x00\x00\x00\x11\x00\x1c\x00America/GuayaquilUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff" + - "\xff\xff\xffi\x87&X\xff\xff\xff\xff\xb6\xa4B\x18\x00\x00\x00\x00+\x16\xfc\xd0\x00\x00\x00\x00+q\xe6@\x01\x03\x02\x03\xff\xff\xb5(\x00\x00\xff\xff\xb6h\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fL" + - "MT\x00QMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc0\x98\x00\b\xc9\x03\x00\x00\xc9\x03\x00\x00\x12\x00\x1c\x00America" + - "/MontevideoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00V\x00\x00\x00\t\x00\x00\x00&\xff\xff\xff\xff\x8c4\xe53\xff\xff\xff\xff\xa2\x92\x87\xb3\xff\xff\xff\xff\xa8\xff\xdb@\xff\xff\xff\xff\xa9\xf1\x0f\xb0\xff\xff\xff\xff\xaa\xe2Y8\xff\xff\xff\xff\xab\xd2" + - "C0\xff\xff\xff\xff\xacÌ\xb8\xff\xff\xff\xff\xad\xb3v\xb0\xff\xff\xff\xff\xbb\xf4\xb5\xb8\xff\xff\xff\xff\xbc\xbf\xb5\xb0\xff\xff\xff\xff\xbdԗ\xb8\xff\xff\xff\xff\xbe\x9f\x97\xb0\xff\xff\xff\xff\xbf\xb4y\xb8\xff\xff" + - "\xff\xff\xc0\u007fy\xb0\xff\xff\xff\xff\xc1\x94[\xb8\xff\xff\xff\xff\xc2_[\xb0\xff\xff\xff\xff\xc3}x8\xff\xff\xff\xff\xc4?=\xb0\xff\xff\xff\xff\xc5]Z8\xff\xff\xff\xff\xc6\x1f\x1f\xb0\xff\xff\xff\xff\xc7\x18" + - "R8\xff\xff\xff\xff\xc8\b<0\xff\xff\xff\xff\xc9\x1d\x1e8\xff\xff\xff\xff\xc9\xe8\x1e0\xff\xff\xff\xffʋ\x9f8\xff\xff\xff\xff\xcd\x1e\xc60\xff\xff\xff\xff͕f(\xff\xff\xff\xff\xec\v\x85\xb0\xff\xff" + - "\xff\xff\xec\xf25(\xff\xff\xff\xff\xedEJ\xb0\xff\xff\xff\xff\xed\x85\xd6 \xff\xff\xff\xff\xf7\x13r\xb0\xff\xff\xff\xff\xf7\xfa\x1b \xff\xff\xff\xff\xfc\xfe>0\xff\xff\xff\xff\xfd\xf6\x11(\x00\x00\x00\x00\x00\x96" + - "u0\x00\x00\x00\x00\x00\xd8R \x00\x00\x00\x00\x04W\x8a\xb0\x00\x00\x00\x00\x04\xc6:\xa0\x00\x00\x00\x00\a\x96\x1b\xb0\x00\x00\x00\x00\a\xdfژ\x00\x00\x00\x00\bƟ(\x00\x00\x00\x00\tZN0\x00\x00" + - "\x00\x00\t\xdbs \x00\x00\x00\x00\r\x1a\x120\x00\x00\x00\x00\r\u007f\x87\xa0\x00\x00\x00\x00\x0e\xe7\u007f0\x00\x00\x00\x00\x0f_i\xa0\x00\x00\x00\x00\x10\xd9\xd60\x00\x00\x00\x00\x11?K\xa0\x00\x00\x00\x00\x11\x89" + - "-\xb0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00!\xc3T0\x00\x00\x00\x00\"'x \x00\x00\x00\x00#\xa1\xe4\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%Jg\xb0\x00\x00\x00\x00%\xe7< \x00\x00" + - "\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\n+\xb0\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x90\x1c\xa0\x00\x00\x00\x00AL\xf60\x00\x00\x00\x00BF" + - "/\xc0\x00\x00\x00\x00CH\xa3\xd0\x00\x00\x00\x00D\x13\x9c\xc0\x00\x00\x00\x00E\x1fKP\x00\x00\x00\x00E\xf3~\xc0\x00\x00\x00\x00G\bg\xd0\x00\x00\x00\x00G\xd3`\xc0\x00\x00\x00\x00H\xe8I\xd0\x00\x00" + - "\x00\x00I\xb3B\xc0\x00\x00\x00\x00J\xc8+\xd0\x00\x00\x00\x00K\x9c_@\x00\x00\x00\x00L\xa8\r\xd0\x00\x00\x00\x00M|A@\x00\x00\x00\x00N\x87\xef\xd0\x00\x00\x00\x00O\\#@\x00\x00\x00\x00Pq" + - "\fP\x00\x00\x00\x00Q<\x05@\x00\x00\x00\x00RP\xeeP\x00\x00\x00\x00S\x1b\xe7@\x00\x00\x00\x00T0\xd0P\x00\x00\x00\x00T\xfb\xc9@\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x06\x05\x06\x05\a\x05\a\x05\x06\x05\a\x05\a\x05\b\x06\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05" + - "\a\x05\a\x05\a\x05\a\x05\xff\xff\xcbM\x00\x00\xff\xff\xcbM\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xce\xc8\x00\f\xff\xff\xd5\xd0\x01\x12\xff\xff\xd5\xd0\x00\x12\xff\xff\xdc\xd8\x01\x16\xff\xff\xe3\xe0\x01\x1c\xff\xff\xea\xe8" + - "\x01 LMT\x00MMT\x00-04\x00-0330\x00-03\x00-0230\x00-02\x00-0130\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\x13\x00\x1c\x00America/Los_AngelesUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff" + - "\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd6\xfet\\\xff\xff\xff\xff\u0600\xad\x90" + - "\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdcޥ\x90\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff" + - "\xe1ip\x90\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10" + - "\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff" + - "\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fА\xff\xff\xff\xff\xf2\u007f\xc1\x90\xff\xff\xff\xff\xf3o\xb2\x90\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\x94\x90\xff\xff\xff\xff\xf6?\x85\x90" + - "\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff" + - "\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0" + - "\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00" + - "\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10" + - "\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00" + - "\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ " + - "\x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00" + - "'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90" + - "\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x00" + - "62\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0" + - "\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00" + - "D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x91&\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00P" + - "DT\x00PST\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\a\x1c\x9e\x9a]\x04\x00" + - "\x00]\x04\x00\x00\x0e\x00\x1c\x00America/HavanaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00j\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87(\xb8\xff\xff\xff\xff\xacb\u0080\xff\xff\xff\xff\xb1ӔP\xff\xff\xff\xff\xb2t]@\xff\xff" + - "\xff\xff\xc8[f\xd0\xff\xff\xff\xff\xc8\xd3Q@\xff\xff\xff\xff\xca;H\xd0\xff\xff\xff\xffʼm\xc0\xff\xff\xff\xff\xcc$eP\xff\xff\xff\xff̜O\xc0\xff\xff\xff\xff\xd1\xc4\vP\xff\xff\xff\xff\xd2;" + - "\xf5\xc0\xff\xff\xff\xffӣ\xedP\xff\xff\xff\xff\xd4\x1b\xd7\xc0\xff\xff\xff\xff\xf7`\x05\xd0\xff\xff\xff\xff\xf7\xff}@\xff\xff\xff\xff\xf9=D\xd0\xff\xff\xff\xff\xf9\xe3S\xc0\xff\xff\xff\xff\xfa\xdb;\xd0\xff\xff" + - "\xff\xff\xfb\xa7\x86@\xff\xff\xff\xff\xfcũ\xd0\xff\xff\xff\xff\xfd\x87h@\xff\xff\xff\xff\xfe\xb8\x00\xd0\xff\xff\xff\xff\xff\xa7\xe3\xc0\x00\x00\x00\x00\x00\x97\xe2\xd0\x00\x00\x00\x00\x01\x87\xc5\xc0\x00\x00\x00\x00\x02w" + - "\xc4\xd0\x00\x00\x00\x00\x03p\xe2@\x00\x00\x00\x00\x04`\xe1P\x00\x00\x00\x00\x055\x14\xc0\x00\x00\x00\x00\x06@\xc3P\x00\x00\x00\x00\a\x16H@\x00\x00\x00\x00\b \xa5P\x00\x00\x00\x00\b\xf7{\xc0\x00\x00" + - "\x00\x00\n\x00\x87P\x00\x00\x00\x00\n\xf0j@\x00\x00\x00\x00\v\xe0iP\x00\x00\x00\x00\fن\xc0\x00\x00\x00\x00\r\xc0KP\x00\x00\x00\x00\x0e\xb9h\xc0\x00\x00\x00\x00\x0f\xb2\xa2P\x00\x00\x00\x00\x10}" + - "\x9b@\x00\x00\x00\x00\x11Q\xea\xd0\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x131\xcc\xd0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15[\x82\xd0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x17;d\xd0\x00\x00" + - "\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x19\x1bF\xd0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xfb(\xd0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\xdb\n\xd0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ez" + - "SP\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 Z5P\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"CQ\xd0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$#3\xd0\x00\x00\x00\x00%.\xc6@\x00\x00" + - "\x00\x00&\x15\x8a\xd0\x00\x00\x00\x00'\x17\xe2\xc0\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00(\xf7\xd2\xd0\x00\x00\x00\x00)މP\x00\x00\x00\x00*״\xd0\x00\x00\x00\x00+\xbekP\x00\x00\x00\x00,\xb7" + - "\x96\xd0\x00\x00\x00\x00-\x9eMP\x00\x00\x00\x00.\x97x\xd0\x00\x00\x00\x00/~/P\x00\x00\x00\x000wZ\xd0\x00\x00\x00\x001gK\xd0\x00\x00\x00\x002W<\xd0\x00\x00\x00\x003G-\xd0\x00\x00" + - "\x00\x004@YP\x00\x00\x00\x005\x1d\xd5P\x00\x00\x00\x0062\xb0P\x00\x00\x00\x006\xfd\xb7P\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6" + - "\xb5\xd0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xd2P\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xb4P\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@f[\xd0\x00\x00\x00\x00ED5P\x00\x00" + - "\x00\x00E\xf3\x8c\xd0\x00\x00\x00\x00G$\x17P\x00\x00\x00\x00GܩP\x00\x00\x00\x00I\x03\xf9P\x00\x00\x00\x00I\xb3P\xd0\x00\x00\x00\x00J\xe3\xdbP\x00\x00\x00\x00K\x9cmP\x00\x00\x00\x00L\xcc" + - "\xf7\xd0\x00\x00\x00\x00M\x85\x89\xd0\x00\x00\x00\x00N\xbfN\xd0\x00\x00\x00\x00Ow\xe0\xd0\x00\x00\x00\x00P\x95\xf6P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb2\xc8\x00\x00\xff\xff\xb2\xc0\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00HMT\x00CDT\x00CST\x00" + - "\nCST5CDT,M3.2.0/0,M11.1.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R8O:\xbf\x95\x03\x00\x00\x95\x03\x00\x00\x11\x00\x1c\x00Am" + - "erica/MenomineeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xffawIc\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff" + - "\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff\xff\xff\xfa\bg\xf0\xff\xff\xff\xff\xfe\xb8" + - "+\x00\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00" + - "\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13i" + - "V\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00" + - "\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81" + - "\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00" + - "\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~" + - "Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00" + - "\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb" + - "\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00" + - "\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x05\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad\xdd\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f" + - "\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\r\x00\x1c\x00America/JujuyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xae\xb8\xff\xff\xff\xff\xa2\x92" + - "\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff" + - "\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~" + - "\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff" + - "\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05" + - "l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff" + - "\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10" + - "\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xe2۰\x00\x00\x00\x00(\xee\x8a@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00" + - "\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x04\x05\x04\x05\x03\x05\x04\x05\xff\xff\xc2\xc8\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01" + - "\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01" + - "\x04\x00\x00\x01\x04\x00\x00\x0f\x00\x1c\x00America/TijuanaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fd" + - "p\xff\xff\xff\xff\xb7\x1b\x10\x00\xff\xff\xff\xff\xb8\n\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1bY\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff" + - "\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23" + - "\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\x00\x00\x00\x00\v\u0be0\x00\x00\x00" + - "\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir" + - " \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00" + - "\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd" + - "\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00" + - "\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u" + - "\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00" + - "\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab" + - "\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00" + - "\x00EDm\x90\x00\x00\x00\x00F\x0f\x82\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3" + - "\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff" + - "\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14LMT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x10\x00\x1c\x00America/EdmontonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x88\xde\xce\xe0\xff\xff\xff\xff" + - "\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x98\x91\x90\xff\xff\xff\xff\xa0҅\x80\xff\xff\xff\xff\xa2\x8a\xe8\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4jʐ\xff\xff\xff\xff\xa55À" + - "\xff\xff\xff\xff\xa6S\xe7\x10\xff\xff\xff\xff\xa7\x15\xa5\x80\xff\xff\xff\xff\xa83\xc9\x10\xff\xff\xff\xff\xa8\xfe\xc2\x00\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff" + - "\xd5U\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b ݐ\x00\x00\x00\x00\t\x10\xc0\x80" + - "\x00\x00\x00\x00\n\x00\xbf\x90\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00" + - "\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10" + - "\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00" + - "\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00" + - "\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00" + - ",\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10" + - "\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00" + - ":\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80" + - "\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x95\xa0\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\n" + - "MST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x82s\x1dT\x01\x00\x00T\x01\x00\x00\x11\x00\x1c\x00America" + - "/ChihuahuaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x7f\xe0\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff" + + "\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0" + + "TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff" + + "\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!" + + "\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x009\xe9\x1d\xb0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xc7 \x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x1c\x00America/PanamaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffi\x87&\x10\xff\xff\xff\xff\x8b\xf4a\xe8\x01\x02\xff" + + "\xff\xb5p\x00\x00\xff\xff\xb5\x18\x00\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00" + + "\x00\x0e\x00\x1c\x00America/NassauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a" + + "\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff" + + "\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbc" + + "op\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff" + + "\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf" + + "\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff" + + "\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u" + + "\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff" + + "\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1i" + + "F`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff" + + "\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf" + + "\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\x7f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff" + + "\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/" + + "vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00" + + "\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU8\xcdZ\x05o\x01\x00\x00o\x01\x00\x00\x10\x00\x1c\x00Americ" + + "a/MazatlanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x13\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86" + - "\xf0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00" + - "\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04" + - "\x01\x04\x01\x04\xff\xff\x9c\x8c\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00CDT\x00MDT\x00\nMST7M" + - "DT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1d`̟\x00\x03\x00\x00\x00\x03\x00\x00\x15\x00\x1c\x00America/Camb" + - "ridge_BayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00>\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff\xa1\xf2̀\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(\x85\xf0" + - "\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00" + - "\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10" + - "\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00" + - "'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80" + - "\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x00" + - "62ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\x04\xe9P\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00" + - "\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00" + - "Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x03\x01\x02\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\a\x06\b\a\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x00\x00\x00\x00\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff\xb9\xb0\x01\x10" + - "\xff\xff\xab\xa0\x01\x15\xff\xff\xb9\xb0\x01\x19\xff\xff\xab\xa0\x00\x1d\xff\xff\xb9\xb0\x00!-00\x00MWT\x00MPT\x00MST\x00MDDT\x00MDT\x00CDT\x00CST\x00EST" + - "\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04\x00\x00\x12\x00\x1c\x00Ameri" + - "ca/WhitehorseUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8a\x9c\xff\xff\xff\xff\x9e\xb8˰\xff\xff\xff\xff\x9f\xbb#\xa0\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2Ҁ\xff\xff\xff\xff" + - "ˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xfb\x1d_\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10" + - "\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00" + - "\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r " + - "\x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00" + - ")\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90" + - "\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x00" + - "8\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0" + - "\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00" + - "E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90" + - "\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90\x00\x00\x00\x00Q\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00" + - "\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\r" + - "l\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00" + - "\x00\x00P\x96.\x90\x00\x00\x00\x00QN\xf0\xa0\x00\x00\x00\x00?\x91\xfe0\x00\x00\x00\x00@.Ҡ\x00\x00\x00\x00A\x86\xf80\x00\x00\x00\x00B\x17\xef \x00" + + "\x00\x00\x00CQ\xc20\x00\x00\x00\x00C\xf7\xd1 \x00\x00\x00\x00EMS\xb0\x00\x00\x00\x00E\xe0\xed\xa0\x00\x00\x00\x00G\x11\x860\x00\x00\x00\x00G\xb7\x95 \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I" + + "\x97w \x00\x00\x00\x00Jڄ\xb0\x00\x00\x00\x00K\x80\x93\xa0\x00\x00\x00\x00L\xbaf\xb0\x00\x00\x00\x00M`u\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x00\x00\x00\x00P\x83e0\x00" + + "\x00\x00\x00Q 9\xa0\x00\x00\x00\x00RcG0\x00\x00\x00\x00S\x00\x1b\xa0\x00\x00\x00\x00TC)0\x00\x00\x00\x00T\xe98 \x00\x00\x00\x00V#\v0\x00\x00\x00\x00V\xc9\x1a \x00\x00\x00\x00X" + + "\x02\xed0\x00\x00\x00\x00X\xa8\xfc \x00\x00\x00\x00Y\xe2\xcf0\x00\x00\x00\x00Z\x88\xde \x00\x00\x00\x00[\xde`\xb0\x00\x00\x00\x00\\h\xc0 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x05\xff\xff\x8c\xf9\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x00\x14LMT\x00PDT\x00PST\x00PWT" + - "\x00PPT\x00MST\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\f\x00\x1c\x00America/AtkaUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00!" + - "\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87Z^\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0\xff\xff\xff\xff\xfe\xb8qP\xff\xff\xff\xff" + - "\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00\x00\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00\x05Q4\xc0\x00\x00\x00\x00\x06A3\xd0" + - "\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t\xad\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0\x00\x00\x00\x00\f\xd9\xf7@\x00\x00\x00\x00" + - "\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00\x00\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00\x13i\x9cP\x00\x00\x00\x00\x14Y\u007f@" + - "\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18\"}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0\x00\x00\x00\x00\x1a+\" \x00\x00\x00\x00" + - "\x1a\xf2P\xc0\x00\x00\x00\x00\x1b\xe23\xb0\x00\x00\x00\x00\x1c\xd22\xc0\x00\x00\x00\x00\x1d\xc2\x15\xb0\x00\x00\x00\x00\x1e\xb2\x14\xc0\x00\x00\x00\x00\x1f\xa1\xf7\xb0\x00\x00\x00\x00 vG@\x00\x00\x00\x00!\x81ٰ" + - "\x00\x00\x00\x00\"V)@\x00\x00\x00\x00#j\xf60\x00\x00\x00\x00$6\v@\x00\x00\x00\x00%J\xd80\x00\x00\x00\x00&\x15\xed@\x00\x00\x00\x00'*\xba0\x00\x00\x00\x00'\xff\t\xc0\x00\x00\x00\x00" + - ")\n\x9c0\x00\x00\x00\x00)\xde\xeb\xc0\x00\x00\x00\x00*\xea~0\x00\x00\x00\x00+\xbe\xcd\xc0\x00\x00\x00\x00,Ӛ\xb0\x00\x00\x00\x00-\x9e\xaf\xc0\x00\x00\x00\x00.\xb3|\xb0\x00\x00\x00\x00/~\x91\xc0" + - "\x00\x00\x00\x000\x93^\xb0\x00\x00\x00\x001g\xae@\x00\x00\x00\x002s@\xb0\x00\x00\x00\x003G\x90@\x00\x00\x00\x004S\"\xb0\x00\x00\x00\x005'r@\x00\x00\x00\x0063\x04\xb0\x00\x00\x00\x00" + - "7\aT@\x00\x00\x00\x008\x1c!0\x00\x00\x00\x008\xe76@\x00\x00\x00\x009\xfc\x030\x00\x00\x00\x00:\xc7\x18@\x00\x00\x00\x00;\xdb\xe50\x00\x00\x00\x00<\xb04\xc0\x00\x00\x00\x00=\xbb\xc70" + - "\x00\x00\x00\x00>\x90\x16\xc0\x00\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0\x00\x00\x00\x00D/\xbc\xc0\x00\x00\x00\x00" + - "ED\x89\xb0\x00\x00\x00\x00E\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b" + - "\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00\xff\xffeP\x00\x04\xff\xffs`\x01\b" + - "\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00NPT\x00BST\x00BDT\x00" + - "AHST\x00HDT\x00\nHST10HDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Ra\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00" + - "\x0e\x00\x1c\x00America/ManausUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\u007fD\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ" + - "0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff" + - "\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0" + - "\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00" + - "\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\xff\xffǼ\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf1\xf9\x1dɻ\x00\x00\x00" + - "\xbb\x00\x00\x00\x12\x00\x1c\x00America/ParamariboUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd4L\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU\xd6\xfe\xf3%\xb4\x02\x00\x00\xb4\x02\x00\x00\x10\x00\x1c\x00America/ResoluteUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xd5\xfb\x81\x80\xff\xff\xff\xff\xf7/L`\xff\xff\xff" + + "\xff\xf8(w\xe0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc" + + "\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00" + + "\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81" + + "\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00" + + "\x00.\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':" + + "\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00" + + "\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdo" + + "p\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xab\xa0\x00\t\xff\xff\xb9\xb0\x01\r\xff\xff\xb9\xb0\x00\x11-00\x00CDD" + + "T\x00CST\x00CDT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00" + + "\xb1\x00\x00\x00\x12\x00\x1c\x00America/GuadeloupeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x12\xff\xff\xff\xff\x91\x05\x8e\xb8\xff\xff\xff\xff\xbe*K\xc4\xff\xff\xff\xff\xd2b,\xb4\x00\x00\x00\x00\x1b\xbe1" + - "\xb8\x01\x02\x03\x04\xff\xff\xccH\x00\x00\xff\xff\xcc<\x00\x04\xff\xff\xccL\x00\x04\xff\xff\xce\xc8\x00\b\xff\xff\xd5\xd0\x00\x0eLMT\x00PMT\x00-0330\x00-03\x00\n<-03>3" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xfe7\xa1\x87\x1b\x01\x00\x00\x1b\x01\x00\x00\f\x00\x1c\x00America/LimaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xffi\x87#\xbc\xff\xff\xff\xff\x8ct" + - "@\xd4\xff\xff\xff\xff\xc3\xcfJP\xff\xff\xff\xff\xc4E\xe3@\xff\xff\xff\xff\xc5/J\xd0\xff\xff\xff\xff\xc6\x1f-\xc0\xff\xff\xff\xff\xc7\x0f,\xd0\xff\xff\xff\xff\xc7\xff\x0f\xc0\x00\x00\x00\x00\x1e\x18\xc4P\x00\x00" + - "\x00\x00\x1e\x8f]@\x00\x00\x00\x00\x1f\xf9\xf7\xd0\x00\x00\x00\x00 p\x90\xc0\x00\x00\x00\x00%\x9e\xe3\xd0\x00\x00\x00\x00&\x15|\xc0\x00\x00\x00\x00-%\x03P\x00\x00\x00\x00-\x9b\x9c@\x01\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb7\xc4\x00\x00\xff\xff\xb7\xac\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\bLMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9Rd\xa9y\x9at\x03\x00\x00t\x03\x00\x00\x10\x00\x1c\x00America/AsuncionUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xffi\x87\x11\x90\xff\xff\xff\xff\xb8\x17\xf5\x90\x00" + - "\x00\x00\x00\x05+\xda@\x00\x00\x00\x00\a\xfc\xf0\xb0\x00\x00\x00\x00\n\xcft\xc0\x00\x00\x00\x00\v\x97ʰ\x00\x00\x00\x00\f\xb1\xf9\xc0\x00\x00\x00\x00\rx\xfe0\x00\x00\x00\x00\x0e\x93-@\x00\x00\x00\x00\x0f" + - "Z1\xb0\x00\x00\x00\x00\x10t`\xc0\x00\x00\x00\x00\x11dC\xb0\x00\x00\x00\x00\x12U\x94@\x00\x00\x00\x00\x13FȰ\x00\x00\x00\x00\x148\x19@\x00\x00\x00\x00\x15'\xfc0\x00\x00\x00\x00\x16\x19L\xc0\x00" + - "\x00\x00\x00\x17\t/\xb0\x00\x00\x00\x00\x17\xfa\x80@\x00\x00\x00\x00\x18\xeac0\x00\x00\x00\x00\x19۳\xc0\x00\x00\x00\x00\x1a\xcc\xe80\x00\x00\x00\x00\x1b\xbe8\xc0\x00\x00\x00\x00\x1c\xae\x1b\xb0\x00\x00\x00\x00\x1d" + - "\x9fl@\x00\x00\x00\x00\x1e\x8fO0\x00\x00\x00\x00\x1f\x80\x9f\xc0\x00\x00\x00\x00 p\x82\xb0\x00\x00\x00\x00!a\xd3@\x00\x00\x00\x00\"S\a\xb0\x00\x00\x00\x00#DX@\x00\x00\x00\x00$4;0\x00" + - "\x00\x00\x00%A;@\x00\x00\x00\x00&\x15n\xb0\x00\x00\x00\x00'\x06\xbf@\x00\x00\x00\x00'\xf6\xa20\x00\x00\x00\x00(\xee\x8a@\x00\x00\x00\x00)\xb0H\xb0\x00\x00\x00\x00*Ͻ\xc0\x00\x00\x00\x00+" + - "\xb9\t0\x00\x00\x00\x00,\xab\xab@\x00\x00\x00\x00-p\f\xb0\x00\x00\x00\x00.\x8c\xde\xc0\x00\x00\x00\x00/O\xee\xb0\x00\x00\x00\x000n\x12@\x00\x00\x00\x0016h0\x00\x00\x00\x002W.\xc0\x00" + - "\x00\x00\x003\x0f\xb2\xb0\x00\x00\x00\x0047\x10\xc0\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006\x16\xf2\xc0\x00\x00\x00\x006\xe1\xeb\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xc1Ͱ\x00\x00\x00\x009" + - "ֶ\xc0\x00\x00\x00\x00:\xa1\xaf\xb0\x00\x00\x00\x00;\xbf\xd3@\x00\x00\x00\x00<\xaf\xb60\x00\x00\x00\x00=q\x90\xc0\x00\x00\x00\x00>\x8f\x980\x00\x00\x00\x00?Z\xad@\x00\x00\x00\x00@oz0\x00" + - "\x00\x00\x00Aq\xee@\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x1a\xce\xc0\x00\x00\x00\x00G" + - "\xd3R\xb0\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00\x00\x00I\xb34\xb0\x00\x00\x00\x00Jڒ\xc0\x00\x00\x00\x00K\xc1;0\x00\x00\x00\x00L\xa7\xff\xc0\x00\x00\x00\x00M\xa1\x1d0\x00\x00\x00\x00N\x87\xe1\xc0\x00" + - "\x00\x00\x00O\x80\xff0\x00\x00\x00\x00Pp\xfe@\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04" + - "\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\xff\xff\xc9\xf0\x00\x00\xff\xff\xc9\xf0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x00\f\xff\xff" + - "\xd5\xd0\x01\fLMT\x00AMT\x00-04\x00-03\x00\n<-04>4<-03>,M10.1.0/0,M3.4.0/0\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\x0f\x00\x1c\x00America/ChicagoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff" + - "\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa2\xcbt\x00\xff\xff\xff\xff\xa3\x83\xf7\xf0\xff\xff\xff\xff\xa4EҀ\xff\xff\xff\xff\xa5c\xd9\xf0\xff\xff\xff\xff\xa6S\xd9\x00" + - "\xff\xff\xff\xff\xa7\x15\x97p\xff\xff\xff\xff\xa83\xbb\x00\xff\xff\xff\xff\xa8\xfe\xb3\xf0\xff\xff\xff\xff\xaa\x13\x9d\x00\xff\xff\xff\xff\xaaޕ\xf0\xff\xff\xff\xff\xab\xf3\u007f\x00\xff\xff\xff\xff\xac\xbew\xf0\xff\xff\xff\xff" + - "\xad\xd3a\x00\xff\xff\xff\xff\xae\x9eY\xf0\xff\xff\xff\xff\xaf\xb3C\x00\xff\xff\xff\xff\xb0~;\xf0\xff\xff\xff\xff\xb1\x9c_\x80\xff\xff\xff\xff\xb2gXp\xff\xff\xff\xff\xb3|A\x80\xff\xff\xff\xff\xb4G:p" + - "\xff\xff\xff\xff\xb5\\#\x80\xff\xff\xff\xff\xb6'\x1cp\xff\xff\xff\xff\xb7<\x05\x80\xff\xff\xff\xff\xb8\x06\xfep\xff\xff\xff\xff\xb9\x1b\xe7\x80\xff\xff\xff\xff\xb9\xe6\xe0p\xff\xff\xff\xff\xbb\x05\x04\x00\xff\xff\xff\xff" + - "\xbb\xc6\xc2p\xff\xff\xff\xff\xbc\xe4\xe6\x00\xff\xff\xff\xff\xbd\xaf\xde\xf0\xff\xff\xff\xff\xbe\xc4\xc8\x00\xff\xff\xff\xff\xbf\x8f\xc0\xf0\xff\xff\xff\xff\xc0Z\xd6\x00\xff\xff\xff\xff\xc1\xb0\x8fހ\x00\x00\x00\x00" + - "?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed" + + "\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x10\x00\x1c\x00America/EdmontonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x88\xde\xce\xe0\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff" + + "\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x98\x91\x90\xff\xff\xff\xff\xa0҅\x80\xff\xff\xff\xff\xa2\x8a\xe8\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4jʐ\xff\xff\xff\xff\xa55À\xff\xff\xff\xff\xa6S\xe7\x10" + + "\xff\xff\xff\xff\xa7\x15\xa5\x80\xff\xff\xff\xff\xa83\xc9\x10\xff\xff\xff\xff\xa8\xfe\xc2\x00\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff\xff" + + "\xd6 \xdc\x00\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b ݐ\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\n\x00\xbf\x90" + + "\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00" + + "\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80" + + "\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00" + + "\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10" + + "\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00" + + "-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80" + + "\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00" + + ";ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90" + + "\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad\xd4\x00" + - "\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00CDT\x00CST\x00EST\x00CWT\x00CPT\x00\nCST6" + - "CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RMv\xa1\x0f%\x01\x00\x00%\x01\x00\x00\x11\x00\x1c\x00America/Mon" + - "terreyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x10\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa5\xb6\xda`\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00" + - "\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04" + - "\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xa1\xf4\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x01\bLMT\x00CST\x00C" + - "DT\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x14\xc1r8\xe0\x00\x00\x00\xe0\x00\x00\x00\x15\x00\x1c\x00Ame" + - "rica/Coral_HarbourUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xffr\xee\x84d\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xffˈ\xfe" + - "\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\x02\x01\x02\x01\x03\x04\x05\xff\xff\xaa\x1c\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14" + - "LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R錴$q\x03\x00\x00q\x03\x00\x00\x13\x00\x1c\x00" + - "America/Thunder_BayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\xff\xff\x95\xa0\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT," + + "M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUM\x94\xc7Kp\x03\x00\x00p\x03\x00\x00\x11\x00\x1c\x00America/Glace_B" + + "ayUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00" + + "\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80\xf1\xa84\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xe0\x9e?" + + "`\xff\xff\xff\xff\xe1i8P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00" + + "\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X" + + "\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00" + + "\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0" + + "`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00" + + "\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3F" + + "P\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00" + + "\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3" + + "\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00" + + "\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xc7\xcc" + + "\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3.2." + + "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb1݂x\xe8\x00\x00\x00\xe8\x00\x00\x00\x12\x00\x1c\x00America/Costa_RicaUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00" + + "\x11\xff\xff\xff\xffi\x87*M\xff\xff\xff\xff\xa3\xe8\x16M\x00\x00\x00\x00\x116I`\x00\x00\x00\x00\x11\xb7nP\x00\x00\x00\x00\x13\x16+`\x00\x00\x00\x00\x13\x97PP\x00\x00\x00\x00'\x97\xe0`\x00\x00\x00" + + "\x00(n\xb6\xd0\x00\x00\x00\x00)w\xc2`\x00\x00\x00\x00)\xc2\xd9\xd0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb13\x00\x00\xff\xff\xb13\x00\x04\xff\xff\xb9\xb0\x01\t\xff\xff\xab\xa0\x00\rLMT\x00S" + + "JMT\x00CDT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x85-\xb9\xf8\x8a\x01\x00\x00\x8a\x01\x00\x00\r\x00\x1c\x00America/Bele" + + "mUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03" + + "\x00\x00\x00\f\xff\xff\xff\xff\x96\xaatt\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0" + + "\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff" + + "\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060" + + "\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xffҌ\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03" + + ">3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x12\x00\x1c\x00America/St_VincentUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz\xe6" + + "\x95\xb9\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AS" + + "T\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\x0f\x00\x1c\x00America/Phoeni" + + "xUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x04" + + "\x00\x00\x00\x10\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xcf\x17\xdf\x1c" + + "\xff\xff\xff\xffϏ\xe5\xac\xff\xff\xff\xffЁ\x1a\x1c\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\x02\x01\x02\x01\x02\x03\x02\x03\x02\x01\x02\xff\xff\x96\xee\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00" + + "\b\xff\xff\xab\xa0\x01\fLMT\x00MDT\x00MST\x00MWT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUk\xc2\rx\xbf\x01\x00\x00\xbf\x01\x00\x00\x14\x00\x1c\x00A" + + "merica/DanmarkshavnUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xffr\xee\x82,\xff\xff\xff\xff\x8f${\xe0\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`" + - "\xfb\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\b \xc1p\x00\x00" + - "\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9" + - "\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00" + - "\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1" + - "\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00" + - "\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe" + - "\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00" + - "\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb" + - "\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00" + - "\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\xff\xff\xacT\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14LMT\x00CST\x00EST\x00EWT\x00EPT\x00" + - "EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x10\x00\x1c\x00Am" + - "erica/EnsenadaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fdp\xff\xff\xff\xff\xb7\x1b\x10\x00\xff\xff\xff" + - "\xff\xb8\n\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1bY\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR" + - "\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff" + - "\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91" + - "\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00" + - "\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17" + - "\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00" + - "\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xde\xcf" + - "\xa0\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00" + - "\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05" + - "\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00" + - "\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00F\x0f\x82" + - "\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9b\x80I\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13" + + "ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00" + + "\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8d" + + "p\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xab\xa0\x00\t\xff\xff\xb9" + + "\xb0\x01\r\xff\xff\xb9\xb0\x00\x11-00\x00CDDT\x00CST\x00CDT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x0f\x00\x1c\x00America/TijuanaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff" + + "\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fdp\xff\xff\xff\xff\xb7\x1b\x10\x00\xff\xff\xff\xff\xb8\n\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1b" + + "Y\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff" + + "\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91" + + "\xd9\x10\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00" + + "\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t" + + "\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00" + + "\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*" + + "\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00" + + "\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'" + + "V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00" + + "\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd" + + "\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00F\x0f\x82\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00" + + "\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04" + + "\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14LMT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3." + + "2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb7-2f\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x1c\x00America/NoronhaUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f" + + "\xff\xff\xff\xff\x96\xaaed\xff\xff\xff\xff\xb8\x0f;\xd0\xff\xff\xff\xff\xb8\xfd2\x90\xff\xff\xff\xff\xb9\xf1& \xff\xff\xff\xff\xba\xdef\x10\xff\xff\xff\xff\xda8\xa0 \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff" + + "\xdc\x19Ӡ\xff\xff\xff\xffܹK\x10\xff\xff\xff\xff\xdd\xfb\a \xff\xff\xff\xffޛ\xd0\x10\xff\xff\xff\xff\xdf\u074c \xff\xff\xff\xff\xe0T%\x10\xff\xff\xff\xff\xf4\x97\xf1\xa0\xff\xff\xff\xff\xf5\x05P\x10" + + "\xff\xff\xff\xff\xf6\xc0V \xff\xff\xff\xff\xf7\x0e\x10\x90\xff\xff\xff\xff\xf8Q\x1e \xff\xff\xff\xff\xf8Ƿ\x10\xff\xff\xff\xff\xfa\nĠ\xff\xff\xff\xff\xfa\xa8\xea\x90\xff\xff\xff\xff\xfb\xeb\xf8 \xff\xff\xff\xff" + + "\xfc\x8bo\x90\x00\x00\x00\x00\x1dɀ \x00\x00\x00\x00\x1exɐ\x00\x00\x00\x00\x1f\xa0'\xa0\x00\x00\x00\x00 3\xc1\x90\x00\x00\x00\x00!\x81[ \x00\x00\x00\x00\"\v\xba\x90\x00\x00\x00\x00#X\x02\xa0" + + "\x00\x00\x00\x00#\xe2b\x10\x00\x00\x00\x00%7\xe4\xa0\x00\x00\x00\x00%Թ\x10\x00\x00\x00\x007\xf6\xb8\xa0\x00\x00\x00\x008\xb8w\x10\x00\x00\x00\x009\xdf\xd5 \x00\x00\x00\x009\xe9\x01\x90\x00\x00\x00\x00" + + ";\xc8\xf1\xa0\x00\x00\x00\x002\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\x11\x00\x1c\x00A" + + "merica/AnchorageUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00(\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87AH\xff\xff\xff\xffˉ6\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aB0\xff" + + "\xff\xff\xff\xfa\xd2G\xa0\xff\xff\xff\xff\xfe\xb8c@\xff\xff\xff\xff\xff\xa8F0\x00\x00\x00\x00\x00\x98E@\x00\x00\x00\x00\x01\x88(0\x00\x00\x00\x00\x02x'@\x00\x00\x00\x00\x03qD\xb0\x00\x00\x00\x00\x04" + + "aC\xc0\x00\x00\x00\x00\x05Q&\xb0\x00\x00\x00\x00\x06A%\xc0\x00\x00\x00\x00\a1\b\xb0\x00\x00\x00\x00\a\x8d_\xc0\x00\x00\x00\x00\t\x10\xea\xb0\x00\x00\x00\x00\t\xad\xdb@\x00\x00\x00\x00\n\xf0̰\x00" + + "\x00\x00\x00\v\xe0\xcb\xc0\x00\x00\x00\x00\f\xd9\xe90\x00\x00\x00\x00\r\xc0\xad\xc0\x00\x00\x00\x00\x0e\xb9\xcb0\x00\x00\x00\x00\x0f\xa9\xca@\x00\x00\x00\x00\x10\x99\xad0\x00\x00\x00\x00\x11\x89\xac@\x00\x00\x00\x00\x12" + + "y\x8f0\x00\x00\x00\x00\x13i\x8e@\x00\x00\x00\x00\x14Yq0\x00\x00\x00\x00\x15Ip@\x00\x00\x00\x00\x169S0\x00\x00\x00\x00\x17)R@\x00\x00\x00\x00\x18\"o\xb0\x00\x00\x00\x00\x19\t4@\x00" + + "\x00\x00\x00\x1a\x02Q\xb0\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f" + + "\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00" + + "\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-" + + "\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00" + + "\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;" + + "\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00" + + "\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xc4\xf8\x00" + + "\x00\xff\xffsx\x00\x00\xff\xffs`\x00\x04\xff\xff\x81p\x01\b\xff\xff\x81p\x01\f\xff\xffs`\x00\x10\xff\xff\x81p\x01\x15\xff\xff\x81p\x00\x1a\xff\xff\x8f\x80\x01\x1e\xff\xff\x81p\x00#LMT\x00A" + + "ST\x00AWT\x00APT\x00AHST\x00AHDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x0e\x00\x1c\x00America/VirginUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff" + + "\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00" + + "AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x12\x00\x1c\x00America/Fort_WayneUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00" + + "\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff" + + "\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7" + + "\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff" + + "\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16" + + "\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00" + + "\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00" + + "\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5ED" + + "T,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\f\x00\x1c\x00America/AdakU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00" + + "\x00!\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87Z^\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0\xff\xff\xff\xff\xfe\xb8qP\xff\xff" + + "\xff\xff\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00\x00\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00\x05Q4\xc0\x00\x00\x00\x00\x06A" + + "3\xd0\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t\xad\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0\x00\x00\x00\x00\f\xd9\xf7@\x00\x00" + + "\x00\x00\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00\x00\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00\x13i\x9cP\x00\x00\x00\x00\x14Y" + + "\x7f@\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18\"}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0\x00\x00\x00\x00\x1a+\" \x00\x00" + + "\x00\x00\x1a\xf2P\xc0\x00\x00\x00\x00\x1b\xe23\xb0\x00\x00\x00\x00\x1c\xd22\xc0\x00\x00\x00\x00\x1d\xc2\x15\xb0\x00\x00\x00\x00\x1e\xb2\x14\xc0\x00\x00\x00\x00\x1f\xa1\xf7\xb0\x00\x00\x00\x00 vG@\x00\x00\x00\x00!\x81" + + "ٰ\x00\x00\x00\x00\"V)@\x00\x00\x00\x00#j\xf60\x00\x00\x00\x00$6\v@\x00\x00\x00\x00%J\xd80\x00\x00\x00\x00&\x15\xed@\x00\x00\x00\x00'*\xba0\x00\x00\x00\x00'\xff\t\xc0\x00\x00" + + "\x00\x00)\n\x9c0\x00\x00\x00\x00)\xde\xeb\xc0\x00\x00\x00\x00*\xea~0\x00\x00\x00\x00+\xbe\xcd\xc0\x00\x00\x00\x00,Ӛ\xb0\x00\x00\x00\x00-\x9e\xaf\xc0\x00\x00\x00\x00.\xb3|\xb0\x00\x00\x00\x00/~" + + "\x91\xc0\x00\x00\x00\x000\x93^\xb0\x00\x00\x00\x001g\xae@\x00\x00\x00\x002s@\xb0\x00\x00\x00\x003G\x90@\x00\x00\x00\x004S\"\xb0\x00\x00\x00\x005'r@\x00\x00\x00\x0063\x04\xb0\x00\x00" + + "\x00\x007\aT@\x00\x00\x00\x008\x1c!0\x00\x00\x00\x008\xe76@\x00\x00\x00\x009\xfc\x030\x00\x00\x00\x00:\xc7\x18@\x00\x00\x00\x00;\xdb\xe50\x00\x00\x00\x00<\xb04\xc0\x00\x00\x00\x00=\xbb" + + "\xc70\x00\x00\x00\x00>\x90\x16\xc0\x00\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0\x00\x00\x00\x00D/\xbc\xc0\x00\x00" + + "\x00\x00ED\x89\xb0\x00\x00\x00\x00E\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b" + + "\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00\xff\xffeP\x00\x04\xff\xffs`" + + "\x01\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00NPT\x00BST\x00BD" + + "T\x00AHST\x00HDT\x00\nHST10HDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU>\x14\xe7\x03\x83\x03\x00\x00\x83\x03" + + "\x00\x00\x0f\x00\x1c\x00America/DetroitUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x85\xbd\"[\xff\xff\xff\xff\x99<\x94\x00\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff" + + "\xd2`\xfb\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0\xff\xff\xff\xff\xfb3\x90\x8c\xff\xff\xff\xff\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\x00\x00\x00\x00\x06@\xdfp" + + "\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00" + + "\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0" + + "\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00" + + "\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0" + + "\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00" + + ")ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`" + + "\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x00" + + "8\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p" + + "\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00" + + "E\xf3\xa8\xf0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\xff\xff\xb2%\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14" + + "LMT\x00CST\x00EST\x00EWT\x00EPT\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vU\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x1c\x00America/RosarioUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@" + + "\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff" + + "\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0" + + "\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff" + + "\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@" + + "\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff" + + "\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0" + + "\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x00" + + "7\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7" + + "\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU\xf6@\rm\xa8\x05\x00\x00\xa8\x05\x00\x00\x13\x00\x1c\x00America/Fort_NelsonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8f\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^=v\x87\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff" + + "\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc" + + "\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff" + + "\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX" + + " \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff" + + "\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3" + + "\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff" + + "\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v" + + " \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\b \xeb\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00" + + "\x00\n\x00͠\x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91" + + "\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00" + + "\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8" + + "\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00" + + "\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~" + + "\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00" + + "\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc" + + " \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00" + + "\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl" + + "\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00" + + "\x00P\x96.\x90\x00\x00\x00\x00Q\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00" + + "\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xba\x9e\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff" + + "\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vUOKjǪ\x02\x00\x00\xaa\x02\x00\x00\r\x00\x1c\x00America/BahiaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaak\x1c\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff" + + "\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ" + + "\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff" + + "\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa0" + + "5\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00" + + "\x00\x00'!\x0f0\x00\x00\x00\x00'\xbd\xe3\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\x94\x8b \x00\x00\x00\x00*\xea\r\xb0\x00\x00\x00\x00+k2\xa0\x00\x00\x00\x00,\xc0\xb50\x00\x00\x00\x00-f" + + "\xc4 \x00\x00\x00\x00.\xa0\x970\x00\x00\x00\x00/F\xa6 \x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00" + + "\x00\x004\xf8\xc1 \x00\x00\x00\x006 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8" + + "\xff\xb0\x00\x00\x00\x00N\xf0\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xdb\xe4\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00" + + "\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\x0f(\b=\x01\x00\x00=\x01\x00\x00\x15\x00\x1c\x00America/S" + + "anto_DomingoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x06\x00\x00\x00\x1b\xff\xff\xff\xffi\x87\x1d\b\xff\xff\xff\xff\xba\xdfB`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xa7\xc3@\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00" + + "C{\xc8\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x01\xfa\x7fH\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x03\xdd\x04H\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x05\xbf\x89H\x00\x00\x00\x00\a0\xb4P\x00" + + "\x00\x00\x00\a\xa0\xbc\xc8\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:)\xe1`\x01\x03\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x05\x03\x05\xff\xff\xbex\x00\x00\xff\xff\xbe`\x00\x04" + + "\xff\xff\xc7\xc0\x01\t\xff\xff\xb9\xb0\x00\r\xff\xff\xc0\xb8\x01\x11\xff\xff\xc7\xc0\x00\x17LMT\x00SDMT\x00EDT\x00EST\x00-0430\x00AST\x00\nAST4\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\x82\x13z\xe2\xc2\x00\x00\x00\xc2\x00\x00\x00\x13\x00\x1c\x00America/TegucigalpaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa4LKD\x00\x00\x00" + + "\x00 \x9a\xdc\xe0\x00\x00\x00\x00!\\\x9bP\x00\x00\x00\x00\"z\xbe\xe0\x00\x00\x00\x00#<}P\x00\x00\x00\x00D]\x8c\xe0\x00\x00\x00\x00D\xd6\xc8\xd0\x02\x01\x02\x01\x02\x01\x02\xff\xff\xae<\x00\x00\xff\xff" + + "\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00CDT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x15\x00\x1c\x00Am" + + "erica/St_BarthelemyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff" + + "\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x89\xd8" + + "\xba\xee\x15\x04\x00\x00\x15\x04\x00\x00\x0e\x00\x1c\x00America/BelizeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xff\x93^ٰ\xff\xff\xff\xff\x9f\x9f;\xe0\xff\xff\xff\xff\xa0EQ\xd8\xff\xff\xff\xff\xa1" + + "\x7f\x1d\xe0\xff\xff\xff\xff\xa2.nX\xff\xff\xff\xff\xa3^\xff\xe0\xff\xff\xff\xff\xa4\x0ePX\xff\xff\xff\xff\xa5>\xe1\xe0\xff\xff\xff\xff\xa5\xee2X\xff\xff\xff\xff\xa7'\xfe`\xff\xff\xff\xff\xa7\xce\x14X\xff" + + "\xff\xff\xff\xa9\a\xe0`\xff\xff\xff\xff\xa9\xad\xf6X\xff\xff\xff\xff\xaa\xe7\xc2`\xff\xff\xff\xff\xab\x97\x12\xd8\xff\xff\xff\xff\xacǤ`\xff\xff\xff\xff\xadv\xf4\xd8\xff\xff\xff\xff\xae\xa7\x86`\xff\xff\xff\xff\xaf" + + "V\xd6\xd8\xff\xff\xff\xff\xb0\x87h`\xff\xff\xff\xff\xb16\xb8\xd8\xff\xff\xff\xff\xb2p\x84\xe0\xff\xff\xff\xff\xb3\x16\x9a\xd8\xff\xff\xff\xff\xb4Pf\xe0\xff\xff\xff\xff\xb4\xf6|\xd8\xff\xff\xff\xff\xb60H\xe0\xff" + + "\xff\xff\xff\xb6ߙX\xff\xff\xff\xff\xb8\x10*\xe0\xff\xff\xff\xff\xb8\xbf{X\xff\xff\xff\xff\xb9\xf0\f\xe0\xff\xff\xff\xff\xba\x9f]X\xff\xff\xff\xff\xbb\xd9)`\xff\xff\xff\xff\xbc\x7f?X\xff\xff\xff\xff\xbd" + + "\xb9\v`\xff\xff\xff\xff\xbe_!X\xff\xff\xff\xff\xbf\x98\xed`\xff\xff\xff\xff\xc0?\x03X\xff\xff\xff\xff\xc1x\xcf`\xff\xff\xff\xff\xc2(\x1f\xd8\xff\xff\xff\xff\xc3X\xb1`\xff\xff\xff\xff\xc4\b\x01\xd8\xff" + + "\xff\xff\xff\xc58\x93`\xff\xff\xff\xff\xc5\xe7\xe3\xd8\xff\xff\xff\xff\xc7!\xaf\xe0\xff\xff\xff\xff\xc7\xc7\xc5\xd8\xff\xff\xff\xff\xc9\x01\x91\xe0\xff\xff\xff\xffɧ\xa7\xd8\xff\xff\xff\xff\xca\xe1s\xe0\xff\xff\xff\xff\xcb" + + "\x90\xc4X\xff\xff\xff\xff\xcc@\"\xe0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2\xc6qP\xff\xff\xff\xff\xd6)\xfa`\xff\xff\xff\xff\xd6\xd9J\xd8\xff\xff\xff\xff\xd8\t\xdc`\xff\xff\xff\xffع,\xd8\xff" + + "\xff\xff\xff\xd9\xe9\xbe`\xff\xff\xff\xffڙ\x0e\xd8\xff\xff\xff\xff\xdb\xd2\xda\xe0\xff\xff\xff\xff\xdcx\xf0\xd8\xff\xff\xff\xffݲ\xbc\xe0\xff\xff\xff\xff\xdeX\xd2\xd8\xff\xff\xff\xffߒ\x9e\xe0\xff\xff\xff\xff\xe0" + + "A\xefX\xff\xff\xff\xff\xe1r\x80\xe0\xff\xff\xff\xff\xe2!\xd1X\xff\xff\xff\xff\xe3Rb\xe0\xff\xff\xff\xff\xe4\x01\xb3X\xff\xff\xff\xff\xe52D\xe0\xff\xff\xff\xff\xe5\xe1\x95X\xff\xff\xff\xff\xe7\x1ba`\xff" + + "\xff\xff\xff\xe7\xc1wX\xff\xff\xff\xff\xe8\xfbC`\xff\xff\xff\xff\xe9\xa1YX\xff\xff\xff\xff\xea\xdb%`\xff\xff\xff\xff\xeb\x8au\xd8\xff\xff\xff\xff\xec\xbb\a`\xff\xff\xff\xff\xedjW\xd8\xff\xff\xff\xff\xee" + + "\x9a\xe9`\xff\xff\xff\xff\xefJ9\xd8\xff\xff\xff\xff\xf0\x84\x05\xe0\xff\xff\xff\xff\xf1*\x1b\xd8\xff\xff\xff\xff\xf2c\xe7\xe0\xff\xff\xff\xff\xf3\t\xfd\xd8\xff\xff\xff\xff\xf4C\xc9\xe0\xff\xff\xff\xff\xf4\xe9\xdf\xd8\xff" + + "\xff\xff\xff\xf6#\xab\xe0\xff\xff\xff\xff\xf6\xd2\xfcX\xff\xff\xff\xff\xf8\x03\x8d\xe0\xff\xff\xff\xff\xf8\xb2\xdeX\xff\xff\xff\xff\xf9\xe3o\xe0\xff\xff\xff\xff\xfa\x92\xc0X\xff\xff\xff\xff\xfb̌`\xff\xff\xff\xff\xfc" + + "r\xa2X\x00\x00\x00\x00\ab\xdb`\x00\x00\x00\x00\a\xb9\xd0P\x00\x00\x00\x00\x18aq`\x00\x00\x00\x00\x18\xab7P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x02\xff\xff\xadP\x00\x00\xff\xff\xb2\xa8\x01\x04\xff\xff\xab\xa0\x00\n\xff\xff\xb9\xb0\x01\x0e\xff\xff\xb9\xb0\x01\x12\xff\xff\xb9\xb0\x01\x16LMT\x00-0530\x00C" + + "ST\x00CWT\x00CPT\x00CDT\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x1c\x00America/I" + + "ndiana/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00" + + "\x00\x1c\x00\x1c\x00America/Indiana/IndianapolisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p" + + "\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff" + + "\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0" + + "\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff" + + "\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0" + + "\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18" + + "LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x14\x00\x1c\x00America/Indiana/KnoxUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6," + + "\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff" + + "\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ" + + "\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff" + + "\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8" + + "\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff" + + "\xff\xf4_\x87p\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r" + + "\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00" + + "\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92" + + "\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00" + + "\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa" + + "\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00" + + "\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQ" + + "p\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\xff\xff\xae\xca\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b" + + "\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M1" + + "1.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU \x17\x89}q\x01\x00\x00q\x01\x00\x00\x15\x00\x1c\x00America/Indiana/VevayUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\a\x00\x00\x00\x1c" + + "\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff" + + "\xd2a\t\xf0\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`" + + "\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\xff\xff\xb0@\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CP" + + "T\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUM/U\x9f7\x02\x00\x007\x02\x00\x00" + + "\x17\x00\x1c\x00America/Indiana/MarengoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86" + + "\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\xe2~=\x80\xff\xff" + + "\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a" + + "\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff" + + "\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@" + + "\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00" + + "\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x01\x05\x06\x05\x06\x05\x06\xff\xff\xaf\r\x00\x00\xff\xff\xb9\xb0\x01\x04" + + "\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nE" + + "ST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\r\xedsp.\x02\x00\x00.\x02\x00\x00\x19\x00\x1c\x00America/" + + "Indiana/VincennesUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp" + + "\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff" + + "\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4g=\xe0\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0" + + "\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff" + + "\xf0q\x9e\xf0\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\x7f\xa5p\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0" + + "\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-m\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x06\x05\x06\x05\x01\x02\x01\x05\xff\xff\xad\xf1\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff" + + "\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1" + + ".0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x01\xd8N\x8c\xab\x02\x00\x00\xab\x02\x00\x00\x1a\x00\x1c\x00America/Indiana/PetersburgU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x06\x00\x00" + + "\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff" + + "\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xe4g=\xe0\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a" + + "\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff" + + "\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\x7f\xa5p\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff\xff\xf6?ip\xff\xff\xff\xff\xf7/h\x80\xff\xff\xff\xff\xfa\b" + + "g\xf0\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00" + + "\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d" + + "'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00" + + "\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-m\xf0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\x05\xff\xff\xae-\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0" + + "\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vUK-E\xfad\x02\x00\x00d\x02\x00\x00\x17\x00\x1c\x00America/Indiana/WinamacUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6" + + ",\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff" + + "\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe" + + "\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff" + + "\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2" + + "\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff" + + "\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x00\x00\x00\x00G-" + + "_\xe0\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x06\x05\x06\x05\x01\x02\x06\x05\xff\xff\xae\xcf\x00\x00\xff\xff\xb9\xb0\x01" + + "\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\n" + + "EST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUصK\xa6\n\x02\x00\x00\n\x02\x00\x00\x19\x00\x1c\x00America" + + "/Indiana/Tell_CityUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdb" + + "p\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xe4g=\xe0\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff" + + "\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xb1\xda\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c" + + "\xf0\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf2\x7f\xa5p\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xf5O\x86\x80\xff\xff\xff" + + "\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/v" + + "p\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x06\x05\x06\x05\x01\x02\x01\xff\xff\xae\xa9\x00\x00" + + "\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00" + + "EDT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x0f\x00\x1c\x00Am" + + "erica/Knox_INUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff" + + "ˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00" + + "\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff" + + "\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0" + + "\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff" + + "\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00" + + "\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00" + + "\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p" + + "\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00" + + "\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00" + + "\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00" + + " v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0" + + "\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\xff\xff\xae\xca\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST" + + "\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUø\xab\x9b\xf0\x00\x00\x00\xf0" + + "\x00\x00\x00\x0f\x00\x1c\x00America/CrestonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff" + + "\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xcf\x17\xdf\x1c\xff\xff\xff\xffϏ\xe5\xac\xff\xff\xff\xffЁ\x1a\x1c\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\x02\x01\x02\x01\x02\x03\x02" + + "\x03\x02\x01\x02\xff\xff\x96\xee\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\fLMT\x00MDT\x00MST\x00MWT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00America/AnguillaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2" + + "#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa2\x81\xbfyS\x02\x00\x00S\x02\x00\x00\x12\x00\x1c\x00America/MetlakatlaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00\b\x00\x00\x00\x1e\xff\xff\xff\xff?\xc2\xfd\xd1" + + "\xff\xff\xff\xff}\x870\x1a\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00" + + "\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0" + + "\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00" + + "\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10" + + "\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00V5\xe2\xa0\x00\x00\x00\x00V\xe5H0\x00\x00\x00\x00X\x1e\xff \x00\x00\x00\x00" + + "X\xc5*0\x00\x00\x00\x00Y\xfe\xe1 \x00\x00\x00\x00Z\xa5\f0\x00\x00\x00\x00[\xde\xc3 \x00\x00\x00\x00\\DF\xa0\x00\x00\x00\x00\\\x84\xee0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x06\a\x06\a\x06\a\x02\x06\a\x00\x00\xd6&\x00\x00\xff\xff\x84\xa6\x00\x00\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff" + + "\x9d\x90\x01\x10\xff\xff\x81p\x00\x14\xff\xff\x8f\x80\x01\x19LMT\x00PST\x00PWT\x00PPT\x00PDT\x00AKST\x00AKDT\x00\nAKST9AKDT,M3." + + "2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU.\xf9\xc0\x1e\xd5\x05\x00\x00\xd5\x05\x00\x00\x0f\x00\x1c\x00America/MonctonUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x92\x00\x00\x00\x06\x00\x00\x00\x18" + + "\xff\xff\xff\xff^\x1e\xed\xbc\xff\xff\xff\xff\x80\xf1\xb6P\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xff\xbb<8\xd0\xff\xff\xff\xff\xbb\xb4#@\xff\xff\xff\xff\xbd\x1c\x1a\xd0\xff\xff\xff\xff" + + "\xbd\x94\x05@\xff\xff\xff\xff\xbe\xfb\xfc\xd0\xff\xff\xff\xff\xbfs\xe7@\xff\xff\xff\xff\xc0\xdb\xde\xd0\xff\xff\xff\xff\xc1S\xc9@\xff\xff\xff\xff»\xc0\xd0\xff\xff\xff\xff\xc33\xab@\xff\xff\xff\xffě\xa2\xd0" + + "\xff\xff\xff\xff\xc5\x13\x8d@\xff\xff\xff\xff\xc6p\xf8\xd0\xff\xff\xff\xff\xc7\r\xcd@\xff\xff\xff\xff\xc8H\xf1\xd0\xff\xff\xff\xff\xc8\xed\xaf@\xff\xff\xff\xff\xca\x16^\xd0\xff\xff\xff\xff\xca\xd6\xcb\xc0\xff\xff\xff\xff" + + "ˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff\xff\xff\xff\xd4@\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0" + + "\xff\xff\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xda\xfe\x99`\xff\xff\xff\xff\xdb\xc0W\xd0\xff\xff\xff\xff\xdc\xde{`\xff\xff\xff\xffݩtP\xff\xff\xff\xff" + + "\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0\x9e?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff\xff\xff\xff\xe4^\x03`\xff\xff\xff\xff\xe5(\xfcP" + + "\xff\xff\xff\xff\xe6G\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe9\x16\xe4\xd0\xff\xff\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xf6\xc6\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff" + + "\xec֨\xd0\xff\xff\xff\xff\xedƧ\xe0\xff\xff\xff\xff\xee\xbf\xc5P\xff\xff\xff\xff\xef\xaf\xc4`\xff\xff\xff\xff\xf0\x9f\xa7P\xff\xff\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\x7f\x89P\xff\xff\xff\xff\xf3o\x88`" + + "\xff\xff\xff\xff\xf4_kP\xff\xff\xff\xff\xf5Oj`\xff\xff\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff" + + "\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0" + + "\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00" + + "\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0" + + "\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00" + + "\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P" + + "\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00" + + "'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbe]|\x00\x00\x00\x00,\xd3*l\x00\x00\x00\x00-\x9e?|" + + "\x00\x00\x00\x00.\xb3\fl\x00\x00\x00\x00/~!|\x00\x00\x00\x000\x92\xeel\x00\x00\x00\x001g=\xfc\x00\x00\x00\x002r\xd0l\x00\x00\x00\x003G\x1f\xfc\x00\x00\x00\x004R\xb2l\x00\x00\x00\x00" + + "5'\x01\xfc\x00\x00\x00\x0062\x94l\x00\x00\x00\x007\x06\xe3\xfc\x00\x00\x00\x008\x1b\xb0\xec\x00\x00\x00\x008\xe6\xc5\xfc\x00\x00\x00\x009\xfb\x92\xec\x00\x00\x00\x00:Ƨ\xfc\x00\x00\x00\x00;\xdbt\xec" + + "\x00\x00\x00\x00<\xaf\xc4|\x00\x00\x00\x00=\xbbV\xec\x00\x00\x00\x00>\x8f\xa6|\x00\x00\x00\x00?\x9b8\xec\x00\x00\x00\x00@o\x88|\x00\x00\x00\x00A\x84Ul\x00\x00\x00\x00BOj|\x00\x00\x00\x00" + + "Cd7l\x00\x00\x00\x00D/L|\x00\x00\x00\x00ED\x19l\x00\x00\x00\x00E\xf3\x9a\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x05\x03\x02\x03\x02\x03\x02\x03\x02" + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14L" + - "MT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "ROKjǪ\x02\x00\x00\xaa\x02\x00\x00\r\x00\x1c\x00America/BahiaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xc3D\x00\x00" + + "\xff\xff\xb9\xb0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\f\xff\xff\xd5\xd0\x01\x10\xff\xff\xd5\xd0\x01\x14LMT\x00EST\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4A" + + "DT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x82s\x1dT\x01\x00\x00T\x01\x00\x00\x11\x00\x1c\x00America/Chih" + + "uahuaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13" + + "\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\x00\x00\x00\x00" + + "1gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00" + + "\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff" + + "\xff\x9c\x8c\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00CDT\x00MDT\x00\nMST7MDT,M4" + + ".1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUU\r\xf7\xd3\xc7\x01\x00\x00\xc7\x01\x00\x00\r\x00\x1c\x00America/ThuleUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x03\x00\x00\x00\f\xff" + + "\xff\xff\xff\x9b\x80w\xfc\x00\x00\x00\x00'\xf5z\xe0\x00\x00\x00\x00(\xe5]\xd0\x00\x00\x00\x00)\xd5\\\xe0\x00\x00\x00\x00*\xc5?\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-" + + "\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xceP\x00" + + "\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3\xe0\x00\x00\x00\x00;" + + "ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00" + + "\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\xff\xff\xbf\x84\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00ADT\x00AST\x00\nAST4ADT,M3.2.0,M11.1.0\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xac\x8e\xee\x13\xbe\x00\x00\x00\xbe\x00\x00\x00\x0f\x00\x1c\x00America/CaracasUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffi\x87\x1a@\xff\xff\xff\xff\x93" + + "\x1e,<\xff\xff\xff\xff\xf6\x98\xecH\x00\x00\x00\x00G[\x92p\x00\x00\x00\x00W%\xa9p\x01\x02\x03\x02\x03\xff\xff\xc1@\x00\x00\xff\xff\xc1D\x00\x04\xff\xff\xc0\xb8\x00\b\xff\xff\xc7\xc0\x00\x0eLMT\x00" + + "CMT\x00-0430\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\xf9\x1dɻ\x00\x00\x00\xbb\x00\x00\x00\x12\x00\x1c\x00America/" + + "ParamariboUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x12\xff\xff\xff\xff\x91\x05\x8e\xb8\xff\xff\xff\xff\xbe*K\xc4\xff\xff\xff\xff\xd2b,\xb4\x00\x00\x00\x00\x1b\xbe1\xb8\x01\x02\x03\x04\xff\xff\xccH\x00\x00\xff\xff\xcc<\x00" + + "\x04\xff\xff\xccL\x00\x04\xff\xff\xce\xc8\x00\b\xff\xff\xd5\xd0\x00\x0eLMT\x00PMT\x00-0330\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc5" + + "\x92Z\x8c\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x1c\x00America/MendozaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaak\x1c\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff" + - "\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde" + - " \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff" + - "\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05" + - "\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00" + - "\x00'!\x0f0\x00\x00\x00\x00'\xbd\xe3\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\x94\x8b \x00\x00\x00\x00*\xea\r\xb0\x00\x00\x00\x00+k2\xa0\x00\x00\x00\x00,\xc0\xb50\x00\x00\x00\x00-f\xc4" + - " \x00\x00\x00\x00.\xa0\x970\x00\x00\x00\x00/F\xa6 \x00\x00\x00\x000\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00\x00" + - "\x004\xf8\xc1 \x00\x00\x00\x006 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8\xff" + - "\xb0\x00\x00\x00\x00N\xf0\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xdb\xe4\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\b" + - "LMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x10\x00\x1c\x00America/Mo" + - "ntrealUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff" + - "\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9" + - "p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff" + - "\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0" + - "`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff" + - "\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|" + - "p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff" + - "\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t" + - "`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff" + - "\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1" + - "\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff" + - "\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\u007f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w" + - "\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0" + - "p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00" + - "\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5" + - "\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2" + - ".0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\x0f\x00\x1c\x00America/JamaicaUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00\x10\xff" + - "\xff\xff\xffi\x87#~\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f" + - "٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00" + - "\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x01\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xb8\x02\x00\x00\xff\xff\xb8\x02\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\fLMT\x00KMT\x00EST\x00EDT\x00\nES" + - "T5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\x11\x00\x1c\x00America/AnchorageUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00(\xff\xff\xff\xff?\xc2\xfd" + - "\xd1\xff\xff\xff\xff}\x87AH\xff\xff\xff\xffˉ6\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aB0\xff\xff\xff\xff\xfa\xd2G\xa0\xff\xff\xff\xff\xfe\xb8c@\xff\xff\xff\xff\xff\xa8F0\x00\x00\x00" + - "\x00\x00\x98E@\x00\x00\x00\x00\x01\x88(0\x00\x00\x00\x00\x02x'@\x00\x00\x00\x00\x03qD\xb0\x00\x00\x00\x00\x04aC\xc0\x00\x00\x00\x00\x05Q&\xb0\x00\x00\x00\x00\x06A%\xc0\x00\x00\x00\x00\a1\b" + - "\xb0\x00\x00\x00\x00\a\x8d_\xc0\x00\x00\x00\x00\t\x10\xea\xb0\x00\x00\x00\x00\t\xad\xdb@\x00\x00\x00\x00\n\xf0̰\x00\x00\x00\x00\v\xe0\xcb\xc0\x00\x00\x00\x00\f\xd9\xe90\x00\x00\x00\x00\r\xc0\xad\xc0\x00\x00\x00" + - "\x00\x0e\xb9\xcb0\x00\x00\x00\x00\x0f\xa9\xca@\x00\x00\x00\x00\x10\x99\xad0\x00\x00\x00\x00\x11\x89\xac@\x00\x00\x00\x00\x12y\x8f0\x00\x00\x00\x00\x13i\x8e@\x00\x00\x00\x00\x14Yq0\x00\x00\x00\x00\x15Ip" + - "@\x00\x00\x00\x00\x169S0\x00\x00\x00\x00\x17)R@\x00\x00\x00\x00\x18\"o\xb0\x00\x00\x00\x00\x19\t4@\x00\x00\x00\x00\x1a\x02Q\xb0\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00" + - "\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b" + - "0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00" + - "\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P" + - "\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00" + - "\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b" + - "\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00" + - "\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t" + - "\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xc4\xf8\x00\x00\xff\xffsx\x00\x00\xff\xffs`\x00\x04\xff\xff\x81p\x01\b\xff\xff\x81p\x01\f\xff" + - "\xffs`\x00\x10\xff\xff\x81p\x01\x15\xff\xff\x81p\x00\x1a\xff\xff\x8f\x80\x01\x1e\xff\xff\x81p\x00#LMT\x00AST\x00AWT\x00APT\x00AHST\x00AHDT\x00YST\x00A" + - "KDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk\xc2\rx\xbf\x01\x00\x00\xbf\x01\x00" + - "\x00\x14\x00\x1c\x00America/DanmarkshavnUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9b\x80I\x00\x00\x00\x00\x00\x13M|P\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffr\x9c\xb2\x04\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff" + + "\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4" + + "\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff" + + "\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff" + + "0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff" + + "\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR" + + "@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00" + + "\x00%\xf0v\xa0\x00\x00\x00\x00'\x194@\x00\x00\x00\x00'\xcdð\x00\x00\x00\x00(\xfag\xc0\x00\x00\x00\x00)\xb0H\xb0\x00\x00\x00\x00*\xe0\xe1@\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6\xc6" + + "\xb0\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00@\xb0\x13\xb0\x00\x00\x00\x00AV>\xc0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\x7f \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x02\x03\x02\x03\x02\x04\x05\x03\x05\x02\x05\x04\x05\xff\xff\xbf|\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b" + + "\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "\x1d`̟\x00\x03\x00\x00\x00\x03\x00\x00\x15\x00\x1c\x00America/Cambridge_BayUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff\xa1\xf2̀\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff" + + "\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(\x85\xf0\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00" + + "\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00" + + "\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10" + + "\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00" + + "+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80" + + "\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x00" + + "9\xfb\xca\xf0\x00\x00\x00\x00:\x04\xe9P\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00" + + "\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x03\x01\x02\x03" + + "\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\a\x06\b\a\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x00\x00" + + "\x00\x00\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xab\xa0\x01\x15\xff\xff\xb9\xb0\x01\x19\xff\xff\xab\xa0\x00\x1d\xff\xff\xb9\xb0\x00!-00\x00MWT\x00" + + "MPT\x00MST\x00MDDT\x00MDT\x00CDT\x00CST\x00EST\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vUԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x15\x00\x1c\x00America/Coral_HarbourUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffi\x87&\x10\xff\xff\xff\xff\x8b" + + "\xf4a\xe8\x01\x02\xff\xff\xb5p\x00\x00\xff\xff\xb5\x18\x00\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1" + + "\x00\x00\x00\xb1\x00\x00\x00\x12\x00\x1c\x00America/KralendijkUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff" + + "\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0f\x00\x1c\x00America/TorontoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff" + + "\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f" + + "?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff" + + "\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G" + + ",`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff" + + "\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084" + + "}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff" + + "\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00" + + "\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff" + + "\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G" + + "-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff" + + "\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\x7f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_" + + "y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00" + + "\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10" + + "LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x81{" + + "\xc1\x92\xbc\x03\x00\x00\xbc\x03\x00\x00\r\x00\x1c\x00America/SitkaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\t\x00\x00\x00\"\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x873\x99\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#" + + "\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00" + + "\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0" + + "\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00" + + "\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t" + + "\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00" + + "\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15" + + "\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00" + + "\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S" + + "\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00" + + "\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO" + + "̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x06\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x00\x00\xd2" + + "\xa7\x00\x00\xff\xff\x81'\x00\x00\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x81p\x00\x14\xff\xff\x8f\x80\x01\x18\xff\xff\x81p\x00\x1dLMT\x00PST\x00P" + + "WT\x00PPT\x00PDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\xc0\x98\x00\b\xc9\x03\x00\x00\xc9\x03\x00\x00\x12\x00\x1c\x00America/MontevideoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00V\x00\x00\x00\t\x00\x00\x00&\xff\xff\xff\xff\x8c4\xe53\xff\xff\xff\xff\xa2\x92\x87\xb3\xff\xff" + + "\xff\xff\xa8\xff\xdb@\xff\xff\xff\xff\xa9\xf1\x0f\xb0\xff\xff\xff\xff\xaa\xe2Y8\xff\xff\xff\xff\xab\xd2C0\xff\xff\xff\xff\xacÌ\xb8\xff\xff\xff\xff\xad\xb3v\xb0\xff\xff\xff\xff\xbb\xf4\xb5\xb8\xff\xff\xff\xff\xbc\xbf" + + "\xb5\xb0\xff\xff\xff\xff\xbdԗ\xb8\xff\xff\xff\xff\xbe\x9f\x97\xb0\xff\xff\xff\xff\xbf\xb4y\xb8\xff\xff\xff\xff\xc0\x7fy\xb0\xff\xff\xff\xff\xc1\x94[\xb8\xff\xff\xff\xff\xc2_[\xb0\xff\xff\xff\xff\xc3}x8\xff\xff" + + "\xff\xff\xc4?=\xb0\xff\xff\xff\xff\xc5]Z8\xff\xff\xff\xff\xc6\x1f\x1f\xb0\xff\xff\xff\xff\xc7\x18R8\xff\xff\xff\xff\xc8\b<0\xff\xff\xff\xff\xc9\x1d\x1e8\xff\xff\xff\xff\xc9\xe8\x1e0\xff\xff\xff\xffʋ" + + "\x9f8\xff\xff\xff\xff\xcd\x1e\xc60\xff\xff\xff\xff͕f(\xff\xff\xff\xff\xec\v\x85\xb0\xff\xff\xff\xff\xec\xf25(\xff\xff\xff\xff\xedEJ\xb0\xff\xff\xff\xff\xed\x85\xd6 \xff\xff\xff\xff\xf7\x13r\xb0\xff\xff" + + "\xff\xff\xf7\xfa\x1b \xff\xff\xff\xff\xfc\xfe>0\xff\xff\xff\xff\xfd\xf6\x11(\x00\x00\x00\x00\x00\x96u0\x00\x00\x00\x00\x00\xd8R \x00\x00\x00\x00\x04W\x8a\xb0\x00\x00\x00\x00\x04\xc6:\xa0\x00\x00\x00\x00\a\x96" + + "\x1b\xb0\x00\x00\x00\x00\a\xdfژ\x00\x00\x00\x00\bƟ(\x00\x00\x00\x00\tZN0\x00\x00\x00\x00\t\xdbs \x00\x00\x00\x00\r\x1a\x120\x00\x00\x00\x00\r\x7f\x87\xa0\x00\x00\x00\x00\x0e\xe7\x7f0\x00\x00" + + "\x00\x00\x0f_i\xa0\x00\x00\x00\x00\x10\xd9\xd60\x00\x00\x00\x00\x11?K\xa0\x00\x00\x00\x00\x11\x89-\xb0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00!\xc3T0\x00\x00\x00\x00\"'x \x00\x00\x00\x00#\xa1" + + "\xe4\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%Jg\xb0\x00\x00\x00\x00%\xe7< \x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\n+\xb0\x00\x00\x00\x00)\xb0:\xa0\x00\x00" + + "\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x90\x1c\xa0\x00\x00\x00\x00AL\xf60\x00\x00\x00\x00BF/\xc0\x00\x00\x00\x00CH\xa3\xd0\x00\x00\x00\x00D\x13\x9c\xc0\x00\x00\x00\x00E\x1fKP\x00\x00\x00\x00E\xf3" + + "~\xc0\x00\x00\x00\x00G\bg\xd0\x00\x00\x00\x00G\xd3`\xc0\x00\x00\x00\x00H\xe8I\xd0\x00\x00\x00\x00I\xb3B\xc0\x00\x00\x00\x00J\xc8+\xd0\x00\x00\x00\x00K\x9c_@\x00\x00\x00\x00L\xa8\r\xd0\x00\x00" + + "\x00\x00M|A@\x00\x00\x00\x00N\x87\xef\xd0\x00\x00\x00\x00O\\#@\x00\x00\x00\x00Pq\fP\x00\x00\x00\x00Q<\x05@\x00\x00\x00\x00RP\xeeP\x00\x00\x00\x00S\x1b\xe7@\x00\x00\x00\x00T0" + + "\xd0P\x00\x00\x00\x00T\xfb\xc9@\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x05\x06\x05\a\x05\a\x05\x06\x05\a\x05\a\x05\b\x06\x05\a\x05\a\x05\a\x05\a\x05" + + "\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\xff\xff\xcbM\x00\x00\xff\xff\xcbM\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xce\xc8\x00\f" + + "\xff\xff\xd5\xd0\x01\x12\xff\xff\xd5\xd0\x00\x12\xff\xff\xdc\xd8\x01\x16\xff\xff\xe3\xe0\x01\x1c\xff\xff\xea\xe8\x01 LMT\x00MMT\x00-04\x00-0330\x00-03\x00-0230\x00-0" + + "2\x00-0130\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x0f\x00\x1c\x00America/Antigu" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04" + + "\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff" + + "\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd7\b\\\xc6&\x02\x00\x00&\x02\x00\x00\x10\x00\x1c\x00Amer" + + "ica/MiquelonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x91\xb68\xa8\x00\x00\x00\x00\x13nc\xc0\x00\x00\x00\x00 u\xe4\xd0\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"U\xc6\xd0\x00\x00\x00\x00#" + + "j\x93\xc0\x00\x00\x00\x00$5\xa8\xd0\x00\x00\x00\x00%Ju\xc0\x00\x00\x00\x00&\x15\x8a\xd0\x00\x00\x00\x00'*W\xc0\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00)\n9\xc0\x00\x00\x00\x00)މP\x00" + + "\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+\xbekP\x00\x00\x00\x00,\xd38@\x00\x00\x00\x00-\x9eMP\x00\x00\x00\x00.\xb3\x1a@\x00\x00\x00\x00/~/P\x00\x00\x00\x000\x92\xfc@\x00\x00\x00\x001" + + "gK\xd0\x00\x00\x00\x002r\xde@\x00\x00\x00\x003G-\xd0\x00\x00\x00\x004R\xc0@\x00\x00\x00\x005'\x0f\xd0\x00\x00\x00\x0062\xa2@\x00\x00\x00\x007\x06\xf1\xd0\x00\x00\x00\x008\x1b\xbe\xc0\x00" + + "\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xa0\xc0\x00\x00\x00\x00:Ƶ\xd0\x00\x00\x00\x00;ۂ\xc0\x00\x00\x00\x00<\xaf\xd2P\x00\x00\x00\x00=\xbbd\xc0\x00\x00\x00\x00>\x8f\xb4P\x00\x00\x00\x00?" + + "\x9bF\xc0\x00\x00\x00\x00@o\x96P\x00\x00\x00\x00A\x84c@\x00\x00\x00\x00BOxP\x00\x00\x00\x00CdE@\x00\x00\x00\x00D/ZP\x00\x00\x00\x00ED'@\x00\x00\x00\x00E\xf3\x8c\xd0\x01" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xcbX\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x00\b" + + "\xff\xff\xe3\xe0\x01\fLMT\x00AST\x00-03\x00-02\x00\n<-03>3<-02>,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vUa\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\x0e\x00\x1c\x00America/ManausUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x7fD\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN" + + "\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff" + + "\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:" + + "@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00" + + "\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xffǼ\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU\xbf\x03u\xf3\xe4\x01\x00\x00\xe4\x01\x00\x00\x0e\x00\x1c\x00America/RecifeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaag\xb8\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff" + + "\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x15" + + "0\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff" + + "\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1ex\xd7" + + "\xa0\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00" + + "\x00%\xd4\xc7 \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x009\xe9\x0f\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x003\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xac\x8a\x83S\xd4\x00\x00\x00\xd4\x00\x00\x00\x11\x00\x1c\x00America/GuatemalaUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x03\x00\x00\x00\f" + + "\xff\xff\xff\xff\x9f\x9d\xea\xdc\x00\x00\x00\x00\aU\xac`\x00\x00\x00\x00\a͖\xd0\x00\x00\x00\x00\x19,x`\x00\x00\x00\x00\x19\xcf\xe4P\x00\x00\x00\x00'\xea\xee\xe0\x00\x00\x00\x00(\xc8\\\xd0\x00\x00\x00\x00" + + "DTR`\x00\x00\x00\x00E\x1fKP\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xab$\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00CDT\x00CST\x00\nCST6\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\x1c\xd8\x19\x9dp\x01\x00\x00p\x01\x00\x00\x15\x00\x1c\x00America/Swift_CurrentUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x86\xfd\x96\x18\xff" + + "\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd3v\x01\x10\xff\xff\xff\xff\xd4So\x00\xff\xff\xff\xff\xd5" + + "U\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\xff\xff\xff\xff\xd75\xc5\x10\xff\xff\xff\xff\xd8\x00\xbe\x00\xff\xff\xff\xff\xd9\x15\xa7\x10\xff\xff\xff\xff\xd9\xe0\xa0\x00\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe9\x17\x0f\x00\xff" + + "\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xd6\xd3\x00\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xcb\x00\xff\xff\xff\xff\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xad\x00\x00\x00\x00\x00\x04a\x19\x90\x02\x01\x02\x03\x04" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\xff\xff\x9a\xe8\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xab\xa0\x00\x14LMT\x00MD" + + "T\x00MST\x00MWT\x00MPT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x13\x00\x1c\x00Americ" + + "a/Puerto_RicoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffz敹\xff\xff\xff\xff\xcb\xf62\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\x01\x03\x02\x01\xff\xff\xc2\a\x00\x00\xff\xff" + + "\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xd5\xd0\x01\fLMT\x00AST\x00APT\x00AWT\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU~\xb2\x0e\x19V\a\x00\x00" + + "V\a\x00\x00\x10\x00\x1c\x00America/St_JohnsUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbb\x00\x00\x00\b\x00\x00\x00\x19\xff\xff\xff\xff^=4\xec\xff\xff\xff\xff\x9c\xcfb\f\xff\xff\xff\xff\x9d\xa4\xe6\xfc\xff\xff\xff\xff\x9e\xb8~\x8c\xff" + + "\xff\xff\xff\x9f\xba\xd6|\xff\xff\xff\xff\xa0\xb6\x88\xdc\xff\xff\xff\xff\xa18\xffL\xff\xff\xff\xff\xa2\x95\x19\\\xff\xff\xff\xff\xa3\x84\xfcL\xff\xff\xff\xff\xa4t\xfb\\\xff\xff\xff\xff\xa5d\xdeL\xff\xff\xff\xff\xa6" + + "^\x17\xdc\xff\xff\xff\xff\xa7D\xc0L\xff\xff\xff\xff\xa8=\xf9\xdc\xff\xff\xff\xff\xa9$\xa2L\xff\xff\xff\xff\xaa\x1d\xdb\xdc\xff\xff\xff\xff\xab\x04\x84L\xff\xff\xff\xff\xab\xfd\xbd\xdc\xff\xff\xff\xff\xac\xe4fL\xff" + + "\xff\xff\xff\xadݟ\xdc\xff\xff\xff\xff\xae͂\xcc\xff\xff\xff\xff\xaf\xbd\x81\xdc\xff\xff\xff\xff\xb0\xadd\xcc\xff\xff\xff\xff\xb1\xa6\x9e\\\xff\xff\xff\xff\xb2\x8dF\xcc\xff\xff\xff\xff\xb3\x86\x80\\\xff\xff\xff\xff\xb4" + + "m(\xcc\xff\xff\xff\xff\xb5fb\\\xff\xff\xff\xff\xb6M\n\xcc\xff\xff\xff\xff\xb7FD\\\xff\xff\xff\xff\xb8,\xec\xcc\xff\xff\xff\xff\xb9&&\\\xff\xff\xff\xff\xba\x16\tL\xff\xff\xff\xff\xbb\x0fB\xdc\xff" + + "\xff\xff\xff\xbb\xf5\xebL\xff\xff\xff\xff\xbc\xef$\xdc\xff\xff\xff\xff\xbd\xd5\xcdL\xff\xff\xff\xff\xbe\x9eMl\xff\xff\xff\xff\xbe\xcf\x06\xa8\xff\xff\xff\xff\xbf\xb5\xaf\x18\xff\xff\xff\xff\xc0\xb818\xff\xff\xff\xff\xc1" + + "y\xef\xa8\xff\xff\xff\xff\u0098\x138\xff\xff\xff\xff\xc3YѨ\xff\xff\xff\xff\xc4w\xf58\xff\xff\xff\xff\xc59\xb3\xa8\xff\xff\xff\xff\xc6a\x11\xb8\xff\xff\xff\xff\xc7\x19\x95\xa8\xff\xff\xff\xff\xc8@\xf3\xb8\xff" + + "\xff\xff\xff\xc9\x02\xb2(\xff\xff\xff\xff\xca ո\xff\xff\xff\xff\xca\xe2\x94(\xff\xff\xff\xff\xcc\x00\xb7\xb8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xe6\xc8\xff\xff\xff\xffӈD\xd8\xff\xff\xff\xff\xd4" + + "J\x03H\xff\xff\xff\xff\xd5h&\xd8\xff\xff\xff\xff\xd6)\xe5H\xff\xff\xff\xff\xd7H\b\xd8\xff\xff\xff\xff\xd8\t\xc7H\xff\xff\xff\xff\xd9'\xea\xd8\xff\xff\xff\xff\xd9\xe9\xa9H\xff\xff\xff\xff\xdb\x11\aX\xff" + + "\xff\xff\xff\xdb\xd2\xc5\xc8\xff\xff\xff\xff\xdc\xdetX\xff\xff\xff\xffݩmH\xff\xff\xff\xff\u07beVX\xff\xff\xff\xff߉OH\xff\xff\xff\xff\xe0\x9e8X\xff\xff\xff\xff\xe1i1H\xff\xff\xff\xff\xe2" + + "~\x1aX\xff\xff\xff\xff\xe3I\x13H\xff\xff\xff\xff\xe4]\xfcX\xff\xff\xff\xff\xe5(\xf5H\xff\xff\xff\xff\xe6G\x18\xd8\xff\xff\xff\xff\xe7\x12\x11\xc8\xff\xff\xff\xff\xe8&\xfa\xd8\xff\xff\xff\xff\xe8\xf1\xf3\xc8\xff" + + "\xff\xff\xff\xea\x06\xdc\xd8\xff\xff\xff\xff\xea\xd1\xd5\xc8\xff\xff\xff\xff\xeb\xe6\xbe\xd8\xff\xff\xff\xff챷\xc8\xff\xff\xff\xff\xedƠ\xd8\xff\xff\xff\xff\ueffeH\xff\xff\xff\xffﯽX\xff\xff\xff\xff\xf0" + + "\x9f\xa0H\xff\xff\xff\xff\xf1\x8f\x9fX\xff\xff\xff\xff\xf2\x7f\x82H\xff\xff\xff\xff\xf3o\x81X\xff\xff\xff\xff\xf4_dH\xff\xff\xff\xff\xf5OcX\xff\xff\xff\xff\xf6?FH\xff\xff\xff\xff\xf7/EX\xff" + + "\xff\xff\xff\xf8(b\xc8\xff\xff\xff\xff\xf9\x0f'X\xff\xff\xff\xff\xfa\bD\xc8\xff\xff\xff\xff\xfa\xf8C\xd8\xff\xff\xff\xff\xfb\xe8&\xc8\xff\xff\xff\xff\xfc\xd8%\xd8\xff\xff\xff\xff\xfd\xc8\b\xc8\xff\xff\xff\xff\xfe" + + "\xb8\a\xd8\xff\xff\xff\xff\xff\xa7\xea\xc8\x00\x00\x00\x00\x00\x97\xe9\xd8\x00\x00\x00\x00\x01\x87\xcc\xc8\x00\x00\x00\x00\x02w\xcb\xd8\x00\x00\x00\x00\x03p\xe9H\x00\x00\x00\x00\x04`\xe8X\x00\x00\x00\x00\x05P\xcbH\x00" + + "\x00\x00\x00\x06@\xcaX\x00\x00\x00\x00\a0\xadH\x00\x00\x00\x00\b \xacX\x00\x00\x00\x00\t\x10\x8fH\x00\x00\x00\x00\n\x00\x8eX\x00\x00\x00\x00\n\xf0qH\x00\x00\x00\x00\v\xe0pX\x00\x00\x00\x00\f" + + "ٍ\xc8\x00\x00\x00\x00\r\xc0RX\x00\x00\x00\x00\x0e\xb9o\xc8\x00\x00\x00\x00\x0f\xa9n\xd8\x00\x00\x00\x00\x10\x99Q\xc8\x00\x00\x00\x00\x11\x89P\xd8\x00\x00\x00\x00\x12y3\xc8\x00\x00\x00\x00\x13i2\xd8\x00" + + "\x00\x00\x00\x14Y\x15\xc8\x00\x00\x00\x00\x15I\x14\xd8\x00\x00\x00\x00\x168\xf7\xc8\x00\x00\x00\x00\x17(\xf6\xd8\x00\x00\x00\x00\x18\"\x14H\x00\x00\x00\x00\x19\b\xd8\xd8\x00\x00\x00\x00\x1a\x01\xf6H\x00\x00\x00\x00\x1a" + + "\xf1\xf5X\x00\x00\x00\x00\x1b\xe1\xd8H\x00\x00\x00\x00\x1c\xd1\xd7X\x00\x00\x00\x00\x1d\xc1\xbaH\x00\x00\x00\x00\x1e\xb1\xb9X\x00\x00\x00\x00\x1f\xa1\x9cH\x00\x00\x00\x00 u\xcf\xf4\x00\x00\x00\x00!\x81bd\x00" + + "\x00\x00\x00\"U\xb1\xf4\x00\x00\x00\x00#jp\xd4\x00\x00\x00\x00$5\x93\xf4\x00\x00\x00\x00%J`\xe4\x00\x00\x00\x00&\x15u\xf4\x00\x00\x00\x00'*B\xe4\x00\x00\x00\x00'\xfe\x92t\x00\x00\x00\x00)" + + "\n$\xe4\x00\x00\x00\x00)\xdett\x00\x00\x00\x00*\xea\x06\xe4\x00\x00\x00\x00+\xbeVt\x00\x00\x00\x00,\xd3#d\x00\x00\x00\x00-\x9e8t\x00\x00\x00\x00.\xb3\x05d\x00\x00\x00\x00/~\x1at\x00" + + "\x00\x00\x000\x92\xe7d\x00\x00\x00\x001g6\xf4\x00\x00\x00\x002r\xc9d\x00\x00\x00\x003G\x18\xf4\x00\x00\x00\x004R\xabd\x00\x00\x00\x005&\xfa\xf4\x00\x00\x00\x0062\x8dd\x00\x00\x00\x007" + + "\x06\xdc\xf4\x00\x00\x00\x008\x1b\xa9\xe4\x00\x00\x00\x008\xe6\xbe\xf4\x00\x00\x00\x009\xfb\x8b\xe4\x00\x00\x00\x00:Ơ\xf4\x00\x00\x00\x00;\xdbm\xe4\x00\x00\x00\x00<\xaf\xbdt\x00\x00\x00\x00=\xbbO\xe4\x00" + + "\x00\x00\x00>\x8f\x9ft\x00\x00\x00\x00?\x9b1\xe4\x00\x00\x00\x00@o\x81t\x00\x00\x00\x00A\x84Nd\x00\x00\x00\x00BOct\x00\x00\x00\x00Cd0d\x00\x00\x00\x00D/Et\x00\x00\x00\x00E" + + "D\x12d\x00\x00\x00\x00E\xf3w\xf4\x00\x00\x00\x00G-.\xe4\x00\x00\x00\x00G\xd3Y\xf4\x00\x00\x00\x00I\r\x10\xe4\x00\x00\x00\x00I\xb3;\xf4\x00\x00\x00\x00J\xec\xf2\xe4\x00\x00\x00\x00K\x9cXt\x00" + + "\x00\x00\x00L\xd6\x0fd\x00\x00\x00\x00M|:t\x00\x00\x00\x00N\xb6\rH\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x05\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\xff\xffΔ\x00\x00\xff\xffܤ\x01\x04\xff\xffΔ\x00\b\xff\xff\xdc\xd8\x01\x04\xff\xff\xce\xc8\x00\b" + + "\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10\xff\xff\xea\xe8\x01\x14LMT\x00NDT\x00NST\x00NPT\x00NWT\x00NDDT\x00\nNST3:30NDT,M3.2." + + "0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x1c\x00America/CaymanUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff" + + "\xffi\x87&\x10\xff\xff\xff\xff\x8b\xf4a\xe8\x01\x02\xff\xff\xb5p\x00\x00\xff\xff\xb5\x18\x00\x04\xff\xff\xb9\xb0\x00\bLMT\x00CMT\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\x7f$*\xa0\xa6\x03\x00\x00\xa6\x03\x00\x00\x0e\x00\x1c\x00America/CuiabaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa{\x94\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfd" + + "N\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff" + + "\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q" + + ":@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00" + + "\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00#X\x1e\xc0\x00\x00\x00\x00#\xe2~0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xd4" + + "\xd50\x00\x00\x00\x00'!\x1d@\x00\x00\x00\x00'\xbd\xf1\xb0\x00\x00\x00\x00)\x00\xff@\x00\x00\x00\x00)\x94\x990\x00\x00\x00\x00*\xea\x1b\xc0\x00\x00\x00\x00+k@\xb0\x00\x00\x00\x00,\xc0\xc3@\x00\x00" + + "\x00\x00-f\xd20\x00\x00\x00\x00.\xa0\xa5@\x00\x00\x00\x00/F\xb40\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001\x1d[\xb0\x00\x00\x00\x002W.\xc0\x00\x00\x00\x003\x06x0\x00\x00\x00\x0048" + + "b@\x00\x00\x00\x004\xf8\xcf0\x00\x00\x00\x006 -@\x00\x00\x00\x006\xcfv\xb0\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x00:\x8f:\xb0\x00\x00" + + "\x00\x00;\xc9\r\xc0\x00\x00\x00\x00N\xfe\xb0\x00\x00\x00\x00A\x87\x06@\x00\x00\x00\x00B\x17\xfd0\x00\x00\x00\x00CQ\xd0@\x00\x00\x00\x00C\xf7" + + "\xdf0\x00\x00\x00\x00EMa\xc0\x00\x00\x00\x00E\xe0\xfb\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xb7\xa30\x00\x00\x00\x00H\xfa\xb0\xc0\x00\x00\x00\x00I\x97\x850\x00\x00\x00\x00Jڒ\xc0\x00\x00" + + "\x00\x00K\x80\xa1\xb0\x00\x00\x00\x00L\xbat\xc0\x00\x00\x00\x00M`\x83\xb0\x00\x00\x00\x00N\x9aV\xc0\x00\x00\x00\x00OI\xa00\x00\x00\x00\x00P\x83s@\x00\x00\x00\x00Q G\xb0\x00\x00\x00\x00Rc" + + "U@\x00\x00\x00\x00S\x00)\xb0\x00\x00\x00\x00TC7@\x00\x00\x00\x00T\xe9F0\x00\x00\x00\x00V#\x19@\x00\x00\x00\x00V\xc9(0\x00\x00\x00\x00X\x02\xfb@\x00\x00\x00\x00X\xa9\n0\x00\x00" + + "\x00\x00Y\xe2\xdd@\x00\x00\x00\x00Z\x88\xec0\x00\x00\x00\x00[\xden\xc0\x00\x00\x00\x00\\h\xce0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff" + + "\xff\xcbl\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcd\xc3v\xe3\xb3\x00\x00\x00\xb3" + + "\x00\x00\x00\x11\x00\x1c\x00America/GuayaquilUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87&X\xff\xff\xff\xff\xb6\xa4B\x18\x00\x00\x00\x00+\x16\xfc\xd0\x00\x00\x00\x00+q\xe6@\x01" + + "\x03\x02\x03\xff\xff\xb5(\x00\x00\xff\xff\xb6h\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00QMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x1c\x00Antarctica/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUƉ\xf71\x84\x00\x00\x00\x84\x00\x00\x00\x12\x00\x1c\x00Antarctica/RotheraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\x00\x00\x00\x00\r\x02-\x00\x01" + + "\x00\x00\x00\x00\x00\x00\xff\xff\xd5\xd0\x00\x04-00\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x12\x00\x1c\x00An" + + "tarctica/McMurdoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff" + + "\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba" + + "̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff" + + "\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8" + + "\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00" + + "\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14" + + "\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00" + + "\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#" + + "i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00" + + "\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001" + + "J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00" + + "\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?" + + "~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00" + + "\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + + "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00" + + "\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5.0,M4" + + ".1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb2\x84J]\xd0\x03\x00\x00\xd0\x03\x00\x00\x14\x00\x1c\x00Antarctica/MacquarieUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00\x00\x00\x03\x00\x00\x00\x0e" + + "\xff\xff\xff\xff|\x05\x16\x00\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xa0\x87\xb4`\xff\xff\xff\xff\xd7\fh\x00\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc\xb2~\x00\xff\xff\xff\xff" + + "\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00" + + "\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00" + + "\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00" + + "\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18\xe31\x00\x00\x00\x00\x00" + + "\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80" + + "\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00" + + "'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x8d\x87\x80" + + "\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x00" + + "6\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00\x00\x00\x00<\xa5\xe1\x00" + + "\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C>\xb2\x80\x00\x00\x00\x00" + + "D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x00\x00\x00\x00G\xf7\xa2\x00\x00\x00\x00\x00H\xe7\x93\x00\x00\x00\x00\x00Iׄ\x00\x00\x00\x00\x00J\xc7u\x00" + + "\x00\x00\x00\x00M\x97H\x00\x01\x02\x01\x00\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\x9a\xb0\x01\t-00" + + "\x00AEST\x00AEDT\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85" + + "\x00\x00\x00\x85\x00\x00\x00\x10\x00\x1c\x00Antarctica/SyowaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*0\x00\x04LMT\x00+03\x00\n" + + "<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x19\x00\x1c\x00Antarctica/DumontDUrvi" + + "lleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00" + + "\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PMMT\x00+10\x00\n<+10" + + ">-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x95{\xf3\xa9w\x03\x00\x00w\x03\x00\x00\x11\x00\x1c\x00Antarctica/PalmerUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xf6" + + "\x98\xad\x00\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff" + + "\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00\x170\xbc\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18" + + "\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00" + + "\x00\x00\x00 \x7f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&" + + "\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00" + + "\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005" + + "\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00" + + "\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00C" + + "H\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00" + + "\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q" + + "|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00XC\x86\xb0\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x04\x03\x04\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xe3\xe0\x01\f\xff\xff\xd5\xd0\x00\b-00\x00-04\x00-03\x00-02\x00\n" + + "<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU:\xc8P7\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00Antarctica/TrollUT\t\x00\x03\xaf\x16" + + "\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\x00\x00\x00\x00" + + "B\rG\x00\x00\x00\x00\x00BF\x05\x90\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x00\x00\x00\b-00\x00+02\x00+00\x00\n<+00>0<+02>-2,M" + + "3.5.0/1,M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x15\x00\x1c\x00Antarctica/S" + + "outh_PoleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h" + + "\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff" + + "\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0" + + "\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff" + + "\xd2ښ@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0" + + "\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00" + + "\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`" + + "\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00" + + "$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`" + + "\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x00" + + "2Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0" + + "\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00" + + "@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`" + + "\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04" + + "\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5.0,M4.1.0/3\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd7N\xab\x8b\x98\x00\x00\x00\x98\x00\x00\x00\x11\x00\x1c\x00Antarctica/MawsonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xe2 2\x80\x00\x00" + + "\x00\x00J\xda\"@\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00T`\x00\x04\x00\x00FP\x00\b-00\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "UB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\x11\x00\x1c\x00Antarctica/VostokUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xb0\xfe\xbad\x01\x00\x00R\x1c\x00\x00\x00\x00T`\x00\x04LM" + + "T\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xddzAh\xf3\x00\x00\x00\xf3\x00\x00\x00\x10\x00\x1c\x00Antarctica/Case" + + "yUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x03" + + "\x00\x00\x00\f\xff\xff\xff\xff\xfe\x1è\x00\x00\x00\x00J\xda\x06 \x00\x00\x00\x00K\x8f\xca\xf0\x00\x00\x00\x00N\xa9\x9c \x00\x00\x00\x00OC͐\x00\x00\x00\x00X\n;\x80\x00\x00\x00\x00Z\xa4\x0f\x10" + + "\x00\x00\x00\x00[\xb9\x14@\x00\x00\x00\x00\\\x8d\x1d\x80\x00\x00\x00\x00]\x96E0\x00\x00\x00\x00^c\xc5\x00\x00\x00\x00\x00_x\xa0<\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00" + + "p\x80\x00\x04\x00\x00\x9a\xb0\x00\b-00\x00+08\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x95\xea\x06\xd3\xc5\x00\x00\x00\xc5\x00\x00\x00\x10\x00" + + "\x1c\x00Antarctica/DavisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xe7\x9c@\x00\xff\xff\xff\xff\xf6G\xdf\x10\xff\xff\xff\xff\xfeG\xab\x00\x00\x00\x00\x00J\xda\x140\x00\x00\x00\x00K\x97\xfa" + + "@\x00\x00\x00\x00N\xa9\xaa0\x00\x00\x00\x00OC\xf7\xc0\x01\x00\x01\x02\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00bp\x00\x04\x00\x00FP\x00\b-00\x00+07\x00+05\x00\n<+07>" + + "-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Arctic/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x13\x00\x1c\x00Arctic/LongyearbyenUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12" + + "\xff\xff\xff\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff" + + "\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00" + + "\xff\xff\xff\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff" + + "\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90" + "\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00" + "\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#N\xf0\xa0\x00\x00\x00\x00P\x83e0\x00\x00\x00\x00Q 9\xa0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd2\xd0\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-0" + - "2\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x1c\x00America/CordobaU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x06\x00\x00" + - "\x00\x14\xff\xff\xff\xffr\x9c\xad\xb0\xff\xff\xff\xff\xa2\x92\x8f0\xff\xff\xff\xff\xb6{R@\xff\xff\xff\xff\xb7\x1aɰ\xff\xff\xff\xff\xb8\x1e\x8f@\xff\xff\xff\xff\xb8\xd4p0\xff\xff\xff\xff\xba\x17}\xc0\xff\xff" + - "\xff\xff\xba\xb5\xa3\xb0\xff\xff\xff\xff\xbb\xf8\xb1@\xff\xff\xff\xff\xbc\x96\xd70\xff\xff\xff\xff\xbd\xd9\xe4\xc0\xff\xff\xff\xff\xbex\n\xb0\xff\xff\xff\xff\xbf\xbb\x18@\xff\xff\xff\xff\xc0Z\x8f\xb0\xff\xff\xff\xff\xc1\x9d" + - "\x9d@\xff\xff\xff\xff\xc2;\xc30\xff\xff\xff\xff\xc3~\xd0\xc0\xff\xff\xff\xff\xc4\x1c\xf6\xb0\xff\xff\xff\xff\xc5`\x04@\xff\xff\xff\xff\xc5\xfe*0\xff\xff\xff\xff\xc7A7\xc0\xff\xff\xff\xff\xc7\xe0\xaf0\xff\xff" + - "\xff\xffȁ\x94@\xff\xff\xff\xff\xcaM\xa1\xb0\xff\xff\xff\xff\xca\xee\x86\xc0\xff\xff\xff\xff\xceM\xff0\xff\xff\xff\xffΰ\xed\xc0\xff\xff\xff\xff\xd3)5\xb0\xff\xff\xff\xff\xd4Cd\xc0\xff\xff\xff\xff\xf4=" + - "\b0\xff\xff\xff\xff\xf4\x9f\xf6\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf62\x10@\xff\xff\xff\xff\xf6柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff" + - "\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$" + - "o\xa0\x00\x00\x00\x00#\x94\xb5\xb0\x00\x00\x00\x00$\x10\x94\xa0\x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xf0v\xa0\x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xd0X\xa0\x00\x00\x00\x00)\x00\xff@\x00\x00" + - "\x00\x00)\xb0:\xa0\x00\x00\x00\x00*\xe0\xd30\x00\x00\x00\x00+\x99W \x00\x00\x00\x007\xf6ư\x00\x00\x00\x008\xbf*\xb0\x00\x00\x00\x00Gw\t\xb0\x00\x00\x00\x00G\xdc\u007f \x00\x00\x00\x00H\xfa" + - "\xa2\xb0\x00\x00\x00\x00I\xbca \x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x04\x05\x04\x05\x04\x05\x04" + - "\x02\x04\x05\x04\x05\x03\x05\x04\x05\x04\x05\xff\xff\xc3\xd0\x00\x00\xff\xff\xc3\xd0\x00\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\fLMT\x00CMT\x00-04\x00-" + - "03\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\u0096dK~\x02\x00\x00~\x02\x00\x00\x0e\x00\x1c\x00America/ReginaU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x06\x00\x00" + - "\x00\x18\xff\xff\xff\xff\x86\xfd\x93\x1c\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xb5eO\xf0\xff\xff\xff\xff\xb60H\xe0\xff\xff\xff\xff\xb7E1\xf0\xff\xff\xff\xff\xb8\x10*\xe0\xff\xff" + - "\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xf0\f\xe0\xff\xff\xff\xff\xbb\x0e0p\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xee\x12p\xff\xff\xff\xff\xbd\xb9\v`\xff\xff\xff\xff\xc2r\b\xf0\xff\xff\xff\xff\xc3a" + - "\xeb\xe0\xff\xff\xff\xff\xc4Q\xea\xf0\xff\xff\xff\xff\xc58\x93`\xff\xff\xff\xff\xc61\xcc\xf0\xff\xff\xff\xff\xc7!\xaf\xe0\xff\xff\xff\xff\xc8\x1a\xe9p\xff\xff\xff\xff\xc9\n\xcc`\xff\xff\xff\xff\xc9\xfa\xcbp\xff\xff" + - "\xff\xff\xca\xea\xae`\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd3c\x8c\x10\xff\xff\xff\xff\xd4So\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff\xff\xd6 " + - "\xdc\x00\xff\xff\xff\xff\xd75\xc5\x10\xff\xff\xff\xff\xd8\x00\xbe\x00\xff\xff\xff\xff\xd9\x15\xa7\x10\xff\xff\xff\xff\xd9\xe0\xa0\x00\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x82\x00\xff\xff\xff\xff\xdcޥ\x90\xff\xff" + - "\xff\xffݩ\x9e\x80\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x80\x80\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff\xe1ib\x80\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3ID\x80\xff\xff\xff\xff\xe4^" + - "-\x90\xff\xff\xff\xff\xe5)&\x80\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12C\x00\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf2%\x00\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xd6\xd3\x00\xff\xff" + - "\xff\xff\xed\xc6\xd2\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\xff" + - "\xff\x9d\xe4\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CST\x00\n" + - "CST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x85-\xb9\xf8\x8a\x01\x00\x00\x8a\x01\x00\x00\r\x00\x1c\x00America/BelemUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + - "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaatt\xff" + - "\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xff\xdc" + - "\xb9Y \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4\x97\xff\xb0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff" + - "\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1d" + - "Ɏ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xffҌ\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9Rӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0f\x00\x1c\x00America/TorontoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba" + - "\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff" + - "\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad\xdc" + - "\x8dp\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff" + - "\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf" + - "\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff" + - "\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#" + - "\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff" + - "\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉" + - "d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff" + - "\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xed\xc6" + - "\xb5\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\u007f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff" + - "\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO" + - "\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00" + - "EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RU9#\xbe2\x05" + - "\x00\x002\x05\x00\x00\x11\x00\x1c\x00America/VancouverUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^=v\xec\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ" + - "\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd3v\x0f \xff\xff\xff\xff\xd4A\b\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff" + - "\xff\xff\xd8\x00\xcc\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be" + - "\x95\xa0\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff" + - "\xff\xff\xe6GX \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1" + - "\xf7\x10\xff\xff\xff\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\u007f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff" + - "\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8" + - "\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00" + - "\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\b \xeb\xa0\x00\x00\x00\x00\t\x10" + - "ΐ\x00\x00\x00\x00\n\x00͠\x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00" + - "\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)" + - "6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00" + - "\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J" + - "\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00" + - "\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003G" + - "t \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00" + - "\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84" + - "\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x8c\x94\x00\x00\xff\xff\x9d" + - "\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11" + - ".1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf8Dz\x97\xae\x01\x00\x00\xae\x01\x00\x00\x11\x00\x1c\x00America/Boa_VistaUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96" + - "\xaa\u007f\xe0\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff" + - "\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6" + - "\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00" + - "\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x007\xf6\xd4\xc0\x00\x00\x00\x008" + - "\xb8\x930\x00\x00\x00\x009\xdf\xf1@\x00\x00\x00\x009\xe9\x1d\xb0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xc7 \x00\x00\xff\xff" + - "\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\x15\x00\x1c\x00" + - "America/Lower_PrincesUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\x93\x1e.#\xff\xff\xff\xff\xf6\x98\xecH\x01\x02\xff\xff\xbf]\x00\x00\xff\xff\xc0\xb8\x00\x04\xff\xff\xc7\xc0\x00\n" + - "LMT\x00-0430\x00AST\x00\nAST4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x1c\x00Antarctica" + - "/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc2\v\xae\b\x85\x00\x00\x00\x85\x00\x00\x00\x11\x00\x1c\x00A" + - "ntarctica/VostokUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "*\xc5\a\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xa4\xe9\x90\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\x84ː\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x00\x00\f\x88\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00*0\x01\rLMT\x00CEST\x00CET\x00CEMT\x00\nCET-1CEST,M3.5.0," + + "M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x1c\x00Asia/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\\\x91\x87\xbb\xf7\x00\x00\x00\xf7\x00\x00\x00\f\x00\x1c\x00Asia/ColomboUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x18\xff\xff" + + "\xff\xffV\xb6\x99$\xff\xff\xff\xff\x87\x9d\xbd\x1c\xff\xff\xff\xff\xcbZ\x1c(\xff\xff\xff\xff̕+\xa0\xff\xff\xff\xff\xd2u\x808\x00\x00\x00\x001\xa6\x00(\x00\x00\x00\x002q\x00 \x00\x00\x00\x00D?" + + "\xea(\x01\x02\x03\x04\x02\x05\x06\x02\x00\x00J\xdc\x00\x00\x00\x00J\xe4\x00\x04\x00\x00MX\x00\b\x00\x00T`\x01\x0e\x00\x00[h\x01\x12\x00\x00[h\x00\x12\x00\x00T`\x00\x0eLMT\x00MMT\x00" + + "+0530\x00+06\x00+0630\x00\n<+0530>-5:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU)p\x1cX\xf1\x02\x00\x00\xf1\x02\x00\x00\x10\x00\x1c\x00" + + "Asia/NovosibirskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xe9X\x89\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00T`\x00\x04-00\x00+06\x00\n<+06>-6\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\x95{\xf3\xa9w\x03\x00\x00w\x03\x00\x00\x11\x00\x1c\x00Antarctica/PalmerUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xf6\x98\xad\x00\xff\xff\xff\xff\xf6" + - "柰\xff\xff\xff\xff\xf8\x13C\xc0\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xf9\xf4w@\xff\xff\xff\xff\xfa\xd36\xb0\xff\xff\xff\xff\xfb\xc35\xc0\xff\xff\xff\xff\xfc\xbcS0\xff\xff\xff\xff\xfd\xacR@\xff" + - "\xff\xff\xff\xfe\x9c50\xff\xff\xff\xff\xff\x8c4@\x00\x00\x00\x00\a\xa3J\xb0\x00\x00\x00\x00\b$o\xa0\x00\x00\x00\x00\x170\xbc\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19" + - "\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f\x030\x00" + - "\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'" + - "١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00" + - "\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006" + - "\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00" + - "\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D" + - "\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00" + - "\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R" + - "+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00XC\x86\xb0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03\x04\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x04\x00\x00\x00\x00\x00\x00\xff\xff\xc7\xc0\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xe3\xe0\x01\f\xff\xff\xd5\xd0\x00\b-00\x00-04\x00-03\x00-02\x00\n<-03>3\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R:\xc8P7\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x1c\x00Antarctica/TrollUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\x00\x00\x00\x00B\rG\x00\x00\x00\x00\x00" + - "BF\x05\x90\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x00\x00\x00\b-00\x00+02\x00+00\x00\n<+00>0<+02>-2,M3.5.0/1," + - "M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x12\x00\x1c\x00Antarctica/McMurdoUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00" + - "\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff" + - "\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8" + - "\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff" + - "\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18\xfd" + - "\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00" + - "\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83" + - "`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00" + - "\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01" + - "`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00" + - "\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab" + - "`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00" + - "\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4" + - "`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00" + - "\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R\x95\xea\x06\xd3\xc5\x00\x00\x00\xc5\x00\x00\x00\x10\x00\x1c\x00Antarctica/DavisUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xe7\x9c@\x00\xff\xff\xff\xff\xf6G\xdf\x10\xff\xff\xff\xff\xfeG" + - "\xab\x00\x00\x00\x00\x00J\xda\x140\x00\x00\x00\x00K\x97\xfa@\x00\x00\x00\x00N\xa9\xaa0\x00\x00\x00\x00OC\xf7\xc0\x01\x00\x01\x02\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00bp\x00\x04\x00\x00FP\x00\b-" + - "00\x00+07\x00+05\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\r\x0e\xf20\x85\x00\x00\x00\x85\x00\x00\x00\x10\x00\x1c\x00Antarctica" + - "/SyowaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\x19$\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00" + + "\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e" + + "\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00" + + "\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+" + + "\xb4\xa40\x00\x00\x00\x00+\xfeN\x00\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\x94@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00" + + "\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008" + + "\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00" + + "\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G" + + "#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00" + + "\x00\x00\x00TK\xf30\x00\x00\x00\x00W\x93\xcc\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + + "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00M\xbc\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00" + + "+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUѾ\xa8\xc7u\x02\x00\x00u\x02\x00\x00\f\x00\x1c\x00Asia/TbilisiU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x06\x00\x00" + + "\x00\x15\xff\xff\xff\xffV\xb6\xba\x01\xff\xff\xff\xff\xaa\x19\x9a\x01\xff\xff\xff\xff\xe7\xda\fP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00" + + "\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|" + + "V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00" + + "\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94" + + "\xa2P\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dY0\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x003=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dV\xc0\x00\x00" + + "\x00\x0062#\xb0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1b@0\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb\"0\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6" + + "\x19@\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xc80\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00@\xddǰ\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x01\x02" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x05\x02\x05\x02\x05\x04\x03\x04\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x05\x02\x04\x00\x00)\xff\x00\x00\x00\x00)\xff" + + "\x00\x04\x00\x00*0\x00\t\x00\x00FP\x01\r\x00\x008@\x00\x11\x00\x008@\x01\x11LMT\x00TBMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vU`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\x12\x00\x1c\x00Asia/Ujung_PandangUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xa1\xf2]\x90\xff\xff\xff\xff\xba" + + "\x16Ր\xff\xff\xff\xffˈ\x1d\x80\xff\xff\xff\xff\xd2V\xeep\x01\x02\x03\x04\x00\x00o\xf0\x00\x00\x00\x00o\xf0\x00\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\f\x00\x00p\x80\x00\x10LMT\x00MMT" + + "\x00+08\x00+09\x00WITA\x00\nWITA-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUw\x86\x8d^\x03\x03\x00\x00\x03\x03\x00\x00\r\x00\x1c\x00Asia/Ust" + + "-NeraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B" + + "\x00\x00\x00\b\x00\x00\x00\x18\xff\xff\xff\xff\xa1\xdbݺ\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00" + + "\x18\xe9\x9eP\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p" + + "\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00" + + "'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0" + + "\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x00" + + "4R\tp\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\a\xf0\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p" + + "\x00\x00\x00\x00;\xda\xcb\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x8f\xf0\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00" + + "BEx\xf0\x00\x00\x00\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p" + + "\x00\x00\x00\x00I\xce;p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x00\x00\x00\x00Nm\xf4@\x00\x00\x00\x00TK\xba\xf0\x01\x02\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x05\x06\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\a" + + "\x03\x06\x00\x00\x86F\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00\x9a\xb0\x00\f\x00\x00\xa8\xc0\x01\x10\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x14\x00\x00\xa8\xc0\x00\x10LMT\x00+08\x00+0" + + "9\x00+11\x00+12\x00+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x87\xbd\xedL\xf1\x02\x00\x00\xf1\x02\x00\x00\f\x00\x1c\x00Asia/B" + + "arnaulUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xe7\xb1X\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00*0\x00\x04-00\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xc8\x14\xdcA\x98\x00\x00\x00\x98\x00\x00\x00\x19\x00\x1c\x00Antarctica/DumontDUrvilleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xffԼv\x80\xff\xff\xff\xff\xde4`" + - "`\xff\xff\xff\xff\xe7<\x02\x80\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xa0\x00\x04-00\x00+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd7N\xab\x8b" + - "\x98\x00\x00\x00\x98\x00\x00\x00\x11\x00\x1c\x00Antarctica/MawsonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xe2 2\x80\x00\x00\x00\x00J\xda\"@\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00T`" + - "\x00\x04\x00\x00FP\x00\b-00\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RƉ\xf71\x84\x00\x00\x00\x84\x00\x00\x00\x12\x00\x1c\x00A" + - "ntarctica/RotheraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "C\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xd5}\xfc\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00\x00" + + "\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|," + + "\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00\x00" + + "\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00,\xa4\x95" + + "0\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x00/\xc7L\x80\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00" + + "\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0" + + "@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00" + + "\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f" + + "\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x00\x00\x00" + + "\x00V\xf6\xea@\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + + "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00N\x84\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<" + + "+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/MuscatUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99\xa8\x01" + + "\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x1c\x00A" + + "sia/BangkokUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00BMT\x00+0" + + "7\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x0f\x00\x1c\x00Asia/Ulan_BatorUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x04\x00\x00\x00\x10\xff" + + "\xff\xff\xff\x86\xd3\xeeL\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00\x00\x00\x1a\xccM\x80\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac/\x80\x00\x00\x00\x00\x1d" + + "\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"KՀ\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xb7\x80\x00" + + "\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x99\x80\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+" + + "\xb4z\x00\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002M=p\x00" + + "\x00\x00\x003=<\x80\x00\x00\x00\x004-\x1fp\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\r\x01p\x00\x00\x00\x00:鳠\x00\x00\x00\x00;\xb4\xac\x90\x00\x00\x00\x00<\xa4\xab\xa0\x00\x00\x00\x00=" + + "\x94\x8e\x90\x00\x00\x00\x00>\x84\x8d\xa0\x00\x00\x00\x00?tp\x90\x00\x00\x00\x00@do\xa0\x00\x00\x00\x00ATR\x90\x00\x00\x00\x00BDQ\xa0\x00\x00\x00\x00C44\x90\x00\x00\x00\x00D$3\xa0\x00" + + "\x00\x00\x00E\x1dQ\x10\x00\x00\x00\x00U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0\x00\x00\x00\x00W\xe5Cp\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00d4\x00\x00\x00\x00bp\x00\x04\x00\x00~\x90\x01\b\x00\x00p\x80\x00\fLMT\x00+07" + + "\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7f^]@\x01\x00\x00@\x01\x00\x00\v\x00\x1c\x00Asia/BruneiU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x05\x00\x00" + + "\x00\x18\xff\xff\xff\xff\xad\x8a\x06\x90\xff\xff\xff\xff\xbagG\x88\xff\xff\xff\xff\xbf{'\x80\xff\xff\xff\xff\xbf\xf3\x1bP\xff\xff\xff\xff\xc1]\xac\x80\xff\xff\xff\xff\xc1ՠP\xff\xff\xff\xff\xc3>\xe0\x00\xff\xff" + + "\xff\xffö\xd3\xd0\xff\xff\xff\xff\xc5 \x13\x80\xff\xff\xff\xffŘ\aP\xff\xff\xff\xff\xc7\x01G\x00\xff\xff\xff\xff\xc7y:\xd0\xff\xff\xff\xff\xc8\xe3\xcc\x00\xff\xff\xff\xff\xc9[\xbf\xd0\xff\xff\xff\xff\xca\xc4" + + "\xff\x80\xff\xff\xff\xff\xcb<\xf3P\xff\xff\xff\xffˑX\x00\xff\xff\xff\xff\xd2Hm\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x00\x00gp\x00\x00\x00\x00ix\x00\x04\x00\x00u0" + + "\x01\n\x00\x00p\x80\x00\x10\x00\x00~\x90\x00\x14LMT\x00+0730\x00+0820\x00+08\x00+09\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U\x8bSnT\xa1\x00\x00\x00\xa1\x00\x00\x00\r\x00\x1c\x00Asia/KatmanduUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf2}\x84\x00\x00\x00\x00\x1e\x180\xa8\x01\x02\x00\x00O\xfc\x00\x00\x00\x00M" + + "X\x00\x04\x00\x00P\xdc\x00\nLMT\x00+0530\x00+0545\x00\n<+0545>-5:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9e\x88|`\x9a\x03\x00" + + "\x00\x9a\x03\x00\x00\n\x00\x1c\x00Asia/AmmanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\xb6\xa3\xd6\xd0\x00\x00\x00\x00\x06ry\xe0\x00\x00\x00\x00\a\f\xabP\x00\x00\x00\x00\b$7`\x00\x00\x00\x00\b\xed" + + "\xde\xd0\x00\x00\x00\x00\n\x05j\xe0\x00\x00\x00\x00\n\xcf\x12P\x00\x00\x00\x00\v\xe7\xef\xe0\x00\x00\x00\x00\f\xdau\xd0\x00\x00\x00\x00\r\xc9#`\x00\x00\x00\x00\x0e\x92\xca\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00" + + "\x00\x00\x10r\xac\xd0\x00\x00\x00\x00\x1c\xad\xd5`\x00\x00\x00\x00\x1d\x9f\t\xd0\x00\x00\x00\x00\x1e\x92\xfd`\x00\x00\x00\x00\x1f\x82\xe0P\x00\x00\x00\x00 r\xdf`\x00\x00\x00\x00!b\xc2P\x00\x00\x00\x00\"R" + + "\xc1`\x00\x00\x00\x00#K\xde\xd0\x00\x00\x00\x00$d\xbc`\x00\x00\x00\x00%+\xc0\xd0\x00\x00\x00\x00&7o`\x00\x00\x00\x00'\v\xa2\xd0\x00\x00\x00\x00(\vs\xe0\x00\x00\x00\x00(\xe2JP\x00\x00" + + "\x00\x00)\xe4\xbe`\x00\x00\x00\x00*\xcbf\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\xabH\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00.x\xb5\xd0\x00\x00\x00\x00/\x84d`\x00\x00\x00\x000X" + + "\xa5\xe0\x00\x00\x00\x001dF`\x00\x00\x00\x002A\xc2`\x00\x00\x00\x003D(`\x00\x00\x00\x004!\xa4`\x00\x00\x00\x005$\n`\x00\x00\x00\x006\x01\x86`\x00\x00\x00\x007z\x93`\x00\x00" + + "\x00\x007\xea\xa2\xe0\x00\x00\x00\x008\xe2|\xe0\x00\x00\x00\x009ӿ`\x00\x00\x00\x00:\xc2^\xe0\x00\x00\x00\x00;\xb3\xa1`\x00\x00\x00\x00<\xa3\x92`\x00\x00\x00\x00=\x93\x83`\x00\x00\x00\x00>\x83" + + "t`\x00\x00\x00\x00?\x98O`\x00\x00\x00\x00@cV`\x00\x00\x00\x00An\xf6\xe0\x00\x00\x00\x00BLr\xe0\x00\x00\x00\x00C-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00" + + "\x00\x0f\x00\x1c\x00Asia/Phnom_PenhUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bL" + + "MT\x00BMT\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf9l\x03\x12\xf8\x02\x00\x00\xf8\x02\x00\x00\f\x00\x1c\x00Asia/Irkut" + + "skUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00" + + "\a\x00\x00\x00\x14\xff\xff\xff\xffV\xb6\x82?\xff\xff\xff\xff\xa2\x12\x0f\xbf\xff\xff\xff\xff\xb5\xa3\xd3\x10\x00\x00\x00\x00\x15'a\x80\x00\x00\x00\x00\x16\x18\x95\xf0\x00\x00\x00\x00\x17\b\x95\x00\x00\x00\x00\x00\x17\xf9\xc9" + + "p\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00\x00\x00\x1a\xccM\x80\x00\x00\x00\x00\x1b\xbcZ\xa0\x00\x00\x00\x00\x1c\xacK\xa0\x00\x00\x00\x00\x1d\x9c<\xa0\x00\x00\x00\x00\x1e\x8c-\xa0\x00\x00\x00" + + "\x00\x1f|\x1e\xa0\x00\x00\x00\x00 l\x0f\xa0\x00\x00\x00\x00!\\\x00\xa0\x00\x00\x00\x00\"K\xf1\xa0\x00\x00\x00\x00#;\xe2\xa0\x00\x00\x00\x00$+Ӡ\x00\x00\x00\x00%\x1bĠ\x00\x00\x00\x00&\v\xb5" + + "\xa0\x00\x00\x00\x00'\x04\xe1 \x00\x00\x00\x00'\xf4\xd2 \x00\x00\x00\x00(\xe4\xd10\x00\x00\x00\x00)xy0\x00\x00\x00\x00)Դ \x00\x00\x00\x00*ĥ \x00\x00\x00\x00+\xb4\x96 \x00\x00\x00" + + "\x00,\xa4\x87 \x00\x00\x00\x00-\x94x \x00\x00\x00\x00.\x84i \x00\x00\x00\x00/tZ \x00\x00\x00\x000dK \x00\x00\x00\x001]v\xa0\x00\x00\x00\x002rQ\xa0\x00\x00\x00\x003=X" + + "\xa0\x00\x00\x00\x004R3\xa0\x00\x00\x00\x005\x1d:\xa0\x00\x00\x00\x0062\x15\xa0\x00\x00\x00\x006\xfd\x1c\xa0\x00\x00\x00\x008\x1b2 \x00\x00\x00\x008\xdc\xfe\xa0\x00\x00\x00\x009\xfb\x14 \x00\x00\x00" + + "\x00:\xbc\xe0\xa0\x00\x00\x00\x00;\xda\xf6 \x00\x00\x00\x00<\xa5\xfd \x00\x00\x00\x00=\xba\xd8 \x00\x00\x00\x00>\x85\xdf \x00\x00\x00\x00?\x9a\xba \x00\x00\x00\x00@e\xc1 \x00\x00\x00\x00A\x83\xd6" + + "\xa0\x00\x00\x00\x00BE\xa3 \x00\x00\x00\x00Cc\xb8\xa0\x00\x00\x00\x00D%\x85 \x00\x00\x00\x00EC\x9a\xa0\x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00" + + "\x00I\x03^\xa0\x00\x00\x00\x00I\xcee\xa0\x00\x00\x00\x00J\xe3@\xa0\x00\x00\x00\x00K\xaeG\xa0\x00\x00\x00\x00L\xcc] \x00\x00\x00\x00M\x8e)\xa0\x00\x00\x00\x00TK\xd7\x10\x01\x02\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x04\x00" + + "\x00a\xc1\x00\x00\x00\x00a\xc1\x00\x04\x00\x00bp\x00\b\x00\x00~\x90\x01\f\x00\x00p\x80\x00\x10\x00\x00p\x80\x01\x10\x00\x00~\x90\x00\fLMT\x00IMT\x00+07\x00+09\x00+08" + + "\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/KuwaitUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b" + + "6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x81z&\x80k\x02\x00\x00k\x02\x00\x00\x0f\x00" + + "\x1c\x00Asia/ChoibalsanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\x00\x00\x00\x00\r\x02-\x00\x01\x00\x00\x00\x00\x00\x00\xff\xff\xd5\xd0\x00\x04-00\x00-03\x00\n<-03>3\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\xddzAh\xf3\x00\x00\x00\xf3\x00\x00\x00\x10\x00\x1c\x00Antarctica/CaseyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xfe\x1è\x00\x00\x00\x00J\xda" + - "\x06 \x00\x00\x00\x00K\x8f\xca\xf0\x00\x00\x00\x00N\xa9\x9c \x00\x00\x00\x00OC͐\x00\x00\x00\x00X\n;\x80\x00\x00\x00\x00Z\xa4\x0f\x10\x00\x00\x00\x00[\xb9\x14@\x00\x00\x00\x00\\\x8d\x1d\x80\x00\x00" + - "\x00\x00]\x96E0\x00\x00\x00\x00^c\xc5\x00\x00\x00\x00\x00_x\xa0<\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x9a\xb0\x00\b-00\x00+08\x00" + - "+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb2\x84J]\xd0\x03\x00\x00\xd0\x03\x00\x00\x14\x00\x1c\x00Antarctica/Macqu" + - "arieUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00" + - "\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff|\x05\x16\x00\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xa0\x87\xb4`\xff\xff\xff\xff\xd7\fh\x00\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc" + - "\xb2~\x00\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00" + - "\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n" + - "\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00" + - "\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18" + - "\xe31\x00\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00" + - "\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00'" + - ")\xaf\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00" + - "\x00\x00\x00.\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005" + - "\x1d\x1e\x80\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00" + - "\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C" + - ">\xb2\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x00\x00\x00\x00G\xf7\xa2\x00\x00\x00\x00\x00H\xe7\x93\x00\x00\x00\x00\x00Iׄ\x00\x00" + - "\x00\x00\x00J\xc7u\x00\x00\x00\x00\x00M\x97H\x00\x01\x02\x01\x00\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00" + - "\x9a\xb0\x01\t-00\x00AEST\x00AEDT\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x15\x00\x1c\x00Antarctica/South_PoleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff" + - "\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18" + - "\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff" + - "\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L" + - "\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00" + - "\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12x" + - "g\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00" + - "\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F" + - "\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00" + - "\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8d" + - "k`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00" + - "\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93" + - "O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00" + - "\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04" + - "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + - "\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT" + - "\x00\nNZST-12NZDT,M9.5.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00" + - "Arctic/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa5\x97\aĤ\x02\x00\x00\xa4\x02\x00" + - "\x00\x13\x00\x1c\x00Arctic/LongyearbyenUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffr\xee$l\xff\xff\xff\xff\x9b'\xe3\x00\xff\xff\xff\xff\x9b\xd4{`\xff\xff\xff\xffȷM`\xff" + - "\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2b\a\x10\xff\xff\xff\xff\xeb" + - "\xaf \x90\xff\xff\xff\xff\xec\xa8L\x10\xff\xff\xff\xff\xed\x98=\x10\xff\xff\xff\xff\xee\x88.\x10\xff\xff\xff\xff\xefx\x1f\x10\xff\xff\xff\xff\xf0h\x10\x10\xff\xff\xff\xff\xf1X\x01\x10\xff\xff\xff\xff\xf2G\xf2\x10\xff" + - "\xff\xff\xff\xf37\xe3\x10\xff\xff\xff\xff\xf4'\xd4\x10\xff\xff\xff\xff\xf5\x17\xc5\x10\xff\xff\xff\xff\xf6\x10\xf0\x90\xff\xff\xff\xff\xf7/\x06\x10\xff\xff\xff\xff\xf7\xf0Ґ\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x14" + - "3\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00" + - "\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"" + - "LT\x10\x00\x00\x00\x00#\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00" + - "\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\xce" + - "\x81\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x00\x00\x00\x00WI\xf8\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00O" + - "\xa7\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x81z&\x80k\x02\x00\x00k\x02\x00\x00\x0f\x00\x1c\x00Asia/ChoibalsanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xff\x86\xd3\xe7(\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00" + - "\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbc\"`\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xe6`" + - "\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xc8`\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xaa`\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00" + - "'\x04\xa8\xe0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\x8a\xe0\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94M\xf0" + - "\x00\x00\x00\x00.\x840\xe0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d\x12\xe0\x00\x00\x00\x001]Lp\x00\x00\x00\x002M/`\x00\x00\x00\x003=.p\x00\x00\x00\x004-\x11`\x00\x00\x00\x00" + - "5\x1d\x10p\x00\x00\x00\x006\f\xf3`\x00\x00\x00\x00:饐\x00\x00\x00\x00;\xb4\x9e\x80\x00\x00\x00\x00<\xa4\x9d\x90\x00\x00\x00\x00=\x94\x80\x80\x00\x00\x00\x00>\x84\u007f\x90\x00\x00\x00\x00?tb\x80" + - "\x00\x00\x00\x00@da\x90\x00\x00\x00\x00ATD\x80\x00\x00\x00\x00BDC\x90\x00\x00\x00\x00C4&\x80\x00\x00\x00\x00D$%\x90\x00\x00\x00\x00E\x1dC\x00\x00\x00\x00\x00G\xef\xaa\xf0\x00\x00\x00\x00" + - "U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0\x00\x00\x00\x00W\xe5Cp\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x02\x05\x02\x05\x02\x00\x00kX\x00\x00\x00\x00bp\x00\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\f\x00\x00\x8c\xa0\x01\x10\x00\x00~\x90\x01\fLMT\x00+" + - "07\x00+08\x00+09\x00+10\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x1c\x00Asia/T" + - "himbuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" + - "\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\xd5\xe6\x15t\x00\x00\x00\x00!aM\xa8\x01\x02\x00\x00T\f\x00\x00\x00\x00MX\x00\x04\x00\x00T`\x00\nLMT\x00+0530\x00+06\x00\n<" + - "+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0e\x00\x1c\x00Asia/VientianeUT\t\x00\x03\x15\xac\x0e`" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + - "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6" + - "\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00BMT\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9Rʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\v\x00\x1c\x00Asia/YangonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffV\xb6\x89\xd1\xff\xff\xff\xff\xa1\xf2sQ\xff\xff\xff\xff\xcb\xf2\xfc\x18\xff" + - "\xff\xff\xffњg\xf0\x01\x02\x03\x02\x00\x00Z/\x00\x00\x00\x00Z/\x00\x04\x00\x00[h\x00\b\x00\x00~\x90\x00\x0eLMT\x00RMT\x00+0630\x00+09\x00\n<+0630" + - ">-6:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R6j\\J\xcf\x04\x00\x00\xcf\x04\x00\x00\v\x00\x1c\x00Asia/HebronUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + - "\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff}\xbdJ\x19\xff" + - "\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xff\xd0" + - "\xa9\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xe86c`\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff" + - "\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xfa\xf0\xff\xff\xff\xff\xec\xb5m\x00\xff\xff\xff\xff\xed\xcf\u007f\xf0\xff\xff\xff\xff\xee\x97\xf2\x00\xff\xff\xff\xffﰳp\xff\xff\xff\xff\xf0y%\x80\xff\xff\xff\xff\xf1" + - "\x91\xe6\xf0\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3s\x1ap\xff\xff\xff\xff\xf4;\x8c\x80\xff\xff\xff\xff\xf5U\x9fp\xff\xff\xff\xff\xf6\x1e\x11\x80\xff\xff\xff\xff\xf76\xd2\xf0\xff\xff\xff\xff\xf7\xffE\x00\xff" + - "\xff\xff\xff\xf9\x18\x06p\xff\xff\xff\xff\xf9\xe1\xca\x00\xff\xff\xff\xff\xfa\xf99\xf0\xff\xff\xff\xff\xfb'BP\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n" + - "\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00" + - "\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&" + - "\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00" + - "\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x000\xe7\a\xe0\x00\x00\x00\x001dF`\x00\x00\x00\x002A\xc2`\x00\x00\x00\x003" + - "D(`\x00\x00\x00\x004!\xa4`\x00\x00\x00\x005$\n`\x00\x00\x00\x006\x01\x86`\x00\x00\x00\x007\x16a`\x00\x00\x00\x008\x06DP\x00\x00\x00\x008\xff}\xe0\x00\x00\x00\x009\xef`\xd0\x00" + - "\x00\x00\x00:\xdf_\xe0\x00\x00\x00\x00;\xcfB\xd0\x00\x00\x00\x00<\xbfA\xe0\x00\x00\x00\x00=\xaf$\xd0\x00\x00\x00\x00>\x9f#\xe0\x00\x00\x00\x00?\x8f\x06\xd0\x00\x00\x00\x00@\u007f\x05\xe0\x00\x00\x00\x00A" + - "\\\x81\xe0\x00\x00\x00\x00B^\xe7\xe0\x00\x00\x00\x00CA\xb7\xf0\x00\x00\x00\x00D-\xa6`\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F\x0e\xd9\xe0\x00\x00\x00\x00F\xe8op\x00\x00\x00\x00G\xec\x18\xe0\x00" + - "\x00\x00\x00H\xbb\x06P\x00\x00\x00\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xa0<`\x00\x00\x00\x00K\xab\xdc\xe0\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00M\x94\xf9\x9c\x00\x00\x00\x00N5\xc2P\x00\x00\x00\x00N" + - "\\\v\xe0\x00\x00\x00\x00N\x84\xdcP\x00\x00\x00\x00Ot\xdb`\x00\x00\x00\x00P[\x91\xe0\x00\x00\x00\x00QT\xbd`\x00\x00\x00\x00RD\xa0P\x00\x00\x00\x00S4\x9f`\x00\x00\x00\x00TIlP\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xff\x86\xd3\xe7(\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p" + + "\x00\x00\x00\x00\x1b\xbc\"`\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xe6`\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xc8`\x00\x00\x00\x00" + + "\"K\xc7p\x00\x00\x00\x00#;\xaa`\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xa8\xe0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\x8a\xe0" + + "\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x840\xe0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x00" + + "0d\x12\xe0\x00\x00\x00\x001]Lp\x00\x00\x00\x002M/`\x00\x00\x00\x003=.p\x00\x00\x00\x004-\x11`\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x006\f\xf3`\x00\x00\x00\x00:饐" + + "\x00\x00\x00\x00;\xb4\x9e\x80\x00\x00\x00\x00<\xa4\x9d\x90\x00\x00\x00\x00=\x94\x80\x80\x00\x00\x00\x00>\x84\x7f\x90\x00\x00\x00\x00?tb\x80\x00\x00\x00\x00@da\x90\x00\x00\x00\x00ATD\x80\x00\x00\x00\x00" + + "BDC\x90\x00\x00\x00\x00C4&\x80\x00\x00\x00\x00D$%\x90\x00\x00\x00\x00E\x1dC\x00\x00\x00\x00\x00G\xef\xaa\xf0\x00\x00\x00\x00U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0" + + "\x00\x00\x00\x00W\xe5Cp\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x02\x05\x02\x05\x02\x00" + + "\x00kX\x00\x00\x00\x00bp\x00\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\f\x00\x00\x8c\xa0\x01\x10\x00\x00~\x90\x01\fLMT\x00+07\x00+08\x00+09\x00+10\x00\n<+08" + + ">-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd5ΜGp\x02\x00\x00p\x02\x00\x00\x0e\x00\x1c\x00Asia/QyzylordaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x86\xa0\xff" + + "\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a" + + "\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00" + + "\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(" + + "\xe4\xfb`\x00\x00\x00\x00)x\x95P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00" + + "\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x006" + + "2?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00" + + "\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x00\x00\x00\x00\\\x1bؠ\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x02\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x00\x00=`\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T" + + "`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1d?v\f\x17\x03" + + "\x00\x00\x17\x03\x00\x00\n\x00\x1c\x00Asia/MacauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x85i[\x8e\xff\xff\xff\xff\xcbGu\xf0\xff\xff\xff\xff\xcb\xf2\xca\xe0\xff\xff\xff\xff\xcc\xfb\xbaP\xff\xff\xff\xff\xcd" + + "\xd3\xfe`\xff\xff\xff\xffΝ\xa5\xd0\xff\xff\xff\xff\xd2azp\xff\xff\xff\xff\xd3x\xf8p\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5K\xabp\xff\xff\xff\xff\xd6tL\xf0\xff\xff\xff\xff\xd7?S\xf0\xff" + + "\xff\xff\xff\xd8/D\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xda\r\xd5p\xff\xff\xff\xff\xda\xd8\xdcp\xff\xff\xff\xff\xdb\xed\xb7p\xff\xff\xff\xffܸ\xbep\xff\xff\xff\xff\xdd\xce\xea\xf0\xff\xff\xff\xff\xde" + + "\xa1\xda\xf0\xff\xff\xff\xff߶\xb5\xf0\xff\xff\xff\xff\xe0\x81\xbc\xf0\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe3vy\xf0\xff\xff\xff\xff\xe4/\v\xf0\xff\xff\xff\xff\xe5_\x96p\xff" + + "\xff\xff\xff\xe6\x0e\xed\xf0\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec" + + "\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff" + + "\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15S\x18\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf55\x18\xff\xff\xff\xff\xfa" + + "\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00" + + "\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t" + + "\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x03\x02\x03\x02\x03\x01\x04\x01" + + "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + + "\x04\x01\x00\x00jr\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\x10LMT\x00CST\x00+10\x00+09\x00CDT\x00\nCST-8\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vUb\xadű\xf8\x00\x00\x00\xf8\x00\x00\x00\f\x00\x1c\x00Asia/JakartaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00 \xff\xff\xff\xff?fI`\xff\xff\xff\xff\xa9x\x85\xe0" + + "\xff\xff\xff\xff\xba\x16\xde`\xff\xff\xff\xff˿\x83\x88\xff\xff\xff\xff\xd2V\xeep\xff\xff\xff\xff\xd7<\xc6\b\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xf4\xb5\xbe\x88\x01\x02\x03\x04\x03\x05\x03\x06\x00\x00d " + + "\x00\x00\x00\x00d \x00\x04\x00\x00g \x00\b\x00\x00ix\x00\x0e\x00\x00~\x90\x00\x14\x00\x00p\x80\x00\x18\x00\x00bp\x00\x1cLMT\x00BMT\x00+0720\x00+0730\x00+0" + + "9\x00+08\x00WIB\x00\nWIB-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x10\x00\x1c\x00Asia/Ulaanbaa" + + "tarUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00" + + "\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x86\xd3\xeeL\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00\x00\x00\x1a\xccM\x80\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac" + + "/\x80\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"KՀ\x00\x00\x00\x00#;\xb8p\x00\x00" + + "\x00\x00$+\xb7\x80\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x99\x80\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xc4" + + "z\xf0\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Z\x80\x00\x00" + + "\x00\x002M=p\x00\x00\x00\x003=<\x80\x00\x00\x00\x004-\x1fp\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\r\x01p\x00\x00\x00\x00:鳠\x00\x00\x00\x00;\xb4\xac\x90\x00\x00\x00\x00<\xa4" + + "\xab\xa0\x00\x00\x00\x00=\x94\x8e\x90\x00\x00\x00\x00>\x84\x8d\xa0\x00\x00\x00\x00?tp\x90\x00\x00\x00\x00@do\xa0\x00\x00\x00\x00ATR\x90\x00\x00\x00\x00BDQ\xa0\x00\x00\x00\x00C44\x90\x00\x00" + + "\x00\x00D$3\xa0\x00\x00\x00\x00E\x1dQ\x10\x00\x00\x00\x00U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0\x00\x00\x00\x00W\xe5Cp\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00d4\x00\x00\x00\x00bp\x00\x04\x00\x00~\x90\x01\b\x00\x00p\x80\x00\f" + + "LMT\x00+07\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9e\xa1=H\xd8\x04\x00\x00\xd8\x04\x00\x00\t\x00\x1c\x00Asia/" + + "GazaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00v\x00" + + "\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff}\xbdJ\xb0\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce" + + "\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xe86c`\xff" + + "\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xfa\xf0\xff\xff\xff\xff\xec\xb5m\x00\xff\xff\xff\xff\xed\xcf\x7f\xf0\xff\xff\xff\xff\xee\x97\xf2\x00\xff\xff\xff\xff\xef" + + "\xb0\xb3p\xff\xff\xff\xff\xf0y%\x80\xff\xff\xff\xff\xf1\x91\xe6\xf0\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3s\x1ap\xff\xff\xff\xff\xf4;\x8c\x80\xff\xff\xff\xff\xf5U\x9fp\xff\xff\xff\xff\xf6\x1e\x11\x80\xff" + + "\xff\xff\xff\xf76\xd2\xf0\xff\xff\xff\xff\xf7\xffE\x00\xff\xff\xff\xff\xf9\x18\x06p\xff\xff\xff\xff\xf9\xe1\xca\x00\xff\xff\xff\xff\xfa\xf99\xf0\xff\xff\xff\xff\xfb'BP\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b" + + "\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00" + + "\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$" + + "Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00" + + "\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x000\xe7\a\xe0\x00\x00\x00\x001" + + "dF`\x00\x00\x00\x002A\xc2`\x00\x00\x00\x003D(`\x00\x00\x00\x004!\xa4`\x00\x00\x00\x005$\n`\x00\x00\x00\x006\x01\x86`\x00\x00\x00\x007\x16a`\x00\x00\x00\x008\x06DP\x00" + + "\x00\x00\x008\xff}\xe0\x00\x00\x00\x009\xef`\xd0\x00\x00\x00\x00:\xdf_\xe0\x00\x00\x00\x00;\xcfB\xd0\x00\x00\x00\x00<\xbfA\xe0\x00\x00\x00\x00=\xaf$\xd0\x00\x00\x00\x00>\x9f#\xe0\x00\x00\x00\x00?" + + "\x8f\x06\xd0\x00\x00\x00\x00@\x7f\x05\xe0\x00\x00\x00\x00A\\\x81\xe0\x00\x00\x00\x00B^\xe7\xe0\x00\x00\x00\x00CA\xb7\xf0\x00\x00\x00\x00D-\xa6`\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F\x0e\xd9\xe0\x00" + + "\x00\x00\x00F\xe8op\x00\x00\x00\x00G\xec\x18\xe0\x00\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xa0<`\x00\x00\x00\x00K\xad.\x9c\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00M" + + "\x94\xf9\x9c\x00\x00\x00\x00N5\xc2P\x00\x00\x00\x00Ot\xdb`\x00\x00\x00\x00P[\x91\xe0\x00\x00\x00\x00QT\xbd`\x00\x00\x00\x00RD\xa0P\x00\x00\x00\x00S4\x9f`\x00\x00\x00\x00TIlP\x00" + "\x00\x00\x00U\x15\xd2\xe0\x00\x00\x00\x00V)\\`\x00\x00\x00\x00V\xf5\xc2\xf0\x00\x00\x00\x00X\x13\xca`\x00\x00\x00\x00Xդ\xf0\x00\x00\x00\x00Y\xf3\xac`\x00\x00\x00\x00Z\xb5\x86\xf0\x00\x00\x00\x00[" + - "ӎ`\x00\x00\x00\x00\\\x9dC\xe0\x00\x00\x00\x00]\xb3bP\x00\x00\x00\x00^~w`\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00 \xe7\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x11LMT\x00EE" + - "ST\x00EET\x00IDT\x00IST\x00\nEET-2EEST,M3.4.4/48,M10.4.4/49\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x1c\x00Asia/ChongqingUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + + "ӎ`\x00\x00\x00\x00\\\x9dC\xe0\x00\x00\x00\x00]\xb3bP\x00\x00\x00\x00^~w`\x00\x00\x00\x00_\x93R`\x00\x00\x00\x00`^Y`\x00\x00\x00\x00a{\x1d`\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00 P\x00\x00\x00\x00*0\x01" + + "\x04\x00\x00\x1c \x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x11LMT\x00EEST\x00EET\x00IDT\x00IST\x00\nEET-2EEST,M3.4.4/72" + + ",M10.4.4/25\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf0\x9cf>\xd7\x02\x00\x00\xd7\x02\x00\x00\x0e\x00\x1c\x00Asia/KamchatkaUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x05\x00\x00\x00\x10\xff" + + "\xff\xff\xff\xa7R\x96\xc4\xff\xff\xff\xff\xb5\xa3\x9a\xd0\x00\x00\x00\x00\x15')@\x00\x00\x00\x00\x16\x18]\xb0\x00\x00\x00\x00\x17\b\\\xc0\x00\x00\x00\x00\x17\xf9\x910\x00\x00\x00\x00\x18\xe9\x90@\x00\x00\x00\x00\x19" + + "\xdaİ\x00\x00\x00\x00\x1a\xcc\x15@\x00\x00\x00\x00\x1b\xbc\"`\x00\x00\x00\x00\x1c\xac\x13`\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8b\xf5`\x00\x00\x00\x00\x1f{\xe6`\x00\x00\x00\x00 k\xd7`\x00" + + "\x00\x00\x00![\xc8`\x00\x00\x00\x00\"K\xb9`\x00\x00\x00\x00#;\xaa`\x00\x00\x00\x00$+\x9b`\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v}`\x00\x00\x00\x00'\x04\xa8\xe0\x00\x00\x00\x00'" + + "\xf4\x99\xe0\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00)x@\xf0\x00\x00\x00\x00)\xd4{\xe0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4]\xe0\x00\x00\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94?\xe0\x00" + + "\x00\x00\x00.\x840\xe0\x00\x00\x00\x00/t!\xe0\x00\x00\x00\x000d\x12\xe0\x00\x00\x00\x001]>`\x00\x00\x00\x002r\x19`\x00\x00\x00\x003= `\x00\x00\x00\x004Q\xfb`\x00\x00\x00\x005" + + "\x1d\x02`\x00\x00\x00\x0061\xdd`\x00\x00\x00\x006\xfc\xe4`\x00\x00\x00\x008\x1a\xf9\xe0\x00\x00\x00\x008\xdc\xc6`\x00\x00\x00\x009\xfa\xdb\xe0\x00\x00\x00\x00:\xbc\xa8`\x00\x00\x00\x00;ڽ\xe0\x00" + + "\x00\x00\x00<\xa5\xc4\xe0\x00\x00\x00\x00=\xba\x9f\xe0\x00\x00\x00\x00>\x85\xa6\xe0\x00\x00\x00\x00?\x9a\x81\xe0\x00\x00\x00\x00@e\x88\xe0\x00\x00\x00\x00A\x83\x9e`\x00\x00\x00\x00BEj\xe0\x00\x00\x00\x00C" + + "c\x80`\x00\x00\x00\x00D%L\xe0\x00\x00\x00\x00ECb`\x00\x00\x00\x00F\x05.\xe0\x00\x00\x00\x00G#D`\x00\x00\x00\x00G\xeeK`\x00\x00\x00\x00I\x03&`\x00\x00\x00\x00I\xce-`\x00" + + "\x00\x00\x00J\xe3\b`\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x00\x94\xbc\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00\xb6\xd0\x01\b\x00\x00\xa8\xc0\x00\f\x00" + + "\x00\xa8\xc0\x01\fLMT\x00+11\x00+13\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\x0e\x00\x1c" + + "\x00Asia/Hong_KongUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff\x85ic\x90\xff\xff\xff\xff\xcaM10\xff\xff\xff\xff\xcaۓ0\xff\xff\xff\xff\xcbKqx\xff\xff\xff\xffҠސ\xff\xff" + + "\xff\xff\xd3k׀\xff\xff\xff\xffԓX\xb8\xff\xff\xff\xff\xd5B\xb08\xff\xff\xff\xff\xd6s:\xb8\xff\xff\xff\xff\xd7>A\xb8\xff\xff\xff\xff\xd8.2\xb8\xff\xff\xff\xff\xd8\xf99\xb8\xff\xff\xff\xff\xda\x0e" + + "\x14\xb8\xff\xff\xff\xff\xda\xd9\x1b\xb8\xff\xff\xff\xff\xdb\xed\xf6\xb8\xff\xff\xff\xffܸ\xfd\xb8\xff\xff\xff\xff\xdd\xcdظ\xff\xff\xff\xffޢ\x1a8\xff\xff\xff\xff߶\xf58\xff\xff\xff\xff\xe0\x81\xfc8\xff\xff" + + "\xff\xff\xe1\x96\xc9(\xff\xff\xff\xff\xe2Oi8\xff\xff\xff\xff\xe3v\xab(\xff\xff\xff\xff\xe4/K8\xff\xff\xff\xff\xe5_Ǩ\xff\xff\xff\xff\xe6\x0f-8\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8" + + "I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff" + + "\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G" + + "\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15a(\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf5C(\xff\xff\xff\xff\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff" + + "\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M" + + "\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00" + + "\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x02\x03\x04\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00k\n\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x01\b\x00\x00w" + + "\x88\x01\r\x00\x00~\x90\x00\x12LMT\x00HKT\x00HKST\x00HKWT\x00JST\x00\nHKT-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1d?v\f\x17\x03\x00\x00" + + "\x17\x03\x00\x00\n\x00\x1c\x00Asia/MacaoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x85i[\x8e\xff\xff\xff\xff\xcbGu\xf0\xff\xff\xff\xff\xcb\xf2\xca\xe0\xff\xff\xff\xff\xcc\xfb\xbaP\xff\xff\xff\xff\xcd\xd3\xfe" + + "`\xff\xff\xff\xffΝ\xa5\xd0\xff\xff\xff\xff\xd2azp\xff\xff\xff\xff\xd3x\xf8p\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5K\xabp\xff\xff\xff\xff\xd6tL\xf0\xff\xff\xff\xff\xd7?S\xf0\xff\xff\xff" + + "\xff\xd8/D\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xda\r\xd5p\xff\xff\xff\xff\xda\xd8\xdcp\xff\xff\xff\xff\xdb\xed\xb7p\xff\xff\xff\xffܸ\xbep\xff\xff\xff\xff\xdd\xce\xea\xf0\xff\xff\xff\xffޡ\xda" + + "\xf0\xff\xff\xff\xff߶\xb5\xf0\xff\xff\xff\xff\xe0\x81\xbc\xf0\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe3vy\xf0\xff\xff\xff\xff\xe4/\v\xf0\xff\xff\xff\xff\xe5_\x96p\xff\xff\xff" + + "\xff\xe6\x0e\xed\xf0\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO" + + "\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff" + + "\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15S\x18\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf55\x18\xff\xff\xff\xff\xfa\xe5B" + + "8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00" + + "\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8" + + "(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x03\x02\x03\x02\x03\x01\x04\x01\x04\x01" + + "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + + "\x00\x00jr\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\x10LMT\x00CST\x00+10\x00+09\x00CDT\x00\nCST-8\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\xc7\x11\xe1[\xdc\x02\x00\x00\xdc\x02\x00\x00\v\x00\x1c\x00Asia/BeirutUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6¸\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff" + + "\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\x7f\xd0\xff\xff\xff\xff\xa8)\xf3\xe0\xff\xff\xff\xff\xa8\xeb\xb2P\xff\xff\xff\xff\xe8*\x85" + + "\xe0\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xec\xe0\xff\xff\xff\xff추P\xff\xff\xff\xff\xed\xcfq\xe0\xff\xff\xff\xff\xee\x99\x19P\xff\xff\xff" + + "\xffﰥ`\xff\xff\xff\xff\xf0zL\xd0\x00\x00\x00\x00\x04\xa6^`\x00\x00\x00\x00\x05+w\xd0\x00\x00\x00\x00\x06C\x03\xe0\x00\x00\x00\x00\a\f\xabP\x00\x00\x00\x00\b$7`\x00\x00\x00\x00\b\xed\xde" + + "\xd0\x00\x00\x00\x00\n\x05j\xe0\x00\x00\x00\x00\n\xcf\x12P\x00\x00\x00\x00\v\xe7\xef\xe0\x00\x00\x00\x00\f\xb1\x97P\x00\x00\x00\x00\r\xc9#`\x00\x00\x00\x00\x0e\x92\xca\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00" + + "\x00\x10r\xac\xd0\x00\x00\x00\x00\x1a\xf4.\xe0\x00\x00\x00\x00\x1bќ\xd0\x00\x00\x00\x00\x1c\xd5b`\x00\x00\x00\x00\x1d\xb2\xd0P\x00\x00\x00\x00\x1e\xb6\x95\xe0\x00\x00\x00\x00\x1f\x94\x03\xd0\x00\x00\x00\x00 \x97\xc9" + + "`\x00\x00\x00\x00!u7P\x00\x00\x00\x00\"\xa3,\xe0\x00\x00\x00\x00#W\xbcP\x00\x00\x00\x00$g_`\x00\x00\x00\x00%8\xef\xd0\x00\x00\x00\x00&<\xb5`\x00\x00\x00\x00'\x1a#P\x00\x00\x00" + + "\x00(\x1d\xe8\xe0\x00\x00\x00\x00(\xfbV\xd0\x00\x00\x00\x00*\x00m\xe0\x00\x00\x00\x00*\xce\t\xd0\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93" + + "P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002M\x91\xd0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004-s\xd0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00" + + "\x006\rU\xd0\x00\x00\x00\x006\xfdT\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00!H\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2EEST,M" + + "3.5.0/0,M10.5.0/0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x83g\x95M\a\x03\x00\x00\a\x03\x00\x00\r\x00\x1c\x00Asia/Khandyg" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\b" + + "\x00\x00\x00\x14\xff\xff\xff\xff\xa1\xdb\xe4\xeb\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18\xe9\xbap" + + "\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00\x00\x00\x00" + + " l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xd3\x10" + + "\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00\x00\x00\x00" + + "-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10\x00\x00\x00\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00\x00\x003=J\x90\x00\x00\x00\x004R%\x90" + + "\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x008\x1b$\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb\x06\x10\x00\x00\x00\x00:\xbcҐ\x00\x00\x00\x00" + + ";\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10\x00\x00\x00\x00?\x9a\xac\x10\x00\x00\x00\x00?\xf2\xe4p\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80" + + "\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00\x00" + + "I\x03B\x80\x00\x00\x00\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00\x00\x00Nn\x02P\x00\x00\x00\x00TK\xc9\x00" + + "\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\x05\x06\a\x06\x03\x00\x00\x7f\x15\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x9a\xb0\x01\x10\x00\x00\x8c\xa0\x00\b\x00\x00\x9a\xb0\x00\x10LMT\x00+" + + "08\x00+10\x00+09\x00+11\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUd%\x05\xd8\xe6\x02\x00\x00\xe6\x02\x00\x00\x10\x00\x1c\x00Asia/V" + + "ladivostokUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa7YG]\xff\xff\xff\xff\xb5\xa3\xb6\xf0\x00\x00\x00\x00\x15'E`\x00\x00\x00\x00\x16\x18y\xd0\x00\x00\x00\x00\x17\bx\xe0\x00\x00\x00\x00\x17\xf9\xad" + + "P\x00\x00\x00\x00\x18\xe9\xac`\x00\x00\x00\x00\x19\xda\xe0\xd0\x00\x00\x00\x00\x1a\xcc1`\x00\x00\x00\x00\x1b\xbc>\x80\x00\x00\x00\x00\x1c\xac/\x80\x00\x00\x00\x00\x1d\x9c \x80\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00" + + "\x00\x1f|\x02\x80\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xe4\x80\x00\x00\x00\x00\"KՀ\x00\x00\x00\x00#;ƀ\x00\x00\x00\x00$+\xb7\x80\x00\x00\x00\x00%\x1b\xa8\x80\x00\x00\x00\x00&\v\x99" + + "\x80\x00\x00\x00\x00'\x04\xc5\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\xb5\x10\x00\x00\x00\x00)x]\x10\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*ĉ\x00\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00" + + "\x00,\xa4k\x00\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84M\x00\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000d/\x00\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<" + + "\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xfa\xf8\x00\x00\x00\x00" + + "\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba" + + "\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00" + + "\x00I\x03B\x80\x00\x00\x00\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00\x00\x00TK\xba\xf0\x01\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00" + + "{\xa3\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x9a\xb0\x01\b\x00\x00\x8c\xa0\x00\f\x00\x00\x8c\xa0\x01\f\x00\x00\x9a\xb0\x00\bLMT\x00+09\x00+11\x00+10\x00\n<+10>-10\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\r\x00\x1c\x00Asia/ShanghaiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97" + + "\xa2\x80\xff\xff\xff\xff\xa1y\x04\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff" + + "\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A" + + "|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00" + + "\x00\x00%\x12|\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\aW" + + "\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\r\x00\x1c\x00Asia/IstanbulUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s\x00\x00\x00\x06\x00\x00\x00\x19\xff\xff\xff\xffV\xb6\xc8\xd8\xff\xff\xff\xff\x90\x8b\xf5\x98\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5" + + "\xbe\xd0\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\x7f\xd0\xff\xff\xff\xff\xaa((`\xff\xff" + + "\xff\xff\xaa\xe1\xfd\xd0\xff\xff\xff\xff\xab\xf9\x89\xe0\xff\xff\xff\xff\xac\xc31P\xff\xff\xff\xffȁ?\xe0\xff\xff\xff\xff\xc9\x01\x13P\xff\xff\xff\xff\xc9J\xf5`\xff\xff\xff\xff\xca\u0380P\xff\xff\xff\xff\xcb\xcb" + + "\xae`\xff\xff\xff\xff\xd2k\tP\xff\xff\xff\xffӢ9`\xff\xff\xff\xff\xd4C\x02P\xff\xff\xff\xff\xd5L\r\xe0\xff\xff\xff\xff\xd6){\xd0\xff\xff\xff\xff\xd7+\xef\xe0\xff\xff\xff\xff\xd8\t]\xd0\xff\xff" + + "\xff\xff\xd9\x02\x97`\xff\xff\xff\xff\xd9\xe9?\xd0\xff\xff\xff\xff\xda\xeb\xb3\xe0\xff\xff\xff\xff\xdb\xd2\\P\xff\xff\xff\xff\xdc\xd4\xd0`\xff\xff\xff\xffݲ>P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b" + + "\xefP\xff\xff\xff\xff\xf5h\x06`\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00" + + "\x00\x00\n\xf9^p\x00\x00\x00\x00\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89" + + "\xb0p\x00\x00\x00\x00\x19ܰ\xe0\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6\xef\xf0\x00\x00\x00\x00\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00" + + "\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5" + + "\x18p\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00" + + "\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062" + + "[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%" + + "\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00" + + "\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rl" + + "e\x90\x00\x00\x00\x00S8\xbe\x10\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*" + + "0\x01\b\x00\x00\x1c \x00\r\x00\x00*0\x00\x11\x00\x008@\x01\x15LMT\x00IMT\x00EEST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vUS\xa5\x81e\xf7\x00\x00\x00\xf7\x00\x00\x00\x0e\x00\x1c\x00Asia/PontianakUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x1f\xff\xff\xff\xff\x8b\xff\x8e\x00\xff\xff\xff\xff\xba\x16\xdf\x00\xff\xff" + + "\xff\xff\xcby\xa4\b\xff\xff\xff\xff\xd2V\xeep\xff\xff\xff\xff\xd7<\xc6\b\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xf4\xb5\xbe\x88\x00\x00\x00\x00!\xdat\x80\x01\x02\x03\x02\x04\x02\x05\x06\x00\x00f\x80\x00\x00" + + "\x00\x00f\x80\x00\x04\x00\x00ix\x00\b\x00\x00~\x90\x00\x0e\x00\x00p\x80\x00\x12\x00\x00p\x80\x00\x16\x00\x00bp\x00\x1bLMT\x00PMT\x00+0730\x00+09\x00+08\x00WI" + + "TA\x00WIB\x00\nWIB-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd7e&uv\x02\x00\x00v\x02\x00\x00\f\x00\x1c\x00Asia/BaghdadUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x04\x00\x00\x00\x10\xff" + + "\xff\xff\xffi\x86\xb1\xdc\xff\xff\xff\xff\x9e0<\xe0\x00\x00\x00\x00\x170hP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xe8\xbdP\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b" + + "\xbd\xc8@\x00\x00\x00\x00\x1c\xad\xc7P\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00" + + "\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf6x\x00\x00\x00\x00\x00(纀\x00\x00\x00\x00)" + + "\xd8\xfd\x00\x00\x00\x00\x00*\xca?\x80\x00\x00\x00\x00+\xba0\x80\x00\x00\x00\x00,\xabs\x00\x00\x00\x00\x00-\x9bd\x00\x00\x00\x00\x00.\x8c\xa6\x80\x00\x00\x00\x00/|\x97\x80\x00\x00\x00\x000m\xda\x00\x00" + + "\x00\x00\x001_\x1c\x80\x00\x00\x00\x002P_\x00\x00\x00\x00\x003@P\x00\x00\x00\x00\x0041\x92\x80\x00\x00\x00\x005!\x83\x80\x00\x00\x00\x006\x12\xc6\x00\x00\x00\x00\x007\x02\xb7\x00\x00\x00\x00\x007" + + "\xf3\xf9\x80\x00\x00\x00\x008\xe5<\x00\x00\x00\x00\x009\xd6~\x80\x00\x00\x00\x00:\xc6o\x80\x00\x00\x00\x00;\xb7\xb2\x00\x00\x00\x00\x00<\xa7\xa3\x00\x00\x00\x00\x00=\x98\xe5\x80\x00\x00\x00\x00>\x88ր\x00" + + "\x00\x00\x00?z\x19\x00\x00\x00\x00\x00@k[\x80\x00\x00\x00\x00A\\\x9e\x00\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00C=р\x00\x00\x00\x00D-\u0080\x00\x00\x00\x00E\x1f\x05\x00\x00\x00\x00\x00F" + + "\x0e\xf6\x00\x00\x00\x00\x00G\x008\x80\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x00\x00)\xa4\x00\x00\x00\x00)\xa0\x00\x04\x00\x00*0\x00\b\x00\x008@\x01\fLMT\x00BMT\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUV\xe0\xe7!\xe7\x02\x00\x00\xe7\x02\x00\x00\v\x00\x1c\x00Asia/AnadyrUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x1d\x9c\xff\xff\xff\xff\xb5\xa3\x8c\xc0\x00\x00\x00\x00\x15'" + + "\x1b0\x00\x00\x00\x00\x16\x18O\xa0\x00\x00\x00\x00\x17\bN\xb0\x00\x00\x00\x00\x17\xf9\x910\x00\x00\x00\x00\x18\xe9\x90@\x00\x00\x00\x00\x19\xdaİ\x00\x00\x00\x00\x1a\xcc\x15@\x00\x00\x00\x00\x1b\xbc\"`\x00\x00" + + "\x00\x00\x1c\xac\x13`\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8b\xf5`\x00\x00\x00\x00\x1f{\xe6`\x00\x00\x00\x00 k\xd7`\x00\x00\x00\x00![\xc8`\x00\x00\x00\x00\"K\xb9`\x00\x00\x00\x00#;" + + "\xaa`\x00\x00\x00\x00$+\x9b`\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v}`\x00\x00\x00\x00'\x04\xa8\xe0\x00\x00\x00\x00'\xf4\x99\xe0\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00)x@\xf0\x00\x00" + + "\x00\x00)\xd4{\xe0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4]\xe0\x00\x00\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94?\xe0\x00\x00\x00\x00.\x840\xe0\x00\x00\x00\x00/t!\xe0\x00\x00\x00\x000d" + + "\x12\xe0\x00\x00\x00\x001]>`\x00\x00\x00\x002r\x19`\x00\x00\x00\x003= `\x00\x00\x00\x004Q\xfb`\x00\x00\x00\x005\x1d\x02`\x00\x00\x00\x0061\xdd`\x00\x00\x00\x006\xfc\xe4`\x00\x00" + + "\x00\x008\x1a\xf9\xe0\x00\x00\x00\x008\xdc\xc6`\x00\x00\x00\x009\xfa\xdb\xe0\x00\x00\x00\x00:\xbc\xa8`\x00\x00\x00\x00;ڽ\xe0\x00\x00\x00\x00<\xa5\xc4\xe0\x00\x00\x00\x00=\xba\x9f\xe0\x00\x00\x00\x00>\x85" + + "\xa6\xe0\x00\x00\x00\x00?\x9a\x81\xe0\x00\x00\x00\x00@e\x88\xe0\x00\x00\x00\x00A\x83\x9e`\x00\x00\x00\x00BEj\xe0\x00\x00\x00\x00Cc\x80`\x00\x00\x00\x00D%L\xe0\x00\x00\x00\x00ECb`\x00\x00" + + "\x00\x00F\x05.\xe0\x00\x00\x00\x00G#D`\x00\x00\x00\x00G\xeeK`\x00\x00\x00\x00I\x03&`\x00\x00\x00\x00I\xce-`\x00\x00\x00\x00J\xe3\b`\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L\xcc" + + "2\xf0\x00\x00\x00\x00M\x8d\xffp\x01\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x05\x06\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + + "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x05\x06\x01\x00\x00\xa6d\x00\x00\x00\x00\xa8\xc0\x00\x04\x00\x00\xc4\xe0\x01\b\x00\x00\xb6\xd0\x00\f\x00\x00\xb6\xd0\x01\f\x00\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\x10LMT\x00" + + "+12\x00+14\x00+13\x00+11\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8a\x9a\x90\xf7\xd6\x02\x00\x00\xd6\x02\x00\x00\x11\x00\x1c\x00Asia" + + "/NovokuznetskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x18 \xc0\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00" + + "\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0" + + "\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00" + + "&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40" + + "\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x000dY0\x00\x00\x00\x001]\x84\xb0\x00\x00\x00\x002r_\xb0\x00\x00\x00\x00" + + "3=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dH\xb0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd*\xb0\x00\x00\x00\x008\x1b@0\x00\x00\x00\x008\xdd\f\xb0\x00\x00\x00\x009\xfb\"0" + + "\x00\x00\x00\x00:\xbc\xee\xb0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\v0\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xed0\x00\x00\x00\x00?\x9a\xc80\x00\x00\x00\x00@e\xcf0\x00\x00\x00\x00" + + "A\x83\xe4\xb0\x00\x00\x00\x00BE\xb10\x00\x00\x00\x00Ccư\x00\x00\x00\x00D%\x930\x00\x00\x00\x00EC\xa8\xb0\x00\x00\x00\x00F\x05u0\x00\x00\x00\x00G#\x8a\xb0\x00\x00\x00\x00G\ue470" + + "\x00\x00\x00\x00I\x03l\xb0\x00\x00\x00\x00I\xces\xb0\x00\x00\x00\x00J\xe3N\xb0\x00\x00\x00\x00K\xaeU\xb0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x00Q\xc0\x00\x00\x00\x00" + + "T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "Uw\rD\an\x01\x00\x00n\x01\x00\x00\x0e\x00\x1c\x00Asia/SamarkandUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00T" + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff\xff\xa1y\x04\xf0\xff\xff" + - "\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B" + - "\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00" + - "\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00&'" + - "e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00" + - "\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R'\xe2\\\xff\x9f\x00\x00\x00\x9f\x00\x00\x00\n\x00" + - "\x1c\x00Asia/KabulUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x857\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00" + + "\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xac" + + "u\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00" + + "\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xedP\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00>\xc9\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\fLMT\x00+04\x00+05\x00+06\x00\n<+0" + + "5>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU's\x96\x1en\x01\x00\x00n\x01\x00\x00\r\x00\x1c\x00Asia/DushanbeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x83\x80\xff" + + "\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a" + + "\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00" + + "\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(" + + "ʏP\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x00\x00@\x80\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT" + + "\x00+05\x00+07\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUO\xb0\x03\xe9\xe5\x02\x00\x00\xe5\x02\x00\x00\f\x00\x1c\x00Asia/Yak" + + "utskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00" + + "\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\xea^\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18" + + "\xe9\xbap\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00" + + "\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'" + + "\x04\xd3\x10\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00" + + "\x00\x00\x00-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10\x00\x00\x00\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00\x00\x003=J\x90\x00\x00\x00\x004" + + "R%\x90\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x008\x1b$\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb\x06\x10\x00\x00\x00\x00:\xbcҐ\x00" + + "\x00\x00\x00;\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10\x00\x00\x00\x00?\x9a\xac\x10\x00\x00\x00\x00@e\xb3\x10\x00\x00\x00\x00A\x83Ȑ\x00\x00\x00\x00B" + + "E\x95\x10\x00\x00\x00\x00Cc\xaa\x90\x00\x00\x00\x00D%w\x10\x00\x00\x00\x00EC\x8c\x90\x00\x00\x00\x00F\x05Y\x10\x00\x00\x00\x00G#n\x90\x00\x00\x00\x00G\xeeu\x90\x00\x00\x00\x00I\x03P\x90\x00" + + "\x00\x00\x00I\xceW\x90\x00\x00\x00\x00J\xe32\x90\x00\x00\x00\x00K\xae9\x90\x00\x00\x00\x00L\xccO\x10\x00\x00\x00\x00M\x8e\x1b\x90\x00\x00\x00\x00TK\xc9\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00y\xa2\x00\x00\x00\x00" + + "p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x8c\xa0\x00\bLMT\x00+08\x00+10\x00+09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\n\x00\x1c\x00Asia/DubaiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99\xa8\x01\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT" + + "\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\v\x00\x1c\x00Asia/HarbinUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff" + + "\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff\xff\xa1y\x04\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|" + + "@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff" + + "\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)" + + "_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vUʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\v\x00\x1c\x00Asia/YangonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffV\xb6\x89\xd1\xff\xff\xff\xff\xa1\xf2sQ\xff\xff\xff\xff" + + "\xcb\xf2\xfc\x18\xff\xff\xff\xffњg\xf0\x01\x02\x03\x02\x00\x00Z/\x00\x00\x00\x00Z/\x00\x04\x00\x00[h\x00\b\x00\x00~\x90\x00\x0eLMT\x00RMT\x00+0630\x00+09\x00\n<" + + "+0630>-6:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xba\xa3b\xc1R\x02\x00\x00R\x02\x00\x00\t\x00\x1c\x00Asia/HovdUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x86\xd3" + + "\xfc\x94\x00\x00\x00\x00\x0f\v\xea\xa0\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbc>\x80\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c \x80\x00\x00" + + "\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x02\x80\x00\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xe4\x80\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;ƀ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b" + + "\xa8\x80\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xc5\x00\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ĉ\x00\x00\x00\x00\x00+\xb4\x88\x10\x00\x00" + + "\x00\x00,\xa4k\x00\x00\x00\x00\x00-\x94j\x10\x00\x00\x00\x00.\x84M\x00\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d/\x00\x00\x00\x00\x001]h\x90\x00\x00\x00\x002MK\x80\x00\x00\x00\x003=" + + "J\x90\x00\x00\x00\x004--\x80\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x006\r\x0f\x80\x00\x00\x00\x00:\xe9\xc1\xb0\x00\x00\x00\x00;\xb4\xba\xa0\x00\x00\x00\x00<\xa4\xb9\xb0\x00\x00\x00\x00=\x94\x9c\xa0\x00\x00" + + "\x00\x00>\x84\x9b\xb0\x00\x00\x00\x00?t~\xa0\x00\x00\x00\x00@d}\xb0\x00\x00\x00\x00AT`\xa0\x00\x00\x00\x00BD_\xb0\x00\x00\x00\x00C4B\xa0\x00\x00\x00\x00D$A\xb0\x00\x00\x00\x00E\x1d" + + "_ \x00\x00\x00\x00U\x15\xa8\xb0\x00\x00\x00\x00V\x05o\x80\x00\x00\x00\x00V\xf5\x8a\xb0\x00\x00\x00\x00W\xe5Q\x80\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00U\xec\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\fLMT\x00+06\x00+08\x00" + + "+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\r\x00\x1c\x00Asia/CalcuttaUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x16\xff" + + "\xff\xff\xff&\xba\x18(\xff\xff\xff\xffC\xe7\xeb0\xff\xff\xff\xff\x87\x9d\xbc\xba\xff\xff\xff\xff\xcaی(\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\x01\x02\x03\x04\x03" + + "\x04\x03\x00\x00R\xd8\x00\x00\x00\x00R\xd0\x00\x04\x00\x00KF\x00\b\x00\x00MX\x00\f\x00\x00[h\x01\x10LMT\x00HMT\x00MMT\x00IST\x00+0630\x00\nIST-5" + + ":30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8c\xdb?\xec,\x03\x00\x00,\x03\x00\x00\v\x00\x1c\x00Asia/TehranUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xff\x9al}\xc8\xff\xff\xff\xff" + + "\xbf\x00\xccH\x00\x00\x00\x00\r\x94D8\x00\x00\x00\x00\x0e\xad\x13\xb8\x00\x00\x00\x00\x0fys@\x00\x00\x00\x00\x10(\xca\xc0\x00\x00\x00\x00\x10\xed:@\x00\x00\x00\x00\x11\xad\xbcH\x00\x00\x00\x00\x12EJ\xb8" + + "\x00\x00\x00\x00\x137\xec\xc8\x00\x00\x00\x00\x14-\x15\xb8\x00\x00\x00\x00( v\xc8\x00\x00\x00\x00(\u06dd\xb8\x00\x00\x00\x00)˜\xc8\x00\x00\x00\x00*\xbe\"\xb8\x00\x00\x00\x00+\xac\xd0H\x00\x00\x00\x00" + + ",\x9fV8\x00\x00\x00\x00-\x8e\x03\xc8\x00\x00\x00\x00.\x80\x89\xb8\x00\x00\x00\x00/o7H\x00\x00\x00\x000a\xbd8\x00\x00\x00\x001Pj\xc8\x00\x00\x00\x002B\xf0\xb8\x00\x00\x00\x0032\xef\xc8" + + "\x00\x00\x00\x004%u\xb8\x00\x00\x00\x005\x14#H\x00\x00\x00\x006\x06\xa98\x00\x00\x00\x006\xf5V\xc8\x00\x00\x00\x007\xe7ܸ\x00\x00\x00\x008֊H\x00\x00\x00\x009\xc9\x108\x00\x00\x00\x00" + + ":\xb9\x0fH\x00\x00\x00\x00;\xab\x958\x00\x00\x00\x00<\x9aB\xc8\x00\x00\x00\x00=\x8cȸ\x00\x00\x00\x00>{vH\x00\x00\x00\x00?m\xfc8\x00\x00\x00\x00@\\\xa9\xc8\x00\x00\x00\x00AO/\xb8" + + "\x00\x00\x00\x00B?.\xc8\x00\x00\x00\x00C1\xb4\xb8\x00\x00\x00\x00G\xe2\xc9H\x00\x00\x00\x00H\xd5O8\x00\x00\x00\x00I\xc5NH\x00\x00\x00\x00J\xb7\xd48\x00\x00\x00\x00K\xa6\x81\xc8\x00\x00\x00\x00" + + "L\x99\a\xb8\x00\x00\x00\x00M\x87\xb5H\x00\x00\x00\x00Nz;8\x00\x00\x00\x00Oh\xe8\xc8\x00\x00\x00\x00P[n\xb8\x00\x00\x00\x00QKm\xc8\x00\x00\x00\x00R=\xf3\xb8\x00\x00\x00\x00S,\xa1H" + + "\x00\x00\x00\x00T\x1f'8\x00\x00\x00\x00U\r\xd4\xc8\x00\x00\x00\x00V\x00Z\xb8\x00\x00\x00\x00V\xef\bH\x00\x00\x00\x00W\xe1\x8e8\x00\x00\x00\x00XэH\x00\x00\x00\x00Y\xc4\x138\x00\x00\x00\x00" + + "Z\xb2\xc0\xc8\x00\x00\x00\x00[\xa5F\xb8\x00\x00\x00\x00\\\x93\xf4H\x00\x00\x00\x00]\x86z8\x00\x00\x00\x00^u'\xc8\x00\x00\x00\x00_g\xad\xb8\x00\x00\x00\x00`W\xac\xc8\x00\x00\x00\x00aJ2\xb8" + + "\x00\x00\x00\x00b8\xe0H\x00\x00\x00\x00c+f8\x01\x03\x02\x05\x04\x05\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x0008\x00\x00\x00\x0008\x00\x04\x00\x00?H\x01\b\x00\x0018\x00\x0e\x00\x00FP\x01\x14\x00\x008" + + "@\x00\x18LMT\x00TMT\x00+0430\x00+0330\x00+05\x00+04\x00\n<+0330>-3:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa1" + + "\xfax\x98g\x02\x00\x00g\x02\x00\x00\r\x00\x1c\x00Asia/QostanayUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x88\\\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16" + + "\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00" + + "\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$" + + "+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00" + + "\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001" + + "]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00" + + "\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?" + + "\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x00\x00;\xa4\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+0" + + "6\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17✳2\x04\x00\x002\x04\x00\x00\r\x00\x1c\x00Asia/Tel_AvivUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff" + + "\xffV\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f" + + "\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff" + + "\xff\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10\xf7\x00\xff\xff\xff\xff\xda\xeb\xd0\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d" + + "\x00\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff\xff\xff\xe1V}\x00\xff\xff\xff\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff" + + "\xff\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff\xe7\x11Ҁ\xff\xff\xff\xff\xe8&\xad\x80\xff\xff\xff\xff\xe8\xe8z\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea" + + "`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00" + + "\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?" + + "P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00" + + "\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4" + + "`\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp\x00\x00\x00\x00D,q\x00\x00\x00\x00\x00E\x1e\xf6\xf0\x00\x00\x00\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00" + + "\x00H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00J\xbe\x9c\xf0\x00\x00\x00\x00K\xab\xf9\x00\x00\x00\x00\x00L\x8c\t\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7" + + "\x80\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!" + + "\x06\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\f\x00\x008@\x01\x10LMT\x00JMT\x00IDT\x00IST\x00IDDT\x00\nIST-2IDT,M3" + + ".4.4/26,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe4_P\x18\xef\x02\x00\x00\xef\x02\x00\x00\f\x00\x1c\x00Asia/MagadanUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x06\x00\x00\x00" + + "\x10\xff\xff\xff\xff\xaa\x196\xa0\xff\xff\xff\xff\xb5\xa3\xa8\xe0\x00\x00\x00\x00\x15'7P\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00\x00\x00" + + "\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xe5" + + "p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00" + + "\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94M" + + "\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\tp\x00\x00\x00" + + "\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\a\xf0\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xcb" + + "\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x8f\xf0\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00BEx\xf0\x00\x00\x00" + + "\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p\x00\x00\x00\x00I\xce;" + + "p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x00\x00\x00\x00TK\xac\xe0\x00\x00\x00\x00W\x1b\x9c\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x01\x03\x00\x00\x8d`\x00" + + "\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00\x9a\xb0\x01\f\x00\x00\xa8\xc0\x00\bLMT\x00+10\x00+12\x00+11\x00\n<+11>-11\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vUT\x81\x18G^\x02\x00\x00^\x02\x00\x00\n\x00\x1c\x00Asia/AqtauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x94\xe0\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00" + + "\x16\x18\xce0\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0" + + "\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00" + + "$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP" + + "\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000d\x83`\x00\x00\x00\x00" + + "1]\xae\xe0\x00\x00\x00\x002r\x89\xe0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00\x00\x008\x1bj`" + + "\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00" + + "?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\x01\x02\x04\x02\x04\x02\x04\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01" + + "\x05\x01\x05\x01\x05\x01\x05\x01\x05\x02\x00\x00/ \x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x00\f\x00\x00T`\x01\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+0" + + "6\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUǯ\xdf\x1c\xee\x00\x00\x00\xee\x00\x00\x00\v\x00\x1c\x00Asia/ManilaUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\x14" + + "\xe1\xdc\x10\xff\xff\xff\xff{\x1f?\x90\xff\xff\xff\xff\xc1\x9c\xf4\x80\xff\xff\xff\xff\xc2\x160p\xff\xff\xff\xff\xcb\xf2\xe7\x00\xff\xff\xff\xffЩ%p\xff\xff\xff\xff\xe2l9\x00\xff\xff\xff\xff\xe2բ\xf0\x00" + + "\x00\x00\x00\x0fuF\x80\x00\x00\x00\x00\x10fz\xf0\x01\x03\x02\x03\x04\x03\x02\x03\x02\x03\xff\xff\x1f\xf0\x00\x00\x00\x00qp\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\fLMT\x00P" + + "DT\x00PST\x00JST\x00\nPST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\n\x00\x1c\x00Asia/SeoulUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x06\x00\x00\x00" + + "\x10\xff\xff\xff\xff\x8b\xd7\xf0x\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2C'\xf0\xff\xff\xff\xff\xd7e\x8fp\xff\xff\xff\xff\xd7\xee\x9d`\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd-\xe0\xff\xff\xff" + + "\xff\xda\u05ca\xf0\xff\xff\xff\xffۭ\x0f\xe0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xf1\xe0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe4k\xb7\xf8\xff\xff\xff\xff\xe5\x13\x18h\xff\xff\xff\xff\xe6b\x03" + + "x\xff\xff\xff\xff\xe7\x11L\xe8\xff\xff\xff\xff\xe8/px\xff\xff\xff\xff\xe8\xe7\xf4h\xff\xff\xff\xff\xea\x0fRx\xff\xff\xff\xff\xea\xc7\xd6h\xff\xff\xff\xff\xeb\xef4x\xff\xff\xff\xff째h\xff\xff\xff" + + "\xff\xed\xcf\x16x\xff\xff\xff\xff\ue1dah\xff\xff\xff\xff\xf05qx\x00\x00\x00\x00 \xa3`\x90\x00\x00\x00\x00!ng\x90\x00\x00\x00\x00\"\x83B\x90\x00\x00\x00\x00#NI\x90\x01\x02\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x04\x03\x04\x03\x04\x00\x00w\b\x00\x00\x00\x00w\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x01\f\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x01\fLM" + + "T\x00KST\x00JST\x00KDT\x00\nKST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xab\xcd\xdf\x05\xee\x02\x00\x00\xee\x02\x00\x00\n\x00\x1c\x00Asia/Chit" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x06" + + "\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\xf9\xa0\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18\xe9\xbap" + + "\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00\x00\x00\x00" + + " l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xd3\x10" + + "\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00\x00\x00\x00" + + "-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10\x00\x00\x00\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00\x00\x003=J\x90\x00\x00\x00\x004R%\x90" + + "\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x008\x1b$\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb\x06\x10\x00\x00\x00\x00:\xbcҐ\x00\x00\x00\x00" + + ";\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10\x00\x00\x00\x00?\x9a\xac\x10\x00\x00\x00\x00@e\xb3\x10\x00\x00\x00\x00A\x83Ȑ\x00\x00\x00\x00BE\x95\x10" + + "\x00\x00\x00\x00Cc\xaa\x90\x00\x00\x00\x00D%w\x10\x00\x00\x00\x00EC\x8c\x90\x00\x00\x00\x00F\x05Y\x10\x00\x00\x00\x00G#n\x90\x00\x00\x00\x00G\xeeu\x90\x00\x00\x00\x00I\x03P\x90\x00\x00\x00\x00" + + "I\xceW\x90\x00\x00\x00\x00J\xe32\x90\x00\x00\x00\x00K\xae9\x90\x00\x00\x00\x00L\xccO\x10\x00\x00\x00\x00M\x8e\x1b\x90\x00\x00\x00\x00TK\xc9\x00\x00\x00\x00\x00V\xf6\xce \x01\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x01\x03\x00\x00" + + "j`\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x8c\xa0\x00\bLMT\x00+08\x00+10\x00+09\x00\n<+09>-9\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x1c\x00Asia/DhakaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xffi\x86\x86\xbc\xff\xff\xff\xff\xcaۆ\xb0\xff\xff" + + "\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xffݨҘ\x00\x00\x00\x00J;\xc4\x10\x00\x00\x00\x00K<ؐ\x01\x02\x03\x02\x04\x05\x04\x00\x00T\xc4\x00\x00\x00\x00R\xd0\x00\x04\x00\x00[" + + "h\x00\b\x00\x00MX\x00\x0e\x00\x00T`\x00\x14\x00\x00bp\x01\x18LMT\x00HMT\x00+0630\x00+0530\x00+06\x00+07\x00\n<+06>-6\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa7f^]@\x01\x00\x00@\x01\x00\x00\f\x00\x1c\x00Asia/KuchingUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x05\x00\x00\x00\x18\xff\xff\xff\xff\xad\x8a\x06\x90\xff\xff\xff\xff\xbagG\x88\xff" + + "\xff\xff\xff\xbf{'\x80\xff\xff\xff\xff\xbf\xf3\x1bP\xff\xff\xff\xff\xc1]\xac\x80\xff\xff\xff\xff\xc1ՠP\xff\xff\xff\xff\xc3>\xe0\x00\xff\xff\xff\xffö\xd3\xd0\xff\xff\xff\xff\xc5 \x13\x80\xff\xff\xff\xff\xc5" + + "\x98\aP\xff\xff\xff\xff\xc7\x01G\x00\xff\xff\xff\xff\xc7y:\xd0\xff\xff\xff\xff\xc8\xe3\xcc\x00\xff\xff\xff\xff\xc9[\xbf\xd0\xff\xff\xff\xff\xca\xc4\xff\x80\xff\xff\xff\xff\xcb<\xf3P\xff\xff\xff\xffˑX\x00\xff" + + "\xff\xff\xff\xd2Hm\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x00\x00gp\x00\x00\x00\x00ix\x00\x04\x00\x00u0\x01\n\x00\x00p\x80\x00\x10\x00\x00~\x90\x00\x14LMT\x00+" + + "0730\x00+0820\x00+08\x00+09\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8a\xc1\x1eB\xb7\x00\x00\x00\xb7\x00\x00\x00\x0e\x00\x1c\x00As" + + "ia/PyongyangUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffi\x86\x9a\xa0\xff\xff\xff\xff\xd0\xf9\xd7@\x01\x02\x00\x00@\xe0\x00\x00\x00\x008@\x00\x04\x00\x00?H\x00\bLMT\x00+04\x00+" + - "0430\x00\n<+0430>-4:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xdav\x19z\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x1c\x00Asia/Bahrai" + - "nUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03" + - "\x00\x00\x00\f\xff\xff\xff\xff\xa1\xf2\x9d0\x00\x00\x00\x00\x04\x8a\x92\xc0\x01\x02\x00\x000P\x00\x00\x00\x008@\x00\x04\x00\x00*0\x00\bLMT\x00+04\x00+03\x00\n<+03>-3" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf9l\x03\x12\xf8\x02\x00\x00\xf8\x02\x00\x00\f\x00\x1c\x00Asia/IrkutskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xffV\xb6\x82?\xff\xff\xff\xff\xa2\x12" + - "\x0f\xbf\xff\xff\xff\xff\xb5\xa3\xd3\x10\x00\x00\x00\x00\x15'a\x80\x00\x00\x00\x00\x16\x18\x95\xf0\x00\x00\x00\x00\x17\b\x95\x00\x00\x00\x00\x00\x17\xf9\xc9p\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00" + - "\x00\x00\x1a\xccM\x80\x00\x00\x00\x00\x1b\xbcZ\xa0\x00\x00\x00\x00\x1c\xacK\xa0\x00\x00\x00\x00\x1d\x9c<\xa0\x00\x00\x00\x00\x1e\x8c-\xa0\x00\x00\x00\x00\x1f|\x1e\xa0\x00\x00\x00\x00 l\x0f\xa0\x00\x00\x00\x00!\\" + - "\x00\xa0\x00\x00\x00\x00\"K\xf1\xa0\x00\x00\x00\x00#;\xe2\xa0\x00\x00\x00\x00$+Ӡ\x00\x00\x00\x00%\x1bĠ\x00\x00\x00\x00&\v\xb5\xa0\x00\x00\x00\x00'\x04\xe1 \x00\x00\x00\x00'\xf4\xd2 \x00\x00" + - "\x00\x00(\xe4\xd10\x00\x00\x00\x00)xy0\x00\x00\x00\x00)Դ \x00\x00\x00\x00*ĥ \x00\x00\x00\x00+\xb4\x96 \x00\x00\x00\x00,\xa4\x87 \x00\x00\x00\x00-\x94x \x00\x00\x00\x00.\x84" + - "i \x00\x00\x00\x00/tZ \x00\x00\x00\x000dK \x00\x00\x00\x001]v\xa0\x00\x00\x00\x002rQ\xa0\x00\x00\x00\x003=X\xa0\x00\x00\x00\x004R3\xa0\x00\x00\x00\x005\x1d:\xa0\x00\x00" + - "\x00\x0062\x15\xa0\x00\x00\x00\x006\xfd\x1c\xa0\x00\x00\x00\x008\x1b2 \x00\x00\x00\x008\xdc\xfe\xa0\x00\x00\x00\x009\xfb\x14 \x00\x00\x00\x00:\xbc\xe0\xa0\x00\x00\x00\x00;\xda\xf6 \x00\x00\x00\x00<\xa5" + - "\xfd \x00\x00\x00\x00=\xba\xd8 \x00\x00\x00\x00>\x85\xdf \x00\x00\x00\x00?\x9a\xba \x00\x00\x00\x00@e\xc1 \x00\x00\x00\x00A\x83֠\x00\x00\x00\x00BE\xa3 \x00\x00\x00\x00Cc\xb8\xa0\x00\x00" + - "\x00\x00D%\x85 \x00\x00\x00\x00EC\x9a\xa0\x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00\x00I\x03^\xa0\x00\x00\x00\x00I\xcee\xa0\x00\x00\x00\x00J\xe3" + - "@\xa0\x00\x00\x00\x00K\xaeG\xa0\x00\x00\x00\x00L\xcc] \x00\x00\x00\x00M\x8e)\xa0\x00\x00\x00\x00TK\xd7\x10\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x04\x00\x00a\xc1\x00\x00\x00\x00a\xc1\x00\x04\x00\x00bp\x00\b\x00\x00" + - "~\x90\x01\f\x00\x00p\x80\x00\x10\x00\x00p\x80\x01\x10\x00\x00~\x90\x00\fLMT\x00IMT\x00+07\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9RO\xb0\x03\xe9\xe5\x02\x00\x00\xe5\x02\x00\x00\f\x00\x1c\x00Asia/YakutskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + + "\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x8b\xd7\xf1\x9c\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2/ap\x00\x00\x00\x00U\xce\x02p\x00\x00\x00\x00Z\xecup\x01\x02\x03\x01\x03" + + "\x00\x00u\xe4\x00\x00\x00\x00w\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x00\x04LMT\x00KST\x00JST\x00\nKST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17\xe2\x9c" + + "\xb32\x04\x00\x002\x04\x00\x00\x0e\x00\x1c\x00Asia/JerusalemUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa" + + "\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff" + + "\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff\xff\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10" + + "\xf7\x00\xff\xff\xff\xff\xda\xeb\xd0\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d\x00\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff" + + "\xff\xff\xe1V}\x00\xff\xff\xff\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff\xff\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff\xe7\x11Ҁ\xff\xff\xff\xff\xe8&" + + "\xad\x80\xff\xff\xff\xff\xe8\xe8z\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00" + + "\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I" + + "\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00" + + "\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{" + + ")\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4`\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp\x00\x00\x00\x00D,q\x00\x00\x00" + + "\x00\x00E\x1e\xf6\xf0\x00\x00\x00\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00\x00H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00J\xbe\x9c\xf0\x00\x00\x00\x00K\xab" + + "\xf9\x00\x00\x00\x00\x00L\x8c\t\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7\x80\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!\x06\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\f\x00\x008@\x01\x10" + + "LMT\x00JMT\x00IDT\x00IST\x00IDDT\x00\nIST-2IDT,M3.4.4/26,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU0I\xc7\xde\xec\x00\x00\x00\xec\x00\x00\x00\v\x00\x1c\x00Asia/SaigonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\x88\x8cC\x8a\xff\xff\xff\xff\x91\xa3+\n\xff\xff\xff\xff\xcd5\xe6\x80\xff\xff\xff" + + "\xff\xd1Y\xcep\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xff\xd52\xbb\x10\xff\xff\xff\xff\xe4\xb6\xe4\x80\xff\xff\xff\xff\xed/\x98\x00\x00\x00\x00\x00\n=\xc7\x00\x01\x02\x03\x04\x02\x03\x02\x03\x02\x00\x00c\xf6\x00\x00" + + "\x00\x00c\xf6\x00\x04\x00\x00bp\x00\t\x00\x00p\x80\x00\r\x00\x00~\x90\x00\x11LMT\x00PLMT\x00+07\x00+08\x00+09\x00\n<+07>-7\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU)\x15II\xf3\x02\x00\x00\xf3\x02\x00\x00\r\x00\x1c\x00Asia/SakhalinUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xff\x86\xf0\u0378\xff\xff\xff\xff\xd20\xb2\xf0\x00\x00\x00\x00" + + "\x15'7P\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p" + + "\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00" + + "#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00" + + "\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x00" + + "0d \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80" + + "\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xfa\xf8\x00\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00" + + ">\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80" + + "\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00\x00I\x03B\x80\x00\x00\x00\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00" + + "L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00\x00\x00TK\xba\xf0\x00\x00\x00\x00V\xf6\xb2\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x05\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x03\x00\x00\x85\xc8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00" + + "\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00+09\x00+12\x00+11\x00+10\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUj$\xcd\xf4\x9a\x00" + + "\x00\x00\x9a\x00\x00\x00\f\x00\x1c\x00Asia/ThimphuUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\xd5\xe6\x15t\x00\x00\x00\x00!aM\xa8\x01\x02\x00\x00T\f\x00\x00\x00\x00MX\x00\x04\x00\x00T`\x00" + + "\nLMT\x00+0530\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\f\x00\x1c\x00Asia/K" + + "ashgarUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xb0\xfe\xbad\x01\x00\x00R\x1c\x00\x00\x00\x00T`\x00\x04LMT\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U]S\xbb\x12\xac\x03\x00\x00\xac\x03\x00\x00\x0e\x00\x1c\x00Asia/FamagustaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00V\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa5w\x1e,\x00\x00\x00\x00\t\xed\xaf\xe0\x00\x00\x00\x00\nݒ\xd0\x00\x00" + + "\x00\x00\v\xfad\xe0\x00\x00\x00\x00\f\xbe\xc6P\x00\x00\x00\x00\r\xa49`\x00\x00\x00\x00\x0e\x8a\xe1\xd0\x00\x00\x00\x00\x0f\x84\x1b`\x00\x00\x00\x00\x10uO\xd0\x00\x00\x00\x00\x11c\xfd`\x00\x00\x00\x00\x12S" + + "\xe0P\x00\x00\x00\x00\x13M\x19\xe0\x00\x00\x00\x00\x143\xc2P\x00\x00\x00\x00\x15#\xc1`\x00\x00\x00\x00\x16\x13\xa4P\x00\x00\x00\x00\x17\x03\xa3`\x00\x00\x00\x00\x17\xf3\x86P\x00\x00\x00\x00\x18\xe3\x85`\x00\x00" + + "\x00\x00\x19\xd3hP\x00\x00\x00\x00\x1a\xc3g`\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l" + + "G\xe0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\vP\x00\x00" + + "\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84" + + "\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002M\x91\xd0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004-s\xd0\x00\x00\x00\x005\x1dr\xe0\x00\x00" + + "\x00\x0062x\x10\x00\x00\x00\x006\xfd\x7f\x10\x00\x00\x00\x008\x1b\x94\x90\x00\x00\x00\x008\xdda\x10\x00\x00\x00\x009\xfbv\x90\x00\x00\x00\x00:\xbdC\x10\x00\x00\x00\x00;\xdbX\x90\x00\x00\x00\x00<\xa6" + + "_\x90\x00\x00\x00\x00=\xbb:\x90\x00\x00\x00\x00>\x86A\x90\x00\x00\x00\x00?\x9b\x1c\x90\x00\x00\x00\x00@f#\x90\x00\x00\x00\x00A\x849\x10\x00\x00\x00\x00BF\x05\x90\x00\x00\x00\x00Cd\x1b\x10\x00\x00" + + "\x00\x00D%\xe7\x90\x00\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3" + + "\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8e\x8c\x10\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00" + + "\x00\x00Rle\x90\x00\x00\x00\x00S7l\x90\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00V,)\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xd0\x7f\xd0\x00\x00\x00\x00Y\xf5" + + "(\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x02\x00\x00\x1f\xd4\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00EEST" + + "\x00EET\x00+03\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUL\xe0\x91y\xe5\x02\x00" + + "\x00\xe5\x02\x00\x00\x10\x00\x1c\x00Asia/KrasnoyarskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf9\r\xf2\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00" + + "\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00" + + "\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0" + + "\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00" + + "*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x000dY0\x00\x00\x00\x001]\x84\xb0" + + "\x00\x00\x00\x002r_\xb0\x00\x00\x00\x003=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dH\xb0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd*\xb0\x00\x00\x00\x008\x1b@0\x00\x00\x00\x00" + + "8\xdd\f\xb0\x00\x00\x00\x009\xfb\"0\x00\x00\x00\x00:\xbc\xee\xb0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\v0\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xed0\x00\x00\x00\x00?\x9a\xc80" + + "\x00\x00\x00\x00@e\xcf0\x00\x00\x00\x00A\x83\xe4\xb0\x00\x00\x00\x00BE\xb10\x00\x00\x00\x00Ccư\x00\x00\x00\x00D%\x930\x00\x00\x00\x00EC\xa8\xb0\x00\x00\x00\x00F\x05u0\x00\x00\x00\x00" + + "G#\x8a\xb0\x00\x00\x00\x00G\ue470\x00\x00\x00\x00I\x03l\xb0\x00\x00\x00\x00I\xces\xb0\x00\x00\x00\x00J\xe3N\xb0\x00\x00\x00\x00K\xaeU\xb0\x00\x00\x00\x00L\xcck0\x00\x00\x00\x00M\x8e7\xb0" + + "\x00\x00\x00\x00TK\xe5 \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00W\x0e\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\f\x00\x00p\x80\x00\bLMT\x00+06\x00+08" + + "\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xdav\x19z\x98\x00\x00\x00\x98\x00\x00\x00\n\x00\x1c\x00Asia/QatarUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff" + + "\xff\xa1\xf2\x9d0\x00\x00\x00\x00\x04\x8a\x92\xc0\x01\x02\x00\x000P\x00\x00\x00\x008@\x00\x04\x00\x00*0\x00\bLMT\x00+04\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU\x03\x87\xb3<\xe8\x02\x00\x00\xe8\x02\x00\x00\t\x00\x1c\x00Asia/BakuUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\xea^\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp" + - "\x00\x00\x00\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18\xe9\xbap\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00" + - "\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ" + - "\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xd3\x10\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00" + - ")Ԧ\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00\x00\x00\x00-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10" + - "\x00\x00\x00\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00\x00\x003=J\x90\x00\x00\x00\x004R%\x90\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x00" + - "8\x1b$\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb\x06\x10\x00\x00\x00\x00:\xbcҐ\x00\x00\x00\x00;\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10" + - "\x00\x00\x00\x00?\x9a\xac\x10\x00\x00\x00\x00@e\xb3\x10\x00\x00\x00\x00A\x83Ȑ\x00\x00\x00\x00BE\x95\x10\x00\x00\x00\x00Cc\xaa\x90\x00\x00\x00\x00D%w\x10\x00\x00\x00\x00EC\x8c\x90\x00\x00\x00\x00" + - "F\x05Y\x10\x00\x00\x00\x00G#n\x90\x00\x00\x00\x00G\xeeu\x90\x00\x00\x00\x00I\x03P\x90\x00\x00\x00\x00I\xceW\x90\x00\x00\x00\x00J\xe32\x90\x00\x00\x00\x00K\xae9\x90\x00\x00\x00\x00L\xccO\x10" + - "\x00\x00\x00\x00M\x8e\x1b\x90\x00\x00\x00\x00TK\xc9\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00y\xa2\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x8c\xa0\x00\bLMT" + - "\x00+08\x00+10\x00+09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R.>[K\xab\x00\x00\x00\xab\x00\x00\x00\r\x00\x1c\x00Asia/Jay" + - "apuraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03" + - "\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\xba\x16\xc1\x98\xff\xff\xff\xff\xd0X\xb9\xf0\xff\xff\xff\xff\xf4\xb5\xa2h\x01\x02\x03\x00\x00\x83\xe8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x00\b\x00\x00~\x90\x00\x0eL" + - "MT\x00+09\x00+0930\x00WIT\x00\nWIT-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\x0e\x00\x1c\x00Asia/H" + - "ong_KongUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00E\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff\x85ic\x90\xff\xff\xff\xff\xcaM10\xff\xff\xff\xff\xcaۓ0\xff\xff\xff\xff\xcbKqx\xff\xff\xff\xffҠސ\xff\xff\xff\xff\xd3k׀\xff" + - "\xff\xff\xffԓX\xb8\xff\xff\xff\xff\xd5B\xb08\xff\xff\xff\xff\xd6s:\xb8\xff\xff\xff\xff\xd7>A\xb8\xff\xff\xff\xff\xd8.2\xb8\xff\xff\xff\xff\xd8\xf99\xb8\xff\xff\xff\xff\xda\x0e\x14\xb8\xff\xff\xff\xff\xda" + - "\xd9\x1b\xb8\xff\xff\xff\xff\xdb\xed\xf6\xb8\xff\xff\xff\xffܸ\xfd\xb8\xff\xff\xff\xff\xdd\xcdظ\xff\xff\xff\xffޢ\x1a8\xff\xff\xff\xff߶\xf58\xff\xff\xff\xff\xe0\x81\xfc8\xff\xff\xff\xff\xe1\x96\xc9(\xff" + - "\xff\xff\xff\xe2Oi8\xff\xff\xff\xff\xe3v\xab(\xff\xff\xff\xff\xe4/K8\xff\xff\xff\xff\xe5_Ǩ\xff\xff\xff\xff\xe6\x0f-8\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9" + - "\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff" + - "\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7" + - "%~8\xff\xff\xff\xff\xf8\x15a(\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf5C(\xff\xff\xff\xff\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff" + - "\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05" + - "G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00" + - "\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x02\x03\x04\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00k\n\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x01\b\x00\x00w\x88\x01\r\x00\x00~\x90" + - "\x00\x12LMT\x00HKT\x00HKST\x00HKWT\x00JST\x00\nHKT-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RS\xa5\x81e\xf7\x00\x00\x00\xf7\x00\x00\x00\x0e\x00\x1c" + - "\x00Asia/PontianakUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x95D\xff\xff\xff\xff\xe7\xda\fP\x00\x00\x00\x00\x15'\x99\xc0" + + "\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00" + + "\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0" + + "\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00" + + "*\xc4\xebp\x00\x00\x00\x001]\xd9\x10\x00\x00\x00\x002r\xb4\x10\x00\x00\x00\x003=\xad\x00\x00\x00\x00\x004R\x88\x00\x00\x00\x00\x005\x1d\x8f\x00\x00\x00\x00\x0062j\x00\x00\x00\x00\x006\xfdq\x00" + + "\x00\x00\x00\x008\x1b\x86\x80\x00\x00\x00\x008\xddS\x00\x00\x00\x00\x009\xfbh\x80\x00\x00\x00\x00:\xbd5\x00\x00\x00\x00\x00;\xdbJ\x80\x00\x00\x00\x00<\xa6Q\x80\x00\x00\x00\x00=\xbb,\x80\x00\x00\x00\x00" + + ">\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%ـ\x00\x00\x00\x00EC\xef\x00" + + "\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00\x00\x00K\xae\x9c\x00\x00\x00\x00\x00" + + "Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x00\x00\x00\x00N\xac\x93\x80\x00\x00\x00\x00On`\x00\x00\x00\x00\x00P\x8cu\x80\x00\x00\x00\x00QW|\x80\x00\x00\x00\x00RlW\x80\x00\x00\x00\x00S7^\x80" + + "\x00\x00\x00\x00TL9\x80\x00\x00\x00\x00U\x17@\x80\x00\x00\x00\x00V,\x1b\x80\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00.\xbc\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\f" + + "LMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\v\x00\x1c\x00Asia/" + + "TaipeiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + ")\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xfft\xce\xf0\x18\xff\xff\xff\xff\xc3UI\x80\xff\xff\xff\xff\xd2TY\x80\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff" + + "\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9\xe7\x99\xf0\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xdb\xc8\xcdp\xff\xff\xff\xff\xdc\xe0Y" + + "\x80\xff\xff\xff\xffݪ\x00\xf0\xff\xff\xff\xff\xders\x00\xff\xff\xff\xffߵdp\xff\xff\xff\xff\xe0|\x85\x00\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2]\xb8\x80\xff\xff\xff\xff\xe3w\xcbp\xff\xff\xff" + + "\xff\xe4>\xec\x00\xff\xff\xff\xff\xe50 p\xff\xff\xff\xff\xe6!q\x00\xff\xff\xff\xff\xe7\x12\xa5p\xff\xff\xff\xff\xe8\x02\xa4\x80\xff\xff\xff\xff\xe8\xf3\xd8\xf0\xff\xff\xff\xff\xe9\xe3\xd8\x00\xff\xff\xff\xff\xea\xd5\f" + + "p\xff\xff\xff\xff\xeb\xc5\v\x80\xff\xff\xff\xff\xec\xb6?\xf0\xff\xff\xff\xff\xed\xf7\xfc\x00\xff\xff\xff\xff\xee\x98\xc4\xf0\xff\xff\xff\xff\xef\xd9/\x80\xff\xff\xff\xff\xf0y\xf8p\x00\x00\x00\x00\a\xfcV\x00\x00\x00\x00" + + "\x00\b\xed\x8ap\x00\x00\x00\x00\t݉\x80\x00\x00\x00\x00\nν\xf0\x00\x00\x00\x00\x11ۡ\x80\x00\x00\x00\x00\x12T\xddp\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01" + + "\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x00\x00q\xe8\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x01\fLMT\x00CST\x00JST\x00CDT\x00\nC" + + "ST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\x0e\x00\x1c\x00Asia/SingaporeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00 \xff\xff\xff\xff~6S\xa3" + + "\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2Hm\xf0\x00\x00\x00\x00\x16\x91\xf5\b\x01\x02\x03\x04" + + "\x05\x06\x05\a\x00\x00a]\x00\x00\x00\x00a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18\x00\x00p\x80\x00\x1cLMT\x00SMT\x00" + + "+07\x00+0720\x00+0730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x03R\xda\xedU\x02\x00\x00U\x02\x00\x00\f" + + "\x00\x1c\x00Asia/NicosiaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x1f\xff\xff\xff\xff\x8b\xff\x8e\x00\xff\xff\xff\xff\xba\x16\xdf\x00\xff\xff\xff\xff\xcby\xa4\b\xff\xff\xff\xff\xd2V\xeep\xff\xff\xff\xff\xd7<\xc6\b\xff\xff" + - "\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xf4\xb5\xbe\x88\x00\x00\x00\x00!\xdat\x80\x01\x02\x03\x02\x04\x02\x05\x06\x00\x00f\x80\x00\x00\x00\x00f\x80\x00\x04\x00\x00ix\x00\b\x00\x00~\x90\x00\x0e\x00\x00p\x80\x00\x12" + - "\x00\x00p\x80\x00\x16\x00\x00bp\x00\x1bLMT\x00PMT\x00+0730\x00+09\x00+08\x00WITA\x00WIB\x00\nWIB-7\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R;\u007fP\x8d\xd4\a\x00\x00\xd4\a\x00\x00\v\x00\x1c\x00Asia/TehranUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc9\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xff\x9al}\xc8\xff\xff\xff\xff\xd2\xdb\x12\xc8\x00\x00\x00\x00\x0e\xbb\xa2H\x00\x00" + - "\x00\x00\x0ft-@\x00\x00\x00\x00\x10\x8e@0\x00\x00\x00\x00\x10\xed:@\x00\x00\x00\x00\x11Ug\xc8\x00\x00\x00\x00\x12EJ\xb8\x00\x00\x00\x00\x137\xec\xc8\x00\x00\x00\x00\x14-\x15\xb8\x00\x00\x00\x00( " + - "v\xc8\x00\x00\x00\x00(\u06dd\xb8\x00\x00\x00\x00)˜\xc8\x00\x00\x00\x00*\xbe\"\xb8\x00\x00\x00\x00+\xac\xd0H\x00\x00\x00\x00,\x9fV8\x00\x00\x00\x00-\x8e\x03\xc8\x00\x00\x00\x00.\x80\x89\xb8\x00\x00" + - "\x00\x00/o7H\x00\x00\x00\x000a\xbd8\x00\x00\x00\x001Pj\xc8\x00\x00\x00\x002B\xf0\xb8\x00\x00\x00\x0032\xef\xc8\x00\x00\x00\x004%u\xb8\x00\x00\x00\x005\x14#H\x00\x00\x00\x006\x06" + - "\xa98\x00\x00\x00\x006\xf5V\xc8\x00\x00\x00\x007\xe7ܸ\x00\x00\x00\x008֊H\x00\x00\x00\x009\xc9\x108\x00\x00\x00\x00:\xb9\x0fH\x00\x00\x00\x00;\xab\x958\x00\x00\x00\x00<\x9aB\xc8\x00\x00" + - "\x00\x00=\x8cȸ\x00\x00\x00\x00>{vH\x00\x00\x00\x00?m\xfc8\x00\x00\x00\x00@\\\xa9\xc8\x00\x00\x00\x00AO/\xb8\x00\x00\x00\x00B?.\xc8\x00\x00\x00\x00C1\xb4\xb8\x00\x00\x00\x00G\xe2" + - "\xc9H\x00\x00\x00\x00H\xd5O8\x00\x00\x00\x00I\xc5NH\x00\x00\x00\x00J\xb7\xd48\x00\x00\x00\x00K\xa6\x81\xc8\x00\x00\x00\x00L\x99\a\xb8\x00\x00\x00\x00M\x87\xb5H\x00\x00\x00\x00Nz;8\x00\x00" + - "\x00\x00Oh\xe8\xc8\x00\x00\x00\x00P[n\xb8\x00\x00\x00\x00QKm\xc8\x00\x00\x00\x00R=\xf3\xb8\x00\x00\x00\x00S,\xa1H\x00\x00\x00\x00T\x1f'8\x00\x00\x00\x00U\r\xd4\xc8\x00\x00\x00\x00V\x00" + - "Z\xb8\x00\x00\x00\x00V\xef\bH\x00\x00\x00\x00W\xe1\x8e8\x00\x00\x00\x00XэH\x00\x00\x00\x00Y\xc4\x138\x00\x00\x00\x00Z\xb2\xc0\xc8\x00\x00\x00\x00[\xa5F\xb8\x00\x00\x00\x00\\\x93\xf4H\x00\x00" + - "\x00\x00]\x86z8\x00\x00\x00\x00^u'\xc8\x00\x00\x00\x00_g\xad\xb8\x00\x00\x00\x00`W\xac\xc8\x00\x00\x00\x00aJ2\xb8\x00\x00\x00\x00b8\xe0H\x00\x00\x00\x00c+f8\x00\x00\x00\x00d\x1a" + - "\x13\xc8\x00\x00\x00\x00e\f\x99\xb8\x00\x00\x00\x00e\xfbGH\x00\x00\x00\x00f\xed\xcd8\x00\x00\x00\x00g\xdd\xccH\x00\x00\x00\x00h\xd0R8\x00\x00\x00\x00i\xbe\xff\xc8\x00\x00\x00\x00j\xb1\x85\xb8\x00\x00" + - "\x00\x00k\xa03H\x00\x00\x00\x00l\x92\xb98\x00\x00\x00\x00m\x81f\xc8\x00\x00\x00\x00ns\xec\xb8\x00\x00\x00\x00ob\x9aH\x00\x00\x00\x00pU 8\x00\x00\x00\x00qE\x1fH\x00\x00\x00\x00r7" + - "\xa58\x00\x00\x00\x00s&R\xc8\x00\x00\x00\x00t\x18ظ\x00\x00\x00\x00u\a\x86H\x00\x00\x00\x00u\xfa\f8\x00\x00\x00\x00v\xe8\xb9\xc8\x00\x00\x00\x00w\xdb?\xb8\x00\x00\x00\x00x\xcb>\xc8\x00\x00" + - "\x00\x00y\xbdĸ\x00\x00\x00\x00z\xacrH\x00\x00\x00\x00{\x9e\xf88\x00\x00\x00\x00|\x8d\xa5\xc8\x00\x00\x00\x00}\x80+\xb8\x00\x00\x00\x00~n\xd9H\x00\x00\x00\x00\u007fa_8\x00\x00\x00\x00\x80Q" + - "^H\x00\x00\x00\x00\x81C\xe48\x00\x00\x00\x00\x822\x91\xc8\x00\x00\x00\x00\x83%\x17\xb8\x00\x00\x00\x00\x84\x13\xc5H\x00\x00\x00\x00\x85\x06K8\x00\x00\x00\x00\x85\xf4\xf8\xc8\x00\x00\x00\x00\x86\xe7~\xb8\x00\x00" + - "\x00\x00\x87\xd7}\xc8\x00\x00\x00\x00\x88\xca\x03\xb8\x00\x00\x00\x00\x89\xb8\xb1H\x00\x00\x00\x00\x8a\xab78\x00\x00\x00\x00\x8b\x99\xe4\xc8\x00\x00\x00\x00\x8c\x8cj\xb8\x00\x00\x00\x00\x8d{\x18H\x00\x00\x00\x00\x8em" + - "\x9e8\x00\x00\x00\x00\x8f]\x9dH\x00\x00\x00\x00\x90P#8\x00\x00\x00\x00\x91>\xd0\xc8\x00\x00\x00\x00\x921V\xb8\x00\x00\x00\x00\x93 \x04H\x00\x00\x00\x00\x94\x12\x8a8\x00\x00\x00\x00\x95\x017\xc8\x00\x00" + - "\x00\x00\x95\xf3\xbd\xb8\x00\x00\x00\x00\x96\xe3\xbc\xc8\x00\x00\x00\x00\x97\xd6B\xb8\x00\x00\x00\x00\x98\xc4\xf0H\x00\x00\x00\x00\x99\xb7v8\x00\x00\x00\x00\x9a\xa6#\xc8\x00\x00\x00\x00\x9b\x98\xa9\xb8\x00\x00\x00\x00\x9c\x87" + - "WH\x00\x00\x00\x00\x9dy\xdd8\x00\x00\x00\x00\x9ei\xdcH\x00\x00\x00\x00\x9f\\b8\x00\x00\x00\x00\xa0K\x0f\xc8\x00\x00\x00\x00\xa1=\x95\xb8\x00\x00\x00\x00\xa2,CH\x00\x00\x00\x00\xa3\x1e\xc98\x00\x00" + - "\x00\x00\xa4\rv\xc8\x00\x00\x00\x00\xa4\xff\xfc\xb8\x00\x00\x00\x00\xa5\xef\xfb\xc8\x00\x00\x00\x00\xa6⁸\x00\x00\x00\x00\xa7\xd1/H\x00\x00\x00\x00\xa8õ8\x00\x00\x00\x00\xa9\xb2b\xc8\x00\x00\x00\x00\xaa\xa4" + - "\xe8\xb8\x00\x00\x00\x00\xab\x93\x96H\x00\x00\x00\x00\xac\x86\x1c8\x00\x00\x00\x00\xadt\xc9\xc8\x00\x00\x00\x00\xaegO\xb8\x00\x00\x00\x00\xafWN\xc8\x00\x00\x00\x00\xb0IԸ\x00\x00\x00\x00\xb18\x82H\x00\x00" + - "\x00\x00\xb2+\b8\x00\x00\x00\x00\xb3\x19\xb5\xc8\x00\x00\x00\x00\xb4\f;\xb8\x00\x00\x00\x00\xb4\xfa\xe9H\x00\x00\x00\x00\xb5\xedo8\x00\x00\x00\x00\xb6\xddnH\x00\x00\x00\x00\xb7\xcf\xf48\x00\x00\x00\x00\xb8\xbe" + - "\xa1\xc8\x00\x00\x00\x00\xb9\xb1'\xb8\x00\x00\x00\x00\xba\x9f\xd5H\x00\x00\x00\x00\xbb\x92[8\x00\x00\x00\x00\xbc\x81\b\xc8\x00\x00\x00\x00\xbds\x8e\xb8\x00\x00\x00\x00\xbec\x8d\xc8\x00\x00\x00\x00\xbfV\x13\xb8\x00\x00" + - "\x00\x00\xc0D\xc1H\x00\x00\x00\x00\xc17G8\x00\x00\x00\x00\xc2%\xf4\xc8\x00\x00\x00\x00\xc3\x18z\xb8\x00\x00\x00\x00\xc4\a(H\x00\x00\x00\x00\xc4\xf9\xae8\x00\x00\x00\x00\xc5\xe9\xadH\x00\x00\x00\x00\xc6\xdc" + - "38\x00\x00\x00\x00\xc7\xca\xe0\xc8\x00\x00\x00\x00Ƚf\xb8\x00\x00\x00\x00ɬ\x14H\x00\x00\x00\x00ʞ\x9a8\x00\x00\x00\x00ˍG\xc8\x00\x00\x00\x00\xcc\u007f\u0378\x00\x00\x00\x00\xcdo\xcc\xc8\x00\x00" + - "\x00\x00\xcebR\xb8\x00\x00\x00\x00\xcfQ\x00H\x00\x00\x00\x00\xd0C\x868\x00\x00\x00\x00\xd123\xc8\x00\x00\x00\x00\xd2$\xb9\xb8\x00\x00\x00\x00\xd3\x13gH\x00\x00\x00\x00\xd4\x05\xed8\x00\x00\x00\x00\xd4\xf5" + - "\xecH\x00\x00\x00\x00\xd5\xe8r8\x00\x00\x00\x00\xd6\xd7\x1f\xc8\x00\x00\x00\x00\xd7ɥ\xb8\x00\x00\x00\x00ظSH\x00\x00\x00\x00٪\xd98\x00\x00\x00\x00ڙ\x86\xc8\x00\x00\x00\x00ی\f\xb8\x00\x00" + - "\x00\x00\xdc|\v\xc8\x00\x00\x00\x00\xddn\x91\xb8\x00\x00\x00\x00\xde]?H\x01\x02\x04\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02" + - "\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02" + - "\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02" + - "\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x00\x0008\x00\x00\x00\x0008\x00\x04\x00\x0018\x00" + - "\b\x00\x00FP\x01\x0e\x00\x008@\x00\x12\x00\x00?H\x01\x16LMT\x00TMT\x00+0330\x00+05\x00+04\x00+0430\x00\n<+0330>-3:30" + - "<+0430>,J79/24,J263/24\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rd%\x05\xd8\xe6\x02\x00\x00\xe6\x02\x00\x00\x10\x00\x1c\x00Asia/Vl" + - "adivostokUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa7YG]\xff\xff\xff\xff\xb5\xa3\xb6\xf0\x00\x00\x00\x00\x15'E`\x00\x00\x00\x00\x16\x18y\xd0\x00\x00\x00\x00\x17\bx\xe0\x00\x00\x00\x00\x17\xf9\xadP" + - "\x00\x00\x00\x00\x18\xe9\xac`\x00\x00\x00\x00\x19\xda\xe0\xd0\x00\x00\x00\x00\x1a\xcc1`\x00\x00\x00\x00\x1b\xbc>\x80\x00\x00\x00\x00\x1c\xac/\x80\x00\x00\x00\x00\x1d\x9c \x80\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00\x00" + - "\x1f|\x02\x80\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xe4\x80\x00\x00\x00\x00\"KՀ\x00\x00\x00\x00#;ƀ\x00\x00\x00\x00$+\xb7\x80\x00\x00\x00\x00%\x1b\xa8\x80\x00\x00\x00\x00&\v\x99\x80" + - "\x00\x00\x00\x00'\x04\xc5\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\xb5\x10\x00\x00\x00\x00)x]\x10\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*ĉ\x00\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00" + - ",\xa4k\x00\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84M\x00\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000d/\x00\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80" + - "\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xfa\xf8\x00\x00\x00\x00\x00" + - ":\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80" + - "\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00\x00" + - "I\x03B\x80\x00\x00\x00\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00\x00\x00TK\xba\xf0\x01\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00{" + - "\xa3\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x9a\xb0\x01\b\x00\x00\x8c\xa0\x00\f\x00\x00\x8c\xa0\x01\f\x00\x00\x9a\xb0\x00\bLMT\x00+09\x00+11\x00+10\x00\n<+10>-10\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R:\x11\xea\xa2\xe5\x02\x00\x00\xe5\x02\x00\x00\t\x00\x1c\x00Asia/OmskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xb3@\xb6\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00" + - "\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv" + - "\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00" + - "\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)x\x95" + - "P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00+\xb4\xb2@\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\x94@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00" + - "\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8" + - "\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00" + - "\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6" + - "\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00" + - "\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00D\xca\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\f\x00\x00bp" + - "\x00\bLMT\x00+05\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\v\x00\x1c\x00Asi" + - "a/TaipeiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00)\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xfft\xce\xf0\x18\xff\xff\xff\xff\xc3UI\x80\xff\xff\xff\xff\xd2TY\x80\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff" + - "\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9\xe7\x99\xf0\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xdb\xc8\xcdp\xff\xff\xff\xff\xdc" + - "\xe0Y\x80\xff\xff\xff\xffݪ\x00\xf0\xff\xff\xff\xff\xders\x00\xff\xff\xff\xffߵdp\xff\xff\xff\xff\xe0|\x85\x00\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2]\xb8\x80\xff\xff\xff\xff\xe3w\xcbp\xff" + - "\xff\xff\xff\xe4>\xec\x00\xff\xff\xff\xff\xe50 p\xff\xff\xff\xff\xe6!q\x00\xff\xff\xff\xff\xe7\x12\xa5p\xff\xff\xff\xff\xe8\x02\xa4\x80\xff\xff\xff\xff\xe8\xf3\xd8\xf0\xff\xff\xff\xff\xe9\xe3\xd8\x00\xff\xff\xff\xff\xea" + - "\xd5\fp\xff\xff\xff\xff\xeb\xc5\v\x80\xff\xff\xff\xff\xec\xb6?\xf0\xff\xff\xff\xff\xed\xf7\xfc\x00\xff\xff\xff\xff\xee\x98\xc4\xf0\xff\xff\xff\xff\xef\xd9/\x80\xff\xff\xff\xff\xf0y\xf8p\x00\x00\x00\x00\a\xfcV\x00\x00" + - "\x00\x00\x00\b\xed\x8ap\x00\x00\x00\x00\t݉\x80\x00\x00\x00\x00\nν\xf0\x00\x00\x00\x00\x11ۡ\x80\x00\x00\x00\x00\x12T\xddp\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01" + - "\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x00\x00q\xe8\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x01\fLMT\x00CST\x00JST\x00CDT\x00" + - "\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x1c\x00Asia/DaccaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xffi\x86\x86\xbc\xff\xff" + - "\xff\xff\xcaۆ\xb0\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xffݨҘ\x00\x00\x00\x00J;\xc4\x10\x00\x00\x00\x00K<ؐ\x01\x02\x03\x02\x04\x05\x04\x00\x00T\xc4\x00\x00\x00" + - "\x00R\xd0\x00\x04\x00\x00[h\x00\b\x00\x00MX\x00\x0e\x00\x00T`\x00\x14\x00\x00bp\x01\x18LMT\x00HMT\x00+0630\x00+0530\x00+06\x00+07\x00\n<+" + - "06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R]S\xbb\x12\xac\x03\x00\x00\xac\x03\x00\x00\x0e\x00\x1c\x00Asia/FamagustaUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00V\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa5w\x1e" + - ",\x00\x00\x00\x00\t\xed\xaf\xe0\x00\x00\x00\x00\nݒ\xd0\x00\x00\x00\x00\v\xfad\xe0\x00\x00\x00\x00\f\xbe\xc6P\x00\x00\x00\x00\r\xa49`\x00\x00\x00\x00\x0e\x8a\xe1\xd0\x00\x00\x00\x00\x0f\x84\x1b`\x00\x00\x00" + - "\x00\x10uO\xd0\x00\x00\x00\x00\x11c\xfd`\x00\x00\x00\x00\x12S\xe0P\x00\x00\x00\x00\x13M\x19\xe0\x00\x00\x00\x00\x143\xc2P\x00\x00\x00\x00\x15#\xc1`\x00\x00\x00\x00\x16\x13\xa4P\x00\x00\x00\x00\x17\x03\xa3" + - "`\x00\x00\x00\x00\x17\xf3\x86P\x00\x00\x00\x00\x18\xe3\x85`\x00\x00\x00\x00\x19\xd3hP\x00\x00\x00\x00\x1a\xc3g`\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00" + - "\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xee" + - "\xd0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00" + - "\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002M\x91\xd0\x00\x00\x00\x003=\x90" + - "\xe0\x00\x00\x00\x004-s\xd0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062x\x10\x00\x00\x00\x006\xfd\u007f\x10\x00\x00\x00\x008\x1b\x94\x90\x00\x00\x00\x008\xdda\x10\x00\x00\x00\x009\xfbv\x90\x00\x00\x00" + - "\x00:\xbdC\x10\x00\x00\x00\x00;\xdbX\x90\x00\x00\x00\x00<\xa6_\x90\x00\x00\x00\x00=\xbb:\x90\x00\x00\x00\x00>\x86A\x90\x00\x00\x00\x00?\x9b\x1c\x90\x00\x00\x00\x00@f#\x90\x00\x00\x00\x00A\x849" + - "\x10\x00\x00\x00\x00BF\x05\x90\x00\x00\x00\x00Cd\x1b\x10\x00\x00\x00\x00D%\xe7\x90\x00\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00" + - "\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8e\x8c\x10\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn" + - "\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S7l\x90\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00V,)\x90\x00\x00\x00" + - "\x00V\xf70\x90\x00\x00\x00\x00W\xd0\u007f\xd0\x00\x00\x00\x00Y\xf5(\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x02\x00\x00\x1f\xd4\x00\x00\x00\x00*0\x01\x04\x00" + - "\x00\x1c \x00\t\x00\x00*0\x00\rLMT\x00EEST\x00EET\x00+03\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/RiyadhUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*" + - "0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x1c\x00Asia/Chung" + - "kingUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00" + - "\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff\xff\xa1y\x04\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb" + - "\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff" + - "\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"" + - "g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nC" + - "ST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xceG|\xea\x13\x03\x00\x00\x13\x03\x00\x00\n\x00\x1c\x00Asia/AmmanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00F\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\xb6\xa3\xd6\xd0\x00\x00\x00\x00" + - "\x06ry\xe0\x00\x00\x00\x00\a\f\xabP\x00\x00\x00\x00\b$7`\x00\x00\x00\x00\b\xed\xde\xd0\x00\x00\x00\x00\n\x05j\xe0\x00\x00\x00\x00\n\xcf\x12P\x00\x00\x00\x00\v\xe7\xef\xe0\x00\x00\x00\x00\f\xdau\xd0" + - "\x00\x00\x00\x00\r\xc9#`\x00\x00\x00\x00\x0e\x92\xca\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00\x00\x10r\xac\xd0\x00\x00\x00\x00\x1c\xad\xd5`\x00\x00\x00\x00\x1d\x9f\t\xd0\x00\x00\x00\x00\x1e\x92\xfd`\x00\x00\x00\x00" + - "\x1f\x82\xe0P\x00\x00\x00\x00 r\xdf`\x00\x00\x00\x00!b\xc2P\x00\x00\x00\x00\"R\xc1`\x00\x00\x00\x00#K\xde\xd0\x00\x00\x00\x00$d\xbc`\x00\x00\x00\x00%+\xc0\xd0\x00\x00\x00\x00&7o`" + - "\x00\x00\x00\x00'\v\xa2\xd0\x00\x00\x00\x00(\vs\xe0\x00\x00\x00\x00(\xe2JP\x00\x00\x00\x00)\xe4\xbe`\x00\x00\x00\x00*\xcbf\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\xabH\xd0\x00\x00\x00\x00" + - "-\x9bG\xe0\x00\x00\x00\x00.x\xb5\xd0\x00\x00\x00\x00/\x84d`\x00\x00\x00\x000X\xa5\xe0\x00\x00\x00\x001dF`\x00\x00\x00\x002A\xc2`\x00\x00\x00\x003D(`\x00\x00\x00\x004!\xa4`" + - "\x00\x00\x00\x005$\n`\x00\x00\x00\x006\x01\x86`\x00\x00\x00\x007z\x93`\x00\x00\x00\x007\xea\xa2\xe0\x00\x00\x00\x008\xe2|\xe0\x00\x00\x00\x009ӿ`\x00\x00\x00\x00:\xc2^\xe0\x00\x00\x00\x00" + - ";\xb3\xa1`\x00\x00\x00\x00<\xa3\x92`\x00\x00\x00\x00=\x93\x83`\x00\x00\x00\x00>\x83t`\x00\x00\x00\x00?\x98O`\x00\x00\x00\x00@cV`\x00\x00\x00\x00An\xf6\xe0\x00\x00\x00\x00BLr\xe0" + - "\x00\x00\x00\x00C\x9f#\xe0\x00\x00\x00\x00?\x8f\x06\xd0\x00\x00\x00\x00@\x7f\x05\xe0\x00\x00\x00\x00A\\\x81\xe0\x00\x00\x00\x00B^\xe7\xe0\x00\x00\x00\x00CA\xb7\xf0\x00\x00\x00\x00D-\xa6`" + + "\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F\x0e\xd9\xe0\x00\x00\x00\x00F\xe8op\x00\x00\x00\x00G\xec\x18\xe0\x00\x00\x00\x00H\xbb\x06P\x00\x00\x00\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xa0<`\x00\x00\x00\x00" + + "K\xab\xdc\xe0\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00M\x94\xf9\x9c\x00\x00\x00\x00N5\xc2P\x00\x00\x00\x00N\\\v\xe0\x00\x00\x00\x00N\x84\xdcP\x00\x00\x00\x00Ot\xdb`\x00\x00\x00\x00P[\x91\xe0" + + "\x00\x00\x00\x00QT\xbd`\x00\x00\x00\x00RD\xa0P\x00\x00\x00\x00S4\x9f`\x00\x00\x00\x00TIlP\x00\x00\x00\x00U\x15\xd2\xe0\x00\x00\x00\x00V)\\`\x00\x00\x00\x00V\xf5\xc2\xf0\x00\x00\x00\x00" + + "X\x13\xca`\x00\x00\x00\x00Xդ\xf0\x00\x00\x00\x00Y\xf3\xac`\x00\x00\x00\x00Z\xb5\x86\xf0\x00\x00\x00\x00[ӎ`\x00\x00\x00\x00\\\x9dC\xe0\x00\x00\x00\x00]\xb3bP\x00\x00\x00\x00^~w`" + + "\x00\x00\x00\x00_\x93R`\x00\x00\x00\x00`^Y`\x00\x00\x00\x00a{\x1d`\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00 \xe7\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x11LMT\x00EE" + + "ST\x00EET\x00IDT\x00IST\x00\nEET-2EEST,M3.4.4/72,M10.4.4/25\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U\xa4Zߐ\xe6\x02\x00\x00\xe6\x02\x00\x00\x12\x00\x1c\x00Asia/SrednekolymskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x193\xe4\xff\xff\xff\xff\xb5\xa3\xa8\xe0\x00\x00\x00\x00\x15'" + + "7P\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00" + + "\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;" + + "\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00" + + "\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d" + + " \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\tp\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00" + + "\x00\x008\x1b\a\xf0\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xcb\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85" + + "\xb4\xf0\x00\x00\x00\x00?\x9a\x8f\xf0\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00" + + "\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p\x00\x00\x00\x00I\xce;p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc" + + "2\xf0\x00\x00\x00\x00M\x8d\xffp\x00\x00\x00\x00TK\xac\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00\x90\x1c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00\x9a\xb0\x01\f\x00\x00\xa8\xc0\x00\bL" + + "MT\x00+10\x00+12\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU9Y\xb7\xf1\n\x01\x00\x00\n\x01\x00\x00\f\x00\x1c\x00Asia/" + + "KarachiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\v\x00\x00\x00\x06\x00\x00\x00\x1d\xff\xff\xff\xff\x89~\xfc\xa4\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\xff\xff\xff\xffݨ\xe0\xa8\x00\x00\x00\x00\x02O\xab0\x00\x00\x00\x00<\xafE\xb0\x00\x00" + + "\x00\x00=\x9f(\xa0\x00\x00\x00\x00HA\xa00\x00\x00\x00\x00I\vG\xa0\x00\x00\x00\x00I\xe4\xdd0\x00\x00\x00\x00J\xec{ \x01\x02\x01\x03\x05\x04\x05\x04\x05\x04\x05\x00\x00>\xdc\x00\x00\x00\x00MX\x00" + + "\x04\x00\x00[h\x01\n\x00\x00FP\x00\x10\x00\x00T`\x01\x14\x00\x00FP\x00\x19LMT\x00+0530\x00+0630\x00+05\x00PKST\x00PKT\x00\nPKT-5" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU'\xe2\\\xff\x9f\x00\x00\x00\x9f\x00\x00\x00\n\x00\x1c\x00Asia/KabulUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffi\x86\x9a\xa0\xff\xff\xff\xff\xd0\xf9\xd7@" + + "\x01\x02\x00\x00@\xe0\x00\x00\x00\x008@\x00\x04\x00\x00?H\x00\bLMT\x00+04\x00+0430\x00\n<+0430>-4:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vU\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\f\x00\x1c\x00Asia/KolkataUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff&\xba\x18(\xff\xff\xff\xffC\xe7\xeb0\xff\xff\xff\xff\x87\x9d\xbc\xba\xff\xff\xff" + + "\xff\xcaی(\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\x01\x02\x03\x04\x03\x04\x03\x00\x00R\xd8\x00\x00\x00\x00R\xd0\x00\x04\x00\x00KF\x00\b\x00\x00MX\x00\f" + + "\x00\x00[h\x01\x10LMT\x00HMT\x00MMT\x00IST\x00+0630\x00\nIST-5:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xdb\xfa\xb5\xbeg\x02\x00\x00" + + "g\x02\x00\x00\v\x00\x1c\x00Asia/AqtobeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x8eh\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b" + + "\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00" + + "\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b" + + "\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00" + + "\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r" + + "{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00" + + "\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e" + + "\xebP\x00\x00\x00\x00A\x84\x00\xd0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x00\x005\x98\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+06\x00\n<+05>-" + + "5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8bSnT\xa1\x00\x00\x00\xa1\x00\x00\x00\x0e\x00\x1c\x00Asia/KathmanduUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf2}\x84\x00\x00\x00" + + "\x00\x1e\x180\xa8\x01\x02\x00\x00O\xfc\x00\x00\x00\x00MX\x00\x04\x00\x00P\xdc\x00\nLMT\x00+0530\x00+0545\x00\n<+0545>-5:45\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vU[u\x99q\xf1\x02\x00\x00\xf1\x02\x00\x00\n\x00\x1c\x00Asia/TomskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xe5N\xd9\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'" + + "o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00" + + "\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;" + + "\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00" + + "\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x000d" + + "Y0\x00\x00\x00\x001]\x84\xb0\x00\x00\x00\x002r_\xb0\x00\x00\x00\x003=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dH\xb0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd*\xb0\x00\x00" + + "\x00\x008\x1b@0\x00\x00\x00\x008\xdd\f\xb0\x00\x00\x00\x009\xfb\"0\x00\x00\x00\x00:\xbc\xee\xb0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\v0\x00\x00\x00\x00<\xce\xe9\xb0\x00\x00\x00\x00=\xba" + + "\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00" + + "\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xae" + + "c\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x00\x00\x00\x00WI\xf8\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00O\xa7\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00" + + "\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xdav\x19z\x98\x00\x00\x00\x98\x00" + + "\x00\x00\f\x00\x1c\x00Asia/BahrainUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa1\xf2\x9d0\x00\x00\x00\x00\x04\x8a\x92\xc0\x01\x02\x00\x000P\x00\x00\x00\x008@\x00\x04\x00\x00*0\x00\bLMT" + + "\x00+04\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0e\x00\x1c\x00Asia/Vientia" + + "neUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00" + + "\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00BMT\x00+07\x00\n<+07>-" + + "7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\t\x00\x1c\x00Asia/AdenUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00" + + "\x00*0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/Riy" + + "adhUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU&\xe9" + + "\xd1\xd8q\x02\x00\x00q\x02\x00\x00\t\x00\x1c\x00Asia/OralUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x93\xdc\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00" + + "\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9c" + + "f\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00" + + "\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4" + + "\xdd`\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xbf`\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\xa1`\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000d\x83`\x00\x00\x00\x001]\xae\xe0\x00\x00" + + "\x00\x002r\x89\xe0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00\x00\x008\x1bj`\x00\x00\x00\x008\xdd" + + "6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00" + + "\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x06\x05\x06\x05\x06\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + + "\x06\x05\x06\x05\x02\x00\x000$\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\b\x00\x008@\x00\x10LMT\x00+03\x00+05\x00+" + + "06\x00+04\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\x11\x00\x1c\x00Asia/Kuala_Lum" + + "purUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00" + + "\x00\b\x00\x00\x00 \xff\xff\xff\xff~6S\xa3\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2H" + + "m\xf0\x00\x00\x00\x00\x16\x91\xf5\b\x01\x02\x03\x04\x05\x06\x05\a\x00\x00a]\x00\x00\x00\x00a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18" + + "\x00\x00p\x80\x00\x1cLMT\x00SMT\x00+07\x00+0720\x00+0730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "U\xef\\\xf4q\x17\x04\x00\x00\x17\x04\x00\x00\r\x00\x1c\x00Asia/DamascusUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\xa1\xf2\xabx\xff\xff\xff\xff\xa2\x81/\x80\xff\xff\xff\xff\xa3^\x9dp\xff\xff\xff" + + "\xff\xa4a\x11\x80\xff\xff\xff\xff\xa5>\x7fp\xff\xff\xff\xff\xa6@\xf3\x80\xff\xff\xff\xff\xa7\x1eap\xff\xff\xff\xff\xa8 Հ\xff\xff\xff\xff\xa9\a}\xf0\xff\xff\xff\xff\xf1\x8fR\x00\xff\xff\xff\xff\xf2[\x9c" + + "p\xff\xff\xff\xff\xf3s(\x80\xff\xff\xff\xff\xf4;~p\xff\xff\xff\xff\xf5U\xad\x80\xff\xff\xff\xff\xf6\x1fT\xf0\xff\xff\xff\xff\xf76\xe1\x00\xff\xff\xff\xff\xf7\xff6\xf0\xff\xff\xff\xff\xf9\x0e\xda\x00\xff\xff\xff" + + "\xff\xf9\xe1\xbb\xf0\xff\xff\xff\xff\xfa\xf9H\x00\xff\xff\xff\xff\xfb\xc2\xefp\xff\xff\xff\xff\xfc\xdb\xcd\x00\xff\xff\xff\xff\xfd\xa5tp\xff\xff\xff\xff\xfe\xbd\x00\x80\xff\xff\xff\xff\xff\x86\xa7\xf0\x00\x00\x00\x00\x00\x9e4" + + "\x00\x00\x00\x00\x00\x01g\xdbp\x00\x00\x00\x00\x02\x7fg\x80\x00\x00\x00\x00\x03I\x0e\xf0\x00\x00\x00\x00\x04a\xec\x80\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06C \x00\x00\x00\x00\x00\a\f\xc7p\x00\x00\x00" + + "\x00\b$S\x80\x00\x00\x00\x00\b\xed\xfa\xf0\x00\x00\x00\x00\n\x05\x87\x00\x00\x00\x00\x00\n\xcf.p\x00\x00\x00\x00\v\xe8\f\x00\x00\x00\x00\x00\f\xb1\xb3p\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0ekY" + + "\xf0\x00\x00\x00\x00\x0f\xaas\x00\x00\x00\x00\x00\x10L\x8dp\x00\x00\x00\x00\x18\xf4\xc5\x00\x00\x00\x00\x00\x19\xdbmp\x00\x00\x00\x00\x1a\xd7J\x00\x00\x00\x00\x00\x1b\xbd\xf2p\x00\x00\x00\x00\x1eU#\x00\x00\x00\x00" + + "\x00\x1f\x8a\xe5p\x00\x00\x00\x00 Gz\x00\x00\x00\x00\x00!\x89\x19\xf0\x00\x00\x00\x00\"\xe2`\x00\x00\x00\x0041h" + + "P\x00\x00\x00\x005\x1e\xc4`\x00\x00\x00\x006\x12\x9b\xd0\x00\x00\x00\x007\x02\x9a\xe0\x00\x00\x00\x007\xf3\xcfP\x00\x00\x00\x008\xe5\x1f\xe0\x00\x00\x00\x009\xd6TP\x00\x00\x00\x00:\xc6S`\x00\x00\x00" + + "\x00;\xb7\x87\xd0\x00\x00\x00\x00<\xa7\x86\xe0\x00\x00\x00\x00=\x98\xbbP\x00\x00\x00\x00>\x88\xba`\x00\x00\x00\x00?y\xee\xd0\x00\x00\x00\x00@k?`\x00\x00\x00\x00A\\s\xd0\x00\x00\x00\x00BLr" + + "\xe0\x00\x00\x00\x00C=\xa7P\x00\x00\x00\x00D-\xa6`\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F\f6\xe0\x00\x00\x00\x00G*>P\x00\x00\x00\x00G\xf5S`\x00\x00\x00\x00I\vq\xd0\x00\x00\x00" + + "\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xea\x02P\x00\x00\x00\x00K\xb5\x17`\x00\x00\x00\x00L\xc9\xe4P\x00\x00\x00\x00M\x94\xf9`\x00\x00\x00\x00N\xa9\xc6P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\"\b\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-" + + "2EEST,M3.5.5/0,M10.5.5/0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUS\xdd\\2a\x02\x00\x00a\x02\x00\x00\v\x00\x1c\x00Asia/" + + "AlmatyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "3\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19{\xdc\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00" + + "\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:" + + "\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00" + + "\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)x\x95P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00+\xb4\xb2@\x00\x00\x00\x00,\xa4\xa3" + + "@\x00\x00\x00\x00-\x94\x94@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00" + + "\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc" + + "\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x01\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00H$\x00\x00\x00\x00FP\x00\x04" + + "\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUʇ{" + + "_\xbb\x00\x00\x00\xbb\x00\x00\x00\f\x00\x1c\x00Asia/RangoonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf2}\x84\x00\x00\x00\x00\x1e\x180\xa8\x01\x02\x00\x00O\xfc\x00\x00\x00\x00MX\x00\x04\x00\x00" + - "P\xdc\x00\nLMT\x00+0530\x00+0545\x00\n<+0545>-5:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00" + - "\r\x00\x1c\x00Asia/ShanghaiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffV\xb6\x89\xd1\xff\xff\xff\xff\xa1\xf2sQ\xff\xff\xff\xff\xcb\xf2\xfc\x18\xff\xff\xff\xffњg\xf0" + + "\x01\x02\x03\x02\x00\x00Z/\x00\x00\x00\x00Z/\x00\x04\x00\x00[h\x00\b\x00\x00~\x90\x00\x0eLMT\x00RMT\x00+0630\x00+09\x00\n<+0630>-6:30\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?\xa7^\xfah\x02\x00\x00h\x02\x00\x00\v\x00\x1c\x00Asia/AtyrauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x93P\xff\xff\xff\xff\xb5\xa4\vP" + + "\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00" + + "\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0" + + "\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00" + + ")\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP" + + "\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x00" + + "8\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`" + + "\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\x06\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02" + + "\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x02\x00\x000\xb0\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x00\b\x00\x00T`\x00\f\x00\x00T`\x01\f\x00\x00FP\x01\b\x00\x008@\x00\x10LMT\x00" + + "+03\x00+05\x00+06\x00+04\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/" + + "UrumqiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xb0\xfe\xbad\x01\x00\x00R\x1c\x00\x00\x00\x00T`\x00\x04LMT\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\v" + + "Uj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x1c\x00Asia/ThimbuUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\xd5\xe6\x15t\x00\x00\x00\x00!aM\xa8\x01\x02\x00\x00T\f\x00\x00\x00\x00MX\x00" + + "\x04\x00\x00T`\x00\nLMT\x00+0530\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x1c\x00" + + "Asia/DaccaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xffi\x86\x86\xbc\xff\xff\xff\xff\xcaۆ\xb0\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xffݨҘ\x00\x00\x00\x00J;\xc4" + + "\x10\x00\x00\x00\x00K<ؐ\x01\x02\x03\x02\x04\x05\x04\x00\x00T\xc4\x00\x00\x00\x00R\xd0\x00\x04\x00\x00[h\x00\b\x00\x00MX\x00\x0e\x00\x00T`\x00\x14\x00\x00bp\x01\x18LMT\x00HMT\x00" + + "+0630\x00+0530\x00+06\x00+07\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\r\x00\x1c\x00A" + + "sia/MakassarUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xa1\xf2]\x90\xff\xff\xff\xff\xba\x16Ր\xff\xff\xff\xffˈ\x1d\x80\xff\xff\xff\xff\xd2V\xeep\x01\x02\x03\x04\x00\x00o\xf0\x00\x00\x00\x00o" + + "\xf0\x00\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\f\x00\x00p\x80\x00\x10LMT\x00MMT\x00+08\x00+09\x00WITA\x00\nWITA-8\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x1c\x00Asia/ChongqingUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff\xff\xa1y\x04" + + "\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff" + + "\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR" + + " \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00" + + "\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00" + + "q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUe\x1bb2w\x01\x00\x00w\x01\x00" + + "\x00\x0e\x00\x1c\x00Asia/AshkhabadUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x8dD\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b" + + "\xbf0\x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00" + + "\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b" + + "\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x04\x01\x03\x00\x006\xbc\x00\x00\x00\x008@\x00\x04\x00\x00T`\x01\b\x00\x00FP\x00\f\x00\x00FP\x01\fLMT\x00+04\x00+06\x00+05\x00\n<+05>-" + + "5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU0I\xc7\xde\xec\x00\x00\x00\xec\x00\x00\x00\x10\x00\x1c\x00Asia/Ho_Chi_MinhUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\x88\x8cC\x8a\xff" + + "\xff\xff\xff\x91\xa3+\n\xff\xff\xff\xff\xcd5\xe6\x80\xff\xff\xff\xff\xd1Y\xcep\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xff\xd52\xbb\x10\xff\xff\xff\xff\xe4\xb6\xe4\x80\xff\xff\xff\xff\xed/\x98\x00\x00\x00\x00\x00\n" + + "=\xc7\x00\x01\x02\x03\x04\x02\x03\x02\x03\x02\x00\x00c\xf6\x00\x00\x00\x00c\xf6\x00\x04\x00\x00bp\x00\t\x00\x00p\x80\x00\r\x00\x00~\x90\x00\x11LMT\x00PLMT\x00+07\x00+08\x00+" + + "09\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x02\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\n\x00\x1c\x00Asia/TokyoUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffe" + + "¤p\xff\xff\xff\xff\xd7>\x02p\xff\xff\xff\xff\xd7\xedY\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd;\xf0\xff\xff\xff\xff\xdb\a\x00\xf0\xff\xff\xff\xffۭ\x1d\xf0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff" + + "\xff\xff\xff\u074c\xff\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x83\x03\x00\x00\x00\x00\x8c\xa0\x01\x04\x00\x00~\x90\x00\bLMT\x00JDT\x00JST\x00\nJST-9\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xb2\xe27Yn\x01\x00\x00n\x01\x00\x00\r\x00\x1c\x00Asia/TashkentUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x83\t\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15" + + "'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00" + + "\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#" + + ";\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x01\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x00\x00@\xf7\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+07\x00+06" + + "\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUΒ\x1a\x8c\xaa\x00\x00\x00\xaa\x00\x00\x00\t\x00\x1c\x00Asia/DiliUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x92\xe6\x18\xc4" + + "\xff\xff\xff\xff˙2\xf0\x00\x00\x00\x00\v\xea0p\x00\x00\x00\x009Ù\x00\x01\x02\x01\x02\x00\x00u\xbc\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\bLMT\x00+08\x00+09\x00\n<" + + "+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x02\x95-\xad\xc4\x02\x00\x00\xc4\x02\x00\x00\f\x00\x1c\x00Asia/YerevanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x9aH" + + "\xff\xff\xff\xff\xe7\xda\fP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00" + + "\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0" + + "\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00" + + "(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p" + + "\x00\x00\x00\x000d\x91p\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00\x00\x008\x1bj`\x00\x00\x00\x00" + + "8\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00?\x9a\xf2`" + + "\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x00\x00\x00\x00BE\xdb`\x00\x00\x00\x00Cc\xf0\xe0\x00\x00\x00\x00D%\xbd`\x00\x00\x00\x00EC\xd2\xe0\x00\x00\x00\x00F\x05\x9f`\x00\x00\x00\x00" + + "G#\xb4\xe0\x00\x00\x00\x00G\xee\xbb\xe0\x00\x00\x00\x00I\x03\x96\xe0\x00\x00\x00\x00IΝ\xe0\x00\x00\x00\x00J\xe3x\xe0\x00\x00\x00\x00K\xae\x7f\xe0\x00\x00\x00\x00L̕`\x00\x00\x00\x00M\x8ea\xe0" + + "\x00\x00\x00\x00N\xacw`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00)\xb8\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+0" + + "4>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU:\x11\xea\xa2\xe5\x02\x00\x00\xe5\x02\x00\x00\t\x00\x1c\x00Asia/OmskUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xb3@\xb6\xff\xff\xff\xff\xb5" + + "\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00" + + "\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"" + + "L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x00" + + "\x00\x00\x00)x\x95P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00+\xb4\xb2@\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\x94@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/" + + "tv@\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00" + + "\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=" + + "\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00" + + "\x00\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K" + + "\xaec\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00D\xca\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`" + + "\x01\f\x00\x00bp\x00\bLMT\x00+05\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e" + + "\x00\x1c\x00Asia/ChungkingUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff\xff\xa1y\x04\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p" + "\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff" + "\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0" + "\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00" + "(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bL" + - "MT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd5ΜGp\x02\x00\x00p\x02\x00\x00\x0e\x00\x1c\x00Asia/Qyzylor" + - "daUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00" + - "\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x86\xa0\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2" + - "\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00" + - "\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\v" + - "P\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\x95P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00" + - "\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]" + - "\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00" + - "\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x00\x00\x00\x00\\\x1b\xd8" + - "\xa0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x02\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x00\x00=`\x00\x00\x00" + - "\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R\x17✳2\x04\x00\x002\x04\x00\x00\r\x00\x1c\x00Asia/Tel_AvivUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff" + - "\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00" + - "\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff\xff\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff" + - "\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10\xf7\x00\xff\xff\xff\xff\xda\xeb\xd0\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d\x00\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80" + - "\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff\xff\xff\xe1V}\x00\xff\xff\xff\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff\xff\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff" + - "\xe7\x11Ҁ\xff\xff\xff\xff\xe8&\xad\x80\xff\xff\xff\xff\xe8\xe8z\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`" + - "\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00" + - " \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0" + - "\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00" + - "._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4`\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp" + - "\x00\x00\x00\x00D,q\x00\x00\x00\x00\x00E\x1e\xf6\xf0\x00\x00\x00\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00\x00H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00" + - "J\xbe\x9c\xf0\x00\x00\x00\x00K\xab\xf9\x00\x00\x00\x00\x00L\x8c\t\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7\x80\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ" + - "\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!\x06\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00" + - "\x1c \x00\f\x00\x008@\x01\x10LMT\x00JMT\x00IDT\x00IST\x00IDDT\x00\nIST-2IDT,M3.4.4/26,M10.5.0\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xe27Yn\x01\x00\x00n\x01\x00\x00\r\x00\x1c\x00Asia/TashkentUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x83\t\xff\xff\xff\xff\xb5\xa3\xef" + - "0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00" + - "\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r" + - "\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x01\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x00\x00@\xf7\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+" + - "07\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/KuwaitUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b" + - "\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RB\x1d\xc6\x1b\x85\x00\x00\x00" + - "\x85\x00\x00\x00\v\x00\x1c\x00Asia/UrumqiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xb0\xfe\xbad\x01\x00\x00R\x1c\x00\x00\x00\x00T`\x00\x04LMT\x00+06\x00\n<+06>-6\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Re\x1bb2w\x01\x00\x00w\x01\x00\x00\x0e\x00\x1c\x00Asia/AshkhabadUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x8dD\xff\xff\xff\xff\xb5" + + "MT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU0]*\x1bj\x02\x00\x00j\x02\x00\x00\f\x00\x1c\x00Asia/Bishkek" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x05\x00" + + "\x00\x00\x10\xff\xff\xff\xff\xaa\x19~\x10\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00" + + "\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 " + + "l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00" + + "\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xbe\xa3\xc0\x00\x00\x00\x00)\xe770\x00\x00\x00\x00*ĥ \x00\x00\x00\x00+\xc7\x190\x00\x00\x00\x00,\xa4\x87 \x00\x00\x00\x00-\xa6\xfb0\x00\x00\x00\x00." + + "\x84i \x00\x00\x00\x00/\x86\xdd0\x00\x00\x00\x000dK \x00\x00\x00\x001f\xbf0\x00\x00\x00\x002Mg\xa0\x00\x00\x00\x003=\x89\xd8\x00\x00\x00\x004RV\xc8\x00\x00\x00\x005\x1dk\xd8\x00" + + "\x00\x00\x00628\xc8\x00\x00\x00\x006\xfdM\xd8\x00\x00\x00\x008\x1bUH\x00\x00\x00\x008\xdd/\xd8\x00\x00\x00\x009\xfb7H\x00\x00\x00\x00:\xbd\x11\xd8\x00\x00\x00\x00;\xdb\x19H\x00\x00\x00\x00<" + + "\xa6.X\x00\x00\x00\x00=\xba\xfbH\x00\x00\x00\x00>\x86\x10X\x00\x00\x00\x00?\x9a\xddH\x00\x00\x00\x00@e\xf2X\x00\x00\x00\x00A\x83\xf9\xc8\x00\x00\x00\x00BE\xd4X\x00\x00\x00\x00B\xfb\x92 \x01" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x03\x00\x00E\xf0\x00\x00\x00\x00F" + + "P\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + ".>[K\xab\x00\x00\x00\xab\x00\x00\x00\r\x00\x1c\x00Asia/JayapuraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\xba\x16\xc1\x98\xff\xff\xff\xff\xd0X\xb9\xf0\xff\xff\xff\xff\xf4\xb5\xa2h\x01\x02\x03\x00" + + "\x00\x83\xe8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x00\b\x00\x00~\x90\x00\x0eLMT\x00+09\x00+0930\x00WIT\x00\nWIT-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vU\x9a\xea\x18\xd4\xf8\x02\x00\x00\xf8\x02\x00\x00\x12\x00\x1c\x00Asia/YekaterinburgUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\x9b_\t'\xff\xff\xff\xff\xa1\x12\xb1\xff\xff\xff\xff\xff\xb5" + "\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xbf0\x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00" + "\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"" + "L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00" + - "\x00\x00\x00)x\xa3`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x006\xbc\x00\x00\x00\x008@\x00\x04\x00\x00T`\x01\b\x00\x00FP\x00\f\x00\x00FP" + - "\x01\fLMT\x00+04\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf0\x9cf>\xd7\x02\x00\x00\xd7\x02\x00\x00\x0e\x00\x1c\x00Asi" + - "a/KamchatkaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00@\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa7R\x96\xc4\xff\xff\xff\xff\xb5\xa3\x9a\xd0\x00\x00\x00\x00\x15')@\x00\x00\x00\x00\x16\x18]\xb0\x00\x00\x00\x00\x17\b\\\xc0\x00\x00\x00\x00\x17\xf9" + - "\x910\x00\x00\x00\x00\x18\xe9\x90@\x00\x00\x00\x00\x19\xdaİ\x00\x00\x00\x00\x1a\xcc\x15@\x00\x00\x00\x00\x1b\xbc\"`\x00\x00\x00\x00\x1c\xac\x13`\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8b\xf5`\x00\x00" + - "\x00\x00\x1f{\xe6`\x00\x00\x00\x00 k\xd7`\x00\x00\x00\x00![\xc8`\x00\x00\x00\x00\"K\xb9`\x00\x00\x00\x00#;\xaa`\x00\x00\x00\x00$+\x9b`\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v" + - "}`\x00\x00\x00\x00'\x04\xa8\xe0\x00\x00\x00\x00'\xf4\x99\xe0\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00)x@\xf0\x00\x00\x00\x00)\xd4{\xe0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4]\xe0\x00\x00" + - "\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94?\xe0\x00\x00\x00\x00.\x840\xe0\x00\x00\x00\x00/t!\xe0\x00\x00\x00\x000d\x12\xe0\x00\x00\x00\x001]>`\x00\x00\x00\x002r\x19`\x00\x00\x00\x003=" + - " `\x00\x00\x00\x004Q\xfb`\x00\x00\x00\x005\x1d\x02`\x00\x00\x00\x0061\xdd`\x00\x00\x00\x006\xfc\xe4`\x00\x00\x00\x008\x1a\xf9\xe0\x00\x00\x00\x008\xdc\xc6`\x00\x00\x00\x009\xfa\xdb\xe0\x00\x00" + - "\x00\x00:\xbc\xa8`\x00\x00\x00\x00;ڽ\xe0\x00\x00\x00\x00<\xa5\xc4\xe0\x00\x00\x00\x00=\xba\x9f\xe0\x00\x00\x00\x00>\x85\xa6\xe0\x00\x00\x00\x00?\x9a\x81\xe0\x00\x00\x00\x00@e\x88\xe0\x00\x00\x00\x00A\x83" + - "\x9e`\x00\x00\x00\x00BEj\xe0\x00\x00\x00\x00Cc\x80`\x00\x00\x00\x00D%L\xe0\x00\x00\x00\x00ECb`\x00\x00\x00\x00F\x05.\xe0\x00\x00\x00\x00G#D`\x00\x00\x00\x00G\xeeK`\x00\x00" + - "\x00\x00I\x03&`\x00\x00\x00\x00I\xce-`\x00\x00\x00\x00J\xe3\b`\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x00\x94\xbc\x00\x00\x00\x00\x9a\xb0" + - "\x00\x04\x00\x00\xb6\xd0\x01\b\x00\x00\xa8\xc0\x00\f\x00\x00\xa8\xc0\x01\fLMT\x00+11\x00+13\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\x87\xbd\xedL\xf1\x02\x00\x00\xf1\x02\x00\x00\f\x00\x1c\x00Asia/BarnaulUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xd5}\xfc\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16" + - "\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00" + - "\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$" + - "+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00" + - "\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x00/\xc7L\x80\x00\x00\x00\x000" + - "dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00" + - "\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>" + - "\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6\xc0\x00" + - "\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00\x00L" + - "\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x00\x00\x00\x00V\xf6\xea@\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x04" + - "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00N\x84\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00" + - "bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8a\x9a\x90\xf7\xd6\x02\x00\x00\xd6\x02\x00\x00\x11\x00\x1c\x00A" + - "sia/NovokuznetskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x18 \xc0\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00" + - "\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e" + - "\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00" + - "\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+" + - "\xb4\xa40\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x000dY0\x00\x00\x00\x001]\x84\xb0\x00\x00\x00\x002r_\xb0\x00" + - "\x00\x00\x003=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dH\xb0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd*\xb0\x00\x00\x00\x008\x1b@0\x00\x00\x00\x008\xdd\f\xb0\x00\x00\x00\x009" + - "\xfb\"0\x00\x00\x00\x00:\xbc\xee\xb0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\v0\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xed0\x00\x00\x00\x00?\x9a\xc80\x00\x00\x00\x00@e\xcf0\x00" + - "\x00\x00\x00A\x83\xe4\xb0\x00\x00\x00\x00BE\xb10\x00\x00\x00\x00Ccư\x00\x00\x00\x00D%\x930\x00\x00\x00\x00EC\xa8\xb0\x00\x00\x00\x00F\x05u0\x00\x00\x00\x00G#\x8a\xb0\x00\x00\x00\x00G" + - "\ue470\x00\x00\x00\x00I\x03l\xb0\x00\x00\x00\x00I\xces\xb0\x00\x00\x00\x00J\xe3N\xb0\x00\x00\x00\x00K\xaeU\xb0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x00Q\xc0\x00" + - "\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R0]*\x1bj\x02\x00\x00j\x02\x00\x00\f\x00\x1c\x00Asia/BishkekUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19~\x10\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00" + - "\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c" + - "\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00" + - "\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(\xbe\xa3\xc0\x00\x00\x00\x00)\xe770\x00\x00\x00\x00*" + - "ĥ \x00\x00\x00\x00+\xc7\x190\x00\x00\x00\x00,\xa4\x87 \x00\x00\x00\x00-\xa6\xfb0\x00\x00\x00\x00.\x84i \x00\x00\x00\x00/\x86\xdd0\x00\x00\x00\x000dK \x00\x00\x00\x001f\xbf0\x00" + - "\x00\x00\x002Mg\xa0\x00\x00\x00\x003=\x89\xd8\x00\x00\x00\x004RV\xc8\x00\x00\x00\x005\x1dk\xd8\x00\x00\x00\x00628\xc8\x00\x00\x00\x006\xfdM\xd8\x00\x00\x00\x008\x1bUH\x00\x00\x00\x008" + - "\xdd/\xd8\x00\x00\x00\x009\xfb7H\x00\x00\x00\x00:\xbd\x11\xd8\x00\x00\x00\x00;\xdb\x19H\x00\x00\x00\x00<\xa6.X\x00\x00\x00\x00=\xba\xfbH\x00\x00\x00\x00>\x86\x10X\x00\x00\x00\x00?\x9a\xddH\x00" + - "\x00\x00\x00@e\xf2X\x00\x00\x00\x00A\x83\xf9\xc8\x00\x00\x00\x00BE\xd4X\x00\x00\x00\x00B\xfb\x92 \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04" + - "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x03\x00\x00E\xf0\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05" + - "\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa7f^]@\x01\x00\x00@\x01\x00\x00\f\x00\x1c\x00Asia/Kuching" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x05\x00" + - "\x00\x00\x18\xff\xff\xff\xff\xad\x8a\x06\x90\xff\xff\xff\xff\xbagG\x88\xff\xff\xff\xff\xbf{'\x80\xff\xff\xff\xff\xbf\xf3\x1bP\xff\xff\xff\xff\xc1]\xac\x80\xff\xff\xff\xff\xc1ՠP\xff\xff\xff\xff\xc3>\xe0\x00\xff" + - "\xff\xff\xffö\xd3\xd0\xff\xff\xff\xff\xc5 \x13\x80\xff\xff\xff\xffŘ\aP\xff\xff\xff\xff\xc7\x01G\x00\xff\xff\xff\xff\xc7y:\xd0\xff\xff\xff\xff\xc8\xe3\xcc\x00\xff\xff\xff\xff\xc9[\xbf\xd0\xff\xff\xff\xff\xca" + - "\xc4\xff\x80\xff\xff\xff\xff\xcb<\xf3P\xff\xff\xff\xffˑX\x00\xff\xff\xff\xff\xd2Hm\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x00\x00gp\x00\x00\x00\x00ix\x00\x04\x00\x00u" + - "0\x01\n\x00\x00p\x80\x00\x10\x00\x00~\x90\x00\x14LMT\x00+0730\x00+0820\x00+08\x00+09\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\x84)\r\xbd\xec\x00\x00\x00\xec\x00\x00\x00\v\x00\x1c\x00Asia/SaigonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\x88\x8cC\x80\xff\xff\xff\xff\x91\xa3+\n\xff\xff\xff\xff\xcd5\xe6\x80\xff\xff\xff\xff" + - "\xd1Y\xcep\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xff\xd52\xbb\x10\xff\xff\xff\xff\xe4\xb6\xe4\x80\xff\xff\xff\xff\xed/\x98\x00\x00\x00\x00\x00\n=\xc7\x00\x01\x02\x03\x04\x02\x03\x02\x03\x02\x00\x00d\x00\x00\x00\x00" + - "\x00c\xf6\x00\x04\x00\x00bp\x00\t\x00\x00p\x80\x00\r\x00\x00~\x90\x00\x11LMT\x00PLMT\x00+07\x00+08\x00+09\x00\n<+07>-7\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9R\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\x0e\x00\x1c\x00Asia/SingaporeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00 \xff\xff\xff\xff~6S\xa3\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff" + - "\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2Hm\xf0\x00\x00\x00\x00\x16\x91\xf5\b\x01\x02\x03\x04\x05\x06\x05\a\x00\x00a]\x00\x00\x00\x00" + - "a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18\x00\x00p\x80\x00\x1cLMT\x00SMT\x00+07\x00+0720\x00+0" + - "730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R)p\x1cX\xf1\x02\x00\x00\xf1\x02\x00\x00\x10\x00\x1c\x00Asia/Novo" + - "sibirskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00C\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\x19$\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00" + - "\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|" + - ",\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00" + - "\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00+\xfe" + - "N\x00\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\x94@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00" + - "\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb" + - "0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00" + - "\x00\x00A\x83\xf2\xc0\x00\x00\x00\x00BE\xbf@\x00\x00\x00\x00Cc\xd4\xc0\x00\x00\x00\x00D%\xa1@\x00\x00\x00\x00EC\xb6\xc0\x00\x00\x00\x00F\x05\x83@\x00\x00\x00\x00G#\x98\xc0\x00\x00\x00\x00G\xee" + - "\x9f\xc0\x00\x00\x00\x00I\x03z\xc0\x00\x00\x00\x00I\u0381\xc0\x00\x00\x00\x00J\xe3\\\xc0\x00\x00\x00\x00K\xaec\xc0\x00\x00\x00\x00L\xccy@\x00\x00\x00\x00M\x8eE\xc0\x00\x00\x00\x00TK\xf30\x00\x00" + - "\x00\x00W\x93\xcc\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + - "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00M\xbc\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\fLMT\x00+06\x00+08\x00+07\x00\n" + - "<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?\xa7^\xfah\x02\x00\x00h\x02\x00\x00\v\x00\x1c\x00Asia/AtyrauUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x93P" + - "\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00" + - "\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0" + - "\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00" + - ")x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P" + - "\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x00" + - "6\xfdF\xd0\x00\x00\x00\x008\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`" + - "\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\x06\x02\x04\x02\x04\x02" + - "\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x02\x00\x000\xb0\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x00\b\x00\x00T`\x00\f\x00\x00T`\x01\f\x00\x00FP\x01\b\x00\x00" + - "8@\x00\x10LMT\x00+03\x00+05\x00+06\x00+04\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x88έ\xe2\xbd\x04\x00\x00\xbd\x04\x00\x00\t" + - "\x00\x1c\x00Asia/GazaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00s\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff}\xbdJ\xb0\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xff\xcd" + - "\xac\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff" + - "\xff\xff\xff\xe86c`\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xfa\xf0\xff\xff\xff\xff\xec\xb5m\x00\xff\xff\xff\xff\xed\xcf\u007f\xf0\xff\xff\xff\xff\xee" + - "\x97\xf2\x00\xff\xff\xff\xffﰳp\xff\xff\xff\xff\xf0y%\x80\xff\xff\xff\xff\xf1\x91\xe6\xf0\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3s\x1ap\xff\xff\xff\xff\xf4;\x8c\x80\xff\xff\xff\xff\xf5U\x9fp\xff" + - "\xff\xff\xff\xf6\x1e\x11\x80\xff\xff\xff\xff\xf76\xd2\xf0\xff\xff\xff\xff\xf7\xffE\x00\xff\xff\xff\xff\xf9\x18\x06p\xff\xff\xff\xff\xf9\xe1\xca\x00\xff\xff\xff\xff\xfa\xf99\xf0\xff\xff\xff\xff\xfb'BP\x00\x00\x00\x00\b" + - "|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00" + - "\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00#" + - " ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00" + - "\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x000" + - "\xe7\a\xe0\x00\x00\x00\x001dF`\x00\x00\x00\x002A\xc2`\x00\x00\x00\x003D(`\x00\x00\x00\x004!\xa4`\x00\x00\x00\x005$\n`\x00\x00\x00\x006\x01\x86`\x00\x00\x00\x007\x16a`\x00" + - "\x00\x00\x008\x06DP\x00\x00\x00\x008\xff}\xe0\x00\x00\x00\x009\xef`\xd0\x00\x00\x00\x00:\xdf_\xe0\x00\x00\x00\x00;\xcfB\xd0\x00\x00\x00\x00<\xbfA\xe0\x00\x00\x00\x00=\xaf$\xd0\x00\x00\x00\x00>" + - "\x9f#\xe0\x00\x00\x00\x00?\x8f\x06\xd0\x00\x00\x00\x00@\u007f\x05\xe0\x00\x00\x00\x00A\\\x81\xe0\x00\x00\x00\x00B^\xe7\xe0\x00\x00\x00\x00CA\xb7\xf0\x00\x00\x00\x00D-\xa6`\x00\x00\x00\x00E\x12\xfdP\x00" + - "\x00\x00\x00F\x0e\xd9\xe0\x00\x00\x00\x00F\xe8op\x00\x00\x00\x00G\xec\x18\xe0\x00\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xa0<`\x00\x00\x00\x00K\xad.\x9c\x00\x00\x00\x00L" + - "a\xbd\xd0\x00\x00\x00\x00M\x94\xf9\x9c\x00\x00\x00\x00N5\xc2P\x00\x00\x00\x00Ot\xdb`\x00\x00\x00\x00P[\x91\xe0\x00\x00\x00\x00QT\xbd`\x00\x00\x00\x00RD\xa0P\x00\x00\x00\x00S4\x9f`\x00" + - "\x00\x00\x00TIlP\x00\x00\x00\x00U\x15\xd2\xe0\x00\x00\x00\x00V)\\`\x00\x00\x00\x00V\xf5\xc2\xf0\x00\x00\x00\x00X\x13\xca`\x00\x00\x00\x00Xդ\xf0\x00\x00\x00\x00Y\xf3\xac`\x00\x00\x00\x00Z" + - "\xb5\x86\xf0\x00\x00\x00\x00[ӎ`\x00\x00\x00\x00\\\x9dC\xe0\x00\x00\x00\x00]\xb3bP\x00\x00\x00\x00^~w`\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00 P\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x11" + - "LMT\x00EEST\x00EET\x00IDT\x00IST\x00\nEET-2EEST,M3.4.4/48,M10.4.4/49\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9RΒ\x1a\x8c\xaa\x00\x00\x00\xaa\x00\x00\x00\t\x00\x1c\x00Asia/DiliUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x92\xe6\x18\xc4\xff\xff\xff\xff˙2\xf0\x00\x00\x00\x00\v\xea0p\x00" + - "\x00\x00\x009Ù\x00\x01\x02\x01\x02\x00\x00u\xbc\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\bLMT\x00+08\x00+09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xab\xcd\xdf\x05\xee\x02\x00\x00\xee\x02\x00\x00\n\x00\x1c\x00Asia/ChitaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xdb\xf9\xa0\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00" + - "\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18\xe9\xbap\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00\x1c\xac=" + - "\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ\x00\x00\x00" + - "\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xd3\x10\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00)Ԧ" + - "\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00\x00\x00\x00-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10\x00\x00\x00" + - "\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00\x00\x003=J\x90\x00\x00\x00\x004R%\x90\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x008\x1b$" + - "\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb\x06\x10\x00\x00\x00\x00:\xbcҐ\x00\x00\x00\x00;\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10\x00\x00\x00" + - "\x00?\x9a\xac\x10\x00\x00\x00\x00@e\xb3\x10\x00\x00\x00\x00A\x83Ȑ\x00\x00\x00\x00BE\x95\x10\x00\x00\x00\x00Cc\xaa\x90\x00\x00\x00\x00D%w\x10\x00\x00\x00\x00EC\x8c\x90\x00\x00\x00\x00F\x05Y" + - "\x10\x00\x00\x00\x00G#n\x90\x00\x00\x00\x00G\xeeu\x90\x00\x00\x00\x00I\x03P\x90\x00\x00\x00\x00I\xceW\x90\x00\x00\x00\x00J\xe32\x90\x00\x00\x00\x00K\xae9\x90\x00\x00\x00\x00L\xccO\x10\x00\x00\x00" + - "\x00M\x8e\x1b\x90\x00\x00\x00\x00TK\xc9\x00\x00\x00\x00\x00V\xf6\xce \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x01\x03\x00\x00j`\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x8c" + - "\xa0\x00\bLMT\x00+08\x00+10\x00+09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x0f\x00\x1c\x00As" + - "ia/Ulan_BatorUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x86\xd3\xeeL\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00\x00\x00\x1a\xccM\x80\x00\x00\x00\x00" + - "\x1b\xbc0p\x00\x00\x00\x00\x1c\xac/\x80\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"KՀ" + - "\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xb7\x80\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x99\x80\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\x98\xf0\x00\x00\x00\x00" + - ")Ԙ\x00\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000d \xf0" + - "\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002M=p\x00\x00\x00\x003=<\x80\x00\x00\x00\x004-\x1fp\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\r\x01p\x00\x00\x00\x00:鳠\x00\x00\x00\x00" + - ";\xb4\xac\x90\x00\x00\x00\x00<\xa4\xab\xa0\x00\x00\x00\x00=\x94\x8e\x90\x00\x00\x00\x00>\x84\x8d\xa0\x00\x00\x00\x00?tp\x90\x00\x00\x00\x00@do\xa0\x00\x00\x00\x00ATR\x90\x00\x00\x00\x00BDQ\xa0" + - "\x00\x00\x00\x00C44\x90\x00\x00\x00\x00D$3\xa0\x00\x00\x00\x00E\x1dQ\x10\x00\x00\x00\x00U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0\x00\x00\x00\x00W\xe5Cp\x01\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00d4\x00\x00\x00\x00bp\x00\x04\x00\x00" + - "~\x90\x01\b\x00\x00p\x80\x00\fLMT\x00+07\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rw\rD\an\x01\x00\x00n\x01\x00" + - "\x00\x0e\x00\x1c\x00Asia/SamarkandUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x857\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b" + - "\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00" + - "\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b" + - "\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xedP\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00" + - ">\xc9\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\fLMT\x00+04\x00+05\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9R\x03R\xda\xedU\x02\x00\x00U\x02\x00\x00\f\x00\x1c\x00Asia/NicosiaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\xa5w\x1e\xb8\x00\x00\x00\x00\t\xed\xaf\xe0\x00\x00\x00\x00\n\xdd" + - "\x92\xd0\x00\x00\x00\x00\v\xfad\xe0\x00\x00\x00\x00\f\xbe\xc6P\x00\x00\x00\x00\r\xa49`\x00\x00\x00\x00\x0e\x8a\xe1\xd0\x00\x00\x00\x00\x0f\x84\x1b`\x00\x00\x00\x00\x10uO\xd0\x00\x00\x00\x00\x11c\xfd`\x00\x00" + - "\x00\x00\x12S\xe0P\x00\x00\x00\x00\x13M\x19\xe0\x00\x00\x00\x00\x143\xc2P\x00\x00\x00\x00\x15#\xc1`\x00\x00\x00\x00\x16\x13\xa4P\x00\x00\x00\x00\x17\x03\xa3`\x00\x00\x00\x00\x17\xf3\x86P\x00\x00\x00\x00\x18\xe3" + - "\x85`\x00\x00\x00\x00\x19\xd3hP\x00\x00\x00\x00\x1a\xc3g`\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|H\xd0\x00\x00" + - "\x00\x00 lG\xe0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05" + - "\vP\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00" + - "\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002M\x91\xd0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004-s\xd0\x00\x00\x00\x005\x1d" + - "r\xe0\x00\x00\x00\x0062x\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00" + - "\x00\x1fH\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\t\x00\x1c\x00Asia/AdenUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xd5\x1b6\xb4\x01\x00\x00+\xcc\x00\x00\x00\x00*" + - "0\x00\x04LMT\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x1c\x00Asia/Dhaka" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00" + - "\x00\x00\x1c\xff\xff\xff\xffi\x86\x86\xbc\xff\xff\xff\xff\xcaۆ\xb0\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xffݨҘ\x00\x00\x00\x00J;\xc4\x10\x00\x00\x00\x00K<ؐ\x01" + - "\x02\x03\x02\x04\x05\x04\x00\x00T\xc4\x00\x00\x00\x00R\xd0\x00\x04\x00\x00[h\x00\b\x00\x00MX\x00\x0e\x00\x00T`\x00\x14\x00\x00bp\x01\x18LMT\x00HMT\x00+0630\x00+053" + - "0\x00+06\x00+07\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R)\x15II\xf3\x02\x00\x00\xf3\x02\x00\x00\r\x00\x1c\x00Asia/Sakhal" + - "inUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00" + - "\x06\x00\x00\x00\x14\xff\xff\xff\xff\x86\xf0\u0378\xff\xff\xff\xff\xd20\xb2\xf0\x00\x00\x00\x00\x15'7P\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9e" + - "P\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00" + - "\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6" + - "\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00" + - "\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\x17" + - "\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xfa\xf8\x00\x00\x00\x00\x00:\xbcĀ\x00\x00\x00" + - "\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87" + - "\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00\x00I\x03B\x80\x00\x00\x00" + - "\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00\x00\x00TK\xba\xf0\x00\x00\x00\x00V\xf6\xb2\x00\x01\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x05\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x03\x05\x03\x00" + - "\x00\x85\xc8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00+09\x00+12\x00+11\x00+10\x00\n<+11" + - ">-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RT\x81\x18G^\x02\x00\x00^\x02\x00\x00\n\x00\x1c\x00Asia/AqtauUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x94\xe0\xff\xff\xff\xff" + - "\xb5\xa3\xfd@\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0" + - "\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00" + - "#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`" + - "\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x00" + - "0d\x83`\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r\x89\xe0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M\xe0\x00\x00\x00\x006\xfdT\xe0" + - "\x00\x00\x00\x008\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00" + - ">\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\x01\x02\x04\x02\x04\x02\x04\x01\x05\x01" + - "\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x02\x00\x00/ \x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x00\f\x00\x00T`\x01\f\x00\x00FP\x01\bLMT\x00+0" + - "4\x00+05\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8a\xc1\x1eB\xb7\x00\x00\x00\xb7\x00\x00\x00\x0e\x00\x1c\x00Asia/Pyongy" + - "angUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00" + - "\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x8b\xd7\xf1\x9c\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2/ap\x00\x00\x00\x00U\xce\x02p\x00\x00\x00\x00Z\xecup\x01\x02\x03\x01\x03\x00\x00u\xe4\x00\x00\x00\x00w" + - "\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x00\x04LMT\x00KST\x00JST\x00\nKST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x17✳2\x04\x00\x002\x04\x00\x00" + - "\x0e\x00\x1c\x00Asia/JerusalemUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c" + - "\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff" + - "\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff\xff\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10\xf7\x00\xff\xff\xff\xff\xda\xeb\xd0" + - "\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d\x00\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff\xff\xff\xe1V}\x00\xff\xff\xff" + - "\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff\xff\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff\xe7\x11Ҁ\xff\xff\xff\xff\xe8&\xad\x80\xff\xff\xff\xff\xe8\xe8z" + - "\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00" + - "\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e" + - "\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00" + - "\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5" + - "\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82" + - "p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4`\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp\x00\x00\x00\x00D,q\x00\x00\x00\x00\x00E\x1e\xf6\xf0\x00\x00\x00" + - "\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00\x00H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00J\xbe\x9c\xf0\x00\x00\x00\x00K\xab\xf9\x00\x00\x00\x00\x00L\x8c\t" + - "\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7\x80\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!\x06\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\f\x00\x008@\x01\x10LMT\x00JMT\x00I" + - "DT\x00IST\x00IDDT\x00\nIST-2IDT,M3.4.4/26,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R's\x96\x1en\x01" + - "\x00\x00n\x01\x00\x00\r\x00\x1c\x00Asia/DushanbeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x83\x80\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00" + - "\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9c" + - "X\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00" + - "\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00\x00'\xf4\xee@\x00\x00\x00\x00(ʏP\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x04\x01\x00\x00@\x80\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+07\x00+06\x00\n<+05>-5\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\\\x91\x87\xbb\xf7\x00\x00\x00\xf7\x00\x00\x00\f\x00\x1c\x00Asia/ColomboUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x18\xff\xff\xff\xffV\xb6\x99$\xff\xff\xff\xff\x87\x9d\xbd\x1c\xff\xff" + - "\xff\xff\xcbZ\x1c(\xff\xff\xff\xff̕+\xa0\xff\xff\xff\xff\xd2u\x808\x00\x00\x00\x001\xa6\x00(\x00\x00\x00\x002q\x00 \x00\x00\x00\x00D?\xea(\x01\x02\x03\x04\x02\x05\x06\x02\x00\x00J\xdc\x00\x00" + - "\x00\x00J\xe4\x00\x04\x00\x00MX\x00\b\x00\x00T`\x01\x0e\x00\x00[h\x01\x12\x00\x00[h\x00\x12\x00\x00T`\x00\x0eLMT\x00MMT\x00+0530\x00+06\x00+0630\x00" + - "\n<+0530>-5:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R9Y\xb7\xf1\n\x01\x00\x00\n\x01\x00\x00\f\x00\x1c\x00Asia/KarachiUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x06\x00\x00\x00\x1d\xff" + - "\xff\xff\xff\x89~\xfc\xa4\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\xff\xff\xff\xffݨ\xe0\xa8\x00\x00\x00\x00\x02O\xab0\x00\x00\x00\x00<\xafE\xb0\x00\x00\x00\x00=\x9f(\xa0\x00\x00\x00\x00H" + - "A\xa00\x00\x00\x00\x00I\vG\xa0\x00\x00\x00\x00I\xe4\xdd0\x00\x00\x00\x00J\xec{ \x01\x02\x01\x03\x05\x04\x05\x04\x05\x04\x05\x00\x00>\xdc\x00\x00\x00\x00MX\x00\x04\x00\x00[h\x01\n\x00\x00FP" + - "\x00\x10\x00\x00T`\x01\x14\x00\x00FP\x00\x19LMT\x00+0530\x00+0630\x00+05\x00PKST\x00PKT\x00\nPKT-5\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R恸\x1e\x00\x01\x00\x00\x00\x01\x00\x00\x11\x00\x1c\x00Asia/Kuala_LumpurUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00 \xff\xff\xff\xff~6U\xaa\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff" + - "\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2Hm\xf0\x00\x00\x00\x00\x16\x91\xf5\b\x01\x02\x03\x04\x05\x06\x05\a\x00\x00_V\x00\x00\x00\x00" + - "a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18\x00\x00p\x80\x00\x1cLMT\x00SMT\x00+07\x00+0720\x00+0" + - "730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Re\x1bb2w\x01\x00\x00w\x01\x00\x00\r\x00\x1c\x00Asia/Ashg" + - "abatUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00" + - "\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x8dD\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xbf0\x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18" + - "\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00" + - "\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'" + - "\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x00\x006\xbc\x00\x00\x00\x00" + - "8@\x00\x04\x00\x00T`\x01\b\x00\x00FP\x00\f\x00\x00FP\x01\fLMT\x00+04\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\f\x00\x1c\x00Asia/KolkataUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff&\xba\x18(\xff\xff\xff\xffC\xe7\xeb0\xff\xff\xff\xff\x87\x9d\xbc\xba\xff\xff\xff\xff" + - "\xcaی(\xff\xff\xff\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\x01\x02\x03\x04\x03\x04\x03\x00\x00R\xd8\x00\x00\x00\x00R\xd0\x00\x04\x00\x00KF\x00\b\x00\x00MX\x00\f\x00" + - "\x00[h\x01\x10LMT\x00HMT\x00MMT\x00IST\x00+0630\x00\nIST-5:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RB\x1d\xc6\x1b\x85\x00\x00\x00\x85" + - "\x00\x00\x00\f\x00\x1c\x00Asia/KashgarUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xb0\xfe\xbad\x01\x00\x00R\x1c\x00\x00\x00\x00T`\x00\x04LMT\x00+06\x00\n<+06>-6\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xba\xa3b\xc1R\x02\x00\x00R\x02\x00\x00\t\x00\x1c\x00Asia/HovdUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x86\xd3\xfc\x94\x00\x00\x00\x00\x0f\v\xea\xa0\x00\x00" + - "\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbc>\x80\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c \x80\x00\x00\x00\x00\x1e\x8c\x1f\x90\x00\x00\x00\x00\x1f|" + - "\x02\x80\x00\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xe4\x80\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;ƀ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xa8\x80\x00\x00\x00\x00&\v\xa7\x90\x00\x00" + - "\x00\x00'\x04\xc5\x00\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ĉ\x00\x00\x00\x00\x00+\xb4\x88\x10\x00\x00\x00\x00,\xa4k\x00\x00\x00\x00\x00-\x94" + - "j\x10\x00\x00\x00\x00.\x84M\x00\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d/\x00\x00\x00\x00\x001]h\x90\x00\x00\x00\x002MK\x80\x00\x00\x00\x003=J\x90\x00\x00\x00\x004--\x80\x00\x00" + - "\x00\x005\x1d,\x90\x00\x00\x00\x006\r\x0f\x80\x00\x00\x00\x00:\xe9\xc1\xb0\x00\x00\x00\x00;\xb4\xba\xa0\x00\x00\x00\x00<\xa4\xb9\xb0\x00\x00\x00\x00=\x94\x9c\xa0\x00\x00\x00\x00>\x84\x9b\xb0\x00\x00\x00\x00?t" + - "~\xa0\x00\x00\x00\x00@d}\xb0\x00\x00\x00\x00AT`\xa0\x00\x00\x00\x00BD_\xb0\x00\x00\x00\x00C4B\xa0\x00\x00\x00\x00D$A\xb0\x00\x00\x00\x00E\x1d_ \x00\x00\x00\x00U\x15\xa8\xb0\x00\x00" + - "\x00\x00V\x05o\x80\x00\x00\x00\x00V\xf5\x8a\xb0\x00\x00\x00\x00W\xe5Q\x80\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00U\xec\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\fLMT\x00+06\x00+08\x00+07\x00\n<+07>-7" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x03\x87\xb3<\xe8\x02\x00\x00\xe8\x02\x00\x00\t\x00\x1c\x00Asia/BakuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x95D\xff\xff\xff\xff\xe7\xda\fP\x00" + - "\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b" + - "\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00" + - "\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)" + - "\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x001]\xd9\x10\x00\x00\x00\x002r\xb4\x10\x00\x00\x00\x003=\xad\x00\x00\x00\x00\x004R\x88\x00\x00\x00\x00\x005\x1d\x8f\x00\x00\x00\x00\x0062j\x00\x00" + - "\x00\x00\x006\xfdq\x00\x00\x00\x00\x008\x1b\x86\x80\x00\x00\x00\x008\xddS\x00\x00\x00\x00\x009\xfbh\x80\x00\x00\x00\x00:\xbd5\x00\x00\x00\x00\x00;\xdbJ\x80\x00\x00\x00\x00<\xa6Q\x80\x00\x00\x00\x00=" + - "\xbb,\x80\x00\x00\x00\x00>\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%ـ\x00" + - "\x00\x00\x00EC\xef\x00\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00\x00\x00K" + - "\xae\x9c\x00\x00\x00\x00\x00Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x00\x00\x00\x00N\xac\x93\x80\x00\x00\x00\x00On`\x00\x00\x00\x00\x00P\x8cu\x80\x00\x00\x00\x00QW|\x80\x00\x00\x00\x00RlW\x80\x00" + - "\x00\x00\x00S7^\x80\x00\x00\x00\x00TL9\x80\x00\x00\x00\x00U\x17@\x80\x00\x00\x00\x00V,\x1b\x80\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00.\xbc\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00" + - "\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x83g\x95M\a\x03\x00\x00\a\x03\x00\x00\r\x00" + - "\x1c\x00Asia/KhandygaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\b\x00\x00\x00\x14\xff\xff\xff\xff\xa1\xdb\xe4\xeb\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18\x87\xe0\x00\x00\x00\x00\x17\b\x86\xf0\x00\x00" + - "\x00\x00\x17\xf9\xbb`\x00\x00\x00\x00\x18\xe9\xbap\x00\x00\x00\x00\x19\xda\xee\xe0\x00\x00\x00\x00\x1a\xcc?p\x00\x00\x00\x00\x1b\xbcL\x90\x00\x00\x00\x00\x1c\xac=\x90\x00\x00\x00\x00\x1d\x9c.\x90\x00\x00\x00\x00\x1e\x8c" + - "\x1f\x90\x00\x00\x00\x00\x1f|\x10\x90\x00\x00\x00\x00 l\x01\x90\x00\x00\x00\x00![\xf2\x90\x00\x00\x00\x00\"K\xe3\x90\x00\x00\x00\x00#;Ԑ\x00\x00\x00\x00$+Ő\x00\x00\x00\x00%\x1b\xb6\x90\x00\x00" + - "\x00\x00&\v\xa7\x90\x00\x00\x00\x00'\x04\xd3\x10\x00\x00\x00\x00'\xf4\xc4\x10\x00\x00\x00\x00(\xe4\xc3 \x00\x00\x00\x00)xk \x00\x00\x00\x00)Ԧ\x10\x00\x00\x00\x00*ė\x10\x00\x00\x00\x00+\xb4" + - "\x88\x10\x00\x00\x00\x00,\xa4y\x10\x00\x00\x00\x00-\x94j\x10\x00\x00\x00\x00.\x84[\x10\x00\x00\x00\x00/tL\x10\x00\x00\x00\x000d=\x10\x00\x00\x00\x001]h\x90\x00\x00\x00\x002rC\x90\x00\x00" + - "\x00\x003=J\x90\x00\x00\x00\x004R%\x90\x00\x00\x00\x005\x1d,\x90\x00\x00\x00\x0062\a\x90\x00\x00\x00\x006\xfd\x0e\x90\x00\x00\x00\x008\x1b$\x10\x00\x00\x00\x008\xdc\xf0\x90\x00\x00\x00\x009\xfb" + - "\x06\x10\x00\x00\x00\x00:\xbcҐ\x00\x00\x00\x00;\xda\xe8\x10\x00\x00\x00\x00<\xa5\xef\x10\x00\x00\x00\x00=\xba\xca\x10\x00\x00\x00\x00>\x85\xd1\x10\x00\x00\x00\x00?\x9a\xac\x10\x00\x00\x00\x00?\xf2\xe4p\x00\x00" + - "\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D%i\x00\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#" + - "`\x80\x00\x00\x00\x00G\xeeg\x80\x00\x00\x00\x00I\x03B\x80\x00\x00\x00\x00I\xceI\x80\x00\x00\x00\x00J\xe3$\x80\x00\x00\x00\x00K\xae+\x80\x00\x00\x00\x00L\xccA\x00\x00\x00\x00\x00M\x8e\r\x80\x00\x00" + - "\x00\x00Nn\x02P\x00\x00\x00\x00TK\xc9\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\x06\x03\x00\x00\u007f\x15\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\f\x00\x00\x9a\xb0\x01\x10\x00\x00\x8c" + - "\xa0\x00\b\x00\x00\x9a\xb0\x00\x10LMT\x00+08\x00+10\x00+09\x00+11\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9a\xea\x18\xd4\xf8\x02\x00\x00" + - "\xf8\x02\x00\x00\x12\x00\x1c\x00Asia/YekaterinburgUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\x9b_\t'\xff\xff\xff\xff\xa1\x12\xb1\xff\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b" + - "\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xbf0\x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00" + - "\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f" + - "\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00" + - "\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000du" + - "P\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00" + - "\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\t" + - "P\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x00\x00\x00\x00BE\xcdP\x00\x00\x00\x00Cc\xe2\xd0\x00\x00\x00\x00D%\xafP\x00\x00\x00\x00EC\xc4\xd0\x00\x00\x00" + - "\x00F\x05\x91P\x00\x00\x00\x00G#\xa6\xd0\x00\x00\x00\x00G\xee\xad\xd0\x00\x00\x00\x00I\x03\x88\xd0\x00\x00\x00\x00IΏ\xd0\x00\x00\x00\x00J\xe3j\xd0\x00\x00\x00\x00K\xaeq\xd0\x00\x00\x00\x00L̇" + - "P\x00\x00\x00\x00M\x8eS\xd0\x00\x00\x00\x00TL\x01@\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x04\x00\x008\xd9\x00\x00\x00\x004\xc1\x00\x04\x00\x008@\x00\b\x00\x00T`\x01\f\x00\x00FP\x00\x10\x00\x00FP\x01\x10\x00" + - "\x00T`\x00\fLMT\x00PMT\x00+04\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00" + - "\f\x00\x1c\x00Asia/RangoonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffV\xb6\x89\xd1\xff\xff\xff\xff\xa1\xf2sQ\xff\xff\xff\xff\xcb\xf2\xfc\x18\xff\xff\xff\xffњg\xf0\x01\x02\x03\x02\x00\x00Z/\x00" + - "\x00\x00\x00Z/\x00\x04\x00\x00[h\x00\b\x00\x00~\x90\x00\x0eLMT\x00RMT\x00+0630\x00+09\x00\n<+0630>-6:30\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9Rǯ\xdf\x1c\xee\x00\x00\x00\xee\x00\x00\x00\v\x00\x1c\x00Asia/ManilaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\x14\xe1\xdc\x10\xff\xff\xff\xff{\x1f?\x90\xff\xff\xff\xff\xc1\x9c\xf4\x80\xff" + - "\xff\xff\xff\xc2\x160p\xff\xff\xff\xff\xcb\xf2\xe7\x00\xff\xff\xff\xffЩ%p\xff\xff\xff\xff\xe2l9\x00\xff\xff\xff\xff\xe2բ\xf0\x00\x00\x00\x00\x0fuF\x80\x00\x00\x00\x00\x10fz\xf0\x01\x03\x02\x03\x04" + - "\x03\x02\x03\x02\x03\xff\xff\x1f\xf0\x00\x00\x00\x00qp\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\fLMT\x00PDT\x00PST\x00JST\x00\nPST-8\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x1c\x00Asia/MacaoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x85i[\x8e\xff\xff\xff\xff\xcbGu\xf0\xff\xff\xff" + - "\xff\xcb\xf2\xca\xe0\xff\xff\xff\xff\xcc\xfb\xbaP\xff\xff\xff\xff\xcd\xd3\xfe`\xff\xff\xff\xffΝ\xa5\xd0\xff\xff\xff\xff\xd2azp\xff\xff\xff\xff\xd3x\xf8p\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5K\xab" + - "p\xff\xff\xff\xff\xd6tL\xf0\xff\xff\xff\xff\xd7?S\xf0\xff\xff\xff\xff\xd8/D\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xda\r\xd5p\xff\xff\xff\xff\xda\xd8\xdcp\xff\xff\xff\xff\xdb\xed\xb7p\xff\xff\xff" + - "\xffܸ\xbep\xff\xff\xff\xff\xdd\xce\xea\xf0\xff\xff\xff\xffޡ\xda\xf0\xff\xff\xff\xff߶\xb5\xf0\xff\xff\xff\xff\xe0\x81\xbc\xf0\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe3vy" + - "\xf0\xff\xff\xff\xff\xe4/\v\xf0\xff\xff\xff\xff\xe5_\x96p\xff\xff\xff\xff\xe6\x0e\xed\xf0\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff" + - "\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3" + - "\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15S\x18\xff\xff\xff" + - "\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf55\x18\xff\xff\xff\xff\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#" + - "\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00" + - "\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b9" + - "8\x00\x00\x00\x00\x12ol\xa8\x01\x03\x02\x03\x02\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + - "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x00\x00jr\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\x10LMT\x00CST\x00+1" + - "0\x00+09\x00CDT\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RS\xdd\\2a\x02\x00\x00a\x02\x00\x00\v\x00\x1c\x00Asia/AlmatyUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x05\x00\x00\x00" + - "\x10\xff\xff\xff\xff\xaa\x19{\xdc\xff\xff\xff\xff\xb5\xa3\xef0\x00\x00\x00\x00\x15'}\xa0\x00\x00\x00\x00\x16\x18\xb2\x10\x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xe5\x90\x00\x00\x00\x00\x18\xe9\xe4\xa0\x00\x00\x00" + - "\x00\x19\xdb\x19\x10\x00\x00\x00\x00\x1a\xcci\xa0\x00\x00\x00\x00\x1b\xbcv\xc0\x00\x00\x00\x00\x1c\xacg\xc0\x00\x00\x00\x00\x1d\x9cX\xc0\x00\x00\x00\x00\x1e\x8cI\xc0\x00\x00\x00\x00\x1f|:\xc0\x00\x00\x00\x00 l+" + - "\xc0\x00\x00\x00\x00!\\\x1c\xc0\x00\x00\x00\x00\"L\r\xc0\x00\x00\x00\x00#;\xfe\xc0\x00\x00\x00\x00$+\xef\xc0\x00\x00\x00\x00%\x1b\xe0\xc0\x00\x00\x00\x00&\v\xd1\xc0\x00\x00\x00\x00'\x04\xfd@\x00\x00\x00" + - "\x00'\xf4\xee@\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)x\x95P\x00\x00\x00\x00)\xd4\xd0@\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00+\xb4\xb2@\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\x94" + - "@\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dg@\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x002rm\xc0\x00\x00\x00\x003=t\xc0\x00\x00\x00\x004RO\xc0\x00\x00\x00" + - "\x005\x1dV\xc0\x00\x00\x00\x00621\xc0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1bN@\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb0@\x00\x00\x00\x00:\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x12" + - "@\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xf4@\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xd6@\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00A\x83\xf2\xc0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00H$\x00\x00\x00\x00FP\x00\x04\x00\x00bp\x01\b\x00\x00" + - "T`\x00\f\x00\x00T`\x01\fLMT\x00+05\x00+07\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RѾ\xa8\xc7u\x02\x00\x00u\x02\x00" + - "\x00\f\x00\x1c\x00Asia/TbilisiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00\x00\x00\x06\x00\x00\x00\x15\xff\xff\xff\xffV\xb6\xba\x01\xff\xff\xff\xff\xaa\x19\x9a\x01\xff\xff\xff\xff\xe7\xda\fP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0" + - "\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00" + - "\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0" + - "\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xc1@\x00\x00\x00\x00" + - "+\xb4\xc0P\x00\x00\x00\x00,\xa4\xa3@\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x85@\x00\x00\x00\x00/tv@\x00\x00\x00\x000dY0\x00\x00\x00\x001]\x92\xc0\x00\x00\x00\x003=f\xb0" + - "\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dV\xc0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd8\xc0\x00\x00\x00\x008\x1b@0\x00\x00\x00\x008\xdd\x1a\xc0\x00\x00\x00\x009\xfb\"0\x00\x00\x00\x00" + - ":\xbc\xfc\xc0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\x19@\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xfb@\x00\x00\x00\x00?\x9a\xc80\x00\x00\x00\x00@e\xdd@\x00\x00\x00\x00@\xddǰ" + - "\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x05\x02\x05\x02\x05\x04\x03\x04\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x05\x02\x04\x00\x00)\xff\x00\x00\x00\x00)\xff\x00\x04\x00\x00*0\x00\t\x00\x00FP\x01\r\x00\x008@\x00\x11\x00\x008@\x01\x11LMT\x00TBMT\x00+03\x00+05" + - "\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0f\x00\x1c\x00Asia/Phnom_PenhU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00" + - "\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00BMT\x00+07\x00\n<+07>-7\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x1c\x00Asia/MacauUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x85i[\x8e\xff\xff\xff\xff\xcbGu\xf0\xff\xff" + - "\xff\xff\xcb\xf2\xca\xe0\xff\xff\xff\xff\xcc\xfb\xbaP\xff\xff\xff\xff\xcd\xd3\xfe`\xff\xff\xff\xffΝ\xa5\xd0\xff\xff\xff\xff\xd2azp\xff\xff\xff\xff\xd3x\xf8p\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5K" + - "\xabp\xff\xff\xff\xff\xd6tL\xf0\xff\xff\xff\xff\xd7?S\xf0\xff\xff\xff\xff\xd8/D\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xda\r\xd5p\xff\xff\xff\xff\xda\xd8\xdcp\xff\xff\xff\xff\xdb\xed\xb7p\xff\xff" + - "\xff\xffܸ\xbep\xff\xff\xff\xff\xdd\xce\xea\xf0\xff\xff\xff\xffޡ\xda\xf0\xff\xff\xff\xff߶\xb5\xf0\xff\xff\xff\xff\xe0\x81\xbc\xf0\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe3v" + - "y\xf0\xff\xff\xff\xff\xe4/\v\xf0\xff\xff\xff\xff\xe5_\x96p\xff\xff\xff\xff\xe6\x0e\xed\xf0\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff" + - "\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W" + - "\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15S\x18\xff\xff" + - "\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf55\x18\xff\xff\xff\xff\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e" + - "#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00" + - "\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b" + - "98\x00\x00\x00\x00\x12ol\xa8\x01\x03\x02\x03\x02\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + - "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x00\x00jr\x00\x00\x00\x00p\x80\x00\x04\x00\x00\x8c\xa0\x01\b\x00\x00~\x90\x00\f\x00\x00~\x90\x01\x10LMT\x00CST\x00+" + - "10\x00+09\x00CDT\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe4_P\x18\xef\x02\x00\x00\xef\x02\x00\x00\f\x00\x1c\x00Asia/Magadan" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\x06\x00" + - "\x00\x00\x10\xff\xff\xff\xff\xaa\x196\xa0\xff\xff\xff\xff\xb5\xa3\xa8\xe0\x00\x00\x00\x00\x15'7P\x00\x00\x00\x00\x16\x18k\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00" + - "\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 " + - "k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00" + - "\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-" + - "\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Lp\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\tp\x00" + - "\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\a\xf0\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;" + - "\xda\xcb\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x8f\xf0\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00BEx\xf0\x00" + - "\x00\x00\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p\x00\x00\x00\x00I" + - "\xce;p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x00\x00\x00\x00TK\xac\xe0\x00\x00\x00\x00W\x1b\x9c\x00\x01\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x01\x03\x00\x00\x8d" + - "`\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00\x9a\xb0\x01\f\x00\x00\xa8\xc0\x00\bLMT\x00+10\x00+12\x00+11\x00\n<+11>-11\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xdav\x19z\x98\x00\x00\x00\x98\x00\x00\x00\n\x00\x1c\x00Asia/QatarUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\xa1\xf2\x9d0\x00\x00\x00\x00\x04\x8a\x92\xc0\x01\x02" + - "\x00\x000P\x00\x00\x00\x008@\x00\x04\x00\x00*0\x00\bLMT\x00+04\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9a\x1a\xdc\xca\xdc\x00\x00" + - "\x00\xdc\x00\x00\x00\r\x00\x1c\x00Asia/CalcuttaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff&\xba\x18(\xff\xff\xff\xffC\xe7\xeb0\xff\xff\xff\xff\x87\x9d\xbc\xba\xff\xff\xff\xff\xcaی(\xff\xff\xff" + - "\xff\xcc\x05q\x18\xff\xff\xff\xff̕2\xa8\xff\xff\xff\xff\xd2t\x12\x98\x01\x02\x03\x04\x03\x04\x03\x00\x00R\xd8\x00\x00\x00\x00R\xd0\x00\x04\x00\x00KF\x00\b\x00\x00MX\x00\f\x00\x00[h\x01\x10LM" + - "T\x00HMT\x00MMT\x00IST\x00+0630\x00\nIST-5:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x10\x00\x1c\x00" + - "Asia/UlaanbaatarUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x86\xd3\xeeL\x00\x00\x00\x00\x0f\vܐ\x00\x00\x00\x00\x18\xe9Ȁ\x00\x00\x00\x00\x19\xda\xfc\xf0\x00\x00\x00\x00\x1a\xccM\x80\x00" + - "\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac/\x80\x00\x00\x00\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x11\x80\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xf3\x80\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"" + - "KՀ\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xb7\x80\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x99\x80\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xe4\x98\xf0\x00" + - "\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000" + - "d \xf0\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002M=p\x00\x00\x00\x003=<\x80\x00\x00\x00\x004-\x1fp\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\r\x01p\x00\x00\x00\x00:鳠\x00" + - "\x00\x00\x00;\xb4\xac\x90\x00\x00\x00\x00<\xa4\xab\xa0\x00\x00\x00\x00=\x94\x8e\x90\x00\x00\x00\x00>\x84\x8d\xa0\x00\x00\x00\x00?tp\x90\x00\x00\x00\x00@do\xa0\x00\x00\x00\x00ATR\x90\x00\x00\x00\x00B" + - "DQ\xa0\x00\x00\x00\x00C44\x90\x00\x00\x00\x00D$3\xa0\x00\x00\x00\x00E\x1dQ\x10\x00\x00\x00\x00U\x15\x9a\xa0\x00\x00\x00\x00V\x05ap\x00\x00\x00\x00V\xf5|\xa0\x00\x00\x00\x00W\xe5Cp\x01" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00d4\x00\x00\x00\x00bp\x00" + - "\x04\x00\x00~\x90\x01\b\x00\x00p\x80\x00\fLMT\x00+07\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa4Zߐ\xe6\x02\x00\x00" + - "\xe6\x02\x00\x00\x12\x00\x1c\x00Asia/SrednekolymskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x193\xe4\xff\xff\xff\xff\xb5\xa3\xa8\xe0\x00\x00\x00\x00\x15'7P\x00\x00\x00\x00\x16\x18k" + - "\xc0\x00\x00\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00" + - "\x00\x1d\x9c\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9" + - "p\x00\x00\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00" + - "\x00*\xc4z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]L" + - "p\x00\x00\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\tp\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\a\xf0\x00\x00\x00" + - "\x008\xdc\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xcb\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x8f" + - "\xf0\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00" + - "\x00G#Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p\x00\x00\x00\x00I\xce;p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xff" + - "p\x00\x00\x00\x00TK\xac\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00\x90\x1c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa8\xc0\x01\b\x00\x00\x9a\xb0\x00\f\x00\x00\x9a\xb0\x01\f\x00\x00\xa8\xc0\x00\bLMT\x00+10\x00+1" + - "2\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xdb\xfa\xb5\xbeg\x02\x00\x00g\x02\x00\x00\v\x00\x1c\x00Asia/AqtobeUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x10" + - "\xff\xff\xff\xff\xaa\x19\x8eh\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00" + - "\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0" + - "\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00" + - "'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P" + - "\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x00" + - "5\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P" + - "\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x005\x98\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T" + - "`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+06\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rw\x86\x8d^\x03\x03" + - "\x00\x00\x03\x03\x00\x00\r\x00\x1c\x00Asia/Ust-NeraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x00\x00\x00\b\x00\x00\x00\x18\xff\xff\xff\xff\xa1\xdbݺ\xff\xff\xff\xff\xb5\xa3\xc5\x00\x00\x00\x00\x00\x15'Sp\x00\x00\x00\x00\x16\x18k\xc0\x00\x00" + - "\x00\x00\x17\bj\xd0\x00\x00\x00\x00\x17\xf9\x9f@\x00\x00\x00\x00\x18\xe9\x9eP\x00\x00\x00\x00\x19\xda\xd2\xc0\x00\x00\x00\x00\x1a\xcc#P\x00\x00\x00\x00\x1b\xbc0p\x00\x00\x00\x00\x1c\xac!p\x00\x00\x00\x00\x1d\x9c" + - "\x12p\x00\x00\x00\x00\x1e\x8c\x03p\x00\x00\x00\x00\x1f{\xf4p\x00\x00\x00\x00 k\xe5p\x00\x00\x00\x00![\xd6p\x00\x00\x00\x00\"K\xc7p\x00\x00\x00\x00#;\xb8p\x00\x00\x00\x00$+\xa9p\x00\x00" + - "\x00\x00%\x1b\x9ap\x00\x00\x00\x00&\v\x8bp\x00\x00\x00\x00'\x04\xb6\xf0\x00\x00\x00\x00'\xf4\xa7\xf0\x00\x00\x00\x00(\xe4\xa7\x00\x00\x00\x00\x00)xO\x00\x00\x00\x00\x00)ԉ\xf0\x00\x00\x00\x00*\xc4" + - "z\xf0\x00\x00\x00\x00+\xb4k\xf0\x00\x00\x00\x00,\xa4\\\xf0\x00\x00\x00\x00-\x94M\xf0\x00\x00\x00\x00.\x84>\xf0\x00\x00\x00\x00/t/\xf0\x00\x00\x00\x000d \xf0\x00\x00\x00\x001]Lp\x00\x00" + - "\x00\x002r'p\x00\x00\x00\x003=.p\x00\x00\x00\x004R\tp\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xebp\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\a\xf0\x00\x00\x00\x008\xdc" + - "\xd4p\x00\x00\x00\x009\xfa\xe9\xf0\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xcb\xf0\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xad\xf0\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x8f\xf0\x00\x00" + - "\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xacp\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x8ep\x00\x00\x00\x00D%Z\xf0\x00\x00\x00\x00ECpp\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#" + - "Rp\x00\x00\x00\x00G\xeeYp\x00\x00\x00\x00I\x034p\x00\x00\x00\x00I\xce;p\x00\x00\x00\x00J\xe3\x16p\x00\x00\x00\x00K\xae\x1dp\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x00\x00" + - "\x00\x00Nm\xf4@\x00\x00\x00\x00TK\xba\xf0\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x05\x06\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\a\x03\x06\x00\x00\x86F\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00\x9a\xb0\x00\f\x00\x00\xa8\xc0\x01\x10\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0" + - "\x00\x14\x00\x00\xa8\xc0\x00\x10LMT\x00+08\x00+09\x00+11\x00+12\x00+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RL\xe0\x91y" + - "\xe5\x02\x00\x00\xe5\x02\x00\x00\x10\x00\x1c\x00Asia/KrasnoyarskUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf9\r\xf2\xff\xff\xff\xff\xb5\xa3\xe1 \x00\x00\x00\x00\x15'o\x90\x00\x00\x00\x00\x16" + - "\x18\xa4\x00\x00\x00\x00\x00\x17\b\xa3\x10\x00\x00\x00\x00\x17\xf9׀\x00\x00\x00\x00\x18\xe9\u0590\x00\x00\x00\x00\x19\xdb\v\x00\x00\x00\x00\x00\x1a\xcc[\x90\x00\x00\x00\x00\x1b\xbch\xb0\x00\x00\x00\x00\x1c\xacY\xb0\x00" + - "\x00\x00\x00\x1d\x9cJ\xb0\x00\x00\x00\x00\x1e\x8c;\xb0\x00\x00\x00\x00\x1f|,\xb0\x00\x00\x00\x00 l\x1d\xb0\x00\x00\x00\x00!\\\x0e\xb0\x00\x00\x00\x00\"K\xff\xb0\x00\x00\x00\x00#;\xf0\xb0\x00\x00\x00\x00$" + - "+\xe1\xb0\x00\x00\x00\x00%\x1bҰ\x00\x00\x00\x00&\vð\x00\x00\x00\x00'\x04\xef0\x00\x00\x00\x00'\xf4\xe00\x00\x00\x00\x00(\xe4\xdf@\x00\x00\x00\x00)x\x87@\x00\x00\x00\x00)\xd4\xc20\x00" + - "\x00\x00\x00*ij0\x00\x00\x00\x00+\xb4\xa40\x00\x00\x00\x00,\xa4\x950\x00\x00\x00\x00-\x94\x860\x00\x00\x00\x00.\x84w0\x00\x00\x00\x00/th0\x00\x00\x00\x000dY0\x00\x00\x00\x001" + - "]\x84\xb0\x00\x00\x00\x002r_\xb0\x00\x00\x00\x003=f\xb0\x00\x00\x00\x004RA\xb0\x00\x00\x00\x005\x1dH\xb0\x00\x00\x00\x0062#\xb0\x00\x00\x00\x006\xfd*\xb0\x00\x00\x00\x008\x1b@0\x00" + - "\x00\x00\x008\xdd\f\xb0\x00\x00\x00\x009\xfb\"0\x00\x00\x00\x00:\xbc\xee\xb0\x00\x00\x00\x00;\xdb\x040\x00\x00\x00\x00<\xa6\v0\x00\x00\x00\x00=\xba\xe60\x00\x00\x00\x00>\x85\xed0\x00\x00\x00\x00?" + - "\x9a\xc80\x00\x00\x00\x00@e\xcf0\x00\x00\x00\x00A\x83\xe4\xb0\x00\x00\x00\x00BE\xb10\x00\x00\x00\x00Ccư\x00\x00\x00\x00D%\x930\x00\x00\x00\x00EC\xa8\xb0\x00\x00\x00\x00F\x05u0\x00" + - "\x00\x00\x00G#\x8a\xb0\x00\x00\x00\x00G\ue470\x00\x00\x00\x00I\x03l\xb0\x00\x00\x00\x00I\xces\xb0\x00\x00\x00\x00J\xe3N\xb0\x00\x00\x00\x00K\xaeU\xb0\x00\x00\x00\x00L\xcck0\x00\x00\x00\x00M" + - "\x8e7\xb0\x00\x00\x00\x00TK\xe5 \x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x03\x00\x00W\x0e\x00\x00\x00\x00T`\x00\x04\x00\x00p\x80\x01\b\x00\x00bp\x00\f\x00\x00bp\x01\f\x00\x00p\x80\x00\bLMT\x00+06\x00" + - "+08\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\n\x00\x1c\x00Asia/SeoulUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x06\x00\x00\x00\x10" + - "\xff\xff\xff\xff\x8b\xd7\xf0x\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2C'\xf0\xff\xff\xff\xff\xd7e\x8fp\xff\xff\xff\xff\xd7\xee\x9d`\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd-\xe0\xff\xff\xff\xff" + - "\xda\u05ca\xf0\xff\xff\xff\xffۭ\x0f\xe0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xf1\xe0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe4k\xb7\xf8\xff\xff\xff\xff\xe5\x13\x18h\xff\xff\xff\xff\xe6b\x03x" + - "\xff\xff\xff\xff\xe7\x11L\xe8\xff\xff\xff\xff\xe8/px\xff\xff\xff\xff\xe8\xe7\xf4h\xff\xff\xff\xff\xea\x0fRx\xff\xff\xff\xff\xea\xc7\xd6h\xff\xff\xff\xff\xeb\xef4x\xff\xff\xff\xff째h\xff\xff\xff\xff" + - "\xed\xcf\x16x\xff\xff\xff\xff\ue1dah\xff\xff\xff\xff\xf05qx\x00\x00\x00\x00 \xa3`\x90\x00\x00\x00\x00!ng\x90\x00\x00\x00\x00\"\x83B\x90\x00\x00\x00\x00#NI\x90\x01\x02\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x04\x03\x04\x03\x04\x00\x00w\b\x00\x00\x00\x00w\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x01\f\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x01\fLMT" + - "\x00KST\x00JST\x00KDT\x00\nKST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Ry\x19\xe0N\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x1c\x00Asia/Brune" + - "iUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03" + - "\x00\x00\x00\x0e\xff\xff\xff\xff\xad\x8a\x02D\xff\xff\xff\xff\xbagG\x88\x01\x02\x00\x00k\xbc\x00\x00\x00\x00ix\x00\x04\x00\x00p\x80\x00\nLMT\x00+0730\x00+08\x00\n<+08>" + - "-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd7e&uv\x02\x00\x00v\x02\x00\x00\f\x00\x1c\x00Asia/BaghdadUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x86\xb1\xdc\xff\xff\xff\xff" + - "\x9e0<\xe0\x00\x00\x00\x00\x170hP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xe8\xbdP\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbd\xc8@\x00\x00\x00\x00\x1c\xad\xc7P" + - "\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00" + - "$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf6x\x00\x00\x00\x00\x00(纀\x00\x00\x00\x00)\xd8\xfd\x00\x00\x00\x00\x00*\xca?\x80" + - "\x00\x00\x00\x00+\xba0\x80\x00\x00\x00\x00,\xabs\x00\x00\x00\x00\x00-\x9bd\x00\x00\x00\x00\x00.\x8c\xa6\x80\x00\x00\x00\x00/|\x97\x80\x00\x00\x00\x000m\xda\x00\x00\x00\x00\x001_\x1c\x80\x00\x00\x00\x00" + - "2P_\x00\x00\x00\x00\x003@P\x00\x00\x00\x00\x0041\x92\x80\x00\x00\x00\x005!\x83\x80\x00\x00\x00\x006\x12\xc6\x00\x00\x00\x00\x007\x02\xb7\x00\x00\x00\x00\x007\xf3\xf9\x80\x00\x00\x00\x008\xe5<\x00" + - "\x00\x00\x00\x009\xd6~\x80\x00\x00\x00\x00:\xc6o\x80\x00\x00\x00\x00;\xb7\xb2\x00\x00\x00\x00\x00<\xa7\xa3\x00\x00\x00\x00\x00=\x98\xe5\x80\x00\x00\x00\x00>\x88ր\x00\x00\x00\x00?z\x19\x00\x00\x00\x00\x00" + - "@k[\x80\x00\x00\x00\x00A\\\x9e\x00\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00C=р\x00\x00\x00\x00D-\u0080\x00\x00\x00\x00E\x1f\x05\x00\x00\x00\x00\x00F\x0e\xf6\x00\x00\x00\x00\x00G\x008\x80" + - "\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00)\xa4\x00\x00" + - "\x00\x00)\xa0\x00\x04\x00\x00*0\x00\b\x00\x008@\x01\fLMT\x00BMT\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R`\xc9\xd4" + - "\\\xbe\x00\x00\x00\xbe\x00\x00\x00\x12\x00\x1c\x00Asia/Ujung_PandangUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\xa1\xf2]\x90\xff\xff\xff\xff\xba\x16Ր\xff\xff\xff\xffˈ\x1d\x80\xff\xff" + - "\xff\xff\xd2V\xeep\x01\x02\x03\x04\x00\x00o\xf0\x00\x00\x00\x00o\xf0\x00\x04\x00\x00p\x80\x00\b\x00\x00~\x90\x00\f\x00\x00p\x80\x00\x10LMT\x00MMT\x00+08\x00+09\x00WITA" + - "\x00\nWITA-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RV\xe0\xe7!\xe7\x02\x00\x00\xe7\x02\x00\x00\v\x00\x1c\x00Asia/AnadyrUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x1d" + - "\x9c\xff\xff\xff\xff\xb5\xa3\x8c\xc0\x00\x00\x00\x00\x15'\x1b0\x00\x00\x00\x00\x16\x18O\xa0\x00\x00\x00\x00\x17\bN\xb0\x00\x00\x00\x00\x17\xf9\x910\x00\x00\x00\x00\x18\xe9\x90@\x00\x00\x00\x00\x19\xdaİ\x00\x00\x00" + - "\x00\x1a\xcc\x15@\x00\x00\x00\x00\x1b\xbc\"`\x00\x00\x00\x00\x1c\xac\x13`\x00\x00\x00\x00\x1d\x9c\x04`\x00\x00\x00\x00\x1e\x8b\xf5`\x00\x00\x00\x00\x1f{\xe6`\x00\x00\x00\x00 k\xd7`\x00\x00\x00\x00![\xc8" + - "`\x00\x00\x00\x00\"K\xb9`\x00\x00\x00\x00#;\xaa`\x00\x00\x00\x00$+\x9b`\x00\x00\x00\x00%\x1b\x8c`\x00\x00\x00\x00&\v}`\x00\x00\x00\x00'\x04\xa8\xe0\x00\x00\x00\x00'\xf4\x99\xe0\x00\x00\x00" + - "\x00(\xe4\x98\xf0\x00\x00\x00\x00)x@\xf0\x00\x00\x00\x00)\xd4{\xe0\x00\x00\x00\x00*\xc4l\xe0\x00\x00\x00\x00+\xb4]\xe0\x00\x00\x00\x00,\xa4N\xe0\x00\x00\x00\x00-\x94?\xe0\x00\x00\x00\x00.\x840" + - "\xe0\x00\x00\x00\x00/t!\xe0\x00\x00\x00\x000d\x12\xe0\x00\x00\x00\x001]>`\x00\x00\x00\x002r\x19`\x00\x00\x00\x003= `\x00\x00\x00\x004Q\xfb`\x00\x00\x00\x005\x1d\x02`\x00\x00\x00" + - "\x0061\xdd`\x00\x00\x00\x006\xfc\xe4`\x00\x00\x00\x008\x1a\xf9\xe0\x00\x00\x00\x008\xdc\xc6`\x00\x00\x00\x009\xfa\xdb\xe0\x00\x00\x00\x00:\xbc\xa8`\x00\x00\x00\x00;ڽ\xe0\x00\x00\x00\x00<\xa5\xc4" + - "\xe0\x00\x00\x00\x00=\xba\x9f\xe0\x00\x00\x00\x00>\x85\xa6\xe0\x00\x00\x00\x00?\x9a\x81\xe0\x00\x00\x00\x00@e\x88\xe0\x00\x00\x00\x00A\x83\x9e`\x00\x00\x00\x00BEj\xe0\x00\x00\x00\x00Cc\x80`\x00\x00\x00" + - "\x00D%L\xe0\x00\x00\x00\x00ECb`\x00\x00\x00\x00F\x05.\xe0\x00\x00\x00\x00G#D`\x00\x00\x00\x00G\xeeK`\x00\x00\x00\x00I\x03&`\x00\x00\x00\x00I\xce-`\x00\x00\x00\x00J\xe3\b" + - "`\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L\xcc2\xf0\x00\x00\x00\x00M\x8d\xffp\x01\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x05\x06\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + - "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x05\x06\x01\x00\x00\xa6d\x00\x00\x00\x00\xa8\xc0\x00\x04\x00\x00\xc4\xe0\x01\b\x00\x00\xb6\xd0\x00\f\x00\x00\xb6\xd0\x01\f\x00" + - "\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\x10LMT\x00+12\x00+14\x00+13\x00+11\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xed\x8c\xf1\x91\x85" + - "\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Asia/MuscatUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99\xa8\x01\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT\x00+04\x00\n<+04>" + - "-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x84)\r\xbd\xec\x00\x00\x00\xec\x00\x00\x00\x10\x00\x1c\x00Asia/Ho_Chi_MinhUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\x88\x8cC\x80" + - "\xff\xff\xff\xff\x91\xa3+\n\xff\xff\xff\xff\xcd5\xe6\x80\xff\xff\xff\xff\xd1Y\xcep\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xff\xd52\xbb\x10\xff\xff\xff\xff\xe4\xb6\xe4\x80\xff\xff\xff\xff\xed/\x98\x00\x00\x00\x00\x00" + - "\n=\xc7\x00\x01\x02\x03\x04\x02\x03\x02\x03\x02\x00\x00d\x00\x00\x00\x00\x00c\xf6\x00\x04\x00\x00bp\x00\t\x00\x00p\x80\x00\r\x00\x00~\x90\x00\x11LMT\x00PLMT\x00+07\x00+08\x00" + - "+09\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xef\\\xf4q\x17\x04\x00\x00\x17\x04\x00\x00\r\x00\x1c\x00Asia/DamascusUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x03\x00\x00\x00\r\xff" + - "\xff\xff\xff\xa1\xf2\xabx\xff\xff\xff\xff\xa2\x81/\x80\xff\xff\xff\xff\xa3^\x9dp\xff\xff\xff\xff\xa4a\x11\x80\xff\xff\xff\xff\xa5>\u007fp\xff\xff\xff\xff\xa6@\xf3\x80\xff\xff\xff\xff\xa7\x1eap\xff\xff\xff\xff\xa8" + - " Հ\xff\xff\xff\xff\xa9\a}\xf0\xff\xff\xff\xff\xf1\x8fR\x00\xff\xff\xff\xff\xf2[\x9cp\xff\xff\xff\xff\xf3s(\x80\xff\xff\xff\xff\xf4;~p\xff\xff\xff\xff\xf5U\xad\x80\xff\xff\xff\xff\xf6\x1fT\xf0\xff" + - "\xff\xff\xff\xf76\xe1\x00\xff\xff\xff\xff\xf7\xff6\xf0\xff\xff\xff\xff\xf9\x0e\xda\x00\xff\xff\xff\xff\xf9\xe1\xbb\xf0\xff\xff\xff\xff\xfa\xf9H\x00\xff\xff\xff\xff\xfb\xc2\xefp\xff\xff\xff\xff\xfc\xdb\xcd\x00\xff\xff\xff\xff\xfd" + - "\xa5tp\xff\xff\xff\xff\xfe\xbd\x00\x80\xff\xff\xff\xff\xff\x86\xa7\xf0\x00\x00\x00\x00\x00\x9e4\x00\x00\x00\x00\x00\x01g\xdbp\x00\x00\x00\x00\x02\u007fg\x80\x00\x00\x00\x00\x03I\x0e\xf0\x00\x00\x00\x00\x04a\xec\x80\x00" + - "\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06C \x00\x00\x00\x00\x00\a\f\xc7p\x00\x00\x00\x00\b$S\x80\x00\x00\x00\x00\b\xed\xfa\xf0\x00\x00\x00\x00\n\x05\x87\x00\x00\x00\x00\x00\n\xcf.p\x00\x00\x00\x00\v" + - "\xe8\f\x00\x00\x00\x00\x00\f\xb1\xb3p\x00\x00\x00\x00\r\xc9?\x80\x00\x00\x00\x00\x0ekY\xf0\x00\x00\x00\x00\x0f\xaas\x00\x00\x00\x00\x00\x10L\x8dp\x00\x00\x00\x00\x18\xf4\xc5\x00\x00\x00\x00\x00\x19\xdbmp\x00" + - "\x00\x00\x00\x1a\xd7J\x00\x00\x00\x00\x00\x1b\xbd\xf2p\x00\x00\x00\x00\x1eU#\x00\x00\x00\x00\x00\x1f\x8a\xe5p\x00\x00\x00\x00 Gz\x00\x00\x00\x00\x00!\x89\x19\xf0\x00\x00\x00\x00\"\xe2`\x00\x00\x00\x0041hP\x00\x00\x00\x005\x1e\xc4`\x00\x00\x00\x006\x12\x9b\xd0\x00\x00\x00\x007\x02\x9a\xe0\x00\x00\x00\x007\xf3\xcfP\x00" + - "\x00\x00\x008\xe5\x1f\xe0\x00\x00\x00\x009\xd6TP\x00\x00\x00\x00:\xc6S`\x00\x00\x00\x00;\xb7\x87\xd0\x00\x00\x00\x00<\xa7\x86\xe0\x00\x00\x00\x00=\x98\xbbP\x00\x00\x00\x00>\x88\xba`\x00\x00\x00\x00?" + - "y\xee\xd0\x00\x00\x00\x00@k?`\x00\x00\x00\x00A\\s\xd0\x00\x00\x00\x00BLr\xe0\x00\x00\x00\x00C=\xa7P\x00\x00\x00\x00D-\xa6`\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F\f6\xe0\x00" + - "\x00\x00\x00G*>P\x00\x00\x00\x00G\xf5S`\x00\x00\x00\x00I\vq\xd0\x00\x00\x00\x00I\xcb\xfa\xe0\x00\x00\x00\x00J\xea\x02P\x00\x00\x00\x00K\xb5\x17`\x00\x00\x00\x00L\xc9\xe4P\x00\x00\x00\x00M" + - "\x94\xf9`\x00\x00\x00\x00N\xa9\xc6P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\"\b\x00\x00\x00\x00*0" + - "\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2EEST,M3.5.5/0,M10.5.5/0\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9Rj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x1c\x00Asia/ThimphuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff\xd5\xe6\x15t\x00\x00\x00\x00!aM\xa8\x01\x02\x00\x00T\f\x00\x00" + - "\x00\x00MX\x00\x04\x00\x00T`\x00\nLMT\x00+0530\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00" + - "\x00\f\x00\x1c\x00Asia/BangkokUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/" + + "t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00" + + "\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=" + + "\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00\x00A\x84\x00\xd0\x00\x00\x00\x00BE\xcdP\x00\x00\x00\x00Cc\xe2\xd0\x00\x00\x00\x00D%\xafP\x00" + + "\x00\x00\x00EC\xc4\xd0\x00\x00\x00\x00F\x05\x91P\x00\x00\x00\x00G#\xa6\xd0\x00\x00\x00\x00G\xee\xad\xd0\x00\x00\x00\x00I\x03\x88\xd0\x00\x00\x00\x00IΏ\xd0\x00\x00\x00\x00J\xe3j\xd0\x00\x00\x00\x00K" + + "\xaeq\xd0\x00\x00\x00\x00L̇P\x00\x00\x00\x00M\x8eS\xd0\x00\x00\x00\x00TL\x01@\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x02\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x04\x00\x008\xd9\x00\x00\x00\x004\xc1\x00\x04\x00\x008@\x00\b\x00\x00T`\x01\f\x00\x00F" + + "P\x00\x10\x00\x00FP\x01\x10\x00\x00T`\x00\fLMT\x00PMT\x00+04\x00+06\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x1c\x00Atlantic/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU1)7\xad\xad\x05\x00\x00\xad\x05\x00\x00\x10\x00\x1c\x00Atlantic/MadeiraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\x00\x00\x00\a\x00\x00\x00\x1d\xff\xff\xff\xff^=\x13X\xff\xff\xff\xff\x92朐\xff\xff\xff\xff" + + "\x9bK{\x80\xff\xff\xff\xff\x9b\xfeՐ\xff\xff\xff\xff\x9c\x9c\xfb\x80\xff\xff\xff\xff\x9dɑ\x80\xff\xff\xff\xff\x9e\x7f\x80\x80\xff\xff\xff\xff\x9f\xaa\xc5\x00\xff\xff\xff\xff\xa0_b\x80\xff\xff\xff\xff\xa1\x8b\xf8\x80" + + "\xff\xff\xff\xff\xa2A\xe7\x80\xff\xff\xff\xff\xa3n}\x80\xff\xff\xff\xff\xa4#\x1b\x00\xff\xff\xff\xff\xa5O\xb1\x00\xff\xff\xff\xff\xaa\x05\xfd\x80\xff\xff\xff\xff\xaa\xf4\x9d\x00\xff\xff\xff\xff\xadɶ\x00\xff\xff\xff\xff" + + "\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0]\x80\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89z\x00\xff\xff\xff\xff\xb2p0\x80\xff\xff\xff\xff\xb3r\x96\x80\xff\xff\xff\xff\xb4P\x12\x80\xff\xff\xff\xff\xb72Z\x80" + + "\xff\xff\xff\xff\xb8\x0fր\xff\xff\xff\xff\xb8\xffǀ\xff\xff\xff\xff\xb9︀\xff\xff\xff\xff\xbc\xc8\xc6\x00\xff\xff\xff\xff\xbd\xb8\xb7\x00\xff\xff\xff\xff\xbe\x9fm\x80\xff\xff\xff\xff\xbf\x98\x99\x00\xff\xff\xff\xff" + + "\xc0\x9a\xff\x00\xff\xff\xff\xff\xc1x{\x00\xff\xff\xff\xff\xc2hl\x00\xff\xff\xff\xff\xc3X]\x00\xff\xff\xff\xff\xc4?\x13\x80\xff\xff\xff\xff\xc58?\x00\xff\xff\xff\xff\xc6:\xa5\x00\xff\xff\xff\xff\xc7X\xba\x80" + + "\xff\xff\xff\xff\xc7\xd9\xed\x80\xff\xff\xff\xff\xc9\x01=\x80\xff\xff\xff\xff\xc9\xf1.\x80\xff\xff\xff\xff\xca\xe2q\x00\xff\xff\xff\xff˵a\x00\xff\xff\xff\xff\xcb\xec\xb1\xf0\xff\xff\xff\xff̀Y\xf0\xff\xff\xff\xff" + + "\xccܱ\x00\xff\xff\xff\xff͕C\x00\xff\xff\xff\xff\xcd\xc3Yp\xff\xff\xff\xff\xcer\xb0\xf0\xff\xff\xff\xff\xce\xc5̀\xff\xff\xff\xff\xcfu%\x00\xff\xff\xff\xffϬu\xf0\xff\xff\xff\xff\xd0R\x92\xf0" + + "\xff\xff\xff\xffХ\xaf\x80\xff\xff\xff\xff\xd1U\a\x00\xff\xff\xff\xffьW\xf0\xff\xff\xff\xff\xd22t\xf0\xff\xff\xff\xff҅\x91\x80\xff\xff\xff\xff\xd3Y\xd3\x00\xff\xff\xff\xff\xd4I\xc4\x00\xff\xff\xff\xff" + + "\xd59\xdf0\xff\xff\xff\xff\xd6)\xd00\xff\xff\xff\xff\xd7\x19\xc10\xff\xff\xff\xff\xd8\t\xb20\xff\xff\xff\xff\xd8\xf9\xa30\xff\xff\xff\xff\xd9\xe9\x940\xff\xff\xff\xff\xdaم0\xff\xff\xff\xff\xdb\xc9v0" + + "\xff\xff\xff\xffܹg0\xff\xff\xff\xffݲ\x92\xb0\xff\xff\xff\xffޢ\x83\xb0\xff\xff\xff\xffߒt\xb0\xff\xff\xff\xff\xe0\x82e\xb0\xff\xff\xff\xff\xe1rV\xb0\xff\xff\xff\xff\xe2bG\xb0\xff\xff\xff\xff" + + "\xe3R8\xb0\xff\xff\xff\xff\xe4B)\xb0\xff\xff\xff\xff\xe52\x1a\xb0\xff\xff\xff\xff\xe6\"\v\xb0\xff\xff\xff\xff\xe7\x1b70\xff\xff\xff\xff\xe8\v(0\xff\xff\xff\xff\xe8\xfb\x190\xff\xff\xff\xff\xe9\xeb\n0" + + "\xff\xff\xff\xff\xea\xda\xfb0\xff\xff\xff\xff\xeb\xca\xec0\xff\xff\xff\xff\xec\xba\xdd0\xff\xff\xff\xff\xed\xaa\xce0\xff\xff\xff\xff\ue6bf0\xff\xff\xff\xff\uf2b00\xff\xff\xff\xff\xf0z\xa10\xff\xff\xff\xff" + + "\xf1j\x920\xff\xff\xff\xff\xf2c\xbd\xb0\xff\xff\xff\xff\xf3S\xae\xb0\xff\xff\xff\xff\xf4C\x9f\xb0\xff\xff\xff\xff\xf53\x90\xb0\xff\xff\xff\xff\xf6#\x81\xb0\xff\xff\xff\xff\xf7\x13r\xb0\xff\xff\xff\xff\xf8\x03c\xb0" + + "\xff\xff\xff\xff\xf8\xf3T\xb0\x00\x00\x00\x00\r\x9b\x1b\x00\x00\x00\x00\x00\x0e\x8b\f\x00\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10t(\x80\x00\x00\x00\x00\x11d\x19\x80\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00" + + "\x13C\xfb\x80\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㽠\x00\x00\x00\x00\x19Ӡ\x90" + + "\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00" + + "!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00" + + "D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\xff\xff\xc3:\x00" + + "\x00\xff\xff\xd1J\x01\x04\xff\xff\xc3:\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xc7\xc0\x00\x10LMT\x00BST\x00BMT\x00ADT\x00AST\x00\nAST4ADT,M3.2.0" + + ",M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe7\xcf^\xb0\x15\x03\x00\x00\x15\x03\x00\x00\x10\x00\x1c\x00Atlantic/StanleyUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00F\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff" + + "\xff\xffi\x87\x11\xbc\xff\xff\xff\xff\x93D_<\xff\xff\xff\xff\xc3OZ\xc0\xff\xff\xff\xff\xc46\x030\xff\xff\xff\xff\xc5/<\xc0\xff\xff\xff\xff\xc6\x15\xe50\xff\xff\xff\xff\xc7\x18Y@\xff\xff\xff\xff\xc7\xff" + + "\x01\xb0\xff\xff\xff\xff\xc8\xf8;@\xff\xff\xff\xff\xc9\xde\xe3\xb0\xff\xff\xff\xff\xca\xd8\x1d@\xff\xff\xff\xff˾Ű\xff\xff\xff\xff̷\xff@\xff\xff\xff\xff\xcd6\x810\x00\x00\x00\x00\x19\x11\xfe@\x00\x00" + + "\x00\x00\x19Ӽ\xb0\x00\x00\x00\x00\x1a\xf1\xc4 \x00\x00\x00\x00\x1b\xaad0\x00\x00\x00\x00\x1cѦ \x00\x00\x00\x00\x1d\x8aF0\x00\x00\x00\x00\x1e\xa8[\xb0\x00\x00\x00\x00\x1fj6@\x00\x00\x00\x00 \x88" + + "=\xb0\x00\x00\x00\x00!J\x18@\x00\x00\x00\x00\"h\x1f\xb0\x00\x00\x00\x00#)\xfa@\x00\x00\x00\x00$H\x01\xb0\x00\x00\x00\x00%\t\xdc@\x00\x00\x00\x00&1\x1e0\x00\x00\x00\x00&\xe9\xbe@\x00\x00" + + "\x00\x00(\x11\x000\x00\x00\x00\x00(\xd2\xda\xc0\x00\x00\x00\x00)\xf0\xe20\x00\x00\x00\x00*\xb2\xbc\xc0\x00\x00\x00\x00+\xd0\xc40\x00\x00\x00\x00,\x92\x9e\xc0\x00\x00\x00\x00-\xb0\xa60\x00\x00\x00\x00.r" + + "\x80\xc0\x00\x00\x00\x00/\x90\x880\x00\x00\x00\x000Rb\xc0\x00\x00\x00\x001y\xa4\xb0\x00\x00\x00\x002;\x7f@\x00\x00\x00\x003Y\x86\xb0\x00\x00\x00\x004\x1ba@\x00\x00\x00\x0059h\xb0\x00\x00" + + "\x00\x005\xfbC@\x00\x00\x00\x007\x19J\xb0\x00\x00\x00\x007\xdb%@\x00\x00\x00\x008\xf9,\xb0\x00\x00\x00\x009\xbb\a@\x00\x00\x00\x00:\xd9*\xd0\x00\x00\x00\x00;\x91\xca\xe0\x00\x00\x00\x00<\xc2" + + "GP\x00\x00\x00\x00=q\xac\xe0\x00\x00\x00\x00>\xa2)P\x00\x00\x00\x00?Z\xc9`\x00\x00\x00\x00@\x82\vP\x00\x00\x00\x00A:\xab`\x00\x00\x00\x00Ba\xedP\x00\x00\x00\x00C\x1a\x8d`\x00\x00" + + "\x00\x00DA\xcfP\x00\x00\x00\x00D\xfao`\x00\x00\x00\x00F!\xb1P\x00\x00\x00\x00F\xdaQ`\x00\x00\x00\x00H\n\xcd\xd0\x00\x00\x00\x00H\xc3m\xe0\x00\x00\x00\x00I\xea\xaf\xd0\x00\x00\x00\x00J\xa3" + + "O\xe0\x00\x00\x00\x00Kʑ\xd0\x00\x00\x00\x00L\x831\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\xff\xff\xc9\xc4\x00\x00\xff\xff\xc9\xc4\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\f\xff\xff\xe3\xe0\x01\x10\xff\xff" + + "\xd5\xd0\x00\bLMT\x00SMT\x00-03\x00-04\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xaf|7\xb3\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00" + + "\x1c\x00Atlantic/CanaryUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00" + - "BMT\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x02\x95-\xad\xc4\x02\x00\x00\xc4\x02\x00\x00\f\x00\x1c\x00Asia/YerevanU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x05\x00\x00" + - "\x00\x10\xff\xff\xff\xff\xaa\x19\x9aH\xff\xff\xff\xff\xe7\xda\fP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00" + - "\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 l" + - "G\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00" + - "\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84" + - "\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00" + - "\x00\x008\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86" + - "\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x00\x00\x00\x00BE\xdb`\x00\x00\x00\x00Cc\xf0\xe0\x00\x00\x00\x00D%\xbd`\x00\x00\x00\x00EC\xd2\xe0\x00\x00" + - "\x00\x00F\x05\x9f`\x00\x00\x00\x00G#\xb4\xe0\x00\x00\x00\x00G\xee\xbb\xe0\x00\x00\x00\x00I\x03\x96\xe0\x00\x00\x00\x00IΝ\xe0\x00\x00\x00\x00J\xe3x\xe0\x00\x00\x00\x00K\xae\u007f\xe0\x00\x00\x00\x00L\xcc" + - "\x95`\x00\x00\x00\x00M\x8ea\xe0\x00\x00\x00\x00N\xacw`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00)\xb8\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+0" + - "5\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8bSnT\xa1\x00\x00\x00\xa1\x00\x00\x00\r\x00\x1c\x00Asia/KatmanduUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00" + - "\x10\xff\xff\xff\xff\xa1\xf2}\x84\x00\x00\x00\x00\x1e\x180\xa8\x01\x02\x00\x00O\xfc\x00\x00\x00\x00MX\x00\x04\x00\x00P\xdc\x00\nLMT\x00+0530\x00+0545\x00\n<+0545" + - ">-5:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R&\xe9\xd1\xd8q\x02\x00\x00q\x02\x00\x00\t\x00\x1c\x00Asia/OralUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\a\x00\x00\x00\x14\xff\xff\xff\xff\xaa\x19\x93\xdc\xff\xff\xff" + - "\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw" + - "\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00" + - "\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xfc\xe0\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00'\x05\x19`\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xfb" + - "`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xdd`\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xbf`\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\xa1`\x00\x00\x00" + - "\x00/t\x92`\x00\x00\x00\x000d\x83`\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r\x89\xe0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x0062M" + - "\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00\x00\x008\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`\x00\x00\x00" + - "\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x06\x05\x06\x05" + - "\x06\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x02\x00\x000$\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00" + - "FP\x01\b\x00\x008@\x00\x10LMT\x00+03\x00+05\x00+06\x00+04\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x02\xf4\xaeg\xd5\x00\x00" + - "\x00\xd5\x00\x00\x00\n\x00\x1c\x00Asia/TokyoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffe¤p\xff\xff\xff\xff\xd7>\x02p\xff\xff\xff\xff\xd7\xedY\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd" + - ";\xf0\xff\xff\xff\xff\xdb\a\x00\xf0\xff\xff\xff\xffۭ\x1d\xf0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xff\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x83\x03\x00\x00\x00\x00\x8c\xa0\x01\x04\x00\x00~\x90\x00" + - "\bLMT\x00JDT\x00JST\x00\nJST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\r\x00\x1c\x00Asia/Istan" + - "bulUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s\x00\x00" + - "\x00\x06\x00\x00\x00\x19\xff\xff\xff\xffV\xb6\xc8\xd8\xff\xff\xff\xff\x90\x8b\xf5\x98\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9bվ\xd0\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N" + - "\x80`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\u007f\xd0\xff\xff\xff\xff\xaa((`\xff\xff\xff\xff\xaa\xe1\xfd\xd0\xff\xff\xff\xff\xab\xf9\x89\xe0\xff\xff\xff\xff\xac\xc31P\xff\xff" + - "\xff\xffȁ?\xe0\xff\xff\xff\xff\xc9\x01\x13P\xff\xff\xff\xff\xc9J\xf5`\xff\xff\xff\xff\xca\u0380P\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xd2k\tP\xff\xff\xff\xffӢ9`\xff\xff\xff\xff\xd4C" + - "\x02P\xff\xff\xff\xff\xd5L\r\xe0\xff\xff\xff\xff\xd6){\xd0\xff\xff\xff\xff\xd7+\xef\xe0\xff\xff\xff\xff\xd8\t]\xd0\xff\xff\xff\xff\xd9\x02\x97`\xff\xff\xff\xff\xd9\xe9?\xd0\xff\xff\xff\xff\xda\xeb\xb3\xe0\xff\xff" + - "\xff\xff\xdb\xd2\\P\xff\xff\xff\xff\xdc\xd4\xd0`\xff\xff\xff\xffݲ>P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b\xefP\xff\xff\xff\xff\xf5h\x06`\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n" + - "\x93p\x00\x00\x00\x00\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00\x00\x00\n\xf9^p\x00\x00\x00\x00\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00" + - "\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89\xb0p\x00\x00\x00\x00\x19ܰ\xe0\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6" + - "\xef\xf0\x00\x00\x00\x00\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00" + - "\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4" + - "\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00" + - "\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xdd" + - "D\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00" + - "\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#" + - "\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00" + - "\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S8\xbe\x10\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17" + - "N\x90\x00\x00\x00\x00V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\r\x00\x00*0\x00\x11\x00\x008@\x01\x15LMT" + - "\x00IMT\x00EEST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc7\x11\xe1[\xdc\x02\x00\x00\xdc\x02\x00\x00\v\x00\x1c" + - "\x00Asia/BeirutUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6¸\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6" + - "%'\xe0\xff\xff\xff\xff\xa7'\u007f\xd0\xff\xff\xff\xff\xa8)\xf3\xe0\xff\xff\xff\xff\xa8\xeb\xb2P\xff\xff\xff\xff\xe8*\x85\xe0\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff" + - "\xff\xff\xff\xeb\xec\xec\xe0\xff\xff\xff\xff추P\xff\xff\xff\xff\xed\xcfq\xe0\xff\xff\xff\xff\xee\x99\x19P\xff\xff\xff\xffﰥ`\xff\xff\xff\xff\xf0zL\xd0\x00\x00\x00\x00\x04\xa6^`\x00\x00\x00\x00\x05" + - "+w\xd0\x00\x00\x00\x00\x06C\x03\xe0\x00\x00\x00\x00\a\f\xabP\x00\x00\x00\x00\b$7`\x00\x00\x00\x00\b\xed\xde\xd0\x00\x00\x00\x00\n\x05j\xe0\x00\x00\x00\x00\n\xcf\x12P\x00\x00\x00\x00\v\xe7\xef\xe0\x00" + - "\x00\x00\x00\f\xb1\x97P\x00\x00\x00\x00\r\xc9#`\x00\x00\x00\x00\x0e\x92\xca\xd0\x00\x00\x00\x00\x0f\xa9\x05`\x00\x00\x00\x00\x10r\xac\xd0\x00\x00\x00\x00\x1a\xf4.\xe0\x00\x00\x00\x00\x1bќ\xd0\x00\x00\x00\x00\x1c" + - "\xd5b`\x00\x00\x00\x00\x1d\xb2\xd0P\x00\x00\x00\x00\x1e\xb6\x95\xe0\x00\x00\x00\x00\x1f\x94\x03\xd0\x00\x00\x00\x00 \x97\xc9`\x00\x00\x00\x00!u7P\x00\x00\x00\x00\"\xa3,\xe0\x00\x00\x00\x00#W\xbcP\x00" + - "\x00\x00\x00$g_`\x00\x00\x00\x00%8\xef\xd0\x00\x00\x00\x00&<\xb5`\x00\x00\x00\x00'\x1a#P\x00\x00\x00\x00(\x1d\xe8\xe0\x00\x00\x00\x00(\xfbV\xd0\x00\x00\x00\x00*\x00m\xe0\x00\x00\x00\x00*" + - "\xce\t\xd0\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00" + - "\x00\x00\x002M\x91\xd0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004-s\xd0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x006\rU\xd0\x00\x00\x00\x006\xfdT\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00!H\x00\x00\x00\x00*" + - "0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2EEST,M3.5.0/0,M10.5.0/0\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9Rb\xadű\xf8\x00\x00\x00\xf8\x00\x00\x00\f\x00\x1c\x00Asia/JakartaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00 \xff\xff\xff\xff?fI`\xff\xff\xff\xff\xa9x\x85\xe0\xff\xff\xff\xff\xba\x16\xde" + - "`\xff\xff\xff\xff˿\x83\x88\xff\xff\xff\xff\xd2V\xeep\xff\xff\xff\xff\xd7<\xc6\b\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xf4\xb5\xbe\x88\x01\x02\x03\x04\x03\x05\x03\x06\x00\x00d \x00\x00\x00\x00d \x00" + - "\x04\x00\x00g \x00\b\x00\x00ix\x00\x0e\x00\x00~\x90\x00\x14\x00\x00p\x80\x00\x18\x00\x00bp\x00\x1cLMT\x00BMT\x00+0720\x00+0730\x00+09\x00+08\x00W" + - "IB\x00\nWIB-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\n\x00\x1c\x00Asia/DubaiUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99" + - "\xa8\x01\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa1\xfax\x98g\x02\x00\x00g\x02\x00\x00\r\x00\x1c" + - "\x00Asia/QostanayUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xaa\x19\x88\\\xff\xff\xff\xff\xb5\xa3\xfd@\x00\x00\x00\x00\x15'\x8b\xb0\x00\x00\x00\x00\x16\x18\xc0 \x00\x00\x00\x00\x17\b\xb1 \x00\x00\x00" + - "\x00\x17\xf9\xf3\xa0\x00\x00\x00\x00\x18\xe9\xf2\xb0\x00\x00\x00\x00\x19\xdb' \x00\x00\x00\x00\x1a\xccw\xb0\x00\x00\x00\x00\x1b\xbc\x84\xd0\x00\x00\x00\x00\x1c\xacu\xd0\x00\x00\x00\x00\x1d\x9cf\xd0\x00\x00\x00\x00\x1e\x8cW" + - "\xd0\x00\x00\x00\x00\x1f|H\xd0\x00\x00\x00\x00 l9\xd0\x00\x00\x00\x00!\\*\xd0\x00\x00\x00\x00\"L\x1b\xd0\x00\x00\x00\x00#<\f\xd0\x00\x00\x00\x00$+\xfd\xd0\x00\x00\x00\x00%\x1b\xee\xd0\x00\x00\x00" + - "\x00&\v\xdf\xd0\x00\x00\x00\x00'\x05\vP\x00\x00\x00\x00'\xf4\xfcP\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)x\xa3`\x00\x00\x00\x00)\xd4\xdeP\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xc0" + - "P\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xa2P\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x84P\x00\x00\x00\x000duP\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r{\xd0\x00\x00\x00" + - "\x003=\x82\xd0\x00\x00\x00\x004R]\xd0\x00\x00\x00\x005\x1dd\xd0\x00\x00\x00\x0062?\xd0\x00\x00\x00\x006\xfdF\xd0\x00\x00\x00\x008\x1b\\P\x00\x00\x00\x008\xdd(\xd0\x00\x00\x00\x009\xfb>" + - "P\x00\x00\x00\x00:\xbd\n\xd0\x00\x00\x00\x00;\xdb P\x00\x00\x00\x00<\xa6'P\x00\x00\x00\x00=\xbb\x02P\x00\x00\x00\x00>\x86\tP\x00\x00\x00\x00?\x9a\xe4P\x00\x00\x00\x00@e\xebP\x00\x00\x00" + - "\x00A\x84\x00\xd0\x01\x02\x03\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x00\x00;\xa4" + - "\x00\x00\x00\x008@\x00\x04\x00\x00FP\x00\b\x00\x00T`\x01\f\x00\x00T`\x00\f\x00\x00FP\x01\bLMT\x00+04\x00+05\x00+06\x00\n<+06>-6\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\v\x00\x1c\x00Asia/HarbinUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~6C)\xff\xff\xff\xff\xa0\x97\xa2\x80\xff\xff\xff" + - "\xff\xa1y\x04\xf0\xff\xff\xff\xff\xc8Y^\x80\xff\xff\xff\xff\xc9\t\xf9p\xff\xff\xff\xff\xc9ӽ\x00\xff\xff\xff\xff\xcb\x05\x8a\xf0\xff\xff\xff\xff\xcb|@\x00\xff\xff\xff\xff\xd2;>\xf0\xff\xff\xff\xffӋ{" + - "\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00" + - "\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|" + - "\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\t\x00\x1c\x00Atlantic/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\u0097N\xad\xaf\x00\x00\x00\xaf\x00\x00\x00\x13\x00\x1c\x00Atlantic/Cape_VerdeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x92檠\xff\xff\xff\xff̕\x9c \xff\xff\xff\xff\xd2t" + - "|\x10\x00\x00\x00\x00\v\x17\xf7@\x01\x02\x01\x03\xff\xff\xe9\xf4\x00\x00\xff\xff\xe3\xe0\x00\x04\xff\xff\xf1\xf0\x01\b\xff\xff\xf1\xf0\x00\bLMT\x00-02\x00-01\x00\n<-01>1\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe7\xcf^\xb0\x15\x03\x00\x00\x15\x03\x00\x00\x10\x00\x1c\x00Atlantic/StanleyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00F\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffi\x87\x11\xbc\xff\xff\xff\xff\x93" + - "D_<\xff\xff\xff\xff\xc3OZ\xc0\xff\xff\xff\xff\xc46\x030\xff\xff\xff\xff\xc5/<\xc0\xff\xff\xff\xff\xc6\x15\xe50\xff\xff\xff\xff\xc7\x18Y@\xff\xff\xff\xff\xc7\xff\x01\xb0\xff\xff\xff\xff\xc8\xf8;@\xff" + - "\xff\xff\xff\xc9\xde\xe3\xb0\xff\xff\xff\xff\xca\xd8\x1d@\xff\xff\xff\xff˾Ű\xff\xff\xff\xff̷\xff@\xff\xff\xff\xff\xcd6\x810\x00\x00\x00\x00\x19\x11\xfe@\x00\x00\x00\x00\x19Ӽ\xb0\x00\x00\x00\x00\x1a" + - "\xf1\xc4 \x00\x00\x00\x00\x1b\xaad0\x00\x00\x00\x00\x1cѦ \x00\x00\x00\x00\x1d\x8aF0\x00\x00\x00\x00\x1e\xa8[\xb0\x00\x00\x00\x00\x1fj6@\x00\x00\x00\x00 \x88=\xb0\x00\x00\x00\x00!J\x18@\x00" + - "\x00\x00\x00\"h\x1f\xb0\x00\x00\x00\x00#)\xfa@\x00\x00\x00\x00$H\x01\xb0\x00\x00\x00\x00%\t\xdc@\x00\x00\x00\x00&1\x1e0\x00\x00\x00\x00&\xe9\xbe@\x00\x00\x00\x00(\x11\x000\x00\x00\x00\x00(" + - "\xd2\xda\xc0\x00\x00\x00\x00)\xf0\xe20\x00\x00\x00\x00*\xb2\xbc\xc0\x00\x00\x00\x00+\xd0\xc40\x00\x00\x00\x00,\x92\x9e\xc0\x00\x00\x00\x00-\xb0\xa60\x00\x00\x00\x00.r\x80\xc0\x00\x00\x00\x00/\x90\x880\x00" + - "\x00\x00\x000Rb\xc0\x00\x00\x00\x001y\xa4\xb0\x00\x00\x00\x002;\u007f@\x00\x00\x00\x003Y\x86\xb0\x00\x00\x00\x004\x1ba@\x00\x00\x00\x0059h\xb0\x00\x00\x00\x005\xfbC@\x00\x00\x00\x007" + - "\x19J\xb0\x00\x00\x00\x007\xdb%@\x00\x00\x00\x008\xf9,\xb0\x00\x00\x00\x009\xbb\a@\x00\x00\x00\x00:\xd9*\xd0\x00\x00\x00\x00;\x91\xca\xe0\x00\x00\x00\x00<\xc2GP\x00\x00\x00\x00=q\xac\xe0\x00" + - "\x00\x00\x00>\xa2)P\x00\x00\x00\x00?Z\xc9`\x00\x00\x00\x00@\x82\vP\x00\x00\x00\x00A:\xab`\x00\x00\x00\x00Ba\xedP\x00\x00\x00\x00C\x1a\x8d`\x00\x00\x00\x00DA\xcfP\x00\x00\x00\x00D" + - "\xfao`\x00\x00\x00\x00F!\xb1P\x00\x00\x00\x00F\xdaQ`\x00\x00\x00\x00H\n\xcd\xd0\x00\x00\x00\x00H\xc3m\xe0\x00\x00\x00\x00I\xea\xaf\xd0\x00\x00\x00\x00J\xa3O\xe0\x00\x00\x00\x00Kʑ\xd0\x00" + - "\x00\x00\x00L\x831\xe0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\xff\xff\xc9\xc4\x00\x00\xff\xff\xc9\xc4\x00\x04\xff\xff\xd5\xd0\x01\b\xff\xff\xc7\xc0\x00\f\xff\xff\xe3\xe0\x01\x10\xff\xff\xd5\xd0\x00\bLMT\x00SMT" + - "\x00-03\x00-04\x00-02\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x82\xfa Z\x9b\x05\x00\x00\x9b\x05\x00\x00\x10\x00\x1c\x00Atlantic/" + - "MadeiraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x8a\x00\x00\x00\a\x00\x00\x00\x1d\xff\xff\xff\xff^=\x13X\xff\xff\xff\xff\x92朐\xff\xff\xff\xff\x9bK{\x80\xff\xff\xff\xff\x9b\xfeՐ\xff\xff\xff\xff\x9c\x9c\xfb\x80\xff\xff\xff\xff\x9dɑ\x80\xff\xff" + - "\xff\xff\x9e\u007f\x80\x80\xff\xff\xff\xff\x9f\xaa\xc5\x00\xff\xff\xff\xff\xa0_b\x80\xff\xff\xff\xff\xa1\x8b\xf8\x80\xff\xff\xff\xff\xa2A\xe7\x80\xff\xff\xff\xff\xa3n}\x80\xff\xff\xff\xff\xa4#\x1b\x00\xff\xff\xff\xff\xa5O" + - "\xb1\x00\xff\xff\xff\xff\xaa\x05\xfd\x80\xff\xff\xff\xff\xaa\xf4\x9d\x00\xff\xff\xff\xff\xadɶ\x00\xff\xff\xff\xff\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0]\x80\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89z\x00\xff\xff" + - "\xff\xff\xb2p0\x80\xff\xff\xff\xff\xb3r\x96\x80\xff\xff\xff\xff\xb4P\x12\x80\xff\xff\xff\xff\xb72Z\x80\xff\xff\xff\xff\xb8\x0fր\xff\xff\xff\xff\xb8\xffǀ\xff\xff\xff\xff\xb9︀\xff\xff\xff\xff\xbc\xc8" + - "\xc6\x00\xff\xff\xff\xff\xbd\xb8\xb7\x00\xff\xff\xff\xff\xbe\x9fm\x80\xff\xff\xff\xff\xbf\x98\x99\x00\xff\xff\xff\xff\xc0\x9a\xff\x00\xff\xff\xff\xff\xc1x{\x00\xff\xff\xff\xff\xc2hl\x00\xff\xff\xff\xff\xc3X]\x00\xff\xff" + - "\xff\xff\xc4?\x13\x80\xff\xff\xff\xff\xc58?\x00\xff\xff\xff\xff\xc6:\xa5\x00\xff\xff\xff\xff\xc7X\xba\x80\xff\xff\xff\xff\xc7\xd9\xed\x80\xff\xff\xff\xff\xc9\x01=\x80\xff\xff\xff\xff\xc9\xf1.\x80\xff\xff\xff\xff\xca\xe2" + - "q\x00\xff\xff\xff\xff˵a\x00\xff\xff\xff\xff\xcb\xec\xb1\xf0\xff\xff\xff\xff̀Y\xf0\xff\xff\xff\xff\xccܱ\x00\xff\xff\xff\xff͕C\x00\xff\xff\xff\xff\xcd\xc3Yp\xff\xff\xff\xff\xcer\xb0\xf0\xff\xff" + - "\xff\xff\xce\xc5̀\xff\xff\xff\xff\xcfu%\x00\xff\xff\xff\xffϬu\xf0\xff\xff\xff\xff\xd0R\x92\xf0\xff\xff\xff\xffХ\xaf\x80\xff\xff\xff\xff\xd1U\a\x00\xff\xff\xff\xffьW\xf0\xff\xff\xff\xff\xd22" + - "t\xf0\xff\xff\xff\xff҅\x91\x80\xff\xff\xff\xff\xd3Y\xd3\x00\xff\xff\xff\xff\xd4I\xc4\x00\xff\xff\xff\xff\xd59\xdf0\xff\xff\xff\xff\xd6)\xd00\xff\xff\xff\xff\xd7\x19\xc10\xff\xff\xff\xff\xd8\t\xb20\xff\xff" + - "\xff\xff\xd8\xf9\xa30\xff\xff\xff\xff\xd9\xe9\x940\xff\xff\xff\xffܹg0\xff\xff\xff\xffݲ\x92\xb0\xff\xff\xff\xffޢ\x83\xb0\xff\xff\xff\xffߒt\xb0\xff\xff\xff\xff\xe0\x82e\xb0\xff\xff\xff\xff\xe1r" + - "V\xb0\xff\xff\xff\xff\xe2bG\xb0\xff\xff\xff\xff\xe3R8\xb0\xff\xff\xff\xff\xe4B)\xb0\xff\xff\xff\xff\xe52\x1a\xb0\xff\xff\xff\xff\xe6\"\v\xb0\xff\xff\xff\xff\xe7\x1b70\xff\xff\xff\xff\xe8\v(0\xff\xff" + - "\xff\xff\xe8\xfb\x190\xff\xff\xff\xff\xe9\xeb\n0\xff\xff\xff\xff\xea\xda\xfb0\xff\xff\xff\xff\xeb\xca\xec0\xff\xff\xff\xff\xec\xba\xdd0\xff\xff\xff\xff\xed\xaa\xce0\xff\xff\xff\xff\ue6bf0\xff\xff\xff\xff\xef\x8a" + - "\xb00\xff\xff\xff\xff\xf0z\xa10\xff\xff\xff\xff\xf1j\x920\xff\xff\xff\xff\xf2c\xbd\xb0\xff\xff\xff\xff\xf3S\xae\xb0\xff\xff\xff\xff\xf4C\x9f\xb0\xff\xff\xff\xff\xf53\x90\xb0\xff\xff\xff\xff\xf6#\x81\xb0\xff\xff" + - "\xff\xff\xf7\x13r\xb0\xff\xff\xff\xff\xf8\x03c\xb0\xff\xff\xff\xff\xf8\xf3T\xb0\x00\x00\x00\x00\r\x9b\x1b\x00\x00\x00\x00\x00\x0e\x8b\f\x00\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10t(\x80\x00\x00\x00\x00\x11d" + - "\x19\x80\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13C\xfb\x80\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00" + - "\x00\x00\x18㽠\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|" + - "\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x8f\xc2`\x00\x00\x00\x00?" + - "\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02" + - "\x01\x02\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\xff\xff\xc3:\x00\x00\xff\xff\xd1J\x01\x04\xff\xff\xc3:\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff" + - "\xc7\xc0\x00\x10LMT\x00BST\x00BMT\x00ADT\x00AST\x00\nAST4ADT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xaf|7\xb3\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x1c\x00Atlantic/CanaryUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa6\x04\\\xf0\xff\xff\xff\xff\xd4A\xf7 \x00\x00\x00\x00\x13M6\x00" + - "\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00" + - "\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10" + - "\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#2\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xb7\x0e\xbdm\xb9\x01\x00\x00\xb9\x01\x00\x00\x0f\x00\x1c\x00Atlantic/FaeroeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\x8bm\xa4X\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16" + - "\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00" + - "\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x90\xff\xff\xff\xff\xb3r\xa4\x90\xff\xff\xff\xff\xb4P \x90\xff\xff\xff\xff\xb72h\x90\xff\xff\xff\xff\xb8\x0f\xe4\x90\xff\xff\xff\xff\xb8\xffՐ\xff\xff\xff\xff\xb9\xefƐ\xff\xff\xff" + - "\xff\xbc\xc8\xd4\x10\xff\xff\xff\xff\xbd\xb8\xc5\x10\xff\xff\xff\xff\xbe\x9f{\x90\xff\xff\xff\xff\xbf\x98\xa7\x10\xff\xff\xff\xff\xc0\x9b\r\x10\xff\xff\xff\xff\xc1x\x89\x10\xff\xff\xff\xff\xc2hz\x10\xff\xff\xff\xff\xc3Xk" + - "\x10\xff\xff\xff\xff\xc4?!\x90\xff\xff\xff\xff\xc58M\x10\xff\xff\xff\xff\xc6:\xb3\x10\xff\xff\xff\xff\xc7XȐ\xff\xff\xff\xff\xc7\xd9\xfb\x90\xff\xff\xff\xff\xc9\x01K\x90\xff\xff\xff\xff\xc9\xf1<\x90\xff\xff\xff" + - "\xff\xca\xe2\u007f\x10\xff\xff\xff\xff˵o\x10\xff\xff\xff\xff\xcb\xec\xc0\x00\xff\xff\xff\xff̀h\x00\xff\xff\xff\xff\xccܿ\x10\xff\xff\xff\xff͕Q\x10\xff\xff\xff\xff\xcd\xc3g\x80\xff\xff\xff\xff\xcer\xbf" + - "\x00\xff\xff\xff\xff\xce\xc5ې\xff\xff\xff\xff\xcfu3\x10\xff\xff\xff\xffϬ\x84\x00\xff\xff\xff\xff\xd0R\xa1\x00\xff\xff\xff\xffХ\xbd\x90\xff\xff\xff\xff\xd1U\x15\x10\xff\xff\xff\xffьf\x00\xff\xff\xff" + - "\xff\xd22\x83\x00\xff\xff\xff\xff҅\x9f\x90\xff\xff\xff\xff\xd3Y\xe1\x10\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd59\xed@\xff\xff\xff\xff\xd6)\xde@\xff\xff\xff\xff\xd7\x19\xcf@\xff\xff\xff\xff\xd8\t\xc0" + - "@\xff\xff\xff\xff\xd8\xf9\xb1@\xff\xff\xff\xff\xd9\xe9\xa2@\xff\xff\xff\xffܹu@\xff\xff\xff\xffݲ\xa0\xc0\xff\xff\xff\xffޢ\x91\xc0\xff\xff\xff\xffߒ\x82\xc0\xff\xff\xff\xff\xe0\x82s\xc0\xff\xff\xff" + - "\xff\xe1rd\xc0\xff\xff\xff\xff\xe2bU\xc0\xff\xff\xff\xff\xe3RF\xc0\xff\xff\xff\xff\xe4B7\xc0\xff\xff\xff\xff\xe52(\xc0\xff\xff\xff\xff\xe6\"\x19\xc0\xff\xff\xff\xff\xe7\x1bE@\xff\xff\xff\xff\xe8\v6" + - "@\xff\xff\xff\xff\xe8\xfb'@\xff\xff\xff\xff\xe9\xeb\x18@\xff\xff\xff\xff\xea\xdb\t@\xff\xff\xff\xff\xeb\xca\xfa@\xff\xff\xff\xff\xec\xba\xeb@\xff\xff\xff\xff\xed\xaa\xdc@\xff\xff\xff\xff\xee\x9a\xcd@\xff\xff\xff" + - "\xff\uf2be@\xff\xff\xff\xff\xf0z\xaf@\xff\xff\xff\xff\xf1j\xa0@\xff\xff\xff\xff\xf2c\xcb\xc0\xff\xff\xff\xff\xf3S\xbc\xc0\xff\xff\xff\xff\xf4C\xad\xc0\xff\xff\xff\xff\xf53\x9e\xc0\xff\xff\xff\xff\xf6#\x8f" + - "\xc0\xff\xff\xff\xff\xf7\x13\x80\xc0\xff\xff\xff\xff\xf8\x03q\xc0\xff\xff\xff\xff\xf8\xf3b\xc0\x00\x00\x00\x00\r\x9b)\x10\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00" + - "\x00\x11d'\x90\x00\x00\x00\x00\x12T&\xa0\x00\x00\x00\x00\x13D\t\x90\x00\x00\x00\x00\x144\b\xa0\x00\x00\x00\x00\x15#\xf9\xa0\x00\x00\x00\x00\x16\x13\xea\xa0\x00\x00\x00\x00\x17\x03۠\x00\x00\x00\x00\x17\xf3\xcc" + - "\xa0\x00\x00\x00\x00\x18\xe3˰\x00\x00\x00\x00\x19Ӯ\xa0\x00\x00\x00\x00\x1aß\xa0\x00\x00\x00\x00\x1b\xbc\xcb \x00\x00\x00\x00\x1c\xac\xbc \x00\x00\x00\x00\x1d\x9c\xad \x00\x00\x00\x00\x1e\x8c\x9e \x00\x00\x00" + - "\x00\x1f|\x8f \x00\x00\x00\x00 l\x80 \x00\x00\x00\x00!\\q \x00\x00\x00\x00\"Lb \x00\x00\x00\x00#1<+00>,M3.5.0/0,M10.5.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n" + - "\x00\x1c\x00Australia/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RX\xb9\x9ap\x88" + - "\x03\x00\x00\x88\x03\x00\x00\r\x00\x1c\x00Australia/NSWUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\u007f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff" + - "\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05" + - "P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00" + - "\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13" + - "\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00" + - "\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!" + - "\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00" + - "\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/" + - "X\x8e\x80\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00" + - "\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=" + - "\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00" + - "\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00" + - "\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9R\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x12\x00\x1c\x00Australia/AdelaideUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x8b\x14\xff\xff\xff\xff{" + - "\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff" + - "\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t" + - "\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\u007f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00" + - "\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x16" + - "禈\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00" + - "\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%" + - "I\xd4\b\x00\x00\x00\x00&\x02f\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xcbd\x88\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00" + - "\x00\x00\x00,Җ\x88\x00\x00\x00\x00-\x8b(\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/tE\b\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003" + - "=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00" + - "\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A" + - "\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x81\xec\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT" + - "\x00\nACST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x12" + - "\x00\x1c\x00Australia/VictoriaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x85\x18\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff" + - "\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80" + - "\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00" + - "\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00" + - "\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x16矀\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00" + - "\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!w\x94\x00" + - "\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00" + - ")\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/t>\x00" + - "\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x00" + - "6\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00" + - "\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00" + - "EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x87\xe8\x00\x00\x00\x00\x9a" + - "\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9Ro3\xdaR\xb4\x02\x00\x00\xb4\x02\x00\x00\r\x00\x1c\x00Australia/LHIUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x05\x00\x00\x00\x19\xff\xff\xff\xffs\x16w\xdc\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x16" + - "8@\xf8\x00\x00\x00\x00\x16\xe7\x8ah\x00\x00\x00\x00\x18!]x\x00\x00\x00\x00\x18\xc7lh\x00\x00\x00\x00\x1a\x01?x\x00\x00\x00\x00\x1a\xa7Nh\x00\x00\x00\x00\x1b\xe1!x\x00\x00\x00\x00\x1c\x870h\x00" + - "\x00\x00\x00\x1d\xc1\x03x\x00\x00\x00\x00\x1ey\x8ep\x00\x00\x00\x00\x1f\x97\xaa\xf8\x00\x00\x00\x00 Ypp\x00\x00\x00\x00!\x80\xc7x\x00\x00\x00\x00\"B\x8c\xf0\x00\x00\x00\x00#i\xe3\xf8\x00\x00\x00\x00$" + - "\"n\xf0\x00\x00\x00\x00%I\xc5\xf8\x00\x00\x00\x00%\xef\xdb\xf0\x00\x00\x00\x00')\xa7\xf8\x00\x00\x00\x00'Ͻ\xf0\x00\x00\x00\x00)\t\x89\xf8\x00\x00\x00\x00)\xaf\x9f\xf0\x00\x00\x00\x00*\xe9k\xf8\x00" + - "\x00\x00\x00+\x98\xbcp\x00\x00\x00\x00,҈x\x00\x00\x00\x00-x\x9ep\x00\x00\x00\x00.\xb2jx\x00\x00\x00\x00/X\x80p\x00\x00\x00\x000\x92Lx\x00\x00\x00\x001]Lp\x00\x00\x00\x002" + - "r.x\x00\x00\x00\x003=.p\x00\x00\x00\x004R\x10x\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xf2x\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\x0e\xf8\x00\x00\x00\x008\xdc\xd4p\x00" + - "\x00\x00\x009\xa7\xe2x\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xd2\xf8\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xb4\xf8\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x96\xf8\x00\x00\x00\x00@" + - "e\x96\xf0\x00\x00\x00\x00A\x83\xb3x\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x95x\x00\x00\x00\x00D.\x95p\x00\x00\x00\x00ECwx\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Yx\x00" + - "\x00\x00\x00G\xf7\x93\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x00\x00\x95$\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa1\xb8\x01\t\x00\x00\x93\xa8\x00\x0f\x00\x00\x9a\xb0\x01\x15LMT\x00AEST\x00+1130\x00+1030\x00+11\x00\n<" + - "+1030>-10:30<+11>-11,M10.1.0,M4.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc8R\x1a\x1b\xea\x00\x00\x00\xea\x00" + - "\x00\x00\x0f\x00\x1c\x00Australia/NorthUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x92X\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff" + - "\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00z\xa8\x00\x00" + - "\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT\x00\nACST-9:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc8R\x1a" + - "\x1b\xea\x00\x00\x00\xea\x00\x00\x00\x10\x00\x1c\x00Australia/DarwinUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa6\x04\\\xf0\xff\xff\xff\xff\xd4A\xf7 \x00\x00\x00\x00\x13M6\x00\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90" + + "\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00" + + "\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x90\xff\xff\xff\xff\xb3r\xa4\x90\xff\xff\xff\xff\xb4P \x90\xff\xff\xff\xff\xb72h\x90\xff\xff\xff\xff\xb8\x0f\xe4\x90\xff" + + "\xff\xff\xff\xb8\xffՐ\xff\xff\xff\xff\xb9\xefƐ\xff\xff\xff\xff\xbc\xc8\xd4\x10\xff\xff\xff\xff\xbd\xb8\xc5\x10\xff\xff\xff\xff\xbe\x9f{\x90\xff\xff\xff\xff\xbf\x98\xa7\x10\xff\xff\xff\xff\xc0\x9b\r\x10\xff\xff\xff\xff\xc1" + + "x\x89\x10\xff\xff\xff\xff\xc2hz\x10\xff\xff\xff\xff\xc3Xk\x10\xff\xff\xff\xff\xc4?!\x90\xff\xff\xff\xff\xc58M\x10\xff\xff\xff\xff\xc6:\xb3\x10\xff\xff\xff\xff\xc7XȐ\xff\xff\xff\xff\xc7\xd9\xfb\x90\xff" + + "\xff\xff\xff\xc9\x01K\x90\xff\xff\xff\xff\xc9\xf1<\x90\xff\xff\xff\xff\xca\xe2\x7f\x10\xff\xff\xff\xff˵o\x10\xff\xff\xff\xff\xcb\xec\xc0\x00\xff\xff\xff\xff̀h\x00\xff\xff\xff\xff\xccܿ\x10\xff\xff\xff\xff\xcd" + + "\x95Q\x10\xff\xff\xff\xff\xcd\xc3g\x80\xff\xff\xff\xff\xcer\xbf\x00\xff\xff\xff\xff\xce\xc5ې\xff\xff\xff\xff\xcfu3\x10\xff\xff\xff\xffϬ\x84\x00\xff\xff\xff\xff\xd0R\xa1\x00\xff\xff\xff\xffХ\xbd\x90\xff" + + "\xff\xff\xff\xd1U\x15\x10\xff\xff\xff\xffьf\x00\xff\xff\xff\xff\xd22\x83\x00\xff\xff\xff\xff҅\x9f\x90\xff\xff\xff\xff\xd3Y\xe1\x10\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd59\xed@\xff\xff\xff\xff\xd6" + + ")\xde@\xff\xff\xff\xff\xd7\x19\xcf@\xff\xff\xff\xff\xd8\t\xc0@\xff\xff\xff\xff\xd8\xf9\xb1@\xff\xff\xff\xff\xd9\xe9\xa2@\xff\xff\xff\xff\xdaٓ@\xff\xff\xff\xff\xdbɄ@\xff\xff\xff\xffܹu@\xff" + + "\xff\xff\xffݲ\xa0\xc0\xff\xff\xff\xffޢ\x91\xc0\xff\xff\xff\xffߒ\x82\xc0\xff\xff\xff\xff\xe0\x82s\xc0\xff\xff\xff\xff\xe1rd\xc0\xff\xff\xff\xff\xe2bU\xc0\xff\xff\xff\xff\xe3RF\xc0\xff\xff\xff\xff\xe4" + + "B7\xc0\xff\xff\xff\xff\xe52(\xc0\xff\xff\xff\xff\xe6\"\x19\xc0\xff\xff\xff\xff\xe7\x1bE@\xff\xff\xff\xff\xe8\v6@\xff\xff\xff\xff\xe8\xfb'@\xff\xff\xff\xff\xe9\xeb\x18@\xff\xff\xff\xff\xea\xdb\t@\xff" + + "\xff\xff\xff\xeb\xca\xfa@\xff\xff\xff\xff\xec\xba\xeb@\xff\xff\xff\xff\xed\xaa\xdc@\xff\xff\xff\xff\xee\x9a\xcd@\xff\xff\xff\xff\uf2be@\xff\xff\xff\xff\xf0z\xaf@\xff\xff\xff\xff\xf1j\xa0@\xff\xff\xff\xff\xf2" + + "c\xcb\xc0\xff\xff\xff\xff\xf3S\xbc\xc0\xff\xff\xff\xff\xf4C\xad\xc0\xff\xff\xff\xff\xf53\x9e\xc0\xff\xff\xff\xff\xf6#\x8f\xc0\xff\xff\xff\xff\xf7\x13\x80\xc0\xff\xff\xff\xff\xf8\x03q\xc0\xff\xff\xff\xff\xf8\xf3b\xc0\x00" + + "\x00\x00\x00\r\x9b)\x10\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T&\xa0\x00\x00\x00\x00\x13D\t\x90\x00\x00\x00\x00\x14" + + "4\b\xa0\x00\x00\x00\x00\x15#\xf9\xa0\x00\x00\x00\x00\x16\x13\xea\xa0\x00\x00\x00\x00\x17\x03۠\x00\x00\x00\x00\x17\xf3̠\x00\x00\x00\x00\x18\xe3˰\x00\x00\x00\x00\x19Ӯ\xa0\x00\x00\x00\x00\x1aß\xa0\x00" + + "\x00\x00\x00\x1b\xbc\xcb \x00\x00\x00\x00\x1c\xac\xbc \x00\x00\x00\x00\x1d\x9c\xad \x00\x00\x00\x00\x1e\x8c\x9e \x00\x00\x00\x00\x1f|\x8f \x00\x00\x00\x00 l\x80 \x00\x00\x00\x00!\\q \x00\x00\x00\x00\"" + + "Lb \x00\x00\x00\x00#1<+00>,M3.5.0/0,M" + + "10.5.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x1c\x00Atlantic/St_HelenaUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b" + + "\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x0f-\xadׄ\x00\x00\x00\x84\x00\x00" + + "\x00\x16\x00\x1c\x00Atlantic/South_GeorgiaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xffi\x86\xfd\xc0\x01\xff\xff\xdd\xc0\x00\x00\xff\xff\xe3\xe0\x00\x04LMT\x00-02\x00\n" + + "<-02>2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x12\x00\x1c\x00Atlantic/Jan_MayenUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff" + + "\xff\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\t" + + "q\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff" + + "\xff\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)" + + "\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00" + + "\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac" + + "\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x1c\x00Australia/" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\r\x00\x1c\x00Au" + + "stralia/ACTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x7f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷" + + "V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00" + + "\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~" + + "\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00" + + "\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1" + + "(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00" + + "\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf" + + "\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00\x00\x000\x92S\x80\x00\x00" + + "\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b" + + "\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00" + + "\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05" + + "K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tL" + + "MT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc8R\x1a" + + "\x1b\xea\x00\x00\x00\xea\x00\x00\x00\x10\x00\x1c\x00Australia/DarwinUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x92X\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff" + "\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x01\x03\x02\x03\x02\x03\x02\x03" + "\x02\x03\x00\x00z\xa8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT\x00\nACST-9:30\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xbd\xca#\u007f\xad\x03\x00\x00\xad\x03\x00\x00\x14\x00\x1c\x00Australia/YancowinnaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\x00\x00\x00\x05\x00\x00\x00\x13\xff\xff\xff\xffs\x16\x88d\xff\xff\xff\xffv\x04\xa5\xe0" + - "\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xff" + - "Πz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88" + - "\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\u007f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00" + - "\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b" + - "\x00\x00\x00\x00\x17\f\x90\x88\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00" + - "\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b" + - "\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00%\xef\xf1\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xaf\xb5\b\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00" + - "+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-x\xb3\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/X\x95\x88\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88" + - "\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x00" + - "9\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b" + - "\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00" + - "G\xf7\xa9\b\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00\x84\x9c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\t\x00\x00\x93\xa8\x01\x0e\x00\x00\x85\x98\x00\tL" + - "MT\x00AEST\x00ACST\x00ACDT\x00\nACST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R3\xba\xde\xd3!\x01\x00\x00!\x01\x00\x00\x14\x00\x1c\x00Australia/QueenslandUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\x9f\b\xff\xff\xff\xff\x9cN\u0080\xff" + - "\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03" + - "p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8fx\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\x10\x00\x1c\x00Australia/SydneyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\u007f<\xff\xff\xff\xff\x9cN" + - "\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00" + - "\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5" + - "\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00" + - "\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!" + - "d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00" + - "\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef" + - "\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00" + - "\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R" + - "\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00" + - "\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE" + - "\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M1" + - "0.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x12\x00\x1c\x00Australia/Tasma" + - "niaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00" + - "\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a\x80\xff\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`" + - "C\x80\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff" + - "\xff\xff\xfc\xb2~\x00\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p" + - "9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00" + - "\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>" + - "\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00" + - "\x00\x00\x18\xe31\x00\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97" + - "\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00" + - "\x00\x00')\xaf\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94" + - "\\\x00\x00\x00\x00\x00.\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00" + - "\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf" + - "*\x80\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00" + - "\x00\x00C>\xb2\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10" + - ".1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x0f\x00\x1c\x00Australia/SouthU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x04\x00\x00" + - "\x00\x0e\xff\xff\xff\xffs\x16\x8b\x14\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff" + - "\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0" + - "\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\u007f\x02\b\x00\x00" + - "\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe" + - "\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x16禈\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00" + - "\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i" + - "\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00&\x02f\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xcbd\x88\x00\x00" + - "\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-\x8b(\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/tE\b\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]" + - "a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00" + - "\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a" + - "\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00" + - "\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x81\xec\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98" + - "\x00\x04LMT\x00ACST\x00ACDT\x00\nACST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9RX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x1c\x00Australia/CanberraUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\u007f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c" + - "\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00" + - "\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n" + - "\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00" + - "\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18" + - "ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00" + - "\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00'" + - ")\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00" + - "\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005" + - "\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00" + - "\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C" + - "c\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M" + - "4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00\x1c\x00Australia/CurrieUT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff" + - "\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a\x80\xff\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`C\x80\xff\xff\xff\xff\xcbT\xb3" + - "\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc\xb2~\x00\xff\xff\xff" + - "\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c" + - "\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00" + - "\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84" + - "\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18\xe31\x00\x00\x00\x00" + - "\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~" + - "\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00" + - "\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x8d\x87" + - "\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00" + - "\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00\x00\x00\x00<\xa5\xe1" + - "\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C>\xb2\x80\x00\x00\x00" + - "\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1" + - ".0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xa2ܺ\xca:\x01\x00\x00:\x01\x00\x00\x0f\x00\x1c\x00Australia/EuclaUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x10\xff\xff\xff\xfft\xa6\n" + - "\xb0\xff\xff\xff\xff\x9cN\xd4\x14\xff\xff\xff\xff\x9c\xbc@\x94\xff\xff\xff\xff\xcbTĔ\xff\xff\xff\xff\xcb\xc7w\x14\xff\xff\xff\xff̷h\x14\xff\xff\xff\xffͧY\x14\x00\x00\x00\x00\t\x0f\xf1\x14\x00\x00\x00" + - "\x00\t\xb6\x0e\x14\x00\x00\x00\x00\x1a\x01X\x14\x00\x00\x00\x00\x1a\xa7u\x14\x00\x00\x00\x00)%R\x14\x00\x00\x00\x00)\xaf\xbf\x94\x00\x00\x00\x00Eq\xb4\x94\x00\x00\x00\x00F\x05\\\x94\x00\x00\x00\x00G#r" + - "\x14\x00\x00\x00\x00G\xeey\x14\x00\x00\x00\x00I\x03T\x14\x00\x00\x00\x00I\xce[\x14\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00x\xd0\x00\x00\x00\x00\x89\x1c\x01\x04\x00\x00{\f" + - "\x00\nLMT\x00+0945\x00+0845\x00\n<+0845>-8:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00" + - "\x1c\x00Australia/HobartUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a" + - "\x80\xff\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`C\x80\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff" + - "\xffχ)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc\xb2~\x00\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d" + - "\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00" + - "\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd" + - "\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00" + - "\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18\xe31\x00\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n" + - "\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00" + - "\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z" + - "\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00" + - "\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9" + - "\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00" + - "\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C>\xb2\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\n" + - "AEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xbd\xca#\u007f\xad\x03\x00\x00\xad\x03\x00\x00\x15\x00\x1c\x00A" + - "ustralia/Broken_HillUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\x00\x00\x00\x05\x00\x00\x00\x13\xff\xff\xff\xffs\x16\x88d\xff\xff\xff\xffv\x04\xa5\xe0\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c" + - "\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00" + - "\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n" + - "\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\u007f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00" + - "\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x17\f\x90\x88\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18" + - "Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00" + - "\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00%\xef\xf1\b\x00\x00\x00\x00'" + - ")\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xaf\xb5\b\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-x\xb3\x88\x00" + - "\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/X\x95\x88\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005" + - "\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00" + - "\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00C" + - "c\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00\x84\x9c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\t\x00\x00\x93\xa8\x01\x0e\x00\x00\x85\x98\x00\tLMT\x00AEST\x00ACST\x00ACDT\x00\nAC" + - "ST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?\x95\xbd\x12E\x01\x00\x00E\x01\x00\x00\x12\x00\x1c\x00A" + - "ustralia/LindemanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\xa2\xd4\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80" + - "\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00" + - "%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80" + - "\x00\x00\x00\x00-x\xac\x80\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8b\xac\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST" + - "\x00\nAEST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x13\x00\x1c\x00Australia/MelbourneU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\bv\vUϻ\xca\x1a2\x01\x00\x002\x01\x00\x00\x0f\x00\x1c\x00Australia/PerthUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft\xa6\x16\xe4\xff\xff\xff\xff\x9cNޠ\xff\xff\xff\xff\x9c" + + "\xbcK \xff\xff\xff\xff\xcbT\xcf \xff\xff\xff\xff\xcbǁ\xa0\xff\xff\xff\xff̷r\xa0\xff\xff\xff\xffͧc\xa0\x00\x00\x00\x00\t\x0f\xfb\xa0\x00\x00\x00\x00\t\xb6\x18\xa0\x00\x00\x00\x00\x1a\x01b\xa0\x00" + + "\x00\x00\x00\x1a\xa7\x7f\xa0\x00\x00\x00\x00)%\\\xa0\x00\x00\x00\x00)\xaf\xca \x00\x00\x00\x00Eq\xbf \x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00\x00I" + + "\x03^\xa0\x00\x00\x00\x00I\xcee\xa0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00l\x9c\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\tLMT\x00AWDT\x00AWS" + + "T\x00\nAWST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x13\x00\x1c\x00Australia/MelbourneU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00" + "\x00\x0e\xff\xff\xff\xffs\x16\x85\x18\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff" + "\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6" + @@ -3812,878 +3605,1235 @@ const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00 "\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00" + "\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x87\xe8\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST" + - "\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R3\xba\xde\xd3!\x01\x00\x00!\x01\x00\x00\x12\x00\x1c" + - "\x00Australia/BrisbaneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x1c" + + "\x00Australia/CanberraUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\x9f\b\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7" + - "e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00" + - "\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8fx\x00" + - "\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rϻ\xca\x1a2\x01\x00\x002\x01" + - "\x00\x00\x0f\x00\x1c\x00Australia/PerthUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft\xa6\x16\xe4\xff\xff\xff\xff\x9cNޠ\xff\xff\xff\xff\x9c\xbcK \xff\xff\xff\xff\xcbT\xcf \xff\xff\xff\xff" + - "\xcbǁ\xa0\xff\xff\xff\xff̷r\xa0\xff\xff\xff\xffͧc\xa0\x00\x00\x00\x00\t\x0f\xfb\xa0\x00\x00\x00\x00\t\xb6\x18\xa0\x00\x00\x00\x00\x1a\x01b\xa0\x00\x00\x00\x00\x1a\xa7\u007f\xa0\x00\x00\x00\x00)%\\\xa0" + - "\x00\x00\x00\x00)\xaf\xca \x00\x00\x00\x00Eq\xbf \x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00\x00I\x03^\xa0\x00\x00\x00\x00I\xcee\xa0\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00l\x9c\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\tLMT\x00AWDT\x00AWST\x00\nAWST-8\nPK\x03\x04\n" + - "\x00\x00\x00\x00\x00\xf1c9Ro3\xdaR\xb4\x02\x00\x00\xb4\x02\x00\x00\x13\x00\x1c\x00Australia/Lord_HoweUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x05\x00\x00\x00\x19\xff\xff\xff\xffs\x16w\xdc\x00\x00\x00\x00\x14" + - "\xfef\xe0\x00\x00\x00\x00\x168@\xf8\x00\x00\x00\x00\x16\xe7\x8ah\x00\x00\x00\x00\x18!]x\x00\x00\x00\x00\x18\xc7lh\x00\x00\x00\x00\x1a\x01?x\x00\x00\x00\x00\x1a\xa7Nh\x00\x00\x00\x00\x1b\xe1!x\x00" + - "\x00\x00\x00\x1c\x870h\x00\x00\x00\x00\x1d\xc1\x03x\x00\x00\x00\x00\x1ey\x8ep\x00\x00\x00\x00\x1f\x97\xaa\xf8\x00\x00\x00\x00 Ypp\x00\x00\x00\x00!\x80\xc7x\x00\x00\x00\x00\"B\x8c\xf0\x00\x00\x00\x00#" + - "i\xe3\xf8\x00\x00\x00\x00$\"n\xf0\x00\x00\x00\x00%I\xc5\xf8\x00\x00\x00\x00%\xef\xdb\xf0\x00\x00\x00\x00')\xa7\xf8\x00\x00\x00\x00'Ͻ\xf0\x00\x00\x00\x00)\t\x89\xf8\x00\x00\x00\x00)\xaf\x9f\xf0\x00" + - "\x00\x00\x00*\xe9k\xf8\x00\x00\x00\x00+\x98\xbcp\x00\x00\x00\x00,҈x\x00\x00\x00\x00-x\x9ep\x00\x00\x00\x00.\xb2jx\x00\x00\x00\x00/X\x80p\x00\x00\x00\x000\x92Lx\x00\x00\x00\x001" + - "]Lp\x00\x00\x00\x002r.x\x00\x00\x00\x003=.p\x00\x00\x00\x004R\x10x\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xf2x\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\x0e\xf8\x00" + - "\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xa7\xe2x\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xd2\xf8\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xb4\xf8\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?" + - "\x9a\x96\xf8\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xb3x\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x95x\x00\x00\x00\x00D.\x95p\x00\x00\x00\x00ECwx\x00\x00\x00\x00F\x05<\xf0\x00" + - "\x00\x00\x00G#Yx\x00\x00\x00\x00G\xf7\x93\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x95$\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa1\xb8\x01\t\x00\x00\x93\xa8\x00\x0f\x00\x00\x9a\xb0\x01\x15LMT\x00AEST\x00+1130\x00+103" + - "0\x00+11\x00\n<+1030>-10:30<+11>-11,M10.1.0,M4.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rϻ" + - "\xca\x1a2\x01\x00\x002\x01\x00\x00\x0e\x00\x1c\x00Australia/WestUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft\xa6\x16\xe4\xff\xff\xff\xff\x9cNޠ\xff\xff\xff\xff\x9c\xbcK \xff\xff\xff\xff\xcb" + - "T\xcf \xff\xff\xff\xff\xcbǁ\xa0\xff\xff\xff\xff̷r\xa0\xff\xff\xff\xffͧc\xa0\x00\x00\x00\x00\t\x0f\xfb\xa0\x00\x00\x00\x00\t\xb6\x18\xa0\x00\x00\x00\x00\x1a\x01b\xa0\x00\x00\x00\x00\x1a\xa7\u007f\xa0\x00" + - "\x00\x00\x00)%\\\xa0\x00\x00\x00\x00)\xaf\xca \x00\x00\x00\x00Eq\xbf \x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00\x00I\x03^\xa0\x00\x00\x00\x00I" + - "\xcee\xa0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00l\x9c\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\tLMT\x00AWDT\x00AWST\x00\nAWST-" + - "8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\r\x00\x1c\x00Australia/ACTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\u007f<\xff\xff\xff\xff" + - "\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80" + - "\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00" + - "\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00" + - "\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00" + - "\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80" + - "\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00" + - "%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80" + - "\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x00" + - "4R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ" + - "\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00" + - "BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT," + - "M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Brazil/UT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9d?\xdfڸ\x03\x00\x00\xb8\x03\x00\x00\v\x00\x1c\x00Brazil/E" + - "astUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[\x00\x00" + - "\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaar\xb4\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff\xff\xff\xff\xda\xeb" + - "\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4Z\t0\xff\xff" + - "\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff\xff\xff\xff\xfb\xec" + - "\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"\vȠ\x00\x00" + - "\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xbd\xe3\xa0\x00\x00\x00\x00)\x00\xf10\x00\x00\x00\x00)\x94" + - "\x8b \x00\x00\x00\x00*\xea\r\xb0\x00\x00\x00\x00+k2\xa0\x00\x00\x00\x00,\xc0\xb50\x00\x00\x00\x00-f\xc4 \x00\x00\x00\x00.\xa0\x970\x00\x00\x00\x00/F\xa6 \x00\x00\x00\x000\x80y0\x00\x00" + - "\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00\x00\x004\xf8\xc1 \x00\x00\x00\x006 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00\x00\x00\x007\xf6" + - "ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x00N\xf0\xa0\x00\x00" + - "\x00\x00?\x91\xfe0\x00\x00\x00\x00@.Ҡ\x00\x00\x00\x00A\x86\xf80\x00\x00\x00\x00B\x17\xef \x00\x00\x00\x00CQ\xc20\x00\x00\x00\x00C\xf7\xd1 \x00\x00\x00\x00EMS\xb0\x00\x00\x00\x00E\xe0" + - "\xed\xa0\x00\x00\x00\x00G\x11\x860\x00\x00\x00\x00G\xb7\x95 \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\x97w \x00\x00\x00\x00Jڄ\xb0\x00\x00\x00\x00K\x80\x93\xa0\x00\x00\x00\x00L\xbaf\xb0\x00\x00" + - "\x00\x00M`u\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x00\x00\x00\x00P\x83e0\x00\x00\x00\x00Q 9\xa0\x00\x00\x00\x00RcG0\x00\x00\x00\x00S\x00\x1b\xa0\x00\x00\x00\x00TC" + - ")0\x00\x00\x00\x00T\xe98 \x00\x00\x00\x00V#\v0\x00\x00\x00\x00V\xc9\x1a \x00\x00\x00\x00X\x02\xed0\x00\x00\x00\x00X\xa8\xfc \x00\x00\x00\x00Y\xe2\xcf0\x00\x00\x00\x00Z\x88\xde \x00\x00" + - "\x00\x00[\xde`\xb0\x00\x00\x00\x00\\h\xc0 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd4L\x00\x00\xff\xff\xe3\xe0\x01\x04\xff\xff\xd5" + - "\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\v\x00\x1c\x00Brazil/" + - "AcreUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00" + - "\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x86\x90\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xbaސ@\xff\xff\xff\xff\xda8\xcaP\xff\xff\xff\xff\xda" + - "\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff\xff\xff\xff\xe0TO@\xff\xff\xff\xff\xf4\x98\x1b\xd0\xff" + - "\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14\xc0\xff\xff\xff\xff\xfb" + - "\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"\v\xe4\xc0\x00" + - "\x00\x00\x00H`\u007fP\x00\x00\x00\x00R\u007f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xc0p\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff" + - "\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb7-2f\xe4\x01\x00\x00\xe4\x01\x00\x00\x10\x00\x1c\x00" + - "Brazil/DeNoronhaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaaed\xff\xff\xff\xff\xb8\x0f;\xd0\xff\xff\xff\xff\xb8\xfd2\x90\xff\xff\xff\xff\xb9\xf1& \xff\xff\xff\xff\xba\xdef\x10\xff" + - "\xff\xff\xff\xda8\xa0 \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdc\x19Ӡ\xff\xff\xff\xffܹK\x10\xff\xff\xff\xff\xdd\xfb\a \xff\xff\xff\xffޛ\xd0\x10\xff\xff\xff\xff\xdf\u074c \xff\xff\xff\xff\xe0" + - "T%\x10\xff\xff\xff\xff\xf4\x97\xf1\xa0\xff\xff\xff\xff\xf5\x05P\x10\xff\xff\xff\xff\xf6\xc0V \xff\xff\xff\xff\xf7\x0e\x10\x90\xff\xff\xff\xff\xf8Q\x1e \xff\xff\xff\xff\xf8Ƿ\x10\xff\xff\xff\xff\xfa\nĠ\xff" + - "\xff\xff\xff\xfa\xa8\xea\x90\xff\xff\xff\xff\xfb\xeb\xf8 \xff\xff\xff\xff\xfc\x8bo\x90\x00\x00\x00\x00\x1dɀ \x00\x00\x00\x00\x1exɐ\x00\x00\x00\x00\x1f\xa0'\xa0\x00\x00\x00\x00 3\xc1\x90\x00\x00\x00\x00!" + - "\x81[ \x00\x00\x00\x00\"\v\xba\x90\x00\x00\x00\x00#X\x02\xa0\x00\x00\x00\x00#\xe2b\x10\x00\x00\x00\x00%7\xe4\xa0\x00\x00\x00\x00%Թ\x10\x00\x00\x00\x007\xf6\xb8\xa0\x00\x00\x00\x008\xb8w\x10\x00" + - "\x00\x00\x009\xdf\xd5 \x00\x00\x00\x009\xe9\x01\x90\x00\x00\x00\x00;\xc8\xf1\xa0\x00\x00\x00\x002\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9Ra\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\v\x00\x1c\x00Brazil/WestUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\u007fD\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff" + - "\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0" + - "\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff" + - "\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0" + - "\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00\"\vְ\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xffǼ\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Canada/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9RU9#\xbe2\x05\x00\x002\x05\x00\x00\x0e\x00\x1c\x00Canada/PacificUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^=v\xec\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff" + - "\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd3v\x0f \xff\xff\xff\xff\xd4A\b\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea" + - "\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc\x10\xff\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff" + - "\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;" + - "\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX \xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff" + - "\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\u007f\xc1" + - "\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff" + - "\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98)" + - " \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00" + - "\x00\b \xeb\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\n\x00͠\x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf" + - "\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00" + - "\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16" + - "\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00" + - "\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab" + - "\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00" + - "\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a" + - " \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00" + - "\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x03\x04\x02\x01" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x7f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7" + + "e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00" + + "\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8" + + "\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00" + + "\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7" + + "c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00" + + "\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t" + + "\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00" + + "\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd" + + "\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00" + + "\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC" + + "~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01" + + "\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x1c\x00Australia/VictoriaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x85\x18\xff\xff\xff\xff\x9cN\u0080\xff\xff" + + "\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p" + + "9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00" + + "\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>" + + "\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x16矀\x00\x00\x00\x00\x18!d\x80\x00\x00" + + "\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97" + + "\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!w\x94\x00\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00" + + "\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x" + + "\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00" + + "\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda" + + "\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00" + + "\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x87\xe8\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1." + + "0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUo3\xdaR\xb4\x02\x00\x00\xb4\x02\x00\x00\r\x00\x1c\x00Australia/LHIUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x05\x00\x00\x00\x19\xff\xff\xff" + + "\xffs\x16w\xdc\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168@\xf8\x00\x00\x00\x00\x16\xe7\x8ah\x00\x00\x00\x00\x18!]x\x00\x00\x00\x00\x18\xc7lh\x00\x00\x00\x00\x1a\x01?x\x00\x00\x00\x00\x1a\xa7N" + + "h\x00\x00\x00\x00\x1b\xe1!x\x00\x00\x00\x00\x1c\x870h\x00\x00\x00\x00\x1d\xc1\x03x\x00\x00\x00\x00\x1ey\x8ep\x00\x00\x00\x00\x1f\x97\xaa\xf8\x00\x00\x00\x00 Ypp\x00\x00\x00\x00!\x80\xc7x\x00\x00\x00" + + "\x00\"B\x8c\xf0\x00\x00\x00\x00#i\xe3\xf8\x00\x00\x00\x00$\"n\xf0\x00\x00\x00\x00%I\xc5\xf8\x00\x00\x00\x00%\xef\xdb\xf0\x00\x00\x00\x00')\xa7\xf8\x00\x00\x00\x00'Ͻ\xf0\x00\x00\x00\x00)\t\x89" + + "\xf8\x00\x00\x00\x00)\xaf\x9f\xf0\x00\x00\x00\x00*\xe9k\xf8\x00\x00\x00\x00+\x98\xbcp\x00\x00\x00\x00,҈x\x00\x00\x00\x00-x\x9ep\x00\x00\x00\x00.\xb2jx\x00\x00\x00\x00/X\x80p\x00\x00\x00" + + "\x000\x92Lx\x00\x00\x00\x001]Lp\x00\x00\x00\x002r.x\x00\x00\x00\x003=.p\x00\x00\x00\x004R\x10x\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xf2x\x00\x00\x00\x006\xfc\xf2" + + "p\x00\x00\x00\x008\x1b\x0e\xf8\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xa7\xe2x\x00\x00\x00\x00:\xbc\xb6p\x00\x00\x00\x00;\xda\xd2\xf8\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xb4\xf8\x00\x00\x00" + + "\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x96\xf8\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xb3x\x00\x00\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x95x\x00\x00\x00\x00D.\x95p\x00\x00\x00\x00ECw" + + "x\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Yx\x00\x00\x00\x00G\xf7\x93\xf0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x95$\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00\xa1\xb8\x01\t\x00\x00\x93\xa8\x00\x0f\x00\x00\x9a\xb0\x01\x15LMT\x00AEST\x00" + + "+1130\x00+1030\x00+11\x00\n<+1030>-10:30<+11>-11,M10.1.0,M4.1.0\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU\xbd\xca#\x7f\xad\x03\x00\x00\xad\x03\x00\x00\x15\x00\x1c\x00Australia/Broken_HillUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\x00\x00\x00\x05\x00\x00\x00\x13\xff\xff\xff\xffs\x16\x88d\xff\xff\xff\xff" + + "v\x04\xa5\xe0\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88" + + "\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00" + + "\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\x7f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b" + + "\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00" + + "\x168O\b\x00\x00\x00\x00\x17\f\x90\x88\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88" + + "\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00" + + "$\"\x84\b\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00%\xef\xf1\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xaf\xb5\b\x00\x00\x00\x00*\xe9z\b" + + "\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-x\xb3\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/X\x95\x88\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x00" + + "2r<\x88\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88" + + "\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00" + + "@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88" + + "\x00\x00\x00\x00G\xf7\xa9\b\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00\x84\x9c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\t\x00\x00\x93\xa8\x01\x0e\x00\x00\x85" + + "\x98\x00\tLMT\x00AEST\x00ACST\x00ACDT\x00\nACST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUϻ\xca\x1a2\x01\x00\x002\x01\x00\x00\x0e\x00\x1c\x00Australia/WestUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft\xa6\x16\xe4\xff\xff\xff\xff\x9cNޠ\xff\xff\xff" + + "\xff\x9c\xbcK \xff\xff\xff\xff\xcbT\xcf \xff\xff\xff\xff\xcbǁ\xa0\xff\xff\xff\xff̷r\xa0\xff\xff\xff\xffͧc\xa0\x00\x00\x00\x00\t\x0f\xfb\xa0\x00\x00\x00\x00\t\xb6\x18\xa0\x00\x00\x00\x00\x1a\x01b" + + "\xa0\x00\x00\x00\x00\x1a\xa7\x7f\xa0\x00\x00\x00\x00)%\\\xa0\x00\x00\x00\x00)\xaf\xca \x00\x00\x00\x00Eq\xbf \x00\x00\x00\x00F\x05g \x00\x00\x00\x00G#|\xa0\x00\x00\x00\x00G\ue0e0\x00\x00\x00" + + "\x00I\x03^\xa0\x00\x00\x00\x00I\xcee\xa0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00l\x9c\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\tLMT\x00AWDT\x00A" + + "WST\x00\nAWST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x12\x00\x1c\x00Australia/Tasmania" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x03\x00" + + "\x00\x00\x0e\xff\xff\xff\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a\x80\xff\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`C\x80\xff" + + "\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc" + + "\xb2~\x00\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00" + + "\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n" + + "\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00" + + "\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18" + + "\xe31\x00\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00" + + "\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00'" + + ")\xaf\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00" + + "\x00\x00\x00.\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005" + + "\x1d\x1e\x80\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00" + + "\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C" + + ">\xb2\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1." + + "0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00\x1c\x00Australia/HobartUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x03\x00\x00\x00\x0e" + + "\xff\xff\xff\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a\x80\xff\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`C\x80\xff\xff\xff\xff" + + "\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc\xb2~\x00" + + "\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00" + + "\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80" + + "\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00" + + "\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18\xe31\x00" + + "\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00" + + " Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00" + + "\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00" + + ".\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005\x1d\x1e\x80" + + "\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00\x00\x00\x00" + + "<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C>\xb2\x80" + + "\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\xff\xff\x8c\x94\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST8PDT" + - ",M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R?_p\x99\x0e\x05\x00\x00\x0e\x05\x00\x00\x0e\x00\x1c\x00Canada/Central" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00" + - "\x00\x00\x14\xff\xff\xff\xffd䰔\xff\xff\xff\xff\x9b\x01\xfb\xe0\xff\xff\xff\xff\x9búP\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\u00a0;\x80\xff\xff\xff\xff\xc3O\x84\xf0\xff" + - "\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xffӈh\x00\xff\xff\xff\xff\xd4S`\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd7" + - "5\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xdb\x00\a\x00\xff\xff\xff\xff\xdb\xc8\\\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff" + - "\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5" + - ")\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff" + - "\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf41b\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa" + - "\xf8g\x00\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xfe\x00\x00" + - "\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b π\x00\x00\x00\x00\t" + - "\x10\xc0\x80\x00\x00\x00\x00\n\x00\xb1\x80\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\x92\x00\x00" + - "\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17" + - ")\x1a\x00\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00" + - "\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%" + - "J\xae\x00\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\x95\x80\x00" + - "\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003" + - "GX\x00\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005':\x00\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xd9\x00\x00" + - "\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A" + - "\x84\x9b\x80\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M" + + "4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xbd\xca#\x7f\xad\x03\x00\x00\xad\x03\x00\x00\x14\x00\x1c\x00Australia/YancowinnaUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\x00\x00\x00\x05\x00\x00\x00" + + "\x13\xff\xff\xff\xffs\x16\x88d\xff\xff\xff\xffv\x04\xa5\xe0\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff" + + "\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?" + + "\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00" + + "\x00\r\x7f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm" + + "\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x17\f\x90\x88\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00" + + "\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2" + + "\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00%\xef\xf1\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00" + + "\x00)\xaf\xb5\b\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-x\xb3\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/X\x95\x88\x00\x00\x00\x000\x92Z" + + "\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00" + + "\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca" + + "\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00" + + "\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x00\x00\x84\x9c\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00" + + "~\x90\x00\t\x00\x00\x93\xa8\x01\x0e\x00\x00\x85\x98\x00\tLMT\x00AEST\x00ACST\x00ACDT\x00\nACST-9:30ACDT,M10.1.0,M4" + + ".1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa2ܺ\xca:\x01\x00\x00:\x01\x00\x00\x0f\x00\x1c\x00Australia/EuclaUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x03\x00\x00\x00\x10\xff\xff\xff\xfft" + + "\xa6\n\xb0\xff\xff\xff\xff\x9cN\xd4\x14\xff\xff\xff\xff\x9c\xbc@\x94\xff\xff\xff\xff\xcbTĔ\xff\xff\xff\xff\xcb\xc7w\x14\xff\xff\xff\xff̷h\x14\xff\xff\xff\xffͧY\x14\x00\x00\x00\x00\t\x0f\xf1\x14\x00" + + "\x00\x00\x00\t\xb6\x0e\x14\x00\x00\x00\x00\x1a\x01X\x14\x00\x00\x00\x00\x1a\xa7u\x14\x00\x00\x00\x00)%R\x14\x00\x00\x00\x00)\xaf\xbf\x94\x00\x00\x00\x00Eq\xb4\x94\x00\x00\x00\x00F\x05\\\x94\x00\x00\x00\x00G" + + "#r\x14\x00\x00\x00\x00G\xeey\x14\x00\x00\x00\x00I\x03T\x14\x00\x00\x00\x00I\xce[\x14\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00x\xd0\x00\x00\x00\x00\x89\x1c\x01\x04\x00\x00" + + "{\f\x00\nLMT\x00+0945\x00+0845\x00\n<+0845>-8:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00" + + "\r\x00\x1c\x00Australia/NSWUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x7f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80" + + "\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00" + + "\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00" + + "\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00" + + "\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80" + + "\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00" + + "\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00" + + "\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80\x00\x00\x00\x00" + + "0\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x006\xfd\x00\x80" + + "\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00\x00\x00\x00\x00" + + ">\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00EC~\x80" + + "\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a\xb0\x01\x04\x00" + + "\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\b" + + "v\vU?\x95\xbd\x12E\x01\x00\x00E\x01\x00\x00\x12\x00\x1c\x00Australia/LindemanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\xa2\xd4\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff" + + "\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80" + + "\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00" + + "*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8b\xac\x00\x00\x00\x00\x9a\xb0\x01" + + "\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU3\xba\xde\xd3!\x01\x00\x00!\x01\x00\x00\x12\x00\x1c\x00" + + "Australia/BrisbaneUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\x9f\b\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e" + + "\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00" + + "\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8fx\x00\x00" + + "\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00" + + "\x00\x10\x00\x1c\x00Australia/SydneyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x7f<\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff" + + "\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80" + + "\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00" + + "\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00" + + "\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17\f\x89\x80\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18ǁ\x80\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00" + + "\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00\x00\x00\x00\x1ey\x9c\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380" + + "\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00" + + ")\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x00\x00\x00\x00*\xe9s\x00\x00\x00\x00\x00+\x98ʀ\x00\x00\x00\x00,ҏ\x80\x00\x00\x00\x00-x\xac\x80\x00\x00\x00\x00.\xb2q\x80\x00\x00\x00\x00/X\x8e\x80" + + "\x00\x00\x00\x000\x92S\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002r5\x80\x00\x00\x00\x003=<\x80\x00\x00\x00\x004R\x17\x80\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x0061\xf9\x80\x00\x00\x00\x00" + + "6\xfd\x00\x80\x00\x00\x00\x008\x1b\x16\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xda\xda\x00\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\xba\xbc\x00" + + "\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?\x9a\x9e\x00\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A\x83\xba\x80\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00Cc\x9c\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00" + + "EC~\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G#`\x80\x00\x00\x00\x00G\xf7\xa2\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8d\xc4\x00\x00\x00\x00\x9a" + + "\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x12\x00\x1c\x00Australia/AdelaideUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x8b\x14\xff\xff\xff\xff{\x12\x03p" + + "\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xff" + + "χ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88" + + "\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00\x00\x00\x00\r\x7f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00" + + "\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x16禈" + + "\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00" + + "\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%I\xd4\b" + + "\x00\x00\x00\x00&\x02f\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00\x00\x00\x00)\xcbd\x88\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00" + + ",Җ\x88\x00\x00\x00\x00-\x8b(\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/tE\b\x00\x00\x00\x000\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003=C\x88" + + "\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00" + + ":\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88" + + "\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x81\xec\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT\x00\nA" + + "CST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00\x1c\x00" + + "Australia/CurrieUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xfft.\x00\xe4\xff\xff\xff\xff\x9b\xd5x\x80\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\x9d\xdaD\x80\xff\xff\xff\xff\x9e\x80a\x80\xff" + + "\xff\xff\xff\x9f\xba&\x80\xff\xff\xff\xff\xa0`C\x80\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xff\xcf" + + "\x87)\x80\xff\xff\xff\xff\xfb\u008d\x00\xff\xff\xff\xff\xfc\xb2~\x00\xff\xff\xff\xff\xfd\xc7Y\x00\xff\xff\xff\xff\xfev\xb0\x80\xff\xff\xff\xff\xff\xa7;\x00\x00\x00\x00\x00\x00V\x92\x80\x00\x00\x00\x00\x01\x87\x1d\x00\x00" + + "\x00\x00\x00\x02?\xaf\x00\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00\x05P\x1b\x80\x00\x00\x00\x00\x05\xf68\x80\x00\x00\x00\x00\a/\xfd\x80\x00\x00\x00\x00\a\xd6\x1a\x80\x00\x00\x00\x00\t" + + "\x0f߀\x00\x00\x00\x00\t\xb5\xfc\x80\x00\x00\x00\x00\n\xef\xc1\x80\x00\x00\x00\x00\v\x9f\x19\x00\x00\x00\x00\x00\f\xd8\xde\x00\x00\x00\x00\x00\r~\xfb\x00\x00\x00\x00\x00\x0e\xb8\xc0\x00\x00\x00\x00\x00\x0f^\xdd\x00\x00" + + "\x00\x00\x00\x10\x98\xa2\x00\x00\x00\x00\x00\x11>\xbf\x00\x00\x00\x00\x00\x12x\x84\x00\x00\x00\x00\x00\x13\x1e\xa1\x00\x00\x00\x00\x00\x14Xf\x00\x00\x00\x00\x00\x14\xfe\x83\x00\x00\x00\x00\x00\x168H\x00\x00\x00\x00\x00\x17" + + "\x03O\x00\x00\x00\x00\x00\x18!d\x80\x00\x00\x00\x00\x18\xe31\x00\x00\x00\x00\x00\x1a\x01F\x80\x00\x00\x00\x00\x1a\xa7c\x80\x00\x00\x00\x00\x1b\xe1(\x80\x00\x00\x00\x00\x1c\x87E\x80\x00\x00\x00\x00\x1d\xc1\n\x80\x00" + + "\x00\x00\x00\x1eg'\x80\x00\x00\x00\x00\x1f\x97\xb2\x00\x00\x00\x00\x00 Y~\x80\x00\x00\x00\x00!\x80\u0380\x00\x00\x00\x00\"B\x9b\x00\x00\x00\x00\x00#i\xeb\x00\x00\x00\x00\x00$\"}\x00\x00\x00\x00\x00%" + + "I\xcd\x00\x00\x00\x00\x00&\x02_\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xf4\xb6\x00\x00\x00\x00\x00(\xed\xe1\x80\x00\x00\x00\x00)Ԙ\x00\x00\x00\x00\x00*\xcdÀ\x00\x00\x00\x00+\xb4z\x00\x00" + + "\x00\x00\x00,\xad\xa5\x80\x00\x00\x00\x00-\x94\\\x00\x00\x00\x00\x00.\x8d\x87\x80\x00\x00\x00\x00/t>\x00\x00\x00\x00\x000mi\x80\x00\x00\x00\x001]Z\x80\x00\x00\x00\x002V\x86\x00\x00\x00\x00\x003" + + "=<\x80\x00\x00\x00\x0046h\x00\x00\x00\x00\x005\x1d\x1e\x80\x00\x00\x00\x006\x16J\x00\x00\x00\x00\x006\xfd\x00\x80\x00\x00\x00\x007\xf6,\x00\x00\x00\x00\x008\xdc\xe2\x80\x00\x00\x00\x009\xa7\xe9\x80\x00" + + "\x00\x00\x00:\xbcĀ\x00\x00\x00\x00;\xbf*\x80\x00\x00\x00\x00<\xa5\xe1\x00\x00\x00\x00\x00=\x9f\f\x80\x00\x00\x00\x00>\x85\xc3\x00\x00\x00\x00\x00?~\xee\x80\x00\x00\x00\x00@e\xa5\x00\x00\x00\x00\x00A" + + "^Ѐ\x00\x00\x00\x00BE\x87\x00\x00\x00\x00\x00C>\xb2\x80\x00\x00\x00\x00D.\xa3\x80\x00\x00\x00\x00E\x1e\x94\x80\x00\x00\x00\x00F\x05K\x00\x00\x00\x00\x00G\a\xb1\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\x8a\x1c\x00\x00\x00\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAE" + + "ST-10AEDT,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU3\xba\xde\xd3!\x01\x00\x00!\x01\x00\x00\x14\x00\x1c\x00Aus" + + "tralia/QueenslandUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffr\xed\x9f\b\xff\xff\xff\xff\x9cN\u0080\xff\xff\xff\xff\x9c\xbc/\x00\xff\xff\xff\xff\xcbT\xb3\x00\xff\xff\xff\xff\xcb\xc7e\x80" + + "\xff\xff\xff\xff̷V\x80\xff\xff\xff\xffͧG\x80\xff\xff\xff\xffΠs\x00\xff\xff\xff\xffχ)\x80\x00\x00\x00\x00\x03p9\x80\x00\x00\x00\x00\x04\r\x1c\x00\x00\x00\x00\x00%I\xcd\x00\x00\x00\x00\x00" + + "%\xef\xea\x00\x00\x00\x00\x00')\xaf\x00\x00\x00\x00\x00'\xcf\xcc\x00\x00\x00\x00\x00)\t\x91\x00\x00\x00\x00\x00)\xaf\xae\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x8fx\x00\x00\x00" + + "\x00\x9a\xb0\x01\x04\x00\x00\x8c\xa0\x00\tLMT\x00AEDT\x00AEST\x00\nAEST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUo3\xdaR\xb4\x02\x00\x00\xb4\x02\x00\x00" + + "\x13\x00\x1c\x00Australia/Lord_HoweUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x05\x00\x00\x00\x19\xff\xff\xff\xffs\x16w\xdc\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168@\xf8\x00\x00\x00\x00\x16\xe7\x8ah\x00\x00" + + "\x00\x00\x18!]x\x00\x00\x00\x00\x18\xc7lh\x00\x00\x00\x00\x1a\x01?x\x00\x00\x00\x00\x1a\xa7Nh\x00\x00\x00\x00\x1b\xe1!x\x00\x00\x00\x00\x1c\x870h\x00\x00\x00\x00\x1d\xc1\x03x\x00\x00\x00\x00\x1ey" + + "\x8ep\x00\x00\x00\x00\x1f\x97\xaa\xf8\x00\x00\x00\x00 Ypp\x00\x00\x00\x00!\x80\xc7x\x00\x00\x00\x00\"B\x8c\xf0\x00\x00\x00\x00#i\xe3\xf8\x00\x00\x00\x00$\"n\xf0\x00\x00\x00\x00%I\xc5\xf8\x00\x00" + + "\x00\x00%\xef\xdb\xf0\x00\x00\x00\x00')\xa7\xf8\x00\x00\x00\x00'Ͻ\xf0\x00\x00\x00\x00)\t\x89\xf8\x00\x00\x00\x00)\xaf\x9f\xf0\x00\x00\x00\x00*\xe9k\xf8\x00\x00\x00\x00+\x98\xbcp\x00\x00\x00\x00,\xd2" + + "\x88x\x00\x00\x00\x00-x\x9ep\x00\x00\x00\x00.\xb2jx\x00\x00\x00\x00/X\x80p\x00\x00\x00\x000\x92Lx\x00\x00\x00\x001]Lp\x00\x00\x00\x002r.x\x00\x00\x00\x003=.p\x00\x00" + + "\x00\x004R\x10x\x00\x00\x00\x005\x1d\x10p\x00\x00\x00\x0061\xf2x\x00\x00\x00\x006\xfc\xf2p\x00\x00\x00\x008\x1b\x0e\xf8\x00\x00\x00\x008\xdc\xd4p\x00\x00\x00\x009\xa7\xe2x\x00\x00\x00\x00:\xbc" + + "\xb6p\x00\x00\x00\x00;\xda\xd2\xf8\x00\x00\x00\x00<\xa5\xd2\xf0\x00\x00\x00\x00=\xba\xb4\xf8\x00\x00\x00\x00>\x85\xb4\xf0\x00\x00\x00\x00?\x9a\x96\xf8\x00\x00\x00\x00@e\x96\xf0\x00\x00\x00\x00A\x83\xb3x\x00\x00" + + "\x00\x00BEx\xf0\x00\x00\x00\x00Cc\x95x\x00\x00\x00\x00D.\x95p\x00\x00\x00\x00ECwx\x00\x00\x00\x00F\x05<\xf0\x00\x00\x00\x00G#Yx\x00\x00\x00\x00G\xf7\x93\xf0\x01\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x95$\x00\x00\x00\x00\x8c\xa0" + + "\x00\x04\x00\x00\xa1\xb8\x01\t\x00\x00\x93\xa8\x00\x0f\x00\x00\x9a\xb0\x01\x15LMT\x00AEST\x00+1130\x00+1030\x00+11\x00\n<+1030>-10:30<" + + "+11>-11,M10.1.0,M4.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x0f\x00\x1c\x00Austral" + + "ia/SouthUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00T\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x8b\x14\xff\xff\xff\xff{\x12\x03p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff" + + "\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff\xffχ0\x88\x00\x00\x00\x00\x03p@\x88\x00\x00\x00\x00\x04\r#\b\x00\x00\x00\x00\x05P\"\x88\x00\x00\x00\x00\x05" + + "\xf6?\x88\x00\x00\x00\x00\a0\x04\x88\x00\x00\x00\x00\a\xd6!\x88\x00\x00\x00\x00\t\x0f\xe6\x88\x00\x00\x00\x00\t\xb6\x03\x88\x00\x00\x00\x00\n\xefȈ\x00\x00\x00\x00\v\x9f \b\x00\x00\x00\x00\f\xd8\xe5\b\x00" + + "\x00\x00\x00\r\x7f\x02\b\x00\x00\x00\x00\x0e\xb8\xc7\b\x00\x00\x00\x00\x0f^\xe4\b\x00\x00\x00\x00\x10\x98\xa9\b\x00\x00\x00\x00\x11>\xc6\b\x00\x00\x00\x00\x12x\x8b\b\x00\x00\x00\x00\x13\x1e\xa8\b\x00\x00\x00\x00\x14" + + "Xm\b\x00\x00\x00\x00\x14\xfe\x8a\b\x00\x00\x00\x00\x168O\b\x00\x00\x00\x00\x16禈\x00\x00\x00\x00\x18!k\x88\x00\x00\x00\x00\x18Lj\x88\x00\x00\x00\x00\x1a\x01M\x88\x00\x00\x00\x00\x1a\xa7j\x88\x00" + + "\x00\x00\x00\x1b\xe1/\x88\x00\x00\x00\x00\x1c\x87L\x88\x00\x00\x00\x00\x1d\xc1\x11\x88\x00\x00\x00\x00\x1ey\xa3\x88\x00\x00\x00\x00\x1f\x97\xb9\b\x00\x00\x00\x00 Y\x85\x88\x00\x00\x00\x00!\x80Ո\x00\x00\x00\x00\"" + + "B\xa2\b\x00\x00\x00\x00#i\xf2\b\x00\x00\x00\x00$\"\x84\b\x00\x00\x00\x00%I\xd4\b\x00\x00\x00\x00&\x02f\b\x00\x00\x00\x00')\xb6\b\x00\x00\x00\x00'\xcf\xd3\b\x00\x00\x00\x00)\t\x98\b\x00" + + "\x00\x00\x00)\xcbd\x88\x00\x00\x00\x00*\xe9z\b\x00\x00\x00\x00+\x98ш\x00\x00\x00\x00,Җ\x88\x00\x00\x00\x00-\x8b(\x88\x00\x00\x00\x00.\xb2x\x88\x00\x00\x00\x00/tE\b\x00\x00\x00\x000" + + "\x92Z\x88\x00\x00\x00\x001]a\x88\x00\x00\x00\x002r<\x88\x00\x00\x00\x003=C\x88\x00\x00\x00\x004R\x1e\x88\x00\x00\x00\x005\x1d%\x88\x00\x00\x00\x0062\x00\x88\x00\x00\x00\x006\xfd\a\x88\x00" + + "\x00\x00\x008\x1b\x1d\b\x00\x00\x00\x008\xdc\xe9\x88\x00\x00\x00\x009\xfa\xff\b\x00\x00\x00\x00:\xbcˈ\x00\x00\x00\x00;\xda\xe1\b\x00\x00\x00\x00<\xa5\xe8\b\x00\x00\x00\x00=\xba\xc3\b\x00\x00\x00\x00>" + + "\x85\xca\b\x00\x00\x00\x00?\x9a\xa5\b\x00\x00\x00\x00@e\xac\b\x00\x00\x00\x00A\x83\xc1\x88\x00\x00\x00\x00BE\x8e\b\x00\x00\x00\x00Cc\xa3\x88\x00\x00\x00\x00D.\xaa\x88\x00\x00\x00\x00EC\x85\x88\x00" + + "\x00\x00\x00F\x05R\b\x00\x00\x00\x00G#g\x88\x00\x00\x00\x00G\xf7\xa9\b\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00\x81\xec\x00\x00\x00\x00~\x90\x00\x04\x00" + + "\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT\x00\nACST-9:30ACDT,M10.1.0,M4.1.0/3\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\xc8R\x1a\x1b\xea\x00\x00\x00\xea\x00\x00\x00\x0f\x00\x1c\x00Australia/NorthUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x0e\xff\xff\xff\xffs\x16\x92X\xff\xff\xff\xff{\x12\x03" + + "p\xff\xff\xff\xff\x9cNɈ\xff\xff\xff\xff\x9c\xbc6\b\xff\xff\xff\xff\xcbT\xba\b\xff\xff\xff\xff\xcb\xc7l\x88\xff\xff\xff\xff̷]\x88\xff\xff\xff\xffͧN\x88\xff\xff\xff\xffΠz\b\xff\xff\xff" + + "\xffχ0\x88\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00\x00z\xa8\x00\x00\x00\x00~\x90\x00\x04\x00\x00\x93\xa8\x01\t\x00\x00\x85\x98\x00\x04LMT\x00ACST\x00ACDT\x00\nACST-9" + + ":30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Brazil/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb7-2f\xe4\x01\x00\x00\xe4\x01\x00\x00\x10\x00\x1c\x00Brazil/DeNoronhaUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x03\x00\x00\x00\f\xff\xff" + + "\xff\xff\x96\xaaed\xff\xff\xff\xff\xb8\x0f;\xd0\xff\xff\xff\xff\xb8\xfd2\x90\xff\xff\xff\xff\xb9\xf1& \xff\xff\xff\xff\xba\xdef\x10\xff\xff\xff\xff\xda8\xa0 \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdc\x19" + + "Ӡ\xff\xff\xff\xffܹK\x10\xff\xff\xff\xff\xdd\xfb\a \xff\xff\xff\xffޛ\xd0\x10\xff\xff\xff\xff\xdf\u074c \xff\xff\xff\xff\xe0T%\x10\xff\xff\xff\xff\xf4\x97\xf1\xa0\xff\xff\xff\xff\xf5\x05P\x10\xff\xff" + + "\xff\xff\xf6\xc0V \xff\xff\xff\xff\xf7\x0e\x10\x90\xff\xff\xff\xff\xf8Q\x1e \xff\xff\xff\xff\xf8Ƿ\x10\xff\xff\xff\xff\xfa\nĠ\xff\xff\xff\xff\xfa\xa8\xea\x90\xff\xff\xff\xff\xfb\xeb\xf8 \xff\xff\xff\xff\xfc\x8b" + + "o\x90\x00\x00\x00\x00\x1dɀ \x00\x00\x00\x00\x1exɐ\x00\x00\x00\x00\x1f\xa0'\xa0\x00\x00\x00\x00 3\xc1\x90\x00\x00\x00\x00!\x81[ \x00\x00\x00\x00\"\v\xba\x90\x00\x00\x00\x00#X\x02\xa0\x00\x00" + + "\x00\x00#\xe2b\x10\x00\x00\x00\x00%7\xe4\xa0\x00\x00\x00\x00%Թ\x10\x00\x00\x00\x007\xf6\xb8\xa0\x00\x00\x00\x008\xb8w\x10\x00\x00\x00\x009\xdf\xd5 \x00\x00\x00\x009\xe9\x01\x90\x00\x00\x00\x00;\xc8" + + "\xf1\xa0\x00\x00\x00\x002\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9d?\xdfڸ\x03\x00\x00\xb8\x03\x00\x00\v\x00\x1c\x00Bra" + + "zil/EastUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00[\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaar\xb4\xff\xff\xff\xff\xb8\x0fI\xe0\xff\xff\xff\xff\xb8\xfd@\xa0\xff\xff\xff\xff\xb9\xf140\xff\xff\xff\xff\xba\xdet \xff\xff\xff\xff\xda8\xae0\xff" + + "\xff\xff\xff\xda\xeb\xfa0\xff\xff\xff\xff\xdc\x19\xe1\xb0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xfb\x150\xff\xff\xff\xffޛ\xde \xff\xff\xff\xff\xdfݚ0\xff\xff\xff\xff\xe0T3 \xff\xff\xff\xff\xf4" + + "Z\t0\xff\xff\xff\xff\xf5\x05^ \xff\xff\xff\xff\xf6\xc0d0\xff\xff\xff\xff\xf7\x0e\x1e\xa0\xff\xff\xff\xff\xf8Q,0\xff\xff\xff\xff\xf8\xc7\xc5 \xff\xff\xff\xff\xfa\nҰ\xff\xff\xff\xff\xfa\xa8\xf8\xa0\xff" + + "\xff\xff\xff\xfb\xec\x060\xff\xff\xff\xff\xfc\x8b}\xa0\x00\x00\x00\x00\x1dɎ0\x00\x00\x00\x00\x1exנ\x00\x00\x00\x00\x1f\xa05\xb0\x00\x00\x00\x00 3Ϡ\x00\x00\x00\x00!\x81i0\x00\x00\x00\x00\"" + + "\vȠ\x00\x00\x00\x00#X\x10\xb0\x00\x00\x00\x00#\xe2p \x00\x00\x00\x00%7\xf2\xb0\x00\x00\x00\x00%\xd4\xc7 \x00\x00\x00\x00'!\x0f0\x00\x00\x00\x00'\xbd\xe3\xa0\x00\x00\x00\x00)\x00\xf10\x00" + + "\x00\x00\x00)\x94\x8b \x00\x00\x00\x00*\xea\r\xb0\x00\x00\x00\x00+k2\xa0\x00\x00\x00\x00,\xc0\xb50\x00\x00\x00\x00-f\xc4 \x00\x00\x00\x00.\xa0\x970\x00\x00\x00\x00/F\xa6 \x00\x00\x00\x000" + + "\x80y0\x00\x00\x00\x001\x1dM\xa0\x00\x00\x00\x002W \xb0\x00\x00\x00\x003\x06j \x00\x00\x00\x0048T0\x00\x00\x00\x004\xf8\xc1 \x00\x00\x00\x006 \x1f0\x00\x00\x00\x006\xcfh\xa0\x00" + + "\x00\x00\x007\xf6ư\x00\x00\x00\x008\xb8\x85 \x00\x00\x00\x009\xdf\xe30\x00\x00\x00\x00:\x8f,\xa0\x00\x00\x00\x00;\xc8\xff\xb0\x00\x00\x00\x00" + + "N\xf0\xa0\x00\x00\x00\x00?\x91\xfe0\x00\x00\x00\x00@.Ҡ\x00\x00\x00\x00A\x86\xf80\x00\x00\x00\x00B\x17\xef \x00\x00\x00\x00CQ\xc20\x00\x00\x00\x00C\xf7\xd1 \x00\x00\x00\x00EMS\xb0\x00" + + "\x00\x00\x00E\xe0\xed\xa0\x00\x00\x00\x00G\x11\x860\x00\x00\x00\x00G\xb7\x95 \x00\x00\x00\x00H\xfa\xa2\xb0\x00\x00\x00\x00I\x97w \x00\x00\x00\x00Jڄ\xb0\x00\x00\x00\x00K\x80\x93\xa0\x00\x00\x00\x00L" + + "\xbaf\xb0\x00\x00\x00\x00M`u\xa0\x00\x00\x00\x00N\x9aH\xb0\x00\x00\x00\x00OI\x92 \x00\x00\x00\x00P\x83e0\x00\x00\x00\x00Q 9\xa0\x00\x00\x00\x00RcG0\x00\x00\x00\x00S\x00\x1b\xa0\x00" + + "\x00\x00\x00TC)0\x00\x00\x00\x00T\xe98 \x00\x00\x00\x00V#\v0\x00\x00\x00\x00V\xc9\x1a \x00\x00\x00\x00X\x02\xed0\x00\x00\x00\x00X\xa8\xfc \x00\x00\x00\x00Y\xe2\xcf0\x00\x00\x00\x00Z" + + "\x88\xde \x00\x00\x00\x00[\xde`\xb0\x00\x00\x00\x00\\h\xc0 \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xff\xd4L\x00\x00\xff\xff\xe3\xe0" + + "\x01\x04\xff\xff\xd5\xd0\x00\bLMT\x00-02\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUa\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\v\x00\x1c\x00Br" + + "azil/WestUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x1f\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x7fD\xff\xff\xff\xff\xb8\x0fW\xf0\xff\xff\xff\xff\xb8\xfdN\xb0\xff\xff\xff\xff\xb9\xf1B@\xff\xff\xff\xff\xbaނ0\xff\xff\xff\xff\xda8\xbc@" + + "\xff\xff\xff\xff\xda\xec\b@\xff\xff\xff\xff\xdc\x19\xef\xc0\xff\xff\xff\xffܹg0\xff\xff\xff\xff\xdd\xfb#@\xff\xff\xff\xffޛ\xec0\xff\xff\xff\xff\xdfݨ@\xff\xff\xff\xff\xe0TA0\xff\xff\xff\xff" + + "\xf4\x98\r\xc0\xff\xff\xff\xff\xf5\x05l0\xff\xff\xff\xff\xf6\xc0r@\xff\xff\xff\xff\xf7\x0e,\xb0\xff\xff\xff\xff\xf8Q:@\xff\xff\xff\xff\xf8\xc7\xd30\xff\xff\xff\xff\xfa\n\xe0\xc0\xff\xff\xff\xff\xfa\xa9\x06\xb0" + + "\xff\xff\xff\xff\xfb\xec\x14@\xff\xff\xff\xff\xfc\x8b\x8b\xb0\x00\x00\x00\x00\x1dɜ@\x00\x00\x00\x00\x1ex\xe5\xb0\x00\x00\x00\x00\x1f\xa0C\xc0\x00\x00\x00\x00 3ݰ\x00\x00\x00\x00!\x81w@\x00\x00\x00\x00" + + "\"\vְ\x00\x00\x00\x00,\xc0\xc3@\x00\x00\x00\x00-f\xd20\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\xff\xffǼ\x00\x00\xff\xff\xd5" + + "\xd0\x01\x04\xff\xff\xc7\xc0\x00\bLMT\x00-03\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\v\x00\x1c\x00B" + + "razil/AcreUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x1f\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x96\xaa\x86\x90\xff\xff\xff\xff\xb8\x0ff\x00\xff\xff\xff\xff\xb8\xfd\\\xc0\xff\xff\xff\xff\xb9\xf1PP\xff\xff\xff\xff\xbaސ@\xff\xff\xff\xff\xda8\xca" + + "P\xff\xff\xff\xff\xda\xec\x16P\xff\xff\xff\xff\xdc\x19\xfd\xd0\xff\xff\xff\xffܹu@\xff\xff\xff\xff\xdd\xfb1P\xff\xff\xff\xffޛ\xfa@\xff\xff\xff\xff\xdfݶP\xff\xff\xff\xff\xe0TO@\xff\xff\xff" + + "\xff\xf4\x98\x1b\xd0\xff\xff\xff\xff\xf5\x05z@\xff\xff\xff\xff\xf6\xc0\x80P\xff\xff\xff\xff\xf7\x0e:\xc0\xff\xff\xff\xff\xf8QHP\xff\xff\xff\xff\xf8\xc7\xe1@\xff\xff\xff\xff\xfa\n\xee\xd0\xff\xff\xff\xff\xfa\xa9\x14" + + "\xc0\xff\xff\xff\xff\xfb\xec\"P\xff\xff\xff\xff\xfc\x8b\x99\xc0\x00\x00\x00\x00\x1dɪP\x00\x00\x00\x00\x1ex\xf3\xc0\x00\x00\x00\x00\x1f\xa0Q\xd0\x00\x00\x00\x00 3\xeb\xc0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00" + + "\x00\"\v\xe4\xc0\x00\x00\x00\x00H`\x7fP\x00\x00\x00\x00R\x7f\x04\xc0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\xff\xff\xc0p\x00\x00\xff\xff" + + "\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\x04LMT\x00-04\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\a\x00\x1c\x00Canada/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\u0096dK~" + + "\x02\x00\x00~\x02\x00\x00\x13\x00\x1c\x00Canada/SaskatchewanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x86\xfd\x93\x1c\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff" + + "\xff\xb5eO\xf0\xff\xff\xff\xff\xb60H\xe0\xff\xff\xff\xff\xb7E1\xf0\xff\xff\xff\xff\xb8\x10*\xe0\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xf0\f\xe0\xff\xff\xff\xff\xbb\x0e0p\xff\xff\xff\xff\xbb\xcf\xee" + + "\xe0\xff\xff\xff\xff\xbc\xee\x12p\xff\xff\xff\xff\xbd\xb9\v`\xff\xff\xff\xff\xc2r\b\xf0\xff\xff\xff\xff\xc3a\xeb\xe0\xff\xff\xff\xff\xc4Q\xea\xf0\xff\xff\xff\xff\xc58\x93`\xff\xff\xff\xff\xc61\xcc\xf0\xff\xff\xff" + + "\xff\xc7!\xaf\xe0\xff\xff\xff\xff\xc8\x1a\xe9p\xff\xff\xff\xff\xc9\n\xcc`\xff\xff\xff\xff\xc9\xfa\xcbp\xff\xff\xff\xff\xca\xea\xae`\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18" + + "\x00\xff\xff\xff\xff\xd3c\x8c\x10\xff\xff\xff\xff\xd4So\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\xff\xff\xff\xff\xd75\xc5\x10\xff\xff\xff\xff\xd8\x00\xbe\x00\xff\xff\xff\xff\xd9\x15\xa7\x10\xff\xff\xff" + + "\xff\xd9\xe0\xa0\x00\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x82\x00\xff\xff\xff\xff\xdcޥ\x90\xff\xff\xff\xffݩ\x9e\x80\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x80\x80\xff\xff\xff\xff\xe0\x9ei" + + "\x90\xff\xff\xff\xff\xe1ib\x80\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3ID\x80\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)&\x80\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12C\x00\xff\xff\xff" + + "\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf2%\x00\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xd6\xd3\x00\xff\xff\xff\xff\xed\xc6\xd2\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\xff\xff\x9d\xe4\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10" + + "\xff\xff\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CST\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUU9#\xbe2\x05\x00\x002\x05" + + "\x00\x00\x0e\x00\x1c\x00Canada/PacificUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^=v\xec\xff\xff\xff\xff\x9e\xb8\xbd\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2" + + "#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd3v\x0f \xff\xff\xff\xff\xd4A\b\x10\xff\xff\xff\xff\xd5U\xf1 \xff\xff\xff\xff\xd6 \xea\x10\xff\xff\xff\xff\xd75\xd3 \xff\xff\xff\xff\xd8\x00\xcc\x10\xff" + + "\xff\xff\xff\xd9\x15\xb5 \xff\xff\xff\xff\xd9\xe0\xae\x10\xff\xff\xff\xff\xda\xfeѠ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc\u07b3\xa0\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x95\xa0\xff\xff\xff\xff\xdf" + + "\x89\x8e\x90\xff\xff\xff\xff\xe0\x9ew\xa0\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~Y\xa0\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^;\xa0\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GX \xff" + + "\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8': \xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x1c \xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xfe \xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed" + + "\xc6\xe0 \xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xfc\xa0\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fޠ\xff\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xc0\xa0\xff\xff\xff\xff\xf4_\xa3\x90\xff" + + "\xff\xff\xff\xf5O\xa2\xa0\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/\x84\xa0\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0ff\xa0\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb" + + "\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00" + + "\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\b \xeb\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\n" + + "\x00͠\x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00" + + "\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18" + + "\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00" + + "\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&" + + "\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00" + + "\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004" + + "S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00" + + "\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00B" + + "O\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xa4\xec\x00\x00\xff\xff\xb9\xb0\x01\x04" + - "\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M11.1." + - "0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R):\x17-\x88\x06\x00\x00\x88\x06\x00\x00\x0f\x00\x1c\x00Canada/AtlanticUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa7\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80\xf1\xab\xa0\xff\xff" + - "\xff\xff\x9a\xe4\xde\xc0\xff\xff\xff\xff\x9b\xd6\x130\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xff\xa2\x9d\x17@\xff\xff\xff\xff\xa30\xb10\xff\xff\xff\xff\xa4zV@\xff\xff\xff\xff\xa5\x1b" + - "\x1f0\xff\xff\xff\xff\xa6S\xa0\xc0\xff\xff\xff\xff\xa6\xfcR\xb0\xff\xff\xff\xff\xa8<\xbd@\xff\xff\xff\xff\xa8\xdc4\xb0\xff\xff\xff\xff\xaa\x1c\x9f@\xff\xff\xff\xff\xaa\xcd:0\xff\xff\xff\xff\xab\xfc\x81@\xff\xff" + - "\xff\xff\xac\xbf\x910\xff\xff\xff\xff\xad\xee\xd8@\xff\xff\xff\xff\xae\x8c\xfe0\xff\xff\xff\xff\xaf\xbcE@\xff\xff\xff\xff\xb0\u007fU0\xff\xff\xff\xff\xb1\xae\x9c@\xff\xff\xff\xff\xb2Kp\xb0\xff\xff\xff\xff\xb3\x8e" + - "~@\xff\xff\xff\xff\xb4$\xbb0\xff\xff\xff\xff\xb5n`@\xff\xff\xff\xff\xb6\x15\xc0\xb0\xff\xff\xff\xff\xb7NB@\xff\xff\xff\xff\xb8\b\x17\xb0\xff\xff\xff\xff\xb9$\xe9\xc0\xff\xff\xff\xff\xb9\xe7\xf9\xb0\xff\xff" + - "\xff\xff\xbb\x04\xcb\xc0\xff\xff\xff\xff\xbb\xd1\x160\xff\xff\xff\xff\xbd\x00]@\xff\xff\xff\xff\xbd\x9d1\xb0\xff\xff\xff\xff\xbe\xf2\xb4@\xff\xff\xff\xff\xbf\x90\xda0\xff\xff\xff\xff\xc0\xd3\xe7\xc0\xff\xff\xff\xff\xc1^" + - "G0\xff\xff\xff\xff\u008d\x8e@\xff\xff\xff\xff\xc3P\x9e0\xff\xff\xff\xff\xc4mp@\xff\xff\xff\xff\xc50\x800\xff\xff\xff\xff\xc6r<@\xff\xff\xff\xff\xc7\x10b0\xff\xff\xff\xff\xc86n\xc0\xff\xff" + - "\xff\xff\xc8\xf9~\xb0\xff\xff\xff\xff\xca\x16P\xc0\xff\xff\xff\xff\xca\xd9`\xb0\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff\xff\xff\xff\xd4@" + - "\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0\xff\xff\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xdc\xde{`\xff\xff" + - "\xff\xffݩtP\xff\xff\xff\xff\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0\x9e?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff\xff\xff\xff\xe6G" + - "\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe8\xf1\xfa\xd0\xff\xff\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xd1\xdc\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff챾\xd0\xff\xff" + - "\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\u007f\x89P\xff\xff\xff\xff\xf3o\x88`\xff\xff\xff\xff\xf4_kP\xff\xff\xff\xff\xf5Oj`\xff\xff\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff\xff\xff\xff\xf8(" + - "i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe\xb8\x0e\xe0\xff\xff" + - "\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00\x00\x00\x00\x06@" + - "\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\fٔ\xd0\x00\x00" + - "\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00\x00\x00\x00\x14Y" + - "\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a\xf1\xfc`\x00\x00" + - "\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00\x00\x00\x00\"U" + - "\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)\nG\xd0\x00\x00" + - "\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00\x00\x00\x000\x93" + - "\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007\x06\xff\xe0\x00\x00" + - "\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f" + - "\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00ED5P\x00\x00" + - "\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x8c\x94\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80" + + "\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x0f\x00\x1c\x00Canada/MountainUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x88\xde\xce\xe0\xff\xff\xff\xff\x9e\xb8" + + "\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x98\x91\x90\xff\xff\xff\xff\xa0҅\x80\xff\xff\xff\xff\xa2\x8a\xe8\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4jʐ\xff\xff\xff\xff\xa55À\xff\xff" + + "\xff\xff\xa6S\xe7\x10\xff\xff\xff\xff\xa7\x15\xa5\x80\xff\xff\xff\xff\xa83\xc9\x10\xff\xff\xff\xff\xa8\xfe\xc2\x00\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd5U" + + "\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b ݐ\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00" + + "\x00\x00\n\x00\xbf\x90\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99" + + "\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00" + + "\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1" + + "\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00" + + "\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3" + + "p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00" + + "\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6" + + "\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00" + + "\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\xff\xff\x95\xa0\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMS" + + "T7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?_p\x99\x0e\x05\x00\x00\x0e\x05\x00\x00\x0e\x00\x1c\x00Canada/Ce" + + "ntralUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}" + + "\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffd䰔\xff\xff\xff\xff\x9b\x01\xfb\xe0\xff\xff\xff\xff\x9búP\xff\xff\xff\xff\x9e\xb8\xa1\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\u00a0;\x80\xff\xff\xff\xff" + + "\xc3O\x84\xf0\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xffӈh\x00\xff\xff\xff\xff\xd4S`\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0" + + "\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xdb\x00\a\x00\xff\xff\xff\xff\xdb\xc8\\\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xff" + + "ݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80" + + "\xff\xff\xff\xff\xe5)\x18p\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe7\x124\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff" + + "\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\ue47c\xf0\xff\xff\xff\xff\xf3o\xa4\x80\xff\xff\xff\xff\xf41b\xf0\xff\xff\xff\xff\xf9\x0fJ\x80\xff\xff\xff\xff\xfa\bv\x00" + + "\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00" + + "\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b π" + + "\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\n\x00\xb1\x80\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00" + + "\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169)\x00" + + "\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00" + + "\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xd3\x00" + + "\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00" + + "+\xbe\x95\x80\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\x16\x80" + + "\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005':\x00\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x00" + + "9\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@o\xc0\x80" + + "\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02" + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xc4`\x00\x00\xff" + - "\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3.2.0,M" + - "11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x0f\x00\x1c\x00Canada/MountainUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x88" + - "\xde\xce\xe0\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x98\x91\x90\xff\xff\xff\xff\xa0҅\x80\xff\xff\xff\xff\xa2\x8a\xe8\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4jʐ\xff" + - "\xff\xff\xff\xa55À\xff\xff\xff\xff\xa6S\xe7\x10\xff\xff\xff\xff\xa7\x15\xa5\x80\xff\xff\xff\xff\xa83\xc9\x10\xff\xff\xff\xff\xa8\xfe\xc2\x00\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2" + - "a\x18\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff\xff\xd6 \xdc\x00\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\b ݐ\x00" + - "\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\n\x00\xbf\x90\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f" + - "\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00" + - "\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d" + - "\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00" + - "\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+" + - "\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00" + - "\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009" + - "\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00" + - "\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xa4\xec\x00\x00\xff" + + "\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10LMT\x00CDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M" + + "11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU):\x17-\x88\x06\x00\x00\x88\x06\x00\x00\x0f\x00\x1c\x00Canada/AtlanticUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa7\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\x80" + + "\xf1\xab\xa0\xff\xff\xff\xff\x9a\xe4\xde\xc0\xff\xff\xff\xff\x9b\xd6\x130\xff\xff\xff\xff\x9e\xb8\x85`\xff\xff\xff\xff\x9f\xba\xddP\xff\xff\xff\xff\xa2\x9d\x17@\xff\xff\xff\xff\xa30\xb10\xff\xff\xff\xff\xa4zV@\xff" + + "\xff\xff\xff\xa5\x1b\x1f0\xff\xff\xff\xff\xa6S\xa0\xc0\xff\xff\xff\xff\xa6\xfcR\xb0\xff\xff\xff\xff\xa8<\xbd@\xff\xff\xff\xff\xa8\xdc4\xb0\xff\xff\xff\xff\xaa\x1c\x9f@\xff\xff\xff\xff\xaa\xcd:0\xff\xff\xff\xff\xab" + + "\xfc\x81@\xff\xff\xff\xff\xac\xbf\x910\xff\xff\xff\xff\xad\xee\xd8@\xff\xff\xff\xff\xae\x8c\xfe0\xff\xff\xff\xff\xaf\xbcE@\xff\xff\xff\xff\xb0\x7fU0\xff\xff\xff\xff\xb1\xae\x9c@\xff\xff\xff\xff\xb2Kp\xb0\xff" + + "\xff\xff\xff\xb3\x8e~@\xff\xff\xff\xff\xb4$\xbb0\xff\xff\xff\xff\xb5n`@\xff\xff\xff\xff\xb6\x15\xc0\xb0\xff\xff\xff\xff\xb7NB@\xff\xff\xff\xff\xb8\b\x17\xb0\xff\xff\xff\xff\xb9$\xe9\xc0\xff\xff\xff\xff\xb9" + + "\xe7\xf9\xb0\xff\xff\xff\xff\xbb\x04\xcb\xc0\xff\xff\xff\xff\xbb\xd1\x160\xff\xff\xff\xff\xbd\x00]@\xff\xff\xff\xff\xbd\x9d1\xb0\xff\xff\xff\xff\xbe\xf2\xb4@\xff\xff\xff\xff\xbf\x90\xda0\xff\xff\xff\xff\xc0\xd3\xe7\xc0\xff" + + "\xff\xff\xff\xc1^G0\xff\xff\xff\xff\u008d\x8e@\xff\xff\xff\xff\xc3P\x9e0\xff\xff\xff\xff\xc4mp@\xff\xff\xff\xff\xc50\x800\xff\xff\xff\xff\xc6r<@\xff\xff\xff\xff\xc7\x10b0\xff\xff\xff\xff\xc8" + + "6n\xc0\xff\xff\xff\xff\xc8\xf9~\xb0\xff\xff\xff\xff\xca\x16P\xc0\xff\xff\xff\xff\xca\xd9`\xb0\xff\xff\xff\xffˈ\xe2`\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xed\xd0\xff\xff\xff\xff\xd3u\xd6\xe0\xff" + + "\xff\xff\xff\xd4@\xcf\xd0\xff\xff\xff\xff\xd5U\xb8\xe0\xff\xff\xff\xff\xd6 \xb1\xd0\xff\xff\xff\xff\xd75\x9a\xe0\xff\xff\xff\xff\xd8\x00\x93\xd0\xff\xff\xff\xff\xd9\x15|\xe0\xff\xff\xff\xff\xd9\xe0u\xd0\xff\xff\xff\xff\xdc" + + "\xde{`\xff\xff\xff\xffݩtP\xff\xff\xff\xff\u07be]`\xff\xff\xff\xff߉VP\xff\xff\xff\xff\xe0\x9e?`\xff\xff\xff\xff\xe1i8P\xff\xff\xff\xff\xe2~!`\xff\xff\xff\xff\xe3I\x1aP\xff" + + "\xff\xff\xff\xe6G\x1f\xe0\xff\xff\xff\xff\xe7\x12\x18\xd0\xff\xff\xff\xff\xe8'\x01\xe0\xff\xff\xff\xff\xe8\xf1\xfa\xd0\xff\xff\xff\xff\xea\x06\xe3\xe0\xff\xff\xff\xff\xea\xd1\xdc\xd0\xff\xff\xff\xff\xeb\xe6\xc5\xe0\xff\xff\xff\xff\xec" + + "\xb1\xbe\xd0\xff\xff\xff\xff\xf1\x8f\xa6`\xff\xff\xff\xff\xf2\x7f\x89P\xff\xff\xff\xff\xf3o\x88`\xff\xff\xff\xff\xf4_kP\xff\xff\xff\xff\xf5Oj`\xff\xff\xff\xff\xf6?MP\xff\xff\xff\xff\xf7/L`\xff" + + "\xff\xff\xff\xf8(i\xd0\xff\xff\xff\xff\xf9\x0f.`\xff\xff\xff\xff\xfa\bK\xd0\xff\xff\xff\xff\xfa\xf8J\xe0\xff\xff\xff\xff\xfb\xe8-\xd0\xff\xff\xff\xff\xfc\xd8,\xe0\xff\xff\xff\xff\xfd\xc8\x0f\xd0\xff\xff\xff\xff\xfe" + + "\xb8\x0e\xe0\xff\xff\xff\xff\xff\xa7\xf1\xd0\x00\x00\x00\x00\x00\x97\xf0\xe0\x00\x00\x00\x00\x01\x87\xd3\xd0\x00\x00\x00\x00\x02w\xd2\xe0\x00\x00\x00\x00\x03p\xf0P\x00\x00\x00\x00\x04`\xef`\x00\x00\x00\x00\x05P\xd2P\x00" + + "\x00\x00\x00\x06@\xd1`\x00\x00\x00\x00\a0\xb4P\x00\x00\x00\x00\b \xb3`\x00\x00\x00\x00\t\x10\x96P\x00\x00\x00\x00\n\x00\x95`\x00\x00\x00\x00\n\xf0xP\x00\x00\x00\x00\v\xe0w`\x00\x00\x00\x00\f" + + "ٔ\xd0\x00\x00\x00\x00\r\xc0Y`\x00\x00\x00\x00\x0e\xb9v\xd0\x00\x00\x00\x00\x0f\xa9u\xe0\x00\x00\x00\x00\x10\x99X\xd0\x00\x00\x00\x00\x11\x89W\xe0\x00\x00\x00\x00\x12y:\xd0\x00\x00\x00\x00\x13i9\xe0\x00" + + "\x00\x00\x00\x14Y\x1c\xd0\x00\x00\x00\x00\x15I\x1b\xe0\x00\x00\x00\x00\x168\xfe\xd0\x00\x00\x00\x00\x17(\xfd\xe0\x00\x00\x00\x00\x18\"\x1bP\x00\x00\x00\x00\x19\b\xdf\xe0\x00\x00\x00\x00\x1a\x01\xfdP\x00\x00\x00\x00\x1a" + + "\xf1\xfc`\x00\x00\x00\x00\x1b\xe1\xdfP\x00\x00\x00\x00\x1c\xd1\xde`\x00\x00\x00\x00\x1d\xc1\xc1P\x00\x00\x00\x00\x1e\xb1\xc0`\x00\x00\x00\x00\x1f\xa1\xa3P\x00\x00\x00\x00 u\xf2\xe0\x00\x00\x00\x00!\x81\x85P\x00" + + "\x00\x00\x00\"U\xd4\xe0\x00\x00\x00\x00#j\xa1\xd0\x00\x00\x00\x00$5\xb6\xe0\x00\x00\x00\x00%J\x83\xd0\x00\x00\x00\x00&\x15\x98\xe0\x00\x00\x00\x00'*e\xd0\x00\x00\x00\x00'\xfe\xb5`\x00\x00\x00\x00)" + + "\nG\xd0\x00\x00\x00\x00)ޗ`\x00\x00\x00\x00*\xea)\xd0\x00\x00\x00\x00+\xbey`\x00\x00\x00\x00,\xd3FP\x00\x00\x00\x00-\x9e[`\x00\x00\x00\x00.\xb3(P\x00\x00\x00\x00/~=`\x00" + + "\x00\x00\x000\x93\nP\x00\x00\x00\x001gY\xe0\x00\x00\x00\x002r\xecP\x00\x00\x00\x003G;\xe0\x00\x00\x00\x004R\xceP\x00\x00\x00\x005'\x1d\xe0\x00\x00\x00\x0062\xb0P\x00\x00\x00\x007" + + "\x06\xff\xe0\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xe1\xe0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:\xc6\xc3\xe0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xe0`\x00\x00\x00\x00=\xbbr\xd0\x00" + + "\x00\x00\x00>\x8f\xc2`\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@o\xa4`\x00\x00\x00\x00A\x84qP\x00\x00\x00\x00BO\x86`\x00\x00\x00\x00CdSP\x00\x00\x00\x00D/h`\x00\x00\x00\x00E" + + "D5P\x00\x00\x00\x00E\xf3\x9a\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + "\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x95\xa0\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MW" + - "T\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04\x00\x00\f\x00\x1c\x00" + - "Canada/YukonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8a\x9c\xff\xff\xff\xff\x9e\xb8˰\xff\xff\xff\xff\x9f\xbb#\xa0\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2Ҁ\xff\xff\xff\xff\xcb" + - "\x89(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xfb\x1d_\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00" + - "\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b" + - "\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00" + - "\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)" + - "\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00" + - "\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008" + - "\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00" + - "\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E" + - "\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00\x00L\xd6j\x90\x00" + - "\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90\x00\x00\x00\x00Q\x8f\x9ft\x00\x00\x00\x00?\x9b1\xe4\x00\x00\x00\x00@o\x81t\x00\x00\x00\x00A\x84Nd\x00\x00\x00\x00BOct\x00\x00\x00\x00Cd0" + - "d\x00\x00\x00\x00D/Et\x00\x00\x00\x00ED\x12d\x00\x00\x00\x00E\xf3w\xf4\x00\x00\x00\x00G-.\xe4\x00\x00\x00\x00G\xd3Y\xf4\x00\x00\x00\x00I\r\x10\xe4\x00\x00\x00\x00I\xb3;\xf4\x00\x00\x00" + - "\x00J\xec\xf2\xe4\x00\x00\x00\x00K\x9cXt\x00\x00\x00\x00L\xd6\x0fd\x00\x00\x00\x00M|:t\x00\x00\x00\x00N\xb6\rH\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x05\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\xff\xffΔ\x00\x00\xff\xffܤ\x01\x04\xff\xffΔ" + - "\x00\b\xff\xff\xdc\xd8\x01\x04\xff\xff\xce\xc8\x00\b\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10\xff\xff\xea\xe8\x01\x14LMT\x00NDT\x00NST\x00NPT\x00NWT\x00NDDT\x00\nNS" + - "T3:30NDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0e\x00\x1c\x00Canada" + - "/EasternUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff" + - "\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa" + - "\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff" + - "\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8" + - "\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff" + - "\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6" + - "M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff" + - "\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6 \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc" + - "\x13t`\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff" + - "\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea" + - "\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff" + - "\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\u007f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8" + - "(w\xe0\xff\xff\xff\xff\xf9\x0f" + - "\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00" + - "\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff" + + "\xc4`\x00\x00\xff\xff\xd5\xd0\x01\x04\xff\xff\xc7\xc0\x00\b\xff\xff\xd5\xd0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00ADT\x00AST\x00AWT\x00APT\x00\nAST4ADT,M3." + + "2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0e\x00\x1c\x00Canada/EasternUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xac\x00\x00\x00\x05\x00\x00\x00\x14\xff" + + "\xff\xff\xffr\xeex\xec\xff\xff\xff\xff\x9e\xb8\x93p\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x87.\xc8\xff\xff\xff\xff\xa1\x9a\xb1@\xff\xff\xff\xff\xa2\x94\x06\xf0\xff\xff\xff\xff\xa3U\xa9@\xff\xff\xff\xff\xa4" + + "\x86]\xf0\xff\xff\xff\xff\xa5(x`\xff\xff\xff\xff\xa6f?\xf0\xff\xff\xff\xff\xa7\fN\xe0\xff\xff\xff\xff\xa8F!\xf0\xff\xff\xff\xff\xa8\xec0\xe0\xff\xff\xff\xff\xaa\x1c\xc9p\xff\xff\xff\xff\xaa\xd5M`\xff" + + "\xff\xff\xff\xab\xfc\xabp\xff\xff\xff\xff\xac\xb5/`\xff\xff\xff\xff\xad܍p\xff\xff\xff\xff\xae\x95\x11`\xff\xff\xff\xff\xaf\xbcop\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2" + + "gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9%\x13\xf0\xff" + + "\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0" + + "\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff" + + "\xff\xff\xff\xc8-^p\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xaa\xd0\xff\xff\xff\xff\xd6" + + " \xa3\xc0\xff\xff\xff\xff\xd75\x8c\xd0\xff\xff\xff\xff\xd8\x00\x85\xc0\xff\xff\xff\xff\xd9\x15n\xd0\xff\xff\xff\xff\xda3v@\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdc\x13t`\xff\xff\xff\xff\xdcމp\xff" + + "\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4" + + "^\x11p\xff\xff\xff\xff\xe5)\n`\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe7\x12&\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff" + + "\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2" + + "\x7f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00" + + "\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff" + - "\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3" + - ".2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\u0096dK~\x02\x00\x00~\x02\x00\x00\x13\x00\x1c\x00Canada/Saskatchew" + - "anUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00" + - "\x06\x00\x00\x00\x18\xff\xff\xff\xff\x86\xfd\x93\x1c\xff\xff\xff\xff\x9e\xb8\xaf\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xb5eO\xf0\xff\xff\xff\xff\xb60H\xe0\xff\xff\xff\xff\xb7E1\xf0\xff\xff\xff\xff\xb8\x10*" + - "\xe0\xff\xff\xff\xff\xb9%\x13\xf0\xff\xff\xff\xff\xb9\xf0\f\xe0\xff\xff\xff\xff\xbb\x0e0p\xff\xff\xff\xff\xbb\xcf\xee\xe0\xff\xff\xff\xff\xbc\xee\x12p\xff\xff\xff\xff\xbd\xb9\v`\xff\xff\xff\xff\xc2r\b\xf0\xff\xff\xff" + - "\xff\xc3a\xeb\xe0\xff\xff\xff\xff\xc4Q\xea\xf0\xff\xff\xff\xff\xc58\x93`\xff\xff\xff\xff\xc61\xcc\xf0\xff\xff\xff\xff\xc7!\xaf\xe0\xff\xff\xff\xff\xc8\x1a\xe9p\xff\xff\xff\xff\xc9\n\xcc`\xff\xff\xff\xff\xc9\xfa\xcb" + - "p\xff\xff\xff\xff\xca\xea\xae`\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xd3c\x8c\x10\xff\xff\xff\xff\xd4So\x00\xff\xff\xff\xff\xd5U\xe3\x10\xff\xff\xff" + - "\xff\xd6 \xdc\x00\xff\xff\xff\xff\xd75\xc5\x10\xff\xff\xff\xff\xd8\x00\xbe\x00\xff\xff\xff\xff\xd9\x15\xa7\x10\xff\xff\xff\xff\xd9\xe0\xa0\x00\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x82\x00\xff\xff\xff\xff\xdcޥ" + - "\x90\xff\xff\xff\xffݩ\x9e\x80\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x80\x80\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff\xe1ib\x80\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3ID\x80\xff\xff\xff" + - "\xff\xe4^-\x90\xff\xff\xff\xff\xe5)&\x80\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12C\x00\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf2%\x00\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xd6\xd3" + - "\x00\xff\xff\xff\xff\xed\xc6\xd2\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x05\xff\xff\x9d\xe4\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10\xff\xff\xab\xa0\x00\x14LMT\x00MDT\x00MST\x00MWT\x00MPT\x00CS" + - "T\x00\nCST6\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe6\x9aM\xbem\x02\x00\x00m\x02\x00\x00\x03\x00\x1c\x00CETUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x02\x00\x00\x00\t\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0" + - "\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff" + - "\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90" + - "\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00" + - "\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10" + - "\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#O@\x00\x00\x00\x00\x06\x00\r" + - "\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00" + - "\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99" + - "\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00" + - "\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb" + - "0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00" + - "\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87" + - "@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00" + - "\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae" + - "0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00" + - "\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:" + - "@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00" + - "\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g" + - "\xb0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\xff\xff\x99x\x00\x00\xff\xff\x99x\x00\x04\xff\xff\xab\xa0\x01" + - "\b\xff\xff\x9d\x90\x00\f\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\x10LMT\x00EMT\x00-06\x00-07\x00-05\x00\n<-06>6<-05>,M9.1.6/2" + - "2,M4.1.6/22\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R[Sp\x90\x02\x05\x00\x00\x02\x05\x00\x00\x11\x00\x1c\x00Chile/ContinentalU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x00\x00\x00\x06\x00\x00" + - "\x00\x14\xff\xff\xff\xffi\x87\x1d\xc6\xff\xff\xff\xff\x8f0GF\xff\xff\xff\xff\x9b\\\xe5P\xff\xff\xff\xff\x9f|\xe2\xc6\xff\xff\xff\xff\xa1\x00q\xc0\xff\xff\xff\xff\xb0^w\xc6\xff\xff\xff\xff\xb1w=@\xff\xff" + - "\xff\xff\xb2A\x00\xd0\xff\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb59\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd" + - "\\\xc0\xff\xff\xff\xff\xb9\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff\xff\xff\xff\xd3\u070f\xc0\xff\xff\xff\xff\xd4\x1bɰ\xff\xff\xff\xff\xd53U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff" + - "\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 " + - "+\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00" + - "\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f" + - "\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00" + - "\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f" + - "\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00" + - "\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97" + - "j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00" + - "\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a" + - "\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00" + - "\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1" + - "X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00" + - "\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7" + - "\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x01\x02\x01\x03\x01\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x05\x03\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\xff\xff\xbd\xba\x00\x00\xff\xff\xbd\xba\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01\f\xff\xff\xd5\xd0\x01\x10LMT\x00" + - "SMT\x00-05\x00-04\x00-03\x00\n<-04>4<-03>,M9.1.6/24,M4.1.6/24\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R<\x8b\x99\x1e\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00CST6CDTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdb" + - "p\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff" + - "\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xee" + - "p\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00" + - "\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV" + - "\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00" + - "\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1" + - "p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00" + - "\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00.\xb3Dp\x00\x00\x00\x00/~Y" + - "\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00\x00\x00\x0062\xccp\x00\x00\x00" + - "\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e" + - "\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00" + - "\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00" + - "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x01\x00\xff\xff\xb9\xb0\x01\b\xff" + - "\xff\xb9\xb0\x01\fCDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\a" + - "\x1c\x9e\x9a]\x04\x00\x00]\x04\x00\x00\x04\x00\x1c\x00CubaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00j\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87(\xb8\xff\xff\xff\xff\xacb\u0080\xff\xff\xff\xff\xb1ӔP\xff\xff\xff\xff\xb2t]@\xff\xff\xff\xff\xc8[" + - "f\xd0\xff\xff\xff\xff\xc8\xd3Q@\xff\xff\xff\xff\xca;H\xd0\xff\xff\xff\xffʼm\xc0\xff\xff\xff\xff\xcc$eP\xff\xff\xff\xff̜O\xc0\xff\xff\xff\xff\xd1\xc4\vP\xff\xff\xff\xff\xd2;\xf5\xc0\xff\xff" + - "\xff\xffӣ\xedP\xff\xff\xff\xff\xd4\x1b\xd7\xc0\xff\xff\xff\xff\xf7`\x05\xd0\xff\xff\xff\xff\xf7\xff}@\xff\xff\xff\xff\xf9=D\xd0\xff\xff\xff\xff\xf9\xe3S\xc0\xff\xff\xff\xff\xfa\xdb;\xd0\xff\xff\xff\xff\xfb\xa7" + - "\x86@\xff\xff\xff\xff\xfcũ\xd0\xff\xff\xff\xff\xfd\x87h@\xff\xff\xff\xff\xfe\xb8\x00\xd0\xff\xff\xff\xff\xff\xa7\xe3\xc0\x00\x00\x00\x00\x00\x97\xe2\xd0\x00\x00\x00\x00\x01\x87\xc5\xc0\x00\x00\x00\x00\x02w\xc4\xd0\x00\x00" + - "\x00\x00\x03p\xe2@\x00\x00\x00\x00\x04`\xe1P\x00\x00\x00\x00\x055\x14\xc0\x00\x00\x00\x00\x06@\xc3P\x00\x00\x00\x00\a\x16H@\x00\x00\x00\x00\b \xa5P\x00\x00\x00\x00\b\xf7{\xc0\x00\x00\x00\x00\n\x00" + - "\x87P\x00\x00\x00\x00\n\xf0j@\x00\x00\x00\x00\v\xe0iP\x00\x00\x00\x00\fن\xc0\x00\x00\x00\x00\r\xc0KP\x00\x00\x00\x00\x0e\xb9h\xc0\x00\x00\x00\x00\x0f\xb2\xa2P\x00\x00\x00\x00\x10}\x9b@\x00\x00" + - "\x00\x00\x11Q\xea\xd0\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x131\xcc\xd0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15[\x82\xd0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x17;d\xd0\x00\x00\x00\x00\x18\x06" + - "]\xc0\x00\x00\x00\x00\x19\x1bF\xd0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xfb(\xd0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\xdb\n\xd0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ezSP\x00\x00" + - "\x00\x00\x1f\x8f @\x00\x00\x00\x00 Z5P\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"CQ\xd0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$#3\xd0\x00\x00\x00\x00%.\xc6@\x00\x00\x00\x00&\x15" + - "\x8a\xd0\x00\x00\x00\x00'\x17\xe2\xc0\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00(\xf7\xd2\xd0\x00\x00\x00\x00)މP\x00\x00\x00\x00*״\xd0\x00\x00\x00\x00+\xbekP\x00\x00\x00\x00,\xb7\x96\xd0\x00\x00" + - "\x00\x00-\x9eMP\x00\x00\x00\x00.\x97x\xd0\x00\x00\x00\x00/~/P\x00\x00\x00\x000wZ\xd0\x00\x00\x00\x001gK\xd0\x00\x00\x00\x002W<\xd0\x00\x00\x00\x003G-\xd0\x00\x00\x00\x004@" + - "YP\x00\x00\x00\x005\x1d\xd5P\x00\x00\x00\x0062\xb0P\x00\x00\x00\x006\xfd\xb7P\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xae\xd0\x00\x00\x00\x00:Ƶ\xd0\x00\x00" + - "\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xd2P\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xb4P\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@f[\xd0\x00\x00\x00\x00ED5P\x00\x00\x00\x00E\xf3" + - "\x8c\xd0\x00\x00\x00\x00G$\x17P\x00\x00\x00\x00GܩP\x00\x00\x00\x00I\x03\xf9P\x00\x00\x00\x00I\xb3P\xd0\x00\x00\x00\x00J\xe3\xdbP\x00\x00\x00\x00K\x9cmP\x00\x00\x00\x00L\xcc\xf7\xd0\x00\x00" + - "\x00\x00M\x85\x89\xd0\x00\x00\x00\x00N\xbfN\xd0\x00\x00\x00\x00Ow\xe0\xd0\x00\x00\x00\x00P\x95\xf6P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb2\xc8\x00\x00\xff\xff\xb2\xc0\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00HMT\x00CDT\x00CST\x00\nCST" + - "5CDT,M3.2.0/0,M11.1.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R`l\x8d~\xf1\x01\x00\x00\xf1\x01\x00\x00\x03\x00\x1c\x00EETUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x02\x00\x00\x00\t" + - "\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00" + - "\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90" + - "\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00" + - "\"LT\x10\x00\x00\x00\x00#\xa8^`\x00\x00\x00\x00?sWP\x00\x00\x00\x00@\x91z\xe0\x00\x00\x00\x00A\\s\xd0\x00\x00\x00\x00Bq\\\xe0" + - "\x00\x00\x00\x00C\xe0\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F1 \xe0\x00\x00\x00\x00F\xe0jP\x00\x00\x00\x00H\x11\x02\xe0\x00\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00" + - "I\xf0\xe4\xe0\x00\x00\x00\x00J\x8d\xb9P\x00\x00\x00\x00K\xda\x01`\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00L\x89X\xe0\x00\x00\x00\x00L\xa4\xfaP\x00\x00\x00\x00Su8\xe0\x00\x00\x00\x00S\xac\x89\xd0" + - "\x00\x00\x00\x00Sڼ`\x00\x00\x00\x00T$\x82P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x1dU\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST\x00EET\x00\nEET-2" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9a\v\xf9/\xd8\x05\x00\x00\xd8\x05\x00\x00\x04\x00\x1c\x00EireUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x91\x00\x00\x00\b\x00\x00\x00\x14\xff\xff\xff\xffW\xd1\n\xdc\xff\xff\xff\xff\x9b&\xb3\x91\xff\xff\xff\xff\x9b\xd6" + - "\v\x11\xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff" + - "\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00" + - "Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff" + - "\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f" + - "\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff" + - "\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:" + - "\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd7,( \xff\xff\xff\xff\xd8.\x8e \xff\xff" + - "\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮" + - "\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff" + - "\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3" + - "\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\u007f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff" + - "\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8" + - "\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00" + - "\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0" + - "\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00" + - "\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac" + - "\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00" + - "\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9" + - "\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x01\x02" + - "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06" + - "\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\xff\xff\xfa$\x00\x00\xff\xff\xfa\x0f\x00\x04\x00\x00\b\x1f\x01\b\x00\x00\x0e\x10\x01\f\x00\x00\x00\x00\x00\x10\x00\x00\x0e\x10\x01\b\x00" + - "\x00\x00\x00\x01\x10\x00\x00\x0e\x10\x00\bLMT\x00DMT\x00IST\x00BST\x00GMT\x00\nIST-1GMT0,M10.5.0,M3.5.0/1\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RtX\xbe\xe4o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00ESTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xb9\xb0\x00\x00EST\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xe7/\xebT\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00EST5EDTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6\x1ep\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x86\x00p\xff\xff\xff\xff\xa1" + - "\x9a\xcd`\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xfa\xf8X\xf0\xff\xff\xff\xff\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff" + - "\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05" + - "P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00" + - "\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13" + - "iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00" + - "\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!" + - "\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00" + - "\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/" + - "~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00" + - "\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=" + - "\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00" + - "\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00" + - "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\xb9\xb0\x00\x04\xff\xff\xc7\xc0\x01\x00\xff\xff\xc7\xc0\x01" + - "\b\xff\xff\xc7\xc0\x01\fEDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x1c\x00Etc/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00Etc/GMT+0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xb5\x94\x00\x00\xff\xff\xc7\xc0\x01\x04\xff" + + "\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU~\xb2\x0e\x19V\a\x00\x00V\a\x00\x00\x13\x00\x1c\x00Canada/NewfoundlandUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbb\x00\x00\x00\b\x00\x00\x00\x19\xff\xff\xff\xff^=4" + + "\xec\xff\xff\xff\xff\x9c\xcfb\f\xff\xff\xff\xff\x9d\xa4\xe6\xfc\xff\xff\xff\xff\x9e\xb8~\x8c\xff\xff\xff\xff\x9f\xba\xd6|\xff\xff\xff\xff\xa0\xb6\x88\xdc\xff\xff\xff\xff\xa18\xffL\xff\xff\xff\xff\xa2\x95\x19\\\xff\xff\xff" + + "\xff\xa3\x84\xfcL\xff\xff\xff\xff\xa4t\xfb\\\xff\xff\xff\xff\xa5d\xdeL\xff\xff\xff\xff\xa6^\x17\xdc\xff\xff\xff\xff\xa7D\xc0L\xff\xff\xff\xff\xa8=\xf9\xdc\xff\xff\xff\xff\xa9$\xa2L\xff\xff\xff\xff\xaa\x1d\xdb" + + "\xdc\xff\xff\xff\xff\xab\x04\x84L\xff\xff\xff\xff\xab\xfd\xbd\xdc\xff\xff\xff\xff\xac\xe4fL\xff\xff\xff\xff\xadݟ\xdc\xff\xff\xff\xff\xae͂\xcc\xff\xff\xff\xff\xaf\xbd\x81\xdc\xff\xff\xff\xff\xb0\xadd\xcc\xff\xff\xff" + + "\xff\xb1\xa6\x9e\\\xff\xff\xff\xff\xb2\x8dF\xcc\xff\xff\xff\xff\xb3\x86\x80\\\xff\xff\xff\xff\xb4m(\xcc\xff\xff\xff\xff\xb5fb\\\xff\xff\xff\xff\xb6M\n\xcc\xff\xff\xff\xff\xb7FD\\\xff\xff\xff\xff\xb8,\xec" + + "\xcc\xff\xff\xff\xff\xb9&&\\\xff\xff\xff\xff\xba\x16\tL\xff\xff\xff\xff\xbb\x0fB\xdc\xff\xff\xff\xff\xbb\xf5\xebL\xff\xff\xff\xff\xbc\xef$\xdc\xff\xff\xff\xff\xbd\xd5\xcdL\xff\xff\xff\xff\xbe\x9eMl\xff\xff\xff" + + "\xff\xbe\xcf\x06\xa8\xff\xff\xff\xff\xbf\xb5\xaf\x18\xff\xff\xff\xff\xc0\xb818\xff\xff\xff\xff\xc1y\xef\xa8\xff\xff\xff\xff\u0098\x138\xff\xff\xff\xff\xc3YѨ\xff\xff\xff\xff\xc4w\xf58\xff\xff\xff\xff\xc59\xb3" + + "\xa8\xff\xff\xff\xff\xc6a\x11\xb8\xff\xff\xff\xff\xc7\x19\x95\xa8\xff\xff\xff\xff\xc8@\xf3\xb8\xff\xff\xff\xff\xc9\x02\xb2(\xff\xff\xff\xff\xca ո\xff\xff\xff\xff\xca\xe2\x94(\xff\xff\xff\xff\xcc\x00\xb7\xb8\xff\xff\xff" + + "\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xe6\xc8\xff\xff\xff\xffӈD\xd8\xff\xff\xff\xff\xd4J\x03H\xff\xff\xff\xff\xd5h&\xd8\xff\xff\xff\xff\xd6)\xe5H\xff\xff\xff\xff\xd7H\b\xd8\xff\xff\xff\xff\xd8\t\xc7" + + "H\xff\xff\xff\xff\xd9'\xea\xd8\xff\xff\xff\xff\xd9\xe9\xa9H\xff\xff\xff\xff\xdb\x11\aX\xff\xff\xff\xff\xdb\xd2\xc5\xc8\xff\xff\xff\xff\xdc\xdetX\xff\xff\xff\xffݩmH\xff\xff\xff\xff\u07beVX\xff\xff\xff" + + "\xff߉OH\xff\xff\xff\xff\xe0\x9e8X\xff\xff\xff\xff\xe1i1H\xff\xff\xff\xff\xe2~\x1aX\xff\xff\xff\xff\xe3I\x13H\xff\xff\xff\xff\xe4]\xfcX\xff\xff\xff\xff\xe5(\xf5H\xff\xff\xff\xff\xe6G\x18" + + "\xd8\xff\xff\xff\xff\xe7\x12\x11\xc8\xff\xff\xff\xff\xe8&\xfa\xd8\xff\xff\xff\xff\xe8\xf1\xf3\xc8\xff\xff\xff\xff\xea\x06\xdc\xd8\xff\xff\xff\xff\xea\xd1\xd5\xc8\xff\xff\xff\xff\xeb\xe6\xbe\xd8\xff\xff\xff\xff챷\xc8\xff\xff\xff" + + "\xff\xedƠ\xd8\xff\xff\xff\xff\ueffeH\xff\xff\xff\xffﯽX\xff\xff\xff\xff\xf0\x9f\xa0H\xff\xff\xff\xff\xf1\x8f\x9fX\xff\xff\xff\xff\xf2\x7f\x82H\xff\xff\xff\xff\xf3o\x81X\xff\xff\xff\xff\xf4_d" + + "H\xff\xff\xff\xff\xf5OcX\xff\xff\xff\xff\xf6?FH\xff\xff\xff\xff\xf7/EX\xff\xff\xff\xff\xf8(b\xc8\xff\xff\xff\xff\xf9\x0f'X\xff\xff\xff\xff\xfa\bD\xc8\xff\xff\xff\xff\xfa\xf8C\xd8\xff\xff\xff" + + "\xff\xfb\xe8&\xc8\xff\xff\xff\xff\xfc\xd8%\xd8\xff\xff\xff\xff\xfd\xc8\b\xc8\xff\xff\xff\xff\xfe\xb8\a\xd8\xff\xff\xff\xff\xff\xa7\xea\xc8\x00\x00\x00\x00\x00\x97\xe9\xd8\x00\x00\x00\x00\x01\x87\xcc\xc8\x00\x00\x00\x00\x02w\xcb" + + "\xd8\x00\x00\x00\x00\x03p\xe9H\x00\x00\x00\x00\x04`\xe8X\x00\x00\x00\x00\x05P\xcbH\x00\x00\x00\x00\x06@\xcaX\x00\x00\x00\x00\a0\xadH\x00\x00\x00\x00\b \xacX\x00\x00\x00\x00\t\x10\x8fH\x00\x00\x00" + + "\x00\n\x00\x8eX\x00\x00\x00\x00\n\xf0qH\x00\x00\x00\x00\v\xe0pX\x00\x00\x00\x00\fٍ\xc8\x00\x00\x00\x00\r\xc0RX\x00\x00\x00\x00\x0e\xb9o\xc8\x00\x00\x00\x00\x0f\xa9n\xd8\x00\x00\x00\x00\x10\x99Q" + + "\xc8\x00\x00\x00\x00\x11\x89P\xd8\x00\x00\x00\x00\x12y3\xc8\x00\x00\x00\x00\x13i2\xd8\x00\x00\x00\x00\x14Y\x15\xc8\x00\x00\x00\x00\x15I\x14\xd8\x00\x00\x00\x00\x168\xf7\xc8\x00\x00\x00\x00\x17(\xf6\xd8\x00\x00\x00" + + "\x00\x18\"\x14H\x00\x00\x00\x00\x19\b\xd8\xd8\x00\x00\x00\x00\x1a\x01\xf6H\x00\x00\x00\x00\x1a\xf1\xf5X\x00\x00\x00\x00\x1b\xe1\xd8H\x00\x00\x00\x00\x1c\xd1\xd7X\x00\x00\x00\x00\x1d\xc1\xbaH\x00\x00\x00\x00\x1e\xb1\xb9" + + "X\x00\x00\x00\x00\x1f\xa1\x9cH\x00\x00\x00\x00 u\xcf\xf4\x00\x00\x00\x00!\x81bd\x00\x00\x00\x00\"U\xb1\xf4\x00\x00\x00\x00#jp\xd4\x00\x00\x00\x00$5\x93\xf4\x00\x00\x00\x00%J`\xe4\x00\x00\x00" + + "\x00&\x15u\xf4\x00\x00\x00\x00'*B\xe4\x00\x00\x00\x00'\xfe\x92t\x00\x00\x00\x00)\n$\xe4\x00\x00\x00\x00)\xdett\x00\x00\x00\x00*\xea\x06\xe4\x00\x00\x00\x00+\xbeVt\x00\x00\x00\x00,\xd3#" + + "d\x00\x00\x00\x00-\x9e8t\x00\x00\x00\x00.\xb3\x05d\x00\x00\x00\x00/~\x1at\x00\x00\x00\x000\x92\xe7d\x00\x00\x00\x001g6\xf4\x00\x00\x00\x002r\xc9d\x00\x00\x00\x003G\x18\xf4\x00\x00\x00" + + "\x004R\xabd\x00\x00\x00\x005&\xfa\xf4\x00\x00\x00\x0062\x8dd\x00\x00\x00\x007\x06\xdc\xf4\x00\x00\x00\x008\x1b\xa9\xe4\x00\x00\x00\x008\xe6\xbe\xf4\x00\x00\x00\x009\xfb\x8b\xe4\x00\x00\x00\x00:Ơ" + + "\xf4\x00\x00\x00\x00;\xdbm\xe4\x00\x00\x00\x00<\xaf\xbdt\x00\x00\x00\x00=\xbbO\xe4\x00\x00\x00\x00>\x8f\x9ft\x00\x00\x00\x00?\x9b1\xe4\x00\x00\x00\x00@o\x81t\x00\x00\x00\x00A\x84Nd\x00\x00\x00" + + "\x00BOct\x00\x00\x00\x00Cd0d\x00\x00\x00\x00D/Et\x00\x00\x00\x00ED\x12d\x00\x00\x00\x00E\xf3w\xf4\x00\x00\x00\x00G-.\xe4\x00\x00\x00\x00G\xd3Y\xf4\x00\x00\x00\x00I\r\x10" + + "\xe4\x00\x00\x00\x00I\xb3;\xf4\x00\x00\x00\x00J\xec\xf2\xe4\x00\x00\x00\x00K\x9cXt\x00\x00\x00\x00L\xd6\x0fd\x00\x00\x00\x00M|:t\x00\x00\x00\x00N\xb6\rH\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x06\x05\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\xff\xffΔ" + + "\x00\x00\xff\xffܤ\x01\x04\xff\xffΔ\x00\b\xff\xff\xdc\xd8\x01\x04\xff\xff\xce\xc8\x00\b\xff\xff\xdc\xd8\x01\f\xff\xff\xdc\xd8\x01\x10\xff\xff\xea\xe8\x01\x14LMT\x00NDT\x00NST\x00NPT\x00" + + "NWT\x00NDDT\x00\nNST3:30NDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04" + + "\x00\x00\f\x00\x1c\x00Canada/YukonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\t\x00\x00\x00%\xff\xff\xff\xff}\x86\x8a\x9c\xff\xff\xff\xff\x9e\xb8˰\xff\xff\xff\xff\x9f\xbb#\xa0\xff\xff\xff\xff\xa0\xd0\f\xb0\xff\xff\xff\xff\xa1\xa2\xd2" + + "\x80\xff\xff\xff\xffˉ(\xb0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a4 \xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xfb\x1d_\x10\x00\x00\x00\x00\x13ir \x00\x00\x00" + + "\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24" + + "\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00" + + "\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80" + + "\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00" + + "\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8" + + " \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00" + + "\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm" + + "\x90\x00\x00\x00\x00E\xf3\xd3 \x00\x00\x00\x00G-\x8a\x10\x00\x00\x00\x00Gӵ \x00\x00\x00\x00I\rl\x10\x00\x00\x00\x00I\xb3\x97 \x00\x00\x00\x00J\xedN\x10\x00\x00\x00\x00K\x9c\xb3\xa0\x00\x00\x00" + + "\x00L\xd6j\x90\x00\x00\x00\x00M|\x95\xa0\x00\x00\x00\x00N\xb6L\x90\x00\x00\x00\x00O\\w\xa0\x00\x00\x00\x00P\x96.\x90\x00\x00\x00\x00QO@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00" + + "\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@" + + "\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00" + + "\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0" + + "\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \x7f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00" + + "$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0" + + "\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x00" + + "2`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0" + + "\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00" + + "@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@" + + "\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00" + + "NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60" + + "\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x00\x00\x00\x00]t|\xc0\x00\x00\x00\x00" + + "^\x89I\xb0\x00\x00\x00\x00_T^\xc0\x00\x00\x00\x00`i+\xb0\x00\x00\x00\x00a4@\xc0\x00\x00\x00\x00bI\r\xb0\x00\x00\x00\x00c\x1d]@\x00\x00\x00\x00d(\xef\xb0\x01\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\xff\xff\x99x\x00\x00\xff\xff\x99x\x00\x04\xff\xff\xab\xa0\x01\b" + + "\xff\xff\x9d\x90\x00\f\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\x10LMT\x00EMT\x00-06\x00-07\x00-05\x00\n<-06>6<-05>,M9.1.6/22" + + ",M4.1.6/22\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU]\"_WJ\x05\x00\x00J\x05\x00\x00\x11\x00\x1c\x00Chile/ContinentalUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x82\x00\x00\x00\x06\x00\x00\x00" + + "\x14\xff\xff\xff\xffi\x87\x1d\xc5\xff\xff\xff\xff\x8f0GE\xff\xff\xff\xff\x9b\\\xe5P\xff\xff\xff\xff\x9f|\xe2\xc5\xff\xff\xff\xff\xa1\x00q\xc0\xff\xff\xff\xff\xb0^w\xc5\xff\xff\xff\xff\xb1w=@\xff\xff\xff" + + "\xff\xb2A\x00\xd0\xff\xff\xff\xff\xb3Xp\xc0\xff\xff\xff\xff\xb4\"4P\xff\xff\xff\xff\xb59\xa4@\xff\xff\xff\xff\xb6\x03g\xd0\xff\xff\xff\xff\xb7\x1a\xd7\xc0\xff\xff\xff\xff\xb7\xe4\x9bP\xff\xff\xff\xff\xb8\xfd\\" + + "\xc0\xff\xff\xff\xff\xb9\xc7 P\xff\xff\xff\xff\xcc\x1cn@\xff\xff\xff\xff\xccl\xe7\xd0\xff\xff\xff\xff\xd3\u070f\xc0\xff\xff\xff\xff\xd4\x17\xd50\xff\xff\xff\xff\xd53U\xc0\xff\xff\xff\xff\xd5v\x92@\xff\xff\xff" + + "\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +" + + "\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00" + + "\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7" + + "\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00" + + "\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \x7f\x03" + + "0\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00" + + "\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j" + + "\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00" + + "\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc" + + "0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00" + + "\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X" + + "@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00" + + "\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa" + + "0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x00\x00\x00\x00]t|\xc0\x00\x00\x00\x00^\x89I\xb0\x00\x00\x00\x00_T^\xc0\x00\x00\x00\x00`i+\xb0\x00\x00\x00\x00a4@\xc0\x00\x00\x00" + + "\x00bI\r\xb0\x00\x00\x00\x00c\x1d]@\x00\x00\x00\x00d(\xef\xb0\x01\x02\x01\x03\x01\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x03\x02\x03\x05\x04\x02\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\xff\xff\xbd\xbb\x00\x00\xff\xff\xbd\xbb\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x00\f\xff\xff\xc7\xc0\x01" + + "\f\xff\xff\xd5\xd0\x01\x10LMT\x00SMT\x00-05\x00-04\x00-03\x00\n<-04>4<-03>,M9.1.6/24,M4.1.6/24\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU<\x8b\x99\x1e\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00CST6CDTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff" + + "\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00" + + "\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00" + + "\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p" + + "\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00" + + "\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00" + + "\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00" + + " v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0" + + "\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00)\u07b3\x80\x00\x00\x00\x00*\xeaE\xf0\x00\x00\x00\x00+\xbe\x95\x80\x00\x00\x00\x00,\xd3bp\x00\x00\x00\x00-\x9ew\x80\x00\x00\x00\x00" + + ".\xb3Dp\x00\x00\x00\x00/~Y\x80\x00\x00\x00\x000\x93&p\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00" + + "\x00\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xc6\xe0\x00\x00\x00\x00\x00;۬\xf0\x00\x00\x00\x00" + + "<\xaf\xfc\x80\x00\x00\x00\x00=\xbb\x8e\xf0\x00\x00\x00\x00>\x8fހ\x00\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop" + + "\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01" + + "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\xab\xa0\x00\x04\xff\xff" + + "\xb9\xb0\x01\x00\xff\xff\xb9\xb0\x01\b\xff\xff\xb9\xb0\x01\fCDT\x00CST\x00CWT\x00CPT\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vU\a\x1c\x9e\x9a]\x04\x00\x00]\x04\x00\x00\x04\x00\x1c\x00CubaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00Etc/GMT-0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xd4X\x9b\xf3q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+5UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xb9\xb0\x00\x00-05\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xf7\x1ac\xc3r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-1UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x0e\x10\x00\x00+01\x00\n<+01>-1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R5\xb8\xe8\x86q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+1UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xf1\xf0\x00\x00-01\x00\n<-01>1\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\"\xf8\x8f/q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+8UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x8f\x80\x00\x00-08\x00\n<-08>8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xf7\x19s\x81s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-12UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xa8\xc0\x00\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/UCTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00j\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87(\xb8\xff\xff\xff\xff\xacb\u0080\xff\xff\xff\xff\xb1ӔP\xff\xff\xff" + + "\xff\xb2t]@\xff\xff\xff\xff\xc8[f\xd0\xff\xff\xff\xff\xc8\xd3Q@\xff\xff\xff\xff\xca;H\xd0\xff\xff\xff\xffʼm\xc0\xff\xff\xff\xff\xcc$eP\xff\xff\xff\xff̜O\xc0\xff\xff\xff\xff\xd1\xc4\v" + + "P\xff\xff\xff\xff\xd2;\xf5\xc0\xff\xff\xff\xffӣ\xedP\xff\xff\xff\xff\xd4\x1b\xd7\xc0\xff\xff\xff\xff\xf7`\x05\xd0\xff\xff\xff\xff\xf7\xff}@\xff\xff\xff\xff\xf9=D\xd0\xff\xff\xff\xff\xf9\xe3S\xc0\xff\xff\xff" + + "\xff\xfa\xdb;\xd0\xff\xff\xff\xff\xfb\xa7\x86@\xff\xff\xff\xff\xfcũ\xd0\xff\xff\xff\xff\xfd\x87h@\xff\xff\xff\xff\xfe\xb8\x00\xd0\xff\xff\xff\xff\xff\xa7\xe3\xc0\x00\x00\x00\x00\x00\x97\xe2\xd0\x00\x00\x00\x00\x01\x87\xc5" + + "\xc0\x00\x00\x00\x00\x02w\xc4\xd0\x00\x00\x00\x00\x03p\xe2@\x00\x00\x00\x00\x04`\xe1P\x00\x00\x00\x00\x055\x14\xc0\x00\x00\x00\x00\x06@\xc3P\x00\x00\x00\x00\a\x16H@\x00\x00\x00\x00\b \xa5P\x00\x00\x00" + + "\x00\b\xf7{\xc0\x00\x00\x00\x00\n\x00\x87P\x00\x00\x00\x00\n\xf0j@\x00\x00\x00\x00\v\xe0iP\x00\x00\x00\x00\fن\xc0\x00\x00\x00\x00\r\xc0KP\x00\x00\x00\x00\x0e\xb9h\xc0\x00\x00\x00\x00\x0f\xb2\xa2" + + "P\x00\x00\x00\x00\x10}\x9b@\x00\x00\x00\x00\x11Q\xea\xd0\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x131\xcc\xd0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15[\x82\xd0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00" + + "\x00\x17;d\xd0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x19\x1bF\xd0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xfb(\xd0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\xdb\n\xd0\x00\x00\x00\x00\x1d\xaf>" + + "@\x00\x00\x00\x00\x1ezSP\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 Z5P\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"CQ\xd0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$#3\xd0\x00\x00\x00" + + "\x00%.\xc6@\x00\x00\x00\x00&\x15\x8a\xd0\x00\x00\x00\x00'\x17\xe2\xc0\x00\x00\x00\x00'\xfe\xa7P\x00\x00\x00\x00(\xf7\xd2\xd0\x00\x00\x00\x00)މP\x00\x00\x00\x00*״\xd0\x00\x00\x00\x00+\xbek" + + "P\x00\x00\x00\x00,\xb7\x96\xd0\x00\x00\x00\x00-\x9eMP\x00\x00\x00\x00.\x97x\xd0\x00\x00\x00\x00/~/P\x00\x00\x00\x000wZ\xd0\x00\x00\x00\x001gK\xd0\x00\x00\x00\x002W<\xd0\x00\x00\x00" + + "\x003G-\xd0\x00\x00\x00\x004@YP\x00\x00\x00\x005\x1d\xd5P\x00\x00\x00\x0062\xb0P\x00\x00\x00\x006\xfd\xb7P\x00\x00\x00\x008\x1b\xcc\xd0\x00\x00\x00\x008\xe6\xd3\xd0\x00\x00\x00\x009\xfb\xae" + + "\xd0\x00\x00\x00\x00:Ƶ\xd0\x00\x00\x00\x00;ې\xd0\x00\x00\x00\x00<\xaf\xd2P\x00\x00\x00\x00=\xbbr\xd0\x00\x00\x00\x00>\x8f\xb4P\x00\x00\x00\x00?\x9bT\xd0\x00\x00\x00\x00@f[\xd0\x00\x00\x00" + + "\x00ED5P\x00\x00\x00\x00E\xf3\x8c\xd0\x00\x00\x00\x00G$\x17P\x00\x00\x00\x00GܩP\x00\x00\x00\x00I\x03\xf9P\x00\x00\x00\x00I\xb3P\xd0\x00\x00\x00\x00J\xe3\xdbP\x00\x00\x00\x00K\x9cm" + + "P\x00\x00\x00\x00L\xcc\xf7\xd0\x00\x00\x00\x00M\x85\x89\xd0\x00\x00\x00\x00N\xbfN\xd0\x00\x00\x00\x00Ow\xe0\xd0\x00\x00\x00\x00P\x95\xf6P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xb2\xc8\x00\x00\xff\xff\xb2\xc0\x00\x04\xff\xff\xc7\xc0\x01\b\xff\xff\xb9\xb0\x00\fLMT\x00HMT\x00C" + + "DT\x00CST\x00\nCST5CDT,M3.2.0/0,M11.1.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU`l\x8d~\xf1\x01\x00\x00\xf1\x01\x00" + + "\x00\x03\x00\x1c\x00EETUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00'\x00\x00\x00\x02\x00\x00\x00\t\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00" + + "\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19" + + "Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00" + + "\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xb2\xab\xd1Is\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-11UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x9a\xb0\x00\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xd0\xfaFDq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+4UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xc7\xc0\x00\x00-04\x00\n<-04>4\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\x9c\xfcm\x99r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-3UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00*0\x00\x00+03\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00" + - "\x00\xf1c9R!\xd6~wr\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-5UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00FP\x00\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9Re\xcb\xe9Qq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+3UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xd5\xd0\x00\x00-03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xd9|\xbd7s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-10UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x8c\xa0\x00\x00+10\x00\n<+10>-10\nPK\x03\x04\n" + - "\x00\x00\x00\x00\x00\xf1c9R\xe5\xf38cr\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+12UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffW@\x00\x00-12\x00\n<-12>12\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\xfc\x19@\xb9r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-9UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00~\x90\x00\x00+09\x00\n<+09>-9\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x90`N\xe8s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-13UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xb6\xd0\x00\x00+13\x00\n<+13>-13" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\b\x00\x1c\x00Etc/GMT0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8e\x1569r\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+10UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffs`\x00\x00-10\x00\n<-10>10\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rj\xd5d\xb0r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-6UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00T`\x00\x00+06\x00\n<+06>-6" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/UTCUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff}\xbdM\xab\xff\xff\xff\xffȓ\xb4\xe0\xff\xff\xff\xff\xc8\xfa{\xd0\xff\xff\xff\xff\xc9" + + "\xfc\xef\xe0\xff\xff\xff\xff\xca\xc7\xe8\xd0\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xcc\xdf)\xd0\xff\xff\xff\xffͬ\xe1\xe0\xff\xff\xff\xff\xce\xc6\xf4\xd0\xff\xff\xff\xffϏf\xe0\xff\xff\xff\xffЩy\xd0\xff" + + "\xff\xff\xffф`\xe0\xff\xff\xff\xffҊ\xadP\xff\xff\xff\xff\xe86c`\xff\xff\xff\xff\xe8\xf4-P\xff\xff\xff\xff\xea\v\xb9`\xff\xff\xff\xff\xea\xd5`\xd0\xff\xff\xff\xff\xeb\xec\xfa\xf0\xff\xff\xff\xff\xec" + + "\xb5m\x00\xff\xff\xff\xff\xed\xcf\x7f\xf0\xff\xff\xff\xff\xee\x97\xf2\x00\xff\xff\xff\xffﰳp\xff\xff\xff\xff\xf0y%\x80\xff\xff\xff\xff\xf1\x91\xe6\xf0\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3s\x1ap\xff" + + "\xff\xff\xff\xf4;\x8c\x80\xff\xff\xff\xff\xf5U\x9fp\xff\xff\xff\xff\xf6\x1e\x11\x80\xff\xff\xff\xff\xf76\xd2\xf0\xff\xff\xff\xff\xf7\xffE\x00\xff\xff\xff\xff\xf9\x18\x06p\xff\xff\xff\xff\xf9\xe1\xca\x00\xff\xff\xff\xff\xfa" + + "\xf99\xf0\xff\xff\xff\xff\xfb\xc2\xfd\x80\xff\xff\xff\xff\xfc۾\xf0\xff\xff\xff\xff\xfd\xa5\x82\x80\xff\xff\xff\xff\xfe\xbc\xf2p\xff\xff\xff\xff\xff\x86\xb6\x00\x00\x00\x00\x00\x00\x9e%\xf0\x00\x00\x00\x00\x01g\xe9\x80\x00" + + "\x00\x00\x00\x02\x7fYp\x00\x00\x00\x00\x03I\x1d\x00\x00\x00\x00\x00\x04a\xdep\x00\x00\x00\x00\x05+\xa2\x00\x00\x00\x00\x00\x06C\x11\xf0\x00\x00\x00\x00\a\fՀ\x00\x00\x00\x00\b$Ep\x00\x00\x00\x00\b" + + "\xee\t\x00\x00\x00\x00\x00\n\x05x\xf0\x00\x00\x00\x00\n\xcf<\x80\x00\x00\x00\x00\v\xe7\xfd\xf0\x00\x00\x00\x00\f\xb1\xc1\x80\x00\x00\x00\x00\r\xc91p\x00\x00\x00\x00\x0e\x92\xf5\x00\x00\x00\x00\x00\x0f\xaad\xf0\x00" + + "\x00\x00\x00\x10t(\x80\x00\x00\x00\x00\x11\x8b\x98p\x00\x00\x00\x00\x12U\\\x00\x00\x00\x00\x00\x13n\x1dp\x00\x00\x00\x00\x147\xe1\x00\x00\x00\x00\x00\x15OP\xf0\x00\x00\x00\x00\x16\x19\x14\x80\x00\x00\x00\x00\x17" + + "\xa0\x93\xf0\x00\x00\x00\x00\x17\xfaH\x00\x00\x00\x00\x00\x19p\xa3\xf0\x00\x00\x00\x00\x19\xdb{\x80\x00\x00\x00\x00\x1a\xf4<\xf0\x00\x00\x00\x00\x1b\xbe\x00\x80\x00\x00\x00\x00\x1c\xd5pp\x00\x00\x00\x00\x1d\x9f4\x00\x00" + + "\x00\x00\x00\x1e\xb6\xa3\xf0\x00\x00\x00\x00\x1f\x80g\x80\x00\x00\x00\x00 \x97\xd7p\x00\x00\x00\x00!a\x9b\x00\x00\x00\x00\x00\"z\\p\x00\x00\x00\x00#D \x00\x00\x00\x00\x00$b'p\x00\x00\x00\x00%" + + "%S\x80\x00\x00\x00\x00&<\xc3p\x00\x00\x00\x00'\x06\x87\x00\x00\x00\x00\x00(\x1d\xf6\xf0\x00\x00\x00\x00(纀\x00\x00\x00\x00*\x00{\xf0\x00\x00\x00\x00*\xca?\x80\x00\x00\x00\x00+\xe1\xafp\x00" + + "\x00\x00\x00,\xabs\x00\x00\x00\x00\x00-\xc2\xe2\xf0\x00\x00\x00\x00.\x8c\xa6\x80\x00\x00\x00\x00/\xa0\x13\xe0\x00\x00\x00\x000k\f\xd0\x00\x00\x00\x001\x7f\xf5\xe0\x00\x00\x00\x002J\xee\xd0\x00\x00\x00\x003" + + "_\xd7\xe0\x00\x00\x00\x004*\xd0\xd0\x00\x00\x00\x005?\xb9\xe0\x00\x00\x00\x006\n\xb2\xd0\x00\x00\x00\x007(\xd6`\x00\x00\x00\x007\xf3\xcfP\x00\x00\x00\x009\b\xb8`\x00\x00\x00\x009ӱP\x00" + + "\x00\x00\x00:\xe8\x9a`\x00\x00\x00\x00;\xb3\x93P\x00\x00\x00\x00<\xc8|`\x00\x00\x00\x00=\x93uP\x00\x00\x00\x00>\xa8^`\x00\x00\x00\x00?sWP\x00\x00\x00\x00@\x91z\xe0\x00\x00\x00\x00A" + + "\\s\xd0\x00\x00\x00\x00Bq\\\xe0\x00\x00\x00\x00C\xe0\x00\x00\x00\x00E\x12\xfdP\x00\x00\x00\x00F1 \xe0\x00\x00\x00\x00F\xe0jP\x00\x00\x00\x00H\x11\x02\xe0\x00" + + "\x00\x00\x00H\xb7\x11\xd0\x00\x00\x00\x00I\xf0\xe4\xe0\x00\x00\x00\x00J\x8d\xb9P\x00\x00\x00\x00K\xda\x01`\x00\x00\x00\x00La\xbd\xd0\x00\x00\x00\x00L\x89X\xe0\x00\x00\x00\x00L\xa4\xfaP\x00\x00\x00\x00S" + + "u8\xe0\x00\x00\x00\x00S\xac\x89\xd0\x00\x00\x00\x00Sڼ`\x00\x00\x00\x00T$\x82P\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x1dU\x00\x00\x00\x00*0\x01\x04\x00\x00\x1c \x00\tLMT\x00EEST" + + "\x00EET\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa1\xd6jL\xd8\x05\x00\x00\xd8\x05\x00\x00\x04\x00\x1c\x00EireUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x91\x00\x00\x00\b\x00\x00\x00\x14\xff\xff\xff\xffW\xd1\n\xf1\xff\xff\xff" + + "\xff\x9b&\xb3\x91\xff\xff\xff\xff\x9b\xd6\v\x11\xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc" + + "\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff" + + "\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y" + + "\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff" + + "\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3" + + " \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff" + + "\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd7,(" + + " \xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff" + + "\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f" + + "\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff" + + "\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf" + + " \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff" + + "\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t:" + + " \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00" + + "\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2" + + "\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00" + + "\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT" + + "\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00" + + "\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97" + + "\x90\x00\x00\x00\x001]\xd9\x10\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a" + + "\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\xff\xff\xfa\x0f\x00\x00\xff\xff\xfa\x0f\x00\x04\x00\x00\b\x1f\x01\b\x00\x00\x0e\x10\x01\f\x00\x00" + + "\x00\x00\x00\x10\x00\x00\x0e\x10\x01\b\x00\x00\x00\x00\x01\x10\x00\x00\x0e\x10\x00\bLMT\x00DMT\x00IST\x00BST\x00GMT\x00\nIST-1GMT0,M10.5.0" + + ",M3.5.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUtX\xbe\xe4o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00ESTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xb9\xb0\x00\x00EST\x00\nEST" + + "5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe7/\xebT\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00EST5EDTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6\x1ep\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff" + + "\xff\xff\xa0\x86\x00p\xff\xff\xff\xff\xa1\x9a\xcd`\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xfa\xf8X\xf0\xff\xff\xff\xff\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8" + + ":\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00\x02w\xe0\xf0\x00\x00\x00\x00\x03p\xfe`\x00\x00" + + "\x00\x00\x04`\xfdp\x00\x00\x00\x00\x05P\xe0`\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0" + + "\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00" + + "\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b" + + "\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00" + + "\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*" + + "s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00" + + "\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'" + + "+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00" + + "\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cd" + + "a`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01" + + "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\xb9\xb0\x00\x04" + + "\xff\xff\xc7\xc0\x01\x00\xff\xff\xc7\xc0\x01\b\xff\xff\xc7\xc0\x01\fEDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x1c\x00Etc/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUH\x9b\xd1\x04q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+6UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xab\xa0\x00\x00-06\x00\n<-06>6\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\b\x00\x1c\x00Etc/ZuluUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\x84+\x9a$q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+7UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x9d\x90\x00\x00-07\x00\n<-07>7\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RH\x9b\xd1\x04q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+6UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xab\xa0\x00\x00-06\x00\n<-06>6\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RJ0p-r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-7UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00bp\x00\x00+07\x00\n<+07>-7\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/GMTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R\xbc\x19y\x04r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-2UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x1c \x00\x00+02\x00\n<+02>-2\nPK\x03\x04\n" + - "\x00\x00\x00\x00\x00\xf1c9R,{\xdc;s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-14UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xc4\xe0\x00\x00+14\x00\n<+14>-14\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\r\x00\x1c\x00Etc/UniversalUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc5\x18\xb6\xfbr\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-8UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00p\x80\x00\x00+08\x00\n<+08>-8" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R)\xb9\xbe\x9dr\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+11UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffeP\x00\x00-11\x00\n<-11>" + - "11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\b\x00\x1c\x00Etc/ZuluUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk\x19-4" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x84\x19\xb3\tq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+9UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x81p\x00\x00-09\x00\n<-09>9" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Europe/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x95\u007fpp\xdc\x02\x00\x00\xdc\x02\x00\x00\r\x00\x1c\x00Europe/SamaraUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\xa1\x009\x80" + - "\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00" + - "\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0" + - "\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00" + - "(\xe5\x17\x80\x00\x00\x00\x00)\x00\xc7\x00\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xdd`\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xbf`\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\xa1`" + - "\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000d\x83`\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r\x89\xe0\x00\x00\x00\x003=\x90\xe0\x00\x00\x00\x004Rk\xe0\x00\x00\x00\x005\x1dr\xe0\x00\x00\x00\x00" + - "62M\xe0\x00\x00\x00\x006\xfdT\xe0\x00\x00\x00\x008\x1bj`\x00\x00\x00\x008\xdd6\xe0\x00\x00\x00\x009\xfbL`\x00\x00\x00\x00:\xbd\x18\xe0\x00\x00\x00\x00;\xdb.`\x00\x00\x00\x00<\xa65`" + - "\x00\x00\x00\x00=\xbb\x10`\x00\x00\x00\x00>\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x00\x00\x00\x00BE\xdb`\x00\x00\x00\x00Cc\xf0\xe0\x00\x00\x00\x00" + - "D%\xbd`\x00\x00\x00\x00EC\xd2\xe0\x00\x00\x00\x00F\x05\x9f`\x00\x00\x00\x00G#\xb4\xe0\x00\x00\x00\x00G\xee\xbb\xe0\x00\x00\x00\x00I\x03\x96\xe0\x00\x00\x00\x00IΝ\xe0\x00\x00\x00\x00J\xe3x\xe0" + - "\x00\x00\x00\x00K\xae\u007f\xe0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x02\x00\x00.\xf4\x00\x00\x00\x00*0\x00\x04\x00\x008@\x00\b\x00\x00FP\x01\f\x00\x008@\x01\b\x00\x00" + - "*0\x01\x04LMT\x00+03\x00+04\x00+05\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x0e\x00\x1c\x00E" + - "urope/BelfastUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff" + - "\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb " + - "\xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff" + - "\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0" + - "\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff" + - "\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 " + - "\xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff" + - "\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90" + - "\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff\xff\xff" + - "\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0" + - "\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff" + - "\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq " + - "\xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff" + - "\xf1aI\xa0\xff\xff\xff\xff\xf2\u007f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 " + - "\xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00" + - "\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N " + - "\x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00" + - "\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐" + - "\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00" + - " lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90" + - "\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00" + - ".\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT0B" + - "ST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Ro\xbc\x831O\x04\x00\x00O\x04\x00\x00\x0f\x00\x1c\x00Europe/Bru" + - "sselsUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f" + - "\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffV\xb6\xdf\xe6\xff\xff\xff\xffm\xe8\xc8\x00\xff\xff\xff\xff\x98DI\x80\xff\xff\xff\xff\x9b\f%p\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff" + - "\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\x9f\xce\xf80\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xbbp\xff\xff\xff\xff\xa2.\x12\xf0\xff\xff\xff\xff\xa3zL\xf0" + - "\xff\xff\xff\xff\xa45\x81\xf0\xff\xff\xff\xff\xa5^#p\xff\xff\xff\xff\xa6%5\xf0\xff\xff\xff\xff\xa7'\x9b\xf0\xff\xff\xff\xff\xa8*\x01\xf0\xff\xff\xff\xff\xa9\a}\xf0\xff\xff\xff\xff\xa9\xee4p\xff\xff\xff\xff" + - "\xaa\xe7_\xf0\xff\xff\xff\xff\xab\xd7P\xf0\xff\xff\xff\xff\xac\xc7A\xf0\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa7#\xf0\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x05\xf0\xff\xff\xff\xff\xb1\x89k\xf0" + - "\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff" + - "\xb8\xff\xe3\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\u058b \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xc8\xe2 \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\x9f\x89\xa0\xff\xff\xff\xff\xbf\x98\xb5 " + - "\xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2h\x88 \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4?/\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff" + - "\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xc8J\x19 \xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0n^\x90" + - "\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N@\x90\xff\xff\xff\xffӑ@\x10\xff\xff\xff\xff\xd4K#\x90\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00" + - "\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐" + - "\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00" + - "\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b\x00" + - "p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00" + - "\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo" + - "\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00[\xd4\xed\xf0\x00\x00\x00\x00_\xe7\xb2`\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + - "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x02\x01\x02\x01\x00\x00)\xa4\x00\x00\x00\x00*0\x00\x04\x00\x008@\x00\b\x00\x00FP\x01\f\x00\x008@\x01\b" + - "LMT\x00+03\x00+04\x00+05\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RO+j\x94\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x1c\x00Europ" + - "e/KaliningradUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\b\x00\x00\x00\"\xff\xff\xff\xffo\xa2[H\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff" + - "\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10" + - "\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1|w\xe0\xff\xff\xff\xffѕ\x84`\xff\xff\xff\xffҊ\xadP\xff\xff\xff\xff\xd3Y\xb6\xe0\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00" + - "\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0" + - "\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00" + - "%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd5\b\x80\x00\x00\x00\x00*\xc4\xf9\x80\x00\x00\x00\x00+\xb4\xea\x80" + - "\x00\x00\x00\x00,\xa4ۀ\x00\x00\x00\x00-\x94̀\x00\x00\x00\x00.\x84\xbd\x80\x00\x00\x00\x00/t\xae\x80\x00\x00\x00\x000d\x9f\x80\x00\x00\x00\x001]\xcb\x00\x00\x00\x00\x002r\xa6\x00\x00\x00\x00\x00" + - "3=\xad\x00\x00\x00\x00\x004R\x88\x00\x00\x00\x00\x005\x1d\x8f\x00\x00\x00\x00\x0062j\x00\x00\x00\x00\x006\xfdq\x00\x00\x00\x00\x008\x1b\x86\x80\x00\x00\x00\x008\xddS\x00\x00\x00\x00\x009\xfbh\x80" + - "\x00\x00\x00\x00:\xbd5\x00\x00\x00\x00\x00;\xdbJ\x80\x00\x00\x00\x00<\xa6Q\x80\x00\x00\x00\x00=\xbb,\x80\x00\x00\x00\x00>\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00" + - "A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%ـ\x00\x00\x00\x00EC\xef\x00\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00" + - "\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00\x00\x00K\xae\x9c\x00\x00\x00\x00\x00Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x00\x00\x00\x00TL+p\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03\x04\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x00\x00\x138\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x12\x00\x008@\x01\x16\x00\x00*0\x00\x1a\x00\x00" + - "*0\x00\x1eLMT\x00CEST\x00CET\x00EEST\x00EET\x00MSD\x00MSK\x00+03\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe1" + - "C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x1c\x00Europe/BelgradeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff^<\xf0H\xff\xff\xff\xff\xca\x025\xe0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff" + - "\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xffѡ\x8c\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ" + - "\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00" + - "\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#1\x90\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p\xff\xff\xff\xff\xd6)\x97\xf0\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff" + - "\xd8\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c\xd2p\xff\xff\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff\xfc\xfc\xb4p\xff\xff\xff\xff\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0" + - "\xff\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00\x00\x00\x02\x9cZp\x00\x00\x00\x00\x03Bwp\x00\x00\x00\x00\x04\x85v\xf0\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00" + - "\x06\x1a3p\x00\x00\x00\x00\a\n$p\x00\x00\x00\x00\b\x17\x16p\x00\x00\x00\x00\b\xda4p\x00\x00\x00\x00\t\xf7\x14\x90\x00\x00\x00\x00\n\xc2\r\x80\x00\x00\x00\x00\v\xd6\xf6\x90\x00\x00\x00\x00\f\xa1\xef\x80" + - "\x00\x00\x00\x00\r\xb6ؐ\x00\x00\x00\x00\x0e\x81р\x00\x00\x00\x00\x0f\x96\xba\x90\x00\x00\x00\x00\x10a\xb3\x80\x00\x00\x00\x00\x11v\x9c\x90\x00\x00\x00\x00\x12A\x95\x80\x00\x00\x00\x00\x13E[\x10\x00\x00\x00\x00" + - "\x14*\xb2\x00\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU\xa9{\xa2qq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+2UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xe3\xe0\x00\x00-02\x00\n<-02>2\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vUk\x19-4\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x84+\x9a$q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+7UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x9d\x90\x00\x00-07\x00\n<-07>7\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vUj\xd5d\xb0r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-6UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00T`\x00\x00+06\x00\n<+06>-6\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe5\xf38cr\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+12UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffW@\x00\x00-12\x00\n<-12>12" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd0\xfaFDq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+4UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xc7\xc0\x00\x00-04\x00\n<-04>4" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf7\x1ac\xc3r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-1UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x0e\x10\x00\x00+01\x00\n<+01>-" + + "1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xfc\x19@\xb9r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-9UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00~\x90\x00\x00+09\x00\n<+09>" + + "-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/UCTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/GMTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vU5\xb8\xe8\x86q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+1UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xf1\xf0\x00\x00-01\x00\n<-01>1\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vU\"\xf8\x8f/q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+8UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x8f\x80\x00\x00-08\x00\n<-08>8\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vU\xb2\xab\xd1Is\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-11UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x9a\xb0\x00\x00+11\x00\n<+11>-11\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xbc\x19y\x04r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-2UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x1c \x00\x00+02\x00\n<+02>-2\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vUJ0p-r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-7UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00bp\x00\x00+07\x00\n<+07>-7\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x90`N\xe8s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-13UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xb6\xd0\x00\x00+13\x00\n<+13>-" + + "13\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x84\x19\xb3\tq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+9UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x81p\x00\x00-09\x00\n<-09" + + ">9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00Etc/GMT-0UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf7\x19s\x81s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-12UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xa8\xc0\x00\x00+12\x00\n<+12>" + + "-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\r\x00\x1c\x00Etc/GreenwichUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00" + + "\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU,{\xdc;s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-14UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\xc4\xe0\x00\x00+14\x00\n" + + "<+14>-14\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\b\x00\x1c\x00Etc/GMT0UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00" + + "\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00Etc/GMT+0UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nG" + + "MT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd9|\xbd7s\x00\x00\x00s\x00\x00\x00\n\x00\x1c\x00Etc/GMT-10UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x8c\xa0\x00\x00+10\x00\n<+" + + "10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\r\x00\x1c\x00Etc/UniversalUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00U" + + "TC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd4X\x9b\xf3q\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+5UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xb9\xb0\x00\x00-05" + + "\x00\n<-05>5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\a\x00\x1c\x00Etc/UTCUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + + "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\n" + + "UTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU)\xb9\xbe\x9dr\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+11UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffeP\x00\x00-11\x00\n<" + + "-11>11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8e\x1569r\x00\x00\x00r\x00\x00\x00\n\x00\x1c\x00Etc/GMT+10UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffs`\x00\x00-10\x00" + + "\n<-10>10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc5\x18\xb6\xfbr\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-8UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00p\x80\x00\x00+08" + + "\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9c\xfcm\x99r\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-3UT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00*0\x00\x00+0" + + "3\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUe\xcb\xe9Qq\x00\x00\x00q\x00\x00\x00\t\x00\x1c\x00Etc/GMT+3UT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\xd5\xd0\x00\x00-" + + "03\x00\n<-03>3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU!\xd6~wr\x00\x00\x00r\x00\x00\x00\t\x00\x1c\x00Etc/GMT-5UT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00FP\x00\x00+" + + "05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Europe/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe1\xc1\xeb\x05\x8c\x03\x00\x00\x8c\x03\x00\x00\r\x00\x1c\x00Europe/Moscow" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\v\x00" + + "\x00\x00&\xff\xff\xff\xffV\xb6\xc0\xc7\xff\xff\xff\xff\x9b_\x1e\xc7\xff\xff\xff\xff\x9d>\xf2y\xff\xff\xff\xff\x9e*\xee\xf9\xff\xff\xff\xff\x9e\xf79i\xff\xff\xff\xff\x9f\x84W\xf9\xff\xff\xff\xff\xa0\xd8l\xe9\xff" + + "\xff\xff\xff\xa1\x009\x80\xff\xff\xff\xff\xa1<\xa6@\xff\xff\xff\xff\xa4\x10m\xc0\xff\xff\xff\xff\xa4=2\xb0\xff\xff\xff\xff\xa5\x15h\xb0\xff\xff\xff\xff\xa5=\x03\xc0\xff\xff\xff\xff\xa7\x1eEP\xff\xff\xff\xff\xb5" + + "\xa4\x19`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00" + + "\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"" + + "L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\x17\x80\x00" + + "\x00\x00\x00)x\xbf\x80\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/" + + "t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00" + + "\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00" + + "\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K" + + "\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x01\x03\x02\x03\x04\x02\x04\x05\x06\x05\a\x05\x06\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\t\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\n\x06\x00\x00#9\x00\x00\x00\x00#9\x00\x04\x00\x001" + + "\x87\x01\b\x00\x00#w\x00\x04\x00\x00?\x97\x01\f\x00\x008@\x01\x11\x00\x00*0\x00\x15\x00\x00FP\x01\x19\x00\x00\x1c \x00\x1d\x00\x00*0\x01!\x00\x008@\x00\x15LMT\x00MMT\x00M" + + "ST\x00MDST\x00MSD\x00MSK\x00+05\x00EET\x00EEST\x00\nMSK-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1b8\xfel\xd6\x02\x00\x00\xd6\x02" + + "\x00\x00\x0e\x00\x1c\x00Europe/SaratovUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\x009\x80\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17" + + "\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00" + + "\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%" + + "\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00" + + "\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004" + + "Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00" + + "\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00B" + + "E\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00" + + "\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00XCNp\x01\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00" + + "\x00+2\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x0f\x00\x1c\x00Europe/GuernseyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff" + + "\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e" + + "\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff" + + "\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x87" + + "0 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff" + + "\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1" + + "\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff" + + "\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ" + + "\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff" + + "\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9" + + "\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff" + + "\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b" + + ") \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff" + + "\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!" + + "\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff" + + "\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9" + + "\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00" + + "\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168" + + "Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00" + + "\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$," + + "6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00" + + "\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e" + + "\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT0BST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "\xea\xc48\xde\\\x02\x00\x00\\\x02\x00\x00\r\x00\x1c\x00Europe/TiraneUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\x96\xaa4h\xff\xff\xff\xff\xc8m\x87p\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xff" + + "ͩ\x17\x90\xff\xff\xff\xff\u0378\xe9\x90\x00\x00\x00\x00\b(9\xf0\x00\x00\x00\x00\b\xef>`\x00\x00\x00\x00\n\x05x\xf0\x00\x00\x00\x00\n\xd0q\xe0\x00\x00\x00\x00\v\xe9Op\x00\x00\x00\x00\f\xb4H`" + + "\x00\x00\x00\x00\r\xd2k\xf0\x00\x00\x00\x00\x0e\x94*`\x00\x00\x00\x00\x0f\xb0\xfcp\x00\x00\x00\x00\x10t\f`\x00\x00\x00\x00\x11\x90\xdep\x00\x00\x00\x00\x12S\xee`\x00\x00\x00\x00\x13p\xc0p\x00\x00\x00\x00" + + "\x14;\xb9`\x00\x00\x00\x00\x15H\xb9p\x00\x00\x00\x00\x16\x13\xb2`\x00\x00\x00\x00\x171\xd5\xf0\x00\x00\x00\x00\x17\xfc\xce\xe0\x00\x00\x00\x00\x19\x00\x94p\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0" + "\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00" + "\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcb" + + "p\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00" + + "\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00V\xf7\x14p\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x03" + + "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00-\f\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00" + + "\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02" + + "\x00\x00\x11\x00\x1c\x00Europe/CopenhagenUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff" + + "\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ" + + "4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff\xff\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff" + + "\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9" + + "x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00" + "\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 l" + "r\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D" + - "%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00" + - "\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00V\xf7\x14p\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04" + - "\x01\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00-\f\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01" + - "\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xea\xc48\xde\\\x02\x00\x00" + - "\\\x02\x00\x00\r\x00\x1c\x00Europe/TiraneUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff\x96\xaa4h\xff\xff\xff\xff\xc8m\x87p\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff" + - "\u0378\xe9\x90\x00\x00\x00\x00\b(9\xf0\x00\x00\x00\x00\b\xef>`\x00\x00\x00\x00\n\x05x\xf0\x00\x00\x00\x00\n\xd0q\xe0\x00\x00\x00\x00\v\xe9Op\x00\x00\x00\x00\f\xb4H`\x00\x00\x00\x00\r\xd2k\xf0" + - "\x00\x00\x00\x00\x0e\x94*`\x00\x00\x00\x00\x0f\xb0\xfcp\x00\x00\x00\x00\x10t\f`\x00\x00\x00\x00\x11\x90\xdep\x00\x00\x00\x00\x12S\xee`\x00\x00\x00\x00\x13p\xc0p\x00\x00\x00\x00\x14;\xb9`\x00\x00\x00\x00" + - "\x15H\xb9p\x00\x00\x00\x00\x16\x13\xb2`\x00\x00\x00\x00\x171\xd5\xf0\x00\x00\x00\x00\x17\xfc\xce\xe0\x00\x00\x00\x00\x19\x00\x94p\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0\x00\x00\x00\x00\x1b\xbc\xbd\x10" + - "\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00" + - "#\x86%p\x00\x00\x00\x00?\x9b\x00p\x00" + + "\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G" + + "#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00" + + "\x00\x00\x00TL\x1d`\x00\x00\x00\x00V\xf7\x14p\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x05\x06\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + + "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00-`\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\f\x00\x00*0\x01\x04\x00\x00\x1c" + + " \x00\x10LMT\x00+03\x00+05\x00+04\x00+02\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\r\x00" + + "\x1c\x00Europe/JerseyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff" + + "\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?" + + "\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff" + + "\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r" + + "\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff" + + "\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x" + + "\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff" + + "\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n" + + "^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff" + + "\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5" + + "\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff" + + "\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfd" + + "q \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff" + + "\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f" + + "\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00" + + "\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0" + + "N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00" + + "\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18\xe3" + + "\xaf\x90\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00" + + "\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*" + + "-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00" + + "\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT" + + "0BST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x12\x00\x1c\x00Europe/I" + + "sle_of_ManUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d" + + "\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff" + + "\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l" + + " \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff" + + "\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00" + + " \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff" + + "\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xca\x16&" + + "\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff" + + "\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff\xff\xff\xd5\xdf\xe0" + + "\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff" + + "\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*" + + "\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff" + + "\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI" + + "\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff" + + "\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X" + + " \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00" + + "\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10" + + "\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00" + + "\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr" + + "\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00" + + "\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5" + + "\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT0BST," + + "M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x0f\x00\x1c\x00Europe/Istanb" + + "ulUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s\x00\x00\x00" + + "\x06\x00\x00\x00\x19\xff\xff\xff\xffV\xb6\xc8\xd8\xff\xff\xff\xff\x90\x8b\xf5\x98\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9bվ\xd0\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80" + + "`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\x7f\xd0\xff\xff\xff\xff\xaa((`\xff\xff\xff\xff\xaa\xe1\xfd\xd0\xff\xff\xff\xff\xab\xf9\x89\xe0\xff\xff\xff\xff\xac\xc31P\xff\xff\xff" + + "\xffȁ?\xe0\xff\xff\xff\xff\xc9\x01\x13P\xff\xff\xff\xff\xc9J\xf5`\xff\xff\xff\xff\xca\u0380P\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xd2k\tP\xff\xff\xff\xffӢ9`\xff\xff\xff\xff\xd4C\x02" + + "P\xff\xff\xff\xff\xd5L\r\xe0\xff\xff\xff\xff\xd6){\xd0\xff\xff\xff\xff\xd7+\xef\xe0\xff\xff\xff\xff\xd8\t]\xd0\xff\xff\xff\xff\xd9\x02\x97`\xff\xff\xff\xff\xd9\xe9?\xd0\xff\xff\xff\xff\xda\xeb\xb3\xe0\xff\xff\xff" + + "\xff\xdb\xd2\\P\xff\xff\xff\xff\xdc\xd4\xd0`\xff\xff\xff\xffݲ>P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b\xefP\xff\xff\xff\xff\xf5h\x06`\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n\x93" + + "p\x00\x00\x00\x00\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00\x00\x00\n\xf9^p\x00\x00\x00\x00\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00\x00" + + "\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89\xb0p\x00\x00\x00\x00\x19ܰ\xe0\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6\xef" + + "\xf0\x00\x00\x00\x00\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00" + + "\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xeb" + + "p\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00" + + "\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD" + + "\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00" + + "\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf" + + "\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00\x00" + + "\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S8\xbe\x10\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N" + + "\x90\x00\x00\x00\x00V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\r\x00\x00*0\x00\x11\x00\x008@\x01\x15LMT\x00" + + "IMT\x00EEST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUgp\xc0\xa7\xb6\x02\x00\x00\xb6\x02\x00\x00\v\x00\x1c\x00" + + "Europe/RigaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x005\x00\x00\x00\t\x00\x00\x00&\xff\xff\xff\xffV\xb6\xcd^\xff\xff\xff\xff\x9e\xb9\x87\xfe\xff\xff\xff\xff\x9f\x84\x8e\xfe\xff\xff\xff\xff\xa0\x88F~\xff\xff\xff\xff\xa0˂\xfe\xff\xff\xff\xff\xad\xe7" + + "\xf1\xde\xff\xff\xff\xffȯd`\xff\xff\xff\xff\xcabeP\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff" + + "\xff\xffА\x89p\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a\xcc" + + "\x93\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00" + + "\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5" + + "\x17\x80\x00\x00\x00\x00)\xd5\b\x80\x00\x00\x00\x00*\xc4\xf9\x80\x00\x00\x00\x00+\xb4\xea\x80\x00\x00\x00\x00,\xa4ۀ\x00\x00\x00\x00-\x94̀\x00\x00\x00\x00.\x84\xbd\x80\x00\x00\x00\x00/t\xae\x80\x00\x00" + + "\x00\x000d\x9f\x80\x00\x00\x00\x001]\xcb\x00\x00\x00\x00\x002M\xbc\x00\x00\x00\x00\x003=\xbb\x10\x00\x00\x00\x004R\x96\x10\x00\x00\x00\x005\x1d\x9d\x10\x00\x00\x00\x0062x\x10\x00\x00\x00\x006\xfd" + + "\x7f\x10\x00\x00\x00\x008\x1b\x94\x90\x00\x00\x00\x00:\xbdC\x10\x01\x02\x01\x02\x01\x03\x04\x06\x05\x06\x05\x06\x05\x04\a\x04\a\x04\a\x04\a\x04\a\x04\a\x04\a\x04\a\x04\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03" + + "\b\x03\b\x03\b\x03\b\x03\b\x03\b\x00\x00\x16\xa2\x00\x00\x00\x00\x16\xa2\x00\x04\x00\x00$\xb2\x01\b\x00\x00\x1c \x00\f\x00\x00*0\x00\x10\x00\x00\x0e\x10\x00\x14\x00\x00\x1c \x01\x18\x00\x008@\x01\x1d\x00" + + "\x00*0\x01!LMT\x00RMT\x00LST\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00\nEET-2EEST,M3.5.0" + + "/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUiS\x18D.\x02\x00\x00.\x02\x00\x00\v\x00\x1c\x00Europe/KievUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\b\x00\x00\x00\"\xff\xff\xff" + + "\xffV\xb6\xc7d\xff\xff\xff\xff\xaa\x19\xa7d\xff\xff\xff\xff\xb5\xa4\x19`\xff\xff\xff\xff\xca\xcd.\xd0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xff\xceͨ" + + "p\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00" + + "\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7" + + "\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00&\x8d \xe0\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd5\b\x80\x00\x00\x00" + + "\x00*\xc4\xf9\x80\x00\x00\x00\x00+\xb4\xea\x80\x00\x00\x00\x00,\xa4ۀ\x00\x00\x00\x00-\x94̀\x00\x00\x00\x00.\x84\xbd\x80\x00\x00\x00\x00/t\xae\x80\x00\x00\x00\x000d\x9f\x80\x00\x00\x00\x001]\xcb" + + "\x00\x00\x00\x00\x002r\xb4\x10\x01\x02\x03\x05\x04\x05\x04\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\x00\x00\x1c\x9c\x00\x00\x00\x00\x1c\x9c\x00\x04" + + "\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10\x00\x00\x1c \x01\x14\x00\x008@\x01\x19\x00\x00*0\x01\x1dLMT\x00KMT\x00EET\x00MSK\x00CET\x00CEST" + + "\x00MSD\x00EEST\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUZk#V\x81\x03" + + "\x00\x00\x81\x03\x00\x00\r\x00\x1c\x00Europe/MadridUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00\x06\x00\x00\x00\x1b\xff\xff\xff\xff~6\xb5\x00\xff\xff\xff\xff\x9e\xba\xc5\xf0\xff\xff\xff\xff\x9f\xa09\x00\xff\xff\xff\xff\xa0\x90\x1b\xf0\xff\xff" + + "\xff\xff\xa1\x81l\x80\xff\xff\xff\xff\xaa\x05\xefp\xff\xff\xff\xff\xaa\xe7n\x00\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89" + + "z\x00\xff\xff\xff\xff\xb2p0\x80\xff\xff\xff\xff\xb3r\x88p\xff\xff\xff\xff\xb4P\x12\x80\xff\xff\xff\xff\xc2\xc9\xec\xf0\xff\xff\xff\xff\xc3X]\x00\xff\xff\xff\xff\xc4H?\xf0\xff\xff\xff\xff\xc4m\x1b\xe0\xff\xff" + + "\xff\xff\xc59t`\xff\xff\xff\xff\xc7![\x80\xff\xff\xff\xff\xc7\xf5\x8e\xf0\xff\xff\xff\xff\xcb\xf5\xde`\xff\xff\xff\xff̕q\xf0\xff\xff\xff\xff\xcd\xc3K`\xff\xff\xff\xffΠ\xd5p\xff\xff\xff\xffϣ" + + "-`\xff\xff\xff\xffЀ\xb7p\xff\xff\xff\xffу\x0f`\xff\xff\xff\xff\xd2`\x99p\xff\xff\xff\xff\xd3b\xf1`\xff\xff\xff\xff\xd4@{p\xff\xff\xff\xff\xd9\x1eF\xe0\xff\xff\xff\xff\xd9\xe9[\xf0\x00\x00" + + "\x00\x00\b\r\xcd\xe0\x00\x00\x00\x00\b\xf4\x92p\x00\x00\x00\x00\t\xed\xaf\xe0\x00\x00\x00\x00\n\xd4tp\x00\x00\x00\x00\v\xbb\x1c\xe0\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xa49`\x00\x00\x00\x00\x0e\x8a" + + "\xfd\xf0\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00" + + "\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac" + + "\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%" + + "ـ\x00\x00\x00\x00EC\xef\x00\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00" + + "\x00\x00K\xae\x9c\x00\x00\x00\x00\x00Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x00\x00\x00\x00TL+p\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x03\x04\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\x05\x06\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\a\x04\x00\x00\x138\x00\x00\x00\x00\x1c " + + "\x01\x04\x00\x00\x0e\x10\x00\t\x00\x00*0\x01\r\x00\x00\x1c \x00\x12\x00\x008@\x01\x16\x00\x00*0\x00\x1a\x00\x00*0\x00\x1eLMT\x00CEST\x00CET\x00EEST\x00EET\x00" + + "MSD\x00MSK\x00+03\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUh\xa5J[\xa0\x03\x00\x00\xa0\x03\x00\x00\f\x00\x1c\x00Europe/Malt" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00V\x00\x00\x00\x03" + + "\x00\x00\x00\r\xff\xff\xff\xffp\xbd\xd3d\xff\xff\xff\xff\x9b8\xf8p\xff\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0\x1c\xe0" + + "\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff" + + "\u03a2C\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2L\xd2\xf0\xff\xff\xff\xff\xd3>1\x90\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p" + + "\xff\xff\xff\xff\xd6)\x97\xf0\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c\xd2p\xff\xff\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff" + + "\xfc\xfc\xb4p\xff\xff\xff\xff\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0\xff\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00\x00\x00\x02\x9cZp\x00\x00\x00\x00\x03Bwp" + + "\x00\x00\x00\x00\x04\x85v\xf0\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06\x1a3p\x00\x00\x00\x00\a\n$p\x00\x00\x00\x00\b\x17\x16p\x00\x00\x00\x00\b\xda4p\x00\x00\x00\x00\t\xf7\x14\x90\x00\x00\x00\x00" + + "\n\xc2\r\x80\x00\x00\x00\x00\v\xd6\xf6\x90\x00\x00\x00\x00\f\xa1\xef\x80\x00\x00\x00\x00\r\xb6ؐ\x00\x00\x00\x00\x0e\x81р\x00\x00\x00\x00\x0f\x96\xba\x90\x00\x00\x00\x00\x10a\xb3\x80\x00\x00\x00\x00\x11v\x9c\x90" + + "\x00\x00\x00\x00\x12A\x95\x80\x00\x00\x00\x00\x13E[\x10\x00\x00\x00\x00\x14*\xb2\x00\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00" + + "\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10" + + "\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xfe垛\x03\x00\x00\x9b\x03\x00\x00\r\x00\x1c\x00Europe/WarsawUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffV\xb6\xd0P\xff\xff\xff\xff\x99\xa8*\xd0\xff" + + "\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xa0\x9a\xb6\x00\xff\xff\xff\xff\xa1" + + "e\xbd\x00\xff\xff\xff\xff\xa6}|`\xff\xff\xff\xff\xc8v\xde\x10\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЄ\xba\x00\xff" + + "\xff\xff\xffѕ\x92p\xff\xff\xff\xffҊ\xbb`\xff\xff\xff\xff\xd3b\xffp\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd5^\xad\x10\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8" + + "\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\xff\xff\xff\xff\xe8T\xd2\x00\xff\xff\xff\xff\xe8\xf1\xb4\x80\xff\xff\xff\xff\xe9᥀\xff\xff\xff\xff\xeaі\x80\xff\xff\xff\xff\xec\x14\x96\x00\xff" + + "\xff\xff\xff캳\x00\xff\xff\xff\xff\xed\xaa\xa4\x00\xff\xff\xff\xff\ue695\x00\xff\xff\xff\xff\xef\xd4Z\x00\xff\xff\xff\xff\xf0zw\x00\xff\xff\xff\xff\xf1\xb4<\x00\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3" + + "\x94\x1e\x00\xff\xff\xff\xff\xf4:;\x00\xff\xff\xff\xff\xf5}:\x80\xff\xff\xff\xff\xf6\x1a\x1d\x00\x00\x00\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\x8b\f\x00\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10t(\x80\x00" + + "\x00\x00\x00\x11d\x19\x80\x00\x00\x00\x00\x12T\n\x80\x00\x00\x00\x00\x13M6\x00\x00\x00\x00\x00\x143\xec\x80\x00\x00\x00\x00\x15#݀\x00\x00\x00\x00\x16\x13\u0380\x00\x00\x00\x00\x17\x03\xbf\x80\x00\x00\x00\x00\x17" + + "\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00\x1b\xbc\xaf\x00\x00\x00\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00" + + "\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00" + + "\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#" + + "\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00" + + "\x00\x00TL\x1d`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + + "\x01\x04\x01\x04\x01\x04\x01\x03\x01\x00\x00.\x98\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+03" + + ">-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc7\xf5\x94\xdaQ\x04\x00\x00Q\x04\x00\x00\r\x00\x1c\x00Europe/MonacoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00e\x00\x00\x00\a\x00\x00\x00\x1f\xff\xff\xff\xffkɛ\xcf\xff\xff" + + "\xff\xff\x91`PO\xff\xff\xff\xff\x9bGx\xf0\xff\xff\xff\xff\x9b\xd7,p\xff\xff\xff\xff\x9c\xbc\x91p\xff\xff\xff\xff\x9d\xc0H\xf0\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0*\xf0\xff\xff\xff\xff\xa0`" + + "\xa5\xf0\xff\xff\xff\xff\xa1\x80\f\xf0\xff\xff\xff\xff\xa2.\x12\xf0\xff\xff\xff\xff\xa3zL\xf0\xff\xff\xff\xff\xa45\x81\xf0\xff\xff\xff\xff\xa5^#p\xff\xff\xff\xff\xa6%5\xf0\xff\xff\xff\xff\xa7'\x9b\xf0\xff\xff" + + "\xff\xff\xa8X&p\xff\xff\xff\xff\xa9\a}\xf0\xff\xff\xff\xff\xa9\xee4p\xff\xff\xff\xff\xaa\xe7_\xf0\xff\xff\xff\xff\xab\xd7P\xf0\xff\xff\xff\xff\xac\xc7A\xf0\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa7" + + "#\xf0\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x05\xf0\xff\xff\xff\xff\xb1\x89k\xf0\xff\xff\xff\xff\xb2p\"p\xff\xff\xff\xff\xb3r\x88p\xff\xff\xff\xff\xb4P\x04p\xff\xff\xff\xff\xb5I/\xf0\xff\xff" + + "\xff\xff\xb6/\xe6p\xff\xff\xff\xff\xb72Lp\xff\xff\xff\xff\xb8\x0f\xc8p\xff\xff\xff\xff\xb8\xff\xb9p\xff\xff\xff\xff\xb9\xef\xaap\xff\xff\xff\xff\xba\xd6`\xf0\xff\xff\xff\xff\xbb\xd8\xc6\xf0\xff\xff\xff\xff\xbc\xc8" + + "\xb7\xf0\xff\xff\xff\xff\xbd\xb8\xa8\xf0\xff\xff\xff\xff\xbe\x9f_p\xff\xff\xff\xff\xbf\x98\x8a\xf0\xff\xff\xff\xff\xc0\x9a\xf0\xf0\xff\xff\xff\xff\xc1xl\xf0\xff\xff\xff\xff\xc2h]\xf0\xff\xff\xff\xff\xc3XN\xf0\xff\xff" + + "\xff\xff\xc4?\x05p\xff\xff\xff\xff\xc580\xf0\xff\xff\xff\xff\xc6:\x96\xf0\xff\xff\xff\xff\xc7X\xacp\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xc8l'\xe0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ" + + "\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0O\xe1\xe0\xff\xff\xff\xffЉ\xf1\xf0\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\v\xbb9\x00\x00\x00" + + "\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13M" + + "D\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00" + + "\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\" + + "c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#(\xe8L\xff\xff\xff\xffp\xbc\x81p\xff\xff\xff\xff\x9b8\xf8p\xff\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff\xff\xff\x9e\x89" + - "\xfep\xff\xff\xff\xff\x9f\xa0\x1c\xe0\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff" + - "\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2L\xd2\xf0\xff\xff\xff\xff\xd3>1\x90\xff\xff\xff\xff\xd4I" + - "\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p\xff\xff\xff\xff\xd6)\x97\xf0\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c\xd2p\xff\xff" + - "\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff\xfc\xfc\xb4p\xff\xff\xff\xff\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0\xff\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00\x00\x00\x02\x9c" + - "Zp\x00\x00\x00\x00\x03Bwp\x00\x00\x00\x00\x04\x85v\xf0\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a\vu\xf0\x00\x00\x00\x00\bE:\xf0\x00\x00\x00\x00\b\xebW\xf0\x00\x00" + - "\x00\x00\n.Wp\x00\x00\x00\x00\n\xcb9\xf0\x00\x00\x00\x00\f\x0e9p\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xe4\xe0\xf0\x00\x00\x00\x00\x0e\x8a\xfd\xf0\x00\x00\x00\x00\x0f\xcd\xfdp\x00\x00\x00\x00\x10t" + - "\x1ap\x00\x00\x00\x00\x11\xad\xdfp\x00\x00\x00\x00\x12S\xfcp\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00" + - "\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c" + - "\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#(\xe8L\xff\xff\xff\xffp\xbc\x81p\xff\xff\xff\xff\x9b8\xf8p\xff\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff" + - "\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0\x1c\xe0\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7" + - "K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2L\xd2\xf0\xff\xff\xff\xff\xd3>1\x90\xff\xff" + - "\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p\xff\xff\xff\xff\xd6)\x97\xf0\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c" + - "\xd2p\xff\xff\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff\xfc\xfc\xb4p\xff\xff\xff\xff\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0\xff\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00" + - "\x00\x00\x02\x9cZp\x00\x00\x00\x00\x03Bwp\x00\x00\x00\x00\x04\x85v\xf0\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a\vu\xf0\x00\x00\x00\x00\bE:\xf0\x00\x00\x00\x00\b\xeb" + - "W\xf0\x00\x00\x00\x00\n.Wp\x00\x00\x00\x00\n\xcb9\xf0\x00\x00\x00\x00\f\x0e9p\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xe4\xe0\xf0\x00\x00\x00\x00\x0e\x8a\xfd\xf0\x00\x00\x00\x00\x0f\xcd\xfdp\x00\x00" + - "\x00\x00\x10t\x1ap\x00\x00\x00\x00\x11\xad\xdfp\x00\x00\x00\x00\x12S\xfcp\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03" + - "͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00" + - "\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%ـ\x00\x00" + - "\x00\x00EC\xef\x00\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00\x00\x00K\xae" + - "\x9c\x00\x00\x00\x00\x00Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x01\x02\x03\x05\x04\x05\x04\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a" + - "\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\b\x00\x00\x19\xd8\x00\x00\x00\x00\x19\xc8\x00\x04\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10\x00\x00\x1c " + - "\x01\x14\x00\x008@\x01\x19\x00\x00*0\x01\x1d\x00\x00*0\x00\"LMT\x00MMT\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00+03\x00\n<" + - "+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RZk#V\x81\x03\x00\x00\x81\x03\x00\x00\r\x00\x1c\x00Europe/MadridUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\x00\x00\x00\x06\x00\x00\x00\x1b\xff\xff\xff\xff~6\xb5" + - "\x00\xff\xff\xff\xff\x9e\xba\xc5\xf0\xff\xff\xff\xff\x9f\xa09\x00\xff\xff\xff\xff\xa0\x90\x1b\xf0\xff\xff\xff\xff\xa1\x81l\x80\xff\xff\xff\xff\xaa\x05\xefp\xff\xff\xff\xff\xaa\xe7n\x00\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff" + - "\xff\xae\xa72\x00\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x14\x00\xff\xff\xff\xff\xb1\x89z\x00\xff\xff\xff\xff\xb2p0\x80\xff\xff\xff\xff\xb3r\x88p\xff\xff\xff\xff\xb4P\x12\x80\xff\xff\xff\xff\xc2\xc9\xec" + - "\xf0\xff\xff\xff\xff\xc3X]\x00\xff\xff\xff\xff\xc4H?\xf0\xff\xff\xff\xff\xc4m\x1b\xe0\xff\xff\xff\xff\xc59t`\xff\xff\xff\xff\xc7![\x80\xff\xff\xff\xff\xc7\xf5\x8e\xf0\xff\xff\xff\xff\xcb\xf5\xde`\xff\xff\xff" + - "\xff̕q\xf0\xff\xff\xff\xff\xcd\xc3K`\xff\xff\xff\xffΠ\xd5p\xff\xff\xff\xffϣ-`\xff\xff\xff\xffЀ\xb7p\xff\xff\xff\xffу\x0f`\xff\xff\xff\xff\xd2`\x99p\xff\xff\xff\xff\xd3b\xf1" + - "`\xff\xff\xff\xff\xd4@{p\xff\xff\xff\xff\xd9\x1eF\xe0\xff\xff\xff\xff\xd9\xe9[\xf0\x00\x00\x00\x00\b\r\xcd\xe0\x00\x00\x00\x00\b\xf4\x92p\x00\x00\x00\x00\t\xed\xaf\xe0\x00\x00\x00\x00\n\xd4tp\x00\x00\x00" + - "\x00\v\xbb\x1c\xe0\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xa49`\x00\x00\x00\x00\x0e\x8a\xfd\xf0\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18" + - "\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00" + - "\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr" + - "\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf2y\xff\xff\xff\xff\x9e*\xee\xf9\xff\xff\xff\xff\x9e\xf79i\xff\xff\xff\xff\x9f\x84W\xf9\xff\xff\xff\xff\xa0\xd8l\xe9\xff\xff\xff\xff" + - "\xa1\x009\x80\xff\xff\xff\xff\xa1<\xa6@\xff\xff\xff\xff\xa4\x10m\xc0\xff\xff\xff\xff\xa4=2\xb0\xff\xff\xff\xff\xa5\x15h\xb0\xff\xff\xff\xff\xa5=\x03\xc0\xff\xff\xff\xff\xa7\x1eEP\xff\xff\xff\xff\xb5\xa4\x19`" + - "\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00" + - "\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0" + - "\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00" + - ")x\xbf\x80\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p" + - "\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x00" + - "6\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00" + - "EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0" + - "\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x01\x03\x02\x03\x04\x02\x04\x05\x06\x05\a\x05\x06\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\t" + - "\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\n\x06\x00\x00#9\x00\x00\x00\x00#9\x00\x04\x00\x001\x87\x01\b" + - "\x00\x00#w\x00\x04\x00\x00?\x97\x01\f\x00\x008@\x01\x11\x00\x00*0\x00\x15\x00\x00FP\x01\x19\x00\x00\x1c \x00\x1d\x00\x00*0\x01!\x00\x008@\x00\x15LMT\x00MMT\x00MST\x00" + - "MDST\x00MSD\x00MSK\x00+05\x00EET\x00EEST\x00\nMSK-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x8c\xc8\x15\xd0P\x02\x00\x00P\x02\x00\x00\f" + - "\x00\x1c\x00Europe/SofiaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00-\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffV\xb6\xce$\xff\xff\xff\xffr\xc3\xe3\x18\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff" + - "\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r$ \x00\x00\x00\x00\x11c\xefP\x00\x00\x00\x00\x12U?\xe0\x00\x00\x00\x00\x13M\v\xd0\x00\x00\x00\x00\x145!\xe0\x00\x00\x00\x00\x15," + - "\xed\xd0\x00\x00\x00\x00\x16\x13\xc0p\x00\x00\x00\x00\x17\f\xcf\xd0\x00\x00\x00\x00\x17\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00\x1b\xbc\xaf\x00\x00\x00" + - "\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LF\x00\x00\x00\x00\x00#<" + - "7\x00\x00\x00\x00\x00$,(\x00\x00\x00\x00\x00%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)\xd4\xec`\x00\x00" + - "\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]" + - "\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\xbb\x10\x01\x02\x03\x04\x03\x04\x03\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02" + - "\x05\x02\x05\x00\x00\x15\xdc\x00\x00\x00\x00\x1bh\x00\x04\x00\x00\x1c \x00\b\x00\x00\x0e\x10\x00\f\x00\x00\x1c \x01\x10\x00\x00*0\x01\x15LMT\x00IMT\x00EET\x00CET\x00CEST\x00" + - "EEST\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xccb\xf72\xa4\x02\x00\x00\xa4\x02\x00" + - "\x00\x0e\x00\x1c\x00Europe/VilniusUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\t\x00\x00\x00&\xff\xff\xff\xffV\xb6\xccD\xff\xff\xff\xff\x9cO\x1fP\xff\xff\xff\xff\xa1\x85J\x98\xff\xff\xff\xff\xa2\xf10\xf0\xff\xff\xff\xff\xa3f" + - "x`\xff\xff\xff\xffȬ\xcfp\xff\xff\xff\xff\xcaY*\xd0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd00=\xe0\x00\x00" + - "\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc" + - "\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00" + - "\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd5" + - "\b\x80\x00\x00\x00\x00*\xc4\xf9\x80\x00\x00\x00\x00+\xb4\xea\x80\x00\x00\x00\x00,\xa4ۀ\x00\x00\x00\x00-\x94̀\x00\x00\x00\x00.\x84\xbd\x80\x00\x00\x00\x00/t\xae\x80\x00\x00\x00\x000d\x9f\x80\x00\x00" + - "\x00\x001]\xcb\x00\x00\x00\x00\x002r\xa6\x00\x00\x00\x00\x003=\xad\x00\x00\x00\x00\x004R\x88\x00\x00\x00\x00\x005\x1d\x9d\x10\x00\x00\x00\x0062x\x10\x00\x00\x00\x006\xfd\u007f\x10\x00\x00\x00\x008\x1b" + - "\x94\x90\x00\x00\x00\x00>\x86A\x90\x01\x02\x03\x04\x03\x05\x06\x03\x06\x03\x06\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\a\x05\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\x06\x03\x06\x04" + - "\b\x00\x00\x17\xbc\x00\x00\x00\x00\x13\xb0\x00\x04\x00\x00\x16h\x00\b\x00\x00\x0e\x10\x00\f\x00\x00\x1c \x00\x10\x00\x00*0\x00\x14\x00\x00\x1c \x01\x18\x00\x008@\x01\x1d\x00\x00*0\x01!LMT\x00W" + - "MT\x00KMT\x00CET\x00EET\x00MSK\x00CEST\x00MSD\x00EEST\x00\nEET-2EEST,M3.5.0/3,M10.5.0" + - "/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\r\x00\x1c\x00Europe/BerlinUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffo\xa2a\xf8\xff\xff\xff" + - "\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K" + - "\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff\xff\xff\xd2X\xbe\x80\xff\xff\xff" + - "\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a" + - "\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00" + - "\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f" + - "\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b" + - "\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00" + - "\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8e" + - "o\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00XCNp\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x04\x01\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04" + - "\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00+2\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00" + - "+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe6Kf\xab\xfe\x02\x00\x00\xfe\x02\x00\x00\x0f\x00\x1c\x00Europe/Budape" + - "stUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\x00\x00\x00" + - "\x03\x00\x00\x00\r\xff\xff\xff\xffk\x17\x91\x9c\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97" + - "\x90\xff\xff\xff\xff\xa0\x9a\xc4\x10\xff\xff\xff\xff\xa1dy\x90\xff\xff\xff\xff\xa2p\x1a\x10\xff\xff\xff\xff\xa3M\x96\x10\xff\xff\xff\xff\xc9\xf3\xb5`\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff" + - "\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xffљx\xe0\xff\xff\xff\xffҊ\xc9p\xff\xff\xff\xff\xd3P\xa6\x90\xff\xff\xff\xff\xd4K\x15\x80\xff\xff\xff\xff\xd59\xc3" + - "\x10\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7\x19\xa5\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\xff\xff\xff\xff⢨\xf0\xff\xff\xff\xff\xe3Q\xf2`\xff\xff\xff" + - "\xff䂧\x10\xff\xff\xff\xff\xe51\xfe\x90\xff\xff\xff\xff\xe6t\xfe\x10\xff\xff\xff\xff\xe7\x11\xe0\x90\xff\xff\xff\xff\xe8T\xe0\x10\xff\xff\xff\xff\xe8\xf1\u0090\x00\x00\x00\x00\x13M'\xf0\x00\x00\x00\x00\x143\xde" + - "p\x00\x00\x00\x00\x15#\xcfp\x00\x00\x00\x00\x16\x13\xc0p\x00\x00\x00\x00\x17\x03\xb1p\x00\x00\x00\x00\x17\xf3\xa2p\x00\x00\x00\x00\x18\xe3\x93p\x00\x00\x00\x00\x19ӄp\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00" + - "\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT" + - "\x10\x00\x00\x00\x00#(\xe8L\xff\xff\xff\xffp\xbc\x81p\xff\xff\xff\xff\x9b8\xf8p\xff\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff" + "\x9f\xa0\x1c\xe0\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90" + @@ -4937,90 +4858,29 @@ const zipdata = "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00 "-\x94ڐ\x00\x00\x00\x00.\x84ː\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + "\x02\x03\x02\x00\x00\v\xb4\x00\x00\x00\x00\v\xb4\x00\x04\x00\x00\x1c \x01\b\x00\x00\x0e\x10\x00\rLMT\x00RMT\x00CEST\x00CET\x00\nCET-1CEST,M3.5." + - "0,M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x90\xa9\xf5ϕ\x02\x00\x00\x95\x02\x00\x00\x10\x00\x1c\x00Europe/BucharestUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x04\x00\x00\x00" + - "\x11\xff\xff\xff\xffl\xcf\xe0\b\xff\xff\xff\xff\xb7\xb0\xd2\b\xff\xff\xff\xff\xb9>\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ`\xff\xff\xff\xff\xbb\xcf~`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff" + - "\xff\xbd\xb8\x9a\xe0\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff\xff\xc2hO\xe0\xff\xff\xff\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1" + - "\xe0\xff\xff\xff\xff\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff\xff\xff\xc7\x18\x04\xe0\x00\x00\x00\x00\x11\xad\xd1`\x00\x00\x00\x00\x12S\xe0P\x00\x00\x00\x00\x13M\v\xd0\x00\x00\x00\x00\x143\xd0`\x00\x00\x00" + - "\x00\x15#݀\x00\x00\x00\x00\x16\x13\u0380\x00\x00\x00\x00\x17\x03\xbf\x80\x00\x00\x00\x00\x17\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00\x1b\xbc\xaf" + - "\x00\x00\x00\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LF\x00\x00\x00\x00" + - "\x00#<7\x00\x00\x00\x00\x00$,(\x00\x00\x00\x00\x00%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)\xd4\xec" + - "`\x00\x00\x00\x00*\xc4\xdd`\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xbf`\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00" + - "\x001]\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\xbb\x10\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\x18x\x00\x00\x00\x00\x18x\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\rLMT\x00BMT\x00EEST\x00EET\x00\nEE" + - "T-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RIo\x11{\xd3\x02\x00\x00\xd3\x02\x00\x00\x11\x00\x1c\x00Eur" + - "ope/BratislavaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff\x1eI\x92\xf8\xff\xff\xff\xffl\xcf\xea\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff" + - "\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4" + - "\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2b\a\x10\xff\xff\xff\xffӀ\x1c\x90\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xffԓ\xb4 \xff\xff\xff\xff\xd5\x02r \xff\xff\xff" + - "\xff\xd5L8\x10\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x01p\x10\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18" + - "\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00" + - "\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr" + - "\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ`\xff\xff\xff\xff\xbb\xcf~`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff\xff\xbd\xb8\x9a\xe0\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98" + - "|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff\xff\xc2hO\xe0\xff\xff\xff\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1\xe0\xff\xff\xff\xff\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff" + - "\xff\xff\xc7\x18\x04\xe0\xff\xff\xff\xffȼ\x93`\xff\xff\xff\xff\xcaw}P\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0N" + - "\x90`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00" + - "\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L" + - "7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00&CL\xe0\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00" + - "\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t" + - "\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\xad\x00\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x06\x05\x06\x05\x06\b" + - "\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x1b\b\x00\x00\x00\x00\x1a\xf4\x00\x04\x00\x00\x18x\x00\b\x00\x00*0\x01\f\x00\x00" + - "\x1c \x00\x11\x00\x00\x0e\x10\x00\x15\x00\x00\x1c \x01\x19\x00\x008@\x01\x1e\x00\x00*0\x00\"LMT\x00CMT\x00BMT\x00EEST\x00EET\x00CET\x00CEST\x00MS" + - "D\x00MSK\x00\nEET-2EEST,M3.5.0,M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00" + - "\x10\x00\x1c\x00Europe/LjubljanaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff^<\xf0H\xff\xff\xff\xff\xca\x025\xe0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\xce" + - "\xa2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xffѡ\x8c\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00" + - "\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"" + - "LT\x10\x00\x00\x00\x00#\x86A\x90\x00\x00\x00\x00?" + - "\x9b\x1c\x90\x00\x00\x00\x00@f#\x90\x00\x00\x00\x00A\x849\x10\x00\x00\x00\x00BF\x05\x90\x00\x00\x00\x00Cd\x1b\x10\x00\x00\x00\x00D%\xe7\x90\x00\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00" + - "\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M" + - "\x8e\x8c\x10\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S7^\x80\x00\x00\x00\x00TL\x1d`\x01" + - "\x02\x03\x05\x04\x05\x04\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x02\a\x02\a\x02\a\x06\x03\x06\x03\x06\x03\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02" + - "\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\b\x03\x00\x00\x1f\xf8\x00\x00\x00\x00\x1f\xe0\x00\x04\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10\x00\x00\x1c \x01\x14\x00\x008@\x01\x19\x00\x00*0" + - "\x01\x1d\x00\x008@\x00\fLMT\x00SMT\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00\nMSK-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R8I\xdeN%\x02\x00\x00%\x02\x00\x00\v\x00\x1c\x00Europe/KievUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\b\x00\x00\x00\"\xff\xff\xff\xffV\xb6\xc7d\xff\xff\xff\xff\xaa\x19\xa7d\xff\xff\xff\xff\xb5\xa4\x19`\xff\xff\xff" + - "\xff\xca\xcd.\xd0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xff\xceͨp\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdb" + - "P\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00" + - "\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n" + - "\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00&\x8d \xe0\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00" + - "\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10\x01\x02\x03\x05\x04\x05\x04\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06" + - "\x03\x06\x03\x06\a\x02\a\x02\a\x02\a\x02\a\x02\a\x00\x00\x1c\x9c\x00\x00\x00\x00\x1c\x9c\x00\x04\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10\x00\x00\x1c \x01\x14\x00\x008@\x01\x19\x00\x00*" + - "0\x01\x1dLMT\x00KMT\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00\nEET-2EEST,M3.5.0/3,M10" + - ".5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RDd#\xc4\xf1\x01\x00\x00\xf1\x01\x00\x00\r\x00\x1c\x00Europe/ZurichUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff$\xf0\xea" + - "\x80\xff\xff\xff\xffq\xd4\x06\x86\xff\xff\xff\xff\xca\x17j\x00\xff\xff\xff\xff\xca\xe2q\x00\xff\xff\xff\xff\xcb\xf7L\x00\xff\xff\xff\xff\xcc\xc2S\x00\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00" + - "\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f" + - "\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00" + - "\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00I" + - "Ϋ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x00\x00\x00\x00V\xf7\x14p\x01\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x05\x06\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x03\x00\x00-" + - "`\x00\x00\x00\x00*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\f\x00\x00*0\x01\x04\x00\x00\x1c \x00\x10LMT\x00+03\x00+05\x00+04\x00+02\x00\n" + - "<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xd9L\xf6\xf7\xf1\x01\x00\x00\xf1\x01\x00\x00\x10\x00\x1c\x00Europe/StockholmUT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff" + - "\xffT՟\x94\xff\xff\xff\xff|Usb\xff\xff\xff\xff\x9b\x1e\x8c`\xff\xff\xff\xff\x9b\xd5\xda\xf0\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13\xdc" + - "\x90\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00" + - "\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ`\xff\xff\xff\xff\xbb\xcf~" + - "`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff\xff\xbd\xb8\x9a\xe0\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff\xff\xc2hO\xe0\xff\xff\xff" + - "\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1\xe0\xff\xff\xff\xff\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff\xff\xff\xc7\x18\x04\xe0\xff\xff\xff\xffȼ\x93`\xff\xff\xff\xff\xcaw}P\xff\xff\xff\xff\xcc\xe7K" + - "\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0N\x90`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00" + - "\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs" + - "\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00" + - "\x00&\v\xfb\xf0\x00\x00\x00\x00&CL\xe0\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce" + - "`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00" + - "\x003=\xad\x00\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x06\x05\x06\x05\x06\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x03\x04\x03\x00\x00\x1b\b\x00\x00\x00\x00\x1a\xf4\x00\x04\x00\x00\x18x\x00\b\x00\x00*0\x01\f\x00\x00\x1c \x00\x11\x00\x00\x0e\x10\x00\x15\x00\x00\x1c \x01\x19\x00\x008@\x01\x1e\x00\x00*0\x00\"L" + - "MT\x00CMT\x00BMT\x00EEST\x00EET\x00CET\x00CEST\x00MSD\x00MSK\x00\nEET-2EEST,M3.5.0,M10.5" + - ".0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf2\xfa\xcb\x130\x02\x00\x000\x02\x00\x00\x11\x00\x1c\x00Europe/ZaporozhyeUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\b\x00\x00\x00$\xff\xff\xff\xffV" + - "\xb6\xc3\b\xff\xff\xff\xff\xaa\x19\xa30\xff\xff\xff\xff\xb5\xa4\x19`\xff\xff\xff\xffʪ\xe7\xd0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffν\xd6p\x00" + - "\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b" + - "\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00" + - "\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe4\xedP\x00\x00\x00\x00)" + - "\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00" + - "\x00\x00\x001]\xd9\x10\x01\x02\x03\x05\x04\x05\x04\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\a\x02\a\x02\a\x02\a\x02\a\x02\a\x00\x00 \xf8\x00\x00\x00\x00 \xd0\x00\x04\x00\x00" + - "\x1c \x00\n\x00\x00*0\x00\x0e\x00\x00\x0e\x10\x00\x12\x00\x00\x1c \x01\x16\x00\x008@\x01\x1b\x00\x00*0\x01\x1fLMT\x00+0220\x00EET\x00MSK\x00CET\x00CEST" + - "\x00MSD\x00EEST\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe0\xfe\x83\xe5\xcd\x02" + - "\x00\x00\xcd\x02\x00\x00\f\x00\x1c\x00Europe/KirovUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\x009\x80\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x99\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00" + - "\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct" + - "\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<\x1a\xe0\x00\x00\x00\x00$,\v\xe0\x00\x00\x00" + - "\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcd" + - "p\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00" + - "\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&" + - "\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00" + - "\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4" + - "\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x04\x01\x04\x01\x03\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x03\x01\x00\x00.\x98\x00\x00\x00\x00" + - "*0\x00\x04\x00\x00FP\x01\b\x00\x008@\x00\f\x00\x008@\x01\fLMT\x00+03\x00+05\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R>\xfe垛\x03\x00\x00\x9b\x03\x00\x00\r\x00\x1c\x00Europe/WarsawUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZ" + + "\x00\x00\x00\x00\x00\bv\vUDd#\xc4\xf1\x01\x00\x00\xf1\x01\x00\x00\r\x00\x1c\x00Europe/ZurichUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff$\xf0\xea\x80\xff\xff\xff\xffq\xd4\x06\x86\xff\xff\xff" + + "\xff\xca\x17j\x00\xff\xff\xff\xff\xca\xe2q\x00\xff\xff\xff\xff\xcb\xf7L\x00\xff\xff\xff\xff\xcc\xc2S\x00\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe" + + "\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00" + + "\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x863\x80\x00\x00\x00\x00?\x9b\x0e\x80\x00\x00\x00\x00@f\x15\x80\x00\x00\x00\x00A\x84+\x00\x00\x00\x00\x00BE\xf7\x80\x00\x00\x00\x00Cd\r\x00\x00\x00\x00\x00D%\xd9" + + "\x80\x00\x00\x00\x00EC\xef\x00\x00\x00\x00\x00F\x05\xbb\x80\x00\x00\x00\x00G#\xd1\x00\x00\x00\x00\x00G\xee\xd8\x00\x00\x00\x00\x00I\x03\xb3\x00\x00\x00\x00\x00Iκ\x00\x00\x00\x00\x00J\xe3\x95\x00\x00\x00\x00" + + "\x00K\xae\x9c\x00\x00\x00\x00\x00Ḻ\x80\x00\x00\x00\x00M\x8e~\x00\x01\x02\x03\x05\x04\x05\x04\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02" + + "\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\b\x00\x00\x19\xd8\x00\x00\x00\x00\x19\xc8\x00\x04\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10\x00" + + "\x00\x1c \x01\x14\x00\x008@\x01\x19\x00\x00*0\x01\x1d\x00\x00*0\x00\"LMT\x00MMT\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00+03" + + "\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x90\xa9\xf5ϕ\x02\x00\x00\x95\x02\x00\x00\x10\x00\x1c\x00Europe/BucharestUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x04\x00\x00\x00\x11\xff" + + "\xff\xff\xffl\xcf\xe0\b\xff\xff\xff\xff\xb7\xb0\xd2\b\xff\xff\xff\xff\xb9>\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ`\xff\xff\xff\xff\xbb\xcf~`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff\xff\xbd" + + "\xb8\x9a\xe0\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff\xff\xc2hO\xe0\xff\xff\xff\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1\xe0\xff" + + "\xff\xff\xff\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff\xff\xff\xc7\x18\x04\xe0\x00\x00\x00\x00\x11\xad\xd1`\x00\x00\x00\x00\x12S\xe0P\x00\x00\x00\x00\x13M\v\xd0\x00\x00\x00\x00\x143\xd0`\x00\x00\x00\x00\x15" + + "#݀\x00\x00\x00\x00\x16\x13\u0380\x00\x00\x00\x00\x17\x03\xbf\x80\x00\x00\x00\x00\x17\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00\x1b\xbc\xaf\x00\x00" + + "\x00\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LF\x00\x00\x00\x00\x00#" + + "<7\x00\x00\x00\x00\x00$,(\x00\x00\x00\x00\x00%\x1c\x19\x00\x00\x00\x00\x00&\f\n\x00\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5\n`\x00\x00\x00\x00(\xe4\xfb`\x00\x00\x00\x00)\xd4\xec`\x00" + + "\x00\x00\x00*\xc4\xdd`\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xbf`\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001" + + "]\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\xbb\x10\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\x18x\x00\x00\x00\x00\x18x\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\rLMT\x00BMT\x00EEST\x00EET\x00\nEET-" + + "2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU}\xb4N\xb8a\x03\x00\x00a\x03\x00\x00\x11\x00\x1c\x00Europ" + + "e/SimferopolUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00K\x00\x00\x00\t\x00\x00\x00\"\xff\xff\xff\xffV\xb6\xc4\b\xff\xff\xff\xff\xaa\x19\xa4 \xff\xff\xff\xff\xb5\xa4\x19`\xff\xff\xff\xff\xcb\x04\x8d\xd0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xff\xcd" + + "\xa9\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffϟ8\xe0\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00" + + "\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f" + + "|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\x8d.\xf0\x00" + + "\x00\x00\x00)\xd5\b\x80\x00\x00\x00\x00*\xc4\xf9\x80\x00\x00\x00\x00+\xb4\xea\x80\x00\x00\x00\x00,\xa4ۀ\x00\x00\x00\x00-\x94̀\x00\x00\x00\x00-\xc2\xc6\xd0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/" + + "t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xa0\xd0\x00\x00\x00\x002r\xa6\x00\x00\x00\x00\x003=\xbb\x10\x00\x00\x00\x004R\x96\x10\x00\x00\x00\x005\x1d\x9d\x10\x00\x00\x00\x0062x\x10\x00" + + "\x00\x00\x006\xfd\x7f\x10\x00\x00\x00\x008\x1b\x94\x90\x00\x00\x00\x008\xdda\x10\x00\x00\x00\x009\xfbv\x90\x00\x00\x00\x00:\xbdC\x10\x00\x00\x00\x00;\xdbX\x90\x00\x00\x00\x00<\xa6_\x90\x00\x00\x00\x00=" + + "\xbb:\x90\x00\x00\x00\x00>\x86A\x90\x00\x00\x00\x00?\x9b\x1c\x90\x00\x00\x00\x00@f#\x90\x00\x00\x00\x00A\x849\x10\x00\x00\x00\x00BF\x05\x90\x00\x00\x00\x00Cd\x1b\x10\x00\x00\x00\x00D%\xe7\x90\x00" + + "\x00\x00\x00EC\xfd\x10\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K" + + "\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8e\x8c\x10\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00" + + "\x00\x00\x00S7^\x80\x00\x00\x00\x00TL\x1d`\x01\x02\x03\x05\x04\x05\x04\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x02\a\x02\a\x02\a\x06\x03\x06\x03\x06\x03\a\x02\a\x02\a\x02" + + "\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\a\x02\b\x03\x00\x00\x1f\xf8\x00\x00\x00\x00\x1f\xe0\x00\x04\x00\x00\x1c \x00\b\x00\x00*0\x00\f\x00\x00\x0e\x10\x00\x10" + + "\x00\x00\x1c \x01\x14\x00\x008@\x01\x19\x00\x00*0\x01\x1d\x00\x008@\x00\fLMT\x00SMT\x00EET\x00MSK\x00CET\x00CEST\x00MSD\x00EEST\x00\nM" + + "SK-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x1c\x00Europe/BelgradeUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xff^<\xf0" + + "H\xff\xff\xff\xff\xca\x025\xe0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xffѡ\x8c\x10\xff\xff\xff" + + "\xff\xd2N@\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90" + + "\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86\x17`\x00\x00\x00\x00?\x9a\xf2`\x00\x00\x00\x00@e\xf9`\x00\x00\x00\x00A\x84\x0e\xe0\x00\x00\x00\x00" + + "BE\xdb`\x00\x00\x00\x00Cc\xf0\xe0\x00\x00\x00\x00D%\xbd`\x00\x00\x00\x00EC\xd2\xe0\x00\x00\x00\x00F\x05\x9f`\x00\x00\x00\x00G#\xb4\xe0\x00\x00\x00\x00G\xee\xbb\xe0\x00\x00\x00\x00I\x03\x96\xe0" + + "\x00\x00\x00\x00IΝ\xe0\x00\x00\x00\x00J\xe3x\xe0\x00\x00\x00\x00K\xae\x7f\xe0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01" + + "\x04\x01\x05\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x02\x00\x00.\xf4\x00\x00\x00\x00*0\x00\x04\x00\x008@" + + "\x00\b\x00\x00FP\x01\f\x00\x008@\x01\b\x00\x00*0\x01\x04LMT\x00+03\x00+04\x00+05\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x95" + + "\xb4\x9e\xe7\xb3\x03\x00\x00\xb3\x03\x00\x00\x11\x00\x1c\x00Europe/San_MarinoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00W\x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff>(\xe8L\xff\xff\xff\xffp\xbc\x81p\xff\xff\xff\xff\x9b8\xf8p\xff" + + "\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0\x1c\xe0\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2" + + "\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0n^\x90\xff" + + "\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2L\xd2\xf0\xff\xff\xff\xff\xd3>1\x90\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p\xff\xff\xff\xff\xd6)\x97\xf0\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff\xd8" + + "\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c\xd2p\xff\xff\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff\xfc\xfc\xb4p\xff\xff\xff\xff\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0\xff" + + "\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00\x00\x00\x02\x9cZp\x00\x00\x00\x00\x03Bwp\x00\x00\x00\x00\x04\x85v\xf0\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06" + + "n\x93p\x00\x00\x00\x00\a\vu\xf0\x00\x00\x00\x00\bE:\xf0\x00\x00\x00\x00\b\xebW\xf0\x00\x00\x00\x00\n.Wp\x00\x00\x00\x00\n\xcb9\xf0\x00\x00\x00\x00\f\x0e9p\x00\x00\x00\x00\f\xab\x1b\xf0\x00" + + "\x00\x00\x00\r\xe4\xe0\xf0\x00\x00\x00\x00\x0e\x8a\xfd\xf0\x00\x00\x00\x00\x0f\xcd\xfdp\x00\x00\x00\x00\x10t\x1ap\x00\x00\x00\x00\x11\xad\xdfp\x00\x00\x00\x00\x12S\xfcp\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x14" + + "3\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00" + + "\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"" + + "LT\x10\x00\x00\x00\x00#(\xe8L\xff\xff\xff\xff" + + "p\xbc\x81p\xff\xff\xff\xff\x9b8\xf8p\xff\xff\xff\xff\x9b\xd5\xcc\xe0\xff\xff\xff\xff\x9c\xc5\xcb\xf0\xff\xff\xff\xff\x9d\xb7\x00`\xff\xff\xff\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0\x1c\xe0\xff\xff\xff\xff\xa0`\xa5\xf0" + + "\xff\xff\xff\xff\xa1~\xad`\xff\xff\xff\xff\xa2\\7p\xff\xff\xff\xff\xa3L\x1a`\xff\xff\xff\xff\xc8l5\xf0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xff" + + "ϒ4\x10\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2L\xd2\xf0\xff\xff\xff\xff\xd3>1\x90\xff\xff\xff\xff\xd4I\xd2\x10\xff\xff\xff\xff\xd5\x1d\xf7p\xff\xff\xff\xff\xd6)\x97\xf0" + + "\xff\xff\xff\xff\xd6뀐\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xf93\xb5\xf0\xff\xff\xff\xff\xf9\xd9\xc4\xe0\xff\xff\xff\xff\xfb\x1c\xd2p\xff\xff\xff\xff\xfb\xb9\xb4\xf0\xff\xff\xff\xff\xfc\xfc\xb4p\xff\xff\xff\xff" + + "\xfd\x99\x96\xf0\xff\xff\xff\xff\xfe\xe5\xd0\xf0\xff\xff\xff\xff\xff\x82\xb3p\x00\x00\x00\x00\x00Ų\xf0\x00\x00\x00\x00\x01b\x95p\x00\x00\x00\x00\x02\x9cZp\x00\x00\x00\x00\x03Bwp\x00\x00\x00\x00\x04\x85v\xf0" + + "\x00\x00\x00\x00\x05+\x93\xf0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a\vu\xf0\x00\x00\x00\x00\bE:\xf0\x00\x00\x00\x00\b\xebW\xf0\x00\x00\x00\x00\n.Wp\x00\x00\x00\x00\n\xcb9\xf0\x00\x00\x00\x00" + + "\f\x0e9p\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xe4\xe0\xf0\x00\x00\x00\x00\x0e\x8a\xfd\xf0\x00\x00\x00\x00\x0f\xcd\xfdp\x00\x00\x00\x00\x10t\x1ap\x00\x00\x00\x00\x11\xad\xdfp\x00\x00\x00\x00\x12S\xfcp" + "\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00" + "\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10" + "\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b\xefP\xff\xff\xff\xff\xf5h\x06" + - "`\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00\x00\x00\n\xf9^p\x00\x00\x00" + - "\x00\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89\xb0p\x00\x00\x00\x00\x19ܰ" + - "\xe0\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6\xef\xf0\x00\x00\x00\x00\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00" + - "\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\t" + - "p\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00" + - "\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb" + - "\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0" + - "\xf0\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00" + - "\x00L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S8\xbe" + - "\x10\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\r" + - "\x00\x00*0\x00\x11\x00\x008@\x01\x15LMT\x00IMT\x00EEST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\xab\x80c$q\x00\x00\x00q\x00\x00\x00\a\x00\x1c\x00FactoryUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00-00\x00\n<-00>0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4" + - ",\xb6?\x06\x00\x00?\x06\x00\x00\x02\x00\x1c\x00GBUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff" + - "\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5" + - "?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff" + - "\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3" + - "r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff" + - "\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1" + - "x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff" + - "\xff\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0" + - "n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff" + - "\xff\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb" + - "\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff" + - "\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9" + - "\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff" + - "\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\u007f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8" + - "\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00" + - "\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n" + - "\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00" + - "\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18" + - "㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00" + - "\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'" + - "*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00" + - "\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGM" + - "T0BST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\a\x00\x1c\x00GB-Eire" + - "UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00" + - "\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff" + + "\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\v\xb4\x00\x00\x00\x00\v" + + "\xb4\x00\x04\x00\x00\x1c \x01\b\x00\x00\x0e\x10\x00\rLMT\x00RMT\x00CEST\x00CET\x00\nCET-1CEST,M3.5.0,M10.5.0/3\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUI\xb8\xbc\xd3\xf3\x02\x00\x00\xf3\x02\x00\x00\x0f\x00\x1c\x00Europe/ChisinauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\t\x00\x00\x00&\xff\xff\xff\xffV\xb6\xc8\xf8\xff\xff\xff\xff" + + "\x9ek\x9f\f\xff\xff\xff\xff\xb7\xb0\xd2\b\xff\xff\xff\xff\xb9>\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ`\xff\xff\xff\xff\xbb\xcf~`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff\xff\xbd\xb8\x9a\xe0" + + "\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff\xff\xc2hO\xe0\xff\xff\xff\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1\xe0\xff\xff\xff\xff" + + "\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff\xff\xff\xc7\x18\x04\xe0\xff\xff\xff\xffȼ\x93`\xff\xff\xff\xff\xcaw}P\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10" + + "\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0N\x90`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00" + + "\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0" + + "\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00&CL\xe0\x00\x00\x00\x00" + + "'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcfP\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`" + + "\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00\x002r{\xd0\x00\x00\x00\x003=\xad\x00\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x06\x05\x06\x05\x06\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x1b\b\x00\x00\x00\x00\x1a\xf4\x00\x04" + + "\x00\x00\x18x\x00\b\x00\x00*0\x01\f\x00\x00\x1c \x00\x11\x00\x00\x0e\x10\x00\x15\x00\x00\x1c \x01\x19\x00\x008@\x01\x1e\x00\x00*0\x00\"LMT\x00CMT\x00BMT\x00EEST\x00E" + + "ET\x00CET\x00CEST\x00MSD\x00MSK\x00\nEET-2EEST,M3.5.0,M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vUVa\x92\xd3\xdf\x02\x00\x00\xdf\x02\x00\x00\x10\x00\x1c\x00Europe/VolgogradUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\xa1\xf5F\xdc\xff\xff\xff\xff\xb5\xa4\vP\x00\x00\x00\x00\x15'\x99" + + "\xc0\x00\x00\x00\x00\x16\x18\xce0\x00\x00\x00\x00\x17\b\xcd@\x00\x00\x00\x00\x17\xfa\x01\xb0\x00\x00\x00\x00\x18\xea\x00\xc0\x00\x00\x00\x00\x19\xdb50\x00\x00\x00\x00\x1a̅\xc0\x00\x00\x00\x00\x1b\xbc\x92\xe0\x00\x00\x00" + + "\x00\x1c\xac\x83\xe0\x00\x00\x00\x00\x1d\x9ct\xe0\x00\x00\x00\x00\x1e\x8ce\xe0\x00\x00\x00\x00\x1f|V\xe0\x00\x00\x00\x00 lG\xe0\x00\x00\x00\x00!\\8\xe0\x00\x00\x00\x00\"L)\xe0\x00\x00\x00\x00#<(" + + "\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00" + + "\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97" + + "\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00" + + "\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\a" + + "p\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00" + + "\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d" + + "`\x00\x00\x00\x00[\xd4\xed\xf0\x00\x00\x00\x00_\xe7\xb2`\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x01\x04\x01\x04\x01\x02\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01" + + "\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x02\x01\x02\x01\x00\x00)\xa4\x00\x00\x00\x00*0\x00\x04\x00\x008@\x00\b\x00\x00FP\x01\f\x00\x008@\x01\bLMT\x00+03\x00" + + "+04\x00+05\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa1\xd6jL\xd8\x05\x00\x00\xd8\x05\x00\x00\r\x00\x1c\x00Europe/Dublin" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x91\x00\x00\x00\b\x00" + + "\x00\x00\x14\xff\xff\xff\xffW\xd1\n\xf1\xff\xff\xff\xff\x9b&\xb3\x91\xff\xff\xff\xff\x9b\xd6\v\x11\xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff" + "\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7" + "'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff" + "\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5" + "IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff" + "\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3" + - "Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff" + - "\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1" + - "\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff" + - "\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd" + - "\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff" + - "\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb" + - "\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\u007f_ \xff" + - "\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9" + - "\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00" + - "\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f" + - "\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00" + - "\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1a" + - "Ñ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00" + - "\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)" + - "\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00" + - "\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00" + - "\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT0BST,M3.5.0/1,M" + - "10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00GMTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + - "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x1c\x00GMT+0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x1c\x00GMT-0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x04\x00\x1c\x00GMT0UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00" + - "\x00\x00o\x00\x00\x00\t\x00\x1c\x00GreenwichUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RE\t\xfa-\a\x03\x00\x00" + - "\a\x03\x00\x00\b\x00\x1c\x00HongkongUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff\x85ic\x90\xff\xff\xff\xff\xcaM10\xff\xff\xff\xff\xcaۓ0\xff\xff\xff\xff\xcbKqx\xff\xff\xff\xffҠސ\xff" + - "\xff\xff\xff\xd3k׀\xff\xff\xff\xffԓX\xb8\xff\xff\xff\xff\xd5B\xb08\xff\xff\xff\xff\xd6s:\xb8\xff\xff\xff\xff\xd7>A\xb8\xff\xff\xff\xff\xd8.2\xb8\xff\xff\xff\xff\xd8\xf99\xb8\xff\xff\xff\xff\xda" + - "\x0e\x14\xb8\xff\xff\xff\xff\xda\xd9\x1b\xb8\xff\xff\xff\xff\xdb\xed\xf6\xb8\xff\xff\xff\xffܸ\xfd\xb8\xff\xff\xff\xff\xdd\xcdظ\xff\xff\xff\xffޢ\x1a8\xff\xff\xff\xff߶\xf58\xff\xff\xff\xff\xe0\x81\xfc8\xff" + - "\xff\xff\xff\xe1\x96\xc9(\xff\xff\xff\xff\xe2Oi8\xff\xff\xff\xff\xe3v\xab(\xff\xff\xff\xff\xe4/K8\xff\xff\xff\xff\xe5_Ǩ\xff\xff\xff\xff\xe6\x0f-8\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7" + - "\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff" + - "\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6" + - "G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15a(\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf5C(\xff\xff\xff\xff\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff" + - "\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04" + - "M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00" + - "\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x02\x03\x04\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00k\n\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x01\b\x00\x00" + - "w\x88\x01\r\x00\x00~\x90\x00\x12LMT\x00HKT\x00HKST\x00HKWT\x00JST\x00\nHKT-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R=\xf7\xfawp\x00\x00" + - "\x00p\x00\x00\x00\x03\x00\x1c\x00HSTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffs`\x00\x00HST\x00\nHST10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rm\xbd\x10k\xf1\x02\x00\x00\xf1\x02\x00\x00\a\x00" + - "\x1c\x00IcelandUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00D\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x8b`\x83\xa0\xff\xff\xff\xff\x9c\x91\x1e\x00\xff\xff\xff\xff\x9dш\x90\xff\xff\xff\xff\x9erQ\x80\xff\xff\xff\xff\x9f\xd5\x03\x10\xff\xff\xff\xff\xa0S\x85\x00" + - "\xff\xff\xff\xff\xa1\xb66\x90\xff\xff\xff\xff\xa4<'\x80\xff\xff\xff\xff\xa4\xb9t\x10\xff\xff\xff\xff\xc6M\x1a\x00\xff\xff\xff\xff\xc7=' \xff\xff\xff\xff\xc7\xda\x17\xb0\xff\xff\xff\xff\xc9&C\xa0\xff\xff\xff\xff" + - "\xc9\xc3& \xff\xff\xff\xff\xcb\x06%\xa0\xff\xff\xff\xffˬB\xa0\xff\xff\xff\xff\xcc\xdc\xcd \xff\xff\xff\xff͌$\xa0\xff\xff\xff\xffμ\xaf \xff\xff\xff\xff\xcfl\x06\xa0\xff\xff\xff\xffМ\x91 " + - "\xff\xff\xff\xff\xd1K\xe8\xa0\xff\xff\xff\xff҅\xad\xa0\xff\xff\xff\xff\xd3+ʠ\xff\xff\xff\xff\xd4e\x8f\xa0\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd6Eq\xa0\xff\xff\xff\xff\xd7\x19\xb3 \xff\xff\xff\xff" + - "\xd8%S\xa0\xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xd9w \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xffܹY \xff\xff\xff\xff\xdd\xce4 \xff\xff\xff\xffޢu\xa0" + - "\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x82W\xa0\xff\xff\xff\xff\xe1\x8d\xf8 \xff\xff\xff\xff\xe2b9\xa0\xff\xff\xff\xff\xe3m\xda \xff\xff\xff\xff\xe4B\x1b\xa0\xff\xff\xff\xff\xe5M\xbc \xff\xff\xff\xff" + - "\xe6!\xfd\xa0\xff\xff\xff\xff\xe76ؠ\xff\xff\xff\xff\xe8\v\x1a \xff\xff\xff\xff\xe9\x16\xba\xa0\xff\xff\xff\xff\xe9\xea\xfc \xff\xff\xff\xff\xea\xf6\x9c\xa0\xff\xff\xff\xff\xeb\xca\xde \xff\xff\xff\xff\xec\xd6~\xa0" + - "\xff\xff\xff\xff\xed\xaa\xc0 \xff\xff\xff\xff\xee\xb6`\xa0\xff\xff\xff\xff\uf2a2 \xff\xff\xff\xff\xf0\x96B\xa0\xff\xff\xff\xff\xf1j\x84 \xff\xff\xff\xff\xf2\u007f_ \xff\xff\xff\xff\xf3S\xa0\xa0\xff\xff\xff\xff" + - "\xf4_A \xff\xff\xff\xff\xf53\x82\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x13d\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xf3F\xa0\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xd3(\xa0" + - "\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc\xbcE \x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\xff\xff\xeb`\x00\x00\x00\x00\x00\x00\x01\x04\xff\xff\xf1\xf0\x00\b\x00\x00\x00\x00\x00\fLMT\x00+00\x00-01\x00" + - "GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Indian/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00Indian/MayotteU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00" + - "\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00" + - "\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb8K\xabυ\x00\x00\x00" + - "\x85\x00\x00\x00\x10\x00\x1c\x00Indian/KerguelenUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xdaab\x80\x01\x00\x00\x00\x00\x00\x00\x00\x00FP\x00\x04-00\x00+05\x00\n<+0" + - "5>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb9\xb2Z\xac\x98\x00\x00\x00\x98\x00\x00\x00\x0f\x00\x1c\x00Indian/MaldivesUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x9f" + - "\x18\xff\xff\xff\xff\xed/Ø\x01\x02\x00\x00D\xe8\x00\x00\x00\x00D\xe8\x00\x04\x00\x00FP\x00\bLMT\x00MMT\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9Ry(\xb6\x8f\x85\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x1c\x00Indian/ReunionUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x91\xcc9\x80\x01\x00\x004\x00\x00\x00\x00\x008@\x00\x04LM" + - "T\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x96\xed=\x98\xb3\x00\x00\x00\xb3\x00\x00\x00\x10\x00\x1c\x00Indian/Mauritiu" + - "sUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x03" + - "\x00\x00\x00\f\xff\xff\xff\xff\x89\u007f\x05\x98\x00\x00\x00\x00\x18\x05\xed@\x00\x00\x00\x00\x18\xdbr0\x00\x00\x00\x00I\x03\x96\xe0\x00\x00\x00\x00IΏ\xd0\x02\x01\x02\x01\x02\x00\x005\xe8\x00\x00\x00\x00FP\x01" + - "\x04\x00\x008@\x00\bLMT\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x13\x00\x1c\x00In" + - "dian/AntananarivoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4" + - "\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04" + - "\n\x00\x00\x00\x00\x00\xf1c9Ra\x85jo\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Indian/MaheUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x89\u007f\a\x84\x01\x00\x003\xfc\x00\x00\x00\x008@\x00" + - "\x04LMT\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x1c\x00Indian/Comor" + - "oUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04" + - "\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00" + - "\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R$l=҅\x00" + - "\x00\x00\x85\x00\x00\x00\x10\x00\x1c\x00Indian/ChristmasUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xffs\x16\xa9\xe4\x01\x00\x00c\x1c\x00\x00\x00\x00bp\x00\x04LMT\x00+07\x00\n<" + - "+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rx\xb0W\x14\x98\x00\x00\x00\x98\x00\x00\x00\r\x00\x1c\x00Indian/ChagosUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x89~\xf7" + - "\x9c\x00\x00\x00\x000\xe6ݰ\x01\x02\x00\x00C\xe4\x00\x00\x00\x00FP\x00\x04\x00\x00T`\x00\bLMT\x00+05\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9RͲ\xfb\xf6\x8c\x00\x00\x00\x8c\x00\x00\x00\f\x00\x1c\x00Indian/CocosUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\n\xff\xff\xff\xff|U&\xa4\x01\x00\x00Z\xdc\x00\x00\x00\x00[h\x00\x04LMT\x00" + - "+0630\x00\n<+0630>-6:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R;\u007fP\x8d\xd4\a\x00\x00\xd4\a\x00\x00\x04\x00\x1c\x00IranUT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc9\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff" + - "\xff\x9al}\xc8\xff\xff\xff\xff\xd2\xdb\x12\xc8\x00\x00\x00\x00\x0e\xbb\xa2H\x00\x00\x00\x00\x0ft-@\x00\x00\x00\x00\x10\x8e@0\x00\x00\x00\x00\x10\xed:@\x00\x00\x00\x00\x11Ug\xc8\x00\x00\x00\x00\x12EJ" + - "\xb8\x00\x00\x00\x00\x137\xec\xc8\x00\x00\x00\x00\x14-\x15\xb8\x00\x00\x00\x00( v\xc8\x00\x00\x00\x00(\u06dd\xb8\x00\x00\x00\x00)˜\xc8\x00\x00\x00\x00*\xbe\"\xb8\x00\x00\x00\x00+\xac\xd0H\x00\x00\x00" + - "\x00,\x9fV8\x00\x00\x00\x00-\x8e\x03\xc8\x00\x00\x00\x00.\x80\x89\xb8\x00\x00\x00\x00/o7H\x00\x00\x00\x000a\xbd8\x00\x00\x00\x001Pj\xc8\x00\x00\x00\x002B\xf0\xb8\x00\x00\x00\x0032\xef" + - "\xc8\x00\x00\x00\x004%u\xb8\x00\x00\x00\x005\x14#H\x00\x00\x00\x006\x06\xa98\x00\x00\x00\x006\xf5V\xc8\x00\x00\x00\x007\xe7ܸ\x00\x00\x00\x008֊H\x00\x00\x00\x009\xc9\x108\x00\x00\x00" + - "\x00:\xb9\x0fH\x00\x00\x00\x00;\xab\x958\x00\x00\x00\x00<\x9aB\xc8\x00\x00\x00\x00=\x8cȸ\x00\x00\x00\x00>{vH\x00\x00\x00\x00?m\xfc8\x00\x00\x00\x00@\\\xa9\xc8\x00\x00\x00\x00AO/" + - "\xb8\x00\x00\x00\x00B?.\xc8\x00\x00\x00\x00C1\xb4\xb8\x00\x00\x00\x00G\xe2\xc9H\x00\x00\x00\x00H\xd5O8\x00\x00\x00\x00I\xc5NH\x00\x00\x00\x00J\xb7\xd48\x00\x00\x00\x00K\xa6\x81\xc8\x00\x00\x00" + - "\x00L\x99\a\xb8\x00\x00\x00\x00M\x87\xb5H\x00\x00\x00\x00Nz;8\x00\x00\x00\x00Oh\xe8\xc8\x00\x00\x00\x00P[n\xb8\x00\x00\x00\x00QKm\xc8\x00\x00\x00\x00R=\xf3\xb8\x00\x00\x00\x00S,\xa1" + - "H\x00\x00\x00\x00T\x1f'8\x00\x00\x00\x00U\r\xd4\xc8\x00\x00\x00\x00V\x00Z\xb8\x00\x00\x00\x00V\xef\bH\x00\x00\x00\x00W\xe1\x8e8\x00\x00\x00\x00XэH\x00\x00\x00\x00Y\xc4\x138\x00\x00\x00" + - "\x00Z\xb2\xc0\xc8\x00\x00\x00\x00[\xa5F\xb8\x00\x00\x00\x00\\\x93\xf4H\x00\x00\x00\x00]\x86z8\x00\x00\x00\x00^u'\xc8\x00\x00\x00\x00_g\xad\xb8\x00\x00\x00\x00`W\xac\xc8\x00\x00\x00\x00aJ2" + - "\xb8\x00\x00\x00\x00b8\xe0H\x00\x00\x00\x00c+f8\x00\x00\x00\x00d\x1a\x13\xc8\x00\x00\x00\x00e\f\x99\xb8\x00\x00\x00\x00e\xfbGH\x00\x00\x00\x00f\xed\xcd8\x00\x00\x00\x00g\xdd\xccH\x00\x00\x00" + - "\x00h\xd0R8\x00\x00\x00\x00i\xbe\xff\xc8\x00\x00\x00\x00j\xb1\x85\xb8\x00\x00\x00\x00k\xa03H\x00\x00\x00\x00l\x92\xb98\x00\x00\x00\x00m\x81f\xc8\x00\x00\x00\x00ns\xec\xb8\x00\x00\x00\x00ob\x9a" + - "H\x00\x00\x00\x00pU 8\x00\x00\x00\x00qE\x1fH\x00\x00\x00\x00r7\xa58\x00\x00\x00\x00s&R\xc8\x00\x00\x00\x00t\x18ظ\x00\x00\x00\x00u\a\x86H\x00\x00\x00\x00u\xfa\f8\x00\x00\x00" + - "\x00v\xe8\xb9\xc8\x00\x00\x00\x00w\xdb?\xb8\x00\x00\x00\x00x\xcb>\xc8\x00\x00\x00\x00y\xbdĸ\x00\x00\x00\x00z\xacrH\x00\x00\x00\x00{\x9e\xf88\x00\x00\x00\x00|\x8d\xa5\xc8\x00\x00\x00\x00}\x80+" + - "\xb8\x00\x00\x00\x00~n\xd9H\x00\x00\x00\x00\u007fa_8\x00\x00\x00\x00\x80Q^H\x00\x00\x00\x00\x81C\xe48\x00\x00\x00\x00\x822\x91\xc8\x00\x00\x00\x00\x83%\x17\xb8\x00\x00\x00\x00\x84\x13\xc5H\x00\x00\x00" + - "\x00\x85\x06K8\x00\x00\x00\x00\x85\xf4\xf8\xc8\x00\x00\x00\x00\x86\xe7~\xb8\x00\x00\x00\x00\x87\xd7}\xc8\x00\x00\x00\x00\x88\xca\x03\xb8\x00\x00\x00\x00\x89\xb8\xb1H\x00\x00\x00\x00\x8a\xab78\x00\x00\x00\x00\x8b\x99\xe4" + - "\xc8\x00\x00\x00\x00\x8c\x8cj\xb8\x00\x00\x00\x00\x8d{\x18H\x00\x00\x00\x00\x8em\x9e8\x00\x00\x00\x00\x8f]\x9dH\x00\x00\x00\x00\x90P#8\x00\x00\x00\x00\x91>\xd0\xc8\x00\x00\x00\x00\x921V\xb8\x00\x00\x00" + - "\x00\x93 \x04H\x00\x00\x00\x00\x94\x12\x8a8\x00\x00\x00\x00\x95\x017\xc8\x00\x00\x00\x00\x95\xf3\xbd\xb8\x00\x00\x00\x00\x96\xe3\xbc\xc8\x00\x00\x00\x00\x97\xd6B\xb8\x00\x00\x00\x00\x98\xc4\xf0H\x00\x00\x00\x00\x99\xb7v" + - "8\x00\x00\x00\x00\x9a\xa6#\xc8\x00\x00\x00\x00\x9b\x98\xa9\xb8\x00\x00\x00\x00\x9c\x87WH\x00\x00\x00\x00\x9dy\xdd8\x00\x00\x00\x00\x9ei\xdcH\x00\x00\x00\x00\x9f\\b8\x00\x00\x00\x00\xa0K\x0f\xc8\x00\x00\x00" + - "\x00\xa1=\x95\xb8\x00\x00\x00\x00\xa2,CH\x00\x00\x00\x00\xa3\x1e\xc98\x00\x00\x00\x00\xa4\rv\xc8\x00\x00\x00\x00\xa4\xff\xfc\xb8\x00\x00\x00\x00\xa5\xef\xfb\xc8\x00\x00\x00\x00\xa6⁸\x00\x00\x00\x00\xa7\xd1/" + - "H\x00\x00\x00\x00\xa8õ8\x00\x00\x00\x00\xa9\xb2b\xc8\x00\x00\x00\x00\xaa\xa4\xe8\xb8\x00\x00\x00\x00\xab\x93\x96H\x00\x00\x00\x00\xac\x86\x1c8\x00\x00\x00\x00\xadt\xc9\xc8\x00\x00\x00\x00\xaegO\xb8\x00\x00\x00" + - "\x00\xafWN\xc8\x00\x00\x00\x00\xb0IԸ\x00\x00\x00\x00\xb18\x82H\x00\x00\x00\x00\xb2+\b8\x00\x00\x00\x00\xb3\x19\xb5\xc8\x00\x00\x00\x00\xb4\f;\xb8\x00\x00\x00\x00\xb4\xfa\xe9H\x00\x00\x00\x00\xb5\xedo" + - "8\x00\x00\x00\x00\xb6\xddnH\x00\x00\x00\x00\xb7\xcf\xf48\x00\x00\x00\x00\xb8\xbe\xa1\xc8\x00\x00\x00\x00\xb9\xb1'\xb8\x00\x00\x00\x00\xba\x9f\xd5H\x00\x00\x00\x00\xbb\x92[8\x00\x00\x00\x00\xbc\x81\b\xc8\x00\x00\x00" + - "\x00\xbds\x8e\xb8\x00\x00\x00\x00\xbec\x8d\xc8\x00\x00\x00\x00\xbfV\x13\xb8\x00\x00\x00\x00\xc0D\xc1H\x00\x00\x00\x00\xc17G8\x00\x00\x00\x00\xc2%\xf4\xc8\x00\x00\x00\x00\xc3\x18z\xb8\x00\x00\x00\x00\xc4\a(" + - "H\x00\x00\x00\x00\xc4\xf9\xae8\x00\x00\x00\x00\xc5\xe9\xadH\x00\x00\x00\x00\xc6\xdc38\x00\x00\x00\x00\xc7\xca\xe0\xc8\x00\x00\x00\x00Ƚf\xb8\x00\x00\x00\x00ɬ\x14H\x00\x00\x00\x00ʞ\x9a8\x00\x00\x00" + - "\x00ˍG\xc8\x00\x00\x00\x00\xcc\u007f\u0378\x00\x00\x00\x00\xcdo\xcc\xc8\x00\x00\x00\x00\xcebR\xb8\x00\x00\x00\x00\xcfQ\x00H\x00\x00\x00\x00\xd0C\x868\x00\x00\x00\x00\xd123\xc8\x00\x00\x00\x00\xd2$\xb9" + - "\xb8\x00\x00\x00\x00\xd3\x13gH\x00\x00\x00\x00\xd4\x05\xed8\x00\x00\x00\x00\xd4\xf5\xecH\x00\x00\x00\x00\xd5\xe8r8\x00\x00\x00\x00\xd6\xd7\x1f\xc8\x00\x00\x00\x00\xd7ɥ\xb8\x00\x00\x00\x00ظSH\x00\x00\x00" + - "\x00٪\xd98\x00\x00\x00\x00ڙ\x86\xc8\x00\x00\x00\x00ی\f\xb8\x00\x00\x00\x00\xdc|\v\xc8\x00\x00\x00\x00\xddn\x91\xb8\x00\x00\x00\x00\xde]?H\x01\x02\x04\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x00\x0008\x00\x00\x00\x0008\x00\x04\x00\x0018\x00\b\x00\x00FP\x01\x0e\x00\x008@\x00\x12\x00\x00?H\x01\x16LMT\x00TMT\x00+0330\x00+05\x00" + - "+04\x00+0430\x00\n<+0330>-3:30<+0430>,J79/24,J263/24\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\x17✳2\x04\x00\x002\x04\x00\x00\x06\x00\x1c\x00IsraelUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff" + - "\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ\xd7" + - "\x80\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff\xff\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10\xf7\x00\xff\xff\xff" + - "\xff\xda\xeb\xd0\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d\x00\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff\xff\xff\xe1V}" + - "\x00\xff\xff\xff\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff\xff\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff\xe7\x11Ҁ\xff\xff\xff\xff\xe8&\xad\x80\xff\xff\xff" + - "\xff\xe8\xe8z\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6" + - "`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00" + - "\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03" + - "P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00,\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00" + - "\x000H\xc5\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4`\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp\x00\x00\x00\x00D,q\x00\x00\x00\x00\x00E\x1e\xf6" + - "\xf0\x00\x00\x00\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00\x00H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00J\xbe\x9c\xf0\x00\x00\x00\x00K\xab\xf9\x00\x00\x00\x00" + - "\x00L\x8c\t\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7\x80\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!\x06\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\f\x00\x008@\x01\x10LMT\x00J" + - "MT\x00IDT\x00IST\x00IDDT\x00\nIST-2IDT,M3.4.4/26,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R%J" + - "\xd5\xebS\x01\x00\x00S\x01\x00\x00\a\x00\x1c\x00JamaicaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi\x87#~\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00" + - "\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0" + - "\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00" + - "\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xb8\x02\x00\x00\xff\xff\xb8\x02\x00\x04\xff\xff\xb9\xb0\x00\b" + - "\xff\xff\xc7\xc0\x01\fLMT\x00KMT\x00EST\x00EDT\x00\nEST5\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x02\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\x05\x00\x1c\x00Ja" + - "panUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00" + - "\x00\x03\x00\x00\x00\f\xff\xff\xff\xffe¤p\xff\xff\xff\xff\xd7>\x02p\xff\xff\xff\xff\xd7\xedY\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd;\xf0\xff\xff\xff\xff\xdb\a\x00\xf0\xff\xff\xff\xffۭ" + - "\x1d\xf0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xff\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x83\x03\x00\x00\x00\x00\x8c\xa0\x01\x04\x00\x00~\x90\x00\bLMT\x00JDT\x00JST\x00\nJS" + - "T-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\t\x00\x1c\x00KwajaleinUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff~6\x18 \xff\xff\xff\xff\xc1\xed" + - "5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xcfF\x81\xf0\xff\xff\xff\xff\xff\x86\x1bP\x00\x00\x00\x00,v\x0e@\x01\x02\x03\x01\x04\x05\x00\x00\x9c\xe0\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00\x8c\xa0\x00\b\x00\x00" + - "~\x90\x00\f\xff\xffW@\x00\x10\x00\x00\xa8\xc0\x00\x14LMT\x00+11\x00+10\x00+09\x00-12\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R_\u007f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x05\x00\x1c\x00LibyaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x04\x00\x00\x00\x11\xff\xff\xff\xff\xa1\xf2\xc1$\xff\xff\xff\xffݻ\xb1\x10\xff\xff\xff\xff\xde#\xad`\xff\xff\xff\xff\xe1x\xd2\x10" + - "\xff\xff\xff\xff\xe1\xe7e\xe0\xff\xff\xff\xff\xe5/?p\xff\xff\xff\xff\xe5\xa9\xcc\xe0\xff\xff\xff\xff\xebN\xc6\xf0\x00\x00\x00\x00\x16\x92B`\x00\x00\x00\x00\x17\b\xf7p\x00\x00\x00\x00\x17\xfa+\xe0\x00\x00\x00\x00" + - "\x18\xea*\xf0\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0\x00\x00\x00\x00\x1b\xbd\xe4`\x00\x00\x00\x00\x1c\xb4z\xf0\x00\x00\x00\x00\x1d\x9f\x17\xe0\x00\x00\x00\x00\x1e\x93\vp\x00\x00\x00\x00\x1f\x82\xee`" + - "\x00\x00\x00\x00 pJp\x00\x00\x00\x00!a~\xe0\x00\x00\x00\x00\"R\xcfp\x00\x00\x00\x00#D\x03\xe0\x00\x00\x00\x00$4\x02\xf0\x00\x00\x00\x00%%7`\x00\x00\x00\x00&@\xb7\xf0\x00\x00\x00\x00" + - "2N\xf1`\x00\x00\x00\x003D6p\x00\x00\x00\x0045j\xe0\x00\x00\x00\x00P\x9d\x99\x00\x00\x00\x00\x00QTـ\x00\x00\x00\x00Ri\xb4\x80\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x03\x02\x01\x03\x00\x00\f\\\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00\x1c \x00\rLMT\x00CEST\x00CET\x00EET\x00\nEE" + - "T-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xfe\x9d\x1b\xc9m\x02\x00\x00m\x02\x00\x00\x03\x00\x1c\x00METUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x02\x00\x00\x00\t\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff" + - "\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10" + - "\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00" + + "Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff" + + "\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd7,( \xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc" + + "\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff" + + "\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea" + + "\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff" + + "\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8" + + "\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00" + + "\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v" + + "\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00" + + "\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19" + + "\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00" + + "\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'" + + "\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00" + + "\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\a\x06\a" + + "\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\x06\a\xff\xff\xfa\x0f\x00\x00\xff\xff\xfa\x0f\x00\x04" + + "\x00\x00\b\x1f\x01\b\x00\x00\x0e\x10\x01\f\x00\x00\x00\x00\x00\x10\x00\x00\x0e\x10\x01\b\x00\x00\x00\x00\x01\x10\x00\x00\x0e\x10\x00\bLMT\x00DMT\x00IST\x00BST\x00GMT\x00\nIST" + + "-1GMT0,M10.5.0,M3.5.0/1\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc7\xf5\x94\xdaQ\x04\x00\x00Q\x04\x00\x00\f\x00\x1c\x00Europe" + + "/ParisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "e\x00\x00\x00\a\x00\x00\x00\x1f\xff\xff\xff\xffkɛ\xcf\xff\xff\xff\xff\x91`PO\xff\xff\xff\xff\x9bGx\xf0\xff\xff\xff\xff\x9b\xd7,p\xff\xff\xff\xff\x9c\xbc\x91p\xff\xff\xff\xff\x9d\xc0H\xf0\xff\xff\xff" + + "\xff\x9e\x89\xfep\xff\xff\xff\xff\x9f\xa0*\xf0\xff\xff\xff\xff\xa0`\xa5\xf0\xff\xff\xff\xff\xa1\x80\f\xf0\xff\xff\xff\xff\xa2.\x12\xf0\xff\xff\xff\xff\xa3zL\xf0\xff\xff\xff\xff\xa45\x81\xf0\xff\xff\xff\xff\xa5^#" + + "p\xff\xff\xff\xff\xa6%5\xf0\xff\xff\xff\xff\xa7'\x9b\xf0\xff\xff\xff\xff\xa8X&p\xff\xff\xff\xff\xa9\a}\xf0\xff\xff\xff\xff\xa9\xee4p\xff\xff\xff\xff\xaa\xe7_\xf0\xff\xff\xff\xff\xab\xd7P\xf0\xff\xff\xff" + + "\xff\xac\xc7A\xf0\xff\xff\xff\xff\xadɧ\xf0\xff\xff\xff\xff\xae\xa7#\xf0\xff\xff\xff\xff\xaf\xa0Op\xff\xff\xff\xff\xb0\x87\x05\xf0\xff\xff\xff\xff\xb1\x89k\xf0\xff\xff\xff\xff\xb2p\"p\xff\xff\xff\xff\xb3r\x88" + + "p\xff\xff\xff\xff\xb4P\x04p\xff\xff\xff\xff\xb5I/\xf0\xff\xff\xff\xff\xb6/\xe6p\xff\xff\xff\xff\xb72Lp\xff\xff\xff\xff\xb8\x0f\xc8p\xff\xff\xff\xff\xb8\xff\xb9p\xff\xff\xff\xff\xb9\xef\xaap\xff\xff\xff" + + "\xff\xba\xd6`\xf0\xff\xff\xff\xff\xbb\xd8\xc6\xf0\xff\xff\xff\xff\xbcȷ\xf0\xff\xff\xff\xff\xbd\xb8\xa8\xf0\xff\xff\xff\xff\xbe\x9f_p\xff\xff\xff\xff\xbf\x98\x8a\xf0\xff\xff\xff\xff\xc0\x9a\xf0\xf0\xff\xff\xff\xff\xc1xl" + + "\xf0\xff\xff\xff\xff\xc2h]\xf0\xff\xff\xff\xff\xc3XN\xf0\xff\xff\xff\xff\xc4?\x05p\xff\xff\xff\xff\xc580\xf0\xff\xff\xff\xff\xc6:\x96\xf0\xff\xff\xff\xff\xc7X\xacp\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff" + + "\xff\xc8l'\xe0\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0O\xe1\xe0\xff\xff\xff\xffЉ\xf1\xf0\xff\xff\xff\xff\xd1r\x16" + + "\x10\xff\xff\xff\xff\xd2N@\x90\x00\x00\x00\x00\v\xbb9\x00\x00\x00\x00\x00\f\xab\x1b\xf0\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00" + + "\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe" + + "\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00" + + "\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf3`\xff\xff\xff\xff\xb9\xef\x9c`\xff\xff\xff\xff\xbaߍ" + + "`\xff\xff\xff\xff\xbb\xcf~`\xff\xff\xff\xff\xbcȩ\xe0\xff\xff\xff\xff\xbd\xb8\x9a\xe0\xff\xff\xff\xff\xbe\xa8\x8b\xe0\xff\xff\xff\xff\xbf\x98|\xe0\xff\xff\xff\xff\xc0\x88m\xe0\xff\xff\xff\xff\xc1x^\xe0\xff\xff\xff" + + "\xff\xc2hO\xe0\xff\xff\xff\xff\xc3X@\xe0\xff\xff\xff\xff\xc4H1\xe0\xff\xff\xff\xff\xc58\"\xe0\xff\xff\xff\xff\xc6(\x13\xe0\xff\xff\xff\xff\xc7\x18\x04\xe0\xff\xff\xff\xffȼ\x93`\xff\xff\xff\xff\xcaw}" + + "P\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xff\xd0N\x90`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00" + + "\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82" + + "\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00" + + "\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00&CL\xe0\x00\x00\x00\x00'\x055\x80\x00\x00\x00\x00'\xf5&\x80\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xc4\xcf" + + "P\x00\x00\x00\x00+\xb4\xce`\x00\x00\x00\x00,\xa4\xb1P\x00\x00\x00\x00-\x94\xb0`\x00\x00\x00\x00.\x84\x93P\x00\x00\x00\x00/t\x92`\x00\x00\x00\x000duP\x00\x00\x00\x001]\xae\xe0\x00\x00\x00" + + "\x002r{\xd0\x00\x00\x00\x003=\xad\x00\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x06\x05\x06\x05\x06\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\b\a\x03\x04" + + "\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\x1b\b\x00\x00\x00\x00\x1a\xf4\x00\x04\x00\x00\x18x\x00\b\x00\x00*0\x01\f\x00\x00\x1c \x00\x11\x00\x00\x0e\x10\x00\x15\x00\x00\x1c \x01\x19\x00\x008@\x01" + + "\x1e\x00\x00*0\x00\"LMT\x00CMT\x00BMT\x00EEST\x00EET\x00CET\x00CEST\x00MSD\x00MSK\x00\nEET-2EEST,M3.5" + + ".0,M10.5.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\v\x00\x1c\x00Europe/OsloUT\t\x00\x03\xaf" + + "\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff" + + "\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq" + + "\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff\xff" + + "\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)\xb4" + + "\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00" + + "\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae" + + "\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x86A\x90\x01\x02\x03\x04\x03\x05\x06\x03\x06\x03\x06\x05\a\x05\a\x05\a\x05" + + "\a\x05\a\x05\a\x05\a\x05\a\x05\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\b\x04\x06\x03\x06\x04\b\x00\x00\x17\xbc\x00\x00\x00\x00\x13\xb0\x00\x04\x00\x00\x16h\x00\b\x00\x00\x0e\x10\x00\f\x00\x00\x1c" + + " \x00\x10\x00\x00*0\x00\x14\x00\x00\x1c \x01\x18\x00\x008@\x01\x1d\x00\x00*0\x01!LMT\x00WMT\x00KMT\x00CET\x00EET\x00MSK\x00CEST\x00MSD\x00" + + "EEST\x00\nEET-2EEST,M3.5.0/3,M10.5.0/4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xcb*j\x8f\xaa\x02\x00\x00\xaa\x02\x00" + + "\x00\r\x00\x1c\x00Europe/AthensUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xfft?\x98D\xff\xff\xff\xff\x9b\x80!\x80\xff\xff\xff\xff\xb9|\xe9\xe0\xff\xff\xff\xff\xb9Ư\xd0\xff\xff\xff\xff\xc9\xf2c" + + "\xe0\xff\xff\xff\xff\xca\x10\xa8P\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͪL\xf0\xff\xff\xff\xff\u03a2\x18\xe0\xff\xff\xff\xffϓip\xff\xff\xff\xff\xdf\x13\x9e`\xff\xff\xff\xff߷\nP\x00\x00\x00" + + "\x00\t\xec^`\x00\x00\x00\x00\v\x18\xf4`\x00\x00\x00\x00\vͮ\x00\x00\x00\x00\x00\f\xbd\x9f\x00\x00\x00\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\x8c]\x80\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10j\xfc" + + "\x10\x00\x00\x00\x00\x11d{\xf0\x00\x00\x00\x00\x12R\xaa\xf0\x00\x00\x00\x00\x13F\x82`\x00\x00\x00\x00\x143\xc2P\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00" + + "\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90" + + "\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x02\x00\x1c\x00GBUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff" + + "\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c " + + "\xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff" + + "\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 " + + "\xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff" + + "\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0" + + "\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b \xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff" + + "\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff\xc7\xda\t\xa0\xff\xff\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90" + + "\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff" + + "\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff\xd5B\xfd\x90\xff\xff\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 " + + "\xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec \xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff" + + "\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) " + + "\xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v \xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff" + + "\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0" + + "\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff" + + "\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c " + + "\x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe \x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00" + + "\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ" + + "\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00" + + "\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10" + + "\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00" + + "+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00" + + "\x04LMT\x00BST\x00GMT\x00BDST\x00\nGMT0BST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUk\xa4" + + ",\xb6?\x06\x00\x00?\x06\x00\x00\a\x00\x1c\x00GB-EireUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\xa5\xb6\xf6\x80\xff\xff\xff\xff\xa9yOp\xff\xff\xff\xff\xaf\xf2|\xf0\xff\xff\xff\xff\xb6fdp\xff\xff\xff\xff" + - "\xb7\x1b\x10\x00\xff\xff\xff\xff\xb8\n\xf2\xf0\xff\xff\xff\xff\xcbꍀ\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xffҙ\xbap\xff\xff\xff\xff\xd7\x1bY\x00\xff\xff\xff\xffؑ\xb4\xf0\xff\xff\xff\xff\xe2~K\x90" + - "\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff" + - "\xea\a\x0e\x10\xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10" + - "\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00" + - "\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0" + - "\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00" + - "\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10" + - "\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x00" + - "0\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 " + - "\x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00" + - ">\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90" + - "\x00\x00\x00\x00F\x0f\x82\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02" + - "\x03\x02\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10" + - "\xff\xff\x9d\x90\x01\x14LMT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00" + - "\x00\x00\x00\x00\xf1c9R8\xcdZ\x05o\x01\x00\x00o\x01\x00\x00\x0e\x00\x1c\x00Mexico/BajaSurUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff" + - "\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff\xcb\xeaq`\xff\xff\xff\xffؑ\xb4\xf0\x00\x00\x00\x00\x00\x00p\x80\x00\x00\x00\x001g\x84" + - "\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00" + - "\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00<\xb0\n\x90\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff" + - "\xff\x9c<\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10LMT\x00MST\x00CST\x00PST\x00MDT\x00\nMST7MDT,M4" + - ".1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf5\x8d\x99\x92o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00MSTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00" + - "\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x9d\x90\x00\x00MST\x00\n" + - "MST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe6h\xcac\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00MST7MDTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a" + - "\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff" + - "\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a" + - "\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00" + - "\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82" + - "\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00" + - "\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1\xcd" + - "\x80\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00" + - "\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85" + - "\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00" + - "\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ" + - "\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00" + - "\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00" + - "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\x9d" + - "\x90\x00\x04\xff\xff\xab\xa0\x01\x00\xff\xff\xab\xa0\x01\b\xff\xff\xab\xa0\x01\fMDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x06\x00\x1c\x00NavajoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff" + - "\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90" + - "\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff" + - "\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10" + - "\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00" + - "\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00" + - "\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00" + - "\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90" + - "\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00" + - "&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80" + - "\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x00" + - "4R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10" + - "\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00" + - "BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\x00\x00\x00\x05\x00\x00\x00\x11\xff\xff\xff\xff\x1a]\t\xcb\xff\xff\xff\xff\x9b&\xad\xa0\xff\xff\xff\xff\x9b\xd6\x05 \xff\xff\xff\xff\x9c\xcf0\xa0\xff\xff\xff\xff" + + "\x9d\xa4à\xff\xff\xff\xff\x9e\x9c\x9d\xa0\xff\xff\xff\xff\x9f\x97\x1a\xa0\xff\xff\xff\xff\xa0\x85\xba \xff\xff\xff\xff\xa1v\xfc\xa0\xff\xff\xff\xff\xa2e\x9c \xff\xff\xff\xff\xa3{Ƞ\xff\xff\xff\xff\xa4N\xb8\xa0" + + "\xff\xff\xff\xff\xa5?\xfb \xff\xff\xff\xff\xa6%` \xff\xff\xff\xff\xa7'\xc6 \xff\xff\xff\xff\xa8*, \xff\xff\xff\xff\xa8\xeb\xf8\xa0\xff\xff\xff\xff\xaa\x00Ӡ\xff\xff\xff\xff\xaa\xd5\x15 \xff\xff\xff\xff" + + "\xab\xe9\xf0 \xff\xff\xff\xff\xac\xc7l \xff\xff\xff\xff\xad\xc9\xd2 \xff\xff\xff\xff\xae\xa7N \xff\xff\xff\xff\xaf\xa0y\xa0\xff\xff\xff\xff\xb0\x870 \xff\xff\xff\xff\xb1\x92Р\xff\xff\xff\xff\xb2pL\xa0" + + "\xff\xff\xff\xff\xb3r\xb2\xa0\xff\xff\xff\xff\xb4P.\xa0\xff\xff\xff\xff\xb5IZ \xff\xff\xff\xff\xb60\x10\xa0\xff\xff\xff\xff\xb72v\xa0\xff\xff\xff\xff\xb8\x0f\xf2\xa0\xff\xff\xff\xff\xb9\x12X\xa0\xff\xff\xff\xff" + + "\xb9\xefԠ\xff\xff\xff\xff\xba\xe9\x00 \xff\xff\xff\xff\xbb\xd8\xf1 \xff\xff\xff\xff\xbc\xdbW \xff\xff\xff\xff\xbd\xb8\xd3 \xff\xff\xff\xff\xbe\xb1\xfe\xa0\xff\xff\xff\xff\xbf\x98\xb5 \xff\xff\xff\xff\xc0\x9b\x1b " + + "\xff\xff\xff\xff\xc1x\x97 \xff\xff\xff\xff\xc2z\xfd \xff\xff\xff\xff\xc3Xy \xff\xff\xff\xff\xc4Q\xa4\xa0\xff\xff\xff\xff\xc58[ \xff\xff\xff\xff\xc6:\xc1 \xff\xff\xff\xff\xc7X֠\xff\xff\xff\xff" + + "\xc7\xda\t\xa0\xff\xff\xff\xff\xca\x16&\x90\xff\xff\xff\xffʗY\x90\xff\xff\xff\xff\xcb\xd1\x1e\x90\xff\xff\xff\xff\xccw;\x90\xff\xff\xff\xffͱ\x00\x90\xff\xff\xff\xff\xce`X\x10\xff\xff\xff\xffϐ\xe2\x90" + + "\xff\xff\xff\xff\xd0n^\x90\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd1\xfb2\x10\xff\xff\xff\xff\xd2i\xfe \xff\xff\xff\xff\xd3c)\xa0\xff\xff\xff\xff\xd4I\xe0 \xff\xff\xff\xff\xd5\x1e!\xa0\xff\xff\xff\xff" + + "\xd5B\xfd\x90\xff\xff\xff\xff\xd5\xdf\xe0\x10\xff\xff\xff\xff\xd6N\xac \xff\xff\xff\xff\xd6\xfe\x03\xa0\xff\xff\xff\xff\xd8.\x8e \xff\xff\xff\xff\xd8\xf9\x95 \xff\xff\xff\xff\xda\x0ep \xff\xff\xff\xff\xda\xeb\xec " + + "\xff\xff\xff\xff\xdb\xe5\x17\xa0\xff\xff\xff\xff\xdc\xcb\xce \xff\xff\xff\xff\xdd\xc4\xf9\xa0\xff\xff\xff\xff\u07b4\xea\xa0\xff\xff\xff\xff߮\x16 \xff\xff\xff\xff\xe0\x94̠\xff\xff\xff\xff\xe1rH\xa0\xff\xff\xff\xff" + + "\xe2kt \xff\xff\xff\xff\xe3R*\xa0\xff\xff\xff\xff\xe4T\x90\xa0\xff\xff\xff\xff\xe52\f\xa0\xff\xff\xff\xff\xe6=\xad \xff\xff\xff\xff\xe7\x1b) \xff\xff\xff\xff\xe8\x14T\xa0\xff\xff\xff\xff\xe8\xfb\v " + + "\xff\xff\xff\xff\xe9\xfdq \xff\xff\xff\xff\xea\xda\xed \xff\xff\xff\xff\xeb\xddS \xff\xff\xff\xff\xec\xba\xcf \xff\xff\xff\xff\xed\xb3\xfa\xa0\xff\xff\xff\xff\ue6b1 \xff\xff\xff\xff\xef\x81g\xa0\xff\xff\xff\xff" + + "\xf0\x9f} \xff\xff\xff\xff\xf1aI\xa0\xff\xff\xff\xff\xf2\x7f_ \xff\xff\xff\xff\xf3Jf \xff\xff\xff\xff\xf4_A \xff\xff\xff\xff\xf5!\r\xa0\xff\xff\xff\xff\xf6?# \xff\xff\xff\xff\xf7\x00\xef\xa0" + + "\xff\xff\xff\xff\xf8\x1f\x05 \xff\xff\xff\xff\xf8\xe0Ѡ\xff\xff\xff\xff\xf9\xfe\xe7 \xff\xff\xff\xff\xfa\xc0\xb3\xa0\xff\xff\xff\xff\xfb\xe8\x03\xa0\xff\xff\xff\xff\xfc{\xab\xa0\xff\xff\xff\xff\xfdǻp\x00\x00\x00\x00" + + "\x03p\xc6 \x00\x00\x00\x00\x04)X \x00\x00\x00\x00\x05P\xa8 \x00\x00\x00\x00\x06\t: \x00\x00\x00\x00\a0\x8a \x00\x00\x00\x00\a\xe9\x1c \x00\x00\x00\x00\t\x10l \x00\x00\x00\x00\t\xc8\xfe " + + "\x00\x00\x00\x00\n\xf0N \x00\x00\x00\x00\v\xb2\x1a\xa0\x00\x00\x00\x00\f\xd00 \x00\x00\x00\x00\r\x91\xfc\xa0\x00\x00\x00\x00\x0e\xb0\x12 \x00\x00\x00\x00\x0fqޠ\x00\x00\x00\x00\x10\x99.\xa0\x00\x00\x00\x00" + + "\x11Q\xc0\xa0\x00\x00\x00\x00\x12y\x10\xa0\x00\x00\x00\x00\x131\xa2\xa0\x00\x00\x00\x00\x14X\xf2\xa0\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x168Ɛ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x18\x18\xa8\x90" + + "\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19\xf8\x8a\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xe1\xa7\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\xc1\x89\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00" + + "\x1f\xa1k\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\x81M\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#a/\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%JK\x90\x00\x00\x00\x00&\f\x18\x10" + + "\x00\x00\x00\x00'*-\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00)\n\x0f\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xe9\xf1\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xc9Ӑ\x00\x00\x00\x00" + + "-\x94ڐ\x00\x00\x00\x00.\xa9\xb5\x90\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000\x89\x97\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xff\xb5\x00\x00\x00\x00\x0e\x10\x01\x04\x00\x00\x00\x00\x00\b\x00\x00\x1c \x01\f\x00\x00\x0e\x10\x00\x04LMT\x00BST\x00GMT\x00BDS" + + "T\x00\nGMT0BST,M3.5.0/1,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00GM" + + "TUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x1c\x00GMT+0UT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00" + + "\x00\x00\x00\x00GMT\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x1c\x00GMT-0UT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GM" + + "T\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x04\x00\x1c\x00GMT0UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0" + + "\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00GreenwichUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00GMT\x00\nGMT0\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vUE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\b\x00\x1c\x00HongkongUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x05\x00\x00\x00\x16\xff\xff\xff\xff\x85ic\x90\xff\xff\xff\xff\xcaM10\xff\xff\xff\xff" + + "\xcaۓ0\xff\xff\xff\xff\xcbKqx\xff\xff\xff\xffҠސ\xff\xff\xff\xff\xd3k׀\xff\xff\xff\xffԓX\xb8\xff\xff\xff\xff\xd5B\xb08\xff\xff\xff\xff\xd6s:\xb8\xff\xff\xff\xff\xd7>A\xb8" + + "\xff\xff\xff\xff\xd8.2\xb8\xff\xff\xff\xff\xd8\xf99\xb8\xff\xff\xff\xff\xda\x0e\x14\xb8\xff\xff\xff\xff\xda\xd9\x1b\xb8\xff\xff\xff\xff\xdb\xed\xf6\xb8\xff\xff\xff\xffܸ\xfd\xb8\xff\xff\xff\xff\xdd\xcdظ\xff\xff\xff\xff" + + "ޢ\x1a8\xff\xff\xff\xff߶\xf58\xff\xff\xff\xff\xe0\x81\xfc8\xff\xff\xff\xff\xe1\x96\xc9(\xff\xff\xff\xff\xe2Oi8\xff\xff\xff\xff\xe3v\xab(\xff\xff\xff\xff\xe4/K8\xff\xff\xff\xff\xe5_Ǩ" + + "\xff\xff\xff\xff\xe6\x0f-8\xff\xff\xff\xff\xe7?\xa9\xa8\xff\xff\xff\xff\xe7\xf8I\xb8\xff\xff\xff\xff\xe9\x1f\x8b\xa8\xff\xff\xff\xff\xe9\xd8+\xb8\xff\xff\xff\xff\xea\xffm\xa8\xff\xff\xff\xff\xeb\xb8\r\xb8\xff\xff\xff\xff" + + "\xec\xdfO\xa8\xff\xff\xff\xff\xed\x97\xef\xb8\xff\xff\xff\xff\xee\xc8l(\xff\xff\xff\xff\xefwѸ\xff\xff\xff\xff\xf0\xa8N(\xff\xff\xff\xff\xf1W\xb3\xb8\xff\xff\xff\xff\xf2\x880(\xff\xff\xff\xff\xf3@\xd08" + + "\xff\xff\xff\xff\xf4h\x12(\xff\xff\xff\xff\xf5 \xb28\xff\xff\xff\xff\xf6G\xf4(\xff\xff\xff\xff\xf7%~8\xff\xff\xff\xff\xf8\x15a(\xff\xff\xff\xff\xf9\x05`8\xff\xff\xff\xff\xf9\xf5C(\xff\xff\xff\xff" + + "\xfa\xe5B8\xff\xff\xff\xff\xfb\xde_\xa8\xff\xff\xff\xff\xfc\xce^\xb8\xff\xff\xff\xff\xfd\xbeA\xa8\xff\xff\xff\xff\xfe\xae@\xb8\xff\xff\xff\xff\xff\x9e#\xa8\x00\x00\x00\x00\x00\x8e\"\xb8\x00\x00\x00\x00\x01~\x05\xa8" + + "\x00\x00\x00\x00\x02n\x04\xb8\x00\x00\x00\x00\x03]\xe7\xa8\x00\x00\x00\x00\x04M\xe6\xb8\x00\x00\x00\x00\x05G\x04(\x00\x00\x00\x00\x067\x038\x00\x00\x00\x00\a&\xe6(\x00\x00\x00\x00\a\x83=8\x00\x00\x00\x00" + + "\t\x06\xc8(\x00\x00\x00\x00\t\xf6\xc78\x00\x00\x00\x00\n\xe6\xaa(\x00\x00\x00\x00\v֩8\x00\x00\x00\x00\fƌ(\x00\x00\x00\x00\x11\x9b98\x00\x00\x00\x00\x12ol\xa8\x01\x02\x03\x04\x01\x02\x01\x02" + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00M" + - "PT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x02\x00\x1c\x00NZU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00" + - "\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff" + - "\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3" + - "\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff" + - "\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18" + - "\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00" + - "\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7" + - "\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00" + - "\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%." + - "\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00" + - "\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*" + - "\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00" + - "\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^" + - "\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + - "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0" + - "\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R\x96\xc5FF(\x03\x00\x00(\x03\x00\x00\a\x00\x1c\x00NZ-CHATUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x04\x00\x00\x00\x16\xff\xff\xff\xffA\xb7D\x84\xff\xff\xff\xff\xd2ږ\xbc\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac" + - "\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00" + - "\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!" + - "H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00" + - "\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02" + - "B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00" + - "\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046" + - "K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00" + - "\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<" + - "0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\xab\xfc\x00\x00\x00\x00\xac" + - "D\x00\x04\x00\x00\xc1\\\x01\n\x00\x00\xb3L\x00\x10LMT\x00+1215\x00+1345\x00+1245\x00\n<+1245>-12:45<+1345>,M" + - "9.5.0/2:45,M4.1.0/3:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x1c\x00Pacific" + - "/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xee\xd0\x1cYN\x04\x00\x00N\x04\x00\x00\x0e\x00\x1c\x00P" + - "acific/EasterUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00f\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffi\x87B\b\xff\xff\xff\xff\xb9\xc7@\x88\xff\xff\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00" + - "\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@" + - "\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00" + - "\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0" + - "\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00" + - "\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \u007f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"9\xfb0\x00\x00\x00\x00#N\xe4@" + - "\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00\x00\x00\x00)½\xb0\x00\x00\x00\x00" + - "*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000\x80\x87@\x00\x00\x00\x001BE\xb0" + - "\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00\x00\x00\x008\x00\x0f@\x00\x00\x00\x00" + - "8\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>j\xae0\x00\x00\x00\x00?\x88\xd1\xc0" + - "\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00" + - "G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L\xb1:@\x00\x00\x00\x00M\xc6\a0" + - "\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00\x00\x00\x00T\v\xd8@\x00\x00\x00\x00" + - "W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\\xa9g\xb0\x01\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + - "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\xff\xff\x99x\x00\x00\xff\xff\x99x\x00\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff" + - "\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\x10LMT\x00EMT\x00-06\x00-07\x00-05\x00\n<-06>6<-05>,M9.1.6/22,M4.1.6/" + - "22\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xfa\x0fA\x05\x99\x00\x00\x00\x99\x00\x00\x00\x10\x00\x1c\x00Pacific/PitcairnUT\t\x00\x03\x15\xac\x0e`\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff~7.\xf4" + - "\x00\x00\x00\x005DB\b\x01\x02\xff\xff\x86\f\x00\x00\xff\xff\x88x\x00\x04\xff\xff\x8f\x80\x00\nLMT\x00-0830\x00-08\x00\n<-08>8\nPK\x03\x04\n\x00\x00\x00\x00\x00" + - "\xf1c9R3\x03\x1f\f\xac\x00\x00\x00\xac\x00\x00\x00\x11\x00\x1c\x00Pacific/EnderburyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff~7Ud\x00\x00\x00\x00\x12V\x04\xc0\x00\x00\x00\x00" + - "/\x059\xb0\x01\x02\x03\xff\xff_\x9c\x00\x00\xff\xffW@\x00\x04\xff\xffeP\x00\b\x00\x00\xb6\xd0\x00\fLMT\x00-12\x00-11\x00+13\x00\n<+13>-13\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9Ra\vೆ\x00\x00\x00\x86\x00\x00\x00\x10\x00\x1c\x00Pacific/FunafutiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\f\xfc\x01\x00\x00\xa8\x04\x00" + - "\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\u07b54-\xd6\x00\x00\x00\xd6\x00\x00\x00\x0e\x00\x1c\x00Pacif" + - "ic/PonapeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x10\xff\xff\xff\xff\x14\xe1\xb9,\xff\xff\xff\xff~6 \xac\xff\xff\xff\xff\x98\x11\x95\xd0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`" + - "\xff\xff\xff\xff\xd2\x11\x0e\xf0\x01\x02\x03\x02\x04\x03\x02\xff\xffB\xd4\x00\x00\x00\x00\x94T\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x00\fLMT\x00+11\x00+09\x00+10" + - "\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xb7\xef\x97\xc6\xc6\x00\x00\x00\xc6\x00\x00\x00\x0e\x00\x1c\x00Pacific/NoumeaUT\t\x00\x03" + - "\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff" + - "\xff\xff\x92\xf5\xc4t\x00\x00\x00\x00\x0e\xe6\xbaP\x00\x00\x00\x00\x0fV\xbb\xc0\x00\x00\x00\x00\x10ƜP\x00\x00\x00\x00\x117\xef@\x00\x00\x00\x002\xa0K\xf0\x00\x00\x00\x003\x18Dp\x02\x01\x02\x01\x02\x01" + - "\x02\x00\x00\x9c\f\x00\x00\x00\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\bLMT\x00+12\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x80\xf8vܔ" + - "\x00\x00\x00\x94\x00\x00\x00\r\x00\x1c\x00Pacific/PalauUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00" + + "\x01\x00\x00k\n\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x01\b\x00\x00w\x88\x01\r\x00\x00~\x90\x00\x12LMT\x00HKT\x00HKST\x00HKWT\x00JST\x00\nHKT-8\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU=\xf7\xfawp\x00\x00\x00p\x00\x00\x00\x03\x00\x1c\x00HSTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xffs`\x00\x00HST\x00\nHST10\nPK\x03\x04\n\x00\x00" + + "\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\a\x00\x1c\x00IcelandUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x92\xe6\x92H\x01\xff\xff\xfc8\x00\x00\x00\x00\x00\x00\x00\x04LMT\x00GM" + + "T\x00\nGMT0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x1c\x00Indian/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x96\xed=\x98\xb3\x00\x00\x00\xb3\x00\x00\x00\x10\x00\x1c\x00Indian/MauritiusU" + + "T\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x03\x00\x00" + + "\x00\f\xff\xff\xff\xff\x89\x7f\x05\x98\x00\x00\x00\x00\x18\x05\xed@\x00\x00\x00\x00\x18\xdbr0\x00\x00\x00\x00I\x03\x96\xe0\x00\x00\x00\x00IΏ\xd0\x02\x01\x02\x01\x02\x00\x005\xe8\x00\x00\x00\x00FP\x01\x04\x00" + + "\x008@\x00\bLMT\x00+05\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x1c\x00Indi" + + "an/ReunionUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99\xa8\x01\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\xb9\xb2Z\xac\x98\x00\x00\x00\x98\x00\x00\x00\x10\x00\x1c\x00Indian/KerguelenUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x9f\x18\xff\xff\xff\xff\xed/Ø\x01\x02\x00\x00" + + "D\xe8\x00\x00\x00\x00D\xe8\x00\x04\x00\x00FP\x00\bLMT\x00MMT\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf" + + "\x00\x00\x00\x13\x00\x1c\x00Indian/AntananarivoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xad" + + "X\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nE" + + "AT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x1c\x00Indian/MayotteUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff\x8b\xff\xd1\xfc" + + "\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*0\x00\n\x00\x00&\xac\x00" + + "\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x10\x00\x1c\x00In" + + "dian/ChristmasUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x85\xc4\xff\xff\xff\xff\xa2jg\xc4\x01\x02\x00\x00^<\x00\x00\x00\x00^<\x00\x04\x00\x00bp\x00\bLMT\x00BMT" + + "\x00+07\x00\n<+07>-7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x1c\x00Indian/ComoroUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x14" + + "\xff\xff\xff\xff\x8b\xff\xd1\xfc\xff\xff\xff\xff\xb1\xee\xdaX\xff\xff\xff\xff\xb4\xc7\xe0\xd0\xff\xff\xff\xff\xc1\xed\xadX\xff\xff\xff\xff\xcclz\xd4\x01\x02\x01\x03\x02\x00\x00\"\x84\x00\x00\x00\x00#(\x00\x04\x00\x00*" + + "0\x00\n\x00\x00&\xac\x00\x0eLMT\x00+0230\x00EAT\x00+0245\x00\nEAT-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUx\xb0W\x14\x98\x00\x00\x00\x98\x00" + + "\x00\x00\r\x00\x1c\x00Indian/ChagosUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x89~\xf7\x9c\x00\x00\x00\x000\xe6ݰ\x01\x02\x00\x00C\xe4\x00\x00\x00\x00FP\x00\x04\x00\x00T`\x00\bLM" + + "T\x00+05\x00+06\x00\n<+06>-6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x1c\x00Indian/Mahe" + + "UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00" + + "\x00\x00\b\xff\xff\xff\xff\xa1\xf2\x99\xa8\x01\x00\x003\xd8\x00\x00\x00\x008@\x00\x04LMT\x00+04\x00\n<+04>-4\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUʇ{_\xbb" + + "\x00\x00\x00\xbb\x00\x00\x00\f\x00\x1c\x00Indian/CocosUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffV\xb6\x89\xd1\xff\xff\xff\xff\xa1\xf2sQ\xff\xff\xff\xff\xcb\xf2\xfc\x18\xff\xff\xff\xffњg\xf0\x01\x02" + + "\x03\x02\x00\x00Z/\x00\x00\x00\x00Z/\x00\x04\x00\x00[h\x00\b\x00\x00~\x90\x00\x0eLMT\x00RMT\x00+0630\x00+09\x00\n<+0630>-6:30\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb9\xb2Z\xac\x98\x00\x00\x00\x98\x00\x00\x00\x0f\x00\x1c\x00Indian/MaldivesUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffV\xb6\x9f\x18\xff\xff\xff\xff\xed/" + + "Ø\x01\x02\x00\x00D\xe8\x00\x00\x00\x00D\xe8\x00\x04\x00\x00FP\x00\bLMT\x00MMT\x00+05\x00\n<+05>-5\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8c\xdb?" + + "\xec,\x03\x00\x00,\x03\x00\x00\x04\x00\x1c\x00IranUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00G\x00\x00\x00\x06\x00\x00\x00\x1c\xff\xff\xff\xff\x9al}\xc8\xff\xff\xff\xff\xbf\x00\xccH\x00\x00\x00\x00\r\x94D8\x00\x00\x00\x00\x0e\xad\x13\xb8\x00\x00\x00\x00\x0fys@" + + "\x00\x00\x00\x00\x10(\xca\xc0\x00\x00\x00\x00\x10\xed:@\x00\x00\x00\x00\x11\xad\xbcH\x00\x00\x00\x00\x12EJ\xb8\x00\x00\x00\x00\x137\xec\xc8\x00\x00\x00\x00\x14-\x15\xb8\x00\x00\x00\x00( v\xc8\x00\x00\x00\x00" + + "(\u06dd\xb8\x00\x00\x00\x00)˜\xc8\x00\x00\x00\x00*\xbe\"\xb8\x00\x00\x00\x00+\xac\xd0H\x00\x00\x00\x00,\x9fV8\x00\x00\x00\x00-\x8e\x03\xc8\x00\x00\x00\x00.\x80\x89\xb8\x00\x00\x00\x00/o7H" + + "\x00\x00\x00\x000a\xbd8\x00\x00\x00\x001Pj\xc8\x00\x00\x00\x002B\xf0\xb8\x00\x00\x00\x0032\xef\xc8\x00\x00\x00\x004%u\xb8\x00\x00\x00\x005\x14#H\x00\x00\x00\x006\x06\xa98\x00\x00\x00\x00" + + "6\xf5V\xc8\x00\x00\x00\x007\xe7ܸ\x00\x00\x00\x008֊H\x00\x00\x00\x009\xc9\x108\x00\x00\x00\x00:\xb9\x0fH\x00\x00\x00\x00;\xab\x958\x00\x00\x00\x00<\x9aB\xc8\x00\x00\x00\x00=\x8cȸ" + + "\x00\x00\x00\x00>{vH\x00\x00\x00\x00?m\xfc8\x00\x00\x00\x00@\\\xa9\xc8\x00\x00\x00\x00AO/\xb8\x00\x00\x00\x00B?.\xc8\x00\x00\x00\x00C1\xb4\xb8\x00\x00\x00\x00G\xe2\xc9H\x00\x00\x00\x00" + + "H\xd5O8\x00\x00\x00\x00I\xc5NH\x00\x00\x00\x00J\xb7\xd48\x00\x00\x00\x00K\xa6\x81\xc8\x00\x00\x00\x00L\x99\a\xb8\x00\x00\x00\x00M\x87\xb5H\x00\x00\x00\x00Nz;8\x00\x00\x00\x00Oh\xe8\xc8" + + "\x00\x00\x00\x00P[n\xb8\x00\x00\x00\x00QKm\xc8\x00\x00\x00\x00R=\xf3\xb8\x00\x00\x00\x00S,\xa1H\x00\x00\x00\x00T\x1f'8\x00\x00\x00\x00U\r\xd4\xc8\x00\x00\x00\x00V\x00Z\xb8\x00\x00\x00\x00" + + "V\xef\bH\x00\x00\x00\x00W\xe1\x8e8\x00\x00\x00\x00XэH\x00\x00\x00\x00Y\xc4\x138\x00\x00\x00\x00Z\xb2\xc0\xc8\x00\x00\x00\x00[\xa5F\xb8\x00\x00\x00\x00\\\x93\xf4H\x00\x00\x00\x00]\x86z8" + + "\x00\x00\x00\x00^u'\xc8\x00\x00\x00\x00_g\xad\xb8\x00\x00\x00\x00`W\xac\xc8\x00\x00\x00\x00aJ2\xb8\x00\x00\x00\x00b8\xe0H\x00\x00\x00\x00c+f8\x01\x03\x02\x05\x04\x05\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x00" + + "\x0008\x00\x00\x00\x0008\x00\x04\x00\x00?H\x01\b\x00\x0018\x00\x0e\x00\x00FP\x01\x14\x00\x008@\x00\x18LMT\x00TMT\x00+0430\x00+0330\x00+05\x00+" + + "04\x00\n<+0330>-3:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x17✳2\x04\x00\x002\x04\x00\x00\x06\x00\x1c\x00IsraelUT\t\x00\x03\xaf\x16" + + "\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xff" + + "V\xb6\xc2\xfa\xff\xff\xff\xff\x9e0E\x88\xff\xff\xff\xff\xc8Y\xcf\x00\xff\xff\xff\xff\xc8\xfa\xa6\x00\xff\xff\xff\xff\xc98\x9c\x80\xff\xff\xff\xff\xcc\xe5\xeb\x80\xff\xff\xff\xffͬ\xfe\x00\xff\xff\xff\xff\xce\xc7\x1f\x00" + + "\xff\xff\xff\xffϏ\x83\x00\xff\xff\xff\xffЩ\xa4\x00\xff\xff\xff\xffф}\x00\xff\xff\xff\xffҊ׀\xff\xff\xff\xff\xd3e\xb0\x80\xff\xff\xff\xff\xd4l\v\x00\xff\xff\xff\xff\xd7Z0\x80\xff\xff\xff\xff" + + "\xd7\xdfX\x00\xff\xff\xff\xff\xd8/À\xff\xff\xff\xff\xd9\x1ec\x00\xff\xff\xff\xff\xda\x10\xf7\x00\xff\xff\xff\xff\xda\xeb\xd0\x00\xff\xff\xff\xff۴4\x00\xff\xff\xff\xffܹ=\x00\xff\xff\xff\xff\xdd\xe0\x8d\x00" + + "\xff\xff\xff\xff\u07b4\u0380\xff\xff\xff\xffߤ\xbf\x80\xff\xff\xff\xff\xe0\x8bv\x00\xff\xff\xff\xff\xe1V}\x00\xff\xff\xff\xff\xe2\xbef\x80\xff\xff\xff\xff\xe36_\x00\xff\xff\xff\xff\xe4\x9eH\x80\xff\xff\xff\xff" + + "\xe5\x16A\x00\xff\xff\xff\xff\xe6t\xf0\x00\xff\xff\xff\xff\xe7\x11Ҁ\xff\xff\xff\xff\xe8&\xad\x80\xff\xff\xff\xff\xe8\xe8z\x00\x00\x00\x00\x00\b|\x8b\xe0\x00\x00\x00\x00\b\xfd\xb0\xd0\x00\x00\x00\x00\t\xf6\xea`" + + "\x00\x00\x00\x00\n\xa63\xd0\x00\x00\x00\x00\x13\xe9\xfc`\x00\x00\x00\x00\x14![`\x00\x00\x00\x00\x1a\xfa\xc6`\x00\x00\x00\x00\x1b\x8en`\x00\x00\x00\x00\x1c\xbe\xf8\xe0\x00\x00\x00\x00\x1dw|\xd0\x00\x00\x00\x00" + + "\x1e\xcc\xff`\x00\x00\x00\x00\x1f`\x99P\x00\x00\x00\x00 \x82\xb1`\x00\x00\x00\x00!I\xb5\xd0\x00\x00\x00\x00\"^\x9e\xe0\x00\x00\x00\x00# ]P\x00\x00\x00\x00$Z0`\x00\x00\x00\x00%\x00?P" + + "\x00\x00\x00\x00&\v\xed\xe0\x00\x00\x00\x00&\xd6\xe6\xd0\x00\x00\x00\x00'\xeb\xcf\xe0\x00\x00\x00\x00(\xc0\x03P\x00\x00\x00\x00)\xd4\xec`\x00\x00\x00\x00*\xa9\x1f\xd0\x00\x00\x00\x00+\xbbe\xe0\x00\x00\x00\x00" + + ",\x89\x01\xd0\x00\x00\x00\x00-\x9bG\xe0\x00\x00\x00\x00._\xa9P\x00\x00\x00\x00/{)\xe0\x00\x00\x00\x000H\xc5\xd0\x00\x00\x00\x001H\x96\xe0\x00\x00\x00\x002\x83\x82p\x00\x00\x00\x00?|\x9f\xe0\x00\x00\x00\x00@s6p\x00\x00\x00\x00AP\xa4`" + + "\x00\x00\x00\x00BL\x8f\x00\x00\x00\x00\x00CHOp\x00\x00\x00\x00D,q\x00\x00\x00\x00\x00E\x1e\xf6\xf0\x00\x00\x00\x00F\fS\x00\x00\x00\x00\x00F\xecc\xf0\x00\x00\x00\x00G\xec5\x00\x00\x00\x00\x00" + + "H\xe7\xf5p\x00\x00\x00\x00I\xcc\x17\x00\x00\x00\x00\x00J\xbe\x9c\xf0\x00\x00\x00\x00K\xab\xf9\x00\x00\x00\x00\x00L\x8c\t\xf0\x00\x00\x00\x00M\x95\x15\x80\x00\x00\x00\x00N\x87\x9bp\x00\x00\x00\x00Ot\xf7\x80" + + "\x00\x00\x00\x00P^B\xf0\x00\x00\x00\x00QTـ\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00!\x06" + + "\x00\x00\x00\x00 \xf8\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\f\x00\x008@\x01\x10LMT\x00JMT\x00IDT\x00IST\x00IDDT\x00\nIST-2IDT,M3." + + "4.4/26,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\a\x00\x1c\x00JamaicaUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xffi" + + "\x87#~\xff\xff\xff\xff\x93\x0f\xb4\xfe\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\t\xad\x94\xf0\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00" + + "\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14" + + "Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x01\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xff\xb8\x02\x00\x00\xff\xff\xb8\x02\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\fLMT\x00KMT\x00EST\x00EDT\x00\nEST5\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x02\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\x05\x00\x1c\x00JapanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xffe¤p\xff\xff\xff\xff\xd7>\x02p\xff\xff\xff\xff\xd7\xedY" + + "\xf0\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd;\xf0\xff\xff\xff\xff\xdb\a\x00\xf0\xff\xff\xff\xffۭ\x1d\xf0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xff\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00" + + "\x83\x03\x00\x00\x00\x00\x8c\xa0\x01\x04\x00\x00~\x90\x00\bLMT\x00JDT\x00JST\x00\nJST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00" + + "\x00\t\x00\x1c\x00KwajaleinUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff~6\x18 \xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xcfF\x81\xf0\xff\xff\xff\xff\xff\x86\x1bP\x00\x00\x00" + + "\x00,v\x0e@\x01\x02\x03\x01\x04\x05\x00\x00\x9c\xe0\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00\x8c\xa0\x00\b\x00\x00~\x90\x00\f\xff\xffW@\x00\x10\x00\x00\xa8\xc0\x00\x14LMT\x00+11\x00+10\x00+" + + "09\x00-12\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU_\x7f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x05\x00\x1c\x00LibyaUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x04\x00\x00\x00\x11\xff" + + "\xff\xff\xff\xa1\xf2\xc1$\xff\xff\xff\xffݻ\xb1\x10\xff\xff\xff\xff\xde#\xad`\xff\xff\xff\xff\xe1x\xd2\x10\xff\xff\xff\xff\xe1\xe7e\xe0\xff\xff\xff\xff\xe5/?p\xff\xff\xff\xff\xe5\xa9\xcc\xe0\xff\xff\xff\xff\xeb" + + "N\xc6\xf0\x00\x00\x00\x00\x16\x92B`\x00\x00\x00\x00\x17\b\xf7p\x00\x00\x00\x00\x17\xfa+\xe0\x00\x00\x00\x00\x18\xea*\xf0\x00\x00\x00\x00\x19\xdb_`\x00\x00\x00\x00\x1a̯\xf0\x00\x00\x00\x00\x1b\xbd\xe4`\x00" + + "\x00\x00\x00\x1c\xb4z\xf0\x00\x00\x00\x00\x1d\x9f\x17\xe0\x00\x00\x00\x00\x1e\x93\vp\x00\x00\x00\x00\x1f\x82\xee`\x00\x00\x00\x00 pJp\x00\x00\x00\x00!a~\xe0\x00\x00\x00\x00\"R\xcfp\x00\x00\x00\x00#" + + "D\x03\xe0\x00\x00\x00\x00$4\x02\xf0\x00\x00\x00\x00%%7`\x00\x00\x00\x00&@\xb7\xf0\x00\x00\x00\x002N\xf1`\x00\x00\x00\x003D6p\x00\x00\x00\x0045j\xe0\x00\x00\x00\x00P\x9d\x99\x00\x00" + + "\x00\x00\x00QTـ\x00\x00\x00\x00Ri\xb4\x80\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x03\x02\x01\x03\x00\x00\f\\\x00\x00\x00\x00\x1c \x01\x04\x00" + + "\x00\x0e\x10\x00\t\x00\x00\x1c \x00\rLMT\x00CEST\x00CET\x00EET\x00\nEET-2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xfe\x9d\x1b\xc9m\x02\x00\x00m\x02\x00" + + "\x00\x03\x00\x1c\x00METUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x005\x00\x00\x00\x02\x00\x00\x00\t\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff" + + "\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xff\xd2" + + "N@\x90\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00" + + "\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1a" + + "Ñ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00" + + "\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00B" + + "O\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00F\x0f\x82\xa0\x00\x00\x00\x00G$O\x90\x00\x00\x00\x00G\xf8\x9f \x00\x00\x00\x00I\x041\x90\x00" + + "\x00\x00\x00I\u0601 \x00\x00\x00\x00J\xe4\x13\x90\x00\x00\x00\x00K\x9c\xb3\xa0\x01\x02\x01\x02\x03\x02\x04\x05\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\x92" + + "L\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10\xff\xff\x9d\x90\x01\x14LMT\x00MST\x00PST\x00PDT\x00PWT\x00PPT\x00\nPS" + + "T8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU8\xcdZ\x05o\x01\x00\x00o\x01\x00\x00\x0e\x00\x1c\x00Mexico/Ba" + + "jaSurUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16" + + "\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff" + + "\xcb\xeaq`\xff\xff\xff\xffؑ\xb4\xf0\x00\x00\x00\x00\x00\x00p\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10" + + "\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xf5\x12\x90\x00\x00\x00\x00;\xb6\xd1\x00\x00\x00\x00\x00" + + "<\xb0\n\x90\x01\x02\x01\x02\x01\x02\x01\x03\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\x01\x04\xff\xff\x9c<\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\x8f\x80\x00\f\xff\xff\xab\xa0\x01\x10LMT\x00" + + "MST\x00CST\x00PST\x00MDT\x00\nMST7MDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xd6\xe1Հ\x9c\x01" + + "\x00\x00\x9c\x01\x00\x00\x0e\x00\x1c\x00Mexico/GeneralUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xff\x14\xe1\xcfl\xff\xff\xff\xff~66\xec\x01\x02\xff\xff,\x94\x00\x00\x00\x00~\x14\x00\x00\x00\x00~" + - "\x90\x00\x04LMT\x00+09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x1c\x00Pacific/Jo" + - "hnstonUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\a\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff\xff\xff\xff\xbb\x05CH\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff" + - "\xffՍsH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00\xff\xfflX\x00\x04\xff\xffzh\x01\b\xff\xffzh\x01\f\xff\xffzh\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00" + - "HWT\x00HPT\x00\nHST10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xca\"\xb8i\xda\x00\x00\x00\xda\x00\x00\x00\x0e\x00\x1c\x00Pacific/MajuroU" + - "T\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00" + - "\x00\x14\xff\xff\xff\xff~6\x14\x80\xff\xff\xff\xff\x98\x11\x95\xd0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xcf=Gp\xff\xff\xff\xff\xff\x86\x1bP\x01\x02" + - "\x01\x03\x02\x01\x04\x00\x00\xa0\x80\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x00\f\x00\x00\xa8\xc0\x00\x10LMT\x00+11\x00+09\x00+10\x00+12\x00\n<+12" + - ">-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x11\x00\x1c\x00Pacific/Pago_PagoUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xffn" + - "=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "\x9e\u007f\xab\x95V\x01\x00\x00V\x01\x00\x00\r\x00\x1c\x00Pacific/EfateUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x92\xf5´\x00\x00\x00\x00\ay\x99@\x00\x00\x00\x00\a\xfa\xcc@\x00\x00\x00\x00" + - "\x19\xd2\xf7\xd0\x00\x00\x00\x00\x1a\xc2\xda\xc0\x00\x00\x00\x00\x1b\xb2\xd9\xd0\x00\x00\x00\x00\x1c\xa2\xbc\xc0\x00\x00\x00\x00\x1d\x9b\xf6P\x00\x00\x00\x00\x1e\x82\x9e\xc0\x00\x00\x00\x00\x1f{\xd8P\x00\x00\x00\x00 k\xbb@" + - "\x00\x00\x00\x00![\xbaP\x00\x00\x00\x00\"K\x9d@\x00\x00\x00\x00#;\x9cP\x00\x00\x00\x00$+\u007f@\x00\x00\x00\x00%\x1b~P\x00\x00\x00\x00&\va@\x00\x00\x00\x00&\xfb`P\x00\x00\x00\x00" + - "'\xebC@\x00\x00\x00\x00(\xe4|\xd0\x00\x00\x00\x00)\x81Q@\x00\x00\x00\x00*\xe9H\xd0\x00\x00\x00\x00+a3@\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00" + - "\x00\x9d\xcc\x00\x00\x00\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\bLMT\x00+12\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc8=ku\xae\x00\x00" + - "\x00\xae\x00\x00\x00\x12\x00\x1c\x00Pacific/KiritimatiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff~7H\x80\x00\x00\x00\x00\x12U\xf2\x00\x00\x00\x00\x00/\x05+\xa0\x01\x02\x03\xff\xffl" + - "\x80\x00\x00\xff\xffj\x00\x00\x04\xff\xffs`\x00\n\x00\x00\xc4\xe0\x00\x0eLMT\x00-1040\x00-10\x00+14\x00\n<+14>-14\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1" + - "c9R\x8a|\xdcU\x99\x00\x00\x00\x99\x00\x00\x00\x0f\x00\x1c\x00Pacific/FakaofoUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~7U\x88\x00\x00\x00\x00N\xfd\x99\xb0\x01\x02\xff\xff_x\x00" + - "\x00\xff\xffeP\x00\x04\x00\x00\xb6\xd0\x00\bLMT\x00-11\x00+13\x00\n<+13>-13\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x96\xc5FF(\x03\x00\x00(\x03\x00" + - "\x00\x0f\x00\x1c\x00Pacific/ChathamUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x04\x00\x00\x00\x16\xff\xff\xff\xffA\xb7D\x84\xff\xff\xff\xff\xd2ږ\xbc\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n" + - "\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00" + - "\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18" + - "\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00" + - "\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'" + - "\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00" + - "\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005" + - "\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00" + - "\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C" + - ">\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\xab\xfc\x00\x00\x00\x00\xacD\x00\x04\x00\x00\xc1\\" + - "\x01\n\x00\x00\xb3L\x00\x10LMT\x00+1215\x00+1345\x00+1245\x00\n<+1245>-12:45<+1345>,M9.5.0/2" + - ":45,M4.1.0/3:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\x11\x00\x1c\x00Pacific/Kwajal" + - "einUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00" + - "\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff~6\x18 \xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xcfF\x81\xf0\xff\xff\xff\xff\xff\x86\x1bP\x00\x00\x00\x00,v\x0e@\x01\x02\x03\x01\x04\x05" + - "\x00\x00\x9c\xe0\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00\x8c\xa0\x00\b\x00\x00~\x90\x00\f\xff\xffW@\x00\x10\x00\x00\xa8\xc0\x00\x14LMT\x00+11\x00+10\x00+09\x00-12\x00+12\x00" + - "\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x0e\x00\x1c\x00Pacific/MidwayUT\t\x00\x03\x15" + - "\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff" + - "\xffn=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R1\xce_(\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Pacific/WallisUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff\xa5\xb6\xe8p\xff\xff\xff\xff\xaf\xf2n\xe0\xff\xff\xff\xff\xb6fV`\xff\xff\xff\xff\xb7C\xd2`\xff" + + "\xff\xff\xff\xb8\f6`\xff\xff\xff\xff\xb8\xfd\x86\xf0\xff\xff\xff\xff\xc5ް`\xff\xff\xff\xffƗ4P\xff\xff\xff\xff\xc9U\xf1\xe0\xff\xff\xff\xff\xc9\xea\xddP\xff\xff\xff\xff\xcf\x02\xc6\xe0\xff\xff\xff\xff\xcf" + + "\xb7VP\xff\xff\xff\xffڙ\x15\xe0\xff\xff\xff\xff\xdbv\x83\xd0\x00\x00\x00\x001gv\x00\x00\x00\x00\x002s\bp\x00\x00\x00\x003GX\x00\x00\x00\x00\x004R\xeap\x00\x00\x00\x005':\x00\x00" + + "\x00\x00\x0062\xccp\x00\x00\x00\x007\a\x1c\x00\x00\x00\x00\x008\x1b\xe8\xf0\x00\x00\x00\x008\xe6\xfe\x00\x00\x00\x00\x009\xfb\xca\xf0\x00\x00\x00\x00:\xf5\x04\x80\x00\x00\x00\x00;\xb6\xc2\xf0\x00\x00\x00\x00<" + + "\xaf\xfc\x80\x01\x02\x01\x02\x01\x02\x03\x02\x03\x02\x04\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\xff\xff\xa3\f\x00\x00\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10" + + "LMT\x00MST\x00CST\x00CDT\x00CWT\x00\nCST6CDT,M4.1.0,M10.5.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf5\x8d" + + "\x99\x92o\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00MSTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\xff\xff\x9d\x90\x00\x00MST\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xe6h\xcac\xb7\x03\x00\x00\xb7\x03" + + "\x00\x00\a\x00\x1c\x00MST7MDTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff" + + "\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00" + + "\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00" + + "\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90" + + "\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00" + + "\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80" + + "\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00" + + "#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90" + + "\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x00" + + "1g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00" + + "\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00" + + "?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10" + + "\x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01" + + "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\x9d\x90\x00\x04\xff\xff\xab\xa0\x01\x00\xff\xff\xab\xa0\x01\b\xff\xff\xab\xa0\x01\fMDT\x00MST\x00" + + "MWT\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x06\x00" + + "\x1c\x00NavajoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff" + + "\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8" + + "(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff" + + "\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06" + + "@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00" + + "\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14" + + "YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00" + + "\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"" + + "U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00" + + "\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000" + + "\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00" + + "\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>" + + "\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00" + + "\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90" + + "\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x02\x00\x1c\x00NZUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZ" + + "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff" + + "\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5" + + "\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff" + + "\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`" + + "\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00" + + "\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84" + + "\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00" + + "\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2" + + "`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00" + + "\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7" + + "`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00" + + "\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0" + + "`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00" + + "\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + + "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3" + + "\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12" + + "NZDT,M9.5.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x96\xc5FF(\x03\x00\x00(\x03\x00\x00\a\x00\x1c\x00NZ-CHATUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x04\x00\x00\x00" + + "\x16\xff\xff\xff\xffA\xb7D\x84\xff\xff\xff\xff\xd2ږ\xbc\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00" + + "\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI" + + "\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00" + + "\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t" + + "\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00" + + "\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM" + + "`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00" + + "\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1" + + "\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00" + + "\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\xab\xfc\x00\x00\x00\x00\xacD\x00\x04\x00\x00\xc1\\\x01\n\x00\x00\xb3L\x00\x10LMT\x00+1215\x00+134" + + "5\x00+1245\x00\n<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x1c\x00Pacific/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUFI\xfe\x14^\x01\x00\x00^\x01\x00\x00\x0e\x00\x1c\x00Pacific/SaipanUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + + "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x06\x00\x00\x00\x15\xff\xff\xff\xff\x14\xe1\xc5\xcc\xff\xff\xff\xff~" + + "6-L\xff\xff\xff\xff\xcb7\x95\xe0\xff\xff\xff\xff\xd0.\x89\xf0\xff\xff\xff\xff\xec7\xbe\x00\xff\xff\xff\xff\xef6\xf8\xf0\xff\xff\xff\xff\xfb\x9b\x00\x00\xff\xff\xff\xff\xfe?'\x8c\xff\xff\xff\xff\xff\x01\x1e\x00\xff" + + "\xff\xff\xff\xff]X\xf0\x00\x00\x00\x00\x00\x97,\x00\x00\x00\x00\x00\x01Fup\x00\x00\x00\x00\x02w\x0e\x00\x00\x00\x00\x00\x03&Wp\x00\x00\x00\x00\ap\x97\x00\x00\x00\x00\x00\a\xcc\xd1\xf0\x00\x00\x00\x00\f" + + "\b\x91\x00\x00\x00\x00\x00\f|\x87,\x00\x00\x00\x00\r\xbf\x94\x80\x00\x00\x00\x00\x0ee\xa3p\x00\x00\x00\x00:C^`\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\xff\xff64" + + "\x00\x00\x00\x00\x87\xb4\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00GST\x00+09\x00GDT\x00ChST\x00\nChST-1" + + "0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x1c\x00Pacific/HonoluluUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01" + + "\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff" + + "\xff\xff\xff\xbb\x05CH\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff\xffՍsH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00" + + "\xff\xfflX\x00\x04\xff\xffzh\x01\b\xff\xffzh\x01\f\xff\xffzh\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00HWT\x00HPT\x00\nHST10\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vUt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x0e\x00\x1c\x00Pacific/MidwayUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xffn=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b" + + "\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc8=ku\xae\x00\x00\x00\xae\x00\x00" + + "\x00\x12\x00\x1c\x00Pacific/KiritimatiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff~7H\x80\x00\x00\x00\x00\x12U\xf2\x00\x00\x00\x00\x00/\x05+\xa0\x01\x02\x03\xff\xffl\x80\x00\x00\xff" + + "\xffj\x00\x00\x04\xff\xffs`\x00\n\x00\x00\xc4\xe0\x00\x0eLMT\x00-1040\x00-10\x00+14\x00\n<+14>-14\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x97" + + "F\x91\xb3\xed\x00\x00\x00\xed\x00\x00\x00\x11\x00\x1c\x00Pacific/TongatapuUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\b\xa8\x01\x00\x00\xacX\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00" + - "+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R4\xd0Yӣ\x01\x00\x00\xa3\x01\x00\x00\f\x00\x1c\x00Pacific/FijiUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x03\x00\x00\x00\f\xff" + - "\xff\xff\xff\x9a\x13\xb1\xc0\x00\x00\x00\x006;\x17\xe0\x00\x00\x00\x006\xd7\xfa`\x00\x00\x00\x008$4`\x00\x00\x00\x008\xb7\xdc`\x00\x00\x00\x00K\x11,\xe0\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L" + - "\xc2\xea`\x00\x00\x00\x00MrA\xe0\x00\x00\x00\x00N\xa2\xcc`\x00\x00\x00\x00O\x1a\xc4\xe0\x00\x00\x00\x00P\x82\xae`\x00\x00\x00\x00P\xfa\xa6\xe0\x00\x00\x00\x00Rk\xca\xe0\x00\x00\x00\x00R\xdaz\xd0\x00" + - "\x00\x00\x00TT\xe7`\x00\x00\x00\x00T\xbaj\xe0\x00\x00\x00\x00V4\xc9`\x00\x00\x00\x00V\x9aL\xe0\x00\x00\x00\x00X\x1d\xe5\xe0\x00\x00\x00\x00Xz.\xe0\x00\x00\x00\x00Y\xfd\xc7\xe0\x00\x00\x00\x00Z" + - "Z\x10\xe0\x00\x00\x00\x00[ݩ\xe0\x00\x00\x00\x00\\9\xf2\xe0\x00\x00\x00\x00]\xc6\xc6`\x00\x00\x00\x00^\x19\xd4\xe0\x00\x00\x00\x00_\xde\a`\x00\x00\x00\x00`\x02\xf1`\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\xa7\xc0\x00\x00\x00\x00\xb6\xd0\x01\x04\x00\x00\xa8\xc0\x00\bLMT\x00+13\x00+12\x00\n<+12>-12<" + - "+13>,M11.2.0,M1.2.3/99\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe9\xdd\x1e\xee\f\x01\x00\x00\f\x01\x00\x00\f\x00\x1c\x00Pacific" + - "/ApiaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b" + - "\x00\x00\x00\a\x00\x00\x00\x1a\xff\xff\xff\xffn=\xc9\x00\xff\xff\xff\xff\x91\x05\xfc\x00\xff\xff\xff\xff\xdab\x048\x00\x00\x00\x00L\x9f'\xb0\x00\x00\x00\x00M\x97+\xe0\x00\x00\x00\x00N}\xe2`\x00\x00\x00\x00" + - "N\xfd\x8b\xa0\x00\x00\x00\x00Ow\r\xe0\x01\x02\x04\x03\x04\x03\x06\x05\x00\x00\xb0\x80\x00\x00\xff\xff_\x00\x00\x00\xff\xff^H\x00\x04\xff\xffs`\x01\n\xff\xffeP\x00\x0e\x00\x00\xb6\xd0\x00\x12\x00\x00\xc4\xe0" + - "\x01\x16LMT\x00-1130\x00-10\x00-11\x00+13\x00+14\x00\n<+13>-13<+14>,M9.5.0/3,M4.1.0/4" + - "\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00\xc3\x00\x00\x00\v\x00\x1c\x00Pacific/YapUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x14\xe1\xbf4\xff\xff\xff\xff~6&" + - "\xb4\xff\xff\xff\xff\x98\x11\xa3\xe0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\x01\x02\x03\x02\x03\x02\xff\xff<\xcc\x00\x00\x00\x00\x8eL\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~" + - "\x90\x00\bLMT\x00+10\x00+09\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x14\x00\x1c\x00Pacif" + - "ic/Port_MoresbyUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PM" + - "MT\x00+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x1c\x00Pacific/Honol" + - "uluUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00" + - "\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff\xff\xff\xff\xbb\x05CH\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff\xffՍ" + - "sH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00\xff\xfflX\x00\x04\xff\xffzh\x01\b\xff\xffzh\x01\f\xff\xffzh\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00HWT" + - "\x00HPT\x00\nHST10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\u07b54-\xd6\x00\x00\x00\xd6\x00\x00\x00\x0f\x00\x1c\x00Pacific/PohnpeiUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x00\x10" + - "\xff\xff\xff\xff\x14\xe1\xb9,\xff\xff\xff\xff~6 \xac\xff\xff\xff\xff\x98\x11\x95\xd0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\x01\x02\x03\x02" + - "\x04\x03\x02\xff\xffB\xd4\x00\x00\x00\x00\x94T\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x00\fLMT\x00+11\x00+09\x00+10\x00\n<+11>-11\nP" + - "K\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc23\xa0\xbc\x84\x00\x00\x00\x84\x00\x00\x00\x0f\x00\x1c\x00Pacific/GambierUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + - "\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94PH\x04\x01\xff\xff\x81|" + - "\x00\x00\xff\xff\x81p\x00\x04LMT\x00-09\x00\n<-09>9\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xe2;Z\xf7\xb7\x00\x00\x00\xb7\x00\x00\x00\r\x00\x1c\x00Pacifi" + - "c/NauruUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\xd2E\x9c@\xff\xff\xff\xff\xef\x11\xe0\x10\x00\x00\x00\x007\xfbG\xd0\x00" + + "\x00\x00\x008\xd3}\xd0\x00\x00\x00\x00:\x04\bP\x00\x00\x00\x00:r\xb8@\x00\x00\x00\x00;\xe3\xeaP\x00\x00\x00\x00-13\nPK\x03" + + "\x04\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Pacific/WallisUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00" + + "\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc23\xa0\xbc\x84\x00\x00\x00\x84\x00\x00\x00\x0f\x00\x1c\x00Pacific" + + "/GambierUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94PH\x04\x01\xff\xff\x81|\x00\x00\xff\xff\x81p\x00\x04LMT\x00-09\x00\n<-09>9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Pacific/TarawaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00" + + "+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\r\x00\x1c\x00Pacific/ChuukUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\r" + + "\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PMMT\x00+10\x00\n<+10>-10\nP" + + "K\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xec =\x89\xac\x00\x00\x00\xac\x00\x00\x00\x0e\x00\x1c\x00Pacific/KantonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\xc3,ۀ\x00\x00\x00\x00\x12V" + + "\x04\xc0\x00\x00\x00\x00/\x059\xb0\x01\x02\x03\x00\x00\x00\x00\x00\x00\xff\xffW@\x00\x04\xff\xffeP\x00\b\x00\x00\xb6\xd0\x00\f-00\x00-12\x00-11\x00+13\x00\n<+13>-" + + "13\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xec =\x89\xac\x00\x00\x00\xac\x00\x00\x00\x11\x00\x1c\x00Pacific/EnderburyUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\xc3,\xdb" + + "\x80\x00\x00\x00\x00\x12V\x04\xc0\x00\x00\x00\x00/\x059\xb0\x01\x02\x03\x00\x00\x00\x00\x00\x00\xff\xffW@\x00\x04\xff\xffeP\x00\b\x00\x00\xb6\xd0\x00\f-00\x00-12\x00-11\x00+13\x00" + + "\n<+13>-13\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x81\xe3w\n\xaf\x00\x00\x00\xaf\x00\x00\x00\x11\x00\x1c\x00Pacific/GalapagosUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\f" + + "\xff\xff\xff\xff\xb6\xa4L\x80\x00\x00\x00\x00\x1e\x18\xc4P\x00\x00\x00\x00+\x17\n\xe0\x00\x00\x00\x00+q\xf4P\x01\x03\x02\x03\xff\xff\xac\x00\x00\x00\xff\xff\xb9\xb0\x00\x04\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b" + + "LMT\x00-05\x00-06\x00\n<-06>6\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x1c\xe3\xa3S\x96\x01\x00\x00\x96\x01\x00\x00\x11\x00\x1c\x00Pacific/Ra" + + "rotongaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\xa3\xe7+\x04\xff\xff\xff\xff̐\xe9\xc8\xff\xff\xff\xff\xd2C'\xf0\x00\x00\x00\x00\x11!\xa8\xe8\x01\x02\x01\x03\x00\x00\x9c|\x00\x00\x00\x00\xa1\xb8\x00\x04\x00\x00" + - "~\x90\x00\n\x00\x00\xa8\xc0\x00\x0eLMT\x00+1130\x00+09\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x97n7\x1a\xf2\x00\x00\x00" + - "\xf2\x00\x00\x00\x0e\x00\x1c\x00Pacific/KosraeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x1c\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff|L\xdc\xc8\xff\xff\xff\xffߡ`\xc8\x00\x00\x00\x00\x10\xac\x1b(\x00\x00\x00\x00\x11?\xb5\x18\x00\x00\x00\x00\x12y\x81 \x00\x00\x00\x00\x13\x1f\x97\x18\x00\x00" + + "\x00\x00\x14Yc \x00\x00\x00\x00\x14\xffy\x18\x00\x00\x00\x00\x169E \x00\x00\x00\x00\x16蕘\x00\x00\x00\x00\x18\"a\xa0\x00\x00\x00\x00\x18\xc8w\x98\x00\x00\x00\x00\x1a\x02C\xa0\x00\x00\x00\x00\x1a\xa8" + + "Y\x98\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\x88;\x98\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1eh\x1d\x98\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 G\xff\x98\x00\x00\x00\x00!\x81ˠ\x00\x00" + + "\x00\x00\"1\x1c\x18\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$\x10\xfe\x18\x00\x00\x00\x00%J\xca \x00\x00\x00\x00%\xf0\xe0\x18\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xd0\xc2\x18\x01\x02\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x00\x00\xbb\xb8\x00\x00\xff\xffj8\x00\x00\xff\xfflX\x00\x04\xff\xffs`\x00\n\xff\xffzh\x01\x0eLMT\x00-103" + + "0\x00-10\x00-0930\x00\n<-10>10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x10\x00\x1c\x00Pacific/A" + + "ucklandUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff" + + "\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8" + + "Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff" + + "\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2\xda" + + "\x9a@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00" + + "\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168" + + "+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00" + + "\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f" + + "\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00" + + "\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9`\x00\x00\x00\x002V" + + "i\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00" + + "\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\" + + "N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04" + + "\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00" + + "\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5.0,M4.1.0/3\nPK" + + "\x03\x04\n\x00\x00\x00\x00\x00\bv\vUY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x0f\x00\x1c\x00Pacific/PohnpeiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94O3\x8c\x01\x00\x00\x95\xf4\x00" + + "\x00\x00\x00\x9a\xb0\x00\x04LMT\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9a\xf2:F\xc9\x00\x00\x00\xc9\x00\x00\x00\x14\x00\x1c\x00Pacif" + + "ic/BougainvilleUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6R(\xff\xff\xff\xffr\xed\xa4\x90\xff\xff\xff\xff\xccC6`\xff\xff\xff\xff\xd2+l\xf0\x00\x00\x00\x00T\x9e׀\x01\x02" + + "\x03\x02\x04\x00\x00\x91\xd8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\t\x00\x00~\x90\x00\r\x00\x00\x9a\xb0\x00\x11LMT\x00PMMT\x00+10\x00+09\x00+11\x00\n<+11>" + + "-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xfa\x0fA\x05\x99\x00\x00\x00\x99\x00\x00\x00\x10\x00\x1c\x00Pacific/PitcairnUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xff~7." + + "\xf4\x00\x00\x00\x005DB\b\x01\x02\xff\xff\x86\f\x00\x00\xff\xff\x88x\x00\x04\xff\xff\x8f\x80\x00\nLMT\x00-0830\x00-08\x00\n<-08>8\nPK\x03\x04\n\x00\x00\x00\x00" + + "\x00\bv\vU\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\x11\x00\x1c\x00Pacific/KwajaleinUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff~6\x18 \xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff" + + "\xff\xc9\xea\n`\xff\xff\xff\xff\xcfF\x81\xf0\xff\xff\xff\xff\xff\x86\x1bP\x00\x00\x00\x00,v\x0e@\x01\x02\x03\x01\x04\x05\x00\x00\x9c\xe0\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00\x8c\xa0\x00\b\x00\x00~\x90\x00\f\xff" + + "\xffW@\x00\x10\x00\x00\xa8\xc0\x00\x14LMT\x00+11\x00+10\x00+09\x00-12\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUD" + + "6\x83\xa1\x8b\x00\x00\x00\x8b\x00\x00\x00\x11\x00\x1c\x00Pacific/MarquesasUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\n\xff\xff\xff\xff\x94PLH\x01\xff\xff}8\x00\x00\xff\xffzh\x00\x04LMT\x00" + + "-0930\x00\n<-0930>9:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x14\x00\x1c\x00Pacific/Por" + + "t_MoresbyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PMMT\x00+10" + + "\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xa8A\x15\xfe\x97\x01\x00\x00\x97\x01\x00\x00\f\x00\x1c\x00Pacific/ApiaUT\t\x00\x03\xaf\x16" + + "\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00\a\x00\x00\x00\x1a\xff\xff\xff\xff" + + "n=\xc9\x00\xff\xff\xff\xff\x91\x05\xfc\x00\xff\xff\xff\xff\xdab\x048\x00\x00\x00\x00L\x9f'\xb0\x00\x00\x00\x00M\x97+\xe0\x00\x00\x00\x00N}\xe2`\x00\x00\x00\x00N\xfd\x8b\xa0\x00\x00\x00\x00Ow\r\xe0" + + "\x00\x00\x00\x00Pf\xfe\xe0\x00\x00\x00\x00Q`*`\x00\x00\x00\x00RF\xe0\xe0\x00\x00\x00\x00S@\f`\x00\x00\x00\x00T&\xc2\xe0\x00\x00\x00\x00U\x1f\xee`\x00\x00\x00\x00V\x06\xa4\xe0\x00\x00\x00\x00" + + "V\xff\xd0`\x00\x00\x00\x00W\xe6\x86\xe0\x00\x00\x00\x00X߲`\x00\x00\x00\x00Y\xc6h\xe0\x00\x00\x00\x00Z\xbf\x94`\x00\x00\x00\x00[\xaf\x85`\x00\x00\x00\x00\\\xa8\xb0\xe0\x00\x00\x00\x00]\x8fg`" + + "\x00\x00\x00\x00^\x88\x92\xe0\x00\x00\x00\x00_oI`\x00\x00\x00\x00`ht\xe0\x01\x02\x04\x03\x04\x03\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x00\x00\xb0\x80\x00\x00\xff\xff_\x00" + + "\x00\x00\xff\xff^H\x00\x04\xff\xffs`\x01\n\xff\xffeP\x00\x0e\x00\x00\xb6\xd0\x00\x12\x00\x00\xc4\xe0\x01\x16LMT\x00-1130\x00-10\x00-11\x00+13\x00+14\x00\n<" + + "+13>-13\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xea\xc1\xdaυ\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x1c\x00Pacific/TahitiUT\t\x00\x03\xaf\x16\xf5" + + "b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94" + + "PU\xb8\x01\xff\xffs\xc8\x00\x00\xff\xffs`\x00\x04LMT\x00-10\x00\n<-10>10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x80\xf8vܔ\x00\x00\x00\x94\x00\x00\x00\r" + + "\x00\x1c\x00Pacific/PalauUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xff\x14\xe1\xcfl\xff\xff\xff\xff~66\xec\x01\x02\xff\xff,\x94\x00\x00\x00\x00~\x14\x00\x00\x00\x00~\x90\x00\x04LMT\x00+" + + "09\x00\n<+09>-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x97n7\x1a\xf2\x00\x00\x00\xf2\x00\x00\x00\x0e\x00\x1c\x00Pacific/KosraeUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x14\xff" + + "\xff\xff\xff\x14ᴴ\xff\xff\xff\xff~6\x1c4\xff\xff\xff\xff\x98\x11\x95\xd0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\xff\xff\xff\xff\xff" + + "\x86\x1bP\x00\x00\x00\x006\x8bg@\x01\x02\x03\x02\x04\x03\x02\x05\x02\xff\xffGL\x00\x00\x00\x00\x98\xcc\x00\x00\x00\x00\x9a\xb0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x00\f\x00\x00\xa8\xc0\x00\x10LMT\x00" + + "+11\x00+09\x00+10\x00+12\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\f\x00\x1c\x00Paci" + + "fic/WakeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vUt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x11\x00\x1c\x00Pacific/Pago_PagoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + + "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xffn=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00" + + "\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x1c" + + "\x00Pacific/YapUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PMMT\x00" + + "+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Pacific/PonapeUT" + + "\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" + + "\b\xff\xff\xff\xff\x94O3\x8c\x01\x00\x00\x95\xf4\x00\x00\x00\x00\x9a\xb0\x00\x04LMT\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUFI\xfe\x14^\x01" + + "\x00\x00^\x01\x00\x00\f\x00\x1c\x00Pacific/GuamUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xff\x14ᴴ\xff\xff\xff\xff~6\x1c4\xff\xff\xff\xff\x98\x11\x95\xd0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff" + - "\xff\xc1\xed5\xd0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\xff\xff\xff\xff\xff\x86\x1bP\x00\x00\x00\x006\x8bg@\x01\x02\x03\x02\x04\x03\x02\x05\x02\xff\xffGL\x00\x00\x00\x00\x98\xcc\x00\x00\x00\x00" + - "\x9a\xb0\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x00\f\x00\x00\xa8\xc0\x00\x10LMT\x00+11\x00+09\x00+10\x00+12\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x85v\xf8\x8c\x87\x01\x00\x00\x87\x01\x00\x00\x11\x00\x1c\x00Pacific/RarotongaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZ" + - "if2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff~7J\xc8\x00\x00\x00\x00\x10\xac\x1b(\x00\x00" + - "\x00\x00\x11?\xb5\x18\x00\x00\x00\x00\x12y\x81 \x00\x00\x00\x00\x13\x1f\x97\x18\x00\x00\x00\x00\x14Yc \x00\x00\x00\x00\x14\xffy\x18\x00\x00\x00\x00\x169E \x00\x00\x00\x00\x16蕘\x00\x00\x00\x00\x18\"" + - "a\xa0\x00\x00\x00\x00\x18\xc8w\x98\x00\x00\x00\x00\x1a\x02C\xa0\x00\x00\x00\x00\x1a\xa8Y\x98\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\x88;\x98\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1eh\x1d\x98\x00\x00" + - "\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 G\xff\x98\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"1\x1c\x18\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$\x10\xfe\x18\x00\x00\x00\x00%J\xca \x00\x00\x00\x00%\xf0" + - "\xe0\x18\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xd0\xc2\x18\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\xff\xffj8\x00\x00\xff\xfflX\x00\x04\xff\xffs" + - "`\x00\n\xff\xffzh\x01\x0eLMT\x00-1030\x00-10\x00-0930\x00\n<-10>10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RD6\x83\xa1\x8b\x00\x00\x00" + - "\x8b\x00\x00\x00\x11\x00\x1c\x00Pacific/MarquesasUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x06\x00\x00\x00\x15\xff\xff\xff\xff\x14\xe1\xc5\xcc\xff\xff\xff\xff~6-L\xff\xff\xff\xff\xcb7\x95\xe0\xff\xff\xff\xff\xd0.\x89\xf0\xff\xff\xff" + + "\xff\xec7\xbe\x00\xff\xff\xff\xff\xef6\xf8\xf0\xff\xff\xff\xff\xfb\x9b\x00\x00\xff\xff\xff\xff\xfe?'\x8c\xff\xff\xff\xff\xff\x01\x1e\x00\xff\xff\xff\xff\xff]X\xf0\x00\x00\x00\x00\x00\x97,\x00\x00\x00\x00\x00\x01Fu" + + "p\x00\x00\x00\x00\x02w\x0e\x00\x00\x00\x00\x00\x03&Wp\x00\x00\x00\x00\ap\x97\x00\x00\x00\x00\x00\a\xcc\xd1\xf0\x00\x00\x00\x00\f\b\x91\x00\x00\x00\x00\x00\f|\x87,\x00\x00\x00\x00\r\xbf\x94\x80\x00\x00\x00" + + "\x00\x0ee\xa3p\x00\x00\x00\x00:C^`\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\xff\xff64\x00\x00\x00\x00\x87\xb4\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\b\x00\x00" + + "\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00GST\x00+09\x00GDT\x00ChST\x00\nChST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU?X'\x8e\x96\x04" + + "\x00\x00\x96\x04\x00\x00\x0e\x00\x1c\x00Pacific/EasterUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xffi\x87B\b\xff\xff\xff\xff\xb9\xc7@\x88\xff\xff\xff\xff\xfd\xd1<@\xff\xff\xff\xff\xfe\x92\xfa\xb0\xff" + + "\xff\xff\xff\xff\xcc\xcd\xc0\x00\x00\x00\x00\x00rܰ\x00\x00\x00\x00\x01uP\xc0\x00\x00\x00\x00\x02@I\xb0\x00\x00\x00\x00\x03U2\xc0\x00\x00\x00\x00\x04 +\xb0\x00\x00\x00\x00\x05>O@\x00\x00\x00\x00\x06" + + "\x00\r\xb0\x00\x00\x00\x00\a\v\xbc@\x00\x00\x00\x00\a\xdf\xef\xb0\x00\x00\x00\x00\b\xfe\x13@\x00\x00\x00\x00\t\xbfѰ\x00\x00\x00\x00\n\xdd\xf5@\x00\x00\x00\x00\v\xa8\xee0\x00\x00\x00\x00\f\xbd\xd7@\x00" + + "\x00\x00\x00\r\x88\xd00\x00\x00\x00\x00\x0e\x9d\xb9@\x00\x00\x00\x00\x0fh\xb20\x00\x00\x00\x00\x10\x86\xd5\xc0\x00\x00\x00\x00\x11H\x940\x00\x00\x00\x00\x12f\xb7\xc0\x00\x00\x00\x00\x13(v0\x00\x00\x00\x00\x14" + + "F\x99\xc0\x00\x00\x00\x00\x15\x11\x92\xb0\x00\x00\x00\x00\x16&{\xc0\x00\x00\x00\x00\x16\xf1t\xb0\x00\x00\x00\x00\x18\x06]\xc0\x00\x00\x00\x00\x18\xd1V\xb0\x00\x00\x00\x00\x19\xe6?\xc0\x00\x00\x00\x00\x1a\xb18\xb0\x00" + + "\x00\x00\x00\x1b\xcf\\@\x00\x00\x00\x00\x1c\x91\x1a\xb0\x00\x00\x00\x00\x1d\xaf>@\x00\x00\x00\x00\x1ep\xfc\xb0\x00\x00\x00\x00\x1f\x8f @\x00\x00\x00\x00 \x7f\x030\x00\x00\x00\x00!o\x02@\x00\x00\x00\x00\"" + + "9\xfb0\x00\x00\x00\x00#N\xe4@\x00\x00\x00\x00$\x19\xdd0\x00\x00\x00\x00%8\x00\xc0\x00\x00\x00\x00%\xf9\xbf0\x00\x00\x00\x00&\xf2\xf8\xc0\x00\x00\x00\x00'١0\x00\x00\x00\x00(\xf7\xc4\xc0\x00" + + "\x00\x00\x00)½\xb0\x00\x00\x00\x00*צ\xc0\x00\x00\x00\x00+\xa2\x9f\xb0\x00\x00\x00\x00,\xb7\x88\xc0\x00\x00\x00\x00-\x82\x81\xb0\x00\x00\x00\x00.\x97j\xc0\x00\x00\x00\x00/bc\xb0\x00\x00\x00\x000" + + "\x80\x87@\x00\x00\x00\x001BE\xb0\x00\x00\x00\x002`i@\x00\x00\x00\x003=\xd70\x00\x00\x00\x004@K@\x00\x00\x00\x005\vD0\x00\x00\x00\x006\r\xb8@\x00\x00\x00\x007\x06հ\x00" + + "\x00\x00\x008\x00\x0f@\x00\x00\x00\x008\xcb\b0\x00\x00\x00\x009\xe9+\xc0\x00\x00\x00\x00:\xaa\xea0\x00\x00\x00\x00;\xc9\r\xc0\x00\x00\x00\x00<\x8a\xcc0\x00\x00\x00\x00=\xa8\xef\xc0\x00\x00\x00\x00>" + + "j\xae0\x00\x00\x00\x00?\x88\xd1\xc0\x00\x00\x00\x00@Sʰ\x00\x00\x00\x00Ah\xb3\xc0\x00\x00\x00\x00B3\xac\xb0\x00\x00\x00\x00CH\x95\xc0\x00\x00\x00\x00D\x13\x8e\xb0\x00\x00\x00\x00E1\xb2@\x00" + + "\x00\x00\x00E\xf3p\xb0\x00\x00\x00\x00G\x11\x94@\x00\x00\x00\x00G\xef\x020\x00\x00\x00\x00H\xf1v@\x00\x00\x00\x00I\xbco0\x00\x00\x00\x00J\xd1X@\x00\x00\x00\x00K\xb8\x00\xb0\x00\x00\x00\x00L" + + "\xb1:@\x00\x00\x00\x00M\xc6\a0\x00\x00\x00\x00NP\x82\xc0\x00\x00\x00\x00O\x9c\xae\xb0\x00\x00\x00\x00PB\xd9\xc0\x00\x00\x00\x00Q|\x90\xb0\x00\x00\x00\x00R+\xf6@\x00\x00\x00\x00S\\r\xb0\x00" + + "\x00\x00\x00T\v\xd8@\x00\x00\x00\x00W7\xe60\x00\x00\x00\x00W\xaf\xec\xc0\x00\x00\x00\x00Y\x17\xc80\x00\x00\x00\x00Y\x8f\xce\xc0\x00\x00\x00\x00Z\xf7\xaa0\x00\x00\x00\x00[o\xb0\xc0\x00\x00\x00\x00\\" + + "\xa9g\xb0\x00\x00\x00\x00]t|\xc0\x00\x00\x00\x00^\x89I\xb0\x00\x00\x00\x00_T^\xc0\x00\x00\x00\x00`i+\xb0\x00\x00\x00\x00a4@\xc0\x00\x00\x00\x00bI\r\xb0\x00\x00\x00\x00c\x1d]@\x00" + + "\x00\x00\x00d(\xef\xb0\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + + "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\xff\xff\x99" + + "x\x00\x00\xff\xff\x99x\x00\x04\xff\xff\xab\xa0\x01\b\xff\xff\x9d\x90\x00\f\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\x10LMT\x00EMT\x00-06\x00-07\x00-05\x00\n<-06>6" + + "<-05>,M9.1.6/22,M4.1.6/22\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Paci" + + "fic/MajuroUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x1c\x00Pacific/TrukUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\r\xff\xff\xff\xffV\xb6Z\b\xff\xff\xff\xffr\xed\xa4\x90\x01\x02\x00\x00\x89\xf8\x00" + + "\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\tLMT\x00PMMT\x00+10\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x8a|\xdcU\x99\x00\x00\x00\x99\x00" + + "\x00\x00\x0f\x00\x1c\x00Pacific/FakaofoUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff~7U\x88\x00\x00\x00\x00N\xfd\x99\xb0\x01\x02\xff\xff_x\x00\x00\xff\xffeP\x00\x04\x00\x00\xb6\xd0\x00\b" + + "LMT\x00-11\x00+13\x00\n<+13>-13\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUY5\x1a6\xf7\x00\x00\x00\xf7\x00\x00\x00\x0f\x00\x1c\x00Pacific/" + + "NorfolkUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x06\x00\x00\x00\x06\x00\x00\x00\x1e\xff\xff\xff\xff~6\x17\x88\xff\xff\xff\xff\xdcA\xf8\x80\x00\x00\x00\x00\t\x0f\xcah\x00\x00\x00\x00\t\xb5\xe7h\x00\x00\x00\x00V\x0f\xe6h\x00\x00\x00\x00]\x98\xaf\xf0\x01\x02" + + "\x03\x02\x04\x05\x00\x00\x9dx\x00\x00\x00\x00\x9d\x80\x00\x04\x00\x00\xa1\xb8\x00\n\x00\x00\xaf\xc8\x01\x10\x00\x00\x9a\xb0\x00\x16\x00\x00\xa8\xc0\x01\x1aLMT\x00+1112\x00+1130\x00+123" + + "0\x00+11\x00+12\x00\n<+11>-11<+12>,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9e\x7f\xab\x95" + + "V\x01\x00\x00V\x01\x00\x00\r\x00\x1c\x00Pacific/EfateUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\n\xff\xff\xff\xff\x94PLH\x01\xff\xff}8\x00\x00\xff\xffzh\x00\x04LMT\x00-0930\x00\n" + - "<-0930>9:30\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x1c\x00Pacific/TarawaUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff" + - "\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00" + - "\xc3\x00\x00\x00\f\x00\x1c\x00Pacific/TrukUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\x14\xe1\xbf4\xff\xff\xff\xff~6&\xb4\xff\xff\xff\xff\x98\x11\xa3\xe0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc9" + - "\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\x01\x02\x03\x02\x03\x02\xff\xff<\xcc\x00\x00\x00\x00\x8eL\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\bLMT\x00+10\x00+09\x00\n<+10>-" + - "10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x81\xeb\xb8m\xaf\x00\x00\x00\xaf\x00\x00\x00\f\x00\x1c\x00Pacific/NiueUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00" + - "\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x14\xff\xff\xff\xff~7TL\xff\xff\xff\xff" + - "\xdcC5`\x00\x00\x00\x00\x10t\xca8\x01\x02\x03\xff\xff`\xb4\x00\x00\xff\xff`\xa0\x00\x04\xff\xff^H\x00\n\xff\xffeP\x00\x10LMT\x00-1120\x00-1130\x00-11\x00\n" + - "<-11>11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\r\x00\x1c\x00Pacific/SamoaUT\t\x00\x03\x15\xac\x0e`" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + - "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xffn=" + - "\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RF" + - "I\xfe\x14^\x01\x00\x00^\x01\x00\x00\f\x00\x1c\x00Pacific/GuamUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x06\x00\x00\x00\x15\xff\xff\xff\xff\x14\xe1\xc5\xcc\xff\xff\xff\xff~6-L\xff\xff\xff\xff\xcb7\x95\xe0\xff\xff\xff\xff\xd0." + - "\x89\xf0\xff\xff\xff\xff\xec7\xbe\x00\xff\xff\xff\xff\xef6\xf8\xf0\xff\xff\xff\xff\xfb\x9b\x00\x00\xff\xff\xff\xff\xfe?'\x8c\xff\xff\xff\xff\xff\x01\x1e\x00\xff\xff\xff\xff\xff]X\xf0\x00\x00\x00\x00\x00\x97,\x00\x00\x00" + - "\x00\x00\x01Fup\x00\x00\x00\x00\x02w\x0e\x00\x00\x00\x00\x00\x03&Wp\x00\x00\x00\x00\ap\x97\x00\x00\x00\x00\x00\a\xcc\xd1\xf0\x00\x00\x00\x00\f\b\x91\x00\x00\x00\x00\x00\f|\x87,\x00\x00\x00\x00\r\xbf" + - "\x94\x80\x00\x00\x00\x00\x0ee\xa3p\x00\x00\x00\x00:C^`\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\xff\xff64\x00\x00\x00\x00\x87\xb4\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~" + - "\x90\x00\b\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00GST\x00+09\x00GDT\x00ChST\x00\nChST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RF" + - "I\xfe\x14^\x01\x00\x00^\x01\x00\x00\x0e\x00\x1c\x00Pacific/SaipanUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x92\xf5´\x00\x00\x00\x00\ay\x99@\x00\x00\x00\x00\a\xfa\xcc@\x00\x00\x00\x00\x19\xd2\xf7\xd0" + + "\x00\x00\x00\x00\x1a\xc2\xda\xc0\x00\x00\x00\x00\x1b\xb2\xd9\xd0\x00\x00\x00\x00\x1c\xa2\xbc\xc0\x00\x00\x00\x00\x1d\x9b\xf6P\x00\x00\x00\x00\x1e\x82\x9e\xc0\x00\x00\x00\x00\x1f{\xd8P\x00\x00\x00\x00 k\xbb@\x00\x00\x00\x00" + + "![\xbaP\x00\x00\x00\x00\"K\x9d@\x00\x00\x00\x00#;\x9cP\x00\x00\x00\x00$+\x7f@\x00\x00\x00\x00%\x1b~P\x00\x00\x00\x00&\va@\x00\x00\x00\x00&\xfb`P\x00\x00\x00\x00'\xebC@" + + "\x00\x00\x00\x00(\xe4|\xd0\x00\x00\x00\x00)\x81Q@\x00\x00\x00\x00*\xe9H\xd0\x00\x00\x00\x00+a3@\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x9d\xcc\x00" + + "\x00\x00\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\bLMT\x00+12\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xb7\xef\x97\xc6\xc6\x00\x00\x00\xc6\x00\x00" + + "\x00\x0e\x00\x1c\x00Pacific/NoumeaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x92\xf5\xc4t\x00\x00\x00\x00\x0e\xe6\xbaP\x00\x00\x00\x00\x0fV\xbb\xc0\x00\x00\x00\x00\x10ƜP\x00\x00\x00\x00\x117" + + "\xef@\x00\x00\x00\x002\xa0K\xf0\x00\x00\x00\x003\x18Dp\x02\x01\x02\x01\x02\x01\x02\x00\x00\x9c\f\x00\x00\x00\x00\xa8\xc0\x01\x04\x00\x00\x9a\xb0\x00\bLMT\x00+12\x00+11\x00\n<+11" + + ">-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x96\xc5FF(\x03\x00\x00(\x03\x00\x00\x0f\x00\x1c\x00Pacific/ChathamUT\t\x00\x03\xaf\x16\xf5b\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + + "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x04\x00\x00\x00\x16\xff\xff\xff\xffA\xb7D" + + "\x84\xff\xff\xff\xff\xd2ږ\xbc\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r~\xde\xe0\x00\x00\x00" + + "\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00\x00\x00\x00\x14\xfef" + + "\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b\xe1\f`\x00\x00\x00" + + "\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00\x00\x00\x00#i\xce" + + "\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)\xc2\x06\xe0\x00\x00\x00" + + "\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00\x00\x00\x001J\xc9" + + "`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007\xf6\x0f\xe0\x00\x00\x00" + + "\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00\x00\x00\x00?~\xd2" + + "`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E\xfb\xf4`\x00\x00\x00" + + "\x00F\xfeZ`\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x00\x00\xab\xfc\x00\x00\x00\x00\xacD\x00\x04\x00\x00\xc1\\\x01\n\x00\x00\xb3L\x00\x10LMT\x00+1215\x00+1345\x00+1245\x00" + + "\n<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "\xe2;Z\xf7\xb7\x00\x00\x00\xb7\x00\x00\x00\r\x00\x1c\x00Pacific/NauruUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x06\x00\x00\x00\x15\xff\xff\xff\xff\x14\xe1\xc5\xcc\xff\xff\xff\xff~6-L\xff\xff\xff\xff\xcb7\x95\xe0\xff\xff\xff\xff" + - "\xd0.\x89\xf0\xff\xff\xff\xff\xec7\xbe\x00\xff\xff\xff\xff\xef6\xf8\xf0\xff\xff\xff\xff\xfb\x9b\x00\x00\xff\xff\xff\xff\xfe?'\x8c\xff\xff\xff\xff\xff\x01\x1e\x00\xff\xff\xff\xff\xff]X\xf0\x00\x00\x00\x00\x00\x97,\x00" + - "\x00\x00\x00\x00\x01Fup\x00\x00\x00\x00\x02w\x0e\x00\x00\x00\x00\x00\x03&Wp\x00\x00\x00\x00\ap\x97\x00\x00\x00\x00\x00\a\xcc\xd1\xf0\x00\x00\x00\x00\f\b\x91\x00\x00\x00\x00\x00\f|\x87,\x00\x00\x00\x00" + - "\r\xbf\x94\x80\x00\x00\x00\x00\x0ee\xa3p\x00\x00\x00\x00:C^`\x01\x02\x03\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x04\x02\x05\xff\xff64\x00\x00\x00\x00\x87\xb4\x00\x00\x00\x00\x8c\xa0\x00\x04\x00" + - "\x00~\x90\x00\b\x00\x00\x9a\xb0\x01\f\x00\x00\x8c\xa0\x00\x10LMT\x00GST\x00+09\x00GDT\x00ChST\x00\nChST-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9" + - "R\x81\xe3w\n\xaf\x00\x00\x00\xaf\x00\x00\x00\x11\x00\x1c\x00Pacific/GalapagosUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\f\xff\xff\xff\xff\xb6\xa4L\x80\x00\x00\x00\x00\x1e\x18\xc4P\x00\x00\x00\x00+\x17\n" + - "\xe0\x00\x00\x00\x00+q\xf4P\x01\x03\x02\x03\xff\xff\xac\x00\x00\x00\xff\xff\xb9\xb0\x00\x04\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\bLMT\x00-05\x00-06\x00\n<-06>6\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9a\xf2:F\xc9\x00\x00\x00\xc9\x00\x00\x00\x14\x00\x1c\x00Pacific/BougainvilleUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x15\xff\xff\xff\xffV\xb6R(\xff\xff" + - "\xff\xffr\xed\xa4\x90\xff\xff\xff\xff\xccC6`\xff\xff\xff\xff\xd2+l\xf0\x00\x00\x00\x00T\x9e׀\x01\x02\x03\x02\x04\x00\x00\x91\xd8\x00\x00\x00\x00\x89\xf0\x00\x04\x00\x00\x8c\xa0\x00\t\x00\x00~\x90\x00\r\x00" + - "\x00\x9a\xb0\x00\x11LMT\x00PMMT\x00+10\x00+09\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R߃\xa0_\x86\x00\x00\x00\x86\x00" + - "\x00\x00\f\x00\x1c\x00Pacific/WakeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x18\xcc\x01\x00\x00\x9c4\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-12\n" + - "PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RP:\xc0\x8c\xed\x00\x00\x00\xed\x00\x00\x00\x11\x00\x1c\x00Pacific/TongatapuUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + - "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff~6\a\xb8\xff\xff" + - "\xff\xff\xc9sB\x90\x00\x00\x00\x007\xfbG\xd0\x00\x00\x00\x008\xd3}\xd0\x00\x00\x00\x00:\x04\bP\x00\x00\x00\x00:r\xb8@\x00\x00\x00\x00;\xe3\xeaP\x00\x00\x00\x00-13\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00\xc3\x00\x00\x00\r\x00\x1c\x00Pacific/ChuukUT\t\x00\x03" + - "\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\f\xff\xff" + - "\xff\xff\x14\xe1\xbf4\xff\xff\xff\xff~6&\xb4\xff\xff\xff\xff\x98\x11\xa3\xe0\xff\xff\xff\xff\xa09\xf9\xf0\xff\xff\xff\xff\xc9\xea\n`\xff\xff\xff\xff\xd2\x11\x0e\xf0\x01\x02\x03\x02\x03\x02\xff\xff<\xcc\x00\x00\x00\x00" + - "\x8eL\x00\x00\x00\x00\x8c\xa0\x00\x04\x00\x00~\x90\x00\bLMT\x00+10\x00+09\x00\n<+10>-10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xea\xc1\xdaυ\x00\x00\x00" + - "\x85\x00\x00\x00\x0e\x00\x1c\x00Pacific/TahitiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94PU\xb8\x01\xff\xffs\xc8\x00\x00\xff\xffs`\x00\x04LMT\x00-10\x00\n<-10>" + - "10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x13\x00\x1c\x00Pacific/GuadalcanalUT\t\x00\x03\x15\xac\x0e" + - "`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" + - "\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94" + - "O3\x8c\x01\x00\x00\x95\xf4\x00\x00\x00\x00\x9a\xb0\x00\x04LMT\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00" + - "\x10\x00\x1c\x00Pacific/AucklandUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xff\xa3\xe7+\x04\xff\xff\xff\xff̐\xe9\xc8\xff\xff\xff\xff\xd2C'\xf0\x00\x00\x00\x00" + + "\x11!\xa8\xe8\x01\x02\x01\x03\x00\x00\x9c|\x00\x00\x00\x00\xa1\xb8\x00\x04\x00\x00~\x90\x00\n\x00\x00\xa8\xc0\x00\x0eLMT\x00+1130\x00+09\x00+12\x00\n<+12>-12\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUcF/.\xac\x01\x00\x00\xac\x01\x00\x00\f\x00\x1c\x00Pacific/FijiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" + + "\x00TZif3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x00\x03\x00\x00\x00\f\xff\xff\xff\xff\x9a\x13\xb1\xc0\x00\x00\x00\x006;\x17" + + "\xe0\x00\x00\x00\x006\xd7\xfa`\x00\x00\x00\x008$4`\x00\x00\x00\x008\xb7\xdc`\x00\x00\x00\x00K\x11,\xe0\x00\x00\x00\x00K\xae\x0f`\x00\x00\x00\x00L\xc2\xea`\x00\x00\x00\x00MrA\xe0\x00\x00\x00" + + "\x00N\xa2\xcc`\x00\x00\x00\x00O\x1a\xc4\xe0\x00\x00\x00\x00P\x82\xae`\x00\x00\x00\x00P\xfa\xa6\xe0\x00\x00\x00\x00Rk\xca\xe0\x00\x00\x00\x00R\xdaz\xd0\x00\x00\x00\x00TT\xe7`\x00\x00\x00\x00T\xbaj" + + "\xe0\x00\x00\x00\x00V4\xc9`\x00\x00\x00\x00V\x9aL\xe0\x00\x00\x00\x00X\x1d\xe5\xe0\x00\x00\x00\x00Xz.\xe0\x00\x00\x00\x00Y\xfd\xc7\xe0\x00\x00\x00\x00ZZ\x10\xe0\x00\x00\x00\x00[ݩ\xe0\x00\x00\x00" + + "\x00\\9\xf2\xe0\x00\x00\x00\x00]\xc6\xc6`\x00\x00\x00\x00^\x19\xd4\xe0\x00\x00\x00\x00_\xde\a`\x00\x00\x00\x00`\x02\xf1`\x00\x00\x00\x00co\xa6\xe0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\xa7\xc0\x00\x00\x00\x00\xb6\xd0\x01\x04\x00\x00\xa8\xc0\x00\bLMT\x00+13\x00+12\x00\n<+12>-12<+13>," + + "M11.2.0,M1.2.3/99\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\r\x00\x1c\x00Pacific/Samo" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03" + + "\x00\x00\x00\b\xff\xff\xff\xffn=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n" + + "\x00\x00\x00\x00\x00\bv\vU\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x1c\x00Pacific/JohnstonUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff\xff\xff\xff\xbb\x05CH" + + "\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff\xffՍsH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00\xff\xfflX\x00\x04\xff" + + "\xffzh\x01\b\xff\xffzh\x01\f\xff\xffzh\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00HWT\x00HPT\x00\nHST10\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vU\x91\xd60\f\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x1c\x00Pacific/NiueUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x0e\xff\xff\xff\xffߡjL\xff\xff\xff\xff\xf5\xa6\xb8`\x01\x02\xff\xff`\xb4\x00\x00\xff" + + "\xff`\xa0\x00\x04\xff\xffeP\x00\nLMT\x00-1120\x00-11\x00\n<-11>11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00" + + "\x10\x00\x1c\x00Pacific/FunafutiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x06\x00\x00\x00\x13\xff\xff\xff\xffA\xb7L\xa8\xff\xff\xff\xff\xb0\xb4\xb2\xe8\xff\xff\xff\xff\xb1Q\x87X\xff\xff\xff\xff\xb2x\xe5h\xff\xff\xff\xff\xb3" + - "C\xe5`\xff\xff\xff\xff\xb4X\xc7h\xff\xff\xff\xff\xb5#\xc7`\xff\xff\xff\xff\xb68\xa9h\xff\xff\xff\xff\xb7\x03\xa9`\xff\xff\xff\xff\xb8\x18\x8bh\xff\xff\xff\xff\xb8\xec\xc5\xe0\xff\xff\xff\xff\xb9\xf8mh\xff" + - "\xff\xff\xff\xba̧\xe0\xff\xff\xff\xff\xbb\xd8Oh\xff\xff\xff\xff\xbc\xe3\xe8\xe0\xff\xff\xff\xff\xbd\xae\xf6\xe8\xff\xff\xff\xff\xbe\xc3\xca\xe0\xff\xff\xff\xff\xbf\x8e\xd8\xe8\xff\xff\xff\xff\xc0\xa3\xac\xe0\xff\xff\xff\xff\xc1" + - "n\xba\xe8\xff\xff\xff\xff\u0083\x8e\xe0\xff\xff\xff\xff\xc3N\x9c\xe8\xff\xff\xff\xff\xc4cp\xe0\xff\xff\xff\xff\xc5.~\xe8\xff\xff\xff\xff\xc6L\x8d`\xff\xff\xff\xff\xc7\x0e`\xe8\xff\xff\xff\xff\xc8,o`\xff" + - "\xff\xff\xff\xc8\xf7}h\xff\xff\xff\xff\xd2ښ@\x00\x00\x00\x00\t\x18\xfd\xe0\x00\x00\x00\x00\t\xac\xa5\xe0\x00\x00\x00\x00\n\xef\xa5`\x00\x00\x00\x00\v\x9e\xfc\xe0\x00\x00\x00\x00\f\xd8\xc1\xe0\x00\x00\x00\x00\r" + - "~\xde\xe0\x00\x00\x00\x00\x0e\xb8\xa3\xe0\x00\x00\x00\x00\x0f^\xc0\xe0\x00\x00\x00\x00\x10\x98\x85\xe0\x00\x00\x00\x00\x11>\xa2\xe0\x00\x00\x00\x00\x12xg\xe0\x00\x00\x00\x00\x13\x1e\x84\xe0\x00\x00\x00\x00\x14XI\xe0\x00" + - "\x00\x00\x00\x14\xfef\xe0\x00\x00\x00\x00\x168+\xe0\x00\x00\x00\x00\x16\xe7\x83`\x00\x00\x00\x00\x18!H`\x00\x00\x00\x00\x18\xc7e`\x00\x00\x00\x00\x1a\x01*`\x00\x00\x00\x00\x1a\xa7G`\x00\x00\x00\x00\x1b" + - "\xe1\f`\x00\x00\x00\x00\x1c\x87)`\x00\x00\x00\x00\x1d\xc0\xee`\x00\x00\x00\x00\x1eg\v`\x00\x00\x00\x00\x1f\xa0\xd0`\x00\x00\x00\x00 F\xed`\x00\x00\x00\x00!\x80\xb2`\x00\x00\x00\x00\"0\t\xe0\x00" + - "\x00\x00\x00#i\xce\xe0\x00\x00\x00\x00$\x0f\xeb\xe0\x00\x00\x00\x00%.\x01`\x00\x00\x00\x00&\x02B\xe0\x00\x00\x00\x00'\r\xe3`\x00\x00\x00\x00'\xe2$\xe0\x00\x00\x00\x00(\xed\xc5`\x00\x00\x00\x00)" + - "\xc2\x06\xe0\x00\x00\x00\x00*ͧ`\x00\x00\x00\x00+\xab#`\x00\x00\x00\x00,\xad\x89`\x00\x00\x00\x00-\x8b\x05`\x00\x00\x00\x00.\x8dk`\x00\x00\x00\x00/j\xe7`\x00\x00\x00\x000mM`\x00" + - "\x00\x00\x001J\xc9`\x00\x00\x00\x002Vi\xe0\x00\x00\x00\x003*\xab`\x00\x00\x00\x0046K\xe0\x00\x00\x00\x005\n\x8d`\x00\x00\x00\x006\x16-\xe0\x00\x00\x00\x006\xf3\xa9\xe0\x00\x00\x00\x007" + - "\xf6\x0f\xe0\x00\x00\x00\x008Ӌ\xe0\x00\x00\x00\x009\xd5\xf1\xe0\x00\x00\x00\x00:\xb3m\xe0\x00\x00\x00\x00;\xbf\x0e`\x00\x00\x00\x00<\x93O\xe0\x00\x00\x00\x00=\x9e\xf0`\x00\x00\x00\x00>s1\xe0\x00" + - "\x00\x00\x00?~\xd2`\x00\x00\x00\x00@\\N`\x00\x00\x00\x00A^\xb4`\x00\x00\x00\x00B<0`\x00\x00\x00\x00C>\x96`\x00\x00\x00\x00D\x1c\x12`\x00\x00\x00\x00E\x1ex`\x00\x00\x00\x00E" + - "\xfb\xf4`\x00\x00\x00\x00F\xfeZ`\x02\x01\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05" + - "\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x05\x04\x00\x00\xa3\xd8\x00\x00\x00\x00\xaf\xc8\x01\x04\x00" + - "\x00\xa1\xb8\x00\t\x00\x00\xa8\xc0\x01\x04\x00\x00\xb6\xd0\x01\x0e\x00\x00\xa8\xc0\x00\x04LMT\x00NZST\x00NZMT\x00NZDT\x00\nNZST-12NZDT,M9.5." + - "0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RY5\x1a6\xf7\x00\x00\x00\xf7\x00\x00\x00\x0f\x00\x1c\x00Pacific/NorfolkUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x1e\xff" + - "\xff\xff\xff~6\x17\x88\xff\xff\xff\xff\xdcA\xf8\x80\x00\x00\x00\x00\t\x0f\xcah\x00\x00\x00\x00\t\xb5\xe7h\x00\x00\x00\x00V\x0f\xe6h\x00\x00\x00\x00]\x98\xaf\xf0\x01\x02\x03\x02\x04\x05\x00\x00\x9dx\x00\x00\x00" + - "\x00\x9d\x80\x00\x04\x00\x00\xa1\xb8\x00\n\x00\x00\xaf\xc8\x01\x10\x00\x00\x9a\xb0\x00\x16\x00\x00\xa8\xc0\x01\x1aLMT\x00+1112\x00+1130\x00+1230\x00+11\x00+12\x00\n" + - "<+11>-11<+12>,M10.1.0,M4.1.0/3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R>\xfe垛\x03\x00\x00\x9b\x03\x00\x00\x06\x00\x1c" + - "\x00PolandUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00R\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffV\xb6\xd0P\xff\xff\xff\xff\x99\xa8*\xd0\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff" + - "\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xa0\x9a\xb6\x00\xff\xff\xff\xff\xa1e\xbd\x00\xff\xff\xff\xff\xa6}|`\xff\xff\xff\xff\xc8v\xde\x10\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ" + - "\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЄ\xba\x00\xff\xff\xff\xffѕ\x92p\xff\xff\xff\xffҊ\xbb`\xff\xff\xff\xff\xd3b\xffp\xff\xff\xff\xff\xd4K#\x90\xff\xff" + - "\xff\xff\xd5^\xad\x10\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\xff\xff\xff\xff\xe8T\xd2\x00\xff\xff\xff\xff\xe8\xf1" + - "\xb4\x80\xff\xff\xff\xff\xe9᥀\xff\xff\xff\xff\xeaі\x80\xff\xff\xff\xff\xec\x14\x96\x00\xff\xff\xff\xff캳\x00\xff\xff\xff\xff\xed\xaa\xa4\x00\xff\xff\xff\xff\ue695\x00\xff\xff\xff\xff\xef\xd4Z\x00\xff\xff" + - "\xff\xff\xf0zw\x00\xff\xff\xff\xff\xf1\xb4<\x00\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3\x94\x1e\x00\xff\xff\xff\xff\xf4:;\x00\xff\xff\xff\xff\xf5}:\x80\xff\xff\xff\xff\xf6\x1a\x1d\x00\x00\x00\x00\x00\r\xa4" + - "U\x80\x00\x00\x00\x00\x0e\x8b\f\x00\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10t(\x80\x00\x00\x00\x00\x11d\x19\x80\x00\x00\x00\x00\x12T\n\x80\x00\x00\x00\x00\x13M6\x00\x00\x00\x00\x00\x143\xec\x80\x00\x00" + - "\x00\x00\x15#݀\x00\x00\x00\x00\x16\x13\u0380\x00\x00\x00\x00\x17\x03\xbf\x80\x00\x00\x00\x00\x17\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00\x1b\xbc" + - "\xaf\x00\x00\x00\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LT\x10\x00\x00" + - "\x00\x00#\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff" + - "\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR " + - "\x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00" + - "&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q" + - "\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9RŭV\xad\xb7\x03\x00\x00\xb7\x03\x00\x00" + - "\a\x00\x1c\x00PST8PDTUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#" + - "\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00" + - "\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0" + - "\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00" + - "\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15I" + - "T \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00" + - "\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j" + - "\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00" + - "\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g" + - "\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00" + - "\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b" + - "\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x01\x00" + - "\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01" + - "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\x00\xff\xff\x9d\x90\x01\b\xff\xff\x9d\x90\x01\fPDT\x00PST\x00PW" + - "T\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\x03\x00\x1c\x00" + - "ROCUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00" + - "\x00\x04\x00\x00\x00\x10\xff\xff\xff\xfft\xce\xf0\x18\xff\xff\xff\xff\xc3UI\x80\xff\xff\xff\xff\xd2TY\x80\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L" + - "\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9\xe7\x99\xf0\xff\xff\xff\xff\xda\xff&\x00\xff\xff\xff\xff\xdb\xc8\xcdp\xff\xff\xff\xff\xdc\xe0Y\x80\xff\xff" + - "\xff\xffݪ\x00\xf0\xff\xff\xff\xff\xders\x00\xff\xff\xff\xffߵdp\xff\xff\xff\xff\xe0|\x85\x00\xff\xff\xff\xffᖗ\xf0\xff\xff\xff\xff\xe2]\xb8\x80\xff\xff\xff\xff\xe3w\xcbp\xff\xff\xff\xff\xe4>" + - "\xec\x00\xff\xff\xff\xff\xe50 p\xff\xff\xff\xff\xe6!q\x00\xff\xff\xff\xff\xe7\x12\xa5p\xff\xff\xff\xff\xe8\x02\xa4\x80\xff\xff\xff\xff\xe8\xf3\xd8\xf0\xff\xff\xff\xff\xe9\xe3\xd8\x00\xff\xff\xff\xff\xea\xd5\fp\xff\xff" + - "\xff\xff\xeb\xc5\v\x80\xff\xff\xff\xff\xec\xb6?\xf0\xff\xff\xff\xff\xed\xf7\xfc\x00\xff\xff\xff\xff\xee\x98\xc4\xf0\xff\xff\xff\xff\xef\xd9/\x80\xff\xff\xff\xff\xf0y\xf8p\x00\x00\x00\x00\a\xfcV\x00\x00\x00\x00\x00\b\xed" + - "\x8ap\x00\x00\x00\x00\t݉\x80\x00\x00\x00\x00\nν\xf0\x00\x00\x00\x00\x11ۡ\x80\x00\x00\x00\x00\x12T\xddp\x01\x02\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03" + - "\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x00\x00q\xe8\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x01\fLMT\x00CST\x00JST\x00CDT\x00\nCST-" + - "8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\x03\x00\x1c\x00ROKUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff\xff\xff\x8b\xd7\xf0x\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2C" + - "'\xf0\xff\xff\xff\xff\xd7e\x8fp\xff\xff\xff\xff\xd7\xee\x9d`\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd-\xe0\xff\xff\xff\xff\xda\u05ca\xf0\xff\xff\xff\xffۭ\x0f\xe0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff" + - "\xff\xff\u074c\xf1\xe0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe4k\xb7\xf8\xff\xff\xff\xff\xe5\x13\x18h\xff\xff\xff\xff\xe6b\x03x\xff\xff\xff\xff\xe7\x11L\xe8\xff\xff\xff\xff\xe8/px\xff\xff\xff\xff\xe8\xe7" + - "\xf4h\xff\xff\xff\xff\xea\x0fRx\xff\xff\xff\xff\xea\xc7\xd6h\xff\xff\xff\xff\xeb\xef4x\xff\xff\xff\xff째h\xff\xff\xff\xff\xed\xcf\x16x\xff\xff\xff\xff\ue1dah\xff\xff\xff\xff\xf05qx\x00\x00" + - "\x00\x00 \xa3`\x90\x00\x00\x00\x00!ng\x90\x00\x00\x00\x00\"\x83B\x90\x00\x00\x00\x00#NI\x90\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03\x04\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x04\x03\x04\x03\x04\x00" + - "\x00w\b\x00\x00\x00\x00w\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x01\f\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x01\fLMT\x00KST\x00JST\x00KDT\x00\nKST-9\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\t\x00\x1c\x00SingaporeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif" + - "2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00 \xff\xff\xff\xff~6S\xa3\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff" + - "\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2Hm\xf0\x00\x00\x00\x00\x16\x91\xf5\b\x01\x02\x03\x04\x05\x06\x05\a\x00\x00a]\x00\x00\x00\x00" + - "a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18\x00\x00p\x80\x00\x1cLMT\x00SMT\x00+07\x00+0720\x00+0" + - "730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x06\x00\x1c\x00TurkeyUT\t" + - "\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s\x00\x00\x00\x06\x00\x00\x00\x19" + - "\xff\xff\xff\xffV\xb6\xc8\xd8\xff\xff\xff\xff\x90\x8b\xf5\x98\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9bվ\xd0\xff\xff\xff\xff\xa2ec\xe0\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80`\xff\xff\xff\xff" + - "\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\u007f\xd0\xff\xff\xff\xff\xaa((`\xff\xff\xff\xff\xaa\xe1\xfd\xd0\xff\xff\xff\xff\xab\xf9\x89\xe0\xff\xff\xff\xff\xac\xc31P\xff\xff\xff\xffȁ?\xe0" + - "\xff\xff\xff\xff\xc9\x01\x13P\xff\xff\xff\xff\xc9J\xf5`\xff\xff\xff\xff\xca\u0380P\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xd2k\tP\xff\xff\xff\xffӢ9`\xff\xff\xff\xff\xd4C\x02P\xff\xff\xff\xff" + - "\xd5L\r\xe0\xff\xff\xff\xff\xd6){\xd0\xff\xff\xff\xff\xd7+\xef\xe0\xff\xff\xff\xff\xd8\t]\xd0\xff\xff\xff\xff\xd9\x02\x97`\xff\xff\xff\xff\xd9\xe9?\xd0\xff\xff\xff\xff\xda\xeb\xb3\xe0\xff\xff\xff\xff\xdb\xd2\\P" + - "\xff\xff\xff\xff\xdc\xd4\xd0`\xff\xff\xff\xffݲ>P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b\xefP\xff\xff\xff\xff\xf5h\x06`\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00" + - "\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00\x00\x00\n\xf9^p\x00\x00\x00\x00\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00\x00\x00\r\xa4U\x80" + - "\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89\xb0p\x00\x00\x00\x00\x19ܰ\xe0\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6\xef\xf0\x00\x00\x00\x00" + - "\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0" + - "\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\tp\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00" + - "+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0" + - "\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x00" + - "9\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap" + - "\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00" + - "G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00\x00\x00N\xac\xa1\x90" + - "\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S8\xbe\x10\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00" + - "V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + - "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\r\x00\x00*0\x00\x11\x00\x008@\x01\x15LMT\x00IMT\x00E" + - "EST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00UCTUT" + - "\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + - "\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\t\x00\x1c\x00UniversalUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00" + - "\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x1c\x00US/UT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\n\x00\x1c\x00US/PacificUT\t\x00" + - "\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff" + - "\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2" + - "a&\x10\xff\xff\xff\xff\xd6\xfet\\\xff\xff\xff\xff\u0600\xad\x90\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdcޥ\x90\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x87\x90\xff" + - "\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6" + - "GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff\xff\xea\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff" + - "\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fА\xff\xff\xff\xff\xf2\u007f\xc1\x90\xff\xff\xff\xff\xf3o\xb2\x90\xff\xff\xff\xff\xf4" + - "_\xa3\x90\xff\xff\xff\xff\xf5O\x94\x90\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff" + - "\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02" + - "x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00" + - "\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10" + - "\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00" + - "\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e" + - "\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00" + - "\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00," + - "\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00" + - "\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:" + - "\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00" + - "\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x91&\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80" + - "\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK" + - "\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\n\x00\x1c\x00US/CentralUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff" + - "\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa2\xcbt\x00\xff\xff\xff\xff\xa3\x83\xf7\xf0\xff\xff\xff\xff\xa4EҀ\xff\xff\xff\xff\xa5c\xd9\xf0\xff\xff\xff\xff\xa6S\xd9" + - "\x00\xff\xff\xff\xff\xa7\x15\x97p\xff\xff\xff\xff\xa83\xbb\x00\xff\xff\xff\xff\xa8\xfe\xb3\xf0\xff\xff\xff\xff\xaa\x13\x9d\x00\xff\xff\xff\xff\xaaޕ\xf0\xff\xff\xff\xff\xab\xf3\u007f\x00\xff\xff\xff\xff\xac\xbew\xf0\xff\xff\xff" + - "\xff\xad\xd3a\x00\xff\xff\xff\xff\xae\x9eY\xf0\xff\xff\xff\xff\xaf\xb3C\x00\xff\xff\xff\xff\xb0~;\xf0\xff\xff\xff\xff\xb1\x9c_\x80\xff\xff\xff\xff\xb2gXp\xff\xff\xff\xff\xb3|A\x80\xff\xff\xff\xff\xb4G:" + - "p\xff\xff\xff\xff\xb5\\#\x80\xff\xff\xff\xff\xb6'\x1cp\xff\xff\xff\xff\xb7<\x05\x80\xff\xff\xff\xff\xb8\x06\xfep\xff\xff\xff\xff\xb9\x1b\xe7\x80\xff\xff\xff\xff\xb9\xe6\xe0p\xff\xff\xff\xff\xbb\x05\x04\x00\xff\xff\xff" + - "\xff\xbb\xc6\xc2p\xff\xff\xff\xff\xbc\xe4\xe6\x00\xff\xff\xff\xff\xbd\xaf\xde\xf0\xff\xff\xff\xff\xbe\xc4\xc8\x00\xff\xff\xff\xff\xbf\x8f\xc0\xf0\xff\xff\xff\xff\xc0Z\xd6\x00\xff\xff\xff\xff\xc1\xb0\x8fހ\x00\x00\x00" + - "\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7" + - "\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x05\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xad\xd4" + - "\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00CDT\x00CST\x00EST\x00CWT\x00CPT\x00\nCST" + - "6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\v\x00\x1c\x00US/Michiga" + - "nUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x06" + - "\x00\x00\x00\x18\xff\xff\xff\xff\x85\xbd\"[\xff\xff\xff\xff\x99<\x94\x00\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0" + - "\xff\xff\xff\xff\xfb3\x90\x8c\xff\xff\xff\xff\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00" + - "\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0" + - "\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00" + - "\x17)\v\xf0\x00\x00\x00\x00\x18\")`\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`" + - "\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00" + - "%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p" + - "\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x00" + - "3GI\xf0\x00\x00\x00\x004R\xdc`\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0" + - "\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00;۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00" + - "A\x84\u007f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + - "\x02\x05\x02\x05\xff\xff\xb2%\x00\x00\xff\xff\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14LMT\x00CST\x00EST\x00EWT\x00EPT\x00" + - "EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\v\x00\x1c\x00US" + - "/AleutianUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff~6\x12\xcc\x01\x00\x00\xa24\x00\x00\x00\x00\xa8\xc0\x00\x04LMT\x00+12\x00\n<+12>-1" + + "2\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x13\x00\x1c\x00Pacific/GuadalcanalUT\t\x00\x03\xaf\x16\xf5b" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + + "\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\b\xff\xff\xff\xff\x94O" + + "3\x8c\x01\x00\x00\x95\xf4\x00\x00\x00\x00\x9a\xb0\x00\x04LMT\x00+11\x00\n<+11>-11\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU>\xfe垛\x03\x00\x00\x9b\x03\x00\x00\x06" + + "\x00\x1c\x00PolandUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00!\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87Z^\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0" + - "\xff\xff\xff\xff\xfe\xb8qP\xff\xff\xff\xff\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00\x00\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00" + - "\x05Q4\xc0\x00\x00\x00\x00\x06A3\xd0\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t\xad\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0" + - "\x00\x00\x00\x00\f\xd9\xf7@\x00\x00\x00\x00\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00\x00\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00" + - "\x13i\x9cP\x00\x00\x00\x00\x14Y\u007f@\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18\"}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0" + - "\x00\x00\x00\x00\x1a+\" \x00\x00\x00\x00\x1a\xf2P\xc0\x00\x00\x00\x00\x1b\xe23\xb0\x00\x00\x00\x00\x1c\xd22\xc0\x00\x00\x00\x00\x1d\xc2\x15\xb0\x00\x00\x00\x00\x1e\xb2\x14\xc0\x00\x00\x00\x00\x1f\xa1\xf7\xb0\x00\x00\x00\x00" + - " vG@\x00\x00\x00\x00!\x81ٰ\x00\x00\x00\x00\"V)@\x00\x00\x00\x00#j\xf60\x00\x00\x00\x00$6\v@\x00\x00\x00\x00%J\xd80\x00\x00\x00\x00&\x15\xed@\x00\x00\x00\x00'*\xba0" + - "\x00\x00\x00\x00'\xff\t\xc0\x00\x00\x00\x00)\n\x9c0\x00\x00\x00\x00)\xde\xeb\xc0\x00\x00\x00\x00*\xea~0\x00\x00\x00\x00+\xbe\xcd\xc0\x00\x00\x00\x00,Ӛ\xb0\x00\x00\x00\x00-\x9e\xaf\xc0\x00\x00\x00\x00" + - ".\xb3|\xb0\x00\x00\x00\x00/~\x91\xc0\x00\x00\x00\x000\x93^\xb0\x00\x00\x00\x001g\xae@\x00\x00\x00\x002s@\xb0\x00\x00\x00\x003G\x90@\x00\x00\x00\x004S\"\xb0\x00\x00\x00\x005'r@" + - "\x00\x00\x00\x0063\x04\xb0\x00\x00\x00\x007\aT@\x00\x00\x00\x008\x1c!0\x00\x00\x00\x008\xe76@\x00\x00\x00\x009\xfc\x030\x00\x00\x00\x00:\xc7\x18@\x00\x00\x00\x00;\xdb\xe50\x00\x00\x00\x00" + - "<\xb04\xc0\x00\x00\x00\x00=\xbb\xc70\x00\x00\x00\x00>\x90\x16\xc0\x00\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0" + - "\x00\x00\x00\x00D/\xbc\xc0\x00\x00\x00\x00ED\x89\xb0\x00\x00\x00\x00E\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a" + - "\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00" + - "\xff\xffeP\x00\x04\xff\xffs`\x01\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00" + - "NPT\x00BST\x00BDT\x00AHST\x00HDT\x00\nHST10HDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R" + - "$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x11\x00\x1c\x00US/Indiana-StarkeUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p" + - "\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff" + - "\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p" + - "\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff" + - "\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00" + - "\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff" + - "\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0" + - "\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00" + - "\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00" + - "\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00" + - "\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp" + - "\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00" + - "%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00" + - "\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x05\x01\x02\x01\xff\xff\xae\xca\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9" + - "\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03" + - "\x04\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\v\x00\x1c\x00US/MountainUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZi" + - "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff" + - "\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f" + - "\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff" + - "\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd" + - "\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00" + - "\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83" + - "\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00" + - "\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea" + - "\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00" + - "\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p" + - "\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00" + - "\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee" + - "\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\u007f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00" + - "\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00" + - "MPT\x00\nMST7MDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\n\x00\x1c\x00US" + - "/ArizonaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\v\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff" + - "\xff\xff\xff\xcf\x17\xdf\x1c\xff\xff\xff\xffϏ\xe5\xac\xff\xff\xff\xffЁ\x1a\x1c\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\x02\x01\x02\x01\x02\x03\x02\x03\x02\x01\x02\xff\xff\x96\xee\x00\x00\xff\xff\xab\xa0" + - "\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0\x01\fLMT\x00MDT\x00MST\x00MWT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06" + - "\x00\x00\n\x00\x1c\x00US/EasternUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00R\x00\x00\x00\x06\x00\x00\x00\x1a\xff\xff\xff\xffV\xb6\xd0P\xff\xff\xff\xff\x99\xa8*\xd0\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90" + + "\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xa0\x9a\xb6\x00\xff\xff\xff\xff\xa1e\xbd\x00\xff\xff\xff\xff\xa6}|`\xff\xff\xff\xff\xc8v\xde\x10\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xff" + + "ͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЄ\xba\x00\xff\xff\xff\xffѕ\x92p\xff\xff\xff\xffҊ\xbb`\xff\xff\xff\xff\xd3b\xffp\xff\xff\xff\xff\xd4K#\x90" + + "\xff\xff\xff\xff\xd5^\xad\x10\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\xff\xff\xff\xff\xe8T\xd2\x00\xff\xff\xff\xff" + + "\xe8\xf1\xb4\x80\xff\xff\xff\xff\xe9᥀\xff\xff\xff\xff\xeaі\x80\xff\xff\xff\xff\xec\x14\x96\x00\xff\xff\xff\xff캳\x00\xff\xff\xff\xff\xed\xaa\xa4\x00\xff\xff\xff\xff\ue695\x00\xff\xff\xff\xff\xef\xd4Z\x00" + + "\xff\xff\xff\xff\xf0zw\x00\xff\xff\xff\xff\xf1\xb4<\x00\xff\xff\xff\xff\xf2ZY\x00\xff\xff\xff\xff\xf3\x94\x1e\x00\xff\xff\xff\xff\xf4:;\x00\xff\xff\xff\xff\xf5}:\x80\xff\xff\xff\xff\xf6\x1a\x1d\x00\x00\x00\x00\x00" + + "\r\xa4U\x80\x00\x00\x00\x00\x0e\x8b\f\x00\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x10t(\x80\x00\x00\x00\x00\x11d\x19\x80\x00\x00\x00\x00\x12T\n\x80\x00\x00\x00\x00\x13M6\x00\x00\x00\x00\x00\x143\xec\x80" + + "\x00\x00\x00\x00\x15#݀\x00\x00\x00\x00\x16\x13\u0380\x00\x00\x00\x00\x17\x03\xbf\x80\x00\x00\x00\x00\x17\xf3\xb0\x80\x00\x00\x00\x00\x18㡀\x00\x00\x00\x00\x19Ӓ\x80\x00\x00\x00\x00\x1aÃ\x80\x00\x00\x00\x00" + + "\x1b\xbc\xaf\x00\x00\x00\x00\x00\x1c\xac\xa0\x00\x00\x00\x00\x00\x1d\x9c\x91\x00\x00\x00\x00\x00\x1e\x8c\x82\x00\x00\x00\x00\x00\x1f|s\x00\x00\x00\x00\x00 ld\x00\x00\x00\x00\x00!\\U\x00\x00\x00\x00\x00\"LT\x10" + + "\x00\x00\x00\x00#\xf0\xff\xff\xff\xffӋ{\x80\xff\xff\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff" + + "\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9A|\xf0\x00\x00\x00\x00\x1e\xbaR \x00\x00\x00\x00\x1fi\x9b\x90\x00\x00\x00\x00 ~\x84\xa0\x00\x00\x00\x00!I}\x90\x00\x00\x00\x00\"g\xa1 \x00\x00\x00\x00#)_\x90" + + "\x00\x00\x00\x00$G\x83 \x00\x00\x00\x00%\x12|\x10\x00\x00\x00\x00&'e \x00\x00\x00\x00&\xf2^\x10\x00\x00\x00\x00(\aG \x00\x00\x00\x00(\xd2@\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00q\xd7\x00\x00\x00\x00~\x90\x01\x04\x00\x00p\x80\x00\bLMT\x00CDT\x00CST\x00\nCST-8\nPK\x03\x04\n\x00" + + "\x00\x00\x00\x00\bv\vUŭV\xad\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x1c\x00PST8PDTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff" + + "\xff\xff\xa1\x9a\xf7\x90\xff\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8" + + "H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00" + + "\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\xe0" + + "\xaf\xa0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00" + + "\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x02" + + "5\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00" + + "\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe" + + "\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00" + + "\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062" + + "\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00" + + "\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/" + + "\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x01\x00\x01\x00\x02\x03\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01" + + "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\xff\xff\x8f\x80\x00\x04\xff\xff\x9d\x90\x01\x00\xff\xff" + + "\x9d\x90\x01\b\xff\xff\x9d\x90\x01\fPDT\x00PST\x00PWT\x00PPT\x00\nPST8PDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00" + + "\bv\vU\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\x03\x00\x1c\x00ROCUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00\x04\x00\x00\x00\x10\xff\xff\xff\xfft\xce\xf0\x18\xff\xff\xff\xff\xc3UI\x80\xff\xff\xff\xff\xd2TY\x80\xff\xff\xff\xffӋ{\x80\xff\xff" + + "\xff\xff\xd4B\xad\xf0\xff\xff\xff\xff\xd5E\"\x00\xff\xff\xff\xff\xd6L\xbf\xf0\xff\xff\xff\xff\xd7<\xbf\x00\xff\xff\xff\xff\xd8\x06fp\xff\xff\xff\xff\xd9\x1d\xf2\x80\xff\xff\xff\xff\xd9\xe7\x99\xf0\xff\xff\xff\xff\xda\xff" + + "&\x00\xff\xff\xff\xff\xdb\xc8\xcdp\xff\xff\xff\xff\xdc\xe0Y\x80\xff\xff\xff\xffݪ\x00\xf0\xff\xff\xff\xff\xders\x00\xff\xff\xff\xffߵdp\xff\xff\xff\xff\xe0|\x85\x00\xff\xff\xff\xffᖗ\xf0\xff\xff" + + "\xff\xff\xe2]\xb8\x80\xff\xff\xff\xff\xe3w\xcbp\xff\xff\xff\xff\xe4>\xec\x00\xff\xff\xff\xff\xe50 p\xff\xff\xff\xff\xe6!q\x00\xff\xff\xff\xff\xe7\x12\xa5p\xff\xff\xff\xff\xe8\x02\xa4\x80\xff\xff\xff\xff\xe8\xf3" + + "\xd8\xf0\xff\xff\xff\xff\xe9\xe3\xd8\x00\xff\xff\xff\xff\xea\xd5\fp\xff\xff\xff\xff\xeb\xc5\v\x80\xff\xff\xff\xff\xec\xb6?\xf0\xff\xff\xff\xff\xed\xf7\xfc\x00\xff\xff\xff\xff\xee\x98\xc4\xf0\xff\xff\xff\xff\xef\xd9/\x80\xff\xff" + + "\xff\xff\xf0y\xf8p\x00\x00\x00\x00\a\xfcV\x00\x00\x00\x00\x00\b\xed\x8ap\x00\x00\x00\x00\t݉\x80\x00\x00\x00\x00\nν\xf0\x00\x00\x00\x00\x11ۡ\x80\x00\x00\x00\x00\x12T\xddp\x01\x02\x01\x03\x01\x03" + + "\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x03\x01\x00\x00q\xe8\x00\x00\x00\x00p\x80\x00\x04\x00\x00~\x90\x00\b\x00\x00~\x90\x01\fL" + + "MT\x00CST\x00JST\x00CDT\x00\nCST-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\x03\x00\x1c\x00ROKUT\t\x00\x03" + + "\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x06\x00\x00\x00\x10\xff\xff" + + "\xff\xff\x8b\xd7\xf0x\xff\xff\xff\xff\x92\xe6\x16\xf8\xff\xff\xff\xff\xd2C'\xf0\xff\xff\xff\xff\xd7e\x8fp\xff\xff\xff\xff\xd7\xee\x9d`\xff\xff\xff\xff\xd8\xf8\xfap\xff\xff\xff\xff\xd9\xcd-\xe0\xff\xff\xff\xff\xda\xd7" + + "\x8a\xf0\xff\xff\xff\xffۭ\x0f\xe0\xff\xff\xff\xff\xdc\xe6\xe2\xf0\xff\xff\xff\xff\u074c\xf1\xe0\xff\xff\xff\xff\xe2O)\xf0\xff\xff\xff\xff\xe4k\xb7\xf8\xff\xff\xff\xff\xe5\x13\x18h\xff\xff\xff\xff\xe6b\x03x\xff\xff" + + "\xff\xff\xe7\x11L\xe8\xff\xff\xff\xff\xe8/px\xff\xff\xff\xff\xe8\xe7\xf4h\xff\xff\xff\xff\xea\x0fRx\xff\xff\xff\xff\xea\xc7\xd6h\xff\xff\xff\xff\xeb\xef4x\xff\xff\xff\xff째h\xff\xff\xff\xff\xed\xcf" + + "\x16x\xff\xff\xff\xff\ue1dah\xff\xff\xff\xff\xf05qx\x00\x00\x00\x00 \xa3`\x90\x00\x00\x00\x00!ng\x90\x00\x00\x00\x00\"\x83B\x90\x00\x00\x00\x00#NI\x90\x01\x02\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x05\x01\x04\x03\x04\x03\x04\x00\x00w\b\x00\x00\x00\x00w\x88\x00\x04\x00\x00~\x90\x00\b\x00\x00\x8c\xa0\x01\f\x00\x00~\x90\x00\x04\x00\x00\x85\x98\x01\fLMT\x00K" + + "ST\x00JST\x00KDT\x00\nKST-9\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\t\x00\x1c\x00SingaporeUT\t" + + "\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00 " + + "\xff\xff\xff\xff~6S\xa3\xff\xff\xff\xff\x86\x83\x85\xa3\xff\xff\xff\xff\xbagN\x90\xff\xff\xff\xff\xc0\n\xe4`\xff\xff\xff\xffʳ\xe5`\xff\xff\xff\xffˑ_\b\xff\xff\xff\xff\xd2Hm\xf0\x00\x00\x00\x00" + + "\x16\x91\xf5\b\x01\x02\x03\x04\x05\x06\x05\a\x00\x00a]\x00\x00\x00\x00a]\x00\x04\x00\x00bp\x00\b\x00\x00g \x01\f\x00\x00g \x00\f\x00\x00ix\x00\x12\x00\x00~\x90\x00\x18\x00\x00p\x80\x00\x1c" + + "LMT\x00SMT\x00+07\x00+0720\x00+0730\x00+09\x00+08\x00\n<+08>-8\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\aW\x10Ѱ" + + "\x04\x00\x00\xb0\x04\x00\x00\x06\x00\x1c\x00TurkeyUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s\x00\x00\x00\x06\x00\x00\x00\x19\xff\xff\xff\xffV\xb6\xc8\xd8\xff\xff\xff\xff\x90\x8b\xf5\x98\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9bվ\xd0\xff\xff\xff\xff\xa2ec\xe0" + + "\xff\xff\xff\xff\xa3{\x82P\xff\xff\xff\xff\xa4N\x80`\xff\xff\xff\xff\xa5?\xb4\xd0\xff\xff\xff\xff\xa6%'\xe0\xff\xff\xff\xff\xa7'\x7f\xd0\xff\xff\xff\xff\xaa((`\xff\xff\xff\xff\xaa\xe1\xfd\xd0\xff\xff\xff\xff" + + "\xab\xf9\x89\xe0\xff\xff\xff\xff\xac\xc31P\xff\xff\xff\xffȁ?\xe0\xff\xff\xff\xff\xc9\x01\x13P\xff\xff\xff\xff\xc9J\xf5`\xff\xff\xff\xff\xca\u0380P\xff\xff\xff\xff\xcbˮ`\xff\xff\xff\xff\xd2k\tP" + + "\xff\xff\xff\xffӢ9`\xff\xff\xff\xff\xd4C\x02P\xff\xff\xff\xff\xd5L\r\xe0\xff\xff\xff\xff\xd6){\xd0\xff\xff\xff\xff\xd7+\xef\xe0\xff\xff\xff\xff\xd8\t]\xd0\xff\xff\xff\xff\xd9\x02\x97`\xff\xff\xff\xff" + + "\xd9\xe9?\xd0\xff\xff\xff\xff\xda\xeb\xb3\xe0\xff\xff\xff\xff\xdb\xd2\\P\xff\xff\xff\xff\xdc\xd4\xd0`\xff\xff\xff\xffݲ>P\xff\xff\xff\xff\xf1\xf4\xb9`\xff\xff\xff\xff\xf4b\xefP\xff\xff\xff\xff\xf5h\x06`" + + "\xff\xff\xff\xff\xf6\x1f8\xd0\x00\x00\x00\x00\x06n\x93p\x00\x00\x00\x00\a9\x9ap\x00\x00\x00\x00\a\xfbu\x00\x00\x00\x00\x00\t\x19|p\x00\x00\x00\x00\t\xd0\xcb\x00\x00\x00\x00\x00\n\xf9^p\x00\x00\x00\x00" + + "\v\xb1\xfe\x80\x00\x00\x00\x00\f\xd9@p\x00\x00\x00\x00\r\xa4U\x80\x00\x00\x00\x00\x0e\xa6\xadp\x00\x00\x00\x00\x0f\x847\x80\x00\x00\x00\x00\x0f\xf8\x11P\x00\x00\x00\x00\x19\x89\xb0p\x00\x00\x00\x00\x19ܰ\xe0" + + "\x00\x00\x00\x00\x1b\xe6\xd0\xf0\x00\x00\x00\x00\x1c\xc6\xef\xf0\x00\x00\x00\x00\x1d\x9b1p\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00" + + "\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\tp" + + "\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x8b\x83\xf0\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x00" + + "0d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0" + + "\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0" + + "\x00\x00\x00\x00F\x05ɐ\x00\x00\x00\x00G#\xdf\x10\x00\x00\x00\x00G\xee\xe6\x10\x00\x00\x00\x00I\x03\xc1\x10\x00\x00\x00\x00I\xce\xc8\x10\x00\x00\x00\x00J\xe3\xa3\x10\x00\x00\x00\x00K\xae\xaa\x10\x00\x00\x00\x00" + + "L̿\x90\x00\x00\x00\x00M\x8fݐ\x00\x00\x00\x00N\xac\xa1\x90\x00\x00\x00\x00Onn\x10\x00\x00\x00\x00P\x8c\x83\x90\x00\x00\x00\x00QW\x8a\x90\x00\x00\x00\x00Rle\x90\x00\x00\x00\x00S8\xbe\x10" + + "\x00\x00\x00\x00TLG\x90\x00\x00\x00\x00U\x17N\x90\x00\x00\x00\x00V>\x9e\x90\x00\x00\x00\x00V\xf70\x90\x00\x00\x00\x00W\xcf.P\x01\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x05\x04\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02" + + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x04\x00\x00\x1b(\x00\x00\x00\x00\x1bh\x00\x04\x00\x00*0\x01\b\x00\x00\x1c \x00\r\x00" + + "\x00*0\x00\x11\x00\x008@\x01\x15LMT\x00IMT\x00EEST\x00EET\x00+03\x00+04\x00\n<+03>-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f" + + ".\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00UCTUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o" + + "\x00\x00\x00\t\x00\x1c\x00UniversalUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x03\xf0\x90\xff\xff\xff\xff\x9e\xa6\x1ep\xff\xff\xff\xff\x9f\xba\xeb`\xff\xff\xff\xff\xa0\x86\x00p\xff\xff\xff\xff\xa1\x9a\xcd`\xff" + - "\xff\xff\xff\xa2e\xe2p\xff\xff\xff\xff\xa3\x83\xe9\xe0\xff\xff\xff\xff\xa4j\xaep\xff\xff\xff\xff\xa55\xa7`\xff\xff\xff\xff\xa6S\xca\xf0\xff\xff\xff\xff\xa7\x15\x89`\xff\xff\xff\xff\xa83\xac\xf0\xff\xff\xff\xff\xa8" + - "\xfe\xa5\xe0\xff\xff\xff\xff\xaa\x13\x8e\xf0\xff\xff\xff\xff\xaaއ\xe0\xff\xff\xff\xff\xab\xf3p\xf0\xff\xff\xff\xff\xac\xbei\xe0\xff\xff\xff\xff\xad\xd3R\xf0\xff\xff\xff\xff\xae\x9eK\xe0\xff\xff\xff\xff\xaf\xb34\xf0\xff" + - "\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7" + - ";\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9\x1b\xd9p\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbbƴ`\xff\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff" + - "\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5" + - "/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xff\xca\r@p\xff\xff\xff\xff\xca\xd89`\xff\xff\xff\xffˈ\xf0p\xff" + - "\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xc6\xf0\xff\xff\xff\xff\xd6 \xbf\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8" + - "\x00\xa1\xe0\xff\xff\xff\xff\xd9\x15\x8a\xf0\xff\xff\xff\xff\xd9\xe0\x83\xe0\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdb\xc0e\xe0\xff\xff\xff\xff\xdcމp\xff\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff" + - "\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4^\x11p\xff\xff\xff\xff\xe5W.\xe0\xff\xff\xff\xff\xe6" + - "G-\xf0\xff\xff\xff\xff\xe77\x10\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff" + - "\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2\u007f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4" + - "_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\u007f`\x00" + - "\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x03\x00\x1c\x00US/UT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05" + + "\x00\x00\n\x00\x1c\x00US/PacificUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00}\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\x1a\xc0\xff\xff\xff\xff\x9e\xa6H\xa0\xff\xff\xff\xff\x9f\xbb\x15\x90\xff\xff\xff\xff\xa0\x86*\xa0\xff\xff\xff\xff\xa1\x9a\xf7\x90\xff" + + "\xff\xff\xffˉ\x1a\xa0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a&\x10\xff\xff\xff\xff\xd6\xfet\\\xff\xff\xff\xff\u0600\xad\x90\xff\xff\xff\xff\xda\xfeÐ\xff\xff\xff\xff\xdb\xc0\x90\x10\xff\xff\xff\xff\xdc" + + "ޥ\x90\xff\xff\xff\xffݩ\xac\x90\xff\xff\xff\xff\u07be\x87\x90\xff\xff\xff\xff߉\x8e\x90\xff\xff\xff\xff\xe0\x9ei\x90\xff\xff\xff\xff\xe1ip\x90\xff\xff\xff\xff\xe2~K\x90\xff\xff\xff\xff\xe3IR\x90\xff" + + "\xff\xff\xff\xe4^-\x90\xff\xff\xff\xff\xe5)4\x90\xff\xff\xff\xff\xe6GJ\x10\xff\xff\xff\xff\xe7\x12Q\x10\xff\xff\xff\xff\xe8',\x10\xff\xff\xff\xff\xe8\xf23\x10\xff\xff\xff\xff\xea\a\x0e\x10\xff\xff\xff\xff\xea" + + "\xd2\x15\x10\xff\xff\xff\xff\xeb\xe6\xf0\x10\xff\xff\xff\xff\xec\xb1\xf7\x10\xff\xff\xff\xff\xed\xc6\xd2\x10\xff\xff\xff\xff\xee\x91\xd9\x10\xff\xff\xff\xff\xef\xaf\xee\x90\xff\xff\xff\xff\xf0q\xbb\x10\xff\xff\xff\xff\xf1\x8fА\xff" + + "\xff\xff\xff\xf2\x7f\xc1\x90\xff\xff\xff\xff\xf3o\xb2\x90\xff\xff\xff\xff\xf4_\xa3\x90\xff\xff\xff\xff\xf5O\x94\x90\xff\xff\xff\xff\xf6?\x85\x90\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff\xff\xf8(\xa2\x10\xff\xff\xff\xff\xf9" + + "\x0fX\x90\xff\xff\xff\xff\xfa\b\x84\x10\xff\xff\xff\xff\xfa\xf8\x83 \xff\xff\xff\xff\xfb\xe8f\x10\xff\xff\xff\xff\xfc\xd8e \xff\xff\xff\xff\xfd\xc8H\x10\xff\xff\xff\xff\xfe\xb8G \xff\xff\xff\xff\xff\xa8*\x10\x00" + + "\x00\x00\x00\x00\x98) \x00\x00\x00\x00\x01\x88\f\x10\x00\x00\x00\x00\x02x\v \x00\x00\x00\x00\x03q(\x90\x00\x00\x00\x00\x04a'\xa0\x00\x00\x00\x00\x05Q\n\x90\x00\x00\x00\x00\x06A\t\xa0\x00\x00\x00\x00\a" + + "0\xec\x90\x00\x00\x00\x00\a\x8dC\xa0\x00\x00\x00\x00\t\x10ΐ\x00\x00\x00\x00\t\xad\xbf \x00\x00\x00\x00\n\xf0\xb0\x90\x00\x00\x00\x00\v\u0be0\x00\x00\x00\x00\f\xd9\xcd\x10\x00\x00\x00\x00\r\xc0\x91\xa0\x00" + + "\x00\x00\x00\x0e\xb9\xaf\x10\x00\x00\x00\x00\x0f\xa9\xae \x00\x00\x00\x00\x10\x99\x91\x10\x00\x00\x00\x00\x11\x89\x90 \x00\x00\x00\x00\x12ys\x10\x00\x00\x00\x00\x13ir \x00\x00\x00\x00\x14YU\x10\x00\x00\x00\x00\x15" + + "IT \x00\x00\x00\x00\x1697\x10\x00\x00\x00\x00\x17)6 \x00\x00\x00\x00\x18\"S\x90\x00\x00\x00\x00\x19\t\x18 \x00\x00\x00\x00\x1a\x025\x90\x00\x00\x00\x00\x1a\xf24\xa0\x00\x00\x00\x00\x1b\xe2\x17\x90\x00" + + "\x00\x00\x00\x1c\xd2\x16\xa0\x00\x00\x00\x00\x1d\xc1\xf9\x90\x00\x00\x00\x00\x1e\xb1\xf8\xa0\x00\x00\x00\x00\x1f\xa1ې\x00\x00\x00\x00 v+ \x00\x00\x00\x00!\x81\xbd\x90\x00\x00\x00\x00\"V\r \x00\x00\x00\x00#" + + "j\xda\x10\x00\x00\x00\x00$5\xef \x00\x00\x00\x00%J\xbc\x10\x00\x00\x00\x00&\x15\xd1 \x00\x00\x00\x00'*\x9e\x10\x00\x00\x00\x00'\xfe\xed\xa0\x00\x00\x00\x00)\n\x80\x10\x00\x00\x00\x00)\xdeϠ\x00" + + "\x00\x00\x00*\xeab\x10\x00\x00\x00\x00+\xbe\xb1\xa0\x00\x00\x00\x00,\xd3~\x90\x00\x00\x00\x00-\x9e\x93\xa0\x00\x00\x00\x00.\xb3`\x90\x00\x00\x00\x00/~u\xa0\x00\x00\x00\x000\x93B\x90\x00\x00\x00\x001" + + "g\x92 \x00\x00\x00\x002s$\x90\x00\x00\x00\x003Gt \x00\x00\x00\x004S\x06\x90\x00\x00\x00\x005'V \x00\x00\x00\x0062\xe8\x90\x00\x00\x00\x007\a8 \x00\x00\x00\x008\x1c\x05\x10\x00" + + "\x00\x00\x008\xe7\x1a \x00\x00\x00\x009\xfb\xe7\x10\x00\x00\x00\x00:\xc6\xfc \x00\x00\x00\x00;\xdb\xc9\x10\x00\x00\x00\x00<\xb0\x18\xa0\x00\x00\x00\x00=\xbb\xab\x10\x00\x00\x00\x00>\x8f\xfa\xa0\x00\x00\x00\x00?" + + "\x9b\x8d\x10\x00\x00\x00\x00@oܠ\x00\x00\x00\x00A\x84\xa9\x90\x00\x00\x00\x00BO\xbe\xa0\x00\x00\x00\x00Cd\x8b\x90\x00\x00\x00\x00D/\xa0\xa0\x00\x00\x00\x00EDm\x90\x00\x00\x00\x00E\xf3\xd3 \x02" + + "\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + - "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xba\x9e\x00\x00\xff\xff\xc7\xc0\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff" + - "\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\t\x00\x1c\x00US/AlaskaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + + "\x02\x01\x02\x01\xff\xff\x91&\x00\x00\xff\xff\x9d\x90\x01\x04\xff\xff\x8f\x80\x00\b\xff\xff\x9d\x90\x01\f\xff\xff\x9d\x90\x01\x10LMT\x00PDT\x00PST\x00PWT\x00PPT\x00\nPST8P" + + "DT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x11\x00\x1c\x00US/Indiana-S" + + "tarkeUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]" + + "\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff" + + "\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0" + + "\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff" + + "\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe5W<\xf0\xff\xff\xff\xff\xe6G<\x00\xff\xff\xff\xff\xe77\x1e\xf0\xff\xff\xff\xff\xe8'\x1e\x00" + + "\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xea\xd1\xf8\xf0\xff\xff\xff\xff\xeb\xe6\xe2\x00\xff\xff\xff\xff\xec\xd6\xc4\xf0\xff\xff\xff\xff\xed\xc6\xc4\x00\xff\xff\xff\xff\xee\xbf\xe1p\xff\xff\xff\xff" + + "\xef\xaf\xe0\x80\xff\xff\xff\xff\xf0\x9f\xc3p\xff\xff\xff\xff\xf1\x8f\u0080\xff\xff\xff\xff\xf4_\x87p\xff\xff\xff\xff\xfa\xf8g\x00\xff\xff\xff\xff\xfb\xe8I\xf0\xff\xff\xff\xff\xfc\xd8I\x00\xff\xff\xff\xff\xfd\xc8+\xf0" + + "\xff\xff\xff\xff\xfe\xb8+\x00\xff\xff\xff\xff\xff\xa8\r\xf0\x00\x00\x00\x00\x00\x98\r\x00\x00\x00\x00\x00\x01\x87\xef\xf0\x00\x00\x00\x00\x02w\xef\x00\x00\x00\x00\x00\x03q\fp\x00\x00\x00\x00\x04a\v\x80\x00\x00\x00\x00" + + "\x05P\xeep\x00\x00\x00\x00\x06@\xed\x80\x00\x00\x00\x00\a0\xd0p\x00\x00\x00\x00\a\x8d'\x80\x00\x00\x00\x00\t\x10\xb2p\x00\x00\x00\x00\t\xad\xa3\x00\x00\x00\x00\x00\n\xf0\x94p\x00\x00\x00\x00\v\xe0\x93\x80" + + "\x00\x00\x00\x00\fٰ\xf0\x00\x00\x00\x00\r\xc0u\x80\x00\x00\x00\x00\x0e\xb9\x92\xf0\x00\x00\x00\x00\x0f\xa9\x92\x00\x00\x00\x00\x00\x10\x99t\xf0\x00\x00\x00\x00\x11\x89t\x00\x00\x00\x00\x00\x12yV\xf0\x00\x00\x00\x00" + + "\x13iV\x00\x00\x00\x00\x00\x14Y8\xf0\x00\x00\x00\x00\x15I8\x00\x00\x00\x00\x00\x169\x1a\xf0\x00\x00\x00\x00\x17)\x1a\x00\x00\x00\x00\x00\x18\"7p\x00\x00\x00\x00\x19\b\xfc\x00\x00\x00\x00\x00\x1a\x02\x19p" + + "\x00\x00\x00\x00\x1a\xf2\x18\x80\x00\x00\x00\x00\x1b\xe1\xfbp\x00\x00\x00\x00\x1c\xd1\xfa\x80\x00\x00\x00\x00\x1d\xc1\xddp\x00\x00\x00\x00\x1e\xb1܀\x00\x00\x00\x00\x1f\xa1\xbfp\x00\x00\x00\x00 v\x0f\x00\x00\x00\x00\x00" + + "!\x81\xa1p\x00\x00\x00\x00\"U\xf1\x00\x00\x00\x00\x00#j\xbd\xf0\x00\x00\x00\x00$5\xd3\x00\x00\x00\x00\x00%J\x9f\xf0\x00\x00\x00\x00&\x15\xb5\x00\x00\x00\x00\x00'*\x81\xf0\x00\x00\x00\x00'\xfeр" + + "\x00\x00\x00\x00)\nc\xf0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDQp\x00\x00\x00\x00E\xf3\xb7\x00\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + + "\x01\x05\x01\x02\x01\xff\xff\xae\xca\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14LMT\x00CDT\x00CST\x00CWT\x00CPT" + + "\x00EST\x00\nCST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\v\x00\x1c\x00U" + + "S/MountainUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00a\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xff\xa2e\xfe" + + "\x90\xff\xff\xff\xff\xa3\x84\x06\x00\xff\xff\xff\xff\xa4E\xe0\x90\xff\xff\xff\xff\xa4\x8f\xa6\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\x18\x00\xff\xff\xff\xff\xf7/v\x90\xff\xff\xff" + + "\xff\xf8(\x94\x00\xff\xff\xff\xff\xf9\x0fX\x90\xff\xff\xff\xff\xfa\bv\x00\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\xff\xff\xff\xff\xfc\xd8W\x10\xff\xff\xff\xff\xfd\xc8:\x00\xff\xff\xff\xff\xfe\xb89" + + "\x10\xff\xff\xff\xff\xff\xa8\x1c\x00\x00\x00\x00\x00\x00\x98\x1b\x10\x00\x00\x00\x00\x01\x87\xfe\x00\x00\x00\x00\x00\x02w\xfd\x10\x00\x00\x00\x00\x03q\x1a\x80\x00\x00\x00\x00\x04a\x19\x90\x00\x00\x00\x00\x05P\xfc\x80\x00\x00\x00" + + "\x00\x06@\xfb\x90\x00\x00\x00\x00\a0ހ\x00\x00\x00\x00\a\x8d5\x90\x00\x00\x00\x00\t\x10\xc0\x80\x00\x00\x00\x00\t\xad\xb1\x10\x00\x00\x00\x00\n\xf0\xa2\x80\x00\x00\x00\x00\vࡐ\x00\x00\x00\x00\fٿ" + + "\x00\x00\x00\x00\x00\r\xc0\x83\x90\x00\x00\x00\x00\x0e\xb9\xa1\x00\x00\x00\x00\x00\x0f\xa9\xa0\x10\x00\x00\x00\x00\x10\x99\x83\x00\x00\x00\x00\x00\x11\x89\x82\x10\x00\x00\x00\x00\x12ye\x00\x00\x00\x00\x00\x13id\x10\x00\x00\x00" + + "\x00\x14YG\x00\x00\x00\x00\x00\x15IF\x10\x00\x00\x00\x00\x169)\x00\x00\x00\x00\x00\x17)(\x10\x00\x00\x00\x00\x18\"E\x80\x00\x00\x00\x00\x19\t\n\x10\x00\x00\x00\x00\x1a\x02'\x80\x00\x00\x00\x00\x1a\xf2&" + + "\x90\x00\x00\x00\x00\x1b\xe2\t\x80\x00\x00\x00\x00\x1c\xd2\b\x90\x00\x00\x00\x00\x1d\xc1\xeb\x80\x00\x00\x00\x00\x1e\xb1\xea\x90\x00\x00\x00\x00\x1f\xa1̀\x00\x00\x00\x00 v\x1d\x10\x00\x00\x00\x00!\x81\xaf\x80\x00\x00\x00" + + "\x00\"U\xff\x10\x00\x00\x00\x00#j\xcc\x00\x00\x00\x00\x00$5\xe1\x10\x00\x00\x00\x00%J\xae\x00\x00\x00\x00\x00&\x15\xc3\x10\x00\x00\x00\x00'*\x90\x00\x00\x00\x00\x00'\xfeߐ\x00\x00\x00\x00)\nr" + + "\x00\x00\x00\x00\x00)\xde\xc1\x90\x00\x00\x00\x00*\xeaT\x00\x00\x00\x00\x00+\xbe\xa3\x90\x00\x00\x00\x00,\xd3p\x80\x00\x00\x00\x00-\x9e\x85\x90\x00\x00\x00\x00.\xb3R\x80\x00\x00\x00\x00/~g\x90\x00\x00\x00" + + "\x000\x934\x80\x00\x00\x00\x001g\x84\x10\x00\x00\x00\x002s\x16\x80\x00\x00\x00\x003Gf\x10\x00\x00\x00\x004R\xf8\x80\x00\x00\x00\x005'H\x10\x00\x00\x00\x0062ڀ\x00\x00\x00\x007\a*" + + "\x10\x00\x00\x00\x008\x1b\xf7\x00\x00\x00\x00\x008\xe7\f\x10\x00\x00\x00\x009\xfb\xd9\x00\x00\x00\x00\x00:\xc6\xee\x10\x00\x00\x00\x00;ۻ\x00\x00\x00\x00\x00<\xb0\n\x90\x00\x00\x00\x00=\xbb\x9d\x00\x00\x00\x00" + + "\x00>\x8f\xec\x90\x00\x00\x00\x00?\x9b\x7f\x00\x00\x00\x00\x00@oΐ\x00\x00\x00\x00A\x84\x9b\x80\x00\x00\x00\x00BO\xb0\x90\x00\x00\x00\x00Cd}\x80\x00\x00\x00\x00D/\x92\x90\x00\x00\x00\x00ED_" + + "\x80\x00\x00\x00\x00E\xf3\xc5\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\x9d\x94\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff" + + "\x9d\x90\x00\b\xff\xff\xab\xa0\x01\f\xff\xff\xab\xa0\x01\x10LMT\x00MDT\x00MST\x00MWT\x00MPT\x00\nMST7MDT,M3.2.0,M11.1.0\n" + + "PK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\n\x00\x1c\x00US/CentralUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00T" + + "Zif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff" + + "\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xa2\xcbt\x00\xff\xff\xff\xff\xa3\x83\xf7\xf0\xff\xff\xff\xff\xa4EҀ\xff\xff\xff\xff\xa5c\xd9\xf0\xff\xff\xff\xff\xa6" + + "S\xd9\x00\xff\xff\xff\xff\xa7\x15\x97p\xff\xff\xff\xff\xa83\xbb\x00\xff\xff\xff\xff\xa8\xfe\xb3\xf0\xff\xff\xff\xff\xaa\x13\x9d\x00\xff\xff\xff\xff\xaaޕ\xf0\xff\xff\xff\xff\xab\xf3\x7f\x00\xff\xff\xff\xff\xac\xbew\xf0\xff" + + "\xff\xff\xff\xad\xd3a\x00\xff\xff\xff\xff\xae\x9eY\xf0\xff\xff\xff\xff\xaf\xb3C\x00\xff\xff\xff\xff\xb0~;\xf0\xff\xff\xff\xff\xb1\x9c_\x80\xff\xff\xff\xff\xb2gXp\xff\xff\xff\xff\xb3|A\x80\xff\xff\xff\xff\xb4" + + "G:p\xff\xff\xff\xff\xb5\\#\x80\xff\xff\xff\xff\xb6'\x1cp\xff\xff\xff\xff\xb7<\x05\x80\xff\xff\xff\xff\xb8\x06\xfep\xff\xff\xff\xff\xb9\x1b\xe7\x80\xff\xff\xff\xff\xb9\xe6\xe0p\xff\xff\xff\xff\xbb\x05\x04\x00\xff" + + "\xff\xff\xff\xbb\xc6\xc2p\xff\xff\xff\xff\xbc\xe4\xe6\x00\xff\xff\xff\xff\xbd\xaf\xde\xf0\xff\xff\xff\xff\xbe\xc4\xc8\x00\xff\xff\xff\xff\xbf\x8f\xc0\xf0\xff\xff\xff\xff\xc0Z\xd6\x00\xff\xff\xff\xff\xc1\xb0\x8fހ\x00" + + "\x00\x00\x00?\x9bp\xf0\x00\x00\x00\x00@o\xc0\x80\x00\x00\x00\x00A\x84\x8dp\x00\x00\x00\x00BO\xa2\x80\x00\x00\x00\x00Cdop\x00\x00\x00\x00D/\x84\x80\x00\x00\x00\x00EDQp\x00\x00\x00\x00E" + + "\xf3\xb7\x00\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x04\x05\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff" + + "\xad\xd4\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x00\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x01\x14LMT\x00CDT\x00CST\x00EST\x00CWT\x00CPT\x00\nC" + + "ST6CDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\t\x00\x1c\x00US/Alask" + + "aUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n" + + "\x00\x00\x00(\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87AH\xff\xff\xff\xffˉ6\xc0\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aB0\xff\xff\xff\xff\xfa\xd2G\xa0\xff\xff\xff\xff\xfe\xb8c@" + + "\xff\xff\xff\xff\xff\xa8F0\x00\x00\x00\x00\x00\x98E@\x00\x00\x00\x00\x01\x88(0\x00\x00\x00\x00\x02x'@\x00\x00\x00\x00\x03qD\xb0\x00\x00\x00\x00\x04aC\xc0\x00\x00\x00\x00\x05Q&\xb0\x00\x00\x00\x00" + + "\x06A%\xc0\x00\x00\x00\x00\a1\b\xb0\x00\x00\x00\x00\a\x8d_\xc0\x00\x00\x00\x00\t\x10\xea\xb0\x00\x00\x00\x00\t\xad\xdb@\x00\x00\x00\x00\n\xf0̰\x00\x00\x00\x00\v\xe0\xcb\xc0\x00\x00\x00\x00\f\xd9\xe90" + + "\x00\x00\x00\x00\r\xc0\xad\xc0\x00\x00\x00\x00\x0e\xb9\xcb0\x00\x00\x00\x00\x0f\xa9\xca@\x00\x00\x00\x00\x10\x99\xad0\x00\x00\x00\x00\x11\x89\xac@\x00\x00\x00\x00\x12y\x8f0\x00\x00\x00\x00\x13i\x8e@\x00\x00\x00\x00" + + "\x14Yq0\x00\x00\x00\x00\x15Ip@\x00\x00\x00\x00\x169S0\x00\x00\x00\x00\x17)R@\x00\x00\x00\x00\x18\"o\xb0\x00\x00\x00\x00\x19\t4@\x00\x00\x00\x00\x1a\x02Q\xb0\x00\x00\x00\x00\x1a+\x14\x10" + + "\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00" + + "!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0" + + "\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00" + + "/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0" + + "\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00" + + "=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0" + + "\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b" + + "\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\x00\x00\xc4\xf8\x00\x00\xff\xffsx\x00\x00\xff\xffs`\x00\x04\xff\xff" + + "\x81p\x01\b\xff\xff\x81p\x01\f\xff\xffs`\x00\x10\xff\xff\x81p\x01\x15\xff\xff\x81p\x00\x1a\xff\xff\x8f\x80\x01\x1e\xff\xff\x81p\x00#LMT\x00AST\x00AWT\x00APT\x00AHST" + + "\x00AHDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU" + + "\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\v\x00\x1c\x00US/AleutianUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00(\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87AH\xff\xff\xff\xffˉ6\xc0\xff\xff\xff\xff\xd2#" + - "\xf4p\xff\xff\xff\xff\xd2aB0\xff\xff\xff\xff\xfa\xd2G\xa0\xff\xff\xff\xff\xfe\xb8c@\xff\xff\xff\xff\xff\xa8F0\x00\x00\x00\x00\x00\x98E@\x00\x00\x00\x00\x01\x88(0\x00\x00\x00\x00\x02x'@\x00\x00" + - "\x00\x00\x03qD\xb0\x00\x00\x00\x00\x04aC\xc0\x00\x00\x00\x00\x05Q&\xb0\x00\x00\x00\x00\x06A%\xc0\x00\x00\x00\x00\a1\b\xb0\x00\x00\x00\x00\a\x8d_\xc0\x00\x00\x00\x00\t\x10\xea\xb0\x00\x00\x00\x00\t\xad" + - "\xdb@\x00\x00\x00\x00\n\xf0̰\x00\x00\x00\x00\v\xe0\xcb\xc0\x00\x00\x00\x00\f\xd9\xe90\x00\x00\x00\x00\r\xc0\xad\xc0\x00\x00\x00\x00\x0e\xb9\xcb0\x00\x00\x00\x00\x0f\xa9\xca@\x00\x00\x00\x00\x10\x99\xad0\x00\x00" + - "\x00\x00\x11\x89\xac@\x00\x00\x00\x00\x12y\x8f0\x00\x00\x00\x00\x13i\x8e@\x00\x00\x00\x00\x14Yq0\x00\x00\x00\x00\x15Ip@\x00\x00\x00\x00\x169S0\x00\x00\x00\x00\x17)R@\x00\x00\x00\x00\x18\"" + - "o\xb0\x00\x00\x00\x00\x19\t4@\x00\x00\x00\x00\x1a\x02Q\xb0\x00\x00\x00\x00\x1a+\x14\x10\x00\x00\x00\x00\x1a\xf2B\xb0\x00\x00\x00\x00\x1b\xe2%\xa0\x00\x00\x00\x00\x1c\xd2$\xb0\x00\x00\x00\x00\x1d\xc2\a\xa0\x00\x00" + - "\x00\x00\x1e\xb2\x06\xb0\x00\x00\x00\x00\x1f\xa1\xe9\xa0\x00\x00\x00\x00 v90\x00\x00\x00\x00!\x81ˠ\x00\x00\x00\x00\"V\x1b0\x00\x00\x00\x00#j\xe8 \x00\x00\x00\x00$5\xfd0\x00\x00\x00\x00%J" + - "\xca \x00\x00\x00\x00&\x15\xdf0\x00\x00\x00\x00'*\xac \x00\x00\x00\x00'\xfe\xfb\xb0\x00\x00\x00\x00)\n\x8e \x00\x00\x00\x00)\xdeݰ\x00\x00\x00\x00*\xeap \x00\x00\x00\x00+\xbe\xbf\xb0\x00\x00" + - "\x00\x00,ӌ\xa0\x00\x00\x00\x00-\x9e\xa1\xb0\x00\x00\x00\x00.\xb3n\xa0\x00\x00\x00\x00/~\x83\xb0\x00\x00\x00\x000\x93P\xa0\x00\x00\x00\x001g\xa00\x00\x00\x00\x002s2\xa0\x00\x00\x00\x003G" + - "\x820\x00\x00\x00\x004S\x14\xa0\x00\x00\x00\x005'd0\x00\x00\x00\x0062\xf6\xa0\x00\x00\x00\x007\aF0\x00\x00\x00\x008\x1c\x13 \x00\x00\x00\x008\xe7(0\x00\x00\x00\x009\xfb\xf5 \x00\x00" + - "\x00\x00:\xc7\n0\x00\x00\x00\x00;\xdb\xd7 \x00\x00\x00\x00<\xb0&\xb0\x00\x00\x00\x00=\xbb\xb9 \x00\x00\x00\x00>\x90\b\xb0\x00\x00\x00\x00?\x9b\x9b \x00\x00\x00\x00@o\xea\xb0\x00\x00\x00\x00A\x84" + - "\xb7\xa0\x00\x00\x00\x00BO̰\x00\x00\x00\x00Cd\x99\xa0\x00\x00\x00\x00D/\xae\xb0\x00\x00\x00\x00ED{\xa0\x00\x00\x00\x00E\xf3\xe10\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\n\x00\x00\x00!\xff\xff\xff\xff?\xc2\xfd\xd1\xff\xff\xff\xff}\x87Z^\xff\xff\xff\xffˉD\xd0\xff\xff\xff\xff\xd2#" + + "\xf4p\xff\xff\xff\xff\xd2aP@\xff\xff\xff\xff\xfa\xd2U\xb0\xff\xff\xff\xff\xfe\xb8qP\xff\xff\xff\xff\xff\xa8T@\x00\x00\x00\x00\x00\x98SP\x00\x00\x00\x00\x01\x886@\x00\x00\x00\x00\x02x5P\x00\x00" + + "\x00\x00\x03qR\xc0\x00\x00\x00\x00\x04aQ\xd0\x00\x00\x00\x00\x05Q4\xc0\x00\x00\x00\x00\x06A3\xd0\x00\x00\x00\x00\a1\x16\xc0\x00\x00\x00\x00\a\x8dm\xd0\x00\x00\x00\x00\t\x10\xf8\xc0\x00\x00\x00\x00\t\xad" + + "\xe9P\x00\x00\x00\x00\n\xf0\xda\xc0\x00\x00\x00\x00\v\xe0\xd9\xd0\x00\x00\x00\x00\f\xd9\xf7@\x00\x00\x00\x00\r\xc0\xbb\xd0\x00\x00\x00\x00\x0e\xb9\xd9@\x00\x00\x00\x00\x0f\xa9\xd8P\x00\x00\x00\x00\x10\x99\xbb@\x00\x00" + + "\x00\x00\x11\x89\xbaP\x00\x00\x00\x00\x12y\x9d@\x00\x00\x00\x00\x13i\x9cP\x00\x00\x00\x00\x14Y\x7f@\x00\x00\x00\x00\x15I~P\x00\x00\x00\x00\x169a@\x00\x00\x00\x00\x17)`P\x00\x00\x00\x00\x18\"" + + "}\xc0\x00\x00\x00\x00\x19\tBP\x00\x00\x00\x00\x1a\x02_\xc0\x00\x00\x00\x00\x1a+\" \x00\x00\x00\x00\x1a\xf2P\xc0\x00\x00\x00\x00\x1b\xe23\xb0\x00\x00\x00\x00\x1c\xd22\xc0\x00\x00\x00\x00\x1d\xc2\x15\xb0\x00\x00" + + "\x00\x00\x1e\xb2\x14\xc0\x00\x00\x00\x00\x1f\xa1\xf7\xb0\x00\x00\x00\x00 vG@\x00\x00\x00\x00!\x81ٰ\x00\x00\x00\x00\"V)@\x00\x00\x00\x00#j\xf60\x00\x00\x00\x00$6\v@\x00\x00\x00\x00%J" + + "\xd80\x00\x00\x00\x00&\x15\xed@\x00\x00\x00\x00'*\xba0\x00\x00\x00\x00'\xff\t\xc0\x00\x00\x00\x00)\n\x9c0\x00\x00\x00\x00)\xde\xeb\xc0\x00\x00\x00\x00*\xea~0\x00\x00\x00\x00+\xbe\xcd\xc0\x00\x00" + + "\x00\x00,Ӛ\xb0\x00\x00\x00\x00-\x9e\xaf\xc0\x00\x00\x00\x00.\xb3|\xb0\x00\x00\x00\x00/~\x91\xc0\x00\x00\x00\x000\x93^\xb0\x00\x00\x00\x001g\xae@\x00\x00\x00\x002s@\xb0\x00\x00\x00\x003G" + + "\x90@\x00\x00\x00\x004S\"\xb0\x00\x00\x00\x005'r@\x00\x00\x00\x0063\x04\xb0\x00\x00\x00\x007\aT@\x00\x00\x00\x008\x1c!0\x00\x00\x00\x008\xe76@\x00\x00\x00\x009\xfc\x030\x00\x00" + + "\x00\x00:\xc7\x18@\x00\x00\x00\x00;\xdb\xe50\x00\x00\x00\x00<\xb04\xc0\x00\x00\x00\x00=\xbb\xc70\x00\x00\x00\x00>\x90\x16\xc0\x00\x00\x00\x00?\x9b\xa90\x00\x00\x00\x00@o\xf8\xc0\x00\x00\x00\x00A\x84" + + "Ű\x00\x00\x00\x00BO\xda\xc0\x00\x00\x00\x00Cd\xa7\xb0\x00\x00\x00\x00D/\xbc\xc0\x00\x00\x00\x00ED\x89\xb0\x00\x00\x00\x00E\xf3\xef@\x01\x02\x03\x04\x02\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05" + "\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\a\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b\t\b" + - "\t\b\t\b\t\b\x00\x00\xc4\xf8\x00\x00\xff\xffsx\x00\x00\xff\xffs`\x00\x04\xff\xff\x81p\x01\b\xff\xff\x81p\x01\f\xff\xffs`\x00\x10\xff\xff\x81p\x01\x15\xff\xff\x81p\x00\x1a\xff\xff\x8f\x80\x01\x1e" + - "\xff\xff\x81p\x00#LMT\x00AST\x00AWT\x00APT\x00AHST\x00AHDT\x00YST\x00AKDT\x00AKST\x00\nAKST9AKDT,M3." + - "2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\b\x00\x1c\x00US/SamoaUT\t\x00\x03\x15\xac\x0e`\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" + - "\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\b\xff\xff\xff\xffn=\xc8" + - "\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6" + - "{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x0f\x00\x1c\x00US/East-IndianaUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZi" + + "\t\b\t\b\t\b\x00\x00\xab\xe2\x00\x00\xff\xffZb\x00\x00\xff\xffeP\x00\x04\xff\xffs`\x01\b\xff\xffs`\x01\f\xff\xffeP\x00\x10\xff\xffs`\x01\x14\xff\xffs`\x00\x18\xff\xff\x81p\x01\x1d" + + "\xff\xffs`\x00\x19LMT\x00NST\x00NWT\x00NPT\x00BST\x00BDT\x00AHST\x00HDT\x00\nHST10HDT,M3.2.0,M11." + + "1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\t\x00\x1c\x00US/HawaiiUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + "\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff\xff\xff\xff\xbb\x05" + + "CH\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff\xffՍsH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00\xff\xfflX\x00" + + "\x04\xff\xffzh\x01\b\xff\xffzh\x01\f\xff\xffzh\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00HWT\x00HPT\x00\nHST10\nPK\x03\x04\n\x00\x00\x00" + + "\x00\x00\bv\vU3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06\x00\x00\n\x00\x1c\x00US/EasternUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x00\x00\x05\x00\x00\x00\x14\xff\xff\xff\xff^\x03\xf0\x90\xff\xff\xff\xff\x9e\xa6\x1ep\xff\xff\xff\xff\x9f\xba\xeb`\xff" + + "\xff\xff\xff\xa0\x86\x00p\xff\xff\xff\xff\xa1\x9a\xcd`\xff\xff\xff\xff\xa2e\xe2p\xff\xff\xff\xff\xa3\x83\xe9\xe0\xff\xff\xff\xff\xa4j\xaep\xff\xff\xff\xff\xa55\xa7`\xff\xff\xff\xff\xa6S\xca\xf0\xff\xff\xff\xff\xa7" + + "\x15\x89`\xff\xff\xff\xff\xa83\xac\xf0\xff\xff\xff\xff\xa8\xfe\xa5\xe0\xff\xff\xff\xff\xaa\x13\x8e\xf0\xff\xff\xff\xff\xaaއ\xe0\xff\xff\xff\xff\xab\xf3p\xf0\xff\xff\xff\xff\xac\xbei\xe0\xff\xff\xff\xff\xad\xd3R\xf0\xff" + + "\xff\xff\xff\xae\x9eK\xe0\xff\xff\xff\xff\xaf\xb34\xf0\xff\xff\xff\xff\xb0~-\xe0\xff\xff\xff\xff\xb1\x9cQp\xff\xff\xff\xff\xb2gJ`\xff\xff\xff\xff\xb3|3p\xff\xff\xff\xff\xb4G,`\xff\xff\xff\xff\xb5" + + "\\\x15p\xff\xff\xff\xff\xb6'\x0e`\xff\xff\xff\xff\xb7;\xf7p\xff\xff\xff\xff\xb8\x06\xf0`\xff\xff\xff\xff\xb9\x1b\xd9p\xff\xff\xff\xff\xb9\xe6\xd2`\xff\xff\xff\xff\xbb\x04\xf5\xf0\xff\xff\xff\xff\xbbƴ`\xff" + + "\xff\xff\xff\xbc\xe4\xd7\xf0\xff\xff\xff\xff\xbd\xaf\xd0\xe0\xff\xff\xff\xff\xbeĹ\xf0\xff\xff\xff\xff\xbf\x8f\xb2\xe0\xff\xff\xff\xff\xc0\xa4\x9b\xf0\xff\xff\xff\xff\xc1o\x94\xe0\xff\xff\xff\xff\u0084}\xf0\xff\xff\xff\xff\xc3" + + "Ov\xe0\xff\xff\xff\xff\xc4d_\xf0\xff\xff\xff\xff\xc5/X\xe0\xff\xff\xff\xff\xc6M|p\xff\xff\xff\xff\xc7\x0f:\xe0\xff\xff\xff\xff\xc8-^p\xff\xff\xff\xff\xc8\xf8W`\xff\xff\xff\xff\xca\r@p\xff" + + "\xff\xff\xff\xca\xd89`\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd3u\xe4\xf0\xff\xff\xff\xff\xd4@\xdd\xe0\xff\xff\xff\xff\xd5U\xc6\xf0\xff\xff\xff\xff\xd6" + + " \xbf\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0\xff\xff\xff\xff\xd9\x15\x8a\xf0\xff\xff\xff\xff\xd9\xe0\x83\xe0\xff\xff\xff\xff\xda\xfe\xa7p\xff\xff\xff\xff\xdb\xc0e\xe0\xff\xff\xff\xff\xdcމp\xff" + + "\xff\xff\xffݩ\x82`\xff\xff\xff\xff\u07bekp\xff\xff\xff\xff߉d`\xff\xff\xff\xff\xe0\x9eMp\xff\xff\xff\xff\xe1iF`\xff\xff\xff\xff\xe2~/p\xff\xff\xff\xff\xe3I(`\xff\xff\xff\xff\xe4" + + "^\x11p\xff\xff\xff\xff\xe5W.\xe0\xff\xff\xff\xff\xe6G-\xf0\xff\xff\xff\xff\xe77\x10\xe0\xff\xff\xff\xff\xe8'\x0f\xf0\xff\xff\xff\xff\xe9\x16\xf2\xe0\xff\xff\xff\xff\xea\x06\xf1\xf0\xff\xff\xff\xff\xea\xf6\xd4\xe0\xff" + + "\xff\xff\xff\xeb\xe6\xd3\xf0\xff\xff\xff\xff\xecֶ\xe0\xff\xff\xff\xff\xedƵ\xf0\xff\xff\xff\xff\xee\xbf\xd3`\xff\xff\xff\xff\xef\xaf\xd2p\xff\xff\xff\xff\xf0\x9f\xb5`\xff\xff\xff\xff\xf1\x8f\xb4p\xff\xff\xff\xff\xf2" + + "\x7f\x97`\xff\xff\xff\xff\xf3o\x96p\xff\xff\xff\xff\xf4_y`\xff\xff\xff\xff\xf5Oxp\xff\xff\xff\xff\xf6?[`\xff\xff\xff\xff\xf7/Zp\xff\xff\xff\xff\xf8(w\xe0\xff\xff\xff\xff\xf9\x0f\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00" + + "\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02" + + "\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01" + + "\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\xff\xff\xba\x9e\x00\x00\xff\xff\xc7\xc0" + + "\x01\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10LMT\x00EDT\x00EST\x00EWT\x00EPT\x00\nEST5EDT,M3.2.0,M11." + + "1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x0f\x00\x1c\x00US/East-IndianaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0" + + "\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff" + + "\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0" + + "\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff" + + "߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00" + + "\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01" + + "\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff" + + "\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0," + + "M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\v\x00\x1c\x00US/MichiganUT\t\x00\x03\xaf\x16\xf5b\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" + + "\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x06\x00\x00\x00\x18\xff\xff\xff\xff\x85\xbd\"[" + + "\xff\xff\xff\xff\x99<\x94\x00\xff\xff\xff\xffˈ\xf0p\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2`\xfb\xe0\xff\xff\xff\xff\xd75\xa8\xf0\xff\xff\xff\xff\xd8\x00\xa1\xe0\xff\xff\xff\xff\xfb3\x90\x8c\xff\xff\xff\xff" + + "\xfb\xe8;\xe0\xff\xff\xff\xff\xfc\xd8:\xf0\xff\xff\xff\xff\xfd\xc8\x1d\xe0\x00\x00\x00\x00\x06@\xdfp\x00\x00\x00\x00\a0\xc2`\x00\x00\x00\x00\a\x8d\x19p\x00\x00\x00\x00\t\x10\xa4`\x00\x00\x00\x00\n\x00\xa3p" + + "\x00\x00\x00\x00\n\xf0\x86`\x00\x00\x00\x00\v\xe0\x85p\x00\x00\x00\x00\f٢\xe0\x00\x00\x00\x00\r\xc0gp\x00\x00\x00\x00\x0e\xb9\x84\xe0\x00\x00\x00\x00\x0f\xa9\x83\xf0\x00\x00\x00\x00\x10\x99f\xe0\x00\x00\x00\x00" + + "\x11\x89e\xf0\x00\x00\x00\x00\x12yH\xe0\x00\x00\x00\x00\x13iG\xf0\x00\x00\x00\x00\x14Y*\xe0\x00\x00\x00\x00\x15I)\xf0\x00\x00\x00\x00\x169\f\xe0\x00\x00\x00\x00\x17)\v\xf0\x00\x00\x00\x00\x18\")`" + + "\x00\x00\x00\x00\x19\b\xed\xf0\x00\x00\x00\x00\x1a\x02\v`\x00\x00\x00\x00\x1a\xf2\np\x00\x00\x00\x00\x1b\xe1\xed`\x00\x00\x00\x00\x1c\xd1\xecp\x00\x00\x00\x00\x1d\xc1\xcf`\x00\x00\x00\x00\x1e\xb1\xcep\x00\x00\x00\x00" + + "\x1f\xa1\xb1`\x00\x00\x00\x00 v\x00\xf0\x00\x00\x00\x00!\x81\x93`\x00\x00\x00\x00\"U\xe2\xf0\x00\x00\x00\x00#j\xaf\xe0\x00\x00\x00\x00$5\xc4\xf0\x00\x00\x00\x00%J\x91\xe0\x00\x00\x00\x00&\x15\xa6\xf0" + + "\x00\x00\x00\x00'*s\xe0\x00\x00\x00\x00'\xfe\xc3p\x00\x00\x00\x00)\nU\xe0\x00\x00\x00\x00)ޥp\x00\x00\x00\x00*\xea7\xe0\x00\x00\x00\x00+\xbe\x87p\x00\x00\x00\x00,\xd3T`\x00\x00\x00\x00" + + "-\x9eip\x00\x00\x00\x00.\xb36`\x00\x00\x00\x00/~Kp\x00\x00\x00\x000\x93\x18`\x00\x00\x00\x001gg\xf0\x00\x00\x00\x002r\xfa`\x00\x00\x00\x003GI\xf0\x00\x00\x00\x004R\xdc`" + + "\x00\x00\x00\x005'+\xf0\x00\x00\x00\x0062\xbe`\x00\x00\x00\x007\a\r\xf0\x00\x00\x00\x008\x1b\xda\xe0\x00\x00\x00\x008\xe6\xef\xf0\x00\x00\x00\x009\xfb\xbc\xe0\x00\x00\x00\x00:\xc6\xd1\xf0\x00\x00\x00\x00" + + ";۞\xe0\x00\x00\x00\x00<\xaf\xeep\x00\x00\x00\x00=\xbb\x80\xe0\x00\x00\x00\x00>\x8f\xd0p\x00\x00\x00\x00?\x9bb\xe0\x00\x00\x00\x00@o\xb2p\x00\x00\x00\x00A\x84\x7f`\x00\x00\x00\x00BO\x94p" + + "\x00\x00\x00\x00Cda`\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x01\x02\x03\x04\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05" + + "\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\x02\x05\xff\xff\xb2%\x00\x00\xff\xff" + + "\xab\xa0\x00\x04\xff\xff\xb9\xb0\x00\b\xff\xff\xc7\xc0\x01\f\xff\xff\xc7\xc0\x01\x10\xff\xff\xc7\xc0\x01\x14LMT\x00CST\x00EST\x00EWT\x00EPT\x00EDT\x00\nEST5EDT" + + ",M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\n\x00\x1c\x00US/ArizonaUT\t\x00" + + "\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x04\x00\x00\x00\x10\xff" + + "\xff\xff\xff^\x04\f\xb0\xff\xff\xff\xff\x9e\xa6:\x90\xff\xff\xff\xff\x9f\xbb\a\x80\xff\xff\xff\xff\xa0\x86\x1c\x90\xff\xff\xff\xff\xa1\x9a\xe9\x80\xff\xff\xff\xffˉ\f\x90\xff\xff\xff\xff\xcf\x17\xdf\x1c\xff\xff\xff\xff\xcf" + + "\x8f\xe5\xac\xff\xff\xff\xffЁ\x1a\x1c\xff\xff\xff\xff\xfa\xf8u\x10\xff\xff\xff\xff\xfb\xe8X\x00\x02\x01\x02\x01\x02\x03\x02\x03\x02\x01\x02\xff\xff\x96\xee\x00\x00\xff\xff\xab\xa0\x01\x04\xff\xff\x9d\x90\x00\b\xff\xff\xab\xa0" + + "\x01\fLMT\x00MDT\x00MST\x00MWT\x00\nMST7\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vUt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\b\x00\x1c\x00US/Sam" + + "oaUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00" + + "\x03\x00\x00\x00\b\xff\xff\xff\xffn=\xc8\b\xff\xff\xff\xff\x91\x05\xfb\b\x01\x02\x00\x00\xb1x\x00\x00\xff\xff_\xf8\x00\x00\xff\xffeP\x00\x04LMT\x00SST\x00\nSST11\nPK\x03\x04" + + "\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x1c\x00UTCUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZi" + "f2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\a\x00\x00\x00\x1c\xff\xff\xff\xff^\x03\xfe\xa0\xff\xff\xff\xff\x9e\xa6,\x80\xff\xff\xff\xff\x9f\xba\xf9p\xff\xff\xff\xff" + - "\xa0\x86\x0e\x80\xff\xff\xff\xff\xa1\x9a\xdbp\xff\xff\xff\xff\xcaW\"\x80\xff\xff\xff\xff\xca\xd8Gp\xff\xff\xff\xffˈ\xfe\x80\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2a\t\xf0\xff\xff\xff\xff\xd3u\xf3\x00" + - "\xff\xff\xff\xff\xd4@\xeb\xf0\xff\xff\xff\xff\xd5U\xd5\x00\xff\xff\xff\xff\xd6 \xcd\xf0\xff\xff\xff\xff\xd75\xb7\x00\xff\xff\xff\xff\xd8\x00\xaf\xf0\xff\xff\xff\xff\xd9\x15\x99\x00\xff\xff\xff\xff\xd9\xe0\x91\xf0\xff\xff\xff\xff" + - "\xda\xfe\xb5\x80\xff\xff\xff\xff\xdb\xc0s\xf0\xff\xff\xff\xff\xdcޗ\x80\xff\xff\xff\xffݩ\x90p\xff\xff\xff\xff\u07bey\x80\xff\xff\xff\xff߉rp\xff\xff\xff\xff\xe0\x9e[\x80\xff\xff\xff\xff\xe1iTp" + - "\xff\xff\xff\xff\xe2~=\x80\xff\xff\xff\xff\xe3I6p\xff\xff\xff\xff\xe4^\x1f\x80\xff\xff\xff\xff\xe8\xf2\x16\xf0\xff\xff\xff\xff\xea\a\x00\x00\xff\xff\xff\xff\xfe\xb8\x1c\xf0\xff\xff\xff\xff\xff\xa7\xff\xe0\x00\x00\x00\x00" + - "\x00\x97\xfe\xf0\x00\x00\x00\x00\x01\x87\xe1\xe0\x00\x00\x00\x00D/vp\x00\x00\x00\x00EDC`\x00\x00\x00\x00E\xf3\xa8\xf0\x02\x01\x02\x01\x02\x01\x02\x03\x04\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02" + - "\x01\x02\x01\x02\x05\x02\x05\x06\x05\x06\x05\x06\x05\x06\xff\xff\xaf:\x00\x00\xff\xff\xb9\xb0\x01\x04\xff\xff\xab\xa0\x00\b\xff\xff\xb9\xb0\x01\f\xff\xff\xb9\xb0\x01\x10\xff\xff\xb9\xb0\x00\x14\xff\xff\xc7\xc0\x01\x18LMT\x00" + - "CDT\x00CST\x00CWT\x00CPT\x00EST\x00EDT\x00\nEST5EDT,M3.2.0,M11.1.0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\t\x00\x1c\x00US/HawaiiUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x14\xff\xff\xff\xfft\xe0p\xbe\xff\xff\xff\xff\xbb\x05CH\xff\xff\xff\xff\xbb!qX\xff\xff\xff\xffˉ" + - "=\xc8\xff\xff\xff\xff\xd2#\xf4p\xff\xff\xff\xff\xd2aI8\xff\xff\xff\xffՍsH\x01\x02\x01\x03\x04\x01\x05\xff\xffl\x02\x00\x00\xff\xfflX\x00\x04\xff\xffzh\x01\b\xff\xffzh\x01\f\xff\xffz" + - "h\x01\x10\xff\xffs`\x00\x04LMT\x00HST\x00HDT\x00HWT\x00HPT\x00\nHST10\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00" + - "\x00\x00\x03\x00\x1c\x00UTCUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\xf1c9R2\x91B\xc0\xee\x01\x00\x00\xee\x01\x00\x00\x03\x00\x1c\x00WE" + - "TUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x02" + - "\x00\x00\x00\t\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10" + - "\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00" + - "\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10" + - "\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf2y\xff\xff\xff\xff\x9e*\xee\xf9\xff\xff\xff\xff\x9e\xf79i" + - "\xff\xff\xff\xff\x9f\x84W\xf9\xff\xff\xff\xff\xa0\xd8l\xe9\xff\xff\xff\xff\xa1\x009\x80\xff\xff\xff\xff\xa1<\xa6@\xff\xff\xff\xff\xa4\x10m\xc0\xff\xff\xff\xff\xa4=2\xb0\xff\xff\xff\xff\xa5\x15h\xb0\xff\xff\xff\xff" + - "\xa5=\x03\xc0\xff\xff\xff\xff\xa7\x1eEP\xff\xff\xff\xff\xb5\xa4\x19`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0" + - "\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00" + - " lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p" + - "\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)x\xbf\x80\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00" + - "-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x002r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0" + - "\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00" + - ";\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p" + - "\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00" + - "IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00TL\x1d`\x01\x03\x02\x03\x04\x02\x04\x05\x06\x05\a\x05\x06\b\x06\x05" + - "\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\t\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + - "\n\x06\x00\x00#9\x00\x00\x00\x00#9\x00\x04\x00\x001\x87\x01\b\x00\x00#w\x00\x04\x00\x00?\x97\x01\f\x00\x008@\x01\x11\x00\x00*0\x00\x15\x00\x00FP\x01\x19\x00\x00\x1c \x00\x1d\x00\x00*0" + - "\x01!\x00\x008@\x00\x15LMT\x00MMT\x00MST\x00MDST\x00MSD\x00MSK\x00+05\x00EET\x00EEST\x00\nMSK-3\nPK\x03\x04\n\x00\x00" + - "\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x04\x00\x1c\x00ZuluUT\t\x00\x03\x15\xac\x0e`\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00TZif2" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\x00\x00\x00\x00Africa/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81A\x00\x00\x00Africa/Nair" + - "obiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81H\x01\x00\x00Africa/FreetownUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9R\x9f\x1b\xeb\xdd2\x02\x00\x002\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x13\x02\x00\x00Africa/CeutaUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8b\x04\x00\x00" + - "Africa/AsmeraUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00" + - "\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x91\x05\x00\x00Africa/LuandaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x14\xcf\x10n\xca\x01\x00\x00\xca\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8c\x06\x00\x00Africa/JubaUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81\x9b\b\x00\x00Africa/JohannesburgUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa6\t\x00\x00Africa/BujumburaUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x12tnj\xfc\x04\x00\x00\xfc\x04\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81s\n" + - "\x00\x00Africa/CairoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RV\xadD\xef\xca\x01" + - "\x00\x00\xca\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb5\x0f\x00\x00Africa/KhartoumUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8\x11\x00\x00Africa/Mbab" + - "aneUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R)\xae\x8eo&\a\x00\x00&\a\x00\x00\x0f\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xce\x12\x00\x00Africa/El_AaiunUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9R6\x99rU\xa4\x00\x00\x00\xa4\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81=\x1a\x00\x00Africa/MonroviaUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81*" + - "\x1b\x00\x00Africa/LusakaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87" + - "\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf4\x1b\x00\x00Africa/BamakoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbd\x1c\x00\x00Africa/Niam" + - "eyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\xa4\x81\xb8\x1d\x00\x00Africa/KigaliUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xca>\xd5\xe0\x95\x00\x00\x00\x95\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x82\x1e\x00\x00Africa/BissauUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81^\x1f\x00\x00Af" + - "rica/KinshasaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00" + - "\x00\xbf\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81[ \x00\x00Africa/Addis_AbabaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x93\xf4\x94\v\xc1\x01\x00\x00\xc1\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81f!\x00\x00Africa/Tu" + - "nisUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81m#\x00\x00Africa/BanjulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x816$\x00\x00Africa/OuagadougouUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "\x04%\x00\x00Africa/LibrevilleUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x03&\x00\x00Africa/BrazzavilleUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x03'\x00\x00A" + - "frica/BanguiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00" + - "\x82\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfe'\x00\x00Africa/AbidjanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RÊ\x0e\xc0\xd6\x01\x00\x00\xd6\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8(\x00\x00Africa/Algiers" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81\xe6*\x00\x00Africa/NouakchottUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xc1\n\x8a\x84\xad\x00\x00\x00\xad\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb3+\x00\x00Africa/Sao_TomeUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa9," + - "\x00\x00Africa/Dar_es_SalaamUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb6-\x00\x00Africa/LubumbashiUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x84.\x00\x00A" + - "frica/KampalaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00" + - "\x00\x83\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8b/\x00\x00Africa/BlantyreUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81W0\x00\x00Africa/Malab" + - "oUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xaa\x81\t\x03\xa0\x00\x00\x00\xa0\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81R1\x00\x00Africa/NdjamenaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81;2\x00\x00Africa/TimbuktuUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x063\x00" + - "\x00Africa/GaboroneUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rd\x01\x05\x89" + - "\u007f\a\x00\x00\u007f\a\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd23\x00\x00Africa/CasablancaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9c;\x00\x00Africa/" + - "MaputoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\f\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81f<\x00\x00Africa/LagosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9R\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81`=\x00\x00Africa/MaseruUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81e>\x00" + - "\x00Africa/Porto-NovoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b" + - "{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81d?\x00\x00Africa/ConakryUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81.@\x00\x00Africa/D" + - "oualaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x10\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81)A\x00\x00Africa/MogadishuUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e" + - "\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x812B\x00\x00Africa/DakarUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R_\u007f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfa" + - "B\x00\x00Africa/TripoliUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x1b\xb0" + - "_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf1D\x00\x00Africa/HarareUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbbE\x00\x00Africa/Asm" + - "araUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rm)\xb8P~\x02\x00\x00~\x02\x00\x00\x0f\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc1F\x00\x00Africa/WindhoekUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9R\xee\xc4h2\xbc\x02\x00\x00\xbc\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x88I\x00\x00Africa/AccraUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8aL\x00\x00" + - "Africa/LomeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf" + - "\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81QM\x00\x00Africa/DjiboutiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedAYN\x00\x00America/UT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x82\x13z\xe2\xc2\x00\x00\x00\xc2\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "\x9bN\x00\x00America/TegucigalpaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaaO\x00\x00America/St_KittsUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81vP\x00\x00A" + - "merica/Puerto_RicoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xdf\b" + - "\x9c\x9f\xe7\x00\x00\x00\xe7\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81tQ\x00\x00America/BarbadosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x14\xc1r8\xe0\x00\x00\x00\xe0\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa5R\x00\x00Americ" + - "a/AtikokanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00" + - "\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcfS\x00\x00America/DominicaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xac\x8a\x83S\xd4\x00\x00\x00\xd4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9bT\x00\x00America/Guatem" + - "alaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x1e+}\x15\xb4\x02\x00\x00\xb4\x02\x00\x00\x14\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbaU\x00\x00America/Rankin_InletUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbcX\x00\x00America/TortolaUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R<\xb9\x18\x87\xe4\x02\x00\x00\xe4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81\x87Y\x00\x00America/IqaluitUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R\xd7\b\\\xc6&\x02\x00\x00&\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb4\\\x00\x00America/MiquelonUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x11Z\xde\xe4\x01\x00\x00\xe4\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81$_\x00\x00A" + - "merica/FortalezaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd6\xfe\xf3%" + - "\xb4\x02\x00\x00\xb4\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Sa\x00\x00America/ResoluteUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Qd\x00\x00America/" + - "St_ThomasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xbf\x03u\xf3\xe4\x01\x00\x00\xe4\x01\x00" + - "\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1ee\x00\x00America/RecifeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Jg\x00\x00America/CuracaoUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7\x17jҲ\x00\x00\x00\xb2\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81*h\x00\x00America/MartiniqueUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R?\xc9\x1c\xd4\xc6\x03\x00\x00\xc6\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81(i\x00\x00America/JuneauUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R.\xbe\x1a>\xe7\x03\x00\x00\xe7\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x816m\x00\x00" + - "America/BoiseUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xaaʂA\xcd\x00\x00" + - "\x00\xcd\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81dq\x00\x00America/Blanc-SablonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\u007f$*\xa0\xa6\x03\x00\x00\xa6\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\u007fr\x00\x00America" + - "/CuiabaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf7\xe9 y\xbd\x02\x00\x00\xbd\x02\x00\x00\x0e" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81mv\x00\x00America/InuvikUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e" + - "\x03\n\x00\x00\x00\x00\x00\xf1c9RU!\x12f\xd9\x02\x00\x00\xd9\x02\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ry\x00\x00America/Yellowknife" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x10\x00\xedA\x98|\x00\x00America/Indiana/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe2|\x00\x00America/Indiana/KnoxUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x01\xd8N\x8c\xab\x02\x00\x00\xab\x02\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81(\x81\x00\x00America/Indiana/PetersburgUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x1c\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81'\x84\x00\x00America/Indiana/In" + - "dianapolisUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RصK\xa6\n\x02\x00\x00\n\x02" + - "\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x90\x86\x00\x00America/Indiana/Tell_CityUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R \x17\x89}q\x01\x00\x00q\x01\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xed\x88\x00\x00Ameri" + - "ca/Indiana/VevayUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RK-E\xfa" + - "d\x02\x00\x00d\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xad\x8a\x00\x00America/Indiana/WinamacUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RM/U\x9f7\x02\x00\x007\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81b\x8d\x00\x00A" + - "merica/Indiana/MarengoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\r\xedsp.\x02\x00\x00.\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xea\x8f\x00\x00America/Indiana/VincennesUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rc)\xf6)\xb3\x00\x00\x00\xb3\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81k\x92\x00\x00America/BogotaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R.\xf9\xc0\x1e\xd5\x05\x00\x00\xd5\x05\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81f\x93\x00\x00America/MonctonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x84\x99\x00\x00Amer" + - "ica/MarigotUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R挋\x92\xf6\x01\x00\x00\xf6" + - "\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81O\x9a\x00\x00America/MaceioUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8d\x9c\x00\x00America/PanamaU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa2\x81\xbfyS\x02\x00\x00S\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81j\x9d\x00\x00America/MetlakatlaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xfe\xe6\xf5J\x05\x04\x00\x00\x05\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\t\xa0\x00\x00America/DawsonUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81V\xa4\x00" + - "\x00America/CatamarcaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xca" + - "g\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81e\xa7\x00\x00America/AntiguaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA0\xa8\x00\x00America" + - "/Kentucky/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xdf\xe5\x8d\xc4\xda\x04\x00\x00\xda\x04" + - "\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81{\xa8\x00\x00America/Kentucky/LouisvilleUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x03\x1a|J\xcc\x03\x00\x00\xcc\x03\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaa\xad\x00\x00Ame" + - "rica/Kentucky/MonticelloUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R$\r\x89l\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81˱\x00\x00America/OjinagaUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf8\xb3\x00\x00A" + - "merica/ArubaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x81{\xc1\x92\xbc\x03\x00\x00" + - "\xbc\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ִ\x00\x00America/SitkaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rѱ\x86b\xee\x03\x00\x00\xee\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ٸ\x00\x00America/NassauU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9d?\xdfڸ\x03\x00\x00\xb8\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\x0f\xbd\x00\x00America/Sao_PauloUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9Rg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x12\xc1\x00\x00America/Rio_BrancoUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb1݂x\xe8\x00\x00\x00\xe8\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "\x00\xc3\x00\x00America/Costa_RicaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\x1b\x81-\xa9\x8a\x01\x00\x00\x8a\x01\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x814\xc4\x00\x00America/Porto_VelhoUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\v\xc6\x00" + - "\x00America/IndianapolisUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xb4T\xbd\xeb5\x02\x00\x005\x02\x00\x00\x16\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81l\xc8\x00\x00America/Port-au-PrinceUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xea$\xc1\xbf\xb0\x00\x00\x00\xb0\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf1" + - "\xca\x00\x00America/El_SalvadorUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xee\xcb\x00\x00America/AdakUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R~\xb2\x0e\x19V\a\x00\x00V\a\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfd\xcf\x00\x00Americ" + - "a/St_JohnsUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RJtZ\x8c\x01\x03\x00\x00\x01\x03" + - "\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9d\xd7\x00\x00America/PangnirtungUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RU\xactA\xb5\x01\x00\x00\xb5\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xeb\xda\x00\x00America/Mat" + - "amorosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rs\xb0\xeau\xb4\x01\x00\x00\xb4\x01\x00\x00\x10\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xeb\xdc\x00\x00America/EirunepeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe9\xde\x00\x00America/ShiprockUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81E\xe3\x00\x00America/GodthabUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9RU\r\xf7\xd3\xc7\x01\x00\x00\xc7\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81_\xe5\x00\x00America/ThuleUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe5s\xb3\\'\x01\x00\x00'\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81m\xe7\x00\x00Amer" + - "ica/ManaguaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RM\x94\xc7Kp\x03\x00\x00p" + - "\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdd\xe8\x00\x00America/Glace_BayUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R⚵\xfb\x9e\x00\x00\x00\x9e\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x98\xec\x00\x00America/Cres" + - "tonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RB\xa0=:\x1e\x01\x00\x00\x1e\x01\x00\x00\x12\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\u007f\xed\x00\x00America/HermosilloUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e" + - "\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe9\xee\x00\x00America/Santa_Isabe" + - "lUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP\x0f(\b=\x01\x00\x00=\x01\x00\x00\x15\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x818\xf3\x00\x00America/Santo_DomingoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc4\xf4\x00\x00America/St_Vincent" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd6\xe1Հ\x9c\x01\x00\x00\x9c\x01\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81\x92\xf5\x00\x00America/Mexico_CityUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x15\xc8\xcb\x00\xac\x00\x00\x00\xac\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81{\xf7\x00\x00America/GuyanaUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81o" + - "\xf8\x00\x00America/Port_of_SpainUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81@\xf9\x00\x00America/DetroitUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\f\xfd\x00\x00A" + - "merica/Argentina/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RR\xc8\xd9" + - "\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81X\xfd\x00\x00America/Argentina/CatamarcaUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00 \x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "q\x00\x01\x00America/Argentina/ComodRivadaviaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8b}\xb6\x1e\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8f\x03\x01\x00America/Argent" + - "ina/UshuaiaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RŒZ\x8c\xc4\x02\x00\x00\xc4" + - "\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa6\x06\x01\x00America/Argentina/MendozaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x1e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbd\t\x01\x00Amer" + - "ica/Argentina/Buenos_AiresUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9Rm\aD\x0e\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd9\f\x01\x00America/Argentina/La_Rio" + - "jaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8ep\xb4c\xc4\x02\x00\x00\xc4\x02\x00\x00\x1e\x00\x18\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\xa4\x81\xfa\x0f\x01\x00America/Argentina/Rio_GallegosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x16\x13\x01\x00America/" + - "Argentina/JujuyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xfcz=\xe1\xcd" + - "\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x19\x16\x01\x00America/Argentina/San_JuanUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x1c\x80\xb9\\\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81:\x19\x01" + - "\x00America/Argentina/San_LuisUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9Rt*\x9b!\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81[\x1c\x01\x00America/Argentina/Salta" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RY\xd8֭\xd6\x02\x00\x00\xd6\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81^\x1f\x01\x00America/Argentina/TucumanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x87\"\x01\x00America/Argenti" + - "na/CordobaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe3\xc9I\xd0U\x03\x00\x00U\x03" + - "\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9e%\x01\x00America/Grand_TurkUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81?)\x01\x00America/Knox" + - "_INUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8f\x19Ԇ\x12\x02\x00\x00\x12\x02\x00\x00\x16\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x80-\x01\x00America/Bahia_BanderasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe2/\x01\x00America/VirginU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa1'\a\xbd\x97\x00\x00\x00\x97\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xac0\x01\x00America/CayenneUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8c1\x01\x00America/MontserratUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R?_p\x99\x0e\x05\x00\x00\x0e\x05\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Z2" + - "\x01\x00America/WinnipegUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xca" + - "g\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb27\x01\x00America/AnguillaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RŒZ\x8c\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81~8\x01\x00Americ" + - "a/MendozaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x04,2h\x99\x01\x00\x00\x99\x01\x00" + - "\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8b;\x01\x00America/SantaremUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81n=\x01\x00America/St_Luci" + - "aUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81:>\x01\x00America/CaymanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9Rp\x1b\xceRC\x03\x00\x00C\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17?\x01\x00America/NipigonUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa3B\x01\x00" + - "America/DenverUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R+\x10`ȫ\x02" + - "\x00\x00\xab\x02\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfdF\x01\x00America/Dawson_CreekUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf6I\x01\x00Americ" + - "a/NuukUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf2\x04\xde\xdd\x11\x02\x00\x00\x11\x02\x00\x00\x0e\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\rL\x01\x00America/CancunUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03" + - "\n\x00\x00\x00\x00\x00\xf1c9Rn\xab\xd5\xf9\xcf\x03\x00\x00\xcf\x03\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81fN\x01\x00America/NomeUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R,\xdb~\xab\xb2\x03\x00\x00\xb2\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81{R" + - "\x01\x00America/YakutatUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag" + - "\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81vV\x01\x00America/GrenadaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81AW\x01\x00America/" + - "RosarioUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xac\x8e\xee\x13\xbe\x00\x00\x00\xbe\x00\x00\x00\x0f" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81NZ\x01\x00America/CaracasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ro_\x00v/\x01\x00\x00/\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81U[\x01\x00America/MeridaUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81\xcc\\\x01\x00America/Buenos_AiresUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x1b\vKdC\x03\x00\x00C\x03\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xde_\x01\x00America/Rainy_RiverUT\x05\x00\x03" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4" + - "\x81nc\x01\x00America/PhoenixUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R" + - "\xdf\xe5\x8d\xc4\xda\x04\x00\x00\xda\x04\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa7d\x01\x00America/LouisvilleUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rk^2S\xb9\x04\x00\x00\xb9\x04\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcdi\x01\x00Am" + - "erica/Punta_ArenasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xf5" + - "K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd4n\x01\x00America/Porto_AcreUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc2p\x01\x00Amer" + - "ica/New_YorkUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xad`\x12\xe9\xaa\x00\x00\x00" + - "\xaa\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdcw\x01\x00America/La_PazUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb7-2f\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcex\x01\x00America/Noronh" + - "aUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81\xfbz\x01\x00America/GuadeloupeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x1e\xfbn۸\x03\x00\x00\xb8\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc9{\x01\x00America/Campo_GrandeU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R:\x9a1T\xdf\x01\x00\x00\xdf\x01\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xcf\u007f\x01\x00America/ScoresbysundUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\xfc\x81\x01\x00America/North_Dakota/" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RH\xeam\xef\xde\x03\x00\x00\xde\x03\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81K\x82\x01\x00America/North_Dakota/CenterUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RR\x1b\x8b(\xde\x03\x00\x00\xde\x03\x00\x00\x1e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81~\x86\x01\x00America/North" + - "_Dakota/New_SalemUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb7.\xb6" + - "*\x13\x04\x00\x00\x13\x04\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb4\x8a\x01\x00America/North_Dakota/BeulahUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x1d\xf7\a ,\x06\x00\x00,\x06\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "\x1c\x8f\x01\x00America/Goose_BayUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x93\x95\x01\x00America/Fort_WayneUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R):\x17-\x88\x06\x00\x00\x88\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf2\x97\x01\x00A" + - "merica/HalifaxUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x1c\xd8\x19\x9dp\x01" + - "\x00\x00p\x01\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Þ\x01\x00America/Swift_CurrentUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R[Sp\x90\x02\x05\x00\x00\x02\x05\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x82\xa0\x01\x00Ameri" + - "ca/SantiagoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R8\xcdZ\x05o\x01\x00\x00o" + - "\x01\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Υ\x01\x00America/MazatlanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rg\xcag\xe7\x82\x00\x00\x00\x82\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x87\xa7\x01\x00America/St_Ba" + - "rthelemyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x89غ\xee\x15\x04\x00\x00\x15\x04\x00\x00" + - "\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81X\xa8\x01\x00America/BelizeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcd\xc3v\xe3\xb3\x00\x00\x00\xb3\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb5\xac\x01\x00America/GuayaquilU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc0\x98\x00\b\xc9\x03\x00\x00\xc9\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xb3\xad\x01\x00America/MontevideoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ȱ\x01\x00America/Los_AngelesUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\a\x1c\x9e\x9a]\x04\x00\x00]\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81#\xb7\x01\x00America/HavanaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R" + - "8O:\xbf\x95\x03\x00\x00\x95\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Ȼ\x01\x00America/MenomineeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa8\xbf\x01\x00Ame" + - "rica/JujuyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04" + - "\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa1\xc2\x01\x00America/TijuanaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xeb\xc6\x01\x00America/Edmonto" + - "nUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x82s\x1dT\x01\x00\x00T\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81\xff\xca\x01\x00America/ChihuahuaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9R\x1d`̟\x00\x03\x00\x00\x00\x03\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9e\xcc\x01\x00America/Cambridge_BayU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xed\xcf\x01\x00America/WhitehorseUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xf6@\rm\xa8\x05\x00\x00\xa8\x05\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81>\xd4\x01\x00America/Fort_NelsonUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x813\xda\x01\x00America/AtkaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ra\xcb" + - "'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81B\xde\x01\x00America/ManausUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\xf9\x1dɻ\x00\x00\x00\xbb\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81&\xe0\x01\x00America/" + - "ParamariboUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xfe7\xa1\x87\x1b\x01\x00\x00\x1b\x01" + - "\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81-\xe1\x01\x00America/LimaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rd\xa9y\x9at\x03\x00\x00t\x03\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8e\xe2\x01\x00America/AsuncionUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81L\xe6\x01\x00America/ChicagoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9RMv\xa1\x0f%\x01\x00\x00%\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81o\xed\x01\x00America/MonterreyUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x14\xc1r8\xe0\x00\x00\x00\xe0\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdf\xee\x01\x00" + - "America/Coral_HarbourUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R錴$q\x03\x00\x00q\x03\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0e\xf0\x01\x00America/Thunder_BayUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcc\xf3\x01\x00" + - "America/EnsenadaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9ROKj\xc7" + - "\xaa\x02\x00\x00\xaa\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17\xf8\x01\x00America/BahiaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\b\xfb\x01\x00America/Mon" + - "trealUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\x0f\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\a\x02\x02\x00America/JamaicaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03" + - "\n\x00\x00\x00\x00\x00\xf1c9R5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa3\x03\x02\x00America/AnchorageUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rk\xc2\rx\xbf\x01\x00\x00\xbf\x01\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81\xbf\a\x02\x00America/DanmarkshavnUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcc\t\x02\x00America/KralendijkUT\x05\x00\x03" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R<\x01V\rP\x02\x00\x00P\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4" + - "\x81\xaf\n\x02\x00America/AraguainaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81J\r\x02\x00America/CordobaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\u0096dK~\x02\x00\x00~\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81W\x10\x02\x00Ame" + - "rica/ReginaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x85-\xb9\xf8\x8a\x01\x00\x00\x8a" + - "\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1d\x13\x02\x00America/BelemUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK" + - "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xee\x14\x02\x00America/TorontoU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RU9#\xbe2\x05\x00\x002\x05\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xec\x1b\x02\x00America/VancouverUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xf8Dz\x97\xae\x01\x00\x00\xae\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81i!\x02\x00America/Boa_VistaUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x19vv\xa0\x97\x00\x00\x00\x97\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81b" + - "#\x02\x00America/Lower_PrincesUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedAH$\x02\x00Antarctica/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc2\v\xae\b\x85\x00\x00\x00\x85\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8d$\x02\x00Antar" + - "ctica/VostokUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x95{\xf3\xa9w\x03\x00\x00" + - "w\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81]%\x02\x00Antarctica/PalmerUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R:\xc8P7\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1f)\x02\x00Antarctica/" + - "TrollUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x12\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1a*\x02\x00Antarctica/McMurdoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x95\xea\x06\xd3\xc5\x00\x00\x00\xc5\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81y.\x02\x00Antarctica/DavisU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\r\x0e\xf20\x85\x00\x00\x00\x85\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\x88/\x02\x00Antarctica/SyowaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xc8\x14\xdcA\x98\x00\x00\x00\x98\x00\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81W0\x02\x00Antarctica/DumontDUrville" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd7N\xab\x8b\x98\x00\x00\x00\x98\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81B1\x02\x00Antarctica/MawsonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9RƉ\xf71\x84\x00\x00\x00\x84\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81%2\x02\x00Antarctica/RotheraUT\x05\x00\x03" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xddzAh\xf3\x00\x00\x00\xf3\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4" + - "\x81\xf52\x02\x00Antarctica/CaseyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xb2\x84J]\xd0\x03\x00\x00\xd0\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x8124\x02\x00Antarctica/MacquarieUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81P8\x02" + - "\x00Antarctica/South_PoleUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\xb2<\x02\x00Arctic/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa5\x97\aĤ\x02\x00\x00\xa4\x02\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf3<\x02\x00Arctic/Long" + - "yearbyenUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\xe4?\x02\x00Asia/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R[u\x99q\xf1\x02\x00\x00\xf1\x02\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81#@\x02\x00Asia/TomskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x81z&\x80k\x02\x00\x00k\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81XC\x02\x00Asia/Ch" + - "oibalsanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00" + - "\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\fF\x02\x00Asia/ThimbuUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xebF\x02\x00Asia/VientianeUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcb" + - "G\x02\x00Asia/YangonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R6j\\J\xcf\x04" + - "\x00\x00\xcf\x04\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcbH\x02\x00Asia/HebronUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdfM\x02\x00Asia/ChongqingU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R'\xe2\\\xff\x9f\x00\x00\x00\x9f\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xb0O\x02\x00Asia/KabulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xda" + - "v\x19z\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x93P\x02\x00Asia/BahrainUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf9l\x03\x12\xf8\x02\x00\x00\xf8\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81qQ\x02\x00Asia/Irku" + - "tskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RO\xb0\x03\xe9\xe5\x02\x00\x00\xe5\x02\x00\x00\f\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xafT\x02\x00Asia/YakutskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9R.>[K\xab\x00\x00\x00\xab\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdaW\x02\x00Asia/JayapuraUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xccX\x02\x00As" + - "ia/Hong_KongUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RS\xa5\x81e\xf7\x00\x00\x00" + - "\xf7\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1b\\\x02\x00Asia/PontianakUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R;\u007fP\x8d\xd4\a\x00\x00\xd4\a\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Z]\x02\x00Asia/TehranUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rd%\x05\xd8\xe6\x02\x00\x00\xe6\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81se\x02\x00Asia/VladivostokUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R:\x11\xea\xa2\xe5\x02\x00\x00\xe5\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa3h\x02\x00Asia/OmskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcbk\x02\x00Asia/Tai" + - "peiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0fn\x02\x00Asia/DaccaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R]S\xbb\x12\xac\x03\x00\x00\xac\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81:o\x02\x00Asia/FamagustaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81.s\x02\x00Asi" + - "a/RiyadhUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00" + - "\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf8s\x02\x00Asia/ChungkingUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xceG|\xea\x13\x03\x00\x00\x13\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc9u\x02\x00Asia/AmmanUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81 y" + - "\x02\x00Asia/MakassarUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8bSnT\xa1" + - "\x00\x00\x00\xa1\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81%z\x02\x00Asia/KathmanduUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0e{\x02\x00Asia/Shangh" + - "aiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd5ΜGp\x02\x00\x00p\x02\x00\x00\x0e\x00\x18\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\xa4\x81\xde|\x02\x00Asia/QyzylordaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x17✳2\x04\x00\x002\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x96\u007f\x02\x00Asia/Tel_AvivUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xe27Yn\x01\x00\x00n\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0f\x84\x02\x00A" + - "sia/TashkentUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00" + - "\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ą\x02\x00Asia/KuwaitUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8e\x86\x02\x00Asia/UrumqiUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Re\x1bb2w\x01\x00\x00w\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "X\x87\x02\x00Asia/AshkhabadUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf0\x9c" + - "f>\xd7\x02\x00\x00\xd7\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17\x89\x02\x00Asia/KamchatkaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x87\xbd\xedL\xf1\x02\x00\x00\xf1\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x816\x8c\x02\x00Asia/Bar" + - "naulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8a\x9a\x90\xf7\xd6\x02\x00\x00\xd6\x02\x00\x00\x11\x00\x18\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81m\x8f\x02\x00Asia/NovokuznetskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e" + - "\x03\n\x00\x00\x00\x00\x00\xf1c9R0]*\x1bj\x02\x00\x00j\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8e\x92\x02\x00Asia/BishkekUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa7f^]@\x01\x00\x00@\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81>" + - "\x95\x02\x00Asia/KuchingUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x84)\r\xbd\xec" + - "\x00\x00\x00\xec\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Ė\x02\x00Asia/SaigonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf5\x97\x02\x00Asia/Singapore" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R)p\x1cX\xf1\x02\x00\x00\xf1\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81=\x99\x02\x00Asia/NovosibirskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R?\xa7^\xfah\x02\x00\x00h\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81x\x9c\x02\x00Asia/AtyrauUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x88έ\xe2\xbd\x04\x00\x00\xbd\x04\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81%\x9f\x02\x00Asi" + - "a/GazaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RΒ\x1a\x8c\xaa\x00\x00\x00\xaa\x00\x00\x00\t\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81%\xa4\x02\x00Asia/DiliUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9R\xab\xcd\xdf\x05\xee\x02\x00\x00\xee\x02\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x12\xa5\x02\x00Asia/ChitaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81D\xa8\x02\x00Asia/" + - "Ulan_BatorUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rw\rD\an\x01\x00\x00n\x01" + - "\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81ߪ\x02\x00Asia/SamarkandUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK" + - "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x03R\xda\xedU\x02\x00\x00U\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x95\xac\x02\x00Asia/NicosiaUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x810\xaf\x02\x00Asia/AdenUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R?Y\xaf\x19\xe7" + - "\x00\x00\x00\xe7\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf8\xaf\x02\x00Asia/DhakaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R)\x15II\xf3\x02\x00\x00\xf3\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81#\xb1\x02\x00Asia/SakhalinUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RT\x81\x18G^\x02\x00\x00^\x02\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81]\xb4\x02\x00Asia/AqtauUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8a\xc1" + - "\x1eB\xb7\x00\x00\x00\xb7\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xff\xb6\x02\x00Asia/PyongyangUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x17✳2\x04\x00\x002\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfe\xb7\x02\x00Asia/Jer" + - "usalemUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R's\x96\x1en\x01\x00\x00n\x01\x00\x00\r\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81x\xbc\x02\x00Asia/DushanbeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\\\x91\x87\xbb\xf7\x00\x00\x00\xf7\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81-\xbe\x02\x00Asia/ColomboUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R9Y\xb7\xf1\n\x01\x00\x00\n\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81j\xbf\x02" + - "\x00Asia/KarachiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R恸\x1e\x00\x01\x00" + - "\x00\x00\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xba\xc0\x02\x00Asia/Kuala_LumpurUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Re\x1bb2w\x01\x00\x00w\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x05\xc2\x02\x00Asia/Ashga" + - "batUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\f\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc3\xc3\x02\x00Asia/KolkataUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9RB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe5\xc4\x02\x00Asia/KashgarUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xba\xa3b\xc1R\x02\x00\x00R\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb0\xc5\x02\x00Asi" + - "a/HovdUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x03\x87\xb3<\xe8\x02\x00\x00\xe8\x02\x00\x00\t\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81E\xc8\x02\x00Asia/BakuUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + - "\x00\xf1c9R\x83g\x95M\a\x03\x00\x00\a\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81p\xcb\x02\x00Asia/KhandygaUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9a\xea\x18\xd4\xf8\x02\x00\x00\xf8\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbe\xce\x02\x00As" + - "ia/YekaterinburgUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rʇ{_" + - "\xbb\x00\x00\x00\xbb\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x02\xd2\x02\x00Asia/RangoonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rǯ\xdf\x1c\xee\x00\x00\x00\xee\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x03\xd3\x02\x00Asia/ManilaU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x816\xd4\x02\x00Asia/MacaoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RS" + - "\xdd\\2a\x02\x00\x00a\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x91\xd7\x02\x00Asia/AlmatyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RѾ\xa8\xc7u\x02\x00\x00u\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x817\xda\x02\x00Asia/Tbili" + - "siUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0f\x00\x18\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\xa4\x81\xf2\xdc\x02\x00Asia/Phnom_PenhUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd3\xdd\x02\x00Asia/MacauUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe4_P\x18\xef\x02\x00\x00\xef\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81.\xe1\x02\x00Asi" + - "a/MagadanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xdav\x19z\x98\x00\x00\x00\x98\x00\x00" + - "\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81c\xe4\x02\x00Asia/QatarUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81?\xe5\x02\x00Asia/CalcuttaUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81b\xe6" + - "\x02\x00Asia/UlaanbaatarUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa4Z" + - "ߐ\xe6\x02\x00\x00\xe6\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfe\xe8\x02\x00Asia/SrednekolymskUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xdb\xfa\xb5\xbeg\x02\x00\x00g\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x810\xec\x02\x00Asia" + - "/AqtobeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rw\x86\x8d^\x03\x03\x00\x00\x03\x03\x00\x00\r" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdc\xee\x02\x00Asia/Ust-NeraUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03" + - "\n\x00\x00\x00\x00\x00\xf1c9RL\xe0\x91y\xe5\x02\x00\x00\xe5\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81&\xf2\x02\x00Asia/KrasnoyarskUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81U\xf5\x02\x00Asia/SeoulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ry\x19\xe0N" + - "\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x818\xf7\x02\x00Asia/BruneiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd7e&uv\x02\x00\x00v\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17\xf8\x02\x00Asia/BaghdadU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xd3\xfa\x02\x00Asia/Ujung_PandangUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9RV\xe0\xe7!\xe7\x02\x00\x00\xe7\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdd\xfb\x02\x00Asia/AnadyrUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\t\xff\x02\x00As" + - "ia/MuscatUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x84)\r\xbd\xec\x00\x00\x00\xec\x00\x00" + - "\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd3\xff\x02\x00Asia/Ho_Chi_MinhUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xef\\\xf4q\x17\x04\x00\x00\x17\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\t\x01\x03\x00Asia/DamascusUT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81g\x05\x03\x00Asia/ThimphuUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R" + - "\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81G\x06\x03\x00Asia/BangkokUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x02\x95-\xad\xc4\x02\x00\x00\xc4\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81%\a\x03\x00Asia/Yer" + - "evanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8bSnT\xa1\x00\x00\x00\xa1\x00\x00\x00\r\x00\x18\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81/\n\x03\x00Asia/KatmanduUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R&\xe9\xd1\xd8q\x02\x00\x00q\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17\v\x03\x00Asia/OralUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x02\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcb\r\x03\x00Asia" + - "/TokyoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\r\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe4\x0e\x03\x00Asia/IstanbulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\xc7\x11\xe1[\xdc\x02\x00\x00\xdc\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xdb\x13\x03\x00Asia/BeirutUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rb\xadű\xf8\x00\x00\x00\xf8\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfc\x16\x03\x00" + - "Asia/JakartaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xed\x8c\xf1\x91\x85\x00\x00\x00" + - "\x85\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81:\x18\x03\x00Asia/DubaiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa1\xfax\x98g\x02\x00\x00g\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x03\x19\x03\x00Asia/QostanayUT\x05\x00\x03" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4" + - "\x81\xb1\x1b\x03\x00Asia/HarbinUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\u007f\x1d\x03\x00Atlantic/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\u0097N\xad\xaf\x00\x00\x00\xaf\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc2\x1d\x03\x00Atlantic/Cape_V" + - "erdeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe7\xcf^\xb0\x15\x03\x00\x00\x15\x03\x00\x00\x10\x00\x18\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbe\x1e\x03\x00Atlantic/StanleyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03" + - "\n\x00\x00\x00\x00\x00\xf1c9R\x82\xfa Z\x9b\x05\x00\x00\x9b\x05\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1d\"\x03\x00Atlantic/MadeiraUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rl&\x04\x99\x00\x04\x00\x00\x00\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81\x02(\x03\x00Atlantic/BermudaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xaf|7\xb3\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81L,\x03\x00Atlantic/CanaryUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb7\x0e\xbdm\xb9\x01\x00\x00\xb9\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81s.\x03\x00Atl" + - "antic/FaroeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rm\xbd\x10k\xf1\x02\x00\x00\xf1" + - "\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81t0\x03\x00Atlantic/ReykjavikUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8" + - "\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa5\x97\aĤ\x02\x00\x00\xa4\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb13\x03\x00Atlantic/Ja" + - "n_MayenUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa16\x03\x00Atlantic/St_HelenaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x0f-\xadׄ\x00\x00\x00\x84\x00\x00\x00\x16\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81o7\x03\x00Atlantic/South_" + - "GeorgiaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb7\x0e\xbdm\xb9\x01\x00\x00\xb9\x01\x00\x00\x0f" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81C8\x03\x00Atlantic/FaeroeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RW\x99\x9d\v\x9b\x05\x00\x00\x9b\x05\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81E:\x03\x00Atlantic/AzoresUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10" + - "\x00\xedA)@\x03\x00Australia/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RX\xb9\x9a" + - "p\x88\x03\x00\x00\x88\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81m@\x03\x00Australia/NSWUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfe垛\x03\x00\x00\x9b\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "?\xbe\x04\x00Europe/WarsawUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4," + - "\xb6?\x06\x00\x00?\x06\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81!\xc2\x04\x00Europe/LondonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rn\x81\xf4\xd7Z\x04\x00\x00Z\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa7\xc8\x04\x00Europe/Mon" + - "acoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xa5\x97\aĤ\x02\x00\x00\xa4\x02\x00\x00\v\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81H\xcd\x04\x00Europe/OsloUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x811\xd0\x04\x00Europe/PodgoricaUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Y\xd2\x04\x00" + - "Europe/IstanbulUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xab\x80c$q" + - "\x00\x00\x00q\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81R\xd7\x04\x00FactoryUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x02\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x04\xd8\x04\x00GBUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\u007f\xde\x04\x00GB-Eir" + - "eUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81\xff\xe4\x04\x00GMTUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00" + - "\x00\x00o\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xab\xe5\x04\x00GMT+0UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Y\xe6\x04\x00GMT-0UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\a\xe7\x04\x00GMT0UT" + - "\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\xa4\x81\xb4\xe7\x04\x00GreenwichUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RE\t\xfa" + - "-\a\x03\x00\x00\a\x03\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81f\xe8\x04\x00HongkongUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R=\xf7\xfawp\x00\x00\x00p\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaf\xeb\x04\x00HSTUT\x05\x00\x03\x15\xac\x0e`ux\v" + - "\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rm\xbd\x10k\xf1\x02\x00\x00\xf1\x02\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\\\xec\x04\x00Ic" + - "elandUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\x8e\xef\x04\x00Indian/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xcf\xef\x04\x00Indian/MayotteUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb8K\xabυ\x00\x00\x00\x85\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd6\xf0\x04\x00Indi" + - "an/KerguelenUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb9\xb2Z\xac\x98\x00\x00\x00" + - "\x98\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa5\xf1\x04\x00Indian/MaldivesUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ry(\xb6\x8f\x85\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x86\xf2\x04\x00Indian/Reunio" + - "nUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x96\xed=\x98\xb3\x00\x00\x00\xb3\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81S\xf3\x04\x00Indian/MauritiusUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81P\xf4\x04\x00Indian/AntananarivoUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ra\x85jo\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81\\\xf5\x04\x00Indian/MaheUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb4\x8d\x98" + - "ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81&\xf6\x04\x00Indian/ComoroUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R$l=҅\x00\x00\x00\x85\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81,\xf7\x04\x00Indian/Chr" + - "istmasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rx\xb0W\x14\x98\x00\x00\x00\x98\x00\x00\x00\r\x00" + - "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfb\xf7\x04\x00Indian/ChagosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9RͲ\xfb\xf6\x8c\x00\x00\x00\x8c\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xda\xf8\x04\x00Indian/CocosUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R;\u007fP\x8d\xd4\a\x00\x00\xd4\a\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xac\xf9\x04" + - "\x00IranUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x17✳2\x04\x00\x002\x04\x00\x00\x06\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbe\x01\x05\x00IsraelUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x810\x06\x05\x00JamaicaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x02\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc4\a\x05\x00JapanUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd8" + - "\b\x05\x00KwajaleinUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R_\u007f2[\xaf\x01\x00\x00" + - "\xaf\x01\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf6\t\x05\x00LibyaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\xfe\x9d\x1b\xc9m\x02\x00\x00m\x02\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe4\v\x05\x00METUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedA\x8e\x0e\x05\x00Mexico/UT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xd6\xe1Հ\x9c\x01\x00\x00\x9c\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81\xcf\x0e\x05\x00Mexico/GeneralUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9" + - "R\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb3\x10\x05\x00Mexico/BajaNorteUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R8\xcdZ\x05o\x01\x00\x00o\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfe\x14\x05\x00Mex" + - "ico/BajaSurUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf5\x8d\x99\x92o\x00\x00\x00o" + - "\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb5\x16\x05\x00MSTUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xe6h\xcac\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81a\x17\x05\x00MST7MDTUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00" + - "\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Y\x1b\x05\x00NavajoUT\x05" + - "\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x02\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\xa4\x81\xab\x1f\x05\x00NZUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x96\xc5FF(\x03\x00\x00(\x03\x00" + - "\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfa#\x05\x00NZ-CHATUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedAc'\x05\x00Pacific/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8" + - "\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xee\xd0\x1cYN\x04\x00\x00N\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa5'\x05\x00Pacifi" + - "c/EasterUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xfa\x0fA\x05\x99\x00\x00\x00\x99\x00\x00\x00" + - "\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81;,\x05\x00Pacific/PitcairnUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK" + - "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R3\x03\x1f\f\xac\x00\x00\x00\xac\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1e-\x05\x00Pacific/Enderbur" + - "yUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Ra\vೆ\x00\x00\x00\x86\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00" + - "\x00\x00\x00\x00\x00\xa4\x81\x15.\x05\x00Pacific/FunafutiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R\u07b54-\xd6\x00\x00\x00\xd6\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe5.\x05\x00Pacific/PonapeUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xb7\xef\x97\xc6\xc6\x00\x00\x00\xc6\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x030\x05" + - "\x00Pacific/NoumeaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x80\xf8vܔ" + - "\x00\x00\x00\x94\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x111\x05\x00Pacific/PalauUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xec1\x05\x00Pacific/John" + - "stonUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xca\"\xb8i\xda\x00\x00\x00\xda\x00\x00\x00\x0e\x00\x18\x00" + - "\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x133\x05\x00Pacific/MajuroUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00" + - "\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x8154\x05\x00Pacific/Pago_PagoUT\x05\x00\x03" + - "\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9e\u007f\xab\x95V\x01\x00\x00V\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4" + - "\x81\x125\x05\x00Pacific/EfateUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc8=" + - "ku\xae\x00\x00\x00\xae\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaf6\x05\x00Pacific/KiritimatiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01" + - "\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x8a|\xdcU\x99\x00\x00\x00\x99\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa97\x05\x00Paci" + - "fic/FakaofoUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x96\xc5FF(\x03\x00\x00(" + - "\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x8b8\x05\x00Pacific/ChathamUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00" + - "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfc;\x05\x00Pacific/Kwajal" + - "einUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x0e\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\"=\x05\x00Pacific/MidwayUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00" + - "\x00\x00\x00\xf1c9R1\xce_(\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfc=\x05\x00Pacific/WallisUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R4\xd0Yӣ\x01\x00\x00\xa3\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xca>\x05" + - "\x00Pacific/FijiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe9\xdd\x1e\xee\f\x01\x00" + - "\x00\f\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb3@\x05\x00Pacific/ApiaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00P" + - "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00\xc3\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x05B\x05\x00Pacific/YapUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81\rC\x05\x00Pacific/Port_MoresbyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf5C\x05\x00Pacific/HonoluluUT\x05\x00\x03\x15\xac\x0e" + - "`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\u07b54-\xd6\x00\x00\x00\xd6\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1cE" + - "\x05\x00Pacific/PohnpeiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc23\xa0" + - "\xbc\x84\x00\x00\x00\x84\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81;F\x05\x00Pacific/GambierUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe2;Z\xf7\xb7\x00\x00\x00\xb7\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\bG\x05\x00Pacific/" + - "NauruUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x97n7\x1a\xf2\x00\x00\x00\xf2\x00\x00\x00\x0e\x00\x18" + - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x06H\x05\x00Pacific/KosraeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n" + - "\x00\x00\x00\x00\x00\xf1c9R\x85v\xf8\x8c\x87\x01\x00\x00\x87\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81@I\x05\x00Pacific/RarotongaUT\x05\x00" + - "\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RD6\x83\xa1\x8b\x00\x00\x00\x8b\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\xa4\x81\x12K\x05\x00Pacific/MarquesasUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe8K\x05\x00Pacific/TarawaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00\xc3\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb6L\x05\x00Pac" + - "ific/TrukUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x81\xeb\xb8m\xaf\x00\x00\x00\xaf\x00\x00" + - "\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbfM\x05\x00Pacific/NiueUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e" + - "\x03\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb4N\x05\x00Pacific/SamoaUT\x05\x00\x03\x15" + - "\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RFI\xfe\x14^\x01\x00\x00^\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81" + - "\x8dO\x05\x00Pacific/GuamUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RFI\xfe\x14" + - "^\x01\x00\x00^\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x811Q\x05\x00Pacific/SaipanUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04" + - "\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x81\xe3w\n\xaf\x00\x00\x00\xaf\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd7R\x05\x00Pacific/Ga" + - "lapagosUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9a\xf2:F\xc9\x00\x00\x00\xc9\x00\x00\x00\x14" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd1S\x05\x00Pacific/BougainvilleUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R߃\xa0_\x86\x00\x00\x00\x86\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe8T\x05\x00Pacific/WakeU" + - "T\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RP:\xc0\x8c\xed\x00\x00\x00\xed\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x00\xa4\x81\xb4U\x05\x00Pacific/TongatapuUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + - "\x00\x00\xf1c9R\xcc\xf39a\xc3\x00\x00\x00\xc3\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xecV\x05\x00Pacific/ChuukUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xea\xc1\xdaυ\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf6W\x05\x00P" + - "acific/TahitiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RY\xd2K|\x86\x00\x00" + - "\x00\x86\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc3X\x05\x00Pacific/GuadalcanalUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00" + - "\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x96Y\x05\x00Pacific/" + - "AucklandUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RY5\x1a6\xf7\x00\x00\x00\xf7\x00\x00\x00" + - "\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf3]\x05\x00Pacific/NorfolkUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R>\xfe垛\x03\x00\x00\x9b\x03\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x813_\x05\x00PolandUT\x05\x00\x03\x15\xac\x0e`ux" + - "\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xfa\xd5\xd6М\x05\x00\x00\x9c\x05\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0ec\x05\x00P" + - "ortugalUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x03" + - "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xech\x05\x00PRCUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R" + - "ŭV\xad\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb2j\x05\x00PST8PDTUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00" + - "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaan\x05\x00ROCUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe6p\x05\x00" + - "ROKUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\t\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc2r\x05\x00SingaporeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c" + - "9R\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x05t\x05\x00TurkeyUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03" + - "\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf5x\x05\x00UCTUT\x05\x00\x03\x15\xac\x0e`" + - "ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa1y\x05" + - "\x00UniversalUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xedASz\x05\x00US/UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1" + - "c9R\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x90z\x05\x00US/PacificUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03" + - "\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe2\u007f\x05\x00US/Cent" + - "ralUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\v\x00\x18\x00\x00" + - "\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x87\x05\x00US/MichiganUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + - "\xf1c9R\xae,\xa44\xc9\x03\x00\x00\xc9\x03\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Ȋ\x05\x00US/AleutianUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04" + - "\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81֎\x05\x00US/In" + - "diana-StarkeUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9RV\x80\x94@\x12\x04\x00\x00" + - "\x12\x04\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x19\x93\x05\x00US/MountainUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01" + - "\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81p\x97\x05\x00US/ArizonaUT\x05\x00\x03\x15\xac" + - "\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa4" + - "\x98\x05\x00US/EasternUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R5\x11Q\x06\xd1\x03\x00" + - "\x00\xd1\x03\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xb8\x9f\x05\x00US/AlaskaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02" + - "\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81̣\x05\x00US/SamoaUT\x05\x00\x03\x15\xac\x0e`u" + - "x\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9Rp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xa0\xa4\x05\x00" + - "US/East-IndianaUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xeaK\x85v\xdd" + - "\x00\x00\x00\xdd\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xfc\xa6\x05\x00US/HawaiiUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK" + - "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x1c\xa8\x05\x00UTCUT\x05\x00\x03\x15\xac\x0e`ux\v\x00" + - "\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R2\x91B\xc0\xee\x01\x00\x00\xee\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Ȩ\x05\x00WET" + - "UT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\xe1\xc1\xeb\x05\x8c\x03\x00\x00\x8c\x03\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00" + - "\x00\x00\x00\x00\xa4\x81\xf3\xaa\x05\x00W-SUUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\xf1c9R\x9f.\xe4xo\x00" + - "\x00\x00o\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xbd\xae\x05\x00ZuluUT\x05\x00\x03\x15\xac\x0e`ux\v\x00\x01\x04\xe8\x03\x00\x00\x04\xe8\x03\x00\x00PK\x05\x06\x00\x00\x00\x00" + - "f\x02f\x02\x96\xc9\x00\x00j\xaf\x05\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00\nUTC0\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv" + + "\vU2\x91B\xc0\xee\x01\x00\x00\xee\x01\x00\x00\x03\x00\x1c\x00WETUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\x00\x00\x00\x02\x00\x00\x00\t\x00\x00\x00\x00\r\xa4c\x90\x00\x00\x00\x00\x0e\x8b\x1a\x10\x00\x00\x00\x00\x0f\x84E\x90\x00\x00\x00\x00\x10t6\x90\x00\x00\x00\x00" + + "\x11d'\x90\x00\x00\x00\x00\x12T\x18\x90\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90" + + "\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00" + + "\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#\xf2y\xff\xff\xff\xff\x9e*\xee\xf9\xff\xff\xff\xff\x9e\xf79i\xff\xff\xff\xff\x9f\x84W\xf9\xff\xff\xff\xff\xa0\xd8l\xe9\xff\xff\xff\xff\xa1\x009\x80\xff\xff\xff\xff\xa1<\xa6@\xff\xff\xff\xff\xa4\x10m\xc0" + + "\xff\xff\xff\xff\xa4=2\xb0\xff\xff\xff\xff\xa5\x15h\xb0\xff\xff\xff\xff\xa5=\x03\xc0\xff\xff\xff\xff\xa7\x1eEP\xff\xff\xff\xff\xb5\xa4\x19`\x00\x00\x00\x00\x15'\xa7\xd0\x00\x00\x00\x00\x16\x18\xdc@\x00\x00\x00\x00" + + "\x17\b\xdbP\x00\x00\x00\x00\x17\xfa\x0f\xc0\x00\x00\x00\x00\x18\xea\x0e\xd0\x00\x00\x00\x00\x19\xdbC@\x00\x00\x00\x00\x1a̓\xd0\x00\x00\x00\x00\x1b\xbc\xa0\xf0\x00\x00\x00\x00\x1c\xac\x91\xf0\x00\x00\x00\x00\x1d\x9c\x82\xf0" + + "\x00\x00\x00\x00\x1e\x8cs\xf0\x00\x00\x00\x00\x1f|d\xf0\x00\x00\x00\x00 lU\xf0\x00\x00\x00\x00!\\F\xf0\x00\x00\x00\x00\"L7\xf0\x00\x00\x00\x00#<(\xf0\x00\x00\x00\x00$,\x19\xf0\x00\x00\x00\x00" + + "%\x1c\n\xf0\x00\x00\x00\x00&\v\xfb\xf0\x00\x00\x00\x00'\x05'p\x00\x00\x00\x00'\xf5\x18p\x00\x00\x00\x00(\xe5\x17\x80\x00\x00\x00\x00)x\xbf\x80\x00\x00\x00\x00)\xd4\xfap\x00\x00\x00\x00*\xc4\xebp" + + "\x00\x00\x00\x00+\xb4\xdcp\x00\x00\x00\x00,\xa4\xcdp\x00\x00\x00\x00-\x94\xbep\x00\x00\x00\x00.\x84\xafp\x00\x00\x00\x00/t\xa0p\x00\x00\x00\x000d\x91p\x00\x00\x00\x001]\xbc\xf0\x00\x00\x00\x00" + + "2r\x97\xf0\x00\x00\x00\x003=\x9e\xf0\x00\x00\x00\x004Ry\xf0\x00\x00\x00\x005\x1d\x80\xf0\x00\x00\x00\x0062[\xf0\x00\x00\x00\x006\xfdb\xf0\x00\x00\x00\x008\x1bxp\x00\x00\x00\x008\xddD\xf0" + + "\x00\x00\x00\x009\xfbZp\x00\x00\x00\x00:\xbd&\xf0\x00\x00\x00\x00;\xdb\x86%p\x00\x00\x00\x00?\x9b\x00p\x00\x00\x00\x00" + + "@f\ap\x00\x00\x00\x00A\x84\x1c\xf0\x00\x00\x00\x00BE\xe9p\x00\x00\x00\x00Cc\xfe\xf0\x00\x00\x00\x00D%\xcbp\x00\x00\x00\x00EC\xe0\xf0\x00\x00\x00\x00F\x05\xadp\x00\x00\x00\x00G#\xc2\xf0" + + "\x00\x00\x00\x00G\xee\xc9\xf0\x00\x00\x00\x00I\x03\xa4\xf0\x00\x00\x00\x00IΫ\xf0\x00\x00\x00\x00J\xe3\x86\xf0\x00\x00\x00\x00K\xae\x8d\xf0\x00\x00\x00\x00Ḷp\x00\x00\x00\x00M\x8eo\xf0\x00\x00\x00\x00" + + "TL\x1d`\x01\x03\x02\x03\x04\x02\x04\x05\x06\x05\a\x05\x06\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\t\b\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06" + + "\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\x05\x06\n\x06\x00\x00#9\x00\x00\x00\x00#9\x00\x04\x00\x001\x87\x01\b\x00\x00#w\x00\x04\x00\x00?\x97\x01\f\x00\x008@\x01\x11\x00\x00" + + "*0\x00\x15\x00\x00FP\x01\x19\x00\x00\x1c \x00\x1d\x00\x00*0\x01!\x00\x008@\x00\x15LMT\x00MMT\x00MST\x00MDST\x00MSD\x00MSK\x00+05\x00EET" + + "\x00EEST\x00\nMSK-3\nPK\x03\x04\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x04\x00\x1c\x00ZuluUT\t\x00\x03\xaf\x16\xf5b\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00" + + "\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00UTC\x00" + + "\nUTC0\nPK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x00\x00\x00\x00Africa/U" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x81A\x00\x00\x00Africa/NairobiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81H\x01\x00\x00Africa/NiameyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81C\x02\x00\x00Afri" + + "ca/KigaliUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00" + + "\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\r\x03\x00\x00Africa/GaboroneUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd9\x03\x00\x00Africa/LagosUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81\xd3\x04\x00\x00Africa/OuagadougouUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa1\x05\x00\x00Africa/Porto-NovoUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUm)\xb8P~\x02\x00\x00~\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa0\x06\x00" + + "\x00Africa/WindhoekUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98\xc6" + + "\xbf\x00\x00\x00\xbf\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81g\t\x00\x00Africa/DjiboutiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81o\n\x00\x00Africa/Mb" + + "abaneUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81u\v\x00\x00Africa/MaputoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81?\f\x00\x00Africa/AsmaraUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81E\r\x00" + + "\x00Africa/DakarUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU)\xae\x8eo&\a\x00" + + "\x00&\a\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\r\x0e\x00\x00Africa/El_AaiunUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81|\x15\x00\x00Africa/Libre" + + "villeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUV\xadD\xef\xca\x01\x00\x00\xca\x01\x00\x00\x0f\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81{\x16\x00\x00Africa/KhartoumUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8e\x18\x00\x00Africa/AccraUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81V\x19" + + "\x00\x00Africa/BamakoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ" + + "\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1f\x1a\x00\x00Africa/AsmeraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81%\x1b\x00\x00Africa/Addis" + + "_AbabaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x0f\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x810\x1c\x00\x00Africa/KinshasaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81-\x1d\x00\x00Africa/BlantyreUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81\xf9\x1d\x00\x00Africa/BanguiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1" + + "\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf4\x1e\x00\x00Africa/AbidjanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xbe\x1f\x00\x00Africa/" + + "LomeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x85 \x00\x00Africa/MalaboUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00" + + "\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x80!\x00\x00Africa/LusakaUT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xca>\xd5\xe0\x95\x00\x00\x00\x95\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81J\"\x00\x00" + + "Africa/BissauUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00" + + "\x00\x83\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81&#\x00\x00Africa/BujumburaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc1\n\x8a\x84\xad\x00\x00\x00\xad\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf3#\x00\x00Africa/Sao_" + + "TomeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe9$\x00\x00Africa/KampalaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU6\x99rU\xa4\x00\x00\x00\xa4\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf0%\x00\x00Africa/MonroviaUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdd" + + "&\x00\x00Africa/MogadishuUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU_" + + "\x7f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe6'\x00\x00Africa/TripoliUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x93\xf4\x94\v\xc1\x01\x00\x00\xc1\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdd)\x00\x00Africa/" + + "TunisUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xaa\x81\t\x03\xa0\x00\x00\x00\xa0\x00\x00\x00\x0f\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe4+\x00\x00Africa/NdjamenaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vU\x14\xcf\x10n\xca\x01\x00\x00\xca\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xcd,\x00\x00Africa/JubaUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdc.\x00" + + "\x00Africa/DoualaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00" + + "\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd7/\x00\x00Africa/TimbuktuUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_\x83\x00\x00\x00\x83\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa20\x00\x00Africa/Hara" + + "reUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\r\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81l1\x00\x00Africa/LuandaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUÊ\x0e\xc0\xd6\x01\x00\x00\xd6\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81g2\x00\x00Africa/AlgiersUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x854\x00\x00A" + + "frica/ConakryUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00" + + "\x00\x82\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81O5\x00\x00Africa/NouakchottUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1c6\x00\x00Africa/Mas" + + "eruUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcc\fTξ\x00\x00\x00\xbe\x00\x00\x00\x13\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81!7\x00\x00Africa/JohannesburgUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f\x1b\xeb\xdd2\x02\x00\x002\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81,8\x00\x00Africa/CeutaUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x12tnj\xfc\x04\x00\x00\xfc\x04\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\xa4:\x00\x00Africa/CairoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU \x1b\xb0_" + + "\x83\x00\x00\x00\x83\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe6?\x00\x00Africa/LubumbashiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb4@\x00\x00Africa/" + + "BanjulUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUd\x01\x05\x89\x7f\a\x00\x00\x7f\a\x00\x00\x11\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81}A\x00\x00Africa/CasablancaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81GI\x00\x00Africa/FreetownUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7\x1d\xb3c\xb4\x00\x00\x00\xb4\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\x12J\x00\x00Africa/BrazzavilleUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x12K\x00\x00Africa/Dar_es_SalaamUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00" + + "\xe8A\x1fL\x00\x00America/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xef\xf0R\x8a\xc4\x02" + + "\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81aL\x00\x00America/CordobaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUMv\xa1\x0f%\x01\x00\x00%\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81nO\x00\x00America/Mon" + + "terreyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf2\x04\xde\xdd\x11\x02\x00\x00\x11\x02\x00\x00\x0e\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdeP\x00\x00America/CancunUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vU\xad`\x12\xe9\xaa\x00\x00\x00\xaa\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x817S\x00\x00America/La_PazUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1b\vKdC\x03\x00\x00C\x03\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + ")T\x00\x00America/Rainy_RiverUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUutZ\x1a\xb2\x02\x00\x00\xb2\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb9W\x00\x00America/JujuyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb2Z\x00\x00Amer" + + "ica/St_KittsUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUU9#\xbe2\x05\x00\x00" + + "2\x05\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xad[\x00\x00America/VancouverUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81*a\x00\x00America/Ind" + + "ianapolisUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUJtZ\x8c\x01\x03\x00\x00\x01\x03\x00" + + "\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8bc\x00\x00America/PangnirtungUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU挋\x92\xf6\x01\x00\x00\xf6\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd9f\x00\x00America/Mace" + + "ioUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe90T\x16\xd1\x01\x00\x00\xd1\x01\x00\x00\f\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\x17i\x00\x00America/NuukUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vUc)\xf6)\xb3\x00\x00\x00\xb3\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81.k\x00\x00America/BogotaUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe0\xbf\xf5\xe5\xc4\x02\x00\x00\xc4\x02\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81)l\x00\x00Am" + + "erica/Buenos_AiresUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9" + + "*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81;o\x00\x00America/MontserratUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUl=\xad\xbe\x16\x01\x00\x00\x16\x01\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x818p\x00\x00Amer" + + "ica/BarbadosUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1d\xf7\a ,\x06\x00\x00" + + ",\x06\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x98q\x00\x00America/Goose_BayUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU$\r\x89l\xe4\x01\x00\x00\xe4\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x0fx\x00\x00America/Oji" + + "nagaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUg\xf5K\x89\xa2\x01\x00\x00\xa2\x01\x00\x00\x12\x00\x18\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x01\x00America/Argent" + + "ina/CatamarcaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8b}\xb6\x1e\xc4\x02\x00" + + "\x00\xc4\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81 A\x01\x00America/Argentina/UshuaiaUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUt*\x9b!\xb2\x02\x00\x00\xb2\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x817D\x01\x00Am" + + "erica/Argentina/SaltaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "UR\xc8\xd9\xf6\xc4\x02\x00\x00\xc4\x02\x00\x00 \x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81:G\x01\x00America/Argentina/ComodRivada" + + "viaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUm\aD\x0e\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81XJ\x01\x00America/Argentina/La_RiojaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xfcz=\xe1\xcd\x02\x00\x00\xcd\x02\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81yM\x01\x00America/Arg" + + "entina/San_JuanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUŒZ\x8c\xc4" + + "\x02\x00\x00\xc4\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9aP\x01\x00America/Argentina/MendozaUT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1b\x81-\xa9\x8a\x01\x00\x00\x8a\x01\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb1S\x01\x00" + + "America/Porto_VelhoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x88U\x01\x00America/North_Dakota/UT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb7.\xb6*\x13\x04\x00\x00\x13\x04\x00\x00\x1b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd7U\x01\x00" + + "America/North_Dakota/BeulahUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00" + + "\x00\x00\x00\bv\vUR\x1b\x8b(\xde\x03\x00\x00\xde\x03\x00\x00\x1e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81?Z\x01\x00America/North_Dakota/Ne" + + "w_SalemUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUH\xeam\xef\xde\x03\x00\x00\xde\x03\x00\x00\x1b" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81u^\x01\x00America/North_Dakota/CenterUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUB\xa0=:\x1e\x01\x00\x00\x1e\x01\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa8b\x01\x00Americ" + + "a/HermosilloUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU.\xbe\x1a>\xe7\x03\x00\x00" + + "\xe7\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x12d\x01\x00America/BoiseUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81@h\x01\x00America/St_Thom" + + "asUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo_\x00v/\x01\x00\x00/\x01\x00\x00\x0e\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x84\xad\x01\x00America/DetroitUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xef\xf0R\x8a\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81P\xb1\x01\x00Am" + + "erica/RosarioUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf6@\rm\xa8\x05\x00" + + "\x00\xa8\x05\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81]\xb4\x01\x00America/Fort_NelsonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81R\xba\x01\x00America/" + + "Lower_PrincesUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU3\x9aG\xc8\xd0\x06\x00" + + "\x00\xd0\x06\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81R\xbb\x01\x00America/New_YorkUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUOKjǪ\x02\x00\x00\xaa\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81l\xc2\x01\x00America/Bah" + + "iaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\x0f(\b=\x01\x00\x00=\x01\x00\x00\x15\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81]\xc5\x01\x00America/Santo_DomingoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x82\x13z\xe2\xc2\x00\x00\x00\xc2\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe9\xc6\x01\x00America/Tegucigal" + + "paUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x15\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\xf8\xc7\x01\x00America/St_BarthelemyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x89غ\xee\x15\x04\x00\x00\x15\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf8\xc8\x01\x00America/BelizeUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10" + + "\x00\xe8AU\xcd\x01\x00America/Indiana/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUp\xb6{\xc9\x13\x02\x00\x00\x13\x02\x00\x00\x1c\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9f\xcd\x01\x00America/Indiana/Indianapoli" + + "sUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\b\xd0\x01\x00America/Indiana/KnoxUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU \x17\x89}q\x01\x00\x00q\x01\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81N\xd4\x01\x00America/Indiana/Vev" + + "ayUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUM/U\x9f7\x02\x00\x007\x02\x00\x00\x17\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\x0e\xd6\x01\x00America/Indiana/MarengoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\r\xedsp.\x02\x00\x00.\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x96\xd8\x01\x00America/Indiana" + + "/VincennesUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x01\xd8N\x8c\xab\x02\x00\x00\xab\x02" + + "\x00\x00\x1a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x17\xdb\x01\x00America/Indiana/PetersburgUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUK-E\xfad\x02\x00\x00d\x02\x00\x00\x17\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x16\xde\x01\x00Amer" + + "ica/Indiana/WinamacUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd8" + + "\xb5K\xa6\n\x02\x00\x00\n\x02\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xcb\xe0\x01\x00America/Indiana/Tell_CityUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "(\xe3\x01\x00America/Knox_INUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc3" + + "\xb8\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81i\xe7\x01\x00America/CrestonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa2\xe8\x01\x00Americ" + + "a/AnguillaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa2\x81\xbfyS\x02\x00\x00S\x02" + + "\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9d\xe9\x01\x00America/MetlakatlaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU.\xf9\xc0\x1e\xd5\x05\x00\x00\xd5\x05\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81<\xec\x01\x00America/Monc" + + "tonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x82s\x1dT\x01\x00\x00T\x01\x00\x00\x11\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81Z\xf2\x01\x00America/ChihuahuaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vUU\r\xf7\xd3\xc7\x01\x00\x00\xc7\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf9\xf3\x01\x00America/ThuleUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xac\x8e\xee\x13\xbe\x00\x00\x00\xbe\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\a" + + "\xf6\x01\x00America/CaracasUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\xf9" + + "\x1dɻ\x00\x00\x00\xbb\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x0e\xf7\x01\x00America/ParamariboUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUŒZ\x8c\xc4\x02\x00\x00\xc4\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x15\xf8\x01\x00Amer" + + "ica/MendozaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1d`̟\x00\x03\x00\x00\x00" + + "\x03\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"\xfb\x01\x00America/Cambridge_BayUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81q\xfe\x01\x00America/" + + "Coral_HarbourUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00" + + "\x00\xb1\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81U\xff\x01\x00America/KralendijkUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81R\x00\x02\x00America/T" + + "orontoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x81{\xc1\x92\xbc\x03\x00\x00\xbc\x03\x00\x00\r\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81P\a\x02\x00America/SitkaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vU\xc0\x98\x00\b\xc9\x03\x00\x00\xc9\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81S\v\x02\x00America/MontevideoUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81h\x0f\x02\x00America/AntiguaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv" + + "\vU\xd7\b\\\xc6&\x02\x00\x00&\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81b\x10\x02\x00America/MiquelonUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUa\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd2\x12\x02\x00Am" + + "erica/ManausUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xbf\x03u\xf3\xe4\x01\x00\x00" + + "\xe4\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb6\x14\x02\x00America/RecifeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xac\x8a\x83S\xd4\x00\x00\x00\xd4\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe2\x16\x02\x00America/Guatem" + + "alaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1c\xd8\x19\x9dp\x01\x00\x00p\x01\x00\x00\x15\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x01\x18\x02\x00America/Swift_CurrentUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\xc9*;\xb1\x00\x00\x00\xb1\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc0\x19\x02\x00America/Puerto_R" + + "icoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU~\xb2\x0e\x19V\a\x00\x00V\a\x00\x00\x10\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xbe\x1a\x02\x00America/St_JohnsUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vUԾ\xe7#\x95\x00\x00\x00\x95\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81^\"\x02\x00America/CaymanUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x7f$*\xa0\xa6\x03\x00\x00\xa6\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81;" + + "#\x02\x00America/CuiabaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcd\xc3v" + + "\xe3\xb3\x00\x00\x00\xb3\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81)'\x02\x00America/GuayaquilUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A'(\x02\x00Antarc" + + "tica/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUƉ\xf71\x84\x00\x00\x00\x84\x00\x00\x00\x12\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81l(\x02\x00Antarctica/RotheraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81<)\x02\x00Antarctica/McMurd" + + "oUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb2\x84J]\xd0\x03\x00\x00\xd0\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\x9b-\x02\x00Antarctica/MacquarieUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb91\x02\x00Antarctica/SyowaUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x19\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81\x882\x02\x00Antarctica/DumontDUrvilleUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x95{\xf3\xa9w\x03\x00\x00w\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81u3\x02\x00Antarctica/PalmerU" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU:\xc8P7\xb1\x00\x00\x00\xb1\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x8177\x02\x00Antarctica/TrollUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x8128\x02\x00Antarctica/South_PoleUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd7N\xab\x8b\x98\x00\x00\x00\x98\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81\x94<\x02\x00Antarctica/MawsonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81w=\x02\x00Antarctica/VostokUT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xddzAh\xf3\x00\x00\x00\xf3\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81G>\x02\x00" + + "Antarctica/CaseyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x95\xea\x06\xd3" + + "\xc5\x00\x00\x00\xc5\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x84?\x02\x00Antarctica/DavisUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x93@\x02\x00Arctic/U" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x81\xd4@\x02\x00Arctic/LongyearbyenUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\xe2C\x02\x00Asia/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\\\x91\x87\xbb\xf7\x00\x00\x00\xf7\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81!D\x02\x00Asia/Co" + + "lomboUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU)p\x1cX\xf1\x02\x00\x00\xf1\x02\x00\x00\x10\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81^E\x02\x00Asia/NovosibirskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vUѾ\xa8\xc7u\x02\x00\x00u\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x99H\x02\x00Asia/TbilisiUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81T" + + "K\x02\x00Asia/Ujung_PandangUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "Uw\x86\x8d^\x03\x03\x00\x00\x03\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81^L\x02\x00Asia/Ust-NeraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x87\xbd\xedL\xf1\x02\x00\x00\xf1\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa8O\x02\x00Asia/B" + + "arnaulUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\v\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdfR\x02\x00Asia/MuscatUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00" + + "\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa9S\x02\x00Asia/BangkokUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x87T\x02\x00A" + + "sia/Ulan_BatorUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7f^]@\x01" + + "\x00\x00@\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"W\x02\x00Asia/BruneiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8bSnT\xa1\x00\x00\x00\xa1\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa7X\x02\x00Asia/KatmanduUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9e\x88|`\x9a\x03\x00\x00\x9a\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\x8fY\x02\x00Asia/AmmanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUe\x1b" + + "b2w\x01\x00\x00w\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81m]\x02\x00Asia/AshgabatUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00" + + "\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81+_\x02\x00Asia/Phno" + + "m_PenhUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf9l\x03\x12\xf8\x02\x00\x00\xf8\x02\x00\x00\f\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\f`\x02\x00Asia/IrkutskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Jc\x02\x00Asia/KuwaitUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x81z&\x80k\x02\x00\x00k\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x14d\x02\x00A" + + "sia/ChoibalsanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd5ΜGp\x02" + + "\x00\x00p\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc8f\x02\x00Asia/QyzylordaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x80i\x02\x00Asia/MacauUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUb\xadű\xf8\x00\x00\x00\xf8\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xdbl\x02\x00Asia/JakartaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\xb2\xb9\xf4\xb6R\x02\x00\x00R\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x19n\x02\x00Asia/UlaanbaatarUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9e\xa1=H\xd8\x04\x00\x00\xd8\x04\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb5p\x02\x00Asia" + + "/GazaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf0\x9cf>\xd7\x02\x00\x00\xd7\x02\x00\x00\x0e\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd0u\x02\x00Asia/KamchatkaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vUE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xefx\x02\x00Asia/Hong_KongUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1d?v\f\x17\x03\x00\x00\x17\x03\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81>" + + "|\x02\x00Asia/MacaoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc7\x11\xe1[\xdc\x02\x00" + + "\x00\xdc\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x99\x7f\x02\x00Asia/BeirutUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x83g\x95M\a\x03\x00\x00\a\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xba\x82\x02\x00Asia/KhandygaUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUd%\x05\xd8\xe6\x02\x00\x00\xe6\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81\b\x86\x02\x00Asia/VladivostokUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x818\x89\x02\x00Asia/ShanghaiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\b\x8b\x02\x00Asia" + + "/IstanbulUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUS\xa5\x81e\xf7\x00\x00\x00\xf7\x00\x00" + + "\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xff\x8f\x02\x00Asia/PontianakUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd7e&uv\x02\x00\x00v\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81>\x91\x02\x00Asia/BaghdadUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUV\xe0\xe7!\xe7\x02\x00\x00\xe7\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xfa\x93\x02\x00Asia/AnadyrUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8a\x9a\x90\xf7" + + "\xd6\x02\x00\x00\xd6\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81&\x97\x02\x00Asia/NovokuznetskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUw\rD\an\x01\x00\x00n\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81G\x9a\x02\x00Asia/Sa" + + "markandUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU's\x96\x1en\x01\x00\x00n\x01\x00\x00\r" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xfd\x9b\x02\x00Asia/DushanbeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vUO\xb0\x03\xe9\xe5\x02\x00\x00\xe5\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb2\x9d\x02\x00Asia/YakutskUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81ݠ" + + "\x02\x00Asia/DubaiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00" + + "\x89\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa6\xa1\x02\x00Asia/HarbinUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81t\xa3\x02\x00Asia/YangonUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xba\xa3b\xc1R\x02\x00\x00R\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "t\xa4\x02\x00Asia/HovdUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9a\x1a\xdc\xca\xdc\x00\x00" + + "\x00\xdc\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\t\xa7\x02\x00Asia/CalcuttaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8c\xdb?\xec,\x03\x00\x00,\x03\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81,\xa8\x02\x00Asia/TehranUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa1\xfax\x98g\x02\x00\x00g\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81\x9d\xab\x02\x00Asia/QostanayUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\x17✳2\x04\x00\x002\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81K\xae\x02\x00Asia/Tel_AvivUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe4_P\x18\xef\x02\x00\x00\xef\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81IJ\x02\x00Asia/Ma" + + "gadanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUT\x81\x18G^\x02\x00\x00^\x02\x00\x00\n\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf9\xb5\x02\x00Asia/AqtauUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUǯ\xdf\x1c\xee\x00\x00\x00\xee\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9b\xb8\x02\x00Asia/ManilaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81ι\x02\x00Asia" + + "/SeoulUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xab\xcd\xdf\x05\xee\x02\x00\x00\xee\x02\x00\x00\n\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb1\xbb\x02\x00Asia/ChitaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vU?Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe3\xbe\x02\x00Asia/DhakaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa7f^]@\x01\x00\x00@\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x0e\xc0\x02\x00Asia" + + "/KuchingUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8a\xc1\x1eB\xb7\x00\x00\x00\xb7\x00\x00\x00" + + "\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x94\xc1\x02\x00Asia/PyongyangUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17✳2\x04\x00\x002\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x93\xc2\x02\x00Asia/JerusalemUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU0I\xc7\xde\xec\x00\x00\x00\xec\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81\r\xc7\x02\x00Asia/SaigonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU)\x15I" + + "I\xf3\x02\x00\x00\xf3\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81>\xc8\x02\x00Asia/SakhalinUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81x\xcb\x02\x00Asia/Thimp" + + "huUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\f\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81X\xcc\x02\x00Asia/KashgarUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU]S\xbb\x12\xac\x03\x00\x00\xac\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81#\xcd\x02\x00Asia/FamagustaUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUL\xe0\x91y\xe5\x02\x00\x00\xe5\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x17\xd1\x02\x00As" + + "ia/KrasnoyarskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xdav\x19z\x98\x00" + + "\x00\x00\x98\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81F\xd4\x02\x00Asia/QatarUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x03\x87\xb3<\xe8\x02\x00\x00\xe8\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"\xd5\x02\x00Asia/BakuUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81M" + + "\xd8\x02\x00Asia/TaipeiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01" + + "\x00\x00\x00\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x91\xda\x02\x00Asia/SingaporeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x03R\xda\xedU\x02\x00\x00U\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd9\xdb\x02\x00Asia/Nicosia" + + "UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x1e\x1f\x96\xde\xea\x04\x00\x00\xea\x04\x00\x00\v\x00\x18\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\xa0\x81t\xde\x02\x00Asia/HebronUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "U\xa4Zߐ\xe6\x02\x00\x00\xe6\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa3\xe3\x02\x00Asia/SrednekolymskUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU9Y\xb7\xf1\n\x01\x00\x00\n\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd5\xe6\x02\x00A" + + "sia/KarachiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU'\xe2\\\xff\x9f\x00\x00\x00\x9f" + + "\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81%\xe8\x02\x00Asia/KabulUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU\x9a\x1a\xdc\xca\xdc\x00\x00\x00\xdc\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\b\xe9\x02\x00Asia/KolkataUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xdb\xfa\xb5\xbeg\x02\x00\x00g\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81*" + + "\xea\x02\x00Asia/AqtobeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8bSnT\xa1\x00" + + "\x00\x00\xa1\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd6\xec\x02\x00Asia/KathmanduUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU[u\x99q\xf1\x02\x00\x00\xf1\x02\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xbf\xed\x02\x00Asia/TomskUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xdav\x19z\x98\x00\x00\x00\x98\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xf4\xf0\x02\x00Asia/BahrainUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\x88\xf6C\x84\x98\x00\x00\x00\x98\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd2\xf1\x02\x00Asia/VientianeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91" + + "\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb2\xf2\x02\x00Asia/A" + + "denUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcfׇ\xe1\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81z\xf3\x02\x00Asia/RiyadhUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU&\xe9\xd1\xd8q\x02\x00\x00q\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81D\xf4\x02\x00Asia/OralUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf8\xf6\x02\x00Asia/Ku" + + "ala_LumpurUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xef\\\xf4q\x17\x04\x00\x00\x17\x04" + + "\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81C\xf8\x02\x00Asia/DamascusUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUS\xdd\\2a\x02\x00\x00a\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa1\xfc\x02\x00Asia/AlmatyUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "G\xff\x02\x00Asia/RangoonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?\xa7^\xfa" + + "h\x02\x00\x00h\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81H\x00\x03\x00Asia/AtyrauUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUB\x1d\xc6\x1b\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf5\x02\x03\x00Asia/UrumqiUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUj$\xcd\xf4\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xbf\x03\x03\x00Asia/ThimbuUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?" + + "Y\xaf\x19\xe7\x00\x00\x00\xe7\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9e\x04\x03\x00Asia/DaccaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU`\xc9\xd4\\\xbe\x00\x00\x00\xbe\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc9\x05\x03\x00Asia/Makass" + + "arUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\xce\x06\x03\x00Asia/ChongqingUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vUe\x1bb2w\x01\x00\x00w\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9f\b\x03\x00Asia/AshkhabadUT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU0I\xc7\xde\xec\x00\x00\x00\xec\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81^\n\x03\x00" + + "Asia/Ho_Chi_MinhUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x02\xf4\xaeg" + + "\xd5\x00\x00\x00\xd5\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x94\v\x03\x00Asia/TokyoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb2\xe27Yn\x01\x00\x00n\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xad\f\x03\x00Asia/TashkentU" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUΒ\x1a\x8c\xaa\x00\x00\x00\xaa\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x81b\x0e\x03\x00Asia/DiliUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x02\x95" + + "-\xad\xc4\x02\x00\x00\xc4\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81O\x0f\x03\x00Asia/YerevanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU:\x11\xea\xa2\xe5\x02\x00\x00\xe5\x02\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Y\x12\x03\x00Asia/OmskU" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00\x89\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x81\x81\x15\x03\x00Asia/ChungkingUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vU0]*\x1bj\x02\x00\x00j\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81R\x17\x03\x00Asia/BishkekUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU.>[K\xab\x00\x00\x00\xab\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x02\x1a\x03\x00Asia/" + + "JayapuraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9a\xea\x18\xd4\xf8\x02\x00\x00\xf8\x02\x00\x00" + + "\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf4\x1a\x03\x00Asia/YekaterinburgUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A8\x1e\x03\x00Atlantic/UT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU1)7\xad\xad\x05\x00\x00\xad\x05\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81{\x1e\x03\x00Atlantic/MadeiraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "U\xb7\x0e\xbdm\xb9\x01\x00\x00\xb9\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81r$\x03\x00Atlantic/FaeroeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81t&\x03\x00Atla" + + "ntic/ReykjavikUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb7\x0e\xbdm\xb9\x01" + + "\x00\x00\xb9\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81B'\x03\x00Atlantic/FaroeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUl&\x04\x99\x00\x04\x00\x00\x00\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81C)\x03\x00Atlantic/Ber" + + "mudaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe7\xcf^\xb0\x15\x03\x00\x00\x15\x03\x00\x00\x10\x00\x18\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8d-\x03\x00Atlantic/StanleyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vU\xaf|7\xb3\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xec0\x03\x00Atlantic/CanaryUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe8\x8dY\x80\xad\x05\x00\x00\xad\x05\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\x133\x03\x00Atlantic/AzoresUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\t9\x03\x00Atlantic/St_HelenaUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x0f-\xadׄ\x00\x00\x00\x84\x00\x00\x00\x16\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd79\x03\x00At" + + "lantic/South_GeorgiaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xab:\x03\x00Atlantic/Jan_MayenUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\u0097N\xad\xaf\x00\x00\x00\xaf\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb8=\x03\x00At" + + "lantic/Cape_VerdeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\xb4>\x03\x00Australia/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf8>\x03\x00Australia/ACT" + + "UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc8R\x1a\x1b\xea\x00\x00\x00\xea\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\xa0\x81\xc7B\x03\x00Australia/DarwinUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vUϻ\xca\x1a2\x01\x00\x002\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xfbC\x03\x00Australia/PerthUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81vE\x03" + + "\x00Australia/MelbourneUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "X\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81KI\x03\x00Australia/CanberraUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9b\xe1\xc1\xa9\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1fM\x03\x00Au" + + "stralia/VictoriaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo3\xdaR" + + "\xb4\x02\x00\x00\xb4\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf3P\x03\x00Australia/LHIUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xbd\xca#\x7f\xad\x03\x00\x00\xad\x03\x00\x00\x15\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xeeS\x03\x00Australia/B" + + "roken_HillUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUϻ\xca\x1a2\x01\x00\x002\x01" + + "\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xeaW\x03\x00Australia/WestUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81dY\x03\x00Australia/Tasman" + + "iaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\x9b]\x03\x00Australia/HobartUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xbd\xca#\x7f\xad\x03\x00\x00\xad\x03\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd0a\x03\x00Australia/YancowinnaUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa2ܺ\xca:\x01\x00\x00:\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xcbe\x03\x00Australia/EuclaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Ng\x03\x00Australia/NSWUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?\x95\xbd\x12E\x01\x00\x00E\x01\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1dk\x03\x00Aust" + + "ralia/LindemanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU3\xba\xde\xd3!\x01" + + "\x00\x00!\x01\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xael\x03\x00Australia/BrisbaneUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUX\xb9\x9ap\x88\x03\x00\x00\x88\x03\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1bn\x03\x00Australi" + + "a/SydneyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00" + + "\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xedq\x03\x00Australia/AdelaideUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUE\xf2\xe6Z\xeb\x03\x00\x00\xeb\x03\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd2u\x03\x00Australia/Curr" + + "ieUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU3\xba\xde\xd3!\x01\x00\x00!\x01\x00\x00\x14\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\az\x03\x00Australia/QueenslandUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo3\xdaR\xb4\x02\x00\x00\xb4\x02\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81v{\x03\x00Australia/Lord_How" + + "eUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8ff~ՙ\x03\x00\x00\x99\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81w~\x03\x00Australia/SouthUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vU\xc8R\x1a\x1b\xea\x00\x00\x00\xea\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Y\x82\x03\x00Australia/NorthUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x8c\x83\x03" + + "\x00Brazil/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb7-2f\xe4\x01\x00\x00\xe4\x01\x00\x00" + + "\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81̓\x03\x00Brazil/DeNoronhaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9d?\xdfڸ\x03\x00\x00\xb8\x03\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xfb\x85\x03\x00Brazil/EastUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUa\xcb'\xe9\x9c\x01\x00\x00\x9c\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xf8\x89\x03\x00Brazil/WestUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUg\xf5K\x89" + + "\xa2\x01\x00\x00\xa2\x01\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81ً\x03\x00Brazil/AcreUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\xc0\x8d\x03\x00Canada/UT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\u0096dK~\x02\x00\x00~\x02\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\x01\x8e\x03\x00Canada/SaskatchewanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUU9#\xbe2\x05\x00\x002\x05\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81̐\x03\x00Canada/PacificUT\x05\x00\x03\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU{\a\a\xdc\xca\x03\x00\x00\xca\x03\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81F\x96\x03\x00Can" + + "ada/MountainUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?_p\x99\x0e\x05\x00\x00" + + "\x0e\x05\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Y\x9a\x03\x00Canada/CentralUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU):\x17-\x88\x06\x00\x00\x88\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xaf\x9f\x03\x00Canada/Atlanti" + + "cUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUӿ\x92\xbc\xb5\x06\x00\x00\xb5\x06\x00\x00\x0e\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\x80\xa6\x03\x00Canada/EasternUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vU~\xb2\x0e\x19V\a\x00\x00V\a\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81}\xad\x03\x00Canada/NewfoundlandUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc1Ȇ\x90\x05\x04\x00\x00\x05\x04\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + " \xb5\x03\x00Canada/YukonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe6\x9aM\xbe" + + "m\x02\x00\x00m\x02\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81k\xb9\x03\x00CETUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x15\xbc\x03\x00Chile/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?X'\x8e\x96\x04\x00\x00\x96\x04\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81U\xbc\x03\x00Chile" + + "/EasterIslandUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU]\"_WJ\x05\x00" + + "\x00J\x05\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x817\xc1\x03\x00Chile/ContinentalUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU<\x8b\x99\x1e\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xcc\xc6\x03\x00CST6CDTUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\a\x1c\x9e\x9a]\x04\x00\x00]\x04\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81\xc4\xca\x03\x00CubaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU`l\x8d~\xf1\x01\x00\x00\xf1" + + "\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81_\xcf\x03\x00EETUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU\x12tnj\xfc\x04\x00\x00\xfc\x04\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8d\xd1\x03\x00EgyptUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa1\xd6jL\xd8\x05\x00\x00\xd8\x05\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc8\xd6\x03\x00EireUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUtX\xbe\xe4o\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xde" + + "\xdc\x03\x00ESTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe7/\xebT\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8a\xdd\x03\x00EST5EDTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x82\xe1\x03\x00Etc/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUH\x9b\xd1\x04q\x00\x00\x00q\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc0\xe1\x03\x00Etc/GMT+6UT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81t\xe2\x03\x00Etc/ZuluUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa9{\xa2qq\x00" + + "\x00\x00q\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81%\xe3\x03\x00Etc/GMT+2UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk\x19\xf1\x03\x00Etc/GMT-14UT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xf5\xf1\x03\x00Etc/GMT0UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03" + + "o\x00\x00\x00o\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa6\xf2\x03\x00Etc/GMT+0UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd9|\xbd7s\x00\x00\x00s\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81X\xf3\x03\x00Etc/GMT-10UT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\x0f\xf4\x03\x00Etc/UniversalUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd4X" + + "\x9b\xf3q\x00\x00\x00q\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc5\xf4\x03\x00Etc/GMT+5UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81y\xf5\x03\x00Etc/UTCUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU)\xb9\xbe\x9dr\x00\x00\x00r\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + ")\xf6\x03\x00Etc/GMT+11UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8e\x1569r\x00" + + "\x00\x00r\x00\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdf\xf6\x03\x00Etc/GMT+10UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc5\x18\xb6\xfbr\x00\x00\x00r\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x95\xf7\x03\x00Etc/GMT-8UT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9c\xfcm\x99r\x00\x00\x00r\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81J" + + "\xf8\x03\x00Etc/GMT-3UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUe\xcb\xe9Qq\x00\x00\x00" + + "q\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xff\xf8\x03\x00Etc/GMT+3UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU!\xd6~wr\x00\x00\x00r\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb3\xf9\x03\x00Etc/GMT-5UT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8Ah\xfa\x03\x00" + + "Europe/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1\xc1\xeb\x05\x8c\x03\x00\x00\x8c\x03\x00\x00\r" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa9\xfa\x03\x00Europe/MoscowUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vU\x1b8\xfel\xd6\x02\x00\x00\xd6\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81|\xfe\x03\x00Europe/SaratovUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\x9a\x01\x04\x00Europe/GuernseyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xea" + + "\xc48\xde\\\x02\x00\x00\\\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"\b\x04\x00Europe/TiraneUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUIo\x11{\xd3\x02\x00\x00\xd3\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc5\n\x04\x00Europe/B" + + "ratislavaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00" + + "\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe3\r\x04\x00Europe/LjubljanaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUDd#\xc4\xf1\x01\x00\x00\xf1\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\v\x10\x04\x00Europe/Busingen" + + "UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe5\xc8X\xa7\xe1\x01\x00\x00\xe1\x01\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\xa0\x81E\x12\x04\x00Europe/MariehamnUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00" + + "\x00\x00\bv\vU]i\x11u\xd6\x02\x00\x00\xd6\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81p\x14\x04\x00Europe/AstrakhanUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x90\x17" + + "\x04\x00Europe/CopenhagenUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk" + + "\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x9c\x1a\x04\x00Europe/LondonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xda\xc0\x86\xd2\x1b\x02\x00\x00\x1b\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"!\x04\x00Europe/U" + + "zhgorodUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUu\xb0\xcd\xfc\xf8\x02\x00\x00\xf8\x02\x00\x00\x10" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x86#\x04\x00Europe/UlyanovskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01" + + "\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc8&\x04\x00Europe/JerseyUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81N-\x04\x00Europe/Isle_of_ManUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd93\x04\x00Europe/IstanbulUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUgp\xc0\xa7\xb6\x02\x00\x00\xb6\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd28\x04\x00E" + + "urope/RigaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUiS\x18D.\x02\x00\x00.\x02" + + "\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xcd;\x04\x00Europe/KievUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vUZk#V\x81\x03\x00\x00\x81\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81@>\x04\x00Europe/MadridUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\bB\x04\x00Europe/StockholmUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + "\xe5\xc8X\xa7\xe1\x01\x00\x00\xe1\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x13E\x04\x00Europe/HelsinkiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81=G\x04\x00Europ" + + "e/BerlinUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8c\xc8\x15\xd0P\x02\x00\x00P\x02\x00\x00" + + "\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81EJ\x04\x00Europe/SofiaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03" + + "\n\x00\x00\x00\x00\x00\bv\vUO+j\x94\x88\x03\x00\x00\x88\x03\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdbL\x04\x00Europe/KaliningradUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUh\xa5J[\xa0\x03\x00\x00\xa0\x03\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xafP\x04\x00Europe/MaltaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU" + + ">\xfe垛\x03\x00\x00\x9b\x03\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x95T\x04\x00Europe/WarsawUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUq\x16\x9b?\xa3\x02\x00\x00\xa3\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81wX\x04\x00Europe/" + + "TallinnUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe6Kf\xab\xfe\x02\x00\x00\xfe\x02\x00\x00\x0f" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81b[\x04\x00Europe/BudapestUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe0\xfe\x83\xe5\xcd\x02\x00\x00\xcd\x02\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa9^\x04\x00Europe/KirovUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc7\xf5\x94\xdaQ\x04\x00\x00Q\x04\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\xbca\x04\x00Europe/MonacoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUiS\x18" + + "D.\x02\x00\x00.\x02\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Tf\x04\x00Europe/KyivUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc7h\x04\x00Europe/Skopj" + + "eUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x95\xb4\x9e\xe7\xb3\x03\x00\x00\xb3\x03\x00\x00\v\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\xecj\x04\x00Europe/RomeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv" + + "\vU==\xa4\x16\xc4\x04\x00\x00\xc4\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe4n\x04\x00Europe/GibraltarUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf2s\x04\x00Eu" + + "rope/ZagrebUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUDd#\xc4\xf1\x01\x00\x00\xf1" + + "\x01\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x17v\x04\x00Europe/ZurichUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUZ\x05wג\x02\x00\x00\x92\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Ox\x04\x00Europe/ViennaUT\x05" + + "\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUWI\xc3\x7f(\x03\x00\x00(\x03\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xa0\x81({\x04\x00Europe/MinskUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x90" + + "\xa9\xf5ϕ\x02\x00\x00\x95\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x96~\x04\x00Europe/BucharestUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU}\xb4N\xb8a\x03\x00\x00a\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81u\x81\x04\x00Europ" + + "e/SimferopolUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00" + + "\xde\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81!\x85\x04\x00Europe/BelgradeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01" + + "\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x03R\xda\xedU\x02\x00\x00U\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81H\x87\x04\x00Europe/Nicosi" + + "aUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU&S\x03\t\xae\x05\x00\x00\xae\x05\x00\x00\r\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\xe5\x89\x04\x00Europe/LisbonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81ڏ\x04\x00Europe/BelfastUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUߜvυ\x01\x00\x00\x85\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81a\x96\x04\x00Eu" + + "rope/AndorraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x95\x7fpp\xdc\x02\x00\x00" + + "\xdc\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81.\x98\x04\x00Europe/SamaraUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x95\xb4\x9e\xe7\xb3\x03\x00\x00\xb3\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Q\x9b\x04\x00Europe/San_Mari" + + "noUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUIo\x11{\xd3\x02\x00\x00\xd3\x02\x00\x00\r\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81O\x9f\x04\x00Europe/PragueUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vU\x95\xb4\x9e\xe7\xb3\x03\x00\x00\xb3\x03\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81i\xa2\x04\x00Europe/VaticanUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUI\xb8\xbc\xd3\xf3\x02\x00\x00\xf3\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81d\xa6\x04\x00E" + + "urope/ChisinauUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUVa\x92\xd3\xdf\x02" + + "\x00\x00\xdf\x02\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa0\xa9\x04\x00Europe/VolgogradUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa1\xd6jL\xd8\x05\x00\x00\xd8\x05\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81ɬ\x04\x00Europe/Dub" + + "linUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc7\xf5\x94\xdaQ\x04\x00\x00Q\x04\x00\x00\f\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe8\xb2\x04\x00Europe/ParisUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x7f\xb7\x04\x00Europe/PodgoricaUT\x05\x00\x03\xaf\x16\xf5b" + + "ux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo\xbc\x831O\x04\x00\x00O\x04\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa7\xb9\x04" + + "\x00Europe/LuxembourgUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo\xbc" + + "\x831O\x04\x00\x00O\x04\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81A\xbe\x04\x00Europe/BrusselsUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUI\xb8\xbc\xd3\xf3\x02\x00\x00\xf3\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd9\xc2\x04\x00Europe/" + + "TiraspolUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17S\x91\xb3\xc1\x02\x00\x00\xc1\x02\x00\x00" + + "\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x15\xc6\x04\x00Europe/OsloUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vU\xccb\xf72\xa4\x02\x00\x00\xa4\x02\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1b\xc9\x04\x00Europe/VilniusUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xcb*j\x8f\xaa\x02\x00\x00\xaa\x02\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\a" + + "\xcc\x04\x00Europe/AthensUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUDd#\xc4" + + "\xf1\x01\x00\x00\xf1\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf8\xce\x04\x00Europe/VaduzUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xfc\xbe\xb5\xac9\x02\x00\x009\x02\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81/\xd1\x04\x00Europe/Zapor" + + "ozhyeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUo\xbc\x831O\x04\x00\x00O\x04\x00\x00\x10\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb3\xd3\x04\x00Europe/AmsterdamUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e" + + "\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1C\xf9\xa1\xde\x01\x00\x00\xde\x01\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81L\xd8\x04\x00Europe/SarajevoUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xab\x80c$q\x00\x00\x00q\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81s\xda\x04\x00FactoryUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUk\xa4,\xb6?\x06\x00" + + "\x00?\x06\x00\x00\x02\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81%\xdb\x04\x00GBUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUk\xa4,\xb6?\x06\x00\x00?\x06\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa0\xe1\x04\x00GB-EireUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81 \xe8\x04\x00GMTUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xcc\xe8\x04\x00GMT+0UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00" + + "\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81z\xe9\x04\x00GMT-0UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81(\xea\x04\x00GMT0UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUP\xda\xfa\x03o\x00\x00\x00o\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd5\xea\x04\x00GreenwichUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUE\t\xfa-\a\x03\x00\x00\a\x03\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\x87\xeb\x04\x00HongkongUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU=\xf7\xfaw" + + "p\x00\x00\x00p\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd0\xee\x04\x00HSTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vU\xf1\b{\x87\x82\x00\x00\x00\x82\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81}\xef\x04\x00IcelandUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A@\xf0\x04\x00Indi" + + "an/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x96\xed=\x98\xb3\x00\x00\x00\xb3\x00\x00\x00\x10\x00\x18\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x81\xf0\x04\x00Indian/MauritiusUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n" + + "\x00\x00\x00\x00\x00\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81~\xf1\x04\x00Indian/ReunionUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb9\xb2Z\xac\x98\x00\x00\x00\x98\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81K" + + "\xf2\x04\x00Indian/KerguelenUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4" + + "\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x13\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81-\xf3\x04\x00Indian/AntananarivoUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x819\xf4\x04\x00In" + + "dian/MayotteUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x88\xf6C\x84\x98\x00\x00\x00" + + "\x98\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81@\xf5\x04\x00Indian/ChristmasUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb4\x8d\x98ƿ\x00\x00\x00\xbf\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\"\xf6\x04\x00Indian/Comor" + + "oUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUx\xb0W\x14\x98\x00\x00\x00\x98\x00\x00\x00\r\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81(\xf7\x04\x00Indian/ChagosUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00" + + "\bv\vU\xed\x8c\xf1\x91\x85\x00\x00\x00\x85\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\a\xf8\x04\x00Indian/MaheUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUʇ{_\xbb\x00\x00\x00\xbb\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd1\xf8\x04\x00India" + + "n/CocosUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xb9\xb2Z\xac\x98\x00\x00\x00\x98\x00\x00\x00\x0f" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd2\xf9\x04\x00Indian/MaldivesUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02" + + "\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8c\xdb?\xec,\x03\x00\x00,\x03\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb3\xfa\x04\x00IranUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x17✳2\x04\x00\x002\x04\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1d\xfe\x04\x00Isra" + + "elUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU%J\xd5\xebS\x01\x00\x00S\x01\x00\x00\a\x00\x18\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\xa0\x81\x8f\x02\x05\x00JamaicaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x02" + + "\xf4\xaeg\xd5\x00\x00\x00\xd5\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81#\x04\x05\x00JapanUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x817\x05\x05\x00KwajaleinUT\x05\x00\x03\xaf\x16" + + "\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU_\x7f2[\xaf\x01\x00\x00\xaf\x01\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81U" + + "\x06\x05\x00LibyaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xfe\x9d\x1b\xc9m\x02\x00\x00m\x02\x00\x00" + + "\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81C\b\x05\x00METUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\xed\n\x05\x00Mexico/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xd0v\x01\x8a\x01\x04\x00\x00\x01\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81.\v\x05\x00Mexico/BajaN" + + "orteUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU8\xcdZ\x05o\x01\x00\x00o\x01\x00\x00\x0e\x00\x18\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81y\x0f\x05\x00Mexico/BajaSurUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xd6\xe1Հ\x9c\x01\x00\x00\x9c\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x810\x11\x05\x00Mexico/GeneralUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf5\x8d\x99\x92o\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x14\x13" + + "\x05\x00MSTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe6h\xcac\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc0\x13\x05\x00MST7MDTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv" + + "\vUV\x80\x94@\x12\x04\x00\x00\x12\x04\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb8\x17\x05\x00NavajoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_" + + "\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x02\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\n\x1c\x05\x00NZUT\x05\x00\x03\xaf\x16\xf5bu" + + "x\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x96\xc5FF(\x03\x00\x00(\x03\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81Y \x05\x00" + + "NZ-CHATUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\xc2#\x05\x00Pacific/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUFI\xfe\x14^\x01\x00\x00^\x01\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x04$\x05\x00Pacific/SaipanUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xaa%\x05\x00P" + + "acific/HonoluluUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUt\xca{e\x92" + + "\x00\x00\x00\x92\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd1&\x05\x00Pacific/MidwayUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc8=ku\xae\x00\x00\x00\xae\x00\x00\x00\x12\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xab'\x05\x00Pacific/Kir" + + "itimatiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x97F\x91\xb3\xed\x00\x00\x00\xed\x00\x00\x00\x11" + + "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa5(\x05\x00Pacific/TongatapuUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xdd)\x05\x00Pacific/WallisUT" + + "\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc23\xa0\xbc\x84\x00\x00\x00\x84\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\xa0\x81\xab*\x05\x00Pacific/GambierUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81x+\x05\x00Pacific/TarawaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00" + + "\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81F,\x05\x00Pac" + + "ific/ChuukUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xec =\x89\xac\x00\x00\x00\xac\x00" + + "\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81'-\x05\x00Pacific/KantonUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK" + + "\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xec =\x89\xac\x00\x00\x00\xac\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x1b.\x05\x00Pacific/Enderbur" + + "yUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x81\xe3w\n\xaf\x00\x00\x00\xaf\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\x12/\x05\x00Pacific/GalapagosUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\x1c\xe3\xa3S\x96\x01\x00\x00\x96\x01\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\f0\x05\x00Pacific/RarotongaUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUb\xb2\xaf\xf7\x13\x04\x00\x00\x13\x04\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xed1\x05\x00Pacific/AucklandUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "UY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81J6\x05\x00Pacific/PohnpeiUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01" + + "\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9a\xf2:F\xc9\x00\x00\x00\xc9\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x197\x05\x00Paci" + + "fic/BougainvilleUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xfa\x0fA\x05" + + "\x99\x00\x00\x00\x99\x00\x00\x00\x10\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x8108\x05\x00Pacific/PitcairnUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xf6\xe8]*\xdb\x00\x00\x00\xdb\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x139\x05\x00Pacific/" + + "KwajaleinUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUD6\x83\xa1\x8b\x00\x00\x00\x8b\x00\x00" + + "\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x819:\x05\x00Pacific/MarquesasUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\x14\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x0f;\x05\x00Pacific/Port_M" + + "oresbyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xa8A\x15\xfe\x97\x01\x00\x00\x97\x01\x00\x00\f\x00" + + "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf7;\x05\x00Pacific/ApiaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xea\xc1\xdaυ\x00\x00\x00\x85\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd4=\x05\x00Pacific/TahitiUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x80\xf8vܔ\x00\x00\x00\x94\x00\x00\x00\r\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xa1>" + + "\x05\x00Pacific/PalauUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x97n7\x1a\xf2" + + "\x00\x00\x00\xf2\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81|?\x05\x00Pacific/KosraeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb6@\x05\x00Pacific/Wak" + + "eUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUt\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\x11\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\x82A\x05\x00Pacific/Pago_PagoUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81_B\x05\x00Pacific/YapUT\x05\x00\x03\xaf\x16\xf5bux" + + "\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUY\xd2K|\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81>C\x05\x00P" + + "acific/PonapeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUFI\xfe\x14^\x01\x00" + + "\x00^\x01\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\fD\x05\x00Pacific/GuamUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00P" + + "K\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU?X'\x8e\x96\x04\x00\x00\x96\x04\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb0E\x05\x00Pacific/EasterU" + + "T\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU6\xb7S{\x86\x00\x00\x00\x86\x00\x00\x00\x0e\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\xa0\x81\x8eJ\x05\x00Pacific/MajuroUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\b" + + "v\vUn\x04\x19y\x9a\x00\x00\x00\x9a\x00\x00\x00\f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\\K\x05\x00Pacific/TrukUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04" + + "\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x8a|\xdcU\x99\x00\x00\x00\x99\x00\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xfe垛\x03\x00\x00\x9b\x03\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xed[\x05\x00PolandUT\x05\x00\x03\xaf" + + "\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU&S\x03\t\xae\x05\x00\x00\xae\x05\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81" + + "\xc8_\x05\x00PortugalUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU*\xe4@\xa9\x89\x01\x00\x00" + + "\x89\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xb8e\x05\x00PRCUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00" + + "\x00\bv\vUŭV\xad\xb7\x03\x00\x00\xb7\x03\x00\x00\a\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81~g\x05\x00PST8PDTUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b" + + "\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xee\xf0BB\xff\x01\x00\x00\xff\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81vk\x05\x00ROCUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xc7X,Y\x9f\x01\x00\x00\x9f\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xb2m\x05\x00ROKUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x06\xaa>\xa8\x00\x01\x00\x00\x00\x01\x00\x00" + + "\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x8eo\x05\x00SingaporeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00" + + "\x00\x00\x00\bv\vU\aW\x10Ѱ\x04\x00\x00\xb0\x04\x00\x00\x06\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xd1p\x05\x00TurkeyUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1" + + "\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xc1u\x05\x00UCTUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81mv\x05\x00UniversalUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\xe8A\x1fw\x05\x00US/UT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00" + + "\x00\x00\x00\x00\bv\vU\xf6\"\x12\xfe\x0e\x05\x00\x00\x0e\x05\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\\w\x05\x00US/PacificUT\x05\x00\x03\xaf\x16\xf5bux\v" + + "\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU$ \x873\xf8\x03\x00\x00\xf8\x03\x00\x00\x11\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xae|\x05\x00US" + + "/Indiana-StarkeUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUV\x80\x94@\x12" + + "\x04\x00\x00\x12\x04\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xf1\x80\x05\x00US/MountainUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9bܩ=\xda\x06\x00\x00\xda\x06\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81H\x85\x05\x00US/CentralUT\x05\x00" + + "\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU5\x11Q\x06\xd1\x03\x00\x00\xd1\x03\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\xa0\x81f\x8c\x05\x00US/AlaskaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xae,\xa44\xc9" + + "\x03\x00\x00\xc9\x03\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81z\x90\x05\x00US/AleutianUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00" + + "PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xeaK\x85v\xdd\x00\x00\x00\xdd\x00\x00\x00\t\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x88\x94\x05\x00US/HawaiiUT\x05\x00\x03" + + "\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU3\x9aG\xc8\xd0\x06\x00\x00\xd0\x06\x00\x00\n\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0" + + "\x81\xa8\x95\x05\x00US/EasternUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUp\xb6{\xc9\x13" + + "\x02\x00\x00\x13\x02\x00\x00\x0f\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xbc\x9c\x05\x00US/East-IndianaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04" + + "S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU>\x14\xe7\x03\x83\x03\x00\x00\x83\x03\x00\x00\v\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x18\x9f\x05\x00US/Michiga" + + "nUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vUø\xab\x9b\xf0\x00\x00\x00\xf0\x00\x00\x00\n\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\xa0\x81\xe0\xa2\x05\x00US/ArizonaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\v" + + "Ut\xca{e\x92\x00\x00\x00\x92\x00\x00\x00\b\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x14\xa4\x05\x00US/SamoaUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S" + + "_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f.\xe4xo\x00\x00\x00o\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xe8\xa4\x05\x00UTCUT\x05\x00\x03\xaf\x16\xf5" + + "bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU2\x91B\xc0\xee\x01\x00\x00\xee\x01\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x94\xa5" + + "\x05\x00WETUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\xe1\xc1\xeb\x05\x8c\x03\x00\x00\x8c\x03\x00\x00\x04\x00\x18" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\xbf\xa7\x05\x00W-SUUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x01\x02\x1e\x03\n\x00\x00\x00\x00\x00\bv\vU\x9f" + + ".\xe4xo\x00\x00\x00o\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x81\x89\xab\x05\x00ZuluUT\x05\x00\x03\xaf\x16\xf5bux\v\x00\x01\x04\x91\xf1\b\x00\x04S_\x01\x00PK\x05" + + "\x06\x00\x00\x00\x00h\x02h\x02;\xca\x00\x006\xac\x05\x00\x00\x00" diff --git a/src/time/tzdata_test.go b/src/time/tzdata_test.go index eb6d6c98a861bc..33c6589d0d7f4b 100644 --- a/src/time/tzdata_test.go +++ b/src/time/tzdata_test.go @@ -17,8 +17,8 @@ var zones = []string{ } func TestEmbeddedTZData(t *testing.T) { - time.ForceZipFileForTesting(true) - defer time.ForceZipFileForTesting(false) + undo := time.DisablePlatformSources() + defer undo() for _, zone := range zones { ref, err := time.LoadLocation(zone) diff --git a/src/time/zoneinfo.go b/src/time/zoneinfo.go index 57aed03fec36cc..dd3b4edd01d0b8 100644 --- a/src/time/zoneinfo.go +++ b/src/time/zoneinfo.go @@ -100,9 +100,30 @@ func (l *Location) String() string { return l.get().name } +var unnamedFixedZones []*Location +var unnamedFixedZonesOnce sync.Once + // FixedZone returns a Location that always uses // the given zone name and offset (seconds east of UTC). func FixedZone(name string, offset int) *Location { + // Most calls to FixedZone have an unnamed zone with an offset by the hour. + // Optimize for that case by returning the same *Location for a given hour. + const hoursBeforeUTC = 12 + const hoursAfterUTC = 14 + hour := offset / 60 / 60 + if name == "" && -hoursBeforeUTC <= hour && hour <= +hoursAfterUTC && hour*60*60 == offset { + unnamedFixedZonesOnce.Do(func() { + unnamedFixedZones = make([]*Location, hoursBeforeUTC+1+hoursAfterUTC) + for hr := -hoursBeforeUTC; hr <= +hoursAfterUTC; hr++ { + unnamedFixedZones[hr+hoursBeforeUTC] = fixedZone("", hr*60*60) + } + }) + return unnamedFixedZones[hour+hoursBeforeUTC] + } + return fixedZone(name, offset) +} + +func fixedZone(name string, offset int) *Location { l := &Location{ name: name, zone: []zone{{name, offset, false}}, @@ -197,14 +218,14 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64, // The reference implementation in localtime.c from // https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz // implements the following algorithm for these cases: -// 1) If the first zone is unused by the transitions, use it. -// 2) Otherwise, if there are transition times, and the first -// transition is to a zone in daylight time, find the first -// non-daylight-time zone before and closest to the first transition -// zone. -// 3) Otherwise, use the first zone that is not daylight time, if -// there is one. -// 4) Otherwise, use the first zone. +// 1. If the first zone is unused by the transitions, use it. +// 2. Otherwise, if there are transition times, and the first +// transition is to a zone in daylight time, find the first +// non-daylight-time zone before and closest to the first transition +// zone. +// 3. Otherwise, use the first zone that is not daylight time, if +// there is one. +// 4. Otherwise, use the first zone. func (l *Location) lookupFirstZone() int { // Case 1. if !l.firstZoneUsed() { @@ -631,12 +652,13 @@ var zoneinfoOnce sync.Once // Otherwise, the name is taken to be a location name corresponding to a file // in the IANA Time Zone database, such as "America/New_York". // -// The time zone database needed by LoadLocation may not be -// present on all systems, especially non-Unix systems. -// LoadLocation looks in the directory or uncompressed zip file -// named by the ZONEINFO environment variable, if any, then looks in -// known installation locations on Unix systems, -// and finally looks in $GOROOT/lib/time/zoneinfo.zip. +// LoadLocation looks for the IANA Time Zone database in the following +// locations in order: +// +// - the directory or uncompressed zip file named by the ZONEINFO environment variable +// - on a Unix system, the system standard installation location +// - $GOROOT/lib/time/zoneinfo.zip +// - the time/tzdata package, if it was imported func LoadLocation(name string) (*Location, error) { if name == "" || name == "UTC" { return UTC, nil @@ -664,7 +686,7 @@ func LoadLocation(name string) (*Location, error) { firstErr = err } } - if z, err := loadLocation(name, zoneSources); err == nil { + if z, err := loadLocation(name, platformZoneSources); err == nil { return z, nil } else if firstErr == nil { firstErr = err diff --git a/src/time/zoneinfo_android.go b/src/time/zoneinfo_android.go index 237ff202f913ff..e4f688dcec5939 100644 --- a/src/time/zoneinfo_android.go +++ b/src/time/zoneinfo_android.go @@ -10,14 +10,12 @@ package time import ( "errors" - "runtime" "syscall" ) -var zoneSources = []string{ +var platformZoneSources = []string{ "/system/usr/share/zoneinfo/tzdata", "/data/misc/zoneinfo/current/tzdata", - runtime.GOROOT() + "/lib/time/zoneinfo.zip", } func initLocal() { @@ -29,6 +27,15 @@ func init() { loadTzinfoFromTzdata = androidLoadTzinfoFromTzdata } +var allowGorootSource = true + +func gorootZoneSource(goroot string) (string, bool) { + if goroot == "" || !allowGorootSource { + return "", false + } + return goroot + "/lib/time/zoneinfo.zip", true +} + func androidLoadTzinfoFromTzdata(file, name string) ([]byte, error) { const ( headersize = 12 + 3*4 diff --git a/src/time/zoneinfo_android_test.go b/src/time/zoneinfo_android_test.go index ba065d10a65481..f8bd7f7674a0ee 100644 --- a/src/time/zoneinfo_android_test.go +++ b/src/time/zoneinfo_android_test.go @@ -10,8 +10,8 @@ import ( ) func TestAndroidTzdata(t *testing.T) { - ForceAndroidTzdataForTest(true) - defer ForceAndroidTzdataForTest(false) + undo := ForceAndroidTzdataForTest() + defer undo() if _, err := LoadLocation("America/Los_Angeles"); err != nil { t.Error(err) } diff --git a/src/time/zoneinfo_goroot.go b/src/time/zoneinfo_goroot.go new file mode 100644 index 00000000000000..92bdcf4afe7161 --- /dev/null +++ b/src/time/zoneinfo_goroot.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !ios && !android + +package time + +func gorootZoneSource(goroot string) (string, bool) { + if goroot == "" { + return "", false + } + return goroot + "/lib/time/zoneinfo.zip", true +} diff --git a/src/time/zoneinfo_ios.go b/src/time/zoneinfo_ios.go index 044691e1305852..d6ad073e85dfd9 100644 --- a/src/time/zoneinfo_ios.go +++ b/src/time/zoneinfo_ios.go @@ -3,25 +3,24 @@ // license that can be found in the LICENSE file. //go:build ios -// +build ios package time import ( - "runtime" "syscall" ) -var zoneSources = []string{ - getZoneRoot() + "/zoneinfo.zip", -} +var platformZoneSources []string // none on iOS -func getZoneRoot() string { +func gorootZoneSource(goroot string) (string, bool) { // The working directory at initialization is the root of the // app bundle: "/private/.../bundlename.app". That's where we // keep zoneinfo.zip for tethered iOS builds. // For self-hosted iOS builds, the zoneinfo.zip is in GOROOT. - roots := []string{runtime.GOROOT() + "/lib/time"} + var roots []string + if goroot != "" { + roots = append(roots, goroot+"/lib/time") + } wd, err := syscall.Getwd() if err == nil { roots = append(roots, wd) @@ -34,10 +33,10 @@ func getZoneRoot() string { } defer syscall.Close(fd) if err := syscall.Fstat(fd, &st); err == nil { - return r + return r + "/zoneinfo.zip", true } } - return "/XXXNOEXIST" + return "", false } func initLocal() { diff --git a/src/time/zoneinfo_js.go b/src/time/zoneinfo_js.go index 8245614d2e15c1..8da34a21fbae0a 100644 --- a/src/time/zoneinfo_js.go +++ b/src/time/zoneinfo_js.go @@ -3,20 +3,18 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package time import ( - "runtime" + "internal/itoa" "syscall/js" ) -var zoneSources = []string{ +var platformZoneSources = []string{ "/usr/share/zoneinfo/", "/usr/share/lib/zoneinfo/", "/usr/lib/locale/TZ/", - runtime.GOROOT() + "/lib/time/zoneinfo.zip", } func initLocal() { @@ -38,31 +36,10 @@ func initLocal() { } else { z.name += "+" } - z.name += itoa(offset / 60) + z.name += itoa.Itoa(offset / 60) min := offset % 60 if min != 0 { - z.name += ":" + itoa(min) + z.name += ":" + itoa.Itoa(min) } localLoc.zone = []zone{z} } - -// itoa is like strconv.Itoa but only works for values of i in range [0,99]. -// It panics if i is out of range. -func itoa(i int) string { - if i < 10 { - return digits[i : i+1] - } - return smallsString[i*2 : i*2+2] -} - -const smallsString = "00010203040506070809" + - "10111213141516171819" + - "20212223242526272829" + - "30313233343536373839" + - "40414243444546474849" + - "50515253545556575859" + - "60616263646566676869" + - "70717273747576777879" + - "80818283848586878889" + - "90919293949596979899" -const digits = "0123456789" diff --git a/src/time/zoneinfo_plan9.go b/src/time/zoneinfo_plan9.go index 4ae718c59e1fc0..5d432fe297dd9e 100644 --- a/src/time/zoneinfo_plan9.go +++ b/src/time/zoneinfo_plan9.go @@ -7,13 +7,10 @@ package time import ( - "runtime" "syscall" ) -var zoneSources = []string{ - runtime.GOROOT() + "/lib/time/zoneinfo.zip", -} +var platformZoneSources []string // none on Plan 9 func isSpace(r rune) bool { return r == ' ' || r == '\t' || r == '\n' diff --git a/src/time/zoneinfo_read.go b/src/time/zoneinfo_read.go index b9830265e12b70..90814ad36a1ad4 100644 --- a/src/time/zoneinfo_read.go +++ b/src/time/zoneinfo_read.go @@ -528,7 +528,7 @@ func loadTzinfo(name string, source string) ([]byte, error) { // and parsed is returned as a Location. func loadLocation(name string, sources []string) (z *Location, firstErr error) { for _, source := range sources { - var zoneData, err = loadTzinfo(name, source) + zoneData, err := loadTzinfo(name, source) if err == nil { if z, err = LoadLocationFromTZData(name, zoneData); err == nil { return z, nil @@ -539,9 +539,20 @@ func loadLocation(name string, sources []string) (z *Location, firstErr error) { } } if loadFromEmbeddedTZData != nil { - zonedata, err := loadFromEmbeddedTZData(name) + zoneData, err := loadFromEmbeddedTZData(name) if err == nil { - if z, err = LoadLocationFromTZData(name, []byte(zonedata)); err == nil { + if z, err = LoadLocationFromTZData(name, []byte(zoneData)); err == nil { + return z, nil + } + } + if firstErr == nil && err != syscall.ENOENT { + firstErr = err + } + } + if source, ok := gorootZoneSource(runtime.GOROOT()); ok { + zoneData, err := loadTzinfo(name, source) + if err == nil { + if z, err = LoadLocationFromTZData(name, zoneData); err == nil { return z, nil } } diff --git a/src/time/zoneinfo_test.go b/src/time/zoneinfo_test.go index f032aa7924ddba..243ff8ebdee775 100644 --- a/src/time/zoneinfo_test.go +++ b/src/time/zoneinfo_test.go @@ -7,6 +7,7 @@ package time_test import ( "errors" "fmt" + "internal/testenv" "os" "reflect" "testing" @@ -66,8 +67,8 @@ func TestLoadLocationValidatesNames(t *testing.T) { } func TestVersion3(t *testing.T) { - time.ForceZipFileForTesting(true) - defer time.ForceZipFileForTesting(false) + undo := time.DisablePlatformSources() + defer undo() _, err := time.LoadLocation("Asia/Jerusalem") if err != nil { t.Fatal(err) @@ -78,8 +79,8 @@ func TestVersion3(t *testing.T) { // transition time. To do this we explicitly check early dates in a // couple of specific timezones. func TestFirstZone(t *testing.T) { - time.ForceZipFileForTesting(true) - defer time.ForceZipFileForTesting(false) + undo := time.DisablePlatformSources() + defer undo() const format = "Mon, 02 Jan 2006 15:04:05 -0700 (MST)" var tests = []struct { @@ -128,8 +129,8 @@ func TestLocationNames(t *testing.T) { } func TestLoadLocationFromTZData(t *testing.T) { - time.ForceZipFileForTesting(true) - defer time.ForceZipFileForTesting(false) + undo := time.DisablePlatformSources() + defer undo() const locationName = "Asia/Jerusalem" reference, err := time.LoadLocation(locationName) @@ -137,7 +138,11 @@ func TestLoadLocationFromTZData(t *testing.T) { t.Fatal(err) } - tzinfo, err := time.LoadTzinfo(locationName, time.OrigZoneSources[len(time.OrigZoneSources)-1]) + gorootSource, ok := time.GorootZoneSource(testenv.GOROOT(t)) + if !ok { + t.Fatal("Failed to locate tzinfo source in GOROOT.") + } + tzinfo, err := time.LoadTzinfo(locationName, gorootSource) if err != nil { t.Fatal(err) } @@ -153,8 +158,8 @@ func TestLoadLocationFromTZData(t *testing.T) { // Issue 30099. func TestEarlyLocation(t *testing.T) { - time.ForceZipFileForTesting(true) - defer time.ForceZipFileForTesting(false) + undo := time.DisablePlatformSources() + defer undo() const locName = "America/New_York" loc, err := time.LoadLocation(locName) diff --git a/src/time/zoneinfo_unix.go b/src/time/zoneinfo_unix.go index 4ea029dbde9798..67b8beb47b3ba7 100644 --- a/src/time/zoneinfo_unix.go +++ b/src/time/zoneinfo_unix.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || (darwin && !ios) || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || solaris -// +build aix darwin,!ios dragonfly freebsd linux,!android netbsd openbsd solaris +//go:build unix && !ios && !android // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. @@ -13,17 +12,15 @@ package time import ( - "runtime" "syscall" ) // Many systems use /usr/share/zoneinfo, Solaris 2 has // /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ. -var zoneSources = []string{ +var platformZoneSources = []string{ "/usr/share/zoneinfo/", "/usr/share/lib/zoneinfo/", "/usr/lib/locale/TZ/", - runtime.GOROOT() + "/lib/time/zoneinfo.zip", } func initLocal() { @@ -58,7 +55,7 @@ func initLocal() { return } } else if tz != "" && tz != "UTC" { - if z, err := loadLocation(tz, zoneSources); err == nil { + if z, err := loadLocation(tz, platformZoneSources); err == nil { localLoc = *z return } diff --git a/src/time/zoneinfo_unix_test.go b/src/time/zoneinfo_unix_test.go index b75b374c3d5351..92680c4f8f7b80 100644 --- a/src/time/zoneinfo_unix_test.go +++ b/src/time/zoneinfo_unix_test.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || (darwin && !ios) || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || solaris -// +build aix darwin,!ios dragonfly freebsd linux,!android netbsd openbsd solaris +//go:build unix && !ios && !android package time_test diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go index ba66f90ffeaf20..76d79759f7de9c 100644 --- a/src/time/zoneinfo_windows.go +++ b/src/time/zoneinfo_windows.go @@ -7,13 +7,10 @@ package time import ( "errors" "internal/syscall/windows/registry" - "runtime" "syscall" ) -var zoneSources = []string{ - runtime.GOROOT() + "/lib/time/zoneinfo.zip", -} +var platformZoneSources []string // none: Windows uses system calls instead // TODO(rsc): Fall back to copy of zoneinfo files. diff --git a/src/unicode/example_test.go b/src/unicode/example_test.go index 50c5b18a48e63d..d3a47aca91b748 100644 --- a/src/unicode/example_test.go +++ b/src/unicode/example_test.go @@ -194,3 +194,63 @@ func ExampleSpecialCase() { // U+0130 'İ' // U+0130 'İ' } + +func ExampleIsDigit() { + fmt.Printf("%t\n", unicode.IsDigit('৩')) + fmt.Printf("%t\n", unicode.IsDigit('A')) + // Output: + // true + // false +} + +func ExampleIsNumber() { + fmt.Printf("%t\n", unicode.IsNumber('Ⅷ')) + fmt.Printf("%t\n", unicode.IsNumber('A')) + // Output: + // true + // false +} + +func ExampleIsLetter() { + fmt.Printf("%t\n", unicode.IsLetter('A')) + fmt.Printf("%t\n", unicode.IsLetter('7')) + // Output: + // true + // false +} + +func ExampleIsLower() { + fmt.Printf("%t\n", unicode.IsLower('a')) + fmt.Printf("%t\n", unicode.IsLower('A')) + // Output: + // true + // false +} + +func ExampleIsUpper() { + fmt.Printf("%t\n", unicode.IsUpper('A')) + fmt.Printf("%t\n", unicode.IsUpper('a')) + // Output: + // true + // false +} + +func ExampleIsTitle() { + fmt.Printf("%t\n", unicode.IsTitle('Dž')) + fmt.Printf("%t\n", unicode.IsTitle('a')) + // Output: + // true + // false +} + +func ExampleIsSpace() { + fmt.Printf("%t\n", unicode.IsSpace(' ')) + fmt.Printf("%t\n", unicode.IsSpace('\n')) + fmt.Printf("%t\n", unicode.IsSpace('\t')) + fmt.Printf("%t\n", unicode.IsSpace('a')) + // Output: + // true + // true + // true + // false +} diff --git a/src/unicode/graphic.go b/src/unicode/graphic.go index ca6241949a23f1..2af29778bf3969 100644 --- a/src/unicode/graphic.go +++ b/src/unicode/graphic.go @@ -120,7 +120,9 @@ func IsPunct(r rune) bool { // IsSpace reports whether the rune is a space character as defined // by Unicode's White Space property; in the Latin-1 space // this is +// // '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP). +// // Other definitions of spacing characters are set by category // Z and property Pattern_White_Space. func IsSpace(r rune) bool { diff --git a/src/unicode/letter.go b/src/unicode/letter.go index 268e457a875311..f3f8e52964829d 100644 --- a/src/unicode/letter.go +++ b/src/unicode/letter.go @@ -49,7 +49,9 @@ type Range32 struct { // means the character is in the corresponding case. There is a special // case representing sequences of alternating corresponding Upper and Lower // pairs. It appears with a fixed Delta of +// // {UpperLower, UpperLower, UpperLower} +// // The constant UpperLower has an otherwise impossible delta value. type CaseRange struct { Lo uint32 @@ -324,6 +326,7 @@ type foldPair struct { // If r is not a valid Unicode code point, SimpleFold(r) returns r. // // For example: +// // SimpleFold('A') = 'a' // SimpleFold('a') = 'A' // @@ -334,7 +337,6 @@ type foldPair struct { // SimpleFold('1') = '1' // // SimpleFold(-2) = -2 -// func SimpleFold(r rune) rune { if r < 0 || r > MaxRune { return r diff --git a/src/unicode/utf16/utf16.go b/src/unicode/utf16/utf16.go index 1a881aa769591e..38d8be6060293e 100644 --- a/src/unicode/utf16/utf16.go +++ b/src/unicode/utf16/utf16.go @@ -83,6 +83,23 @@ func Encode(s []rune) []uint16 { return a[:n] } +// AppendRune appends the UTF-16 encoding of the Unicode code point r +// to the end of p and returns the extended buffer. If the rune is not +// a valid Unicode code point, it appends the encoding of U+FFFD. +func AppendRune(a []uint16, r rune) []uint16 { + // This function is inlineable for fast handling of ASCII. + switch { + case 0 <= r && r < surr1, surr3 <= r && r < surrSelf: + // normal rune + return append(a, uint16(r)) + case surrSelf <= r && r <= maxRune: + // needs surrogate sequence + r1, r2 := EncodeRune(r) + return append(a, uint16(r1), uint16(r2)) + } + return append(a, replacementChar) +} + // Decode returns the Unicode code point sequence represented // by the UTF-16 encoding s. func Decode(s []uint16) []rune { diff --git a/src/unicode/utf16/utf16_test.go b/src/unicode/utf16/utf16_test.go index 4ecaabef96c59f..be339b1fdf137b 100644 --- a/src/unicode/utf16/utf16_test.go +++ b/src/unicode/utf16/utf16_test.go @@ -43,6 +43,18 @@ func TestEncode(t *testing.T) { } } +func TestAppendRune(t *testing.T) { + for _, tt := range encodeTests { + var out []uint16 + for _, u := range tt.in { + out = AppendRune(out, u) + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("AppendRune(%x) = %x; want %x", tt.in, out, tt.out) + } + } +} + func TestEncodeRune(t *testing.T) { for i, tt := range encodeTests { j := 0 @@ -193,6 +205,28 @@ func BenchmarkEncodeValidJapaneseChars(b *testing.B) { } } +func BenchmarkAppendRuneValidASCII(b *testing.B) { + data := []rune{'h', 'e', 'l', 'l', 'o'} + a := make([]uint16, 0, len(data)*2) + for i := 0; i < b.N; i++ { + for _, u := range data { + a = AppendRune(a, u) + } + a = a[:0] + } +} + +func BenchmarkAppendRuneValidJapaneseChars(b *testing.B) { + data := []rune{'日', '本', '語'} + a := make([]uint16, 0, len(data)*2) + for i := 0; i < b.N; i++ { + for _, u := range data { + a = AppendRune(a, u) + } + a = a[:0] + } +} + func BenchmarkEncodeRune(b *testing.B) { for i := 0; i < b.N; i++ { for _, u := range []rune{'𝓐', '𝓑', '𝓒', '𝓓', '𝓔'} { diff --git a/src/unicode/utf8/example_test.go b/src/unicode/utf8/example_test.go index 5cd931d24279ff..fe434c94767c08 100644 --- a/src/unicode/utf8/example_test.go +++ b/src/unicode/utf8/example_test.go @@ -214,3 +214,13 @@ func ExampleValidString() { // true // false } + +func ExampleAppendRune() { + buf1 := utf8.AppendRune(nil, 0x10000) + buf2 := utf8.AppendRune([]byte("init"), 0x10000) + fmt.Println(string(buf1)) + fmt.Println(string(buf2)) + // Output: + // 𐀀 + // init𐀀 +} diff --git a/src/unicode/utf8/utf8.go b/src/unicode/utf8/utf8.go index 557e8a7770c7dd..1e9f666e235d35 100644 --- a/src/unicode/utf8/utf8.go +++ b/src/unicode/utf8/utf8.go @@ -369,6 +369,32 @@ func EncodeRune(p []byte, r rune) int { } } +// AppendRune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendRune(p []byte, r rune) []byte { + // This function is inlineable for fast handling of ASCII. + if uint32(r) <= rune1Max { + return append(p, byte(r)) + } + return appendRuneNonASCII(p, r) +} + +func appendRuneNonASCII(p []byte, r rune) []byte { + // Negative values are erroneous. Making it unsigned addresses the problem. + switch i := uint32(r); { + case i <= rune2Max: + return append(p, t2|byte(r>>6), tx|byte(r)&maskx) + case i > MaxRune, surrogateMin <= i && i <= surrogateMax: + r = RuneError + fallthrough + case i <= rune3Max: + return append(p, t3|byte(r>>12), tx|byte(r>>6)&maskx, tx|byte(r)&maskx) + default: + return append(p, t4|byte(r>>18), tx|byte(r>>12)&maskx, tx|byte(r>>6)&maskx, tx|byte(r)&maskx) + } +} + // RuneCount returns the number of runes in p. Erroneous and short // encodings are treated as single runes of width 1 byte. func RuneCount(p []byte) int { @@ -449,6 +475,11 @@ func RuneStart(b byte) bool { return b&0xC0 != 0x80 } // Valid reports whether p consists entirely of valid UTF-8-encoded runes. func Valid(p []byte) bool { + // This optimization avoids the need to recompute the capacity + // when generating code for p[8:], bringing it to parity with + // ValidString, which was 20% faster on long ASCII strings. + p = p[:len(p):len(p)] + // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. for len(p) >= 8 { // Combining two 32 bit loads allows the same code to be used diff --git a/src/unicode/utf8/utf8_test.go b/src/unicode/utf8/utf8_test.go index eaf1b5ffee39e0..19a04dc92e7fd8 100644 --- a/src/unicode/utf8/utf8_test.go +++ b/src/unicode/utf8/utf8_test.go @@ -6,6 +6,7 @@ package utf8_test import ( "bytes" + "strings" "testing" "unicode" . "unicode/utf8" @@ -127,6 +128,17 @@ func TestEncodeRune(t *testing.T) { } } +func TestAppendRune(t *testing.T) { + for _, m := range utf8map { + if buf := AppendRune(nil, m.r); string(buf) != m.str { + t.Errorf("AppendRune(nil, %#04x) = %s, want %s", m.r, buf, m.str) + } + if buf := AppendRune([]byte("init"), m.r); string(buf) != "init"+m.str { + t.Errorf("AppendRune(init, %#04x) = %s, want %s", m.r, buf, "init"+m.str) + } + } +} + func TestDecodeRune(t *testing.T) { for _, m := range utf8map { b := []byte(m.str) @@ -543,6 +555,8 @@ func BenchmarkRuneCountInStringTenJapaneseChars(b *testing.B) { } } +var ascii100000 = strings.Repeat("0123456789", 10000) + func BenchmarkValidTenASCIIChars(b *testing.B) { s := []byte("0123456789") for i := 0; i < b.N; i++ { @@ -550,12 +564,32 @@ func BenchmarkValidTenASCIIChars(b *testing.B) { } } +func BenchmarkValid100KASCIIChars(b *testing.B) { + s := []byte(ascii100000) + for i := 0; i < b.N; i++ { + Valid(s) + } +} + func BenchmarkValidTenJapaneseChars(b *testing.B) { s := []byte("日本語日本語日本語日") for i := 0; i < b.N; i++ { Valid(s) } } +func BenchmarkValidLongMostlyASCII(b *testing.B) { + longMostlyASCII := []byte(longStringMostlyASCII) + for i := 0; i < b.N; i++ { + Valid(longMostlyASCII) + } +} + +func BenchmarkValidLongJapanese(b *testing.B) { + longJapanese := []byte(longStringJapanese) + for i := 0; i < b.N; i++ { + Valid(longJapanese) + } +} func BenchmarkValidStringTenASCIIChars(b *testing.B) { for i := 0; i < b.N; i++ { @@ -563,12 +597,47 @@ func BenchmarkValidStringTenASCIIChars(b *testing.B) { } } +func BenchmarkValidString100KASCIIChars(b *testing.B) { + for i := 0; i < b.N; i++ { + ValidString(ascii100000) + } +} + func BenchmarkValidStringTenJapaneseChars(b *testing.B) { for i := 0; i < b.N; i++ { ValidString("日本語日本語日本語日") } } +func BenchmarkValidStringLongMostlyASCII(b *testing.B) { + for i := 0; i < b.N; i++ { + ValidString(longStringMostlyASCII) + } +} + +func BenchmarkValidStringLongJapanese(b *testing.B) { + for i := 0; i < b.N; i++ { + ValidString(longStringJapanese) + } +} + +var longStringMostlyASCII string // ~100KB, ~97% ASCII +var longStringJapanese string // ~100KB, non-ASCII + +func init() { + const japanese = "日本語日本語日本語日" + var b strings.Builder + for i := 0; b.Len() < 100_000; i++ { + if i%100 == 0 { + b.WriteString(japanese) + } else { + b.WriteString("0123456789") + } + } + longStringMostlyASCII = b.String() + longStringJapanese = strings.Repeat(japanese, 100_000/len(japanese)) +} + func BenchmarkEncodeASCIIRune(b *testing.B) { buf := make([]byte, UTFMax) for i := 0; i < b.N; i++ { @@ -583,6 +652,20 @@ func BenchmarkEncodeJapaneseRune(b *testing.B) { } } +func BenchmarkAppendASCIIRune(b *testing.B) { + buf := make([]byte, UTFMax) + for i := 0; i < b.N; i++ { + AppendRune(buf[:0], 'a') + } +} + +func BenchmarkAppendJapaneseRune(b *testing.B) { + buf := make([]byte, UTFMax) + for i := 0; i < b.N; i++ { + AppendRune(buf[:0], '本') + } +} + func BenchmarkDecodeASCIIRune(b *testing.B) { a := []byte{'a'} for i := 0; i < b.N; i++ { diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go index 16e3890d0bee39..5051b3ee9fe2e2 100644 --- a/src/unsafe/unsafe.go +++ b/src/unsafe/unsafe.go @@ -3,10 +3,10 @@ // license that can be found in the LICENSE file. /* - Package unsafe contains operations that step around the type safety of Go programs. +Package unsafe contains operations that step around the type safety of Go programs. - Packages that import unsafe may be non-portable and are not protected by the - Go 1 compatibility guidelines. +Packages that import unsafe may be non-portable and are not protected by the +Go 1 compatibility guidelines. */ package unsafe @@ -20,10 +20,11 @@ type IntegerType int // Pointer represents a pointer to an arbitrary type. There are four special operations // available for type Pointer that are not available for other types: -// - A pointer value of any type can be converted to a Pointer. -// - A Pointer can be converted to a pointer value of any type. -// - A uintptr can be converted to a Pointer. -// - A Pointer can be converted to a uintptr. +// - A pointer value of any type can be converted to a Pointer. +// - A Pointer can be converted to a pointer value of any type. +// - A uintptr can be converted to a Pointer. +// - A Pointer can be converted to a uintptr. +// // Pointer therefore allows a program to defeat the type system and read and write // arbitrary memory. It should be used with extreme care. // @@ -180,7 +181,6 @@ type IntegerType int // hdr.Data = uintptr(unsafe.Pointer(p)) // hdr.Len = n // s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost -// type Pointer *ArbitraryType // Sizeof takes an expression x of any type and returns the size in bytes @@ -188,13 +188,19 @@ type Pointer *ArbitraryType // The size does not include any memory possibly referenced by x. // For instance, if x is a slice, Sizeof returns the size of the slice // descriptor, not the size of the memory referenced by the slice. -// The return value of Sizeof is a Go constant. +// For a struct, the size includes any padding introduced by field alignment. +// The return value of Sizeof is a Go constant if the type of the argument x +// does not have variable size. +// (A type has variable size if it is a type parameter or if it is an array +// or struct type with elements of variable size). func Sizeof(x ArbitraryType) uintptr // Offsetof returns the offset within the struct of the field represented by x, // which must be of the form structValue.field. In other words, it returns the // number of bytes between the start of the struct and the start of the field. -// The return value of Offsetof is a Go constant. +// The return value of Offsetof is a Go constant if the type of the argument x +// does not have variable size. +// (See the description of [Sizeof] for a definition of variable sized types.) func Offsetof(x ArbitraryType) uintptr // Alignof takes an expression x of any type and returns the required alignment @@ -205,7 +211,9 @@ func Offsetof(x ArbitraryType) uintptr // within that struct, then Alignof(s.f) will return the required alignment // of a field of that type within a struct. This case is the same as the // value returned by reflect.TypeOf(s.f).FieldAlign(). -// The return value of Alignof is a Go constant. +// The return value of Alignof is a Go constant if the type of the argument +// does not have variable size. +// (See the description of [Sizeof] for a definition of variable sized types.) func Alignof(x ArbitraryType) uintptr // The function Add adds len to ptr and returns the updated pointer diff --git a/src/vendor/golang.org/x/crypto/AUTHORS b/src/vendor/golang.org/x/crypto/AUTHORS deleted file mode 100644 index 2b00ddba0dfee1..00000000000000 --- a/src/vendor/golang.org/x/crypto/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at https://tip.golang.org/AUTHORS. diff --git a/src/vendor/golang.org/x/crypto/CONTRIBUTORS b/src/vendor/golang.org/x/crypto/CONTRIBUTORS deleted file mode 100644 index 1fbd3e976faf5a..00000000000000 --- a/src/vendor/golang.org/x/crypto/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at https://tip.golang.org/CONTRIBUTORS. diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s b/src/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s index 8fb49a13e3bf23..63cae9e6f0b1ba 100644 --- a/src/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s +++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.11 && gc && !purego // +build go1.11,gc,!purego #include "textflag.h" diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/src/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s index 3dad4b2fa27b6a..5c0fed26f85027 100644 --- a/src/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s +++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s @@ -19,6 +19,7 @@ // The differences in this and the original implementation are // due to the calling conventions and initialization of constants. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go index c5898db4658453..4652247b8a637e 100644 --- a/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go +++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go @@ -15,6 +15,7 @@ const bufSize = 256 // xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only // be called when the vector facility is available. Implementation in asm_s390x.s. +// //go:noescape func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) diff --git a/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s b/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s index 818161189bc4b1..f3ef5a019d9593 100644 --- a/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s +++ b/src/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "go_asm.h" diff --git a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go index 0d7bac3f7db586..93da7322bc48c2 100644 --- a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go +++ b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go @@ -26,6 +26,10 @@ const ( // NonceSizeX is the size of the nonce used with the XChaCha20-Poly1305 // variant of this AEAD, in bytes. NonceSizeX = 24 + + // Overhead is the size of the Poly1305 authentication tag, and the + // difference between a ciphertext length and its plaintext. + Overhead = 16 ) type chacha20poly1305 struct { @@ -47,7 +51,7 @@ func (c *chacha20poly1305) NonceSize() int { } func (c *chacha20poly1305) Overhead() int { - return 16 + return Overhead } func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { diff --git a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s index 55226b0e6c2c59..867c181a14c074 100644 --- a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s +++ b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s @@ -4,6 +4,7 @@ // This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go index fe191d395d5551..96b2fd898bbca5 100644 --- a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go +++ b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go @@ -8,8 +8,8 @@ import ( "encoding/binary" "golang.org/x/crypto/chacha20" + "golang.org/x/crypto/internal/poly1305" "golang.org/x/crypto/internal/subtle" - "golang.org/x/crypto/poly1305" ) func writeWithPadding(p *poly1305.MAC, b []byte) { diff --git a/src/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go b/src/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go index d9d46b96396e73..1cebfe946f4440 100644 --- a/src/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go +++ b/src/vendor/golang.org/x/crypto/chacha20poly1305/xchacha20poly1305.go @@ -35,7 +35,7 @@ func (*xchacha20poly1305) NonceSize() int { } func (*xchacha20poly1305) Overhead() int { - return 16 + return Overhead } func (x *xchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { diff --git a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 83c776de083024..3a1674a1e57cbb 100644 --- a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -407,7 +407,12 @@ func (s *String) ReadASN1Enum(out *int) bool { func (s *String) readBase128Int(out *int) bool { ret := 0 for i := 0; len(*s) > 0; i++ { - if i == 4 { + if i == 5 { + return false + } + // Avoid overflowing int on a 32-bit platform. + // We don't want different behavior based on the architecture. + if ret >= 1<<(31-7) { return false } ret <<= 7 diff --git a/src/vendor/golang.org/x/crypto/cryptobyte/builder.go b/src/vendor/golang.org/x/crypto/cryptobyte/builder.go index ca7b1db5ce9d1d..c7ded7577168ff 100644 --- a/src/vendor/golang.org/x/crypto/cryptobyte/builder.go +++ b/src/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -106,13 +106,13 @@ func (b *Builder) AddBytes(v []byte) { // supplied to them. The child builder passed to the continuation can be used // to build the content of the length-prefixed sequence. For example: // -// parent := cryptobyte.NewBuilder() -// parent.AddUint8LengthPrefixed(func (child *Builder) { -// child.AddUint8(42) -// child.AddUint8LengthPrefixed(func (grandchild *Builder) { -// grandchild.AddUint8(5) -// }) -// }) +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) // // It is an error to write more bytes to the child than allowed by the reserved // length prefix. After the continuation returns, the child must be considered diff --git a/src/vendor/golang.org/x/crypto/curve25519/curve25519.go b/src/vendor/golang.org/x/crypto/curve25519/curve25519.go deleted file mode 100644 index 4b9a655d1b5623..00000000000000 --- a/src/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package curve25519 provides an implementation of the X25519 function, which -// performs scalar multiplication on the elliptic curve known as Curve25519. -// See RFC 7748. -package curve25519 // import "golang.org/x/crypto/curve25519" - -import ( - "crypto/subtle" - "fmt" -) - -// ScalarMult sets dst to the product scalar * point. -// -// Deprecated: when provided a low-order point, ScalarMult will set dst to all -// zeroes, irrespective of the scalar. Instead, use the X25519 function, which -// will return an error. -func ScalarMult(dst, scalar, point *[32]byte) { - scalarMult(dst, scalar, point) -} - -// ScalarBaseMult sets dst to the product scalar * base where base is the -// standard generator. -// -// It is recommended to use the X25519 function with Basepoint instead, as -// copying into fixed size arrays can lead to unexpected bugs. -func ScalarBaseMult(dst, scalar *[32]byte) { - ScalarMult(dst, scalar, &basePoint) -} - -const ( - // ScalarSize is the size of the scalar input to X25519. - ScalarSize = 32 - // PointSize is the size of the point input to X25519. - PointSize = 32 -) - -// Basepoint is the canonical Curve25519 generator. -var Basepoint []byte - -var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - -func init() { Basepoint = basePoint[:] } - -func checkBasepoint() { - if subtle.ConstantTimeCompare(Basepoint, []byte{ - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }) != 1 { - panic("curve25519: global Basepoint value was modified") - } -} - -// X25519 returns the result of the scalar multiplication (scalar * point), -// according to RFC 7748, Section 5. scalar, point and the return value are -// slices of 32 bytes. -// -// scalar can be generated at random, for example with crypto/rand. point should -// be either Basepoint or the output of another X25519 call. -// -// If point is Basepoint (but not if it's a different slice with the same -// contents) a precomputed implementation might be used for performance. -func X25519(scalar, point []byte) ([]byte, error) { - // Outline the body of function, to let the allocation be inlined in the - // caller, and possibly avoid escaping to the heap. - var dst [32]byte - return x25519(&dst, scalar, point) -} - -func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { - var in [32]byte - if l := len(scalar); l != 32 { - return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32) - } - if l := len(point); l != 32 { - return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32) - } - copy(in[:], scalar) - if &point[0] == &Basepoint[0] { - checkBasepoint() - ScalarBaseMult(dst, &in) - } else { - var base, zero [32]byte - copy(base[:], point) - ScalarMult(dst, &in, &base) - if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 { - return nil, fmt.Errorf("bad input point: low order point") - } - } - return dst[:], nil -} diff --git a/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go b/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go deleted file mode 100644 index 84858480dff5fb..00000000000000 --- a/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build amd64 && gc && !purego -// +build amd64,gc,!purego - -package curve25519 - -// These functions are implemented in the .s files. The names of the functions -// in the rest of the file are also taken from the SUPERCOP sources to help -// people following along. - -//go:noescape - -func cswap(inout *[5]uint64, v uint64) - -//go:noescape - -func ladderstep(inout *[5][5]uint64) - -//go:noescape - -func freeze(inout *[5]uint64) - -//go:noescape - -func mul(dest, a, b *[5]uint64) - -//go:noescape - -func square(out, in *[5]uint64) - -// mladder uses a Montgomery ladder to calculate (xr/zr) *= s. -func mladder(xr, zr *[5]uint64, s *[32]byte) { - var work [5][5]uint64 - - work[0] = *xr - setint(&work[1], 1) - setint(&work[2], 0) - work[3] = *xr - setint(&work[4], 1) - - j := uint(6) - var prevbit byte - - for i := 31; i >= 0; i-- { - for j < 8 { - bit := ((*s)[i] >> j) & 1 - swap := bit ^ prevbit - prevbit = bit - cswap(&work[1], uint64(swap)) - ladderstep(&work) - j-- - } - j = 7 - } - - *xr = work[1] - *zr = work[2] -} - -func scalarMult(out, in, base *[32]byte) { - var e [32]byte - copy(e[:], (*in)[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var t, z [5]uint64 - unpack(&t, base) - mladder(&t, &z, &e) - invert(&z, &z) - mul(&t, &t, &z) - pack(out, &t) -} - -func setint(r *[5]uint64, v uint64) { - r[0] = v - r[1] = 0 - r[2] = 0 - r[3] = 0 - r[4] = 0 -} - -// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian -// order. -func unpack(r *[5]uint64, x *[32]byte) { - r[0] = uint64(x[0]) | - uint64(x[1])<<8 | - uint64(x[2])<<16 | - uint64(x[3])<<24 | - uint64(x[4])<<32 | - uint64(x[5])<<40 | - uint64(x[6]&7)<<48 - - r[1] = uint64(x[6])>>3 | - uint64(x[7])<<5 | - uint64(x[8])<<13 | - uint64(x[9])<<21 | - uint64(x[10])<<29 | - uint64(x[11])<<37 | - uint64(x[12]&63)<<45 - - r[2] = uint64(x[12])>>6 | - uint64(x[13])<<2 | - uint64(x[14])<<10 | - uint64(x[15])<<18 | - uint64(x[16])<<26 | - uint64(x[17])<<34 | - uint64(x[18])<<42 | - uint64(x[19]&1)<<50 - - r[3] = uint64(x[19])>>1 | - uint64(x[20])<<7 | - uint64(x[21])<<15 | - uint64(x[22])<<23 | - uint64(x[23])<<31 | - uint64(x[24])<<39 | - uint64(x[25]&15)<<47 - - r[4] = uint64(x[25])>>4 | - uint64(x[26])<<4 | - uint64(x[27])<<12 | - uint64(x[28])<<20 | - uint64(x[29])<<28 | - uint64(x[30])<<36 | - uint64(x[31]&127)<<44 -} - -// pack sets out = x where out is the usual, little-endian form of the 5, -// 51-bit limbs in x. -func pack(out *[32]byte, x *[5]uint64) { - t := *x - freeze(&t) - - out[0] = byte(t[0]) - out[1] = byte(t[0] >> 8) - out[2] = byte(t[0] >> 16) - out[3] = byte(t[0] >> 24) - out[4] = byte(t[0] >> 32) - out[5] = byte(t[0] >> 40) - out[6] = byte(t[0] >> 48) - - out[6] ^= byte(t[1]<<3) & 0xf8 - out[7] = byte(t[1] >> 5) - out[8] = byte(t[1] >> 13) - out[9] = byte(t[1] >> 21) - out[10] = byte(t[1] >> 29) - out[11] = byte(t[1] >> 37) - out[12] = byte(t[1] >> 45) - - out[12] ^= byte(t[2]<<6) & 0xc0 - out[13] = byte(t[2] >> 2) - out[14] = byte(t[2] >> 10) - out[15] = byte(t[2] >> 18) - out[16] = byte(t[2] >> 26) - out[17] = byte(t[2] >> 34) - out[18] = byte(t[2] >> 42) - out[19] = byte(t[2] >> 50) - - out[19] ^= byte(t[3]<<1) & 0xfe - out[20] = byte(t[3] >> 7) - out[21] = byte(t[3] >> 15) - out[22] = byte(t[3] >> 23) - out[23] = byte(t[3] >> 31) - out[24] = byte(t[3] >> 39) - out[25] = byte(t[3] >> 47) - - out[25] ^= byte(t[4]<<4) & 0xf0 - out[26] = byte(t[4] >> 4) - out[27] = byte(t[4] >> 12) - out[28] = byte(t[4] >> 20) - out[29] = byte(t[4] >> 28) - out[30] = byte(t[4] >> 36) - out[31] = byte(t[4] >> 44) -} - -// invert calculates r = x^-1 mod p using Fermat's little theorem. -func invert(r *[5]uint64, x *[5]uint64) { - var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 - - square(&z2, x) /* 2 */ - square(&t, &z2) /* 4 */ - square(&t, &t) /* 8 */ - mul(&z9, &t, x) /* 9 */ - mul(&z11, &z9, &z2) /* 11 */ - square(&t, &z11) /* 22 */ - mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ - - square(&t, &z2_5_0) /* 2^6 - 2^1 */ - for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ - square(&t, &t) - } - mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ - - square(&t, &z2_10_0) /* 2^11 - 2^1 */ - for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ - square(&t, &t) - } - mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ - - square(&t, &z2_20_0) /* 2^21 - 2^1 */ - for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ - square(&t, &t) - } - mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ - - square(&t, &t) /* 2^41 - 2^1 */ - for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ - square(&t, &t) - } - mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ - - square(&t, &z2_50_0) /* 2^51 - 2^1 */ - for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ - square(&t, &t) - } - mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ - - square(&t, &z2_100_0) /* 2^101 - 2^1 */ - for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ - square(&t, &t) - } - mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ - - square(&t, &t) /* 2^201 - 2^1 */ - for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ - square(&t, &t) - } - mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ - - square(&t, &t) /* 2^251 - 2^1 */ - square(&t, &t) /* 2^252 - 2^2 */ - square(&t, &t) /* 2^253 - 2^3 */ - - square(&t, &t) /* 2^254 - 2^4 */ - - square(&t, &t) /* 2^255 - 2^5 */ - mul(r, &t, &z11) /* 2^255 - 21 */ -} diff --git a/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s b/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s deleted file mode 100644 index 6c533809266b11..00000000000000 --- a/src/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s +++ /dev/null @@ -1,1793 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html - -// +build amd64,gc,!purego - -#define REDMASK51 0x0007FFFFFFFFFFFF - -// These constants cannot be encoded in non-MOVQ immediates. -// We access them directly from memory instead. - -DATA ·_121666_213(SB)/8, $996687872 -GLOBL ·_121666_213(SB), 8, $8 - -DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA -GLOBL ·_2P0(SB), 8, $8 - -DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE -GLOBL ·_2P1234(SB), 8, $8 - -// func freeze(inout *[5]uint64) -TEXT ·freeze(SB),7,$0-8 - MOVQ inout+0(FP), DI - - MOVQ 0(DI),SI - MOVQ 8(DI),DX - MOVQ 16(DI),CX - MOVQ 24(DI),R8 - MOVQ 32(DI),R9 - MOVQ $REDMASK51,AX - MOVQ AX,R10 - SUBQ $18,R10 - MOVQ $3,R11 -REDUCELOOP: - MOVQ SI,R12 - SHRQ $51,R12 - ANDQ AX,SI - ADDQ R12,DX - MOVQ DX,R12 - SHRQ $51,R12 - ANDQ AX,DX - ADDQ R12,CX - MOVQ CX,R12 - SHRQ $51,R12 - ANDQ AX,CX - ADDQ R12,R8 - MOVQ R8,R12 - SHRQ $51,R12 - ANDQ AX,R8 - ADDQ R12,R9 - MOVQ R9,R12 - SHRQ $51,R12 - ANDQ AX,R9 - IMUL3Q $19,R12,R12 - ADDQ R12,SI - SUBQ $1,R11 - JA REDUCELOOP - MOVQ $1,R12 - CMPQ R10,SI - CMOVQLT R11,R12 - CMPQ AX,DX - CMOVQNE R11,R12 - CMPQ AX,CX - CMOVQNE R11,R12 - CMPQ AX,R8 - CMOVQNE R11,R12 - CMPQ AX,R9 - CMOVQNE R11,R12 - NEGQ R12 - ANDQ R12,AX - ANDQ R12,R10 - SUBQ R10,SI - SUBQ AX,DX - SUBQ AX,CX - SUBQ AX,R8 - SUBQ AX,R9 - MOVQ SI,0(DI) - MOVQ DX,8(DI) - MOVQ CX,16(DI) - MOVQ R8,24(DI) - MOVQ R9,32(DI) - RET - -// func ladderstep(inout *[5][5]uint64) -TEXT ·ladderstep(SB),0,$296-8 - MOVQ inout+0(FP),DI - - MOVQ 40(DI),SI - MOVQ 48(DI),DX - MOVQ 56(DI),CX - MOVQ 64(DI),R8 - MOVQ 72(DI),R9 - MOVQ SI,AX - MOVQ DX,R10 - MOVQ CX,R11 - MOVQ R8,R12 - MOVQ R9,R13 - ADDQ ·_2P0(SB),AX - ADDQ ·_2P1234(SB),R10 - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 80(DI),SI - ADDQ 88(DI),DX - ADDQ 96(DI),CX - ADDQ 104(DI),R8 - ADDQ 112(DI),R9 - SUBQ 80(DI),AX - SUBQ 88(DI),R10 - SUBQ 96(DI),R11 - SUBQ 104(DI),R12 - SUBQ 112(DI),R13 - MOVQ SI,0(SP) - MOVQ DX,8(SP) - MOVQ CX,16(SP) - MOVQ R8,24(SP) - MOVQ R9,32(SP) - MOVQ AX,40(SP) - MOVQ R10,48(SP) - MOVQ R11,56(SP) - MOVQ R12,64(SP) - MOVQ R13,72(SP) - MOVQ 40(SP),AX - MULQ 40(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 48(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 56(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 64(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 40(SP),AX - SHLQ $1,AX - MULQ 72(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 48(SP),AX - MULQ 48(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 48(SP),AX - SHLQ $1,AX - MULQ 56(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 48(SP),AX - SHLQ $1,AX - MULQ 64(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 48(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 56(SP),AX - MULQ 56(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 56(SP),DX - IMUL3Q $38,DX,AX - MULQ 64(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 56(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 64(SP),DX - IMUL3Q $19,DX,AX - MULQ 64(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 64(SP),DX - IMUL3Q $38,DX,AX - MULQ 72(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 72(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,80(SP) - MOVQ R8,88(SP) - MOVQ R9,96(SP) - MOVQ AX,104(SP) - MOVQ R10,112(SP) - MOVQ 0(SP),AX - MULQ 0(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 8(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 16(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 24(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 0(SP),AX - SHLQ $1,AX - MULQ 32(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 8(SP),AX - MULQ 8(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - SHLQ $1,AX - MULQ 16(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SP),AX - SHLQ $1,AX - MULQ 24(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 16(SP),AX - MULQ 16(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 16(SP),DX - IMUL3Q $38,DX,AX - MULQ 24(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 16(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 24(SP),DX - IMUL3Q $19,DX,AX - MULQ 24(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 24(SP),DX - IMUL3Q $38,DX,AX - MULQ 32(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 32(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,120(SP) - MOVQ R8,128(SP) - MOVQ R9,136(SP) - MOVQ AX,144(SP) - MOVQ R10,152(SP) - MOVQ SI,SI - MOVQ R8,DX - MOVQ R9,CX - MOVQ AX,R8 - MOVQ R10,R9 - ADDQ ·_2P0(SB),SI - ADDQ ·_2P1234(SB),DX - ADDQ ·_2P1234(SB),CX - ADDQ ·_2P1234(SB),R8 - ADDQ ·_2P1234(SB),R9 - SUBQ 80(SP),SI - SUBQ 88(SP),DX - SUBQ 96(SP),CX - SUBQ 104(SP),R8 - SUBQ 112(SP),R9 - MOVQ SI,160(SP) - MOVQ DX,168(SP) - MOVQ CX,176(SP) - MOVQ R8,184(SP) - MOVQ R9,192(SP) - MOVQ 120(DI),SI - MOVQ 128(DI),DX - MOVQ 136(DI),CX - MOVQ 144(DI),R8 - MOVQ 152(DI),R9 - MOVQ SI,AX - MOVQ DX,R10 - MOVQ CX,R11 - MOVQ R8,R12 - MOVQ R9,R13 - ADDQ ·_2P0(SB),AX - ADDQ ·_2P1234(SB),R10 - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 160(DI),SI - ADDQ 168(DI),DX - ADDQ 176(DI),CX - ADDQ 184(DI),R8 - ADDQ 192(DI),R9 - SUBQ 160(DI),AX - SUBQ 168(DI),R10 - SUBQ 176(DI),R11 - SUBQ 184(DI),R12 - SUBQ 192(DI),R13 - MOVQ SI,200(SP) - MOVQ DX,208(SP) - MOVQ CX,216(SP) - MOVQ R8,224(SP) - MOVQ R9,232(SP) - MOVQ AX,240(SP) - MOVQ R10,248(SP) - MOVQ R11,256(SP) - MOVQ R12,264(SP) - MOVQ R13,272(SP) - MOVQ 224(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,280(SP) - MULQ 56(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 232(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,288(SP) - MULQ 48(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 200(SP),AX - MULQ 40(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 200(SP),AX - MULQ 48(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 200(SP),AX - MULQ 56(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 200(SP),AX - MULQ 64(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 200(SP),AX - MULQ 72(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 208(SP),AX - MULQ 40(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 208(SP),AX - MULQ 48(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 208(SP),AX - MULQ 56(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 208(SP),AX - MULQ 64(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 208(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 216(SP),AX - MULQ 40(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 216(SP),AX - MULQ 48(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 216(SP),AX - MULQ 56(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 216(SP),DX - IMUL3Q $19,DX,AX - MULQ 64(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 216(SP),DX - IMUL3Q $19,DX,AX - MULQ 72(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 224(SP),AX - MULQ 40(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 224(SP),AX - MULQ 48(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 280(SP),AX - MULQ 64(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 280(SP),AX - MULQ 72(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 232(SP),AX - MULQ 40(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 288(SP),AX - MULQ 56(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 288(SP),AX - MULQ 64(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 288(SP),AX - MULQ 72(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,40(SP) - MOVQ R8,48(SP) - MOVQ R9,56(SP) - MOVQ AX,64(SP) - MOVQ R10,72(SP) - MOVQ 264(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,200(SP) - MULQ 16(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 272(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,208(SP) - MULQ 8(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 240(SP),AX - MULQ 0(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 240(SP),AX - MULQ 8(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 240(SP),AX - MULQ 16(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 240(SP),AX - MULQ 24(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 240(SP),AX - MULQ 32(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 248(SP),AX - MULQ 0(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 248(SP),AX - MULQ 8(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 248(SP),AX - MULQ 16(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 248(SP),AX - MULQ 24(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 248(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 256(SP),AX - MULQ 0(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 256(SP),AX - MULQ 8(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 256(SP),AX - MULQ 16(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 256(SP),DX - IMUL3Q $19,DX,AX - MULQ 24(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 256(SP),DX - IMUL3Q $19,DX,AX - MULQ 32(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 264(SP),AX - MULQ 0(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 264(SP),AX - MULQ 8(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 200(SP),AX - MULQ 24(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 200(SP),AX - MULQ 32(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 272(SP),AX - MULQ 0(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 208(SP),AX - MULQ 16(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 208(SP),AX - MULQ 24(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 208(SP),AX - MULQ 32(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,DX - MOVQ R8,CX - MOVQ R9,R11 - MOVQ AX,R12 - MOVQ R10,R13 - ADDQ ·_2P0(SB),DX - ADDQ ·_2P1234(SB),CX - ADDQ ·_2P1234(SB),R11 - ADDQ ·_2P1234(SB),R12 - ADDQ ·_2P1234(SB),R13 - ADDQ 40(SP),SI - ADDQ 48(SP),R8 - ADDQ 56(SP),R9 - ADDQ 64(SP),AX - ADDQ 72(SP),R10 - SUBQ 40(SP),DX - SUBQ 48(SP),CX - SUBQ 56(SP),R11 - SUBQ 64(SP),R12 - SUBQ 72(SP),R13 - MOVQ SI,120(DI) - MOVQ R8,128(DI) - MOVQ R9,136(DI) - MOVQ AX,144(DI) - MOVQ R10,152(DI) - MOVQ DX,160(DI) - MOVQ CX,168(DI) - MOVQ R11,176(DI) - MOVQ R12,184(DI) - MOVQ R13,192(DI) - MOVQ 120(DI),AX - MULQ 120(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 128(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 136(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 144(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 120(DI),AX - SHLQ $1,AX - MULQ 152(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 128(DI),AX - MULQ 128(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 128(DI),AX - SHLQ $1,AX - MULQ 136(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 128(DI),AX - SHLQ $1,AX - MULQ 144(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 128(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(DI),AX - MULQ 136(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 136(DI),DX - IMUL3Q $38,DX,AX - MULQ 144(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(DI),DX - IMUL3Q $19,DX,AX - MULQ 144(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(DI),DX - IMUL3Q $38,DX,AX - MULQ 152(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 152(DI),DX - IMUL3Q $19,DX,AX - MULQ 152(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,120(DI) - MOVQ R8,128(DI) - MOVQ R9,136(DI) - MOVQ AX,144(DI) - MOVQ R10,152(DI) - MOVQ 160(DI),AX - MULQ 160(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 168(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 176(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 184(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 160(DI),AX - SHLQ $1,AX - MULQ 192(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 168(DI),AX - MULQ 168(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 168(DI),AX - SHLQ $1,AX - MULQ 176(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 168(DI),AX - SHLQ $1,AX - MULQ 184(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 168(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),AX - MULQ 176(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 176(DI),DX - IMUL3Q $38,DX,AX - MULQ 184(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),DX - IMUL3Q $19,DX,AX - MULQ 184(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),DX - IMUL3Q $38,DX,AX - MULQ 192(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 192(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - ANDQ DX,SI - MOVQ CX,R8 - SHRQ $51,CX - ADDQ R10,CX - ANDQ DX,R8 - MOVQ CX,R9 - SHRQ $51,CX - ADDQ R12,CX - ANDQ DX,R9 - MOVQ CX,AX - SHRQ $51,CX - ADDQ R14,CX - ANDQ DX,AX - MOVQ CX,R10 - SHRQ $51,CX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,160(DI) - MOVQ R8,168(DI) - MOVQ R9,176(DI) - MOVQ AX,184(DI) - MOVQ R10,192(DI) - MOVQ 184(DI),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 16(DI) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 192(DI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 8(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 160(DI),AX - MULQ 0(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 160(DI),AX - MULQ 8(DI) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 160(DI),AX - MULQ 16(DI) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 160(DI),AX - MULQ 24(DI) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 160(DI),AX - MULQ 32(DI) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 168(DI),AX - MULQ 0(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 168(DI),AX - MULQ 8(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 168(DI),AX - MULQ 16(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 168(DI),AX - MULQ 24(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 168(DI),DX - IMUL3Q $19,DX,AX - MULQ 32(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),AX - MULQ 0(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 176(DI),AX - MULQ 8(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 176(DI),AX - MULQ 16(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 176(DI),DX - IMUL3Q $19,DX,AX - MULQ 24(DI) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 176(DI),DX - IMUL3Q $19,DX,AX - MULQ 32(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 184(DI),AX - MULQ 0(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 184(DI),AX - MULQ 8(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 24(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 32(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 192(DI),AX - MULQ 0(DI) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 16(DI) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 24(DI) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 32(DI) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,160(DI) - MOVQ R8,168(DI) - MOVQ R9,176(DI) - MOVQ AX,184(DI) - MOVQ R10,192(DI) - MOVQ 144(SP),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 96(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 152(SP),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 88(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 120(SP),AX - MULQ 80(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 120(SP),AX - MULQ 88(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 120(SP),AX - MULQ 96(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 120(SP),AX - MULQ 104(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 120(SP),AX - MULQ 112(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 128(SP),AX - MULQ 80(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 128(SP),AX - MULQ 88(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 128(SP),AX - MULQ 96(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 128(SP),AX - MULQ 104(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 128(SP),DX - IMUL3Q $19,DX,AX - MULQ 112(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(SP),AX - MULQ 80(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 136(SP),AX - MULQ 88(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 136(SP),AX - MULQ 96(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 136(SP),DX - IMUL3Q $19,DX,AX - MULQ 104(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 136(SP),DX - IMUL3Q $19,DX,AX - MULQ 112(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 144(SP),AX - MULQ 80(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 144(SP),AX - MULQ 88(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 104(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 112(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 152(SP),AX - MULQ 80(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 96(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 104(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 112(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,40(DI) - MOVQ R8,48(DI) - MOVQ R9,56(DI) - MOVQ AX,64(DI) - MOVQ R10,72(DI) - MOVQ 160(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - MOVQ AX,SI - MOVQ DX,CX - MOVQ 168(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,CX - MOVQ DX,R8 - MOVQ 176(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R8 - MOVQ DX,R9 - MOVQ 184(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R9 - MOVQ DX,R10 - MOVQ 192(SP),AX - MULQ ·_121666_213(SB) - SHRQ $13,AX - ADDQ AX,R10 - IMUL3Q $19,DX,DX - ADDQ DX,SI - ADDQ 80(SP),SI - ADDQ 88(SP),CX - ADDQ 96(SP),R8 - ADDQ 104(SP),R9 - ADDQ 112(SP),R10 - MOVQ SI,80(DI) - MOVQ CX,88(DI) - MOVQ R8,96(DI) - MOVQ R9,104(DI) - MOVQ R10,112(DI) - MOVQ 104(DI),SI - IMUL3Q $19,SI,AX - MOVQ AX,0(SP) - MULQ 176(SP) - MOVQ AX,SI - MOVQ DX,CX - MOVQ 112(DI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 168(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 80(DI),AX - MULQ 160(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 80(DI),AX - MULQ 168(SP) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 80(DI),AX - MULQ 176(SP) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 80(DI),AX - MULQ 184(SP) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 80(DI),AX - MULQ 192(SP) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 88(DI),AX - MULQ 160(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 88(DI),AX - MULQ 168(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 88(DI),AX - MULQ 176(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 88(DI),AX - MULQ 184(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 88(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 96(DI),AX - MULQ 160(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 96(DI),AX - MULQ 168(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 96(DI),AX - MULQ 176(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 96(DI),DX - IMUL3Q $19,DX,AX - MULQ 184(SP) - ADDQ AX,SI - ADCQ DX,CX - MOVQ 96(DI),DX - IMUL3Q $19,DX,AX - MULQ 192(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 104(DI),AX - MULQ 160(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 104(DI),AX - MULQ 168(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 0(SP),AX - MULQ 184(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SP),AX - MULQ 192(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 112(DI),AX - MULQ 160(SP) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SP),AX - MULQ 176(SP) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 8(SP),AX - MULQ 184(SP) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 192(SP) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ $REDMASK51,DX - SHLQ $13,SI,CX - ANDQ DX,SI - SHLQ $13,R8,R9 - ANDQ DX,R8 - ADDQ CX,R8 - SHLQ $13,R10,R11 - ANDQ DX,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ DX,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ DX,R14 - ADDQ R13,R14 - IMUL3Q $19,R15,CX - ADDQ CX,SI - MOVQ SI,CX - SHRQ $51,CX - ADDQ R8,CX - MOVQ CX,R8 - SHRQ $51,CX - ANDQ DX,SI - ADDQ R10,CX - MOVQ CX,R9 - SHRQ $51,CX - ANDQ DX,R8 - ADDQ R12,CX - MOVQ CX,AX - SHRQ $51,CX - ANDQ DX,R9 - ADDQ R14,CX - MOVQ CX,R10 - SHRQ $51,CX - ANDQ DX,AX - IMUL3Q $19,CX,CX - ADDQ CX,SI - ANDQ DX,R10 - MOVQ SI,80(DI) - MOVQ R8,88(DI) - MOVQ R9,96(DI) - MOVQ AX,104(DI) - MOVQ R10,112(DI) - RET - -// func cswap(inout *[4][5]uint64, v uint64) -TEXT ·cswap(SB),7,$0 - MOVQ inout+0(FP),DI - MOVQ v+8(FP),SI - - SUBQ $1, SI - NOTQ SI - MOVQ SI, X15 - PSHUFD $0x44, X15, X15 - - MOVOU 0(DI), X0 - MOVOU 16(DI), X2 - MOVOU 32(DI), X4 - MOVOU 48(DI), X6 - MOVOU 64(DI), X8 - MOVOU 80(DI), X1 - MOVOU 96(DI), X3 - MOVOU 112(DI), X5 - MOVOU 128(DI), X7 - MOVOU 144(DI), X9 - - MOVO X1, X10 - MOVO X3, X11 - MOVO X5, X12 - MOVO X7, X13 - MOVO X9, X14 - - PXOR X0, X10 - PXOR X2, X11 - PXOR X4, X12 - PXOR X6, X13 - PXOR X8, X14 - PAND X15, X10 - PAND X15, X11 - PAND X15, X12 - PAND X15, X13 - PAND X15, X14 - PXOR X10, X0 - PXOR X10, X1 - PXOR X11, X2 - PXOR X11, X3 - PXOR X12, X4 - PXOR X12, X5 - PXOR X13, X6 - PXOR X13, X7 - PXOR X14, X8 - PXOR X14, X9 - - MOVOU X0, 0(DI) - MOVOU X2, 16(DI) - MOVOU X4, 32(DI) - MOVOU X6, 48(DI) - MOVOU X8, 64(DI) - MOVOU X1, 80(DI) - MOVOU X3, 96(DI) - MOVOU X5, 112(DI) - MOVOU X7, 128(DI) - MOVOU X9, 144(DI) - RET - -// func mul(dest, a, b *[5]uint64) -TEXT ·mul(SB),0,$16-24 - MOVQ dest+0(FP), DI - MOVQ a+8(FP), SI - MOVQ b+16(FP), DX - - MOVQ DX,CX - MOVQ 24(SI),DX - IMUL3Q $19,DX,AX - MOVQ AX,0(SP) - MULQ 16(CX) - MOVQ AX,R8 - MOVQ DX,R9 - MOVQ 32(SI),DX - IMUL3Q $19,DX,AX - MOVQ AX,8(SP) - MULQ 8(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SI),AX - MULQ 0(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 0(SI),AX - MULQ 8(CX) - MOVQ AX,R10 - MOVQ DX,R11 - MOVQ 0(SI),AX - MULQ 16(CX) - MOVQ AX,R12 - MOVQ DX,R13 - MOVQ 0(SI),AX - MULQ 24(CX) - MOVQ AX,R14 - MOVQ DX,R15 - MOVQ 0(SI),AX - MULQ 32(CX) - MOVQ AX,BX - MOVQ DX,BP - MOVQ 8(SI),AX - MULQ 0(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SI),AX - MULQ 8(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SI),AX - MULQ 16(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 8(SI),AX - MULQ 24(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 8(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 16(SI),AX - MULQ 0(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 16(SI),AX - MULQ 8(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 16(SI),AX - MULQ 16(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 16(SI),DX - IMUL3Q $19,DX,AX - MULQ 24(CX) - ADDQ AX,R8 - ADCQ DX,R9 - MOVQ 16(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 24(SI),AX - MULQ 0(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ 24(SI),AX - MULQ 8(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 0(SP),AX - MULQ 24(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 0(SP),AX - MULQ 32(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 32(SI),AX - MULQ 0(CX) - ADDQ AX,BX - ADCQ DX,BP - MOVQ 8(SP),AX - MULQ 16(CX) - ADDQ AX,R10 - ADCQ DX,R11 - MOVQ 8(SP),AX - MULQ 24(CX) - ADDQ AX,R12 - ADCQ DX,R13 - MOVQ 8(SP),AX - MULQ 32(CX) - ADDQ AX,R14 - ADCQ DX,R15 - MOVQ $REDMASK51,SI - SHLQ $13,R8,R9 - ANDQ SI,R8 - SHLQ $13,R10,R11 - ANDQ SI,R10 - ADDQ R9,R10 - SHLQ $13,R12,R13 - ANDQ SI,R12 - ADDQ R11,R12 - SHLQ $13,R14,R15 - ANDQ SI,R14 - ADDQ R13,R14 - SHLQ $13,BX,BP - ANDQ SI,BX - ADDQ R15,BX - IMUL3Q $19,BP,DX - ADDQ DX,R8 - MOVQ R8,DX - SHRQ $51,DX - ADDQ R10,DX - MOVQ DX,CX - SHRQ $51,DX - ANDQ SI,R8 - ADDQ R12,DX - MOVQ DX,R9 - SHRQ $51,DX - ANDQ SI,CX - ADDQ R14,DX - MOVQ DX,AX - SHRQ $51,DX - ANDQ SI,R9 - ADDQ BX,DX - MOVQ DX,R10 - SHRQ $51,DX - ANDQ SI,AX - IMUL3Q $19,DX,DX - ADDQ DX,R8 - ANDQ SI,R10 - MOVQ R8,0(DI) - MOVQ CX,8(DI) - MOVQ R9,16(DI) - MOVQ AX,24(DI) - MOVQ R10,32(DI) - RET - -// func square(out, in *[5]uint64) -TEXT ·square(SB),7,$0-16 - MOVQ out+0(FP), DI - MOVQ in+8(FP), SI - - MOVQ 0(SI),AX - MULQ 0(SI) - MOVQ AX,CX - MOVQ DX,R8 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 8(SI) - MOVQ AX,R9 - MOVQ DX,R10 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 16(SI) - MOVQ AX,R11 - MOVQ DX,R12 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 24(SI) - MOVQ AX,R13 - MOVQ DX,R14 - MOVQ 0(SI),AX - SHLQ $1,AX - MULQ 32(SI) - MOVQ AX,R15 - MOVQ DX,BX - MOVQ 8(SI),AX - MULQ 8(SI) - ADDQ AX,R11 - ADCQ DX,R12 - MOVQ 8(SI),AX - SHLQ $1,AX - MULQ 16(SI) - ADDQ AX,R13 - ADCQ DX,R14 - MOVQ 8(SI),AX - SHLQ $1,AX - MULQ 24(SI) - ADDQ AX,R15 - ADCQ DX,BX - MOVQ 8(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,CX - ADCQ DX,R8 - MOVQ 16(SI),AX - MULQ 16(SI) - ADDQ AX,R15 - ADCQ DX,BX - MOVQ 16(SI),DX - IMUL3Q $38,DX,AX - MULQ 24(SI) - ADDQ AX,CX - ADCQ DX,R8 - MOVQ 16(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,R9 - ADCQ DX,R10 - MOVQ 24(SI),DX - IMUL3Q $19,DX,AX - MULQ 24(SI) - ADDQ AX,R9 - ADCQ DX,R10 - MOVQ 24(SI),DX - IMUL3Q $38,DX,AX - MULQ 32(SI) - ADDQ AX,R11 - ADCQ DX,R12 - MOVQ 32(SI),DX - IMUL3Q $19,DX,AX - MULQ 32(SI) - ADDQ AX,R13 - ADCQ DX,R14 - MOVQ $REDMASK51,SI - SHLQ $13,CX,R8 - ANDQ SI,CX - SHLQ $13,R9,R10 - ANDQ SI,R9 - ADDQ R8,R9 - SHLQ $13,R11,R12 - ANDQ SI,R11 - ADDQ R10,R11 - SHLQ $13,R13,R14 - ANDQ SI,R13 - ADDQ R12,R13 - SHLQ $13,R15,BX - ANDQ SI,R15 - ADDQ R14,R15 - IMUL3Q $19,BX,DX - ADDQ DX,CX - MOVQ CX,DX - SHRQ $51,DX - ADDQ R9,DX - ANDQ SI,CX - MOVQ DX,R8 - SHRQ $51,DX - ADDQ R11,DX - ANDQ SI,R8 - MOVQ DX,R9 - SHRQ $51,DX - ADDQ R13,DX - ANDQ SI,R9 - MOVQ DX,AX - SHRQ $51,DX - ADDQ R15,DX - ANDQ SI,AX - MOVQ DX,R10 - SHRQ $51,DX - IMUL3Q $19,DX,DX - ADDQ DX,CX - ANDQ SI,R10 - MOVQ CX,0(DI) - MOVQ R8,8(DI) - MOVQ R9,16(DI) - MOVQ AX,24(DI) - MOVQ R10,32(DI) - RET diff --git a/src/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go b/src/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go deleted file mode 100644 index c43b13fc83e70c..00000000000000 --- a/src/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go +++ /dev/null @@ -1,828 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package curve25519 - -import "encoding/binary" - -// This code is a port of the public domain, "ref10" implementation of -// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. - -// fieldElement represents an element of the field GF(2^255 - 19). An element -// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 -// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on -// context. -type fieldElement [10]int32 - -func feZero(fe *fieldElement) { - for i := range fe { - fe[i] = 0 - } -} - -func feOne(fe *fieldElement) { - feZero(fe) - fe[0] = 1 -} - -func feAdd(dst, a, b *fieldElement) { - for i := range dst { - dst[i] = a[i] + b[i] - } -} - -func feSub(dst, a, b *fieldElement) { - for i := range dst { - dst[i] = a[i] - b[i] - } -} - -func feCopy(dst, src *fieldElement) { - for i := range dst { - dst[i] = src[i] - } -} - -// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. -// -// Preconditions: b in {0,1}. -func feCSwap(f, g *fieldElement, b int32) { - b = -b - for i := range f { - t := b & (f[i] ^ g[i]) - f[i] ^= t - g[i] ^= t - } -} - -// load3 reads a 24-bit, little-endian value from in. -func load3(in []byte) int64 { - var r int64 - r = int64(in[0]) - r |= int64(in[1]) << 8 - r |= int64(in[2]) << 16 - return r -} - -// load4 reads a 32-bit, little-endian value from in. -func load4(in []byte) int64 { - return int64(binary.LittleEndian.Uint32(in)) -} - -func feFromBytes(dst *fieldElement, src *[32]byte) { - h0 := load4(src[:]) - h1 := load3(src[4:]) << 6 - h2 := load3(src[7:]) << 5 - h3 := load3(src[10:]) << 3 - h4 := load3(src[13:]) << 2 - h5 := load4(src[16:]) - h6 := load3(src[20:]) << 7 - h7 := load3(src[23:]) << 5 - h8 := load3(src[26:]) << 4 - h9 := (load3(src[29:]) & 0x7fffff) << 2 - - var carry [10]int64 - carry[9] = (h9 + 1<<24) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - carry[1] = (h1 + 1<<24) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[3] = (h3 + 1<<24) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[5] = (h5 + 1<<24) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - carry[7] = (h7 + 1<<24) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[0] = (h0 + 1<<25) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[2] = (h2 + 1<<25) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[4] = (h4 + 1<<25) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[6] = (h6 + 1<<25) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - carry[8] = (h8 + 1<<25) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - dst[0] = int32(h0) - dst[1] = int32(h1) - dst[2] = int32(h2) - dst[3] = int32(h3) - dst[4] = int32(h4) - dst[5] = int32(h5) - dst[6] = int32(h6) - dst[7] = int32(h7) - dst[8] = int32(h8) - dst[9] = int32(h9) -} - -// feToBytes marshals h to s. -// Preconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Write p=2^255-19; q=floor(h/p). -// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). -// -// Proof: -// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. -// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. -// -// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). -// Then 0> 25 - q = (h[0] + q) >> 26 - q = (h[1] + q) >> 25 - q = (h[2] + q) >> 26 - q = (h[3] + q) >> 25 - q = (h[4] + q) >> 26 - q = (h[5] + q) >> 25 - q = (h[6] + q) >> 26 - q = (h[7] + q) >> 25 - q = (h[8] + q) >> 26 - q = (h[9] + q) >> 25 - - // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. - h[0] += 19 * q - // Goal: Output h-2^255 q, which is between 0 and 2^255-20. - - carry[0] = h[0] >> 26 - h[1] += carry[0] - h[0] -= carry[0] << 26 - carry[1] = h[1] >> 25 - h[2] += carry[1] - h[1] -= carry[1] << 25 - carry[2] = h[2] >> 26 - h[3] += carry[2] - h[2] -= carry[2] << 26 - carry[3] = h[3] >> 25 - h[4] += carry[3] - h[3] -= carry[3] << 25 - carry[4] = h[4] >> 26 - h[5] += carry[4] - h[4] -= carry[4] << 26 - carry[5] = h[5] >> 25 - h[6] += carry[5] - h[5] -= carry[5] << 25 - carry[6] = h[6] >> 26 - h[7] += carry[6] - h[6] -= carry[6] << 26 - carry[7] = h[7] >> 25 - h[8] += carry[7] - h[7] -= carry[7] << 25 - carry[8] = h[8] >> 26 - h[9] += carry[8] - h[8] -= carry[8] << 26 - carry[9] = h[9] >> 25 - h[9] -= carry[9] << 25 - // h10 = carry9 - - // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. - // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; - // evidently 2^255 h10-2^255 q = 0. - // Goal: Output h[0]+...+2^230 h[9]. - - s[0] = byte(h[0] >> 0) - s[1] = byte(h[0] >> 8) - s[2] = byte(h[0] >> 16) - s[3] = byte((h[0] >> 24) | (h[1] << 2)) - s[4] = byte(h[1] >> 6) - s[5] = byte(h[1] >> 14) - s[6] = byte((h[1] >> 22) | (h[2] << 3)) - s[7] = byte(h[2] >> 5) - s[8] = byte(h[2] >> 13) - s[9] = byte((h[2] >> 21) | (h[3] << 5)) - s[10] = byte(h[3] >> 3) - s[11] = byte(h[3] >> 11) - s[12] = byte((h[3] >> 19) | (h[4] << 6)) - s[13] = byte(h[4] >> 2) - s[14] = byte(h[4] >> 10) - s[15] = byte(h[4] >> 18) - s[16] = byte(h[5] >> 0) - s[17] = byte(h[5] >> 8) - s[18] = byte(h[5] >> 16) - s[19] = byte((h[5] >> 24) | (h[6] << 1)) - s[20] = byte(h[6] >> 7) - s[21] = byte(h[6] >> 15) - s[22] = byte((h[6] >> 23) | (h[7] << 3)) - s[23] = byte(h[7] >> 5) - s[24] = byte(h[7] >> 13) - s[25] = byte((h[7] >> 21) | (h[8] << 4)) - s[26] = byte(h[8] >> 4) - s[27] = byte(h[8] >> 12) - s[28] = byte((h[8] >> 20) | (h[9] << 6)) - s[29] = byte(h[9] >> 2) - s[30] = byte(h[9] >> 10) - s[31] = byte(h[9] >> 18) -} - -// feMul calculates h = f * g -// Can overlap h with f or g. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Notes on implementation strategy: -// -// Using schoolbook multiplication. -// Karatsuba would save a little in some cost models. -// -// Most multiplications by 2 and 19 are 32-bit precomputations; -// cheaper than 64-bit postcomputations. -// -// There is one remaining multiplication by 19 in the carry chain; -// one *19 precomputation can be merged into this, -// but the resulting data flow is considerably less clean. -// -// There are 12 carries below. -// 10 of them are 2-way parallelizable and vectorizable. -// Can get away with 11 carries, but then data flow is much deeper. -// -// With tighter constraints on inputs can squeeze carries into int32. -func feMul(h, f, g *fieldElement) { - f0 := f[0] - f1 := f[1] - f2 := f[2] - f3 := f[3] - f4 := f[4] - f5 := f[5] - f6 := f[6] - f7 := f[7] - f8 := f[8] - f9 := f[9] - g0 := g[0] - g1 := g[1] - g2 := g[2] - g3 := g[3] - g4 := g[4] - g5 := g[5] - g6 := g[6] - g7 := g[7] - g8 := g[8] - g9 := g[9] - g1_19 := 19 * g1 // 1.4*2^29 - g2_19 := 19 * g2 // 1.4*2^30; still ok - g3_19 := 19 * g3 - g4_19 := 19 * g4 - g5_19 := 19 * g5 - g6_19 := 19 * g6 - g7_19 := 19 * g7 - g8_19 := 19 * g8 - g9_19 := 19 * g9 - f1_2 := 2 * f1 - f3_2 := 2 * f3 - f5_2 := 2 * f5 - f7_2 := 2 * f7 - f9_2 := 2 * f9 - f0g0 := int64(f0) * int64(g0) - f0g1 := int64(f0) * int64(g1) - f0g2 := int64(f0) * int64(g2) - f0g3 := int64(f0) * int64(g3) - f0g4 := int64(f0) * int64(g4) - f0g5 := int64(f0) * int64(g5) - f0g6 := int64(f0) * int64(g6) - f0g7 := int64(f0) * int64(g7) - f0g8 := int64(f0) * int64(g8) - f0g9 := int64(f0) * int64(g9) - f1g0 := int64(f1) * int64(g0) - f1g1_2 := int64(f1_2) * int64(g1) - f1g2 := int64(f1) * int64(g2) - f1g3_2 := int64(f1_2) * int64(g3) - f1g4 := int64(f1) * int64(g4) - f1g5_2 := int64(f1_2) * int64(g5) - f1g6 := int64(f1) * int64(g6) - f1g7_2 := int64(f1_2) * int64(g7) - f1g8 := int64(f1) * int64(g8) - f1g9_38 := int64(f1_2) * int64(g9_19) - f2g0 := int64(f2) * int64(g0) - f2g1 := int64(f2) * int64(g1) - f2g2 := int64(f2) * int64(g2) - f2g3 := int64(f2) * int64(g3) - f2g4 := int64(f2) * int64(g4) - f2g5 := int64(f2) * int64(g5) - f2g6 := int64(f2) * int64(g6) - f2g7 := int64(f2) * int64(g7) - f2g8_19 := int64(f2) * int64(g8_19) - f2g9_19 := int64(f2) * int64(g9_19) - f3g0 := int64(f3) * int64(g0) - f3g1_2 := int64(f3_2) * int64(g1) - f3g2 := int64(f3) * int64(g2) - f3g3_2 := int64(f3_2) * int64(g3) - f3g4 := int64(f3) * int64(g4) - f3g5_2 := int64(f3_2) * int64(g5) - f3g6 := int64(f3) * int64(g6) - f3g7_38 := int64(f3_2) * int64(g7_19) - f3g8_19 := int64(f3) * int64(g8_19) - f3g9_38 := int64(f3_2) * int64(g9_19) - f4g0 := int64(f4) * int64(g0) - f4g1 := int64(f4) * int64(g1) - f4g2 := int64(f4) * int64(g2) - f4g3 := int64(f4) * int64(g3) - f4g4 := int64(f4) * int64(g4) - f4g5 := int64(f4) * int64(g5) - f4g6_19 := int64(f4) * int64(g6_19) - f4g7_19 := int64(f4) * int64(g7_19) - f4g8_19 := int64(f4) * int64(g8_19) - f4g9_19 := int64(f4) * int64(g9_19) - f5g0 := int64(f5) * int64(g0) - f5g1_2 := int64(f5_2) * int64(g1) - f5g2 := int64(f5) * int64(g2) - f5g3_2 := int64(f5_2) * int64(g3) - f5g4 := int64(f5) * int64(g4) - f5g5_38 := int64(f5_2) * int64(g5_19) - f5g6_19 := int64(f5) * int64(g6_19) - f5g7_38 := int64(f5_2) * int64(g7_19) - f5g8_19 := int64(f5) * int64(g8_19) - f5g9_38 := int64(f5_2) * int64(g9_19) - f6g0 := int64(f6) * int64(g0) - f6g1 := int64(f6) * int64(g1) - f6g2 := int64(f6) * int64(g2) - f6g3 := int64(f6) * int64(g3) - f6g4_19 := int64(f6) * int64(g4_19) - f6g5_19 := int64(f6) * int64(g5_19) - f6g6_19 := int64(f6) * int64(g6_19) - f6g7_19 := int64(f6) * int64(g7_19) - f6g8_19 := int64(f6) * int64(g8_19) - f6g9_19 := int64(f6) * int64(g9_19) - f7g0 := int64(f7) * int64(g0) - f7g1_2 := int64(f7_2) * int64(g1) - f7g2 := int64(f7) * int64(g2) - f7g3_38 := int64(f7_2) * int64(g3_19) - f7g4_19 := int64(f7) * int64(g4_19) - f7g5_38 := int64(f7_2) * int64(g5_19) - f7g6_19 := int64(f7) * int64(g6_19) - f7g7_38 := int64(f7_2) * int64(g7_19) - f7g8_19 := int64(f7) * int64(g8_19) - f7g9_38 := int64(f7_2) * int64(g9_19) - f8g0 := int64(f8) * int64(g0) - f8g1 := int64(f8) * int64(g1) - f8g2_19 := int64(f8) * int64(g2_19) - f8g3_19 := int64(f8) * int64(g3_19) - f8g4_19 := int64(f8) * int64(g4_19) - f8g5_19 := int64(f8) * int64(g5_19) - f8g6_19 := int64(f8) * int64(g6_19) - f8g7_19 := int64(f8) * int64(g7_19) - f8g8_19 := int64(f8) * int64(g8_19) - f8g9_19 := int64(f8) * int64(g9_19) - f9g0 := int64(f9) * int64(g0) - f9g1_38 := int64(f9_2) * int64(g1_19) - f9g2_19 := int64(f9) * int64(g2_19) - f9g3_38 := int64(f9_2) * int64(g3_19) - f9g4_19 := int64(f9) * int64(g4_19) - f9g5_38 := int64(f9_2) * int64(g5_19) - f9g6_19 := int64(f9) * int64(g6_19) - f9g7_38 := int64(f9_2) * int64(g7_19) - f9g8_19 := int64(f9) * int64(g8_19) - f9g9_38 := int64(f9_2) * int64(g9_19) - h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 - h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 - h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 - h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 - h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 - h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 - h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 - h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 - h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 - h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 - var carry [10]int64 - - // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) - // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 - // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) - // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - // |h0| <= 2^25 - // |h4| <= 2^25 - // |h1| <= 1.51*2^58 - // |h5| <= 1.51*2^58 - - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - // |h1| <= 2^24; from now on fits into int32 - // |h5| <= 2^24; from now on fits into int32 - // |h2| <= 1.21*2^59 - // |h6| <= 1.21*2^59 - - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - // |h2| <= 2^25; from now on fits into int32 unchanged - // |h6| <= 2^25; from now on fits into int32 unchanged - // |h3| <= 1.51*2^58 - // |h7| <= 1.51*2^58 - - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - // |h3| <= 2^24; from now on fits into int32 unchanged - // |h7| <= 2^24; from now on fits into int32 unchanged - // |h4| <= 1.52*2^33 - // |h8| <= 1.52*2^33 - - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - // |h4| <= 2^25; from now on fits into int32 unchanged - // |h8| <= 2^25; from now on fits into int32 unchanged - // |h5| <= 1.01*2^24 - // |h9| <= 1.51*2^58 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - // |h9| <= 2^24; from now on fits into int32 unchanged - // |h0| <= 1.8*2^37 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - // |h0| <= 2^25; from now on fits into int32 unchanged - // |h1| <= 1.01*2^24 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feSquare calculates h = f*f. Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func feSquare(h, f *fieldElement) { - f0 := f[0] - f1 := f[1] - f2 := f[2] - f3 := f[3] - f4 := f[4] - f5 := f[5] - f6 := f[6] - f7 := f[7] - f8 := f[8] - f9 := f[9] - f0_2 := 2 * f0 - f1_2 := 2 * f1 - f2_2 := 2 * f2 - f3_2 := 2 * f3 - f4_2 := 2 * f4 - f5_2 := 2 * f5 - f6_2 := 2 * f6 - f7_2 := 2 * f7 - f5_38 := 38 * f5 // 1.31*2^30 - f6_19 := 19 * f6 // 1.31*2^30 - f7_38 := 38 * f7 // 1.31*2^30 - f8_19 := 19 * f8 // 1.31*2^30 - f9_38 := 38 * f9 // 1.31*2^30 - f0f0 := int64(f0) * int64(f0) - f0f1_2 := int64(f0_2) * int64(f1) - f0f2_2 := int64(f0_2) * int64(f2) - f0f3_2 := int64(f0_2) * int64(f3) - f0f4_2 := int64(f0_2) * int64(f4) - f0f5_2 := int64(f0_2) * int64(f5) - f0f6_2 := int64(f0_2) * int64(f6) - f0f7_2 := int64(f0_2) * int64(f7) - f0f8_2 := int64(f0_2) * int64(f8) - f0f9_2 := int64(f0_2) * int64(f9) - f1f1_2 := int64(f1_2) * int64(f1) - f1f2_2 := int64(f1_2) * int64(f2) - f1f3_4 := int64(f1_2) * int64(f3_2) - f1f4_2 := int64(f1_2) * int64(f4) - f1f5_4 := int64(f1_2) * int64(f5_2) - f1f6_2 := int64(f1_2) * int64(f6) - f1f7_4 := int64(f1_2) * int64(f7_2) - f1f8_2 := int64(f1_2) * int64(f8) - f1f9_76 := int64(f1_2) * int64(f9_38) - f2f2 := int64(f2) * int64(f2) - f2f3_2 := int64(f2_2) * int64(f3) - f2f4_2 := int64(f2_2) * int64(f4) - f2f5_2 := int64(f2_2) * int64(f5) - f2f6_2 := int64(f2_2) * int64(f6) - f2f7_2 := int64(f2_2) * int64(f7) - f2f8_38 := int64(f2_2) * int64(f8_19) - f2f9_38 := int64(f2) * int64(f9_38) - f3f3_2 := int64(f3_2) * int64(f3) - f3f4_2 := int64(f3_2) * int64(f4) - f3f5_4 := int64(f3_2) * int64(f5_2) - f3f6_2 := int64(f3_2) * int64(f6) - f3f7_76 := int64(f3_2) * int64(f7_38) - f3f8_38 := int64(f3_2) * int64(f8_19) - f3f9_76 := int64(f3_2) * int64(f9_38) - f4f4 := int64(f4) * int64(f4) - f4f5_2 := int64(f4_2) * int64(f5) - f4f6_38 := int64(f4_2) * int64(f6_19) - f4f7_38 := int64(f4) * int64(f7_38) - f4f8_38 := int64(f4_2) * int64(f8_19) - f4f9_38 := int64(f4) * int64(f9_38) - f5f5_38 := int64(f5) * int64(f5_38) - f5f6_38 := int64(f5_2) * int64(f6_19) - f5f7_76 := int64(f5_2) * int64(f7_38) - f5f8_38 := int64(f5_2) * int64(f8_19) - f5f9_76 := int64(f5_2) * int64(f9_38) - f6f6_19 := int64(f6) * int64(f6_19) - f6f7_38 := int64(f6) * int64(f7_38) - f6f8_38 := int64(f6_2) * int64(f8_19) - f6f9_38 := int64(f6) * int64(f9_38) - f7f7_38 := int64(f7) * int64(f7_38) - f7f8_38 := int64(f7_2) * int64(f8_19) - f7f9_76 := int64(f7_2) * int64(f9_38) - f8f8_19 := int64(f8) * int64(f8_19) - f8f9_38 := int64(f8) * int64(f9_38) - f9f9_38 := int64(f9) * int64(f9_38) - h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 - h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 - h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 - h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 - h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 - h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 - h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 - h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 - h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 - h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 - var carry [10]int64 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feMul121666 calculates h = f * 121666. Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func feMul121666(h, f *fieldElement) { - h0 := int64(f[0]) * 121666 - h1 := int64(f[1]) * 121666 - h2 := int64(f[2]) * 121666 - h3 := int64(f[3]) * 121666 - h4 := int64(f[4]) * 121666 - h5 := int64(f[5]) * 121666 - h6 := int64(f[6]) * 121666 - h7 := int64(f[7]) * 121666 - h8 := int64(f[8]) * 121666 - h9 := int64(f[9]) * 121666 - var carry [10]int64 - - carry[9] = (h9 + (1 << 24)) >> 25 - h0 += carry[9] * 19 - h9 -= carry[9] << 25 - carry[1] = (h1 + (1 << 24)) >> 25 - h2 += carry[1] - h1 -= carry[1] << 25 - carry[3] = (h3 + (1 << 24)) >> 25 - h4 += carry[3] - h3 -= carry[3] << 25 - carry[5] = (h5 + (1 << 24)) >> 25 - h6 += carry[5] - h5 -= carry[5] << 25 - carry[7] = (h7 + (1 << 24)) >> 25 - h8 += carry[7] - h7 -= carry[7] << 25 - - carry[0] = (h0 + (1 << 25)) >> 26 - h1 += carry[0] - h0 -= carry[0] << 26 - carry[2] = (h2 + (1 << 25)) >> 26 - h3 += carry[2] - h2 -= carry[2] << 26 - carry[4] = (h4 + (1 << 25)) >> 26 - h5 += carry[4] - h4 -= carry[4] << 26 - carry[6] = (h6 + (1 << 25)) >> 26 - h7 += carry[6] - h6 -= carry[6] << 26 - carry[8] = (h8 + (1 << 25)) >> 26 - h9 += carry[8] - h8 -= carry[8] << 26 - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// feInvert sets out = z^-1. -func feInvert(out, z *fieldElement) { - var t0, t1, t2, t3 fieldElement - var i int - - feSquare(&t0, z) - for i = 1; i < 1; i++ { - feSquare(&t0, &t0) - } - feSquare(&t1, &t0) - for i = 1; i < 2; i++ { - feSquare(&t1, &t1) - } - feMul(&t1, z, &t1) - feMul(&t0, &t0, &t1) - feSquare(&t2, &t0) - for i = 1; i < 1; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t1, &t2) - feSquare(&t2, &t1) - for i = 1; i < 5; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t2, &t1) - for i = 1; i < 10; i++ { - feSquare(&t2, &t2) - } - feMul(&t2, &t2, &t1) - feSquare(&t3, &t2) - for i = 1; i < 20; i++ { - feSquare(&t3, &t3) - } - feMul(&t2, &t3, &t2) - feSquare(&t2, &t2) - for i = 1; i < 10; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t2, &t1) - for i = 1; i < 50; i++ { - feSquare(&t2, &t2) - } - feMul(&t2, &t2, &t1) - feSquare(&t3, &t2) - for i = 1; i < 100; i++ { - feSquare(&t3, &t3) - } - feMul(&t2, &t3, &t2) - feSquare(&t2, &t2) - for i = 1; i < 50; i++ { - feSquare(&t2, &t2) - } - feMul(&t1, &t2, &t1) - feSquare(&t1, &t1) - for i = 1; i < 5; i++ { - feSquare(&t1, &t1) - } - feMul(out, &t1, &t0) -} - -func scalarMultGeneric(out, in, base *[32]byte) { - var e [32]byte - - copy(e[:], in[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement - feFromBytes(&x1, base) - feOne(&x2) - feCopy(&x3, &x1) - feOne(&z3) - - swap := int32(0) - for pos := 254; pos >= 0; pos-- { - b := e[pos/8] >> uint(pos&7) - b &= 1 - swap ^= int32(b) - feCSwap(&x2, &x3, swap) - feCSwap(&z2, &z3, swap) - swap = int32(b) - - feSub(&tmp0, &x3, &z3) - feSub(&tmp1, &x2, &z2) - feAdd(&x2, &x2, &z2) - feAdd(&z2, &x3, &z3) - feMul(&z3, &tmp0, &x2) - feMul(&z2, &z2, &tmp1) - feSquare(&tmp0, &tmp1) - feSquare(&tmp1, &x2) - feAdd(&x3, &z3, &z2) - feSub(&z2, &z3, &z2) - feMul(&x2, &tmp1, &tmp0) - feSub(&tmp1, &tmp1, &tmp0) - feSquare(&z2, &z2) - feMul121666(&z3, &tmp1) - feSquare(&x3, &x3) - feAdd(&tmp0, &tmp0, &z3) - feMul(&z3, &x1, &z2) - feMul(&z2, &tmp1, &tmp0) - } - - feCSwap(&x2, &x3, swap) - feCSwap(&z2, &z3, swap) - - feInvert(&z2, &z2) - feMul(&x2, &x2, &z2) - feToBytes(out, &x2) -} diff --git a/src/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go b/src/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go deleted file mode 100644 index 259728af7dad7b..00000000000000 --- a/src/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !amd64 || !gc || purego -// +build !amd64 !gc purego - -package curve25519 - -func scalarMult(out, in, base *[32]byte) { - scalarMultGeneric(out, in, base) -} diff --git a/src/vendor/golang.org/x/crypto/poly1305/bits_compat.go b/src/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go similarity index 100% rename from src/vendor/golang.org/x/crypto/poly1305/bits_compat.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go diff --git a/src/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go b/src/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go similarity index 100% rename from src/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go diff --git a/src/vendor/golang.org/x/crypto/poly1305/mac_noasm.go b/src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go similarity index 100% rename from src/vendor/golang.org/x/crypto/poly1305/mac_noasm.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go diff --git a/src/vendor/golang.org/x/crypto/poly1305/poly1305.go b/src/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go similarity index 98% rename from src/vendor/golang.org/x/crypto/poly1305/poly1305.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go index 9d7a6af09feb47..4aaea810a26823 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/poly1305.go +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/poly1305.go @@ -15,7 +15,7 @@ // used with a fixed key in order to generate one-time keys from an nonce. // However, in this package AES isn't used and the one-time key is specified // directly. -package poly1305 // import "golang.org/x/crypto/poly1305" +package poly1305 import "crypto/subtle" diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_amd64.go b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go similarity index 100% rename from src/vendor/golang.org/x/crypto/poly1305/sum_amd64.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_amd64.s b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s similarity index 99% rename from src/vendor/golang.org/x/crypto/poly1305/sum_amd64.s rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s index 2cb03731408c96..1d74f0f88189b1 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/sum_amd64.s +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_generic.go b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go similarity index 99% rename from src/vendor/golang.org/x/crypto/poly1305/sum_generic.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go index c942a65904fa97..e041da5ea3e7d0 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/sum_generic.go +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go @@ -136,7 +136,7 @@ func shiftRightBy2(a uint128) uint128 { // updateGeneric absorbs msg into the state.h accumulator. For each chunk m of // 128 bits of message, it computes // -// h₊ = (h + m) * r mod 2¹³⁰ - 5 +// h₊ = (h + m) * r mod 2¹³⁰ - 5 // // If the msg length is not a multiple of TagSize, it assumes the last // incomplete chunk is the final one. @@ -278,8 +278,7 @@ const ( // finalize completes the modular reduction of h and computes // -// out = h + s mod 2¹²⁸ -// +// out = h + s mod 2¹²⁸ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { h0, h1, h2 := h[0], h[1], h[2] diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go similarity index 100% rename from src/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s similarity index 99% rename from src/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s index 3cede539dc40d7..58422aad230570 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_s390x.go b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go similarity index 99% rename from src/vendor/golang.org/x/crypto/poly1305/sum_s390x.go rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go index 62cc9f84709e30..ec95966889691d 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/sum_s390x.go +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go @@ -14,6 +14,7 @@ import ( // updateVX is an assembly implementation of Poly1305 that uses vector // instructions. It must only be called if the vector facility (vx) is // available. +// //go:noescape func updateVX(state *macState, msg []byte) diff --git a/src/vendor/golang.org/x/crypto/poly1305/sum_s390x.s b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s similarity index 99% rename from src/vendor/golang.org/x/crypto/poly1305/sum_s390x.s rename to src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s index bdd882c606def8..aa9e0494c909d8 100644 --- a/src/vendor/golang.org/x/crypto/poly1305/sum_s390x.s +++ b/src/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build gc && !purego // +build gc,!purego #include "textflag.h" @@ -17,7 +18,7 @@ // value. These limbs are, for the most part, zero extended and // placed into 64-bit vector register elements. Each vector // register is 128-bits wide and so holds 2 of these elements. -// Using 26-bit limbs allows us plenty of headroom to accomodate +// Using 26-bit limbs allows us plenty of headroom to accommodate // accumulations before and after multiplication without // overflowing either 32-bits (before multiplication) or 64-bits // (after multiplication). diff --git a/src/vendor/golang.org/x/net/AUTHORS b/src/vendor/golang.org/x/net/AUTHORS deleted file mode 100644 index 15167cd746c560..00000000000000 --- a/src/vendor/golang.org/x/net/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/src/vendor/golang.org/x/net/CONTRIBUTORS b/src/vendor/golang.org/x/net/CONTRIBUTORS deleted file mode 100644 index 1c4577e9680611..00000000000000 --- a/src/vendor/golang.org/x/net/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/src/vendor/golang.org/x/net/dns/dnsmessage/message.go b/src/vendor/golang.org/x/net/dns/dnsmessage/message.go index 1736fc5d12e2f9..0935878292f7c8 100644 --- a/src/vendor/golang.org/x/net/dns/dnsmessage/message.go +++ b/src/vendor/golang.org/x/net/dns/dnsmessage/message.go @@ -125,14 +125,14 @@ func (o OpCode) GoString() string { // An RCode is a DNS response status code. type RCode uint16 +// Header.RCode values. const ( - // Message.Rcode - RCodeSuccess RCode = 0 - RCodeFormatError RCode = 1 - RCodeServerFailure RCode = 2 - RCodeNameError RCode = 3 - RCodeNotImplemented RCode = 4 - RCodeRefused RCode = 5 + RCodeSuccess RCode = 0 // NoError + RCodeFormatError RCode = 1 // FormErr + RCodeServerFailure RCode = 2 // ServFail + RCodeNameError RCode = 3 // NXDomain + RCodeNotImplemented RCode = 4 // NotImp + RCodeRefused RCode = 5 // Refused ) var rCodeNames = map[RCode]string{ @@ -317,6 +317,8 @@ type Header struct { Truncated bool RecursionDesired bool RecursionAvailable bool + AuthenticData bool + CheckingDisabled bool RCode RCode } @@ -338,6 +340,12 @@ func (m *Header) pack() (id uint16, bits uint16) { if m.Response { bits |= headerBitQR } + if m.AuthenticData { + bits |= headerBitAD + } + if m.CheckingDisabled { + bits |= headerBitCD + } return } @@ -379,6 +387,8 @@ const ( headerBitTC = 1 << 9 // truncated headerBitRD = 1 << 8 // recursion desired headerBitRA = 1 << 7 // recursion available + headerBitAD = 1 << 5 // authentic data + headerBitCD = 1 << 4 // checking disabled ) var sectionNames = map[section]string{ @@ -456,6 +466,8 @@ func (h *header) header() Header { Truncated: (h.bits & headerBitTC) != 0, RecursionDesired: (h.bits & headerBitRD) != 0, RecursionAvailable: (h.bits & headerBitRA) != 0, + AuthenticData: (h.bits & headerBitAD) != 0, + CheckingDisabled: (h.bits & headerBitCD) != 0, RCode: RCode(h.bits & 0xF), } } @@ -1173,6 +1185,7 @@ func (m *Message) GoString() string { // A Builder allows incrementally packing a DNS message. // // Example usage: +// // buf := make([]byte, 2, 514) // b := NewBuilder(buf, Header{...}) // b.EnableCompression() @@ -1207,8 +1220,8 @@ type Builder struct { // // The DNS message is appended to the provided initial buffer buf (which may be // nil) as it is built. The final message is returned by the (*Builder).Finish -// method, which may return the same underlying array if there was sufficient -// capacity in the slice. +// method, which includes buf[:len(buf)] and may return the same underlying +// array if there was sufficient capacity in the slice. func NewBuilder(buf []byte, h Header) Builder { if buf == nil { buf = make([]byte, 0, packStartingCap) @@ -1713,7 +1726,7 @@ const ( // SetEDNS0 configures h for EDNS(0). // -// The provided extRCode must be an extedned RCode. +// The provided extRCode must be an extended RCode. func (h *ResourceHeader) SetEDNS0(udpPayloadLen int, extRCode RCode, dnssecOK bool) error { h.Name = Name{Data: [nameLen]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2 h.Type = TypeOPT @@ -1880,7 +1893,7 @@ const nameLen = 255 // A Name is a non-encoded domain name. It is used instead of strings to avoid // allocations. type Name struct { - Data [nameLen]byte + Data [nameLen]byte // 255 bytes Length uint8 } diff --git a/src/vendor/golang.org/x/net/http/httpguts/httplex.go b/src/vendor/golang.org/x/net/http/httpguts/httplex.go index c79aa73f28bb9a..6e071e8524328e 100644 --- a/src/vendor/golang.org/x/net/http/httpguts/httplex.go +++ b/src/vendor/golang.org/x/net/http/httpguts/httplex.go @@ -173,13 +173,15 @@ func tokenEqual(t1, t2 string) bool { // isLWS reports whether b is linear white space, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 -// LWS = [CRLF] 1*( SP | HT ) +// +// LWS = [CRLF] 1*( SP | HT ) func isLWS(b byte) bool { return b == ' ' || b == '\t' } // isCTL reports whether b is a control byte, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 -// CTL = +// +// CTL = func isCTL(b byte) bool { const del = 0x7f // a CTL return b < ' ' || b == del @@ -189,12 +191,13 @@ func isCTL(b byte) bool { // HTTP/2 imposes the additional restriction that uppercase ASCII // letters are not allowed. // -// RFC 7230 says: -// header-field = field-name ":" OWS field-value OWS -// field-name = token -// token = 1*tchar -// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / -// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// RFC 7230 says: +// +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// token = 1*tchar +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA func ValidHeaderFieldName(v string) bool { if len(v) == 0 { return false @@ -267,27 +270,28 @@ var validHostByte = [256]bool{ // ValidHeaderFieldValue reports whether v is a valid "field-value" according to // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : // -// message-header = field-name ":" [ field-value ] -// field-value = *( field-content | LWS ) -// field-content = +// message-header = field-name ":" [ field-value ] +// field-value = *( field-content | LWS ) +// field-content = // // http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : // -// TEXT = -// LWS = [CRLF] 1*( SP | HT ) -// CTL = +// TEXT = +// LWS = [CRLF] 1*( SP | HT ) +// CTL = // // 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" +// +// 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 diff --git a/src/vendor/golang.org/x/net/http/httpproxy/proxy.go b/src/vendor/golang.org/x/net/http/httpproxy/proxy.go index 1415b077912fac..16994ac1347b05 100644 --- a/src/vendor/golang.org/x/net/http/httpproxy/proxy.go +++ b/src/vendor/golang.org/x/net/http/httpproxy/proxy.go @@ -113,8 +113,8 @@ func getEnvAny(names ...string) string { // environment, or a proxy should not be used for the given request, as // defined by NO_PROXY. // -// As a special case, if req.URL.Host is "localhost" (with or without a -// port number), then a nil URL and nil error will be returned. +// As a special case, if req.URL.Host is "localhost" or a loopback address +// (with or without a port number), then a nil URL and nil error will be returned. func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) { // Preprocess the Config settings for more efficient evaluation. cfg1 := &config{ @@ -267,6 +267,9 @@ func (c *config) init() { matchHost = true phost = "." + phost } + if v, err := idnaASCII(phost); err == nil { + phost = v + } c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost}) } } diff --git a/src/vendor/golang.org/x/net/http2/hpack/encode.go b/src/vendor/golang.org/x/net/http2/hpack/encode.go index 97f17831fc55b8..6886dc163cba59 100644 --- a/src/vendor/golang.org/x/net/http2/hpack/encode.go +++ b/src/vendor/golang.org/x/net/http2/hpack/encode.go @@ -191,7 +191,7 @@ func appendTableSize(dst []byte, v uint32) []byte { // bit prefix, to dst and returns the extended buffer. // // See -// http://http2.github.io/http2-spec/compression.html#integer.representation +// https://httpwg.org/specs/rfc7541.html#integer.representation func appendVarInt(dst []byte, n byte, i uint64) []byte { k := uint64((1 << n) - 1) if i < k { diff --git a/src/vendor/golang.org/x/net/http2/hpack/hpack.go b/src/vendor/golang.org/x/net/http2/hpack/hpack.go index 85f18a2b0a8616..ebdfbee964ae38 100644 --- a/src/vendor/golang.org/x/net/http2/hpack/hpack.go +++ b/src/vendor/golang.org/x/net/http2/hpack/hpack.go @@ -59,7 +59,7 @@ func (hf HeaderField) String() string { // Size returns the size of an entry per RFC 7541 section 4.1. func (hf HeaderField) Size() uint32 { - // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.4.1 // "The size of the dynamic table is the sum of the size of // its entries. The size of an entry is the sum of its name's // length in octets (as defined in Section 5.2), its value's @@ -158,7 +158,7 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) { } type dynamicTable struct { - // http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2 + // https://httpwg.org/specs/rfc7541.html#rfc.section.2.3.2 table headerFieldTable size uint32 // in bytes maxSize uint32 // current maxSize @@ -307,27 +307,27 @@ func (d *Decoder) parseHeaderFieldRepr() error { case b&128 != 0: // Indexed representation. // High bit set? - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.1 return d.parseFieldIndexed() case b&192 == 64: // 6.2.1 Literal Header Field with Incremental Indexing // 0b10xxxxxx: top two bits are 10 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1 return d.parseFieldLiteral(6, indexedTrue) case b&240 == 0: // 6.2.2 Literal Header Field without Indexing // 0b0000xxxx: top four bits are 0000 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2 return d.parseFieldLiteral(4, indexedFalse) case b&240 == 16: // 6.2.3 Literal Header Field never Indexed // 0b0001xxxx: top four bits are 0001 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3 return d.parseFieldLiteral(4, indexedNever) case b&224 == 32: // 6.3 Dynamic Table Size Update // Top three bits are '001'. - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.3 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.3 return d.parseDynamicTableSizeUpdate() } @@ -420,7 +420,7 @@ var errVarintOverflow = DecodingError{errors.New("varint integer overflow")} // readVarInt reads an unsigned variable length integer off the // beginning of p. n is the parameter as described in -// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1. +// https://httpwg.org/specs/rfc7541.html#rfc.section.5.1. // // n must always be between 1 and 8. // 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 a1ab2f05679480..20d083a716da1b 100644 --- a/src/vendor/golang.org/x/net/http2/hpack/huffman.go +++ b/src/vendor/golang.org/x/net/http2/hpack/huffman.go @@ -140,50 +140,79 @@ func buildRootHuffmanNode() { panic("unexpected size") } lazyRootHuffmanNode = newInternalNode() - for i, code := range huffmanCodes { - addDecoderNode(byte(i), code, huffmanCodeLen[i]) - } -} + // allocate a leaf node for each of the 256 symbols + leaves := new([256]node) + + for sym, code := range huffmanCodes { + codeLen := huffmanCodeLen[sym] + + cur := lazyRootHuffmanNode + for codeLen > 8 { + codeLen -= 8 + i := uint8(code >> codeLen) + if cur.children[i] == nil { + cur.children[i] = newInternalNode() + } + cur = cur.children[i] + } + shift := 8 - codeLen + start, end := int(uint8(code< 8 { - codeLen -= 8 - i := uint8(code >> codeLen) - if cur.children[i] == nil { - cur.children[i] = newInternalNode() + leaves[sym].sym = byte(sym) + leaves[sym].codeLen = codeLen + for i := start; i < start+end; i++ { + cur.children[i] = &leaves[sym] } - cur = cur.children[i] - } - shift := 8 - codeLen - start, end := int(uint8(code<= 32 { + n %= 32 // Normally would be -= 32 but %= 32 informs compiler 0 <= n <= 31 for upcoming shift + y := uint32(x >> n) // Compiler doesn't combine memory writes if y isn't uint32 + dst = append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } - dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i]) } - - if rembits < 8 { - // special EOS symbol - code := uint32(0x3fffffff) - nbits := uint8(30) - - t := uint8(code >> (nbits - rembits)) - dst[len(dst)-1] |= t + // Add padding bits if necessary + if over := n % 8; over > 0 { + const ( + eosCode = 0x3fffffff + eosNBits = 30 + eosPadByte = eosCode >> (eosNBits - 8) + ) + pad := 8 - over + x = (x << pad) | (eosPadByte >> over) + n += pad // 8 now divides into n exactly } - - return dst + // n in (0, 8, 16, 24, 32) + switch n / 8 { + case 0: + return dst + case 1: + return append(dst, byte(x)) + case 2: + y := uint16(x) + return append(dst, byte(y>>8), byte(y)) + case 3: + y := uint16(x >> 8) + return append(dst, byte(y>>8), byte(y), byte(x)) + } + // case 4: + y := uint32(x) + return append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } // HuffmanEncodeLength returns the number of bytes required to encode @@ -195,35 +224,3 @@ func HuffmanEncodeLength(s string) uint64 { } return (n + 7) / 8 } - -// appendByteToHuffmanCode appends Huffman code for c to dst and -// returns the extended buffer and the remaining bits in the last -// element. The appending is not byte aligned and the remaining bits -// in the last element of dst is given in rembits. -func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) { - code := huffmanCodes[c] - nbits := huffmanCodeLen[c] - - for { - if rembits > nbits { - t := uint8(code << (rembits - nbits)) - dst[len(dst)-1] |= t - rembits -= nbits - break - } - - t := uint8(code >> (nbits - rembits)) - dst[len(dst)-1] |= t - - nbits -= rembits - rembits = 8 - - if nbits == 0 { - break - } - - dst = append(dst, 0) - } - - return dst, rembits -} diff --git a/src/vendor/golang.org/x/net/idna/go118.go b/src/vendor/golang.org/x/net/idna/go118.go new file mode 100644 index 00000000000000..c5c4338dbed47e --- /dev/null +++ b/src/vendor/golang.org/x/net/idna/go118.go @@ -0,0 +1,14 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package idna + +// Transitional processing is disabled by default in Go 1.18. +// https://golang.org/issue/47510 +const transitionalLookup = false diff --git a/src/vendor/golang.org/x/net/idna/idna10.0.0.go b/src/vendor/golang.org/x/net/idna/idna10.0.0.go index 5208ba6cb88424..64ccf85febb661 100644 --- a/src/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/src/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -59,10 +59,10 @@ type Option func(*options) // Transitional sets a Profile to use the Transitional mapping as defined in UTS // #46. This will cause, for example, "ß" to be mapped to "ss". Using the // transitional mapping provides a compromise between IDNA2003 and IDNA2008 -// compatibility. It is used by most browsers when resolving domain names. This +// compatibility. It is used by some browsers when resolving domain names. This // option is only meaningful if combined with MapForLookup. func Transitional(transitional bool) Option { - return func(o *options) { o.transitional = true } + return func(o *options) { o.transitional = transitional } } // VerifyDNSLength sets whether a Profile should fail if any of the IDN parts @@ -284,7 +284,7 @@ var ( punycode = &Profile{} lookup = &Profile{options{ - transitional: true, + transitional: transitionalLookup, useSTD3Rules: true, checkHyphens: true, checkJoiners: true, diff --git a/src/vendor/golang.org/x/net/idna/idna9.0.0.go b/src/vendor/golang.org/x/net/idna/idna9.0.0.go index 55f718f1274471..aae6aac872b31d 100644 --- a/src/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/src/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -58,10 +58,10 @@ type Option func(*options) // Transitional sets a Profile to use the Transitional mapping as defined in UTS // #46. This will cause, for example, "ß" to be mapped to "ss". Using the // transitional mapping provides a compromise between IDNA2003 and IDNA2008 -// compatibility. It is used by most browsers when resolving domain names. This +// compatibility. It is used by some browsers when resolving domain names. This // option is only meaningful if combined with MapForLookup. func Transitional(transitional bool) Option { - return func(o *options) { o.transitional = true } + return func(o *options) { o.transitional = transitional } } // VerifyDNSLength sets whether a Profile should fail if any of the IDN parts diff --git a/src/vendor/golang.org/x/net/idna/pre_go118.go b/src/vendor/golang.org/x/net/idna/pre_go118.go new file mode 100644 index 00000000000000..3aaccab1c5a0e4 --- /dev/null +++ b/src/vendor/golang.org/x/net/idna/pre_go118.go @@ -0,0 +1,12 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package idna + +const transitionalLookup = true diff --git a/src/vendor/golang.org/x/net/idna/punycode.go b/src/vendor/golang.org/x/net/idna/punycode.go index 02c7d59af3b417..e8e3ac11a94b19 100644 --- a/src/vendor/golang.org/x/net/idna/punycode.go +++ b/src/vendor/golang.org/x/net/idna/punycode.go @@ -49,6 +49,7 @@ func decode(encoded string) (string, error) { } } i, n, bias := int32(0), initialN, initialBias + overflow := false for pos < len(encoded) { oldI, w := i, int32(1) for k := base; ; k += base { @@ -60,29 +61,32 @@ func decode(encoded string) (string, error) { return "", punyError(encoded) } pos++ - i += digit * w - if i < 0 { + i, overflow = madd(i, digit, w) + if overflow { return "", punyError(encoded) } t := k - bias - if t < tmin { + if k <= bias { t = tmin - } else if t > tmax { + } else if k >= bias+tmax { t = tmax } if digit < t { break } - w *= base - t - if w >= math.MaxInt32/base { + w, overflow = madd(0, w, base-t) + if overflow { return "", punyError(encoded) } } + if len(output) >= 1024 { + return "", punyError(encoded) + } x := int32(len(output) + 1) bias = adapt(i-oldI, x, oldI == 0) n += i / x i %= x - if n > utf8.MaxRune || len(output) >= 1024 { + if n < 0 || n > utf8.MaxRune { return "", punyError(encoded) } output = append(output, 0) @@ -115,6 +119,7 @@ func encode(prefix, s string) (string, error) { if b > 0 { output = append(output, '-') } + overflow := false for remaining != 0 { m := int32(0x7fffffff) for _, r := range s { @@ -122,8 +127,8 @@ func encode(prefix, s string) (string, error) { m = r } } - delta += (m - n) * (h + 1) - if delta < 0 { + delta, overflow = madd(delta, m-n, h+1) + if overflow { return "", punyError(s) } n = m @@ -141,9 +146,9 @@ func encode(prefix, s string) (string, error) { q := delta for k := base; ; k += base { t := k - bias - if t < tmin { + if k <= bias { t = tmin - } else if t > tmax { + } else if k >= bias+tmax { t = tmax } if q < t { @@ -164,6 +169,15 @@ func encode(prefix, s string) (string, error) { return string(output), nil } +// madd computes a + (b * c), detecting overflow. +func madd(a, b, c int32) (next int32, overflow bool) { + p := int64(b) * int64(c) + if p > math.MaxInt32-int64(a) { + return 0, true + } + return a + int32(p), false +} + func decodeDigit(x byte) (digit int32, ok bool) { switch { case '0' <= x && x <= '9': diff --git a/src/vendor/golang.org/x/net/idna/trieval.go b/src/vendor/golang.org/x/net/idna/trieval.go index 7a8cf889b5bc74..9c070a44b377ad 100644 --- a/src/vendor/golang.org/x/net/idna/trieval.go +++ b/src/vendor/golang.org/x/net/idna/trieval.go @@ -17,23 +17,23 @@ package idna // // The per-rune values have the following format: // -// if mapped { -// if inlinedXOR { -// 15..13 inline XOR marker -// 12..11 unused -// 10..3 inline XOR mask -// } else { -// 15..3 index into xor or mapping table -// } -// } else { -// 15..14 unused -// 13 mayNeedNorm -// 12..11 attributes -// 10..8 joining type -// 7..3 category type -// } -// 2 use xor pattern -// 1..0 mapped category +// if mapped { +// if inlinedXOR { +// 15..13 inline XOR marker +// 12..11 unused +// 10..3 inline XOR mask +// } else { +// 15..3 index into xor or mapping table +// } +// } else { +// 15..14 unused +// 13 mayNeedNorm +// 12..11 attributes +// 10..8 joining type +// 7..3 category type +// } +// 2 use xor pattern +// 1..0 mapped category // // See the definitions below for a more detailed description of the various // bits. diff --git a/src/vendor/golang.org/x/net/lif/address.go b/src/vendor/golang.org/x/net/lif/address.go index afb957fd8e15a3..8eaddb508dcd98 100644 --- a/src/vendor/golang.org/x/net/lif/address.go +++ b/src/vendor/golang.org/x/net/lif/address.go @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris package lif import ( "errors" + "syscall" "unsafe" ) @@ -24,7 +26,7 @@ type Inet4Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet4Addr) Family() int { return sysAF_INET } +func (a *Inet4Addr) Family() int { return syscall.AF_INET } // An Inet6Addr represents an internet address for IPv6. type Inet6Addr struct { @@ -34,7 +36,7 @@ type Inet6Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet6Addr) Family() int { return sysAF_INET6 } +func (a *Inet6Addr) Family() int { return syscall.AF_INET6 } // Addrs returns a list of interface addresses. // @@ -61,7 +63,7 @@ func Addrs(af int, name string) ([]Addr, error) { lifr.Name[i] = int8(ll.Name[i]) } for _, ep := range eps { - ioc := int64(sysSIOCGLIFADDR) + ioc := int64(syscall.SIOCGLIFADDR) err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifr)) if err != nil { continue @@ -72,11 +74,11 @@ func Addrs(af int, name string) ([]Addr, error) { continue } switch sa.Family { - case sysAF_INET: + case syscall.AF_INET: a := &Inet4Addr{PrefixLen: l} copy(a.IP[:], lifr.Lifru[4:8]) as = append(as, a) - case sysAF_INET6: + case syscall.AF_INET6: a := &Inet6Addr{PrefixLen: l, ZoneID: int(nativeEndian.Uint32(lifr.Lifru[24:28]))} copy(a.IP[:], lifr.Lifru[8:24]) as = append(as, a) diff --git a/src/vendor/golang.org/x/net/lif/binary.go b/src/vendor/golang.org/x/net/lif/binary.go index 738a94f422409d..f31ca3ad0726c9 100644 --- a/src/vendor/golang.org/x/net/lif/binary.go +++ b/src/vendor/golang.org/x/net/lif/binary.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris package lif diff --git a/src/vendor/golang.org/x/net/lif/lif.go b/src/vendor/golang.org/x/net/lif/lif.go index 6e81f81f1c2a85..f1fce48b34c764 100644 --- a/src/vendor/golang.org/x/net/lif/lif.go +++ b/src/vendor/golang.org/x/net/lif/lif.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris // Package lif provides basic functions for the manipulation of @@ -10,7 +11,9 @@ // The package supports Solaris 11 or above. package lif -import "syscall" +import ( + "syscall" +) type endpoint struct { af int @@ -24,12 +27,12 @@ func (ep *endpoint) close() error { func newEndpoints(af int) ([]endpoint, error) { var lastErr error var eps []endpoint - afs := []int{sysAF_INET, sysAF_INET6} - if af != sysAF_UNSPEC { + afs := []int{syscall.AF_INET, syscall.AF_INET6} + if af != syscall.AF_UNSPEC { afs = []int{af} } for _, af := range afs { - s, err := syscall.Socket(af, sysSOCK_DGRAM, 0) + s, err := syscall.Socket(af, syscall.SOCK_DGRAM, 0) if err != nil { lastErr = err continue diff --git a/src/vendor/golang.org/x/net/lif/link.go b/src/vendor/golang.org/x/net/lif/link.go index 913a53e1185fc2..00b78545b517ce 100644 --- a/src/vendor/golang.org/x/net/lif/link.go +++ b/src/vendor/golang.org/x/net/lif/link.go @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris package lif -import "unsafe" +import ( + "syscall" + "unsafe" +) // A Link represents logical data link information. // @@ -29,22 +33,22 @@ func (ll *Link) fetch(s uintptr) { for i := 0; i < len(ll.Name); i++ { lifr.Name[i] = int8(ll.Name[i]) } - ioc := int64(sysSIOCGLIFINDEX) + ioc := int64(syscall.SIOCGLIFINDEX) if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil { ll.Index = int(nativeEndian.Uint32(lifr.Lifru[:4])) } - ioc = int64(sysSIOCGLIFFLAGS) + ioc = int64(syscall.SIOCGLIFFLAGS) if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil { ll.Flags = int(nativeEndian.Uint64(lifr.Lifru[:8])) } - ioc = int64(sysSIOCGLIFMTU) + ioc = int64(syscall.SIOCGLIFMTU) if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil { ll.MTU = int(nativeEndian.Uint32(lifr.Lifru[:4])) } switch ll.Type { - case sysIFT_IPV4, sysIFT_IPV6, sysIFT_6TO4: + case syscall.IFT_IPV4, syscall.IFT_IPV6, syscall.IFT_6TO4: default: - ioc = int64(sysSIOCGLIFHWADDR) + ioc = int64(syscall.SIOCGLIFHWADDR) if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil { ll.Addr, _ = parseLinkAddr(lifr.Lifru[4:]) } @@ -74,7 +78,7 @@ func links(eps []endpoint, name string) ([]Link, error) { lifc := lifconf{Flags: sysLIFC_NOXMIT | sysLIFC_TEMPORARY | sysLIFC_ALLZONES | sysLIFC_UNDER_IPMP} for _, ep := range eps { lifn.Family = uint16(ep.af) - ioc := int64(sysSIOCGLIFNUM) + ioc := int64(syscall.SIOCGLIFNUM) if err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifn)); err != nil { continue } @@ -89,7 +93,7 @@ func links(eps []endpoint, name string) ([]Link, error) { } else { nativeEndian.PutUint32(lifc.Lifcu[:], uint32(uintptr(unsafe.Pointer(&b[0])))) } - ioc = int64(sysSIOCGLIFCONF) + ioc = int64(syscall.SIOCGLIFCONF) if err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifc)); err != nil { continue } diff --git a/src/vendor/golang.org/x/net/lif/sys.go b/src/vendor/golang.org/x/net/lif/sys.go index c896041b7b4578..d0b532d9dc95e0 100644 --- a/src/vendor/golang.org/x/net/lif/sys.go +++ b/src/vendor/golang.org/x/net/lif/sys.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris package lif diff --git a/src/vendor/golang.org/x/net/lif/syscall.go b/src/vendor/golang.org/x/net/lif/syscall.go index aadab2e14bae4b..8d03b4aa92895e 100644 --- a/src/vendor/golang.org/x/net/lif/syscall.go +++ b/src/vendor/golang.org/x/net/lif/syscall.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build solaris // +build solaris package lif diff --git a/src/vendor/golang.org/x/net/lif/zsys_solaris_amd64.go b/src/vendor/golang.org/x/net/lif/zsys_solaris_amd64.go index d7a70d4ed94a54..30651c76b683ad 100644 --- a/src/vendor/golang.org/x/net/lif/zsys_solaris_amd64.go +++ b/src/vendor/golang.org/x/net/lif/zsys_solaris_amd64.go @@ -3,14 +3,6 @@ package lif -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_INET6 = 0x1a - - sysSOCK_DGRAM = 0x1 -) - type sockaddrStorage struct { Family uint16 X_ss_pad1 [6]int8 @@ -25,37 +17,6 @@ const ( sysLIFC_ALLZONES = 0x8 sysLIFC_UNDER_IPMP = 0x10 sysLIFC_ENABLED = 0x20 - - sysSIOCGLIFADDR = -0x3f87968f - sysSIOCGLIFDSTADDR = -0x3f87968d - sysSIOCGLIFFLAGS = -0x3f87968b - sysSIOCGLIFMTU = -0x3f879686 - sysSIOCGLIFNETMASK = -0x3f879683 - sysSIOCGLIFMETRIC = -0x3f879681 - sysSIOCGLIFNUM = -0x3ff3967e - sysSIOCGLIFINDEX = -0x3f87967b - sysSIOCGLIFSUBNET = -0x3f879676 - sysSIOCGLIFLNKINFO = -0x3f879674 - sysSIOCGLIFCONF = -0x3fef965b - sysSIOCGLIFHWADDR = -0x3f879640 -) - -const ( - sysIFF_UP = 0x1 - sysIFF_BROADCAST = 0x2 - sysIFF_DEBUG = 0x4 - sysIFF_LOOPBACK = 0x8 - sysIFF_POINTOPOINT = 0x10 - sysIFF_NOTRAILERS = 0x20 - sysIFF_RUNNING = 0x40 - sysIFF_NOARP = 0x80 - sysIFF_PROMISC = 0x100 - sysIFF_ALLMULTI = 0x200 - sysIFF_INTELLIGENT = 0x400 - sysIFF_MULTICAST = 0x800 - sysIFF_MULTI_BCAST = 0x1000 - sysIFF_UNNUMBERED = 0x2000 - sysIFF_PRIVATE = 0x8000 ) const ( @@ -95,9 +56,3 @@ type lifIfinfoReq struct { Reachretrans uint32 Maxmtu uint32 } - -const ( - sysIFT_IPV4 = 0xc8 - sysIFT_IPV6 = 0xc9 - sysIFT_6TO4 = 0xca -) diff --git a/src/vendor/golang.org/x/net/nettest/conntest.go b/src/vendor/golang.org/x/net/nettest/conntest.go index 39cc6a631ee559..615f4980c580c8 100644 --- a/src/vendor/golang.org/x/net/nettest/conntest.go +++ b/src/vendor/golang.org/x/net/nettest/conntest.go @@ -398,10 +398,14 @@ func checkForTimeoutError(t *testing.T, err error) { t.Helper() if nerr, ok := err.(net.Error); ok { if !nerr.Timeout() { - t.Errorf("err.Timeout() = false, want true") + if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" && t.Name() == "TestTestConn/TCP/RacyRead" { + t.Logf("ignoring known failure mode on windows/arm64; see https://go.dev/issue/52893") + } else { + t.Errorf("got error: %v, want err.Timeout() = true", nerr) + } } } else { - t.Errorf("got %T, want net.Error", err) + t.Errorf("got %T: %v, want net.Error", err, err) } } diff --git a/src/vendor/golang.org/x/net/nettest/nettest.go b/src/vendor/golang.org/x/net/nettest/nettest.go index 83ba858e2494f3..6918f2c36229ed 100644 --- a/src/vendor/golang.org/x/net/nettest/nettest.go +++ b/src/vendor/golang.org/x/net/nettest/nettest.go @@ -95,13 +95,8 @@ func TestableNetwork(network string) bool { // This is an internal network name for testing on the // package net of the standard library. switch runtime.GOOS { - case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": + case "android", "fuchsia", "hurd", "ios", "js", "nacl", "plan9", "windows": return false - case "darwin", "ios": - // iOS doesn't support it. - if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { - return false - } } case "ip", "ip4", "ip6": switch runtime.GOOS { @@ -114,15 +109,10 @@ func TestableNetwork(network string) bool { } case "unix", "unixgram": switch runtime.GOOS { - case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": + case "android", "fuchsia", "hurd", "ios", "js", "nacl", "plan9", "windows": return false case "aix": return unixStrmDgramEnabled() - case "darwin", "ios": - // iOS does not support unix, unixgram. - if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { - return false - } } case "unixpacket": switch runtime.GOOS { @@ -228,7 +218,11 @@ func NewLocalPacketListener(network string) (net.PacketConn, error) { // LocalPath returns a local path that can be used for Unix-domain // protocol testing. func LocalPath() (string, error) { - f, err := ioutil.TempFile("", "go-nettest") + dir := "" + if runtime.GOOS == "darwin" { + dir = "/tmp" + } + f, err := ioutil.TempFile(dir, "go-nettest") if err != nil { return "", err } diff --git a/src/vendor/golang.org/x/net/route/address.go b/src/vendor/golang.org/x/net/route/address.go index 4f6ad968a247e4..5a3cc065493065 100644 --- a/src/vendor/golang.org/x/net/route/address.go +++ b/src/vendor/golang.org/x/net/route/address.go @@ -7,7 +7,10 @@ package route -import "runtime" +import ( + "runtime" + "syscall" +) // An Addr represents an address associated with packet routing. type Addr interface { @@ -23,7 +26,7 @@ type LinkAddr struct { } // Family implements the Family method of Addr interface. -func (a *LinkAddr) Family() int { return sysAF_LINK } +func (a *LinkAddr) Family() int { return syscall.AF_LINK } func (a *LinkAddr) lenAndSpace() (int, int) { l := 8 + len(a.Name) + len(a.Addr) @@ -40,7 +43,7 @@ func (a *LinkAddr) marshal(b []byte) (int, error) { return 0, errInvalidAddr } b[0] = byte(l) - b[1] = sysAF_LINK + b[1] = syscall.AF_LINK if a.Index > 0 { nativeEndian.PutUint16(b[2:4], uint16(a.Index)) } @@ -62,7 +65,7 @@ func parseLinkAddr(b []byte) (Addr, error) { if len(b) < 8 { return nil, errInvalidAddr } - _, a, err := parseKernelLinkAddr(sysAF_LINK, b[4:]) + _, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:]) if err != nil { return nil, err } @@ -122,7 +125,7 @@ type Inet4Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet4Addr) Family() int { return sysAF_INET } +func (a *Inet4Addr) Family() int { return syscall.AF_INET } func (a *Inet4Addr) lenAndSpace() (int, int) { return sizeofSockaddrInet, roundup(sizeofSockaddrInet) @@ -134,7 +137,7 @@ func (a *Inet4Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = sysAF_INET + b[1] = syscall.AF_INET copy(b[4:8], a.IP[:]) return ll, nil } @@ -146,7 +149,7 @@ type Inet6Addr struct { } // Family implements the Family method of Addr interface. -func (a *Inet6Addr) Family() int { return sysAF_INET6 } +func (a *Inet6Addr) Family() int { return syscall.AF_INET6 } func (a *Inet6Addr) lenAndSpace() (int, int) { return sizeofSockaddrInet6, roundup(sizeofSockaddrInet6) @@ -158,7 +161,7 @@ func (a *Inet6Addr) marshal(b []byte) (int, error) { return 0, errShortBuffer } b[0] = byte(l) - b[1] = sysAF_INET6 + b[1] = syscall.AF_INET6 copy(b[8:24], a.IP[:]) if a.ZoneID > 0 { nativeEndian.PutUint32(b[24:28], uint32(a.ZoneID)) @@ -169,14 +172,14 @@ func (a *Inet6Addr) marshal(b []byte) (int, error) { // parseInetAddr parses b as an internet address for IPv4 or IPv6. func parseInetAddr(af int, b []byte) (Addr, error) { switch af { - case sysAF_INET: + case syscall.AF_INET: if len(b) < sizeofSockaddrInet { return nil, errInvalidAddr } a := &Inet4Addr{} copy(a.IP[:], b[4:8]) return a, nil - case sysAF_INET6: + case syscall.AF_INET6: if len(b) < sizeofSockaddrInet6 { return nil, errInvalidAddr } @@ -245,7 +248,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { a := &Inet6Addr{} copy(a.IP[:], b[off6:off6+16]) return int(b[0]), a, nil - case af == sysAF_INET6: + case af == syscall.AF_INET6: a := &Inet6Addr{} if l-1 < off6 { copy(a.IP[:], b[1:l]) @@ -365,15 +368,15 @@ func marshalAddrs(b []byte, as []Addr) (uint, error) { } 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++ { + var as [syscall.RTAX_MAX]Addr + af := int(syscall.AF_UNSPEC) + for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ { if attrs&(1<= 1102000 { // see https://github.com/freebsd/freebsd/commit/027c7f4d66ff8d8c4a46c3665a5ee7d6d8462034#diff-ad4e5b7f1449ea3fc87bc97280de145b - align = wordSize - } } rtm.parse = rtm.parseRouteMessage ifm.parse = ifm.parseInterfaceMessage @@ -144,20 +141,20 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage ifanm.parse = ifanm.parseInterfaceAnnounceMessage return align, map[int]*wireFormat{ - sysRTM_ADD: rtm, - sysRTM_DELETE: rtm, - sysRTM_CHANGE: rtm, - sysRTM_GET: rtm, - sysRTM_LOSING: rtm, - sysRTM_REDIRECT: rtm, - sysRTM_MISS: rtm, - sysRTM_LOCK: rtm, - sysRTM_RESOLVE: rtm, - sysRTM_NEWADDR: ifam, - sysRTM_DELADDR: ifam, - sysRTM_IFINFO: ifm, - sysRTM_NEWMADDR: ifmam, - sysRTM_DELMADDR: ifmam, - sysRTM_IFANNOUNCE: ifanm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + syscall.RTM_IFANNOUNCE: ifanm, } } diff --git a/src/vendor/golang.org/x/net/route/sys_netbsd.go b/src/vendor/golang.org/x/net/route/sys_netbsd.go index 02f71d54bbdfa6..be4460e13f044c 100644 --- a/src/vendor/golang.org/x/net/route/sys_netbsd.go +++ b/src/vendor/golang.org/x/net/route/sys_netbsd.go @@ -4,6 +4,8 @@ package route +import "syscall" + func (typ RIBType) parseable() bool { return true } // RouteMetrics represents route metrics. @@ -54,18 +56,18 @@ func probeRoutingStack() (int, map[int]*wireFormat) { // NetBSD 6 and above kernels require 64-bit aligned access to // routing facilities. return 8, map[int]*wireFormat{ - sysRTM_ADD: rtm, - sysRTM_DELETE: rtm, - sysRTM_CHANGE: rtm, - sysRTM_GET: rtm, - sysRTM_LOSING: rtm, - sysRTM_REDIRECT: rtm, - sysRTM_MISS: rtm, - sysRTM_LOCK: rtm, - sysRTM_RESOLVE: rtm, - sysRTM_NEWADDR: ifam, - sysRTM_DELADDR: ifam, - sysRTM_IFANNOUNCE: ifanm, - sysRTM_IFINFO: ifm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_LOCK: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFANNOUNCE: ifanm, + syscall.RTM_IFINFO: ifm, } } diff --git a/src/vendor/golang.org/x/net/route/sys_openbsd.go b/src/vendor/golang.org/x/net/route/sys_openbsd.go index c5674e83d01350..7f4f93cbea088d 100644 --- a/src/vendor/golang.org/x/net/route/sys_openbsd.go +++ b/src/vendor/golang.org/x/net/route/sys_openbsd.go @@ -4,11 +4,14 @@ package route -import "unsafe" +import ( + "syscall" + "unsafe" +) func (typ RIBType) parseable() bool { switch typ { - case sysNET_RT_STATS, sysNET_RT_TABLE: + case syscall.NET_RT_STATS, syscall.NET_RT_TABLE: return false default: return true @@ -62,19 +65,18 @@ func probeRoutingStack() (int, map[int]*wireFormat) { ifanm := &wireFormat{extOff: -1, bodyOff: -1} ifanm.parse = ifanm.parseInterfaceAnnounceMessage return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - sysRTM_ADD: rtm, - sysRTM_DELETE: rtm, - sysRTM_CHANGE: rtm, - sysRTM_GET: rtm, - sysRTM_LOSING: rtm, - sysRTM_REDIRECT: rtm, - sysRTM_MISS: rtm, - sysRTM_LOCK: rtm, - sysRTM_RESOLVE: rtm, - sysRTM_NEWADDR: ifam, - sysRTM_DELADDR: ifam, - sysRTM_IFINFO: ifm, - sysRTM_IFANNOUNCE: ifanm, - sysRTM_DESYNC: rtm, + syscall.RTM_ADD: rtm, + syscall.RTM_DELETE: rtm, + syscall.RTM_CHANGE: rtm, + syscall.RTM_GET: rtm, + syscall.RTM_LOSING: rtm, + syscall.RTM_REDIRECT: rtm, + syscall.RTM_MISS: rtm, + syscall.RTM_RESOLVE: rtm, + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_IFANNOUNCE: ifanm, + syscall.RTM_DESYNC: rtm, } } diff --git a/src/vendor/golang.org/x/net/route/syscall.go b/src/vendor/golang.org/x/net/route/syscall.go index 97166dd3c4c52e..68d37c9621cba7 100644 --- a/src/vendor/golang.org/x/net/route/syscall.go +++ b/src/vendor/golang.org/x/net/route/syscall.go @@ -2,28 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || netbsd || openbsd -// +build dragonfly freebsd netbsd openbsd +//go:build darwin || dragonfly || freebsd || netbsd || openbsd +// +build darwin dragonfly freebsd netbsd openbsd package route -import ( - "syscall" - "unsafe" -) +import _ "unsafe" // for linkname -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)), newlen) - if errno != 0 { - return error(errno) - } - return nil -} +//go:linkname sysctl syscall.sysctl +func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error diff --git a/src/vendor/golang.org/x/net/route/syscall_go1_12_darwin.go b/src/vendor/golang.org/x/net/route/syscall_go1_12_darwin.go deleted file mode 100644 index 7a13e4fd90430d..00000000000000 --- a/src/vendor/golang.org/x/net/route/syscall_go1_12_darwin.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.12 -// +build go1.12 - -package route - -import _ "unsafe" // for linkname - -//go:linkname sysctl syscall.sysctl -func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error diff --git a/src/vendor/golang.org/x/net/route/zsys_darwin.go b/src/vendor/golang.org/x/net/route/zsys_darwin.go index 19e4133f7d1911..56a0c66f44f202 100644 --- a/src/vendor/golang.org/x/net/route/zsys_darwin.go +++ b/src/vendor/golang.org/x/net/route/zsys_darwin.go @@ -3,83 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1e - - sysSOCK_RAW = 0x3 - - 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 diff --git a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go index 34f0eaaa42b043..f7c7a60cd64220 100644 --- a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go +++ b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go @@ -3,84 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 - - 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_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 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 index f36aaadb59f71a..3f985c7ee90c1d 100644 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go @@ -3,77 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 - - 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 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 index 4c639b82e4b3a3..929339369829dc 100644 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go @@ -3,77 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 - - 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 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 index 710c1472b64397..a2bdb4ad3b58ea 100644 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go @@ -3,77 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 - - 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 diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go index 4c639b82e4b3a3..929339369829dc 100644 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go @@ -3,77 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x1c - - sysSOCK_RAW = 0x3 - - 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 diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go new file mode 100644 index 00000000000000..929339369829dc --- /dev/null +++ b/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go @@ -0,0 +1,52 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_freebsd.go + +package route + +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 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/src/vendor/golang.org/x/net/route/zsys_netbsd.go b/src/vendor/golang.org/x/net/route/zsys_netbsd.go index b4f66ca6cbc818..eaffe8c40804cb 100644 --- a/src/vendor/golang.org/x/net/route/zsys_netbsd.go +++ b/src/vendor/golang.org/x/net/route/zsys_netbsd.go @@ -3,86 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x22 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 - - 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 diff --git a/src/vendor/golang.org/x/net/route/zsys_openbsd.go b/src/vendor/golang.org/x/net/route/zsys_openbsd.go index 1021b4cea4f3dd..b11b8126801b68 100644 --- a/src/vendor/golang.org/x/net/route/zsys_openbsd.go +++ b/src/vendor/golang.org/x/net/route/zsys_openbsd.go @@ -3,95 +3,6 @@ package route -const ( - sysAF_UNSPEC = 0x0 - sysAF_INET = 0x2 - sysAF_ROUTE = 0x11 - sysAF_LINK = 0x12 - sysAF_INET6 = 0x18 - - sysSOCK_RAW = 0x3 - - 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 - sysRTM_INVALIDATE = 0x11 - sysRTM_BFD = 0x12 - sysRTM_PROPOSAL = 0x13 - - 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 - sysRTA_BFD = 0x800 - sysRTA_DNS = 0x1000 - sysRTA_STATIC = 0x2000 - sysRTA_SEARCH = 0x4000 - - 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_BFD = 0xb - sysRTAX_DNS = 0xc - sysRTAX_STATIC = 0xd - sysRTAX_SEARCH = 0xe - sysRTAX_MAX = 0xf -) - const ( sizeofRtMsghdr = 0x60 diff --git a/src/vendor/golang.org/x/sys/AUTHORS b/src/vendor/golang.org/x/sys/AUTHORS deleted file mode 100644 index 15167cd746c560..00000000000000 --- a/src/vendor/golang.org/x/sys/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/src/vendor/golang.org/x/sys/CONTRIBUTORS b/src/vendor/golang.org/x/sys/CONTRIBUTORS deleted file mode 100644 index 1c4577e9680611..00000000000000 --- a/src/vendor/golang.org/x/sys/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/src/vendor/golang.org/x/sys/cpu/byteorder.go b/src/vendor/golang.org/x/sys/cpu/byteorder.go index dcbb14ef35a480..271055be0b1e1a 100644 --- a/src/vendor/golang.org/x/sys/cpu/byteorder.go +++ b/src/vendor/golang.org/x/sys/cpu/byteorder.go @@ -46,6 +46,7 @@ func hostByteOrder() byteOrder { case "386", "amd64", "amd64p32", "alpha", "arm", "arm64", + "loong64", "mipsle", "mips64le", "mips64p32le", "nios2", "ppc64le", diff --git a/src/vendor/golang.org/x/sys/cpu/cpu.go b/src/vendor/golang.org/x/sys/cpu/cpu.go index abbec2d44bfbe3..83f112c4c808c6 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu.go +++ b/src/vendor/golang.org/x/sys/cpu/cpu.go @@ -56,6 +56,7 @@ var X86 struct { HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions HasBMI1 bool // Bit manipulation instruction set 1 HasBMI2 bool // Bit manipulation instruction set 2 + HasCX16 bool // Compare and exchange 16 Bytes HasERMS bool // Enhanced REP for MOVSB and STOSB HasFMA bool // Fused-multiply-add instructions HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers. @@ -105,8 +106,8 @@ var ARM64 struct { // ARM contains the supported CPU features of the current ARM (32-bit) platform. // All feature flags are false if: -// 1. the current platform is not arm, or -// 2. the current operating system is not Linux. +// 1. the current platform is not arm, or +// 2. the current operating system is not Linux. var ARM struct { _ CacheLinePad HasSWP bool // SWP instruction support diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/src/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go index 3298a87e981293..fa7cdb9bcd5fa4 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go +++ b/src/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -15,7 +15,3 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) // xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler // and in cpu_gccgo.c for gccgo. func xgetbv() (eax, edx uint32) - -// darwinSupportsAVX512 is implemented in cpu_x86.s for gc compiler -// and in cpu_gccgo_x86.go for gccgo. -func darwinSupportsAVX512() bool diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c b/src/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c index e363c7d1319782..a4605e6d12e897 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c +++ b/src/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c @@ -7,6 +7,7 @@ #include #include +#include // Need to wrap __get_cpuid_count because it's declared as static. int @@ -17,27 +18,21 @@ gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); } +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC push_options +#pragma GCC target("xsave") +#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function) + // xgetbv reads the contents of an XCR (Extended Control Register) // specified in the ECX register into registers EDX:EAX. // Currently, the only supported value for XCR is 0. -// -// TODO: Replace with a better alternative: -// -// #include -// -// #pragma GCC target("xsave") -// -// void gccgoXgetbv(uint32_t *eax, uint32_t *edx) { -// unsigned long long x = _xgetbv(0); -// *eax = x & 0xffffffff; -// *edx = (x >> 32) & 0xffffffff; -// } -// -// Note that _xgetbv is defined starting with GCC 8. void gccgoXgetbv(uint32_t *eax, uint32_t *edx) { - __asm(" xorl %%ecx, %%ecx\n" - " xgetbv" - : "=a"(*eax), "=d"(*edx)); + uint64_t v = _xgetbv(0); + *eax = v & 0xffffffff; + *edx = v >> 32; } + +#pragma clang attribute pop +#pragma GCC pop_options diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/src/vendor/golang.org/x/sys/cpu/cpu_loong64.go new file mode 100644 index 00000000000000..0f57b05bdbe5d0 --- /dev/null +++ b/src/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 +// +build loong64 + +package cpu + +const cacheLineSize = 64 + +func initOptions() { +} diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go b/src/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go new file mode 100644 index 00000000000000..dd10eb79feefa8 --- /dev/null +++ b/src/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && riscv64 +// +build !linux,riscv64 + +package cpu + +func archInit() { + Initialized = true +} diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_x86.go b/src/vendor/golang.org/x/sys/cpu/cpu_x86.go index 54ca4667fb8ea0..f5aacfc825d5b7 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu_x86.go +++ b/src/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -39,6 +39,7 @@ func initOptions() { {Name: "avx512bf16", Feature: &X86.HasAVX512BF16}, {Name: "bmi1", Feature: &X86.HasBMI1}, {Name: "bmi2", Feature: &X86.HasBMI2}, + {Name: "cx16", Feature: &X86.HasCX16}, {Name: "erms", Feature: &X86.HasERMS}, {Name: "fma", Feature: &X86.HasFMA}, {Name: "osxsave", Feature: &X86.HasOSXSAVE}, @@ -73,6 +74,7 @@ func archInit() { X86.HasPCLMULQDQ = isSet(1, ecx1) X86.HasSSSE3 = isSet(9, ecx1) X86.HasFMA = isSet(12, ecx1) + X86.HasCX16 = isSet(13, ecx1) X86.HasSSE41 = isSet(19, ecx1) X86.HasSSE42 = isSet(20, ecx1) X86.HasPOPCNT = isSet(23, ecx1) @@ -88,9 +90,10 @@ func archInit() { osSupportsAVX = isSet(1, eax) && isSet(2, eax) if runtime.GOOS == "darwin" { - // Check darwin commpage for AVX512 support. Necessary because: - // https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/osfmk/i386/fpu.c#L175-L201 - osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512() + // Darwin doesn't save/restore AVX-512 mask registers correctly across signal handlers. + // Since users can't rely on mask register contents, let's not advertise AVX-512 support. + // See issue 49233. + osSupportsAVX512 = false } else { // Check if OPMASK and ZMM registers have OS support. osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax) diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_x86.s b/src/vendor/golang.org/x/sys/cpu/cpu_x86.s index b748ba52f7cf55..39acab2ff5c203 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu_x86.s +++ b/src/vendor/golang.org/x/sys/cpu/cpu_x86.s @@ -26,27 +26,3 @@ TEXT ·xgetbv(SB),NOSPLIT,$0-8 MOVL AX, eax+0(FP) MOVL DX, edx+4(FP) RET - -// func darwinSupportsAVX512() bool -TEXT ·darwinSupportsAVX512(SB), NOSPLIT, $0-1 - MOVB $0, ret+0(FP) // default to false -#ifdef GOOS_darwin // return if not darwin -#ifdef GOARCH_amd64 // return if not amd64 -// These values from: -// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h -#define commpage64_base_address 0x00007fffffe00000 -#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010) -#define commpage64_version (commpage64_base_address+0x01E) -#define hasAVX512F 0x0000004000000000 - MOVQ $commpage64_version, BX - CMPW (BX), $13 // cpu_capabilities64 undefined in versions < 13 - JL no_avx512 - MOVQ $commpage64_cpu_capabilities64, BX - MOVQ $hasAVX512F, CX - TESTQ (BX), CX - JZ no_avx512 - MOVB $1, ret+0(FP) -no_avx512: -#endif -#endif - RET diff --git a/src/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go b/src/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go index a864f24d7589aa..96134157a10d18 100644 --- a/src/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go +++ b/src/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go @@ -5,7 +5,7 @@ // Recreate a getsystemcfg syscall handler instead of // using the one provided by x/sys/unix to avoid having // the dependency between them. (See golang.org/issue/32102) -// Morever, this file will be used during the building of +// Moreover, this file will be used during the building of // gccgo's libgo and thus must not used a CGo method. //go:build aix && gccgo diff --git a/src/vendor/golang.org/x/text/AUTHORS b/src/vendor/golang.org/x/text/AUTHORS deleted file mode 100644 index 15167cd746c560..00000000000000 --- a/src/vendor/golang.org/x/text/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/src/vendor/golang.org/x/text/CONTRIBUTORS b/src/vendor/golang.org/x/text/CONTRIBUTORS deleted file mode 100644 index 1c4577e9680611..00000000000000 --- a/src/vendor/golang.org/x/text/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/src/vendor/golang.org/x/text/unicode/bidi/core.go b/src/vendor/golang.org/x/text/unicode/bidi/core.go index e4c0811016c2ac..9d2ae547b5ed4d 100644 --- a/src/vendor/golang.org/x/text/unicode/bidi/core.go +++ b/src/vendor/golang.org/x/text/unicode/bidi/core.go @@ -193,14 +193,14 @@ func (p *paragraph) run() { // // At the end of this function: // -// - The member variable matchingPDI is set to point to the index of the -// matching PDI character for each isolate initiator character. If there is -// no matching PDI, it is set to the length of the input text. For other -// characters, it is set to -1. -// - The member variable matchingIsolateInitiator is set to point to the -// index of the matching isolate initiator character for each PDI character. -// If there is no matching isolate initiator, or the character is not a PDI, -// it is set to -1. +// - The member variable matchingPDI is set to point to the index of the +// matching PDI character for each isolate initiator character. If there is +// no matching PDI, it is set to the length of the input text. For other +// characters, it is set to -1. +// - The member variable matchingIsolateInitiator is set to point to the +// index of the matching isolate initiator character for each PDI character. +// If there is no matching isolate initiator, or the character is not a PDI, +// it is set to -1. func (p *paragraph) determineMatchingIsolates() { p.matchingPDI = make([]int, p.Len()) p.matchingIsolateInitiator = make([]int, p.Len()) @@ -435,7 +435,7 @@ func maxLevel(a, b level) level { } // Rule X10, second bullet: Determine the start-of-sequence (sos) and end-of-sequence (eos) types, -// either L or R, for each isolating run sequence. +// either L or R, for each isolating run sequence. func (p *paragraph) isolatingRunSequence(indexes []int) *isolatingRunSequence { length := len(indexes) types := make([]Class, length) @@ -495,9 +495,9 @@ func (s *isolatingRunSequence) resolveWeakTypes() { if t == NSM { s.types[i] = precedingCharacterType } else { - if t.in(LRI, RLI, FSI, PDI) { - precedingCharacterType = ON - } + // if t.in(LRI, RLI, FSI, PDI) { + // precedingCharacterType = ON + // } precedingCharacterType = t } } @@ -905,7 +905,7 @@ func (p *paragraph) getLevels(linebreaks []int) []level { // Lines are concatenated from left to right. So for example, the fifth // character from the left on the third line is // -// getReordering(linebreaks)[linebreaks[1] + 4] +// getReordering(linebreaks)[linebreaks[1] + 4] // // (linebreaks[1] is the position after the last character of the second // line, which is also the index of the first character on the third line, diff --git a/src/vendor/golang.org/x/text/unicode/norm/forminfo.go b/src/vendor/golang.org/x/text/unicode/norm/forminfo.go index 526c7033ac464c..d69ccb4f976116 100644 --- a/src/vendor/golang.org/x/text/unicode/norm/forminfo.go +++ b/src/vendor/golang.org/x/text/unicode/norm/forminfo.go @@ -110,10 +110,11 @@ func (p Properties) BoundaryAfter() bool { } // We pack quick check data in 4 bits: -// 5: Combines forward (0 == false, 1 == true) -// 4..3: NFC_QC Yes(00), No (10), or Maybe (11) -// 2: NFD_QC Yes (0) or No (1). No also means there is a decomposition. -// 1..0: Number of trailing non-starters. +// +// 5: Combines forward (0 == false, 1 == true) +// 4..3: NFC_QC Yes(00), No (10), or Maybe (11) +// 2: NFD_QC Yes (0) or No (1). No also means there is a decomposition. +// 1..0: Number of trailing non-starters. // // When all 4 bits are zero, the character is inert, meaning it is never // influenced by normalization. diff --git a/src/vendor/golang.org/x/text/unicode/norm/normalize.go b/src/vendor/golang.org/x/text/unicode/norm/normalize.go index 95efcf26e81d7a..4747ad07a839c1 100644 --- a/src/vendor/golang.org/x/text/unicode/norm/normalize.go +++ b/src/vendor/golang.org/x/text/unicode/norm/normalize.go @@ -18,16 +18,17 @@ import ( // A Form denotes a canonical representation of Unicode code points. // The Unicode-defined normalization and equivalence forms are: // -// NFC Unicode Normalization Form C -// NFD Unicode Normalization Form D -// NFKC Unicode Normalization Form KC -// NFKD Unicode Normalization Form KD +// NFC Unicode Normalization Form C +// NFD Unicode Normalization Form D +// NFKC Unicode Normalization Form KC +// NFKD Unicode Normalization Form KD // // For a Form f, this documentation uses the notation f(x) to mean // the bytes or string x converted to the given form. // A position n in x is called a boundary if conversion to the form can // proceed independently on both sides: -// f(x) == append(f(x[0:n]), f(x[n:])...) +// +// f(x) == append(f(x[0:n]), f(x[n:])...) // // References: https://unicode.org/reports/tr15/ and // https://unicode.org/notes/tn5/. diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index ff01db5cdc77fe..a821f21f338bd8 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,14 +1,13 @@ -# golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e +# golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa ## explicit; go 1.17 golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 -golang.org/x/crypto/curve25519 golang.org/x/crypto/hkdf +golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/internal/subtle -golang.org/x/crypto/poly1305 -# golang.org/x/net v0.0.0-20210510120150-4163338589ed +# golang.org/x/net v0.0.0-20220906165146-f3363e06e74c ## explicit; go 1.17 golang.org/x/net/dns/dnsmessage golang.org/x/net/http/httpguts @@ -18,10 +17,10 @@ golang.org/x/net/idna golang.org/x/net/lif golang.org/x/net/nettest golang.org/x/net/route -# golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 +# golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 ## explicit; go 1.17 golang.org/x/sys/cpu -# golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f +# golang.org/x/text v0.3.8-0.20220722155301-d03b41800055 ## explicit; go 1.17 golang.org/x/text/secure/bidirule golang.org/x/text/transform diff --git a/test/abi/method_wrapper.go b/test/abi/method_wrapper.go new file mode 100644 index 00000000000000..7aa262fb5254fb --- /dev/null +++ b/test/abi/method_wrapper.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2021 The Go Authors. 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 int + +type T struct { + a int + S +} + +//go:noinline +func (s *S) M(a int, x [2]int, b float64, y [2]float64) (S, int, [2]int, float64, [2]float64) { + return *s, a, x, b, y +} + +var s S = 42 +var t = &T{S: s} + +var fn = (*T).M // force a method wrapper + +func main() { + a := 123 + x := [2]int{456, 789} + b := 1.2 + y := [2]float64{3.4, 5.6} + s1, a1, x1, b1, y1 := fn(t, a, x, b, y) + if a1 != a || x1 != x || b1 != b || y1 != y || s1 != s { + panic("FAIL") + } +} diff --git a/test/alias2.go b/test/alias2.go index d7b5dccb68c2c9..61c7551f799032 100644 --- a/test/alias2.go +++ b/test/alias2.go @@ -46,8 +46,8 @@ var _ A0 = T0{} var _ T0 = A0{} // But aliases and original types cannot be used with new types based on them. -var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" -var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" +var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|cannot use T0{} \(value of type T0\) as type N0 in variable declaration" +var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|cannot use A0{} \(value of type T0\) as type N0 in variable declaration" var _ A5 = Value{} @@ -82,10 +82,10 @@ func _() { var _ A0 = T0{} var _ T0 = A0{} - var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" - var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|incompatible type" + var _ N0 = T0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|cannot use T0{} \(value of type T0\) as type N0 in variable declaration" + var _ N0 = A0{} // ERROR "cannot use T0{} \(type T0\) as type N0 in assignment|cannot use A0{} \(value of type T0\) as type N0 in variable declaration" - var _ A5 = Value{} // ERROR "cannot use reflect\.Value{} \(type reflect.Value\) as type A5 in assignment|incompatible type" + var _ A5 = Value{} // ERROR "cannot use reflect\.Value{} \(type reflect.Value\) as type A5 in assignment|cannot use Value{} \(value of type reflect.Value\) as type A5 in variable declaration" } // Invalid type alias declarations. diff --git a/test/append1.go b/test/append1.go index 9dab120b25b197..397be570d99fd5 100644 --- a/test/append1.go +++ b/test/append1.go @@ -17,6 +17,6 @@ func main() { _ = append(s...) // ERROR "cannot use ... on first argument|not enough arguments in call to append" _ = append(s, 2, s...) // ERROR "too many arguments to append|too many arguments in call to append" - _ = append(s, make([]int, 0)) // ERROR "cannot use make.* as type int in append|cannot use make.* as int value" + _ = append(s, make([]int, 0)) // ERROR "cannot use make.* as type int in append|cannot use make.* \(value of type \[\]int\) as type int in argument to append" _ = append(s, make([]int, -1)...) // ERROR "negative len argument in make|index -1.* must not be negative" } diff --git a/test/asmhdr.dir/main.go b/test/asmhdr.dir/main.go new file mode 100644 index 00000000000000..4e1813d2edf4ab --- /dev/null +++ b/test/asmhdr.dir/main.go @@ -0,0 +1,72 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "unsafe" + +const ( + smallInt = 42 + + // For bigInt, we use a value that's too big for an int64, but still + // fits in uint64. go/constant uses a different representation for + // values larger than int64, but the cmd/asm parser can't parse + // anything bigger than a uint64. + bigInt = 0xffffffffffffffff + + stringVal = "test" + + longStringVal = "this_is_a_string_constant_longer_than_seventy_characters_which_used_to_fail_see_issue_50253" +) + +var ( + smallIntAsm int64 + bigIntAsm uint64 + stringAsm [len(stringVal)]byte + longStringAsm [len(longStringVal)]byte +) + +type typ struct { + a uint64 + b [100]uint8 + c uint8 +} + +var ( + typSize uint64 + + typA, typB, typC uint64 +) + +func main() { + if smallInt != smallIntAsm { + println("smallInt", smallInt, "!=", smallIntAsm) + } + if bigInt != bigIntAsm { + println("bigInt", uint64(bigInt), "!=", bigIntAsm) + } + if stringVal != string(stringAsm[:]) { + println("stringVal", stringVal, "!=", string(stringAsm[:])) + } + if longStringVal != string(longStringAsm[:]) { + println("longStringVal", longStringVal, "!=", string(longStringAsm[:])) + } + + // We also include boolean consts in go_asm.h, but they're + // defined to be "true" or "false", and it's not clear how to + // use that in assembly. + + if want := unsafe.Sizeof(typ{}); want != uintptr(typSize) { + println("typSize", want, "!=", typSize) + } + if want := unsafe.Offsetof(typ{}.a); want != uintptr(typA) { + println("typA", want, "!=", typA) + } + if want := unsafe.Offsetof(typ{}.b); want != uintptr(typB) { + println("typB", want, "!=", typB) + } + if want := unsafe.Offsetof(typ{}.c); want != uintptr(typC) { + println("typC", want, "!=", typC) + } +} diff --git a/test/asmhdr.dir/main.s b/test/asmhdr.dir/main.s new file mode 100644 index 00000000000000..bc2aa99b0b3be5 --- /dev/null +++ b/test/asmhdr.dir/main.s @@ -0,0 +1,30 @@ +// Copyright 2021 The Go 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 "go_asm.h" +#define RODATA 8 + +DATA ·smallIntAsm(SB)/8, $const_smallInt +GLOBL ·smallIntAsm(SB),RODATA,$8 + +DATA ·bigIntAsm(SB)/8, $const_bigInt +GLOBL ·bigIntAsm(SB),RODATA,$8 + +DATA ·stringAsm(SB)/4, $const_stringVal +GLOBL ·stringAsm(SB),RODATA,$4 + +DATA ·longStringAsm(SB)/91, $const_longStringVal +GLOBL ·longStringAsm(SB),RODATA,$91 + +DATA ·typSize(SB)/8, $typ__size +GLOBL ·typSize(SB),RODATA,$8 + +DATA ·typA(SB)/8, $typ_a +GLOBL ·typA(SB),RODATA,$8 + +DATA ·typB(SB)/8, $typ_b +GLOBL ·typB(SB),RODATA,$8 + +DATA ·typC(SB)/8, $typ_c +GLOBL ·typC(SB),RODATA,$8 diff --git a/test/asmhdr.go b/test/asmhdr.go new file mode 100644 index 00000000000000..772f98e2cc6214 --- /dev/null +++ b/test/asmhdr.go @@ -0,0 +1,9 @@ +// buildrundir + +// Copyright 2021 The Go 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 -asmhdr output of the compiler. + +package ignored diff --git a/test/chan/perm.go b/test/chan/perm.go index 4c94ab7ffaeefb..04046723a4f6fe 100644 --- a/test/chan/perm.go +++ b/test/chan/perm.go @@ -66,5 +66,5 @@ func main() { close(c) close(cs) close(cr) // ERROR "receive" - close(n) // ERROR "invalid operation.*non-chan type|must be channel|not a channel" + close(n) // ERROR "invalid operation.*non-chan type|must be channel|non-channel" } diff --git a/test/closure3.dir/main.go b/test/closure3.dir/main.go index 662a2e967bb103..7ef0a47595ea1f 100644 --- a/test/closure3.dir/main.go +++ b/test/closure3.dir/main.go @@ -94,10 +94,10 @@ func main() { return x + 2 } y, sink = func() (func(int) int, int) { // ERROR "can inline main.func12" - return func(x int) int { // ERROR "func literal does not escape" "can inline main.func12" + return func(x int) int { // ERROR "can inline main.func12" return x + 1 }, 42 - }() // ERROR "inlining call to main.func12" + }() // ERROR "func literal does not escape" "inlining call to main.func12" if y(40) != 41 { ppanic("y(40) != 41") } @@ -109,10 +109,10 @@ func main() { return x + 2 } y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2" - return func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.2" + return func(x int) int { // ERROR "can inline main.func13.2" return x + 1 }, 42 - }() // ERROR "inlining call to main.func13.2" + }() // ERROR "func literal does not escape" "inlining call to main.func13.2" if y(40) != 41 { ppanic("y(40) != 41") } diff --git a/test/closure5.dir/main.go b/test/closure5.dir/main.go index ee5dba648156ce..30b378495bad51 100644 --- a/test/closure5.dir/main.go +++ b/test/closure5.dir/main.go @@ -6,7 +6,7 @@ // that are expected to be inlined package main -import "a" +import "./a" func main() { if !a.G()()() { diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index a27a17f6e112df..3fb9ce646b81c3 100644 --- a/test/codegen/arithmetic.go +++ b/test/codegen/arithmetic.go @@ -84,6 +84,38 @@ func NegAddFromConstNeg(a int) int { return c } +func SubSubNegSimplify(a, b int) int { + // amd64:"NEGQ" + // ppc64:"NEG" + // ppc64le:"NEG" + r := (a - b) - a + return r +} + +func SubAddSimplify(a, b int) int { + // amd64:-"SUBQ",-"ADDQ" + // ppc64:-"SUB",-"ADD" + // ppc64le:-"SUB",-"ADD" + r := a + (b - a) + return r +} + +func SubAddNegSimplify(a, b int) int { + // amd64:"NEGQ",-"ADDQ",-"SUBQ" + // ppc64:"NEG",-"ADD",-"SUB" + // ppc64le:"NEG",-"ADD",-"SUB" + r := a - (b + a) + return r +} + +func AddAddSubSimplify(a, b, c int) int { + // amd64:-"SUBQ" + // ppc64:-"SUB" + // ppc64le:-"SUB" + r := a + (b + (c - a)) + return r +} + // -------------------- // // Multiplication // // -------------------- // @@ -135,30 +167,40 @@ func MulMemSrc(a []uint32, b []float32) { func MergeMuls1(n int) int { // amd64:"IMUL3Q\t[$]46" // 386:"IMUL3L\t[$]46" + // ppc64le:"MULLD\t[$]46" + // ppc64:"MULLD\t[$]46" return 15*n + 31*n // 46n } func MergeMuls2(n int) int { // amd64:"IMUL3Q\t[$]23","(ADDQ\t[$]29)|(LEAQ\t29)" // 386:"IMUL3L\t[$]23","ADDL\t[$]29" + // ppc64le/power9:"MADDLD",-"MULLD\t[$]23",-"ADD\t[$]29" + // ppc64le/power8:"MULLD\t[$]23","ADD\t[$]29" return 5*n + 7*(n+1) + 11*(n+2) // 23n + 29 } func MergeMuls3(a, n int) int { // amd64:"ADDQ\t[$]19",-"IMULQ\t[$]19" // 386:"ADDL\t[$]19",-"IMULL\t[$]19" + // ppc64:"ADD\t[$]19",-"MULLD\t[$]19" + // ppc64le:"ADD\t[$]19",-"MULLD\t[$]19" return a*n + 19*n // (a+19)n } func MergeMuls4(n int) int { // amd64:"IMUL3Q\t[$]14" // 386:"IMUL3L\t[$]14" + // ppc64:"MULLD\t[$]14" + // ppc64le:"MULLD\t[$]14" return 23*n - 9*n // 14n } func MergeMuls5(a, n int) int { // amd64:"ADDQ\t[$]-19",-"IMULQ\t[$]19" // 386:"ADDL\t[$]-19",-"IMULL\t[$]19" + // ppc64:"ADD\t[$]-19",-"MULLD\t[$]19" + // ppc64le:"ADD\t[$]-19",-"MULLD\t[$]19" return a*n - 19*n // (a-19)n } @@ -202,7 +244,7 @@ func ConstDivs(n1 uint, n2 int) (uint, int) { // amd64:"MOVQ\t[$]-1085102592571150095","IMULQ",-"IDIVQ" // 386:"MOVL\t[$]-252645135","IMULL",-"IDIVL" - // arm64:`MOVD`,`SMULH`,-`DIV` + // arm64:`SMULH`,-`DIV` // arm:`MOVW`,`MUL`,-`.*udiv` b := n2 / 17 // signed @@ -217,7 +259,7 @@ func FloatDivs(a []float32) float32 { func Pow2Mods(n1 uint, n2 int) (uint, int) { // 386:"ANDL\t[$]31",-"DIVL" - // amd64:"ANDQ\t[$]31",-"DIVQ" + // amd64:"ANDL\t[$]31",-"DIVQ" // arm:"AND\t[$]31",-".*udiv" // arm64:"AND\t[$]31",-"UDIV" // ppc64:"ANDCC\t[$]31" @@ -240,7 +282,7 @@ func Pow2DivisibleSigned(n1, n2 int) (bool, bool) { // 386:"TESTL\t[$]63",-"DIVL",-"SHRL" // amd64:"TESTQ\t[$]63",-"DIVQ",-"SHRQ" // arm:"AND\t[$]63",-".*udiv",-"SRA" - // arm64:"AND\t[$]63",-"UDIV",-"ASR" + // arm64:"TST\t[$]63",-"UDIV",-"ASR",-"AND" // ppc64:"ANDCC\t[$]63",-"SRAD" // ppc64le:"ANDCC\t[$]63",-"SRAD" a := n1%64 == 0 // signed divisible @@ -248,7 +290,7 @@ func Pow2DivisibleSigned(n1, n2 int) (bool, bool) { // 386:"TESTL\t[$]63",-"DIVL",-"SHRL" // amd64:"TESTQ\t[$]63",-"DIVQ",-"SHRQ" // arm:"AND\t[$]63",-".*udiv",-"SRA" - // arm64:"AND\t[$]63",-"UDIV",-"ASR" + // arm64:"TST\t[$]63",-"UDIV",-"ASR",-"AND" // ppc64:"ANDCC\t[$]63",-"SRAD" // ppc64le:"ANDCC\t[$]63",-"SRAD" b := n2%64 != 0 // signed indivisible @@ -266,7 +308,7 @@ func ConstMods(n1 uint, n2 int) (uint, int) { // amd64:"MOVQ\t[$]-1085102592571150095","IMULQ",-"IDIVQ" // 386:"MOVL\t[$]-252645135","IMULL",-"IDIVL" - // arm64:`MOVD`,`SMULH`,-`DIV` + // arm64:`SMULH`,-`DIV` // arm:`MOVW`,`MUL`,-`.*udiv` b := n2 % 17 // signed @@ -428,7 +470,7 @@ func LenDiv2(s string) int { func LenMod1(a []int) int { // 386:"ANDL\t[$]1023" - // amd64:"ANDQ\t[$]1023" + // amd64:"ANDL\t[$]1023" // arm64:"AND\t[$]1023",-"SDIV" // arm/6:"AND",-".*udiv" // arm/7:"BFC",-".*udiv",-"AND" @@ -439,7 +481,7 @@ func LenMod1(a []int) int { func LenMod2(s string) int { // 386:"ANDL\t[$]2047" - // amd64:"ANDQ\t[$]2047" + // amd64:"ANDL\t[$]2047" // arm64:"AND\t[$]2047",-"SDIV" // arm/6:"AND",-".*udiv" // arm/7:"BFC",-".*udiv",-"AND" @@ -460,7 +502,7 @@ func CapDiv(a []int) int { func CapMod(a []int) int { // 386:"ANDL\t[$]4095" - // amd64:"ANDQ\t[$]4095" + // amd64:"ANDL\t[$]4095" // arm64:"AND\t[$]4095",-"SDIV" // arm/6:"AND",-".*udiv" // arm/7:"BFC",-".*udiv",-"AND" @@ -483,6 +525,8 @@ func MULA(a, b, c uint32) (uint32, uint32, uint32) { r1 := c*79 + a // arm:`ADD`,-`MULA`,-`MUL\s` // arm64:`ADD`,-`MADD`,-`MULW` + // ppc64:`ADD`,-`MULLD` + // ppc64le:`ADD`,-`MULLD` r2 := b*64 + c return r0, r1, r2 } @@ -498,6 +542,8 @@ func MULS(a, b, c uint32) (uint32, uint32, uint32) { r1 := a - c*79 // arm/7:`SUB`,-`MULS`,-`MUL\s` // arm64:`SUB`,-`MSUBW`,-`MULW` + // ppc64:`SUB`,-`MULLD` + // ppc64le:`SUB`,-`MULLD` r2 := c - b*64 return r0, r1, r2 } @@ -526,12 +572,20 @@ func divInt(v int64) int64 { // "(z + C) -x -> C + (z - x)" can optimize the following cases. func constantFold1(i0, j0, i1, j1, i2, j2, i3, j3 int) (int, int, int, int) { // arm64:"SUB","ADD\t[$]2" + // ppc64:"SUB","ADD\t[$]2" + // ppc64le:"SUB","ADD\t[$]2" r0 := (i0 + 3) - (j0 + 1) // arm64:"SUB","SUB\t[$]4" + // ppc64:"SUB","ADD\t[$]-4" + // ppc64le:"SUB","ADD\t[$]-4" r1 := (i1 - 3) - (j1 + 1) // arm64:"SUB","ADD\t[$]4" + // ppc64:"SUB","ADD\t[$]4" + // ppc64le:"SUB","ADD\t[$]4" r2 := (i2 + 3) - (j2 - 1) // arm64:"SUB","SUB\t[$]2" + // ppc64:"SUB","ADD\t[$]-2" + // ppc64le:"SUB","ADD\t[$]-2" r3 := (i3 - 3) - (j3 - 1) return r0, r1, r2, r3 } @@ -540,14 +594,20 @@ func constantFold1(i0, j0, i1, j1, i2, j2, i3, j3 int) (int, int, int, int) { // "(C - z) - x -> C - (z + x)" can optimize the following cases. func constantFold2(i0, j0, i1, j1 int) (int, int) { // arm64:"ADD","MOVD\t[$]2","SUB" + // ppc64le: `SUBC\tR[0-9]+,\s[$]2,\sR` + // ppc64: `SUBC\tR[0-9]+,\s[$]2,\sR` r0 := (3 - i0) - (j0 + 1) // arm64:"ADD","MOVD\t[$]4","SUB" + // ppc64le: `SUBC\tR[0-9]+,\s[$]4,\sR` + // ppc64: `SUBC\tR[0-9]+,\s[$]4,\sR` r1 := (3 - i1) - (j1 - 1) return r0, r1 } func constantFold3(i, j int) int { // arm64: "MOVD\t[$]30","MUL",-"ADD",-"LSL" + // ppc64:"MULLD\t[$]30","MULLD" + // ppc64le:"MULLD\t[$]30","MULLD" r := (5 * i) * (6 * j) return r } diff --git a/test/codegen/bitfield.go b/test/codegen/bitfield.go index 0fe6799ec1d68e..3276af3de03b5e 100644 --- a/test/codegen/bitfield.go +++ b/test/codegen/bitfield.go @@ -77,11 +77,13 @@ func bfxil2(x, y uint64) uint64 { } // sbfiz +// merge shifts into sbfiz: (x << lc) >> rc && lc > rc. func sbfiz1(x int64) int64 { // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" return (x << 4) >> 3 } +// merge shift and sign-extension into sbfiz. func sbfiz2(x int32) int64 { return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]29",-"LSL" } @@ -94,20 +96,36 @@ func sbfiz4(x int8) int64 { return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]5",-"LSL" } +// sbfiz combinations. +// merge shift with sbfiz into sbfiz. func sbfiz5(x int32) int32 { // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" return (x << 4) >> 3 } +func sbfiz6(x int16) int64 { + return int64(x+1) << 3 // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL" +} + +func sbfiz7(x int8) int64 { + return int64(x+1) << 62 // arm64:"SBFIZ\t[$]62, R[0-9]+, [$]2",-"LSL" +} + +func sbfiz8(x int32) int64 { + return int64(x+1) << 40 // arm64:"SBFIZ\t[$]40, R[0-9]+, [$]24",-"LSL" +} + // sbfx +// merge shifts into sbfx: (x << lc) >> rc && lc <= rc. func sbfx1(x int64) int64 { return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" } func sbfx2(x int64) int64 { - return (x << 60) >> 60 // arm64:"SBFX\tZR, R[0-9]+, [$]4",-"LSL",-"ASR" + return (x << 60) >> 60 // arm64:"SBFX\t[$]0, R[0-9]+, [$]4",-"LSL",-"ASR" } +// merge shift and sign-extension into sbfx. func sbfx3(x int32) int64 { return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]29",-"ASR" } @@ -120,124 +138,197 @@ func sbfx5(x int8) int64 { return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]5",-"ASR" } -func sbfx6(x int32) int32 { +func sbfx6(x int32) int64 { + return int64(x >> 30) // arm64:"SBFX\t[$]30, R[0-9]+, [$]2" +} + +func sbfx7(x int16) int64 { + return int64(x >> 10) // arm64:"SBFX\t[$]10, R[0-9]+, [$]6" +} + +func sbfx8(x int8) int64 { + return int64(x >> 5) // arm64:"SBFX\t[$]5, R[0-9]+, [$]3" +} + +// sbfx combinations. +// merge shifts with sbfiz into sbfx. +func sbfx9(x int32) int32 { return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" } -// ubfiz -func ubfiz1(x uint64) uint64 { - // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND" - // s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND" - return (x & 0xfff) << 3 +// merge sbfx and sign-extension into sbfx. +func sbfx10(x int32) int64 { + c := x + 5 + return int64(c >> 20) // arm64"SBFX\t[$]20, R[0-9]+, [$]12",-"MOVW\tR[0-9]+, R[0-9]+" } -func ubfiz2(x uint64) uint64 { - // arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND" - // s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND" - return (x << 4) & 0xfff0 +// ubfiz +// merge shifts into ubfiz: (x<>rc && lc>rc +func ubfiz1(x uint64) uint64 { + // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR" + // s390x:"RISBGZ\t[$]3, [$]62, [$]1, ",-"SLD",-"SRD" + return (x << 4) >> 3 } -func ubfiz3(x uint32) uint64 { +// merge shift and zero-extension into ubfiz. +func ubfiz2(x uint32) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]32",-"LSL" } -func ubfiz4(x uint16) uint64 { +func ubfiz3(x uint16) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL" } -func ubfiz5(x uint8) uint64 { +func ubfiz4(x uint8) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]8",-"LSL" } -func ubfiz6(x uint64) uint64 { - // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR" - // s390x:"RISBGZ\t[$]3, [$]62, [$]1, ",-"SLD",-"SRD" - return (x << 4) >> 3 +func ubfiz5(x uint8) uint64 { + return uint64(x) << 60 // arm64:"UBFIZ\t[$]60, R[0-9]+, [$]4",-"LSL" +} + +func ubfiz6(x uint32) uint64 { + return uint64(x << 30) // arm64:"UBFIZ\t[$]30, R[0-9]+, [$]2", +} + +func ubfiz7(x uint16) uint64 { + return uint64(x << 10) // arm64:"UBFIZ\t[$]10, R[0-9]+, [$]6", +} + +func ubfiz8(x uint8) uint64 { + return uint64(x << 7) // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]1", +} + +// merge ANDconst into ubfiz. +func ubfiz9(x uint64) uint64 { + // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND" + // s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND" + return (x & 0xfff) << 3 +} + +func ubfiz10(x uint64) uint64 { + // arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND" + // s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND" + return (x << 4) & 0xfff0 } -func ubfiz7(x uint32) uint32 { +// ubfiz combinations +func ubfiz11(x uint32) uint32 { // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"LSR" return (x << 4) >> 3 } -func ubfiz8(x uint64) uint64 { +func ubfiz12(x uint64) uint64 { // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]20",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]43, [$]62, [$]1, ",-"SLD",-"SRD",-"AND" return ((x & 0xfffff) << 4) >> 3 } -func ubfiz9(x uint64) uint64 { +func ubfiz13(x uint64) uint64 { // arm64:"UBFIZ\t[$]5, R[0-9]+, [$]13",-"LSL",-"LSR",-"AND" return ((x << 3) & 0xffff) << 2 } -func ubfiz10(x uint64) uint64 { +func ubfiz14(x uint64) uint64 { // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]45, [$]56, [$]7, ",-"SLD",-"SRD",-"AND" return ((x << 5) & (0xfff << 5)) << 2 } // ubfx +// merge shifts into ubfx: (x<>rc && lc> 25) & 1023 -} - -func ubfx2(x uint64) uint64 { - // arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND" - // s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND" - return (x & 0x0ff0) >> 4 + // arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR" + // s390x:"RISBGZ\t[$]2, [$]63, [$]63,",-"SLD",-"SRD" + return (x << 1) >> 2 } -func ubfx3(x uint32) uint64 { +// merge shift and zero-extension into ubfx. +func ubfx2(x uint32) uint64 { return uint64(x >> 15) // arm64:"UBFX\t[$]15, R[0-9]+, [$]17",-"LSR" } -func ubfx4(x uint16) uint64 { +func ubfx3(x uint16) uint64 { return uint64(x >> 9) // arm64:"UBFX\t[$]9, R[0-9]+, [$]7",-"LSR" } -func ubfx5(x uint8) uint64 { +func ubfx4(x uint8) uint64 { return uint64(x >> 3) // arm64:"UBFX\t[$]3, R[0-9]+, [$]5",-"LSR" } -func ubfx6(x uint64) uint64 { - // arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR" - // s390x:"RISBGZ\t[$]2, [$]63, [$]63,",-"SLD",-"SRD" - return (x << 1) >> 2 +func ubfx5(x uint32) uint64 { + return uint64(x) >> 30 // arm64:"UBFX\t[$]30, R[0-9]+, [$]2" +} + +func ubfx6(x uint16) uint64 { + return uint64(x) >> 10 // arm64:"UBFX\t[$]10, R[0-9]+, [$]6" } -func ubfx7(x uint32) uint32 { +func ubfx7(x uint8) uint64 { + return uint64(x) >> 3 // arm64:"UBFX\t[$]3, R[0-9]+, [$]5" +} + +// merge ANDconst into ubfx. +func ubfx8(x uint64) uint64 { + // arm64:"UBFX\t[$]25, R[0-9]+, [$]10",-"LSR",-"AND" + // s390x:"RISBGZ\t[$]54, [$]63, [$]39, ",-"SRD",-"AND" + return (x >> 25) & 1023 +} + +func ubfx9(x uint64) uint64 { + // arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND" + // s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND" + return (x & 0x0ff0) >> 4 +} + +// ubfx combinations. +func ubfx10(x uint32) uint32 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]30",-"LSL",-"LSR" return (x << 1) >> 2 } -func ubfx8(x uint64) uint64 { +func ubfx11(x uint64) uint64 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]52, [$]63, [$]63,",-"SLD",-"SRD",-"AND" return ((x << 1) >> 2) & 0xfff } -func ubfx9(x uint64) uint64 { +func ubfx12(x uint64) uint64 { // arm64:"UBFX\t[$]4, R[0-9]+, [$]11",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]53, [$]63, [$]60, ",-"SLD",-"SRD",-"AND" return ((x >> 3) & 0xfff) >> 1 } -func ubfx10(x uint64) uint64 { +func ubfx13(x uint64) uint64 { // arm64:"UBFX\t[$]5, R[0-9]+, [$]56",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]8, [$]63, [$]59, ",-"SLD",-"SRD" return ((x >> 2) << 5) >> 8 } -func ubfx11(x uint64) uint64 { +func ubfx14(x uint64) uint64 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]19",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]45, [$]63, [$]63, ",-"SLD",-"SRD",-"AND" return ((x & 0xfffff) << 3) >> 4 } +// merge ubfx and zero-extension into ubfx. +func ubfx15(x uint64) bool { + midr := x + 10 + part_num := uint16((midr >> 4) & 0xfff) + if part_num == 0xd0c { // arm64:"UBFX\t[$]4, R[0-9]+, [$]12",-"MOVHU\tR[0-9]+, R[0-9]+" + return true + } + return false +} + +// merge ANDconst and ubfx into ubfx +func ubfx16(x uint64) uint64 { + // arm64:"UBFX\t[$]4, R[0-9]+, [$]6",-"AND\t[$]63" + return ((x >> 3) & 0xfff) >> 1 & 0x3f +} + // Check that we don't emit comparisons for constant shifts. +// //go:nosplit func shift_no_cmp(x int) int { // arm64:`LSL\t[$]17`,-`CMP` diff --git a/test/codegen/bits.go b/test/codegen/bits.go index 8117a623072934..e7826b8e658dc8 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -6,6 +6,8 @@ package codegen +import "math/bits" + /************************************ * 64-bit instructions ************************************/ @@ -330,7 +332,7 @@ func bitSetPowerOf2Test(x int) bool { } func bitSetTest(x int) bool { - // amd64:"ANDQ\t[$]9, AX" + // amd64:"ANDL\t[$]9, AX" // amd64:"CMPQ\tAX, [$]9" return x&9 == 9 } @@ -355,3 +357,9 @@ func issue44228b(a []int32, i int) bool { // amd64: "BTL", -"SHL" return a[i>>5]&(1<<(i&31)) != 0 } + +func issue48467(x, y uint64) uint64 { + // arm64: -"NEG" + d, borrow := bits.Sub64(x, y, 0) + return x - d&(-borrow) +} diff --git a/test/codegen/bmi.go b/test/codegen/bmi.go new file mode 100644 index 00000000000000..3b125a1b5901ca --- /dev/null +++ b/test/codegen/bmi.go @@ -0,0 +1,105 @@ +// asmcheck + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package codegen + +func andn64(x, y int64) int64 { + // amd64/v3:"ANDNQ" + return x &^ y +} + +func andn32(x, y int32) int32 { + // amd64/v3:"ANDNL" + return x &^ y +} + +func blsi64(x int64) int64 { + // amd64/v3:"BLSIQ" + return x & -x +} + +func blsi32(x int32) int32 { + // amd64/v3:"BLSIL" + return x & -x +} + +func blsmsk64(x int64) int64 { + // amd64/v3:"BLSMSKQ" + return x ^ (x - 1) +} + +func blsmsk32(x int32) int32 { + // amd64/v3:"BLSMSKL" + return x ^ (x - 1) +} + +func blsr64(x int64) int64 { + // amd64/v3:"BLSRQ" + return x & (x - 1) +} + +func blsr32(x int32) int32 { + // amd64/v3:"BLSRL" + return x & (x - 1) +} + +func sarx64(x, y int64) int64 { + // amd64/v3:"SARXQ" + return x >> y +} + +func sarx32(x, y int32) int32 { + // amd64/v3:"SARXL" + return x >> y +} + +func sarx64_load(x []int64, i int) int64 { + // amd64/v3: `SARXQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s := x[i] >> (i & 63) + // amd64/v3: `SARXQ\t[A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s = x[i+1] >> (s & 63) + return s +} + +func sarx32_load(x []int32, i int) int32 { + // amd64/v3: `SARXL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s := x[i] >> (i & 63) + // amd64/v3: `SARXL\t[A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s = x[i+1] >> (s & 63) + return s +} + +func shlrx64(x, y uint64) uint64 { + // amd64/v3:"SHRXQ" + s := x >> y + // amd64/v3:"SHLXQ" + s = s << y + return s +} + +func shlrx32(x, y uint32) uint32 { + // amd64/v3:"SHRXL" + s := x >> y + // amd64/v3:"SHLXL" + s = s << y + return s +} + +func shlrx64_load(x []uint64, i int, s uint64) uint64 { + // amd64/v3: `SHRXQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s = x[i] >> i + // amd64/v3: `SHLXQ\t[A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s = x[i+1] << s + return s +} + +func shlrx32_load(x []uint32, i int, s uint32) uint32 { + // amd64/v3: `SHRXL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s = x[i] >> i + // amd64/v3: `SHLXL\t[A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s = x[i+1] << s + return s +} diff --git a/test/codegen/bool.go b/test/codegen/bool.go index 929b1b49b9bd01..d921b55c066c87 100644 --- a/test/codegen/bool.go +++ b/test/codegen/bool.go @@ -10,24 +10,64 @@ package codegen func convertNeq0B(x uint8, c bool) bool { // amd64:"ANDL\t[$]1",-"SETNE" + // ppc64:"ANDCC",-"CMPW",-"ISEL" + // ppc64le:"ANDCC",-"CMPW",-"ISEL" + // ppc64le/power9:"ANDCC",-"CMPW",-"ISEL" b := x&1 != 0 return c && b } func convertNeq0W(x uint16, c bool) bool { // amd64:"ANDL\t[$]1",-"SETNE" + // ppc64:"ANDCC",-"CMPW",-"ISEL" + // ppc64le:"ANDCC",-"CMPW",-"ISEL" + // ppc64le/power9:"ANDCC",-CMPW",-"ISEL" b := x&1 != 0 return c && b } func convertNeq0L(x uint32, c bool) bool { // amd64:"ANDL\t[$]1",-"SETB" + // ppc64:"ANDCC",-"CMPW",-"ISEL" + // ppc64le:"ANDCC",-"CMPW",-"ISEL" + // ppc64le/power9:"ANDCC",-"CMPW",-"ISEL" b := x&1 != 0 return c && b } func convertNeq0Q(x uint64, c bool) bool { - // amd64:"ANDQ\t[$]1",-"SETB" + // amd64:"ANDL\t[$]1",-"SETB" + // ppc64:"ANDCC",-"CMP",-"ISEL" + // ppc64le:"ANDCC",-"CMP",-"ISEL" + // ppc64le/power9:"ANDCC",-"CMP",-"ISEL" b := x&1 != 0 return c && b } + +func convertNeqBool32(x uint32) bool { + // ppc64:"ANDCC",-"CMPW",-"ISEL" + // ppc64le:"ANDCC",-"CMPW",-"ISEL" + // ppc64le/power9:"ANDCC",-"CMPW",-"ISEL" + return x&1 != 0 +} + +func convertEqBool32(x uint32) bool { + // ppc64:"ANDCC",-"CMPW","XOR",-"ISEL" + // ppc64le:"ANDCC",-"CMPW","XOR",-"ISEL" + // ppc64le/power9:"ANDCC","XOR",-"CMPW",-"ISEL" + return x&1 == 0 +} + +func convertNeqBool64(x uint64) bool { + // ppc64:"ANDCC",-"CMP",-"ISEL" + // ppc64le:"ANDCC",-"CMP",-"ISEL" + // ppc64le/power9:"ANDCC",-"CMP",-"ISEL" + return x&1 != 0 +} + +func convertEqBool64(x uint64) bool { + // ppc64:"ANDCC","XOR",-"CMP",-"ISEL" + // ppc64le:"ANDCC","XOR",-"CMP",-"ISEL" + // ppc64le/power9:"ANDCC","XOR",-"CMP",-"ISEL" + return x&1 == 0 +} diff --git a/test/codegen/clobberdead.go b/test/codegen/clobberdead.go index f8d964cba689a8..732be5f73b9954 100644 --- a/test/codegen/clobberdead.go +++ b/test/codegen/clobberdead.go @@ -1,6 +1,6 @@ // asmcheck -gcflags=-clobberdead -// +build amd64 +// +build amd64 arm64 // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -13,15 +13,18 @@ type T [2]*int // contain pointer, not SSA-able (so locals are not registerized) var p1, p2, p3 T func F() { - // 3735936685 is 0xdeaddead + // 3735936685 is 0xdeaddead. On ARM64 R27 is REGTMP. // clobber x, y at entry. not clobber z (stack object). - // amd64:`MOVL\t\$3735936685, ""\.x`, `MOVL\t\$3735936685, ""\.y`, -`MOVL\t\$3735936685, ""\.z` + // amd64:`MOVL\t\$3735936685, command-line-arguments\.x`, `MOVL\t\$3735936685, command-line-arguments\.y`, -`MOVL\t\$3735936685, command-line-arguments\.z` + // arm64:`MOVW\tR27, command-line-arguments\.x`, `MOVW\tR27, command-line-arguments\.y`, -`MOVW\tR27, command-line-arguments\.z` x, y, z := p1, p2, p3 addrTaken(&z) // x is dead at the call (the value of x is loaded before the CALL), y is not - // amd64:`MOVL\t\$3735936685, ""\.x`, -`MOVL\t\$3735936685, ""\.y` + // amd64:`MOVL\t\$3735936685, command-line-arguments\.x`, -`MOVL\t\$3735936685, command-line-arguments\.y` + // arm64:`MOVW\tR27, command-line-arguments\.x`, -`MOVW\tR27, command-line-arguments\.y` use(x) - // amd64:`MOVL\t\$3735936685, ""\.x`, `MOVL\t\$3735936685, ""\.y` + // amd64:`MOVL\t\$3735936685, command-line-arguments\.x`, `MOVL\t\$3735936685, command-line-arguments\.y` + // arm64:`MOVW\tR27, command-line-arguments\.x`, `MOVW\tR27, command-line-arguments\.y` use(y) } diff --git a/test/codegen/comparisons.go b/test/codegen/comparisons.go index 17dcd94ae1eff1..b1dba2482f20cf 100644 --- a/test/codegen/comparisons.go +++ b/test/codegen/comparisons.go @@ -45,7 +45,7 @@ func CompareString3(s string) bool { // Check that arrays compare use 2/4/8 byte compares func CompareArray1(a, b [2]byte) bool { - // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPW\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` // arm64:-`MOVBU\t` // ppc64le:-`MOVBZ\t` // s390x:-`MOVBZ\t` @@ -53,25 +53,25 @@ func CompareArray1(a, b [2]byte) bool { } func CompareArray2(a, b [3]uint16) bool { - // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` - // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPL\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPW\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray3(a, b [3]int16) bool { - // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` - // amd64:`CMPW\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPL\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPW\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray4(a, b [12]int8) bool { - // amd64:`CMPQ\t""[.+_a-z0-9]+\(SP\), [A-Z]` - // amd64:`CMPL\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPL\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } func CompareArray5(a, b [15]byte) bool { - // amd64:`CMPQ\t""[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` return a == b } @@ -84,6 +84,51 @@ func CompareArray6(a, b unsafe.Pointer) bool { return *((*[4]byte)(a)) != *((*[4]byte)(b)) } +// Check that some structs generate 2/4/8 byte compares. + +type T1 struct { + a [8]byte +} + +func CompareStruct1(s1, s2 T1) bool { + // amd64:`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:-`CALL` + return s1 == s2 +} + +type T2 struct { + a [16]byte +} + +func CompareStruct2(s1, s2 T2) bool { + // amd64:`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:-`CALL` + return s1 == s2 +} + +// Assert that a memequal call is still generated when +// inlining would increase binary size too much. + +type T3 struct { + a [24]byte +} + +func CompareStruct3(s1, s2 T3) bool { + // amd64:-`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CALL` + return s1 == s2 +} + +type T4 struct { + a [32]byte +} + +func CompareStruct4(s1, s2 T4) bool { + // amd64:-`CMPQ\tcommand-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]` + // amd64:`CALL` + return s1 == s2 +} + // -------------- // // Ordering // // -------------- // @@ -161,7 +206,7 @@ func CmpZero4(a int64, ptr *int) { } } -func CmpToZero(a, b, d int32, e, f int64) int32 { +func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 { // arm:`TST`,-`AND` // arm64:`TSTW`,-`AND` // 386:`TESTL`,-`ANDL` @@ -201,13 +246,17 @@ func CmpToZero(a, b, d int32, e, f int64) int32 { } else if c4 { return 5 } else if c5 { - return b + d + return 6 } else if c6 { - return a & d - } else if c7 { return 7 + } else if c7 { + return 9 } else if c8 { - return 8 + return 10 + } else if deOptC0 { + return b + d + } else if deOptC1 { + return a & d } else { return 0 } @@ -538,3 +587,81 @@ func CmpToOneU_ex2(a uint8, b uint16, c uint32, d uint64) int { } return 0 } + +// Check that small memequals are replaced with eq instructions + +func equalConstString1() bool { + a := string("A") + b := string("Z") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a == b +} + +func equalVarString1(a string) bool { + b := string("Z") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a[:1] == b +} + +func equalConstString2() bool { + a := string("AA") + b := string("ZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a == b +} + +func equalVarString2(a string) bool { + b := string("ZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a[:2] == b +} + +func equalConstString4() bool { + a := string("AAAA") + b := string("ZZZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a == b +} + +func equalVarString4(a string) bool { + b := string("ZZZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a[:4] == b +} + +func equalConstString8() bool { + a := string("AAAAAAAA") + b := string("ZZZZZZZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a == b +} + +func equalVarString8(a string) bool { + b := string("ZZZZZZZZ") + // amd64:-".*memequal" + // arm64:-".*memequal" + // ppc64:-".*memequal" + // ppc64le:-".*memequal" + return a[:8] == b +} diff --git a/test/codegen/copy.go b/test/codegen/copy.go index ea8a01f803f7b9..9b3bf75b7a35cd 100644 --- a/test/codegen/copy.go +++ b/test/codegen/copy.go @@ -103,6 +103,8 @@ func moveArchLowering1(b []byte, x *[1]byte) { _ = b[1] // amd64:-".*memmove" // arm64:-".*memmove" + // ppc64:-".*memmove" + // ppc64le:-".*memmove" copy(b, x[:]) } @@ -110,6 +112,8 @@ func moveArchLowering2(b []byte, x *[2]byte) { _ = b[2] // amd64:-".*memmove" // arm64:-".*memmove" + // ppc64:-".*memmove" + // ppc64le:-".*memmove" copy(b, x[:]) } @@ -117,6 +121,8 @@ func moveArchLowering4(b []byte, x *[4]byte) { _ = b[4] // amd64:-".*memmove" // arm64:-".*memmove" + // ppc64:-".*memmove" + // ppc64le:-".*memmove" copy(b, x[:]) } @@ -124,6 +130,8 @@ func moveArchLowering8(b []byte, x *[8]byte) { _ = b[8] // amd64:-".*memmove" // arm64:-".*memmove" + // ppc64:-".*memmove" + // ppc64le:-".*memmove" copy(b, x[:]) } diff --git a/test/codegen/ifaces.go b/test/codegen/ifaces.go new file mode 100644 index 00000000000000..d773845e8ee781 --- /dev/null +++ b/test/codegen/ifaces.go @@ -0,0 +1,21 @@ +// asmcheck + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package codegen + +type I interface { M() } + +func NopConvertIface(x I) I { + // amd64:-`.*runtime.convI2I` + return I(x) +} + +func NopConvertGeneric[T any](x T) T { + // amd64:-`.*runtime.convI2I` + return T(x) +} + +var NopConvertGenericIface = NopConvertGeneric[I] diff --git a/test/codegen/issue48054.go b/test/codegen/issue48054.go new file mode 100644 index 00000000000000..1f3a041044274f --- /dev/null +++ b/test/codegen/issue48054.go @@ -0,0 +1,31 @@ +// asmcheck + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package codegen + +func a(n string) bool { + // arm64:"CBZ" + if len(n) > 0 { + return true + } + return false +} + +func a2(n []int) bool { + // arm64:"CBZ" + if len(n) > 0 { + return true + } + return false +} + +func a3(n []int) bool { + // amd64:"TESTQ" + if len(n) < 1 { + return true + } + return false +} diff --git a/test/codegen/issue52635.go b/test/codegen/issue52635.go new file mode 100644 index 00000000000000..0e4d169081d124 --- /dev/null +++ b/test/codegen/issue52635.go @@ -0,0 +1,36 @@ +// asmcheck + +// Copyright 2022 The Go 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 optimized range memclr works with pointers to arrays. + +package codegen + +type T struct { + a *[10]int + b [10]int +} + +func (t *T) f() { + // amd64:".*runtime.memclrNoHeapPointers" + for i := range t.a { + t.a[i] = 0 + } + + // amd64:".*runtime.memclrNoHeapPointers" + for i := range *t.a { + t.a[i] = 0 + } + + // amd64:".*runtime.memclrNoHeapPointers" + for i := range t.a { + (*t.a)[i] = 0 + } + + // amd64:".*runtime.memclrNoHeapPointers" + for i := range *t.a { + (*t.a)[i] = 0 + } +} diff --git a/test/codegen/logic.go b/test/codegen/logic.go index 9afdfd760fcd10..50ce5f0cca8782 100644 --- a/test/codegen/logic.go +++ b/test/codegen/logic.go @@ -22,3 +22,11 @@ func andWithUse(x, y int) int { // use z by returning it return z } + +// Verify (OR x (NOT y)) rewrites to (ORN x y) where supported +func ornot(x, y int) int { + // ppc64:"ORN" + // ppc64le:"ORN" + z := x | ^y + return z +} diff --git a/test/codegen/mapaccess.go b/test/codegen/mapaccess.go index a914a0c766ece0..3d494e7cc7dab7 100644 --- a/test/codegen/mapaccess.go +++ b/test/codegen/mapaccess.go @@ -234,29 +234,28 @@ func mapCompoundAssignmentString() { var sinkAppend bool -// TODO: optimization is not applied because of mapslow flag. func mapAppendAssignmentInt8() { m := make(map[int8][]int8, 0) var k int8 = 0 - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], 1) - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], 1, 2, 3) a := []int8{7, 8, 9, 0} - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], a...) // Exceptions @@ -394,29 +393,28 @@ func mapAppendAssignmentInt64() { m[k] = append(m[k+1], 100) } -// TODO: optimization is not applied because of mapslow flag. func mapAppendAssignmentComplex128() { m := make(map[complex128][]complex128, 0) var k complex128 = 0 - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], 1) - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], 1, 2, 3) a := []complex128{7, 8, 9, 0} - // 386:".*mapaccess" - // amd64:".*mapaccess" - // arm:".*mapaccess" - // arm64:".*mapaccess" + // 386:-".*mapaccess" + // amd64:-".*mapaccess" + // arm:-".*mapaccess" + // arm64:-".*mapaccess" m[k] = append(m[k], a...) // Exceptions diff --git a/test/codegen/maps.go b/test/codegen/maps.go index dcb4a9381f7f66..ea3a70d1f07646 100644 --- a/test/codegen/maps.go +++ b/test/codegen/maps.go @@ -122,3 +122,33 @@ func MapClearSideEffect(m map[int]int) int { } return k } + +func MapLiteralSizing(x int) (map[int]int, map[int]int) { + // amd64:"MOVL\t[$]10," + m := map[int]int{ + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + } + // amd64:"MOVL\t[$]10," + n := map[int]int{ + 0: x, + 1: x, + 2: x, + 3: x, + 4: x, + 5: x, + 6: x, + 7: x, + 8: x, + 9: x, + } + return m, n +} diff --git a/test/codegen/math.go b/test/codegen/math.go index 04cb4e577da6ac..7c76d26b386d8c 100644 --- a/test/codegen/math.go +++ b/test/codegen/math.go @@ -11,6 +11,8 @@ import "math" var sink64 [8]float64 func approx(x float64) { + // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" + // amd64:"ROUNDSD\t[$]2" // s390x:"FIDBR\t[$]6" // arm64:"FRINTPD" // ppc64:"FRIP" @@ -18,6 +20,8 @@ func approx(x float64) { // wasm:"F64Ceil" sink64[0] = math.Ceil(x) + // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" + // amd64:"ROUNDSD\t[$]1" // s390x:"FIDBR\t[$]7" // arm64:"FRINTMD" // ppc64:"FRIM" @@ -31,6 +35,8 @@ func approx(x float64) { // ppc64le:"FRIN" sink64[2] = math.Round(x) + // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" + // amd64:"ROUNDSD\t[$]3" // s390x:"FIDBR\t[$]5" // arm64:"FRINTZD" // ppc64:"FRIZ" @@ -38,6 +44,8 @@ func approx(x float64) { // wasm:"F64Trunc" sink64[3] = math.Trunc(x) + // amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41" + // amd64:"ROUNDSD\t[$]0" // s390x:"FIDBR\t[$]4" // arm64:"FRINTND" // wasm:"F64Nearest" @@ -52,6 +60,8 @@ func sqrt(x float64) float64 { // mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD" // mips64/hardfloat:"SQRTD" mips64/softfloat:-"SQRTD" // wasm:"F64Sqrt" + // ppc64le:"FSQRT" + // ppc64:"FSQRT" return math.Sqrt(x) } @@ -63,6 +73,8 @@ func sqrt32(x float32) float32 { // mips/hardfloat:"SQRTF" mips/softfloat:-"SQRTF" // mips64/hardfloat:"SQRTF" mips64/softfloat:-"SQRTF" // wasm:"F32Sqrt" + // ppc64le:"FSQRTS" + // ppc64:"FSQRTS" return float32(math.Sqrt(float64(x))) } @@ -73,6 +85,7 @@ func abs(x, y float64) { // s390x:"LPDFR\t",-"MOVD\t" (no integer load/store) // ppc64:"FABS\t" // ppc64le:"FABS\t" + // riscv64:"FABSD\t" // wasm:"F64Abs" // arm/6:"ABSD\t" sink64[0] = math.Abs(x) @@ -96,6 +109,7 @@ func copysign(a, b, c float64) { // s390x:"CPSDR",-"MOVD" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" // wasm:"F64Copysign" sink64[0] = math.Copysign(a, b) @@ -103,6 +117,7 @@ func copysign(a, b, c float64) { // s390x:"LNDFR\t",-"MOVD\t" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" // arm64:"ORR", -"AND" sink64[1] = math.Copysign(c, -1) @@ -115,19 +130,37 @@ func copysign(a, b, c float64) { // s390x:"CPSDR\t",-"MOVD\t" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" sink64[3] = math.Copysign(-1, c) } func fma(x, y, z float64) float64 { + // amd64/v3:-".*x86HasFMA" // amd64:"VFMADD231SD" // arm/6:"FMULAD" // arm64:"FMADDD" // s390x:"FMADD" // ppc64:"FMADD" // ppc64le:"FMADD" + // riscv64:"FMADDD" return math.FMA(x, y, z) } +func fms(x, y, z float64) float64 { + // riscv64:"FMSUBD" + return math.FMA(x, y, -z) +} + +func fnma(x, y, z float64) float64 { + // riscv64:"FNMADDD" + return math.FMA(-x, y, z) +} + +func fnms(x, y, z float64) float64 { + // riscv64:"FNMSUBD" + return math.FMA(x, -y, -z) +} + func fromFloat64(f64 float64) uint64 { // amd64:"MOVQ\tX.*, [^X].*" // arm64:"FMOVD\tF.*, R.*" diff --git a/test/codegen/mathbits.go b/test/codegen/mathbits.go index 03012eff5d8444..0620766f5aea50 100644 --- a/test/codegen/mathbits.go +++ b/test/codegen/mathbits.go @@ -13,47 +13,62 @@ import "math/bits" // ----------------------- // func LeadingZeros(n uint) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3:"LZCNTQ", -"BSRQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"CNTLZD" + // ppc64:"CNTLZD" return bits.LeadingZeros(n) } func LeadingZeros64(n uint64) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3:"LZCNTQ", -"BSRQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"CNTLZD" + // ppc64:"CNTLZD" return bits.LeadingZeros64(n) } func LeadingZeros32(n uint32) int { - // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZW" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"CNTLZW" + // ppc64:"CNTLZW" return bits.LeadingZeros32(n) } func LeadingZeros16(n uint16) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"CNTLZD" + // ppc64:"CNTLZD" return bits.LeadingZeros16(n) } func LeadingZeros8(n uint8) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"CNTLZD" + // ppc64:"CNTLZD" return bits.LeadingZeros8(n) } @@ -62,16 +77,20 @@ func LeadingZeros8(n uint8) int { // --------------- // func Len(n uint) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3: "LZCNTQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"SUBC","CNTLZD" + // ppc64:"SUBC","CNTLZD" return bits.Len(n) } func Len64(n uint64) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3: "LZCNTQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -88,29 +107,38 @@ func SubFromLen64(n uint64) int { } func Len32(n uint32) int { - // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64: "CNTLZW" + // ppc64le: "CNTLZW" return bits.Len32(n) } func Len16(n uint16) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"SUBC","CNTLZD" + // ppc64:"SUBC","CNTLZD" return bits.Len16(n) } func Len8(n uint8) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" // wasm:"I64Clz" + // ppc64le:"SUBC","CNTLZD" + // ppc64:"SUBC","CNTLZD" return bits.Len8(n) } @@ -118,8 +146,9 @@ func Len8(n uint8) int { // bits.OnesCount // // -------------------- // -// TODO(register args) Restore a m d 6 4 :.*x86HasPOPCNT when only one ABI is tested. +// TODO(register args) Restore a m d 6 4 / v 1 :.*x86HasPOPCNT when only one ABI is tested. func OnesCount(n uint) int { + // amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT" // amd64:"POPCNTQ" // arm64:"VCNT","VUADDLV" // s390x:"POPCNT" @@ -130,6 +159,7 @@ func OnesCount(n uint) int { } func OnesCount64(n uint64) int { + // amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT" // amd64:"POPCNTQ" // arm64:"VCNT","VUADDLV" // s390x:"POPCNT" @@ -140,6 +170,7 @@ func OnesCount64(n uint64) int { } func OnesCount32(n uint32) int { + // amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT" // amd64:"POPCNTL" // arm64:"VCNT","VUADDLV" // s390x:"POPCNT" @@ -150,6 +181,7 @@ func OnesCount32(n uint32) int { } func OnesCount16(n uint16) int { + // amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT" // amd64:"POPCNTL" // arm64:"VCNT","VUADDLV" // s390x:"POPCNT" @@ -226,14 +258,16 @@ func RotateLeft32(n uint32) uint32 { return bits.RotateLeft32(n, 9) } -func RotateLeft16(n uint16) uint16 { +func RotateLeft16(n uint16, s int) uint16 { // amd64:"ROLW" 386:"ROLW" - return bits.RotateLeft16(n, 5) + // arm64:"RORW",-"CSEL" + return bits.RotateLeft16(n, s) } -func RotateLeft8(n uint8) uint8 { +func RotateLeft8(n uint8, s int) uint8 { // amd64:"ROLB" 386:"ROLB" - return bits.RotateLeft8(n, 5) + // arm64:"LSL","LSR",-"CSEL" + return bits.RotateLeft8(n, s) } func RotateLeftVariable(n uint, m int) uint { @@ -272,7 +306,8 @@ func RotateLeftVariable32(n uint32, m int) uint32 { // ------------------------ // func TrailingZeros(n uint) int { - // amd64:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // amd64/v1,amd64/v2:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // amd64/v3:"TZCNTQ" // arm:"CLZ" // arm64:"RBIT","CLZ" // s390x:"FLOGR" @@ -285,7 +320,8 @@ func TrailingZeros(n uint) int { } func TrailingZeros64(n uint64) int { - // amd64:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // amd64/v1,amd64/v2:"BSFQ","MOVL\t\\$64","CMOVQEQ" + // amd64/v3:"TZCNTQ" // arm64:"RBIT","CLZ" // s390x:"FLOGR" // ppc64/power8:"ANDN","POPCNTD" @@ -303,7 +339,8 @@ func TrailingZeros64Subtract(n uint64) int { } func TrailingZeros32(n uint32) int { - // amd64:"BTSQ\\t\\$32","BSFQ" + // amd64/v1,amd64/v2:"BTSQ\\t\\$32","BSFQ" + // amd64/v3:"TZCNTL" // arm:"CLZ" // arm64:"RBITW","CLZW" // s390x:"FLOGR","MOVWZ" @@ -343,7 +380,8 @@ func TrailingZeros8(n uint8) int { func IterateBits(n uint) int { i := 0 for n != 0 { - // amd64:"BSFQ",-"CMOVEQ" + // amd64/v1,amd64/v2:"BSFQ",-"CMOVEQ" + // amd64/v3:"TZCNTQ" i += bits.TrailingZeros(n) n &= n - 1 } @@ -353,7 +391,8 @@ func IterateBits(n uint) int { func IterateBits64(n uint64) int { i := 0 for n != 0 { - // amd64:"BSFQ",-"CMOVEQ" + // amd64/v1,amd64/v2:"BSFQ",-"CMOVEQ" + // amd64/v3:"TZCNTQ" i += bits.TrailingZeros64(n) n &= n - 1 } @@ -363,7 +402,8 @@ func IterateBits64(n uint64) int { func IterateBits32(n uint32) int { i := 0 for n != 0 { - // amd64:"BSFL",-"BTSQ" + // amd64/v1,amd64/v2:"BSFL",-"BTSQ" + // amd64/v3:"TZCNTL" i += bits.TrailingZeros32(n) n &= n - 1 } @@ -373,7 +413,8 @@ func IterateBits32(n uint32) int { func IterateBits16(n uint16) int { i := 0 for n != 0 { - // amd64:"BSFL",-"BTSL" + // amd64/v1,amd64/v2:"BSFL",-"BTSL" + // amd64/v3:"TZCNTL" // arm64:"RBITW","CLZW",-"ORR" i += bits.TrailingZeros16(n) n &= n - 1 @@ -384,7 +425,8 @@ func IterateBits16(n uint16) int { func IterateBits8(n uint8) int { i := 0 for n != 0 { - // amd64:"BSFL",-"BTSL" + // amd64/v1,amd64/v2:"BSFL",-"BTSL" + // amd64/v3:"TZCNTL" // arm64:"RBITW","CLZW",-"ORR" i += bits.TrailingZeros8(n) n &= n - 1 @@ -399,28 +441,40 @@ func IterateBits8(n uint8) int { func Add(x, y, ci uint) (r, co uint) { // arm64:"ADDS","ADCS","ADC",-"ADD\t",-"CMP" // amd64:"NEGL","ADCQ","SBBQ","NEGQ" + // ppc64: "ADDC", "ADDE", "ADDZE" + // ppc64le: "ADDC", "ADDE", "ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add(x, y, ci) } func AddC(x, ci uint) (r, co uint) { // arm64:"ADDS","ADCS","ADC",-"ADD\t",-"CMP" // amd64:"NEGL","ADCQ","SBBQ","NEGQ" + // ppc64: "ADDC", "ADDE", "ADDZE" + // ppc64le: "ADDC", "ADDE", "ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add(x, 7, ci) } func AddZ(x, y uint) (r, co uint) { // arm64:"ADDS","ADC",-"ADCS",-"ADD\t",-"CMP" // amd64:"ADDQ","SBBQ","NEGQ",-"NEGL",-"ADCQ" + // ppc64: "ADDC", -"ADDE", "ADDZE" + // ppc64le: "ADDC", -"ADDE", "ADDZE" // s390x:"ADDC",-"ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add(x, y, 0) } func AddR(x, y, ci uint) uint { // arm64:"ADDS","ADCS",-"ADD\t",-"CMP" // amd64:"NEGL","ADCQ",-"SBBQ",-"NEGQ" + // ppc64: "ADDC", "ADDE", -"ADDZE" + // ppc64le: "ADDC", "ADDE", -"ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD",-"SLTU" r, _ := bits.Add(x, y, ci) return r } @@ -441,6 +495,7 @@ func Add64(x, y, ci uint64) (r, co uint64) { // ppc64: "ADDC", "ADDE", "ADDZE" // ppc64le: "ADDC", "ADDE", "ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add64(x, y, ci) } @@ -450,24 +505,27 @@ func Add64C(x, ci uint64) (r, co uint64) { // ppc64: "ADDC", "ADDE", "ADDZE" // ppc64le: "ADDC", "ADDE", "ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add64(x, 7, ci) } func Add64Z(x, y uint64) (r, co uint64) { // arm64:"ADDS","ADC",-"ADCS",-"ADD\t",-"CMP" // amd64:"ADDQ","SBBQ","NEGQ",-"NEGL",-"ADCQ" - // ppc64: "ADDC", "ADDE", "ADDZE" - // ppc64le: "ADDC", "ADDE", "ADDZE" + // ppc64: "ADDC", -"ADDE", "ADDZE" + // ppc64le: "ADDC", -"ADDE", "ADDZE" // s390x:"ADDC",-"ADDC\t[$]-1," + // riscv64: "ADD","SLTU" return bits.Add64(x, y, 0) } func Add64R(x, y, ci uint64) uint64 { // arm64:"ADDS","ADCS",-"ADD\t",-"CMP" // amd64:"NEGL","ADCQ",-"SBBQ",-"NEGQ" - // ppc64: "ADDC", "ADDE", "ADDZE" - // ppc64le: "ADDC", "ADDE", "ADDZE" + // ppc64: "ADDC", "ADDE", -"ADDZE" + // ppc64le: "ADDC", "ADDE", -"ADDZE" // s390x:"ADDE","ADDC\t[$]-1," + // riscv64: "ADD",-"SLTU" r, _ := bits.Add64(x, y, ci) return r } @@ -476,13 +534,22 @@ func Add64M(p, q, r *[3]uint64) { r[0], c = bits.Add64(p[0], q[0], c) // arm64:"ADCS",-"ADD\t",-"CMP" // amd64:"ADCQ",-"NEGL",-"SBBQ",-"NEGQ" - // ppc64: "ADDC", "ADDE", "ADDZE" - // ppc64le: "ADDC", "ADDE", "ADDZE" + // ppc64: -"ADDC", "ADDE", -"ADDZE" + // ppc64le: -"ADDC", "ADDE", -"ADDZE" // s390x:"ADDE",-"ADDC\t[$]-1," r[1], c = bits.Add64(p[1], q[1], c) r[2], c = bits.Add64(p[2], q[2], c) } +func Add64MSaveC(p, q, r, c *[2]uint64) { + // ppc64: "ADDC\tR", "ADDZE" + // ppc64le: "ADDC\tR", "ADDZE" + r[0], c[0] = bits.Add64(p[0], q[0], 0) + // ppc64: "ADDC\t[$]-1", "ADDE", "ADDZE" + // ppc64le: "ADDC\t[$]-1", "ADDE", "ADDZE" + r[1], c[1] = bits.Add64(p[1], q[1], c[0]) +} + func Add64PanicOnOverflowEQ(a, b uint64) uint64 { r, c := bits.Add64(a, b, 0) // s390x:"BRC\t[$]3,",-"ADDE" @@ -553,28 +620,40 @@ func Add64MPanicOnOverflowGT(a, b [2]uint64) [2]uint64 { func Sub(x, y, ci uint) (r, co uint) { // amd64:"NEGL","SBBQ","NEGQ" // arm64:"NEGS","SBCS","NGC","NEG",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", "SUBE", "SUBZE", "NEG" // s390x:"SUBE" + // riscv64: "SUB","SLTU" return bits.Sub(x, y, ci) } func SubC(x, ci uint) (r, co uint) { // amd64:"NEGL","SBBQ","NEGQ" // arm64:"NEGS","SBCS","NGC","NEG",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", "SUBE", "SUBZE", "NEG" // s390x:"SUBE" + // riscv64: "SUB","SLTU" return bits.Sub(x, 7, ci) } func SubZ(x, y uint) (r, co uint) { // amd64:"SUBQ","SBBQ","NEGQ",-"NEGL" // arm64:"SUBS","NGC","NEG",-"SBCS",-"ADD",-"SUB\t",-"CMP" + // ppc64:"SUBC", -"SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", -"SUBE", "SUBZE", "NEG" // s390x:"SUBC" + // riscv64: "SUB","SLTU" return bits.Sub(x, y, 0) } func SubR(x, y, ci uint) uint { // amd64:"NEGL","SBBQ",-"NEGQ" // arm64:"NEGS","SBCS",-"NGC",-"NEG\t",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", -"SUBZE", -"NEG" + // ppc64le:"SUBC", "SUBE", -"SUBZE", -"NEG" // s390x:"SUBE" + // riscv64: "SUB",-"SLTU" r, _ := bits.Sub(x, y, ci) return r } @@ -583,6 +662,8 @@ func SubM(p, q, r *[3]uint) { r[0], c = bits.Sub(p[0], q[0], c) // amd64:"SBBQ",-"NEGL",-"NEGQ" // arm64:"SBCS",-"NEGS",-"NGC",-"NEG",-"ADD",-"SUB",-"CMP" + // ppc64:-"SUBC", "SUBE", -"SUBZE", -"NEG" + // ppc64le:-"SUBC", "SUBE", -"SUBZE", -"NEG" // s390x:"SUBE" r[1], c = bits.Sub(p[1], q[1], c) r[2], c = bits.Sub(p[2], q[2], c) @@ -591,28 +672,40 @@ func SubM(p, q, r *[3]uint) { func Sub64(x, y, ci uint64) (r, co uint64) { // amd64:"NEGL","SBBQ","NEGQ" // arm64:"NEGS","SBCS","NGC","NEG",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", "SUBE", "SUBZE", "NEG" // s390x:"SUBE" + // riscv64: "SUB","SLTU" return bits.Sub64(x, y, ci) } func Sub64C(x, ci uint64) (r, co uint64) { // amd64:"NEGL","SBBQ","NEGQ" // arm64:"NEGS","SBCS","NGC","NEG",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", "SUBE", "SUBZE", "NEG" // s390x:"SUBE" + // riscv64: "SUB","SLTU" return bits.Sub64(x, 7, ci) } func Sub64Z(x, y uint64) (r, co uint64) { // amd64:"SUBQ","SBBQ","NEGQ",-"NEGL" // arm64:"SUBS","NGC","NEG",-"SBCS",-"ADD",-"SUB\t",-"CMP" + // ppc64:"SUBC", -"SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC", -"SUBE", "SUBZE", "NEG" // s390x:"SUBC" + // riscv64: "SUB","SLTU" return bits.Sub64(x, y, 0) } func Sub64R(x, y, ci uint64) uint64 { // amd64:"NEGL","SBBQ",-"NEGQ" // arm64:"NEGS","SBCS",-"NGC",-"NEG\t",-"ADD",-"SUB",-"CMP" + // ppc64:"SUBC", "SUBE", -"SUBZE", -"NEG" + // ppc64le:"SUBC", "SUBE", -"SUBZE", -"NEG" // s390x:"SUBE" + // riscv64: "SUB",-"SLTU" r, _ := bits.Sub64(x, y, ci) return r } @@ -626,6 +719,15 @@ func Sub64M(p, q, r *[3]uint64) { r[2], c = bits.Sub64(p[2], q[2], c) } +func Sub64MSaveC(p, q, r, c *[2]uint64) { + // ppc64:"SUBC\tR\\d+, R\\d+,", "SUBZE", "NEG" + // ppc64le:"SUBC\tR\\d+, R\\d+,", "SUBZE", "NEG" + r[0], c[0] = bits.Sub64(p[0], q[0], 0) + // ppc64:"SUBC\tR\\d+, [$]0,", "SUBE", "SUBZE", "NEG" + // ppc64le:"SUBC\tR\\d+, [$]0,", "SUBE", "SUBZE", "NEG" + r[1], c[1] = bits.Sub64(p[1], q[1], c[0]) +} + func Sub64PanicOnOverflowEQ(a, b uint64) uint64 { r, b := bits.Sub64(a, b, 0) // s390x:"BRC\t[$]12,",-"ADDE",-"SUBE" @@ -710,9 +812,24 @@ func Mul64(x, y uint64) (hi, lo uint64) { // ppc64le:"MULHDU","MULLD" // s390x:"MLGR" // mips64: "MULVU" + // riscv64:"MULHU","MUL" return bits.Mul64(x, y) } +func Mul64HiOnly(x, y uint64) uint64 { + // arm64:"UMULH",-"MUL" + // riscv64:"MULHU",-"MUL\t" + hi, _ := bits.Mul64(x, y) + return hi +} + +func Mul64LoOnly(x, y uint64) uint64 { + // arm64:"MUL",-"UMULH" + // riscv64:"MUL\t",-"MULHU" + _, lo := bits.Mul64(x, y) + return lo +} + // --------------- // // bits.Div* // // --------------- // diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go index d74dae07f5b548..8143b6bed5beac 100644 --- a/test/codegen/memcombine.go +++ b/test/codegen/memcombine.go @@ -11,110 +11,110 @@ import ( "runtime" ) -var sink64 uint64 -var sink32 uint32 -var sink16 uint16 - // ------------- // // Loading // // ------------- // -func load_le64(b []byte) { +func load_le64(b []byte) uint64 { // amd64:`MOVQ\s\(.*\),`,-`MOV[BWL]\t[^$]`,-`OR` // s390x:`MOVDBR\s\(.*\),` // arm64:`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]` // ppc64le:`MOVD\s`,-`MOV[BHW]Z` - sink64 = binary.LittleEndian.Uint64(b) + return binary.LittleEndian.Uint64(b) } -func load_le64_idx(b []byte, idx int) { +func load_le64_idx(b []byte, idx int) uint64 { // amd64:`MOVQ\s\(.*\)\(.*\*1\),`,-`MOV[BWL]\t[^$]`,-`OR` // s390x:`MOVDBR\s\(.*\)\(.*\*1\),` // arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BHW]` // ppc64le:`MOVD\s`,-`MOV[BHW]Z\s` - sink64 = binary.LittleEndian.Uint64(b[idx:]) + return binary.LittleEndian.Uint64(b[idx:]) } -func load_le32(b []byte) { +func load_le32(b []byte) uint32 { // amd64:`MOVL\s\(.*\),`,-`MOV[BW]`,-`OR` // 386:`MOVL\s\(.*\),`,-`MOV[BW]`,-`OR` // s390x:`MOVWBR\s\(.*\),` // arm64:`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]` // ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s` - sink32 = binary.LittleEndian.Uint32(b) + return binary.LittleEndian.Uint32(b) } -func load_le32_idx(b []byte, idx int) { +func load_le32_idx(b []byte, idx int) uint32 { // amd64:`MOVL\s\(.*\)\(.*\*1\),`,-`MOV[BW]`,-`OR` // 386:`MOVL\s\(.*\)\(.*\*1\),`,-`MOV[BW]`,-`OR` // s390x:`MOVWBR\s\(.*\)\(.*\*1\),` // arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BH]` // ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s` - sink32 = binary.LittleEndian.Uint32(b[idx:]) + return binary.LittleEndian.Uint32(b[idx:]) } -func load_le16(b []byte) { +func load_le16(b []byte) uint16 { // amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR` // ppc64le:`MOVHZ\s`,-`MOVBZ` // arm64:`MOVHU\s\(R[0-9]+\),`,-`MOVB` // s390x:`MOVHBR\s\(.*\),` - sink16 = binary.LittleEndian.Uint16(b) + return binary.LittleEndian.Uint16(b) } -func load_le16_idx(b []byte, idx int) { +func load_le16_idx(b []byte, idx int) uint16 { // amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR` // ppc64le:`MOVHZ\s`,-`MOVBZ` // arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB` // s390x:`MOVHBR\s\(.*\)\(.*\*1\),` - sink16 = binary.LittleEndian.Uint16(b[idx:]) + return binary.LittleEndian.Uint16(b[idx:]) } -func load_be64(b []byte) { - // amd64:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` +func load_be64(b []byte) uint64 { + // amd64/v1,amd64/v2:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` + // amd64/v3:`MOVBEQ` // s390x:`MOVD\s\(.*\),` // arm64:`REV`,`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]`,-`REVW`,-`REV16W` // ppc64le:`MOVDBR`,-`MOV[BHW]Z` - sink64 = binary.BigEndian.Uint64(b) + return binary.BigEndian.Uint64(b) } -func load_be64_idx(b []byte, idx int) { - // amd64:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` +func load_be64_idx(b []byte, idx int) uint64 { + // amd64/v1,amd64/v2:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` + // amd64/v3: `MOVBEQ\t\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*` // s390x:`MOVD\s\(.*\)\(.*\*1\),` // arm64:`REV`,`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[WHB]`,-`REVW`,-`REV16W` // ppc64le:`MOVDBR`,-`MOV[BHW]Z` - sink64 = binary.BigEndian.Uint64(b[idx:]) + return binary.BigEndian.Uint64(b[idx:]) } -func load_be32(b []byte) { - // amd64:`BSWAPL`,-`MOV[BW]`,-`OR` +func load_be32(b []byte) uint32 { + // amd64/v1,amd64/v2:`BSWAPL`,-`MOV[BW]`,-`OR` + // amd64/v3: `MOVBEL` // s390x:`MOVWZ\s\(.*\),` // arm64:`REVW`,`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]`,-`REV16W` // ppc64le:`MOVWBR`,-`MOV[BH]Z` - sink32 = binary.BigEndian.Uint32(b) + return binary.BigEndian.Uint32(b) } -func load_be32_idx(b []byte, idx int) { - // amd64:`BSWAPL`,-`MOV[BW]`,-`OR` +func load_be32_idx(b []byte, idx int) uint32 { + // amd64/v1,amd64/v2:`BSWAPL`,-`MOV[BW]`,-`OR` + // amd64/v3: `MOVBEL\t\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*` // s390x:`MOVWZ\s\(.*\)\(.*\*1\),` // arm64:`REVW`,`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[HB]`,-`REV16W` // ppc64le:`MOVWBR`,-`MOV[BH]Z` - sink32 = binary.BigEndian.Uint32(b[idx:]) + return binary.BigEndian.Uint32(b[idx:]) } -func load_be16(b []byte) { +func load_be16(b []byte) uint16 { // amd64:`ROLW\s\$8`,-`MOVB`,-`OR` // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\),`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVHZ\s\(.*\),`,-`OR`,-`ORW`,-`SLD`,-`SLW` - sink16 = binary.BigEndian.Uint16(b) + return binary.BigEndian.Uint16(b) } -func load_be16_idx(b []byte, idx int) { +func load_be16_idx(b []byte, idx int) uint16 { // amd64:`ROLW\s\$8`,-`MOVB`,-`OR` // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVHZ\s\(.*\)\(.*\*1\),`,-`OR`,-`ORW`,-`SLD`,-`SLW` - sink16 = binary.BigEndian.Uint16(b[idx:]) + return binary.BigEndian.Uint16(b[idx:]) } func load_le_byte2_uint16(s []byte) uint16 { @@ -179,7 +179,8 @@ func load_be_byte4_uint32(s []byte) uint32 { func load_be_byte4_uint32_inv(s []byte) uint32 { // arm64:`MOVWU\t\(R[0-9]+\)`,`REVW`,-`ORR`,-`REV16W`,-`MOV[BH]` - // amd64:`MOVL\s\([A-Z]+\)`,`BSWAPL`,-`MOV[BW]`,-`OR` + // amd64/v1,amd64/v2:`MOVL\s\([A-Z]+\)`,`BSWAPL`,-`MOV[BW]`,-`OR` + // amd64/v3: `MOVBEL` return uint32(s[3]) | uint32(s[2])<<8 | uint32(s[1])<<16 | uint32(s[0])<<24 } @@ -191,7 +192,8 @@ func load_be_byte8_uint64(s []byte) uint64 { func load_be_byte8_uint64_inv(s []byte) uint64 { // arm64:`MOVD\t\(R[0-9]+\)`,`REV`,-`ORR`,-`REVW`,-`REV16W`,-`MOV[BHW]` - // amd64:`MOVQ\s\([A-Z]+\),\s[A-Z]+`,`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` + // amd64/v1,amd64/v2:`MOVQ\s\([A-Z]+\),\s[A-Z]+`,`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR` + // amd64/v3: `MOVBEQ` // ppc64le:`MOVDBR\t\(R[0-9]+\)`,-`MOV[BHW]Z` return uint64(s[7]) | uint64(s[6])<<8 | uint64(s[5])<<16 | uint64(s[4])<<24 | uint64(s[3])<<32 | uint64(s[2])<<40 | uint64(s[1])<<48 | uint64(s[0])<<56 } @@ -333,7 +335,7 @@ func load_op_no_merge(p, q *int) { // Make sure offsets are folded into loads and stores. func offsets_fold(_, a [20]byte) (b [20]byte) { - // arm64:`MOVD\t""\.a\+[0-9]+\(FP\), R[0-9]+`,`MOVD\tR[0-9]+, ""\.b\+[0-9]+\(FP\)` + // arm64:`MOVD\tcommand-line-arguments\.a\+[0-9]+\(FP\), R[0-9]+`,`MOVD\tR[0-9]+, command-line-arguments\.b\+[0-9]+\(FP\)` b = a return } @@ -351,20 +353,33 @@ func safe_point(p, q *[2]*int) { // Storing // // ------------- // -func store_le64(b []byte) { +func store_le64(b []byte, x uint64) { // amd64:`MOVQ\s.*\(.*\)$`,-`SHR.` // arm64:`MOVD`,-`MOV[WBH]` // ppc64le:`MOVD\s`,-`MOV[BHW]\s` // s390x:`MOVDBR\s.*\(.*\)$` - binary.LittleEndian.PutUint64(b, sink64) + binary.LittleEndian.PutUint64(b, x) } -func store_le64_idx(b []byte, idx int) { +func store_le64_idx(b []byte, x uint64, idx int) { // amd64:`MOVQ\s.*\(.*\)\(.*\*1\)$`,-`SHR.` // arm64:`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]` // ppc64le:`MOVD\s`,-`MOV[BHW]\s` // s390x:`MOVDBR\s.*\(.*\)\(.*\*1\)$` - binary.LittleEndian.PutUint64(b[idx:], sink64) + binary.LittleEndian.PutUint64(b[idx:], x) +} + +func store_le64_idx2(dst []byte, d, length, offset int) []byte { + a := dst[d : d+length] + b := dst[d-offset:] + // amd64:`MOVQ\s.*\(.*\)\(.*\*1\)$`,-`SHR.` + binary.LittleEndian.PutUint64(a, binary.LittleEndian.Uint64(b)) + return dst +} + +func store_le64_idx_const(b []byte, idx int) { + // amd64:`MOVQ\s\$123, \(.*\)\(.*\*1\)$` + binary.LittleEndian.PutUint64(b[idx:], 123) } func store_le64_load(b []byte, x *[8]byte) { @@ -376,84 +391,112 @@ func store_le64_load(b []byte, x *[8]byte) { binary.LittleEndian.PutUint64(b, binary.LittleEndian.Uint64(x[:])) } -func store_le32(b []byte) { +func store_le32(b []byte, x uint32) { // amd64:`MOVL\s` // arm64:`MOVW`,-`MOV[BH]` // ppc64le:`MOVW\s` // s390x:`MOVWBR\s.*\(.*\)$` - binary.LittleEndian.PutUint32(b, sink32) + binary.LittleEndian.PutUint32(b, x) } -func store_le32_idx(b []byte, idx int) { +func store_le32_idx(b []byte, x uint32, idx int) { // amd64:`MOVL\s` // arm64:`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]` // ppc64le:`MOVW\s` // s390x:`MOVWBR\s.*\(.*\)\(.*\*1\)$` - binary.LittleEndian.PutUint32(b[idx:], sink32) + binary.LittleEndian.PutUint32(b[idx:], x) +} + +func store_le32_idx_const(b []byte, idx int) { + // amd64:`MOVL\s\$123, \(.*\)\(.*\*1\)$` + binary.LittleEndian.PutUint32(b[idx:], 123) } -func store_le16(b []byte) { +func store_le16(b []byte, x uint16) { // amd64:`MOVW\s` // arm64:`MOVH`,-`MOVB` // ppc64le:`MOVH\s` // s390x:`MOVHBR\s.*\(.*\)$` - binary.LittleEndian.PutUint16(b, sink16) + binary.LittleEndian.PutUint16(b, x) } -func store_le16_idx(b []byte, idx int) { +func store_le16_idx(b []byte, x uint16, idx int) { // amd64:`MOVW\s` // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB` // ppc64le:`MOVH\s` // s390x:`MOVHBR\s.*\(.*\)\(.*\*1\)$` - binary.LittleEndian.PutUint16(b[idx:], sink16) + binary.LittleEndian.PutUint16(b[idx:], x) } -func store_be64(b []byte) { - // amd64:`BSWAPQ`,-`SHR.` +func store_le16_idx_const(b []byte, idx int) { + // amd64:`MOVW\s\$123, \(.*\)\(.*\*1\)$` + binary.LittleEndian.PutUint16(b[idx:], 123) +} + +func store_be64(b []byte, x uint64) { + // amd64/v1,amd64/v2:`BSWAPQ`,-`SHR.` + // amd64/v3: `MOVBEQ` // arm64:`MOVD`,`REV`,-`MOV[WBH]`,-`REVW`,-`REV16W` // ppc64le:`MOVDBR` // s390x:`MOVD\s.*\(.*\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint64(b, sink64) + binary.BigEndian.PutUint64(b, x) } -func store_be64_idx(b []byte, idx int) { - // amd64:`BSWAPQ`,-`SHR.` +func store_be64_idx(b []byte, x uint64, idx int) { + // amd64/v1,amd64/v2:`BSWAPQ`,-`SHR.` + // amd64/v3:`MOVBEQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)` // arm64:`REV`,`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`,-`REV16W`,-`REVW` // ppc64le:`MOVDBR` // s390x:`MOVD\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint64(b[idx:], sink64) + binary.BigEndian.PutUint64(b[idx:], x) } -func store_be32(b []byte) { - // amd64:`BSWAPL`,-`SHR.` +func store_be32(b []byte, x uint32) { + // amd64/v1,amd64/v2:`BSWAPL`,-`SHR.` + // amd64/v3:`MOVBEL` // arm64:`MOVW`,`REVW`,-`MOV[BH]`,-`REV16W` // ppc64le:`MOVWBR` // s390x:`MOVW\s.*\(.*\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint32(b, sink32) + binary.BigEndian.PutUint32(b, x) +} + +func store_be64_load(b, x *[8]byte) { + // arm64:-`REV` + // amd64:-`BSWAPQ` + binary.BigEndian.PutUint64(b[:], binary.BigEndian.Uint64(x[:])) +} + +func store_be32_load(b, x *[8]byte) { + // arm64:-`REVW` + // amd64:-`BSWAPL` + binary.BigEndian.PutUint32(b[:], binary.BigEndian.Uint32(x[:])) } -func store_be32_idx(b []byte, idx int) { - // amd64:`BSWAPL`,-`SHR.` +func store_be32_idx(b []byte, x uint32, idx int) { + // amd64/v1,amd64/v2:`BSWAPL`,-`SHR.` + // amd64/v3:`MOVBEL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)` // arm64:`REVW`,`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`,-`REV16W` // ppc64le:`MOVWBR` // s390x:`MOVW\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint32(b[idx:], sink32) + binary.BigEndian.PutUint32(b[idx:], x) } -func store_be16(b []byte) { - // amd64:`ROLW\s\$8`,-`SHR.` +func store_be16(b []byte, x uint16) { + // amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.` + // amd64/v3:`MOVBEW`,-`ROLW` // arm64:`MOVH`,`REV16W`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVH\s.*\(.*\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint16(b, sink16) + binary.BigEndian.PutUint16(b, x) } -func store_be16_idx(b []byte, idx int) { - // amd64:`ROLW\s\$8`,-`SHR.` +func store_be16_idx(b []byte, x uint16, idx int) { + // amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.` + // amd64/v3:`MOVBEW\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)` // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVH\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s` - binary.BigEndian.PutUint16(b[idx:], sink16) + binary.BigEndian.PutUint16(b[idx:], x) } func store_le_byte_2(b []byte, val uint16) { @@ -489,21 +532,24 @@ func store_le_byte_8(b []byte, val uint64) { func store_be_byte_2(b []byte, val uint16) { _ = b[2] // arm64:`REV16W`,`MOVH\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB` - // amd64:`MOVW\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB` + // amd64/v1,amd64/v2:`MOVW\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB` + // amd64/v3: `MOVBEW` b[1], b[2] = byte(val>>8), byte(val) } func store_be_byte_4(b []byte, val uint32) { _ = b[4] // arm64:`REVW`,`MOVW\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`REV16W` - // amd64:`MOVL\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB`,-`MOVW` + // amd64/v1,amd64/v2:`MOVL\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB`,-`MOVW` + // amd64/v3:`MOVBEL\s[A-Z]+,\s1\([A-Z]+\)` b[1], b[2], b[3], b[4] = byte(val>>24), byte(val>>16), byte(val>>8), byte(val) } func store_be_byte_8(b []byte, val uint64) { _ = b[8] // arm64:`REV`,`MOVD\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB`,-`MOVH`,-`MOVW`,-`REV16W`,-`REVW` - // amd64:`MOVQ\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB`,-`MOVW`,-`MOVL` + // amd64/v1,amd64/v2:`MOVQ\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB`,-`MOVW`,-`MOVL` + // amd64/v3:`MOVBEQ\s[A-Z]+,\s1\([A-Z]+\)`, -`MOVBEL` b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8] = byte(val>>56), byte(val>>48), byte(val>>40), byte(val>>32), byte(val>>24), byte(val>>16), byte(val>>8), byte(val) } diff --git a/test/codegen/memops.go b/test/codegen/memops.go index fb8208f9844db7..7e59d88560ecc8 100644 --- a/test/codegen/memops.go +++ b/test/codegen/memops.go @@ -13,23 +13,23 @@ var x32 [2]uint32 var x64 [2]uint64 func compMem1() int { - // amd64:`CMPB\t"".x\+1\(SB\), [$]0` + // amd64:`CMPB\tcommand-line-arguments.x\+1\(SB\), [$]0` if x[1] { return 1 } - // amd64:`CMPB\t"".x8\+1\(SB\), [$]7` + // amd64:`CMPB\tcommand-line-arguments.x8\+1\(SB\), [$]7` if x8[1] == 7 { return 1 } - // amd64:`CMPW\t"".x16\+2\(SB\), [$]7` + // amd64:`CMPW\tcommand-line-arguments.x16\+2\(SB\), [$]7` if x16[1] == 7 { return 1 } - // amd64:`CMPL\t"".x32\+4\(SB\), [$]7` + // amd64:`CMPL\tcommand-line-arguments.x32\+4\(SB\), [$]7` if x32[1] == 7 { return 1 } - // amd64:`CMPQ\t"".x64\+8\(SB\), [$]7` + // amd64:`CMPQ\tcommand-line-arguments.x64\+8\(SB\), [$]7` if x64[1] == 7 { return 1 } diff --git a/test/codegen/noextend.go b/test/codegen/noextend.go index 424fd2008d25fb..d8e2917bf276c7 100644 --- a/test/codegen/noextend.go +++ b/test/codegen/noextend.go @@ -15,19 +15,18 @@ var val32 [8]uint32 var val16 [8]uint16 var val8 [8]uint8 -// ----------------------------- // -// avoid zero/sign extensions // -// ----------------------------- // +// Avoid zero/sign extensions following a load +// which has extended the value correctly. +// Note: No tests are done for int8 since +// an extra extension is usually needed due to +// no signed byte load. -func set16(x8 int8, u8 uint8, y8 int8, z8 uint8) { +func set16(x8 int8, u8 *uint8, y8 int8, z8 uint8) { // Truncate not needed, load does sign/zero extend - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - sval16[0] = int16(x8) // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - val16[0] = uint16(u8) + val16[0] = uint16(*u8) // AND not needed due to size // ppc64:-"ANDCC" @@ -36,217 +35,186 @@ func set16(x8 int8, u8 uint8, y8 int8, z8 uint8) { // ppc64:-"ANDCC" // ppc64le:-"ANDCC" - val16[1] = 255 & uint16(u8+z8) + val16[1] = 255 & uint16(*u8+z8) } -func shiftidx(x8 int8, u8 uint8, x16 int16, u16 uint16, x32 int32, u32 uint32) { - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - sval16[0] = int16(val16[x8>>1]) +func shiftidx(u8 *uint8, x16 *int16, u16 *uint16) { // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - val16[0] = uint16(sval16[u8>>2]) + val16[0] = uint16(sval16[*u8>>2]) // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - sval16[1] = int16(val16[x16>>1]) + sval16[1] = int16(val16[*x16>>1]) // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - val16[1] = uint16(sval16[u16>>2]) + val16[1] = uint16(sval16[*u16>>2]) } -func setnox(x8 int8, u8 uint8, y8 int8, z8 uint8, x16 int16, u16 uint16, x32 int32, u32 uint32) { - // Truncate not needed due to sign/zero extension on load - - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - sval16[0] = int16(x8) +func setnox(x8 int8, u8 *uint8, y8 *int8, z8 *uint8, x16 *int16, u16 *uint16, x32 *int32, u32 *uint32) { // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - val16[0] = uint16(u8) + val16[0] = uint16(*u8) // AND not needed due to size // ppc64:-"ANDCC" // ppc64le:-"ANDCC" - sval16[1] = 255 & int16(x8+y8) + sval16[1] = 255 & int16(x8+*y8) // ppc64:-"ANDCC" // ppc64le:-"ANDCC" - val16[1] = 255 & uint16(u8+z8) - - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - sval32[0] = int32(x8) + val16[1] = 255 & uint16(*u8+*z8) // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - sval32[1] = int32(x16) + sval32[1] = int32(*x16) //ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" //ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - val32[0] = uint32(u8) + val32[0] = uint32(*u8) // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - val32[1] = uint32(u16) - - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - sval64[0] = int64(x8) + val32[1] = uint32(*u16) // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - sval64[1] = int64(x16) + sval64[1] = int64(*x16) // ppc64:-"MOVW\tR\\d+,\\sR\\d+" // ppc64le:-"MOVW\tR\\d+,\\sR\\d+" - sval64[2] = int64(x32) + sval64[2] = int64(*x32) //ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" //ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - val64[0] = uint64(u8) + val64[0] = uint64(*u8) // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - val64[1] = uint64(u16) + val64[1] = uint64(*u16) // ppc64:-"MOVWZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVWZ\tR\\d+,\\sR\\d+" - val64[2] = uint64(u32) + val64[2] = uint64(*u32) } -func cmp16(x8 int8, u8 uint8, x32 int32, u32 uint32, x64 int64, u64 uint64) bool { - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - if int16(x8) == sval16[0] { - return true - } +func cmp16(u8 *uint8, x32 *int32, u32 *uint32, x64 *int64, u64 *uint64) bool { // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - if uint16(u8) == val16[0] { + if uint16(*u8) == val16[0] { return true } // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if uint16(u32>>16) == val16[0] { + if uint16(*u32>>16) == val16[0] { return true } // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if uint16(u64>>48) == val16[0] { + if uint16(*u64>>48) == val16[0] { return true } // Verify the truncates are using the correct sign. // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if int16(x32) == sval16[0] { + if int16(*x32) == sval16[0] { return true } // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - if uint16(u32) == val16[0] { + if uint16(*u32) == val16[0] { return true } // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if int16(x64) == sval16[0] { + if int16(*x64) == sval16[0] { return true } // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - if uint16(u64) == val16[0] { + if uint16(*u64) == val16[0] { return true } return false } -func cmp32(x8 int8, u8 uint8, x16 int16, u16 uint16, x64 int64, u64 uint64) bool { - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - if int32(x8) == sval32[0] { - return true - } +func cmp32(u8 *uint8, x16 *int16, u16 *uint16, x64 *int64, u64 *uint64) bool { // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - if uint32(u8) == val32[0] { + if uint32(*u8) == val32[0] { return true } // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - if int32(x16) == sval32[0] { + if int32(*x16) == sval32[0] { return true } // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if uint32(u16) == val32[0] { + if uint32(*u16) == val32[0] { return true } // Verify the truncates are using the correct sign. // ppc64:-"MOVWZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVWZ\tR\\d+,\\sR\\d+" - if int32(x64) == sval32[0] { + if int32(*x64) == sval32[0] { return true } // ppc64:-"MOVW\tR\\d+,\\sR\\d+" // ppc64le:-"MOVW\tR\\d+,\\sR\\d+" - if uint32(u64) == val32[0] { + if uint32(*u64) == val32[0] { return true } return false } -func cmp64(x8 int8, u8 uint8, x16 int16, u16 uint16, x32 int32, u32 uint32) bool { - // ppc64:-"MOVB\tR\\d+,\\sR\\d+" - // ppc64le:-"MOVB\tR\\d+,\\sR\\d+" - if int64(x8) == sval64[0] { - return true - } +func cmp64(u8 *uint8, x16 *int16, u16 *uint16, x32 *int32, u32 *uint32) bool { // ppc64:-"MOVBZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVBZ\tR\\d+,\\sR\\d+" - if uint64(u8) == val64[0] { + if uint64(*u8) == val64[0] { return true } // ppc64:-"MOVH\tR\\d+,\\sR\\d+" // ppc64le:-"MOVH\tR\\d+,\\sR\\d+" - if int64(x16) == sval64[0] { + if int64(*x16) == sval64[0] { return true } // ppc64:-"MOVHZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVHZ\tR\\d+,\\sR\\d+" - if uint64(u16) == val64[0] { + if uint64(*u16) == val64[0] { return true } // ppc64:-"MOVW\tR\\d+,\\sR\\d+" // ppc64le:-"MOVW\tR\\d+,\\sR\\d+" - if int64(x32) == sval64[0] { + if int64(*x32) == sval64[0] { return true } // ppc64:-"MOVWZ\tR\\d+,\\sR\\d+" // ppc64le:-"MOVWZ\tR\\d+,\\sR\\d+" - if uint64(u32) == val64[0] { + if uint64(*u32) == val64[0] { return true } return false diff --git a/test/codegen/rotate.go b/test/codegen/rotate.go index 519cc8326338be..b22288f82a7610 100644 --- a/test/codegen/rotate.go +++ b/test/codegen/rotate.go @@ -18,6 +18,7 @@ func rot64(x uint64) uint64 { // amd64:"ROLQ\t[$]7" // ppc64:"ROTL\t[$]7" // ppc64le:"ROTL\t[$]7" + // loong64: "ROTRV\t[$]57" a += x<<7 | x>>57 // amd64:"ROLQ\t[$]8" @@ -25,6 +26,7 @@ func rot64(x uint64) uint64 { // s390x:"RISBGZ\t[$]0, [$]63, [$]8, " // ppc64:"ROTL\t[$]8" // ppc64le:"ROTL\t[$]8" + // loong64: "ROTRV\t[$]56" a += x<<8 + x>>56 // amd64:"ROLQ\t[$]9" @@ -32,10 +34,19 @@ func rot64(x uint64) uint64 { // s390x:"RISBGZ\t[$]0, [$]63, [$]9, " // ppc64:"ROTL\t[$]9" // ppc64le:"ROTL\t[$]9" + // loong64: "ROTRV\t[$]55" a += x<<9 ^ x>>55 - // s390x:"RISBGZ\t[$]0, [$]63, [$]7, " - // arm64:"ROR\t[$]57" // TODO this is not great line numbering, but then again, the instruction did appear + // amd64:"ROLQ\t[$]10" + // arm64:"ROR\t[$]54" + // s390x:"RISBGZ\t[$]0, [$]63, [$]10, " + // ppc64:"ROTL\t[$]10" + // ppc64le:"ROTL\t[$]10" + // arm64:"ROR\t[$]54" + // s390x:"RISBGZ\t[$]0, [$]63, [$]10, " + // loong64: "ROTRV\t[$]54" + a += bits.RotateLeft64(x, 10) + return a } @@ -46,6 +57,7 @@ func rot32(x uint32) uint32 { // arm:"MOVW\tR\\d+@>25" // ppc64:"ROTLW\t[$]7" // ppc64le:"ROTLW\t[$]7" + // loong64: "ROTR\t[$]25" a += x<<7 | x>>25 // amd64:`ROLL\t[$]8` @@ -54,6 +66,7 @@ func rot32(x uint32) uint32 { // s390x:"RLL\t[$]8" // ppc64:"ROTLW\t[$]8" // ppc64le:"ROTLW\t[$]8" + // loong64: "ROTR\t[$]24" a += x<<8 + x>>24 // amd64:"ROLL\t[$]9" @@ -62,10 +75,20 @@ func rot32(x uint32) uint32 { // s390x:"RLL\t[$]9" // ppc64:"ROTLW\t[$]9" // ppc64le:"ROTLW\t[$]9" + // loong64: "ROTR\t[$]23" a += x<<9 ^ x>>23 - // s390x:"RLL\t[$]7" - // arm64:"RORW\t[$]25" // TODO this is not great line numbering, but then again, the instruction did appear + // amd64:"ROLL\t[$]10" + // arm:"MOVW\tR\\d+@>22" + // arm64:"RORW\t[$]22" + // s390x:"RLL\t[$]10" + // ppc64:"ROTLW\t[$]10" + // ppc64le:"ROTLW\t[$]10" + // arm64:"RORW\t[$]22" + // s390x:"RLL\t[$]10" + // loong64: "ROTR\t[$]22" + a += bits.RotateLeft32(x, 10) + return a } @@ -108,12 +131,18 @@ func rot64nc(x uint64, z uint) uint64 { z &= 63 - // amd64:"ROLQ" - // ppc64:"ROTL" - // ppc64le:"ROTL" + // amd64:"ROLQ",-"AND" + // arm64:"ROR","NEG",-"AND" + // ppc64:"ROTL",-"NEG",-"AND" + // ppc64le:"ROTL",-"NEG",-"AND" + // loong64: "ROTRV", -"AND" a += x<>(64-z) - // amd64:"RORQ" + // amd64:"RORQ",-"AND" + // arm64:"ROR",-"NEG",-"AND" + // ppc64:"ROTL","NEG",-"AND" + // ppc64le:"ROTL","NEG",-"AND" + // loong64: "ROTRV", -"AND" a += x>>z | x<<(64-z) return a @@ -124,12 +153,18 @@ func rot32nc(x uint32, z uint) uint32 { z &= 31 - // amd64:"ROLL" - // ppc64:"ROTLW" - // ppc64le:"ROTLW" + // amd64:"ROLL",-"AND" + // arm64:"ROR","NEG",-"AND" + // ppc64:"ROTLW",-"NEG",-"AND" + // ppc64le:"ROTLW",-"NEG",-"AND" + // loong64: "ROTR", -"AND" a += x<>(32-z) - // amd64:"RORL" + // amd64:"RORL",-"AND" + // arm64:"ROR",-"NEG",-"AND" + // ppc64:"ROTLW","NEG",-"AND" + // ppc64le:"ROTLW","NEG",-"AND" + // loong64: "ROTR", -"AND" a += x>>z | x<<(32-z) return a @@ -140,10 +175,10 @@ func rot16nc(x uint16, z uint) uint16 { z &= 15 - // amd64:"ROLW" + // amd64:"ROLW",-"ANDQ" a += x<>(16-z) - // amd64:"RORW" + // amd64:"RORW",-"ANDQ" a += x>>z | x<<(16-z) return a @@ -154,10 +189,10 @@ func rot8nc(x uint8, z uint) uint8 { z &= 7 - // amd64:"ROLB" + // amd64:"ROLB",-"ANDQ" a += x<>(8-z) - // amd64:"RORB" + // amd64:"RORB",-"ANDQ" a += x>>z | x<<(8-z) return a @@ -169,6 +204,14 @@ func f32(x uint32) uint32 { return rot32nc(x, 7) } +func doubleRotate(x uint64) uint64 { + x = (x << 5) | (x >> 59) + // amd64:"ROLQ\t[$]15" + // arm64:"ROR\t[$]49" + x = (x << 10) | (x >> 54) + return x +} + // --------------------------------------- // // Combined Rotate + Masking operations // // --------------------------------------- // @@ -199,15 +242,38 @@ func checkMaskedRotate32(a []uint32, r int) { i++ // ppc64le: "RLWNM\tR[0-9]+, R[0-9]+, [$]16, [$]23, R[0-9]+" // ppc64: "RLWNM\tR[0-9]+, R[0-9]+, [$]16, [$]23, R[0-9]+" - a[i] = bits.RotateLeft32(a[3], r) & 0xFF00 + a[i] = bits.RotateLeft32(a[i], r) & 0xFF00 i++ // ppc64le: "RLWNM\tR[0-9]+, R[0-9]+, [$]20, [$]11, R[0-9]+" // ppc64: "RLWNM\tR[0-9]+, R[0-9]+, [$]20, [$]11, R[0-9]+" - a[i] = bits.RotateLeft32(a[3], r) & 0xFFF00FFF + a[i] = bits.RotateLeft32(a[i], r) & 0xFFF00FFF i++ // ppc64le: "RLWNM\t[$]4, R[0-9]+, [$]20, [$]11, R[0-9]+" // ppc64: "RLWNM\t[$]4, R[0-9]+, [$]20, [$]11, R[0-9]+" - a[i] = bits.RotateLeft32(a[3], 4) & 0xFFF00FFF + a[i] = bits.RotateLeft32(a[i], 4) & 0xFFF00FFF i++ } + +// combined arithmetic and rotate on arm64 +func checkArithmeticWithRotate(a *[1000]uint64) { + // arm64: "AND\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[2] = a[1] & bits.RotateLeft64(a[0], 13) + // arm64: "ORR\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[5] = a[4] | bits.RotateLeft64(a[3], 13) + // arm64: "EOR\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[8] = a[7] ^ bits.RotateLeft64(a[6], 13) + // arm64: "MVN\tR[0-9]+@>51, R[0-9]+" + a[10] = ^bits.RotateLeft64(a[9], 13) + // arm64: "BIC\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[13] = a[12] &^ bits.RotateLeft64(a[11], 13) + // arm64: "EON\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[16] = a[15] ^ ^bits.RotateLeft64(a[14], 13) + // arm64: "ORN\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[19] = a[18] | ^bits.RotateLeft64(a[17], 13) + // arm64: "TST\tR[0-9]+@>51, R[0-9]+" + if a[18]&bits.RotateLeft64(a[19], 13) == 0 { + a[20] = 1 + } + +} diff --git a/test/codegen/select.go b/test/codegen/select.go index 4426924b36afa4..82f6d1c7efe7f0 100644 --- a/test/codegen/select.go +++ b/test/codegen/select.go @@ -10,7 +10,7 @@ func f() { ch1 := make(chan int) ch2 := make(chan int) for { - // amd64:-`MOVQ\t[$]0, ""..autotmp_3` + // amd64:-`MOVQ\t[$]0, command-line-arguments..autotmp_3` select { case <-ch1: case <-ch2: diff --git a/test/codegen/shift.go b/test/codegen/shift.go index 06f6f1247399b8..5a2391358c31d5 100644 --- a/test/codegen/shift.go +++ b/test/codegen/shift.go @@ -6,91 +6,179 @@ package codegen +// ------------------ // +// constant shifts // +// ------------------ // + +func lshConst64x64(v int64) int64 { + // riscv64:"SLLI",-"AND",-"SLTIU" + // ppc64le:"SLD" + // ppc64:"SLD" + return v << uint64(33) +} + +func rshConst64Ux64(v uint64) uint64 { + // riscv64:"SRLI",-"AND",-"SLTIU" + // ppc64le:"SRD" + // ppc64:"SRD" + return v >> uint64(33) +} + +func rshConst64x64(v int64) int64 { + // riscv64:"SRAI",-"OR",-"SLTIU" + // ppc64le:"SRAD" + // ppc64:"SRAD" + return v >> uint64(33) +} + +func lshConst32x64(v int32) int32 { + // riscv64:"SLLI",-"AND",-"SLTIU" + // ppc64le:"SLW" + // ppc64:"SLW" + return v << uint64(29) +} + +func rshConst32Ux64(v uint32) uint32 { + // riscv64:"SRLI",-"AND",-"SLTIU" + // ppc64le:"SRW" + // ppc64:"SRW" + return v >> uint64(29) +} + +func rshConst32x64(v int32) int32 { + // riscv64:"SRAI",-"OR",-"SLTIU" + // ppc64le:"SRAW" + // ppc64:"SRAW" + return v >> uint64(29) +} + +func lshConst64x32(v int64) int64 { + // riscv64:"SLLI",-"AND",-"SLTIU" + // ppc64le:"SLD" + // ppc64:"SLD" + return v << uint32(33) +} + +func rshConst64Ux32(v uint64) uint64 { + // riscv64:"SRLI",-"AND",-"SLTIU" + // ppc64le:"SRD" + // ppc64:"SRD" + return v >> uint32(33) +} + +func rshConst64x32(v int64) int64 { + // riscv64:"SRAI",-"OR",-"SLTIU" + // ppc64le:"SRAD" + // ppc64:"SRAD" + return v >> uint32(33) +} + // ------------------ // // masked shifts // // ------------------ // func lshMask64x64(v int64, s uint64) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SLL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSL",-"AND" return v << (s & 63) } func rshMask64Ux64(v uint64, s uint64) uint64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SRL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSR",-"AND",-"CSEL" return v >> (s & 63) } func rshMask64x64(v int64, s uint64) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-ORN",-"ISEL" + // riscv64:"SRA",-"OR",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"ASR",-"AND",-"CSEL" return v >> (s & 63) } func lshMask32x64(v int32, s uint64) int32 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ISEL",-"ORN" // ppc64:"ISEL",-"ORN" + // ppc64le:"ISEL",-"ORN" + // riscv64:"SLL","AND","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSL",-"AND" return v << (s & 63) } func rshMask32Ux64(v uint32, s uint64) uint32 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ISEL",-"ORN" // ppc64:"ISEL",-"ORN" + // ppc64le:"ISEL",-"ORN" + // riscv64:"SRL","AND","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSR",-"AND" return v >> (s & 63) } func rshMask32x64(v int32, s uint64) int32 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ISEL",-"ORN" // ppc64:"ISEL",-"ORN" + // ppc64le:"ISEL",-"ORN" + // riscv64:"SRA","OR","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"ASR",-"AND" return v >> (s & 63) } func lshMask64x32(v int64, s uint32) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN" // ppc64:"ANDCC",-"ORN" + // ppc64le:"ANDCC",-"ORN" + // riscv64:"SLL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSL",-"AND" return v << (s & 63) } func rshMask64Ux32(v uint64, s uint32) uint64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN" // ppc64:"ANDCC",-"ORN" + // ppc64le:"ANDCC",-"ORN" + // riscv64:"SRL",-"AND\t",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"LSR",-"AND",-"CSEL" return v >> (s & 63) } func rshMask64x32(v int64, s uint32) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SRA",-"OR",-"SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" + // arm64:"ASR",-"AND",-"CSEL" return v >> (s & 63) } func lshMask64x32Ext(v int64, s int32) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SLL","AND","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" return v << uint(s&63) } func rshMask64Ux32Ext(v uint64, s int32) uint64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SRL","AND","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" return v >> uint(s&63) } func rshMask64x32Ext(v int64, s int32) int64 { - // s390x:-"RISBGZ",-"AND",-"LOCGR" - // ppc64le:"ANDCC",-"ORN",-"ISEL" // ppc64:"ANDCC",-"ORN",-"ISEL" + // ppc64le:"ANDCC",-"ORN",-"ISEL" + // riscv64:"SRA","OR","SLTIU" + // s390x:-"RISBGZ",-"AND",-"LOCGR" return v >> uint(s&63) } @@ -126,33 +214,119 @@ func lshSignedMasked(v8 int8, v16 int16, v32 int32, v64 int64, x int) { // bounded shifts // // ------------------ // -func rshGuarded64(v int64, s uint) int64 { +func lshGuarded64(v int64, s uint) int64 { if s < 64 { + // riscv64:"SLL",-"AND",-"SLTIU" // s390x:-"RISBGZ",-"AND",-"LOCGR" // wasm:-"Select",-".*LtU" - return v >> s + // arm64:"LSL",-"CSEL" + return v << s } panic("shift too large") } func rshGuarded64U(v uint64, s uint) uint64 { if s < 64 { + // riscv64:"SRL",-"AND",-"SLTIU" // s390x:-"RISBGZ",-"AND",-"LOCGR" // wasm:-"Select",-".*LtU" + // arm64:"LSR",-"CSEL" return v >> s } panic("shift too large") } -func lshGuarded64(v int64, s uint) int64 { +func rshGuarded64(v int64, s uint) int64 { if s < 64 { + // riscv64:"SRA",-"OR",-"SLTIU" // s390x:-"RISBGZ",-"AND",-"LOCGR" // wasm:-"Select",-".*LtU" - return v << s + // arm64:"ASR",-"CSEL" + return v >> s } panic("shift too large") } +func provedUnsignedShiftLeft(val64 uint64, val32 uint32, val16 uint16, val8 uint8, shift int) (r1 uint64, r2 uint32, r3 uint16, r4 uint8) { + if shift >= 0 && shift < 64 { + // arm64:"LSL",-"CSEL" + r1 = val64 << shift + } + if shift >= 0 && shift < 32 { + // arm64:"LSL",-"CSEL" + r2 = val32 << shift + } + if shift >= 0 && shift < 16 { + // arm64:"LSL",-"CSEL" + r3 = val16 << shift + } + if shift >= 0 && shift < 8 { + // arm64:"LSL",-"CSEL" + r4 = val8 << shift + } + return r1, r2, r3, r4 +} + +func provedSignedShiftLeft(val64 int64, val32 int32, val16 int16, val8 int8, shift int) (r1 int64, r2 int32, r3 int16, r4 int8) { + if shift >= 0 && shift < 64 { + // arm64:"LSL",-"CSEL" + r1 = val64 << shift + } + if shift >= 0 && shift < 32 { + // arm64:"LSL",-"CSEL" + r2 = val32 << shift + } + if shift >= 0 && shift < 16 { + // arm64:"LSL",-"CSEL" + r3 = val16 << shift + } + if shift >= 0 && shift < 8 { + // arm64:"LSL",-"CSEL" + r4 = val8 << shift + } + return r1, r2, r3, r4 +} + +func provedUnsignedShiftRight(val64 uint64, val32 uint32, val16 uint16, val8 uint8, shift int) (r1 uint64, r2 uint32, r3 uint16, r4 uint8) { + if shift >= 0 && shift < 64 { + // arm64:"LSR",-"CSEL" + r1 = val64 >> shift + } + if shift >= 0 && shift < 32 { + // arm64:"LSR",-"CSEL" + r2 = val32 >> shift + } + if shift >= 0 && shift < 16 { + // arm64:"LSR",-"CSEL" + r3 = val16 >> shift + } + if shift >= 0 && shift < 8 { + // arm64:"LSR",-"CSEL" + r4 = val8 >> shift + } + return r1, r2, r3, r4 +} + +func provedSignedShiftRight(val64 int64, val32 int32, val16 int16, val8 int8, shift int) (r1 int64, r2 int32, r3 int16, r4 int8) { + if shift >= 0 && shift < 64 { + // arm64:"ASR",-"CSEL" + r1 = val64 >> shift + } + if shift >= 0 && shift < 32 { + // arm64:"ASR",-"CSEL" + r2 = val32 >> shift + } + if shift >= 0 && shift < 16 { + // arm64:"ASR",-"CSEL" + r3 = val16 >> shift + } + if shift >= 0 && shift < 8 { + // arm64:"ASR",-"CSEL" + r4 = val8 >> shift + } + return r1, r2, r3, r4 +} + func checkUnneededTrunc(tab *[100000]uint32, d uint64, v uint32, h uint16, b byte) (uint32, uint64) { // ppc64le:-".*RLWINM",-".*RLDICR",".*CLRLSLDI" @@ -275,17 +449,17 @@ func checkShiftAndMask32(v []uint32) { } func checkMergedShifts32(a [256]uint32, b [256]uint64, u uint32, v uint32) { - //ppc64le: -"CLRLSLDI", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]29, R[0-9]+" - //ppc64: -"CLRLSLDI", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]29, R[0-9]+" + // ppc64le: -"CLRLSLDI", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]29, R[0-9]+" + // ppc64: -"CLRLSLDI", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]29, R[0-9]+" a[0] = a[uint8(v>>24)] - //ppc64le: -"CLRLSLDI", "RLWNM\t[$]11, R[0-9]+, [$]21, [$]28, R[0-9]+" - //ppc64: -"CLRLSLDI", "RLWNM\t[$]11, R[0-9]+, [$]21, [$]28, R[0-9]+" + // ppc64le: -"CLRLSLDI", "RLWNM\t[$]11, R[0-9]+, [$]21, [$]28, R[0-9]+" + // ppc64: -"CLRLSLDI", "RLWNM\t[$]11, R[0-9]+, [$]21, [$]28, R[0-9]+" b[0] = b[uint8(v>>24)] - //ppc64le: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+" - //ppc64: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+" + // ppc64le: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+" + // ppc64: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+" b[1] = b[(v>>20)&0xFF] - //ppc64le: -"SLD", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]28, R[0-9]+" - //ppc64: -"SLD", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]28, R[0-9]+" + // ppc64le: -"SLD", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]28, R[0-9]+" + // ppc64: -"SLD", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]28, R[0-9]+" b[2] = b[v>>25] } @@ -301,3 +475,12 @@ func check128bitShifts(x, y uint64, bits uint) (uint64, uint64) { shl := x<>ŝ return shr, shl } + +func checkShiftToMask(u []uint64, s []int64) { + // amd64:-"SHR",-"SHL","ANDQ" + u[0] = u[0] >> 5 << 5 + // amd64:-"SAR",-"SHL","ANDQ" + s[0] = s[0] >> 5 << 5 + // amd64:-"SHR",-"SHL","ANDQ" + u[1] = u[1] << 5 >> 5 +} diff --git a/test/codegen/slices.go b/test/codegen/slices.go index d20aa9eddfa444..fa4142d76770ba 100644 --- a/test/codegen/slices.go +++ b/test/codegen/slices.go @@ -6,6 +6,8 @@ package codegen +import "unsafe" + // This file contains code generation tests related to the handling of // slice types. @@ -17,6 +19,8 @@ package codegen func SliceClear(s []int) []int { // amd64:`.*memclrNoHeapPointers` + // ppc64le:`.*memclrNoHeapPointers` + // ppc64:`.*memclrNoHeapPointers` for i := range s { s[i] = 0 } @@ -25,6 +29,8 @@ func SliceClear(s []int) []int { func SliceClearPointers(s []*int) []*int { // amd64:`.*memclrHasPointers` + // ppc64le:`.*memclrHasPointers` + // ppc64:`.*memclrHasPointers` for i := range s { s[i] = nil } @@ -41,6 +47,12 @@ func SliceExtensionConst(s []int) []int { // amd64:`.*runtime\.memclrNoHeapPointers` // amd64:-`.*runtime\.makeslice` // amd64:-`.*runtime\.panicmakeslicelen` + // ppc64le:`.*runtime\.memclrNoHeapPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64le:-`.*runtime\.panicmakeslicelen` + // ppc64:`.*runtime\.memclrNoHeapPointers` + // ppc64:-`.*runtime\.makeslice` + // ppc64:-`.*runtime\.panicmakeslicelen` return append(s, make([]int, 1<<2)...) } @@ -48,6 +60,12 @@ func SliceExtensionConstInt64(s []int) []int { // amd64:`.*runtime\.memclrNoHeapPointers` // amd64:-`.*runtime\.makeslice` // amd64:-`.*runtime\.panicmakeslicelen` + // ppc64le:`.*runtime\.memclrNoHeapPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64le:-`.*runtime\.panicmakeslicelen` + // ppc64:`.*runtime\.memclrNoHeapPointers` + // ppc64:-`.*runtime\.makeslice` + // ppc64:-`.*runtime\.panicmakeslicelen` return append(s, make([]int, int64(1<<2))...) } @@ -55,6 +73,12 @@ func SliceExtensionConstUint64(s []int) []int { // amd64:`.*runtime\.memclrNoHeapPointers` // amd64:-`.*runtime\.makeslice` // amd64:-`.*runtime\.panicmakeslicelen` + // ppc64le:`.*runtime\.memclrNoHeapPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64le:-`.*runtime\.panicmakeslicelen` + // ppc64:`.*runtime\.memclrNoHeapPointers` + // ppc64:-`.*runtime\.makeslice` + // ppc64:-`.*runtime\.panicmakeslicelen` return append(s, make([]int, uint64(1<<2))...) } @@ -62,18 +86,32 @@ func SliceExtensionConstUint(s []int) []int { // amd64:`.*runtime\.memclrNoHeapPointers` // amd64:-`.*runtime\.makeslice` // amd64:-`.*runtime\.panicmakeslicelen` + // ppc64le:`.*runtime\.memclrNoHeapPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64le:-`.*runtime\.panicmakeslicelen` + // ppc64:`.*runtime\.memclrNoHeapPointers` + // ppc64:-`.*runtime\.makeslice` + // ppc64:-`.*runtime\.panicmakeslicelen` return append(s, make([]int, uint(1<<2))...) } func SliceExtensionPointer(s []*int, l int) []*int { // amd64:`.*runtime\.memclrHasPointers` // amd64:-`.*runtime\.makeslice` + // ppc64le:`.*runtime\.memclrHasPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64:`.*runtime\.memclrHasPointers` + // ppc64:-`.*runtime\.makeslice` return append(s, make([]*int, l)...) } func SliceExtensionVar(s []byte, l int) []byte { // amd64:`.*runtime\.memclrNoHeapPointers` // amd64:-`.*runtime\.makeslice` + // ppc64le:`.*runtime\.memclrNoHeapPointers` + // ppc64le:-`.*runtime\.makeslice` + // ppc64:`.*runtime\.memclrNoHeapPointers` + // ppc64:-`.*runtime\.makeslice` return append(s, make([]byte, l)...) } @@ -114,6 +152,12 @@ func SliceMakeCopyLen(s []int) []int { // amd64:`.*runtime\.mallocgc` // amd64:`.*runtime\.memmove` // amd64:-`.*runtime\.makeslice` + // ppc64le:`.*runtime\.mallocgc` + // ppc64le:`.*runtime\.memmove` + // ppc64le:-`.*runtime\.makeslice` + // ppc64:`.*runtime\.mallocgc` + // ppc64:`.*runtime\.memmove` + // ppc64:-`.*runtime\.makeslice` a := make([]int, len(s)) copy(a, s) return a @@ -123,6 +167,12 @@ func SliceMakeCopyLenPtr(s []*int) []*int { // amd64:`.*runtime\.makeslicecopy` // amd64:-`.*runtime\.makeslice\(` // amd64:-`.*runtime\.typedslicecopy + // ppc64le:`.*runtime\.makeslicecopy` + // ppc64le:-`.*runtime\.makeslice\(` + // ppc64le:-`.*runtime\.typedslicecopy + // ppc64:`.*runtime\.makeslicecopy` + // ppc64:-`.*runtime\.makeslice\(` + // ppc64:-`.*runtime\.typedslicecopy a := make([]*int, len(s)) copy(a, s) return a @@ -368,3 +418,16 @@ func SliceWithSubtractBound(a []int, b int) []int { // ppc64:"SUBC",-"NEG" return a[(3 - b):] } + +// --------------------------------------- // +// Code generation for unsafe.Slice // +// --------------------------------------- // + +func Slice1(p *byte, i int) []byte { + // amd64:-"MULQ" + return unsafe.Slice(p, i) +} +func Slice0(p *struct{}, i int) []struct{} { + // amd64:-"MULQ" + return unsafe.Slice(p, i) +} diff --git a/test/codegen/strings.go b/test/codegen/strings.go index 19e1dbda51c9be..a2c2fc0a629b0c 100644 --- a/test/codegen/strings.go +++ b/test/codegen/strings.go @@ -15,7 +15,7 @@ func CountRunes(s string) int { // Issue #24923 } func ToByteSlice() []byte { // Issue #24698 - // amd64:`LEAQ\ttype\.\[3\]uint8` + // amd64:`LEAQ\ttype:\[3\]uint8` // amd64:`CALL\truntime\.newobject` // amd64:-`.*runtime.stringtoslicebyte` return []byte("foo") @@ -45,7 +45,7 @@ func ConstantLoad() { // 7306073769690871863 = 0x6564636261393837 // amd64:`MOVQ\t\$3978425819141910832`,`MOVQ\t\$7306073769690871863` // 386:`MOVL\t\$858927408, \(`,`DUFFCOPY` - // arm64:`MOVD\t\$3978425819141910832`,`MOVD\t\$1650538808`,`MOVD\t\$25699`,`MOVD\t\$101` + // arm64:`MOVD\t\$3978425819141910832`,`MOVD\t\$7306073769690871863`,`MOVD\t\$15` // wasm:`I64Const\t\$3978425819141910832`,`I64Store\t\$0`,`I64Const\t\$7306073769690871863`,`I64Store\t\$7` bsink = []byte("0123456789abcde") diff --git a/test/codegen/switch.go b/test/codegen/switch.go index 2ac817d14c8494..c3c24e2e112497 100644 --- a/test/codegen/switch.go +++ b/test/codegen/switch.go @@ -20,3 +20,82 @@ func f(x string) int { return -3 } } + +// use jump tables for 8+ int cases +func square(x int) int { + // amd64:`JMP\s\(.*\)\(.*\)$` + // arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$` + switch x { + case 1: + return 1 + case 2: + return 4 + case 3: + return 9 + case 4: + return 16 + case 5: + return 25 + case 6: + return 36 + case 7: + return 49 + case 8: + return 64 + default: + return x * x + } +} + +// use jump tables for 8+ string lengths +func length(x string) int { + // amd64:`JMP\s\(.*\)\(.*\)$` + // arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$` + switch x { + case "a": + return 1 + case "bb": + return 2 + case "ccc": + return 3 + case "dddd": + return 4 + case "eeeee": + return 5 + case "ffffff": + return 6 + case "ggggggg": + return 7 + case "hhhhhhhh": + return 8 + default: + return len(x) + } +} + +// Use single-byte ordered comparisons for binary searching strings. +// See issue 53333. +func mimetype(ext string) string { + // amd64: `CMPB\s1\(.*\), \$104$`,-`cmpstring` + // arm64: `MOVB\s1\(R.*\), R.*$`, `CMPW\s\$104, R.*$`, -`cmpstring` + switch ext { + // amd64: `CMPL\s\(.*\), \$1836345390$` + // arm64: `CMPW\s\$1836345390, R.*$` + case ".htm": + return "A" + // amd64: `CMPL\s\(.*\), \$1953457454$` + // arm64: `CMPW\s\$1953457454, R.*$` + case ".eot": + return "B" + // amd64: `CMPL\s\(.*\), \$1735815982$` + // arm64: `CMPW\s\$1735815982, R.*$` + case ".svg": + return "C" + // amd64: `CMPL\s\(.*\), \$1718907950$` + // arm64: `CMPW\s\$1718907950, R.*$` + case ".ttf": + return "D" + default: + return "" + } +} diff --git a/test/codegen/zerosize.go b/test/codegen/zerosize.go index 292c5a018b24f5..ecf33054617578 100644 --- a/test/codegen/zerosize.go +++ b/test/codegen/zerosize.go @@ -12,12 +12,12 @@ package codegen func zeroSize() { c := make(chan struct{}) - // amd64:`MOVQ\t\$0, ""\.s\+56\(SP\)` + // amd64:`MOVQ\t\$0, command-line-arguments\.s\+56\(SP\)` var s *int // force s to be a stack object, also use some (fixed) stack space g(&s, 1, 2, 3, 4, 5) - // amd64:`LEAQ\t""\..*\+55\(SP\)` + // amd64:`LEAQ\tcommand-line-arguments\..*\+55\(SP\)` c <- struct{}{} } diff --git a/test/complit1.go b/test/complit1.go index 7c2a4e2996d516..8cbcd63ee0d176 100644 --- a/test/complit1.go +++ b/test/complit1.go @@ -46,20 +46,20 @@ var ( _ = &T{0, 0, "", nil} // ok _ = &T{i: 0, f: 0, s: "", next: {}} // ERROR "missing type in composite literal|omit types within composite literal" _ = &T{0, 0, "", {}} // ERROR "missing type in composite literal|omit types within composite literal" - _ = TP{i: 0, f: 0, s: "", next: {}} // ERROR "invalid composite literal type TP|omit types within composite literal" + _ = TP{i: 0, f: 0, s: ""} // ERROR "invalid composite literal type TP" _ = &Ti{} // ERROR "invalid composite literal type Ti|expected.*type for composite literal" ) type M map[T]T var ( - _ = M{{i:1}: {i:2}} - _ = M{T{i:1}: {i:2}} - _ = M{{i:1}: T{i:2}} - _ = M{T{i:1}: T{i:2}} + _ = M{{i: 1}: {i: 2}} + _ = M{T{i: 1}: {i: 2}} + _ = M{{i: 1}: T{i: 2}} + _ = M{T{i: 1}: T{i: 2}} ) -type S struct { s [1]*M1 } +type S struct{ s [1]*M1 } type M1 map[S]int -var _ = M1{{s:[1]*M1{&M1{{}:1}}}:2} +var _ = M1{{s: [1]*M1{&M1{{}: 1}}}: 2} diff --git a/test/const7.go b/test/const7.go index 9ffd678fc5d882..8b252a24cf81d4 100644 --- a/test/const7.go +++ b/test/const7.go @@ -24,12 +24,12 @@ import ( // which declares an untyped constant of the given length. // testProg compiles this package and checks for the absence or // presence of a constant literal error. -func testProg(dir, name string, G_option, length int, ok bool) { +func testProg(dir, name string, length int, msg string) { var buf bytes.Buffer fmt.Fprintf(&buf, - "package %s; const _ = %s // %d digits", - name, strings.Repeat("9", length), length, + "package %s; const _ = 0b%s // %d bits", + name, strings.Repeat("1", length), length, ) filename := filepath.Join(dir, fmt.Sprintf("%s.go", name)) @@ -37,11 +37,11 @@ func testProg(dir, name string, G_option, length int, ok bool) { log.Fatal(err) } - cmd := exec.Command("go", "tool", "compile", fmt.Sprintf("-G=%d", G_option), filename) + cmd := exec.Command("go", "tool", "compile", "-p=p", filename) cmd.Dir = dir output, err := cmd.CombinedOutput() - if ok { + if msg == "" { // no error expected if err != nil { log.Fatalf("%s: compile failed unexpectedly: %v", name, err) @@ -53,7 +53,7 @@ func testProg(dir, name string, G_option, length int, ok bool) { if err == nil { log.Fatalf("%s: compile succeeded unexpectedly", name) } - if !bytes.Contains(output, []byte("excessively long constant")) { + if !bytes.Contains(output, []byte(msg)) { log.Fatalf("%s: wrong compiler error message:\n%s\n", name, output) } } @@ -69,9 +69,10 @@ func main() { } defer os.RemoveAll(dir) - const limit = 10000 // compiler-internal constant length limit - testProg(dir, "x1", 0, limit, true) // -G=0 - testProg(dir, "x2", 0, limit+1, false) // -G=0 - testProg(dir, "x1", 1, limit, true) // -G=1 (new type checker) - testProg(dir, "x2", 1, limit+1, false) // -G=1 (new type checker) + const bitLimit = 512 + const charLimit = 10000 // compiler-internal constant length limit + testProg(dir, "x1", bitLimit, "") + testProg(dir, "x2", bitLimit+1, "constant overflow") + testProg(dir, "x3", charLimit-2, "constant overflow") // -2 because literal contains 0b prefix + testProg(dir, "x4", charLimit-1, "excessively long constant") } diff --git a/test/const8.go b/test/const8.go new file mode 100644 index 00000000000000..9c04cc7e2cab63 --- /dev/null +++ b/test/const8.go @@ -0,0 +1,36 @@ +// run + +// Copyright 2022 The Go 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 identifiers in implicit (omitted) RHS +// expressions of constant declarations are resolved +// in the correct context; see issues #49157, #53585. + +package main + +const X = 2 + +func main() { + const ( + A = iota // 0 + iota = iota // 1 + B // 1 (iota is declared locally on prev. line) + C // 1 + ) + if A != 0 || B != 1 || C != 1 { + println("got", A, B, C, "want 0 1 1") + panic("FAILED") + } + + const ( + X = X + X + Y + Z = iota + ) + if X != 4 || Y != 8 || Z != 1 { + println("got", X, Y, Z, "want 4 8 1") + panic("FAILED") + } +} diff --git a/test/convert2.go b/test/convert2.go index 8e43967aaaea57..ef93fe1f9bda37 100644 --- a/test/convert2.go +++ b/test/convert2.go @@ -316,11 +316,11 @@ func _() { func _() { var s []byte - _ = ([4]byte)(s) // ERROR "cannot convert" + _ = ([4]byte)(s) _ = (*[4]byte)(s) type A [4]byte - _ = (A)(s) // ERROR "cannot convert" + _ = (A)(s) _ = (*A)(s) type P *[4]byte diff --git a/test/ddd1.go b/test/ddd1.go index ad49b347f49dd6..639b0bfdbdea8e 100644 --- a/test/ddd1.go +++ b/test/ddd1.go @@ -17,9 +17,9 @@ var ( _ = sum(1, 2, 3) _ = sum() _ = sum(1.0, 2.0) - _ = sum(1.5) // ERROR "integer" - _ = sum("hello") // ERROR ".hello. .type untyped string. as type int|incompatible" - _ = sum([]int{1}) // ERROR "\[\]int{...}.*as type int|incompatible" + _ = sum(1.5) // ERROR "1\.5 .untyped float constant. as int|integer" + _ = sum("hello") // ERROR ".hello. (.untyped string constant. as int|.type untyped string. as type int)|incompatible" + _ = sum([]int{1}) // ERROR "\[\]int{.*}.*as type int" ) func sum3(int, int, int) int { return 0 } @@ -27,9 +27,9 @@ func tuple() (int, int, int) { return 1, 2, 3 } var ( _ = sum(tuple()) - _ = sum(tuple()...) // ERROR "multiple-value" + _ = sum(tuple()...) // ERROR "\.{3} with 3-valued|multiple-value" _ = sum3(tuple()) - _ = sum3(tuple()...) // ERROR "multiple-value" ERROR "invalid use of .*[.][.][.]" + _ = sum3(tuple()...) // ERROR "\.{3} in call to non-variadic|multiple-value|invalid use of .*[.][.][.]" ) type T []T @@ -60,5 +60,5 @@ func bad(args ...int) { _ = [...]byte("foo") // ERROR "[.][.][.]" _ = [...][...]int{{1,2,3},{4,5,6}} // ERROR "[.][.][.]" - Foo(x...) // ERROR "invalid use of .*[.][.][.]" + Foo(x...) // ERROR "\.{3} in call to non-variadic|invalid use of .*[.][.][.]" } diff --git a/test/devirt.go b/test/devirt.go index e0149d8229247f..d5c815222e448c 100644 --- a/test/devirt.go +++ b/test/devirt.go @@ -31,9 +31,8 @@ func main() { panic("not 3") } - // Can't do types that aren't "direct" interfaces (yet). r = indirectiface{3, 4, 5} - if r.Value() != 12 { + if r.Value() != 12 { // ERROR "de-virtualizing call$" panic("not 12") } } diff --git a/test/directive.go b/test/directive.go index 37781c30d54e9d..8da15e24379836 100644 --- a/test/directive.go +++ b/test/directive.go @@ -6,16 +6,11 @@ // Verify that misplaced directives are diagnosed. -// ok -//go:build !ignore - //go:noinline // ERROR "misplaced compiler directive" //go:noinline // ERROR "misplaced compiler directive" package main -//go:build bad // ERROR "misplaced compiler directive" - //go:nosplit func f1() {} @@ -34,69 +29,29 @@ const c = 1 //go:noinline // ERROR "misplaced compiler directive" type T int -// ok -//go:notinheap -type T1 int - -//go:notinheap // ERROR "misplaced compiler directive" type ( - //go:notinheap //go:noinline // ERROR "misplaced compiler directive" - T2 int //go:notinheap // ERROR "misplaced compiler directive" - T2b int - //go:notinheap - T2c int + T2 int //go:noinline // ERROR "misplaced compiler directive" T3 int ) -//go:notinheap // ERROR "misplaced compiler directive" -type ( - //go:notinheap - T4 int -) - -//go:notinheap // ERROR "misplaced compiler directive" -type () - -type T5 int - -func g() {} //go:noinline // ERROR "misplaced compiler directive" - -// ok: attached to f (duplicated yes, but ok) -//go:noinline - //go:noinline func f() { - //go:noinline // ERROR "misplaced compiler directive" x := 1 - //go:noinline // ERROR "misplaced compiler directive" { - _ = x //go:noinline // ERROR "misplaced compiler directive" + _ = x } //go:noinline // ERROR "misplaced compiler directive" - var y int //go:noinline // ERROR "misplaced compiler directive" - //go:noinline // ERROR "misplaced compiler directive" + var y int _ = y //go:noinline // ERROR "misplaced compiler directive" const c = 1 - //go:noinline // ERROR "misplaced compiler directive" _ = func() {} //go:noinline // ERROR "misplaced compiler directive" - // ok: - //go:notinheap type T int } - -// someday there might be a directive that can apply to type aliases, but go:notinheap doesn't. -//go:notinheap // ERROR "misplaced compiler directive" -type T6 = int - -// EOF -//go:noinline // ERROR "misplaced compiler directive" - -//go:build bad // ERROR "misplaced compiler directive" diff --git a/test/directive2.go b/test/directive2.go new file mode 100644 index 00000000000000..2bb9ca9f0a4404 --- /dev/null +++ b/test/directive2.go @@ -0,0 +1,58 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Verify that misplaced directives are diagnosed. + +// ok +//go:build !ignore + +package main + +//go:build bad // ERROR "misplaced compiler directive" + +//go:noinline // ERROR "misplaced compiler directive" +type ( + T2 int //go:noinline // ERROR "misplaced compiler directive" + T2b int + T2c int + T3 int +) + +//go:noinline // ERROR "misplaced compiler directive" +type ( + T4 int +) + +//go:noinline // ERROR "misplaced compiler directive" +type () + +type T5 int + +func g() {} //go:noinline // ERROR "misplaced compiler directive" + +// ok: attached to f (duplicated yes, but ok) +//go:noinline + +//go:noinline +func f() { + //go:noinline // ERROR "misplaced compiler directive" + x := 1 + + //go:noinline // ERROR "misplaced compiler directive" + { + _ = x //go:noinline // ERROR "misplaced compiler directive" + } + var y int //go:noinline // ERROR "misplaced compiler directive" + //go:noinline // ERROR "misplaced compiler directive" + _ = y + + const c = 1 + + _ = func() {} +} + +// EOF +//go:noinline // ERROR "misplaced compiler directive" diff --git a/test/escape2.go b/test/escape2.go index b9b723d8666862..e3e5904cde525b 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -59,7 +59,7 @@ func foo8(xx, yy *int) int { // ERROR "xx does not escape$" "yy does not escape$ return *xx } -func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r2 level=0$" "leaking param: yy to result ~r2 level=0$" +func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r0 level=0$" "leaking param: yy to result ~r0 level=0$" xx = yy return xx } @@ -343,11 +343,11 @@ func indaddr1(x int) *int { // ERROR "moved to heap: x$" return &x } -func indaddr2(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func indaddr2(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" return *&x } -func indaddr3(x *int32) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func indaddr3(x *int32) *int { // ERROR "leaking param: x to result ~r0 level=0$" return *(**int)(unsafe.Pointer(&x)) } @@ -374,11 +374,11 @@ func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: f$" return (*uint64)(unsafe.Pointer(&f)) } -func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f to result ~r1 level=0$" +func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f to result ~r0 level=0$" return (*uint64)(unsafe.Pointer(f)) } -func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r0 level=0$" switch val := i.(type) { case *int: return val @@ -389,7 +389,7 @@ func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r1 level return nil } -func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" switch j := i; *j + 110 { case 12: return j @@ -401,7 +401,7 @@ func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" } // assigning to an array element is like assigning to the array -func foo60(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func foo60(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" var a [12]*int a[0] = i return a[1] @@ -414,7 +414,7 @@ func foo60a(i *int) *int { // ERROR "i does not escape$" } // assigning to a struct field is like assigning to the struct -func foo61(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func foo61(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" type S struct { a, b *int } @@ -611,11 +611,11 @@ func foo74c() { } } -func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "x does not escape$" +func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r0 level=0$" "x does not escape$" return y } -func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "y does not escape$" +func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r0 level=0$" "y does not escape$" return &x[0] } @@ -667,13 +667,13 @@ func foo76e() { func foo76f() { for { // TODO: This one really only escapes its scope, but we don't distinguish yet. - defer myprint(nil, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" + defer myprint(nil, 1, 2, 3) // ERROR "... argument does not escape$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" } } func foo76g() { for { - defer myprint1(nil, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" + defer myprint1(nil, 1, 2, 3) // ERROR "... argument does not escape$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" } } @@ -770,7 +770,7 @@ func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } -func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r0 level=0$" return [2]*int{x, nil} } @@ -783,7 +783,7 @@ func foo93(c chan *int) *int { // ERROR "c does not escape$" } // does not leak m -func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1" +func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r0 level=1" for k, v := range m { if b { return k @@ -799,12 +799,12 @@ func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape$" "leaking par } // does not leak m but does leak content -func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" +func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r0 level=1" return m[0] } // does leak m -func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r0 level=0$" return m[0] } @@ -814,12 +814,12 @@ func foo98(m map[int]*int) *int { // ERROR "m does not escape$" } // does leak m -func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r0 level=0$" return m[:] } // does not leak m -func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" +func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r0 level=1" for _, v := range m { return v } @@ -827,7 +827,7 @@ func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" } // does leak m -func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r0 level=0$" for _, v := range m { return v } @@ -890,27 +890,27 @@ func foo110(x *int) *int { // ERROR "leaking param: x$" return m[nil] } -func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" +func foo111(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0" m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } -func foo112(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo112(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := [1]*int{x} return m[0] } -func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo113(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := Bar{ii: x} return m.ii } -func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo114(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } -func foo115(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo115(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1)) } @@ -1148,16 +1148,16 @@ L100: func foo121() { for i := 0; i < 10; i++ { - defer myprint(nil, i) // ERROR "... argument escapes to heap$" "i escapes to heap$" - go myprint(nil, i) // ERROR "... argument escapes to heap$" "i escapes to heap$" + defer myprint(nil, i) // ERROR "... argument does not escape$" "i escapes to heap$" + go myprint(nil, i) // ERROR "... argument does not escape$" "i escapes to heap$" } } // same as foo121 but check across import func foo121b() { for i := 0; i < 10; i++ { - defer fmt.Printf("%d", i) // ERROR "... argument escapes to heap$" "i escapes to heap$" - go fmt.Printf("%d", i) // ERROR "... argument escapes to heap$" "i escapes to heap$" + defer fmt.Printf("%d", i) // ERROR "... argument does not escape$" "i escapes to heap$" + go fmt.Printf("%d", i) // ERROR "... argument does not escape$" "i escapes to heap$" } } diff --git a/test/escape2n.go b/test/escape2n.go index 7c8208aa73ca11..57cc1a01639eb4 100644 --- a/test/escape2n.go +++ b/test/escape2n.go @@ -59,7 +59,7 @@ func foo8(xx, yy *int) int { // ERROR "xx does not escape$" "yy does not escape$ return *xx } -func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r2 level=0$" "leaking param: yy to result ~r2 level=0$" +func foo9(xx, yy *int) *int { // ERROR "leaking param: xx to result ~r0 level=0$" "leaking param: yy to result ~r0 level=0$" xx = yy return xx } @@ -343,11 +343,11 @@ func indaddr1(x int) *int { // ERROR "moved to heap: x$" return &x } -func indaddr2(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func indaddr2(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" return *&x } -func indaddr3(x *int32) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func indaddr3(x *int32) *int { // ERROR "leaking param: x to result ~r0 level=0$" return *(**int)(unsafe.Pointer(&x)) } @@ -374,11 +374,11 @@ func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: f$" return (*uint64)(unsafe.Pointer(&f)) } -func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f to result ~r1 level=0$" +func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f to result ~r0 level=0$" return (*uint64)(unsafe.Pointer(f)) } -func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r0 level=0$" switch val := i.(type) { case *int: return val @@ -389,7 +389,7 @@ func typesw(i interface{}) *int { // ERROR "leaking param: i to result ~r1 level return nil } -func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" switch j := i; *j + 110 { case 12: return j @@ -401,7 +401,7 @@ func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" } // assigning to an array element is like assigning to the array -func foo60(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func foo60(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" var a [12]*int a[0] = i return a[1] @@ -414,7 +414,7 @@ func foo60a(i *int) *int { // ERROR "i does not escape$" } // assigning to a struct field is like assigning to the struct -func foo61(i *int) *int { // ERROR "leaking param: i to result ~r1 level=0$" +func foo61(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" type S struct { a, b *int } @@ -611,11 +611,11 @@ func foo74c() { } } -func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r2 level=0$" "x does not escape$" +func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: y to result ~r0 level=0$" "x does not escape$" return y } -func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r2 level=0$" "y does not escape$" +func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: x to result ~r0 level=0$" "y does not escape$" return &x[0] } @@ -667,13 +667,13 @@ func foo76e() { func foo76f() { for { // TODO: This one really only escapes its scope, but we don't distinguish yet. - defer myprint(nil, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" + defer myprint(nil, 1, 2, 3) // ERROR "... argument does not escape$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" } } func foo76g() { for { - defer myprint1(nil, 1, 2, 3) // ERROR "... argument escapes to heap$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" + defer myprint1(nil, 1, 2, 3) // ERROR "... argument does not escape$" "1 escapes to heap$" "2 escapes to heap$" "3 escapes to heap$" } } @@ -770,7 +770,7 @@ func foo91(x *int) map[*int]*int { // ERROR "leaking param: x$" return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int{...} escapes to heap$" } -func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo92(x *int) [2]*int { // ERROR "leaking param: x to result ~r0 level=0$" return [2]*int{x, nil} } @@ -783,7 +783,7 @@ func foo93(c chan *int) *int { // ERROR "c does not escape$" } // does not leak m -func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1" +func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r0 level=1" for k, v := range m { if b { return k @@ -799,12 +799,12 @@ func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape$" "leaking par } // does not leak m but does leak content -func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" +func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r0 level=1" return m[0] } // does leak m -func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo97(m [1]*int) *int { // ERROR "leaking param: m to result ~r0 level=0$" return m[0] } @@ -814,12 +814,12 @@ func foo98(m map[int]*int) *int { // ERROR "m does not escape$" } // does leak m -func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r0 level=0$" return m[:] } // does not leak m -func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" +func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r0 level=1" for _, v := range m { return v } @@ -827,7 +827,7 @@ func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1" } // does leak m -func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r1 level=0$" +func foo101(m [1]*int) *int { // ERROR "leaking param: m to result ~r0 level=0$" for _, v := range m { return v } @@ -890,27 +890,27 @@ func foo110(x *int) *int { // ERROR "leaking param: x$" return m[nil] } -func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0" +func foo111(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0" m := []*int{x} // ERROR "\[\]\*int{...} does not escape$" return m[0] } -func foo112(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo112(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := [1]*int{x} return m[0] } -func foo113(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo113(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := Bar{ii: x} return m.ii } -func foo114(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo114(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" m := &Bar{ii: x} // ERROR "&Bar{...} does not escape$" return m.ii } -func foo115(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0$" +func foo115(x *int) *int { // ERROR "leaking param: x to result ~r0 level=0$" return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1)) } @@ -1148,16 +1148,16 @@ L100: func foo121() { for i := 0; i < 10; i++ { - defer myprint(nil, i) // ERROR "... argument escapes to heap$" "i escapes to heap$" - go myprint(nil, i) // ERROR "... argument escapes to heap$" "i escapes to heap$" + defer myprint(nil, i) // ERROR "... argument does not escape$" "i escapes to heap$" + go myprint(nil, i) // ERROR "... argument does not escape$" "i escapes to heap$" } } // same as foo121 but check across import func foo121b() { for i := 0; i < 10; i++ { - defer fmt.Printf("%d", i) // ERROR "... argument escapes to heap$" "i escapes to heap$" - go fmt.Printf("%d", i) // ERROR "... argument escapes to heap$" "i escapes to heap$" + defer fmt.Printf("%d", i) // ERROR "... argument does not escape$" "i escapes to heap$" + go fmt.Printf("%d", i) // ERROR "... argument does not escape$" "i escapes to heap$" } } diff --git a/test/escape5.go b/test/escape5.go index 82be2c38e790e6..089130dad5c73e 100644 --- a/test/escape5.go +++ b/test/escape5.go @@ -22,19 +22,19 @@ func leaktoret(p *int) *int { // ERROR "leaking param: p to result" return p } -func leaktoret2(p *int) (*int, *int) { // ERROR "leaking param: p to result ~r1" "leaking param: p to result ~r2" +func leaktoret2(p *int) (*int, *int) { // ERROR "leaking param: p to result ~r0" "leaking param: p to result ~r1" return p, p } -func leaktoret22(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r2" "leaking param: q to result ~r3" +func leaktoret22(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r0" "leaking param: q to result ~r1" return p, q } -func leaktoret22b(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r3" "leaking param: q to result ~r2" +func leaktoret22b(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r1" "leaking param: q to result ~r0" return leaktoret22(q, p) } -func leaktoret22c(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r3" "leaking param: q to result ~r2" +func leaktoret22c(p, q *int) (*int, *int) { // ERROR "leaking param: p to result ~r1" "leaking param: q to result ~r0" r, s := leaktoret22(q, p) return r, s } @@ -173,15 +173,14 @@ type U int func (*U) M() {} func (_ *U) N() {} -func _() { +func fbad24305a() { var u U u.M() u.N() } -func fbad24305() { - // BAD u should not be heap allocated - var u U // ERROR "moved to heap: u" +func fbad24305b() { + var u U (*U).M(&u) (*U).N(&u) } diff --git a/test/escape_array.go b/test/escape_array.go index 0d07fd861ffb33..83062c9436e71d 100644 --- a/test/escape_array.go +++ b/test/escape_array.go @@ -12,15 +12,15 @@ var Ssink *string type U [2]*string -func bar(a, b *string) U { // ERROR "leaking param: a to result ~r2 level=0$" "leaking param: b to result ~r2 level=0$" +func bar(a, b *string) U { // ERROR "leaking param: a to result ~r0 level=0$" "leaking param: b to result ~r0 level=0$" return U{a, b} } -func foo(x U) U { // ERROR "leaking param: x to result ~r1 level=0$" +func foo(x U) U { // ERROR "leaking param: x to result ~r0 level=0$" return U{x[1], x[0]} } -func bff(a, b *string) U { // ERROR "leaking param: a to result ~r2 level=0$" "leaking param: b to result ~r2 level=0$" +func bff(a, b *string) U { // ERROR "leaking param: a to result ~r0 level=0$" "leaking param: b to result ~r0 level=0$" return foo(foo(bar(a, b))) } @@ -41,27 +41,27 @@ func tbff2() *string { return u[1] } -func car(x U) *string { // ERROR "leaking param: x to result ~r1 level=0$" +func car(x U) *string { // ERROR "leaking param: x to result ~r0 level=0$" return x[0] } // BAD: need fine-grained analysis to track x[0] and x[1] differently. -func fun(x U, y *string) *string { // ERROR "leaking param: x to result ~r2 level=0$" "leaking param: y to result ~r2 level=0$" +func fun(x U, y *string) *string { // ERROR "leaking param: x to result ~r0 level=0$" "leaking param: y to result ~r0 level=0$" x[0] = y return x[1] } -func fup(x *U, y *string) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param: y$" +func fup(x *U, y *string) *string { // ERROR "leaking param: x to result ~r0 level=1$" "leaking param: y$" x[0] = y // leaking y to heap is intended return x[1] } -func fum(x *U, y **string) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$" +func fum(x *U, y **string) *string { // ERROR "leaking param: x to result ~r0 level=1$" "leaking param content: y$" x[0] = *y return x[1] } -func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$" +func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r0 level=1$" "leaking param content: y$" x[0] = y[0] return x[1] } diff --git a/test/escape_calls.go b/test/escape_calls.go index 9e1db5426ed5d6..aa7c7f516cf96e 100644 --- a/test/escape_calls.go +++ b/test/escape_calls.go @@ -11,7 +11,7 @@ package foo -func f(buf []byte) []byte { // ERROR "leaking param: buf to result ~r1 level=0$" +func f(buf []byte) []byte { // ERROR "leaking param: buf to result ~r0 level=0$" return buf } diff --git a/test/escape_closure.go b/test/escape_closure.go index 9152319fe045ba..bd6c025476ee8a 100644 --- a/test/escape_closure.go +++ b/test/escape_closure.go @@ -44,7 +44,7 @@ func ClosureCallArgs3() { func ClosureCallArgs4() { x := 0 - _ = func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape" + _ = func(p *int) *int { // ERROR "leaking param: p to result ~r0" "func literal does not escape" return p }(&x) } @@ -111,7 +111,7 @@ func ClosureCallArgs11() { func ClosureCallArgs12() { x := 0 - defer func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape" + defer func(p *int) *int { // ERROR "leaking param: p to result ~r0" "func literal does not escape" return p }(&x) } @@ -126,7 +126,7 @@ func ClosureCallArgs13() { func ClosureCallArgs14() { x := 0 p := &x - _ = func(p **int) *int { // ERROR "leaking param: p to result ~r1 level=1" "func literal does not escape" + _ = func(p **int) *int { // ERROR "leaking param: p to result ~r0 level=1" "func literal does not escape" return *p }(&p) } @@ -145,7 +145,7 @@ func ClosureLeak1(s string) string { // ERROR "s does not escape" } // See #14409 -- returning part of captured var leaks it. -func ClosureLeak1a(a ...string) string { // ERROR "leaking param: a to result ~r1 level=1$" +func ClosureLeak1a(a ...string) string { // ERROR "leaking param: a to result ~r0 level=1$" return func() string { // ERROR "func literal does not escape" return a[0] }() diff --git a/test/escape_goto.go b/test/escape_goto.go index f024a9afe378b2..90da5a21512970 100644 --- a/test/escape_goto.go +++ b/test/escape_goto.go @@ -10,7 +10,7 @@ package escape var x bool -func _() { +func f1() { var p *int loop: if x { @@ -22,7 +22,7 @@ loop: _ = p } -func _() { +func f2() { var p *int if x { loop: @@ -33,7 +33,7 @@ func _() { _ = p } -func _() { +func f3() { var p *int if x { loop: diff --git a/test/escape_iface.go b/test/escape_iface.go index dba08e3cb33b30..986228129a698c 100644 --- a/test/escape_iface.go +++ b/test/escape_iface.go @@ -234,16 +234,6 @@ func dotTypeEscape2() { // #13805, #15796 *(&v) = x.(int) *(&v), *(&ok) = y.(int) } - { - i := 0 - j := 0 - var ok bool - var x interface{} = i // ERROR "i does not escape" - var y interface{} = j // ERROR "j does not escape" - - sink = x.(int) // ERROR "x.\(int\) escapes to heap" - sink, *(&ok) = y.(int) - } { i := 0 // ERROR "moved to heap: i" j := 0 // ERROR "moved to heap: j" diff --git a/test/escape_iface_nounified.go b/test/escape_iface_nounified.go new file mode 100644 index 00000000000000..1d267bcd185f50 --- /dev/null +++ b/test/escape_iface_nounified.go @@ -0,0 +1,25 @@ +// errorcheck -0 -m -l +//go:build !goexperiment.unified +// +build !goexperiment.unified + +// 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 escape + +var sink interface{} + +func dotTypeEscape2() { // #13805, #15796 + { + i := 0 + j := 0 + var ok bool + var x interface{} = i // ERROR "i does not escape" + var y interface{} = j // ERROR "j does not escape" + + sink = x.(int) // ERROR "x.\(int\) escapes to heap" + // BAD: should be "y.\(int\) escapes to heap" too + sink, *(&ok) = y.(int) + } +} diff --git a/test/escape_iface_unified.go b/test/escape_iface_unified.go new file mode 100644 index 00000000000000..80222dae5fe035 --- /dev/null +++ b/test/escape_iface_unified.go @@ -0,0 +1,24 @@ +// errorcheck -0 -m -l +//go:build goexperiment.unified +// +build goexperiment.unified + +// 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 escape + +var sink interface{} + +func dotTypeEscape2() { // #13805, #15796 + { + i := 0 + j := 0 + var ok bool + var x interface{} = i // ERROR "i does not escape" + var y interface{} = j // ERROR "j does not escape" + + sink = x.(int) // ERROR "x.\(int\) escapes to heap" + sink, *(&ok) = y.(int) // ERROR "autotmp_.* escapes to heap" + } +} diff --git a/test/escape_param.go b/test/escape_param.go index dc93f689cf9fbd..b630bae88fcd9b 100644 --- a/test/escape_param.go +++ b/test/escape_param.go @@ -16,7 +16,7 @@ func zero() int { return 0 } var sink interface{} // in -> out -func param0(p *int) *int { // ERROR "leaking param: p to result ~r1" +func param0(p *int) *int { // ERROR "leaking param: p to result ~r0" return p } @@ -31,7 +31,7 @@ func caller0b() { } // in, in -> out, out -func param1(p1, p2 *int) (*int, *int) { // ERROR "leaking param: p1 to result ~r2" "leaking param: p2 to result ~r3" +func param1(p1, p2 *int) (*int, *int) { // ERROR "leaking param: p1 to result ~r0" "leaking param: p2 to result ~r1" return p1, p2 } @@ -222,7 +222,7 @@ func caller8() { } // *in -> out -func param9(p ***int) **int { // ERROR "leaking param: p to result ~r1 level=1" +func param9(p ***int) **int { // ERROR "leaking param: p to result ~r0 level=1" return *p } @@ -241,7 +241,7 @@ func caller9b() { } // **in -> out -func param10(p ***int) *int { // ERROR "leaking param: p to result ~r1 level=2" +func param10(p ***int) *int { // ERROR "leaking param: p to result ~r0 level=2" return **p } @@ -436,6 +436,6 @@ func param14a(x [4]*int) interface{} { // ERROR "leaking param: x$" // Convert to a direct interface, does not need an allocation. // So x only leaks to result. -func param14b(x *int) interface{} { // ERROR "leaking param: x to result ~r1 level=0" +func param14b(x *int) interface{} { // ERROR "leaking param: x to result ~r0 level=0" return x } diff --git a/test/escape_runtime_atomic.go b/test/escape_runtime_atomic.go index 62e8fede278b7f..30d1d0c0c1d89d 100644 --- a/test/escape_runtime_atomic.go +++ b/test/escape_runtime_atomic.go @@ -13,8 +13,8 @@ import ( "unsafe" ) -// BAD: should always be "leaking param: addr to result ~r1 level=1$". -func Loadp(addr unsafe.Pointer) unsafe.Pointer { // ERROR "leaking param: addr( to result ~r1 level=1)?$" +// BAD: should always be "leaking param: addr to result ~r0 level=1$". +func Loadp(addr unsafe.Pointer) unsafe.Pointer { // ERROR "leaking param: addr( to result ~r0 level=1)?$" return atomic.Loadp(addr) } diff --git a/test/escape_slice.go b/test/escape_slice.go index d60414736c38d2..055b60be4173cf 100644 --- a/test/escape_slice.go +++ b/test/escape_slice.go @@ -101,7 +101,7 @@ func slice11() { _ = s } -func slice12(x []int) *[1]int { // ERROR "leaking param: x to result ~r1 level=0$" +func slice12(x []int) *[1]int { // ERROR "leaking param: x to result ~r0 level=0$" return (*[1]int)(x) } @@ -110,7 +110,7 @@ func envForDir(dir string) []string { // ERROR "dir does not escape" return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string{...} does not escape" } -func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0" +func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r0 level=0" NextVar: for _, inkv := range in { k := strings.SplitAfterN(inkv, "=", 2)[0] diff --git a/test/escape_struct_return.go b/test/escape_struct_return.go index 222ef8bc22a064..a42ae1e8c9b527 100644 --- a/test/escape_struct_return.go +++ b/test/escape_struct_return.go @@ -15,11 +15,11 @@ type U struct { _spp **string } -func A(sp *string, spp **string) U { // ERROR "leaking param: sp to result ~r2 level=0$" "leaking param: spp to result ~r2 level=0$" +func A(sp *string, spp **string) U { // ERROR "leaking param: sp to result ~r0 level=0$" "leaking param: spp to result ~r0 level=0$" return U{sp, spp} } -func B(spp **string) U { // ERROR "leaking param: spp to result ~r1 level=0$" +func B(spp **string) U { // ERROR "leaking param: spp to result ~r0 level=0$" return U{*spp, spp} } diff --git a/test/escape_unsafe.go b/test/escape_unsafe.go index b34beacccb5992..56c536fdfb4c99 100644 --- a/test/escape_unsafe.go +++ b/test/escape_unsafe.go @@ -15,7 +15,7 @@ import ( // (1) Conversion of a *T1 to Pointer to *T2. -func convert(p *float64) *uint64 { // ERROR "leaking param: p to result ~r1 level=0$" +func convert(p *float64) *uint64 { // ERROR "leaking param: p to result ~r0 level=0$" return (*uint64)(unsafe.Pointer(p)) } @@ -39,12 +39,12 @@ func arithMask() unsafe.Pointer { // (5) Conversion of the result of reflect.Value.Pointer or // reflect.Value.UnsafeAddr from uintptr to Pointer. -// BAD: should be "leaking param: p to result ~r1 level=0$" +// BAD: should be "leaking param: p to result ~r0 level=0$" func valuePointer(p *int) unsafe.Pointer { // ERROR "leaking param: p$" return unsafe.Pointer(reflect.ValueOf(p).Pointer()) } -// BAD: should be "leaking param: p to result ~r1 level=0$" +// BAD: should be "leaking param: p to result ~r0 level=0$" func valueUnsafeAddr(p *int) unsafe.Pointer { // ERROR "leaking param: p$" return unsafe.Pointer(reflect.ValueOf(p).Elem().UnsafeAddr()) } @@ -52,11 +52,11 @@ func valueUnsafeAddr(p *int) unsafe.Pointer { // ERROR "leaking param: p$" // (6) Conversion of a reflect.SliceHeader or reflect.StringHeader // Data field to or from Pointer. -func fromSliceData(s []int) unsafe.Pointer { // ERROR "leaking param: s to result ~r1 level=0$" +func fromSliceData(s []int) unsafe.Pointer { // ERROR "leaking param: s to result ~r0 level=0$" return unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data) } -func fromStringData(s string) unsafe.Pointer { // ERROR "leaking param: s to result ~r1 level=0$" +func fromStringData(s string) unsafe.Pointer { // ERROR "leaking param: s to result ~r0 level=0$" return unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data) } @@ -65,5 +65,5 @@ func toSliceData(s *[]int, p unsafe.Pointer) { // ERROR "s does not escape" "lea } func toStringData(s *string, p unsafe.Pointer) { // ERROR "s does not escape" "leaking param: p$" - (*reflect.SliceHeader)(unsafe.Pointer(s)).Data = uintptr(p) + (*reflect.StringHeader)(unsafe.Pointer(s)).Data = uintptr(p) } diff --git a/test/fixedbugs/bug062.go b/test/fixedbugs/bug062.go index 24c2dff9339a68..1008f1af9ceca8 100644 --- a/test/fixedbugs/bug062.go +++ b/test/fixedbugs/bug062.go @@ -7,6 +7,5 @@ package main func main() { - var s string = nil; // ERROR "illegal|invalid|incompatible|cannot" - _ = s + var s string = nil // ERROR "illegal|invalid|incompatible|cannot" } diff --git a/test/fixedbugs/bug121.go b/test/fixedbugs/bug121.go index 22c71817526236..471c27eb821e04 100644 --- a/test/fixedbugs/bug121.go +++ b/test/fixedbugs/bug121.go @@ -9,7 +9,7 @@ package main type T func() type I interface { - f, g (); // ERROR "name list not allowed" + f, g (); // ERROR "unexpected comma" } type J interface { diff --git a/test/fixedbugs/bug131.go b/test/fixedbugs/bug131.go index 2c9d120ed069bf..de606da1679d97 100644 --- a/test/fixedbugs/bug131.go +++ b/test/fixedbugs/bug131.go @@ -7,7 +7,6 @@ package main func main() { - const a uint64 = 10; - var b int64 = a; // ERROR "convert|cannot|incompatible" - _ = b + const a uint64 = 10 + var b int64 = a // ERROR "convert|cannot|incompatible" } diff --git a/test/fixedbugs/bug150.go b/test/fixedbugs/bug150.go index b565ef73dd8b82..bb4b0d224211d5 100644 --- a/test/fixedbugs/bug150.go +++ b/test/fixedbugs/bug150.go @@ -19,5 +19,5 @@ func f() (a, b M) { } /* -bugs/bug150.go:13: reorder2: too many funcation calls evaluating parameters +bugs/bug150.go:13: reorder2: too many function calls evaluating parameters */ diff --git a/test/fixedbugs/bug175.go b/test/fixedbugs/bug175.go index 88210a59b3e520..caf3168536e2a8 100644 --- a/test/fixedbugs/bug175.go +++ b/test/fixedbugs/bug175.go @@ -9,6 +9,5 @@ package main func f() (int, bool) { return 0, true } func main() { - x, y := f(), 2; // ERROR "multi|2-valued" - _, _ = x, y + x, y := f(), 2 // ERROR "multi|2-valued" } diff --git a/test/fixedbugs/bug176.go b/test/fixedbugs/bug176.go index 7001dd081e9ab0..61e63c76564b01 100644 --- a/test/fixedbugs/bug176.go +++ b/test/fixedbugs/bug176.go @@ -8,7 +8,6 @@ package main var x int -var a = []int{ x: 1} // ERROR "constant" -var b = [...]int{x: 1} // GCCGO_ERROR "constant" -var c = map[int]int{ x: 1} - +var a = []int{x: 1} // ERROR "constant" +var b = [...]int{x: 1} // ERROR "constant" +var c = map[int]int{x: 1} diff --git a/test/fixedbugs/bug193.go b/test/fixedbugs/bug193.go index 36073220f9b2c4..64e06da897516c 100644 --- a/test/fixedbugs/bug193.go +++ b/test/fixedbugs/bug193.go @@ -11,8 +11,6 @@ func main() { ss := 1 << s y1 := float64(ss) y2 := float64(1 << s) // ERROR "shift" - // see issues #45114, #45117 - // y3 := string(1 << s) // DISABLED "shift" - y3 := 0 + y3 := string(1 << s) // ERROR "shift" _, _, _, _, _ = s, ss, y1, y2, y3 } diff --git a/test/fixedbugs/bug195.go b/test/fixedbugs/bug195.go index 94f61fff7f1cb0..4a3bf0db81d840 100644 --- a/test/fixedbugs/bug195.go +++ b/test/fixedbugs/bug195.go @@ -1,4 +1,4 @@ -// errorcheck +// errorcheck -lang=go1.17 // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,14 +11,14 @@ type I2 int type I3 interface{ int } // ERROR "interface" -type S struct { - x interface{ S } // ERROR "interface" +type S struct { // GC_ERROR "invalid recursive type" + x interface{ S } // GCCGO_ERROR "interface" } -type I4 interface { // GC_ERROR "invalid recursive type I4\n\tLINE: I4 refers to\n\tLINE: I4$" +type I4 interface { // GC_ERROR "invalid recursive type I4\n\tLINE:.* I4 refers to\n\tLINE:.* I4$" I4 // GCCGO_ERROR "interface" } -type I5 interface { // GC_ERROR "invalid recursive type I5\n\tLINE: I5 refers to\n\tLINE+4: I6 refers to\n\tLINE: I5$" +type I5 interface { // GC_ERROR "invalid recursive type I5\n\tLINE:.* I5 refers to\n\tLINE+4:.* I6 refers to\n\tLINE:.* I5$" I6 } diff --git a/test/fixedbugs/bug222.dir/chanbug2.go b/test/fixedbugs/bug222.dir/chanbug2.go index 109581dc30380b..b6c416f83458d7 100644 --- a/test/fixedbugs/bug222.dir/chanbug2.go +++ b/test/fixedbugs/bug222.dir/chanbug2.go @@ -3,4 +3,5 @@ // license that can be found in the LICENSE file package Bar -import _ "chanbug" + +import _ "./chanbug" diff --git a/test/fixedbugs/bug228.go b/test/fixedbugs/bug228.go index 50e895917fab26..5c0e7e5122d40e 100644 --- a/test/fixedbugs/bug228.go +++ b/test/fixedbugs/bug228.go @@ -6,14 +6,10 @@ package main -func f(x int, y ...int) // ok +func f(x int, y ...int) // ok func g(x int, y float32) (...) // ERROR "[.][.][.]" -func h(x, y ...int) // ERROR "[.][.][.]" - -func i(x int, y ...int, z float32) // ERROR "[.][.][.]" - var x ...int; // ERROR "[.][.][.]|syntax|type" type T ...int; // ERROR "[.][.][.]|syntax|type" diff --git a/test/fixedbugs/bug228a.go b/test/fixedbugs/bug228a.go new file mode 100644 index 00000000000000..c42b0bffb7d20f --- /dev/null +++ b/test/fixedbugs/bug228a.go @@ -0,0 +1,13 @@ +// errorcheck + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f(x int, y ...int) // ok + +func h(x, y ...int) // ERROR "[.][.][.]" + +func i(x int, y ...int, z float32) // ERROR "[.][.][.]" diff --git a/test/fixedbugs/bug231.go b/test/fixedbugs/bug231.go index f64ddc3e757cd0..db0d536db7d01a 100644 --- a/test/fixedbugs/bug231.go +++ b/test/fixedbugs/bug231.go @@ -6,17 +6,19 @@ package main -type I interface { m() } -type T struct { m func() } -type M struct {} +type I interface{ m() } +type T struct{ m func() } +type M struct{} + func (M) m() {} func main() { var t T var m M var i I - + i = m - i = t // ERROR "not a method|has no methods" "does not implement I" + // types2 does not give extra error "T.m is a field, not a method" + i = t // ERROR "not a method|has no methods|does not implement I" _ = i } diff --git a/test/fixedbugs/bug248.dir/bug2.go b/test/fixedbugs/bug248.dir/bug2.go index c0fdecfdb7b1e2..92a7974679eae1 100644 --- a/test/fixedbugs/bug248.dir/bug2.go +++ b/test/fixedbugs/bug248.dir/bug2.go @@ -50,8 +50,8 @@ var p0i2 p1.I = t0(0) // ERROR "does not implement|incompatible" func foobar() { // check that cannot assign one to the other, // but can convert. - v0 = v1 // ERROR "assign" - v1 = v0 // ERROR "assign" + v0 = v1 // ERROR "assign|cannot use" + v1 = v0 // ERROR "assign|cannot use" v0 = p0.T(v1) v1 = p1.T(v0) diff --git a/test/fixedbugs/bug255.go b/test/fixedbugs/bug255.go index 458fb972b20958..184ff2d378b58d 100644 --- a/test/fixedbugs/bug255.go +++ b/test/fixedbugs/bug255.go @@ -6,15 +6,16 @@ package main -var a [10]int // ok -var b [1e1]int // ok -var c [1.5]int // ERROR "truncated" -var d ["abc"]int // ERROR "invalid array bound|not numeric" -var e [nil]int // ERROR "use of untyped nil|invalid array bound|not numeric" -var f [e]int // ok: error already reported for e -var g [1 << 65]int // ERROR "array bound is too large|overflows" +var a [10]int // ok +var b [1e1]int // ok +var c [1.5]int // ERROR "truncated|must be integer" +var d ["abc"]int // ERROR "invalid array bound|not numeric|must be integer" +var e [nil]int // ERROR "use of untyped nil|invalid array (bound|length)|not numeric|must be constant" +// var f [e]int // ok with Go 1.17 because an error was reported for e; leads to an error for Go 1.18 +var f [ee]int // ERROR "undefined|undeclared" +var g [1 << 65]int // ERROR "array bound is too large|overflows|must be integer" var h [len(a)]int // ok func ff() string -var i [len([1]string{ff()})]int // ERROR "non-constant array bound|not constant" +var i [len([1]string{ff()})]int // ERROR "non-constant array bound|not constant|must be constant" diff --git a/test/fixedbugs/bug267.go b/test/fixedbugs/bug267.go index cf8bf841f8b1da..b61216a9d5a93f 100644 --- a/test/fixedbugs/bug267.go +++ b/test/fixedbugs/bug267.go @@ -10,7 +10,7 @@ type T []int var a []bool -func _() { +func f1() { if a[T{42}[0]] { } // if (a[T{42}[0]]) {} // this compiles diff --git a/test/fixedbugs/bug289.go b/test/fixedbugs/bug289.go index fea6829992f57a..7e8346ee0f631b 100644 --- a/test/fixedbugs/bug289.go +++ b/test/fixedbugs/bug289.go @@ -9,18 +9,14 @@ package main func f1() { - a, b := f() // ERROR "assignment mismatch|does not match|cannot initialize" - _ = a - _ = b + a, b := f() // ERROR "assignment mismatch|does not match|cannot initialize" } func f2() { var a, b int - a, b = f() // ERROR "assignment mismatch|does not match|cannot assign" - _ = a - _ = b + a, b = f() // ERROR "assignment mismatch|does not match|cannot assign" } func f() int { - return 1; + return 1 } diff --git a/test/fixedbugs/bug302.dir/main.go b/test/fixedbugs/bug302.dir/main.go deleted file mode 100644 index 52c054fb4c6633..00000000000000 --- a/test/fixedbugs/bug302.dir/main.go +++ /dev/null @@ -1,12 +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 main - -// Check that the export information is correct in p.6. -import _ "p" - -// Check that it's still correct in pp.a (which contains p.6). -import _ "pp" - diff --git a/test/fixedbugs/bug302.dir/p.go b/test/fixedbugs/bug302.dir/p.go deleted file mode 100644 index 0be521b4f88d51..00000000000000 --- a/test/fixedbugs/bug302.dir/p.go +++ /dev/null @@ -1,1011 +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 p - -type T struct { - x1 int - x2 int - x3 int - x4 int - x5 int - x6 int - x7 int - x8 int - x9 int - x10 int - x11 int - x12 int - x13 int - x14 int - x15 int - x16 int - x17 int - x18 int - x19 int - x20 int - x21 int - x22 int - x23 int - x24 int - x25 int - x26 int - x27 int - x28 int - x29 int - x30 int - x31 int - x32 int - x33 int - x34 int - x35 int - x36 int - x37 int - x38 int - x39 int - x40 int - x41 int - x42 int - x43 int - x44 int - x45 int - x46 int - x47 int - x48 int - x49 int - x50 int - x51 int - x52 int - x53 int - x54 int - x55 int - x56 int - x57 int - x58 int - x59 int - x60 int - x61 int - x62 int - x63 int - x64 int - x65 int - x66 int - x67 int - x68 int - x69 int - x70 int - x71 int - x72 int - x73 int - x74 int - x75 int - x76 int - x77 int - x78 int - x79 int - x80 int - x81 int - x82 int - x83 int - x84 int - x85 int - x86 int - x87 int - x88 int - x89 int - x90 int - x91 int - x92 int - x93 int - x94 int - x95 int - x96 int - x97 int - x98 int - x99 int - x100 int - x101 int - x102 int - x103 int - x104 int - x105 int - x106 int - x107 int - x108 int - x109 int - x110 int - x111 int - x112 int - x113 int - x114 int - x115 int - x116 int - x117 int - x118 int - x119 int - x120 int - x121 int - x122 int - x123 int - x124 int - x125 int - x126 int - x127 int - x128 int - x129 int - x130 int - x131 int - x132 int - x133 int - x134 int - x135 int - x136 int - x137 int - x138 int - x139 int - x140 int - x141 int - x142 int - x143 int - x144 int - x145 int - x146 int - x147 int - x148 int - x149 int - x150 int - x151 int - x152 int - x153 int - x154 int - x155 int - x156 int - x157 int - x158 int - x159 int - x160 int - x161 int - x162 int - x163 int - x164 int - x165 int - x166 int - x167 int - x168 int - x169 int - x170 int - x171 int - x172 int - x173 int - x174 int - x175 int - x176 int - x177 int - x178 int - x179 int - x180 int - x181 int - x182 int - x183 int - x184 int - x185 int - x186 int - x187 int - x188 int - x189 int - x190 int - x191 int - x192 int - x193 int - x194 int - x195 int - x196 int - x197 int - x198 int - x199 int - x200 int - x201 int - x202 int - x203 int - x204 int - x205 int - x206 int - x207 int - x208 int - x209 int - x210 int - x211 int - x212 int - x213 int - x214 int - x215 int - x216 int - x217 int - x218 int - x219 int - x220 int - x221 int - x222 int - x223 int - x224 int - x225 int - x226 int - x227 int - x228 int - x229 int - x230 int - x231 int - x232 int - x233 int - x234 int - x235 int - x236 int - x237 int - x238 int - x239 int - x240 int - x241 int - x242 int - x243 int - x244 int - x245 int - x246 int - x247 int - x248 int - x249 int - x250 int - x251 int - x252 int - x253 int - x254 int - x255 int - x256 int - x257 int - x258 int - x259 int - x260 int - x261 int - x262 int - x263 int - x264 int - x265 int - x266 int - x267 int - x268 int - x269 int - x270 int - x271 int - x272 int - x273 int - x274 int - x275 int - x276 int - x277 int - x278 int - x279 int - x280 int - x281 int - x282 int - x283 int - x284 int - x285 int - x286 int - x287 int - x288 int - x289 int - x290 int - x291 int - x292 int - x293 int - x294 int - x295 int - x296 int - x297 int - x298 int - x299 int - x300 int - x301 int - x302 int - x303 int - x304 int - x305 int - x306 int - x307 int - x308 int - x309 int - x310 int - x311 int - x312 int - x313 int - x314 int - x315 int - x316 int - x317 int - x318 int - x319 int - x320 int - x321 int - x322 int - x323 int - x324 int - x325 int - x326 int - x327 int - x328 int - x329 int - x330 int - x331 int - x332 int - x333 int - x334 int - x335 int - x336 int - x337 int - x338 int - x339 int - x340 int - x341 int - x342 int - x343 int - x344 int - x345 int - x346 int - x347 int - x348 int - x349 int - x350 int - x351 int - x352 int - x353 int - x354 int - x355 int - x356 int - x357 int - x358 int - x359 int - x360 int - x361 int - x362 int - x363 int - x364 int - x365 int - x366 int - x367 int - x368 int - x369 int - x370 int - x371 int - x372 int - x373 int - x374 int - x375 int - x376 int - x377 int - x378 int - x379 int - x380 int - x381 int - x382 int - x383 int - x384 int - x385 int - x386 int - x387 int - x388 int - x389 int - x390 int - x391 int - x392 int - x393 int - x394 int - x395 int - x396 int - x397 int - x398 int - x399 int - x400 int - x401 int - x402 int - x403 int - x404 int - x405 int - x406 int - x407 int - x408 int - x409 int - x410 int - x411 int - x412 int - x413 int - x414 int - x415 int - x416 int - x417 int - x418 int - x419 int - x420 int - x421 int - x422 int - x423 int - x424 int - x425 int - x426 int - x427 int - x428 int - x429 int - x430 int - x431 int - x432 int - x433 int - x434 int - x435 int - x436 int - x437 int - x438 int - x439 int - x440 int - x441 int - x442 int - x443 int - x444 int - x445 int - x446 int - x447 int - x448 int - x449 int - x450 int - x451 int - x452 int - x453 int - x454 int - x455 int - x456 int - x457 int - x458 int - x459 int - x460 int - x461 int - x462 int - x463 int - x464 int - x465 int - x466 int - x467 int - x468 int - x469 int - x470 int - x471 int - x472 int - x473 int - x474 int - x475 int - x476 int - x477 int - x478 int - x479 int - x480 int - x481 int - x482 int - x483 int - x484 int - x485 int - x486 int - x487 int - x488 int - x489 int - x490 int - x491 int - x492 int - x493 int - x494 int - x495 int - x496 int - x497 int - x498 int - x499 int - x500 int - x501 int - x502 int - x503 int - x504 int - x505 int - x506 int - x507 int - x508 int - x509 int - x510 int - x511 int - x512 int - x513 int - x514 int - x515 int - x516 int - x517 int - x518 int - x519 int - x520 int - x521 int - x522 int - x523 int - x524 int - x525 int - x526 int - x527 int - x528 int - x529 int - x530 int - x531 int - x532 int - x533 int - x534 int - x535 int - x536 int - x537 int - x538 int - x539 int - x540 int - x541 int - x542 int - x543 int - x544 int - x545 int - x546 int - x547 int - x548 int - x549 int - x550 int - x551 int - x552 int - x553 int - x554 int - x555 int - x556 int - x557 int - x558 int - x559 int - x560 int - x561 int - x562 int - x563 int - x564 int - x565 int - x566 int - x567 int - x568 int - x569 int - x570 int - x571 int - x572 int - x573 int - x574 int - x575 int - x576 int - x577 int - x578 int - x579 int - x580 int - x581 int - x582 int - x583 int - x584 int - x585 int - x586 int - x587 int - x588 int - x589 int - x590 int - x591 int - x592 int - x593 int - x594 int - x595 int - x596 int - x597 int - x598 int - x599 int - x600 int - x601 int - x602 int - x603 int - x604 int - x605 int - x606 int - x607 int - x608 int - x609 int - x610 int - x611 int - x612 int - x613 int - x614 int - x615 int - x616 int - x617 int - x618 int - x619 int - x620 int - x621 int - x622 int - x623 int - x624 int - x625 int - x626 int - x627 int - x628 int - x629 int - x630 int - x631 int - x632 int - x633 int - x634 int - x635 int - x636 int - x637 int - x638 int - x639 int - x640 int - x641 int - x642 int - x643 int - x644 int - x645 int - x646 int - x647 int - x648 int - x649 int - x650 int - x651 int - x652 int - x653 int - x654 int - x655 int - x656 int - x657 int - x658 int - x659 int - x660 int - x661 int - x662 int - x663 int - x664 int - x665 int - x666 int - x667 int - x668 int - x669 int - x670 int - x671 int - x672 int - x673 int - x674 int - x675 int - x676 int - x677 int - x678 int - x679 int - x680 int - x681 int - x682 int - x683 int - x684 int - x685 int - x686 int - x687 int - x688 int - x689 int - x690 int - x691 int - x692 int - x693 int - x694 int - x695 int - x696 int - x697 int - x698 int - x699 int - x700 int - x701 int - x702 int - x703 int - x704 int - x705 int - x706 int - x707 int - x708 int - x709 int - x710 int - x711 int - x712 int - x713 int - x714 int - x715 int - x716 int - x717 int - x718 int - x719 int - x720 int - x721 int - x722 int - x723 int - x724 int - x725 int - x726 int - x727 int - x728 int - x729 int - x730 int - x731 int - x732 int - x733 int - x734 int - x735 int - x736 int - x737 int - x738 int - x739 int - x740 int - x741 int - x742 int - x743 int - x744 int - x745 int - x746 int - x747 int - x748 int - x749 int - x750 int - x751 int - x752 int - x753 int - x754 int - x755 int - x756 int - x757 int - x758 int - x759 int - x760 int - x761 int - x762 int - x763 int - x764 int - x765 int - x766 int - x767 int - x768 int - x769 int - x770 int - x771 int - x772 int - x773 int - x774 int - x775 int - x776 int - x777 int - x778 int - x779 int - x780 int - x781 int - x782 int - x783 int - x784 int - x785 int - x786 int - x787 int - x788 int - x789 int - x790 int - x791 int - x792 int - x793 int - x794 int - x795 int - x796 int - x797 int - x798 int - x799 int - x800 int - x801 int - x802 int - x803 int - x804 int - x805 int - x806 int - x807 int - x808 int - x809 int - x810 int - x811 int - x812 int - x813 int - x814 int - x815 int - x816 int - x817 int - x818 int - x819 int - x820 int - x821 int - x822 int - x823 int - x824 int - x825 int - x826 int - x827 int - x828 int - x829 int - x830 int - x831 int - x832 int - x833 int - x834 int - x835 int - x836 int - x837 int - x838 int - x839 int - x840 int - x841 int - x842 int - x843 int - x844 int - x845 int - x846 int - x847 int - x848 int - x849 int - x850 int - x851 int - x852 int - x853 int - x854 int - x855 int - x856 int - x857 int - x858 int - x859 int - x860 int - x861 int - x862 int - x863 int - x864 int - x865 int - x866 int - x867 int - x868 int - x869 int - x870 int - x871 int - x872 int - x873 int - x874 int - x875 int - x876 int - x877 int - x878 int - x879 int - x880 int - x881 int - x882 int - x883 int - x884 int - x885 int - x886 int - x887 int - x888 int - x889 int - x890 int - x891 int - x892 int - x893 int - x894 int - x895 int - x896 int - x897 int - x898 int - x899 int - x900 int - x901 int - x902 int - x903 int - x904 int - x905 int - x906 int - x907 int - x908 int - x909 int - x910 int - x911 int - x912 int - x913 int - x914 int - x915 int - x916 int - x917 int - x918 int - x919 int - x920 int - x921 int - x922 int - x923 int - x924 int - x925 int - x926 int - x927 int - x928 int - x929 int - x930 int - x931 int - x932 int - x933 int - x934 int - x935 int - x936 int - x937 int - x938 int - x939 int - x940 int - x941 int - x942 int - x943 int - x944 int - x945 int - x946 int - x947 int - x948 int - x949 int - x950 int - x951 int - x952 int - x953 int - x954 int - x955 int - x956 int - x957 int - x958 int - x959 int - x960 int - x961 int - x962 int - x963 int - x964 int - x965 int - x966 int - x967 int - x968 int - x969 int - x970 int - x971 int - x972 int - x973 int - x974 int - x975 int - x976 int - x977 int - x978 int - x979 int - x980 int - x981 int - x982 int - x983 int - x984 int - x985 int - x986 int - x987 int - x988 int - x989 int - x990 int - x991 int - x992 int - x993 int - x994 int - x995 int - x996 int - x997 int - x998 int - x999 int - x1000 int -} - -func (t *T) M() { -} diff --git a/test/fixedbugs/bug302.go b/test/fixedbugs/bug302.go deleted file mode 100644 index a2ab661277a38f..00000000000000 --- a/test/fixedbugs/bug302.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build !nacl,!js,gc -// run - -// 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 main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" -) - -var tmpDir string - -func main() { - fb, err := filepath.Abs("fixedbugs") - if err == nil { - tmpDir, err = ioutil.TempDir("", "bug302") - } - if err != nil { - fmt.Println(err) - os.Exit(1) - } - defer os.RemoveAll(tmpDir) - - run("go", "tool", "compile", filepath.Join(fb, "bug302.dir", "p.go")) - run("go", "tool", "pack", "grc", "pp.a", "p.o") - run("go", "tool", "compile", "-I", ".", filepath.Join(fb, "bug302.dir", "main.go")) -} - -func run(cmd string, args ...string) { - c := exec.Command(cmd, args...) - c.Dir = tmpDir - out, err := c.CombinedOutput() - if err != nil { - fmt.Println(string(out)) - fmt.Println(err) - os.Exit(1) - } -} diff --git a/test/fixedbugs/bug326.go b/test/fixedbugs/bug326.go index dfd8be80050eda..74e06f39d78707 100644 --- a/test/fixedbugs/bug326.go +++ b/test/fixedbugs/bug326.go @@ -19,7 +19,7 @@ func h() (_ int, _ error) { } func i() (int, error) { - return // ERROR "not enough arguments to return|wrong number of return values" + return // ERROR "not enough return values|not enough arguments to return" } func f1() (_ int, err error) { diff --git a/test/fixedbugs/bug345.dir/main.go b/test/fixedbugs/bug345.dir/main.go index b77a2fad5fba07..a53d3e8586ed55 100644 --- a/test/fixedbugs/bug345.dir/main.go +++ b/test/fixedbugs/bug345.dir/main.go @@ -23,7 +23,7 @@ func main() { // main.go:27: cannot use &x (type *"io".SectionReader) as type *"/Users/rsc/g/go/test/fixedbugs/bug345.dir/io".SectionReader in function argument var w io.Writer - bufio.NewWriter(w) // ERROR "[\w.]+[^.]/io|has incompatible type" + bufio.NewWriter(w) // ERROR "[\w.]+[^.]/io|has incompatible type|cannot use" var x goio.SectionReader - io.SR(&x) // ERROR "[\w.]+[^.]/io|has incompatible type" + io.SR(&x) // ERROR "[\w.]+[^.]/io|has incompatible type|cannot use" } diff --git a/test/fixedbugs/bug345.go b/test/fixedbugs/bug345.go index b974a61ffb2b39..d9349fb06fa45c 100644 --- a/test/fixedbugs/bug345.go +++ b/test/fixedbugs/bug345.go @@ -1,10 +1,7 @@ -// +build !windows -// errorcheckdir -n +// errorcheckdir // 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 ignored - -// TODO(ysmolsky): Fix golang.org/issue/25693 to enable on Windows. diff --git a/test/fixedbugs/bug369.go b/test/fixedbugs/bug369.go index 83f638d04632af..5bababd63ec243 100644 --- a/test/fixedbugs/bug369.go +++ b/test/fixedbugs/bug369.go @@ -1,4 +1,4 @@ -// +build !nacl,!js,!windows,gc +// +build !nacl,!js,gc // run // Copyright 2011 The Go Authors. All rights reserved. @@ -29,10 +29,12 @@ func main() { return filepath.Join(tmpDir, name) } - run("go", "tool", "compile", "-N", "-o", tmp("slow.o"), "pkg.go") - run("go", "tool", "compile", "-o", tmp("fast.o"), "pkg.go") - run("go", "tool", "compile", "-D", tmpDir, "-o", tmp("main.o"), "main.go") - run("go", "tool", "link", "-o", tmp("a.exe"), tmp("main.o")) + check(os.Mkdir(tmp("test"), 0777)) + + run("go", "tool", "compile", "-p=test/slow", "-N", "-o", tmp("test/slow.o"), "pkg.go") + run("go", "tool", "compile", "-p=test/fast", "-o", tmp("test/fast.o"), "pkg.go") + run("go", "tool", "compile", "-p=main", "-D", "test", "-I", tmpDir, "-o", tmp("main.o"), "main.go") + run("go", "tool", "link", "-L", tmpDir, "-o", tmp("a.exe"), tmp("main.o")) run(tmp("a.exe")) } diff --git a/test/fixedbugs/bug388.go b/test/fixedbugs/bug388.go index 2d508501e07989..a060c9fd5a75c6 100644 --- a/test/fixedbugs/bug388.go +++ b/test/fixedbugs/bug388.go @@ -13,16 +13,6 @@ func foo(runtime.UintType, i int) { // ERROR "cannot declare name runtime.UintT println(i, runtime.UintType) // GCCGO_ERROR "undefined identifier" } -func bar(i int) { - runtime.UintType := i // ERROR "non-name runtime.UintType|non-name on left side|undefined identifier" - println(runtime.UintType) // GCCGO_ERROR "invalid use of type|undefined identifier" -} - -func baz() { - main.i := 1 // ERROR "non-name main.i|non-name on left side" - println(main.i) // GCCGO_ERROR "no fields or methods" -} - func qux() { var main.i // ERROR "unexpected [.]|expected type" println(main.i) diff --git a/test/fixedbugs/bug388a.go b/test/fixedbugs/bug388a.go new file mode 100644 index 00000000000000..fca6d9c1e4b8a9 --- /dev/null +++ b/test/fixedbugs/bug388a.go @@ -0,0 +1,23 @@ +// errorcheck + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 2231 + +package main +import "runtime" + +func bar(i int) { + runtime.UintType := i // ERROR "non-name runtime.UintType|non-name on left side|undefined" + println(runtime.UintType) // ERROR "invalid use of type|undefined" +} + +func baz() { + main.i := 1 // ERROR "non-name main.i|non-name on left side|undefined" + println(main.i) // ERROR "no fields or methods|undefined" +} + +func main() { +} diff --git a/test/fixedbugs/bug389.go b/test/fixedbugs/bug389.go index 167e64e72c731a..209be8e6f7ace3 100644 --- a/test/fixedbugs/bug389.go +++ b/test/fixedbugs/bug389.go @@ -9,4 +9,4 @@ package foo func fn(a float32) {} -var f func(arg int) = fn // ERROR "cannot use fn .type func.float32.. as type func.int. in assignment|different parameter types|incompatible type" +var f func(arg int) = fn // ERROR "cannot use fn .type func.float32.. as type func.int. in assignment|different parameter types|cannot use fn .*type func.*float32.. as type func.*int. in variable declaration" diff --git a/test/fixedbugs/bug412.go b/test/fixedbugs/bug412.go index 183fb7e4af026b..26ac45fbb40a38 100644 --- a/test/fixedbugs/bug412.go +++ b/test/fixedbugs/bug412.go @@ -7,10 +7,10 @@ package p type t struct { - x int // GCCGO_ERROR "duplicate field name .x." - x int // GC_ERROR "duplicate field x" + x int // GCCGO_ERROR "duplicate field name .x." + x int // GC_ERROR "duplicate field x|x redeclared" } func f(t *t) int { - return t.x // GC_ERROR "ambiguous selector t.x" + return t.x } diff --git a/test/fixedbugs/bug460.dir/b.go b/test/fixedbugs/bug460.dir/b.go index ef646946cf187a..5d388fc413f783 100644 --- a/test/fixedbugs/bug460.dir/b.go +++ b/test/fixedbugs/bug460.dir/b.go @@ -9,9 +9,9 @@ import "./a" var x a.Foo func main() { - x.int = 20 // ERROR "unexported field" - x.int8 = 20 // ERROR "unexported field" - x.error = nil // ERROR "unexported field" - x.rune = 'a' // ERROR "unexported field" - x.byte = 20 // ERROR "unexported field" + x.int = 20 // ERROR "unexported field|undefined" + x.int8 = 20 // ERROR "unexported field|undefined" + x.error = nil // ERROR "unexported field|undefined" + x.rune = 'a' // ERROR "unexported field|undefined" + x.byte = 20 // ERROR "unexported field|undefined" } diff --git a/test/fixedbugs/bug514.go b/test/fixedbugs/bug514.go new file mode 100644 index 00000000000000..9b231853378258 --- /dev/null +++ b/test/fixedbugs/bug514.go @@ -0,0 +1,59 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo + +package main + +import "runtime/cgo" + +type iface interface { + Get() int +} + +type notInHeap struct { + _ cgo.Incomplete + i int +} + +type myInt struct { + f *notInHeap +} + +func (mi myInt) Get() int { + return int(mi.f.i) +} + +type embed struct { + *myInt +} + +var val = 1234 + +var valNotInHeap = notInHeap{i: val} + +func main() { + i := val + check(i) + mi := myInt{f: &valNotInHeap} + check(mi.Get()) + ifv := iface(mi) + check(ifv.Get()) + ifv = iface(&mi) + check(ifv.Get()) + em := embed{&mi} + check(em.Get()) + ifv = em + check(ifv.Get()) + ifv = &em + check(ifv.Get()) +} + +func check(v int) { + if v != val { + panic(v) + } +} diff --git a/test/fixedbugs/bug515.go b/test/fixedbugs/bug515.go new file mode 100644 index 00000000000000..80d426fd676156 --- /dev/null +++ b/test/fixedbugs/bug515.go @@ -0,0 +1,23 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Caused a gofrontend crash. + +//go:build gccgo + +package p + +//go:notinheap +type S1 struct{} + +type S2 struct { + r interface{ Read([]byte) (int, error) } + s1, s2 []byte + p *S1 + n uintptr +} + +var V any = S2{} diff --git a/test/fixedbugs/gcc101994.go b/test/fixedbugs/gcc101994.go new file mode 100644 index 00000000000000..6e1e2b80751812 --- /dev/null +++ b/test/fixedbugs/gcc101994.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// https://gcc.gnu.org/PR101994 +// gccgo compiler crash with zero-sized result. + +package p + +type Empty struct{} + +func F() (int, Empty) { + return 0, Empty{} +} diff --git a/test/fixedbugs/issue10441.go b/test/fixedbugs/issue10441.go index 9bc4948b15b288..7cd26d841b66d1 100644 --- a/test/fixedbugs/issue10441.go +++ b/test/fixedbugs/issue10441.go @@ -1,4 +1,4 @@ -// build +// compile // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue10975.go b/test/fixedbugs/issue10975.go index 89ef23c1a86e55..a58ccce2db16d4 100644 --- a/test/fixedbugs/issue10975.go +++ b/test/fixedbugs/issue10975.go @@ -1,4 +1,4 @@ -// errorcheck +// errorcheck -lang=go1.17 // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -10,7 +10,7 @@ package main type I interface { - int // ERROR "interface contains embedded non-interface|not an interface" + int // ERROR "interface contains embedded non-interface|embedding non-interface type" } func New() I { diff --git a/test/fixedbugs/issue11053.dir/p_test.go b/test/fixedbugs/issue11053.dir/p_test.go index 542c2a349e5ef7..412352d7c5e1f3 100644 --- a/test/fixedbugs/issue11053.dir/p_test.go +++ b/test/fixedbugs/issue11053.dir/p_test.go @@ -5,8 +5,8 @@ package main import ( + "./p" "fmt" - "p" ) type I interface { diff --git a/test/fixedbugs/issue11590.go b/test/fixedbugs/issue11590.go index f2a955f96d0672..9f8978334314c2 100644 --- a/test/fixedbugs/issue11590.go +++ b/test/fixedbugs/issue11590.go @@ -6,6 +6,6 @@ package p -var _ = int8(4) * 300 // ERROR "constant 300 overflows int8" "constant 1200 overflows int8|integer constant overflow" -var _ = complex64(1) * 1e200 // ERROR "constant 1e\+200 overflows complex64|complex real part overflow" -var _ = complex128(1) * 1e500 // ERROR "constant 1e\+500 overflows complex128|complex real part overflow" +var _ = int8(4) * 300 // ERROR "overflows int8" +var _ = complex64(1) * 1e200 // ERROR "complex real part overflow|overflows complex64" +var _ = complex128(1) * 1e500 // ERROR "complex real part overflow|overflows complex128" diff --git a/test/fixedbugs/issue11610.go b/test/fixedbugs/issue11610.go index 7ebfae6709f35d..8d68c98f2d437d 100644 --- a/test/fixedbugs/issue11610.go +++ b/test/fixedbugs/issue11610.go @@ -8,7 +8,6 @@ // following an empty import. package a -import"" // ERROR "import path is empty" var? // ERROR "invalid character U\+003F '\?'|invalid character 0x3f in input file" var x int // ERROR "unexpected var|expected identifier|expected type" diff --git a/test/fixedbugs/issue11610a.go b/test/fixedbugs/issue11610a.go new file mode 100644 index 00000000000000..bbf60b6b049df6 --- /dev/null +++ b/test/fixedbugs/issue11610a.go @@ -0,0 +1,11 @@ +// errorcheck + +// 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. + +// Test an internal compiler error on ? symbol in declaration +// following an empty import. + +package a +import"" // ERROR "import path is empty|invalid import path \(empty string\)" diff --git a/test/fixedbugs/issue11614.go b/test/fixedbugs/issue11614.go index de15f9827ffb28..e8d6badfb90fc2 100644 --- a/test/fixedbugs/issue11614.go +++ b/test/fixedbugs/issue11614.go @@ -1,4 +1,4 @@ -// errorcheck +// errorcheck -lang=go1.17 // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,15 +11,15 @@ package main type I interface { - int // ERROR "interface contains embedded non-interface" + int // ERROR "interface contains embedded non-interface|embedding non-interface type int requires" } func n() { - (I) + (I) // GC_ERROR "is not an expression" } func m() { - (interface{int}) // ERROR "interface contains embedded non-interface" "type interface { int } is not an expression" + (interface{int}) // ERROR "interface contains embedded non-interface|embedding non-interface type int requires" "type interface { int } is not an expression|\(interface{int}\) \(type\) is not an expression" } func main() { diff --git a/test/fixedbugs/issue11656.dir/asm.go b/test/fixedbugs/issue11656.dir/asm.go new file mode 100644 index 00000000000000..cdcb064dc572c5 --- /dev/null +++ b/test/fixedbugs/issue11656.dir/asm.go @@ -0,0 +1,10 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le +// +build ppc64 ppc64le + +package main + +func syncIcache(p uintptr) diff --git a/test/fixedbugs/issue11656.dir/asm_generic.go b/test/fixedbugs/issue11656.dir/asm_generic.go new file mode 100644 index 00000000000000..104d44dfeb49ad --- /dev/null +++ b/test/fixedbugs/issue11656.dir/asm_generic.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !ppc64 && !ppc64le +// +build !ppc64,!ppc64le + +package main + +func syncIcache(p uintptr) { +} diff --git a/test/fixedbugs/issue11656.dir/asm_ppc64.s b/test/fixedbugs/issue11656.dir/asm_ppc64.s new file mode 100644 index 00000000000000..125a197ed8e1f7 --- /dev/null +++ b/test/fixedbugs/issue11656.dir/asm_ppc64.s @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func syncIcache(p uintptr) +TEXT main·syncIcache(SB), NOSPLIT|NOFRAME, $0-0 + SYNC + MOVD (R3), R3 + ICBI (R3) + ISYNC + RET diff --git a/test/fixedbugs/issue11656.dir/asm_ppc64le.s b/test/fixedbugs/issue11656.dir/asm_ppc64le.s new file mode 100644 index 00000000000000..125a197ed8e1f7 --- /dev/null +++ b/test/fixedbugs/issue11656.dir/asm_ppc64le.s @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func syncIcache(p uintptr) +TEXT main·syncIcache(SB), NOSPLIT|NOFRAME, $0-0 + SYNC + MOVD (R3), R3 + ICBI (R3) + ISYNC + RET diff --git a/test/fixedbugs/issue11656.dir/issue11656.go b/test/fixedbugs/issue11656.dir/issue11656.go new file mode 100644 index 00000000000000..a5a52df698ad9c --- /dev/null +++ b/test/fixedbugs/issue11656.dir/issue11656.go @@ -0,0 +1,75 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/binary" + "runtime" + "runtime/debug" + "unsafe" +) + +func main() { + debug.SetPanicOnFault(true) + defer func() { + if err := recover(); err == nil { + panic("not panicking") + } + pc, _, _, _ := runtime.Caller(10) + f := runtime.FuncForPC(pc) + if f == nil || f.Name() != "main.f" { + if f == nil { + println("no func for ", unsafe.Pointer(pc)) + } else { + println("found func:", f.Name()) + } + panic("cannot find main.f on stack") + } + }() + f(20) +} + +func f(n int) { + if n > 0 { + f(n - 1) + } + var f struct { + x uintptr + } + + // We want to force a seg fault, to get a crash at a PC value != 0. + // Not all systems make the data section non-executable. + ill := make([]byte, 64) + switch runtime.GOARCH { + case "386", "amd64": + ill = append(ill[:0], 0x89, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00) // MOVL AX, 0 + case "arm": + binary.LittleEndian.PutUint32(ill[0:4], 0xe3a00000) // MOVW $0, R0 + binary.LittleEndian.PutUint32(ill[4:8], 0xe5800000) // MOVW R0, (R0) + case "arm64": + binary.LittleEndian.PutUint32(ill, 0xf90003ff) // MOVD ZR, (ZR) + case "ppc64": + binary.BigEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) + case "ppc64le": + binary.LittleEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) + case "mips", "mips64": + binary.BigEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) + case "mipsle", "mips64le": + binary.LittleEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) + case "s390x": + ill = append(ill[:0], 0xa7, 0x09, 0x00, 0x00) // MOVD $0, R0 + ill = append(ill, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x24) // MOVD R0, (R0) + case "riscv64": + binary.LittleEndian.PutUint32(ill, 0x00003023) // MOV X0, (X0) + default: + // Just leave it as 0 and hope for the best. + } + + f.x = uintptr(unsafe.Pointer(&ill[0])) + p := &f + fn := *(*func())(unsafe.Pointer(&p)) + syncIcache(f.x) + fn() +} diff --git a/test/fixedbugs/issue11656.go b/test/fixedbugs/issue11656.go index 85fe720b30b667..dba8e354391818 100644 --- a/test/fixedbugs/issue11656.go +++ b/test/fixedbugs/issue11656.go @@ -1,89 +1,22 @@ -// run +// runindir -// Copyright 2015 The Go Authors. All rights reserved. +// Copyright 2022 The Go 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 11656: runtime: jump to bad PC missing good traceback + // windows doesn't work, because Windows exception handling // delivers signals based on the current PC, and that current PC // doesn't go into the Go runtime. -// +build !windows // wasm does not work, because the linear memory is not executable. -// +build !wasm // This test doesn't work on gccgo/GoLLVM, because they will not find // any unwind information for the artificial function, and will not be // able to unwind past that point. -// +build !gccgo - -package main - -import ( - "encoding/binary" - "runtime" - "runtime/debug" - "unsafe" -) - -func main() { - debug.SetPanicOnFault(true) - defer func() { - if err := recover(); err == nil { - panic("not panicking") - } - pc, _, _, _ := runtime.Caller(10) - f := runtime.FuncForPC(pc) - if f == nil || f.Name() != "main.f" { - if f == nil { - println("no func for ", unsafe.Pointer(pc)) - } else { - println("found func:", f.Name()) - } - panic("cannot find main.f on stack") - } - }() - f(20) -} - -func f(n int) { - if n > 0 { - f(n - 1) - } - var f struct { - x uintptr - } - // We want to force a seg fault, to get a crash at a PC value != 0. - // Not all systems make the data section non-executable. - ill := make([]byte, 64) - switch runtime.GOARCH { - case "386", "amd64": - ill = append(ill[:0], 0x89, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00) // MOVL AX, 0 - case "arm": - binary.LittleEndian.PutUint32(ill[0:4], 0xe3a00000) // MOVW $0, R0 - binary.LittleEndian.PutUint32(ill[4:8], 0xe5800000) // MOVW R0, (R0) - case "arm64": - binary.LittleEndian.PutUint32(ill, 0xf90003ff) // MOVD ZR, (ZR) - case "ppc64": - binary.BigEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) - case "ppc64le": - binary.LittleEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) - case "mips", "mips64": - binary.BigEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) - case "mipsle", "mips64le": - binary.LittleEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) - case "s390x": - ill = append(ill[:0], 0xa7, 0x09, 0x00, 0x00) // MOVD $0, R0 - ill = append(ill, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x24) // MOVD R0, (R0) - case "riscv64": - binary.LittleEndian.PutUint32(ill, 0x00003023) // MOV X0, (X0) - default: - // Just leave it as 0 and hope for the best. - } +//go:build !windows && !wasm && !gccgo +// +build !windows,!wasm,!gccgo - f.x = uintptr(unsafe.Pointer(&ill[0])) - p := &f - fn := *(*func())(unsafe.Pointer(&p)) - fn() -} +package ignored diff --git a/test/fixedbugs/issue11737.go b/test/fixedbugs/issue11737.go index eb4bfe8964e82c..aa4abbc327407a 100644 --- a/test/fixedbugs/issue11737.go +++ b/test/fixedbugs/issue11737.go @@ -12,6 +12,6 @@ func f() func s(x interface{}) { switch x { - case f: // ERROR "invalid case f \(type func\(\)\) in switch \(incomparable type\)|cannot compare" + case f: // ERROR "invalid case f \(type func\(\)\) in switch \(incomparable type\)|can only be compared to nil" } } diff --git a/test/fixedbugs/issue11771.go b/test/fixedbugs/issue11771.go index c95dd6ba396584..e5bed186bbd820 100644 --- a/test/fixedbugs/issue11771.go +++ b/test/fixedbugs/issue11771.go @@ -52,7 +52,7 @@ func x() { log.Fatal(err) } - cmd := exec.Command("go", "tool", "compile", "x.go") + cmd := exec.Command("go", "tool", "compile", "-p=p", "x.go") cmd.Dir = dir output, err := cmd.CombinedOutput() if err == nil { diff --git a/test/fixedbugs/issue12006.go b/test/fixedbugs/issue12006.go index 0a2ef8dad04dbc..e878bc48e241e1 100644 --- a/test/fixedbugs/issue12006.go +++ b/test/fixedbugs/issue12006.go @@ -87,7 +87,7 @@ func TFooI() { FooI(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "... argument does not escape" } -func FooJ(args ...interface{}) *int32 { // ERROR "leaking param: args to result ~r1 level=1" +func FooJ(args ...interface{}) *int32 { // ERROR "leaking param: args to result ~r0 level=1" for i := 0; i < len(args); i++ { switch x := args[i].(type) { case nil: @@ -123,7 +123,7 @@ type fakeSlice struct { a *[4]interface{} } -func FooK(args fakeSlice) *int32 { // ERROR "leaking param: args to result ~r1 level=1" +func FooK(args fakeSlice) *int32 { // ERROR "leaking param: args to result ~r0 level=1" for i := 0; i < args.l; i++ { switch x := (*args.a)[i].(type) { case nil: @@ -148,7 +148,7 @@ func TFooK2() { isink = FooK(fs) } -func FooL(args []interface{}) *int32 { // ERROR "leaking param: args to result ~r1 level=1" +func FooL(args []interface{}) *int32 { // ERROR "leaking param: args to result ~r0 level=1" for i := 0; i < len(args); i++ { switch x := args[i].(type) { case nil: diff --git a/test/fixedbugs/issue12588.go b/test/fixedbugs/issue12588.go index 950ef36e206c76..dc8111198c49d2 100644 --- a/test/fixedbugs/issue12588.go +++ b/test/fixedbugs/issue12588.go @@ -35,7 +35,7 @@ func g(a *A) int { // ERROR "a does not escape" return 0 } -func h(a *B) *uint64 { // ERROR "leaking param: a to result ~r1 level=1" +func h(a *B) *uint64 { // ERROR "leaking param: a to result ~r0 level=1" for i, x := range &a.b { if i == 0 { return x @@ -44,7 +44,7 @@ func h(a *B) *uint64 { // ERROR "leaking param: a to result ~r1 level=1" return nil } -func h2(a *B) *uint64 { // ERROR "leaking param: a to result ~r1 level=1" +func h2(a *B) *uint64 { // ERROR "leaking param: a to result ~r0 level=1" p := &a.b for i, x := range p { if i == 0 { @@ -55,7 +55,7 @@ func h2(a *B) *uint64 { // ERROR "leaking param: a to result ~r1 level=1" } // Seems like below should be level=1, not 0. -func k(a B) *uint64 { // ERROR "leaking param: a to result ~r1 level=0" +func k(a B) *uint64 { // ERROR "leaking param: a to result ~r0 level=0" for i, x := range &a.b { if i == 0 { return x diff --git a/test/fixedbugs/issue13273.go b/test/fixedbugs/issue13273.go index 2498da4d47161a..ea729d60803245 100644 --- a/test/fixedbugs/issue13273.go +++ b/test/fixedbugs/issue13273.go @@ -47,9 +47,9 @@ func f() { <-(<-chan (<-chan (<-chan (<-chan int))))(nil) <-(<-chan (<-chan (<-chan (<-chan (<-chan int)))))(nil) - type _ <-<-chan int // ERROR "unexpected <-, expecting chan|expected .*chan.*" - <-<-chan int // ERROR "unexpected <-, expecting chan|expecting {" (new parser: same error as for type decl) + type _ <-<-chan int // ERROR "unexpected <-, expected chan|expected .*chan.*" + <-<-chan int // ERROR "unexpected <-, expected chan|expecting {" (new parser: same error as for type decl) - type _ <-chan<-int // ERROR "unexpected int, expecting chan|expected .*chan.*|expecting chan|expected .*;.* or .*}.* or newline" - <-chan<-int // ERROR "unexpected int, expecting chan|expecting {" (new parser: same error as for type decl) + type _ <-chan<-int // ERROR "unexpected int, expected chan|expected .*chan.*|expected chan|expected .*;.* or .*}.* or newline" + <-chan<-int // ERROR "unexpected int, expected chan|expecting {" (new parser: same error as for type decl) } diff --git a/test/fixedbugs/issue13319.go b/test/fixedbugs/issue13319.go index c9b4896a051135..7e1df3e45ee8a5 100644 --- a/test/fixedbugs/issue13319.go +++ b/test/fixedbugs/issue13319.go @@ -9,10 +9,10 @@ package main func f(int, int) { switch x { case 1: - f(1, g() // ERROR "expecting \)|expecting comma or \)" + f(1, g() // ERROR "expecting \)|possibly missing comma or \)" case 2: f() case 3: - f(1, g() // ERROR "expecting \)|expecting comma or \)" + f(1, g() // ERROR "expecting \)|possibly missing comma or \)" } } diff --git a/test/fixedbugs/issue13365.go b/test/fixedbugs/issue13365.go index b22fa0fb4e576d..02c6e0369844e0 100644 --- a/test/fixedbugs/issue13365.go +++ b/test/fixedbugs/issue13365.go @@ -16,7 +16,7 @@ func main() { _ = [...]int{-1: 0} // ERROR "index must be non\-negative integer constant|index expression is negative|must not be negative" _ = []int{100: 0} - _ = [10]int{100: 0} // ERROR "array index 100 out of bounds|out of range" + _ = [10]int{100: 0} // ERROR "index 100 out of bounds|out of range" _ = [...]int{100: 0} _ = []int{t} // ERROR "cannot use .* as (type )?int( in slice literal)?|incompatible type" diff --git a/test/fixedbugs/issue14520.go b/test/fixedbugs/issue14520.go index 0b840ff4bee9ad..29cc270deb070b 100644 --- a/test/fixedbugs/issue14520.go +++ b/test/fixedbugs/issue14520.go @@ -6,9 +6,6 @@ package f -import /* // ERROR "import path" */ ` -bogus` - func f(x int /* // GC_ERROR "unexpected newline" */) // GCCGO_ERROR "expected .*\).*|expected declaration" diff --git a/test/fixedbugs/issue14520a.go b/test/fixedbugs/issue14520a.go new file mode 100644 index 00000000000000..bb45d7e186de3f --- /dev/null +++ b/test/fixedbugs/issue14520a.go @@ -0,0 +1,10 @@ +// errorcheck + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package f + +import /* // ERROR "import path" */ ` +bogus` diff --git a/test/fixedbugs/issue14652.go b/test/fixedbugs/issue14652.go index d53b4126683148..586663b676fd9c 100644 --- a/test/fixedbugs/issue14652.go +++ b/test/fixedbugs/issue14652.go @@ -1,4 +1,4 @@ -// errorcheck +// compile // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -6,4 +6,5 @@ package p -var x any // ERROR "undefined: any|undefined type .*any.*" +// any is now permitted instead of interface{} +var x any diff --git a/test/fixedbugs/issue14999.go b/test/fixedbugs/issue14999.go index b648441fc29779..a25a50e519a5ea 100644 --- a/test/fixedbugs/issue14999.go +++ b/test/fixedbugs/issue14999.go @@ -7,11 +7,11 @@ package p func f(x int) func(int) int { - return func(y int) int { return x + y } // ERROR "heap-allocated closure, not allowed in runtime" + return func(y int) int { return x + y } // ERROR "heap-allocated closure f\.func1, not allowed in runtime" } func g(x int) func(int) int { // ERROR "x escapes to heap, not allowed in runtime" - return func(y int) int { // ERROR "heap-allocated closure, not allowed in runtime" + return func(y int) int { // ERROR "heap-allocated closure g\.func1, not allowed in runtime" x += y return x + y } diff --git a/test/fixedbugs/issue16008.go b/test/fixedbugs/issue16008.go index 45457cdb7f5682..b88e2351b7f58d 100644 --- a/test/fixedbugs/issue16008.go +++ b/test/fixedbugs/issue16008.go @@ -37,7 +37,7 @@ type Node struct { type MemoryStore struct { } -// go:noinline +//go:noinline func setupNodes(n int) (s *MemoryStore, nodeIDs []string) { return } diff --git a/test/fixedbugs/issue16428.go b/test/fixedbugs/issue16428.go index 5696d186c77d08..91e1079959a0a3 100644 --- a/test/fixedbugs/issue16428.go +++ b/test/fixedbugs/issue16428.go @@ -7,6 +7,6 @@ package p var ( - b = [...]byte("abc") // ERROR "outside of array literal" + b = [...]byte("abc") // ERROR "outside of array literal|outside a composite literal" s = len(b) ) diff --git a/test/fixedbugs/issue17038.go b/test/fixedbugs/issue17038.go index 4d7422c60c056e..1b65ffc1f0eec6 100644 --- a/test/fixedbugs/issue17038.go +++ b/test/fixedbugs/issue17038.go @@ -6,4 +6,4 @@ package main -const A = complex(0()) // ERROR "cannot call non-function" "not enough arguments" +const A = complex(0()) // ERROR "cannot call non-function" diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go index bb34e4ee97a3bf..111fa81e136368 100644 --- a/test/fixedbugs/issue17645.go +++ b/test/fixedbugs/issue17645.go @@ -12,5 +12,5 @@ type Foo struct { func main() { var s []int - var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(type untyped string\) as type int in field value|incompatible type" "cannot use Foo{...} \(type Foo\) as type int in append" "cannot use append\(s\, Foo{...}\) \(type \[\]int\) as type string in assignment" + var _ string = append(s, Foo{""}) // ERROR "cannot use .. \(.*untyped string.*\) as .*int.*|incompatible type" "cannot use Foo{.*} \(.*type Foo\) as type int in .*append" "cannot use append\(s\, Foo{.*}\) \(.*type \[\]int\) as type string in (assignment|variable declaration)" } diff --git a/test/fixedbugs/issue18092.go b/test/fixedbugs/issue18092.go index a0f7eddda5a773..c8e60f31c515fe 100644 --- a/test/fixedbugs/issue18092.go +++ b/test/fixedbugs/issue18092.go @@ -11,5 +11,5 @@ func _() { select { default: case <-ch { // GCCGO_ERROR "expected colon" - } // GC_ERROR "expecting :" + } // GC_ERROR "expected :" } diff --git a/test/fixedbugs/issue18747.go b/test/fixedbugs/issue18747.go index fb8331fcc9eeed..4eabe0e61df598 100644 --- a/test/fixedbugs/issue18747.go +++ b/test/fixedbugs/issue18747.go @@ -23,6 +23,6 @@ func _ () { if ; foo {} - if foo // ERROR "unexpected newline, expecting { after if clause" + if foo // ERROR "unexpected newline, expected { after if clause" {} } diff --git a/test/fixedbugs/issue19012.go b/test/fixedbugs/issue19012.go index 158618aa27efcc..77b22360635eea 100644 --- a/test/fixedbugs/issue19012.go +++ b/test/fixedbugs/issue19012.go @@ -13,9 +13,9 @@ package main func f(x int, y uint) { if true { - return "a" > 10 // ERROR "^too many arguments to return$|return with value in function with no return|mismatched types" + return "a" > 10 // ERROR "^too many arguments to return$|return with value in function with no return|no result values expected|mismatched types" } - return "gopher" == true, 10 // ERROR "^too many arguments to return$|return with value in function with no return|mismatched types" + return "gopher" == true, 10 // ERROR "^too many arguments to return$|return with value in function with no return|no result values expected|mismatched types" } func main() { diff --git a/test/fixedbugs/issue19323.go b/test/fixedbugs/issue19323.go index 77cac3ee16a230..5db0a48a345faf 100644 --- a/test/fixedbugs/issue19323.go +++ b/test/fixedbugs/issue19323.go @@ -15,5 +15,5 @@ func f() { func g2() ([]byte, []byte) { return nil, nil } func f2() { - g2()[:] // ERROR "multiple-value g2.. in single-value context|attempt to slice object that is not|2\-valued g" + g2()[:] // ERROR "multiple-value g2.* in single-value context|attempt to slice object that is not|2\-valued g" } diff --git a/test/fixedbugs/issue19467.dir/z.go b/test/fixedbugs/issue19467.dir/z.go index d381103ce7ef3f..cfbf34869cb7cd 100644 --- a/test/fixedbugs/issue19467.dir/z.go +++ b/test/fixedbugs/issue19467.dir/z.go @@ -5,9 +5,10 @@ package main import ( - "./mysync" "log" "runtime" + + "./mysync" ) func main() { @@ -23,8 +24,8 @@ func main() { } } expecting := []string{ - "mysync.(*WaitGroup).Add", - "mysync.(*WaitGroup).Done", + "test/mysync.(*WaitGroup).Add", + "test/mysync.(*WaitGroup).Done", } for i := 0; i < 2; i++ { if frames[i].Function != expecting[i] { diff --git a/test/fixedbugs/issue19667.go b/test/fixedbugs/issue19667.go index e33e35048752df..4b0925add24719 100644 --- a/test/fixedbugs/issue19667.go +++ b/test/fixedbugs/issue19667.go @@ -10,4 +10,4 @@ package p func f() { if err := http.ListenAndServe( // GCCGO_ERROR "undefined name" -} // ERROR "unexpected }, expecting expression|expected operand|missing .*\)|expected .*;|expected .*{" +} // ERROR "unexpected }, expected expression|expected operand|missing .*\)|expected .*;|expected .*{" diff --git a/test/fixedbugs/issue20014.dir/main.go b/test/fixedbugs/issue20014.dir/main.go index ac9957de40a035..098ac6b99a8006 100644 --- a/test/fixedbugs/issue20014.dir/main.go +++ b/test/fixedbugs/issue20014.dir/main.go @@ -5,6 +5,7 @@ package main import ( + "sort" "strings" "issue20014.dir/a" @@ -13,12 +14,17 @@ import ( func main() { samePackage() crossPackage() + // Print fields registered with field tracking. + var fields []string for _, line := range strings.Split(fieldTrackInfo, "\n") { - if line == "" { - continue + if line != "" { + fields = append(fields, strings.Split(line, "\t")[0]) } - println(strings.Split(line, "\t")[0]) + } + sort.Strings(fields) // for stable output, regardless of optimizations + for _, field := range fields { + println(field) } } diff --git a/test/fixedbugs/issue20014.out b/test/fixedbugs/issue20014.out index 252e78da5ecaa7..3f79c77ba3cf4c 100644 --- a/test/fixedbugs/issue20014.out +++ b/test/fixedbugs/issue20014.out @@ -2,5 +2,5 @@ 0 0 0 -main.T.X issue20014.dir/a.T.X +main.T.X diff --git a/test/fixedbugs/issue20250.go b/test/fixedbugs/issue20250.go index 1a513bea56df1f..7c6e796e8e330d 100644 --- a/test/fixedbugs/issue20250.go +++ b/test/fixedbugs/issue20250.go @@ -16,8 +16,10 @@ type T struct { func f(a T) { // ERROR "live at entry to f: a" var e interface{} // ERROR "stack object e interface \{\}$" - func() { // ERROR "live at entry to f.func1: a &e" - e = a.s // ERROR "live at call to convT2E: &e" "stack object a T$" + // TODO(go.dev/issue/54402): Investigate why "live at entry to + // f.func1" is sensitive to regabi. + func() { // ERROR "live at entry to f.func1: (a &e|&e a)" + e = a.s // ERROR "live at call to convT: &e" "stack object a T$" }() // Before the fix, both a and e were live at the previous line. _ = e diff --git a/test/fixedbugs/issue20789.go b/test/fixedbugs/issue20789.go index 82aec965ede64d..4e4eed42a7ff4b 100644 --- a/test/fixedbugs/issue20789.go +++ b/test/fixedbugs/issue20789.go @@ -10,4 +10,4 @@ // there yet, so put it here for now. See also #20800.) package e -func([<-chan<-[func u){go // ERROR "unexpected u", ERROR "must be function call" \ No newline at end of file +func([<-chan<-[func u){go // ERROR "unexpected u" \ No newline at end of file diff --git a/test/fixedbugs/issue21317.go b/test/fixedbugs/issue21317.go index 32b660c1639169..fe51ef1738a7fe 100644 --- a/test/fixedbugs/issue21317.go +++ b/test/fixedbugs/issue21317.go @@ -38,7 +38,7 @@ func main() { defer os.RemoveAll(f.Name()) // compile and test output - cmd := exec.Command("go", "tool", "compile", f.Name()) + cmd := exec.Command("go", "tool", "compile", "-p=main", f.Name()) out, err := cmd.CombinedOutput() if err == nil { log.Fatalf("expected cmd/compile to fail") diff --git a/test/fixedbugs/issue21979.go b/test/fixedbugs/issue21979.go index addf786c037a06..c6575a3928a417 100644 --- a/test/fixedbugs/issue21979.go +++ b/test/fixedbugs/issue21979.go @@ -7,39 +7,40 @@ package p func f() { - _ = bool("") // ERROR "cannot convert .. \(type untyped string\) to type bool|invalid type conversion" - _ = bool(1) // ERROR "cannot convert 1 \(type untyped int\) to type bool|invalid type conversion" - _ = bool(1.0) // ERROR "cannot convert 1 \(type untyped float\) to type bool|invalid type conversion" - _ = bool(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(type untyped complex\) to type bool|invalid type conversion" + _ = bool("") // ERROR "cannot convert .. \(.*untyped string.*\) to type bool|invalid type conversion" + _ = bool(1) // ERROR "cannot convert 1 \(.*untyped int.*\) to type bool|invalid type conversion" + _ = bool(1.0) // ERROR "cannot convert 1.* \(.*untyped float.*\) to type bool|invalid type conversion" + _ = bool(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(.*untyped complex.*\) to type bool|invalid type conversion" - _ = string(true) // ERROR "cannot convert true \(type untyped bool\) to type string|invalid type conversion" + _ = string(true) // ERROR "cannot convert true \(.*untyped bool.*\) to type string|invalid type conversion" _ = string(-1) - _ = string(1.0) // ERROR "cannot convert 1 \(type untyped float\) to type string|invalid type conversion" - _ = string(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(type untyped complex\) to type string|invalid type conversion" + _ = string(1.0) // ERROR "cannot convert 1.* \(.*untyped float.*\) to type string|invalid type conversion" + _ = string(-4 + 2i) // ERROR "cannot convert -4 \+ 2i \(.*untyped complex.*\) to type string|invalid type conversion" - _ = int("") // ERROR "cannot convert .. \(type untyped string\) to type int|invalid type conversion" - _ = int(true) // ERROR "cannot convert true \(type untyped bool\) to type int|invalid type conversion" + _ = int("") // ERROR "cannot convert .. \(.*untyped string.*\) to type int|invalid type conversion" + _ = int(true) // ERROR "cannot convert true \(.*untyped bool.*\) to type int|invalid type conversion" _ = int(-1) _ = int(1) _ = int(1.0) - _ = int(-4 + 2i) // ERROR "truncated to integer" + _ = int(-4 + 2i) // ERROR "truncated to integer|cannot convert -4 \+ 2i \(.*untyped complex.*\) to type int" - _ = uint("") // ERROR "cannot convert .. \(type untyped string\) to type uint|invalid type conversion" - _ = uint(true) // ERROR "cannot convert true \(type untyped bool\) to type uint|invalid type conversion" - _ = uint(-1) // ERROR "constant -1 overflows uint|integer constant overflow" + _ = uint("") // ERROR "cannot convert .. \(.*untyped string.*\) to type uint|invalid type conversion" + _ = uint(true) // ERROR "cannot convert true \(.*untyped bool.*\) to type uint|invalid type conversion" + _ = uint(-1) // ERROR "constant -1 overflows uint|integer constant overflow|cannot convert -1 \(untyped int constant\) to type uint" _ = uint(1) _ = uint(1.0) - _ = uint(-4 + 2i) // ERROR "constant -4 overflows uint" "truncated to integer" + // types1 reports extra error "truncated to integer" + _ = uint(-4 + 2i) // ERROR "constant -4 overflows uint|truncated to integer|cannot convert -4 \+ 2i \(untyped complex constant.*\) to type uint" - _ = float64("") // ERROR "cannot convert .. \(type untyped string\) to type float64|invalid type conversion" - _ = float64(true) // ERROR "cannot convert true \(type untyped bool\) to type float64|invalid type conversion" + _ = float64("") // ERROR "cannot convert .. \(.*untyped string.*\) to type float64|invalid type conversion" + _ = float64(true) // ERROR "cannot convert true \(.*untyped bool.*\) to type float64|invalid type conversion" _ = float64(-1) _ = float64(1) _ = float64(1.0) - _ = float64(-4 + 2i) // ERROR "truncated to" + _ = float64(-4 + 2i) // ERROR "truncated to|cannot convert -4 \+ 2i \(.*untyped complex.*\) to type float64" - _ = complex128("") // ERROR "cannot convert .. \(type untyped string\) to type complex128|invalid type conversion" - _ = complex128(true) // ERROR "cannot convert true \(type untyped bool\) to type complex128|invalid type conversion" + _ = complex128("") // ERROR "cannot convert .. \(.*untyped string.*\) to type complex128|invalid type conversion" + _ = complex128(true) // ERROR "cannot convert true \(.*untyped bool.*\) to type complex128|invalid type conversion" _ = complex128(-1) _ = complex128(1) _ = complex128(1.0) diff --git a/test/fixedbugs/issue22076.go b/test/fixedbugs/issue22076.go index 5d628b96bd3a08..b383a674e2f66b 100644 --- a/test/fixedbugs/issue22076.go +++ b/test/fixedbugs/issue22076.go @@ -13,12 +13,12 @@ import . "bytes" var _ Reader // use "bytes" import -func _() { +func f1() { Buffer := 0 _ = Buffer } -func _() { +func f2() { for Buffer := range []int{} { _ = Buffer } diff --git a/test/fixedbugs/issue22660.go b/test/fixedbugs/issue22660.go index 9ce9c4d732e6d4..7f542c5153437f 100644 --- a/test/fixedbugs/issue22660.go +++ b/test/fixedbugs/issue22660.go @@ -35,7 +35,7 @@ func main() { log.Fatal(err) } - out, err := exec.Command("go", "tool", "compile", fmt.Sprintf("-trimpath=%s", path), f.Name()).CombinedOutput() + out, err := exec.Command("go", "tool", "compile", "-p=p", fmt.Sprintf("-trimpath=%s", path), f.Name()).CombinedOutput() if err == nil { log.Fatalf("expected compiling %s to fail", f.Name()) } diff --git a/test/fixedbugs/issue22662b.go b/test/fixedbugs/issue22662b.go index 8da17679be08e9..df4f28429c7a12 100644 --- a/test/fixedbugs/issue22662b.go +++ b/test/fixedbugs/issue22662b.go @@ -48,7 +48,7 @@ func main() { log.Fatal(err) } - out, err := exec.Command("go", "tool", "compile", f.Name()).CombinedOutput() + out, err := exec.Command("go", "tool", "compile", "-p=p", f.Name()).CombinedOutput() if err == nil { log.Fatalf("expected compiling\n---\n%s\n---\nto fail", test.src) } diff --git a/test/fixedbugs/issue22962.dir/b.go b/test/fixedbugs/issue22962.dir/b.go index e1568c8fe9b323..4937ef577f37d5 100644 --- a/test/fixedbugs/issue22962.dir/b.go +++ b/test/fixedbugs/issue22962.dir/b.go @@ -4,6 +4,6 @@ package b -import "a" +import "./a" var V = func() { a.F() } diff --git a/test/fixedbugs/issue23179.dir/b.go b/test/fixedbugs/issue23179.dir/b.go index bec3d15e1e1f5c..4bde498cd8713a 100644 --- a/test/fixedbugs/issue23179.dir/b.go +++ b/test/fixedbugs/issue23179.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" func G(x int) int { return a.F(x, 1, false, a.Large{}) diff --git a/test/fixedbugs/issue23536.go b/test/fixedbugs/issue23536.go new file mode 100644 index 00000000000000..07b5033d361187 --- /dev/null +++ b/test/fixedbugs/issue23536.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go 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 case where a slice of a user-defined byte type (not uint8 or byte) is +// converted to a string. Same for slice of runes. + +package main + +type MyByte byte + +type MyRune rune + +func main() { + var y []MyByte + _ = string(y) + + var z []MyRune + _ = string(z) +} diff --git a/test/fixedbugs/issue23587.go b/test/fixedbugs/issue23587.go index 2308992347a9c7..9040767f8cb626 100644 --- a/test/fixedbugs/issue23587.go +++ b/test/fixedbugs/issue23587.go @@ -7,7 +7,7 @@ package p func _(x int) { - _ = ~x // ERROR "unexpected ~" + _ = ~x // unary ~ permitted but the type-checker will complain } func _(x int) { diff --git a/test/fixedbugs/issue23664.go b/test/fixedbugs/issue23664.go index 1925ebffe73778..715654be70deb5 100644 --- a/test/fixedbugs/issue23664.go +++ b/test/fixedbugs/issue23664.go @@ -9,9 +9,9 @@ package p func f() { - if f() true { // ERROR "unexpected true, expecting {" + if f() true { // ERROR "unexpected true, expected {" } - switch f() true { // ERROR "unexpected true, expecting {" + switch f() true { // ERROR "unexpected true, expected {" } } diff --git a/test/fixedbugs/issue23732.go b/test/fixedbugs/issue23732.go index db2d182234100e..79b60e26df0db0 100644 --- a/test/fixedbugs/issue23732.go +++ b/test/fixedbugs/issue23732.go @@ -24,19 +24,19 @@ func main() { _ = Foo{ // GCCGO_ERROR "too few expressions" 1, 2, - 3, // GC_ERROR "too few values in Foo{...}" - } + 3, + } // GC_ERROR "too few values in" _ = Foo{ 1, 2, 3, - Bar{"A", "B"}, // ERROR "too many values in Bar{...}|too many expressions" + Bar{"A", "B"}, // ERROR "too many values in|too many expressions" } _ = Foo{ // GCCGO_ERROR "too few expressions" 1, 2, - Bar{"A", "B"}, // ERROR "too many values in Bar{...}|too many expressions" "too few values in Foo{...}" - } + Bar{"A", "B"}, // ERROR "too many values in|too many expressions" + } // GC_ERROR "too few values in" } diff --git a/test/fixedbugs/issue23814.go b/test/fixedbugs/issue23814.go new file mode 100644 index 00000000000000..25ed2322b6447f --- /dev/null +++ b/test/fixedbugs/issue23814.go @@ -0,0 +1,61 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Examples from the language spec section on string conversions. + +package main + +func main() { + // 1 + _ = string('a') // "a" + _ = string(-1) // "\ufffd" == "\xef\xbf\xbd" + _ = string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8" + + type myString string + _ = myString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5" + + // 2 + _ = string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + _ = string([]byte{}) // "" + _ = string([]byte(nil)) // "" + + type bytes []byte + _ = string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" + + type myByte byte + _ = string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" + _ = myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'}) // "🌍 + + // 3 + _ = string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + _ = string([]rune{}) // "" + _ = string([]rune(nil)) // "" + + type runes []rune + _ = string(runes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" + + type myRune rune + _ = string([]myRune{0x266b, 0x266c}) // "\u266b\u266c" == "♫♬" + _ = myString([]myRune{0x1f30e}) // "\U0001f30e" == "🌎 + + // 4 + _ = []byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} + _ = []byte("") // []byte{} + + _ = bytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} + + _ = []myByte("world!") // []myByte{'w', 'o', 'r', 'l', 'd', '!'} + _ = []myByte(myString("🌏")) // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'} + + // 5 + _ = []rune(myString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4} + _ = []rune("") // []rune{} + + _ = runes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4} + + _ = []myRune("♫♬") // []myRune{0x266b, 0x266c} + _ = []myRune(myString("🌐")) // []myRune{0x1f310} +} diff --git a/test/fixedbugs/issue23868.go b/test/fixedbugs/issue23868.go new file mode 100644 index 00000000000000..af15c5fb39bf9b --- /dev/null +++ b/test/fixedbugs/issue23868.go @@ -0,0 +1,14 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Crashed gccgo. + +package p + +var F func([0]int) int +var G func() [0]int + +var V = make([]int, F(G())) diff --git a/test/fixedbugs/issue23870.go b/test/fixedbugs/issue23870.go new file mode 100644 index 00000000000000..02aa2949c33d15 --- /dev/null +++ b/test/fixedbugs/issue23870.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Crashed gccgo. + +package p + +var F func() [0]struct{ + A int +} + +var i int +var V = (F()[i]).A diff --git a/test/fixedbugs/issue24651a.go b/test/fixedbugs/issue24651a.go index 6c7bf3090877fc..1bfe8ac1ce8243 100644 --- a/test/fixedbugs/issue24651a.go +++ b/test/fixedbugs/issue24651a.go @@ -21,5 +21,5 @@ var x = 5 //go:noinline Provide a clean, constant reason for not inlining main func main() { // ERROR "cannot inline main: marked go:noinline$" println("Foo(", x, ")=", Foo(x)) - println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar" } diff --git a/test/fixedbugs/issue24651b.go b/test/fixedbugs/issue24651b.go index aa88a6787b96bf..2af54fc4b53e52 100644 --- a/test/fixedbugs/issue24651b.go +++ b/test/fixedbugs/issue24651b.go @@ -19,6 +19,6 @@ var x = 5 //go:noinline Provide a clean, constant reason for not inlining main func main() { // ERROR "cannot inline main: marked go:noinline$" - println("Foo(", x, ")=", Foo(x)) // ERROR "inlining call to Foo func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" - println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar func\(int\) int { return x \* \(x \+ 1\) \* \(x \+ 2\) }$" + println("Foo(", x, ")=", Foo(x)) // ERROR "inlining call to Foo" + println("Bar(", x, ")=", Bar(x)) // ERROR "inlining call to Bar" } diff --git a/test/fixedbugs/issue25897a.go b/test/fixedbugs/issue25897a.go index 6a724a79a59eb7..d4fa6c82feb507 100644 --- a/test/fixedbugs/issue25897a.go +++ b/test/fixedbugs/issue25897a.go @@ -18,17 +18,34 @@ const N = 100 func main() { runtime.GOMAXPROCS(1) + // Run GC in a loop. This makes it more likely GC will catch + // an unstarted goroutine then if we were to GC after kicking + // everything off. + go func() { + for { + runtime.GC() + } + }() c := make(chan bool, N) for i := 0; i < N; i++ { + // Test both with an argument and without because this + // affects whether the compiler needs to generate a + // wrapper closure for the "go" statement. f := reflect.MakeFunc(reflect.TypeOf(((func(*int))(nil))), func(args []reflect.Value) []reflect.Value { c <- true return nil }).Interface().(func(*int)) go f(nil) + + g := reflect.MakeFunc(reflect.TypeOf(((func())(nil))), + func(args []reflect.Value) []reflect.Value { + c <- true + return nil + }).Interface().(func()) + go g() } - runtime.GC() - for i := 0; i < N; i++ { + for i := 0; i < N*2; i++ { <-c } } diff --git a/test/fixedbugs/issue25958.go b/test/fixedbugs/issue25958.go index 90fcee15fd0b90..91358f83fe0ae0 100644 --- a/test/fixedbugs/issue25958.go +++ b/test/fixedbugs/issue25958.go @@ -11,7 +11,7 @@ package p func f(done chan struct{}) { select { - case done: // ERROR "must be receive|expected .*<-.* or .*=" "not used" - case (chan struct{})(done): // ERROR "must be receive|expected .*<-.* or .*=" + case done: // ERROR "must be receive|expected .*<-.* or .*=|must be send or receive|not used" + case (chan struct{})(done): // ERROR "must be receive|expected .*<-.* or .*=|must be send or receive" } } diff --git a/test/fixedbugs/issue26163.go b/test/fixedbugs/issue26163.go index d141a2797d60f1..3f3d77859def81 100644 --- a/test/fixedbugs/issue26163.go +++ b/test/fixedbugs/issue26163.go @@ -1,4 +1,4 @@ -// compile -N -d=softfloat -goexperiment noregabiargs +// compile -N -d=softfloat // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/fixedbugs/issue26411.go b/test/fixedbugs/issue26411.go index 5f40bf25229a1a..eb17960c47670d 100644 --- a/test/fixedbugs/issue26411.go +++ b/test/fixedbugs/issue26411.go @@ -75,7 +75,7 @@ bar : log.Printf("#%d: failed to create file %s", i, filename) continue } - output, _ := exec.Command("go", "tool", "compile", filename).CombinedOutput() + output, _ := exec.Command("go", "tool", "compile", "-p=p", filename).CombinedOutput() // remove each matching error from the output for _, err := range test.errors { diff --git a/test/fixedbugs/issue26616.go b/test/fixedbugs/issue26616.go index d5210e87b0f5af..edf88d489ecc66 100644 --- a/test/fixedbugs/issue26616.go +++ b/test/fixedbugs/issue26616.go @@ -6,11 +6,11 @@ package p -var x int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued" +var x int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value " func f() { - var _ int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued" - var a int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|3\-valued" + var _ int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value " + var a int = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|multiple-value " a = three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|multiple-value function call in single-value context|cannot assign" b := three() // ERROR "assignment mismatch: 1 variable but three returns 3 values|single variable set to multiple-value|multiple-value function call in single-value context|cannot initialize" _, _ = a, b diff --git a/test/fixedbugs/issue27557.go b/test/fixedbugs/issue27557.go index e35ab5a1690fb0..f609b27faa5e12 100644 --- a/test/fixedbugs/issue27557.go +++ b/test/fixedbugs/issue27557.go @@ -8,19 +8,19 @@ package p var sink interface{} -func _() { +func f1() { var t T f := t.noescape // ERROR "t.noescape does not escape" f() } -func _() { +func f2() { var t T // ERROR "moved to heap" f := t.escape // ERROR "t.escape does not escape" f() } -func _() { +func f3() { var t T // ERROR "moved to heap" f := t.returns // ERROR "t.returns does not escape" sink = f() diff --git a/test/fixedbugs/issue27595.go b/test/fixedbugs/issue27595.go index 2fc0eb2a58ea17..86fb6384cdb5b4 100644 --- a/test/fixedbugs/issue27595.go +++ b/test/fixedbugs/issue27595.go @@ -6,7 +6,7 @@ package main -var a = twoResults() // ERROR "assignment mismatch: 1 variable but twoResults returns 2 values|2\-valued" +var a = twoResults() // ERROR "assignment mismatch: 1 variable but twoResults returns 2 values|multiple-value twoResults\(\) .*in single-value context" var b, c, d = twoResults() // ERROR "assignment mismatch: 3 variables but twoResults returns 2 values|cannot initialize" var e, f = oneResult() // ERROR "assignment mismatch: 2 variables but oneResult returns 1 value|cannot initialize" diff --git "a/test/fixedbugs/issue27836.dir/\303\204foo.go" "b/test/fixedbugs/issue27836.dir/\303\204foo.go" deleted file mode 100644 index 8b6a814c3c4de4..00000000000000 --- "a/test/fixedbugs/issue27836.dir/\303\204foo.go" +++ /dev/null @@ -1,13 +0,0 @@ -package Äfoo - -var ÄbarV int = 101 - -func Äbar(x int) int { - defer func() { ÄbarV += 3 }() - return Äblix(x) -} - -func Äblix(x int) int { - defer func() { ÄbarV += 9 }() - return ÄbarV + x -} diff --git "a/test/fixedbugs/issue27836.dir/\303\204main.go" "b/test/fixedbugs/issue27836.dir/\303\204main.go" deleted file mode 100644 index 25d2c71fc00a96..00000000000000 --- "a/test/fixedbugs/issue27836.dir/\303\204main.go" +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "fmt" - - "./Äfoo" - Äblix "./Äfoo" -) - -func main() { - fmt.Printf("Äfoo.Äbar(33) returns %v\n", Äfoo.Äbar(33)) - fmt.Printf("Äblix.Äbar(33) returns %v\n", Äblix.Äbar(33)) -} diff --git "a/test/fixedbugs/issue27836.dir/\303\236foo.go" "b/test/fixedbugs/issue27836.dir/\303\236foo.go" new file mode 100644 index 00000000000000..ea6be0f49fdcc5 --- /dev/null +++ "b/test/fixedbugs/issue27836.dir/\303\236foo.go" @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package Þfoo + +var ÞbarV int = 101 + +func Þbar(x int) int { + defer func() { ÞbarV += 3 }() + return Þblix(x) +} + +func Þblix(x int) int { + defer func() { ÞbarV += 9 }() + return ÞbarV + x +} diff --git "a/test/fixedbugs/issue27836.dir/\303\236main.go" "b/test/fixedbugs/issue27836.dir/\303\236main.go" new file mode 100644 index 00000000000000..596c620d80a321 --- /dev/null +++ "b/test/fixedbugs/issue27836.dir/\303\236main.go" @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "./Þfoo" + Þblix "./Þfoo" +) + +func main() { + fmt.Printf("Þfoo.Þbar(33) returns %v\n", Þfoo.Þbar(33)) + fmt.Printf("Þblix.Þbar(33) returns %v\n", Þblix.Þbar(33)) +} diff --git a/test/fixedbugs/issue27938.go b/test/fixedbugs/issue27938.go index 2589e1eff8d2f5..5392c65f1fcf84 100644 --- a/test/fixedbugs/issue27938.go +++ b/test/fixedbugs/issue27938.go @@ -11,13 +11,13 @@ package p type _ struct { - F sync.Mutex // ERROR "undefined: sync|expected package" + F sync.Mutex // ERROR "undefined: sync|expected package|reference to undefined name" } type _ struct { - sync.Mutex // ERROR "undefined: sync|expected package" + sync.Mutex // ERROR "undefined: sync|expected package|reference to undefined name" } type _ interface { - sync.Mutex // ERROR "undefined: sync|expected package|expected signature or type name" + sync.Mutex // ERROR "undefined: sync|expected package|expected signature or type name|reference to undefined name" } diff --git a/test/fixedbugs/issue28079b.go b/test/fixedbugs/issue28079b.go index 54c9db994b8fa2..69d1a2f480897d 100644 --- a/test/fixedbugs/issue28079b.go +++ b/test/fixedbugs/issue28079b.go @@ -10,8 +10,8 @@ package p import "unsafe" -type T [uintptr(unsafe.Pointer(nil))]int // ERROR "non-constant array bound|array bound is not constant" +type T [uintptr(unsafe.Pointer(nil))]int // ERROR "non-constant array bound|array bound is not constant|must be constant" func f() { - _ = complex(1<> 1) func main() { s := make([]T, maxInt) - shouldPanic("cap out of range", func() { s = append(s, T{}) }) + shouldPanic("len out of range", func() { s = append(s, T{}) }) var oneElem = make([]T, 1) - shouldPanic("cap out of range", func() { s = append(s, oneElem...) }) + shouldPanic("len out of range", func() { s = append(s, oneElem...) }) } func shouldPanic(str string, f func()) { diff --git a/test/fixedbugs/issue29312.go b/test/fixedbugs/issue29312.go index 4293e0100421b4..cbf79f704c11b0 100644 --- a/test/fixedbugs/issue29312.go +++ b/test/fixedbugs/issue29312.go @@ -22,14 +22,14 @@ // The type names for these types are as follows. Because we truncate // the name at depth 250, the last few names are all identical: // -// type.[]*"".pwn -// type.[][]*"".pwn +// type:[]*"".pwn +// type:[][]*"".pwn // ... -// type.[][]...[][]*pwn - 249 total "[]" -// type.[][]...[][][]*<...> - 250 total "[]" -// type.[][]...[][][][]<...> - 251 total "[]" -// type.[][]...[][][][]<...> - 252 total "[]" (but only 251 "[]" in the name) -// type.[][]...[][][][]<...> - 253 total "[]" (but only 251 "[]" in the name) +// type:[][]...[][]*pwn - 249 total "[]" +// type:[][]...[][][]*<...> - 250 total "[]" +// type:[][]...[][][][]<...> - 251 total "[]" +// type:[][]...[][][][]<...> - 252 total "[]" (but only 251 "[]" in the name) +// type:[][]...[][][][]<...> - 253 total "[]" (but only 251 "[]" in the name) // // Because the names of the last 3 types are all identical, the // compiler will generate only a single runtime.slicetype data @@ -37,7 +37,7 @@ // generates just the 251-entry one. There aren't any // runtime.slicetypes generated for the final two types. // -// The compiler passes type.[]...[]<...> (251 total "[]") to +// The compiler passes type:[]...[]<...> (251 total "[]") to // fmt.Sprintf (instead of the correct 253 one). But the data // structure at runtime actually has 253 nesting levels. So we end up // calling String on something that is of type [][]*pwn instead of diff --git a/test/fixedbugs/issue29919.dir/a.go b/test/fixedbugs/issue29919.dir/a.go index 078f973b4b5c70..3b1dac40aab0ce 100644 --- a/test/fixedbugs/issue29919.dir/a.go +++ b/test/fixedbugs/issue29919.dir/a.go @@ -51,14 +51,14 @@ func f() int { } iter := runtime.CallersFrames(pcs[:n]) f, more := iter.Next() - if f.Function != "a.f" || !strings.HasSuffix(f.File, "a.go") || f.Line != 22 { + if f.Function != "test/a.f" || !strings.HasSuffix(f.File, "a.go") || f.Line != 22 { panic(fmt.Sprintf("bad f %v\n", f)) } if !more { panic("traceback truncated after f") } f, more = iter.Next() - if f.Function != "a.init" || !strings.HasSuffix(f.File, "a.go") || f.Line != 15 { + if f.Function != "test/a.init" || !strings.HasSuffix(f.File, "a.go") || f.Line != 15 { panic(fmt.Sprintf("bad init %v\n", f)) } if !more { diff --git a/test/fixedbugs/issue30862.dir/a.go b/test/fixedbugs/issue30862.dir/a/a.go similarity index 100% rename from test/fixedbugs/issue30862.dir/a.go rename to test/fixedbugs/issue30862.dir/a/a.go diff --git a/test/fixedbugs/issue30862.dir/b.go b/test/fixedbugs/issue30862.dir/b.go deleted file mode 100644 index 3e501bb8dcbdc7..00000000000000 --- a/test/fixedbugs/issue30862.dir/b.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 The Go Authors. 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 EmbedImported struct { - a.NoitfStruct -} - -func Test() []string { - bad := []string{} - x := interface{}(new(a.NoitfStruct)) - if _, ok := x.(interface { - NoInterfaceMethod() - }); ok { - bad = append(bad, "fail 1") - } - - x = interface{}(new(EmbedImported)) - if _, ok := x.(interface { - NoInterfaceMethod() - }); ok { - bad = append(bad, "fail 2") - } - return bad -} diff --git a/test/fixedbugs/issue30862.dir/b/b.go b/test/fixedbugs/issue30862.dir/b/b.go new file mode 100644 index 00000000000000..230221d5036248 --- /dev/null +++ b/test/fixedbugs/issue30862.dir/b/b.go @@ -0,0 +1,29 @@ +// Copyright 2019 The Go Authors. 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 "issue30862.dir/a" + +type EmbedImported struct { + a.NoitfStruct +} + +func Test() []string { + bad := []string{} + x := interface{}(new(a.NoitfStruct)) + if _, ok := x.(interface { + NoInterfaceMethod() + }); ok { + bad = append(bad, "fail 1") + } + + x = interface{}(new(EmbedImported)) + if _, ok := x.(interface { + NoInterfaceMethod() + }); ok { + bad = append(bad, "fail 2") + } + return bad +} diff --git a/test/fixedbugs/issue30862.dir/main.go b/test/fixedbugs/issue30862.dir/main.go index 80db0e13a84753..1489c5a34255fb 100644 --- a/test/fixedbugs/issue30862.dir/main.go +++ b/test/fixedbugs/issue30862.dir/main.go @@ -8,7 +8,7 @@ import ( "fmt" "os" - "./b" + "issue30862.dir/b" ) // Test case for issue 30862. diff --git a/test/fixedbugs/issue30862.go b/test/fixedbugs/issue30862.go index ba122cc3c8b805..acac71e2ccabd4 100644 --- a/test/fixedbugs/issue30862.go +++ b/test/fixedbugs/issue30862.go @@ -1,4 +1,4 @@ -// rundir +// runindir -goexperiment fieldtrack // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -9,6 +9,4 @@ // is set when building it, whereas gccgo has field tracking // enabled by default (hence the build tag below). -// +build gccgo - package ignored diff --git a/test/fixedbugs/issue30898.go b/test/fixedbugs/issue30898.go index b6376d3f9e7a7f..c7f6f2d3712b3c 100644 --- a/test/fixedbugs/issue30898.go +++ b/test/fixedbugs/issue30898.go @@ -15,5 +15,5 @@ func debugf(format string, args ...interface{}) { // ERROR "can inline debugf" " func bar() { // ERROR "can inline bar" value := 10 - debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\[\]interface {}{...} does not escape" + debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\.\.\. argument does not escape" } diff --git a/test/fixedbugs/issue30908.go b/test/fixedbugs/issue30908.go index 60fbd11457fb59..27f070ececc24f 100644 --- a/test/fixedbugs/issue30908.go +++ b/test/fixedbugs/issue30908.go @@ -1,9 +1,10 @@ -// rundir -P -ldflags -strictdups=2 -w=0 +// rundir -ldflags -strictdups=2 -w=0 // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !nacl && !js // +build !nacl,!js package ignored diff --git a/test/fixedbugs/issue31252.dir/c.go b/test/fixedbugs/issue31252.dir/c.go index 928c8eee1cde60..98ecf6117720c6 100644 --- a/test/fixedbugs/issue31252.dir/c.go +++ b/test/fixedbugs/issue31252.dir/c.go @@ -5,8 +5,8 @@ package c import ( - "a" - "b" + "./a" + "./b" ) type HandlerFunc func(*string) diff --git a/test/fixedbugs/issue31252.dir/main.go b/test/fixedbugs/issue31252.dir/main.go index 25a7548668897e..6c0c9ce5d2c380 100644 --- a/test/fixedbugs/issue31252.dir/main.go +++ b/test/fixedbugs/issue31252.dir/main.go @@ -4,7 +4,7 @@ package main -import "c" +import "./c" func main() { c.RouterInit() diff --git a/test/fixedbugs/issue31573.go b/test/fixedbugs/issue31573.go index 005910e00d16b8..eaab5634316916 100644 --- a/test/fixedbugs/issue31573.go +++ b/test/fixedbugs/issue31573.go @@ -19,31 +19,31 @@ func g() { defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) does not escape$" go f() - go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" - go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" + go f(new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" + go f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} does not escape$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" for { defer f() - defer f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" - defer f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" + defer f(new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" + defer f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" defer f(nil...) - defer f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" - defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" - defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + defer f([]*int{}...) // ERROR "\[\]\*int{} does not escape$" + defer f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" + defer f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" go f() - go f(new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" - go f(new(int), new(int)) // ERROR "... argument escapes to heap$" "new\(int\) escapes to heap$" + go f(new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" + go f(new(int), new(int)) // ERROR "... argument does not escape$" "new\(int\) escapes to heap$" go f(nil...) - go f([]*int{}...) // ERROR "\[\]\*int{} escapes to heap$" - go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" - go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} escapes to heap$" "new\(int\) escapes to heap$" + go f([]*int{}...) // ERROR "\[\]\*int{} does not escape$" + go f([]*int{new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" + go f([]*int{new(int), new(int)}...) // ERROR "\[\]\*int{...} does not escape$" "new\(int\) escapes to heap$" } } diff --git a/test/fixedbugs/issue31636.dir/main.go b/test/fixedbugs/issue31636.dir/main.go index d8ae902c64c0a9..bbc58369d3fcd6 100644 --- a/test/fixedbugs/issue31636.dir/main.go +++ b/test/fixedbugs/issue31636.dir/main.go @@ -9,11 +9,11 @@ package main // 1.13. For 1.14, we will move to a variant of lexicographic ordering // which will require a change to the test output of this test. import ( - _ "c" + _ "./c" - _ "b" + _ "./b" - _ "a" + _ "./a" ) func main() { diff --git a/test/fixedbugs/issue31959.dir/main.go b/test/fixedbugs/issue31959.dir/main.go index 895c4e5345773e..6604e3abbdaa4c 100644 --- a/test/fixedbugs/issue31959.dir/main.go +++ b/test/fixedbugs/issue31959.dir/main.go @@ -12,7 +12,7 @@ package main import ( "fmt" - "a" + "./a" ) func main() { diff --git a/test/fixedbugs/issue32187.go b/test/fixedbugs/issue32187.go index 9c8c9c26d7980c..268da8112ff8f2 100644 --- a/test/fixedbugs/issue32187.go +++ b/test/fixedbugs/issue32187.go @@ -36,7 +36,11 @@ func main() { {"type assertion", "", func() { _ = x == x.(*int) }}, {"out of bounds", "", func() { _ = x == s[1] }}, {"nil pointer dereference #1", "", func() { _ = x == *p }}, - {"nil pointer dereference #2", "nil pointer dereference", func() { _ = *l == r[0] }}, + // TODO(mdempsky): Restore "nil pointer dereference" check. The Go + // spec doesn't mandate an order for panics (or even panic + // messages), but left-to-right is less confusing to users. + {"nil pointer dereference #2", "", func() { _ = *l == r[0] }}, + {"nil pointer dereference #3", "", func() { _ = *l == any(r[0]) }}, } for _, tc := range tests { @@ -44,16 +48,14 @@ func main() { } } -func testFuncShouldPanic(name, errStr string, f func()) { +func testFuncShouldPanic(name, want string, f func()) { defer func() { e := recover() if e == nil { log.Fatalf("%s: comparison did not panic\n", name) } - if errStr != "" { - if !strings.Contains(e.(error).Error(), errStr) { - log.Fatalf("%s: wrong panic message\n", name) - } + if have := e.(error).Error(); !strings.Contains(have, want) { + log.Fatalf("%s: wrong panic message: have %q, want %q\n", name, have, want) } }() f() diff --git a/test/fixedbugs/issue32595.dir/main.go b/test/fixedbugs/issue32595.dir/main.go index 20472cd72a4a53..979efe3a910fec 100644 --- a/test/fixedbugs/issue32595.dir/main.go +++ b/test/fixedbugs/issue32595.dir/main.go @@ -5,8 +5,8 @@ package main import ( - "a" - "b" + "./a" + "./b" ) func main() { diff --git a/test/fixedbugs/issue32901.dir/main.go b/test/fixedbugs/issue32901.dir/main.go index 28bb8cde283437..673c6ab3e0f894 100644 --- a/test/fixedbugs/issue32901.dir/main.go +++ b/test/fixedbugs/issue32901.dir/main.go @@ -4,13 +4,16 @@ package main -import "./c" -import "reflect" +import ( + "reflect" + + "./c" +) func main() { x := c.F() p := c.P() - t := reflect.PtrTo(reflect.TypeOf(x)) + t := reflect.PointerTo(reflect.TypeOf(x)) tp := reflect.TypeOf(p) if t != tp { panic("FAIL") diff --git a/test/fixedbugs/issue33013.dir/b.go b/test/fixedbugs/issue33013.dir/b.go index 5694b58282c545..a8f5cc0650551d 100644 --- a/test/fixedbugs/issue33013.dir/b.go +++ b/test/fixedbugs/issue33013.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" type Service uint64 type ServiceDesc struct { diff --git a/test/fixedbugs/issue33013.dir/c.go b/test/fixedbugs/issue33013.dir/c.go index bfdc0b535fe543..74425dfaae10fd 100644 --- a/test/fixedbugs/issue33013.dir/c.go +++ b/test/fixedbugs/issue33013.dir/c.go @@ -5,8 +5,8 @@ package c import ( - "a" - "b" + "./a" + "./b" ) type BI interface { diff --git a/test/fixedbugs/issue33013.dir/d.go b/test/fixedbugs/issue33013.dir/d.go index f4fff4ac62a299..c70c6647e8ccb4 100644 --- a/test/fixedbugs/issue33013.dir/d.go +++ b/test/fixedbugs/issue33013.dir/d.go @@ -5,8 +5,8 @@ package d import ( - "b" - "c" + "./b" + "./c" ) var GA b.Service diff --git a/test/fixedbugs/issue33020.dir/b.go b/test/fixedbugs/issue33020.dir/b.go index 354ab3ebfe66e9..14a2a87041e0d8 100644 --- a/test/fixedbugs/issue33020.dir/b.go +++ b/test/fixedbugs/issue33020.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" var N n diff --git a/test/fixedbugs/issue33158.dir/b.go b/test/fixedbugs/issue33158.dir/b.go index a16f0da600e3a4..4174b417ec174f 100644 --- a/test/fixedbugs/issue33158.dir/b.go +++ b/test/fixedbugs/issue33158.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" func B() string { return a.M() diff --git a/test/fixedbugs/issue33219.dir/c.go b/test/fixedbugs/issue33219.dir/c.go index ece48d76031da3..a78fe69a9e948d 100644 --- a/test/fixedbugs/issue33219.dir/c.go +++ b/test/fixedbugs/issue33219.dir/c.go @@ -5,8 +5,8 @@ package c import ( - "a" - "b" + "./a" + "./b" ) type BI interface { diff --git a/test/fixedbugs/issue33386.go b/test/fixedbugs/issue33386.go index 7b2f565285e6f8..a7074069febca1 100644 --- a/test/fixedbugs/issue33386.go +++ b/test/fixedbugs/issue33386.go @@ -13,17 +13,17 @@ package p func _() { go func() { // no error here about goroutine send <- // GCCGO_ERROR "undefined name" - }() // ERROR "expecting expression|expected operand" + }() // ERROR "expected expression|expected operand" } func _() { defer func() { // no error here about deferred function 1 + // GCCGO_ERROR "value computed is not used" - }() // ERROR "expecting expression|expected operand" + }() // ERROR "expected expression|expected operand" } func _() { - _ = (1 +) // ERROR "expecting expression|expected operand" - _ = a[2 +] // ERROR "expecting expression|expected operand|undefined name" - _ = []int{1, 2, 3 + } // ERROR "expecting expression|expected operand" + _ = (1 +) // ERROR "expected expression|expected operand" + _ = a[2 +] // ERROR "expected expression|expected operand|undefined name" + _ = []int{1, 2, 3 + } // ERROR "expected expression|expected operand" } diff --git a/test/fixedbugs/issue33460.go b/test/fixedbugs/issue33460.go index d90b0a43489d4b..dff468a12cc7f9 100644 --- a/test/fixedbugs/issue33460.go +++ b/test/fixedbugs/issue33460.go @@ -18,11 +18,11 @@ const iii int = 0x3 func f(v int) { switch v { case zero, one: - case two, one: // ERROR "previous case at LINE-1|duplicate case in switch" + case two, one: // ERROR "previous case at LINE-1|duplicate case .*in.* switch" case three: - case 3: // ERROR "previous case at LINE-1|duplicate case in switch" - case iii: // ERROR "previous case at LINE-2|duplicate case in switch" + case 3: // ERROR "previous case at LINE-1|duplicate case .*in.* switch" + case iii: // ERROR "previous case at LINE-2|duplicate case .*in.* switch" } } @@ -31,7 +31,7 @@ const b = "b" var _ = map[string]int{ "a": 0, b: 1, - "a": 2, // ERROR "previous key at LINE-2|duplicate key in map literal" - "b": 3, // GC_ERROR "previous key at LINE-2" - "b": 4, // GC_ERROR "previous key at LINE-3" + "a": 2, // ERROR "previous key at LINE-2|duplicate key.*in map literal" + "b": 3, // GC_ERROR "previous key at LINE-2|duplicate key.*in map literal" + "b": 4, // GC_ERROR "previous key at LINE-3|duplicate key.*in map literal" } diff --git a/test/fixedbugs/issue33739.dir/b.go b/test/fixedbugs/issue33739.dir/b.go index caca1ec686e252..d22fe34d668d97 100644 --- a/test/fixedbugs/issue33739.dir/b.go +++ b/test/fixedbugs/issue33739.dir/b.go @@ -4,7 +4,7 @@ package main -import "a" +import "./a" func main() { a.F()() diff --git a/test/fixedbugs/issue34503.dir/b.go b/test/fixedbugs/issue34503.dir/b.go index 21bdfcc1b50cfd..7ea02511b06e78 100644 --- a/test/fixedbugs/issue34503.dir/b.go +++ b/test/fixedbugs/issue34503.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" func Bfunc() { a.Hook(101) diff --git a/test/fixedbugs/issue34577.dir/b.go b/test/fixedbugs/issue34577.dir/b.go index bbcd1af5179cd0..c61d7da7820970 100644 --- a/test/fixedbugs/issue34577.dir/b.go +++ b/test/fixedbugs/issue34577.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" type B struct { s string diff --git a/test/fixedbugs/issue35586.dir/b.go b/test/fixedbugs/issue35586.dir/b.go index e8b674fe30a961..e0abb99fd41cb9 100644 --- a/test/fixedbugs/issue35586.dir/b.go +++ b/test/fixedbugs/issue35586.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" func F(addr string) (uint64, string) { return a.D(addr, 32) diff --git a/test/fixedbugs/issue36085.dir/b.go b/test/fixedbugs/issue36085.dir/b.go index c5ee26970a7caa..0ebfe9fb6107d2 100644 --- a/test/fixedbugs/issue36085.dir/b.go +++ b/test/fixedbugs/issue36085.dir/b.go @@ -1,6 +1,6 @@ package main -import "a" +import "./a" var w a.W var X interface{} = &w diff --git a/test/fixedbugs/issue40954.go b/test/fixedbugs/issue40954.go index 53e9ccf387e19c..0beaabb7439755 100644 --- a/test/fixedbugs/issue40954.go +++ b/test/fixedbugs/issue40954.go @@ -4,24 +4,29 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo + package main import ( + "runtime/cgo" "unsafe" ) -//go:notinheap -type S struct{ x int } +type S struct { + _ cgo.Incomplete + x int +} func main() { var i int p := (*S)(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) v := uintptr(unsafe.Pointer(p)) - // p is a pointer to a go:notinheap type. Like some C libraries, + // p is a pointer to a not-in-heap type. Like some C libraries, // we stored an integer in that pointer. That integer just happens // to be the address of i. // v is also the address of i. - // p has a base type which is marked go:notinheap, so it + // p has a base type which is marked not-in-heap, so it // should not be adjusted when the stack is copied. recurse(100, p, v) } diff --git a/test/fixedbugs/issue41247.go b/test/fixedbugs/issue41247.go index c5e495ba93396c..05889a9ce88473 100644 --- a/test/fixedbugs/issue41247.go +++ b/test/fixedbugs/issue41247.go @@ -7,5 +7,5 @@ package p func f() [2]int { - return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int{...} \(type \[3\]int\)|incompatible type" + return [...]int{2: 0} // ERROR "cannot use \[\.\.\.\]int{.*} \(.*type \[3\]int\)" } diff --git a/test/fixedbugs/issue41500.go b/test/fixedbugs/issue41500.go index 3ec23a0dfe08ba..b0ae7cfd59f575 100644 --- a/test/fixedbugs/issue41500.go +++ b/test/fixedbugs/issue41500.go @@ -13,8 +13,8 @@ type s struct { func f() { var x *s - _ = x == nil || len(x.slice) // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)|incompatible types|cannot convert" - _ = len(x.slice) || x == nil // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)|incompatible types|cannot convert" - _ = x == nil && len(x.slice) // ERROR "invalid operation: .+ \(operator && not defined on int\)|incompatible types|cannot convert" - _ = len(x.slice) && x == nil // ERROR "invalid operation: .+ \(operator && not defined on int\)|incompatible types|cannot convert" + _ = x == nil || len(x.slice) // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)|incompatible types|mismatched types untyped bool and int" + _ = len(x.slice) || x == nil // ERROR "invalid operation: .+ \(operator \|\| not defined on int\)|incompatible types|mismatched types int and untyped bool" + _ = x == nil && len(x.slice) // ERROR "invalid operation: .+ \(operator && not defined on int\)|incompatible types|mismatched types untyped bool and int" + _ = len(x.slice) && x == nil // ERROR "invalid operation: .+ \(operator && not defined on int\)|incompatible types|mismatched types int and untyped bool" } diff --git a/test/fixedbugs/issue42032.go b/test/fixedbugs/issue42032.go index c456b1db02cc16..eb118591019e73 100644 --- a/test/fixedbugs/issue42032.go +++ b/test/fixedbugs/issue42032.go @@ -4,10 +4,14 @@ // source code is governed by a BSD-style license that can be found in // the LICENSE file. +//go:build cgo + package main -//go:notinheap +import "runtime/cgo" + type NIH struct { + _ cgo.Incomplete } type T struct { diff --git a/test/fixedbugs/issue42076.go b/test/fixedbugs/issue42076.go index 3e954813c93181..ef8db2da30d685 100644 --- a/test/fixedbugs/issue42076.go +++ b/test/fixedbugs/issue42076.go @@ -4,12 +4,17 @@ // source code is governed by a BSD-style license that can be found in // the LICENSE file. +//go:build cgo + package main -import "reflect" +import ( + "reflect" + "runtime/cgo" +) -//go:notinheap type NIH struct { + _ cgo.Incomplete } var x, y NIH diff --git a/test/fixedbugs/issue4215.go b/test/fixedbugs/issue4215.go index 7201591f3f3c85..9f32f5b100649f 100644 --- a/test/fixedbugs/issue4215.go +++ b/test/fixedbugs/issue4215.go @@ -7,28 +7,28 @@ package main func foo() (int, int) { - return 2.3 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int\)|not enough arguments to return|wrong number of return values" + return 2.3 // ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)|not enough arguments to return" } func foo2() { - return int(2), 2 // ERROR "too many arguments to return\n\thave \(int, number\)\n\twant \(\)|return with value in function with no return type|no result values expected" + return int(2), 2 // ERROR "too many (arguments to return|return values)\n\thave \(int, number\)\n\twant \(\)|return with value in function with no return type" } func foo3(v int) (a, b, c, d int) { if v >= 0 { - return 1 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int, int, int\)|not enough arguments to return|wrong number of return values" + return 1 // ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int, int, int\)|not enough arguments to return" } - return 2, 3 // ERROR "not enough arguments to return\n\thave \(number, number\)\n\twant \(int, int, int, int\)|not enough arguments to return|wrong number of return values" + return 2, 3 // ERROR "not enough return values\n\thave \(number, number\)\n\twant \(int, int, int, int\)|not enough arguments to return" } func foo4(name string) (string, int) { switch name { case "cow": - return "moo" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return|wrong number of return values" + return "moo" // ERROR "not enough return values\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return" case "dog": - return "dog", 10, true // ERROR "too many arguments to return\n\thave \(string, number, bool\)\n\twant \(string, int\)|too many values in return statement|wrong number of return values" + return "dog", 10, true // ERROR "too many return values\n\thave \(string, number, bool\)\n\twant \(string, int\)|too many arguments to return" case "fish": - return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return|wrong number of return values" + return "" // ERROR "not enough return values\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return" default: return "lizard", 10 } @@ -40,14 +40,14 @@ type U float64 func foo5() (S, T, U) { if false { - return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(S, T, U\)|not enough arguments to return|wrong number of return values" + return "" // ERROR "not enough return values\n\thave \(string\)\n\twant \(S, T, U\)|not enough arguments to return" } else { ptr := new(T) - return ptr // ERROR "not enough arguments to return\n\thave \(\*T\)\n\twant \(S, T, U\)|not enough arguments to return|wrong number of return values" + return ptr // ERROR "not enough return values\n\thave \(\*T\)\n\twant \(S, T, U\)|not enough arguments to return" } - return new(S), 12.34, 1 + 0i, 'r', true // ERROR "too many arguments to return\n\thave \(\*S, number, number, number, bool\)\n\twant \(S, T, U\)|too many values in return statement|wrong number of return values" + return new(S), 12.34, 1 + 0i, 'r', true // ERROR "too many return values\n\thave \(\*S, number, number, number, bool\)\n\twant \(S, T, U\)|too many arguments to return" } func foo6() (T, string) { - return "T", true, true // ERROR "too many arguments to return\n\thave \(string, bool, bool\)\n\twant \(T, string\)|too many values in return statement|wrong number of return values" + return "T", true, true // ERROR "too many return values\n\thave \(string, bool, bool\)\n\twant \(T, string\)|too many arguments to return" } diff --git a/test/fixedbugs/issue42284.dir/a.go b/test/fixedbugs/issue42284.dir/a.go index ffe9310be35b64..f7fd80bd2070bb 100644 --- a/test/fixedbugs/issue42284.dir/a.go +++ b/test/fixedbugs/issue42284.dir/a.go @@ -13,7 +13,7 @@ func E() I { // ERROR "can inline E" return T(0) // ERROR "T\(0\) escapes to heap" } -func F(i I) I { // ERROR "can inline F" "leaking param: i to result ~r1 level=0" +func F(i I) I { // ERROR "can inline F" "leaking param: i to result ~r0 level=0" i = nil return i } diff --git a/test/fixedbugs/issue42284.dir/b.go b/test/fixedbugs/issue42284.dir/b.go index 652aa3212265a3..8cd93b8db453f9 100644 --- a/test/fixedbugs/issue42284.dir/b.go +++ b/test/fixedbugs/issue42284.dir/b.go @@ -7,7 +7,7 @@ package b import "./a" func g() { - h := a.E() // ERROR "inlining call to a.E" "a.I\(a.T\(0\)\) does not escape" + h := a.E() // ERROR "inlining call to a.E" "T\(0\) does not escape" h.M() // ERROR "devirtualizing h.M to a.T" // BAD: T(0) could be stack allocated. diff --git a/test/fixedbugs/issue4232.go b/test/fixedbugs/issue4232.go index 30d132683a981a..f49c6152e0c2b5 100644 --- a/test/fixedbugs/issue4232.go +++ b/test/fixedbugs/issue4232.go @@ -11,46 +11,46 @@ package p func f() { var a [10]int - _ = a[-1] // ERROR "invalid array index -1|index out of bounds" - _ = a[-1:] // ERROR "invalid slice index -1|index out of bounds" - _ = a[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = a[10] // ERROR "invalid array index 10|index out of bounds" + _ = a[-1] // ERROR "invalid array index -1|index out of bounds|must not be negative" + _ = a[-1:] // ERROR "invalid slice index -1|index out of bounds|must not be negative" + _ = a[:-1] // ERROR "invalid slice index -1|index out of bounds|must not be negative" + _ = a[10] // ERROR "invalid array index 10|index .*out of bounds" _ = a[9:10] _ = a[10:10] - _ = a[9:12] // ERROR "invalid slice index 12|index out of bounds" - _ = a[11:12] // ERROR "invalid slice index 11|index out of bounds" - _ = a[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow" "invalid slice index 1 << 100|index out of bounds" + _ = a[9:12] // ERROR "invalid slice index 12|index .*out of bounds" + _ = a[11:12] // ERROR "invalid slice index 11|index .*out of bounds" + _ = a[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow|invalid slice index 1 << 100|index out of bounds" var s []int - _ = s[-1] // ERROR "invalid slice index -1|index out of bounds" - _ = s[-1:] // ERROR "invalid slice index -1|index out of bounds" - _ = s[:-1] // ERROR "invalid slice index -1|index out of bounds" + _ = s[-1] // ERROR "invalid slice index -1|index .*out of bounds|must not be negative" + _ = s[-1:] // ERROR "invalid slice index -1|index .*out of bounds|must not be negative" + _ = s[:-1] // ERROR "invalid slice index -1|index .*out of bounds|must not be negative" _ = s[10] _ = s[9:10] _ = s[10:10] _ = s[9:12] _ = s[11:12] - _ = s[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow" "invalid slice index 1 << 100|index out of bounds" + _ = s[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow|invalid slice index 1 << 100|index out of bounds" const c = "foofoofoof" - _ = c[-1] // ERROR "invalid string index -1|index out of bounds" - _ = c[-1:] // ERROR "invalid slice index -1|index out of bounds" - _ = c[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = c[10] // ERROR "invalid string index 10|index out of bounds" + _ = c[-1] // ERROR "invalid string index -1|index out of bounds|must not be negative" + _ = c[-1:] // ERROR "invalid slice index -1|index out of bounds|must not be negative" + _ = c[:-1] // ERROR "invalid slice index -1|index out of bounds|must not be negative" + _ = c[10] // ERROR "invalid string index 10|index .*out of bounds" _ = c[9:10] _ = c[10:10] - _ = c[9:12] // ERROR "invalid slice index 12|index out of bounds" - _ = c[11:12] // ERROR "invalid slice index 11|index out of bounds" - _ = c[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow" "invalid slice index 1 << 100|index out of bounds" + _ = c[9:12] // ERROR "invalid slice index 12|index .*out of bounds" + _ = c[11:12] // ERROR "invalid slice index 11|index .*out of bounds" + _ = c[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow|invalid slice index 1 << 100|index out of bounds" var t string - _ = t[-1] // ERROR "invalid string index -1|index out of bounds" - _ = t[-1:] // ERROR "invalid slice index -1|index out of bounds" - _ = t[:-1] // ERROR "invalid slice index -1|index out of bounds" + _ = t[-1] // ERROR "invalid string index -1|index out of bounds|must not be negative" + _ = t[-1:] // ERROR "invalid slice index -1|index out of bounds|must not be negative" + _ = t[:-1] // ERROR "invalid slice index -1|index out of bounds|must not be negative" _ = t[10] _ = t[9:10] _ = t[10:10] _ = t[9:12] _ = t[11:12] - _ = t[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow" "invalid slice index 1 << 100|index out of bounds" + _ = t[1<<100 : 1<<110] // ERROR "overflows int|integer constant overflow|invalid slice index 1 << 100|index out of bounds" } diff --git a/test/fixedbugs/issue42401.dir/b.go b/test/fixedbugs/issue42401.dir/b.go index a834f4efe8d96f..fc675d82301881 100644 --- a/test/fixedbugs/issue42401.dir/b.go +++ b/test/fixedbugs/issue42401.dir/b.go @@ -5,11 +5,12 @@ package main import ( - "./a" _ "unsafe" + + "./a" ) -//go:linkname s a.s +//go:linkname s test/a.s var s string func main() { diff --git a/test/fixedbugs/issue43762.go b/test/fixedbugs/issue43762.go index 9f7682ad6ac50b..bf950c8f524d58 100644 --- a/test/fixedbugs/issue43762.go +++ b/test/fixedbugs/issue43762.go @@ -6,6 +6,6 @@ package p -var _ = true == '\\' // ERROR "invalid operation: true == '\\\\'|cannot convert true" -var _ = true == '\'' // ERROR "invalid operation: true == '\\''|cannot convert true" -var _ = true == '\n' // ERROR "invalid operation: true == '\\n'|cannot convert true" +var _ = true == '\\' // ERROR "invalid operation: (cannot compare true)|(true) == '\\\\' \(mismatched types untyped bool and untyped rune\)" +var _ = true == '\'' // ERROR "invalid operation: (cannot compare true)|(true) == '\\'' \(mismatched types untyped bool and untyped rune\)" +var _ = true == '\n' // ERROR "invalid operation: (cannot compare true)|(true) == '\\n' \(mismatched types untyped bool and untyped rune\)" diff --git a/test/fixedbugs/issue43942.go b/test/fixedbugs/issue43942.go new file mode 100644 index 00000000000000..a37d664ce4d82c --- /dev/null +++ b/test/fixedbugs/issue43942.go @@ -0,0 +1,48 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "log" + +func main() { + defer func() { + expect(5, recover()) + }() + func() { + expect(nil, recover()) + defer func() { + defer func() { + defer func() { + defer func() { + expect(3, recover()) + }() + defer panic(3) + panic(2) + }() + defer func() { + expect(1, recover()) + }() + panic(1) + }() + }() + }() + func() { + for { + defer func() { + defer panic(5) + }() + break + } + panic(4) + }() +} + +func expect(want, have interface{}) { + if want != have { + log.Fatalf("want %v, have %v", want, have) + } +} diff --git a/test/fixedbugs/issue44432.go b/test/fixedbugs/issue44432.go index c5fb67e0d782b6..8628edd03e5d22 100644 --- a/test/fixedbugs/issue44432.go +++ b/test/fixedbugs/issue44432.go @@ -1,4 +1,4 @@ -// errorcheck -G=0 -d=panic +// errorcheck -d=panic // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,6 +8,6 @@ package p var m = map[string]int{ "a": 1, - 1: 1, // ERROR "cannot use 1.*as type string in map key" - 2: 2, // ERROR "cannot use 2.*as type string in map key" + 1: 1, // ERROR "cannot use 1.*as.*string.*in map" + 2: 2, // ERROR "cannot use 2.*as.*string.*in map" } diff --git a/test/fixedbugs/issue4452.go b/test/fixedbugs/issue4452.go index f91bd2c0fbe1b9..c3d9b61ecb654a 100644 --- a/test/fixedbugs/issue4452.go +++ b/test/fixedbugs/issue4452.go @@ -9,5 +9,5 @@ package main func main() { - _ = [...]int(4) // ERROR "\[\.\.\.\].*outside of array literal" + _ = [...]int(4) // ERROR "\[\.\.\.\].*outside of array literal|invalid use of \[\.\.\.\] array" } diff --git a/test/fixedbugs/issue4510.dir/f1.go b/test/fixedbugs/issue4510.dir/f1.go index 7e2cffa5dd8af7..1217106fca4db5 100644 --- a/test/fixedbugs/issue4510.dir/f1.go +++ b/test/fixedbugs/issue4510.dir/f1.go @@ -4,6 +4,6 @@ package p -import "fmt" // ERROR "fmt redeclared|imported" +import "fmt" // GCCGO_ERROR "fmt redeclared|imported" var _ = fmt.Printf diff --git a/test/fixedbugs/issue4510.dir/f2.go b/test/fixedbugs/issue4510.dir/f2.go index 895fc342ba139f..1a67153771fa64 100644 --- a/test/fixedbugs/issue4510.dir/f2.go +++ b/test/fixedbugs/issue4510.dir/f2.go @@ -4,4 +4,4 @@ package p -func fmt() {} +func fmt() {} // GC_ERROR "fmt already declared through import of package" diff --git a/test/fixedbugs/issue45258.go b/test/fixedbugs/issue45258.go index f4d6fccf17d85d..b026c0c8f539c4 100644 --- a/test/fixedbugs/issue45258.go +++ b/test/fixedbugs/issue45258.go @@ -22,7 +22,7 @@ func (r *impl) Foo() Barer { func (r *impl) Bar() {} -func _() { +func f1() { var r Fooer = &impl{} r.Foo().Bar() } diff --git a/test/fixedbugs/issue45503.dir/b.go b/test/fixedbugs/issue45503.dir/b.go index df4877a882786d..530c394eaf9d73 100644 --- a/test/fixedbugs/issue45503.dir/b.go +++ b/test/fixedbugs/issue45503.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" func F() { s := a.S{} diff --git a/test/fixedbugs/issue46234.go b/test/fixedbugs/issue46234.go index 8e7eb8bf8d2d66..ed1c05cfbf4b81 100644 --- a/test/fixedbugs/issue46234.go +++ b/test/fixedbugs/issue46234.go @@ -1,5 +1,6 @@ -// buildrun -t 30 +// buildrun -t 45 +//go:build !js // +build !js // Copyright 2021 The Go Authors. All rights reserved. diff --git a/test/fixedbugs/issue46556.go b/test/fixedbugs/issue46556.go new file mode 100644 index 00000000000000..b159f61b0c5034 --- /dev/null +++ b/test/fixedbugs/issue46556.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type A = interface{} +type B interface{} + +// Test that embedding both anonymous and defined types is supported. +type C interface { + A + B +} diff --git a/test/fixedbugs/issue46725.go b/test/fixedbugs/issue46725.go index 29799c7d7ee854..f6e443e60f062f 100644 --- a/test/fixedbugs/issue46725.go +++ b/test/fixedbugs/issue46725.go @@ -8,7 +8,7 @@ package main import "runtime" -type T [4]int +type T [4]int // N.B., [4]int avoids runtime's tiny object allocator //go:noinline func g(x []*T) ([]*T, []*T) { return x, x } diff --git a/test/fixedbugs/issue46749.go b/test/fixedbugs/issue46749.go index 63ed19795e5fc7..faf1f884a6b40c 100644 --- a/test/fixedbugs/issue46749.go +++ b/test/fixedbugs/issue46749.go @@ -14,13 +14,13 @@ var iface interface{} var ( _ = "" + b // ERROR "invalid operation.*mismatched types.*untyped string and bool" _ = "" + i // ERROR "invalid operation.*mismatched types.*untyped string and int" - _ = "" + nil // ERROR "invalid operation.*mismatched types.*untyped string and nil" + _ = "" + nil // ERROR "invalid operation.*mismatched types.*untyped string and nil|(untyped nil)" ) var ( _ = s + false // ERROR "invalid operation.*mismatched types.*string and untyped bool" _ = s + 1 // ERROR "invalid operation.*mismatched types.*string and untyped int" - _ = s + nil // ERROR "invalid operation.*mismatched types.*string and nil" + _ = s + nil // ERROR "invalid operation.*mismatched types.*string and nil|(untyped nil)" ) var ( @@ -31,7 +31,7 @@ var ( var ( _ = b + 1 // ERROR "invalid operation.*mismatched types.*bool and untyped int" _ = i + false // ERROR "invalid operation.*mismatched types.*int and untyped bool" - _ = iface + 1 // ERROR "invalid operation.*mismatched types.*interface {} and int" - _ = iface + 1.0 // ERROR "invalid operation.*mismatched types.*interface {} and float64" - _ = iface + false // ERROR "invalid operation.*mismatched types.*interface {} and bool" + _ = iface + 1 // ERROR "invalid operation.*mismatched types.*interface *{} and int" + _ = iface + 1.0 // ERROR "invalid operation.*mismatched types.*interface *{} and float64" + _ = iface + false // ERROR "invalid operation.*mismatched types.*interface *{} and bool" ) diff --git a/test/fixedbugs/issue46903.go b/test/fixedbugs/issue46903.go new file mode 100644 index 00000000000000..28cb43df3bab44 --- /dev/null +++ b/test/fixedbugs/issue46903.go @@ -0,0 +1,35 @@ +// run +//go:build goexperiment.unified && cgo + +// TODO(mdempsky): Enable test unconditionally. This test should pass +// for non-unified mode too. + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "runtime/cgo" + +type A struct { + B + _ cgo.Incomplete +} +type B struct{ x byte } +type I interface{ M() *B } + +func (p *B) M() *B { return p } + +var ( + a A + i I = &a +) + +func main() { + got, want := i.M(), &a.B + if got != want { + println(got, "!=", want) + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue46938.go b/test/fixedbugs/issue46938.go new file mode 100644 index 00000000000000..87532d47694edc --- /dev/null +++ b/test/fixedbugs/issue46938.go @@ -0,0 +1,29 @@ +// run -gcflags="-d=checkptr" + +// Copyright 2021 The Go Authors. 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 ( + "strings" + "unsafe" +) + +func main() { + defer func() { + err := recover() + if err == nil { + panic("expected panic") + } + if got := err.(error).Error(); !strings.Contains(got, "slice bounds out of range") { + panic("expected panic slice out of bound, got " + got) + } + }() + s := make([]int64, 100) + p := unsafe.Pointer(&s[0]) + n := 1000 + + _ = (*[10]int64)(p)[:n:n] +} diff --git a/test/fixedbugs/issue46957.go b/test/fixedbugs/issue46957.go index f3ed3c3def0c9b..6c1c0fe0c28981 100644 --- a/test/fixedbugs/issue46957.go +++ b/test/fixedbugs/issue46957.go @@ -9,5 +9,5 @@ package main func f(a int, b ...int) {} func main() { - f(nil...) // ERROR "not enough arguments in call to f$" + f(nil...) // ERROR "not enough arguments in call to f\n\thave \(nil\)\n\twant \(int, \[\]int\)|not enough arguments" } diff --git a/test/fixedbugs/issue47068.dir/a.go b/test/fixedbugs/issue47068.dir/a.go new file mode 100644 index 00000000000000..f7b780d459d274 --- /dev/null +++ b/test/fixedbugs/issue47068.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 A() { + var m map[int]int = map[int]int{ + 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, + 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, + 20: 0, 21: 0, 22: 0, 23: 0, 24: 0, 25: 0, 26: 0, 27: 0, 28: 0, 29: 0} + if len(m) != 30 { + panic("unepexted map length") + } +} diff --git a/test/fixedbugs/issue47068.dir/b.go b/test/fixedbugs/issue47068.dir/b.go new file mode 100644 index 00000000000000..d341a4a395005c --- /dev/null +++ b/test/fixedbugs/issue47068.dir/b.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 "reflect" + +func B() { + t1 := reflect.TypeOf([30]int{}) + t2 := reflect.TypeOf(new([30]int)).Elem() + if t1 != t2 { + panic("[30]int types do not match") + } +} diff --git a/test/fixedbugs/issue47068.dir/main.go b/test/fixedbugs/issue47068.dir/main.go new file mode 100644 index 00000000000000..411e7db10382dc --- /dev/null +++ b/test/fixedbugs/issue47068.dir/main.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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" + "./b" +) + +func main() { + a.A() + b.B() +} diff --git a/test/fixedbugs/issue47068.go b/test/fixedbugs/issue47068.go new file mode 100644 index 00000000000000..af6f1341729458 --- /dev/null +++ b/test/fixedbugs/issue47068.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/fixedbugs/issue47087.dir/main.go b/test/fixedbugs/issue47087.dir/main.go index ccd0891a61a8ab..16c1cc616d09bb 100644 --- a/test/fixedbugs/issue47087.dir/main.go +++ b/test/fixedbugs/issue47087.dir/main.go @@ -5,8 +5,8 @@ package main import ( - "a" - "b" + "./a" + "./b" ) func main() { diff --git a/test/fixedbugs/issue47131.dir/a.go b/test/fixedbugs/issue47131.dir/a.go new file mode 100644 index 00000000000000..6e798d1d0c8e2d --- /dev/null +++ b/test/fixedbugs/issue47131.dir/a.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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 MyInt int + +type MyIntAlias = MyInt + +func (mia *MyIntAlias) Get() int { + return int(*mia) +} diff --git a/test/fixedbugs/issue47131.dir/b.go b/test/fixedbugs/issue47131.dir/b.go new file mode 100644 index 00000000000000..c658127ca9d53c --- /dev/null +++ b/test/fixedbugs/issue47131.dir/b.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 F2() int { + var mia a.MyIntAlias + return mia.Get() +} diff --git a/test/fixedbugs/issue47131.go b/test/fixedbugs/issue47131.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/fixedbugs/issue47131.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/fixedbugs/issue47201.dir/b.go b/test/fixedbugs/issue47201.dir/b.go index 5fd0635af2e11d..ae3ff3f2b8143d 100644 --- a/test/fixedbugs/issue47201.dir/b.go +++ b/test/fixedbugs/issue47201.dir/b.go @@ -4,6 +4,6 @@ package main -func Println() {} // ERROR "Println redeclared in this block" +func Println() {} // ERROR "Println redeclared in this block|Println already declared" func main() {} diff --git a/test/fixedbugs/issue47227.go b/test/fixedbugs/issue47227.go new file mode 100644 index 00000000000000..a14efc9a689410 --- /dev/null +++ b/test/fixedbugs/issue47227.go @@ -0,0 +1,23 @@ +// run fake-arg-to-force-use-of-go-run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package main + +// void f(int *p) { *p = 0x12345678; } +import "C" + +func main() { + var x C.int + func() { + defer C.f(&x) + }() + if x != 0x12345678 { + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue47712.go b/test/fixedbugs/issue47712.go new file mode 100644 index 00000000000000..81a2681592e92c --- /dev/null +++ b/test/fixedbugs/issue47712.go @@ -0,0 +1,23 @@ +// compile + +// Copyright 2021 The Go Authors. 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() { + if false { + defer func() { + _ = recover() + }() + } +} + +func g() { + for false { + defer func() { + _ = recover() + }() + } +} diff --git a/test/fixedbugs/issue47771.go b/test/fixedbugs/issue47771.go new file mode 100644 index 00000000000000..a434bffe4b1ad1 --- /dev/null +++ b/test/fixedbugs/issue47771.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// gofrontend miscompiled some cases of append(s, make(typ, ln)...). + +package main + +var g int + +func main() { + a := []*int{&g, &g, &g, &g} + a = append(a[:0], make([]*int, len(a) - 1)...) + if len(a) != 3 || a[0] != nil || a[1] != nil || a[2] != nil { + panic(a) + } +} diff --git a/test/fixedbugs/issue47928.go b/test/fixedbugs/issue47928.go new file mode 100644 index 00000000000000..3bc291dd3f1b4e --- /dev/null +++ b/test/fixedbugs/issue47928.go @@ -0,0 +1,21 @@ +// run -goexperiment fieldtrack + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + var i interface{} = new(T) + if _, ok := i.(interface{ Bad() }); ok { + panic("FAIL") + } +} + +type T struct{ U } + +type U struct{} + +//go:nointerface +func (*U) Bad() {} diff --git a/test/fixedbugs/issue48026.go b/test/fixedbugs/issue48026.go new file mode 100644 index 00000000000000..a693d33b45f00f --- /dev/null +++ b/test/fixedbugs/issue48026.go @@ -0,0 +1,26 @@ +// compile -d=ssa/check/on + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var i int + +type t struct { + a, b, c, d, e int +} + +func f(p t, q int) int { + var a, b, c, d, e, f, g int + var h, i, j, k, l, m int + _, _, _, _, _, _, _ = a, b, c, d, e, f, g + _, _, _, _, _, _ = h, i, j, k, l, m + return 0 +} + +func g() int { + var v t + return f(v, 1< CMN(x,y) in arm and arm64 + +//go:noinline +func f(p int64, x, y int64) bool { return -x <= p && p <= y } + +//go:noinline +func g(p int32, x, y int32) bool { return -x <= p && p <= y } + +// There are some more complicated patterns involving compares and shifts, try to trigger those. + +//go:noinline +func h(p int64, x, y int64) bool { return -(x<<1) <= p && p <= y } + +//go:noinline +func k(p int32, x, y int32) bool { return -(1<= math.MinInt64; i-- { + if i > 0 { + println("done") + return + } + println(i, i > 0) + } +} +func g() { + for i := int64(math.MinInt64) + 1; i >= math.MinInt64; i-- { + if i > 0 { + println("done") + return + } + println(i, i > 0) + } +} +func h() { + for i := int64(math.MinInt64) + 2; i >= math.MinInt64; i -= 2 { + if i > 0 { + println("done") + return + } + println(i, i > 0) + } +} diff --git a/test/fixedbugs/issue53653.out b/test/fixedbugs/issue53653.out new file mode 100644 index 00000000000000..f699392cf3fe06 --- /dev/null +++ b/test/fixedbugs/issue53653.out @@ -0,0 +1,8 @@ +-9223372036854775808 false +done +-9223372036854775807 false +-9223372036854775808 false +done +-9223372036854775806 false +-9223372036854775808 false +done diff --git a/test/fixedbugs/issue53702.go b/test/fixedbugs/issue53702.go new file mode 100644 index 00000000000000..0b251c2d7be969 --- /dev/null +++ b/test/fixedbugs/issue53702.go @@ -0,0 +1,39 @@ +// run + +// Copyright 2022 The Go Authors. 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 Elem struct{} + +func (*Elem) Wait(callback func()) {} + +type Base struct { + elem [8]*Elem +} + +var g_val = 1 + +func (s *Base) Do() *int { + resp := &g_val + for _, e := range s.elem { + e.Wait(func() { + *resp = 0 + }) + } + return resp +} + +type Sub struct { + *Base +} + +func main() { + a := Sub{new(Base)} + resp := a.Do() + if resp != nil && *resp != 1 { + panic("FAIL") + } +} diff --git a/test/fixedbugs/issue53982.go b/test/fixedbugs/issue53982.go new file mode 100644 index 00000000000000..512b1af2bca1f6 --- /dev/null +++ b/test/fixedbugs/issue53982.go @@ -0,0 +1,25 @@ +// build + +// Copyright 2022 The Go Authors. 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[K, V any] struct { + E[V] +} + +type E[K any] struct{} + +func (e E[K]) M() E[K] { + return e +} + +func G[K, V any](V) { + _ = (*S[K, V]).M +} + +func main() { + G[*int](new(int)) +} diff --git a/test/fixedbugs/issue54220.go b/test/fixedbugs/issue54220.go new file mode 100644 index 00000000000000..105f6e909840f5 --- /dev/null +++ b/test/fixedbugs/issue54220.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2022 The Go Authors. 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 ( + "strconv" + "sync/atomic" + "unsafe" +) + +type t struct { + i1 atomic.Int32 + i2 atomic.Int64 +} + +var v t + +func main() { + if o := unsafe.Offsetof(v.i2); o != 8 { + panic("unexpected offset, want: 8, got: " + strconv.Itoa(int(o))) + } +} diff --git a/test/fixedbugs/issue54280.go b/test/fixedbugs/issue54280.go new file mode 100644 index 00000000000000..4f9103d4e6060d --- /dev/null +++ b/test/fixedbugs/issue54280.go @@ -0,0 +1,11 @@ +// errorcheck + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Don't crash in export of oversized integer constant. + +package p + +const C = 912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912_345_678_901_234_567_890_123_456_789_012_345_678_901_234_567_890_912 // ERROR "constant overflow" diff --git a/test/fixedbugs/issue54307.go b/test/fixedbugs/issue54307.go new file mode 100644 index 00000000000000..342a53af84506e --- /dev/null +++ b/test/fixedbugs/issue54307.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2022 The Go Authors. 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[Int int, Uint uint]() { + _ = uint(Int(-1)) + _ = uint(Uint(0) - 1) +} + +func g[String string]() { + _ = String("")[100] +} + +var _ = f[int, uint] +var _ = g[string] diff --git a/test/fixedbugs/issue54343.go b/test/fixedbugs/issue54343.go new file mode 100644 index 00000000000000..10b91a5398d572 --- /dev/null +++ b/test/fixedbugs/issue54343.go @@ -0,0 +1,45 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "runtime" + +func main() { + if wait() { + panic("GC'd early") + } + m = nil + if !wait() { + panic("never GC'd") + } +} + +var m = New[int]().M + +func New[X any]() *T[X] { + p := new(T[X]) + runtime.SetFinalizer(p, func(*T[X]) { close(done) }) + return p +} + +type T[X any] [4]int // N.B., [4]int avoids runtime's tiny object allocator + +func (*T[X]) M() {} + +var done = make(chan int) + +func wait() bool { + for i := 0; i < 10; i++ { + runtime.GC() + select { + case <-done: + return true + default: + } + } + return false +} diff --git a/test/fixedbugs/issue54348.go b/test/fixedbugs/issue54348.go new file mode 100644 index 00000000000000..15b2f758efddb3 --- /dev/null +++ b/test/fixedbugs/issue54348.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + F[T[int]]() +} + +func F[X interface{ M() }]() { + var x X + x.M() +} + +type T[X any] struct{ E } + +type E struct{} + +func (h E) M() {} diff --git a/test/fixedbugs/issue54467.go b/test/fixedbugs/issue54467.go new file mode 100644 index 00000000000000..42e221c9541264 --- /dev/null +++ b/test/fixedbugs/issue54467.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func main() { + var x [64]byte + for i := range x { + x[i] = byte(i) + } + y := x + + copy(x[4:36], x[2:34]) + *(*[32]byte)(y[4:36]) = *(*[32]byte)(y[2:34]) + + for i := range x { + if x[i] != y[i] { + fmt.Printf("x[%v] = %v; y[%v] = %v\n", i, x[i], i, y[i]) + } + } +} diff --git a/test/fixedbugs/issue54632.go b/test/fixedbugs/issue54632.go new file mode 100644 index 00000000000000..0d4e32f28f1ee9 --- /dev/null +++ b/test/fixedbugs/issue54632.go @@ -0,0 +1,31 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The inliner would erroneously scan the caller function's body for +// reassignments *before* substituting the inlined function call body, +// which could cause false positives in deciding when it's safe to +// transitively inline indirect function calls. + +package main + +func main() { + bug1() + bug2(fail) +} + +func bug1() { + fn := fail + fn = pass + fn() +} + +func bug2(fn func()) { + fn = pass + fn() +} + +func pass() {} +func fail() { panic("FAIL") } diff --git a/test/fixedbugs/issue54638.go b/test/fixedbugs/issue54638.go new file mode 100644 index 00000000000000..d0258b0c6881ce --- /dev/null +++ b/test/fixedbugs/issue54638.go @@ -0,0 +1,40 @@ +// compile + +// Copyright 2022 The Go 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 54638: composite literal assignment with +// alignment > PtrSize causes ICE. + +package p + +import "sync/atomic" + +type S struct{ l any } + +type T struct { + H any + a [14]int64 + f func() + x atomic.Int64 +} + +//go:noinline +func (T) M(any) {} + +type W [2]int64 + +//go:noinline +func (W) Done() {} + +func F(l any) [3]*int { + var w W + var x [3]*int // use some stack + t := T{H: S{l: l}} + go func() { + t.M(l) + w.Done() + }() + return x +} diff --git a/test/fixedbugs/issue54722.go b/test/fixedbugs/issue54722.go new file mode 100644 index 00000000000000..7de27082b188ac --- /dev/null +++ b/test/fixedbugs/issue54722.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type G[T any] struct { + h H[G[T]] +} + +type H[T any] struct{} + +var x G[int] diff --git a/test/fixedbugs/issue54722b.go b/test/fixedbugs/issue54722b.go new file mode 100644 index 00000000000000..a6c8f829ab6865 --- /dev/null +++ b/test/fixedbugs/issue54722b.go @@ -0,0 +1,30 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type value[V comparable] struct { + node *node[value[V]] + value V +} + +type node[V comparable] struct { + index *index[V] + children map[string]*node[V] +} + +type index[V comparable] struct { + arrays []array[V] +} + +type array[V comparable] struct { + valueMap map[int]V +} + +var x value[int] +var y value[*Column] + +type Column struct{ column int } diff --git a/test/fixedbugs/issue54911.go b/test/fixedbugs/issue54911.go new file mode 100644 index 00000000000000..dee24da0bdb30d --- /dev/null +++ b/test/fixedbugs/issue54911.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Set[T comparable] map[T]struct{} + +func (s Set[T]) Add() Set[T] { + return s +} + +func (s Set[T]) Copy() Set[T] { + return Set[T].Add(s) +} + +func main() { + _ = Set[int]{42: {}} +} diff --git a/test/fixedbugs/issue54912.dir/a.go b/test/fixedbugs/issue54912.dir/a.go new file mode 100644 index 00000000000000..b425223da989b4 --- /dev/null +++ b/test/fixedbugs/issue54912.dir/a.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go 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 inlining a function literal that captures both a type +// switch case variable and another local variable works correctly. + +package a + +func F(p *int, x any) func() { + switch x := x.(type) { + case int: + return func() { + *p += x + } + } + return nil +} diff --git a/test/fixedbugs/issue54912.dir/main.go b/test/fixedbugs/issue54912.dir/main.go new file mode 100644 index 00000000000000..67b9012b36b5b7 --- /dev/null +++ b/test/fixedbugs/issue54912.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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 "test/a" + +func main() { + a.F(new(int), 0)() +} diff --git a/test/fixedbugs/issue54912.go b/test/fixedbugs/issue54912.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/fixedbugs/issue54912.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/fixedbugs/issue5609.go b/test/fixedbugs/issue5609.go index ea770b4865417d..a39d3fb0c66e56 100644 --- a/test/fixedbugs/issue5609.go +++ b/test/fixedbugs/issue5609.go @@ -10,4 +10,4 @@ package pkg const Large uint64 = 18446744073709551615 -var foo [Large]uint64 // ERROR "array bound is too large|array bound overflows" +var foo [Large]uint64 // ERROR "array bound is too large|array bound overflows|array length.*must be integer" diff --git a/test/fixedbugs/issue5910.dir/main.go b/test/fixedbugs/issue5910.dir/main.go index c5d42ea0986e86..80ddfbbca3641a 100644 --- a/test/fixedbugs/issue5910.dir/main.go +++ b/test/fixedbugs/issue5910.dir/main.go @@ -4,7 +4,7 @@ package main -import "a" +import "./a" func main() { f := new(a.Future) diff --git a/test/fixedbugs/issue5957.dir/c.go b/test/fixedbugs/issue5957.dir/c.go index d115eacdd56420..821b37e4ca7abd 100644 --- a/test/fixedbugs/issue5957.dir/c.go +++ b/test/fixedbugs/issue5957.dir/c.go @@ -1,9 +1,9 @@ package p import ( - "./a" // ERROR "imported and not used: \x22a\x22 as surprise|imported and not used: surprise" - "./b" // ERROR "imported and not used: \x22b\x22 as surprise2|imported and not used: surprise2" - b "./b" // ERROR "imported and not used: \x22b\x22$|imported and not used: surprise2" + "./a" // ERROR "imported and not used: \x22test/a\x22 as surprise|imported and not used: surprise" + "./b" // ERROR "imported and not used: \x22test/b\x22 as surprise2|imported and not used: surprise2" + b "./b" // ERROR "imported and not used: \x22test/b\x22$|imported and not used: surprise2" foo "math" // ERROR "imported and not used: \x22math\x22 as foo|imported and not used: math" "fmt" // actually used "strings" // ERROR "imported and not used: \x22strings\x22|imported and not used: strings" diff --git a/test/fixedbugs/issue6004.go b/test/fixedbugs/issue6004.go index 2b3dcd923d6a65..99d6ab85ea5def 100644 --- a/test/fixedbugs/issue6004.go +++ b/test/fixedbugs/issue6004.go @@ -7,9 +7,8 @@ package main func main() { - _ = nil // ERROR "use of untyped nil" - _, _ = nil, 1 // ERROR "use of untyped nil" - _, _ = 1, nil // ERROR "use of untyped nil" - _ = append(nil, 1, 2, 3) // ERROR "untyped nil" + _ = nil // ERROR "use of untyped nil" + _, _ = nil, 1 // ERROR "use of untyped nil" + _, _ = 1, nil // ERROR "use of untyped nil" + _ = append(nil, 1, 2, 3) // ERROR "untyped nil|nil" } - diff --git a/test/fixedbugs/issue6402.go b/test/fixedbugs/issue6402.go index 39cb9ac3f06f94..9977027d183fef 100644 --- a/test/fixedbugs/issue6402.go +++ b/test/fixedbugs/issue6402.go @@ -9,5 +9,5 @@ package p func f() uintptr { - return nil // ERROR "cannot use nil as type uintptr in return argument|incompatible type|cannot use untyped nil" + return nil // ERROR "cannot use nil as type uintptr in return argument|incompatible type|cannot use nil" } diff --git a/test/fixedbugs/issue6572.go b/test/fixedbugs/issue6572.go index 9f4d2de0e3fd11..d69bf5aee2428f 100644 --- a/test/fixedbugs/issue6572.go +++ b/test/fixedbugs/issue6572.go @@ -17,6 +17,6 @@ func bar() (T, string, T) { // ERROR "undefined" func main() { var x, y, z int x, y = foo() - x, y, z = bar() // ERROR "cannot (use type|assign) string|incompatible type" + x, y, z = bar() // ERROR "cannot (use type|assign|use.*type) string|" _, _, _ = x, y, z } diff --git a/test/fixedbugs/issue7223.go b/test/fixedbugs/issue7223.go index c78de287ff61f3..129e20f4970227 100644 --- a/test/fixedbugs/issue7223.go +++ b/test/fixedbugs/issue7223.go @@ -7,14 +7,15 @@ package main var bits1 uint = 10 + const bits2 uint = 10 func main() { _ = make([]byte, 1< 0 { goto L1 } else { diff --git a/test/fixedbugs/issue8060.dir/b.go b/test/fixedbugs/issue8060.dir/b.go index 85fb6ec7db7398..fc7eb251d0bc1d 100644 --- a/test/fixedbugs/issue8060.dir/b.go +++ b/test/fixedbugs/issue8060.dir/b.go @@ -4,7 +4,7 @@ package b -import "a" +import "./a" var X = a.A diff --git a/test/fixedbugs/issue8606b.go b/test/fixedbugs/issue8606b.go index 448ea566f0bea0..41b9a3d00eab60 100644 --- a/test/fixedbugs/issue8606b.go +++ b/test/fixedbugs/issue8606b.go @@ -1,4 +1,5 @@ // run +// +build linux darwin // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -20,20 +21,10 @@ package main import ( "fmt" "reflect" + "syscall" "unsafe" ) -func bad1() string { - s := "foo" - (*reflect.StringHeader)(unsafe.Pointer(&s)).Data = 1 // write bad value to data ptr - return s -} -func bad2() string { - s := "foo" - (*reflect.StringHeader)(unsafe.Pointer(&s)).Data = 2 // write bad value to data ptr - return s -} - type SI struct { s string i int @@ -45,15 +36,31 @@ type SS struct { } func main() { + bad1 := "foo" + bad2 := "foo" + + p := syscall.Getpagesize() + b, err := syscall.Mmap(-1, 0, p, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE) + if err != nil { + panic(err) + } + err = syscall.Mprotect(b, syscall.PROT_NONE) + if err != nil { + panic(err) + } + // write inaccessible pointers as the data fields of bad1 and bad2. + (*reflect.StringHeader)(unsafe.Pointer(&bad1)).Data = uintptr(unsafe.Pointer(&b[0])) + (*reflect.StringHeader)(unsafe.Pointer(&bad2)).Data = uintptr(unsafe.Pointer(&b[1])) + for _, test := range []struct { a, b interface{} }{ - {SI{s: bad1(), i: 1}, SI{s: bad2(), i: 2}}, - {SS{s: bad1(), t: "a"}, SS{s: bad2(), t: "aa"}}, - {SS{s: "a", t: bad1()}, SS{s: "b", t: bad2()}}, + {SI{s: bad1, i: 1}, SI{s: bad2, i: 2}}, + {SS{s: bad1, t: "a"}, SS{s: bad2, t: "aa"}}, + {SS{s: "a", t: bad1}, SS{s: "b", t: bad2}}, // This one would panic because the length of both strings match, and we check // the body of the bad strings before the body of the good strings. - //{SS{s: bad1(), t: "a"}, SS{s: bad2(), t: "b"}}, + //{SS{s: bad1, t: "a"}, SS{s: bad2, t: "b"}}, } { if test.a == test.b { panic(fmt.Sprintf("values %#v and %#v should not be equal", test.a, test.b)) diff --git a/test/fixedbugs/issue8761.go b/test/fixedbugs/issue8761.go index 7f458f7f03378d..e5130e1ff5ca92 100644 --- a/test/fixedbugs/issue8761.go +++ b/test/fixedbugs/issue8761.go @@ -10,17 +10,17 @@ package p -func _() { +func f1() { type C chan int _ = [1][]C{[]C{make(chan int)}} } -func _() { +func f2() { type C interface{} _ = [1][]C{[]C{recover()}} } -func _() { +func f3() { type C *int _ = [1][]C{[]C{new(int)}} } diff --git a/test/fixedbugs/issue9083.go b/test/fixedbugs/issue9083.go index f5c5296a2bf7c6..ea53e7a69ae0b2 100644 --- a/test/fixedbugs/issue9083.go +++ b/test/fixedbugs/issue9083.go @@ -13,12 +13,10 @@ const zero = 0 func main() { var x int - _ = x - x = make(map[int]int) // ERROR "cannot use make\(map\[int\]int\)|incompatible" - x = make(map[int]int, 0) // ERROR "cannot use make\(map\[int\]int, 0\)|incompatible" + x = make(map[int]int) // ERROR "cannot use make\(map\[int\]int\)|incompatible" + x = make(map[int]int, 0) // ERROR "cannot use make\(map\[int\]int, 0\)|incompatible" x = make(map[int]int, zero) // ERROR "cannot use make\(map\[int\]int, zero\)|incompatible" - x = make(chan int) // ERROR "cannot use make\(chan int\)|incompatible" - x = make(chan int, 0) // ERROR "cannot use make\(chan int, 0\)|incompatible" - x = make(chan int, zero) // ERROR "cannot use make\(chan int, zero\)|incompatible" - _ = x + x = make(chan int) // ERROR "cannot use make\(chan int\)|incompatible" + x = make(chan int, 0) // ERROR "cannot use make\(chan int, 0\)|incompatible" + x = make(chan int, zero) // ERROR "cannot use make\(chan int, zero\)|incompatible" } diff --git a/test/fixedbugs/issue9355.go b/test/fixedbugs/issue9355.go index 319a2a90df9ba2..1c3999c67c6b0d 100644 --- a/test/fixedbugs/issue9355.go +++ b/test/fixedbugs/issue9355.go @@ -27,15 +27,15 @@ func main() { } f.Close() - out := run("go", "tool", "compile", "-o", f.Name(), "-S", "a.go") + out := run("go", "tool", "compile", "-p=p", "-o", f.Name(), "-S", "a.go") os.Remove(f.Name()) // 6g/8g print the offset as dec, but 5g/9g print the offset as hex. patterns := []string{ - `rel 0\+\d t=1 \"\"\.x\+8\r?\n`, // y = &x.b - `rel 0\+\d t=1 \"\"\.x\+(28|1c)\r?\n`, // z = &x.d.q - `rel 0\+\d t=1 \"\"\.b\+5\r?\n`, // c = &b[5] - `rel 0\+\d t=1 \"\"\.x\+(88|58)\r?\n`, // w = &x.f[3].r + `rel 0\+\d t=1 p\.x\+8\r?\n`, // y = &x.b + `rel 0\+\d t=1 p\.x\+(28|1c)\r?\n`, // z = &x.d.q + `rel 0\+\d t=1 p\.b\+5\r?\n`, // c = &b[5] + `rel 0\+\d t=1 p\.x\+(88|58)\r?\n`, // w = &x.f[3].r } for _, p := range patterns { if ok, err := regexp.Match(p, out); !ok || err != nil { diff --git a/test/fixedbugs/issue9521.go b/test/fixedbugs/issue9521.go index 1ad40bdfda1ff5..a029ec145e9637 100644 --- a/test/fixedbugs/issue9521.go +++ b/test/fixedbugs/issue9521.go @@ -13,6 +13,6 @@ func f() (_, _ []int) { return } func g() (x []int, y float64) { return } func main() { - _ = append(f()) // ERROR "cannot use \[\]int value as type int in append|incompatible type" - _ = append(g()) // ERROR "cannot use float64 value as type int in append|incompatible type" + _ = append(f()) // ERROR "cannot use \[\]int value as type int in append|cannot use.*type \[\]int.*to append" + _ = append(g()) // ERROR "cannot use float64 value as type int in append|cannot use.*type float64.*to append" } diff --git a/test/notinheap.go b/test/fixedbugs/notinheap.go similarity index 100% rename from test/notinheap.go rename to test/fixedbugs/notinheap.go diff --git a/test/notinheap2.go b/test/fixedbugs/notinheap2.go similarity index 94% rename from test/notinheap2.go rename to test/fixedbugs/notinheap2.go index 100ed37b7218ad..cc1024ad5a5f92 100644 --- a/test/notinheap2.go +++ b/test/fixedbugs/notinheap2.go @@ -4,12 +4,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Test walk errors for go:notinheap. +// Test walk errors for not-in-heap. + +//go:build cgo package p -//go:notinheap +import "runtime/cgo" + type nih struct { + _ cgo.Incomplete next *nih } diff --git a/test/notinheap3.go b/test/fixedbugs/notinheap3.go similarity index 95% rename from test/notinheap3.go rename to test/fixedbugs/notinheap3.go index 5ace8d6793f21b..b442ed42cd6e36 100644 --- a/test/notinheap3.go +++ b/test/fixedbugs/notinheap3.go @@ -6,8 +6,12 @@ // Test write barrier elimination for notinheap. +//go:build cgo + package p +import "runtime/cgo" + type t1 struct { x *nih s []nih @@ -20,8 +24,8 @@ type t2 struct { y [1024]byte } -//go:notinheap type nih struct { + _ cgo.Incomplete x uintptr } diff --git a/test/float_lit3.go b/test/float_lit3.go index 850d02c9c7f973..37a1289fb9dd21 100644 --- a/test/float_lit3.go +++ b/test/float_lit3.go @@ -29,19 +29,19 @@ const ( var x = []interface{}{ float32(max32 + ulp32/2 - 1), // ok float32(max32 + ulp32/2 - two128/two256), // ok - float32(max32 + ulp32/2), // ERROR "constant 3\.40282e\+38 overflows float32" + float32(max32 + ulp32/2), // ERROR "constant 3\.40282e\+38 overflows float32|cannot convert.*to type float32" float32(-max32 - ulp32/2 + 1), // ok float32(-max32 - ulp32/2 + two128/two256), // ok - float32(-max32 - ulp32/2), // ERROR "constant -3\.40282e\+38 overflows float32" + float32(-max32 - ulp32/2), // ERROR "constant -3\.40282e\+38 overflows float32|cannot convert.*to type float32" // If the compiler's internal floating point representation // is shorter than 1024 bits, it cannot distinguish max64+ulp64/2-1 and max64+ulp64/2. float64(max64 + ulp64/2 - two1024/two256), // ok float64(max64 + ulp64/2 - 1), // ok - float64(max64 + ulp64/2), // ERROR "constant 1\.79769e\+308 overflows float64" + float64(max64 + ulp64/2), // ERROR "constant 1\.79769e\+308 overflows float64|cannot convert.*to type float64" float64(-max64 - ulp64/2 + two1024/two256), // ok float64(-max64 - ulp64/2 + 1), // ok - float64(-max64 - ulp64/2), // ERROR "constant -1\.79769e\+308 overflows float64" + float64(-max64 - ulp64/2), // ERROR "constant -1\.79769e\+308 overflows float64|cannot convert.*to type float64" } diff --git a/test/heapsampling.go b/test/heapsampling.go index cc72832ab48d46..741db74f894d45 100644 --- a/test/heapsampling.go +++ b/test/heapsampling.go @@ -45,7 +45,7 @@ func main() { // the testcase allows for a 10% margin of error, but only fails if it // consistently fails across three experiments, avoiding flakes. func testInterleavedAllocations() error { - const iters = 100000 + const iters = 50000 // Sizes of the allocations performed by each experiment. frames := []string{"main.allocInterleaved1", "main.allocInterleaved2", "main.allocInterleaved3"} @@ -79,6 +79,9 @@ func allocInterleaved(n int) { a16k = new([16 * 1024]byte) a256 = new([256]byte) // Test verification depends on these lines being contiguous. + + // Slow down the allocation rate to avoid #52433. + runtime.Gosched() } } @@ -101,7 +104,7 @@ func allocInterleaved3(n int) { // the testcase allows for a 10% margin of error, but only fails if it // consistently fails across three experiments, avoiding flakes. func testSmallAllocations() error { - const iters = 100000 + const iters = 50000 // Sizes of the allocations performed by each experiment. sizes := []int64{1024, 512, 256} frames := []string{"main.allocSmall1", "main.allocSmall2", "main.allocSmall3"} @@ -130,6 +133,9 @@ func allocSmall(n int) { a1k = new([1024]byte) a512 = new([512]byte) a256 = new([256]byte) + + // Slow down the allocation rate to avoid #52433. + runtime.Gosched() } } diff --git a/test/import1.go b/test/import1.go index 2433b5f2ad2b47..294ef3a46b2363 100644 --- a/test/import1.go +++ b/test/import1.go @@ -9,11 +9,11 @@ package main -import "bufio" // GCCGO_ERROR "previous|not used" +import "bufio" // ERROR "previous|not used" import bufio "os" // ERROR "redeclared|redefinition|incompatible" "imported and not used" import ( - "fmt" // GCCGO_ERROR "previous|not used" + "fmt" // ERROR "previous|not used" fmt "math" // ERROR "redeclared|redefinition|incompatible" "imported and not used: \x22math\x22 as fmt" . "math" // GC_ERROR "imported and not used: \x22math\x22$" ) diff --git a/test/import6.go b/test/import6.go index c19280f0e0bc3b..17c7dd4817388a 100644 --- a/test/import6.go +++ b/test/import6.go @@ -34,5 +34,6 @@ import "\xFFFD" // ERROR "import path" import `\xFFFD` // ERROR "import path" // Invalid local imports. -import "/foo" // ERROR "import path cannot be absolute path" -import "c:/foo" // ERROR "import path contains invalid character" +// types2 adds extra "not used" error. +import "/foo" // ERROR "import path cannot be absolute path|not used" +import "c:/foo" // ERROR "import path contains invalid character|invalid character" diff --git a/test/initializerr.go b/test/initializerr.go index 5e2e9a91a0b2fd..aae740da38ffe6 100644 --- a/test/initializerr.go +++ b/test/initializerr.go @@ -18,24 +18,24 @@ type T struct { } var x = 1 -var a1 = S { 0, X: 1 } // ERROR "mixture|undefined" -var a2 = S { Y: 3, Z: 2, Y: 3 } // ERROR "duplicate" -var a3 = T { S{}, 2, 3, 4, 5, 6 } // ERROR "convert|too many" -var a4 = [5]byte{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } // ERROR "index|too many" -var a5 = []byte { x: 2 } // ERROR "index" -var a6 = []byte{1: 1, 2: 2, 1: 3} // ERROR "duplicate" +var a1 = S{0, X: 1} // ERROR "mixture|undefined" "too few values" +var a2 = S{Y: 3, Z: 2, Y: 3} // ERROR "duplicate" +var a3 = T{S{}, 2, 3, 4, 5, 6} // ERROR "convert|too many" +var a4 = [5]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // ERROR "index|too many" +var a5 = []byte{x: 2} // ERROR "index" +var a6 = []byte{1: 1, 2: 2, 1: 3} // ERROR "duplicate" -var ok1 = S { } // should be ok -var ok2 = T { S: ok1 } // should be ok +var ok1 = S{} // should be ok +var ok2 = T{S: ok1} // should be ok // These keys can be computed at compile time but they are // not constants as defined by the spec, so they do not trigger // compile-time errors about duplicate key values. // See issue 4555. -type Key struct {X, Y int} +type Key struct{ X, Y int } var _ = map[Key]string{ - Key{1,2}: "hello", - Key{1,2}: "world", + Key{1, 2}: "hello", + Key{1, 2}: "world", } diff --git a/test/inline.go b/test/inline.go index 472a941dca3a66..04ba16858fa1bc 100644 --- a/test/inline.go +++ b/test/inline.go @@ -1,4 +1,4 @@ -// errorcheck -0 -m -d=inlfuncswithclosures=1 +// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1 // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -49,7 +49,7 @@ func j(x int) int { // ERROR "can inline j" } } -func _() int { // ERROR "can inline _" +func f2() int { // ERROR "can inline f2" tmp1 := h tmp2 := tmp1 return tmp2(0) // ERROR "inlining call to h" @@ -92,9 +92,9 @@ func o() int { foo := func() int { return 1 } // ERROR "can inline o.func1" "func literal does not escape" func(x int) { // ERROR "can inline o.func2" if x > 10 { - foo = func() int { return 2 } // ERROR "func literal does not escape" "can inline o.func2" + foo = func() int { return 2 } // ERROR "can inline o.func2" } - }(11) // ERROR "inlining call to o.func2" + }(11) // ERROR "func literal does not escape" "inlining call to o.func2" return foo() } @@ -107,18 +107,6 @@ func q(x int) int { // ERROR "can inline q" return foo() // ERROR "inlining call to q.func1" } -func r(z int) int { - foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape" - return x + z - } - bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2" - return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3" - return 2*y + x*z - }(x) // ERROR "inlining call to r.func2.1" - } - return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3" -} - func s0(x int) int { // ERROR "can inline s0" foo := func() { // ERROR "can inline s0.func1" "func literal does not escape" x = x + 1 @@ -135,8 +123,7 @@ func s1(x int) int { // ERROR "can inline s1" return foo() // ERROR "inlining call to s1.func1" } -// can't currently inline functions with a break statement -func switchBreak(x, y int) int { +func switchBreak(x, y int) int { // ERROR "can inline switchBreak" var n int switch x { case 0: @@ -161,14 +148,73 @@ func switchType(x interface{}) int { // ERROR "can inline switchType" "x does no } } +// Test that switches on constant things, with constant cases, only cost anything for +// the case that matches. See issue 50253. +func switchConst1(p func(string)) { // ERROR "can inline switchConst" "p does not escape" + const c = 1 + switch c { + case 0: + p("zero") + case 1: + p("one") + case 2: + p("two") + default: + p("other") + } +} + +func switchConst2() string { // ERROR "can inline switchConst2" + switch runtime.GOOS { + case "linux": + return "Leenooks" + case "windows": + return "Windoze" + case "darwin": + return "MackBone" + case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100": + return "Numbers" + default: + return "oh nose!" + } +} +func switchConst3() string { // ERROR "can inline switchConst3" + switch runtime.GOOS { + case "Linux": + panic("Linux") + case "Windows": + panic("Windows") + case "Darwin": + panic("Darwin") + case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100": + panic("Numbers") + default: + return "oh nose!" + } +} + +func inlineRangeIntoMe(data []int) { // ERROR "can inline inlineRangeIntoMe" "data does not escape" + rangeFunc(data, 12) // ERROR "inlining call to rangeFunc" +} + +func rangeFunc(xs []int, b int) int { // ERROR "can inline rangeFunc" "xs does not escape" + for i, x := range xs { + if x == b { + return i + } + } + return -1 +} + type T struct{} func (T) meth(int, int) {} // ERROR "can inline T.meth" func k() (T, int, int) { return T{}, 0, 0 } // ERROR "can inline k" -func _() { // ERROR "can inline _" +func f3() { // ERROR "can inline f3" T.meth(k()) // ERROR "inlining call to k" "inlining call to T.meth" + // ERRORAUTO "inlining call to T.meth" } func small1() { // ERROR "can inline small1" @@ -217,8 +263,7 @@ func for1(fn func() bool) { // ERROR "can inline for1" "fn does not escape" } } -// BAD: for2 should be inlineable too. -func for2(fn func() bool) { // ERROR "fn does not escape" +func for2(fn func() bool) { // ERROR "can inline for2" "fn does not escape" Loop: for { if fn() { @@ -232,12 +277,13 @@ Loop: // Issue #18493 - make sure we can do inlining of functions with a method value type T1 struct{} -func (a T1) meth(val int) int { // ERROR "can inline T1.meth" "inlining call to T1.meth" +func (a T1) meth(val int) int { // ERROR "can inline T1.meth" return val + 5 } func getMeth(t1 T1) func(int) int { // ERROR "can inline getMeth" return t1.meth // ERROR "t1.meth escapes to heap" + // ERRORAUTO "inlining call to T1.meth" } func ii() { // ERROR "can inline ii" @@ -290,3 +336,31 @@ func conv2(v uint64) uint64 { // ERROR "can inline conv2" func conv1(v uint64) uint64 { // ERROR "can inline conv1" return uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(uint64(v))))))))))) } + +func select1(x, y chan bool) int { // ERROR "can inline select1" "x does not escape" "y does not escape" + select { + case <-x: + return 1 + case <-y: + return 2 + } +} + +func select2(x, y chan bool) { // ERROR "can inline select2" "x does not escape" "y does not escape" +loop: // test that labeled select can be inlined. + select { + case <-x: + break loop + case <-y: + } +} + +func inlineSelect2(x, y chan bool) { // ERROR "can inline inlineSelect2" ERROR "x does not escape" "y does not escape" +loop: + for i := 0; i < 5; i++ { + if i == 3 { + break loop + } + select2(x, y) // ERROR "inlining call to select2" + } +} diff --git a/test/inline_big.go b/test/inline_big.go index 68e1101d3b0de9..83672753f78a17 100644 --- a/test/inline_big.go +++ b/test/inline_big.go @@ -1023,7 +1023,7 @@ func f(a []int) int { // ERROR "cannot inline f:.*" "a does not escape" a[997] = 0 a[998] = 0 a[999] = 0 - x := small(a) // ERROR "inlining call to small .*" + x := small(a) // ERROR "inlining call to small" y := medium(a) // The crux of this test: medium is not inlined. return x + y } diff --git a/test/inline_endian.go b/test/inline_endian.go new file mode 100644 index 00000000000000..e00e06a75ee959 --- /dev/null +++ b/test/inline_endian.go @@ -0,0 +1,23 @@ +// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1 + +//go:build (386 || amd64 || arm64 || ppc64le || s390x) && !gcflags_noopt +// +build 386 amd64 arm64 ppc64le s390x +// +build !gcflags_noopt + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Similar to inline.go, but only for architectures that can merge loads. + +package foo + +import ( + "encoding/binary" +) + +// Ensure that simple encoding/binary functions are cheap enough +// that functions using them can also be inlined (issue 42958). +func endian(b []byte) uint64 { // ERROR "can inline endian" "b does not escape" + return binary.LittleEndian.Uint64(b) + binary.BigEndian.Uint64(b) // ERROR "inlining call to binary.littleEndian.Uint64" "inlining call to binary.bigEndian.Uint64" +} diff --git a/test/inline_nounified.go b/test/inline_nounified.go new file mode 100644 index 00000000000000..7a9fc100716806 --- /dev/null +++ b/test/inline_nounified.go @@ -0,0 +1,21 @@ +// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1 +//go:build !goexperiment.unified +// +build !goexperiment.unified + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package foo + +func r(z int) int { + foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape" + return x + z + } + bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2" + return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3" + return 2*y + x*z + }(x) // ERROR "inlining call to r.func2.1" + } + return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3" +} diff --git a/test/inline_unified.go b/test/inline_unified.go new file mode 100644 index 00000000000000..5dc43ab070cd36 --- /dev/null +++ b/test/inline_unified.go @@ -0,0 +1,21 @@ +// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1 +//go:build goexperiment.unified +// +build goexperiment.unified + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package foo + +func r(z int) int { + foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape" + return x + z + } + bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2" + return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3" + return 2*y + x*z + }(x) // ERROR "inlining call to r.func2.1" + } + return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3" +} diff --git a/test/inline_variadic.go b/test/inline_variadic.go index 687048a1922d55..49483d77f79934 100644 --- a/test/inline_variadic.go +++ b/test/inline_variadic.go @@ -14,6 +14,6 @@ func head(xs ...string) string { // ERROR "can inline head" "leaking param: xs t } func f() string { // ERROR "can inline f" - x := head("hello", "world") // ERROR "inlining call to head" "\[\]string{...} does not escape" + x := head("hello", "world") // ERROR "inlining call to head" "\.\.\. argument does not escape" return x } diff --git a/test/interface/embed1.dir/embed0.go b/test/interface/embed1.dir/embed0.go index 728bec74e876ca..4aed391b634a00 100644 --- a/test/interface/embed1.dir/embed0.go +++ b/test/interface/embed1.dir/embed0.go @@ -7,10 +7,11 @@ package p type T int + func (t T) m() {} -type I interface { m() } -type J interface { I } +type I interface{ m() } +type J interface{ I } func main() { var i I diff --git a/test/interface/explicit.go b/test/interface/explicit.go index 1b7af6712b661d..e18d6843ec66ec 100644 --- a/test/interface/explicit.go +++ b/test/interface/explicit.go @@ -38,7 +38,7 @@ var e E func main() { e = t // ok - t = e // ERROR "need explicit|need type assertion|incompatible type" + t = e // ERROR "need explicit|need type assertion" // neither of these can work, // because i has an extra method @@ -57,7 +57,7 @@ func main() { // cannot type-assert non-interfaces f := 2.0 - _ = f.(int) // ERROR "non-interface type|only valid for interface types|not an interface type" + _ = f.(int) // ERROR "non-interface type|only valid for interface types|not an interface" } diff --git a/test/interface/pointer.go b/test/interface/pointer.go index c21e4da390ab0f..a71b3f4bf89ddd 100644 --- a/test/interface/pointer.go +++ b/test/interface/pointer.go @@ -24,7 +24,6 @@ type Start struct { func (start *Start) Next() *Inst { return nil } - func AddInst(Inst) *Inst { print("ok in addinst\n") return nil @@ -33,8 +32,6 @@ func AddInst(Inst) *Inst { func main() { print("call addinst\n") var x Inst = AddInst(new(Start)) // ERROR "pointer to interface|incompatible type" - _ = x print("return from addinst\n") - var y *Inst = new(Start) // ERROR "pointer to interface|incompatible type" - _ = y + var y *Inst = new(Start) // ERROR "pointer to interface|incompatible type" } diff --git a/test/intrinsic_atomic.go b/test/intrinsic_atomic.go index 61911b7a46e2ed..a1004c89d9538d 100644 --- a/test/intrinsic_atomic.go +++ b/test/intrinsic_atomic.go @@ -1,5 +1,5 @@ // errorcheck -0 -d=ssa/intrinsics/debug -// +build amd64 arm64 mips mipsle mips64 mips64le ppc64 ppc64le riscv64 s390x +// +build amd64 arm64 loong64 mips mipsle mips64 mips64le ppc64 ppc64le riscv64 s390x // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/ken/slicearray.go b/test/ken/slicearray.go index 6cf676c588b255..a431983d156b18 100644 --- a/test/ken/slicearray.go +++ b/test/ken/slicearray.go @@ -89,7 +89,7 @@ func main() { by = bx[2:8] tstb() - // width 4 (float64) + // width 8 (float64) lb = 0 hb = 10 fy = fx[lb:hb] diff --git a/test/linkmain_run.go b/test/linkmain_run.go index 077f7ee91753c5..6bc82dfafcd1b9 100644 --- a/test/linkmain_run.go +++ b/test/linkmain_run.go @@ -62,14 +62,14 @@ func main() { } // helloworld.go is package main - run("go tool compile -o", tmp("linkmain.o"), "helloworld.go") - run("go tool compile -pack -o", tmp("linkmain.a"), "helloworld.go") + run("go tool compile -p=main -o", tmp("linkmain.o"), "helloworld.go") + run("go tool compile -p=main -pack -o", tmp("linkmain.a"), "helloworld.go") run("go tool link -o", tmp("linkmain.exe"), tmp("linkmain.o")) run("go tool link -o", tmp("linkmain.exe"), tmp("linkmain.a")) // linkmain.go is not - run("go tool compile -o", tmp("linkmain1.o"), "linkmain.go") - run("go tool compile -pack -o", tmp("linkmain1.a"), "linkmain.go") + run("go tool compile -p=notmain -o", tmp("linkmain1.o"), "linkmain.go") + run("go tool compile -p=notmain -pack -o", tmp("linkmain1.a"), "linkmain.go") runFail("go tool link -o", tmp("linkmain.exe"), tmp("linkmain1.o")) runFail("go tool link -o", tmp("linkmain.exe"), tmp("linkmain1.a")) cleanup() diff --git a/test/linkname.dir/linkname1.go b/test/linkname.dir/linkname1.go index c61a0d7d95ce09..7d51b94802841f 100644 --- a/test/linkname.dir/linkname1.go +++ b/test/linkname.dir/linkname1.go @@ -1,6 +1,6 @@ package x -func indexByte(xs []byte, b byte) int { // ERROR "xs does not escape" +func indexByte(xs []byte, b byte) int { // ERROR "xs does not escape" "can inline indexByte" for i, x := range xs { if x == b { return i diff --git a/test/linkname.dir/linkname2.go b/test/linkname.dir/linkname2.go index 9323ac5f1ee28b..d2ee841624b599 100644 --- a/test/linkname.dir/linkname2.go +++ b/test/linkname.dir/linkname2.go @@ -2,7 +2,7 @@ package y import _ "unsafe" -//go:linkname byteIndex linkname1.indexByte +//go:linkname byteIndex test/linkname1.indexByte func byteIndex(xs []byte, b byte) int // ERROR "leaking param: xs" func ContainsSlash(data []byte) bool { // ERROR "leaking param: data" "can inline ContainsSlash" diff --git a/test/linkname2.go b/test/linkname2.go deleted file mode 100644 index 43e66a584958bb..00000000000000 --- a/test/linkname2.go +++ /dev/null @@ -1,30 +0,0 @@ -// errorcheck - -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Tests that errors are reported for misuse of linkname. -package p - -import _ "unsafe" - -type t int - -var x, y int - -//go:linkname x ok - -// ERROR "//go:linkname requires linkname argument or -p compiler flag" -// BAD: want error "//go:linkname must refer to declared function or variable" -// BAD: want error "//go:linkname must refer to declared function or variable" -// ERROR "duplicate //go:linkname for x" - -// The two BAD lines are just waiting for #42938 before we can -// re-enable the errors. - -//line linkname2.go:18 -//go:linkname y -//go:linkname nonexist nonexist -//go:linkname t notvarfunc -//go:linkname x duplicate diff --git a/test/linkname3.go b/test/linkname3.go new file mode 100644 index 00000000000000..df110cd064d151 --- /dev/null +++ b/test/linkname3.go @@ -0,0 +1,25 @@ +// errorcheck + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests that errors are reported for misuse of linkname. +package p + +import _ "unsafe" + +type t int + +var x, y int + +//go:linkname x ok + +// ERROR "//go:linkname must refer to declared function or variable" +// ERROR "//go:linkname must refer to declared function or variable" +// ERROR "duplicate //go:linkname for x" + +//line linkname3.go:18 +//go:linkname nonexist nonexist +//go:linkname t notvarfunc +//go:linkname x duplicate diff --git a/test/linkobj.go b/test/linkobj.go index 4c9bd24568b112..023996aa30952a 100644 --- a/test/linkobj.go +++ b/test/linkobj.go @@ -37,28 +37,28 @@ func main() { 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") @@ -76,9 +76,9 @@ func main() { } // 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") + run("go", "tool", "compile", "-p=p1", pkg, "-D", ".", "-I", ".", "-l", "-o", "p1."+o, "-linkobj", "p1.lo", "p1.go") + run("go", "tool", "compile", "-p=p2", pkg, "-D", ".", "-I", ".", "-l", "-o", "p2."+o, "-linkobj", "p2.lo", "p2.go") + run("go", "tool", "compile", "-p=main", pkg, "-D", ".", "-I", ".", "-l", "-o", "p3."+o, "-linkobj", "p3.lo", "p3.go") cp("p1."+o, "p1.oo") cp("p2."+o, "p2.oo") diff --git a/test/live.go b/test/live.go index bc7b3849cf3b57..6f3b86a35d69c2 100644 --- a/test/live.go +++ b/test/live.go @@ -1,5 +1,6 @@ // errorcheckwithauto -0 -l -live -wb=0 -d=ssa/insert_resched_checks/off -// +build !ppc64,!ppc64le,!goexperiment.regabi,!goexperiment.regabidefer +//go:build !ppc64 && !ppc64le && !goexperiment.regabiargs +// +build !ppc64,!ppc64le,!goexperiment.regabiargs // ppc64 needs a better tighten pass to make f18 pass // rescheduling checks need to be turned off because there are some live variables across the inserted check call @@ -144,8 +145,8 @@ var i9 interface{} func f9() bool { g8() x := i9 - y := interface{}(g18()) // ERROR "live at call to convT2E: x.data$" "live at call to g18: x.data$" "stack object .autotmp_[0-9]+ \[2\]string$" - i9 = y // make y escape so the line above has to call convT2E + y := interface{}(g18()) // ERROR "live at call to convT: x.data$" "live at call to g18: x.data$" "stack object .autotmp_[0-9]+ \[2\]string$" + i9 = y // make y escape so the line above has to call convT return x != y } @@ -424,7 +425,7 @@ func f27defer(b bool) { } defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+" - return // ERROR "live at call to call27: .autotmp_[0-9]+" + return // ERROR "live at indirect call: .autotmp_[0-9]+" } // and newproc (go) escapes to the heap @@ -432,9 +433,9 @@ func f27defer(b bool) { func f27go(b bool) { x := 0 if b { - go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newproc: &x$" + go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: &x .autotmp_[0-9]+$" "live at call to newproc: &x$" // allocate two closures, the func literal, and the wrapper for go } - go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" + go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: .autotmp_[0-9]+$" // allocate two closures, the func literal, and the wrapper for go printnl() } @@ -503,7 +504,7 @@ func f31(b1, b2, b3 bool) { g31(g18()) // ERROR "stack object .autotmp_[0-9]+ \[2\]string$" } if b2 { - h31(g18()) // ERROR "live at call to convT2E: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$" + h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$" } if b3 { panic(g18()) @@ -606,7 +607,7 @@ func f38(b bool) { printnl() case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ string$" printnl() - case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" + case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to f[ibc]38:( .autotmp_[0-9]+)+$" printnl() } printnl() @@ -688,7 +689,7 @@ type T struct{} func (*T) Foo(ptr *int) {} -type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "live at entry to R\.Foo: \.this ptr" +type R struct{ *T } // issue 18860: output arguments must be live all the time if there is a defer. // In particular, at printint r must be live. @@ -718,7 +719,7 @@ func f44(f func() [2]*int) interface{} { // ERROR "live at entry to f44: f" type T struct { s [1][2]*int } - ret := T{} + ret := T{} // ERROR "stack object ret T" ret.s[0] = f() - return ret // ERROR "stack object .autotmp_[0-9]+ T" + return ret } diff --git a/test/live_regabi.go b/test/live_regabi.go index 2b0278ecb83c90..027d476ab206df 100644 --- a/test/live_regabi.go +++ b/test/live_regabi.go @@ -1,5 +1,6 @@ // errorcheckwithauto -0 -l -live -wb=0 -d=ssa/insert_resched_checks/off -// +build amd64,goexperiment.regabidefer,goexperiment.regabiargs +//go:build (amd64 && goexperiment.regabiargs) || (arm64 && goexperiment.regabiargs) +// +build amd64,goexperiment.regabiargs arm64,goexperiment.regabiargs // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -139,8 +140,8 @@ var i9 interface{} func f9() bool { g8() x := i9 - y := interface{}(g18()) // ERROR "live at call to convT2E: x.data$" "live at call to g18: x.data$" "stack object .autotmp_[0-9]+ \[2\]string$" - i9 = y // make y escape so the line above has to call convT2E + y := interface{}(g18()) // ERROR "live at call to convT: x.data$" "live at call to g18: x.data$" "stack object .autotmp_[0-9]+ \[2\]string$" + i9 = y // make y escape so the line above has to call convT return x != y } @@ -498,7 +499,7 @@ func f31(b1, b2, b3 bool) { g31(g18()) // ERROR "stack object .autotmp_[0-9]+ \[2\]string$" } if b2 { - h31(g18()) // ERROR "live at call to convT2E: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$" + h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$" } if b3 { panic(g18()) @@ -601,7 +602,7 @@ func f38(b bool) { printnl() case *fi38(2) = <-fc38(): // ERROR "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" "stack object .autotmp_[0-9]+ string$" printnl() - case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to fc38:( .autotmp_[0-9]+)+$" "live at call to fi38:( .autotmp_[0-9]+)+$" + case *fi38(3), *fb38() = <-fc38(): // ERROR "stack object .autotmp_[0-9]+ string$" "live at call to f[ibc]38:( .autotmp_[0-9]+)+$" printnl() } printnl() @@ -683,7 +684,7 @@ type T struct{} func (*T) Foo(ptr *int) {} -type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "live at entry to R\.Foo: \.this ptr" +type R struct{ *T } // issue 18860: output arguments must be live all the time if there is a defer. // In particular, at printint r must be live. @@ -713,7 +714,7 @@ func f44(f func() [2]*int) interface{} { // ERROR "live at entry to f44: f" type T struct { s [1][2]*int } - ret := T{} + ret := T{} // ERROR "stack object ret T" ret.s[0] = f() - return ret // ERROR "stack object .autotmp_[0-9]+ T" + return ret } diff --git a/test/live_syscall.go b/test/live_syscall.go deleted file mode 100644 index b920ff68aabbd9..00000000000000 --- a/test/live_syscall.go +++ /dev/null @@ -1,40 +0,0 @@ -// errorcheck -0 -m -live - -// +build !windows,!js - -// 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. - -// Test escape analysis and liveness inferred for syscall.Syscall-like functions. - -package p - -import ( - "syscall" - "unsafe" -) - -func f(uintptr) // ERROR "assuming arg#1 is unsafe uintptr" - -func g() { // ERROR "can inline g" - var t int - f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" -} - -func h() { // ERROR "can inline h" - var v int - syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" -} - -func i() { // ERROR "can inline i" - var t int - p := unsafe.Pointer(&t) - f(uintptr(p)) // ERROR "live at call to f: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" -} - -func j() { // ERROR "can inline j" - var v int - p := unsafe.Pointer(&v) - syscall.Syscall(0, 1, uintptr(p), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" -} diff --git a/test/live_uintptrkeepalive.go b/test/live_uintptrkeepalive.go new file mode 100644 index 00000000000000..e39e31f77faf93 --- /dev/null +++ b/test/live_uintptrkeepalive.go @@ -0,0 +1,63 @@ +// errorcheck -0 -m -live -std + +// +build !windows,!js + +// 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. + +// Test escape analysis and liveness inferred for uintptrkeepalive functions. +// +// This behavior is enabled automatically for function declarations with no +// bodies (assembly, linkname), as well as explicitly on complete functions +// with //go:uintptrkeepalive. +// +// This is most important for syscall.Syscall (and similar functions), so we +// test it explicitly. + +package p + +import ( + "syscall" + "unsafe" +) + +func implicit(uintptr) // ERROR "assuming arg#1 is unsafe uintptr" + +//go:uintptrkeepalive +//go:nosplit +func explicit(uintptr) { +} + +func autotmpImplicit() { // ERROR "can inline autotmpImplicit" + var t int + implicit(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to implicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} + +func autotmpExplicit() { // ERROR "can inline autotmpExplicit" + var t int + explicit(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to explicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} + +func autotmpSyscall() { // ERROR "can inline autotmpSyscall" + var v int + syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} + +func localImplicit() { // ERROR "can inline localImplicit" + var t int + p := unsafe.Pointer(&t) + implicit(uintptr(p)) // ERROR "live at call to implicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} + +func localExplicit() { // ERROR "can inline localExplicit" + var t int + p := unsafe.Pointer(&t) + explicit(uintptr(p)) // ERROR "live at call to explicit: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} + +func localSyscall() { // ERROR "can inline localSyscall" + var v int + p := unsafe.Pointer(&v) + syscall.Syscall(0, 1, uintptr(p), 2) // ERROR "live at call to Syscall: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$" +} diff --git a/test/locklinear.go b/test/locklinear.go deleted file mode 100644 index 54e40a543b160c..00000000000000 --- a/test/locklinear.go +++ /dev/null @@ -1,171 +0,0 @@ -// run - -// Copyright 2017 The Go 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 locks don't go quadratic due to runtime hash table collisions. - -package main - -import ( - "bytes" - "fmt" - "log" - "os" - "runtime" - "runtime/pprof" - "sync" - "time" -) - -const debug = false - -// checkLinear asserts that the running time of f(n) is at least linear but sub-quadratic. -// tries is the initial number of iterations. -func checkLinear(typ string, tries int, f func(n int)) { - // Depending on the machine and OS, this test might be too fast - // to measure with accurate enough granularity. On failure, - // make it run longer, hoping that the timing granularity - // is eventually sufficient. - - timeF := func(n int) time.Duration { - t1 := time.Now() - f(n) - return time.Since(t1) - } - - n := tries - fails := 0 - var buf bytes.Buffer - inversions := 0 - for { - t1 := timeF(n) - t2 := timeF(2 * n) - if debug { - println(n, t1.String(), 2*n, t2.String()) - } - fmt.Fprintf(&buf, "%d %v %d %v (%.1fX)\n", n, t1, 2*n, t2, float64(t2)/float64(t1)) - // should be 2x (linear); allow up to 3x - if t1*3/2 < t2 && t2 < t1*3 { - return - } - if t2 < t1 { - if inversions++; inversions >= 5 { - // The system must be overloaded (some builders). Give up. - return - } - continue // try again; don't increment fails - } - // Once the test runs long enough for n ops, - // try to get the right ratio at least once. - // If many in a row all fail, give up. - if fails++; fails >= 5 { - // If 2n ops run in under a second and the ratio - // doesn't work out, make n bigger, trying to reduce - // the effect that a constant amount of overhead has - // on the computed ratio. - if t2 < time.Second*4/10 { - fails = 0 - n *= 2 - continue - } - panic(fmt.Sprintf("%s: too slow: %d ops: %v; %d ops: %v\n\n%s", - typ, n, t1, 2*n, t2, buf.String())) - } - } -} - -const offset = 251 // known size of runtime hash table - -const profile = false - -func main() { - if profile { - f, err := os.Create("lock.prof") - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - checkLinear("lockone", 1000, func(n int) { - ch := make(chan int) - locks := make([]sync.RWMutex, offset+1) - for i := 0; i < n; i++ { - go func() { - locks[0].Lock() - ch <- 1 - }() - } - time.Sleep(1 * time.Millisecond) - - go func() { - for j := 0; j < n; j++ { - locks[1].Lock() - locks[offset].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[offset].Unlock() - } - }() - - for j := 0; j < n; j++ { - locks[1].Lock() - locks[offset].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[offset].Unlock() - } - - for i := 0; i < n; i++ { - <-ch - locks[0].Unlock() - } - }) - - if runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" { - // lockmany reliably fails on the linux-arm-arm5spacemonkey - // builder. See https://golang.org/issue/24221. - return - } - - checkLinear("lockmany", 1000, func(n int) { - locks := make([]sync.RWMutex, n*offset+1) - - var wg sync.WaitGroup - for i := 0; i < n; i++ { - wg.Add(1) - go func(i int) { - locks[(i+1)*offset].Lock() - wg.Done() - locks[(i+1)*offset].Lock() - locks[(i+1)*offset].Unlock() - }(i) - } - wg.Wait() - - go func() { - for j := 0; j < n; j++ { - locks[1].Lock() - locks[0].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[0].Unlock() - } - }() - - for j := 0; j < n; j++ { - locks[1].Lock() - locks[0].Lock() - locks[1].Unlock() - runtime.Gosched() - locks[0].Unlock() - } - - for i := 0; i < n; i++ { - locks[(i+1)*offset].Unlock() - } - }) -} diff --git a/test/loopbce.go b/test/loopbce.go index f0c9bd0f81985d..db830daf5cc191 100644 --- a/test/loopbce.go +++ b/test/loopbce.go @@ -3,6 +3,8 @@ package main +import "math" + func f0a(a []int) int { x := 0 for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$" @@ -47,7 +49,7 @@ func f2(a []int) int { func f4(a [10]int) int { x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,8\], increment 2$" x += a[i] // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x @@ -55,7 +57,7 @@ func f4(a [10]int) int { func f5(a [10]int) int { x := 0 - for i := -10; i < len(a); i += 2 { // ERROR "Induction variable: limits \[-10,10\), increment 2$" + for i := -10; i < len(a); i += 2 { // ERROR "Induction variable: limits \[-10,8\], increment 2$" x += a[i] } return x @@ -119,7 +121,7 @@ func g0f(a string) int { func g1() int { a := "evenlength" x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,8\], increment 2$" x += int(a[i]) // ERROR "(\([0-9]+\) )?Proved IsInBounds$" } return x @@ -128,7 +130,7 @@ func g1() int { func g2() int { a := "evenlength" x := 0 - for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,10\), increment 2$" + for i := 0; i < len(a); i += 2 { // ERROR "Induction variable: limits \[0,8\], increment 2$" j := i if a[i] == 'e' { // ERROR "(\([0-9]+\) )?Proved IsInBounds$" j = j + 1 @@ -140,9 +142,11 @@ func g2() int { func g3a() { a := "this string has length 25" - for i := 0; i < len(a); i += 5 { // ERROR "Induction variable: limits \[0,25\), increment 5$" + for i := 0; i < len(a); i += 5 { // ERROR "Induction variable: limits \[0,20\], increment 5$" useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" - useString(a[:i+3]) + useString(a[:i+3]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useString(a[:i+5]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$" + useString(a[:i+6]) } } @@ -281,8 +285,8 @@ func d2(a [100]int) [100]int { func d3(a [100]int) [100]int { for i := 0; i <= 99; i++ { // ERROR "Induction variable: limits \[0,99\], increment 1$" - for j := 0; j <= i-1; j++ { // ERROR "Induction variable: limits \[0,\?\], increment 1$" - a[j] = 0 // ERROR "Proved IsInBounds$" + for j := 0; j <= i-1; j++ { + a[j] = 0 a[j+1] = 0 // ERROR "Proved IsInBounds$" a[j+2] = 0 } @@ -290,7 +294,61 @@ func d3(a [100]int) [100]int { return a } -func nobce1() { +func d4() { + for i := int64(math.MaxInt64 - 9); i < math.MaxInt64-2; i += 4 { // ERROR "Induction variable: limits \[9223372036854775798,9223372036854775802\], increment 4$" + useString("foo") + } + for i := int64(math.MaxInt64 - 8); i < math.MaxInt64-2; i += 4 { // ERROR "Induction variable: limits \[9223372036854775799,9223372036854775803\], increment 4$" + useString("foo") + } + for i := int64(math.MaxInt64 - 7); i < math.MaxInt64-2; i += 4 { + useString("foo") + } + for i := int64(math.MaxInt64 - 6); i < math.MaxInt64-2; i += 4 { // ERROR "Induction variable: limits \[9223372036854775801,9223372036854775801\], increment 4$" + useString("foo") + } + for i := int64(math.MaxInt64 - 9); i <= math.MaxInt64-2; i += 4 { // ERROR "Induction variable: limits \[9223372036854775798,9223372036854775802\], increment 4$" + useString("foo") + } + for i := int64(math.MaxInt64 - 8); i <= math.MaxInt64-2; i += 4 { // ERROR "Induction variable: limits \[9223372036854775799,9223372036854775803\], increment 4$" + useString("foo") + } + for i := int64(math.MaxInt64 - 7); i <= math.MaxInt64-2; i += 4 { + useString("foo") + } + for i := int64(math.MaxInt64 - 6); i <= math.MaxInt64-2; i += 4 { + useString("foo") + } +} + +func d5() { + for i := int64(math.MinInt64 + 9); i > math.MinInt64+2; i -= 4 { // ERROR "Induction variable: limits \[-9223372036854775803,-9223372036854775799\], increment 4" + useString("foo") + } + for i := int64(math.MinInt64 + 8); i > math.MinInt64+2; i -= 4 { // ERROR "Induction variable: limits \[-9223372036854775804,-9223372036854775800\], increment 4" + useString("foo") + } + for i := int64(math.MinInt64 + 7); i > math.MinInt64+2; i -= 4 { + useString("foo") + } + for i := int64(math.MinInt64 + 6); i > math.MinInt64+2; i -= 4 { // ERROR "Induction variable: limits \[-9223372036854775802,-9223372036854775802\], increment 4" + useString("foo") + } + for i := int64(math.MinInt64 + 9); i >= math.MinInt64+2; i -= 4 { // ERROR "Induction variable: limits \[-9223372036854775803,-9223372036854775799\], increment 4" + useString("foo") + } + for i := int64(math.MinInt64 + 8); i >= math.MinInt64+2; i -= 4 { // ERROR "Induction variable: limits \[-9223372036854775804,-9223372036854775800\], increment 4" + useString("foo") + } + for i := int64(math.MinInt64 + 7); i >= math.MinInt64+2; i -= 4 { + useString("foo") + } + for i := int64(math.MinInt64 + 6); i >= math.MinInt64+2; i -= 4 { + useString("foo") + } +} + +func bce1() { // tests overflow of max-min a := int64(9223372036854774057) b := int64(-1547) @@ -300,8 +358,7 @@ func nobce1() { panic("invalid test: modulos should differ") } - for i := b; i < a; i += z { - // No induction variable is possible because i will overflow a first iteration. + for i := b; i < a; i += z { // ERROR "Induction variable: limits \[-1547,9223372036854772720\], increment 1337" useString("foobar") } } @@ -345,6 +402,22 @@ func issue26116a(a []int) { } } +func stride1(x *[7]int) int { + s := 0 + for i := 0; i <= 8; i += 3 { // ERROR "Induction variable: limits \[0,6\], increment 3" + s += x[i] // ERROR "Proved IsInBounds" + } + return s +} + +func stride2(x *[7]int) int { + s := 0 + for i := 0; i < 9; i += 3 { // ERROR "Induction variable: limits \[0,6\], increment 3" + s += x[i] // ERROR "Proved IsInBounds" + } + return s +} + //go:noinline func useString(a string) { } diff --git a/test/maymorestack.go b/test/maymorestack.go new file mode 100644 index 00000000000000..ec84ad44bc42c8 --- /dev/null +++ b/test/maymorestack.go @@ -0,0 +1,47 @@ +// run -gcflags=-d=maymorestack=main.mayMoreStack + +// Copyright 2021 The Go 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 maymorestack testing hook by injecting a hook that counts +// how many times it is called and checking that count. + +package main + +import "runtime" + +var count uint32 + +//go:nosplit +func mayMoreStack() { + count++ +} + +func main() { + const wantCount = 128 + + anotherFunc(wantCount - 1) // -1 because the call to main already counted + + if count == 0 { + panic("mayMoreStack not called") + } else if count != wantCount { + println(count, "!=", wantCount) + panic("wrong number of calls to mayMoreStack") + } +} + +//go:noinline +func anotherFunc(n int) { + // Trigger a stack growth on at least some calls to + // anotherFunc to test that mayMoreStack is called outside the + // morestack loop. It's also important that it is called + // before (not after) morestack, but that's hard to test. + var x [1 << 10]byte + + if n > 1 { + anotherFunc(n - 1) + } + + runtime.KeepAlive(x) +} diff --git a/test/method2.go b/test/method2.go index 2a92136d6c10ba..0a497b4b84c942 100644 --- a/test/method2.go +++ b/test/method2.go @@ -28,7 +28,7 @@ type Val interface { val() int } -var _ = (*Val).val // ERROR "method" +var _ = (*Val).val // ERROR "method|type \*Val is pointer to interface, not interface" var v Val var pv = &v diff --git a/test/nilcheck.go b/test/nilcheck.go index 6879438e9cdb0f..e81db6dcb07eb8 100644 --- a/test/nilcheck.go +++ b/test/nilcheck.go @@ -184,6 +184,7 @@ func f4(x *[10]int) { func f5(m map[string]struct{}) bool { // Existence-only map lookups should not generate a nil check - _, ok := m[""] + tmp1, tmp2 := m[""] // ERROR "removed nil check" + _, ok := tmp1, tmp2 return ok } diff --git a/test/nilptr5.go b/test/nilptr5.go index 2c48c0b2610196..118746e4aa9086 100644 --- a/test/nilptr5.go +++ b/test/nilptr5.go @@ -1,7 +1,7 @@ // errorcheck -0 -d=nil -// +build !wasm -// +build !aix +//go:build !wasm && !aix +// +build !wasm,!aix // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -20,7 +20,7 @@ func f5(p *float32, q *float64, r *float32, s *float64) float64 { return x + y } -type T [29]byte +type T struct{ b [29]byte } func f6(p, q *T) { x := *p // ERROR "removed nil check" @@ -28,6 +28,6 @@ func f6(p, q *T) { } // make sure to remove nil check for memory move (issue #18003) -func f8(t *[8]int) [8]int { +func f8(t *struct{ b [8]int }) struct{ b [8]int } { return *t // ERROR "removed nil check" } diff --git a/test/nosplit.go b/test/nosplit.go index 7c7e1bfd99e01a..218eb73727affe 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -51,7 +51,8 @@ var tests = ` start 0 # Large frame marked nosplit is always wrong. -start 10000 nosplit +# Frame is so large it overflows cmd/link's int16. +start 100000 nosplit REJECT # Calling a large frame is okay. @@ -70,6 +71,18 @@ start 0 call start start 0 nosplit call start REJECT +# Non-trivial recursion runs out of space. +start 0 call f1 +f1 0 nosplit call f2 +f2 0 nosplit call f1 +REJECT +# Same but cycle starts below nosplit entry. +start 0 call f1 +f1 0 nosplit call f2 +f2 0 nosplit call f3 +f3 0 nosplit call f2 +REJECT + # Chains of ordinary functions okay. start 0 call f1 f1 80 call f2 @@ -105,6 +118,14 @@ f8 16 nosplit call end end 1000 REJECT +# Two paths both go over the stack limit. +start 0 call f1 +f1 80 nosplit call f2 call f3 +f2 40 nosplit call f4 +f3 96 nosplit +f4 40 nosplit +REJECT + # Test cases near the 128-byte limit. # Ordinary stack split frame is always okay. @@ -263,6 +284,9 @@ TestCases: case "mips64", "mips64le": ptrSize = 8 fmt.Fprintf(&buf, "#define REGISTER (R0)\n") + case "loong64": + ptrSize = 8 + fmt.Fprintf(&buf, "#define REGISTER (R0)\n") case "ppc64", "ppc64le": ptrSize = 8 fmt.Fprintf(&buf, "#define REGISTER (CTR)\n") @@ -292,12 +316,13 @@ TestCases: fmt.Fprintf(&gobuf, "func main() { main0() }\n") fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n") + adjusted := false for _, line := range strings.Split(lines, "\n") { line = strings.TrimSpace(line) if line == "" { continue } - for i, subline := range strings.Split(line, ";") { + for _, subline := range strings.Split(line, ";") { subline = strings.TrimSpace(subline) if subline == "" { continue @@ -311,10 +336,19 @@ TestCases: name := m[1] size, _ := strconv.Atoi(m[2]) + if size%ptrSize == 4 { + continue TestCases + } + nosplit := m[3] + body := m[4] + // The limit was originally 128 but is now 800 (928-128). // Instead of rewriting the test cases above, adjust - // the first stack frame to use up the extra bytes. - if i == 0 { + // the first nosplit frame to use up the extra bytes. + // This isn't exactly right because we could have + // nosplit -> split -> nosplit, but it's good enough. + if !adjusted && nosplit != "" { + adjusted = true size += (928 - 128) - 128 // Noopt builds have a larger stackguard. // See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier @@ -326,12 +360,6 @@ TestCases: } } - if size%ptrSize == 4 { - continue TestCases - } - nosplit := m[3] - body := m[4] - if nosplit != "" { nosplit = ",7" } else { diff --git a/test/oldescape_linkname.dir/linkname1.go b/test/oldescape_linkname.dir/linkname1.go deleted file mode 100644 index 9c61522fcc9185..00000000000000 --- a/test/oldescape_linkname.dir/linkname1.go +++ /dev/null @@ -1,10 +0,0 @@ -package x - -func indexByte(xs []byte, b byte) int { // ERROR "indexByte xs does not escape" - for i, x := range xs { - if x == b { - return i - } - } - return -1 -} diff --git a/test/oldescape_linkname.dir/linkname2.go b/test/oldescape_linkname.dir/linkname2.go deleted file mode 100644 index 5df4f50ff2c00f..00000000000000 --- a/test/oldescape_linkname.dir/linkname2.go +++ /dev/null @@ -1,13 +0,0 @@ -package y - -import _ "unsafe" - -//go:linkname byteIndex linkname1.indexByte -func byteIndex(xs []byte, b byte) int - -func ContainsSlash(data []byte) bool { // ERROR "leaking param: data" "can inline ContainsSlash" - if byteIndex(data, '/') != -1 { - return true - } - return false -} diff --git a/test/oldescape_linkname.dir/linkname3.go b/test/oldescape_linkname.dir/linkname3.go deleted file mode 100644 index cbbd3a10ba2baf..00000000000000 --- a/test/oldescape_linkname.dir/linkname3.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import _ "./linkname1" -import "./linkname2" - -func main() { // ERROR "can inline main" - str := "hello/world" - bs := []byte(str) // ERROR "\(\[\]byte\)\(str\) escapes to heap" - if y.ContainsSlash(bs) { // ERROR "inlining call to y.ContainsSlash" - } -} diff --git a/test/prove.go b/test/prove.go index 83b03808384cb6..1be257f2063484 100644 --- a/test/prove.go +++ b/test/prove.go @@ -1,6 +1,8 @@ -// +build amd64 // errorcheck -0 -d=ssa/prove/debug=1 +//go:build amd64 +// +build amd64 + // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -735,8 +737,8 @@ func range1(b []int) { // range2 elements are larger, so they use the general form of a range loop. func range2(b [][32]int) { - for i, v := range b { - b[i][0] = v[0] + 1 // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" + for i, v := range b { // ERROR "Induction variable: limits \[0,\?\), increment 1$" + b[i][0] = v[0] + 1 // ERROR "Proved IsInBounds$" if i < len(b) { // ERROR "Proved Less64$" println("x") } @@ -793,7 +795,7 @@ func unrollUpExcl(a []int) int { func unrollUpIncl(a []int) int { var i, x int for i = 0; i <= len(a)-2; i += 2 { // ERROR "Induction variable: limits \[0,\?\], increment 2$" - x += a[i] + x += a[i] // ERROR "Proved IsInBounds$" x += a[i+1] } if i == len(a)-1 { @@ -833,7 +835,7 @@ func unrollDownInclStep(a []int) int { var i, x int for i = len(a); i >= 2; i -= 2 { // ERROR "Induction variable: limits \[2,\?\], increment 2$" x += a[i-1] // ERROR "Proved IsInBounds$" - x += a[i-2] + x += a[i-2] // ERROR "Proved IsInBounds$" } if i == 1 { x += a[i-1] @@ -1036,6 +1038,26 @@ func divShiftClean32(n int32) int32 { return n / int32(16) // ERROR "Proved Rsh32x64 shifts to zero" } +func and(p []byte) ([]byte, []byte) { // issue #52563 + const blocksize = 16 + fullBlocks := len(p) &^ (blocksize - 1) + blk := p[:fullBlocks] // ERROR "Proved IsSliceInBounds$" + rem := p[fullBlocks:] // ERROR "Proved IsSliceInBounds$" + return blk, rem +} + +func issue51622(b []byte) int { + if len(b) >= 3 && b[len(b)-3] == '#' { // ERROR "Proved IsInBounds$" + return len(b) + } + return 0 +} + +func issue45928(x int) { + combinedFrac := (x) / (x | (1 << 31)) // ERROR "Proved Neq64$" + useInt(combinedFrac) +} + //go:noinline func useInt(a int) { } diff --git a/test/prove_constant_folding.go b/test/prove_constant_folding.go new file mode 100644 index 00000000000000..d4bdb20d83a564 --- /dev/null +++ b/test/prove_constant_folding.go @@ -0,0 +1,32 @@ +// +build amd64 +// errorcheck -0 -d=ssa/prove/debug=2 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f0i(x int) int { + if x == 20 { + return x // ERROR "Proved.+is constant 20$" + } + + if (x + 20) == 20 { + return x + 5 // ERROR "Proved.+is constant 0$" + } + + return x / 2 +} + +func f0u(x uint) uint { + if x == 20 { + return x // ERROR "Proved.+is constant 20$" + } + + if (x + 20) == 20 { + return x + 5 // ERROR "Proved.+is constant 0$" + } + + return x / 2 +} diff --git a/test/recover4.go b/test/recover4.go index 67ed970ecb2f00..7cab15a5a814dc 100644 --- a/test/recover4.go +++ b/test/recover4.go @@ -24,12 +24,13 @@ import ( "log" "runtime/debug" "syscall" - "unsafe" ) func memcopy(dst, src []byte) (n int, err error) { defer func() { - err = recover().(error) + if r, ok := recover().(error); ok { + err = r + } }() for i := 0; i < len(dst) && i < len(src); i++ { @@ -52,22 +53,23 @@ func main() { log.Fatalf("mmap: %v", err) } - other := make([]byte, 16*size) - - // Note: Cannot call syscall.Munmap, because Munmap checks - // that you are unmapping a whole region returned by Mmap. - // We are trying to unmap just a hole in the middle. - if _, _, err := syscall.Syscall(syscall.SYS_MUNMAP, uintptr(unsafe.Pointer(&data[8*size])), uintptr(4*size), 0); err != 0 { - log.Fatalf("munmap: %v", err) + // Create a hole in the mapping that's PROT_NONE. + // Note that we can't use munmap here because the Go runtime + // could create a mapping that ends up in this hole otherwise, + // invalidating the test. + hole := data[len(data)/2 : 3*(len(data)/4)] + if err := syscall.Mprotect(hole, syscall.PROT_NONE); err != nil { + log.Fatalf("mprotect: %v", err) } // Check that memcopy returns the actual amount copied - // before the fault (8*size - 5, the offset we skip in the argument). - n, err := memcopy(data[5:], other) + // before the fault. + const offset = 5 + n, err := memcopy(data[offset:], make([]byte, len(data))) if err == nil { log.Fatal("no error from memcopy across memory hole") } - if n != 8*size-5 { - log.Fatalf("memcopy returned %d, want %d", n, 8*size-5) + if expect := len(data)/2 - offset; n != expect { + log.Fatalf("memcopy returned %d, want %d", n, expect) } } diff --git a/test/reflectmethod7.go b/test/reflectmethod7.go index 42429978b45a69..688238c5119fa6 100644 --- a/test/reflectmethod7.go +++ b/test/reflectmethod7.go @@ -16,7 +16,7 @@ func (s S) M() {} func main() { t := reflect.TypeOf(S(0)) - fn, ok := reflect.PtrTo(t).MethodByName("M") + fn, ok := reflect.PointerTo(t).MethodByName("M") if !ok { panic("FAIL") } diff --git a/test/reflectmethod8.go b/test/reflectmethod8.go new file mode 100644 index 00000000000000..482163bae6f441 --- /dev/null +++ b/test/reflectmethod8.go @@ -0,0 +1,26 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure that the compiler can analyze non-reflect +// Type.{Method,MethodByName} calls. + +package p + +type I interface { + MethodByName(string) + Method(int) +} + +type M struct{} + +func (M) MethodByName(string) {} +func (M) Method(int) {} + +func f() { + var m M + I.MethodByName(m, "") + I.Method(m, 42) +} diff --git a/test/retjmp.dir/a.s b/test/retjmp.dir/a.s index c67a06638f90be..101b3428fc2d6c 100644 --- a/test/retjmp.dir/a.s +++ b/test/retjmp.dir/a.s @@ -10,3 +10,7 @@ TEXT ·f(SB), 4, $8-0 TEXT ·leaf(SB), 4, $0-0 RET ·f3(SB) JMP ·unreachable(SB) + +TEXT ·leaf2(SB), 4, $32-0 // nonzero frame size + RET ·f4(SB) + JMP ·unreachable(SB) diff --git a/test/retjmp.dir/main.go b/test/retjmp.dir/main.go index cb4bd018bf8c02..0bed5a61b7ac62 100644 --- a/test/retjmp.dir/main.go +++ b/test/retjmp.dir/main.go @@ -6,8 +6,9 @@ package main func f() func leaf() +func leaf2() -var f1called, f2called, f3called bool +var f1called, f2called, f3called, f4called bool func main() { f() @@ -21,11 +22,16 @@ func main() { if !f3called { panic("f3 not called") } + leaf2() + if !f4called { + panic("f4 not called") + } } func f1() { f1called = true } func f2() { f2called = true } func f3() { f3called = true } +func f4() { f4called = true } func unreachable() { panic("unreachable function called") diff --git a/test/run.go b/test/run.go index d7f5d02391b67d..3c5b10ad32c46d 100644 --- a/test/run.go +++ b/test/run.go @@ -9,10 +9,12 @@ package main import ( "bytes" + "encoding/json" "errors" "flag" "fmt" "go/build" + "go/build/constraint" "hash/fnv" "io" "io/fs" @@ -42,11 +44,46 @@ var ( linkshared = flag.Bool("linkshared", false, "") updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output") runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run") + force = flag.Bool("f", false, "ignore expected-failure test lists") shard = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.") shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.") ) +type envVars struct { + GOOS string + GOARCH string + GOEXPERIMENT string + CGO_ENABLED string +} + +var env = func() (res envVars) { + cmd := exec.Command(goTool(), "env", "-json") + stdout, err := cmd.StdoutPipe() + if err != nil { + log.Fatal("StdoutPipe:", err) + } + if err := cmd.Start(); err != nil { + log.Fatal("Start:", err) + } + if err := json.NewDecoder(stdout).Decode(&res); err != nil { + log.Fatal("Decode:", err) + } + if err := cmd.Wait(); err != nil { + log.Fatal("Wait:", err) + } + return +}() + +var unifiedEnabled = func() bool { + for _, tag := range build.Default.ToolTags { + if tag == "goexperiment.unified" { + return true + } + } + return false +}() + // defaultAllCodeGen returns the default value of the -all_codegen // flag. By default, we prefer to be fast (returning false), except on // the linux-amd64 builder that's already very fast, so we get more @@ -55,13 +92,18 @@ func defaultAllCodeGen() bool { return os.Getenv("GO_BUILDER_NAME") == "linux-amd64" } +func optimizationOff() bool { + return strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") +} + var ( - goos, goarch string - cgoEnabled bool + goos = env.GOOS + goarch = env.GOARCH + cgoEnabled, _ = strconv.ParseBool(env.CGO_ENABLED) // dirs are the directories to look for *.go files in. // TODO(bradfitz): just use all directories? - dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam"} + dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky"} // ratec controls the max number of tests running at a time. ratec chan bool @@ -82,13 +124,6 @@ const maxTests = 5000 func main() { flag.Parse() - goos = getenv("GOOS", runtime.GOOS) - goarch = getenv("GOARCH", runtime.GOARCH) - cgoEnv, err := exec.Command(goTool(), "env", "CGO_ENABLED").Output() - if err == nil { - cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(cgoEnv))) - } - findExecCmd() // Disable parallelism if printing or if using a simulator. @@ -142,8 +177,15 @@ func main() { status = "FAIL" } if test.err != nil { - status = "FAIL" errStr = test.err.Error() + if test.expectFail { + errStr += " (expected)" + } else { + status = "FAIL" + } + } else if test.expectFail { + status = "FAIL" + errStr = "unexpected success" } if status == "FAIL" { failed = true @@ -221,7 +263,7 @@ func goFiles(dir string) []string { type runCmd func(...string) ([]byte, error) func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) { - cmd := []string{goTool(), "tool", "compile", "-e"} + cmd := []string{goTool(), "tool", "compile", "-e", "-p=p"} cmd = append(cmd, flags...) if *linkshared { cmd = append(cmd, "-dynlink", "-installsuffix=dynlink") @@ -230,11 +272,13 @@ func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, er return runcmd(cmd...) } -func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) { - cmd := []string{goTool(), "tool", "compile", "-e"} - if localImports { - // Set relative path for local imports and import search path to current dir. - cmd = append(cmd, "-D", ".", "-I", ".") +func compileInDir(runcmd runCmd, dir string, flags []string, pkgname string, names ...string) (out []byte, err error) { + cmd := []string{goTool(), "tool", "compile", "-e", "-D", "test", "-I", "."} + if pkgname == "main" { + cmd = append(cmd, "-p=main") + } else { + pkgname = path.Join("test", strings.TrimSuffix(names[0], ".go")) + cmd = append(cmd, "-o", pkgname+".a", "-p", pkgname) } cmd = append(cmd, flags...) if *linkshared { @@ -275,9 +319,45 @@ type test struct { tempDir string err error + + // expectFail indicates whether the (overall) test recipe is + // expected to fail under the current test configuration (e.g., + // GOEXPERIMENT=unified). + expectFail bool +} + +// initExpectFail initializes t.expectFail based on the build+test +// configuration. +func (t *test) initExpectFail() { + if *force { + return + } + + failureSets := []map[string]bool{types2Failures} + + // Note: gccgo supports more 32-bit architectures than this, but + // hopefully the 32-bit failures are fixed before this matters. + switch goarch { + case "386", "arm", "mips", "mipsle": + failureSets = append(failureSets, types2Failures32Bit) + } + + if unifiedEnabled { + failureSets = append(failureSets, unifiedFailures) + } else { + failureSets = append(failureSets, go118Failures) + } + + filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows + + for _, set := range failureSets { + if set[filename] { + t.expectFail = true + return + } + } } -// startTest func startTest(dir, gofile string) *test { t := &test{ dir: dir, @@ -346,28 +426,33 @@ func getPackageNameFromSource(fn string) (string, error) { return pkgname[1], nil } +type goDirPkg struct { + name string + files []string +} + // If singlefilepkgs is set, each file is considered a separate package // even if the package names are the same. -func goDirPackages(longdir string, singlefilepkgs bool) ([][]string, error) { +func goDirPackages(longdir string, singlefilepkgs bool) ([]*goDirPkg, error) { files, err := goDirFiles(longdir) if err != nil { return nil, err } - var pkgs [][]string - m := make(map[string]int) + var pkgs []*goDirPkg + m := make(map[string]*goDirPkg) for _, file := range files { name := file.Name() pkgname, err := getPackageNameFromSource(filepath.Join(longdir, name)) if err != nil { log.Fatal(err) } - i, ok := m[pkgname] + p, ok := m[pkgname] if singlefilepkgs || !ok { - i = len(pkgs) - pkgs = append(pkgs, nil) - m[pkgname] = i + p = &goDirPkg{name: pkgname} + pkgs = append(pkgs, p) + m[pkgname] = p } - pkgs[i] = append(pkgs[i], name) + p.files = append(p.files, name) } return pkgs, nil } @@ -386,40 +471,24 @@ func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) { return true, "" } for _, line := range strings.Split(src, "\n") { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "//") { - line = line[2:] - } else { - continue - } - line = strings.TrimSpace(line) - if len(line) == 0 || line[0] != '+' { - continue + if strings.HasPrefix(line, "package ") { + break } - gcFlags := os.Getenv("GO_GCFLAGS") - ctxt := &context{ - GOOS: goos, - GOARCH: goarch, - cgoEnabled: cgoEnabled, - noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"), - } - - words := strings.Fields(line) - if words[0] == "+build" { - ok := false - for _, word := range words[1:] { - if ctxt.match(word) { - ok = true - break - } + + if expr, err := constraint.Parse(line); err == nil { + gcFlags := os.Getenv("GO_GCFLAGS") + ctxt := &context{ + GOOS: goos, + GOARCH: goarch, + cgoEnabled: cgoEnabled, + noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"), } - if !ok { - // no matching tag found. + + if !expr.Eval(ctxt.match) { return false, line } } } - // no build tags return true, "" } @@ -427,16 +496,6 @@ func (ctxt *context) match(name string) bool { if name == "" { return false } - if i := strings.Index(name, ","); i >= 0 { - // comma-separated list - return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) - } - if strings.HasPrefix(name, "!!") { // bad syntax, reject always - return false - } - if strings.HasPrefix(name, "!") { // negation - return len(name) > 1 && !ctxt.match(name[1:]) - } // Tags must be letters, digits, underscores or dots. // Unlike in Go identifiers, all digits are fine (e.g., "386"). @@ -474,17 +533,19 @@ func (ctxt *context) match(name string) bool { return false } -func init() { checkShouldTest() } +func init() { + checkShouldTest() +} // goGcflags returns the -gcflags argument to use with go build / go run. // This must match the flags used for building the standard library, // or else the commands will rebuild any needed packages (like runtime) // over and over. -func goGcflags() string { +func (t *test) goGcflags() string { return "-gcflags=all=" + os.Getenv("GO_GCFLAGS") } -func goGcflagsIsEmpty() bool { +func (t *test) goGcflagsIsEmpty() bool { return "" == os.Getenv("GO_GCFLAGS") } @@ -510,24 +571,23 @@ func (t *test) run() { } // Execution recipe stops at first blank line. - pos := strings.Index(t.src, "\n\n") - if pos == -1 { + action, _, ok := strings.Cut(t.src, "\n\n") + if !ok { t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName()) return } - action := t.src[:pos] - if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") { + if firstLine, rest, ok := strings.Cut(action, "\n"); ok && strings.Contains(firstLine, "+build") { // skip first line - action = action[nl+1:] + action = rest } action = strings.TrimPrefix(action, "//") // Check for build constraints only up to the actual code. - pkgPos := strings.Index(t.src, "\npackage") - if pkgPos == -1 { - pkgPos = pos // some files are intentionally malformed + header, _, ok := strings.Cut(t.src, "\npackage") + if !ok { + header = action // some files are intentionally malformed } - if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok { + if ok, why := shouldTest(header, goos, goarch); !ok { if *showSkips { fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why) } @@ -539,9 +599,11 @@ func (t *test) run() { wantError := false wantAuto := false singlefilepkgs := false - setpkgpaths := false - localImports := true - f := strings.Fields(action) + f, err := splitQuoted(action) + if err != nil { + t.err = fmt.Errorf("invalid test recipe: %v", err) + return + } if len(f) > 0 { action = f[0] args = f[1:] @@ -569,6 +631,8 @@ func (t *test) run() { return } + goexp := env.GOEXPERIMENT + // collect flags for len(args) > 0 && strings.HasPrefix(args[0], "-") { switch args[0] { @@ -578,14 +642,6 @@ func (t *test) run() { wantError = false case "-s": singlefilepkgs = true - case "-P": - setpkgpaths = true - case "-n": - // Do not set relative path for local imports to current dir, - // e.g. do not pass -D . -I . to the compiler. - // Used in fixedbugs/bug345.go to allow compilation and import of local pkg. - // See golang.org/issue/25635 - localImports = false case "-t": // timeout in seconds args = args[1:] var err error @@ -593,9 +649,20 @@ func (t *test) run() { if err != nil { t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0]) } + if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { + timeoutScale, err := strconv.Atoi(s) + if err != nil { + log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err) + } + tim *= timeoutScale + } case "-goexperiment": // set GOEXPERIMENT environment args = args[1:] - runenv = append(runenv, "GOEXPERIMENT="+args[0]) + if goexp != "" { + goexp += "," + } + goexp += args[0] + runenv = append(runenv, "GOEXPERIMENT="+goexp) default: flags = append(flags, args[0]) @@ -616,6 +683,7 @@ func (t *test) run() { } } + t.initExpectFail() t.makeTempDir() if !*keep { defer os.RemoveAll(t.tempDir) @@ -652,6 +720,22 @@ func (t *test) run() { if tempDirIsGOPATH { cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir) } + // Put the bin directory of the GOROOT that built this program + // first in the path. This ensures that tests that use the "go" + // tool use the same one that built this program. This ensures + // that if you do "../bin/go run run.go" in this directory, all + // the tests that start subprocesses that "go tool compile" or + // whatever, use ../bin/go as their go tool, not whatever happens + // to be first in the user's path. + path := os.Getenv("PATH") + newdir := filepath.Join(runtime.GOROOT(), "bin") + if path != "" { + path = newdir + string(filepath.ListSeparator) + path + } else { + path = newdir + } + cmd.Env = append(cmd.Env, "PATH="+path) + cmd.Env = append(cmd.Env, runenv...) var err error @@ -659,6 +743,13 @@ func (t *test) run() { if tim != 0 { err = cmd.Start() // This command-timeout code adapted from cmd/go/test.go + // Note: the Go command uses a more sophisticated timeout + // strategy, first sending SIGQUIT (if appropriate for the + // OS in question) to try to trigger a stack trace, then + // finally much later SIGKILL. If timeouts prove to be a + // common problem here, it would be worth porting over + // that code as well. See https://do.dev/issue/50973 + // for more discussion. if err == nil { tick := time.NewTimer(time.Duration(tim) * time.Second) done := make(chan error) @@ -750,7 +841,7 @@ func (t *test) run() { // Fail if wantError is true and compilation was successful and vice versa. // Match errors produced by gc against errors in comments. // TODO(gri) remove need for -C (disable printing of columns in error messages) - cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-C", "-e", "-o", "a.o"} + cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-C", "-e", "-o", "a.o"} // No need to add -dynlink even if linkshared if we're just checking for errors... cmdline = append(cmdline, flags...) cmdline = append(cmdline, long) @@ -774,66 +865,6 @@ func (t *test) run() { t.updateErrors(string(out), long) } t.err = t.errorCheck(string(out), wantAuto, long, t.gofile) - if t.err != nil { - return // don't hide error if run below succeeds - } - - // The following is temporary scaffolding to get types2 typechecker - // up and running against the existing test cases. The explicitly - // listed files don't pass yet, usually because the error messages - // are slightly different (this list is not complete). Any errorcheck - // tests that require output from analysis phases past initial type- - // checking are also excluded since these phases are not running yet. - // We can get rid of this code once types2 is fully plugged in. - - // For now we're done when we can't handle the file or some of the flags. - // The first goal is to eliminate the excluded list; the second goal is to - // eliminate the flag list. - - // Excluded files. - filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows - if excluded[filename] { - if *verbose { - fmt.Printf("excl\t%s\n", filename) - } - return // cannot handle file yet - } - - // Excluded flags. - for _, flag := range flags { - for _, pattern := range []string{ - "-m", - } { - if strings.Contains(flag, pattern) { - if *verbose { - fmt.Printf("excl\t%s\t%s\n", filename, flags) - } - return // cannot handle flag - } - } - } - - // Run errorcheck again with -G option (new typechecker). - cmdline = []string{goTool(), "tool", "compile", "-G=3", "-C", "-e", "-o", "a.o"} - // No need to add -dynlink even if linkshared if we're just checking for errors... - cmdline = append(cmdline, flags...) - cmdline = append(cmdline, long) - out, err = runcmd(cmdline...) - if wantError { - if err == nil { - t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) - return - } - } else { - if err != nil { - t.err = err - return - } - } - if *updateErrors { - t.updateErrors(string(out), long) - } - t.err = t.errorCheck(string(out), wantAuto, long, t.gofile) case "compile": // Compile Go file. @@ -847,8 +878,8 @@ func (t *test) run() { t.err = err return } - for _, gofiles := range pkgs { - _, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...) + for _, pkg := range pkgs { + _, t.err = compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) if t.err != nil { return } @@ -871,8 +902,8 @@ func (t *test) run() { // Preceding pkg must return an error from compileInDir. errPkg-- } - for i, gofiles := range pkgs { - out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...) + for i, pkg := range pkgs { + out, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) if i == errPkg { if wantError && err == nil { t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out) @@ -886,7 +917,7 @@ func (t *test) run() { return } var fullshort []string - for _, name := range gofiles { + for _, name := range pkg.files { fullshort = append(fullshort, filepath.Join(longdir, name), name) } t.err = t.errorCheck(string(out), wantAuto, fullshort...) @@ -920,18 +951,8 @@ func (t *test) run() { } } - for i, gofiles := range pkgs { - pflags := []string{} - pflags = append(pflags, flags...) - if setpkgpaths { - fp := filepath.Join(longdir, gofiles[0]) - pkgname, err := getPackageNameFromSource(fp) - if err != nil { - log.Fatal(err) - } - pflags = append(pflags, "-p", pkgname) - } - _, err := compileInDir(runcmd, longdir, pflags, localImports, gofiles...) + for i, pkg := range pkgs { + _, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...) // Allow this package compilation fail based on conditions below; // its errors were checked in previous case. if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) { @@ -939,7 +960,7 @@ func (t *test) run() { return } if i == len(pkgs)-1 { - err = linkFile(runcmd, gofiles[0], ldflags) + err = linkFile(runcmd, pkg.files[0], ldflags) if err != nil { t.err = err return @@ -983,7 +1004,7 @@ func (t *test) run() { return } - cmd := []string{goTool(), "run", goGcflags()} + cmd := []string{goTool(), "run", t.goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -998,7 +1019,10 @@ func (t *test) run() { case "build": // Build Go file. - _, err := runcmd(goTool(), "build", goGcflags(), "-o", "a.exe", long) + cmd := []string{goTool(), "build", t.goGcflags()} + cmd = append(cmd, flags...) + cmd = append(cmd, "-o", "a.exe", long) + _, err := runcmd(cmd...) if err != nil { t.err = err } @@ -1029,7 +1053,7 @@ func (t *test) run() { t.err = fmt.Errorf("write empty go_asm.h: %s", err) return } - cmd := []string{goTool(), "tool", "asm", "-gensymabis", "-o", "symabis"} + cmd := []string{goTool(), "tool", "asm", "-p=main", "-gensymabis", "-o", "symabis"} cmd = append(cmd, asms...) _, err = runcmd(cmd...) if err != nil { @@ -1038,7 +1062,7 @@ func (t *test) run() { } } var objs []string - cmd := []string{goTool(), "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"} + cmd := []string{goTool(), "tool", "compile", "-p=main", "-e", "-D", ".", "-I", ".", "-o", "go.o"} if len(asms) > 0 { cmd = append(cmd, "-asmhdr", "go_asm.h", "-symabis", "symabis") } @@ -1050,7 +1074,7 @@ func (t *test) run() { } objs = append(objs, "go.o") if len(asms) > 0 { - cmd = []string{goTool(), "tool", "asm", "-e", "-I", ".", "-o", "asm.o"} + cmd = []string{goTool(), "tool", "asm", "-p=main", "-e", "-I", ".", "-o", "asm.o"} cmd = append(cmd, asms...) _, err = runcmd(cmd...) if err != nil { @@ -1086,7 +1110,7 @@ func (t *test) run() { // Build an executable from Go file, then run it, verify its output. // Useful for timeout tests where failure mode is infinite loop. // TODO: not supported on NaCl - cmd := []string{goTool(), "build", goGcflags(), "-o", "a.exe"} + cmd := []string{goTool(), "build", t.goGcflags(), "-o", "a.exe"} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -1114,7 +1138,7 @@ func (t *test) run() { runInDir = "" var out []byte var err error - if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS { + if len(flags)+len(args) == 0 && t.goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS && goexp == env.GOEXPERIMENT { // If we're not using special go command flags, // skip all the go command machinery. // This avoids any time the go command would @@ -1123,7 +1147,7 @@ func (t *test) run() { // Because we run lots of trivial test programs, // the time adds up. pkg := filepath.Join(t.tempDir, "pkg.a") - if _, err := runcmd(goTool(), "tool", "compile", "-o", pkg, t.goFileName()); err != nil { + if _, err := runcmd(goTool(), "tool", "compile", "-p=main", "-o", pkg, t.goFileName()); err != nil { t.err = err return } @@ -1136,7 +1160,7 @@ func (t *test) run() { } out, err = runcmd(append([]string{exe}, args...)...) } else { - cmd := []string{goTool(), "run", goGcflags()} + cmd := []string{goTool(), "run", t.goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -1158,7 +1182,7 @@ func (t *test) run() { <-rungatec }() runInDir = "" - cmd := []string{goTool(), "run", goGcflags()} + cmd := []string{goTool(), "run", t.goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -1173,7 +1197,7 @@ func (t *test) run() { t.err = fmt.Errorf("write tempfile:%s", err) return } - cmd = []string{goTool(), "run", goGcflags()} + cmd = []string{goTool(), "run", t.goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -1189,7 +1213,7 @@ func (t *test) run() { // Run Go file and write its output into temporary Go file. // Compile and errorCheck generated Go file. runInDir = "" - cmd := []string{goTool(), "run", goGcflags()} + cmd := []string{goTool(), "run", t.goGcflags()} if *linkshared { cmd = append(cmd, "-linkshared") } @@ -1205,7 +1229,7 @@ func (t *test) run() { t.err = fmt.Errorf("write tempfile:%s", err) return } - cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-e", "-o", "a.o"} + cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-e", "-o", "a.o"} cmdline = append(cmdline, flags...) cmdline = append(cmdline, tfile) out, err = runcmd(cmdline...) @@ -1255,6 +1279,10 @@ func (t *test) makeTempDir() { if *keep { log.Printf("Temporary directory is %s", t.tempDir) } + err = os.Mkdir(filepath.Join(t.tempDir, "test"), 0o755) + if err != nil { + log.Fatal(err) + } } // checkExpectedOutput compares the output from compiling and/or running with the contents @@ -1348,8 +1376,8 @@ func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (er // Assume errmsg says "file:line: foo". // Cut leading "file:line: " to avoid accidental matching of file name instead of message. text := errmsg - if i := strings.Index(text, " "); i >= 0 { - text = text[i+1:] + if _, suffix, ok := strings.Cut(text, " "); ok { + text = suffix } if we.re.MatchString(text) { matched = true @@ -1394,31 +1422,26 @@ func (t *test) updateErrors(out, file string) { } lines := strings.Split(string(src), "\n") // Remove old errors. - for i, ln := range lines { - pos := strings.Index(ln, " // ERROR ") - if pos >= 0 { - lines[i] = ln[:pos] - } + for i := range lines { + lines[i], _, _ = strings.Cut(lines[i], " // ERROR ") } // Parse new errors. errors := make(map[int]map[string]bool) tmpRe := regexp.MustCompile(`autotmp_[0-9]+`) for _, errStr := range splitOutput(out, false) { - colon1 := strings.Index(errStr, ":") - if colon1 < 0 || errStr[:colon1] != file { + errFile, rest, ok := strings.Cut(errStr, ":") + if !ok || errFile != file { continue } - colon2 := strings.Index(errStr[colon1+1:], ":") - if colon2 < 0 { + lineStr, msg, ok := strings.Cut(rest, ":") + if !ok { continue } - colon2 += colon1 + 1 - line, err := strconv.Atoi(errStr[colon1+1 : colon2]) + line, err := strconv.Atoi(lineStr) line-- if err != nil || line < 0 || line >= len(lines) { continue } - msg := errStr[colon2+2:] msg = strings.Replace(msg, file, base, -1) // normalize file mentions in error itself msg = strings.TrimLeft(msg, " \t") for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} { @@ -1585,15 +1608,17 @@ var ( // are the supported variants. archVariants = map[string][]string{ "386": {"GO386", "sse2", "softfloat"}, - "amd64": {}, + "amd64": {"GOAMD64", "v1", "v2", "v3", "v4"}, "arm": {"GOARM", "5", "6", "7"}, "arm64": {}, + "loong64": {}, "mips": {"GOMIPS", "hardfloat", "softfloat"}, "mips64": {"GOMIPS64", "hardfloat", "softfloat"}, "ppc64": {"GOPPC64", "power8", "power9"}, "ppc64le": {"GOPPC64", "power8", "power9"}, "s390x": {}, "wasm": {}, + "riscv64": {}, } ) @@ -1868,14 +1893,6 @@ func checkShouldTest() { assert(shouldTest("// +build !windows !plan9", "windows", "amd64")) } -func getenv(key, def string) string { - value := os.Getenv(key) - if value != "" { - return value - } - return def -} - // overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. func overlayDir(dstRoot, srcRoot string) error { dstRoot = filepath.Clean(dstRoot) @@ -1941,66 +1958,138 @@ func overlayDir(dstRoot, srcRoot string) error { }) } -// List of files that the compiler cannot errorcheck with the new typechecker (compiler -G option). -// Temporary scaffolding until we pass all the tests at which point this map can be removed. -var excluded = map[string]bool{ - "complit1.go": true, // types2 reports extra errors - "const2.go": true, // types2 not run after syntax errors - "ddd1.go": true, // issue #42987 - "directive.go": true, // misplaced compiler directive checks - "float_lit3.go": true, // types2 reports extra errors - "import1.go": true, // types2 reports extra errors - "import5.go": true, // issue #42988 - "import6.go": true, // issue #43109 - "initializerr.go": true, // types2 reports extra errors - "linkname2.go": true, // error reported by noder (not running for types2 errorcheck test) - "notinheap.go": true, // types2 doesn't report errors about conversions that are invalid due to //go:notinheap - "shift1.go": true, // issue #42989 - "typecheck.go": true, // invalid function is not causing errors when called - "writebarrier.go": true, // correct diagnostics, but different lines (probably irgen's fault) - - "fixedbugs/bug176.go": true, // types2 reports all errors (pref: types2) - "fixedbugs/bug195.go": true, // types2 reports slightly different (but correct) bugs - "fixedbugs/bug228.go": true, // types2 not run after syntax errors - "fixedbugs/bug231.go": true, // types2 bug? (same error reported twice) - "fixedbugs/bug255.go": true, // types2 reports extra errors - "fixedbugs/bug351.go": true, // types2 reports extra errors - "fixedbugs/bug374.go": true, // types2 reports extra errors - "fixedbugs/bug385_32.go": true, // types2 doesn't produce missing error "type .* too large" (32-bit specific) - "fixedbugs/bug388.go": true, // types2 not run due to syntax errors - "fixedbugs/bug412.go": true, // types2 produces a follow-on error - - "fixedbugs/issue11590.go": true, // types2 doesn't report a follow-on error (pref: types2) - "fixedbugs/issue11610.go": true, // types2 not run after syntax errors - "fixedbugs/issue11614.go": true, // types2 reports an extra error - "fixedbugs/issue13415.go": true, // declared but not used conflict - "fixedbugs/issue14520.go": true, // missing import path error by types2 - "fixedbugs/issue16428.go": true, // types2 reports two instead of one error - "fixedbugs/issue17038.go": true, // types2 doesn't report a follow-on error (pref: types2) - "fixedbugs/issue17645.go": true, // multiple errors on same line - "fixedbugs/issue18331.go": true, // missing error about misuse of //go:noescape (irgen needs code from noder) - "fixedbugs/issue18393.go": true, // types2 not run after syntax errors - "fixedbugs/issue19012.go": true, // multiple errors on same line - "fixedbugs/issue20233.go": true, // types2 reports two instead of one error (pref: compiler) - "fixedbugs/issue20245.go": true, // types2 reports two instead of one error (pref: compiler) - "fixedbugs/issue20250.go": true, // correct diagnostics, but different lines (probably irgen's fault) - "fixedbugs/issue21979.go": true, // types2 doesn't report a follow-on error (pref: types2) - "fixedbugs/issue23732.go": true, // types2 reports different (but ok) line numbers - "fixedbugs/issue25958.go": true, // types2 doesn't report a follow-on error (pref: types2) - "fixedbugs/issue28079b.go": true, // types2 reports follow-on errors - "fixedbugs/issue28268.go": true, // types2 reports follow-on errors - "fixedbugs/issue33460.go": true, // types2 reports alternative positions in separate error - "fixedbugs/issue41575.go": true, // types2 reports alternative positions in separate error - "fixedbugs/issue42058a.go": true, // types2 doesn't report "channel element type too large" - "fixedbugs/issue42058b.go": true, // types2 doesn't report "channel element type too large" - "fixedbugs/issue4232.go": true, // types2 reports (correct) extra errors - "fixedbugs/issue4452.go": true, // types2 reports (correct) extra errors - "fixedbugs/issue5609.go": true, // types2 needs a better error message - "fixedbugs/issue6889.go": true, // types2 can handle this without constant overflow - "fixedbugs/issue7525.go": true, // types2 reports init cycle error on different line - ok otherwise - "fixedbugs/issue7525b.go": true, // types2 reports init cycle error on different line - ok otherwise - "fixedbugs/issue7525c.go": true, // types2 reports init cycle error on different line - ok otherwise - "fixedbugs/issue7525d.go": true, // types2 reports init cycle error on different line - ok otherwise - "fixedbugs/issue7525e.go": true, // types2 reports init cycle error on different line - ok otherwise - "fixedbugs/issue46749.go": true, // types2 reports can not convert error instead of type mismatched +// The following sets of files are excluded from testing depending on configuration. +// The types2Failures(32Bit) files pass with the 1.17 compiler but don't pass with +// the 1.18 compiler using the new types2 type checker, or pass with sub-optimal +// error(s). + +// List of files that the compiler cannot errorcheck with the new typechecker (types2). +var types2Failures = setOf( + "shift1.go", // types2 reports two new errors which are probably not right + "fixedbugs/issue10700.go", // types2 should give hint about ptr to interface + "fixedbugs/issue18331.go", // missing error about misuse of //go:noescape (irgen needs code from noder) + "fixedbugs/issue18419.go", // types2 reports no field or method member, but should say unexported + "fixedbugs/issue20233.go", // types2 reports two instead of one error (preference: 1.17 compiler) + "fixedbugs/issue20245.go", // types2 reports two instead of one error (preference: 1.17 compiler) + "fixedbugs/issue31053.go", // types2 reports "unknown field" instead of "cannot refer to unexported field" + "fixedbugs/notinheap.go", // types2 doesn't report errors about conversions that are invalid due to //go:notinheap +) + +var types2Failures32Bit = setOf( + "printbig.go", // large untyped int passed to print (32-bit) + "fixedbugs/bug114.go", // large untyped int passed to println (32-bit) + "fixedbugs/issue23305.go", // large untyped int passed to println (32-bit) +) + +var go118Failures = setOf( + "fixedbugs/issue54343.go", // 1.18 compiler assigns receiver parameter to global variable + "typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics + "typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error + "typeparam/issue54456.go", // 1.18 compiler fails to distinguish local generic types + "typeparam/issue54497.go", // 1.18 compiler is more conservative about inlining due to repeated issues + "typeparam/mdempsky/16.go", // 1.18 compiler uses interface shape type in failed type assertions + "typeparam/mdempsky/17.go", // 1.18 compiler mishandles implicit conversions from range loops + "typeparam/mdempsky/18.go", // 1.18 compiler mishandles implicit conversions in select statements + "typeparam/mdempsky/20.go", // 1.18 compiler crashes on method expressions promoted to derived types +) + +// In all of these cases, the 1.17 compiler reports reasonable errors, but either the +// 1.17 or 1.18 compiler report extra errors, so we can't match correctly on both. We +// now set the patterns to match correctly on all the 1.18 errors. +// This list remains here just as a reference and for comparison - these files all pass. +var _ = setOf( + "import1.go", // types2 reports extra errors + "initializerr.go", // types2 reports extra error + "typecheck.go", // types2 reports extra error at function call + + "fixedbugs/bug176.go", // types2 reports all errors (pref: types2) + "fixedbugs/bug195.go", // types2 reports slight different errors, and an extra error + "fixedbugs/bug412.go", // types2 produces a follow-on error + + "fixedbugs/issue11614.go", // types2 reports an extra error + "fixedbugs/issue17038.go", // types2 doesn't report a follow-on error (pref: types2) + "fixedbugs/issue23732.go", // types2 reports different (but ok) line numbers + "fixedbugs/issue4510.go", // types2 reports different (but ok) line numbers + "fixedbugs/issue7525b.go", // types2 reports init cycle error on different line - ok otherwise + "fixedbugs/issue7525c.go", // types2 reports init cycle error on different line - ok otherwise + "fixedbugs/issue7525d.go", // types2 reports init cycle error on different line - ok otherwise + "fixedbugs/issue7525e.go", // types2 reports init cycle error on different line - ok otherwise + "fixedbugs/issue7525.go", // types2 reports init cycle error on different line - ok otherwise +) + +var unifiedFailures = setOf( + "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures + "escape4.go", // unified IR can inline f5 and f6; test doesn't expect this + + "typeparam/issue47631.go", // unified IR can handle local type declarations +) + +func setOf(keys ...string) map[string]bool { + m := make(map[string]bool, len(keys)) + for _, key := range keys { + m[key] = true + } + return m +} + +// splitQuoted splits the string s around each instance of one or more consecutive +// white space characters while taking into account quotes and escaping, and +// returns an array of substrings of s or an empty list if s contains only white space. +// Single quotes and double quotes are recognized to prevent splitting within the +// quoted region, and are removed from the resulting substrings. If a quote in s +// isn't closed err will be set and r will have the unclosed argument as the +// last element. The backslash is used for escaping. +// +// For example, the following string: +// +// a b:"c d" 'e''f' "g\"" +// +// Would be parsed as: +// +// []string{"a", "b:c d", "ef", `g"`} +// +// [copied from src/go/build/build.go] +func splitQuoted(s string) (r []string, err error) { + var args []string + arg := make([]rune, len(s)) + escaped := false + quoted := false + quote := '\x00' + i := 0 + for _, rune := range s { + switch { + case escaped: + escaped = false + case rune == '\\': + escaped = true + continue + case quote != '\x00': + if rune == quote { + quote = '\x00' + continue + } + case rune == '"' || rune == '\'': + quoted = true + quote = rune + continue + case unicode.IsSpace(rune): + if quoted || i > 0 { + quoted = false + args = append(args, string(arg[:i])) + i = 0 + } + continue + } + arg[i] = rune + i++ + } + if quoted || i > 0 { + args = append(args, string(arg[:i])) + } + if quote != 0 { + err = errors.New("unclosed quote") + } else if escaped { + err = errors.New("unfinished escaping") + } + return args, err } diff --git a/test/shift1.go b/test/shift1.go index d6a6c38839f560..0dae49a74dc590 100644 --- a/test/shift1.go +++ b/test/shift1.go @@ -25,7 +25,7 @@ var ( var ( e1 = g(2.0 << s) // ERROR "invalid|shift of non-integer operand" f1 = h(2 << s) // ERROR "invalid" - g1 int64 = 1.1 << s // ERROR "truncated" + g1 int64 = 1.1 << s // ERROR "truncated|must be integer" ) // constant shift expressions @@ -44,7 +44,7 @@ var ( b3 = 1< x: os.Exit(1) } + + // Unified IR converts the tag and all case values to empty + // interface, when any of the case values aren't assignable to the + // tag value's type. Make sure that `case nil:` compares against the + // tag type's nil value (i.e., `(*int)(nil)`), not nil interface + // (i.e., `any(nil)`). + switch (*int)(nil) { + case nil: + // ok + case any(nil): + assert(false, "case any(nil) matched") + default: + assert(false, "default matched") + } } diff --git a/test/switch2.go b/test/switch2.go index 11b85d3692cead..66e89fda19e2c3 100644 --- a/test/switch2.go +++ b/test/switch2.go @@ -11,11 +11,11 @@ package main func f() { switch { - case 0; // ERROR "expecting := or = or : or comma|expecting :" + case 0; // ERROR "expecting := or = or : or comma|expected :" } switch { - case 0; // ERROR "expecting := or = or : or comma|expecting :" + case 0; // ERROR "expecting := or = or : or comma|expected :" default: } @@ -34,6 +34,6 @@ func f() { } switch { - if x: // ERROR "expecting case or default or }" + if x: // ERROR "expected case or default or }" } } diff --git a/test/switch6.go b/test/switch6.go index 4f95d02615dbdb..b9d98003918160 100644 --- a/test/switch6.go +++ b/test/switch6.go @@ -15,7 +15,7 @@ package main // Verify that type switch statements with impossible cases are detected by the compiler. func f0(e error) { switch e.(type) { - case int: // ERROR "impossible type switch case: e \(type error\) cannot have dynamic type int \(missing Error method\)|impossible type assertion" + case int: // ERROR "impossible type switch case: (int\n\t)?e \(.*type error\) cannot have dynamic type int \(missing Error method\)" } } @@ -41,6 +41,6 @@ func (*X) Foo() {} func f2() { var i I switch i.(type) { - case X: // ERROR "impossible type switch case: i \(type I\) cannot have dynamic type X \(Foo method has pointer receiver\)|impossible type assertion" + case X: // ERROR "impossible type switch case: (X\n\t)?i \(.*type I\) cannot have dynamic type X \(Foo method has pointer receiver\)" } } diff --git a/test/syntax/composite.go b/test/syntax/composite.go index f891931b6c98e3..b4e03f3167a543 100644 --- a/test/syntax/composite.go +++ b/test/syntax/composite.go @@ -7,5 +7,5 @@ package main var a = []int{ - 3 // ERROR "need trailing comma before newline in composite literal|expecting comma or }" + 3 // ERROR "need trailing comma before newline in composite literal|possibly missing comma or }" } diff --git a/test/syntax/ddd.go b/test/syntax/ddd.go index 476ae22793d3ff..8d1e4d19035dcc 100644 --- a/test/syntax/ddd.go +++ b/test/syntax/ddd.go @@ -7,5 +7,5 @@ package main func f() { - g(f..3) // ERROR "unexpected literal \.3, expecting name or \(" + g(f..3) // ERROR "unexpected literal \.3, expected name or \(" } diff --git a/test/syntax/semi4.go b/test/syntax/semi4.go index 08c354751b6da1..62a511e6104e47 100644 --- a/test/syntax/semi4.go +++ b/test/syntax/semi4.go @@ -8,5 +8,5 @@ package main func main() { for x // GCCGO_ERROR "undefined" - { // ERROR "unexpected {, expecting for loop condition|expecting .*{.* after for clause" + { // ERROR "unexpected {, expected for loop condition|expecting .*{.* after for clause" z // GCCGO_ERROR "undefined" diff --git a/test/typecheck.go b/test/typecheck.go index 4c55d2edcb5015..f822ae98ef3237 100644 --- a/test/typecheck.go +++ b/test/typecheck.go @@ -17,6 +17,6 @@ func mine(int b) int { // ERROR "undefined.*b" } func main() { - mine() // GCCGO_ERROR "not enough arguments" + mine() // ERROR "not enough arguments" c = mine() // ERROR "undefined.*c|not enough arguments" } diff --git a/test/typeparam/absdiff.go b/test/typeparam/absdiff.go index 1381d7c92c90b5..9c83effbda4f2b 100644 --- a/test/typeparam/absdiff.go +++ b/test/typeparam/absdiff.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -6,16 +6,11 @@ package main -import ( - "fmt" - "math" -) - type Numeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - complex64, complex128 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~complex64 | ~complex128 } // numericAbs matches numeric types with an Abs method. @@ -33,66 +28,67 @@ func absDifference[T numericAbs[T]](a, b T) T { // orderedNumeric matches numeric types that support the < operator. type orderedNumeric interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 } // Complex matches the two complex types, which do not have a < operator. type Complex interface { - type complex64, complex128 -} - -// orderedAbs is a helper type that defines an Abs method for -// ordered numeric types. -type orderedAbs[T orderedNumeric] T - -func (a orderedAbs[T]) Abs() orderedAbs[T] { - // TODO(danscales): orderedAbs[T] conversion shouldn't be needed - if a < orderedAbs[T](0) { - return -a - } - return a + ~complex64 | ~complex128 } -// complexAbs is a helper type that defines an Abs method for -// complex types. -type complexAbs[T Complex] T - -func (a complexAbs[T]) Abs() complexAbs[T] { - r := float64(real(a)) - i := float64(imag(a)) - d := math.Sqrt(r * r + i * i) - return complexAbs[T](complex(d, 0)) -} - -// OrderedAbsDifference returns the absolute value of the difference -// between a and b, where a and b are of an ordered type. -func orderedAbsDifference[T orderedNumeric](a, b T) T { - return T(absDifference(orderedAbs[T](a), orderedAbs[T](b))) -} - -// ComplexAbsDifference returns the absolute value of the difference -// between a and b, where a and b are of a complex type. -func complexAbsDifference[T Complex](a, b T) T { - return T(absDifference(complexAbs[T](a), complexAbs[T](b))) -} +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // orderedAbs is a helper type that defines an Abs method for +// // ordered numeric types. +// type orderedAbs[T orderedNumeric] T +// +// func (a orderedAbs[T]) Abs() orderedAbs[T] { +// if a < 0 { +// return -a +// } +// return a +// } +// +// // complexAbs is a helper type that defines an Abs method for +// // complex types. +// type complexAbs[T Complex] T +// +// func (a complexAbs[T]) Abs() complexAbs[T] { +// r := float64(real(a)) +// i := float64(imag(a)) +// d := math.Sqrt(r*r + i*i) +// return complexAbs[T](complex(d, 0)) +// } +// +// // OrderedAbsDifference returns the absolute value of the difference +// // between a and b, where a and b are of an ordered type. +// func orderedAbsDifference[T orderedNumeric](a, b T) T { +// return T(absDifference(orderedAbs[T](a), orderedAbs[T](b))) +// } +// +// // ComplexAbsDifference returns the absolute value of the difference +// // between a and b, where a and b are of a complex type. +// func complexAbsDifference[T Complex](a, b T) T { +// return T(absDifference(complexAbs[T](a), complexAbs[T](b))) +// } func main() { - if got, want := orderedAbsDifference(1.0, -2.0), 3.0; got != want { - panic(fmt.Sprintf("got = %v, want = %v", got, want)) - } - if got, want := orderedAbsDifference(-1.0, 2.0), 3.0; got != want { - panic(fmt.Sprintf("got = %v, want = %v", got, want)) - } - if got, want := orderedAbsDifference(-20, 15), 35; got != want { - panic(fmt.Sprintf("got = %v, want = %v", got, want)) - } - - if got, want := complexAbsDifference(5.0 + 2.0i, 2.0 - 2.0i), 5+0i; got != want { - panic(fmt.Sprintf("got = %v, want = %v", got, want)) - } - if got, want := complexAbsDifference(2.0 - 2.0i, 5.0 + 2.0i), 5+0i; got != want { - panic(fmt.Sprintf("got = %v, want = %v", got, want)) - } + // // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). + // if got, want := orderedAbsDifference(1.0, -2.0), 3.0; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := orderedAbsDifference(-1.0, 2.0), 3.0; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := orderedAbsDifference(-20, 15), 35; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // + // if got, want := complexAbsDifference(5.0+2.0i, 2.0-2.0i), 5+0i; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := complexAbsDifference(2.0-2.0i, 5.0+2.0i), 5+0i; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } } diff --git a/test/typeparam/absdiff2.go b/test/typeparam/absdiff2.go new file mode 100644 index 00000000000000..87a1ec6de1aa6c --- /dev/null +++ b/test/typeparam/absdiff2.go @@ -0,0 +1,135 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// absdiff example in which an Abs method is attached to a generic type, which is a +// structure with a single field that may be a list of possible basic types. + +package main + +import ( + "fmt" + "math" +) + +type Numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~complex64 | ~complex128 +} + +// numericAbs matches a struct containing a numeric type that has an Abs method. +type numericAbs[T Numeric] interface { + ~struct{ Value_ T } + Abs() T + Value() T +} + +// absDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func absDifference[T Numeric, U numericAbs[T]](a, b U) T { + d := a.Value() - b.Value() + dt := U{Value_: d} + return dt.Abs() +} + +// orderedNumeric matches numeric types that support the < operator. +type orderedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 +} + +// Complex matches the two complex types, which do not have a < operator. +type Complex interface { + ~complex64 | ~complex128 +} + +// orderedAbs is a helper type that defines an Abs method for +// a struct containing an ordered numeric type. +type orderedAbs[T orderedNumeric] struct { + Value_ T +} + +func (a orderedAbs[T]) Abs() T { + if a.Value_ < 0 { + return -a.Value_ + } + return a.Value_ +} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. +// Use accessor method instead. + +func (a orderedAbs[T]) Value() T { + return a.Value_ +} + +// complexAbs is a helper type that defines an Abs method for +// a struct containing a complex type. +type complexAbs[T Complex] struct { + Value_ T +} + +func realimag(x any) (re, im float64) { + switch z := x.(type) { + case complex64: + re = float64(real(z)) + im = float64(imag(z)) + case complex128: + re = real(z) + im = imag(z) + default: + panic("unknown complex type") + } + return +} + +func (a complexAbs[T]) Abs() T { + // TODO use direct conversion instead of realimag once #50937 is fixed + r, i := realimag(a.Value_) + // r := float64(real(a.Value)) + // i := float64(imag(a.Value)) + d := math.Sqrt(r*r + i*i) + return T(complex(d, 0)) +} + +func (a complexAbs[T]) Value() T { + return a.Value_ +} + +// OrderedAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of an ordered type. +func OrderedAbsDifference[T orderedNumeric](a, b T) T { + return absDifference(orderedAbs[T]{a}, orderedAbs[T]{b}) +} + +// ComplexAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of a complex type. +func ComplexAbsDifference[T Complex](a, b T) T { + return absDifference(complexAbs[T]{a}, complexAbs[T]{b}) +} + +func main() { + if got, want := OrderedAbsDifference(1.0, -2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := OrderedAbsDifference(-1.0, 2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := OrderedAbsDifference(-20, 15), 35; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + + if got, want := ComplexAbsDifference(5.0+2.0i, 2.0-2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := ComplexAbsDifference(2.0-2.0i, 5.0+2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } +} diff --git a/test/typeparam/absdiff3.go b/test/typeparam/absdiff3.go new file mode 100644 index 00000000000000..c85cd1d6a552ec --- /dev/null +++ b/test/typeparam/absdiff3.go @@ -0,0 +1,98 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// absdiff example using a function argument rather than attaching an +// Abs method to a structure containing base types. + +package main + +import ( + "fmt" + "math" +) + +type Numeric interface { + OrderedNumeric | Complex +} + +// absDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the abs function. +func absDifference[T Numeric](a, b T, abs func(a T) T) T { + return abs(a - b) +} + +// OrderedNumeric matches numeric types that support the < operator. +type OrderedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 +} + +func Abs[T OrderedNumeric](a T) T { + if a < 0 { + return -a + } + return a +} + +// Complex matches the two complex types, which do not have a < operator. +type Complex interface { + ~complex64 | ~complex128 +} + +func realimag(x any) (re, im float64) { + switch z := x.(type) { + case complex64: + re = float64(real(z)) + im = float64(imag(z)) + case complex128: + re = real(z) + im = imag(z) + default: + panic("unknown complex type") + } + return +} + +func ComplexAbs[T Complex](a T) T { + // TODO use direct conversion instead of realimag once #50937 is fixed + r, i := realimag(a) + // r := float64(real(a)) + // i := float64(imag(a)) + d := math.Sqrt(r*r + i*i) + return T(complex(d, 0)) +} + +// OrderedAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of an ordered type. +func OrderedAbsDifference[T OrderedNumeric](a, b T) T { + return absDifference(a, b, Abs[T]) +} + +// ComplexAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of a complex type. +func ComplexAbsDifference[T Complex](a, b T) T { + return absDifference(a, b, ComplexAbs[T]) +} + +func main() { + if got, want := OrderedAbsDifference(1.0, -2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := OrderedAbsDifference(-1.0, 2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := OrderedAbsDifference(-20, 15), 35; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + + if got, want := ComplexAbsDifference(5.0+2.0i, 2.0-2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := ComplexAbsDifference(2.0-2.0i, 5.0+2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } +} diff --git a/test/typeparam/absdiffimp.dir/a.go b/test/typeparam/absdiffimp.dir/a.go new file mode 100644 index 00000000000000..60822fdb8bd869 --- /dev/null +++ b/test/typeparam/absdiffimp.dir/a.go @@ -0,0 +1,72 @@ +// Copyright 2021 The Go Authors. 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 Numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~complex64 | ~complex128 +} + +// numericAbs matches numeric types with an Abs method. +type numericAbs[T any] interface { + Numeric + Abs() T +} + +// AbsDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func absDifference[T numericAbs[T]](a, b T) T { + d := a - b + return d.Abs() +} + +// orderedNumeric matches numeric types that support the < operator. +type orderedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 +} + +// Complex matches the two complex types, which do not have a < operator. +type Complex interface { + ~complex64 | ~complex128 +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// // orderedAbs is a helper type that defines an Abs method for +// // ordered numeric types. +// type orderedAbs[T orderedNumeric] T +// +// func (a orderedAbs[T]) Abs() orderedAbs[T] { +// if a < 0 { +// return -a +// } +// return a +// } +// +// // complexAbs is a helper type that defines an Abs method for +// // complex types. +// type complexAbs[T Complex] T +// +// func (a complexAbs[T]) Abs() complexAbs[T] { +// r := float64(real(a)) +// i := float64(imag(a)) +// d := math.Sqrt(r*r + i*i) +// return complexAbs[T](complex(d, 0)) +// } +// +// // OrderedAbsDifference returns the absolute value of the difference +// // between a and b, where a and b are of an ordered type. +// func OrderedAbsDifference[T orderedNumeric](a, b T) T { +// return T(absDifference(orderedAbs[T](a), orderedAbs[T](b))) +// } +// +// // ComplexAbsDifference returns the absolute value of the difference +// // between a and b, where a and b are of a complex type. +// func ComplexAbsDifference[T Complex](a, b T) T { +// return T(absDifference(complexAbs[T](a), complexAbs[T](b))) +// } diff --git a/test/typeparam/absdiffimp.dir/main.go b/test/typeparam/absdiffimp.dir/main.go new file mode 100644 index 00000000000000..c648013327d345 --- /dev/null +++ b/test/typeparam/absdiffimp.dir/main.go @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). + // if got, want := a.OrderedAbsDifference(1.0, -2.0), 3.0; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := a.OrderedAbsDifference(-1.0, 2.0), 3.0; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := a.OrderedAbsDifference(-20, 15), 35; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // + // if got, want := a.ComplexAbsDifference(5.0+2.0i, 2.0-2.0i), 5+0i; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } + // if got, want := a.ComplexAbsDifference(2.0-2.0i, 5.0+2.0i), 5+0i; got != want { + // panic(fmt.Sprintf("got = %v, want = %v", got, want)) + // } +} diff --git a/test/typeparam/absdiffimp.go b/test/typeparam/absdiffimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/absdiffimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/absdiffimp2.dir/a.go b/test/typeparam/absdiffimp2.dir/a.go new file mode 100644 index 00000000000000..dc64f2dcbed759 --- /dev/null +++ b/test/typeparam/absdiffimp2.dir/a.go @@ -0,0 +1,110 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "math" +) + +type Numeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~complex64 | ~complex128 +} + +// numericAbs matches a struct containing a numeric type that has an Abs method. +type numericAbs[T Numeric] interface { + ~struct{ Value_ T } + Abs() T + Value() T +} + +// absDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func absDifference[T Numeric, U numericAbs[T]](a, b U) T { + d := a.Value() - b.Value() + dt := U{Value_: d} + return dt.Abs() +} + +// orderedNumeric matches numeric types that support the < operator. +type orderedNumeric interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 +} + +// Complex matches the two complex types, which do not have a < operator. +type Complex interface { + ~complex64 | ~complex128 +} + +// orderedAbs is a helper type that defines an Abs method for +// a struct containing an ordered numeric type. +type orderedAbs[T orderedNumeric] struct { + Value_ T +} + +func (a orderedAbs[T]) Abs() T { + if a.Value_ < 0 { + return -a.Value_ + } + return a.Value_ +} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. +// Use accessor method instead. + +func (a orderedAbs[T]) Value() T { + return a.Value_ +} + +// complexAbs is a helper type that defines an Abs method for +// a struct containing a complex type. +type complexAbs[T Complex] struct { + Value_ T +} + +func realimag(x any) (re, im float64) { + switch z := x.(type) { + case complex64: + re = float64(real(z)) + im = float64(imag(z)) + case complex128: + re = real(z) + im = imag(z) + default: + panic("unknown complex type") + } + return +} + +func (a complexAbs[T]) Abs() T { + // TODO use direct conversion instead of realimag once #50937 is fixed + r, i := realimag(a.Value_) + // r := float64(real(a.Value)) + // i := float64(imag(a.Value)) + d := math.Sqrt(r*r + i*i) + return T(complex(d, 0)) +} + +func (a complexAbs[T]) Value() T { + return a.Value_ +} + +// OrderedAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of an ordered type. +func OrderedAbsDifference[T orderedNumeric](a, b T) T { + return absDifference(orderedAbs[T]{a}, orderedAbs[T]{b}) +} + +// ComplexAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of a complex type. +func ComplexAbsDifference[T Complex](a, b T) T { + return absDifference(complexAbs[T]{a}, complexAbs[T]{b}) +} diff --git a/test/typeparam/absdiffimp2.dir/main.go b/test/typeparam/absdiffimp2.dir/main.go new file mode 100644 index 00000000000000..1519da091bcbd4 --- /dev/null +++ b/test/typeparam/absdiffimp2.dir/main.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + if got, want := a.OrderedAbsDifference(1.0, -2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := a.OrderedAbsDifference(-1.0, 2.0), 3.0; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := a.OrderedAbsDifference(-20, 15), 35; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + + if got, want := a.ComplexAbsDifference(5.0+2.0i, 2.0-2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } + if got, want := a.ComplexAbsDifference(2.0-2.0i, 5.0+2.0i), 5+0i; got != want { + panic(fmt.Sprintf("got = %v, want = %v", got, want)) + } +} diff --git a/test/typeparam/absdiffimp2.go b/test/typeparam/absdiffimp2.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/absdiffimp2.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/adder.go b/test/typeparam/adder.go index 0c25ad4ef2d1c3..fbb492514c3c1d 100644 --- a/test/typeparam/adder.go +++ b/test/typeparam/adder.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,19 +11,19 @@ import ( ) type AddType interface { - type int, int64, string + int | int64 | string } -// _Add can add numbers or strings -func _Add[T AddType](a, b T) T { +// Add can add numbers or strings +func Add[T AddType](a, b T) T { return a + b } func main() { - if got, want := _Add(5, 3), 8; got != want { + if got, want := Add(5, 3), 8; got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } - if got, want := _Add("ab", "cd"), "abcd"; got != want { + if got, want := Add("ab", "cd"), "abcd"; got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } } diff --git a/test/typeparam/aliasimp.dir/a.go b/test/typeparam/aliasimp.dir/a.go new file mode 100644 index 00000000000000..c64e87c10fa2fd --- /dev/null +++ b/test/typeparam/aliasimp.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type Rimp[T any] struct { + F T +} diff --git a/test/typeparam/aliasimp.dir/main.go b/test/typeparam/aliasimp.dir/main.go new file mode 100644 index 00000000000000..39c29fc74cdbe2 --- /dev/null +++ b/test/typeparam/aliasimp.dir/main.go @@ -0,0 +1,41 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +type R[T any] struct { + F T +} + +// type S = R // disallowed for now + +type Sint = R[int] + +// type Simp = a.Rimp // disallowed for now + +// type SimpString Simp[string] // disallowed for now +type SimpString a.Rimp[string] + +func main() { + // var s S[int] // disallowed for now + var s R[int] + if s.F != 0 { + panic(s.F) + } + var s2 Sint + if s2.F != 0 { + panic(s2.F) + } + // var s3 Simp[string] // disallowed for now + var s3 a.Rimp[string] + if s3.F != "" { + panic(s3.F) + } + var s4 SimpString + if s4.F != "" { + panic(s4.F) + } +} diff --git a/test/typeparam/aliasimp.go b/test/typeparam/aliasimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/aliasimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/append.go b/test/typeparam/append.go index 8b9bc2039fc310..61682625c1d4db 100644 --- a/test/typeparam/append.go +++ b/test/typeparam/append.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -9,7 +9,7 @@ package main type Recv <-chan int type sliceOf[E any] interface { - type []E + ~[]E } func _Append[S sliceOf[T], T any](s S, t ...T) S { diff --git a/test/typeparam/boundmethod.go b/test/typeparam/boundmethod.go new file mode 100644 index 00000000000000..510519a2742d3f --- /dev/null +++ b/test/typeparam/boundmethod.go @@ -0,0 +1,108 @@ +// run + +// Copyright 2021 The Go 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 test illustrates how a type bound method (String below) can be implemented +// either by a concrete type (myint below) or a instantiated generic type +// (StringInt[myint] below). + +package main + +import ( + "fmt" + "reflect" + "strconv" +) + +type myint int + +//go:noinline +func (m myint) String() string { + return strconv.Itoa(int(m)) +} + +type Stringer interface { + String() string +} + +func stringify[T Stringer](s []T) (ret []string) { + for _, v := range s { + // Test normal bounds method call on type param + x1 := v.String() + + // Test converting type param to its bound interface first + v1 := Stringer(v) + x2 := v1.String() + + // Test method expression with type param type + f1 := T.String + x3 := f1(v) + + // Test creating and calling closure equivalent to the method expression + f2 := func(v1 T) string { + return Stringer(v1).String() + } + x4 := f2(v) + + if x1 != x2 || x2 != x3 || x3 != x4 { + panic(fmt.Sprintf("Mismatched values %v, %v, %v, %v\n", x1, x2, x3, x4)) + } + + ret = append(ret, v.String()) + } + return ret +} + +type Ints interface { + ~int32 | ~int +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type StringInt[T Ints] T +// +// //go:noinline +// func (m StringInt[T]) String() string { +// return strconv.Itoa(int(m)) +// } + +type StringStruct[T Ints] struct { + f T +} + +func (m StringStruct[T]) String() string { + return strconv.Itoa(int(m.f)) +} + +func main() { + x := []myint{myint(1), myint(2), myint(3)} + + // stringify on a normal type, whose bound method is associated with the base type. + got := stringify(x) + want := []string{"1", "2", "3"} + if !reflect.DeepEqual(got, want) { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } + + // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). + // x2 := []StringInt[myint]{StringInt[myint](5), StringInt[myint](7), StringInt[myint](6)} + // + // // stringify on an instantiated type, whose bound method is associated with + // // the generic type StringInt[T], which maps directly to T. + // got2 := stringify(x2) + // want2 := []string{"5", "7", "6"} + // if !reflect.DeepEqual(got2, want2) { + // panic(fmt.Sprintf("got %s, want %s", got2, want2)) + // } + + // stringify on an instantiated type, whose bound method is associated with + // the generic type StringStruct[T], which maps to a struct containing T. + x3 := []StringStruct[myint]{StringStruct[myint]{f: 11}, StringStruct[myint]{f: 10}, StringStruct[myint]{f: 9}} + + got3 := stringify(x3) + want3 := []string{"11", "10", "9"} + if !reflect.DeepEqual(got3, want3) { + panic(fmt.Sprintf("got %s, want %s", got3, want3)) + } +} diff --git a/test/typeparam/builtins.go b/test/typeparam/builtins.go new file mode 100644 index 00000000000000..763d7202d01a39 --- /dev/null +++ b/test/typeparam/builtins.go @@ -0,0 +1,112 @@ +// compile + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file tests built-in calls on generic types. + +// derived and expanded from cmd/compile/internal/types2/testdata/check/builtins.go2 + +package builtins + +// close + +type C0 interface{ int } +type C1 interface{ chan int } +type C2 interface{ chan int | <-chan int } +type C3 interface{ chan int | chan float32 } +type C4 interface{ chan int | chan<- int } +type C5[T any] interface{ ~chan T | chan<- T } + +func f1[T C1](ch T) { + close(ch) +} + +func f2[T C3](ch T) { + close(ch) +} + +func f3[T C4](ch T) { + close(ch) +} + +func f4[T C5[X], X any](ch T) { + close(ch) +} + +// delete + +type M0 interface{ int } +type M1 interface{ map[string]int } +type M2 interface { + map[string]int | map[string]float64 +} +type M3 interface{ map[string]int | map[rune]int } +type M4[K comparable, V any] interface{ map[K]V | map[rune]V } + +func g1[T M1](m T) { + delete(m, "foo") +} + +func g2[T M2](m T) { + delete(m, "foo") +} + +func g3[T M4[rune, V], V any](m T) { + delete(m, 'k') +} + +// make + +func m1[ + S1 interface{ []int }, + S2 interface{ []int | chan int }, + + M1 interface{ map[string]int }, + M2 interface{ map[string]int | chan int }, + + C1 interface{ chan int }, + C2 interface{ chan int | chan string }, +]() { + _ = make([]int, 10) + _ = make(m1S0, 10) + _ = make(S1, 10) + _ = make(S1, 10, 20) + + _ = make(map[string]int) + _ = make(m1M0) + _ = make(M1) + _ = make(M1, 10) + + _ = make(chan int) + _ = make(m1C0) + _ = make(C1) + _ = make(C1, 10) +} +// TODO: put these type declarations back inside m1 when issue 47631 is fixed. +type m1S0 []int +type m1M0 map[string]int +type m1C0 chan int + +// len/cap + +type Slice[T any] interface { + []T +} + +func c1[T any, S Slice[T]]() { + x := make(S, 5, 10) + _ = len(x) + _ = cap(x) +} + +// append + +func a1[T any, S Slice[T]]() { + x := make(S, 5) + y := make(S, 2) + var z T + _ = append(x, y...) + _ = append(x, z) +} diff --git a/test/typeparam/chans.go b/test/typeparam/chans.go index 2fcd4af75e544d..d73ce6e5316735 100644 --- a/test/typeparam/chans.go +++ b/test/typeparam/chans.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -183,7 +183,7 @@ func _Ranger[Elem any]() (*_Sender[Elem], *_Receiver[Elem]) { values: c, done: d, } - r := &_Receiver[Elem] { + r := &_Receiver[Elem]{ values: c, done: d, } diff --git a/test/typeparam/chansimp.dir/a.go b/test/typeparam/chansimp.dir/a.go new file mode 100644 index 00000000000000..73219927041c02 --- /dev/null +++ b/test/typeparam/chansimp.dir/a.go @@ -0,0 +1,232 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "context" + "runtime" +) + +// Equal reports whether two slices are equal: the same length and all +// elements equal. All floating point NaNs are considered equal. +func SliceEqual[Elem comparable](s1, s2 []Elem) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if v1 != v2 { + isNaN := func(f Elem) bool { return f != f } + if !isNaN(v1) || !isNaN(v2) { + return false + } + } + } + return true +} + +// ReadAll reads from c until the channel is closed or the context is +// canceled, returning all the values read. +func ReadAll[Elem any](ctx context.Context, c <-chan Elem) []Elem { + var r []Elem + for { + select { + case <-ctx.Done(): + return r + case v, ok := <-c: + if !ok { + return r + } + r = append(r, v) + } + } +} + +// Merge merges two channels into a single channel. +// This will leave a goroutine running until either both channels are closed +// or the context is canceled, at which point the returned channel is closed. +func Merge[Elem any](ctx context.Context, c1, c2 <-chan Elem) <-chan Elem { + r := make(chan Elem) + go func(ctx context.Context, c1, c2 <-chan Elem, r chan<- Elem) { + defer close(r) + for c1 != nil || c2 != nil { + select { + case <-ctx.Done(): + return + case v1, ok := <-c1: + if ok { + r <- v1 + } else { + c1 = nil + } + case v2, ok := <-c2: + if ok { + r <- v2 + } else { + c2 = nil + } + } + } + }(ctx, c1, c2, r) + return r +} + +// Filter calls f on each value read from c. If f returns true the value +// is sent on the returned channel. This will leave a goroutine running +// until c is closed or the context is canceled, at which point the +// returned channel is closed. +func Filter[Elem any](ctx context.Context, c <-chan Elem, f func(Elem) bool) <-chan Elem { + r := make(chan Elem) + go func(ctx context.Context, c <-chan Elem, f func(Elem) bool, r chan<- Elem) { + defer close(r) + for { + select { + case <-ctx.Done(): + return + case v, ok := <-c: + if !ok { + return + } + if f(v) { + r <- v + } + } + } + }(ctx, c, f, r) + return r +} + +// Sink returns a channel that discards all values sent to it. +// This will leave a goroutine running until the context is canceled +// or the returned channel is closed. +func Sink[Elem any](ctx context.Context) chan<- Elem { + r := make(chan Elem) + go func(ctx context.Context, r <-chan Elem) { + for { + select { + case <-ctx.Done(): + return + case _, ok := <-r: + if !ok { + return + } + } + } + }(ctx, r) + return r +} + +// An Exclusive is a value that may only be used by a single goroutine +// at a time. This is implemented using channels rather than a mutex. +type Exclusive[Val any] struct { + c chan Val +} + +// MakeExclusive makes an initialized exclusive value. +func MakeExclusive[Val any](initial Val) *Exclusive[Val] { + r := &Exclusive[Val]{ + c: make(chan Val, 1), + } + r.c <- initial + return r +} + +// Acquire acquires the exclusive value for private use. +// It must be released using the Release method. +func (e *Exclusive[Val]) Acquire() Val { + return <-e.c +} + +// TryAcquire attempts to acquire the value. The ok result reports whether +// the value was acquired. If the value is acquired, it must be released +// using the Release method. +func (e *Exclusive[Val]) TryAcquire() (v Val, ok bool) { + select { + case r := <-e.c: + return r, true + default: + return v, false + } +} + +// Release updates and releases the value. +// This method panics if the value has not been acquired. +func (e *Exclusive[Val]) Release(v Val) { + select { + case e.c <- v: + default: + panic("Exclusive Release without Acquire") + } +} + +// Ranger returns a Sender and a Receiver. The Receiver provides a +// Next method to retrieve values. The Sender provides a Send method +// to send values and a Close method to stop sending values. The Next +// method indicates when the Sender has been closed, and the Send +// method indicates when the Receiver has been freed. +// +// This is a convenient way to exit a goroutine sending values when +// the receiver stops reading them. +func Ranger[Elem any]() (*Sender[Elem], *Receiver[Elem]) { + c := make(chan Elem) + d := make(chan struct{}) + s := &Sender[Elem]{ + values: c, + done: d, + } + r := &Receiver[Elem]{ + values: c, + done: d, + } + runtime.SetFinalizer(r, (*Receiver[Elem]).finalize) + return s, r +} + +// A Sender is used to send values to a Receiver. +type Sender[Elem any] struct { + values chan<- Elem + done <-chan struct{} +} + +// Send sends a value to the receiver. It reports whether the value was sent. +// The value will not be sent if the context is closed or the receiver +// is freed. +func (s *Sender[Elem]) Send(ctx context.Context, v Elem) bool { + select { + case <-ctx.Done(): + return false + case s.values <- v: + return true + case <-s.done: + return false + } +} + +// Close tells the receiver that no more values will arrive. +// After Close is called, the Sender may no longer be used. +func (s *Sender[Elem]) Close() { + close(s.values) +} + +// A Receiver receives values from a Sender. +type Receiver[Elem any] struct { + values <-chan Elem + done chan<- struct{} +} + +// Next returns the next value from the channel. The bool result indicates +// whether the value is valid. +func (r *Receiver[Elem]) Next(ctx context.Context) (v Elem, ok bool) { + select { + case <-ctx.Done(): + case v, ok = <-r.values: + } + return v, ok +} + +// finalize is a finalizer for the receiver. +func (r *Receiver[Elem]) finalize() { + close(r.done) +} diff --git a/test/typeparam/chansimp.dir/main.go b/test/typeparam/chansimp.dir/main.go new file mode 100644 index 00000000000000..a380a3c7e4472a --- /dev/null +++ b/test/typeparam/chansimp.dir/main.go @@ -0,0 +1,189 @@ +// Copyright 2021 The Go Authors. 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" + "context" + "fmt" + "runtime" + "sort" + "sync" + "time" +) + +func TestReadAll() { + c := make(chan int) + go func() { + c <- 4 + c <- 2 + c <- 5 + close(c) + }() + got := a.ReadAll(context.Background(), c) + want := []int{4, 2, 5} + if !a.SliceEqual(got, want) { + panic(fmt.Sprintf("ReadAll returned %v, want %v", got, want)) + } +} + +func TestMerge() { + c1 := make(chan int) + c2 := make(chan int) + go func() { + c1 <- 1 + c1 <- 3 + c1 <- 5 + close(c1) + }() + go func() { + c2 <- 2 + c2 <- 4 + c2 <- 6 + close(c2) + }() + ctx := context.Background() + got := a.ReadAll(ctx, a.Merge(ctx, c1, c2)) + sort.Ints(got) + want := []int{1, 2, 3, 4, 5, 6} + if !a.SliceEqual(got, want) { + panic(fmt.Sprintf("Merge returned %v, want %v", got, want)) + } +} + +func TestFilter() { + c := make(chan int) + go func() { + c <- 1 + c <- 2 + c <- 3 + close(c) + }() + even := func(i int) bool { return i%2 == 0 } + ctx := context.Background() + got := a.ReadAll(ctx, a.Filter(ctx, c, even)) + want := []int{2} + if !a.SliceEqual(got, want) { + panic(fmt.Sprintf("Filter returned %v, want %v", got, want)) + } +} + +func TestSink() { + c := a.Sink[int](context.Background()) + after := time.NewTimer(time.Minute) + defer after.Stop() + send := func(v int) { + select { + case c <- v: + case <-after.C: + panic("timed out sending to Sink") + } + } + send(1) + send(2) + send(3) + close(c) +} + +func TestExclusive() { + val := 0 + ex := a.MakeExclusive(&val) + + var wg sync.WaitGroup + f := func() { + defer wg.Done() + for i := 0; i < 10; i++ { + p := ex.Acquire() + (*p)++ + ex.Release(p) + } + } + + wg.Add(2) + go f() + go f() + + wg.Wait() + if val != 20 { + panic(fmt.Sprintf("after Acquire/Release loop got %d, want 20", val)) + } +} + +func TestExclusiveTry() { + s := "" + ex := a.MakeExclusive(&s) + p, ok := ex.TryAcquire() + if !ok { + panic("TryAcquire failed") + } + *p = "a" + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + _, ok := ex.TryAcquire() + if ok { + panic(fmt.Sprintf("TryAcquire succeeded unexpectedly")) + } + }() + wg.Wait() + + ex.Release(p) + + p, ok = ex.TryAcquire() + if !ok { + panic(fmt.Sprintf("TryAcquire failed")) + } +} + +func TestRanger() { + s, r := a.Ranger[int]() + + ctx := context.Background() + go func() { + // Receive one value then exit. + v, ok := r.Next(ctx) + if !ok { + panic(fmt.Sprintf("did not receive any values")) + } else if v != 1 { + panic(fmt.Sprintf("received %d, want 1", v)) + } + }() + + c1 := make(chan bool) + c2 := make(chan bool) + go func() { + defer close(c2) + if !s.Send(ctx, 1) { + panic(fmt.Sprintf("Send failed unexpectedly")) + } + close(c1) + if s.Send(ctx, 2) { + panic(fmt.Sprintf("Send succeeded unexpectedly")) + } + }() + + <-c1 + + // Force a garbage collection to try to get the finalizers to run. + runtime.GC() + + select { + case <-c2: + case <-time.After(time.Minute): + panic("Ranger Send should have failed, but timed out") + } +} + +func main() { + TestReadAll() + TestMerge() + TestFilter() + TestSink() + TestExclusive() + TestExclusiveTry() + TestRanger() +} diff --git a/test/typeparam/chansimp.go b/test/typeparam/chansimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/chansimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/combine.go b/test/typeparam/combine.go index d4a2988a7b0e46..361708f1ebb1df 100644 --- a/test/typeparam/combine.go +++ b/test/typeparam/combine.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -10,56 +10,56 @@ import ( "fmt" ) -type _Gen[A any] func() (A, bool) +type Gen[A any] func() (A, bool) -func combine[T1, T2, T any](g1 _Gen[T1], g2 _Gen[T2], join func(T1, T2) T) _Gen[T] { - return func() (T, bool) { - var t T - t1, ok := g1() - if !ok { - return t, false - } - t2, ok := g2() - if !ok { - return t, false - } - return join(t1, t2), true - } +func Combine[T1, T2, T any](g1 Gen[T1], g2 Gen[T2], join func(T1, T2) T) Gen[T] { + return func() (T, bool) { + var t T + t1, ok := g1() + if !ok { + return t, false + } + t2, ok := g2() + if !ok { + return t, false + } + return join(t1, t2), true + } } -type _Pair[A, B any] struct { +type Pair[A, B any] struct { A A B B } -func _NewPair[A, B any](a A, b B) _Pair[A, B] { - return _Pair[A, B]{a, b} +func _NewPair[A, B any](a A, b B) Pair[A, B] { + return Pair[A, B]{a, b} } -func _Combine2[A, B any](ga _Gen[A], gb _Gen[B]) _Gen[_Pair[A, B]] { - return combine(ga, gb, _NewPair[A, B]) +func Combine2[A, B any](ga Gen[A], gb Gen[B]) Gen[Pair[A, B]] { + return Combine(ga, gb, _NewPair[A, B]) } func main() { - var g1 _Gen[int] = func() (int, bool) { return 3, true } - var g2 _Gen[string] = func() (string, bool) { return "x", false } - var g3 _Gen[string] = func() (string, bool) { return "y", true } + var g1 Gen[int] = func() (int, bool) { return 3, true } + var g2 Gen[string] = func() (string, bool) { return "x", false } + var g3 Gen[string] = func() (string, bool) { return "y", true } - gc := combine(g1, g2, _NewPair[int, string]) + gc := Combine(g1, g2, _NewPair[int, string]) if got, ok := gc(); ok { panic(fmt.Sprintf("got %v, %v, wanted -/false", got, ok)) } - gc2 := _Combine2(g1, g2) + gc2 := Combine2(g1, g2) if got, ok := gc2(); ok { panic(fmt.Sprintf("got %v, %v, wanted -/false", got, ok)) } - gc3 := combine(g1, g3, _NewPair[int, string]) + gc3 := Combine(g1, g3, _NewPair[int, string]) if got, ok := gc3(); !ok || got.A != 3 || got.B != "y" { panic(fmt.Sprintf("got %v, %v, wanted {3, y}, true", got, ok)) } - gc4 := _Combine2(g1, g3) + gc4 := Combine2(g1, g3) if got, ok := gc4(); !ok || got.A != 3 || got.B != "y" { - panic (fmt.Sprintf("got %v, %v, wanted {3, y}, true", got, ok)) + panic(fmt.Sprintf("got %v, %v, wanted {3, y}, true", got, ok)) } } diff --git a/test/typeparam/cons.go b/test/typeparam/cons.go index 8d255ebdb83ea8..733e5793b16dbc 100644 --- a/test/typeparam/cons.go +++ b/test/typeparam/cons.go @@ -1,8 +1,8 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style -// lice +// license that can be found in the LICENSE file. package main @@ -12,7 +12,7 @@ import "fmt" // argument type any interface{} -type _Function[a, b any] interface { +type Function[a, b any] interface { Apply(x a) b } @@ -29,8 +29,8 @@ func (this pos) Apply(x int) bool { } type compose[a, b, c any] struct { - f _Function[a, b] - g _Function[b, c] + f Function[a, b] + g Function[b, c] } func (this compose[a, b, c]) Apply(x a) c { @@ -47,52 +47,52 @@ func (this Int) Equal(that int) bool { return int(this) == that } -type _List[a any] interface { - Match(casenil _Function[_Nil[a], any], casecons _Function[_Cons[a], any]) any +type List[a any] interface { + Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any } -type _Nil[a any] struct{ +type Nil[a any] struct { } -func (xs _Nil[a]) Match(casenil _Function[_Nil[a], any], casecons _Function[_Cons[a], any]) any { +func (xs Nil[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { return casenil.Apply(xs) } -type _Cons[a any] struct { +type Cons[a any] struct { Head a - Tail _List[a] + Tail List[a] } -func (xs _Cons[a]) Match(casenil _Function[_Nil[a], any], casecons _Function[_Cons[a], any]) any { +func (xs Cons[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { return casecons.Apply(xs) } -type mapNil[a, b any] struct{ +type mapNil[a, b any] struct { } -func (m mapNil[a, b]) Apply(_ _Nil[a]) any { - return _Nil[b]{} +func (m mapNil[a, b]) Apply(_ Nil[a]) any { + return Nil[b]{} } type mapCons[a, b any] struct { - f _Function[a, b] + f Function[a, b] } -func (m mapCons[a, b]) Apply(xs _Cons[a]) any { - return _Cons[b]{m.f.Apply(xs.Head), _Map[a, b](m.f, xs.Tail)} +func (m mapCons[a, b]) Apply(xs Cons[a]) any { + return Cons[b]{m.f.Apply(xs.Head), Map[a, b](m.f, xs.Tail)} } -func _Map[a, b any](f _Function[a, b], xs _List[a]) _List[b] { - return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(_List[b]) +func Map[a, b any](f Function[a, b], xs List[a]) List[b] { + return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b]) } func main() { - var xs _List[int] = _Cons[int]{3, _Cons[int]{6, _Nil[int]{}}} - var ys _List[int] = _Map[int, int](incr{-5}, xs) - var xz _List[bool] = _Map[int, bool](pos{}, ys) - cs1 := xz.(_Cons[bool]) - cs2 := cs1.Tail.(_Cons[bool]) - _, ok := cs2.Tail.(_Nil[bool]) + var xs List[int] = Cons[int]{3, Cons[int]{6, Nil[int]{}}} + var ys List[int] = Map[int, int](incr{-5}, xs) + var xz List[bool] = Map[int, bool](pos{}, ys) + cs1 := xz.(Cons[bool]) + cs2 := cs1.Tail.(Cons[bool]) + _, ok := cs2.Tail.(Nil[bool]) if cs1.Head != false || cs2.Head != true || !ok { panic(fmt.Sprintf("got %v, %v, %v, expected false, true, true", cs1.Head, cs2.Head, ok)) diff --git a/test/typeparam/dedup.dir/a.go b/test/typeparam/dedup.dir/a.go new file mode 100644 index 00000000000000..f5cb6dc7623c86 --- /dev/null +++ b/test/typeparam/dedup.dir/a.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. 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 + +//go:noinline +func F[T comparable](a, b T) bool { + return a == b +} diff --git a/test/typeparam/dedup.dir/b.go b/test/typeparam/dedup.dir/b.go new file mode 100644 index 00000000000000..8507c6413770c9 --- /dev/null +++ b/test/typeparam/dedup.dir/b.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. 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 B() { + var x int64 + println(a.F(&x, &x)) + var y int32 + println(a.F(&y, &y)) +} diff --git a/test/typeparam/dedup.dir/c.go b/test/typeparam/dedup.dir/c.go new file mode 100644 index 00000000000000..a1c950f1cb876d --- /dev/null +++ b/test/typeparam/dedup.dir/c.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package c + +import "./a" + +func C() { + var x int64 + println(a.F(&x, &x)) + var y int32 + println(a.F(&y, &y)) +} diff --git a/test/typeparam/dedup.dir/main.go b/test/typeparam/dedup.dir/main.go new file mode 100644 index 00000000000000..920591b04fb3a8 --- /dev/null +++ b/test/typeparam/dedup.dir/main.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 ( + "./b" + "./c" +) + +func main() { + b.B() + c.C() +} diff --git a/test/typeparam/dedup.go b/test/typeparam/dedup.go new file mode 100644 index 00000000000000..3b98e03d9d439a --- /dev/null +++ b/test/typeparam/dedup.go @@ -0,0 +1,12 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Note: this doesn't really test the deduplication of +// instantiations. It just provides an easy mechanism to build a +// binary that you can then check with objdump manually to make sure +// deduplication is happening. TODO: automate this somehow? + +package ignored diff --git a/test/typeparam/dedup.out b/test/typeparam/dedup.out new file mode 100644 index 00000000000000..1140ff52e2ba1f --- /dev/null +++ b/test/typeparam/dedup.out @@ -0,0 +1,4 @@ +true +true +true +true diff --git a/test/typeparam/dictionaryCapture-noinline.go b/test/typeparam/dictionaryCapture-noinline.go new file mode 100644 index 00000000000000..4c5e7ec7a6e008 --- /dev/null +++ b/test/typeparam/dictionaryCapture-noinline.go @@ -0,0 +1,126 @@ +// run -gcflags="-l" + +// Copyright 2021 The Go 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 situations where functions/methods are not +// immediately called and we need to capture the dictionary +// required for later invocation. + +package main + +func main() { + functions() + methodExpressions() + methodValues() + interfaceMethods() + globals() +} + +func g0[T any](x T) { +} +func g1[T any](x T) T { + return x +} +func g2[T any](x T) (T, T) { + return x, x +} + +func functions() { + f0 := g0[int] + f0(7) + f1 := g1[int] + is7(f1(7)) + f2 := g2[int] + is77(f2(7)) +} + +func is7(x int) { + if x != 7 { + println(x) + panic("assertion failed") + } +} +func is77(x, y int) { + if x != 7 || y != 7 { + println(x, y) + panic("assertion failed") + } +} + +type s[T any] struct { + a T +} + +func (x s[T]) g0() { +} +func (x s[T]) g1() T { + return x.a +} +func (x s[T]) g2() (T, T) { + return x.a, x.a +} + +func methodExpressions() { + x := s[int]{a: 7} + f0 := s[int].g0 + f0(x) + f1 := s[int].g1 + is7(f1(x)) + f2 := s[int].g2 + is77(f2(x)) +} + +func methodValues() { + x := s[int]{a: 7} + f0 := x.g0 + f0() + f1 := x.g1 + is7(f1()) + f2 := x.g2 + is77(f2()) +} + +var x interface { + g0() + g1() int + g2() (int, int) +} = s[int]{a: 7} +var y interface{} = s[int]{a: 7} + +func interfaceMethods() { + x.g0() + is7(x.g1()) + is77(x.g2()) + y.(interface{ g0() }).g0() + is7(y.(interface{ g1() int }).g1()) + is77(y.(interface{ g2() (int, int) }).g2()) +} + +// Also check for instantiations outside functions. +var gg0 = g0[int] +var gg1 = g1[int] +var gg2 = g2[int] + +var hh0 = s[int].g0 +var hh1 = s[int].g1 +var hh2 = s[int].g2 + +var xtop = s[int]{a: 7} +var ii0 = x.g0 +var ii1 = x.g1 +var ii2 = x.g2 + +func globals() { + gg0(7) + is7(gg1(7)) + is77(gg2(7)) + x := s[int]{a: 7} + hh0(x) + is7(hh1(x)) + is77(hh2(x)) + ii0() + is7(ii1()) + is77(ii2()) +} diff --git a/test/typeparam/dictionaryCapture.go b/test/typeparam/dictionaryCapture.go new file mode 100644 index 00000000000000..b503abb18f3560 --- /dev/null +++ b/test/typeparam/dictionaryCapture.go @@ -0,0 +1,203 @@ +// run + +// Copyright 2021 The Go 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 situations where functions/methods are not +// immediately called and we need to capture the dictionary +// required for later invocation. + +package main + +import ( + "fmt" +) + +func main() { + functions() + methodExpressions() + genMethodExpressions[int](7) + methodValues() + genMethodValues[int](7) + interfaceMethods() + globals() + recursive() +} + +func g0[T any](x T) { +} +func g1[T any](x T) T { + return x +} +func g2[T any](x T) (T, T) { + return x, x +} + +func functions() { + f0 := g0[int] + f0(7) + f1 := g1[int] + is7(f1(7)) + f2 := g2[int] + is77(f2(7)) +} + +func is7(x int) { + if x != 7 { + println(x) + panic("assertion failed") + } +} +func is77(x, y int) { + if x != 7 || y != 7 { + println(x, y) + panic("assertion failed") + } +} + +type s[T any] struct { + a T +} + +func (x s[T]) g0() { +} +func (x s[T]) g1() T { + return x.a +} +func (x s[T]) g2() (T, T) { + return x.a, x.a +} + +func methodExpressions() { + x := s[int]{a: 7} + f0 := s[int].g0 + f0(x) + f0p := (*s[int]).g0 + f0p(&x) + f1 := s[int].g1 + is7(f1(x)) + f1p := (*s[int]).g1 + is7(f1p(&x)) + f2 := s[int].g2 + is77(f2(x)) + f2p := (*s[int]).g2 + is77(f2p(&x)) +} + +func genMethodExpressions[T comparable](want T) { + x := s[T]{a: want} + f0 := s[T].g0 + f0(x) + f0p := (*s[T]).g0 + f0p(&x) + f1 := s[T].g1 + if got := f1(x); got != want { + panic(fmt.Sprintf("f1(x) == %d, want %d", got, want)) + } + f1p := (*s[T]).g1 + if got := f1p(&x); got != want { + panic(fmt.Sprintf("f1p(&x) == %d, want %d", got, want)) + } + f2 := s[T].g2 + if got1, got2 := f2(x); got1 != want || got2 != want { + panic(fmt.Sprintf("f2(x) == %d, %d, want %d, %d", got1, got2, want, want)) + } +} + +func methodValues() { + x := s[int]{a: 7} + f0 := x.g0 + f0() + f1 := x.g1 + is7(f1()) + f2 := x.g2 + is77(f2()) +} + +func genMethodValues[T comparable](want T) { + x := s[T]{a: want} + f0 := x.g0 + f0() + f1 := x.g1 + if got := f1(); got != want { + panic(fmt.Sprintf("f1() == %d, want %d", got, want)) + } + f2 := x.g2 + if got1, got2 := f2(); got1 != want || got2 != want { + panic(fmt.Sprintf("f2() == %d, %d, want %d, %d", got1, got2, want, want)) + } +} + +var x interface { + g0() + g1() int + g2() (int, int) +} = s[int]{a: 7} +var y interface{} = s[int]{a: 7} + +func interfaceMethods() { + x.g0() + is7(x.g1()) + is77(x.g2()) + y.(interface{ g0() }).g0() + is7(y.(interface{ g1() int }).g1()) + is77(y.(interface{ g2() (int, int) }).g2()) +} + +// Also check for instantiations outside functions. +var gg0 = g0[int] +var gg1 = g1[int] +var gg2 = g2[int] + +var hh0 = s[int].g0 +var hh1 = s[int].g1 +var hh2 = s[int].g2 + +var xtop = s[int]{a: 7} +var ii0 = x.g0 +var ii1 = x.g1 +var ii2 = x.g2 + +func globals() { + gg0(7) + is7(gg1(7)) + is77(gg2(7)) + x := s[int]{a: 7} + hh0(x) + is7(hh1(x)) + is77(hh2(x)) + ii0() + is7(ii1()) + is77(ii2()) +} + +func recursive() { + if got, want := recur1[int](5), 110; got != want { + panic(fmt.Sprintf("recur1[int](5) = %d, want = %d", got, want)) + } +} + +type Integer interface { + int | int32 | int64 +} + +func recur1[T Integer](n T) T { + if n == 0 || n == 1 { + return T(1) + } else { + return n * recur2(n-1) + } +} + +func recur2[T Integer](n T) T { + list := make([]T, n) + for i, _ := range list { + list[i] = T(i + 1) + } + var sum T + for _, elt := range list { + sum += elt + } + return sum + recur1(n-1) +} diff --git a/test/typeparam/dottype.go b/test/typeparam/dottype.go new file mode 100644 index 00000000000000..9f1630d19cd367 --- /dev/null +++ b/test/typeparam/dottype.go @@ -0,0 +1,86 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T any](x interface{}) T { + return x.(T) +} +func f2[T any](x interface{}) (T, bool) { + t, ok := x.(T) + return t, ok +} + +type I interface { + foo() +} + +type myint int + +func (myint) foo() { +} + +type myfloat float64 + +func (myfloat) foo() { +} + +func g[T I](x I) T { + return x.(T) +} +func g2[T I](x I) (T, bool) { + t, ok := x.(T) + return t, ok +} + +func h[T any](x interface{}) struct{ a, b T } { + return x.(struct{ a, b T }) +} + +func k[T any](x interface{}) interface{ bar() T } { + return x.(interface{ bar() T }) +} + +type mybar int + +func (x mybar) bar() int { + return int(x) +} + +func main() { + var i interface{} = int(3) + var j I = myint(3) + var x interface{} = float64(3) + var y I = myfloat(3) + + println(f[int](i)) + shouldpanic(func() { f[int](x) }) + println(f2[int](i)) + println(f2[int](x)) + + println(g[myint](j)) + shouldpanic(func() { g[myint](y) }) + println(g2[myint](j)) + println(g2[myint](y)) + + println(h[int](struct{ a, b int }{3, 5}).a) + + println(k[int](mybar(3)).bar()) + + type large struct {a,b,c,d,e,f int} + println(f[large](large{}).a) + l2, ok := f2[large](large{}) + println(l2.a, ok) +} +func shouldpanic(x func()) { + defer func() { + e := recover() + if e == nil { + panic("didn't panic") + } + }() + x() +} diff --git a/test/typeparam/dottype.out b/test/typeparam/dottype.out new file mode 100644 index 00000000000000..8e6a3c25526093 --- /dev/null +++ b/test/typeparam/dottype.out @@ -0,0 +1,10 @@ +3 +3 true +0 false +3 +3 true +0 false +3 +3 +0 +0 true diff --git a/test/typeparam/double.go b/test/typeparam/double.go index ce78ec9748319c..fbbe602d1336a1 100644 --- a/test/typeparam/double.go +++ b/test/typeparam/double.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -12,14 +12,14 @@ import ( ) type Number interface { - type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 } type MySlice []int type MyFloatSlice []float64 type _SliceOf[E any] interface { - type []E + ~[]E } func _DoubleElems[S _SliceOf[E], E Number](s S) S { @@ -44,29 +44,29 @@ func main() { want := MySlice{2, 4, 6} got := _DoubleElems[MySlice, int](arg) if !reflect.DeepEqual(got, want) { - panic(fmt.Sprintf("got %s, want %s", got, want)) + panic(fmt.Sprintf("got %s, want %s", got, want)) } // constraint type inference got = _DoubleElems[MySlice](arg) if !reflect.DeepEqual(got, want) { - panic(fmt.Sprintf("got %s, want %s", got, want)) + panic(fmt.Sprintf("got %s, want %s", got, want)) } got = _DoubleElems(arg) if !reflect.DeepEqual(got, want) { - panic(fmt.Sprintf("got %s, want %s", got, want)) + panic(fmt.Sprintf("got %s, want %s", got, want)) } farg := MyFloatSlice{1.2, 2.0, 3.5} fwant := MyFloatSlice{2.4, 4.0, 7.0} fgot := _DoubleElems(farg) if !reflect.DeepEqual(fgot, fwant) { - panic(fmt.Sprintf("got %s, want %s", fgot, fwant)) + panic(fmt.Sprintf("got %s, want %s", fgot, fwant)) } fgot = _DoubleElems2(farg) if !reflect.DeepEqual(fgot, fwant) { - panic(fmt.Sprintf("got %s, want %s", fgot, fwant)) + panic(fmt.Sprintf("got %s, want %s", fgot, fwant)) } } diff --git a/test/typeparam/eface.go b/test/typeparam/eface.go new file mode 100644 index 00000000000000..05d5503c82f1a2 --- /dev/null +++ b/test/typeparam/eface.go @@ -0,0 +1,67 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure we handle instantiated empty interfaces. + +package main + +type E[T any] interface { +} + +//go:noinline +func f[T any](x E[T]) interface{} { + return x +} + +//go:noinline +func g[T any](x interface{}) E[T] { + return x +} + +type I[T any] interface { + foo() +} + +type myint int + +func (x myint) foo() {} + +//go:noinline +func h[T any](x I[T]) interface{ foo() } { + return x +} + +//go:noinline +func i[T any](x interface{ foo() }) I[T] { + return x +} + +func main() { + if f[int](1) != 1 { + println("test 1 failed") + } + if f[int](2) != (interface{})(2) { + println("test 2 failed") + } + if g[int](3) != 3 { + println("test 3 failed") + } + if g[int](4) != (E[int])(4) { + println("test 4 failed") + } + if h[int](myint(5)) != myint(5) { + println("test 5 failed") + } + if h[int](myint(6)) != interface{ foo() }(myint(6)) { + println("test 6 failed") + } + if i[int](myint(7)) != myint(7) { + println("test 7 failed") + } + if i[int](myint(8)) != I[int](myint(8)) { + println("test 8 failed") + } +} diff --git a/test/typeparam/equal.go b/test/typeparam/equal.go new file mode 100644 index 00000000000000..21e210345bb0e1 --- /dev/null +++ b/test/typeparam/equal.go @@ -0,0 +1,69 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// comparisons of type parameters to interfaces + +package main + +func f[T comparable](t, u T) bool { + // Comparing two type parameters directly. + // (Not really testing comparisons to interfaces, but just 'cause we're here.) + return t == u +} + +func g[T comparable](t T, i interface{}) bool { + // Compare type parameter value to empty interface. + return t == i +} + +type I interface { + foo() +} + +type C interface { + comparable + I +} + +func h[T C](t T, i I) bool { + // Compare type parameter value to nonempty interface. + return t == i +} + +type myint int + +func (x myint) foo() { +} + +func k[T comparable](t T, i interface{}) bool { + // Compare derived type value to interface. + return struct{ a, b T }{t, t} == i +} + +func main() { + assert(f(3, 3)) + assert(!f(3, 5)) + assert(g(3, 3)) + assert(!g(3, 5)) + assert(h(myint(3), myint(3))) + assert(!h(myint(3), myint(5))) + + type S struct{ a, b float64 } + + assert(f(S{3, 5}, S{3, 5})) + assert(!f(S{3, 5}, S{4, 6})) + assert(g(S{3, 5}, S{3, 5})) + assert(!g(S{3, 5}, S{4, 6})) + + assert(k(3, struct{ a, b int }{3, 3})) + assert(!k(3, struct{ a, b int }{3, 4})) +} + +func assert(b bool) { + if !b { + panic("assertion failed") + } +} diff --git a/test/typeparam/fact.go b/test/typeparam/fact.go index 16b2adf6fb7155..3c9a13aa736f11 100644 --- a/test/typeparam/fact.go +++ b/test/typeparam/fact.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,11 +8,11 @@ package main import "fmt" -func fact[T interface { type int, int64, float64 }](n T) T { - if n == T(1) { - return T(1) +func fact[T interface{ ~int | ~int64 | ~float64 }](n T) T { + if n == 1 { + return 1 } - return n * fact(n - T(1)) + return n * fact(n-1) } func main() { diff --git a/test/typeparam/factimp.dir/a.go b/test/typeparam/factimp.dir/a.go new file mode 100644 index 00000000000000..0bd73a88e7daef --- /dev/null +++ b/test/typeparam/factimp.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 Fact[T interface{ int | int64 | float64 }](n T) T { + if n == 1 { + return 1 + } + return n * Fact(n-1) +} diff --git a/test/typeparam/factimp.dir/main.go b/test/typeparam/factimp.dir/main.go new file mode 100644 index 00000000000000..75e08da01acd81 --- /dev/null +++ b/test/typeparam/factimp.dir/main.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + const want = 120 + + if got := a.Fact(5); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Fact[int64](5); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Fact(5.0); got != want { + panic(fmt.Sprintf("got %f, want %f", got, want)) + } +} diff --git a/test/typeparam/factimp.go b/test/typeparam/factimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/factimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/gencrawler.dir/a.go b/test/typeparam/gencrawler.dir/a.go new file mode 100644 index 00000000000000..50d6b4adebd335 --- /dev/null +++ b/test/typeparam/gencrawler.dir/a.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Go Authors. 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 + +var V val[int] + +type val[T any] struct { + valx T +} + +func (v *val[T]) Print() { + v.print1() +} + +func (v *val[T]) print1() { + println(v.valx) +} + +func (v *val[T]) fnprint1() { + println(v.valx) +} + +func FnPrint[T any](v *val[T]) { + v.fnprint1() +} diff --git a/test/typeparam/gencrawler.dir/main.go b/test/typeparam/gencrawler.dir/main.go new file mode 100644 index 00000000000000..198d117df6274c --- /dev/null +++ b/test/typeparam/gencrawler.dir/main.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + a.V.Print() + a.FnPrint(&a.V) +} diff --git a/test/typeparam/gencrawler.go b/test/typeparam/gencrawler.go new file mode 100644 index 00000000000000..66b5f433544a4d --- /dev/null +++ b/test/typeparam/gencrawler.go @@ -0,0 +1,10 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Testing that all methods of a private generic type are exported, if a variable +// with that type is exported. + +package ignored diff --git a/test/typeparam/gencrawler.out b/test/typeparam/gencrawler.out new file mode 100644 index 00000000000000..aa47d0d46d47a0 --- /dev/null +++ b/test/typeparam/gencrawler.out @@ -0,0 +1,2 @@ +0 +0 diff --git a/test/typeparam/genembed.go b/test/typeparam/genembed.go new file mode 100644 index 00000000000000..6a11be1cb6912a --- /dev/null +++ b/test/typeparam/genembed.go @@ -0,0 +1,52 @@ +// run + +// Copyright 2021 The Go 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 wrappers/interfaces for generic type embedding another generic type. + +package main + +import "fmt" + +type A[T any] struct { + B[T] +} + +type B[T any] struct { + val T +} + +func (b *B[T]) get() T { + return b.val +} + +type getter[T any] interface { + get() T +} + +//go:noinline +func doGet[T any](i getter[T]) T { + return i.get() +} + +//go:noline +func doGet2[T any](i interface{}) T { + i2 := i.(getter[T]) + return i2.get() +} + +func main() { + a := A[int]{B: B[int]{3}} + var i getter[int] = &a + + if got, want := doGet(i), 3; got != want { + panic(fmt.Sprintf("got %v, want %v", got, want)) + } + + as := A[string]{B: B[string]{"abc"}} + if got, want := doGet2[string](&as), "abc"; got != want { + panic(fmt.Sprintf("got %v, want %v", got, want)) + } +} diff --git a/test/typeparam/genembed2.go b/test/typeparam/genembed2.go new file mode 100644 index 00000000000000..f75731fd519e51 --- /dev/null +++ b/test/typeparam/genembed2.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2021 The Go 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 for declaration and use of a parameterized embedded field. + +package main + +import ( + "fmt" + "sync" +) + +type MyStruct[T any] struct { + val T +} + +type Lockable[T any] struct { + MyStruct[T] + mu sync.Mutex +} + +// Get returns the value stored in a Lockable. +func (l *Lockable[T]) Get() T { + l.mu.Lock() + defer l.mu.Unlock() + return l.MyStruct.val +} + +// Set sets the value in a Lockable. +func (l *Lockable[T]) Set(v T) { + l.mu.Lock() + defer l.mu.Unlock() + l.MyStruct = MyStruct[T]{v} +} + +func main() { + var li Lockable[int] + + li.Set(5) + if got, want := li.Get(), 5; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/geninline.dir/a.go b/test/typeparam/geninline.dir/a.go new file mode 100644 index 00000000000000..fe5ba22f6ee51a --- /dev/null +++ b/test/typeparam/geninline.dir/a.go @@ -0,0 +1,56 @@ +// Copyright 2021 The Go Authors. 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 IVal[T comparable] interface { + check(want T) +} + +type Val[T comparable] struct { + val T +} + +//go:noinline +func (l *Val[T]) check(want T) { + if l.val != want { + panic("hi") + } +} + +func Test1() { + var l Val[int] + if l.val != 0 { + panic("hi") + } + _ = IVal[int](&l) +} + +func Test2() { + var l Val[float64] + l.val = 3.0 + l.check(float64(3)) + _ = IVal[float64](&l) +} + +type privateVal[T comparable] struct { + val T +} + +//go:noinline +func (l *privateVal[T]) check(want T) { + if l.val != want { + panic("hi") + } +} + +type Outer struct { + val privateVal[string] +} + +func Test3() { + var o Outer + o.val.check("") + _ = IVal[string](&o.val) +} diff --git a/test/typeparam/geninline.dir/main.go b/test/typeparam/geninline.dir/main.go new file mode 100644 index 00000000000000..cfc48859a3eac2 --- /dev/null +++ b/test/typeparam/geninline.dir/main.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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" + +// Testing inlining of functions that refer to instantiated exported and non-exported +// generic types. + +func main() { + a.Test1() + a.Test2() + a.Test3() +} diff --git a/test/typeparam/geninline.go b/test/typeparam/geninline.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/geninline.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/graph.go b/test/typeparam/graph.go index f2a2630ad09d60..38a97bcfb1e8ca 100644 --- a/test/typeparam/graph.go +++ b/test/typeparam/graph.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -225,7 +225,6 @@ func TestShortestPath() { } } - func main() { TestShortestPath() } diff --git a/test/typeparam/ifaceconv.go b/test/typeparam/ifaceconv.go new file mode 100644 index 00000000000000..4dfc68f6e4e71e --- /dev/null +++ b/test/typeparam/ifaceconv.go @@ -0,0 +1,83 @@ +// run + +// Copyright 2021 The Go 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 we can convert type parameters to both empty +// and nonempty interfaces, and named and nonnamed versions +// thereof. + +package main + +import "fmt" + +type E interface{} + +func f[T any](x T) interface{} { + var i interface{} = x + return i +} + +func fs[T any](x T) interface{} { + y := []T{x} + var i interface{} = y + return i +} + +func g[T any](x T) E { + var i E = x + return i +} + +type C interface { + foo() int +} + +type myInt int + +func (x myInt) foo() int { + return int(x + 1) +} + +func h[T C](x T) interface{ foo() int } { + var i interface{ foo() int } = x + return i +} +func i[T C](x T) C { + var i C = x // conversion in assignment + return i +} + +func j[T C](t T) C { + return C(t) // explicit conversion +} + +func js[T any](x T) interface{} { + y := []T{x} + return interface{}(y) +} + +func main() { + if got, want := f[int](7), 7; got != want { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := fs[int](7), []int{7}; got.([]int)[0] != want[0] { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := g[int](7), 7; got != want { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := h[myInt](7).foo(), 8; got != want { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := i[myInt](7).foo(), 8; got != want { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := j[myInt](7).foo(), 8; got != want { + panic(fmt.Sprintf("got %d want %d", got, want)) + } + if got, want := js[int](7), []int{7}; got.([]int)[0] != want[0] { + panic(fmt.Sprintf("got %d want %d", got, want)) + } +} diff --git a/test/typeparam/importtest.go b/test/typeparam/importtest.go index 9cb30e8a7cc923..a49dcd9ba14995 100644 --- a/test/typeparam/importtest.go +++ b/test/typeparam/importtest.go @@ -1,4 +1,4 @@ -// compile -G +// compile // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/index.go b/test/typeparam/index.go index 83e65acdd006e2..064d33cfe747b4 100644 --- a/test/typeparam/index.go +++ b/test/typeparam/index.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,7 +11,7 @@ import ( ) // Index returns the index of x in s, or -1 if not found. -func index[T comparable](s []T, x T) int { +func Index[T comparable](s []T, x T) int { for i, v := range s { // v and x are type T, which has the comparable // constraint, so we can use == here. @@ -26,21 +26,56 @@ type obj struct { x int } +type obj2 struct { + x int8 + y float64 +} + +type obj3 struct { + x int64 + y int8 +} + +type inner struct { + y int64 + z int32 +} + +type obj4 struct { + x int32 + s inner +} + func main() { want := 2 vec1 := []string{"ab", "cd", "ef"} - if got := index(vec1, "ef"); got != want { + if got := Index(vec1, "ef"); got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } vec2 := []byte{'c', '6', '@'} - if got := index(vec2, '@'); got != want { + if got := Index(vec2, '@'); got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } vec3 := []*obj{&obj{2}, &obj{42}, &obj{1}} - if got := index(vec3, vec3[2]); got != want { + if got := Index(vec3, vec3[2]); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + vec4 := []obj2{obj2{2, 3.0}, obj2{3, 4.0}, obj2{4, 5.0}} + if got := Index(vec4, vec4[2]); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + vec5 := []obj3{obj3{2, 3}, obj3{3, 4}, obj3{4, 5}} + if got := Index(vec5, vec5[2]); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + vec6 := []obj4{obj4{2, inner{3, 4}}, obj4{3, inner{4, 5}}, obj4{4, inner{5, 6}}} + if got := Index(vec6, vec6[2]); got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } } diff --git a/test/typeparam/index2.go b/test/typeparam/index2.go new file mode 100644 index 00000000000000..ae1b44a0e4b212 --- /dev/null +++ b/test/typeparam/index2.go @@ -0,0 +1,67 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Testing various generic uses of indexing, both for reads and writes. + +package main + +import "fmt" + +// Can index an argument (read/write) constrained to be a slice or an array. +func Index1[T interface{ []int64 | [5]int64 }](x T) int64 { + x[2] = 5 + return x[3] +} + +// Can index an argument (read) constrained to be a byte array or a string. +func Index2[T interface{ []byte | string }](x T) byte { + return x[3] +} + +// Can index an argument (write) constrained to be a byte array, but not a string. +func Index2a[T interface{ []byte }](x T) byte { + x[2] = 'b' + return x[3] +} + +// Can index an argument (read/write) constrained to be a map. Maps can't +// be combined with any other type for indexing purposes. +func Index3[T interface{ map[int]int64 }](x T) int64 { + x[2] = 43 + return x[3] +} + +// But the type of the map keys or values can be parameterized. +func Index4[T any](x map[int]T) T { + var zero T + x[2] = zero + return x[3] +} + +func test[T comparable](got, want T) { + if got != want { + panic(fmt.Sprintf("got %v, want %v", got, want)) + } +} + +func main() { + x := make([]int64, 4) + x[3] = 2 + y := [5]int64{1, 2, 3, 4, 5} + z := "abcd" + w := make([]byte, 4) + w[3] = 5 + v := make(map[int]int64) + v[3] = 18 + + test(Index1(x), int64(2)) + test(Index1(y), int64(4)) + test(Index2(z), byte(100)) + test(Index2(w), byte(5)) + test(Index2a(w), byte(5)) + test(Index3(v), int64(18)) + test(Index4(v), int64(18)) +} diff --git a/test/typeparam/interfacearg.go b/test/typeparam/interfacearg.go index e2d85e3647b374..0e1fd0075deae9 100644 --- a/test/typeparam/interfacearg.go +++ b/test/typeparam/interfacearg.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -9,23 +9,25 @@ package main type I interface{} type _S[T any] struct { - *T + x *T } // F is a non-generic function, but has a type _S[I] which is instantiated from a // generic type. Test that _S[I] is successfully exported. func F() { v := _S[I]{} - if v.T != nil { + if v.x != nil { panic(v) } } // Testing the various combinations of method expressions. type S1 struct{} + func (*S1) M() {} type S2 struct{} + func (S2) M() {} func _F1[T interface{ M() }](t T) { @@ -33,9 +35,9 @@ func _F1[T interface{ M() }](t T) { } func F2() { - _F1(&S1{}) - _F1(S2{}) - _F1(&S2{}) + _F1(&S1{}) + _F1(S2{}) + _F1(&S2{}) } func main() { diff --git a/test/typeparam/issue23536.go b/test/typeparam/issue23536.go new file mode 100644 index 00000000000000..1d6d79b4292aa1 --- /dev/null +++ b/test/typeparam/issue23536.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2022 The Go 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 case where a slice of a user-defined byte type (not uint8 or byte) is +// converted to a string. Same for slice of runes. + +package main + +type MyByte byte + +type MyRune rune + +func f[T []MyByte](x T) string { + return string(x) +} + +func g[T []MyRune](x T) string { + return string(x) +} + +func main() { + var y []MyByte + _ = f(y) + _ = string(y) + + var z []MyRune + _ = g(z) + _ = string(z) +} diff --git a/test/typeparam/issue376214.go b/test/typeparam/issue376214.go new file mode 100644 index 00000000000000..269b684b50843d --- /dev/null +++ b/test/typeparam/issue376214.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func add[S ~string | ~[]byte](buf *[]byte, s S) { + *buf = append(*buf, s...) +} + +func main() { + var buf []byte + add(&buf, "foo") + add(&buf, []byte("bar")) + if string(buf) != "foobar" { + panic("got " + string(buf)) + } +} diff --git a/test/typeparam/issue39755.go b/test/typeparam/issue39755.go new file mode 100644 index 00000000000000..52c7e7c6527c18 --- /dev/null +++ b/test/typeparam/issue39755.go @@ -0,0 +1,27 @@ +// compile + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// copied from cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go + +package p + +func _[T interface{ ~map[string]int }](x T) { + _ = x == nil +} + +// simplified test case from issue + +type PathParamsConstraint interface { + ~map[string]string | ~[]struct{ key, value string } +} + +type PathParams[T PathParamsConstraint] struct { + t T +} + +func (pp *PathParams[T]) IsNil() bool { + return pp.t == nil // this must succeed +} diff --git a/test/typeparam/issue42758.go b/test/typeparam/issue42758.go new file mode 100644 index 00000000000000..25fb85ffb6dd75 --- /dev/null +++ b/test/typeparam/issue42758.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func F[T, U int]() interface{} { + switch interface{}(nil) { + case int(0), T(0), U(0): + } + + return map[interface{}]int{int(0): 0, T(0): 0, U(0): 0} +} + +func main() { + F[int, int]() +} diff --git a/test/typeparam/issue44688.go b/test/typeparam/issue44688.go new file mode 100644 index 00000000000000..48160e07303ce6 --- /dev/null +++ b/test/typeparam/issue44688.go @@ -0,0 +1,149 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// derived & expanded from cmd/compile/internal/types2/testdata/fixedbugs/issue44688.go2 + +package main + +type A1[T any] struct { + val T +} + +func (p *A1[T]) m1(val T) { + p.val = val +} + +type A2[T any] interface { + m2(T) +} + +type B1[T any] struct { + filler int + *A1[T] + A2[T] +} + +type B2[T any] interface { + A2[T] +} + +type ImpA2[T any] struct { + f T +} + +func (a2 *ImpA2[T]) m2(s T) { + a2.f = s +} + +type C[T any] struct { + filler1 int + filler2 int + B1[T] +} + +type D[T any] struct { + filler1 int + filler2 int + filler3 int + C[T] +} + +func test1[T any](arg T) { + // calling embedded methods + var b1 B1[T] + b1.A1 = &A1[T]{} + b1.A2 = &ImpA2[T]{} + + b1.A1.m1(arg) + b1.m1(arg) + + b1.A2.m2(arg) + b1.m2(arg) + + var b2 B2[T] + b2 = &ImpA2[T]{} + b2.m2(arg) + + // a deeper nesting + var d D[T] + d.C.B1.A1 = &A1[T]{} + d.C.B1.A2 = &ImpA2[T]{} + d.m1(arg) + d.m2(arg) + + // calling method expressions + m1x := B1[T].m1 + m1x(b1, arg) + // TODO(khr): reenable these. + //m2x := B2[T].m2 + //m2x(b2, arg) + + // calling method values + m1v := b1.m1 + m1v(arg) + m2v := b1.m2 + m2v(arg) + b2v := b2.m2 + b2v(arg) +} + +func test2() { + // calling embedded methods + var b1 B1[string] + b1.A1 = &A1[string]{} + b1.A2 = &ImpA2[string]{} + + b1.A1.m1("") + b1.m1("") + + b1.A2.m2("") + b1.m2("") + + var b2 B2[string] + b2 = &ImpA2[string]{} + b2.m2("") + + // a deeper nesting + var d D[string] + d.C.B1.A1 = &A1[string]{} + d.C.B1.A2 = &ImpA2[string]{} + d.m1("") + d.m2("") + + // calling method expressions + m1x := B1[string].m1 + m1x(b1, "") + m2x := B2[string].m2 + m2x(b2, "") + + // calling method values + m1v := b1.m1 + m1v("") + m2v := b1.m2 + m2v("") + b2v := b2.m2 + b2v("") +} + +// actual test case from issue + +type A[T any] struct{} + +func (*A[T]) f(T) {} + +type B[T any] struct{ A[T] } + +func test3() { + var b B[string] + b.A.f("") + b.f("") +} + +func main() { + test1[string]("") + test2() + test3() +} diff --git a/test/typeparam/issue45547.go b/test/typeparam/issue45547.go index 0a08d66b70e1f3..0024f364ba85c7 100644 --- a/test/typeparam/issue45547.go +++ b/test/typeparam/issue45547.go @@ -1,4 +1,4 @@ -// compile -G=3 +// compile // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,7 +11,7 @@ func f[T any]() (f, g T) { return f, g } // Tests for generic function instantiation on the right hande side of multi-value // assignments. -func _() { +func g() { // Multi-value assignment within a function var _, _ = f[int]() } diff --git a/test/typeparam/issue45722.go b/test/typeparam/issue45722.go index 0d7c20c2640e49..52a3c6359f9115 100644 --- a/test/typeparam/issue45722.go +++ b/test/typeparam/issue45722.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/issue45738.go b/test/typeparam/issue45738.go index 9f03e796a363ec..89b3b11ddef54a 100644 --- a/test/typeparam/issue45738.go +++ b/test/typeparam/issue45738.go @@ -1,4 +1,4 @@ -// compile -G=3 +// compile // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/issue45817.go b/test/typeparam/issue45817.go new file mode 100644 index 00000000000000..78e472f36a446c --- /dev/null +++ b/test/typeparam/issue45817.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type s[T any] struct { + a T +} + +func (x s[T]) f() T { + return x.a +} +func main() { + x := s[int]{a: 7} + f := x.f + if got, want := f(), 7; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue46461.go b/test/typeparam/issue46461.go new file mode 100644 index 00000000000000..4d4d4400c2333c --- /dev/null +++ b/test/typeparam/issue46461.go @@ -0,0 +1,13 @@ +// errorcheck + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T[U interface{ M() T[U] }] int // ERROR "invalid recursive type T" + +type X int + +func (X) M() T[X] { return 0 } diff --git a/test/typeparam/issue46461b.dir/a.go b/test/typeparam/issue46461b.dir/a.go new file mode 100644 index 00000000000000..fcb414266d741c --- /dev/null +++ b/test/typeparam/issue46461b.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. 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[U interface{ M() int }] int diff --git a/test/typeparam/issue46461b.dir/b.go b/test/typeparam/issue46461b.dir/b.go new file mode 100644 index 00000000000000..a4583257ffd657 --- /dev/null +++ b/test/typeparam/issue46461b.dir/b.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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 X int + +func (X) M() int { return 0 } + +type _ a.T[X] diff --git a/test/typeparam/issue46461b.go b/test/typeparam/issue46461b.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue46461b.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue46472.go b/test/typeparam/issue46472.go new file mode 100644 index 00000000000000..027a8aaec1eedf --- /dev/null +++ b/test/typeparam/issue46472.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func foo[T any](d T) { + switch v := interface{}(d).(type) { + case string: + if v != "x" { + panic("unexpected v: " + v) + } + } + +} +func main() { + foo("x") +} diff --git a/test/typeparam/issue46591.go b/test/typeparam/issue46591.go new file mode 100644 index 00000000000000..9e2c31de5d0d0a --- /dev/null +++ b/test/typeparam/issue46591.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2021 The Go Authors. 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 T[_ any] struct{} + +var m = map[interface{}]int{ + T[struct{ int }]{}: 0, + T[struct { + int "x" + }]{}: 0, +} + +func main() { + if len(m) != 2 { + panic(len(m)) + } +} diff --git a/test/typeparam/issue47258.go b/test/typeparam/issue47258.go new file mode 100644 index 00000000000000..7b202c93df8af6 --- /dev/null +++ b/test/typeparam/issue47258.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Numeric interface { + int32 | int64 | float64 | complex64 +} + +//go:noline +func inc[T Numeric](x T) T { + x++ + return x +} +func main() { + if got, want := inc(int32(5)), int32(6); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := inc(float64(5)), float64(6.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := inc(complex64(5)), complex64(6.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue47272.go b/test/typeparam/issue47272.go new file mode 100644 index 00000000000000..79748ad1aa091f --- /dev/null +++ b/test/typeparam/issue47272.go @@ -0,0 +1,55 @@ +// run + +// Copyright 2021 The Go Authors. 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 ( + "errors" + "fmt" +) + +type Option[T any] struct { + ok bool + val T +} + +func (o Option[T]) String() string { + if o.ok { + return fmt.Sprintf("Some(%v)", o.val) + } + return "None" +} + +func Some[T any](val T) Option[T] { return Option[T]{ok: true, val: val} } +func None[T any]() Option[T] { return Option[T]{ok: false} } + +type Result[T, E any] struct { + ok bool + val T + err E +} + +func (r Result[T, E]) String() string { + if r.ok { + return fmt.Sprintf("Ok(%v)", r.val) + } + return fmt.Sprintf("Err(%v)", r.err) +} + +func Ok[T, E any](val T) Result[T, E] { return Result[T, E]{ok: true, val: val} } +func Err[T, E any](err E) Result[T, E] { return Result[T, E]{ok: false, err: err} } + +func main() { + a := Some[int](1) + b := None[int]() + fmt.Println(a, b) + + x := Ok[int, error](1) + y := Err[int, error](errors.New("test")) + fmt.Println(x, y) + // fmt.Println(x) + _, _, _, _ = a, b, x, y +} diff --git a/test/typeparam/issue47272.out b/test/typeparam/issue47272.out new file mode 100644 index 00000000000000..9c433faa9701c4 --- /dev/null +++ b/test/typeparam/issue47272.out @@ -0,0 +1,2 @@ +Some(1) None +Ok(1) Err(test) diff --git a/test/typeparam/issue47514.go b/test/typeparam/issue47514.go new file mode 100644 index 00000000000000..1fc054ec356c1a --- /dev/null +++ b/test/typeparam/issue47514.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2021 The Go 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 closures inside a generic function are not exported, +// even though not themselves generic. + +package main + +func Do[T any]() { + _ = func() string { + return "" + } +} + +func main() { + Do[int]() +} diff --git a/test/typeparam/issue47514b.go b/test/typeparam/issue47514b.go new file mode 100644 index 00000000000000..0609296501873b --- /dev/null +++ b/test/typeparam/issue47514b.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func Do[T any](do func() (T, string)) { + _ = func() (T, string) { + return do() + } +} + +func main() { + Do[int](func() (int, string) { + return 3, "3" + }) +} diff --git a/test/typeparam/issue47514c.dir/a.go b/test/typeparam/issue47514c.dir/a.go new file mode 100644 index 00000000000000..782b1d2a4f83af --- /dev/null +++ b/test/typeparam/issue47514c.dir/a.go @@ -0,0 +1,5 @@ +package a + +type Doer[T any] interface { + Do() T +} diff --git a/test/typeparam/issue47514c.dir/main.go b/test/typeparam/issue47514c.dir/main.go new file mode 100644 index 00000000000000..0ef423f6807069 --- /dev/null +++ b/test/typeparam/issue47514c.dir/main.go @@ -0,0 +1,10 @@ +package main + +import "./a" + +func Do[T any](doer a.Doer[T]) { + doer.Do() +} + +func main() { +} diff --git a/test/typeparam/issue47514c.go b/test/typeparam/issue47514c.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue47514c.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue47631.go b/test/typeparam/issue47631.go new file mode 100644 index 00000000000000..c2ce951caccbcb --- /dev/null +++ b/test/typeparam/issue47631.go @@ -0,0 +1,34 @@ +// errorcheck + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO: one day we will support internal type declarations, at which time this test will be removed. + +package p + +func g[T any]() { + type U []T // ERROR "type declarations inside generic functions are not currently supported" + type V []int // ERROR "type declarations inside generic functions are not currently supported" +} + +type S[T any] struct { +} + +func (s S[T]) m() { + type U []T // ERROR "type declarations inside generic functions are not currently supported" + type V []int // ERROR "type declarations inside generic functions are not currently supported" +} + + +func f() { + type U []int // ok +} + +type X struct { +} + +func (x X) m() { + type U []int // ok +} diff --git a/test/typeparam/issue47676.go b/test/typeparam/issue47676.go new file mode 100644 index 00000000000000..8569378cd67998 --- /dev/null +++ b/test/typeparam/issue47676.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + d := diff([]int{}, func(int) string { + return "foo" + }) + d() +} + +func diff[T any](previous []T, uniqueKey func(T) string) func() { + return func() { + newJSON := map[string]T{} + for _, prev := range previous { + delete(newJSON, uniqueKey(prev)) + } + } +} diff --git a/test/typeparam/issue47684.go b/test/typeparam/issue47684.go new file mode 100644 index 00000000000000..f0e4ed05de3c2d --- /dev/null +++ b/test/typeparam/issue47684.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[G any]() int { + return func() int { + return func() int { + return 0 + }() + }() +} + +func main() { + f[int]() +} diff --git a/test/typeparam/issue47684b.go b/test/typeparam/issue47684b.go new file mode 100644 index 00000000000000..3e9fa93f34f6f3 --- /dev/null +++ b/test/typeparam/issue47684b.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[G any]() interface{} { + return func() interface{} { + return func() interface{} { + var x G + return x + }() + }() +} + +func main() { + x := f[int]() + if v, ok := x.(int); !ok || v != 0 { + panic("bad") + } +} diff --git a/test/typeparam/issue47684c.go b/test/typeparam/issue47684c.go new file mode 100644 index 00000000000000..b1d45202f09a0b --- /dev/null +++ b/test/typeparam/issue47684c.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[G any]() func()func()int { + return func() func()int { + return func() int { + return 0 + } + } +} + +func main() { + f[int]()()() +} diff --git a/test/typeparam/issue47708.go b/test/typeparam/issue47708.go new file mode 100644 index 00000000000000..d6140f3b4fd7aa --- /dev/null +++ b/test/typeparam/issue47708.go @@ -0,0 +1,37 @@ +// run + +// Copyright 2021 The Go Authors. 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 FooType[T any] interface { + Foo(BarType[T]) string +} +type BarType[T any] interface { + Bar(FooType[T]) string +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type Baz[T any] T +// func (l Baz[T]) Foo(v BarType[T]) string { +// return v.Bar(l) +// } +// type Bob[T any] T +// func (l Bob[T]) Bar(v FooType[T]) string { +// if v,ok := v.(Baz[T]);ok{ +// return fmt.Sprintf("%v%v",v,l) +// } +// return "" +// } + +func main() { + // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). + // var baz Baz[int] = 123 + // var bob Bob[int] = 456 + // + // if got, want := baz.Foo(bob), "123456"; got != want { + // panic(fmt.Sprintf("got %s want %s", got, want)) + // } +} diff --git a/test/typeparam/issue47710.go b/test/typeparam/issue47710.go new file mode 100644 index 00000000000000..2263c8b88fd817 --- /dev/null +++ b/test/typeparam/issue47710.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type FooType[t any] interface { + Foo(BarType[t]) +} +type BarType[t any] interface { + Int(IntType[t]) FooType[int] +} + +type IntType[t any] int + +func (n IntType[t]) Foo(BarType[t]) {} +func (n IntType[_]) String() {} diff --git a/test/typeparam/issue47713.go b/test/typeparam/issue47713.go new file mode 100644 index 00000000000000..7e3b5a5d322fb8 --- /dev/null +++ b/test/typeparam/issue47713.go @@ -0,0 +1,52 @@ +// run + +// Copyright 2021 The Go Authors. 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 ( + "encoding" + "fmt" +) + +type Seralizable interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +type SerDeString string + +func (s *SerDeString) UnmarshalBinary(in []byte) error { + *s = SerDeString(in) + return nil +} + +func (s SerDeString) MarshalBinary() ([]byte, error) { + return []byte(s), nil +} + + +type GenericSerializable[T Seralizable] struct { + Key string + Value T +} + +func (g GenericSerializable[T]) Send() { + out, err := g.Value.MarshalBinary() + if err != nil { + panic("bad") + } + var newval SerDeString + newval.UnmarshalBinary(out) + fmt.Printf("Sent %s\n", newval) +} + +func main() { + val := SerDeString("asdf") + x := GenericSerializable[*SerDeString]{ + Value: &val, + } + x.Send() +} diff --git a/test/typeparam/issue47713.out b/test/typeparam/issue47713.out new file mode 100644 index 00000000000000..a6e77197d81c62 --- /dev/null +++ b/test/typeparam/issue47713.out @@ -0,0 +1 @@ +Sent asdf diff --git a/test/typeparam/issue47716.go b/test/typeparam/issue47716.go new file mode 100644 index 00000000000000..5024ac9b7346fe --- /dev/null +++ b/test/typeparam/issue47716.go @@ -0,0 +1,68 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +// size returns the size of type T +func size[T any](x T) uintptr { + return unsafe.Sizeof(x) +} + +// size returns the alignment of type T +func align[T any](x T) uintptr { + return unsafe.Alignof(x) +} + +type Tstruct[T any] struct { + f1 T + f2 int +} + +// offset returns the offset of field f2 in the generic type Tstruct +func (r *Tstruct[T]) offset() uintptr { + return unsafe.Offsetof(r.f2) +} + +func main() { + v1 := int(5) + if got, want := size(v1), unsafe.Sizeof(v1); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := align(v1), unsafe.Alignof(v1); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + v2 := "abc" + if got, want := size(v2), unsafe.Sizeof(v2); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := align(v2), unsafe.Alignof(v2); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + var v3 Tstruct[int] + if got, want := unsafe.Offsetof(v3.f2), unsafe.Sizeof(v1); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + var v4 Tstruct[interface{}] + var v5 interface{} + if got, want := unsafe.Offsetof(v4.f2), unsafe.Sizeof(v5); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got, want := v3.offset(), unsafe.Offsetof(v3.f2); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := v4.offset(), unsafe.Offsetof(v4.f2); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue47723.go b/test/typeparam/issue47723.go new file mode 100644 index 00000000000000..44c55b6145af55 --- /dev/null +++ b/test/typeparam/issue47723.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[_ any]() int { + var a [1]int + _ = func() int { + return func() int { + return 0 + }() + }() + return a[func() int { + return 0 + }()] +} + +func main() { + f[int]() +} diff --git a/test/typeparam/issue47740.go b/test/typeparam/issue47740.go new file mode 100644 index 00000000000000..f34394ce36ea6c --- /dev/null +++ b/test/typeparam/issue47740.go @@ -0,0 +1,40 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +type Exp[Ty any] interface { + Eval() Ty +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// type Lit[Ty any] Ty +// +// func (lit Lit[Ty]) Eval() Ty { return Ty(lit) } +// func (lit Lit[Ty]) String() string { return fmt.Sprintf("(lit %v)", Ty(lit)) } + +type Eq[Ty any] struct { + a Exp[Ty] + b Exp[Ty] +} + +func (e Eq[Ty]) String() string { + return fmt.Sprintf("(eq %v %v)", e.a, e.b) +} + +// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). +// var ( +// e0 = Eq[int]{Lit[int](128), Lit[int](64)} +// e1 = Eq[bool]{Lit[bool](true), Lit[bool](true)} +// ) + +func main() { + // For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639). + // fmt.Printf("%v\n", e0) + // fmt.Printf("%v\n", e1) +} diff --git a/api/next.txt b/test/typeparam/issue47740.out similarity index 100% rename from api/next.txt rename to test/typeparam/issue47740.out diff --git a/test/typeparam/issue47740b.go b/test/typeparam/issue47740b.go new file mode 100644 index 00000000000000..d46d05802eb4cd --- /dev/null +++ b/test/typeparam/issue47740b.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "reflect" + +type S[T any] struct { + a interface{} +} + +func (e S[T]) M() { + v := reflect.ValueOf(e.a) + _, _ = v.Interface().(int) +} + +func main() { + e := S[int]{0} + e.M() +} diff --git a/test/typeparam/issue47775.dir/b.go b/test/typeparam/issue47775.dir/b.go new file mode 100644 index 00000000000000..b6d7ba97c59c11 --- /dev/null +++ b/test/typeparam/issue47775.dir/b.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. 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 + +type C[T any] struct { +} + +func (c *C[T]) reset() { +} + +func New[T any]() { + c := &C[T]{} + z(c.reset) +} + +func z(interface{}) { +} diff --git a/test/typeparam/issue47775.dir/main.go b/test/typeparam/issue47775.dir/main.go new file mode 100644 index 00000000000000..5ec85a49d27575 --- /dev/null +++ b/test/typeparam/issue47775.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 "./b" + +func main() { + b.New[int]() +} diff --git a/test/typeparam/issue47775.go b/test/typeparam/issue47775.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue47775.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue47775b.go b/test/typeparam/issue47775b.go new file mode 100644 index 00000000000000..e084e0316f3abb --- /dev/null +++ b/test/typeparam/issue47775b.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2021 The Go Authors. 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 C[T any] struct { +} + +func (c *C[T]) reset() { +} + +func New[T any]() { + c := &C[T]{} + i = c.reset + z(c.reset) +} + +var i interface{} + +func z(interface{}) { +} + +func main() { + New[int]() +} diff --git a/test/typeparam/issue47797.go b/test/typeparam/issue47797.go new file mode 100644 index 00000000000000..ad89bcce01213b --- /dev/null +++ b/test/typeparam/issue47797.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Foo[T any] struct { + Val T +} + +func (f Foo[T]) Bat() {} + +type Bar struct { + Foo[int] +} + +func foo() { + var b Bar + b.Bat() +} diff --git a/test/typeparam/issue47877.go b/test/typeparam/issue47877.go new file mode 100644 index 00000000000000..be5c5c08f6dc8c --- /dev/null +++ b/test/typeparam/issue47877.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2021 The Go Authors. 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 Map[K comparable, V any] struct { + m map[K]V +} + +func NewMap[K comparable, V any]() Map[K, V] { + return Map[K, V]{m: map[K]V{}} +} + +func (m Map[K, V]) Get(key K) V { + return m.m[key] +} + +func main() { + _ = NewMap[int, struct{}]() +} diff --git a/test/typeparam/issue47878.go b/test/typeparam/issue47878.go new file mode 100644 index 00000000000000..25758cbe9097c4 --- /dev/null +++ b/test/typeparam/issue47878.go @@ -0,0 +1,56 @@ +// compile + +// Copyright 2021 The Go Authors. 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 Src1[T any] func() Src1[T] + +func (s *Src1[T]) Next() { + *s = (*s)() +} + +type Src2[T any] []func() Src2[T] + +func (s Src2[T]) Next() { + _ = s[0]() +} + +type Src3[T comparable] map[T]func() Src3[T] + +func (s Src3[T]) Next() { + var a T + _ = s[a]() +} + +type Src4[T any] chan func() T + +func (s Src4[T]) Next() { + _ = (<-s)() +} + +type Src5[T any] func() Src5[T] + +func (s Src5[T]) Next() { + var x interface{} = s + _ = (x.(Src5[T]))() +} + +func main() { + var src1 Src1[int] + src1.Next() + + var src2 Src2[int] + src2.Next() + + var src3 Src3[string] + src3.Next() + + var src4 Src4[int] + src4.Next() + + var src5 Src5[int] + src5.Next() +} diff --git a/test/typeparam/issue47892.dir/a.go b/test/typeparam/issue47892.dir/a.go new file mode 100644 index 00000000000000..b63d604eeb6e1a --- /dev/null +++ b/test/typeparam/issue47892.dir/a.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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 Index[T any] interface { + G() T +} + +type I1[T any] struct { + a T +} + +func (i *I1[T]) G() T { + return i.a +} diff --git a/test/typeparam/issue47892.dir/main.go b/test/typeparam/issue47892.dir/main.go new file mode 100644 index 00000000000000..348e38b638cee4 --- /dev/null +++ b/test/typeparam/issue47892.dir/main.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. 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" + +type Model[T any] struct { + index a.Index[T] +} + +func NewModel[T any](index a.Index[T]) Model[T] { + return Model[T]{ + index: index, + } +} + +func main() { + _ = NewModel[int]((*a.I1[int])(nil)) +} diff --git a/test/typeparam/issue47892.go b/test/typeparam/issue47892.go new file mode 100644 index 00000000000000..5bb6a746b3fd89 --- /dev/null +++ b/test/typeparam/issue47892.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored \ No newline at end of file diff --git a/test/typeparam/issue47892b.dir/a.go b/test/typeparam/issue47892b.dir/a.go new file mode 100644 index 00000000000000..5adb492578499f --- /dev/null +++ b/test/typeparam/issue47892b.dir/a.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Go Authors. 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{ p *int64 } + +type i struct{} + +func G() *T { return &T{nil} } + +func (j i) F(a, b *T) *T { + n := *a.p + *b.p + return &T{&n} +} + +func (j i) G() *T { + return &T{} +} + +type I[Idx any] interface { + G() Idx + F(a, b Idx) Idx +} + +func Gen() I[*T] { + return i{} +} diff --git a/test/typeparam/issue47892b.dir/main.go b/test/typeparam/issue47892b.dir/main.go new file mode 100644 index 00000000000000..3cd658f0e37627 --- /dev/null +++ b/test/typeparam/issue47892b.dir/main.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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" + +type S[Idx any] struct { + A string + B Idx +} + +type O[Idx any] struct { + A int + B a.I[Idx] +} diff --git a/test/typeparam/issue47892b.go b/test/typeparam/issue47892b.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue47892b.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue47896.go b/test/typeparam/issue47896.go new file mode 100644 index 00000000000000..616e90714b99a8 --- /dev/null +++ b/test/typeparam/issue47896.go @@ -0,0 +1,74 @@ +// compile + +// Copyright 2021 The Go Authors. 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 ( + "database/sql" +) + +// Collection generic interface which things can be added to. +type Collection[T any] interface { + Add(T) +} + +// Slice generic slice implementation of a Collection +type Slice[T any] []*T + +func (s *Slice[T]) Add(t *T) { + *s = append(*s, t) +} + +type Scanner interface { + Scan(...interface{}) error +} + +type Mapper[T any] func(s Scanner, t T) error + +type Repository[T any] struct { + db *sql.DB +} + +func (r *Repository[T]) scan(rows *sql.Rows, m Mapper[*T], c Collection[*T]) error { + for rows.Next() { + t := new(T) + if err := m(rows, t); err != nil { + return err + } + c.Add(t) + } + return rows.Err() +} + +func (r *Repository[T]) query(query string, m Mapper[*T], c Collection[*T]) error { + rows, err := r.db.Query(query) + if err != nil { + return err + } + if err := r.scan(rows, m, c); err != nil { + rows.Close() + return err + } + return rows.Close() +} + +type Actor struct { + ActorID uint16 + FirstName string + LastName string +} + +type ActorRepository struct { + r Repository[Actor] +} + +func (ActorRepository) scan(s Scanner, a *Actor) error { + return s.Scan(&a.ActorID, &a.FirstName, &a.LastName) +} + +func (r *ActorRepository) SelectAll(c Collection[*Actor]) error { + return r.r.query("SELECT `actor_id`, `first_name`, `last_name` FROM `actor` LIMIT 10", r.scan, c) +} diff --git a/test/typeparam/issue47901.go b/test/typeparam/issue47901.go new file mode 100644 index 00000000000000..e005135185445d --- /dev/null +++ b/test/typeparam/issue47901.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2021 The Go Authors. 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 Chan[T any] chan Chan[T] + +func (ch Chan[T]) recv() Chan[T] { + return <-ch +} + +func main() { + ch := Chan[int](make(chan Chan[int])) + go func() { + ch <- make(Chan[int]) + }() + ch.recv() +} diff --git a/test/typeparam/issue47924.go b/test/typeparam/issue47924.go new file mode 100644 index 00000000000000..eea7acbdf00e43 --- /dev/null +++ b/test/typeparam/issue47924.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Cache[K any] struct{} + +func (c Cache[K]) foo(x interface{}, f func(K) bool) { + f(x.(K)) +} + +var _ Cache[int] diff --git a/test/typeparam/issue47925.go b/test/typeparam/issue47925.go new file mode 100644 index 00000000000000..c595e14a6fe22d --- /dev/null +++ b/test/typeparam/issue47925.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2021 The Go Authors. 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 myifacer[T any] interface{ do(T) error } + +type stuff[T any] struct{} + +func (s stuff[T]) run() interface{} { + var i myifacer[T] + return i +} + +func main() { + stuff[int]{}.run() +} diff --git a/test/typeparam/issue47925b.go b/test/typeparam/issue47925b.go new file mode 100644 index 00000000000000..bffbe4e3d44e00 --- /dev/null +++ b/test/typeparam/issue47925b.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I[T any] interface { + foo() +} + +type E[T any] interface { +} + +//go:noinline +func f[T I[T]](x T) E[T] { + // contains a cast from nonempty to empty interface + return E[T](I[T](x)) +} + +type S struct { + x int +} + +func (s *S) foo() {} + +func main() { + i := f(&S{x: 7}) + if i.(*S).x != 7 { + panic("bad") + } +} diff --git a/test/typeparam/issue47925c.go b/test/typeparam/issue47925c.go new file mode 100644 index 00000000000000..9636b9d10dc5e8 --- /dev/null +++ b/test/typeparam/issue47925c.go @@ -0,0 +1,36 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I[T any] interface { + foo() +} + +type J[T any] interface { + foo() + bar() +} + +//go:noinline +func f[T J[T]](x T) I[T] { + // contains a cast between two nonempty interfaces + return I[T](J[T](x)) +} + +type S struct { + x int +} + +func (s *S) foo() {} +func (s *S) bar() {} + +func main() { + i := f(&S{x: 7}) + if i.(*S).x != 7 { + panic("bad") + } +} diff --git a/test/typeparam/issue47925d.go b/test/typeparam/issue47925d.go new file mode 100644 index 00000000000000..c5647f9a5b0605 --- /dev/null +++ b/test/typeparam/issue47925d.go @@ -0,0 +1,47 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I[T any] interface { + foo() +} + +type J[T any] interface { + foo() + bar() +} + +//go:noinline +func f[T J[T]](x T, g func(T) T) I[T] { + // contains a cast between two nonempty interfaces + // Also make sure we don't evaluate g(x) twice. + return I[T](J[T](g(x))) +} + +type S struct { + x int +} + +func (s *S) foo() {} +func (s *S) bar() {} + +var cnt int + +func inc(s *S) *S { + cnt++ + return s +} + +func main() { + i := f(&S{x: 7}, inc) + if i.(*S).x != 7 { + panic("bad") + } + if cnt != 1 { + panic("multiple calls") + } +} diff --git a/test/typeparam/issue47929.go b/test/typeparam/issue47929.go new file mode 100644 index 00000000000000..1aa6885bdd4049 --- /dev/null +++ b/test/typeparam/issue47929.go @@ -0,0 +1,29 @@ +// compile -p=p + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package v4 + +var sink interface{} + +//go:noinline +func Do(result, body interface{}) { + sink = &result +} + +func DataAction(result DataActionResponse, body DataActionRequest) { + Do(&result, body) +} + +type DataActionRequest struct { + Action *interface{} +} + +type DataActionResponse struct { + ValidationErrors *ValidationError +} + +type ValidationError struct { +} diff --git a/test/typeparam/issue47948.go b/test/typeparam/issue47948.go new file mode 100644 index 00000000000000..deab0efe57eb36 --- /dev/null +++ b/test/typeparam/issue47948.go @@ -0,0 +1,18 @@ +// compile + +// Copyright 2021 The Go Authors. 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 fun func() + +func F[T any]() { + _ = fun(func() { + + }) +} +func main() { + F[int]() +} diff --git a/test/typeparam/issue47966.go b/test/typeparam/issue47966.go new file mode 100644 index 00000000000000..ec664783b099c0 --- /dev/null +++ b/test/typeparam/issue47966.go @@ -0,0 +1,9 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type C comparable diff --git a/test/typeparam/issue48013.go b/test/typeparam/issue48013.go new file mode 100644 index 00000000000000..3fbf2490df4580 --- /dev/null +++ b/test/typeparam/issue48013.go @@ -0,0 +1,39 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +type S[T any] struct { + val T +} + +// Test type substitution where base type is unsafe.Pointer +type U[T any] unsafe.Pointer + +func test[T any]() T { + var q U[T] + var v struct { + // Test derived type that contains an unsafe.Pointer + p unsafe.Pointer + val T + } + _ = q + return v.val +} + +func main() { + want := 0 + got := test[int]() + if got != want { + panic(fmt.Sprintf("got %f, want %f", got, want)) + } + +} diff --git a/test/typeparam/issue48016.go b/test/typeparam/issue48016.go new file mode 100644 index 00000000000000..dbc87eccba5555 --- /dev/null +++ b/test/typeparam/issue48016.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "strconv" +) + +func test1[T any](fn func(T) int, v T) int { + fn1 := func() int { + var i interface{} = v + val := fn(i.(T)) + return val + } + return fn1() +} + +func main() { + want := 123 + got := test1(func(s string) int { + r, err := strconv.Atoi(s) + if err != nil { + return 0 + } + return r + }, "123") + if got != want { + panic(fmt.Sprintf("got %f, want %f", got, want)) + } +} diff --git a/test/typeparam/issue48030.go b/test/typeparam/issue48030.go new file mode 100644 index 00000000000000..23494f90dbdf4e --- /dev/null +++ b/test/typeparam/issue48030.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2021 The Go Authors. 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 Src[T any] func() Src[T] + +func Seq[T any]() Src[T] { + return nil +} + +func Seq2[T1 any, T2 any](v1 T1, v2 T2) Src[T2] { + return nil +} + +func main() { + // Type args fully supplied + Seq[int]() + // Partial inference of type args + Seq2[int](5, "abc") + // Full inference of type args + Seq2(5, "abc") +} diff --git a/test/typeparam/issue48042.go b/test/typeparam/issue48042.go new file mode 100644 index 00000000000000..1cfbfbe96ce7cf --- /dev/null +++ b/test/typeparam/issue48042.go @@ -0,0 +1,77 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" +) + +type G[T any] interface { + g() func()(*T) +} +type Foo[T any] struct { + +} +// OCALL +func (l *Foo[T]) f1() (*T) { + return g[T]()() +} +// OCALLFUNC +func (l *Foo[T]) f2() (*T) { + var f = g[T] + return f()() +} +// OCALLMETH +func (l *Foo[T]) f3() (*T) { + return l.g()() +} +// OCALLINTER +func (l *Foo[T]) f4() (*T) { + var g G[T] = l + return g.g()() +} +// ODYNAMICDOTTYPE +func (l *Foo[T]) f5() (*T) { + var x interface{} + x = g[T] + return x.(func()func()(*T))()() +} +func (l *Foo[T]) g() func() (*T) { + return func() (*T) { + t := new(T) + reflect.ValueOf(t).Elem().SetInt(100) + return t + } +} +func g[T any]() func() (*T) { + return func() (*T) { + t := new(T) + reflect.ValueOf(t).Elem().SetInt(100) + return t + } +} + +func main() { + foo := Foo[int]{} + // Make sure the function conversion is correct + if n := *(foo.f1()) ; n != 100{ + panic(fmt.Sprintf("%v",n)) + } + if n := *(foo.f2()) ; n != 100{ + panic(fmt.Sprintf("%v",n)) + } + if n := *(foo.f3()) ; n != 100{ + panic(fmt.Sprintf("%v",n)) + } + if n := *(foo.f4()) ; n != 100{ + panic(fmt.Sprintf("%v",n)) + } + if n := *(foo.f5()) ; n != 100{ + panic(fmt.Sprintf("%v",n)) + } +} \ No newline at end of file diff --git a/test/typeparam/issue48047.go b/test/typeparam/issue48047.go new file mode 100644 index 00000000000000..06a2ebdb3968d7 --- /dev/null +++ b/test/typeparam/issue48047.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2021 The Go Authors. 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 A[T any] struct { + field B[T] +} + +type B[T any] interface { + Work(T) +} + +func (a *A[T]) Work(t T) { + a.field.Work(t) +} + +type BImpl struct{} + +func (b BImpl) Work(s string) {} + +func main() { + a := &A[string]{ + field: BImpl{}, + } + a.Work("") +} diff --git a/test/typeparam/issue48049.go b/test/typeparam/issue48049.go new file mode 100644 index 00000000000000..3e87b387f013ed --- /dev/null +++ b/test/typeparam/issue48049.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + Gooer2[byte]() +} + +type Fooer[T any] interface { + Foo(p T) +} + +type fooer1[T any] struct{} + +func (fooer1[T]) Foo(T) {} + +type fooer2[T any] struct { + r []Fooer[T] +} + +//go:noinline +func (mr fooer2[T]) Foo(p T) { + mr.r[0] = fooer1[T]{} + return +} + +func Gooer2[T any]() Fooer[T] { + return fooer2[T]{} +} diff --git a/test/typeparam/issue48056.go b/test/typeparam/issue48056.go new file mode 100644 index 00000000000000..e91d689d2bb702 --- /dev/null +++ b/test/typeparam/issue48056.go @@ -0,0 +1,27 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type B[T any] interface { + Work() +} +type BImpl[T any] struct{} + +func (b *BImpl[T]) Work() { +} + +type A[T any] struct { + B[T] +} + +func f[T any]() { + s := &A[T]{ + &BImpl[T]{}, + } + // golang.org/issue/48056 + s.Work() +} diff --git a/test/typeparam/issue48094.dir/a.go b/test/typeparam/issue48094.dir/a.go new file mode 100644 index 00000000000000..dd8c16f3ae24f9 --- /dev/null +++ b/test/typeparam/issue48094.dir/a.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import "unsafe" + +func F[T any]() uintptr { + var t T + return unsafe.Sizeof(t) +} + +func G[T any]() uintptr { + var t T + return unsafe.Alignof(t) +} + +//func H[T any]() uintptr { +// type S struct { +// a T +// b T +// } +// var s S +// return unsafe.Offsetof(s.b) +//} diff --git a/test/typeparam/issue48094.dir/main.go b/test/typeparam/issue48094.dir/main.go new file mode 100644 index 00000000000000..78337da9ef27fa --- /dev/null +++ b/test/typeparam/issue48094.dir/main.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + if a.F[int64]() != 8 { + panic("bad") + } + if a.G[int8]() != 1 { + panic("bad") + } + // TODO: enable once 47631 is fixed. + //if a.H[int64]() != 8 { + // panic("bad") + //} +} diff --git a/test/typeparam/issue48094.go b/test/typeparam/issue48094.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48094.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48094b.dir/a.go b/test/typeparam/issue48094b.dir/a.go new file mode 100644 index 00000000000000..a113a224f7739d --- /dev/null +++ b/test/typeparam/issue48094b.dir/a.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. 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 F() { G(0) } +func G[T any](t T) {} diff --git a/test/typeparam/issue48094b.dir/b.go b/test/typeparam/issue48094b.dir/b.go new file mode 100644 index 00000000000000..242b34aa3185fa --- /dev/null +++ b/test/typeparam/issue48094b.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 H() { a.F() } diff --git a/test/typeparam/issue48094b.go b/test/typeparam/issue48094b.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue48094b.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48137.go b/test/typeparam/issue48137.go new file mode 100644 index 00000000000000..84a0f6db6f7ebb --- /dev/null +++ b/test/typeparam/issue48137.go @@ -0,0 +1,25 @@ +// run + +// Copyright 2021 The Go Authors. 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 Constraint[T any] interface { + ~func() T +} + +func Foo[T Constraint[T]]() T { + var t T + + t = func() T { + return t + } + return t +} + +func main() { + type Bar func() Bar + Foo[Bar]() +} diff --git a/test/typeparam/issue48185a.dir/p.go b/test/typeparam/issue48185a.dir/p.go new file mode 100644 index 00000000000000..176c7f4de5c5a8 --- /dev/null +++ b/test/typeparam/issue48185a.dir/p.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type MarshalOptions struct { + Marshalers *Marshalers +} + +type Encoder struct {} + +type Marshalers = marshalers[MarshalOptions, Encoder] + +type marshalers[Options, Coder any] struct{} + +func MarshalFuncV1[T any](fn func(T) ([]byte, error)) *Marshalers { + return &Marshalers{} +} diff --git a/test/typeparam/issue48185a.dir/p_test.go b/test/typeparam/issue48185a.dir/p_test.go new file mode 100644 index 00000000000000..a89d69744c994d --- /dev/null +++ b/test/typeparam/issue48185a.dir/p_test.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 "./p" + +func main() { + _ = p.MarshalFuncV1[int](func(int) ([]byte, error) { return nil, nil }) +} diff --git a/test/typeparam/issue48185a.go b/test/typeparam/issue48185a.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48185a.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48185b.dir/a.go b/test/typeparam/issue48185b.dir/a.go new file mode 100644 index 00000000000000..9aed60cfaee739 --- /dev/null +++ b/test/typeparam/issue48185b.dir/a.go @@ -0,0 +1,37 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "reflect" + "sync" +) + +type addressableValue struct{ reflect.Value } + +type arshalers[Options, Coder any] struct { + fncVals []typedArshaler[Options, Coder] + fncCache sync.Map // map[reflect.Type]unmarshaler +} +type typedArshaler[Options, Coder any] struct { + typ reflect.Type + fnc func(Options, *Coder, addressableValue) error +} + +type UnmarshalOptions1 struct { + // Unmarshalers is a list of type-specific unmarshalers to use. + Unmarshalers *arshalers[UnmarshalOptions1, Decoder1] +} + +type Decoder1 struct { +} + +func (a *arshalers[Options, Coder]) lookup(fnc func(Options, *Coder, addressableValue) error, t reflect.Type) func(Options, *Coder, addressableValue) error { + return fnc +} + +func UnmarshalFuncV2[T any](fn func(UnmarshalOptions1, *Decoder1, T) error) *arshalers[UnmarshalOptions1, Decoder1] { + return &arshalers[UnmarshalOptions1, Decoder1]{} +} diff --git a/test/typeparam/issue48185b.dir/main.go b/test/typeparam/issue48185b.dir/main.go new file mode 100644 index 00000000000000..ea157f7d6d4319 --- /dev/null +++ b/test/typeparam/issue48185b.dir/main.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + _ = a.UnmarshalOptions1{ + Unmarshalers: a.UnmarshalFuncV2(func(opts a.UnmarshalOptions1, dec *a.Decoder1, val *interface{}) (err error) { + return fmt.Errorf("error") + }), + } +} diff --git a/test/typeparam/issue48185b.go b/test/typeparam/issue48185b.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48185b.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48191.go b/test/typeparam/issue48191.go new file mode 100644 index 00000000000000..9c3218b9fadf03 --- /dev/null +++ b/test/typeparam/issue48191.go @@ -0,0 +1,269 @@ +// compile -c=2 + +// Copyright 2021 The Go Authors. 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 I1 interface { + int8 | int16 | int32 | int64 | int | uint +} +type I2 interface{ float32 | float64 } +type I3 interface{ string } + +func F[G1 I1, G2 I2, G3 I3]() { + var m0 map[G2]rune + var ch0, ch1 chan bool + var ast0, ast1 []struct{ s0 G3 } + var ai64_2 []int64 + var m1, m2, m3 map[bool]map[int]struct { + m0 map[G2]byte + s1 G3 + } + var i8_0, i8_1 G1 + var i16_0 int16 + var am3, am4 []map[float64]map[G2]*func(*byte, map[uint]int64, G3, struct{}) G2 + var pi64_0, pi64_1 *int64 + var i, i1, i2 int + var as5, as6, as7 []G3 + var ch2, ch3, ch4 chan uint + var m4, m5, m6 map[G1]chan bool + + if func(G2, int32) byte { + return m1[false][30].m0[G2(28.6)] * m3[func(bool, uint) bool { + return false + }(false, uint(94))][31].m0[G2(185.0)] * m1[(true || true) && (false && false)][51-i2].m0[G2(278.6)] + }(G2(672.5), int32(35)) < m3[<-m5[func(int64, int64) G1 { + return i8_1 + }(*pi64_0, int64(50))]][15&i1^i2^i2].m0[G2(895.3)] || (func(int64, uint) uint { + return uint(94) + }(int64(30), uint(95))&^<-ch2^<-ch4)&<-ch2^<-ch4 == <-ch2 { + var f0 float64 + var pf2 *float64 + var ch5, ch6 chan int16 + var fnc0 func(*int64, G2, struct { + i8_0 G1 + m1 map[float64]bool + i64_2 int64 + }, map[byte]func(G2, float64, *uint, float64) struct{}) complex128 = func(p0 *int64, p1 G2, p2 struct { + i8_0 G1 + m1 map[float64]bool + i64_2 int64 + }, p3 map[byte]func(G2, float64, *uint, float64) struct{}) complex128 { + p0 = pi64_1 + m5 = map[G1]chan bool{(p2.i8_0 + i8_1 + i8_1 ^ i8_1) * p2.i8_0 / p2.i8_0: m4[p2.i8_0>><-ch2]} + return (2.65i - 31.18i) * func(byte, byte) complex128 { + return 13.12i - 32.90i + (44.15i - 70.53i - (87.16i*92.67i + (24.18i - 9.13i))) + (func(G1, int16) complex128 { + return 55.80i + }(G1(30), int16(80)) + 8.48i*79.18i + (37.30i*73.81i + (21.01i - 76.30i)) + func(G3, G2) complex128 { + return 35.58i + }(G3("2JYizeFiEMvXLkUR"), p1)*(81.59i-21.76i)) + }(m1[<-m5[G1(37)*i8_1<= '4'&'\uab3e'>>uint(83) && (<-m6[G1(24)%i8_0] && <-ch1)][i].s1 + i = len([]G3{ast1[2].s0}) + i16_0 = <-ch6 / i16_0 & <-ch6 + i = (i1^i|i2|i2)/i + i + m6 = m4 + am3 = am3 + m1[G2(869.6) == G2(i2)] = m2[func(float64, rune) byte { + return func(G3, byte) byte { + return byte(42) + }(G3("8iDnlygG194xl"), byte(89)) + }(*pf2, '\u9cf4')/m1[func(G3, float64) bool { + return false + }(G3("6MbwBSHYzr9t0zD"), 774.4)][76].m0[G2(508.0)]/m2[<-m4[i8_0]][92&^i2].m0[G2(807.0)] > m3[(int32(39)|int32(i2))&^int32(i2) < int32(i2)][89*i1&i2].m0[G2(327.5)]] + m2[<-m4[func(G1, complex128) G1 { + return i8_1 + }(i8_0, 35.01i)] && <-m4[func(int, G1) G1 { + return G1(0) + }(10, G1(70))*i8_1&i8_1>><-ch2] || fnc0(pi64_0, G2(689.5), struct { + i8_0 G1 + m1 map[float64]bool + i64_2 int64 + }{(G1(78)*i8_1 - i8_1) / i8_1, map[float64]bool{499.2: <-m6[G1(88)^i8_0]}, int64(83) &^ ai64_2[33] & *pi64_1 * ai64_2[i1]}, map[byte]func(G2, float64, *uint, float64) struct { + }{m1[len(G3("bNIJZq")+G3("Fri5pn1MsZzYtsaV7b")) >= i][i^i1].m0[G2(691.7)]: nil}) != 71.77i-34.84i] = map[int]struct { + m0 map[G2]byte + s1 G3 + }{((18+i2)&^i2%i2 ^ i) / i: m3[(G2(267.1)*G2(i1) > G2(i2) || (false || true || (true || false))) && func(int32, int64) bool { + return <-ch0 + }(int32(63), ai64_2[61&^i1&i2])][i|i^i1]} + i2 = 90 - i1 + _, _, _, _, _, _, _, _ = f0, pf2, ch5, ch6, fnc0, m7, ch7, fnc1 + } else { + var m7 map[G1]chan uint + var ch5, ch6, ch7 chan G3 + var i32_0, i32_1 int32 + var m8, m9, m10 map[bool]struct { + } + pi64_1 = pi64_0 + m6[func(G3, G2) G1 { + return (G1(35) | i8_0) << i8_1 / i8_1 &^ i8_1 / i8_1 + }(G3("YBiKg"), G2(122.6))] = make(chan bool) + ast0 = ast0 + i8_1 = (((G1(10)+i8_1)&i8_0+i8_0)&i8_0&i8_1 ^ i8_1) & i8_1 + am4 = am3 + i32_1 = int32(10) &^ i32_0 + m8[func(float64, G3) bool { + return func(rune, int16) bool { + return (G2(267.0)*G2(i2) == G2(i) || func(G2, G3) bool { + return <-ch0 + }(G2(53.3), <-ch5)) && func(G2, G1) int32 { + return int32(63) + }(G2(804.8), G1(2))-i32_0 < i32_1 + }('\xbd', i16_0) + }(370.9, ast0[len([]complex128{})+i-i2].s0) && (G2(245.0)-G2(i1) == G2(i1) || byte(17)&m2[false][26].m0[G2(628.5)] > m3[false][55].m0[G2(608.8)] || func(G1, G1) bool { + return true + }(G1(24), G1(2)) || (<-m5[G1(38)] || <-ch1) && func(int32, int) bool { + return false && true + }(int32(6), i1) && '\x26'&'\x27'|func(G2, G3) rune { + return '\x13' + }(G2(229.6), G3("ys1msVeg61uSImCDkRG3C")) <= 'V'>>uint(88)-('\xbe'+'\uafd4')) == (53.04i == 37.22i)] = m8[func(byte, int64) bool { + return <-ch1 + }(m3[false && false][96].m0[G2(147.6)], *pi64_0) && 643.5 > float64(i1) && (<-ch0 && <-ch1)] + i8_1 = func(byte, uint) G1 { + return G1(68) + }(m2[<-ch1 || <-m5[G1(96)+i8_0] || func(bool, int32) bool { + return func(int, byte) bool { + return m1[true][89].s1 <= G3("2ZMnHGOMQnyHSbJ") + }(i2, m2[<-m6[G1(47)]][94].m0[G2(981.3)]) + }(<-m4[G1(0)&^i8_0&i8_0], i32_0)][i2%i&^i].m0[func(complex128, rune) G2 { + return G2(93.1) * G2(i2) + }(4.63i, m0[G2(975.8)])], uint(21)) + _, _, _, _, _, _, _, _, _ = m7, ch5, ch6, ch7, i32_0, i32_1, m8, m9, m10 + } + + if *pi64_0>><-ch3 <= *pi64_0 || func(bool, int32) int32 { + return (int32(69)&^int32(i2) + int32(i2)) * int32(i2) + }(true, int32(49))^int32(i2) >= int32(i) { + var ai8_8, ai8_9 []G1 + var pi2, pi3, pi4 *int + var pi8_5, pi8_6 *G1 + var i64_0, i64_1 int64 + m1[754.8*float64(i2) != float64(i) && 6.26i == 69.99i] = map[int]struct { + m0 map[G2]byte + s1 G3 + }{len([]G2{G2(935.9) / G2(i2), func(int64, G2) G2 { + return G2(720.5) + }(int64(36), G2(349.7))})&*pi2 + i2 - i1: m1[(uint(29) >= <-ch4 || int64(45)+ai64_2[18] >= *pi64_1) == (func(G2, G2) bool { + return <-m5[G1(25)] + }(G2(447.2), G2(946.6)) || func(int, int16) bool { + return true + }(40, int16(41)) && byte(51) >= m2[true][13].m0[G2(6.6)])][*pi3]} + am4 = []map[float64]map[G2]*func(*byte, map[uint]int64, G3, struct { + }) G2{am4[i2%*pi3]} + pi2 = &i2 + pi64_0 = pi64_1 + ai8_8[*pi3] = *pi8_5&ai8_9[(*pi4+*pi3)%*pi3] ^ ai8_8[90+i2|*pi4] + ai64_2 = []int64{} + m4 = m4 + pi2 = &i1 + pi3 = &i2 + _, _, _, _, _, _, _, _, _ = ai8_8, ai8_9, pi2, pi3, pi4, pi8_5, pi8_6, i64_0, i64_1 + } + + if (true || false || int32(68) > int32(i1) || <-m5[G1(11)-i8_0] && true) && func(int, float64) bool { + return <-m5[(G1(83)-i8_1)&^i8_1] + }(i1, 886.6) || func(byte, int) bool { + return 401.0/float64(i1)/float64(i1)-float64(i) == float64(i2) + }(m1[(G1(85)^i8_1)&^i8_1 <= i8_1][72].m0[G2(617.4)], i1) || (<-m6[(G1(3)|i8_0)>><-ch2%i8_0|i8_0] || <-ch0) { + var ch5 chan map[byte]complex128 + var fnc0 func(int32, *map[rune]complex128) complex128 + var c0 complex128 + var st0, st1, st2 struct { + } + var au8 []uint + var st3, st4, st5 struct { + ph0 *G2 + st1 struct { + m0 map[rune]complex128 + pch1 *chan int64 + m2 map[bool]byte + st3 struct { + ch0 chan func(map[G1]*struct { + pm0 *map[bool]int64 + h1 G2 + }, struct { + u0 uint + }, uint, float64) *struct { + ch0 chan map[int16]G2 + } + i1 int + ch2 chan complex128 + } + } + pm2 *map[int64]struct { + s0 G3 + pi1 *int + st2 struct { + m0 map[int]map[rune]int64 + r1 rune + } + } + } + var am9, am10, am11 []map[uint]int64 + m1[G3("E")+(*st4.pm2)[*pi64_0+<-*st3.st1.pch1].s0 < (*st4.pm2)[int64(46)].s0+(G3("4Jsp3pv0x")+G3("MTKt98c")+(G3("E6Nxqpl70")+G3("eXhhxb")))+(G3("siISQNeBXoQIHwGB")+G3("CzocwLRWIUD")+(G3("cDWy3E3qpeJOmw1wP9wZ")+G3("S3ZRONdtB7K1LBC"))+func(G1, uint) G3 { + return m2[false][74].s1 + }(G1(9), uint(26)))+func(G2, int) G3 { + return G3("WzncXvaqK4zPn") + }(G2(291.6), i)+(ast1[(40^i1+i1)&^st4.st1.st3.i1].s0+func(byte, int64) G3 { + return m2[207.7 == float64(i2) && (false || false)][i2].s1 + }(byte(34), am11[25][func(int32, float64) uint { + return uint(77) + }(int32(29), 403.1)]))] = map[int]struct { + m0 map[G2]byte + s1 G3 + }{st3.st1.st3.i1: m2[<-m4[i8_1]][st5.st1.st3.i1-st3.st1.st3.i1-i2]} + st1 = struct { + }{} + pi64_0 = pi64_1 + m4 = m6 + as7 = as7 + m6[(i8_0+i8_0)&^i8_1&^i8_1] = m5[G1(96)^i8_1] + st2 = struct { + }{} + st1 = struct { + }{} + am10 = []map[uint]int64{am9[len((*st4.pm2)[int64(65)].s0)+i], am11[st4.st1.st3.i1%st4.st1.st3.i1^i1]} + i2 = st5.st1.st3.i1*i - st5.st1.st3.i1 + _, _, _, _, _, _, _, _, _, _, _, _, _ = ch5, fnc0, c0, st0, st1, st2, au8, st3, st4, st5, am9, am10, am11 + } + +} + +func main() { + F[int16, float32, string]() +} diff --git a/test/typeparam/issue48198.go b/test/typeparam/issue48198.go new file mode 100644 index 00000000000000..1ed29b89db159d --- /dev/null +++ b/test/typeparam/issue48198.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package p + +type Foo[T any] struct { +} + +func (foo Foo[T]) Get() { +} + +var( + _ = Foo[byte]{} + _ = Foo[[]byte]{} + _ = Foo[map[byte]rune]{} + + _ = Foo[rune]{} + _ = Foo[[]rune]{} + _ = Foo[map[rune]byte]{} +) diff --git a/test/typeparam/issue48225.go b/test/typeparam/issue48225.go new file mode 100644 index 00000000000000..702bc077994e3f --- /dev/null +++ b/test/typeparam/issue48225.go @@ -0,0 +1,37 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "reflect" + +type Foo[T any] struct { + val int +} + +func (foo Foo[T]) Get() *T { + if foo.val != 1 { + panic("bad val field in Foo receiver") + } + return new(T) +} + +var ( + newInt = Foo[int]{val: 1}.Get + newString = Foo[string]{val: 1}.Get +) + +func main() { + i := newInt() + s := newString() + + if t := reflect.TypeOf(i).String(); t != "*int" { + panic(t) + } + if t := reflect.TypeOf(s).String(); t != "*string" { + panic(t) + } +} diff --git a/test/typeparam/issue48253.go b/test/typeparam/issue48253.go new file mode 100644 index 00000000000000..20d5db662a3134 --- /dev/null +++ b/test/typeparam/issue48253.go @@ -0,0 +1,34 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "reflect" +) + +type A[T any] struct { + B[int] +} + +type B[T any] struct { +} + +func (b B[T]) Bat() { + t := new(T) + if tt := reflect.TypeOf(t); tt.Kind() != reflect.Pointer || tt.Elem().Kind() != reflect.Int { + panic("unexpected type, want: *int, got: "+tt.String()) + } +} + +type Foo struct { + A[string] +} +func main() { + Foo{}.A.Bat() + Foo{}.A.B.Bat() + Foo{}.Bat() +} diff --git a/test/typeparam/issue48276a.go b/test/typeparam/issue48276a.go new file mode 100644 index 00000000000000..2a79268198d83a --- /dev/null +++ b/test/typeparam/issue48276a.go @@ -0,0 +1,19 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func main() { + IsZero[int](0) +} + +func IsZero[T comparable](val T) bool { + var zero T + fmt.Printf("%v:%v\n", zero, val) + return val != zero +} diff --git a/test/typeparam/issue48276a.out b/test/typeparam/issue48276a.out new file mode 100644 index 00000000000000..8f38db999d80ee --- /dev/null +++ b/test/typeparam/issue48276a.out @@ -0,0 +1 @@ +0:0 diff --git a/test/typeparam/issue48276b.go b/test/typeparam/issue48276b.go new file mode 100644 index 00000000000000..774898d0406069 --- /dev/null +++ b/test/typeparam/issue48276b.go @@ -0,0 +1,15 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + f[interface{}](nil) +} + +func f[T any](x T) { + var _ interface{} = x +} diff --git a/test/typeparam/issue48280.dir/a.go b/test/typeparam/issue48280.dir/a.go new file mode 100644 index 00000000000000..f66fd30e34ee43 --- /dev/null +++ b/test/typeparam/issue48280.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 I[T any] interface { + F() T +} + +type S struct{} diff --git a/test/typeparam/issue48280.dir/main.go b/test/typeparam/issue48280.dir/main.go new file mode 100644 index 00000000000000..2c8387dd429686 --- /dev/null +++ b/test/typeparam/issue48280.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + _ = a.S{} +} diff --git a/test/typeparam/issue48280.go b/test/typeparam/issue48280.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48280.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48306.dir/a.go b/test/typeparam/issue48306.dir/a.go new file mode 100644 index 00000000000000..fdfd86cb6d4a5d --- /dev/null +++ b/test/typeparam/issue48306.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 I[T any] interface { + F() T +} diff --git a/test/typeparam/issue48306.dir/main.go b/test/typeparam/issue48306.dir/main.go new file mode 100644 index 00000000000000..260c3c87eb1dca --- /dev/null +++ b/test/typeparam/issue48306.dir/main.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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" + +type S struct{} + +func (*S) F() *S { return nil } + +func main() { + var _ a.I[*S] = &S{} +} diff --git a/test/typeparam/issue48306.go b/test/typeparam/issue48306.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48306.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48317.go b/test/typeparam/issue48317.go new file mode 100644 index 00000000000000..0220360ed8d6c9 --- /dev/null +++ b/test/typeparam/issue48317.go @@ -0,0 +1,38 @@ +// run + +// Copyright 2021 The Go Authors. 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 ( + "encoding/json" +) + +type A[T any] struct { + F1 string `json:"t1"` + F2 T `json:"t2"` + B B `json:"t3"` +} + +type B struct { + F4 int `json:"t4"` +} + +func a[T any]() { + data := `{"t1":"1","t2":2,"t3":{"t4":4}}` + a1 := A[T]{} + if err := json.Unmarshal([]byte(data), &a1); err != nil { + panic(err) + } + if bytes, err := json.Marshal(&a1); err != nil { + panic(err) + } else if string(bytes) != data { + panic(string(bytes)) + } +} + +func main() { + a[int]() +} diff --git a/test/typeparam/issue48318.go b/test/typeparam/issue48318.go new file mode 100644 index 00000000000000..b75c520c6e3240 --- /dev/null +++ b/test/typeparam/issue48318.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2021 The Go Authors. 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 ( + "encoding/xml" + "fmt" +) + +type A[T, U any] struct { + Name T `xml:"name"` + Data U `xml:"data"` +} + +func main() { + src := &A[string, int]{Name: "name", Data: 1} + data, err := xml.Marshal(src) + if err != nil { + panic(err) + } + dst := &A[string, int]{} + err = xml.Unmarshal(data, dst) + if err != nil { + panic(err) + } + if *src != *dst { + panic(fmt.Sprintf("wanted %#v got %#v", src, dst)) + } +} diff --git a/test/typeparam/issue48337a.dir/a.go b/test/typeparam/issue48337a.dir/a.go new file mode 100644 index 00000000000000..6f1b128589bde6 --- /dev/null +++ b/test/typeparam/issue48337a.dir/a.go @@ -0,0 +1,32 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "fmt" + "sync" +) + +type WrapperWithLock[T any] interface { + PrintWithLock() +} + +func NewWrapperWithLock[T any](value T) WrapperWithLock[T] { + return &wrapperWithLock[T]{ + Object: value, + } +} + +type wrapperWithLock[T any] struct { + Lock sync.Mutex + Object T +} + +func (w *wrapperWithLock[T]) PrintWithLock() { + w.Lock.Lock() + defer w.Lock.Unlock() + + fmt.Println(w.Object) +} diff --git a/test/typeparam/issue48337a.dir/main.go b/test/typeparam/issue48337a.dir/main.go new file mode 100644 index 00000000000000..ddf672414e6588 --- /dev/null +++ b/test/typeparam/issue48337a.dir/main.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + obj := a.NewWrapperWithLock("this file does import sync") + obj.PrintWithLock() +} diff --git a/test/typeparam/issue48337a.go b/test/typeparam/issue48337a.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48337a.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48337a.out b/test/typeparam/issue48337a.out new file mode 100644 index 00000000000000..fa8d3eedcbabae --- /dev/null +++ b/test/typeparam/issue48337a.out @@ -0,0 +1 @@ +this file does import sync diff --git a/test/typeparam/issue48337b.dir/a.go b/test/typeparam/issue48337b.dir/a.go new file mode 100644 index 00000000000000..a3c2e88a2f2ba3 --- /dev/null +++ b/test/typeparam/issue48337b.dir/a.go @@ -0,0 +1,25 @@ +// Copyright 2021 The Go Authors. 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 Container[T any] struct { + X T +} + +func NewContainer[T any](x T) *Container[T] { + return &Container[T]{x} +} + +type MetaContainer struct { + C *Container[Value] +} + +type Value struct{} + +func NewMetaContainer() *MetaContainer { + c := NewContainer(Value{}) + // c := &Container[Value]{Value{}} // <-- this works + return &MetaContainer{c} +} diff --git a/test/typeparam/issue48337b.dir/main.go b/test/typeparam/issue48337b.dir/main.go new file mode 100644 index 00000000000000..0318b676087303 --- /dev/null +++ b/test/typeparam/issue48337b.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + a.NewMetaContainer() +} diff --git a/test/typeparam/issue48337b.go b/test/typeparam/issue48337b.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48337b.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48344.go b/test/typeparam/issue48344.go new file mode 100644 index 00000000000000..220bce948498ce --- /dev/null +++ b/test/typeparam/issue48344.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2021 The Go Authors. 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 G[T any] interface { + g() +} + +type Foo[T any] struct { +} + +func (foo *Foo[T]) g() { + +} + +func f[T any]() { + v := []G[T]{} + v = append(v, &Foo[T]{}) +} +func main() { + f[int]() +} diff --git a/test/typeparam/issue48424.go b/test/typeparam/issue48424.go new file mode 100644 index 00000000000000..c5e5d4b105f31d --- /dev/null +++ b/test/typeparam/issue48424.go @@ -0,0 +1,54 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Smoke test for constraint literals with elided interface +// per issue #48424. + +package main + +func identity[T int](x T) T { + return x +} + +func min[T int | string](x, y T) T { + if x < y { + return x + } + return y +} + +func max[T ~int | ~float64](x, y T) T { + if x > y { + return x + } + return y +} + +func main() { + if identity(1) != 1 { + panic("identity(1) failed") + } + + if min(2, 3) != 2 { + panic("min(2, 3) failed") + } + + if min("foo", "bar") != "bar" { + panic(`min("foo", "bar") failed`) + } + + if max(2, 3) != 3 { + panic("max(2, 3) failed") + } +} + +// Some random type parameter lists with elided interfaces. + +type ( + _[T struct{}] struct{} + _[M map[K]V, K comparable, V any] struct{} + _[_ interface{} | int] struct{} +) diff --git a/test/typeparam/issue48453.go b/test/typeparam/issue48453.go new file mode 100644 index 00000000000000..ef8c7f766fd47e --- /dev/null +++ b/test/typeparam/issue48453.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2021 The Go Authors. 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 + +//go:noinline +func CopyMap[M interface{ ~map[K]V }, K comparable, V any](m M) M { + out := make(M, len(m)) + for k, v := range m { + out[k] = v + } + return out +} + +func main() { + var m map[*string]int + CopyMap(m) +} diff --git a/test/typeparam/issue48454.dir/a.go b/test/typeparam/issue48454.dir/a.go new file mode 100644 index 00000000000000..9613916a4c8bb8 --- /dev/null +++ b/test/typeparam/issue48454.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import "sync" + +type Val[T any] struct { + mu sync.RWMutex + val T +} + +func (v *Val[T]) Has() { + v.mu.RLock() +} diff --git a/test/typeparam/issue48454.dir/b.go b/test/typeparam/issue48454.dir/b.go new file mode 100644 index 00000000000000..deb59d2ec84444 --- /dev/null +++ b/test/typeparam/issue48454.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 Session struct { + privateField a.Val[string] +} diff --git a/test/typeparam/issue48454.dir/main.go b/test/typeparam/issue48454.dir/main.go new file mode 100644 index 00000000000000..ad9d290a8a467b --- /dev/null +++ b/test/typeparam/issue48454.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 "./b" + +func main() { + var _ b.Session +} diff --git a/test/typeparam/issue48454.go b/test/typeparam/issue48454.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48454.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48462.dir/a.go b/test/typeparam/issue48462.dir/a.go new file mode 100644 index 00000000000000..26c704dbe4617d --- /dev/null +++ b/test/typeparam/issue48462.dir/a.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. 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 Unique[T comparable](set []T) []T { + nset := make([]T, 0, 8) + +loop: + for _, s := range set { + for _, e := range nset { + if s == e { + continue loop + } + } + + nset = append(nset, s) + } + + return nset +} diff --git a/test/typeparam/issue48462.dir/main.go b/test/typeparam/issue48462.dir/main.go new file mode 100644 index 00000000000000..e615367f3e2090 --- /dev/null +++ b/test/typeparam/issue48462.dir/main.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" + + "./a" +) + +func main() { + e := []int{1, 2, 2, 3, 1, 6} + + got := a.Unique(e) + want := []int{1, 2, 3, 6} + if !reflect.DeepEqual(got, want) { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + +} diff --git a/test/typeparam/issue48462.go b/test/typeparam/issue48462.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48462.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48537.go b/test/typeparam/issue48537.go new file mode 100644 index 00000000000000..3ae85c794b0496 --- /dev/null +++ b/test/typeparam/issue48537.go @@ -0,0 +1,21 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { +} + +type C interface { + map[int]string +} + +func f[A C]() A { + return A{ + 1: "a", + 2: "b", + } +} diff --git a/test/typeparam/issue48538.go b/test/typeparam/issue48538.go new file mode 100644 index 00000000000000..985f84ebc8d73c --- /dev/null +++ b/test/typeparam/issue48538.go @@ -0,0 +1,60 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Testing composite literal for a type param constrained to be a struct or a map. + +package p + +type C interface { + ~struct{ b1, b2 string } +} + +func f[T C]() T { + return T{ + b1: "a", + b2: "b", + } +} + +func f2[T ~struct{ b1, b2 string }]() T { + return T{ + b1: "a", + b2: "b", + } +} + +type D interface { + map[string]string | S +} + +type S map[string]string + +func g[T D]() T { + b1 := "foo" + b2 := "bar" + return T{ + b1: "a", + b2: "b", + } +} + +func g2[T map[string]string]() T { + b1 := "foo" + b2 := "bar" + return T{ + b1: "a", + b2: "b", + } +} + +func g3[T S]() T { + b1 := "foo" + b2 := "bar" + return T{ + b1: "a", + b2: "b", + } +} diff --git a/test/typeparam/issue48598.go b/test/typeparam/issue48598.go new file mode 100644 index 00000000000000..945b33269b65ed --- /dev/null +++ b/test/typeparam/issue48598.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2021 The Go Authors. 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 Iterator[T any] interface { + Iterate() +} + +type IteratorFunc[T any] func(fn func(T) bool) + +func (f IteratorFunc[T]) Iterate() { +} + +func FromIterator[T any](it Iterator[T]) { + it.Iterate() +} + +func Foo[T, R any]() { + FromIterator[R](IteratorFunc[R](nil)) +} + +func main() { + Foo[int, int]() +} diff --git a/test/typeparam/issue48602.go b/test/typeparam/issue48602.go new file mode 100644 index 00000000000000..c544697399d7f0 --- /dev/null +++ b/test/typeparam/issue48602.go @@ -0,0 +1,25 @@ +// run + +// Copyright 2021 The Go Authors. 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 Iterator[T any] interface { + Iterate(fn T) +} + +type IteratorFunc[T any] func(fn T) + +func (f IteratorFunc[T]) Iterate(fn T) { + f(fn) +} + +func Foo[R any]() { + var _ Iterator[R] = IteratorFunc[R](nil) +} + +func main() { + Foo[int]() +} diff --git a/test/typeparam/issue48604.go b/test/typeparam/issue48604.go new file mode 100644 index 00000000000000..348abf7c90005d --- /dev/null +++ b/test/typeparam/issue48604.go @@ -0,0 +1,25 @@ +// build + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type Foo[T any] interface { + CreateBar() Bar[T] +} + +type Bar[T any] func() Bar[T] + +func (f Bar[T]) CreateBar() Bar[T] { + return f +} + +func abc[R any]() { + var _ Foo[R] = Bar[R](nil)() +} + +func main() { + abc[int]() +} \ No newline at end of file diff --git a/test/typeparam/issue48609.go b/test/typeparam/issue48609.go new file mode 100644 index 00000000000000..53144d206aad60 --- /dev/null +++ b/test/typeparam/issue48609.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2021 The Go Authors. 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[T ~chan E, E any](e E) T { + ch := make(T) + go func() { + defer close(ch) + ch <- e + }() + return ch +} diff --git a/test/typeparam/issue48617.go b/test/typeparam/issue48617.go new file mode 100644 index 00000000000000..96978d4c9d80e9 --- /dev/null +++ b/test/typeparam/issue48617.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type Foo[T any] interface { + CreateBar() Bar[T] +} + +type Bar[T any] func() Bar[T] + +func (f Bar[T]) CreateBar() Bar[T] { + return f +} + +func abc[T any]() { + var b Bar[T] = func() Bar[T] { + var b Bar[T] + return b + } + var _ Foo[T] = b() +} + +func main() { + abc[int]() +} diff --git a/test/typeparam/issue48645a.go b/test/typeparam/issue48645a.go new file mode 100644 index 00000000000000..39267a9c2f68bd --- /dev/null +++ b/test/typeparam/issue48645a.go @@ -0,0 +1,31 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" +) + +type Stream[T any] struct { +} + +func (s Stream[T]) DropWhile() Stream[T] { + return Pipe[T, T](s) +} + +func Pipe[T, R any](s Stream[T]) Stream[R] { + it := func(fn func(R) bool) { + } + fmt.Println(reflect.TypeOf(it).String()) + return Stream[R]{} +} + +func main() { + s := Stream[int]{} + s = s.DropWhile() +} diff --git a/test/typeparam/issue48645a.out b/test/typeparam/issue48645a.out new file mode 100644 index 00000000000000..5093d0f0ffce70 --- /dev/null +++ b/test/typeparam/issue48645a.out @@ -0,0 +1 @@ +func(func(int) bool) diff --git a/test/typeparam/issue48645b.go b/test/typeparam/issue48645b.go new file mode 100644 index 00000000000000..619e7ee8b5b209 --- /dev/null +++ b/test/typeparam/issue48645b.go @@ -0,0 +1,81 @@ +// run + +// Copyright 2021 The Go Authors. 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 Iterator[T any] interface { + Iterate(fn func(T) bool) +} + +type IteratorFunc[T any] func(fn func(T) bool) + +func (f IteratorFunc[T]) Iterate(fn func(T) bool) { + f(fn) +} + +type Stream[T any] struct { + it Iterator[T] +} + +func (s Stream[T]) Iterate(fn func(T) bool) { + if s.it == nil { + return + } + s.it.Iterate(fn) +} + +func FromIterator[T any](it Iterator[T]) Stream[T] { + return Stream[T]{it: it} +} + +func (s Stream[T]) DropWhile(fn func(T) bool) Stream[T] { + return Pipe[T, T](s, func(t T) (T, bool) { + return t, true + }) +} + +func Pipe[T, R any](s Stream[T], op func(d T) (R, bool)) Stream[R] { + it := func(fn func(R) bool) { + // XXX Not getting the closure right when converting to interface. + // s.it.Iterate(func(t T) bool { + // r, ok := op(t) + // if !ok { + // return true + // } + + // return fn(r) + // }) + } + + return FromIterator[R](IteratorFunc[R](it)) +} + +func Reduce[T, U any](s Stream[T], identity U, acc func(U, T) U) (r U) { + r = identity + s.Iterate(func(t T) bool { + r = acc(r, t) + return true + }) + + return r +} + +type myIterator struct { +} + +func (myIterator) Iterate(fn func(int) bool) { +} + +func main() { + s := Stream[int]{} + s.it = myIterator{} + s = s.DropWhile(func(i int) bool { + return false + }) + Reduce(s, nil, func(acc []int, e int) []int { + return append(acc, e) + }) +} diff --git a/test/typeparam/issue48711.go b/test/typeparam/issue48711.go new file mode 100644 index 00000000000000..477a5d597897f0 --- /dev/null +++ b/test/typeparam/issue48711.go @@ -0,0 +1,18 @@ +// errorcheck + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T interface{ ~[]P }, P any](t T) { // ERROR "instantiation cycle" + if t == nil { + return + } + f[[]T, T]([]T{t}) +} + +func main() { + f[[]int](nil) +} diff --git a/test/typeparam/issue48716.dir/a.go b/test/typeparam/issue48716.dir/a.go new file mode 100644 index 00000000000000..63e599d9a18696 --- /dev/null +++ b/test/typeparam/issue48716.dir/a.go @@ -0,0 +1,51 @@ +// Copyright 2021 The Go Authors. 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 Pair[L, R any] struct { + L L + R R +} + +func Two[L, R any](l L, r R) Pair[L, R] { + return Pair[L, R]{L: l, R: r} +} + +type Map[K, V any] interface { + Put(K, V) + Len() int + Iterate(func(Pair[K, V]) bool) +} + +type HashMap[K comparable, V any] struct { + m map[K]V +} + +func NewHashMap[K comparable, V any](capacity int) HashMap[K, V] { + var m map[K]V + if capacity >= 1 { + m = make(map[K]V, capacity) + } else { + m = map[K]V{} + } + + return HashMap[K, V]{m: m} +} + +func (m HashMap[K, V]) Put(k K, v V) { + m.m[k] = v +} + +func (m HashMap[K, V]) Len() int { + return len(m.m) +} + +func (m HashMap[K, V]) Iterate(cb func(Pair[K, V]) bool) { + for k, v := range m.m { + if !cb(Two(k, v)) { + return + } + } +} diff --git a/test/typeparam/issue48716.dir/main.go b/test/typeparam/issue48716.dir/main.go new file mode 100644 index 00000000000000..13a126e8690662 --- /dev/null +++ b/test/typeparam/issue48716.dir/main.go @@ -0,0 +1,58 @@ +// Copyright 2021 The Go Authors. 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" +) + +// Creates copy of set +func Copy[T comparable](src MapSet[T]) (dst MapSet[T]) { + dst = HashSet[T](src.Len()) + Fill(src, dst) + return +} + +// Fill src from dst +func Fill[T any](src, dst MapSet[T]) { + src.Iterate(func(t T) bool { + dst.Add(t) + return true + }) + return +} + +type MapSet[T any] struct { + m a.Map[T, struct{}] +} + +func HashSet[T comparable](capacity int) MapSet[T] { + return FromMap[T](a.NewHashMap[T, struct{}](capacity)) +} + +func FromMap[T any](m a.Map[T, struct{}]) MapSet[T] { + return MapSet[T]{ + m: m, + } +} + +func (s MapSet[T]) Add(t T) { + s.m.Put(t, struct{}{}) +} + +func (s MapSet[T]) Len() int { + return s.m.Len() +} + +func (s MapSet[T]) Iterate(cb func(T) bool) { + s.m.Iterate(func(p a.Pair[T, struct{}]) bool { + return cb(p.L) + }) +} + +func main() { + x := FromMap[int](a.NewHashMap[int, struct{}](1)) + Copy[int](x) +} diff --git a/test/typeparam/issue48716.go b/test/typeparam/issue48716.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue48716.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue48838.go b/test/typeparam/issue48838.go new file mode 100644 index 00000000000000..1711d0405d3f10 --- /dev/null +++ b/test/typeparam/issue48838.go @@ -0,0 +1,31 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + check[string]() +} + +func check[T any]() { + var result setter[T] + switch result.(type) { + case fooA[T]: + case fooB[T]: + } +} + +type setter[T any] interface { + Set(T) +} + +type fooA[T any] struct{} + +func (fooA[T]) Set(T) {} + +type fooB[T any] struct{} + +func (fooB[T]) Set(T) {} diff --git a/test/typeparam/issue48962.dir/a.go b/test/typeparam/issue48962.dir/a.go new file mode 100644 index 00000000000000..a6d273476edade --- /dev/null +++ b/test/typeparam/issue48962.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type ( + A[P any] [10]P + S[P any] struct{ f P } + P[P any] *P + M[K comparable, V any] map[K]V +) diff --git a/test/typeparam/issue48962.dir/b.go b/test/typeparam/issue48962.dir/b.go new file mode 100644 index 00000000000000..e4eaa068197863 --- /dev/null +++ b/test/typeparam/issue48962.dir/b.go @@ -0,0 +1,51 @@ +// Copyright 2022 The Go Authors. 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 ( + lA[P any] [10]P + lS[P any] struct{ f P } + lP[P any] *P + lM[K comparable, V any] map[K]V +) + +// local cycles +type ( + A lA[A] // ERROR "invalid recursive type" + S lS[S] // ERROR "invalid recursive type" + P lP[P] // ok (indirection through lP) + M1 lM[int, M1] // ok (indirection through lM) + M2 lM[lA[byte], M2] // ok (indirection through lM) + + A2 lA[lS[lP[A2]]] // ok (indirection through lP) + A3 lA[lS[lS[A3]]] // ERROR "invalid recursive type" +) + +// cycles through imported types +type ( + Ai a.A[Ai] // ERROR "invalid recursive type" + Si a.S[Si] // ERROR "invalid recursive type" + Pi a.P[Pi] // ok (indirection through a.P) + M1i a.M[int, M1i] // ok (indirection through a.M) + M2i a.M[a.A[byte], M2i] // ok (indirection through a.M) + + A2i a.A[a.S[a.P[A2i]]] // ok (indirection through a.P) + A3i a.A[a.S[a.S[A3i]]] // ERROR "invalid recursive type" + + T2 a.S[T0[T2]] // ERROR "invalid recursive type" + T3 T0[Ai] // no follow-on error here +) + +// test case from issue + +type T0[P any] struct { + f P +} + +type T1 struct { // ERROR "invalid recursive type" + _ T0[T1] +} diff --git a/test/typeparam/issue48962.go b/test/typeparam/issue48962.go new file mode 100644 index 00000000000000..24d0eb002a3e76 --- /dev/null +++ b/test/typeparam/issue48962.go @@ -0,0 +1,7 @@ +// errorcheckdir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49027.dir/a.go b/test/typeparam/issue49027.dir/a.go new file mode 100644 index 00000000000000..da88297965ec21 --- /dev/null +++ b/test/typeparam/issue49027.dir/a.go @@ -0,0 +1,55 @@ +// Copyright 2021 The Go Authors. 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 Conv(v interface{}) string { + return conv[string](v) +} + +func conv[T any](v interface{}) T { + return v.(T) +} + +func Conv2(v interface{}) (string, bool) { + return conv2[string](v) +} + +func conv2[T any](v interface{}) (T, bool) { + x, ok := v.(T) + return x, ok +} + +func Conv3(v interface{}) string { + return conv3[string](v) +} + +func conv3[T any](v interface{}) T { + switch v := v.(type) { + case T: + return v + default: + var z T + return z + } +} + +type Mystring string + +func (Mystring) Foo() { +} + +func Conv4(v interface{Foo()}) Mystring { + return conv4[Mystring](v) +} + +func conv4[T interface{Foo()}](v interface{Foo()}) T { + switch v := v.(type) { + case T: + return v + default: + var z T + return z + } +} diff --git a/test/typeparam/issue49027.dir/main.go b/test/typeparam/issue49027.dir/main.go new file mode 100644 index 00000000000000..d998c5bd22a043 --- /dev/null +++ b/test/typeparam/issue49027.dir/main.go @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + s := "foo" + x := a.Conv(s) + if x != s { + panic(fmt.Sprintf("got %s wanted %s", x, s)) + } + y, ok := a.Conv2(s) + if !ok { + panic("conversion failed") + } + if y != s { + panic(fmt.Sprintf("got %s wanted %s", y, s)) + } + z := a.Conv3(s) + if z != s { + panic(fmt.Sprintf("got %s wanted %s", z, s)) + } + w := a.Conv4(a.Mystring(s)) + if w != a.Mystring(s) { + panic(fmt.Sprintf("got %s wanted %s", w, s)) + } +} diff --git a/test/typeparam/issue49027.go b/test/typeparam/issue49027.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue49027.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49049.go b/test/typeparam/issue49049.go new file mode 100644 index 00000000000000..b4b3bae59b4ccb --- /dev/null +++ b/test/typeparam/issue49049.go @@ -0,0 +1,27 @@ +// run + +// Copyright 2021 The Go Authors. 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 A[T any] interface { + m() +} + +type Z struct { + a,b int +} + +func (z *Z) m() { +} + +func test[T any]() { + var a A[T] = &Z{} + f := a.m + f() +} +func main() { + test[string]() +} diff --git a/test/typeparam/issue49241.dir/a.go b/test/typeparam/issue49241.dir/a.go new file mode 100644 index 00000000000000..34c99657d49b95 --- /dev/null +++ b/test/typeparam/issue49241.dir/a.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. 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[P any] struct { + x P +} + +type U struct { + a,b int +} diff --git a/test/typeparam/issue49241.dir/b.go b/test/typeparam/issue49241.dir/b.go new file mode 100644 index 00000000000000..e5f1e1290eb8dd --- /dev/null +++ b/test/typeparam/issue49241.dir/b.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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" + +//go:noinline +func F() interface{} { + return a.T[int]{} +} + +//go:noinline +func G() interface{} { + return struct{ X, Y a.U }{} +} diff --git a/test/typeparam/issue49241.dir/c.go b/test/typeparam/issue49241.dir/c.go new file mode 100644 index 00000000000000..34ea7c3ffae86f --- /dev/null +++ b/test/typeparam/issue49241.dir/c.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package c + +import "./a" + +//go:noinline +func F() interface{} { + return a.T[int]{} +} + +//go:noinline +func G() interface{} { + return struct{ X, Y a.U }{} +} diff --git a/test/typeparam/issue49241.dir/main.go b/test/typeparam/issue49241.dir/main.go new file mode 100644 index 00000000000000..58bb8a017f16f3 --- /dev/null +++ b/test/typeparam/issue49241.dir/main.go @@ -0,0 +1,21 @@ +// Copyright 2021 The Go Authors. 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 ( + "./b" + "./c" +) + +func main() { + if b.G() != c.G() { + println(b.G(), c.G()) + panic("bad") + } + if b.F() != c.F() { + println(b.F(), c.F()) + panic("bad") + } +} diff --git a/test/typeparam/issue49241.go b/test/typeparam/issue49241.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue49241.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49246.dir/a.go b/test/typeparam/issue49246.dir/a.go new file mode 100644 index 00000000000000..97459ee7481de5 --- /dev/null +++ b/test/typeparam/issue49246.dir/a.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. 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 R[T any] struct{ v T } + +func (r R[T]) Self() R[T] { return R[T]{} } + +type Fn[T any] func() R[T] + +func X() (r R[int]) { return r.Self() } + +func Y[T any](a Fn[T]) Fn[int] { + return func() (r R[int]) { + // No crash: return R[int]{} + return r.Self() + } +} diff --git a/test/typeparam/issue49246.dir/b.go b/test/typeparam/issue49246.dir/b.go new file mode 100644 index 00000000000000..5141b72fd4ddce --- /dev/null +++ b/test/typeparam/issue49246.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 Crash() { a.Y(a.X)() } diff --git a/test/typeparam/issue49246.go b/test/typeparam/issue49246.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue49246.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49295.go b/test/typeparam/issue49295.go new file mode 100644 index 00000000000000..f96c896eac0d47 --- /dev/null +++ b/test/typeparam/issue49295.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2021 The Go Authors. 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 "io" + +type Reader struct { + buf []byte +} +type Token *[16]byte + +func Read[T interface{ ~*[16]byte }](r *Reader) (t T, err error) { + if n := len(t); len(r.buf) >= n { + t = T(r.buf[:n]) + r.buf = r.buf[n:] + return + } + err = io.EOF + return +} + +func main() { + r := &Reader{buf: []byte("0123456789abcdef")} + token, err := Read[Token](r) + _, _ = token, err +} diff --git a/test/typeparam/issue49309.go b/test/typeparam/issue49309.go new file mode 100644 index 00000000000000..16c97cd451a215 --- /dev/null +++ b/test/typeparam/issue49309.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func genfunc[T any](f func(c T)) { + var r T + + f(r) +} + +func myfunc(c string) { + test2(c) +} + +//go:noinline +func test2(a interface{}) { + _ = a.(string) +} + +func main() { + genfunc(myfunc) +} diff --git a/test/typeparam/issue49421.go b/test/typeparam/issue49421.go new file mode 100644 index 00000000000000..65c32afbf42044 --- /dev/null +++ b/test/typeparam/issue49421.go @@ -0,0 +1,27 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + var a, b foo + bar(a, b) +} + +type foo int + +func (a foo) less(b foo) bool { + return a < b +} + +type lesser[T any] interface { + less(T) bool + comparable +} + +func bar[T lesser[T]](a, b T) { + a.less(b) +} diff --git a/test/typeparam/issue49432.go b/test/typeparam/issue49432.go new file mode 100644 index 00000000000000..d522b2281062da --- /dev/null +++ b/test/typeparam/issue49432.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2021 The Go Authors. 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 Handler func(in ...interface{}) + +type Foo[T any] struct{} + +func (b *Foo[T]) Bar(in ...interface{}) {} + +func (b *Foo[T]) Init() { + _ = Handler(b.Bar) +} + +func main() { + c := &Foo[int]{} + c.Init() +} diff --git a/test/typeparam/issue49497.dir/a.go b/test/typeparam/issue49497.dir/a.go new file mode 100644 index 00000000000000..86062d446ffff6 --- /dev/null +++ b/test/typeparam/issue49497.dir/a.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. 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 F[T any]() A[T] { + var x A[T] + return x +} + +type A[T any] struct { + b B[T] +} + +func (a A[T]) M() C[T] { + return C[T]{ + B: a.b, + } +} + +type B[T any] struct{} + +type C[T any] struct { + B B[T] +} diff --git a/test/typeparam/issue49497.dir/main.go b/test/typeparam/issue49497.dir/main.go new file mode 100644 index 00000000000000..e74dae08598ced --- /dev/null +++ b/test/typeparam/issue49497.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + a.F[string]() +} diff --git a/test/typeparam/issue49497.go b/test/typeparam/issue49497.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue49497.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49516.go b/test/typeparam/issue49516.go new file mode 100644 index 00000000000000..11b460df92a8eb --- /dev/null +++ b/test/typeparam/issue49516.go @@ -0,0 +1,26 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type Q[T any] struct { + s []T +} + +func (q *Q[T]) Push(v ...T) { + q.s = append(q.s, v...) +} + +func pushN(push func(*Q[int], ...int), n int) { + var q Q[int] + for i := 0; i < n; i++ { + push(&q, i) + } +} + +func f() { + pushN((*Q[int]).Push, 100) +} diff --git a/test/typeparam/issue49524.dir/a.go b/test/typeparam/issue49524.dir/a.go new file mode 100644 index 00000000000000..f40075e953ee6a --- /dev/null +++ b/test/typeparam/issue49524.dir/a.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. 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 F[T any]() { +} diff --git a/test/typeparam/issue49524.dir/main.go b/test/typeparam/issue49524.dir/main.go new file mode 100644 index 00000000000000..8787e7ea69b365 --- /dev/null +++ b/test/typeparam/issue49524.dir/main.go @@ -0,0 +1,11 @@ +package main + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +import "./a" + +func main() { + a.F[int]() +} diff --git a/test/typeparam/issue49524.go b/test/typeparam/issue49524.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue49524.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49536.dir/a.go b/test/typeparam/issue49536.dir/a.go new file mode 100644 index 00000000000000..a95ad60812cbaa --- /dev/null +++ b/test/typeparam/issue49536.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. 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 F() interface{} { return new(T[int]) } + +type T[P any] int + +func (x *T[P]) One() int { return x.Two() } +func (x *T[P]) Two() int { return 0 } diff --git a/test/typeparam/issue49536.dir/b.go b/test/typeparam/issue49536.dir/b.go new file mode 100644 index 00000000000000..b08a77b9de1c2d --- /dev/null +++ b/test/typeparam/issue49536.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import "./a" + +var _ = a.F() diff --git a/test/typeparam/issue49536.go b/test/typeparam/issue49536.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue49536.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49538.go b/test/typeparam/issue49538.go new file mode 100644 index 00000000000000..cb22a06e2d203c --- /dev/null +++ b/test/typeparam/issue49538.go @@ -0,0 +1,23 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type I interface { + M(interface{}) +} + +type a[T any] struct{} + +func (a[T]) M(interface{}) {} + +func f[T I](t *T) { + (*t).M(t) +} + +func g() { + f(&a[int]{}) +} diff --git a/test/typeparam/issue49547.go b/test/typeparam/issue49547.go new file mode 100644 index 00000000000000..6d359baa3132ae --- /dev/null +++ b/test/typeparam/issue49547.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +type foo int + +func main() { + want := "main.F[main.foo]" + got := fmt.Sprintf("%T", F[foo]{}) + if got != want { + fmt.Printf("want: %s, got: %s\n", want, got) + } +} + +type F[T any] struct { +} diff --git a/test/typeparam/issue49611.go b/test/typeparam/issue49611.go new file mode 100644 index 00000000000000..879e4d2e900b75 --- /dev/null +++ b/test/typeparam/issue49611.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2021 The Go Authors. 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[T any]() { + var () +} diff --git a/test/typeparam/issue49659.dir/a.go b/test/typeparam/issue49659.dir/a.go new file mode 100644 index 00000000000000..718bc0c5fce5d7 --- /dev/null +++ b/test/typeparam/issue49659.dir/a.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A[T any] struct { + a int +} + +func (a A[T]) F() { + _ = &a.a +} diff --git a/test/typeparam/issue49659.dir/b.go b/test/typeparam/issue49659.dir/b.go new file mode 100644 index 00000000000000..4818a42a486f37 --- /dev/null +++ b/test/typeparam/issue49659.dir/b.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 B[T any] struct { + v a.A[T] +} + +func (b B[T]) F() { + b.v.F() +} diff --git a/test/typeparam/issue49659.go b/test/typeparam/issue49659.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue49659.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49659b.go b/test/typeparam/issue49659b.go new file mode 100644 index 00000000000000..7e1535e0454a8d --- /dev/null +++ b/test/typeparam/issue49659b.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Testing that AddrTaken logic doesn't cause problems for function instantiations + +package main + +type A[T interface{ []int | [5]int }] struct { + val T +} + +//go:noinline +func (a A[T]) F() { + _ = &a.val[2] +} + +func main() { + var x A[[]int] + x.val = make([]int, 4) + _ = &x.val[3] + x.F() + var y A[[5]int] + _ = &y.val[3] + y.F() +} diff --git a/test/typeparam/issue49667.dir/a.go b/test/typeparam/issue49667.dir/a.go new file mode 100644 index 00000000000000..3b1889f699272d --- /dev/null +++ b/test/typeparam/issue49667.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A[T any] struct { +} + +func (a A[T]) F() { + _ = a +} diff --git a/test/typeparam/issue49667.dir/b.go b/test/typeparam/issue49667.dir/b.go new file mode 100644 index 00000000000000..81cdb80036dc14 --- /dev/null +++ b/test/typeparam/issue49667.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 B[T any] struct { + _ a.A[T] +} diff --git a/test/typeparam/issue49667.dir/main.go b/test/typeparam/issue49667.dir/main.go new file mode 100644 index 00000000000000..f9fa60f3f533a0 --- /dev/null +++ b/test/typeparam/issue49667.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 "./b" + +func main() { + var _ b.B[int] +} diff --git a/test/typeparam/issue49667.go b/test/typeparam/issue49667.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue49667.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue49875.go b/test/typeparam/issue49875.go new file mode 100644 index 00000000000000..3fbe48c8a0904c --- /dev/null +++ b/test/typeparam/issue49875.go @@ -0,0 +1,14 @@ +// compile + +// Copyright 2021 The Go Authors. 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(args ...interface{}) {} + +func g() { + var args []any + f(args...) +} diff --git a/test/typeparam/issue49893.dir/a.go b/test/typeparam/issue49893.dir/a.go new file mode 100644 index 00000000000000..bc810cd3dd5300 --- /dev/null +++ b/test/typeparam/issue49893.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 Option[T any] interface { + ToSeq() Seq[T] +} + +type Seq[T any] []T + +func (r Seq[T]) Find(p func(v T) bool) Option[T] { + panic("") +} diff --git a/test/typeparam/issue49893.dir/b.go b/test/typeparam/issue49893.dir/b.go new file mode 100644 index 00000000000000..b86b5366648914 --- /dev/null +++ b/test/typeparam/issue49893.dir/b.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 Ap1[A, B any] struct { + opt a.Option[A] +} + +type Ap2[A, B any] struct { + opt a.Option[A] +} diff --git a/test/typeparam/issue49893.dir/main.go b/test/typeparam/issue49893.dir/main.go new file mode 100644 index 00000000000000..447212d027648f --- /dev/null +++ b/test/typeparam/issue49893.dir/main.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 ( + "./b" + "fmt" +) + +func main() { + opt := b.Ap1[string, string]{} + fmt.Println(opt) +} diff --git a/test/typeparam/issue49893.go b/test/typeparam/issue49893.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue49893.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50002.go b/test/typeparam/issue50002.go new file mode 100644 index 00000000000000..42d97f54827826 --- /dev/null +++ b/test/typeparam/issue50002.go @@ -0,0 +1,64 @@ +// run + +// Copyright 2021 The Go 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 for cases where certain instantiations of a generic function (F in this +// example) will always fail on a type assertion or mismatch on a type case. + +package main + +import "fmt" + +type S struct{} + +func (S) M() byte { + return 0 +} + +type I[T any] interface { + M() T +} + +func F[T, A any](x I[T], shouldMatch bool) { + switch x.(type) { + case A: + if !shouldMatch { + fmt.Printf("wanted mis-match, got match") + } + default: + if shouldMatch { + fmt.Printf("wanted match, got mismatch") + } + } + + _, ok := x.(A) + if ok != shouldMatch { + fmt.Printf("ok: got %v, wanted %v", ok, shouldMatch) + } + + if !shouldMatch { + defer func() { + if shouldMatch { + fmt.Printf("Shouldn't have panicked") + } + recover() + }() + } + _ = x.(A) + if !shouldMatch { + fmt.Printf("Should have panicked") + } +} + +func main() { + // Test instantiation where the type switch/type asserts can't possibly succeed + // (since string does not implement I[byte]). + F[byte, string](S{}, false) + + // Test instantiation where the type switch/type asserts should succeed + // (since S does implement I[byte]) + F[byte, S](S{}, true) + F[byte, S](I[byte](S{}), true) +} diff --git a/test/typeparam/issue50109.go b/test/typeparam/issue50109.go new file mode 100644 index 00000000000000..30aebb2c948bea --- /dev/null +++ b/test/typeparam/issue50109.go @@ -0,0 +1,105 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type AnyCacher[T any] interface { + // Get an item from the cache. Returns the item or nil, and a bool indicating + // whether the key was found. + Get(k string) (T, bool) + // Add an item to the cache, replacing any existing item. + Set(k string, x T) +} + +// Item ... +type Item[T any] struct { + Object T +} + +// AnyCache implements AnyCacher +type AnyCache[T any] struct { + *anyCache[T] +} + +type anyCache[T any] struct { + items map[string]Item[T] + janitor *janitor[T] // Needed for the failure in the issue +} + +// Set adds an item to the cache, replacing any existing item. +func (c *anyCache[T]) Set(k string, x T) { + c.items[k] = Item[T]{ + Object: x, + } +} + +// Get gets an item from the cache. Returns the item or nil, and a bool indicating +// whether the key was found. +func (c *anyCache[T]) Get(k string) (T, bool) { + // "Inlining" of get and Expired + item, found := c.items[k] + if !found { + var ret T + return ret, false + } + + return item.Object, true +} + +type janitor[T any] struct { + stop chan bool +} + +func newAnyCache[T any](m map[string]Item[T]) *anyCache[T] { + c := &anyCache[T]{ + items: m, + } + return c +} + +// NewAny[T any](...) returns a new AnyCache[T]. +func NewAny[T any]() *AnyCache[T] { + items := make(map[string]Item[T]) + return &AnyCache[T]{newAnyCache(items)} +} + +// NewAnyCacher[T any](...) returns an AnyCacher[T] interface. +func NewAnyCacher[T any]() AnyCacher[T] { + return NewAny[T]() +} + +type MyStruct struct { + Name string +} + +func main() { + // Create a generic cache. + // All items are cached as interface{} so they need to be cast back to their + // original type when retrieved. + // Failure in issue doesn't happen with 'any' replaced by 'interface{}' + c := NewAnyCacher[any]() + + myStruct := &MyStruct{"MySuperStruct"} + + c.Set("MySuperStruct", myStruct) + + myRawCachedStruct, found := c.Get("MySuperStruct") + + if found { + // Casting the retrieved object back to its original type + myCachedStruct := myRawCachedStruct.(*MyStruct) + fmt.Printf("%s", myCachedStruct.Name) + } else { + fmt.Printf("Error: MySuperStruct not found in cache") + } + + // Output: + // MySuperStruct +} diff --git a/test/typeparam/issue50109.out b/test/typeparam/issue50109.out new file mode 100644 index 00000000000000..7d6ecc0c6d2bf0 --- /dev/null +++ b/test/typeparam/issue50109.out @@ -0,0 +1 @@ +MySuperStruct \ No newline at end of file diff --git a/test/typeparam/issue50109b.go b/test/typeparam/issue50109b.go new file mode 100644 index 00000000000000..ee494415ff60d4 --- /dev/null +++ b/test/typeparam/issue50109b.go @@ -0,0 +1,29 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + F[any]() +} + +func F[T any]() I[T] { + return (*S1[T])(nil) +} + +type I[T any] interface{} + +type S1[T any] struct { + *S2[T] +} + +type S2[T any] struct { + S3 *S3[T] +} + +type S3[T any] struct { + x int +} diff --git a/test/typeparam/issue50121.dir/a.go b/test/typeparam/issue50121.dir/a.go new file mode 100644 index 00000000000000..ca11b6b27a2d1a --- /dev/null +++ b/test/typeparam/issue50121.dir/a.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "math/rand" +) + +type Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Builder[T Integer] struct{} + +func (r Builder[T]) New() T { + return T(rand.Int()) +} + +var IntBuilder = Builder[int]{} + +func BuildInt() int { + return IntBuilder.New() +} diff --git a/test/typeparam/issue50121.dir/main.go b/test/typeparam/issue50121.dir/main.go new file mode 100644 index 00000000000000..3978ef4fba8353 --- /dev/null +++ b/test/typeparam/issue50121.dir/main.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. 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" +) + +//go:noinline +func BuildInt() int { + return a.BuildInt() +} + +func main() { + BuildInt() +} diff --git a/test/typeparam/issue50121.go b/test/typeparam/issue50121.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue50121.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50121b.dir/a.go b/test/typeparam/issue50121b.dir/a.go new file mode 100644 index 00000000000000..4ddbb6ea847765 --- /dev/null +++ b/test/typeparam/issue50121b.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. 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 Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Builder[T Integer] struct{} + +func (r Builder[T]) New() T { + return T(42) +} diff --git a/test/typeparam/issue50121b.dir/b.go b/test/typeparam/issue50121b.dir/b.go new file mode 100644 index 00000000000000..efa6cbbc307e37 --- /dev/null +++ b/test/typeparam/issue50121b.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "./a" +) + +var IntBuilder = a.Builder[int]{} diff --git a/test/typeparam/issue50121b.dir/c.go b/test/typeparam/issue50121b.dir/c.go new file mode 100644 index 00000000000000..169135678dad1e --- /dev/null +++ b/test/typeparam/issue50121b.dir/c.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package c + +import ( + "./b" +) + +func BuildInt() int { + return b.IntBuilder.New() +} diff --git a/test/typeparam/issue50121b.dir/d.go b/test/typeparam/issue50121b.dir/d.go new file mode 100644 index 00000000000000..93b40c921e04a7 --- /dev/null +++ b/test/typeparam/issue50121b.dir/d.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package d + +import ( + "./c" +) + +func BuildInt() int { + return c.BuildInt() +} diff --git a/test/typeparam/issue50121b.dir/main.go b/test/typeparam/issue50121b.dir/main.go new file mode 100644 index 00000000000000..33986018506d59 --- /dev/null +++ b/test/typeparam/issue50121b.dir/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "./d" + "fmt" +) + +func main() { + if got, want := d.BuildInt(), 42; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue50121b.go b/test/typeparam/issue50121b.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/issue50121b.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50147.go b/test/typeparam/issue50147.go new file mode 100644 index 00000000000000..f97bace894b8ad --- /dev/null +++ b/test/typeparam/issue50147.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2021 The Go Authors. 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 Foo[T any, U interface{ *T }](x T) { + var _ U = &x +} diff --git a/test/typeparam/issue50177.go b/test/typeparam/issue50177.go new file mode 100644 index 00000000000000..c4858fc6dd1ac6 --- /dev/null +++ b/test/typeparam/issue50177.go @@ -0,0 +1,101 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +type Fn[T any] func(T) +type FnErr[T any] func(T) error + +// Test that local generic types across functions don't conflict, and they also don't +// conflict with local non-generic types and local variables. +func caller0() { + type X[T any] struct { + fn Fn[int] + } + + x := X[int]{func(v int) { fmt.Println(v) }} + x.fn(0) +} + +func caller1(val int) { + type X[T any] struct { + fn FnErr[int] + } + + x := X[int]{func(v int) error { fmt.Println(v); return nil }} + x.fn(0) +} + +func caller1a(val int) { + type X struct { + fn func(float64) error + } + + x := X{func(v float64) error { fmt.Println(v); return nil }} + x.fn(float64(3.2)) +} + +func caller1b(val int) { + type Y struct { + fn func(float64) error + } + + X := Y{func(v float64) error { fmt.Println(v); return nil }} + X.fn(float64(3.2)) +} + +// Test that local generic types within different if clauses don't conflict. +func caller2(val int) { + if val > 2 { + type X[T any] struct { + fn func(v int) float64 + } + + x := X[int]{func(v int) float64 { fmt.Println(v); return 1.5 }} + x.fn(0) + } else { + type X[T any] struct { + fn func(v int) int + } + x := X[int]{func(v int) int { fmt.Println(v); return 5 }} + x.fn(0) + } +} + +// Test that local generic types within different cases don't conflict with each +// other or with local non-generic types or local variables. +func caller3(val int) { + switch val { + case 0: + type X[T any] struct { + fn func(v int) float64 + } + + x := X[int]{func(v int) float64 { fmt.Println(v); return 1.5 }} + x.fn(0) + case 1: + type X[T any] struct { + fn func(v int) int + } + x := X[int]{func(v int) int { fmt.Println(v); return 5 }} + x.fn(0) + case 2: + type X struct { + fn func(v int) bool + } + x := X{func(v int) bool { fmt.Println(v); return false }} + x.fn(0) + case 3: + type Y struct { + fn func(v int) bool + } + X := Y{func(v int) bool { fmt.Println(v); return false }} + X.fn(0) + + } +} diff --git a/test/typeparam/issue50193.go b/test/typeparam/issue50193.go new file mode 100644 index 00000000000000..8b4b84180f6e2f --- /dev/null +++ b/test/typeparam/issue50193.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Complex interface { + ~complex64 | ~complex128 +} + +func zero[T Complex]() T { + return T(0) +} +func pi[T Complex]() T { + return T(3.14) +} +func sqrtN1[T Complex]() T { + return T(-1i) +} + +func main() { + fmt.Println(zero[complex128]()) + fmt.Println(pi[complex128]()) + fmt.Println(sqrtN1[complex128]()) + fmt.Println(zero[complex64]()) + fmt.Println(pi[complex64]()) + fmt.Println(sqrtN1[complex64]()) +} + diff --git a/test/typeparam/issue50193.out b/test/typeparam/issue50193.out new file mode 100644 index 00000000000000..68186222c7fb9d --- /dev/null +++ b/test/typeparam/issue50193.out @@ -0,0 +1,6 @@ +(0+0i) +(3.14+0i) +(0-1i) +(0+0i) +(3.14+0i) +(0-1i) diff --git a/test/typeparam/issue50259.go b/test/typeparam/issue50259.go new file mode 100644 index 00000000000000..50edf8f737527a --- /dev/null +++ b/test/typeparam/issue50259.go @@ -0,0 +1,13 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +var x T[B] + +type T[_ any] struct{} +type A T[B] +type B = T[A] diff --git a/test/typeparam/issue50264.go b/test/typeparam/issue50264.go new file mode 100644 index 00000000000000..1acab87f42a2c7 --- /dev/null +++ b/test/typeparam/issue50264.go @@ -0,0 +1,45 @@ +// run + +// Copyright 2021 The Go Authors. 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 hello struct{} + +func main() { + _ = Some(hello{}) + res := Applicative2(func(a int, b int) int { + return 0 + }) + _ = res +} + +type NoneType[T any] struct{} + +func (r NoneType[T]) Recover() any { + return nil +} + +type Func2[A1, A2, R any] func(a1 A1, a2 A2) R + +func Some[T any](v T) any { + _ = Some2[T](v) + return NoneType[T]{}.Recover() +} + +//go:noinline +func Some2[T any](v T) any { + return v +} + +type Nil struct{} + +type ApplicativeFunctor2[H, HT, A1, A2, R any] struct { + h any +} + +func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] { + return ApplicativeFunctor2[Nil, Nil, A1, A2, R]{Some(Nil{})} +} diff --git a/test/typeparam/issue50317.go b/test/typeparam/issue50317.go new file mode 100644 index 00000000000000..c33c4f061c7d45 --- /dev/null +++ b/test/typeparam/issue50317.go @@ -0,0 +1,15 @@ +// errorcheck + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type S struct{} + +func (S) _[_ any]() {} // ERROR "method must have no type parameters" + +type _ interface { + m[_ any]() // ERROR "method must have no type parameters" +} diff --git a/test/typeparam/issue50417.go b/test/typeparam/issue50417.go new file mode 100644 index 00000000000000..b32e270bdfca8c --- /dev/null +++ b/test/typeparam/issue50417.go @@ -0,0 +1,146 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() {} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. + +/* +type Sf struct { + f int +} + +func f0[P Sf](p P) { + _ = p.f + p.f = 0 +} + +func f0t[P ~struct{ f int }](p P) { + _ = p.f + p.f = 0 +} + +var _ = f0[Sf] +var _ = f0t[Sf] + +func f1[P interface { + ~struct{ f int } + m() +}](p P) { + _ = p.f + p.f = 0 + p.m() +} + +var _ = f1[Sfm] + +type Sm struct{} + +func (Sm) m() {} + +type Sfm struct { + f int +} + +func (Sfm) m() {} + +func f2[P interface { + Sfm + m() +}](p P) { + _ = p.f + p.f = 0 + p.m() +} + +var _ = f2[Sfm] + +// special case: core type is a named pointer type + +type PSfm *Sfm + +func f3[P interface{ PSfm }](p P) { + _ = p.f + p.f = 0 +} + +var _ = f3[PSfm] + +// special case: core type is an unnamed pointer type + +func f4[P interface{ *Sfm }](p P) { + _ = p.f + p.f = 0 +} + +var _ = f4[*Sfm] + +type A int +type B int +type C float64 + +type Int interface { + *Sf | A + *Sf | B +} + +func f5[P Int](p P) { + _ = p.f + p.f = 0 +} + +var _ = f5[*Sf] + +type Int2 interface { + *Sf | A + any + *Sf | C +} + +func f6[P Int2](p P) { + _ = p.f + p.f = 0 +} + +var _ = f6[*Sf] + +type Int3 interface { + Sf + ~struct{ f int } +} + +func f7[P Int3](p P) { + _ = p.f + p.f = 0 +} + +var _ = f7[Sf] + +type Em1 interface { + *Sf | A +} + +type Em2 interface { + *Sf | B +} + +type Int4 interface { + Em1 + Em2 + any +} + +func f8[P Int4](p P) { + _ = p.f + p.f = 0 +} + +var _ = f8[*Sf] +*/ diff --git a/test/typeparam/issue50417b.go b/test/typeparam/issue50417b.go new file mode 100644 index 00000000000000..1c803b09bdff09 --- /dev/null +++ b/test/typeparam/issue50417b.go @@ -0,0 +1,58 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() {} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. + +/* +import "fmt" + +type MyStruct struct { + b1, b2 string + E +} + +type E struct { + val int +} + +type C interface { + ~struct { + b1, b2 string + E + } +} + +func f[T C]() T { + var x T = T{ + b1: "a", + b2: "b", + } + + if got, want := x.b2, "b"; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + x.b1 = "y" + x.val = 5 + + return x +} + +func main() { + x := f[MyStruct]() + if got, want := x.b1, "y"; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + if got, want := x.val, 5; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} +*/ diff --git a/test/typeparam/issue50419.go b/test/typeparam/issue50419.go new file mode 100644 index 00000000000000..dfe55f94459913 --- /dev/null +++ b/test/typeparam/issue50419.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2022 The Go 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 type substitution works correctly even for a method of a generic type +// that has multiple blank type params. + +package main + +import ( + "fmt" +) + +func main() { + foo := &Foo[string, int]{ + valueA: "i am a string", + valueB: 123, + } + if got, want := fmt.Sprintln(foo), "i am a string 123\n"; got != want { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } +} + +type Foo[T1 any, T2 any] struct { + valueA T1 + valueB T2 +} + +func (f *Foo[_, _]) String() string { + return fmt.Sprintf("%v %v", f.valueA, f.valueB) +} diff --git a/test/typeparam/issue50437.dir/a.go b/test/typeparam/issue50437.dir/a.go new file mode 100644 index 00000000000000..4a136b52ae2d00 --- /dev/null +++ b/test/typeparam/issue50437.dir/a.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Go Authors. 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 MarshalOptions struct { + *typedArshalers[MarshalOptions] +} + +func Marshal(in interface{}) (out []byte, err error) { + return MarshalOptions{}.Marshal(in) +} + +func (mo MarshalOptions) Marshal(in interface{}) (out []byte, err error) { + err = mo.MarshalNext(in) + return nil, err +} + +func (mo MarshalOptions) MarshalNext(in interface{}) error { + a := new(arshaler) + a.marshal = func(MarshalOptions) error { return nil } + return a.marshal(mo) +} + +type arshaler struct { + marshal func(MarshalOptions) error +} + +type typedArshalers[Options any] struct { + m M +} + +func (a *typedArshalers[Options]) lookup(fnc func(Options) error) (func(Options) error, bool) { + a.m.Load(nil) + return fnc, false +} + +type M struct {} + +func (m *M) Load(key any) (value any, ok bool) { + return +} diff --git a/test/typeparam/issue50437.dir/b.go b/test/typeparam/issue50437.dir/b.go new file mode 100644 index 00000000000000..afddc3f3307493 --- /dev/null +++ b/test/typeparam/issue50437.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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.Marshal(map[int]int{}) +} diff --git a/test/typeparam/issue50437.go b/test/typeparam/issue50437.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue50437.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50481b.dir/b.go b/test/typeparam/issue50481b.dir/b.go new file mode 100644 index 00000000000000..d458357c51f2f6 --- /dev/null +++ b/test/typeparam/issue50481b.dir/b.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. 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 "fmt" + +type Foo[T1 ~string, T2 ~int] struct { + ValueA T1 + ValueB T2 +} + +func (f *Foo[_, _]) String() string { + return fmt.Sprintf("%v %v", f.ValueA, f.ValueB) +} diff --git a/test/typeparam/issue50481b.dir/main.go b/test/typeparam/issue50481b.dir/main.go new file mode 100644 index 00000000000000..6a5067c9fb2d86 --- /dev/null +++ b/test/typeparam/issue50481b.dir/main.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go 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 type substitution and export/import works correctly even for a method of +// a generic type that has multiple blank type params. + +package main + +import ( + "./b" + "fmt" +) + +func main() { + foo := &b.Foo[string, int]{ + ValueA: "i am a string", + ValueB: 123, + } + if got, want := fmt.Sprintln(foo), "i am a string 123\n"; got != want { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } +} diff --git a/test/typeparam/issue50481b.go b/test/typeparam/issue50481b.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue50481b.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50481c.dir/a.go b/test/typeparam/issue50481c.dir/a.go new file mode 100644 index 00000000000000..384ba23f989ed0 --- /dev/null +++ b/test/typeparam/issue50481c.dir/a.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A interface { + int | int64 +} + +type B interface { + string +} + +type C interface { + String() string +} + +type Myint int + +func (i Myint) String() string { + return "aa" +} + +type T[P A, _ C, _ B] int + +func (v T[P, Q, R]) test() { + var r Q + r.String() +} diff --git a/test/typeparam/issue50481c.dir/main.go b/test/typeparam/issue50481c.dir/main.go new file mode 100644 index 00000000000000..178542bc380eb3 --- /dev/null +++ b/test/typeparam/issue50481c.dir/main.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go 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 type substitution works and export/import works correctly even for a +// generic type that has multiple blank type params. + +package main + +import ( + "./a" + "fmt" +) + +func main() { + var x a.T[int, a.Myint, string] + fmt.Printf("%v\n", x) +} diff --git a/test/typeparam/issue50481c.go b/test/typeparam/issue50481c.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue50481c.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50481c.out b/test/typeparam/issue50481c.out new file mode 100644 index 00000000000000..573541ac9702dd --- /dev/null +++ b/test/typeparam/issue50481c.out @@ -0,0 +1 @@ +0 diff --git a/test/typeparam/issue50485.dir/a.go b/test/typeparam/issue50485.dir/a.go new file mode 100644 index 00000000000000..3a7c71a711e204 --- /dev/null +++ b/test/typeparam/issue50485.dir/a.go @@ -0,0 +1,239 @@ +package a + +import "fmt" + +type ImplicitOrd interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +func LessGiven[T ImplicitOrd]() Ord[T] { + return LessFunc[T](func(a, b T) bool { + return a < b + }) +} + +type Eq[T any] interface { + Eqv(a T, b T) bool +} + +type Ord[T any] interface { + Eq[T] + Less(a T, b T) bool +} + +type LessFunc[T any] func(a, b T) bool + +func (r LessFunc[T]) Eqv(a, b T) bool { + return r(a, b) == false && r(b, a) == false +} + +func (r LessFunc[T]) Less(a, b T) bool { + return r(a, b) +} + +type Option[T any] struct { + v *T +} + +func (r Option[T]) IsDefined() bool { + return r.v != nil +} + +func (r Option[T]) IsEmpty() bool { + return !r.IsDefined() +} + +func (r Option[T]) Get() T { + return *r.v +} + +func (r Option[T]) String() string { + if r.IsDefined() { + return fmt.Sprintf("Some(%v)", r.v) + } else { + return "None" + } +} + +func (r Option[T]) OrElse(t T) T { + if r.IsDefined() { + return *r.v + } + return t +} + +func (r Option[T]) Recover(f func() T) Option[T] { + if r.IsDefined() { + return r + } + t := f() + return Option[T]{&t} +} + +type Func1[A1, R any] func(a1 A1) R + +type Func2[A1, A2, R any] func(a1 A1, a2 A2) R + +func (r Func2[A1, A2, R]) Curried() Func1[A1, Func1[A2, R]] { + return func(a1 A1) Func1[A2, R] { + return Func1[A2, R](func(a2 A2) R { + return r(a1, a2) + }) + } +} + +type HList interface { + sealed() +} + +// Header is constrains interface type, enforce Head type of Cons is HT +type Header[HT any] interface { + HList + Head() HT +} + +// Cons means H :: T +// zero value of Cons[H,T] is not allowed. +// so Cons defined as interface type +type Cons[H any, T HList] interface { + HList + Head() H + Tail() T +} + +type Nil struct { +} + +func (r Nil) Head() Nil { + return r +} + +func (r Nil) Tail() Nil { + return r +} + +func (r Nil) String() string { + return "Nil" +} + +func (r Nil) sealed() { + +} + +type hlistImpl[H any, T HList] struct { + head H + tail T +} + +func (r hlistImpl[H, T]) Head() H { + return r.head +} + +func (r hlistImpl[H, T]) Tail() T { + return r.tail +} + +func (r hlistImpl[H, T]) String() string { + return fmt.Sprintf("%v :: %v", r.head, r.tail) +} + +func (r hlistImpl[H, T]) sealed() { + +} + +func hlist[H any, T HList](h H, t T) Cons[H, T] { + return hlistImpl[H, T]{h, t} +} + +func Concat[H any, T HList](h H, t T) Cons[H, T] { + return hlist(h, t) +} + +func Empty() Nil { + return Nil{} +} +func Some[T any](v T) Option[T] { + return Option[T]{}.Recover(func() T { + return v + }) +} + +func None[T any]() Option[T] { + return Option[T]{} +} + +func Ap[T, U any](t Option[Func1[T, U]], a Option[T]) Option[U] { + return FlatMap(t, func(f Func1[T, U]) Option[U] { + return Map(a, f) + }) +} + +func Map[T, U any](opt Option[T], f func(v T) U) Option[U] { + return FlatMap(opt, func(v T) Option[U] { + return Some(f(v)) + }) +} + +func FlatMap[T, U any](opt Option[T], fn func(v T) Option[U]) Option[U] { + if opt.IsDefined() { + return fn(opt.Get()) + } + return None[U]() +} + +type ApplicativeFunctor1[H Header[HT], HT, A, R any] struct { + h Option[H] + fn Option[Func1[A, R]] +} + +func (r ApplicativeFunctor1[H, HT, A, R]) ApOption(a Option[A]) Option[R] { + return Ap(r.fn, a) +} + +func (r ApplicativeFunctor1[H, HT, A, R]) Ap(a A) Option[R] { + return r.ApOption(Some(a)) +} + +func Applicative1[A, R any](fn Func1[A, R]) ApplicativeFunctor1[Nil, Nil, A, R] { + return ApplicativeFunctor1[Nil, Nil, A, R]{Some(Empty()), Some(fn)} +} + +type ApplicativeFunctor2[H Header[HT], HT, A1, A2, R any] struct { + h Option[H] + fn Option[Func1[A1, Func1[A2, R]]] +} + +func (r ApplicativeFunctor2[H, HT, A1, A2, R]) ApOption(a Option[A1]) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { + + nh := FlatMap(r.h, func(hv H) Option[Cons[A1, H]] { + return Map(a, func(av A1) Cons[A1, H] { + return Concat(av, hv) + }) + }) + + return ApplicativeFunctor1[Cons[A1, H], A1, A2, R]{nh, Ap(r.fn, a)} +} +func (r ApplicativeFunctor2[H, HT, A1, A2, R]) Ap(a A1) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { + + return r.ApOption(Some(a)) + +} + +func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] { + return ApplicativeFunctor2[Nil, Nil, A1, A2, R]{Some(Empty()), Some(fn.Curried())} +} +func OrdOption[T any](m Ord[T]) Ord[Option[T]] { + return LessFunc[Option[T]](func(t1 Option[T], t2 Option[T]) bool { + if !t1.IsDefined() && !t2.IsDefined() { + return false + } + return Applicative2(m.Less).ApOption(t1).ApOption(t2).OrElse(!t1.IsDefined()) + }) +} + +func Given[T ImplicitOrd]() Ord[T] { + return LessGiven[T]() +} diff --git a/test/typeparam/issue50485.dir/main.go b/test/typeparam/issue50485.dir/main.go new file mode 100644 index 00000000000000..7181b937fd6c0e --- /dev/null +++ b/test/typeparam/issue50485.dir/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "./a" +) + +func main() { + _ = a.OrdOption(a.Given[int]()) +} diff --git a/test/typeparam/issue50485.go b/test/typeparam/issue50485.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue50485.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50486.dir/goerror_fp.go b/test/typeparam/issue50486.dir/goerror_fp.go new file mode 100644 index 00000000000000..fec9095f79678f --- /dev/null +++ b/test/typeparam/issue50486.dir/goerror_fp.go @@ -0,0 +1,75 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goerror_fp + +type Seq[T any] []T + +func (r Seq[T]) Size() int { + return len(r) +} + +func (r Seq[T]) Append(items ...T) Seq[T] { + tail := Seq[T](items) + ret := make(Seq[T], r.Size()+tail.Size()) + + for i := range r { + ret[i] = r[i] + } + + for i := range tail { + ret[i+r.Size()] = tail[i] + } + + return ret +} + +func (r Seq[T]) Iterator() Iterator[T] { + idx := 0 + + return Iterator[T]{ + IsHasNext: func() bool { + return idx < r.Size() + }, + GetNext: func() T { + ret := r[idx] + idx++ + return ret + }, + } +} + +type Iterator[T any] struct { + IsHasNext func() bool + GetNext func() T +} + +func (r Iterator[T]) ToSeq() Seq[T] { + ret := Seq[T]{} + for r.HasNext() { + ret = append(ret, r.Next()) + } + return ret +} + +func (r Iterator[T]) Map(f func(T) any) Iterator[any] { + return MakeIterator(r.HasNext, func() any { + return f(r.Next()) + }) +} + +func (r Iterator[T]) HasNext() bool { + return r.IsHasNext() +} + +func (r Iterator[T]) Next() T { + return r.GetNext() +} + +func MakeIterator[T any](has func() bool, next func() T) Iterator[T] { + return Iterator[T]{ + IsHasNext: has, + GetNext: next, + } +} diff --git a/test/typeparam/issue50486.dir/main.go b/test/typeparam/issue50486.dir/main.go new file mode 100644 index 00000000000000..c2c8eea73d7bcc --- /dev/null +++ b/test/typeparam/issue50486.dir/main.go @@ -0,0 +1,16 @@ +package main + +import fp "./goerror_fp" + +func Fold[A, B any](zero B, a A, f func(B, A) B) B { + return f(zero, a) +} + +func main() { + + var v any = "hello" + Fold(fp.Seq[any]{}, v, func(seq fp.Seq[any], v any) fp.Seq[any] { + return seq.Append(v) + }) + +} diff --git a/test/typeparam/issue50486.go b/test/typeparam/issue50486.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue50486.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50552.dir/a.go b/test/typeparam/issue50552.dir/a.go new file mode 100644 index 00000000000000..89b9bcb87775ea --- /dev/null +++ b/test/typeparam/issue50552.dir/a.go @@ -0,0 +1,16 @@ +package a + +type Builder[T any] struct{} + +func (r Builder[T]) New() T { + var v T + return v +} + +func (r Builder[T]) New2() T { + return r.New() +} + +func BuildInt() int { + return Builder[int]{}.New() +} diff --git a/test/typeparam/issue50552.dir/main.go b/test/typeparam/issue50552.dir/main.go new file mode 100644 index 00000000000000..0ff2ed360877ac --- /dev/null +++ b/test/typeparam/issue50552.dir/main.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. 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" + "fmt" +) + +func BuildInt() int { + return a.BuildInt() +} + +func main() { + if got, want := BuildInt(), 0; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue50552.go b/test/typeparam/issue50552.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/issue50552.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50561.dir/diameter.go b/test/typeparam/issue50561.dir/diameter.go new file mode 100644 index 00000000000000..2bfe92405da520 --- /dev/null +++ b/test/typeparam/issue50561.dir/diameter.go @@ -0,0 +1,86 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diameter + +type Runnable interface { + Run() +} + +// RunnableFunc is converter which converts function to Runnable interface +type RunnableFunc func() + +// Run is Runnable.Run +func (r RunnableFunc) Run() { + r() +} + +type Executor interface { + ExecuteUnsafe(runnable Runnable) +} + +type Promise[T any] interface { + Future() Future[T] + Success(value T) bool + Failure(err error) bool + IsCompleted() bool + Complete(result Try[T]) bool +} + +type Future[T any] interface { + OnFailure(cb func(err error), ctx ...Executor) + OnSuccess(cb func(success T), ctx ...Executor) + Foreach(f func(v T), ctx ...Executor) + OnComplete(cb func(try Try[T]), ctx ...Executor) + IsCompleted() bool + // Value() Option[Try[T]] + Failed() Future[error] + Recover(f func(err error) T, ctx ...Executor) Future[T] + RecoverWith(f func(err error) Future[T], ctx ...Executor) Future[T] +} + +type Try[T any] struct { + v *T + err error +} + +func (r Try[T]) IsSuccess() bool { + return r.v != nil +} + +type ByteBuffer struct { + pos int + buf []byte + underflow error +} + +// InboundHandler is extends of uclient.NetInboundHandler +type InboundHandler interface { + OriginHost() string + OriginRealm() string +} + +type transactionID struct { + hopID uint32 + endID uint32 +} + +type roundTripper struct { + promise map[transactionID]Promise[*ByteBuffer] + host string + realm string +} + +func (r *roundTripper) OriginHost() string { + return r.host +} +func (r *roundTripper) OriginRealm() string { + return r.realm +} + +func NewInboundHandler(host string, realm string, productName string) InboundHandler { + ret := &roundTripper{promise: make(map[transactionID]Promise[*ByteBuffer]), host: host, realm: realm} + + return ret +} diff --git a/test/typeparam/issue50561.dir/main.go b/test/typeparam/issue50561.dir/main.go new file mode 100644 index 00000000000000..3e656bde32dc81 --- /dev/null +++ b/test/typeparam/issue50561.dir/main.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. 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 ( + "./diameter" +) + +func main() { + diameter.NewInboundHandler("hello", "world", "hi") +} diff --git a/test/typeparam/issue50561.go b/test/typeparam/issue50561.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue50561.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50598.dir/a0.go b/test/typeparam/issue50598.dir/a0.go new file mode 100644 index 00000000000000..61d353e462cd69 --- /dev/null +++ b/test/typeparam/issue50598.dir/a0.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a0 + +type Builder[T any] struct{} + +func (r Builder[T]) New1() T { + var v T + return v +} + +func (r Builder[T]) New2() T { + var v T + return v +} + +type IntBuilder struct{} + +func (b IntBuilder) New() int { + return Builder[int]{}.New2() +} diff --git a/test/typeparam/issue50598.dir/a1.go b/test/typeparam/issue50598.dir/a1.go new file mode 100644 index 00000000000000..36624b4bd67ac8 --- /dev/null +++ b/test/typeparam/issue50598.dir/a1.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a1 + +import "./a0" + +func New() int { + return a0.IntBuilder{}.New() +} diff --git a/test/typeparam/issue50598.dir/a2.go b/test/typeparam/issue50598.dir/a2.go new file mode 100644 index 00000000000000..c28be66f6bab28 --- /dev/null +++ b/test/typeparam/issue50598.dir/a2.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a2 + +import "./a0" + +func New() int { + return a0.Builder[int]{}.New1() +} diff --git a/test/typeparam/issue50598.dir/main.go b/test/typeparam/issue50598.dir/main.go new file mode 100644 index 00000000000000..b0b6844ccc2858 --- /dev/null +++ b/test/typeparam/issue50598.dir/main.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "./a1" + "./a2" +) + +func New() int { + return a1.New() + a2.New() +} + +func main() { + if got, want := New(), 0; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } +} diff --git a/test/typeparam/issue50598.go b/test/typeparam/issue50598.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue50598.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50642.go b/test/typeparam/issue50642.go new file mode 100644 index 00000000000000..d2d4a6690750d2 --- /dev/null +++ b/test/typeparam/issue50642.go @@ -0,0 +1,63 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +type Temp[T any] struct { +} + +var temp, temp1 any +var ch any + +func (it Temp[T]) HasNext() bool { + var ok bool + temp1 = <-ch.(chan T) + // test conversion of T to interface{} during an OAS2RECV + temp, ok = <-ch.(chan T) + return ok +} + +type MyInt int + +func (i MyInt) String() string { + return "a" +} + +type Stringer interface { + String() string +} + +type Temp2[T Stringer] struct { +} + +var temp2 Stringer + +func (it Temp2[T]) HasNext() string { + var x map[int]T + + var ok bool + // test conversion of T to Stringer during an OAS2MAPR + temp2, ok = x[43] + _ = ok + return temp2.String() +} + +func main() { + ch1 := make(chan int, 2) + ch1 <- 5 + ch1 <- 6 + ch = ch1 + iter := Temp[int]{} + iter.HasNext() + + iter2 := Temp2[MyInt]{} + if got, want := iter2.HasNext(), "a"; got != want { + panic(fmt.Sprintf("got %v, want %v", got, want)) + } + +} diff --git a/test/typeparam/issue50690a.go b/test/typeparam/issue50690a.go new file mode 100644 index 00000000000000..6691af0a073036 --- /dev/null +++ b/test/typeparam/issue50690a.go @@ -0,0 +1,75 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +// Numeric expresses a type constraint satisfied by any numeric type. +type Numeric interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~float32 | ~float64 | + ~complex64 | ~complex128 +} + +// Sum returns the sum of the provided arguments. +func Sum[T Numeric](args ...T) T { + var sum T + for i := 0; i < len(args); i++ { + sum += args[i] + } + return sum +} + +// Ledger is an identifiable, financial record. +type Ledger[T ~string, K Numeric] struct { + // ID identifies the ledger. + ID_ T + + // Amounts is a list of monies associated with this ledger. + Amounts_ []K + + // SumFn is a function that can be used to sum the amounts + // in this ledger. + SumFn_ func(...K) K +} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. +// Use accessor methods instead. + +func (l Ledger[T, _]) ID() T { return l.ID_ } +func (l Ledger[_, K]) Amounts() []K { return l.Amounts_ } +func (l Ledger[_, K]) SumFn() func(...K) K { return l.SumFn_ } + +func PrintLedger[ + T ~string, + K Numeric, + L interface { + ~struct { + ID_ T + Amounts_ []K + SumFn_ func(...K) K + } + ID() T + Amounts() []K + SumFn() func(...K) K + }, +](l L) { + fmt.Printf("%s has a sum of %v\n", l.ID(), l.SumFn()(l.Amounts()...)) +} + +func main() { + PrintLedger(Ledger[string, int]{ + ID_: "fake", + Amounts_: []int{1, 2, 3}, + SumFn_: Sum[int], + }) +} diff --git a/test/typeparam/issue50690a.out b/test/typeparam/issue50690a.out new file mode 100644 index 00000000000000..293276716f62d2 --- /dev/null +++ b/test/typeparam/issue50690a.out @@ -0,0 +1 @@ +fake has a sum of 6 diff --git a/test/typeparam/issue50690b.go b/test/typeparam/issue50690b.go new file mode 100644 index 00000000000000..09c84e089dfdb7 --- /dev/null +++ b/test/typeparam/issue50690b.go @@ -0,0 +1,51 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Printer[T ~string] struct { + PrintFn func(T) +} + +func Print[T ~string](s T) { + fmt.Println(s) +} + +func PrintWithPrinter[T ~string, S interface { + ~struct { + ID T + PrintFn_ func(T) + } + PrintFn() func(T) +}](message T, obj S) { + obj.PrintFn()(message) +} + +type PrintShop[T ~string] struct { + ID T + PrintFn_ func(T) +} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. +// Use accessor method instead. + +func (s PrintShop[T]) PrintFn() func(T) { return s.PrintFn_ } + +func main() { + PrintWithPrinter( + "Hello, world.", + PrintShop[string]{ + ID: "fake", + PrintFn_: Print[string], + }, + ) +} diff --git a/test/typeparam/issue50690b.out b/test/typeparam/issue50690b.out new file mode 100644 index 00000000000000..f75ba05f340c51 --- /dev/null +++ b/test/typeparam/issue50690b.out @@ -0,0 +1 @@ +Hello, world. diff --git a/test/typeparam/issue50690c.go b/test/typeparam/issue50690c.go new file mode 100644 index 00000000000000..2db1487ecb188a --- /dev/null +++ b/test/typeparam/issue50690c.go @@ -0,0 +1,50 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Printer[T ~string] struct { + PrintFn func(T) +} + +func Print[T ~string](s T) { + fmt.Println(s) +} + +func PrintWithPrinter[T ~string, S interface { + ~struct { + ID T + PrintFn_ func(T) + } + PrintFn() func(T) +}](message T, obj S) { + obj.PrintFn()(message) +} + +func main() { + PrintWithPrinter( + "Hello, world.", + StructWithPrinter{ID: "fake", PrintFn_: Print[string]}, + ) +} + +type StructWithPrinter struct { + ID string + PrintFn_ func(string) +} + +// Field accesses through type parameters are disabled +// until we have a more thorough understanding of the +// implications on the spec. See issue #51576. +// Use accessor method instead. + +func (s StructWithPrinter) PrintFn() func(string) { + return s.PrintFn_ +} diff --git a/test/typeparam/issue50690c.out b/test/typeparam/issue50690c.out new file mode 100644 index 00000000000000..f75ba05f340c51 --- /dev/null +++ b/test/typeparam/issue50690c.out @@ -0,0 +1 @@ +Hello, world. diff --git a/test/typeparam/issue50833.go b/test/typeparam/issue50833.go new file mode 100644 index 00000000000000..fe729b1f289f35 --- /dev/null +++ b/test/typeparam/issue50833.go @@ -0,0 +1,23 @@ +// run + +// Copyright 2022 The Go Authors. 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{ f int } + PS *S +) + +func a() []*S { return []*S{{f: 1}} } +func b() []PS { return []PS{{f: 1}} } + +func c[P *S]() []P { return []P{{f: 1}} } +func d[P PS]() []P { return []P{{f: 1}} } + +func main() { + c[*S]() + d[PS]() +} diff --git a/test/typeparam/issue50841.dir/a.go b/test/typeparam/issue50841.dir/a.go new file mode 100644 index 00000000000000..37e023370187e3 --- /dev/null +++ b/test/typeparam/issue50841.dir/a.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. 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 Marshal[foobar any]() { + _ = NewEncoder[foobar]() +} + +func NewEncoder[foobar any]() *Encoder[foobar] { + return nil +} + +type Encoder[foobar any] struct { +} + +func (e *Encoder[foobar]) EncodeToken(t Token[foobar]) { + +} + +type Token[foobar any] any diff --git a/test/typeparam/issue50841.dir/b.go b/test/typeparam/issue50841.dir/b.go new file mode 100644 index 00000000000000..38e3de3a6bb6aa --- /dev/null +++ b/test/typeparam/issue50841.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. 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.Marshal[int]() +} diff --git a/test/typeparam/issue50841.go b/test/typeparam/issue50841.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue50841.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue50993.go b/test/typeparam/issue50993.go new file mode 100644 index 00000000000000..4d459fd04c1634 --- /dev/null +++ b/test/typeparam/issue50993.go @@ -0,0 +1,35 @@ +// compile -d=checkptr + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "sync/atomic" + "unsafe" +) + +type Node[T any] struct { + Next *Node[T] + // Prev *Node[T] +} + +func LoadPointer[T any](addr **T) (val *T) { + return (*T)( + atomic.LoadPointer( + (*unsafe.Pointer)(unsafe.Pointer(addr)), + )) +} + +func (q *Node[T]) Pop() { + var tail, head *Node[T] + if head == LoadPointer(&tail) { + } +} + +func main() { + ch := Node[uint64]{} + ch.Pop() +} diff --git a/test/typeparam/issue51219.dir/a.go b/test/typeparam/issue51219.dir/a.go new file mode 100644 index 00000000000000..29670df0d3a764 --- /dev/null +++ b/test/typeparam/issue51219.dir/a.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. 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 I is the first basic test for the issue, which relates to a type that is recursive +// via a type constraint. (In this test, I -> IConstraint -> MyStruct -> I.) +type JsonRaw []byte + +type MyStruct struct { + x *I[JsonRaw] +} + +type IConstraint interface { + JsonRaw | MyStruct +} + +type I[T IConstraint] struct { +} diff --git a/test/typeparam/issue51219.dir/main.go b/test/typeparam/issue51219.dir/main.go new file mode 100644 index 00000000000000..14c6d179d2b38e --- /dev/null +++ b/test/typeparam/issue51219.dir/main.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. 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" + "fmt" +) + +func main() { + var x a.I[a.JsonRaw] + + fmt.Printf("%v\n", x) +} diff --git a/test/typeparam/issue51219.go b/test/typeparam/issue51219.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue51219.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51219.out b/test/typeparam/issue51219.out new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/test/typeparam/issue51219.out @@ -0,0 +1 @@ +{} diff --git a/test/typeparam/issue51219b.dir/a.go b/test/typeparam/issue51219b.dir/a.go new file mode 100644 index 00000000000000..19049406a6d9e9 --- /dev/null +++ b/test/typeparam/issue51219b.dir/a.go @@ -0,0 +1,37 @@ +// Copyright 2022 The Go Authors. 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 Interaction[DataT InteractionDataConstraint] struct { +} + +type InteractionDataConstraint interface { + []byte | + UserCommandInteractionData +} + +type UserCommandInteractionData struct { + resolvedInteractionWithOptions +} + +type resolvedInteractionWithOptions struct { + Resolved Resolved `json:"resolved,omitempty"` +} + +type Resolved struct { + Users ResolvedData[User] `json:"users,omitempty"` +} + +type ResolvedData[T ResolvedDataConstraint] map[uint64]T + +type ResolvedDataConstraint interface { + User | Message +} + +type User struct{} + +type Message struct { + Interaction *Interaction[[]byte] `json:"interaction,omitempty"` +} diff --git a/test/typeparam/issue51219b.dir/b.go b/test/typeparam/issue51219b.dir/b.go new file mode 100644 index 00000000000000..8413d666b7e2cd --- /dev/null +++ b/test/typeparam/issue51219b.dir/b.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. 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" +) + +// InteractionRequest is an incoming request Interaction +type InteractionRequest[T a.InteractionDataConstraint] struct { + a.Interaction[T] +} diff --git a/test/typeparam/issue51219b.dir/p.go b/test/typeparam/issue51219b.dir/p.go new file mode 100644 index 00000000000000..9f8b840d48b1b7 --- /dev/null +++ b/test/typeparam/issue51219b.dir/p.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import ( + "./b" +) + +// ResponseWriterMock mocks corde's ResponseWriter interface +type ResponseWriterMock struct { + x b.InteractionRequest[[]byte] +} diff --git a/test/typeparam/issue51219b.go b/test/typeparam/issue51219b.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue51219b.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51232.go b/test/typeparam/issue51232.go new file mode 100644 index 00000000000000..0d25e1863d7007 --- /dev/null +++ b/test/typeparam/issue51232.go @@ -0,0 +1,31 @@ +// errorcheck + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type RC[RG any] interface { + ~[]RG +} + +type Fn[RCT RC[RG], RG any] func(RCT) + +type F[RCT RC[RG], RG any] interface { + Fn() Fn[RCT] // ERROR "got 1 arguments" +} + +type concreteF[RCT RC[RG], RG any] struct { + makeFn func() Fn[RCT] // ERROR "got 1 arguments" +} + +func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "got 1 arguments" + return c.makeFn() +} + +func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] { // ERROR "got 1 arguments" + return &concreteF[RCT]{ // ERROR "cannot use" "got 1 arguments" + makeFn: nil, + } +} diff --git a/test/typeparam/issue51233.go b/test/typeparam/issue51233.go new file mode 100644 index 00000000000000..96a25ddb9c3952 --- /dev/null +++ b/test/typeparam/issue51233.go @@ -0,0 +1,28 @@ +// errorcheck + +// Copyright 2022 The Go Authors. 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 + +// As of issue #51527, type-type inference has been disabled. + +type RC[RG any] interface { + ~[]RG +} + +type Fn[RCT RC[RG], RG any] func(RCT) + +type FFn[RCT RC[RG], RG any] func() Fn[RCT] // ERROR "got 1 arguments" + +type F[RCT RC[RG], RG any] interface { + Fn() Fn[RCT] // ERROR "got 1 arguments" +} + +type concreteF[RCT RC[RG], RG any] struct { + makeFn FFn[RCT] // ERROR "got 1 arguments" +} + +func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "got 1 arguments" + return c.makeFn() +} diff --git a/test/typeparam/issue51236.go b/test/typeparam/issue51236.go new file mode 100644 index 00000000000000..51fde1e9b6819f --- /dev/null +++ b/test/typeparam/issue51236.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I interface { + []byte +} + +func F[T I]() { + var t T + explodes(t) +} + +func explodes(b []byte) {} + +func main() { + +} diff --git a/test/typeparam/issue51245.go b/test/typeparam/issue51245.go new file mode 100644 index 00000000000000..425d51713ff4d1 --- /dev/null +++ b/test/typeparam/issue51245.go @@ -0,0 +1,16 @@ +// build + +// Copyright 2022 The Go Authors. 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 T[P any] int +const C T[int] = 3 + +type T2 int +const C2 T2 = 9 + +func main() { +} diff --git a/test/typeparam/issue51250a.dir/a.go b/test/typeparam/issue51250a.dir/a.go new file mode 100644 index 00000000000000..12dd60a3d16b51 --- /dev/null +++ b/test/typeparam/issue51250a.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. 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 G[T any] struct { + x T +} diff --git a/test/typeparam/issue51250a.dir/b.go b/test/typeparam/issue51250a.dir/b.go new file mode 100644 index 00000000000000..114c9f80f702f4 --- /dev/null +++ b/test/typeparam/issue51250a.dir/b.go @@ -0,0 +1,24 @@ +// Copyright 2022 The Go Authors. 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 int } + +var I interface{} = a.G[T]{} + +//go:noinline +func F(x interface{}) { + switch x.(type) { + case a.G[T]: + case int: + panic("bad") + case float64: + panic("bad") + default: + panic("bad") + } +} diff --git a/test/typeparam/issue51250a.dir/main.go b/test/typeparam/issue51250a.dir/main.go new file mode 100644 index 00000000000000..45288be482679d --- /dev/null +++ b/test/typeparam/issue51250a.dir/main.go @@ -0,0 +1,24 @@ +// Copyright 2022 The Go Authors. 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" + "./b" +) + +func main() { + switch b.I.(type) { + case a.G[b.T]: + case int: + panic("bad") + case float64: + panic("bad") + default: + panic("bad") + } + + b.F(a.G[b.T]{}) +} diff --git a/test/typeparam/issue51250a.go b/test/typeparam/issue51250a.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue51250a.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51303.go b/test/typeparam/issue51303.go new file mode 100644 index 00000000000000..a3a784969b391f --- /dev/null +++ b/test/typeparam/issue51303.go @@ -0,0 +1,65 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +func main() { + x := [][]int{{1}} + y := [][]int{{2, 3}} + IntersectSS(x, y) +} + +type list[E any] interface { + ~[]E + Equal(x, y E) bool +} + +// ss is a set of sets +type ss[E comparable, T []E] []T + +func (ss[E, T]) Equal(a, b T) bool { + return SetEq(a, b) +} + +func IntersectSS[E comparable](x, y [][]E) [][]E { + return IntersectT[[]E, ss[E, []E]](ss[E, []E](x), ss[E, []E](y)) +} + +func IntersectT[E any, L list[E]](x, y L) L { + var z L +outer: + for _, xe := range x { + fmt.Println("xe", xe) + for _, ye := range y { + fmt.Println("ye", ye) + fmt.Println("x", x) + if x.Equal(xe, ye) { + fmt.Println("appending") + z = append(z, xe) + continue outer + } + } + } + return z +} + +func SetEq[S []E, E comparable](x, y S) bool { + fmt.Println("SetEq", x, y) +outer: + for _, xe := range x { + for _, ye := range y { + if xe == ye { + continue outer + } + } + return false // xs wasn't found in y + } + return true +} diff --git a/test/typeparam/issue51303.out b/test/typeparam/issue51303.out new file mode 100644 index 00000000000000..34b3be32ddd64c --- /dev/null +++ b/test/typeparam/issue51303.out @@ -0,0 +1,4 @@ +xe [1] +ye [2 3] +x [[1]] +SetEq [1] [2 3] diff --git a/test/typeparam/issue51355.go b/test/typeparam/issue51355.go new file mode 100644 index 00000000000000..321d5ff96e4496 --- /dev/null +++ b/test/typeparam/issue51355.go @@ -0,0 +1,31 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Cache[E comparable] struct { + adder func(...E) +} + +func New[E comparable]() *Cache[E] { + c := &Cache[E]{} + + c.adder = func(elements ...E) { + for _, value := range elements { + value := value + go func() { + println(value) + }() + } + } + + return c +} + +func main() { + c := New[string]() + c.adder("test") +} diff --git a/test/typeparam/issue51367.dir/a.go b/test/typeparam/issue51367.dir/a.go new file mode 100644 index 00000000000000..be0c3b0688bb91 --- /dev/null +++ b/test/typeparam/issue51367.dir/a.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A[T any] struct{} + +func (_ A[T]) Method() {} + +func DoSomething[P any]() { + a := A[*byte]{} + a.Method() +} diff --git a/test/typeparam/issue51367.dir/main.go b/test/typeparam/issue51367.dir/main.go new file mode 100644 index 00000000000000..1de8793d4db0c9 --- /dev/null +++ b/test/typeparam/issue51367.dir/main.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "./a" +) + +func main() { + a.DoSomething[byte]() +} diff --git a/test/typeparam/issue51367.go b/test/typeparam/issue51367.go new file mode 100644 index 00000000000000..aefbe673109b6c --- /dev/null +++ b/test/typeparam/issue51367.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51423.dir/a.go b/test/typeparam/issue51423.dir/a.go new file mode 100644 index 00000000000000..e824d0e1654d60 --- /dev/null +++ b/test/typeparam/issue51423.dir/a.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. 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 Comparator[T any] func(v1, v2 T) int + +func CompareInt[T ~int](a, b T) int { + if a < b { + return -1 + } + if a == b { + return 0 + } + return 1 +} diff --git a/test/typeparam/issue51423.dir/b.go b/test/typeparam/issue51423.dir/b.go new file mode 100644 index 00000000000000..2bad19fbdac9f6 --- /dev/null +++ b/test/typeparam/issue51423.dir/b.go @@ -0,0 +1,11 @@ +package b + +import "./a" + +func C() a.Comparator[int] { + return a.CompareInt[int] +} + +func main() { + _ = C()(1, 2) +} diff --git a/test/typeparam/issue51423.go b/test/typeparam/issue51423.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue51423.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51521.go b/test/typeparam/issue51521.go new file mode 100644 index 00000000000000..5eb4e35c185b0d --- /dev/null +++ b/test/typeparam/issue51521.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "strings" +) + +type I interface{ M() } + +func F[P I](p P) { defer catch(); p.M() } +func G[T any]() { defer catch(); interface{ M() T }.M(nil) } + +func main() { + F[I](nil) + G[int]() +} + +func catch() { + err := recover() + if err, ok := err.(error); ok && strings.Contains(err.Error(), "nil pointer dereference") { + return + } + fmt.Println("FAIL", err) +} diff --git a/test/typeparam/issue51522a.go b/test/typeparam/issue51522a.go new file mode 100644 index 00000000000000..3f854080a746d8 --- /dev/null +++ b/test/typeparam/issue51522a.go @@ -0,0 +1,42 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package main + + +func f[T comparable](i any) { + var t T + + if i != t { + println("FAIL: if i != t") + } +} + +type myint int + +func (m myint) foo() { +} + +type fooer interface { + foo() +} + +type comparableFoo interface { + comparable + foo() +} + +func g[T comparableFoo](i fooer) { + var t T + + if i != t { + println("FAIL: if i != t") + } +} + +func main() { + f[int](int(0)) + g[myint](myint(0)) +} diff --git a/test/typeparam/issue51522b.go b/test/typeparam/issue51522b.go new file mode 100644 index 00000000000000..115b6b9c425f2b --- /dev/null +++ b/test/typeparam/issue51522b.go @@ -0,0 +1,62 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T comparable](i any) { + var t T + + switch i { + case t: + // ok + default: + println("FAIL: switch i") + } + + switch t { + case i: + // ok + default: + println("FAIL: switch t") + } +} + +type myint int + +func (m myint) foo() { +} + +type fooer interface { + foo() +} + +type comparableFoo interface { + comparable + foo() +} + +func g[T comparableFoo](i fooer) { + var t T + + switch i { + case t: + // ok + default: + println("FAIL: switch i") + } + + switch t { + case i: + // ok + default: + println("FAIL: switch t") + } +} + +func main() { + f[int](0) + g[myint](myint(0)) +} diff --git a/test/typeparam/issue51700.go b/test/typeparam/issue51700.go new file mode 100644 index 00000000000000..bf8a1f6289bb5a --- /dev/null +++ b/test/typeparam/issue51700.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[B any](b B) { + if b1, ok := any(b).(interface{ m1() }); ok { + panic(1) + _ = b1.(B) + } + if b2, ok := any(b).(interface{ m2() }); ok { + panic(2) + _ = b2.(B) + } +} + +type S struct{} + +func (S) m3() {} + +func main() { + f(S{}) +} diff --git a/test/typeparam/issue51765.go b/test/typeparam/issue51765.go new file mode 100644 index 00000000000000..683cb0f2e2c86a --- /dev/null +++ b/test/typeparam/issue51765.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type empty[T any] struct{} + +func (this *empty[T]) Next() (empty T, _ error) { + return empty, nil +} + +var _ = &empty[string]{} diff --git a/test/typeparam/issue51832.go b/test/typeparam/issue51832.go new file mode 100644 index 00000000000000..c325ae6c2e855d --- /dev/null +++ b/test/typeparam/issue51832.go @@ -0,0 +1,25 @@ +// compile + +// Copyright 2022 The Go Authors. 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 F func() F + +func do[T any]() F { + return nil +} + +type G[T any] func() G[T] + +//go:noinline +func dog[T any]() G[T] { + return nil +} + +func main() { + do[int]() + dog[int]() +} diff --git a/test/typeparam/issue51836.dir/a.go b/test/typeparam/issue51836.dir/a.go new file mode 100644 index 00000000000000..e9223c9aa8270f --- /dev/null +++ b/test/typeparam/issue51836.dir/a.go @@ -0,0 +1,8 @@ +// Copyright 2022 The Go Authors. 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[K any] struct { +} diff --git a/test/typeparam/issue51836.dir/aa.go b/test/typeparam/issue51836.dir/aa.go new file mode 100644 index 00000000000000..d774be282e5aa8 --- /dev/null +++ b/test/typeparam/issue51836.dir/aa.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "./a" +) + +type T[K any] struct { + t a.T[K] +} diff --git a/test/typeparam/issue51836.dir/p.go b/test/typeparam/issue51836.dir/p.go new file mode 100644 index 00000000000000..98197ae0fd9dac --- /dev/null +++ b/test/typeparam/issue51836.dir/p.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import ( + a "./aa" +) + +var Foo a.T[int] diff --git a/test/typeparam/issue51836.go b/test/typeparam/issue51836.go new file mode 100644 index 00000000000000..c755e74b9cdd1a --- /dev/null +++ b/test/typeparam/issue51836.go @@ -0,0 +1,7 @@ +// compiledir -s + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue51840.go b/test/typeparam/issue51840.go new file mode 100644 index 00000000000000..19fa3e468b26b1 --- /dev/null +++ b/test/typeparam/issue51840.go @@ -0,0 +1,36 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Addr struct { + hi uint64 + lo uint64 + z *byte +} + +func EqualMap[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool { + for k, v1 := range m1 { + if v2, ok := m2[k]; !ok || v1 != v2 { + return false + } + } + return true +} + +type Set[T comparable] map[T]struct{} + +func NewSet[T comparable](items ...T) Set[T] { + return nil +} + +func (s Set[T]) Equals(other Set[T]) bool { + return EqualMap(s, other) +} + +func main() { + NewSet[Addr](Addr{0, 0, nil}) +} diff --git a/test/typeparam/issue51909.go b/test/typeparam/issue51909.go new file mode 100644 index 00000000000000..5fe39ca2b3b349 --- /dev/null +++ b/test/typeparam/issue51909.go @@ -0,0 +1,30 @@ +// compile + +// Copyright 2022 The Go Authors. 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 None struct{} + +type Response interface { + send(ctx *struct{}) +} + +type HandlerFunc[Input any] func(Input) Response + +func Operation[Input any](method, path string, h HandlerFunc[Input]) { + var input Input + h(input) +} + +func Get[Body any](path string, h HandlerFunc[struct{ Body Body }]) { + Operation("GET", path, h) +} + +func main() { + Get("/", func(req struct{ Body None }) Response { + return nil + }) +} diff --git a/test/typeparam/issue51925.go b/test/typeparam/issue51925.go new file mode 100644 index 00000000000000..0a385acd17c398 --- /dev/null +++ b/test/typeparam/issue51925.go @@ -0,0 +1,52 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +type IntLike interface { + ~int | ~int64 | ~int32 | ~int16 | ~int8 +} + +func Reduce[T any, U any, Uslice ~[]U](function func(T, U) T, sequence Uslice, initial T) T { + result := initial + for _, x := range sequence { + result = function(result, x) + } + return result +} + +func min[T IntLike](x, y T) T { + if x < y { + return x + } + return y + +} + +// Min returns the minimum element of `nums`. +func Min[T IntLike, NumSlice ~[]T](nums NumSlice) T { + if len(nums) == 0 { + return T(0) + } + return Reduce(min[T], nums, nums[0]) +} + +// VarMin is the variadic version of Min. +func VarMin[T IntLike](nums ...T) T { + return Min(nums) +} + +type myInt int + +func main() { + fmt.Println(VarMin(myInt(1), myInt(2))) + + seq := []myInt{1, 2} + fmt.Println(Min(seq)) + fmt.Println(VarMin(seq...)) +} diff --git a/test/typeparam/issue52026.go b/test/typeparam/issue52026.go new file mode 100644 index 00000000000000..db8999a2b09eb1 --- /dev/null +++ b/test/typeparam/issue52026.go @@ -0,0 +1,50 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func returnOption[T any](n int) Option[T] { + if n == 1 { + return Some[T]{} + } else { + return None{} + } +} + +type Option[T any] interface { + sealedOption() +} + +type Some[T any] struct { + val T +} + +func (s Some[T]) Value() T { + return s.val +} + +func (s Some[T]) sealedOption() {} + +type None struct{} + +func (s None) sealedOption() {} + +func main() { + s := returnOption[int](1) + _ = s.(Some[int]) + + s = returnOption[int](0) + _ = s.(None) + + switch (any)(s).(type) { + case Some[int]: + panic("s is a Some[int]") + case None: + // ok + default: + panic("oops") + } +} diff --git a/test/typeparam/issue52117.dir/a.go b/test/typeparam/issue52117.dir/a.go new file mode 100644 index 00000000000000..e571ea94623cc7 --- /dev/null +++ b/test/typeparam/issue52117.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. 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 Compare[T int | uint](a, b T) int { + return 0 +} + +type Slice[T int | uint] struct{} + +func (l Slice[T]) Comparator() func(v1, v2 T) int { + return Compare[T] +} diff --git a/test/typeparam/issue52117.dir/b.go b/test/typeparam/issue52117.dir/b.go new file mode 100644 index 00000000000000..3d3bf4ced9e2f8 --- /dev/null +++ b/test/typeparam/issue52117.dir/b.go @@ -0,0 +1,7 @@ +package b + +import "./a" + +func Test() { + var _ a.Slice[uint] +} diff --git a/test/typeparam/issue52117.go b/test/typeparam/issue52117.go new file mode 100644 index 00000000000000..8bb5c3e21398f4 --- /dev/null +++ b/test/typeparam/issue52117.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/issue52124.go b/test/typeparam/issue52124.go new file mode 100644 index 00000000000000..a113fc74441f11 --- /dev/null +++ b/test/typeparam/issue52124.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type I interface{ any | int } + +var ( + X I = 42 + Y I = "xxx" + Z I = true +) diff --git a/test/typeparam/issue52228.go b/test/typeparam/issue52228.go new file mode 100644 index 00000000000000..3fbbde59ab62a2 --- /dev/null +++ b/test/typeparam/issue52228.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2022 The Go Authors. 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 SomeInterface interface { + Whatever() +} + +func X[T any]() T { + var m T + + // for this example, this block should never run + if _, ok := any(m).(SomeInterface); ok { + var dst SomeInterface + _, _ = dst.(T) + return dst.(T) + } + + return m +} + +type holder struct{} + +func main() { + X[holder]() +} diff --git a/test/typeparam/issue52241.go b/test/typeparam/issue52241.go new file mode 100644 index 00000000000000..4feb97e0131d07 --- /dev/null +++ b/test/typeparam/issue52241.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Collector[T any] struct { +} + +func (c *Collector[T]) Collect() { +} + +func TestInOrderIntTree() { + collector := Collector[int]{} + _ = collector.Collect +} + +func main() { + TestInOrderIntTree() +} diff --git a/test/typeparam/issue53254.go b/test/typeparam/issue53254.go new file mode 100644 index 00000000000000..afc0f184712877 --- /dev/null +++ b/test/typeparam/issue53254.go @@ -0,0 +1,19 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Interface[T any] interface { +} + +func F[T any]() Interface[T] { + var i int + return i +} + +func main() { + F[int]() +} diff --git a/test/typeparam/issue53390.go b/test/typeparam/issue53390.go new file mode 100644 index 00000000000000..52098c520b4b7e --- /dev/null +++ b/test/typeparam/issue53390.go @@ -0,0 +1,20 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "unsafe" + +func F[T any](v T) uintptr { + return unsafe.Alignof(func() T { + func(any) {}(struct{ _ T }{}) + return v + }()) +} + +func f() { + F(0) +} diff --git a/test/typeparam/issue53406.go b/test/typeparam/issue53406.go new file mode 100644 index 00000000000000..90fe78fb6f5795 --- /dev/null +++ b/test/typeparam/issue53406.go @@ -0,0 +1,22 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + f[int]() +} + +func f[T1 any]() { + var x Outer[T1, int] + x.M() +} + +type Outer[T1, T2 any] struct{ Inner[T2] } + +type Inner[_ any] int + +func (Inner[_]) M() {} diff --git a/test/typeparam/issue53419.go b/test/typeparam/issue53419.go new file mode 100644 index 00000000000000..62a226ff9f777d --- /dev/null +++ b/test/typeparam/issue53419.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2022 The Go Authors. 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 T1 struct{} +type T2 struct{} +type Both struct { + T1 + T2 +} + +func (T1) m() { panic("FAIL") } +func (T2) m() { panic("FAIL") } +func (Both) m() {} + +func f[T interface{ m() }](c T) { + c.m() +} + +func main() { + var b Both + b.m() + f(b) +} diff --git a/test/typeparam/issue53477.go b/test/typeparam/issue53477.go new file mode 100644 index 00000000000000..d128a7e84876d7 --- /dev/null +++ b/test/typeparam/issue53477.go @@ -0,0 +1,34 @@ +// run + +// Copyright 2022 The Go 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 generic interface-interface comparisons resulting from +// value switch statements are handled correctly. + +package main + +func main() { + f[X](0) +} + +type Mer[T any] interface{ M(T) } +type MNer[T any] interface { + Mer[T] + N() +} + +type X int + +func (X) M(X) {} +func (X) N() {} + +func f[T MNer[T]](t T) { + switch Mer[T](t) { + case MNer[T](t): + // ok + default: + panic("FAIL") + } +} diff --git a/test/typeparam/issue53762.go b/test/typeparam/issue53762.go new file mode 100644 index 00000000000000..4d959888544daf --- /dev/null +++ b/test/typeparam/issue53762.go @@ -0,0 +1,18 @@ +// compile + +// Copyright 2022 The Go Authors. 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 Value[T any] interface { +} + +func use[T any](v Value[T]) { + _, _ = v.(int) +} + +func main() { + use(Value[int](1)) +} diff --git a/test/typeparam/issue54135.go b/test/typeparam/issue54135.go new file mode 100644 index 00000000000000..b489a514165d23 --- /dev/null +++ b/test/typeparam/issue54135.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type Foo struct{} + +func (Foo) Blanker() {} + +type Bar[T any] interface { + Blanker() +} + +type Baz interface { + Some() +} + +func check[T comparable](p Bar[T]) { + if x, ok := p.(any); !ok || x != p { + panic("FAIL") + } + if _, ok := p.(Baz); ok { + panic("FAIL") + } +} + +func main() { + check[int](Foo{}) +} diff --git a/test/typeparam/issue54302.dir/a.go b/test/typeparam/issue54302.dir/a.go new file mode 100644 index 00000000000000..52875ab5e15808 --- /dev/null +++ b/test/typeparam/issue54302.dir/a.go @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. 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 A() { + B[int](new(G[int])) +} + +func B[T any](iface interface{ M(T) }) { + x, ok := iface.(*G[T]) + if !ok || iface != x { + panic("FAIL") + } +} + +type G[T any] struct{} + +func (*G[T]) M(T) {} diff --git a/test/typeparam/issue54302.dir/main.go b/test/typeparam/issue54302.dir/main.go new file mode 100644 index 00000000000000..b4c6cd142dcfc8 --- /dev/null +++ b/test/typeparam/issue54302.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + a.A() +} diff --git a/test/typeparam/issue54302.go b/test/typeparam/issue54302.go new file mode 100644 index 00000000000000..f132421c84abfa --- /dev/null +++ b/test/typeparam/issue54302.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2022 The Go Authors. 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 diff --git a/test/typeparam/issue54456.go b/test/typeparam/issue54456.go new file mode 100644 index 00000000000000..8342163e51d643 --- /dev/null +++ b/test/typeparam/issue54456.go @@ -0,0 +1,37 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The Go 1.18 frontend failed to disambiguate instantiations of +// different, locally defined generic types with the same name. +// +// The unified frontend also exposed the scope-disambiguation mangling +// to end users in reflect data. + +package main + +import ( + "reflect" +) + +func one() any { type T[_ any] int; return T[int](0) } +func two() any { type T[_ any] int; return T[int](0) } + +func main() { + p, q := one(), two() + + // p and q have different dynamic types; this comparison should + // evaluate false. + if p == q { + panic("bad type identity") + } + + for _, x := range []any{p, q} { + // The names here should not contain "·1" or "·2". + if name := reflect.TypeOf(x).String(); name != "main.T[int]" { + panic(name) + } + } +} diff --git a/test/typeparam/issue54497.go b/test/typeparam/issue54497.go new file mode 100644 index 00000000000000..1b24cd9afb6b22 --- /dev/null +++ b/test/typeparam/issue54497.go @@ -0,0 +1,19 @@ +// errorcheck -0 -m + +// Copyright 2022 The Go 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 inlining works with generic functions. + +package testcase + +type C interface{ ~uint | ~uint32 | ~uint64 } + +func isAligned[T C](x, y T) bool { // ERROR "can inline isAligned\[uint\]" "can inline isAligned\[go\.shape\.uint\]" "inlining call to isAligned\[go\.shape\.uint\]" + return x%y == 0 +} + +func foo(x uint) bool { // ERROR "can inline foo" + return isAligned(x, 64) // ERROR "inlining call to isAligned\[go\.shape\.uint\]" +} diff --git a/test/typeparam/issue54535.go b/test/typeparam/issue54535.go new file mode 100644 index 00000000000000..574b2755986ba2 --- /dev/null +++ b/test/typeparam/issue54535.go @@ -0,0 +1,37 @@ +// run + +// Copyright 2022 The Go Authors. 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 node[T any] struct { + items items[T] + children items[*node[T]] +} + +func (n *node[T]) f(i int, j int) bool { + if len(n.children[i].items) < j { + return false + } + return true +} + +type items[T any] []T + +func main() { + _ = node[int]{} + _ = f[int] +} + +type s[T, U any] struct { + a T + c U +} + +func f[T any]() { + var x s[*struct{ b T }, *struct{ d int }] + _ = x.a.b + _ = x.c.d +} diff --git a/test/typeparam/issue54537.go b/test/typeparam/issue54537.go new file mode 100644 index 00000000000000..614ed4648d5097 --- /dev/null +++ b/test/typeparam/issue54537.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + _ = F[bool] + + var x string + _ = G(x == "foo") +} + +func F[T ~bool](x string) { + var _ T = x == "foo" +} + +func G[T any](t T) *T { + return &t +} diff --git a/test/typeparam/issue54765.go b/test/typeparam/issue54765.go new file mode 100644 index 00000000000000..364567d258a6ea --- /dev/null +++ b/test/typeparam/issue54765.go @@ -0,0 +1,28 @@ +// errorcheck + +// Copyright 2022 The Go 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 not-in-heap types cannot be used as type +// arguments. (pointer-to-nih types are okay though.) + +//go:build cgo +// +build cgo + +package p + +import ( + "runtime/cgo" + "sync/atomic" +) + +var _ atomic.Pointer[cgo.Incomplete] // ERROR "cannot use incomplete \(or unallocatable\) type as a type argument: runtime/cgo\.Incomplete" +var _ atomic.Pointer[*cgo.Incomplete] // ok + +func implicit(ptr *cgo.Incomplete) { + g(ptr) // ERROR "cannot use incomplete \(or unallocatable\) type as a type argument: runtime/cgo\.Incomplete" + g(&ptr) // ok +} + +func g[T any](_ *T) {} diff --git a/test/typeparam/list.go b/test/typeparam/list.go index 579078f02f7b13..311207e892c57d 100644 --- a/test/typeparam/list.go +++ b/test/typeparam/list.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,10 +11,10 @@ import ( ) type Ordered interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - string + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string } // _List is a linked list of ordered values of type T. @@ -34,9 +34,9 @@ func (l *_List[T]) Largest() T { } type OrderedNum interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64 + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 } // _ListNum is a linked _List of ordered numeric values of type T. @@ -64,40 +64,40 @@ func main() { i2 := &_List[int]{i3, 3} i1 := &_List[int]{i2, 2} if got, want := i1.Largest(), 3; got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + panic(fmt.Sprintf("got %d, want %d", got, want)) } b3 := &_List[byte]{nil, byte(1)} b2 := &_List[byte]{b3, byte(3)} b1 := &_List[byte]{b2, byte(2)} if got, want := b1.Largest(), byte(3); got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + panic(fmt.Sprintf("got %d, want %d", got, want)) } f3 := &_List[float64]{nil, 13.5} f2 := &_List[float64]{f3, 1.2} f1 := &_List[float64]{f2, 4.5} if got, want := f1.Largest(), 13.5; got != want { - panic(fmt.Sprintf("got %f, want %f", got, want)) + panic(fmt.Sprintf("got %f, want %f", got, want)) } s3 := &_List[string]{nil, "dd"} s2 := &_List[string]{s3, "aa"} s1 := &_List[string]{s2, "bb"} if got, want := s1.Largest(), "dd"; got != want { - panic(fmt.Sprintf("got %s, want %s", got, want)) + panic(fmt.Sprintf("got %s, want %s", got, want)) } j3 := &_ListNum[int]{nil, 1} j2 := &_ListNum[int]{j3, 32} j1 := &_ListNum[int]{j2, 2} if got, want := j1.ClippedLargest(), 2; got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + panic(fmt.Sprintf("got %d, want %d", got, want)) } g3 := &_ListNum[float64]{nil, 13.5} g2 := &_ListNum[float64]{g3, 1.2} g1 := &_ListNum[float64]{g2, 4.5} if got, want := g1.ClippedLargest(), 4.5; got != want { - panic(fmt.Sprintf("got %f, want %f", got, want)) + panic(fmt.Sprintf("got %f, want %f", got, want)) } } diff --git a/test/typeparam/list2.go b/test/typeparam/list2.go index 385193d87657c7..111ac787e509d0 100644 --- a/test/typeparam/list2.go +++ b/test/typeparam/list2.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -50,7 +50,7 @@ func (e *_Element[T]) Prev() *_Element[T] { // The zero value for _List is an empty list ready to use. type _List[T any] struct { root _Element[T] // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element + len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. @@ -594,8 +594,15 @@ func TestTransform() { checkList(l2, []interface{}{"1", "2"}) } - func main() { TestList() + TestExtending() + TestRemove() + TestIssue4103() + TestIssue6349() + TestMove() + TestZeroList() + TestInsertBeforeUnknownMark() + TestInsertAfterUnknownMark() + TestTransform() } - diff --git a/test/typeparam/listimp.dir/a.go b/test/typeparam/listimp.dir/a.go new file mode 100644 index 00000000000000..bf1641af9c384d --- /dev/null +++ b/test/typeparam/listimp.dir/a.go @@ -0,0 +1,53 @@ +// Copyright 2021 The Go Authors. 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 Ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +// List is a linked list of ordered values of type T. +type List[T Ordered] struct { + Next *List[T] + Val T +} + +func (l *List[T]) Largest() T { + var max T + for p := l; p != nil; p = p.Next { + if p.Val > max { + max = p.Val + } + } + return max +} + +type OrderedNum interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 +} + +// ListNum is a linked _List of ordered numeric values of type T. +type ListNum[T OrderedNum] struct { + Next *ListNum[T] + Val T +} + +const Clip = 5 + +// clippedLargest returns the largest in the list of OrderNums, but a max of 5. +func (l *ListNum[T]) ClippedLargest() T { + var max T + for p := l; p != nil; p = p.Next { + if p.Val > max && p.Val < Clip { + max = p.Val + } + } + return max +} diff --git a/test/typeparam/listimp.dir/main.go b/test/typeparam/listimp.dir/main.go new file mode 100644 index 00000000000000..652a34a0828846 --- /dev/null +++ b/test/typeparam/listimp.dir/main.go @@ -0,0 +1,52 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + i3 := &a.List[int]{nil, 1} + i2 := &a.List[int]{i3, 3} + i1 := &a.List[int]{i2, 2} + if got, want := i1.Largest(), 3; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + b3 := &a.List[byte]{nil, byte(1)} + b2 := &a.List[byte]{b3, byte(3)} + b1 := &a.List[byte]{b2, byte(2)} + if got, want := b1.Largest(), byte(3); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + f3 := &a.List[float64]{nil, 13.5} + f2 := &a.List[float64]{f3, 1.2} + f1 := &a.List[float64]{f2, 4.5} + if got, want := f1.Largest(), 13.5; got != want { + panic(fmt.Sprintf("got %f, want %f", got, want)) + } + + s3 := &a.List[string]{nil, "dd"} + s2 := &a.List[string]{s3, "aa"} + s1 := &a.List[string]{s2, "bb"} + if got, want := s1.Largest(), "dd"; got != want { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } + j3 := &a.ListNum[int]{nil, 1} + j2 := &a.ListNum[int]{j3, 32} + j1 := &a.ListNum[int]{j2, 2} + if got, want := j1.ClippedLargest(), 2; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + g3 := &a.ListNum[float64]{nil, 13.5} + g2 := &a.ListNum[float64]{g3, 1.2} + g1 := &a.ListNum[float64]{g2, 4.5} + if got, want := g1.ClippedLargest(), 4.5; got != want { + panic(fmt.Sprintf("got %f, want %f", got, want)) + } +} diff --git a/test/typeparam/listimp.go b/test/typeparam/listimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/listimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/listimp2.dir/a.go b/test/typeparam/listimp2.dir/a.go new file mode 100644 index 00000000000000..3a7dfc3999a0db --- /dev/null +++ b/test/typeparam/listimp2.dir/a.go @@ -0,0 +1,298 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "fmt" +) + +// Element is an element of a linked list. +type Element[T any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Element[T] + + // The list to which this element belongs. + list *List[T] + + // The value stored with this element. + Value T +} + +// Next returns the next list element or nil. +func (e *Element[T]) Next() *Element[T] { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *Element[T]) Prev() *Element[T] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// List represents a doubly linked list. +// The zero value for List is an empty list ready to use. +type List[T any] struct { + root Element[T] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element +} + +// Init initializes or clears list l. +func (l *List[T]) Init() *List[T] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// New returns an initialized list. +func New[T any]() *List[T] { return new(List[T]).Init() } + +// Len returns the number of elements of list l. +// The complexity is O(1). +func (l *List[_]) Len() int { return l.len } + +// Front returns the first element of list l or nil if the list is empty. +func (l *List[T]) Front() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *List[T]) Back() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List value. +func (l *List[_]) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *List[T]) insert(e, at *Element[T]) *Element[T] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element[T]{Value: v}, at). +func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { + return l.insert(&Element[T]{Value: v}, at) +} + +// remove removes e from its list, decrements l.len, and returns e. +func (l *List[T]) remove(e *Element[T]) *Element[T] { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- + return e +} + +// move moves e to next to at and returns e. +func (l *List[T]) move(e, at *Element[T]) *Element[T] { + if e == at { + return e + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + + return e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +// The element must not be nil. +func (l *List[T]) Remove(e *Element[T]) T { + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) + } + return e.Value +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List[T]) PushFront(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, &l.root) +} + +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List[T]) PushBack(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, l.root.prev) +} + +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) +} + +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToFront(e *Element[T]) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToBack(e *Element[T]) { + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// MoveBefore moves element e to its new position before mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveBefore(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveAfter(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark) +} + +// PushBackList inserts a copy of an other list at the back of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushBackList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of an other list at the front of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushFrontList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} + +// Transform runs a transform function on a list returning a new list. +func Transform[TElem1, TElem2 any](lst *List[TElem1], f func(TElem1) TElem2) *List[TElem2] { + ret := New[TElem2]() + for p := lst.Front(); p != nil; p = p.Next() { + ret.PushBack(f(p.Value)) + } + return ret +} + +func CheckListLen[T any](l *List[T], len int) bool { + if n := l.Len(); n != len { + panic(fmt.Sprintf("l.Len() = %d, want %d", n, len)) + return false + } + return true +} + +func CheckListPointers[T any](l *List[T], es []*Element[T]) { + root := &l.root + + if !CheckListLen(l, len(es)) { + return + } + + // zero length lists must be the zero value or properly initialized (sentinel circle) + if len(es) == 0 { + if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root { + panic(fmt.Sprintf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root)) + } + return + } + // len(es) > 0 + + // check internal and external prev/next connections + for i, e := range es { + prev := root + Prev := (*Element[T])(nil) + if i > 0 { + prev = es[i-1] + Prev = prev + } + if p := e.prev; p != prev { + panic(fmt.Sprintf("elt[%d](%p).prev = %p, want %p", i, e, p, prev)) + } + if p := e.Prev(); p != Prev { + panic(fmt.Sprintf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev)) + } + + next := root + Next := (*Element[T])(nil) + if i < len(es)-1 { + next = es[i+1] + Next = next + } + if n := e.next; n != next { + panic(fmt.Sprintf("elt[%d](%p).next = %p, want %p", i, e, n, next)) + } + if n := e.Next(); n != Next { + panic(fmt.Sprintf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next)) + } + } +} diff --git a/test/typeparam/listimp2.dir/main.go b/test/typeparam/listimp2.dir/main.go new file mode 100644 index 00000000000000..c3b936eada0033 --- /dev/null +++ b/test/typeparam/listimp2.dir/main.go @@ -0,0 +1,315 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "strconv" +) + +func TestList() { + l := a.New[string]() + a.CheckListPointers(l, []*(a.Element[string]){}) + + // Single element list + e := l.PushFront("a") + a.CheckListPointers(l, []*(a.Element[string]){e}) + l.MoveToFront(e) + a.CheckListPointers(l, []*(a.Element[string]){e}) + l.MoveToBack(e) + a.CheckListPointers(l, []*(a.Element[string]){e}) + l.Remove(e) + a.CheckListPointers(l, []*(a.Element[string]){}) + + // Bigger list + l2 := a.New[int]() + e2 := l2.PushFront(2) + e1 := l2.PushFront(1) + e3 := l2.PushBack(3) + e4 := l2.PushBack(600) + a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e3, e4}) + + l2.Remove(e2) + a.CheckListPointers(l2, []*(a.Element[int]){e1, e3, e4}) + + l2.MoveToFront(e3) // move from middle + a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4}) + + l2.MoveToFront(e1) + l2.MoveToBack(e3) // move from middle + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3}) + + l2.MoveToFront(e3) // move from back + a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4}) + l2.MoveToFront(e3) // should be no-op + a.CheckListPointers(l2, []*(a.Element[int]){e3, e1, e4}) + + l2.MoveToBack(e3) // move from front + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3}) + l2.MoveToBack(e3) // should be no-op + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3}) + + e2 = l2.InsertBefore(2, e1) // insert before front + a.CheckListPointers(l2, []*(a.Element[int]){e2, e1, e4, e3}) + l2.Remove(e2) + e2 = l2.InsertBefore(2, e4) // insert before middle + a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e4, e3}) + l2.Remove(e2) + e2 = l2.InsertBefore(2, e3) // insert before back + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e2, e3}) + l2.Remove(e2) + + e2 = l2.InsertAfter(2, e1) // insert after front + a.CheckListPointers(l2, []*(a.Element[int]){e1, e2, e4, e3}) + l2.Remove(e2) + e2 = l2.InsertAfter(2, e4) // insert after middle + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e2, e3}) + l2.Remove(e2) + e2 = l2.InsertAfter(2, e3) // insert after back + a.CheckListPointers(l2, []*(a.Element[int]){e1, e4, e3, e2}) + l2.Remove(e2) + + // Check standard iteration. + sum := 0 + for e := l2.Front(); e != nil; e = e.Next() { + sum += e.Value + } + if sum != 604 { + panic(fmt.Sprintf("sum over l = %d, want 604", sum)) + } + + // Clear all elements by iterating + var next *a.Element[int] + for e := l2.Front(); e != nil; e = next { + next = e.Next() + l2.Remove(e) + } + a.CheckListPointers(l2, []*(a.Element[int]){}) +} + +func checkList[T comparable](l *a.List[T], es []interface{}) { + if !a.CheckListLen(l, len(es)) { + return + } + + i := 0 + for e := l.Front(); e != nil; e = e.Next() { + le := e.Value + // Comparison between a generically-typed variable le and an interface. + if le != es[i] { + panic(fmt.Sprintf("elt[%d].Value = %v, want %v", i, le, es[i])) + } + i++ + } +} + +func TestExtending() { + l1 := a.New[int]() + l2 := a.New[int]() + + l1.PushBack(1) + l1.PushBack(2) + l1.PushBack(3) + + l2.PushBack(4) + l2.PushBack(5) + + l3 := a.New[int]() + l3.PushBackList(l1) + checkList(l3, []interface{}{1, 2, 3}) + l3.PushBackList(l2) + checkList(l3, []interface{}{1, 2, 3, 4, 5}) + + l3 = a.New[int]() + l3.PushFrontList(l2) + checkList(l3, []interface{}{4, 5}) + l3.PushFrontList(l1) + checkList(l3, []interface{}{1, 2, 3, 4, 5}) + + checkList(l1, []interface{}{1, 2, 3}) + checkList(l2, []interface{}{4, 5}) + + l3 = a.New[int]() + l3.PushBackList(l1) + checkList(l3, []interface{}{1, 2, 3}) + l3.PushBackList(l3) + checkList(l3, []interface{}{1, 2, 3, 1, 2, 3}) + + l3 = a.New[int]() + l3.PushFrontList(l1) + checkList(l3, []interface{}{1, 2, 3}) + l3.PushFrontList(l3) + checkList(l3, []interface{}{1, 2, 3, 1, 2, 3}) + + l3 = a.New[int]() + l1.PushBackList(l3) + checkList(l1, []interface{}{1, 2, 3}) + l1.PushFrontList(l3) + checkList(l1, []interface{}{1, 2, 3}) +} + +func TestRemove() { + l := a.New[int]() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + a.CheckListPointers(l, []*(a.Element[int]){e1, e2}) + e := l.Front() + l.Remove(e) + a.CheckListPointers(l, []*(a.Element[int]){e2}) + l.Remove(e) + a.CheckListPointers(l, []*(a.Element[int]){e2}) +} + +func TestIssue4103() { + l1 := a.New[int]() + l1.PushBack(1) + l1.PushBack(2) + + l2 := a.New[int]() + l2.PushBack(3) + l2.PushBack(4) + + e := l1.Front() + l2.Remove(e) // l2 should not change because e is not an element of l2 + if n := l2.Len(); n != 2 { + panic(fmt.Sprintf("l2.Len() = %d, want 2", n)) + } + + l1.InsertBefore(8, e) + if n := l1.Len(); n != 3 { + panic(fmt.Sprintf("l1.Len() = %d, want 3", n)) + } +} + +func TestIssue6349() { + l := a.New[int]() + l.PushBack(1) + l.PushBack(2) + + e := l.Front() + l.Remove(e) + if e.Value != 1 { + panic(fmt.Sprintf("e.value = %d, want 1", e.Value)) + } + if e.Next() != nil { + panic(fmt.Sprintf("e.Next() != nil")) + } + if e.Prev() != nil { + panic(fmt.Sprintf("e.Prev() != nil")) + } +} + +func TestMove() { + l := a.New[int]() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + e3 := l.PushBack(3) + e4 := l.PushBack(4) + + l.MoveAfter(e3, e3) + a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4}) + l.MoveBefore(e2, e2) + a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4}) + + l.MoveAfter(e3, e2) + a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4}) + l.MoveBefore(e2, e3) + a.CheckListPointers(l, []*(a.Element[int]){e1, e2, e3, e4}) + + l.MoveBefore(e2, e4) + a.CheckListPointers(l, []*(a.Element[int]){e1, e3, e2, e4}) + e2, e3 = e3, e2 + + l.MoveBefore(e4, e1) + a.CheckListPointers(l, []*(a.Element[int]){e4, e1, e2, e3}) + e1, e2, e3, e4 = e4, e1, e2, e3 + + l.MoveAfter(e4, e1) + a.CheckListPointers(l, []*(a.Element[int]){e1, e4, e2, e3}) + e2, e3, e4 = e4, e2, e3 + + l.MoveAfter(e2, e3) + a.CheckListPointers(l, []*(a.Element[int]){e1, e3, e2, e4}) + e2, e3 = e3, e2 +} + +// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized a.List +func TestZeroList() { + var l1 = new(a.List[int]) + l1.PushFront(1) + checkList(l1, []interface{}{1}) + + var l2 = new(a.List[int]) + l2.PushBack(1) + checkList(l2, []interface{}{1}) + + var l3 = new(a.List[int]) + l3.PushFrontList(l1) + checkList(l3, []interface{}{1}) + + var l4 = new(a.List[int]) + l4.PushBackList(l2) + checkList(l4, []interface{}{1}) +} + +// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l. +func TestInsertBeforeUnknownMark() { + var l a.List[int] + l.PushBack(1) + l.PushBack(2) + l.PushBack(3) + l.InsertBefore(1, new(a.Element[int])) + checkList(&l, []interface{}{1, 2, 3}) +} + +// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l. +func TestInsertAfterUnknownMark() { + var l a.List[int] + l.PushBack(1) + l.PushBack(2) + l.PushBack(3) + l.InsertAfter(1, new(a.Element[int])) + checkList(&l, []interface{}{1, 2, 3}) +} + +// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l. +func TestMoveUnknownMark() { + var l1 a.List[int] + e1 := l1.PushBack(1) + + var l2 a.List[int] + e2 := l2.PushBack(2) + + l1.MoveAfter(e1, e2) + checkList(&l1, []interface{}{1}) + checkList(&l2, []interface{}{2}) + + l1.MoveBefore(e1, e2) + checkList(&l1, []interface{}{1}) + checkList(&l2, []interface{}{2}) +} + +// Test the Transform function. +func TestTransform() { + l1 := a.New[int]() + l1.PushBack(1) + l1.PushBack(2) + l2 := a.Transform(l1, strconv.Itoa) + checkList(l2, []interface{}{"1", "2"}) +} + +func main() { + TestList() + TestExtending() + TestRemove() + TestIssue4103() + TestIssue6349() + TestMove() + TestZeroList() + TestInsertBeforeUnknownMark() + TestInsertAfterUnknownMark() + TestTransform() +} diff --git a/test/typeparam/listimp2.go b/test/typeparam/listimp2.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/listimp2.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/lockable.go b/test/typeparam/lockable.go index d53817521f30c8..2b50e2c20e8bef 100644 --- a/test/typeparam/lockable.go +++ b/test/typeparam/lockable.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,29 +8,29 @@ package main import "sync" -// A _Lockable is a value that may be safely simultaneously accessed +// A Lockable is a value that may be safely simultaneously accessed // from multiple goroutines via the Get and Set methods. -type _Lockable[T any] struct { - T +type Lockable[T any] struct { + x T mu sync.Mutex } -// Get returns the value stored in a _Lockable. -func (l *_Lockable[T]) get() T { +// Get returns the value stored in a Lockable. +func (l *Lockable[T]) get() T { l.mu.Lock() defer l.mu.Unlock() - return l.T + return l.x } -// set sets the value in a _Lockable. -func (l *_Lockable[T]) set(v T) { +// set sets the value in a Lockable. +func (l *Lockable[T]) set(v T) { l.mu.Lock() defer l.mu.Unlock() - l.T = v + l.x = v } func main() { - sl := _Lockable[string]{T: "a"} + sl := Lockable[string]{x: "a"} if got := sl.get(); got != "a" { panic(got) } @@ -39,7 +39,7 @@ func main() { panic(got) } - il := _Lockable[int]{T: 1} + il := Lockable[int]{x: 1} if got := il.get(); got != 1 { panic(got) } diff --git a/test/typeparam/map.go b/test/typeparam/map.go index 72d05f08721113..eb68fe5c334f29 100644 --- a/test/typeparam/map.go +++ b/test/typeparam/map.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/mapimp.dir/a.go b/test/typeparam/mapimp.dir/a.go new file mode 100644 index 00000000000000..cbfa80ac6b1d33 --- /dev/null +++ b/test/typeparam/mapimp.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 + +// Map calls the function f on every element of the slice s, +// returning a new slice of the results. +func Mapper[F, T any](s []F, f func(F) T) []T { + r := make([]T, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} diff --git a/test/typeparam/mapimp.dir/main.go b/test/typeparam/mapimp.dir/main.go new file mode 100644 index 00000000000000..8a56ce2bfb391f --- /dev/null +++ b/test/typeparam/mapimp.dir/main.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "reflect" + "strconv" +) + +func main() { + got := a.Mapper([]int{1, 2, 3}, strconv.Itoa) + want := []string{"1", "2", "3"} + if !reflect.DeepEqual(got, want) { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } + + fgot := a.Mapper([]float64{2.5, 2.3, 3.5}, func(f float64) string { + return strconv.FormatFloat(f, 'f', -1, 64) + }) + fwant := []string{"2.5", "2.3", "3.5"} + if !reflect.DeepEqual(fgot, fwant) { + panic(fmt.Sprintf("got %s, want %s", fgot, fwant)) + } +} diff --git a/test/typeparam/mapimp.go b/test/typeparam/mapimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/mapimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/maps.go b/test/typeparam/maps.go index d18dd59aed4d61..d4be5dd0f4fe09 100644 --- a/test/typeparam/maps.go +++ b/test/typeparam/maps.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/mapsimp.dir/a.go b/test/typeparam/mapsimp.dir/a.go new file mode 100644 index 00000000000000..696e2a568014a9 --- /dev/null +++ b/test/typeparam/mapsimp.dir/a.go @@ -0,0 +1,108 @@ +// Copyright 2021 The Go Authors. 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 + +// SliceEqual reports whether two slices are equal: the same length and all +// elements equal. All floating point NaNs are considered equal. +func SliceEqual[Elem comparable](s1, s2 []Elem) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if v1 != v2 { + isNaN := func(f Elem) bool { return f != f } + if !isNaN(v1) || !isNaN(v2) { + return false + } + } + } + return true +} + +// Keys returns the keys of the map m. +// The keys will be an indeterminate order. +func Keys[K comparable, V any](m map[K]V) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} + +// Values returns the values of the map m. +// The values will be in an indeterminate order. +func Values[K comparable, V any](m map[K]V) []V { + r := make([]V, 0, len(m)) + for _, v := range m { + r = append(r, v) + } + return r +} + +// Equal reports whether two maps contain the same key/value pairs. +// Values are compared using ==. +func Equal[K, V comparable](m1, m2 map[K]V) bool { + if len(m1) != len(m2) { + return false + } + for k, v1 := range m1 { + if v2, ok := m2[k]; !ok || v1 != v2 { + return false + } + } + return true +} + +// Copy returns a copy of m. +func Copy[K comparable, V any](m map[K]V) map[K]V { + r := make(map[K]V, len(m)) + for k, v := range m { + r[k] = v + } + return r +} + +// Add adds all key/value pairs in m2 to m1. Keys in m2 that are already +// present in m1 will be overwritten with the value in m2. +func Add[K comparable, V any](m1, m2 map[K]V) { + for k, v := range m2 { + m1[k] = v + } +} + +// Sub removes all keys in m2 from m1. Keys in m2 that are not present +// in m1 are ignored. The values in m2 are ignored. +func Sub[K comparable, V any](m1, m2 map[K]V) { + for k := range m2 { + delete(m1, k) + } +} + +// Intersect removes all keys from m1 that are not present in m2. +// Keys in m2 that are not in m1 are ignored. The values in m2 are ignored. +func Intersect[K comparable, V any](m1, m2 map[K]V) { + for k := range m1 { + if _, ok := m2[k]; !ok { + delete(m1, k) + } + } +} + +// Filter deletes any key/value pairs from m for which f returns false. +func Filter[K comparable, V any](m map[K]V, f func(K, V) bool) { + for k, v := range m { + if !f(k, v) { + delete(m, k) + } + } +} + +// TransformValues applies f to each value in m. The keys remain unchanged. +func TransformValues[K comparable, V any](m map[K]V, f func(V) V) { + for k, v := range m { + m[k] = f(v) + } +} diff --git a/test/typeparam/mapsimp.dir/main.go b/test/typeparam/mapsimp.dir/main.go new file mode 100644 index 00000000000000..45f7d39f931e00 --- /dev/null +++ b/test/typeparam/mapsimp.dir/main.go @@ -0,0 +1,156 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "math" + "sort" +) + +var m1 = map[int]int{1: 2, 2: 4, 4: 8, 8: 16} +var m2 = map[int]string{1: "2", 2: "4", 4: "8", 8: "16"} + +func TestKeys() { + want := []int{1, 2, 4, 8} + + got1 := a.Keys(m1) + sort.Ints(got1) + if !a.SliceEqual(got1, want) { + panic(fmt.Sprintf("a.Keys(%v) = %v, want %v", m1, got1, want)) + } + + got2 := a.Keys(m2) + sort.Ints(got2) + if !a.SliceEqual(got2, want) { + panic(fmt.Sprintf("a.Keys(%v) = %v, want %v", m2, got2, want)) + } +} + +func TestValues() { + got1 := a.Values(m1) + want1 := []int{2, 4, 8, 16} + sort.Ints(got1) + if !a.SliceEqual(got1, want1) { + panic(fmt.Sprintf("a.Values(%v) = %v, want %v", m1, got1, want1)) + } + + got2 := a.Values(m2) + want2 := []string{"16", "2", "4", "8"} + sort.Strings(got2) + if !a.SliceEqual(got2, want2) { + panic(fmt.Sprintf("a.Values(%v) = %v, want %v", m2, got2, want2)) + } +} + +func TestEqual() { + if !a.Equal(m1, m1) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", m1, m1)) + } + if a.Equal(m1, nil) { + panic(fmt.Sprintf("a.Equal(%v, nil) = true, want false", m1)) + } + if a.Equal(nil, m1) { + panic(fmt.Sprintf("a.Equal(nil, %v) = true, want false", m1)) + } + if !a.Equal[int, int](nil, nil) { + panic("a.Equal(nil, nil) = false, want true") + } + if ms := map[int]int{1: 2}; a.Equal(m1, ms) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", m1, ms)) + } + + // Comparing NaN for equality is expected to fail. + mf := map[int]float64{1: 0, 2: math.NaN()} + if a.Equal(mf, mf) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", mf, mf)) + } +} + +func TestCopy() { + m2 := a.Copy(m1) + if !a.Equal(m1, m2) { + panic(fmt.Sprintf("a.Copy(%v) = %v, want %v", m1, m2, m1)) + } + m2[16] = 32 + if a.Equal(m1, m2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", m1, m2)) + } +} + +func TestAdd() { + mc := a.Copy(m1) + a.Add(mc, mc) + if !a.Equal(mc, m1) { + panic(fmt.Sprintf("a.Add(%v, %v) = %v, want %v", m1, m1, mc, m1)) + } + a.Add(mc, map[int]int{16: 32}) + want := map[int]int{1: 2, 2: 4, 4: 8, 8: 16, 16: 32} + if !a.Equal(mc, want) { + panic(fmt.Sprintf("a.Add result = %v, want %v", mc, want)) + } +} + +func TestSub() { + mc := a.Copy(m1) + a.Sub(mc, mc) + if len(mc) > 0 { + panic(fmt.Sprintf("a.Sub(%v, %v) = %v, want empty map", m1, m1, mc)) + } + mc = a.Copy(m1) + a.Sub(mc, map[int]int{1: 0}) + want := map[int]int{2: 4, 4: 8, 8: 16} + if !a.Equal(mc, want) { + panic(fmt.Sprintf("a.Sub result = %v, want %v", mc, want)) + } +} + +func TestIntersect() { + mc := a.Copy(m1) + a.Intersect(mc, mc) + if !a.Equal(mc, m1) { + panic(fmt.Sprintf("a.Intersect(%v, %v) = %v, want %v", m1, m1, mc, m1)) + } + a.Intersect(mc, map[int]int{1: 0, 2: 0}) + want := map[int]int{1: 2, 2: 4} + if !a.Equal(mc, want) { + panic(fmt.Sprintf("a.Intersect result = %v, want %v", mc, want)) + } +} + +func TestFilter() { + mc := a.Copy(m1) + a.Filter(mc, func(int, int) bool { return true }) + if !a.Equal(mc, m1) { + panic(fmt.Sprintf("a.Filter(%v, true) = %v, want %v", m1, mc, m1)) + } + a.Filter(mc, func(k, v int) bool { return k < 3 }) + want := map[int]int{1: 2, 2: 4} + if !a.Equal(mc, want) { + panic(fmt.Sprintf("a.Filter result = %v, want %v", mc, want)) + } +} + +func TestTransformValues() { + mc := a.Copy(m1) + a.TransformValues(mc, func(i int) int { return i / 2 }) + want := map[int]int{1: 1, 2: 2, 4: 4, 8: 8} + if !a.Equal(mc, want) { + panic(fmt.Sprintf("a.TransformValues result = %v, want %v", mc, want)) + } +} + +func main() { + TestKeys() + TestValues() + TestEqual() + TestCopy() + TestAdd() + TestSub() + TestIntersect() + TestFilter() + TestTransformValues() +} diff --git a/test/typeparam/mapsimp.go b/test/typeparam/mapsimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/mapsimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/1.dir/a.go b/test/typeparam/mdempsky/1.dir/a.go new file mode 100644 index 00000000000000..a668eb52dc9dc1 --- /dev/null +++ b/test/typeparam/mdempsky/1.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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[_ any] int + +func F() { _ = new(T[int]) } diff --git a/test/typeparam/mdempsky/1.dir/b.go b/test/typeparam/mdempsky/1.dir/b.go new file mode 100644 index 00000000000000..af6fef3f6d8bad --- /dev/null +++ b/test/typeparam/mdempsky/1.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { a.F() } diff --git a/test/typeparam/mdempsky/1.go b/test/typeparam/mdempsky/1.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/mdempsky/1.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/10.dir/a.go b/test/typeparam/mdempsky/10.dir/a.go new file mode 100644 index 00000000000000..95e111d3470a1a --- /dev/null +++ b/test/typeparam/mdempsky/10.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. 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 I[T any] interface{ M() T } diff --git a/test/typeparam/mdempsky/10.dir/b.go b/test/typeparam/mdempsky/10.dir/b.go new file mode 100644 index 00000000000000..0ef28fd02de328 --- /dev/null +++ b/test/typeparam/mdempsky/10.dir/b.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. 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" + +var m = a.I[int].M + +var never bool + +func main() { + if never { + m(nil) + } +} diff --git a/test/typeparam/mdempsky/10.go b/test/typeparam/mdempsky/10.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/mdempsky/10.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/12.dir/a.go b/test/typeparam/mdempsky/12.dir/a.go new file mode 100644 index 00000000000000..ee8be939a8fadd --- /dev/null +++ b/test/typeparam/mdempsky/12.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 S[T any] struct { + F T +} + +var X = S[int]{} diff --git a/test/typeparam/mdempsky/12.dir/main.go b/test/typeparam/mdempsky/12.dir/main.go new file mode 100644 index 00000000000000..2891322e298867 --- /dev/null +++ b/test/typeparam/mdempsky/12.dir/main.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "./a" +) + +func main() { + _ = a.X +} diff --git a/test/typeparam/mdempsky/12.go b/test/typeparam/mdempsky/12.go new file mode 100644 index 00000000000000..0316402a78d0de --- /dev/null +++ b/test/typeparam/mdempsky/12.go @@ -0,0 +1,9 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Reported by Cuong Manh Le. + +package ignored diff --git a/test/typeparam/mdempsky/13.go b/test/typeparam/mdempsky/13.go new file mode 100644 index 00000000000000..8e11352b514840 --- /dev/null +++ b/test/typeparam/mdempsky/13.go @@ -0,0 +1,84 @@ +// run + +// Copyright 2021 The Go Authors. 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 + +// Interface which will be used as a regular interface type and as a type bound. +type Mer interface{ + M() +} + +// Interface that is a superset of Mer. +type Mer2 interface { + M() + String() string +} + +func F[T Mer](t T) { + T.M(t) + t.M() +} + +type MyMer int + +func (MyMer) M() {} +func (MyMer) String() string { + return "aa" +} + +// Parameterized interface +type Abs[T any] interface { + Abs() T +} + +func G[T Abs[U], U any](t T) { + T.Abs(t) + t.Abs() +} + +type MyInt int +func (m MyInt) Abs() MyInt { + if m < 0 { + return -m + } + return m +} + +type Abs2 interface { + Abs() MyInt +} + + +func main() { + mm := MyMer(3) + ms := struct{ Mer }{Mer: mm } + + // Testing F with an interface type arg: Mer and Mer2 + F[Mer](mm) + F[Mer2](mm) + F[struct{ Mer }](ms) + F[*struct{ Mer }](&ms) + + ms2 := struct { MyMer }{MyMer: mm} + ms3 := struct { *MyMer }{MyMer: &mm} + + // Testing F with a concrete type arg + F[MyMer](mm) + F[*MyMer](&mm) + F[struct{ MyMer }](ms2) + F[struct{ *MyMer }](ms3) + F[*struct{ MyMer }](&ms2) + F[*struct{ *MyMer }](&ms3) + + // Testing G with a concrete type args + mi := MyInt(-3) + G[MyInt,MyInt](mi) + + // Interface Abs[MyInt] holding an mi. + intMi := Abs[MyInt](mi) + // First type arg here is Abs[MyInt], an interface type. + G[Abs[MyInt],MyInt](intMi) +} diff --git a/test/typeparam/mdempsky/14.go b/test/typeparam/mdempsky/14.go new file mode 100644 index 00000000000000..4af990c49a4a1f --- /dev/null +++ b/test/typeparam/mdempsky/14.go @@ -0,0 +1,40 @@ +// run + +// Copyright 2021 The Go Authors. 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 + +// Zero returns the zero value of T +func Zero[T any]() (_ T) { + return +} + +type AnyInt[X any] int + +func (AnyInt[X]) M() { + var have interface{} = Zero[X]() + var want interface{} = Zero[MyInt]() + + if have != want { + println("FAIL") + } +} + +type I interface{ M() } + +type MyInt int +type U = AnyInt[MyInt] + +var x = U(0) +var i I = x + +func main() { + x.M() + U.M(x) + (*U).M(&x) + + i.M() + I.M(x) +} diff --git a/test/typeparam/mdempsky/15.go b/test/typeparam/mdempsky/15.go new file mode 100644 index 00000000000000..b03ad6f2104840 --- /dev/null +++ b/test/typeparam/mdempsky/15.go @@ -0,0 +1,69 @@ +// run -goexperiment fieldtrack + +// Copyright 2021 The Go 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 generics, promoted methods, and //go:nointerface +// interoperate as expected. + +package main + +import ( + "reflect" +) + +func TypeString[T any]() string { + return reflect.TypeOf(new(T)).Elem().String() +} + +func Test[T, Bad, Good any]() { + switch interface{}(new(T)).(type) { + case Bad: + println("FAIL:", TypeString[T](), "matched", TypeString[Bad]()) + case Good: + // ok + default: + println("FAIL:", TypeString[T](), "did not match", TypeString[Good]()) + } +} + +func TestE[T any]() { Test[T, interface{ EBad() }, interface{ EGood() }]() } +func TestX[T any]() { Test[T, interface{ XBad() }, interface{ XGood() }]() } + +type E struct{} + +//go:nointerface +func (E) EBad() {} +func (E) EGood() {} + +type X[T any] struct{ E } + +//go:nointerface +func (X[T]) XBad() {} +func (X[T]) XGood() {} + +type W struct{ X[int] } + +func main() { + _ = E.EGood + _ = E.EBad + + TestE[E]() + + _ = X[int].EGood + _ = X[int].EBad + _ = X[int].XGood + _ = X[int].XBad + + TestE[X[int]]() + TestX[X[int]]() + + _ = W.EGood + _ = W.EBad + _ = W.XGood + _ = W.XBad + + TestE[W]() + TestX[W]() +} diff --git a/test/typeparam/mdempsky/16.go b/test/typeparam/mdempsky/16.go new file mode 100644 index 00000000000000..f4f79b9aac5d15 --- /dev/null +++ b/test/typeparam/mdempsky/16.go @@ -0,0 +1,34 @@ +// run + +// Copyright 2022 The Go 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 type assertion panics mention the real interface type, +// not their shape type. + +package main + +import ( + "fmt" + "runtime" + "strings" +) + +func main() { + // The exact error message isn't important, but it should mention + // `main.T`, not `go.shape.int_0`. + if have := F[T](); !strings.Contains(have, "interface { T() main.T }") { + fmt.Printf("FAIL: unexpected panic message: %q\n", have) + } +} + +type T int + +func F[T any]() (res string) { + defer func() { + res = recover().(runtime.Error).Error() + }() + _ = interface{ T() T }(nil).(T) + return +} diff --git a/test/typeparam/mdempsky/17.go b/test/typeparam/mdempsky/17.go new file mode 100644 index 00000000000000..12385c3f9eea78 --- /dev/null +++ b/test/typeparam/mdempsky/17.go @@ -0,0 +1,110 @@ +// run + +// Copyright 2022 The Go 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 implicit conversions of derived types to interface type +// in range loops work correctly. + +package main + +import ( + "fmt" + "reflect" +) + +func main() { + test{"int", "V"}.match(RangeArrayAny[V]()) + test{"int", "V"}.match(RangeArrayIface[V]()) + test{"V"}.match(RangeChanAny[V]()) + test{"V"}.match(RangeChanIface[V]()) + test{"K", "V"}.match(RangeMapAny[K, V]()) + test{"K", "V"}.match(RangeMapIface[K, V]()) + test{"int", "V"}.match(RangeSliceAny[V]()) + test{"int", "V"}.match(RangeSliceIface[V]()) +} + +type test []string + +func (t test) match(args ...any) { + if len(t) != len(args) { + fmt.Printf("FAIL: want %v values, have %v\n", len(t), len(args)) + return + } + for i, want := range t { + if have := reflect.TypeOf(args[i]).Name(); want != have { + fmt.Printf("FAIL: %v: want type %v, have %v\n", i, want, have) + } + } +} + +type iface interface{ M() int } + +type K int +type V int + +func (K) M() int { return 0 } +func (V) M() int { return 0 } + +func RangeArrayAny[V any]() (k, v any) { + for k, v = range [...]V{zero[V]()} { + } + return +} + +func RangeArrayIface[V iface]() (k any, v iface) { + for k, v = range [...]V{zero[V]()} { + } + return +} + +func RangeChanAny[V any]() (v any) { + for v = range chanOf(zero[V]()) { + } + return +} + +func RangeChanIface[V iface]() (v iface) { + for v = range chanOf(zero[V]()) { + } + return +} + +func RangeMapAny[K comparable, V any]() (k, v any) { + for k, v = range map[K]V{zero[K](): zero[V]()} { + } + return +} + +func RangeMapIface[K interface { + iface + comparable +}, V iface]() (k, v iface) { + for k, v = range map[K]V{zero[K](): zero[V]()} { + } + return +} + +func RangeSliceAny[V any]() (k, v any) { + for k, v = range []V{zero[V]()} { + } + return +} + +func RangeSliceIface[V iface]() (k any, v iface) { + for k, v = range []V{zero[V]()} { + } + return +} + +func chanOf[T any](elems ...T) chan T { + c := make(chan T, len(elems)) + for _, elem := range elems { + c <- elem + } + close(c) + return c +} + +func zero[T any]() (_ T) { return } diff --git a/test/typeparam/mdempsky/18.go b/test/typeparam/mdempsky/18.go new file mode 100644 index 00000000000000..f4a4ec73c5eb6e --- /dev/null +++ b/test/typeparam/mdempsky/18.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2022 The Go 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 implicit conversions to interface type in a select/case +// clause are compiled correctly. + +package main + +import "fmt" + +func main() { f[int]() } + +func f[T any]() { + ch := make(chan T) + close(ch) + + var i, ok any + select { + case i, ok = <-ch: + } + + fmt.Printf("%T %T\n", i, ok) +} diff --git a/test/typeparam/mdempsky/18.out b/test/typeparam/mdempsky/18.out new file mode 100644 index 00000000000000..19f1c39a22d995 --- /dev/null +++ b/test/typeparam/mdempsky/18.out @@ -0,0 +1 @@ +int bool diff --git a/test/typeparam/mdempsky/19.go b/test/typeparam/mdempsky/19.go new file mode 100644 index 00000000000000..53d979a1f24956 --- /dev/null +++ b/test/typeparam/mdempsky/19.go @@ -0,0 +1,32 @@ +// run + +// Copyright 2022 The Go 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 type parameter methods are handled correctly, even when +// the instantiating type argument has additional methods. + +package main + +func main() { + F(X(0)) +} + +type I interface{ B() } + +func F[T I](t T) { + CallMethod(t) + MethodExpr[T]()(t) + MethodVal(t)() +} + +func CallMethod[T I](t T) { t.B() } +func MethodExpr[T I]() func(T) { return T.B } +func MethodVal[T I](t T) func() { return t.B } + +type X int + +func (X) A() { panic("FAIL") } +func (X) B() {} +func (X) C() { panic("FAIL") } diff --git a/test/typeparam/mdempsky/2.go b/test/typeparam/mdempsky/2.go new file mode 100644 index 00000000000000..ad548e6a549f92 --- /dev/null +++ b/test/typeparam/mdempsky/2.go @@ -0,0 +1,20 @@ +// compile + +// Copyright 2021 The Go Authors. 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 T[A, B, C any] int + +func (T[A, B, C]) m(x int) { + if x <= 0 { + return + } + T[B, C, A](0).m(x - 1) +} + +func main() { + T[int8, int16, int32](0).m(3) +} diff --git a/test/typeparam/mdempsky/20.go b/test/typeparam/mdempsky/20.go new file mode 100644 index 00000000000000..6b97ca102c3440 --- /dev/null +++ b/test/typeparam/mdempsky/20.go @@ -0,0 +1,38 @@ +// run + +// Copyright 2022 The Go 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 with a derived receiver type and +// promoted methods work correctly. + +package main + +func main() { + F[int]() + F[string]() +} + +func F[X any]() { + call(T[X].M, T[X].N) +} + +func call[X any](fns ...func(T[X]) int) { + for want, fn := range fns { + if have := fn(T[X]{}); have != want { + println("FAIL:", have, "!=", want) + } + } +} + +type T[X any] struct { + E1 + *E2[*X] +} + +type E1 struct{} +type E2[_ any] struct{} + +func (E1) M() int { return 0 } +func (*E2[_]) N() int { return 1 } diff --git a/test/typeparam/mdempsky/21.go b/test/typeparam/mdempsky/21.go new file mode 100644 index 00000000000000..da10ae3ea32988 --- /dev/null +++ b/test/typeparam/mdempsky/21.go @@ -0,0 +1,26 @@ +// run + +// Copyright 2022 The Go 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 devirtualization doesn't introduce spurious type +// assertion failures due to shaped and non-shaped interfaces having +// distinct itabs. + +package main + +func main() { + F[int]() +} + +func F[T any]() { + var i I[T] = X(0) + i.M() +} + +type I[T any] interface{ M() } + +type X int + +func (X) M() {} diff --git a/test/typeparam/mdempsky/3.dir/a.go b/test/typeparam/mdempsky/3.dir/a.go new file mode 100644 index 00000000000000..cf456e8d48f174 --- /dev/null +++ b/test/typeparam/mdempsky/3.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. 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 F[T interface{ chan int }](c T) {} diff --git a/test/typeparam/mdempsky/3.dir/b.go b/test/typeparam/mdempsky/3.dir/b.go new file mode 100644 index 00000000000000..0cfd142f4c454e --- /dev/null +++ b/test/typeparam/mdempsky/3.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 g() { a.F(make(chan int)) } diff --git a/test/typeparam/mdempsky/3.go b/test/typeparam/mdempsky/3.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/mdempsky/3.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/4.dir/a.go b/test/typeparam/mdempsky/4.dir/a.go new file mode 100644 index 00000000000000..cb672949eaedcd --- /dev/null +++ b/test/typeparam/mdempsky/4.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 F[T any](T) { +Loop: + for { + break Loop + } +} diff --git a/test/typeparam/mdempsky/4.dir/b.go b/test/typeparam/mdempsky/4.dir/b.go new file mode 100644 index 00000000000000..e1fb0e7c5eaabc --- /dev/null +++ b/test/typeparam/mdempsky/4.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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(0) } diff --git a/test/typeparam/mdempsky/4.go b/test/typeparam/mdempsky/4.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/mdempsky/4.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/5.go b/test/typeparam/mdempsky/5.go new file mode 100644 index 00000000000000..00d3b71be0ee53 --- /dev/null +++ b/test/typeparam/mdempsky/5.go @@ -0,0 +1,15 @@ +// compile + +// Copyright 2021 The Go Authors. 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 X[T any] int + +func (X[T]) F(T) {} + +func x() { + X[interface{}](0).F(0) +} diff --git a/test/typeparam/mdempsky/6.go b/test/typeparam/mdempsky/6.go new file mode 100644 index 00000000000000..ed57009436850f --- /dev/null +++ b/test/typeparam/mdempsky/6.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2021 The Go Authors. 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 I[T any] interface{ M() T } + +var _ = I[int].M diff --git a/test/typeparam/mdempsky/7.dir/a.go b/test/typeparam/mdempsky/7.dir/a.go new file mode 100644 index 00000000000000..59c5995611834a --- /dev/null +++ b/test/typeparam/mdempsky/7.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. 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 I[T any] interface{ M() T } + +var X I[int] diff --git a/test/typeparam/mdempsky/7.dir/b.go b/test/typeparam/mdempsky/7.dir/b.go new file mode 100644 index 00000000000000..9f70530811482e --- /dev/null +++ b/test/typeparam/mdempsky/7.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import "./a" + +var _ = a.X diff --git a/test/typeparam/mdempsky/7.go b/test/typeparam/mdempsky/7.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/mdempsky/7.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/8.dir/a.go b/test/typeparam/mdempsky/8.dir/a.go new file mode 100644 index 00000000000000..607fe5e0af2e09 --- /dev/null +++ b/test/typeparam/mdempsky/8.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. 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 F[T interface{ comparable }]() {} diff --git a/test/typeparam/mdempsky/8.dir/b.go b/test/typeparam/mdempsky/8.dir/b.go new file mode 100644 index 00000000000000..84037bf763ecb6 --- /dev/null +++ b/test/typeparam/mdempsky/8.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 init() { + a.F[func()]() // ERROR "does not implement comparable" +} diff --git a/test/typeparam/mdempsky/8.go b/test/typeparam/mdempsky/8.go new file mode 100644 index 00000000000000..e3a470b4195ab9 --- /dev/null +++ b/test/typeparam/mdempsky/8.go @@ -0,0 +1,7 @@ +// errorcheckdir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mdempsky/9.go b/test/typeparam/mdempsky/9.go new file mode 100644 index 00000000000000..948a9e532e0c76 --- /dev/null +++ b/test/typeparam/mdempsky/9.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2021 The Go Authors. 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 f[V any]() []V { return []V{0: *new(V)} } + +func g() { f[int]() } diff --git a/test/typeparam/metrics.go b/test/typeparam/metrics.go index 8a39d9945d6119..dcc5737a66f36d 100644 --- a/test/typeparam/metrics.go +++ b/test/typeparam/metrics.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/min.go b/test/typeparam/min.go index a3e4464a3039c8..a9224507dfc2f2 100644 --- a/test/typeparam/min.go +++ b/test/typeparam/min.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,7 +11,7 @@ import ( ) type Ordered interface { - type int, int64, float64 + ~int | ~int64 | ~float64 | ~string } func min[T Ordered](x, y T) T { @@ -38,4 +38,13 @@ func main() { if got := min(3.5, 2.0); got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } + + const want2 = "ay" + if got := min[string]("bb", "ay"); got != want2 { + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } + + if got := min("bb", "ay"); got != want2 { + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } } diff --git a/test/typeparam/mincheck.dir/a.go b/test/typeparam/mincheck.dir/a.go new file mode 100644 index 00000000000000..fa0f249e61510c --- /dev/null +++ b/test/typeparam/mincheck.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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 Ordered interface { + int | int64 | float64 +} + +func Min[T Ordered](x, y T) T { + if x < y { + return x + } + return y +} diff --git a/test/typeparam/mincheck.dir/main.go b/test/typeparam/mincheck.dir/main.go new file mode 100644 index 00000000000000..c9ca50a23be50b --- /dev/null +++ b/test/typeparam/mincheck.dir/main.go @@ -0,0 +1,38 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + const want = 2 + if got := a.Min[int](2, 3); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Min(2, 3); got != want { + panic(fmt.Sprintf("want %d, got %d", want, got)) + } + + if got := a.Min[float64](3.5, 2.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Min(3.5, 2.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + const want2 = "ay" + if got := a.Min[string]("bb", "ay"); got != want2 { // ERROR "string does not implement" + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } + + if got := a.Min("bb", "ay"); got != want2 { // ERROR "string does not implement" + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } +} diff --git a/test/typeparam/mincheck.go b/test/typeparam/mincheck.go new file mode 100644 index 00000000000000..e3a470b4195ab9 --- /dev/null +++ b/test/typeparam/mincheck.go @@ -0,0 +1,7 @@ +// errorcheckdir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/minimp.dir/a.go b/test/typeparam/minimp.dir/a.go new file mode 100644 index 00000000000000..fabde62c5d7218 --- /dev/null +++ b/test/typeparam/minimp.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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 Ordered interface { + ~int | ~int64 | ~float64 | ~string +} + +func Min[T Ordered](x, y T) T { + if x < y { + return x + } + return y +} diff --git a/test/typeparam/minimp.dir/main.go b/test/typeparam/minimp.dir/main.go new file mode 100644 index 00000000000000..36bec0f600de2d --- /dev/null +++ b/test/typeparam/minimp.dir/main.go @@ -0,0 +1,38 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + const want = 2 + if got := a.Min[int](2, 3); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Min(2, 3); got != want { + panic(fmt.Sprintf("want %d, got %d", want, got)) + } + + if got := a.Min[float64](3.5, 2.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + if got := a.Min(3.5, 2.0); got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + + const want2 = "ay" + if got := a.Min[string]("bb", "ay"); got != want2 { + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } + + if got := a.Min("bb", "ay"); got != want2 { + panic(fmt.Sprintf("got %d, want %d", got, want2)) + } +} diff --git a/test/typeparam/minimp.go b/test/typeparam/minimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/minimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/mutualimp.dir/a.go b/test/typeparam/mutualimp.dir/a.go new file mode 100644 index 00000000000000..5b924d3ce5dfec --- /dev/null +++ b/test/typeparam/mutualimp.dir/a.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 X int + +func (x X) M() X { return x } + +func F[T interface{ M() U }, U interface{ M() T }]() {} +func G() { F[X, X]() } diff --git a/test/typeparam/mutualimp.dir/b.go b/test/typeparam/mutualimp.dir/b.go new file mode 100644 index 00000000000000..83cc3af2835c55 --- /dev/null +++ b/test/typeparam/mutualimp.dir/b.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 H() { + a.F[a.X, a.X]() + a.G() +} diff --git a/test/typeparam/mutualimp.go b/test/typeparam/mutualimp.go new file mode 100644 index 00000000000000..b83fbd7af16a57 --- /dev/null +++ b/test/typeparam/mutualimp.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/nested.go b/test/typeparam/nested.go new file mode 100644 index 00000000000000..cdb8bfb57424fb --- /dev/null +++ b/test/typeparam/nested.go @@ -0,0 +1,134 @@ +// run + +// Copyright 2021 The Go 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 test case stress tests a number of subtle cases involving +// nested type-parameterized declarations. At a high-level, it +// declares a generic function that contains a generic type +// declaration: +// +// func F[A intish]() { +// type T[B intish] struct{} +// +// // store reflect.Type tuple (A, B, F[A].T[B]) in tests +// } +// +// It then instantiates this function with a variety of type arguments +// for A and B. Particularly tricky things like shadowed types. +// +// From this data it tests two things: +// +// 1. Given tuples (A, B, F[A].T[B]) and (A', B', F[A'].T[B']), +// F[A].T[B] should be identical to F[A'].T[B'] iff (A, B) is +// identical to (A', B'). +// +// 2. A few of the instantiations are constructed to be identical, and +// it tests that exactly these pairs are duplicated (by golden +// output comparison to nested.out). +// +// In both cases, we're effectively using the compiler's existing +// runtime.Type handling (which is well tested) of type identity of A +// and B as a way to help bootstrap testing and validate its new +// runtime.Type handling of F[A].T[B]. +// +// This isn't perfect, but it smoked out a handful of issues in +// gotypes2 and unified IR. + +package main + +import ( + "fmt" + "reflect" +) + +type test struct { + TArgs [2]reflect.Type + Instance reflect.Type +} + +var tests []test + +type intish interface{ ~int } + +type Int int +type GlobalInt = Int // allow access to global Int, even when shadowed + +func F[A intish]() { + add := func(B, T interface{}) { + tests = append(tests, test{ + TArgs: [2]reflect.Type{ + reflect.TypeOf(A(0)), + reflect.TypeOf(B), + }, + Instance: reflect.TypeOf(T), + }) + } + + type Int int + + type T[B intish] struct{} + + add(int(0), T[int]{}) + add(Int(0), T[Int]{}) + add(GlobalInt(0), T[GlobalInt]{}) + add(A(0), T[A]{}) // NOTE: intentionally dups with int and GlobalInt + + type U[_ any] int + type V U[int] + type W V + + add(U[int](0), T[U[int]]{}) + add(U[Int](0), T[U[Int]]{}) + add(U[GlobalInt](0), T[U[GlobalInt]]{}) + add(U[A](0), T[U[A]]{}) // NOTE: intentionally dups with U[int] and U[GlobalInt] + add(V(0), T[V]{}) + add(W(0), T[W]{}) +} + +func main() { + type Int int + + F[int]() + F[Int]() + F[GlobalInt]() + + type U[_ any] int + type V U[int] + type W V + + F[U[int]]() + F[U[Int]]() + F[U[GlobalInt]]() + F[V]() + F[W]() + + type X[A any] U[X[A]] + + F[X[int]]() + F[X[Int]]() + F[X[GlobalInt]]() + + for j, tj := range tests { + for i, ti := range tests[:j+1] { + if (ti.TArgs == tj.TArgs) != (ti.Instance == tj.Instance) { + fmt.Printf("FAIL: %d,%d: %s, but %s\n", i, j, eq(ti.TArgs, tj.TArgs), eq(ti.Instance, tj.Instance)) + } + + // The test is constructed so we should see a few identical types. + // See "NOTE" comments above. + if i != j && ti.Instance == tj.Instance { + fmt.Printf("%d,%d: %v\n", i, j, ti.Instance) + } + } + } +} + +func eq(a, b interface{}) string { + op := "==" + if a != b { + op = "!=" + } + return fmt.Sprintf("%v %s %v", a, op, b) +} diff --git a/test/typeparam/nested.out b/test/typeparam/nested.out new file mode 100644 index 00000000000000..0836d9b0dc0642 --- /dev/null +++ b/test/typeparam/nested.out @@ -0,0 +1,4 @@ +0,3: main.T[int;int] +4,7: main.T[int;main.U[int;int]·3] +22,23: main.T[main.Int;main.Int] +26,27: main.T[main.Int;main.U[main.Int;main.Int]·3] diff --git a/test/typeparam/ordered.go b/test/typeparam/ordered.go index 448db68bb55bf5..d30429885c1510 100644 --- a/test/typeparam/ordered.go +++ b/test/typeparam/ordered.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -13,15 +13,15 @@ import ( ) type Ordered interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - string + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string } type orderedSlice[Elem Ordered] []Elem -func (s orderedSlice[Elem]) Len() int { return len(s) } +func (s orderedSlice[Elem]) Len() int { return len(s) } func (s orderedSlice[Elem]) Less(i, j int) bool { if s[i] < s[j] { return true @@ -32,7 +32,7 @@ func (s orderedSlice[Elem]) Less(i, j int) bool { } return false } -func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func _OrderedSlice[Elem Ordered](s []Elem) { sort.Sort(orderedSlice[Elem](s)) @@ -68,7 +68,7 @@ func testOrdered[Elem Ordered](name string, s []Elem, sorter func([]Elem)) bool } for i := len(s1) - 1; i > 0; i-- { if s1[i] < s1[i-1] { - fmt.Printf("%s: element %d (%v) < element %d (%v)", name, i, s1[i], i - 1, s1[i - 1]) + fmt.Printf("%s: element %d (%v) < element %d (%v)", name, i, s1[i], i-1, s1[i-1]) ok = false } } diff --git a/test/typeparam/orderedmap.go b/test/typeparam/orderedmap.go index db1b3742674080..12456699a5a1ef 100644 --- a/test/typeparam/orderedmap.go +++ b/test/typeparam/orderedmap.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -15,10 +15,10 @@ import ( ) type Ordered interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - string + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string } // _Map is an ordered map. @@ -230,7 +230,7 @@ func _Ranger[Elem any]() (*_Sender[Elem], *_Receiver[Elem]) { values: c, done: d, } - r := &_Receiver[Elem] { + r := &_Receiver[Elem]{ values: c, done: d, } diff --git a/test/typeparam/orderedmapsimp.dir/a.go b/test/typeparam/orderedmapsimp.dir/a.go new file mode 100644 index 00000000000000..d6a2de5d7b3e4a --- /dev/null +++ b/test/typeparam/orderedmapsimp.dir/a.go @@ -0,0 +1,226 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "context" + "runtime" +) + +type Ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +// Map is an ordered map. +type Map[K, V any] struct { + root *node[K, V] + compare func(K, K) int +} + +// node is the type of a node in the binary tree. +type node[K, V any] struct { + key K + val V + left, right *node[K, V] +} + +// New returns a new map. It takes a comparison function that compares two +// keys and returns < 0 if the first is less, == 0 if they are equal, +// > 0 if the first is greater. +func New[K, V any](compare func(K, K) int) *Map[K, V] { + return &Map[K, V]{compare: compare} +} + +// NewOrdered returns a new map whose key is an ordered type. +// This is like New, but does not require providing a compare function. +// The map compare function uses the obvious key ordering. +func NewOrdered[K Ordered, V any]() *Map[K, V] { + return New[K, V](func(k1, k2 K) int { + switch { + case k1 < k2: + return -1 + case k1 > k2: + return 1 + default: + return 0 + } + }) +} + +// find looks up key in the map, returning either a pointer to the slot of the +// node holding key, or a pointer to the slot where a node would go. +func (m *Map[K, V]) find(key K) **node[K, V] { + pn := &m.root + for *pn != nil { + switch cmp := m.compare(key, (*pn).key); { + case cmp < 0: + pn = &(*pn).left + case cmp > 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Reports whether this is a new key. +func (m *Map[K, V]) Insert(key K, val V) bool { + pn := m.find(key) + if *pn != nil { + (*pn).val = val + return false + } + *pn = &node[K, V]{key: key, val: val} + return true +} + +// Find returns the value associated with a key, or the zero value +// if not present. The second result reports whether the key was found. +func (m *Map[K, V]) Find(key K) (V, bool) { + pn := m.find(key) + if *pn == nil { + var zero V + return zero, false + } + return (*pn).val, true +} + +// keyValue is a pair of key and value used while iterating. +type keyValue[K, V any] struct { + key K + val V +} + +// iterate returns an iterator that traverses the map. +func (m *Map[K, V]) Iterate() *Iterator[K, V] { + sender, receiver := Ranger[keyValue[K, V]]() + var f func(*node[K, V]) bool + f = func(n *node[K, V]) bool { + if n == nil { + return true + } + // Stop the traversal if Send fails, which means that + // nothing is listening to the receiver. + return f(n.left) && + sender.Send(context.Background(), keyValue[K, V]{n.key, n.val}) && + f(n.right) + } + go func() { + f(m.root) + sender.Close() + }() + return &Iterator[K, V]{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator[K, V any] struct { + r *Receiver[keyValue[K, V]] +} + +// Next returns the next key and value pair, and a boolean that reports +// whether they are valid. If not valid, we have reached the end of the map. +func (it *Iterator[K, V]) Next() (K, V, bool) { + keyval, ok := it.r.Next(context.Background()) + if !ok { + var zerok K + var zerov V + return zerok, zerov, false + } + return keyval.key, keyval.val, true +} + +// Equal reports whether two slices are equal: the same length and all +// elements equal. All floating point NaNs are considered equal. +func SliceEqual[Elem comparable](s1, s2 []Elem) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if v1 != v2 { + isNaN := func(f Elem) bool { return f != f } + if !isNaN(v1) || !isNaN(v2) { + return false + } + } + } + return true +} + +// Ranger returns a Sender and a Receiver. The Receiver provides a +// Next method to retrieve values. The Sender provides a Send method +// to send values and a Close method to stop sending values. The Next +// method indicates when the Sender has been closed, and the Send +// method indicates when the Receiver has been freed. +// +// This is a convenient way to exit a goroutine sending values when +// the receiver stops reading them. +func Ranger[Elem any]() (*Sender[Elem], *Receiver[Elem]) { + c := make(chan Elem) + d := make(chan struct{}) + s := &Sender[Elem]{ + values: c, + done: d, + } + r := &Receiver[Elem]{ + values: c, + done: d, + } + runtime.SetFinalizer(r, (*Receiver[Elem]).finalize) + return s, r +} + +// A Sender is used to send values to a Receiver. +type Sender[Elem any] struct { + values chan<- Elem + done <-chan struct{} +} + +// Send sends a value to the receiver. It reports whether the value was sent. +// The value will not be sent if the context is closed or the receiver +// is freed. +func (s *Sender[Elem]) Send(ctx context.Context, v Elem) bool { + select { + case <-ctx.Done(): + return false + case s.values <- v: + return true + case <-s.done: + return false + } +} + +// Close tells the receiver that no more values will arrive. +// After Close is called, the Sender may no longer be used. +func (s *Sender[Elem]) Close() { + close(s.values) +} + +// A Receiver receives values from a Sender. +type Receiver[Elem any] struct { + values <-chan Elem + done chan<- struct{} +} + +// Next returns the next value from the channel. The bool result indicates +// whether the value is valid. +func (r *Receiver[Elem]) Next(ctx context.Context) (v Elem, ok bool) { + select { + case <-ctx.Done(): + case v, ok = <-r.values: + } + return v, ok +} + +// finalize is a finalizer for the receiver. +func (r *Receiver[Elem]) finalize() { + close(r.done) +} diff --git a/test/typeparam/orderedmapsimp.dir/main.go b/test/typeparam/orderedmapsimp.dir/main.go new file mode 100644 index 00000000000000..7758a75c233776 --- /dev/null +++ b/test/typeparam/orderedmapsimp.dir/main.go @@ -0,0 +1,64 @@ +// Copyright 2021 The Go Authors. 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" + "bytes" + "fmt" +) + +func TestMap() { + m := a.New[[]byte, int](bytes.Compare) + + if _, found := m.Find([]byte("a")); found { + panic(fmt.Sprintf("unexpectedly found %q in empty map", []byte("a"))) + } + + for _, c := range []int{'a', 'c', 'b'} { + if !m.Insert([]byte(string(c)), c) { + panic(fmt.Sprintf("key %q unexpectedly already present", []byte(string(c)))) + } + } + if m.Insert([]byte("c"), 'x') { + panic(fmt.Sprintf("key %q unexpectedly not present", []byte("c"))) + } + + if v, found := m.Find([]byte("a")); !found { + panic(fmt.Sprintf("did not find %q", []byte("a"))) + } else if v != 'a' { + panic(fmt.Sprintf("key %q returned wrong value %c, expected %c", []byte("a"), v, 'a')) + } + if v, found := m.Find([]byte("c")); !found { + panic(fmt.Sprintf("did not find %q", []byte("c"))) + } else if v != 'x' { + panic(fmt.Sprintf("key %q returned wrong value %c, expected %c", []byte("c"), v, 'x')) + } + + if _, found := m.Find([]byte("d")); found { + panic(fmt.Sprintf("unexpectedly found %q", []byte("d"))) + } + + gather := func(it *a.Iterator[[]byte, int]) []int { + var r []int + for { + _, v, ok := it.Next() + if !ok { + return r + } + r = append(r, v) + } + } + got := gather(m.Iterate()) + want := []int{'a', 'b', 'x'} + if !a.SliceEqual(got, want) { + panic(fmt.Sprintf("Iterate returned %v, want %v", got, want)) + } + +} + +func main() { + TestMap() +} diff --git a/test/typeparam/orderedmapsimp.go b/test/typeparam/orderedmapsimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/orderedmapsimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/pair.go b/test/typeparam/pair.go index 7faf083c89d117..dd0adb18400c28 100644 --- a/test/typeparam/pair.go +++ b/test/typeparam/pair.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -24,7 +24,11 @@ func main() { if got, want := unsafe.Sizeof(p.f2), uintptr(8); got != want { panic(fmt.Sprintf("unexpected f2 size == %d, want %d", got, want)) } - type mypair struct { f1 int32; f2 int64 } + + type mypair struct { + f1 int32 + f2 int64 + } mp := mypair(p) if mp.f1 != 1 || mp.f2 != 2 { panic(fmt.Sprintf("mp == %#v, want %#v", mp, mypair{1, 2})) diff --git a/test/typeparam/pairimp.dir/a.go b/test/typeparam/pairimp.dir/a.go new file mode 100644 index 00000000000000..a984fba37b6e66 --- /dev/null +++ b/test/typeparam/pairimp.dir/a.go @@ -0,0 +1,10 @@ +// Copyright 2021 The Go Authors. 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 Pair[F1, F2 any] struct { + Field1 F1 + Field2 F2 +} diff --git a/test/typeparam/pairimp.dir/main.go b/test/typeparam/pairimp.dir/main.go new file mode 100644 index 00000000000000..f76da434d4cfe3 --- /dev/null +++ b/test/typeparam/pairimp.dir/main.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "unsafe" +) + +func main() { + p := a.Pair[int32, int64]{1, 2} + if got, want := unsafe.Sizeof(p.Field1), uintptr(4); got != want { + panic(fmt.Sprintf("unexpected f1 size == %d, want %d", got, want)) + } + if got, want := unsafe.Sizeof(p.Field2), uintptr(8); got != want { + panic(fmt.Sprintf("unexpected f2 size == %d, want %d", got, want)) + } + + type mypair struct { + Field1 int32 + Field2 int64 + } + mp := mypair(p) + if mp.Field1 != 1 || mp.Field2 != 2 { + panic(fmt.Sprintf("mp == %#v, want %#v", mp, mypair{1, 2})) + } +} diff --git a/test/typeparam/pairimp.go b/test/typeparam/pairimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/pairimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/pragma.go b/test/typeparam/pragma.go index 6743e24ff390ef..59411ab6e61446 100644 --- a/test/typeparam/pragma.go +++ b/test/typeparam/pragma.go @@ -1,4 +1,4 @@ -// errorcheck -0 -m -G=3 +// errorcheck -0 -m // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/recoverimp.dir/a.go b/test/typeparam/recoverimp.dir/a.go new file mode 100644 index 00000000000000..a465fd1545fe9d --- /dev/null +++ b/test/typeparam/recoverimp.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import "fmt" + +func F[T any](a T) { + defer func() { + if x := recover(); x != nil { + fmt.Printf("panic: %v\n", x) + } + }() + panic(a) +} diff --git a/test/typeparam/recoverimp.dir/main.go b/test/typeparam/recoverimp.dir/main.go new file mode 100644 index 00000000000000..d8cfa3875c0b5e --- /dev/null +++ b/test/typeparam/recoverimp.dir/main.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "./a" + +func main() { + a.F(5.3) + a.F("hello") +} diff --git a/test/typeparam/recoverimp.go b/test/typeparam/recoverimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/recoverimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/recoverimp.out b/test/typeparam/recoverimp.out new file mode 100644 index 00000000000000..3c8b38cbaeaa7a --- /dev/null +++ b/test/typeparam/recoverimp.out @@ -0,0 +1,2 @@ +panic: 5.3 +panic: hello diff --git a/test/typeparam/select.dir/a.go b/test/typeparam/select.dir/a.go new file mode 100644 index 00000000000000..983e4b1d5f7cd8 --- /dev/null +++ b/test/typeparam/select.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 F[T any](c, d chan T) T { + select { + case x := <- c: + return x + case x := <- d: + return x + } +} + diff --git a/test/typeparam/select.dir/main.go b/test/typeparam/select.dir/main.go new file mode 100644 index 00000000000000..6ea3fe2eeac3a4 --- /dev/null +++ b/test/typeparam/select.dir/main.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. 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 ( + "sort" + + "./a" +) + +func main() { + c := make(chan int, 1) + d := make(chan int, 1) + + c <- 5 + d <- 6 + + var r [2]int + r[0] = a.F(c, d) + r[1] = a.F(c, d) + sort.Ints(r[:]) + + if r != [2]int{5, 6} { + panic("incorrect results") + } +} diff --git a/test/typeparam/select.go b/test/typeparam/select.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/select.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/sets.go b/test/typeparam/sets.go index 258514489e1708..bd08ad7b4b6f9c 100644 --- a/test/typeparam/sets.go +++ b/test/typeparam/sets.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -160,7 +160,7 @@ func TestSet() { vals := s1.Values() sort.Ints(vals) w1 := []int{1, 2, 3, 4} - if !_SliceEqual(vals, w1) { + if !_SliceEqual(vals, w1) { panic(fmt.Sprintf("(%v).Values() == %v, want %v", s1, vals, w1)) } } diff --git a/test/typeparam/setsimp.dir/a.go b/test/typeparam/setsimp.dir/a.go new file mode 100644 index 00000000000000..92449ce95620ca --- /dev/null +++ b/test/typeparam/setsimp.dir/a.go @@ -0,0 +1,128 @@ +// Copyright 2021 The Go Authors. 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 + +// SliceEqual reports whether two slices are equal: the same length and all +// elements equal. All floating point NaNs are considered equal. +func SliceEqual[Elem comparable](s1, s2 []Elem) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if v1 != v2 { + isNaN := func(f Elem) bool { return f != f } + if !isNaN(v1) || !isNaN(v2) { + return false + } + } + } + return true +} + +// A Set is a set of elements of some type. +type Set[Elem comparable] struct { + m map[Elem]struct{} +} + +// Make makes a new set. +func Make[Elem comparable]() Set[Elem] { + return Set[Elem]{m: make(map[Elem]struct{})} +} + +// Add adds an element to a set. +func (s Set[Elem]) Add(v Elem) { + s.m[v] = struct{}{} +} + +// Delete removes an element from a set. If the element is not present +// in the set, this does nothing. +func (s Set[Elem]) Delete(v Elem) { + delete(s.m, v) +} + +// Contains reports whether v is in the set. +func (s Set[Elem]) Contains(v Elem) bool { + _, ok := s.m[v] + return ok +} + +// Len returns the number of elements in the set. +func (s Set[Elem]) Len() int { + return len(s.m) +} + +// Values returns the values in the set. +// The values will be in an indeterminate order. +func (s Set[Elem]) Values() []Elem { + r := make([]Elem, 0, len(s.m)) + for v := range s.m { + r = append(r, v) + } + return r +} + +// Equal reports whether two sets contain the same elements. +func Equal[Elem comparable](s1, s2 Set[Elem]) bool { + if len(s1.m) != len(s2.m) { + return false + } + for v1 := range s1.m { + if !s2.Contains(v1) { + return false + } + } + return true +} + +// Copy returns a copy of s. +func (s Set[Elem]) Copy() Set[Elem] { + r := Set[Elem]{m: make(map[Elem]struct{}, len(s.m))} + for v := range s.m { + r.m[v] = struct{}{} + } + return r +} + +// AddSet adds all the elements of s2 to s. +func (s Set[Elem]) AddSet(s2 Set[Elem]) { + for v := range s2.m { + s.m[v] = struct{}{} + } +} + +// SubSet removes all elements in s2 from s. +// Values in s2 that are not in s are ignored. +func (s Set[Elem]) SubSet(s2 Set[Elem]) { + for v := range s2.m { + delete(s.m, v) + } +} + +// Intersect removes all elements from s that are not present in s2. +// Values in s2 that are not in s are ignored. +func (s Set[Elem]) Intersect(s2 Set[Elem]) { + for v := range s.m { + if !s2.Contains(v) { + delete(s.m, v) + } + } +} + +// Iterate calls f on every element in the set. +func (s Set[Elem]) Iterate(f func(Elem)) { + for v := range s.m { + f(v) + } +} + +// Filter deletes any elements from s for which f returns false. +func (s Set[Elem]) Filter(f func(Elem) bool) { + for v := range s.m { + if !f(v) { + delete(s.m, v) + } + } +} diff --git a/test/typeparam/setsimp.dir/main.go b/test/typeparam/setsimp.dir/main.go new file mode 100644 index 00000000000000..e1ec86a6f0d030 --- /dev/null +++ b/test/typeparam/setsimp.dir/main.go @@ -0,0 +1,156 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "sort" +) + +func TestSet() { + s1 := a.Make[int]() + if got := s1.Len(); got != 0 { + panic(fmt.Sprintf("Len of empty set = %d, want 0", got)) + } + s1.Add(1) + s1.Add(1) + s1.Add(1) + if got := s1.Len(); got != 1 { + panic(fmt.Sprintf("(%v).Len() == %d, want 1", s1, got)) + } + s1.Add(2) + s1.Add(3) + s1.Add(4) + if got := s1.Len(); got != 4 { + panic(fmt.Sprintf("(%v).Len() == %d, want 4", s1, got)) + } + if !s1.Contains(1) { + panic(fmt.Sprintf("(%v).Contains(1) == false, want true", s1)) + } + if s1.Contains(5) { + panic(fmt.Sprintf("(%v).Contains(5) == true, want false", s1)) + } + vals := s1.Values() + sort.Ints(vals) + w1 := []int{1, 2, 3, 4} + if !a.SliceEqual(vals, w1) { + panic(fmt.Sprintf("(%v).Values() == %v, want %v", s1, vals, w1)) + } +} + +func TestEqual() { + s1 := a.Make[string]() + s2 := a.Make[string]() + if !a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s1, s2)) + } + s1.Add("hello") + s1.Add("world") + if got := s1.Len(); got != 2 { + panic(fmt.Sprintf("(%v).Len() == %d, want 2", s1, got)) + } + if a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", s1, s2)) + } +} + +func TestCopy() { + s1 := a.Make[float64]() + s1.Add(0) + s2 := s1.Copy() + if !a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s1, s2)) + } + s1.Add(1) + if a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", s1, s2)) + } +} + +func TestAddSet() { + s1 := a.Make[int]() + s1.Add(1) + s1.Add(2) + s2 := a.Make[int]() + s2.Add(2) + s2.Add(3) + s1.AddSet(s2) + if got := s1.Len(); got != 3 { + panic(fmt.Sprintf("(%v).Len() == %d, want 3", s1, got)) + } + s2.Add(1) + if !a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s1, s2)) + } +} + +func TestSubSet() { + s1 := a.Make[int]() + s1.Add(1) + s1.Add(2) + s2 := a.Make[int]() + s2.Add(2) + s2.Add(3) + s1.SubSet(s2) + if got := s1.Len(); got != 1 { + panic(fmt.Sprintf("(%v).Len() == %d, want 1", s1, got)) + } + if vals, want := s1.Values(), []int{1}; !a.SliceEqual(vals, want) { + panic(fmt.Sprintf("after SubSet got %v, want %v", vals, want)) + } +} + +func TestIntersect() { + s1 := a.Make[int]() + s1.Add(1) + s1.Add(2) + s2 := a.Make[int]() + s2.Add(2) + s2.Add(3) + s1.Intersect(s2) + if got := s1.Len(); got != 1 { + panic(fmt.Sprintf("(%v).Len() == %d, want 1", s1, got)) + } + if vals, want := s1.Values(), []int{2}; !a.SliceEqual(vals, want) { + panic(fmt.Sprintf("after Intersect got %v, want %v", vals, want)) + } +} + +func TestIterate() { + s1 := a.Make[int]() + s1.Add(1) + s1.Add(2) + s1.Add(3) + s1.Add(4) + tot := 0 + s1.Iterate(func(i int) { tot += i }) + if tot != 10 { + panic(fmt.Sprintf("total of %v == %d, want 10", s1, tot)) + } +} + +func TestFilter() { + s1 := a.Make[int]() + s1.Add(1) + s1.Add(2) + s1.Add(3) + s1.Filter(func(v int) bool { return v%2 == 0 }) + if vals, want := s1.Values(), []int{2}; !a.SliceEqual(vals, want) { + panic(fmt.Sprintf("after Filter got %v, want %v", vals, want)) + } + +} + +func main() { + TestSet() + TestEqual() + TestCopy() + TestAddSet() + TestSubSet() + TestIntersect() + TestIterate() + TestFilter() +} diff --git a/test/typeparam/setsimp.go b/test/typeparam/setsimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/setsimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/settable.go b/test/typeparam/settable.go index 588166da8584fa..56cf36745b0369 100644 --- a/test/typeparam/settable.go +++ b/test/typeparam/settable.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -13,13 +13,13 @@ import ( // Various implementations of fromStrings(). -type _Setter[B any] interface { +type Setter[B any] interface { Set(string) - type *B + *B } // Takes two type parameters where PT = *T -func fromStrings1[T any, PT _Setter[T]](s []string) []T { +func fromStrings1[T any, PT Setter[T]](s []string) []T { result := make([]T, len(s)) for i, v := range s { // The type of &result[i] is *T which is in the type list @@ -31,7 +31,7 @@ func fromStrings1[T any, PT _Setter[T]](s []string) []T { return result } -func fromStrings1a[T any, PT _Setter[T]](s []string) []PT { +func fromStrings1a[T any, PT Setter[T]](s []string) []PT { result := make([]PT, len(s)) for i, v := range s { // The type new(T) is *T which is in the type list @@ -44,7 +44,6 @@ func fromStrings1a[T any, PT _Setter[T]](s []string) []PT { return result } - // Takes one type parameter and a set function func fromStrings2[T any](s []string, set func(*T, string)) []T { results := make([]T, len(s)) @@ -54,12 +53,12 @@ func fromStrings2[T any](s []string, set func(*T, string)) []T { return results } -type _Setter2 interface { +type Setter2 interface { Set(string) } // Takes only one type parameter, but causes a panic (see below) -func fromStrings3[T _Setter2](s []string) []T { +func fromStrings3[T Setter2](s []string) []T { results := make([]T, len(s)) for i, v := range s { // Panics if T is a pointer type because receiver is T(nil). diff --git a/test/typeparam/shape1.go b/test/typeparam/shape1.go new file mode 100644 index 00000000000000..2400f1c29a237d --- /dev/null +++ b/test/typeparam/shape1.go @@ -0,0 +1,50 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I interface { + foo() int +} + +// There should be one instantiation of f for both squarer and doubler. +// Similarly, there should be one instantiation of f for both *incrementer and *decrementer. +func f[T I](x T) int { + return x.foo() +} + +type squarer int + +func (x squarer) foo() int { + return int(x*x) +} + +type doubler int + +func (x doubler) foo() int { + return int(2*x) +} + +type incrementer int16 + +func (x *incrementer) foo() int { + return int(*x+1) +} + +type decrementer int32 + +func (x *decrementer) foo() int{ + return int(*x-1) +} + +func main() { + println(f(squarer(5))) + println(f(doubler(5))) + var i incrementer = 5 + println(f(&i)) + var d decrementer = 5 + println(f(&d)) +} diff --git a/test/typeparam/shape1.out b/test/typeparam/shape1.out new file mode 100644 index 00000000000000..da9a12ded55a5e --- /dev/null +++ b/test/typeparam/shape1.out @@ -0,0 +1,4 @@ +25 +10 +6 +4 diff --git a/test/typeparam/sliceimp.dir/a.go b/test/typeparam/sliceimp.dir/a.go new file mode 100644 index 00000000000000..dbcfae893185e3 --- /dev/null +++ b/test/typeparam/sliceimp.dir/a.go @@ -0,0 +1,141 @@ +// Copyright 2021 The Go Authors. 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 Ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +// Max returns the maximum of two values of some ordered type. +func Max[T Ordered](a, b T) T { + if a > b { + return a + } + return b +} + +// Min returns the minimum of two values of some ordered type. +func Min[T Ordered](a, b T) T { + if a < b { + return a + } + return b +} + +// Equal reports whether two slices are equal: the same length and all +// elements equal. All floating point NaNs are considered equal. +func Equal[Elem comparable](s1, s2 []Elem) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if v1 != v2 { + isNaN := func(f Elem) bool { return f != f } + if !isNaN(v1) || !isNaN(v2) { + return false + } + } + } + return true +} + +// EqualFn reports whether two slices are equal using a comparison +// function on each element. +func EqualFn[Elem any](s1, s2 []Elem, eq func(Elem, Elem) bool) bool { + if len(s1) != len(s2) { + return false + } + for i, v1 := range s1 { + v2 := s2[i] + if !eq(v1, v2) { + return false + } + } + return true +} + +// Map turns a []Elem1 to a []Elem2 using a mapping function. +func Map[Elem1, Elem2 any](s []Elem1, f func(Elem1) Elem2) []Elem2 { + r := make([]Elem2, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} + +// Reduce reduces a []Elem1 to a single value of type Elem2 using +// a reduction function. +func Reduce[Elem1, Elem2 any](s []Elem1, initializer Elem2, f func(Elem2, Elem1) Elem2) Elem2 { + r := initializer + for _, v := range s { + r = f(r, v) + } + return r +} + +// Filter filters values from a slice using a filter function. +func Filter[Elem any](s []Elem, f func(Elem) bool) []Elem { + var r []Elem + for _, v := range s { + if f(v) { + r = append(r, v) + } + } + return r +} + +// Max returns the maximum element in a slice of some ordered type. +// If the slice is empty it returns the zero value of the element type. +func SliceMax[Elem Ordered](s []Elem) Elem { + if len(s) == 0 { + var zero Elem + return zero + } + return Reduce(s[1:], s[0], Max[Elem]) +} + +// Min returns the minimum element in a slice of some ordered type. +// If the slice is empty it returns the zero value of the element type. +func SliceMin[Elem Ordered](s []Elem) Elem { + if len(s) == 0 { + var zero Elem + return zero + } + return Reduce(s[1:], s[0], Min[Elem]) +} + +// Append adds values to the end of a slice, returning a new slice. +// This is like the predeclared append function; it's an example +// of how to write it using generics. We used to write code like +// this before append was added to the language, but we had to write +// a separate copy for each type. +func Append[T any](s []T, t ...T) []T { + lens := len(s) + tot := lens + len(t) + if tot <= cap(s) { + s = s[:tot] + } else { + news := make([]T, tot, tot+tot/2) + Copy(news, s) + s = news + } + Copy(s[lens:tot], t) + return s +} + +// Copy copies values from t to s, stopping when either slice is full, +// returning the number of values copied. This is like the predeclared +// copy function; it's an example of how to write it using generics. +func Copy[T any](s, t []T) int { + i := 0 + for ; i < len(s) && i < len(t); i++ { + s[i] = t[i] + } + return i +} diff --git a/test/typeparam/sliceimp.dir/main.go b/test/typeparam/sliceimp.dir/main.go new file mode 100644 index 00000000000000..ec13188ba9654d --- /dev/null +++ b/test/typeparam/sliceimp.dir/main.go @@ -0,0 +1,179 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "math" + "strings" +) + +type Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +func TestEqual() { + s1 := []int{1, 2, 3} + if !a.Equal(s1, s1) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s1, s1)) + } + s2 := []int{1, 2, 3} + if !a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s1, s2)) + } + s2 = append(s2, 4) + if a.Equal(s1, s2) { + panic(fmt.Sprintf("a.Equal(%v, %v) = true, want false", s1, s2)) + } + + s3 := []float64{1, 2, math.NaN()} + if !a.Equal(s3, s3) { + panic(fmt.Sprintf("a.Equal(%v, %v) = false, want true", s3, s3)) + } + + if a.Equal(s1, nil) { + panic(fmt.Sprintf("a.Equal(%v, nil) = true, want false", s1)) + } + if a.Equal(nil, s1) { + panic(fmt.Sprintf("a.Equal(nil, %v) = true, want false", s1)) + } + if !a.Equal(s1[:0], nil) { + panic(fmt.Sprintf("a.Equal(%v, nil = false, want true", s1[:0])) + } +} + +func offByOne[Elem Integer](a, b Elem) bool { + return a == b+1 || a == b-1 +} + +func TestEqualFn() { + s1 := []int{1, 2, 3} + s2 := []int{2, 3, 4} + if a.EqualFn(s1, s1, offByOne[int]) { + panic(fmt.Sprintf("a.EqualFn(%v, %v, offByOne) = true, want false", s1, s1)) + } + if !a.EqualFn(s1, s2, offByOne[int]) { + panic(fmt.Sprintf("a.EqualFn(%v, %v, offByOne) = false, want true", s1, s2)) + } + + if !a.EqualFn(s1[:0], nil, offByOne[int]) { + panic(fmt.Sprintf("a.EqualFn(%v, nil, offByOne) = false, want true", s1[:0])) + } + + s3 := []string{"a", "b", "c"} + s4 := []string{"A", "B", "C"} + if !a.EqualFn(s3, s4, strings.EqualFold) { + panic(fmt.Sprintf("a.EqualFn(%v, %v, strings.EqualFold) = false, want true", s3, s4)) + } +} + +func TestMap() { + s1 := []int{1, 2, 3} + s2 := a.Map(s1, func(i int) float64 { return float64(i) * 2.5 }) + if want := []float64{2.5, 5, 7.5}; !a.Equal(s2, want) { + panic(fmt.Sprintf("a.Map(%v, ...) = %v, want %v", s1, s2, want)) + } + + s3 := []string{"Hello", "World"} + s4 := a.Map(s3, strings.ToLower) + if want := []string{"hello", "world"}; !a.Equal(s4, want) { + panic(fmt.Sprintf("a.Map(%v, strings.ToLower) = %v, want %v", s3, s4, want)) + } + + s5 := a.Map(nil, func(i int) int { return i }) + if len(s5) != 0 { + panic(fmt.Sprintf("a.Map(nil, identity) = %v, want empty slice", s5)) + } +} + +func TestReduce() { + s1 := []int{1, 2, 3} + r := a.Reduce(s1, 0, func(f float64, i int) float64 { return float64(i)*2.5 + f }) + if want := 15.0; r != want { + panic(fmt.Sprintf("a.Reduce(%v, 0, ...) = %v, want %v", s1, r, want)) + } + + if got := a.Reduce(nil, 0, func(i, j int) int { return i + j }); got != 0 { + panic(fmt.Sprintf("a.Reduce(nil, 0, add) = %v, want 0", got)) + } +} + +func TestFilter() { + s1 := []int{1, 2, 3} + s2 := a.Filter(s1, func(i int) bool { return i%2 == 0 }) + if want := []int{2}; !a.Equal(s2, want) { + panic(fmt.Sprintf("a.Filter(%v, even) = %v, want %v", s1, s2, want)) + } + + if s3 := a.Filter(s1[:0], func(i int) bool { return true }); len(s3) > 0 { + panic(fmt.Sprintf("a.Filter(%v, identity) = %v, want empty slice", s1[:0], s3)) + } +} + +func TestMax() { + s1 := []int{1, 2, 3, -5} + if got, want := a.SliceMax(s1), 3; got != want { + panic(fmt.Sprintf("a.Max(%v) = %d, want %d", s1, got, want)) + } + + s2 := []string{"aaa", "a", "aa", "aaaa"} + if got, want := a.SliceMax(s2), "aaaa"; got != want { + panic(fmt.Sprintf("a.Max(%v) = %q, want %q", s2, got, want)) + } + + if got, want := a.SliceMax(s2[:0]), ""; got != want { + panic(fmt.Sprintf("a.Max(%v) = %q, want %q", s2[:0], got, want)) + } +} + +func TestMin() { + s1 := []int{1, 2, 3, -5} + if got, want := a.SliceMin(s1), -5; got != want { + panic(fmt.Sprintf("a.Min(%v) = %d, want %d", s1, got, want)) + } + + s2 := []string{"aaa", "a", "aa", "aaaa"} + if got, want := a.SliceMin(s2), "a"; got != want { + panic(fmt.Sprintf("a.Min(%v) = %q, want %q", s2, got, want)) + } + + if got, want := a.SliceMin(s2[:0]), ""; got != want { + panic(fmt.Sprintf("a.Min(%v) = %q, want %q", s2[:0], got, want)) + } +} + +func TestAppend() { + s := []int{1, 2, 3} + s = a.Append(s, 4, 5, 6) + want := []int{1, 2, 3, 4, 5, 6} + if !a.Equal(s, want) { + panic(fmt.Sprintf("after a.Append got %v, want %v", s, want)) + } +} + +func TestCopy() { + s1 := []int{1, 2, 3} + s2 := []int{4, 5} + if got := a.Copy(s1, s2); got != 2 { + panic(fmt.Sprintf("a.Copy returned %d, want 2", got)) + } + want := []int{4, 5, 3} + if !a.Equal(s1, want) { + panic(fmt.Sprintf("after a.Copy got %v, want %v", s1, want)) + } +} +func main() { + TestEqual() + TestEqualFn() + TestMap() + TestReduce() + TestFilter() + TestMax() + TestMin() + TestAppend() + TestCopy() +} diff --git a/test/typeparam/sliceimp.go b/test/typeparam/sliceimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/sliceimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/slices.go b/test/typeparam/slices.go index 149199eb64909d..b24817deb0375b 100644 --- a/test/typeparam/slices.go +++ b/test/typeparam/slices.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -15,31 +15,31 @@ import ( ) type Ordered interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - string + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string } type Integer interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } // Max returns the maximum of two values of some ordered type. func _Max[T Ordered](a, b T) T { - if a > b { - return a - } - return b + if a > b { + return a + } + return b } // Min returns the minimum of two values of some ordered type. func _Min[T Ordered](a, b T) T { - if a < b { - return a - } - return b + if a < b { + return a + } + return b } // _Equal reports whether two slices are equal: the same length and all @@ -60,7 +60,7 @@ func _Equal[Elem comparable](s1, s2 []Elem) bool { return true } -// _EqualFn reports whether two slices are equal using a comparision +// _EqualFn reports whether two slices are equal using a comparison // function on each element. func _EqualFn[Elem any](s1, s2 []Elem, eq func(Elem, Elem) bool) bool { if len(s1) != len(s2) { @@ -136,7 +136,7 @@ func _Append[T any](s []T, t ...T) []T { if tot <= cap(s) { s = s[:tot] } else { - news := make([]T, tot, tot + tot/2) + news := make([]T, tot, tot+tot/2) _Copy(news, s) s = news } @@ -156,37 +156,37 @@ func _Copy[T any](s, t []T) int { } func TestEqual() { - s1 := []int{1, 2, 3} - if !_Equal(s1, s1) { - panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s1, s1)) - } - s2 := []int{1, 2, 3} - if !_Equal(s1, s2) { - panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s1, s2)) - } - s2 = append(s2, 4) - if _Equal(s1, s2) { - panic(fmt.Sprintf("_Equal(%v, %v) = true, want false", s1, s2)) - } - - s3 := []float64{1, 2, math.NaN()} - if !_Equal(s3, s3) { - panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s3, s3)) - } - - if _Equal(s1, nil) { - panic(fmt.Sprintf("_Equal(%v, nil) = true, want false", s1)) - } - if _Equal(nil, s1) { - panic(fmt.Sprintf("_Equal(nil, %v) = true, want false", s1)) - } - if !_Equal(s1[:0], nil) { - panic(fmt.Sprintf("_Equal(%v, nil = false, want true", s1[:0])) - } + s1 := []int{1, 2, 3} + if !_Equal(s1, s1) { + panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s1, s1)) + } + s2 := []int{1, 2, 3} + if !_Equal(s1, s2) { + panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s1, s2)) + } + s2 = append(s2, 4) + if _Equal(s1, s2) { + panic(fmt.Sprintf("_Equal(%v, %v) = true, want false", s1, s2)) + } + + s3 := []float64{1, 2, math.NaN()} + if !_Equal(s3, s3) { + panic(fmt.Sprintf("_Equal(%v, %v) = false, want true", s3, s3)) + } + + if _Equal(s1, nil) { + panic(fmt.Sprintf("_Equal(%v, nil) = true, want false", s1)) + } + if _Equal(nil, s1) { + panic(fmt.Sprintf("_Equal(nil, %v) = true, want false", s1)) + } + if !_Equal(s1[:0], nil) { + panic(fmt.Sprintf("_Equal(%v, nil = false, want true", s1[:0])) + } } func offByOne[Elem Integer](a, b Elem) bool { - return a == b + 1 || a == b - 1 + return a == b+1 || a == b-1 } func TestEqualFn() { @@ -231,12 +231,12 @@ func TestMap() { func TestReduce() { s1 := []int{1, 2, 3} - r := _Reduce(s1, 0, func(f float64, i int) float64 { return float64(i) * 2.5 + f }) + r := _Reduce(s1, 0, func(f float64, i int) float64 { return float64(i)*2.5 + f }) if want := 15.0; r != want { panic(fmt.Sprintf("_Reduce(%v, 0, ...) = %v, want %v", s1, r, want)) } - if got := _Reduce(nil, 0, func(i, j int) int { return i + j}); got != 0 { + if got := _Reduce(nil, 0, func(i, j int) int { return i + j }); got != 0 { panic(fmt.Sprintf("_Reduce(nil, 0, add) = %v, want 0", got)) } } diff --git a/test/typeparam/smallest.go b/test/typeparam/smallest.go index 63dd9ddb700fe3..0ebd10ec3b6d84 100644 --- a/test/typeparam/smallest.go +++ b/test/typeparam/smallest.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,13 +11,13 @@ import ( ) type Ordered interface { - type int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, uintptr, - float32, float64, - string + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string } -func smallest[T Ordered](s []T) T { +func Smallest[T Ordered](s []T) T { r := s[0] // panics if slice is empty for _, v := range s[1:] { if v < r { @@ -32,11 +32,11 @@ func main() { vec2 := []string{"abc", "def", "aaa"} want1 := 1.2 - if got := smallest(vec1); got != want1 { + if got := Smallest(vec1); got != want1 { panic(fmt.Sprintf("got %d, want %d", got, want1)) } want2 := "aaa" - if got := smallest(vec2); got != want2 { + if got := Smallest(vec2); got != want2 { panic(fmt.Sprintf("got %d, want %d", got, want2)) } } diff --git a/test/typeparam/smoketest.go b/test/typeparam/smoketest.go index b7d6201b2c1249..b720e04d4a85e8 100644 --- a/test/typeparam/smoketest.go +++ b/test/typeparam/smoketest.go @@ -1,4 +1,4 @@ -// compile -G +// compile // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -9,9 +9,9 @@ package smoketest // type parameters for functions -func f1[P any]() -func f2[P1, P2 any, P3 any]() -func f3[P interface{}](x P, y T1[int]) +func f1[P any]() {} +func f2[P1, P2 any, P3 any]() {} +func f3[P interface{}](x P, y T1[int]) {} // function instantiations var _ = f1[int] @@ -29,15 +29,15 @@ type _ T2[int, string, struct{}] type _ T3[bool] // methods -func (T1[P]) m1() {} -func (T1[_]) m2() {} +func (T1[P]) m1() {} +func (T1[_]) m2() {} func (x T2[P1, P2, P3]) m() {} // type lists type _ interface { m1() m2() - type int, float32, string + int | float32 | string m3() } diff --git a/test/typeparam/stringable.go b/test/typeparam/stringable.go index 9340a3b10a9d18..791b670a9155a9 100644 --- a/test/typeparam/stringable.go +++ b/test/typeparam/stringable.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -16,11 +16,11 @@ type Stringer interface { String() string } -// stringableList is a slice of some type, where the type +// StringableList is a slice of some type, where the type // must have a String method. -type stringableList[T Stringer] []T +type StringableList[T Stringer] []T -func (s stringableList[T]) String() string { +func (s StringableList[T]) String() string { var sb strings.Builder for i, v := range s { if i > 0 { @@ -38,9 +38,9 @@ func (a myint) String() string { } func main() { - v := stringableList[myint]{ myint(1), myint(2) } + v := StringableList[myint]{myint(1), myint(2)} if got, want := v.String(), "1, 2"; got != want { - panic(fmt.Sprintf("got %s, want %s", got, want)) + panic(fmt.Sprintf("got %s, want %s", got, want)) } } diff --git a/test/typeparam/stringer.go b/test/typeparam/stringer.go index 81290d599ecda5..0892cd881d546b 100644 --- a/test/typeparam/stringer.go +++ b/test/typeparam/stringer.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/test/typeparam/stringerimp.dir/a.go b/test/typeparam/stringerimp.dir/a.go new file mode 100644 index 00000000000000..3f70937ff55e86 --- /dev/null +++ b/test/typeparam/stringerimp.dir/a.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. 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 Stringer interface { + String() string +} + +func Stringify[T Stringer](s []T) (ret []string) { + for _, v := range s { + ret = append(ret, v.String()) + } + return ret +} diff --git a/test/typeparam/stringerimp.dir/main.go b/test/typeparam/stringerimp.dir/main.go new file mode 100644 index 00000000000000..9b41d3bc1db010 --- /dev/null +++ b/test/typeparam/stringerimp.dir/main.go @@ -0,0 +1,38 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" + "reflect" + "strconv" +) + +type myint int + +func (i myint) String() string { + return strconv.Itoa(int(i)) +} + +func main() { + x := []myint{myint(1), myint(2), myint(3)} + + got := a.Stringify(x) + want := []string{"1", "2", "3"} + if !reflect.DeepEqual(got, want) { + panic(fmt.Sprintf("got %s, want %s", got, want)) + } + + m1 := myint(1) + m2 := myint(2) + m3 := myint(3) + y := []*myint{&m1, &m2, &m3} + got2 := a.Stringify(y) + want2 := []string{"1", "2", "3"} + if !reflect.DeepEqual(got2, want2) { + panic(fmt.Sprintf("got %s, want %s", got2, want2)) + } +} diff --git a/test/typeparam/stringerimp.go b/test/typeparam/stringerimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/stringerimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/struct.go b/test/typeparam/struct.go index 98f0fcd888839d..2dad9087bc9aee 100644 --- a/test/typeparam/struct.go +++ b/test/typeparam/struct.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -10,40 +10,40 @@ import ( "fmt" ) -type _E[T any] struct { +type E[T any] struct { v T } -type _S1 struct { - _E[int] +type S1 struct { + E[int] v string } -type _Eint = _E[int] -type _Ebool = _E[bool] +type Eint = E[int] +type Ebool = E[bool] -type _S2 struct { - _Eint - _Ebool +type S2 struct { + Eint + Ebool v string } -type _S3 struct { - *_E[int] +type S3 struct { + *E[int] } func main() { - s1 := _S1{_Eint{2}, "foo"} - if got, want := s1._E.v, 2; got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + s1 := S1{Eint{2}, "foo"} + if got, want := s1.E.v, 2; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) } - s2 := _S2{_Eint{3}, _Ebool{true}, "foo"} - if got, want := s2._Eint.v, 3; got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + s2 := S2{Eint{3}, Ebool{true}, "foo"} + if got, want := s2.Eint.v, 3; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) } - var s3 _S3 - s3._E = &_Eint{4} - if got, want := s3._E.v, 4; got != want { - panic(fmt.Sprintf("got %d, want %d", got, want)) + var s3 S3 + s3.E = &Eint{4} + if got, want := s3.E.v, 4; got != want { + panic(fmt.Sprintf("got %d, want %d", got, want)) } } diff --git a/test/typeparam/structinit.dir/a.go b/test/typeparam/structinit.dir/a.go new file mode 100644 index 00000000000000..c76d1551adbeea --- /dev/null +++ b/test/typeparam/structinit.dir/a.go @@ -0,0 +1,15 @@ +// Copyright 2021 The Go Authors. 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 S[T any] struct { +} + +func (b *S[T]) build() *X[T] { + return &X[T]{f:0} +} +type X[T any] struct { + f int +} diff --git a/test/typeparam/structinit.dir/b.go b/test/typeparam/structinit.dir/b.go new file mode 100644 index 00000000000000..40a929bcaede4a --- /dev/null +++ b/test/typeparam/structinit.dir/b.go @@ -0,0 +1,12 @@ +// Copyright 2021 The Go Authors. 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 B() { + var x a.S[int] + _ = x +} diff --git a/test/typeparam/structinit.dir/main.go b/test/typeparam/structinit.dir/main.go new file mode 100644 index 00000000000000..c564171879578c --- /dev/null +++ b/test/typeparam/structinit.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. 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 "./b" + +func main() { + b.B() +} diff --git a/test/typeparam/structinit.go b/test/typeparam/structinit.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/structinit.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/subdict.go b/test/typeparam/subdict.go new file mode 100644 index 00000000000000..463c510484c571 --- /dev/null +++ b/test/typeparam/subdict.go @@ -0,0 +1,43 @@ +// run + +// Copyright 2021 The Go 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 where a main dictionary is needed inside a generic function/method, because +// we are calling a method on a fully-instantiated type or a fully-instantiated function. +// (probably not common situations, of course) + +package main + +import ( + "fmt" +) + +type C comparable + +type value[T C] struct { + val T +} + +func (v *value[T]) test(def T) bool { + return (v.val == def) +} + +func (v *value[T]) get(def T) T { + var c value[int] + if c.test(32) { + return def + } else if v.test(def) { + return def + } else { + return v.val + } +} + +func main() { + var s value[string] + if got, want := s.get("ab"), ""; got != want { + panic(fmt.Sprintf("get() == %d, want %d", got, want)) + } +} diff --git a/test/typeparam/sum.go b/test/typeparam/sum.go index f0f5e6aa07537b..25bac181aa9d96 100644 --- a/test/typeparam/sum.go +++ b/test/typeparam/sum.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -10,7 +10,7 @@ import ( "fmt" ) -func sum[T interface{ type int, float64 }](vec []T) T { +func Sum[T interface{ int | float64 }](vec []T) T { var sum T for _, elt := range vec { sum = sum + elt @@ -18,7 +18,7 @@ func sum[T interface{ type int, float64 }](vec []T) T { return sum } -func abs(f float64) float64 { +func Abs(f float64) float64 { if f < 0.0 { return -f } @@ -28,23 +28,23 @@ func abs(f float64) float64 { func main() { vec1 := []int{3, 4} vec2 := []float64{5.8, 9.6} - got := sum[int](vec1) + got := Sum[int](vec1) want := vec1[0] + vec1[1] if got != want { panic(fmt.Sprintf("got %d, want %d", got, want)) } - got = sum(vec1) + got = Sum(vec1) if want != got { panic(fmt.Sprintf("got %d, want %d", got, want)) } fwant := vec2[0] + vec2[1] - fgot := sum[float64](vec2) - if abs(fgot - fwant) > 1e-10 { + fgot := Sum[float64](vec2) + if Abs(fgot-fwant) > 1e-10 { panic(fmt.Sprintf("got %f, want %f", fgot, fwant)) } - fgot = sum(vec2) - if abs(fgot - fwant) > 1e-10 { + fgot = Sum(vec2) + if Abs(fgot-fwant) > 1e-10 { panic(fmt.Sprintf("got %f, want %f", fgot, fwant)) } } diff --git a/test/typeparam/tparam1.go b/test/typeparam/tparam1.go index 70439333269a70..a05f54265b941e 100644 --- a/test/typeparam/tparam1.go +++ b/test/typeparam/tparam1.go @@ -1,4 +1,4 @@ -// errorcheck -G +// errorcheck // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -8,35 +8,36 @@ package tparam1 -// The predeclared identifier "any" is only visible as a constraint -// in a type parameter list. -var _ any // ERROR "undefined" -func _(_ any) // ERROR "undefined" -type _[_ any /* ok here */ ] struct{} +// The predeclared identifier "any" may be used in place of interface{}. +var _ any + +func _(_ any) + +type _[_ any] struct{} const N = 10 type ( - _[] struct{} // slice - _[N] struct{} // array - _[T any] struct{} - _[T, T any] struct{} // ERROR "T redeclared" - _[T1, T2 any, T3 any] struct{} + _ []struct{} // slice + _ [N]struct{} // array + _[T any] struct{} + _[T, T any] struct{} // ERROR "T redeclared" + _[T1, T2 any, T3 any] struct{} ) -func _[T any]() -func _[T, T any]() // ERROR "T redeclared" -func _[T1, T2 any](x T1) T2 +func _[T any]() {} +func _[T, T any]() {} // ERROR "T redeclared" +func _[T1, T2 any](x T1) T2 { panic(0) } // Type parameters are visible from opening [ to end of function. type C interface{} -func _[T interface{}]() -func _[T C]() -func _[T struct{}]() // ERROR "not an interface" -func _[T interface{ m() T }]() +func _[T interface{}]() {} +func _[T C]() {} +func _[T struct{}]() {} // ok if #48424 is accepted +func _[T interface{ m() T }]() {} func _[T1 interface{ m() T2 }, T2 interface{ m() T1 }]() { - var _ T1 + var _ T1 } // TODO(gri) expand this diff --git a/test/typeparam/typelist.go b/test/typeparam/typelist.go index bd90d86fcf2a36..7c713212b061be 100644 --- a/test/typeparam/typelist.go +++ b/test/typeparam/typelist.go @@ -1,90 +1,96 @@ -// compile -G=3 +// compile // Copyright 2021 The Go 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 tests type lists & structural constraints. +// This file tests type lists & constraints with core types. + +// Note: This test has been adjusted to use the new +// type set notation rather than type lists. package p // Assignability of an unnamed pointer type to a type parameter that // has a matching underlying type. -func _[T interface{}, PT interface{type *T}] (x T) PT { - return &x +func _[T interface{}, PT interface{ ~*T }](x T) PT { + return &x } // Indexing of generic types containing type parameters in their type list: -func at[T interface{ type []E }, E any](x T, i int) E { - return x[i] +func at[T interface{ ~[]E }, E any](x T, i int) E { + return x[i] } // A generic type inside a function acts like a named type. Its underlying // type is itself, its "operational type" is defined by the type list in // the tybe bound, if any. -func _[T interface{type int}](x T) { - type myint int +func _[T interface{ ~int }](x T) { var _ int = int(x) var _ T = 42 var _ T = T(myint(42)) } -// Indexing a generic type which has a structural contraints to be an array. -func _[T interface { type [10]int }](x T) { +// TODO: put this type declaration back inside the above function when issue 47631 is fixed. +type myint int + +// Indexing a generic type which has a an array as core type. +func _[T interface{ ~[10]int }](x T) { _ = x[9] // ok } -// Dereference of a generic type which has a structural contraint to be a pointer. -func _[T interface{ type *int }](p T) int { +// Dereference of a generic type which has a pointer as core type. +func _[T interface{ ~*int }](p T) int { return *p } -// Channel send and receive on a generic type which has a structural constraint to -// be a channel. -func _[T interface{ type chan int }](ch T) int { +// Channel send and receive on a generic type which has a channel as core type. +func _[T interface{ ~chan int }](ch T) int { // This would deadlock if executed (but ok for a compile test) ch <- 0 - return <- ch + return <-ch } -// Calling of a generic type which has a structural constraint to be a function. -func _[T interface{ type func() }](f T) { +// Calling of a generic type which has a function as core type. +func _[T interface{ ~func() }](f T) { f() go f() } // Same, but function has a parameter and return value. -func _[T interface{ type func(string) int }](f T) int { +func _[T interface{ ~func(string) int }](f T) int { return f("hello") } -// Map access of a generic type which has a structural constraint to be a map. -func _[V any, T interface { type map[string]V }](p T) V { +// Map access of a generic type which has a map as core type. +func _[V any, T interface{ ~map[string]V }](p T) V { return p["test"] } - // Testing partial and full type inference, including the case where the types can // be inferred without needing the types of the function arguments. +// Cannot embed stand-alone type parameters. Disabled for now. +/* func f0[A any, B interface{type C}, C interface{type D}, D interface{type A}](A, B, C, D) -func _() { +func f0x() { f := f0[string] f("a", "b", "c", "d") f0("a", "b", "c", "d") } func f1[A any, B interface{type A}](A, B) -func _() { +func f1x() { f := f1[int] f(int(0), int(0)) f1(int(0), int(0)) f(0, 0) f1(0, 0) } +*/ -func f2[A any, B interface{type []A}](_ A, _ B) -func _() { +func f2[A any, B interface{ []A }](_ A, _ B) {} +func f2x() { f := f2[byte] f(byte(0), []byte{}) f2(byte(0), []byte{}) @@ -92,31 +98,41 @@ func _() { // f2(0, []byte{}) - this one doesn't work } +// Cannot embed stand-alone type parameters. Disabled for now. +/* func f3[A any, B interface{type C}, C interface{type *A}](a A, _ B, c C) -func _() { +func f3x() { f := f3[int] var x int f(x, &x, &x) f3(x, &x, &x) } +*/ -func f4[A any, B interface{type []C}, C interface{type *A}](_ A, _ B, c C) -func _() { +func f4[A any, B interface{ []C }, C interface{ *A }](_ A, _ B, c C) {} +func f4x() { f := f4[int] var x int f(x, []*int{}, &x) f4(x, []*int{}, &x) } -func f5[A interface{type struct{b B; c C}}, B any, C interface{type *B}](x B) A -func _() { +func f5[A interface { + struct { + b B + c C + } +}, B any, C interface{ *B }](x B) A { + panic(0) +} +func f5x() { x := f5(1.2) var _ float64 = x.b var _ float64 = *x.c } -func f6[A any, B interface{type struct{f []A}}](B) A -func _() { - x := f6(struct{f []string}{}) +func f6[A any, B interface{ ~struct{ f []A } }](B) A { panic(0) } +func f6x() { + x := f6(struct{ f []string }{}) var _ string = x } diff --git a/test/typeparam/typeswitch1.go b/test/typeparam/typeswitch1.go new file mode 100644 index 00000000000000..a0468d378fba70 --- /dev/null +++ b/test/typeparam/typeswitch1.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T any](i interface{}) { + switch i.(type) { + case T: + println("T") + case int: + println("int") + case int32, int16: + println("int32/int16") + case struct{ a, b T }: + println("struct{T,T}") + default: + println("other") + } +} +func main() { + f[float64](float64(6)) + f[float64](int(7)) + f[float64](int32(8)) + f[float64](struct{ a, b float64 }{a: 1, b: 2}) + f[float64](int8(9)) + f[int32](int32(7)) + f[int](int32(7)) + f[any](int(10)) + f[interface{ M() }](int(11)) +} diff --git a/test/typeparam/typeswitch1.out b/test/typeparam/typeswitch1.out new file mode 100644 index 00000000000000..6b8a33c3457537 --- /dev/null +++ b/test/typeparam/typeswitch1.out @@ -0,0 +1,9 @@ +T +int +int32/int16 +struct{T,T} +other +T +int32/int16 +T +int diff --git a/test/typeparam/typeswitch2.go b/test/typeparam/typeswitch2.go new file mode 100644 index 00000000000000..286002a830263b --- /dev/null +++ b/test/typeparam/typeswitch2.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func f[T any](i interface{}) { + switch x := i.(type) { + case T: + fmt.Println("T", x) + case int: + fmt.Println("int", x) + case int32, int16: + fmt.Println("int32/int16", x) + case struct{ a, b T }: + fmt.Println("struct{T,T}", x.a, x.b) + default: + fmt.Println("other", x) + } +} +func main() { + f[float64](float64(6)) + f[float64](int(7)) + f[float64](int32(8)) + f[float64](struct{ a, b float64 }{a: 1, b: 2}) + f[float64](int8(9)) + f[int32](int32(7)) + f[int](int32(7)) + f[any](int(10)) + f[interface{ M() }](int(11)) +} diff --git a/test/typeparam/typeswitch2.out b/test/typeparam/typeswitch2.out new file mode 100644 index 00000000000000..6d4df54124c0fc --- /dev/null +++ b/test/typeparam/typeswitch2.out @@ -0,0 +1,9 @@ +T 6 +int 7 +int32/int16 8 +struct{T,T} 1 2 +other 9 +T 7 +int32/int16 7 +T 10 +int 11 diff --git a/test/typeparam/typeswitch3.go b/test/typeparam/typeswitch3.go new file mode 100644 index 00000000000000..b84fdd02eaae37 --- /dev/null +++ b/test/typeparam/typeswitch3.go @@ -0,0 +1,48 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I interface{ foo() int } +type J interface { + I + bar() +} + +type myint int + +func (x myint) foo() int { return int(x) } + +type myfloat float64 + +func (x myfloat) foo() int { return int(x) } + +type myint32 int32 + +func (x myint32) foo() int { return int(x) } +func (x myint32) bar() {} + +func f[T I](i I) { + switch x := i.(type) { + case T: + println("T", x.foo()) + case myint: + println("myint", x.foo()) + default: + println("other", x.foo()) + } +} +func main() { + f[myfloat](myint(6)) + f[myfloat](myfloat(7)) + f[myfloat](myint32(8)) + f[myint32](myint32(8)) + f[myint32](myfloat(7)) + f[myint](myint32(9)) + f[I](myint(10)) + f[J](myint(11)) + f[J](myint32(12)) +} diff --git a/test/typeparam/typeswitch3.out b/test/typeparam/typeswitch3.out new file mode 100644 index 00000000000000..05ed5331973bde --- /dev/null +++ b/test/typeparam/typeswitch3.out @@ -0,0 +1,9 @@ +myint 6 +T 7 +other 8 +T 8 +other 7 +other 9 +T 10 +myint 11 +T 12 diff --git a/test/typeparam/typeswitch4.go b/test/typeparam/typeswitch4.go new file mode 100644 index 00000000000000..3fdf5527202ef3 --- /dev/null +++ b/test/typeparam/typeswitch4.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type I interface{ foo() int } +type J interface { + I + bar() +} + +type myint int + +func (x myint) foo() int { return int(x) } + +type myfloat float64 + +func (x myfloat) foo() int { return int(x) } + +type myint32 int32 + +func (x myint32) foo() int { return int(x) } +func (x myint32) bar() {} + +func f[T I](i I) { + switch x := i.(type) { + case T, myint32: + println("T/myint32", x.foo()) + default: + println("other", x.foo()) + } +} +func main() { + f[myfloat](myint(6)) + f[myfloat](myfloat(7)) + f[myfloat](myint32(8)) + f[myint32](myint32(9)) + f[myint](myint32(10)) + f[myint](myfloat(42)) + f[I](myint(10)) + f[J](myint(11)) + f[J](myint32(12)) +} diff --git a/test/typeparam/typeswitch4.out b/test/typeparam/typeswitch4.out new file mode 100644 index 00000000000000..b98f0743c2ea18 --- /dev/null +++ b/test/typeparam/typeswitch4.out @@ -0,0 +1,9 @@ +other 6 +T/myint32 7 +T/myint32 8 +T/myint32 9 +T/myint32 10 +other 42 +T/myint32 10 +other 11 +T/myint32 12 diff --git a/test/typeparam/typeswitch5.go b/test/typeparam/typeswitch5.go new file mode 100644 index 00000000000000..ac52adbdd0709f --- /dev/null +++ b/test/typeparam/typeswitch5.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2021 The Go Authors. 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 myint int +func (x myint) foo() int {return int(x)} + +type myfloat float64 +func (x myfloat) foo() float64 {return float64(x) } + +func f[T any](i interface{}) { + switch x := i.(type) { + case interface { foo() T }: + println("fooer", x.foo()) + default: + println("other") + } +} +func main() { + f[int](myint(6)) + f[int](myfloat(7)) + f[float64](myint(8)) + f[float64](myfloat(9)) +} diff --git a/test/typeparam/typeswitch5.out b/test/typeparam/typeswitch5.out new file mode 100644 index 00000000000000..6b4cb4416f4536 --- /dev/null +++ b/test/typeparam/typeswitch5.out @@ -0,0 +1,4 @@ +fooer 6 +other +other +fooer +9.000000e+000 diff --git a/test/typeparam/typeswitch6.go b/test/typeparam/typeswitch6.go new file mode 100644 index 00000000000000..81d4f2082dbee5 --- /dev/null +++ b/test/typeparam/typeswitch6.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T any](i interface{}) { + switch i.(type) { + case T: + println("T") + case int: + println("int") + default: + println("other") + } +} + +type myint int +func (myint) foo() { +} + +func main() { + f[interface{}](nil) + f[interface{}](6) + f[interface{foo()}](nil) + f[interface{foo()}](7) + f[interface{foo()}](myint(8)) +} diff --git a/test/typeparam/typeswitch6.out b/test/typeparam/typeswitch6.out new file mode 100644 index 00000000000000..441add5ec5bed8 --- /dev/null +++ b/test/typeparam/typeswitch6.out @@ -0,0 +1,5 @@ +other +T +other +int +T diff --git a/test/typeparam/typeswitch7.go b/test/typeparam/typeswitch7.go new file mode 100644 index 00000000000000..067bed713811d8 --- /dev/null +++ b/test/typeparam/typeswitch7.go @@ -0,0 +1,37 @@ +// run + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f[T any](i interface{foo()}) { + switch i.(type) { + case interface{bar() T}: + println("barT") + case myint: + println("myint") + case myfloat: + println("myfloat") + default: + println("other") + } +} + +type myint int +func (myint) foo() { +} +func (x myint) bar() int { + return int(x) +} + +type myfloat float64 +func (myfloat) foo() { +} + +func main() { + f[int](nil) + f[int](myint(6)) + f[int](myfloat(7)) +} diff --git a/test/typeparam/typeswitch7.out b/test/typeparam/typeswitch7.out new file mode 100644 index 00000000000000..d7fcad4fee14f8 --- /dev/null +++ b/test/typeparam/typeswitch7.out @@ -0,0 +1,3 @@ +other +barT +myfloat diff --git a/test/typeparam/valimp.dir/a.go b/test/typeparam/valimp.dir/a.go new file mode 100644 index 00000000000000..2ed0063cfd6473 --- /dev/null +++ b/test/typeparam/valimp.dir/a.go @@ -0,0 +1,32 @@ +// Copyright 2021 The Go Authors. 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 Value[T any] struct { + val T +} + +// The noinline directive should survive across import, and prevent instantiations +// of these functions from being inlined. + +//go:noinline +func Get[T any](v *Value[T]) T { + return v.val +} + +//go:noinline +func Set[T any](v *Value[T], val T) { + v.val = val +} + +//go:noinline +func (v *Value[T]) Set(val T) { + v.val = val +} + +//go:noinline +func (v *Value[T]) Get() T { + return v.val +} diff --git a/test/typeparam/valimp.dir/main.go b/test/typeparam/valimp.dir/main.go new file mode 100644 index 00000000000000..e357af4615f1ab --- /dev/null +++ b/test/typeparam/valimp.dir/main.go @@ -0,0 +1,55 @@ +// Copyright 2021 The Go Authors. 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" + "fmt" +) + +func main() { + var v1 a.Value[int] + + a.Set(&v1, 1) + if got, want := a.Get(&v1), 1; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } + v1.Set(2) + if got, want := v1.Get(), 2; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } + v1p := new(a.Value[int]) + a.Set(v1p, 3) + if got, want := a.Get(v1p), 3; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } + + v1p.Set(4) + if got, want := v1p.Get(), 4; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } + + var v2 a.Value[string] + a.Set(&v2, "a") + if got, want := a.Get(&v2), "a"; got != want { + panic(fmt.Sprintf("Get() == %q, want %q", got, want)) + } + + v2.Set("b") + if got, want := a.Get(&v2), "b"; got != want { + panic(fmt.Sprintf("Get() == %q, want %q", got, want)) + } + + v2p := new(a.Value[string]) + a.Set(v2p, "c") + if got, want := a.Get(v2p), "c"; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } + + v2p.Set("d") + if got, want := v2p.Get(), "d"; got != want { + panic(fmt.Sprintf("Get() == %d, want %d", got, want)) + } +} diff --git a/test/typeparam/valimp.go b/test/typeparam/valimp.go new file mode 100644 index 00000000000000..40df49f83bef07 --- /dev/null +++ b/test/typeparam/valimp.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/typeparam/value.go b/test/typeparam/value.go index 5dd7449d9c76f9..be25dcefb3f813 100644 --- a/test/typeparam/value.go +++ b/test/typeparam/value.go @@ -1,4 +1,4 @@ -// run -gcflags=-G=3 +// run // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -12,7 +12,7 @@ type value[T any] struct { val T } -func get[T2 any](v *value[T2]) T2 { +func get[T any](v *value[T]) T { return v.val } @@ -20,11 +20,11 @@ func set[T any](v *value[T], val T) { v.val = val } -func (v *value[T2]) set(val T2) { +func (v *value[T]) set(val T) { v.val = val } -func (v *value[T2]) get() T2 { +func (v *value[T]) get() T { return v.val } diff --git a/test/typeswitch3.go b/test/typeswitch3.go index a57889bc1dee4f..2e144d81c00cef 100644 --- a/test/typeswitch3.go +++ b/test/typeswitch3.go @@ -42,7 +42,7 @@ func main() { func noninterface() { var i int - switch i.(type) { // ERROR "cannot type switch on non-interface value|not an interface type" + switch i.(type) { // ERROR "cannot type switch on non-interface value|not an interface" case string: case int: } @@ -51,6 +51,6 @@ func noninterface() { name string } var s S - switch s.(type) { // ERROR "cannot type switch on non-interface value|not an interface type" + switch s.(type) { // ERROR "cannot type switch on non-interface value|not an interface" } } diff --git a/test/uintptrescapes2.go b/test/uintptrescapes2.go index 3ff1d940425863..656286c0ff2bd7 100644 --- a/test/uintptrescapes2.go +++ b/test/uintptrescapes2.go @@ -30,7 +30,7 @@ type T struct{} func (T) M1(a uintptr) {} // ERROR "escaping uintptr" //go:uintptrescapes -func (T) M2(a ...uintptr) {} // ERROR "escaping ...uintptr" "leaking param: a" +func (T) M2(a ...uintptr) {} // ERROR "escaping ...uintptr" func TestF1() { var t int // ERROR "moved to heap" diff --git a/test/uintptrkeepalive.go b/test/uintptrkeepalive.go new file mode 100644 index 00000000000000..97834dcd1af547 --- /dev/null +++ b/test/uintptrkeepalive.go @@ -0,0 +1,11 @@ +// errorcheck -std + +// Copyright 2022 The Go Authors. 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 + +//go:uintptrkeepalive +func missingNosplit(uintptr) { // ERROR "go:uintptrkeepalive requires go:nosplit" +} diff --git a/test/unsafe_slice_data.go b/test/unsafe_slice_data.go new file mode 100644 index 00000000000000..e8b8207547fabd --- /dev/null +++ b/test/unsafe_slice_data.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" + "unsafe" +) + +func main() { + var s = []byte("abc") + sh1 := *(*reflect.SliceHeader)(unsafe.Pointer(&s)) + ptr2 := unsafe.Pointer(unsafe.SliceData(s)) + if ptr2 != unsafe.Pointer(sh1.Data) { + panic(fmt.Errorf("unsafe.SliceData %p != %p", ptr2, unsafe.Pointer(sh1.Data))) + } +} diff --git a/test/unsafe_string.go b/test/unsafe_string.go new file mode 100644 index 00000000000000..ceecc6ff2f2efc --- /dev/null +++ b/test/unsafe_string.go @@ -0,0 +1,18 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "unsafe" +) + +func main() { + hello := [5]byte{'m', 'o', 's', 'h', 'i'} + if unsafe.String(&hello[0], uint64(len(hello))) != "moshi" { + panic("unsafe.String convert error") + } +} diff --git a/test/unsafe_string_data.go b/test/unsafe_string_data.go new file mode 100644 index 00000000000000..a3a69af5180a79 --- /dev/null +++ b/test/unsafe_string_data.go @@ -0,0 +1,22 @@ +// run + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "reflect" + "unsafe" +) + +func main() { + var s = "abc" + sh1 := (*reflect.StringHeader)(unsafe.Pointer(&s)) + ptr2 := unsafe.Pointer(unsafe.StringData(s)) + if ptr2 != unsafe.Pointer(sh1.Data) { + panic(fmt.Errorf("unsafe.StringData ret %p != %p", ptr2, unsafe.Pointer(sh1.Data))) + } +} diff --git a/test/unsafebuiltins.go b/test/unsafebuiltins.go index 4c940aa85599c1..8ee72ec2e8abdf 100644 --- a/test/unsafebuiltins.go +++ b/test/unsafebuiltins.go @@ -47,6 +47,49 @@ func main() { // size overflows address space mustPanic(func() { _ = unsafe.Slice(new(uint64), maxUintptr/8) }) mustPanic(func() { _ = unsafe.Slice(new(uint64), maxUintptr/8+1) }) + + // sliced memory overflows address space + last := (*byte)(unsafe.Pointer(^uintptr(0))) + _ = unsafe.Slice(last, 1) + mustPanic(func() { _ = unsafe.Slice(last, 2) }) + } + + // unsafe.String + { + s := unsafe.String(&p[0], len(p)) + assert(s == string(p[:])) + assert(len(s) == len(p)) + + // the empty string + assert(unsafe.String(nil, 0) == "") + + // nil pointer with positive length panics + mustPanic(func() { _ = unsafe.String(nil, 1) }) + + // negative length + var neg int = -1 + mustPanic(func() { _ = unsafe.String(new(byte), neg) }) + + // length too large + var tooBig uint64 = math.MaxUint64 + mustPanic(func() { _ = unsafe.String(new(byte), tooBig) }) + + // string memory overflows address space + last := (*byte)(unsafe.Pointer(^uintptr(0))) + _ = unsafe.String(last, 1) + mustPanic(func() { _ = unsafe.String(last, 2) }) + } + + // unsafe.StringData + { + var s = "string" + assert(string(unsafe.Slice(unsafe.StringData(s), len(s))) == s) + } + + //unsafe.SliceData + { + var s = []byte("slice") + assert(unsafe.String(unsafe.SliceData(s), len(s)) == string(s)) } } diff --git a/test/writebarrier.go b/test/writebarrier.go index dbf0b6dde28662..1b30fa509e5503 100644 --- a/test/writebarrier.go +++ b/test/writebarrier.go @@ -289,3 +289,17 @@ func f27(p *int) []interface{} { p, // ERROR "write barrier" } } + +var g28 [256]uint64 + +func f28() []interface{} { + return []interface{}{ + false, // no write barrier + true, // no write barrier + 0, // no write barrier + 1, // no write barrier + uint8(127), // no write barrier + int8(-4), // no write barrier + &g28[5], // no write barrier + } +} diff --git a/test/zerodivide.go b/test/zerodivide.go index 214d481164442a..fd36d67d1ad6dc 100644 --- a/test/zerodivide.go +++ b/test/zerodivide.go @@ -218,7 +218,7 @@ func main() { } fmt.Printf("%s: expected no error; got %q\n", t.name, err) case t.err != "" && err != "": - if strings.Index(err, t.err) < 0 { + if !strings.Contains(err, t.err) { if !bad { bad = true fmt.Printf("BUG\n")